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; -}