blob: 5c1cd27882472b50a9647dbc0001f933cf82b3c8 [file] [log] [blame]
// Copyright (c) 2016 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 "gpu/command_buffer/service/gles2_cmd_decoder_passthrough.h"
#include "base/bind_helpers.h"
#include "base/strings/string_number_conversions.h"
#include "gpu/command_buffer/common/discardable_handle.h"
#include "gpu/command_buffer/service/decoder_client.h"
#include "gpu/command_buffer/service/gpu_fence_manager.h"
#include "gpu/command_buffer/service/gpu_tracer.h"
#include "gpu/command_buffer/service/passthrough_discardable_manager.h"
#include "gpu/command_buffer/service/shared_image_factory.h"
#include "gpu/command_buffer/service/shared_image_representation.h"
#include "ui/gfx/geometry/rect_conversions.h"
#include "ui/gl/dc_renderer_layer_params.h"
#include "ui/gl/gl_version_info.h"
namespace gpu {
namespace gles2 {
namespace {
template <typename ClientType, typename ServiceType, typename GenFunction>
error::Error GenHelper(GLsizei n,
const volatile ClientType* client_ids,
ClientServiceMap<ClientType, ServiceType>* id_map,
GenFunction gen_function) {
DCHECK(n >= 0);
std::vector<ClientType> client_ids_copy(client_ids, client_ids + n);
for (GLsizei ii = 0; ii < n; ++ii) {
if (id_map->HasClientID(client_ids_copy[ii])) {
return error::kInvalidArguments;
}
}
if (!CheckUniqueAndNonNullIds(n, client_ids_copy.data())) {
return error::kInvalidArguments;
}
std::vector<ServiceType> service_ids(n, 0);
gen_function(n, service_ids.data());
for (GLsizei ii = 0; ii < n; ++ii) {
id_map->SetIDMapping(client_ids_copy[ii], service_ids[ii]);
}
return error::kNoError;
}
template <typename ClientType, typename ServiceType, typename GenFunction>
error::Error CreateHelper(ClientType client_id,
ClientServiceMap<ClientType, ServiceType>* id_map,
GenFunction create_function) {
if (id_map->HasClientID(client_id)) {
return error::kInvalidArguments;
}
ServiceType service_id = create_function();
id_map->SetIDMapping(client_id, service_id);
return error::kNoError;
}
template <typename ClientType, typename ServiceType, typename DeleteFunction>
error::Error DeleteHelper(GLsizei n,
const volatile ClientType* client_ids,
ClientServiceMap<ClientType, ServiceType>* id_map,
DeleteFunction delete_function) {
DCHECK(n >= 0);
std::vector<ServiceType> service_ids(n, 0);
for (GLsizei ii = 0; ii < n; ++ii) {
ClientType client_id = client_ids[ii];
// Don't pass service IDs of objects with a client ID of 0. They are
// emulated and should not be deleteable
if (client_id != 0) {
service_ids[ii] = id_map->GetServiceIDOrInvalid(client_id);
id_map->RemoveClientID(client_id);
}
}
delete_function(n, service_ids.data());
return error::kNoError;
}
template <typename ClientType, typename ServiceType, typename DeleteFunction>
error::Error DeleteHelper(ClientType client_id,
ClientServiceMap<ClientType, ServiceType>* id_map,
DeleteFunction delete_function) {
delete_function(id_map->GetServiceIDOrInvalid(client_id));
id_map->RemoveClientID(client_id);
return error::kNoError;
}
template <typename ClientType, typename ServiceType, typename GenFunction>
ServiceType GetServiceID(ClientType client_id,
ClientServiceMap<ClientType, ServiceType>* id_map,
bool create_if_missing,
GenFunction gen_function) {
ServiceType service_id = id_map->invalid_service_id();
if (id_map->GetServiceID(client_id, &service_id)) {
return service_id;
}
if (create_if_missing) {
service_id = gen_function();
id_map->SetIDMapping(client_id, service_id);
return service_id;
}
return id_map->invalid_service_id();
}
GLuint GetTextureServiceID(gl::GLApi* api,
GLuint client_id,
PassthroughResources* resources,
bool create_if_missing) {
GLuint service_id = resources->texture_id_map.invalid_service_id();
if (resources->texture_id_map.GetServiceID(client_id, &service_id)) {
return service_id;
}
if (create_if_missing) {
GLuint service_id = 0;
api->glGenTexturesFn(1, &service_id);
resources->texture_id_map.SetIDMapping(client_id, service_id);
return service_id;
}
return resources->texture_id_map.invalid_service_id();
}
GLuint GetBufferServiceID(gl::GLApi* api,
GLuint client_id,
PassthroughResources* resources,
bool create_if_missing) {
return GetServiceID(client_id, &resources->buffer_id_map, create_if_missing,
[api]() {
GLuint service_id = 0;
api->glGenBuffersARBFn(1, &service_id);
return service_id;
});
}
GLuint GetRenderbufferServiceID(gl::GLApi* api,
GLuint client_id,
PassthroughResources* resources,
bool create_if_missing) {
return GetServiceID(client_id, &resources->renderbuffer_id_map,
create_if_missing, [api]() {
GLuint service_id = 0;
api->glGenRenderbuffersEXTFn(1, &service_id);
return service_id;
});
}
GLuint GetFramebufferServiceID(gl::GLApi* api,
GLuint client_id,
ClientServiceMap<GLuint, GLuint>* id_map,
bool create_if_missing) {
return GetServiceID(client_id, id_map, create_if_missing, [api]() {
GLuint service_id = 0;
api->glGenFramebuffersEXTFn(1, &service_id);
return service_id;
});
}
GLuint GetTransformFeedbackServiceID(GLuint client_id,
ClientServiceMap<GLuint, GLuint>* id_map) {
return id_map->GetServiceIDOrInvalid(client_id);
}
GLuint GetVertexArrayServiceID(GLuint client_id,
ClientServiceMap<GLuint, GLuint>* id_map) {
return id_map->GetServiceIDOrInvalid(client_id);
}
GLuint GetProgramServiceID(GLuint client_id, PassthroughResources* resources) {
return resources->program_id_map.GetServiceIDOrInvalid(client_id);
}
GLuint GetShaderServiceID(GLuint client_id, PassthroughResources* resources) {
return resources->shader_id_map.GetServiceIDOrInvalid(client_id);
}
GLuint GetQueryServiceID(GLuint client_id,
ClientServiceMap<GLuint, GLuint>* id_map) {
return id_map->GetServiceIDOrInvalid(client_id);
}
GLuint GetSamplerServiceID(GLuint client_id, PassthroughResources* resources) {
return resources->sampler_id_map.GetServiceIDOrInvalid(client_id);
}
GLsync GetSyncServiceID(GLuint client_id, PassthroughResources* resources) {
return reinterpret_cast<GLsync>(
resources->sync_id_map.GetServiceIDOrInvalid(client_id));
}
template <typename T>
void InsertValueIntoBuffer(std::vector<uint8_t>* data,
const T& value,
size_t offset) {
DCHECK_LE(offset + sizeof(T), data->size());
memcpy(data->data() + offset, &value, sizeof(T));
}
template <typename T>
void AppendValueToBuffer(std::vector<uint8_t>* data, const T& value) {
const base::CheckedNumeric<size_t> old_size = data->size();
data->resize((old_size + sizeof(T)).ValueOrDie());
memcpy(data->data() + old_size.ValueOrDie(), &value, sizeof(T));
}
void AppendStringToBuffer(std::vector<uint8_t>* data,
const char* str,
size_t len) {
const base::CheckedNumeric<size_t> old_size = data->size();
data->resize((old_size + len).ValueOrDie());
memcpy(data->data() + old_size.ValueOrDie(), str, len);
}
void AssignGLRectangle(GLint rectangle[4],
GLint x,
GLint y,
GLint width,
GLint height) {
rectangle[0] = x;
rectangle[1] = y;
rectangle[2] = width;
rectangle[3] = height;
}
// In order to minimize the amount of data copied, the command buffer client
// unpack pixels before sending the glTex[Sub]Image[2|3]D calls. The only
// parameter it doesn't handle is the alignment. Resetting the unpack state is
// not needed when uploading from a PBO and for compressed formats which the
// client sends untouched. This class handles resetting and restoring the unpack
// state.
// TODO(cwallez@chromium.org) it would be nicer to handle the resetting /
// restoring on the client side.
class ScopedUnpackStateButAlignmentReset {
public:
ScopedUnpackStateButAlignmentReset(gl::GLApi* api, bool enable, bool is_3d)
: api_(api) {
if (!enable) {
return;
}
api_->glGetIntegervFn(GL_UNPACK_SKIP_PIXELS, &skip_pixels_);
api_->glPixelStoreiFn(GL_UNPACK_SKIP_PIXELS, 0);
api_->glGetIntegervFn(GL_UNPACK_SKIP_ROWS, &skip_rows_);
api_->glPixelStoreiFn(GL_UNPACK_SKIP_ROWS, 0);
api_->glGetIntegervFn(GL_UNPACK_ROW_LENGTH, &row_length_);
api_->glPixelStoreiFn(GL_UNPACK_ROW_LENGTH, 0);
if (is_3d) {
api_->glGetIntegervFn(GL_UNPACK_SKIP_IMAGES, &skip_images_);
api_->glPixelStoreiFn(GL_UNPACK_SKIP_IMAGES, 0);
api_->glGetIntegervFn(GL_UNPACK_IMAGE_HEIGHT, &image_height_);
api_->glPixelStoreiFn(GL_UNPACK_IMAGE_HEIGHT, 0);
}
}
~ScopedUnpackStateButAlignmentReset() {
if (skip_pixels_ != 0) {
api_->glPixelStoreiFn(GL_UNPACK_SKIP_PIXELS, skip_pixels_);
}
if (skip_rows_ != 0) {
api_->glPixelStoreiFn(GL_UNPACK_SKIP_ROWS, skip_rows_);
}
if (skip_images_ != 0) {
api_->glPixelStoreiFn(GL_UNPACK_SKIP_IMAGES, skip_images_);
}
if (row_length_ != 0) {
api_->glPixelStoreiFn(GL_UNPACK_ROW_LENGTH, row_length_);
}
if (image_height_ != 0) {
api_->glPixelStoreiFn(GL_UNPACK_IMAGE_HEIGHT, image_height_);
}
}
private:
gl::GLApi* api_;
GLint skip_pixels_ = 0;
GLint skip_rows_ = 0;
GLint skip_images_ = 0;
GLint row_length_ = 0;
GLint image_height_ = 0;
};
class ScopedPackStateRowLengthReset {
public:
ScopedPackStateRowLengthReset(gl::GLApi* api, bool enable) : api_(api) {
if (!enable) {
return;
}
api_->glGetIntegervFn(GL_PACK_ROW_LENGTH, &row_length_);
api_->glPixelStoreiFn(GL_PACK_ROW_LENGTH, 0);
}
~ScopedPackStateRowLengthReset() {
if (row_length_ != 0) {
api_->glPixelStoreiFn(GL_PACK_ROW_LENGTH, row_length_);
}
}
private:
gl::GLApi* api_;
GLint row_length_ = 0;
};
bool ModifyAttachmentForEmulatedFramebuffer(GLenum* attachment) {
switch (*attachment) {
case GL_BACK:
*attachment = GL_COLOR_ATTACHMENT0;
return true;
case GL_DEPTH:
*attachment = GL_DEPTH_ATTACHMENT;
return true;
case GL_STENCIL:
*attachment = GL_STENCIL_ATTACHMENT;
return true;
default:
return false;
}
}
bool ModifyAttachmentsForEmulatedFramebuffer(std::vector<GLenum>* attachments) {
for (GLenum& attachment : *attachments) {
if (!ModifyAttachmentForEmulatedFramebuffer(&attachment)) {
return false;
}
}
return true;
}
} // anonymous namespace
// Implementations of commands
error::Error GLES2DecoderPassthroughImpl::DoActiveTexture(GLenum texture) {
CheckErrorCallbackState();
api()->glActiveTextureFn(texture);
if (CheckErrorCallbackState()) {
return error::kNoError;
}
active_texture_unit_ = static_cast<size_t>(texture) - GL_TEXTURE0;
DCHECK(active_texture_unit_ < kMaxTextureUnits);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoAttachShader(GLuint program,
GLuint shader) {
api()->glAttachShaderFn(GetProgramServiceID(program, resources_),
GetShaderServiceID(shader, resources_));
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoBindAttribLocation(
GLuint program,
GLuint index,
const char* name) {
api()->glBindAttribLocationFn(GetProgramServiceID(program, resources_), index,
name);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoBindBuffer(GLenum target,
GLuint buffer) {
CheckErrorCallbackState();
api()->glBindBufferFn(target, GetBufferServiceID(api(), buffer, resources_,
bind_generates_resource_));
if (CheckErrorCallbackState()) {
return error::kNoError;
}
DCHECK(bound_buffers_.find(target) != bound_buffers_.end());
bound_buffers_[target] = buffer;
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoBindBufferBase(GLenum target,
GLuint index,
GLuint buffer) {
CheckErrorCallbackState();
api()->glBindBufferBaseFn(
target, index,
GetBufferServiceID(api(), buffer, resources_, bind_generates_resource_));
if (CheckErrorCallbackState()) {
return error::kNoError;
}
DCHECK(bound_buffers_.find(target) != bound_buffers_.end());
bound_buffers_[target] = buffer;
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoBindBufferRange(GLenum target,
GLuint index,
GLuint buffer,
GLintptr offset,
GLsizeiptr size) {
CheckErrorCallbackState();
api()->glBindBufferRangeFn(
target, index,
GetBufferServiceID(api(), buffer, resources_, bind_generates_resource_),
offset, size);
if (CheckErrorCallbackState()) {
return error::kNoError;
}
DCHECK(bound_buffers_.find(target) != bound_buffers_.end());
bound_buffers_[target] = buffer;
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoBindFramebuffer(
GLenum target,
GLuint framebuffer) {
CheckErrorCallbackState();
api()->glBindFramebufferEXTFn(
target, GetFramebufferServiceID(api(), framebuffer, &framebuffer_id_map_,
bind_generates_resource_));
if (CheckErrorCallbackState()) {
return error::kNoError;
}
// Update tracking of the bound framebuffer
bool draw_framebuffer_changed = false;
switch (target) {
case GL_FRAMEBUFFER_EXT:
draw_framebuffer_changed = true;
bound_draw_framebuffer_ = framebuffer;
bound_read_framebuffer_ = framebuffer;
break;
case GL_DRAW_FRAMEBUFFER:
draw_framebuffer_changed = true;
bound_draw_framebuffer_ = framebuffer;
break;
case GL_READ_FRAMEBUFFER:
bound_read_framebuffer_ = framebuffer;
break;
default:
NOTREACHED();
break;
}
// Resync the surface offset if the draw framebuffer has changed to or from
// the default framebuffer
if (draw_framebuffer_changed && bound_draw_framebuffer_ != framebuffer &&
(bound_draw_framebuffer_ == 0 || framebuffer == 0)) {
ApplySurfaceDrawOffset();
}
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoBindImageTexture(GLuint unit,
GLuint texture,
GLint level,
GLboolean layered,
GLint layer,
GLenum access,
GLenum format) {
api()->glBindImageTextureEXTFn(
unit,
GetTextureServiceID(api(), texture, resources_, bind_generates_resource_),
level, layered, layer, access, format);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoBindRenderbuffer(
GLenum target,
GLuint renderbuffer) {
api()->glBindRenderbufferEXTFn(
target, GetRenderbufferServiceID(api(), renderbuffer, resources_,
bind_generates_resource_));
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoBindSampler(GLuint unit,
GLuint sampler) {
api()->glBindSamplerFn(unit, GetSamplerServiceID(sampler, resources_));
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoBindTexture(GLenum target,
GLuint texture) {
GLuint service_id =
GetTextureServiceID(api(), texture, resources_, bind_generates_resource_);
CheckErrorCallbackState();
api()->glBindTextureFn(target, service_id);
// Only update tracking if no error was generated in the bind call
if (CheckErrorCallbackState()) {
return error::kNoError;
}
// Track the currently bound textures
DCHECK(GLenumToTextureTarget(target) != TextureTarget::kUnkown);
scoped_refptr<TexturePassthrough> texture_passthrough = nullptr;
// If there was anything bound that required an image bind / copy,
// forget it since it's no longer bound to a sampler.
RemovePendingBindingTexture(target, active_texture_unit_);
if (service_id != 0) {
// Create a new texture object to track this texture
if (!resources_->texture_object_map.GetServiceID(texture,
&texture_passthrough)) {
texture_passthrough = new TexturePassthrough(service_id, target);
resources_->texture_object_map.SetIDMapping(texture, texture_passthrough);
} else {
// Shouldn't be possible to get here if this texture has a different
// target than the one it was just bound to
DCHECK(texture_passthrough->target() == target);
}
DCHECK(texture_passthrough);
// If |texture_passthrough| has a bound image that requires processing
// before a draw, then keep track of it.
if (texture_passthrough->is_bind_pending()) {
textures_pending_binding_.emplace_back(target, active_texture_unit_,
texture_passthrough->AsWeakPtr());
}
}
BoundTexture* bound_texture =
&bound_textures_[static_cast<size_t>(GLenumToTextureTarget(target))]
[active_texture_unit_];
bound_texture->client_id = texture;
bound_texture->texture = std::move(texture_passthrough);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoBindTransformFeedback(
GLenum target,
GLuint transformfeedback) {
api()->glBindTransformFeedbackFn(
target, GetTransformFeedbackServiceID(transformfeedback,
&transform_feedback_id_map_));
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoBlendColor(GLclampf red,
GLclampf green,
GLclampf blue,
GLclampf alpha) {
api()->glBlendColorFn(red, green, blue, alpha);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoBlendEquation(GLenum mode) {
api()->glBlendEquationFn(mode);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoBlendEquationSeparate(
GLenum modeRGB,
GLenum modeAlpha) {
api()->glBlendEquationSeparateFn(modeRGB, modeAlpha);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoBlendFunc(GLenum sfactor,
GLenum dfactor) {
api()->glBlendFuncFn(sfactor, dfactor);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoBlendFuncSeparate(GLenum srcRGB,
GLenum dstRGB,
GLenum srcAlpha,
GLenum dstAlpha) {
api()->glBlendFuncSeparateFn(srcRGB, dstRGB, srcAlpha, dstAlpha);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoBufferData(GLenum target,
GLsizeiptr size,
const void* data,
GLenum usage) {
CheckErrorCallbackState();
api()->glBufferDataFn(target, size, data, usage);
if (CheckErrorCallbackState()) {
return error::kNoError;
}
// Calling buffer data on a mapped buffer will implicitly unmap it
resources_->mapped_buffer_map.erase(bound_buffers_[target]);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoBufferSubData(GLenum target,
GLintptr offset,
GLsizeiptr size,
const void* data) {
api()->glBufferSubDataFn(target, offset, size, data);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoCheckFramebufferStatus(
GLenum target,
uint32_t* result) {
*result = api()->glCheckFramebufferStatusEXTFn(target);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoClear(GLbitfield mask) {
api()->glClearFn(mask);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoClearBufferfi(GLenum buffer,
GLint drawbuffers,
GLfloat depth,
GLint stencil) {
api()->glClearBufferfiFn(buffer, drawbuffers, depth, stencil);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoClearBufferfv(
GLenum buffer,
GLint drawbuffers,
const volatile GLfloat* value) {
api()->glClearBufferfvFn(buffer, drawbuffers,
const_cast<const GLfloat*>(value));
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoClearBufferiv(
GLenum buffer,
GLint drawbuffers,
const volatile GLint* value) {
api()->glClearBufferivFn(buffer, drawbuffers,
const_cast<const GLint*>(value));
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoClearBufferuiv(
GLenum buffer,
GLint drawbuffers,
const volatile GLuint* value) {
api()->glClearBufferuivFn(buffer, drawbuffers,
const_cast<const GLuint*>(value));
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoClearColor(GLclampf red,
GLclampf green,
GLclampf blue,
GLclampf alpha) {
api()->glClearColorFn(red, green, blue, alpha);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoClearDepthf(GLclampf depth) {
api()->glClearDepthfFn(depth);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoClearStencil(GLint s) {
api()->glClearStencilFn(s);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoClientWaitSync(GLuint sync,
GLbitfield flags,
GLuint64 timeout,
GLenum* result) {
// Force GL_SYNC_FLUSH_COMMANDS_BIT to avoid infinite wait.
GLbitfield modified_flags = flags | GL_SYNC_FLUSH_COMMANDS_BIT;
*result = api()->glClientWaitSyncFn(GetSyncServiceID(sync, resources_),
modified_flags, timeout);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoColorMask(GLboolean red,
GLboolean green,
GLboolean blue,
GLboolean alpha) {
api()->glColorMaskFn(red, green, blue, alpha);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoCompileShader(GLuint shader) {
api()->glCompileShaderFn(GetShaderServiceID(shader, resources_));
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoCompressedTexImage2D(
GLenum target,
GLint level,
GLenum internalformat,
GLsizei width,
GLsizei height,
GLint border,
GLsizei image_size,
GLsizei data_size,
const void* data) {
CheckErrorCallbackState();
api()->glCompressedTexImage2DRobustANGLEFn(target, level, internalformat,
width, height, border, image_size,
data_size, data);
if (CheckErrorCallbackState()) {
return error::kNoError;
}
UpdateTextureSizeFromTarget(target);
// Texture data upload can be slow. Exit command processing to allow for
// context preemption and GPU watchdog checks.
ExitCommandProcessingEarly();
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoCompressedTexSubImage2D(
GLenum target,
GLint level,
GLint xoffset,
GLint yoffset,
GLsizei width,
GLsizei height,
GLenum format,
GLsizei image_size,
GLsizei data_size,
const void* data) {
api()->glCompressedTexSubImage2DRobustANGLEFn(target, level, xoffset, yoffset,
width, height, format,
image_size, data_size, data);
// Texture data upload can be slow. Exit command processing to allow for
// context preemption and GPU watchdog checks.
ExitCommandProcessingEarly();
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoCompressedTexImage3D(
GLenum target,
GLint level,
GLenum internalformat,
GLsizei width,
GLsizei height,
GLsizei depth,
GLint border,
GLsizei image_size,
GLsizei data_size,
const void* data) {
CheckErrorCallbackState();
api()->glCompressedTexImage3DRobustANGLEFn(target, level, internalformat,
width, height, depth, border,
image_size, data_size, data);
if (CheckErrorCallbackState()) {
return error::kNoError;
}
UpdateTextureSizeFromTarget(target);
// Texture data upload can be slow. Exit command processing to allow for
// context preemption and GPU watchdog checks.
ExitCommandProcessingEarly();
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoCompressedTexSubImage3D(
GLenum target,
GLint level,
GLint xoffset,
GLint yoffset,
GLint zoffset,
GLsizei width,
GLsizei height,
GLsizei depth,
GLenum format,
GLsizei image_size,
GLsizei data_size,
const void* data) {
api()->glCompressedTexSubImage3DRobustANGLEFn(
target, level, xoffset, yoffset, zoffset, width, height, depth, format,
image_size, data_size, data);
// Texture data upload can be slow. Exit command processing to allow for
// context preemption and GPU watchdog checks.
ExitCommandProcessingEarly();
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoCopyBufferSubData(
GLenum readtarget,
GLenum writetarget,
GLintptr readoffset,
GLintptr writeoffset,
GLsizeiptr size) {
api()->glCopyBufferSubDataFn(readtarget, writetarget, readoffset, writeoffset,
size);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoCopyTexImage2D(
GLenum target,
GLint level,
GLenum internalformat,
GLint x,
GLint y,
GLsizei width,
GLsizei height,
GLint border) {
CheckErrorCallbackState();
api()->glCopyTexImage2DFn(target, level, internalformat, x, y, width, height,
border);
if (CheckErrorCallbackState()) {
return error::kNoError;
}
UpdateTextureSizeFromTarget(target);
// Texture data copying can be slow. Exit command processing to allow for
// context preemption and GPU watchdog checks.
ExitCommandProcessingEarly();
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoCopyTexSubImage2D(GLenum target,
GLint level,
GLint xoffset,
GLint yoffset,
GLint x,
GLint y,
GLsizei width,
GLsizei height) {
api()->glCopyTexSubImage2DFn(target, level, xoffset, yoffset, x, y, width,
height);
// Texture data copying can be slow. Exit command processing to allow for
// context preemption and GPU watchdog checks.
ExitCommandProcessingEarly();
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoCopyTexSubImage3D(GLenum target,
GLint level,
GLint xoffset,
GLint yoffset,
GLint zoffset,
GLint x,
GLint y,
GLsizei width,
GLsizei height) {
api()->glCopyTexSubImage3DFn(target, level, xoffset, yoffset, zoffset, x, y,
width, height);
// Texture data copying can be slow. Exit command processing to allow for
// context preemption and GPU watchdog checks.
ExitCommandProcessingEarly();
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoCreateProgram(GLuint client_id) {
return CreateHelper(client_id, &resources_->program_id_map,
[this]() { return api()->glCreateProgramFn(); });
}
error::Error GLES2DecoderPassthroughImpl::DoCreateShader(GLenum type,
GLuint client_id) {
return CreateHelper(client_id, &resources_->shader_id_map,
[this, type]() { return api()->glCreateShaderFn(type); });
}
error::Error GLES2DecoderPassthroughImpl::DoCullFace(GLenum mode) {
api()->glCullFaceFn(mode);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoDeleteBuffers(
GLsizei n,
const volatile GLuint* buffers) {
// DeleteHelper requires that n is non-negative because it allocates a copy of
// the IDs
if (n < 0) {
InsertError(GL_INVALID_VALUE, "n cannot be negative.");
return error::kNoError;
}
std::vector<GLuint> service_ids(n, 0);
for (GLsizei ii = 0; ii < n; ++ii) {
GLuint client_id = buffers[ii];
// Update the bound and mapped buffer state tracking
for (auto& buffer_binding : bound_buffers_) {
if (buffer_binding.second == client_id) {
buffer_binding.second = 0;
}
resources_->mapped_buffer_map.erase(client_id);
}
service_ids[ii] =
resources_->buffer_id_map.GetServiceIDOrInvalid(client_id);
resources_->buffer_id_map.RemoveClientID(client_id);
auto is_the_deleted_buffer = [client_id](const auto& update) {
return update.first == client_id;
};
base::EraseIf(buffer_shadow_updates_, is_the_deleted_buffer);
}
api()->glDeleteBuffersARBFn(n, service_ids.data());
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoDeleteFramebuffers(
GLsizei n,
const volatile GLuint* framebuffers) {
// DeleteHelper requires that n is non-negative because it allocates a copy of
// the IDs
if (n < 0) {
InsertError(GL_INVALID_VALUE, "n cannot be negative.");
return error::kNoError;
}
std::vector<GLuint> framebuffers_copy(framebuffers, framebuffers + n);
// If a bound framebuffer is deleted, it's binding is reset to 0. In the case
// of an emulated default framebuffer, bind the emulated one.
for (GLuint framebuffer : framebuffers_copy) {
if (framebuffer == bound_draw_framebuffer_) {
bound_draw_framebuffer_ = 0;
if (emulated_back_buffer_) {
api()->glBindFramebufferEXTFn(
GL_DRAW_FRAMEBUFFER, emulated_back_buffer_->framebuffer_service_id);
}
// Update the surface offset if the bound draw framebuffer is deleted
ApplySurfaceDrawOffset();
}
if (framebuffer == bound_read_framebuffer_) {
bound_read_framebuffer_ = 0;
if (emulated_back_buffer_) {
api()->glBindFramebufferEXTFn(
GL_READ_FRAMEBUFFER, emulated_back_buffer_->framebuffer_service_id);
}
}
}
return DeleteHelper(n, framebuffers_copy.data(), &framebuffer_id_map_,
[this](GLsizei n, GLuint* framebuffers) {
api()->glDeleteFramebuffersEXTFn(n, framebuffers);
});
}
error::Error GLES2DecoderPassthroughImpl::DoDeleteProgram(GLuint program) {
return DeleteHelper(
program, &resources_->program_id_map,
[this](GLuint program) { api()->glDeleteProgramFn(program); });
}
error::Error GLES2DecoderPassthroughImpl::DoDeleteRenderbuffers(
GLsizei n,
const volatile GLuint* renderbuffers) {
// DeleteHelper requires that n is non-negative because it allocates a copy of
// the IDs
if (n < 0) {
InsertError(GL_INVALID_VALUE, "n cannot be negative.");
return error::kNoError;
}
return DeleteHelper(n, renderbuffers, &resources_->renderbuffer_id_map,
[this](GLsizei n, GLuint* renderbuffers) {
api()->glDeleteRenderbuffersEXTFn(n, renderbuffers);
});
}
error::Error GLES2DecoderPassthroughImpl::DoDeleteSamplers(
GLsizei n,
const volatile GLuint* samplers) {
// DeleteHelper requires that n is non-negative because it allocates a copy of
// the IDs
if (n < 0) {
InsertError(GL_INVALID_VALUE, "n cannot be negative.");
return error::kNoError;
}
return DeleteHelper(n, samplers, &resources_->sampler_id_map,
[this](GLsizei n, GLuint* samplers) {
api()->glDeleteSamplersFn(n, samplers);
});
}
error::Error GLES2DecoderPassthroughImpl::DoDeleteSync(GLuint sync) {
return DeleteHelper(sync, &resources_->sync_id_map, [this](uintptr_t sync) {
api()->glDeleteSyncFn(reinterpret_cast<GLsync>(sync));
});
}
error::Error GLES2DecoderPassthroughImpl::DoDeleteShader(GLuint shader) {
return DeleteHelper(
shader, &resources_->shader_id_map,
[this](GLuint shader) { api()->glDeleteShaderFn(shader); });
}
error::Error GLES2DecoderPassthroughImpl::DoDeleteTextures(
GLsizei n,
const volatile GLuint* textures) {
// DeleteHelper requires that n is non-negative because it allocates a copy of
// the IDs
if (n < 0) {
InsertError(GL_INVALID_VALUE, "n cannot be negative.");
return error::kNoError;
}
// Textures that are currently associated with a mailbox are stored in the
// texture_object_map_ and are deleted automatically when they are
// unreferenced. Only delete textures that are not in this map.
std::vector<GLuint> non_mailbox_client_ids;
for (GLsizei ii = 0; ii < n; ++ii) {
GLuint client_id = textures[ii];
scoped_refptr<TexturePassthrough> texture = nullptr;
if (!resources_->texture_object_map.GetServiceID(client_id, &texture) ||
texture == nullptr) {
// Delete with DeleteHelper
non_mailbox_client_ids.push_back(client_id);
} else {
// Deleted when unreferenced
resources_->texture_id_map.RemoveClientID(client_id);
resources_->texture_object_map.RemoveClientID(client_id);
resources_->texture_shared_image_map.erase(client_id);
UpdateTextureBinding(texture->target(), client_id, nullptr);
}
// Notify the discardable manager that the texture is deleted
group_->passthrough_discardable_manager()->DeleteTexture(client_id,
group_.get());
}
return DeleteHelper(
non_mailbox_client_ids.size(), non_mailbox_client_ids.data(),
&resources_->texture_id_map, [this](GLsizei n, GLuint* textures) {
api()->glDeleteTexturesFn(n, textures);
});
}
error::Error GLES2DecoderPassthroughImpl::DoDeleteTransformFeedbacks(
GLsizei n,
const volatile GLuint* ids) {
// DeleteHelper requires that n is non-negative because it allocates a copy of
// the IDs
if (n < 0) {
InsertError(GL_INVALID_VALUE, "n cannot be negative.");
return error::kNoError;
}
return DeleteHelper(n, ids, &transform_feedback_id_map_,
[this](GLsizei n, GLuint* transform_feedbacks) {
api()->glDeleteTransformFeedbacksFn(
n, transform_feedbacks);
});
}
error::Error GLES2DecoderPassthroughImpl::DoDepthFunc(GLenum func) {
api()->glDepthFuncFn(func);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoDepthMask(GLboolean flag) {
api()->glDepthMaskFn(flag);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoDepthRangef(GLclampf zNear,
GLclampf zFar) {
api()->glDepthRangefFn(zNear, zFar);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoDetachShader(GLuint program,
GLuint shader) {
api()->glDetachShaderFn(GetProgramServiceID(program, resources_),
GetShaderServiceID(shader, resources_));
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoDisable(GLenum cap) {
api()->glDisableFn(cap);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoDisableVertexAttribArray(
GLuint index) {
api()->glDisableVertexAttribArrayFn(index);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoDispatchCompute(
GLuint num_groups_x,
GLuint num_groups_y,
GLuint num_groups_z) {
BindPendingImagesForSamplersIfNeeded();
api()->glDispatchComputeFn(num_groups_x, num_groups_y, num_groups_z);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoDrawArrays(GLenum mode,
GLint first,
GLsizei count) {
BindPendingImagesForSamplersIfNeeded();
api()->glDrawArraysFn(mode, first, count);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoDrawElements(GLenum mode,
GLsizei count,
GLenum type,
const void* indices) {
BindPendingImagesForSamplersIfNeeded();
api()->glDrawElementsFn(mode, count, type, indices);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoEnable(GLenum cap) {
api()->glEnableFn(cap);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoEnableVertexAttribArray(
GLuint index) {
api()->glEnableVertexAttribArrayFn(index);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoFenceSync(GLenum condition,
GLbitfield flags,
GLuint client_id) {
if (resources_->sync_id_map.HasClientID(client_id)) {
return error::kInvalidArguments;
}
CheckErrorCallbackState();
GLsync service_id = api()->glFenceSyncFn(condition, flags);
if (CheckErrorCallbackState()) {
return error::kNoError;
}
resources_->sync_id_map.SetIDMapping(client_id,
reinterpret_cast<uintptr_t>(service_id));
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoFinish() {
api()->glFinishFn();
error::Error error = ProcessReadPixels(true);
if (error != error::kNoError) {
return error;
}
return ProcessQueries(true);
}
error::Error GLES2DecoderPassthroughImpl::DoFlush() {
api()->glFlushFn();
error::Error error = ProcessReadPixels(false);
if (error != error::kNoError) {
return error;
}
return ProcessQueries(false);
}
error::Error GLES2DecoderPassthroughImpl::DoFlushMappedBufferRange(
GLenum target,
GLintptr offset,
GLsizeiptr size) {
auto bound_buffers_iter = bound_buffers_.find(target);
if (bound_buffers_iter == bound_buffers_.end() ||
bound_buffers_iter->second == 0) {
InsertError(GL_INVALID_OPERATION, "No buffer bound to this target.");
return error::kNoError;
}
GLuint client_buffer = bound_buffers_iter->second;
auto mapped_buffer_info_iter =
resources_->mapped_buffer_map.find(client_buffer);
if (mapped_buffer_info_iter == resources_->mapped_buffer_map.end()) {
InsertError(GL_INVALID_OPERATION, "Buffer is not mapped.");
return error::kNoError;
}
const MappedBuffer& map_info = mapped_buffer_info_iter->second;
if (offset < 0) {
InsertError(GL_INVALID_VALUE, "Offset cannot be negative.");
return error::kNoError;
}
if (size < 0) {
InsertError(GL_INVALID_VALUE, "Size cannot be negative.");
return error::kNoError;
}
base::CheckedNumeric<size_t> range_start(offset);
base::CheckedNumeric<size_t> range_end = offset + size;
if (!range_end.IsValid() && range_end.ValueOrDefault(0) > map_info.size) {
InsertError(GL_INVALID_OPERATION,
"Flush range is not within the original mapping size.");
return error::kNoError;
}
uint8_t* mem = GetSharedMemoryAs<uint8_t*>(
map_info.data_shm_id, map_info.data_shm_offset, map_info.size);
if (!mem) {
return error::kOutOfBounds;
}
memcpy(map_info.map_ptr + offset, mem + offset, size);
api()->glFlushMappedBufferRangeFn(target, offset, size);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoFramebufferParameteri(GLenum target,
GLenum pname,
GLint param) {
api()->glFramebufferParameteriFn(target, pname, param);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoFramebufferRenderbuffer(
GLenum target,
GLenum attachment,
GLenum renderbuffertarget,
GLuint renderbuffer) {
if (IsEmulatedFramebufferBound(target)) {
InsertError(GL_INVALID_OPERATION,
"Cannot change the attachments of the default framebuffer.");
return error::kNoError;
}
api()->glFramebufferRenderbufferEXTFn(
target, attachment, renderbuffertarget,
GetRenderbufferServiceID(api(), renderbuffer, resources_, false));
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoFramebufferTexture2D(
GLenum target,
GLenum attachment,
GLenum textarget,
GLuint texture,
GLint level) {
if (IsEmulatedFramebufferBound(target)) {
InsertError(GL_INVALID_OPERATION,
"Cannot change the attachments of the default framebuffer.");
return error::kNoError;
}
BindPendingImageForClientIDIfNeeded(texture);
api()->glFramebufferTexture2DEXTFn(
target, attachment, textarget,
GetTextureServiceID(api(), texture, resources_, false), level);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoFramebufferTextureLayer(
GLenum target,
GLenum attachment,
GLuint texture,
GLint level,
GLint layer) {
if (IsEmulatedFramebufferBound(target)) {
InsertError(GL_INVALID_OPERATION,
"Cannot change the attachments of the default framebuffer.");
return error::kNoError;
}
api()->glFramebufferTextureLayerFn(
target, attachment,
GetTextureServiceID(api(), texture, resources_, false), level, layer);
return error::kNoError;
}
error::Error
GLES2DecoderPassthroughImpl::DoFramebufferTextureMultiviewLayeredANGLE(
GLenum target,
GLenum attachment,
GLuint texture,
GLint level,
GLint base_view_index,
GLsizei num_views) {
if (IsEmulatedFramebufferBound(target)) {
InsertError(GL_INVALID_OPERATION,
"Cannot change the attachments of the default framebuffer.");
return error::kNoError;
}
api()->glFramebufferTextureMultiviewLayeredANGLEFn(
target, attachment,
GetTextureServiceID(api(), texture, resources_, false), level,
base_view_index, num_views);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoFrontFace(GLenum mode) {
api()->glFrontFaceFn(mode);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoGenBuffers(
GLsizei n,
volatile GLuint* buffers) {
return GenHelper(n, buffers, &resources_->buffer_id_map,
[this](GLsizei n, GLuint* buffers) {
api()->glGenBuffersARBFn(n, buffers);
});
}
error::Error GLES2DecoderPassthroughImpl::DoGenerateMipmap(GLenum target) {
api()->glGenerateMipmapEXTFn(target);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoGenFramebuffers(
GLsizei n,
volatile GLuint* framebuffers) {
return GenHelper(n, framebuffers, &framebuffer_id_map_,
[this](GLsizei n, GLuint* framebuffers) {
api()->glGenFramebuffersEXTFn(n, framebuffers);
});
}
error::Error GLES2DecoderPassthroughImpl::DoGenRenderbuffers(
GLsizei n,
volatile GLuint* renderbuffers) {
return GenHelper(n, renderbuffers, &resources_->renderbuffer_id_map,
[this](GLsizei n, GLuint* renderbuffers) {
api()->glGenRenderbuffersEXTFn(n, renderbuffers);
});
}
error::Error GLES2DecoderPassthroughImpl::DoGenSamplers(
GLsizei n,
volatile GLuint* samplers) {
return GenHelper(n, samplers, &resources_->sampler_id_map,
[this](GLsizei n, GLuint* samplers) {
api()->glGenSamplersFn(n, samplers);
});
}
error::Error GLES2DecoderPassthroughImpl::DoGenTextures(
GLsizei n,
volatile GLuint* textures) {
return GenHelper(n, textures, &resources_->texture_id_map,
[this](GLsizei n, GLuint* textures) {
api()->glGenTexturesFn(n, textures);
});
}
error::Error GLES2DecoderPassthroughImpl::DoGenTransformFeedbacks(
GLsizei n,
volatile GLuint* ids) {
return GenHelper(n, ids, &transform_feedback_id_map_,
[this](GLsizei n, GLuint* transform_feedbacks) {
api()->glGenTransformFeedbacksFn(n, transform_feedbacks);
});
}
error::Error GLES2DecoderPassthroughImpl::DoGetActiveAttrib(GLuint program,
GLuint index,
GLint* size,
GLenum* type,
std::string* name,
int32_t* success) {
CheckErrorCallbackState();
GLuint service_id = GetProgramServiceID(program, resources_);
GLint active_attribute_max_length = 0;
api()->glGetProgramivFn(service_id, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH,
&active_attribute_max_length);
if (CheckErrorCallbackState()) {
*success = 0;
return error::kNoError;
}
std::vector<char> name_buffer(active_attribute_max_length, 0);
api()->glGetActiveAttribFn(service_id, index, name_buffer.size(), nullptr,
size, type, name_buffer.data());
*name = std::string(name_buffer.data());
*success = CheckErrorCallbackState() ? 0 : 1;
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoGetActiveUniform(GLuint program,
GLuint index,
GLint* size,
GLenum* type,
std::string* name,
int32_t* success) {
CheckErrorCallbackState();
GLuint service_id = GetProgramServiceID(program, resources_);
GLint active_uniform_max_length = 0;
api()->glGetProgramivFn(service_id, GL_ACTIVE_UNIFORM_MAX_LENGTH,
&active_uniform_max_length);
if (CheckErrorCallbackState()) {
*success = 0;
return error::kNoError;
}
std::vector<char> name_buffer(active_uniform_max_length, 0);
api()->glGetActiveUniformFn(service_id, index, name_buffer.size(), nullptr,
size, type, name_buffer.data());
*name = std::string(name_buffer.data());
*success = CheckErrorCallbackState() ? 0 : 1;
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoGetActiveUniformBlockiv(
GLuint program,
GLuint index,
GLenum pname,
GLsizei bufSize,
GLsizei* length,
GLint* params) {
api()->glGetActiveUniformBlockivRobustANGLEFn(
GetProgramServiceID(program, resources_), index, pname, bufSize, length,
params);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoGetActiveUniformBlockName(
GLuint program,
GLuint index,
std::string* name) {
CheckErrorCallbackState();
GLuint program_service_id = GetProgramServiceID(program, resources_);
GLint max_name_length = 0;
api()->glGetProgramivFn(program_service_id,
GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH,
&max_name_length);
if (CheckErrorCallbackState()) {
return error::kNoError;
}
std::vector<GLchar> buffer(max_name_length, 0);
GLsizei length = 0;
api()->glGetActiveUniformBlockNameFn(program_service_id, index,
max_name_length, &length, buffer.data());
DCHECK(length <= max_name_length);
*name = length > 0 ? std::string(buffer.data(), length) : std::string();
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoGetActiveUniformsiv(
GLuint program,
GLsizei count,
const GLuint* indices,
GLenum pname,
GLint* params) {
api()->glGetActiveUniformsivFn(GetProgramServiceID(program, resources_),
count, indices, pname, params);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoGetAttachedShaders(
GLuint program,
GLsizei maxcount,
GLsizei* count,
GLuint* shaders) {
api()->glGetAttachedShadersFn(GetProgramServiceID(program, resources_),
maxcount, count, shaders);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoGetAttribLocation(GLuint program,
const char* name,
GLint* result) {
*result = api()->glGetAttribLocationFn(
GetProgramServiceID(program, resources_), name);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoGetBooleanv(GLenum pname,
GLsizei bufsize,
GLsizei* length,
GLboolean* params) {
return GetNumericHelper(pname, bufsize, length, params,
[this](GLenum pname, GLsizei bufsize, GLsizei* length,
GLboolean* params) {
api()->glGetBooleanvRobustANGLEFn(pname, bufsize,
length, params);
});
}
error::Error GLES2DecoderPassthroughImpl::DoGetBufferParameteri64v(
GLenum target,
GLenum pname,
GLsizei bufsize,
GLsizei* length,
GLint64* params) {
CheckErrorCallbackState();
api()->glGetBufferParameteri64vRobustANGLEFn(target, pname, bufsize, length,
params);
if (CheckErrorCallbackState()) {
return error::kNoError;
}
PatchGetBufferResults(target, pname, bufsize, length, params);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoGetBufferParameteriv(
GLenum target,
GLenum pname,
GLsizei bufsize,
GLsizei* length,
GLint* params) {
CheckErrorCallbackState();
api()->glGetBufferParameterivRobustANGLEFn(target, pname, bufsize, length,
params);
if (CheckErrorCallbackState()) {
return error::kNoError;
}
PatchGetBufferResults(target, pname, bufsize, length, params);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoGetError(uint32_t* result) {
FlushErrors();
*result = PopError();
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoGetFloatv(GLenum pname,
GLsizei bufsize,
GLsizei* length,
GLfloat* params) {
return GetNumericHelper(
pname, bufsize, length, params,
[this](GLenum pname, GLsizei bufsize, GLsizei* length, GLfloat* params) {
api()->glGetFloatvRobustANGLEFn(pname, bufsize, length, params);
});
}
error::Error GLES2DecoderPassthroughImpl::DoGetFragDataLocation(
GLuint program,
const char* name,
GLint* result) {
*result = api()->glGetFragDataLocationFn(
GetProgramServiceID(program, resources_), name);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoGetFramebufferAttachmentParameteriv(
GLenum target,
GLenum attachment,
GLenum pname,
GLsizei bufsize,
GLsizei* length,
GLint* params) {
GLenum updated_attachment = attachment;
if (IsEmulatedFramebufferBound(target)) {
// Update the attachment do the equivalent one in the emulated framebuffer
if (!ModifyAttachmentForEmulatedFramebuffer(&updated_attachment)) {
InsertError(GL_INVALID_OPERATION, "Invalid attachment.");
*length = 0;
return error::kNoError;
}
// Generate errors for parameter names that are only valid for non-default
// framebuffers
switch (pname) {
case GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME:
case GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL:
case GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE:
case GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER:
InsertError(GL_INVALID_ENUM, "Invalid parameter name.");
*length = 0;
return error::kNoError;
}
}
CheckErrorCallbackState();
// Get a scratch buffer to hold the result of the query
GLint* scratch_params = GetTypedScratchMemory<GLint>(bufsize);
api()->glGetFramebufferAttachmentParameterivRobustANGLEFn(
target, updated_attachment, pname, bufsize, length, scratch_params);
if (CheckErrorCallbackState()) {
DCHECK(*length == 0);
return error::kNoError;
}
// Update the results of the query, if needed
error::Error error = PatchGetFramebufferAttachmentParameter(
target, updated_attachment, pname, *length, scratch_params);
if (error != error::kNoError) {
*length = 0;
return error;
}
// Copy into the destination
DCHECK(*length < bufsize);
std::copy(scratch_params, scratch_params + *length, params);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoGetInteger64v(GLenum pname,
GLsizei bufsize,
GLsizei* length,
GLint64* params) {
return GetNumericHelper(
pname, bufsize, length, params,
[this](GLenum pname, GLsizei bufsize, GLsizei* length, GLint64* params) {
api()->glGetInteger64vRobustANGLEFn(pname, bufsize, length, params);
});
}
error::Error GLES2DecoderPassthroughImpl::DoGetIntegeri_v(GLenum pname,
GLuint index,
GLsizei bufsize,
GLsizei* length,
GLint* data) {
glGetIntegeri_vRobustANGLE(pname, index, bufsize, length, data);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoGetInteger64i_v(GLenum pname,
GLuint index,
GLsizei bufsize,
GLsizei* length,
GLint64* data) {
glGetInteger64i_vRobustANGLE(pname, index, bufsize, length, data);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoGetIntegerv(GLenum pname,
GLsizei bufsize,
GLsizei* length,
GLint* params) {
return GetNumericHelper(
pname, bufsize, length, params,
[this](GLenum pname, GLsizei bufsize, GLsizei* length, GLint* params) {
api()->glGetIntegervRobustANGLEFn(pname, bufsize, length, params);
});
}
error::Error GLES2DecoderPassthroughImpl::DoGetInternalformativ(GLenum target,
GLenum format,
GLenum pname,
GLsizei bufSize,
GLsizei* length,
GLint* params) {
api()->glGetInternalformativRobustANGLEFn(target, format, pname, bufSize,
length, params);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoGetProgramiv(GLuint program,
GLenum pname,
GLsizei bufsize,
GLsizei* length,
GLint* params) {
api()->glGetProgramivRobustANGLEFn(GetProgramServiceID(program, resources_),
pname, bufsize, length, params);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoGetProgramInfoLog(
GLuint program,
std::string* infolog) {
CheckErrorCallbackState();
GLint info_log_len = 0;
api()->glGetProgramivFn(GetProgramServiceID(program, resources_),
GL_INFO_LOG_LENGTH, &info_log_len);
if (CheckErrorCallbackState()) {
return error::kNoError;
}
std::vector<char> buffer(info_log_len, 0);
GLsizei length = 0;
api()->glGetProgramInfoLogFn(GetProgramServiceID(program, resources_),
info_log_len, &length, buffer.data());
DCHECK(length <= info_log_len);
*infolog = length > 0 ? std::string(buffer.data(), length) : std::string();
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoGetRenderbufferParameteriv(
GLenum target,
GLenum pname,
GLsizei bufsize,
GLsizei* length,
GLint* params) {
api()->glGetRenderbufferParameterivRobustANGLEFn(target, pname, bufsize,
length, params);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoGetSamplerParameterfv(
GLuint sampler,
GLenum pname,
GLsizei bufsize,
GLsizei* length,
GLfloat* params) {
api()->glGetSamplerParameterfvRobustANGLEFn(
GetSamplerServiceID(sampler, resources_), pname, bufsize, length, params);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoGetSamplerParameteriv(
GLuint sampler,
GLenum pname,
GLsizei bufsize,
GLsizei* length,
GLint* params) {
api()->glGetSamplerParameterivRobustANGLEFn(
GetSamplerServiceID(sampler, resources_), pname, bufsize, length, params);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoGetShaderiv(GLuint shader,
GLenum pname,
GLsizei bufsize,
GLsizei* length,
GLint* params) {
api()->glGetShaderivRobustANGLEFn(GetShaderServiceID(shader, resources_),
pname, bufsize, length, params);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoGetShaderInfoLog(
GLuint shader,
std::string* infolog) {
CheckErrorCallbackState();
GLuint service_id = GetShaderServiceID(shader, resources_);
GLint info_log_len = 0;
api()->glGetShaderivFn(service_id, GL_INFO_LOG_LENGTH, &info_log_len);
if (CheckErrorCallbackState()) {
return error::kNoError;
}
std::vector<char> buffer(info_log_len, 0);
GLsizei length = 0;
api()->glGetShaderInfoLogFn(service_id, info_log_len, &length, buffer.data());
DCHECK(length <= info_log_len);
*infolog = length > 0 ? std::string(buffer.data(), length) : std::string();
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoGetShaderPrecisionFormat(
GLenum shadertype,
GLenum precisiontype,
GLint* range,
GLint* precision,
int32_t* success) {
CheckErrorCallbackState();
api()->glGetShaderPrecisionFormatFn(shadertype, precisiontype, range,
precision);
*success = CheckErrorCallbackState() ? 0 : 1;
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoGetShaderSource(
GLuint shader,
std::string* source) {
CheckErrorCallbackState();
GLuint shader_service_id = GetShaderServiceID(shader, resources_);
GLint shader_source_length = 0;
api()->glGetShaderivFn(shader_service_id, GL_SHADER_SOURCE_LENGTH,
&shader_source_length);
if (CheckErrorCallbackState()) {
return error::kNoError;
}
std::vector<char> buffer(shader_source_length, 0);
GLsizei length = 0;
api()->glGetShaderSourceFn(shader_service_id, shader_source_length, &length,
buffer.data());
DCHECK(length <= shader_source_length);
*source = shader_source_length > 0 ? std::string(buffer.data(), length)
: std::string();
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoGetString(GLenum name,
uint32_t bucket_id) {
std::string extensions;
const char* str = nullptr;
switch (name) {
case GL_VERSION:
str = GetServiceVersionString(feature_info_.get());
break;
case GL_SHADING_LANGUAGE_VERSION:
str = GetServiceShadingLanguageVersionString(feature_info_.get());
break;
case GL_EXTENSIONS: {
extensions = gfx::MakeExtensionString(feature_info_->extensions());
str = extensions.c_str();
break;
}
default:
str = reinterpret_cast<const char*>(api()->glGetStringFn(name));
break;
}
Bucket* bucket = CreateBucket(bucket_id);
bucket->SetFromString(str);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoGetSynciv(GLuint sync,
GLenum pname,
GLsizei bufsize,
GLsizei* length,
GLint* values) {
api()->glGetSyncivFn(GetSyncServiceID(sync, resources_), pname, bufsize,
length, values);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoGetTexParameterfv(GLenum target,
GLenum pname,
GLsizei bufsize,
GLsizei* length,
GLfloat* params) {
api()->glGetTexParameterfvRobustANGLEFn(target, pname, bufsize, length,
params);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoGetTexParameteriv(GLenum target,
GLenum pname,
GLsizei bufsize,
GLsizei* length,
GLint* params) {
api()->glGetTexParameterivRobustANGLEFn(target, pname, bufsize, length,
params);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoGetTransformFeedbackVarying(
GLuint program,
GLuint index,
GLsizei* size,
GLenum* type,
std::string* name,
int32_t* success) {
CheckErrorCallbackState();
GLuint service_id = GetProgramServiceID(program, resources_);
GLint transform_feedback_varying_max_length = 0;
api()->glGetProgramivFn(service_id, GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH,
&transform_feedback_varying_max_length);
if (CheckErrorCallbackState()) {
*success = 0;
return error::kNoError;
}
std::vector<char> name_buffer(transform_feedback_varying_max_length, 0);
api()->glGetTransformFeedbackVaryingFn(service_id, index, name_buffer.size(),
nullptr, size, type,
name_buffer.data());
*name = std::string(name_buffer.data());
*success = CheckErrorCallbackState() ? 0 : 1;
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoGetUniformBlockIndex(
GLuint program,
const char* name,
GLint* index) {
*index = api()->glGetUniformBlockIndexFn(
GetProgramServiceID(program, resources_), name);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoGetUniformfv(GLuint program,
GLint location,
GLsizei bufsize,
GLsizei* length,
GLfloat* params) {
// GetUniform*RobustANGLE entry points expect bufsize in bytes like the entry
// points in GL_EXT_robustness
api()->glGetUniformfvRobustANGLEFn(GetProgramServiceID(program, resources_),
location, bufsize * sizeof(*params),
length, params);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoGetUniformiv(GLuint program,
GLint location,
GLsizei bufsize,
GLsizei* length,
GLint* params) {
// GetUniform*RobustANGLE entry points expect bufsize in bytes like the entry
// points in GL_EXT_robustness
api()->glGetUniformivRobustANGLEFn(GetProgramServiceID(program, resources_),
location, bufsize * sizeof(*params),
length, params);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoGetUniformuiv(GLuint program,
GLint location,
GLsizei bufsize,
GLsizei* length,
GLuint* params) {
// GetUniform*RobustANGLE entry points expect bufsize in bytes like the entry
// points in GL_EXT_robustness
api()->glGetUniformuivRobustANGLEFn(GetProgramServiceID(program, resources_),
location, bufsize * sizeof(*params),
length, params);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoGetUniformIndices(
GLuint program,
GLsizei count,
const char* const* names,
GLsizei bufSize,
GLuint* indices) {
api()->glGetUniformIndicesFn(GetProgramServiceID(program, resources_), count,
names, indices);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoGetUniformLocation(
GLuint program,
const char* name,
GLint* location) {
*location = api()->glGetUniformLocationFn(
GetProgramServiceID(program, resources_), name);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoGetVertexAttribfv(GLuint index,
GLenum pname,
GLsizei bufsize,
GLsizei* length,
GLfloat* params) {
api()->glGetVertexAttribfvRobustANGLEFn(index, pname, bufsize, length,
params);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoGetVertexAttribiv(GLuint index,
GLenum pname,
GLsizei bufsize,
GLsizei* length,
GLint* params) {
api()->glGetVertexAttribivRobustANGLEFn(index, pname, bufsize, length,
params);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoGetVertexAttribIiv(GLuint index,
GLenum pname,
GLsizei bufsize,
GLsizei* length,
GLint* params) {
api()->glGetVertexAttribIivRobustANGLEFn(index, pname, bufsize, length,
params);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoGetVertexAttribIuiv(
GLuint index,
GLenum pname,
GLsizei bufsize,
GLsizei* length,
GLuint* params) {
api()->glGetVertexAttribIuivRobustANGLEFn(index, pname, bufsize, length,
params);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoGetVertexAttribPointerv(
GLuint index,
GLenum pname,
GLsizei bufsize,
GLsizei* length,
GLuint* pointer) {
std::array<void*, 1> temp_pointers{{nullptr}};
GLsizei temp_length = 0;
api()->glGetVertexAttribPointervRobustANGLEFn(
index, pname, static_cast<GLsizei>(temp_pointers.size()), &temp_length,
temp_pointers.data());
DCHECK(temp_length >= 0 &&
temp_length <= static_cast<GLsizei>(temp_pointers.size()) &&
temp_length <= bufsize);
for (GLsizei ii = 0; ii < temp_length; ii++) {
pointer[ii] =
static_cast<GLuint>(reinterpret_cast<uintptr_t>(temp_pointers[ii]));
}
*length = temp_length;
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoHint(GLenum target, GLenum mode) {
api()->glHintFn(target, mode);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoInvalidateFramebuffer(
GLenum target,
GLsizei count,
const volatile GLenum* attachments) {
// Validate that count is non-negative before allocating a vector
if (count < 0) {
InsertError(GL_INVALID_VALUE, "count cannot be negative.");
return error::kNoError;
}
std::vector<GLenum> attachments_copy(attachments, attachments + count);
if (IsEmulatedFramebufferBound(target)) {
// Update the attachment do the equivalent one in the emulated framebuffer
if (!ModifyAttachmentsForEmulatedFramebuffer(&attachments_copy)) {
InsertError(GL_INVALID_OPERATION, "Invalid attachment.");
return error::kNoError;
}
}
api()->glInvalidateFramebufferFn(target, count, attachments_copy.data());
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoInvalidateSubFramebuffer(
GLenum target,
GLsizei count,
const volatile GLenum* attachments,
GLint x,
GLint y,
GLsizei width,
GLsizei height) {
// Validate that count is non-negative before allocating a vector
if (count < 0) {
InsertError(GL_INVALID_VALUE, "count cannot be negative.");
return error::kNoError;
}
std::vector<GLenum> attachments_copy(attachments, attachments + count);
if (IsEmulatedFramebufferBound(target)) {
// Update the attachment do the equivalent one in the emulated framebuffer
if (!ModifyAttachmentsForEmulatedFramebuffer(&attachments_copy)) {
InsertError(GL_INVALID_OPERATION, "Invalid attachment.");
return error::kNoError;
}
}
api()->glInvalidateSubFramebufferFn(target, count, attachments_copy.data(), x,
y, width, height);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoIsBuffer(GLuint buffer,
uint32_t* result) {
*result =
api()->glIsBufferFn(GetBufferServiceID(api(), buffer, resources_, false));
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoIsEnabled(GLenum cap,
uint32_t* result) {
*result = api()->glIsEnabledFn(cap);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoIsFramebuffer(GLuint framebuffer,
uint32_t* result) {
*result = api()->glIsFramebufferEXTFn(
GetFramebufferServiceID(api(), framebuffer, &framebuffer_id_map_, false));
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoIsProgram(GLuint program,
uint32_t* result) {
*result = api()->glIsProgramFn(GetProgramServiceID(program, resources_));
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoIsRenderbuffer(GLuint renderbuffer,
uint32_t* result) {
*result = api()->glIsRenderbufferEXTFn(
GetRenderbufferServiceID(api(), renderbuffer, resources_, false));
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoIsSampler(GLuint sampler,
uint32_t* result) {
*result = api()->glIsSamplerFn(GetSamplerServiceID(sampler, resources_));
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoIsShader(GLuint shader,
uint32_t* result) {
*result = api()->glIsShaderFn(GetShaderServiceID(shader, resources_));
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoIsSync(GLuint sync,
uint32_t* result) {
*result = api()->glIsSyncFn(GetSyncServiceID(sync, resources_));
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoIsTexture(GLuint texture,
uint32_t* result) {
*result = api()->glIsTextureFn(
GetTextureServiceID(api(), texture, resources_, false));
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoIsTransformFeedback(
GLuint transformfeedback,
uint32_t* result) {
*result = api()->glIsTransformFeedbackFn(GetTransformFeedbackServiceID(
transformfeedback, &transform_feedback_id_map_));
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoLineWidth(GLfloat width) {
api()->glLineWidthFn(width);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoLinkProgram(GLuint program) {
api()->glLinkProgramFn(GetProgramServiceID(program, resources_));
// Program linking can be very slow. Exit command processing to allow for
// context preemption and GPU watchdog checks.
ExitCommandProcessingEarly();
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoMemoryBarrierEXT(
GLbitfield barriers) {
api()->glMemoryBarrierEXTFn(barriers);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoMemoryBarrierByRegion(
GLbitfield barriers) {
api()->glMemoryBarrierByRegionFn(barriers);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoMultiDrawArraysWEBGL(
GLenum mode,
const GLint* firsts,
const GLsizei* counts,
GLsizei drawcount) {
api()->glMultiDrawArraysANGLEFn(mode, firsts, counts, drawcount);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoMultiDrawArraysInstancedWEBGL(
GLenum mode,
const GLint* firsts,
const GLsizei* counts,
const GLsizei* instanceCounts,
GLsizei drawcount) {
api()->glMultiDrawArraysInstancedANGLEFn(mode, firsts, counts, instanceCounts,
drawcount);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoMultiDrawElementsWEBGL(
GLenum mode,
const GLsizei* counts,
GLenum type,
const GLvoid* const* indices,
GLsizei drawcount) {
api()->glMultiDrawElementsANGLEFn(mode, counts, type, indices, drawcount);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoMultiDrawElementsInstancedWEBGL(
GLenum mode,
const GLsizei* counts,
GLenum type,
const GLvoid* const* indices,
const GLsizei* instanceCounts,
GLsizei drawcount) {
api()->glMultiDrawElementsInstancedANGLEFn(mode, counts, type, indices,
instanceCounts, drawcount);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoPauseTransformFeedback() {
api()->glPauseTransformFeedbackFn();
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoPixelStorei(GLenum pname,
GLint param) {
api()->glPixelStoreiFn(pname, param);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoPolygonOffset(GLfloat factor,
GLfloat units) {
api()->glPolygonOffsetFn(factor, units);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoReadBuffer(GLenum src) {
api()->glReadBufferFn(src);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoReadPixels(GLint x,
GLint y,
GLsizei width,
GLsizei height,
GLenum format,
GLenum type,
GLsizei bufsize,
GLsizei* length,
GLsizei* columns,
GLsizei* rows,
void* pixels,
int32_t* success) {
CheckErrorCallbackState();
ScopedPackStateRowLengthReset reset_row_length(
api(), bufsize != 0 && feature_info_->gl_version_info().is_es3);
api()->glReadPixelsRobustANGLEFn(x, y, width, height, format, type, bufsize,
length, columns, rows, pixels);
*success = CheckErrorCallbackState() ? 0 : 1;
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoReadPixelsAsync(
GLint x,
GLint y,
GLsizei width,
GLsizei height,
GLenum format,
GLenum type,
GLsizei bufsize,
GLsizei* length,
GLsizei* columns,
GLsizei* rows,
uint32_t pixels_shm_id,
uint32_t pixels_shm_offset,
uint32_t result_shm_id,
uint32_t result_shm_offset) {
DCHECK(feature_info_->feature_flags().use_async_readpixels &&
bound_buffers_[GL_PIXEL_PACK_BUFFER] == 0);
CheckErrorCallbackState();
ScopedPackStateRowLengthReset reset_row_length(
api(), bufsize != 0 && feature_info_->gl_version_info().is_es3);
PendingReadPixels pending_read_pixels;
pending_read_pixels.pixels_shm_id = pixels_shm_id;
pending_read_pixels.pixels_shm_offset = pixels_shm_offset;
pending_read_pixels.result_shm_id = result_shm_id;
pending_read_pixels.result_shm_offset = result_shm_offset;
api()->glGenBuffersARBFn(1, &pending_read_pixels.buffer_service_id);
api()->glBindBufferFn(GL_PIXEL_PACK_BUFFER_ARB,
pending_read_pixels.buffer_service_id);
// GL_STREAM_READ is not available until ES3.
const GLenum usage_hint = feature_info_->gl_version_info().IsAtLeastGLES(3, 0)
? GL_STREAM_READ
: GL_STATIC_DRAW;
const uint32_t bytes_per_pixel =
GLES2Util::ComputeImageGroupSize(format, type);
if (bytes_per_pixel == 0) {
InsertError(GL_INVALID_ENUM, "Invalid ReadPixels format or type.");
return error::kNoError;
}
if (width < 0 || height < 0) {
InsertError(GL_INVALID_VALUE, "Width and height cannot be negative.");
return error::kNoError;
}
if (!base::CheckMul(bytes_per_pixel, width, height)
.AssignIfValid(&pending_read_pixels.pixels_size)) {
return error::kOutOfBounds;
}
api()->glBufferDataFn(GL_PIXEL_PACK_BUFFER_ARB,
pending_read_pixels.pixels_size, nullptr, usage_hint);
// No need to worry about ES3 pixel pack parameters, because no
// PIXEL_PACK_BUFFER is bound, and all these settings haven't been
// sent to GL.
api()->glReadPixelsFn(x, y, width, height, format, type, nullptr);
api()->glBindBufferFn(GL_PIXEL_PACK_BUFFER_ARB, 0);
// Test for errors now before creating a fence
if (CheckErrorCallbackState()) {
return error::kNoError;
}
pending_read_pixels.fence = gl::GLFence::Create();
if (CheckErrorCallbackState()) {
return error::kNoError;
}
pending_read_pixels_.push_back(std::move(pending_read_pixels));
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoReleaseShaderCompiler() {
api()->glReleaseShaderCompilerFn();
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoRenderbufferStorage(
GLenum target,
GLenum internalformat,
GLsizei width,
GLsizei height) {
api()->glRenderbufferStorageEXTFn(target, internalformat, width, height);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoResumeTransformFeedback() {
api()->glResumeTransformFeedbackFn();
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoSampleCoverage(GLclampf value,
GLboolean invert) {
api()->glSampleCoverageFn(value, invert);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoSamplerParameterf(GLuint sampler,
GLenum pname,
GLfloat param) {
api()->glSamplerParameterfFn(GetSamplerServiceID(sampler, resources_), pname,
param);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoSamplerParameterfv(
GLuint sampler,
GLenum pname,
const volatile GLfloat* params) {
std::array<GLfloat, 1> params_copy{{params[0]}};
api()->glSamplerParameterfvRobustANGLEFn(
GetSamplerServiceID(sampler, resources_), pname,
static_cast<GLsizei>(params_copy.size()), params_copy.data());
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoSamplerParameteri(GLuint sampler,
GLenum pname,
GLint param) {
api()->glSamplerParameteriFn(GetSamplerServiceID(sampler, resources_), pname,
param);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoSamplerParameteriv(
GLuint sampler,
GLenum pname,
const volatile GLint* params) {
std::array<GLint, 1> params_copy{{params[0]}};
api()->glSamplerParameterivRobustANGLEFn(
GetSamplerServiceID(sampler, resources_), pname,
static_cast<GLsizei>(params_copy.size()), params_copy.data());
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoScissor(GLint x,
GLint y,
GLsizei width,
GLsizei height) {
CheckErrorCallbackState();
gfx::Vector2d scissor_offset = GetSurfaceDrawOffset();
api()->glScissorFn(x + scissor_offset.x(), y + scissor_offset.y(), width,
height);
if (CheckErrorCallbackState()) {
// Skip any state tracking updates if an error was generated
return error::kNoError;
}
AssignGLRectangle(scissor_, x, y, width, height);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoShaderBinary(GLsizei n,
const GLuint* shaders,
GLenum binaryformat,
const void* binary,
GLsizei length) {
std::vector<GLuint> service_shaders(n, 0);
for (GLsizei i = 0; i < n; i++) {
service_shaders[i] = GetShaderServiceID(shaders[i], resources_);
}
api()->glShaderBinaryFn(n, service_shaders.data(), binaryformat, binary,
length);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoShaderSource(GLuint shader,
GLsizei count,
const char** string,
const GLint* length) {
api()->glShaderSourceFn(GetShaderServiceID(shader, resources_), count, string,
length);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoStencilFunc(GLenum func,
GLint ref,
GLuint mask) {
api()->glStencilFuncFn(func, ref, mask);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoStencilFuncSeparate(GLenum face,
GLenum func,
GLint ref,
GLuint mask) {
api()->glStencilFuncSeparateFn(face, func, ref, mask);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoStencilMask(GLuint mask) {
api()->glStencilMaskFn(mask);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoStencilMaskSeparate(GLenum face,
GLuint mask) {
api()->glStencilMaskSeparateFn(face, mask);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoStencilOp(GLenum fail,
GLenum zfail,
GLenum zpass) {
api()->glStencilOpFn(fail, zfail, zpass);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoStencilOpSeparate(GLenum face,
GLenum fail,
GLenum zfail,
GLenum zpass) {
api()->glStencilOpSeparateFn(face, fail, zfail, zpass);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoTexImage2D(GLenum target,
GLint level,
GLint internalformat,
GLsizei width,
GLsizei height,
GLint border,
GLenum format,
GLenum type,
GLsizei image_size,
const void* pixels) {
ScopedUnpackStateButAlignmentReset reset_unpack(
api(), image_size != 0 && feature_info_->gl_version_info().is_es3, false);
CheckErrorCallbackState();
api()->glTexImage2DRobustANGLEFn(target, level, internalformat, width, height,
border, format, type, image_size, pixels);
if (CheckErrorCallbackState()) {
return error::kNoError;
}
UpdateTextureSizeFromTarget(target);
// Texture data upload can be slow. Exit command processing to allow for
// context preemption and GPU watchdog checks.
ExitCommandProcessingEarly();
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoTexImage3D(GLenum target,
GLint level,
GLint internalformat,
GLsizei width,
GLsizei height,
GLsizei depth,
GLint border,
GLenum format,
GLenum type,
GLsizei image_size,
const void* pixels) {
ScopedUnpackStateButAlignmentReset reset_unpack(
api(), image_size != 0 && feature_info_->gl_version_info().is_es3, true);
CheckErrorCallbackState();
api()->glTexImage3DRobustANGLEFn(target, level, internalformat, width, height,
depth, border, format, type, image_size,
pixels);
if (CheckErrorCallbackState()) {
return error::kNoError;
}
UpdateTextureSizeFromTarget(target);
// Texture data upload can be slow. Exit command processing to allow for
// context preemption and GPU watchdog checks.
ExitCommandProcessingEarly();
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoTexParameterf(GLenum target,
GLenum pname,
GLfloat param) {
api()->glTexParameterfFn(target, pname, param);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoTexParameterfv(
GLenum target,
GLenum pname,
const volatile GLfloat* params) {
std::array<GLfloat, 1> params_copy{{params[0]}};
api()->glTexParameterfvRobustANGLEFn(target, pname,
static_cast<GLsizei>(params_copy.size()),
params_copy.data());
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoTexParameteri(GLenum target,
GLenum pname,
GLint param) {
api()->glTexParameteriFn(target, pname, param);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoTexParameteriv(
GLenum target,
GLenum pname,
const volatile GLint* params) {
std::array<GLint, 1> params_copy{{params[0]}};
api()->glTexParameterivRobustANGLEFn(target, pname,
static_cast<GLsizei>(params_copy.size()),
params_copy.data());
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoTexStorage3D(GLenum target,
GLsizei levels,
GLenum internalFormat,
GLsizei width,
GLsizei height,
GLsizei depth) {
CheckErrorCallbackState();
api()->glTexStorage3DFn(target, levels, internalFormat, width, height, depth);
if (CheckErrorCallbackState()) {
return error::kNoError;
}
UpdateTextureSizeFromTarget(target);
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoTexSubImage2D(GLenum target,
GLint level,
GLint xoffset,
GLint yoffset,
GLsizei width,
GLsizei height,
GLenum format,
GLenum type,
GLsizei image_size,
const void* pixels) {
ScopedUnpackStateButAlignmentReset reset_unpack(
api(), image_size != 0 && feature_info_->gl_version_info().is_es3, false);
api()->glTexSubImage2DRobustANGLEFn(target, level, xoffset, yoffset, width,
height, format, type, image_size, pixels);
// Texture data upload can be slow. Exit command processing to allow for
// context preemption and GPU watchdog checks.
ExitCommandProcessingEarly();
return error::kNoError;
}
error::Error GLES2DecoderPassthroughImpl::DoTexSubImage3D(GLenum target,
GLint level,
GLint xoffset,
GLint yoffset,
GLint zoffset,
GLsizei width,
GLsizei height,
GLsizei depth,
GLenum format,
GLenum type,
GLsizei image_size,
const void* pixels) {
ScopedUnpackStateButAlignmentReset reset_unpack(