blob: 3531cba5e97a7db72c0cd664ec161873eee5bb64 [file] [log] [blame]
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "config.h"
#include "modules/webgl/WebGL2RenderingContextBase.h"
#include "bindings/modules/v8/WebGLAny.h"
#include "core/html/HTMLCanvasElement.h"
#include "core/html/HTMLImageElement.h"
#include "core/html/HTMLVideoElement.h"
#include "core/html/ImageData.h"
#include "modules/webgl/WebGLActiveInfo.h"
#include "modules/webgl/WebGLBuffer.h"
#include "modules/webgl/WebGLFenceSync.h"
#include "modules/webgl/WebGLFramebuffer.h"
#include "modules/webgl/WebGLProgram.h"
#include "modules/webgl/WebGLQuery.h"
#include "modules/webgl/WebGLRenderbuffer.h"
#include "modules/webgl/WebGLSampler.h"
#include "modules/webgl/WebGLSync.h"
#include "modules/webgl/WebGLTexture.h"
#include "modules/webgl/WebGLTransformFeedback.h"
#include "modules/webgl/WebGLUniformLocation.h"
#include "modules/webgl/WebGLVertexArrayObject.h"
#include "platform/NotImplemented.h"
#include "wtf/OwnPtr.h"
#include "wtf/PassOwnPtr.h"
namespace blink {
namespace {
WGC3Dsync syncObjectOrZero(const WebGLSync* object)
{
return object ? object->object() : 0;
}
} // namespace
// These enums are from manual pages for glTexStorage2D/glTexStorage3D.
const GLenum kSupportedInternalFormatsStorage[] = {
GL_R8,
GL_R8_SNORM,
GL_R16F,
GL_R32F,
GL_R8UI,
GL_R8I,
GL_R16UI,
GL_R16I,
GL_R32UI,
GL_R32I,
GL_RG8,
GL_RG8_SNORM,
GL_RG16F,
GL_RG32F,
GL_RG8UI,
GL_RG8I,
GL_RG16UI,
GL_RG16I,
GL_RG32UI,
GL_RG32I,
GL_RGB8,
GL_SRGB8,
GL_RGB565,
GL_RGB8_SNORM,
GL_R11F_G11F_B10F,
GL_RGB9_E5,
GL_RGB16F,
GL_RGB32F,
GL_RGB8UI,
GL_RGB8I,
GL_RGB16UI,
GL_RGB16I,
GL_RGB32UI,
GL_RGB32I,
GL_RGBA8,
GL_SRGB8_ALPHA8,
GL_RGBA8_SNORM,
GL_RGB5_A1,
GL_RGBA4,
GL_RGB10_A2,
GL_RGBA16F,
GL_RGBA32F,
GL_RGBA8UI,
GL_RGBA8I,
GL_RGB10_A2UI,
GL_RGBA16UI,
GL_RGBA16I,
GL_RGBA32UI,
GL_RGBA32I,
GL_DEPTH_COMPONENT16,
GL_DEPTH_COMPONENT24,
GL_DEPTH_COMPONENT32F,
GL_DEPTH24_STENCIL8,
GL_DEPTH32F_STENCIL8,
GL_COMPRESSED_R11_EAC,
GL_COMPRESSED_SIGNED_R11_EAC,
GL_COMPRESSED_RG11_EAC,
GL_COMPRESSED_SIGNED_RG11_EAC,
GL_COMPRESSED_RGB8_ETC2,
GL_COMPRESSED_SRGB8_ETC2,
GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2,
GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2,
GL_COMPRESSED_RGBA8_ETC2_EAC,
GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC,
};
WebGL2RenderingContextBase::WebGL2RenderingContextBase(HTMLCanvasElement* passedCanvas, PassOwnPtr<WebGraphicsContext3D> context, const WebGLContextAttributes& requestedAttributes)
: WebGLRenderingContextBase(passedCanvas, context, requestedAttributes)
{
m_supportedInternalFormatsStorage.insert(kSupportedInternalFormatsStorage, kSupportedInternalFormatsStorage + arraysize(kSupportedInternalFormatsStorage));
}
WebGL2RenderingContextBase::~WebGL2RenderingContextBase()
{
m_readFramebufferBinding = nullptr;
m_boundCopyReadBuffer = nullptr;
m_boundCopyWriteBuffer = nullptr;
m_boundPixelPackBuffer = nullptr;
m_boundPixelUnpackBuffer = nullptr;
m_boundTransformFeedbackBuffer = nullptr;
m_boundUniformBuffer = nullptr;
m_currentBooleanOcclusionQuery = nullptr;
m_currentTransformFeedbackPrimitivesWrittenQuery = nullptr;
}
void WebGL2RenderingContextBase::initializeNewContext()
{
ASSERT(!isContextLost());
ASSERT(drawingBuffer());
m_readFramebufferBinding = nullptr;
m_boundCopyReadBuffer = nullptr;
m_boundCopyWriteBuffer = nullptr;
m_boundPixelPackBuffer = nullptr;
m_boundPixelUnpackBuffer = nullptr;
m_boundTransformFeedbackBuffer = nullptr;
m_boundUniformBuffer = nullptr;
m_currentBooleanOcclusionQuery = nullptr;
m_currentTransformFeedbackPrimitivesWrittenQuery = nullptr;
m_maxArrayTextureLayers = 0;
webContext()->getIntegerv(GL_MAX_ARRAY_TEXTURE_LAYERS, &m_maxArrayTextureLayers);
GLint numCombinedTextureImageUnits = 0;
webContext()->getIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &numCombinedTextureImageUnits);
m_samplerUnits.clear();
m_samplerUnits.resize(numCombinedTextureImageUnits);
GLint maxTransformFeedbackSeparateAttribs = 0;
webContext()->getIntegerv(GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS, &maxTransformFeedbackSeparateAttribs);
m_boundIndexedTransformFeedbackBuffers.clear();
m_boundIndexedTransformFeedbackBuffers.resize(maxTransformFeedbackSeparateAttribs);
GLint maxUniformBufferBindings = 0;
webContext()->getIntegerv(GL_MAX_UNIFORM_BUFFER_BINDINGS, &maxUniformBufferBindings);
m_boundIndexedUniformBuffers.clear();
m_boundIndexedUniformBuffers.resize(maxUniformBufferBindings);
m_maxBoundUniformBufferIndex = 0;
WebGLRenderingContextBase::initializeNewContext();
}
void WebGL2RenderingContextBase::copyBufferSubData(GLenum readTarget, GLenum writeTarget, long long readOffset, long long writeOffset, long long size)
{
if (isContextLost())
return;
if (!validateValueFitNonNegInt32("copyBufferSubData", "readOffset", readOffset)
|| !validateValueFitNonNegInt32("copyBufferSubData", "writeOffset", writeOffset)
|| !validateValueFitNonNegInt32("copyBufferSubData", "size", size)) {
return;
}
WebGLBuffer* readBuffer = validateBufferDataTarget("copyBufferSubData", readTarget);
if (!readBuffer)
return;
WebGLBuffer* writeBuffer = validateBufferDataTarget("copyBufferSubData", writeTarget);
if (!writeBuffer)
return;
if (readOffset + size > readBuffer->getSize() || writeOffset + size > writeBuffer->getSize()) {
synthesizeGLError(GL_INVALID_VALUE, "copyBufferSubData", "buffer overflow");
return;
}
if ((writeBuffer->getInitialTarget() == GL_ELEMENT_ARRAY_BUFFER && readBuffer->getInitialTarget() != GL_ELEMENT_ARRAY_BUFFER)
|| (writeBuffer->getInitialTarget() != GL_ELEMENT_ARRAY_BUFFER && readBuffer->getInitialTarget() == GL_ELEMENT_ARRAY_BUFFER)) {
synthesizeGLError(GL_INVALID_OPERATION, "copyBufferSubData", "Cannot copy into an element buffer destination from a non-element buffer source");
return;
}
if (writeBuffer->getInitialTarget() == 0)
writeBuffer->setInitialTarget(readBuffer->getInitialTarget());
webContext()->copyBufferSubData(readTarget, writeTarget, static_cast<GLintptr>(readOffset), static_cast<GLintptr>(writeOffset), static_cast<GLsizeiptr>(size));
}
void WebGL2RenderingContextBase::getBufferSubData(GLenum target, long long offset, DOMArrayBuffer* returnedData)
{
if (isContextLost())
return;
if (!returnedData) {
synthesizeGLError(GL_INVALID_VALUE, "getBufferSubData", "ArrayBuffer cannot be null");
return;
}
if (!validateValueFitNonNegInt32("getBufferSubData", "offset", offset)) {
return;
}
WebGLBuffer* buffer = validateBufferDataTarget("getBufferSubData", target);
if (!buffer)
return;
if (offset + returnedData->byteLength() > buffer->getSize()) {
synthesizeGLError(GL_INVALID_VALUE, "getBufferSubData", "buffer overflow");
return;
}
void* mappedData = webContext()->mapBufferRange(target, static_cast<GLintptr>(offset), returnedData->byteLength(), GL_MAP_READ_BIT);
if (!mappedData)
return;
memcpy(returnedData->data(), mappedData, returnedData->byteLength());
webContext()->unmapBuffer(target);
}
void WebGL2RenderingContextBase::blitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter)
{
if (isContextLost())
return;
webContext()->blitFramebufferCHROMIUM(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);
}
bool WebGL2RenderingContextBase::validateTexFuncLayer(const char* functionName, GLenum texTarget, GLint layer)
{
if (layer < 0) {
synthesizeGLError(GL_INVALID_VALUE, functionName, "layer out of range");
return false;
}
switch (texTarget) {
case GL_TEXTURE_3D:
if (layer > m_max3DTextureSize - 1) {
synthesizeGLError(GL_INVALID_VALUE, functionName, "layer out of range");
return false;
}
break;
case GL_TEXTURE_2D_ARRAY:
if (layer > m_maxArrayTextureLayers - 1) {
synthesizeGLError(GL_INVALID_VALUE, functionName, "layer out of range");
return false;
}
break;
default:
ASSERT_NOT_REACHED();
return false;
}
return true;
}
void WebGL2RenderingContextBase::framebufferTextureLayer(ScriptState* scriptState, GLenum target, GLenum attachment, WebGLTexture* texture, GLint level, GLint layer)
{
if (isContextLost() || !validateFramebufferFuncParameters("framebufferTextureLayer", target, attachment))
return;
if (texture && !texture->validate(contextGroup(), this)) {
synthesizeGLError(GL_INVALID_VALUE, "framebufferTextureLayer", "no texture or texture not from this context");
return;
}
GLenum textarget = texture ? texture->getTarget() : 0;
if (texture) {
if (textarget != GL_TEXTURE_3D && textarget != GL_TEXTURE_2D_ARRAY) {
synthesizeGLError(GL_INVALID_OPERATION, "framebufferTextureLayer", "invalid texture type");
return;
}
if (!validateTexFuncLayer("framebufferTextureLayer", textarget, layer))
return;
if (!validateTexFuncLevel("framebufferTextureLayer", textarget, level))
return;
}
WebGLFramebuffer* framebufferBinding = getFramebufferBinding(target);
if (!framebufferBinding || !framebufferBinding->object()) {
synthesizeGLError(GL_INVALID_OPERATION, "framebufferTextureLayer", "no framebuffer bound");
return;
}
webContext()->framebufferTextureLayer(target, attachment, objectOrZero(texture), level, layer);
framebufferBinding->setAttachmentForBoundFramebuffer(target, attachment, textarget, texture, level, layer);
applyStencilTest();
preserveObjectWrapper(scriptState, framebufferBinding, "attachment", attachment, texture);
}
ScriptValue WebGL2RenderingContextBase::getInternalformatParameter(ScriptState* scriptState, GLenum target, GLenum internalformat, GLenum pname)
{
if (isContextLost())
return ScriptValue::createNull(scriptState);
if (target != GL_RENDERBUFFER) {
synthesizeGLError(GL_INVALID_ENUM, "getInternalformatParameter", "invalid target");
return ScriptValue::createNull(scriptState);
}
switch (internalformat) {
case GL_R8UI:
case GL_R8I:
case GL_R16UI:
case GL_R16I:
case GL_R32UI:
case GL_R32I:
case GL_RG8UI:
case GL_RG8I:
case GL_RG16UI:
case GL_RG16I:
case GL_RG32UI:
case GL_RG32I:
case GL_RGBA8UI:
case GL_RGBA8I:
case GL_RGB10_A2UI:
case GL_RGBA16UI:
case GL_RGBA16I:
case GL_RGBA32UI:
case GL_RGBA32I:
return WebGLAny(scriptState, DOMInt32Array::create(0));
case GL_R8:
case GL_RG8:
case GL_RGB8:
case GL_RGB565:
case GL_RGBA8:
case GL_SRGB8_ALPHA8:
case GL_RGB5_A1:
case GL_RGBA4:
case GL_RGB10_A2:
case GL_DEPTH_COMPONENT16:
case GL_DEPTH_COMPONENT24:
case GL_DEPTH_COMPONENT32F:
case GL_DEPTH24_STENCIL8:
case GL_DEPTH32F_STENCIL8:
case GL_STENCIL_INDEX8:
break;
default:
synthesizeGLError(GL_INVALID_ENUM, "getInternalformatParameter", "invalid internalformat");
return ScriptValue::createNull(scriptState);
}
switch (pname) {
case GL_SAMPLES:
{
GLint length = -1;
webContext()->getInternalformativ(target, internalformat, GL_NUM_SAMPLE_COUNTS, 1, &length);
if (length <= 0)
return WebGLAny(scriptState, DOMInt32Array::create(0));
OwnPtr<GLint[]> values = adoptArrayPtr(new GLint[length]);
for (GLint ii = 0; ii < length; ++ii)
values[ii] = 0;
webContext()->getInternalformativ(target, internalformat, GL_SAMPLES, length, values.get());
return WebGLAny(scriptState, DOMInt32Array::create(values.get(), length));
}
default:
synthesizeGLError(GL_INVALID_ENUM, "getInternalformatParameter", "invalid parameter name");
return ScriptValue::createNull(scriptState);
}
}
void WebGL2RenderingContextBase::invalidateFramebuffer(GLenum target, Vector<GLenum>& attachments)
{
if (isContextLost())
return;
webContext()->invalidateFramebuffer(target, attachments.size(), attachments.data());
}
void WebGL2RenderingContextBase::invalidateSubFramebuffer(GLenum target, Vector<GLenum>& attachments, GLint x, GLint y, GLsizei width, GLsizei height)
{
if (isContextLost())
return;
webContext()->invalidateSubFramebuffer(target, attachments.size(), attachments.data(), x, y, width, height);
}
void WebGL2RenderingContextBase::readBuffer(GLenum mode)
{
if (isContextLost())
return;
switch (mode) {
case GL_BACK:
case GL_NONE:
case GL_COLOR_ATTACHMENT0:
break;
default:
if (mode > GL_COLOR_ATTACHMENT0
&& mode < static_cast<GLenum>(GL_COLOR_ATTACHMENT0 + maxColorAttachments()))
break;
synthesizeGLError(GL_INVALID_ENUM, "readBuffer", "invalid read buffer");
return;
}
WebGLFramebuffer* readFramebufferBinding = getFramebufferBinding(GL_READ_FRAMEBUFFER);
if (!readFramebufferBinding) {
ASSERT(drawingBuffer());
if (mode != GL_BACK && mode != GL_NONE) {
synthesizeGLError(GL_INVALID_OPERATION, "readBuffer", "invalid read buffer");
return;
}
m_readBufferOfDefaultFramebuffer = mode;
// translate GL_BACK to GL_COLOR_ATTACHMENT0, because the default
// framebuffer for WebGL is not fb 0, it is an internal fbo.
if (mode == GL_BACK)
mode = GL_COLOR_ATTACHMENT0;
} else {
if (mode == GL_BACK) {
synthesizeGLError(GL_INVALID_OPERATION, "readBuffer", "invalid read buffer");
return;
}
readFramebufferBinding->readBuffer(mode);
}
webContext()->readBuffer(mode);
}
void WebGL2RenderingContextBase::readPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, DOMArrayBufferView* pixels)
{
if (isContextLost())
return;
if (m_boundPixelPackBuffer.get()) {
synthesizeGLError(GL_INVALID_OPERATION, "readPixels", "PIXEL_PACK buffer should not be bound");
return;
}
WebGLRenderingContextBase::readPixels(x, y, width, height, format, type, pixels);
}
void WebGL2RenderingContextBase::readPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, long long offset)
{
if (isContextLost())
return;
// Due to WebGL's same-origin restrictions, it is not possible to
// taint the origin using the WebGL API.
ASSERT(canvas()->originClean());
if (!validateValueFitNonNegInt32("readPixels", "offset", offset))
return;
WebGLBuffer* buffer = m_boundPixelPackBuffer.get();
if (!buffer) {
synthesizeGLError(GL_INVALID_OPERATION, "readPixels", "no PIXEL_PACK buffer bound");
return;
}
// Need to validate whether the pixel pack buffer is mapped,
// if we decide to expose mapBufferRange() to web developers.
long long size = buffer->getSize() - offset;
// If size is negative, or size is not large enough to store pixels, those cases
// are handled by validateReadPixelsFuncParameters to generate INVALID_OPERATION.
if (!validateReadPixelsFuncParameters(width, height, format, type, size))
return;
clearIfComposited();
WebGLFramebuffer* readFramebufferBinding = getFramebufferBinding(GL_READ_FRAMEBUFFER);
{
ScopedDrawingBufferBinder binder(drawingBuffer(), readFramebufferBinding);
webContext()->readPixels(x, y, width, height, format, type, reinterpret_cast<void*>(offset));
}
}
void WebGL2RenderingContextBase::renderbufferStorageImpl(
GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height,
const char* functionName)
{
switch (internalformat) {
case GL_R8UI:
case GL_R8I:
case GL_R16UI:
case GL_R16I:
case GL_R32UI:
case GL_R32I:
case GL_RG8UI:
case GL_RG8I:
case GL_RG16UI:
case GL_RG16I:
case GL_RG32UI:
case GL_RG32I:
case GL_RGBA8UI:
case GL_RGBA8I:
case GL_RGB10_A2UI:
case GL_RGBA16UI:
case GL_RGBA16I:
case GL_RGBA32UI:
case GL_RGBA32I:
if (samples > 0) {
synthesizeGLError(GL_INVALID_OPERATION, functionName,
"for integer formats, samples > 0");
return;
}
case GL_R8:
case GL_RG8:
case GL_RGB8:
case GL_RGB565:
case GL_RGBA8:
case GL_SRGB8_ALPHA8:
case GL_RGB5_A1:
case GL_RGBA4:
case GL_RGB10_A2:
case GL_DEPTH_COMPONENT16:
case GL_DEPTH_COMPONENT24:
case GL_DEPTH_COMPONENT32F:
case GL_DEPTH24_STENCIL8:
case GL_DEPTH32F_STENCIL8:
case GL_STENCIL_INDEX8:
if (!samples) {
webContext()->renderbufferStorage(target, internalformat, width, height);
} else {
webContext()->renderbufferStorageMultisampleCHROMIUM(
target, samples, internalformat, width, height);
}
break;
case GL_DEPTH_STENCIL:
// To be WebGL 1 backward compatible.
if (samples > 0) {
synthesizeGLError(GL_INVALID_ENUM, functionName, "invalid internalformat");
return;
}
webContext()->renderbufferStorage(target, GL_DEPTH24_STENCIL8, width, height);
break;
default:
synthesizeGLError(GL_INVALID_ENUM, functionName, "invalid internalformat");
return;
}
m_renderbufferBinding->setInternalFormat(internalformat);
m_renderbufferBinding->setSize(width, height);
}
void WebGL2RenderingContextBase::renderbufferStorageMultisample(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height)
{
const char* functionName = "renderbufferStorageMultisample";
if (isContextLost())
return;
if (target != GL_RENDERBUFFER) {
synthesizeGLError(GL_INVALID_ENUM, functionName, "invalid target");
return;
}
if (!m_renderbufferBinding || !m_renderbufferBinding->object()) {
synthesizeGLError(GL_INVALID_OPERATION, functionName, "no bound renderbuffer");
return;
}
if (!validateSize("renderbufferStorage", width, height))
return;
if (samples < 0) {
synthesizeGLError(GL_INVALID_VALUE, functionName, "samples < 0");
return;
}
renderbufferStorageImpl(target, samples, internalformat, width, height, functionName);
applyStencilTest();
}
/* Texture objects */
bool WebGL2RenderingContextBase::validateTexStorage(const char* functionName, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, TexStorageType functionType)
{
if (functionType == TexStorageType2D) {
if (target != GL_TEXTURE_2D && target != GL_TEXTURE_CUBE_MAP) {
synthesizeGLError(GL_INVALID_ENUM, functionName, "invalid 2D target");
return false;
}
} else {
if (target != GL_TEXTURE_3D && target != GL_TEXTURE_2D_ARRAY) {
synthesizeGLError(GL_INVALID_ENUM, functionName, "invalid 3D target");
return false;
}
}
WebGLTexture* tex = validateTextureBinding(functionName, target, false);
if (!tex)
return false;
if (m_supportedInternalFormatsStorage.find(internalformat) == m_supportedInternalFormatsStorage.end()) {
synthesizeGLError(GL_INVALID_ENUM, functionName, "invalid internalformat");
return false;
}
if (tex->isImmutable()) {
synthesizeGLError(GL_INVALID_OPERATION, functionName, "attempted to modify immutable texture");
return false;
}
if (width <= 0 || height <= 0 || depth <= 0) {
synthesizeGLError(GL_INVALID_VALUE, functionName, "invalid dimensions");
return false;
}
if (levels <= 0) {
synthesizeGLError(GL_INVALID_VALUE, functionName, "invalid levels");
return false;
}
if (target == GL_TEXTURE_3D) {
if (levels > log2(std::max(std::max(width, height), depth)) + 1) {
synthesizeGLError(GL_INVALID_OPERATION, functionName, "to many levels");
return false;
}
} else {
if (levels > log2(std::max(width, height)) + 1) {
synthesizeGLError(GL_INVALID_OPERATION, functionName, "to many levels");
return false;
}
}
return true;
}
void WebGL2RenderingContextBase::texStorage2D(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height)
{
if (isContextLost() || !validateTexStorage("texStorage2D", target, levels, internalformat, width, height, 1, TexStorageType2D))
return;
WebGLTexture* tex = validateTextureBinding("texStorage2D", target, false);
webContext()->texStorage2DEXT(target, levels, internalformat, width, height);
tex->setTexStorageInfo(target, levels, internalformat, width, height, 1);
}
void WebGL2RenderingContextBase::texStorage3D(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth)
{
if (isContextLost() || !validateTexStorage("texStorage3D", target, levels, internalformat, width, height, depth, TexStorageType3D))
return;
WebGLTexture* tex = validateTextureBinding("texStorage3D", target, false);
webContext()->texStorage3D(target, levels, internalformat, width, height, depth);
tex->setTexStorageInfo(target, levels, internalformat, width, height, depth);
}
bool WebGL2RenderingContextBase::validateTexImage3D(const char* functionName, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type)
{
switch (target) {
case GL_TEXTURE_3D:
case GL_TEXTURE_2D_ARRAY:
break;
default:
synthesizeGLError(GL_INVALID_ENUM, functionName, "invalid target");
return false;
}
if (!validateTexFuncLevel(functionName, target, level))
return false;
if (!validateTexFuncParameters(functionName, NotTexSubImage2D, target, level, internalformat, width, height, depth, border, format, type))
return false;
return true;
}
void WebGL2RenderingContextBase::texImage3D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, DOMArrayBufferView* pixels)
{
if (isContextLost() || !validateTexImage3D("texImage3D", target, level, internalformat, width, height, depth, border, format, type)
|| !validateTexFuncData("texImage3D", level, width, height, format, type, pixels, NullAllowed))
return;
void* data = pixels ? pixels->baseAddress() : 0;
Vector<uint8_t> tempData;
if (data && (m_unpackFlipY || m_unpackPremultiplyAlpha)) {
// FIXME: WebGLImageConversion needs to be updated to accept image depth.
notImplemented();
return;
}
WebGLTexture* tex = validateTextureBinding("texImage3D", target, true);
if (!tex)
return;
if (tex->isImmutable()) {
synthesizeGLError(GL_INVALID_OPERATION, "texImage3D", "attempted to modify immutable texture");
return;
}
webContext()->texImage3D(target, level, convertTexInternalFormat(internalformat, type), width, height, depth, border, format, type, data);
tex->setLevelInfo(target, level, internalformat, width, height, depth, type);
}
bool WebGL2RenderingContextBase::validateTexSubImage3D(const char* functionName, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset,
GLenum format, GLenum type, GLsizei width, GLsizei height, GLsizei depth)
{
switch (target) {
case GL_TEXTURE_3D:
case GL_TEXTURE_2D_ARRAY:
break;
default:
synthesizeGLError(GL_INVALID_ENUM, functionName, "invalid target");
return false;
}
WebGLTexture* tex = validateTextureBinding(functionName, target, false);
if (!tex)
return false;
if (!validateTexFuncLevel(functionName, target, level))
return false;
if (!tex->isValid(target, level)) {
synthesizeGLError(GL_INVALID_OPERATION, "texSubImage3D", "no previously defined texture image");
return false;
}
// Before checking if it is in the range, check if overflow happens first.
Checked<GLint, RecordOverflow> maxX = xoffset, maxY = yoffset, maxZ = zoffset;
maxX += width;
maxY += height;
maxZ += depth;
if (maxX.hasOverflowed() || maxY.hasOverflowed() || maxZ.hasOverflowed()
|| maxX.unsafeGet() > tex->getWidth(target, level)
|| maxY.unsafeGet() > tex->getHeight(target, level)
|| maxZ.unsafeGet() > tex->getDepth(target, level)) {
synthesizeGLError(GL_INVALID_VALUE, functionName, "dimensions out of range");
return false;
}
GLenum internalformat = tex->getInternalFormat(target, level);
if (!validateTexFuncFormatAndType(functionName, internalformat, format, type, level))
return false;
return true;
}
void WebGL2RenderingContextBase::texSubImage3DImpl(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLenum format, GLenum type, Image* image, WebGLImageConversion::ImageHtmlDomSource domSource, bool flipY, bool premultiplyAlpha)
{
if (!validateTexSubImage3D("texSubImage3D", target, level, xoffset, yoffset, zoffset, format, type, image->width(), image->height(), 1))
return;
// All calling functions check isContextLost, so a duplicate check is not needed here.
if (type == GL_UNSIGNED_INT_10F_11F_11F_REV) {
// The UNSIGNED_INT_10F_11F_11F_REV type pack/unpack isn't implemented.
type = GL_FLOAT;
}
Vector<uint8_t> data;
WebGLImageConversion::ImageExtractor imageExtractor(image, domSource, premultiplyAlpha, m_unpackColorspaceConversion == GL_NONE);
if (!imageExtractor.imagePixelData()) {
synthesizeGLError(GL_INVALID_VALUE, "texSubImage3D", "bad image");
return;
}
WebGLImageConversion::DataFormat sourceDataFormat = imageExtractor.imageSourceFormat();
WebGLImageConversion::AlphaOp alphaOp = imageExtractor.imageAlphaOp();
const void* imagePixelData = imageExtractor.imagePixelData();
bool needConversion = true;
if (type == GL_UNSIGNED_BYTE && sourceDataFormat == WebGLImageConversion::DataFormatRGBA8 && format == GL_RGBA && alphaOp == WebGLImageConversion::AlphaDoNothing && !flipY) {
needConversion = false;
} else {
if (!WebGLImageConversion::packImageData(image, imagePixelData, format, type, flipY, alphaOp, sourceDataFormat, imageExtractor.imageWidth(), imageExtractor.imageHeight(), imageExtractor.imageSourceUnpackAlignment(), data)) {
synthesizeGLError(GL_INVALID_VALUE, "texSubImage3D", "bad image data");
return;
}
}
if (m_unpackAlignment != 1)
webContext()->pixelStorei(GL_UNPACK_ALIGNMENT, 1);
webContext()->texSubImage3D(target, level, xoffset, yoffset, zoffset, imageExtractor.imageWidth(), imageExtractor.imageHeight(), 1, format, type, needConversion ? data.data() : imagePixelData);
if (m_unpackAlignment != 1)
webContext()->pixelStorei(GL_UNPACK_ALIGNMENT, m_unpackAlignment);
}
void WebGL2RenderingContextBase::texSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, DOMArrayBufferView* pixels)
{
if (isContextLost() || !pixels || !validateTexSubImage3D("texSubImage3D", target, level, xoffset, yoffset, zoffset, format, type, width, height, depth)
|| !validateTexFuncData("texSubImage3D", level, width, height, format, type, pixels, NullAllowed))
return;
// FIXME: Ensure pixels is large enough to contain the desired texture dimensions.
void* data = pixels->baseAddress();
Vector<uint8_t> tempData;
bool changeUnpackAlignment = false;
if (data && (m_unpackFlipY || m_unpackPremultiplyAlpha)) {
if (!WebGLImageConversion::extractTextureData(width, height, format, type,
m_unpackAlignment,
m_unpackFlipY, m_unpackPremultiplyAlpha,
data,
tempData))
return;
data = tempData.data();
changeUnpackAlignment = true;
}
if (changeUnpackAlignment)
webContext()->pixelStorei(GL_UNPACK_ALIGNMENT, 1);
webContext()->texSubImage3D(target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, data);
if (changeUnpackAlignment)
webContext()->pixelStorei(GL_UNPACK_ALIGNMENT, m_unpackAlignment);
}
void WebGL2RenderingContextBase::texSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLenum format, GLenum type, ImageData* pixels)
{
if (isContextLost() || !pixels || !validateTexSubImage3D("texSubImage3D", target, level, xoffset, yoffset, zoffset, format, type, pixels->width(), pixels->height(), 1))
return;
if (type == GL_UNSIGNED_INT_10F_11F_11F_REV) {
// The UNSIGNED_INT_10F_11F_11F_REV type pack/unpack isn't implemented.
type = GL_FLOAT;
}
Vector<uint8_t> data;
bool needConversion = true;
// The data from ImageData is always of format RGBA8.
// No conversion is needed if destination format is RGBA and type is USIGNED_BYTE and no Flip or Premultiply operation is required.
if (format == GL_RGBA && type == GL_UNSIGNED_BYTE && !m_unpackFlipY && !m_unpackPremultiplyAlpha) {
needConversion = false;
} else {
if (!WebGLImageConversion::extractImageData(pixels->data()->data(), pixels->size(), format, type, m_unpackFlipY, m_unpackPremultiplyAlpha, data)) {
synthesizeGLError(GL_INVALID_VALUE, "texSubImage3D", "bad image data");
return;
}
}
if (m_unpackAlignment != 1)
webContext()->pixelStorei(GL_UNPACK_ALIGNMENT, 1);
webContext()->texSubImage3D(target, level, xoffset, yoffset, zoffset, pixels->width(), pixels->height(), 1, format, type, needConversion ? data.data() : pixels->data()->data());
if (m_unpackAlignment != 1)
webContext()->pixelStorei(GL_UNPACK_ALIGNMENT, m_unpackAlignment);
}
void WebGL2RenderingContextBase::texSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLenum format, GLenum type, HTMLImageElement* image, ExceptionState& exceptionState)
{
if (isContextLost() || !image || !validateHTMLImageElement("texSubImage3D", image, exceptionState))
return;
RefPtr<Image> imageForRender = image->cachedImage()->image();
if (imageForRender->isSVGImage())
imageForRender = drawImageIntoBuffer(imageForRender.get(), image->width(), image->height(), "texSubImage3D");
texSubImage3DImpl(target, level, xoffset, yoffset, zoffset, format, type, imageForRender.get(), WebGLImageConversion::HtmlDomImage, m_unpackFlipY, m_unpackPremultiplyAlpha);
}
void WebGL2RenderingContextBase::texSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLenum format, GLenum type, HTMLCanvasElement* canvas, ExceptionState& exceptionState)
{
if (isContextLost() || !validateHTMLCanvasElement("texSubImage3D", canvas, exceptionState))
return;
WebGLTexture* texture = validateTextureBinding("texSubImage3D", target, false);
ASSERT(texture);
GLenum internalformat = texture->getInternalFormat(target, level);
if (!canvas->renderingContext() || !canvas->renderingContext()->isAccelerated() || !canUseTexImageCanvasByGPU(internalformat, type)) {
ASSERT(!canvas->renderingContext() || canvas->renderingContext()->is2d());
// 2D canvas has only FrontBuffer.
texSubImage3DImpl(target, level, xoffset, yoffset, zoffset, format, type, canvas->copiedImage(FrontBuffer, PreferAcceleration).get(),
WebGLImageConversion::HtmlDomCanvas, m_unpackFlipY, m_unpackPremultiplyAlpha);
return;
}
texImageCanvasByGPU(TexSubImage3DByGPU, texture, target, level, GL_RGBA, type, xoffset, yoffset, zoffset, canvas);
}
void WebGL2RenderingContextBase::texSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLenum format, GLenum type, HTMLVideoElement* video, ExceptionState& exceptionState)
{
if (isContextLost() || !validateHTMLVideoElement("texSubImage3D", video, exceptionState))
return;
RefPtr<Image> image = videoFrameToImage(video);
if (!image)
return;
texSubImage3DImpl(target, level, xoffset, yoffset, zoffset, format, type, image.get(), WebGLImageConversion::HtmlDomVideo, m_unpackFlipY, m_unpackPremultiplyAlpha);
}
void WebGL2RenderingContextBase::copyTexSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height)
{
if (isContextLost())
return;
if (!validateCopyTexSubImage("copyTexSubImage3D", target, level, xoffset, yoffset, zoffset, x, y, width, height))
return;
WebGLFramebuffer* readFramebufferBinding = nullptr;
if (!validateReadBufferAndGetInfo("copyTexSubImage3D", readFramebufferBinding, nullptr, nullptr))
return;
clearIfComposited();
ScopedDrawingBufferBinder binder(drawingBuffer(), readFramebufferBinding);
webContext()->copyTexSubImage3D(target, level, xoffset, yoffset, zoffset, x, y, width, height);
}
void WebGL2RenderingContextBase::compressedTexImage3D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, DOMArrayBufferView* data)
{
if (isContextLost())
return;
WebGLTexture* tex = validateTextureBinding("compressedTexImage3D", target, true);
if (!tex)
return;
if (tex->isImmutable()) {
synthesizeGLError(GL_INVALID_OPERATION, "compressedTexImage3D", "attempted to modify immutable texture");
return;
}
webContext()->compressedTexImage3D(target, level, internalformat, width, height, depth, border, data->byteLength(), data->baseAddress());
tex->setLevelInfo(target, level, internalformat, width, height, depth, GL_UNSIGNED_BYTE);
}
void WebGL2RenderingContextBase::compressedTexSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, DOMArrayBufferView* data)
{
if (isContextLost())
return;
WebGLTexture* tex = validateTextureBinding("compressedTexSubImage3D", target, true);
if (!tex)
return;
if (format != tex->getInternalFormat(target, level)) {
synthesizeGLError(GL_INVALID_OPERATION, "compressedTexSubImage3D", "format does not match texture format");
return;
}
webContext()->compressedTexSubImage3D(target, level, xoffset, yoffset, zoffset,
width, height, depth, format, data->byteLength(), data->baseAddress());
}
GLint WebGL2RenderingContextBase::getFragDataLocation(WebGLProgram* program, const String& name)
{
if (isContextLost() || !validateWebGLObject("getFragDataLocation", program))
return -1;
return webContext()->getFragDataLocation(objectOrZero(program), name.utf8().data());
}
void WebGL2RenderingContextBase::uniform1ui(const WebGLUniformLocation* location, GLuint v0)
{
if (isContextLost() || !location)
return;
if (location->program() != m_currentProgram) {
synthesizeGLError(GL_INVALID_OPERATION, "uniform1ui", "location not for current program");
return;
}
webContext()->uniform1ui(location->location(), v0);
}
void WebGL2RenderingContextBase::uniform2ui(const WebGLUniformLocation* location, GLuint v0, GLuint v1)
{
if (isContextLost() || !location)
return;
if (location->program() != m_currentProgram) {
synthesizeGLError(GL_INVALID_OPERATION, "uniform2ui", "location not for current program");
return;
}
webContext()->uniform2ui(location->location(), v0, v1);
}
void WebGL2RenderingContextBase::uniform3ui(const WebGLUniformLocation* location, GLuint v0, GLuint v1, GLuint v2)
{
if (isContextLost() || !location)
return;
if (location->program() != m_currentProgram) {
synthesizeGLError(GL_INVALID_OPERATION, "uniform3ui", "location not for current program");
return;
}
webContext()->uniform3ui(location->location(), v0, v1, v2);
}
void WebGL2RenderingContextBase::uniform4ui(const WebGLUniformLocation* location, GLuint v0, GLuint v1, GLuint v2, GLuint v3)
{
if (isContextLost() || !location)
return;
if (location->program() != m_currentProgram) {
synthesizeGLError(GL_INVALID_OPERATION, "uniform4ui", "location not for current program");
return;
}
webContext()->uniform4ui(location->location(), v0, v1, v2, v3);
}
void WebGL2RenderingContextBase::uniform1uiv(const WebGLUniformLocation* location, const FlexibleUint32ArrayView& v)
{
if (isContextLost() || !validateUniformParameters<WTF::Uint32Array>("uniform1uiv", location, v, 1))
return;
webContext()->uniform1uiv(location->location(), v.length(), v.dataMaybeOnStack());
}
void WebGL2RenderingContextBase::uniform1uiv(const WebGLUniformLocation* location, Vector<GLuint>& value)
{
if (isContextLost() || !validateUniformParameters("uniform1uiv", location, value.data(), value.size(), 1))
return;
webContext()->uniform1uiv(location->location(), value.size(), value.data());
}
void WebGL2RenderingContextBase::uniform2uiv(const WebGLUniformLocation* location, const FlexibleUint32ArrayView& v)
{
if (isContextLost() || !validateUniformParameters<WTF::Uint32Array>("uniform2uiv", location, v, 2))
return;
webContext()->uniform2uiv(location->location(), v.length() >> 1, v.dataMaybeOnStack());
}
void WebGL2RenderingContextBase::uniform2uiv(const WebGLUniformLocation* location, Vector<GLuint>& value)
{
if (isContextLost() || !validateUniformParameters("uniform2uiv", location, value.data(), value.size(), 2))
return;
webContext()->uniform2uiv(location->location(), value.size() / 2, value.data());
}
void WebGL2RenderingContextBase::uniform3uiv(const WebGLUniformLocation* location, const FlexibleUint32ArrayView& v)
{
if (isContextLost() || !validateUniformParameters<WTF::Uint32Array>("uniform3uiv", location, v, 3))
return;
webContext()->uniform3uiv(location->location(), v.length() / 3, v.dataMaybeOnStack());
}
void WebGL2RenderingContextBase::uniform3uiv(const WebGLUniformLocation* location, Vector<GLuint>& value)
{
if (isContextLost() || !validateUniformParameters("uniform3uiv", location, value.data(), value.size(), 3))
return;
webContext()->uniform3uiv(location->location(), value.size() / 3, value.data());
}
void WebGL2RenderingContextBase::uniform4uiv(const WebGLUniformLocation* location, const FlexibleUint32ArrayView& v)
{
if (isContextLost() || !validateUniformParameters<WTF::Uint32Array>("uniform4uiv", location, v, 4))
return;
webContext()->uniform4uiv(location->location(), v.length() >> 2, v.dataMaybeOnStack());
}
void WebGL2RenderingContextBase::uniform4uiv(const WebGLUniformLocation* location, Vector<GLuint>& value)
{
if (isContextLost() || !validateUniformParameters("uniform4uiv", location, value.data(), value.size(), 4))
return;
webContext()->uniform4uiv(location->location(), value.size() / 4, value.data());
}
void WebGL2RenderingContextBase::uniformMatrix2x3fv(const WebGLUniformLocation* location, GLboolean transpose, DOMFloat32Array* value)
{
if (isContextLost() || !validateUniformMatrixParameters("uniformMatrix2x3fv", location, transpose, value, 6))
return;
webContext()->uniformMatrix2x3fv(location->location(), value->length() / 6, transpose, value->data());
}
void WebGL2RenderingContextBase::uniformMatrix2x3fv(const WebGLUniformLocation* location, GLboolean transpose, Vector<GLfloat>& value)
{
if (isContextLost() || !validateUniformMatrixParameters("uniformMatrix2x3fv", location, transpose, value.data(), value.size(), 6))
return;
webContext()->uniformMatrix2x3fv(location->location(), value.size() / 6, transpose, value.data());
}
void WebGL2RenderingContextBase::uniformMatrix3x2fv(const WebGLUniformLocation* location, GLboolean transpose, DOMFloat32Array* value)
{
if (isContextLost() || !validateUniformMatrixParameters("uniformMatrix3x2fv", location, transpose, value, 6))
return;
webContext()->uniformMatrix3x2fv(location->location(), value->length() / 6, transpose, value->data());
}
void WebGL2RenderingContextBase::uniformMatrix3x2fv(const WebGLUniformLocation* location, GLboolean transpose, Vector<GLfloat>& value)
{
if (isContextLost() || !validateUniformMatrixParameters("uniformMatrix3x2fv", location, transpose, value.data(), value.size(), 6))
return;
webContext()->uniformMatrix3x2fv(location->location(), value.size() / 6, transpose, value.data());
}
void WebGL2RenderingContextBase::uniformMatrix2x4fv(const WebGLUniformLocation* location, GLboolean transpose, DOMFloat32Array* value)
{
if (isContextLost() || !validateUniformMatrixParameters("uniformMatrix2x4fv", location, transpose, value, 8))
return;
webContext()->uniformMatrix2x4fv(location->location(), value->length() / 8, transpose, value->data());
}
void WebGL2RenderingContextBase::uniformMatrix2x4fv(const WebGLUniformLocation* location, GLboolean transpose, Vector<GLfloat>& value)
{
if (isContextLost() || !validateUniformMatrixParameters("uniformMatrix2x4fv", location, transpose, value.data(), value.size(), 8))
return;
webContext()->uniformMatrix2x4fv(location->location(), value.size() / 8, transpose, value.data());
}
void WebGL2RenderingContextBase::uniformMatrix4x2fv(const WebGLUniformLocation* location, GLboolean transpose, DOMFloat32Array* value)
{
if (isContextLost() || !validateUniformMatrixParameters("uniformMatrix4x2fv", location, transpose, value, 8))
return;
webContext()->uniformMatrix4x2fv(location->location(), value->length() / 8, transpose, value->data());
}
void WebGL2RenderingContextBase::uniformMatrix4x2fv(const WebGLUniformLocation* location, GLboolean transpose, Vector<GLfloat>& value)
{
if (isContextLost() || !validateUniformMatrixParameters("uniformMatrix4x2fv", location, transpose, value.data(), value.size(), 8))
return;
webContext()->uniformMatrix4x2fv(location->location(), value.size() / 8, transpose, value.data());
}
void WebGL2RenderingContextBase::uniformMatrix3x4fv(const WebGLUniformLocation* location, GLboolean transpose, DOMFloat32Array* value)
{
if (isContextLost() || !validateUniformMatrixParameters("uniformMatrix3x4fv", location, transpose, value, 12))
return;
webContext()->uniformMatrix3x4fv(location->location(), value->length() / 12, transpose, value->data());
}
void WebGL2RenderingContextBase::uniformMatrix3x4fv(const WebGLUniformLocation* location, GLboolean transpose, Vector<GLfloat>& value)
{
if (isContextLost() || !validateUniformMatrixParameters("uniformMatrix3x4fv", location, transpose, value.data(), value.size(), 12))
return;
webContext()->uniformMatrix3x4fv(location->location(), value.size() / 12, transpose, value.data());
}
void WebGL2RenderingContextBase::uniformMatrix4x3fv(const WebGLUniformLocation* location, GLboolean transpose, DOMFloat32Array* value)
{
if (isContextLost() || !validateUniformMatrixParameters("uniformMatrix4x3fv", location, transpose, value, 12))
return;
webContext()->uniformMatrix4x3fv(location->location(), value->length() / 12, transpose, value->data());
}
void WebGL2RenderingContextBase::uniformMatrix4x3fv(const WebGLUniformLocation* location, GLboolean transpose, Vector<GLfloat>& value)
{
if (isContextLost() || !validateUniformMatrixParameters("uniformMatrix4x3fv", location, transpose, value.data(), value.size(), 12))
return;
webContext()->uniformMatrix4x3fv(location->location(), value.size() / 12, transpose, value.data());
}
void WebGL2RenderingContextBase::vertexAttribI4i(GLuint index, GLint x, GLint y, GLint z, GLint w)
{
if (isContextLost())
return;
if (index >= m_maxVertexAttribs) {
synthesizeGLError(GL_INVALID_VALUE, "vertexAttribI4i", "index out of range");
return;
}
webContext()->vertexAttribI4i(index, x, y, z, w);
VertexAttribValue& attribValue = m_vertexAttribValue[index];
attribValue.type = Int32ArrayType;
attribValue.value.intValue[0] = x;
attribValue.value.intValue[1] = y;
attribValue.value.intValue[2] = z;
attribValue.value.intValue[3] = w;
}
void WebGL2RenderingContextBase::vertexAttribI4iv(GLuint index, const DOMInt32Array* value)
{
vertexAttribIivImpl("vertexAttribI4iv", index, value->data(), value->length());
}
void WebGL2RenderingContextBase::vertexAttribI4iv(GLuint index, const Vector<GLint>& value)
{
vertexAttribIivImpl("vertexAttribI4iv", index, value.data(), value.size());
}
void WebGL2RenderingContextBase::vertexAttribI4ui(GLuint index, GLuint x, GLuint y, GLuint z, GLuint w)
{
if (isContextLost())
return;
if (index >= m_maxVertexAttribs) {
synthesizeGLError(GL_INVALID_VALUE, "vertexAttribI4ui", "index out of range");
return;
}
webContext()->vertexAttribI4ui(index, x, y, z, w);
VertexAttribValue& attribValue = m_vertexAttribValue[index];
attribValue.type = Uint32ArrayType;
attribValue.value.uintValue[0] = x;
attribValue.value.uintValue[1] = y;
attribValue.value.uintValue[2] = z;
attribValue.value.uintValue[3] = w;
}
void WebGL2RenderingContextBase::vertexAttribI4uiv(GLuint index, const DOMUint32Array* value)
{
vertexAttribIuivImpl("vertexAttribI4uiv", index, value->data(), value->length());
}
void WebGL2RenderingContextBase::vertexAttribI4uiv(GLuint index, const Vector<GLuint>& value)
{
vertexAttribIuivImpl("vertexAttribI4uiv", index, value.data(), value.size());
}
void WebGL2RenderingContextBase::vertexAttribIivImpl(const char* functionName, GLuint index, const GLint* value, GLsizei size)
{
if (isContextLost())
return;
if (!value) {
synthesizeGLError(GL_INVALID_VALUE, functionName, "no array");
return;
}
if (size < 4) {
synthesizeGLError(GL_INVALID_VALUE, functionName, "invalid size");
return;
}
if (index >= m_maxVertexAttribs) {
synthesizeGLError(GL_INVALID_VALUE, functionName, "index out of range");
return;
}
webContext()->vertexAttribI4iv(index, value);
VertexAttribValue& attribValue = m_vertexAttribValue[index];
attribValue.type = Int32ArrayType;
attribValue.value.intValue[0] = value[0];
attribValue.value.intValue[1] = value[1];
attribValue.value.intValue[2] = value[2];
attribValue.value.intValue[3] = value[3];
}
void WebGL2RenderingContextBase::vertexAttribIuivImpl(const char* functionName, GLuint index, const GLuint* value, GLsizei size)
{
if (isContextLost())
return;
if (!value) {
synthesizeGLError(GL_INVALID_VALUE, functionName, "no array");
return;
}
if (size < 4) {
synthesizeGLError(GL_INVALID_VALUE, functionName, "invalid size");
return;
}
if (index >= m_maxVertexAttribs) {
synthesizeGLError(GL_INVALID_VALUE, functionName, "index out of range");
return;
}
webContext()->vertexAttribI4uiv(index, value);
VertexAttribValue& attribValue = m_vertexAttribValue[index];
attribValue.type = Uint32ArrayType;
attribValue.value.uintValue[0] = value[0];
attribValue.value.uintValue[1] = value[1];
attribValue.value.uintValue[2] = value[2];
attribValue.value.uintValue[3] = value[3];
}
void WebGL2RenderingContextBase::vertexAttribIPointer(GLuint index, GLint size, GLenum type, GLsizei stride, long long offset)
{
if (isContextLost())
return;
switch (type) {
case GL_BYTE:
case GL_UNSIGNED_BYTE:
case GL_SHORT:
case GL_UNSIGNED_SHORT:
case GL_INT:
case GL_UNSIGNED_INT:
break;
default:
synthesizeGLError(GL_INVALID_ENUM, "vertexAttribIPointer", "invalid type");
return;
}
if (index >= m_maxVertexAttribs) {
synthesizeGLError(GL_INVALID_VALUE, "vertexAttribIPointer", "index out of range");
return;
}
if (size < 1 || size > 4 || stride < 0 || stride > 255) {
synthesizeGLError(GL_INVALID_VALUE, "vertexAttribIPointer", "bad size or stride");
return;
}
if (!validateValueFitNonNegInt32("vertexAttribIPointer", "offset", offset))
return;
if (!m_boundArrayBuffer) {
synthesizeGLError(GL_INVALID_OPERATION, "vertexAttribIPointer", "no bound ARRAY_BUFFER");
return;
}
unsigned typeSize = sizeInBytes(type);
ASSERT((typeSize & (typeSize - 1)) == 0); // Ensure that the value is POT.
if ((stride & (typeSize - 1)) || (static_cast<GLintptr>(offset) & (typeSize - 1))) {
synthesizeGLError(GL_INVALID_OPERATION, "vertexAttribIPointer", "stride or offset not valid for type");
return;
}
GLsizei bytesPerElement = size * typeSize;
m_boundVertexArrayObject->setVertexAttribState(index, bytesPerElement, size, type, false, stride, static_cast<GLintptr>(offset), m_boundArrayBuffer);
webContext()->vertexAttribIPointer(index, size, type, stride, static_cast<GLintptr>(offset));
}
/* Writing to the drawing buffer */
void WebGL2RenderingContextBase::vertexAttribDivisor(GLuint index, GLuint divisor)
{
if (isContextLost())
return;
if (index >= m_maxVertexAttribs) {
synthesizeGLError(GL_INVALID_VALUE, "vertexAttribDivisor", "index out of range");
return;
}
m_boundVertexArrayObject->setVertexAttribDivisor(index, divisor);
webContext()->vertexAttribDivisorANGLE(index, divisor);
}
void WebGL2RenderingContextBase::drawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsizei instanceCount)
{
if (!validateDrawArrays("drawArraysInstanced", mode, first, count))
return;
if (!validateDrawInstanced("drawArraysInstanced", instanceCount))
return;
clearIfComposited();
handleTextureCompleteness("drawArraysInstanced", true);
webContext()->drawArraysInstancedANGLE(mode, first, count, instanceCount);
handleTextureCompleteness("drawArraysInstanced", false);
markContextChanged(CanvasChanged);
}
void WebGL2RenderingContextBase::drawElementsInstanced(GLenum mode, GLsizei count, GLenum type, long long offset, GLsizei instanceCount)
{
if (!validateDrawElements("drawElementsInstanced", mode, count, type, offset))
return;
if (!validateDrawInstanced("drawElementsInstanced", instanceCount))
return;
clearIfComposited();
handleTextureCompleteness("drawElementsInstanced", true);
webContext()->drawElementsInstancedANGLE(mode, count, type, static_cast<GLintptr>(offset), instanceCount);
handleTextureCompleteness("drawElementsInstanced", false);
markContextChanged(CanvasChanged);
}
void WebGL2RenderingContextBase::drawRangeElements(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, long long offset)
{
if (!validateDrawElements("drawRangeElements", mode, count, type, offset))
return;
clearIfComposited();
handleTextureCompleteness("drawRangeElements", true);
webContext()->drawRangeElements(mode, start, end, count, type, static_cast<GLintptr>(offset));
handleTextureCompleteness("drawRangeElements", false);
markContextChanged(CanvasChanged);
}
void WebGL2RenderingContextBase::drawBuffers(const Vector<GLenum>& buffers)
{
if (isContextLost())
return;
GLsizei n = buffers.size();
const GLenum* bufs = buffers.data();
if (!m_framebufferBinding) {
if (n != 1) {
synthesizeGLError(GL_INVALID_VALUE, "drawBuffers", "more than one buffer");
return;
}
if (bufs[0] != GL_BACK && bufs[0] != GL_NONE) {
synthesizeGLError(GL_INVALID_OPERATION, "drawBuffers", "BACK or NONE");
return;
}
// Because the backbuffer is simulated on all current WebKit ports, we need to change BACK to COLOR_ATTACHMENT0.
GLenum value = (bufs[0] == GL_BACK) ? GL_COLOR_ATTACHMENT0 : GL_NONE;
webContext()->drawBuffersEXT(1, &value);
setBackDrawBuffer(bufs[0]);
} else {
if (n > maxDrawBuffers()) {
synthesizeGLError(GL_INVALID_VALUE, "drawBuffers", "more than max draw buffers");
return;
}
for (GLsizei i = 0; i < n; ++i) {
if (bufs[i] != GL_NONE && bufs[i] != static_cast<GLenum>(GL_COLOR_ATTACHMENT0_EXT + i)) {
synthesizeGLError(GL_INVALID_OPERATION, "drawBuffers", "COLOR_ATTACHMENTi_EXT or NONE");
return;
}
}
m_framebufferBinding->drawBuffers(buffers);
}
}
bool WebGL2RenderingContextBase::validateClearBuffer(const char* functionName, GLenum buffer, GLsizei size)
{
switch (buffer) {
case GL_COLOR:
case GL_FRONT:
case GL_BACK:
case GL_FRONT_AND_BACK:
if (size < 4) {
synthesizeGLError(GL_INVALID_VALUE, functionName, "invalid array size");
return false;
}
break;
case GL_DEPTH:
case GL_STENCIL:
if (size < 1) {
synthesizeGLError(GL_INVALID_VALUE, functionName, "invalid array size");
return false;
}
break;
default:
synthesizeGLError(GL_INVALID_ENUM, functionName, "invalid buffer");
return false;
}
return true;
}
void WebGL2RenderingContextBase::clearBufferiv(GLenum buffer, GLint drawbuffer, DOMInt32Array* value)
{
if (isContextLost() || !validateClearBuffer("clearBufferiv", buffer, value->length()))
return;
webContext()->clearBufferiv(buffer, drawbuffer, value->data());
}
void WebGL2RenderingContextBase::clearBufferiv(GLenum buffer, GLint drawbuffer, const Vector<GLint>& value)
{
if (isContextLost() || !validateClearBuffer("clearBufferiv", buffer, value.size()))
return;
webContext()->clearBufferiv(buffer, drawbuffer, value.data());
}
void WebGL2RenderingContextBase::clearBufferuiv(GLenum buffer, GLint drawbuffer, DOMUint32Array* value)
{
if (isContextLost() || !validateClearBuffer("clearBufferuiv", buffer, value->length()))
return;
webContext()->clearBufferuiv(buffer, drawbuffer, value->data());
}
void WebGL2RenderingContextBase::clearBufferuiv(GLenum buffer, GLint drawbuffer, const Vector<GLuint>& value)
{
if (isContextLost() || !validateClearBuffer("clearBufferuiv", buffer, value.size()))
return;
webContext()->clearBufferuiv(buffer, drawbuffer, value.data());
}
void WebGL2RenderingContextBase::clearBufferfv(GLenum buffer, GLint drawbuffer, DOMFloat32Array* value)
{
if (isContextLost() || !validateClearBuffer("clearBufferfv", buffer, value->length()))
return;
webContext()->clearBufferfv(buffer, drawbuffer, value->data());
}
void WebGL2RenderingContextBase::clearBufferfv(GLenum buffer, GLint drawbuffer, const Vector<GLfloat>& value)
{
if (isContextLost() || !validateClearBuffer("clearBufferfv", buffer, value.size()))
return;
webContext()->clearBufferfv(buffer, drawbuffer, value.data());
}
void WebGL2RenderingContextBase::clearBufferfi(GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil)
{
if (isContextLost())
return;
webContext()->clearBufferfi(buffer, drawbuffer, depth, stencil);
}
WebGLQuery* WebGL2RenderingContextBase::createQuery()
{
if (isContextLost())
return nullptr;
WebGLQuery* o = WebGLQuery::create(this);
addSharedObject(o);
return o;
}
void WebGL2RenderingContextBase::deleteQuery(WebGLQuery* query)
{
if (m_currentBooleanOcclusionQuery == query) {
webContext()->endQueryEXT(m_currentBooleanOcclusionQuery->getTarget());
m_currentBooleanOcclusionQuery = nullptr;
}
if (m_currentTransformFeedbackPrimitivesWrittenQuery == query) {
webContext()->endQueryEXT(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN);
m_currentTransformFeedbackPrimitivesWrittenQuery = nullptr;
}
deleteObject(query);
}
GLboolean WebGL2RenderingContextBase::isQuery(WebGLQuery* query)
{
if (isContextLost() || !query)
return 0;
return webContext()->isQueryEXT(query->object());
}
void WebGL2RenderingContextBase::beginQuery(GLenum target, WebGLQuery* query)
{
bool deleted;
if (!checkObjectToBeBound("beginQuery", query, deleted))
return;
if (deleted) {
synthesizeGLError(GL_INVALID_OPERATION, "beginQuery", "attempted to begin a deleted query object");
return;
}
if (query->getTarget() && query->getTarget() != target) {
synthesizeGLError(GL_INVALID_OPERATION, "beginQuery", "query type does not match target");
return;
}
switch (target) {
case GL_ANY_SAMPLES_PASSED:
case GL_ANY_SAMPLES_PASSED_CONSERVATIVE:
{
if (m_currentBooleanOcclusionQuery) {
synthesizeGLError(GL_INVALID_OPERATION, "beginQuery", "a query is already active for target");
return;
}
m_currentBooleanOcclusionQuery = query;
}
break;
case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN:
{
if (m_currentTransformFeedbackPrimitivesWrittenQuery) {
synthesizeGLError(GL_INVALID_OPERATION, "beginQuery", "a query is already active for target");
return;
}
m_currentTransformFeedbackPrimitivesWrittenQuery = query;
}
break;
default:
synthesizeGLError(GL_INVALID_ENUM, "beginQuery", "invalid target");
return;
}
if (!query->getTarget())
query->setTarget(target);
webContext()->beginQueryEXT(target, query->object());
}
void WebGL2RenderingContextBase::endQuery(GLenum target)
{
if (isContextLost())
return;
switch (target) {
case GL_ANY_SAMPLES_PASSED:
case GL_ANY_SAMPLES_PASSED_CONSERVATIVE:
{
if (m_currentBooleanOcclusionQuery && m_currentBooleanOcclusionQuery->getTarget() == target) {
m_currentBooleanOcclusionQuery->resetCachedResult();
m_currentBooleanOcclusionQuery = nullptr;
} else {
synthesizeGLError(GL_INVALID_OPERATION, "endQuery", "target query is not active");
return;
}
}
break;
case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN:
{
if (m_currentTransformFeedbackPrimitivesWrittenQuery) {
m_currentTransformFeedbackPrimitivesWrittenQuery->resetCachedResult();
m_currentTransformFeedbackPrimitivesWrittenQuery = nullptr;
} else {
synthesizeGLError(GL_INVALID_OPERATION, "endQuery", "target query is not active");
return;
}
}
break;
default:
synthesizeGLError(GL_INVALID_ENUM, "endQuery", "invalid target");
return;
}
webContext()->endQueryEXT(target);
}
WebGLQuery* WebGL2RenderingContextBase::getQuery(GLenum target, GLenum pname)
{
if (isContextLost())
return nullptr;
if (pname != GL_CURRENT_QUERY) {
synthesizeGLError(GL_INVALID_ENUM, "getQuery", "invalid parameter name");
return nullptr;
}
switch (target) {
case GL_ANY_SAMPLES_PASSED:
case GL_ANY_SAMPLES_PASSED_CONSERVATIVE:
if (m_currentBooleanOcclusionQuery && m_currentBooleanOcclusionQuery->getTarget() == target)
return m_currentBooleanOcclusionQuery;
break;
case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN:
return m_currentTransformFeedbackPrimitivesWrittenQuery;
default:
synthesizeGLError(GL_INVALID_ENUM, "getQuery", "invalid target");
return nullptr;
}
return nullptr;
}
ScriptValue WebGL2RenderingContextBase::getQueryParameter(ScriptState* scriptState, WebGLQuery* query, GLenum pname)
{
if (isContextLost() || !validateWebGLObject("getQueryParameter", query))
return ScriptValue::createNull(scriptState);
// Query is non-null at this point.
if (query == m_currentBooleanOcclusionQuery || query == m_currentTransformFeedbackPrimitivesWrittenQuery) {
synthesizeGLError(GL_INVALID_OPERATION, "getQueryParameter", "query is currently active");
return ScriptValue::createNull(scriptState);
}
switch (pname) {
case GL_QUERY_RESULT:
{
query->updateCachedResult(webContext());
return WebGLAny(scriptState, query->getQueryResult());
}
case GL_QUERY_RESULT_AVAILABLE:
{
query->updateCachedResult(webContext());
return WebGLAny(scriptState, query->isQueryResultAvailable());
}
default:
synthesizeGLError(GL_INVALID_ENUM, "getQueryParameter", "invalid parameter name");
return ScriptValue::createNull(scriptState);
}
}
WebGLSampler* WebGL2RenderingContextBase::createSampler()
{
if (isContextLost())
return nullptr;
WebGLSampler* o = WebGLSampler::create(this);
addSharedObject(o);
return o;
}
void WebGL2RenderingContextBase::deleteSampler(WebGLSampler* sampler)
{
if (isContextLost())
return;
for (size_t i = 0; i < m_samplerUnits.size(); ++i) {
if (sampler == m_samplerUnits[i]) {
m_samplerUnits[i] = nullptr;
webContext()->bindSampler(i, 0);
}
}
deleteObject(sampler);
}
GLboolean WebGL2RenderingContextBase::isSampler(WebGLSampler* sampler)
{
if (isContextLost() || !sampler)
return 0;
return webContext()->isSampler(sampler->object());
}
void WebGL2RenderingContextBase::bindSampler(GLuint unit, WebGLSampler* sampler)
{
if (isContextLost())
return;
bool deleted;
if (!checkObjectToBeBound("bindSampler", sampler, deleted))
return;
if (deleted) {
synthesizeGLError(GL_INVALID_OPERATION, "bindSampler", "attempted to bind a deleted sampler");
return;
}
if (unit >= m_samplerUnits.size()) {
synthesizeGLError(GL_INVALID_VALUE, "bindSampler", "texture unit out of range");
return;
}
m_samplerUnits[unit] = sampler;
webContext()->bindSampler(unit, objectOrZero(sampler));
}
void WebGL2RenderingContextBase::samplerParameter(WebGLSampler* sampler, GLenum pname, GLfloat paramf, GLint parami, bool isFloat)
{
if (isContextLost() || !validateWebGLObject("samplerParameter", sampler))
return;
GLint param = isFloat ? static_cast<GLint>(paramf) : parami;
switch (pname) {
case GL_TEXTURE_MAX_LOD:
case GL_TEXTURE_MIN_LOD:
break;
case GL_TEXTURE_COMPARE_FUNC:
switch (param) {
case GL_LEQUAL:
case GL_GEQUAL:
case GL_LESS:
case GL_GREATER:
case GL_EQUAL:
case GL_NOTEQUAL:
case GL_ALWAYS:
case GL_NEVER:
break;
default:
synthesizeGLError(GL_INVALID_ENUM, "samplerParameter", "invalid parameter");
return;
}
break;
case GL_TEXTURE_COMPARE_MODE:
switch (param) {
case GL_COMPARE_REF_TO_TEXTURE:
case GL_NONE:
break;
default:
synthesizeGLError(GL_INVALID_ENUM, "samplerParameter", "invalid parameter");
return;
}
break;
case GL_TEXTURE_MAG_FILTER:
switch (param) {
case GL_NEAREST:
case GL_LINEAR:
break;
default:
synthesizeGLError(GL_INVALID_ENUM, "samplerParameter", "invalid parameter");
return;
}
break;
case GL_TEXTURE_MIN_FILTER:
switch (param) {
case GL_NEAREST:
case GL_LINEAR:
case GL_NEAREST_MIPMAP_NEAREST:
case GL_LINEAR_MIPMAP_NEAREST:
case GL_NEAREST_MIPMAP_LINEAR:
case GL_LINEAR_MIPMAP_LINEAR:
break;
default:
synthesizeGLError(GL_INVALID_ENUM, "samplerParameter", "invalid parameter");
return;
}
break;
case GL_TEXTURE_WRAP_R:
case GL_TEXTURE_WRAP_S:
case GL_TEXTURE_WRAP_T:
switch (param) {
case GL_CLAMP_TO_EDGE:
case GL_MIRRORED_REPEAT:
case GL_REPEAT:
break;
default:
synthesizeGLError(GL_INVALID_ENUM, "samplerParameter", "invalid parameter");
return;
}
break;
default:
synthesizeGLError(GL_INVALID_ENUM, "samplerParameter", "invalid parameter name");
return;
}
if (isFloat) {
sampler->setParameterf(pname, paramf);
webContext()->samplerParameterf(objectOrZero(sampler), pname, paramf);
} else {
sampler->setParameteri(pname, parami);
webContext()->samplerParameteri(objectOrZero(sampler), pname, parami);
}
}
void WebGL2RenderingContextBase::samplerParameteri(WebGLSampler* sampler, GLenum pname, GLint param)
{
samplerParameter(sampler, pname, 0, param, false);
}
void WebGL2RenderingContextBase::samplerParameterf(WebGLSampler* sampler, GLenum pname, GLfloat param)
{
samplerParameter(sampler, pname, param, 0, true);
}
ScriptValue WebGL2RenderingContextBase::getSamplerParameter(ScriptState* scriptState, WebGLSampler* sampler, GLenum pname)
{
if (isContextLost() || !validateWebGLObject("getSamplerParameter", sampler))
return ScriptValue::createNull(scriptState);
switch (pname) {
case GL_TEXTURE_COMPARE_FUNC:
case GL_TEXTURE_COMPARE_MODE:
case GL_TEXTURE_MAG_FILTER:
case GL_TEXTURE_MIN_FILTER:
case GL_TEXTURE_WRAP_R:
case GL_TEXTURE_WRAP_S:
case GL_TEXTURE_WRAP_T:
{
GLint value = 0;
webContext()->getSamplerParameteriv(objectOrZero(sampler), pname, &value);
return WebGLAny(scriptState, static_cast<unsigned>(value));
}
case GL_TEXTURE_MAX_LOD:
case GL_TEXTURE_MIN_LOD:
{
GLfloat value = 0.f;
webContext()->getSamplerParameterfv(objectOrZero(sampler), pname, &value);
return WebGLAny(scriptState, value);
}
default:
synthesizeGLError(GL_INVALID_ENUM, "getSamplerParameter", "invalid parameter name");
return ScriptValue::createNull(scriptState);
}
}
WebGLSync* WebGL2RenderingContextBase::fenceSync(GLenum condition, GLbitfield flags)
{
if (isContextLost())
return nullptr;
WebGLSync* o = WebGLFenceSync::create(this, condition, flags);
addSharedObject(o);
return o;
}
GLboolean WebGL2RenderingContextBase::isSync(WebGLSync* sync)
{
if (isContextLost() || !sync)
return 0;
return webContext()->isSync(sync->object());
}
void WebGL2RenderingContextBase::deleteSync(WebGLSync* sync)
{
deleteObject(sync);
}
GLenum WebGL2RenderingContextBase::clientWaitSync(WebGLSync* sync, GLbitfield flags, GLint64 timeout)
{
if (isContextLost() || !validateWebGLObject("clientWaitSync", sync))
return GL_WAIT_FAILED;
if (timeout < -1) {
synthesizeGLError(GL_INVALID_VALUE, "clientWaitSync", "timeout < -1");
return GL_WAIT_FAILED;
}
GLuint64 timeout64 = timeout == -1 ? GL_TIMEOUT_IGNORED : static_cast<GLuint64>(timeout);
return webContext()->clientWaitSync(syncObjectOrZero(sync), flags, timeout64);
}
void WebGL2RenderingContextBase::waitSync(WebGLSync* sync, GLbitfield flags, GLint64 timeout)
{
if (isContextLost() || !validateWebGLObject("waitSync", sync))
return;
if (timeout < -1) {
synthesizeGLError(GL_INVALID_VALUE, "waitSync", "timeout < -1");
return;
}
GLuint64 timeout64 = timeout == -1 ? GL_TIMEOUT_IGNORED : static_cast<GLuint64>(timeout);
webContext()->waitSync(syncObjectOrZero(sync), flags, timeout64);
}
ScriptValue WebGL2RenderingContextBase::getSyncParameter(ScriptState* scriptState, WebGLSync* sync, GLenum pname)
{
if (isContextLost() || !validateWebGLObject("getSyncParameter", sync))
return ScriptValue::createNull(scriptState);
switch (pname) {
case GL_OBJECT_TYPE:
case GL_SYNC_STATUS:
case GL_SYNC_CONDITION:
case GL_SYNC_FLAGS:
{
GLint value = 0;
GLsizei length = -1;
webContext()->getSynciv(syncObjectOrZero(sync), pname, 1, &length, &value);
return WebGLAny(scriptState, static_cast<unsigned>(value));
}
default:
synthesizeGLError(GL_INVALID_ENUM, "getSyncParameter", "invalid parameter name");
return ScriptValue::createNull(scriptState);
}
}
WebGLTransformFeedback* WebGL2RenderingContextBase::createTransformFeedback()
{
if (isContextLost())
return nullptr;
WebGLTransformFeedback* o = WebGLTransformFeedback::create(this);
addSharedObject(o);
return o;
}
void WebGL2RenderingContextBase::deleteTransformFeedback(WebGLTransformFeedback* feedback)
{
if (feedback == m_transformFeedbackBinding)
m_transformFeedbackBinding = nullptr;
deleteObject(feedback);
}
GLboolean WebGL2RenderingContextBase::isTransformFeedback(WebGLTransformFeedback* feedback)
{
if (isContextLost() || !feedback)
return 0;
if (!feedback->hasEverBeenBound())
return 0;
return webContext()->isTransformFeedback(feedback->object());
}
void WebGL2RenderingContextBase::bindTransformFeedback(GLenum target, WebGLTransformFeedback* feedback)
{
bool deleted;
if (!checkObjectToBeBound("bindTransformFeedback", feedback, deleted))
return;
if (deleted) {
synthesizeGLError(GL_INVALID_OPERATION, "bindTransformFeedback", "attempted to bind a deleted transform feedback object");
return;
}
if (target != GL_TRANSFORM_FEEDBACK) {
synthesizeGLError(GL_INVALID_ENUM, "bindTransformFeedback", "target must be TRANSFORM_FEEDBACK");
return;
}
m_transformFeedbackBinding = feedback;
webContext()->bindTransformFeedback(target, objectOrZero(feedback));
if (feedback)
feedback->setTarget(target);
}
void WebGL2RenderingContextBase::beginTransformFeedback(GLenum primitiveMode)
{
if (isContextLost())
return;
webContext()->beginTransformFeedback(primitiveMode);
}
void WebGL2RenderingContextBase::endTransformFeedback()
{
if (isContextLost())
return;
webContext()->endTransformFeedback();
}
void WebGL2RenderingContextBase::transformFeedbackVaryings(WebGLProgram* program, const Vector<String>& varyings, GLenum bufferMode)
{
if (isContextLost() || !validateWebGLObject("transformFeedbackVaryings", program))
return;
Vector<CString> keepAlive; // Must keep these instances alive while looking at their data
Vector<const char*> varyingStrings;
for (size_t i = 0; i < varyings.size(); ++i) {
keepAlive.append(varyings[i].ascii());
varyingStrings.append(keepAlive.last().data());
}
webContext()->transformFeedbackVaryings(objectOrZero(program), varyings.size(), varyingStrings.data(), bufferMode);
}
WebGLActiveInfo* WebGL2RenderingContextBase::getTransformFeedbackVarying(WebGLProgram* program, GLuint index)
{
if (isContextLost() || !validateWebGLObject("getTransformFeedbackVarying", program))
return nullptr;
GLint maxNameLength = -1;
webContext()->getProgramiv(objectOrZero(program), GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH, &maxNameLength);
if (maxNameLength <= 0) {
return nullptr;
}
OwnPtr<GLchar[]> name = adoptArrayPtr(new GLchar[maxNameLength]);
GLsizei length = 0;
GLsizei size = 0;
GLenum type = 0;
webContext()->getTransformFeedbackVarying(objectOrZero(program), index, maxNameLength, &length, &size, &type, name.get());
if (length == 0 || size == 0 || type == 0) {
return nullptr;
}
return WebGLActiveInfo::create(String(name.get(), length), type, size);
}
void WebGL2RenderingContextBase::pauseTransformFeedback()
{
if (isContextLost())
return;
webContext()->pauseTransformFeedback();
}
void WebGL2RenderingContextBase::resumeTransformFeedback()
{
if (isContextLost())
return;
webContext()->resumeTransformFeedback();
}
void WebGL2RenderingContextBase::bindBufferBase(GLenum target, GLuint index, WebGLBuffer* buffer)
{
if (isContextLost())
return;
bool deleted;
if (!checkObjectToBeBound("bindBufferBase", buffer, deleted))
return;
if (deleted)
buffer = 0;
if (!validateAndUpdateBufferBindBaseTarget("bindBufferBase", target, index, buffer))
return;
webContext()->bindBufferBase(target, index, objectOrZero(buffer));
}
void WebGL2RenderingContextBase::bindBufferRange(GLenum target, GLuint index, WebGLBuffer* buffer, long long offset, long long size)
{
if (isContextLost())
return;
bool deleted;
if (!checkObjectToBeBound("bindBufferRange", buffer, deleted))
return;
if (deleted)
buffer = 0;
if (!validateValueFitNonNegInt32("bindBufferRange", "offset", offset)
|| !validateValueFitNonNegInt32("bindBufferRange", "size", size)) {
return;
}
if (!validateAndUpdateBufferBindBaseTarget("bindBufferRange", target, index, buffer))
return;
webContext()->bindBufferRange(target, index, objectOrZero(buffer), static_cast<GLintptr>(offset), static_cast<GLsizeiptr>(size));
}
ScriptValue WebGL2RenderingContextBase::getIndexedParameter(ScriptState* scriptState, GLenum target, GLuint index)
{
if (isContextLost())
return ScriptValue::createNull(scriptState);
switch (target) {
case GL_TRANSFORM_FEEDBACK_BUFFER_BINDING:
if (index >= m_boundIndexedTransformFeedbackBuffers.size()) {
synthesizeGLError(GL_INVALID_VALUE, "getIndexedParameter", "index out of range");
return ScriptValue::createNull(scriptState);
}
return WebGLAny(scriptState, m_boundIndexedTransformFeedbackBuffers[index].get());
case GL_UNIFORM_BUFFER_BINDING:
if (index >= m_boundIndexedUniformBuffers.size()) {
synthesizeGLError(GL_INVALID_VALUE, "getIndexedParameter", "index out of range");
return ScriptValue::createNull(scriptState);
}
return WebGLAny(scriptState, m_boundIndexedUniformBuffers[index].get());
case GL_TRANSFORM_FEEDBACK_BUFFER_SIZE:
case GL_TRANSFORM_FEEDBACK_BUFFER_START:
case GL_UNIFORM_BUFFER_SIZE:
case GL_UNIFORM_BUFFER_START:
{
GLint64 value = -1;
webContext()->getInteger64i_v(target, index, &value);
return WebGLAny(scriptState, value);
}
default:
synthesizeGLError(GL_INVALID_ENUM, "getIndexedParameter", "invalid parameter name");
return ScriptValue::createNull(scriptState);
}
}
Vector<GLuint> WebGL2RenderingContextBase::getUniformIndices(WebGLProgram* program, const Vector<String>& uniformNames)
{
Vector<GLuint> result;
if (isContextLost() || !validateWebGLObject("getUniformIndices", program))
return result;
Vector<CString> keepAlive; // Must keep these instances alive while looking at their data
Vector<const char*> uniformStrings;
for (size_t i = 0; i < uniformNames.size(); ++i) {
keepAlive.append(uniformNames[i].ascii());
uniformStrings.append(keepAlive.last().data());
}
result.resize(uniformNames.size());
webContext()->getUniformIndices(objectOrZero(program), uniformStrings.size(), uniformStrings.data(), result.data());
return result;
}
Vector<GLint> WebGL2RenderingContextBase::getActiveUniforms(WebGLProgram* program, const Vector<GLuint>& uniformIndices, GLenum pname)
{
Vector<GLint> result;
if (isContextLost() || !validateWebGLObject("getActiveUniforms", program))
return result;
if (pname == GL_UNIFORM_NAME_LENGTH) {
synthesizeGLError(GL_INVALID_ENUM, "getActiveUniforms", "invalid parameter name");
return result;
}
GLint activeUniforms = -1;
webContext()->getProgramiv(objectOrZero(program), GL_ACTIVE_UNIFORMS, &activeUniforms);
GLuint activeUniformsUnsigned = activeUniforms;
for (size_t i = 0; i < uniformIndices.size(); ++i) {
if (uniformIndices[i] >= activeUniformsUnsigned) {
synthesizeGLError(GL_INVALID_VALUE, "getActiveUniforms", "uniform index greater than ACTIVE_UNIFORMS");
return result;
}
}
result.resize(uniformIndices.size());
webContext()->getActiveUniformsiv(objectOrZero(program), uniformIndices.size(), uniformIndices.data(), pname, result.data());
return result;
}
GLuint WebGL2RenderingContextBase::getUniformBlockIndex(WebGLProgram* program, const String& uniformBlockName)
{
if (isContextLost() || !validateWebGLObject("getUniformBlockIndex", program))
return 0;
if (!validateString("getUniformBlockIndex", uniformBlockName))
return 0;
return webContext()->getUniformBlockIndex(objectOrZero(program), uniformBlockName.utf8().data());
}
ScriptValue WebGL2RenderingContextBase::getActiveUniformBlockParameter(ScriptState* scriptState, WebGLProgram* program, GLuint uniformBlockIndex, GLenum pname)
{
if (isContextLost() || !validateWebGLObject("getActiveUniformBlockParameter", program))
return ScriptValue::createNull(scriptState);
switch (pname) {
case GL_UNIFORM_BLOCK_BINDING:
case GL_UNIFORM_BLOCK_DATA_SIZE:
case GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS:
{
GLint intValue = 0;
webContext()->getActiveUniformBlockiv(objectOrZero(program), uniformBlockIndex, pname, &intValue);
return WebGLAny(scriptState, static_cast<unsigned>(intValue));
}
case GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES:
{
GLint uniformCount = 0;
webContext()->getActiveUniformBlockiv(objectOrZero(program), uniformBlockIndex, GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, &uniformCount);
Vector<GLint> indices(uniformCount);
webContext()->getActiveUniformBlockiv(objectOrZero(program), uniformBlockIndex, pname, indices.data());
return WebGLAny(scriptState, DOMUint32Array::create(reinterpret_cast<GLuint*>(indices.data()), indices.size()));
}
case GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER:
case GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER:
{
GLint boolValue = 0;
webContext()->getActiveUniformBlockiv(objectOrZero(program), uniformBlockIndex, pname, &boolValue);
return WebGLAny(scriptState, static_cast<bool>(boolValue));
}
default:
synthesizeGLError(GL_INVALID_ENUM, "getActiveUniformBlockParameter", "invalid parameter name");
return ScriptValue::createNull(scriptState);
}
}
String WebGL2RenderingContextBase::getActiveUniformBlockName(WebGLProgram* program, GLuint uniformBlockIndex)
{
if (isContextLost() || !validateWebGLObject("getActiveUniformBlockName", program))
return String();
GLint maxNameLength = -1;
webContext()->getProgramiv(objectOrZero(program), GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH, &maxNameLength);
if (maxNameLength <= 0) {
// This state indicates that there are no active uniform blocks
synthesizeGLError(GL_INVALID_VALUE, "getActiveUniformBlockName", "invalid uniform block index");
return String();
}
OwnPtr<GLchar[]> name = adoptArrayPtr(new GLchar[maxNameLength]);
GLsizei length;
webContext()->getActiveUniformBlockName(objectOrZero(program), uniformBlockIndex, maxNameLength, &length, name.get());
return String(name.get(), length);
}
void WebGL2RenderingContextBase::uniformBlockBinding(WebGLProgram* program, GLuint uniformBlockIndex, GLuint uniformBlockBinding)
{
if (isContextLost() || !validateWebGLObject("uniformBlockBinding", program))
return;
webContext()->uniformBlockBinding(objectOrZero(program), uniformBlockIndex, uniformBlockBinding);
}
WebGLVertexArrayObject* WebGL2RenderingContextBase::createVertexArray()
{
if (isContextLost())
return nullptr;
WebGLVertexArrayObject* o = WebGLVertexArrayObject::create(this, WebGLVertexArrayObjectBase::VaoTypeUser);
addContextObject(o);
return o;
}
void WebGL2RenderingContextBase::deleteVertexArray(ScriptState* scriptState, WebGLVertexArrayObject* vertexArray)
{
if (isContextLost() || !vertexArray)
return;
if (!vertexArray->isDefaultObject() && vertexArray == m_boundVertexArrayObject)
setBoundVertexArrayObject(scriptState, nullptr);
vertexArray->deleteObject(webContext());
}
GLboolean WebGL2RenderingContextBase::isVertexArray(WebGLVertexArrayObject* vertexArray)
{
if (isContextLost() || !vertexArray)
return 0;
if (!vertexArray->hasEverBeenBound())
return 0;
return webContext()->isVertexArrayOES(vertexArray->object());
}
void WebGL2RenderingContextBase::bindVertexArray(ScriptState* scriptState, WebGLVertexArrayObject* vertexArray)
{
if (isContextLost())
return;
if (vertexArray && (vertexArray->isDeleted() || !vertexArray->validate(0, this))) {
webContext()->synthesizeGLError(GL_INVALID_OPERATION);
return;
}
if (vertexArray && !vertexArray->isDefaultObject() && vertexArray->object()) {
webContext()->bindVertexArrayOES(objectOrZero(vertexArray));
vertexArray->setHasEverBeenBound();
setBoundVertexArrayObject(scriptState, vertexArray);
} else {
webContext()->bindVertexArrayOES(0);
setBoundVertexArrayObject(scriptState, nullptr);
}
}
void WebGL2RenderingContextBase::bindFramebuffer(ScriptState* scriptState, GLenum target, WebGLFramebuffer* buffer)
{
bool deleted;
if (!checkObjectToBeBound("bindFramebuffer", buffer, deleted))
return;
if (deleted)
buffer = 0;
switch (target) {
case GL_DRAW_FRAMEBUFFER:
break;
case GL_FRAMEBUFFER:
case GL_READ_FRAMEBUFFER:
m_readFramebufferBinding = buffer;
break;
default:
synthesizeGLError(GL_INVALID_ENUM, "bindFramebuffer", "invalid target");
return;
}
setFramebuffer(target, buffer);
if (scriptState)
preserveObjectWrapper(scriptState, this, "framebuffer", 0, buffer);
}
void WebGL2RenderingContextBase::deleteFramebuffer(WebGLFramebuffer* framebuffer)
{
if (!deleteObject(framebuffer))
return;
GLenum target = 0;
if (framebuffer == m_framebufferBinding) {
if (framebuffer == m_readFramebufferBinding) {
target = GL_FRAMEBUFFER;
m_framebufferBinding = nullptr;
m_readFramebufferBinding = nullptr;
} else {
target = GL_DRAW_FRAMEBUFFER;
m_framebufferBinding = nullptr;
}
} else if (framebuffer == m_readFramebufferBinding) {
target = GL_READ_FRAMEBUFFER;
m_readFramebufferBinding = nullptr;
}
if (target) {
drawingBuffer()->setFramebufferBinding(target, 0);
// Have to call drawingBuffer()->bind() here to bind back to internal fbo.
drawingBuffer()->bind(target);
}
}
ScriptValue WebGL2RenderingContextBase::getParameter(ScriptState* scriptState, GLenum pname)
{
if (isContextLost())
return ScriptValue::createNull(scriptState);
switch (pname) {
case GL_SHADING_LANGUAGE_VERSION:
return WebGLAny(scriptState, "WebGL GLSL ES 3.00 (" + String(webContext()->getString(GL_SHADING_LANGUAGE_VERSION)) + ")");
case GL_VERSION:
return WebGLAny(scriptState, "WebGL 2.0 (" + String(webContext()->getString(GL_VERSION)) + ")");
case GL_COPY_READ_BUFFER_BINDING:
return WebGLAny(scriptState, m_boundCopyReadBuffer.get());
case GL_COPY_WRITE_BUFFER_BINDING:
return WebGLAny(scriptState, m_boundCopyWriteBuffer.get());
case GL_DRAW_FRAMEBUFFER_BINDING:
return WebGLAny(scriptState, m_framebufferBinding.get());
case GL_FRAGMENT_SHADER_DERIVATIVE_HINT:
return getUnsignedIntParameter(scriptState, pname);
case GL_MAX_3D_TEXTURE_SIZE:
return getIntParameter(scriptState, pname);
case GL_MAX_ARRAY_TEXTURE_LAYERS:
return getIntParameter(scriptState, pname);
case GC3D_MAX_CLIENT_WAIT_TIMEOUT_WEBGL:
return WebGLAny(scriptState, 0u);
case GL_MAX_COLOR_ATTACHMENTS:
return getIntParameter(scriptState, pname);
case GL_MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS:
return getInt64Parameter(scriptState, pname);
case GL_MAX_COMBINED_UNIFORM_BLOCKS:
return getIntParameter(scriptState, pname);
case GL_MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS:
return getInt64Parameter(scriptState, pname);
case GL_MAX_DRAW_BUFFERS:
return getIntParameter(scriptState, pname);
case GL_MAX_ELEMENT_INDEX:
return getInt64Parameter(scriptState, pname);
case GL_MAX_ELEMENTS_INDICES:
return getIntParameter(scriptState, pname);
case GL_MAX_ELEMENTS_VERTICES:
return getIntParameter(scriptState, pname);
case GL_MAX_FRAGMENT_INPUT_COMPONENTS:
return getIntParameter(scriptState, pname);
case GL_MAX_FRAGMENT_UNIFORM_BLOCKS:
return getIntParameter(scriptState, pname);
case GL_MAX_FRAGMENT_UNIFORM_COMPONENTS:
return getIntParameter(scriptState, pname);
case GL_MAX_PROGRAM_TEXEL_OFFSET:
return getIntParameter(scriptState, pname);
case GL_MAX_SAMPLES:
return getIntParameter(scriptState, pname);
case GL_MAX_SERVER_WAIT_TIMEOUT:
return getInt64Parameter(scriptState, pname);
case GL_MAX_TEXTURE_LOD_BIAS:
return getFloatParameter(scriptState, pname);
case GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS:
return getIntParameter(scriptState, pname);
case GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS:
return getIntParameter(scriptState, pname);
case GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS:
return getIntParameter(scriptState, pname);
case GL_MAX_UNIFORM_BLOCK_SIZE:
return getInt64Parameter(scriptState, pname);
case GL_MAX_UNIFORM_BUFFER_BINDINGS:
return getIntParameter(scriptState, pname);
case GL_MAX_VARYING_COMPONENTS:
return getIntParameter(scriptState, pname);
case GL_MAX_VERTEX_OUTPUT_COMPONENTS:
return getIntParameter(scriptState, pname);
case GL_MAX_VERTEX_UNIFORM_BLOCKS:
return getIntParameter(scriptState, pname);
case GL_MAX_VERTEX_UNIFORM_COMPONENTS:
return getIntParameter(scriptState, pname);
case GL_MIN_PROGRAM_TEXEL_OFFSET:
return getIntParameter(scriptState, pname);
case GL_PACK_ROW_LENGTH:
return getIntParameter(scriptState, pname);
case GL_PACK_SKIP_PIXELS:
return getIntParameter(scriptState, pname);
case GL_PACK_SKIP_ROWS:
return getIntParameter(scriptState, pname);
case GL_PIXEL_PACK_BUFFER_BINDING:
return WebGLAny(scriptState, m_boundPixelPackBuffer.get());
case GL_PIXEL_UNPACK_BUFFER_BINDING:
return WebGLAny(scriptState, m_boundPixelUnpackBuffer.get());
case GL_RASTERIZER_DISCARD:
return getBooleanParameter(scriptState, pname);
case GL_READ_BUFFER:
{
GLenum value = 0;
if (!isContextLost()) {
WebGLFramebuffer* readFramebufferBinding = getFramebufferBinding(GL_READ_FRAMEBUFFER);
if (!readFramebufferBinding)
value = m_readBufferOfDefaultFramebuffer;
else
value = readFramebufferBinding->getReadBuffer();
}
return WebGLAny(scriptState, value);
}
case GL_READ_FRAMEBUFFER_BINDING:
return WebGLAny(scriptState, m_readFramebufferBinding.get());
case GL_SAMPLE_ALPHA_TO_COVERAGE:
return getBooleanParameter(scriptState, pname);
case GL_SAMPLE_COVERAGE:
return getBooleanParameter(scriptState, pname);
case GL_SAMPLER_BINDING:
return WebGLAny(scriptState, m_samplerUnits[m_activeTextureUnit].get());
case GL_TEXTURE_BINDING_2D_ARRAY:
return WebGLAny(scriptState, m_textureUnits[m_activeTextureUnit].m_texture2DArrayBinding.get());
case GL_TEXTURE_BINDING_3D:
return WebGLAny(scriptState, m_textureUnits[m_activeTextureUnit].m_texture3DBinding.get());
case GL_TRANSFORM_FEEDBACK_ACTIVE:
return getBooleanParameter(scriptState, pname);
case GL_TRANSFORM_FEEDBACK_BUFFER_BINDING:
return WebGLAny(scriptState, m_boundTransformFeedbackBuffer.get());
case GL_TRANSFORM_FEEDBACK_BINDING:
return WebGLAny(scriptState, m_transformFeedbackBinding.get());
case GL_TRANSFORM_FEEDBACK_PAUSED:
return getBooleanParameter(scriptState, pname);
case GL_UNIFORM_BUFFER_BINDING:
return WebGLAny(scriptState, m_boundUniformBuffer.get());
case GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT:
return getIntParameter(scriptState, pname);
case GL_UNPACK_IMAGE_HEIGHT:
return getIntParameter(scriptState, pname);
case GL_UNPACK_ROW_LENGTH:
return getIntParameter(scriptState, pname);
case GL_UNPACK_SKIP_IMAGES:
return getBooleanParameter(scriptState, pname);
case GL_UNPACK_SKIP_PIXELS:
return getBooleanParameter(scriptState, pname);
case GL_UNPACK_SKIP_ROWS:
return getBooleanParameter(scriptState, pname);
default:
return WebGLRenderingContextBase::getParameter(scriptState, pname);
}
}
ScriptValue WebGL2RenderingContextBase::getInt64Parameter(ScriptState* scriptState, GLenum pname)
{
GLint64 value = 0;
if (!isContextLost())
webContext()->getInteger64v(pname, &value);
return WebGLAny(scriptState, value);
}
bool WebGL2RenderingContextBase::validateCapability(const char* functionName, GLenum cap)
{
switch (cap) {
case GL_RASTERIZER_DISCARD:
return true;
default:
return WebGLRenderingContextBase::validateCapability(functionName, cap);
}
}
bool WebGL2RenderingContextBase::isBufferBoundToTransformFeedback(WebGLBuffer* buffer)
{
ASSERT(buffer);
if (m_boundTransformFeedbackBuffer == buffer)
return true;
for (size_t i = 0; i < m_boundIndexedTransformFeedbackBuffers.size(); ++i) {
if (m_boundIndexedTransformFeedbackBuffers[i] == buffer)
return true;
}
return false;
}
bool WebGL2RenderingContextBase::isBufferBoundToNonTransformFeedback(WebGLBuffer* buffer)
{
ASSERT(buffer);
if (m_boundArrayBuffer == buffer
|| m_boundVertexArrayObject->boundElementArrayBuffer() == buffer
|| m_boundCopyReadBuffer == buffer
|| m_boundCopyWriteBuffer == buffer
|| m_boundPixelPackBuffer == buffer
|| m_boundPixelUnpackBuffer == buffer
|| m_boundUniformBuffer == buffer) {
return true;
}
for (size_t i = 0; i <= m_maxBoundUniformBufferIndex; ++i) {
if (m_boundIndexedUniformBuffers[i] == buffer)
return true;
}
return false;
}
bool WebGL2RenderingContextBase::validateBufferTargetCompatibility(const char* functionName, GLenum target, WebGLBuffer* buffer)
{
ASSERT(buffer);
switch (buffer->getInitialTarget()) {
case GL_ELEMENT_ARRAY_BUFFER:
switch (target) {
case GL_ARRAY_BUFFER:
case GL_PIXEL_PACK_BUFFER:
case GL_PIXEL_UNPACK_BUFFER:
case GL_TRANSFORM_FEEDBACK_BUFFER:
case GL_UNIFORM_BUFFER:
synthesizeGLError(GL_INVALID_OPERATION, functionName,
"element array buffers can not be bound to a different target");
return false;
default:
break;
}
break;
case GL_ARRAY_BUFFER:
case GL_COPY_READ_BUFFER:
case GL_COPY_WRITE_BUFFER:
case GL_PIXEL_PACK_BUFFER:
case GL_PIXEL_UNPACK_BUFFER:
case GL_UNIFORM_BUFFER:
case GL_TRANSFORM_FEEDBACK_BUFFER:
if (target == GL_ELEMENT_ARRAY_BUFFER) {
synthesizeGLError(GL_INVALID_OPERATION, functionName,
"buffers bound to non ELEMENT_ARRAY_BUFFER targets can not be bound to ELEMENT_ARRAY_BUFFER target");
return false;
}
break;
default:
break;
}
if (target == GL_TRANSFORM_FEEDBACK_BUFFER) {
if (isBufferBoundToNonTransformFeedback(buffer)) {
synthesizeGLError(GL_INVALID_OPERATION, functionName,
"a buffer bound to TRANSFORM_FEEDBACK_BUFFER can not be bound to any other targets");
return false;
}
} else if (isBufferBoundToTransformFeedback(buffer)) {
synthesizeGLError(GL_INVALID_OPERATION, functionName,
"a buffer bound to TRANSFORM_FEEDBACK_BUFFER can not be bound to any other targets");
return false;
}
return true;
}
bool WebGL2RenderingContextBase::validateBufferTarget(const char* functionName, GLenum target)
{
switch (target) {
case GL_ARRAY_BUFFER:
case GL_COPY_READ_BUFFER:
case GL_COPY_WRITE_BUFFER:
case GL_ELEMENT_ARRAY_BUFFER:
case GL_PIXEL_PACK_BUFFER:
case GL_PIXEL_UNPACK_BUFFER:
case GL_TRANSFORM_FEEDBACK_BUFFER:
case GL_UNIFORM_BUFFER:
return true;
default:
synthesizeGLError(GL_INVALID_ENUM, functionName, "invalid target");
return false;
}
}
bool WebGL2RenderingContextBase::validateAndUpdateBufferBindTarget(const char* functionName, GLenum target, WebGLBuffer* buffer)
{
if (!validateBufferTarget(functionName, target))
return false;
if (buffer && !validateBufferTargetCompatibility(functionName, target, buffer))
return false;
switch (target) {
case GL_ARRAY_BUFFER:
m_boundArrayBuffer = buffer;
break;
case GL_COPY_READ_BUFFER:
m_boundCopyReadBuffer = buffer;
break;
case GL_COPY_WRITE_BUFFER:
m_boundCopyWriteBuffer = buffer;
break;
case GL_ELEMENT_ARRAY_BUFFER:
m_boundVertexArrayObject->setElementArrayBuffer(buffer);
break;
case GL_PIXEL_PACK_BUFFER:
m_boundPixelPackBuffer = buffer;
break;
case GL_PIXEL_UNPACK_BUFFER:
m_boundPixelUnpackBuffer = buffer;
break;
case GL_TRANSFORM_FEEDBACK_BUFFER:
m_boundTransformFeedbackBuffer = buffer;
break;
case GL_UNIFORM_BUFFER:
m_boundUniformBuffer = buffer;
break;
default:
ASSERT_NOT_REACHED();
break;
}
if (buffer && !buffer->getInitialTarget())
buffer->setInitialTarget(target);
return true;
}
bool WebGL2RenderingContextBase::validateBufferBaseTarget(const char* functionName, GLenum target)
{
switch (target) {
case GL_TRANSFORM_FEEDBACK_BUFFER:
case GL_UNIFORM_BUFFER:
return true;
default:
synthesizeGLError(GL_INVALID_ENUM, functionName, "invalid target");
return false;
}
}
bool WebGL2RenderingContextBase::validateAndUpdateBufferBindBaseTarget(const char* functionName, GLenum target, GLuint index, WebGLBuffer* buffer)
{
if (!validateBufferBaseTarget(functionName, target))
return false;
if (buffer && !validateBufferTargetCompatibility(functionName, target, buffer))
return false;
switch (target) {
case GL_TRANSFORM_FEEDBACK_BUFFER:
if (index >= m_boundIndexedTransformFeedbackBuffers.size()) {
synthesizeGLError(GL_INVALID_OPERATION, functionName, "index out of range");
return false;
}
m_boundIndexedTransformFeedbackBuffers[index] = buffer;
m_boundTransformFeedbackBuffer = buffer;
break;
case GL_UNIFORM_BUFFER:
if (index >= m_boundIndexedUniformBuffers.size()) {
synthesizeGLError(GL_INVALID_OPERATION, functionName, "index out of range");
return false;
}
m_boundIndexedUniformBuffers[index] = buffer;
m_boundUniformBuffer = buffer;
// Keep track of what the maximum bound uniform buffer index is
if (buffer) {
if (index > m_maxBoundUniformBufferIndex)
m_maxBoundUniformBufferIndex = index;
} else if (m_maxBoundUniformBufferIndex > 0 && index == m_maxBoundUniformBufferIndex) {
size_t i = m_maxBoundUniformBufferIndex - 1;
for (; i > 0; --i) {
if (m_boundIndexedUniformBuffers[i].get())
break;
}
m_maxBoundUniformBufferIndex = i;
}
break;
default:
ASSERT_NOT_REACHED();
break;
}
if (buffer && !buffer->getInitialTarget())
buffer->setInitialTarget(target);
return true;
}
bool WebGL2RenderingContextBase::validateFramebufferTarget(GLenum target)
{
switch (target) {
case GL_FRAMEBUFFER:
case GL_READ_FRAMEBUFFER:
case GL_DRAW_FRAMEBUFFER:
return true;
default:
return false;
}
}
bool WebGL2RenderingContextBase::validateReadPixelsFormatAndType(GLenum format, GLenum type)
{
switch (format) {
case GL_RED:
case GL_RED_INTEGER:
case GL_RG:
case GL_RG_INTEGER:
case GL_RGB:
case GL_RGB_INTEGER:
case GL_RGBA:
case GL_RGBA_INTEGER:
case GL_LUMINANCE_ALPHA:
case GL_LUMINANCE:
case GL_ALPHA:
break;
default:
synthesizeGLError(GL_INVALID_ENUM, "readPixels", "invalid format");
return false;
}
switch (type) {
case GL_UNSIGNED_BYTE:
case GL_BYTE:
case GL_HALF_FLOAT:
case GL_FLOAT:
case GL_UNSIGNED_SHORT:
case GL_UNSIGNED_SHORT_5_6_5:
case GL_UNSIGNED_SHORT_4_4_4_4:
case GL_UNSIGNED_SHORT_5_5_5_1:
case GL_SHORT:
case GL_UNSIGNED_INT:
case GL_UNSIGNED_INT_2_10_10_10_REV:
case GL_UNSIGNED_INT_10F_11F_11F_REV:
case GL_UNSIGNED_INT_5_9_9_9_REV:
case GL_INT:
break;
default:
synthesizeGLError(GL_INVALID_ENUM, "readPixels", "invalid type");
return false;
}
return true;
}
DOMArrayBufferView::ViewType WebGL2RenderingContextBase::readPixelsExpectedArrayBufferViewType(GLenum type)
{
switch (type) {
case GL_BYTE:
return DOMArrayBufferView::TypeInt8;
case GL_UNSIGNED_SHORT:
return DOMArrayBufferView::TypeUint16;
case GL_SHORT:
return DOMArrayBufferView::TypeInt16;
case GL_HALF_FLOAT:
return DOMArrayBufferView::TypeUint16;
case GL_UNSIGNED_INT:
case GL_UNSIGNED_INT_2_10_10_10_REV:
case GL_UNSIGNED_INT_10F_11F_11F_REV:
case GL_UNSIGNED_INT_5_9_9_9_REV:
return DOMArrayBufferView::TypeUint32;
case GL_INT:
return DOMArrayBufferView::TypeInt32;
default:
return WebGLRenderingContextBase::readPixelsExpectedArrayBufferViewType(type);
}
}
WebGLFramebuffer* WebGL2RenderingContextBase::getFramebufferBinding(GLenum target)
{
if (target == GL_READ_FRAMEBUFFER)
return m_readFramebufferBinding.get();
return m_framebufferBinding.get();
}
bool WebGL2RenderingContextBase::validateGetFramebufferAttachmentParameterFunc(const char* functionName, GLenum target, GLenum attachment)
{
if (!validateFramebufferTarget(target)) {
synthesizeGLError(GL_INVALID_ENUM, functionName, "invalid target");
return false;
}
WebGLFramebuffer* framebufferBinding = getFramebufferBinding(target);
ASSERT(framebufferBinding || drawingBuffer());
if (!framebufferBinding) {
// for the default framebuffer
switch (attachment) {
case GL_BACK:
case GL_DEPTH:
case GL_STENCIL:
break;
default:
synthesizeGLError(GL_INVALID_ENUM, functionName, "invalid attachment");
return false;
}
} else {
// for the FBO
switch (attachment) {
case GL_COLOR_ATTACHMENT0:
case GL_DEPTH_ATTACHMENT:
case GL_STENCIL_ATTACHMENT:
break;
case GL_DEPTH_STENCIL_ATTACHMENT:
if (framebufferBinding->getAttachmentObject(GL_DEPTH_ATTACHMENT) != framebufferBinding->getAttachmentObject(GL_STENCIL_ATTACHMENT)) {
synthesizeGLError(GL_INVALID_OPERATION, functionName, "different objects are bound to the depth and stencil attachment points");
return false;
}
break;
default:
if (attachment > GL_COLOR_ATTACHMENT0
&& attachment < static_cast<GLenum>(GL_COLOR_ATTACHMENT0 + maxColorAttachments()))
break;
synthesizeGLError(GL_INVALID_ENUM, functionName, "invalid attachment");
return false;
}
}
return true;
}
ScriptValue WebGL2RenderingContextBase::getFramebufferAttachmentParameter(ScriptState* scriptState, GLenum target, GLenum attachment, GLenum pname)
{
const char kFunctionName[] = "getFramebufferAttachmentParameter";
if (isContextLost() || !validateGetFramebufferAttachmentParameterFunc(kFunctionName, target, attachment))
return ScriptValue::createNull(scriptState);
WebGLFramebuffer* framebufferBinding = getFramebufferBinding(target);
ASSERT(!framebufferBinding || framebufferBinding->object());
// Default framebuffer (an internal fbo)
if (!framebufferBinding) {
// We can use m_requestedAttribs because in WebGL 2, they are required to be honored.
bool hasDepth = m_requestedAttributes.depth();
bool hasStencil = m_requestedAttributes.stencil();
bool hasAlpha = m_requestedAttributes.alpha();
bool missingImage = (attachment == GL_DEPTH && !hasDepth)
|| (attachment == GL_STENCIL && !hasStencil);
if (missingImage) {
switch (pname) {
case GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE:
return WebGLAny(scriptState, GL_FRAMEBUFFER_DEFAULT);
default:
synthesizeGLError(GL_INVALID_ENUM, kFunctionName, "invalid parameter name");
return ScriptValue::createNull(scriptState);
}
}
switch (pname) {
case GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE:
return WebGLAny(scriptState, GL_FRAMEBUFFER_DEFAULT);
case GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE:
case GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE:
case GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE:
{
GLint value = attachment == GL_BACK ? 8 : 0;
return WebGLAny(scriptState, value);
}
case GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE:
{
GLint value = (attachment == GL_BACK && hasAlpha) ? 8 : 0;
return WebGLAny(scriptState, value);
}
case GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE:
{
// For ES3 capable backend, DEPTH24_STENCIL8 has to be supported.
GLint value = attachment == GL_DEPTH ? 24 : 0;
return WebGLAny(scriptState, value);
}
case GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE:
{
GLint value = attachment == GL_STENCIL ? 8 : 0;
return WebGLAny(scriptState, value);
}
case GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE:
return WebGLAny(scriptState, GL_UNSIGNED_NORMALIZED);
case GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING:
return WebGLAny(scriptState, GL_LINEAR);
default:
synthesizeGLError(GL_INVALID_ENUM, kFunctionName, "invalid parameter name");
return ScriptValue::createNull(scriptState);
}
}
WebGLSharedObject* attachmentObject = nullptr;
if (attachment == GL_DEPTH_STENCIL_ATTACHMENT) {
WebGLSharedObject* depthAttachment = framebufferBinding->getAttachmentObject(GL_DEPTH_ATTACHMENT);
WebGLSharedObject* stencilAttachment = framebufferBinding->getAttachmentObject(GL_STENCIL_ATTACHMENT);
if (depthAttachment != stencilAttachment) {
synthesizeGLError(GL_INVALID_OPERATION, kFunctionName,
"different objects bound to DEPTH_ATTACHMENT and STENCIL_ATTACHMENT");
return ScriptValue::createNull(scriptState);
}
attachmentObject = depthAttachment;
} else {
attachmentObject = framebufferBinding->getAttachmentObject(attachment);
}
if (!attachmentObject) {
switch (pname) {
case GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE:
return WebGLAny(scriptState, GL_NONE);
case GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME:
return ScriptValue::createNull(scriptState);
default:
synthesizeGLError(GL_INVALID_OPERATION, kFunctionName, "invalid parameter name");
return ScriptValue::createNull(scriptState);
}
}
ASSERT(attachmentObject->isTexture() || attachmentObject->isRenderbuffer());
switch (pname) {
case GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE:
if (attachmentObject->isTexture())
return WebGLAny(scriptState, GL_TEXTURE);
return WebGLAny(scriptState, GL_RENDERBUFFER);
case GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME:
return WebGLAny(scriptState, attachmentObject);
case GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE:
case GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER:
case GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL:
if (!attachmentObject->isTexture())
break;
case GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE:
case GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE:
case GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE:
case GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE:
case GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE:
case GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE:
{
GLint value = 0;
webContext()->getFramebufferAttachmentParameteriv(target, attachment, pname, &value);
return WebGLAny(scriptState, value);
}
case GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE:
if (attachment == GL_DEPTH_STENCIL_ATTACHMENT) {
synthesizeGLError(GL_INVALID_OPERATION, kFunctionName,
"COMPONENT_TYPE can't be queried for DEPTH_STENCIL_ATTACHMENT");
return ScriptValue::createNull(scriptState);
}
case GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING:
{
GLint value = 0;
webContext()->getFramebufferAttachmentParameteriv(target, attachment, pname, &value);
return WebGLAny(scriptState, static_cast<unsigned>(value));
}
default:
break;
}
synthesizeGLError(GL_INVALID_ENUM, kFunctionName, "invalid parameter name");
return ScriptValue::createNull(scriptState);
}
DEFINE_TRACE(WebGL2RenderingContextBase)
{
visitor->trace(m_readFramebufferBinding);
visitor->trace(m_transformFeedbackBinding);
visitor->trace(m_boundCopyReadBuffer);
visitor->trace(m_boundCopyWriteBuffer);
visitor->trace(m_boundPixelPackBuffer);
visitor->trace(m_boundPixelUnpackBuffer);
visitor->trace(m_boundTransformFeedbackBuffer);
visitor->trace(m_boundUniformBuffer);
visitor->trace(m_boundIndexedTransformFeedbackBuffers);
visitor->trace(m_boundIndexedUniformBuffers);
visitor->trace(m_currentBooleanOcclusionQuery);
visitor->trace(m_currentTransformFeedbackPrimitivesWrittenQuery);
visitor->trace(m_samplerUnits);
WebGLRenderingContextBase::trace(visitor);
}
WebGLTexture* WebGL2RenderingContextBase::validateTextureBinding(const char* functionName, GLenum target, bool useSixEnumsForCubeMap)
{
WebGLTexture* tex = nullptr;
switch (target) {
case GL_TEXTURE_2D_ARRAY:
tex = m_textureUnits[m_activeTextureUnit].m_texture2DArrayBinding.get();
if (!tex)
synthesizeGLError(GL_INVALID_OPERATION, functionName, "no texture bound to GL_TEXTURE_2D_ARRAY");
break;
case GL_TEXTURE_3D:
tex = m_textureUnits[m_activeTextureUnit].m_texture3DBinding.get();
if (!tex)
synthesizeGLError(GL_INVALID_OPERATION, functionName, "no texture bound to GL_TEXTURE_3D");
break;
default:
return WebGLRenderingContextBase::validateTextureBinding(functionName, target, useSixEnumsForCubeMap);
}
return tex;
}
GLint WebGL2RenderingContextBase::getMaxTextureLevelForTarget(GLenum target)
{
switch (target) {
case GL_TEXTURE_3D:
return m_max3DTextureLevel;
case GL_TEXTURE_2D_ARRAY:
return m_maxTextureLevel;
}
return WebGLRenderingContextBase::getMaxTextureLevelForTarget(target);
}
ScriptValue WebGL2RenderingContextBase::getTexParameter(ScriptState* scriptState, GLenum target, GLenum pname)
{
if (isContextLost() || !validateTextureBinding("getTexParameter", target, false))
return ScriptValue::createNull(scriptState);
switch (pname) {
case GL_TEXTURE_WRAP_R:
case GL_TEXTURE_COMPARE_FUNC:
case GL_TEXTURE_COMPARE_MODE:
case GL_TEXTURE_IMMUTABLE_LEVELS:
{
GLint value = 0;
webContext()->getTexParameteriv(target, pname, &value);
return WebGLAny(scriptState, static_cast<unsigned>(value));
}
case GL_TEXTURE_IMMUTABLE_FORMAT:
{
GLint value = 0;
webContext()->getTexParameteriv(target, pname, &value);
return WebGLAny(scriptState, static_cast<bool>(value));
}
case GL_TEXTURE_BASE_LEVEL:
case GL_TEXTURE_MAX_LEVEL:
{
GLint value = 0;
webContext()->getTexParameteriv(target, pname, &value);
return WebGLAny(scriptState, value);
}
case GL_TEXTURE_MAX_LOD:
case GL_TEXTURE_MIN_LOD:
{
GLfloat value = 0.f;
webContext()->getTexParameterfv(target, pname, &value);
return WebGLAny(scriptState, value);
}
default:
return WebGLRenderingContextBase::getTexParameter(scriptState, target, pname);
}
}
WebGLBuffer* WebGL2RenderingContextBase::validateBufferDataTarget(const char* functionName, GLenum target)
{
WebGLBuffer* buffer = nullptr;
switch (target) {
case GL_ELEMENT_ARRAY_BUFFER:
buffer = m_boundVertexArrayObject->boundElementArrayBuffer();
break;
case GL_ARRAY_BUFFER:
buffer = m_boundArrayBuffer.get();
break;
case GL_COPY_READ_BUFFER:
buffer = m_boundCopyReadBuffer.get();
break;
case GL_COPY_WRITE_BUFFER:
buffer = m_boundCopyWriteBuffer.get();
break;
case GL_PIXEL_PACK_BUFFER:
buffer = m_boundPixelPackBuffer.get();
break;
case GL_PIXEL_UNPACK_BUFFER:
buffer = m_boundPixelUnpackBuffer.get();
break;
case GL_TRANSFORM_FEEDBACK_BUFFER:
buffer = m_boundTransformFeedbackBuffer.get();
break;
case GL_UNIFORM_BUFFER:
buffer = m_boundUniformBuffer.get();
break;
default:
synthesizeGLError(GL_INVALID_ENUM, functionName, "invalid target");
return nullptr;
}
if (!buffer) {
synthesizeGLError(GL_INVALID_OPERATION, functionName, "no buffer");
return nullptr;
}
return buffer;
}
bool WebGL2RenderingContextBase::validateBufferDataUsage(const char* functionName, GLenum usage)
{
switch (usage) {
case GL_STREAM_READ:
case GL_STREAM_COPY:
case GL_STATIC_READ:
case GL_STATIC_COPY:
case GL_DYNAMIC_READ:
case GL_DYNAMIC_COPY:
return true;
default:
return WebGLRenderingContextBase::validateBufferDataUsage(functionName, usage);
}
}
void WebGL2RenderingContextBase::removeBoundBuffer(WebGLBuffer* buffer)
{
if (m_boundCopyReadBuffer == buffer)
m_boundCopyReadBuffer = nullptr;
if (m_boundCopyWriteBuffer == buffer)
m_boundCopyWriteBuffer = nullptr;
if (m_boundPixelPackBuffer == buffer)
m_boundPixelPackBuffer = nullptr;
if (m_boundPixelUnpackBuffer == buffer)
m_boundPixelUnpackBuffer = nullptr;
if (m_boundTransformFeedbackBuffer == buffer)
m_boundTransformFeedbackBuffer = nullptr;
if (m_boundUniformBuffer == buffer)
m_boundUniformBuffer = nullptr;
WebGLRenderingContextBase::removeBoundBuffer(buffer);
}
void WebGL2RenderingContextBase::restoreCurrentFramebuffer()
{
bindFramebuffer(nullptr, GL_DRAW_FRAMEBUFFER, m_framebufferBinding.get());
bindFramebuffer(nullptr, GL_READ_FRAMEBUFFER, m_readFramebufferBinding.get());
}
GLenum WebGL2RenderingContextBase::boundFramebufferColorFormat()
{
if (m_readFramebufferBinding && m_readFramebufferBinding->object())
return m_readFramebufferBinding->colorBufferFormat();
if (m_requestedAttributes.alpha())
return GL_RGBA;
return GL_RGB;
}
} // namespace blink