| // 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/dom/DOMException.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/GLES2/gl2extchromium.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 "public/platform/WebGraphicsContext3DProvider.h" |
| #include "wtf/CheckedNumeric.h" |
| #include "wtf/PtrUtil.h" |
| #include "wtf/text/WTFString.h" |
| #include <memory> |
| |
| using WTF::String; |
| |
| namespace blink { |
| |
| namespace { |
| |
| const GLuint64 kMaxClientWaitTimeout = 0u; |
| |
| GLsync syncObjectOrZero(const WebGLSync* object) { |
| return object ? object->object() : nullptr; |
| } |
| |
| // TODO(kainino): Change outByteLength to GLuint and change the associated |
| // range checking (and all uses) - overflow becomes possible in cases below |
| bool validateSubSourceAndGetData(DOMArrayBufferView* view, |
| GLuint subOffset, |
| GLuint subLength, |
| void** outBaseAddress, |
| long long* outByteLength) { |
| // This is guaranteed to be non-null by DOM. |
| DCHECK(view); |
| |
| size_t typeSize = view->typeSize(); |
| DCHECK_GE(8u, typeSize); |
| long long byteLength = 0; |
| if (subLength) { |
| // type size is at most 8, so no overflow. |
| byteLength = subLength * typeSize; |
| } |
| long long byteOffset = 0; |
| if (subOffset) { |
| // type size is at most 8, so no overflow. |
| byteOffset = subOffset * typeSize; |
| } |
| CheckedNumeric<long long> total = byteOffset; |
| total += byteLength; |
| if (!total.IsValid() || total.ValueOrDie() > view->byteLength()) { |
| return false; |
| } |
| if (!byteLength) { |
| byteLength = view->byteLength() - byteOffset; |
| } |
| uint8_t* data = static_cast<uint8_t*>(view->baseAddress()); |
| data += byteOffset; |
| *outBaseAddress = data; |
| *outByteLength = byteLength; |
| return true; |
| } |
| |
| } // 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, |
| }; |
| |
| class WebGLGetBufferSubDataAsyncCallback |
| : public GarbageCollected<WebGLGetBufferSubDataAsyncCallback> { |
| public: |
| WebGLGetBufferSubDataAsyncCallback( |
| WebGL2RenderingContextBase* context, |
| ScriptPromiseResolver* promiseResolver, |
| void* shmReadbackResultData, |
| GLuint commandsIssuedQueryID, |
| DOMArrayBufferView* destinationArrayBufferView, |
| void* destinationDataPtr, |
| long long destinationByteLength) |
| : m_context(context), |
| m_promiseResolver(promiseResolver), |
| m_shmReadbackResultData(shmReadbackResultData), |
| m_commandsIssuedQueryID(commandsIssuedQueryID), |
| m_destinationArrayBufferView(destinationArrayBufferView), |
| m_destinationDataPtr(destinationDataPtr), |
| m_destinationByteLength(destinationByteLength) { |
| DCHECK(shmReadbackResultData); |
| DCHECK(destinationDataPtr); |
| } |
| |
| void destroy() { |
| DCHECK(m_shmReadbackResultData); |
| m_context->contextGL()->FreeSharedMemory(m_shmReadbackResultData); |
| m_shmReadbackResultData = nullptr; |
| DOMException* exception = |
| DOMException::create(InvalidStateError, "Context lost or destroyed"); |
| m_promiseResolver->reject(exception); |
| } |
| |
| void resolve() { |
| if (!m_context || !m_shmReadbackResultData) { |
| DOMException* exception = |
| DOMException::create(InvalidStateError, "Context lost or destroyed"); |
| m_promiseResolver->reject(exception); |
| return; |
| } |
| if (m_destinationArrayBufferView->buffer()->isNeutered()) { |
| DOMException* exception = DOMException::create( |
| InvalidStateError, "ArrayBufferView became invalid asynchronously"); |
| m_promiseResolver->reject(exception); |
| return; |
| } |
| memcpy(m_destinationDataPtr, m_shmReadbackResultData, |
| m_destinationByteLength); |
| // TODO(kainino): What would happen if the DOM was suspended when the |
| // promise became resolved? Could another JS task happen between the memcpy |
| // and the promise resolution task, which would see the wrong data? |
| m_promiseResolver->resolve(m_destinationArrayBufferView); |
| |
| m_context->contextGL()->DeleteQueriesEXT(1, &m_commandsIssuedQueryID); |
| this->destroy(); |
| m_context->unregisterGetBufferSubDataAsyncCallback(this); |
| } |
| |
| DECLARE_TRACE(); |
| |
| private: |
| WeakMember<WebGL2RenderingContextBase> m_context; |
| Member<ScriptPromiseResolver> m_promiseResolver; |
| |
| // Pointer to shared memory where the gpu readback result is stored. |
| void* m_shmReadbackResultData; |
| // ID of the GL query used to call this callback. |
| GLuint m_commandsIssuedQueryID; |
| |
| // ArrayBufferView returned from the promise. |
| Member<DOMArrayBufferView> m_destinationArrayBufferView; |
| // Pointer into the offset into destinationArrayBufferView. |
| void* m_destinationDataPtr; |
| // Size in bytes of the copy operation being performed. |
| long long m_destinationByteLength; |
| }; |
| |
| DEFINE_TRACE(WebGLGetBufferSubDataAsyncCallback) { |
| visitor->trace(m_context); |
| visitor->trace(m_promiseResolver); |
| visitor->trace(m_destinationArrayBufferView); |
| } |
| |
| WebGL2RenderingContextBase::WebGL2RenderingContextBase( |
| HTMLCanvasElement* passedCanvas, |
| std::unique_ptr<WebGraphicsContext3DProvider> contextProvider, |
| const CanvasContextCreationAttributes& requestedAttributes) |
| : WebGLRenderingContextBase(passedCanvas, |
| std::move(contextProvider), |
| requestedAttributes, |
| 2), |
| m_readFramebufferBinding(this, nullptr), |
| m_transformFeedbackBinding(this, nullptr), |
| m_boundCopyReadBuffer(this, nullptr), |
| m_boundCopyWriteBuffer(this, nullptr), |
| m_boundPixelPackBuffer(this, nullptr), |
| m_boundPixelUnpackBuffer(this, nullptr), |
| m_boundTransformFeedbackBuffer(this, nullptr), |
| m_boundUniformBuffer(this, nullptr), |
| m_currentBooleanOcclusionQuery(this, nullptr), |
| m_currentTransformFeedbackPrimitivesWrittenQuery(this, nullptr), |
| m_currentElapsedQuery(this, nullptr) { |
| m_supportedInternalFormatsStorage.insert( |
| kSupportedInternalFormatsStorage, |
| kSupportedInternalFormatsStorage + |
| WTF_ARRAY_LENGTH(kSupportedInternalFormatsStorage)); |
| } |
| |
| WebGL2RenderingContextBase::~WebGL2RenderingContextBase() { |
| m_readFramebufferBinding = nullptr; |
| |
| m_boundCopyReadBuffer = nullptr; |
| m_boundCopyWriteBuffer = nullptr; |
| m_boundPixelPackBuffer = nullptr; |
| m_boundPixelUnpackBuffer = nullptr; |
| m_boundTransformFeedbackBuffer = nullptr; |
| m_boundUniformBuffer = nullptr; |
| |
| m_currentBooleanOcclusionQuery = nullptr; |
| m_currentTransformFeedbackPrimitivesWrittenQuery = nullptr; |
| m_currentElapsedQuery = nullptr; |
| } |
| |
| void WebGL2RenderingContextBase::destroyContext() { |
| for (auto& callback : m_getBufferSubDataAsyncCallbacks) { |
| callback->destroy(); |
| } |
| m_getBufferSubDataAsyncCallbacks.clear(); |
| |
| WebGLRenderingContextBase::destroyContext(); |
| } |
| |
| void WebGL2RenderingContextBase::initializeNewContext() { |
| ASSERT(!isContextLost()); |
| ASSERT(drawingBuffer()); |
| |
| m_readFramebufferBinding = nullptr; |
| |
| m_boundCopyReadBuffer = nullptr; |
| m_boundCopyWriteBuffer = nullptr; |
| m_boundPixelPackBuffer = nullptr; |
| m_boundPixelUnpackBuffer = nullptr; |
| m_boundTransformFeedbackBuffer = nullptr; |
| m_boundUniformBuffer = nullptr; |
| |
| m_currentBooleanOcclusionQuery = nullptr; |
| m_currentTransformFeedbackPrimitivesWrittenQuery = nullptr; |
| m_currentElapsedQuery = 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::bufferData(GLenum target, |
| DOMArrayBufferView* srcData, |
| GLenum usage, |
| GLuint srcOffset, |
| GLuint length) { |
| if (isContextLost()) |
| return; |
| void* subBaseAddress = nullptr; |
| long long subByteLength = 0; |
| if (!validateSubSourceAndGetData(srcData, srcOffset, length, &subBaseAddress, |
| &subByteLength)) { |
| synthesizeGLError(GL_INVALID_VALUE, "bufferData", |
| "srcOffset + length too large"); |
| return; |
| } |
| bufferDataImpl(target, subByteLength, subBaseAddress, usage); |
| } |
| |
| void WebGL2RenderingContextBase::bufferData(GLenum target, |
| long long size, |
| GLenum usage) { |
| WebGLRenderingContextBase::bufferData(target, size, usage); |
| } |
| |
| void WebGL2RenderingContextBase::bufferData(GLenum target, |
| DOMArrayBuffer* data, |
| GLenum usage) { |
| WebGLRenderingContextBase::bufferData(target, data, usage); |
| } |
| |
| void WebGL2RenderingContextBase::bufferData(GLenum target, |
| DOMArrayBufferView* data, |
| GLenum usage) { |
| WebGLRenderingContextBase::bufferData(target, data, usage); |
| } |
| |
| void WebGL2RenderingContextBase::bufferSubData(GLenum target, |
| GLintptr dstByteOffset, |
| DOMArrayBufferView* srcData, |
| GLuint srcOffset, |
| GLuint length) { |
| if (isContextLost()) |
| return; |
| void* subBaseAddress = nullptr; |
| long long subByteLength = 0; |
| if (!validateSubSourceAndGetData(srcData, srcOffset, length, &subBaseAddress, |
| &subByteLength)) { |
| synthesizeGLError(GL_INVALID_VALUE, "bufferSubData", |
| "srcOffset + length too large"); |
| return; |
| } |
| bufferSubDataImpl(target, dstByteOffset, subByteLength, subBaseAddress); |
| } |
| |
| void WebGL2RenderingContextBase::bufferSubData(GLenum target, |
| long long offset, |
| DOMArrayBuffer* data) { |
| WebGLRenderingContextBase::bufferSubData(target, offset, data); |
| } |
| |
| void WebGL2RenderingContextBase::bufferSubData( |
| GLenum target, |
| long long offset, |
| const FlexibleArrayBufferView& data) { |
| WebGLRenderingContextBase::bufferSubData(target, offset, data); |
| } |
| |
| 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 srcByteOffset, |
| DOMArrayBufferView* dstData, |
| GLuint dstOffset, |
| GLuint length) { |
| WebGLBuffer* sourceBuffer = nullptr; |
| void* destinationDataPtr = nullptr; |
| long long destinationByteLength = 0; |
| const char* message = validateGetBufferSubData( |
| __FUNCTION__, target, srcByteOffset, dstData, dstOffset, length, |
| &sourceBuffer, &destinationDataPtr, &destinationByteLength); |
| if (message) { |
| // If there was a GL error, it was already synthesized in |
| // validateGetBufferSubData, so it's not done here. |
| return; |
| } |
| |
| // If the length of the copy is zero, this is a no-op. |
| if (!destinationByteLength) { |
| return; |
| } |
| |
| void* mappedData = |
| contextGL()->MapBufferRange(target, static_cast<GLintptr>(srcByteOffset), |
| destinationByteLength, GL_MAP_READ_BIT); |
| |
| if (!mappedData) |
| return; |
| |
| memcpy(destinationDataPtr, mappedData, destinationByteLength); |
| |
| contextGL()->UnmapBuffer(target); |
| } |
| |
| ScriptPromise WebGL2RenderingContextBase::getBufferSubDataAsync( |
| ScriptState* scriptState, |
| GLenum target, |
| GLintptr srcByteOffset, |
| DOMArrayBufferView* dstData, |
| GLuint dstOffset, |
| GLuint length) { |
| ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState); |
| ScriptPromise promise = resolver->promise(); |
| |
| WebGLBuffer* sourceBuffer = nullptr; |
| void* destinationDataPtr = nullptr; |
| long long destinationByteLength = 0; |
| const char* message = validateGetBufferSubData( |
| __FUNCTION__, target, srcByteOffset, dstData, dstOffset, length, |
| &sourceBuffer, &destinationDataPtr, &destinationByteLength); |
| if (message) { |
| // If there was a GL error, it was already synthesized in |
| // validateGetBufferSubData, so it's not done here. |
| DOMException* exception = DOMException::create(InvalidStateError, message); |
| resolver->reject(exception); |
| return promise; |
| } |
| |
| message = validateGetBufferSubDataBounds( |
| __FUNCTION__, sourceBuffer, srcByteOffset, destinationByteLength); |
| if (message) { |
| // If there was a GL error, it was already synthesized in |
| // validateGetBufferSubDataBounds, so it's not done here. |
| DOMException* exception = DOMException::create(InvalidStateError, message); |
| resolver->reject(exception); |
| return promise; |
| } |
| |
| // If the length of the copy is zero, this is a no-op. |
| if (!destinationByteLength) { |
| resolver->resolve(dstData); |
| return promise; |
| } |
| |
| GLuint queryID; |
| contextGL()->GenQueriesEXT(1, &queryID); |
| contextGL()->BeginQueryEXT(GL_COMMANDS_ISSUED_CHROMIUM, queryID); |
| void* mappedData = contextGL()->GetBufferSubDataAsyncCHROMIUM( |
| target, srcByteOffset, destinationByteLength); |
| contextGL()->EndQueryEXT(GL_COMMANDS_ISSUED_CHROMIUM); |
| if (!mappedData) { |
| DOMException* exception = |
| DOMException::create(InvalidStateError, "Out of memory"); |
| resolver->reject(exception); |
| return promise; |
| } |
| |
| auto callbackObject = new WebGLGetBufferSubDataAsyncCallback( |
| this, resolver, mappedData, queryID, dstData, destinationDataPtr, |
| destinationByteLength); |
| registerGetBufferSubDataAsyncCallback(callbackObject); |
| auto callback = WTF::bind(&WebGLGetBufferSubDataAsyncCallback::resolve, |
| wrapPersistent(callbackObject)); |
| drawingBuffer()->contextProvider()->signalQuery( |
| queryID, convertToBaseCallback(std::move(callback))); |
| |
| return promise; |
| } |
| |
| void WebGL2RenderingContextBase::registerGetBufferSubDataAsyncCallback( |
| WebGLGetBufferSubDataAsyncCallback* callback) { |
| m_getBufferSubDataAsyncCallbacks.add(callback); |
| } |
| |
| void WebGL2RenderingContextBase::unregisterGetBufferSubDataAsyncCallback( |
| WebGLGetBufferSubDataAsyncCallback* callback) { |
| m_getBufferSubDataAsyncCallbacks.remove(callback); |
| } |
| |
| 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(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; |
| } |
| framebufferBinding->setAttachmentForBoundFramebuffer( |
| target, attachment, textarget, texture, level, layer); |
| 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); |
| } |
| |
| 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); |
| } |
| 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; |
| 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()); |
| 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. |
| // 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; |
| } |
| |
| readPixelsHelper(x, y, width, height, format, type, pixels, 0); |
| } |
| |
| void WebGL2RenderingContextBase::readPixels(GLint x, |
| GLint y, |
| GLsizei width, |
| GLsizei height, |
| GLenum format, |
| GLenum type, |
| DOMArrayBufferView* pixels, |
| GLuint offset) { |
| if (isContextLost()) |
| return; |
| if (m_boundPixelPackBuffer.get()) { |
| synthesizeGLError(GL_INVALID_OPERATION, "readPixels", |
| "PIXEL_PACK buffer should not be bound"); |
| return; |
| } |
| |
| readPixelsHelper(x, y, width, height, format, type, pixels, offset); |
| } |
| |
| 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::renderbufferStorageHelper( |
| GLenum target, |
| GLsizei samples, |
| GLenum internalformat, |
| GLsizei width, |
| GLsizei height, |
| const char* functionName) { |
| 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); |
| } |
| } |
| |
| 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: |
| renderbufferStorageHelper(target, samples, internalformat, width, height, |
| functionName); |
| break; |
| case GL_DEPTH_STENCIL: |
| // To be WebGL 1 backward compatible. |
| if (samples > 0) { |
| synthesizeGLError(GL_INVALID_ENUM, functionName, |
| "invalid internalformat"); |
| return; |
| } |
| renderbufferStorageHelper(target, 0, GL_DEPTH24_STENCIL8, width, height, |
| functionName); |
| 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; |
| } |
| renderbufferStorageHelper(target, samples, internalformat, width, height, |
| functionName); |
| 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, |
| GLsizei width, |
| GLsizei height, |
| GLint border, |
| GLenum format, |
| GLenum type, |
| DOMArrayBufferView* data, |
| GLuint srcOffset) { |
| texImageHelperDOMArrayBufferView(TexImage2D, target, level, internalformat, |
| width, height, 1, border, format, type, 0, 0, |
| 0, data, NullNotReachable, srcOffset); |
| } |
| |
| void WebGL2RenderingContextBase::texImage2D(GLenum target, |
| GLint level, |
| GLint internalformat, |
| GLsizei width, |
| GLsizei height, |
| GLint border, |
| GLenum format, |
| GLenum type, |
| ImageData* imageData) { |
| // TODO(zmo): To be implemented. |
| } |
| |
| void WebGL2RenderingContextBase::texImage2D(GLenum target, |
| GLint level, |
| GLint internalformat, |
| GLsizei width, |
| GLsizei height, |
| GLint border, |
| GLenum format, |
| GLenum type, |
| HTMLImageElement* image, |
| ExceptionState& exceptionState) { |
| IntRect sourceImageRect; |
| if (image) { |
| sourceImageRect.setLocation(IntPoint(m_unpackSkipPixels, m_unpackSkipRows)); |
| sourceImageRect.setSize(IntSize(width, height)); |
| } |
| |
| texImageHelperHTMLImageElement(TexImage2D, target, level, internalformat, |
| format, type, 0, 0, 0, image, sourceImageRect, |
| exceptionState); |
| } |
| |
| void WebGL2RenderingContextBase::texImage2D(GLenum target, |
| GLint level, |
| GLint internalformat, |
| GLsizei width, |
| GLsizei height, |
| GLint border, |
| GLenum format, |
| GLenum type, |
| HTMLCanvasElement* canvas, |
| ExceptionState& exceptionState) { |
| // TODO(zmo): To be implemented. |
| } |
| |
| void WebGL2RenderingContextBase::texImage2D(GLenum target, |
| GLint level, |
| GLint internalformat, |
| GLsizei width, |
| GLsizei height, |
| GLint border, |
| GLenum format, |
| GLenum type, |
| HTMLVideoElement* video, |
| ExceptionState& exceptionState) { |
| // TODO(zmo): To be implemented. |
| } |
| |
| void WebGL2RenderingContextBase::texImage2D(GLenum target, |
| GLint level, |
| GLint internalformat, |
| GLsizei width, |
| GLsizei height, |
| GLint border, |
| GLenum format, |
| GLenum type, |
| ImageBitmap* imageBitMap, |
| ExceptionState& exceptionState) { |
| // TODO(zmo): To be implemented. |
| } |
| |
| 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, |
| GLsizei width, |
| GLsizei height, |
| GLenum format, |
| GLenum type, |
| DOMArrayBufferView* pixels, |
| GLuint srcOffset) { |
| texImageHelperDOMArrayBufferView(TexSubImage2D, target, level, 0, width, |
| height, 1, 0, format, type, xoffset, yoffset, |
| 0, pixels, NullNotReachable, srcOffset); |
| } |
| |
| void WebGL2RenderingContextBase::texSubImage2D(GLenum target, |
| GLint level, |
| GLint xoffset, |
| GLint yoffset, |
| GLsizei width, |
| GLsizei height, |
| GLenum format, |
| GLenum type, |
| ImageData* pixels) { |
| // TODO(zmo): To be implemented. |
| } |
| |
| void WebGL2RenderingContextBase::texSubImage2D(GLenum target, |
| GLint level, |
| GLint xoffset, |
| GLint yoffset, |
| GLsizei width, |
| GLsizei height, |
| GLenum format, |
| GLenum type, |
| HTMLImageElement* image, |
| ExceptionState& exceptionState) { |
| IntRect sourceImageRect; |
| if (image) { |
| sourceImageRect.setLocation(IntPoint(m_unpackSkipPixels, m_unpackSkipRows)); |
| sourceImageRect.setSize(IntSize(width, height)); |
| } |
| |
| texImageHelperHTMLImageElement(TexSubImage2D, target, level, 0, format, type, |
| xoffset, yoffset, 0, image, sourceImageRect, |
| exceptionState); |
| } |
| |
| void WebGL2RenderingContextBase::texSubImage2D(GLenum target, |
| GLint level, |
| GLint xoffset, |
| GLint yoffset, |
| GLsizei width, |
| GLsizei height, |
| GLenum format, |
| GLenum type, |
| HTMLCanvasElement* canvas, |
| ExceptionState& exceptionState) { |
| // TODO(zmo): To be implemented. |
| } |
| |
| void WebGL2RenderingContextBase::texSubImage2D(GLenum target, |
| GLint level, |
| GLint xoffset, |
| GLint yoffset, |
| GLsizei width, |
| GLsizei height, |
| GLenum format, |
| GLenum type, |
| HTMLVideoElement* video, |
| ExceptionState& exceptionState) { |
| // TODO(zmo): To be implemented. |
| } |
| |
| void WebGL2RenderingContextBase::texSubImage2D(GLenum target, |
| GLint level, |
| GLint xoffset, |
| GLint yoffset, |
| GLsizei width, |
| GLsizei height, |
| GLenum format, |
| GLenum type, |
| ImageBitmap* bitmap, |
| ExceptionState& exceptionState) { |
| // TODO(zmo): To be implemented. |
| } |
| |
| 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, depth, border, format, type, |
| 0, 0, 0, pixels, NullAllowed, 0); |
| } |
| |
| void WebGL2RenderingContextBase::texImage3D(GLenum target, |
| GLint level, |
| GLint internalformat, |
| GLsizei width, |
| GLsizei height, |
| GLsizei depth, |
| GLint border, |
| GLenum format, |
| GLenum type, |
| DOMArrayBufferView* pixels, |
| GLuint srcOffset) { |
| texImageHelperDOMArrayBufferView( |
| TexImage3D, target, level, internalformat, width, height, depth, border, |
| format, type, 0, 0, 0, pixels, NullNotReachable, srcOffset); |
| } |
| |
| 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::texImage3D(GLenum target, |
| GLint level, |
| GLint internalformat, |
| GLsizei width, |
| GLsizei height, |
| GLsizei depth, |
| GLint border, |
| GLenum format, |
| GLenum type, |
| ImageData* imageData) { |
| // TODO(zmo): To be implemented. |
| } |
| |
| void WebGL2RenderingContextBase::texImage3D(GLenum target, |
| GLint level, |
| GLint internalformat, |
| GLsizei width, |
| GLsizei height, |
| GLsizei depth, |
| GLint border, |
| GLenum format, |
| GLenum type, |
| HTMLImageElement* image, |
| ExceptionState& exceptionState) { |
| // TODO(zmo): To be implemented. |
| } |
| |
| void WebGL2RenderingContextBase::texImage3D(GLenum target, |
| GLint level, |
| GLint internalformat, |
| GLsizei width, |
| GLsizei height, |
| GLsizei depth, |
| GLint border, |
| GLenum format, |
| GLenum type, |
| HTMLCanvasElement* canvas, |
| ExceptionState& exceptionState) { |
| // TODO(zmo): To be implemented. |
| } |
| |
| void WebGL2RenderingContextBase::texImage3D(GLenum target, |
| GLint level, |
| GLint internalformat, |
| GLsizei width, |
| GLsizei height, |
| GLsizei depth, |
| GLint border, |
| GLenum format, |
| GLenum type, |
| HTMLVideoElement* video, |
| ExceptionState& exceptionState) { |
| // TODO(zmo): To be implemented. |
| } |
| |
| void WebGL2RenderingContextBase::texImage3D(GLenum target, |
| GLint level, |
| GLint internalformat, |
| GLsizei width, |
| GLsizei height, |
| GLsizei depth, |
| GLint border, |
| GLenum format, |
| GLenum type, |
| ImageBitmap* imageBitMap, |
| ExceptionState& exceptionState) { |
| // TODO(zmo): To be implemented. |
| } |
| |
| 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, |
| GLuint srcOffset) { |
| texImageHelperDOMArrayBufferView( |
| TexSubImage3D, target, level, 0, width, height, depth, 0, format, type, |
| xoffset, yoffset, zoffset, pixels, NullNotReachable, srcOffset); |
| } |
| |
| 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, |
| GLsizei width, |
| GLsizei height, |
| GLsizei depth, |
| GLenum format, |
| GLenum type, |
| ImageData* pixels) { |
| // TODO(zmo): To be implemented. |
| } |
| |
| void WebGL2RenderingContextBase::texSubImage3D(GLenum target, |
| GLint level, |
| GLint xoffset, |
| GLint yoffset, |
| GLint zoffset, |
| GLsizei width, |
| GLsizei height, |
| GLsizei depth, |
| GLenum format, |
| GLenum type, |
| HTMLImageElement* image, |
| ExceptionState& exceptionState) { |
| // TODO(zmo): To be implemented. |
| } |
| |
| void WebGL2RenderingContextBase::texSubImage3D(GLenum target, |
| GLint level, |
| GLint xoffset, |
| GLint yoffset, |
| GLint zoffset, |
| GLsizei width, |
| GLsizei height, |
| GLsizei depth, |
| GLenum format, |
| GLenum type, |
| HTMLCanvasElement* canvas, |
| ExceptionState& exceptionState) { |
| // TODO(zmo): To be implemented. |
| } |
| |
| void WebGL2RenderingContextBase::texSubImage3D(GLenum target, |
| GLint level, |
| GLint xoffset, |
| GLint yoffset, |
| GLint zoffset, |
| GLsizei width, |
| GLsizei height, |
| GLsizei depth, |
| GLenum format, |
| GLenum type, |
| HTMLVideoElement* video, |
| ExceptionState& exceptionState) { |
| // TODO(zmo): To be implemented. |
| } |
| |
| void WebGL2RenderingContextBase::texSubImage3D(GLenum target, |
| GLint level, |
| GLint xoffset, |
| GLint yoffset, |
| GLint zoffset, |
| GLsizei width, |
| GLsizei height, |
| GLsizei depth, |
| GLenum format, |
| GLenum type, |
| ImageBitmap* bitmap, |
| ExceptionState& exceptionState) { |
| // TODO(zmo): To be implemented. |
| } |
| |
| 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, |
| sentinelEmptyRect(), 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::compressedTexImage2D( |
| GLenum target, |
| GLint level, |
| GLenum internalformat, |
| GLsizei width, |
| GLsizei height, |
| GLint border, |
| DOMArrayBufferView* data) { |
| WebGLRenderingContextBase::compressedTexImage2D(target, level, internalformat, |
| width, height, border, data); |
| } |
| |
| void WebGL2RenderingContextBase::compressedTexImage2D( |
| GLenum target, |
| GLint level, |
| GLenum internalformat, |
| GLsizei width, |
| GLsizei height, |
| GLint border, |
| DOMArrayBufferView* data, |
| GLuint srcOffset, |
| GLuint srcLengthOverride) { |
| if (isContextLost()) |
| return; |
| if (!validateTexture2DBinding("compressedTexImage2D", target)) |
| return; |
| if (!validateCompressedTexFormat("compressedTexImage2D", internalformat)) |
| return; |
| if (srcOffset > data->byteLength()) { |
| synthesizeGLError(GL_INVALID_VALUE, "compressedTexImage2D", |
| "srcOffset is out of range"); |
| return; |
| } |
| if (srcLengthOverride == 0) { |
| srcLengthOverride = data->byteLength() - srcOffset; |
| } else if (srcLengthOverride > data->byteLength() - srcOffset) { |
| synthesizeGLError(GL_INVALID_VALUE, "compressedTexImage2D", |
| "srcLengthOverride is out of range"); |
| } |
| contextGL()->CompressedTexImage2D( |
| target, level, internalformat, width, height, border, srcLengthOverride, |
| static_cast<uint8_t*>(data->baseAddress()) + srcOffset); |
| } |
| |
| void WebGL2RenderingContextBase::compressedTexSubImage2D( |
| GLenum target, |
| GLint level, |
| GLint xoffset, |
| GLint yoffset, |
| GLsizei width, |
| GLsizei height, |
| GLenum format, |
| DOMArrayBufferView* data) { |
| WebGLRenderingContextBase::compressedTexSubImage2D( |
| target, level, xoffset, yoffset, width, height, format, data); |
| } |
| |
| void WebGL2RenderingContextBase::compressedTexSubImage2D( |
| GLenum target, |
| GLint level, |
| GLint xoffset, |
| GLint yoffset, |
| GLsizei width, |
| GLsizei height, |
| GLenum format, |
| DOMArrayBufferView* data, |
| GLuint srcOffset, |
| GLuint srcLengthOverride) { |
| if (isContextLost()) |
| return; |
| if (!validateTexture2DBinding("compressedTexSubImage2D", target)) |
| return; |
| if (!validateCompressedTexFormat("compressedTexSubImage2D", format)) |
| return; |
| if (srcOffset > data->byteLength()) { |
| synthesizeGLError(GL_INVALID_VALUE, "compressedTexSubImage2D", |
| "srcOffset is out of range"); |
| return; |
| } |
| if (srcLengthOverride == 0) { |
| srcLengthOverride = data->byteLength() - srcOffset; |
| } else if (srcLengthOverride > data->byteLength() - srcOffset) { |
| synthesizeGLError(GL_INVALID_VALUE, "compressedTexImage2D", |
| "srcLengthOverride is out of range"); |
| } |
| contextGL()->CompressedTexSubImage2D( |
| target, level, xoffset, yoffset, width, height, format, srcLengthOverride, |
| static_cast<uint8_t*>(data->baseAddress()) + srcOffset); |
| } |
| |
| void WebGL2RenderingContextBase::compressedTexImage3D( |
| GLenum target, |
| GLint level, |
| GLenum internalformat, |
| GLsizei width, |
| GLsizei height, |
| GLsizei depth, |
| GLint border, |
| DOMArrayBufferView* data, |
| GLuint srcOffset, |
| GLuint srcLengthOverride) { |
| if (isContextLost()) |
| return; |
| if (!validateTexture3DBinding("compressedTexImage3D", target)) |
| return; |
| if (!validateCompressedTexFormat("compressedTexImage3D", internalformat)) |
| return; |
| if (srcOffset > data->byteLength()) { |
| synthesizeGLError(GL_INVALID_VALUE, "compressedTexImage3D", |
| "srcOffset is out of range"); |
| return; |
| } |
| if (srcLengthOverride == 0) { |
| srcLengthOverride = data->byteLength() - srcOffset; |
| } else if (srcLengthOverride > data->byteLength() - srcOffset) { |
| synthesizeGLError(GL_INVALID_VALUE, "compressedTexImage3D", |
| "srcLengthOverride is out of range"); |
| } |
| contextGL()->CompressedTexImage3D( |
| target, level, internalformat, width, height, depth, border, |
| srcLengthOverride, |
| static_cast<uint8_t*>(data->baseAddress()) + srcOffset); |
| } |
| |
| void WebGL2RenderingContextBase::compressedTexSubImage3D( |
| GLenum target, |
| GLint level, |
| GLint xoffset, |
| GLint yoffset, |
| GLint zoffset, |
| GLsizei width, |
| GLsizei height, |
| GLsizei depth, |
| GLenum format, |
| DOMArrayBufferView* data, |
| GLuint srcOffset, |
| GLuint srcLengthOverride) { |
| if (isContextLost()) |
| return; |
| if (!validateTexture3DBinding("compressedTexSubImage3D", target)) |
| return; |
| if (!validateCompressedTexFormat("compressedTexSubImage3D", format)) |
| return; |
| if (srcOffset > data->byteLength()) { |
| synthesizeGLError(GL_INVALID_VALUE, "compressedTexSubImage3D", |
| "srcOffset is out of range"); |
| return; |
| } |
| if (srcLengthOverride == 0) { |
| srcLengthOverride = data->byteLength() - srcOffset; |
| } else if (srcLengthOverride > data->byteLength() - srcOffset) { |
| synthesizeGLError(GL_INVALID_VALUE, "compressedTexSubImage3D", |
| "srcLengthOverride is out of range"); |
| } |
| contextGL()->CompressedTexSubImage3D( |
| target, level, xoffset, yoffset, zoffset, width, height, depth, format, |
| srcLengthOverride, |
| static_cast<uint8_t*>(data->baseAddress()) + srcOffset); |
| } |
| |
| 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; |
| } |
| |
| if (m_currentElapsedQuery == query) { |
| contextGL()->EndQueryEXT(m_currentElapsedQuery->getTarget()); |
| m_currentElapsedQuery = nullptr; |
| } |
| |
| deleteObject(query); |
| } |
| |
| GLboolean WebGL2RenderingContextBase::isQuery(WebGLQuery* query) { |
| if (isContextLost() || !query) |
| return 0; |
| |
| return contextGL()->IsQueryEXT(query->object()); |
| } |
| |
| void WebGL2RenderingContextBase::beginQuery(GLenum target, WebGLQuery* query) { |
| bool deleted; |
| DCHECK(query); |
| 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; |
| case GL_TIME_ELAPSED_EXT: { |
| if (!extensionEnabled(EXTDisjointTimerQueryWebGL2Name)) { |
| synthesizeGLError(GL_INVALID_ENUM, "beginQuery", "invalid target"); |
| return; |
| } |
| if (m_currentElapsedQuery) { |
| synthesizeGLError(GL_INVALID_OPERATION, "beginQuery", |
| "a query is already active for target"); |
| return; |
| } |
| m_currentElapsedQuery = query; |
| } break; |
| default: |
| synthesizeGLError(GL_INVALID_ENUM, "beginQuery", "invalid target"); |
| return; |
| } |
| |
| if (!query->getTarget()) |
| query->setTarget(target); |
| |
| contextGL()->BeginQueryEXT(target, query->object()); |
| } |
| |
| void WebGL2RenderingContextBase::endQuery(GLenum target) { |
| if (isContextLost()) |
| return; |
| |
| switch (target) { |
| case GL_ANY_SAMPLES_PASSED: |
| case GL_ANY_SAMPLES_PASSED_CONSERVATIVE: { |
| if (m_currentBooleanOcclusionQuery && |
| m_currentBooleanOcclusionQuery->getTarget() == target) { |
| m_currentBooleanOcclusionQuery->resetCachedResult(); |
| m_currentBooleanOcclusionQuery = nullptr; |
| } else { |
| synthesizeGLError(GL_INVALID_OPERATION, "endQuery", |
| "target query is not active"); |
| return; |
| } |
| } break; |
| case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN: { |
| if (m_currentTransformFeedbackPrimitivesWrittenQuery) { |
| m_currentTransformFeedbackPrimitivesWrittenQuery->resetCachedResult(); |
| m_currentTransformFeedbackPrimitivesWrittenQuery = nullptr; |
| } else { |
| synthesizeGLError(GL_INVALID_OPERATION, "endQuery", |
| "target query is not active"); |
| return; |
| } |
| } break; |
| case GL_TIME_ELAPSED_EXT: { |
| if (!extensionEnabled(EXTDisjointTimerQueryWebGL2Name)) { |
| synthesizeGLError(GL_INVALID_ENUM, "endQuery", "invalid target"); |
| return; |
| } |
| if (m_currentElapsedQuery) { |
| m_currentElapsedQuery->resetCachedResult(); |
| m_currentElapsedQuery = 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); |
| } |
| |
| ScriptValue WebGL2RenderingContextBase::getQuery(ScriptState* scriptState, |
| GLenum target, |
| GLenum pname) { |
| if (isContextLost()) |
| return ScriptValue::createNull(scriptState); |
| |
| if (extensionEnabled(EXTDisjointTimerQueryWebGL2Name)) { |
| if (pname == GL_QUERY_COUNTER_BITS_EXT) { |
| if (target == GL_TIMESTAMP_EXT || target == GL_TIME_ELAPSED_EXT) { |
| GLint value = 0; |
| contextGL()->GetQueryivEXT(target, pname, &value); |
| return WebGLAny(scriptState, value); |
| } |
| synthesizeGLError(GL_INVALID_ENUM, "getQuery", |
| "invalid target/pname combination"); |
| return ScriptValue::createNull(scriptState); |
| } |
| |
| if (target == GL_TIME_ELAPSED_EXT && pname == GL_CURRENT_QUERY) { |
| return m_currentElapsedQuery |
| ? WebGLAny(scriptState, m_currentElapsedQuery) |
| : ScriptValue::createNull(scriptState); |
| } |
| |
| if (target == GL_TIMESTAMP_EXT && pname == GL_CURRENT_QUERY) { |
| return ScriptValue::createNull(scriptState); |
| } |
| } |
| |
| if (pname != GL_CURRENT_QUERY) { |
| synthesizeGLError(GL_INVALID_ENUM, "getQuery", "invalid parameter name"); |
| return ScriptValue::createNull(scriptState); |
| } |
| |
| switch (target) { |
| case GL_ANY_SAMPLES_PASSED: |
| case GL_ANY_SAMPLES_PASSED_CONSERVATIVE: |
| if (m_currentBooleanOcclusionQuery && |
| m_currentBooleanOcclusionQuery->getTarget() == target) |
| return WebGLAny(scriptState, m_currentBooleanOcclusionQuery); |
| break; |
| case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN: |
| return WebGLAny(scriptState, |
| m_currentTransformFeedbackPrimitivesWrittenQuery); |
| default: |
| synthesizeGLError(GL_INVALID_ENUM, "getQuery", "invalid target"); |
| return ScriptValue::createNull(scriptState); |
| } |
| return ScriptValue::createNull(scriptState); |
| } |
| |
| ScriptValue WebGL2RenderingContextBase::getQueryParameter( |
| ScriptState* scriptState, |
| WebGLQuery* query, |
| GLenum pname) { |
| DCHECK(query); |
| bool deleted; |
| 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 || |
| query == m_currentElapsedQuery) { |
| 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(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] = TraceWrapperMember<WebGLSampler>(this, sampler); |
| |
| contextGL()->BindSampler(unit, objectOrZero(sampler)); |
| } |
| |
| void WebGL2RenderingContextBase::samplerParameter(WebGLSampler* sampler, |
| GLenum pname, |
| GLfloat paramf, |
| GLint parami, |
| bool isFloat) { |
| if (isContextLost() || !validateWebGLObject("samplerParameter", sampler)) |
| return; |
| |
| GLint param = isFloat ? static_cast<GLint>(paramf) : parami; |
| switch (pname) { |
| case GL_TEXTURE_MAX_LOD: |
| case GL_TEXTURE_MIN_LOD: |
| break; |
| case GL_TEXTURE_COMPARE_FUNC: |
| switch (param) { |
| case GL_LEQUAL: |
| case GL_GEQUAL: |
| case GL_LESS: |
| case GL_GREATER: |
| case GL_EQUAL: |
| case GL_NOTEQUAL: |
| case GL_ALWAYS: |
| case GL_NEVER: |
| break; |
| default: |
| synthesizeGLError(GL_INVALID_ENUM, "samplerParameter", |
| "invalid parameter"); |
| return; |
| } |
| break; |
| case GL_TEXTURE_COMPARE_MODE: |
| switch (param) { |
| case GL_COMPARE_REF_TO_TEXTURE: |
| case GL_NONE: |
| break; |
| default: |
| synthesizeGLError(GL_INVALID_ENUM, "samplerParameter", |
| "invalid parameter"); |
| return; |
| } |
| break; |
| case GL_TEXTURE_MAG_FILTER: |
| switch (param) { |
| case GL_NEAREST: |
| case GL_LINEAR: |
| break; |
| default: |
| synthesizeGLError(GL_INVALID_ENUM, "samplerParameter", |
| "invalid parameter"); |
| return; |
| } |
| break; |
| case GL_TEXTURE_MIN_FILTER: |
| switch (param) { |
| case GL_NEAREST: |
| case GL_LINEAR: |
| case GL_NEAREST_MIPMAP_NEAREST: |
| case GL_LINEAR_MIPMAP_NEAREST: |
| case GL_NEAREST_MIPMAP_LINEAR: |
| case GL_LINEAR_MIPMAP_LINEAR: |
| break; |
| default: |
| synthesizeGLError(GL_INVALID_ENUM, "samplerParameter", |
| "invalid parameter"); |
| return; |
| } |
| break; |
| case GL_TEXTURE_WRAP_R: |
| case GL_TEXTURE_WRAP_S: |
| case GL_TEXTURE_WRAP_T: |
| switch (param) { |
| case GL_CLAMP_TO_EDGE: |
| case GL_MIRRORED_REPEAT: |
| case GL_REPEAT: |
| break; |
| default: |
| synthesizeGLError(GL_INVALID_ENUM, "samplerParameter", |
| "invalid parameter"); |
| return; |
| } |
| break; |
| default: |
| synthesizeGLError(GL_INVALID_ENUM, "samplerParameter", |
| "invalid parameter name"); |
| return; |
| } |
| |
| if (isFloat) { |
| 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, |
| GLuint64 timeout) { |
| if (isContextLost() || !validateWebGLObject("clientWaitSync", sync)) |
| return GL_WAIT_FAILED; |
| |
| if (timeout > kMaxClientWaitTimeout) { |
| synthesizeGLError(GL_INVALID_OPERATION, "clientWaitSync", |
| "timeout > MAX_CLIENT_WAIT_TIMEOUT_WEBGL"); |
| return GL_WAIT_FAILED; |
| } |
| |
| return contextGL()->ClientWaitSync(syncObjectOrZero(sync), flags, timeout); |
| } |
| |
| void WebGL2RenderingContextBase::waitSync(WebGLSync* sync, |
| GLbitfield flags, |
| GLint64 timeout) { |
| if (isContextLost() || !validateWebGLObject("waitSync", sync)) |
| return; |
| |
| if (flags) { |
| synthesizeGLError(GL_INVALID_VALUE, "waitSync", "invalid flags"); |
| return; |
| } |
| |
| if (timeout != -1) { |
| synthesizeGLError(GL_INVALID_VALUE, "waitSync", "invalid timeout"); |
| return; |
| } |
| |
| // This is intentionally changed to an no-op in WebGL2. |
| } |
| |
| 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( |
| 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); |
| } |
| } |
| |
| 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) { |
| synthesizeGLError(GL_INVALID_OPERATION, "bindBufferBase", |
| "attempt to bind a deleted buffer"); |
| return; |
| } |
| 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) { |
| synthesizeGLError(GL_INVALID_OPERATION, "bindBufferRange", |
| "attempt to bind a deleted buffer"); |
| return; |
| } |
| 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( |
| WebGLVertexArrayObject* vertexArray) { |
| if (isContextLost() || !vertexArray) |
| return; |
| |
| if (!vertexArray->isDefaultObject() && |
| vertexArray == m_boundVertexArrayObject) |
| setBoundVertexArrayObject(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( |
| 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(vertexArray); |
| } else { |
| contextGL()->BindVertexArrayOES(0); |
| setBoundVertexArrayObject(nullptr); |
| } |
| } |
| |
| void WebGL2RenderingContextBase::bindFramebuffer(GLenum target, |
| WebGLFramebuffer* buffer) { |
| bool deleted; |
| if (!checkObjectToBeBound("bindFramebuffer", buffer, deleted)) |
| return; |
| |
| if (deleted) { |
| synthesizeGLError(GL_INVALID_OPERATION, "bindFramebuffer", |
| "attempt to bind a deleted framebuffer"); |
| return; |
| } |
| |
| 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); |
| } |
| |
| 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) { |
| // 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, kMaxClientWaitTimeout); |
| 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); |
| case GL_TIMESTAMP_EXT: |
| if (extensionEnabled(EXTDisjointTimerQueryWebGL2Name)) { |
| return WebGLAny(scriptState, 0); |
| } |
| synthesizeGLError(GL_INVALID_ENUM, "getParameter", |
| "invalid parameter name, " |
| "EXT_disjoint_timer_query_webgl2 not enabled"); |
| return ScriptValue::createNull(scriptState); |
| case GL_GPU_DISJOINT_EXT: |
| if (extensionEnabled(EXTDisjointTimerQueryWebGL2Name)) { |
| return getBooleanParameter(scriptState, GL_GPU_DISJOINT_EXT); |
| } |
| synthesizeGLError(GL_INVALID_ENUM, "getParameter", |
| "invalid parameter name, " |
| "EXT_disjoint_timer_query_webgl2 not enabled"); |
| return ScriptValue::createNull(scriptState); |
| |
| 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] = |
| TraceWrapperMember<WebGLBuffer>(this, 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] = |
| TraceWrapperMember<WebGLBuffer>(this, 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 creationAttributes() because in WebGL 2, they are required to |
| // be honored. |
| bool hasDepth = creationAttributes().depth(); |
| bool hasStencil = creationAttributes().stencil(); |
| bool hasAlpha = creationAttributes().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_currentElapsedQuery); |
| visitor->trace(m_samplerUnits); |
| visitor->trace(m_getBufferSubDataAsyncCallbacks); |
| WebGLRenderingContextBase::trace(visitor); |
| } |
| |
| DEFINE_TRACE_WRAPPERS(WebGL2RenderingContextBase) { |
| if (isContextLost()) { |
| return; |
| } |
| |
| visitor->traceWrappers(m_transformFeedbackBinding); |
| visitor->traceWrappers(m_readFramebufferBinding); |
| visitor->traceWrappers(m_boundCopyReadBuffer); |
| visitor->traceWrappers(m_boundCopyWriteBuffer); |
| visitor->traceWrappers(m_boundPixelPackBuffer); |
| visitor->traceWrappers(m_boundPixelUnpackBuffer); |
| visitor->traceWrappers(m_boundTransformFeedbackBuffer); |
| visitor->traceWrappers(m_boundUniformBuffer); |
| for (auto& buf : m_boundIndexedTransformFeedbackBuffers) { |
| visitor->traceWrappers(buf); |
| } |
| for (auto& buf : m_boundIndexedUniformBuffers) { |
| visitor->traceWrappers(buf); |
| } |
| visitor->traceWrappers(m_currentBooleanOcclusionQuery); |
| visitor->traceWrappers(m_currentTransformFeedbackPrimitivesWrittenQuery); |
| visitor->traceWrappers(m_currentElapsedQuery); |
| for (auto& unit : m_samplerUnits) { |
| visitor->traceWrappers(unit); |
| } |
| WebGLRenderingContextBase::traceWrappers(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); |
| } |
| } |
| |
| const char* WebGL2RenderingContextBase::validateGetBufferSubData( |
| const char* functionName, |
| GLenum target, |
| GLintptr sourceByteOffset, |
| DOMArrayBufferView* destinationArrayBufferView, |
| GLuint destinationOffset, |
| GLuint length, |
| WebGLBuffer** outSourceBuffer, |
| void** outDestinationDataPtr, |
| long long* outDestinationByteLength) { |
| if (isContextLost()) { |
| return "Context lost"; |
| } |
| |
| if (!validateValueFitNonNegInt32(functionName, "srcByteOffset", |
| sourceByteOffset)) { |
| return "Invalid value: srcByteOffset"; |
| } |
| if (target == GL_TRANSFORM_FEEDBACK_BUFFER && m_currentProgram && |
| m_currentProgram->activeTransformFeedbackCount()) { |
| synthesizeGLError(GL_INVALID_OPERATION, functionName, |
| "targeted transform feedback buffer is active"); |
| return "Invalid operation: targeted transform feedback buffer is active"; |
| } |
| |
| WebGLBuffer* sourceBuffer = validateBufferDataTarget(functionName, target); |
| if (!sourceBuffer) { |
| return "Invalid operation: no buffer bound to target"; |
| } |
| *outSourceBuffer = sourceBuffer; |
| |
| if (!validateSubSourceAndGetData( |
| destinationArrayBufferView, destinationOffset, length, |
| outDestinationDataPtr, outDestinationByteLength)) { |
| synthesizeGLError(GL_INVALID_VALUE, functionName, "overflow of dstData"); |
| return "Invalid value: overflow of dstData"; |
| } |
| |
| return nullptr; |
| } |
| |
| const char* WebGL2RenderingContextBase::validateGetBufferSubDataBounds( |
| const char* functionName, |
| WebGLBuffer* sourceBuffer, |
| GLintptr sourceByteOffset, |
| long long destinationByteLength) { |
| CheckedNumeric<long long> srcEnd = sourceByteOffset; |
| srcEnd += destinationByteLength; |
| if (!srcEnd.IsValid() || srcEnd.ValueOrDie() > sourceBuffer->getSize()) { |
| synthesizeGLError(GL_INVALID_VALUE, functionName, |
| "overflow of bound buffer"); |
| return "Invalid value: overflow of bound buffer"; |
| } |
| |
| return nullptr; |
| } |
| |
| 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(GL_DRAW_FRAMEBUFFER, m_framebufferBinding.get()); |
| bindFramebuffer(GL_READ_FRAMEBUFFER, m_readFramebufferBinding.get()); |
| } |
| |
| void WebGL2RenderingContextBase::visitChildDOMWrappers( |
| v8::Isolate* isolate, |
| const v8::Persistent<v8::Object>& wrapper) { |
| if (isContextLost()) { |
| return; |
| } |
| |
| WebGLRenderingContextBase::visitChildDOMWrappers(isolate, wrapper); |
| |
| DOMWrapperWorld::setWrapperReferencesInAllWorlds( |
| wrapper, m_transformFeedbackBinding, isolate); |
| DOMWrapperWorld::setWrapperReferencesInAllWorlds( |
| wrapper, m_readFramebufferBinding, isolate); |
| |
| DOMWrapperWorld::setWrapperReferencesInAllWorlds( |
| wrapper, m_boundCopyReadBuffer, isolate); |
| DOMWrapperWorld::setWrapperReferencesInAllWorlds( |
| wrapper, m_boundCopyWriteBuffer, isolate); |
| DOMWrapperWorld::setWrapperReferencesInAllWorlds( |
| wrapper, m_boundPixelPackBuffer, isolate); |
| DOMWrapperWorld::setWrapperReferencesInAllWorlds( |
| wrapper, m_boundPixelUnpackBuffer, isolate); |
| DOMWrapperWorld::setWrapperReferencesInAllWorlds( |
| wrapper, m_boundTransformFeedbackBuffer, isolate); |
| DOMWrapperWorld::setWrapperReferencesInAllWorlds( |
| wrapper, m_boundUniformBuffer, isolate); |
| |
| for (auto& buf : m_boundIndexedTransformFeedbackBuffers) { |
| DOMWrapperWorld::setWrapperReferencesInAllWorlds(wrapper, buf, isolate); |
| } |
| |
| for (auto& buf : m_boundIndexedUniformBuffers) { |
| DOMWrapperWorld::setWrapperReferencesInAllWorlds(wrapper, buf, isolate); |
| } |
| |
| DOMWrapperWorld::setWrapperReferencesInAllWorlds( |
| wrapper, m_currentBooleanOcclusionQuery, isolate); |
| DOMWrapperWorld::setWrapperReferencesInAllWorlds( |
| wrapper, m_currentTransformFeedbackPrimitivesWrittenQuery, isolate); |
| DOMWrapperWorld::setWrapperReferencesInAllWorlds( |
| wrapper, m_currentElapsedQuery, isolate); |
| |
| for (auto& unit : m_samplerUnits) { |
| DOMWrapperWorld::setWrapperReferencesInAllWorlds(wrapper, unit, isolate); |
| } |
| } |
| |
| 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; |
| } |
| |
| void WebGL2RenderingContextBase:: |
| DrawingBufferClientRestorePixelUnpackBufferBinding() { |
| if (!contextGL()) |
| return; |
| contextGL()->BindBuffer(GL_PIXEL_UNPACK_BUFFER, |
| objectOrZero(m_boundPixelUnpackBuffer.get())); |
| } |
| |
| } // namespace blink |