| // 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 "config.h" |
| #include "modules/webgl/WebGL2RenderingContextBase.h" |
| |
| #include "bindings/modules/v8/WebGLAny.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/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, |
| 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<WebGraphicsContext3D> context, const WebGLContextAttributes& requestedAttributes) |
| : WebGLRenderingContextBase(passedCanvas, context, requestedAttributes) |
| { |
| m_supportedInternalFormatsStorage.insert(kSupportedInternalFormatsStorage, kSupportedInternalFormatsStorage + arraysize(kSupportedInternalFormatsStorage)); |
| } |
| |
| 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; |
| |
| m_maxArrayTextureLayers = 0; |
| webContext()->getIntegerv(GL_MAX_ARRAY_TEXTURE_LAYERS, &m_maxArrayTextureLayers); |
| |
| GLint numCombinedTextureImageUnits = 0; |
| webContext()->getIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &numCombinedTextureImageUnits); |
| m_samplerUnits.clear(); |
| m_samplerUnits.resize(numCombinedTextureImageUnits); |
| |
| GLint maxTransformFeedbackSeparateAttribs = 0; |
| webContext()->getIntegerv(GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS, &maxTransformFeedbackSeparateAttribs); |
| m_boundIndexedTransformFeedbackBuffers.clear(); |
| m_boundIndexedTransformFeedbackBuffers.resize(maxTransformFeedbackSeparateAttribs); |
| |
| GLint maxUniformBufferBindings = 0; |
| webContext()->getIntegerv(GL_MAX_UNIFORM_BUFFER_BINDINGS, &maxUniformBufferBindings); |
| m_boundIndexedUniformBuffers.clear(); |
| m_boundIndexedUniformBuffers.resize(maxUniformBufferBindings); |
| m_maxBoundUniformBufferIndex = 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<GLintptr>(readOffset), static_cast<GLintptr>(writeOffset), static_cast<GLsizeiptr>(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<GLintptr>(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; |
| } |
| webContext()->framebufferTextureLayer(target, attachment, objectOrZero(texture), level, layer); |
| framebufferBinding->setAttachmentForBoundFramebuffer(target, attachment, textarget, texture, level, layer); |
| applyStencilTest(); |
| preserveObjectWrapper(scriptState, framebufferBinding, "attachment", attachment, texture); |
| } |
| |
| 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); |
| } |
| |
| 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: |
| 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; |
| default: |
| synthesizeGLError(GL_INVALID_ENUM, "getInternalformatParameter", "invalid internalformat"); |
| return ScriptValue::createNull(scriptState); |
| } |
| |
| switch (pname) { |
| case GL_SAMPLES: |
| { |
| GLint length = -1; |
| webContext()->getInternalformativ(target, internalformat, GL_NUM_SAMPLE_COUNTS, 1, &length); |
| if (length <= 0) |
| return WebGLAny(scriptState, DOMInt32Array::create(0)); |
| |
| OwnPtr<GLint[]> values = adoptArrayPtr(new GLint[length]); |
| for (GLint ii = 0; ii < length; ++ii) |
| values[ii] = 0; |
| webContext()->getInternalformativ(target, internalformat, GL_SAMPLES, length, values.get()); |
| return WebGLAny(scriptState, DOMInt32Array::create(values.get(), length)); |
| } |
| default: |
| synthesizeGLError(GL_INVALID_ENUM, "getInternalformatParameter", "invalid parameter name"); |
| return ScriptValue::createNull(scriptState); |
| } |
| } |
| |
| void WebGL2RenderingContextBase::invalidateFramebuffer(GLenum target, Vector<GLenum>& attachments) |
| { |
| if (isContextLost()) |
| return; |
| |
| webContext()->invalidateFramebuffer(target, attachments.size(), attachments.data()); |
| } |
| |
| void WebGL2RenderingContextBase::invalidateSubFramebuffer(GLenum target, Vector<GLenum>& attachments, GLint x, GLint y, GLsizei width, GLsizei height) |
| { |
| if (isContextLost()) |
| return; |
| |
| webContext()->invalidateSubFramebuffer(target, attachments.size(), attachments.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<GLenum>(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::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; |
| } |
| |
| // Need to validate whether the pixel pack buffer is mapped, |
| // if we decide to expose mapBufferRange() to web developers. |
| |
| 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, size)) |
| return; |
| |
| clearIfComposited(); |
| |
| WebGLFramebuffer* readFramebufferBinding = getFramebufferBinding(GL_READ_FRAMEBUFFER); |
| { |
| ScopedDrawingBufferBinder binder(drawingBuffer(), readFramebufferBinding); |
| webContext()->readPixels(x, y, width, height, format, type, reinterpret_cast<void*>(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 { |
| 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; |
| 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(); |
| } |
| |
| /* 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; |
| } |
| } |
| |
| WebGLTexture* tex = validateTextureBinding(functionName, target, false); |
| if (!tex) |
| return false; |
| |
| if (m_supportedInternalFormatsStorage.find(internalformat) == m_supportedInternalFormatsStorage.end()) { |
| synthesizeGLError(GL_INVALID_ENUM, functionName, "invalid internalformat"); |
| return false; |
| } |
| |
| if (tex->isImmutable()) { |
| synthesizeGLError(GL_INVALID_OPERATION, functionName, "attempted to modify immutable texture"); |
| 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::texStorage2D(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height) |
| { |
| if (isContextLost() || !validateTexStorage("texStorage2D", target, levels, internalformat, width, height, 1, TexStorageType2D)) |
| return; |
| |
| WebGLTexture* tex = validateTextureBinding("texStorage2D", target, false); |
| webContext()->texStorage2DEXT(target, levels, internalformat, width, height); |
| tex->setTexStorageInfo(target, levels, internalformat, width, height, 1); |
| } |
| |
| 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; |
| |
| WebGLTexture* tex = validateTextureBinding("texStorage3D", target, false); |
| webContext()->texStorage3D(target, levels, internalformat, width, height, depth); |
| tex->setTexStorageInfo(target, levels, internalformat, width, height, depth); |
| } |
| |
| bool WebGL2RenderingContextBase::validateTexImage3D(const char* functionName, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type) |
| { |
| switch (target) { |
| case GL_TEXTURE_3D: |
| case GL_TEXTURE_2D_ARRAY: |
| break; |
| default: |
| synthesizeGLError(GL_INVALID_ENUM, functionName, "invalid target"); |
| return false; |
| } |
| |
| if (!validateTexFuncLevel(functionName, target, level)) |
| return false; |
| |
| if (!validateTexFuncParameters(functionName, NotTexSubImage2D, target, level, internalformat, width, height, depth, border, format, type)) |
| return false; |
| |
| return true; |
| } |
| |
| 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() || !validateTexImage3D("texImage3D", target, level, internalformat, width, height, depth, border, format, type) |
| || !validateTexFuncData("texImage3D", level, width, height, format, type, pixels, NullAllowed)) |
| return; |
| |
| void* data = pixels ? pixels->baseAddress() : 0; |
| Vector<uint8_t> tempData; |
| if (data && (m_unpackFlipY || m_unpackPremultiplyAlpha)) { |
| // FIXME: WebGLImageConversion needs to be updated to accept image depth. |
| notImplemented(); |
| return; |
| } |
| |
| WebGLTexture* tex = validateTextureBinding("texImage3D", target, true); |
| if (!tex) |
| return; |
| |
| if (tex->isImmutable()) { |
| synthesizeGLError(GL_INVALID_OPERATION, "texImage3D", "attempted to modify immutable texture"); |
| return; |
| } |
| |
| webContext()->texImage3D(target, level, convertTexInternalFormat(internalformat, type), width, height, depth, border, format, type, data); |
| tex->setLevelInfo(target, level, internalformat, width, height, depth, type); |
| } |
| |
| bool WebGL2RenderingContextBase::validateTexSubImage3D(const char* functionName, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, |
| GLenum format, GLenum type, GLsizei width, GLsizei height, GLsizei depth) |
| { |
| switch (target) { |
| case GL_TEXTURE_3D: |
| case GL_TEXTURE_2D_ARRAY: |
| break; |
| default: |
| synthesizeGLError(GL_INVALID_ENUM, functionName, "invalid target"); |
| return false; |
| } |
| |
| WebGLTexture* tex = validateTextureBinding(functionName, target, false); |
| if (!tex) |
| return false; |
| |
| if (!validateTexFuncLevel(functionName, target, level)) |
| return false; |
| |
| if (!tex->isValid(target, level)) { |
| synthesizeGLError(GL_INVALID_OPERATION, "texSubImage3D", "no previously defined texture image"); |
| return false; |
| } |
| |
| // Before checking if it is in the range, check if overflow happens first. |
| Checked<GLint, RecordOverflow> maxX = xoffset, maxY = yoffset, maxZ = zoffset; |
| maxX += width; |
| maxY += height; |
| maxZ += depth; |
| if (maxX.hasOverflowed() || maxY.hasOverflowed() || maxZ.hasOverflowed() |
| || maxX.unsafeGet() > tex->getWidth(target, level) |
| || maxY.unsafeGet() > tex->getHeight(target, level) |
| || maxZ.unsafeGet() > tex->getDepth(target, level)) { |
| synthesizeGLError(GL_INVALID_VALUE, functionName, "dimensions out of range"); |
| return false; |
| } |
| |
| GLenum internalformat = tex->getInternalFormat(target, level); |
| if (!validateTexFuncFormatAndType(functionName, internalformat, format, type, level)) |
| return false; |
| |
| return true; |
| } |
| |
| 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) |
| { |
| if (!validateTexSubImage3D("texSubImage3D", target, level, xoffset, yoffset, zoffset, format, type, image->width(), image->height(), 1)) |
| return; |
| |
| // 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<uint8_t> 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; |
| } |
| } |
| |
| if (m_unpackAlignment != 1) |
| webContext()->pixelStorei(GL_UNPACK_ALIGNMENT, 1); |
| webContext()->texSubImage3D(target, level, xoffset, yoffset, zoffset, imageExtractor.imageWidth(), imageExtractor.imageHeight(), 1, format, type, needConversion ? data.data() : imagePixelData); |
| if (m_unpackAlignment != 1) |
| webContext()->pixelStorei(GL_UNPACK_ALIGNMENT, m_unpackAlignment); |
| } |
| |
| 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() || !pixels || !validateTexSubImage3D("texSubImage3D", target, level, xoffset, yoffset, zoffset, format, type, width, height, depth) |
| || !validateTexFuncData("texSubImage3D", level, width, height, format, type, pixels, NullAllowed)) |
| return; |
| |
| // FIXME: Ensure pixels is large enough to contain the desired texture dimensions. |
| |
| void* data = pixels->baseAddress(); |
| Vector<uint8_t> tempData; |
| bool changeUnpackAlignment = false; |
| if (data && (m_unpackFlipY || m_unpackPremultiplyAlpha)) { |
| if (!WebGLImageConversion::extractTextureData(width, height, format, type, |
| m_unpackAlignment, |
| m_unpackFlipY, m_unpackPremultiplyAlpha, |
| data, |
| tempData)) |
| return; |
| data = tempData.data(); |
| changeUnpackAlignment = true; |
| } |
| if (changeUnpackAlignment) |
| webContext()->pixelStorei(GL_UNPACK_ALIGNMENT, 1); |
| webContext()->texSubImage3D(target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, data); |
| if (changeUnpackAlignment) |
| webContext()->pixelStorei(GL_UNPACK_ALIGNMENT, m_unpackAlignment); |
| } |
| |
| void WebGL2RenderingContextBase::texSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLenum format, GLenum type, ImageData* pixels) |
| { |
| if (isContextLost() || !pixels || !validateTexSubImage3D("texSubImage3D", target, level, xoffset, yoffset, zoffset, format, type, pixels->width(), pixels->height(), 1)) |
| 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<uint8_t> 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; |
| } |
| } |
| if (m_unpackAlignment != 1) |
| webContext()->pixelStorei(GL_UNPACK_ALIGNMENT, 1); |
| webContext()->texSubImage3D(target, level, xoffset, yoffset, zoffset, pixels->width(), pixels->height(), 1, format, type, needConversion ? data.data() : pixels->data()->data()); |
| if (m_unpackAlignment != 1) |
| webContext()->pixelStorei(GL_UNPACK_ALIGNMENT, m_unpackAlignment); |
| } |
| |
| void WebGL2RenderingContextBase::texSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLenum format, GLenum type, HTMLImageElement* image, ExceptionState& exceptionState) |
| { |
| if (isContextLost() || !image || !validateHTMLImageElement("texSubImage3D", image, exceptionState)) |
| return; |
| |
| RefPtr<Image> imageForRender = image->cachedImage()->image(); |
| if (imageForRender->isSVGImage()) |
| imageForRender = drawImageIntoBuffer(imageForRender.get(), image->width(), image->height(), "texSubImage3D"); |
| |
| 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() || !validateHTMLCanvasElement("texSubImage3D", canvas, exceptionState)) |
| return; |
| |
| WebGLTexture* texture = validateTextureBinding("texSubImage3D", target, false); |
| ASSERT(texture); |
| GLenum internalformat = texture->getInternalFormat(target, level); |
| |
| if (!canvas->renderingContext() || !canvas->renderingContext()->isAccelerated() || !canUseTexImageCanvasByGPU(internalformat, type)) { |
| ASSERT(!canvas->renderingContext() || canvas->renderingContext()->is2d()); |
| // 2D canvas has only FrontBuffer. |
| texSubImage3DImpl(target, level, xoffset, yoffset, zoffset, format, type, canvas->copiedImage(FrontBuffer, PreferAcceleration).get(), |
| WebGLImageConversion::HtmlDomCanvas, m_unpackFlipY, m_unpackPremultiplyAlpha); |
| return; |
| } |
| |
| texImageCanvasByGPU(TexSubImage3DByGPU, texture, target, level, GL_RGBA, type, xoffset, yoffset, zoffset, canvas); |
| } |
| |
| void WebGL2RenderingContextBase::texSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLenum format, GLenum type, HTMLVideoElement* video, ExceptionState& exceptionState) |
| { |
| if (isContextLost() || !validateHTMLVideoElement("texSubImage3D", video, exceptionState)) |
| return; |
| |
| RefPtr<Image> image = videoFrameToImage(video); |
| if (!image) |
| return; |
| texSubImage3DImpl(target, level, xoffset, yoffset, zoffset, format, type, image.get(), WebGLImageConversion::HtmlDomVideo, m_unpackFlipY, m_unpackPremultiplyAlpha); |
| } |
| |
| 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 (!validateCopyTexSubImage("copyTexSubImage3D", target, level, xoffset, yoffset, zoffset, x, y, width, height)) |
| return; |
| WebGLFramebuffer* readFramebufferBinding = nullptr; |
| if (!validateReadBufferAndGetInfo("copyTexSubImage3D", readFramebufferBinding, nullptr, nullptr)) |
| 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; |
| |
| WebGLTexture* tex = validateTextureBinding("compressedTexImage3D", target, true); |
| if (!tex) |
| return; |
| |
| if (tex->isImmutable()) { |
| synthesizeGLError(GL_INVALID_OPERATION, "compressedTexImage3D", "attempted to modify immutable texture"); |
| return; |
| } |
| |
| webContext()->compressedTexImage3D(target, level, internalformat, width, height, depth, border, data->byteLength(), data->baseAddress()); |
| tex->setLevelInfo(target, level, internalformat, width, height, depth, GL_UNSIGNED_BYTE); |
| } |
| |
| 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; |
| |
| WebGLTexture* tex = validateTextureBinding("compressedTexSubImage3D", target, true); |
| if (!tex) |
| return; |
| |
| if (format != tex->getInternalFormat(target, level)) { |
| synthesizeGLError(GL_INVALID_OPERATION, "compressedTexSubImage3D", "format does not match texture 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<WTF::Uint32Array>("uniform1uiv", location, v, 1)) |
| return; |
| |
| webContext()->uniform1uiv(location->location(), v.length(), v.dataMaybeOnStack()); |
| } |
| |
| void WebGL2RenderingContextBase::uniform1uiv(const WebGLUniformLocation* location, Vector<GLuint>& 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<WTF::Uint32Array>("uniform2uiv", location, v, 2)) |
| return; |
| |
| webContext()->uniform2uiv(location->location(), v.length() >> 1, v.dataMaybeOnStack()); |
| } |
| |
| void WebGL2RenderingContextBase::uniform2uiv(const WebGLUniformLocation* location, Vector<GLuint>& 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<WTF::Uint32Array>("uniform3uiv", location, v, 3)) |
| return; |
| |
| webContext()->uniform3uiv(location->location(), v.length() / 3, v.dataMaybeOnStack()); |
| } |
| |
| void WebGL2RenderingContextBase::uniform3uiv(const WebGLUniformLocation* location, Vector<GLuint>& 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<WTF::Uint32Array>("uniform4uiv", location, v, 4)) |
| return; |
| |
| webContext()->uniform4uiv(location->location(), v.length() >> 2, v.dataMaybeOnStack()); |
| } |
| |
| void WebGL2RenderingContextBase::uniform4uiv(const WebGLUniformLocation* location, Vector<GLuint>& 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<GLfloat>& 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<GLfloat>& 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<GLfloat>& 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<GLfloat>& 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<GLfloat>& 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<GLfloat>& 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; |
| |
| if (index >= m_maxVertexAttribs) { |
| synthesizeGLError(GL_INVALID_VALUE, "vertexAttribI4i", "index out of range"); |
| return; |
| } |
| |
| webContext()->vertexAttribI4i(index, x, y, z, w); |
| VertexAttribValue& attribValue = m_vertexAttribValue[index]; |
| attribValue.type = Int32ArrayType; |
| attribValue.value.intValue[0] = x; |
| attribValue.value.intValue[1] = y; |
| attribValue.value.intValue[2] = z; |
| attribValue.value.intValue[3] = w; |
| } |
| |
| void WebGL2RenderingContextBase::vertexAttribI4iv(GLuint index, const DOMInt32Array* value) |
| { |
| vertexAttribIivImpl("vertexAttribI4iv", index, value->data(), value->length()); |
| } |
| |
| void WebGL2RenderingContextBase::vertexAttribI4iv(GLuint index, const Vector<GLint>& value) |
| { |
| vertexAttribIivImpl("vertexAttribI4iv", index, value.data(), value.size()); |
| } |
| |
| void WebGL2RenderingContextBase::vertexAttribI4ui(GLuint index, GLuint x, GLuint y, GLuint z, GLuint w) |
| { |
| if (isContextLost()) |
| return; |
| |
| if (index >= m_maxVertexAttribs) { |
| synthesizeGLError(GL_INVALID_VALUE, "vertexAttribI4ui", "index out of range"); |
| return; |
| } |
| |
| webContext()->vertexAttribI4ui(index, x, y, z, w); |
| VertexAttribValue& attribValue = m_vertexAttribValue[index]; |
| attribValue.type = Uint32ArrayType; |
| attribValue.value.uintValue[0] = x; |
| attribValue.value.uintValue[1] = y; |
| attribValue.value.uintValue[2] = z; |
| attribValue.value.uintValue[3] = w; |
| } |
| |
| void WebGL2RenderingContextBase::vertexAttribI4uiv(GLuint index, const DOMUint32Array* value) |
| { |
| vertexAttribIuivImpl("vertexAttribI4uiv", index, value->data(), value->length()); |
| } |
| |
| void WebGL2RenderingContextBase::vertexAttribI4uiv(GLuint index, const Vector<GLuint>& value) |
| { |
| vertexAttribIuivImpl("vertexAttribI4uiv", index, value.data(), value.size()); |
| } |
| |
| void WebGL2RenderingContextBase::vertexAttribIivImpl(const char* functionName, GLuint index, const GLint* value, GLsizei size) |
| { |
| if (isContextLost()) |
| return; |
| |
| if (!value) { |
| synthesizeGLError(GL_INVALID_VALUE, functionName, "no array"); |
| return; |
| } |
| if (size < 4) { |
| synthesizeGLError(GL_INVALID_VALUE, functionName, "invalid size"); |
| return; |
| } |
| if (index >= m_maxVertexAttribs) { |
| synthesizeGLError(GL_INVALID_VALUE, functionName, "index out of range"); |
| return; |
| } |
| |
| webContext()->vertexAttribI4iv(index, value); |
| VertexAttribValue& attribValue = m_vertexAttribValue[index]; |
| attribValue.type = Int32ArrayType; |
| attribValue.value.intValue[0] = value[0]; |
| attribValue.value.intValue[1] = value[1]; |
| attribValue.value.intValue[2] = value[2]; |
| attribValue.value.intValue[3] = value[3]; |
| } |
| |
| void WebGL2RenderingContextBase::vertexAttribIuivImpl(const char* functionName, GLuint index, const GLuint* value, GLsizei size) |
| { |
| if (isContextLost()) |
| return; |
| |
| if (!value) { |
| synthesizeGLError(GL_INVALID_VALUE, functionName, "no array"); |
| return; |
| } |
| if (size < 4) { |
| synthesizeGLError(GL_INVALID_VALUE, functionName, "invalid size"); |
| return; |
| } |
| if (index >= m_maxVertexAttribs) { |
| synthesizeGLError(GL_INVALID_VALUE, functionName, "index out of range"); |
| return; |
| } |
| |
| webContext()->vertexAttribI4uiv(index, value); |
| VertexAttribValue& attribValue = m_vertexAttribValue[index]; |
| attribValue.type = Uint32ArrayType; |
| attribValue.value.uintValue[0] = value[0]; |
| attribValue.value.uintValue[1] = value[1]; |
| attribValue.value.uintValue[2] = value[2]; |
| attribValue.value.uintValue[3] = value[3]; |
| } |
| |
| void WebGL2RenderingContextBase::vertexAttribIPointer(GLuint index, GLint size, GLenum type, GLsizei stride, long long offset) |
| { |
| if (isContextLost()) |
| return; |
| |
| switch (type) { |
| case GL_BYTE: |
| case GL_UNSIGNED_BYTE: |
| case GL_SHORT: |
| case GL_UNSIGNED_SHORT: |
| case GL_INT: |
| case GL_UNSIGNED_INT: |
| break; |
| default: |
| synthesizeGLError(GL_INVALID_ENUM, "vertexAttribIPointer", "invalid type"); |
| return; |
| } |
| if (index >= m_maxVertexAttribs) { |
| synthesizeGLError(GL_INVALID_VALUE, "vertexAttribIPointer", "index out of range"); |
| return; |
| } |
| if (size < 1 || size > 4 || stride < 0 || stride > 255) { |
| synthesizeGLError(GL_INVALID_VALUE, "vertexAttribIPointer", "bad size or stride"); |
| return; |
| } |
| if (!validateValueFitNonNegInt32("vertexAttribIPointer", "offset", offset)) |
| return; |
| if (!m_boundArrayBuffer) { |
| synthesizeGLError(GL_INVALID_OPERATION, "vertexAttribIPointer", "no bound ARRAY_BUFFER"); |
| return; |
| } |
| unsigned typeSize = sizeInBytes(type); |
| ASSERT((typeSize & (typeSize - 1)) == 0); // Ensure that the value is POT. |
| if ((stride & (typeSize - 1)) || (static_cast<GLintptr>(offset) & (typeSize - 1))) { |
| synthesizeGLError(GL_INVALID_OPERATION, "vertexAttribIPointer", "stride or offset not valid for type"); |
| return; |
| } |
| GLsizei bytesPerElement = size * typeSize; |
| |
| m_boundVertexArrayObject->setVertexAttribState(index, bytesPerElement, size, type, false, stride, static_cast<GLintptr>(offset), m_boundArrayBuffer); |
| webContext()->vertexAttribIPointer(index, size, type, stride, static_cast<GLintptr>(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; |
| } |
| |
| m_boundVertexArrayObject->setVertexAttribDivisor(index, divisor); |
| webContext()->vertexAttribDivisorANGLE(index, divisor); |
| } |
| |
| void WebGL2RenderingContextBase::drawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsizei instanceCount) |
| { |
| if (!validateDrawArrays("drawArraysInstanced", mode, first, count)) |
| return; |
| |
| if (!validateDrawInstanced("drawArraysInstanced", instanceCount)) |
| return; |
| |
| clearIfComposited(); |
| |
| handleTextureCompleteness("drawArraysInstanced", true); |
| webContext()->drawArraysInstancedANGLE(mode, first, count, instanceCount); |
| handleTextureCompleteness("drawArraysInstanced", false); |
| 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 (!validateDrawInstanced("drawElementsInstanced", instanceCount)) |
| return; |
| |
| clearIfComposited(); |
| |
| handleTextureCompleteness("drawElementsInstanced", true); |
| webContext()->drawElementsInstancedANGLE(mode, count, type, static_cast<GLintptr>(offset), instanceCount); |
| handleTextureCompleteness("drawElementsInstanced", false); |
| 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; |
| |
| clearIfComposited(); |
| |
| handleTextureCompleteness("drawRangeElements", true); |
| webContext()->drawRangeElements(mode, start, end, count, type, static_cast<GLintptr>(offset)); |
| handleTextureCompleteness("drawRangeElements", false); |
| markContextChanged(CanvasChanged); |
| } |
| |
| void WebGL2RenderingContextBase::drawBuffers(const Vector<GLenum>& buffers) |
| { |
| if (isContextLost()) |
| return; |
| |
| GLsizei n = buffers.size(); |
| const GLenum* bufs = buffers.data(); |
| if (!m_framebufferBinding) { |
| if (n != 1) { |
| synthesizeGLError(GL_INVALID_VALUE, "drawBuffers", "more than one buffer"); |
| 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<GLenum>(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: |
| case GL_FRONT: |
| case GL_BACK: |
| case GL_FRONT_AND_BACK: |
| 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<GLint>& 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<GLuint>& 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<GLfloat>& 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 (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 (!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) |
| { |
| if (isContextLost() || !validateWebGLObject("getQueryParameter", query)) |
| return ScriptValue::createNull(scriptState); |
| |
| // Query is non-null at this point. |
| 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<GLint>(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) { |
| sampler->setParameterf(pname, paramf); |
| webContext()->samplerParameterf(objectOrZero(sampler), pname, paramf); |
| } else { |
| sampler->setParameteri(pname, parami); |
| 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<unsigned>(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<GLuint64>(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<GLuint64>(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<unsigned>(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; |
| } |
| |
| m_transformFeedbackBinding = feedback; |
| |
| webContext()->bindTransformFeedback(target, objectOrZero(feedback)); |
| if (feedback) |
| feedback->setTarget(target); |
| } |
| |
| void WebGL2RenderingContextBase::beginTransformFeedback(GLenum primitiveMode) |
| { |
| if (isContextLost()) |
| return; |
| |
| webContext()->beginTransformFeedback(primitiveMode); |
| } |
| |
| void WebGL2RenderingContextBase::endTransformFeedback() |
| { |
| if (isContextLost()) |
| return; |
| |
| webContext()->endTransformFeedback(); |
| } |
| |
| void WebGL2RenderingContextBase::transformFeedbackVaryings(WebGLProgram* program, const Vector<String>& varyings, GLenum bufferMode) |
| { |
| if (isContextLost() || !validateWebGLObject("transformFeedbackVaryings", program)) |
| return; |
| |
| Vector<CString> keepAlive; // Must keep these instances alive while looking at their data |
| Vector<const char*> 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; |
| |
| GLint maxNameLength = -1; |
| webContext()->getProgramiv(objectOrZero(program), GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH, &maxNameLength); |
| if (maxNameLength <= 0) { |
| return nullptr; |
| } |
| OwnPtr<GLchar[]> 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; |
| |
| webContext()->pauseTransformFeedback(); |
| } |
| |
| void WebGL2RenderingContextBase::resumeTransformFeedback() |
| { |
| if (isContextLost()) |
| return; |
| |
| webContext()->resumeTransformFeedback(); |
| } |
| |
| 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; |
| 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; |
| } |
| |
| if (!validateAndUpdateBufferBindBaseTarget("bindBufferRange", target, index, buffer)) |
| return; |
| |
| webContext()->bindBufferRange(target, index, objectOrZero(buffer), static_cast<GLintptr>(offset), static_cast<GLsizeiptr>(size)); |
| } |
| |
| 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<GLuint> WebGL2RenderingContextBase::getUniformIndices(WebGLProgram* program, const Vector<String>& uniformNames) |
| { |
| Vector<GLuint> result; |
| if (isContextLost() || !validateWebGLObject("getUniformIndices", program)) |
| return result; |
| |
| Vector<CString> keepAlive; // Must keep these instances alive while looking at their data |
| Vector<const char*> 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; |
| } |
| |
| Vector<GLint> WebGL2RenderingContextBase::getActiveUniforms(WebGLProgram* program, const Vector<GLuint>& uniformIndices, GLenum pname) |
| { |
| Vector<GLint> result; |
| if (isContextLost() || !validateWebGLObject("getActiveUniforms", program)) |
| return result; |
| |
| if (pname == GL_UNIFORM_NAME_LENGTH) { |
| synthesizeGLError(GL_INVALID_ENUM, "getActiveUniforms", "invalid parameter name"); |
| return result; |
| } |
| |
| GLint activeUniforms = -1; |
| webContext()->getProgramiv(objectOrZero(program), GL_ACTIVE_UNIFORMS, &activeUniforms); |
| |
| GLuint activeUniformsUnsigned = activeUniforms; |
| for (size_t i = 0; i < uniformIndices.size(); ++i) { |
| if (uniformIndices[i] >= activeUniformsUnsigned) { |
| synthesizeGLError(GL_INVALID_VALUE, "getActiveUniforms", "uniform index greater than ACTIVE_UNIFORMS"); |
| return result; |
| } |
| } |
| |
| result.resize(uniformIndices.size()); |
| webContext()->getActiveUniformsiv(objectOrZero(program), uniformIndices.size(), uniformIndices.data(), pname, result.data()); |
| return result; |
| } |
| |
| 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()); |
| } |
| |
| ScriptValue WebGL2RenderingContextBase::getActiveUniformBlockParameter(ScriptState* scriptState, WebGLProgram* program, GLuint uniformBlockIndex, GLenum pname) |
| { |
| if (isContextLost() || !validateWebGLObject("getActiveUniformBlockParameter", program)) |
| 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<unsigned>(intValue)); |
| } |
| case GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES: |
| { |
| GLint uniformCount = 0; |
| webContext()->getActiveUniformBlockiv(objectOrZero(program), uniformBlockIndex, GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, &uniformCount); |
| |
| Vector<GLint> indices(uniformCount); |
| webContext()->getActiveUniformBlockiv(objectOrZero(program), uniformBlockIndex, pname, indices.data()); |
| return WebGLAny(scriptState, DOMUint32Array::create(reinterpret_cast<GLuint*>(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<bool>(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(); |
| |
| 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<GLchar[]> name = adoptArrayPtr(new GLchar[maxNameLength]); |
| GLsizei length; |
| 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; |
| |
| 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 getBooleanParameter(scriptState, pname); |
| 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 getBooleanParameter(scriptState, pname); |
| 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 getBooleanParameter(scriptState, pname); |
| case GL_UNPACK_SKIP_PIXELS: |
| return getBooleanParameter(scriptState, pname); |
| case GL_UNPACK_SKIP_ROWS: |
| return getBooleanParameter(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_OPERATION, 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_OPERATION, 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) |
| { |
| 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: |
| case GL_BYTE: |
| case GL_HALF_FLOAT: |
| case GL_FLOAT: |
| 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: |
| case GL_SHORT: |
| 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: |
| case GL_INT: |
| break; |
| default: |
| synthesizeGLError(GL_INVALID_ENUM, "readPixels", "invalid type"); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| DOMArrayBufferView::ViewType WebGL2RenderingContextBase::readPixelsExpectedArrayBufferViewType(GLenum type) |
| { |
| switch (type) { |
| case GL_BYTE: |
| return DOMArrayBufferView::TypeInt8; |
| case GL_UNSIGNED_SHORT: |
| return DOMArrayBufferView::TypeUint16; |
| case GL_SHORT: |
| return DOMArrayBufferView::TypeInt16; |
| case GL_HALF_FLOAT: |
| return DOMArrayBufferView::TypeUint16; |
| 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: |
| return DOMArrayBufferView::TypeUint32; |
| case GL_INT: |
| return DOMArrayBufferView::TypeInt32; |
| default: |
| return WebGLRenderingContextBase::readPixelsExpectedArrayBufferViewType(type); |
| } |
| } |
| |
| WebGLFramebuffer* WebGL2RenderingContextBase::getFramebufferBinding(GLenum target) |
| { |
| if (target == GL_READ_FRAMEBUFFER) |
| return m_readFramebufferBinding.get(); |
| return m_framebufferBinding.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<GLenum>(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_FRAMEBUFFER_DEFAULT); |
| default: |
| synthesizeGLError(GL_INVALID_ENUM, 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<unsigned>(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::validateTextureBinding(const char* functionName, GLenum target, bool useSixEnumsForCubeMap) |
| { |
| WebGLTexture* tex = nullptr; |
| switch (target) { |
| case GL_TEXTURE_2D_ARRAY: |
| tex = m_textureUnits[m_activeTextureUnit].m_texture2DArrayBinding.get(); |
| if (!tex) |
| synthesizeGLError(GL_INVALID_OPERATION, functionName, "no texture bound to GL_TEXTURE_2D_ARRAY"); |
| break; |
| case GL_TEXTURE_3D: |
| tex = m_textureUnits[m_activeTextureUnit].m_texture3DBinding.get(); |
| if (!tex) |
| synthesizeGLError(GL_INVALID_OPERATION, functionName, "no texture bound to GL_TEXTURE_3D"); |
| break; |
| default: |
| return WebGLRenderingContextBase::validateTextureBinding(functionName, target, useSixEnumsForCubeMap); |
| } |
| 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, false)) |
| 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<unsigned>(value)); |
| } |
| case GL_TEXTURE_IMMUTABLE_FORMAT: |
| { |
| GLint value = 0; |
| webContext()->getTexParameteriv(target, pname, &value); |
| return WebGLAny(scriptState, static_cast<bool>(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()); |
| } |
| |
| GLenum WebGL2RenderingContextBase::boundFramebufferColorFormat() |
| { |
| if (m_readFramebufferBinding && m_readFramebufferBinding->object()) |
| return m_readFramebufferBinding->colorBufferFormat(); |
| if (m_requestedAttributes.alpha()) |
| return GL_RGBA; |
| return GL_RGB; |
| } |
| |
| } // namespace blink |