blob: f6533dfd37b882e6b53e096b86f629b57b1216a9 [file] [log] [blame]
// Copyright 2018 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 "third_party/blink/renderer/modules/webgl/webgl2_compute_rendering_context_base.h"
#include <algorithm>
#include <memory>
#include "third_party/blink/public/platform/web_graphics_context_3d_provider.h"
#include "third_party/blink/renderer/bindings/modules/v8/webgl_any.h"
#include "third_party/blink/renderer/modules/webgl/webgl_program.h"
#include "third_party/blink/renderer/modules/webgl/webgl_uniform_location.h"
#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
namespace blink {
WebGL2ComputeRenderingContextBase::WebGL2ComputeRenderingContextBase(
CanvasRenderingContextHost* host,
std::unique_ptr<WebGraphicsContext3DProvider> context_provider,
bool using_gpu_compositing,
const CanvasContextCreationAttributesCore& requested_attributes)
: WebGL2RenderingContextBase(host,
std::move(context_provider),
using_gpu_compositing,
requested_attributes,
Platform::kWebGL2ComputeContextType) {}
void WebGL2ComputeRenderingContextBase::DestroyContext() {
WebGL2RenderingContextBase::DestroyContext();
}
void WebGL2ComputeRenderingContextBase::InitializeNewContext() {
DCHECK(!isContextLost());
DCHECK(GetDrawingBuffer());
bound_dispatch_indirect_buffer_ = nullptr;
bound_draw_indirect_buffer_ = nullptr;
bound_atomic_counter_buffer_ = nullptr;
bound_shader_storage_buffer_ = nullptr;
GLint max_atomic_counter_buffer_bindings = 0;
ContextGL()->GetIntegerv(GL_MAX_ATOMIC_COUNTER_BUFFER_BINDINGS,
&max_atomic_counter_buffer_bindings);
bound_indexed_atomic_counter_buffers_.clear();
bound_indexed_atomic_counter_buffers_.resize(
max_atomic_counter_buffer_bindings);
GLint max_shader_storage_buffer_bindings = 0;
ContextGL()->GetIntegerv(GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS,
&max_shader_storage_buffer_bindings);
bound_indexed_shader_storage_buffers_.clear();
bound_indexed_shader_storage_buffers_.resize(
max_shader_storage_buffer_bindings);
WebGL2RenderingContextBase::InitializeNewContext();
}
void WebGL2ComputeRenderingContextBase::dispatchCompute(GLuint numGroupsX,
GLuint numGroupsY,
GLuint numGroupsZ) {
ContextGL()->DispatchCompute(numGroupsX, numGroupsY, numGroupsZ);
}
void WebGL2ComputeRenderingContextBase::dispatchComputeIndirect(
int64_t offset) {
if (!ValidateValueFitNonNegInt32("dispatchComputeIndirect", "offset", offset))
return;
ContextGL()->DispatchComputeIndirect(static_cast<GLintptr>(offset));
}
ScriptValue WebGL2ComputeRenderingContextBase::getProgramInterfaceParameter(
ScriptState* script_state,
WebGLProgram* program,
GLenum program_interface,
GLenum pname) {
if (!ValidateWebGLProgramOrShader("getProgramInterfaceParameter", program))
return ScriptValue::CreateNull(script_state);
if (!ValidateProgramInterface(
"getProgramInterfaceParameter", program_interface))
return ScriptValue::CreateNull(script_state);
if (program_interface == GL_ATOMIC_COUNTER_BUFFER &&
pname == GL_MAX_NAME_LENGTH) {
SynthesizeGLError(GL_INVALID_OPERATION, "getProgramInterfaceParameter",
"atomic counter resources are not assigned name strings");
return ScriptValue::CreateNull(script_state);
}
if (program_interface != GL_ATOMIC_COUNTER_BUFFER &&
program_interface != GL_SHADER_STORAGE_BLOCK &&
program_interface != GL_UNIFORM_BLOCK &&
pname == GL_MAX_NUM_ACTIVE_VARIABLES) {
SynthesizeGLError(
GL_INVALID_OPERATION, "getProgramInterfaceParameter",
"invalid parameter name for the specified program interface");
return ScriptValue::CreateNull(script_state);
}
switch (pname) {
case GL_ACTIVE_RESOURCES:
case GL_MAX_NAME_LENGTH:
case GL_MAX_NUM_ACTIVE_VARIABLES: {
GLint value = 0;
ContextGL()->GetProgramInterfaceiv(
ObjectOrZero(program), program_interface, pname, &value);
return WebGLAny(script_state, value);
}
default:
SynthesizeGLError(GL_INVALID_ENUM, "getProgramInterfaceParameter",
"invalid parameter name");
return ScriptValue::CreateNull(script_state);
}
}
GLuint WebGL2ComputeRenderingContextBase::getProgramResourceIndex(
WebGLProgram* program,
GLenum program_interface,
const String& name) {
if (!ValidateWebGLProgramOrShader("getProgramResourceIndex", program))
return GL_INVALID_INDEX;
if (!ValidateProgramInterface("getProgramResourceIndex", program_interface))
return GL_INVALID_INDEX;
if (program_interface == GL_ATOMIC_COUNTER_BUFFER) {
SynthesizeGLError(GL_INVALID_ENUM, "getProgramResourceIndex",
"atomic counter resources are not assigned name strings");
return GL_INVALID_INDEX;
}
if (!ValidateString("getProgramResourceIndex", name))
return GL_INVALID_INDEX;
return ContextGL()->GetProgramResourceIndex(
ObjectOrZero(program), program_interface, name.Utf8().c_str());
}
String WebGL2ComputeRenderingContextBase::getProgramResourceName(
WebGLProgram* program,
GLenum program_interface,
GLuint index) {
if (!ValidateWebGLProgramOrShader("getProgramResourceName", program))
return String();
if (!ValidateProgramInterface("getProgramResourceName", program_interface))
return String();
if (program_interface == GL_ATOMIC_COUNTER_BUFFER) {
SynthesizeGLError(GL_INVALID_ENUM, "getProgramResourceName",
"atomic counter resources are not assigned name strings");
return String();
}
if (!ValidateProgramResourceIndex(
"getProgramResourceName", program, program_interface, index))
return String();
GLint max_name_length = -1;
ContextGL()->GetProgramInterfaceiv(ObjectOrZero(program), program_interface,
GL_MAX_NAME_LENGTH, &max_name_length);
if (max_name_length <= 0)
return String();
auto name = std::make_unique<GLchar[]>(max_name_length);
GLsizei length = 0;
ContextGL()->GetProgramResourceName(ObjectOrZero(program), program_interface,
index, max_name_length, &length,
name.get());
if (length <= 0)
return String();
return String(name.get(), static_cast<uint32_t>(length));
}
base::Optional<HeapVector<ScriptValue>>
WebGL2ComputeRenderingContextBase::getProgramResource(
ScriptState* script_state,
WebGLProgram* program,
GLenum program_interface,
GLuint index,
const Vector<GLenum>& props) {
if (!ValidateWebGLProgramOrShader("getProgramResource", program))
return base::nullopt;
if (!ValidateProgramInterface("getProgramResource", program_interface))
return base::nullopt;
if (props.IsEmpty()) {
SynthesizeGLError(GL_INVALID_VALUE, "getProgramResource",
"resource prop array is empty");
return base::nullopt;
}
if (!ValidateProgramResourceIndex(
"getProgramResource", program, program_interface, index))
return base::nullopt;
// For props with variable-length return values, their lengths will be queried
// first with |auxiliary_props|, and |extended_params| will be adequately
// sized for the whole result after that.
Vector<GLenum> auxiliary_props;
Vector<GLint> auxiliary_params;
Vector<GLenum> extended_props;
Vector<GLint> extended_params;
if (!ValidateAndExtendProgramResourceProperties(
"getProgramResource", program_interface, props, extended_props))
return base::nullopt;
extended_params.resize(extended_props.size());
for (wtf_size_t i = 0; i < extended_props.size() - props.size(); ++i) {
auxiliary_props.push_back(extended_props[i]);
auxiliary_params.push_back(-1);
extended_params.pop_back();
}
if (auxiliary_props.size()) {
ContextGL()->GetProgramResourceiv(ObjectOrZero(program), program_interface,
index, auxiliary_props.size(),
auxiliary_props.data(),
auxiliary_params.size(), nullptr,
auxiliary_params.data());
for (GLint n : auxiliary_params) {
extended_params.resize(extended_params.size() + std::max(n, 0));
}
}
GLsizei length = 0;
ContextGL()->GetProgramResourceiv(ObjectOrZero(program), program_interface,
index, extended_props.size(),
extended_props.data(),
extended_params.size(), &length,
extended_params.data());
if (length <= 0) {
return base::nullopt;
}
for (wtf_size_t i = 0; i < auxiliary_params.size(); ++i) {
// The returned lengths really should not differ from the previous ones.
CHECK_EQ(extended_params[i], auxiliary_params[i]);
}
// Interpret the returned values and construct the result array. The type of
// each array element is the natural type for the requested property.
HeapVector<ScriptValue> result;
wtf_size_t auxiliary_param_index = 0;
wtf_size_t extended_param_index = auxiliary_params.size();
for (GLenum prop : props) {
switch (prop) {
case GL_IS_ROW_MAJOR:
case GL_REFERENCED_BY_COMPUTE_SHADER:
case GL_REFERENCED_BY_FRAGMENT_SHADER:
case GL_REFERENCED_BY_VERTEX_SHADER: {
bool value = extended_params[extended_param_index];
result.push_back(WebGLAny(script_state, value));
++extended_param_index;
break;
}
case GL_ARRAY_STRIDE:
case GL_ATOMIC_COUNTER_BUFFER_INDEX:
case GL_BLOCK_INDEX:
case GL_MATRIX_STRIDE:
case GL_OFFSET: {
int value = extended_params[extended_param_index];
result.push_back(WebGLAny(script_state, value));
++extended_param_index;
break;
}
case GL_LOCATION: {
int value = extended_params[extended_param_index];
result.push_back(
WrapLocation(script_state, value, program, program_interface));
++extended_param_index;
break;
}
case GL_ARRAY_SIZE:
case GL_BUFFER_BINDING:
case GL_BUFFER_DATA_SIZE:
case GL_NAME_LENGTH:
case GL_NUM_ACTIVE_VARIABLES:
case GL_TOP_LEVEL_ARRAY_SIZE:
case GL_TOP_LEVEL_ARRAY_STRIDE: {
unsigned value = extended_params[extended_param_index];
result.push_back(WebGLAny(script_state, value));
++extended_param_index;
break;
}
case GL_TYPE: {
GLenum value = extended_params[extended_param_index];
result.push_back(WebGLAny(script_state, value));
++extended_param_index;
break;
}
case GL_ACTIVE_VARIABLES: {
DOMUint32Array* value = DOMUint32Array::Create(
reinterpret_cast<unsigned*>(&extended_params[extended_param_index]),
auxiliary_params[auxiliary_param_index]);
result.push_back(WebGLAny(script_state, value));
extended_param_index += auxiliary_params[auxiliary_param_index];
++auxiliary_param_index;
break;
}
default:
NOTREACHED();
}
}
return result;
}
ScriptValue WebGL2ComputeRenderingContextBase::getProgramResourceLocation(
ScriptState* script_state,
WebGLProgram* program,
GLenum program_interface,
const String& name) {
if (!ValidateWebGLProgramOrShader("getProgramResourceLocation", program))
return WrapLocation(script_state, -1, program, program_interface);
if (!ValidateProgramInterface(
"getProgramResourceLocation", program_interface))
return WrapLocation(script_state, -1, program, program_interface);
if (!ValidateLocationLength("getProgramResourceLocation", name))
return WrapLocation(script_state, -1, program, program_interface);
if (!ValidateString("getProgramResourceLocation", name))
return WrapLocation(script_state, -1, program, program_interface);
if (IsPrefixReserved(name))
return WrapLocation(script_state, -1, program, program_interface);
if (!program->LinkStatus(this)) {
SynthesizeGLError(GL_INVALID_OPERATION, "getProgramResourceLocation",
"program not linked");
return WrapLocation(script_state, -1, program, program_interface);
}
GLint location = ContextGL()->GetProgramResourceLocation(
ObjectOrZero(program), program_interface, name.Utf8().c_str());
return WrapLocation(script_state, location, program, program_interface);
}
void WebGL2ComputeRenderingContextBase::bindImageTexture(GLuint unit,
WebGLTexture* texture,
GLint level,
GLboolean layered,
GLint layer,
GLenum access,
GLenum format) {
ContextGL()->BindImageTexture(unit, ObjectOrZero(texture), level, layered,
layer, access, format);
}
void WebGL2ComputeRenderingContextBase::memoryBarrier(GLbitfield barriers) {
ContextGL()->MemoryBarrierEXT(barriers);
}
void WebGL2ComputeRenderingContextBase::memoryBarrierByRegion(
GLbitfield barriers) {
ContextGL()->MemoryBarrierByRegion(barriers);
}
ScriptValue WebGL2ComputeRenderingContextBase::getParameter(
ScriptState* script_state,
GLenum pname) {
if (isContextLost())
return ScriptValue::CreateNull(script_state);
switch (pname) {
case GL_SHADING_LANGUAGE_VERSION: {
return WebGLAny(
script_state,
"WebGL GLSL ES 3.10 (" +
String(ContextGL()->GetString(GL_SHADING_LANGUAGE_VERSION)) +
")");
}
case GL_VERSION: {
return WebGLAny(script_state,
"WebGL 2.0 Compute (" +
String(ContextGL()->GetString(GL_VERSION)) + ")");
}
case GL_MAX_ATOMIC_COUNTER_BUFFER_SIZE:
case GL_MAX_ATOMIC_COUNTER_BUFFER_BINDINGS:
case GL_MAX_COMBINED_ATOMIC_COUNTER_BUFFERS:
case GL_MAX_COMBINED_ATOMIC_COUNTERS:
case GL_MAX_COMBINED_SHADER_STORAGE_BLOCKS:
case GL_MAX_COMPUTE_SHADER_STORAGE_BLOCKS:
case GL_MAX_COMPUTE_SHARED_MEMORY_SIZE:
case GL_MAX_COMPUTE_WORK_GROUP_INVOCATIONS:
case GL_MAX_COMPUTE_UNIFORM_COMPONENTS:
case GL_MAX_COMPUTE_UNIFORM_BLOCKS:
case GL_MAX_COMPUTE_TEXTURE_IMAGE_UNITS:
case GL_MAX_COMPUTE_IMAGE_UNIFORMS:
case GL_MAX_FRAGMENT_ATOMIC_COUNTER_BUFFERS:
case GL_MAX_FRAGMENT_ATOMIC_COUNTERS:
case GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS:
case GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS:
case GL_MAX_VERTEX_ATOMIC_COUNTER_BUFFERS:
case GL_MAX_VERTEX_ATOMIC_COUNTERS:
case GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS:
return GetIntParameter(script_state, pname);
case GL_MAX_SHADER_STORAGE_BLOCK_SIZE:
return GetInt64Parameter(script_state, pname);
case GL_DISPATCH_INDIRECT_BUFFER_BINDING:
return WebGLAny(script_state, bound_dispatch_indirect_buffer_.Get());
case GL_DRAW_INDIRECT_BUFFER_BINDING:
return WebGLAny(script_state, bound_draw_indirect_buffer_.Get());
default:
return WebGL2RenderingContextBase::getParameter(script_state, pname);
}
}
ScriptValue WebGL2ComputeRenderingContextBase::getIndexedParameter(
ScriptState* script_state,
GLenum target,
GLuint index) {
if (isContextLost())
return ScriptValue::CreateNull(script_state);
switch (target) {
case GL_ATOMIC_COUNTER_BUFFER_BINDING:
if (index >= bound_indexed_atomic_counter_buffers_.size()) {
SynthesizeGLError(GL_INVALID_VALUE, "getIndexedParameter",
"index out of range");
return ScriptValue::CreateNull(script_state);
}
return WebGLAny(script_state,
bound_indexed_atomic_counter_buffers_[index].Get());
case GL_SHADER_STORAGE_BUFFER_BINDING:
if (index >= bound_indexed_shader_storage_buffers_.size()) {
SynthesizeGLError(GL_INVALID_VALUE, "getIndexedParameter",
"index out of range");
return ScriptValue::CreateNull(script_state);
}
return WebGLAny(script_state,
bound_indexed_shader_storage_buffers_[index].Get());
case GL_MAX_COMPUTE_WORK_GROUP_COUNT:
case GL_MAX_COMPUTE_WORK_GROUP_SIZE:
case GL_ATOMIC_COUNTER_BUFFER_SIZE:
case GL_ATOMIC_COUNTER_BUFFER_START:
case GL_SHADER_STORAGE_BUFFER_SIZE:
case GL_SHADER_STORAGE_BUFFER_START: {
GLint64 value = -1;
ContextGL()->GetInteger64i_v(target, index, &value);
return WebGLAny(script_state, value);
}
default:
return WebGL2RenderingContextBase::getIndexedParameter(
script_state, target, index);
}
}
void WebGL2ComputeRenderingContextBase::Trace(blink::Visitor* visitor) {
visitor->Trace(bound_dispatch_indirect_buffer_);
visitor->Trace(bound_draw_indirect_buffer_);
visitor->Trace(bound_atomic_counter_buffer_);
visitor->Trace(bound_indexed_atomic_counter_buffers_);
visitor->Trace(bound_shader_storage_buffer_);
visitor->Trace(bound_indexed_shader_storage_buffers_);
WebGL2RenderingContextBase::Trace(visitor);
}
bool WebGL2ComputeRenderingContextBase::ValidateProgramInterface(
const char* function_name,
GLenum program_interface) {
switch (program_interface) {
case GL_ATOMIC_COUNTER_BUFFER:
case GL_BUFFER_VARIABLE:
case GL_PROGRAM_INPUT:
case GL_PROGRAM_OUTPUT:
case GL_SHADER_STORAGE_BLOCK:
case GL_TRANSFORM_FEEDBACK_VARYING:
case GL_UNIFORM:
case GL_UNIFORM_BLOCK:
return true;
default:
SynthesizeGLError(GL_INVALID_ENUM, function_name,
"invalid program interface");
return false;
}
}
bool WebGL2ComputeRenderingContextBase::ValidateProgramResourceIndex(
const char* function_name,
WebGLProgram* program,
GLenum program_interface,
GLuint index) {
DCHECK(program);
if (!program->LinkStatus(this)) {
SynthesizeGLError(GL_INVALID_OPERATION, function_name,
"program not linked");
return false;
}
GLint active_resources = 0;
ContextGL()->GetProgramInterfaceiv(ObjectOrZero(program), program_interface,
GL_ACTIVE_RESOURCES, &active_resources);
if (index >= static_cast<GLuint>(active_resources)) {
SynthesizeGLError(GL_INVALID_VALUE, function_name,
"invalid program resource index");
return false;
}
return true;
}
bool
WebGL2ComputeRenderingContextBase::ValidateAndExtendProgramResourceProperties(
const char* function_name,
GLenum program_interface,
const Vector<GLenum>& props,
Vector<GLenum>& extended_props) {
Vector<GLenum> auxiliary_props;
for (GLenum prop : props) {
GLenum error = GL_NO_ERROR;
switch (prop) {
// Handle props with fixed-length return values.
case GL_BUFFER_BINDING:
case GL_NUM_ACTIVE_VARIABLES:
if (program_interface != GL_ATOMIC_COUNTER_BUFFER &&
program_interface != GL_SHADER_STORAGE_BLOCK &&
program_interface != GL_UNIFORM_BLOCK)
error = GL_INVALID_OPERATION;
break;
case GL_ARRAY_SIZE:
if (program_interface != GL_BUFFER_VARIABLE &&
program_interface != GL_PROGRAM_INPUT &&
program_interface != GL_PROGRAM_OUTPUT &&
program_interface != GL_TRANSFORM_FEEDBACK_VARYING &&
program_interface != GL_UNIFORM)
error = GL_INVALID_OPERATION;
break;
case GL_ARRAY_STRIDE:
case GL_BLOCK_INDEX:
case GL_IS_ROW_MAJOR:
case GL_MATRIX_STRIDE:
if (program_interface != GL_BUFFER_VARIABLE &&
program_interface != GL_UNIFORM)
error = GL_INVALID_OPERATION;
break;
case GL_ATOMIC_COUNTER_BUFFER_INDEX:
if (program_interface != GL_UNIFORM)
error = GL_INVALID_OPERATION;
break;
case GL_BUFFER_DATA_SIZE:
if (program_interface != GL_ATOMIC_COUNTER_BUFFER &&
program_interface != GL_SHADER_STORAGE_BLOCK &&
program_interface != GL_UNIFORM_BLOCK)
error = GL_INVALID_OPERATION;
break;
case GL_LOCATION:
if (program_interface != GL_PROGRAM_INPUT &&
program_interface != GL_PROGRAM_OUTPUT &&
program_interface != GL_UNIFORM)
error = GL_INVALID_OPERATION;
break;
case GL_NAME_LENGTH:
if (program_interface == GL_ATOMIC_COUNTER_BUFFER)
error = GL_INVALID_OPERATION;
break;
case GL_OFFSET:
if (program_interface != GL_BUFFER_VARIABLE &&
program_interface != GL_UNIFORM)
error = GL_INVALID_OPERATION;
break;
case GL_REFERENCED_BY_VERTEX_SHADER:
case GL_REFERENCED_BY_FRAGMENT_SHADER:
case GL_REFERENCED_BY_COMPUTE_SHADER:
if (program_interface != GL_ATOMIC_COUNTER_BUFFER &&
program_interface != GL_BUFFER_VARIABLE &&
program_interface != GL_PROGRAM_INPUT &&
program_interface != GL_PROGRAM_OUTPUT &&
program_interface != GL_SHADER_STORAGE_BLOCK &&
program_interface != GL_UNIFORM &&
program_interface != GL_UNIFORM_BLOCK)
error = GL_INVALID_OPERATION;
break;
case GL_TOP_LEVEL_ARRAY_SIZE:
case GL_TOP_LEVEL_ARRAY_STRIDE:
if (program_interface != GL_BUFFER_VARIABLE)
error = GL_INVALID_OPERATION;
break;
case GL_TYPE:
if (program_interface != GL_BUFFER_VARIABLE &&
program_interface != GL_PROGRAM_INPUT &&
program_interface != GL_PROGRAM_OUTPUT &&
program_interface != GL_TRANSFORM_FEEDBACK_VARYING &&
program_interface != GL_UNIFORM)
error = GL_INVALID_OPERATION;
break;
// Handle props with variable-length return values.
case GL_ACTIVE_VARIABLES:
if (program_interface != GL_ATOMIC_COUNTER_BUFFER &&
program_interface != GL_SHADER_STORAGE_BLOCK &&
program_interface != GL_UNIFORM_BLOCK) {
error = GL_INVALID_OPERATION;
break;
}
auxiliary_props.push_back(GL_NUM_ACTIVE_VARIABLES);
break;
default:
error = GL_INVALID_ENUM;
}
switch (error) {
case GL_NO_ERROR:
break;
case GL_INVALID_ENUM:
SynthesizeGLError(GL_INVALID_ENUM, function_name,
"invalid program resource prop");
return false;
case GL_INVALID_OPERATION:
SynthesizeGLError(
GL_INVALID_OPERATION, function_name,
"invalid resource prop for the specified program interface");
return false;
default:
NOTREACHED();
}
}
extended_props = auxiliary_props;
extended_props.AppendVector(props);
return true;
}
ScriptValue WebGL2ComputeRenderingContextBase::WrapLocation(
ScriptState* script_state,
GLint location,
WebGLProgram* program,
GLenum program_interface) {
switch (program_interface) {
case GL_PROGRAM_INPUT:
case GL_PROGRAM_OUTPUT: {
return WebGLAny(script_state, location);
}
case GL_UNIFORM: {
if (location == -1)
return ScriptValue::CreateNull(script_state);
DCHECK_GE(location, 0);
WebGLUniformLocation* uniform_location =
WebGLUniformLocation::Create(program, location);
return ScriptValue(script_state, ToV8(uniform_location, script_state));
}
default: {
return WebGLAny(script_state, location);
}
}
}
bool WebGL2ComputeRenderingContextBase::ValidateShaderType(
const char* function_name,
GLenum shader_type) {
switch (shader_type) {
case GL_COMPUTE_SHADER:
return true;
default:
return WebGL2RenderingContextBase::ValidateShaderType(
function_name, shader_type);
}
}
bool WebGL2ComputeRenderingContextBase::ValidateBufferTarget(
const char* function_name,
GLenum target) {
switch (target) {
case GL_DISPATCH_INDIRECT_BUFFER:
case GL_DRAW_INDIRECT_BUFFER:
case GL_ATOMIC_COUNTER_BUFFER:
case GL_SHADER_STORAGE_BUFFER:
return true;
default:
return WebGL2RenderingContextBase::ValidateBufferTarget(
function_name, target);
}
}
WebGLBuffer* WebGL2ComputeRenderingContextBase::ValidateBufferDataTarget(
const char* function_name,
GLenum target) {
WebGLBuffer* buffer = nullptr;
switch (target) {
case GL_DISPATCH_INDIRECT_BUFFER:
buffer = bound_dispatch_indirect_buffer_.Get();
break;
case GL_DRAW_INDIRECT_BUFFER:
buffer = bound_draw_indirect_buffer_.Get();
break;
case GL_ATOMIC_COUNTER_BUFFER:
buffer = bound_atomic_counter_buffer_.Get();
break;
case GL_SHADER_STORAGE_BUFFER:
buffer = bound_shader_storage_buffer_.Get();
break;
default:
return WebGL2RenderingContextBase::ValidateBufferDataTarget(
function_name, target);
}
if (!buffer) {
SynthesizeGLError(GL_INVALID_OPERATION, function_name, "no buffer");
return nullptr;
}
return buffer;
}
bool WebGL2ComputeRenderingContextBase::ValidateAndUpdateBufferBindTarget(
const char* function_name,
GLenum target,
WebGLBuffer* buffer) {
if (!ValidateBufferTarget(function_name, target))
return false;
if (buffer &&
!ValidateBufferTargetCompatibility(function_name, target, buffer))
return false;
switch (target) {
case GL_DISPATCH_INDIRECT_BUFFER:
bound_dispatch_indirect_buffer_ = buffer;
break;
case GL_DRAW_INDIRECT_BUFFER:
bound_draw_indirect_buffer_ = buffer;
break;
case GL_ATOMIC_COUNTER_BUFFER:
bound_atomic_counter_buffer_ = buffer;
break;
case GL_SHADER_STORAGE_BUFFER:
bound_shader_storage_buffer_ = buffer;
break;
default:
return WebGL2RenderingContextBase::ValidateAndUpdateBufferBindTarget(
function_name, target, buffer);
}
if (buffer && !buffer->GetInitialTarget())
buffer->SetInitialTarget(target);
return true;
}
void WebGL2ComputeRenderingContextBase::RemoveBoundBuffer(WebGLBuffer* buffer) {
if (bound_dispatch_indirect_buffer_ == buffer)
bound_dispatch_indirect_buffer_ = nullptr;
if (bound_draw_indirect_buffer_ == buffer)
bound_draw_indirect_buffer_ = nullptr;
if (bound_atomic_counter_buffer_ == buffer)
bound_atomic_counter_buffer_ = nullptr;
if (bound_shader_storage_buffer_ == buffer)
bound_shader_storage_buffer_ = nullptr;
WebGL2RenderingContextBase::RemoveBoundBuffer(buffer);
}
bool WebGL2ComputeRenderingContextBase::ValidateBufferTargetCompatibility(
const char* function_name,
GLenum target,
WebGLBuffer* buffer) {
DCHECK(buffer);
switch (buffer->GetInitialTarget()) {
case GL_ELEMENT_ARRAY_BUFFER:
switch (target) {
case GL_DISPATCH_INDIRECT_BUFFER:
case GL_DRAW_INDIRECT_BUFFER:
case GL_ATOMIC_COUNTER_BUFFER:
case GL_SHADER_STORAGE_BUFFER:
SynthesizeGLError(
GL_INVALID_OPERATION, function_name,
"element array buffers can not be bound to a different target");
return false;
default:
break;
}
break;
case GL_DISPATCH_INDIRECT_BUFFER:
case GL_DRAW_INDIRECT_BUFFER:
case GL_ATOMIC_COUNTER_BUFFER:
case GL_SHADER_STORAGE_BUFFER:
if (target == GL_ELEMENT_ARRAY_BUFFER) {
SynthesizeGLError(GL_INVALID_OPERATION, function_name,
"buffers bound to non ELEMENT_ARRAY_BUFFER targets "
"can not be bound to ELEMENT_ARRAY_BUFFER target");
return false;
}
return true;
default:
break;
}
return WebGL2RenderingContextBase::ValidateBufferTargetCompatibility(
function_name, target, buffer);
}
bool WebGL2ComputeRenderingContextBase::ValidateBufferBaseTarget(
const char* function_name,
GLenum target) {
switch (target) {
case GL_ATOMIC_COUNTER_BUFFER:
case GL_SHADER_STORAGE_BUFFER:
return true;
default:
return WebGL2RenderingContextBase::ValidateBufferBaseTarget(
function_name, target);
}
}
bool WebGL2ComputeRenderingContextBase::ValidateAndUpdateBufferBindBaseTarget(
const char* function_name,
GLenum target,
GLuint index,
WebGLBuffer* buffer) {
if (!ValidateBufferBaseTarget(function_name, target))
return false;
if (buffer &&
!ValidateBufferTargetCompatibility(function_name, target, buffer))
return false;
switch (target) {
case GL_ATOMIC_COUNTER_BUFFER:
if (index >= bound_indexed_atomic_counter_buffers_.size()) {
SynthesizeGLError(GL_INVALID_VALUE, function_name,
"index out of range");
return false;
}
bound_indexed_atomic_counter_buffers_[index] = buffer;
bound_atomic_counter_buffer_ = buffer;
break;
case GL_SHADER_STORAGE_BUFFER:
if (index >= bound_indexed_shader_storage_buffers_.size()) {
SynthesizeGLError(GL_INVALID_VALUE, function_name,
"index out of range");
return false;
}
bound_indexed_shader_storage_buffers_[index] = buffer;
bound_shader_storage_buffer_ = buffer;
break;
default:
return WebGL2RenderingContextBase::ValidateAndUpdateBufferBindBaseTarget(
function_name, target, index, buffer);
}
if (buffer && !buffer->GetInitialTarget())
buffer->SetInitialTarget(target);
return true;
}
} // namespace blink