summaryrefslogtreecommitdiff
path: root/chromium/gpu/command_buffer/client/gles2_implementation.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/gpu/command_buffer/client/gles2_implementation.cc')
-rw-r--r--chromium/gpu/command_buffer/client/gles2_implementation.cc3831
1 files changed, 3831 insertions, 0 deletions
diff --git a/chromium/gpu/command_buffer/client/gles2_implementation.cc b/chromium/gpu/command_buffer/client/gles2_implementation.cc
new file mode 100644
index 00000000000..9285b6d82ef
--- /dev/null
+++ b/chromium/gpu/command_buffer/client/gles2_implementation.cc
@@ -0,0 +1,3831 @@
+// Copyright (c) 2012 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.
+
+// A class to emulate GLES2 over command buffers.
+
+#include "gpu/command_buffer/client/gles2_implementation.h"
+
+#include <algorithm>
+#include <map>
+#include <queue>
+#include <set>
+#include <limits>
+#include <stdio.h>
+#include <string.h>
+#include <GLES2/gl2ext.h>
+#include <GLES2/gl2extchromium.h>
+#include "gpu/command_buffer/client/buffer_tracker.h"
+#include "gpu/command_buffer/client/gpu_memory_buffer_tracker.h"
+#include "gpu/command_buffer/client/mapped_memory.h"
+#include "gpu/command_buffer/client/program_info_manager.h"
+#include "gpu/command_buffer/client/query_tracker.h"
+#include "gpu/command_buffer/client/transfer_buffer.h"
+#include "gpu/command_buffer/client/vertex_array_object_manager.h"
+#include "gpu/command_buffer/common/gles2_cmd_utils.h"
+#include "gpu/command_buffer/common/trace_event.h"
+#include "ui/gfx/gpu_memory_buffer.h"
+
+#if defined(__native_client__) && !defined(GLES2_SUPPORT_CLIENT_SIDE_ARRAYS)
+#define GLES2_SUPPORT_CLIENT_SIDE_ARRAYS
+#endif
+
+#if defined(GPU_CLIENT_DEBUG)
+#include "ui/gl/gl_switches.h"
+#include "base/command_line.h"
+#endif
+
+namespace gpu {
+namespace gles2 {
+
+// A 32-bit and 64-bit compatible way of converting a pointer to a GLuint.
+static GLuint ToGLuint(const void* ptr) {
+ return static_cast<GLuint>(reinterpret_cast<size_t>(ptr));
+}
+
+#if !defined(_MSC_VER)
+const size_t GLES2Implementation::kMaxSizeOfSimpleResult;
+const unsigned int GLES2Implementation::kStartingOffset;
+#endif
+
+GLES2Implementation::GLStaticState::GLStaticState() {
+}
+
+GLES2Implementation::GLStaticState::~GLStaticState() {
+}
+
+GLES2Implementation::GLStaticState::IntState::IntState()
+ : max_combined_texture_image_units(0),
+ max_cube_map_texture_size(0),
+ max_fragment_uniform_vectors(0),
+ max_renderbuffer_size(0),
+ max_texture_image_units(0),
+ max_texture_size(0),
+ max_varying_vectors(0),
+ max_vertex_attribs(0),
+ max_vertex_texture_image_units(0),
+ max_vertex_uniform_vectors(0),
+ num_compressed_texture_formats(0),
+ num_shader_binary_formats(0) {
+}
+
+GLES2Implementation::SingleThreadChecker::SingleThreadChecker(
+ GLES2Implementation* gles2_implementation)
+ : gles2_implementation_(gles2_implementation) {
+ GPU_CHECK_EQ(0, gles2_implementation_->use_count_);
+ ++gles2_implementation_->use_count_;
+}
+
+GLES2Implementation::SingleThreadChecker::~SingleThreadChecker() {
+ --gles2_implementation_->use_count_;
+ GPU_CHECK_EQ(0, gles2_implementation_->use_count_);
+}
+
+GLES2Implementation::GLES2Implementation(
+ GLES2CmdHelper* helper,
+ ShareGroup* share_group,
+ TransferBufferInterface* transfer_buffer,
+ bool bind_generates_resource,
+ ImageFactory* image_factory)
+ : helper_(helper),
+ transfer_buffer_(transfer_buffer),
+ angle_pack_reverse_row_order_status_(kUnknownExtensionStatus),
+ chromium_framebuffer_multisample_(kUnknownExtensionStatus),
+ pack_alignment_(4),
+ unpack_alignment_(4),
+ unpack_flip_y_(false),
+ unpack_row_length_(0),
+ unpack_skip_rows_(0),
+ unpack_skip_pixels_(0),
+ pack_reverse_row_order_(false),
+ active_texture_unit_(0),
+ bound_framebuffer_(0),
+ bound_read_framebuffer_(0),
+ bound_renderbuffer_(0),
+ current_program_(0),
+ bound_array_buffer_id_(0),
+ bound_pixel_pack_transfer_buffer_id_(0),
+ bound_pixel_unpack_transfer_buffer_id_(0),
+ error_bits_(0),
+ debug_(false),
+ use_count_(0),
+ current_query_(NULL),
+ error_message_callback_(NULL),
+ image_factory_(image_factory) {
+ GPU_DCHECK(helper);
+ GPU_DCHECK(transfer_buffer);
+
+ char temp[128];
+ sprintf(temp, "%p", static_cast<void*>(this));
+ this_in_hex_ = std::string(temp);
+
+ GPU_CLIENT_LOG_CODE_BLOCK({
+ debug_ = CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kEnableGPUClientLogging);
+ });
+
+ share_group_ =
+ (share_group ? share_group : new ShareGroup(bind_generates_resource));
+
+ memset(&reserved_ids_, 0, sizeof(reserved_ids_));
+}
+
+bool GLES2Implementation::Initialize(
+ unsigned int starting_transfer_buffer_size,
+ unsigned int min_transfer_buffer_size,
+ unsigned int max_transfer_buffer_size) {
+ GPU_DCHECK_GE(starting_transfer_buffer_size, min_transfer_buffer_size);
+ GPU_DCHECK_LE(starting_transfer_buffer_size, max_transfer_buffer_size);
+ GPU_DCHECK_GE(min_transfer_buffer_size, kStartingOffset);
+
+ if (!transfer_buffer_->Initialize(
+ starting_transfer_buffer_size,
+ kStartingOffset,
+ min_transfer_buffer_size,
+ max_transfer_buffer_size,
+ kAlignment,
+ kSizeToFlush)) {
+ return false;
+ }
+
+ mapped_memory_.reset(new MappedMemoryManager(helper_));
+ SetSharedMemoryChunkSizeMultiple(1024 * 1024 * 2);
+
+ if (!QueryAndCacheStaticState())
+ return false;
+
+ util_.set_num_compressed_texture_formats(
+ static_state_.int_state.num_compressed_texture_formats);
+ util_.set_num_shader_binary_formats(
+ static_state_.int_state.num_shader_binary_formats);
+
+ texture_units_.reset(
+ new TextureUnit[
+ static_state_.int_state.max_combined_texture_image_units]);
+
+ query_tracker_.reset(new QueryTracker(mapped_memory_.get()));
+ buffer_tracker_.reset(new BufferTracker(mapped_memory_.get()));
+ gpu_memory_buffer_tracker_.reset(new GpuMemoryBufferTracker(image_factory_));
+
+#if defined(GLES2_SUPPORT_CLIENT_SIDE_ARRAYS)
+ GetIdHandler(id_namespaces::kBuffers)->MakeIds(
+ this, kClientSideArrayId, arraysize(reserved_ids_), &reserved_ids_[0]);
+#endif
+
+ vertex_array_object_manager_.reset(new VertexArrayObjectManager(
+ static_state_.int_state.max_vertex_attribs,
+ reserved_ids_[0],
+ reserved_ids_[1]));
+
+ return true;
+}
+
+bool GLES2Implementation::QueryAndCacheStaticState() {
+ // Setup query for multiple GetIntegerv's
+ static const GLenum pnames[] = {
+ GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS,
+ GL_MAX_CUBE_MAP_TEXTURE_SIZE,
+ GL_MAX_FRAGMENT_UNIFORM_VECTORS,
+ GL_MAX_RENDERBUFFER_SIZE,
+ GL_MAX_TEXTURE_IMAGE_UNITS,
+ GL_MAX_TEXTURE_SIZE,
+ GL_MAX_VARYING_VECTORS,
+ GL_MAX_VERTEX_ATTRIBS,
+ GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS,
+ GL_MAX_VERTEX_UNIFORM_VECTORS,
+ GL_NUM_COMPRESSED_TEXTURE_FORMATS,
+ GL_NUM_SHADER_BINARY_FORMATS,
+ };
+
+ GetMultipleIntegervState integerv_state(
+ pnames, arraysize(pnames),
+ &static_state_.int_state.max_combined_texture_image_units,
+ sizeof(static_state_.int_state));
+ if (!GetMultipleIntegervSetup(&integerv_state)) {
+ return false;
+ }
+
+ // Setup query for multiple GetShaderPrecisionFormat's
+ static const GLenum precision_params[][2] = {
+ { GL_VERTEX_SHADER, GL_LOW_INT },
+ { GL_VERTEX_SHADER, GL_MEDIUM_INT },
+ { GL_VERTEX_SHADER, GL_HIGH_INT },
+ { GL_VERTEX_SHADER, GL_LOW_FLOAT },
+ { GL_VERTEX_SHADER, GL_MEDIUM_FLOAT },
+ { GL_VERTEX_SHADER, GL_HIGH_FLOAT },
+ { GL_FRAGMENT_SHADER, GL_LOW_INT },
+ { GL_FRAGMENT_SHADER, GL_MEDIUM_INT },
+ { GL_FRAGMENT_SHADER, GL_HIGH_INT },
+ { GL_FRAGMENT_SHADER, GL_LOW_FLOAT },
+ { GL_FRAGMENT_SHADER, GL_MEDIUM_FLOAT },
+ { GL_FRAGMENT_SHADER, GL_HIGH_FLOAT },
+ };
+
+ GetAllShaderPrecisionFormatsState precision_state(
+ precision_params, arraysize(precision_params));
+ GetAllShaderPrecisionFormatsSetup(&precision_state);
+
+ // Allocate and partition transfer buffer for all requests
+ void* buffer = transfer_buffer_->Alloc(
+ integerv_state.transfer_buffer_size_needed +
+ precision_state.transfer_buffer_size_needed);
+ if (!buffer) {
+ SetGLError(GL_OUT_OF_MEMORY, "QueryAndCacheStaticState",
+ "Transfer buffer allocation failed.");
+ return false;
+ }
+ integerv_state.buffer = buffer;
+ precision_state.results_buffer =
+ static_cast<char*>(buffer) + integerv_state.transfer_buffer_size_needed;
+
+ // Make all the requests and wait once for all the results.
+ GetMultipleIntegervRequest(&integerv_state);
+ GetAllShaderPrecisionFormatsRequest(&precision_state);
+ WaitForCmd();
+ GetMultipleIntegervOnCompleted(&integerv_state);
+ GetAllShaderPrecisionFormatsOnCompleted(&precision_state);
+
+ // TODO(gman): We should be able to free without a token.
+ transfer_buffer_->FreePendingToken(buffer, helper_->InsertToken());
+ CheckGLError();
+
+ return true;
+}
+
+GLES2Implementation::~GLES2Implementation() {
+ // Make sure the queries are finished otherwise we'll delete the
+ // shared memory (mapped_memory_) which will free the memory used
+ // by the queries. The GPU process when validating that memory is still
+ // shared will fail and abort (ie, it will stop running).
+ WaitForCmd();
+ query_tracker_.reset();
+
+#if defined(GLES2_SUPPORT_CLIENT_SIDE_ARRAYS)
+ DeleteBuffers(arraysize(reserved_ids_), &reserved_ids_[0]);
+#endif
+ buffer_tracker_.reset();
+
+ // Make sure the commands make it the service.
+ WaitForCmd();
+}
+
+GLES2CmdHelper* GLES2Implementation::helper() const {
+ return helper_;
+}
+
+IdHandlerInterface* GLES2Implementation::GetIdHandler(int namespace_id) const {
+ return share_group_->GetIdHandler(namespace_id);
+}
+
+void* GLES2Implementation::GetResultBuffer() {
+ return transfer_buffer_->GetResultBuffer();
+}
+
+int32 GLES2Implementation::GetResultShmId() {
+ return transfer_buffer_->GetShmId();
+}
+
+uint32 GLES2Implementation::GetResultShmOffset() {
+ return transfer_buffer_->GetResultOffset();
+}
+
+void GLES2Implementation::SetSharedMemoryChunkSizeMultiple(
+ unsigned int multiple) {
+ mapped_memory_->set_chunk_size_multiple(multiple);
+}
+
+void GLES2Implementation::FreeUnusedSharedMemory() {
+ mapped_memory_->FreeUnused();
+}
+
+void GLES2Implementation::FreeEverything() {
+ WaitForCmd();
+ query_tracker_->Shrink();
+ FreeUnusedSharedMemory();
+ transfer_buffer_->Free();
+ helper_->FreeRingBuffer();
+}
+
+void GLES2Implementation::WaitForCmd() {
+ TRACE_EVENT0("gpu", "GLES2::WaitForCmd");
+ helper_->CommandBufferHelper::Finish();
+}
+
+bool GLES2Implementation::IsExtensionAvailable(const char* ext) {
+ const char* extensions =
+ reinterpret_cast<const char*>(GetStringHelper(GL_EXTENSIONS));
+ if (!extensions)
+ return false;
+
+ int length = strlen(ext);
+ while (true) {
+ int n = strcspn(extensions, " ");
+ if (n == length && 0 == strncmp(ext, extensions, length)) {
+ return true;
+ }
+ if ('\0' == extensions[n]) {
+ return false;
+ }
+ extensions += n + 1;
+ }
+}
+
+bool GLES2Implementation::IsExtensionAvailableHelper(
+ const char* extension, ExtensionStatus* status) {
+ switch (*status) {
+ case kAvailableExtensionStatus:
+ return true;
+ case kUnavailableExtensionStatus:
+ return false;
+ default: {
+ bool available = IsExtensionAvailable(extension);
+ *status = available ? kAvailableExtensionStatus :
+ kUnavailableExtensionStatus;
+ return available;
+ }
+ }
+}
+
+bool GLES2Implementation::IsAnglePackReverseRowOrderAvailable() {
+ return IsExtensionAvailableHelper(
+ "GL_ANGLE_pack_reverse_row_order",
+ &angle_pack_reverse_row_order_status_);
+}
+
+bool GLES2Implementation::IsChromiumFramebufferMultisampleAvailable() {
+ return IsExtensionAvailableHelper(
+ "GL_CHROMIUM_framebuffer_multisample",
+ &chromium_framebuffer_multisample_);
+}
+
+const std::string& GLES2Implementation::GetLogPrefix() const {
+ const std::string& prefix(debug_marker_manager_.GetMarker());
+ return prefix.empty() ? this_in_hex_ : prefix;
+}
+
+GLenum GLES2Implementation::GetError() {
+ GPU_CLIENT_SINGLE_THREAD_CHECK();
+ GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glGetError()");
+ GLenum err = GetGLError();
+ GPU_CLIENT_LOG("returned " << GLES2Util::GetStringError(err));
+ return err;
+}
+
+GLenum GLES2Implementation::GetClientSideGLError() {
+ if (error_bits_ == 0) {
+ return GL_NO_ERROR;
+ }
+
+ GLenum error = GL_NO_ERROR;
+ for (uint32 mask = 1; mask != 0; mask = mask << 1) {
+ if ((error_bits_ & mask) != 0) {
+ error = GLES2Util::GLErrorBitToGLError(mask);
+ break;
+ }
+ }
+ error_bits_ &= ~GLES2Util::GLErrorToErrorBit(error);
+ return error;
+}
+
+GLenum GLES2Implementation::GetGLError() {
+ TRACE_EVENT0("gpu", "GLES2::GetGLError");
+ // Check the GL error first, then our wrapped error.
+ typedef cmds::GetError::Result Result;
+ Result* result = GetResultAs<Result*>();
+ // If we couldn't allocate a result the context is lost.
+ if (!result) {
+ return GL_NO_ERROR;
+ }
+ *result = GL_NO_ERROR;
+ helper_->GetError(GetResultShmId(), GetResultShmOffset());
+ WaitForCmd();
+ GLenum error = *result;
+ if (error == GL_NO_ERROR) {
+ error = GetClientSideGLError();
+ } else {
+ // There was an error, clear the corresponding wrapped error.
+ error_bits_ &= ~GLES2Util::GLErrorToErrorBit(error);
+ }
+ return error;
+}
+
+#if defined(GL_CLIENT_FAIL_GL_ERRORS)
+void GLES2Implementation::FailGLError(GLenum error) {
+ if (error != GL_NO_ERROR) {
+ GPU_NOTREACHED() << "Error";
+ }
+}
+// NOTE: Calling GetGLError overwrites data in the result buffer.
+void GLES2Implementation::CheckGLError() {
+ FailGLError(GetGLError());
+}
+#endif // defined(GPU_CLIENT_FAIL_GL_ERRORS)
+
+void GLES2Implementation::SetGLError(
+ GLenum error, const char* function_name, const char* msg) {
+ GPU_CLIENT_LOG("[" << GetLogPrefix() << "] Client Synthesized Error: "
+ << GLES2Util::GetStringError(error) << ": "
+ << function_name << ": " << msg);
+ FailGLError(error);
+ if (msg) {
+ last_error_ = msg;
+ }
+ if (error_message_callback_) {
+ std::string temp(GLES2Util::GetStringError(error) + " : " +
+ function_name + ": " + (msg ? msg : ""));
+ error_message_callback_->OnErrorMessage(temp.c_str(), 0);
+ }
+ error_bits_ |= GLES2Util::GLErrorToErrorBit(error);
+}
+
+void GLES2Implementation::SetGLErrorInvalidEnum(
+ const char* function_name, GLenum value, const char* label) {
+ SetGLError(GL_INVALID_ENUM, function_name,
+ (std::string(label) + " was " +
+ GLES2Util::GetStringEnum(value)).c_str());
+}
+
+bool GLES2Implementation::GetBucketContents(uint32 bucket_id,
+ std::vector<int8>* data) {
+ TRACE_EVENT0("gpu", "GLES2::GetBucketContents");
+ GPU_DCHECK(data);
+ const uint32 kStartSize = 32 * 1024;
+ ScopedTransferBufferPtr buffer(kStartSize, helper_, transfer_buffer_);
+ if (!buffer.valid()) {
+ return false;
+ }
+ typedef cmd::GetBucketStart::Result Result;
+ Result* result = GetResultAs<Result*>();
+ if (!result) {
+ return false;
+ }
+ *result = 0;
+ helper_->GetBucketStart(
+ bucket_id, GetResultShmId(), GetResultShmOffset(),
+ buffer.size(), buffer.shm_id(), buffer.offset());
+ WaitForCmd();
+ uint32 size = *result;
+ data->resize(size);
+ if (size > 0u) {
+ uint32 offset = 0;
+ while (size) {
+ if (!buffer.valid()) {
+ buffer.Reset(size);
+ if (!buffer.valid()) {
+ return false;
+ }
+ helper_->GetBucketData(
+ bucket_id, offset, buffer.size(), buffer.shm_id(), buffer.offset());
+ WaitForCmd();
+ }
+ uint32 size_to_copy = std::min(size, buffer.size());
+ memcpy(&(*data)[offset], buffer.address(), size_to_copy);
+ offset += size_to_copy;
+ size -= size_to_copy;
+ buffer.Release();
+ };
+ // Free the bucket. This is not required but it does free up the memory.
+ // and we don't have to wait for the result so from the client's perspective
+ // it's cheap.
+ helper_->SetBucketSize(bucket_id, 0);
+ }
+ return true;
+}
+
+void GLES2Implementation::SetBucketContents(
+ uint32 bucket_id, const void* data, size_t size) {
+ GPU_DCHECK(data);
+ helper_->SetBucketSize(bucket_id, size);
+ if (size > 0u) {
+ uint32 offset = 0;
+ while (size) {
+ ScopedTransferBufferPtr buffer(size, helper_, transfer_buffer_);
+ if (!buffer.valid()) {
+ return;
+ }
+ memcpy(buffer.address(), static_cast<const int8*>(data) + offset,
+ buffer.size());
+ helper_->SetBucketData(
+ bucket_id, offset, buffer.size(), buffer.shm_id(), buffer.offset());
+ offset += buffer.size();
+ size -= buffer.size();
+ }
+ }
+}
+
+void GLES2Implementation::SetBucketAsCString(
+ uint32 bucket_id, const char* str) {
+ // NOTE: strings are passed NULL terminated. That means the empty
+ // string will have a size of 1 and no-string will have a size of 0
+ if (str) {
+ SetBucketContents(bucket_id, str, strlen(str) + 1);
+ } else {
+ helper_->SetBucketSize(bucket_id, 0);
+ }
+}
+
+bool GLES2Implementation::GetBucketAsString(
+ uint32 bucket_id, std::string* str) {
+ GPU_DCHECK(str);
+ std::vector<int8> data;
+ // NOTE: strings are passed NULL terminated. That means the empty
+ // string will have a size of 1 and no-string will have a size of 0
+ if (!GetBucketContents(bucket_id, &data)) {
+ return false;
+ }
+ if (data.empty()) {
+ return false;
+ }
+ str->assign(&data[0], &data[0] + data.size() - 1);
+ return true;
+}
+
+void GLES2Implementation::SetBucketAsString(
+ uint32 bucket_id, const std::string& str) {
+ // NOTE: strings are passed NULL terminated. That means the empty
+ // string will have a size of 1 and no-string will have a size of 0
+ SetBucketContents(bucket_id, str.c_str(), str.size() + 1);
+}
+
+void GLES2Implementation::Disable(GLenum cap) {
+ GPU_CLIENT_SINGLE_THREAD_CHECK();
+ GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glDisable("
+ << GLES2Util::GetStringCapability(cap) << ")");
+ bool changed = false;
+ if (!state_.SetCapabilityState(cap, false, &changed) || changed) {
+ helper_->Disable(cap);
+ }
+ CheckGLError();
+}
+
+void GLES2Implementation::Enable(GLenum cap) {
+ GPU_CLIENT_SINGLE_THREAD_CHECK();
+ GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glEnable("
+ << GLES2Util::GetStringCapability(cap) << ")");
+ bool changed = false;
+ if (!state_.SetCapabilityState(cap, true, &changed) || changed) {
+ helper_->Enable(cap);
+ }
+ CheckGLError();
+}
+
+GLboolean GLES2Implementation::IsEnabled(GLenum cap) {
+ GPU_CLIENT_SINGLE_THREAD_CHECK();
+ GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glIsEnabled("
+ << GLES2Util::GetStringCapability(cap) << ")");
+ bool state = false;
+ if (!state_.GetEnabled(cap, &state)) {
+ typedef cmds::IsEnabled::Result Result;
+ Result* result = GetResultAs<Result*>();
+ if (!result) {
+ return GL_FALSE;
+ }
+ *result = 0;
+ helper_->IsEnabled(cap, GetResultShmId(), GetResultShmOffset());
+ WaitForCmd();
+ state = (*result) != 0;
+ }
+
+ GPU_CLIENT_LOG("returned " << state);
+ CheckGLError();
+ return state;
+}
+
+bool GLES2Implementation::GetHelper(GLenum pname, GLint* params) {
+ switch (pname) {
+ case GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS:
+ *params = static_state_.int_state.max_combined_texture_image_units;
+ return true;
+ case GL_MAX_CUBE_MAP_TEXTURE_SIZE:
+ *params = static_state_.int_state.max_cube_map_texture_size;
+ return true;
+ case GL_MAX_FRAGMENT_UNIFORM_VECTORS:
+ *params = static_state_.int_state.max_fragment_uniform_vectors;
+ return true;
+ case GL_MAX_RENDERBUFFER_SIZE:
+ *params = static_state_.int_state.max_renderbuffer_size;
+ return true;
+ case GL_MAX_TEXTURE_IMAGE_UNITS:
+ *params = static_state_.int_state.max_texture_image_units;
+ return true;
+ case GL_MAX_TEXTURE_SIZE:
+ *params = static_state_.int_state.max_texture_size;
+ return true;
+ case GL_MAX_VARYING_VECTORS:
+ *params = static_state_.int_state.max_varying_vectors;
+ return true;
+ case GL_MAX_VERTEX_ATTRIBS:
+ *params = static_state_.int_state.max_vertex_attribs;
+ return true;
+ case GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS:
+ *params = static_state_.int_state.max_vertex_texture_image_units;
+ return true;
+ case GL_MAX_VERTEX_UNIFORM_VECTORS:
+ *params = static_state_.int_state.max_vertex_uniform_vectors;
+ return true;
+ case GL_NUM_COMPRESSED_TEXTURE_FORMATS:
+ *params = static_state_.int_state.num_compressed_texture_formats;
+ return true;
+ case GL_NUM_SHADER_BINARY_FORMATS:
+ *params = static_state_.int_state.num_shader_binary_formats;
+ return true;
+ case GL_ARRAY_BUFFER_BINDING:
+ if (share_group_->bind_generates_resource()) {
+ *params = bound_array_buffer_id_;
+ return true;
+ }
+ return false;
+ case GL_ELEMENT_ARRAY_BUFFER_BINDING:
+ if (share_group_->bind_generates_resource()) {
+ *params =
+ vertex_array_object_manager_->bound_element_array_buffer();
+ return true;
+ }
+ return false;
+ case GL_PIXEL_PACK_TRANSFER_BUFFER_BINDING_CHROMIUM:
+ *params = bound_pixel_pack_transfer_buffer_id_;
+ return true;
+ case GL_PIXEL_UNPACK_TRANSFER_BUFFER_BINDING_CHROMIUM:
+ *params = bound_pixel_unpack_transfer_buffer_id_;
+ return true;
+ case GL_ACTIVE_TEXTURE:
+ *params = active_texture_unit_ + GL_TEXTURE0;
+ return true;
+ case GL_TEXTURE_BINDING_2D:
+ if (share_group_->bind_generates_resource()) {
+ *params = texture_units_[active_texture_unit_].bound_texture_2d;
+ return true;
+ }
+ return false;
+ case GL_TEXTURE_BINDING_CUBE_MAP:
+ if (share_group_->bind_generates_resource()) {
+ *params = texture_units_[active_texture_unit_].bound_texture_cube_map;
+ return true;
+ }
+ return false;
+ case GL_FRAMEBUFFER_BINDING:
+ if (share_group_->bind_generates_resource()) {
+ *params = bound_framebuffer_;
+ return true;
+ }
+ return false;
+ case GL_READ_FRAMEBUFFER_BINDING:
+ if (IsChromiumFramebufferMultisampleAvailable() &&
+ share_group_->bind_generates_resource()) {
+ *params = bound_read_framebuffer_;
+ return true;
+ }
+ return false;
+ case GL_RENDERBUFFER_BINDING:
+ if (share_group_->bind_generates_resource()) {
+ *params = bound_renderbuffer_;
+ return true;
+ }
+ return false;
+ default:
+ return false;
+ }
+}
+
+bool GLES2Implementation::GetBooleanvHelper(GLenum pname, GLboolean* params) {
+ // TODO(gman): Make this handle pnames that return more than 1 value.
+ GLint value;
+ if (!GetHelper(pname, &value)) {
+ return false;
+ }
+ *params = static_cast<GLboolean>(value);
+ return true;
+}
+
+bool GLES2Implementation::GetFloatvHelper(GLenum pname, GLfloat* params) {
+ // TODO(gman): Make this handle pnames that return more than 1 value.
+ GLint value;
+ if (!GetHelper(pname, &value)) {
+ return false;
+ }
+ *params = static_cast<GLfloat>(value);
+ return true;
+}
+
+bool GLES2Implementation::GetIntegervHelper(GLenum pname, GLint* params) {
+ return GetHelper(pname, params);
+}
+
+GLuint GLES2Implementation::GetMaxValueInBufferCHROMIUMHelper(
+ GLuint buffer_id, GLsizei count, GLenum type, GLuint offset) {
+ typedef cmds::GetMaxValueInBufferCHROMIUM::Result Result;
+ Result* result = GetResultAs<Result*>();
+ if (!result) {
+ return 0;
+ }
+ *result = 0;
+ helper_->GetMaxValueInBufferCHROMIUM(
+ buffer_id, count, type, offset, GetResultShmId(), GetResultShmOffset());
+ WaitForCmd();
+ return *result;
+}
+
+GLuint GLES2Implementation::GetMaxValueInBufferCHROMIUM(
+ GLuint buffer_id, GLsizei count, GLenum type, GLuint offset) {
+ GPU_CLIENT_SINGLE_THREAD_CHECK();
+ GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glGetMaxValueInBufferCHROMIUM("
+ << buffer_id << ", " << count << ", "
+ << GLES2Util::GetStringGetMaxIndexType(type)
+ << ", " << offset << ")");
+ GLuint result = GetMaxValueInBufferCHROMIUMHelper(
+ buffer_id, count, type, offset);
+ GPU_CLIENT_LOG("returned " << result);
+ CheckGLError();
+ return result;
+}
+
+void GLES2Implementation::RestoreElementAndArrayBuffers(bool restore) {
+ if (restore) {
+ RestoreArrayBuffer(restore);
+ // Restore the element array binding.
+ // We only need to restore it if it wasn't a client side array.
+ if (vertex_array_object_manager_->bound_element_array_buffer() == 0) {
+ helper_->BindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+ }
+ }
+}
+
+void GLES2Implementation::RestoreArrayBuffer(bool restore) {
+ if (restore) {
+ // Restore the user's current binding.
+ helper_->BindBuffer(GL_ARRAY_BUFFER, bound_array_buffer_id_);
+ }
+}
+
+void GLES2Implementation::DrawElements(
+ GLenum mode, GLsizei count, GLenum type, const void* indices) {
+ GPU_CLIENT_SINGLE_THREAD_CHECK();
+ GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glDrawElements("
+ << GLES2Util::GetStringDrawMode(mode) << ", "
+ << count << ", "
+ << GLES2Util::GetStringIndexType(type) << ", "
+ << static_cast<const void*>(indices) << ")");
+ if (count < 0) {
+ SetGLError(GL_INVALID_VALUE, "glDrawElements", "count less than 0.");
+ return;
+ }
+ if (count == 0) {
+ return;
+ }
+ GLuint offset = 0;
+ bool simulated = false;
+ if (!vertex_array_object_manager_->SetupSimulatedIndexAndClientSideBuffers(
+ "glDrawElements", this, helper_, count, type, 0, indices,
+ &offset, &simulated)) {
+ return;
+ }
+ helper_->DrawElements(mode, count, type, offset);
+ RestoreElementAndArrayBuffers(simulated);
+ CheckGLError();
+}
+
+void GLES2Implementation::Flush() {
+ GPU_CLIENT_SINGLE_THREAD_CHECK();
+ GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glFlush()");
+ // Insert the cmd to call glFlush
+ helper_->Flush();
+ // Flush our command buffer
+ // (tell the service to execute up to the flush cmd.)
+ helper_->CommandBufferHelper::Flush();
+}
+
+void GLES2Implementation::ShallowFlushCHROMIUM() {
+ GPU_CLIENT_SINGLE_THREAD_CHECK();
+ GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glShallowFlushCHROMIUM()");
+ // Flush our command buffer
+ // (tell the service to execute up to the flush cmd.)
+ helper_->CommandBufferHelper::Flush();
+}
+
+void GLES2Implementation::Finish() {
+ GPU_CLIENT_SINGLE_THREAD_CHECK();
+ FinishHelper();
+}
+
+void GLES2Implementation::ShallowFinishCHROMIUM() {
+ GPU_CLIENT_SINGLE_THREAD_CHECK();
+ // Flush our command buffer (tell the service to execute up to the flush cmd
+ // and don't return until it completes).
+ helper_->CommandBufferHelper::Finish();
+}
+
+bool GLES2Implementation::MustBeContextLost() {
+ bool context_lost = helper_->IsContextLost();
+ if (!context_lost) {
+ WaitForCmd();
+ context_lost = helper_->IsContextLost();
+ }
+ GPU_CHECK(context_lost);
+ return context_lost;
+}
+
+void GLES2Implementation::FinishHelper() {
+ GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glFinish()");
+ TRACE_EVENT0("gpu", "GLES2::Finish");
+ // Insert the cmd to call glFinish
+ helper_->Finish();
+ // Finish our command buffer
+ // (tell the service to execute up to the Finish cmd and wait for it to
+ // execute.)
+ helper_->CommandBufferHelper::Finish();
+}
+
+void GLES2Implementation::SwapBuffers() {
+ GPU_CLIENT_SINGLE_THREAD_CHECK();
+ GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glSwapBuffers()");
+ // TODO(piman): Strictly speaking we'd want to insert the token after the
+ // swap, but the state update with the updated token might not have happened
+ // by the time the SwapBuffer callback gets called, forcing us to synchronize
+ // with the GPU process more than needed. So instead, make it happen before.
+ // All it means is that we could be slightly looser on the kMaxSwapBuffers
+ // semantics if the client doesn't use the callback mechanism, and by chance
+ // the scheduler yields between the InsertToken and the SwapBuffers.
+ swap_buffers_tokens_.push(helper_->InsertToken());
+ helper_->SwapBuffers();
+ helper_->CommandBufferHelper::Flush();
+ // Wait if we added too many swap buffers. Add 1 to kMaxSwapBuffers to
+ // compensate for TODO above.
+ if (swap_buffers_tokens_.size() > kMaxSwapBuffers + 1) {
+ helper_->WaitForToken(swap_buffers_tokens_.front());
+ swap_buffers_tokens_.pop();
+ }
+}
+
+void GLES2Implementation::GenSharedIdsCHROMIUM(
+ GLuint namespace_id, GLuint id_offset, GLsizei n, GLuint* ids) {
+ GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glGenSharedIdsCHROMIUM("
+ << namespace_id << ", " << id_offset << ", " << n << ", " <<
+ static_cast<void*>(ids) << ")");
+ TRACE_EVENT0("gpu", "GLES2::GenSharedIdsCHROMIUM");
+ GLsizei num = n;
+ GLuint* dst = ids;
+ while (num) {
+ ScopedTransferBufferArray<GLint> id_buffer(num, helper_, transfer_buffer_);
+ if (!id_buffer.valid()) {
+ return;
+ }
+ helper_->GenSharedIdsCHROMIUM(
+ namespace_id, id_offset, id_buffer.num_elements(),
+ id_buffer.shm_id(), id_buffer.offset());
+ WaitForCmd();
+ memcpy(dst, id_buffer.address(), sizeof(*dst) * id_buffer.num_elements());
+ num -= id_buffer.num_elements();
+ dst += id_buffer.num_elements();
+ }
+ GPU_CLIENT_LOG_CODE_BLOCK({
+ for (GLsizei i = 0; i < n; ++i) {
+ GPU_CLIENT_LOG(" " << i << ": " << namespace_id << ", " << ids[i]);
+ }
+ });
+}
+
+void GLES2Implementation::DeleteSharedIdsCHROMIUM(
+ GLuint namespace_id, GLsizei n, const GLuint* ids) {
+ GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glDeleteSharedIdsCHROMIUM("
+ << namespace_id << ", " << n << ", "
+ << static_cast<const void*>(ids) << ")");
+ GPU_CLIENT_LOG_CODE_BLOCK({
+ for (GLsizei i = 0; i < n; ++i) {
+ GPU_CLIENT_LOG(" " << i << ": " << namespace_id << ", " << ids[i]);
+ }
+ });
+ TRACE_EVENT0("gpu", "GLES2::DeleteSharedIdsCHROMIUM");
+ while (n) {
+ ScopedTransferBufferArray<GLint> id_buffer(n, helper_, transfer_buffer_);
+ if (!id_buffer.valid()) {
+ return;
+ }
+ memcpy(id_buffer.address(), ids, sizeof(*ids) * id_buffer.num_elements());
+ helper_->DeleteSharedIdsCHROMIUM(
+ namespace_id, id_buffer.num_elements(),
+ id_buffer.shm_id(), id_buffer.offset());
+ WaitForCmd();
+ n -= id_buffer.num_elements();
+ ids += id_buffer.num_elements();
+ }
+}
+
+void GLES2Implementation::RegisterSharedIdsCHROMIUM(
+ GLuint namespace_id, GLsizei n, const GLuint* ids) {
+ GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glRegisterSharedIdsCHROMIUM("
+ << namespace_id << ", " << n << ", "
+ << static_cast<const void*>(ids) << ")");
+ GPU_CLIENT_LOG_CODE_BLOCK({
+ for (GLsizei i = 0; i < n; ++i) {
+ GPU_CLIENT_LOG(" " << i << ": " << namespace_id << ", " << ids[i]);
+ }
+ });
+ TRACE_EVENT0("gpu", "GLES2::RegisterSharedIdsCHROMIUM");
+ while (n) {
+ ScopedTransferBufferArray<GLint> id_buffer(n, helper_, transfer_buffer_);
+ if (!id_buffer.valid()) {
+ return;
+ }
+ memcpy(id_buffer.address(), ids, sizeof(*ids) * id_buffer.num_elements());
+ helper_->RegisterSharedIdsCHROMIUM(
+ namespace_id, id_buffer.num_elements(),
+ id_buffer.shm_id(), id_buffer.offset());
+ WaitForCmd();
+ n -= id_buffer.num_elements();
+ ids += id_buffer.num_elements();
+ }
+}
+
+void GLES2Implementation::BindAttribLocation(
+ GLuint program, GLuint index, const char* name) {
+ GPU_CLIENT_SINGLE_THREAD_CHECK();
+ GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glBindAttribLocation("
+ << program << ", " << index << ", " << name << ")");
+ SetBucketAsString(kResultBucketId, name);
+ helper_->BindAttribLocationBucket(program, index, kResultBucketId);
+ helper_->SetBucketSize(kResultBucketId, 0);
+ CheckGLError();
+}
+
+void GLES2Implementation::BindUniformLocationCHROMIUM(
+ GLuint program, GLint location, const char* name) {
+ GPU_CLIENT_SINGLE_THREAD_CHECK();
+ GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glBindUniformLocationCHROMIUM("
+ << program << ", " << location << ", " << name << ")");
+ SetBucketAsString(kResultBucketId, name);
+ helper_->BindUniformLocationCHROMIUMBucket(
+ program, location, kResultBucketId);
+ helper_->SetBucketSize(kResultBucketId, 0);
+ CheckGLError();
+}
+
+void GLES2Implementation::GetVertexAttribPointerv(
+ GLuint index, GLenum pname, void** ptr) {
+ GPU_CLIENT_SINGLE_THREAD_CHECK();
+ GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glGetVertexAttribPointer("
+ << index << ", " << GLES2Util::GetStringVertexPointer(pname) << ", "
+ << static_cast<void*>(ptr) << ")");
+ GPU_CLIENT_LOG_CODE_BLOCK(int32 num_results = 1);
+ if (!vertex_array_object_manager_->GetAttribPointer(index, pname, ptr)) {
+ TRACE_EVENT0("gpu", "GLES2::GetVertexAttribPointerv");
+ typedef cmds::GetVertexAttribPointerv::Result Result;
+ Result* result = GetResultAs<Result*>();
+ if (!result) {
+ return;
+ }
+ result->SetNumResults(0);
+ helper_->GetVertexAttribPointerv(
+ index, pname, GetResultShmId(), GetResultShmOffset());
+ WaitForCmd();
+ result->CopyResult(ptr);
+ GPU_CLIENT_LOG_CODE_BLOCK(num_results = result->GetNumResults());
+ }
+ GPU_CLIENT_LOG_CODE_BLOCK({
+ for (int32 i = 0; i < num_results; ++i) {
+ GPU_CLIENT_LOG(" " << i << ": " << ptr[i]);
+ }
+ });
+ CheckGLError();
+}
+
+bool GLES2Implementation::DeleteProgramHelper(GLuint program) {
+ if (!GetIdHandler(id_namespaces::kProgramsAndShaders)->FreeIds(
+ this, 1, &program, &GLES2Implementation::DeleteProgramStub)) {
+ SetGLError(
+ GL_INVALID_VALUE,
+ "glDeleteProgram", "id not created by this context.");
+ return false;
+ }
+ if (program == current_program_) {
+ current_program_ = 0;
+ }
+ return true;
+}
+
+void GLES2Implementation::DeleteProgramStub(
+ GLsizei n, const GLuint* programs) {
+ GPU_DCHECK_EQ(1, n);
+ share_group_->program_info_manager()->DeleteInfo(programs[0]);
+ helper_->DeleteProgram(programs[0]);
+}
+
+bool GLES2Implementation::DeleteShaderHelper(GLuint shader) {
+ if (!GetIdHandler(id_namespaces::kProgramsAndShaders)->FreeIds(
+ this, 1, &shader, &GLES2Implementation::DeleteShaderStub)) {
+ SetGLError(
+ GL_INVALID_VALUE,
+ "glDeleteShader", "id not created by this context.");
+ return false;
+ }
+ return true;
+}
+
+void GLES2Implementation::DeleteShaderStub(
+ GLsizei n, const GLuint* shaders) {
+ GPU_DCHECK_EQ(1, n);
+ share_group_->program_info_manager()->DeleteInfo(shaders[0]);
+ helper_->DeleteShader(shaders[0]);
+}
+
+
+GLint GLES2Implementation::GetAttribLocationHelper(
+ GLuint program, const char* name) {
+ typedef cmds::GetAttribLocationBucket::Result Result;
+ Result* result = GetResultAs<Result*>();
+ if (!result) {
+ return -1;
+ }
+ *result = -1;
+ SetBucketAsCString(kResultBucketId, name);
+ helper_->GetAttribLocationBucket(
+ program, kResultBucketId, GetResultShmId(), GetResultShmOffset());
+ WaitForCmd();
+ helper_->SetBucketSize(kResultBucketId, 0);
+ return *result;
+}
+
+GLint GLES2Implementation::GetAttribLocation(
+ GLuint program, const char* name) {
+ GPU_CLIENT_SINGLE_THREAD_CHECK();
+ GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glGetAttribLocation(" << program
+ << ", " << name << ")");
+ TRACE_EVENT0("gpu", "GLES2::GetAttribLocation");
+ GLint loc = share_group_->program_info_manager()->GetAttribLocation(
+ this, program, name);
+ GPU_CLIENT_LOG("returned " << loc);
+ CheckGLError();
+ return loc;
+}
+
+GLint GLES2Implementation::GetUniformLocationHelper(
+ GLuint program, const char* name) {
+ typedef cmds::GetUniformLocationBucket::Result Result;
+ Result* result = GetResultAs<Result*>();
+ if (!result) {
+ return -1;
+ }
+ *result = -1;
+ SetBucketAsCString(kResultBucketId, name);
+ helper_->GetUniformLocationBucket(program, kResultBucketId,
+ GetResultShmId(), GetResultShmOffset());
+ WaitForCmd();
+ helper_->SetBucketSize(kResultBucketId, 0);
+ return *result;
+}
+
+GLint GLES2Implementation::GetUniformLocation(
+ GLuint program, const char* name) {
+ GPU_CLIENT_SINGLE_THREAD_CHECK();
+ GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glGetUniformLocation(" << program
+ << ", " << name << ")");
+ TRACE_EVENT0("gpu", "GLES2::GetUniformLocation");
+ GLint loc = share_group_->program_info_manager()->GetUniformLocation(
+ this, program, name);
+ GPU_CLIENT_LOG("returned " << loc);
+ CheckGLError();
+ return loc;
+}
+
+void GLES2Implementation::UseProgram(GLuint program) {
+ GPU_CLIENT_SINGLE_THREAD_CHECK();
+ GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glUseProgram(" << program << ")");
+ if (current_program_ != program) {
+ current_program_ = program;
+ helper_->UseProgram(program);
+ }
+ CheckGLError();
+}
+
+bool GLES2Implementation::GetProgramivHelper(
+ GLuint program, GLenum pname, GLint* params) {
+ bool got_value = share_group_->program_info_manager()->GetProgramiv(
+ this, program, pname, params);
+ GPU_CLIENT_LOG_CODE_BLOCK({
+ if (got_value) {
+ GPU_CLIENT_LOG(" 0: " << *params);
+ }
+ });
+ return got_value;
+}
+
+void GLES2Implementation::LinkProgram(GLuint program) {
+ GPU_CLIENT_SINGLE_THREAD_CHECK();
+ GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glLinkProgram(" << program << ")");
+ helper_->LinkProgram(program);
+ share_group_->program_info_manager()->CreateInfo(program);
+ CheckGLError();
+}
+
+void GLES2Implementation::ShaderBinary(
+ GLsizei n, const GLuint* shaders, GLenum binaryformat, const void* binary,
+ GLsizei length) {
+ GPU_CLIENT_SINGLE_THREAD_CHECK();
+ GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glShaderBinary(" << n << ", "
+ << static_cast<const void*>(shaders) << ", "
+ << GLES2Util::GetStringEnum(binaryformat) << ", "
+ << static_cast<const void*>(binary) << ", "
+ << length << ")");
+ if (n < 0) {
+ SetGLError(GL_INVALID_VALUE, "glShaderBinary", "n < 0.");
+ return;
+ }
+ if (length < 0) {
+ SetGLError(GL_INVALID_VALUE, "glShaderBinary", "length < 0.");
+ return;
+ }
+ // TODO(gman): ShaderBinary should use buckets.
+ unsigned int shader_id_size = n * sizeof(*shaders);
+ ScopedTransferBufferArray<GLint> buffer(
+ shader_id_size + length, helper_, transfer_buffer_);
+ if (!buffer.valid() || buffer.num_elements() != shader_id_size + length) {
+ SetGLError(GL_OUT_OF_MEMORY, "glShaderBinary", "out of memory.");
+ return;
+ }
+ void* shader_ids = buffer.elements();
+ void* shader_data = buffer.elements() + shader_id_size;
+ memcpy(shader_ids, shaders, shader_id_size);
+ memcpy(shader_data, binary, length);
+ helper_->ShaderBinary(
+ n,
+ buffer.shm_id(),
+ buffer.offset(),
+ binaryformat,
+ buffer.shm_id(),
+ buffer.offset() + shader_id_size,
+ length);
+ CheckGLError();
+}
+
+void GLES2Implementation::PixelStorei(GLenum pname, GLint param) {
+ GPU_CLIENT_SINGLE_THREAD_CHECK();
+ GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glPixelStorei("
+ << GLES2Util::GetStringPixelStore(pname) << ", "
+ << param << ")");
+ switch (pname) {
+ case GL_PACK_ALIGNMENT:
+ pack_alignment_ = param;
+ break;
+ case GL_UNPACK_ALIGNMENT:
+ unpack_alignment_ = param;
+ break;
+ case GL_UNPACK_ROW_LENGTH:
+ unpack_row_length_ = param;
+ return;
+ case GL_UNPACK_SKIP_ROWS:
+ unpack_skip_rows_ = param;
+ return;
+ case GL_UNPACK_SKIP_PIXELS:
+ unpack_skip_pixels_ = param;
+ return;
+ case GL_UNPACK_FLIP_Y_CHROMIUM:
+ unpack_flip_y_ = (param != 0);
+ break;
+ case GL_PACK_REVERSE_ROW_ORDER_ANGLE:
+ pack_reverse_row_order_ =
+ IsAnglePackReverseRowOrderAvailable() ? (param != 0) : false;
+ break;
+ default:
+ break;
+ }
+ helper_->PixelStorei(pname, param);
+ CheckGLError();
+}
+
+
+void GLES2Implementation::VertexAttribPointer(
+ GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride,
+ const void* ptr) {
+ GPU_CLIENT_SINGLE_THREAD_CHECK();
+ GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glVertexAttribPointer("
+ << index << ", "
+ << size << ", "
+ << GLES2Util::GetStringVertexAttribType(type) << ", "
+ << GLES2Util::GetStringBool(normalized) << ", "
+ << stride << ", "
+ << static_cast<const void*>(ptr) << ")");
+ // Record the info on the client side.
+ if (!vertex_array_object_manager_->SetAttribPointer(
+ bound_array_buffer_id_, index, size, type, normalized, stride, ptr)) {
+ SetGLError(GL_INVALID_OPERATION, "glVertexAttribPointer",
+ "client side arrays are not allowed in vertex array objects.");
+ return;
+ }
+#if defined(GLES2_SUPPORT_CLIENT_SIDE_ARRAYS)
+ if (bound_array_buffer_id_ != 0) {
+ // Only report NON client side buffers to the service.
+ helper_->VertexAttribPointer(index, size, type, normalized, stride,
+ ToGLuint(ptr));
+ }
+#else // !defined(GLES2_SUPPORT_CLIENT_SIDE_ARRAYS)
+ helper_->VertexAttribPointer(index, size, type, normalized, stride,
+ ToGLuint(ptr));
+#endif // !defined(GLES2_SUPPORT_CLIENT_SIDE_ARRAYS)
+ CheckGLError();
+}
+
+void GLES2Implementation::VertexAttribDivisorANGLE(
+ GLuint index, GLuint divisor) {
+ GPU_CLIENT_SINGLE_THREAD_CHECK();
+ GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glVertexAttribDivisorANGLE("
+ << index << ", "
+ << divisor << ") ");
+ // Record the info on the client side.
+ vertex_array_object_manager_->SetAttribDivisor(index, divisor);
+ helper_->VertexAttribDivisorANGLE(index, divisor);
+ CheckGLError();
+}
+
+void GLES2Implementation::ShaderSource(
+ GLuint shader, GLsizei count, const GLchar* const* source, const GLint* length) {
+ GPU_CLIENT_SINGLE_THREAD_CHECK();
+ GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glShaderSource("
+ << shader << ", " << count << ", "
+ << static_cast<const void*>(source) << ", "
+ << static_cast<const void*>(length) << ")");
+ GPU_CLIENT_LOG_CODE_BLOCK({
+ for (GLsizei ii = 0; ii < count; ++ii) {
+ if (source[ii]) {
+ if (length && length[ii] >= 0) {
+ std::string str(source[ii], length[ii]);
+ GPU_CLIENT_LOG(" " << ii << ": ---\n" << str << "\n---");
+ } else {
+ GPU_CLIENT_LOG(" " << ii << ": ---\n" << source[ii] << "\n---");
+ }
+ } else {
+ GPU_CLIENT_LOG(" " << ii << ": NULL");
+ }
+ }
+ });
+ if (count < 0) {
+ SetGLError(GL_INVALID_VALUE, "glShaderSource", "count < 0");
+ return;
+ }
+ if (shader == 0) {
+ SetGLError(GL_INVALID_VALUE, "glShaderSource", "shader == 0");
+ return;
+ }
+
+ // Compute the total size.
+ uint32 total_size = 1;
+ for (GLsizei ii = 0; ii < count; ++ii) {
+ if (source[ii]) {
+ total_size += (length && length[ii] >= 0) ?
+ static_cast<size_t>(length[ii]) : strlen(source[ii]);
+ }
+ }
+
+ // Concatenate all the strings in to a bucket on the service.
+ helper_->SetBucketSize(kResultBucketId, total_size);
+ uint32 offset = 0;
+ for (GLsizei ii = 0; ii <= count; ++ii) {
+ const char* src = ii < count ? source[ii] : "";
+ if (src) {
+ uint32 size = ii < count ?
+ (length ? static_cast<size_t>(length[ii]) : strlen(src)) : 1;
+ while (size) {
+ ScopedTransferBufferPtr buffer(size, helper_, transfer_buffer_);
+ if (!buffer.valid()) {
+ return;
+ }
+ memcpy(buffer.address(), src, buffer.size());
+ helper_->SetBucketData(kResultBucketId, offset, buffer.size(),
+ buffer.shm_id(), buffer.offset());
+ offset += buffer.size();
+ src += buffer.size();
+ size -= buffer.size();
+ }
+ }
+ }
+
+ GPU_DCHECK_EQ(total_size, offset);
+
+ helper_->ShaderSourceBucket(shader, kResultBucketId);
+ helper_->SetBucketSize(kResultBucketId, 0);
+ CheckGLError();
+}
+
+void GLES2Implementation::BufferDataHelper(
+ GLenum target, GLsizeiptr size, const void* data, GLenum usage) {
+ if (size < 0) {
+ SetGLError(GL_INVALID_VALUE, "glBufferData", "size < 0");
+ return;
+ }
+
+ GLuint buffer_id;
+ if (GetBoundPixelTransferBuffer(target, "glBufferData", &buffer_id)) {
+ if (!buffer_id) {
+ return;
+ }
+
+ BufferTracker::Buffer* buffer = buffer_tracker_->GetBuffer(buffer_id);
+ if (buffer) {
+ // Free buffer memory, pending the passage of a token.
+ buffer_tracker_->FreePendingToken(buffer, helper_->InsertToken());
+
+ // Remove old buffer.
+ buffer_tracker_->RemoveBuffer(buffer_id);
+ }
+
+ // Create new buffer.
+ buffer = buffer_tracker_->CreateBuffer(buffer_id, size);
+ GPU_DCHECK(buffer);
+ if (buffer->address() && data)
+ memcpy(buffer->address(), data, size);
+ return;
+ }
+
+ if (size == 0) {
+ return;
+ }
+
+ // If there is no data just send BufferData
+ if (!data) {
+ helper_->BufferData(target, size, 0, 0, usage);
+ return;
+ }
+
+ // See if we can send all at once.
+ ScopedTransferBufferPtr buffer(size, helper_, transfer_buffer_);
+ if (!buffer.valid()) {
+ return;
+ }
+
+ if (buffer.size() >= static_cast<unsigned int>(size)) {
+ memcpy(buffer.address(), data, size);
+ helper_->BufferData(
+ target,
+ size,
+ buffer.shm_id(),
+ buffer.offset(),
+ usage);
+ return;
+ }
+
+ // Make the buffer with BufferData then send via BufferSubData
+ helper_->BufferData(target, size, 0, 0, usage);
+ BufferSubDataHelperImpl(target, 0, size, data, &buffer);
+ CheckGLError();
+}
+
+void GLES2Implementation::BufferData(
+ GLenum target, GLsizeiptr size, const void* data, GLenum usage) {
+ GPU_CLIENT_SINGLE_THREAD_CHECK();
+ GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glBufferData("
+ << GLES2Util::GetStringBufferTarget(target) << ", "
+ << size << ", "
+ << static_cast<const void*>(data) << ", "
+ << GLES2Util::GetStringBufferUsage(usage) << ")");
+ BufferDataHelper(target, size, data, usage);
+ CheckGLError();
+}
+
+void GLES2Implementation::BufferSubDataHelper(
+ GLenum target, GLintptr offset, GLsizeiptr size, const void* data) {
+ if (size == 0) {
+ return;
+ }
+
+ if (size < 0) {
+ SetGLError(GL_INVALID_VALUE, "glBufferSubData", "size < 0");
+ return;
+ }
+
+ GLuint buffer_id;
+ if (GetBoundPixelTransferBuffer(target, "glBufferSubData", &buffer_id)) {
+ if (!buffer_id) {
+ return;
+ }
+ BufferTracker::Buffer* buffer = buffer_tracker_->GetBuffer(buffer_id);
+ if (!buffer) {
+ SetGLError(GL_INVALID_VALUE, "glBufferSubData", "unknown buffer");
+ return;
+ }
+
+ int32 end = 0;
+ int32 buffer_size = buffer->size();
+ if (!SafeAddInt32(offset, size, &end) || end > buffer_size) {
+ SetGLError(GL_INVALID_VALUE, "glBufferSubData", "out of range");
+ return;
+ }
+
+ if (buffer->address() && data)
+ memcpy(static_cast<uint8*>(buffer->address()) + offset, data, size);
+ return;
+ }
+
+ ScopedTransferBufferPtr buffer(size, helper_, transfer_buffer_);
+ BufferSubDataHelperImpl(target, offset, size, data, &buffer);
+}
+
+void GLES2Implementation::BufferSubDataHelperImpl(
+ GLenum target, GLintptr offset, GLsizeiptr size, const void* data,
+ ScopedTransferBufferPtr* buffer) {
+ GPU_DCHECK(buffer);
+ GPU_DCHECK_GT(size, 0);
+
+ const int8* source = static_cast<const int8*>(data);
+ while (size) {
+ if (!buffer->valid() || buffer->size() == 0) {
+ buffer->Reset(size);
+ if (!buffer->valid()) {
+ return;
+ }
+ }
+ memcpy(buffer->address(), source, buffer->size());
+ helper_->BufferSubData(
+ target, offset, buffer->size(), buffer->shm_id(), buffer->offset());
+ offset += buffer->size();
+ source += buffer->size();
+ size -= buffer->size();
+ buffer->Release();
+ }
+}
+
+void GLES2Implementation::BufferSubData(
+ GLenum target, GLintptr offset, GLsizeiptr size, const void* data) {
+ GPU_CLIENT_SINGLE_THREAD_CHECK();
+ GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glBufferSubData("
+ << GLES2Util::GetStringBufferTarget(target) << ", "
+ << offset << ", " << size << ", "
+ << static_cast<const void*>(data) << ")");
+ BufferSubDataHelper(target, offset, size, data);
+ CheckGLError();
+}
+
+bool GLES2Implementation::GetBoundPixelTransferBuffer(
+ GLenum target,
+ const char* function_name,
+ GLuint* buffer_id) {
+ *buffer_id = 0;
+
+ switch (target) {
+ case GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM:
+ *buffer_id = bound_pixel_pack_transfer_buffer_id_;
+ break;
+ case GL_PIXEL_UNPACK_TRANSFER_BUFFER_CHROMIUM:
+ *buffer_id = bound_pixel_unpack_transfer_buffer_id_;
+ break;
+ default:
+ // Unknown target
+ return false;
+ }
+ if (!*buffer_id) {
+ SetGLError(GL_INVALID_OPERATION, function_name, "no buffer bound");
+ }
+ return true;
+}
+
+BufferTracker::Buffer*
+GLES2Implementation::GetBoundPixelUnpackTransferBufferIfValid(
+ GLuint buffer_id,
+ const char* function_name,
+ GLuint offset, GLsizei size)
+{
+ DCHECK(buffer_id);
+ BufferTracker::Buffer* buffer = buffer_tracker_->GetBuffer(buffer_id);
+ if (!buffer) {
+ SetGLError(GL_INVALID_OPERATION, function_name, "invalid buffer");
+ return NULL;
+ }
+ if (buffer->mapped()) {
+ SetGLError(GL_INVALID_OPERATION, function_name, "buffer mapped");
+ return NULL;
+ }
+ if ((buffer->size() - offset) < static_cast<GLuint>(size)) {
+ SetGLError(GL_INVALID_VALUE, function_name, "unpack size to large");
+ return NULL;
+ }
+ return buffer;
+}
+
+void GLES2Implementation::CompressedTexImage2D(
+ GLenum target, GLint level, GLenum internalformat, GLsizei width,
+ GLsizei height, GLint border, GLsizei image_size, const void* data) {
+ GPU_CLIENT_SINGLE_THREAD_CHECK();
+ GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glCompressedTexImage2D("
+ << GLES2Util::GetStringTextureTarget(target) << ", "
+ << level << ", "
+ << GLES2Util::GetStringCompressedTextureFormat(internalformat) << ", "
+ << width << ", " << height << ", " << border << ", "
+ << image_size << ", "
+ << static_cast<const void*>(data) << ")");
+ if (width < 0 || height < 0 || level < 0) {
+ SetGLError(GL_INVALID_VALUE, "glCompressedTexImage2D", "dimension < 0");
+ return;
+ }
+ if (height == 0 || width == 0) {
+ return;
+ }
+ // If there's a pixel unpack buffer bound use it when issuing
+ // CompressedTexImage2D.
+ if (bound_pixel_unpack_transfer_buffer_id_) {
+ GLuint offset = ToGLuint(data);
+ BufferTracker::Buffer* buffer = GetBoundPixelUnpackTransferBufferIfValid(
+ bound_pixel_unpack_transfer_buffer_id_,
+ "glCompressedTexImage2D", offset, image_size);
+ if (buffer && buffer->shm_id() != -1) {
+ helper_->CompressedTexImage2D(
+ target, level, internalformat, width, height, border, image_size,
+ buffer->shm_id(), buffer->shm_offset() + offset);
+ buffer->set_transfer_ready_token(helper_->InsertToken());
+ }
+ return;
+ }
+ SetBucketContents(kResultBucketId, data, image_size);
+ helper_->CompressedTexImage2DBucket(
+ target, level, internalformat, width, height, border, kResultBucketId);
+ // Free the bucket. This is not required but it does free up the memory.
+ // and we don't have to wait for the result so from the client's perspective
+ // it's cheap.
+ helper_->SetBucketSize(kResultBucketId, 0);
+ CheckGLError();
+}
+
+void GLES2Implementation::CompressedTexSubImage2D(
+ GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width,
+ GLsizei height, GLenum format, GLsizei image_size, const void* data) {
+ GPU_CLIENT_SINGLE_THREAD_CHECK();
+ GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glCompressedTexSubImage2D("
+ << GLES2Util::GetStringTextureTarget(target) << ", "
+ << level << ", "
+ << xoffset << ", " << yoffset << ", "
+ << width << ", " << height << ", "
+ << GLES2Util::GetStringCompressedTextureFormat(format) << ", "
+ << image_size << ", "
+ << static_cast<const void*>(data) << ")");
+ if (width < 0 || height < 0 || level < 0) {
+ SetGLError(GL_INVALID_VALUE, "glCompressedTexSubImage2D", "dimension < 0");
+ return;
+ }
+ // If there's a pixel unpack buffer bound use it when issuing
+ // CompressedTexSubImage2D.
+ if (bound_pixel_unpack_transfer_buffer_id_) {
+ GLuint offset = ToGLuint(data);
+ BufferTracker::Buffer* buffer = GetBoundPixelUnpackTransferBufferIfValid(
+ bound_pixel_unpack_transfer_buffer_id_,
+ "glCompressedTexSubImage2D", offset, image_size);
+ if (buffer && buffer->shm_id() != -1) {
+ helper_->CompressedTexSubImage2D(
+ target, level, xoffset, yoffset, width, height, format, image_size,
+ buffer->shm_id(), buffer->shm_offset() + offset);
+ buffer->set_transfer_ready_token(helper_->InsertToken());
+ CheckGLError();
+ }
+ return;
+ }
+ SetBucketContents(kResultBucketId, data, image_size);
+ helper_->CompressedTexSubImage2DBucket(
+ target, level, xoffset, yoffset, width, height, format, kResultBucketId);
+ // Free the bucket. This is not required but it does free up the memory.
+ // and we don't have to wait for the result so from the client's perspective
+ // it's cheap.
+ helper_->SetBucketSize(kResultBucketId, 0);
+ CheckGLError();
+}
+
+namespace {
+
+void CopyRectToBuffer(
+ const void* pixels,
+ uint32 height,
+ uint32 unpadded_row_size,
+ uint32 pixels_padded_row_size,
+ bool flip_y,
+ void* buffer,
+ uint32 buffer_padded_row_size) {
+ const int8* source = static_cast<const int8*>(pixels);
+ int8* dest = static_cast<int8*>(buffer);
+ if (flip_y || pixels_padded_row_size != buffer_padded_row_size) {
+ if (flip_y) {
+ dest += buffer_padded_row_size * (height - 1);
+ }
+ // the last row is copied unpadded at the end
+ for (; height > 1; --height) {
+ memcpy(dest, source, buffer_padded_row_size);
+ if (flip_y) {
+ dest -= buffer_padded_row_size;
+ } else {
+ dest += buffer_padded_row_size;
+ }
+ source += pixels_padded_row_size;
+ }
+ memcpy(dest, source, unpadded_row_size);
+ } else {
+ uint32 size = (height - 1) * pixels_padded_row_size + unpadded_row_size;
+ memcpy(dest, source, size);
+ }
+}
+
+} // anonymous namespace
+
+void GLES2Implementation::TexImage2D(
+ GLenum target, GLint level, GLint internalformat, GLsizei width,
+ GLsizei height, GLint border, GLenum format, GLenum type,
+ const void* pixels) {
+ GPU_CLIENT_SINGLE_THREAD_CHECK();
+ GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glTexImage2D("
+ << GLES2Util::GetStringTextureTarget(target) << ", "
+ << level << ", "
+ << GLES2Util::GetStringTextureInternalFormat(internalformat) << ", "
+ << width << ", " << height << ", " << border << ", "
+ << GLES2Util::GetStringTextureFormat(format) << ", "
+ << GLES2Util::GetStringPixelType(type) << ", "
+ << static_cast<const void*>(pixels) << ")");
+ if (level < 0 || height < 0 || width < 0) {
+ SetGLError(GL_INVALID_VALUE, "glTexImage2D", "dimension < 0");
+ return;
+ }
+ uint32 size;
+ uint32 unpadded_row_size;
+ uint32 padded_row_size;
+ if (!GLES2Util::ComputeImageDataSizes(
+ width, height, format, type, unpack_alignment_, &size,
+ &unpadded_row_size, &padded_row_size)) {
+ SetGLError(GL_INVALID_VALUE, "glTexImage2D", "image size too large");
+ return;
+ }
+
+ // If there's a pixel unpack buffer bound use it when issuing TexImage2D.
+ if (bound_pixel_unpack_transfer_buffer_id_) {
+ GLuint offset = ToGLuint(pixels);
+ BufferTracker::Buffer* buffer = GetBoundPixelUnpackTransferBufferIfValid(
+ bound_pixel_unpack_transfer_buffer_id_,
+ "glTexImage2D", offset, size);
+ if (buffer && buffer->shm_id() != -1) {
+ helper_->TexImage2D(
+ target, level, internalformat, width, height, border, format, type,
+ buffer->shm_id(), buffer->shm_offset() + offset);
+ buffer->set_transfer_ready_token(helper_->InsertToken());
+ CheckGLError();
+ }
+ return;
+ }
+
+ // If there's no data just issue TexImage2D
+ if (!pixels) {
+ helper_->TexImage2D(
+ target, level, internalformat, width, height, border, format, type,
+ 0, 0);
+ CheckGLError();
+ return;
+ }
+
+ // compute the advance bytes per row for the src pixels
+ uint32 src_padded_row_size;
+ if (unpack_row_length_ > 0) {
+ if (!GLES2Util::ComputeImagePaddedRowSize(
+ unpack_row_length_, format, type, unpack_alignment_,
+ &src_padded_row_size)) {
+ SetGLError(
+ GL_INVALID_VALUE, "glTexImage2D", "unpack row length too large");
+ return;
+ }
+ } else {
+ src_padded_row_size = padded_row_size;
+ }
+
+ // advance pixels pointer past the skip rows and skip pixels
+ pixels = reinterpret_cast<const int8*>(pixels) +
+ unpack_skip_rows_ * src_padded_row_size;
+ if (unpack_skip_pixels_) {
+ uint32 group_size = GLES2Util::ComputeImageGroupSize(format, type);
+ pixels = reinterpret_cast<const int8*>(pixels) +
+ unpack_skip_pixels_ * group_size;
+ }
+
+ // Check if we can send it all at once.
+ ScopedTransferBufferPtr buffer(size, helper_, transfer_buffer_);
+ if (!buffer.valid()) {
+ return;
+ }
+
+ if (buffer.size() >= size) {
+ CopyRectToBuffer(
+ pixels, height, unpadded_row_size, src_padded_row_size, unpack_flip_y_,
+ buffer.address(), padded_row_size);
+ helper_->TexImage2D(
+ target, level, internalformat, width, height, border, format, type,
+ buffer.shm_id(), buffer.offset());
+ CheckGLError();
+ return;
+ }
+
+ // No, so send it using TexSubImage2D.
+ helper_->TexImage2D(
+ target, level, internalformat, width, height, border, format, type,
+ 0, 0);
+ TexSubImage2DImpl(
+ target, level, 0, 0, width, height, format, type, unpadded_row_size,
+ pixels, src_padded_row_size, GL_TRUE, &buffer, padded_row_size);
+ CheckGLError();
+}
+
+void GLES2Implementation::TexSubImage2D(
+ GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width,
+ GLsizei height, GLenum format, GLenum type, const void* pixels) {
+ GPU_CLIENT_SINGLE_THREAD_CHECK();
+ GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glTexSubImage2D("
+ << GLES2Util::GetStringTextureTarget(target) << ", "
+ << level << ", "
+ << xoffset << ", " << yoffset << ", "
+ << width << ", " << height << ", "
+ << GLES2Util::GetStringTextureFormat(format) << ", "
+ << GLES2Util::GetStringPixelType(type) << ", "
+ << static_cast<const void*>(pixels) << ")");
+
+ if (level < 0 || height < 0 || width < 0) {
+ SetGLError(GL_INVALID_VALUE, "glTexSubImage2D", "dimension < 0");
+ return;
+ }
+ if (height == 0 || width == 0) {
+ return;
+ }
+
+ uint32 temp_size;
+ uint32 unpadded_row_size;
+ uint32 padded_row_size;
+ if (!GLES2Util::ComputeImageDataSizes(
+ width, height, format, type, unpack_alignment_, &temp_size,
+ &unpadded_row_size, &padded_row_size)) {
+ SetGLError(GL_INVALID_VALUE, "glTexSubImage2D", "size to large");
+ return;
+ }
+
+ // If there's a pixel unpack buffer bound use it when issuing TexSubImage2D.
+ if (bound_pixel_unpack_transfer_buffer_id_) {
+ GLuint offset = ToGLuint(pixels);
+ BufferTracker::Buffer* buffer = GetBoundPixelUnpackTransferBufferIfValid(
+ bound_pixel_unpack_transfer_buffer_id_,
+ "glTexSubImage2D", offset, temp_size);
+ if (buffer && buffer->shm_id() != -1) {
+ helper_->TexSubImage2D(
+ target, level, xoffset, yoffset, width, height, format, type,
+ buffer->shm_id(), buffer->shm_offset() + offset, false);
+ buffer->set_transfer_ready_token(helper_->InsertToken());
+ CheckGLError();
+ }
+ return;
+ }
+
+ // compute the advance bytes per row for the src pixels
+ uint32 src_padded_row_size;
+ if (unpack_row_length_ > 0) {
+ if (!GLES2Util::ComputeImagePaddedRowSize(
+ unpack_row_length_, format, type, unpack_alignment_,
+ &src_padded_row_size)) {
+ SetGLError(
+ GL_INVALID_VALUE, "glTexImage2D", "unpack row length too large");
+ return;
+ }
+ } else {
+ src_padded_row_size = padded_row_size;
+ }
+
+ // advance pixels pointer past the skip rows and skip pixels
+ pixels = reinterpret_cast<const int8*>(pixels) +
+ unpack_skip_rows_ * src_padded_row_size;
+ if (unpack_skip_pixels_) {
+ uint32 group_size = GLES2Util::ComputeImageGroupSize(format, type);
+ pixels = reinterpret_cast<const int8*>(pixels) +
+ unpack_skip_pixels_ * group_size;
+ }
+
+ ScopedTransferBufferPtr buffer(temp_size, helper_, transfer_buffer_);
+ TexSubImage2DImpl(
+ target, level, xoffset, yoffset, width, height, format, type,
+ unpadded_row_size, pixels, src_padded_row_size, GL_FALSE, &buffer,
+ padded_row_size);
+ CheckGLError();
+}
+
+static GLint ComputeNumRowsThatFitInBuffer(
+ GLsizeiptr padded_row_size, GLsizeiptr unpadded_row_size,
+ unsigned int size) {
+ GPU_DCHECK_GE(unpadded_row_size, 0);
+ if (padded_row_size == 0) {
+ return 1;
+ }
+ GLint num_rows = size / padded_row_size;
+ return num_rows + (size - num_rows * padded_row_size) / unpadded_row_size;
+}
+
+void GLES2Implementation::TexSubImage2DImpl(
+ GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width,
+ GLsizei height, GLenum format, GLenum type, uint32 unpadded_row_size,
+ const void* pixels, uint32 pixels_padded_row_size, GLboolean internal,
+ ScopedTransferBufferPtr* buffer, uint32 buffer_padded_row_size) {
+ GPU_DCHECK(buffer);
+ GPU_DCHECK_GE(level, 0);
+ GPU_DCHECK_GT(height, 0);
+ GPU_DCHECK_GT(width, 0);
+
+ const int8* source = reinterpret_cast<const int8*>(pixels);
+ GLint original_yoffset = yoffset;
+ // Transfer by rows.
+ while (height) {
+ unsigned int desired_size =
+ buffer_padded_row_size * (height - 1) + unpadded_row_size;
+ if (!buffer->valid() || buffer->size() == 0) {
+ buffer->Reset(desired_size);
+ if (!buffer->valid()) {
+ return;
+ }
+ }
+
+ GLint num_rows = ComputeNumRowsThatFitInBuffer(
+ buffer_padded_row_size, unpadded_row_size, buffer->size());
+ num_rows = std::min(num_rows, height);
+ CopyRectToBuffer(
+ source, num_rows, unpadded_row_size, pixels_padded_row_size,
+ unpack_flip_y_, buffer->address(), buffer_padded_row_size);
+ GLint y = unpack_flip_y_ ? original_yoffset + height - num_rows : yoffset;
+ helper_->TexSubImage2D(
+ target, level, xoffset, y, width, num_rows, format, type,
+ buffer->shm_id(), buffer->offset(), internal);
+ buffer->Release();
+ yoffset += num_rows;
+ source += num_rows * pixels_padded_row_size;
+ height -= num_rows;
+ }
+}
+
+bool GLES2Implementation::GetActiveAttribHelper(
+ GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size,
+ GLenum* type, char* name) {
+ // Clear the bucket so if the command fails nothing will be in it.
+ helper_->SetBucketSize(kResultBucketId, 0);
+ typedef cmds::GetActiveAttrib::Result Result;
+ Result* result = GetResultAs<Result*>();
+ if (!result) {
+ return false;
+ }
+ // Set as failed so if the command fails we'll recover.
+ result->success = false;
+ helper_->GetActiveAttrib(program, index, kResultBucketId,
+ GetResultShmId(), GetResultShmOffset());
+ WaitForCmd();
+ if (result->success) {
+ if (size) {
+ *size = result->size;
+ }
+ if (type) {
+ *type = result->type;
+ }
+ if (length || name) {
+ std::vector<int8> str;
+ GetBucketContents(kResultBucketId, &str);
+ GLsizei max_size = std::min(static_cast<size_t>(bufsize) - 1,
+ std::max(static_cast<size_t>(0),
+ str.size() - 1));
+ if (length) {
+ *length = max_size;
+ }
+ if (name && bufsize > 0) {
+ memcpy(name, &str[0], max_size);
+ name[max_size] = '\0';
+ }
+ }
+ }
+ return result->success != 0;
+}
+
+void GLES2Implementation::GetActiveAttrib(
+ GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size,
+ GLenum* type, char* name) {
+ GPU_CLIENT_SINGLE_THREAD_CHECK();
+ GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glGetActiveAttrib("
+ << program << ", " << index << ", " << bufsize << ", "
+ << static_cast<const void*>(length) << ", "
+ << static_cast<const void*>(size) << ", "
+ << static_cast<const void*>(type) << ", "
+ << static_cast<const void*>(name) << ", ");
+ if (bufsize < 0) {
+ SetGLError(GL_INVALID_VALUE, "glGetActiveAttrib", "bufsize < 0");
+ return;
+ }
+ TRACE_EVENT0("gpu", "GLES2::GetActiveAttrib");
+ bool success = share_group_->program_info_manager()->GetActiveAttrib(
+ this, program, index, bufsize, length, size, type, name);
+ if (success) {
+ if (size) {
+ GPU_CLIENT_LOG(" size: " << *size);
+ }
+ if (type) {
+ GPU_CLIENT_LOG(" type: " << GLES2Util::GetStringEnum(*type));
+ }
+ if (name) {
+ GPU_CLIENT_LOG(" name: " << name);
+ }
+ }
+ CheckGLError();
+}
+
+bool GLES2Implementation::GetActiveUniformHelper(
+ GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size,
+ GLenum* type, char* name) {
+ // Clear the bucket so if the command fails nothing will be in it.
+ helper_->SetBucketSize(kResultBucketId, 0);
+ typedef cmds::GetActiveUniform::Result Result;
+ Result* result = GetResultAs<Result*>();
+ if (!result) {
+ return false;
+ }
+ // Set as failed so if the command fails we'll recover.
+ result->success = false;
+ helper_->GetActiveUniform(program, index, kResultBucketId,
+ GetResultShmId(), GetResultShmOffset());
+ WaitForCmd();
+ if (result->success) {
+ if (size) {
+ *size = result->size;
+ }
+ if (type) {
+ *type = result->type;
+ }
+ if (length || name) {
+ std::vector<int8> str;
+ GetBucketContents(kResultBucketId, &str);
+ GLsizei max_size = std::min(static_cast<size_t>(bufsize) - 1,
+ std::max(static_cast<size_t>(0),
+ str.size() - 1));
+ if (length) {
+ *length = max_size;
+ }
+ if (name && bufsize > 0) {
+ memcpy(name, &str[0], max_size);
+ name[max_size] = '\0';
+ }
+ }
+ }
+ return result->success != 0;
+}
+
+void GLES2Implementation::GetActiveUniform(
+ GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size,
+ GLenum* type, char* name) {
+ GPU_CLIENT_SINGLE_THREAD_CHECK();
+ GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glGetActiveUniform("
+ << program << ", " << index << ", " << bufsize << ", "
+ << static_cast<const void*>(length) << ", "
+ << static_cast<const void*>(size) << ", "
+ << static_cast<const void*>(type) << ", "
+ << static_cast<const void*>(name) << ", ");
+ if (bufsize < 0) {
+ SetGLError(GL_INVALID_VALUE, "glGetActiveUniform", "bufsize < 0");
+ return;
+ }
+ TRACE_EVENT0("gpu", "GLES2::GetActiveUniform");
+ bool success = share_group_->program_info_manager()->GetActiveUniform(
+ this, program, index, bufsize, length, size, type, name);
+ if (success) {
+ if (size) {
+ GPU_CLIENT_LOG(" size: " << *size);
+ }
+ if (type) {
+ GPU_CLIENT_LOG(" type: " << GLES2Util::GetStringEnum(*type));
+ }
+ if (name) {
+ GPU_CLIENT_LOG(" name: " << name);
+ }
+ }
+ CheckGLError();
+}
+
+void GLES2Implementation::GetAttachedShaders(
+ GLuint program, GLsizei maxcount, GLsizei* count, GLuint* shaders) {
+ GPU_CLIENT_SINGLE_THREAD_CHECK();
+ GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glGetAttachedShaders("
+ << program << ", " << maxcount << ", "
+ << static_cast<const void*>(count) << ", "
+ << static_cast<const void*>(shaders) << ", ");
+ if (maxcount < 0) {
+ SetGLError(GL_INVALID_VALUE, "glGetAttachedShaders", "maxcount < 0");
+ return;
+ }
+ TRACE_EVENT0("gpu", "GLES2::GetAttachedShaders");
+ typedef cmds::GetAttachedShaders::Result Result;
+ uint32 size = Result::ComputeSize(maxcount);
+ Result* result = static_cast<Result*>(transfer_buffer_->Alloc(size));
+ if (!result) {
+ return;
+ }
+ result->SetNumResults(0);
+ helper_->GetAttachedShaders(
+ program,
+ transfer_buffer_->GetShmId(),
+ transfer_buffer_->GetOffset(result),
+ size);
+ int32 token = helper_->InsertToken();
+ WaitForCmd();
+ if (count) {
+ *count = result->GetNumResults();
+ }
+ result->CopyResult(shaders);
+ GPU_CLIENT_LOG_CODE_BLOCK({
+ for (int32 i = 0; i < result->GetNumResults(); ++i) {
+ GPU_CLIENT_LOG(" " << i << ": " << result->GetData()[i]);
+ }
+ });
+ transfer_buffer_->FreePendingToken(result, token);
+ CheckGLError();
+}
+
+void GLES2Implementation::GetShaderPrecisionFormat(
+ GLenum shadertype, GLenum precisiontype, GLint* range, GLint* precision) {
+ GPU_CLIENT_SINGLE_THREAD_CHECK();
+ GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glGetShaderPrecisionFormat("
+ << GLES2Util::GetStringShaderType(shadertype) << ", "
+ << GLES2Util::GetStringShaderPrecision(precisiontype) << ", "
+ << static_cast<const void*>(range) << ", "
+ << static_cast<const void*>(precision) << ", ");
+ TRACE_EVENT0("gpu", "GLES2::GetShaderPrecisionFormat");
+ typedef cmds::GetShaderPrecisionFormat::Result Result;
+ Result* result = GetResultAs<Result*>();
+ if (!result) {
+ return;
+ }
+
+ GLStaticState::ShaderPrecisionKey key(shadertype, precisiontype);
+ GLStaticState::ShaderPrecisionMap::iterator i =
+ static_state_.shader_precisions.find(key);
+ if (i != static_state_.shader_precisions.end()) {
+ *result = i->second;
+ } else {
+ result->success = false;
+ helper_->GetShaderPrecisionFormat(
+ shadertype, precisiontype, GetResultShmId(), GetResultShmOffset());
+ WaitForCmd();
+ if (result->success)
+ static_state_.shader_precisions[key] = *result;
+ }
+
+ if (result->success) {
+ if (range) {
+ range[0] = result->min_range;
+ range[1] = result->max_range;
+ GPU_CLIENT_LOG(" min_range: " << range[0]);
+ GPU_CLIENT_LOG(" min_range: " << range[1]);
+ }
+ if (precision) {
+ precision[0] = result->precision;
+ GPU_CLIENT_LOG(" min_range: " << precision[0]);
+ }
+ }
+ CheckGLError();
+}
+
+const GLubyte* GLES2Implementation::GetStringHelper(GLenum name) {
+ const char* result = NULL;
+ // Clears the bucket so if the command fails nothing will be in it.
+ helper_->SetBucketSize(kResultBucketId, 0);
+ helper_->GetString(name, kResultBucketId);
+ std::string str;
+ if (GetBucketAsString(kResultBucketId, &str)) {
+ // Adds extensions implemented on client side only.
+ switch (name) {
+ case GL_EXTENSIONS:
+ str += std::string(str.empty() ? "" : " ") +
+ "GL_CHROMIUM_flipy "
+ "GL_CHROMIUM_map_sub "
+ "GL_CHROMIUM_shallow_flush "
+ "GL_EXT_unpack_subimage";
+ if (image_factory_ != NULL) {
+ // The first space character is intentional.
+ str += " GL_CHROMIUM_map_image";
+ }
+ break;
+ default:
+ break;
+ }
+
+ // Because of WebGL the extensions can change. We have to cache each unique
+ // result since we don't know when the client will stop referring to a
+ // previous one it queries.
+ GLStringMap::iterator it = gl_strings_.find(name);
+ if (it == gl_strings_.end()) {
+ std::set<std::string> strings;
+ std::pair<GLStringMap::iterator, bool> insert_result =
+ gl_strings_.insert(std::make_pair(name, strings));
+ GPU_DCHECK(insert_result.second);
+ it = insert_result.first;
+ }
+ std::set<std::string>& string_set = it->second;
+ std::set<std::string>::const_iterator sit = string_set.find(str);
+ if (sit != string_set.end()) {
+ result = sit->c_str();
+ } else {
+ std::pair<std::set<std::string>::const_iterator, bool> insert_result =
+ string_set.insert(str);
+ GPU_DCHECK(insert_result.second);
+ result = insert_result.first->c_str();
+ }
+ }
+ return reinterpret_cast<const GLubyte*>(result);
+}
+
+const GLubyte* GLES2Implementation::GetString(GLenum name) {
+ GPU_CLIENT_SINGLE_THREAD_CHECK();
+ GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glGetString("
+ << GLES2Util::GetStringStringType(name) << ")");
+ TRACE_EVENT0("gpu", "GLES2::GetString");
+ const GLubyte* result = GetStringHelper(name);
+ GPU_CLIENT_LOG(" returned " << reinterpret_cast<const char*>(result));
+ CheckGLError();
+ return result;
+}
+
+void GLES2Implementation::GetUniformfv(
+ GLuint program, GLint location, GLfloat* params) {
+ GPU_CLIENT_SINGLE_THREAD_CHECK();
+ GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glGetUniformfv("
+ << program << ", " << location << ", "
+ << static_cast<const void*>(params) << ")");
+ TRACE_EVENT0("gpu", "GLES2::GetUniformfv");
+ typedef cmds::GetUniformfv::Result Result;
+ Result* result = GetResultAs<Result*>();
+ if (!result) {
+ return;
+ }
+ result->SetNumResults(0);
+ helper_->GetUniformfv(
+ program, location, GetResultShmId(), GetResultShmOffset());
+ WaitForCmd();
+ result->CopyResult(params);
+ GPU_CLIENT_LOG_CODE_BLOCK({
+ for (int32 i = 0; i < result->GetNumResults(); ++i) {
+ GPU_CLIENT_LOG(" " << i << ": " << result->GetData()[i]);
+ }
+ });
+ CheckGLError();
+}
+
+void GLES2Implementation::GetUniformiv(
+ GLuint program, GLint location, GLint* params) {
+ GPU_CLIENT_SINGLE_THREAD_CHECK();
+ GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glGetUniformiv("
+ << program << ", " << location << ", "
+ << static_cast<const void*>(params) << ")");
+ TRACE_EVENT0("gpu", "GLES2::GetUniformiv");
+ typedef cmds::GetUniformiv::Result Result;
+ Result* result = GetResultAs<Result*>();
+ if (!result) {
+ return;
+ }
+ result->SetNumResults(0);
+ helper_->GetUniformiv(
+ program, location, GetResultShmId(), GetResultShmOffset());
+ WaitForCmd();
+ GetResultAs<cmds::GetUniformfv::Result*>()->CopyResult(params);
+ GPU_CLIENT_LOG_CODE_BLOCK({
+ for (int32 i = 0; i < result->GetNumResults(); ++i) {
+ GPU_CLIENT_LOG(" " << i << ": " << result->GetData()[i]);
+ }
+ });
+ CheckGLError();
+}
+
+void GLES2Implementation::ReadPixels(
+ GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format,
+ GLenum type, void* pixels) {
+ GPU_CLIENT_SINGLE_THREAD_CHECK();
+ GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glReadPixels("
+ << xoffset << ", " << yoffset << ", "
+ << width << ", " << height << ", "
+ << GLES2Util::GetStringReadPixelFormat(format) << ", "
+ << GLES2Util::GetStringPixelType(type) << ", "
+ << static_cast<const void*>(pixels) << ")");
+ if (width < 0 || height < 0) {
+ SetGLError(GL_INVALID_VALUE, "glReadPixels", "dimensions < 0");
+ return;
+ }
+ if (width == 0 || height == 0) {
+ return;
+ }
+
+ // glReadPixel pads the size of each row of pixels by an amount specified by
+ // glPixelStorei. So, we have to take that into account both in the fact that
+ // the pixels returned from the ReadPixel command will include that padding
+ // and that when we copy the results to the user's buffer we need to not
+ // write those padding bytes but leave them as they are.
+
+ TRACE_EVENT0("gpu", "GLES2::ReadPixels");
+ typedef cmds::ReadPixels::Result Result;
+
+ int8* dest = reinterpret_cast<int8*>(pixels);
+ uint32 temp_size;
+ uint32 unpadded_row_size;
+ uint32 padded_row_size;
+ if (!GLES2Util::ComputeImageDataSizes(
+ width, 2, format, type, pack_alignment_, &temp_size, &unpadded_row_size,
+ &padded_row_size)) {
+ SetGLError(GL_INVALID_VALUE, "glReadPixels", "size too large.");
+ return;
+ }
+
+ if (bound_pixel_pack_transfer_buffer_id_) {
+ GLuint offset = ToGLuint(pixels);
+ BufferTracker::Buffer* buffer = GetBoundPixelUnpackTransferBufferIfValid(
+ bound_pixel_pack_transfer_buffer_id_,
+ "glReadPixels", offset, padded_row_size * height);
+ if (buffer && buffer->shm_id() != -1) {
+ helper_->ReadPixels(xoffset, yoffset, width, height, format, type,
+ buffer->shm_id(), buffer->shm_offset(),
+ 0, 0, true);
+ CheckGLError();
+ }
+ return;
+ }
+
+ if (!pixels) {
+ SetGLError(GL_INVALID_OPERATION, "glReadPixels", "pixels = NULL");
+ return;
+ }
+
+ // Transfer by rows.
+ // The max rows we can transfer.
+ while (height) {
+ GLsizei desired_size = padded_row_size * height - 1 + unpadded_row_size;
+ ScopedTransferBufferPtr buffer(desired_size, helper_, transfer_buffer_);
+ if (!buffer.valid()) {
+ return;
+ }
+ GLint num_rows = ComputeNumRowsThatFitInBuffer(
+ padded_row_size, unpadded_row_size, buffer.size());
+ num_rows = std::min(num_rows, height);
+ // NOTE: We must look up the address of the result area AFTER allocation
+ // of the transfer buffer since the transfer buffer may be reallocated.
+ Result* result = GetResultAs<Result*>();
+ if (!result) {
+ return;
+ }
+ *result = 0; // mark as failed.
+ helper_->ReadPixels(
+ xoffset, yoffset, width, num_rows, format, type,
+ buffer.shm_id(), buffer.offset(),
+ GetResultShmId(), GetResultShmOffset(),
+ false);
+ WaitForCmd();
+ if (*result != 0) {
+ // when doing a y-flip we have to iterate through top-to-bottom chunks
+ // of the dst. The service side handles reversing the rows within a
+ // chunk.
+ int8* rows_dst;
+ if (pack_reverse_row_order_) {
+ rows_dst = dest + (height - num_rows) * padded_row_size;
+ } else {
+ rows_dst = dest;
+ }
+ // We have to copy 1 row at a time to avoid writing pad bytes.
+ const int8* src = static_cast<const int8*>(buffer.address());
+ for (GLint yy = 0; yy < num_rows; ++yy) {
+ memcpy(rows_dst, src, unpadded_row_size);
+ rows_dst += padded_row_size;
+ src += padded_row_size;
+ }
+ if (!pack_reverse_row_order_) {
+ dest = rows_dst;
+ }
+ }
+ // If it was not marked as successful exit.
+ if (*result == 0) {
+ return;
+ }
+ yoffset += num_rows;
+ height -= num_rows;
+ }
+ CheckGLError();
+}
+
+void GLES2Implementation::ActiveTexture(GLenum texture) {
+ GPU_CLIENT_SINGLE_THREAD_CHECK();
+ GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glActiveTexture("
+ << GLES2Util::GetStringEnum(texture) << ")");
+ GLuint texture_index = texture - GL_TEXTURE0;
+ if (texture_index >= static_cast<GLuint>(
+ static_state_.int_state.max_combined_texture_image_units)) {
+ SetGLErrorInvalidEnum(
+ "glActiveTexture", texture, "texture");
+ return;
+ }
+
+ active_texture_unit_ = texture_index;
+ helper_->ActiveTexture(texture);
+ CheckGLError();
+}
+
+void GLES2Implementation::GenBuffersHelper(
+ GLsizei /* n */, const GLuint* /* buffers */) {
+}
+
+void GLES2Implementation::GenFramebuffersHelper(
+ GLsizei /* n */, const GLuint* /* framebuffers */) {
+}
+
+void GLES2Implementation::GenRenderbuffersHelper(
+ GLsizei /* n */, const GLuint* /* renderbuffers */) {
+}
+
+void GLES2Implementation::GenTexturesHelper(
+ GLsizei /* n */, const GLuint* /* textures */) {
+}
+
+void GLES2Implementation::GenVertexArraysOESHelper(
+ GLsizei n, const GLuint* arrays) {
+ vertex_array_object_manager_->GenVertexArrays(n, arrays);
+}
+
+void GLES2Implementation::GenQueriesEXTHelper(
+ GLsizei /* n */, const GLuint* /* queries */) {
+}
+
+// NOTE #1: On old versions of OpenGL, calling glBindXXX with an unused id
+// generates a new resource. On newer versions of OpenGL they don't. The code
+// related to binding below will need to change if we switch to the new OpenGL
+// model. Specifically it assumes a bind will succeed which is always true in
+// the old model but possibly not true in the new model if another context has
+// deleted the resource.
+
+bool GLES2Implementation::BindBufferHelper(
+ GLenum target, GLuint buffer) {
+ // TODO(gman): See note #1 above.
+ bool changed = false;
+ switch (target) {
+ case GL_ARRAY_BUFFER:
+ if (bound_array_buffer_id_ != buffer) {
+ bound_array_buffer_id_ = buffer;
+ changed = true;
+ }
+ break;
+ case GL_ELEMENT_ARRAY_BUFFER:
+ changed = vertex_array_object_manager_->BindElementArray(buffer);
+ break;
+ case GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM:
+ bound_pixel_pack_transfer_buffer_id_ = buffer;
+ break;
+ case GL_PIXEL_UNPACK_TRANSFER_BUFFER_CHROMIUM:
+ bound_pixel_unpack_transfer_buffer_id_ = buffer;
+ break;
+ default:
+ changed = true;
+ break;
+ }
+ // TODO(gman): There's a bug here. If the target is invalid the ID will not be
+ // used even though it's marked it as used here.
+ GetIdHandler(id_namespaces::kBuffers)->MarkAsUsedForBind(buffer);
+ return changed;
+}
+
+bool GLES2Implementation::BindFramebufferHelper(
+ GLenum target, GLuint framebuffer) {
+ // TODO(gman): See note #1 above.
+ bool changed = false;
+ switch (target) {
+ case GL_FRAMEBUFFER:
+ if (bound_framebuffer_ != framebuffer ||
+ bound_read_framebuffer_ != framebuffer) {
+ bound_framebuffer_ = framebuffer;
+ bound_read_framebuffer_ = framebuffer;
+ changed = true;
+ }
+ break;
+ case GL_READ_FRAMEBUFFER:
+ if (!IsChromiumFramebufferMultisampleAvailable()) {
+ SetGLErrorInvalidEnum("glBindFramebuffer", target, "target");
+ return false;
+ }
+ if (bound_read_framebuffer_ != framebuffer) {
+ bound_read_framebuffer_ = framebuffer;
+ changed = true;
+ }
+ break;
+ case GL_DRAW_FRAMEBUFFER:
+ if (!IsChromiumFramebufferMultisampleAvailable()) {
+ SetGLErrorInvalidEnum("glBindFramebuffer", target, "target");
+ return false;
+ }
+ if (bound_framebuffer_ != framebuffer) {
+ bound_framebuffer_ = framebuffer;
+ changed = true;
+ }
+ break;
+ default:
+ SetGLErrorInvalidEnum("glBindFramebuffer", target, "target");
+ return false;
+ }
+ GetIdHandler(id_namespaces::kFramebuffers)->MarkAsUsedForBind(framebuffer);
+ return changed;
+}
+
+bool GLES2Implementation::BindRenderbufferHelper(
+ GLenum target, GLuint renderbuffer) {
+ // TODO(gman): See note #1 above.
+ bool changed = false;
+ switch (target) {
+ case GL_RENDERBUFFER:
+ if (bound_renderbuffer_ != renderbuffer) {
+ bound_renderbuffer_ = renderbuffer;
+ changed = true;
+ }
+ break;
+ default:
+ changed = true;
+ break;
+ }
+ // TODO(gman): There's a bug here. If the target is invalid the ID will not be
+ // used even though it's marked it as used here.
+ GetIdHandler(id_namespaces::kRenderbuffers)->MarkAsUsedForBind(renderbuffer);
+ return changed;
+}
+
+bool GLES2Implementation::BindTextureHelper(GLenum target, GLuint texture) {
+ // TODO(gman): See note #1 above.
+ // TODO(gman): Change this to false once we figure out why it's failing
+ // on daisy.
+ bool changed = true;
+ TextureUnit& unit = texture_units_[active_texture_unit_];
+ switch (target) {
+ case GL_TEXTURE_2D:
+ if (unit.bound_texture_2d != texture) {
+ unit.bound_texture_2d = texture;
+ changed = true;
+ }
+ break;
+ case GL_TEXTURE_CUBE_MAP:
+ if (unit.bound_texture_cube_map != texture) {
+ unit.bound_texture_cube_map = texture;
+ changed = true;
+ }
+ break;
+ default:
+ changed = true;
+ break;
+ }
+ // TODO(gman): There's a bug here. If the target is invalid the ID will not be
+ // used. even though it's marked it as used here.
+ GetIdHandler(id_namespaces::kTextures)->MarkAsUsedForBind(texture);
+ return changed;
+}
+
+bool GLES2Implementation::BindVertexArrayHelper(GLuint array) {
+ // TODO(gman): See note #1 above.
+ bool changed = false;
+ if (!vertex_array_object_manager_->BindVertexArray(array, &changed)) {
+ SetGLError(
+ GL_INVALID_OPERATION, "glBindVertexArrayOES",
+ "id was not generated with glGenVertexArrayOES");
+ }
+ // Unlike other BindXXXHelpers we don't call MarkAsUsedForBind
+ // because unlike other resources VertexArrayObject ids must
+ // be generated by GenVertexArrays. A random id to Bind will not
+ // generate a new object.
+ return changed;
+}
+
+bool GLES2Implementation::IsBufferReservedId(GLuint id) {
+ return vertex_array_object_manager_->IsReservedId(id);
+}
+
+void GLES2Implementation::DeleteBuffersHelper(
+ GLsizei n, const GLuint* buffers) {
+ if (!GetIdHandler(id_namespaces::kBuffers)->FreeIds(
+ this, n, buffers, &GLES2Implementation::DeleteBuffersStub)) {
+ SetGLError(
+ GL_INVALID_VALUE,
+ "glDeleteBuffers", "id not created by this context.");
+ return;
+ }
+ for (GLsizei ii = 0; ii < n; ++ii) {
+ if (buffers[ii] == bound_array_buffer_id_) {
+ bound_array_buffer_id_ = 0;
+ }
+ vertex_array_object_manager_->UnbindBuffer(buffers[ii]);
+ BufferTracker::Buffer* buffer = buffer_tracker_->GetBuffer(buffers[ii]);
+ if (buffer) {
+ // Free buffer memory, pending the passage of a token.
+ buffer_tracker_->FreePendingToken(buffer, helper_->InsertToken());
+ // Remove buffer.
+ buffer_tracker_->RemoveBuffer(buffers[ii]);
+ }
+ if (buffers[ii] == bound_pixel_unpack_transfer_buffer_id_) {
+ bound_pixel_unpack_transfer_buffer_id_ = 0;
+ }
+ }
+}
+
+void GLES2Implementation::DeleteBuffersStub(
+ GLsizei n, const GLuint* buffers) {
+ helper_->DeleteBuffersImmediate(n, buffers);
+}
+
+
+void GLES2Implementation::DeleteFramebuffersHelper(
+ GLsizei n, const GLuint* framebuffers) {
+ if (!GetIdHandler(id_namespaces::kFramebuffers)->FreeIds(
+ this, n, framebuffers, &GLES2Implementation::DeleteFramebuffersStub)) {
+ SetGLError(
+ GL_INVALID_VALUE,
+ "glDeleteFramebuffers", "id not created by this context.");
+ return;
+ }
+ for (GLsizei ii = 0; ii < n; ++ii) {
+ if (framebuffers[ii] == bound_framebuffer_) {
+ bound_framebuffer_ = 0;
+ }
+ if (framebuffers[ii] == bound_read_framebuffer_) {
+ bound_read_framebuffer_ = 0;
+ }
+ }
+}
+
+void GLES2Implementation::DeleteFramebuffersStub(
+ GLsizei n, const GLuint* framebuffers) {
+ helper_->DeleteFramebuffersImmediate(n, framebuffers);
+}
+
+void GLES2Implementation::DeleteRenderbuffersHelper(
+ GLsizei n, const GLuint* renderbuffers) {
+ if (!GetIdHandler(id_namespaces::kRenderbuffers)->FreeIds(
+ this, n, renderbuffers, &GLES2Implementation::DeleteRenderbuffersStub)) {
+ SetGLError(
+ GL_INVALID_VALUE,
+ "glDeleteRenderbuffers", "id not created by this context.");
+ return;
+ }
+ for (GLsizei ii = 0; ii < n; ++ii) {
+ if (renderbuffers[ii] == bound_renderbuffer_) {
+ bound_renderbuffer_ = 0;
+ }
+ }
+}
+
+void GLES2Implementation::DeleteRenderbuffersStub(
+ GLsizei n, const GLuint* renderbuffers) {
+ helper_->DeleteRenderbuffersImmediate(n, renderbuffers);
+}
+
+void GLES2Implementation::DeleteTexturesHelper(
+ GLsizei n, const GLuint* textures) {
+ if (!GetIdHandler(id_namespaces::kTextures)->FreeIds(
+ this, n, textures, &GLES2Implementation::DeleteTexturesStub)) {
+ SetGLError(
+ GL_INVALID_VALUE,
+ "glDeleteTextures", "id not created by this context.");
+ return;
+ }
+ for (GLsizei ii = 0; ii < n; ++ii) {
+ for (GLint tt = 0;
+ tt < static_state_.int_state.max_combined_texture_image_units;
+ ++tt) {
+ TextureUnit& unit = texture_units_[tt];
+ if (textures[ii] == unit.bound_texture_2d) {
+ unit.bound_texture_2d = 0;
+ }
+ if (textures[ii] == unit.bound_texture_cube_map) {
+ unit.bound_texture_cube_map = 0;
+ }
+ }
+ }
+}
+
+void GLES2Implementation::DeleteVertexArraysOESHelper(
+ GLsizei n, const GLuint* arrays) {
+ vertex_array_object_manager_->DeleteVertexArrays(n, arrays);
+ if (!GetIdHandler(id_namespaces::kVertexArrays)->FreeIds(
+ this, n, arrays, &GLES2Implementation::DeleteVertexArraysOESStub)) {
+ SetGLError(
+ GL_INVALID_VALUE,
+ "glDeleteVertexArraysOES", "id not created by this context.");
+ return;
+ }
+}
+
+void GLES2Implementation::DeleteVertexArraysOESStub(
+ GLsizei n, const GLuint* arrays) {
+ helper_->DeleteVertexArraysOESImmediate(n, arrays);
+}
+
+void GLES2Implementation::DeleteTexturesStub(
+ GLsizei n, const GLuint* textures) {
+ helper_->DeleteTexturesImmediate(n, textures);
+}
+
+void GLES2Implementation::DisableVertexAttribArray(GLuint index) {
+ GPU_CLIENT_SINGLE_THREAD_CHECK();
+ GPU_CLIENT_LOG(
+ "[" << GetLogPrefix() << "] glDisableVertexAttribArray(" << index << ")");
+ vertex_array_object_manager_->SetAttribEnable(index, false);
+ helper_->DisableVertexAttribArray(index);
+ CheckGLError();
+}
+
+void GLES2Implementation::EnableVertexAttribArray(GLuint index) {
+ GPU_CLIENT_SINGLE_THREAD_CHECK();
+ GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glEnableVertexAttribArray("
+ << index << ")");
+ vertex_array_object_manager_->SetAttribEnable(index, true);
+ helper_->EnableVertexAttribArray(index);
+ CheckGLError();
+}
+
+void GLES2Implementation::DrawArrays(GLenum mode, GLint first, GLsizei count) {
+ GPU_CLIENT_SINGLE_THREAD_CHECK();
+ GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glDrawArrays("
+ << GLES2Util::GetStringDrawMode(mode) << ", "
+ << first << ", " << count << ")");
+ if (count < 0) {
+ SetGLError(GL_INVALID_VALUE, "glDrawArrays", "count < 0");
+ return;
+ }
+ bool simulated = false;
+ if (!vertex_array_object_manager_->SetupSimulatedClientSideBuffers(
+ "glDrawArrays", this, helper_, first + count, 0, &simulated)) {
+ return;
+ }
+ helper_->DrawArrays(mode, first, count);
+ RestoreArrayBuffer(simulated);
+ CheckGLError();
+}
+
+void GLES2Implementation::GetVertexAttribfv(
+ GLuint index, GLenum pname, GLfloat* params) {
+ GPU_CLIENT_SINGLE_THREAD_CHECK();
+ GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glGetVertexAttribfv("
+ << index << ", "
+ << GLES2Util::GetStringVertexAttribute(pname) << ", "
+ << static_cast<const void*>(params) << ")");
+ uint32 value = 0;
+ if (vertex_array_object_manager_->GetVertexAttrib(index, pname, &value)) {
+ *params = static_cast<float>(value);
+ return;
+ }
+ TRACE_EVENT0("gpu", "GLES2::GetVertexAttribfv");
+ typedef cmds::GetVertexAttribfv::Result Result;
+ Result* result = GetResultAs<Result*>();
+ if (!result) {
+ return;
+ }
+ result->SetNumResults(0);
+ helper_->GetVertexAttribfv(
+ index, pname, GetResultShmId(), GetResultShmOffset());
+ WaitForCmd();
+ result->CopyResult(params);
+ GPU_CLIENT_LOG_CODE_BLOCK({
+ for (int32 i = 0; i < result->GetNumResults(); ++i) {
+ GPU_CLIENT_LOG(" " << i << ": " << result->GetData()[i]);
+ }
+ });
+ CheckGLError();
+}
+
+void GLES2Implementation::GetVertexAttribiv(
+ GLuint index, GLenum pname, GLint* params) {
+ GPU_CLIENT_SINGLE_THREAD_CHECK();
+ GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glGetVertexAttribiv("
+ << index << ", "
+ << GLES2Util::GetStringVertexAttribute(pname) << ", "
+ << static_cast<const void*>(params) << ")");
+ uint32 value = 0;
+ if (vertex_array_object_manager_->GetVertexAttrib(index, pname, &value)) {
+ *params = value;
+ return;
+ }
+ TRACE_EVENT0("gpu", "GLES2::GetVertexAttribiv");
+ typedef cmds::GetVertexAttribiv::Result Result;
+ Result* result = GetResultAs<Result*>();
+ if (!result) {
+ return;
+ }
+ result->SetNumResults(0);
+ helper_->GetVertexAttribiv(
+ index, pname, GetResultShmId(), GetResultShmOffset());
+ WaitForCmd();
+ result->CopyResult(params);
+ GPU_CLIENT_LOG_CODE_BLOCK({
+ for (int32 i = 0; i < result->GetNumResults(); ++i) {
+ GPU_CLIENT_LOG(" " << i << ": " << result->GetData()[i]);
+ }
+ });
+ CheckGLError();
+}
+
+GLboolean GLES2Implementation::EnableFeatureCHROMIUM(
+ const char* feature) {
+ GPU_CLIENT_SINGLE_THREAD_CHECK();
+ GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glEnableFeatureCHROMIUM("
+ << feature << ")");
+ TRACE_EVENT0("gpu", "GLES2::EnableFeatureCHROMIUM");
+ typedef cmds::EnableFeatureCHROMIUM::Result Result;
+ Result* result = GetResultAs<Result*>();
+ if (!result) {
+ return false;
+ }
+ *result = 0;
+ SetBucketAsCString(kResultBucketId, feature);
+ helper_->EnableFeatureCHROMIUM(
+ kResultBucketId, GetResultShmId(), GetResultShmOffset());
+ WaitForCmd();
+ helper_->SetBucketSize(kResultBucketId, 0);
+ GPU_CLIENT_LOG(" returned " << GLES2Util::GetStringBool(*result));
+ return *result;
+}
+
+void* GLES2Implementation::MapBufferSubDataCHROMIUM(
+ GLuint target, GLintptr offset, GLsizeiptr size, GLenum access) {
+ GPU_CLIENT_SINGLE_THREAD_CHECK();
+ GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glMapBufferSubDataCHROMIUM("
+ << target << ", " << offset << ", " << size << ", "
+ << GLES2Util::GetStringEnum(access) << ")");
+ // NOTE: target is NOT checked because the service will check it
+ // and we don't know what targets are valid.
+ if (access != GL_WRITE_ONLY) {
+ SetGLErrorInvalidEnum(
+ "glMapBufferSubDataCHROMIUM", access, "access");
+ return NULL;
+ }
+ if (offset < 0 || size < 0) {
+ SetGLError(GL_INVALID_VALUE, "glMapBufferSubDataCHROMIUM", "bad range");
+ return NULL;
+ }
+ int32 shm_id;
+ unsigned int shm_offset;
+ void* mem = mapped_memory_->Alloc(size, &shm_id, &shm_offset);
+ if (!mem) {
+ SetGLError(GL_OUT_OF_MEMORY, "glMapBufferSubDataCHROMIUM", "out of memory");
+ return NULL;
+ }
+
+ std::pair<MappedBufferMap::iterator, bool> result =
+ mapped_buffers_.insert(std::make_pair(
+ mem,
+ MappedBuffer(
+ access, shm_id, mem, shm_offset, target, offset, size)));
+ GPU_DCHECK(result.second);
+ GPU_CLIENT_LOG(" returned " << mem);
+ return mem;
+}
+
+void GLES2Implementation::UnmapBufferSubDataCHROMIUM(const void* mem) {
+ GPU_CLIENT_SINGLE_THREAD_CHECK();
+ GPU_CLIENT_LOG(
+ "[" << GetLogPrefix() << "] glUnmapBufferSubDataCHROMIUM(" << mem << ")");
+ MappedBufferMap::iterator it = mapped_buffers_.find(mem);
+ if (it == mapped_buffers_.end()) {
+ SetGLError(
+ GL_INVALID_VALUE, "UnmapBufferSubDataCHROMIUM", "buffer not mapped");
+ return;
+ }
+ const MappedBuffer& mb = it->second;
+ helper_->BufferSubData(
+ mb.target, mb.offset, mb.size, mb.shm_id, mb.shm_offset);
+ mapped_memory_->FreePendingToken(mb.shm_memory, helper_->InsertToken());
+ mapped_buffers_.erase(it);
+ CheckGLError();
+}
+
+void* GLES2Implementation::MapTexSubImage2DCHROMIUM(
+ GLenum target,
+ GLint level,
+ GLint xoffset,
+ GLint yoffset,
+ GLsizei width,
+ GLsizei height,
+ GLenum format,
+ GLenum type,
+ GLenum access) {
+ GPU_CLIENT_SINGLE_THREAD_CHECK();
+ GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glMapTexSubImage2DCHROMIUM("
+ << target << ", " << level << ", "
+ << xoffset << ", " << yoffset << ", "
+ << width << ", " << height << ", "
+ << GLES2Util::GetStringTextureFormat(format) << ", "
+ << GLES2Util::GetStringPixelType(type) << ", "
+ << GLES2Util::GetStringEnum(access) << ")");
+ if (access != GL_WRITE_ONLY) {
+ SetGLErrorInvalidEnum(
+ "glMapTexSubImage2DCHROMIUM", access, "access");
+ return NULL;
+ }
+ // NOTE: target is NOT checked because the service will check it
+ // and we don't know what targets are valid.
+ if (level < 0 || xoffset < 0 || yoffset < 0 || width < 0 || height < 0) {
+ SetGLError(
+ GL_INVALID_VALUE, "glMapTexSubImage2DCHROMIUM", "bad dimensions");
+ return NULL;
+ }
+ uint32 size;
+ if (!GLES2Util::ComputeImageDataSizes(
+ width, height, format, type, unpack_alignment_, &size, NULL, NULL)) {
+ SetGLError(
+ GL_INVALID_VALUE, "glMapTexSubImage2DCHROMIUM", "image size too large");
+ return NULL;
+ }
+ int32 shm_id;
+ unsigned int shm_offset;
+ void* mem = mapped_memory_->Alloc(size, &shm_id, &shm_offset);
+ if (!mem) {
+ SetGLError(GL_OUT_OF_MEMORY, "glMapTexSubImage2DCHROMIUM", "out of memory");
+ return NULL;
+ }
+
+ std::pair<MappedTextureMap::iterator, bool> result =
+ mapped_textures_.insert(std::make_pair(
+ mem,
+ MappedTexture(
+ access, shm_id, mem, shm_offset,
+ target, level, xoffset, yoffset, width, height, format, type)));
+ GPU_DCHECK(result.second);
+ GPU_CLIENT_LOG(" returned " << mem);
+ return mem;
+}
+
+void GLES2Implementation::UnmapTexSubImage2DCHROMIUM(const void* mem) {
+ GPU_CLIENT_SINGLE_THREAD_CHECK();
+ GPU_CLIENT_LOG(
+ "[" << GetLogPrefix() << "] glUnmapTexSubImage2DCHROMIUM(" << mem << ")");
+ MappedTextureMap::iterator it = mapped_textures_.find(mem);
+ if (it == mapped_textures_.end()) {
+ SetGLError(
+ GL_INVALID_VALUE, "UnmapTexSubImage2DCHROMIUM", "texture not mapped");
+ return;
+ }
+ const MappedTexture& mt = it->second;
+ helper_->TexSubImage2D(
+ mt.target, mt.level, mt.xoffset, mt.yoffset, mt.width, mt.height,
+ mt.format, mt.type, mt.shm_id, mt.shm_offset, GL_FALSE);
+ mapped_memory_->FreePendingToken(mt.shm_memory, helper_->InsertToken());
+ mapped_textures_.erase(it);
+ CheckGLError();
+}
+
+void GLES2Implementation::ResizeCHROMIUM(GLuint width, GLuint height,
+ float scale_factor) {
+ GPU_CLIENT_SINGLE_THREAD_CHECK();
+ GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glResizeCHROMIUM("
+ << width << ", " << height << ", " << scale_factor << ")");
+ helper_->ResizeCHROMIUM(width, height, scale_factor);
+ CheckGLError();
+}
+
+const GLchar* GLES2Implementation::GetRequestableExtensionsCHROMIUM() {
+ GPU_CLIENT_SINGLE_THREAD_CHECK();
+ GPU_CLIENT_LOG("[" << GetLogPrefix()
+ << "] glGetRequestableExtensionsCHROMIUM()");
+ TRACE_EVENT0("gpu",
+ "GLES2Implementation::GetRequestableExtensionsCHROMIUM()");
+ const char* result = NULL;
+ // Clear the bucket so if the command fails nothing will be in it.
+ helper_->SetBucketSize(kResultBucketId, 0);
+ helper_->GetRequestableExtensionsCHROMIUM(kResultBucketId);
+ std::string str;
+ if (GetBucketAsString(kResultBucketId, &str)) {
+ // The set of requestable extensions shrinks as we enable
+ // them. Because we don't know when the client will stop referring
+ // to a previous one it queries (see GetString) we need to cache
+ // the unique results.
+ std::set<std::string>::const_iterator sit =
+ requestable_extensions_set_.find(str);
+ if (sit != requestable_extensions_set_.end()) {
+ result = sit->c_str();
+ } else {
+ std::pair<std::set<std::string>::const_iterator, bool> insert_result =
+ requestable_extensions_set_.insert(str);
+ GPU_DCHECK(insert_result.second);
+ result = insert_result.first->c_str();
+ }
+ }
+ GPU_CLIENT_LOG(" returned " << result);
+ return reinterpret_cast<const GLchar*>(result);
+}
+
+// TODO(gman): Remove this command. It's here for WebGL but is incompatible
+// with VirtualGL contexts.
+void GLES2Implementation::RequestExtensionCHROMIUM(const char* extension) {
+ GPU_CLIENT_SINGLE_THREAD_CHECK();
+ GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glRequestExtensionCHROMIUM("
+ << extension << ")");
+ SetBucketAsCString(kResultBucketId, extension);
+ helper_->RequestExtensionCHROMIUM(kResultBucketId);
+ helper_->SetBucketSize(kResultBucketId, 0);
+
+ struct ExtensionCheck {
+ const char* extension;
+ ExtensionStatus* status;
+ };
+ const ExtensionCheck checks[] = {
+ {
+ "GL_ANGLE_pack_reverse_row_order",
+ &angle_pack_reverse_row_order_status_,
+ },
+ {
+ "GL_CHROMIUM_framebuffer_multisample",
+ &chromium_framebuffer_multisample_,
+ },
+ };
+ const size_t kNumChecks = sizeof(checks)/sizeof(checks[0]);
+ for (size_t ii = 0; ii < kNumChecks; ++ii) {
+ const ExtensionCheck& check = checks[ii];
+ if (*check.status == kUnavailableExtensionStatus &&
+ !strcmp(extension, check.extension)) {
+ *check.status = kUnknownExtensionStatus;
+ }
+ }
+}
+
+void GLES2Implementation::RateLimitOffscreenContextCHROMIUM() {
+ GPU_CLIENT_SINGLE_THREAD_CHECK();
+ GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glRateLimitOffscreenCHROMIUM()");
+ // Wait if this would add too many rate limit tokens.
+ if (rate_limit_tokens_.size() == kMaxSwapBuffers) {
+ helper_->WaitForToken(rate_limit_tokens_.front());
+ rate_limit_tokens_.pop();
+ }
+ rate_limit_tokens_.push(helper_->InsertToken());
+}
+
+void GLES2Implementation::GetMultipleIntegervCHROMIUM(
+ const GLenum* pnames, GLuint count, GLint* results, GLsizeiptr size) {
+ GPU_CLIENT_SINGLE_THREAD_CHECK();
+ GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glGetMultipleIntegervCHROMIUM("
+ << static_cast<const void*>(pnames) << ", "
+ << count << ", " << results << ", "
+ << size << ")");
+ GPU_CLIENT_LOG_CODE_BLOCK({
+ for (GLuint i = 0; i < count; ++i) {
+ GPU_CLIENT_LOG(
+ " " << i << ": " << GLES2Util::GetStringGLState(pnames[i]));
+ }
+ });
+
+ GetMultipleIntegervState state(pnames, count, results, size);
+ if (!GetMultipleIntegervSetup(&state)) {
+ return;
+ }
+ state.buffer = transfer_buffer_->Alloc(state.transfer_buffer_size_needed);
+ if (!state.buffer) {
+ SetGLError(GL_OUT_OF_MEMORY, "glGetMultipleIntegervCHROMIUM",
+ "Transfer buffer allocation failed.");
+ return;
+ }
+ GetMultipleIntegervRequest(&state);
+ WaitForCmd();
+ GetMultipleIntegervOnCompleted(&state);
+
+ GPU_CLIENT_LOG(" returned");
+ GPU_CLIENT_LOG_CODE_BLOCK({
+ for (int i = 0; i < state.num_results; ++i) {
+ GPU_CLIENT_LOG(" " << i << ": " << (results[i]));
+ }
+ });
+
+ // TODO(gman): We should be able to free without a token.
+ transfer_buffer_->FreePendingToken(state.buffer, helper_->InsertToken());
+ CheckGLError();
+}
+
+bool GLES2Implementation::GetMultipleIntegervSetup(
+ GetMultipleIntegervState* state) {
+ state->num_results = 0;
+ for (GLuint ii = 0; ii < state->pnames_count; ++ii) {
+ int num = util_.GLGetNumValuesReturned(state->pnames[ii]);
+ if (!num) {
+ SetGLErrorInvalidEnum(
+ "glGetMultipleIntegervCHROMIUM", state->pnames[ii], "pname");
+ return false;
+ }
+ state->num_results += num;
+ }
+ if (static_cast<size_t>(state->results_size) !=
+ state->num_results * sizeof(GLint)) {
+ SetGLError(GL_INVALID_VALUE, "glGetMultipleIntegervCHROMIUM", "bad size");
+ return false;
+ }
+ for (int ii = 0; ii < state->num_results; ++ii) {
+ if (state->results[ii] != 0) {
+ SetGLError(GL_INVALID_VALUE,
+ "glGetMultipleIntegervCHROMIUM", "results not set to zero.");
+ return false;
+ }
+ }
+ state->transfer_buffer_size_needed =
+ state->pnames_count * sizeof(state->pnames[0]) +
+ state->num_results * sizeof(state->results[0]);
+ return true;
+}
+
+void GLES2Implementation::GetMultipleIntegervRequest(
+ GetMultipleIntegervState* state) {
+ GLenum* pnames_buffer = static_cast<GLenum*>(state->buffer);
+ state->results_buffer = pnames_buffer + state->pnames_count;
+ memcpy(pnames_buffer, state->pnames, state->pnames_count * sizeof(GLenum));
+ memset(state->results_buffer, 0, state->num_results * sizeof(GLint));
+ helper_->GetMultipleIntegervCHROMIUM(
+ transfer_buffer_->GetShmId(),
+ transfer_buffer_->GetOffset(pnames_buffer),
+ state->pnames_count,
+ transfer_buffer_->GetShmId(),
+ transfer_buffer_->GetOffset(state->results_buffer),
+ state->results_size);
+}
+
+void GLES2Implementation::GetMultipleIntegervOnCompleted(
+ GetMultipleIntegervState* state) {
+ memcpy(state->results, state->results_buffer, state->results_size);;
+}
+
+void GLES2Implementation::GetAllShaderPrecisionFormatsSetup(
+ GetAllShaderPrecisionFormatsState* state) {
+ state->transfer_buffer_size_needed =
+ state->precision_params_count *
+ sizeof(cmds::GetShaderPrecisionFormat::Result);
+}
+
+void GLES2Implementation::GetAllShaderPrecisionFormatsRequest(
+ GetAllShaderPrecisionFormatsState* state) {
+ typedef cmds::GetShaderPrecisionFormat::Result Result;
+ Result* result = static_cast<Result*>(state->results_buffer);
+
+ for (int i = 0; i < state->precision_params_count; i++) {
+ result->success = false;
+ helper_->GetShaderPrecisionFormat(state->precision_params[i][0],
+ state->precision_params[i][1],
+ transfer_buffer_->GetShmId(),
+ transfer_buffer_->GetOffset(result));
+ result++;
+ }
+}
+
+void GLES2Implementation::GetAllShaderPrecisionFormatsOnCompleted(
+ GetAllShaderPrecisionFormatsState* state) {
+ typedef cmds::GetShaderPrecisionFormat::Result Result;
+ Result* result = static_cast<Result*>(state->results_buffer);
+
+ for (int i = 0; i < state->precision_params_count; i++) {
+ if (result->success) {
+ const GLStaticState::ShaderPrecisionKey key(
+ state->precision_params[i][0], state->precision_params[i][1]);
+ static_state_.shader_precisions[key] = *result;
+ }
+ result++;
+ }
+}
+
+void GLES2Implementation::GetProgramInfoCHROMIUMHelper(
+ GLuint program, std::vector<int8>* result) {
+ GPU_DCHECK(result);
+ // Clear the bucket so if the command fails nothing will be in it.
+ helper_->SetBucketSize(kResultBucketId, 0);
+ helper_->GetProgramInfoCHROMIUM(program, kResultBucketId);
+ GetBucketContents(kResultBucketId, result);
+}
+
+void GLES2Implementation::GetProgramInfoCHROMIUM(
+ GLuint program, GLsizei bufsize, GLsizei* size, void* info) {
+ GPU_CLIENT_SINGLE_THREAD_CHECK();
+ if (bufsize < 0) {
+ SetGLError(
+ GL_INVALID_VALUE, "glProgramInfoCHROMIUM", "bufsize less than 0.");
+ return;
+ }
+ if (size == NULL) {
+ SetGLError(GL_INVALID_VALUE, "glProgramInfoCHROMIUM", "size is null.");
+ return;
+ }
+ // Make sure they've set size to 0 else the value will be undefined on
+ // lost context.
+ GPU_DCHECK(*size == 0);
+ std::vector<int8> result;
+ GetProgramInfoCHROMIUMHelper(program, &result);
+ if (result.empty()) {
+ return;
+ }
+ *size = result.size();
+ if (!info) {
+ return;
+ }
+ if (static_cast<size_t>(bufsize) < result.size()) {
+ SetGLError(GL_INVALID_OPERATION,
+ "glProgramInfoCHROMIUM", "bufsize is too small for result.");
+ return;
+ }
+ memcpy(info, &result[0], result.size());
+}
+
+GLuint GLES2Implementation::CreateStreamTextureCHROMIUM(GLuint texture) {
+ GPU_CLIENT_SINGLE_THREAD_CHECK();
+ GPU_CLIENT_LOG("[" << GetLogPrefix() << "] CreateStreamTextureCHROMIUM("
+ << texture << ")");
+ TRACE_EVENT0("gpu", "GLES2::CreateStreamTextureCHROMIUM");
+ typedef cmds::CreateStreamTextureCHROMIUM::Result Result;
+ Result* result = GetResultAs<Result*>();
+ if (!result) {
+ return GL_ZERO;
+ }
+ *result = GL_ZERO;
+
+ helper_->CreateStreamTextureCHROMIUM(texture,
+ GetResultShmId(),
+ GetResultShmOffset());
+ WaitForCmd();
+ GLuint result_value = *result;
+ CheckGLError();
+ return result_value;
+}
+
+void GLES2Implementation::DestroyStreamTextureCHROMIUM(GLuint texture) {
+ GPU_CLIENT_SINGLE_THREAD_CHECK();
+ GPU_CLIENT_LOG("[" << GetLogPrefix() << "] DestroyStreamTextureCHROMIUM("
+ << texture << ")");
+ TRACE_EVENT0("gpu", "GLES2::DestroyStreamTextureCHROMIUM");
+ helper_->DestroyStreamTextureCHROMIUM(texture);
+ CheckGLError();
+}
+
+void GLES2Implementation::PostSubBufferCHROMIUM(
+ GLint x, GLint y, GLint width, GLint height) {
+ GPU_CLIENT_SINGLE_THREAD_CHECK();
+ GPU_CLIENT_LOG("[" << GetLogPrefix() << "] PostSubBufferCHROMIUM("
+ << x << ", " << y << ", " << width << ", " << height << ")");
+ TRACE_EVENT2("gpu", "GLES2::PostSubBufferCHROMIUM",
+ "width", width, "height", height);
+
+ // Same flow control as GLES2Implementation::SwapBuffers (see comments there).
+ swap_buffers_tokens_.push(helper_->InsertToken());
+ helper_->PostSubBufferCHROMIUM(x, y, width, height);
+ helper_->CommandBufferHelper::Flush();
+ if (swap_buffers_tokens_.size() > kMaxSwapBuffers + 1) {
+ helper_->WaitForToken(swap_buffers_tokens_.front());
+ swap_buffers_tokens_.pop();
+ }
+}
+
+void GLES2Implementation::DeleteQueriesEXTHelper(
+ GLsizei n, const GLuint* queries) {
+ // TODO(gman): Remove this as queries are not shared resources.
+ if (!GetIdHandler(id_namespaces::kQueries)->FreeIds(
+ this, n, queries, &GLES2Implementation::DeleteQueriesStub)) {
+ SetGLError(
+ GL_INVALID_VALUE,
+ "glDeleteTextures", "id not created by this context.");
+ return;
+ }
+
+ for (GLsizei ii = 0; ii < n; ++ii)
+ query_tracker_->RemoveQuery(queries[ii]);
+
+ helper_->DeleteQueriesEXTImmediate(n, queries);
+}
+
+// TODO(gman): Remove this. Queries are not shared resources.
+void GLES2Implementation::DeleteQueriesStub(
+ GLsizei /* n */, const GLuint* /* queries */) {
+}
+
+GLboolean GLES2Implementation::IsQueryEXT(GLuint id) {
+ GPU_CLIENT_SINGLE_THREAD_CHECK();
+ GPU_CLIENT_LOG("[" << GetLogPrefix() << "] IsQueryEXT(" << id << ")");
+
+ // TODO(gman): To be spec compliant IDs from other contexts sharing
+ // resources need to return true here even though you can't share
+ // queries across contexts?
+ return query_tracker_->GetQuery(id) != NULL;
+}
+
+void GLES2Implementation::BeginQueryEXT(GLenum target, GLuint id) {
+ GPU_CLIENT_SINGLE_THREAD_CHECK();
+ GPU_CLIENT_LOG("[" << GetLogPrefix() << "] BeginQueryEXT("
+ << GLES2Util::GetStringQueryTarget(target)
+ << ", " << id << ")");
+
+ // if any outstanding queries INV_OP
+ if (current_query_) {
+ SetGLError(
+ GL_INVALID_OPERATION, "glBeginQueryEXT", "query already in progress");
+ return;
+ }
+
+ // id = 0 INV_OP
+ if (id == 0) {
+ SetGLError(GL_INVALID_OPERATION, "glBeginQueryEXT", "id is 0");
+ return;
+ }
+
+ // TODO(gman) if id not GENned INV_OPERATION
+
+ // if id does not have an object
+ QueryTracker::Query* query = query_tracker_->GetQuery(id);
+ if (!query) {
+ query = query_tracker_->CreateQuery(id, target);
+ if (!query) {
+ MustBeContextLost();
+ return;
+ }
+ } else if (query->target() != target) {
+ SetGLError(
+ GL_INVALID_OPERATION, "glBeginQueryEXT", "target does not match");
+ return;
+ }
+
+ current_query_ = query;
+
+ query->Begin(this);
+ CheckGLError();
+}
+
+void GLES2Implementation::EndQueryEXT(GLenum target) {
+ GPU_CLIENT_SINGLE_THREAD_CHECK();
+ GPU_CLIENT_LOG("[" << GetLogPrefix() << "] EndQueryEXT("
+ << GLES2Util::GetStringQueryTarget(target) << ")");
+ // Don't do anything if the context is lost.
+ if (helper_->IsContextLost()) {
+ return;
+ }
+
+ if (!current_query_) {
+ SetGLError(GL_INVALID_OPERATION, "glEndQueryEXT", "no active query");
+ return;
+ }
+
+ if (current_query_->target() != target) {
+ SetGLError(GL_INVALID_OPERATION,
+ "glEndQueryEXT", "target does not match active query");
+ return;
+ }
+
+ current_query_->End(this);
+ current_query_ = NULL;
+ CheckGLError();
+}
+
+void GLES2Implementation::GetQueryivEXT(
+ GLenum target, GLenum pname, GLint* params) {
+ GPU_CLIENT_SINGLE_THREAD_CHECK();
+ GPU_CLIENT_LOG("[" << GetLogPrefix() << "] GetQueryivEXT("
+ << GLES2Util::GetStringQueryTarget(target) << ", "
+ << GLES2Util::GetStringQueryParameter(pname) << ", "
+ << static_cast<const void*>(params) << ")");
+
+ if (pname != GL_CURRENT_QUERY_EXT) {
+ SetGLErrorInvalidEnum("glGetQueryivEXT", pname, "pname");
+ return;
+ }
+ *params = (current_query_ && current_query_->target() == target) ?
+ current_query_->id() : 0;
+ GPU_CLIENT_LOG(" " << *params);
+ CheckGLError();
+}
+
+void GLES2Implementation::GetQueryObjectuivEXT(
+ GLuint id, GLenum pname, GLuint* params) {
+ GPU_CLIENT_SINGLE_THREAD_CHECK();
+ GPU_CLIENT_LOG("[" << GetLogPrefix() << "] GetQueryivEXT(" << id << ", "
+ << GLES2Util::GetStringQueryObjectParameter(pname) << ", "
+ << static_cast<const void*>(params) << ")");
+
+ QueryTracker::Query* query = query_tracker_->GetQuery(id);
+ if (!query) {
+ SetGLError(GL_INVALID_OPERATION, "glQueryObjectuivEXT", "unknown query id");
+ return;
+ }
+
+ if (query == current_query_) {
+ SetGLError(
+ GL_INVALID_OPERATION,
+ "glQueryObjectuivEXT", "query active. Did you to call glEndQueryEXT?");
+ return;
+ }
+
+ if (query->NeverUsed()) {
+ SetGLError(
+ GL_INVALID_OPERATION,
+ "glQueryObjectuivEXT", "Never used. Did you call glBeginQueryEXT?");
+ return;
+ }
+
+ switch (pname) {
+ case GL_QUERY_RESULT_EXT:
+ if (!query->CheckResultsAvailable(helper_)) {
+ helper_->WaitForToken(query->token());
+ if (!query->CheckResultsAvailable(helper_)) {
+ // TODO(gman): Speed this up.
+ WaitForCmd();
+ GPU_CHECK(query->CheckResultsAvailable(helper_));
+ }
+ }
+ *params = query->GetResult();
+ break;
+ case GL_QUERY_RESULT_AVAILABLE_EXT:
+ *params = query->CheckResultsAvailable(helper_);
+ break;
+ default:
+ SetGLErrorInvalidEnum("glQueryObjectuivEXT", pname, "pname");
+ break;
+ }
+ GPU_CLIENT_LOG(" " << *params);
+ CheckGLError();
+}
+
+void GLES2Implementation::DrawArraysInstancedANGLE(
+ GLenum mode, GLint first, GLsizei count, GLsizei primcount) {
+ GPU_CLIENT_SINGLE_THREAD_CHECK();
+ GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glDrawArraysInstancedANGLE("
+ << GLES2Util::GetStringDrawMode(mode) << ", "
+ << first << ", " << count << ", " << primcount << ")");
+ if (count < 0) {
+ SetGLError(GL_INVALID_VALUE, "glDrawArraysInstancedANGLE", "count < 0");
+ return;
+ }
+ if (primcount < 0) {
+ SetGLError(GL_INVALID_VALUE, "glDrawArraysInstancedANGLE", "primcount < 0");
+ return;
+ }
+ if (primcount == 0) {
+ return;
+ }
+ bool simulated = false;
+ if (!vertex_array_object_manager_->SetupSimulatedClientSideBuffers(
+ "glDrawArraysInstancedANGLE", this, helper_, first + count, primcount,
+ &simulated)) {
+ return;
+ }
+ helper_->DrawArraysInstancedANGLE(mode, first, count, primcount);
+ RestoreArrayBuffer(simulated);
+ CheckGLError();
+}
+
+void GLES2Implementation::DrawElementsInstancedANGLE(
+ GLenum mode, GLsizei count, GLenum type, const void* indices,
+ GLsizei primcount) {
+ GPU_CLIENT_SINGLE_THREAD_CHECK();
+ GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glDrawElementsInstancedANGLE("
+ << GLES2Util::GetStringDrawMode(mode) << ", "
+ << count << ", "
+ << GLES2Util::GetStringIndexType(type) << ", "
+ << static_cast<const void*>(indices) << ", "
+ << primcount << ")");
+ if (count < 0) {
+ SetGLError(GL_INVALID_VALUE,
+ "glDrawElementsInstancedANGLE", "count less than 0.");
+ return;
+ }
+ if (count == 0) {
+ return;
+ }
+ if (primcount < 0) {
+ SetGLError(GL_INVALID_VALUE,
+ "glDrawElementsInstancedANGLE", "primcount < 0");
+ return;
+ }
+ if (primcount == 0) {
+ return;
+ }
+ GLuint offset = 0;
+ bool simulated = false;
+ if (!vertex_array_object_manager_->SetupSimulatedIndexAndClientSideBuffers(
+ "glDrawElementsInstancedANGLE", this, helper_, count, type, primcount,
+ indices, &offset, &simulated)) {
+ return;
+ }
+ helper_->DrawElementsInstancedANGLE(mode, count, type, offset, primcount);
+ RestoreElementAndArrayBuffers(simulated);
+ CheckGLError();
+}
+
+void GLES2Implementation::GenMailboxCHROMIUM(
+ GLbyte* mailbox) {
+ GPU_CLIENT_SINGLE_THREAD_CHECK();
+ GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glGenMailboxCHROMIUM("
+ << static_cast<const void*>(mailbox) << ")");
+ TRACE_EVENT0("gpu", "GLES2::GenMailboxCHROMIUM");
+
+ helper_->GenMailboxCHROMIUM(kResultBucketId);
+
+ std::vector<GLbyte> result;
+ GetBucketContents(kResultBucketId, &result);
+
+ std::copy(result.begin(), result.end(), mailbox);
+ CheckGLError();
+}
+
+void GLES2Implementation::PushGroupMarkerEXT(
+ GLsizei length, const GLchar* marker) {
+ GPU_CLIENT_SINGLE_THREAD_CHECK();
+ GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glPushGroupMarkerEXT("
+ << length << ", " << marker << ")");
+ if (!marker) {
+ marker = "";
+ }
+ SetBucketAsString(
+ kResultBucketId,
+ (length ? std::string(marker, length) : std::string(marker)));
+ helper_->PushGroupMarkerEXT(kResultBucketId);
+ helper_->SetBucketSize(kResultBucketId, 0);
+ debug_marker_manager_.PushGroup(
+ length ? std::string(marker, length) : std::string(marker));
+}
+
+void GLES2Implementation::InsertEventMarkerEXT(
+ GLsizei length, const GLchar* marker) {
+ GPU_CLIENT_SINGLE_THREAD_CHECK();
+ GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glInsertEventMarkerEXT("
+ << length << ", " << marker << ")");
+ if (!marker) {
+ marker = "";
+ }
+ SetBucketAsString(
+ kResultBucketId,
+ (length ? std::string(marker, length) : std::string(marker)));
+ helper_->InsertEventMarkerEXT(kResultBucketId);
+ helper_->SetBucketSize(kResultBucketId, 0);
+ debug_marker_manager_.SetMarker(
+ length ? std::string(marker, length) : std::string(marker));
+}
+
+void GLES2Implementation::PopGroupMarkerEXT() {
+ GPU_CLIENT_SINGLE_THREAD_CHECK();
+ GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glPopGroupMarkerEXT()");
+ helper_->PopGroupMarkerEXT();
+ debug_marker_manager_.PopGroup();
+}
+
+void GLES2Implementation::TraceBeginCHROMIUM(const char* name) {
+ GPU_CLIENT_SINGLE_THREAD_CHECK();
+ GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glTraceBeginCHROMIUM("
+ << name << ")");
+ if (current_trace_name_.get()) {
+ SetGLError(GL_INVALID_OPERATION, "glTraceBeginCHROMIUM",
+ "trace already running");
+ return;
+ }
+ TRACE_EVENT_COPY_ASYNC_BEGIN0("gpu", name, this);
+ SetBucketAsCString(kResultBucketId, name);
+ helper_->TraceBeginCHROMIUM(kResultBucketId);
+ helper_->SetBucketSize(kResultBucketId, 0);
+ current_trace_name_.reset(new std::string(name));
+}
+
+void GLES2Implementation::TraceEndCHROMIUM() {
+ GPU_CLIENT_SINGLE_THREAD_CHECK();
+ GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glTraceEndCHROMIUM(" << ")");
+ if (!current_trace_name_.get()) {
+ SetGLError(GL_INVALID_OPERATION, "glTraceEndCHROMIUM",
+ "missing begin trace");
+ return;
+ }
+ helper_->TraceEndCHROMIUM();
+ TRACE_EVENT_COPY_ASYNC_END0("gpu", current_trace_name_->c_str(), this);
+ current_trace_name_.reset();
+}
+
+void* GLES2Implementation::MapBufferCHROMIUM(GLuint target, GLenum access) {
+ GPU_CLIENT_SINGLE_THREAD_CHECK();
+ GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glMapBufferCHROMIUM("
+ << target << ", " << GLES2Util::GetStringEnum(access) << ")");
+ switch (target) {
+ case GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM:
+ if (access != GL_READ_ONLY) {
+ SetGLError(GL_INVALID_ENUM, "glMapBufferCHROMIUM", "bad access mode");
+ return NULL;
+ }
+ break;
+ case GL_PIXEL_UNPACK_TRANSFER_BUFFER_CHROMIUM:
+ if (access != GL_WRITE_ONLY) {
+ SetGLError(GL_INVALID_ENUM, "glMapBufferCHROMIUM", "bad access mode");
+ return NULL;
+ }
+ break;
+ default:
+ SetGLError(
+ GL_INVALID_ENUM, "glMapBufferCHROMIUM", "invalid target");
+ return NULL;
+ }
+ GLuint buffer_id;
+ GetBoundPixelTransferBuffer(target, "glMapBufferCHROMIUM", &buffer_id);
+ if (!buffer_id) {
+ return NULL;
+ }
+ BufferTracker::Buffer* buffer = buffer_tracker_->GetBuffer(buffer_id);
+ if (!buffer) {
+ SetGLError(GL_INVALID_OPERATION, "glMapBufferCHROMIUM", "invalid buffer");
+ return NULL;
+ }
+ if (buffer->mapped()) {
+ SetGLError(GL_INVALID_OPERATION, "glMapBufferCHROMIUM", "already mapped");
+ return NULL;
+ }
+ // Here we wait for previous transfer operations to be finished.
+ // TODO(hubbe): AsyncTex(Sub)Image2dCHROMIUM does not currently work
+ // with this method of synchronization. Until this is fixed,
+ // MapBufferCHROMIUM will not block even if the transfer is not ready
+ // for these calls.
+ if (buffer->transfer_ready_token()) {
+ helper_->WaitForToken(buffer->transfer_ready_token());
+ buffer->set_transfer_ready_token(0);
+ }
+ buffer->set_mapped(true);
+
+ GPU_CLIENT_LOG(" returned " << buffer->address());
+ CheckGLError();
+ return buffer->address();
+}
+
+GLboolean GLES2Implementation::UnmapBufferCHROMIUM(GLuint target) {
+ GPU_CLIENT_SINGLE_THREAD_CHECK();
+ GPU_CLIENT_LOG(
+ "[" << GetLogPrefix() << "] glUnmapBufferCHROMIUM(" << target << ")");
+ GLuint buffer_id;
+ if (!GetBoundPixelTransferBuffer(target, "glMapBufferCHROMIUM", &buffer_id)) {
+ SetGLError(GL_INVALID_ENUM, "glUnmapBufferCHROMIUM", "invalid target");
+ }
+ if (!buffer_id) {
+ return false;
+ }
+ BufferTracker::Buffer* buffer = buffer_tracker_->GetBuffer(buffer_id);
+ if (!buffer) {
+ SetGLError(GL_INVALID_OPERATION, "glUnmapBufferCHROMIUM", "invalid buffer");
+ return false;
+ }
+ if (!buffer->mapped()) {
+ SetGLError(GL_INVALID_OPERATION, "glUnmapBufferCHROMIUM", "not mapped");
+ return false;
+ }
+ buffer->set_mapped(false);
+ CheckGLError();
+ return true;
+}
+
+void GLES2Implementation::AsyncTexImage2DCHROMIUM(
+ GLenum target, GLint level, GLint internalformat, GLsizei width,
+ GLsizei height, GLint border, GLenum format, GLenum type,
+ const void* pixels) {
+ GPU_CLIENT_SINGLE_THREAD_CHECK();
+ GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glTexImage2D("
+ << GLES2Util::GetStringTextureTarget(target) << ", "
+ << level << ", "
+ << GLES2Util::GetStringTextureInternalFormat(internalformat) << ", "
+ << width << ", " << height << ", " << border << ", "
+ << GLES2Util::GetStringTextureFormat(format) << ", "
+ << GLES2Util::GetStringPixelType(type) << ", "
+ << static_cast<const void*>(pixels) << ")");
+ if (level < 0 || height < 0 || width < 0) {
+ SetGLError(GL_INVALID_VALUE, "glTexImage2D", "dimension < 0");
+ return;
+ }
+ uint32 size;
+ uint32 unpadded_row_size;
+ uint32 padded_row_size;
+ if (!GLES2Util::ComputeImageDataSizes(
+ width, height, format, type, unpack_alignment_, &size,
+ &unpadded_row_size, &padded_row_size)) {
+ SetGLError(GL_INVALID_VALUE, "glTexImage2D", "image size too large");
+ return;
+ }
+
+ // If there's no data/buffer just issue the AsyncTexImage2D
+ if (!pixels && !bound_pixel_unpack_transfer_buffer_id_) {
+ helper_->AsyncTexImage2DCHROMIUM(
+ target, level, internalformat, width, height, border, format, type,
+ 0, 0);
+ return;
+ }
+
+ // Otherwise, async uploads require a transfer buffer to be bound.
+ // TODO(hubbe): Make MapBufferCHROMIUM block if someone tries to re-use
+ // the buffer before the transfer is finished. (Currently such
+ // synchronization has to be handled manually.)
+ GLuint offset = ToGLuint(pixels);
+ BufferTracker::Buffer* buffer = GetBoundPixelUnpackTransferBufferIfValid(
+ bound_pixel_unpack_transfer_buffer_id_,
+ "glAsyncTexImage2DCHROMIUM", offset, size);
+ if (buffer && buffer->shm_id() != -1) {
+ helper_->AsyncTexImage2DCHROMIUM(
+ target, level, internalformat, width, height, border, format, type,
+ buffer->shm_id(), buffer->shm_offset() + offset);
+ }
+}
+
+void GLES2Implementation::AsyncTexSubImage2DCHROMIUM(
+ GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width,
+ GLsizei height, GLenum format, GLenum type, const void* pixels) {
+ GPU_CLIENT_SINGLE_THREAD_CHECK();
+ GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glAsyncTexSubImage2DCHROMIUM("
+ << GLES2Util::GetStringTextureTarget(target) << ", "
+ << level << ", "
+ << xoffset << ", " << yoffset << ", "
+ << width << ", " << height << ", "
+ << GLES2Util::GetStringTextureFormat(format) << ", "
+ << GLES2Util::GetStringPixelType(type) << ", "
+ << static_cast<const void*>(pixels) << ")");
+ if (level < 0 || height < 0 || width < 0) {
+ SetGLError(
+ GL_INVALID_VALUE, "glAsyncTexSubImage2DCHROMIUM", "dimension < 0");
+ return;
+ }
+
+ uint32 size;
+ uint32 unpadded_row_size;
+ uint32 padded_row_size;
+ if (!GLES2Util::ComputeImageDataSizes(
+ width, height, format, type, unpack_alignment_, &size,
+ &unpadded_row_size, &padded_row_size)) {
+ SetGLError(
+ GL_INVALID_VALUE, "glAsyncTexSubImage2DCHROMIUM", "size to large");
+ return;
+ }
+
+ // Async uploads require a transfer buffer to be bound.
+ // TODO(hubbe): Make MapBufferCHROMIUM block if someone tries to re-use
+ // the buffer before the transfer is finished. (Currently such
+ // synchronization has to be handled manually.)
+ GLuint offset = ToGLuint(pixels);
+ BufferTracker::Buffer* buffer = GetBoundPixelUnpackTransferBufferIfValid(
+ bound_pixel_unpack_transfer_buffer_id_,
+ "glAsyncTexSubImage2DCHROMIUM", offset, size);
+ if (buffer && buffer->shm_id() != -1) {
+ helper_->AsyncTexSubImage2DCHROMIUM(
+ target, level, xoffset, yoffset, width, height, format, type,
+ buffer->shm_id(), buffer->shm_offset() + offset);
+ }
+}
+
+void GLES2Implementation::WaitAsyncTexImage2DCHROMIUM(GLenum target) {
+ GPU_CLIENT_SINGLE_THREAD_CHECK();
+ GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glWaitAsyncTexImage2DCHROMIUM("
+ << GLES2Util::GetStringTextureTarget(target) << ")");
+ helper_->WaitAsyncTexImage2DCHROMIUM(target);
+ CheckGLError();
+}
+
+GLuint GLES2Implementation::InsertSyncPointCHROMIUM() {
+ GPU_CLIENT_SINGLE_THREAD_CHECK();
+ GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glInsertSyncPointCHROMIUM");
+ return helper_->InsertSyncPointCHROMIUM();
+}
+
+GLuint GLES2Implementation::CreateImageCHROMIUMHelper(
+ GLsizei width, GLsizei height, GLenum internalformat) {
+ if (width <= 0) {
+ SetGLError(GL_INVALID_VALUE, "glCreateImageCHROMIUM", "width <= 0");
+ return 0;
+ }
+
+ if (height <= 0) {
+ SetGLError(GL_INVALID_VALUE, "glCreateImageCHROMIUM", "height <= 0");
+ return 0;
+ }
+ // Flush the command stream to ensure ordering in case the newly
+ // returned image_id has recently been in use with a different buffer.
+ helper_->CommandBufferHelper::Flush();
+
+ // Create new buffer.
+ GLuint buffer_id = gpu_memory_buffer_tracker_->CreateBuffer(
+ width, height, internalformat);
+ if (buffer_id == 0) {
+ SetGLError(GL_OUT_OF_MEMORY, "glCreateImageCHROMIUM", "out of GPU memory.");
+ return 0;
+ }
+ return buffer_id;
+}
+
+GLuint GLES2Implementation::CreateImageCHROMIUM(
+ GLsizei width, GLsizei height, GLenum internalformat) {
+ GPU_CLIENT_SINGLE_THREAD_CHECK();
+ GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glCreateImageCHROMIUM("
+ << width << ", "
+ << height << ", "
+ << GLES2Util::GetStringTextureInternalFormat(internalformat) << ")");
+ GLuint image_id = CreateImageCHROMIUMHelper(width, height, internalformat);
+ CheckGLError();
+ return image_id;
+}
+
+void GLES2Implementation::DestroyImageCHROMIUMHelper(GLuint image_id) {
+ gfx::GpuMemoryBuffer* gpu_buffer = gpu_memory_buffer_tracker_->GetBuffer(
+ image_id);
+ if (!gpu_buffer) {
+ SetGLError(GL_INVALID_OPERATION, "glDestroyImageCHROMIUM", "invalid image");
+ return;
+ }
+
+ // Flush the command stream to make sure all pending commands
+ // that may refer to the image_id are executed on the service side.
+ helper_->CommandBufferHelper::Flush();
+ gpu_memory_buffer_tracker_->RemoveBuffer(image_id);
+}
+
+void GLES2Implementation::DestroyImageCHROMIUM(GLuint image_id) {
+ GPU_CLIENT_SINGLE_THREAD_CHECK();
+ GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glDestroyImageCHROMIUM("
+ << image_id << ")");
+ DestroyImageCHROMIUMHelper(image_id);
+ CheckGLError();
+}
+
+void GLES2Implementation::UnmapImageCHROMIUMHelper(GLuint image_id) {
+ gfx::GpuMemoryBuffer* gpu_buffer = gpu_memory_buffer_tracker_->GetBuffer(
+ image_id);
+ if (!gpu_buffer) {
+ SetGLError(GL_INVALID_OPERATION, "glUnmapImageCHROMIUM", "invalid image");
+ return;
+ }
+
+ if (!gpu_buffer->IsMapped()) {
+ SetGLError(GL_INVALID_OPERATION, "glUnmapImageCHROMIUM", "not mapped");
+ return;
+ }
+ gpu_buffer->Unmap();
+}
+
+void GLES2Implementation::UnmapImageCHROMIUM(GLuint image_id) {
+ GPU_CLIENT_SINGLE_THREAD_CHECK();
+ GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glUnmapImageCHROMIUM("
+ << image_id << ")");
+
+ UnmapImageCHROMIUMHelper(image_id);
+ CheckGLError();
+}
+
+void* GLES2Implementation::MapImageCHROMIUMHelper(GLuint image_id,
+ GLenum access) {
+ gfx::GpuMemoryBuffer* gpu_buffer = gpu_memory_buffer_tracker_->GetBuffer(
+ image_id);
+ if (!gpu_buffer) {
+ SetGLError(GL_INVALID_OPERATION, "glMapImageCHROMIUM", "invalid image");
+ return NULL;
+ }
+ gfx::GpuMemoryBuffer::AccessMode mode;
+ switch(access) {
+ case GL_WRITE_ONLY:
+ mode = gfx::GpuMemoryBuffer::WRITE_ONLY;
+ break;
+ case GL_READ_ONLY:
+ mode = gfx::GpuMemoryBuffer::READ_ONLY;
+ break;
+ case GL_READ_WRITE:
+ mode = gfx::GpuMemoryBuffer::READ_WRITE;
+ break;
+ default:
+ SetGLError(GL_INVALID_ENUM, "glMapImageCHROMIUM",
+ "invalid GPU access mode");
+ return NULL;
+ }
+
+ if (gpu_buffer->IsMapped()) {
+ SetGLError(GL_INVALID_OPERATION, "glMapImageCHROMIUM", "already mapped");
+ return NULL;
+ }
+
+ void* mapped_buffer = NULL;
+ gpu_buffer->Map(mode, &mapped_buffer);
+ return mapped_buffer;
+}
+
+void* GLES2Implementation::MapImageCHROMIUM(
+ GLuint image_id, GLenum access) {
+ GPU_CLIENT_SINGLE_THREAD_CHECK();
+ GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glMapImageCHROMIUM("
+ << image_id << ", "
+ << GLES2Util::GetStringEnum(access) << ")");
+
+ void* mapped = MapImageCHROMIUMHelper(image_id, access);
+ CheckGLError();
+ return mapped;
+}
+
+void GLES2Implementation::GetImageParameterivCHROMIUMHelper(
+ GLuint image_id, GLenum pname, GLint* params) {
+ if (pname != GL_IMAGE_ROWBYTES_CHROMIUM) {
+ SetGLError(GL_INVALID_ENUM, "glGetImageParameterivCHROMIUM",
+ "invalid parameter");
+ return;
+ }
+
+ gfx::GpuMemoryBuffer* gpu_buffer = gpu_memory_buffer_tracker_->GetBuffer(
+ image_id);
+ if (!gpu_buffer) {
+ SetGLError(GL_INVALID_OPERATION, "glGetImageParameterivCHROMIUM",
+ "invalid image");
+ return;
+ }
+
+ *params = gpu_buffer->GetStride();
+}
+
+void GLES2Implementation::GetImageParameterivCHROMIUM(
+ GLuint image_id, GLenum pname, GLint* params) {
+ GPU_CLIENT_SINGLE_THREAD_CHECK();
+ GPU_CLIENT_VALIDATE_DESTINATION_INITALIZATION(GLint, params);
+ GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glImageParameterivCHROMIUM("
+ << image_id << ", "
+ << GLES2Util::GetStringBufferParameter(pname) << ", "
+ << static_cast<const void*>(params) << ")");
+ GetImageParameterivCHROMIUMHelper(image_id, pname, params);
+ CheckGLError();
+}
+
+// Include the auto-generated part of this file. We split this because it means
+// we can easily edit the non-auto generated parts right here in this file
+// instead of having to edit some template or the code generator.
+#include "gpu/command_buffer/client/gles2_implementation_impl_autogen.h"
+
+} // namespace gles2
+} // namespace gpu