From 9a8a7798ec2ffd53b33237aa03523e9e8184744b Mon Sep 17 00:00:00 2001 From: Aliaksandr Kalenik Date: Mon, 15 Jun 2026 19:27:13 +0200 Subject: [PATCH] Meta+LibWeb: Generate WebGL command proxy scaffolding Moving WebGL execution into the Compositor needs a serializable command stream and a client-side proxy that can queue commands before sending them over IPC. The existing generator metadata only described direct GL wrappers, so generated code could not distinguish async commands from sync calls or object factory methods. This teaches the WebGL metadata and generators about command streams and adds the unused LibWeb proxy/list types. No rendering behavior changes yet; the later host wiring can build on these generated interfaces without mixing the metadata churn into that commit. --- Libraries/LibWeb/CMakeLists.txt | 4 + Libraries/LibWeb/WebGL/GLFunctions.json | 1484 ++++++++++++----- Libraries/LibWeb/WebGL/RemoteWebGLTransport.h | 45 + Libraries/LibWeb/WebGL/Types.h | 20 + Libraries/LibWeb/WebGL/WebGLCommandList.cpp | 84 + Libraries/LibWeb/WebGL/WebGLCommandList.h | 169 ++ .../LibWeb/WebGL/WebGLContextProxyBase.cpp | 258 +++ .../LibWeb/WebGL/WebGLContextProxyBase.h | 104 ++ Meta/CMake/libweb_generators.cmake | 22 + .../generate_libweb_webgl_commands.py | 181 ++ .../generate_libweb_webgl_functions.py | 4 + .../Generators/generate_libweb_webgl_proxy.py | 227 +++ Meta/Generators/libweb_webgl.py | 102 +- 13 files changed, 2309 insertions(+), 395 deletions(-) create mode 100644 Libraries/LibWeb/WebGL/RemoteWebGLTransport.h create mode 100644 Libraries/LibWeb/WebGL/WebGLCommandList.cpp create mode 100644 Libraries/LibWeb/WebGL/WebGLCommandList.h create mode 100644 Libraries/LibWeb/WebGL/WebGLContextProxyBase.cpp create mode 100644 Libraries/LibWeb/WebGL/WebGLContextProxyBase.h create mode 100644 Meta/Generators/generate_libweb_webgl_commands.py create mode 100644 Meta/Generators/generate_libweb_webgl_proxy.py diff --git a/Libraries/LibWeb/CMakeLists.txt b/Libraries/LibWeb/CMakeLists.txt index ecc57417e5..a4cfd90e5e 100644 --- a/Libraries/LibWeb/CMakeLists.txt +++ b/Libraries/LibWeb/CMakeLists.txt @@ -1155,7 +1155,9 @@ set(SOURCES WebGL/WebGL2RenderingContextOverloads.cpp WebGL/WebGLActiveInfo.cpp WebGL/WebGLBuffer.cpp + WebGL/WebGLCommandList.cpp WebGL/WebGLContextAttributes.cpp + WebGL/WebGLContextProxyBase.cpp WebGL/WebGLContextEvent.cpp WebGL/WebGLFramebuffer.cpp WebGL/WebGLObject.cpp @@ -1256,6 +1258,8 @@ set(GENERATED_SOURCES HTML/MediaControlsDOM.cpp HTML/Parser/NamedCharacterReferences.cpp WebGL/GLFunctions.cpp + WebGL/WebGLCommands.cpp + WebGL/WebGLContextProxy.cpp ) ladybird_lib(LibWeb web EXPLICIT_SYMBOL_EXPORT) diff --git a/Libraries/LibWeb/WebGL/GLFunctions.json b/Libraries/LibWeb/WebGL/GLFunctions.json index 3e3540a299..ac4493104e 100644 --- a/Libraries/LibWeb/WebGL/GLFunctions.json +++ b/Libraries/LibWeb/WebGL/GLFunctions.json @@ -7,7 +7,8 @@ "type": "GLenum", "name": "texture" } - ] + ], + "category": "command" }, { "name": "glAttachShader", @@ -15,13 +16,16 @@ "args": [ { "type": "GLuint", - "name": "program" + "name": "program", + "object": true }, { "type": "GLuint", - "name": "shader" + "name": "shader", + "object": true } - ] + ], + "category": "command" }, { "name": "glBeginQuery", @@ -33,9 +37,11 @@ }, { "type": "GLuint", - "name": "id" + "name": "id", + "object": true } - ] + ], + "category": "command" }, { "name": "glBeginTransformFeedback", @@ -45,7 +51,8 @@ "type": "GLenum", "name": "primitiveMode" } - ] + ], + "category": "command" }, { "name": "glBindAttribLocation", @@ -53,7 +60,8 @@ "args": [ { "type": "GLuint", - "name": "program" + "name": "program", + "object": true }, { "type": "GLuint", @@ -61,9 +69,11 @@ }, { "type": "GLchar const*", - "name": "name" + "name": "name", + "string": true } - ] + ], + "category": "command" }, { "name": "glBindBuffer", @@ -75,9 +85,11 @@ }, { "type": "GLuint", - "name": "buffer" + "name": "buffer", + "object": true } - ] + ], + "category": "command" }, { "name": "glBindBufferBase", @@ -93,9 +105,11 @@ }, { "type": "GLuint", - "name": "buffer" + "name": "buffer", + "object": true } - ] + ], + "category": "command" }, { "name": "glBindBufferRange", @@ -111,7 +125,8 @@ }, { "type": "GLuint", - "name": "buffer" + "name": "buffer", + "object": true }, { "type": "GLintptr", @@ -121,7 +136,8 @@ "type": "GLsizeiptr", "name": "size" } - ] + ], + "category": "command" }, { "name": "glBindFramebuffer", @@ -133,9 +149,12 @@ }, { "type": "GLuint", - "name": "framebuffer" + "name": "framebuffer", + "object": true, + "zero_means_default": "default_framebuffer" } - ] + ], + "category": "command" }, { "name": "glBindRenderbuffer", @@ -147,9 +166,12 @@ }, { "type": "GLuint", - "name": "renderbuffer" + "name": "renderbuffer", + "object": true, + "zero_means_default": "default_renderbuffer" } - ] + ], + "category": "command" }, { "name": "glBindSampler", @@ -161,9 +183,11 @@ }, { "type": "GLuint", - "name": "sampler" + "name": "sampler", + "object": true } - ] + ], + "category": "command" }, { "name": "glBindTexture", @@ -175,9 +199,11 @@ }, { "type": "GLuint", - "name": "texture" + "name": "texture", + "object": true } - ] + ], + "category": "command" }, { "name": "glBindTransformFeedback", @@ -189,9 +215,11 @@ }, { "type": "GLuint", - "name": "id" + "name": "id", + "object": true } - ] + ], + "category": "command" }, { "name": "glBindVertexArray", @@ -199,9 +227,11 @@ "args": [ { "type": "GLuint", - "name": "array" + "name": "array", + "object": true } - ] + ], + "category": "command" }, { "name": "glBindVertexArrayOES", @@ -209,9 +239,11 @@ "args": [ { "type": "GLuint", - "name": "array" + "name": "array", + "object": true } - ] + ], + "category": "command" }, { "name": "glBlendColor", @@ -233,7 +265,8 @@ "type": "GLfloat", "name": "alpha" } - ] + ], + "category": "command" }, { "name": "glBlendEquation", @@ -243,7 +276,8 @@ "type": "GLenum", "name": "mode" } - ] + ], + "category": "command" }, { "name": "glBlendEquationSeparate", @@ -257,7 +291,8 @@ "type": "GLenum", "name": "modeAlpha" } - ] + ], + "category": "command" }, { "name": "glBlendFunc", @@ -271,7 +306,8 @@ "type": "GLenum", "name": "dfactor" } - ] + ], + "category": "command" }, { "name": "glBlendFuncSeparate", @@ -293,7 +329,8 @@ "type": "GLenum", "name": "dfactorAlpha" } - ] + ], + "category": "command" }, { "name": "glBlitFramebuffer", @@ -339,7 +376,8 @@ "type": "GLenum", "name": "filter" } - ] + ], + "category": "command" }, { "name": "glBufferData", @@ -355,13 +393,16 @@ }, { "type": "void const*", - "name": "data" + "name": "data", + "payload": "size", + "nullable": true }, { "type": "GLenum", "name": "usage" } - ] + ], + "category": "command" }, { "name": "glBufferSubData", @@ -381,9 +422,11 @@ }, { "type": "void const*", - "name": "data" + "name": "data", + "payload": "size" } - ] + ], + "category": "command" }, { "name": "glCheckFramebufferStatus", @@ -393,7 +436,8 @@ "type": "GLenum", "name": "target" } - ] + ], + "category": "sync" }, { "name": "glClear", @@ -403,7 +447,8 @@ "type": "GLbitfield", "name": "mask" } - ] + ], + "category": "command" }, { "name": "glClearBufferfi", @@ -425,7 +470,8 @@ "type": "GLint", "name": "stencil" } - ] + ], + "category": "command" }, { "name": "glClearBufferfv", @@ -441,9 +487,11 @@ }, { "type": "GLfloat const*", - "name": "value" + "name": "value", + "payload": "(buffer == GL_COLOR ? 4 : 1) * sizeof(GLfloat)" } - ] + ], + "category": "command" }, { "name": "glClearBufferiv", @@ -459,9 +507,11 @@ }, { "type": "GLint const*", - "name": "value" + "name": "value", + "payload": "(buffer == GL_COLOR ? 4 : 1) * sizeof(GLint)" } - ] + ], + "category": "command" }, { "name": "glClearBufferuiv", @@ -477,9 +527,11 @@ }, { "type": "GLuint const*", - "name": "value" + "name": "value", + "payload": "(buffer == GL_COLOR ? 4 : 1) * sizeof(GLuint)" } - ] + ], + "category": "command" }, { "name": "glClearColor", @@ -501,7 +553,8 @@ "type": "GLfloat", "name": "alpha" } - ] + ], + "category": "command" }, { "name": "glClearDepthf", @@ -511,7 +564,8 @@ "type": "GLfloat", "name": "d" } - ] + ], + "category": "command" }, { "name": "glClearStencil", @@ -521,7 +575,8 @@ "type": "GLint", "name": "s" } - ] + ], + "category": "command" }, { "name": "glClientWaitSync", @@ -529,7 +584,8 @@ "args": [ { "type": "GLsync", - "name": "sync" + "name": "sync", + "object": true }, { "type": "GLbitfield", @@ -537,9 +593,11 @@ }, { "type": "GLuint64", - "name": "timeout" + "name": "timeout", + "host_override": "0" } - ] + ], + "category": "sync" }, { "name": "glColorMask", @@ -561,7 +619,8 @@ "type": "GLboolean", "name": "alpha" } - ] + ], + "category": "command" }, { "name": "glCompileShader", @@ -569,9 +628,11 @@ "args": [ { "type": "GLuint", - "name": "shader" + "name": "shader", + "object": true } - ] + ], + "category": "command" }, { "name": "glCompressedTexImage2DRobustANGLE", @@ -611,9 +672,11 @@ }, { "type": "void const*", - "name": "data" + "name": "data", + "payload": "bufSize" } - ] + ], + "category": "command" }, { "name": "glCompressedTexImage3DRobustANGLE", @@ -657,9 +720,11 @@ }, { "type": "void const*", - "name": "data" + "name": "data", + "payload": "bufSize" } - ] + ], + "category": "command" }, { "name": "glCompressedTexSubImage2DRobustANGLE", @@ -703,9 +768,11 @@ }, { "type": "void const*", - "name": "data" + "name": "data", + "payload": "bufSize" } - ] + ], + "category": "command" }, { "name": "glCompressedTexSubImage3DRobustANGLE", @@ -757,9 +824,11 @@ }, { "type": "void const*", - "name": "data" + "name": "data", + "payload": "bufSize" } - ] + ], + "category": "command" }, { "name": "glCopyBufferSubData", @@ -785,7 +854,8 @@ "type": "GLsizeiptr", "name": "size" } - ] + ], + "category": "command" }, { "name": "glCopyTexImage2D", @@ -823,7 +893,8 @@ "type": "GLint", "name": "border" } - ] + ], + "category": "command" }, { "name": "glCopyTexSubImage2D", @@ -861,12 +932,14 @@ "type": "GLsizei", "name": "height" } - ] + ], + "category": "command" }, { "name": "glCreateProgram", "return": "GLuint", - "args": [] + "args": [], + "category": "gen" }, { "name": "glCreateShader", @@ -876,7 +949,8 @@ "type": "GLenum", "name": "type" } - ] + ], + "category": "gen" }, { "name": "glCullFace", @@ -886,7 +960,8 @@ "type": "GLenum", "name": "mode" } - ] + ], + "category": "command" }, { "name": "glDeleteBuffers", @@ -898,9 +973,13 @@ }, { "type": "GLuint const*", - "name": "buffers" + "name": "buffers", + "object": true, + "payload": "n * sizeof(GLuint)" } - ] + ], + "category": "command", + "deletes_objects": true }, { "name": "glDeleteFramebuffers", @@ -912,9 +991,13 @@ }, { "type": "GLuint const*", - "name": "framebuffers" + "name": "framebuffers", + "object": true, + "payload": "n * sizeof(GLuint)" } - ] + ], + "category": "command", + "deletes_objects": true }, { "name": "glDeleteProgram", @@ -922,9 +1005,12 @@ "args": [ { "type": "GLuint", - "name": "program" + "name": "program", + "object": true } - ] + ], + "category": "command", + "deletes_objects": true }, { "name": "glDeleteQueries", @@ -936,9 +1022,13 @@ }, { "type": "GLuint const*", - "name": "ids" + "name": "ids", + "object": true, + "payload": "n * sizeof(GLuint)" } - ] + ], + "category": "command", + "deletes_objects": true }, { "name": "glDeleteRenderbuffers", @@ -950,9 +1040,13 @@ }, { "type": "GLuint const*", - "name": "renderbuffers" + "name": "renderbuffers", + "object": true, + "payload": "n * sizeof(GLuint)" } - ] + ], + "category": "command", + "deletes_objects": true }, { "name": "glDeleteSamplers", @@ -964,9 +1058,13 @@ }, { "type": "GLuint const*", - "name": "samplers" + "name": "samplers", + "object": true, + "payload": "count * sizeof(GLuint)" } - ] + ], + "category": "command", + "deletes_objects": true }, { "name": "glDeleteShader", @@ -974,9 +1072,12 @@ "args": [ { "type": "GLuint", - "name": "shader" + "name": "shader", + "object": true } - ] + ], + "category": "command", + "deletes_objects": true }, { "name": "glDeleteSync", @@ -984,9 +1085,12 @@ "args": [ { "type": "GLsync", - "name": "sync" + "name": "sync", + "object": true } - ] + ], + "category": "command", + "deletes_objects": true }, { "name": "glDeleteTextures", @@ -998,9 +1102,13 @@ }, { "type": "GLuint const*", - "name": "textures" + "name": "textures", + "object": true, + "payload": "n * sizeof(GLuint)" } - ] + ], + "category": "command", + "deletes_objects": true }, { "name": "glDeleteTransformFeedbacks", @@ -1012,9 +1120,13 @@ }, { "type": "GLuint const*", - "name": "ids" + "name": "ids", + "object": true, + "payload": "n * sizeof(GLuint)" } - ] + ], + "category": "command", + "deletes_objects": true }, { "name": "glDeleteVertexArrays", @@ -1026,9 +1138,13 @@ }, { "type": "GLuint const*", - "name": "arrays" + "name": "arrays", + "object": true, + "payload": "n * sizeof(GLuint)" } - ] + ], + "category": "command", + "deletes_objects": true }, { "name": "glDeleteVertexArraysOES", @@ -1040,9 +1156,13 @@ }, { "type": "GLuint const*", - "name": "arrays" + "name": "arrays", + "object": true, + "payload": "n * sizeof(GLuint)" } - ] + ], + "category": "command", + "deletes_objects": true }, { "name": "glDepthFunc", @@ -1052,7 +1172,8 @@ "type": "GLenum", "name": "func" } - ] + ], + "category": "command" }, { "name": "glDepthMask", @@ -1062,7 +1183,8 @@ "type": "GLboolean", "name": "flag" } - ] + ], + "category": "command" }, { "name": "glDepthRangef", @@ -1076,7 +1198,8 @@ "type": "GLfloat", "name": "f" } - ] + ], + "category": "command" }, { "name": "glDetachShader", @@ -1084,13 +1207,16 @@ "args": [ { "type": "GLuint", - "name": "program" + "name": "program", + "object": true }, { "type": "GLuint", - "name": "shader" + "name": "shader", + "object": true } - ] + ], + "category": "command" }, { "name": "glDisable", @@ -1100,7 +1226,8 @@ "type": "GLenum", "name": "cap" } - ] + ], + "category": "command" }, { "name": "glDisableVertexAttribArray", @@ -1110,7 +1237,8 @@ "type": "GLuint", "name": "index" } - ] + ], + "category": "command" }, { "name": "glDrawArrays", @@ -1128,7 +1256,8 @@ "type": "GLsizei", "name": "count" } - ] + ], + "category": "command" }, { "name": "glDrawArraysInstanced", @@ -1150,7 +1279,8 @@ "type": "GLsizei", "name": "instancecount" } - ] + ], + "category": "command" }, { "name": "glDrawArraysInstancedANGLE", @@ -1172,7 +1302,8 @@ "type": "GLsizei", "name": "primcount" } - ] + ], + "category": "command" }, { "name": "glDrawBuffers", @@ -1184,9 +1315,11 @@ }, { "type": "GLenum const*", - "name": "bufs" + "name": "bufs", + "payload": "n * sizeof(GLenum)" } - ] + ], + "category": "command" }, { "name": "glDrawBuffersEXT", @@ -1198,9 +1331,11 @@ }, { "type": "GLenum const*", - "name": "bufs" + "name": "bufs", + "payload": "n * sizeof(GLenum)" } - ] + ], + "category": "command" }, { "name": "glDrawElements", @@ -1220,9 +1355,11 @@ }, { "type": "void const*", - "name": "indices" + "name": "indices", + "offset": true } - ] + ], + "category": "command" }, { "name": "glDrawElementsInstanced", @@ -1242,13 +1379,15 @@ }, { "type": "void const*", - "name": "indices" + "name": "indices", + "offset": true }, { "type": "GLsizei", "name": "instancecount" } - ] + ], + "category": "command" }, { "name": "glDrawElementsInstancedANGLE", @@ -1268,13 +1407,15 @@ }, { "type": "void const*", - "name": "indices" + "name": "indices", + "offset": true }, { "type": "GLsizei", "name": "primcount" } - ] + ], + "category": "command" }, { "name": "glDrawRangeElements", @@ -1302,9 +1443,11 @@ }, { "type": "void const*", - "name": "indices" + "name": "indices", + "offset": true } - ] + ], + "category": "command" }, { "name": "glEnable", @@ -1314,7 +1457,8 @@ "type": "GLenum", "name": "cap" } - ] + ], + "category": "command" }, { "name": "glEnableVertexAttribArray", @@ -1324,7 +1468,8 @@ "type": "GLuint", "name": "index" } - ] + ], + "category": "command" }, { "name": "glEndQuery", @@ -1334,12 +1479,14 @@ "type": "GLenum", "name": "target" } - ] + ], + "category": "command" }, { "name": "glEndTransformFeedback", "return": "void", - "args": [] + "args": [], + "category": "command" }, { "name": "glFenceSync", @@ -1353,17 +1500,20 @@ "type": "GLbitfield", "name": "flags" } - ] + ], + "category": "gen" }, { "name": "glFinish", "return": "void", - "args": [] + "args": [], + "category": "sync" }, { "name": "glFlush", "return": "void", - "args": [] + "args": [], + "category": "command" }, { "name": "glFramebufferRenderbuffer", @@ -1383,9 +1533,11 @@ }, { "type": "GLuint", - "name": "renderbuffer" + "name": "renderbuffer", + "object": true } - ] + ], + "category": "command" }, { "name": "glFramebufferTexture2D", @@ -1405,13 +1557,15 @@ }, { "type": "GLuint", - "name": "texture" + "name": "texture", + "object": true }, { "type": "GLint", "name": "level" } - ] + ], + "category": "command" }, { "name": "glFramebufferTextureLayer", @@ -1427,7 +1581,8 @@ }, { "type": "GLuint", - "name": "texture" + "name": "texture", + "object": true }, { "type": "GLint", @@ -1437,7 +1592,8 @@ "type": "GLint", "name": "layer" } - ] + ], + "category": "command" }, { "name": "glFrontFace", @@ -1447,7 +1603,8 @@ "type": "GLenum", "name": "mode" } - ] + ], + "category": "command" }, { "name": "glGenBuffers", @@ -1461,7 +1618,8 @@ "type": "GLuint*", "name": "buffers" } - ] + ], + "category": "gen" }, { "name": "glGenFramebuffers", @@ -1475,7 +1633,8 @@ "type": "GLuint*", "name": "framebuffers" } - ] + ], + "category": "gen" }, { "name": "glGenQueries", @@ -1489,7 +1648,8 @@ "type": "GLuint*", "name": "ids" } - ] + ], + "category": "gen" }, { "name": "glGenRenderbuffers", @@ -1503,7 +1663,8 @@ "type": "GLuint*", "name": "renderbuffers" } - ] + ], + "category": "gen" }, { "name": "glGenSamplers", @@ -1517,7 +1678,8 @@ "type": "GLuint*", "name": "samplers" } - ] + ], + "category": "gen" }, { "name": "glGenTextures", @@ -1531,7 +1693,8 @@ "type": "GLuint*", "name": "textures" } - ] + ], + "category": "gen" }, { "name": "glGenTransformFeedbacks", @@ -1545,7 +1708,8 @@ "type": "GLuint*", "name": "ids" } - ] + ], + "category": "gen" }, { "name": "glGenVertexArrays", @@ -1559,7 +1723,8 @@ "type": "GLuint*", "name": "arrays" } - ] + ], + "category": "gen" }, { "name": "glGenVertexArraysOES", @@ -1573,7 +1738,8 @@ "type": "GLuint*", "name": "arrays" } - ] + ], + "category": "gen" }, { "name": "glGenerateMipmap", @@ -1583,7 +1749,8 @@ "type": "GLenum", "name": "target" } - ] + ], + "category": "command" }, { "name": "glGetActiveAttrib", @@ -1591,7 +1758,8 @@ "args": [ { "type": "GLuint", - "name": "program" + "name": "program", + "object": true }, { "type": "GLuint", @@ -1603,21 +1771,27 @@ }, { "type": "GLsizei*", - "name": "length" + "name": "length", + "out": true }, { "type": "GLint*", - "name": "size" + "name": "size", + "out": true }, { "type": "GLenum*", - "name": "type" + "name": "type", + "out": true }, { "type": "GLchar*", - "name": "name" + "name": "name", + "out": true, + "payload": "bufSize * sizeof(GLchar)" } - ] + ], + "category": "sync" }, { "name": "glGetActiveUniform", @@ -1625,7 +1799,8 @@ "args": [ { "type": "GLuint", - "name": "program" + "name": "program", + "object": true }, { "type": "GLuint", @@ -1637,21 +1812,27 @@ }, { "type": "GLsizei*", - "name": "length" + "name": "length", + "out": true }, { "type": "GLint*", - "name": "size" + "name": "size", + "out": true }, { "type": "GLenum*", - "name": "type" + "name": "type", + "out": true }, { "type": "GLchar*", - "name": "name" + "name": "name", + "out": true, + "payload": "bufSize * sizeof(GLchar)" } - ] + ], + "category": "sync" }, { "name": "glGetActiveUniformBlockName", @@ -1659,7 +1840,8 @@ "args": [ { "type": "GLuint", - "name": "program" + "name": "program", + "object": true }, { "type": "GLuint", @@ -1671,13 +1853,17 @@ }, { "type": "GLsizei*", - "name": "length" + "name": "length", + "out": true }, { "type": "GLchar*", - "name": "uniformBlockName" + "name": "uniformBlockName", + "out": true, + "payload": "bufSize * sizeof(GLchar)" } - ] + ], + "category": "sync" }, { "name": "glGetActiveUniformBlockivRobustANGLE", @@ -1685,7 +1871,8 @@ "args": [ { "type": "GLuint", - "name": "program" + "name": "program", + "object": true }, { "type": "GLuint", @@ -1701,13 +1888,17 @@ }, { "type": "GLsizei*", - "name": "length" + "name": "length", + "out": true }, { "type": "GLint*", - "name": "params" + "name": "params", + "out": true, + "payload": "bufSize * sizeof(GLint)" } - ] + ], + "category": "sync" }, { "name": "glGetActiveUniformsiv", @@ -1715,7 +1906,8 @@ "args": [ { "type": "GLuint", - "name": "program" + "name": "program", + "object": true }, { "type": "GLsizei", @@ -1723,7 +1915,8 @@ }, { "type": "GLuint const*", - "name": "uniformIndices" + "name": "uniformIndices", + "payload": "uniformCount * sizeof(GLuint)" }, { "type": "GLenum", @@ -1731,9 +1924,12 @@ }, { "type": "GLint*", - "name": "params" + "name": "params", + "out": true, + "payload": "uniformCount * sizeof(GLint)" } - ] + ], + "category": "sync" }, { "name": "glGetAttribLocation", @@ -1741,13 +1937,16 @@ "args": [ { "type": "GLuint", - "name": "program" + "name": "program", + "object": true }, { "type": "GLchar const*", - "name": "name" + "name": "name", + "string": true } - ] + ], + "category": "sync" }, { "name": "glGetBooleanvRobustANGLE", @@ -1763,13 +1962,17 @@ }, { "type": "GLsizei*", - "name": "length" + "name": "length", + "out": true }, { "type": "GLboolean*", - "name": "data" + "name": "data", + "out": true, + "payload": "bufSize * sizeof(GLboolean)" } - ] + ], + "category": "sync" }, { "name": "glGetBufferParameterivRobustANGLE", @@ -1789,18 +1992,23 @@ }, { "type": "GLsizei*", - "name": "length" + "name": "length", + "out": true }, { "type": "GLint*", - "name": "params" + "name": "params", + "out": true, + "payload": "bufSize * sizeof(GLint)" } - ] + ], + "category": "sync" }, { "name": "glGetError", "return": "GLenum", - "args": [] + "args": [], + "category": "sync" }, { "name": "glGetFloatvRobustANGLE", @@ -1816,13 +2024,17 @@ }, { "type": "GLsizei*", - "name": "length" + "name": "length", + "out": true }, { "type": "GLfloat*", - "name": "data" + "name": "data", + "out": true, + "payload": "bufSize * sizeof(GLfloat)" } - ] + ], + "category": "sync" }, { "name": "glGetInteger64vRobustANGLE", @@ -1838,13 +2050,17 @@ }, { "type": "GLsizei*", - "name": "length" + "name": "length", + "out": true }, { "type": "GLint64*", - "name": "data" + "name": "data", + "out": true, + "payload": "bufSize * sizeof(GLint64)" } - ] + ], + "category": "sync" }, { "name": "glGetIntegervRobustANGLE", @@ -1860,13 +2076,17 @@ }, { "type": "GLsizei*", - "name": "length" + "name": "length", + "out": true }, { "type": "GLint*", - "name": "data" + "name": "data", + "out": true, + "payload": "bufSize * sizeof(GLint)" } - ] + ], + "category": "sync" }, { "name": "glGetInternalformativRobustANGLE", @@ -1890,13 +2110,17 @@ }, { "type": "GLsizei*", - "name": "length" + "name": "length", + "out": true }, { "type": "GLint*", - "name": "params" + "name": "params", + "out": true, + "payload": "bufSize * sizeof(GLint)" } - ] + ], + "category": "sync" }, { "name": "glGetProgramInfoLog", @@ -1904,7 +2128,8 @@ "args": [ { "type": "GLuint", - "name": "program" + "name": "program", + "object": true }, { "type": "GLsizei", @@ -1912,13 +2137,17 @@ }, { "type": "GLsizei*", - "name": "length" + "name": "length", + "out": true }, { "type": "GLchar*", - "name": "infoLog" + "name": "infoLog", + "out": true, + "payload": "bufSize * sizeof(GLchar)" } - ] + ], + "category": "sync" }, { "name": "glGetProgramiv", @@ -1926,7 +2155,8 @@ "args": [ { "type": "GLuint", - "name": "program" + "name": "program", + "object": true }, { "type": "GLenum", @@ -1934,9 +2164,12 @@ }, { "type": "GLint*", - "name": "params" + "name": "params", + "out": true, + "payload": "sizeof(GLint)" } - ] + ], + "category": "sync" }, { "name": "glGetProgramivRobustANGLE", @@ -1944,7 +2177,8 @@ "args": [ { "type": "GLuint", - "name": "program" + "name": "program", + "object": true }, { "type": "GLenum", @@ -1956,13 +2190,17 @@ }, { "type": "GLsizei*", - "name": "length" + "name": "length", + "out": true }, { "type": "GLint*", - "name": "params" + "name": "params", + "out": true, + "payload": "bufSize * sizeof(GLint)" } - ] + ], + "category": "sync" }, { "name": "glGetQueryObjectuivRobustANGLE", @@ -1970,7 +2208,8 @@ "args": [ { "type": "GLuint", - "name": "id" + "name": "id", + "object": true }, { "type": "GLenum", @@ -1982,13 +2221,17 @@ }, { "type": "GLsizei*", - "name": "length" + "name": "length", + "out": true }, { "type": "GLuint*", - "name": "params" + "name": "params", + "out": true, + "payload": "bufSize * sizeof(GLuint)" } - ] + ], + "category": "sync" }, { "name": "glGetRenderbufferParameterivRobustANGLE", @@ -2008,13 +2251,17 @@ }, { "type": "GLsizei*", - "name": "length" + "name": "length", + "out": true }, { "type": "GLint*", - "name": "params" + "name": "params", + "out": true, + "payload": "bufSize * sizeof(GLint)" } - ] + ], + "category": "sync" }, { "name": "glGetShaderInfoLog", @@ -2022,7 +2269,8 @@ "args": [ { "type": "GLuint", - "name": "shader" + "name": "shader", + "object": true }, { "type": "GLsizei", @@ -2030,13 +2278,17 @@ }, { "type": "GLsizei*", - "name": "length" + "name": "length", + "out": true }, { "type": "GLchar*", - "name": "infoLog" + "name": "infoLog", + "out": true, + "payload": "bufSize * sizeof(GLchar)" } - ] + ], + "category": "sync" }, { "name": "glGetShaderPrecisionFormat", @@ -2052,13 +2304,18 @@ }, { "type": "GLint*", - "name": "range" + "name": "range", + "out": true, + "payload": "2 * sizeof(GLint)" }, { "type": "GLint*", - "name": "precision" + "name": "precision", + "out": true, + "payload": "sizeof(GLint)" } - ] + ], + "category": "sync" }, { "name": "glGetShaderSource", @@ -2066,7 +2323,8 @@ "args": [ { "type": "GLuint", - "name": "shader" + "name": "shader", + "object": true }, { "type": "GLsizei", @@ -2074,13 +2332,17 @@ }, { "type": "GLsizei*", - "name": "length" + "name": "length", + "out": true }, { "type": "GLchar*", - "name": "source" + "name": "source", + "out": true, + "payload": "bufSize * sizeof(GLchar)" } - ] + ], + "category": "sync" }, { "name": "glGetShaderiv", @@ -2088,7 +2350,8 @@ "args": [ { "type": "GLuint", - "name": "shader" + "name": "shader", + "object": true }, { "type": "GLenum", @@ -2096,9 +2359,12 @@ }, { "type": "GLint*", - "name": "params" + "name": "params", + "out": true, + "payload": "sizeof(GLint)" } - ] + ], + "category": "sync" }, { "name": "glGetShaderivRobustANGLE", @@ -2106,7 +2372,8 @@ "args": [ { "type": "GLuint", - "name": "shader" + "name": "shader", + "object": true }, { "type": "GLenum", @@ -2118,13 +2385,17 @@ }, { "type": "GLsizei*", - "name": "length" + "name": "length", + "out": true }, { "type": "GLint*", - "name": "params" + "name": "params", + "out": true, + "payload": "bufSize * sizeof(GLint)" } - ] + ], + "category": "sync" }, { "name": "glGetString", @@ -2134,6 +2405,19 @@ "type": "GLenum", "name": "name" } + ], + "category": "custom", + "wire_request": [ + { + "type": "GLenum", + "name": "name" + } + ], + "wire_reply": [ + { + "type": "WebGLDataSpan", + "name": "value" + } ] }, { @@ -2142,7 +2426,8 @@ "args": [ { "type": "GLsync", - "name": "sync" + "name": "sync", + "object": true }, { "type": "GLenum", @@ -2154,13 +2439,17 @@ }, { "type": "GLsizei*", - "name": "length" + "name": "length", + "out": true }, { "type": "GLint*", - "name": "values" + "name": "values", + "out": true, + "payload": "count * sizeof(GLint)" } - ] + ], + "category": "sync" }, { "name": "glGetTexParameterfvRobustANGLE", @@ -2180,13 +2469,17 @@ }, { "type": "GLsizei*", - "name": "length" + "name": "length", + "out": true }, { "type": "GLfloat*", - "name": "params" + "name": "params", + "out": true, + "payload": "bufSize * sizeof(GLfloat)" } - ] + ], + "category": "sync" }, { "name": "glGetTexParameterivRobustANGLE", @@ -2206,13 +2499,17 @@ }, { "type": "GLsizei*", - "name": "length" + "name": "length", + "out": true }, { "type": "GLint*", - "name": "params" + "name": "params", + "out": true, + "payload": "bufSize * sizeof(GLint)" } - ] + ], + "category": "sync" }, { "name": "glGetUniformBlockIndex", @@ -2220,13 +2517,16 @@ "args": [ { "type": "GLuint", - "name": "program" + "name": "program", + "object": true }, { "type": "GLchar const*", - "name": "uniformBlockName" + "name": "uniformBlockName", + "string": true } - ] + ], + "category": "sync" }, { "name": "glGetUniformIndices", @@ -2234,7 +2534,8 @@ "args": [ { "type": "GLuint", - "name": "program" + "name": "program", + "object": true }, { "type": "GLsizei", @@ -2248,6 +2549,27 @@ "type": "GLuint*", "name": "uniformIndices" } + ], + "category": "custom", + "wire_request": [ + { + "type": "WebGLObjectId", + "name": "program" + }, + { + "type": "GLsizei", + "name": "uniform_count" + }, + { + "type": "WebGLDataSpan", + "name": "uniform_names" + } + ], + "wire_reply": [ + { + "type": "WebGLDataSpan", + "name": "uniform_indices" + } ] }, { @@ -2256,13 +2578,16 @@ "args": [ { "type": "GLuint", - "name": "program" + "name": "program", + "object": true }, { "type": "GLchar const*", - "name": "name" + "name": "name", + "string": true } - ] + ], + "category": "sync" }, { "name": "glGetVertexAttribPointervRobustANGLE", @@ -2288,6 +2613,23 @@ "type": "void**", "name": "pointer" } + ], + "category": "custom", + "wire_request": [ + { + "type": "GLuint", + "name": "index" + }, + { + "type": "GLenum", + "name": "pname" + } + ], + "wire_reply": [ + { + "type": "GLintptr", + "name": "pointer" + } ] }, { @@ -2308,13 +2650,17 @@ }, { "type": "GLsizei*", - "name": "length" + "name": "length", + "out": true }, { "type": "GLfloat*", - "name": "params" + "name": "params", + "out": true, + "payload": "bufSize * sizeof(GLfloat)" } - ] + ], + "category": "sync" }, { "name": "glGetVertexAttribivRobustANGLE", @@ -2334,13 +2680,17 @@ }, { "type": "GLsizei*", - "name": "length" + "name": "length", + "out": true }, { "type": "GLint*", - "name": "params" + "name": "params", + "out": true, + "payload": "bufSize * sizeof(GLint)" } - ] + ], + "category": "sync" }, { "name": "glHint", @@ -2354,7 +2704,8 @@ "type": "GLenum", "name": "mode" } - ] + ], + "category": "command" }, { "name": "glInvalidateFramebuffer", @@ -2370,9 +2721,11 @@ }, { "type": "GLenum const*", - "name": "attachments" + "name": "attachments", + "payload": "numAttachments * sizeof(GLenum)" } - ] + ], + "category": "command" }, { "name": "glInvalidateSubFramebuffer", @@ -2388,7 +2741,8 @@ }, { "type": "GLenum const*", - "name": "attachments" + "name": "attachments", + "payload": "numAttachments * sizeof(GLenum)" }, { "type": "GLint", @@ -2406,7 +2760,8 @@ "type": "GLsizei", "name": "height" } - ] + ], + "category": "command" }, { "name": "glIsBuffer", @@ -2414,9 +2769,11 @@ "args": [ { "type": "GLuint", - "name": "buffer" + "name": "buffer", + "object": true } - ] + ], + "category": "sync" }, { "name": "glIsEnabled", @@ -2426,7 +2783,8 @@ "type": "GLenum", "name": "cap" } - ] + ], + "category": "sync" }, { "name": "glIsFramebuffer", @@ -2434,9 +2792,11 @@ "args": [ { "type": "GLuint", - "name": "framebuffer" + "name": "framebuffer", + "object": true } - ] + ], + "category": "sync" }, { "name": "glIsProgram", @@ -2444,9 +2804,11 @@ "args": [ { "type": "GLuint", - "name": "program" + "name": "program", + "object": true } - ] + ], + "category": "sync" }, { "name": "glIsRenderbuffer", @@ -2454,9 +2816,11 @@ "args": [ { "type": "GLuint", - "name": "renderbuffer" + "name": "renderbuffer", + "object": true } - ] + ], + "category": "sync" }, { "name": "glIsShader", @@ -2464,9 +2828,11 @@ "args": [ { "type": "GLuint", - "name": "shader" + "name": "shader", + "object": true } - ] + ], + "category": "sync" }, { "name": "glIsTexture", @@ -2474,9 +2840,11 @@ "args": [ { "type": "GLuint", - "name": "texture" + "name": "texture", + "object": true } - ] + ], + "category": "sync" }, { "name": "glIsVertexArray", @@ -2484,9 +2852,11 @@ "args": [ { "type": "GLuint", - "name": "array" + "name": "array", + "object": true } - ] + ], + "category": "sync" }, { "name": "glIsVertexArrayOES", @@ -2494,9 +2864,11 @@ "args": [ { "type": "GLuint", - "name": "array" + "name": "array", + "object": true } - ] + ], + "category": "sync" }, { "name": "glLineWidth", @@ -2506,7 +2878,8 @@ "type": "GLfloat", "name": "width" } - ] + ], + "category": "command" }, { "name": "glLinkProgram", @@ -2514,9 +2887,11 @@ "args": [ { "type": "GLuint", - "name": "program" + "name": "program", + "object": true } - ] + ], + "category": "command" }, { "name": "glMapBufferRange", @@ -2538,12 +2913,14 @@ "type": "GLbitfield", "name": "access" } - ] + ], + "category": "custom" }, { "name": "glPauseTransformFeedback", "return": "void", - "args": [] + "args": [], + "category": "command" }, { "name": "glPixelStorei", @@ -2557,7 +2934,8 @@ "type": "GLint", "name": "param" } - ] + ], + "category": "command" }, { "name": "glPolygonOffset", @@ -2571,7 +2949,8 @@ "type": "GLfloat", "name": "units" } - ] + ], + "category": "command" }, { "name": "glReadBuffer", @@ -2581,7 +2960,8 @@ "type": "GLenum", "name": "src" } - ] + ], + "category": "command" }, { "name": "glReadPixelsRobustANGLE", @@ -2631,7 +3011,8 @@ "type": "void*", "name": "pixels" } - ] + ], + "category": "custom" }, { "name": "glRenderbufferStorage", @@ -2653,7 +3034,8 @@ "type": "GLsizei", "name": "height" } - ] + ], + "category": "command" }, { "name": "glRenderbufferStorageMultisample", @@ -2679,12 +3061,26 @@ "type": "GLsizei", "name": "height" } + ], + "category": "command" + }, + { + "name": "glRequestExtensionANGLE", + "return": "void", + "category": "command", + "args": [ + { + "type": "GLchar const*", + "name": "name", + "string": true + } ] }, { "name": "glResumeTransformFeedback", "return": "void", - "args": [] + "args": [], + "category": "command" }, { "name": "glSampleCoverage", @@ -2698,7 +3094,8 @@ "type": "GLboolean", "name": "invert" } - ] + ], + "category": "command" }, { "name": "glSamplerParameterf", @@ -2706,7 +3103,8 @@ "args": [ { "type": "GLuint", - "name": "sampler" + "name": "sampler", + "object": true }, { "type": "GLenum", @@ -2716,7 +3114,8 @@ "type": "GLfloat", "name": "param" } - ] + ], + "category": "command" }, { "name": "glSamplerParameteri", @@ -2724,7 +3123,8 @@ "args": [ { "type": "GLuint", - "name": "sampler" + "name": "sampler", + "object": true }, { "type": "GLenum", @@ -2734,7 +3134,8 @@ "type": "GLint", "name": "param" } - ] + ], + "category": "command" }, { "name": "glScissor", @@ -2756,7 +3157,8 @@ "type": "GLsizei", "name": "height" } - ] + ], + "category": "command" }, { "name": "glShaderSource", @@ -2764,7 +3166,8 @@ "args": [ { "type": "GLuint", - "name": "shader" + "name": "shader", + "object": true }, { "type": "GLsizei", @@ -2778,6 +3181,17 @@ "type": "GLint const*", "name": "length" } + ], + "category": "custom", + "wire_command": [ + { + "type": "WebGLObjectId", + "name": "shader" + }, + { + "type": "WebGLDataSpan", + "name": "source" + } ] }, { @@ -2796,7 +3210,8 @@ "type": "GLuint", "name": "mask" } - ] + ], + "category": "command" }, { "name": "glStencilFuncSeparate", @@ -2818,7 +3233,8 @@ "type": "GLuint", "name": "mask" } - ] + ], + "category": "command" }, { "name": "glStencilMask", @@ -2828,7 +3244,8 @@ "type": "GLuint", "name": "mask" } - ] + ], + "category": "command" }, { "name": "glStencilMaskSeparate", @@ -2842,7 +3259,8 @@ "type": "GLuint", "name": "mask" } - ] + ], + "category": "command" }, { "name": "glStencilOp", @@ -2860,7 +3278,8 @@ "type": "GLenum", "name": "zpass" } - ] + ], + "category": "command" }, { "name": "glStencilOpSeparate", @@ -2882,7 +3301,8 @@ "type": "GLenum", "name": "dppass" } - ] + ], + "category": "command" }, { "name": "glTexImage2DRobustANGLE", @@ -2926,9 +3346,12 @@ }, { "type": "void const*", - "name": "pixels" + "name": "pixels", + "payload": "bufSize", + "nullable": true } - ] + ], + "category": "command" }, { "name": "glTexImage3DRobustANGLE", @@ -2976,9 +3399,12 @@ }, { "type": "void const*", - "name": "pixels" + "name": "pixels", + "payload": "bufSize", + "nullable": true } - ] + ], + "category": "command" }, { "name": "glTexParameterf", @@ -2996,7 +3422,8 @@ "type": "GLfloat", "name": "param" } - ] + ], + "category": "command" }, { "name": "glTexParameteri", @@ -3014,7 +3441,8 @@ "type": "GLint", "name": "param" } - ] + ], + "category": "command" }, { "name": "glTexStorage2D", @@ -3040,7 +3468,8 @@ "type": "GLsizei", "name": "height" } - ] + ], + "category": "command" }, { "name": "glTexStorage3D", @@ -3070,7 +3499,8 @@ "type": "GLsizei", "name": "depth" } - ] + ], + "category": "command" }, { "name": "glTexSubImage2DRobustANGLE", @@ -3114,9 +3544,12 @@ }, { "type": "void const*", - "name": "pixels" + "name": "pixels", + "payload": "bufSize", + "nullable": true } - ] + ], + "category": "command" }, { "name": "glTexSubImage3DRobustANGLE", @@ -3168,9 +3601,12 @@ }, { "type": "void const*", - "name": "pixels" + "name": "pixels", + "payload": "bufSize", + "nullable": true } - ] + ], + "category": "command" }, { "name": "glTransformFeedbackVaryings", @@ -3178,7 +3614,8 @@ "args": [ { "type": "GLuint", - "name": "program" + "name": "program", + "object": true }, { "type": "GLsizei", @@ -3192,6 +3629,25 @@ "type": "GLenum", "name": "bufferMode" } + ], + "category": "custom", + "wire_command": [ + { + "type": "WebGLObjectId", + "name": "program" + }, + { + "type": "GLsizei", + "name": "count" + }, + { + "type": "WebGLDataSpan", + "name": "varyings" + }, + { + "type": "GLenum", + "name": "buffer_mode" + } ] }, { @@ -3206,7 +3662,8 @@ "type": "GLfloat", "name": "v0" } - ] + ], + "category": "command" }, { "name": "glUniform1fv", @@ -3222,9 +3679,11 @@ }, { "type": "GLfloat const*", - "name": "value" + "name": "value", + "payload": "count * 1 * sizeof(GLfloat)" } - ] + ], + "category": "command" }, { "name": "glUniform1i", @@ -3238,7 +3697,8 @@ "type": "GLint", "name": "v0" } - ] + ], + "category": "command" }, { "name": "glUniform1iv", @@ -3254,9 +3714,11 @@ }, { "type": "GLint const*", - "name": "value" + "name": "value", + "payload": "count * 1 * sizeof(GLint)" } - ] + ], + "category": "command" }, { "name": "glUniform1ui", @@ -3270,7 +3732,8 @@ "type": "GLuint", "name": "v0" } - ] + ], + "category": "command" }, { "name": "glUniform1uiv", @@ -3286,9 +3749,11 @@ }, { "type": "GLuint const*", - "name": "value" + "name": "value", + "payload": "count * 1 * sizeof(GLuint)" } - ] + ], + "category": "command" }, { "name": "glUniform2f", @@ -3306,7 +3771,8 @@ "type": "GLfloat", "name": "v1" } - ] + ], + "category": "command" }, { "name": "glUniform2fv", @@ -3322,9 +3788,11 @@ }, { "type": "GLfloat const*", - "name": "value" + "name": "value", + "payload": "count * 2 * sizeof(GLfloat)" } - ] + ], + "category": "command" }, { "name": "glUniform2i", @@ -3342,7 +3810,8 @@ "type": "GLint", "name": "v1" } - ] + ], + "category": "command" }, { "name": "glUniform2iv", @@ -3358,9 +3827,11 @@ }, { "type": "GLint const*", - "name": "value" + "name": "value", + "payload": "count * 2 * sizeof(GLint)" } - ] + ], + "category": "command" }, { "name": "glUniform2ui", @@ -3378,7 +3849,8 @@ "type": "GLuint", "name": "v1" } - ] + ], + "category": "command" }, { "name": "glUniform2uiv", @@ -3394,9 +3866,11 @@ }, { "type": "GLuint const*", - "name": "value" + "name": "value", + "payload": "count * 2 * sizeof(GLuint)" } - ] + ], + "category": "command" }, { "name": "glUniform3f", @@ -3418,7 +3892,8 @@ "type": "GLfloat", "name": "v2" } - ] + ], + "category": "command" }, { "name": "glUniform3fv", @@ -3434,9 +3909,11 @@ }, { "type": "GLfloat const*", - "name": "value" + "name": "value", + "payload": "count * 3 * sizeof(GLfloat)" } - ] + ], + "category": "command" }, { "name": "glUniform3i", @@ -3458,7 +3935,8 @@ "type": "GLint", "name": "v2" } - ] + ], + "category": "command" }, { "name": "glUniform3iv", @@ -3474,9 +3952,11 @@ }, { "type": "GLint const*", - "name": "value" + "name": "value", + "payload": "count * 3 * sizeof(GLint)" } - ] + ], + "category": "command" }, { "name": "glUniform3ui", @@ -3498,7 +3978,8 @@ "type": "GLuint", "name": "v2" } - ] + ], + "category": "command" }, { "name": "glUniform3uiv", @@ -3514,9 +3995,11 @@ }, { "type": "GLuint const*", - "name": "value" + "name": "value", + "payload": "count * 3 * sizeof(GLuint)" } - ] + ], + "category": "command" }, { "name": "glUniform4f", @@ -3542,7 +4025,8 @@ "type": "GLfloat", "name": "v3" } - ] + ], + "category": "command" }, { "name": "glUniform4fv", @@ -3558,9 +4042,11 @@ }, { "type": "GLfloat const*", - "name": "value" + "name": "value", + "payload": "count * 4 * sizeof(GLfloat)" } - ] + ], + "category": "command" }, { "name": "glUniform4i", @@ -3586,7 +4072,8 @@ "type": "GLint", "name": "v3" } - ] + ], + "category": "command" }, { "name": "glUniform4iv", @@ -3602,9 +4089,11 @@ }, { "type": "GLint const*", - "name": "value" + "name": "value", + "payload": "count * 4 * sizeof(GLint)" } - ] + ], + "category": "command" }, { "name": "glUniform4ui", @@ -3630,7 +4119,8 @@ "type": "GLuint", "name": "v3" } - ] + ], + "category": "command" }, { "name": "glUniform4uiv", @@ -3646,9 +4136,11 @@ }, { "type": "GLuint const*", - "name": "value" + "name": "value", + "payload": "count * 4 * sizeof(GLuint)" } - ] + ], + "category": "command" }, { "name": "glUniformBlockBinding", @@ -3656,7 +4148,8 @@ "args": [ { "type": "GLuint", - "name": "program" + "name": "program", + "object": true }, { "type": "GLuint", @@ -3666,7 +4159,8 @@ "type": "GLuint", "name": "uniformBlockBinding" } - ] + ], + "category": "command" }, { "name": "glUniformMatrix2fv", @@ -3686,9 +4180,11 @@ }, { "type": "GLfloat const*", - "name": "value" + "name": "value", + "payload": "count * 4 * sizeof(GLfloat)" } - ] + ], + "category": "command" }, { "name": "glUniformMatrix2x3fv", @@ -3708,9 +4204,11 @@ }, { "type": "GLfloat const*", - "name": "value" + "name": "value", + "payload": "count * 6 * sizeof(GLfloat)" } - ] + ], + "category": "command" }, { "name": "glUniformMatrix2x4fv", @@ -3730,9 +4228,11 @@ }, { "type": "GLfloat const*", - "name": "value" + "name": "value", + "payload": "count * 8 * sizeof(GLfloat)" } - ] + ], + "category": "command" }, { "name": "glUniformMatrix3fv", @@ -3752,9 +4252,11 @@ }, { "type": "GLfloat const*", - "name": "value" + "name": "value", + "payload": "count * 9 * sizeof(GLfloat)" } - ] + ], + "category": "command" }, { "name": "glUniformMatrix3x2fv", @@ -3774,9 +4276,11 @@ }, { "type": "GLfloat const*", - "name": "value" + "name": "value", + "payload": "count * 6 * sizeof(GLfloat)" } - ] + ], + "category": "command" }, { "name": "glUniformMatrix3x4fv", @@ -3796,9 +4300,11 @@ }, { "type": "GLfloat const*", - "name": "value" + "name": "value", + "payload": "count * 12 * sizeof(GLfloat)" } - ] + ], + "category": "command" }, { "name": "glUniformMatrix4fv", @@ -3818,9 +4324,11 @@ }, { "type": "GLfloat const*", - "name": "value" + "name": "value", + "payload": "count * 16 * sizeof(GLfloat)" } - ] + ], + "category": "command" }, { "name": "glUniformMatrix4x2fv", @@ -3840,9 +4348,11 @@ }, { "type": "GLfloat const*", - "name": "value" + "name": "value", + "payload": "count * 8 * sizeof(GLfloat)" } - ] + ], + "category": "command" }, { "name": "glUniformMatrix4x3fv", @@ -3862,9 +4372,11 @@ }, { "type": "GLfloat const*", - "name": "value" + "name": "value", + "payload": "count * 12 * sizeof(GLfloat)" } - ] + ], + "category": "command" }, { "name": "glUnmapBuffer", @@ -3874,7 +4386,8 @@ "type": "GLenum", "name": "target" } - ] + ], + "category": "custom" }, { "name": "glUseProgram", @@ -3882,9 +4395,11 @@ "args": [ { "type": "GLuint", - "name": "program" + "name": "program", + "object": true } - ] + ], + "category": "command" }, { "name": "glValidateProgram", @@ -3892,9 +4407,11 @@ "args": [ { "type": "GLuint", - "name": "program" + "name": "program", + "object": true } - ] + ], + "category": "command" }, { "name": "glVertexAttrib1f", @@ -3908,7 +4425,8 @@ "type": "GLfloat", "name": "x" } - ] + ], + "category": "command" }, { "name": "glVertexAttrib1fv", @@ -3920,9 +4438,11 @@ }, { "type": "GLfloat const*", - "name": "v" + "name": "v", + "payload": "1 * sizeof(GLfloat)" } - ] + ], + "category": "command" }, { "name": "glVertexAttrib2f", @@ -3940,7 +4460,8 @@ "type": "GLfloat", "name": "y" } - ] + ], + "category": "command" }, { "name": "glVertexAttrib2fv", @@ -3952,9 +4473,11 @@ }, { "type": "GLfloat const*", - "name": "v" + "name": "v", + "payload": "2 * sizeof(GLfloat)" } - ] + ], + "category": "command" }, { "name": "glVertexAttrib3f", @@ -3976,7 +4499,8 @@ "type": "GLfloat", "name": "z" } - ] + ], + "category": "command" }, { "name": "glVertexAttrib3fv", @@ -3988,9 +4512,11 @@ }, { "type": "GLfloat const*", - "name": "v" + "name": "v", + "payload": "3 * sizeof(GLfloat)" } - ] + ], + "category": "command" }, { "name": "glVertexAttrib4f", @@ -4016,7 +4542,8 @@ "type": "GLfloat", "name": "w" } - ] + ], + "category": "command" }, { "name": "glVertexAttrib4fv", @@ -4028,9 +4555,11 @@ }, { "type": "GLfloat const*", - "name": "v" + "name": "v", + "payload": "4 * sizeof(GLfloat)" } - ] + ], + "category": "command" }, { "name": "glVertexAttribDivisor", @@ -4044,7 +4573,8 @@ "type": "GLuint", "name": "divisor" } - ] + ], + "category": "command" }, { "name": "glVertexAttribDivisorANGLE", @@ -4058,7 +4588,8 @@ "type": "GLuint", "name": "divisor" } - ] + ], + "category": "command" }, { "name": "glVertexAttribI4i", @@ -4084,7 +4615,8 @@ "type": "GLint", "name": "w" } - ] + ], + "category": "command" }, { "name": "glVertexAttribI4iv", @@ -4096,9 +4628,11 @@ }, { "type": "GLint const*", - "name": "v" + "name": "v", + "payload": "4 * sizeof(GLint)" } - ] + ], + "category": "command" }, { "name": "glVertexAttribI4ui", @@ -4124,7 +4658,8 @@ "type": "GLuint", "name": "w" } - ] + ], + "category": "command" }, { "name": "glVertexAttribI4uiv", @@ -4136,9 +4671,11 @@ }, { "type": "GLuint const*", - "name": "v" + "name": "v", + "payload": "4 * sizeof(GLuint)" } - ] + ], + "category": "command" }, { "name": "glVertexAttribIPointer", @@ -4162,9 +4699,11 @@ }, { "type": "void const*", - "name": "pointer" + "name": "pointer", + "offset": true } - ] + ], + "category": "command" }, { "name": "glVertexAttribPointer", @@ -4192,9 +4731,11 @@ }, { "type": "void const*", - "name": "pointer" + "name": "pointer", + "offset": true } - ] + ], + "category": "command" }, { "name": "glViewport", @@ -4216,7 +4757,8 @@ "type": "GLsizei", "name": "height" } - ] + ], + "category": "command" }, { "name": "glWaitSync", @@ -4224,7 +4766,8 @@ "args": [ { "type": "GLsync", - "name": "sync" + "name": "sync", + "object": true }, { "type": "GLbitfield", @@ -4234,6 +4777,159 @@ "type": "GLuint64", "name": "timeout" } + ], + "category": "command" + }, + { + "name": "SetDrawingBufferSize", + "category": "builtin-command", + "wire_command": [ + { + "type": "int", + "name": "width" + }, + { + "type": "int", + "name": "height" + } + ] + }, + { + "name": "ReadPixelsIntoPixelPackBuffer", + "category": "builtin-command", + "wire_command": [ + { + "type": "GLint", + "name": "x" + }, + { + "type": "GLint", + "name": "y" + }, + { + "type": "GLsizei", + "name": "width" + }, + { + "type": "GLsizei", + "name": "height" + }, + { + "type": "GLenum", + "name": "format" + }, + { + "type": "GLenum", + "name": "type" + }, + { + "type": "GLintptr", + "name": "offset" + } + ] + }, + { + "name": "TexImage2DFromBitmap", + "category": "builtin-command", + "wire_command": [ + { + "type": "GLenum", + "name": "target" + }, + { + "type": "GLint", + "name": "level" + }, + { + "type": "GLint", + "name": "internalformat" + }, + { + "type": "GLenum", + "name": "format" + }, + { + "type": "GLenum", + "name": "type" + }, + { + "type": "u32", + "name": "bitmap_index" + }, + { + "type": "bool", + "name": "has_explicit_destination_size" + }, + { + "type": "GLsizei", + "name": "destination_width" + }, + { + "type": "GLsizei", + "name": "destination_height" + }, + { + "type": "bool", + "name": "flip_y" + }, + { + "type": "bool", + "name": "premultiply_alpha" + } + ] + }, + { + "name": "TexSubImage2DFromBitmap", + "category": "builtin-command", + "wire_command": [ + { + "type": "GLenum", + "name": "target" + }, + { + "type": "GLint", + "name": "level" + }, + { + "type": "GLint", + "name": "xoffset" + }, + { + "type": "GLint", + "name": "yoffset" + }, + { + "type": "GLenum", + "name": "format" + }, + { + "type": "GLenum", + "name": "type" + }, + { + "type": "u32", + "name": "bitmap_index" + }, + { + "type": "bool", + "name": "has_explicit_destination_size" + }, + { + "type": "GLsizei", + "name": "destination_width" + }, + { + "type": "GLsizei", + "name": "destination_height" + }, + { + "type": "bool", + "name": "flip_y" + }, + { + "type": "bool", + "name": "premultiply_alpha" + } ] } ] diff --git a/Libraries/LibWeb/WebGL/RemoteWebGLTransport.h b/Libraries/LibWeb/WebGL/RemoteWebGLTransport.h new file mode 100644 index 0000000000..22744f04fd --- /dev/null +++ b/Libraries/LibWeb/WebGL/RemoteWebGLTransport.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2026, Aliaksandr Kalenik + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Web::WebGL { + +class WEB_API RemoteWebGLTransport : public RefCounted { +public: + virtual ~RemoteWebGLTransport() = default; + + struct CreateResult { + bool success { false }; + Painting::CanvasId canvas_id { 0 }; + Vector supported_extensions; + }; + virtual CreateResult create_context(WebGLVersion, Gfx::IntSize initial_size, bool depth, bool stencil, bool antialias) = 0; + virtual void destroy_context(Painting::CanvasId) = 0; + + virtual void send_commands(Painting::CanvasId, ByteBuffer const&, Vector const& bitmaps) = 0; + virtual void present_canvas(Painting::CanvasId, bool preserve_drawing_buffer) = 0; + virtual ByteBuffer sync_call(Painting::CanvasId, ByteBuffer request) = 0; + virtual ReadPixelsResult read_pixels_robust_angle(Painting::CanvasId, GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei buf_size, Core::AnonymousBuffer pixels) = 0; + virtual void read_buffer_sub_data(Painting::CanvasId, GLenum target, GLintptr offset, GLintptr size, Core::AnonymousBuffer data) = 0; + + virtual Gfx::ShareableBitmap read_back_drawing_buffer(Painting::CanvasId, Gfx::IntRect const&) = 0; +}; + +} diff --git a/Libraries/LibWeb/WebGL/Types.h b/Libraries/LibWeb/WebGL/Types.h index 9dc4a39fb7..c0f4ef1cac 100644 --- a/Libraries/LibWeb/WebGL/Types.h +++ b/Libraries/LibWeb/WebGL/Types.h @@ -22,4 +22,24 @@ using GLchar = char; // Since this conflicts with the original definition of GLsync, the suffix "Internal" has been added. using GLsyncInternal = void*; +enum class WebGLVersion { + WebGL1, + WebGL2, +}; + +static constexpr int max_webgl_drawing_buffer_dimension = 16384; + +using WebGLObjectId = u32; + +struct WebGLDataSpan { + u32 offset { 0 }; + u32 size { 0 }; +}; + +struct ReadPixelsResult { + GLsizei length { 0 }; + GLsizei columns { 0 }; + GLsizei rows { 0 }; +}; + } diff --git a/Libraries/LibWeb/WebGL/WebGLCommandList.cpp b/Libraries/LibWeb/WebGL/WebGLCommandList.cpp new file mode 100644 index 0000000000..a8f41f5e80 --- /dev/null +++ b/Libraries/LibWeb/WebGL/WebGLCommandList.cpp @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2026, Aliaksandr Kalenik + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include + +namespace Web::WebGL { + +static size_t payload_layout_size(ReadonlyBytes payload, ReadonlyBytes inline_data, ReadonlyBytes more_inline_data = {}) +{ + auto size = payload.size(); + if (!inline_data.is_empty()) + size = align_up_to(size, WebGLCommandList::command_alignment) + inline_data.size(); + if (!more_inline_data.is_empty()) + size = align_up_to(size, WebGLCommandList::command_alignment) + more_inline_data.size(); + return size; +} + +static void write_payload(Bytes destination, ReadonlyBytes payload, ReadonlyBytes inline_data, ReadonlyBytes more_inline_data = {}) +{ + __builtin_memcpy(destination.data(), payload.data(), payload.size()); + auto cursor = payload.size(); + for (auto blob : { inline_data, more_inline_data }) { + if (blob.is_empty()) + continue; + auto offset = align_up_to(cursor, WebGLCommandList::command_alignment); + __builtin_memset(destination.offset_pointer(cursor), 0, offset - cursor); + __builtin_memcpy(destination.offset_pointer(offset), blob.data(), blob.size()); + cursor = offset + blob.size(); + } + __builtin_memset(destination.offset_pointer(cursor), 0, destination.size() - cursor); +} + +void WebGLCommandList::append_bytes(WebGLCommandType type, ReadonlyBytes payload, ReadonlyBytes inline_data) +{ + VERIFY(m_bytes.size() % command_alignment == 0); + + auto record_size = sizeof(WebGLCommandHeader) + payload_layout_size(payload, inline_data); + auto padded_record_size = align_up_to(record_size, command_alignment); + auto padded_payload_size = padded_record_size - sizeof(WebGLCommandHeader); + VERIFY(padded_payload_size <= NumericLimits::max()); + + WebGLCommandHeader header { + .type = type, + .payload_size = static_cast(padded_payload_size), + }; + + auto record_offset = m_bytes.size(); + m_bytes.resize(record_offset + padded_record_size); + auto record = m_bytes.bytes().slice(record_offset); + __builtin_memcpy(record.data(), &header, sizeof(header)); + write_payload(record.slice(sizeof(header)), payload, inline_data); +} + +ByteBuffer WebGLSyncCall::encode_request_bytes(WebGLSyncCallType type, ReadonlyBytes request, ReadonlyBytes inline_data) +{ + auto padded_payload_size = align_up_to(payload_layout_size(request, inline_data), WebGLCommandList::command_alignment); + VERIFY(padded_payload_size <= NumericLimits::max()); + + WebGLSyncCallHeader header { + .type = type, + .payload_size = static_cast(padded_payload_size), + }; + + auto bytes = MUST(ByteBuffer::create_uninitialized(sizeof(header) + padded_payload_size)); + __builtin_memcpy(bytes.data(), &header, sizeof(header)); + write_payload(bytes.bytes().slice(sizeof(header)), request, inline_data); + return bytes; +} + +ByteBuffer WebGLSyncCall::encode_reply_bytes(ReadonlyBytes reply, ReadonlyBytes inline_data, ReadonlyBytes more_inline_data) +{ + VERIFY(more_inline_data.is_empty() || !inline_data.is_empty()); + + auto reply_size = align_up_to(payload_layout_size(reply, inline_data, more_inline_data), WebGLCommandList::command_alignment); + auto bytes = MUST(ByteBuffer::create_uninitialized(reply_size)); + write_payload(bytes, reply, inline_data, more_inline_data); + return bytes; +} + +} diff --git a/Libraries/LibWeb/WebGL/WebGLCommandList.h b/Libraries/LibWeb/WebGL/WebGLCommandList.h new file mode 100644 index 0000000000..36169a0dfd --- /dev/null +++ b/Libraries/LibWeb/WebGL/WebGLCommandList.h @@ -0,0 +1,169 @@ +/* + * Copyright (c) 2026, Aliaksandr Kalenik + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace Web::WebGL { + +struct WebGLCommandHeader { + WebGLCommandType type; + u32 payload_size { 0 }; // command struct + inline data + trailing padding +}; +static_assert(IsTriviallyCopyable); + +class WEB_API WebGLCommandList { +public: + static constexpr size_t command_alignment = 16; + + static constexpr u32 first_inline_data_offset(size_t command_size) + { + return static_cast(align_up_to(command_size, command_alignment)); + } + + static constexpr u32 next_inline_data_offset(WebGLDataSpan previous) + { + return static_cast(align_up_to(previous.offset + previous.size, command_alignment)); + } + + template + void append(Command const& command, ReadonlyBytes inline_data = {}) + { + append_bytes(Command::command_type, { &command, sizeof(command) }, inline_data); + } + + void append_bytes(WebGLCommandType, ReadonlyBytes payload, ReadonlyBytes inline_data); + + template + static ErrorOr for_each_command(ReadonlyBytes bytes, Callback&& callback) + { + size_t offset = 0; + while (offset < bytes.size()) { + if (bytes.size() - offset < sizeof(WebGLCommandHeader)) + return Error::from_string_literal("Truncated WebGL command header"); + WebGLCommandHeader header; + __builtin_memcpy(&header, bytes.offset_pointer(offset), sizeof(header)); + if (to_underlying(header.type) >= webgl_command_type_count) + return Error::from_string_literal("Invalid WebGL command type"); + if (header.payload_size > bytes.size() - offset - sizeof(header)) + return Error::from_string_literal("Truncated WebGL command payload"); + auto payload = bytes.slice(offset + sizeof(header), header.payload_size); + TRY(visit_webgl_command_type(header.type, [&]() -> ErrorOr { + if (payload.size() < sizeof(Command)) + return Error::from_string_literal("WebGL command payload too small"); + Command command; + __builtin_memcpy(&command, payload.data(), sizeof(Command)); + return callback(command, payload); + })); + offset += sizeof(WebGLCommandHeader) + header.payload_size; + } + return {}; + } + + static ReadonlyBytes resolve_data_span(ReadonlyBytes payload, WebGLDataSpan span) + { + VERIFY(span.offset <= payload.size()); + VERIFY(span.size <= payload.size() - span.offset); + return payload.slice(span.offset, span.size); + } + + template + static Span resolve_typed_span(ReadonlyBytes payload, WebGLDataSpan span) + { + auto bytes = resolve_data_span(payload, span); + VERIFY(reinterpret_cast(bytes.data()) % alignof(T) == 0); + VERIFY(bytes.size() % sizeof(T) == 0); + return Span { reinterpret_cast(bytes.data()), bytes.size() / sizeof(T) }; + } + + static ReadonlyBytes resolve_string_span(ReadonlyBytes payload, WebGLDataSpan span) + { + auto bytes = resolve_data_span(payload, span); + VERIFY(!bytes.is_empty()); + VERIFY(bytes[bytes.size() - 1] == 0); + return bytes; + } + + static void copy_data_span(ReadonlyBytes payload, WebGLDataSpan span, Bytes destination) + { + auto resolved = resolve_data_span(payload, span); + VERIFY(resolved.size() <= destination.size()); + __builtin_memcpy(destination.data(), resolved.data(), resolved.size()); + } + + ReadonlyBytes bytes() const { return m_bytes; } + ByteBuffer const& buffer() const { return m_bytes; } + void clear_with_capacity() { m_bytes.set_size(0); } + bool is_empty() const { return m_bytes.is_empty(); } + size_t size_in_bytes() const { return m_bytes.size(); } + +private: + ByteBuffer m_bytes; +}; + +struct WebGLSyncCallHeader { + WebGLSyncCallType type; + u32 payload_size { 0 }; +}; +static_assert(IsTriviallyCopyable); + +class WEB_API WebGLSyncCall { +public: + template + static ByteBuffer encode_request(typename Call::Request const& request, ReadonlyBytes inline_data = {}) + { + return encode_request_bytes(Call::call_type, { &request, sizeof(request) }, inline_data); + } + + template + static ErrorOr dispatch_request(ReadonlyBytes bytes, Callback&& callback) + { + if (bytes.size() < sizeof(WebGLSyncCallHeader)) + return Error::from_string_literal("Truncated WebGL sync call header"); + WebGLSyncCallHeader header; + __builtin_memcpy(&header, bytes.data(), sizeof(header)); + if (to_underlying(header.type) >= webgl_sync_call_type_count) + return Error::from_string_literal("Invalid WebGL sync call type"); + if (header.payload_size != bytes.size() - sizeof(header)) + return Error::from_string_literal("Truncated WebGL sync call payload"); + auto payload = bytes.slice(sizeof(header), header.payload_size); + return visit_webgl_sync_call_type(header.type, [&]() -> ErrorOr { + if (payload.size() < sizeof(typename Call::Request)) + return Error::from_string_literal("WebGL sync call payload too small"); + typename Call::Request request; + __builtin_memcpy(&request, payload.data(), sizeof(request)); + return callback.template operator()(request, payload); + }); + } + + template + static ByteBuffer encode_reply(Reply const& reply, ReadonlyBytes inline_data = {}, ReadonlyBytes more_inline_data = {}) + { + return encode_reply_bytes({ &reply, sizeof(reply) }, inline_data, more_inline_data); + } + + template + static Reply decode_reply(ReadonlyBytes bytes) + { + VERIFY(bytes.size() >= sizeof(Reply)); + Reply reply; + __builtin_memcpy(&reply, bytes.data(), sizeof(reply)); + return reply; + } + +private: + static ByteBuffer encode_request_bytes(WebGLSyncCallType, ReadonlyBytes request, ReadonlyBytes inline_data); + static ByteBuffer encode_reply_bytes(ReadonlyBytes reply, ReadonlyBytes inline_data, ReadonlyBytes more_inline_data); +}; + +} diff --git a/Libraries/LibWeb/WebGL/WebGLContextProxyBase.cpp b/Libraries/LibWeb/WebGL/WebGLContextProxyBase.cpp new file mode 100644 index 0000000000..e7ac865e75 --- /dev/null +++ b/Libraries/LibWeb/WebGL/WebGLContextProxyBase.cpp @@ -0,0 +1,258 @@ +/* + * Copyright (c) 2026, Aliaksandr Kalenik + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include +#include + +namespace Web::WebGL { + +WebGLContextProxyBase::WebGLContextProxyBase(NonnullRefPtr transport, Painting::CanvasId canvas_id, WebGLVersion webgl_version, Vector supported_extensions) + : m_transport(move(transport)) + , m_canvas_id(canvas_id) + , m_webgl_version(webgl_version) + , m_supported_extensions(move(supported_extensions)) +{ +} + +WebGLContextProxyBase::~WebGLContextProxyBase() +{ + m_transport->destroy_context(m_canvas_id); +} + +void WebGLContextProxyBase::flush_commands() +{ + if (m_commands.is_empty()) + return; + m_transport->send_commands(m_canvas_id, m_commands.buffer(), m_pending_bitmaps); + m_commands.clear_with_capacity(); + m_pending_bitmaps.clear_with_capacity(); +} + +ByteBuffer WebGLContextProxyBase::send_sync_call(ByteBuffer request) +{ + if (m_lost) + return {}; + flush_commands(); + return m_transport->sync_call(m_canvas_id, move(request)); +} + +ReadPixelsResult WebGLContextProxyBase::read_pixels_robust_angle_into_shared_buffer(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei buf_size, Core::AnonymousBuffer const& pixels) +{ + flush_commands(); + return m_transport->read_pixels_robust_angle(m_canvas_id, x, y, width, height, format, type, buf_size, pixels); +} + +void WebGLContextProxyBase::set_size(Gfx::IntSize const& size) +{ + record(Commands::SetDrawingBufferSize { .width = size.width(), .height = size.height() }); +} + +void WebGLContextProxyBase::present_canvas_for_compositing(bool preserve_drawing_buffer) +{ + flush_commands(); + m_transport->present_canvas(m_canvas_id, preserve_drawing_buffer); +} + +RefPtr WebGLContextProxyBase::read_back_drawing_buffer(Gfx::IntRect const& rect) +{ + if (m_lost) + return nullptr; + flush_commands(); + auto bitmap = m_transport->read_back_drawing_buffer(m_canvas_id, rect); + if (!bitmap.is_valid()) + return nullptr; + return bitmap.bitmap(); +} + +void WebGLContextProxyBase::read_pixels_into_pixel_pack_buffer(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, long long offset) +{ + record(Commands::ReadPixelsIntoPixelPackBuffer { + .x = x, + .y = y, + .width = width, + .height = height, + .format = format, + .type = type, + .offset = static_cast(offset), + }); +} + +void WebGLContextProxyBase::tex_image2d_from_bitmap(GLenum target, GLint level, GLint internalformat, GLenum format, GLenum type, Gfx::DecodedImageFrame frame, Optional destination_size, bool flip_y, bool premultiply_alpha) +{ + if (m_lost) + return; + auto bitmap_index = static_cast(m_pending_bitmaps.size()); + m_pending_bitmaps.append(move(frame)); + auto has_explicit_destination_size = destination_size.has_value(); + record(Commands::TexImage2DFromBitmap { + .target = target, + .level = level, + .internalformat = internalformat, + .format = format, + .type = type, + .bitmap_index = bitmap_index, + .has_explicit_destination_size = has_explicit_destination_size, + .destination_width = has_explicit_destination_size ? destination_size->width() : 0, + .destination_height = has_explicit_destination_size ? destination_size->height() : 0, + .flip_y = flip_y, + .premultiply_alpha = premultiply_alpha, + }); +} + +void WebGLContextProxyBase::tex_sub_image2d_from_bitmap(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLenum format, GLenum type, Gfx::DecodedImageFrame frame, Optional destination_size, bool flip_y, bool premultiply_alpha) +{ + if (m_lost) + return; + auto bitmap_index = static_cast(m_pending_bitmaps.size()); + m_pending_bitmaps.append(move(frame)); + auto has_explicit_destination_size = destination_size.has_value(); + record(Commands::TexSubImage2DFromBitmap { + .target = target, + .level = level, + .xoffset = xoffset, + .yoffset = yoffset, + .format = format, + .type = type, + .bitmap_index = bitmap_index, + .has_explicit_destination_size = has_explicit_destination_size, + .destination_width = has_explicit_destination_size ? destination_size->width() : 0, + .destination_height = has_explicit_destination_size ? destination_size->height() : 0, + .flip_y = flip_y, + .premultiply_alpha = premultiply_alpha, + }); +} + +void WebGLContextProxyBase::read_buffer_sub_data(GLenum target, long long offset, Bytes destination) +{ + if (m_lost || destination.is_empty()) + return; + + auto shared_data_or_error = Core::AnonymousBuffer::create_with_size(destination.size()); + + auto shared_data = shared_data_or_error.release_value_but_fixme_should_propagate_errors(); + flush_commands(); + m_transport->read_buffer_sub_data(m_canvas_id, target, static_cast(offset), static_cast(destination.size()), shared_data); + if (m_lost) + return; + __builtin_memcpy(destination.data(), shared_data.data(), destination.size()); +} + +void WebGLContextProxy::shader_source(GLuint shader, GLsizei count, GLchar const* const* string, GLint const* length) +{ + VERIFY(count == 1); + auto source_length = length ? static_cast(length[0]) : __builtin_strlen(string[0]); + ByteBuffer source_bytes = MUST(ByteBuffer::create_uninitialized(source_length + 1)); + __builtin_memcpy(source_bytes.data(), string[0], source_length); + source_bytes[source_length] = 0; + + Commands::ShaderSource command { .shader = shader, .source = {} }; + command.source = { WebGLCommandList::first_inline_data_offset(sizeof(command)), static_cast(source_bytes.size()) }; + record(command, source_bytes); +} + +static ByteBuffer pack_strings(GLsizei count, GLchar const* const* strings) +{ + StringBuilder builder; + for (GLsizei i = 0; i < count; ++i) { + builder.append({ strings[i], __builtin_strlen(strings[i]) }); + builder.append('\0'); + } + return MUST(builder.to_byte_buffer()); +} + +void WebGLContextProxy::transform_feedback_varyings(GLuint program, GLsizei count, GLchar const* const* varyings, GLenum bufferMode) +{ + auto varyings_bytes = pack_strings(count, varyings); + Commands::TransformFeedbackVaryings command { .program = program, .count = count, .varyings = {}, .buffer_mode = bufferMode }; + command.varyings = { WebGLCommandList::first_inline_data_offset(sizeof(command)), static_cast(varyings_bytes.size()) }; + record(command, varyings_bytes); +} + +void WebGLContextProxy::read_pixels_robust_angle(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, GLsizei* length, GLsizei* columns, GLsizei* rows, void* pixels) +{ + if (is_lost()) + return; + + Core::AnonymousBuffer shared_pixels; + if (bufSize > 0) { + shared_pixels = Core::AnonymousBuffer::create_with_size(static_cast(bufSize)).release_value_but_fixme_should_propagate_errors(); + } + + auto result = read_pixels_robust_angle_into_shared_buffer(x, y, width, height, format, type, bufSize, shared_pixels); + if (is_lost()) + return; + if (length) + *length = result.length; + if (columns) + *columns = result.columns; + if (rows) + *rows = result.rows; + if (pixels && result.length > 0) { + VERIFY(result.length <= bufSize); + __builtin_memcpy(pixels, shared_pixels.data(), static_cast(result.length)); + } +} + +GLubyte const* WebGLContextProxy::get_string(GLenum name) +{ + if (auto cached = m_string_cache.get(name); cached.has_value()) + return cached.value()->data(); + + SyncCalls::GetString::Request request { .name = name }; + auto reply_bytes = send_sync_call(WebGLSyncCall::encode_request(request)); + if (is_lost()) + return reinterpret_cast(""); + auto reply = WebGLSyncCall::decode_reply(reply_bytes); + auto resolved = WebGLCommandList::resolve_string_span(reply_bytes, reply.value); + auto value = make(MUST(ByteBuffer::copy(resolved))); + auto const* data = value->data(); + m_string_cache.set(name, move(value)); + return data; +} + +void WebGLContextProxy::get_vertex_attrib_pointerv_robust_angle(GLuint index, GLenum pname, GLsizei bufSize, GLsizei* length, void** pointer) +{ + (void)bufSize; + SyncCalls::GetVertexAttribPointervRobustANGLE::Request request { .index = index, .pname = pname }; + auto reply_bytes = send_sync_call(WebGLSyncCall::encode_request(request)); + if (is_lost()) + return; + auto reply = WebGLSyncCall::decode_reply(reply_bytes); + if (length) + *length = 1; + if (pointer) + *pointer = reinterpret_cast(static_cast(reply.pointer)); +} + +void WebGLContextProxy::get_uniform_indices(GLuint program, GLsizei uniformCount, GLchar const* const* uniformNames, GLuint* uniformIndices) +{ + auto names_bytes = pack_strings(uniformCount, uniformNames); + SyncCalls::GetUniformIndices::Request request { .program = program, .uniform_count = uniformCount, .uniform_names = {} }; + request.uniform_names = { WebGLCommandList::first_inline_data_offset(sizeof(request)), static_cast(names_bytes.size()) }; + auto reply_bytes = send_sync_call(WebGLSyncCall::encode_request(request, names_bytes)); + if (is_lost()) + return; + auto reply = WebGLSyncCall::decode_reply(reply_bytes); + if (uniformIndices) + WebGLCommandList::copy_data_span(reply_bytes, reply.uniform_indices, { uniformIndices, static_cast(uniformCount) * sizeof(GLuint) }); +} + +void* WebGLContextProxy::map_buffer_range(GLenum, GLintptr, GLsizeiptr, GLbitfield) +{ + // getBufferSubData() goes through read_buffer_sub_data() instead; nothing else maps. + VERIFY_NOT_REACHED(); +} + +GLboolean WebGLContextProxy::unmap_buffer(GLenum) +{ + VERIFY_NOT_REACHED(); +} + +} diff --git a/Libraries/LibWeb/WebGL/WebGLContextProxyBase.h b/Libraries/LibWeb/WebGL/WebGLContextProxyBase.h new file mode 100644 index 0000000000..eb18264dc5 --- /dev/null +++ b/Libraries/LibWeb/WebGL/WebGLContextProxyBase.h @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2026, Aliaksandr Kalenik + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Web::WebGL { + +class WEB_API WebGLContextProxyBase { + AK_MAKE_NONCOPYABLE(WebGLContextProxyBase); + AK_MAKE_NONMOVABLE(WebGLContextProxyBase); + +public: + WebGLContextProxyBase(NonnullRefPtr, Painting::CanvasId, WebGLVersion, Vector supported_extensions); + ~WebGLContextProxyBase(); + + void flush_commands(); + Painting::CanvasId canvas_id() const { return m_canvas_id; } + + void make_current() { } + void notify_content_will_change() { } + u32 default_framebuffer() const { return 0; } + u32 default_renderbuffer() const { return 0; } + WebGLVersion webgl_version() const { return m_webgl_version; } + Vector const& get_supported_opengl_extensions() const { return m_supported_extensions; } + void set_size(Gfx::IntSize const&); + + void present_canvas_for_compositing(bool preserve_drawing_buffer); + + RefPtr read_back_drawing_buffer(Gfx::IntRect const&); + + void read_pixels_into_pixel_pack_buffer(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, long long offset); + void read_buffer_sub_data(GLenum target, long long offset, Bytes destination); + + void tex_image2d_from_bitmap(GLenum target, GLint level, GLint internalformat, GLenum format, GLenum type, Gfx::DecodedImageFrame, Optional destination_size, bool flip_y, bool premultiply_alpha); + void tex_sub_image2d_from_bitmap(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLenum format, GLenum type, Gfx::DecodedImageFrame, Optional destination_size, bool flip_y, bool premultiply_alpha); + + GLenum take_pending_local_error() + { + auto error = m_pending_local_error; + m_pending_local_error = 0; + return error; + } + +protected: + static constexpr size_t max_pending_command_bytes = 4 * MiB; + + WebGLObjectId allocate_object_id() { return m_next_object_id++; } + + void set_pending_local_error(GLenum error) + { + if (m_pending_local_error == 0) + m_pending_local_error = error; + } + ReadPixelsResult read_pixels_robust_angle_into_shared_buffer(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei buf_size, Core::AnonymousBuffer const& pixels); + + template + void record(Command const& command, ReadonlyBytes inline_data = {}) + { + if (m_lost) + return; + m_commands.append(command, inline_data); + if (m_commands.size_in_bytes() >= max_pending_command_bytes) + flush_commands(); + } + + ByteBuffer send_sync_call(ByteBuffer request); + bool is_lost() const { return m_lost; } + + HashMap> m_string_cache; + +private: + NonnullRefPtr m_transport; + Painting::CanvasId m_canvas_id { 0 }; + WebGLVersion m_webgl_version { WebGLVersion::WebGL1 }; + Vector m_supported_extensions; + WebGLCommandList m_commands; + Vector m_pending_bitmaps; + u32 m_next_object_id { 1 }; + bool m_lost { false }; + GLenum m_pending_local_error { 0 }; +}; + +} diff --git a/Meta/CMake/libweb_generators.cmake b/Meta/CMake/libweb_generators.cmake index 27e17f3af6..fc76471d39 100644 --- a/Meta/CMake/libweb_generators.cmake +++ b/Meta/CMake/libweb_generators.cmake @@ -254,8 +254,30 @@ function (generate_webgl_implementation) dependencies "${LADYBIRD_SOURCE_DIR}/Meta/Generators/libweb_webgl.py" ) + invoke_py_generator( + "WebGLCommands.cpp" + "generate_libweb_webgl_commands.py" + "${LIBWEB_INPUT_FOLDER}/WebGL/GLFunctions.json" + "WebGL/WebGLCommands.h" + "WebGL/WebGLCommands.cpp" + arguments -j "${LIBWEB_INPUT_FOLDER}/WebGL/GLFunctions.json" + dependencies "${LADYBIRD_SOURCE_DIR}/Meta/Generators/libweb_webgl.py" + ) + + invoke_py_generator( + "WebGLContextProxy.cpp" + "generate_libweb_webgl_proxy.py" + "${LIBWEB_INPUT_FOLDER}/WebGL/GLFunctions.json" + "WebGL/WebGLContextProxy.h" + "WebGL/WebGLContextProxy.cpp" + arguments -j "${LIBWEB_INPUT_FOLDER}/WebGL/GLFunctions.json" + dependencies "${LADYBIRD_SOURCE_DIR}/Meta/Generators/libweb_webgl.py" + ) + set(WEBGL_GENERATED_HEADERS "WebGL/GLFunctions.h" + "WebGL/WebGLCommands.h" + "WebGL/WebGLContextProxy.h" ) list(TRANSFORM WEBGL_GENERATED_HEADERS PREPEND "${CMAKE_CURRENT_BINARY_DIR}/") if (ENABLE_INSTALL_HEADERS) diff --git a/Meta/Generators/generate_libweb_webgl_commands.py b/Meta/Generators/generate_libweb_webgl_commands.py new file mode 100644 index 0000000000..42014f2e70 --- /dev/null +++ b/Meta/Generators/generate_libweb_webgl_commands.py @@ -0,0 +1,181 @@ +#!/usr/bin/env python3 + +# Copyright (c) 2026, Aliaksandr Kalenik +# +# SPDX-License-Identifier: BSD-2-Clause + +import sys + +from pathlib import Path +from typing import TextIO + +sys.path.append(str(Path(__file__).resolve().parent)) + +from libweb_webgl import command_name +from libweb_webgl import command_stream_entries +from libweb_webgl import command_struct_fields +from libweb_webgl import is_wire_command +from libweb_webgl import is_wire_sync +from libweb_webgl import run_generator +from libweb_webgl import sync_call_entries +from libweb_webgl import sync_reply_fields +from libweb_webgl import sync_request_fields + +# Generates the WebGL command stream vocabulary from GLFunctions.json: one opcode and one +# trivially-copyable struct per "command"/"gen" entry. The recorder (WebContent) and the +# replayer (Compositor) are generated from the same source of truth, so the wire format +# cannot drift between the two sides. + +# The command and sync-call vocabularies share their X-macro shape; everything but the +# structs themselves is emitted by the helpers below. + + +def write_type_enum(out: TextIO, macro: str, enum_name: str, count_name: str, names: list) -> None: + out.write(f"#define {macro}(V) \\\n") + for name in names: + out.write(f" V({name}) \\\n") + out.write(f""" +enum class {enum_name} : u16 {{ +#define __ENUMERATE(name) name, + {macro}(__ENUMERATE) +#undef __ENUMERATE +}}; + +inline constexpr u16 {count_name} = {len(names)}; + +WEB_API StringView to_string({enum_name}); +""") + + +def write_visit_function(out: TextIO, macro: str, enum_name: str, function_name: str, namespace: str) -> None: + out.write(f""" +template +decltype(auto) {function_name}({enum_name} type, Callback&& callback) +{{ + switch (type) {{ +#define __ENUMERATE(name) \\ + case {enum_name}::name: \\ + return callback.template operator()<{namespace}::name>(); + {macro}(__ENUMERATE) +#undef __ENUMERATE + }} + VERIFY_NOT_REACHED(); +}} +""") + + +def write_static_asserts(out: TextIO, macro: str, assert_body: str) -> None: + out.write(f""" +#define __ENUMERATE(name) {assert_body} +{macro}(__ENUMERATE) +#undef __ENUMERATE +""") + + +def write_to_string_implementation(out: TextIO, macro: str, enum_name: str) -> None: + out.write(f""" +StringView to_string({enum_name} type) +{{ + switch (type) {{ +#define __ENUMERATE(name) \\ + case {enum_name}::name: \\ + return #name##sv; + {macro}(__ENUMERATE) +#undef __ENUMERATE + }} + VERIFY_NOT_REACHED(); +}} +""") + + +def write_header_file(out: TextIO, functions: list) -> None: + commands = command_stream_entries(functions) + sync_calls = sync_call_entries(functions) + + out.write("""#pragma once + +#include +#include +#include +#include +#include +#include + +namespace Web::WebGL { + +""") + write_type_enum( + out, + "ENUMERATE_WEBGL_COMMANDS", + "WebGLCommandType", + "webgl_command_type_count", + [command_name(f) for f in commands], + ) + out.write("\nnamespace Commands {\n") + for f in commands: + name = command_name(f) + out.write(f"\nstruct {name} {{\n") + out.write(f" static constexpr auto command_type = WebGLCommandType::{name};\n") + if is_wire_command(f): + for field in f["wire_command"]: + out.write(f" {field['type']} {field['name']} {{}};\n") + else: + for cpp_type, field_name, _ in command_struct_fields(f): + out.write(f" {cpp_type} {field_name} {{}};\n") + out.write("};\n") + out.write("\n}\n") + write_visit_function(out, "ENUMERATE_WEBGL_COMMANDS", "WebGLCommandType", "visit_webgl_command_type", "Commands") + write_static_asserts(out, "ENUMERATE_WEBGL_COMMANDS", "static_assert(IsTriviallyCopyable);") + + out.write("\n") + write_type_enum( + out, + "ENUMERATE_WEBGL_SYNC_CALLS", + "WebGLSyncCallType", + "webgl_sync_call_type_count", + [command_name(f) for f in sync_calls], + ) + out.write("\nnamespace SyncCalls {\n") + for f in sync_calls: + name = command_name(f) + if is_wire_sync(f): + request_fields = [(field["type"], field["name"]) for field in f["wire_request"]] + reply_fields = [(field["type"], field["name"]) for field in f["wire_reply"]] + else: + request_fields = [(cpp_type, field_name) for cpp_type, field_name, _ in sync_request_fields(f)] + reply_fields = [(cpp_type, field_name) for cpp_type, field_name, _ in sync_reply_fields(f)] + out.write(f"\nstruct {name} {{\n") + out.write(f" static constexpr auto call_type = WebGLSyncCallType::{name};\n") + out.write(" struct Request {\n") + for cpp_type, field_name in request_fields: + out.write(f" {cpp_type} {field_name} {{}};\n") + out.write(" };\n") + out.write(" struct Reply {\n") + for cpp_type, field_name in reply_fields: + out.write(f" {cpp_type} {field_name} {{}};\n") + out.write(" };\n") + out.write("};\n") + out.write("\n}\n") + write_visit_function( + out, "ENUMERATE_WEBGL_SYNC_CALLS", "WebGLSyncCallType", "visit_webgl_sync_call_type", "SyncCalls" + ) + write_static_asserts( + out, + "ENUMERATE_WEBGL_SYNC_CALLS", + "static_assert(IsTriviallyCopyable); static_assert(IsTriviallyCopyable);", + ) + out.write("\n}\n") + + +def write_implementation_file(out: TextIO, functions: list) -> None: + out.write("""#include + +namespace Web::WebGL { +""") + write_to_string_implementation(out, "ENUMERATE_WEBGL_COMMANDS", "WebGLCommandType") + write_to_string_implementation(out, "ENUMERATE_WEBGL_SYNC_CALLS", "WebGLSyncCallType") + out.write("\n}\n") + + +if __name__ == "__main__": + run_generator("Generate WebGL command stream vocabulary", write_header_file, write_implementation_file) diff --git a/Meta/Generators/generate_libweb_webgl_functions.py b/Meta/Generators/generate_libweb_webgl_functions.py index e8347e1d2f..c2dba35909 100644 --- a/Meta/Generators/generate_libweb_webgl_functions.py +++ b/Meta/Generators/generate_libweb_webgl_functions.py @@ -42,6 +42,8 @@ public: """) for function in functions: + if function["category"].startswith("builtin"): + continue out.write(f" {method_signature(function)};\n") out.write("""}; @@ -65,6 +67,8 @@ namespace Web::WebGL { """) for function in functions: + if function["category"].startswith("builtin"): + continue forwarded_args = ", ".join(arg["name"] for arg in function["args"]) call = f"::{function['name']}({forwarded_args})" if function["return"] != "void": diff --git a/Meta/Generators/generate_libweb_webgl_proxy.py b/Meta/Generators/generate_libweb_webgl_proxy.py new file mode 100644 index 0000000000..223bb82d5d --- /dev/null +++ b/Meta/Generators/generate_libweb_webgl_proxy.py @@ -0,0 +1,227 @@ +#!/usr/bin/env python3 + +# Copyright (c) 2026, Aliaksandr Kalenik +# +# SPDX-License-Identifier: BSD-2-Clause + +import sys + +from pathlib import Path +from typing import TextIO + +sys.path.append(str(Path(__file__).resolve().parent)) + +from libweb_webgl import command_name +from libweb_webgl import is_const_pointer +from libweb_webgl import is_pointer +from libweb_webgl import method_signature +from libweb_webgl import run_generator +from libweb_webgl import snake_case +from libweb_webgl import sync_reply_fields + +# Generates WebGLContextProxy, WebContent's drop-in replacement for the GL seam: the +# same method signatures as the generated GLFunctions class, but recording commands into +# a WebGLCommandList (flushed to the Compositor) instead of calling GL. Value-returning +# entry points become synchronous round trips. Method parameter names match the GL +# argument names from GLFunctions.json, so the JSON's payload-size expressions are used +# verbatim. + + +def object_id_expression(arg: dict) -> str: + if arg["type"] == "GLsync": + return f"static_cast(reinterpret_cast({arg['name']}))" + return arg["name"] + + +# Emits statements declaring `_bytes` spans and a fully-initialized `command` +# whose WebGLDataSpan fields point at them in append order. +def emit_command_construction(out: TextIO, function: dict, command_type: str) -> list: + blobs = [] # (field, bytes_variable) + initializers = [] + for arg in function["args"]: + field = snake_case(arg["name"]) + name = arg["name"] + if arg.get("string"): + out.write(f" ReadonlyBytes {field}_bytes {{ {name}, __builtin_strlen({name}) + 1 }};\n") + blobs.append(field) + initializers.append((field, None)) + elif arg.get("offset"): + initializers.append((field, f"static_cast(reinterpret_cast({name}))")) + elif arg.get("object") and not is_pointer(arg): + initializers.append((field, object_id_expression(arg))) + elif (arg.get("object") and is_const_pointer(arg)) or "payload" in arg: + size = f"static_cast({arg['payload']})" + if arg.get("nullable"): + out.write(f" ReadonlyBytes {field}_bytes {{ {name}, {name} ? {size} : 0 }};\n") + initializers.append((f"has_{field}", f"{name} != nullptr")) + else: + out.write(f" ReadonlyBytes {field}_bytes {{ {name}, {size} }};\n") + blobs.append(field) + initializers.append((field, None)) + else: + initializers.append((field, name)) + + fields = ", ".join(f".{field} = {value}" for field, value in initializers if value is not None) + out.write(f" {command_type} command {{ {fields} }};\n") + for index, field in enumerate(blobs): + if index == 0: + offset = "WebGLCommandList::first_inline_data_offset(sizeof(command))" + else: + offset = f"WebGLCommandList::next_inline_data_offset(command.{blobs[index - 1]})" + out.write(f" command.{field} = {{ {offset}, static_cast({field}_bytes.size()) }};\n") + return blobs + + +def emit_record(out: TextIO, blobs: list) -> None: + blob_args = "".join(f", {field}_bytes" for field in blobs) + out.write(f" record(command{blob_args});\n") + + +def emit_command_method(out: TextIO, function: dict) -> None: + command_type = f"Commands::{command_name(function)}" + out.write(f"{method_signature(function, 'WebGLContextProxy::')}\n{{\n") + blobs = emit_command_construction(out, function, command_type) + assert len(blobs) <= 1, f"{function['name']} needs more inline blobs than record() supports" + emit_record(out, blobs) + out.write("}\n\n") + + +def emit_gen_method(out: TextIO, function: dict) -> None: + command_type = f"Commands::{command_name(function)}" + out.write(f"{method_signature(function, 'WebGLContextProxy::')}\n{{\n") + if function["return"] != "void": + scalars = "".join(f", .{snake_case(a['name'])} = {a['name']}" for a in function["args"]) + out.write(" auto id = allocate_object_id();\n") + out.write(f" record({command_type} {{ .id = id{scalars} }});\n") + if function["return"] == "GLsync": + out.write(" return reinterpret_cast(static_cast(id));\n") + else: + out.write(" return id;\n") + out.write("}\n\n") + return + + count_name = function["args"][0]["name"] + span_field = snake_case(function["args"][1]["name"]) + out_name = function["args"][1]["name"] + out.write(f""" if ({count_name} <= 0) + return; + static_assert(IsSame); + for (GLsizei i = 0; i < {count_name}; ++i) + {out_name}[i] = allocate_object_id(); + ReadonlyBytes {span_field}_bytes {{ {out_name}, static_cast({count_name}) * sizeof(WebGLObjectId) }}; + Commands::{command_name(function)} command {{ .{snake_case(count_name)} = {count_name} }}; + command.{span_field} = {{ WebGLCommandList::first_inline_data_offset(sizeof(command)), static_cast({span_field}_bytes.size()) }}; + record(command, {span_field}_bytes); +}} + +""") + + +def emit_sync_method(out: TextIO, function: dict) -> None: + call = f"SyncCalls::{command_name(function)}" + out.write(f"{method_signature(function, 'WebGLContextProxy::')}\n{{\n") + + # Build the request (in-args only) plus its inline blobs. + blobs = [] + initializers = [] + for arg in function["args"]: + if arg.get("out"): + continue + field = snake_case(arg["name"]) + name = arg["name"] + if arg.get("string"): + out.write(f" ReadonlyBytes {field}_bytes {{ {name}, __builtin_strlen({name}) + 1 }};\n") + blobs.append(field) + elif "payload" in arg: + out.write(f" ReadonlyBytes {field}_bytes {{ {name}, static_cast({arg['payload']}) }};\n") + blobs.append(field) + elif arg.get("object") and not is_pointer(arg): + initializers.append((field, object_id_expression(arg))) + else: + initializers.append((field, name)) + assert len(blobs) <= 1, f"{function['name']} needs more request blobs than encode_request supports" + + fields = ", ".join(f".{field} = {value}" for field, value in initializers) + out.write(f" {call}::Request request {{ {fields} }};\n") + for field in blobs: + out.write( + f" request.{field} = {{ WebGLCommandList::first_inline_data_offset(sizeof(request)), static_cast({field}_bytes.size()) }};\n" + ) + blob_argument = f", {blobs[0]}_bytes" if blobs else "" + out.write( + f" auto reply_bytes = send_sync_call(WebGLSyncCall::encode_request<{call}>(request{blob_argument}));\n" + ) + + has_return = function["return"] != "void" + failure = " return {};\n" if has_return else " return;\n" + out.write(" if (is_lost())\n") + out.write(failure) + if sync_reply_fields(function): + out.write(f" auto reply = WebGLSyncCall::decode_reply<{call}::Reply>(reply_bytes);\n") + + for _, field_name, arg in sync_reply_fields(function): + if arg is None: + continue + name = arg["name"] + if "payload" in arg: + out.write(f""" if ({name}) + WebGLCommandList::copy_data_span(reply_bytes, reply.{field_name}, {{ {name}, static_cast({arg["payload"]}) }}); +""") + else: + out.write(f" if ({name})\n *{name} = reply.{field_name};\n") + + if has_return: + out.write(" return reply.return_value;\n") + out.write("}\n\n") + + +def write_header_file(out: TextIO, functions: list) -> None: + out.write("""#pragma once + +#include +#include + +namespace Web::WebGL { + +// The remote-recording implementation of the GL seam: identical signatures to +// GLFunctions, so the WebGL implementation files cannot tell the difference. +class WEB_API WebGLContextProxy final : public WebGLContextProxyBase { +public: + using WebGLContextProxyBase::WebGLContextProxyBase; + +""") + for function in functions: + if function["category"] not in ("command", "gen", "sync"): + continue + out.write(f" {method_signature(function)};\n") + out.write(""" + // Custom-handled entry points; defined manually in WebGLContextProxyBase.cpp. +""") + for function in functions: + if function["category"] != "custom": + continue + out.write(f" {method_signature(function)};\n") + out.write("""}; + +} +""") + + +def write_implementation_file(out: TextIO, functions: list) -> None: + out.write("""#include + +namespace Web::WebGL { + +""") + for function in functions: + if function["category"] == "command": + emit_command_method(out, function) + elif function["category"] == "gen": + emit_gen_method(out, function) + elif function["category"] == "sync": + emit_sync_method(out, function) + out.write("}\n") + + +if __name__ == "__main__": + run_generator("Generate the WebGL context proxy recorder", write_header_file, write_implementation_file) diff --git a/Meta/Generators/libweb_webgl.py b/Meta/Generators/libweb_webgl.py index ef9b7144ff..ab37020d88 100644 --- a/Meta/Generators/libweb_webgl.py +++ b/Meta/Generators/libweb_webgl.py @@ -3,7 +3,7 @@ # SPDX-License-Identifier: BSD-2-Clause # Shared model and CLI driver for the WebGL generators: loads GLFunctions.json and -# derives the generated method name and signature of each GL entry point. +# derives the command-struct shape each annotated GL function serializes to. import argparse import json @@ -41,6 +41,8 @@ def run_generator(description: str, write_header_file, write_implementation_file def command_name(function: dict) -> str: + if function["category"].startswith("builtin"): + return function["name"] assert function["name"].startswith("gl") return function["name"][2:] @@ -49,6 +51,104 @@ def method_name(function: dict) -> str: return snake_case(command_name(function)) +# The GLFunctions and WebGLContextProxy classes must stay signature-identical (the WebGL +# implementation cannot tell them apart), so both generators emit from this one helper. def method_signature(function: dict, qualifier: str = "") -> str: args = ", ".join(f"{arg['type']} {arg['name']}" for arg in function["args"]) return f"{function['return']} {qualifier}{method_name(function)}({args})" + + +# Entries carried by the command stream: regular commands and object creation (struct +# shapes derived from the GL signature) plus wire-specified ops (custom-handled GL +# functions and builtins, whose struct shapes are spelled out in the JSON). +def is_wire_command(function: dict) -> bool: + return "wire_command" in function + + +def is_wire_sync(function: dict) -> bool: + return "wire_request" in function + + +def command_stream_entries(functions: list) -> list: + return [f for f in functions if f["category"] in ("command", "gen") or is_wire_command(f)] + + +def sync_call_entries(functions: list) -> list: + return [f for f in functions if f["category"] == "sync" or is_wire_sync(f)] + + +def is_pointer(arg: dict) -> bool: + return arg["type"].endswith("*") + + +def is_const_pointer(arg: dict) -> bool: + return is_pointer(arg) and "const" in arg["type"] + + +def deref_type(pointer_type: str) -> str: + return pointer_type.replace("*", "").strip() + + +# Returns the ordered (cpp_type, field_name, arg_or_none) triples of a synchronous +# call's request struct: every non-out argument, with object ids, strings, and input +# payloads in their wire representations. +def sync_request_fields(function: dict) -> list: + fields = [] + for arg in function["args"]: + if arg.get("out"): + continue + field_name = snake_case(arg["name"]) + if arg.get("object") and not is_pointer(arg): + fields.append(("WebGLObjectId", field_name, arg)) + elif arg.get("string") or "payload" in arg: + fields.append(("WebGLDataSpan", field_name, arg)) + else: + assert not is_pointer(arg), f"unhandled pointer arg {function['name']}.{arg['name']}" + fields.append((arg["type"], field_name, arg)) + return fields + + +# Returns the ordered (cpp_type, field_name, arg_or_none) triples of a synchronous +# call's reply struct: the return value, scalar outs by value, buffer outs as spans +# into the reply's inline data. +def sync_reply_fields(function: dict) -> list: + fields = [] + if function["return"] != "void": + fields.append((function["return"], "return_value", None)) + for arg in function["args"]: + if not arg.get("out"): + continue + field_name = snake_case(arg["name"]) + if "payload" in arg: + fields.append(("WebGLDataSpan", field_name, arg)) + else: + fields.append((deref_type(arg["type"]), field_name, arg)) + return fields + + +# Returns the ordered (cpp_type, field_name, arg_or_none) triples of the +# trivially-copyable struct a command/gen function serializes to. +def command_struct_fields(function: dict) -> list: + fields = [] + if function["category"] == "gen" and function["return"] != "void": + fields.append(("WebGLObjectId", "id", None)) + for arg in function["args"]: + field_name = snake_case(arg["name"]) + if function["category"] == "gen" and is_pointer(arg) and not is_const_pointer(arg): + fields.append(("WebGLDataSpan", field_name, arg)) # client-allocated ids + elif arg.get("object") and not is_pointer(arg): + fields.append(("WebGLObjectId", field_name, arg)) + elif arg.get("object") and is_const_pointer(arg): + fields.append(("WebGLDataSpan", field_name, arg)) # array of client ids + elif arg.get("string"): + fields.append(("WebGLDataSpan", field_name, arg)) # NUL-terminated bytes + elif arg.get("offset"): + fields.append(("GLintptr", field_name, arg)) + elif "payload" in arg: + if arg.get("nullable"): + fields.append(("bool", "has_" + field_name, arg)) + fields.append(("WebGLDataSpan", field_name, arg)) + else: + assert not is_pointer(arg), f"unhandled pointer arg {function['name']}.{arg['name']}" + fields.append((arg["type"], field_name, arg)) + return fields