| // Copyright 2015 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "modules/webgl/WebGL2RenderingContextBase.h" |
| |
| #include "bindings/modules/v8/WebGLAny.h" |
| #include "core/frame/ImageBitmap.h" |
| #include "core/html/HTMLCanvasElement.h" |
| #include "core/html/HTMLImageElement.h" |
| #include "core/html/HTMLVideoElement.h" |
| #include "core/html/ImageData.h" |
| #include "gpu/command_buffer/client/gles2_interface.h" |
| #include "modules/webgl/WebGLActiveInfo.h" |
| #include "modules/webgl/WebGLBuffer.h" |
| #include "modules/webgl/WebGLFenceSync.h" |
| #include "modules/webgl/WebGLFramebuffer.h" |
| #include "modules/webgl/WebGLProgram.h" |
| #include "modules/webgl/WebGLQuery.h" |
| #include "modules/webgl/WebGLRenderbuffer.h" |
| #include "modules/webgl/WebGLSampler.h" |
| #include "modules/webgl/WebGLSync.h" |
| #include "modules/webgl/WebGLTexture.h" |
| #include "modules/webgl/WebGLTransformFeedback.h" |
| #include "modules/webgl/WebGLUniformLocation.h" |
| #include "modules/webgl/WebGLVertexArrayObject.h" |
| #include "platform/CheckedInt.h" |
| #include "public/platform/WebGraphicsContext3DProvider.h" |
| #include "wtf/PtrUtil.h" |
| #include "wtf/text/WTFString.h" |
| #include <memory> |
| |
| using WTF::String; |
| |
| namespace blink { |
| |
| namespace { |
| |
| GLsync syncObjectOrZero(const WebGLSync* object) |
| { |
| return object ? object->object() : nullptr; |
| } |
| |
| } // namespace |
| |
| // These enums are from manual pages for glTexStorage2D/glTexStorage3D. |
| const GLenum kSupportedInternalFormatsStorage[] = { |
| GL_R8, |
| GL_R8_SNORM, |
| GL_R16F, |
| GL_R32F, |
| GL_R8UI, |
| GL_R8I, |
| GL_R16UI, |
| GL_R16I, |
| GL_R32UI, |
| GL_R32I, |
| GL_RG8, |
| GL_RG8_SNORM, |
| GL_RG16F, |
| GL_RG32F, |
| GL_RG8UI, |
| GL_RG8I, |
| GL_RG16UI, |
| GL_RG16I, |
| GL_RG32UI, |
| GL_RG32I, |
| GL_RGB8, |
| GL_SRGB8, |
| GL_RGB565, |
| GL_RGB8_SNORM, |
| GL_R11F_G11F_B10F, |
| GL_RGB9_E5, |
| GL_RGB16F, |
| GL_RGB32F, |
| GL_RGB8UI, |
| GL_RGB8I, |
| GL_RGB16UI, |
| GL_RGB16I, |
| GL_RGB32UI, |
| GL_RGB32I, |
| GL_RGBA8, |
| GL_SRGB8_ALPHA8, |
| GL_RGBA8_SNORM, |
| GL_RGB5_A1, |
| GL_RGBA4, |
| GL_RGB10_A2, |
| GL_RGBA16F, |
| GL_RGBA32F, |
| GL_RGBA8UI, |
| GL_RGBA8I, |
| GL_RGB10_A2UI, |
| GL_RGBA16UI, |
| GL_RGBA16I, |
| GL_RGBA32UI, |
| GL_RGBA32I, |
| GL_DEPTH_COMPONENT16, |
| GL_DEPTH_COMPONENT24, |
| GL_DEPTH_COMPONENT32F, |
| GL_DEPTH24_STENCIL8, |
| GL_DEPTH32F_STENCIL8, |
| }; |
| |
| const GLenum kCompressedTextureFormatsETC2EAC[] = { |
| GL_COMPRESSED_R11_EAC, |
| GL_COMPRESSED_SIGNED_R11_EAC, |
| GL_COMPRESSED_RG11_EAC, |
| GL_COMPRESSED_SIGNED_RG11_EAC, |
| GL_COMPRESSED_RGB8_ETC2, |
| GL_COMPRESSED_SRGB8_ETC2, |
| GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2, |
| GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2, |
| GL_COMPRESSED_RGBA8_ETC2_EAC, |
| GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC, |
| }; |
| |
| WebGL2RenderingContextBase::WebGL2RenderingContextBase(HTMLCanvasElement* passedCanvas, std::unique_ptr<WebGraphicsContext3DProvider> contextProvider, const WebGLContextAttributes& requestedAttributes) |
| : WebGLRenderingContextBase(passedCanvas, std::move(contextProvider), requestedAttributes, 2) |
| { |
| m_supportedInternalFormatsStorage.insert(kSupportedInternalFormatsStorage, kSupportedInternalFormatsStorage + WTF_ARRAY_LENGTH(kSupportedInternalFormatsStorage)); |
| m_supportedInternalFormatsStorage.insert(kCompressedTextureFormatsETC2EAC, kCompressedTextureFormatsETC2EAC + WTF_ARRAY_LENGTH(kCompressedTextureFormatsETC2EAC)); |
| m_compressedTextureFormatsETC2EAC.insert(kCompressedTextureFormatsETC2EAC, kCompressedTextureFormatsETC2EAC + WTF_ARRAY_LENGTH(kCompressedTextureFormatsETC2EAC)); |
| m_compressedTextureFormats.append(kCompressedTextureFormatsETC2EAC, WTF_ARRAY_LENGTH(kCompressedTextureFormatsETC2EAC)); |
| } |
| |
| WebGL2RenderingContextBase::~WebGL2RenderingContextBase() |
| { |
| m_readFramebufferBinding = nullptr; |
| |
| m_boundCopyReadBuffer = nullptr; |
| m_boundCopyWriteBuffer = nullptr; |
| m_boundPixelPackBuffer = nullptr; |
| m_boundPixelUnpackBuffer = nullptr; |
| m_boundTransformFeedbackBuffer = nullptr; |
| m_boundUniformBuffer = nullptr; |
| |
| m_currentBooleanOcclusionQuery = nullptr; |
| m_currentTransformFeedbackPrimitivesWrittenQuery = nullptr; |
| } |
| |
| void WebGL2RenderingContextBase::initializeNewContext() |
| { |
| ASSERT(!isContextLost()); |
| ASSERT(drawingBuffer()); |
| |
| m_readFramebufferBinding = nullptr; |
| |
| m_boundCopyReadBuffer = nullptr; |
| m_boundCopyWriteBuffer = nullptr; |
| m_boundPixelPackBuffer = nullptr; |
| m_boundPixelUnpackBuffer = nullptr; |
| m_boundTransformFeedbackBuffer = nullptr; |
| m_boundUniformBuffer = nullptr; |
| |
| m_currentBooleanOcclusionQuery = nullptr; |
| m_currentTransformFeedbackPrimitivesWrittenQuery = nullptr; |
| |
| GLint numCombinedTextureImageUnits = 0; |
| contextGL()->GetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &numCombinedTextureImageUnits); |
| m_samplerUnits.clear(); |
| m_samplerUnits.resize(numCombinedTextureImageUnits); |
| |
| m_maxTransformFeedbackSeparateAttribs = 0; |
| contextGL()->GetIntegerv(GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS, &m_maxTransformFeedbackSeparateAttribs); |
| m_boundIndexedTransformFeedbackBuffers.clear(); |
| m_boundIndexedTransformFeedbackBuffers.resize(m_maxTransformFeedbackSeparateAttribs); |
| |
| GLint maxUniformBufferBindings = 0; |
| contextGL()->GetIntegerv(GL_MAX_UNIFORM_BUFFER_BINDINGS, &maxUniformBufferBindings); |
| m_boundIndexedUniformBuffers.clear(); |
| m_boundIndexedUniformBuffers.resize(maxUniformBufferBindings); |
| m_maxBoundUniformBufferIndex = 0; |
| |
| m_packRowLength = 0; |
| m_packSkipPixels = 0; |
| m_packSkipRows = 0; |
| m_unpackRowLength = 0; |
| m_unpackImageHeight = 0; |
| m_unpackSkipPixels = 0; |
| m_unpackSkipRows = 0; |
| m_unpackSkipImages = 0; |
| |
| WebGLRenderingContextBase::initializeNewContext(); |
| } |
| |
| void WebGL2RenderingContextBase::copyBufferSubData(GLenum readTarget, GLenum writeTarget, long long readOffset, long long writeOffset, long long size) |
| { |
| if (isContextLost()) |
| return; |
| |
| if (!validateValueFitNonNegInt32("copyBufferSubData", "readOffset", readOffset) |
| || !validateValueFitNonNegInt32("copyBufferSubData", "writeOffset", writeOffset) |
| || !validateValueFitNonNegInt32("copyBufferSubData", "size", size)) { |
| return; |
| } |
| |
| WebGLBuffer* readBuffer = validateBufferDataTarget("copyBufferSubData", readTarget); |
| if (!readBuffer) |
| return; |
| |
| WebGLBuffer* writeBuffer = validateBufferDataTarget("copyBufferSubData", writeTarget); |
| if (!writeBuffer) |
| return; |
| |
| if (readOffset + size > readBuffer->getSize() || writeOffset + size > writeBuffer->getSize()) { |
| synthesizeGLError(GL_INVALID_VALUE, "copyBufferSubData", "buffer overflow"); |
| return; |
| } |
| |
| if ((writeBuffer->getInitialTarget() == GL_ELEMENT_ARRAY_BUFFER && readBuffer->getInitialTarget() != GL_ELEMENT_ARRAY_BUFFER) |
| || (writeBuffer->getInitialTarget() != GL_ELEMENT_ARRAY_BUFFER && readBuffer->getInitialTarget() == GL_ELEMENT_ARRAY_BUFFER)) { |
| synthesizeGLError(GL_INVALID_OPERATION, "copyBufferSubData", "Cannot copy into an element buffer destination from a non-element buffer source"); |
| return; |
| } |
| |
| if (writeBuffer->getInitialTarget() == 0) |
| writeBuffer->setInitialTarget(readBuffer->getInitialTarget()); |
| |
| contextGL()->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 = contextGL()->MapBufferRange(target, static_cast<GLintptr>(offset), returnedData->byteLength(), GL_MAP_READ_BIT); |
| |
| if (!mappedData) |
| return; |
| |
| memcpy(returnedData->data(), mappedData, returnedData->byteLength()); |
| |
| contextGL()->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; |
| |
| contextGL()->BlitFramebufferCHROMIUM(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter); |
| } |
| |
| bool WebGL2RenderingContextBase::validateTexFuncLayer(const char* functionName, GLenum texTarget, GLint layer) |
| { |
| if (layer < 0) { |
| synthesizeGLError(GL_INVALID_VALUE, functionName, "layer out of range"); |
| return false; |
| } |
| switch (texTarget) { |
| case GL_TEXTURE_3D: |
| if (layer > m_max3DTextureSize - 1) { |
| synthesizeGLError(GL_INVALID_VALUE, functionName, "layer out of range"); |
| return false; |
| } |
| break; |
| case GL_TEXTURE_2D_ARRAY: |
| if (layer > m_maxArrayTextureLayers - 1) { |
| synthesizeGLError(GL_INVALID_VALUE, functionName, "layer out of range"); |
| return false; |
| } |
| break; |
| default: |
| ASSERT_NOT_REACHED(); |
| return false; |
| } |
| return true; |
| } |
| |
| void WebGL2RenderingContextBase::framebufferTextureLayer(ScriptState* scriptState, GLenum target, GLenum attachment, WebGLTexture* texture, GLint level, GLint layer) |
| { |
| if (isContextLost() || !validateFramebufferFuncParameters("framebufferTextureLayer", target, attachment)) |
| return; |
| if (texture && !texture->validate(contextGroup(), this)) { |
| synthesizeGLError(GL_INVALID_VALUE, "framebufferTextureLayer", "no texture or texture not from this context"); |
| return; |
| } |
| GLenum textarget = texture ? texture->getTarget() : 0; |
| if (texture) { |
| if (textarget != GL_TEXTURE_3D && textarget != GL_TEXTURE_2D_ARRAY) { |
| synthesizeGLError(GL_INVALID_OPERATION, "framebufferTextureLayer", "invalid texture type"); |
| return; |
| } |
| if (!validateTexFuncLayer("framebufferTextureLayer", textarget, layer)) |
| return; |
| if (!validateTexFuncLevel("framebufferTextureLayer", textarget, level)) |
| return; |
| } |
| |
| WebGLFramebuffer* framebufferBinding = getFramebufferBinding(target); |
| if (!framebufferBinding || !framebufferBinding->object()) { |
| synthesizeGLError(GL_INVALID_OPERATION, "framebufferTextureLayer", "no framebuffer bound"); |
| return; |
| } |
| if (attachment == GL_DEPTH_STENCIL_ATTACHMENT) { |
| contextGL()->FramebufferTextureLayer(target, GL_DEPTH_ATTACHMENT, objectOrZero(texture), level, layer); |
| contextGL()->FramebufferTextureLayer(target, GL_STENCIL_ATTACHMENT, objectOrZero(texture), level, layer); |
| } else { |
| contextGL()->FramebufferTextureLayer(target, attachment, objectOrZero(texture), level, layer); |
| } |
| if (attachment == GL_DEPTH_STENCIL_ATTACHMENT) { |
| // On ES3, DEPTH_STENCIL_ATTACHMENT is like an alias for DEPTH_ATTACHMENT + STENCIL_ATTACHMENT. |
| // We divide it here so in WebGLFramebuffer, we don't have to handle DEPTH_STENCIL_ATTACHMENT in WebGL 2. |
| framebufferBinding->setAttachmentForBoundFramebuffer(target, GL_DEPTH_ATTACHMENT, textarget, texture, level, layer); |
| framebufferBinding->setAttachmentForBoundFramebuffer(target, GL_STENCIL_ATTACHMENT, textarget, texture, level, layer); |
| preserveObjectWrapper(scriptState, framebufferBinding, V8HiddenValue::webglAttachments(scriptState->isolate()), framebufferBinding->getPersistentCache(), GL_DEPTH_ATTACHMENT, texture); |
| preserveObjectWrapper(scriptState, framebufferBinding, V8HiddenValue::webglAttachments(scriptState->isolate()), framebufferBinding->getPersistentCache(), GL_STENCIL_ATTACHMENT, texture); |
| } else { |
| framebufferBinding->setAttachmentForBoundFramebuffer(target, attachment, textarget, texture, level, layer); |
| preserveObjectWrapper(scriptState, framebufferBinding, V8HiddenValue::webglAttachments(scriptState->isolate()), framebufferBinding->getPersistentCache(), attachment, texture); |
| } |
| applyStencilTest(); |
| } |
| |
| ScriptValue WebGL2RenderingContextBase::getInternalformatParameter(ScriptState* scriptState, GLenum target, GLenum internalformat, GLenum pname) |
| { |
| if (isContextLost()) |
| return ScriptValue::createNull(scriptState); |
| |
| if (target != GL_RENDERBUFFER) { |
| synthesizeGLError(GL_INVALID_ENUM, "getInternalformatParameter", "invalid target"); |
| return ScriptValue::createNull(scriptState); |
| } |
| |
| bool floatType = false; |
| |
| switch (internalformat) { |
| // Renderbuffer doesn't support unsized internal formats, |
| // though GL_RGB and GL_RGBA are color-renderable. |
| case GL_RGB: |
| case GL_RGBA: |
| // Multisampling is not supported for signed and unsigned integer internal formats. |
| case GL_R8UI: |
| case GL_R8I: |
| case GL_R16UI: |
| case GL_R16I: |
| case GL_R32UI: |
| case GL_R32I: |
| case GL_RG8UI: |
| case GL_RG8I: |
| case GL_RG16UI: |
| case GL_RG16I: |
| case GL_RG32UI: |
| case GL_RG32I: |
| case GL_RGBA8UI: |
| case GL_RGBA8I: |
| case GL_RGB10_A2UI: |
| case GL_RGBA16UI: |
| case GL_RGBA16I: |
| case GL_RGBA32UI: |
| case GL_RGBA32I: |
| return WebGLAny(scriptState, DOMInt32Array::create(0)); |
| case GL_R8: |
| case GL_RG8: |
| case GL_RGB8: |
| case GL_RGB565: |
| case GL_RGBA8: |
| case GL_SRGB8_ALPHA8: |
| case GL_RGB5_A1: |
| case GL_RGBA4: |
| case GL_RGB10_A2: |
| case GL_DEPTH_COMPONENT16: |
| case GL_DEPTH_COMPONENT24: |
| case GL_DEPTH_COMPONENT32F: |
| case GL_DEPTH24_STENCIL8: |
| case GL_DEPTH32F_STENCIL8: |
| case GL_STENCIL_INDEX8: |
| break; |
| case GL_R16F: |
| case GL_RG16F: |
| case GL_RGBA16F: |
| case GL_R32F: |
| case GL_RG32F: |
| case GL_RGBA32F: |
| case GL_R11F_G11F_B10F: |
| if (!extensionEnabled(EXTColorBufferFloatName)) { |
| synthesizeGLError(GL_INVALID_ENUM, "getInternalformatParameter", "invalid internalformat when EXT_color_buffer_float is not enabled"); |
| return ScriptValue::createNull(scriptState); |
| } |
| floatType = true; |
| break; |
| default: |
| synthesizeGLError(GL_INVALID_ENUM, "getInternalformatParameter", "invalid internalformat"); |
| return ScriptValue::createNull(scriptState); |
| } |
| |
| switch (pname) { |
| case GL_SAMPLES: |
| { |
| std::unique_ptr<GLint[]> values; |
| GLint length = -1; |
| if (!floatType) { |
| contextGL()->GetInternalformativ(target, internalformat, GL_NUM_SAMPLE_COUNTS, 1, &length); |
| if (length <= 0) |
| return WebGLAny(scriptState, DOMInt32Array::create(0)); |
| |
| values = wrapArrayUnique(new GLint[length]); |
| for (GLint ii = 0; ii < length; ++ii) |
| values[ii] = 0; |
| contextGL()->GetInternalformativ(target, internalformat, GL_SAMPLES, length, values.get()); |
| } else { |
| length = 1; |
| values = wrapArrayUnique(new GLint[1]); |
| values[0] = 1; |
| } |
| return WebGLAny(scriptState, DOMInt32Array::create(values.get(), length)); |
| } |
| default: |
| synthesizeGLError(GL_INVALID_ENUM, "getInternalformatParameter", "invalid parameter name"); |
| return ScriptValue::createNull(scriptState); |
| } |
| } |
| |
| bool WebGL2RenderingContextBase::checkAndTranslateAttachments(const char* functionName, GLenum target, Vector<GLenum>& attachments) |
| { |
| 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 |
| // Translate GL_COLOR/GL_DEPTH/GL_STENCIL, because the default framebuffer of WebGL is not fb 0, it is an internal fbo |
| for (size_t i = 0; i < attachments.size(); ++i) { |
| switch (attachments[i]) { |
| case GL_COLOR: |
| attachments[i] = GL_COLOR_ATTACHMENT0; |
| break; |
| case GL_DEPTH: |
| attachments[i] = GL_DEPTH_ATTACHMENT; |
| break; |
| case GL_STENCIL: |
| attachments[i] = GL_STENCIL_ATTACHMENT; |
| break; |
| default: |
| synthesizeGLError(GL_INVALID_ENUM, functionName, "invalid attachment"); |
| return false; |
| } |
| } |
| } |
| return true; |
| } |
| |
| bool WebGL2RenderingContextBase::canUseTexImageByGPU(TexImageFunctionID functionID, GLint internalformat, GLenum type) |
| { |
| switch (internalformat) { |
| case GL_RGB565: |
| case GL_RGBA4: |
| case GL_RGB5_A1: |
| // FIXME: ES3 limitation that CopyTexImage with sized internalformat, |
| // component sizes have to match the source color format. |
| return false; |
| default: |
| break; |
| } |
| return WebGLRenderingContextBase::canUseTexImageByGPU(functionID, internalformat, type); |
| } |
| |
| void WebGL2RenderingContextBase::invalidateFramebuffer(GLenum target, const Vector<GLenum>& attachments) |
| { |
| if (isContextLost()) |
| return; |
| |
| Vector<GLenum> translatedAttachments = attachments; |
| if (!checkAndTranslateAttachments("invalidateFramebuffer", target, translatedAttachments)) |
| return; |
| contextGL()->InvalidateFramebuffer(target, translatedAttachments.size(), translatedAttachments.data()); |
| } |
| |
| void WebGL2RenderingContextBase::invalidateSubFramebuffer(GLenum target, const Vector<GLenum>& attachments, GLint x, GLint y, GLsizei width, GLsizei height) |
| { |
| if (isContextLost()) |
| return; |
| |
| Vector<GLenum> translatedAttachments = attachments; |
| if (!checkAndTranslateAttachments("invalidateSubFramebuffer", target, translatedAttachments)) |
| return; |
| contextGL()->InvalidateSubFramebuffer(target, translatedAttachments.size(), translatedAttachments.data(), x, y, width, height); |
| } |
| |
| void WebGL2RenderingContextBase::readBuffer(GLenum mode) |
| { |
| if (isContextLost()) |
| return; |
| |
| switch (mode) { |
| case GL_BACK: |
| case GL_NONE: |
| case GL_COLOR_ATTACHMENT0: |
| break; |
| default: |
| if (mode > GL_COLOR_ATTACHMENT0 |
| && mode < static_cast<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); |
| } |
| contextGL()->ReadBuffer(mode); |
| } |
| |
| void WebGL2RenderingContextBase::pixelStorei(GLenum pname, GLint param) |
| { |
| if (isContextLost()) |
| return; |
| if (param < 0) { |
| synthesizeGLError(GL_INVALID_VALUE, "pixelStorei", "negative value"); |
| return; |
| } |
| switch (pname) { |
| case GL_PACK_ROW_LENGTH: |
| m_packRowLength = param; |
| break; |
| case GL_PACK_SKIP_PIXELS: |
| m_packSkipPixels = param; |
| break; |
| case GL_PACK_SKIP_ROWS: |
| m_packSkipRows = param; |
| break; |
| case GL_UNPACK_ROW_LENGTH: |
| m_unpackRowLength = param; |
| break; |
| case GL_UNPACK_IMAGE_HEIGHT: |
| m_unpackImageHeight = param; |
| break; |
| case GL_UNPACK_SKIP_PIXELS: |
| m_unpackSkipPixels = param; |
| break; |
| case GL_UNPACK_SKIP_ROWS: |
| m_unpackSkipRows = param; |
| break; |
| case GL_UNPACK_SKIP_IMAGES: |
| m_unpackSkipImages = param; |
| break; |
| default: |
| WebGLRenderingContextBase::pixelStorei(pname, param); |
| return; |
| } |
| contextGL()->PixelStorei(pname, param); |
| } |
| |
| void WebGL2RenderingContextBase::readPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, DOMArrayBufferView* pixels) |
| { |
| if (isContextLost()) |
| return; |
| if (m_boundPixelPackBuffer.get()) { |
| synthesizeGLError(GL_INVALID_OPERATION, "readPixels", "PIXEL_PACK buffer should not be bound"); |
| return; |
| } |
| |
| WebGLRenderingContextBase::readPixels(x, y, width, height, format, type, pixels); |
| } |
| |
| void WebGL2RenderingContextBase::readPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, long long offset) |
| { |
| if (isContextLost()) |
| return; |
| |
| // Due to WebGL's same-origin restrictions, it is not possible to |
| // taint the origin using the WebGL API. |
| ASSERT(canvas()->originClean()); |
| |
| if (!validateValueFitNonNegInt32("readPixels", "offset", offset)) |
| return; |
| |
| WebGLBuffer* buffer = m_boundPixelPackBuffer.get(); |
| if (!buffer) { |
| synthesizeGLError(GL_INVALID_OPERATION, "readPixels", "no PIXEL_PACK buffer bound"); |
| return; |
| } |
| |
| const char* reason = "framebuffer incomplete"; |
| WebGLFramebuffer* framebuffer = getReadFramebufferBinding(); |
| if (framebuffer && framebuffer->checkDepthStencilStatus(&reason) != GL_FRAMEBUFFER_COMPLETE) { |
| synthesizeGLError(GL_INVALID_FRAMEBUFFER_OPERATION, "readPixels", reason); |
| return; |
| } |
| |
| long long size = buffer->getSize() - offset; |
| // If size is negative, or size is not large enough to store pixels, those cases |
| // are handled by validateReadPixelsFuncParameters to generate INVALID_OPERATION. |
| if (!validateReadPixelsFuncParameters(width, height, format, type, nullptr, size)) |
| return; |
| |
| clearIfComposited(); |
| |
| { |
| ScopedDrawingBufferBinder binder(drawingBuffer(), framebuffer); |
| contextGL()->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) { |
| contextGL()->RenderbufferStorage(target, internalformat, width, height); |
| } else { |
| GLint maxNumberOfSamples = 0; |
| contextGL()->GetInternalformativ(target, internalformat, GL_SAMPLES, 1, &maxNumberOfSamples); |
| if (samples > maxNumberOfSamples) { |
| synthesizeGLError(GL_INVALID_OPERATION, functionName, "samples out of range"); |
| return; |
| } |
| contextGL()->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; |
| } |
| contextGL()->RenderbufferStorage(target, GL_DEPTH24_STENCIL8, width, height); |
| break; |
| case GL_R16F: |
| case GL_RG16F: |
| case GL_RGBA16F: |
| case GL_R32F: |
| case GL_RG32F: |
| case GL_RGBA32F: |
| case GL_R11F_G11F_B10F: |
| if (!extensionEnabled(EXTColorBufferFloatName)) { |
| synthesizeGLError(GL_INVALID_ENUM, functionName, "EXT_color_buffer_float not enabled"); |
| return; |
| } |
| if (samples) { |
| synthesizeGLError(GL_INVALID_VALUE, functionName, "multisampled float buffers not supported"); |
| return; |
| } |
| contextGL()->RenderbufferStorage(target, internalformat, width, height); |
| break; |
| default: |
| synthesizeGLError(GL_INVALID_ENUM, functionName, "invalid internalformat"); |
| return; |
| } |
| m_renderbufferBinding->setInternalFormat(internalformat); |
| m_renderbufferBinding->setSize(width, height); |
| } |
| |
| void WebGL2RenderingContextBase::renderbufferStorageMultisample(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height) |
| { |
| const char* functionName = "renderbufferStorageMultisample"; |
| if (isContextLost()) |
| return; |
| if (target != GL_RENDERBUFFER) { |
| synthesizeGLError(GL_INVALID_ENUM, functionName, "invalid target"); |
| return; |
| } |
| if (!m_renderbufferBinding || !m_renderbufferBinding->object()) { |
| synthesizeGLError(GL_INVALID_OPERATION, functionName, "no bound renderbuffer"); |
| return; |
| } |
| if (!validateSize("renderbufferStorage", width, height)) |
| return; |
| if (samples < 0) { |
| synthesizeGLError(GL_INVALID_VALUE, functionName, "samples < 0"); |
| return; |
| } |
| renderbufferStorageImpl(target, samples, internalformat, width, height, functionName); |
| applyStencilTest(); |
| } |
| |
| void WebGL2RenderingContextBase::resetUnpackParameters() |
| { |
| WebGLRenderingContextBase::resetUnpackParameters(); |
| |
| if (!m_unpackRowLength) |
| contextGL()->PixelStorei(GL_UNPACK_ROW_LENGTH, 0); |
| if (!m_unpackImageHeight) |
| contextGL()->PixelStorei(GL_UNPACK_IMAGE_HEIGHT, 0); |
| if (!m_unpackSkipPixels) |
| contextGL()->PixelStorei(GL_UNPACK_SKIP_PIXELS, 0); |
| if (!m_unpackSkipRows) |
| contextGL()->PixelStorei(GL_UNPACK_SKIP_ROWS, 0); |
| if (!m_unpackSkipImages) |
| contextGL()->PixelStorei(GL_UNPACK_SKIP_IMAGES, 0); |
| } |
| |
| void WebGL2RenderingContextBase::restoreUnpackParameters() |
| { |
| WebGLRenderingContextBase::restoreUnpackParameters(); |
| |
| if (!m_unpackRowLength) |
| contextGL()->PixelStorei(GL_UNPACK_ROW_LENGTH, m_unpackRowLength); |
| if (!m_unpackImageHeight) |
| contextGL()->PixelStorei(GL_UNPACK_IMAGE_HEIGHT, m_unpackImageHeight); |
| if (!m_unpackSkipPixels) |
| contextGL()->PixelStorei(GL_UNPACK_SKIP_PIXELS, m_unpackSkipPixels); |
| if (!m_unpackSkipRows) |
| contextGL()->PixelStorei(GL_UNPACK_SKIP_ROWS, m_unpackSkipRows); |
| if (!m_unpackSkipImages) |
| contextGL()->PixelStorei(GL_UNPACK_SKIP_IMAGES, m_unpackSkipImages); |
| } |
| |
| /* Texture objects */ |
| bool WebGL2RenderingContextBase::validateTexStorage(const char* functionName, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, TexStorageType functionType) |
| { |
| if (functionType == TexStorageType2D) { |
| if (target != GL_TEXTURE_2D && target != GL_TEXTURE_CUBE_MAP) { |
| synthesizeGLError(GL_INVALID_ENUM, functionName, "invalid 2D target"); |
| return false; |
| } |
| } else { |
| if (target != GL_TEXTURE_3D && target != GL_TEXTURE_2D_ARRAY) { |
| synthesizeGLError(GL_INVALID_ENUM, functionName, "invalid 3D target"); |
| return false; |
| } |
| } |
| |
| if (functionType == TexStorageType3D && target != GL_TEXTURE_2D_ARRAY && m_compressedTextureFormatsETC2EAC.find(internalformat) != m_compressedTextureFormatsETC2EAC.end()) { |
| synthesizeGLError(GL_INVALID_OPERATION, functionName, "target for ETC2/EAC internal formats must be TEXTURE_2D_ARRAY"); |
| return false; |
| } |
| |
| if (m_supportedInternalFormatsStorage.find(internalformat) == m_supportedInternalFormatsStorage.end() |
| && (functionType == TexStorageType2D && !m_compressedTextureFormats.contains(internalformat))) { |
| synthesizeGLError(GL_INVALID_ENUM, functionName, "invalid internalformat"); |
| return false; |
| } |
| |
| if (width <= 0 || height <= 0 || depth <= 0) { |
| synthesizeGLError(GL_INVALID_VALUE, functionName, "invalid dimensions"); |
| return false; |
| } |
| |
| if (levels <= 0) { |
| synthesizeGLError(GL_INVALID_VALUE, functionName, "invalid levels"); |
| return false; |
| } |
| |
| if (target == GL_TEXTURE_3D) { |
| if (levels > log2(std::max(std::max(width, height), depth)) + 1) { |
| synthesizeGLError(GL_INVALID_OPERATION, functionName, "to many levels"); |
| return false; |
| } |
| } else { |
| if (levels > log2(std::max(width, height)) + 1) { |
| synthesizeGLError(GL_INVALID_OPERATION, functionName, "to many levels"); |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| void WebGL2RenderingContextBase::texImage2D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, GLintptr offset) |
| { |
| if (isContextLost()) |
| return; |
| if (!validateTexture2DBinding("texImage2D", target)) |
| return; |
| if (!m_boundPixelUnpackBuffer) { |
| synthesizeGLError(GL_INVALID_OPERATION, "texImage2D", "no bound PIXEL_UNPACK_BUFFER"); |
| return; |
| } |
| if (!validateTexFunc("texImage2D", TexImage, SourceUnpackBuffer, target, level, internalformat, width, height, 1, border, format, type, 0, 0, 0)) |
| return; |
| if (!validateValueFitNonNegInt32("texImage2D", "offset", offset)) |
| return; |
| |
| contextGL()->TexImage2D(target, level, convertTexInternalFormat(internalformat, type), width, height, border, format, type, reinterpret_cast<const void *>(offset)); |
| } |
| |
| void WebGL2RenderingContextBase::texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLintptr offset) |
| { |
| if (isContextLost()) |
| return; |
| if (!validateTexture2DBinding("texSubImage2D", target)) |
| return; |
| if (!m_boundPixelUnpackBuffer) { |
| synthesizeGLError(GL_INVALID_OPERATION, "texSubImage2D", "no bound PIXEL_UNPACK_BUFFER"); |
| return; |
| } |
| if (!validateTexFunc("texSubImage2D", TexSubImage, SourceUnpackBuffer, target, level, 0, width, height, 1, 0, format, type, xoffset, yoffset, 0)) |
| return; |
| if (!validateValueFitNonNegInt32("texSubImage2D", "offset", offset)) |
| return; |
| |
| contextGL()->TexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, reinterpret_cast<const void*>(offset)); |
| } |
| |
| void WebGL2RenderingContextBase::texImage2D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, DOMArrayBufferView* data) |
| { |
| WebGLRenderingContextBase::texImage2D(target, level, internalformat, width, height, border, format, type, data); |
| } |
| |
| void WebGL2RenderingContextBase::texImage2D(GLenum target, GLint level, GLint internalformat, GLenum format, GLenum type, ImageData* imageData) |
| { |
| WebGLRenderingContextBase::texImage2D(target, level, internalformat, format, type, imageData); |
| } |
| |
| void WebGL2RenderingContextBase::texImage2D(GLenum target, GLint level, GLint internalformat, GLenum format, GLenum type, HTMLImageElement* image, ExceptionState& exceptionState) |
| { |
| WebGLRenderingContextBase::texImage2D(target, level, internalformat, format, type, image, exceptionState); |
| } |
| |
| void WebGL2RenderingContextBase::texImage2D(GLenum target, GLint level, GLint internalformat, GLenum format, GLenum type, HTMLCanvasElement* canvas, ExceptionState& exceptionState) |
| { |
| WebGLRenderingContextBase::texImage2D(target, level, internalformat, format, type, canvas, exceptionState); |
| } |
| |
| void WebGL2RenderingContextBase::texImage2D(GLenum target, GLint level, GLint internalformat, GLenum format, GLenum type, HTMLVideoElement* video, ExceptionState& exceptionState) |
| { |
| WebGLRenderingContextBase::texImage2D(target, level, internalformat, format, type, video, exceptionState); |
| } |
| |
| void WebGL2RenderingContextBase::texImage2D(GLenum target, GLint level, GLint internalformat, GLenum format, GLenum type, ImageBitmap* imageBitMap, ExceptionState& exceptionState) |
| { |
| WebGLRenderingContextBase::texImage2D(target, level, internalformat, format, type, imageBitMap, exceptionState); |
| } |
| |
| void WebGL2RenderingContextBase::texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, |
| GLsizei width, GLsizei height, GLenum format, GLenum type, DOMArrayBufferView* pixels) |
| { |
| WebGLRenderingContextBase::texSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels); |
| } |
| |
| void WebGL2RenderingContextBase::texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, |
| GLenum format, GLenum type, ImageData* pixels) |
| { |
| WebGLRenderingContextBase::texSubImage2D(target, level, xoffset, yoffset, format, type, pixels); |
| } |
| |
| void WebGL2RenderingContextBase::texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, |
| GLenum format, GLenum type, HTMLImageElement* image, ExceptionState& exceptionState) |
| { |
| WebGLRenderingContextBase::texSubImage2D(target, level, xoffset, yoffset, format, type, image, exceptionState); |
| } |
| |
| void WebGL2RenderingContextBase::texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, |
| GLenum format, GLenum type, HTMLCanvasElement* canvas, ExceptionState& exceptionState) |
| { |
| WebGLRenderingContextBase::texSubImage2D(target, level, xoffset, yoffset, format, type, canvas, exceptionState); |
| } |
| |
| void WebGL2RenderingContextBase::texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, |
| GLenum format, GLenum type, HTMLVideoElement* video, ExceptionState& exceptionState) |
| { |
| WebGLRenderingContextBase::texSubImage2D(target, level, xoffset, yoffset, format, type, video, exceptionState); |
| } |
| |
| void WebGL2RenderingContextBase::texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, |
| GLenum format, GLenum type, ImageBitmap* bitmap, ExceptionState& exceptionState) |
| { |
| WebGLRenderingContextBase::texSubImage2D(target, level, xoffset, yoffset, format, type, bitmap, exceptionState); |
| } |
| |
| void WebGL2RenderingContextBase::texStorage2D(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height) |
| { |
| if (isContextLost() || !validateTexStorage("texStorage2D", target, levels, internalformat, width, height, 1, TexStorageType2D)) |
| return; |
| |
| contextGL()->TexStorage2DEXT(target, levels, internalformat, width, height); |
| } |
| |
| void WebGL2RenderingContextBase::texStorage3D(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth) |
| { |
| if (isContextLost() || !validateTexStorage("texStorage3D", target, levels, internalformat, width, height, depth, TexStorageType3D)) |
| return; |
| |
| contextGL()->TexStorage3D(target, levels, internalformat, width, height, depth); |
| } |
| |
| void WebGL2RenderingContextBase::texImage3D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, DOMArrayBufferView* pixels) |
| { |
| texImageHelperDOMArrayBufferView(TexImage3D, target, level, internalformat, width, height, border, format, type, depth, 0, 0, 0, pixels); |
| } |
| |
| void WebGL2RenderingContextBase::texImage3D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, GLintptr offset) |
| { |
| if (isContextLost()) |
| return; |
| if (!validateTexture3DBinding("texImage3D", target)) |
| return; |
| if (!m_boundPixelUnpackBuffer) { |
| synthesizeGLError(GL_INVALID_OPERATION, "texImage3D", "no bound PIXEL_UNPACK_BUFFER"); |
| return; |
| } |
| if (!validateTexFunc("texImage3D", TexImage, SourceUnpackBuffer, target, level, internalformat, width, height, depth, border, format, type, 0, 0, 0)) |
| return; |
| if (!validateValueFitNonNegInt32("texImage3D", "offset", offset)) |
| return; |
| |
| contextGL()->TexImage3D(target, level, convertTexInternalFormat(internalformat, type), width, height, depth, border, format, type, reinterpret_cast<const void *>(offset)); |
| } |
| |
| 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) |
| { |
| texImageHelperDOMArrayBufferView(TexSubImage3D, target, level, 0, width, height, 0, format, type, depth, xoffset, yoffset, zoffset, pixels); |
| } |
| |
| void WebGL2RenderingContextBase::texSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, GLintptr offset) |
| { |
| if (isContextLost()) |
| return; |
| if (!validateTexture3DBinding("texSubImage3D", target)) |
| return; |
| if (!m_boundPixelUnpackBuffer) { |
| synthesizeGLError(GL_INVALID_OPERATION, "texSubImage3D", "no bound PIXEL_UNPACK_BUFFER"); |
| return; |
| } |
| if (!validateTexFunc("texSubImage3D", TexSubImage, SourceUnpackBuffer, target, level, 0, width, height, depth, 0, format, type, xoffset, yoffset, zoffset)) |
| return; |
| if (!validateValueFitNonNegInt32("texSubImage3D", "offset", offset)) |
| return; |
| |
| contextGL()->TexSubImage3D(target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, reinterpret_cast<const void*>(offset)); |
| } |
| |
| void WebGL2RenderingContextBase::texSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLenum format, GLenum type, ImageData* pixels) |
| { |
| texImageHelperImageData(TexSubImage3D, target, level, 0, 0, format, type, 1, xoffset, yoffset, zoffset, pixels); |
| } |
| |
| void WebGL2RenderingContextBase::texSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLenum format, GLenum type, HTMLImageElement* image, ExceptionState& exceptionState) |
| { |
| texImageHelperHTMLImageElement(TexSubImage3D, target, level, 0, format, type, xoffset, yoffset, zoffset, image, exceptionState); |
| } |
| |
| void WebGL2RenderingContextBase::texSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLenum format, GLenum type, HTMLCanvasElement* canvas, ExceptionState& exceptionState) |
| { |
| texImageHelperHTMLCanvasElement(TexSubImage3D, target, level, 0, format, type, xoffset, yoffset, zoffset, canvas, exceptionState); |
| } |
| |
| void WebGL2RenderingContextBase::texSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLenum format, GLenum type, HTMLVideoElement* video, ExceptionState& exceptionState) |
| { |
| texImageHelperHTMLVideoElement(TexSubImage3D, target, level, 0, format, type, xoffset, yoffset, zoffset, video, exceptionState); |
| } |
| |
| void WebGL2RenderingContextBase::texSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLenum format, GLenum type, ImageBitmap* bitmap, ExceptionState& exceptionState) |
| { |
| texImageHelperImageBitmap(TexSubImage3D, target, level, 0, format, type, xoffset, yoffset, zoffset, bitmap, exceptionState); |
| } |
| |
| void WebGL2RenderingContextBase::copyTexSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height) |
| { |
| if (isContextLost()) |
| return; |
| if (!validateTexture3DBinding("copyTexSubImage3D", target)) |
| return; |
| WebGLFramebuffer* readFramebufferBinding = nullptr; |
| if (!validateReadBufferAndGetInfo("copyTexSubImage3D", readFramebufferBinding)) |
| return; |
| clearIfComposited(); |
| ScopedDrawingBufferBinder binder(drawingBuffer(), readFramebufferBinding); |
| contextGL()->CopyTexSubImage3D(target, level, xoffset, yoffset, zoffset, x, y, width, height); |
| } |
| |
| void WebGL2RenderingContextBase::compressedTexImage3D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, DOMArrayBufferView* data) |
| { |
| if (isContextLost()) |
| return; |
| if (!validateTexture3DBinding("compressedTexImage3D", target)) |
| return; |
| if (!validateCompressedTexFormat("compressedTexImage3D", internalformat)) |
| return; |
| contextGL()->CompressedTexImage3D(target, level, internalformat, width, height, depth, border, data->byteLength(), data->baseAddress()); |
| } |
| |
| void WebGL2RenderingContextBase::compressedTexSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, DOMArrayBufferView* data) |
| { |
| if (isContextLost()) |
| return; |
| if (!validateTexture3DBinding("compressedTexSubImage3D", target)) |
| return; |
| if (!validateCompressedTexFormat("compressedTexSubImage3D", format)) |
| return; |
| contextGL()->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 contextGL()->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; |
| } |
| |
| contextGL()->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; |
| } |
| |
| contextGL()->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; |
| } |
| |
| contextGL()->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; |
| } |
| |
| contextGL()->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; |
| |
| contextGL()->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; |
| |
| contextGL()->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; |
| |
| contextGL()->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; |
| |
| contextGL()->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; |
| |
| contextGL()->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; |
| |
| contextGL()->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; |
| |
| contextGL()->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; |
| |
| contextGL()->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; |
| contextGL()->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; |
| contextGL()->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; |
| contextGL()->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; |
| contextGL()->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; |
| contextGL()->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; |
| contextGL()->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; |
| contextGL()->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; |
| contextGL()->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; |
| contextGL()->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; |
| contextGL()->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; |
| contextGL()->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; |
| contextGL()->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; |
| contextGL()->VertexAttribI4i(index, x, y, z, w); |
| setVertexAttribType(index, Int32ArrayType); |
| } |
| |
| void WebGL2RenderingContextBase::vertexAttribI4iv(GLuint index, const DOMInt32Array* v) |
| { |
| if (isContextLost()) |
| return; |
| if (!v || v->length() < 4) { |
| synthesizeGLError(GL_INVALID_VALUE, "vertexAttribI4iv", "invalid array"); |
| return; |
| } |
| contextGL()->VertexAttribI4iv(index, v->data()); |
| setVertexAttribType(index, Int32ArrayType); |
| } |
| |
| void WebGL2RenderingContextBase::vertexAttribI4iv(GLuint index, const Vector<GLint>& v) |
| { |
| if (isContextLost()) |
| return; |
| if (v.size() < 4) { |
| synthesizeGLError(GL_INVALID_VALUE, "vertexAttribI4iv", "invalid array"); |
| return; |
| } |
| contextGL()->VertexAttribI4iv(index, v.data()); |
| setVertexAttribType(index, Int32ArrayType); |
| } |
| |
| void WebGL2RenderingContextBase::vertexAttribI4ui(GLuint index, GLuint x, GLuint y, GLuint z, GLuint w) |
| { |
| if (isContextLost()) |
| return; |
| contextGL()->VertexAttribI4ui(index, x, y, z, w); |
| setVertexAttribType(index, Uint32ArrayType); |
| } |
| |
| void WebGL2RenderingContextBase::vertexAttribI4uiv(GLuint index, const DOMUint32Array* v) |
| { |
| if (isContextLost()) |
| return; |
| if (!v || v->length() < 4) { |
| synthesizeGLError(GL_INVALID_VALUE, "vertexAttribI4uiv", "invalid array"); |
| return; |
| } |
| contextGL()->VertexAttribI4uiv(index, v->data()); |
| setVertexAttribType(index, Uint32ArrayType); |
| } |
| |
| void WebGL2RenderingContextBase::vertexAttribI4uiv(GLuint index, const Vector<GLuint>& v) |
| { |
| if (isContextLost()) |
| return; |
| if (v.size() < 4) { |
| synthesizeGLError(GL_INVALID_VALUE, "vertexAttribI4uiv", "invalid array"); |
| return; |
| } |
| contextGL()->VertexAttribI4uiv(index, v.data()); |
| setVertexAttribType(index, Uint32ArrayType); |
| } |
| |
| void WebGL2RenderingContextBase::vertexAttribIPointer(GLuint index, GLint size, GLenum type, GLsizei stride, long long offset) |
| { |
| if (isContextLost()) |
| return; |
| if (index >= m_maxVertexAttribs) { |
| synthesizeGLError(GL_INVALID_VALUE, "vertexAttribIPointer", "index out of range"); |
| return; |
| } |
| if (!validateValueFitNonNegInt32("vertexAttribIPointer", "offset", offset)) |
| return; |
| if (!m_boundArrayBuffer) { |
| synthesizeGLError(GL_INVALID_OPERATION, "vertexAttribIPointer", "no bound ARRAY_BUFFER"); |
| return; |
| } |
| |
| m_boundVertexArrayObject->setArrayBufferForAttrib(index, m_boundArrayBuffer); |
| contextGL()->VertexAttribIPointer(index, size, type, stride, reinterpret_cast<void*>(static_cast<intptr_t>(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; |
| } |
| |
| contextGL()->VertexAttribDivisorANGLE(index, divisor); |
| } |
| |
| void WebGL2RenderingContextBase::drawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsizei instanceCount) |
| { |
| if (!validateDrawArrays("drawArraysInstanced")) |
| return; |
| |
| if (!m_boundVertexArrayObject->isAllEnabledAttribBufferBound()) { |
| synthesizeGLError(GL_INVALID_OPERATION, "drawArraysInstanced", "no buffer is bound to enabled attribute"); |
| return; |
| } |
| |
| ScopedRGBEmulationColorMask emulationColorMask(contextGL(), m_colorMask, m_drawingBuffer.get()); |
| clearIfComposited(); |
| contextGL()->DrawArraysInstancedANGLE(mode, first, count, instanceCount); |
| markContextChanged(CanvasChanged); |
| } |
| |
| void WebGL2RenderingContextBase::drawElementsInstanced(GLenum mode, GLsizei count, GLenum type, long long offset, GLsizei instanceCount) |
| { |
| if (!validateDrawElements("drawElementsInstanced", type, offset)) |
| return; |
| |
| if (!m_boundVertexArrayObject->isAllEnabledAttribBufferBound()) { |
| synthesizeGLError(GL_INVALID_OPERATION, "drawElementsInstanced", "no buffer is bound to enabled attribute"); |
| return; |
| } |
| |
| ScopedRGBEmulationColorMask emulationColorMask(contextGL(), m_colorMask, m_drawingBuffer.get()); |
| clearIfComposited(); |
| contextGL()->DrawElementsInstancedANGLE(mode, count, type, reinterpret_cast<void*>(static_cast<intptr_t>(offset)), instanceCount); |
| markContextChanged(CanvasChanged); |
| } |
| |
| void WebGL2RenderingContextBase::drawRangeElements(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, long long offset) |
| { |
| if (!validateDrawElements("drawRangeElements", type, offset)) |
| return; |
| |
| if (!m_boundVertexArrayObject->isAllEnabledAttribBufferBound()) { |
| synthesizeGLError(GL_INVALID_OPERATION, "drawRangeElements", "no buffer is bound to enabled attribute"); |
| return; |
| } |
| |
| ScopedRGBEmulationColorMask emulationColorMask(contextGL(), m_colorMask, m_drawingBuffer.get()); |
| clearIfComposited(); |
| contextGL()->DrawRangeElements(mode, start, end, count, type, reinterpret_cast<void*>(static_cast<intptr_t>(offset))); |
| markContextChanged(CanvasChanged); |
| } |
| |
| void WebGL2RenderingContextBase::drawBuffers(const Vector<GLenum>& buffers) |
| { |
| if (isContextLost()) |
| return; |
| |
| ScopedRGBEmulationColorMask emulationColorMask(contextGL(), m_colorMask, m_drawingBuffer.get()); |
| GLsizei n = buffers.size(); |
| const GLenum* bufs = buffers.data(); |
| for (GLsizei i = 0; i < n; ++i) { |
| switch (bufs[i]) { |
| case GL_NONE: |
| case GL_BACK: |
| case GL_COLOR_ATTACHMENT0: |
| break; |
| default: |
| if (bufs[i] > GL_COLOR_ATTACHMENT0 |
| && bufs[i] < static_cast<GLenum>(GL_COLOR_ATTACHMENT0 + maxColorAttachments())) |
| break; |
| synthesizeGLError(GL_INVALID_ENUM, "drawBuffers", "invalid buffer"); |
| return; |
| } |
| } |
| if (!m_framebufferBinding) { |
| if (n != 1) { |
| synthesizeGLError(GL_INVALID_OPERATION, "drawBuffers", "the number of buffers is not 1"); |
| return; |
| } |
| if (bufs[0] != GL_BACK && bufs[0] != GL_NONE) { |
| synthesizeGLError(GL_INVALID_OPERATION, "drawBuffers", "BACK or NONE"); |
| return; |
| } |
| // Because the backbuffer is simulated on all current WebKit ports, we need to change BACK to COLOR_ATTACHMENT0. |
| GLenum value = (bufs[0] == GL_BACK) ? GL_COLOR_ATTACHMENT0 : GL_NONE; |
| contextGL()->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: |
| 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; |
| } |
| |
| WebGLTexture* WebGL2RenderingContextBase::validateTexImageBinding(const char* funcName, TexImageFunctionID functionID, GLenum target) |
| { |
| if (functionID == TexImage3D || functionID == TexSubImage3D) |
| return validateTexture3DBinding(funcName, target); |
| return validateTexture2DBinding(funcName, target); |
| } |
| |
| void WebGL2RenderingContextBase::clearBufferiv(GLenum buffer, GLint drawbuffer, DOMInt32Array* value) |
| { |
| if (isContextLost() || !validateClearBuffer("clearBufferiv", buffer, value->length())) |
| return; |
| |
| contextGL()->ClearBufferiv(buffer, drawbuffer, value->data()); |
| } |
| |
| void WebGL2RenderingContextBase::clearBufferiv(GLenum buffer, GLint drawbuffer, const Vector<GLint>& value) |
| { |
| if (isContextLost() || !validateClearBuffer("clearBufferiv", buffer, value.size())) |
| return; |
| |
| contextGL()->ClearBufferiv(buffer, drawbuffer, value.data()); |
| } |
| |
| void WebGL2RenderingContextBase::clearBufferuiv(GLenum buffer, GLint drawbuffer, DOMUint32Array* value) |
| { |
| if (isContextLost() || !validateClearBuffer("clearBufferuiv", buffer, value->length())) |
| return; |
| |
| contextGL()->ClearBufferuiv(buffer, drawbuffer, value->data()); |
| } |
| |
| void WebGL2RenderingContextBase::clearBufferuiv(GLenum buffer, GLint drawbuffer, const Vector<GLuint>& value) |
| { |
| if (isContextLost() || !validateClearBuffer("clearBufferuiv", buffer, value.size())) |
| return; |
| |
| contextGL()->ClearBufferuiv(buffer, drawbuffer, value.data()); |
| } |
| |
| void WebGL2RenderingContextBase::clearBufferfv(GLenum buffer, GLint drawbuffer, DOMFloat32Array* value) |
| { |
| if (isContextLost() || !validateClearBuffer("clearBufferfv", buffer, value->length())) |
| return; |
| |
| contextGL()->ClearBufferfv(buffer, drawbuffer, value->data()); |
| } |
| |
| void WebGL2RenderingContextBase::clearBufferfv(GLenum buffer, GLint drawbuffer, const Vector<GLfloat>& value) |
| { |
| if (isContextLost() || !validateClearBuffer("clearBufferfv", buffer, value.size())) |
| return; |
| |
| contextGL()->ClearBufferfv(buffer, drawbuffer, value.data()); |
| } |
| |
| void WebGL2RenderingContextBase::clearBufferfi(GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil) |
| { |
| if (isContextLost()) |
| return; |
| |
| contextGL()->ClearBufferfi(buffer, drawbuffer, depth, stencil); |
| } |
| |
| WebGLQuery* WebGL2RenderingContextBase::createQuery() |
| { |
| if (isContextLost()) |
| return nullptr; |
| WebGLQuery* o = WebGLQuery::create(this); |
| addSharedObject(o); |
| return o; |
| } |
| |
| void WebGL2RenderingContextBase::deleteQuery(WebGLQuery* query) |
| { |
| if (isContextLost() || !query) |
| return; |
| |
| if (m_currentBooleanOcclusionQuery == query) { |
| contextGL()->EndQueryEXT(m_currentBooleanOcclusionQuery->getTarget()); |
| m_currentBooleanOcclusionQuery = nullptr; |
| } |
| |
| if (m_currentTransformFeedbackPrimitivesWrittenQuery == query) { |
| contextGL()->EndQueryEXT(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN); |
| m_currentTransformFeedbackPrimitivesWrittenQuery = nullptr; |
| } |
| |
| deleteObject(query); |
| } |
| |
| GLboolean WebGL2RenderingContextBase::isQuery(WebGLQuery* query) |
| { |
| if (isContextLost() || !query) |
| return 0; |
| |
| return contextGL()->IsQueryEXT(query->object()); |
| } |
| |
| void WebGL2RenderingContextBase::beginQuery(ScriptState* scriptState, GLenum target, WebGLQuery* query) |
| { |
| bool deleted; |
| if (!query) { |
| synthesizeGLError(GL_INVALID_OPERATION, "beginQuery", "query object is null"); |
| return; |
| } |
| |
| if (!checkObjectToBeBound("beginQuery", query, deleted)) |
| return; |
| if (deleted) { |
| synthesizeGLError(GL_INVALID_OPERATION, "beginQuery", "attempted to begin a deleted query object"); |
| return; |
| } |
| |
| if (query->getTarget() && query->getTarget() != target) { |
| synthesizeGLError(GL_INVALID_OPERATION, "beginQuery", "query type does not match target"); |
| return; |
| } |
| |
| switch (target) { |
| case GL_ANY_SAMPLES_PASSED: |
| case GL_ANY_SAMPLES_PASSED_CONSERVATIVE: |
| { |
| if (m_currentBooleanOcclusionQuery) { |
| synthesizeGLError(GL_INVALID_OPERATION, "beginQuery", "a query is already active for target"); |
| return; |
| } |
| m_currentBooleanOcclusionQuery = query; |
| } |
| break; |
| case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN: |
| { |
| if (m_currentTransformFeedbackPrimitivesWrittenQuery) { |
| synthesizeGLError(GL_INVALID_OPERATION, "beginQuery", "a query is already active for target"); |
| return; |
| } |
| m_currentTransformFeedbackPrimitivesWrittenQuery = query; |
| } |
| break; |
| default: |
| synthesizeGLError(GL_INVALID_ENUM, "beginQuery", "invalid target"); |
| return; |
| } |
| |
| if (!query->getTarget()) |
| query->setTarget(target); |
| |
| contextGL()->BeginQueryEXT(target, query->object()); |
| preserveObjectWrapper(scriptState, this, V8HiddenValue::webglQueries(scriptState->isolate()), &m_queryWrappers, static_cast<uint32_t>(target), query); |
| } |
| |
| 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; |
| } |
| |
| contextGL()->EndQueryEXT(target); |
| } |
| |
| WebGLQuery* WebGL2RenderingContextBase::getQuery(GLenum target, GLenum pname) |
| { |
| if (isContextLost()) |
| return nullptr; |
| |
| if (pname != GL_CURRENT_QUERY) { |
| synthesizeGLError(GL_INVALID_ENUM, "getQuery", "invalid parameter name"); |
| return nullptr; |
| } |
| |
| switch (target) { |
| case GL_ANY_SAMPLES_PASSED: |
| case GL_ANY_SAMPLES_PASSED_CONSERVATIVE: |
| if (m_currentBooleanOcclusionQuery && m_currentBooleanOcclusionQuery->getTarget() == target) |
| return m_currentBooleanOcclusionQuery; |
| break; |
| case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN: |
| return m_currentTransformFeedbackPrimitivesWrittenQuery; |
| default: |
| synthesizeGLError(GL_INVALID_ENUM, "getQuery", "invalid target"); |
| return nullptr; |
| } |
| return nullptr; |
| } |
| |
| ScriptValue WebGL2RenderingContextBase::getQueryParameter(ScriptState* scriptState, WebGLQuery* query, GLenum pname) |
| { |
| bool deleted; |
| if (!query) { |
| synthesizeGLError(GL_INVALID_OPERATION, "getQueryParameter", "query object is null"); |
| return ScriptValue::createNull(scriptState); |
| } |
| if (!checkObjectToBeBound("getQueryParameter", query, deleted)) |
| return ScriptValue::createNull(scriptState); |
| if (deleted) { |
| synthesizeGLError(GL_INVALID_OPERATION, "getQueryParameter", "attempted to access to a deleted query object"); |
| return ScriptValue::createNull(scriptState); |
| } |
| |
| // Query is non-null at this point. |
| if (!query->getTarget()) { |
| synthesizeGLError(GL_INVALID_OPERATION, "getQueryParameter", "'query' is not a query object yet, since it has't been used by beginQuery"); |
| return ScriptValue::createNull(scriptState); |
| } |
| if (query == m_currentBooleanOcclusionQuery || query == m_currentTransformFeedbackPrimitivesWrittenQuery) { |
| synthesizeGLError(GL_INVALID_OPERATION, "getQueryParameter", "query is currently active"); |
| return ScriptValue::createNull(scriptState); |
| } |
| |
| switch (pname) { |
| case GL_QUERY_RESULT: |
| { |
| query->updateCachedResult(contextGL()); |
| return WebGLAny(scriptState, query->getQueryResult()); |
| } |
| case GL_QUERY_RESULT_AVAILABLE: |
| { |
| query->updateCachedResult(contextGL()); |
| 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; |
| contextGL()->BindSampler(i, 0); |
| } |
| } |
| |
| deleteObject(sampler); |
| } |
| |
| GLboolean WebGL2RenderingContextBase::isSampler(WebGLSampler* sampler) |
| { |
| if (isContextLost() || !sampler) |
| return 0; |
| |
| return contextGL()->IsSampler(sampler->object()); |
| } |
| |
| void WebGL2RenderingContextBase::bindSampler(ScriptState* scriptState, 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; |
| |
| contextGL()->BindSampler(unit, objectOrZero(sampler)); |
| |
| preserveObjectWrapper(scriptState, this, V8HiddenValue::webglSamplers(scriptState->isolate()), &m_samplerWrappers, static_cast<uint32_t>(unit), 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) { |
| contextGL()->SamplerParameterf(objectOrZero(sampler), pname, paramf); |
| } else { |
| contextGL()->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; |
| contextGL()->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; |
| contextGL()->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 contextGL()->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 contextGL()->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); |
| contextGL()->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; |
| contextGL()->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 contextGL()->IsTransformFeedback(feedback->object()); |
| } |
| |
| void WebGL2RenderingContextBase::bindTransformFeedback(ScriptState* scriptState, 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; |
| |
| contextGL()->BindTransformFeedback(target, objectOrZero(feedback)); |
| if (feedback) { |
| feedback->setTarget(target); |
| preserveObjectWrapper(scriptState, this, V8HiddenValue::webglMisc(scriptState->isolate()), &m_miscWrappers, static_cast<uint32_t>(PreservedTransformFeedback), feedback); |
| } |
| |
| } |
| |
| void WebGL2RenderingContextBase::beginTransformFeedback(GLenum primitiveMode) |
| { |
| if (isContextLost()) |
| return; |
| if (!validateTransformFeedbackPrimitiveMode("beginTransformFeedback", primitiveMode)) |
| return; |
| |
| contextGL()->BeginTransformFeedback(primitiveMode); |
| |
| if (m_currentProgram) |
| m_currentProgram->increaseActiveTransformFeedbackCount(); |
| |
| if (m_transformFeedbackBinding) |
| m_transformFeedbackBinding->setProgram(m_currentProgram); |
| } |
| |
| void WebGL2RenderingContextBase::endTransformFeedback() |
| { |
| if (isContextLost()) |
| return; |
| |
| contextGL()->EndTransformFeedback(); |
| |
| if (m_currentProgram) |
| m_currentProgram->decreaseActiveTransformFeedbackCount(); |
| } |
| |
| void WebGL2RenderingContextBase::transformFeedbackVaryings(WebGLProgram* program, const Vector<String>& varyings, GLenum bufferMode) |
| { |
| if (isContextLost() || !validateWebGLObject("transformFeedbackVaryings", program)) |
| return; |
| |
| switch (bufferMode) { |
| case GL_SEPARATE_ATTRIBS: |
| if (varyings.size() > static_cast<size_t>(m_maxTransformFeedbackSeparateAttribs)) { |
| synthesizeGLError(GL_INVALID_VALUE, "transformFeedbackVaryings", "too many varyings"); |
| return; |
| } |
| break; |
| case GL_INTERLEAVED_ATTRIBS: |
| break; |
| default: |
| synthesizeGLError(GL_INVALID_ENUM, "transformFeedbackVaryings", "invalid buffer mode"); |
| return; |
| } |
| |
| Vector<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()); |
| } |
| |
| contextGL()->TransformFeedbackVaryings(objectOrZero(program), varyings.size(), varyingStrings.data(), bufferMode); |
| } |
| |
| WebGLActiveInfo* WebGL2RenderingContextBase::getTransformFeedbackVarying(WebGLProgram* program, GLuint index) |
| { |
| if (isContextLost() || !validateWebGLObject("getTransformFeedbackVarying", program)) |
| return nullptr; |
| |
| if (!program->linkStatus(this)) { |
| synthesizeGLError(GL_INVALID_OPERATION, "getTransformFeedbackVarying", "program not linked"); |
| return nullptr; |
| } |
| GLint maxIndex = 0; |
| contextGL()->GetProgramiv(objectOrZero(program), GL_TRANSFORM_FEEDBACK_VARYINGS, &maxIndex); |
| if (index >= static_cast<GLuint>(maxIndex)) { |
| synthesizeGLError(GL_INVALID_VALUE, "getTransformFeedbackVarying", "invalid index"); |
| return nullptr; |
| } |
| |
| GLint maxNameLength = -1; |
| contextGL()->GetProgramiv(objectOrZero(program), GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH, &maxNameLength); |
| if (maxNameLength <= 0) { |
| return nullptr; |
| } |
| std::unique_ptr<GLchar[]> name = wrapArrayUnique(new GLchar[maxNameLength]); |
| GLsizei length = 0; |
| GLsizei size = 0; |
| GLenum type = 0; |
| contextGL()->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; |
| |
| contextGL()->PauseTransformFeedback(); |
| } |
| |
| void WebGL2RenderingContextBase::resumeTransformFeedback() |
| { |
| if (isContextLost()) |
| return; |
| |
| if (m_transformFeedbackBinding && m_transformFeedbackBinding->getProgram() != m_currentProgram) { |
| synthesizeGLError(GL_INVALID_OPERATION, "resumeTransformFeedback", "the program object is not active"); |
| return; |
| } |
| |
| contextGL()->ResumeTransformFeedback(); |
| } |
| |
| bool WebGL2RenderingContextBase::validateTransformFeedbackPrimitiveMode(const char* functionName, GLenum primitiveMode) |
| { |
| switch (primitiveMode) { |
| case GL_POINTS: |
| case GL_LINES: |
| case GL_TRIANGLES: |
| return true; |
| default: |
| synthesizeGLError(GL_INVALID_ENUM, functionName, "invalid transform feedback primitive mode"); |
| return false; |
| } |
| } |
| |
| void WebGL2RenderingContextBase::bindBufferBase(GLenum target, GLuint index, WebGLBuffer* buffer) |
| { |
| if (isContextLost()) |
| return; |
| bool deleted; |
| if (!checkObjectToBeBound("bindBufferBase", buffer, deleted)) |
| return; |
| if (deleted) |
| buffer = 0; |
| if (!validateAndUpdateBufferBindBaseTarget("bindBufferBase", target, index, buffer)) |
| return; |
| |
| contextGL()->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; |
| |
| contextGL()->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; |
| contextGL()->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()); |
| contextGL()->GetUniformIndices(objectOrZero(program), uniformStrings.size(), uniformStrings.data(), result.data()); |
| return result; |
| } |
| |
| ScriptValue WebGL2RenderingContextBase::getActiveUniforms(ScriptState* scriptState, WebGLProgram* program, const Vector<GLuint>& uniformIndices, GLenum pname) |
| { |
| if (isContextLost() || !validateWebGLObject("getActiveUniforms", program)) |
| return ScriptValue::createNull(scriptState); |
| |
| enum ReturnType { |
| EnumType, |
| UnsignedIntType, |
| IntType, |
| BoolType |
| }; |
| |
| int returnType; |
| switch (pname) { |
| case GL_UNIFORM_TYPE: |
| returnType = EnumType; |
| break; |
| case GL_UNIFORM_SIZE: |
| returnType = UnsignedIntType; |
| break; |
| case GL_UNIFORM_BLOCK_INDEX: |
| case GL_UNIFORM_OFFSET: |
| case GL_UNIFORM_ARRAY_STRIDE: |
| case GL_UNIFORM_MATRIX_STRIDE: |
| returnType = IntType; |
| break; |
| case GL_UNIFORM_IS_ROW_MAJOR: |
| returnType = BoolType; |
| break; |
| default: |
| synthesizeGLError(GL_INVALID_ENUM, "getActiveUniforms", "invalid parameter name"); |
| return ScriptValue::createNull(scriptState); |
| } |
| |
| GLint activeUniforms = -1; |
| contextGL()->GetProgramiv(objectOrZero(program), GL_ACTIVE_UNIFORMS, &activeUniforms); |
| |
| GLuint activeUniformsUnsigned = activeUniforms; |
| size_t size = uniformIndices.size(); |
| for (size_t i = 0; i < size; ++i) { |
| if (uniformIndices[i] >= activeUniformsUnsigned) { |
| synthesizeGLError(GL_INVALID_VALUE, "getActiveUniforms", "uniform index greater than ACTIVE_UNIFORMS"); |
| return ScriptValue::createNull(scriptState); |
| } |
| } |
| |
| Vector<GLint> result(size); |
| contextGL()->GetActiveUniformsiv(objectOrZero(program), uniformIndices.size(), uniformIndices.data(), pname, result.data()); |
| switch (returnType) { |
| case EnumType: |
| { |
| Vector<GLenum> enumResult(size); |
| for (size_t i = 0; i < size; ++i) |
| enumResult[i] = static_cast<GLenum>(result[i]); |
| return WebGLAny(scriptState, enumResult); |
| } |
| case UnsignedIntType: |
| { |
| Vector<GLuint> uintResult(size); |
| for (size_t i = 0; i < size; ++i) |
| uintResult[i] = static_cast<GLuint>(result[i]); |
| return WebGLAny(scriptState, uintResult); |
| } |
| case IntType: |
| { |
| return WebGLAny(scriptState, result); |
| } |
| case BoolType: |
| { |
| Vector<bool> boolResult(size); |
| for (size_t i = 0; i < size; ++i) |
| boolResult[i] = static_cast<bool>(result[i]); |
| return WebGLAny(scriptState, boolResult); |
| } |
| default: |
| ASSERT_NOT_REACHED(); |
| return ScriptValue::createNull(scriptState); |
| } |
| } |
| |
| GLuint WebGL2RenderingContextBase::getUniformBlockIndex(WebGLProgram* program, const String& uniformBlockName) |
| { |
| if (isContextLost() || !validateWebGLObject("getUniformBlockIndex", program)) |
| return 0; |
| if (!validateString("getUniformBlockIndex", uniformBlockName)) |
| return 0; |
| |
| return contextGL()->GetUniformBlockIndex(objectOrZero(program), uniformBlockName.utf8().data()); |
| } |
| |
| bool WebGL2RenderingContextBase::validateUniformBlockIndex(const char* functionName, WebGLProgram* program, GLuint blockIndex) |
| { |
| ASSERT(program); |
| if (!program->linkStatus(this)) { |
| synthesizeGLError(GL_INVALID_OPERATION, functionName, "program not linked"); |
| return false; |
| } |
| GLint activeUniformBlocks = 0; |
| contextGL()->GetProgramiv(objectOrZero(program), GL_ACTIVE_UNIFORM_BLOCKS, &activeUniformBlocks); |
| if (blockIndex >= static_cast<GLuint>(activeUniformBlocks)) { |
| synthesizeGLError(GL_INVALID_VALUE, functionName, "invalid uniform block index"); |
| return false; |
| } |
| return true; |
| } |
| |
| ScriptValue WebGL2RenderingContextBase::getActiveUniformBlockParameter(ScriptState* scriptState, WebGLProgram* program, GLuint uniformBlockIndex, GLenum pname) |
| { |
| if (isContextLost() || !validateWebGLObject("getActiveUniformBlockParameter", program)) |
| return ScriptValue::createNull(scriptState); |
| |
| if (!validateUniformBlockIndex("getActiveUniformBlockParameter", program, uniformBlockIndex)) |
| return ScriptValue::createNull(scriptState); |
| |
| switch (pname) { |
| case GL_UNIFORM_BLOCK_BINDING: |
| case GL_UNIFORM_BLOCK_DATA_SIZE: |
| case GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS: |
| { |
| GLint intValue = 0; |
| contextGL()->GetActiveUniformBlockiv(objectOrZero(program), uniformBlockIndex, pname, &intValue); |
| return WebGLAny(scriptState, static_cast<unsigned>(intValue)); |
| } |
| case GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES: |
| { |
| GLint uniformCount = 0; |
| contextGL()->GetActiveUniformBlockiv(objectOrZero(program), uniformBlockIndex, GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, &uniformCount); |
| |
| Vector<GLint> indices(uniformCount); |
| contextGL()->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; |
| contextGL()->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(); |
| |
| if (!validateUniformBlockIndex("getActiveUniformBlockName", program, uniformBlockIndex)) |
| return String(); |
| |
| GLint maxNameLength = -1; |
| contextGL()->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(); |
| } |
| std::unique_ptr<GLchar[]> name = wrapArrayUnique(new GLchar[maxNameLength]); |
| |
| GLsizei length = 0; |
| contextGL()->GetActiveUniformBlockName(objectOrZero(program), uniformBlockIndex, maxNameLength, &length, name.get()); |
| |
| return String(name.get(), length); |
| } |
| |
| void WebGL2RenderingContextBase::uniformBlockBinding(WebGLProgram* program, GLuint uniformBlockIndex, GLuint uniformBlockBinding) |
| { |
| if (isContextLost() || !validateWebGLObject("uniformBlockBinding", program)) |
| return; |
| |
| if (!validateUniformBlockIndex("uniformBlockBinding", program, uniformBlockIndex)) |
| return; |
| |
| contextGL()->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(contextGL()); |
| } |
| |
| GLboolean WebGL2RenderingContextBase::isVertexArray(WebGLVertexArrayObject* vertexArray) |
| { |
| if (isContextLost() || !vertexArray) |
| return 0; |
| |
| if (!vertexArray->hasEverBeenBound()) |
| return 0; |
| |
| return contextGL()->IsVertexArrayOES(vertexArray->object()); |
| } |
| |
| void WebGL2RenderingContextBase::bindVertexArray(ScriptState* scriptState, WebGLVertexArrayObject* vertexArray) |
| { |
| if (isContextLost()) |
| return; |
| |
| if (vertexArray && (vertexArray->isDeleted() || !vertexArray->validate(0, this))) { |
| synthesizeGLError(GL_INVALID_OPERATION, "bindVertexArray", "invalid vertexArray"); |
| return; |
| } |
| |
| if (vertexArray && !vertexArray->isDefaultObject() && vertexArray->object()) { |
| contextGL()->BindVertexArrayOES(objectOrZero(vertexArray)); |
| |
| vertexArray->setHasEverBeenBound(); |
| setBoundVertexArrayObject(scriptState, vertexArray); |
| } else { |
| contextGL()->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, V8HiddenValue::webglMisc(scriptState->isolate()), &m_miscWrappers, static_cast<uint32_t>(PreservedFramebuffer), 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(contextGL()->GetString(GL_SHADING_LANGUAGE_VERSION)) + ")"); |
| } |
| case GL_VERSION: |
| return WebGLAny(scriptState, "WebGL 2.0 (" + String(contextGL()->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 getIntParameter(scriptState, pname); |
| case GL_UNPACK_SKIP_PIXELS: |
| return getIntParameter(scriptState, pname); |
| case GL_UNPACK_SKIP_ROWS: |
| return getIntParameter(scriptState, pname); |
| |
| default: |
| return WebGLRenderingContextBase::getParameter(scriptState, pname); |
| } |
| } |
| |
| ScriptValue WebGL2RenderingContextBase::getInt64Parameter(ScriptState* scriptState, GLenum pname) |
| { |
| GLint64 value = 0; |
| if (!isContextLost()) |
| contextGL()->GetInteger64v(pname, &value); |
| return WebGLAny(scriptState, value); |
| } |
| |
| bool WebGL2RenderingContextBase::validateCapability(const char* functionName, GLenum cap) |
| { |
| switch (cap) { |
| case GL_RASTERIZER_DISCARD: |
| return true; |
| default: |
| return WebGLRenderingContextBase::validateCapability(functionName, cap); |
| } |
| } |
| |
| bool WebGL2RenderingContextBase::isBufferBoundToTransformFeedback(WebGLBuffer* buffer) |
| { |
| ASSERT(buffer); |
| |
| if (m_boundTransformFeedbackBuffer == buffer) |
| return true; |
| |
| for (size_t i = 0; i < m_boundIndexedTransformFeedbackBuffers.size(); ++i) { |
| if (m_boundIndexedTransformFeedbackBuffers[i] == buffer) |
| return true; |
| } |
| |
| return false; |
| } |
| |
| bool WebGL2RenderingContextBase::isBufferBoundToNonTransformFeedback(WebGLBuffer* buffer) |
| { |
| ASSERT(buffer); |
| |
| if (m_boundArrayBuffer == buffer |
| || m_boundVertexArrayObject->boundElementArrayBuffer() == buffer |
| || m_boundCopyReadBuffer == buffer |
| || m_boundCopyWriteBuffer == buffer |
| || m_boundPixelPackBuffer == buffer |
| || m_boundPixelUnpackBuffer == buffer |
| || m_boundUniformBuffer == buffer) { |
| return true; |
| } |
| |
| for (size_t i = 0; i <= m_maxBoundUniformBufferIndex; ++i) { |
| if (m_boundIndexedUniformBuffers[i] == buffer) |
| return true; |
| } |
| |
| return false; |
| } |
| |
| bool WebGL2RenderingContextBase::validateBufferTargetCompatibility(const char* functionName, GLenum target, WebGLBuffer* buffer) |
| { |
| ASSERT(buffer); |
| |
| switch (buffer->getInitialTarget()) { |
| case GL_ELEMENT_ARRAY_BUFFER: |
| switch (target) { |
| case GL_ARRAY_BUFFER: |
| case GL_PIXEL_PACK_BUFFER: |
| case GL_PIXEL_UNPACK_BUFFER: |
| case GL_TRANSFORM_FEEDBACK_BUFFER: |
| case GL_UNIFORM_BUFFER: |
| synthesizeGLError(GL_INVALID_OPERATION, functionName, |
| "element array buffers can not be bound to a different target"); |
| |
| return false; |
| default: |
| break; |
| } |
| break; |
| case GL_ARRAY_BUFFER: |
| case GL_COPY_READ_BUFFER: |
| case GL_COPY_WRITE_BUFFER: |
| case GL_PIXEL_PACK_BUFFER: |
| case GL_PIXEL_UNPACK_BUFFER: |
| case GL_UNIFORM_BUFFER: |
| case GL_TRANSFORM_FEEDBACK_BUFFER: |
| if (target == GL_ELEMENT_ARRAY_BUFFER) { |
| synthesizeGLError(GL_INVALID_OPERATION, functionName, |
| "buffers bound to non ELEMENT_ARRAY_BUFFER targets can not be bound to ELEMENT_ARRAY_BUFFER target"); |
| return false; |
| } |
| break; |
| default: |
| break; |
| } |
| |
| if (target == GL_TRANSFORM_FEEDBACK_BUFFER) { |
| if (isBufferBoundToNonTransformFeedback(buffer)) { |
| synthesizeGLError(GL_INVALID_OPERATION, functionName, |
| "a buffer bound to TRANSFORM_FEEDBACK_BUFFER can not be bound to any other targets"); |
| return false; |
| } |
| } else if (isBufferBoundToTransformFeedback(buffer)) { |
| synthesizeGLError(GL_INVALID_OPERATION, functionName, |
| "a buffer bound to TRANSFORM_FEEDBACK_BUFFER can not be bound to any other targets"); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool WebGL2RenderingContextBase::validateBufferTarget(const char* functionName, GLenum target) |
| { |
| switch (target) { |
| case GL_ARRAY_BUFFER: |
| case GL_COPY_READ_BUFFER: |
| case GL_COPY_WRITE_BUFFER: |
| case GL_ELEMENT_ARRAY_BUFFER: |
| case GL_PIXEL_PACK_BUFFER: |
| case GL_PIXEL_UNPACK_BUFFER: |
| case GL_TRANSFORM_FEEDBACK_BUFFER: |
| case GL_UNIFORM_BUFFER: |
| return true; |
| default: |
| synthesizeGLError(GL_INVALID_ENUM, functionName, "invalid target"); |
| return false; |
| } |
| } |
| |
| bool WebGL2RenderingContextBase::validateAndUpdateBufferBindTarget(const char* functionName, GLenum target, WebGLBuffer* buffer) |
| { |
| if (!validateBufferTarget(functionName, target)) |
| return false; |
| |
| if (buffer && !validateBufferTargetCompatibility(functionName, target, buffer)) |
| return false; |
| |
| switch (target) { |
| case GL_ARRAY_BUFFER: |
| m_boundArrayBuffer = buffer; |
| break; |
| case GL_COPY_READ_BUFFER: |
| m_boundCopyReadBuffer = buffer; |
| break; |
| case GL_COPY_WRITE_BUFFER: |
| m_boundCopyWriteBuffer = buffer; |
| break; |
| case GL_ELEMENT_ARRAY_BUFFER: |
| m_boundVertexArrayObject->setElementArrayBuffer(buffer); |
| break; |
| case GL_PIXEL_PACK_BUFFER: |
| m_boundPixelPackBuffer = buffer; |
| break; |
| case GL_PIXEL_UNPACK_BUFFER: |
| m_boundPixelUnpackBuffer = buffer; |
| break; |
| case GL_TRANSFORM_FEEDBACK_BUFFER: |
| m_boundTransformFeedbackBuffer = buffer; |
| break; |
| case GL_UNIFORM_BUFFER: |
| m_boundUniformBuffer = buffer; |
| break; |
| default: |
| ASSERT_NOT_REACHED(); |
| break; |
| } |
| |
| if (buffer && !buffer->getInitialTarget()) |
| buffer->setInitialTarget(target); |
| return true; |
| } |
| |
| bool WebGL2RenderingContextBase::validateBufferBaseTarget(const char* functionName, GLenum target) |
| { |
| switch (target) { |
| case GL_TRANSFORM_FEEDBACK_BUFFER: |
| case GL_UNIFORM_BUFFER: |
| return true; |
| default: |
| synthesizeGLError(GL_INVALID_ENUM, functionName, "invalid target"); |
| return false; |
| } |
| } |
| |
| bool WebGL2RenderingContextBase::validateAndUpdateBufferBindBaseTarget(const char* functionName, GLenum target, GLuint index, WebGLBuffer* buffer) |
| { |
| if (!validateBufferBaseTarget(functionName, target)) |
| return false; |
| |
| if (buffer && !validateBufferTargetCompatibility(functionName, target, buffer)) |
| return false; |
| |
| switch (target) { |
| case GL_TRANSFORM_FEEDBACK_BUFFER: |
| if (index >= m_boundIndexedTransformFeedbackBuffers.size()) { |
| synthesizeGLError(GL_INVALID_VALUE, functionName, "index out of range"); |
| return false; |
| } |
| m_boundIndexedTransformFeedbackBuffers[index] = buffer; |
| m_boundTransformFeedbackBuffer = buffer; |
| break; |
| case GL_UNIFORM_BUFFER: |
| if (index >= m_boundIndexedUniformBuffers.size()) { |
| synthesizeGLError(GL_INVALID_VALUE, functionName, "index out of range"); |
| return false; |
| } |
| m_boundIndexedUniformBuffers[index] = buffer; |
| m_boundUniformBuffer = buffer; |
| |
| // Keep track of what the maximum bound uniform buffer index is |
| if (buffer) { |
| if (index > m_maxBoundUniformBufferIndex) |
| m_maxBoundUniformBufferIndex = index; |
| } else if (m_maxBoundUniformBufferIndex > 0 && index == m_maxBoundUniformBufferIndex) { |
| size_t i = m_maxBoundUniformBufferIndex - 1; |
| for (; i > 0; --i) { |
| if (m_boundIndexedUniformBuffers[i].get()) |
| break; |
| } |
| m_maxBoundUniformBufferIndex = i; |
| } |
| break; |
| default: |
| ASSERT_NOT_REACHED(); |
| break; |
| } |
| |
| if (buffer && !buffer->getInitialTarget()) |
| buffer->setInitialTarget(target); |
| return true; |
| } |
| |
| bool WebGL2RenderingContextBase::validateFramebufferTarget(GLenum target) |
| { |
| switch (target) { |
| case GL_FRAMEBUFFER: |
| case GL_READ_FRAMEBUFFER: |
| case GL_DRAW_FRAMEBUFFER: |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| bool WebGL2RenderingContextBase::validateReadPixelsFormatAndType(GLenum format, GLenum type, DOMArrayBufferView* buffer) |
| { |
| switch (format) { |
| case GL_RED: |
| case GL_RED_INTEGER: |
| case GL_RG: |
| case GL_RG_INTEGER: |
| case GL_RGB: |
| case GL_RGB_INTEGER: |
| case GL_RGBA: |
| case GL_RGBA_INTEGER: |
| case GL_LUMINANCE_ALPHA: |
| case GL_LUMINANCE: |
| case GL_ALPHA: |
| break; |
| default: |
| synthesizeGLError(GL_INVALID_ENUM, "readPixels", "invalid format"); |
| return false; |
| } |
| |
| switch (type) { |
| case GL_UNSIGNED_BYTE: |
| if (buffer && buffer->type() != DOMArrayBufferView::TypeUint8) { |
| synthesizeGLError(GL_INVALID_OPERATION, "readPixels", "type UNSIGNED_BYTE but ArrayBufferView not Uint8Array"); |
| return false; |
| } |
| return true; |
| case GL_BYTE: |
| if (buffer && buffer->type() != DOMArrayBufferView::TypeInt8) { |
| synthesizeGLError(GL_INVALID_OPERATION, "readPixels", "type BYTE but ArrayBufferView not Int8Array"); |
| return false; |
| } |
| return true; |
| case GL_HALF_FLOAT: |
| if (buffer && buffer->type() != DOMArrayBufferView::TypeUint16) { |
| synthesizeGLError(GL_INVALID_OPERATION, "readPixels", "type HALF_FLOAT but ArrayBufferView not Uint16Array"); |
| return false; |
| } |
| return true; |
| case GL_FLOAT: |
| if (buffer && buffer->type() != DOMArrayBufferView::TypeFloat32) { |
| synthesizeGLError(GL_INVALID_OPERATION, "readPixels", "type FLOAT but ArrayBufferView not Float32Array"); |
| return false; |
| } |
| return true; |
| case GL_UNSIGNED_SHORT: |
| case GL_UNSIGNED_SHORT_5_6_5: |
| case GL_UNSIGNED_SHORT_4_4_4_4: |
| case GL_UNSIGNED_SHORT_5_5_5_1: |
| if (buffer && buffer->type() != DOMArrayBufferView::TypeUint16) { |
| synthesizeGLError(GL_INVALID_OPERATION, "readPixels", "type UNSIGNED_SHORT but ArrayBufferView not Uint16Array"); |
| return false; |
| } |
| return true; |
| case GL_SHORT: |
| if (buffer && buffer->type() != DOMArrayBufferView::TypeInt16) { |
| synthesizeGLError(GL_INVALID_OPERATION, "readPixels", "type SHORT but ArrayBufferView not Int16Array"); |
| return false; |
| } |
| return true; |
| case GL_UNSIGNED_INT: |
| case GL_UNSIGNED_INT_2_10_10_10_REV: |
| case GL_UNSIGNED_INT_10F_11F_11F_REV: |
| case GL_UNSIGNED_INT_5_9_9_9_REV: |
| if (buffer && buffer->type() != DOMArrayBufferView::TypeUint32) { |
| synthesizeGLError(GL_INVALID_OPERATION, "readPixels", "type UNSIGNED_INT but ArrayBufferView not Uint32Array"); |
| return false; |
| } |
| return true; |
| case GL_INT: |
| if (buffer && buffer->type() != DOMArrayBufferView::TypeInt32) { |
| synthesizeGLError(GL_INVALID_OPERATION, "readPixels", "type INT but ArrayBufferView not Int32Array"); |
| return false; |
| } |
| return true; |
| default: |
| synthesizeGLError(GL_INVALID_ENUM, "readPixels", "invalid type"); |
| return false; |
| } |
| } |
| |
| WebGLFramebuffer* WebGL2RenderingContextBase::getFramebufferBinding(GLenum target) |
| { |
| switch (target) { |
| case GL_READ_FRAMEBUFFER: |
| return m_readFramebufferBinding.get(); |
| case GL_DRAW_FRAMEBUFFER: |
| return m_framebufferBinding.get(); |
| default: |
| return WebGLRenderingContextBase::getFramebufferBinding(target); |
| } |
| } |
| |
| WebGLFramebuffer* WebGL2RenderingContextBase::getReadFramebufferBinding() |
| { |
| return m_readFramebufferBinding.get(); |
| } |
| |
| bool WebGL2RenderingContextBase::validateGetFramebufferAttachmentParameterFunc(const char* functionName, GLenum target, GLenum attachment) |
| { |
| if (!validateFramebufferTarget(target)) { |
| synthesizeGLError(GL_INVALID_ENUM, functionName, "invalid target"); |
| return false; |
| } |
| |
| WebGLFramebuffer* framebufferBinding = getFramebufferBinding(target); |
| ASSERT(framebufferBinding || drawingBuffer()); |
| if (!framebufferBinding) { |
| // for the default framebuffer |
| switch (attachment) { |
| case GL_BACK: |
| case GL_DEPTH: |
| case GL_STENCIL: |
| break; |
| default: |
| synthesizeGLError(GL_INVALID_ENUM, functionName, "invalid attachment"); |
| return false; |
| } |
| } else { |
| // for the FBO |
| switch (attachment) { |
| case GL_COLOR_ATTACHMENT0: |
| case GL_DEPTH_ATTACHMENT: |
| case GL_STENCIL_ATTACHMENT: |
| break; |
| case GL_DEPTH_STENCIL_ATTACHMENT: |
| if (framebufferBinding->getAttachmentObject(GL_DEPTH_ATTACHMENT) != framebufferBinding->getAttachmentObject(GL_STENCIL_ATTACHMENT)) { |
| synthesizeGLError(GL_INVALID_OPERATION, functionName, "different objects are bound to the depth and stencil attachment points"); |
| return false; |
| } |
| break; |
| default: |
| if (attachment > GL_COLOR_ATTACHMENT0 |
| && attachment < static_cast<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_NONE); |
| default: |
| synthesizeGLError(GL_INVALID_OPERATION, kFunctionName, "invalid parameter name"); |
| return ScriptValue::createNull(scriptState); |
| } |
| } |
| switch (pname) { |
| case GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE: |
| return WebGLAny(scriptState, GL_FRAMEBUFFER_DEFAULT); |
| case GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE: |
| case GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE: |
| case GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE: |
| { |
| GLint value = attachment == GL_BACK ? 8 : 0; |
| return WebGLAny(scriptState, value); |
| } |
| case GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE: |
| { |
| GLint value = (attachment == GL_BACK && hasAlpha) ? 8 : 0; |
| return WebGLAny(scriptState, value); |
| } |
| case GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE: |
| { |
| // For ES3 capable backend, DEPTH24_STENCIL8 has to be supported. |
| GLint value = attachment == GL_DEPTH ? 24 : 0; |
| return WebGLAny(scriptState, value); |
| } |
| case GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE: |
| { |
| GLint value = attachment == GL_STENCIL ? 8 : 0; |
| return WebGLAny(scriptState, value); |
| } |
| case GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE: |
| return WebGLAny(scriptState, GL_UNSIGNED_NORMALIZED); |
| case GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING: |
| return WebGLAny(scriptState, GL_LINEAR); |
| default: |
| synthesizeGLError(GL_INVALID_ENUM, kFunctionName, "invalid parameter name"); |
| return ScriptValue::createNull(scriptState); |
| } |
| } |
| |
| WebGLSharedObject* attachmentObject = nullptr; |
| if (attachment == GL_DEPTH_STENCIL_ATTACHMENT) { |
| WebGLSharedObject* depthAttachment = framebufferBinding->getAttachmentObject(GL_DEPTH_ATTACHMENT); |
| WebGLSharedObject* stencilAttachment = framebufferBinding->getAttachmentObject(GL_STENCIL_ATTACHMENT); |
| if (depthAttachment != stencilAttachment) { |
| synthesizeGLError(GL_INVALID_OPERATION, kFunctionName, |
| "different objects bound to DEPTH_ATTACHMENT and STENCIL_ATTACHMENT"); |
| return ScriptValue::createNull(scriptState); |
| } |
| attachmentObject = depthAttachment; |
| } else { |
| attachmentObject = framebufferBinding->getAttachmentObject(attachment); |
| } |
| |
| if (!attachmentObject) { |
| switch (pname) { |
| case GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE: |
| return WebGLAny(scriptState, GL_NONE); |
| case GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME: |
| return ScriptValue::createNull(scriptState); |
| default: |
| synthesizeGLError(GL_INVALID_OPERATION, kFunctionName, "invalid parameter name"); |
| return ScriptValue::createNull(scriptState); |
| } |
| } |
| ASSERT(attachmentObject->isTexture() || attachmentObject->isRenderbuffer()); |
| |
| switch (pname) { |
| case GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE: |
| if (attachmentObject->isTexture()) |
| return WebGLAny(scriptState, GL_TEXTURE); |
| return WebGLAny(scriptState, GL_RENDERBUFFER); |
| case GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME: |
| return WebGLAny(scriptState, attachmentObject); |
| case GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE: |
| case GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER: |
| case GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL: |
| if (!attachmentObject->isTexture()) |
| break; |
| case GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE: |
| case GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE: |
| case GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE: |
| case GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE: |
| case GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE: |
| case GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE: |
| { |
| GLint value = 0; |
| contextGL()->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; |
| contextGL()->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::validateTexture3DBinding(const char* functionName, GLenum target) |
| { |
| WebGLTexture* tex = nullptr; |
| switch (target) { |
| case GL_TEXTURE_2D_ARRAY: |
| tex = m_textureUnits[m_activeTextureUnit].m_texture2DArrayBinding.get(); |
| break; |
| case GL_TEXTURE_3D: |
| tex = m_textureUnits[m_activeTextureUnit].m_texture3DBinding.get(); |
| break; |
| default: |
| synthesizeGLError(GL_INVALID_ENUM, functionName, "invalid texture target"); |
| return nullptr; |
| } |
| if (!tex) |
| synthesizeGLError(GL_INVALID_OPERATION, functionName, "no texture bound to target"); |
| return tex; |
| } |
| |
| GLint WebGL2RenderingContextBase::getMaxTextureLevelForTarget(GLenum target) |
| { |
| switch (target) { |
| case GL_TEXTURE_3D: |
| return m_max3DTextureLevel; |
| case GL_TEXTURE_2D_ARRAY: |
| return m_maxTextureLevel; |
| } |
| return WebGLRenderingContextBase::getMaxTextureLevelForTarget(target); |
| } |
| |
| ScriptValue WebGL2RenderingContextBase::getTexParameter(ScriptState* scriptState, GLenum target, GLenum pname) |
| { |
| if (isContextLost() || !validateTextureBinding("getTexParameter", target)) |
| return ScriptValue::createNull(scriptState); |
| |
| switch (pname) { |
| case GL_TEXTURE_WRAP_R: |
| case GL_TEXTURE_COMPARE_FUNC: |
| case GL_TEXTURE_COMPARE_MODE: |
| case GL_TEXTURE_IMMUTABLE_LEVELS: |
| { |
| GLint value = 0; |
| contextGL()->GetTexParameteriv(target, pname, &value); |
| return WebGLAny(scriptState, static_cast<unsigned>(value)); |
| } |
| case GL_TEXTURE_IMMUTABLE_FORMAT: |
| { |
| GLint value = 0; |
| contextGL()->GetTexParameteriv(target, pname, &value); |
| return WebGLAny(scriptState, static_cast<bool>(value)); |
| } |
| case GL_TEXTURE_BASE_LEVEL: |
| case GL_TEXTURE_MAX_LEVEL: |
| { |
| GLint value = 0; |
| contextGL()->GetTexParameteriv(target, pname, &value); |
| return WebGLAny(scriptState, value); |
| } |
| case GL_TEXTURE_MAX_LOD: |
| case GL_TEXTURE_MIN_LOD: |
| { |
| GLfloat value = 0.f; |
| contextGL()->GetTexParameterfv(target, pname, &value); |
| return WebGLAny(scriptState, value); |
| } |
| default: |
| return WebGLRenderingContextBase::getTexParameter(scriptState, target, pname); |
| } |
| } |
| |
| WebGLBuffer* WebGL2RenderingContextBase::validateBufferDataTarget(const char* functionName, GLenum target) |
| { |
| WebGLBuffer* buffer = nullptr; |
| switch (target) { |
| case GL_ELEMENT_ARRAY_BUFFER: |
| buffer = m_boundVertexArrayObject->boundElementArrayBuffer(); |
| break; |
| case GL_ARRAY_BUFFER: |
| buffer = m_boundArrayBuffer.get(); |
| break; |
| case GL_COPY_READ_BUFFER: |
| buffer = m_boundCopyReadBuffer.get(); |
| break; |
| case GL_COPY_WRITE_BUFFER: |
| buffer = m_boundCopyWriteBuffer.get(); |
| break; |
| case GL_PIXEL_PACK_BUFFER: |
| buffer = m_boundPixelPackBuffer.get(); |
| break; |
| case GL_PIXEL_UNPACK_BUFFER: |
| buffer = m_boundPixelUnpackBuffer.get(); |
| break; |
| case GL_TRANSFORM_FEEDBACK_BUFFER: |
| buffer = m_boundTransformFeedbackBuffer.get(); |
| break; |
| case GL_UNIFORM_BUFFER: |
| buffer = m_boundUniformBuffer.get(); |
| break; |
| default: |
| synthesizeGLError(GL_INVALID_ENUM, functionName, "invalid target"); |
| return nullptr; |
| } |
| if (!buffer) { |
| synthesizeGLError(GL_INVALID_OPERATION, functionName, "no buffer"); |
| return nullptr; |
| } |
| return buffer; |
| } |
| |
| bool WebGL2RenderingContextBase::validateBufferDataUsage(const char* functionName, GLenum usage) |
| { |
| switch (usage) { |
| case GL_STREAM_READ: |
| case GL_STREAM_COPY: |
| case GL_STATIC_READ: |
| case GL_STATIC_COPY: |
| case GL_DYNAMIC_READ: |
| case GL_DYNAMIC_COPY: |
| return true; |
| default: |
| return WebGLRenderingContextBase::validateBufferDataUsage(functionName, usage); |
| } |
| } |
| |
| void WebGL2RenderingContextBase::removeBoundBuffer(WebGLBuffer* buffer) |
| { |
| if (m_boundCopyReadBuffer == buffer) |
| m_boundCopyReadBuffer = nullptr; |
| if (m_boundCopyWriteBuffer == buffer) |
| m_boundCopyWriteBuffer = nullptr; |
| if (m_boundPixelPackBuffer == buffer) |
| m_boundPixelPackBuffer = nullptr; |
| if (m_boundPixelUnpackBuffer == buffer) |
| m_boundPixelUnpackBuffer = nullptr; |
| if (m_boundTransformFeedbackBuffer == buffer) |
| m_boundTransformFeedbackBuffer = nullptr; |
| if (m_boundUniformBuffer == buffer) |
| m_boundUniformBuffer = nullptr; |
| |
| WebGLRenderingContextBase::removeBoundBuffer(buffer); |
| } |
| |
| void WebGL2RenderingContextBase::restoreCurrentFramebuffer() |
| { |
| bindFramebuffer(nullptr, GL_DRAW_FRAMEBUFFER, m_framebufferBinding.get()); |
| bindFramebuffer(nullptr, GL_READ_FRAMEBUFFER, m_readFramebufferBinding.get()); |
| } |
| |
| WebGLImageConversion::PixelStoreParams WebGL2RenderingContextBase::getPackPixelStoreParams() |
| { |
| WebGLImageConversion::PixelStoreParams params; |
| params.alignment = m_packAlignment; |
| params.rowLength = m_packRowLength; |
| params.skipPixels = m_packSkipPixels; |
| params.skipRows = m_packSkipRows; |
| return params; |
| } |
| |
| WebGLImageConversion::PixelStoreParams WebGL2RenderingContextBase::getUnpackPixelStoreParams(TexImageDimension dimension) |
| { |
| WebGLImageConversion::PixelStoreParams params; |
| params.alignment = m_unpackAlignment; |
| params.rowLength = m_unpackRowLength; |
| params.skipPixels = m_unpackSkipPixels; |
| params.skipRows = m_unpackSkipRows; |
| if (dimension == Tex3D) { |
| params.imageHeight = m_unpackImageHeight; |
| params.skipImages = m_unpackSkipImages; |
| } |
| return params; |
| } |
| |
| } // namespace blink |