ladybird/Libraries/LibWeb/WebGL/WebGL2RenderingContextImpl.cpp
Undefine 5b88b76b84 LibWeb: Deduplicate WebGL1 implementations from WebGL2
This makes the WebGL2 implementation file inherit from the WebGL1
implementation file. This is actually closer to what the IDL files
describe and allows us to not have to maintain two copies of the same
functions.
2025-11-05 02:19:32 +01:00

1210 lines
43 KiB
C++

/*
* Copyright (c) 2024-2025, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
* Copyright (c) 2024-2025, Luke Wilde <luke@ladybird.org>
* Copyright (c) 2025, Jelle Raaijmakers <jelle@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#define GL_GLEXT_PROTOTYPES 1
#include <GLES3/gl3.h>
extern "C" {
#include <GLES2/gl2ext.h>
#include <GLES2/gl2ext_angle.h>
}
#include <LibJS/Runtime/Array.h>
#include <LibJS/Runtime/ArrayBuffer.h>
#include <LibJS/Runtime/TypedArray.h>
#include <LibWeb/WebGL/OpenGLContext.h>
#include <LibWeb/WebGL/WebGL2RenderingContextImpl.h>
#include <LibWeb/WebGL/WebGLActiveInfo.h>
#include <LibWeb/WebGL/WebGLBuffer.h>
#include <LibWeb/WebGL/WebGLFramebuffer.h>
#include <LibWeb/WebGL/WebGLProgram.h>
#include <LibWeb/WebGL/WebGLQuery.h>
#include <LibWeb/WebGL/WebGLRenderbuffer.h>
#include <LibWeb/WebGL/WebGLSampler.h>
#include <LibWeb/WebGL/WebGLShader.h>
#include <LibWeb/WebGL/WebGLShaderPrecisionFormat.h>
#include <LibWeb/WebGL/WebGLSync.h>
#include <LibWeb/WebGL/WebGLTexture.h>
#include <LibWeb/WebGL/WebGLTransformFeedback.h>
#include <LibWeb/WebGL/WebGLUniformLocation.h>
#include <LibWeb/WebGL/WebGLVertexArrayObject.h>
#include <LibWeb/WebIDL/Buffers.h>
namespace Web::WebGL {
WebGL2RenderingContextImpl::WebGL2RenderingContextImpl(JS::Realm& realm, NonnullOwnPtr<OpenGLContext> context)
: WebGLRenderingContextImpl(realm, 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);
}
// https://registry.khronos.org/webgl/specs/latest/2.0/#3.7.3
void WebGL2RenderingContextImpl::get_buffer_sub_data(WebIDL::UnsignedLong target, WebIDL::LongLong src_byte_offset,
GC::Root<WebIDL::ArrayBufferView> dst_buffer, WebIDL::UnsignedLongLong dst_offset, WebIDL::UnsignedLong length)
{
// If dstBuffer is a DataView, let elementSize be 1; otherwise, let elementSize be dstBuffer.BYTES_PER_ELEMENT.
size_t element_size = dst_buffer->element_size();
// If length is 0:
size_t copy_length;
if (length == 0) {
// If dstBuffer is a DataView, let copyLength be dstBuffer.byteLength - dstOffset; the typed elements in the
// text below are bytes. Otherwise, let copyLength be dstBuffer.length - dstOffset.
copy_length = dst_buffer->byte_length() / element_size - dst_offset;
}
// Otherwise, let copyLength be length.
else {
copy_length = length;
}
// If copyLength is 0, no data is written to dstBuffer, but this does not cause a GL error to be generated.
if (copy_length == 0)
return;
// If dstOffset is greater than dstBuffer.length (or dstBuffer.byteLength in the case of DataView), generates an
// INVALID_VALUE error.
size_t dst_offset_in_bytes = dst_offset * element_size;
if (dst_offset_in_bytes > dst_buffer->byte_length()) {
set_error(GL_INVALID_VALUE);
return;
}
// If dstOffset + copyLength is greater than dstBuffer.length (or dstBuffer.byteLength in the case of DataView),
// generates an INVALID_VALUE error.
size_t copy_bytes = copy_length * element_size;
if (dst_offset_in_bytes + copy_bytes > dst_buffer->byte_length()) {
set_error(GL_INVALID_VALUE);
return;
}
// If copyLength is greater than zero, copy copyLength typed elements (each of size elementSize) from buf into
// dstBuffer, reading buf starting at byte index srcByteOffset and writing into dstBuffer starting at element
// index dstOffset.
auto* buffer_data = glMapBufferRange(target, src_byte_offset, copy_bytes, GL_MAP_READ_BIT);
if (!buffer_data)
return;
dst_buffer->write({ buffer_data, copy_bytes }, dst_offset_in_bytes);
glUnmapBuffer(target);
}
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::framebuffer_texture_layer(WebIDL::UnsignedLong target, WebIDL::UnsignedLong attachment, GC::Root<WebGLTexture> texture, WebIDL::Long level, WebIDL::Long layer)
{
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();
}
glFramebufferTextureLayer(target, attachment, texture_handle, level, layer);
}
void WebGL2RenderingContextImpl::invalidate_framebuffer(WebIDL::UnsignedLong target, Vector<WebIDL::UnsignedLong> attachments)
{
m_context->make_current();
m_context->notify_content_will_change();
glInvalidateFramebuffer(target, attachments.size(), attachments.data());
needs_to_present();
}
void WebGL2RenderingContextImpl::invalidate_sub_framebuffer(WebIDL::UnsignedLong target, Vector<WebIDL::UnsignedLong> attachments, WebIDL::Long x, WebIDL::Long y, WebIDL::Long width, WebIDL::Long height)
{
m_context->make_current();
m_context->notify_content_will_change();
glInvalidateSubFramebuffer(target, attachments.size(), attachments.data(), x, y, width, height);
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<GLint*>(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<WebIDL::ArrayBufferView> src_data)
{
m_context->make_current();
ReadonlyBytes src_data_span;
if (src_data) {
src_data_span = SET_ERROR_VALUE_IF_ERROR(get_offset_span<u8 const>(*src_data, /* src_offset= */ 0), GL_INVALID_OPERATION);
}
glTexImage3DRobustANGLE(target, level, internalformat, width, height, depth, border, format, type, src_data_span.size(), src_data_span.data());
}
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<WebIDL::ArrayBufferView> src_data, WebIDL::UnsignedLongLong src_offset)
{
m_context->make_current();
ReadonlyBytes src_data_span;
if (src_data) {
src_data_span = SET_ERROR_VALUE_IF_ERROR(get_offset_span<u8 const>(*src_data, src_offset), GL_INVALID_OPERATION);
}
glTexImage3DRobustANGLE(target, level, internalformat, width, height, depth, border, format, type, src_data_span.size(), src_data_span.data());
}
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<WebIDL::ArrayBufferView> src_data, WebIDL::UnsignedLongLong src_offset)
{
m_context->make_current();
ReadonlyBytes src_data_span;
if (src_data) {
src_data_span = SET_ERROR_VALUE_IF_ERROR(get_offset_span<u8 const>(*src_data, src_offset), GL_INVALID_OPERATION);
}
glTexSubImage3DRobustANGLE(target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, src_data_span.size(), src_data_span.data());
}
void WebGL2RenderingContextImpl::uniform1ui(GC::Root<WebGLUniformLocation> location, WebIDL::UnsignedLong v0)
{
m_context->make_current();
glUniform1ui(location ? location->handle() : 0, v0);
}
void WebGL2RenderingContextImpl::uniform2ui(GC::Root<WebGLUniformLocation> location, WebIDL::UnsignedLong v0, WebIDL::UnsignedLong v1)
{
m_context->make_current();
glUniform2ui(location ? location->handle() : 0, v0, v1);
}
void WebGL2RenderingContextImpl::uniform3ui(GC::Root<WebGLUniformLocation> 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<WebGLUniformLocation> 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::uniform1uiv(GC::Root<WebGLUniformLocation> location, Uint32List values, WebIDL::UnsignedLongLong src_offset, WebIDL::UnsignedLong src_length)
{
m_context->make_current();
if (!location)
return;
auto span = SET_ERROR_VALUE_IF_ERROR(span_from_uint32_list(values, src_offset, src_length), GL_INVALID_VALUE);
glUniform1uiv(location->handle(), span.size(), span.data());
}
void WebGL2RenderingContextImpl::uniform2uiv(GC::Root<WebGLUniformLocation> location, Uint32List values, WebIDL::UnsignedLongLong src_offset, WebIDL::UnsignedLong src_length)
{
m_context->make_current();
if (!location)
return;
auto span = SET_ERROR_VALUE_IF_ERROR(span_from_uint32_list(values, src_offset, src_length), GL_INVALID_VALUE);
if (span.size() % 2 != 0) [[unlikely]] {
set_error(GL_INVALID_VALUE);
return;
}
glUniform2uiv(location->handle(), span.size() / 2, span.data());
}
void WebGL2RenderingContextImpl::uniform3uiv(GC::Root<WebGLUniformLocation> location, Uint32List values, WebIDL::UnsignedLongLong src_offset, WebIDL::UnsignedLong src_length)
{
m_context->make_current();
if (!location)
return;
auto span = SET_ERROR_VALUE_IF_ERROR(span_from_uint32_list(values, src_offset, src_length), GL_INVALID_VALUE);
if (span.size() % 3 != 0) [[unlikely]] {
set_error(GL_INVALID_VALUE);
return;
}
glUniform3uiv(location->handle(), span.size() / 3, span.data());
}
void WebGL2RenderingContextImpl::uniform4uiv(GC::Root<WebGLUniformLocation> location, Uint32List values, WebIDL::UnsignedLongLong src_offset, WebIDL::UnsignedLong src_length)
{
m_context->make_current();
if (!location)
return;
auto span = SET_ERROR_VALUE_IF_ERROR(span_from_uint32_list(values, src_offset, src_length), GL_INVALID_VALUE);
if (span.size() % 4 != 0) [[unlikely]] {
set_error(GL_INVALID_VALUE);
return;
}
glUniform4uiv(location->handle(), span.size() / 4, span.data());
}
void WebGL2RenderingContextImpl::uniform_matrix3x2fv(GC::Root<WebGLUniformLocation> location, bool transpose, Float32List data, WebIDL::UnsignedLongLong src_offset, WebIDL::UnsignedLong src_length)
{
m_context->make_current();
if (!location)
return;
constexpr auto matrix_size = 3 * 2;
auto span = SET_ERROR_VALUE_IF_ERROR(span_from_float32_list(data, src_offset, src_length), GL_INVALID_VALUE);
if (span.size() % matrix_size != 0) [[unlikely]] {
set_error(GL_INVALID_VALUE);
return;
}
glUniformMatrix3x2fv(location->handle(), span.size() / matrix_size, transpose, span.data());
}
void WebGL2RenderingContextImpl::uniform_matrix4x2fv(GC::Root<WebGLUniformLocation> location, bool transpose, Float32List data, WebIDL::UnsignedLongLong src_offset, WebIDL::UnsignedLong src_length)
{
m_context->make_current();
if (!location)
return;
constexpr auto matrix_size = 4 * 2;
auto span = SET_ERROR_VALUE_IF_ERROR(span_from_float32_list(data, src_offset, src_length), GL_INVALID_VALUE);
if (span.size() % matrix_size != 0) [[unlikely]] {
set_error(GL_INVALID_VALUE);
return;
}
glUniformMatrix4x2fv(location->handle(), span.size() / matrix_size, transpose, span.data());
}
void WebGL2RenderingContextImpl::uniform_matrix2x3fv(GC::Root<WebGLUniformLocation> location, bool transpose, Float32List data, WebIDL::UnsignedLongLong src_offset, WebIDL::UnsignedLong src_length)
{
m_context->make_current();
if (!location)
return;
constexpr auto matrix_size = 2 * 3;
auto span = SET_ERROR_VALUE_IF_ERROR(span_from_float32_list(data, src_offset, src_length), GL_INVALID_VALUE);
if (span.size() % matrix_size != 0) [[unlikely]] {
set_error(GL_INVALID_VALUE);
return;
}
glUniformMatrix2x3fv(location->handle(), span.size() / matrix_size, transpose, span.data());
}
void WebGL2RenderingContextImpl::uniform_matrix4x3fv(GC::Root<WebGLUniformLocation> location, bool transpose, Float32List data, WebIDL::UnsignedLongLong src_offset, WebIDL::UnsignedLong src_length)
{
m_context->make_current();
if (!location)
return;
constexpr auto matrix_size = 4 * 3;
auto span = SET_ERROR_VALUE_IF_ERROR(span_from_float32_list(data, src_offset, src_length), GL_INVALID_VALUE);
if (span.size() % matrix_size != 0) [[unlikely]] {
set_error(GL_INVALID_VALUE);
return;
}
glUniformMatrix4x3fv(location->handle(), span.size() / matrix_size, transpose, span.data());
}
void WebGL2RenderingContextImpl::uniform_matrix2x4fv(GC::Root<WebGLUniformLocation> location, bool transpose, Float32List data, WebIDL::UnsignedLongLong src_offset, WebIDL::UnsignedLong src_length)
{
m_context->make_current();
if (!location)
return;
constexpr auto matrix_size = 2 * 4;
auto span = SET_ERROR_VALUE_IF_ERROR(span_from_float32_list(data, src_offset, src_length), GL_INVALID_VALUE);
if (span.size() % matrix_size != 0) [[unlikely]] {
set_error(GL_INVALID_VALUE);
return;
}
glUniformMatrix2x4fv(location->handle(), span.size() / matrix_size, transpose, span.data());
}
void WebGL2RenderingContextImpl::uniform_matrix3x4fv(GC::Root<WebGLUniformLocation> location, bool transpose, Float32List data, WebIDL::UnsignedLongLong src_offset, WebIDL::UnsignedLong src_length)
{
m_context->make_current();
if (!location)
return;
constexpr auto matrix_size = 3 * 4;
auto span = SET_ERROR_VALUE_IF_ERROR(span_from_float32_list(data, src_offset, src_length), GL_INVALID_VALUE);
if (span.size() % matrix_size != 0) [[unlikely]] {
set_error(GL_INVALID_VALUE);
return;
}
glUniformMatrix3x4fv(location->handle(), span.size() / matrix_size, transpose, span.data());
}
void WebGL2RenderingContextImpl::vertex_attrib_i4i(WebIDL::UnsignedLong index, WebIDL::Long x, WebIDL::Long y, WebIDL::Long z, WebIDL::Long w)
{
m_context->make_current();
glVertexAttribI4i(index, x, y, z, w);
}
void WebGL2RenderingContextImpl::vertex_attrib_i4iv(WebIDL::UnsignedLong index, Int32List values)
{
m_context->make_current();
auto span = MUST(span_from_int32_list(values, /* src_offset= */ 0));
if (span.size() < 4) [[unlikely]] {
set_error(GL_INVALID_VALUE);
return;
}
glVertexAttribI4iv(index, span.data());
}
void WebGL2RenderingContextImpl::vertex_attrib_i4ui(WebIDL::UnsignedLong index, WebIDL::UnsignedLong x, WebIDL::UnsignedLong y, WebIDL::UnsignedLong z, WebIDL::UnsignedLong w)
{
m_context->make_current();
glVertexAttribI4ui(index, x, y, z, w);
}
void WebGL2RenderingContextImpl::vertex_attrib_i4uiv(WebIDL::UnsignedLong index, Uint32List values)
{
m_context->make_current();
auto span = MUST(span_from_uint32_list(values, /* src_offset= */ 0));
if (span.size() < 4) [[unlikely]] {
set_error(GL_INVALID_VALUE);
return;
}
glVertexAttribI4uiv(index, span.data());
}
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<void*>(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<void*>(offset), instance_count);
needs_to_present();
}
void WebGL2RenderingContextImpl::draw_range_elements(WebIDL::UnsignedLong mode, WebIDL::UnsignedLong start, WebIDL::UnsignedLong end, WebIDL::Long count, WebIDL::UnsignedLong type, WebIDL::LongLong offset)
{
m_context->make_current();
m_context->notify_content_will_change();
needs_to_present();
glDrawRangeElements(mode, start, end, count, type, reinterpret_cast<void*>(offset));
}
void WebGL2RenderingContextImpl::draw_buffers(Vector<WebIDL::UnsignedLong> buffers)
{
m_context->make_current();
glDrawBuffers(buffers.size(), buffers.data());
}
void WebGL2RenderingContextImpl::clear_bufferfv(WebIDL::UnsignedLong buffer, WebIDL::Long drawbuffer, Float32List values, WebIDL::UnsignedLongLong src_offset)
{
m_context->make_current();
m_context->notify_content_will_change();
auto span = SET_ERROR_VALUE_IF_ERROR(span_from_float32_list(values, src_offset), GL_INVALID_VALUE);
switch (buffer) {
case GL_COLOR:
if (span.size() < 4) {
set_error(GL_INVALID_VALUE);
return;
}
break;
case GL_DEPTH:
case GL_STENCIL:
if (span.size() < 1) {
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;
}
glClearBufferfv(buffer, drawbuffer, span.data());
needs_to_present();
}
void WebGL2RenderingContextImpl::clear_bufferiv(WebIDL::UnsignedLong buffer, WebIDL::Long drawbuffer, Int32List values, WebIDL::UnsignedLongLong src_offset)
{
m_context->make_current();
m_context->notify_content_will_change();
auto span = SET_ERROR_VALUE_IF_ERROR(span_from_int32_list(values, src_offset), GL_INVALID_VALUE);
switch (buffer) {
case GL_COLOR:
if (span.size() < 4) {
set_error(GL_INVALID_VALUE);
return;
}
break;
case GL_DEPTH:
case GL_STENCIL:
if (span.size() < 1) {
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;
}
glClearBufferiv(buffer, drawbuffer, span.data());
needs_to_present();
}
void WebGL2RenderingContextImpl::clear_bufferuiv(WebIDL::UnsignedLong buffer, WebIDL::Long drawbuffer, Uint32List values, WebIDL::UnsignedLongLong src_offset)
{
m_context->make_current();
m_context->notify_content_will_change();
auto span = SET_ERROR_VALUE_IF_ERROR(span_from_uint32_list(values, src_offset), GL_INVALID_VALUE);
switch (buffer) {
case GL_COLOR:
if (span.size() < 4) {
set_error(GL_INVALID_VALUE);
return;
}
break;
case GL_DEPTH:
case GL_STENCIL:
if (span.size() < 1) {
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;
}
glClearBufferuiv(buffer, drawbuffer, span.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<WebGLQuery> WebGL2RenderingContextImpl::create_query()
{
m_context->make_current();
GLuint handle = 0;
glGenQueries(1, &handle);
return WebGLQuery::create(m_realm, *this, handle);
}
void WebGL2RenderingContextImpl::delete_query(GC::Root<WebGLQuery> query)
{
m_context->make_current();
GLuint query_handle = 0;
if (query) {
auto handle_or_error = query->handle(this);
if (handle_or_error.is_error()) {
set_error(GL_INVALID_OPERATION);
return;
}
query_handle = handle_or_error.release_value();
}
glDeleteQueries(1, &query_handle);
}
void WebGL2RenderingContextImpl::begin_query(WebIDL::UnsignedLong target, GC::Root<WebGLQuery> query)
{
m_context->make_current();
GLuint query_handle = 0;
if (query) {
auto handle_or_error = query->handle(this);
if (handle_or_error.is_error()) {
set_error(GL_INVALID_OPERATION);
return;
}
query_handle = handle_or_error.release_value();
}
glBeginQuery(target, query_handle);
}
void WebGL2RenderingContextImpl::end_query(WebIDL::UnsignedLong target)
{
m_context->make_current();
glEndQuery(target);
}
JS::Value WebGL2RenderingContextImpl::get_query_parameter(GC::Root<WebGLQuery> query, WebIDL::UnsignedLong pname)
{
m_context->make_current();
GLuint query_handle = 0;
if (query) {
auto handle_or_error = query->handle(this);
if (handle_or_error.is_error()) {
set_error(GL_INVALID_OPERATION);
return JS::js_null();
}
query_handle = handle_or_error.release_value();
}
GLuint result { 0 };
glGetQueryObjectuivRobustANGLE(query_handle, pname, 1, nullptr, &result);
switch (pname) {
case GL_QUERY_RESULT:
return JS::Value(result);
case GL_QUERY_RESULT_AVAILABLE:
return JS::Value(result == GL_TRUE);
default:
return JS::js_null();
}
}
GC::Root<WebGLSampler> 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<WebGLSampler> 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<WebGLSampler> 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<WebGLSampler> 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;
case GL_TEXTURE_MAX_ANISOTROPY_EXT: {
if (ext_texture_filter_anisotropic_extension_enabled())
break;
set_error(GL_INVALID_ENUM);
return;
}
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<WebGLSampler> 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;
case GL_TEXTURE_MAX_ANISOTROPY_EXT: {
if (ext_texture_filter_anisotropic_extension_enabled())
break;
set_error(GL_INVALID_ENUM);
return;
}
default:
dbgln("Unknown WebGL sampler parameter name: 0x{:04x}", pname);
set_error(GL_INVALID_ENUM);
return;
}
glSamplerParameterf(sampler_handle, pname, param);
}
GC::Root<WebGLSync> 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<WebGLSync> sync)
{
m_context->make_current();
GLsync sync_handle = nullptr;
if (sync) {
auto handle_or_error = sync->sync_handle(this);
if (handle_or_error.is_error()) {
set_error(GL_INVALID_OPERATION);
return;
}
sync_handle = static_cast<GLsync>(handle_or_error.release_value());
}
glDeleteSync(sync_handle);
}
WebIDL::UnsignedLong WebGL2RenderingContextImpl::client_wait_sync(GC::Root<WebGLSync> sync, WebIDL::UnsignedLong flags, WebIDL::UnsignedLongLong timeout)
{
m_context->make_current();
GLsync sync_handle = nullptr;
if (sync) {
auto handle_or_error = sync->sync_handle(this);
if (handle_or_error.is_error()) {
set_error(GL_INVALID_OPERATION);
return GL_WAIT_FAILED;
}
sync_handle = static_cast<GLsync>(handle_or_error.release_value());
}
return glClientWaitSync(sync_handle, flags, timeout);
}
void WebGL2RenderingContextImpl::wait_sync(GC::Root<WebGLSync> sync, WebIDL::UnsignedLong flags, WebIDL::UnsignedLongLong timeout)
{
m_context->make_current();
GLsync sync_handle = nullptr;
if (sync) {
auto handle_or_error = sync->sync_handle(this);
if (handle_or_error.is_error()) {
set_error(GL_INVALID_OPERATION);
return;
}
sync_handle = static_cast<GLsync>(handle_or_error.release_value());
}
glWaitSync(sync_handle, flags, timeout);
}
JS::Value WebGL2RenderingContextImpl::get_sync_parameter(GC::Root<WebGLSync> sync, WebIDL::UnsignedLong pname)
{
m_context->make_current();
GLsync sync_handle = nullptr;
if (sync) {
auto handle_or_error = sync->sync_handle(this);
if (handle_or_error.is_error()) {
set_error(GL_INVALID_OPERATION);
return JS::js_null();
}
sync_handle = static_cast<GLsync>(handle_or_error.release_value());
}
GLint result = 0;
glGetSynciv(sync_handle, pname, 1, nullptr, &result);
return JS::Value(result);
}
GC::Root<WebGLTransformFeedback> WebGL2RenderingContextImpl::create_transform_feedback()
{
m_context->make_current();
GLuint handle = 0;
glGenTransformFeedbacks(1, &handle);
return WebGLTransformFeedback::create(m_realm, *this, handle);
}
void WebGL2RenderingContextImpl::delete_transform_feedback(GC::Root<WebGLTransformFeedback> transform_feedback)
{
m_context->make_current();
GLuint transform_feedback_handle = 0;
if (transform_feedback) {
auto handle_or_error = transform_feedback->handle(this);
if (handle_or_error.is_error()) {
set_error(GL_INVALID_OPERATION);
return;
}
transform_feedback_handle = handle_or_error.release_value();
}
glDeleteTransformFeedbacks(1, &transform_feedback_handle);
}
void WebGL2RenderingContextImpl::bind_transform_feedback(WebIDL::UnsignedLong target, GC::Root<WebGLTransformFeedback> transform_feedback)
{
m_context->make_current();
GLuint transform_feedback_handle = 0;
if (transform_feedback) {
auto handle_or_error = transform_feedback->handle(this);
if (handle_or_error.is_error()) {
set_error(GL_INVALID_OPERATION);
return;
}
transform_feedback_handle = handle_or_error.release_value();
}
glBindTransformFeedback(target, transform_feedback_handle);
}
void WebGL2RenderingContextImpl::begin_transform_feedback(WebIDL::UnsignedLong primitive_mode)
{
m_context->make_current();
glBeginTransformFeedback(primitive_mode);
}
void WebGL2RenderingContextImpl::end_transform_feedback()
{
m_context->make_current();
glEndTransformFeedback();
}
void WebGL2RenderingContextImpl::transform_feedback_varyings(GC::Root<WebGLProgram> program, Vector<String> const& varyings, WebIDL::UnsignedLong buffer_mode)
{
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();
}
Vector<Vector<GLchar>> varying_strings;
varying_strings.ensure_capacity(varyings.size());
for (auto const& varying : varyings) {
varying_strings.unchecked_append(null_terminated_string(varying));
}
Vector<GLchar const*> varying_strings_characters;
varying_strings.ensure_capacity(varying_strings.size());
for (auto const& varying_string : varying_strings) {
varying_strings_characters.append(varying_string.data());
}
glTransformFeedbackVaryings(program_handle, varying_strings_characters.size(), varying_strings_characters.data(), buffer_mode);
}
void WebGL2RenderingContextImpl::pause_transform_feedback()
{
m_context->make_current();
glPauseTransformFeedback();
}
void WebGL2RenderingContextImpl::resume_transform_feedback()
{
m_context->make_current();
glResumeTransformFeedback();
}
void WebGL2RenderingContextImpl::bind_buffer_base(WebIDL::UnsignedLong target, WebIDL::UnsignedLong index, GC::Root<WebGLBuffer> 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<WebGLBuffer> 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<WebGLProgram> program, Vector<WebIDL::UnsignedLong> 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<GLint> params_span(reinterpret_cast<GLint*>(params.data()), uniform_indices.size());
glGetActiveUniformsiv(program_handle, uniform_indices.size(), uniform_indices.data(), pname, params_span.data());
Vector<JS::Value> 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<GLenum>(param)));
break;
case GL_UNIFORM_SIZE:
params_as_values.unchecked_append(JS::Value(static_cast<GLuint>(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<WebGLProgram> 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<WebGLProgram> 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<GLint*>(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<String> WebGL2RenderingContextImpl::get_active_uniform_block_name(GC::Root<WebGLProgram> 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<GLchar> 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<size_t>(uniform_block_name_length - 1) });
}
void WebGL2RenderingContextImpl::uniform_block_binding(GC::Root<WebGLProgram> 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<WebGLVertexArrayObject> 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<WebGLVertexArrayObject> 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<WebGLVertexArrayObject> 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<WebGLVertexArrayObject> 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::compressed_tex_image3d(WebIDL::UnsignedLong target, WebIDL::Long level, WebIDL::UnsignedLong internalformat, WebIDL::Long width, WebIDL::Long height, WebIDL::Long depth, WebIDL::Long border, GC::Root<WebIDL::ArrayBufferView> src_data, WebIDL::UnsignedLongLong src_offset, WebIDL::UnsignedLong src_length_override)
{
m_context->make_current();
auto pixels = SET_ERROR_VALUE_IF_ERROR(get_offset_span<u8 const>(*src_data, src_offset, src_length_override), GL_INVALID_VALUE);
glCompressedTexImage3DRobustANGLE(target, level, internalformat, width, height, depth, border, pixels.size(), pixels.size(), pixels.data());
}
void WebGL2RenderingContextImpl::compressed_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, GC::Root<WebIDL::ArrayBufferView> src_data, WebIDL::UnsignedLongLong src_offset, WebIDL::UnsignedLong src_length_override)
{
m_context->make_current();
auto pixels = SET_ERROR_VALUE_IF_ERROR(get_offset_span<u8 const>(*src_data, src_offset, src_length_override), GL_INVALID_VALUE);
glCompressedTexSubImage3DRobustANGLE(target, level, xoffset, yoffset, zoffset, width, height, depth, format, pixels.size(), pixels.size(), pixels.data());
}
}