From e85b809f8d0ca6c98b8f0556ab909d5580e59100 Mon Sep 17 00:00:00 2001 From: Luke Wilde Date: Wed, 2 Jul 2025 15:14:56 +0100 Subject: [PATCH] LibWeb+Meta: Move WebGL rendering context implementations in tree This copies the latest generated code in tree and then removes code generation for the WebGL rendering contexts. This is because it didn't add much value, and we can maintain the generated output instead of both that and the generator itself. --- Libraries/LibWeb/CMakeLists.txt | 28 +- .../WebGL/WebGL2RenderingContextImpl.cpp | 3577 +++++++++++++++++ .../LibWeb/WebGL/WebGL2RenderingContextImpl.h | 232 ++ .../WebGL/WebGLRenderingContextImpl.cpp | 2411 +++++++++++ .../LibWeb/WebGL/WebGLRenderingContextImpl.h | 176 + .../CodeGenerators/LibWeb/CMakeLists.txt | 1 - .../LibWeb/GenerateWebGLRenderingContext.cpp | 2227 ---------- 7 files changed, 6398 insertions(+), 2254 deletions(-) create mode 100644 Libraries/LibWeb/WebGL/WebGL2RenderingContextImpl.cpp create mode 100644 Libraries/LibWeb/WebGL/WebGL2RenderingContextImpl.h create mode 100644 Libraries/LibWeb/WebGL/WebGLRenderingContextImpl.cpp create mode 100644 Libraries/LibWeb/WebGL/WebGLRenderingContextImpl.h delete mode 100644 Meta/Lagom/Tools/CodeGenerators/LibWeb/GenerateWebGLRenderingContext.cpp diff --git a/Libraries/LibWeb/CMakeLists.txt b/Libraries/LibWeb/CMakeLists.txt index 7f84ec453a0..8d3f8261f75 100644 --- a/Libraries/LibWeb/CMakeLists.txt +++ b/Libraries/LibWeb/CMakeLists.txt @@ -910,6 +910,7 @@ set(SOURCES WebGL/Extensions/WebGLVertexArrayObjectOES.cpp WebGL/OpenGLContext.cpp WebGL/WebGL2RenderingContext.cpp + WebGL/WebGL2RenderingContextImpl.cpp WebGL/WebGLActiveInfo.cpp WebGL/WebGLBuffer.cpp WebGL/WebGLContextAttributes.cpp @@ -920,6 +921,7 @@ set(SOURCES WebGL/WebGLQuery.cpp WebGL/WebGLRenderbuffer.cpp WebGL/WebGLRenderingContext.cpp + WebGL/WebGLRenderingContextImpl.cpp WebGL/WebGLSampler.cpp WebGL/WebGLShader.cpp WebGL/WebGLShaderPrecisionFormat.cpp @@ -968,30 +970,6 @@ generate_css_implementation() generate_html_implementation() -invoke_cpp_generator( - "WebGLRenderingContextImpl.cpp" - Lagom::GenerateWebGLRenderingContext - "${CMAKE_CURRENT_SOURCE_DIR}/WebGL/WebGLRenderingContextBase.idl" - "WebGL/WebGLRenderingContextImpl.h" - "WebGL/WebGLRenderingContextImpl.cpp" - arguments -b "${CMAKE_CURRENT_SOURCE_DIR}" -b "${CMAKE_CURRENT_BINARY_DIR}" -i "${CMAKE_CURRENT_SOURCE_DIR}/WebGL/WebGLRenderingContext.idl" - # NOTE: GeneratedCSSStyleProperties.idl is listed because it's transitively included by WebGLRenderingContextBase.idl - # and we need to make sure it's generated before we generate the WebGLRenderingContext implementation. - dependencies WebGL/WebGLRenderingContextBase.idl WebGL/WebGLRenderingContextOverloads.idl CSS/GeneratedCSSStyleProperties.idl -) - -invoke_cpp_generator( - "WebGL2RenderingContextImpl.cpp" - Lagom::GenerateWebGLRenderingContext - "${CMAKE_CURRENT_SOURCE_DIR}/WebGL/WebGL2RenderingContextBase.idl" - "WebGL/WebGL2RenderingContextImpl.h" - "WebGL/WebGL2RenderingContextImpl.cpp" - arguments -b "${CMAKE_CURRENT_SOURCE_DIR}" -b "${CMAKE_CURRENT_BINARY_DIR}" -i "${CMAKE_CURRENT_SOURCE_DIR}/WebGL/WebGL2RenderingContext.idl" - # NOTE: GeneratedCSSStyleProperties.idl is listed because it's transitively included by WebGLRenderingContextBase.idl - # and we need to make sure it's generated before we generate the WebGLRenderingContext implementation. - dependencies WebGL/WebGL2RenderingContextBase.idl WebGL/WebGL2RenderingContextOverloads.idl WebGL/WebGLRenderingContextBase.idl WebGL/WebGLRenderingContextOverloads.idl CSS/GeneratedCSSStyleProperties.idl -) - set(GENERATED_SOURCES ARIA/AriaRoles.cpp CSS/DefaultStyleSheetSource.cpp @@ -1009,8 +987,6 @@ set(GENERATED_SOURCES CSS/TransformFunctions.cpp MathML/MathMLStyleSheetSource.cpp SVG/SVGStyleSheetSource.cpp - WebGL/WebGL2RenderingContextImpl.cpp - WebGL/WebGLRenderingContextImpl.cpp Worker/WebWorkerClientEndpoint.h Worker/WebWorkerServerEndpoint.h HTML/Parser/NamedCharacterReferences.cpp diff --git a/Libraries/LibWeb/WebGL/WebGL2RenderingContextImpl.cpp b/Libraries/LibWeb/WebGL/WebGL2RenderingContextImpl.cpp new file mode 100644 index 00000000000..9e860f821f6 --- /dev/null +++ b/Libraries/LibWeb/WebGL/WebGL2RenderingContextImpl.cpp @@ -0,0 +1,3577 @@ +/* + * Copyright (c) 2024, Aliaksandr Kalenik + * Copyright (c) 2024-2025, Luke Wilde + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#define GL_GLEXT_PROTOTYPES 1 +#include +extern "C" { +#include +} + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace Web::WebGL { + +static Vector null_terminated_string(StringView string) +{ + Vector result; + for (auto c : string.bytes()) + result.append(c); + result.append('\0'); + return result; +} + +static constexpr Optional opengl_format_number_of_components(WebIDL::UnsignedLong format) +{ + switch (format) { + case GL_RED: + case GL_RED_INTEGER: + case GL_LUMINANCE: + case GL_ALPHA: + case GL_DEPTH_COMPONENT: + return 1; + case GL_RG: + case GL_RG_INTEGER: + case GL_DEPTH_STENCIL: + case GL_LUMINANCE_ALPHA: + return 2; + case GL_RGB: + case GL_RGB_INTEGER: + return 3; + case GL_RGBA: + case GL_RGBA_INTEGER: + return 4; + default: + return OptionalNone {}; + } +} + +static constexpr Optional opengl_type_size_in_bytes(WebIDL::UnsignedLong type) +{ + switch (type) { + case GL_UNSIGNED_BYTE: + case GL_BYTE: + return 1; + case GL_UNSIGNED_SHORT: + case GL_SHORT: + case GL_HALF_FLOAT: + case GL_UNSIGNED_SHORT_5_6_5: + case GL_UNSIGNED_SHORT_4_4_4_4: + case GL_UNSIGNED_SHORT_5_5_5_1: + return 2; + case GL_UNSIGNED_INT: + case GL_INT: + case GL_UNSIGNED_INT_2_10_10_10_REV: + case GL_UNSIGNED_INT_10F_11F_11F_REV: + case GL_UNSIGNED_INT_5_9_9_9_REV: + case GL_UNSIGNED_INT_24_8: + return 4; + case GL_FLOAT_32_UNSIGNED_INT_24_8_REV: + return 8; + default: + return OptionalNone {}; + } +} + +static constexpr SkColorType opengl_format_and_type_to_skia_color_type(WebIDL::UnsignedLong format, WebIDL::UnsignedLong type) +{ + switch (format) { + case GL_RGB: + switch (type) { + case GL_UNSIGNED_BYTE: + return SkColorType::kRGB_888x_SkColorType; + case GL_UNSIGNED_SHORT_5_6_5: + return SkColorType::kRGB_565_SkColorType; + default: + break; + } + break; + case GL_RGBA: + switch (type) { + case GL_UNSIGNED_BYTE: + return SkColorType::kRGBA_8888_SkColorType; + case GL_UNSIGNED_SHORT_4_4_4_4: + // FIXME: This is not exactly the same as RGBA. + return SkColorType::kARGB_4444_SkColorType; + case GL_UNSIGNED_SHORT_5_5_5_1: + dbgln("WebGL2 FIXME: Support conversion to RGBA5551."); + break; + default: + break; + } + break; + case GL_ALPHA: + switch (type) { + case GL_UNSIGNED_BYTE: + return SkColorType::kAlpha_8_SkColorType; + default: + break; + } + break; + case GL_LUMINANCE: + switch (type) { + case GL_UNSIGNED_BYTE: + return SkColorType::kGray_8_SkColorType; + default: + break; + } + break; + default: + break; + } + + dbgln("WebGL2: Unsupported format and type combination. format: 0x{:04x}, type: 0x{:04x}", format, type); + return SkColorType::kUnknown_SkColorType; +} + +struct ConvertedTexture { + ByteBuffer buffer; + int width { 0 }; + int height { 0 }; +}; + +static Optional read_and_pixel_convert_texture_image_source(TexImageSource const& source, WebIDL::UnsignedLong format, WebIDL::UnsignedLong type, Optional destination_width = OptionalNone {}, Optional destination_height = OptionalNone {}) +{ + // FIXME: If this function is called with an ImageData whose data attribute has been neutered, + // an INVALID_VALUE error is generated. + // FIXME: If this function is called with an ImageBitmap that has been neutered, an INVALID_VALUE + // error is generated. + // FIXME: If this function is called with an HTMLImageElement or HTMLVideoElement whose origin + // differs from the origin of the containing Document, or with an HTMLCanvasElement, + // ImageBitmap or OffscreenCanvas whose bitmap's origin-clean flag is set to false, + // a SECURITY_ERR exception must be thrown. See Origin Restrictions. + // FIXME: If source is null then an INVALID_VALUE error is generated. + auto bitmap = source.visit( + [](GC::Root const& source) -> RefPtr { + return source->immutable_bitmap(); + }, + [](GC::Root const& source) -> RefPtr { + auto surface = source->surface(); + if (!surface) + return {}; + auto bitmap = MUST(Gfx::Bitmap::create(Gfx::BitmapFormat::RGBA8888, Gfx::AlphaType::Premultiplied, surface->size())); + surface->read_into_bitmap(*bitmap); + return Gfx::ImmutableBitmap::create(*bitmap); + }, + [](GC::Root const& source) -> RefPtr { + return Gfx::ImmutableBitmap::create(*source->bitmap()); + }, + [](GC::Root const& source) -> RefPtr { + return Gfx::ImmutableBitmap::create(*source->bitmap()); + }, + [](GC::Root const& source) -> RefPtr { + return Gfx::ImmutableBitmap::create(*source->bitmap()); + }, + [](GC::Root const& source) -> RefPtr { + return Gfx::ImmutableBitmap::create(source->bitmap()); + }); + if (!bitmap) + return OptionalNone {}; + + int width = destination_width.value_or(bitmap->width()); + int height = destination_height.value_or(bitmap->height()); + + Checked buffer_pitch = width; + + auto number_of_components = opengl_format_number_of_components(format); + if (!number_of_components.has_value()) + return OptionalNone {}; + + buffer_pitch *= number_of_components.value(); + + auto type_size = opengl_type_size_in_bytes(type); + if (!type_size.has_value()) + return OptionalNone {}; + + buffer_pitch *= type_size.value(); + + if (buffer_pitch.has_overflow()) + return OptionalNone {}; + + if (Checked::multiplication_would_overflow(buffer_pitch.value(), height)) + return OptionalNone {}; + + auto buffer = MUST(ByteBuffer::create_zeroed(buffer_pitch.value() * height)); + + auto skia_format = opengl_format_and_type_to_skia_color_type(format, type); + + // FIXME: Respect UNPACK_PREMULTIPLY_ALPHA_WEBGL + // FIXME: Respect unpackColorSpace + auto color_space = SkColorSpace::MakeSRGB(); + auto image_info = SkImageInfo::Make(width, height, skia_format, SkAlphaType::kPremul_SkAlphaType, color_space); + SkPixmap const pixmap(image_info, buffer.data(), buffer_pitch.value()); + bitmap->sk_image()->readPixels(pixmap, 0, 0); + return ConvertedTexture { + .buffer = move(buffer), + .width = width, + .height = height, + }; +} + +WebGL2RenderingContextImpl::WebGL2RenderingContextImpl(JS::Realm& realm, NonnullOwnPtr context) + : m_realm(realm) + , m_context(move(context)) +{ +} + +void WebGL2RenderingContextImpl::copy_buffer_sub_data(WebIDL::UnsignedLong read_target, WebIDL::UnsignedLong write_target, WebIDL::LongLong read_offset, WebIDL::LongLong write_offset, WebIDL::LongLong size) +{ + m_context->make_current(); + glCopyBufferSubData(read_target, write_target, read_offset, write_offset, size); +} + +void WebGL2RenderingContextImpl::blit_framebuffer(WebIDL::Long src_x0, WebIDL::Long src_y0, WebIDL::Long src_x1, WebIDL::Long src_y1, WebIDL::Long dst_x0, WebIDL::Long dst_y0, WebIDL::Long dst_x1, WebIDL::Long dst_y1, WebIDL::UnsignedLong mask, WebIDL::UnsignedLong filter) +{ + m_context->make_current(); + m_context->notify_content_will_change(); + needs_to_present(); + glBlitFramebuffer(src_x0, src_y0, src_x1, src_y1, dst_x0, dst_y0, dst_x1, dst_y1, mask, filter); +} + +void WebGL2RenderingContextImpl::invalidate_framebuffer(WebIDL::UnsignedLong target, Vector attachments) +{ + m_context->make_current(); + m_context->notify_content_will_change(); + + glInvalidateFramebuffer(target, attachments.size(), attachments.data()); + needs_to_present(); +} + +void WebGL2RenderingContextImpl::read_buffer(WebIDL::UnsignedLong src) +{ + m_context->make_current(); + glReadBuffer(src); +} + +JS::Value WebGL2RenderingContextImpl::get_internalformat_parameter(WebIDL::UnsignedLong target, WebIDL::UnsignedLong internalformat, WebIDL::UnsignedLong pname) +{ + m_context->make_current(); + + switch (pname) { + case GL_SAMPLES: { + GLint num_sample_counts { 0 }; + glGetInternalformativRobustANGLE(target, internalformat, GL_NUM_SAMPLE_COUNTS, 1, nullptr, &num_sample_counts); + size_t buffer_size = num_sample_counts * sizeof(GLint); + auto samples_buffer = MUST(ByteBuffer::create_zeroed(buffer_size)); + glGetInternalformativRobustANGLE(target, internalformat, GL_SAMPLES, buffer_size, nullptr, reinterpret_cast(samples_buffer.data())); + auto array_buffer = JS::ArrayBuffer::create(m_realm, move(samples_buffer)); + return JS::Int32Array::create(m_realm, num_sample_counts, array_buffer); + } + default: + dbgln("Unknown WebGL internal format parameter name: {:x}", pname); + set_error(GL_INVALID_ENUM); + return JS::js_null(); + } +} + +void WebGL2RenderingContextImpl::renderbuffer_storage_multisample(WebIDL::UnsignedLong target, WebIDL::Long samples, WebIDL::UnsignedLong internalformat, WebIDL::Long width, WebIDL::Long height) +{ + m_context->make_current(); + glRenderbufferStorageMultisample(target, samples, internalformat, width, height); +} + +void WebGL2RenderingContextImpl::tex_storage2d(WebIDL::UnsignedLong target, WebIDL::Long levels, WebIDL::UnsignedLong internalformat, WebIDL::Long width, WebIDL::Long height) +{ + m_context->make_current(); + + glTexStorage2D(target, levels, internalformat, width, height); +} + +void WebGL2RenderingContextImpl::tex_storage3d(WebIDL::UnsignedLong target, WebIDL::Long levels, WebIDL::UnsignedLong internalformat, WebIDL::Long width, WebIDL::Long height, WebIDL::Long depth) +{ + m_context->make_current(); + glTexStorage3D(target, levels, internalformat, width, height, depth); +} + +void WebGL2RenderingContextImpl::tex_image3d(WebIDL::UnsignedLong target, WebIDL::Long level, WebIDL::Long internalformat, WebIDL::Long width, WebIDL::Long height, WebIDL::Long depth, WebIDL::Long border, WebIDL::UnsignedLong format, WebIDL::UnsignedLong type, GC::Root src_data) +{ + m_context->make_current(); + + void const* src_data_ptr = nullptr; + size_t buffer_size = 0; + if (src_data) { + auto const& viewed_array_buffer = src_data->viewed_array_buffer(); + auto const& byte_buffer = viewed_array_buffer->buffer(); + src_data_ptr = byte_buffer.data() + src_data->byte_offset(); + buffer_size = src_data->byte_length(); + } + glTexImage3DRobustANGLE(target, level, internalformat, width, height, depth, border, format, type, buffer_size, src_data_ptr); +} + +void WebGL2RenderingContextImpl::tex_image3d(WebIDL::UnsignedLong target, WebIDL::Long level, WebIDL::Long internalformat, WebIDL::Long width, WebIDL::Long height, WebIDL::Long depth, WebIDL::Long border, WebIDL::UnsignedLong format, WebIDL::UnsignedLong type, GC::Root src_data, WebIDL::UnsignedLongLong src_offset) +{ + m_context->make_current(); + + void const* src_data_ptr = nullptr; + size_t buffer_size = 0; + if (src_data) { + auto const& viewed_array_buffer = src_data->viewed_array_buffer(); + auto const& byte_buffer = viewed_array_buffer->buffer(); + src_data_ptr = byte_buffer.data() + src_offset; + buffer_size = src_data->byte_length(); + } + glTexImage3DRobustANGLE(target, level, internalformat, width, height, depth, border, format, type, buffer_size, src_data_ptr); +} + +void WebGL2RenderingContextImpl::tex_sub_image3d(WebIDL::UnsignedLong target, WebIDL::Long level, WebIDL::Long xoffset, WebIDL::Long yoffset, WebIDL::Long zoffset, WebIDL::Long width, WebIDL::Long height, WebIDL::Long depth, WebIDL::UnsignedLong format, WebIDL::UnsignedLong type, GC::Root src_data, WebIDL::UnsignedLongLong src_offset) +{ + m_context->make_current(); + + void const* pixels_ptr = nullptr; + size_t buffer_size = 0; + if (src_data) { + auto const& viewed_array_buffer = src_data->viewed_array_buffer(); + auto const& byte_buffer = viewed_array_buffer->buffer(); + pixels_ptr = byte_buffer.data() + src_offset; + buffer_size = src_data->byte_length(); + } + glTexSubImage3DRobustANGLE(target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, buffer_size, pixels_ptr); +} + +void WebGL2RenderingContextImpl::uniform1ui(GC::Root location, WebIDL::UnsignedLong v0) +{ + m_context->make_current(); + glUniform1ui(location ? location->handle() : 0, v0); +} + +void WebGL2RenderingContextImpl::uniform2ui(GC::Root location, WebIDL::UnsignedLong v0, WebIDL::UnsignedLong v1) +{ + m_context->make_current(); + glUniform2ui(location ? location->handle() : 0, v0, v1); +} + +void WebGL2RenderingContextImpl::uniform3ui(GC::Root location, WebIDL::UnsignedLong v0, WebIDL::UnsignedLong v1, WebIDL::UnsignedLong v2) +{ + m_context->make_current(); + glUniform3ui(location ? location->handle() : 0, v0, v1, v2); +} + +void WebGL2RenderingContextImpl::uniform4ui(GC::Root location, WebIDL::UnsignedLong v0, WebIDL::UnsignedLong v1, WebIDL::UnsignedLong v2, WebIDL::UnsignedLong v3) +{ + m_context->make_current(); + glUniform4ui(location ? location->handle() : 0, v0, v1, v2, v3); +} + +void WebGL2RenderingContextImpl::vertex_attrib_i_pointer(WebIDL::UnsignedLong index, WebIDL::Long size, WebIDL::UnsignedLong type, WebIDL::Long stride, WebIDL::LongLong offset) +{ + m_context->make_current(); + + glVertexAttribIPointer(index, size, type, stride, reinterpret_cast(offset)); +} + +void WebGL2RenderingContextImpl::vertex_attrib_divisor(WebIDL::UnsignedLong index, WebIDL::UnsignedLong divisor) +{ + m_context->make_current(); + glVertexAttribDivisor(index, divisor); +} + +void WebGL2RenderingContextImpl::draw_arrays_instanced(WebIDL::UnsignedLong mode, WebIDL::Long first, WebIDL::Long count, WebIDL::Long instance_count) +{ + m_context->make_current(); + m_context->notify_content_will_change(); + needs_to_present(); + glDrawArraysInstanced(mode, first, count, instance_count); +} + +void WebGL2RenderingContextImpl::draw_elements_instanced(WebIDL::UnsignedLong mode, WebIDL::Long count, WebIDL::UnsignedLong type, WebIDL::LongLong offset, WebIDL::Long instance_count) +{ + m_context->make_current(); + m_context->notify_content_will_change(); + + glDrawElementsInstanced(mode, count, type, reinterpret_cast(offset), instance_count); + needs_to_present(); +} + +void WebGL2RenderingContextImpl::draw_buffers(Vector buffers) +{ + m_context->make_current(); + + glDrawBuffers(buffers.size(), buffers.data()); +} + +void WebGL2RenderingContextImpl::clear_bufferfv(WebIDL::UnsignedLong buffer, WebIDL::Long drawbuffer, Variant, Vector> values, WebIDL::UnsignedLongLong src_offset) +{ + m_context->make_current(); + m_context->notify_content_will_change(); + + float const* data = nullptr; + size_t count = 0; + if (values.has>()) { + auto& vector = values.get>(); + data = vector.data(); + count = vector.size(); + } else if (values.has>()) { + auto& typed_array_base = static_cast(*values.get>()->raw_object()); + auto& typed_array = as(typed_array_base); + data = typed_array.data().data(); + count = typed_array.array_length().length(); + } else { + VERIFY_NOT_REACHED(); + } + + switch (buffer) { + case GL_COLOR: + if (src_offset + 4 > count) { + set_error(GL_INVALID_VALUE); + return; + } + break; + case GL_DEPTH: + case GL_STENCIL: + if (src_offset + 1 > count) { + set_error(GL_INVALID_VALUE); + return; + } + break; + default: + dbgln("Unknown WebGL buffer target for buffer clearing: 0x{:04x}", buffer); + set_error(GL_INVALID_ENUM); + return; + } + + data += src_offset; + glClearBufferfv(buffer, drawbuffer, data); + needs_to_present(); +} + +void WebGL2RenderingContextImpl::clear_bufferiv(WebIDL::UnsignedLong buffer, WebIDL::Long drawbuffer, Variant, Vector> values, WebIDL::UnsignedLongLong src_offset) +{ + m_context->make_current(); + m_context->notify_content_will_change(); + + int const* data = nullptr; + size_t count = 0; + if (values.has>()) { + auto& vector = values.get>(); + data = vector.data(); + count = vector.size(); + } else if (values.has>()) { + auto& typed_array_base = static_cast(*values.get>()->raw_object()); + auto& typed_array = as(typed_array_base); + data = typed_array.data().data(); + count = typed_array.array_length().length(); + } else { + VERIFY_NOT_REACHED(); + } + + switch (buffer) { + case GL_COLOR: + if (src_offset + 4 > count) { + set_error(GL_INVALID_VALUE); + return; + } + break; + case GL_DEPTH: + case GL_STENCIL: + if (src_offset + 1 > count) { + set_error(GL_INVALID_VALUE); + return; + } + break; + default: + dbgln("Unknown WebGL buffer target for buffer clearing: 0x{:04x}", buffer); + set_error(GL_INVALID_ENUM); + return; + } + + data += src_offset; + glClearBufferiv(buffer, drawbuffer, data); + needs_to_present(); +} + +void WebGL2RenderingContextImpl::clear_bufferuiv(WebIDL::UnsignedLong buffer, WebIDL::Long drawbuffer, Variant, Vector> values, WebIDL::UnsignedLongLong src_offset) +{ + m_context->make_current(); + m_context->notify_content_will_change(); + + u32 const* data = nullptr; + size_t count = 0; + if (values.has>()) { + auto& vector = values.get>(); + data = vector.data(); + count = vector.size(); + } else if (values.has>()) { + auto& typed_array_base = static_cast(*values.get>()->raw_object()); + auto& typed_array = as(typed_array_base); + data = typed_array.data().data(); + count = typed_array.array_length().length(); + } else { + VERIFY_NOT_REACHED(); + } + + switch (buffer) { + case GL_COLOR: + if (src_offset + 4 > count) { + set_error(GL_INVALID_VALUE); + return; + } + break; + case GL_DEPTH: + case GL_STENCIL: + if (src_offset + 1 > count) { + set_error(GL_INVALID_VALUE); + return; + } + break; + default: + dbgln("Unknown WebGL buffer target for buffer clearing: 0x{:04x}", buffer); + set_error(GL_INVALID_ENUM); + return; + } + + data += src_offset; + glClearBufferuiv(buffer, drawbuffer, data); + needs_to_present(); +} + +void WebGL2RenderingContextImpl::clear_bufferfi(WebIDL::UnsignedLong buffer, WebIDL::Long drawbuffer, float depth, WebIDL::Long stencil) +{ + m_context->make_current(); + m_context->notify_content_will_change(); + needs_to_present(); + glClearBufferfi(buffer, drawbuffer, depth, stencil); +} + +GC::Root WebGL2RenderingContextImpl::create_sampler() +{ + m_context->make_current(); + + GLuint handle = 0; + glGenSamplers(1, &handle); + return WebGLSampler::create(m_realm, *this, handle); +} + +void WebGL2RenderingContextImpl::delete_sampler(GC::Root sampler) +{ + m_context->make_current(); + + GLuint sampler_handle = 0; + if (sampler) { + auto handle_or_error = sampler->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return; + } + sampler_handle = handle_or_error.release_value(); + } + + glDeleteSamplers(1, &sampler_handle); +} + +void WebGL2RenderingContextImpl::bind_sampler(WebIDL::UnsignedLong unit, GC::Root sampler) +{ + m_context->make_current(); + + auto sampler_handle = 0; + if (sampler) { + auto handle_or_error = sampler->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return; + } + sampler_handle = handle_or_error.release_value(); + } + glBindSampler(unit, sampler_handle); +} + +void WebGL2RenderingContextImpl::sampler_parameteri(GC::Root sampler, WebIDL::UnsignedLong pname, WebIDL::Long param) +{ + m_context->make_current(); + + GLuint sampler_handle = 0; + if (sampler) { + auto handle_or_error = sampler->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return; + } + sampler_handle = handle_or_error.release_value(); + } + + switch (pname) { + case GL_TEXTURE_COMPARE_FUNC: + case GL_TEXTURE_COMPARE_MODE: + case GL_TEXTURE_MAG_FILTER: + case GL_TEXTURE_MAX_LOD: + case GL_TEXTURE_MIN_FILTER: + case GL_TEXTURE_MIN_LOD: + case GL_TEXTURE_WRAP_R: + case GL_TEXTURE_WRAP_S: + case GL_TEXTURE_WRAP_T: + break; + default: + dbgln("Unknown WebGL sampler parameter name: 0x{:04x}", pname); + set_error(GL_INVALID_ENUM); + return; + } + glSamplerParameteri(sampler_handle, pname, param); +} + +void WebGL2RenderingContextImpl::sampler_parameterf(GC::Root sampler, WebIDL::UnsignedLong pname, float param) +{ + m_context->make_current(); + + GLuint sampler_handle = 0; + if (sampler) { + auto handle_or_error = sampler->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return; + } + sampler_handle = handle_or_error.release_value(); + } + + switch (pname) { + case GL_TEXTURE_COMPARE_FUNC: + case GL_TEXTURE_COMPARE_MODE: + case GL_TEXTURE_MAG_FILTER: + case GL_TEXTURE_MAX_LOD: + case GL_TEXTURE_MIN_FILTER: + case GL_TEXTURE_MIN_LOD: + case GL_TEXTURE_WRAP_R: + case GL_TEXTURE_WRAP_S: + case GL_TEXTURE_WRAP_T: + break; + default: + dbgln("Unknown WebGL sampler parameter name: 0x{:04x}", pname); + set_error(GL_INVALID_ENUM); + return; + } + glSamplerParameterf(sampler_handle, pname, param); +} + +GC::Root WebGL2RenderingContextImpl::fence_sync(WebIDL::UnsignedLong condition, WebIDL::UnsignedLong flags) +{ + m_context->make_current(); + + GLsync handle = glFenceSync(condition, flags); + return WebGLSync::create(m_realm, *this, handle); +} + +void WebGL2RenderingContextImpl::delete_sync(GC::Root sync) +{ + m_context->make_current(); + glDeleteSync((GLsync)(sync ? sync->sync_handle() : nullptr)); +} + +WebIDL::UnsignedLong WebGL2RenderingContextImpl::client_wait_sync(GC::Root sync, WebIDL::UnsignedLong flags, WebIDL::UnsignedLongLong timeout) +{ + m_context->make_current(); + return glClientWaitSync((GLsync)(sync ? sync->sync_handle() : nullptr), flags, timeout); +} + +JS::Value WebGL2RenderingContextImpl::get_sync_parameter(GC::Root sync, WebIDL::UnsignedLong pname) +{ + m_context->make_current(); + + GLint result = 0; + glGetSynciv((GLsync)(sync ? sync->sync_handle() : nullptr), pname, 1, nullptr, &result); + return JS::Value(result); +} + +void WebGL2RenderingContextImpl::bind_buffer_base(WebIDL::UnsignedLong target, WebIDL::UnsignedLong index, GC::Root buffer) +{ + m_context->make_current(); + + auto buffer_handle = 0; + if (buffer) { + auto handle_or_error = buffer->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return; + } + buffer_handle = handle_or_error.release_value(); + } + glBindBufferBase(target, index, buffer_handle); +} + +void WebGL2RenderingContextImpl::bind_buffer_range(WebIDL::UnsignedLong target, WebIDL::UnsignedLong index, GC::Root buffer, WebIDL::LongLong offset, WebIDL::LongLong size) +{ + m_context->make_current(); + + auto buffer_handle = 0; + if (buffer) { + auto handle_or_error = buffer->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return; + } + buffer_handle = handle_or_error.release_value(); + } + glBindBufferRange(target, index, buffer_handle, offset, size); +} + +JS::Value WebGL2RenderingContextImpl::get_active_uniforms(GC::Root program, Vector uniform_indices, WebIDL::UnsignedLong pname) +{ + m_context->make_current(); + + GLuint program_handle = 0; + if (program) { + auto handle_or_error = program->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return {}; + } + program_handle = handle_or_error.release_value(); + } + + auto params = MUST(ByteBuffer::create_zeroed(uniform_indices.size() * sizeof(GLint))); + Span params_span(reinterpret_cast(params.data()), uniform_indices.size()); + glGetActiveUniformsiv(program_handle, uniform_indices.size(), uniform_indices.data(), pname, params_span.data()); + + Vector params_as_values; + params_as_values.ensure_capacity(params.size()); + for (GLint param : params_span) { + switch (pname) { + case GL_UNIFORM_TYPE: + params_as_values.unchecked_append(JS::Value(static_cast(param))); + break; + case GL_UNIFORM_SIZE: + params_as_values.unchecked_append(JS::Value(static_cast(param))); + break; + case GL_UNIFORM_BLOCK_INDEX: + case GL_UNIFORM_OFFSET: + case GL_UNIFORM_ARRAY_STRIDE: + case GL_UNIFORM_MATRIX_STRIDE: + params_as_values.unchecked_append(JS::Value(param)); + break; + case GL_UNIFORM_IS_ROW_MAJOR: + params_as_values.unchecked_append(JS::Value(param == GL_TRUE)); + break; + default: + dbgln("Unknown WebGL uniform parameter name in getActiveUniforms: 0x{:04x}", pname); + set_error(GL_INVALID_ENUM); + return JS::js_null(); + } + } + + return JS::Array::create_from(m_realm, params_as_values); +} + +WebIDL::UnsignedLong WebGL2RenderingContextImpl::get_uniform_block_index(GC::Root program, String uniform_block_name) +{ + m_context->make_current(); + + auto program_handle = 0; + if (program) { + auto handle_or_error = program->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return -1; + } + program_handle = handle_or_error.release_value(); + } + + auto uniform_block_name_null_terminated = null_terminated_string(uniform_block_name); + return glGetUniformBlockIndex(program_handle, uniform_block_name_null_terminated.data()); +} + +JS::Value WebGL2RenderingContextImpl::get_active_uniform_block_parameter(GC::Root program, WebIDL::UnsignedLong uniform_block_index, WebIDL::UnsignedLong pname) +{ + m_context->make_current(); + + GLuint program_handle = 0; + if (program) { + auto handle_or_error = program->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return JS::js_null(); + } + program_handle = handle_or_error.release_value(); + } + + switch (pname) { + case GL_UNIFORM_BLOCK_BINDING: + case GL_UNIFORM_BLOCK_DATA_SIZE: + case GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS: { + GLint result = 0; + glGetActiveUniformBlockivRobustANGLE(program_handle, uniform_block_index, pname, 1, nullptr, &result); + return JS::Value(result); + } + case GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES: { + GLint num_active_uniforms = 0; + glGetActiveUniformBlockivRobustANGLE(program_handle, uniform_block_index, GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, sizeof(GLint), nullptr, &num_active_uniforms); + size_t buffer_size = num_active_uniforms * sizeof(GLint); + auto active_uniform_indices_buffer = MUST(ByteBuffer::create_zeroed(buffer_size)); + glGetActiveUniformBlockivRobustANGLE(program_handle, uniform_block_index, GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES, num_active_uniforms, nullptr, reinterpret_cast(active_uniform_indices_buffer.data())); + auto array_buffer = JS::ArrayBuffer::create(m_realm, move(active_uniform_indices_buffer)); + return JS::Uint32Array::create(m_realm, num_active_uniforms, array_buffer); + } + case GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER: + case GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER: { + GLint result = 0; + glGetActiveUniformBlockivRobustANGLE(program_handle, uniform_block_index, pname, 1, nullptr, &result); + return JS::Value(result == GL_TRUE); + } + default: + dbgln("Unknown WebGL active uniform block parameter name: {:x}", pname); + set_error(GL_INVALID_ENUM); + return JS::js_null(); + } +} + +Optional WebGL2RenderingContextImpl::get_active_uniform_block_name(GC::Root program, WebIDL::UnsignedLong uniform_block_index) +{ + m_context->make_current(); + + GLuint program_handle = 0; + if (program) { + auto handle_or_error = program->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return OptionalNone {}; + } + program_handle = handle_or_error.release_value(); + } + + GLint uniform_block_name_length = 0; + glGetActiveUniformBlockivRobustANGLE(program_handle, uniform_block_index, GL_UNIFORM_BLOCK_NAME_LENGTH, 1, nullptr, &uniform_block_name_length); + Vector uniform_block_name; + uniform_block_name.resize(uniform_block_name_length); + if (!uniform_block_name_length) + return String {}; + glGetActiveUniformBlockName(program_handle, uniform_block_index, uniform_block_name_length, nullptr, uniform_block_name.data()); + return String::from_utf8_without_validation(ReadonlyBytes { uniform_block_name.data(), static_cast(uniform_block_name_length - 1) }); +} + +void WebGL2RenderingContextImpl::uniform_block_binding(GC::Root program, WebIDL::UnsignedLong uniform_block_index, WebIDL::UnsignedLong uniform_block_binding) +{ + m_context->make_current(); + + auto program_handle = 0; + if (program) { + auto handle_or_error = program->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return; + } + program_handle = handle_or_error.release_value(); + } + glUniformBlockBinding(program_handle, uniform_block_index, uniform_block_binding); +} + +GC::Root WebGL2RenderingContextImpl::create_vertex_array() +{ + m_context->make_current(); + + GLuint handle = 0; + glGenVertexArrays(1, &handle); + return WebGLVertexArrayObject::create(m_realm, *this, handle); +} + +void WebGL2RenderingContextImpl::delete_vertex_array(GC::Root vertex_array) +{ + m_context->make_current(); + + GLuint vertex_array_handle = 0; + if (vertex_array) { + auto handle_or_error = vertex_array->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return; + } + vertex_array_handle = handle_or_error.release_value(); + } + + glDeleteVertexArrays(1, &vertex_array_handle); +} + +bool WebGL2RenderingContextImpl::is_vertex_array(GC::Root vertex_array) +{ + m_context->make_current(); + + auto vertex_array_handle = 0; + if (vertex_array) { + auto handle_or_error = vertex_array->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return false; + } + vertex_array_handle = handle_or_error.release_value(); + } + return glIsVertexArray(vertex_array_handle); +} + +void WebGL2RenderingContextImpl::bind_vertex_array(GC::Root array) +{ + m_context->make_current(); + + auto array_handle = 0; + if (array) { + auto handle_or_error = array->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return; + } + array_handle = handle_or_error.release_value(); + } + glBindVertexArray(array_handle); +} + +void WebGL2RenderingContextImpl::buffer_data(WebIDL::UnsignedLong target, WebIDL::LongLong size, WebIDL::UnsignedLong usage) +{ + m_context->make_current(); + + glBufferData(target, size, 0, usage); +} + +void WebGL2RenderingContextImpl::buffer_data(WebIDL::UnsignedLong target, GC::Root src_data, WebIDL::UnsignedLong usage) +{ + m_context->make_current(); + + void const* ptr = nullptr; + size_t byte_size = 0; + if (src_data->is_typed_array_base()) { + auto& typed_array_base = static_cast(*src_data->raw_object()); + ptr = typed_array_base.viewed_array_buffer()->buffer().data() + typed_array_base.byte_offset(); + byte_size = src_data->byte_length(); + } else if (src_data->is_data_view()) { + auto& data_view = static_cast(*src_data->raw_object()); + ptr = data_view.viewed_array_buffer()->buffer().data(); + byte_size = data_view.viewed_array_buffer()->byte_length(); + } else if (src_data->is_array_buffer()) { + auto& array_buffer = static_cast(*src_data->raw_object()); + ptr = array_buffer.buffer().data(); + byte_size = array_buffer.byte_length(); + } else { + VERIFY_NOT_REACHED(); + } + glBufferData(target, byte_size, ptr, usage); +} + +void WebGL2RenderingContextImpl::buffer_sub_data(WebIDL::UnsignedLong target, WebIDL::LongLong dst_byte_offset, GC::Root src_data) +{ + m_context->make_current(); + + void const* ptr = nullptr; + size_t byte_size = 0; + if (src_data->is_typed_array_base()) { + auto& typed_array_base = static_cast(*src_data->raw_object()); + ptr = typed_array_base.viewed_array_buffer()->buffer().data() + typed_array_base.byte_offset(); + byte_size = src_data->byte_length(); + } else if (src_data->is_data_view()) { + auto& data_view = static_cast(*src_data->raw_object()); + ptr = data_view.viewed_array_buffer()->buffer().data(); + byte_size = data_view.viewed_array_buffer()->byte_length(); + } else if (src_data->is_array_buffer()) { + auto& array_buffer = static_cast(*src_data->raw_object()); + ptr = array_buffer.buffer().data(); + byte_size = array_buffer.byte_length(); + } else { + VERIFY_NOT_REACHED(); + } + glBufferSubData(target, dst_byte_offset, byte_size, ptr); +} + +void WebGL2RenderingContextImpl::buffer_data(WebIDL::UnsignedLong target, GC::Root src_data, WebIDL::UnsignedLong usage, WebIDL::UnsignedLongLong src_offset, WebIDL::UnsignedLong length) +{ + m_context->make_current(); + + VERIFY(src_data); + auto const& viewed_array_buffer = src_data->viewed_array_buffer(); + auto const& byte_buffer = viewed_array_buffer->buffer(); + auto src_data_length = src_data->byte_length(); + auto src_data_element_size = src_data->element_size(); + u8 const* buffer_ptr = byte_buffer.data(); + + u64 copy_length = length == 0 ? src_data_length - src_offset : length; + copy_length *= src_data_element_size; + + if (src_offset > src_data_length) { + set_error(GL_INVALID_VALUE); + return; + } + + if (src_offset + copy_length > src_data_length) { + set_error(GL_INVALID_VALUE); + return; + } + + buffer_ptr += src_offset * src_data_element_size; + glBufferData(target, copy_length, buffer_ptr, usage); +} + +void WebGL2RenderingContextImpl::buffer_sub_data(WebIDL::UnsignedLong target, WebIDL::LongLong dst_byte_offset, GC::Root src_data, WebIDL::UnsignedLongLong src_offset, WebIDL::UnsignedLong length) +{ + m_context->make_current(); + + VERIFY(src_data); + auto const& viewed_array_buffer = src_data->viewed_array_buffer(); + auto const& byte_buffer = viewed_array_buffer->buffer(); + auto src_data_length = src_data->byte_length(); + auto src_data_element_size = src_data->element_size(); + u8 const* buffer_ptr = byte_buffer.data(); + + u64 copy_length = length == 0 ? src_data_length - src_offset : length; + copy_length *= src_data_element_size; + + if (src_offset > src_data_length) { + set_error(GL_INVALID_VALUE); + return; + } + + if (src_offset + copy_length > src_data_length) { + set_error(GL_INVALID_VALUE); + return; + } + + buffer_ptr += src_offset * src_data_element_size; + glBufferSubData(target, dst_byte_offset, copy_length, buffer_ptr); +} + +void WebGL2RenderingContextImpl::tex_image2d(WebIDL::UnsignedLong target, WebIDL::Long level, WebIDL::Long internalformat, WebIDL::Long width, WebIDL::Long height, WebIDL::Long border, WebIDL::UnsignedLong format, WebIDL::UnsignedLong type, GC::Root pixels) +{ + m_context->make_current(); + + void const* pixels_ptr = nullptr; + size_t buffer_size = 0; + if (pixels) { + auto const& viewed_array_buffer = pixels->viewed_array_buffer(); + auto const& byte_buffer = viewed_array_buffer->buffer(); + pixels_ptr = byte_buffer.data() + pixels->byte_offset(); + buffer_size = pixels->byte_length(); + } + glTexImage2DRobustANGLE(target, level, internalformat, width, height, border, format, type, buffer_size, pixels_ptr); +} + +void WebGL2RenderingContextImpl::tex_image2d(WebIDL::UnsignedLong target, WebIDL::Long level, WebIDL::Long internalformat, WebIDL::UnsignedLong format, WebIDL::UnsignedLong type, Variant, GC::Root, GC::Root, GC::Root, GC::Root, GC::Root> source) +{ + m_context->make_current(); + + auto maybe_converted_texture = read_and_pixel_convert_texture_image_source(source, format, type); + if (!maybe_converted_texture.has_value()) + return; + auto converted_texture = maybe_converted_texture.release_value(); + glTexImage2DRobustANGLE(target, level, internalformat, converted_texture.width, converted_texture.height, 0, format, type, converted_texture.buffer.size(), converted_texture.buffer.data()); +} + +void WebGL2RenderingContextImpl::tex_sub_image2d(WebIDL::UnsignedLong target, WebIDL::Long level, WebIDL::Long xoffset, WebIDL::Long yoffset, WebIDL::Long width, WebIDL::Long height, WebIDL::UnsignedLong format, WebIDL::UnsignedLong type, GC::Root pixels) +{ + m_context->make_current(); + + void const* pixels_ptr = nullptr; + size_t buffer_size = 0; + if (pixels) { + auto const& viewed_array_buffer = pixels->viewed_array_buffer(); + auto const& byte_buffer = viewed_array_buffer->buffer(); + pixels_ptr = byte_buffer.data() + pixels->byte_offset(); + buffer_size = pixels->byte_length(); + } + glTexSubImage2DRobustANGLE(target, level, xoffset, yoffset, width, height, format, type, buffer_size, pixels_ptr); +} + +void WebGL2RenderingContextImpl::tex_sub_image2d(WebIDL::UnsignedLong target, WebIDL::Long level, WebIDL::Long xoffset, WebIDL::Long yoffset, WebIDL::UnsignedLong format, WebIDL::UnsignedLong type, Variant, GC::Root, GC::Root, GC::Root, GC::Root, GC::Root> source) +{ + m_context->make_current(); + + auto maybe_converted_texture = read_and_pixel_convert_texture_image_source(source, format, type); + + if (!maybe_converted_texture.has_value()) + return; + auto converted_texture = maybe_converted_texture.release_value(); + glTexSubImage2DRobustANGLE(target, level, xoffset, yoffset, converted_texture.width, converted_texture.height, format, type, converted_texture.buffer.size(), converted_texture.buffer.data()); +} + +void WebGL2RenderingContextImpl::tex_image2d(WebIDL::UnsignedLong target, WebIDL::Long level, WebIDL::Long internalformat, WebIDL::Long width, WebIDL::Long height, WebIDL::Long border, WebIDL::UnsignedLong format, WebIDL::UnsignedLong type, Variant, GC::Root, GC::Root, GC::Root, GC::Root, GC::Root> source) +{ + m_context->make_current(); + + auto maybe_converted_texture = read_and_pixel_convert_texture_image_source(source, format, type, width, height); + if (!maybe_converted_texture.has_value()) + return; + auto converted_texture = maybe_converted_texture.release_value(); + glTexImage2DRobustANGLE(target, level, internalformat, converted_texture.width, converted_texture.height, border, format, type, converted_texture.buffer.size(), converted_texture.buffer.data()); +} + +void WebGL2RenderingContextImpl::tex_image2d(WebIDL::UnsignedLong target, WebIDL::Long level, WebIDL::Long internalformat, WebIDL::Long width, WebIDL::Long height, WebIDL::Long border, WebIDL::UnsignedLong format, WebIDL::UnsignedLong type, GC::Root src_data, WebIDL::UnsignedLongLong src_offset) +{ + m_context->make_current(); + + void const* pixels_ptr = nullptr; + size_t buffer_size = 0; + if (src_data) { + auto const& viewed_array_buffer = src_data->viewed_array_buffer(); + auto const& byte_buffer = viewed_array_buffer->buffer(); + pixels_ptr = byte_buffer.data() + src_offset; + buffer_size = src_data->byte_length(); + } + glTexImage2DRobustANGLE(target, level, internalformat, width, height, border, format, type, buffer_size, pixels_ptr); +} + +void WebGL2RenderingContextImpl::tex_sub_image2d(WebIDL::UnsignedLong target, WebIDL::Long level, WebIDL::Long xoffset, WebIDL::Long yoffset, WebIDL::Long width, WebIDL::Long height, WebIDL::UnsignedLong format, WebIDL::UnsignedLong type, Variant, GC::Root, GC::Root, GC::Root, GC::Root, GC::Root> source) +{ + m_context->make_current(); + + auto maybe_converted_texture = read_and_pixel_convert_texture_image_source(source, format, type, width, height); + + if (!maybe_converted_texture.has_value()) + return; + auto converted_texture = maybe_converted_texture.release_value(); + glTexSubImage2DRobustANGLE(target, level, xoffset, yoffset, converted_texture.width, converted_texture.height, format, type, converted_texture.buffer.size(), converted_texture.buffer.data()); +} + +void WebGL2RenderingContextImpl::tex_sub_image2d(WebIDL::UnsignedLong target, WebIDL::Long level, WebIDL::Long xoffset, WebIDL::Long yoffset, WebIDL::Long width, WebIDL::Long height, WebIDL::UnsignedLong format, WebIDL::UnsignedLong type, GC::Root src_data, WebIDL::UnsignedLongLong src_offset) +{ + m_context->make_current(); + + void const* pixels_ptr = nullptr; + size_t buffer_size = 0; + if (src_data) { + auto const& viewed_array_buffer = src_data->viewed_array_buffer(); + auto const& byte_buffer = viewed_array_buffer->buffer(); + pixels_ptr = byte_buffer.data() + src_data->byte_offset() + src_offset; + buffer_size = src_data->byte_length(); + } + glTexSubImage2DRobustANGLE(target, level, xoffset, yoffset, width, height, format, type, buffer_size, pixels_ptr); +} + +void WebGL2RenderingContextImpl::compressed_tex_image2d(WebIDL::UnsignedLong target, WebIDL::Long level, WebIDL::UnsignedLong internalformat, WebIDL::Long width, WebIDL::Long height, WebIDL::Long border, GC::Root src_data, WebIDL::UnsignedLongLong src_offset, WebIDL::UnsignedLong src_length_override) +{ + m_context->make_current(); + + u8 const* pixels_ptr = src_data->viewed_array_buffer()->buffer().data(); + size_t count = src_data->byte_length(); + auto src_data_element_size = src_data->element_size(); + + if ((src_offset * src_data_element_size) + src_length_override > count) { + set_error(GL_INVALID_VALUE); + return; + } + + pixels_ptr += src_data->byte_offset(); + pixels_ptr += src_offset * src_data_element_size; + if (src_length_override == 0) { + count -= src_offset; + } else { + count = src_length_override; + } + + glCompressedTexImage2DRobustANGLE(target, level, internalformat, width, height, border, count, src_data->byte_length(), pixels_ptr); +} + +void WebGL2RenderingContextImpl::compressed_tex_sub_image2d(WebIDL::UnsignedLong target, WebIDL::Long level, WebIDL::Long xoffset, WebIDL::Long yoffset, WebIDL::Long width, WebIDL::Long height, WebIDL::UnsignedLong format, GC::Root src_data, WebIDL::UnsignedLongLong src_offset, WebIDL::UnsignedLong src_length_override) +{ + m_context->make_current(); + + u8 const* pixels_ptr = src_data->viewed_array_buffer()->buffer().data(); + size_t count = src_data->byte_length(); + auto src_data_element_size = src_data->element_size(); + + if ((src_offset * src_data_element_size) + src_length_override > count) { + set_error(GL_INVALID_VALUE); + return; + } + + pixels_ptr += src_data->byte_offset(); + pixels_ptr += src_offset * src_data_element_size; + if (src_length_override == 0) { + count -= src_offset; + } else { + count = src_length_override; + } + + glCompressedTexSubImage2DRobustANGLE(target, level, xoffset, yoffset, width, height, format, count, src_data->byte_length(), pixels_ptr); +} + +void WebGL2RenderingContextImpl::uniform1fv(GC::Root location, Variant, Vector> v, WebIDL::UnsignedLongLong src_offset, WebIDL::UnsignedLong src_length) +{ + m_context->make_current(); + + if (!location) + return; + + float const* data = nullptr; + size_t count = 0; + if (v.has>()) { + auto& vector = v.get>(); + data = vector.data(); + count = vector.size(); + } else if (v.has>()) { + auto& typed_array_base = static_cast(*v.get>()->raw_object()); + auto& typed_array = as(typed_array_base); + data = typed_array.data().data(); + count = typed_array.array_length().length(); + } else { + VERIFY_NOT_REACHED(); + } + + if (src_offset + src_length > count) { + set_error(GL_INVALID_VALUE); + return; + } + + data += src_offset; + if (src_length == 0) { + count -= src_offset; + } else { + count = src_length; + } + + glUniform1fv(location->handle(), count / 1, data); +} + +void WebGL2RenderingContextImpl::uniform2fv(GC::Root location, Variant, Vector> v, WebIDL::UnsignedLongLong src_offset, WebIDL::UnsignedLong src_length) +{ + m_context->make_current(); + + if (!location) + return; + + float const* data = nullptr; + size_t count = 0; + if (v.has>()) { + auto& vector = v.get>(); + data = vector.data(); + count = vector.size(); + } else if (v.has>()) { + auto& typed_array_base = static_cast(*v.get>()->raw_object()); + auto& typed_array = as(typed_array_base); + data = typed_array.data().data(); + count = typed_array.array_length().length(); + } else { + VERIFY_NOT_REACHED(); + } + + if (src_offset + src_length > count) { + set_error(GL_INVALID_VALUE); + return; + } + + data += src_offset; + if (src_length == 0) { + count -= src_offset; + } else { + count = src_length; + } + + glUniform2fv(location->handle(), count / 2, data); +} + +void WebGL2RenderingContextImpl::uniform3fv(GC::Root location, Variant, Vector> v, WebIDL::UnsignedLongLong src_offset, WebIDL::UnsignedLong src_length) +{ + m_context->make_current(); + + if (!location) + return; + + float const* data = nullptr; + size_t count = 0; + if (v.has>()) { + auto& vector = v.get>(); + data = vector.data(); + count = vector.size(); + } else if (v.has>()) { + auto& typed_array_base = static_cast(*v.get>()->raw_object()); + auto& typed_array = as(typed_array_base); + data = typed_array.data().data(); + count = typed_array.array_length().length(); + } else { + VERIFY_NOT_REACHED(); + } + + if (src_offset + src_length > count) { + set_error(GL_INVALID_VALUE); + return; + } + + data += src_offset; + if (src_length == 0) { + count -= src_offset; + } else { + count = src_length; + } + + glUniform3fv(location->handle(), count / 3, data); +} + +void WebGL2RenderingContextImpl::uniform4fv(GC::Root location, Variant, Vector> v, WebIDL::UnsignedLongLong src_offset, WebIDL::UnsignedLong src_length) +{ + m_context->make_current(); + + if (!location) + return; + + float const* data = nullptr; + size_t count = 0; + if (v.has>()) { + auto& vector = v.get>(); + data = vector.data(); + count = vector.size(); + } else if (v.has>()) { + auto& typed_array_base = static_cast(*v.get>()->raw_object()); + auto& typed_array = as(typed_array_base); + data = typed_array.data().data(); + count = typed_array.array_length().length(); + } else { + VERIFY_NOT_REACHED(); + } + + if (src_offset + src_length > count) { + set_error(GL_INVALID_VALUE); + return; + } + + data += src_offset; + if (src_length == 0) { + count -= src_offset; + } else { + count = src_length; + } + + glUniform4fv(location->handle(), count / 4, data); +} + +void WebGL2RenderingContextImpl::uniform1iv(GC::Root location, Variant, Vector> v, WebIDL::UnsignedLongLong src_offset, WebIDL::UnsignedLong src_length) +{ + m_context->make_current(); + + if (!location) + return; + + int const* data = nullptr; + size_t count = 0; + if (v.has>()) { + auto& vector = v.get>(); + data = vector.data(); + count = vector.size(); + } else if (v.has>()) { + auto& typed_array_base = static_cast(*v.get>()->raw_object()); + auto& typed_array = as(typed_array_base); + data = typed_array.data().data(); + count = typed_array.array_length().length(); + } else { + VERIFY_NOT_REACHED(); + } + + if (src_offset + src_length > count) { + set_error(GL_INVALID_VALUE); + return; + } + + data += src_offset; + if (src_length == 0) { + count -= src_offset; + } else { + count = src_length; + } + + glUniform1iv(location->handle(), count / 1, data); +} + +void WebGL2RenderingContextImpl::uniform2iv(GC::Root location, Variant, Vector> v, WebIDL::UnsignedLongLong src_offset, WebIDL::UnsignedLong src_length) +{ + m_context->make_current(); + + if (!location) + return; + + int const* data = nullptr; + size_t count = 0; + if (v.has>()) { + auto& vector = v.get>(); + data = vector.data(); + count = vector.size(); + } else if (v.has>()) { + auto& typed_array_base = static_cast(*v.get>()->raw_object()); + auto& typed_array = as(typed_array_base); + data = typed_array.data().data(); + count = typed_array.array_length().length(); + } else { + VERIFY_NOT_REACHED(); + } + + if (src_offset + src_length > count) { + set_error(GL_INVALID_VALUE); + return; + } + + data += src_offset; + if (src_length == 0) { + count -= src_offset; + } else { + count = src_length; + } + + glUniform2iv(location->handle(), count / 2, data); +} + +void WebGL2RenderingContextImpl::uniform3iv(GC::Root location, Variant, Vector> v, WebIDL::UnsignedLongLong src_offset, WebIDL::UnsignedLong src_length) +{ + m_context->make_current(); + + if (!location) + return; + + int const* data = nullptr; + size_t count = 0; + if (v.has>()) { + auto& vector = v.get>(); + data = vector.data(); + count = vector.size(); + } else if (v.has>()) { + auto& typed_array_base = static_cast(*v.get>()->raw_object()); + auto& typed_array = as(typed_array_base); + data = typed_array.data().data(); + count = typed_array.array_length().length(); + } else { + VERIFY_NOT_REACHED(); + } + + if (src_offset + src_length > count) { + set_error(GL_INVALID_VALUE); + return; + } + + data += src_offset; + if (src_length == 0) { + count -= src_offset; + } else { + count = src_length; + } + + glUniform3iv(location->handle(), count / 3, data); +} + +void WebGL2RenderingContextImpl::uniform4iv(GC::Root location, Variant, Vector> v, WebIDL::UnsignedLongLong src_offset, WebIDL::UnsignedLong src_length) +{ + m_context->make_current(); + + if (!location) + return; + + int const* data = nullptr; + size_t count = 0; + if (v.has>()) { + auto& vector = v.get>(); + data = vector.data(); + count = vector.size(); + } else if (v.has>()) { + auto& typed_array_base = static_cast(*v.get>()->raw_object()); + auto& typed_array = as(typed_array_base); + data = typed_array.data().data(); + count = typed_array.array_length().length(); + } else { + VERIFY_NOT_REACHED(); + } + + if (src_offset + src_length > count) { + set_error(GL_INVALID_VALUE); + return; + } + + data += src_offset; + if (src_length == 0) { + count -= src_offset; + } else { + count = src_length; + } + + glUniform4iv(location->handle(), count / 4, data); +} + +void WebGL2RenderingContextImpl::uniform_matrix2fv(GC::Root location, bool transpose, Variant, Vector> data, WebIDL::UnsignedLongLong src_offset, WebIDL::UnsignedLong src_length) +{ + m_context->make_current(); + + if (!location) + return; + + auto matrix_size = 2 * 2; + float const* raw_data = nullptr; + u64 count = 0; + if (data.has>()) { + auto& vector_data = data.get>(); + raw_data = vector_data.data(); + count = vector_data.size() / matrix_size; + } else { + auto& typed_array_base = static_cast(*data.get>()->raw_object()); + auto& float32_array = as(typed_array_base); + raw_data = float32_array.data().data(); + count = float32_array.array_length().length() / matrix_size; + } + + if (src_offset + src_length > (count * matrix_size)) { + set_error(GL_INVALID_VALUE); + return; + } + + raw_data += src_offset; + if (src_length == 0) { + count -= src_offset; + } else { + count = src_length; + } + + glUniformMatrix2fv(location->handle(), count, transpose, raw_data); +} + +void WebGL2RenderingContextImpl::uniform_matrix3fv(GC::Root location, bool transpose, Variant, Vector> data, WebIDL::UnsignedLongLong src_offset, WebIDL::UnsignedLong src_length) +{ + m_context->make_current(); + + if (!location) + return; + + auto matrix_size = 3 * 3; + float const* raw_data = nullptr; + u64 count = 0; + if (data.has>()) { + auto& vector_data = data.get>(); + raw_data = vector_data.data(); + count = vector_data.size() / matrix_size; + } else { + auto& typed_array_base = static_cast(*data.get>()->raw_object()); + auto& float32_array = as(typed_array_base); + raw_data = float32_array.data().data(); + count = float32_array.array_length().length() / matrix_size; + } + + if (src_offset + src_length > (count * matrix_size)) { + set_error(GL_INVALID_VALUE); + return; + } + + raw_data += src_offset; + if (src_length == 0) { + count -= src_offset; + } else { + count = src_length; + } + + glUniformMatrix3fv(location->handle(), count, transpose, raw_data); +} + +void WebGL2RenderingContextImpl::uniform_matrix4fv(GC::Root location, bool transpose, Variant, Vector> data, WebIDL::UnsignedLongLong src_offset, WebIDL::UnsignedLong src_length) +{ + m_context->make_current(); + + if (!location) + return; + + auto matrix_size = 4 * 4; + float const* raw_data = nullptr; + u64 count = 0; + if (data.has>()) { + auto& vector_data = data.get>(); + raw_data = vector_data.data(); + count = vector_data.size() / matrix_size; + } else { + auto& typed_array_base = static_cast(*data.get>()->raw_object()); + auto& float32_array = as(typed_array_base); + raw_data = float32_array.data().data(); + count = float32_array.array_length().length() / matrix_size; + } + + if (src_offset + src_length > (count * matrix_size)) { + set_error(GL_INVALID_VALUE); + return; + } + + raw_data += src_offset; + if (src_length == 0) { + count -= src_offset; + } else { + count = src_length; + } + + glUniformMatrix4fv(location->handle(), count, transpose, raw_data); +} + +void WebGL2RenderingContextImpl::read_pixels(WebIDL::Long x, WebIDL::Long y, WebIDL::Long width, WebIDL::Long height, WebIDL::UnsignedLong format, WebIDL::UnsignedLong type, GC::Root pixels) +{ + m_context->make_current(); + + if (!pixels) { + return; + } + + void* ptr = pixels->viewed_array_buffer()->buffer().data() + pixels->byte_offset(); + glReadPixelsRobustANGLE(x, y, width, height, format, type, pixels->byte_length(), nullptr, nullptr, nullptr, ptr); +} + +void WebGL2RenderingContextImpl::active_texture(WebIDL::UnsignedLong texture) +{ + m_context->make_current(); + glActiveTexture(texture); +} + +void WebGL2RenderingContextImpl::attach_shader(GC::Root program, GC::Root shader) +{ + m_context->make_current(); + + GLuint program_handle = 0; + if (program) { + auto handle_or_error = program->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return; + } + program_handle = handle_or_error.release_value(); + } + + GLuint shader_handle = 0; + if (shader) { + auto handle_or_error = shader->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return; + } + shader_handle = handle_or_error.release_value(); + } + + if (program->attached_vertex_shader() == shader || program->attached_fragment_shader() == shader) { + dbgln("WebGL: Shader is already attached to program"); + set_error(GL_INVALID_OPERATION); + return; + } + + if (shader->type() == GL_VERTEX_SHADER && program->attached_vertex_shader()) { + dbgln("WebGL: Not attaching vertex shader to program as it already has a vertex shader attached"); + set_error(GL_INVALID_OPERATION); + return; + } + + if (shader->type() == GL_FRAGMENT_SHADER && program->attached_fragment_shader()) { + dbgln("WebGL: Not attaching fragment shader to program as it already has a fragment shader attached"); + set_error(GL_INVALID_OPERATION); + return; + } + + glAttachShader(program_handle, shader_handle); + + switch (shader->type()) { + case GL_VERTEX_SHADER: + program->set_attached_vertex_shader(shader.ptr()); + break; + case GL_FRAGMENT_SHADER: + program->set_attached_fragment_shader(shader.ptr()); + break; + default: + VERIFY_NOT_REACHED(); + } +} + +void WebGL2RenderingContextImpl::bind_attrib_location(GC::Root program, WebIDL::UnsignedLong index, String name) +{ + m_context->make_current(); + + auto program_handle = 0; + if (program) { + auto handle_or_error = program->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return; + } + program_handle = handle_or_error.release_value(); + } + + auto name_null_terminated = null_terminated_string(name); + glBindAttribLocation(program_handle, index, name_null_terminated.data()); +} + +void WebGL2RenderingContextImpl::bind_buffer(WebIDL::UnsignedLong target, GC::Root buffer) +{ + m_context->make_current(); + + GLuint buffer_handle = 0; + if (buffer) { + auto handle_or_error = buffer->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return; + } + buffer_handle = handle_or_error.release_value(); + } + + switch (target) { + case GL_ELEMENT_ARRAY_BUFFER: + m_element_array_buffer_binding = buffer; + break; + case GL_ARRAY_BUFFER: + m_array_buffer_binding = buffer; + break; + + case GL_UNIFORM_BUFFER: + m_uniform_buffer_binding = buffer; + break; + case GL_COPY_READ_BUFFER: + m_copy_read_buffer_binding = buffer; + break; + case GL_COPY_WRITE_BUFFER: + m_copy_write_buffer_binding = buffer; + break; + + default: + dbgln("Unknown WebGL buffer object binding target for storing current binding: 0x{:04x}", target); + set_error(GL_INVALID_ENUM); + return; + } + + glBindBuffer(target, buffer_handle); +} + +void WebGL2RenderingContextImpl::bind_framebuffer(WebIDL::UnsignedLong target, GC::Root framebuffer) +{ + m_context->make_current(); + + GLuint framebuffer_handle = 0; + if (framebuffer) { + auto handle_or_error = framebuffer->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return; + } + framebuffer_handle = handle_or_error.release_value(); + } + + glBindFramebuffer(target, framebuffer ? framebuffer_handle : m_context->default_framebuffer()); + m_framebuffer_binding = framebuffer; +} + +void WebGL2RenderingContextImpl::bind_renderbuffer(WebIDL::UnsignedLong target, GC::Root renderbuffer) +{ + m_context->make_current(); + + GLuint renderbuffer_handle = 0; + if (renderbuffer) { + auto handle_or_error = renderbuffer->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return; + } + renderbuffer_handle = handle_or_error.release_value(); + } + + glBindRenderbuffer(target, renderbuffer ? renderbuffer_handle : m_context->default_renderbuffer()); + m_renderbuffer_binding = renderbuffer; +} + +void WebGL2RenderingContextImpl::bind_texture(WebIDL::UnsignedLong target, GC::Root texture) +{ + m_context->make_current(); + + GLuint texture_handle = 0; + if (texture) { + auto handle_or_error = texture->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return; + } + texture_handle = handle_or_error.release_value(); + } + + switch (target) { + case GL_TEXTURE_2D: + m_texture_binding_2d = texture; + break; + case GL_TEXTURE_CUBE_MAP: + m_texture_binding_cube_map = texture; + break; + + case GL_TEXTURE_2D_ARRAY: + m_texture_binding_2d_array = texture; + break; + case GL_TEXTURE_3D: + m_texture_binding_3d = texture; + break; + + default: + dbgln("Unknown WebGL texture target for storing current binding: 0x{:04x}", target); + set_error(GL_INVALID_ENUM); + return; + } + glBindTexture(target, texture_handle); +} + +void WebGL2RenderingContextImpl::blend_color(float red, float green, float blue, float alpha) +{ + m_context->make_current(); + glBlendColor(red, green, blue, alpha); +} + +void WebGL2RenderingContextImpl::blend_equation(WebIDL::UnsignedLong mode) +{ + m_context->make_current(); + glBlendEquation(mode); +} + +void WebGL2RenderingContextImpl::blend_equation_separate(WebIDL::UnsignedLong mode_rgb, WebIDL::UnsignedLong mode_alpha) +{ + m_context->make_current(); + glBlendEquationSeparate(mode_rgb, mode_alpha); +} + +void WebGL2RenderingContextImpl::blend_func(WebIDL::UnsignedLong sfactor, WebIDL::UnsignedLong dfactor) +{ + m_context->make_current(); + glBlendFunc(sfactor, dfactor); +} + +void WebGL2RenderingContextImpl::blend_func_separate(WebIDL::UnsignedLong src_rgb, WebIDL::UnsignedLong dst_rgb, WebIDL::UnsignedLong src_alpha, WebIDL::UnsignedLong dst_alpha) +{ + m_context->make_current(); + glBlendFuncSeparate(src_rgb, dst_rgb, src_alpha, dst_alpha); +} + +WebIDL::UnsignedLong WebGL2RenderingContextImpl::check_framebuffer_status(WebIDL::UnsignedLong target) +{ + m_context->make_current(); + return glCheckFramebufferStatus(target); +} + +void WebGL2RenderingContextImpl::clear(WebIDL::UnsignedLong mask) +{ + m_context->make_current(); + m_context->notify_content_will_change(); + needs_to_present(); + glClear(mask); +} + +void WebGL2RenderingContextImpl::clear_color(float red, float green, float blue, float alpha) +{ + m_context->make_current(); + glClearColor(red, green, blue, alpha); +} + +void WebGL2RenderingContextImpl::clear_depth(float depth) +{ + m_context->make_current(); + glClearDepthf(depth); +} + +void WebGL2RenderingContextImpl::clear_stencil(WebIDL::Long s) +{ + m_context->make_current(); + glClearStencil(s); +} + +void WebGL2RenderingContextImpl::color_mask(bool red, bool green, bool blue, bool alpha) +{ + m_context->make_current(); + glColorMask(red, green, blue, alpha); +} + +void WebGL2RenderingContextImpl::compile_shader(GC::Root shader) +{ + m_context->make_current(); + + auto shader_handle = 0; + if (shader) { + auto handle_or_error = shader->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return; + } + shader_handle = handle_or_error.release_value(); + } + glCompileShader(shader_handle); +} + +void WebGL2RenderingContextImpl::copy_tex_image2d(WebIDL::UnsignedLong target, WebIDL::Long level, WebIDL::UnsignedLong internalformat, WebIDL::Long x, WebIDL::Long y, WebIDL::Long width, WebIDL::Long height, WebIDL::Long border) +{ + m_context->make_current(); + glCopyTexImage2D(target, level, internalformat, x, y, width, height, border); +} + +void WebGL2RenderingContextImpl::copy_tex_sub_image2d(WebIDL::UnsignedLong target, WebIDL::Long level, WebIDL::Long xoffset, WebIDL::Long yoffset, WebIDL::Long x, WebIDL::Long y, WebIDL::Long width, WebIDL::Long height) +{ + m_context->make_current(); + glCopyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, height); +} + +GC::Root WebGL2RenderingContextImpl::create_buffer() +{ + m_context->make_current(); + + GLuint handle = 0; + glGenBuffers(1, &handle); + return WebGLBuffer::create(m_realm, *this, handle); +} + +GC::Root WebGL2RenderingContextImpl::create_framebuffer() +{ + m_context->make_current(); + + GLuint handle = 0; + glGenFramebuffers(1, &handle); + return WebGLFramebuffer::create(m_realm, *this, handle); +} + +GC::Root WebGL2RenderingContextImpl::create_program() +{ + m_context->make_current(); + return WebGLProgram::create(m_realm, *this, glCreateProgram()); +} + +GC::Root WebGL2RenderingContextImpl::create_renderbuffer() +{ + m_context->make_current(); + + GLuint handle = 0; + glGenRenderbuffers(1, &handle); + return WebGLRenderbuffer::create(m_realm, *this, handle); +} + +GC::Root WebGL2RenderingContextImpl::create_shader(WebIDL::UnsignedLong type) +{ + m_context->make_current(); + + if (type != GL_VERTEX_SHADER && type != GL_FRAGMENT_SHADER) { + dbgln("Unknown WebGL shader type: 0x{:04x}", type); + set_error(GL_INVALID_ENUM); + return nullptr; + } + + GLuint handle = glCreateShader(type); + return WebGLShader::create(m_realm, *this, handle, type); +} + +GC::Root WebGL2RenderingContextImpl::create_texture() +{ + m_context->make_current(); + + GLuint handle = 0; + glGenTextures(1, &handle); + return WebGLTexture::create(m_realm, *this, handle); +} + +void WebGL2RenderingContextImpl::cull_face(WebIDL::UnsignedLong mode) +{ + m_context->make_current(); + glCullFace(mode); +} + +void WebGL2RenderingContextImpl::delete_buffer(GC::Root buffer) +{ + m_context->make_current(); + + GLuint buffer_handle = 0; + if (buffer) { + auto handle_or_error = buffer->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return; + } + buffer_handle = handle_or_error.release_value(); + } + + glDeleteBuffers(1, &buffer_handle); +} + +void WebGL2RenderingContextImpl::delete_framebuffer(GC::Root framebuffer) +{ + m_context->make_current(); + + GLuint framebuffer_handle = 0; + if (framebuffer) { + auto handle_or_error = framebuffer->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return; + } + framebuffer_handle = handle_or_error.release_value(); + } + + glDeleteFramebuffers(1, &framebuffer_handle); +} + +void WebGL2RenderingContextImpl::delete_program(GC::Root program) +{ + m_context->make_current(); + + auto program_handle = 0; + if (program) { + auto handle_or_error = program->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return; + } + program_handle = handle_or_error.release_value(); + } + glDeleteProgram(program_handle); +} + +void WebGL2RenderingContextImpl::delete_renderbuffer(GC::Root renderbuffer) +{ + m_context->make_current(); + + GLuint renderbuffer_handle = 0; + if (renderbuffer) { + auto handle_or_error = renderbuffer->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return; + } + renderbuffer_handle = handle_or_error.release_value(); + } + + glDeleteRenderbuffers(1, &renderbuffer_handle); +} + +void WebGL2RenderingContextImpl::delete_shader(GC::Root shader) +{ + m_context->make_current(); + + auto shader_handle = 0; + if (shader) { + auto handle_or_error = shader->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return; + } + shader_handle = handle_or_error.release_value(); + } + glDeleteShader(shader_handle); +} + +void WebGL2RenderingContextImpl::delete_texture(GC::Root texture) +{ + m_context->make_current(); + + GLuint texture_handle = 0; + if (texture) { + auto handle_or_error = texture->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return; + } + texture_handle = handle_or_error.release_value(); + } + + glDeleteTextures(1, &texture_handle); +} + +void WebGL2RenderingContextImpl::depth_func(WebIDL::UnsignedLong func) +{ + m_context->make_current(); + glDepthFunc(func); +} + +void WebGL2RenderingContextImpl::depth_mask(bool flag) +{ + m_context->make_current(); + glDepthMask(flag); +} + +void WebGL2RenderingContextImpl::depth_range(float z_near, float z_far) +{ + m_context->make_current(); + glDepthRangef(z_near, z_far); +} + +void WebGL2RenderingContextImpl::detach_shader(GC::Root program, GC::Root shader) +{ + m_context->make_current(); + + auto program_handle = 0; + if (program) { + auto handle_or_error = program->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return; + } + program_handle = handle_or_error.release_value(); + } + + auto shader_handle = 0; + if (shader) { + auto handle_or_error = shader->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return; + } + shader_handle = handle_or_error.release_value(); + } + glDetachShader(program_handle, shader_handle); +} + +void WebGL2RenderingContextImpl::disable(WebIDL::UnsignedLong cap) +{ + m_context->make_current(); + glDisable(cap); +} + +void WebGL2RenderingContextImpl::disable_vertex_attrib_array(WebIDL::UnsignedLong index) +{ + m_context->make_current(); + glDisableVertexAttribArray(index); +} + +void WebGL2RenderingContextImpl::draw_arrays(WebIDL::UnsignedLong mode, WebIDL::Long first, WebIDL::Long count) +{ + m_context->make_current(); + m_context->notify_content_will_change(); + needs_to_present(); + glDrawArrays(mode, first, count); +} + +void WebGL2RenderingContextImpl::draw_elements(WebIDL::UnsignedLong mode, WebIDL::Long count, WebIDL::UnsignedLong type, WebIDL::LongLong offset) +{ + m_context->make_current(); + m_context->notify_content_will_change(); + + glDrawElements(mode, count, type, reinterpret_cast(offset)); + needs_to_present(); +} + +void WebGL2RenderingContextImpl::enable(WebIDL::UnsignedLong cap) +{ + m_context->make_current(); + glEnable(cap); +} + +void WebGL2RenderingContextImpl::enable_vertex_attrib_array(WebIDL::UnsignedLong index) +{ + m_context->make_current(); + glEnableVertexAttribArray(index); +} + +void WebGL2RenderingContextImpl::finish() +{ + m_context->make_current(); + glFinish(); +} + +void WebGL2RenderingContextImpl::flush() +{ + m_context->make_current(); + glFlush(); +} + +void WebGL2RenderingContextImpl::framebuffer_renderbuffer(WebIDL::UnsignedLong target, WebIDL::UnsignedLong attachment, WebIDL::UnsignedLong renderbuffertarget, GC::Root renderbuffer) +{ + m_context->make_current(); + + auto renderbuffer_handle = 0; + if (renderbuffer) { + auto handle_or_error = renderbuffer->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return; + } + renderbuffer_handle = handle_or_error.release_value(); + } + glFramebufferRenderbuffer(target, attachment, renderbuffertarget, renderbuffer_handle); +} + +void WebGL2RenderingContextImpl::framebuffer_texture2d(WebIDL::UnsignedLong target, WebIDL::UnsignedLong attachment, WebIDL::UnsignedLong textarget, GC::Root texture, WebIDL::Long level) +{ + m_context->make_current(); + + auto texture_handle = 0; + if (texture) { + auto handle_or_error = texture->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return; + } + texture_handle = handle_or_error.release_value(); + } + glFramebufferTexture2D(target, attachment, textarget, texture_handle, level); +} + +void WebGL2RenderingContextImpl::front_face(WebIDL::UnsignedLong mode) +{ + m_context->make_current(); + glFrontFace(mode); +} + +void WebGL2RenderingContextImpl::generate_mipmap(WebIDL::UnsignedLong target) +{ + m_context->make_current(); + glGenerateMipmap(target); +} + +GC::Root WebGL2RenderingContextImpl::get_active_attrib(GC::Root program, WebIDL::UnsignedLong index) +{ + m_context->make_current(); + + GLuint program_handle = 0; + if (program) { + auto handle_or_error = program->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return {}; + } + program_handle = handle_or_error.release_value(); + } + + GLint size = 0; + GLenum type = 0; + GLsizei buf_size = 256; + GLsizei length = 0; + GLchar name[256]; + glGetActiveAttrib(program_handle, index, buf_size, &length, &size, &type, name); + auto readonly_bytes = ReadonlyBytes { name, static_cast(length) }; + return WebGLActiveInfo::create(m_realm, String::from_utf8_without_validation(readonly_bytes), type, size); +} + +GC::Root WebGL2RenderingContextImpl::get_active_uniform(GC::Root program, WebIDL::UnsignedLong index) +{ + m_context->make_current(); + + GLuint program_handle = 0; + if (program) { + auto handle_or_error = program->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return {}; + } + program_handle = handle_or_error.release_value(); + } + + GLint size = 0; + GLenum type = 0; + GLsizei buf_size = 256; + GLsizei length = 0; + GLchar name[256]; + glGetActiveUniform(program_handle, index, buf_size, &length, &size, &type, name); + auto readonly_bytes = ReadonlyBytes { name, static_cast(length) }; + return WebGLActiveInfo::create(m_realm, String::from_utf8_without_validation(readonly_bytes), type, size); +} + +Optional>> WebGL2RenderingContextImpl::get_attached_shaders(GC::Root program) +{ + m_context->make_current(); + + GLuint program_handle = 0; + if (program) { + auto handle_or_error = program->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return OptionalNone {}; + } + program_handle = handle_or_error.release_value(); + } + + (void)program_handle; + + Vector> result; + + if (program->attached_vertex_shader()) + result.append(GC::make_root(*program->attached_vertex_shader())); + + if (program->attached_fragment_shader()) + result.append(GC::make_root(*program->attached_fragment_shader())); + + return result; +} + +WebIDL::Long WebGL2RenderingContextImpl::get_attrib_location(GC::Root program, String name) +{ + m_context->make_current(); + + auto program_handle = 0; + if (program) { + auto handle_or_error = program->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return -1; + } + program_handle = handle_or_error.release_value(); + } + + auto name_null_terminated = null_terminated_string(name); + return glGetAttribLocation(program_handle, name_null_terminated.data()); +} + +JS::Value WebGL2RenderingContextImpl::get_buffer_parameter(WebIDL::UnsignedLong target, WebIDL::UnsignedLong pname) +{ + m_context->make_current(); + switch (pname) { + case GL_BUFFER_SIZE: { + GLint result { 0 }; + glGetBufferParameterivRobustANGLE(target, GL_BUFFER_SIZE, 1, nullptr, &result); + return JS::Value(result); + } + + case GL_BUFFER_USAGE: { + GLint result { 0 }; + glGetBufferParameterivRobustANGLE(target, GL_BUFFER_USAGE, 1, nullptr, &result); + return JS::Value(result); + } + + default: + dbgln("Unknown WebGL buffer parameter name: {:x}", pname); + set_error(GL_INVALID_ENUM); + return JS::js_null(); + } +} + +JS::Value WebGL2RenderingContextImpl::get_parameter(WebIDL::UnsignedLong pname) +{ + m_context->make_current(); + switch (pname) { + case GL_ACTIVE_TEXTURE: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_ACTIVE_TEXTURE, 1, nullptr, &result); + return JS::Value(result); + } + case GL_ALIASED_LINE_WIDTH_RANGE: { + Array result; + result.fill(0); + constexpr size_t buffer_size = 2 * sizeof(GLfloat); + glGetFloatvRobustANGLE(GL_ALIASED_LINE_WIDTH_RANGE, 2, nullptr, result.data()); + auto byte_buffer = MUST(ByteBuffer::copy(result.data(), buffer_size)); + auto array_buffer = JS::ArrayBuffer::create(m_realm, move(byte_buffer)); + return JS::Float32Array::create(m_realm, 2, array_buffer); + } + case GL_ALIASED_POINT_SIZE_RANGE: { + Array result; + result.fill(0); + constexpr size_t buffer_size = 2 * sizeof(GLfloat); + glGetFloatvRobustANGLE(GL_ALIASED_POINT_SIZE_RANGE, 2, nullptr, result.data()); + auto byte_buffer = MUST(ByteBuffer::copy(result.data(), buffer_size)); + auto array_buffer = JS::ArrayBuffer::create(m_realm, move(byte_buffer)); + return JS::Float32Array::create(m_realm, 2, array_buffer); + } + case GL_ALPHA_BITS: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_ALPHA_BITS, 1, nullptr, &result); + return JS::Value(result); + } + case GL_ARRAY_BUFFER_BINDING: { + if (!m_array_buffer_binding) + return JS::js_null(); + return JS::Value(m_array_buffer_binding); + } + case GL_BLEND: { + GLboolean result { GL_FALSE }; + glGetBooleanvRobustANGLE(GL_BLEND, 1, nullptr, &result); + return JS::Value(result == GL_TRUE); + } + case GL_BLEND_COLOR: { + Array result; + result.fill(0); + constexpr size_t buffer_size = 4 * sizeof(GLfloat); + glGetFloatvRobustANGLE(GL_BLEND_COLOR, 4, nullptr, result.data()); + auto byte_buffer = MUST(ByteBuffer::copy(result.data(), buffer_size)); + auto array_buffer = JS::ArrayBuffer::create(m_realm, move(byte_buffer)); + return JS::Float32Array::create(m_realm, 4, array_buffer); + } + case GL_BLEND_DST_ALPHA: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_BLEND_DST_ALPHA, 1, nullptr, &result); + return JS::Value(result); + } + case GL_BLEND_DST_RGB: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_BLEND_DST_RGB, 1, nullptr, &result); + return JS::Value(result); + } + case GL_BLEND_EQUATION_ALPHA: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_BLEND_EQUATION_ALPHA, 1, nullptr, &result); + return JS::Value(result); + } + case GL_BLEND_EQUATION_RGB: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_BLEND_EQUATION_RGB, 1, nullptr, &result); + return JS::Value(result); + } + case GL_BLEND_SRC_ALPHA: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_BLEND_SRC_ALPHA, 1, nullptr, &result); + return JS::Value(result); + } + case GL_BLEND_SRC_RGB: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_BLEND_SRC_RGB, 1, nullptr, &result); + return JS::Value(result); + } + case GL_BLUE_BITS: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_BLUE_BITS, 1, nullptr, &result); + return JS::Value(result); + } + case GL_COLOR_CLEAR_VALUE: { + Array result; + result.fill(0); + constexpr size_t buffer_size = 4 * sizeof(GLfloat); + glGetFloatvRobustANGLE(GL_COLOR_CLEAR_VALUE, 4, nullptr, result.data()); + auto byte_buffer = MUST(ByteBuffer::copy(result.data(), buffer_size)); + auto array_buffer = JS::ArrayBuffer::create(m_realm, move(byte_buffer)); + return JS::Float32Array::create(m_realm, 4, array_buffer); + } + case GL_CULL_FACE: { + GLboolean result { GL_FALSE }; + glGetBooleanvRobustANGLE(GL_CULL_FACE, 1, nullptr, &result); + return JS::Value(result == GL_TRUE); + } + case GL_CULL_FACE_MODE: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_CULL_FACE_MODE, 1, nullptr, &result); + return JS::Value(result); + } + case GL_CURRENT_PROGRAM: { + if (!m_current_program) + return JS::js_null(); + return JS::Value(m_current_program); + } + case GL_DEPTH_BITS: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_DEPTH_BITS, 1, nullptr, &result); + return JS::Value(result); + } + case GL_DEPTH_CLEAR_VALUE: { + GLfloat result { 0.0f }; + glGetFloatvRobustANGLE(GL_DEPTH_CLEAR_VALUE, 1, nullptr, &result); + return JS::Value(result); + } + case GL_DEPTH_FUNC: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_DEPTH_FUNC, 1, nullptr, &result); + return JS::Value(result); + } + case GL_DEPTH_RANGE: { + Array result; + result.fill(0); + constexpr size_t buffer_size = 2 * sizeof(GLfloat); + glGetFloatvRobustANGLE(GL_DEPTH_RANGE, 2, nullptr, result.data()); + auto byte_buffer = MUST(ByteBuffer::copy(result.data(), buffer_size)); + auto array_buffer = JS::ArrayBuffer::create(m_realm, move(byte_buffer)); + return JS::Float32Array::create(m_realm, 2, array_buffer); + } + case GL_DEPTH_TEST: { + GLboolean result { GL_FALSE }; + glGetBooleanvRobustANGLE(GL_DEPTH_TEST, 1, nullptr, &result); + return JS::Value(result == GL_TRUE); + } + case GL_DEPTH_WRITEMASK: { + GLboolean result { GL_FALSE }; + glGetBooleanvRobustANGLE(GL_DEPTH_WRITEMASK, 1, nullptr, &result); + return JS::Value(result == GL_TRUE); + } + case GL_DITHER: { + GLboolean result { GL_FALSE }; + glGetBooleanvRobustANGLE(GL_DITHER, 1, nullptr, &result); + return JS::Value(result == GL_TRUE); + } + case GL_ELEMENT_ARRAY_BUFFER_BINDING: { + if (!m_element_array_buffer_binding) + return JS::js_null(); + return JS::Value(m_element_array_buffer_binding); + } + case GL_FRAMEBUFFER_BINDING: { + if (!m_framebuffer_binding) + return JS::js_null(); + return JS::Value(m_framebuffer_binding); + } + case GL_FRONT_FACE: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_FRONT_FACE, 1, nullptr, &result); + return JS::Value(result); + } + case GL_GENERATE_MIPMAP_HINT: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_GENERATE_MIPMAP_HINT, 1, nullptr, &result); + return JS::Value(result); + } + case GL_GREEN_BITS: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_GREEN_BITS, 1, nullptr, &result); + return JS::Value(result); + } + case GL_IMPLEMENTATION_COLOR_READ_FORMAT: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_IMPLEMENTATION_COLOR_READ_FORMAT, 1, nullptr, &result); + return JS::Value(result); + } + case GL_IMPLEMENTATION_COLOR_READ_TYPE: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_IMPLEMENTATION_COLOR_READ_TYPE, 1, nullptr, &result); + return JS::Value(result); + } + case GL_LINE_WIDTH: { + GLfloat result { 0.0f }; + glGetFloatvRobustANGLE(GL_LINE_WIDTH, 1, nullptr, &result); + return JS::Value(result); + } + case GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, 1, nullptr, &result); + return JS::Value(result); + } + case GL_MAX_CUBE_MAP_TEXTURE_SIZE: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_MAX_CUBE_MAP_TEXTURE_SIZE, 1, nullptr, &result); + return JS::Value(result); + } + case GL_MAX_FRAGMENT_UNIFORM_VECTORS: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_MAX_FRAGMENT_UNIFORM_VECTORS, 1, nullptr, &result); + return JS::Value(result); + } + case GL_MAX_RENDERBUFFER_SIZE: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_MAX_RENDERBUFFER_SIZE, 1, nullptr, &result); + return JS::Value(result); + } + case GL_MAX_TEXTURE_IMAGE_UNITS: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_MAX_TEXTURE_IMAGE_UNITS, 1, nullptr, &result); + return JS::Value(result); + } + case GL_MAX_TEXTURE_SIZE: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_MAX_TEXTURE_SIZE, 1, nullptr, &result); + return JS::Value(result); + } + case GL_MAX_VARYING_VECTORS: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_MAX_VARYING_VECTORS, 1, nullptr, &result); + return JS::Value(result); + } + case GL_MAX_VERTEX_ATTRIBS: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_MAX_VERTEX_ATTRIBS, 1, nullptr, &result); + return JS::Value(result); + } + case GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, 1, nullptr, &result); + return JS::Value(result); + } + case GL_MAX_VERTEX_UNIFORM_VECTORS: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_MAX_VERTEX_UNIFORM_VECTORS, 1, nullptr, &result); + return JS::Value(result); + } + case GL_MAX_VIEWPORT_DIMS: { + Array result; + result.fill(0); + constexpr size_t buffer_size = 2 * sizeof(GLint); + glGetIntegervRobustANGLE(GL_MAX_VIEWPORT_DIMS, 2, nullptr, result.data()); + auto byte_buffer = MUST(ByteBuffer::copy(result.data(), buffer_size)); + auto array_buffer = JS::ArrayBuffer::create(m_realm, move(byte_buffer)); + return JS::Int32Array::create(m_realm, 2, array_buffer); + } + case GL_PACK_ALIGNMENT: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_PACK_ALIGNMENT, 1, nullptr, &result); + return JS::Value(result); + } + case GL_POLYGON_OFFSET_FACTOR: { + GLfloat result { 0.0f }; + glGetFloatvRobustANGLE(GL_POLYGON_OFFSET_FACTOR, 1, nullptr, &result); + return JS::Value(result); + } + case GL_POLYGON_OFFSET_FILL: { + GLboolean result { GL_FALSE }; + glGetBooleanvRobustANGLE(GL_POLYGON_OFFSET_FILL, 1, nullptr, &result); + return JS::Value(result == GL_TRUE); + } + case GL_POLYGON_OFFSET_UNITS: { + GLfloat result { 0.0f }; + glGetFloatvRobustANGLE(GL_POLYGON_OFFSET_UNITS, 1, nullptr, &result); + return JS::Value(result); + } + case GL_RED_BITS: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_RED_BITS, 1, nullptr, &result); + return JS::Value(result); + } + case GL_RENDERBUFFER_BINDING: { + if (!m_renderbuffer_binding) + return JS::js_null(); + return JS::Value(m_renderbuffer_binding); + } + case GL_RENDERER: { + auto result = reinterpret_cast(glGetString(GL_RENDERER)); + return JS::PrimitiveString::create(m_realm->vm(), ByteString { result }); + } + case GL_SAMPLE_ALPHA_TO_COVERAGE: { + GLboolean result { GL_FALSE }; + glGetBooleanvRobustANGLE(GL_SAMPLE_ALPHA_TO_COVERAGE, 1, nullptr, &result); + return JS::Value(result == GL_TRUE); + } + case GL_SAMPLE_BUFFERS: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_SAMPLE_BUFFERS, 1, nullptr, &result); + return JS::Value(result); + } + case GL_SAMPLE_COVERAGE: { + GLboolean result { GL_FALSE }; + glGetBooleanvRobustANGLE(GL_SAMPLE_COVERAGE, 1, nullptr, &result); + return JS::Value(result == GL_TRUE); + } + case GL_SAMPLE_COVERAGE_INVERT: { + GLboolean result { GL_FALSE }; + glGetBooleanvRobustANGLE(GL_SAMPLE_COVERAGE_INVERT, 1, nullptr, &result); + return JS::Value(result == GL_TRUE); + } + case GL_SAMPLE_COVERAGE_VALUE: { + GLfloat result { 0.0f }; + glGetFloatvRobustANGLE(GL_SAMPLE_COVERAGE_VALUE, 1, nullptr, &result); + return JS::Value(result); + } + case GL_SAMPLES: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_SAMPLES, 1, nullptr, &result); + return JS::Value(result); + } + case GL_SCISSOR_BOX: { + Array result; + result.fill(0); + constexpr size_t buffer_size = 4 * sizeof(GLint); + glGetIntegervRobustANGLE(GL_SCISSOR_BOX, 4, nullptr, result.data()); + auto byte_buffer = MUST(ByteBuffer::copy(result.data(), buffer_size)); + auto array_buffer = JS::ArrayBuffer::create(m_realm, move(byte_buffer)); + return JS::Int32Array::create(m_realm, 4, array_buffer); + } + case GL_SCISSOR_TEST: { + GLboolean result { GL_FALSE }; + glGetBooleanvRobustANGLE(GL_SCISSOR_TEST, 1, nullptr, &result); + return JS::Value(result == GL_TRUE); + } + case GL_SHADING_LANGUAGE_VERSION: { + auto result = reinterpret_cast(glGetString(GL_SHADING_LANGUAGE_VERSION)); + return JS::PrimitiveString::create(m_realm->vm(), ByteString { result }); + } + case GL_STENCIL_BACK_FAIL: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_STENCIL_BACK_FAIL, 1, nullptr, &result); + return JS::Value(result); + } + case GL_STENCIL_BACK_FUNC: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_STENCIL_BACK_FUNC, 1, nullptr, &result); + return JS::Value(result); + } + case GL_STENCIL_BACK_PASS_DEPTH_FAIL: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_STENCIL_BACK_PASS_DEPTH_FAIL, 1, nullptr, &result); + return JS::Value(result); + } + case GL_STENCIL_BACK_PASS_DEPTH_PASS: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_STENCIL_BACK_PASS_DEPTH_PASS, 1, nullptr, &result); + return JS::Value(result); + } + case GL_STENCIL_BACK_REF: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_STENCIL_BACK_REF, 1, nullptr, &result); + return JS::Value(result); + } + case GL_STENCIL_BACK_VALUE_MASK: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_STENCIL_BACK_VALUE_MASK, 1, nullptr, &result); + return JS::Value(result); + } + case GL_STENCIL_BACK_WRITEMASK: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_STENCIL_BACK_WRITEMASK, 1, nullptr, &result); + return JS::Value(result); + } + case GL_STENCIL_BITS: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_STENCIL_BITS, 1, nullptr, &result); + return JS::Value(result); + } + case GL_STENCIL_CLEAR_VALUE: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_STENCIL_CLEAR_VALUE, 1, nullptr, &result); + return JS::Value(result); + } + case GL_STENCIL_FAIL: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_STENCIL_FAIL, 1, nullptr, &result); + return JS::Value(result); + } + case GL_STENCIL_FUNC: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_STENCIL_FUNC, 1, nullptr, &result); + return JS::Value(result); + } + case GL_STENCIL_PASS_DEPTH_FAIL: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_STENCIL_PASS_DEPTH_FAIL, 1, nullptr, &result); + return JS::Value(result); + } + case GL_STENCIL_PASS_DEPTH_PASS: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_STENCIL_PASS_DEPTH_PASS, 1, nullptr, &result); + return JS::Value(result); + } + case GL_STENCIL_REF: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_STENCIL_REF, 1, nullptr, &result); + return JS::Value(result); + } + case GL_STENCIL_TEST: { + GLboolean result { GL_FALSE }; + glGetBooleanvRobustANGLE(GL_STENCIL_TEST, 1, nullptr, &result); + return JS::Value(result == GL_TRUE); + } + case GL_STENCIL_VALUE_MASK: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_STENCIL_VALUE_MASK, 1, nullptr, &result); + return JS::Value(result); + } + case GL_STENCIL_WRITEMASK: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_STENCIL_WRITEMASK, 1, nullptr, &result); + return JS::Value(result); + } + case GL_SUBPIXEL_BITS: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_SUBPIXEL_BITS, 1, nullptr, &result); + return JS::Value(result); + } + case GL_TEXTURE_BINDING_2D: { + if (!m_texture_binding_2d) + return JS::js_null(); + return JS::Value(m_texture_binding_2d); + } + case GL_TEXTURE_BINDING_CUBE_MAP: { + if (!m_texture_binding_cube_map) + return JS::js_null(); + return JS::Value(m_texture_binding_cube_map); + } + case GL_UNPACK_ALIGNMENT: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_UNPACK_ALIGNMENT, 1, nullptr, &result); + return JS::Value(result); + } + case GL_VENDOR: { + auto result = reinterpret_cast(glGetString(GL_VENDOR)); + return JS::PrimitiveString::create(m_realm->vm(), ByteString { result }); + } + case GL_VERSION: { + auto result = reinterpret_cast(glGetString(GL_VERSION)); + return JS::PrimitiveString::create(m_realm->vm(), ByteString { result }); + } + case GL_VIEWPORT: { + Array result; + result.fill(0); + constexpr size_t buffer_size = 4 * sizeof(GLint); + glGetIntegervRobustANGLE(GL_VIEWPORT, 4, nullptr, result.data()); + auto byte_buffer = MUST(ByteBuffer::copy(result.data(), buffer_size)); + auto array_buffer = JS::ArrayBuffer::create(m_realm, move(byte_buffer)); + return JS::Int32Array::create(m_realm, 4, array_buffer); + } + case GL_MAX_SAMPLES: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_MAX_SAMPLES, 1, nullptr, &result); + return JS::Value(result); + } + case GL_MAX_3D_TEXTURE_SIZE: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_MAX_3D_TEXTURE_SIZE, 1, nullptr, &result); + return JS::Value(result); + } + case GL_MAX_ARRAY_TEXTURE_LAYERS: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_MAX_ARRAY_TEXTURE_LAYERS, 1, nullptr, &result); + return JS::Value(result); + } + case GL_MAX_COLOR_ATTACHMENTS: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_MAX_COLOR_ATTACHMENTS, 1, nullptr, &result); + return JS::Value(result); + } + case GL_MAX_VERTEX_UNIFORM_COMPONENTS: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_MAX_VERTEX_UNIFORM_COMPONENTS, 1, nullptr, &result); + return JS::Value(result); + } + case GL_MAX_UNIFORM_BLOCK_SIZE: { + GLint64 result { 0 }; + glGetInteger64vRobustANGLE(GL_MAX_UNIFORM_BLOCK_SIZE, 1, nullptr, &result); + return JS::Value(static_cast(result)); + } + case GL_MAX_UNIFORM_BUFFER_BINDINGS: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_MAX_UNIFORM_BUFFER_BINDINGS, 1, nullptr, &result); + return JS::Value(result); + } + case GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, 1, nullptr, &result); + return JS::Value(result); + } + case GL_MAX_DRAW_BUFFERS: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_MAX_DRAW_BUFFERS, 1, nullptr, &result); + return JS::Value(result); + } + case GL_MAX_VERTEX_UNIFORM_BLOCKS: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_MAX_VERTEX_UNIFORM_BLOCKS, 1, nullptr, &result); + return JS::Value(result); + } + case GL_MAX_FRAGMENT_INPUT_COMPONENTS: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_MAX_FRAGMENT_INPUT_COMPONENTS, 1, nullptr, &result); + return JS::Value(result); + } + case GL_MAX_FRAGMENT_UNIFORM_COMPONENTS: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, 1, nullptr, &result); + return JS::Value(result); + } + case GL_MAX_COMBINED_UNIFORM_BLOCKS: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_MAX_COMBINED_UNIFORM_BLOCKS, 1, nullptr, &result); + return JS::Value(result); + } + case GL_MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS: { + GLint64 result { 0 }; + glGetInteger64vRobustANGLE(GL_MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS, 1, nullptr, &result); + return JS::Value(static_cast(result)); + } + case GL_MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS: { + GLint64 result { 0 }; + glGetInteger64vRobustANGLE(GL_MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS, 1, nullptr, &result); + return JS::Value(static_cast(result)); + } + case GL_UNIFORM_BUFFER_BINDING: { + if (!m_uniform_buffer_binding) + return JS::js_null(); + return JS::Value(m_uniform_buffer_binding); + } + case GL_TEXTURE_BINDING_2D_ARRAY: { + if (!m_texture_binding_2d_array) + return JS::js_null(); + return JS::Value(m_texture_binding_2d_array); + } + case GL_COPY_READ_BUFFER_BINDING: { + if (!m_copy_read_buffer_binding) + return JS::js_null(); + return JS::Value(m_copy_read_buffer_binding); + } + case GL_COPY_WRITE_BUFFER_BINDING: { + if (!m_copy_write_buffer_binding) + return JS::js_null(); + return JS::Value(m_copy_write_buffer_binding); + } + case GL_MAX_ELEMENT_INDEX: { + GLint64 result { 0 }; + glGetInteger64vRobustANGLE(GL_MAX_ELEMENT_INDEX, 1, nullptr, &result); + return JS::Value(static_cast(result)); + } + case GL_MAX_FRAGMENT_UNIFORM_BLOCKS: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_MAX_FRAGMENT_UNIFORM_BLOCKS, 1, nullptr, &result); + return JS::Value(result); + } + case GL_MAX_VARYING_COMPONENTS: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_MAX_VARYING_COMPONENTS, 1, nullptr, &result); + return JS::Value(result); + } + case GL_MAX_ELEMENTS_INDICES: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_MAX_ELEMENTS_INDICES, 1, nullptr, &result); + return JS::Value(result); + } + case GL_MAX_ELEMENTS_VERTICES: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_MAX_ELEMENTS_VERTICES, 1, nullptr, &result); + return JS::Value(result); + } + case GL_MAX_TEXTURE_LOD_BIAS: { + GLfloat result { 0.0f }; + glGetFloatvRobustANGLE(GL_MAX_TEXTURE_LOD_BIAS, 1, nullptr, &result); + return JS::Value(result); + } + case GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS, 1, nullptr, &result); + return JS::Value(result); + } + case GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS, 1, nullptr, &result); + return JS::Value(result); + } + case GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS, 1, nullptr, &result); + return JS::Value(result); + } + case GL_MIN_PROGRAM_TEXEL_OFFSET: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_MIN_PROGRAM_TEXEL_OFFSET, 1, nullptr, &result); + return JS::Value(result); + } + case GL_MAX_PROGRAM_TEXEL_OFFSET: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_MAX_PROGRAM_TEXEL_OFFSET, 1, nullptr, &result); + return JS::Value(result); + } + case GL_MAX_VERTEX_OUTPUT_COMPONENTS: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_MAX_VERTEX_OUTPUT_COMPONENTS, 1, nullptr, &result); + return JS::Value(result); + } + case GL_MAX_SERVER_WAIT_TIMEOUT: { + GLint64 result { 0 }; + glGetInteger64vRobustANGLE(GL_MAX_SERVER_WAIT_TIMEOUT, 1, nullptr, &result); + return JS::Value(static_cast(result)); + } + default: + dbgln("Unknown WebGL parameter name: {:x}", pname); + set_error(GL_INVALID_ENUM); + return JS::js_null(); + } +} + +WebIDL::UnsignedLong WebGL2RenderingContextImpl::get_error() +{ + m_context->make_current(); + return glGetError(); +} + +JS::Value WebGL2RenderingContextImpl::get_program_parameter(GC::Root program, WebIDL::UnsignedLong pname) +{ + m_context->make_current(); + + GLuint program_handle = 0; + if (program) { + auto handle_or_error = program->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return JS::js_null(); + } + program_handle = handle_or_error.release_value(); + } + + GLint result = 0; + glGetProgramivRobustANGLE(program_handle, pname, 1, nullptr, &result); + switch (pname) { + case GL_ATTACHED_SHADERS: + case GL_ACTIVE_ATTRIBUTES: + case GL_ACTIVE_UNIFORMS: + + case GL_TRANSFORM_FEEDBACK_BUFFER_MODE: + case GL_TRANSFORM_FEEDBACK_VARYINGS: + case GL_ACTIVE_UNIFORM_BLOCKS: + + return JS::Value(result); + case GL_DELETE_STATUS: + case GL_LINK_STATUS: + case GL_VALIDATE_STATUS: + return JS::Value(result == GL_TRUE); + default: + dbgln("Unknown WebGL program parameter name: 0x{:04x}", pname); + set_error(GL_INVALID_ENUM); + return JS::js_null(); + } +} + +Optional WebGL2RenderingContextImpl::get_program_info_log(GC::Root program) +{ + m_context->make_current(); + + GLuint program_handle = 0; + if (program) { + auto handle_or_error = program->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return {}; + } + program_handle = handle_or_error.release_value(); + } + + GLint info_log_length = 0; + glGetProgramiv(program_handle, GL_INFO_LOG_LENGTH, &info_log_length); + Vector info_log; + info_log.resize(info_log_length); + if (!info_log_length) + return String {}; + glGetProgramInfoLog(program_handle, info_log_length, nullptr, info_log.data()); + return String::from_utf8_without_validation(ReadonlyBytes { info_log.data(), static_cast(info_log_length - 1) }); +} + +JS::Value WebGL2RenderingContextImpl::get_shader_parameter(GC::Root shader, WebIDL::UnsignedLong pname) +{ + m_context->make_current(); + + GLuint shader_handle = 0; + if (shader) { + auto handle_or_error = shader->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return JS::js_null(); + } + shader_handle = handle_or_error.release_value(); + } + + GLint result = 0; + glGetShaderivRobustANGLE(shader_handle, pname, 1, nullptr, &result); + switch (pname) { + case GL_SHADER_TYPE: + return JS::Value(result); + case GL_DELETE_STATUS: + case GL_COMPILE_STATUS: + return JS::Value(result == GL_TRUE); + default: + dbgln("Unknown WebGL shader parameter name: 0x{:04x}", pname); + set_error(GL_INVALID_ENUM); + return JS::js_null(); + } +} + +GC::Root WebGL2RenderingContextImpl::get_shader_precision_format(WebIDL::UnsignedLong shadertype, WebIDL::UnsignedLong precisiontype) +{ + m_context->make_current(); + + GLint range[2]; + GLint precision; + glGetShaderPrecisionFormat(shadertype, precisiontype, range, &precision); + return WebGLShaderPrecisionFormat::create(m_realm, range[0], range[1], precision); +} + +Optional WebGL2RenderingContextImpl::get_shader_info_log(GC::Root shader) +{ + m_context->make_current(); + + GLuint shader_handle = 0; + if (shader) { + auto handle_or_error = shader->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return {}; + } + shader_handle = handle_or_error.release_value(); + } + + GLint info_log_length = 0; + glGetShaderiv(shader_handle, GL_INFO_LOG_LENGTH, &info_log_length); + Vector info_log; + info_log.resize(info_log_length); + if (!info_log_length) + return String {}; + glGetShaderInfoLog(shader_handle, info_log_length, nullptr, info_log.data()); + return String::from_utf8_without_validation(ReadonlyBytes { info_log.data(), static_cast(info_log_length - 1) }); +} + +GC::Root WebGL2RenderingContextImpl::get_uniform_location(GC::Root program, String name) +{ + m_context->make_current(); + + GLuint program_handle = 0; + if (program) { + auto handle_or_error = program->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return {}; + } + program_handle = handle_or_error.release_value(); + } + + auto name_null_terminated = null_terminated_string(name); + return WebGLUniformLocation::create(m_realm, glGetUniformLocation(program_handle, name_null_terminated.data())); +} + +void WebGL2RenderingContextImpl::hint(WebIDL::UnsignedLong target, WebIDL::UnsignedLong mode) +{ + m_context->make_current(); + glHint(target, mode); +} + +bool WebGL2RenderingContextImpl::is_buffer(GC::Root buffer) +{ + m_context->make_current(); + + auto buffer_handle = 0; + if (buffer) { + auto handle_or_error = buffer->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return false; + } + buffer_handle = handle_or_error.release_value(); + } + return glIsBuffer(buffer_handle); +} + +bool WebGL2RenderingContextImpl::is_enabled(WebIDL::UnsignedLong cap) +{ + m_context->make_current(); + return glIsEnabled(cap); +} + +bool WebGL2RenderingContextImpl::is_framebuffer(GC::Root framebuffer) +{ + m_context->make_current(); + + auto framebuffer_handle = 0; + if (framebuffer) { + auto handle_or_error = framebuffer->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return false; + } + framebuffer_handle = handle_or_error.release_value(); + } + return glIsFramebuffer(framebuffer_handle); +} + +bool WebGL2RenderingContextImpl::is_program(GC::Root program) +{ + m_context->make_current(); + + auto program_handle = 0; + if (program) { + auto handle_or_error = program->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return false; + } + program_handle = handle_or_error.release_value(); + } + return glIsProgram(program_handle); +} + +bool WebGL2RenderingContextImpl::is_renderbuffer(GC::Root renderbuffer) +{ + m_context->make_current(); + + auto renderbuffer_handle = 0; + if (renderbuffer) { + auto handle_or_error = renderbuffer->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return false; + } + renderbuffer_handle = handle_or_error.release_value(); + } + return glIsRenderbuffer(renderbuffer_handle); +} + +bool WebGL2RenderingContextImpl::is_shader(GC::Root shader) +{ + m_context->make_current(); + + auto shader_handle = 0; + if (shader) { + auto handle_or_error = shader->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return false; + } + shader_handle = handle_or_error.release_value(); + } + return glIsShader(shader_handle); +} + +bool WebGL2RenderingContextImpl::is_texture(GC::Root texture) +{ + m_context->make_current(); + + auto texture_handle = 0; + if (texture) { + auto handle_or_error = texture->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return false; + } + texture_handle = handle_or_error.release_value(); + } + return glIsTexture(texture_handle); +} + +void WebGL2RenderingContextImpl::line_width(float width) +{ + m_context->make_current(); + glLineWidth(width); +} + +void WebGL2RenderingContextImpl::link_program(GC::Root program) +{ + m_context->make_current(); + + auto program_handle = 0; + if (program) { + auto handle_or_error = program->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return; + } + program_handle = handle_or_error.release_value(); + } + glLinkProgram(program_handle); +} + +void WebGL2RenderingContextImpl::pixel_storei(WebIDL::UnsignedLong pname, WebIDL::Long param) +{ + m_context->make_current(); + glPixelStorei(pname, param); +} + +void WebGL2RenderingContextImpl::polygon_offset(float factor, float units) +{ + m_context->make_current(); + glPolygonOffset(factor, units); +} + +void WebGL2RenderingContextImpl::renderbuffer_storage(WebIDL::UnsignedLong target, WebIDL::UnsignedLong internalformat, WebIDL::Long width, WebIDL::Long height) +{ + m_context->make_current(); + + if (internalformat == GL_DEPTH_STENCIL) + internalformat = GL_DEPTH24_STENCIL8; + + glRenderbufferStorage(target, internalformat, width, height); +} + +void WebGL2RenderingContextImpl::sample_coverage(float value, bool invert) +{ + m_context->make_current(); + glSampleCoverage(value, invert); +} + +void WebGL2RenderingContextImpl::scissor(WebIDL::Long x, WebIDL::Long y, WebIDL::Long width, WebIDL::Long height) +{ + m_context->make_current(); + glScissor(x, y, width, height); +} + +void WebGL2RenderingContextImpl::shader_source(GC::Root shader, String source) +{ + m_context->make_current(); + + GLuint shader_handle = 0; + if (shader) { + auto handle_or_error = shader->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return; + } + shader_handle = handle_or_error.release_value(); + } + + Vector strings; + auto string = null_terminated_string(source); + strings.append(string.data()); + Vector length; + length.append(source.bytes().size()); + glShaderSource(shader_handle, 1, strings.data(), length.data()); +} + +void WebGL2RenderingContextImpl::stencil_func(WebIDL::UnsignedLong func, WebIDL::Long ref, WebIDL::UnsignedLong mask) +{ + m_context->make_current(); + glStencilFunc(func, ref, mask); +} + +void WebGL2RenderingContextImpl::stencil_func_separate(WebIDL::UnsignedLong face, WebIDL::UnsignedLong func, WebIDL::Long ref, WebIDL::UnsignedLong mask) +{ + m_context->make_current(); + glStencilFuncSeparate(face, func, ref, mask); +} + +void WebGL2RenderingContextImpl::stencil_mask(WebIDL::UnsignedLong mask) +{ + m_context->make_current(); + glStencilMask(mask); +} + +void WebGL2RenderingContextImpl::stencil_mask_separate(WebIDL::UnsignedLong face, WebIDL::UnsignedLong mask) +{ + m_context->make_current(); + glStencilMaskSeparate(face, mask); +} + +void WebGL2RenderingContextImpl::stencil_op(WebIDL::UnsignedLong fail, WebIDL::UnsignedLong zfail, WebIDL::UnsignedLong zpass) +{ + m_context->make_current(); + glStencilOp(fail, zfail, zpass); +} + +void WebGL2RenderingContextImpl::stencil_op_separate(WebIDL::UnsignedLong face, WebIDL::UnsignedLong fail, WebIDL::UnsignedLong zfail, WebIDL::UnsignedLong zpass) +{ + m_context->make_current(); + glStencilOpSeparate(face, fail, zfail, zpass); +} + +void WebGL2RenderingContextImpl::tex_parameterf(WebIDL::UnsignedLong target, WebIDL::UnsignedLong pname, float param) +{ + m_context->make_current(); + glTexParameterf(target, pname, param); +} + +void WebGL2RenderingContextImpl::tex_parameteri(WebIDL::UnsignedLong target, WebIDL::UnsignedLong pname, WebIDL::Long param) +{ + m_context->make_current(); + glTexParameteri(target, pname, param); +} + +void WebGL2RenderingContextImpl::uniform1f(GC::Root location, float x) +{ + m_context->make_current(); + glUniform1f(location ? location->handle() : 0, x); +} + +void WebGL2RenderingContextImpl::uniform2f(GC::Root location, float x, float y) +{ + m_context->make_current(); + glUniform2f(location ? location->handle() : 0, x, y); +} + +void WebGL2RenderingContextImpl::uniform3f(GC::Root location, float x, float y, float z) +{ + m_context->make_current(); + glUniform3f(location ? location->handle() : 0, x, y, z); +} + +void WebGL2RenderingContextImpl::uniform4f(GC::Root location, float x, float y, float z, float w) +{ + m_context->make_current(); + glUniform4f(location ? location->handle() : 0, x, y, z, w); +} + +void WebGL2RenderingContextImpl::uniform1i(GC::Root location, WebIDL::Long x) +{ + m_context->make_current(); + glUniform1i(location ? location->handle() : 0, x); +} + +void WebGL2RenderingContextImpl::uniform2i(GC::Root location, WebIDL::Long x, WebIDL::Long y) +{ + m_context->make_current(); + glUniform2i(location ? location->handle() : 0, x, y); +} + +void WebGL2RenderingContextImpl::uniform3i(GC::Root location, WebIDL::Long x, WebIDL::Long y, WebIDL::Long z) +{ + m_context->make_current(); + glUniform3i(location ? location->handle() : 0, x, y, z); +} + +void WebGL2RenderingContextImpl::uniform4i(GC::Root location, WebIDL::Long x, WebIDL::Long y, WebIDL::Long z, WebIDL::Long w) +{ + m_context->make_current(); + glUniform4i(location ? location->handle() : 0, x, y, z, w); +} + +void WebGL2RenderingContextImpl::use_program(GC::Root program) +{ + m_context->make_current(); + + GLuint program_handle = 0; + if (program) { + auto handle_or_error = program->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return; + } + program_handle = handle_or_error.release_value(); + } + + glUseProgram(program_handle); + m_current_program = program; +} + +void WebGL2RenderingContextImpl::validate_program(GC::Root program) +{ + m_context->make_current(); + + auto program_handle = 0; + if (program) { + auto handle_or_error = program->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return; + } + program_handle = handle_or_error.release_value(); + } + glValidateProgram(program_handle); +} + +void WebGL2RenderingContextImpl::vertex_attrib1f(WebIDL::UnsignedLong index, float x) +{ + m_context->make_current(); + glVertexAttrib1f(index, x); +} + +void WebGL2RenderingContextImpl::vertex_attrib2f(WebIDL::UnsignedLong index, float x, float y) +{ + m_context->make_current(); + glVertexAttrib2f(index, x, y); +} + +void WebGL2RenderingContextImpl::vertex_attrib3f(WebIDL::UnsignedLong index, float x, float y, float z) +{ + m_context->make_current(); + glVertexAttrib3f(index, x, y, z); +} + +void WebGL2RenderingContextImpl::vertex_attrib4f(WebIDL::UnsignedLong index, float x, float y, float z, float w) +{ + m_context->make_current(); + glVertexAttrib4f(index, x, y, z, w); +} + +void WebGL2RenderingContextImpl::vertex_attrib1fv(WebIDL::UnsignedLong index, Variant, Vector> values) +{ + m_context->make_current(); + + if (values.has>()) { + auto& data = values.get>(); + if (data.size() < 1) { + set_error(GL_INVALID_VALUE); + return; + } + + glVertexAttrib1fv(index, data.data()); + return; + } + + auto& buffer_source = values.get>(); + if (buffer_source->byte_length() < 1 * sizeof(float)) { + set_error(GL_INVALID_VALUE); + return; + } + + auto& typed_array_base = static_cast(*buffer_source->raw_object()); + auto& float32_array = as(typed_array_base); + float const* data = float32_array.data().data(); + glVertexAttrib1fv(index, data); +} + +void WebGL2RenderingContextImpl::vertex_attrib2fv(WebIDL::UnsignedLong index, Variant, Vector> values) +{ + m_context->make_current(); + + if (values.has>()) { + auto& data = values.get>(); + if (data.size() < 2) { + set_error(GL_INVALID_VALUE); + return; + } + + glVertexAttrib2fv(index, data.data()); + return; + } + + auto& buffer_source = values.get>(); + if (buffer_source->byte_length() < 2 * sizeof(float)) { + set_error(GL_INVALID_VALUE); + return; + } + + auto& typed_array_base = static_cast(*buffer_source->raw_object()); + auto& float32_array = as(typed_array_base); + float const* data = float32_array.data().data(); + glVertexAttrib2fv(index, data); +} + +void WebGL2RenderingContextImpl::vertex_attrib3fv(WebIDL::UnsignedLong index, Variant, Vector> values) +{ + m_context->make_current(); + + if (values.has>()) { + auto& data = values.get>(); + if (data.size() < 3) { + set_error(GL_INVALID_VALUE); + return; + } + + glVertexAttrib3fv(index, data.data()); + return; + } + + auto& buffer_source = values.get>(); + if (buffer_source->byte_length() < 3 * sizeof(float)) { + set_error(GL_INVALID_VALUE); + return; + } + + auto& typed_array_base = static_cast(*buffer_source->raw_object()); + auto& float32_array = as(typed_array_base); + float const* data = float32_array.data().data(); + glVertexAttrib3fv(index, data); +} + +void WebGL2RenderingContextImpl::vertex_attrib4fv(WebIDL::UnsignedLong index, Variant, Vector> values) +{ + m_context->make_current(); + + if (values.has>()) { + auto& data = values.get>(); + if (data.size() < 4) { + set_error(GL_INVALID_VALUE); + return; + } + + glVertexAttrib4fv(index, data.data()); + return; + } + + auto& buffer_source = values.get>(); + if (buffer_source->byte_length() < 4 * sizeof(float)) { + set_error(GL_INVALID_VALUE); + return; + } + + auto& typed_array_base = static_cast(*buffer_source->raw_object()); + auto& float32_array = as(typed_array_base); + float const* data = float32_array.data().data(); + glVertexAttrib4fv(index, data); +} + +void WebGL2RenderingContextImpl::vertex_attrib_pointer(WebIDL::UnsignedLong index, WebIDL::Long size, WebIDL::UnsignedLong type, bool normalized, WebIDL::Long stride, WebIDL::LongLong offset) +{ + m_context->make_current(); + + glVertexAttribPointer(index, size, type, normalized, stride, reinterpret_cast(offset)); +} + +void WebGL2RenderingContextImpl::viewport(WebIDL::Long x, WebIDL::Long y, WebIDL::Long width, WebIDL::Long height) +{ + m_context->make_current(); + glViewport(x, y, width, height); +} + +void WebGL2RenderingContextImpl::visit_edges(JS::Cell::Visitor& visitor) +{ + visitor.visit(m_realm); + visitor.visit(m_array_buffer_binding); + visitor.visit(m_element_array_buffer_binding); + visitor.visit(m_current_program); + visitor.visit(m_framebuffer_binding); + visitor.visit(m_renderbuffer_binding); + visitor.visit(m_texture_binding_2d); + visitor.visit(m_texture_binding_cube_map); + + visitor.visit(m_uniform_buffer_binding); + visitor.visit(m_copy_read_buffer_binding); + visitor.visit(m_copy_write_buffer_binding); + visitor.visit(m_texture_binding_2d_array); + visitor.visit(m_texture_binding_3d); +} + +} diff --git a/Libraries/LibWeb/WebGL/WebGL2RenderingContextImpl.h b/Libraries/LibWeb/WebGL/WebGL2RenderingContextImpl.h new file mode 100644 index 00000000000..500bc2ac00d --- /dev/null +++ b/Libraries/LibWeb/WebGL/WebGL2RenderingContextImpl.h @@ -0,0 +1,232 @@ +/* + * Copyright (c) 2024, Aliaksandr Kalenik + * Copyright (c) 2024-2025, Luke Wilde + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace Web::WebGL { + +using namespace Web::HTML; + +class WebGL2RenderingContextImpl : public WebGLRenderingContextBase { +public: + WebGL2RenderingContextImpl(JS::Realm&, NonnullOwnPtr); + + virtual OpenGLContext& context() override { return *m_context; } + + virtual void present() = 0; + virtual void needs_to_present() = 0; + virtual void set_error(GLenum) = 0; + void copy_buffer_sub_data(WebIDL::UnsignedLong read_target, WebIDL::UnsignedLong write_target, WebIDL::LongLong read_offset, WebIDL::LongLong write_offset, WebIDL::LongLong size); + void blit_framebuffer(WebIDL::Long src_x0, WebIDL::Long src_y0, WebIDL::Long src_x1, WebIDL::Long src_y1, WebIDL::Long dst_x0, WebIDL::Long dst_y0, WebIDL::Long dst_x1, WebIDL::Long dst_y1, WebIDL::UnsignedLong mask, WebIDL::UnsignedLong filter); + void invalidate_framebuffer(WebIDL::UnsignedLong target, Vector attachments); + void read_buffer(WebIDL::UnsignedLong src); + JS::Value get_internalformat_parameter(WebIDL::UnsignedLong target, WebIDL::UnsignedLong internalformat, WebIDL::UnsignedLong pname); + void renderbuffer_storage_multisample(WebIDL::UnsignedLong target, WebIDL::Long samples, WebIDL::UnsignedLong internalformat, WebIDL::Long width, WebIDL::Long height); + void tex_storage2d(WebIDL::UnsignedLong target, WebIDL::Long levels, WebIDL::UnsignedLong internalformat, WebIDL::Long width, WebIDL::Long height); + void tex_storage3d(WebIDL::UnsignedLong target, WebIDL::Long levels, WebIDL::UnsignedLong internalformat, WebIDL::Long width, WebIDL::Long height, WebIDL::Long depth); + void tex_image3d(WebIDL::UnsignedLong target, WebIDL::Long level, WebIDL::Long internalformat, WebIDL::Long width, WebIDL::Long height, WebIDL::Long depth, WebIDL::Long border, WebIDL::UnsignedLong format, WebIDL::UnsignedLong type, GC::Root src_data); + void tex_image3d(WebIDL::UnsignedLong target, WebIDL::Long level, WebIDL::Long internalformat, WebIDL::Long width, WebIDL::Long height, WebIDL::Long depth, WebIDL::Long border, WebIDL::UnsignedLong format, WebIDL::UnsignedLong type, GC::Root src_data, WebIDL::UnsignedLongLong src_offset); + void tex_sub_image3d(WebIDL::UnsignedLong target, WebIDL::Long level, WebIDL::Long xoffset, WebIDL::Long yoffset, WebIDL::Long zoffset, WebIDL::Long width, WebIDL::Long height, WebIDL::Long depth, WebIDL::UnsignedLong format, WebIDL::UnsignedLong type, GC::Root src_data, WebIDL::UnsignedLongLong src_offset); + void uniform1ui(GC::Root location, WebIDL::UnsignedLong v0); + void uniform2ui(GC::Root location, WebIDL::UnsignedLong v0, WebIDL::UnsignedLong v1); + void uniform3ui(GC::Root location, WebIDL::UnsignedLong v0, WebIDL::UnsignedLong v1, WebIDL::UnsignedLong v2); + void uniform4ui(GC::Root location, WebIDL::UnsignedLong v0, WebIDL::UnsignedLong v1, WebIDL::UnsignedLong v2, WebIDL::UnsignedLong v3); + void vertex_attrib_i_pointer(WebIDL::UnsignedLong index, WebIDL::Long size, WebIDL::UnsignedLong type, WebIDL::Long stride, WebIDL::LongLong offset); + void vertex_attrib_divisor(WebIDL::UnsignedLong index, WebIDL::UnsignedLong divisor); + void draw_arrays_instanced(WebIDL::UnsignedLong mode, WebIDL::Long first, WebIDL::Long count, WebIDL::Long instance_count); + void draw_elements_instanced(WebIDL::UnsignedLong mode, WebIDL::Long count, WebIDL::UnsignedLong type, WebIDL::LongLong offset, WebIDL::Long instance_count); + void draw_buffers(Vector buffers); + void clear_bufferfv(WebIDL::UnsignedLong buffer, WebIDL::Long drawbuffer, Variant, Vector> values, WebIDL::UnsignedLongLong src_offset); + void clear_bufferiv(WebIDL::UnsignedLong buffer, WebIDL::Long drawbuffer, Variant, Vector> values, WebIDL::UnsignedLongLong src_offset); + void clear_bufferuiv(WebIDL::UnsignedLong buffer, WebIDL::Long drawbuffer, Variant, Vector> values, WebIDL::UnsignedLongLong src_offset); + void clear_bufferfi(WebIDL::UnsignedLong buffer, WebIDL::Long drawbuffer, float depth, WebIDL::Long stencil); + GC::Root create_sampler(); + void delete_sampler(GC::Root sampler); + void bind_sampler(WebIDL::UnsignedLong unit, GC::Root sampler); + void sampler_parameteri(GC::Root sampler, WebIDL::UnsignedLong pname, WebIDL::Long param); + void sampler_parameterf(GC::Root sampler, WebIDL::UnsignedLong pname, float param); + GC::Root fence_sync(WebIDL::UnsignedLong condition, WebIDL::UnsignedLong flags); + void delete_sync(GC::Root sync); + WebIDL::UnsignedLong client_wait_sync(GC::Root sync, WebIDL::UnsignedLong flags, WebIDL::UnsignedLongLong timeout); + JS::Value get_sync_parameter(GC::Root sync, WebIDL::UnsignedLong pname); + void bind_buffer_base(WebIDL::UnsignedLong target, WebIDL::UnsignedLong index, GC::Root buffer); + void bind_buffer_range(WebIDL::UnsignedLong target, WebIDL::UnsignedLong index, GC::Root buffer, WebIDL::LongLong offset, WebIDL::LongLong size); + JS::Value get_active_uniforms(GC::Root program, Vector uniform_indices, WebIDL::UnsignedLong pname); + WebIDL::UnsignedLong get_uniform_block_index(GC::Root program, String uniform_block_name); + JS::Value get_active_uniform_block_parameter(GC::Root program, WebIDL::UnsignedLong uniform_block_index, WebIDL::UnsignedLong pname); + Optional get_active_uniform_block_name(GC::Root program, WebIDL::UnsignedLong uniform_block_index); + void uniform_block_binding(GC::Root program, WebIDL::UnsignedLong uniform_block_index, WebIDL::UnsignedLong uniform_block_binding); + GC::Root create_vertex_array(); + void delete_vertex_array(GC::Root vertex_array); + bool is_vertex_array(GC::Root vertex_array); + void bind_vertex_array(GC::Root array); + void buffer_data(WebIDL::UnsignedLong target, WebIDL::LongLong size, WebIDL::UnsignedLong usage); + void buffer_data(WebIDL::UnsignedLong target, GC::Root src_data, WebIDL::UnsignedLong usage); + void buffer_sub_data(WebIDL::UnsignedLong target, WebIDL::LongLong dst_byte_offset, GC::Root src_data); + void buffer_data(WebIDL::UnsignedLong target, GC::Root src_data, WebIDL::UnsignedLong usage, WebIDL::UnsignedLongLong src_offset, WebIDL::UnsignedLong length); + void buffer_sub_data(WebIDL::UnsignedLong target, WebIDL::LongLong dst_byte_offset, GC::Root src_data, WebIDL::UnsignedLongLong src_offset, WebIDL::UnsignedLong length); + void tex_image2d(WebIDL::UnsignedLong target, WebIDL::Long level, WebIDL::Long internalformat, WebIDL::Long width, WebIDL::Long height, WebIDL::Long border, WebIDL::UnsignedLong format, WebIDL::UnsignedLong type, GC::Root pixels); + void tex_image2d(WebIDL::UnsignedLong target, WebIDL::Long level, WebIDL::Long internalformat, WebIDL::UnsignedLong format, WebIDL::UnsignedLong type, Variant, GC::Root, GC::Root, GC::Root, GC::Root, GC::Root> source); + void tex_sub_image2d(WebIDL::UnsignedLong target, WebIDL::Long level, WebIDL::Long xoffset, WebIDL::Long yoffset, WebIDL::Long width, WebIDL::Long height, WebIDL::UnsignedLong format, WebIDL::UnsignedLong type, GC::Root pixels); + void tex_sub_image2d(WebIDL::UnsignedLong target, WebIDL::Long level, WebIDL::Long xoffset, WebIDL::Long yoffset, WebIDL::UnsignedLong format, WebIDL::UnsignedLong type, Variant, GC::Root, GC::Root, GC::Root, GC::Root, GC::Root> source); + void tex_image2d(WebIDL::UnsignedLong target, WebIDL::Long level, WebIDL::Long internalformat, WebIDL::Long width, WebIDL::Long height, WebIDL::Long border, WebIDL::UnsignedLong format, WebIDL::UnsignedLong type, Variant, GC::Root, GC::Root, GC::Root, GC::Root, GC::Root> source); + void tex_image2d(WebIDL::UnsignedLong target, WebIDL::Long level, WebIDL::Long internalformat, WebIDL::Long width, WebIDL::Long height, WebIDL::Long border, WebIDL::UnsignedLong format, WebIDL::UnsignedLong type, GC::Root src_data, WebIDL::UnsignedLongLong src_offset); + void tex_sub_image2d(WebIDL::UnsignedLong target, WebIDL::Long level, WebIDL::Long xoffset, WebIDL::Long yoffset, WebIDL::Long width, WebIDL::Long height, WebIDL::UnsignedLong format, WebIDL::UnsignedLong type, Variant, GC::Root, GC::Root, GC::Root, GC::Root, GC::Root> source); + void tex_sub_image2d(WebIDL::UnsignedLong target, WebIDL::Long level, WebIDL::Long xoffset, WebIDL::Long yoffset, WebIDL::Long width, WebIDL::Long height, WebIDL::UnsignedLong format, WebIDL::UnsignedLong type, GC::Root src_data, WebIDL::UnsignedLongLong src_offset); + void compressed_tex_image2d(WebIDL::UnsignedLong target, WebIDL::Long level, WebIDL::UnsignedLong internalformat, WebIDL::Long width, WebIDL::Long height, WebIDL::Long border, GC::Root src_data, WebIDL::UnsignedLongLong src_offset, WebIDL::UnsignedLong src_length_override); + void compressed_tex_sub_image2d(WebIDL::UnsignedLong target, WebIDL::Long level, WebIDL::Long xoffset, WebIDL::Long yoffset, WebIDL::Long width, WebIDL::Long height, WebIDL::UnsignedLong format, GC::Root src_data, WebIDL::UnsignedLongLong src_offset, WebIDL::UnsignedLong src_length_override); + void uniform1fv(GC::Root location, Variant, Vector> v, WebIDL::UnsignedLongLong src_offset, WebIDL::UnsignedLong src_length); + void uniform2fv(GC::Root location, Variant, Vector> v, WebIDL::UnsignedLongLong src_offset, WebIDL::UnsignedLong src_length); + void uniform3fv(GC::Root location, Variant, Vector> v, WebIDL::UnsignedLongLong src_offset, WebIDL::UnsignedLong src_length); + void uniform4fv(GC::Root location, Variant, Vector> v, WebIDL::UnsignedLongLong src_offset, WebIDL::UnsignedLong src_length); + void uniform1iv(GC::Root location, Variant, Vector> v, WebIDL::UnsignedLongLong src_offset, WebIDL::UnsignedLong src_length); + void uniform2iv(GC::Root location, Variant, Vector> v, WebIDL::UnsignedLongLong src_offset, WebIDL::UnsignedLong src_length); + void uniform3iv(GC::Root location, Variant, Vector> v, WebIDL::UnsignedLongLong src_offset, WebIDL::UnsignedLong src_length); + void uniform4iv(GC::Root location, Variant, Vector> v, WebIDL::UnsignedLongLong src_offset, WebIDL::UnsignedLong src_length); + void uniform_matrix2fv(GC::Root location, bool transpose, Variant, Vector> data, WebIDL::UnsignedLongLong src_offset, WebIDL::UnsignedLong src_length); + void uniform_matrix3fv(GC::Root location, bool transpose, Variant, Vector> data, WebIDL::UnsignedLongLong src_offset, WebIDL::UnsignedLong src_length); + void uniform_matrix4fv(GC::Root location, bool transpose, Variant, Vector> data, WebIDL::UnsignedLongLong src_offset, WebIDL::UnsignedLong src_length); + void read_pixels(WebIDL::Long x, WebIDL::Long y, WebIDL::Long width, WebIDL::Long height, WebIDL::UnsignedLong format, WebIDL::UnsignedLong type, GC::Root pixels); + void active_texture(WebIDL::UnsignedLong texture); + void attach_shader(GC::Root program, GC::Root shader); + void bind_attrib_location(GC::Root program, WebIDL::UnsignedLong index, String name); + void bind_buffer(WebIDL::UnsignedLong target, GC::Root buffer); + void bind_framebuffer(WebIDL::UnsignedLong target, GC::Root framebuffer); + void bind_renderbuffer(WebIDL::UnsignedLong target, GC::Root renderbuffer); + void bind_texture(WebIDL::UnsignedLong target, GC::Root texture); + void blend_color(float red, float green, float blue, float alpha); + void blend_equation(WebIDL::UnsignedLong mode); + void blend_equation_separate(WebIDL::UnsignedLong mode_rgb, WebIDL::UnsignedLong mode_alpha); + void blend_func(WebIDL::UnsignedLong sfactor, WebIDL::UnsignedLong dfactor); + void blend_func_separate(WebIDL::UnsignedLong src_rgb, WebIDL::UnsignedLong dst_rgb, WebIDL::UnsignedLong src_alpha, WebIDL::UnsignedLong dst_alpha); + WebIDL::UnsignedLong check_framebuffer_status(WebIDL::UnsignedLong target); + void clear(WebIDL::UnsignedLong mask); + void clear_color(float red, float green, float blue, float alpha); + void clear_depth(float depth); + void clear_stencil(WebIDL::Long s); + void color_mask(bool red, bool green, bool blue, bool alpha); + void compile_shader(GC::Root shader); + void copy_tex_image2d(WebIDL::UnsignedLong target, WebIDL::Long level, WebIDL::UnsignedLong internalformat, WebIDL::Long x, WebIDL::Long y, WebIDL::Long width, WebIDL::Long height, WebIDL::Long border); + void copy_tex_sub_image2d(WebIDL::UnsignedLong target, WebIDL::Long level, WebIDL::Long xoffset, WebIDL::Long yoffset, WebIDL::Long x, WebIDL::Long y, WebIDL::Long width, WebIDL::Long height); + GC::Root create_buffer(); + GC::Root create_framebuffer(); + GC::Root create_program(); + GC::Root create_renderbuffer(); + GC::Root create_shader(WebIDL::UnsignedLong type); + GC::Root create_texture(); + void cull_face(WebIDL::UnsignedLong mode); + void delete_buffer(GC::Root buffer); + void delete_framebuffer(GC::Root framebuffer); + void delete_program(GC::Root program); + void delete_renderbuffer(GC::Root renderbuffer); + void delete_shader(GC::Root shader); + void delete_texture(GC::Root texture); + void depth_func(WebIDL::UnsignedLong func); + void depth_mask(bool flag); + void depth_range(float z_near, float z_far); + void detach_shader(GC::Root program, GC::Root shader); + void disable(WebIDL::UnsignedLong cap); + void disable_vertex_attrib_array(WebIDL::UnsignedLong index); + void draw_arrays(WebIDL::UnsignedLong mode, WebIDL::Long first, WebIDL::Long count); + void draw_elements(WebIDL::UnsignedLong mode, WebIDL::Long count, WebIDL::UnsignedLong type, WebIDL::LongLong offset); + void enable(WebIDL::UnsignedLong cap); + void enable_vertex_attrib_array(WebIDL::UnsignedLong index); + void finish(); + void flush(); + void framebuffer_renderbuffer(WebIDL::UnsignedLong target, WebIDL::UnsignedLong attachment, WebIDL::UnsignedLong renderbuffertarget, GC::Root renderbuffer); + void framebuffer_texture2d(WebIDL::UnsignedLong target, WebIDL::UnsignedLong attachment, WebIDL::UnsignedLong textarget, GC::Root texture, WebIDL::Long level); + void front_face(WebIDL::UnsignedLong mode); + void generate_mipmap(WebIDL::UnsignedLong target); + GC::Root get_active_attrib(GC::Root program, WebIDL::UnsignedLong index); + GC::Root get_active_uniform(GC::Root program, WebIDL::UnsignedLong index); + Optional>> get_attached_shaders(GC::Root program); + WebIDL::Long get_attrib_location(GC::Root program, String name); + JS::Value get_buffer_parameter(WebIDL::UnsignedLong target, WebIDL::UnsignedLong pname); + JS::Value get_parameter(WebIDL::UnsignedLong pname); + WebIDL::UnsignedLong get_error(); + JS::Value get_program_parameter(GC::Root program, WebIDL::UnsignedLong pname); + Optional get_program_info_log(GC::Root program); + JS::Value get_shader_parameter(GC::Root shader, WebIDL::UnsignedLong pname); + GC::Root get_shader_precision_format(WebIDL::UnsignedLong shadertype, WebIDL::UnsignedLong precisiontype); + Optional get_shader_info_log(GC::Root shader); + GC::Root get_uniform_location(GC::Root program, String name); + void hint(WebIDL::UnsignedLong target, WebIDL::UnsignedLong mode); + bool is_buffer(GC::Root buffer); + bool is_enabled(WebIDL::UnsignedLong cap); + bool is_framebuffer(GC::Root framebuffer); + bool is_program(GC::Root program); + bool is_renderbuffer(GC::Root renderbuffer); + bool is_shader(GC::Root shader); + bool is_texture(GC::Root texture); + void line_width(float width); + void link_program(GC::Root program); + void pixel_storei(WebIDL::UnsignedLong pname, WebIDL::Long param); + void polygon_offset(float factor, float units); + void renderbuffer_storage(WebIDL::UnsignedLong target, WebIDL::UnsignedLong internalformat, WebIDL::Long width, WebIDL::Long height); + void sample_coverage(float value, bool invert); + void scissor(WebIDL::Long x, WebIDL::Long y, WebIDL::Long width, WebIDL::Long height); + void shader_source(GC::Root shader, String source); + void stencil_func(WebIDL::UnsignedLong func, WebIDL::Long ref, WebIDL::UnsignedLong mask); + void stencil_func_separate(WebIDL::UnsignedLong face, WebIDL::UnsignedLong func, WebIDL::Long ref, WebIDL::UnsignedLong mask); + void stencil_mask(WebIDL::UnsignedLong mask); + void stencil_mask_separate(WebIDL::UnsignedLong face, WebIDL::UnsignedLong mask); + void stencil_op(WebIDL::UnsignedLong fail, WebIDL::UnsignedLong zfail, WebIDL::UnsignedLong zpass); + void stencil_op_separate(WebIDL::UnsignedLong face, WebIDL::UnsignedLong fail, WebIDL::UnsignedLong zfail, WebIDL::UnsignedLong zpass); + void tex_parameterf(WebIDL::UnsignedLong target, WebIDL::UnsignedLong pname, float param); + void tex_parameteri(WebIDL::UnsignedLong target, WebIDL::UnsignedLong pname, WebIDL::Long param); + void uniform1f(GC::Root location, float x); + void uniform2f(GC::Root location, float x, float y); + void uniform3f(GC::Root location, float x, float y, float z); + void uniform4f(GC::Root location, float x, float y, float z, float w); + void uniform1i(GC::Root location, WebIDL::Long x); + void uniform2i(GC::Root location, WebIDL::Long x, WebIDL::Long y); + void uniform3i(GC::Root location, WebIDL::Long x, WebIDL::Long y, WebIDL::Long z); + void uniform4i(GC::Root location, WebIDL::Long x, WebIDL::Long y, WebIDL::Long z, WebIDL::Long w); + void use_program(GC::Root program); + void validate_program(GC::Root program); + void vertex_attrib1f(WebIDL::UnsignedLong index, float x); + void vertex_attrib2f(WebIDL::UnsignedLong index, float x, float y); + void vertex_attrib3f(WebIDL::UnsignedLong index, float x, float y, float z); + void vertex_attrib4f(WebIDL::UnsignedLong index, float x, float y, float z, float w); + void vertex_attrib1fv(WebIDL::UnsignedLong index, Variant, Vector> values); + void vertex_attrib2fv(WebIDL::UnsignedLong index, Variant, Vector> values); + void vertex_attrib3fv(WebIDL::UnsignedLong index, Variant, Vector> values); + void vertex_attrib4fv(WebIDL::UnsignedLong index, Variant, Vector> values); + void vertex_attrib_pointer(WebIDL::UnsignedLong index, WebIDL::Long size, WebIDL::UnsignedLong type, bool normalized, WebIDL::Long stride, WebIDL::LongLong offset); + void viewport(WebIDL::Long x, WebIDL::Long y, WebIDL::Long width, WebIDL::Long height); + +protected: + virtual void visit_edges(JS::Cell::Visitor&) override; + +private: + GC::Ref m_realm; + GC::Ptr m_array_buffer_binding; + GC::Ptr m_element_array_buffer_binding; + GC::Ptr m_current_program; + GC::Ptr m_framebuffer_binding; + GC::Ptr m_renderbuffer_binding; + GC::Ptr m_texture_binding_2d; + GC::Ptr m_texture_binding_cube_map; + + GC::Ptr m_uniform_buffer_binding; + GC::Ptr m_copy_read_buffer_binding; + GC::Ptr m_copy_write_buffer_binding; + GC::Ptr m_texture_binding_2d_array; + GC::Ptr m_texture_binding_3d; + + NonnullOwnPtr m_context; +}; + +} diff --git a/Libraries/LibWeb/WebGL/WebGLRenderingContextImpl.cpp b/Libraries/LibWeb/WebGL/WebGLRenderingContextImpl.cpp new file mode 100644 index 00000000000..00597174ff5 --- /dev/null +++ b/Libraries/LibWeb/WebGL/WebGLRenderingContextImpl.cpp @@ -0,0 +1,2411 @@ +/* + * Copyright (c) 2024, Aliaksandr Kalenik + * Copyright (c) 2024-2025, Luke Wilde + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#define GL_GLEXT_PROTOTYPES 1 +#include +#include +extern "C" { +#include +} + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace Web::WebGL { + +static Vector null_terminated_string(StringView string) +{ + Vector result; + for (auto c : string.bytes()) + result.append(c); + result.append('\0'); + return result; +} + +static constexpr Optional opengl_format_number_of_components(WebIDL::UnsignedLong format) +{ + switch (format) { + case GL_LUMINANCE: + case GL_ALPHA: + return 1; + case GL_LUMINANCE_ALPHA: + return 2; + case GL_RGB: + return 3; + case GL_RGBA: + return 4; + default: + return OptionalNone {}; + } +} + +static constexpr Optional opengl_type_size_in_bytes(WebIDL::UnsignedLong type) +{ + switch (type) { + case GL_UNSIGNED_BYTE: + return 1; + case GL_UNSIGNED_SHORT_5_6_5: + case GL_UNSIGNED_SHORT_4_4_4_4: + case GL_UNSIGNED_SHORT_5_5_5_1: + return 2; + default: + return OptionalNone {}; + } +} + +static constexpr SkColorType opengl_format_and_type_to_skia_color_type(WebIDL::UnsignedLong format, WebIDL::UnsignedLong type) +{ + switch (format) { + case GL_RGB: + switch (type) { + case GL_UNSIGNED_BYTE: + return SkColorType::kRGB_888x_SkColorType; + case GL_UNSIGNED_SHORT_5_6_5: + return SkColorType::kRGB_565_SkColorType; + default: + break; + } + break; + case GL_RGBA: + switch (type) { + case GL_UNSIGNED_BYTE: + return SkColorType::kRGBA_8888_SkColorType; + case GL_UNSIGNED_SHORT_4_4_4_4: + // FIXME: This is not exactly the same as RGBA. + return SkColorType::kARGB_4444_SkColorType; + case GL_UNSIGNED_SHORT_5_5_5_1: + dbgln("WebGL FIXME: Support conversion to RGBA5551."); + break; + default: + break; + } + break; + case GL_ALPHA: + switch (type) { + case GL_UNSIGNED_BYTE: + return SkColorType::kAlpha_8_SkColorType; + default: + break; + } + break; + case GL_LUMINANCE: + switch (type) { + case GL_UNSIGNED_BYTE: + return SkColorType::kGray_8_SkColorType; + default: + break; + } + break; + default: + break; + } + + dbgln("WebGL: Unsupported format and type combination. format: 0x{:04x}, type: 0x{:04x}", format, type); + return SkColorType::kUnknown_SkColorType; +} + +struct ConvertedTexture { + ByteBuffer buffer; + int width { 0 }; + int height { 0 }; +}; + +static Optional read_and_pixel_convert_texture_image_source(TexImageSource const& source, WebIDL::UnsignedLong format, WebIDL::UnsignedLong type, Optional destination_width = OptionalNone {}, Optional destination_height = OptionalNone {}) +{ + // FIXME: If this function is called with an ImageData whose data attribute has been neutered, + // an INVALID_VALUE error is generated. + // FIXME: If this function is called with an ImageBitmap that has been neutered, an INVALID_VALUE + // error is generated. + // FIXME: If this function is called with an HTMLImageElement or HTMLVideoElement whose origin + // differs from the origin of the containing Document, or with an HTMLCanvasElement, + // ImageBitmap or OffscreenCanvas whose bitmap's origin-clean flag is set to false, + // a SECURITY_ERR exception must be thrown. See Origin Restrictions. + // FIXME: If source is null then an INVALID_VALUE error is generated. + auto bitmap = source.visit( + [](GC::Root const& source) -> RefPtr { + return source->immutable_bitmap(); + }, + [](GC::Root const& source) -> RefPtr { + auto surface = source->surface(); + if (!surface) + return {}; + auto bitmap = MUST(Gfx::Bitmap::create(Gfx::BitmapFormat::RGBA8888, Gfx::AlphaType::Premultiplied, surface->size())); + surface->read_into_bitmap(*bitmap); + return Gfx::ImmutableBitmap::create(*bitmap); + }, + [](GC::Root const& source) -> RefPtr { + return Gfx::ImmutableBitmap::create(*source->bitmap()); + }, + [](GC::Root const& source) -> RefPtr { + return Gfx::ImmutableBitmap::create(*source->bitmap()); + }, + [](GC::Root const& source) -> RefPtr { + return Gfx::ImmutableBitmap::create(*source->bitmap()); + }, + [](GC::Root const& source) -> RefPtr { + return Gfx::ImmutableBitmap::create(source->bitmap()); + }); + if (!bitmap) + return OptionalNone {}; + + int width = destination_width.value_or(bitmap->width()); + int height = destination_height.value_or(bitmap->height()); + + Checked buffer_pitch = width; + + auto number_of_components = opengl_format_number_of_components(format); + if (!number_of_components.has_value()) + return OptionalNone {}; + + buffer_pitch *= number_of_components.value(); + + auto type_size = opengl_type_size_in_bytes(type); + if (!type_size.has_value()) + return OptionalNone {}; + + buffer_pitch *= type_size.value(); + + if (buffer_pitch.has_overflow()) + return OptionalNone {}; + + if (Checked::multiplication_would_overflow(buffer_pitch.value(), height)) + return OptionalNone {}; + + auto buffer = MUST(ByteBuffer::create_zeroed(buffer_pitch.value() * height)); + + auto skia_format = opengl_format_and_type_to_skia_color_type(format, type); + + // FIXME: Respect UNPACK_PREMULTIPLY_ALPHA_WEBGL + // FIXME: Respect unpackColorSpace + auto color_space = SkColorSpace::MakeSRGB(); + auto image_info = SkImageInfo::Make(width, height, skia_format, SkAlphaType::kPremul_SkAlphaType, color_space); + SkPixmap const pixmap(image_info, buffer.data(), buffer_pitch.value()); + bitmap->sk_image()->readPixels(pixmap, 0, 0); + return ConvertedTexture { + .buffer = move(buffer), + .width = width, + .height = height, + }; +} + +WebGLRenderingContextImpl::WebGLRenderingContextImpl(JS::Realm& realm, NonnullOwnPtr context) + : m_realm(realm) + , m_context(move(context)) +{ +} + +void WebGLRenderingContextImpl::buffer_data(WebIDL::UnsignedLong target, WebIDL::LongLong size, WebIDL::UnsignedLong usage) +{ + m_context->make_current(); + + glBufferData(target, size, 0, usage); +} + +void WebGLRenderingContextImpl::buffer_data(WebIDL::UnsignedLong target, GC::Root data, WebIDL::UnsignedLong usage) +{ + m_context->make_current(); + + void const* ptr = nullptr; + size_t byte_size = 0; + if (data->is_typed_array_base()) { + auto& typed_array_base = static_cast(*data->raw_object()); + ptr = typed_array_base.viewed_array_buffer()->buffer().data() + typed_array_base.byte_offset(); + byte_size = data->byte_length(); + } else if (data->is_data_view()) { + auto& data_view = static_cast(*data->raw_object()); + ptr = data_view.viewed_array_buffer()->buffer().data(); + byte_size = data_view.viewed_array_buffer()->byte_length(); + } else if (data->is_array_buffer()) { + auto& array_buffer = static_cast(*data->raw_object()); + ptr = array_buffer.buffer().data(); + byte_size = array_buffer.byte_length(); + } else { + VERIFY_NOT_REACHED(); + } + glBufferData(target, byte_size, ptr, usage); +} + +void WebGLRenderingContextImpl::buffer_sub_data(WebIDL::UnsignedLong target, WebIDL::LongLong offset, GC::Root data) +{ + m_context->make_current(); + + void const* ptr = nullptr; + size_t byte_size = 0; + if (data->is_typed_array_base()) { + auto& typed_array_base = static_cast(*data->raw_object()); + ptr = typed_array_base.viewed_array_buffer()->buffer().data() + typed_array_base.byte_offset(); + byte_size = data->byte_length(); + } else if (data->is_data_view()) { + auto& data_view = static_cast(*data->raw_object()); + ptr = data_view.viewed_array_buffer()->buffer().data(); + byte_size = data_view.viewed_array_buffer()->byte_length(); + } else if (data->is_array_buffer()) { + auto& array_buffer = static_cast(*data->raw_object()); + ptr = array_buffer.buffer().data(); + byte_size = array_buffer.byte_length(); + } else { + VERIFY_NOT_REACHED(); + } + glBufferSubData(target, offset, byte_size, ptr); +} + +void WebGLRenderingContextImpl::compressed_tex_image2d(WebIDL::UnsignedLong target, WebIDL::Long level, WebIDL::UnsignedLong internalformat, WebIDL::Long width, WebIDL::Long height, WebIDL::Long border, GC::Root data) +{ + m_context->make_current(); + + void const* ptr = data->viewed_array_buffer()->buffer().data() + data->byte_offset(); + size_t byte_size = data->byte_length(); + glCompressedTexImage2DRobustANGLE(target, level, internalformat, width, height, border, byte_size, byte_size, ptr); +} + +void WebGLRenderingContextImpl::compressed_tex_sub_image2d(WebIDL::UnsignedLong target, WebIDL::Long level, WebIDL::Long xoffset, WebIDL::Long yoffset, WebIDL::Long width, WebIDL::Long height, WebIDL::UnsignedLong format, GC::Root data) +{ + m_context->make_current(); + + void const* ptr = data->viewed_array_buffer()->buffer().data() + data->byte_offset(); + size_t byte_size = data->byte_length(); + glCompressedTexSubImage2DRobustANGLE(target, level, xoffset, yoffset, width, height, format, byte_size, byte_size, ptr); +} + +void WebGLRenderingContextImpl::read_pixels(WebIDL::Long x, WebIDL::Long y, WebIDL::Long width, WebIDL::Long height, WebIDL::UnsignedLong format, WebIDL::UnsignedLong type, GC::Root pixels) +{ + m_context->make_current(); + + if (!pixels) { + return; + } + + void* ptr = pixels->viewed_array_buffer()->buffer().data() + pixels->byte_offset(); + glReadPixelsRobustANGLE(x, y, width, height, format, type, pixels->byte_length(), nullptr, nullptr, nullptr, ptr); +} + +void WebGLRenderingContextImpl::tex_image2d(WebIDL::UnsignedLong target, WebIDL::Long level, WebIDL::Long internalformat, WebIDL::Long width, WebIDL::Long height, WebIDL::Long border, WebIDL::UnsignedLong format, WebIDL::UnsignedLong type, GC::Root pixels) +{ + m_context->make_current(); + + void const* pixels_ptr = nullptr; + size_t buffer_size = 0; + if (pixels) { + auto const& viewed_array_buffer = pixels->viewed_array_buffer(); + auto const& byte_buffer = viewed_array_buffer->buffer(); + pixels_ptr = byte_buffer.data() + pixels->byte_offset(); + buffer_size = pixels->byte_length(); + } + glTexImage2DRobustANGLE(target, level, internalformat, width, height, border, format, type, buffer_size, pixels_ptr); +} + +void WebGLRenderingContextImpl::tex_image2d(WebIDL::UnsignedLong target, WebIDL::Long level, WebIDL::Long internalformat, WebIDL::UnsignedLong format, WebIDL::UnsignedLong type, Variant, GC::Root, GC::Root, GC::Root, GC::Root, GC::Root> source) +{ + m_context->make_current(); + + auto maybe_converted_texture = read_and_pixel_convert_texture_image_source(source, format, type); + if (!maybe_converted_texture.has_value()) + return; + auto converted_texture = maybe_converted_texture.release_value(); + glTexImage2DRobustANGLE(target, level, internalformat, converted_texture.width, converted_texture.height, 0, format, type, converted_texture.buffer.size(), converted_texture.buffer.data()); +} + +void WebGLRenderingContextImpl::tex_sub_image2d(WebIDL::UnsignedLong target, WebIDL::Long level, WebIDL::Long xoffset, WebIDL::Long yoffset, WebIDL::Long width, WebIDL::Long height, WebIDL::UnsignedLong format, WebIDL::UnsignedLong type, GC::Root pixels) +{ + m_context->make_current(); + + void const* pixels_ptr = nullptr; + size_t buffer_size = 0; + if (pixels) { + auto const& viewed_array_buffer = pixels->viewed_array_buffer(); + auto const& byte_buffer = viewed_array_buffer->buffer(); + pixels_ptr = byte_buffer.data() + pixels->byte_offset(); + buffer_size = pixels->byte_length(); + } + glTexSubImage2DRobustANGLE(target, level, xoffset, yoffset, width, height, format, type, buffer_size, pixels_ptr); +} + +void WebGLRenderingContextImpl::tex_sub_image2d(WebIDL::UnsignedLong target, WebIDL::Long level, WebIDL::Long xoffset, WebIDL::Long yoffset, WebIDL::UnsignedLong format, WebIDL::UnsignedLong type, Variant, GC::Root, GC::Root, GC::Root, GC::Root, GC::Root> source) +{ + m_context->make_current(); + + auto maybe_converted_texture = read_and_pixel_convert_texture_image_source(source, format, type); + + if (!maybe_converted_texture.has_value()) + return; + auto converted_texture = maybe_converted_texture.release_value(); + glTexSubImage2DRobustANGLE(target, level, xoffset, yoffset, converted_texture.width, converted_texture.height, format, type, converted_texture.buffer.size(), converted_texture.buffer.data()); +} + +void WebGLRenderingContextImpl::uniform1fv(GC::Root location, Variant, Vector> v) +{ + m_context->make_current(); + + if (!location) + return; + + float const* data = nullptr; + size_t count = 0; + if (v.has>()) { + auto& vector = v.get>(); + data = vector.data(); + count = vector.size(); + } else if (v.has>()) { + auto& typed_array_base = static_cast(*v.get>()->raw_object()); + auto& typed_array = as(typed_array_base); + data = typed_array.data().data(); + count = typed_array.array_length().length(); + } else { + VERIFY_NOT_REACHED(); + } + + glUniform1fv(location->handle(), count / 1, data); +} + +void WebGLRenderingContextImpl::uniform2fv(GC::Root location, Variant, Vector> v) +{ + m_context->make_current(); + + if (!location) + return; + + float const* data = nullptr; + size_t count = 0; + if (v.has>()) { + auto& vector = v.get>(); + data = vector.data(); + count = vector.size(); + } else if (v.has>()) { + auto& typed_array_base = static_cast(*v.get>()->raw_object()); + auto& typed_array = as(typed_array_base); + data = typed_array.data().data(); + count = typed_array.array_length().length(); + } else { + VERIFY_NOT_REACHED(); + } + + glUniform2fv(location->handle(), count / 2, data); +} + +void WebGLRenderingContextImpl::uniform3fv(GC::Root location, Variant, Vector> v) +{ + m_context->make_current(); + + if (!location) + return; + + float const* data = nullptr; + size_t count = 0; + if (v.has>()) { + auto& vector = v.get>(); + data = vector.data(); + count = vector.size(); + } else if (v.has>()) { + auto& typed_array_base = static_cast(*v.get>()->raw_object()); + auto& typed_array = as(typed_array_base); + data = typed_array.data().data(); + count = typed_array.array_length().length(); + } else { + VERIFY_NOT_REACHED(); + } + + glUniform3fv(location->handle(), count / 3, data); +} + +void WebGLRenderingContextImpl::uniform4fv(GC::Root location, Variant, Vector> v) +{ + m_context->make_current(); + + if (!location) + return; + + float const* data = nullptr; + size_t count = 0; + if (v.has>()) { + auto& vector = v.get>(); + data = vector.data(); + count = vector.size(); + } else if (v.has>()) { + auto& typed_array_base = static_cast(*v.get>()->raw_object()); + auto& typed_array = as(typed_array_base); + data = typed_array.data().data(); + count = typed_array.array_length().length(); + } else { + VERIFY_NOT_REACHED(); + } + + glUniform4fv(location->handle(), count / 4, data); +} + +void WebGLRenderingContextImpl::uniform1iv(GC::Root location, Variant, Vector> v) +{ + m_context->make_current(); + + if (!location) + return; + + int const* data = nullptr; + size_t count = 0; + if (v.has>()) { + auto& vector = v.get>(); + data = vector.data(); + count = vector.size(); + } else if (v.has>()) { + auto& typed_array_base = static_cast(*v.get>()->raw_object()); + auto& typed_array = as(typed_array_base); + data = typed_array.data().data(); + count = typed_array.array_length().length(); + } else { + VERIFY_NOT_REACHED(); + } + + glUniform1iv(location->handle(), count / 1, data); +} + +void WebGLRenderingContextImpl::uniform2iv(GC::Root location, Variant, Vector> v) +{ + m_context->make_current(); + + if (!location) + return; + + int const* data = nullptr; + size_t count = 0; + if (v.has>()) { + auto& vector = v.get>(); + data = vector.data(); + count = vector.size(); + } else if (v.has>()) { + auto& typed_array_base = static_cast(*v.get>()->raw_object()); + auto& typed_array = as(typed_array_base); + data = typed_array.data().data(); + count = typed_array.array_length().length(); + } else { + VERIFY_NOT_REACHED(); + } + + glUniform2iv(location->handle(), count / 2, data); +} + +void WebGLRenderingContextImpl::uniform3iv(GC::Root location, Variant, Vector> v) +{ + m_context->make_current(); + + if (!location) + return; + + int const* data = nullptr; + size_t count = 0; + if (v.has>()) { + auto& vector = v.get>(); + data = vector.data(); + count = vector.size(); + } else if (v.has>()) { + auto& typed_array_base = static_cast(*v.get>()->raw_object()); + auto& typed_array = as(typed_array_base); + data = typed_array.data().data(); + count = typed_array.array_length().length(); + } else { + VERIFY_NOT_REACHED(); + } + + glUniform3iv(location->handle(), count / 3, data); +} + +void WebGLRenderingContextImpl::uniform4iv(GC::Root location, Variant, Vector> v) +{ + m_context->make_current(); + + if (!location) + return; + + int const* data = nullptr; + size_t count = 0; + if (v.has>()) { + auto& vector = v.get>(); + data = vector.data(); + count = vector.size(); + } else if (v.has>()) { + auto& typed_array_base = static_cast(*v.get>()->raw_object()); + auto& typed_array = as(typed_array_base); + data = typed_array.data().data(); + count = typed_array.array_length().length(); + } else { + VERIFY_NOT_REACHED(); + } + + glUniform4iv(location->handle(), count / 4, data); +} + +void WebGLRenderingContextImpl::uniform_matrix2fv(GC::Root location, bool transpose, Variant, Vector> value) +{ + m_context->make_current(); + + if (!location) + return; + + auto matrix_size = 2 * 2; + float const* raw_data = nullptr; + u64 count = 0; + if (value.has>()) { + auto& vector_data = value.get>(); + raw_data = vector_data.data(); + count = vector_data.size() / matrix_size; + } else { + auto& typed_array_base = static_cast(*value.get>()->raw_object()); + auto& float32_array = as(typed_array_base); + raw_data = float32_array.data().data(); + count = float32_array.array_length().length() / matrix_size; + } + + glUniformMatrix2fv(location->handle(), count, transpose, raw_data); +} + +void WebGLRenderingContextImpl::uniform_matrix3fv(GC::Root location, bool transpose, Variant, Vector> value) +{ + m_context->make_current(); + + if (!location) + return; + + auto matrix_size = 3 * 3; + float const* raw_data = nullptr; + u64 count = 0; + if (value.has>()) { + auto& vector_data = value.get>(); + raw_data = vector_data.data(); + count = vector_data.size() / matrix_size; + } else { + auto& typed_array_base = static_cast(*value.get>()->raw_object()); + auto& float32_array = as(typed_array_base); + raw_data = float32_array.data().data(); + count = float32_array.array_length().length() / matrix_size; + } + + glUniformMatrix3fv(location->handle(), count, transpose, raw_data); +} + +void WebGLRenderingContextImpl::uniform_matrix4fv(GC::Root location, bool transpose, Variant, Vector> value) +{ + m_context->make_current(); + + if (!location) + return; + + auto matrix_size = 4 * 4; + float const* raw_data = nullptr; + u64 count = 0; + if (value.has>()) { + auto& vector_data = value.get>(); + raw_data = vector_data.data(); + count = vector_data.size() / matrix_size; + } else { + auto& typed_array_base = static_cast(*value.get>()->raw_object()); + auto& float32_array = as(typed_array_base); + raw_data = float32_array.data().data(); + count = float32_array.array_length().length() / matrix_size; + } + + glUniformMatrix4fv(location->handle(), count, transpose, raw_data); +} + +void WebGLRenderingContextImpl::active_texture(WebIDL::UnsignedLong texture) +{ + m_context->make_current(); + glActiveTexture(texture); +} + +void WebGLRenderingContextImpl::attach_shader(GC::Root program, GC::Root shader) +{ + m_context->make_current(); + + GLuint program_handle = 0; + if (program) { + auto handle_or_error = program->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return; + } + program_handle = handle_or_error.release_value(); + } + + GLuint shader_handle = 0; + if (shader) { + auto handle_or_error = shader->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return; + } + shader_handle = handle_or_error.release_value(); + } + + if (program->attached_vertex_shader() == shader || program->attached_fragment_shader() == shader) { + dbgln("WebGL: Shader is already attached to program"); + set_error(GL_INVALID_OPERATION); + return; + } + + if (shader->type() == GL_VERTEX_SHADER && program->attached_vertex_shader()) { + dbgln("WebGL: Not attaching vertex shader to program as it already has a vertex shader attached"); + set_error(GL_INVALID_OPERATION); + return; + } + + if (shader->type() == GL_FRAGMENT_SHADER && program->attached_fragment_shader()) { + dbgln("WebGL: Not attaching fragment shader to program as it already has a fragment shader attached"); + set_error(GL_INVALID_OPERATION); + return; + } + + glAttachShader(program_handle, shader_handle); + + switch (shader->type()) { + case GL_VERTEX_SHADER: + program->set_attached_vertex_shader(shader.ptr()); + break; + case GL_FRAGMENT_SHADER: + program->set_attached_fragment_shader(shader.ptr()); + break; + default: + VERIFY_NOT_REACHED(); + } +} + +void WebGLRenderingContextImpl::bind_attrib_location(GC::Root program, WebIDL::UnsignedLong index, String name) +{ + m_context->make_current(); + + auto program_handle = 0; + if (program) { + auto handle_or_error = program->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return; + } + program_handle = handle_or_error.release_value(); + } + + auto name_null_terminated = null_terminated_string(name); + glBindAttribLocation(program_handle, index, name_null_terminated.data()); +} + +void WebGLRenderingContextImpl::bind_buffer(WebIDL::UnsignedLong target, GC::Root buffer) +{ + m_context->make_current(); + + GLuint buffer_handle = 0; + if (buffer) { + auto handle_or_error = buffer->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return; + } + buffer_handle = handle_or_error.release_value(); + } + + switch (target) { + case GL_ELEMENT_ARRAY_BUFFER: + m_element_array_buffer_binding = buffer; + break; + case GL_ARRAY_BUFFER: + m_array_buffer_binding = buffer; + break; + + default: + dbgln("Unknown WebGL buffer object binding target for storing current binding: 0x{:04x}", target); + set_error(GL_INVALID_ENUM); + return; + } + + glBindBuffer(target, buffer_handle); +} + +void WebGLRenderingContextImpl::bind_framebuffer(WebIDL::UnsignedLong target, GC::Root framebuffer) +{ + m_context->make_current(); + + GLuint framebuffer_handle = 0; + if (framebuffer) { + auto handle_or_error = framebuffer->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return; + } + framebuffer_handle = handle_or_error.release_value(); + } + + glBindFramebuffer(target, framebuffer ? framebuffer_handle : m_context->default_framebuffer()); + m_framebuffer_binding = framebuffer; +} + +void WebGLRenderingContextImpl::bind_renderbuffer(WebIDL::UnsignedLong target, GC::Root renderbuffer) +{ + m_context->make_current(); + + GLuint renderbuffer_handle = 0; + if (renderbuffer) { + auto handle_or_error = renderbuffer->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return; + } + renderbuffer_handle = handle_or_error.release_value(); + } + + glBindRenderbuffer(target, renderbuffer ? renderbuffer_handle : m_context->default_renderbuffer()); + m_renderbuffer_binding = renderbuffer; +} + +void WebGLRenderingContextImpl::bind_texture(WebIDL::UnsignedLong target, GC::Root texture) +{ + m_context->make_current(); + + GLuint texture_handle = 0; + if (texture) { + auto handle_or_error = texture->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return; + } + texture_handle = handle_or_error.release_value(); + } + + switch (target) { + case GL_TEXTURE_2D: + m_texture_binding_2d = texture; + break; + case GL_TEXTURE_CUBE_MAP: + m_texture_binding_cube_map = texture; + break; + + default: + dbgln("Unknown WebGL texture target for storing current binding: 0x{:04x}", target); + set_error(GL_INVALID_ENUM); + return; + } + glBindTexture(target, texture_handle); +} + +void WebGLRenderingContextImpl::blend_color(float red, float green, float blue, float alpha) +{ + m_context->make_current(); + glBlendColor(red, green, blue, alpha); +} + +void WebGLRenderingContextImpl::blend_equation(WebIDL::UnsignedLong mode) +{ + m_context->make_current(); + glBlendEquation(mode); +} + +void WebGLRenderingContextImpl::blend_equation_separate(WebIDL::UnsignedLong mode_rgb, WebIDL::UnsignedLong mode_alpha) +{ + m_context->make_current(); + glBlendEquationSeparate(mode_rgb, mode_alpha); +} + +void WebGLRenderingContextImpl::blend_func(WebIDL::UnsignedLong sfactor, WebIDL::UnsignedLong dfactor) +{ + m_context->make_current(); + glBlendFunc(sfactor, dfactor); +} + +void WebGLRenderingContextImpl::blend_func_separate(WebIDL::UnsignedLong src_rgb, WebIDL::UnsignedLong dst_rgb, WebIDL::UnsignedLong src_alpha, WebIDL::UnsignedLong dst_alpha) +{ + m_context->make_current(); + glBlendFuncSeparate(src_rgb, dst_rgb, src_alpha, dst_alpha); +} + +WebIDL::UnsignedLong WebGLRenderingContextImpl::check_framebuffer_status(WebIDL::UnsignedLong target) +{ + m_context->make_current(); + return glCheckFramebufferStatus(target); +} + +void WebGLRenderingContextImpl::clear(WebIDL::UnsignedLong mask) +{ + m_context->make_current(); + m_context->notify_content_will_change(); + needs_to_present(); + glClear(mask); +} + +void WebGLRenderingContextImpl::clear_color(float red, float green, float blue, float alpha) +{ + m_context->make_current(); + glClearColor(red, green, blue, alpha); +} + +void WebGLRenderingContextImpl::clear_depth(float depth) +{ + m_context->make_current(); + glClearDepthf(depth); +} + +void WebGLRenderingContextImpl::clear_stencil(WebIDL::Long s) +{ + m_context->make_current(); + glClearStencil(s); +} + +void WebGLRenderingContextImpl::color_mask(bool red, bool green, bool blue, bool alpha) +{ + m_context->make_current(); + glColorMask(red, green, blue, alpha); +} + +void WebGLRenderingContextImpl::compile_shader(GC::Root shader) +{ + m_context->make_current(); + + auto shader_handle = 0; + if (shader) { + auto handle_or_error = shader->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return; + } + shader_handle = handle_or_error.release_value(); + } + glCompileShader(shader_handle); +} + +void WebGLRenderingContextImpl::copy_tex_image2d(WebIDL::UnsignedLong target, WebIDL::Long level, WebIDL::UnsignedLong internalformat, WebIDL::Long x, WebIDL::Long y, WebIDL::Long width, WebIDL::Long height, WebIDL::Long border) +{ + m_context->make_current(); + glCopyTexImage2D(target, level, internalformat, x, y, width, height, border); +} + +void WebGLRenderingContextImpl::copy_tex_sub_image2d(WebIDL::UnsignedLong target, WebIDL::Long level, WebIDL::Long xoffset, WebIDL::Long yoffset, WebIDL::Long x, WebIDL::Long y, WebIDL::Long width, WebIDL::Long height) +{ + m_context->make_current(); + glCopyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, height); +} + +GC::Root WebGLRenderingContextImpl::create_buffer() +{ + m_context->make_current(); + + GLuint handle = 0; + glGenBuffers(1, &handle); + return WebGLBuffer::create(m_realm, *this, handle); +} + +GC::Root WebGLRenderingContextImpl::create_framebuffer() +{ + m_context->make_current(); + + GLuint handle = 0; + glGenFramebuffers(1, &handle); + return WebGLFramebuffer::create(m_realm, *this, handle); +} + +GC::Root WebGLRenderingContextImpl::create_program() +{ + m_context->make_current(); + return WebGLProgram::create(m_realm, *this, glCreateProgram()); +} + +GC::Root WebGLRenderingContextImpl::create_renderbuffer() +{ + m_context->make_current(); + + GLuint handle = 0; + glGenRenderbuffers(1, &handle); + return WebGLRenderbuffer::create(m_realm, *this, handle); +} + +GC::Root WebGLRenderingContextImpl::create_shader(WebIDL::UnsignedLong type) +{ + m_context->make_current(); + + if (type != GL_VERTEX_SHADER && type != GL_FRAGMENT_SHADER) { + dbgln("Unknown WebGL shader type: 0x{:04x}", type); + set_error(GL_INVALID_ENUM); + return nullptr; + } + + GLuint handle = glCreateShader(type); + return WebGLShader::create(m_realm, *this, handle, type); +} + +GC::Root WebGLRenderingContextImpl::create_texture() +{ + m_context->make_current(); + + GLuint handle = 0; + glGenTextures(1, &handle); + return WebGLTexture::create(m_realm, *this, handle); +} + +void WebGLRenderingContextImpl::cull_face(WebIDL::UnsignedLong mode) +{ + m_context->make_current(); + glCullFace(mode); +} + +void WebGLRenderingContextImpl::delete_buffer(GC::Root buffer) +{ + m_context->make_current(); + + GLuint buffer_handle = 0; + if (buffer) { + auto handle_or_error = buffer->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return; + } + buffer_handle = handle_or_error.release_value(); + } + + glDeleteBuffers(1, &buffer_handle); +} + +void WebGLRenderingContextImpl::delete_framebuffer(GC::Root framebuffer) +{ + m_context->make_current(); + + GLuint framebuffer_handle = 0; + if (framebuffer) { + auto handle_or_error = framebuffer->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return; + } + framebuffer_handle = handle_or_error.release_value(); + } + + glDeleteFramebuffers(1, &framebuffer_handle); +} + +void WebGLRenderingContextImpl::delete_program(GC::Root program) +{ + m_context->make_current(); + + auto program_handle = 0; + if (program) { + auto handle_or_error = program->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return; + } + program_handle = handle_or_error.release_value(); + } + glDeleteProgram(program_handle); +} + +void WebGLRenderingContextImpl::delete_renderbuffer(GC::Root renderbuffer) +{ + m_context->make_current(); + + GLuint renderbuffer_handle = 0; + if (renderbuffer) { + auto handle_or_error = renderbuffer->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return; + } + renderbuffer_handle = handle_or_error.release_value(); + } + + glDeleteRenderbuffers(1, &renderbuffer_handle); +} + +void WebGLRenderingContextImpl::delete_shader(GC::Root shader) +{ + m_context->make_current(); + + auto shader_handle = 0; + if (shader) { + auto handle_or_error = shader->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return; + } + shader_handle = handle_or_error.release_value(); + } + glDeleteShader(shader_handle); +} + +void WebGLRenderingContextImpl::delete_texture(GC::Root texture) +{ + m_context->make_current(); + + GLuint texture_handle = 0; + if (texture) { + auto handle_or_error = texture->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return; + } + texture_handle = handle_or_error.release_value(); + } + + glDeleteTextures(1, &texture_handle); +} + +void WebGLRenderingContextImpl::depth_func(WebIDL::UnsignedLong func) +{ + m_context->make_current(); + glDepthFunc(func); +} + +void WebGLRenderingContextImpl::depth_mask(bool flag) +{ + m_context->make_current(); + glDepthMask(flag); +} + +void WebGLRenderingContextImpl::depth_range(float z_near, float z_far) +{ + m_context->make_current(); + glDepthRangef(z_near, z_far); +} + +void WebGLRenderingContextImpl::detach_shader(GC::Root program, GC::Root shader) +{ + m_context->make_current(); + + auto program_handle = 0; + if (program) { + auto handle_or_error = program->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return; + } + program_handle = handle_or_error.release_value(); + } + + auto shader_handle = 0; + if (shader) { + auto handle_or_error = shader->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return; + } + shader_handle = handle_or_error.release_value(); + } + glDetachShader(program_handle, shader_handle); +} + +void WebGLRenderingContextImpl::disable(WebIDL::UnsignedLong cap) +{ + m_context->make_current(); + glDisable(cap); +} + +void WebGLRenderingContextImpl::disable_vertex_attrib_array(WebIDL::UnsignedLong index) +{ + m_context->make_current(); + glDisableVertexAttribArray(index); +} + +void WebGLRenderingContextImpl::draw_arrays(WebIDL::UnsignedLong mode, WebIDL::Long first, WebIDL::Long count) +{ + m_context->make_current(); + m_context->notify_content_will_change(); + needs_to_present(); + glDrawArrays(mode, first, count); +} + +void WebGLRenderingContextImpl::draw_elements(WebIDL::UnsignedLong mode, WebIDL::Long count, WebIDL::UnsignedLong type, WebIDL::LongLong offset) +{ + m_context->make_current(); + m_context->notify_content_will_change(); + + glDrawElements(mode, count, type, reinterpret_cast(offset)); + needs_to_present(); +} + +void WebGLRenderingContextImpl::enable(WebIDL::UnsignedLong cap) +{ + m_context->make_current(); + glEnable(cap); +} + +void WebGLRenderingContextImpl::enable_vertex_attrib_array(WebIDL::UnsignedLong index) +{ + m_context->make_current(); + glEnableVertexAttribArray(index); +} + +void WebGLRenderingContextImpl::finish() +{ + m_context->make_current(); + glFinish(); +} + +void WebGLRenderingContextImpl::flush() +{ + m_context->make_current(); + glFlush(); +} + +void WebGLRenderingContextImpl::framebuffer_renderbuffer(WebIDL::UnsignedLong target, WebIDL::UnsignedLong attachment, WebIDL::UnsignedLong renderbuffertarget, GC::Root renderbuffer) +{ + m_context->make_current(); + + auto renderbuffer_handle = 0; + if (renderbuffer) { + auto handle_or_error = renderbuffer->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return; + } + renderbuffer_handle = handle_or_error.release_value(); + } + glFramebufferRenderbuffer(target, attachment, renderbuffertarget, renderbuffer_handle); +} + +void WebGLRenderingContextImpl::framebuffer_texture2d(WebIDL::UnsignedLong target, WebIDL::UnsignedLong attachment, WebIDL::UnsignedLong textarget, GC::Root texture, WebIDL::Long level) +{ + m_context->make_current(); + + auto texture_handle = 0; + if (texture) { + auto handle_or_error = texture->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return; + } + texture_handle = handle_or_error.release_value(); + } + glFramebufferTexture2D(target, attachment, textarget, texture_handle, level); +} + +void WebGLRenderingContextImpl::front_face(WebIDL::UnsignedLong mode) +{ + m_context->make_current(); + glFrontFace(mode); +} + +void WebGLRenderingContextImpl::generate_mipmap(WebIDL::UnsignedLong target) +{ + m_context->make_current(); + glGenerateMipmap(target); +} + +GC::Root WebGLRenderingContextImpl::get_active_attrib(GC::Root program, WebIDL::UnsignedLong index) +{ + m_context->make_current(); + + GLuint program_handle = 0; + if (program) { + auto handle_or_error = program->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return {}; + } + program_handle = handle_or_error.release_value(); + } + + GLint size = 0; + GLenum type = 0; + GLsizei buf_size = 256; + GLsizei length = 0; + GLchar name[256]; + glGetActiveAttrib(program_handle, index, buf_size, &length, &size, &type, name); + auto readonly_bytes = ReadonlyBytes { name, static_cast(length) }; + return WebGLActiveInfo::create(m_realm, String::from_utf8_without_validation(readonly_bytes), type, size); +} + +GC::Root WebGLRenderingContextImpl::get_active_uniform(GC::Root program, WebIDL::UnsignedLong index) +{ + m_context->make_current(); + + GLuint program_handle = 0; + if (program) { + auto handle_or_error = program->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return {}; + } + program_handle = handle_or_error.release_value(); + } + + GLint size = 0; + GLenum type = 0; + GLsizei buf_size = 256; + GLsizei length = 0; + GLchar name[256]; + glGetActiveUniform(program_handle, index, buf_size, &length, &size, &type, name); + auto readonly_bytes = ReadonlyBytes { name, static_cast(length) }; + return WebGLActiveInfo::create(m_realm, String::from_utf8_without_validation(readonly_bytes), type, size); +} + +Optional>> WebGLRenderingContextImpl::get_attached_shaders(GC::Root program) +{ + m_context->make_current(); + + GLuint program_handle = 0; + if (program) { + auto handle_or_error = program->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return OptionalNone {}; + } + program_handle = handle_or_error.release_value(); + } + + (void)program_handle; + + Vector> result; + + if (program->attached_vertex_shader()) + result.append(GC::make_root(*program->attached_vertex_shader())); + + if (program->attached_fragment_shader()) + result.append(GC::make_root(*program->attached_fragment_shader())); + + return result; +} + +WebIDL::Long WebGLRenderingContextImpl::get_attrib_location(GC::Root program, String name) +{ + m_context->make_current(); + + auto program_handle = 0; + if (program) { + auto handle_or_error = program->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return -1; + } + program_handle = handle_or_error.release_value(); + } + + auto name_null_terminated = null_terminated_string(name); + return glGetAttribLocation(program_handle, name_null_terminated.data()); +} + +JS::Value WebGLRenderingContextImpl::get_buffer_parameter(WebIDL::UnsignedLong target, WebIDL::UnsignedLong pname) +{ + m_context->make_current(); + switch (pname) { + case GL_BUFFER_SIZE: { + GLint result { 0 }; + glGetBufferParameterivRobustANGLE(target, GL_BUFFER_SIZE, 1, nullptr, &result); + return JS::Value(result); + } + + case GL_BUFFER_USAGE: { + GLint result { 0 }; + glGetBufferParameterivRobustANGLE(target, GL_BUFFER_USAGE, 1, nullptr, &result); + return JS::Value(result); + } + + default: + dbgln("Unknown WebGL buffer parameter name: {:x}", pname); + set_error(GL_INVALID_ENUM); + return JS::js_null(); + } +} + +JS::Value WebGLRenderingContextImpl::get_parameter(WebIDL::UnsignedLong pname) +{ + m_context->make_current(); + switch (pname) { + case GL_ACTIVE_TEXTURE: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_ACTIVE_TEXTURE, 1, nullptr, &result); + return JS::Value(result); + } + case GL_ALIASED_LINE_WIDTH_RANGE: { + Array result; + result.fill(0); + constexpr size_t buffer_size = 2 * sizeof(GLfloat); + glGetFloatvRobustANGLE(GL_ALIASED_LINE_WIDTH_RANGE, 2, nullptr, result.data()); + auto byte_buffer = MUST(ByteBuffer::copy(result.data(), buffer_size)); + auto array_buffer = JS::ArrayBuffer::create(m_realm, move(byte_buffer)); + return JS::Float32Array::create(m_realm, 2, array_buffer); + } + case GL_ALIASED_POINT_SIZE_RANGE: { + Array result; + result.fill(0); + constexpr size_t buffer_size = 2 * sizeof(GLfloat); + glGetFloatvRobustANGLE(GL_ALIASED_POINT_SIZE_RANGE, 2, nullptr, result.data()); + auto byte_buffer = MUST(ByteBuffer::copy(result.data(), buffer_size)); + auto array_buffer = JS::ArrayBuffer::create(m_realm, move(byte_buffer)); + return JS::Float32Array::create(m_realm, 2, array_buffer); + } + case GL_ALPHA_BITS: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_ALPHA_BITS, 1, nullptr, &result); + return JS::Value(result); + } + case GL_ARRAY_BUFFER_BINDING: { + if (!m_array_buffer_binding) + return JS::js_null(); + return JS::Value(m_array_buffer_binding); + } + case GL_BLEND: { + GLboolean result { GL_FALSE }; + glGetBooleanvRobustANGLE(GL_BLEND, 1, nullptr, &result); + return JS::Value(result == GL_TRUE); + } + case GL_BLEND_COLOR: { + Array result; + result.fill(0); + constexpr size_t buffer_size = 4 * sizeof(GLfloat); + glGetFloatvRobustANGLE(GL_BLEND_COLOR, 4, nullptr, result.data()); + auto byte_buffer = MUST(ByteBuffer::copy(result.data(), buffer_size)); + auto array_buffer = JS::ArrayBuffer::create(m_realm, move(byte_buffer)); + return JS::Float32Array::create(m_realm, 4, array_buffer); + } + case GL_BLEND_DST_ALPHA: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_BLEND_DST_ALPHA, 1, nullptr, &result); + return JS::Value(result); + } + case GL_BLEND_DST_RGB: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_BLEND_DST_RGB, 1, nullptr, &result); + return JS::Value(result); + } + case GL_BLEND_EQUATION_ALPHA: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_BLEND_EQUATION_ALPHA, 1, nullptr, &result); + return JS::Value(result); + } + case GL_BLEND_EQUATION_RGB: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_BLEND_EQUATION_RGB, 1, nullptr, &result); + return JS::Value(result); + } + case GL_BLEND_SRC_ALPHA: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_BLEND_SRC_ALPHA, 1, nullptr, &result); + return JS::Value(result); + } + case GL_BLEND_SRC_RGB: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_BLEND_SRC_RGB, 1, nullptr, &result); + return JS::Value(result); + } + case GL_BLUE_BITS: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_BLUE_BITS, 1, nullptr, &result); + return JS::Value(result); + } + case GL_COLOR_CLEAR_VALUE: { + Array result; + result.fill(0); + constexpr size_t buffer_size = 4 * sizeof(GLfloat); + glGetFloatvRobustANGLE(GL_COLOR_CLEAR_VALUE, 4, nullptr, result.data()); + auto byte_buffer = MUST(ByteBuffer::copy(result.data(), buffer_size)); + auto array_buffer = JS::ArrayBuffer::create(m_realm, move(byte_buffer)); + return JS::Float32Array::create(m_realm, 4, array_buffer); + } + case GL_CULL_FACE: { + GLboolean result { GL_FALSE }; + glGetBooleanvRobustANGLE(GL_CULL_FACE, 1, nullptr, &result); + return JS::Value(result == GL_TRUE); + } + case GL_CULL_FACE_MODE: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_CULL_FACE_MODE, 1, nullptr, &result); + return JS::Value(result); + } + case GL_CURRENT_PROGRAM: { + if (!m_current_program) + return JS::js_null(); + return JS::Value(m_current_program); + } + case GL_DEPTH_BITS: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_DEPTH_BITS, 1, nullptr, &result); + return JS::Value(result); + } + case GL_DEPTH_CLEAR_VALUE: { + GLfloat result { 0.0f }; + glGetFloatvRobustANGLE(GL_DEPTH_CLEAR_VALUE, 1, nullptr, &result); + return JS::Value(result); + } + case GL_DEPTH_FUNC: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_DEPTH_FUNC, 1, nullptr, &result); + return JS::Value(result); + } + case GL_DEPTH_RANGE: { + Array result; + result.fill(0); + constexpr size_t buffer_size = 2 * sizeof(GLfloat); + glGetFloatvRobustANGLE(GL_DEPTH_RANGE, 2, nullptr, result.data()); + auto byte_buffer = MUST(ByteBuffer::copy(result.data(), buffer_size)); + auto array_buffer = JS::ArrayBuffer::create(m_realm, move(byte_buffer)); + return JS::Float32Array::create(m_realm, 2, array_buffer); + } + case GL_DEPTH_TEST: { + GLboolean result { GL_FALSE }; + glGetBooleanvRobustANGLE(GL_DEPTH_TEST, 1, nullptr, &result); + return JS::Value(result == GL_TRUE); + } + case GL_DEPTH_WRITEMASK: { + GLboolean result { GL_FALSE }; + glGetBooleanvRobustANGLE(GL_DEPTH_WRITEMASK, 1, nullptr, &result); + return JS::Value(result == GL_TRUE); + } + case GL_DITHER: { + GLboolean result { GL_FALSE }; + glGetBooleanvRobustANGLE(GL_DITHER, 1, nullptr, &result); + return JS::Value(result == GL_TRUE); + } + case GL_ELEMENT_ARRAY_BUFFER_BINDING: { + if (!m_element_array_buffer_binding) + return JS::js_null(); + return JS::Value(m_element_array_buffer_binding); + } + case GL_FRAMEBUFFER_BINDING: { + if (!m_framebuffer_binding) + return JS::js_null(); + return JS::Value(m_framebuffer_binding); + } + case GL_FRONT_FACE: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_FRONT_FACE, 1, nullptr, &result); + return JS::Value(result); + } + case GL_GENERATE_MIPMAP_HINT: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_GENERATE_MIPMAP_HINT, 1, nullptr, &result); + return JS::Value(result); + } + case GL_GREEN_BITS: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_GREEN_BITS, 1, nullptr, &result); + return JS::Value(result); + } + case GL_IMPLEMENTATION_COLOR_READ_FORMAT: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_IMPLEMENTATION_COLOR_READ_FORMAT, 1, nullptr, &result); + return JS::Value(result); + } + case GL_IMPLEMENTATION_COLOR_READ_TYPE: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_IMPLEMENTATION_COLOR_READ_TYPE, 1, nullptr, &result); + return JS::Value(result); + } + case GL_LINE_WIDTH: { + GLfloat result { 0.0f }; + glGetFloatvRobustANGLE(GL_LINE_WIDTH, 1, nullptr, &result); + return JS::Value(result); + } + case GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, 1, nullptr, &result); + return JS::Value(result); + } + case GL_MAX_CUBE_MAP_TEXTURE_SIZE: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_MAX_CUBE_MAP_TEXTURE_SIZE, 1, nullptr, &result); + return JS::Value(result); + } + case GL_MAX_FRAGMENT_UNIFORM_VECTORS: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_MAX_FRAGMENT_UNIFORM_VECTORS, 1, nullptr, &result); + return JS::Value(result); + } + case GL_MAX_RENDERBUFFER_SIZE: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_MAX_RENDERBUFFER_SIZE, 1, nullptr, &result); + return JS::Value(result); + } + case GL_MAX_TEXTURE_IMAGE_UNITS: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_MAX_TEXTURE_IMAGE_UNITS, 1, nullptr, &result); + return JS::Value(result); + } + case GL_MAX_TEXTURE_SIZE: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_MAX_TEXTURE_SIZE, 1, nullptr, &result); + return JS::Value(result); + } + case GL_MAX_VARYING_VECTORS: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_MAX_VARYING_VECTORS, 1, nullptr, &result); + return JS::Value(result); + } + case GL_MAX_VERTEX_ATTRIBS: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_MAX_VERTEX_ATTRIBS, 1, nullptr, &result); + return JS::Value(result); + } + case GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, 1, nullptr, &result); + return JS::Value(result); + } + case GL_MAX_VERTEX_UNIFORM_VECTORS: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_MAX_VERTEX_UNIFORM_VECTORS, 1, nullptr, &result); + return JS::Value(result); + } + case GL_MAX_VIEWPORT_DIMS: { + Array result; + result.fill(0); + constexpr size_t buffer_size = 2 * sizeof(GLint); + glGetIntegervRobustANGLE(GL_MAX_VIEWPORT_DIMS, 2, nullptr, result.data()); + auto byte_buffer = MUST(ByteBuffer::copy(result.data(), buffer_size)); + auto array_buffer = JS::ArrayBuffer::create(m_realm, move(byte_buffer)); + return JS::Int32Array::create(m_realm, 2, array_buffer); + } + case GL_PACK_ALIGNMENT: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_PACK_ALIGNMENT, 1, nullptr, &result); + return JS::Value(result); + } + case GL_POLYGON_OFFSET_FACTOR: { + GLfloat result { 0.0f }; + glGetFloatvRobustANGLE(GL_POLYGON_OFFSET_FACTOR, 1, nullptr, &result); + return JS::Value(result); + } + case GL_POLYGON_OFFSET_FILL: { + GLboolean result { GL_FALSE }; + glGetBooleanvRobustANGLE(GL_POLYGON_OFFSET_FILL, 1, nullptr, &result); + return JS::Value(result == GL_TRUE); + } + case GL_POLYGON_OFFSET_UNITS: { + GLfloat result { 0.0f }; + glGetFloatvRobustANGLE(GL_POLYGON_OFFSET_UNITS, 1, nullptr, &result); + return JS::Value(result); + } + case GL_RED_BITS: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_RED_BITS, 1, nullptr, &result); + return JS::Value(result); + } + case GL_RENDERBUFFER_BINDING: { + if (!m_renderbuffer_binding) + return JS::js_null(); + return JS::Value(m_renderbuffer_binding); + } + case GL_RENDERER: { + auto result = reinterpret_cast(glGetString(GL_RENDERER)); + return JS::PrimitiveString::create(m_realm->vm(), ByteString { result }); + } + case GL_SAMPLE_ALPHA_TO_COVERAGE: { + GLboolean result { GL_FALSE }; + glGetBooleanvRobustANGLE(GL_SAMPLE_ALPHA_TO_COVERAGE, 1, nullptr, &result); + return JS::Value(result == GL_TRUE); + } + case GL_SAMPLE_BUFFERS: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_SAMPLE_BUFFERS, 1, nullptr, &result); + return JS::Value(result); + } + case GL_SAMPLE_COVERAGE: { + GLboolean result { GL_FALSE }; + glGetBooleanvRobustANGLE(GL_SAMPLE_COVERAGE, 1, nullptr, &result); + return JS::Value(result == GL_TRUE); + } + case GL_SAMPLE_COVERAGE_INVERT: { + GLboolean result { GL_FALSE }; + glGetBooleanvRobustANGLE(GL_SAMPLE_COVERAGE_INVERT, 1, nullptr, &result); + return JS::Value(result == GL_TRUE); + } + case GL_SAMPLE_COVERAGE_VALUE: { + GLfloat result { 0.0f }; + glGetFloatvRobustANGLE(GL_SAMPLE_COVERAGE_VALUE, 1, nullptr, &result); + return JS::Value(result); + } + case GL_SAMPLES: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_SAMPLES, 1, nullptr, &result); + return JS::Value(result); + } + case GL_SCISSOR_BOX: { + Array result; + result.fill(0); + constexpr size_t buffer_size = 4 * sizeof(GLint); + glGetIntegervRobustANGLE(GL_SCISSOR_BOX, 4, nullptr, result.data()); + auto byte_buffer = MUST(ByteBuffer::copy(result.data(), buffer_size)); + auto array_buffer = JS::ArrayBuffer::create(m_realm, move(byte_buffer)); + return JS::Int32Array::create(m_realm, 4, array_buffer); + } + case GL_SCISSOR_TEST: { + GLboolean result { GL_FALSE }; + glGetBooleanvRobustANGLE(GL_SCISSOR_TEST, 1, nullptr, &result); + return JS::Value(result == GL_TRUE); + } + case GL_SHADING_LANGUAGE_VERSION: { + auto result = reinterpret_cast(glGetString(GL_SHADING_LANGUAGE_VERSION)); + return JS::PrimitiveString::create(m_realm->vm(), ByteString { result }); + } + case GL_STENCIL_BACK_FAIL: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_STENCIL_BACK_FAIL, 1, nullptr, &result); + return JS::Value(result); + } + case GL_STENCIL_BACK_FUNC: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_STENCIL_BACK_FUNC, 1, nullptr, &result); + return JS::Value(result); + } + case GL_STENCIL_BACK_PASS_DEPTH_FAIL: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_STENCIL_BACK_PASS_DEPTH_FAIL, 1, nullptr, &result); + return JS::Value(result); + } + case GL_STENCIL_BACK_PASS_DEPTH_PASS: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_STENCIL_BACK_PASS_DEPTH_PASS, 1, nullptr, &result); + return JS::Value(result); + } + case GL_STENCIL_BACK_REF: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_STENCIL_BACK_REF, 1, nullptr, &result); + return JS::Value(result); + } + case GL_STENCIL_BACK_VALUE_MASK: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_STENCIL_BACK_VALUE_MASK, 1, nullptr, &result); + return JS::Value(result); + } + case GL_STENCIL_BACK_WRITEMASK: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_STENCIL_BACK_WRITEMASK, 1, nullptr, &result); + return JS::Value(result); + } + case GL_STENCIL_BITS: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_STENCIL_BITS, 1, nullptr, &result); + return JS::Value(result); + } + case GL_STENCIL_CLEAR_VALUE: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_STENCIL_CLEAR_VALUE, 1, nullptr, &result); + return JS::Value(result); + } + case GL_STENCIL_FAIL: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_STENCIL_FAIL, 1, nullptr, &result); + return JS::Value(result); + } + case GL_STENCIL_FUNC: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_STENCIL_FUNC, 1, nullptr, &result); + return JS::Value(result); + } + case GL_STENCIL_PASS_DEPTH_FAIL: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_STENCIL_PASS_DEPTH_FAIL, 1, nullptr, &result); + return JS::Value(result); + } + case GL_STENCIL_PASS_DEPTH_PASS: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_STENCIL_PASS_DEPTH_PASS, 1, nullptr, &result); + return JS::Value(result); + } + case GL_STENCIL_REF: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_STENCIL_REF, 1, nullptr, &result); + return JS::Value(result); + } + case GL_STENCIL_TEST: { + GLboolean result { GL_FALSE }; + glGetBooleanvRobustANGLE(GL_STENCIL_TEST, 1, nullptr, &result); + return JS::Value(result == GL_TRUE); + } + case GL_STENCIL_VALUE_MASK: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_STENCIL_VALUE_MASK, 1, nullptr, &result); + return JS::Value(result); + } + case GL_STENCIL_WRITEMASK: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_STENCIL_WRITEMASK, 1, nullptr, &result); + return JS::Value(result); + } + case GL_SUBPIXEL_BITS: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_SUBPIXEL_BITS, 1, nullptr, &result); + return JS::Value(result); + } + case GL_TEXTURE_BINDING_2D: { + if (!m_texture_binding_2d) + return JS::js_null(); + return JS::Value(m_texture_binding_2d); + } + case GL_TEXTURE_BINDING_CUBE_MAP: { + if (!m_texture_binding_cube_map) + return JS::js_null(); + return JS::Value(m_texture_binding_cube_map); + } + case GL_UNPACK_ALIGNMENT: { + GLint result { 0 }; + glGetIntegervRobustANGLE(GL_UNPACK_ALIGNMENT, 1, nullptr, &result); + return JS::Value(result); + } + case GL_VENDOR: { + auto result = reinterpret_cast(glGetString(GL_VENDOR)); + return JS::PrimitiveString::create(m_realm->vm(), ByteString { result }); + } + case GL_VERSION: { + auto result = reinterpret_cast(glGetString(GL_VERSION)); + return JS::PrimitiveString::create(m_realm->vm(), ByteString { result }); + } + case GL_VIEWPORT: { + Array result; + result.fill(0); + constexpr size_t buffer_size = 4 * sizeof(GLint); + glGetIntegervRobustANGLE(GL_VIEWPORT, 4, nullptr, result.data()); + auto byte_buffer = MUST(ByteBuffer::copy(result.data(), buffer_size)); + auto array_buffer = JS::ArrayBuffer::create(m_realm, move(byte_buffer)); + return JS::Int32Array::create(m_realm, 4, array_buffer); + } + default: + dbgln("Unknown WebGL parameter name: {:x}", pname); + set_error(GL_INVALID_ENUM); + return JS::js_null(); + } +} + +WebIDL::UnsignedLong WebGLRenderingContextImpl::get_error() +{ + m_context->make_current(); + return glGetError(); +} + +JS::Value WebGLRenderingContextImpl::get_program_parameter(GC::Root program, WebIDL::UnsignedLong pname) +{ + m_context->make_current(); + + GLuint program_handle = 0; + if (program) { + auto handle_or_error = program->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return JS::js_null(); + } + program_handle = handle_or_error.release_value(); + } + + GLint result = 0; + glGetProgramivRobustANGLE(program_handle, pname, 1, nullptr, &result); + switch (pname) { + case GL_ATTACHED_SHADERS: + case GL_ACTIVE_ATTRIBUTES: + case GL_ACTIVE_UNIFORMS: + + return JS::Value(result); + case GL_DELETE_STATUS: + case GL_LINK_STATUS: + case GL_VALIDATE_STATUS: + return JS::Value(result == GL_TRUE); + default: + dbgln("Unknown WebGL program parameter name: 0x{:04x}", pname); + set_error(GL_INVALID_ENUM); + return JS::js_null(); + } +} + +Optional WebGLRenderingContextImpl::get_program_info_log(GC::Root program) +{ + m_context->make_current(); + + GLuint program_handle = 0; + if (program) { + auto handle_or_error = program->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return {}; + } + program_handle = handle_or_error.release_value(); + } + + GLint info_log_length = 0; + glGetProgramiv(program_handle, GL_INFO_LOG_LENGTH, &info_log_length); + Vector info_log; + info_log.resize(info_log_length); + if (!info_log_length) + return String {}; + glGetProgramInfoLog(program_handle, info_log_length, nullptr, info_log.data()); + return String::from_utf8_without_validation(ReadonlyBytes { info_log.data(), static_cast(info_log_length - 1) }); +} + +JS::Value WebGLRenderingContextImpl::get_shader_parameter(GC::Root shader, WebIDL::UnsignedLong pname) +{ + m_context->make_current(); + + GLuint shader_handle = 0; + if (shader) { + auto handle_or_error = shader->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return JS::js_null(); + } + shader_handle = handle_or_error.release_value(); + } + + GLint result = 0; + glGetShaderivRobustANGLE(shader_handle, pname, 1, nullptr, &result); + switch (pname) { + case GL_SHADER_TYPE: + return JS::Value(result); + case GL_DELETE_STATUS: + case GL_COMPILE_STATUS: + return JS::Value(result == GL_TRUE); + default: + dbgln("Unknown WebGL shader parameter name: 0x{:04x}", pname); + set_error(GL_INVALID_ENUM); + return JS::js_null(); + } +} + +GC::Root WebGLRenderingContextImpl::get_shader_precision_format(WebIDL::UnsignedLong shadertype, WebIDL::UnsignedLong precisiontype) +{ + m_context->make_current(); + + GLint range[2]; + GLint precision; + glGetShaderPrecisionFormat(shadertype, precisiontype, range, &precision); + return WebGLShaderPrecisionFormat::create(m_realm, range[0], range[1], precision); +} + +Optional WebGLRenderingContextImpl::get_shader_info_log(GC::Root shader) +{ + m_context->make_current(); + + GLuint shader_handle = 0; + if (shader) { + auto handle_or_error = shader->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return {}; + } + shader_handle = handle_or_error.release_value(); + } + + GLint info_log_length = 0; + glGetShaderiv(shader_handle, GL_INFO_LOG_LENGTH, &info_log_length); + Vector info_log; + info_log.resize(info_log_length); + if (!info_log_length) + return String {}; + glGetShaderInfoLog(shader_handle, info_log_length, nullptr, info_log.data()); + return String::from_utf8_without_validation(ReadonlyBytes { info_log.data(), static_cast(info_log_length - 1) }); +} + +GC::Root WebGLRenderingContextImpl::get_uniform_location(GC::Root program, String name) +{ + m_context->make_current(); + + GLuint program_handle = 0; + if (program) { + auto handle_or_error = program->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return {}; + } + program_handle = handle_or_error.release_value(); + } + + auto name_null_terminated = null_terminated_string(name); + return WebGLUniformLocation::create(m_realm, glGetUniformLocation(program_handle, name_null_terminated.data())); +} + +void WebGLRenderingContextImpl::hint(WebIDL::UnsignedLong target, WebIDL::UnsignedLong mode) +{ + m_context->make_current(); + glHint(target, mode); +} + +bool WebGLRenderingContextImpl::is_buffer(GC::Root buffer) +{ + m_context->make_current(); + + auto buffer_handle = 0; + if (buffer) { + auto handle_or_error = buffer->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return false; + } + buffer_handle = handle_or_error.release_value(); + } + return glIsBuffer(buffer_handle); +} + +bool WebGLRenderingContextImpl::is_enabled(WebIDL::UnsignedLong cap) +{ + m_context->make_current(); + return glIsEnabled(cap); +} + +bool WebGLRenderingContextImpl::is_framebuffer(GC::Root framebuffer) +{ + m_context->make_current(); + + auto framebuffer_handle = 0; + if (framebuffer) { + auto handle_or_error = framebuffer->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return false; + } + framebuffer_handle = handle_or_error.release_value(); + } + return glIsFramebuffer(framebuffer_handle); +} + +bool WebGLRenderingContextImpl::is_program(GC::Root program) +{ + m_context->make_current(); + + auto program_handle = 0; + if (program) { + auto handle_or_error = program->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return false; + } + program_handle = handle_or_error.release_value(); + } + return glIsProgram(program_handle); +} + +bool WebGLRenderingContextImpl::is_renderbuffer(GC::Root renderbuffer) +{ + m_context->make_current(); + + auto renderbuffer_handle = 0; + if (renderbuffer) { + auto handle_or_error = renderbuffer->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return false; + } + renderbuffer_handle = handle_or_error.release_value(); + } + return glIsRenderbuffer(renderbuffer_handle); +} + +bool WebGLRenderingContextImpl::is_shader(GC::Root shader) +{ + m_context->make_current(); + + auto shader_handle = 0; + if (shader) { + auto handle_or_error = shader->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return false; + } + shader_handle = handle_or_error.release_value(); + } + return glIsShader(shader_handle); +} + +bool WebGLRenderingContextImpl::is_texture(GC::Root texture) +{ + m_context->make_current(); + + auto texture_handle = 0; + if (texture) { + auto handle_or_error = texture->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return false; + } + texture_handle = handle_or_error.release_value(); + } + return glIsTexture(texture_handle); +} + +void WebGLRenderingContextImpl::line_width(float width) +{ + m_context->make_current(); + glLineWidth(width); +} + +void WebGLRenderingContextImpl::link_program(GC::Root program) +{ + m_context->make_current(); + + auto program_handle = 0; + if (program) { + auto handle_or_error = program->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return; + } + program_handle = handle_or_error.release_value(); + } + glLinkProgram(program_handle); +} + +void WebGLRenderingContextImpl::pixel_storei(WebIDL::UnsignedLong pname, WebIDL::Long param) +{ + m_context->make_current(); + glPixelStorei(pname, param); +} + +void WebGLRenderingContextImpl::polygon_offset(float factor, float units) +{ + m_context->make_current(); + glPolygonOffset(factor, units); +} + +void WebGLRenderingContextImpl::renderbuffer_storage(WebIDL::UnsignedLong target, WebIDL::UnsignedLong internalformat, WebIDL::Long width, WebIDL::Long height) +{ + m_context->make_current(); + +#define GL_DEPTH_STENCIL 0x84F9 +#define GL_DEPTH24_STENCIL8 0x88F0 + + if (internalformat == GL_DEPTH_STENCIL) + internalformat = GL_DEPTH24_STENCIL8; + +#undef GL_DEPTH_STENCIL +#undef GL_DEPTH24_STENCIL8 + + glRenderbufferStorage(target, internalformat, width, height); +} + +void WebGLRenderingContextImpl::sample_coverage(float value, bool invert) +{ + m_context->make_current(); + glSampleCoverage(value, invert); +} + +void WebGLRenderingContextImpl::scissor(WebIDL::Long x, WebIDL::Long y, WebIDL::Long width, WebIDL::Long height) +{ + m_context->make_current(); + glScissor(x, y, width, height); +} + +void WebGLRenderingContextImpl::shader_source(GC::Root shader, String source) +{ + m_context->make_current(); + + GLuint shader_handle = 0; + if (shader) { + auto handle_or_error = shader->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return; + } + shader_handle = handle_or_error.release_value(); + } + + Vector strings; + auto string = null_terminated_string(source); + strings.append(string.data()); + Vector length; + length.append(source.bytes().size()); + glShaderSource(shader_handle, 1, strings.data(), length.data()); +} + +void WebGLRenderingContextImpl::stencil_func(WebIDL::UnsignedLong func, WebIDL::Long ref, WebIDL::UnsignedLong mask) +{ + m_context->make_current(); + glStencilFunc(func, ref, mask); +} + +void WebGLRenderingContextImpl::stencil_func_separate(WebIDL::UnsignedLong face, WebIDL::UnsignedLong func, WebIDL::Long ref, WebIDL::UnsignedLong mask) +{ + m_context->make_current(); + glStencilFuncSeparate(face, func, ref, mask); +} + +void WebGLRenderingContextImpl::stencil_mask(WebIDL::UnsignedLong mask) +{ + m_context->make_current(); + glStencilMask(mask); +} + +void WebGLRenderingContextImpl::stencil_mask_separate(WebIDL::UnsignedLong face, WebIDL::UnsignedLong mask) +{ + m_context->make_current(); + glStencilMaskSeparate(face, mask); +} + +void WebGLRenderingContextImpl::stencil_op(WebIDL::UnsignedLong fail, WebIDL::UnsignedLong zfail, WebIDL::UnsignedLong zpass) +{ + m_context->make_current(); + glStencilOp(fail, zfail, zpass); +} + +void WebGLRenderingContextImpl::stencil_op_separate(WebIDL::UnsignedLong face, WebIDL::UnsignedLong fail, WebIDL::UnsignedLong zfail, WebIDL::UnsignedLong zpass) +{ + m_context->make_current(); + glStencilOpSeparate(face, fail, zfail, zpass); +} + +void WebGLRenderingContextImpl::tex_parameterf(WebIDL::UnsignedLong target, WebIDL::UnsignedLong pname, float param) +{ + m_context->make_current(); + glTexParameterf(target, pname, param); +} + +void WebGLRenderingContextImpl::tex_parameteri(WebIDL::UnsignedLong target, WebIDL::UnsignedLong pname, WebIDL::Long param) +{ + m_context->make_current(); + glTexParameteri(target, pname, param); +} + +void WebGLRenderingContextImpl::uniform1f(GC::Root location, float x) +{ + m_context->make_current(); + glUniform1f(location ? location->handle() : 0, x); +} + +void WebGLRenderingContextImpl::uniform2f(GC::Root location, float x, float y) +{ + m_context->make_current(); + glUniform2f(location ? location->handle() : 0, x, y); +} + +void WebGLRenderingContextImpl::uniform3f(GC::Root location, float x, float y, float z) +{ + m_context->make_current(); + glUniform3f(location ? location->handle() : 0, x, y, z); +} + +void WebGLRenderingContextImpl::uniform4f(GC::Root location, float x, float y, float z, float w) +{ + m_context->make_current(); + glUniform4f(location ? location->handle() : 0, x, y, z, w); +} + +void WebGLRenderingContextImpl::uniform1i(GC::Root location, WebIDL::Long x) +{ + m_context->make_current(); + glUniform1i(location ? location->handle() : 0, x); +} + +void WebGLRenderingContextImpl::uniform2i(GC::Root location, WebIDL::Long x, WebIDL::Long y) +{ + m_context->make_current(); + glUniform2i(location ? location->handle() : 0, x, y); +} + +void WebGLRenderingContextImpl::uniform3i(GC::Root location, WebIDL::Long x, WebIDL::Long y, WebIDL::Long z) +{ + m_context->make_current(); + glUniform3i(location ? location->handle() : 0, x, y, z); +} + +void WebGLRenderingContextImpl::uniform4i(GC::Root location, WebIDL::Long x, WebIDL::Long y, WebIDL::Long z, WebIDL::Long w) +{ + m_context->make_current(); + glUniform4i(location ? location->handle() : 0, x, y, z, w); +} + +void WebGLRenderingContextImpl::use_program(GC::Root program) +{ + m_context->make_current(); + + GLuint program_handle = 0; + if (program) { + auto handle_or_error = program->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return; + } + program_handle = handle_or_error.release_value(); + } + + glUseProgram(program_handle); + m_current_program = program; +} + +void WebGLRenderingContextImpl::validate_program(GC::Root program) +{ + m_context->make_current(); + + auto program_handle = 0; + if (program) { + auto handle_or_error = program->handle(this); + if (handle_or_error.is_error()) { + set_error(GL_INVALID_OPERATION); + return; + } + program_handle = handle_or_error.release_value(); + } + glValidateProgram(program_handle); +} + +void WebGLRenderingContextImpl::vertex_attrib1f(WebIDL::UnsignedLong index, float x) +{ + m_context->make_current(); + glVertexAttrib1f(index, x); +} + +void WebGLRenderingContextImpl::vertex_attrib2f(WebIDL::UnsignedLong index, float x, float y) +{ + m_context->make_current(); + glVertexAttrib2f(index, x, y); +} + +void WebGLRenderingContextImpl::vertex_attrib3f(WebIDL::UnsignedLong index, float x, float y, float z) +{ + m_context->make_current(); + glVertexAttrib3f(index, x, y, z); +} + +void WebGLRenderingContextImpl::vertex_attrib4f(WebIDL::UnsignedLong index, float x, float y, float z, float w) +{ + m_context->make_current(); + glVertexAttrib4f(index, x, y, z, w); +} + +void WebGLRenderingContextImpl::vertex_attrib1fv(WebIDL::UnsignedLong index, Variant, Vector> values) +{ + m_context->make_current(); + + if (values.has>()) { + auto& data = values.get>(); + if (data.size() < 1) { + set_error(GL_INVALID_VALUE); + return; + } + + glVertexAttrib1fv(index, data.data()); + return; + } + + auto& buffer_source = values.get>(); + if (buffer_source->byte_length() < 1 * sizeof(float)) { + set_error(GL_INVALID_VALUE); + return; + } + + auto& typed_array_base = static_cast(*buffer_source->raw_object()); + auto& float32_array = as(typed_array_base); + float const* data = float32_array.data().data(); + glVertexAttrib1fv(index, data); +} + +void WebGLRenderingContextImpl::vertex_attrib2fv(WebIDL::UnsignedLong index, Variant, Vector> values) +{ + m_context->make_current(); + + if (values.has>()) { + auto& data = values.get>(); + if (data.size() < 2) { + set_error(GL_INVALID_VALUE); + return; + } + + glVertexAttrib2fv(index, data.data()); + return; + } + + auto& buffer_source = values.get>(); + if (buffer_source->byte_length() < 2 * sizeof(float)) { + set_error(GL_INVALID_VALUE); + return; + } + + auto& typed_array_base = static_cast(*buffer_source->raw_object()); + auto& float32_array = as(typed_array_base); + float const* data = float32_array.data().data(); + glVertexAttrib2fv(index, data); +} + +void WebGLRenderingContextImpl::vertex_attrib3fv(WebIDL::UnsignedLong index, Variant, Vector> values) +{ + m_context->make_current(); + + if (values.has>()) { + auto& data = values.get>(); + if (data.size() < 3) { + set_error(GL_INVALID_VALUE); + return; + } + + glVertexAttrib3fv(index, data.data()); + return; + } + + auto& buffer_source = values.get>(); + if (buffer_source->byte_length() < 3 * sizeof(float)) { + set_error(GL_INVALID_VALUE); + return; + } + + auto& typed_array_base = static_cast(*buffer_source->raw_object()); + auto& float32_array = as(typed_array_base); + float const* data = float32_array.data().data(); + glVertexAttrib3fv(index, data); +} + +void WebGLRenderingContextImpl::vertex_attrib4fv(WebIDL::UnsignedLong index, Variant, Vector> values) +{ + m_context->make_current(); + + if (values.has>()) { + auto& data = values.get>(); + if (data.size() < 4) { + set_error(GL_INVALID_VALUE); + return; + } + + glVertexAttrib4fv(index, data.data()); + return; + } + + auto& buffer_source = values.get>(); + if (buffer_source->byte_length() < 4 * sizeof(float)) { + set_error(GL_INVALID_VALUE); + return; + } + + auto& typed_array_base = static_cast(*buffer_source->raw_object()); + auto& float32_array = as(typed_array_base); + float const* data = float32_array.data().data(); + glVertexAttrib4fv(index, data); +} + +void WebGLRenderingContextImpl::vertex_attrib_pointer(WebIDL::UnsignedLong index, WebIDL::Long size, WebIDL::UnsignedLong type, bool normalized, WebIDL::Long stride, WebIDL::LongLong offset) +{ + m_context->make_current(); + + glVertexAttribPointer(index, size, type, normalized, stride, reinterpret_cast(offset)); +} + +void WebGLRenderingContextImpl::viewport(WebIDL::Long x, WebIDL::Long y, WebIDL::Long width, WebIDL::Long height) +{ + m_context->make_current(); + glViewport(x, y, width, height); +} + +void WebGLRenderingContextImpl::visit_edges(JS::Cell::Visitor& visitor) +{ + visitor.visit(m_realm); + visitor.visit(m_array_buffer_binding); + visitor.visit(m_element_array_buffer_binding); + visitor.visit(m_current_program); + visitor.visit(m_framebuffer_binding); + visitor.visit(m_renderbuffer_binding); + visitor.visit(m_texture_binding_2d); + visitor.visit(m_texture_binding_cube_map); +} + +} diff --git a/Libraries/LibWeb/WebGL/WebGLRenderingContextImpl.h b/Libraries/LibWeb/WebGL/WebGLRenderingContextImpl.h new file mode 100644 index 00000000000..01eddc192e2 --- /dev/null +++ b/Libraries/LibWeb/WebGL/WebGLRenderingContextImpl.h @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2024, Aliaksandr Kalenik + * Copyright (c) 2024-2025, Luke Wilde + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace Web::WebGL { + +using namespace Web::HTML; + +class WebGLRenderingContextImpl : public WebGLRenderingContextBase { +public: + WebGLRenderingContextImpl(JS::Realm&, NonnullOwnPtr); + + virtual OpenGLContext& context() override { return *m_context; } + + virtual void present() = 0; + virtual void needs_to_present() = 0; + virtual void set_error(GLenum) = 0; + void buffer_data(WebIDL::UnsignedLong target, WebIDL::LongLong size, WebIDL::UnsignedLong usage); + void buffer_data(WebIDL::UnsignedLong target, GC::Root data, WebIDL::UnsignedLong usage); + void buffer_sub_data(WebIDL::UnsignedLong target, WebIDL::LongLong offset, GC::Root data); + void compressed_tex_image2d(WebIDL::UnsignedLong target, WebIDL::Long level, WebIDL::UnsignedLong internalformat, WebIDL::Long width, WebIDL::Long height, WebIDL::Long border, GC::Root data); + void compressed_tex_sub_image2d(WebIDL::UnsignedLong target, WebIDL::Long level, WebIDL::Long xoffset, WebIDL::Long yoffset, WebIDL::Long width, WebIDL::Long height, WebIDL::UnsignedLong format, GC::Root data); + void read_pixels(WebIDL::Long x, WebIDL::Long y, WebIDL::Long width, WebIDL::Long height, WebIDL::UnsignedLong format, WebIDL::UnsignedLong type, GC::Root pixels); + void tex_image2d(WebIDL::UnsignedLong target, WebIDL::Long level, WebIDL::Long internalformat, WebIDL::Long width, WebIDL::Long height, WebIDL::Long border, WebIDL::UnsignedLong format, WebIDL::UnsignedLong type, GC::Root pixels); + void tex_image2d(WebIDL::UnsignedLong target, WebIDL::Long level, WebIDL::Long internalformat, WebIDL::UnsignedLong format, WebIDL::UnsignedLong type, Variant, GC::Root, GC::Root, GC::Root, GC::Root, GC::Root> source); + void tex_sub_image2d(WebIDL::UnsignedLong target, WebIDL::Long level, WebIDL::Long xoffset, WebIDL::Long yoffset, WebIDL::Long width, WebIDL::Long height, WebIDL::UnsignedLong format, WebIDL::UnsignedLong type, GC::Root pixels); + void tex_sub_image2d(WebIDL::UnsignedLong target, WebIDL::Long level, WebIDL::Long xoffset, WebIDL::Long yoffset, WebIDL::UnsignedLong format, WebIDL::UnsignedLong type, Variant, GC::Root, GC::Root, GC::Root, GC::Root, GC::Root> source); + void uniform1fv(GC::Root location, Variant, Vector> v); + void uniform2fv(GC::Root location, Variant, Vector> v); + void uniform3fv(GC::Root location, Variant, Vector> v); + void uniform4fv(GC::Root location, Variant, Vector> v); + void uniform1iv(GC::Root location, Variant, Vector> v); + void uniform2iv(GC::Root location, Variant, Vector> v); + void uniform3iv(GC::Root location, Variant, Vector> v); + void uniform4iv(GC::Root location, Variant, Vector> v); + void uniform_matrix2fv(GC::Root location, bool transpose, Variant, Vector> value); + void uniform_matrix3fv(GC::Root location, bool transpose, Variant, Vector> value); + void uniform_matrix4fv(GC::Root location, bool transpose, Variant, Vector> value); + void active_texture(WebIDL::UnsignedLong texture); + void attach_shader(GC::Root program, GC::Root shader); + void bind_attrib_location(GC::Root program, WebIDL::UnsignedLong index, String name); + void bind_buffer(WebIDL::UnsignedLong target, GC::Root buffer); + void bind_framebuffer(WebIDL::UnsignedLong target, GC::Root framebuffer); + void bind_renderbuffer(WebIDL::UnsignedLong target, GC::Root renderbuffer); + void bind_texture(WebIDL::UnsignedLong target, GC::Root texture); + void blend_color(float red, float green, float blue, float alpha); + void blend_equation(WebIDL::UnsignedLong mode); + void blend_equation_separate(WebIDL::UnsignedLong mode_rgb, WebIDL::UnsignedLong mode_alpha); + void blend_func(WebIDL::UnsignedLong sfactor, WebIDL::UnsignedLong dfactor); + void blend_func_separate(WebIDL::UnsignedLong src_rgb, WebIDL::UnsignedLong dst_rgb, WebIDL::UnsignedLong src_alpha, WebIDL::UnsignedLong dst_alpha); + WebIDL::UnsignedLong check_framebuffer_status(WebIDL::UnsignedLong target); + void clear(WebIDL::UnsignedLong mask); + void clear_color(float red, float green, float blue, float alpha); + void clear_depth(float depth); + void clear_stencil(WebIDL::Long s); + void color_mask(bool red, bool green, bool blue, bool alpha); + void compile_shader(GC::Root shader); + void copy_tex_image2d(WebIDL::UnsignedLong target, WebIDL::Long level, WebIDL::UnsignedLong internalformat, WebIDL::Long x, WebIDL::Long y, WebIDL::Long width, WebIDL::Long height, WebIDL::Long border); + void copy_tex_sub_image2d(WebIDL::UnsignedLong target, WebIDL::Long level, WebIDL::Long xoffset, WebIDL::Long yoffset, WebIDL::Long x, WebIDL::Long y, WebIDL::Long width, WebIDL::Long height); + GC::Root create_buffer(); + GC::Root create_framebuffer(); + GC::Root create_program(); + GC::Root create_renderbuffer(); + GC::Root create_shader(WebIDL::UnsignedLong type); + GC::Root create_texture(); + void cull_face(WebIDL::UnsignedLong mode); + void delete_buffer(GC::Root buffer); + void delete_framebuffer(GC::Root framebuffer); + void delete_program(GC::Root program); + void delete_renderbuffer(GC::Root renderbuffer); + void delete_shader(GC::Root shader); + void delete_texture(GC::Root texture); + void depth_func(WebIDL::UnsignedLong func); + void depth_mask(bool flag); + void depth_range(float z_near, float z_far); + void detach_shader(GC::Root program, GC::Root shader); + void disable(WebIDL::UnsignedLong cap); + void disable_vertex_attrib_array(WebIDL::UnsignedLong index); + void draw_arrays(WebIDL::UnsignedLong mode, WebIDL::Long first, WebIDL::Long count); + void draw_elements(WebIDL::UnsignedLong mode, WebIDL::Long count, WebIDL::UnsignedLong type, WebIDL::LongLong offset); + void enable(WebIDL::UnsignedLong cap); + void enable_vertex_attrib_array(WebIDL::UnsignedLong index); + void finish(); + void flush(); + void framebuffer_renderbuffer(WebIDL::UnsignedLong target, WebIDL::UnsignedLong attachment, WebIDL::UnsignedLong renderbuffertarget, GC::Root renderbuffer); + void framebuffer_texture2d(WebIDL::UnsignedLong target, WebIDL::UnsignedLong attachment, WebIDL::UnsignedLong textarget, GC::Root texture, WebIDL::Long level); + void front_face(WebIDL::UnsignedLong mode); + void generate_mipmap(WebIDL::UnsignedLong target); + GC::Root get_active_attrib(GC::Root program, WebIDL::UnsignedLong index); + GC::Root get_active_uniform(GC::Root program, WebIDL::UnsignedLong index); + Optional>> get_attached_shaders(GC::Root program); + WebIDL::Long get_attrib_location(GC::Root program, String name); + JS::Value get_buffer_parameter(WebIDL::UnsignedLong target, WebIDL::UnsignedLong pname); + JS::Value get_parameter(WebIDL::UnsignedLong pname); + WebIDL::UnsignedLong get_error(); + JS::Value get_program_parameter(GC::Root program, WebIDL::UnsignedLong pname); + Optional get_program_info_log(GC::Root program); + JS::Value get_shader_parameter(GC::Root shader, WebIDL::UnsignedLong pname); + GC::Root get_shader_precision_format(WebIDL::UnsignedLong shadertype, WebIDL::UnsignedLong precisiontype); + Optional get_shader_info_log(GC::Root shader); + GC::Root get_uniform_location(GC::Root program, String name); + void hint(WebIDL::UnsignedLong target, WebIDL::UnsignedLong mode); + bool is_buffer(GC::Root buffer); + bool is_enabled(WebIDL::UnsignedLong cap); + bool is_framebuffer(GC::Root framebuffer); + bool is_program(GC::Root program); + bool is_renderbuffer(GC::Root renderbuffer); + bool is_shader(GC::Root shader); + bool is_texture(GC::Root texture); + void line_width(float width); + void link_program(GC::Root program); + void pixel_storei(WebIDL::UnsignedLong pname, WebIDL::Long param); + void polygon_offset(float factor, float units); + void renderbuffer_storage(WebIDL::UnsignedLong target, WebIDL::UnsignedLong internalformat, WebIDL::Long width, WebIDL::Long height); + void sample_coverage(float value, bool invert); + void scissor(WebIDL::Long x, WebIDL::Long y, WebIDL::Long width, WebIDL::Long height); + void shader_source(GC::Root shader, String source); + void stencil_func(WebIDL::UnsignedLong func, WebIDL::Long ref, WebIDL::UnsignedLong mask); + void stencil_func_separate(WebIDL::UnsignedLong face, WebIDL::UnsignedLong func, WebIDL::Long ref, WebIDL::UnsignedLong mask); + void stencil_mask(WebIDL::UnsignedLong mask); + void stencil_mask_separate(WebIDL::UnsignedLong face, WebIDL::UnsignedLong mask); + void stencil_op(WebIDL::UnsignedLong fail, WebIDL::UnsignedLong zfail, WebIDL::UnsignedLong zpass); + void stencil_op_separate(WebIDL::UnsignedLong face, WebIDL::UnsignedLong fail, WebIDL::UnsignedLong zfail, WebIDL::UnsignedLong zpass); + void tex_parameterf(WebIDL::UnsignedLong target, WebIDL::UnsignedLong pname, float param); + void tex_parameteri(WebIDL::UnsignedLong target, WebIDL::UnsignedLong pname, WebIDL::Long param); + void uniform1f(GC::Root location, float x); + void uniform2f(GC::Root location, float x, float y); + void uniform3f(GC::Root location, float x, float y, float z); + void uniform4f(GC::Root location, float x, float y, float z, float w); + void uniform1i(GC::Root location, WebIDL::Long x); + void uniform2i(GC::Root location, WebIDL::Long x, WebIDL::Long y); + void uniform3i(GC::Root location, WebIDL::Long x, WebIDL::Long y, WebIDL::Long z); + void uniform4i(GC::Root location, WebIDL::Long x, WebIDL::Long y, WebIDL::Long z, WebIDL::Long w); + void use_program(GC::Root program); + void validate_program(GC::Root program); + void vertex_attrib1f(WebIDL::UnsignedLong index, float x); + void vertex_attrib2f(WebIDL::UnsignedLong index, float x, float y); + void vertex_attrib3f(WebIDL::UnsignedLong index, float x, float y, float z); + void vertex_attrib4f(WebIDL::UnsignedLong index, float x, float y, float z, float w); + void vertex_attrib1fv(WebIDL::UnsignedLong index, Variant, Vector> values); + void vertex_attrib2fv(WebIDL::UnsignedLong index, Variant, Vector> values); + void vertex_attrib3fv(WebIDL::UnsignedLong index, Variant, Vector> values); + void vertex_attrib4fv(WebIDL::UnsignedLong index, Variant, Vector> values); + void vertex_attrib_pointer(WebIDL::UnsignedLong index, WebIDL::Long size, WebIDL::UnsignedLong type, bool normalized, WebIDL::Long stride, WebIDL::LongLong offset); + void viewport(WebIDL::Long x, WebIDL::Long y, WebIDL::Long width, WebIDL::Long height); + +protected: + virtual void visit_edges(JS::Cell::Visitor&) override; + +private: + GC::Ref m_realm; + GC::Ptr m_array_buffer_binding; + GC::Ptr m_element_array_buffer_binding; + GC::Ptr m_current_program; + GC::Ptr m_framebuffer_binding; + GC::Ptr m_renderbuffer_binding; + GC::Ptr m_texture_binding_2d; + GC::Ptr m_texture_binding_cube_map; + + NonnullOwnPtr m_context; +}; + +} diff --git a/Meta/Lagom/Tools/CodeGenerators/LibWeb/CMakeLists.txt b/Meta/Lagom/Tools/CodeGenerators/LibWeb/CMakeLists.txt index 3acf6ccd86d..d208044780e 100644 --- a/Meta/Lagom/Tools/CodeGenerators/LibWeb/CMakeLists.txt +++ b/Meta/Lagom/Tools/CodeGenerators/LibWeb/CMakeLists.txt @@ -12,7 +12,6 @@ lagom_tool(GenerateCSSStyleProperties SOURCES GenerateCSSStyleProperties.c lagom_tool(GenerateCSSTransformFunctions SOURCES GenerateCSSTransformFunctions.cpp LIBS LibMain) lagom_tool(GenerateWindowOrWorkerInterfaces SOURCES GenerateWindowOrWorkerInterfaces.cpp LIBS LibMain LibIDL) lagom_tool(GenerateAriaRoles SOURCES GenerateAriaRoles.cpp LIBS LibMain) -lagom_tool(GenerateWebGLRenderingContext SOURCES GenerateWebGLRenderingContext.cpp BindingsGenerator/IDLGenerators.cpp LIBS LibMain LibIDL) lagom_tool(GenerateNamedCharacterReferences SOURCES GenerateNamedCharacterReferences.cpp LIBS LibMain) add_subdirectory(BindingsGenerator) diff --git a/Meta/Lagom/Tools/CodeGenerators/LibWeb/GenerateWebGLRenderingContext.cpp b/Meta/Lagom/Tools/CodeGenerators/LibWeb/GenerateWebGLRenderingContext.cpp deleted file mode 100644 index 68087a5b204..00000000000 --- a/Meta/Lagom/Tools/CodeGenerators/LibWeb/GenerateWebGLRenderingContext.cpp +++ /dev/null @@ -1,2227 +0,0 @@ -/* - * Copyright (c) 2024, Aliaksandr Kalenik - * Copyright (c) 2024-2025, Luke Wilde - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "BindingsGenerator/IDLGenerators.h" - -#include -#include -#include -#include -#include -#include -#include - -static bool is_webgl_object_type(StringView type_name) -{ - return type_name == "WebGLBuffer"sv - || type_name == "WebGLFramebuffer"sv - || type_name == "WebGLProgram"sv - || type_name == "WebGLRenderbuffer"sv - || type_name == "WebGLSampler"sv - || type_name == "WebGLShader"sv - || type_name == "WebGLTexture"sv - || type_name == "WebGLVertexArrayObject"sv; -} - -static bool gl_function_modifies_framebuffer(StringView function_name) -{ - return function_name == "clear"sv - || function_name == "clearBufferfv"sv - || function_name == "clearBufferiv"sv - || function_name == "clearBufferuiv"sv - || function_name == "clearBufferfi"sv - || function_name == "drawArrays"sv - || function_name == "drawArraysInstanced"sv - || function_name == "drawElements"sv - || function_name == "drawElementsInstanced"sv - || function_name == "blitFramebuffer"sv - || function_name == "invalidateFramebuffer"sv; -} - -static ByteString to_cpp_type(const IDL::Type& type, const IDL::Interface& interface) -{ - if (type.name() == "undefined"sv) - return "void"sv; - if (type.name() == "object"sv) { - if (type.is_nullable()) - return "JS::Object*"sv; - return "JS::Object&"sv; - } - if (type.name() == "DOMString"sv) { - if (type.is_nullable()) - return "Optional"sv; - return "String"sv; - } - if (type.name() == "sequence") { - auto& parameterized_type = as(type); - auto sequence_cpp_type = idl_type_name_to_cpp_type(parameterized_type.parameters().first(), interface); - - if (type.is_nullable()) { - return ByteString::formatted("Optional>", sequence_cpp_type.name); - } - - return ByteString::formatted("Vector<{}>", sequence_cpp_type.name); - } - auto cpp_type = idl_type_name_to_cpp_type(type, interface); - return cpp_type.name; -} - -static ByteString idl_to_gl_function_name(StringView function_name) -{ - StringBuilder gl_function_name_builder; - gl_function_name_builder.append("gl"sv); - for (size_t i = 0; i < function_name.length(); ++i) { - if (i == 0) { - gl_function_name_builder.append(to_ascii_uppercase(function_name[i])); - } else { - gl_function_name_builder.append(function_name[i]); - } - } - if (function_name == "clearDepth"sv || function_name == "depthRange"sv) { - gl_function_name_builder.append("f"sv); - } - return gl_function_name_builder.to_byte_string(); -} - -struct NameAndType { - StringView name; - struct { - StringView type; - int element_count { 0 }; - } return_type; - Optional webgl_version { OptionalNone {} }; -}; - -static void generate_get_parameter(SourceGenerator& generator, int webgl_version) -{ - Vector const name_to_type = { - { "ACTIVE_TEXTURE"sv, { "GLenum"sv } }, - { "ALIASED_LINE_WIDTH_RANGE"sv, { "Float32Array"sv, 2 } }, - { "ALIASED_POINT_SIZE_RANGE"sv, { "Float32Array"sv, 2 } }, - { "ALPHA_BITS"sv, { "GLint"sv } }, - { "ARRAY_BUFFER_BINDING"sv, { "WebGLBuffer"sv } }, - { "BLEND"sv, { "GLboolean"sv } }, - { "BLEND_COLOR"sv, { "Float32Array"sv, 4 } }, - { "BLEND_DST_ALPHA"sv, { "GLenum"sv } }, - { "BLEND_DST_RGB"sv, { "GLenum"sv } }, - { "BLEND_EQUATION_ALPHA"sv, { "GLenum"sv } }, - { "BLEND_EQUATION_RGB"sv, { "GLenum"sv } }, - { "BLEND_SRC_ALPHA"sv, { "GLenum"sv } }, - { "BLEND_SRC_RGB"sv, { "GLenum"sv } }, - { "BLUE_BITS"sv, { "GLint"sv } }, - { "COLOR_CLEAR_VALUE"sv, { "Float32Array"sv, 4 } }, - // FIXME: { "COLOR_WRITEMASK"sv, { "sequence"sv, 4 } }, - // FIXME: { "COMPRESSED_TEXTURE_FORMATS"sv, { "Uint32Array"sv } }, - { "CULL_FACE"sv, { "GLboolean"sv } }, - { "CULL_FACE_MODE"sv, { "GLenum"sv } }, - { "CURRENT_PROGRAM"sv, { "WebGLProgram"sv } }, - { "DEPTH_BITS"sv, { "GLint"sv } }, - { "DEPTH_CLEAR_VALUE"sv, { "GLfloat"sv } }, - { "DEPTH_FUNC"sv, { "GLenum"sv } }, - { "DEPTH_RANGE"sv, { "Float32Array"sv, 2 } }, - { "DEPTH_TEST"sv, { "GLboolean"sv } }, - { "DEPTH_WRITEMASK"sv, { "GLboolean"sv } }, - { "DITHER"sv, { "GLboolean"sv } }, - { "ELEMENT_ARRAY_BUFFER_BINDING"sv, { "WebGLBuffer"sv } }, - { "FRAMEBUFFER_BINDING"sv, { "WebGLFramebuffer"sv } }, - { "FRONT_FACE"sv, { "GLenum"sv } }, - { "GENERATE_MIPMAP_HINT"sv, { "GLenum"sv } }, - { "GREEN_BITS"sv, { "GLint"sv } }, - { "IMPLEMENTATION_COLOR_READ_FORMAT"sv, { "GLenum"sv } }, - { "IMPLEMENTATION_COLOR_READ_TYPE"sv, { "GLenum"sv } }, - { "LINE_WIDTH"sv, { "GLfloat"sv } }, - { "MAX_COMBINED_TEXTURE_IMAGE_UNITS"sv, { "GLint"sv } }, - { "MAX_CUBE_MAP_TEXTURE_SIZE"sv, { "GLint"sv } }, - { "MAX_FRAGMENT_UNIFORM_VECTORS"sv, { "GLint"sv } }, - { "MAX_RENDERBUFFER_SIZE"sv, { "GLint"sv } }, - { "MAX_TEXTURE_IMAGE_UNITS"sv, { "GLint"sv } }, - { "MAX_TEXTURE_SIZE"sv, { "GLint"sv } }, - { "MAX_VARYING_VECTORS"sv, { "GLint"sv } }, - { "MAX_VERTEX_ATTRIBS"sv, { "GLint"sv } }, - { "MAX_VERTEX_TEXTURE_IMAGE_UNITS"sv, { "GLint"sv } }, - { "MAX_VERTEX_UNIFORM_VECTORS"sv, { "GLint"sv } }, - { "MAX_VIEWPORT_DIMS"sv, { "Int32Array"sv, 2 } }, - { "PACK_ALIGNMENT"sv, { "GLint"sv } }, - { "POLYGON_OFFSET_FACTOR"sv, { "GLfloat"sv } }, - { "POLYGON_OFFSET_FILL"sv, { "GLboolean"sv } }, - { "POLYGON_OFFSET_UNITS"sv, { "GLfloat"sv } }, - { "RED_BITS"sv, { "GLint"sv } }, - { "RENDERBUFFER_BINDING"sv, { "WebGLRenderbuffer"sv } }, - { "RENDERER"sv, { "DOMString"sv } }, - { "SAMPLE_ALPHA_TO_COVERAGE"sv, { "GLboolean"sv } }, - { "SAMPLE_BUFFERS"sv, { "GLint"sv } }, - { "SAMPLE_COVERAGE"sv, { "GLboolean"sv } }, - { "SAMPLE_COVERAGE_INVERT"sv, { "GLboolean"sv } }, - { "SAMPLE_COVERAGE_VALUE"sv, { "GLfloat"sv } }, - { "SAMPLES"sv, { "GLint"sv } }, - { "SCISSOR_BOX"sv, { "Int32Array"sv, 4 } }, - { "SCISSOR_TEST"sv, { "GLboolean"sv } }, - { "SHADING_LANGUAGE_VERSION"sv, { "DOMString"sv } }, - { "STENCIL_BACK_FAIL"sv, { "GLenum"sv } }, - { "STENCIL_BACK_FUNC"sv, { "GLenum"sv } }, - { "STENCIL_BACK_PASS_DEPTH_FAIL"sv, { "GLenum"sv } }, - { "STENCIL_BACK_PASS_DEPTH_PASS"sv, { "GLenum"sv } }, - { "STENCIL_BACK_REF"sv, { "GLint"sv } }, - { "STENCIL_BACK_VALUE_MASK"sv, { "GLuint"sv } }, - { "STENCIL_BACK_WRITEMASK"sv, { "GLuint"sv } }, - { "STENCIL_BITS"sv, { "GLint"sv } }, - { "STENCIL_CLEAR_VALUE"sv, { "GLint"sv } }, - { "STENCIL_FAIL"sv, { "GLenum"sv } }, - { "STENCIL_FUNC"sv, { "GLenum"sv } }, - { "STENCIL_PASS_DEPTH_FAIL"sv, { "GLenum"sv } }, - { "STENCIL_PASS_DEPTH_PASS"sv, { "GLenum"sv } }, - { "STENCIL_REF"sv, { "GLint"sv } }, - { "STENCIL_TEST"sv, { "GLboolean"sv } }, - { "STENCIL_VALUE_MASK"sv, { "GLuint"sv } }, - { "STENCIL_WRITEMASK"sv, { "GLuint"sv } }, - { "SUBPIXEL_BITS"sv, { "GLint"sv } }, - { "TEXTURE_BINDING_2D"sv, { "WebGLTexture"sv } }, - { "TEXTURE_BINDING_CUBE_MAP"sv, { "WebGLTexture"sv } }, - { "UNPACK_ALIGNMENT"sv, { "GLint"sv } }, - // FIXME: { "UNPACK_COLORSPACE_CONVERSION_WEBGL"sv, { "GLenum"sv } }, - // FIXME: { "UNPACK_FLIP_Y_WEBGL"sv, { "GLboolean"sv } }, - // FIXME: { "UNPACK_PREMULTIPLY_ALPHA_WEBGL"sv, { "GLboolean"sv } }, - { "VENDOR"sv, { "DOMString"sv } }, - { "VERSION"sv, { "DOMString"sv } }, - { "VIEWPORT"sv, { "Int32Array"sv, 4 } }, - { "MAX_SAMPLES"sv, { "GLint"sv }, 2 }, - { "MAX_3D_TEXTURE_SIZE"sv, { "GLint"sv }, 2 }, - { "MAX_ARRAY_TEXTURE_LAYERS"sv, { "GLint"sv }, 2 }, - { "MAX_COLOR_ATTACHMENTS"sv, { "GLint"sv }, 2 }, - { "MAX_VERTEX_UNIFORM_COMPONENTS"sv, { "GLint"sv }, 2 }, - { "MAX_UNIFORM_BLOCK_SIZE"sv, { "GLint64"sv }, 2 }, - { "MAX_UNIFORM_BUFFER_BINDINGS"sv, { "GLint"sv }, 2 }, - { "UNIFORM_BUFFER_OFFSET_ALIGNMENT"sv, { "GLint"sv }, 2 }, - { "MAX_DRAW_BUFFERS"sv, { "GLint"sv }, 2 }, - { "MAX_VERTEX_UNIFORM_BLOCKS"sv, { "GLint"sv }, 2 }, - { "MAX_FRAGMENT_INPUT_COMPONENTS"sv, { "GLint"sv }, 2 }, - { "MAX_FRAGMENT_UNIFORM_COMPONENTS"sv, { "GLint"sv }, 2 }, - { "MAX_COMBINED_UNIFORM_BLOCKS"sv, { "GLint"sv }, 2 }, - { "MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS"sv, { "GLint64"sv }, 2 }, - { "MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS"sv, { "GLint64"sv }, 2 }, - { "UNIFORM_BUFFER_BINDING"sv, { "WebGLBuffer"sv }, 2 }, - { "TEXTURE_BINDING_2D_ARRAY"sv, { "WebGLTexture"sv }, 2 }, - { "COPY_READ_BUFFER_BINDING"sv, { "WebGLBuffer"sv }, 2 }, - { "COPY_WRITE_BUFFER_BINDING"sv, { "WebGLBuffer"sv }, 2 }, - { "MAX_ELEMENT_INDEX"sv, { "GLint64"sv }, 2 }, - { "MAX_FRAGMENT_UNIFORM_BLOCKS"sv, { "GLint"sv }, 2 }, - { "MAX_VARYING_COMPONENTS"sv, { "GLint"sv }, 2 }, - { "MAX_ELEMENTS_INDICES"sv, { "GLint"sv }, 2 }, - { "MAX_ELEMENTS_VERTICES"sv, { "GLint"sv }, 2 }, - { "MAX_TEXTURE_LOD_BIAS"sv, { "GLfloat"sv }, 2 }, - { "MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS"sv, { "GLint"sv }, 2 }, - { "MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS"sv, { "GLint"sv }, 2 }, - { "MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS"sv, { "GLint"sv }, 2 }, - { "MIN_PROGRAM_TEXEL_OFFSET"sv, { "GLint"sv }, 2 }, - { "MAX_PROGRAM_TEXEL_OFFSET"sv, { "GLint"sv }, 2 }, - { "MAX_VERTEX_OUTPUT_COMPONENTS"sv, { "GLint"sv }, 2 }, - { "MAX_SERVER_WAIT_TIMEOUT"sv, { "GLint64"sv }, 2 }, - }; - - auto is_integer_type = [](StringView type) { - return type == "GLint"sv || type == "GLenum"sv || type == "GLuint"sv; - }; - - generator.append(" switch (pname) {"); - for (auto const& name_and_type : name_to_type) { - if (name_and_type.webgl_version.has_value() && name_and_type.webgl_version.value() != webgl_version) - continue; - - auto const& parameter_name = name_and_type.name; - auto const& type_name = name_and_type.return_type.type; - - StringBuilder string_builder; - SourceGenerator impl_generator { string_builder }; - impl_generator.set("parameter_name", parameter_name); - impl_generator.set("type_name", type_name); - impl_generator.append(R"~~~( - case GL_@parameter_name@: {)~~~"); - if (is_integer_type(type_name)) { - impl_generator.append(R"~~~( - GLint result { 0 }; - glGetIntegervRobustANGLE(GL_@parameter_name@, 1, nullptr, &result); - return JS::Value(result); -)~~~"); - } else if (type_name == "GLfloat"sv) { - impl_generator.append(R"~~~( - GLfloat result { 0.0f }; - glGetFloatvRobustANGLE(GL_@parameter_name@, 1, nullptr, &result); - return JS::Value(result); -)~~~"); - } else if (type_name == "GLboolean"sv) { - impl_generator.append(R"~~~( - GLboolean result { GL_FALSE }; - glGetBooleanvRobustANGLE(GL_@parameter_name@, 1, nullptr, &result); - return JS::Value(result == GL_TRUE); -)~~~"); - } else if (type_name == "GLint64"sv) { - impl_generator.append(R"~~~( - GLint64 result { 0 }; - glGetInteger64vRobustANGLE(GL_@parameter_name@, 1, nullptr, &result); - return JS::Value(static_cast(result)); -)~~~"); - } else if (type_name == "DOMString"sv) { - impl_generator.append(R"~~~( - auto result = reinterpret_cast(glGetString(GL_@parameter_name@)); - return JS::PrimitiveString::create(m_realm->vm(), ByteString { result });)~~~"); - } else if (type_name == "Float32Array"sv || type_name == "Int32Array"sv) { - auto element_count = name_and_type.return_type.element_count; - impl_generator.set("element_count", MUST(String::formatted("{}", element_count))); - if (type_name == "Int32Array"sv) { - impl_generator.set("gl_function_name", "glGetIntegervRobustANGLE"sv); - impl_generator.set("element_type", "GLint"sv); - } else if (type_name == "Float32Array"sv) { - impl_generator.set("gl_function_name", "glGetFloatvRobustANGLE"sv); - impl_generator.set("element_type", "GLfloat"sv); - } else { - VERIFY_NOT_REACHED(); - } - impl_generator.append(R"~~~( - Array<@element_type@, @element_count@> result; - result.fill(0); - constexpr size_t buffer_size = @element_count@ * sizeof(@element_type@); - @gl_function_name@(GL_@parameter_name@, @element_count@, nullptr, result.data()); - auto byte_buffer = MUST(ByteBuffer::copy(result.data(), buffer_size)); - auto array_buffer = JS::ArrayBuffer::create(m_realm, move(byte_buffer)); - return JS::@type_name@::create(m_realm, @element_count@, array_buffer); -)~~~"); - } else if (type_name == "WebGLProgram"sv || type_name == "WebGLBuffer"sv || type_name == "WebGLTexture"sv || type_name == "WebGLFramebuffer"sv || type_name == "WebGLRenderbuffer"sv) { - impl_generator.set("stored_name", name_and_type.name.to_ascii_lowercase_string()); - impl_generator.append(R"~~~( - if (!m_@stored_name@) - return JS::js_null(); - return JS::Value(m_@stored_name@); -)~~~"); - } else { - VERIFY_NOT_REACHED(); - } - - impl_generator.append(" }"); - - generator.append(string_builder.string_view()); - } - - generator.appendln(R"~~~( - default: - dbgln("Unknown WebGL parameter name: {:x}", pname); - set_error(GL_INVALID_ENUM); - return JS::js_null(); - })~~~"); -} - -static void generate_get_buffer_parameter(SourceGenerator& generator) -{ - Vector const name_to_type = { - { "BUFFER_SIZE"sv, { "GLint"sv } }, - { "BUFFER_USAGE"sv, { "GLenum"sv } }, - }; - - generator.append(" switch (pname) {"); - - for (auto const& name_and_type : name_to_type) { - auto const& parameter_name = name_and_type.name; - auto const& type_name = name_and_type.return_type.type; - - StringBuilder string_builder; - SourceGenerator impl_generator { string_builder }; - impl_generator.set("parameter_name", parameter_name); - impl_generator.set("type_name", type_name); - impl_generator.append(R"~~~( - case GL_@parameter_name@: { - GLint result { 0 }; - glGetBufferParameterivRobustANGLE(target, GL_@parameter_name@, 1, nullptr, &result); - return JS::Value(result); - } -)~~~"); - - generator.append(string_builder.string_view()); - } - - generator.appendln(R"~~~( - default: - dbgln("Unknown WebGL buffer parameter name: {:x}", pname); - set_error(GL_INVALID_ENUM); - return JS::js_null(); - })~~~"); -} - -static void generate_get_internal_format_parameter(SourceGenerator& generator) -{ - generator.append(R"~~~( - switch (pname) { - case GL_SAMPLES: { - GLint num_sample_counts { 0 }; - glGetInternalformativRobustANGLE(target, internalformat, GL_NUM_SAMPLE_COUNTS, 1, nullptr, &num_sample_counts); - size_t buffer_size = num_sample_counts * sizeof(GLint); - auto samples_buffer = MUST(ByteBuffer::create_zeroed(buffer_size)); - glGetInternalformativRobustANGLE(target, internalformat, GL_SAMPLES, buffer_size, nullptr, reinterpret_cast(samples_buffer.data())); - auto array_buffer = JS::ArrayBuffer::create(m_realm, move(samples_buffer)); - return JS::Int32Array::create(m_realm, num_sample_counts, array_buffer); - } - default: - dbgln("Unknown WebGL internal format parameter name: {:x}", pname); - set_error(GL_INVALID_ENUM); - return JS::js_null(); - } -)~~~"); -} - -static void generate_webgl_object_handle_unwrap(SourceGenerator& generator, StringView object_name, StringView early_return_value) -{ - StringBuilder string_builder; - SourceGenerator unwrap_generator { string_builder }; - unwrap_generator.set("object_name", object_name); - unwrap_generator.set("early_return_value", early_return_value); - unwrap_generator.append(R"~~~( - GLuint @object_name@_handle = 0; - if (@object_name@) { - auto handle_or_error = @object_name@->handle(this); - if (handle_or_error.is_error()) { - set_error(GL_INVALID_OPERATION); - return @early_return_value@; - } - @object_name@_handle = handle_or_error.release_value(); - } -)~~~"); - - generator.append(unwrap_generator.as_string_view()); -} - -static void generate_get_active_uniform_block_parameter(SourceGenerator& generator) -{ - generate_webgl_object_handle_unwrap(generator, "program"sv, "JS::js_null()"sv); - generator.append(R"~~~( - switch (pname) { - case GL_UNIFORM_BLOCK_BINDING: - case GL_UNIFORM_BLOCK_DATA_SIZE: - case GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS: { - GLint result = 0; - glGetActiveUniformBlockivRobustANGLE(program_handle, uniform_block_index, pname, 1, nullptr, &result); - return JS::Value(result); - } - case GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES: { - GLint num_active_uniforms = 0; - glGetActiveUniformBlockivRobustANGLE(program_handle, uniform_block_index, GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, sizeof(GLint), nullptr, &num_active_uniforms); - size_t buffer_size = num_active_uniforms * sizeof(GLint); - auto active_uniform_indices_buffer = MUST(ByteBuffer::create_zeroed(buffer_size)); - glGetActiveUniformBlockivRobustANGLE(program_handle, uniform_block_index, GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES, num_active_uniforms, nullptr, reinterpret_cast(active_uniform_indices_buffer.data())); - auto array_buffer = JS::ArrayBuffer::create(m_realm, move(active_uniform_indices_buffer)); - return JS::Uint32Array::create(m_realm, num_active_uniforms, array_buffer); - } - case GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER: - case GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER: { - GLint result = 0; - glGetActiveUniformBlockivRobustANGLE(program_handle, uniform_block_index, pname, 1, nullptr, &result); - return JS::Value(result == GL_TRUE); - } - default: - dbgln("Unknown WebGL active uniform block parameter name: {:x}", pname); - set_error(GL_INVALID_ENUM); - return JS::js_null(); - } -)~~~"); -} - -ErrorOr serenity_main(Main::Arguments arguments) -{ - StringView generated_header_path; - StringView generated_implementation_path; - Vector base_paths; - StringView webgl_context_idl_path; - - Core::ArgsParser args_parser; - args_parser.add_option(webgl_context_idl_path, "Path to the WebGLRenderingContext idl file", "webgl-idl-path", 'i', "webgl-idl-path"); - args_parser.add_option(Core::ArgsParser::Option { - .argument_mode = Core::ArgsParser::OptionArgumentMode::Required, - .help_string = "Path to root of IDL file tree(s)", - .long_name = "base-path", - .short_name = 'b', - .value_name = "base-path", - .accept_value = [&](StringView s) { - base_paths.append(s); - return true; - }, - }); - args_parser.add_option(generated_header_path, "Path to the header file to generate", "generated-header-path", 'h', "generated-header-path"); - args_parser.add_option(generated_implementation_path, "Path to the implementation file to generate", "generated-implementation-path", 'c', "generated-implementation-path"); - args_parser.parse(arguments); - - auto generated_header_file = TRY(Core::File::open(generated_header_path, Core::File::OpenMode::Write)); - auto generated_implementation_file = TRY(Core::File::open(generated_implementation_path, Core::File::OpenMode::Write)); - - auto idl_file = MUST(Core::File::open(webgl_context_idl_path, Core::File::OpenMode::Read)); - auto webgl_context_idl_file_content = MUST(idl_file->read_until_eof()); - - Vector import_base_paths; - for (auto const& base_path : base_paths) { - VERIFY(!base_path.is_empty()); - import_base_paths.append(base_path); - } - - IDL::Parser parser(webgl_context_idl_path, StringView(webgl_context_idl_file_content), import_base_paths); - auto const& interface = parser.parse(); - - auto path = LexicalPath(generated_header_path); - auto title = path.title(); - auto first_dot = title.find('.'); - ByteString class_name = title; - if (first_dot.has_value()) - class_name = title.substring_view(0, *first_dot); - - StringBuilder header_file_string_builder; - SourceGenerator header_file_generator { header_file_string_builder }; - header_file_generator.set("class_name", class_name); - - StringBuilder implementation_file_string_builder; - SourceGenerator implementation_file_generator { implementation_file_string_builder }; - implementation_file_generator.set("class_name", class_name); - - auto webgl_version = class_name == "WebGLRenderingContextImpl" ? 1 : 2; - if (webgl_version == 1) { - implementation_file_generator.append(R"~~~( -#define GL_GLEXT_PROTOTYPES 1 -#include -#include -extern "C" { -#include -} -)~~~"); - } else { - implementation_file_generator.append(R"~~~( -#define GL_GLEXT_PROTOTYPES 1 -#include -extern "C" { -#include -} -)~~~"); - } - - implementation_file_generator.append(R"~~~( -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -namespace Web::WebGL { - -static Vector null_terminated_string(StringView string) -{ - Vector result; - for (auto c : string.bytes()) - result.append(c); - result.append('\\0'); - return result; -} -)~~~"); - - if (webgl_version == 2) { - implementation_file_generator.append(R"~~~( -static constexpr Optional opengl_format_number_of_components(WebIDL::UnsignedLong format) -{ - switch (format) { - case GL_RED: - case GL_RED_INTEGER: - case GL_LUMINANCE: - case GL_ALPHA: - case GL_DEPTH_COMPONENT: - return 1; - case GL_RG: - case GL_RG_INTEGER: - case GL_DEPTH_STENCIL: - case GL_LUMINANCE_ALPHA: - return 2; - case GL_RGB: - case GL_RGB_INTEGER: - return 3; - case GL_RGBA: - case GL_RGBA_INTEGER: - return 4; - default: - return OptionalNone {}; - } -} - -static constexpr Optional opengl_type_size_in_bytes(WebIDL::UnsignedLong type) -{ - switch (type) { - case GL_UNSIGNED_BYTE: - case GL_BYTE: - return 1; - case GL_UNSIGNED_SHORT: - case GL_SHORT: - case GL_HALF_FLOAT: - case GL_UNSIGNED_SHORT_5_6_5: - case GL_UNSIGNED_SHORT_4_4_4_4: - case GL_UNSIGNED_SHORT_5_5_5_1: - return 2; - case GL_UNSIGNED_INT: - case GL_INT: - case GL_UNSIGNED_INT_2_10_10_10_REV: - case GL_UNSIGNED_INT_10F_11F_11F_REV: - case GL_UNSIGNED_INT_5_9_9_9_REV: - case GL_UNSIGNED_INT_24_8: - return 4; - case GL_FLOAT_32_UNSIGNED_INT_24_8_REV: - return 8; - default: - return OptionalNone {}; - } -} - -static constexpr SkColorType opengl_format_and_type_to_skia_color_type(WebIDL::UnsignedLong format, WebIDL::UnsignedLong type) -{ - switch (format) - { - case GL_RGB: - switch (type) { - case GL_UNSIGNED_BYTE: - return SkColorType::kRGB_888x_SkColorType; - case GL_UNSIGNED_SHORT_5_6_5: - return SkColorType::kRGB_565_SkColorType; - default: - break; - } - break; - case GL_RGBA: - switch (type) { - case GL_UNSIGNED_BYTE: - return SkColorType::kRGBA_8888_SkColorType; - case GL_UNSIGNED_SHORT_4_4_4_4: - // FIXME: This is not exactly the same as RGBA. - return SkColorType::kARGB_4444_SkColorType; - case GL_UNSIGNED_SHORT_5_5_5_1: - dbgln("WebGL2 FIXME: Support conversion to RGBA5551."); - break; - default: - break; - } - break; - case GL_ALPHA: - switch (type) { - case GL_UNSIGNED_BYTE: - return SkColorType::kAlpha_8_SkColorType; - default: - break; - } - break; - case GL_LUMINANCE: - switch (type) { - case GL_UNSIGNED_BYTE: - return SkColorType::kGray_8_SkColorType; - default: - break; - } - break; - default: - break; - } - - dbgln("WebGL2: Unsupported format and type combination. format: 0x{:04x}, type: 0x{:04x}", format, type); - return SkColorType::kUnknown_SkColorType; -} -)~~~"); - } else { - implementation_file_generator.append(R"~~~( -static constexpr Optional opengl_format_number_of_components(WebIDL::UnsignedLong format) -{ - switch (format) { - case GL_LUMINANCE: - case GL_ALPHA: - return 1; - case GL_LUMINANCE_ALPHA: - return 2; - case GL_RGB: - return 3; - case GL_RGBA: - return 4; - default: - return OptionalNone {}; - } -} - -static constexpr Optional opengl_type_size_in_bytes(WebIDL::UnsignedLong type) -{ - switch (type) { - case GL_UNSIGNED_BYTE: - return 1; - case GL_UNSIGNED_SHORT_5_6_5: - case GL_UNSIGNED_SHORT_4_4_4_4: - case GL_UNSIGNED_SHORT_5_5_5_1: - return 2; - default: - return OptionalNone {}; - } -} - -static constexpr SkColorType opengl_format_and_type_to_skia_color_type(WebIDL::UnsignedLong format, WebIDL::UnsignedLong type) -{ - switch (format) - { - case GL_RGB: - switch (type) { - case GL_UNSIGNED_BYTE: - return SkColorType::kRGB_888x_SkColorType; - case GL_UNSIGNED_SHORT_5_6_5: - return SkColorType::kRGB_565_SkColorType; - default: - break; - } - break; - case GL_RGBA: - switch (type) { - case GL_UNSIGNED_BYTE: - return SkColorType::kRGBA_8888_SkColorType; - case GL_UNSIGNED_SHORT_4_4_4_4: - // FIXME: This is not exactly the same as RGBA. - return SkColorType::kARGB_4444_SkColorType; - case GL_UNSIGNED_SHORT_5_5_5_1: - dbgln("WebGL FIXME: Support conversion to RGBA5551."); - break; - default: - break; - } - break; - case GL_ALPHA: - switch (type) { - case GL_UNSIGNED_BYTE: - return SkColorType::kAlpha_8_SkColorType; - default: - break; - } - break; - case GL_LUMINANCE: - switch (type) { - case GL_UNSIGNED_BYTE: - return SkColorType::kGray_8_SkColorType; - default: - break; - } - break; - default: - break; - } - - dbgln("WebGL: Unsupported format and type combination. format: 0x{:04x}, type: 0x{:04x}", format, type); - return SkColorType::kUnknown_SkColorType; -} -)~~~"); - } - - implementation_file_generator.append(R"~~~( -struct ConvertedTexture { - ByteBuffer buffer; - int width { 0 }; - int height { 0 }; -}; - -static Optional read_and_pixel_convert_texture_image_source(TexImageSource const& source, WebIDL::UnsignedLong format, WebIDL::UnsignedLong type, Optional destination_width = OptionalNone {}, Optional destination_height = OptionalNone {}) -{ - // FIXME: If this function is called with an ImageData whose data attribute has been neutered, - // an INVALID_VALUE error is generated. - // FIXME: If this function is called with an ImageBitmap that has been neutered, an INVALID_VALUE - // error is generated. - // FIXME: If this function is called with an HTMLImageElement or HTMLVideoElement whose origin - // differs from the origin of the containing Document, or with an HTMLCanvasElement, - // ImageBitmap or OffscreenCanvas whose bitmap's origin-clean flag is set to false, - // a SECURITY_ERR exception must be thrown. See Origin Restrictions. - // FIXME: If source is null then an INVALID_VALUE error is generated. - auto bitmap = source.visit( - [](GC::Root const& source) -> RefPtr { - return source->immutable_bitmap(); - }, - [](GC::Root const& source) -> RefPtr { - auto surface = source->surface(); - if (!surface) - return {}; - auto bitmap = MUST(Gfx::Bitmap::create(Gfx::BitmapFormat::RGBA8888, Gfx::AlphaType::Premultiplied, surface->size())); - surface->read_into_bitmap(*bitmap); - return Gfx::ImmutableBitmap::create(*bitmap); - }, - [](GC::Root const& source) -> RefPtr { - return Gfx::ImmutableBitmap::create(*source->bitmap()); - }, - [](GC::Root const& source) -> RefPtr { - return Gfx::ImmutableBitmap::create(*source->bitmap()); - }, - [](GC::Root const& source) -> RefPtr { - return Gfx::ImmutableBitmap::create(*source->bitmap()); - }, - [](GC::Root const& source) -> RefPtr { - return Gfx::ImmutableBitmap::create(source->bitmap()); - }); - if (!bitmap) - return OptionalNone {}; - - int width = destination_width.value_or(bitmap->width()); - int height = destination_height.value_or(bitmap->height()); - - Checked buffer_pitch = width; - - auto number_of_components = opengl_format_number_of_components(format); - if (!number_of_components.has_value()) - return OptionalNone {}; - - buffer_pitch *= number_of_components.value(); - - auto type_size = opengl_type_size_in_bytes(type); - if (!type_size.has_value()) - return OptionalNone {}; - - buffer_pitch *= type_size.value(); - - if (buffer_pitch.has_overflow()) - return OptionalNone {}; - - if (Checked::multiplication_would_overflow(buffer_pitch.value(), height)) - return OptionalNone {}; - - auto buffer = MUST(ByteBuffer::create_zeroed(buffer_pitch.value() * height)); - - auto skia_format = opengl_format_and_type_to_skia_color_type(format, type); - - // FIXME: Respect UNPACK_PREMULTIPLY_ALPHA_WEBGL - // FIXME: Respect unpackColorSpace - auto color_space = SkColorSpace::MakeSRGB(); - auto image_info = SkImageInfo::Make(width, height, skia_format, SkAlphaType::kPremul_SkAlphaType, color_space); - SkPixmap const pixmap(image_info, buffer.data(), buffer_pitch.value()); - bitmap->sk_image()->readPixels(pixmap, 0, 0); - return ConvertedTexture { - .buffer = move(buffer), - .width = width, - .height = height, - }; -} - -@class_name@::@class_name@(JS::Realm& realm, NonnullOwnPtr context) - : m_realm(realm) - , m_context(move(context)) -{ -} - -)~~~"); - - header_file_generator.append(R"~~~( -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -namespace Web::WebGL { - -using namespace Web::HTML; - -class @class_name@ : public WebGLRenderingContextBase { -public: - @class_name@(JS::Realm&, NonnullOwnPtr); - - virtual OpenGLContext& context() override { return *m_context; } - - virtual void present() = 0; - virtual void needs_to_present() = 0; - virtual void set_error(GLenum) = 0; -)~~~"); - - for (auto const& function : interface.functions) { - if (function.extended_attributes.contains("FIXME")) { - continue; - } - - if (function.name == "getSupportedExtensions"sv || function.name == "getExtension"sv || function.name == "getContextAttributes"sv || function.name == "isContextLost"sv) { - // Implemented in WebGLRenderingContext - continue; - } - - StringBuilder function_declaration; - - StringBuilder function_parameters; - for (size_t i = 0; i < function.parameters.size(); ++i) { - auto const& parameter = function.parameters[i]; - function_parameters.append(to_cpp_type(*parameter.type, interface)); - function_parameters.append(" "sv); - function_parameters.append(parameter.name.to_snakecase()); - if (i != function.parameters.size() - 1) { - function_parameters.append(", "sv); - } - } - - auto function_name = function.name.to_snakecase(); - function_declaration.append(to_cpp_type(*function.return_type, interface)); - function_declaration.append(" "sv); - function_declaration.append(function_name); - function_declaration.append("("sv); - - function_declaration.append(function_parameters.string_view()); - function_declaration.append(");"sv); - - header_file_generator.append(" "sv); - header_file_generator.append(function_declaration.string_view()); - header_file_generator.append("\n"sv); - - StringBuilder function_impl; - SourceGenerator function_impl_generator { function_impl }; - function_impl_generator.set("class_name", class_name); - - ScopeGuard function_guard { [&] { - function_impl_generator.append("}\n"sv); - implementation_file_generator.append(function_impl_generator.as_string_view().bytes()); - } }; - - function_impl_generator.set("function_name", function_name); - function_impl_generator.set("function_parameters", function_parameters.string_view()); - function_impl_generator.set("function_return_type", to_cpp_type(*function.return_type, interface)); - function_impl_generator.append(R"~~~( -@function_return_type@ @class_name@::@function_name@(@function_parameters@) -{ - m_context->make_current(); -)~~~"); - - if (gl_function_modifies_framebuffer(function.name)) { - function_impl_generator.append(" m_context->notify_content_will_change();\n"sv); - } - - if (function.name == "attachShader"sv) { - generate_webgl_object_handle_unwrap(function_impl_generator, "program"sv, ""sv); - generate_webgl_object_handle_unwrap(function_impl_generator, "shader"sv, ""sv); - function_impl_generator.append(R"~~~( - if (program->attached_vertex_shader() == shader || program->attached_fragment_shader() == shader) { - dbgln("WebGL: Shader is already attached to program"); - set_error(GL_INVALID_OPERATION); - return; - } - - if (shader->type() == GL_VERTEX_SHADER && program->attached_vertex_shader()) { - dbgln("WebGL: Not attaching vertex shader to program as it already has a vertex shader attached"); - set_error(GL_INVALID_OPERATION); - return; - } - - if (shader->type() == GL_FRAGMENT_SHADER && program->attached_fragment_shader()) { - dbgln("WebGL: Not attaching fragment shader to program as it already has a fragment shader attached"); - set_error(GL_INVALID_OPERATION); - return; - } - - glAttachShader(program_handle, shader_handle); - - switch (shader->type()) { - case GL_VERTEX_SHADER: - program->set_attached_vertex_shader(shader.ptr()); - break; - case GL_FRAGMENT_SHADER: - program->set_attached_fragment_shader(shader.ptr()); - break; - default: - VERIFY_NOT_REACHED(); - } -)~~~"); - continue; - } - - if (function.name == "getUniformLocation"sv) { - generate_webgl_object_handle_unwrap(function_impl_generator, "program"sv, "{}"sv); - function_impl_generator.append(R"~~~( - auto name_null_terminated = null_terminated_string(name); - return WebGLUniformLocation::create(m_realm, glGetUniformLocation(program_handle, name_null_terminated.data())); -)~~~"); - continue; - } - - if (function.name == "createBuffer"sv) { - function_impl_generator.append(R"~~~( - GLuint handle = 0; - glGenBuffers(1, &handle); - return WebGLBuffer::create(m_realm, *this, handle); -)~~~"); - continue; - } - - if (function.name == "createShader"sv) { - function_impl_generator.append(R"~~~( - if (type != GL_VERTEX_SHADER && type != GL_FRAGMENT_SHADER) { - dbgln("Unknown WebGL shader type: 0x{:04x}", type); - set_error(GL_INVALID_ENUM); - return nullptr; - } - - GLuint handle = glCreateShader(type); - return WebGLShader::create(m_realm, *this, handle, type); -)~~~"); - continue; - } - - if (function.name == "createTexture"sv) { - function_impl_generator.append(R"~~~( - GLuint handle = 0; - glGenTextures(1, &handle); - return WebGLTexture::create(m_realm, *this, handle); -)~~~"); - continue; - } - - if (function.name == "createFramebuffer"sv) { - function_impl_generator.append(R"~~~( - GLuint handle = 0; - glGenFramebuffers(1, &handle); - return WebGLFramebuffer::create(m_realm, *this, handle); -)~~~"); - continue; - } - - if (function.name == "createRenderbuffer"sv) { - function_impl_generator.append(R"~~~( - GLuint handle = 0; - glGenRenderbuffers(1, &handle); - return WebGLRenderbuffer::create(m_realm, *this, handle); -)~~~"); - continue; - } - - if (function.name == "invalidateFramebuffer"sv) { - function_impl_generator.append(R"~~~( - glInvalidateFramebuffer(target, attachments.size(), attachments.data()); - needs_to_present(); -)~~~"); - continue; - } - - if (function.name == "createVertexArray"sv) { - function_impl_generator.append(R"~~~( - GLuint handle = 0; - glGenVertexArrays(1, &handle); - return WebGLVertexArrayObject::create(m_realm, *this, handle); -)~~~"); - continue; - } - - if (function.name == "createSampler"sv) { - function_impl_generator.append(R"~~~( - GLuint handle = 0; - glGenSamplers(1, &handle); - return WebGLSampler::create(m_realm, *this, handle); -)~~~"); - continue; - } - - if (function.name == "fenceSync"sv) { - function_impl_generator.append(R"~~~( - GLsync handle = glFenceSync(condition, flags); - return WebGLSync::create(m_realm, *this, handle); -)~~~"); - continue; - } - - if (function.name == "shaderSource"sv) { - generate_webgl_object_handle_unwrap(function_impl_generator, "shader"sv, ""sv); - function_impl_generator.append(R"~~~( - Vector strings; - auto string = null_terminated_string(source); - strings.append(string.data()); - Vector length; - length.append(source.bytes().size()); - glShaderSource(shader_handle, 1, strings.data(), length.data()); -)~~~"); - continue; - } - - if (function.name == "vertexAttribPointer"sv) { - function_impl_generator.append(R"~~~( - glVertexAttribPointer(index, size, type, normalized, stride, reinterpret_cast(offset)); -)~~~"); - continue; - } - - if (function.name == "texStorage2D") { - function_impl_generator.append(R"~~~( - glTexStorage2D(target, levels, internalformat, width, height); -)~~~"); - continue; - } - - if (function.name == "texImage2D"sv && function.overload_index == 0) { - function_impl_generator.append(R"~~~( - void const* pixels_ptr = nullptr; - size_t buffer_size = 0; - if (pixels) { - auto const& viewed_array_buffer = pixels->viewed_array_buffer(); - auto const& byte_buffer = viewed_array_buffer->buffer(); - pixels_ptr = byte_buffer.data() + pixels->byte_offset(); - buffer_size = pixels->byte_length(); - } - glTexImage2DRobustANGLE(target, level, internalformat, width, height, border, format, type, buffer_size, pixels_ptr); -)~~~"); - continue; - } - - if (function.name == "texImage3D"sv && function.overload_index == 0) { - // FIXME: If a WebGLBuffer is bound to the PIXEL_UNPACK_BUFFER target, generates an INVALID_OPERATION error. - // FIXME: If srcData is null, a buffer of sufficient size initialized to 0 is passed. - // FIXME: If type is specified as FLOAT_32_UNSIGNED_INT_24_8_REV, srcData must be null; otherwise, generates an INVALID_OPERATION error. - // FIXME: If srcData is non-null, the type of srcData must match the type according to the above table; otherwise, generate an INVALID_OPERATION error. - // FIXME: If an attempt is made to call this function with no WebGLTexture bound (see above), generates an INVALID_OPERATION error. - // If there's not enough data in srcData starting at srcOffset, generate INVALID_OPERATION. - // NOTE: This is done by using the RobustANGLE version of the API call. - function_impl_generator.append(R"~~~( - void const* src_data_ptr = nullptr; - size_t buffer_size = 0; - if (src_data) { - auto const& viewed_array_buffer = src_data->viewed_array_buffer(); - auto const& byte_buffer = viewed_array_buffer->buffer(); - src_data_ptr = byte_buffer.data() + src_data->byte_offset(); - buffer_size = src_data->byte_length(); - } - glTexImage3DRobustANGLE(target, level, internalformat, width, height, depth, border, format, type, buffer_size, src_data_ptr); -)~~~"); - continue; - } - - if (function.name == "texImage3D"sv && function.overload_index == 1) { - // FIXME: If a WebGLBuffer is bound to the PIXEL_UNPACK_BUFFER target, generates an INVALID_OPERATION error. - // FIXME: If srcData is null, a buffer of sufficient size initialized to 0 is passed. - // FIXME: If type is specified as FLOAT_32_UNSIGNED_INT_24_8_REV, srcData must be null; otherwise, generates an INVALID_OPERATION error. - // FIXME: If srcData is non-null, the type of srcData must match the type according to the above table; otherwise, generate an INVALID_OPERATION error. - // FIXME: If an attempt is made to call this function with no WebGLTexture bound (see above), generates an INVALID_OPERATION error. - // - If there's not enough data in srcData starting at srcOffset, generate INVALID_OPERATION. - // NOTE: This is done by using the RobustANGLE version of the API call. - function_impl_generator.append(R"~~~( - void const* src_data_ptr = nullptr; - size_t buffer_size = 0; - if (src_data) { - auto const& viewed_array_buffer = src_data->viewed_array_buffer(); - auto const& byte_buffer = viewed_array_buffer->buffer(); - src_data_ptr = byte_buffer.data() + src_offset; - buffer_size = src_data->byte_length(); - } - glTexImage3DRobustANGLE(target, level, internalformat, width, height, depth, border, format, type, buffer_size, src_data_ptr); -)~~~"); - continue; - } - - if (function.name == "texImage2D"sv && (function.overload_index == 1 || (webgl_version == 2 && function.overload_index == 2))) { - if (webgl_version == 2 && function.overload_index == 2) { - function_impl_generator.append(R"~~~( - auto maybe_converted_texture = read_and_pixel_convert_texture_image_source(source, format, type, width, height); - if (!maybe_converted_texture.has_value()) - return; - auto converted_texture = maybe_converted_texture.release_value(); - glTexImage2DRobustANGLE(target, level, internalformat, converted_texture.width, converted_texture.height, border, format, type, converted_texture.buffer.size(), converted_texture.buffer.data()); -)~~~"); - } else { - function_impl_generator.append(R"~~~( - auto maybe_converted_texture = read_and_pixel_convert_texture_image_source(source, format, type); - if (!maybe_converted_texture.has_value()) - return; - auto converted_texture = maybe_converted_texture.release_value(); - glTexImage2DRobustANGLE(target, level, internalformat, converted_texture.width, converted_texture.height, 0, format, type, converted_texture.buffer.size(), converted_texture.buffer.data()); -)~~~"); - } - continue; - } - - if (webgl_version == 2 && function.name == "texImage2D"sv && function.overload_index == 3) { - function_impl_generator.append(R"~~~( - void const* pixels_ptr = nullptr; - size_t buffer_size = 0; - if (src_data) { - auto const& viewed_array_buffer = src_data->viewed_array_buffer(); - auto const& byte_buffer = viewed_array_buffer->buffer(); - pixels_ptr = byte_buffer.data() + src_offset; - buffer_size = src_data->byte_length(); - } - glTexImage2DRobustANGLE(target, level, internalformat, width, height, border, format, type, buffer_size, pixels_ptr); -)~~~"); - continue; - } - - if (function.name == "texSubImage2D"sv && function.overload_index == 0) { - function_impl_generator.append(R"~~~( - void const* pixels_ptr = nullptr; - size_t buffer_size = 0; - if (pixels) { - auto const& viewed_array_buffer = pixels->viewed_array_buffer(); - auto const& byte_buffer = viewed_array_buffer->buffer(); - pixels_ptr = byte_buffer.data() + pixels->byte_offset(); - buffer_size = pixels->byte_length(); - } - glTexSubImage2DRobustANGLE(target, level, xoffset, yoffset, width, height, format, type, buffer_size, pixels_ptr); -)~~~"); - continue; - } - - if (function.name == "texSubImage2D" && (function.overload_index == 1 || (webgl_version == 2 && function.overload_index == 2))) { - if (webgl_version == 2 && function.overload_index == 2) { - function_impl_generator.append(R"~~~( - auto maybe_converted_texture = read_and_pixel_convert_texture_image_source(source, format, type, width, height); -)~~~"); - } else { - function_impl_generator.append(R"~~~( - auto maybe_converted_texture = read_and_pixel_convert_texture_image_source(source, format, type); -)~~~"); - } - - function_impl_generator.append(R"~~~( - if (!maybe_converted_texture.has_value()) - return; - auto converted_texture = maybe_converted_texture.release_value(); - glTexSubImage2DRobustANGLE(target, level, xoffset, yoffset, converted_texture.width, converted_texture.height, format, type, converted_texture.buffer.size(), converted_texture.buffer.data()); -)~~~"); - continue; - } - - if (webgl_version == 2 && function.name == "texSubImage2D"sv && function.overload_index == 3) { - function_impl_generator.append(R"~~~( - void const* pixels_ptr = nullptr; - size_t buffer_size = 0; - if (src_data) { - auto const& viewed_array_buffer = src_data->viewed_array_buffer(); - auto const& byte_buffer = viewed_array_buffer->buffer(); - pixels_ptr = byte_buffer.data() + src_data->byte_offset() + src_offset; - buffer_size = src_data->byte_length(); - } - glTexSubImage2DRobustANGLE(target, level, xoffset, yoffset, width, height, format, type, buffer_size, pixels_ptr); -)~~~"); - continue; - } - - if (function.name == "texSubImage3D"sv && function.overload_index == 0) { - function_impl_generator.append(R"~~~( - void const* pixels_ptr = nullptr; - size_t buffer_size = 0; - if (src_data) { - auto const& viewed_array_buffer = src_data->viewed_array_buffer(); - auto const& byte_buffer = viewed_array_buffer->buffer(); - pixels_ptr = byte_buffer.data() + src_offset; - buffer_size = src_data->byte_length(); - } - glTexSubImage3DRobustANGLE(target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, buffer_size, pixels_ptr); -)~~~"); - continue; - } - - if (webgl_version == 1 && function.name == "compressedTexImage2D"sv) { - function_impl_generator.append(R"~~~( - void const* ptr = data->viewed_array_buffer()->buffer().data() + data->byte_offset(); - size_t byte_size = data->byte_length(); - glCompressedTexImage2DRobustANGLE(target, level, internalformat, width, height, border, byte_size, byte_size, ptr); -)~~~"); - continue; - } - - if (webgl_version == 1 && function.name == "compressedTexSubImage2D"sv) { - function_impl_generator.append(R"~~~( - void const* ptr = data->viewed_array_buffer()->buffer().data() + data->byte_offset(); - size_t byte_size = data->byte_length(); - glCompressedTexSubImage2DRobustANGLE(target, level, xoffset, yoffset, width, height, format, byte_size, byte_size, ptr); -)~~~"); - continue; - } - - if (webgl_version == 2 && function.name == "compressedTexImage2D"sv && function.overload_index == 0) { - function_impl_generator.append(R"~~~( - u8 const* pixels_ptr = src_data->viewed_array_buffer()->buffer().data(); - size_t count = src_data->byte_length(); - auto src_data_element_size = src_data->element_size(); - - if ((src_offset * src_data_element_size) + src_length_override > count) { - set_error(GL_INVALID_VALUE); - return; - } - - pixels_ptr += src_data->byte_offset(); - pixels_ptr += src_offset * src_data_element_size; - if (src_length_override == 0) { - count -= src_offset; - } else { - count = src_length_override; - } - - glCompressedTexImage2DRobustANGLE(target, level, internalformat, width, height, border, count, src_data->byte_length(), pixels_ptr); -)~~~"); - continue; - } - - if (webgl_version == 2 && function.name == "compressedTexSubImage2D"sv && function.overload_index == 0) { - function_impl_generator.append(R"~~~( - u8 const* pixels_ptr = src_data->viewed_array_buffer()->buffer().data(); - size_t count = src_data->byte_length(); - auto src_data_element_size = src_data->element_size(); - - if ((src_offset * src_data_element_size) + src_length_override > count) { - set_error(GL_INVALID_VALUE); - return; - } - - pixels_ptr += src_data->byte_offset(); - pixels_ptr += src_offset * src_data_element_size; - if (src_length_override == 0) { - count -= src_offset; - } else { - count = src_length_override; - } - - glCompressedTexSubImage2DRobustANGLE(target, level, xoffset, yoffset, width, height, format, count, src_data->byte_length(), pixels_ptr); -)~~~"); - continue; - } - - if (function.name == "getShaderParameter"sv) { - generate_webgl_object_handle_unwrap(function_impl_generator, "shader"sv, "JS::js_null()"sv); - function_impl_generator.append(R"~~~( - GLint result = 0; - glGetShaderivRobustANGLE(shader_handle, pname, 1, nullptr, &result); - switch (pname) { - case GL_SHADER_TYPE: - return JS::Value(result); - case GL_DELETE_STATUS: - case GL_COMPILE_STATUS: - return JS::Value(result == GL_TRUE); - default: - dbgln("Unknown WebGL shader parameter name: 0x{:04x}", pname); - set_error(GL_INVALID_ENUM); - return JS::js_null(); - } -)~~~"); - continue; - } - - if (function.name == "getProgramParameter"sv) { - generate_webgl_object_handle_unwrap(function_impl_generator, "program"sv, "JS::js_null()"sv); - function_impl_generator.append(R"~~~( - GLint result = 0; - glGetProgramivRobustANGLE(program_handle, pname, 1, nullptr, &result); - switch (pname) { - case GL_ATTACHED_SHADERS: - case GL_ACTIVE_ATTRIBUTES: - case GL_ACTIVE_UNIFORMS: -)~~~"); - - if (webgl_version == 2) { - function_impl_generator.append(R"~~~( - case GL_TRANSFORM_FEEDBACK_BUFFER_MODE: - case GL_TRANSFORM_FEEDBACK_VARYINGS: - case GL_ACTIVE_UNIFORM_BLOCKS: -)~~~"); - } - - function_impl_generator.append(R"~~~( - return JS::Value(result); - case GL_DELETE_STATUS: - case GL_LINK_STATUS: - case GL_VALIDATE_STATUS: - return JS::Value(result == GL_TRUE); - default: - dbgln("Unknown WebGL program parameter name: 0x{:04x}", pname); - set_error(GL_INVALID_ENUM); - return JS::js_null(); - } -)~~~"); - continue; - } - - if (function.name == "getActiveUniformBlockParameter"sv) { - generate_get_active_uniform_block_parameter(function_impl_generator); - continue; - } - - if (function.name == "getSyncParameter"sv) { - // FIXME: In order to ensure consistent behavior across platforms, sync objects may only transition to the - // signaled state when the user agent's event loop is not executing a task. In other words: - // - A sync object must not become signaled until control has returned to the user agent's main - // loop. - // - Repeatedly fetching a sync object's SYNC_STATUS parameter in a loop, without returning - // control to the user agent, must always return the same value. - // FIXME: Remove the GLsync cast once sync_handle actually returns the proper GLsync type. - function_impl_generator.append(R"~~~( - GLint result = 0; - glGetSynciv((GLsync)(sync ? sync->sync_handle() : nullptr), pname, 1, nullptr, &result); - return JS::Value(result); -)~~~"); - continue; - } - - if (function.name == "getAttachedShaders"sv) { - generate_webgl_object_handle_unwrap(function_impl_generator, "program"sv, "OptionalNone {}"sv); - function_impl_generator.append(R"~~~( - (void)program_handle; - - Vector> result; - - if (program->attached_vertex_shader()) - result.append(GC::make_root(*program->attached_vertex_shader())); - - if (program->attached_fragment_shader()) - result.append(GC::make_root(*program->attached_fragment_shader())); - - return result; -)~~~"); - continue; - } - - if (function.name == "bufferData"sv && function.overload_index == 0) { - function_impl_generator.append(R"~~~( - glBufferData(target, size, 0, usage); -)~~~"); - continue; - } - - if (webgl_version == 2 && function.name == "bufferData"sv && function.overload_index == 2) { - function_impl_generator.append(R"~~~( - VERIFY(src_data); - auto const& viewed_array_buffer = src_data->viewed_array_buffer(); - auto const& byte_buffer = viewed_array_buffer->buffer(); - auto src_data_length = src_data->byte_length(); - auto src_data_element_size = src_data->element_size(); - u8 const* buffer_ptr = byte_buffer.data(); - - u64 copy_length = length == 0 ? src_data_length - src_offset : length; - copy_length *= src_data_element_size; - - if (src_offset > src_data_length) { - set_error(GL_INVALID_VALUE); - return; - } - - if (src_offset + copy_length > src_data_length) { - set_error(GL_INVALID_VALUE); - return; - } - - buffer_ptr += src_offset * src_data_element_size; - glBufferData(target, copy_length, buffer_ptr, usage); -)~~~"); - continue; - } - - if (webgl_version == 2 && function.name == "bufferSubData"sv && function.overload_index == 1) { - function_impl_generator.append(R"~~~( - VERIFY(src_data); - auto const& viewed_array_buffer = src_data->viewed_array_buffer(); - auto const& byte_buffer = viewed_array_buffer->buffer(); - auto src_data_length = src_data->byte_length(); - auto src_data_element_size = src_data->element_size(); - u8 const* buffer_ptr = byte_buffer.data(); - - u64 copy_length = length == 0 ? src_data_length - src_offset : length; - copy_length *= src_data_element_size; - - if (src_offset > src_data_length) { - set_error(GL_INVALID_VALUE); - return; - } - - if (src_offset + copy_length > src_data_length) { - set_error(GL_INVALID_VALUE); - return; - } - - buffer_ptr += src_offset * src_data_element_size; - glBufferSubData(target, dst_byte_offset, copy_length, buffer_ptr); -)~~~"); - continue; - } - - if (function.name == "readPixels"sv) { - function_impl_generator.append(R"~~~( - if (!pixels) { - return; - } - - void *ptr = pixels->viewed_array_buffer()->buffer().data() + pixels->byte_offset(); - glReadPixelsRobustANGLE(x, y, width, height, format, type, pixels->byte_length(), nullptr, nullptr, nullptr, ptr); -)~~~"); - continue; - } - - if (function.name == "drawElements"sv) { - function_impl_generator.append(R"~~~( - glDrawElements(mode, count, type, reinterpret_cast(offset)); - needs_to_present(); -)~~~"); - continue; - } - - if (function.name == "drawElementsInstanced"sv) { - function_impl_generator.append(R"~~~( - glDrawElementsInstanced(mode, count, type, reinterpret_cast(offset), instance_count); - needs_to_present(); -)~~~"); - continue; - } - - if (function.name == "drawBuffers"sv) { - function_impl_generator.append(R"~~~( - glDrawBuffers(buffers.size(), buffers.data()); -)~~~"); - continue; - } - - if (function.name.starts_with("uniformMatrix"sv)) { - auto number_of_matrix_elements = function.name.substring_view(13, 1); - function_impl_generator.set("number_of_matrix_elements", number_of_matrix_elements); - - if (webgl_version == 1) { - function_impl_generator.set("array_argument_name", "value"); - } else { - function_impl_generator.set("array_argument_name", "data"); - } - - // "If the passed location is null, the data passed in will be silently ignored and no uniform variables will be changed." - function_impl_generator.append(R"~~~( - if (!location) - return; - - auto matrix_size = @number_of_matrix_elements@ * @number_of_matrix_elements@; - float const* raw_data = nullptr; - u64 count = 0; - if (@array_argument_name@.has>()) { - auto& vector_data = @array_argument_name@.get>(); - raw_data = vector_data.data(); - count = vector_data.size() / matrix_size; - } else { - auto& typed_array_base = static_cast(*@array_argument_name@.get>()->raw_object()); - auto& float32_array = as(typed_array_base); - raw_data = float32_array.data().data(); - count = float32_array.array_length().length() / matrix_size; - } -)~~~"); - - if (webgl_version == 2) { - function_impl_generator.append(R"~~~( - if (src_offset + src_length > (count * matrix_size)) { - set_error(GL_INVALID_VALUE); - return; - } - - raw_data += src_offset; - if (src_length == 0) { - count -= src_offset; - } else { - count = src_length; - } -)~~~"); - } - - function_impl_generator.append(R"~~~( - glUniformMatrix@number_of_matrix_elements@fv(location->handle(), count, transpose, raw_data); -)~~~"); - continue; - } - - if (function.name == "uniform1fv"sv || function.name == "uniform2fv"sv || function.name == "uniform3fv"sv || function.name == "uniform4fv"sv || function.name == "uniform1iv"sv || function.name == "uniform2iv"sv || function.name == "uniform3iv"sv || function.name == "uniform4iv"sv) { - auto number_of_vector_elements = function.name.substring_view(7, 1); - auto element_type = function.name.substring_view(8, 1); - if (element_type == "f"sv) { - function_impl_generator.set("cpp_element_type", "float"sv); - function_impl_generator.set("typed_array_type", "Float32Array"sv); - function_impl_generator.set("gl_postfix", "f"sv); - } else if (element_type == "i"sv) { - function_impl_generator.set("cpp_element_type", "int"sv); - function_impl_generator.set("typed_array_type", "Int32Array"sv); - function_impl_generator.set("gl_postfix", "i"sv); - } else { - VERIFY_NOT_REACHED(); - } - function_impl_generator.set("number_of_vector_elements", number_of_vector_elements); - - // "If the passed location is null, the data passed in will be silently ignored and no uniform variables will be changed." - function_impl_generator.append(R"~~~( - if (!location) - return; - - @cpp_element_type@ const* data = nullptr; - size_t count = 0; - if (v.has>()) { - auto& vector = v.get>(); - data = vector.data(); - count = vector.size(); - } else if (v.has>()) { - auto& typed_array_base = static_cast(*v.get>()->raw_object()); - auto& typed_array = as(typed_array_base); - data = typed_array.data().data(); - count = typed_array.array_length().length(); - } else { - VERIFY_NOT_REACHED(); - } -)~~~"); - - if (webgl_version == 2) { - function_impl_generator.append(R"~~~( - if (src_offset + src_length > count) { - set_error(GL_INVALID_VALUE); - return; - } - - data += src_offset; - if (src_length == 0) { - count -= src_offset; - } else { - count = src_length; - } -)~~~"); - } - - function_impl_generator.append(R"~~~( - glUniform@number_of_vector_elements@@gl_postfix@v(location->handle(), count / @number_of_vector_elements@, data); -)~~~"); - continue; - } - - if (function.name == "vertexAttrib1fv"sv || function.name == "vertexAttrib2fv"sv || function.name == "vertexAttrib3fv"sv || function.name == "vertexAttrib4fv"sv) { - // If the array passed to any of the vector forms (those ending in v) is too short, an INVALID_VALUE error will be generated. - auto number_of_vector_elements = function.name.substring_view(12, 1); - function_impl_generator.set("number_of_vector_elements", number_of_vector_elements); - function_impl_generator.append(R"~~~( - if (values.has>()) { - auto& data = values.get>(); - if (data.size() < @number_of_vector_elements@) { - set_error(GL_INVALID_VALUE); - return; - } - - glVertexAttrib@number_of_vector_elements@fv(index, data.data()); - return; - } - - auto& buffer_source = values.get>(); - if (buffer_source->byte_length() < @number_of_vector_elements@ * sizeof(float)) { - set_error(GL_INVALID_VALUE); - return; - } - - auto& typed_array_base = static_cast(*buffer_source->raw_object()); - auto& float32_array = as(typed_array_base); - float const* data = float32_array.data().data(); - glVertexAttrib@number_of_vector_elements@fv(index, data); -)~~~"); - continue; - } - - if (function.name == "vertexAttribIPointer"sv) { - function_impl_generator.append(R"~~~( - glVertexAttribIPointer(index, size, type, stride, reinterpret_cast(offset)); -)~~~"); - continue; - } - - if (function.name == "getParameter"sv) { - generate_get_parameter(function_impl_generator, webgl_version); - continue; - } - - if (function.name == "getBufferParameter"sv) { - generate_get_buffer_parameter(function_impl_generator); - continue; - } - - if (function.name == "getInternalformatParameter") { - generate_get_internal_format_parameter(function_impl_generator); - continue; - } - - if (function.name == "getActiveUniform"sv) { - generate_webgl_object_handle_unwrap(function_impl_generator, "program"sv, "{}"sv); - function_impl_generator.append(R"~~~( - GLint size = 0; - GLenum type = 0; - GLsizei buf_size = 256; - GLsizei length = 0; - GLchar name[256]; - glGetActiveUniform(program_handle, index, buf_size, &length, &size, &type, name); - auto readonly_bytes = ReadonlyBytes { name, static_cast(length) }; - return WebGLActiveInfo::create(m_realm, String::from_utf8_without_validation(readonly_bytes), type, size); -)~~~"); - continue; - } - - if (function.name == "getActiveUniforms"sv) { - generate_webgl_object_handle_unwrap(function_impl_generator, "program"sv, "{}"sv); - function_impl_generator.append(R"~~~( - auto params = MUST(ByteBuffer::create_zeroed(uniform_indices.size() * sizeof(GLint))); - Span params_span(reinterpret_cast(params.data()), uniform_indices.size()); - glGetActiveUniformsiv(program_handle, uniform_indices.size(), uniform_indices.data(), pname, params_span.data()); - - Vector params_as_values; - params_as_values.ensure_capacity(params.size()); - for (GLint param : params_span) { - switch (pname) { - case GL_UNIFORM_TYPE: - params_as_values.unchecked_append(JS::Value(static_cast(param))); - break; - case GL_UNIFORM_SIZE: - params_as_values.unchecked_append(JS::Value(static_cast(param))); - break; - case GL_UNIFORM_BLOCK_INDEX: - case GL_UNIFORM_OFFSET: - case GL_UNIFORM_ARRAY_STRIDE: - case GL_UNIFORM_MATRIX_STRIDE: - params_as_values.unchecked_append(JS::Value(param)); - break; - case GL_UNIFORM_IS_ROW_MAJOR: - params_as_values.unchecked_append(JS::Value(param == GL_TRUE)); - break; - default: - dbgln("Unknown WebGL uniform parameter name in getActiveUniforms: 0x{:04x}", pname); - set_error(GL_INVALID_ENUM); - return JS::js_null(); - } - } - - return JS::Array::create_from(m_realm, params_as_values); -)~~~"); - continue; - } - - if (function.name == "getActiveAttrib"sv) { - generate_webgl_object_handle_unwrap(function_impl_generator, "program"sv, "{}"sv); - function_impl_generator.append(R"~~~( - GLint size = 0; - GLenum type = 0; - GLsizei buf_size = 256; - GLsizei length = 0; - GLchar name[256]; - glGetActiveAttrib(program_handle, index, buf_size, &length, &size, &type, name); - auto readonly_bytes = ReadonlyBytes { name, static_cast(length) }; - return WebGLActiveInfo::create(m_realm, String::from_utf8_without_validation(readonly_bytes), type, size); -)~~~"); - continue; - } - - if (function.name == "getShaderInfoLog"sv) { - generate_webgl_object_handle_unwrap(function_impl_generator, "shader"sv, "{}"sv); - function_impl_generator.append(R"~~~( - GLint info_log_length = 0; - glGetShaderiv(shader_handle, GL_INFO_LOG_LENGTH, &info_log_length); - Vector info_log; - info_log.resize(info_log_length); - if (!info_log_length) - return String {}; - glGetShaderInfoLog(shader_handle, info_log_length, nullptr, info_log.data()); - return String::from_utf8_without_validation(ReadonlyBytes { info_log.data(), static_cast(info_log_length - 1) }); -)~~~"); - continue; - } - - if (function.name == "getProgramInfoLog"sv) { - generate_webgl_object_handle_unwrap(function_impl_generator, "program"sv, "{}"sv); - function_impl_generator.append(R"~~~( - GLint info_log_length = 0; - glGetProgramiv(program_handle, GL_INFO_LOG_LENGTH, &info_log_length); - Vector info_log; - info_log.resize(info_log_length); - if (!info_log_length) - return String {}; - glGetProgramInfoLog(program_handle, info_log_length, nullptr, info_log.data()); - return String::from_utf8_without_validation(ReadonlyBytes { info_log.data(), static_cast(info_log_length - 1) }); -)~~~"); - continue; - } - - if (function.name == "getShaderPrecisionFormat"sv) { - function_impl_generator.append(R"~~~( - GLint range[2]; - GLint precision; - glGetShaderPrecisionFormat(shadertype, precisiontype, range, &precision); - return WebGLShaderPrecisionFormat::create(m_realm, range[0], range[1], precision); -)~~~"); - continue; - } - - if (function.name == "getActiveUniformBlockName"sv) { - generate_webgl_object_handle_unwrap(function_impl_generator, "program"sv, "OptionalNone {}"sv); - function_impl_generator.append(R"~~~( - GLint uniform_block_name_length = 0; - glGetActiveUniformBlockivRobustANGLE(program_handle, uniform_block_index, GL_UNIFORM_BLOCK_NAME_LENGTH, 1, nullptr, &uniform_block_name_length); - Vector uniform_block_name; - uniform_block_name.resize(uniform_block_name_length); - if (!uniform_block_name_length) - return String {}; - glGetActiveUniformBlockName(program_handle, uniform_block_index, uniform_block_name_length, nullptr, uniform_block_name.data()); - return String::from_utf8_without_validation(ReadonlyBytes { uniform_block_name.data(), static_cast(uniform_block_name_length - 1) }); -)~~~"); - continue; - } - - if (function.name == "deleteBuffer"sv) { - generate_webgl_object_handle_unwrap(function_impl_generator, "buffer"sv, ""sv); - function_impl_generator.append(R"~~~( - glDeleteBuffers(1, &buffer_handle); -)~~~"); - continue; - } - - if (function.name == "deleteFramebuffer"sv) { - generate_webgl_object_handle_unwrap(function_impl_generator, "framebuffer"sv, ""sv); - function_impl_generator.append(R"~~~( - glDeleteFramebuffers(1, &framebuffer_handle); -)~~~"); - continue; - } - - if (function.name == "deleteRenderbuffer"sv) { - generate_webgl_object_handle_unwrap(function_impl_generator, "renderbuffer"sv, ""sv); - function_impl_generator.append(R"~~~( - glDeleteRenderbuffers(1, &renderbuffer_handle); -)~~~"); - continue; - } - - if (function.name == "deleteTexture"sv) { - generate_webgl_object_handle_unwrap(function_impl_generator, "texture"sv, ""sv); - function_impl_generator.append(R"~~~( - glDeleteTextures(1, &texture_handle); -)~~~"); - continue; - } - - if (function.name == "deleteVertexArray"sv) { - generate_webgl_object_handle_unwrap(function_impl_generator, "vertex_array"sv, ""sv); - function_impl_generator.append(R"~~~( - glDeleteVertexArrays(1, &vertex_array_handle); -)~~~"); - continue; - } - - if (function.name == "deleteSampler"sv) { - generate_webgl_object_handle_unwrap(function_impl_generator, "sampler"sv, ""sv); - function_impl_generator.append(R"~~~( - glDeleteSamplers(1, &sampler_handle); -)~~~"); - continue; - } - - if (function.name == "bindBuffer"sv) { - // FIXME: Implement Buffer Object Binding restrictions. - generate_webgl_object_handle_unwrap(function_impl_generator, "buffer"sv, ""sv); - function_impl_generator.append(R"~~~( - switch (target) { - case GL_ELEMENT_ARRAY_BUFFER: - m_element_array_buffer_binding = buffer; - break; - case GL_ARRAY_BUFFER: - m_array_buffer_binding = buffer; - break; -)~~~"); - - if (webgl_version == 2) { - function_impl_generator.append(R"~~~( - case GL_UNIFORM_BUFFER: - m_uniform_buffer_binding = buffer; - break; - case GL_COPY_READ_BUFFER: - m_copy_read_buffer_binding = buffer; - break; - case GL_COPY_WRITE_BUFFER: - m_copy_write_buffer_binding = buffer; - break; -)~~~"); - } - - function_impl_generator.append(R"~~~( - default: - dbgln("Unknown WebGL buffer object binding target for storing current binding: 0x{:04x}", target); - set_error(GL_INVALID_ENUM); - return; - } - - glBindBuffer(target, buffer_handle); -)~~~"); - continue; - } - - if (function.name == "useProgram"sv) { - generate_webgl_object_handle_unwrap(function_impl_generator, "program"sv, ""sv); - function_impl_generator.append(R"~~~( - glUseProgram(program_handle); - m_current_program = program; -)~~~"); - continue; - } - - if (function.name == "bindFramebuffer"sv) { - generate_webgl_object_handle_unwrap(function_impl_generator, "framebuffer"sv, ""sv); - function_impl_generator.append(R"~~~( - glBindFramebuffer(target, framebuffer ? framebuffer_handle : m_context->default_framebuffer()); - m_framebuffer_binding = framebuffer; -)~~~"); - continue; - } - - if (function.name == "bindRenderbuffer"sv) { - generate_webgl_object_handle_unwrap(function_impl_generator, "renderbuffer"sv, ""sv); - function_impl_generator.append(R"~~~( - glBindRenderbuffer(target, renderbuffer ? renderbuffer_handle : m_context->default_renderbuffer()); - m_renderbuffer_binding = renderbuffer; -)~~~"); - continue; - } - - if (function.name == "bindTexture"sv) { - generate_webgl_object_handle_unwrap(function_impl_generator, "texture"sv, ""sv); - function_impl_generator.append(R"~~~( - switch (target) { - case GL_TEXTURE_2D: - m_texture_binding_2d = texture; - break; - case GL_TEXTURE_CUBE_MAP: - m_texture_binding_cube_map = texture; - break; -)~~~"); - - if (webgl_version == 2) { - function_impl_generator.append(R"~~~( - case GL_TEXTURE_2D_ARRAY: - m_texture_binding_2d_array = texture; - break; - case GL_TEXTURE_3D: - m_texture_binding_3d = texture; - break; -)~~~"); - } - - function_impl_generator.append(R"~~~( - default: - dbgln("Unknown WebGL texture target for storing current binding: 0x{:04x}", target); - set_error(GL_INVALID_ENUM); - return; - } - glBindTexture(target, texture_handle); -)~~~"); - continue; - } - - if (function.name == "renderbufferStorage"sv) { - // To be backward compatible with WebGL 1, also accepts internal format DEPTH_STENCIL, which should be - // mapped to DEPTH24_STENCIL8 by implementations. - if (webgl_version == 1) { - function_impl_generator.append(R"~~~( -#define GL_DEPTH_STENCIL 0x84F9 -#define GL_DEPTH24_STENCIL8 0x88F0 -)~~~"); - } - - function_impl_generator.append(R"~~~( - if (internalformat == GL_DEPTH_STENCIL) - internalformat = GL_DEPTH24_STENCIL8; -)~~~"); - - if (webgl_version == 1) { - function_impl_generator.append(R"~~~( -#undef GL_DEPTH_STENCIL -#undef GL_DEPTH24_STENCIL8 -)~~~"); - } - - function_impl_generator.append(R"~~~( - glRenderbufferStorage(target, internalformat, width, height); -)~~~"); - continue; - } - - if (function.name.starts_with("samplerParameter"sv)) { - generate_webgl_object_handle_unwrap(function_impl_generator, "sampler"sv, ""sv); - function_impl_generator.set("param_type", function.name.substring_view(16, 1)); - // pname is given in the following table: - // - TEXTURE_COMPARE_FUNC - // - TEXTURE_COMPARE_MODE - // - TEXTURE_MAG_FILTER - // - TEXTURE_MAX_LOD - // - TEXTURE_MIN_FILTER - // - TEXTURE_MIN_LOD - // - TEXTURE_WRAP_R - // - TEXTURE_WRAP_S - // - TEXTURE_WRAP_T - // If pname is not in the table above, generates an INVALID_ENUM error. - // NOTE: We have to do this ourselves, as OpenGL does not. - function_impl_generator.append(R"~~~( - switch (pname) { - case GL_TEXTURE_COMPARE_FUNC: - case GL_TEXTURE_COMPARE_MODE: - case GL_TEXTURE_MAG_FILTER: - case GL_TEXTURE_MAX_LOD: - case GL_TEXTURE_MIN_FILTER: - case GL_TEXTURE_MIN_LOD: - case GL_TEXTURE_WRAP_R: - case GL_TEXTURE_WRAP_S: - case GL_TEXTURE_WRAP_T: - break; - default: - dbgln("Unknown WebGL sampler parameter name: 0x{:04x}", pname); - set_error(GL_INVALID_ENUM); - return; - } - glSamplerParameter@param_type@(sampler_handle, pname, param); -)~~~"); - continue; - } - - if (function.name.starts_with("clearBuffer"sv) && function.name.ends_with('v')) { - auto element_type = function.name.substring_view(11, 2); - if (element_type == "fv"sv) { - function_impl_generator.set("cpp_element_type", "float"sv); - function_impl_generator.set("typed_array_type", "Float32Array"sv); - function_impl_generator.set("gl_postfix", "f"sv); - } else if (element_type == "iv"sv) { - function_impl_generator.set("cpp_element_type", "int"sv); - function_impl_generator.set("typed_array_type", "Int32Array"sv); - function_impl_generator.set("gl_postfix", "i"sv); - } else if (element_type == "ui"sv) { - function_impl_generator.set("cpp_element_type", "u32"sv); - function_impl_generator.set("typed_array_type", "Uint32Array"sv); - function_impl_generator.set("gl_postfix", "ui"sv); - } else { - VERIFY_NOT_REACHED(); - } - function_impl_generator.append(R"~~~( - @cpp_element_type@ const* data = nullptr; - size_t count = 0; - if (values.has>()) { - auto& vector = values.get>(); - data = vector.data(); - count = vector.size(); - } else if (values.has>()) { - auto& typed_array_base = static_cast(*values.get>()->raw_object()); - auto& typed_array = as(typed_array_base); - data = typed_array.data().data(); - count = typed_array.array_length().length(); - } else { - VERIFY_NOT_REACHED(); - } - - switch (buffer) { - case GL_COLOR: - if (src_offset + 4 > count) { - set_error(GL_INVALID_VALUE); - return; - } - break; - case GL_DEPTH: - case GL_STENCIL: - if (src_offset + 1 > count) { - set_error(GL_INVALID_VALUE); - return; - } - break; - default: - dbgln("Unknown WebGL buffer target for buffer clearing: 0x{:04x}", buffer); - set_error(GL_INVALID_ENUM); - return; - } - - data += src_offset; - glClearBuffer@gl_postfix@v(buffer, drawbuffer, data); - needs_to_present(); -)~~~"); - continue; - } - - Vector gl_call_arguments; - for (size_t i = 0; i < function.parameters.size(); ++i) { - auto const& parameter = function.parameters[i]; - auto parameter_name = parameter.name.to_snakecase(); - if (parameter.type->is_numeric() || parameter.type->is_boolean()) { - gl_call_arguments.append(parameter_name); - continue; - } - if (parameter.type->is_string()) { - function_impl_generator.set("parameter_name", parameter_name); - function_impl_generator.append(R"~~~( - auto @parameter_name@_null_terminated = null_terminated_string(@parameter_name@); -)~~~"); - gl_call_arguments.append(ByteString::formatted("{}_null_terminated.data()", parameter_name)); - continue; - } - if (is_webgl_object_type(parameter.type->name())) { - if (function.return_type->name() == "undefined"sv) { - function_impl_generator.set("early_return_value", ""); - } else if (function.return_type->is_integer()) { - function_impl_generator.set("early_return_value", "-1"); - } else if (function.return_type->is_boolean()) { - function_impl_generator.set("early_return_value", "false"); - } else { - VERIFY_NOT_REACHED(); - } - function_impl_generator.set("handle_parameter_name", parameter_name); - function_impl_generator.append(R"~~~( - auto @handle_parameter_name@_handle = 0; - if (@handle_parameter_name@) { - auto handle_or_error = @handle_parameter_name@->handle(this); - if (handle_or_error.is_error()) { - set_error(GL_INVALID_OPERATION); - return @early_return_value@; - } - @handle_parameter_name@_handle = handle_or_error.release_value(); - } -)~~~"); - gl_call_arguments.append(ByteString::formatted("{}_handle", parameter_name)); - continue; - } - if (parameter.type->name() == "WebGLUniformLocation"sv) { - gl_call_arguments.append(ByteString::formatted("{} ? {}->handle() : 0", parameter_name, parameter_name)); - continue; - } - if (parameter.type->name() == "WebGLSync"sv) { - // FIXME: Remove the GLsync cast once sync_handle actually returns the proper GLsync type. - gl_call_arguments.append(ByteString::formatted("(GLsync)({} ? {}->sync_handle() : nullptr)", parameter_name, parameter_name)); - continue; - } - if (parameter.type->name() == "BufferSource"sv) { - function_impl_generator.set("buffer_source_name", parameter_name); - function_impl_generator.append(R"~~~( - void const* ptr = nullptr; - size_t byte_size = 0; - if (@buffer_source_name@->is_typed_array_base()) { - auto& typed_array_base = static_cast(*@buffer_source_name@->raw_object()); - ptr = typed_array_base.viewed_array_buffer()->buffer().data() + typed_array_base.byte_offset(); - byte_size = @buffer_source_name@->byte_length(); - } else if (@buffer_source_name@->is_data_view()) { - auto& data_view = static_cast(*@buffer_source_name@->raw_object()); - ptr = data_view.viewed_array_buffer()->buffer().data(); - byte_size = data_view.viewed_array_buffer()->byte_length(); - } else if (@buffer_source_name@->is_array_buffer()) { - auto& array_buffer = static_cast(*@buffer_source_name@->raw_object()); - ptr = array_buffer.buffer().data(); - byte_size = array_buffer.byte_length(); - } else { - VERIFY_NOT_REACHED(); - } -)~~~"); - gl_call_arguments.append(ByteString::formatted("byte_size")); - gl_call_arguments.append(ByteString::formatted("ptr")); - continue; - } - if (parameter.type->name() == "ArrayBufferView"sv) { - function_impl_generator.set("buffer_source_name", parameter_name); - - function_impl_generator.append(R"~~~( - void const* ptr = @buffer_source_name@->viewed_array_buffer()->buffer().data() + @buffer_source_name@->byte_offset(); - size_t byte_size = @buffer_source_name@->byte_length(); -)~~~"); - gl_call_arguments.append(ByteString::formatted("byte_size")); - gl_call_arguments.append(ByteString::formatted("ptr")); - continue; - } - VERIFY_NOT_REACHED(); - } - - StringBuilder gl_call_arguments_string_builder; - gl_call_arguments_string_builder.join(", "sv, gl_call_arguments); - - auto gl_call_string = ByteString::formatted("{}({})", idl_to_gl_function_name(function.name), gl_call_arguments_string_builder.string_view()); - function_impl_generator.set("call_string", gl_call_string); - - if (gl_function_modifies_framebuffer(function.name)) { - function_impl_generator.append(" needs_to_present();\n"sv); - } - - if (function.return_type->name() == "undefined"sv) { - function_impl_generator.append(" @call_string@;"sv); - } else if (function.return_type->is_integer() || function.return_type->is_boolean()) { - function_impl_generator.append(" return @call_string@;"sv); - } else if (is_webgl_object_type(function.return_type->name())) { - function_impl_generator.set("return_type_name", function.return_type->name()); - function_impl_generator.append(" return @return_type_name@::create(m_realm, *this, @call_string@);"sv); - } else { - VERIFY_NOT_REACHED(); - } - - function_impl_generator.append("\n"sv); - } - - header_file_generator.append(R"~~~( -protected: - virtual void visit_edges(JS::Cell::Visitor&) override; - -private: - GC::Ref m_realm; - GC::Ptr m_array_buffer_binding; - GC::Ptr m_element_array_buffer_binding; - GC::Ptr m_current_program; - GC::Ptr m_framebuffer_binding; - GC::Ptr m_renderbuffer_binding; - GC::Ptr m_texture_binding_2d; - GC::Ptr m_texture_binding_cube_map; -)~~~"); - - if (webgl_version == 2) { - header_file_generator.append(R"~~~( - GC::Ptr m_uniform_buffer_binding; - GC::Ptr m_copy_read_buffer_binding; - GC::Ptr m_copy_write_buffer_binding; - GC::Ptr m_texture_binding_2d_array; - GC::Ptr m_texture_binding_3d; -)~~~"); - } - - header_file_generator.append(R"~~~( - NonnullOwnPtr m_context; -}; - -} -)~~~"); - - implementation_file_generator.append(R"~~~( -void @class_name@::visit_edges(JS::Cell::Visitor& visitor) -{ - visitor.visit(m_realm); - visitor.visit(m_array_buffer_binding); - visitor.visit(m_element_array_buffer_binding); - visitor.visit(m_current_program); - visitor.visit(m_framebuffer_binding); - visitor.visit(m_renderbuffer_binding); - visitor.visit(m_texture_binding_2d); - visitor.visit(m_texture_binding_cube_map); -)~~~"); - - if (webgl_version == 2) { - implementation_file_generator.append(R"~~~( - visitor.visit(m_uniform_buffer_binding); - visitor.visit(m_copy_read_buffer_binding); - visitor.visit(m_copy_write_buffer_binding); - visitor.visit(m_texture_binding_2d_array); - visitor.visit(m_texture_binding_3d); -)~~~"); - } - - implementation_file_generator.append(R"~~~( -} - -} -)~~~"); - - MUST(generated_header_file->write_until_depleted(header_file_generator.as_string_view().bytes())); - MUST(generated_implementation_file->write_until_depleted(implementation_file_generator.as_string_view().bytes())); - - return 0; -}