// Copyright 2015 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "modules/webgl/WebGL2RenderingContextBase.h" #include "bindings/modules/v8/WebGLAny.h" #include "core/frame/ImageBitmap.h" #include "core/html/HTMLCanvasElement.h" #include "core/html/HTMLImageElement.h" #include "core/html/HTMLVideoElement.h" #include "core/html/ImageData.h" #include "modules/webgl/WebGLActiveInfo.h" #include "modules/webgl/WebGLBuffer.h" #include "modules/webgl/WebGLFenceSync.h" #include "modules/webgl/WebGLFramebuffer.h" #include "modules/webgl/WebGLProgram.h" #include "modules/webgl/WebGLQuery.h" #include "modules/webgl/WebGLRenderbuffer.h" #include "modules/webgl/WebGLSampler.h" #include "modules/webgl/WebGLSync.h" #include "modules/webgl/WebGLTexture.h" #include "modules/webgl/WebGLTransformFeedback.h" #include "modules/webgl/WebGLUniformLocation.h" #include "modules/webgl/WebGLVertexArrayObject.h" #include "platform/CheckedInt.h" #include "platform/NotImplemented.h" #include "wtf/OwnPtr.h" #include "wtf/PassOwnPtr.h" namespace blink { namespace { WGC3Dsync syncObjectOrZero(const WebGLSync* object) { return object ? object->object() : 0; } } // namespace // These enums are from manual pages for glTexStorage2D/glTexStorage3D. const GLenum kSupportedInternalFormatsStorage[] = { GL_R8, GL_R8_SNORM, GL_R16F, GL_R32F, GL_R8UI, GL_R8I, GL_R16UI, GL_R16I, GL_R32UI, GL_R32I, GL_RG8, GL_RG8_SNORM, GL_RG16F, GL_RG32F, GL_RG8UI, GL_RG8I, GL_RG16UI, GL_RG16I, GL_RG32UI, GL_RG32I, GL_RGB8, GL_SRGB8, GL_RGB565, GL_RGB8_SNORM, GL_R11F_G11F_B10F, GL_RGB9_E5, GL_RGB16F, GL_RGB32F, GL_RGB8UI, GL_RGB8I, GL_RGB16UI, GL_RGB16I, GL_RGB32UI, GL_RGB32I, GL_RGBA8, GL_SRGB8_ALPHA8, GL_RGBA8_SNORM, GL_RGB5_A1, GL_RGBA4, GL_RGB10_A2, GL_RGBA16F, GL_RGBA32F, GL_RGBA8UI, GL_RGBA8I, GL_RGB10_A2UI, GL_RGBA16UI, GL_RGBA16I, GL_RGBA32UI, GL_RGBA32I, GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT32F, GL_DEPTH24_STENCIL8, GL_DEPTH32F_STENCIL8, }; const GLenum kCompressedTextureFormatsETC2EAC[] = { GL_COMPRESSED_R11_EAC, GL_COMPRESSED_SIGNED_R11_EAC, GL_COMPRESSED_RG11_EAC, GL_COMPRESSED_SIGNED_RG11_EAC, GL_COMPRESSED_RGB8_ETC2, GL_COMPRESSED_SRGB8_ETC2, GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2, GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2, GL_COMPRESSED_RGBA8_ETC2_EAC, GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC, }; WebGL2RenderingContextBase::WebGL2RenderingContextBase(HTMLCanvasElement* passedCanvas, PassOwnPtr context, const WebGLContextAttributes& requestedAttributes) : WebGLRenderingContextBase(passedCanvas, context, requestedAttributes) { m_supportedInternalFormatsStorage.insert(kSupportedInternalFormatsStorage, kSupportedInternalFormatsStorage + WTF_ARRAY_LENGTH(kSupportedInternalFormatsStorage)); m_supportedInternalFormatsStorage.insert(kCompressedTextureFormatsETC2EAC, kCompressedTextureFormatsETC2EAC + WTF_ARRAY_LENGTH(kCompressedTextureFormatsETC2EAC)); m_compressedTextureFormatsETC2EAC.insert(kCompressedTextureFormatsETC2EAC, kCompressedTextureFormatsETC2EAC + WTF_ARRAY_LENGTH(kCompressedTextureFormatsETC2EAC)); m_compressedTextureFormats.append(kCompressedTextureFormatsETC2EAC, WTF_ARRAY_LENGTH(kCompressedTextureFormatsETC2EAC)); } WebGL2RenderingContextBase::~WebGL2RenderingContextBase() { m_readFramebufferBinding = nullptr; m_boundCopyReadBuffer = nullptr; m_boundCopyWriteBuffer = nullptr; m_boundPixelPackBuffer = nullptr; m_boundPixelUnpackBuffer = nullptr; m_boundTransformFeedbackBuffer = nullptr; m_boundUniformBuffer = nullptr; m_currentBooleanOcclusionQuery = nullptr; m_currentTransformFeedbackPrimitivesWrittenQuery = nullptr; } void WebGL2RenderingContextBase::initializeNewContext() { ASSERT(!isContextLost()); ASSERT(drawingBuffer()); m_readFramebufferBinding = nullptr; m_boundCopyReadBuffer = nullptr; m_boundCopyWriteBuffer = nullptr; m_boundPixelPackBuffer = nullptr; m_boundPixelUnpackBuffer = nullptr; m_boundTransformFeedbackBuffer = nullptr; m_boundUniformBuffer = nullptr; m_currentBooleanOcclusionQuery = nullptr; m_currentTransformFeedbackPrimitivesWrittenQuery = nullptr; GLint numCombinedTextureImageUnits = 0; webContext()->getIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &numCombinedTextureImageUnits); m_samplerUnits.clear(); m_samplerUnits.resize(numCombinedTextureImageUnits); m_maxTransformFeedbackSeparateAttribs = 0; webContext()->getIntegerv(GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS, &m_maxTransformFeedbackSeparateAttribs); m_boundIndexedTransformFeedbackBuffers.clear(); m_boundIndexedTransformFeedbackBuffers.resize(m_maxTransformFeedbackSeparateAttribs); GLint maxUniformBufferBindings = 0; webContext()->getIntegerv(GL_MAX_UNIFORM_BUFFER_BINDINGS, &maxUniformBufferBindings); m_boundIndexedUniformBuffers.clear(); m_boundIndexedUniformBuffers.resize(maxUniformBufferBindings); m_maxBoundUniformBufferIndex = 0; m_packRowLength = 0; m_packSkipPixels = 0; m_packSkipRows = 0; m_unpackRowLength = 0; m_unpackImageHeight = 0; m_unpackSkipPixels = 0; m_unpackSkipRows = 0; m_unpackSkipImages = 0; WebGLRenderingContextBase::initializeNewContext(); } void WebGL2RenderingContextBase::copyBufferSubData(GLenum readTarget, GLenum writeTarget, long long readOffset, long long writeOffset, long long size) { if (isContextLost()) return; if (!validateValueFitNonNegInt32("copyBufferSubData", "readOffset", readOffset) || !validateValueFitNonNegInt32("copyBufferSubData", "writeOffset", writeOffset) || !validateValueFitNonNegInt32("copyBufferSubData", "size", size)) { return; } WebGLBuffer* readBuffer = validateBufferDataTarget("copyBufferSubData", readTarget); if (!readBuffer) return; WebGLBuffer* writeBuffer = validateBufferDataTarget("copyBufferSubData", writeTarget); if (!writeBuffer) return; if (readOffset + size > readBuffer->getSize() || writeOffset + size > writeBuffer->getSize()) { synthesizeGLError(GL_INVALID_VALUE, "copyBufferSubData", "buffer overflow"); return; } if ((writeBuffer->getInitialTarget() == GL_ELEMENT_ARRAY_BUFFER && readBuffer->getInitialTarget() != GL_ELEMENT_ARRAY_BUFFER) || (writeBuffer->getInitialTarget() != GL_ELEMENT_ARRAY_BUFFER && readBuffer->getInitialTarget() == GL_ELEMENT_ARRAY_BUFFER)) { synthesizeGLError(GL_INVALID_OPERATION, "copyBufferSubData", "Cannot copy into an element buffer destination from a non-element buffer source"); return; } if (writeBuffer->getInitialTarget() == 0) writeBuffer->setInitialTarget(readBuffer->getInitialTarget()); webContext()->copyBufferSubData(readTarget, writeTarget, static_cast(readOffset), static_cast(writeOffset), static_cast(size)); } void WebGL2RenderingContextBase::getBufferSubData(GLenum target, long long offset, DOMArrayBuffer* returnedData) { if (isContextLost()) return; if (!returnedData) { synthesizeGLError(GL_INVALID_VALUE, "getBufferSubData", "ArrayBuffer cannot be null"); return; } if (!validateValueFitNonNegInt32("getBufferSubData", "offset", offset)) { return; } WebGLBuffer* buffer = validateBufferDataTarget("getBufferSubData", target); if (!buffer) return; if (offset + returnedData->byteLength() > buffer->getSize()) { synthesizeGLError(GL_INVALID_VALUE, "getBufferSubData", "buffer overflow"); return; } void* mappedData = webContext()->mapBufferRange(target, static_cast(offset), returnedData->byteLength(), GL_MAP_READ_BIT); if (!mappedData) return; memcpy(returnedData->data(), mappedData, returnedData->byteLength()); webContext()->unmapBuffer(target); } void WebGL2RenderingContextBase::blitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter) { if (isContextLost()) return; webContext()->blitFramebufferCHROMIUM(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter); } bool WebGL2RenderingContextBase::validateTexFuncLayer(const char* functionName, GLenum texTarget, GLint layer) { if (layer < 0) { synthesizeGLError(GL_INVALID_VALUE, functionName, "layer out of range"); return false; } switch (texTarget) { case GL_TEXTURE_3D: if (layer > m_max3DTextureSize - 1) { synthesizeGLError(GL_INVALID_VALUE, functionName, "layer out of range"); return false; } break; case GL_TEXTURE_2D_ARRAY: if (layer > m_maxArrayTextureLayers - 1) { synthesizeGLError(GL_INVALID_VALUE, functionName, "layer out of range"); return false; } break; default: ASSERT_NOT_REACHED(); return false; } return true; } void WebGL2RenderingContextBase::framebufferTextureLayer(ScriptState* scriptState, GLenum target, GLenum attachment, WebGLTexture* texture, GLint level, GLint layer) { if (isContextLost() || !validateFramebufferFuncParameters("framebufferTextureLayer", target, attachment)) return; if (texture && !texture->validate(contextGroup(), this)) { synthesizeGLError(GL_INVALID_VALUE, "framebufferTextureLayer", "no texture or texture not from this context"); return; } GLenum textarget = texture ? texture->getTarget() : 0; if (texture) { if (textarget != GL_TEXTURE_3D && textarget != GL_TEXTURE_2D_ARRAY) { synthesizeGLError(GL_INVALID_OPERATION, "framebufferTextureLayer", "invalid texture type"); return; } if (!validateTexFuncLayer("framebufferTextureLayer", textarget, layer)) return; if (!validateTexFuncLevel("framebufferTextureLayer", textarget, level)) return; } WebGLFramebuffer* framebufferBinding = getFramebufferBinding(target); if (!framebufferBinding || !framebufferBinding->object()) { synthesizeGLError(GL_INVALID_OPERATION, "framebufferTextureLayer", "no framebuffer bound"); return; } if (attachment == GL_DEPTH_STENCIL_ATTACHMENT) { webContext()->framebufferTextureLayer(target, GL_DEPTH_ATTACHMENT, objectOrZero(texture), level, layer); webContext()->framebufferTextureLayer(target, GL_STENCIL_ATTACHMENT, objectOrZero(texture), level, layer); } else { webContext()->framebufferTextureLayer(target, attachment, objectOrZero(texture), level, layer); } if (attachment == GL_DEPTH_STENCIL_ATTACHMENT) { // On ES3, DEPTH_STENCIL_ATTACHMENT is like an alias for DEPTH_ATTACHMENT + STENCIL_ATTACHMENT. // We divide it here so in WebGLFramebuffer, we don't have to handle DEPTH_STENCIL_ATTACHMENT in WebGL 2. framebufferBinding->setAttachmentForBoundFramebuffer(target, GL_DEPTH_ATTACHMENT, textarget, texture, level, layer); framebufferBinding->setAttachmentForBoundFramebuffer(target, GL_STENCIL_ATTACHMENT, textarget, texture, level, layer); preserveObjectWrapper(scriptState, framebufferBinding, "attachment", GL_DEPTH_ATTACHMENT, texture); preserveObjectWrapper(scriptState, framebufferBinding, "attachment", GL_STENCIL_ATTACHMENT, texture); } else { framebufferBinding->setAttachmentForBoundFramebuffer(target, attachment, textarget, texture, level, layer); preserveObjectWrapper(scriptState, framebufferBinding, "attachment", attachment, texture); } applyStencilTest(); } ScriptValue WebGL2RenderingContextBase::getInternalformatParameter(ScriptState* scriptState, GLenum target, GLenum internalformat, GLenum pname) { if (isContextLost()) return ScriptValue::createNull(scriptState); if (target != GL_RENDERBUFFER) { synthesizeGLError(GL_INVALID_ENUM, "getInternalformatParameter", "invalid target"); return ScriptValue::createNull(scriptState); } bool floatType = false; switch (internalformat) { // Renderbuffer doesn't support unsized internal formats, // though GL_RGB and GL_RGBA are color-renderable. case GL_RGB: case GL_RGBA: // Multisampling is not supported for signed and unsigned integer internal formats. case GL_R8UI: case GL_R8I: case GL_R16UI: case GL_R16I: case GL_R32UI: case GL_R32I: case GL_RG8UI: case GL_RG8I: case GL_RG16UI: case GL_RG16I: case GL_RG32UI: case GL_RG32I: case GL_RGBA8UI: case GL_RGBA8I: case GL_RGB10_A2UI: case GL_RGBA16UI: case GL_RGBA16I: case GL_RGBA32UI: case GL_RGBA32I: return WebGLAny(scriptState, DOMInt32Array::create(0)); case GL_R8: case GL_RG8: case GL_RGB8: case GL_RGB565: case GL_RGBA8: case GL_SRGB8_ALPHA8: case GL_RGB5_A1: case GL_RGBA4: case GL_RGB10_A2: case GL_DEPTH_COMPONENT16: case GL_DEPTH_COMPONENT24: case GL_DEPTH_COMPONENT32F: case GL_DEPTH24_STENCIL8: case GL_DEPTH32F_STENCIL8: case GL_STENCIL_INDEX8: break; case GL_R16F: case GL_RG16F: case GL_RGBA16F: case GL_R32F: case GL_RG32F: case GL_RGBA32F: case GL_R11F_G11F_B10F: if (!extensionEnabled(EXTColorBufferFloatName)) { synthesizeGLError(GL_INVALID_ENUM, "getInternalformatParameter", "invalid internalformat when EXT_color_buffer_float is not enabled"); return ScriptValue::createNull(scriptState); } floatType = true; break; default: synthesizeGLError(GL_INVALID_ENUM, "getInternalformatParameter", "invalid internalformat"); return ScriptValue::createNull(scriptState); } switch (pname) { case GL_SAMPLES: { OwnPtr values; GLint length = -1; if (!floatType) { webContext()->getInternalformativ(target, internalformat, GL_NUM_SAMPLE_COUNTS, 1, &length); if (length <= 0) return WebGLAny(scriptState, DOMInt32Array::create(0)); values = adoptArrayPtr(new GLint[length]); for (GLint ii = 0; ii < length; ++ii) values[ii] = 0; webContext()->getInternalformativ(target, internalformat, GL_SAMPLES, length, values.get()); } else { length = 1; values = adoptArrayPtr(new GLint[1]); values[0] = 1; } return WebGLAny(scriptState, DOMInt32Array::create(values.get(), length)); } default: synthesizeGLError(GL_INVALID_ENUM, "getInternalformatParameter", "invalid parameter name"); return ScriptValue::createNull(scriptState); } } bool WebGL2RenderingContextBase::checkAndTranslateAttachments(const char* functionName, GLenum target, const Vector& attachments, Vector& translatedAttachments) { GLsizei size = attachments.size(); translatedAttachments.resize(size); WebGLFramebuffer* framebufferBinding = getFramebufferBinding(target); ASSERT(framebufferBinding || drawingBuffer()); if (!framebufferBinding) { // For the default framebuffer // Translate GL_COLOR/GL_DEPTH/GL_STENCIL, because the default framebuffer of WebGL is not fb 0, it is an internal fbo for (GLsizei i = 0; i < size; ++i) { switch (attachments[i]) { case GL_COLOR: translatedAttachments[i] = GL_COLOR_ATTACHMENT0; break; case GL_DEPTH: translatedAttachments[i] = GL_DEPTH_ATTACHMENT; break; case GL_STENCIL: translatedAttachments[i] = GL_STENCIL_ATTACHMENT; break; default: synthesizeGLError(GL_INVALID_ENUM, functionName, "invalid attachment"); return false; } } } else { // For the FBO for (GLsizei i = 0; i < size; ++i) { switch (attachments[i]) { case GL_COLOR_ATTACHMENT0: case GL_DEPTH_ATTACHMENT: case GL_STENCIL_ATTACHMENT: case GL_DEPTH_STENCIL_ATTACHMENT: translatedAttachments[i] = attachments[i]; break; default: if (attachments[i] > GL_COLOR_ATTACHMENT0 && attachments[i] < static_cast(GL_COLOR_ATTACHMENT0 + maxColorAttachments())) { translatedAttachments[i] = attachments[i]; break; } synthesizeGLError(GL_INVALID_OPERATION, functionName, "invalid attachment"); return false; } } } return true; } void WebGL2RenderingContextBase::invalidateFramebuffer(GLenum target, const Vector& attachments) { if (isContextLost()) return; if (!validateFramebufferTarget(target)) { synthesizeGLError(GL_INVALID_ENUM, "invalidateFramebuffer", "invalid target"); return; } Vector translatedAttachments; if (!checkAndTranslateAttachments("invalidateFramebuffer", target, attachments, translatedAttachments)) return; webContext()->invalidateFramebuffer(target, translatedAttachments.size(), translatedAttachments.data()); } void WebGL2RenderingContextBase::invalidateSubFramebuffer(GLenum target, const Vector& attachments, GLint x, GLint y, GLsizei width, GLsizei height) { if (isContextLost()) return; if (!validateFramebufferTarget(target)) { synthesizeGLError(GL_INVALID_ENUM, "invalidateFramebuffer", "invalid target"); return; } if (width < 0 || height < 0) { synthesizeGLError(GL_INVALID_VALUE, "invalidateSubFramebuffer", "invalid width or height"); return; } Vector translatedAttachments; if (!checkAndTranslateAttachments("invalidateSubFramebuffer", target, attachments, translatedAttachments)) return; webContext()->invalidateSubFramebuffer(target, translatedAttachments.size(), translatedAttachments.data(), x, y, width, height); } void WebGL2RenderingContextBase::readBuffer(GLenum mode) { if (isContextLost()) return; switch (mode) { case GL_BACK: case GL_NONE: case GL_COLOR_ATTACHMENT0: break; default: if (mode > GL_COLOR_ATTACHMENT0 && mode < static_cast(GL_COLOR_ATTACHMENT0 + maxColorAttachments())) break; synthesizeGLError(GL_INVALID_ENUM, "readBuffer", "invalid read buffer"); return; } WebGLFramebuffer* readFramebufferBinding = getFramebufferBinding(GL_READ_FRAMEBUFFER); if (!readFramebufferBinding) { ASSERT(drawingBuffer()); if (mode != GL_BACK && mode != GL_NONE) { synthesizeGLError(GL_INVALID_OPERATION, "readBuffer", "invalid read buffer"); return; } m_readBufferOfDefaultFramebuffer = mode; // translate GL_BACK to GL_COLOR_ATTACHMENT0, because the default // framebuffer for WebGL is not fb 0, it is an internal fbo. if (mode == GL_BACK) mode = GL_COLOR_ATTACHMENT0; } else { if (mode == GL_BACK) { synthesizeGLError(GL_INVALID_OPERATION, "readBuffer", "invalid read buffer"); return; } readFramebufferBinding->readBuffer(mode); } webContext()->readBuffer(mode); } void WebGL2RenderingContextBase::pixelStorei(GLenum pname, GLint param) { if (isContextLost()) return; if (param < 0) { synthesizeGLError(GL_INVALID_VALUE, "pixelStorei", "negative value"); return; } switch (pname) { case GL_PACK_ROW_LENGTH: m_packRowLength = param; break; case GL_PACK_SKIP_PIXELS: m_packSkipPixels = param; break; case GL_PACK_SKIP_ROWS: m_packSkipRows = param; break; case GL_UNPACK_ROW_LENGTH: m_unpackRowLength = param; break; case GL_UNPACK_IMAGE_HEIGHT: m_unpackImageHeight = param; break; case GL_UNPACK_SKIP_PIXELS: m_unpackSkipPixels = param; break; case GL_UNPACK_SKIP_ROWS: m_unpackSkipRows = param; break; case GL_UNPACK_SKIP_IMAGES: m_unpackSkipImages = param; break; default: WebGLRenderingContextBase::pixelStorei(pname, param); return; } webContext()->pixelStorei(pname, param); } void WebGL2RenderingContextBase::readPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, DOMArrayBufferView* pixels) { if (isContextLost()) return; if (m_boundPixelPackBuffer.get()) { synthesizeGLError(GL_INVALID_OPERATION, "readPixels", "PIXEL_PACK buffer should not be bound"); return; } WebGLRenderingContextBase::readPixels(x, y, width, height, format, type, pixels); } void WebGL2RenderingContextBase::readPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, long long offset) { if (isContextLost()) return; // Due to WebGL's same-origin restrictions, it is not possible to // taint the origin using the WebGL API. ASSERT(canvas()->originClean()); if (!validateValueFitNonNegInt32("readPixels", "offset", offset)) return; WebGLBuffer* buffer = m_boundPixelPackBuffer.get(); if (!buffer) { synthesizeGLError(GL_INVALID_OPERATION, "readPixels", "no PIXEL_PACK buffer bound"); return; } const char* reason = "framebuffer incomplete"; WebGLFramebuffer* framebuffer = getReadFramebufferBinding(); if (framebuffer && framebuffer->checkDepthStencilStatus(&reason) != GL_FRAMEBUFFER_COMPLETE) { synthesizeGLError(GL_INVALID_FRAMEBUFFER_OPERATION, "readPixels", reason); return; } long long size = buffer->getSize() - offset; // If size is negative, or size is not large enough to store pixels, those cases // are handled by validateReadPixelsFuncParameters to generate INVALID_OPERATION. if (!validateReadPixelsFuncParameters(width, height, format, type, nullptr, size)) return; clearIfComposited(); { ScopedDrawingBufferBinder binder(drawingBuffer(), framebuffer); webContext()->readPixels(x, y, width, height, format, type, reinterpret_cast(offset)); } } void WebGL2RenderingContextBase::renderbufferStorageImpl( GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, const char* functionName) { switch (internalformat) { case GL_R8UI: case GL_R8I: case GL_R16UI: case GL_R16I: case GL_R32UI: case GL_R32I: case GL_RG8UI: case GL_RG8I: case GL_RG16UI: case GL_RG16I: case GL_RG32UI: case GL_RG32I: case GL_RGBA8UI: case GL_RGBA8I: case GL_RGB10_A2UI: case GL_RGBA16UI: case GL_RGBA16I: case GL_RGBA32UI: case GL_RGBA32I: if (samples > 0) { synthesizeGLError(GL_INVALID_OPERATION, functionName, "for integer formats, samples > 0"); return; } case GL_R8: case GL_RG8: case GL_RGB8: case GL_RGB565: case GL_RGBA8: case GL_SRGB8_ALPHA8: case GL_RGB5_A1: case GL_RGBA4: case GL_RGB10_A2: case GL_DEPTH_COMPONENT16: case GL_DEPTH_COMPONENT24: case GL_DEPTH_COMPONENT32F: case GL_DEPTH24_STENCIL8: case GL_DEPTH32F_STENCIL8: case GL_STENCIL_INDEX8: if (!samples) { webContext()->renderbufferStorage(target, internalformat, width, height); } else { GLint maxNumberOfSamples = 0; webContext()->getInternalformativ(target, internalformat, GL_SAMPLES, 1, &maxNumberOfSamples); if (samples > maxNumberOfSamples) { synthesizeGLError(GL_INVALID_OPERATION, functionName, "samples out of range"); return; } webContext()->renderbufferStorageMultisampleCHROMIUM( target, samples, internalformat, width, height); } break; case GL_DEPTH_STENCIL: // To be WebGL 1 backward compatible. if (samples > 0) { synthesizeGLError(GL_INVALID_ENUM, functionName, "invalid internalformat"); return; } webContext()->renderbufferStorage(target, GL_DEPTH24_STENCIL8, width, height); break; case GL_R16F: case GL_RG16F: case GL_RGBA16F: case GL_R32F: case GL_RG32F: case GL_RGBA32F: case GL_R11F_G11F_B10F: if (!extensionEnabled(EXTColorBufferFloatName)) { synthesizeGLError(GL_INVALID_ENUM, functionName, "EXT_color_buffer_float not enabled"); return; } if (samples) { synthesizeGLError(GL_INVALID_VALUE, functionName, "multisampled float buffers not supported"); return; } webContext()->renderbufferStorage(target, internalformat, width, height); break; default: synthesizeGLError(GL_INVALID_ENUM, functionName, "invalid internalformat"); return; } m_renderbufferBinding->setInternalFormat(internalformat); m_renderbufferBinding->setSize(width, height); } void WebGL2RenderingContextBase::renderbufferStorageMultisample(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height) { const char* functionName = "renderbufferStorageMultisample"; if (isContextLost()) return; if (target != GL_RENDERBUFFER) { synthesizeGLError(GL_INVALID_ENUM, functionName, "invalid target"); return; } if (!m_renderbufferBinding || !m_renderbufferBinding->object()) { synthesizeGLError(GL_INVALID_OPERATION, functionName, "no bound renderbuffer"); return; } if (!validateSize("renderbufferStorage", width, height)) return; if (samples < 0) { synthesizeGLError(GL_INVALID_VALUE, functionName, "samples < 0"); return; } renderbufferStorageImpl(target, samples, internalformat, width, height, functionName); applyStencilTest(); } void WebGL2RenderingContextBase::resetUnpackParameters() { WebGLRenderingContextBase::resetUnpackParameters(); if (!m_unpackRowLength) webContext()->pixelStorei(GL_UNPACK_ROW_LENGTH, 0); if (!m_unpackImageHeight) webContext()->pixelStorei(GL_UNPACK_IMAGE_HEIGHT, 0); if (!m_unpackSkipPixels) webContext()->pixelStorei(GL_UNPACK_SKIP_PIXELS, 0); if (!m_unpackSkipRows) webContext()->pixelStorei(GL_UNPACK_SKIP_ROWS, 0); if (!m_unpackSkipImages) webContext()->pixelStorei(GL_UNPACK_SKIP_IMAGES, 0); } void WebGL2RenderingContextBase::restoreUnpackParameters() { WebGLRenderingContextBase::restoreUnpackParameters(); if (!m_unpackRowLength) webContext()->pixelStorei(GL_UNPACK_ROW_LENGTH, m_unpackRowLength); if (!m_unpackImageHeight) webContext()->pixelStorei(GL_UNPACK_IMAGE_HEIGHT, m_unpackImageHeight); if (!m_unpackSkipPixels) webContext()->pixelStorei(GL_UNPACK_SKIP_PIXELS, m_unpackSkipPixels); if (!m_unpackSkipRows) webContext()->pixelStorei(GL_UNPACK_SKIP_ROWS, m_unpackSkipRows); if (!m_unpackSkipImages) webContext()->pixelStorei(GL_UNPACK_SKIP_IMAGES, m_unpackSkipImages); } /* Texture objects */ bool WebGL2RenderingContextBase::validateTexStorage(const char* functionName, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, TexStorageType functionType) { if (functionType == TexStorageType2D) { if (target != GL_TEXTURE_2D && target != GL_TEXTURE_CUBE_MAP) { synthesizeGLError(GL_INVALID_ENUM, functionName, "invalid 2D target"); return false; } } else { if (target != GL_TEXTURE_3D && target != GL_TEXTURE_2D_ARRAY) { synthesizeGLError(GL_INVALID_ENUM, functionName, "invalid 3D target"); return false; } } if (functionType == TexStorageType3D && target != GL_TEXTURE_2D_ARRAY && m_compressedTextureFormatsETC2EAC.find(internalformat) != m_compressedTextureFormatsETC2EAC.end()) { synthesizeGLError(GL_INVALID_OPERATION, functionName, "target for ETC2/EAC internal formats must be TEXTURE_2D_ARRAY"); return false; } if (m_supportedInternalFormatsStorage.find(internalformat) == m_supportedInternalFormatsStorage.end()) { synthesizeGLError(GL_INVALID_ENUM, functionName, "invalid internalformat"); return false; } if (width <= 0 || height <= 0 || depth <= 0) { synthesizeGLError(GL_INVALID_VALUE, functionName, "invalid dimensions"); return false; } if (levels <= 0) { synthesizeGLError(GL_INVALID_VALUE, functionName, "invalid levels"); return false; } if (target == GL_TEXTURE_3D) { if (levels > log2(std::max(std::max(width, height), depth)) + 1) { synthesizeGLError(GL_INVALID_OPERATION, functionName, "to many levels"); return false; } } else { if (levels > log2(std::max(width, height)) + 1) { synthesizeGLError(GL_INVALID_OPERATION, functionName, "to many levels"); return false; } } return true; } void WebGL2RenderingContextBase::texImage2D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, GLintptr offset) { if (isContextLost()) return; if (!validateTexture2DBinding("texImage2D", target)) return; if (!validateTexFunc("texImage2D", TexImage, SourceUnpackBuffer, target, level, internalformat, width, height, 1, border, format, type, 0, 0, 0)) return; if (!validateValueFitNonNegInt32("texImage2D", "offset", offset)) return; webContext()->texImage2D(target, level, convertTexInternalFormat(internalformat, type), width, height, border, format, type, reinterpret_cast(offset)); } void WebGL2RenderingContextBase::texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLintptr offset) { if (isContextLost()) return; if (!validateTexture2DBinding("texSubImage2D", target)) return; if (!validateTexFunc("texSubImage2D", TexSubImage, SourceUnpackBuffer, target, level, 0, width, height, 1, 0, format, type, xoffset, yoffset, 0)) return; if (!validateValueFitNonNegInt32("texSubImage2D", "offset", offset)) return; webContext()->texSubImage2D(target, level, xoffset, yoffset, width, height, format, type, reinterpret_cast(offset)); } void WebGL2RenderingContextBase::texImage2D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, DOMArrayBufferView* data) { WebGLRenderingContextBase::texImage2D(target, level, internalformat, width, height, border, format, type, data); } void WebGL2RenderingContextBase::texImage2D(GLenum target, GLint level, GLint internalformat, GLenum format, GLenum type, ImageData* imageData) { WebGLRenderingContextBase::texImage2D(target, level, internalformat, format, type, imageData); } void WebGL2RenderingContextBase::texImage2D(GLenum target, GLint level, GLint internalformat, GLenum format, GLenum type, HTMLImageElement* image, ExceptionState& exceptionState) { WebGLRenderingContextBase::texImage2D(target, level, internalformat, format, type, image, exceptionState); } void WebGL2RenderingContextBase::texImage2D(GLenum target, GLint level, GLint internalformat, GLenum format, GLenum type, HTMLCanvasElement* canvas, ExceptionState& exceptionState) { WebGLRenderingContextBase::texImage2D(target, level, internalformat, format, type, canvas, exceptionState); } void WebGL2RenderingContextBase::texImage2D(GLenum target, GLint level, GLint internalformat, GLenum format, GLenum type, HTMLVideoElement* video, ExceptionState& exceptionState) { WebGLRenderingContextBase::texImage2D(target, level, internalformat, format, type, video, exceptionState); } void WebGL2RenderingContextBase::texImage2D(GLenum target, GLint level, GLint internalformat, GLenum format, GLenum type, PassRefPtrWillBeRawPtr imageBitMap, ExceptionState& exceptionState) { WebGLRenderingContextBase::texImage2D(target, level, internalformat, format, type, imageBitMap, exceptionState); } void WebGL2RenderingContextBase::texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, DOMArrayBufferView* pixels) { WebGLRenderingContextBase::texSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels); } void WebGL2RenderingContextBase::texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLenum format, GLenum type, ImageData* pixels) { WebGLRenderingContextBase::texSubImage2D(target, level, xoffset, yoffset, format, type, pixels); } void WebGL2RenderingContextBase::texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLenum format, GLenum type, HTMLImageElement* image, ExceptionState& exceptionState) { WebGLRenderingContextBase::texSubImage2D(target, level, xoffset, yoffset, format, type, image, exceptionState); } void WebGL2RenderingContextBase::texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLenum format, GLenum type, HTMLCanvasElement* canvas, ExceptionState& exceptionState) { WebGLRenderingContextBase::texSubImage2D(target, level, xoffset, yoffset, format, type, canvas, exceptionState); } void WebGL2RenderingContextBase::texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLenum format, GLenum type, HTMLVideoElement* video, ExceptionState& exceptionState) { WebGLRenderingContextBase::texSubImage2D(target, level, xoffset, yoffset, format, type, video, exceptionState); } void WebGL2RenderingContextBase::texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLenum format, GLenum type, PassRefPtrWillBeRawPtr bitmap, ExceptionState& exceptionState) { WebGLRenderingContextBase::texSubImage2D(target, level, xoffset, yoffset, format, type, bitmap, exceptionState); } void WebGL2RenderingContextBase::texStorage2D(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height) { if (isContextLost() || !validateTexStorage("texStorage2D", target, levels, internalformat, width, height, 1, TexStorageType2D)) return; webContext()->texStorage2DEXT(target, levels, internalformat, width, height); } void WebGL2RenderingContextBase::texStorage3D(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth) { if (isContextLost() || !validateTexStorage("texStorage3D", target, levels, internalformat, width, height, depth, TexStorageType3D)) return; webContext()->texStorage3D(target, levels, internalformat, width, height, depth); } void WebGL2RenderingContextBase::texImage3D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, DOMArrayBufferView* pixels) { if (isContextLost()) return; if (!validateTexture3DBinding("texImage3D", target)) return; if (!validateTexFunc("texImage3D", TexImage, SourceArrayBufferView, target, level, internalformat, width, height, depth, border, format, type, 0, 0, 0)) return; if (!validateTexFuncData("texImage3D", Tex3D, level, width, height, depth, format, type, pixels, NullAllowed)) return; void* data = pixels ? pixels->baseAddress() : 0; Vector tempData; if (data && (m_unpackFlipY || m_unpackPremultiplyAlpha)) { // FIXME: WebGLImageConversion needs to be updated to accept image depth. notImplemented(); return; } webContext()->texImage3D(target, level, convertTexInternalFormat(internalformat, type), width, height, depth, border, format, type, data); } void WebGL2RenderingContextBase::texImage3D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, GLintptr offset) { if (isContextLost()) return; if (!validateTexture3DBinding("texImage3D", target)) return; if (!validateTexFunc("texImage3D", TexImage, SourceUnpackBuffer, target, level, internalformat, width, height, depth, border, format, type, 0, 0, 0)) return; if (!validateValueFitNonNegInt32("texImage3D", "offset", offset)) return; webContext()->texImage3D(target, level, convertTexInternalFormat(internalformat, type), width, height, depth, border, format, type, reinterpret_cast(offset)); } void WebGL2RenderingContextBase::texSubImage3DImpl(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLenum format, GLenum type, Image* image, WebGLImageConversion::ImageHtmlDomSource domSource, bool flipY, bool premultiplyAlpha) { // All calling functions check isContextLost, so a duplicate check is not needed here. if (type == GL_UNSIGNED_INT_10F_11F_11F_REV) { // The UNSIGNED_INT_10F_11F_11F_REV type pack/unpack isn't implemented. type = GL_FLOAT; } Vector data; WebGLImageConversion::ImageExtractor imageExtractor(image, domSource, premultiplyAlpha, m_unpackColorspaceConversion == GL_NONE); if (!imageExtractor.imagePixelData()) { synthesizeGLError(GL_INVALID_VALUE, "texSubImage3D", "bad image"); return; } WebGLImageConversion::DataFormat sourceDataFormat = imageExtractor.imageSourceFormat(); WebGLImageConversion::AlphaOp alphaOp = imageExtractor.imageAlphaOp(); const void* imagePixelData = imageExtractor.imagePixelData(); bool needConversion = true; if (type == GL_UNSIGNED_BYTE && sourceDataFormat == WebGLImageConversion::DataFormatRGBA8 && format == GL_RGBA && alphaOp == WebGLImageConversion::AlphaDoNothing && !flipY) { needConversion = false; } else { if (!WebGLImageConversion::packImageData(image, imagePixelData, format, type, flipY, alphaOp, sourceDataFormat, imageExtractor.imageWidth(), imageExtractor.imageHeight(), imageExtractor.imageSourceUnpackAlignment(), data)) { synthesizeGLError(GL_INVALID_VALUE, "texSubImage3D", "bad image data"); return; } } resetUnpackParameters(); webContext()->texSubImage3D(target, level, xoffset, yoffset, zoffset, imageExtractor.imageWidth(), imageExtractor.imageHeight(), 1, format, type, needConversion ? data.data() : imagePixelData); restoreUnpackParameters(); } void WebGL2RenderingContextBase::texSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, DOMArrayBufferView* pixels) { if (isContextLost()) return; if (!validateTexture3DBinding("texSubImage3D", target)) return; if (!validateTexFunc("texSubImage3D", TexSubImage, SourceArrayBufferView, target, level, 0, width, height, depth, 0, format, type, xoffset, yoffset, zoffset)) return; if (!validateTexFuncData("texSubImage3D", Tex3D, level, width, height, depth, format, type, pixels, NullNotAllowed)) return; void* data = pixels->baseAddress(); Vector tempData; bool changeUnpackParameters = false; if (data && (m_unpackFlipY || m_unpackPremultiplyAlpha)) { // FIXME: WebGLImageConversion needs to be updated to accept image depth. notImplemented(); changeUnpackParameters = true; } if (changeUnpackParameters) resetUnpackParameters(); webContext()->texSubImage3D(target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, data); if (changeUnpackParameters) restoreUnpackParameters(); } void WebGL2RenderingContextBase::texSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, GLintptr offset) { if (isContextLost()) return; if (!validateTexture3DBinding("texSubImage3D", target)) return; if (!validateTexFunc("texSubImage3D", TexSubImage, SourceUnpackBuffer, target, level, 0, width, height, depth, 0, format, type, xoffset, yoffset, zoffset)) return; if (!validateValueFitNonNegInt32("texSubImage3D", "offset", offset)) return; webContext()->texSubImage3D(target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, reinterpret_cast(offset)); } void WebGL2RenderingContextBase::texSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLenum format, GLenum type, ImageData* pixels) { if (isContextLost()) return; if (!pixels) { synthesizeGLError(GL_INVALID_VALUE, "texSubImage3D", "no image data"); return; } if (pixels->data()->bufferBase()->isNeutered()) { synthesizeGLError(GL_INVALID_VALUE, "texSubImage3D", "The source data has been neutered."); return; } if (!validateTexture3DBinding("texSubImage3D", target)) return; if (!validateTexFunc("texSubImage3D", TexSubImage, SourceImageData, target, level, 0, pixels->width(), pixels->height(), 1, 0, format, type, xoffset, yoffset, zoffset)) return; if (type == GL_UNSIGNED_INT_10F_11F_11F_REV) { // The UNSIGNED_INT_10F_11F_11F_REV type pack/unpack isn't implemented. type = GL_FLOAT; } Vector data; bool needConversion = true; // The data from ImageData is always of format RGBA8. // No conversion is needed if destination format is RGBA and type is USIGNED_BYTE and no Flip or Premultiply operation is required. if (format == GL_RGBA && type == GL_UNSIGNED_BYTE && !m_unpackFlipY && !m_unpackPremultiplyAlpha) { needConversion = false; } else { if (!WebGLImageConversion::extractImageData(pixels->data()->data(), pixels->size(), format, type, m_unpackFlipY, m_unpackPremultiplyAlpha, data)) { synthesizeGLError(GL_INVALID_VALUE, "texSubImage3D", "bad image data"); return; } } resetUnpackParameters(); webContext()->texSubImage3D(target, level, xoffset, yoffset, zoffset, pixels->width(), pixels->height(), 1, format, type, needConversion ? data.data() : pixels->data()->data()); restoreUnpackParameters(); } void WebGL2RenderingContextBase::texSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLenum format, GLenum type, HTMLImageElement* image, ExceptionState& exceptionState) { if (isContextLost()) return; if (!validateHTMLImageElement("texSubImage3D", image, exceptionState)) return; if (!validateTexture3DBinding("texSubImage3D", target)) return; RefPtr imageForRender = image->cachedImage()->getImage(); if (imageForRender->isSVGImage()) imageForRender = drawImageIntoBuffer(imageForRender.get(), image->width(), image->height(), "texSubImage3D"); if (!imageForRender || !validateTexFunc("texSubImage3D", TexSubImage, SourceHTMLImageElement, target, level, 0, imageForRender->width(), imageForRender->height(), 1, 0, format, type, xoffset, yoffset, zoffset)) return; texSubImage3DImpl(target, level, xoffset, yoffset, zoffset, format, type, imageForRender.get(), WebGLImageConversion::HtmlDomImage, m_unpackFlipY, m_unpackPremultiplyAlpha); } void WebGL2RenderingContextBase::texSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLenum format, GLenum type, HTMLCanvasElement* canvas, ExceptionState& exceptionState) { if (isContextLost()) return; if (!validateHTMLCanvasElement("texSubImage3D", canvas, exceptionState)) return; if (!validateTexture3DBinding("texSubImage3D", target)) return; if (!validateTexFunc("texSubImage3D", TexSubImage, SourceHTMLCanvasElement, target, level, 0, canvas->width(), canvas->height(), 1, 0, format, type, xoffset, yoffset, zoffset)) return; // FIXME: Implement GPU-to-GPU copy path (crbug.com/586269). texSubImage3DImpl(target, level, xoffset, yoffset, zoffset, format, type, canvas->copiedImage(FrontBuffer, PreferAcceleration).get(), WebGLImageConversion::HtmlDomCanvas, m_unpackFlipY, m_unpackPremultiplyAlpha); } void WebGL2RenderingContextBase::texSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLenum format, GLenum type, HTMLVideoElement* video, ExceptionState& exceptionState) { if (isContextLost()) return; if (!validateHTMLVideoElement("texSubImage3D", video, exceptionState)) return; if (!validateTexture3DBinding("texSubImage3D", target)) return; if (!validateTexFunc("texSubImage3D", TexSubImage, SourceHTMLVideoElement, target, level, 0, video->videoWidth(), video->videoHeight(), 1, 0, format, type, xoffset, yoffset, zoffset)) return; RefPtr image = videoFrameToImage(video); if (!image) return; texSubImage3DImpl(target, level, xoffset, yoffset, zoffset, format, type, image.get(), WebGLImageConversion::HtmlDomVideo, m_unpackFlipY, m_unpackPremultiplyAlpha); } void WebGL2RenderingContextBase::texSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLenum format, GLenum type, PassRefPtrWillBeRawPtr bitmap, ExceptionState& exceptionState) { if (isContextLost()) return; if (!validateImageBitmap("texSubImage3D", bitmap.get(), exceptionState)) return; if (!validateTexture3DBinding("texSubImage3D", target)) return; if (!validateTexFunc("texSubImage3D", TexSubImage, SourceImageBitmap, target, level, 0, bitmap->width(), bitmap->height(), 1, 0, format, type, xoffset, yoffset, zoffset)) return; if (type == GL_UNSIGNED_INT_10F_11F_11F_REV) { // The UNSIGNED_INT_10F_11F_11F_REV type pack/unpack isn't implemented. type = GL_FLOAT; } ASSERT(bitmap->bitmapImage()); OwnPtr pixelData = bitmap->copyBitmapData(bitmap->isPremultiplied() ? PremultiplyAlpha : DontPremultiplyAlpha); Vector data; bool needConversion = true; if (format == GL_RGBA && type == GL_UNSIGNED_BYTE) { needConversion = false; } else { // In the case of ImageBitmap, we do not need to apply flipY or premultiplyAlpha. if (!WebGLImageConversion::extractImageData(pixelData.get(), bitmap->size(), format, type, false, false, data)) { synthesizeGLError(GL_INVALID_VALUE, "texSubImage3D", "bad image data"); return; } } resetUnpackParameters(); webContext()->texSubImage3D(target, level, xoffset, yoffset, zoffset, bitmap->width(), bitmap->height(), 1, format, type, needConversion ? data.data() : pixelData.get()); restoreUnpackParameters(); } void WebGL2RenderingContextBase::copyTexSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height) { if (isContextLost()) return; if (!validateTexture3DBinding("copyTexSubImage3D", target)) return; WebGLFramebuffer* readFramebufferBinding = nullptr; if (!validateReadBufferAndGetInfo("copyTexSubImage3D", readFramebufferBinding)) return; clearIfComposited(); ScopedDrawingBufferBinder binder(drawingBuffer(), readFramebufferBinding); webContext()->copyTexSubImage3D(target, level, xoffset, yoffset, zoffset, x, y, width, height); } void WebGL2RenderingContextBase::compressedTexImage3D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, DOMArrayBufferView* data) { if (isContextLost()) return; if (!validateTexture3DBinding("compressedTexImage3D", target)) return; if (!validateCompressedTexFormat("compressedTexImage3D", internalformat)) return; webContext()->compressedTexImage3D(target, level, internalformat, width, height, depth, border, data->byteLength(), data->baseAddress()); } void WebGL2RenderingContextBase::compressedTexSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, DOMArrayBufferView* data) { if (isContextLost()) return; if (!validateTexture3DBinding("compressedTexSubImage3D", target)) return; if (!validateCompressedTexFormat("compressedTexSubImage3D", format)) return; webContext()->compressedTexSubImage3D(target, level, xoffset, yoffset, zoffset, width, height, depth, format, data->byteLength(), data->baseAddress()); } GLint WebGL2RenderingContextBase::getFragDataLocation(WebGLProgram* program, const String& name) { if (isContextLost() || !validateWebGLObject("getFragDataLocation", program)) return -1; return webContext()->getFragDataLocation(objectOrZero(program), name.utf8().data()); } void WebGL2RenderingContextBase::uniform1ui(const WebGLUniformLocation* location, GLuint v0) { if (isContextLost() || !location) return; if (location->program() != m_currentProgram) { synthesizeGLError(GL_INVALID_OPERATION, "uniform1ui", "location not for current program"); return; } webContext()->uniform1ui(location->location(), v0); } void WebGL2RenderingContextBase::uniform2ui(const WebGLUniformLocation* location, GLuint v0, GLuint v1) { if (isContextLost() || !location) return; if (location->program() != m_currentProgram) { synthesizeGLError(GL_INVALID_OPERATION, "uniform2ui", "location not for current program"); return; } webContext()->uniform2ui(location->location(), v0, v1); } void WebGL2RenderingContextBase::uniform3ui(const WebGLUniformLocation* location, GLuint v0, GLuint v1, GLuint v2) { if (isContextLost() || !location) return; if (location->program() != m_currentProgram) { synthesizeGLError(GL_INVALID_OPERATION, "uniform3ui", "location not for current program"); return; } webContext()->uniform3ui(location->location(), v0, v1, v2); } void WebGL2RenderingContextBase::uniform4ui(const WebGLUniformLocation* location, GLuint v0, GLuint v1, GLuint v2, GLuint v3) { if (isContextLost() || !location) return; if (location->program() != m_currentProgram) { synthesizeGLError(GL_INVALID_OPERATION, "uniform4ui", "location not for current program"); return; } webContext()->uniform4ui(location->location(), v0, v1, v2, v3); } void WebGL2RenderingContextBase::uniform1uiv(const WebGLUniformLocation* location, const FlexibleUint32ArrayView& v) { if (isContextLost() || !validateUniformParameters("uniform1uiv", location, v, 1)) return; webContext()->uniform1uiv(location->location(), v.length(), v.dataMaybeOnStack()); } void WebGL2RenderingContextBase::uniform1uiv(const WebGLUniformLocation* location, Vector& value) { if (isContextLost() || !validateUniformParameters("uniform1uiv", location, value.data(), value.size(), 1)) return; webContext()->uniform1uiv(location->location(), value.size(), value.data()); } void WebGL2RenderingContextBase::uniform2uiv(const WebGLUniformLocation* location, const FlexibleUint32ArrayView& v) { if (isContextLost() || !validateUniformParameters("uniform2uiv", location, v, 2)) return; webContext()->uniform2uiv(location->location(), v.length() >> 1, v.dataMaybeOnStack()); } void WebGL2RenderingContextBase::uniform2uiv(const WebGLUniformLocation* location, Vector& value) { if (isContextLost() || !validateUniformParameters("uniform2uiv", location, value.data(), value.size(), 2)) return; webContext()->uniform2uiv(location->location(), value.size() / 2, value.data()); } void WebGL2RenderingContextBase::uniform3uiv(const WebGLUniformLocation* location, const FlexibleUint32ArrayView& v) { if (isContextLost() || !validateUniformParameters("uniform3uiv", location, v, 3)) return; webContext()->uniform3uiv(location->location(), v.length() / 3, v.dataMaybeOnStack()); } void WebGL2RenderingContextBase::uniform3uiv(const WebGLUniformLocation* location, Vector& value) { if (isContextLost() || !validateUniformParameters("uniform3uiv", location, value.data(), value.size(), 3)) return; webContext()->uniform3uiv(location->location(), value.size() / 3, value.data()); } void WebGL2RenderingContextBase::uniform4uiv(const WebGLUniformLocation* location, const FlexibleUint32ArrayView& v) { if (isContextLost() || !validateUniformParameters("uniform4uiv", location, v, 4)) return; webContext()->uniform4uiv(location->location(), v.length() >> 2, v.dataMaybeOnStack()); } void WebGL2RenderingContextBase::uniform4uiv(const WebGLUniformLocation* location, Vector& value) { if (isContextLost() || !validateUniformParameters("uniform4uiv", location, value.data(), value.size(), 4)) return; webContext()->uniform4uiv(location->location(), value.size() / 4, value.data()); } void WebGL2RenderingContextBase::uniformMatrix2x3fv(const WebGLUniformLocation* location, GLboolean transpose, DOMFloat32Array* value) { if (isContextLost() || !validateUniformMatrixParameters("uniformMatrix2x3fv", location, transpose, value, 6)) return; webContext()->uniformMatrix2x3fv(location->location(), value->length() / 6, transpose, value->data()); } void WebGL2RenderingContextBase::uniformMatrix2x3fv(const WebGLUniformLocation* location, GLboolean transpose, Vector& value) { if (isContextLost() || !validateUniformMatrixParameters("uniformMatrix2x3fv", location, transpose, value.data(), value.size(), 6)) return; webContext()->uniformMatrix2x3fv(location->location(), value.size() / 6, transpose, value.data()); } void WebGL2RenderingContextBase::uniformMatrix3x2fv(const WebGLUniformLocation* location, GLboolean transpose, DOMFloat32Array* value) { if (isContextLost() || !validateUniformMatrixParameters("uniformMatrix3x2fv", location, transpose, value, 6)) return; webContext()->uniformMatrix3x2fv(location->location(), value->length() / 6, transpose, value->data()); } void WebGL2RenderingContextBase::uniformMatrix3x2fv(const WebGLUniformLocation* location, GLboolean transpose, Vector& value) { if (isContextLost() || !validateUniformMatrixParameters("uniformMatrix3x2fv", location, transpose, value.data(), value.size(), 6)) return; webContext()->uniformMatrix3x2fv(location->location(), value.size() / 6, transpose, value.data()); } void WebGL2RenderingContextBase::uniformMatrix2x4fv(const WebGLUniformLocation* location, GLboolean transpose, DOMFloat32Array* value) { if (isContextLost() || !validateUniformMatrixParameters("uniformMatrix2x4fv", location, transpose, value, 8)) return; webContext()->uniformMatrix2x4fv(location->location(), value->length() / 8, transpose, value->data()); } void WebGL2RenderingContextBase::uniformMatrix2x4fv(const WebGLUniformLocation* location, GLboolean transpose, Vector& value) { if (isContextLost() || !validateUniformMatrixParameters("uniformMatrix2x4fv", location, transpose, value.data(), value.size(), 8)) return; webContext()->uniformMatrix2x4fv(location->location(), value.size() / 8, transpose, value.data()); } void WebGL2RenderingContextBase::uniformMatrix4x2fv(const WebGLUniformLocation* location, GLboolean transpose, DOMFloat32Array* value) { if (isContextLost() || !validateUniformMatrixParameters("uniformMatrix4x2fv", location, transpose, value, 8)) return; webContext()->uniformMatrix4x2fv(location->location(), value->length() / 8, transpose, value->data()); } void WebGL2RenderingContextBase::uniformMatrix4x2fv(const WebGLUniformLocation* location, GLboolean transpose, Vector& value) { if (isContextLost() || !validateUniformMatrixParameters("uniformMatrix4x2fv", location, transpose, value.data(), value.size(), 8)) return; webContext()->uniformMatrix4x2fv(location->location(), value.size() / 8, transpose, value.data()); } void WebGL2RenderingContextBase::uniformMatrix3x4fv(const WebGLUniformLocation* location, GLboolean transpose, DOMFloat32Array* value) { if (isContextLost() || !validateUniformMatrixParameters("uniformMatrix3x4fv", location, transpose, value, 12)) return; webContext()->uniformMatrix3x4fv(location->location(), value->length() / 12, transpose, value->data()); } void WebGL2RenderingContextBase::uniformMatrix3x4fv(const WebGLUniformLocation* location, GLboolean transpose, Vector& value) { if (isContextLost() || !validateUniformMatrixParameters("uniformMatrix3x4fv", location, transpose, value.data(), value.size(), 12)) return; webContext()->uniformMatrix3x4fv(location->location(), value.size() / 12, transpose, value.data()); } void WebGL2RenderingContextBase::uniformMatrix4x3fv(const WebGLUniformLocation* location, GLboolean transpose, DOMFloat32Array* value) { if (isContextLost() || !validateUniformMatrixParameters("uniformMatrix4x3fv", location, transpose, value, 12)) return; webContext()->uniformMatrix4x3fv(location->location(), value->length() / 12, transpose, value->data()); } void WebGL2RenderingContextBase::uniformMatrix4x3fv(const WebGLUniformLocation* location, GLboolean transpose, Vector& value) { if (isContextLost() || !validateUniformMatrixParameters("uniformMatrix4x3fv", location, transpose, value.data(), value.size(), 12)) return; webContext()->uniformMatrix4x3fv(location->location(), value.size() / 12, transpose, value.data()); } void WebGL2RenderingContextBase::vertexAttribI4i(GLuint index, GLint x, GLint y, GLint z, GLint w) { if (isContextLost()) return; webContext()->vertexAttribI4i(index, x, y, z, w); setVertexAttribType(index, Int32ArrayType); } void WebGL2RenderingContextBase::vertexAttribI4iv(GLuint index, const DOMInt32Array* v) { if (isContextLost()) return; if (!v || v->length() < 4) { synthesizeGLError(GL_INVALID_VALUE, "vertexAttribI4iv", "invalid array"); return; } webContext()->vertexAttribI4iv(index, v->data()); setVertexAttribType(index, Int32ArrayType); } void WebGL2RenderingContextBase::vertexAttribI4iv(GLuint index, const Vector& v) { if (isContextLost()) return; if (v.size() < 4) { synthesizeGLError(GL_INVALID_VALUE, "vertexAttribI4iv", "invalid array"); return; } webContext()->vertexAttribI4iv(index, v.data()); setVertexAttribType(index, Int32ArrayType); } void WebGL2RenderingContextBase::vertexAttribI4ui(GLuint index, GLuint x, GLuint y, GLuint z, GLuint w) { if (isContextLost()) return; webContext()->vertexAttribI4ui(index, x, y, z, w); setVertexAttribType(index, Uint32ArrayType); } void WebGL2RenderingContextBase::vertexAttribI4uiv(GLuint index, const DOMUint32Array* v) { if (isContextLost()) return; if (!v || v->length() < 4) { synthesizeGLError(GL_INVALID_VALUE, "vertexAttribI4uiv", "invalid array"); return; } webContext()->vertexAttribI4uiv(index, v->data()); setVertexAttribType(index, Uint32ArrayType); } void WebGL2RenderingContextBase::vertexAttribI4uiv(GLuint index, const Vector& v) { if (isContextLost()) return; if (v.size() < 4) { synthesizeGLError(GL_INVALID_VALUE, "vertexAttribI4uiv", "invalid array"); return; } webContext()->vertexAttribI4uiv(index, v.data()); setVertexAttribType(index, Uint32ArrayType); } void WebGL2RenderingContextBase::vertexAttribIPointer(GLuint index, GLint size, GLenum type, GLsizei stride, long long offset) { if (isContextLost()) return; if (index >= m_maxVertexAttribs) { synthesizeGLError(GL_INVALID_VALUE, "vertexAttribIPointer", "index out of range"); return; } if (!validateValueFitNonNegInt32("vertexAttribIPointer", "offset", offset)) return; if (!m_boundArrayBuffer) { synthesizeGLError(GL_INVALID_OPERATION, "vertexAttribIPointer", "no bound ARRAY_BUFFER"); return; } m_boundVertexArrayObject->setArrayBufferForAttrib(index, m_boundArrayBuffer); webContext()->vertexAttribIPointer(index, size, type, stride, static_cast(offset)); } /* Writing to the drawing buffer */ void WebGL2RenderingContextBase::vertexAttribDivisor(GLuint index, GLuint divisor) { if (isContextLost()) return; if (index >= m_maxVertexAttribs) { synthesizeGLError(GL_INVALID_VALUE, "vertexAttribDivisor", "index out of range"); return; } webContext()->vertexAttribDivisorANGLE(index, divisor); } void WebGL2RenderingContextBase::drawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsizei instanceCount) { if (!validateDrawArrays("drawArraysInstanced", mode, first, count)) return; clearIfComposited(); webContext()->drawArraysInstancedANGLE(mode, first, count, instanceCount); markContextChanged(CanvasChanged); } void WebGL2RenderingContextBase::drawElementsInstanced(GLenum mode, GLsizei count, GLenum type, long long offset, GLsizei instanceCount) { if (!validateDrawElements("drawElementsInstanced", mode, count, type, offset)) return; if (transformFeedbackActive() && !transformFeedbackPaused()) { synthesizeGLError(GL_INVALID_OPERATION, "drawElementsInstanced", "transform feedback is active and not paused"); return; } clearIfComposited(); webContext()->drawElementsInstancedANGLE(mode, count, type, static_cast(offset), instanceCount); markContextChanged(CanvasChanged); } void WebGL2RenderingContextBase::drawRangeElements(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, long long offset) { if (!validateDrawElements("drawRangeElements", mode, count, type, offset)) return; if (transformFeedbackActive() && !transformFeedbackPaused()) { synthesizeGLError(GL_INVALID_OPERATION, "drawRangeElements", "transform feedback is active and not paused"); return; } clearIfComposited(); webContext()->drawRangeElements(mode, start, end, count, type, static_cast(offset)); markContextChanged(CanvasChanged); } void WebGL2RenderingContextBase::drawBuffers(const Vector& buffers) { if (isContextLost()) return; GLsizei n = buffers.size(); const GLenum* bufs = buffers.data(); for (GLsizei i = 0; i < n; ++i) { switch (bufs[i]) { case GL_NONE: case GL_BACK: case GL_COLOR_ATTACHMENT0: break; default: if (bufs[i] > GL_COLOR_ATTACHMENT0 && bufs[i] < static_cast(GL_COLOR_ATTACHMENT0 + maxColorAttachments())) break; synthesizeGLError(GL_INVALID_ENUM, "drawBuffers", "invalid buffer"); return; } } if (!m_framebufferBinding) { if (n != 1) { synthesizeGLError(GL_INVALID_OPERATION, "drawBuffers", "the number of buffers is not 1"); return; } if (bufs[0] != GL_BACK && bufs[0] != GL_NONE) { synthesizeGLError(GL_INVALID_OPERATION, "drawBuffers", "BACK or NONE"); return; } // Because the backbuffer is simulated on all current WebKit ports, we need to change BACK to COLOR_ATTACHMENT0. GLenum value = (bufs[0] == GL_BACK) ? GL_COLOR_ATTACHMENT0 : GL_NONE; webContext()->drawBuffersEXT(1, &value); setBackDrawBuffer(bufs[0]); } else { if (n > maxDrawBuffers()) { synthesizeGLError(GL_INVALID_VALUE, "drawBuffers", "more than max draw buffers"); return; } for (GLsizei i = 0; i < n; ++i) { if (bufs[i] != GL_NONE && bufs[i] != static_cast(GL_COLOR_ATTACHMENT0_EXT + i)) { synthesizeGLError(GL_INVALID_OPERATION, "drawBuffers", "COLOR_ATTACHMENTi_EXT or NONE"); return; } } m_framebufferBinding->drawBuffers(buffers); } } bool WebGL2RenderingContextBase::validateClearBuffer(const char* functionName, GLenum buffer, GLsizei size) { switch (buffer) { case GL_COLOR: if (size < 4) { synthesizeGLError(GL_INVALID_VALUE, functionName, "invalid array size"); return false; } break; case GL_DEPTH: case GL_STENCIL: if (size < 1) { synthesizeGLError(GL_INVALID_VALUE, functionName, "invalid array size"); return false; } break; default: synthesizeGLError(GL_INVALID_ENUM, functionName, "invalid buffer"); return false; } return true; } void WebGL2RenderingContextBase::clearBufferiv(GLenum buffer, GLint drawbuffer, DOMInt32Array* value) { if (isContextLost() || !validateClearBuffer("clearBufferiv", buffer, value->length())) return; webContext()->clearBufferiv(buffer, drawbuffer, value->data()); } void WebGL2RenderingContextBase::clearBufferiv(GLenum buffer, GLint drawbuffer, const Vector& value) { if (isContextLost() || !validateClearBuffer("clearBufferiv", buffer, value.size())) return; webContext()->clearBufferiv(buffer, drawbuffer, value.data()); } void WebGL2RenderingContextBase::clearBufferuiv(GLenum buffer, GLint drawbuffer, DOMUint32Array* value) { if (isContextLost() || !validateClearBuffer("clearBufferuiv", buffer, value->length())) return; webContext()->clearBufferuiv(buffer, drawbuffer, value->data()); } void WebGL2RenderingContextBase::clearBufferuiv(GLenum buffer, GLint drawbuffer, const Vector& value) { if (isContextLost() || !validateClearBuffer("clearBufferuiv", buffer, value.size())) return; webContext()->clearBufferuiv(buffer, drawbuffer, value.data()); } void WebGL2RenderingContextBase::clearBufferfv(GLenum buffer, GLint drawbuffer, DOMFloat32Array* value) { if (isContextLost() || !validateClearBuffer("clearBufferfv", buffer, value->length())) return; webContext()->clearBufferfv(buffer, drawbuffer, value->data()); } void WebGL2RenderingContextBase::clearBufferfv(GLenum buffer, GLint drawbuffer, const Vector& value) { if (isContextLost() || !validateClearBuffer("clearBufferfv", buffer, value.size())) return; webContext()->clearBufferfv(buffer, drawbuffer, value.data()); } void WebGL2RenderingContextBase::clearBufferfi(GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil) { if (isContextLost()) return; webContext()->clearBufferfi(buffer, drawbuffer, depth, stencil); } WebGLQuery* WebGL2RenderingContextBase::createQuery() { if (isContextLost()) return nullptr; WebGLQuery* o = WebGLQuery::create(this); addSharedObject(o); return o; } void WebGL2RenderingContextBase::deleteQuery(WebGLQuery* query) { if (isContextLost() || !query) return; if (m_currentBooleanOcclusionQuery == query) { webContext()->endQueryEXT(m_currentBooleanOcclusionQuery->getTarget()); m_currentBooleanOcclusionQuery = nullptr; } if (m_currentTransformFeedbackPrimitivesWrittenQuery == query) { webContext()->endQueryEXT(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN); m_currentTransformFeedbackPrimitivesWrittenQuery = nullptr; } deleteObject(query); } GLboolean WebGL2RenderingContextBase::isQuery(WebGLQuery* query) { if (isContextLost() || !query) return 0; return webContext()->isQueryEXT(query->object()); } void WebGL2RenderingContextBase::beginQuery(GLenum target, WebGLQuery* query) { bool deleted; if (!query) { synthesizeGLError(GL_INVALID_OPERATION, "beginQuery", "query object is null"); return; } if (!checkObjectToBeBound("beginQuery", query, deleted)) return; if (deleted) { synthesizeGLError(GL_INVALID_OPERATION, "beginQuery", "attempted to begin a deleted query object"); return; } if (query->getTarget() && query->getTarget() != target) { synthesizeGLError(GL_INVALID_OPERATION, "beginQuery", "query type does not match target"); return; } switch (target) { case GL_ANY_SAMPLES_PASSED: case GL_ANY_SAMPLES_PASSED_CONSERVATIVE: { if (m_currentBooleanOcclusionQuery) { synthesizeGLError(GL_INVALID_OPERATION, "beginQuery", "a query is already active for target"); return; } m_currentBooleanOcclusionQuery = query; } break; case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN: { if (m_currentTransformFeedbackPrimitivesWrittenQuery) { synthesizeGLError(GL_INVALID_OPERATION, "beginQuery", "a query is already active for target"); return; } m_currentTransformFeedbackPrimitivesWrittenQuery = query; } break; default: synthesizeGLError(GL_INVALID_ENUM, "beginQuery", "invalid target"); return; } if (!query->getTarget()) query->setTarget(target); webContext()->beginQueryEXT(target, query->object()); } void WebGL2RenderingContextBase::endQuery(GLenum target) { if (isContextLost()) return; switch (target) { case GL_ANY_SAMPLES_PASSED: case GL_ANY_SAMPLES_PASSED_CONSERVATIVE: { if (m_currentBooleanOcclusionQuery && m_currentBooleanOcclusionQuery->getTarget() == target) { m_currentBooleanOcclusionQuery->resetCachedResult(); m_currentBooleanOcclusionQuery = nullptr; } else { synthesizeGLError(GL_INVALID_OPERATION, "endQuery", "target query is not active"); return; } } break; case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN: { if (m_currentTransformFeedbackPrimitivesWrittenQuery) { m_currentTransformFeedbackPrimitivesWrittenQuery->resetCachedResult(); m_currentTransformFeedbackPrimitivesWrittenQuery = nullptr; } else { synthesizeGLError(GL_INVALID_OPERATION, "endQuery", "target query is not active"); return; } } break; default: synthesizeGLError(GL_INVALID_ENUM, "endQuery", "invalid target"); return; } webContext()->endQueryEXT(target); } WebGLQuery* WebGL2RenderingContextBase::getQuery(GLenum target, GLenum pname) { if (isContextLost()) return nullptr; if (pname != GL_CURRENT_QUERY) { synthesizeGLError(GL_INVALID_ENUM, "getQuery", "invalid parameter name"); return nullptr; } switch (target) { case GL_ANY_SAMPLES_PASSED: case GL_ANY_SAMPLES_PASSED_CONSERVATIVE: if (m_currentBooleanOcclusionQuery && m_currentBooleanOcclusionQuery->getTarget() == target) return m_currentBooleanOcclusionQuery; break; case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN: return m_currentTransformFeedbackPrimitivesWrittenQuery; default: synthesizeGLError(GL_INVALID_ENUM, "getQuery", "invalid target"); return nullptr; } return nullptr; } ScriptValue WebGL2RenderingContextBase::getQueryParameter(ScriptState* scriptState, WebGLQuery* query, GLenum pname) { bool deleted; if (!query) { synthesizeGLError(GL_INVALID_OPERATION, "getQueryParameter", "query object is null"); return ScriptValue::createNull(scriptState); } if (!checkObjectToBeBound("getQueryParameter", query, deleted)) return ScriptValue::createNull(scriptState); if (deleted) { synthesizeGLError(GL_INVALID_OPERATION, "getQueryParameter", "attempted to access to a deleted query object"); return ScriptValue::createNull(scriptState); } // Query is non-null at this point. if (!query->getTarget()) { synthesizeGLError(GL_INVALID_OPERATION, "getQueryParameter", "'query' is not a query object yet, since it has't been used by beginQuery"); return ScriptValue::createNull(scriptState); } if (query == m_currentBooleanOcclusionQuery || query == m_currentTransformFeedbackPrimitivesWrittenQuery) { synthesizeGLError(GL_INVALID_OPERATION, "getQueryParameter", "query is currently active"); return ScriptValue::createNull(scriptState); } switch (pname) { case GL_QUERY_RESULT: { query->updateCachedResult(webContext()); return WebGLAny(scriptState, query->getQueryResult()); } case GL_QUERY_RESULT_AVAILABLE: { query->updateCachedResult(webContext()); return WebGLAny(scriptState, query->isQueryResultAvailable()); } default: synthesizeGLError(GL_INVALID_ENUM, "getQueryParameter", "invalid parameter name"); return ScriptValue::createNull(scriptState); } } WebGLSampler* WebGL2RenderingContextBase::createSampler() { if (isContextLost()) return nullptr; WebGLSampler* o = WebGLSampler::create(this); addSharedObject(o); return o; } void WebGL2RenderingContextBase::deleteSampler(WebGLSampler* sampler) { if (isContextLost()) return; for (size_t i = 0; i < m_samplerUnits.size(); ++i) { if (sampler == m_samplerUnits[i]) { m_samplerUnits[i] = nullptr; webContext()->bindSampler(i, 0); } } deleteObject(sampler); } GLboolean WebGL2RenderingContextBase::isSampler(WebGLSampler* sampler) { if (isContextLost() || !sampler) return 0; return webContext()->isSampler(sampler->object()); } void WebGL2RenderingContextBase::bindSampler(GLuint unit, WebGLSampler* sampler) { if (isContextLost()) return; bool deleted; if (!checkObjectToBeBound("bindSampler", sampler, deleted)) return; if (deleted) { synthesizeGLError(GL_INVALID_OPERATION, "bindSampler", "attempted to bind a deleted sampler"); return; } if (unit >= m_samplerUnits.size()) { synthesizeGLError(GL_INVALID_VALUE, "bindSampler", "texture unit out of range"); return; } m_samplerUnits[unit] = sampler; webContext()->bindSampler(unit, objectOrZero(sampler)); } void WebGL2RenderingContextBase::samplerParameter(WebGLSampler* sampler, GLenum pname, GLfloat paramf, GLint parami, bool isFloat) { if (isContextLost() || !validateWebGLObject("samplerParameter", sampler)) return; GLint param = isFloat ? static_cast(paramf) : parami; switch (pname) { case GL_TEXTURE_MAX_LOD: case GL_TEXTURE_MIN_LOD: break; case GL_TEXTURE_COMPARE_FUNC: switch (param) { case GL_LEQUAL: case GL_GEQUAL: case GL_LESS: case GL_GREATER: case GL_EQUAL: case GL_NOTEQUAL: case GL_ALWAYS: case GL_NEVER: break; default: synthesizeGLError(GL_INVALID_ENUM, "samplerParameter", "invalid parameter"); return; } break; case GL_TEXTURE_COMPARE_MODE: switch (param) { case GL_COMPARE_REF_TO_TEXTURE: case GL_NONE: break; default: synthesizeGLError(GL_INVALID_ENUM, "samplerParameter", "invalid parameter"); return; } break; case GL_TEXTURE_MAG_FILTER: switch (param) { case GL_NEAREST: case GL_LINEAR: break; default: synthesizeGLError(GL_INVALID_ENUM, "samplerParameter", "invalid parameter"); return; } break; case GL_TEXTURE_MIN_FILTER: switch (param) { case GL_NEAREST: case GL_LINEAR: case GL_NEAREST_MIPMAP_NEAREST: case GL_LINEAR_MIPMAP_NEAREST: case GL_NEAREST_MIPMAP_LINEAR: case GL_LINEAR_MIPMAP_LINEAR: break; default: synthesizeGLError(GL_INVALID_ENUM, "samplerParameter", "invalid parameter"); return; } break; case GL_TEXTURE_WRAP_R: case GL_TEXTURE_WRAP_S: case GL_TEXTURE_WRAP_T: switch (param) { case GL_CLAMP_TO_EDGE: case GL_MIRRORED_REPEAT: case GL_REPEAT: break; default: synthesizeGLError(GL_INVALID_ENUM, "samplerParameter", "invalid parameter"); return; } break; default: synthesizeGLError(GL_INVALID_ENUM, "samplerParameter", "invalid parameter name"); return; } if (isFloat) { webContext()->samplerParameterf(objectOrZero(sampler), pname, paramf); } else { webContext()->samplerParameteri(objectOrZero(sampler), pname, parami); } } void WebGL2RenderingContextBase::samplerParameteri(WebGLSampler* sampler, GLenum pname, GLint param) { samplerParameter(sampler, pname, 0, param, false); } void WebGL2RenderingContextBase::samplerParameterf(WebGLSampler* sampler, GLenum pname, GLfloat param) { samplerParameter(sampler, pname, param, 0, true); } ScriptValue WebGL2RenderingContextBase::getSamplerParameter(ScriptState* scriptState, WebGLSampler* sampler, GLenum pname) { if (isContextLost() || !validateWebGLObject("getSamplerParameter", sampler)) return ScriptValue::createNull(scriptState); switch (pname) { case GL_TEXTURE_COMPARE_FUNC: case GL_TEXTURE_COMPARE_MODE: case GL_TEXTURE_MAG_FILTER: case GL_TEXTURE_MIN_FILTER: case GL_TEXTURE_WRAP_R: case GL_TEXTURE_WRAP_S: case GL_TEXTURE_WRAP_T: { GLint value = 0; webContext()->getSamplerParameteriv(objectOrZero(sampler), pname, &value); return WebGLAny(scriptState, static_cast(value)); } case GL_TEXTURE_MAX_LOD: case GL_TEXTURE_MIN_LOD: { GLfloat value = 0.f; webContext()->getSamplerParameterfv(objectOrZero(sampler), pname, &value); return WebGLAny(scriptState, value); } default: synthesizeGLError(GL_INVALID_ENUM, "getSamplerParameter", "invalid parameter name"); return ScriptValue::createNull(scriptState); } } WebGLSync* WebGL2RenderingContextBase::fenceSync(GLenum condition, GLbitfield flags) { if (isContextLost()) return nullptr; WebGLSync* o = WebGLFenceSync::create(this, condition, flags); addSharedObject(o); return o; } GLboolean WebGL2RenderingContextBase::isSync(WebGLSync* sync) { if (isContextLost() || !sync) return 0; return webContext()->isSync(sync->object()); } void WebGL2RenderingContextBase::deleteSync(WebGLSync* sync) { deleteObject(sync); } GLenum WebGL2RenderingContextBase::clientWaitSync(WebGLSync* sync, GLbitfield flags, GLint64 timeout) { if (isContextLost() || !validateWebGLObject("clientWaitSync", sync)) return GL_WAIT_FAILED; if (timeout < -1) { synthesizeGLError(GL_INVALID_VALUE, "clientWaitSync", "timeout < -1"); return GL_WAIT_FAILED; } GLuint64 timeout64 = timeout == -1 ? GL_TIMEOUT_IGNORED : static_cast(timeout); return webContext()->clientWaitSync(syncObjectOrZero(sync), flags, timeout64); } void WebGL2RenderingContextBase::waitSync(WebGLSync* sync, GLbitfield flags, GLint64 timeout) { if (isContextLost() || !validateWebGLObject("waitSync", sync)) return; if (timeout < -1) { synthesizeGLError(GL_INVALID_VALUE, "waitSync", "timeout < -1"); return; } GLuint64 timeout64 = timeout == -1 ? GL_TIMEOUT_IGNORED : static_cast(timeout); webContext()->waitSync(syncObjectOrZero(sync), flags, timeout64); } ScriptValue WebGL2RenderingContextBase::getSyncParameter(ScriptState* scriptState, WebGLSync* sync, GLenum pname) { if (isContextLost() || !validateWebGLObject("getSyncParameter", sync)) return ScriptValue::createNull(scriptState); switch (pname) { case GL_OBJECT_TYPE: case GL_SYNC_STATUS: case GL_SYNC_CONDITION: case GL_SYNC_FLAGS: { GLint value = 0; GLsizei length = -1; webContext()->getSynciv(syncObjectOrZero(sync), pname, 1, &length, &value); return WebGLAny(scriptState, static_cast(value)); } default: synthesizeGLError(GL_INVALID_ENUM, "getSyncParameter", "invalid parameter name"); return ScriptValue::createNull(scriptState); } } WebGLTransformFeedback* WebGL2RenderingContextBase::createTransformFeedback() { if (isContextLost()) return nullptr; WebGLTransformFeedback* o = WebGLTransformFeedback::create(this); addSharedObject(o); return o; } void WebGL2RenderingContextBase::deleteTransformFeedback(WebGLTransformFeedback* feedback) { if (feedback == m_transformFeedbackBinding) m_transformFeedbackBinding = nullptr; deleteObject(feedback); } GLboolean WebGL2RenderingContextBase::isTransformFeedback(WebGLTransformFeedback* feedback) { if (isContextLost() || !feedback) return 0; if (!feedback->hasEverBeenBound()) return 0; return webContext()->isTransformFeedback(feedback->object()); } void WebGL2RenderingContextBase::bindTransformFeedback(GLenum target, WebGLTransformFeedback* feedback) { bool deleted; if (!checkObjectToBeBound("bindTransformFeedback", feedback, deleted)) return; if (deleted) { synthesizeGLError(GL_INVALID_OPERATION, "bindTransformFeedback", "attempted to bind a deleted transform feedback object"); return; } if (target != GL_TRANSFORM_FEEDBACK) { synthesizeGLError(GL_INVALID_ENUM, "bindTransformFeedback", "target must be TRANSFORM_FEEDBACK"); return; } if (transformFeedbackActive() && !transformFeedbackPaused()) { synthesizeGLError(GL_INVALID_OPERATION, "bindTransformFeedback", "transform feedback is active and not paused"); return; } m_transformFeedbackBinding = feedback; webContext()->bindTransformFeedback(target, objectOrZero(feedback)); if (feedback) feedback->setTarget(target); } void WebGL2RenderingContextBase::beginTransformFeedback(GLenum primitiveMode) { if (isContextLost()) return; if (!validateTransformFeedbackPrimitiveMode("beginTransformFeedback", primitiveMode)) return; if (transformFeedbackActive()) { synthesizeGLError(GL_INVALID_OPERATION, "beginTransformFeedback", "transform feedback is active"); return; } webContext()->beginTransformFeedback(primitiveMode); // TODO(kbr): there are many more reasons BeginTransformFeedback may fail, the most // problematic being "if any binding point used in transform feedback mode does not // have a buffer object bound", and "if no binding points would be used". // crbug.com/448164 setTransformFeedbackActive(true); if (m_currentProgram) m_currentProgram->increaseActiveTransformFeedbackCount(); if (m_transformFeedbackBinding) m_transformFeedbackBinding->setProgram(m_currentProgram); } void WebGL2RenderingContextBase::endTransformFeedback() { if (isContextLost()) return; if (!transformFeedbackActive()) { synthesizeGLError(GL_INVALID_OPERATION, "endTransformFeedback", "transform feedback is not active"); return; } webContext()->endTransformFeedback(); setTransformFeedbackPaused(false); setTransformFeedbackActive(false); if (m_currentProgram) m_currentProgram->decreaseActiveTransformFeedbackCount(); } void WebGL2RenderingContextBase::transformFeedbackVaryings(WebGLProgram* program, const Vector& varyings, GLenum bufferMode) { if (isContextLost() || !validateWebGLObject("transformFeedbackVaryings", program)) return; switch (bufferMode) { case GL_SEPARATE_ATTRIBS: if (varyings.size() > static_cast(m_maxTransformFeedbackSeparateAttribs)) { synthesizeGLError(GL_INVALID_VALUE, "transformFeedbackVaryings", "too many varyings"); return; } break; case GL_INTERLEAVED_ATTRIBS: break; default: synthesizeGLError(GL_INVALID_ENUM, "transformFeedbackVaryings", "invalid buffer mode"); return; } Vector keepAlive; // Must keep these instances alive while looking at their data Vector varyingStrings; for (size_t i = 0; i < varyings.size(); ++i) { keepAlive.append(varyings[i].ascii()); varyingStrings.append(keepAlive.last().data()); } webContext()->transformFeedbackVaryings(objectOrZero(program), varyings.size(), varyingStrings.data(), bufferMode); } WebGLActiveInfo* WebGL2RenderingContextBase::getTransformFeedbackVarying(WebGLProgram* program, GLuint index) { if (isContextLost() || !validateWebGLObject("getTransformFeedbackVarying", program)) return nullptr; if (!program->linkStatus()) { synthesizeGLError(GL_INVALID_OPERATION, "getTransformFeedbackVarying", "program not linked"); return nullptr; } GLint maxIndex = 0; webContext()->getProgramiv(objectOrZero(program), GL_TRANSFORM_FEEDBACK_VARYINGS, &maxIndex); if (index >= static_cast(maxIndex)) { synthesizeGLError(GL_INVALID_VALUE, "getTransformFeedbackVarying", "invalid index"); return nullptr; } GLint maxNameLength = -1; webContext()->getProgramiv(objectOrZero(program), GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH, &maxNameLength); if (maxNameLength <= 0) { return nullptr; } OwnPtr name = adoptArrayPtr(new GLchar[maxNameLength]); GLsizei length = 0; GLsizei size = 0; GLenum type = 0; webContext()->getTransformFeedbackVarying(objectOrZero(program), index, maxNameLength, &length, &size, &type, name.get()); if (length == 0 || size == 0 || type == 0) { return nullptr; } return WebGLActiveInfo::create(String(name.get(), length), type, size); } void WebGL2RenderingContextBase::pauseTransformFeedback() { if (isContextLost()) return; if (!transformFeedbackActive() || transformFeedbackPaused()) { synthesizeGLError(GL_INVALID_OPERATION, "pauseTransformFeedback", "transform feedback is not active or is paused"); return; } webContext()->pauseTransformFeedback(); setTransformFeedbackPaused(true); } void WebGL2RenderingContextBase::resumeTransformFeedback() { if (isContextLost()) return; if (!transformFeedbackActive() || !transformFeedbackPaused()) { synthesizeGLError(GL_INVALID_OPERATION, "resumeTransformFeedback", "transform feedback is not active or is not paused"); return; } if (m_transformFeedbackBinding && m_transformFeedbackBinding->getProgram() != m_currentProgram) { synthesizeGLError(GL_INVALID_OPERATION, "resumeTransformFeedback", "the program object is not active"); return; } webContext()->resumeTransformFeedback(); setTransformFeedbackPaused(false); } bool WebGL2RenderingContextBase::validateTransformFeedbackPrimitiveMode(const char* functionName, GLenum primitiveMode) { switch (primitiveMode) { case GL_POINTS: case GL_LINES: case GL_TRIANGLES: return true; default: synthesizeGLError(GL_INVALID_ENUM, functionName, "invalid transform feedback primitive mode"); return false; } } void WebGL2RenderingContextBase::bindBufferBase(GLenum target, GLuint index, WebGLBuffer* buffer) { if (isContextLost()) return; bool deleted; if (!checkObjectToBeBound("bindBufferBase", buffer, deleted)) return; if (deleted) buffer = 0; if (!validateAndUpdateBufferBindBaseTarget("bindBufferBase", target, index, buffer)) return; // ES 3.0.4 spec section 2.15.2 "Transform Feedback Primitive Capture" if (target == GL_TRANSFORM_FEEDBACK_BUFFER && transformFeedbackActive()) { synthesizeGLError(GL_INVALID_OPERATION, "bindBufferBase", "target is TRANSFORM_FEEDBACK_BUFFER and transform feedback is active"); return; } webContext()->bindBufferBase(target, index, objectOrZero(buffer)); } void WebGL2RenderingContextBase::bindBufferRange(GLenum target, GLuint index, WebGLBuffer* buffer, long long offset, long long size) { if (isContextLost()) return; bool deleted; if (!checkObjectToBeBound("bindBufferRange", buffer, deleted)) return; if (deleted) buffer = 0; if (!validateValueFitNonNegInt32("bindBufferRange", "offset", offset) || !validateValueFitNonNegInt32("bindBufferRange", "size", size)) { return; } // ES 3.0.4 spec section 2.15.2 "Transform Feedback Primitive Capture" if (target == GL_TRANSFORM_FEEDBACK_BUFFER && transformFeedbackActive()) { synthesizeGLError(GL_INVALID_OPERATION, "bindBufferRange", "target is TRANSFORM_FEEDBACK_BUFFER and transform feedback is active"); return; } if (!validateAndUpdateBufferBindBaseTarget("bindBufferRange", target, index, buffer)) return; webContext()->bindBufferRange(target, index, objectOrZero(buffer), static_cast(offset), static_cast(size)); } bool WebGL2RenderingContextBase::transformFeedbackActive() const { if (m_transformFeedbackBinding) return m_transformFeedbackBinding->isActive(); return false; } bool WebGL2RenderingContextBase::transformFeedbackPaused() const { if (m_transformFeedbackBinding) return m_transformFeedbackBinding->isPaused(); return false; } void WebGL2RenderingContextBase::setTransformFeedbackActive(bool transformFeedbackActive) { if (m_transformFeedbackBinding) m_transformFeedbackBinding->setActive(transformFeedbackActive); } void WebGL2RenderingContextBase::setTransformFeedbackPaused(bool transformFeedbackPaused) { if (m_transformFeedbackBinding) m_transformFeedbackBinding->setPaused(transformFeedbackPaused); } ScriptValue WebGL2RenderingContextBase::getIndexedParameter(ScriptState* scriptState, GLenum target, GLuint index) { if (isContextLost()) return ScriptValue::createNull(scriptState); switch (target) { case GL_TRANSFORM_FEEDBACK_BUFFER_BINDING: if (index >= m_boundIndexedTransformFeedbackBuffers.size()) { synthesizeGLError(GL_INVALID_VALUE, "getIndexedParameter", "index out of range"); return ScriptValue::createNull(scriptState); } return WebGLAny(scriptState, m_boundIndexedTransformFeedbackBuffers[index].get()); case GL_UNIFORM_BUFFER_BINDING: if (index >= m_boundIndexedUniformBuffers.size()) { synthesizeGLError(GL_INVALID_VALUE, "getIndexedParameter", "index out of range"); return ScriptValue::createNull(scriptState); } return WebGLAny(scriptState, m_boundIndexedUniformBuffers[index].get()); case GL_TRANSFORM_FEEDBACK_BUFFER_SIZE: case GL_TRANSFORM_FEEDBACK_BUFFER_START: case GL_UNIFORM_BUFFER_SIZE: case GL_UNIFORM_BUFFER_START: { GLint64 value = -1; webContext()->getInteger64i_v(target, index, &value); return WebGLAny(scriptState, value); } default: synthesizeGLError(GL_INVALID_ENUM, "getIndexedParameter", "invalid parameter name"); return ScriptValue::createNull(scriptState); } } Vector WebGL2RenderingContextBase::getUniformIndices(WebGLProgram* program, const Vector& uniformNames) { Vector result; if (isContextLost() || !validateWebGLObject("getUniformIndices", program)) return result; Vector keepAlive; // Must keep these instances alive while looking at their data Vector uniformStrings; for (size_t i = 0; i < uniformNames.size(); ++i) { keepAlive.append(uniformNames[i].ascii()); uniformStrings.append(keepAlive.last().data()); } result.resize(uniformNames.size()); webContext()->getUniformIndices(objectOrZero(program), uniformStrings.size(), uniformStrings.data(), result.data()); return result; } ScriptValue WebGL2RenderingContextBase::getActiveUniforms(ScriptState* scriptState, WebGLProgram* program, const Vector& uniformIndices, GLenum pname) { if (isContextLost() || !validateWebGLObject("getActiveUniforms", program)) return ScriptValue::createNull(scriptState); enum ReturnType { EnumType, UnsignedIntType, IntType, BoolType }; int returnType; switch (pname) { case GL_UNIFORM_TYPE: returnType = EnumType; break; case GL_UNIFORM_SIZE: returnType = UnsignedIntType; break; case GL_UNIFORM_BLOCK_INDEX: case GL_UNIFORM_OFFSET: case GL_UNIFORM_ARRAY_STRIDE: case GL_UNIFORM_MATRIX_STRIDE: returnType = IntType; break; case GL_UNIFORM_IS_ROW_MAJOR: returnType = BoolType; break; default: synthesizeGLError(GL_INVALID_ENUM, "getActiveUniforms", "invalid parameter name"); return ScriptValue::createNull(scriptState); } GLint activeUniforms = -1; webContext()->getProgramiv(objectOrZero(program), GL_ACTIVE_UNIFORMS, &activeUniforms); GLuint activeUniformsUnsigned = activeUniforms; size_t size = uniformIndices.size(); for (size_t i = 0; i < size; ++i) { if (uniformIndices[i] >= activeUniformsUnsigned) { synthesizeGLError(GL_INVALID_VALUE, "getActiveUniforms", "uniform index greater than ACTIVE_UNIFORMS"); return ScriptValue::createNull(scriptState); } } Vector result(size); webContext()->getActiveUniformsiv(objectOrZero(program), uniformIndices.size(), uniformIndices.data(), pname, result.data()); switch (returnType) { case EnumType: { Vector enumResult(size); for (size_t i = 0; i < size; ++i) enumResult[i] = static_cast(result[i]); return WebGLAny(scriptState, enumResult); } case UnsignedIntType: { Vector uintResult(size); for (size_t i = 0; i < size; ++i) uintResult[i] = static_cast(result[i]); return WebGLAny(scriptState, uintResult); } case IntType: { return WebGLAny(scriptState, result); } case BoolType: { Vector boolResult(size); for (size_t i = 0; i < size; ++i) boolResult[i] = static_cast(result[i]); return WebGLAny(scriptState, boolResult); } default: ASSERT_NOT_REACHED(); return ScriptValue::createNull(scriptState); } } GLuint WebGL2RenderingContextBase::getUniformBlockIndex(WebGLProgram* program, const String& uniformBlockName) { if (isContextLost() || !validateWebGLObject("getUniformBlockIndex", program)) return 0; if (!validateString("getUniformBlockIndex", uniformBlockName)) return 0; return webContext()->getUniformBlockIndex(objectOrZero(program), uniformBlockName.utf8().data()); } bool WebGL2RenderingContextBase::validateUniformBlockIndex(const char* functionName, WebGLProgram* program, GLuint blockIndex) { ASSERT(program); if (!program->linkStatus()) { synthesizeGLError(GL_INVALID_OPERATION, functionName, "program not linked"); return false; } GLint activeUniformBlocks = 0; webContext()->getProgramiv(objectOrZero(program), GL_ACTIVE_UNIFORM_BLOCKS, &activeUniformBlocks); if (blockIndex >= static_cast(activeUniformBlocks)) { synthesizeGLError(GL_INVALID_VALUE, functionName, "invalid uniform block index"); return false; } return true; } ScriptValue WebGL2RenderingContextBase::getActiveUniformBlockParameter(ScriptState* scriptState, WebGLProgram* program, GLuint uniformBlockIndex, GLenum pname) { if (isContextLost() || !validateWebGLObject("getActiveUniformBlockParameter", program)) return ScriptValue::createNull(scriptState); if (!validateUniformBlockIndex("getActiveUniformBlockParameter", program, uniformBlockIndex)) return ScriptValue::createNull(scriptState); switch (pname) { case GL_UNIFORM_BLOCK_BINDING: case GL_UNIFORM_BLOCK_DATA_SIZE: case GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS: { GLint intValue = 0; webContext()->getActiveUniformBlockiv(objectOrZero(program), uniformBlockIndex, pname, &intValue); return WebGLAny(scriptState, static_cast(intValue)); } case GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES: { GLint uniformCount = 0; webContext()->getActiveUniformBlockiv(objectOrZero(program), uniformBlockIndex, GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, &uniformCount); Vector indices(uniformCount); webContext()->getActiveUniformBlockiv(objectOrZero(program), uniformBlockIndex, pname, indices.data()); return WebGLAny(scriptState, DOMUint32Array::create(reinterpret_cast(indices.data()), indices.size())); } case GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER: case GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER: { GLint boolValue = 0; webContext()->getActiveUniformBlockiv(objectOrZero(program), uniformBlockIndex, pname, &boolValue); return WebGLAny(scriptState, static_cast(boolValue)); } default: synthesizeGLError(GL_INVALID_ENUM, "getActiveUniformBlockParameter", "invalid parameter name"); return ScriptValue::createNull(scriptState); } } String WebGL2RenderingContextBase::getActiveUniformBlockName(WebGLProgram* program, GLuint uniformBlockIndex) { if (isContextLost() || !validateWebGLObject("getActiveUniformBlockName", program)) return String(); if (!validateUniformBlockIndex("getActiveUniformBlockName", program, uniformBlockIndex)) return String(); GLint maxNameLength = -1; webContext()->getProgramiv(objectOrZero(program), GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH, &maxNameLength); if (maxNameLength <= 0) { // This state indicates that there are no active uniform blocks synthesizeGLError(GL_INVALID_VALUE, "getActiveUniformBlockName", "invalid uniform block index"); return String(); } OwnPtr name = adoptArrayPtr(new GLchar[maxNameLength]); GLsizei length = 0; webContext()->getActiveUniformBlockName(objectOrZero(program), uniformBlockIndex, maxNameLength, &length, name.get()); return String(name.get(), length); } void WebGL2RenderingContextBase::uniformBlockBinding(WebGLProgram* program, GLuint uniformBlockIndex, GLuint uniformBlockBinding) { if (isContextLost() || !validateWebGLObject("uniformBlockBinding", program)) return; if (!validateUniformBlockIndex("uniformBlockBinding", program, uniformBlockIndex)) return; webContext()->uniformBlockBinding(objectOrZero(program), uniformBlockIndex, uniformBlockBinding); } WebGLVertexArrayObject* WebGL2RenderingContextBase::createVertexArray() { if (isContextLost()) return nullptr; WebGLVertexArrayObject* o = WebGLVertexArrayObject::create(this, WebGLVertexArrayObjectBase::VaoTypeUser); addContextObject(o); return o; } void WebGL2RenderingContextBase::deleteVertexArray(ScriptState* scriptState, WebGLVertexArrayObject* vertexArray) { if (isContextLost() || !vertexArray) return; if (!vertexArray->isDefaultObject() && vertexArray == m_boundVertexArrayObject) setBoundVertexArrayObject(scriptState, nullptr); vertexArray->deleteObject(webContext()); } GLboolean WebGL2RenderingContextBase::isVertexArray(WebGLVertexArrayObject* vertexArray) { if (isContextLost() || !vertexArray) return 0; if (!vertexArray->hasEverBeenBound()) return 0; return webContext()->isVertexArrayOES(vertexArray->object()); } void WebGL2RenderingContextBase::bindVertexArray(ScriptState* scriptState, WebGLVertexArrayObject* vertexArray) { if (isContextLost()) return; if (vertexArray && (vertexArray->isDeleted() || !vertexArray->validate(0, this))) { webContext()->synthesizeGLError(GL_INVALID_OPERATION); return; } if (vertexArray && !vertexArray->isDefaultObject() && vertexArray->object()) { webContext()->bindVertexArrayOES(objectOrZero(vertexArray)); vertexArray->setHasEverBeenBound(); setBoundVertexArrayObject(scriptState, vertexArray); } else { webContext()->bindVertexArrayOES(0); setBoundVertexArrayObject(scriptState, nullptr); } } void WebGL2RenderingContextBase::bindFramebuffer(ScriptState* scriptState, GLenum target, WebGLFramebuffer* buffer) { bool deleted; if (!checkObjectToBeBound("bindFramebuffer", buffer, deleted)) return; if (deleted) buffer = 0; switch (target) { case GL_DRAW_FRAMEBUFFER: break; case GL_FRAMEBUFFER: case GL_READ_FRAMEBUFFER: m_readFramebufferBinding = buffer; break; default: synthesizeGLError(GL_INVALID_ENUM, "bindFramebuffer", "invalid target"); return; } setFramebuffer(target, buffer); if (scriptState) preserveObjectWrapper(scriptState, this, "framebuffer", 0, buffer); } void WebGL2RenderingContextBase::deleteFramebuffer(WebGLFramebuffer* framebuffer) { if (!deleteObject(framebuffer)) return; GLenum target = 0; if (framebuffer == m_framebufferBinding) { if (framebuffer == m_readFramebufferBinding) { target = GL_FRAMEBUFFER; m_framebufferBinding = nullptr; m_readFramebufferBinding = nullptr; } else { target = GL_DRAW_FRAMEBUFFER; m_framebufferBinding = nullptr; } } else if (framebuffer == m_readFramebufferBinding) { target = GL_READ_FRAMEBUFFER; m_readFramebufferBinding = nullptr; } if (target) { drawingBuffer()->setFramebufferBinding(target, 0); // Have to call drawingBuffer()->bind() here to bind back to internal fbo. drawingBuffer()->bind(target); } } ScriptValue WebGL2RenderingContextBase::getParameter(ScriptState* scriptState, GLenum pname) { if (isContextLost()) return ScriptValue::createNull(scriptState); switch (pname) { case GL_SHADING_LANGUAGE_VERSION: return WebGLAny(scriptState, "WebGL GLSL ES 3.00 (" + String(webContext()->getString(GL_SHADING_LANGUAGE_VERSION)) + ")"); case GL_VERSION: return WebGLAny(scriptState, "WebGL 2.0 (" + String(webContext()->getString(GL_VERSION)) + ")"); case GL_COPY_READ_BUFFER_BINDING: return WebGLAny(scriptState, m_boundCopyReadBuffer.get()); case GL_COPY_WRITE_BUFFER_BINDING: return WebGLAny(scriptState, m_boundCopyWriteBuffer.get()); case GL_DRAW_FRAMEBUFFER_BINDING: return WebGLAny(scriptState, m_framebufferBinding.get()); case GL_FRAGMENT_SHADER_DERIVATIVE_HINT: return getUnsignedIntParameter(scriptState, pname); case GL_MAX_3D_TEXTURE_SIZE: return getIntParameter(scriptState, pname); case GL_MAX_ARRAY_TEXTURE_LAYERS: return getIntParameter(scriptState, pname); case GC3D_MAX_CLIENT_WAIT_TIMEOUT_WEBGL: return WebGLAny(scriptState, 0u); case GL_MAX_COLOR_ATTACHMENTS: return getIntParameter(scriptState, pname); case GL_MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS: return getInt64Parameter(scriptState, pname); case GL_MAX_COMBINED_UNIFORM_BLOCKS: return getIntParameter(scriptState, pname); case GL_MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS: return getInt64Parameter(scriptState, pname); case GL_MAX_DRAW_BUFFERS: return getIntParameter(scriptState, pname); case GL_MAX_ELEMENT_INDEX: return getInt64Parameter(scriptState, pname); case GL_MAX_ELEMENTS_INDICES: return getIntParameter(scriptState, pname); case GL_MAX_ELEMENTS_VERTICES: return getIntParameter(scriptState, pname); case GL_MAX_FRAGMENT_INPUT_COMPONENTS: return getIntParameter(scriptState, pname); case GL_MAX_FRAGMENT_UNIFORM_BLOCKS: return getIntParameter(scriptState, pname); case GL_MAX_FRAGMENT_UNIFORM_COMPONENTS: return getIntParameter(scriptState, pname); case GL_MAX_PROGRAM_TEXEL_OFFSET: return getIntParameter(scriptState, pname); case GL_MAX_SAMPLES: return getIntParameter(scriptState, pname); case GL_MAX_SERVER_WAIT_TIMEOUT: return getInt64Parameter(scriptState, pname); case GL_MAX_TEXTURE_LOD_BIAS: return getFloatParameter(scriptState, pname); case GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS: return getIntParameter(scriptState, pname); case GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS: return getIntParameter(scriptState, pname); case GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS: return getIntParameter(scriptState, pname); case GL_MAX_UNIFORM_BLOCK_SIZE: return getInt64Parameter(scriptState, pname); case GL_MAX_UNIFORM_BUFFER_BINDINGS: return getIntParameter(scriptState, pname); case GL_MAX_VARYING_COMPONENTS: return getIntParameter(scriptState, pname); case GL_MAX_VERTEX_OUTPUT_COMPONENTS: return getIntParameter(scriptState, pname); case GL_MAX_VERTEX_UNIFORM_BLOCKS: return getIntParameter(scriptState, pname); case GL_MAX_VERTEX_UNIFORM_COMPONENTS: return getIntParameter(scriptState, pname); case GL_MIN_PROGRAM_TEXEL_OFFSET: return getIntParameter(scriptState, pname); case GL_PACK_ROW_LENGTH: return getIntParameter(scriptState, pname); case GL_PACK_SKIP_PIXELS: return getIntParameter(scriptState, pname); case GL_PACK_SKIP_ROWS: return getIntParameter(scriptState, pname); case GL_PIXEL_PACK_BUFFER_BINDING: return WebGLAny(scriptState, m_boundPixelPackBuffer.get()); case GL_PIXEL_UNPACK_BUFFER_BINDING: return WebGLAny(scriptState, m_boundPixelUnpackBuffer.get()); case GL_RASTERIZER_DISCARD: return getBooleanParameter(scriptState, pname); case GL_READ_BUFFER: { GLenum value = 0; if (!isContextLost()) { WebGLFramebuffer* readFramebufferBinding = getFramebufferBinding(GL_READ_FRAMEBUFFER); if (!readFramebufferBinding) value = m_readBufferOfDefaultFramebuffer; else value = readFramebufferBinding->getReadBuffer(); } return WebGLAny(scriptState, value); } case GL_READ_FRAMEBUFFER_BINDING: return WebGLAny(scriptState, m_readFramebufferBinding.get()); case GL_SAMPLE_ALPHA_TO_COVERAGE: return getBooleanParameter(scriptState, pname); case GL_SAMPLE_COVERAGE: return getBooleanParameter(scriptState, pname); case GL_SAMPLER_BINDING: return WebGLAny(scriptState, m_samplerUnits[m_activeTextureUnit].get()); case GL_TEXTURE_BINDING_2D_ARRAY: return WebGLAny(scriptState, m_textureUnits[m_activeTextureUnit].m_texture2DArrayBinding.get()); case GL_TEXTURE_BINDING_3D: return WebGLAny(scriptState, m_textureUnits[m_activeTextureUnit].m_texture3DBinding.get()); case GL_TRANSFORM_FEEDBACK_ACTIVE: return WebGLAny(scriptState, transformFeedbackActive()); case GL_TRANSFORM_FEEDBACK_BUFFER_BINDING: return WebGLAny(scriptState, m_boundTransformFeedbackBuffer.get()); case GL_TRANSFORM_FEEDBACK_BINDING: return WebGLAny(scriptState, m_transformFeedbackBinding.get()); case GL_TRANSFORM_FEEDBACK_PAUSED: return WebGLAny(scriptState, transformFeedbackPaused()); case GL_UNIFORM_BUFFER_BINDING: return WebGLAny(scriptState, m_boundUniformBuffer.get()); case GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT: return getIntParameter(scriptState, pname); case GL_UNPACK_IMAGE_HEIGHT: return getIntParameter(scriptState, pname); case GL_UNPACK_ROW_LENGTH: return getIntParameter(scriptState, pname); case GL_UNPACK_SKIP_IMAGES: return getIntParameter(scriptState, pname); case GL_UNPACK_SKIP_PIXELS: return getIntParameter(scriptState, pname); case GL_UNPACK_SKIP_ROWS: return getIntParameter(scriptState, pname); default: return WebGLRenderingContextBase::getParameter(scriptState, pname); } } ScriptValue WebGL2RenderingContextBase::getInt64Parameter(ScriptState* scriptState, GLenum pname) { GLint64 value = 0; if (!isContextLost()) webContext()->getInteger64v(pname, &value); return WebGLAny(scriptState, value); } bool WebGL2RenderingContextBase::validateCapability(const char* functionName, GLenum cap) { switch (cap) { case GL_RASTERIZER_DISCARD: return true; default: return WebGLRenderingContextBase::validateCapability(functionName, cap); } } bool WebGL2RenderingContextBase::isBufferBoundToTransformFeedback(WebGLBuffer* buffer) { ASSERT(buffer); if (m_boundTransformFeedbackBuffer == buffer) return true; for (size_t i = 0; i < m_boundIndexedTransformFeedbackBuffers.size(); ++i) { if (m_boundIndexedTransformFeedbackBuffers[i] == buffer) return true; } return false; } bool WebGL2RenderingContextBase::isBufferBoundToNonTransformFeedback(WebGLBuffer* buffer) { ASSERT(buffer); if (m_boundArrayBuffer == buffer || m_boundVertexArrayObject->boundElementArrayBuffer() == buffer || m_boundCopyReadBuffer == buffer || m_boundCopyWriteBuffer == buffer || m_boundPixelPackBuffer == buffer || m_boundPixelUnpackBuffer == buffer || m_boundUniformBuffer == buffer) { return true; } for (size_t i = 0; i <= m_maxBoundUniformBufferIndex; ++i) { if (m_boundIndexedUniformBuffers[i] == buffer) return true; } return false; } bool WebGL2RenderingContextBase::validateBufferTargetCompatibility(const char* functionName, GLenum target, WebGLBuffer* buffer) { ASSERT(buffer); switch (buffer->getInitialTarget()) { case GL_ELEMENT_ARRAY_BUFFER: switch (target) { case GL_ARRAY_BUFFER: case GL_PIXEL_PACK_BUFFER: case GL_PIXEL_UNPACK_BUFFER: case GL_TRANSFORM_FEEDBACK_BUFFER: case GL_UNIFORM_BUFFER: synthesizeGLError(GL_INVALID_OPERATION, functionName, "element array buffers can not be bound to a different target"); return false; default: break; } break; case GL_ARRAY_BUFFER: case GL_COPY_READ_BUFFER: case GL_COPY_WRITE_BUFFER: case GL_PIXEL_PACK_BUFFER: case GL_PIXEL_UNPACK_BUFFER: case GL_UNIFORM_BUFFER: case GL_TRANSFORM_FEEDBACK_BUFFER: if (target == GL_ELEMENT_ARRAY_BUFFER) { synthesizeGLError(GL_INVALID_OPERATION, functionName, "buffers bound to non ELEMENT_ARRAY_BUFFER targets can not be bound to ELEMENT_ARRAY_BUFFER target"); return false; } break; default: break; } if (target == GL_TRANSFORM_FEEDBACK_BUFFER) { if (isBufferBoundToNonTransformFeedback(buffer)) { synthesizeGLError(GL_INVALID_OPERATION, functionName, "a buffer bound to TRANSFORM_FEEDBACK_BUFFER can not be bound to any other targets"); return false; } } else if (isBufferBoundToTransformFeedback(buffer)) { synthesizeGLError(GL_INVALID_OPERATION, functionName, "a buffer bound to TRANSFORM_FEEDBACK_BUFFER can not be bound to any other targets"); return false; } return true; } bool WebGL2RenderingContextBase::validateBufferTarget(const char* functionName, GLenum target) { switch (target) { case GL_ARRAY_BUFFER: case GL_COPY_READ_BUFFER: case GL_COPY_WRITE_BUFFER: case GL_ELEMENT_ARRAY_BUFFER: case GL_PIXEL_PACK_BUFFER: case GL_PIXEL_UNPACK_BUFFER: case GL_TRANSFORM_FEEDBACK_BUFFER: case GL_UNIFORM_BUFFER: return true; default: synthesizeGLError(GL_INVALID_ENUM, functionName, "invalid target"); return false; } } bool WebGL2RenderingContextBase::validateAndUpdateBufferBindTarget(const char* functionName, GLenum target, WebGLBuffer* buffer) { if (!validateBufferTarget(functionName, target)) return false; if (buffer && !validateBufferTargetCompatibility(functionName, target, buffer)) return false; switch (target) { case GL_ARRAY_BUFFER: m_boundArrayBuffer = buffer; break; case GL_COPY_READ_BUFFER: m_boundCopyReadBuffer = buffer; break; case GL_COPY_WRITE_BUFFER: m_boundCopyWriteBuffer = buffer; break; case GL_ELEMENT_ARRAY_BUFFER: m_boundVertexArrayObject->setElementArrayBuffer(buffer); break; case GL_PIXEL_PACK_BUFFER: m_boundPixelPackBuffer = buffer; break; case GL_PIXEL_UNPACK_BUFFER: m_boundPixelUnpackBuffer = buffer; break; case GL_TRANSFORM_FEEDBACK_BUFFER: m_boundTransformFeedbackBuffer = buffer; break; case GL_UNIFORM_BUFFER: m_boundUniformBuffer = buffer; break; default: ASSERT_NOT_REACHED(); break; } if (buffer && !buffer->getInitialTarget()) buffer->setInitialTarget(target); return true; } bool WebGL2RenderingContextBase::validateBufferBaseTarget(const char* functionName, GLenum target) { switch (target) { case GL_TRANSFORM_FEEDBACK_BUFFER: case GL_UNIFORM_BUFFER: return true; default: synthesizeGLError(GL_INVALID_ENUM, functionName, "invalid target"); return false; } } bool WebGL2RenderingContextBase::validateAndUpdateBufferBindBaseTarget(const char* functionName, GLenum target, GLuint index, WebGLBuffer* buffer) { if (!validateBufferBaseTarget(functionName, target)) return false; if (buffer && !validateBufferTargetCompatibility(functionName, target, buffer)) return false; switch (target) { case GL_TRANSFORM_FEEDBACK_BUFFER: if (index >= m_boundIndexedTransformFeedbackBuffers.size()) { synthesizeGLError(GL_INVALID_VALUE, functionName, "index out of range"); return false; } m_boundIndexedTransformFeedbackBuffers[index] = buffer; m_boundTransformFeedbackBuffer = buffer; break; case GL_UNIFORM_BUFFER: if (index >= m_boundIndexedUniformBuffers.size()) { synthesizeGLError(GL_INVALID_VALUE, functionName, "index out of range"); return false; } m_boundIndexedUniformBuffers[index] = buffer; m_boundUniformBuffer = buffer; // Keep track of what the maximum bound uniform buffer index is if (buffer) { if (index > m_maxBoundUniformBufferIndex) m_maxBoundUniformBufferIndex = index; } else if (m_maxBoundUniformBufferIndex > 0 && index == m_maxBoundUniformBufferIndex) { size_t i = m_maxBoundUniformBufferIndex - 1; for (; i > 0; --i) { if (m_boundIndexedUniformBuffers[i].get()) break; } m_maxBoundUniformBufferIndex = i; } break; default: ASSERT_NOT_REACHED(); break; } if (buffer && !buffer->getInitialTarget()) buffer->setInitialTarget(target); return true; } bool WebGL2RenderingContextBase::validateFramebufferTarget(GLenum target) { switch (target) { case GL_FRAMEBUFFER: case GL_READ_FRAMEBUFFER: case GL_DRAW_FRAMEBUFFER: return true; default: return false; } } bool WebGL2RenderingContextBase::validateReadPixelsFormatAndType(GLenum format, GLenum type, DOMArrayBufferView* buffer) { switch (format) { case GL_RED: case GL_RED_INTEGER: case GL_RG: case GL_RG_INTEGER: case GL_RGB: case GL_RGB_INTEGER: case GL_RGBA: case GL_RGBA_INTEGER: case GL_LUMINANCE_ALPHA: case GL_LUMINANCE: case GL_ALPHA: break; default: synthesizeGLError(GL_INVALID_ENUM, "readPixels", "invalid format"); return false; } switch (type) { case GL_UNSIGNED_BYTE: if (buffer && buffer->type() != DOMArrayBufferView::TypeUint8) { synthesizeGLError(GL_INVALID_OPERATION, "readPixels", "type UNSIGNED_BYTE but ArrayBufferView not Uint8Array"); return false; } return true; case GL_BYTE: if (buffer && buffer->type() != DOMArrayBufferView::TypeInt8) { synthesizeGLError(GL_INVALID_OPERATION, "readPixels", "type BYTE but ArrayBufferView not Int8Array"); return false; } return true; case GL_HALF_FLOAT: if (buffer && buffer->type() != DOMArrayBufferView::TypeUint16) { synthesizeGLError(GL_INVALID_OPERATION, "readPixels", "type HALF_FLOAT but ArrayBufferView not Uint16Array"); return false; } return true; case GL_FLOAT: if (buffer && buffer->type() != DOMArrayBufferView::TypeFloat32) { synthesizeGLError(GL_INVALID_OPERATION, "readPixels", "type FLOAT but ArrayBufferView not Float32Array"); return false; } return true; case GL_UNSIGNED_SHORT: case GL_UNSIGNED_SHORT_5_6_5: case GL_UNSIGNED_SHORT_4_4_4_4: case GL_UNSIGNED_SHORT_5_5_5_1: if (buffer && buffer->type() != DOMArrayBufferView::TypeUint16) { synthesizeGLError(GL_INVALID_OPERATION, "readPixels", "type UNSIGNED_SHORT but ArrayBufferView not Uint16Array"); return false; } return true; case GL_SHORT: if (buffer && buffer->type() != DOMArrayBufferView::TypeInt16) { synthesizeGLError(GL_INVALID_OPERATION, "readPixels", "type SHORT but ArrayBufferView not Int16Array"); return false; } return true; case GL_UNSIGNED_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: if (buffer && buffer->type() != DOMArrayBufferView::TypeUint32) { synthesizeGLError(GL_INVALID_OPERATION, "readPixels", "type UNSIGNED_INT but ArrayBufferView not Uint32Array"); return false; } return true; case GL_INT: if (buffer && buffer->type() != DOMArrayBufferView::TypeInt32) { synthesizeGLError(GL_INVALID_OPERATION, "readPixels", "type INT but ArrayBufferView not Int32Array"); return false; } return true; default: synthesizeGLError(GL_INVALID_ENUM, "readPixels", "invalid type"); return false; } } WebGLFramebuffer* WebGL2RenderingContextBase::getFramebufferBinding(GLenum target) { switch (target) { case GL_READ_FRAMEBUFFER: return m_readFramebufferBinding.get(); case GL_DRAW_FRAMEBUFFER: return m_framebufferBinding.get(); default: return WebGLRenderingContextBase::getFramebufferBinding(target); } } WebGLFramebuffer* WebGL2RenderingContextBase::getReadFramebufferBinding() { return m_readFramebufferBinding.get(); } bool WebGL2RenderingContextBase::validateGetFramebufferAttachmentParameterFunc(const char* functionName, GLenum target, GLenum attachment) { if (!validateFramebufferTarget(target)) { synthesizeGLError(GL_INVALID_ENUM, functionName, "invalid target"); return false; } WebGLFramebuffer* framebufferBinding = getFramebufferBinding(target); ASSERT(framebufferBinding || drawingBuffer()); if (!framebufferBinding) { // for the default framebuffer switch (attachment) { case GL_BACK: case GL_DEPTH: case GL_STENCIL: break; default: synthesizeGLError(GL_INVALID_ENUM, functionName, "invalid attachment"); return false; } } else { // for the FBO switch (attachment) { case GL_COLOR_ATTACHMENT0: case GL_DEPTH_ATTACHMENT: case GL_STENCIL_ATTACHMENT: break; case GL_DEPTH_STENCIL_ATTACHMENT: if (framebufferBinding->getAttachmentObject(GL_DEPTH_ATTACHMENT) != framebufferBinding->getAttachmentObject(GL_STENCIL_ATTACHMENT)) { synthesizeGLError(GL_INVALID_OPERATION, functionName, "different objects are bound to the depth and stencil attachment points"); return false; } break; default: if (attachment > GL_COLOR_ATTACHMENT0 && attachment < static_cast(GL_COLOR_ATTACHMENT0 + maxColorAttachments())) break; synthesizeGLError(GL_INVALID_ENUM, functionName, "invalid attachment"); return false; } } return true; } ScriptValue WebGL2RenderingContextBase::getFramebufferAttachmentParameter(ScriptState* scriptState, GLenum target, GLenum attachment, GLenum pname) { const char kFunctionName[] = "getFramebufferAttachmentParameter"; if (isContextLost() || !validateGetFramebufferAttachmentParameterFunc(kFunctionName, target, attachment)) return ScriptValue::createNull(scriptState); WebGLFramebuffer* framebufferBinding = getFramebufferBinding(target); ASSERT(!framebufferBinding || framebufferBinding->object()); // Default framebuffer (an internal fbo) if (!framebufferBinding) { // We can use m_requestedAttribs because in WebGL 2, they are required to be honored. bool hasDepth = m_requestedAttributes.depth(); bool hasStencil = m_requestedAttributes.stencil(); bool hasAlpha = m_requestedAttributes.alpha(); bool missingImage = (attachment == GL_DEPTH && !hasDepth) || (attachment == GL_STENCIL && !hasStencil); if (missingImage) { switch (pname) { case GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE: return WebGLAny(scriptState, GL_NONE); default: synthesizeGLError(GL_INVALID_OPERATION, kFunctionName, "invalid parameter name"); return ScriptValue::createNull(scriptState); } } switch (pname) { case GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE: return WebGLAny(scriptState, GL_FRAMEBUFFER_DEFAULT); case GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE: case GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE: case GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE: { GLint value = attachment == GL_BACK ? 8 : 0; return WebGLAny(scriptState, value); } case GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE: { GLint value = (attachment == GL_BACK && hasAlpha) ? 8 : 0; return WebGLAny(scriptState, value); } case GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE: { // For ES3 capable backend, DEPTH24_STENCIL8 has to be supported. GLint value = attachment == GL_DEPTH ? 24 : 0; return WebGLAny(scriptState, value); } case GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE: { GLint value = attachment == GL_STENCIL ? 8 : 0; return WebGLAny(scriptState, value); } case GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE: return WebGLAny(scriptState, GL_UNSIGNED_NORMALIZED); case GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING: return WebGLAny(scriptState, GL_LINEAR); default: synthesizeGLError(GL_INVALID_ENUM, kFunctionName, "invalid parameter name"); return ScriptValue::createNull(scriptState); } } WebGLSharedObject* attachmentObject = nullptr; if (attachment == GL_DEPTH_STENCIL_ATTACHMENT) { WebGLSharedObject* depthAttachment = framebufferBinding->getAttachmentObject(GL_DEPTH_ATTACHMENT); WebGLSharedObject* stencilAttachment = framebufferBinding->getAttachmentObject(GL_STENCIL_ATTACHMENT); if (depthAttachment != stencilAttachment) { synthesizeGLError(GL_INVALID_OPERATION, kFunctionName, "different objects bound to DEPTH_ATTACHMENT and STENCIL_ATTACHMENT"); return ScriptValue::createNull(scriptState); } attachmentObject = depthAttachment; } else { attachmentObject = framebufferBinding->getAttachmentObject(attachment); } if (!attachmentObject) { switch (pname) { case GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE: return WebGLAny(scriptState, GL_NONE); case GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME: return ScriptValue::createNull(scriptState); default: synthesizeGLError(GL_INVALID_OPERATION, kFunctionName, "invalid parameter name"); return ScriptValue::createNull(scriptState); } } ASSERT(attachmentObject->isTexture() || attachmentObject->isRenderbuffer()); switch (pname) { case GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE: if (attachmentObject->isTexture()) return WebGLAny(scriptState, GL_TEXTURE); return WebGLAny(scriptState, GL_RENDERBUFFER); case GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME: return WebGLAny(scriptState, attachmentObject); case GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE: case GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER: case GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL: if (!attachmentObject->isTexture()) break; case GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE: case GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE: case GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE: case GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE: case GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE: case GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE: { GLint value = 0; webContext()->getFramebufferAttachmentParameteriv(target, attachment, pname, &value); return WebGLAny(scriptState, value); } case GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE: if (attachment == GL_DEPTH_STENCIL_ATTACHMENT) { synthesizeGLError(GL_INVALID_OPERATION, kFunctionName, "COMPONENT_TYPE can't be queried for DEPTH_STENCIL_ATTACHMENT"); return ScriptValue::createNull(scriptState); } case GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING: { GLint value = 0; webContext()->getFramebufferAttachmentParameteriv(target, attachment, pname, &value); return WebGLAny(scriptState, static_cast(value)); } default: break; } synthesizeGLError(GL_INVALID_ENUM, kFunctionName, "invalid parameter name"); return ScriptValue::createNull(scriptState); } DEFINE_TRACE(WebGL2RenderingContextBase) { visitor->trace(m_readFramebufferBinding); visitor->trace(m_transformFeedbackBinding); visitor->trace(m_boundCopyReadBuffer); visitor->trace(m_boundCopyWriteBuffer); visitor->trace(m_boundPixelPackBuffer); visitor->trace(m_boundPixelUnpackBuffer); visitor->trace(m_boundTransformFeedbackBuffer); visitor->trace(m_boundUniformBuffer); visitor->trace(m_boundIndexedTransformFeedbackBuffers); visitor->trace(m_boundIndexedUniformBuffers); visitor->trace(m_currentBooleanOcclusionQuery); visitor->trace(m_currentTransformFeedbackPrimitivesWrittenQuery); visitor->trace(m_samplerUnits); WebGLRenderingContextBase::trace(visitor); } WebGLTexture* WebGL2RenderingContextBase::validateTexture3DBinding(const char* functionName, GLenum target) { WebGLTexture* tex = nullptr; switch (target) { case GL_TEXTURE_2D_ARRAY: tex = m_textureUnits[m_activeTextureUnit].m_texture2DArrayBinding.get(); break; case GL_TEXTURE_3D: tex = m_textureUnits[m_activeTextureUnit].m_texture3DBinding.get(); break; default: synthesizeGLError(GL_INVALID_ENUM, functionName, "invalid texture target"); return nullptr; } if (!tex) synthesizeGLError(GL_INVALID_OPERATION, functionName, "no texture bound to target"); return tex; } GLint WebGL2RenderingContextBase::getMaxTextureLevelForTarget(GLenum target) { switch (target) { case GL_TEXTURE_3D: return m_max3DTextureLevel; case GL_TEXTURE_2D_ARRAY: return m_maxTextureLevel; } return WebGLRenderingContextBase::getMaxTextureLevelForTarget(target); } ScriptValue WebGL2RenderingContextBase::getTexParameter(ScriptState* scriptState, GLenum target, GLenum pname) { if (isContextLost() || !validateTextureBinding("getTexParameter", target)) return ScriptValue::createNull(scriptState); switch (pname) { case GL_TEXTURE_WRAP_R: case GL_TEXTURE_COMPARE_FUNC: case GL_TEXTURE_COMPARE_MODE: case GL_TEXTURE_IMMUTABLE_LEVELS: { GLint value = 0; webContext()->getTexParameteriv(target, pname, &value); return WebGLAny(scriptState, static_cast(value)); } case GL_TEXTURE_IMMUTABLE_FORMAT: { GLint value = 0; webContext()->getTexParameteriv(target, pname, &value); return WebGLAny(scriptState, static_cast(value)); } case GL_TEXTURE_BASE_LEVEL: case GL_TEXTURE_MAX_LEVEL: { GLint value = 0; webContext()->getTexParameteriv(target, pname, &value); return WebGLAny(scriptState, value); } case GL_TEXTURE_MAX_LOD: case GL_TEXTURE_MIN_LOD: { GLfloat value = 0.f; webContext()->getTexParameterfv(target, pname, &value); return WebGLAny(scriptState, value); } default: return WebGLRenderingContextBase::getTexParameter(scriptState, target, pname); } } WebGLBuffer* WebGL2RenderingContextBase::validateBufferDataTarget(const char* functionName, GLenum target) { WebGLBuffer* buffer = nullptr; switch (target) { case GL_ELEMENT_ARRAY_BUFFER: buffer = m_boundVertexArrayObject->boundElementArrayBuffer(); break; case GL_ARRAY_BUFFER: buffer = m_boundArrayBuffer.get(); break; case GL_COPY_READ_BUFFER: buffer = m_boundCopyReadBuffer.get(); break; case GL_COPY_WRITE_BUFFER: buffer = m_boundCopyWriteBuffer.get(); break; case GL_PIXEL_PACK_BUFFER: buffer = m_boundPixelPackBuffer.get(); break; case GL_PIXEL_UNPACK_BUFFER: buffer = m_boundPixelUnpackBuffer.get(); break; case GL_TRANSFORM_FEEDBACK_BUFFER: buffer = m_boundTransformFeedbackBuffer.get(); break; case GL_UNIFORM_BUFFER: buffer = m_boundUniformBuffer.get(); break; default: synthesizeGLError(GL_INVALID_ENUM, functionName, "invalid target"); return nullptr; } if (!buffer) { synthesizeGLError(GL_INVALID_OPERATION, functionName, "no buffer"); return nullptr; } return buffer; } bool WebGL2RenderingContextBase::validateBufferDataUsage(const char* functionName, GLenum usage) { switch (usage) { case GL_STREAM_READ: case GL_STREAM_COPY: case GL_STATIC_READ: case GL_STATIC_COPY: case GL_DYNAMIC_READ: case GL_DYNAMIC_COPY: return true; default: return WebGLRenderingContextBase::validateBufferDataUsage(functionName, usage); } } void WebGL2RenderingContextBase::removeBoundBuffer(WebGLBuffer* buffer) { if (m_boundCopyReadBuffer == buffer) m_boundCopyReadBuffer = nullptr; if (m_boundCopyWriteBuffer == buffer) m_boundCopyWriteBuffer = nullptr; if (m_boundPixelPackBuffer == buffer) m_boundPixelPackBuffer = nullptr; if (m_boundPixelUnpackBuffer == buffer) m_boundPixelUnpackBuffer = nullptr; if (m_boundTransformFeedbackBuffer == buffer) m_boundTransformFeedbackBuffer = nullptr; if (m_boundUniformBuffer == buffer) m_boundUniformBuffer = nullptr; WebGLRenderingContextBase::removeBoundBuffer(buffer); } void WebGL2RenderingContextBase::restoreCurrentFramebuffer() { bindFramebuffer(nullptr, GL_DRAW_FRAMEBUFFER, m_framebufferBinding.get()); bindFramebuffer(nullptr, GL_READ_FRAMEBUFFER, m_readFramebufferBinding.get()); } WebGLImageConversion::PixelStoreParams WebGL2RenderingContextBase::getPackPixelStoreParams() { WebGLImageConversion::PixelStoreParams params; params.alignment = m_packAlignment; params.rowLength = m_packRowLength; params.skipPixels = m_packSkipPixels; params.skipRows = m_packSkipRows; return params; } WebGLImageConversion::PixelStoreParams WebGL2RenderingContextBase::getUnpackPixelStoreParams(TexImageDimension dimension) { WebGLImageConversion::PixelStoreParams params; params.alignment = m_unpackAlignment; params.rowLength = m_unpackRowLength; params.skipPixels = m_unpackSkipPixels; params.skipRows = m_unpackSkipRows; if (dimension == Tex3D) { params.imageHeight = m_unpackImageHeight; params.skipImages = m_unpackSkipImages; } return params; } } // namespace blink