| // 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 <GLES2/gl2.h> | 
 | #include <GLES2/gl2ext.h> | 
 | #include <GLES2/gl2extchromium.h> | 
 | #include <GLES3/gl3.h> | 
 | #include <stddef.h> | 
 | #include <stdint.h> | 
 | #include <algorithm> | 
 | #include <map> | 
 | #include <set> | 
 | #include <sstream> | 
 | #include <string> | 
 | #include "base/atomic_sequence_num.h" | 
 | #include "base/compiler_specific.h" | 
 | #include "base/strings/string_split.h" | 
 | #include "base/strings/stringprintf.h" | 
 | #include "base/sys_info.h" | 
 | #include "base/threading/thread_task_runner_handle.h" | 
 | #include "base/trace_event/memory_allocator_dump.h" | 
 | #include "base/trace_event/memory_dump_manager.h" | 
 | #include "base/trace_event/process_memory_dump.h" | 
 | #include "base/trace_event/trace_event.h" | 
 | #include "gpu/command_buffer/client/buffer_tracker.h" | 
 | #include "gpu/command_buffer/client/gles2_cmd_helper.h" | 
 | #include "gpu/command_buffer/client/gpu_control.h" | 
 | #include "gpu/command_buffer/client/program_info_manager.h" | 
 | #include "gpu/command_buffer/client/query_tracker.h" | 
 | #include "gpu/command_buffer/client/shared_memory_limits.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/id_allocator.h" | 
 | #include "gpu/command_buffer/common/sync_token.h" | 
 | #include "ui/gfx/geometry/rect.h" | 
 | #include "ui/gfx/geometry/rect_f.h" | 
 |  | 
 | #if defined(GPU_CLIENT_DEBUG) | 
 | #include "base/command_line.h" | 
 | #include "gpu/command_buffer/client/gpu_switches.h" | 
 | #endif | 
 |  | 
 | namespace gpu { | 
 | namespace gles2 { | 
 |  | 
 | namespace { | 
 |  | 
 | void CopyRectToBuffer(const void* pixels, | 
 |                       uint32_t height, | 
 |                       uint32_t unpadded_row_size, | 
 |                       uint32_t pixels_padded_row_size, | 
 |                       void* buffer, | 
 |                       uint32_t buffer_padded_row_size) { | 
 |   if (height == 0) | 
 |     return; | 
 |   const int8_t* source = static_cast<const int8_t*>(pixels); | 
 |   int8_t* dest = static_cast<int8_t*>(buffer); | 
 |   if (pixels_padded_row_size != buffer_padded_row_size) { | 
 |     for (uint32_t ii = 0; ii < height; ++ii) { | 
 |       memcpy(dest, source, unpadded_row_size); | 
 |       dest += buffer_padded_row_size; | 
 |       source += pixels_padded_row_size; | 
 |     } | 
 |   } else { | 
 |     uint32_t size = (height - 1) * pixels_padded_row_size + unpadded_row_size; | 
 |     memcpy(dest, source, size); | 
 |   } | 
 | } | 
 |  | 
 | // A 32-bit and 64-bit compatible way of converting a pointer to a GLuint. | 
 | GLuint ToGLuint(const void* ptr) { | 
 |   return static_cast<GLuint>(reinterpret_cast<size_t>(ptr)); | 
 | } | 
 |  | 
 | static base::StaticAtomicSequenceNumber g_flush_id; | 
 |  | 
 | uint32_t GenerateNextFlushId() { | 
 |   return static_cast<uint32_t>(g_flush_id.GetNext()); | 
 | } | 
 |  | 
 | }  // anonymous namespace | 
 |  | 
 | #if !defined(_MSC_VER) | 
 | const size_t GLES2Implementation::kMaxSizeOfSimpleResult; | 
 | const unsigned int GLES2Implementation::kStartingOffset; | 
 | #endif | 
 |  | 
 | GLES2Implementation::GLStaticState::GLStaticState() { | 
 | } | 
 |  | 
 | GLES2Implementation::GLStaticState::~GLStaticState() { | 
 | } | 
 |  | 
 | GLES2Implementation::SingleThreadChecker::SingleThreadChecker( | 
 |     GLES2Implementation* gles2_implementation) | 
 |     : gles2_implementation_(gles2_implementation) { | 
 |   CHECK_EQ(0, gles2_implementation_->use_count_); | 
 |   ++gles2_implementation_->use_count_; | 
 | } | 
 |  | 
 | GLES2Implementation::SingleThreadChecker::~SingleThreadChecker() { | 
 |   --gles2_implementation_->use_count_; | 
 |   CHECK_EQ(0, gles2_implementation_->use_count_); | 
 | } | 
 |  | 
 | GLES2Implementation::GLES2Implementation( | 
 |     GLES2CmdHelper* helper, | 
 |     scoped_refptr<ShareGroup> share_group, | 
 |     TransferBufferInterface* transfer_buffer, | 
 |     bool bind_generates_resource, | 
 |     bool lose_context_when_out_of_memory, | 
 |     bool support_client_side_arrays, | 
 |     GpuControl* gpu_control) | 
 |     : helper_(helper), | 
 |       transfer_buffer_(transfer_buffer), | 
 |       chromium_framebuffer_multisample_(kUnknownExtensionStatus), | 
 |       pack_alignment_(4), | 
 |       pack_row_length_(0), | 
 |       pack_skip_pixels_(0), | 
 |       pack_skip_rows_(0), | 
 |       unpack_alignment_(4), | 
 |       unpack_row_length_(0), | 
 |       unpack_image_height_(0), | 
 |       unpack_skip_rows_(0), | 
 |       unpack_skip_pixels_(0), | 
 |       unpack_skip_images_(0), | 
 |       active_texture_unit_(0), | 
 |       bound_framebuffer_(0), | 
 |       bound_read_framebuffer_(0), | 
 |       bound_renderbuffer_(0), | 
 |       current_program_(0), | 
 |       bound_array_buffer_(0), | 
 |       bound_copy_read_buffer_(0), | 
 |       bound_copy_write_buffer_(0), | 
 |       bound_pixel_pack_buffer_(0), | 
 |       bound_pixel_unpack_buffer_(0), | 
 |       bound_transform_feedback_buffer_(0), | 
 |       bound_uniform_buffer_(0), | 
 |       bound_pixel_pack_transfer_buffer_id_(0), | 
 |       bound_pixel_unpack_transfer_buffer_id_(0), | 
 |       error_bits_(0), | 
 |       debug_(false), | 
 |       lose_context_when_out_of_memory_(lose_context_when_out_of_memory), | 
 |       support_client_side_arrays_(support_client_side_arrays), | 
 |       use_count_(0), | 
 |       flush_id_(0), | 
 |       max_extra_transfer_buffer_size_( | 
 | #if defined(OS_NACL) | 
 |           0), | 
 | #else | 
 |           // Do not use more than 5% of extra shared memory, and do not | 
 |           // use any extra for memory contrained devices (<=1GB). | 
 |           base::SysInfo::AmountOfPhysicalMemory() > 1024 * 1024 * 1024 | 
 |               ? base::saturated_cast<uint32_t>( | 
 |                     base::SysInfo::AmountOfPhysicalMemory() / 20) | 
 |               : 0), | 
 | #endif | 
 |       current_trace_stack_(0), | 
 |       gpu_control_(gpu_control), | 
 |       capabilities_(gpu_control->GetCapabilities()), | 
 |       aggressively_free_resources_(false), | 
 |       cached_extension_string_(nullptr), | 
 |       weak_ptr_factory_(this) { | 
 |   DCHECK(helper); | 
 |   DCHECK(transfer_buffer); | 
 |   DCHECK(gpu_control); | 
 |  | 
 |   std::stringstream ss; | 
 |   ss << std::hex << this; | 
 |   this_in_hex_ = ss.str(); | 
 |  | 
 |   GPU_CLIENT_LOG_CODE_BLOCK({ | 
 |     debug_ = base::CommandLine::ForCurrentProcess()->HasSwitch( | 
 |         switches::kEnableGPUClientLogging); | 
 |   }); | 
 |  | 
 |   share_group_ = | 
 |       (share_group ? std::move(share_group) | 
 |                    : new ShareGroup( | 
 |                          bind_generates_resource, | 
 |                          gpu_control_->GetCommandBufferID().GetUnsafeValue())); | 
 |   DCHECK(share_group_->bind_generates_resource() == 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, | 
 |     unsigned int mapped_memory_limit) { | 
 |   TRACE_EVENT0("gpu", "GLES2Implementation::Initialize"); | 
 |   DCHECK_GE(starting_transfer_buffer_size, min_transfer_buffer_size); | 
 |   DCHECK_LE(starting_transfer_buffer_size, max_transfer_buffer_size); | 
 |   DCHECK_GE(min_transfer_buffer_size, kStartingOffset); | 
 |  | 
 |   gpu_control_->SetGpuControlClient(this); | 
 |  | 
 |   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_, mapped_memory_limit)); | 
 |  | 
 |   unsigned chunk_size = 2 * 1024 * 1024; | 
 |   if (mapped_memory_limit != SharedMemoryLimits::kNoLimit) { | 
 |     // Use smaller chunks if the client is very memory conscientious. | 
 |     chunk_size = std::min(mapped_memory_limit / 4, chunk_size); | 
 |   } | 
 |   mapped_memory_->set_chunk_size_multiple(chunk_size); | 
 |  | 
 |   GLStaticState::ShaderPrecisionMap* shader_precisions = | 
 |       &static_state_.shader_precisions; | 
 |   capabilities_.VisitPrecisions([shader_precisions]( | 
 |       GLenum shader, GLenum type, Capabilities::ShaderPrecision* result) { | 
 |     const GLStaticState::ShaderPrecisionKey key(shader, type); | 
 |     cmds::GetShaderPrecisionFormat::Result cached_result = { | 
 |         true, result->min_range, result->max_range, result->precision}; | 
 |     shader_precisions->insert(std::make_pair(key, cached_result)); | 
 |   }); | 
 |  | 
 |   util_.set_num_compressed_texture_formats( | 
 |       capabilities_.num_compressed_texture_formats); | 
 |   util_.set_num_shader_binary_formats(capabilities_.num_shader_binary_formats); | 
 |  | 
 |   texture_units_.reset( | 
 |       new TextureUnit[capabilities_.max_combined_texture_image_units]); | 
 |  | 
 |   query_tracker_.reset(new QueryTracker(mapped_memory_.get())); | 
 |   buffer_tracker_.reset(new BufferTracker(mapped_memory_.get())); | 
 |  | 
 |   query_id_allocator_.reset(new IdAllocator()); | 
 |   if (support_client_side_arrays_) { | 
 |     GetIdHandler(id_namespaces::kBuffers)->MakeIds( | 
 |        this, kClientSideArrayId, arraysize(reserved_ids_), &reserved_ids_[0]); | 
 |   } | 
 |  | 
 |   vertex_array_object_manager_.reset(new VertexArrayObjectManager( | 
 |       capabilities_.max_vertex_attribs, reserved_ids_[0], reserved_ids_[1], | 
 |       support_client_side_arrays_)); | 
 |  | 
 |   // GL_BIND_GENERATES_RESOURCE_CHROMIUM state must be the same | 
 |   // on Client & Service. | 
 |   if (capabilities_.bind_generates_resource_chromium != | 
 |       (share_group_->bind_generates_resource() ? 1 : 0)) { | 
 |     SetGLError(GL_INVALID_OPERATION, | 
 |                "Initialize", | 
 |                "Service bind_generates_resource mismatch."); | 
 |     return false; | 
 |   } | 
 |  | 
 |   // In certain cases, ThreadTaskRunnerHandle isn't set (Android Webview). | 
 |   // Don't register a dump provider in these cases. | 
 |   // TODO(ericrk): Get this working in Android Webview. crbug.com/517156 | 
 |   if (base::ThreadTaskRunnerHandle::IsSet()) { | 
 |     base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider( | 
 |         this, "GLES2Implementation", base::ThreadTaskRunnerHandle::Get()); | 
 |   } | 
 |  | 
 |   return true; | 
 | } | 
 |  | 
 | GLES2Implementation::~GLES2Implementation() { | 
 |   base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider( | 
 |       this); | 
 |  | 
 |   // 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(); | 
 |  | 
 |   // GLES2Implementation::Initialize() could fail before allocating | 
 |   // reserved_ids_, so we need delete them carefully. | 
 |   if (support_client_side_arrays_ && reserved_ids_[0]) { | 
 |     DeleteBuffers(arraysize(reserved_ids_), &reserved_ids_[0]); | 
 |   } | 
 |  | 
 |   // Release remaining BufferRange mem; This is when a MapBufferRange() is | 
 |   // called but not the UnmapBuffer() pair. | 
 |   ClearMappedBufferRangeMap(); | 
 |  | 
 |   // Release any per-context data in share group. | 
 |   share_group_->FreeContext(this); | 
 |  | 
 |   buffer_tracker_.reset(); | 
 |  | 
 |   // Make sure the commands make it the service. | 
 |   WaitForCmd(); | 
 |  | 
 |   // The gpu_control_ outlives this class, so clear the client on it before we | 
 |   // self-destruct. | 
 |   gpu_control_->SetGpuControlClient(nullptr); | 
 | } | 
 |  | 
 | GLES2CmdHelper* GLES2Implementation::helper() const { | 
 |   return helper_; | 
 | } | 
 |  | 
 | IdHandlerInterface* GLES2Implementation::GetIdHandler(int namespace_id) const { | 
 |   return share_group_->GetIdHandler(namespace_id); | 
 | } | 
 |  | 
 | RangeIdHandlerInterface* GLES2Implementation::GetRangeIdHandler( | 
 |     int namespace_id) const { | 
 |   return share_group_->GetRangeIdHandler(namespace_id); | 
 | } | 
 |  | 
 | IdAllocator* GLES2Implementation::GetIdAllocator(int namespace_id) const { | 
 |   if (namespace_id == id_namespaces::kQueries) | 
 |     return query_id_allocator_.get(); | 
 |   NOTREACHED(); | 
 |   return NULL; | 
 | } | 
 |  | 
 | void GLES2Implementation::OnGpuControlLostContext() { | 
 |   // This should never occur more than once. | 
 |   DCHECK(!lost_context_callback_run_); | 
 |   lost_context_callback_run_ = true; | 
 |   share_group_->Lose(); | 
 |   if (!lost_context_callback_.is_null()) | 
 |     lost_context_callback_.Run(); | 
 | } | 
 |  | 
 | void GLES2Implementation::OnGpuControlLostContextMaybeReentrant() { | 
 |   // Queries for lost context state should immediately reflect reality, | 
 |   // but don't call out to clients yet to avoid them re-entering this | 
 |   // class. | 
 |   share_group_->Lose(); | 
 | } | 
 |  | 
 | void GLES2Implementation::OnGpuControlErrorMessage(const char* message, | 
 |                                                    int32_t id) { | 
 |   if (!error_message_callback_.is_null()) | 
 |     error_message_callback_.Run(message, id); | 
 | } | 
 |  | 
 | void* GLES2Implementation::GetResultBuffer() { | 
 |   return transfer_buffer_->GetResultBuffer(); | 
 | } | 
 |  | 
 | int32_t GLES2Implementation::GetResultShmId() { | 
 |   return transfer_buffer_->GetShmId(); | 
 | } | 
 |  | 
 | uint32_t GLES2Implementation::GetResultShmOffset() { | 
 |   return transfer_buffer_->GetResultOffset(); | 
 | } | 
 |  | 
 | void GLES2Implementation::FreeUnusedSharedMemory() { | 
 |   mapped_memory_->FreeUnused(); | 
 | } | 
 |  | 
 | void GLES2Implementation::FreeEverything() { | 
 |   WaitForCmd(); | 
 |   query_tracker_->Shrink(); | 
 |   FreeUnusedSharedMemory(); | 
 |   transfer_buffer_->Free(); | 
 |   helper_->FreeRingBuffer(); | 
 | } | 
 |  | 
 | void GLES2Implementation::RunIfContextNotLost(const base::Closure& callback) { | 
 |   if (!lost_context_callback_run_) | 
 |     callback.Run(); | 
 | } | 
 |  | 
 | void GLES2Implementation::SignalSyncToken(const gpu::SyncToken& sync_token, | 
 |                                           const base::Closure& callback) { | 
 |   if (sync_token.HasData() && | 
 |       (sync_token.verified_flush() || | 
 |        gpu_control_->CanWaitUnverifiedSyncToken(&sync_token))) { | 
 |  | 
 |     gpu::SyncToken intermediate_sync_token = sync_token; | 
 |  | 
 |     // Mark the intermediate sync token as verified if we can wait on | 
 |     // unverified sync tokens. | 
 |     intermediate_sync_token.SetVerifyFlush(); | 
 |  | 
 |     gpu_control_->SignalSyncToken( | 
 |         intermediate_sync_token, | 
 |         base::Bind(&GLES2Implementation::RunIfContextNotLost, | 
 |                    weak_ptr_factory_.GetWeakPtr(), | 
 |                    callback)); | 
 |   } else { | 
 |     // Invalid sync token, just call the callback immediately. | 
 |     callback.Run(); | 
 |   } | 
 | } | 
 |  | 
 | void GLES2Implementation::SignalQuery(uint32_t query, | 
 |                                       const base::Closure& callback) { | 
 |   // Flush previously entered commands to ensure ordering with any | 
 |   // glBeginQueryEXT() calls that may have been put into the context. | 
 |   ShallowFlushCHROMIUM(); | 
 |   gpu_control_->SignalQuery( | 
 |       query, | 
 |       base::Bind(&GLES2Implementation::RunIfContextNotLost, | 
 |                  weak_ptr_factory_.GetWeakPtr(), | 
 |                  callback)); | 
 | } | 
 |  | 
 | void GLES2Implementation::SetAggressivelyFreeResources( | 
 |     bool aggressively_free_resources) { | 
 |   TRACE_EVENT1("gpu", "GLES2Implementation::SetAggressivelyFreeResources", | 
 |                "aggressively_free_resources", aggressively_free_resources); | 
 |   aggressively_free_resources_ = aggressively_free_resources; | 
 |  | 
 |   if (aggressively_free_resources_ && helper_->HaveRingBuffer()) { | 
 |     // Ensure that we clean up as much cache memory as possible and fully flush. | 
 |     FlushDriverCachesCHROMIUM(); | 
 |  | 
 |     // Flush will delete transfer buffer resources if | 
 |     // |aggressively_free_resources_| is true. | 
 |     Flush(); | 
 |   } else { | 
 |     ShallowFlushCHROMIUM(); | 
 |   } | 
 | } | 
 |  | 
 | bool GLES2Implementation::OnMemoryDump( | 
 |     const base::trace_event::MemoryDumpArgs& args, | 
 |     base::trace_event::ProcessMemoryDump* pmd) { | 
 |   if (!transfer_buffer_->HaveBuffer()) | 
 |     return true; | 
 |  | 
 |   const uint64_t tracing_process_id = | 
 |       base::trace_event::MemoryDumpManager::GetInstance() | 
 |           ->GetTracingProcessId(); | 
 |  | 
 |   base::trace_event::MemoryAllocatorDump* dump = pmd->CreateAllocatorDump( | 
 |       base::StringPrintf("gpu/transfer_buffer_memory/buffer_%d", | 
 |                          transfer_buffer_->GetShmId())); | 
 |   dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize, | 
 |                   base::trace_event::MemoryAllocatorDump::kUnitsBytes, | 
 |                   transfer_buffer_->GetSize()); | 
 |   dump->AddScalar("free_size", | 
 |                   base::trace_event::MemoryAllocatorDump::kUnitsBytes, | 
 |                   transfer_buffer_->GetFreeSize()); | 
 |   auto guid = | 
 |       GetBufferGUIDForTracing(tracing_process_id, transfer_buffer_->GetShmId()); | 
 |   const int kImportance = 2; | 
 |   pmd->CreateSharedGlobalAllocatorDump(guid); | 
 |   pmd->AddOwnershipEdge(dump->guid(), guid, kImportance); | 
 |  | 
 |   return true; | 
 | } | 
 |  | 
 | 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::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_t 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) { | 
 |     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_.is_null()) { | 
 |     std::string temp(GLES2Util::GetStringError(error)  + " : " + | 
 |                      function_name + ": " + (msg ? msg : "")); | 
 |     error_message_callback_.Run(temp.c_str(), 0); | 
 |   } | 
 |   error_bits_ |= GLES2Util::GLErrorToErrorBit(error); | 
 |  | 
 |   if (error == GL_OUT_OF_MEMORY && lose_context_when_out_of_memory_) { | 
 |     helper_->LoseContextCHROMIUM(GL_GUILTY_CONTEXT_RESET_ARB, | 
 |                                  GL_UNKNOWN_CONTEXT_RESET_ARB); | 
 |   } | 
 | } | 
 |  | 
 | 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_t bucket_id, | 
 |                                             std::vector<int8_t>* data) { | 
 |   TRACE_EVENT0("gpu", "GLES2::GetBucketContents"); | 
 |   DCHECK(data); | 
 |   const uint32_t 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_t size = *result; | 
 |   data->resize(size); | 
 |   if (size > 0u) { | 
 |     uint32_t 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_t 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_t bucket_id, | 
 |                                             const void* data, | 
 |                                             size_t size) { | 
 |   DCHECK(data); | 
 |   helper_->SetBucketSize(bucket_id, size); | 
 |   if (size > 0u) { | 
 |     uint32_t offset = 0; | 
 |     while (size) { | 
 |       ScopedTransferBufferPtr buffer(size, helper_, transfer_buffer_); | 
 |       if (!buffer.valid()) { | 
 |         return; | 
 |       } | 
 |       memcpy(buffer.address(), static_cast<const int8_t*>(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_t 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_t bucket_id, | 
 |                                             std::string* str) { | 
 |   DCHECK(str); | 
 |   std::vector<int8_t> 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_t 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) { | 
 |   // TODO(zmo): For all the BINDING points, there is a possibility where | 
 |   // resources are shared among multiple contexts, that the cached points | 
 |   // are invalid. It is not a problem for now, but once we allow resource | 
 |   // sharing in WebGL, we need to implement a mechanism to allow correct | 
 |   // client side binding points tracking.  crbug.com/465562. | 
 |  | 
 |   // ES2 parameters. | 
 |   switch (pname) { | 
 |     case GL_ACTIVE_TEXTURE: | 
 |       *params = active_texture_unit_ + GL_TEXTURE0; | 
 |       return true; | 
 |     case GL_ARRAY_BUFFER_BINDING: | 
 |       *params = bound_array_buffer_; | 
 |       return true; | 
 |     case GL_ELEMENT_ARRAY_BUFFER_BINDING: | 
 |       *params = | 
 |           vertex_array_object_manager_->bound_element_array_buffer(); | 
 |       return true; | 
 |     case GL_FRAMEBUFFER_BINDING: | 
 |       *params = bound_framebuffer_; | 
 |       return true; | 
 |     case GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS: | 
 |       *params = capabilities_.max_combined_texture_image_units; | 
 |       return true; | 
 |     case GL_MAX_CUBE_MAP_TEXTURE_SIZE: | 
 |       *params = capabilities_.max_cube_map_texture_size; | 
 |       return true; | 
 |     case GL_MAX_FRAGMENT_UNIFORM_VECTORS: | 
 |       *params = capabilities_.max_fragment_uniform_vectors; | 
 |       return true; | 
 |     case GL_MAX_RENDERBUFFER_SIZE: | 
 |       *params = capabilities_.max_renderbuffer_size; | 
 |       return true; | 
 |     case GL_MAX_TEXTURE_IMAGE_UNITS: | 
 |       *params = capabilities_.max_texture_image_units; | 
 |       return true; | 
 |     case GL_MAX_TEXTURE_SIZE: | 
 |       *params = capabilities_.max_texture_size; | 
 |       return true; | 
 |     case GL_MAX_VARYING_VECTORS: | 
 |       *params = capabilities_.max_varying_vectors; | 
 |       return true; | 
 |     case GL_MAX_VERTEX_ATTRIBS: | 
 |       *params = capabilities_.max_vertex_attribs; | 
 |       return true; | 
 |     case GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS: | 
 |       *params = capabilities_.max_vertex_texture_image_units; | 
 |       return true; | 
 |     case GL_MAX_VERTEX_UNIFORM_VECTORS: | 
 |       *params = capabilities_.max_vertex_uniform_vectors; | 
 |       return true; | 
 |     case GL_NUM_COMPRESSED_TEXTURE_FORMATS: | 
 |       *params = capabilities_.num_compressed_texture_formats; | 
 |       return true; | 
 |     case GL_NUM_SHADER_BINARY_FORMATS: | 
 |       *params = capabilities_.num_shader_binary_formats; | 
 |       return true; | 
 |     case GL_RENDERBUFFER_BINDING: | 
 |       *params = bound_renderbuffer_; | 
 |       return true; | 
 |     case GL_TEXTURE_BINDING_2D: | 
 |       *params = texture_units_[active_texture_unit_].bound_texture_2d; | 
 |       return true; | 
 |     case GL_TEXTURE_BINDING_CUBE_MAP: | 
 |       *params = texture_units_[active_texture_unit_].bound_texture_cube_map; | 
 |       return true; | 
 |  | 
 |     // Non-standard parameters. | 
 |     case GL_TEXTURE_BINDING_EXTERNAL_OES: | 
 |       *params = | 
 |           texture_units_[active_texture_unit_].bound_texture_external_oes; | 
 |       return true; | 
 |     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_READ_FRAMEBUFFER_BINDING: | 
 |       if (IsChromiumFramebufferMultisampleAvailable()) { | 
 |         *params = bound_read_framebuffer_; | 
 |         return true; | 
 |       } | 
 |       break; | 
 |     case GL_TIMESTAMP_EXT: | 
 |       // We convert all GPU timestamps to CPU time. | 
 |       *params = base::saturated_cast<GLint>( | 
 |           (base::TimeTicks::Now() - base::TimeTicks()).InMicroseconds() | 
 |           * base::Time::kNanosecondsPerMicrosecond); | 
 |       return true; | 
 |     case GL_GPU_DISJOINT_EXT: | 
 |       *params = static_cast<GLint>(query_tracker_->CheckAndResetDisjoint()); | 
 |       return true; | 
 |  | 
 |     // Non-cached parameters. | 
 |     case GL_ALIASED_LINE_WIDTH_RANGE: | 
 |     case GL_ALIASED_POINT_SIZE_RANGE: | 
 |     case GL_ALPHA_BITS: | 
 |     case GL_BLEND: | 
 |     case GL_BLEND_COLOR: | 
 |     case GL_BLEND_DST_ALPHA: | 
 |     case GL_BLEND_DST_RGB: | 
 |     case GL_BLEND_EQUATION_ALPHA: | 
 |     case GL_BLEND_EQUATION_RGB: | 
 |     case GL_BLEND_SRC_ALPHA: | 
 |     case GL_BLEND_SRC_RGB: | 
 |     case GL_BLUE_BITS: | 
 |     case GL_COLOR_CLEAR_VALUE: | 
 |     case GL_COLOR_WRITEMASK: | 
 |     case GL_COMPRESSED_TEXTURE_FORMATS: | 
 |     case GL_CULL_FACE: | 
 |     case GL_CULL_FACE_MODE: | 
 |     case GL_CURRENT_PROGRAM: | 
 |     case GL_DEPTH_BITS: | 
 |     case GL_DEPTH_CLEAR_VALUE: | 
 |     case GL_DEPTH_FUNC: | 
 |     case GL_DEPTH_RANGE: | 
 |     case GL_DEPTH_TEST: | 
 |     case GL_DEPTH_WRITEMASK: | 
 |     case GL_DITHER: | 
 |     case GL_FRONT_FACE: | 
 |     case GL_GENERATE_MIPMAP_HINT: | 
 |     case GL_GREEN_BITS: | 
 |     case GL_IMPLEMENTATION_COLOR_READ_FORMAT: | 
 |     case GL_IMPLEMENTATION_COLOR_READ_TYPE: | 
 |     case GL_LINE_WIDTH: | 
 |     case GL_MAX_VIEWPORT_DIMS: | 
 |     case GL_PACK_ALIGNMENT: | 
 |     case GL_POLYGON_OFFSET_FACTOR: | 
 |     case GL_POLYGON_OFFSET_FILL: | 
 |     case GL_POLYGON_OFFSET_UNITS: | 
 |     case GL_RED_BITS: | 
 |     case GL_SAMPLE_ALPHA_TO_COVERAGE: | 
 |     case GL_SAMPLE_BUFFERS: | 
 |     case GL_SAMPLE_COVERAGE: | 
 |     case GL_SAMPLE_COVERAGE_INVERT: | 
 |     case GL_SAMPLE_COVERAGE_VALUE: | 
 |     case GL_SAMPLES: | 
 |     case GL_SCISSOR_BOX: | 
 |     case GL_SCISSOR_TEST: | 
 |     case GL_SHADER_BINARY_FORMATS: | 
 |     case GL_SHADER_COMPILER: | 
 |     case GL_STENCIL_BACK_FAIL: | 
 |     case GL_STENCIL_BACK_FUNC: | 
 |     case GL_STENCIL_BACK_PASS_DEPTH_FAIL: | 
 |     case GL_STENCIL_BACK_PASS_DEPTH_PASS: | 
 |     case GL_STENCIL_BACK_REF: | 
 |     case GL_STENCIL_BACK_VALUE_MASK: | 
 |     case GL_STENCIL_BACK_WRITEMASK: | 
 |     case GL_STENCIL_BITS: | 
 |     case GL_STENCIL_CLEAR_VALUE: | 
 |     case GL_STENCIL_FAIL: | 
 |     case GL_STENCIL_FUNC: | 
 |     case GL_STENCIL_PASS_DEPTH_FAIL: | 
 |     case GL_STENCIL_PASS_DEPTH_PASS: | 
 |     case GL_STENCIL_REF: | 
 |     case GL_STENCIL_TEST: | 
 |     case GL_STENCIL_VALUE_MASK: | 
 |     case GL_STENCIL_WRITEMASK: | 
 |     case GL_SUBPIXEL_BITS: | 
 |     case GL_UNPACK_ALIGNMENT: | 
 |     case GL_VIEWPORT: | 
 |       return false; | 
 |     default: | 
 |       break; | 
 |   } | 
 |  | 
 |   if (capabilities_.major_version < 3) { | 
 |     return false; | 
 |   } | 
 |  | 
 |   // ES3 parameters. | 
 |   switch (pname) { | 
 |     case GL_COPY_READ_BUFFER_BINDING: | 
 |       *params = bound_copy_read_buffer_; | 
 |       return true; | 
 |     case GL_COPY_WRITE_BUFFER_BINDING: | 
 |       *params = bound_copy_write_buffer_; | 
 |       return true; | 
 |     case GL_MAJOR_VERSION: | 
 |       *params = capabilities_.major_version; | 
 |       return true; | 
 |     case GL_MAX_3D_TEXTURE_SIZE: | 
 |       *params = capabilities_.max_3d_texture_size; | 
 |       return true; | 
 |     case GL_MAX_ARRAY_TEXTURE_LAYERS: | 
 |       *params = capabilities_.max_array_texture_layers; | 
 |       return true; | 
 |     case GL_MAX_COLOR_ATTACHMENTS: | 
 |       *params = capabilities_.max_color_attachments; | 
 |       return true; | 
 |     case GL_MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS: | 
 |       *params = static_cast<GLint>( | 
 |           capabilities_.max_combined_fragment_uniform_components); | 
 |       return true; | 
 |     case GL_MAX_COMBINED_UNIFORM_BLOCKS: | 
 |       *params = capabilities_.max_combined_uniform_blocks; | 
 |       return true; | 
 |     case GL_MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS: | 
 |       *params = static_cast<GLint>( | 
 |           capabilities_.max_combined_vertex_uniform_components); | 
 |       return true; | 
 |     case GL_MAX_DRAW_BUFFERS: | 
 |       *params = capabilities_.max_draw_buffers; | 
 |       return true; | 
 |     case GL_MAX_ELEMENT_INDEX: | 
 |       *params = static_cast<GLint>(capabilities_.max_element_index); | 
 |       return true; | 
 |     case GL_MAX_ELEMENTS_INDICES: | 
 |       *params = capabilities_.max_elements_indices; | 
 |       return true; | 
 |     case GL_MAX_ELEMENTS_VERTICES: | 
 |       *params = capabilities_.max_elements_vertices; | 
 |       return true; | 
 |     case GL_MAX_FRAGMENT_INPUT_COMPONENTS: | 
 |       *params = capabilities_.max_fragment_input_components; | 
 |       return true; | 
 |     case GL_MAX_FRAGMENT_UNIFORM_BLOCKS: | 
 |       *params = capabilities_.max_fragment_uniform_blocks; | 
 |       return true; | 
 |     case GL_MAX_FRAGMENT_UNIFORM_COMPONENTS: | 
 |       *params = capabilities_.max_fragment_uniform_components; | 
 |       return true; | 
 |     case GL_MAX_PROGRAM_TEXEL_OFFSET: | 
 |       *params = capabilities_.max_program_texel_offset; | 
 |       return true; | 
 |     case GL_MAX_SAMPLES: | 
 |       *params = capabilities_.max_samples; | 
 |       return true; | 
 |     case GL_MAX_SERVER_WAIT_TIMEOUT: | 
 |       *params = static_cast<GLint>(capabilities_.max_server_wait_timeout); | 
 |       return true; | 
 |     case GL_MAX_TEXTURE_LOD_BIAS: | 
 |       *params = static_cast<GLint>(capabilities_.max_texture_lod_bias); | 
 |       return true; | 
 |     case GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS: | 
 |       *params = capabilities_.max_transform_feedback_interleaved_components; | 
 |       return true; | 
 |     case GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS: | 
 |       *params = capabilities_.max_transform_feedback_separate_attribs; | 
 |       return true; | 
 |     case GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS: | 
 |       *params = capabilities_.max_transform_feedback_separate_components; | 
 |       return true; | 
 |     case GL_MAX_UNIFORM_BLOCK_SIZE: | 
 |       *params = static_cast<GLint>(capabilities_.max_uniform_block_size); | 
 |       return true; | 
 |     case GL_MAX_UNIFORM_BUFFER_BINDINGS: | 
 |       *params = capabilities_.max_uniform_buffer_bindings; | 
 |       return true; | 
 |     case GL_MAX_VARYING_COMPONENTS: | 
 |       *params = capabilities_.max_varying_components; | 
 |       return true; | 
 |     case GL_MAX_VERTEX_OUTPUT_COMPONENTS: | 
 |       *params = capabilities_.max_vertex_output_components; | 
 |       return true; | 
 |     case GL_MAX_VERTEX_UNIFORM_BLOCKS: | 
 |       *params = capabilities_.max_vertex_uniform_blocks; | 
 |       return true; | 
 |     case GL_MAX_VERTEX_UNIFORM_COMPONENTS: | 
 |       *params = capabilities_.max_vertex_uniform_components; | 
 |       return true; | 
 |     case GL_MIN_PROGRAM_TEXEL_OFFSET: | 
 |       *params = capabilities_.min_program_texel_offset; | 
 |       return true; | 
 |     case GL_MINOR_VERSION: | 
 |       *params = capabilities_.minor_version; | 
 |       return true; | 
 |     case GL_NUM_EXTENSIONS: | 
 |       UpdateCachedExtensionsIfNeeded(); | 
 |       *params = cached_extensions_.size(); | 
 |       return true; | 
 |     case GL_NUM_PROGRAM_BINARY_FORMATS: | 
 |       *params = capabilities_.num_program_binary_formats; | 
 |       return true; | 
 |     case GL_PACK_SKIP_PIXELS: | 
 |       *params = pack_skip_pixels_; | 
 |       return true; | 
 |     case GL_PACK_SKIP_ROWS: | 
 |       *params = pack_skip_rows_; | 
 |       return true; | 
 |     case GL_PIXEL_PACK_BUFFER_BINDING: | 
 |       *params = bound_pixel_pack_buffer_; | 
 |       return true; | 
 |     case GL_PIXEL_UNPACK_BUFFER_BINDING: | 
 |       *params = bound_pixel_unpack_buffer_; | 
 |       return true; | 
 |     case GL_TRANSFORM_FEEDBACK_BUFFER_BINDING: | 
 |       *params = bound_transform_feedback_buffer_; | 
 |       return true; | 
 |     case GL_UNIFORM_BUFFER_BINDING: | 
 |       *params = bound_uniform_buffer_; | 
 |       return true; | 
 |     case GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT: | 
 |       *params = capabilities_.uniform_buffer_offset_alignment; | 
 |       return true; | 
 |     case GL_UNPACK_SKIP_IMAGES: | 
 |       *params = unpack_skip_images_; | 
 |       return true; | 
 |     case GL_UNPACK_SKIP_PIXELS: | 
 |       *params = unpack_skip_pixels_; | 
 |       return true; | 
 |     case GL_UNPACK_SKIP_ROWS: | 
 |       *params = unpack_skip_rows_; | 
 |       return true; | 
 |  | 
 |     // Non-cached ES3 parameters. | 
 |     case GL_DRAW_BUFFER0: | 
 |     case GL_DRAW_BUFFER1: | 
 |     case GL_DRAW_BUFFER2: | 
 |     case GL_DRAW_BUFFER3: | 
 |     case GL_DRAW_BUFFER4: | 
 |     case GL_DRAW_BUFFER5: | 
 |     case GL_DRAW_BUFFER6: | 
 |     case GL_DRAW_BUFFER7: | 
 |     case GL_DRAW_BUFFER8: | 
 |     case GL_DRAW_BUFFER9: | 
 |     case GL_DRAW_BUFFER10: | 
 |     case GL_DRAW_BUFFER11: | 
 |     case GL_DRAW_BUFFER12: | 
 |     case GL_DRAW_BUFFER13: | 
 |     case GL_DRAW_BUFFER14: | 
 |     case GL_DRAW_BUFFER15: | 
 |     case GL_DRAW_FRAMEBUFFER_BINDING: | 
 |     case GL_FRAGMENT_SHADER_DERIVATIVE_HINT: | 
 |     case GL_PACK_ROW_LENGTH: | 
 |     case GL_PRIMITIVE_RESTART_FIXED_INDEX: | 
 |     case GL_PROGRAM_BINARY_FORMATS: | 
 |     case GL_RASTERIZER_DISCARD: | 
 |     case GL_READ_BUFFER: | 
 |     case GL_READ_FRAMEBUFFER_BINDING: | 
 |     case GL_SAMPLER_BINDING: | 
 |     case GL_TEXTURE_BINDING_2D_ARRAY: | 
 |     case GL_TEXTURE_BINDING_3D: | 
 |     case GL_TRANSFORM_FEEDBACK_BINDING: | 
 |     case GL_TRANSFORM_FEEDBACK_ACTIVE: | 
 |     case GL_TRANSFORM_FEEDBACK_PAUSED: | 
 |     case GL_TRANSFORM_FEEDBACK_BUFFER_SIZE: | 
 |     case GL_TRANSFORM_FEEDBACK_BUFFER_START: | 
 |     case GL_UNIFORM_BUFFER_SIZE: | 
 |     case GL_UNIFORM_BUFFER_START: | 
 |     case GL_UNPACK_IMAGE_HEIGHT: | 
 |     case GL_UNPACK_ROW_LENGTH: | 
 |     case GL_VERTEX_ARRAY_BINDING: | 
 |       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. | 
 |   switch (pname) { | 
 |     case GL_MAX_TEXTURE_LOD_BIAS: | 
 |       *params = capabilities_.max_texture_lod_bias; | 
 |       return true; | 
 |     default: | 
 |       break; | 
 |   } | 
 |   GLint value; | 
 |   if (!GetHelper(pname, &value)) { | 
 |     return false; | 
 |   } | 
 |   *params = static_cast<GLfloat>(value); | 
 |   return true; | 
 | } | 
 |  | 
 | bool GLES2Implementation::GetInteger64vHelper(GLenum pname, GLint64* params) { | 
 |   switch (pname) { | 
 |     case GL_MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS: | 
 |       *params = capabilities_.max_combined_fragment_uniform_components; | 
 |       return true; | 
 |     case GL_MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS: | 
 |       *params = capabilities_.max_combined_vertex_uniform_components; | 
 |       return true; | 
 |     case GL_MAX_ELEMENT_INDEX: | 
 |       *params = capabilities_.max_element_index; | 
 |       return true; | 
 |     case GL_MAX_SERVER_WAIT_TIMEOUT: | 
 |       *params = capabilities_.max_server_wait_timeout; | 
 |       return true; | 
 |     case GL_MAX_UNIFORM_BLOCK_SIZE: | 
 |       *params = capabilities_.max_uniform_block_size; | 
 |       return true; | 
 |     case GL_TIMESTAMP_EXT: | 
 |       // We convert all GPU timestamps to CPU time. | 
 |       *params = (base::TimeTicks::Now() - base::TimeTicks()).InMicroseconds() | 
 |                 * base::Time::kNanosecondsPerMicrosecond; | 
 |       return true; | 
 |     default: | 
 |       break; | 
 |   } | 
 |   GLint value; | 
 |   if (!GetHelper(pname, &value)) { | 
 |     return false; | 
 |   } | 
 |   *params = static_cast<GLint64>(value); | 
 |   return true; | 
 | } | 
 |  | 
 | bool GLES2Implementation::GetIntegervHelper(GLenum pname, GLint* params) { | 
 |   return GetHelper(pname, params); | 
 | } | 
 |  | 
 | bool GLES2Implementation::GetIntegeri_vHelper( | 
 |     GLenum pname, GLuint index, GLint* data) { | 
 |   // TODO(zmo): Implement client side caching. | 
 |   return false; | 
 | } | 
 |  | 
 | bool GLES2Implementation::GetInteger64i_vHelper( | 
 |     GLenum pname, GLuint index, GLint64* data) { | 
 |   // TODO(zmo): Implement client side caching. | 
 |   return false; | 
 | } | 
 |  | 
 | bool GLES2Implementation::GetInternalformativHelper( | 
 |     GLenum target, GLenum format, GLenum pname, GLsizei bufSize, | 
 |     GLint* params) { | 
 |   // TODO(zmo): Implement the client side caching. | 
 |   return false; | 
 | } | 
 |  | 
 | bool GLES2Implementation::GetSyncivHelper( | 
 |     GLsync sync, GLenum pname, GLsizei bufsize, GLsizei* length, | 
 |     GLint* values) { | 
 |   GLint value = 0; | 
 |   switch (pname) { | 
 |     case GL_OBJECT_TYPE: | 
 |       value = GL_SYNC_FENCE; | 
 |       break; | 
 |     case GL_SYNC_CONDITION: | 
 |       value = GL_SYNC_GPU_COMMANDS_COMPLETE; | 
 |       break; | 
 |     case GL_SYNC_FLAGS: | 
 |       value = 0; | 
 |       break; | 
 |     default: | 
 |       return false; | 
 |   } | 
 |   if (bufsize > 0) { | 
 |     DCHECK(values); | 
 |     *values = value; | 
 |   } | 
 |   if (length) { | 
 |     *length = 1; | 
 |   } | 
 |   return true; | 
 | } | 
 |  | 
 | bool GLES2Implementation::GetQueryObjectValueHelper( | 
 |     const char* function_name, GLuint id, GLenum pname, GLuint64* params) { | 
 |   GPU_CLIENT_SINGLE_THREAD_CHECK(); | 
 |   GPU_CLIENT_LOG("[" << GetLogPrefix() << "] GetQueryObjectValueHelper(" | 
 |                  << id << ", " | 
 |                  << GLES2Util::GetStringQueryObjectParameter(pname) << ", " | 
 |                  << static_cast<const void*>(params) << ")"); | 
 |  | 
 |   QueryTracker::Query* query = query_tracker_->GetQuery(id); | 
 |   if (!query) { | 
 |     SetGLError(GL_INVALID_OPERATION, | 
 |                function_name, "unknown query id"); | 
 |     return false; | 
 |   } | 
 |  | 
 |   if (query->Active()) { | 
 |     SetGLError( | 
 |         GL_INVALID_OPERATION, | 
 |         function_name, | 
 |         "query active. Did you call glEndQueryEXT?"); | 
 |     return false; | 
 |   } | 
 |  | 
 |   if (query->NeverUsed()) { | 
 |     SetGLError( | 
 |         GL_INVALID_OPERATION, | 
 |         function_name, "Never used. Did you call glBeginQueryEXT?"); | 
 |     return false; | 
 |   } | 
 |  | 
 |   bool valid_value = false; | 
 |   switch (pname) { | 
 |     case GL_QUERY_RESULT_EXT: | 
 |       if (!query->CheckResultsAvailable(helper_)) { | 
 |         helper_->WaitForToken(query->token()); | 
 |         if (!query->CheckResultsAvailable(helper_)) { | 
 |           FinishHelper(); | 
 |           CHECK(query->CheckResultsAvailable(helper_)); | 
 |         } | 
 |       } | 
 |       *params = query->GetResult(); | 
 |       valid_value = true; | 
 |       break; | 
 |     case GL_QUERY_RESULT_AVAILABLE_EXT: | 
 |       *params = query->CheckResultsAvailable(helper_); | 
 |       valid_value = true; | 
 |       break; | 
 |     default: | 
 |       SetGLErrorInvalidEnum(function_name, pname, "pname"); | 
 |       break; | 
 |   } | 
 |   GPU_CLIENT_LOG("  " << *params); | 
 |   CheckGLError(); | 
 |   return valid_value; | 
 | } | 
 |  | 
 | 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_); | 
 |   } | 
 | } | 
 |  | 
 | 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) << ")"); | 
 |   DrawElementsImpl(mode, count, type, indices, "glDrawElements"); | 
 | } | 
 |  | 
 | void GLES2Implementation::DrawRangeElements( | 
 |     GLenum mode, GLuint start, GLuint end, | 
 |     GLsizei count, GLenum type, const void* indices) { | 
 |   GPU_CLIENT_SINGLE_THREAD_CHECK(); | 
 |   GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glDrawRangeElements(" | 
 |       << GLES2Util::GetStringDrawMode(mode) << ", " | 
 |       << start << ", " << end << ", " << count << ", " | 
 |       << GLES2Util::GetStringIndexType(type) << ", " | 
 |       << static_cast<const void*>(indices) << ")"); | 
 |   if (end < start) { | 
 |     SetGLError(GL_INVALID_VALUE, "glDrawRangeElements", "end < start"); | 
 |     return; | 
 |   } | 
 |   DrawElementsImpl(mode, count, type, indices, "glDrawRangeElements"); | 
 | } | 
 |  | 
 | void GLES2Implementation::DrawElementsImpl( | 
 |     GLenum mode, GLsizei count, GLenum type, const void* indices, | 
 |     const char* func_name) { | 
 |   if (count < 0) { | 
 |     SetGLError(GL_INVALID_VALUE, func_name, "count < 0"); | 
 |     return; | 
 |   } | 
 |   bool simulated = false; | 
 |   GLuint offset = ToGLuint(indices); | 
 |   if (count > 0) { | 
 |     if (vertex_array_object_manager_->bound_element_array_buffer() != 0 && | 
 |         !ValidateOffset(func_name, reinterpret_cast<GLintptr>(indices))) { | 
 |       return; | 
 |     } | 
 |     if (!vertex_array_object_manager_->SetupSimulatedIndexAndClientSideBuffers( | 
 |         func_name, 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()"); | 
 |   flush_id_ = GenerateNextFlushId(); | 
 |   // Insert the cmd to call glFlush | 
 |   helper_->Flush(); | 
 |   FlushHelper(); | 
 | } | 
 |  | 
 | void GLES2Implementation::ShallowFlushCHROMIUM() { | 
 |   GPU_CLIENT_SINGLE_THREAD_CHECK(); | 
 |   GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glShallowFlushCHROMIUM()"); | 
 |   flush_id_ = GenerateNextFlushId(); | 
 |   FlushHelper(); | 
 | } | 
 |  | 
 | void GLES2Implementation::FlushHelper() { | 
 |   // Flush our command buffer | 
 |   // (tell the service to execute up to the flush cmd.) | 
 |   helper_->CommandBufferHelper::Flush(); | 
 |  | 
 |   if (aggressively_free_resources_) | 
 |     FreeEverything(); | 
 | } | 
 |  | 
 | void GLES2Implementation::OrderingBarrierCHROMIUM() { | 
 |   GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glOrderingBarrierCHROMIUM"); | 
 |   // Flush command buffer at the GPU channel level.  May be implemented as | 
 |   // Flush(). | 
 |   helper_->CommandBufferHelper::OrderingBarrier(); | 
 | } | 
 |  | 
 | void GLES2Implementation::Finish() { | 
 |   GPU_CLIENT_SINGLE_THREAD_CHECK(); | 
 |   flush_id_ = GenerateNextFlushId(); | 
 |   FinishHelper(); | 
 | } | 
 |  | 
 | void GLES2Implementation::ShallowFinishCHROMIUM() { | 
 |   GPU_CLIENT_SINGLE_THREAD_CHECK(); | 
 |   TRACE_EVENT0("gpu", "GLES2::ShallowFinishCHROMIUM"); | 
 |   flush_id_ = GenerateNextFlushId(); | 
 |   // Flush our command buffer (tell the service to execute up to the flush cmd | 
 |   // and don't return until it completes). | 
 |   helper_->CommandBufferHelper::Finish(); | 
 |  | 
 |   if (aggressively_free_resources_) | 
 |     FreeEverything(); | 
 | } | 
 |  | 
 | 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(); | 
 |  | 
 |   if (aggressively_free_resources_) | 
 |     FreeEverything(); | 
 | } | 
 |  | 
 | GLuint GLES2Implementation::GetLastFlushIdCHROMIUM() { | 
 |   GPU_CLIENT_SINGLE_THREAD_CHECK(); | 
 |   GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glGetLastFlushIdCHROMIUM()"); | 
 |   return flush_id_; | 
 | } | 
 |  | 
 | 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::SwapInterval(int interval) { | 
 |   GPU_CLIENT_SINGLE_THREAD_CHECK(); | 
 |   GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glSwapInterval(" | 
 |       << interval << ")"); | 
 |   helper_->SwapInterval(interval); | 
 | } | 
 |  | 
 | 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::BindFragDataLocationEXT(GLuint program, | 
 |                                                   GLuint colorName, | 
 |                                                   const char* name) { | 
 |   GPU_CLIENT_SINGLE_THREAD_CHECK(); | 
 |   GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glBindFragDataLocationEXT(" | 
 |                      << program << ", " << colorName << ", " << name << ")"); | 
 |   SetBucketAsString(kResultBucketId, name); | 
 |   helper_->BindFragDataLocationEXTBucket(program, colorName, kResultBucketId); | 
 |   helper_->SetBucketSize(kResultBucketId, 0); | 
 |   CheckGLError(); | 
 | } | 
 |  | 
 | void GLES2Implementation::BindFragDataLocationIndexedEXT(GLuint program, | 
 |                                                          GLuint colorName, | 
 |                                                          GLuint index, | 
 |                                                          const char* name) { | 
 |   GPU_CLIENT_SINGLE_THREAD_CHECK(); | 
 |   GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glBindFragDataLocationEXT(" | 
 |                      << program << ", " << colorName << ", " << index << ", " | 
 |                      << name << ")"); | 
 |   SetBucketAsString(kResultBucketId, name); | 
 |   helper_->BindFragDataLocationIndexedEXTBucket(program, colorName, 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_t 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_t 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) { | 
 |   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) { | 
 |   DCHECK_EQ(1, n); | 
 |   share_group_->program_info_manager()->DeleteInfo(shaders[0]); | 
 |   helper_->DeleteShader(shaders[0]); | 
 | } | 
 |  | 
 | void GLES2Implementation::DeleteSyncHelper(GLsync sync) { | 
 |   GLuint sync_uint = ToGLuint(sync); | 
 |   if (!GetIdHandler(id_namespaces::kSyncs)->FreeIds( | 
 |       this, 1, &sync_uint, &GLES2Implementation::DeleteSyncStub)) { | 
 |     SetGLError( | 
 |         GL_INVALID_VALUE, | 
 |         "glDeleteSync", "id not created by this context."); | 
 |   } | 
 | } | 
 |  | 
 | void GLES2Implementation::DeleteSyncStub(GLsizei n, const GLuint* syncs) { | 
 |   DCHECK_EQ(1, n); | 
 |   helper_->DeleteSync(syncs[0]); | 
 | } | 
 |  | 
 | GLint GLES2Implementation::GetAttribLocationHelper( | 
 |     GLuint program, const char* name) { | 
 |   typedef cmds::GetAttribLocation::Result Result; | 
 |   Result* result = GetResultAs<Result*>(); | 
 |   if (!result) { | 
 |     return -1; | 
 |   } | 
 |   *result = -1; | 
 |   SetBucketAsCString(kResultBucketId, name); | 
 |   helper_->GetAttribLocation( | 
 |       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::GetUniformLocation::Result Result; | 
 |   Result* result = GetResultAs<Result*>(); | 
 |   if (!result) { | 
 |     return -1; | 
 |   } | 
 |   *result = -1; | 
 |   SetBucketAsCString(kResultBucketId, name); | 
 |   helper_->GetUniformLocation(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; | 
 | } | 
 |  | 
 | bool GLES2Implementation::GetUniformIndicesHelper( | 
 |     GLuint program, GLsizei count, const char* const* names, GLuint* indices) { | 
 |   typedef cmds::GetUniformIndices::Result Result; | 
 |   Result* result = GetResultAs<Result*>(); | 
 |   if (!result) { | 
 |     return false; | 
 |   } | 
 |   result->SetNumResults(0); | 
 |   if (!PackStringsToBucket(count, names, NULL, "glGetUniformIndices")) { | 
 |     return false; | 
 |   } | 
 |   helper_->GetUniformIndices(program, kResultBucketId, | 
 |                              GetResultShmId(), GetResultShmOffset()); | 
 |   WaitForCmd(); | 
 |   if (result->GetNumResults() != count) { | 
 |     return false; | 
 |   } | 
 |   result->CopyResult(indices); | 
 |   return true; | 
 | } | 
 |  | 
 | void GLES2Implementation::GetUniformIndices( | 
 |     GLuint program, GLsizei count, const char* const* names, GLuint* indices) { | 
 |   GPU_CLIENT_SINGLE_THREAD_CHECK(); | 
 |   GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glGetUniformIndices(" << program | 
 |       << ", " << count << ", " << names << ", " << indices << ")"); | 
 |   TRACE_EVENT0("gpu", "GLES2::GetUniformIndices"); | 
 |   if (count < 0) { | 
 |     SetGLError(GL_INVALID_VALUE, "glGetUniformIndices", "count < 0"); | 
 |     return; | 
 |   } | 
 |   if (count == 0) { | 
 |     return; | 
 |   } | 
 |   bool success = share_group_->program_info_manager()->GetUniformIndices( | 
 |       this, program, count, names, indices); | 
 |   if (success) { | 
 |     GPU_CLIENT_LOG_CODE_BLOCK({ | 
 |       for (GLsizei ii = 0; ii < count; ++ii) { | 
 |         GPU_CLIENT_LOG("  " << ii << ": " << indices[ii]); | 
 |       } | 
 |     }); | 
 |   } | 
 |   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; | 
 | } | 
 |  | 
 | GLint GLES2Implementation::GetFragDataIndexEXTHelper(GLuint program, | 
 |                                                      const char* name) { | 
 |   typedef cmds::GetFragDataIndexEXT::Result Result; | 
 |   Result* result = GetResultAs<Result*>(); | 
 |   if (!result) { | 
 |     return -1; | 
 |   } | 
 |   *result = -1; | 
 |   SetBucketAsCString(kResultBucketId, name); | 
 |   helper_->GetFragDataIndexEXT(program, kResultBucketId, GetResultShmId(), | 
 |                                GetResultShmOffset()); | 
 |   WaitForCmd(); | 
 |   helper_->SetBucketSize(kResultBucketId, 0); | 
 |   return *result; | 
 | } | 
 |  | 
 | GLint GLES2Implementation::GetFragDataIndexEXT(GLuint program, | 
 |                                                const char* name) { | 
 |   GPU_CLIENT_SINGLE_THREAD_CHECK(); | 
 |   GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glGetFragDataIndexEXT(" << program | 
 |                      << ", " << name << ")"); | 
 |   TRACE_EVENT0("gpu", "GLES2::GetFragDataIndexEXT"); | 
 |   GLint loc = share_group_->program_info_manager()->GetFragDataIndex( | 
 |       this, program, name); | 
 |   GPU_CLIENT_LOG("returned " << loc); | 
 |   CheckGLError(); | 
 |   return loc; | 
 | } | 
 |  | 
 | GLint GLES2Implementation::GetFragDataLocationHelper( | 
 |     GLuint program, const char* name) { | 
 |   typedef cmds::GetFragDataLocation::Result Result; | 
 |   Result* result = GetResultAs<Result*>(); | 
 |   if (!result) { | 
 |     return -1; | 
 |   } | 
 |   *result = -1; | 
 |   SetBucketAsCString(kResultBucketId, name); | 
 |   helper_->GetFragDataLocation( | 
 |       program, kResultBucketId, GetResultShmId(), GetResultShmOffset()); | 
 |   WaitForCmd(); | 
 |   helper_->SetBucketSize(kResultBucketId, 0); | 
 |   return *result; | 
 | } | 
 |  | 
 | GLint GLES2Implementation::GetFragDataLocation( | 
 |     GLuint program, const char* name) { | 
 |   GPU_CLIENT_SINGLE_THREAD_CHECK(); | 
 |   GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glGetFragDataLocation(" | 
 |       << program << ", " << name << ")"); | 
 |   TRACE_EVENT0("gpu", "GLES2::GetFragDataLocation"); | 
 |   GLint loc = share_group_->program_info_manager()->GetFragDataLocation( | 
 |       this, program, name); | 
 |   GPU_CLIENT_LOG("returned " << loc); | 
 |   CheckGLError(); | 
 |   return loc; | 
 | } | 
 |  | 
 | GLuint GLES2Implementation::GetUniformBlockIndexHelper( | 
 |     GLuint program, const char* name) { | 
 |   typedef cmds::GetUniformBlockIndex::Result Result; | 
 |   Result* result = GetResultAs<Result*>(); | 
 |   if (!result) { | 
 |     return GL_INVALID_INDEX; | 
 |   } | 
 |   *result = GL_INVALID_INDEX; | 
 |   SetBucketAsCString(kResultBucketId, name); | 
 |   helper_->GetUniformBlockIndex( | 
 |       program, kResultBucketId, GetResultShmId(), GetResultShmOffset()); | 
 |   WaitForCmd(); | 
 |   helper_->SetBucketSize(kResultBucketId, 0); | 
 |   return *result; | 
 | } | 
 |  | 
 | GLuint GLES2Implementation::GetUniformBlockIndex( | 
 |     GLuint program, const char* name) { | 
 |   GPU_CLIENT_SINGLE_THREAD_CHECK(); | 
 |   GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glGetUniformBlockIndex(" | 
 |       << program << ", " << name << ")"); | 
 |   TRACE_EVENT0("gpu", "GLES2::GetUniformBlockIndex"); | 
 |   GLuint index = share_group_->program_info_manager()->GetUniformBlockIndex( | 
 |       this, program, name); | 
 |   GPU_CLIENT_LOG("returned " << index); | 
 |   CheckGLError(); | 
 |   return index; | 
 | } | 
 |  | 
 | 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 << ")"); | 
 |   // We have to validate before caching these parameters because we use them | 
 |   // to compute image sizes on the client side. | 
 |   switch (pname) { | 
 |     case GL_PACK_ALIGNMENT: | 
 |     case GL_UNPACK_ALIGNMENT: | 
 |       if (param != 1 && param != 2 && param != 4 && param != 8) { | 
 |         SetGLError(GL_INVALID_VALUE, "glPixelStorei", "invalid param"); | 
 |         return; | 
 |       } | 
 |       break; | 
 |     case GL_PACK_ROW_LENGTH: | 
 |     case GL_PACK_SKIP_PIXELS: | 
 |     case GL_PACK_SKIP_ROWS: | 
 |     case GL_UNPACK_IMAGE_HEIGHT: | 
 |     case GL_UNPACK_SKIP_IMAGES: | 
 |       if (capabilities_.major_version < 3) { | 
 |         SetGLError(GL_INVALID_ENUM, "glPixelStorei", "invalid pname"); | 
 |         return; | 
 |       } | 
 |       if (param < 0) { | 
 |         SetGLError(GL_INVALID_VALUE, "glPixelStorei", "invalid param"); | 
 |         return; | 
 |       } | 
 |       break; | 
 |     case GL_UNPACK_ROW_LENGTH: | 
 |     case GL_UNPACK_SKIP_ROWS: | 
 |     case GL_UNPACK_SKIP_PIXELS: | 
 |       // These parameters are always enabled in ES2 by EXT_unpack_subimage. | 
 |       if (param < 0) { | 
 |         SetGLError(GL_INVALID_VALUE, "glPixelStorei", "invalid param"); | 
 |         return; | 
 |       } | 
 |       break; | 
 |     default: | 
 |       SetGLError(GL_INVALID_ENUM, "glPixelStorei", "invalid pname"); | 
 |       return; | 
 |   } | 
 |   // Do not send SKIP parameters to the service side. | 
 |   // Handle them on the client side. | 
 |   switch (pname) { | 
 |     case GL_PACK_ALIGNMENT: | 
 |       pack_alignment_ = param; | 
 |       break; | 
 |     case GL_PACK_ROW_LENGTH: | 
 |       pack_row_length_ = param; | 
 |       break; | 
 |     case GL_PACK_SKIP_PIXELS: | 
 |       pack_skip_pixels_ = param; | 
 |       return; | 
 |     case GL_PACK_SKIP_ROWS: | 
 |       pack_skip_rows_ = param; | 
 |       return; | 
 |     case GL_UNPACK_ALIGNMENT: | 
 |       unpack_alignment_ = param; | 
 |       break; | 
 |     case GL_UNPACK_ROW_LENGTH: | 
 |       unpack_row_length_ = param; | 
 |       if (capabilities_.major_version < 3) { | 
 |         // In ES2 with EXT_unpack_subimage, it's handled on the client side | 
 |         // and there is no need to send it to the service side. | 
 |         return; | 
 |       } | 
 |       break; | 
 |     case GL_UNPACK_IMAGE_HEIGHT: | 
 |       unpack_image_height_ = param; | 
 |       break; | 
 |     case GL_UNPACK_SKIP_ROWS: | 
 |       unpack_skip_rows_ = param; | 
 |       return; | 
 |     case GL_UNPACK_SKIP_PIXELS: | 
 |       unpack_skip_pixels_ = param; | 
 |       return; | 
 |     case GL_UNPACK_SKIP_IMAGES: | 
 |       unpack_skip_images_ = param; | 
 |       return; | 
 |     default: | 
 |       NOTREACHED(); | 
 |       break; | 
 |   } | 
 |   helper_->PixelStorei(pname, param); | 
 |   CheckGLError(); | 
 | } | 
 |  | 
 | void GLES2Implementation::VertexAttribIPointer( | 
 |     GLuint index, GLint size, GLenum type, GLsizei stride, const void* ptr) { | 
 |   GPU_CLIENT_SINGLE_THREAD_CHECK(); | 
 |   GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glVertexAttribIPointer(" | 
 |       << index << ", " | 
 |       << size << ", " | 
 |       << GLES2Util::GetStringVertexAttribIType(type) << ", " | 
 |       << stride << ", " | 
 |       << ptr << ")"); | 
 |   // Record the info on the client side. | 
 |   if (!vertex_array_object_manager_->SetAttribPointer(bound_array_buffer_, | 
 |                                                       index, | 
 |                                                       size, | 
 |                                                       type, | 
 |                                                       GL_FALSE, | 
 |                                                       stride, | 
 |                                                       ptr, | 
 |                                                       GL_TRUE)) { | 
 |     SetGLError(GL_INVALID_OPERATION, "glVertexAttribIPointer", | 
 |                "client side arrays are not allowed in vertex array objects."); | 
 |     return; | 
 |   } | 
 |   if (!support_client_side_arrays_ || bound_array_buffer_ != 0) { | 
 |     // Only report NON client side buffers to the service. | 
 |     if (!ValidateOffset("glVertexAttribIPointer", | 
 |                         reinterpret_cast<GLintptr>(ptr))) { | 
 |       return; | 
 |     } | 
 |     helper_->VertexAttribIPointer(index, size, type, stride, ToGLuint(ptr)); | 
 |   } | 
 |   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 << ", " | 
 |       << ptr << ")"); | 
 |   // Record the info on the client side. | 
 |   if (!vertex_array_object_manager_->SetAttribPointer(bound_array_buffer_, | 
 |                                                       index, | 
 |                                                       size, | 
 |                                                       type, | 
 |                                                       normalized, | 
 |                                                       stride, | 
 |                                                       ptr, | 
 |                                                       GL_FALSE)) { | 
 |     SetGLError(GL_INVALID_OPERATION, "glVertexAttribPointer", | 
 |                "client side arrays are not allowed in vertex array objects."); | 
 |     return; | 
 |   } | 
 |   if (!support_client_side_arrays_ || bound_array_buffer_ != 0) { | 
 |     // Only report NON client side buffers to the service. | 
 |     if (!ValidateOffset("glVertexAttribPointer", | 
 |                         reinterpret_cast<GLintptr>(ptr))) { | 
 |       return; | 
 |     } | 
 |     helper_->VertexAttribPointer(index, size, type, normalized, stride, | 
 |                                  ToGLuint(ptr)); | 
 |   } | 
 |   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::BufferDataHelper( | 
 |     GLenum target, GLsizeiptr size, const void* data, GLenum usage) { | 
 |   if (!ValidateSize("glBufferData", size)) | 
 |     return; | 
 |  | 
 | #if defined(MEMORY_SANITIZER) && !defined(OS_NACL) | 
 |   // Do not upload uninitialized data. Even if it's not a bug, it can cause a | 
 |   // bogus MSan report during a readback later. This is because MSan doesn't | 
 |   // understand shared memory and would assume we were reading back the same | 
 |   // unintialized data. | 
 |   if (data) __msan_check_mem_is_initialized(data, size); | 
 | #endif | 
 |  | 
 |   GLuint buffer_id; | 
 |   if (GetBoundPixelTransferBuffer(target, "glBufferData", &buffer_id)) { | 
 |     if (!buffer_id) { | 
 |       return; | 
 |     } | 
 |  | 
 |     BufferTracker::Buffer* buffer = buffer_tracker_->GetBuffer(buffer_id); | 
 |     if (buffer) | 
 |       RemoveTransferBuffer(buffer); | 
 |  | 
 |     // Create new buffer. | 
 |     buffer = buffer_tracker_->CreateBuffer(buffer_id, size); | 
 |     DCHECK(buffer); | 
 |     if (buffer->address() && data) | 
 |       memcpy(buffer->address(), data, size); | 
 |     return; | 
 |   } | 
 |  | 
 |   RemoveMappedBufferRangeByTarget(target); | 
 |  | 
 |   // If there is no data just send BufferData | 
 |   if (size == 0 || !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 (!ValidateSize("glBufferSubData", size) || | 
 |       !ValidateOffset("glBufferSubData", offset)) { | 
 |     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_t end = 0; | 
 |     int32_t 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_t*>(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) { | 
 |   DCHECK(buffer); | 
 |   DCHECK_GT(size, 0); | 
 |  | 
 |   const int8_t* source = static_cast<const int8_t*>(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(); | 
 | } | 
 |  | 
 | void GLES2Implementation::RemoveTransferBuffer(BufferTracker::Buffer* buffer) { | 
 |   int32_t token = buffer->last_usage_token(); | 
 |  | 
 |   if (token) { | 
 |     if (helper_->HasTokenPassed(token)) | 
 |       buffer_tracker_->Free(buffer); | 
 |     else | 
 |       buffer_tracker_->FreePendingToken(buffer, token); | 
 |   } else { | 
 |       buffer_tracker_->Free(buffer); | 
 |   } | 
 |  | 
 |   buffer_tracker_->RemoveBuffer(buffer->id()); | 
 | } | 
 |  | 
 | 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::GetBoundPixelTransferBufferIfValid( | 
 |     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 nullptr; | 
 |   } | 
 |   if (buffer->mapped()) { | 
 |     SetGLError(GL_INVALID_OPERATION, function_name, "buffer mapped"); | 
 |     return nullptr; | 
 |   } | 
 |   base::CheckedNumeric<uint32_t> buffer_offset = buffer->shm_offset(); | 
 |   buffer_offset += offset; | 
 |   if (!buffer_offset.IsValid()) { | 
 |     SetGLError(GL_INVALID_VALUE, function_name, "offset to large"); | 
 |     return nullptr; | 
 |   } | 
 |   base::CheckedNumeric<uint32_t> required_size = offset; | 
 |   required_size += size; | 
 |   if (!required_size.IsValid() || | 
 |       buffer->size() < required_size.ValueOrDefault(0)) { | 
 |     SetGLError(GL_INVALID_VALUE, function_name, "unpack size to large"); | 
 |     return nullptr; | 
 |   } | 
 |   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 (border != 0) { | 
 |     SetGLError(GL_INVALID_VALUE, "glCompressedTexImage2D", "border != 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 = GetBoundPixelTransferBufferIfValid( | 
 |         bound_pixel_unpack_transfer_buffer_id_, | 
 |         "glCompressedTexImage2D", offset, image_size); | 
 |     if (buffer && buffer->shm_id() != -1) { | 
 |       helper_->CompressedTexImage2D( | 
 |           target, level, internalformat, width, height, image_size, | 
 |           buffer->shm_id(), buffer->shm_offset() + offset); | 
 |       buffer->set_last_usage_token(helper_->InsertToken()); | 
 |     } | 
 |     return; | 
 |   } | 
 |   if (data) { | 
 |     SetBucketContents(kResultBucketId, data, image_size); | 
 |     helper_->CompressedTexImage2DBucket(target, level, internalformat, width, | 
 |                                         height, 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); | 
 |   } else { | 
 |     helper_->CompressedTexImage2D(target, level, internalformat, width, height, | 
 |                                   image_size, 0, 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 = GetBoundPixelTransferBufferIfValid( | 
 |         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_last_usage_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(); | 
 | } | 
 |  | 
 | void GLES2Implementation::CompressedTexImage3D( | 
 |     GLenum target, GLint level, GLenum internalformat, GLsizei width, | 
 |     GLsizei height, GLsizei depth, GLint border, GLsizei image_size, | 
 |     const void* data) { | 
 |   GPU_CLIENT_SINGLE_THREAD_CHECK(); | 
 |   GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glCompressedTexImage3D(" | 
 |       << GLES2Util::GetStringTexture3DTarget(target) << ", " << level << ", " | 
 |       << GLES2Util::GetStringCompressedTextureFormat(internalformat) << ", " | 
 |       << width << ", " << height << ", " << depth << ", " << border << ", " | 
 |       << image_size << ", " << static_cast<const void*>(data) << ")"); | 
 |   if (width < 0 || height < 0 || depth < 0 || level < 0) { | 
 |     SetGLError(GL_INVALID_VALUE, "glCompressedTexImage3D", "dimension < 0"); | 
 |     return; | 
 |   } | 
 |   if (border != 0) { | 
 |     SetGLError(GL_INVALID_VALUE, "glCompressedTexImage3D", "border != 0"); | 
 |     return; | 
 |   } | 
 |   // If there's a pixel unpack buffer bound use it when issuing | 
 |   // CompressedTexImage3D. | 
 |   if (bound_pixel_unpack_transfer_buffer_id_) { | 
 |     GLuint offset = ToGLuint(data); | 
 |     BufferTracker::Buffer* buffer = GetBoundPixelTransferBufferIfValid( | 
 |         bound_pixel_unpack_transfer_buffer_id_, | 
 |         "glCompressedTexImage3D", offset, image_size); | 
 |     if (buffer && buffer->shm_id() != -1) { | 
 |       helper_->CompressedTexImage3D( | 
 |           target, level, internalformat, width, height, depth, image_size, | 
 |           buffer->shm_id(), buffer->shm_offset() + offset); | 
 |       buffer->set_last_usage_token(helper_->InsertToken()); | 
 |     } | 
 |     return; | 
 |   } | 
 |   if (data) { | 
 |     SetBucketContents(kResultBucketId, data, image_size); | 
 |     helper_->CompressedTexImage3DBucket(target, level, internalformat, width, | 
 |                                         height, depth, 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); | 
 |   } else { | 
 |     helper_->CompressedTexImage3D(target, level, internalformat, width, height, | 
 |                                   depth, image_size, 0, 0); | 
 |   } | 
 |   CheckGLError(); | 
 | } | 
 |  | 
 | void GLES2Implementation::CompressedTexSubImage3D( | 
 |     GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, | 
 |     GLsizei width, GLsizei height, GLsizei depth, GLenum format, | 
 |     GLsizei image_size, const void* data) { | 
 |   GPU_CLIENT_SINGLE_THREAD_CHECK(); | 
 |   GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glCompressedTexSubImage3D(" | 
 |       << GLES2Util::GetStringTextureTarget(target) << ", " | 
 |       << level << ", " | 
 |       << xoffset << ", " << yoffset << ", " << zoffset << ", " | 
 |       << width << ", " << height << ", " << depth << ", " | 
 |       << GLES2Util::GetStringCompressedTextureFormat(format) << ", " | 
 |       << image_size << ", " | 
 |       << static_cast<const void*>(data) << ")"); | 
 |   if (width < 0 || height < 0 || depth < 0 || level < 0) { | 
 |     SetGLError(GL_INVALID_VALUE, "glCompressedTexSubImage3D", "dimension < 0"); | 
 |     return; | 
 |   } | 
 |   // If there's a pixel unpack buffer bound use it when issuing | 
 |   // CompressedTexSubImage3D. | 
 |   if (bound_pixel_unpack_transfer_buffer_id_) { | 
 |     GLuint offset = ToGLuint(data); | 
 |     BufferTracker::Buffer* buffer = GetBoundPixelTransferBufferIfValid( | 
 |         bound_pixel_unpack_transfer_buffer_id_, | 
 |         "glCompressedTexSubImage3D", offset, image_size); | 
 |     if (buffer && buffer->shm_id() != -1) { | 
 |       helper_->CompressedTexSubImage3D( | 
 |           target, level, xoffset, yoffset, zoffset, | 
 |           width, height, depth, format, image_size, | 
 |           buffer->shm_id(), buffer->shm_offset() + offset); | 
 |       buffer->set_last_usage_token(helper_->InsertToken()); | 
 |       CheckGLError(); | 
 |     } | 
 |     return; | 
 |   } | 
 |   SetBucketContents(kResultBucketId, data, image_size); | 
 |   helper_->CompressedTexSubImage3DBucket( | 
 |       target, level, xoffset, yoffset, zoffset, width, height, depth, 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(); | 
 | } | 
 |  | 
 | PixelStoreParams GLES2Implementation::GetUnpackParameters(Dimension dimension) { | 
 |   PixelStoreParams params; | 
 |   params.alignment = unpack_alignment_; | 
 |   params.row_length = unpack_row_length_; | 
 |   params.skip_pixels = unpack_skip_pixels_; | 
 |   params.skip_rows = unpack_skip_rows_; | 
 |   if (dimension == k3D) { | 
 |     params.image_height = unpack_image_height_; | 
 |     params.skip_images = unpack_skip_images_; | 
 |   } | 
 |   return params; | 
 | } | 
 |  | 
 | void GLES2Implementation::TexImage2D( | 
 |     GLenum target, GLint level, GLint internalformat, GLsizei width, | 
 |     GLsizei height, GLint border, GLenum format, GLenum type, | 
 |     const void* pixels) { | 
 |   const char* func_name = "glTexImage2D"; | 
 |   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, func_name, "dimension < 0"); | 
 |     return; | 
 |   } | 
 |   if (border != 0) { | 
 |     SetGLError(GL_INVALID_VALUE, func_name, "border != 0"); | 
 |     return; | 
 |   } | 
 |   if ((bound_pixel_unpack_buffer_ || pixels) && | 
 |       (unpack_skip_pixels_ + width > | 
 |        (unpack_row_length_ ? unpack_row_length_ : width))) { | 
 |     // This is WebGL 2 specific constraints, but we do it for all ES3 contexts. | 
 |     SetGLError(GL_INVALID_OPERATION, func_name, | 
 |                "invalid unpack params combination"); | 
 |     return; | 
 |   } | 
 |  | 
 |   uint32_t size; | 
 |   uint32_t unpadded_row_size; | 
 |   uint32_t padded_row_size; | 
 |   uint32_t skip_size; | 
 |   PixelStoreParams params = GetUnpackParameters(k2D); | 
 |   if (!GLES2Util::ComputeImageDataSizesES3(width, height, 1, | 
 |                                            format, type, | 
 |                                            params, | 
 |                                            &size, | 
 |                                            &unpadded_row_size, | 
 |                                            &padded_row_size, | 
 |                                            &skip_size, | 
 |                                            nullptr)) { | 
 |     SetGLError(GL_INVALID_VALUE, func_name, "image size too large"); | 
 |     return; | 
 |   } | 
 |  | 
 |   if (bound_pixel_unpack_buffer_) { | 
 |     base::CheckedNumeric<uint32_t> offset = ToGLuint(pixels); | 
 |     offset += skip_size; | 
 |     if (!offset.IsValid()) { | 
 |       SetGLError(GL_INVALID_VALUE, func_name, "skip size too large"); | 
 |       return; | 
 |     } | 
 |     helper_->TexImage2D( | 
 |         target, level, internalformat, width, height, format, type, | 
 |         0, offset.ValueOrDefault(0)); | 
 |     CheckGLError(); | 
 |     return; | 
 |   } | 
 |  | 
 |   // If there's a pixel unpack buffer bound use it when issuing TexImage2D. | 
 |   if (bound_pixel_unpack_transfer_buffer_id_) { | 
 |     if (unpack_row_length_ > 0 || unpack_image_height_ > 0 || | 
 |         unpack_skip_pixels_ > 0 || unpack_skip_rows_ > 0 || | 
 |         unpack_skip_images_ > 0) { | 
 |       SetGLError(GL_INVALID_OPERATION, func_name, | 
 |                  "No ES3 pack parameters with pixel unpack transfer buffer."); | 
 |       return; | 
 |     } | 
 |     DCHECK_EQ(0u, skip_size); | 
 |     GLuint offset = ToGLuint(pixels); | 
 |     BufferTracker::Buffer* buffer = GetBoundPixelTransferBufferIfValid( | 
 |         bound_pixel_unpack_transfer_buffer_id_, func_name, offset, size); | 
 |     if (buffer && buffer->shm_id() != -1) { | 
 |       helper_->TexImage2D( | 
 |           target, level, internalformat, width, height, format, type, | 
 |           buffer->shm_id(), buffer->shm_offset() + offset); | 
 |       buffer->set_last_usage_token(helper_->InsertToken()); | 
 |       CheckGLError(); | 
 |     } | 
 |     return; | 
 |   } | 
 |  | 
 |   // If there's no data just issue TexImage2D | 
 |   if (!pixels || width == 0 || height == 0) { | 
 |     helper_->TexImage2D( | 
 |        target, level, internalformat, width, height, format, type, 0, 0); | 
 |     CheckGLError(); | 
 |     return; | 
 |   } | 
 |  | 
 |   // Compute the advance bytes per row on the service side. | 
 |   // Note |size| is recomputed here if needed. | 
 |   uint32_t service_padded_row_size; | 
 |   if (unpack_row_length_ > 0 && unpack_row_length_ != width) { | 
 |     // All parameters have been applied to the data that are sent to the | 
 |     // service side except UNPACK_ALIGNMENT. | 
 |     PixelStoreParams service_params; | 
 |     service_params.alignment = unpack_alignment_; | 
 |     if (!GLES2Util::ComputeImageDataSizesES3(width, height, 1, | 
 |                                              format, type, | 
 |                                              service_params, | 
 |                                              &size, | 
 |                                              nullptr, | 
 |                                              &service_padded_row_size, | 
 |                                              nullptr, | 
 |                                              nullptr)) { | 
 |       SetGLError(GL_INVALID_VALUE, func_name, "image size too large"); | 
 |       return; | 
 |     } | 
 |   } else { | 
 |     service_padded_row_size = padded_row_size; | 
 |   } | 
 |  | 
 |   // advance pixels pointer past the skip rows and skip pixels | 
 |   pixels = reinterpret_cast<const int8_t*>(pixels) + skip_size; | 
 |  | 
 |   // Check if we can send it all at once. | 
 |   int32_t shm_id = 0; | 
 |   uint32_t shm_offset = 0; | 
 |   void* buffer_pointer = nullptr; | 
 |  | 
 |   ScopedTransferBufferPtr transfer_alloc(size, helper_, transfer_buffer_); | 
 |   ScopedMappedMemoryPtr mapped_alloc(0, helper_, mapped_memory_.get()); | 
 |  | 
 |   if (transfer_alloc.valid() && transfer_alloc.size() >= size) { | 
 |     shm_id = transfer_alloc.shm_id(); | 
 |     shm_offset = transfer_alloc.offset(); | 
 |     buffer_pointer = transfer_alloc.address(); | 
 |   } else if (size < max_extra_transfer_buffer_size_) { | 
 |     mapped_alloc.Reset(size); | 
 |     if (mapped_alloc.valid()) { | 
 |       transfer_alloc.Discard(); | 
 |  | 
 |       mapped_alloc.SetFlushAfterRelease(true); | 
 |       shm_id = mapped_alloc.shm_id(); | 
 |       shm_offset = mapped_alloc.offset(); | 
 |       buffer_pointer = mapped_alloc.address(); | 
 |     } | 
 |   } | 
 |  | 
 |   if (buffer_pointer) { | 
 |     CopyRectToBuffer( | 
 |         pixels, height, unpadded_row_size, padded_row_size, | 
 |         buffer_pointer, service_padded_row_size); | 
 |     helper_->TexImage2D( | 
 |         target, level, internalformat, width, height, format, type, | 
 |         shm_id, shm_offset); | 
 |     CheckGLError(); | 
 |     return; | 
 |   } | 
 |  | 
 |   // No, so send it using TexSubImage2D. | 
 |   helper_->TexImage2D( | 
 |      target, level, internalformat, width, height, format, type, | 
 |      0, 0); | 
 |   TexSubImage2DImpl( | 
 |       target, level, 0, 0, width, height, format, type, unpadded_row_size, | 
 |       pixels, padded_row_size, GL_TRUE, &transfer_alloc, | 
 |       service_padded_row_size); | 
 |   CheckGLError(); | 
 | } | 
 |  | 
 | void GLES2Implementation::TexImage3D( | 
 |     GLenum target, GLint level, GLint internalformat, GLsizei width, | 
 |     GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, | 
 |     const void* pixels) { | 
 |   const char* func_name = "glTexImage3D"; | 
 |   GPU_CLIENT_SINGLE_THREAD_CHECK(); | 
 |   GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glTexImage3D(" | 
 |       << GLES2Util::GetStringTextureTarget(target) << ", " | 
 |       << level << ", " | 
 |       << GLES2Util::GetStringTextureInternalFormat(internalformat) << ", " | 
 |       << width << ", " << height << ", " << depth << ", " << border << ", " | 
 |       << GLES2Util::GetStringTextureFormat(format) << ", " | 
 |       << GLES2Util::GetStringPixelType(type) << ", " | 
 |       << static_cast<const void*>(pixels) << ")"); | 
 |   if (level < 0 || height < 0 || width < 0 || depth < 0) { | 
 |     SetGLError(GL_INVALID_VALUE, func_name, "dimension < 0"); | 
 |     return; | 
 |   } | 
 |   if (border != 0) { | 
 |     SetGLError(GL_INVALID_VALUE, func_name, "border != 0"); | 
 |     return; | 
 |   } | 
 |   if ((bound_pixel_unpack_buffer_ || pixels) && | 
 |       ((unpack_skip_pixels_ + width > | 
 |         (unpack_row_length_ ? unpack_row_length_ : width)) || | 
 |        (unpack_skip_rows_ + height > | 
 |         (unpack_image_height_ ? unpack_image_height_ : height)))) { | 
 |     // This is WebGL 2 specific constraints, but we do it for all ES3 contexts. | 
 |     SetGLError(GL_INVALID_OPERATION, func_name, | 
 |                "invalid unpack params combination"); | 
 |     return; | 
 |   } | 
 |  | 
 |   uint32_t size; | 
 |   uint32_t unpadded_row_size; | 
 |   uint32_t padded_row_size; | 
 |   uint32_t skip_size; | 
 |   PixelStoreParams params = GetUnpackParameters(k3D); | 
 |   if (!GLES2Util::ComputeImageDataSizesES3(width, height, depth, | 
 |                                            format, type, | 
 |                                            params, | 
 |                                            &size, | 
 |                                            &unpadded_row_size, | 
 |                                            &padded_row_size, | 
 |                                            &skip_size, | 
 |                                            nullptr)) { | 
 |     SetGLError(GL_INVALID_VALUE, func_name, "image size too large"); | 
 |     return; | 
 |   } | 
 |  | 
 |   if (bound_pixel_unpack_buffer_) { | 
 |     base::CheckedNumeric<uint32_t> offset = ToGLuint(pixels); | 
 |     offset += skip_size; | 
 |     if (!offset.IsValid()) { | 
 |       SetGLError(GL_INVALID_VALUE, func_name, "skip size too large"); | 
 |       return; | 
 |     } | 
 |     helper_->TexImage3D( | 
 |         target, level, internalformat, width, height, depth, format, type, | 
 |         0, offset.ValueOrDefault(0)); | 
 |     CheckGLError(); | 
 |     return; | 
 |   } | 
 |  | 
 |   // If there's a pixel unpack buffer bound use it when issuing TexImage3D. | 
 |   if (bound_pixel_unpack_transfer_buffer_id_) { | 
 |     if (unpack_row_length_ > 0 || unpack_image_height_ > 0 || | 
 |         unpack_skip_pixels_ > 0 || unpack_skip_rows_ > 0 || | 
 |         unpack_skip_images_ > 0) { | 
 |       SetGLError(GL_INVALID_OPERATION, func_name, | 
 |                  "No ES3 pack parameters with pixel unpack transfer buffer."); | 
 |       return; | 
 |     } | 
 |     DCHECK_EQ(0u, skip_size); | 
 |     GLuint offset = ToGLuint(pixels); | 
 |     BufferTracker::Buffer* buffer = GetBoundPixelTransferBufferIfValid( | 
 |         bound_pixel_unpack_transfer_buffer_id_, func_name, offset, size); | 
 |     if (buffer && buffer->shm_id() != -1) { | 
 |       helper_->TexImage3D( | 
 |           target, level, internalformat, width, height, depth, format, type, | 
 |           buffer->shm_id(), buffer->shm_offset() + offset); | 
 |       buffer->set_last_usage_token(helper_->InsertToken()); | 
 |       CheckGLError(); | 
 |     } | 
 |     return; | 
 |   } | 
 |  | 
 |   // If there's no data just issue TexImage3D | 
 |   if (!pixels || width == 0 || height == 0 || depth == 0) { | 
 |     helper_->TexImage3D( | 
 |        target, level, internalformat, width, height, depth, format, type, | 
 |        0, 0); | 
 |     CheckGLError(); | 
 |     return; | 
 |   } | 
 |  | 
 |   // Compute the advance bytes per row on the service side. | 
 |   // Note |size| is recomputed here if needed. | 
 |   uint32_t service_padded_row_size; | 
 |   if ((unpack_row_length_ > 0 && unpack_row_length_ != width) || | 
 |       (unpack_image_height_ > 0 && unpack_image_height_ != height)) { | 
 |     // All parameters have been applied to the data that are sent to the | 
 |     // service side except UNPACK_ALIGNMENT. | 
 |     PixelStoreParams service_params; | 
 |     service_params.alignment = unpack_alignment_; | 
 |     if (!GLES2Util::ComputeImageDataSizesES3(width, height, depth, | 
 |                                              format, type, | 
 |                                              service_params, | 
 |                                              &size, | 
 |                                              nullptr, | 
 |                                              &service_padded_row_size, | 
 |                                              nullptr, | 
 |                                              nullptr)) { | 
 |       SetGLError(GL_INVALID_VALUE, func_name, "image size too large"); | 
 |       return; | 
 |     } | 
 |   } else { | 
 |     service_padded_row_size = padded_row_size; | 
 |   } | 
 |   uint32_t src_height = | 
 |       unpack_image_height_ > 0 ? unpack_image_height_ : height; | 
 |  | 
 |   // advance pixels pointer past the skip images/rows/pixels | 
 |   pixels = reinterpret_cast<const int8_t*>(pixels) + skip_size; | 
 |  | 
 |   // Check if we can send it all at once. | 
 |   int32_t shm_id = 0; | 
 |   uint32_t shm_offset = 0; | 
 |   void* buffer_pointer = nullptr; | 
 |  | 
 |   ScopedTransferBufferPtr transfer_alloc(size, helper_, transfer_buffer_); | 
 |   ScopedMappedMemoryPtr mapped_alloc(0, helper_, mapped_memory_.get()); | 
 |  | 
 |   if (transfer_alloc.valid() && transfer_alloc.size() >= size) { | 
 |     shm_id = transfer_alloc.shm_id(); | 
 |     shm_offset = transfer_alloc.offset(); | 
 |     buffer_pointer = transfer_alloc.address(); | 
 |   } else if (size < max_extra_transfer_buffer_size_) { | 
 |     mapped_alloc.Reset(size); | 
 |     if (mapped_alloc.valid()) { | 
 |       transfer_alloc.Discard(); | 
 |  | 
 |       mapped_alloc.SetFlushAfterRelease(true); | 
 |       shm_id = mapped_alloc.shm_id(); | 
 |       shm_offset = mapped_alloc.offset(); | 
 |       buffer_pointer = mapped_alloc.address(); | 
 |     } | 
 |   } | 
 |  | 
 |   if (buffer_pointer) { | 
 |     for (GLsizei z = 0; z < depth; ++z) { | 
 |       CopyRectToBuffer( | 
 |           pixels, height, unpadded_row_size, padded_row_size, | 
 |           buffer_pointer, service_padded_row_size); | 
 |       pixels = reinterpret_cast<const int8_t*>(pixels) + | 
 |                padded_row_size * src_height; | 
 |       buffer_pointer = reinterpret_cast<int8_t*>(buffer_pointer) + | 
 |                        service_padded_row_size * height; | 
 |     } | 
 |     helper_->TexImage3D( | 
 |         target, level, internalformat, width, height, depth, format, type, | 
 |         shm_id, shm_offset); | 
 |     CheckGLError(); | 
 |     return; | 
 |   } | 
 |  | 
 |   // No, so send it using TexSubImage3D. | 
 |   helper_->TexImage3D( | 
 |      target, level, internalformat, width, height, depth, format, type, | 
 |      0, 0); | 
 |   TexSubImage3DImpl( | 
 |       target, level, 0, 0, 0, width, height, depth, format, type, | 
 |       unpadded_row_size, pixels, padded_row_size, GL_TRUE, &transfer_alloc, | 
 |       service_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) { | 
 |   const char* func_name = "glTexSubImage2D"; | 
 |   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 || xoffset < 0 || yoffset < 0) { | 
 |     SetGLError(GL_INVALID_VALUE, func_name, "dimension < 0"); | 
 |     return; | 
 |   } | 
 |   if (unpack_skip_pixels_ + width > | 
 |       (unpack_row_length_ ? unpack_row_length_ : width)) { | 
 |     // This is WebGL 2 specific constraints, but we do it for all ES3 contexts. | 
 |     SetGLError(GL_INVALID_OPERATION, func_name, | 
 |                "invalid unpack params combination"); | 
 |     return; | 
 |   } | 
 |  | 
 |   uint32_t size; | 
 |   uint32_t unpadded_row_size; | 
 |   uint32_t padded_row_size; | 
 |   uint32_t skip_size; | 
 |   PixelStoreParams params = GetUnpackParameters(k2D); | 
 |   if (!GLES2Util::ComputeImageDataSizesES3(width, height, 1, | 
 |                                            format, type, | 
 |                                            params, | 
 |                                            &size, | 
 |                                            &unpadded_row_size, | 
 |                                            &padded_row_size, | 
 |                                            &skip_size, | 
 |                                            nullptr)) { | 
 |     SetGLError(GL_INVALID_VALUE, func_name, "image size to large"); | 
 |     return; | 
 |   } | 
 |  | 
 |   if (bound_pixel_unpack_buffer_) { | 
 |     base::CheckedNumeric<uint32_t> offset = ToGLuint(pixels); | 
 |     offset += skip_size; | 
 |     if (!offset.IsValid()) { | 
 |       SetGLError(GL_INVALID_VALUE, func_name, "skip size too large"); | 
 |       return; | 
 |     } | 
 |     helper_->TexSubImage2D(target, level, xoffset, yoffset, width, height, | 
 |                            format, type, 0, offset.ValueOrDefault(0), false); | 
 |     CheckGLError(); | 
 |     return; | 
 |   } | 
 |  | 
 |   // If there's a pixel unpack buffer bound use it when issuing TexSubImage2D. | 
 |   if (bound_pixel_unpack_transfer_buffer_id_) { | 
 |     if (unpack_row_length_ > 0 || unpack_image_height_ > 0 || | 
 |         unpack_skip_pixels_ > 0 || unpack_skip_rows_ > 0 || | 
 |         unpack_skip_images_ > 0) { | 
 |       SetGLError(GL_INVALID_OPERATION, func_name, | 
 |                  "No ES3 pack parameters with pixel unpack transfer buffer."); | 
 |       return; | 
 |     } | 
 |     DCHECK_EQ(0u, skip_size); | 
 |     GLuint offset = ToGLuint(pixels); | 
 |     BufferTracker::Buffer* buffer = GetBoundPixelTransferBufferIfValid( | 
 |         bound_pixel_unpack_transfer_buffer_id_, func_name, offset, 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_last_usage_token(helper_->InsertToken()); | 
 |       CheckGLError(); | 
 |     } | 
 |     return; | 
 |   } | 
 |  | 
 |   if (width == 0 || height == 0) { | 
 |     // No need to worry about pixel data. | 
 |     helper_->TexSubImage2D(target, level, xoffset, yoffset, width, height, | 
 |                            format, type, 0, 0, false); | 
 |     CheckGLError(); | 
 |     return; | 
 |   } | 
 |  | 
 |   // Compute the advance bytes per row on the service side. | 
 |   // Note |size| is recomputed here if needed. | 
 |   uint32_t service_padded_row_size; | 
 |   if (unpack_row_length_ > 0 && unpack_row_length_ != width) { | 
 |     // All parameters have been applied to the data that are sent to the | 
 |     // service side except UNPACK_ALIGNMENT. | 
 |     PixelStoreParams service_params; | 
 |     service_params.alignment = unpack_alignment_; | 
 |     if (!GLES2Util::ComputeImageDataSizesES3(width, height, 1, | 
 |                                              format, type, | 
 |                                              service_params, | 
 |                                              &size, | 
 |                                              nullptr, | 
 |                                              &service_padded_row_size, | 
 |                                              nullptr, | 
 |                                              nullptr)) { | 
 |       SetGLError(GL_INVALID_VALUE, func_name, "image size too large"); | 
 |       return; | 
 |     } | 
 |   } else { | 
 |     service_padded_row_size = padded_row_size; | 
 |   } | 
 |  | 
 |   // advance pixels pointer past the skip rows and skip pixels | 
 |   pixels = reinterpret_cast<const int8_t*>(pixels) + skip_size; | 
 |  | 
 |   ScopedTransferBufferPtr buffer(size, helper_, transfer_buffer_); | 
 |   TexSubImage2DImpl( | 
 |       target, level, xoffset, yoffset, width, height, format, type, | 
 |       unpadded_row_size, pixels, padded_row_size, GL_FALSE, &buffer, | 
 |       service_padded_row_size); | 
 |   CheckGLError(); | 
 | } | 
 |  | 
 | void GLES2Implementation::TexSubImage3D( | 
 |     GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, | 
 |     GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, | 
 |     const void* pixels) { | 
 |   const char* func_name = "glTexSubImage3D"; | 
 |   GPU_CLIENT_SINGLE_THREAD_CHECK(); | 
 |   GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glTexSubImage3D(" | 
 |       << GLES2Util::GetStringTextureTarget(target) << ", " | 
 |       << level << ", " | 
 |       << xoffset << ", " << yoffset << ", " << zoffset << ", " | 
 |       << width << ", " << height << ", " << depth << ", " | 
 |       << GLES2Util::GetStringTextureFormat(format) << ", " | 
 |       << GLES2Util::GetStringPixelType(type) << ", " | 
 |       << static_cast<const void*>(pixels) << ")"); | 
 |  | 
 |   if (level < 0 || height < 0 || width < 0 || depth < 0 || | 
 |       xoffset < 0 || yoffset < 0 || zoffset < 0) { | 
 |     SetGLError(GL_INVALID_VALUE, func_name, "dimension < 0"); | 
 |     return; | 
 |   } | 
 |   if ((unpack_skip_pixels_ + width > | 
 |        (unpack_row_length_ ? unpack_row_length_ : width)) || | 
 |       (unpack_skip_rows_ + height > | 
 |        (unpack_image_height_ ? unpack_image_height_ : height))) { | 
 |     // This is WebGL 2 specific constraints, but we do it for all ES3 contexts. | 
 |     SetGLError(GL_INVALID_OPERATION, func_name, | 
 |                "invalid unpack params combination"); | 
 |     return; | 
 |   } | 
 |  | 
 |   uint32_t size; | 
 |   uint32_t unpadded_row_size; | 
 |   uint32_t padded_row_size; | 
 |   uint32_t skip_size; | 
 |   PixelStoreParams params = GetUnpackParameters(k3D); | 
 |   if (!GLES2Util::ComputeImageDataSizesES3(width, height, depth, | 
 |                                            format, type, | 
 |                                            params, | 
 |                                            &size, | 
 |                                            &unpadded_row_size, | 
 |                                            &padded_row_size, | 
 |                                            &skip_size, | 
 |                                            nullptr)) { | 
 |     SetGLError(GL_INVALID_VALUE, func_name, "image size to large"); | 
 |     return; | 
 |   } | 
 |  | 
 |   if (bound_pixel_unpack_buffer_) { | 
 |     base::CheckedNumeric<uint32_t> offset = ToGLuint(pixels); | 
 |     offset += skip_size; | 
 |     if (!offset.IsValid()) { | 
 |       SetGLError(GL_INVALID_VALUE, func_name, "skip size too large"); | 
 |       return; | 
 |     } | 
 |     helper_->TexSubImage3D( | 
 |         target, level, xoffset, yoffset, zoffset, width, height, depth, | 
 |         format, type, 0, offset.ValueOrDefault(0), false); | 
 |     CheckGLError(); | 
 |     return; | 
 |   } | 
 |  | 
 |   // If there's a pixel unpack buffer bound use it when issuing TexSubImage2D. | 
 |   if (bound_pixel_unpack_transfer_buffer_id_) { | 
 |     if (unpack_row_length_ > 0 || unpack_image_height_ > 0 || | 
 |         unpack_skip_pixels_ > 0 || unpack_skip_rows_ > 0 || | 
 |         unpack_skip_images_ > 0) { | 
 |       SetGLError(GL_INVALID_OPERATION, func_name, | 
 |                  "No ES3 pack parameters with pixel unpack transfer buffer."); | 
 |       return; | 
 |     } | 
 |     DCHECK_EQ(0u, skip_size); | 
 |     GLuint offset = ToGLuint(pixels); | 
 |     BufferTracker::Buffer* buffer = GetBoundPixelTransferBufferIfValid( | 
 |         bound_pixel_unpack_transfer_buffer_id_, func_name, offset, size); | 
 |     if (buffer && buffer->shm_id() != -1) { | 
 |       helper_->TexSubImage3D( | 
 |           target, level, xoffset, yoffset, zoffset, width, height, depth, | 
 |           format, type, buffer->shm_id(), buffer->shm_offset() + offset, false); | 
 |       buffer->set_last_usage_token(helper_->InsertToken()); | 
 |       CheckGLError(); | 
 |     } | 
 |     return; | 
 |   } | 
 |  | 
 |   if (width == 0 || height == 0 || depth == 0) { | 
 |     // No need to worry about pixel data. | 
 |     helper_->TexSubImage3D(target, level, xoffset, yoffset, zoffset, | 
 |                            width, height, depth, format, type, 0, 0, false); | 
 |     CheckGLError(); | 
 |     return; | 
 |   } | 
 |  | 
 |   // Compute the advance bytes per row on the service side | 
 |   // Note |size| is recomputed here if needed. | 
 |   uint32_t service_padded_row_size; | 
 |   if ((unpack_row_length_ > 0 && unpack_row_length_ != width) || | 
 |       (unpack_image_height_ > 0 && unpack_image_height_ != height)) { | 
 |     PixelStoreParams service_params; | 
 |     service_params.alignment = unpack_alignment_; | 
 |     if (!GLES2Util::ComputeImageDataSizesES3(width, height, depth, | 
 |                                              format, type, | 
 |                                              service_params, | 
 |                                              &size, | 
 |                                              nullptr, | 
 |                                              &service_padded_row_size, | 
 |                                              nullptr, | 
 |                                              nullptr)) { | 
 |       SetGLError(GL_INVALID_VALUE, func_name, "image size too large"); | 
 |       return; | 
 |     } | 
 |   } else { | 
 |     service_padded_row_size = padded_row_size; | 
 |   } | 
 |  | 
 |   // advance pixels pointer past the skip images/rows/pixels | 
 |   pixels = reinterpret_cast<const int8_t*>(pixels) + skip_size; | 
 |  | 
 |   ScopedTransferBufferPtr buffer(size, helper_, transfer_buffer_); | 
 |   TexSubImage3DImpl( | 
 |       target, level, xoffset, yoffset, zoffset, width, height, depth, | 
 |       format, type, unpadded_row_size, pixels, padded_row_size, GL_FALSE, | 
 |       &buffer, service_padded_row_size); | 
 |   CheckGLError(); | 
 | } | 
 |  | 
 | static GLint ComputeNumRowsThatFitInBuffer(uint32_t padded_row_size, | 
 |                                            uint32_t unpadded_row_size, | 
 |                                            unsigned int size, | 
 |                                            GLsizei remaining_rows) { | 
 |   DCHECK_GE(unpadded_row_size, 0u); | 
 |   if (padded_row_size == 0) { | 
 |     return 1; | 
 |   } | 
 |   GLint num_rows = size / padded_row_size; | 
 |   if (num_rows + 1 == remaining_rows && | 
 |       size - num_rows * padded_row_size >= unpadded_row_size) { | 
 |     num_rows++; | 
 |   } | 
 |   return num_rows; | 
 | } | 
 |  | 
 | void GLES2Implementation::TexSubImage2DImpl(GLenum target, | 
 |                                             GLint level, | 
 |                                             GLint xoffset, | 
 |                                             GLint yoffset, | 
 |                                             GLsizei width, | 
 |                                             GLsizei height, | 
 |                                             GLenum format, | 
 |                                             GLenum type, | 
 |                                             uint32_t unpadded_row_size, | 
 |                                             const void* pixels, | 
 |                                             uint32_t pixels_padded_row_size, | 
 |                                             GLboolean internal, | 
 |                                             ScopedTransferBufferPtr* buffer, | 
 |                                             uint32_t buffer_padded_row_size) { | 
 |   DCHECK(buffer); | 
 |   DCHECK_GE(level, 0); | 
 |   DCHECK_GT(height, 0); | 
 |   DCHECK_GT(width, 0); | 
 |   DCHECK_GE(xoffset, 0); | 
 |   DCHECK_GE(yoffset, 0); | 
 |  | 
 |   const int8_t* source = reinterpret_cast<const int8_t*>(pixels); | 
 |   // 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(), height); | 
 |     num_rows = std::min(num_rows, height); | 
 |     CopyRectToBuffer( | 
 |         source, num_rows, unpadded_row_size, pixels_padded_row_size, | 
 |         buffer->address(), buffer_padded_row_size); | 
 |     helper_->TexSubImage2D( | 
 |         target, level, xoffset, yoffset, 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; | 
 |   } | 
 | } | 
 |  | 
 | void GLES2Implementation::TexSubImage3DImpl(GLenum target, | 
 |                                             GLint level, | 
 |                                             GLint xoffset, | 
 |                                             GLint yoffset, | 
 |                                             GLsizei zoffset, | 
 |                                             GLsizei width, | 
 |                                             GLsizei height, | 
 |                                             GLsizei depth, | 
 |                                             GLenum format, | 
 |                                             GLenum type, | 
 |                                             uint32_t unpadded_row_size, | 
 |                                             const void* pixels, | 
 |                                             uint32_t pixels_padded_row_size, | 
 |                                             GLboolean internal, | 
 |                                             ScopedTransferBufferPtr* buffer, | 
 |                                             uint32_t buffer_padded_row_size) { | 
 |   DCHECK(buffer); | 
 |   DCHECK_GE(level, 0); | 
 |   DCHECK_GT(width, 0); | 
 |   DCHECK_GT(height, 0); | 
 |   DCHECK_GT(depth, 0); | 
 |   DCHECK_GE(xoffset, 0); | 
 |   DCHECK_GE(yoffset, 0); | 
 |   DCHECK_GE(zoffset, 0); | 
 |   const int8_t* source = reinterpret_cast<const int8_t*>(pixels); | 
 |   GLsizei total_rows = height * depth; | 
 |   GLint row_index = 0, depth_index = 0; | 
 |   while (total_rows) { | 
 |     // Each time, we either copy one or more images, or copy one or more rows | 
 |     // within a single image, depending on the buffer size limit. | 
 |     GLsizei max_rows; | 
 |     unsigned int desired_size; | 
 |     if (row_index > 0) { | 
 |       // We are in the middle of an image. Send the remaining of the image. | 
 |       max_rows = height - row_index; | 
 |       if (total_rows <= height) { | 
 |         // Last image, so last row is unpadded. | 
 |         desired_size = buffer_padded_row_size * (max_rows - 1) + | 
 |             unpadded_row_size; | 
 |       } else { | 
 |         desired_size = buffer_padded_row_size * max_rows; | 
 |       } | 
 |     } else { | 
 |       // Send all the remaining data if possible. | 
 |       max_rows = total_rows; | 
 |       desired_size = | 
 |           buffer_padded_row_size * (max_rows - 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(), total_rows); | 
 |     num_rows = std::min(num_rows, max_rows); | 
 |     GLint num_images = num_rows / height; | 
 |     GLsizei my_height, my_depth; | 
 |     if (num_images > 0) { | 
 |       num_rows = num_images * height; | 
 |       my_height = height; | 
 |       my_depth = num_images; | 
 |     } else { | 
 |       my_height = num_rows; | 
 |       my_depth = 1; | 
 |     } | 
 |  | 
 |     if (num_images > 0) { | 
 |       int8_t* buffer_pointer = reinterpret_cast<int8_t*>(buffer->address()); | 
 |       uint32_t src_height = | 
 |           unpack_image_height_ > 0 ? unpack_image_height_ : height; | 
 |       uint32_t image_size_dst = buffer_padded_row_size * height; | 
 |       uint32_t image_size_src = pixels_padded_row_size * src_height; | 
 |       for (GLint ii = 0; ii < num_images; ++ii) { | 
 |         CopyRectToBuffer( | 
 |             source + ii * image_size_src, my_height, unpadded_row_size, | 
 |             pixels_padded_row_size, buffer_pointer + ii * image_size_dst, | 
 |             buffer_padded_row_size); | 
 |       } | 
 |     } else { | 
 |       CopyRectToBuffer( | 
 |           source, my_height, unpadded_row_size, pixels_padded_row_size, | 
 |           buffer->address(), buffer_padded_row_size); | 
 |     } | 
 |     helper_->TexSubImage3D( | 
 |         target, level, xoffset, yoffset + row_index, zoffset + depth_index, | 
 |         width, my_height, my_depth, | 
 |         format, type, buffer->shm_id(), buffer->offset(), internal); | 
 |     buffer->Release(); | 
 |  | 
 |     total_rows -= num_rows; | 
 |     if (total_rows > 0) { | 
 |       GLint num_image_paddings; | 
 |       if (num_images > 0) { | 
 |         DCHECK_EQ(row_index, 0); | 
 |         depth_index += num_images; | 
 |         num_image_paddings = num_images; | 
 |       } else { | 
 |         row_index = (row_index + my_height) % height; | 
 |         num_image_paddings = 0; | 
 |         if (my_height > 0 && row_index == 0) { | 
 |           depth_index++; | 
 |           num_image_paddings++; | 
 |         } | 
 |       } | 
 |       source += num_rows * pixels_padded_row_size; | 
 |       if (unpack_image_height_ > height && num_image_paddings > 0) { | 
 |         source += num_image_paddings * (unpack_image_height_ - height) * | 
 |             pixels_padded_row_size; | 
 |       } | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | 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_t> 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_t> 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(); | 
 | } | 
 |  | 
 | bool GLES2Implementation::GetActiveUniformBlockNameHelper( | 
 |     GLuint program, GLuint index, GLsizei bufsize, | 
 |     GLsizei* length, char* name) { | 
 |   DCHECK_LE(0, bufsize); | 
 |   // Clear the bucket so if the command fails nothing will be in it. | 
 |   helper_->SetBucketSize(kResultBucketId, 0); | 
 |   typedef cmds::GetActiveUniformBlockName::Result Result; | 
 |   Result* result = GetResultAs<Result*>(); | 
 |   if (!result) { | 
 |     return false; | 
 |   } | 
 |   // Set as failed so if the command fails we'll recover. | 
 |   *result = 0; | 
 |   helper_->GetActiveUniformBlockName(program, index, kResultBucketId, | 
 |                                      GetResultShmId(), GetResultShmOffset()); | 
 |   WaitForCmd(); | 
 |   if (*result) { | 
 |     if (bufsize == 0) { | 
 |       if (length) { | 
 |         *length = 0; | 
 |       } | 
 |     } else if (length || name) { | 
 |       std::vector<int8_t> str; | 
 |       GetBucketContents(kResultBucketId, &str); | 
 |       DCHECK_GT(str.size(), 0u); | 
 |       GLsizei max_size = | 
 |           std::min(bufsize, static_cast<GLsizei>(str.size())) - 1; | 
 |       if (length) { | 
 |         *length = max_size; | 
 |       } | 
 |       if (name) { | 
 |         memcpy(name, &str[0], max_size); | 
 |         name[max_size] = '\0'; | 
 |       } | 
 |     } | 
 |   } | 
 |   return *result != 0; | 
 | } | 
 |  | 
 | void GLES2Implementation::GetActiveUniformBlockName( | 
 |     GLuint program, GLuint index, GLsizei bufsize, | 
 |     GLsizei* length, char* name) { | 
 |   GPU_CLIENT_SINGLE_THREAD_CHECK(); | 
 |   GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glGetActiveUniformBlockName(" | 
 |       << program << ", " << index << ", " << bufsize << ", " | 
 |       << static_cast<const void*>(length) << ", " | 
 |       << static_cast<const void*>(name) << ")"); | 
 |   if (bufsize < 0) { | 
 |     SetGLError(GL_INVALID_VALUE, "glGetActiveUniformBlockName", "bufsize < 0"); | 
 |     return; | 
 |   } | 
 |   TRACE_EVENT0("gpu", "GLES2::GetActiveUniformBlockName"); | 
 |   bool success = | 
 |       share_group_->program_info_manager()->GetActiveUniformBlockName( | 
 |           this, program, index, bufsize, length, name); | 
 |   if (success) { | 
 |     if (name) { | 
 |       GPU_CLIENT_LOG("  name: " << name); | 
 |     } | 
 |   } | 
 |   CheckGLError(); | 
 | } | 
 |  | 
 | bool GLES2Implementation::GetActiveUniformBlockivHelper( | 
 |     GLuint program, GLuint index, GLenum pname, GLint* params) { | 
 |   typedef cmds::GetActiveUniformBlockiv::Result Result; | 
 |   Result* result = GetResultAs<Result*>(); | 
 |   if (!result) { | 
 |     return false; | 
 |   } | 
 |   result->SetNumResults(0); | 
 |   helper_->GetActiveUniformBlockiv( | 
 |       program, index, pname, GetResultShmId(), GetResultShmOffset()); | 
 |   WaitForCmd(); | 
 |   if (result->GetNumResults() > 0) { | 
 |     if (params) { | 
 |       result->CopyResult(params); | 
 |     } | 
 |     GPU_CLIENT_LOG_CODE_BLOCK({ | 
 |       for (int32_t i = 0; i < result->GetNumResults(); ++i) { | 
 |         GPU_CLIENT_LOG("  " << i << ": " << result->GetData()[i]); | 
 |       } | 
 |     }); | 
 |     return true; | 
 |   } | 
 |   return false; | 
 | } | 
 |  | 
 | void GLES2Implementation::GetActiveUniformBlockiv( | 
 |     GLuint program, GLuint index, GLenum pname, GLint* params) { | 
 |   GPU_CLIENT_SINGLE_THREAD_CHECK(); | 
 |   GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glGetActiveUniformBlockiv(" | 
 |       << program << ", " << index << ", " | 
 |       << GLES2Util::GetStringUniformBlockParameter(pname) << ", " | 
 |       << static_cast<const void*>(params) << ")"); | 
 |   TRACE_EVENT0("gpu", "GLES2::GetActiveUniformBlockiv"); | 
 |   bool success = | 
 |       share_group_->program_info_manager()->GetActiveUniformBlockiv( | 
 |           this, program, index, pname, params); | 
 |   if (success) { | 
 |     if (params) { | 
 |       // TODO(zmo): For GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES, there will | 
 |       // be more than one value returned in params. | 
 |       GPU_CLIENT_LOG("  params: " << params[0]); | 
 |     } | 
 |   } | 
 |   CheckGLError(); | 
 | } | 
 |  | 
 | bool GLES2Implementation::GetActiveUniformsivHelper( | 
 |     GLuint program, GLsizei count, const GLuint* indices, | 
 |     GLenum pname, GLint* params) { | 
 |   typedef cmds::GetActiveUniformsiv::Result Result; | 
 |   Result* result = GetResultAs<Result*>(); | 
 |   if (!result) { | 
 |     return false; | 
 |   } | 
 |   result->SetNumResults(0); | 
 |   base::CheckedNumeric<size_t> bytes = static_cast<size_t>(count); | 
 |   bytes *= sizeof(GLuint); | 
 |   if (!bytes.IsValid()) { | 
 |     SetGLError(GL_INVALID_VALUE, "glGetActiveUniformsiv", "count overflow"); | 
 |     return false; | 
 |   } | 
 |   SetBucketContents(kResultBucketId, indices, bytes.ValueOrDefault(0)); | 
 |   helper_->GetActiveUniformsiv( | 
 |       program, kResultBucketId, pname, GetResultShmId(), GetResultShmOffset()); | 
 |   WaitForCmd(); | 
 |   bool success = result->GetNumResults() == count; | 
 |   if (success) { | 
 |     if (params) { | 
 |       result->CopyResult(params); | 
 |     } | 
 |     GPU_CLIENT_LOG_CODE_BLOCK({ | 
 |       for (int32_t i = 0; i < result->GetNumResults(); ++i) { | 
 |         GPU_CLIENT_LOG("  " << i << ": " << result->GetData()[i]); | 
 |       } | 
 |     }); | 
 |   } | 
 |   helper_->SetBucketSize(kResultBucketId, 0); | 
 |   return success; | 
 | } | 
 |  | 
 | void GLES2Implementation::GetActiveUniformsiv( | 
 |     GLuint program, GLsizei count, const GLuint* indices, | 
 |     GLenum pname, GLint* params) { | 
 |   GPU_CLIENT_SINGLE_THREAD_CHECK(); | 
 |   GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glGetActiveUniformsiv(" | 
 |       << program << ", " << count << ", " | 
 |       << static_cast<const void*>(indices) << ", " | 
 |       << GLES2Util::GetStringUniformParameter(pname) << ", " | 
 |       << static_cast<const void*>(params) << ")"); | 
 |   TRACE_EVENT0("gpu", "GLES2::GetActiveUniformsiv"); | 
 |   if (count < 0) { | 
 |     SetGLError(GL_INVALID_VALUE, "glGetActiveUniformsiv", "count < 0"); | 
 |     return; | 
 |   } | 
 |   bool success = share_group_->program_info_manager()->GetActiveUniformsiv( | 
 |       this, program, count, indices, pname, params); | 
 |   if (success) { | 
 |     if (params) { | 
 |       GPU_CLIENT_LOG_CODE_BLOCK({ | 
 |         for (GLsizei ii = 0; ii < count; ++ii) { | 
 |           GPU_CLIENT_LOG("  " << ii << ": " << params[ii]); | 
 |         } | 
 |       }); | 
 |     } | 
 |   } | 
 |   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_t 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_t token = helper_->InsertToken(); | 
 |   WaitForCmd(); | 
 |   if (count) { | 
 |     *count = result->GetNumResults(); | 
 |   } | 
 |   result->CopyResult(shaders); | 
 |   GPU_CLIENT_LOG_CODE_BLOCK({ | 
 |     for (int32_t 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) { | 
 |   if (name == GL_EXTENSIONS && cached_extension_string_) { | 
 |     return reinterpret_cast<const GLubyte*>(cached_extension_string_); | 
 |   } | 
 |   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. | 
 |     if (name == GL_EXTENSIONS) { | 
 |       str += std::string(str.empty() ? "" : " ") + | 
 |              "GL_EXT_unpack_subimage " | 
 |              "GL_CHROMIUM_map_sub"; | 
 |       if (capabilities_.image) | 
 |         str += " GL_CHROMIUM_image GL_CHROMIUM_gpu_memory_buffer_image"; | 
 |       if (capabilities_.future_sync_points) | 
 |         str += " GL_CHROMIUM_future_sync_point"; | 
 |     } | 
 |  | 
 |     // 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. | 
 |     // TODO: Here we could save memory by defining RequestExtensions | 
 |     // invalidating the GL_EXTENSIONS string. http://crbug.com/586414 | 
 |     const std::string& cache = *gl_strings_.insert(str).first; | 
 |     result = cache.c_str(); | 
 |  | 
 |     if (name == GL_EXTENSIONS) { | 
 |       cached_extension_string_ = result; | 
 |       std::vector<std::string> extensions = | 
 |           base::SplitString(cache, base::kWhitespaceASCII, | 
 |                             base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY); | 
 |       for (const std::string& extension : extensions) { | 
 |         cached_extensions_.push_back( | 
 |             gl_strings_.insert(extension).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; | 
 | } | 
 |  | 
 | const GLubyte* GLES2Implementation::GetStringi(GLenum name, GLuint index) { | 
 |   GPU_CLIENT_SINGLE_THREAD_CHECK(); | 
 |   GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glGetStringi(" | 
 |                      << GLES2Util::GetStringStringType(name) << "," << index | 
 |                      << ")"); | 
 |   TRACE_EVENT0("gpu", "GLES2::GetStringi"); | 
 |   UpdateCachedExtensionsIfNeeded(); | 
 |   if (name != GL_EXTENSIONS) { | 
 |     SetGLError(GL_INVALID_ENUM, "glGetStringi", "name"); | 
 |     return nullptr; | 
 |   } | 
 |   if (index >= cached_extensions_.size()) { | 
 |     SetGLError(GL_INVALID_VALUE, "glGetStringi", "index too large"); | 
 |     return nullptr; | 
 |   } | 
 |  | 
 |   const char* result = cached_extensions_[index]; | 
 |   GPU_CLIENT_LOG("  returned " << result); | 
 |   CheckGLError(); | 
 |   return reinterpret_cast<const GLubyte*>(result); | 
 | } | 
 |  | 
 | bool GLES2Implementation::GetTransformFeedbackVaryingHelper( | 
 |     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::GetTransformFeedbackVarying::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_->GetTransformFeedbackVarying( | 
 |       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_t> str; | 
 |       GetBucketContents(kResultBucketId, &str); | 
 |       GLsizei max_size = std::min(bufsize, static_cast<GLsizei>(str.size())); | 
 |       if (max_size > 0) { | 
 |         --max_size; | 
 |       } | 
 |       if (length) { | 
 |         *length = max_size; | 
 |       } | 
 |       if (name) { | 
 |         if (max_size > 0) { | 
 |           memcpy(name, &str[0], max_size); | 
 |           name[max_size] = '\0'; | 
 |         } else if (bufsize > 0) { | 
 |           name[0] = '\0'; | 
 |         } | 
 |       } | 
 |     } | 
 |   } | 
 |   return result->success != 0; | 
 | } | 
 |  | 
 | void GLES2Implementation::GetTransformFeedbackVarying( | 
 |     GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, | 
 |     GLenum* type, char* name) { | 
 |   GPU_CLIENT_SINGLE_THREAD_CHECK(); | 
 |   GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glGetTransformFeedbackVarying(" | 
 |       << 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, "glGetTransformFeedbackVarying", | 
 |                "bufsize < 0"); | 
 |     return; | 
 |   } | 
 |   TRACE_EVENT0("gpu", "GLES2::GetTransformFeedbackVarying"); | 
 |   bool success = | 
 |       share_group_->program_info_manager()->GetTransformFeedbackVarying( | 
 |           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::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_t 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::GetUniformiv::Result*>()->CopyResult(params); | 
 |   GPU_CLIENT_LOG_CODE_BLOCK({ | 
 |     for (int32_t i = 0; i < result->GetNumResults(); ++i) { | 
 |       GPU_CLIENT_LOG("  " << i << ": " << result->GetData()[i]); | 
 |     } | 
 |   }); | 
 |   CheckGLError(); | 
 | } | 
 |  | 
 | void GLES2Implementation::GetUniformuiv( | 
 |     GLuint program, GLint location, GLuint* params) { | 
 |   GPU_CLIENT_SINGLE_THREAD_CHECK(); | 
 |   GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glGetUniformuiv(" | 
 |       << program << ", " << location << ", " | 
 |       << static_cast<const void*>(params) << ")"); | 
 |   TRACE_EVENT0("gpu", "GLES2::GetUniformuiv"); | 
 |   typedef cmds::GetUniformuiv::Result Result; | 
 |   Result* result = GetResultAs<Result*>(); | 
 |   if (!result) { | 
 |     return; | 
 |   } | 
 |   result->SetNumResults(0); | 
 |   helper_->GetUniformuiv( | 
 |       program, location, GetResultShmId(), GetResultShmOffset()); | 
 |   WaitForCmd(); | 
 |   GetResultAs<cmds::GetUniformuiv::Result*>()->CopyResult(params); | 
 |   GPU_CLIENT_LOG_CODE_BLOCK({ | 
 |     for (int32_t 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) { | 
 |   const char* func_name = "glReadPixels"; | 
 |   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, func_name, "dimensions < 0"); | 
 |     return; | 
 |   } | 
 |  | 
 |   if (pack_skip_pixels_ + width > | 
 |       (pack_row_length_ ? pack_row_length_ : width)) { | 
 |     // This is WebGL 2 specific constraints, but we do it for all ES3 contexts. | 
 |     SetGLError(GL_INVALID_OPERATION, func_name, | 
 |                "invalid pack params combination"); | 
 |     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; | 
 |  | 
 |   uint32_t size; | 
 |   uint32_t unpadded_row_size; | 
 |   uint32_t padded_row_size; | 
 |   uint32_t skip_size; | 
 |   PixelStoreParams params; | 
 |   params.alignment = pack_alignment_; | 
 |   params.row_length = pack_row_length_; | 
 |   params.skip_pixels = pack_skip_pixels_; | 
 |   params.skip_rows = pack_skip_rows_; | 
 |   if (!GLES2Util::ComputeImageDataSizesES3(width, height, 1, | 
 |                                            format, type, | 
 |                                            params, | 
 |                                            &size, | 
 |                                            &unpadded_row_size, | 
 |                                            &padded_row_size, | 
 |                                            &skip_size, | 
 |                                            nullptr)) { | 
 |     SetGLError(GL_INVALID_VALUE, func_name, "size too large."); | 
 |     return; | 
 |   } | 
 |  | 
 |   if (bound_pixel_pack_buffer_) { | 
 |     base::CheckedNumeric<GLuint> offset = ToGLuint(pixels); | 
 |     offset += skip_size; | 
 |     if (!offset.IsValid()) { | 
 |       SetGLError(GL_INVALID_VALUE, func_name, "skip size too large."); | 
 |       return; | 
 |     } | 
 |     helper_->ReadPixels(xoffset, yoffset, width, height, format, type, 0, | 
 |                         offset.ValueOrDefault(0), 0, 0, false); | 
 |     CheckGLError(); | 
 |     return; | 
 |   } | 
 |  | 
 |   uint32_t service_padded_row_size = 0; | 
 |   if (pack_row_length_ > 0 && pack_row_length_ != width) { | 
 |     if (!GLES2Util::ComputeImagePaddedRowSize(width, | 
 |                                               format, type, | 
 |                                               pack_alignment_, | 
 |                                               &service_padded_row_size)) { | 
 |       SetGLError(GL_INVALID_VALUE, func_name, "size too large."); | 
 |       return; | 
 |     } | 
 |   } else { | 
 |     service_padded_row_size = padded_row_size; | 
 |   } | 
 |  | 
 |   if (bound_pixel_pack_transfer_buffer_id_) { | 
 |     if (pack_row_length_ > 0 || pack_skip_pixels_ > 0 || pack_skip_rows_ > 0) { | 
 |       SetGLError(GL_INVALID_OPERATION, func_name, | 
 |                  "No ES3 pack parameters with pixel pack transfer buffer."); | 
 |       return; | 
 |     } | 
 |     DCHECK_EQ(0u, skip_size); | 
 |     GLuint offset = ToGLuint(pixels); | 
 |     BufferTracker::Buffer* buffer = GetBoundPixelTransferBufferIfValid( | 
 |         bound_pixel_pack_transfer_buffer_id_, func_name, offset, size); | 
 |     if (buffer && buffer->shm_id() != -1) { | 
 |       helper_->ReadPixels(xoffset, yoffset, width, height, format, type, | 
 |                           buffer->shm_id(), buffer->shm_offset() + offset, | 
 |                           0, 0, true); | 
 |       CheckGLError(); | 
 |     } | 
 |     return; | 
 |   } | 
 |  | 
 |   if (!pixels) { | 
 |     SetGLError(GL_INVALID_OPERATION, func_name, "pixels = NULL"); | 
 |     return; | 
 |   } | 
 |  | 
 |   int8_t* dest = reinterpret_cast<int8_t*>(pixels); | 
 |   // Advance pixels pointer past the skip rows and skip pixels | 
 |   dest += skip_size; | 
 |  | 
 |   // Transfer by rows. | 
 |   // The max rows we can transfer. | 
 |   GLsizei remaining_rows = height; | 
 |   GLint y_index = yoffset; | 
 |   uint32_t group_size = GLES2Util::ComputeImageGroupSize(format, type); | 
 |   uint32_t skip_row_bytes = 0; | 
 |   if (xoffset < 0) { | 
 |     skip_row_bytes = static_cast<uint32_t>(-xoffset) * group_size; | 
 |   } | 
 |   do { | 
 |     // Even if height == 0, we still need to trigger the service side handling | 
 |     // in case invalid args are passed in and a GL errro needs to be generated. | 
 |     GLsizei desired_size = remaining_rows == 0 ? 0 : | 
 |         service_padded_row_size * (remaining_rows - 1) + unpadded_row_size; | 
 |     ScopedTransferBufferPtr buffer(desired_size, helper_, transfer_buffer_); | 
 |     if (!buffer.valid()) { | 
 |       break; | 
 |     } | 
 |     GLint num_rows = ComputeNumRowsThatFitInBuffer( | 
 |         service_padded_row_size, unpadded_row_size, buffer.size(), | 
 |         remaining_rows); | 
 |     // 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) { | 
 |       break; | 
 |     } | 
 |     result->success = 0;  // mark as failed. | 
 |     result->row_length = 0; | 
 |     result->num_rows = 0; | 
 |     helper_->ReadPixels( | 
 |         xoffset, y_index, width, num_rows, format, type, | 
 |         buffer.shm_id(), buffer.offset(), | 
 |         GetResultShmId(), GetResultShmOffset(), | 
 |         false); | 
 |     WaitForCmd(); | 
 |     // If it was not marked as successful exit. | 
 |     if (!result->success) { | 
 |       break; | 
 |     } | 
 |     if (remaining_rows == 0) { | 
 |       break; | 
 |     } | 
 |     const uint8_t* src = static_cast<const uint8_t*>(buffer.address()); | 
 |     if (padded_row_size == unpadded_row_size && | 
 |         (pack_row_length_ == 0 || pack_row_length_ == width) && | 
 |         result->row_length == width && result->num_rows == num_rows) { | 
 |       // The pixels are tightly packed. | 
 |       uint32_t copy_size = unpadded_row_size * num_rows; | 
 |       memcpy(dest, src, copy_size); | 
 |       dest += copy_size; | 
 |     } else if (result->row_length > 0 && result->num_rows > 0) { | 
 |       uint32_t copy_row_size = result->row_length * group_size; | 
 |       uint32_t copy_last_row_size = copy_row_size; | 
 |       if (copy_row_size + skip_row_bytes > padded_row_size) { | 
 |         // We need to avoid writing into next row in case the leading pixels | 
 |         // are out-of-bounds and they need to be left untouched. | 
 |         copy_row_size = padded_row_size - skip_row_bytes; | 
 |       } | 
 |       // We have to copy 1 row at a time to avoid writing padding bytes. | 
 |       GLint copied_rows = 0; | 
 |       for (GLint yy = 0; yy < num_rows; ++yy) { | 
 |         if (y_index + yy >= 0 && copied_rows < result->num_rows) { | 
 |           if (yy + 1 == num_rows && remaining_rows == num_rows) { | 
 |             memcpy(dest + skip_row_bytes, src + skip_row_bytes, | 
 |                    copy_last_row_size); | 
 |           } else { | 
 |             memcpy(dest + skip_row_bytes, src + skip_row_bytes, copy_row_size); | 
 |           } | 
 |           ++copied_rows; | 
 |         } | 
 |         dest += padded_row_size; | 
 |         src += service_padded_row_size; | 
 |       } | 
 |       DCHECK_EQ(result->num_rows, copied_rows); | 
 |     } | 
 |     y_index += num_rows; | 
 |     remaining_rows -= num_rows; | 
 |   } while (remaining_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>(capabilities_.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 */) { | 
 | } | 
 |  | 
 | void GLES2Implementation::GenSamplersHelper( | 
 |     GLsizei /* n */, const GLuint* /* samplers */) { | 
 | } | 
 |  | 
 | void GLES2Implementation::GenTransformFeedbacksHelper( | 
 |     GLsizei /* n */, const GLuint* /* transformfeedbacks */) { | 
 | } | 
 |  | 
 | // 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. | 
 |  | 
 | // NOTE #2: There is a bug in some BindXXXHelpers, that IDs might be marked as | 
 | // used even when Bind has failed. However, the bug is minor compared to the | 
 | // overhead & duplicated checking in client side. | 
 |  | 
 | void GLES2Implementation::BindBufferHelper( | 
 |     GLenum target, GLuint buffer_id) { | 
 |   // TODO(gman): See note #1 above. | 
 |   bool changed = false; | 
 |   switch (target) { | 
 |     case GL_ARRAY_BUFFER: | 
 |       if (bound_array_buffer_ != buffer_id) { | 
 |         bound_array_buffer_ = buffer_id; | 
 |         changed = true; | 
 |       } | 
 |       break; | 
 |     case GL_COPY_READ_BUFFER: | 
 |       if (bound_copy_read_buffer_ != buffer_id) { | 
 |         bound_copy_read_buffer_ = buffer_id; | 
 |         changed = true; | 
 |       } | 
 |       break; | 
 |     case GL_COPY_WRITE_BUFFER: | 
 |       if (bound_copy_write_buffer_ != buffer_id) { | 
 |         bound_copy_write_buffer_ = buffer_id; | 
 |         changed = true; | 
 |       } | 
 |       break; | 
 |     case GL_ELEMENT_ARRAY_BUFFER: | 
 |       changed = vertex_array_object_manager_->BindElementArray(buffer_id); | 
 |       break; | 
 |     case GL_PIXEL_PACK_BUFFER: | 
 |       if (bound_pixel_pack_buffer_ != buffer_id) { | 
 |         bound_pixel_pack_buffer_ = buffer_id; | 
 |         changed = true; | 
 |       } | 
 |       break; | 
 |     case GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM: | 
 |       bound_pixel_pack_transfer_buffer_id_ = buffer_id; | 
 |       break; | 
 |     case GL_PIXEL_UNPACK_BUFFER: | 
 |       if (bound_pixel_unpack_buffer_ != buffer_id) { | 
 |         bound_pixel_unpack_buffer_ = buffer_id; | 
 |         changed = true; | 
 |       } | 
 |       break; | 
 |     case GL_PIXEL_UNPACK_TRANSFER_BUFFER_CHROMIUM: | 
 |       bound_pixel_unpack_transfer_buffer_id_ = buffer_id; | 
 |       break; | 
 |     case GL_TRANSFORM_FEEDBACK_BUFFER: | 
 |       if (bound_transform_feedback_buffer_ != buffer_id) { | 
 |         bound_transform_feedback_buffer_ = buffer_id; | 
 |         changed = true; | 
 |       } | 
 |       break; | 
 |     case GL_UNIFORM_BUFFER: | 
 |       if (bound_uniform_buffer_ != buffer_id) { | 
 |         bound_uniform_buffer_ = buffer_id; | 
 |         changed = true; | 
 |       } | 
 |       break; | 
 |     default: | 
 |       changed = true; | 
 |       break; | 
 |   } | 
 |   // TODO(gman): See note #2 above. | 
 |   if (changed) { | 
 |     GetIdHandler(id_namespaces::kBuffers)->MarkAsUsedForBind( | 
 |         this, target, buffer_id, &GLES2Implementation::BindBufferStub); | 
 |   } | 
 | } | 
 |  | 
 | void GLES2Implementation::BindBufferStub(GLenum target, GLuint buffer) { | 
 |   helper_->BindBuffer(target, buffer); | 
 |   if (share_group_->bind_generates_resource()) | 
 |     helper_->CommandBufferHelper::OrderingBarrier(); | 
 | } | 
 |  | 
 | void GLES2Implementation::BindBufferBaseHelper( | 
 |     GLenum target, GLuint index, GLuint buffer_id) { | 
 |   // TODO(zmo): See note #1 above. | 
 |   // TODO(zmo): See note #2 above. | 
 |   switch (target) { | 
 |     case GL_TRANSFORM_FEEDBACK_BUFFER: | 
 |       if (index >= | 
 |           static_cast<GLuint>( | 
 |               capabilities_.max_transform_feedback_separate_attribs)) { | 
 |         SetGLError(GL_INVALID_VALUE, | 
 |                    "glBindBufferBase", "index out of range"); | 
 |         return; | 
 |       } | 
 |       if (bound_transform_feedback_buffer_ != buffer_id) { | 
 |         bound_transform_feedback_buffer_ = buffer_id; | 
 |       } | 
 |       break; | 
 |     case GL_UNIFORM_BUFFER: | 
 |       if (index >= | 
 |           static_cast<GLuint>(capabilities_.max_uniform_buffer_bindings)) { | 
 |         SetGLError(GL_INVALID_VALUE, | 
 |                    "glBindBufferBase", "index out of range"); | 
 |         return; | 
 |       } | 
 |       if (bound_uniform_buffer_ != buffer_id) { | 
 |         bound_uniform_buffer_ = buffer_id; | 
 |       } | 
 |       break; | 
 |     default: | 
 |       SetGLError(GL_INVALID_ENUM, "glBindBufferBase", "invalid target"); | 
 |       return; | 
 |   } | 
 |   GetIdHandler(id_namespaces::kBuffers)->MarkAsUsedForBind( | 
 |       this, target, index, buffer_id, &GLES2Implementation::BindBufferBaseStub); | 
 | } | 
 |  | 
 | void GLES2Implementation::BindBufferBaseStub( | 
 |     GLenum target, GLuint index, GLuint buffer) { | 
 |   helper_->BindBufferBase(target, index, buffer); | 
 |   if (share_group_->bind_generates_resource()) | 
 |     helper_->CommandBufferHelper::Flush(); | 
 | } | 
 |  | 
 | void GLES2Implementation::BindBufferRangeHelper( | 
 |     GLenum target, GLuint index, GLuint buffer_id, | 
 |     GLintptr offset, GLsizeiptr size) { | 
 |   // TODO(zmo): See note #1 above. | 
 |   // TODO(zmo): See note #2 above. | 
 |   GetIdHandler(id_namespaces::kBuffers)->MarkAsUsedForBind( | 
 |       this, target, index, buffer_id, offset, size, | 
 |       &GLES2Implementation::BindBufferRangeStub); | 
 | } | 
 |  | 
 | void GLES2Implementation::BindBufferRangeStub( | 
 |     GLenum target, GLuint index, GLuint buffer, | 
 |     GLintptr offset, GLsizeiptr size) { | 
 |   helper_->BindBufferRange(target, index, buffer, offset, size); | 
 |   if (share_group_->bind_generates_resource()) | 
 |     helper_->CommandBufferHelper::Flush(); | 
 | } | 
 |  | 
 | void 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; | 
 |       } | 
 |       if (bound_read_framebuffer_ != framebuffer) { | 
 |         bound_read_framebuffer_ = framebuffer; | 
 |         changed = true; | 
 |       } | 
 |       break; | 
 |     case GL_DRAW_FRAMEBUFFER: | 
 |       if (!IsChromiumFramebufferMultisampleAvailable()) { | 
 |         SetGLErrorInvalidEnum("glBindFramebuffer", target, "target"); | 
 |         return; | 
 |       } | 
 |       if (bound_framebuffer_ != framebuffer) { | 
 |         bound_framebuffer_ = framebuffer; | 
 |         changed = true; | 
 |       } | 
 |       break; | 
 |     default: | 
 |       SetGLErrorInvalidEnum("glBindFramebuffer", target, "target"); | 
 |       return; | 
 |   } | 
 |  | 
 |   if (changed) { | 
 |     GetIdHandler(id_namespaces::kFramebuffers)->MarkAsUsedForBind( | 
 |         this, target, framebuffer, &GLES2Implementation::BindFramebufferStub); | 
 |   } | 
 | } | 
 |  | 
 | void GLES2Implementation::BindFramebufferStub(GLenum target, | 
 |                                               GLuint framebuffer) { | 
 |   helper_->BindFramebuffer(target, framebuffer); | 
 |   if (share_group_->bind_generates_resource()) | 
 |     helper_->CommandBufferHelper::OrderingBarrier(); | 
 | } | 
 |  | 
 | void 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(zmo): See note #2 above. | 
 |   if (changed) { | 
 |     GetIdHandler(id_namespaces::kRenderbuffers)->MarkAsUsedForBind( | 
 |         this, target, renderbuffer, | 
 |         &GLES2Implementation::BindRenderbufferStub); | 
 |   } | 
 | } | 
 |  | 
 | void GLES2Implementation::BindRenderbufferStub(GLenum target, | 
 |                                                GLuint renderbuffer) { | 
 |   helper_->BindRenderbuffer(target, renderbuffer); | 
 |   if (share_group_->bind_generates_resource()) | 
 |     helper_->CommandBufferHelper::OrderingBarrier(); | 
 | } | 
 |  | 
 | void GLES2Implementation::BindSamplerHelper(GLuint unit, | 
 |                                             GLuint sampler) { | 
 |   helper_->BindSampler(unit, sampler); | 
 | } | 
 |  | 
 | void 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; | 
 |     case GL_TEXTURE_EXTERNAL_OES: | 
 |       if (unit.bound_texture_external_oes != texture) { | 
 |         unit.bound_texture_external_oes = texture; | 
 |         changed = true; | 
 |       } | 
 |       break; | 
 |     default: | 
 |       changed = true; | 
 |       break; | 
 |   } | 
 |   // TODO(gman): See note #2 above. | 
 |   if (changed) { | 
 |     GetIdHandler(id_namespaces::kTextures)->MarkAsUsedForBind( | 
 |         this, target, texture, &GLES2Implementation::BindTextureStub); | 
 |   } | 
 | } | 
 |  | 
 | void GLES2Implementation::BindTextureStub(GLenum target, GLuint texture) { | 
 |   helper_->BindTexture(target, texture); | 
 |   if (share_group_->bind_generates_resource()) | 
 |     helper_->CommandBufferHelper::OrderingBarrier(); | 
 | } | 
 |  | 
 | void GLES2Implementation::BindTransformFeedbackHelper( | 
 |     GLenum target, GLuint transformfeedback) { | 
 |   helper_->BindTransformFeedback(target, transformfeedback); | 
 | } | 
 |  | 
 | void GLES2Implementation::BindVertexArrayOESHelper(GLuint array) { | 
 |   bool changed = false; | 
 |   if (vertex_array_object_manager_->BindVertexArray(array, &changed)) { | 
 |     if (changed) { | 
 |       // 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. | 
 |       helper_->BindVertexArrayOES(array); | 
 |     } | 
 |   } else { | 
 |     SetGLError( | 
 |         GL_INVALID_OPERATION, "glBindVertexArrayOES", | 
 |         "id was not generated with glGenVertexArrayOES"); | 
 |   } | 
 | } | 
 |  | 
 | void GLES2Implementation::UseProgramHelper(GLuint program) { | 
 |   if (current_program_ != program) { | 
 |     current_program_ = program; | 
 |     helper_->UseProgram(program); | 
 |   } | 
 | } | 
 |  | 
 | 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_) { | 
 |       bound_array_buffer_ = 0; | 
 |     } | 
 |     if (buffers[ii] == bound_copy_read_buffer_) { | 
 |       bound_copy_read_buffer_ = 0; | 
 |     } | 
 |     if (buffers[ii] == bound_copy_write_buffer_) { | 
 |       bound_copy_write_buffer_ = 0; | 
 |     } | 
 |     if (buffers[ii] == bound_pixel_pack_buffer_) { | 
 |       bound_pixel_pack_buffer_ = 0; | 
 |     } | 
 |     if (buffers[ii] == bound_pixel_unpack_buffer_) { | 
 |       bound_pixel_unpack_buffer_ = 0; | 
 |     } | 
 |     if (buffers[ii] == bound_transform_feedback_buffer_) { | 
 |       bound_transform_feedback_buffer_ = 0; | 
 |     } | 
 |     if (buffers[ii] == bound_uniform_buffer_) { | 
 |       bound_uniform_buffer_ = 0; | 
 |     } | 
 |     vertex_array_object_manager_->UnbindBuffer(buffers[ii]); | 
 |  | 
 |     BufferTracker::Buffer* buffer = buffer_tracker_->GetBuffer(buffers[ii]); | 
 |     if (buffer) | 
 |       RemoveTransferBuffer(buffer); | 
 |  | 
 |     if (buffers[ii] == bound_pixel_unpack_transfer_buffer_id_) { | 
 |       bound_pixel_unpack_transfer_buffer_id_ = 0; | 
 |     } | 
 |  | 
 |     RemoveMappedBufferRangeById(buffers[ii]); | 
 |   } | 
 | } | 
 |  | 
 | 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 < capabilities_.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; | 
 |       } | 
 |       if (textures[ii] == unit.bound_texture_external_oes) { | 
 |         unit.bound_texture_external_oes = 0; | 
 |       } | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | void GLES2Implementation::DeleteTexturesStub(GLsizei n, | 
 |                                              const GLuint* textures) { | 
 |   helper_->DeleteTexturesImmediate(n, textures); | 
 | } | 
 |  | 
 | 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::DeleteSamplersStub( | 
 |     GLsizei n, const GLuint* samplers) { | 
 |   helper_->DeleteSamplersImmediate(n, samplers); | 
 | } | 
 |  | 
 | void GLES2Implementation::DeleteSamplersHelper( | 
 |     GLsizei n, const GLuint* samplers) { | 
 |   if (!GetIdHandler(id_namespaces::kSamplers)->FreeIds( | 
 |       this, n, samplers, &GLES2Implementation::DeleteSamplersStub)) { | 
 |     SetGLError( | 
 |         GL_INVALID_VALUE, | 
 |         "glDeleteSamplers", "id not created by this context."); | 
 |     return; | 
 |   } | 
 | } | 
 |  | 
 | void GLES2Implementation::DeleteTransformFeedbacksStub( | 
 |     GLsizei n, const GLuint* transformfeedbacks) { | 
 |   helper_->DeleteTransformFeedbacksImmediate(n, transformfeedbacks); | 
 | } | 
 |  | 
 | void GLES2Implementation::DeleteTransformFeedbacksHelper( | 
 |     GLsizei n, const GLuint* transformfeedbacks) { | 
 |   if (!GetIdHandler(id_namespaces::kTransformFeedbacks)->FreeIds( | 
 |       this, n, transformfeedbacks, | 
 |       &GLES2Implementation::DeleteTransformFeedbacksStub)) { | 
 |     SetGLError( | 
 |         GL_INVALID_VALUE, | 
 |         "glDeleteTransformFeedbacks", "id not created by this context."); | 
 |     return; | 
 |   } | 
 | } | 
 |  | 
 | 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_->SupportsClientSideBuffers()) { | 
 |     GLsizei num_elements; | 
 |     SafeAddInt32(first, count, &num_elements); | 
 |     if (!vertex_array_object_manager_->SetupSimulatedClientSideBuffers( | 
 |             "glDrawArrays", this, helper_, num_elements, 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_t value = 0; | 
 |   if (vertex_array_object_manager_->GetVertexAttrib(index, pname, &value)) { | 
 |     *params = static_cast<GLfloat>(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_t 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_t value = 0; | 
 |   if (vertex_array_object_manager_->GetVertexAttrib(index, pname, &value)) { | 
 |     *params = static_cast<GLint>(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_t i = 0; i < result->GetNumResults(); ++i) { | 
 |       GPU_CLIENT_LOG("  " << i << ": " << result->GetData()[i]); | 
 |     } | 
 |   }); | 
 |   CheckGLError(); | 
 | } | 
 |  | 
 | void GLES2Implementation::GetVertexAttribIiv( | 
 |     GLuint index, GLenum pname, GLint* params) { | 
 |   GPU_CLIENT_SINGLE_THREAD_CHECK(); | 
 |   GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glGetVertexAttribIiv(" | 
 |       << index << ", " | 
 |       << GLES2Util::GetStringVertexAttribute(pname) << ", " | 
 |       << static_cast<const void*>(params) << ")"); | 
 |   uint32_t value = 0; | 
 |   if (vertex_array_object_manager_->GetVertexAttrib(index, pname, &value)) { | 
 |     *params = static_cast<GLint>(value); | 
 |     return; | 
 |   } | 
 |   TRACE_EVENT0("gpu", "GLES2::GetVertexAttribIiv"); | 
 |   typedef cmds::GetVertexAttribiv::Result Result; | 
 |   Result* result = GetResultAs<Result*>(); | 
 |   if (!result) { | 
 |     return; | 
 |   } | 
 |   result->SetNumResults(0); | 
 |   helper_->GetVertexAttribIiv( | 
 |       index, pname, GetResultShmId(), GetResultShmOffset()); | 
 |   WaitForCmd(); | 
 |   result->CopyResult(params); | 
 |   GPU_CLIENT_LOG_CODE_BLOCK({ | 
 |     for (int32_t i = 0; i < result->GetNumResults(); ++i) { | 
 |       GPU_CLIENT_LOG("  " << i << ": " << result->GetData()[i]); | 
 |     } | 
 |   }); | 
 |   CheckGLError(); | 
 | } | 
 |  | 
 | void GLES2Implementation::GetVertexAttribIuiv( | 
 |     GLuint index, GLenum pname, GLuint* params) { | 
 |   GPU_CLIENT_SINGLE_THREAD_CHECK(); | 
 |   GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glGetVertexAttribIuiv(" | 
 |       << index << ", " | 
 |       << GLES2Util::GetStringVertexAttribute(pname) << ", " | 
 |       << static_cast<const void*>(params) << ")"); | 
 |   uint32_t value = 0; | 
 |   if (vertex_array_object_manager_->GetVertexAttrib(index, pname, &value)) { | 
 |     *params = static_cast<GLuint>(value); | 
 |     return; | 
 |   } | 
 |   TRACE_EVENT0("gpu", "GLES2::GetVertexAttribIuiv"); | 
 |   typedef cmds::GetVertexAttribiv::Result Result; | 
 |   Result* result = GetResultAs<Result*>(); | 
 |   if (!result) { | 
 |     return; | 
 |   } | 
 |   result->SetNumResults(0); | 
 |   helper_->GetVertexAttribIuiv( | 
 |       index, pname, GetResultShmId(), GetResultShmOffset()); | 
 |   WaitForCmd(); | 
 |   result->CopyResult(params); | 
 |   GPU_CLIENT_LOG_CODE_BLOCK({ | 
 |     for (int32_t i = 0; i < result->GetNumResults(); ++i) { | 
 |       GPU_CLIENT_LOG("  " << i << ": " << result->GetData()[i]); | 
 |     } | 
 |   }); | 
 |   CheckGLError(); | 
 | } | 
 |  | 
 | GLenum GLES2Implementation::GetGraphicsResetStatusKHR() { | 
 |   GPU_CLIENT_SINGLE_THREAD_CHECK(); | 
 |   GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glGetGraphicsResetStatusKHR()"); | 
 |   // If any context (including ourselves) has seen itself become lost, | 
 |   // then it will have told the ShareGroup, so just report its status. | 
 |   if (share_group_->IsLost()) | 
 |     return GL_UNKNOWN_CONTEXT_RESET_KHR; | 
 |   return GL_NO_ERROR; | 
 | } | 
 |  | 
 | void GLES2Implementation::Swap() { | 
 |   SwapBuffers(); | 
 | } | 
 |  | 
 | void GLES2Implementation::PartialSwapBuffers(const gfx::Rect& sub_buffer) { | 
 |   PostSubBufferCHROMIUM( | 
 |       sub_buffer.x(), sub_buffer.y(), sub_buffer.width(), sub_buffer.height()); | 
 | } | 
 |  | 
 | void GLES2Implementation::CommitOverlayPlanes() { | 
 |   CommitOverlayPlanesCHROMIUM(); | 
 | } | 
 |  | 
 | static GLenum GetGLESOverlayTransform(gfx::OverlayTransform plane_transform) { | 
 |   switch (plane_transform) { | 
 |     case gfx::OVERLAY_TRANSFORM_INVALID: | 
 |       break; | 
 |     case gfx::OVERLAY_TRANSFORM_NONE: | 
 |       return GL_OVERLAY_TRANSFORM_NONE_CHROMIUM; | 
 |     case gfx::OVERLAY_TRANSFORM_FLIP_HORIZONTAL: | 
 |       return GL_OVERLAY_TRANSFORM_FLIP_HORIZONTAL_CHROMIUM; | 
 |     case gfx::OVERLAY_TRANSFORM_FLIP_VERTICAL: | 
 |       return GL_OVERLAY_TRANSFORM_FLIP_VERTICAL_CHROMIUM; | 
 |     case gfx::OVERLAY_TRANSFORM_ROTATE_90: | 
 |       return GL_OVERLAY_TRANSFORM_ROTATE_90_CHROMIUM; | 
 |     case gfx::OVERLAY_TRANSFORM_ROTATE_180: | 
 |       return GL_OVERLAY_TRANSFORM_ROTATE_180_CHROMIUM; | 
 |     case gfx::OVERLAY_TRANSFORM_ROTATE_270: | 
 |       return GL_OVERLAY_TRANSFORM_ROTATE_270_CHROMIUM; | 
 |   } | 
 |   NOTREACHED(); | 
 |   return GL_OVERLAY_TRANSFORM_NONE_CHROMIUM; | 
 | } | 
 |  | 
 | void GLES2Implementation::ScheduleOverlayPlane( | 
 |     int plane_z_order, | 
 |     gfx::OverlayTransform plane_transform, | 
 |     unsigned overlay_texture_id, | 
 |     const gfx::Rect& display_bounds, | 
 |     const gfx::RectF& uv_rect) { | 
 |   ScheduleOverlayPlaneCHROMIUM(plane_z_order, | 
 |                                GetGLESOverlayTransform(plane_transform), | 
 |                                overlay_texture_id, | 
 |                                display_bounds.x(), | 
 |                                display_bounds.y(), | 
 |                                display_bounds.width(), | 
 |                                display_bounds.height(), | 
 |                                uv_rect.x(), | 
 |                                uv_rect.y(), | 
 |                                uv_rect.width(), | 
 |                                uv_rect.height()); | 
 | } | 
 |  | 
 | void GLES2Implementation::ScheduleCALayerSharedStateCHROMIUM( | 
 |     GLfloat opacity, | 
 |     GLboolean is_clipped, | 
 |     const GLfloat* clip_rect, | 
 |     GLint sorting_context_id, | 
 |     const GLfloat* transform) { | 
 |   size_t shm_size = 20 * sizeof(GLfloat); | 
 |   ScopedTransferBufferPtr buffer(shm_size, helper_, transfer_buffer_); | 
 |   if (!buffer.valid() || buffer.size() < shm_size) { | 
 |     SetGLError(GL_OUT_OF_MEMORY, "GLES2::ScheduleCALayerSharedStateCHROMIUM", | 
 |                "out of memory"); | 
 |     return; | 
 |   } | 
 |   GLfloat* mem = static_cast<GLfloat*>(buffer.address()); | 
 |   memcpy(mem + 0, clip_rect, 4 * sizeof(GLfloat)); | 
 |   memcpy(mem + 4, transform, 16 * sizeof(GLfloat)); | 
 |   helper_->ScheduleCALayerSharedStateCHROMIUM(opacity, is_clipped, | 
 |                                               sorting_context_id, | 
 |                                               buffer.shm_id(), buffer.offset()); | 
 | } | 
 |  | 
 | void GLES2Implementation::ScheduleCALayerCHROMIUM(GLuint contents_texture_id, | 
 |                                                   const GLfloat* contents_rect, | 
 |                                                   GLuint background_color, | 
 |                                                   GLuint edge_aa_mask, | 
 |                                                   const GLfloat* bounds_rect, | 
 |                                                   GLuint filter) { | 
 |   size_t shm_size = 8 * sizeof(GLfloat); | 
 |   ScopedTransferBufferPtr buffer(shm_size, helper_, transfer_buffer_); | 
 |   if (!buffer.valid() || buffer.size() < shm_size) { | 
 |     SetGLError(GL_OUT_OF_MEMORY, "GLES2::ScheduleCALayerCHROMIUM", | 
 |                "out of memory"); | 
 |     return; | 
 |   } | 
 |   GLfloat* mem = static_cast<GLfloat*>(buffer.address()); | 
 |   memcpy(mem + 0, contents_rect, 4 * sizeof(GLfloat)); | 
 |   memcpy(mem + 4, bounds_rect, 4 * sizeof(GLfloat)); | 
 |   helper_->ScheduleCALayerCHROMIUM(contents_texture_id, background_color, | 
 |                                    edge_aa_mask, filter, buffer.shm_id(), | 
 |                                    buffer.offset()); | 
 | } | 
 |  | 
 | void GLES2Implementation::CommitOverlayPlanesCHROMIUM() { | 
 |   GPU_CLIENT_SINGLE_THREAD_CHECK(); | 
 |   GPU_CLIENT_LOG("[" << GetLogPrefix() << "] CommitOverlayPlanesCHROMIUM()"); | 
 |   TRACE_EVENT0("gpu", "GLES2::CommitOverlayPlanesCHROMIUM"); | 
 |  | 
 |   // Same flow control as GLES2Implementation::SwapBuffers (see comments there). | 
 |   swap_buffers_tokens_.push(helper_->InsertToken()); | 
 |   helper_->CommitOverlayPlanesCHROMIUM(); | 
 |   helper_->CommandBufferHelper::Flush(); | 
 |   if (swap_buffers_tokens_.size() > kMaxSwapBuffers + 1) { | 
 |     helper_->WaitForToken(swap_buffers_tokens_.front()); | 
 |     swap_buffers_tokens_.pop(); | 
 |   } | 
 | } | 
 |  | 
 | 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 != 0; | 
 | } | 
 |  | 
 | 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 (!ValidateSize("glMapBufferSubDataCHROMIUM", size) || | 
 |       !ValidateOffset("glMapBufferSubDataCHROMIUM", offset)) { | 
 |     return NULL; | 
 |   } | 
 |  | 
 |   int32_t 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))); | 
 |   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(); | 
 | } | 
 |  | 
 | GLuint GLES2Implementation::GetBoundBufferHelper(GLenum target) { | 
 |   GLenum binding = GLES2Util::MapBufferTargetToBindingEnum(target); | 
 |   GLint id = 0; | 
 |   bool cached = GetHelper(binding, &id); | 
 |   DCHECK(cached); | 
 |   return static_cast<GLuint>(id); | 
 | } | 
 |  | 
 | void GLES2Implementation::RemoveMappedBufferRangeByTarget(GLenum target) { | 
 |   GLuint buffer = GetBoundBufferHelper(target); | 
 |   RemoveMappedBufferRangeById(buffer); | 
 | } | 
 |  | 
 | void GLES2Implementation::RemoveMappedBufferRangeById(GLuint buffer) { | 
 |   if (buffer > 0) { | 
 |     auto iter = mapped_buffer_range_map_.find(buffer); | 
 |     if (iter != mapped_buffer_range_map_.end() && iter->second.shm_memory) { | 
 |       mapped_memory_->FreePendingToken( | 
 |           iter->second.shm_memory, helper_->InsertToken()); | 
 |       mapped_buffer_range_map_.erase(iter); | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | void GLES2Implementation::ClearMappedBufferRangeMap() { | 
 |   for (auto& buffer_range : mapped_buffer_range_map_) { | 
 |     if (buffer_range.second.shm_memory) { | 
 |       mapped_memory_->FreePendingToken( | 
 |           buffer_range.second.shm_memory, helper_->InsertToken()); | 
 |     } | 
 |   } | 
 |   mapped_buffer_range_map_.clear(); | 
 | } | 
 |  | 
 | void* GLES2Implementation::MapBufferRange( | 
 |     GLenum target, GLintptr offset, GLsizeiptr size, GLbitfield access) { | 
 |   GPU_CLIENT_SINGLE_THREAD_CHECK(); | 
 |   GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glMapBufferRange(" | 
 |       << GLES2Util::GetStringEnum(target) << ", " << offset << ", " | 
 |       << size << ", " << access << ")"); | 
 |   if (!ValidateSize("glMapBufferRange", size) || | 
 |       !ValidateOffset("glMapBufferRange", offset)) { | 
 |     return nullptr; | 
 |   } | 
 |  | 
 |   int32_t shm_id; | 
 |   unsigned int shm_offset; | 
 |   void* mem = mapped_memory_->Alloc(size, &shm_id, &shm_offset); | 
 |   if (!mem) { | 
 |     SetGLError(GL_OUT_OF_MEMORY, "glMapBufferRange", "out of memory"); | 
 |     return nullptr; | 
 |   } | 
 |  | 
 |   typedef cmds::MapBufferRange::Result Result; | 
 |   Result* result = GetResultAs<Result*>(); | 
 |   *result = 0; | 
 |   helper_->MapBufferRange(target, offset, size, access, shm_id, shm_offset, | 
 |                           GetResultShmId(), GetResultShmOffset()); | 
 |   // TODO(zmo): For write only mode with MAP_INVALID_*_BIT, we should | 
 |   // consider an early return without WaitForCmd(). crbug.com/465804. | 
 |   WaitForCmd(); | 
 |   if (*result) { | 
 |     const GLbitfield kInvalidateBits = | 
 |         GL_MAP_INVALIDATE_BUFFER_BIT | GL_MAP_INVALIDATE_RANGE_BIT; | 
 |     if ((access & kInvalidateBits) != 0) { | 
 |       // We do not read back from the buffer, therefore, we set the client | 
 |       // side memory to zero to avoid uninitialized data. | 
 |       memset(mem, 0, size); | 
 |     } | 
 |     GLuint buffer = GetBoundBufferHelper(target); | 
 |     DCHECK_NE(0u, buffer); | 
 |     // glMapBufferRange fails on an already mapped buffer. | 
 |     DCHECK(mapped_buffer_range_map_.find(buffer) == | 
 |            mapped_buffer_range_map_.end()); | 
 |     auto iter = mapped_buffer_range_map_.insert(std::make_pair( | 
 |         buffer, | 
 |         MappedBuffer(access, shm_id, mem, shm_offset, target, offset, size))); | 
 |     DCHECK(iter.second); | 
 |   } else { | 
 |     mapped_memory_->Free(mem); | 
 |     mem = nullptr; | 
 |   } | 
 |  | 
 |   GPU_CLIENT_LOG("  returned " << mem); | 
 |   CheckGLError(); | 
 |   return mem; | 
 | } | 
 |  | 
 | GLboolean GLES2Implementation::UnmapBuffer(GLenum target) { | 
 |   GPU_CLIENT_SINGLE_THREAD_CHECK(); | 
 |   GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glUnmapBuffer(" | 
 |       << GLES2Util::GetStringEnum(target) << ")"); | 
 |   switch (target) { | 
 |     case GL_ARRAY_BUFFER: | 
 |     case GL_ELEMENT_ARRAY_BUFFER: | 
 |     case GL_COPY_READ_BUFFER: | 
 |     case GL_COPY_WRITE_BUFFER: | 
 |     case GL_PIXEL_PACK_BUFFER: | 
 |     case GL_PIXEL_UNPACK_BUFFER: | 
 |     case GL_TRANSFORM_FEEDBACK_BUFFER: | 
 |     case GL_UNIFORM_BUFFER: | 
 |       break; | 
 |     default: | 
 |       SetGLError(GL_INVALID_ENUM, "glUnmapBuffer", "invalid target"); | 
 |       return GL_FALSE; | 
 |   } | 
 |   GLuint buffer = GetBoundBufferHelper(target); | 
 |   if (buffer == 0) { | 
 |     SetGLError(GL_INVALID_OPERATION, "glUnmapBuffer", "no buffer bound"); | 
 |     return GL_FALSE; | 
 |   } | 
 |   auto iter = mapped_buffer_range_map_.find(buffer); | 
 |   if (iter == mapped_buffer_range_map_.end()) { | 
 |     SetGLError(GL_INVALID_OPERATION, "glUnmapBuffer", "buffer is unmapped"); | 
 |     return GL_FALSE; | 
 |   } | 
 |  | 
 |   helper_->UnmapBuffer(target); | 
 |   RemoveMappedBufferRangeById(buffer); | 
 |   // TODO(zmo): There is a rare situation that data might be corrupted and | 
 |   // GL_FALSE should be returned. We lose context on that sitatuon, so we | 
 |   // don't have to WaitForCmd(). | 
 |   GPU_CLIENT_LOG("  returned " << GL_TRUE); | 
 |   CheckGLError(); | 
 |   return GL_TRUE; | 
 | } | 
 |  | 
 | 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_t size; | 
 |   if (!GLES2Util::ComputeImageDataSizes( | 
 |       width, height, 1, format, type, unpack_alignment_, &size, NULL, NULL)) { | 
 |     SetGLError( | 
 |         GL_INVALID_VALUE, "glMapTexSubImage2DCHROMIUM", "image size too large"); | 
 |     return NULL; | 
 |   } | 
 |   int32_t 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))); | 
 |   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, | 
 |                                          GLboolean alpha) { | 
 |   GPU_CLIENT_SINGLE_THREAD_CHECK(); | 
 |   GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glResizeCHROMIUM(" << width << ", " | 
 |                      << height << ", " << scale_factor << ", " << alpha << ")"); | 
 |   helper_->ResizeCHROMIUM(width, height, scale_factor, alpha); | 
 |   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. | 
 |     // TODO: Here we could save memory by defining RequestExtensions | 
 |     // invalidating the GL_EXTENSIONS string. http://crbug.com/586414 | 
 |     result = gl_strings_.insert(str).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 << ")"); | 
 |   InvalidateCachedExtensions(); | 
 |   SetBucketAsCString(kResultBucketId, extension); | 
 |   helper_->RequestExtensionCHROMIUM(kResultBucketId); | 
 |   helper_->SetBucketSize(kResultBucketId, 0); | 
 |  | 
 |   struct ExtensionCheck { | 
 |     const char* extension; | 
 |     ExtensionStatus* status; | 
 |   }; | 
 |   const ExtensionCheck checks[] = { | 
 |     { | 
 |       "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::GetProgramInfoCHROMIUMHelper( | 
 |     GLuint program, | 
 |     std::vector<int8_t>* result) { | 
 |   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. | 
 |   DCHECK_EQ(0, *size); | 
 |   std::vector<int8_t> 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()); | 
 | } | 
 |  | 
 | void GLES2Implementation::GetUniformBlocksCHROMIUMHelper( | 
 |     GLuint program, | 
 |     std::vector<int8_t>* result) { | 
 |   DCHECK(result); | 
 |   // Clear the bucket so if the command fails nothing will be in it. | 
 |   helper_->SetBucketSize(kResultBucketId, 0); | 
 |   helper_->GetUniformBlocksCHROMIUM(program, kResultBucketId); | 
 |   GetBucketContents(kResultBucketId, result); | 
 | } | 
 |  | 
 | void GLES2Implementation::GetUniformBlocksCHROMIUM( | 
 |     GLuint program, GLsizei bufsize, GLsizei* size, void* info) { | 
 |   GPU_CLIENT_SINGLE_THREAD_CHECK(); | 
 |   if (bufsize < 0) { | 
 |     SetGLError( | 
 |         GL_INVALID_VALUE, "glGetUniformBlocksCHROMIUM", "bufsize less than 0."); | 
 |     return; | 
 |   } | 
 |   if (size == NULL) { | 
 |     SetGLError(GL_INVALID_VALUE, "glGetUniformBlocksCHROMIUM", "size is null."); | 
 |     return; | 
 |   } | 
 |   // Make sure they've set size to 0 else the value will be undefined on | 
 |   // lost context. | 
 |   DCHECK_EQ(0, *size); | 
 |   std::vector<int8_t> result; | 
 |   GetUniformBlocksCHROMIUMHelper(program, &result); | 
 |   if (result.empty()) { | 
 |     return; | 
 |   } | 
 |   *size = result.size(); | 
 |   if (!info) { | 
 |     return; | 
 |   } | 
 |   if (static_cast<size_t>(bufsize) < result.size()) { | 
 |     SetGLError(GL_INVALID_OPERATION, "glGetUniformBlocksCHROMIUM", | 
 |                "bufsize is too small for result."); | 
 |     return; | 
 |   } | 
 |   memcpy(info, &result[0], result.size()); | 
 | } | 
 |  | 
 | void GLES2Implementation::GetUniformsES3CHROMIUMHelper( | 
 |     GLuint program, | 
 |     std::vector<int8_t>* result) { | 
 |   DCHECK(result); | 
 |   // Clear the bucket so if the command fails nothing will be in it. | 
 |   helper_->SetBucketSize(kResultBucketId, 0); | 
 |   helper_->GetUniformsES3CHROMIUM(program, kResultBucketId); | 
 |   GetBucketContents(kResultBucketId, result); | 
 | } | 
 |  | 
 | void GLES2Implementation::GetUniformsES3CHROMIUM( | 
 |     GLuint program, GLsizei bufsize, GLsizei* size, void* info) { | 
 |   GPU_CLIENT_SINGLE_THREAD_CHECK(); | 
 |   if (bufsize < 0) { | 
 |     SetGLError( | 
 |         GL_INVALID_VALUE, "glGetUniformsES3CHROMIUM", "bufsize less than 0."); | 
 |     return; | 
 |   } | 
 |   if (size == NULL) { | 
 |     SetGLError(GL_INVALID_VALUE, "glGetUniformsES3CHROMIUM", "size is null."); | 
 |     return; | 
 |   } | 
 |   // Make sure they've set size to 0 else the value will be undefined on | 
 |   // lost context. | 
 |   DCHECK_EQ(0, *size); | 
 |   std::vector<int8_t> result; | 
 |   GetUniformsES3CHROMIUMHelper(program, &result); | 
 |   if (result.empty()) { | 
 |     return; | 
 |   } | 
 |   *size = result.size(); | 
 |   if (!info) { | 
 |     return; | 
 |   } | 
 |   if (static_cast<size_t>(bufsize) < result.size()) { | 
 |     SetGLError(GL_INVALID_OPERATION, | 
 |                "glGetUniformsES3CHROMIUM", "bufsize is too small for result."); | 
 |     return; | 
 |   } | 
 |   memcpy(info, &result[0], result.size()); | 
 | } | 
 |  | 
 | void GLES2Implementation::GetTransformFeedbackVaryingsCHROMIUMHelper( | 
 |     GLuint program, | 
 |     std::vector<int8_t>* result) { | 
 |   DCHECK(result); | 
 |   // Clear the bucket so if the command fails nothing will be in it. | 
 |   helper_->SetBucketSize(kResultBucketId, 0); | 
 |   helper_->GetTransformFeedbackVaryingsCHROMIUM(program, kResultBucketId); | 
 |   GetBucketContents(kResultBucketId, result); | 
 | } | 
 |  | 
 | void GLES2Implementation::GetTransformFeedbackVaryingsCHROMIUM( | 
 |     GLuint program, GLsizei bufsize, GLsizei* size, void* info) { | 
 |   GPU_CLIENT_SINGLE_THREAD_CHECK(); | 
 |   if (bufsize < 0) { | 
 |     SetGLError(GL_INVALID_VALUE, "glGetTransformFeedbackVaryingsCHROMIUM", | 
 |                "bufsize less than 0."); | 
 |     return; | 
 |   } | 
 |   if (size == NULL) { | 
 |     SetGLError(GL_INVALID_VALUE, "glGetTransformFeedbackVaryingsCHROMIUM", | 
 |                "size is null."); | 
 |     return; | 
 |   } | 
 |   // Make sure they've set size to 0 else the value will be undefined on | 
 |   // lost context. | 
 |   DCHECK_EQ(0, *size); | 
 |   std::vector<int8_t> result; | 
 |   GetTransformFeedbackVaryingsCHROMIUMHelper(program, &result); | 
 |   if (result.empty()) { | 
 |     return; | 
 |   } | 
 |   *size = result.size(); | 
 |   if (!info) { | 
 |     return; | 
 |   } | 
 |   if (static_cast<size_t>(bufsize) < result.size()) { | 
 |     SetGLError(GL_INVALID_OPERATION, "glGetTransformFeedbackVaryingsCHROMIUM", | 
 |                "bufsize is too small for result."); | 
 |     return; | 
 |   } | 
 |   memcpy(info, &result[0], result.size()); | 
 | } | 
 |  | 
 | 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) { | 
 |   for (GLsizei ii = 0; ii < n; ++ii) { | 
 |     query_tracker_->RemoveQuery(queries[ii]); | 
 |     query_id_allocator_->FreeID(queries[ii]); | 
 |   } | 
 |  | 
 |   helper_->DeleteQueriesEXTImmediate(n, 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 << ")"); | 
 |  | 
 |   switch (target) { | 
 |     case GL_COMMANDS_ISSUED_CHROMIUM: | 
 |     case GL_LATENCY_QUERY_CHROMIUM: | 
 |     case GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM: | 
 |     case GL_GET_ERROR_QUERY_CHROMIUM: | 
 |       break; | 
 |     case GL_COMMANDS_COMPLETED_CHROMIUM: | 
 |       if (!capabilities_.sync_query) { | 
 |         SetGLError( | 
 |             GL_INVALID_OPERATION, "glBeginQueryEXT", | 
 |             "not enabled for commands completed queries"); | 
 |         return; | 
 |       } | 
 |       break; | 
 |     case GL_ANY_SAMPLES_PASSED: | 
 |     case GL_ANY_SAMPLES_PASSED_CONSERVATIVE: | 
 |       if (!capabilities_.occlusion_query_boolean) { | 
 |         SetGLError( | 
 |             GL_INVALID_OPERATION, "glBeginQueryEXT", | 
 |             "not enabled for occlusion queries"); | 
 |         return; | 
 |       } | 
 |       break; | 
 |     case GL_TIME_ELAPSED_EXT: | 
 |       if (!capabilities_.timer_queries) { | 
 |         SetGLError( | 
 |             GL_INVALID_OPERATION, "glBeginQueryEXT", | 
 |             "not enabled for timing queries"); | 
 |         return; | 
 |       } | 
 |       break; | 
 |     case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN: | 
 |       if (capabilities_.major_version >= 3) | 
 |         break; | 
 |       // Fall through | 
 |     default: | 
 |       SetGLError( | 
 |           GL_INVALID_ENUM, "glBeginQueryEXT", "unknown query target"); | 
 |       return; | 
 |   } | 
 |  | 
 |   // if any outstanding queries INV_OP | 
 |   if (query_tracker_->GetCurrentQuery(target)) { | 
 |     SetGLError( | 
 |         GL_INVALID_OPERATION, "glBeginQueryEXT", "query already in progress"); | 
 |     return; | 
 |   } | 
 |  | 
 |   if (id == 0) { | 
 |     SetGLError(GL_INVALID_OPERATION, "glBeginQueryEXT", "id is 0"); | 
 |     return; | 
 |   } | 
 |  | 
 |   if (!query_id_allocator_->InUse(id)) { | 
 |     SetGLError(GL_INVALID_OPERATION, "glBeginQueryEXT", "invalid id"); | 
 |     return; | 
 |   } | 
 |  | 
 |   // Extra setups some targets might need. | 
 |   switch (target) { | 
 |     case GL_TIME_ELAPSED_EXT: | 
 |       if (!query_tracker_->SetDisjointSync(this)) { | 
 |         SetGLError(GL_OUT_OF_MEMORY, | 
 |                    "glBeginQueryEXT", | 
 |                    "buffer allocation failed"); | 
 |         return; | 
 |       } | 
 |       break; | 
 |     default: | 
 |       break; | 
 |   } | 
 |  | 
 |   if (query_tracker_->BeginQuery(id, target, this)) | 
 |     CheckGLError(); | 
 | } | 
 |  | 
 | void GLES2Implementation::EndQueryEXT(GLenum target) { | 
 |   GPU_CLIENT_SINGLE_THREAD_CHECK(); | 
 |   GPU_CLIENT_LOG("[" << GetLogPrefix() << "] EndQueryEXT(" | 
 |                  << GLES2Util::GetStringQueryTarget(target) << ")"); | 
 |   if (query_tracker_->EndQuery(target, this)) | 
 |     CheckGLError(); | 
 | } | 
 |  | 
 | void GLES2Implementation::QueryCounterEXT(GLuint id, GLenum target) { | 
 |   GPU_CLIENT_SINGLE_THREAD_CHECK(); | 
 |   GPU_CLIENT_LOG("[" << GetLogPrefix() << "] QueryCounterEXT(" | 
 |                  << id | 
 |                  << ", " << GLES2Util::GetStringQueryTarget(target) << ")"); | 
 |  | 
 |   switch (target) { | 
 |     case GL_TIMESTAMP_EXT: | 
 |       if (!capabilities_.timer_queries) { | 
 |         SetGLError( | 
 |             GL_INVALID_OPERATION, "glQueryCounterEXT", | 
 |             "not enabled for timing queries"); | 
 |         return; | 
 |       } | 
 |       break; | 
 |     default: | 
 |       SetGLError( | 
 |           GL_INVALID_ENUM, "glQueryCounterEXT", "unknown query target"); | 
 |       return; | 
 |   } | 
 |  | 
 |   if (id == 0) { | 
 |     SetGLError(GL_INVALID_OPERATION, "glQueryCounterEXT", "id is 0"); | 
 |     return; | 
 |   } | 
 |  | 
 |   if (!query_id_allocator_->InUse(id)) { | 
 |     SetGLError(GL_INVALID_OPERATION, "glQueryCounterEXT", "invalid id"); | 
 |     return; | 
 |   } | 
 |  | 
 |   // Extra setups some targets might need. | 
 |   switch (target) { | 
 |     case GL_TIMESTAMP_EXT: | 
 |       if (!query_tracker_->SetDisjointSync(this)) { | 
 |         SetGLError(GL_OUT_OF_MEMORY, | 
 |                    "glQueryCounterEXT", | 
 |                    "buffer allocation failed"); | 
 |         return; | 
 |       } | 
 |       break; | 
 |     default: | 
 |       break; | 
 |   } | 
 |  | 
 |   if (query_tracker_->QueryCounter(id, target, this)) | 
 |     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_QUERY_COUNTER_BITS_EXT) { | 
 |     switch (target) { | 
 |       case GL_TIMESTAMP_EXT: | 
 |         // Overall reliable driver support for timestamps is limited, so we | 
 |         // disable the timestamp portion of this extension to encourage use of | 
 |         // the better supported time elapsed queries. | 
 |         *params = 0; | 
 |         break; | 
 |       case GL_TIME_ELAPSED_EXT: | 
 |         // We convert all queries to CPU time so we support 64 bits. | 
 |         *params = 64; | 
 |         break; | 
 |       default: | 
 |         SetGLErrorInvalidEnum("glGetQueryivEXT", target, "target"); | 
 |         break; | 
 |     } | 
 |     return; | 
 |   } else if (pname != GL_CURRENT_QUERY_EXT) { | 
 |     SetGLErrorInvalidEnum("glGetQueryivEXT", pname, "pname"); | 
 |     return; | 
 |   } | 
 |   QueryTracker::Query* query = query_tracker_->GetCurrentQuery(target); | 
 |   *params = query ? query->id() : 0; | 
 |   GPU_CLIENT_LOG("  " << *params); | 
 |   CheckGLError(); | 
 | } | 
 |  | 
 | void GLES2Implementation::GetQueryObjectivEXT( | 
 |     GLuint id, GLenum pname, GLint* params) { | 
 |   GLuint64 result = 0; | 
 |   if (GetQueryObjectValueHelper("glGetQueryObjectivEXT", id, pname, &result)) | 
 |     *params = base::saturated_cast<GLint>(result); | 
 | } | 
 |  | 
 | void GLES2Implementation::GetQueryObjectuivEXT( | 
 |     GLuint id, GLenum pname, GLuint* params) { | 
 |   GLuint64 result = 0; | 
 |   if (GetQueryObjectValueHelper("glGetQueryObjectuivEXT", id, pname, &result)) | 
 |     *params = base::saturated_cast<GLuint>(result); | 
 | } | 
 |  | 
 | void GLES2Implementation::GetQueryObjecti64vEXT( | 
 |     GLuint id, GLenum pname, GLint64* params) { | 
 |   GLuint64 result = 0; | 
 |   if (GetQueryObjectValueHelper("glGetQueryObjectiv64vEXT", id, pname, &result)) | 
 |     *params = base::saturated_cast<GLint64>(result); | 
 | } | 
 |  | 
 | void GLES2Implementation::GetQueryObjectui64vEXT( | 
 |     GLuint id, GLenum pname, GLuint64* params) { | 
 |   GLuint64 result = 0; | 
 |   if (GetQueryObjectValueHelper("glGetQueryObjectui64vEXT", id, pname, &result)) | 
 |     *params = result; | 
 | } | 
 |  | 
 | void GLES2Implementation::SetDisjointValueSyncCHROMIUM() { | 
 |   query_tracker_->SetDisjointSync(this); | 
 | } | 
 |  | 
 | 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_->SupportsClientSideBuffers()) { | 
 |     GLsizei num_elements; | 
 |     SafeAddInt32(first, count, &num_elements); | 
 |     if (!vertex_array_object_manager_->SetupSimulatedClientSideBuffers( | 
 |             "glDrawArraysInstancedANGLE", this, helper_, num_elements, | 
 |             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; | 
 |   } | 
 |   if (vertex_array_object_manager_->bound_element_array_buffer() != 0 && | 
 |       !ValidateOffset("glDrawElementsInstancedANGLE", | 
 |                       reinterpret_cast<GLintptr>(indices))) { | 
 |     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"); | 
 |  | 
 |   gpu::Mailbox result = gpu::Mailbox::Generate(); | 
 |   memcpy(mailbox, result.name, sizeof(result.name)); | 
 | } | 
 |  | 
 | void GLES2Implementation::ProduceTextureCHROMIUM(GLenum target, | 
 |                                                  const GLbyte* data) { | 
 |   GPU_CLIENT_SINGLE_THREAD_CHECK(); | 
 |   GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glProduceTextureCHROMIUM(" | 
 |                      << static_cast<const void*>(data) << ")"); | 
 |   const Mailbox& mailbox = *reinterpret_cast<const Mailbox*>(data); | 
 |   DCHECK(mailbox.Verify()) << "ProduceTextureCHROMIUM was passed a " | 
 |                               "mailbox that was not generated by " | 
 |                               "GenMailboxCHROMIUM."; | 
 |   helper_->ProduceTextureCHROMIUMImmediate(target, data); | 
 |   CheckGLError(); | 
 | } | 
 |  | 
 | void GLES2Implementation::ProduceTextureDirectCHROMIUM( | 
 |     GLuint texture, GLenum target, const GLbyte* data) { | 
 |   GPU_CLIENT_SINGLE_THREAD_CHECK(); | 
 |   GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glProduceTextureDirectCHROMIUM(" | 
 |                      << static_cast<const void*>(data) << ")"); | 
 |   const Mailbox& mailbox = *reinterpret_cast<const Mailbox*>(data); | 
 |   DCHECK(mailbox.Verify()) << "ProduceTextureDirectCHROMIUM was passed a " | 
 |                               "mailbox that was not generated by " | 
 |                               "GenMailboxCHROMIUM."; | 
 |   helper_->ProduceTextureDirectCHROMIUMImmediate(texture, target, data); | 
 |   CheckGLError(); | 
 | } | 
 |  | 
 | void GLES2Implementation::ConsumeTextureCHROMIUM(GLenum target, | 
 |                                                  const GLbyte* data) { | 
 |   GPU_CLIENT_SINGLE_THREAD_CHECK(); | 
 |   GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glConsumeTextureCHROMIUM(" | 
 |                      << static_cast<const void*>(data) << ")"); | 
 |   const Mailbox& mailbox = *reinterpret_cast<const Mailbox*>(data); | 
 |   DCHECK(mailbox.Verify()) << "ConsumeTextureCHROMIUM was passed a " | 
 |                               "mailbox that was not generated by " | 
 |                               "GenMailboxCHROMIUM."; | 
 |   helper_->ConsumeTextureCHROMIUMImmediate(target, data); | 
 |   CheckGLError(); | 
 | } | 
 |  | 
 | GLuint GLES2Implementation::CreateAndConsumeTextureCHROMIUM( | 
 |     GLenum target, const GLbyte* data) { | 
 |   GPU_CLIENT_SINGLE_THREAD_CHECK(); | 
 |   GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glCreateAndConsumeTextureCHROMIUM(" | 
 |                      << static_cast<const void*>(data) << ")"); | 
 |   const Mailbox& mailbox = *reinterpret_cast<const Mailbox*>(data); | 
 |   DCHECK(mailbox.Verify()) << "CreateAndConsumeTextureCHROMIUM was passed a " | 
 |                               "mailbox that was not generated by " | 
 |                               "GenMailboxCHROMIUM."; | 
 |   GLuint client_id; | 
 |   GetIdHandler(id_namespaces::kTextures)->MakeIds(this, 0, 1, &client_id); | 
 |   helper_->CreateAndConsumeTextureINTERNALImmediate(target, | 
 |       client_id, data); | 
 |   if (share_group_->bind_generates_resource()) | 
 |     helper_->CommandBufferHelper::Flush(); | 
 |   CheckGLError(); | 
 |   return client_id; | 
 | } | 
 |  | 
 | 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* category_name, const char* trace_name) { | 
 |   GPU_CLIENT_SINGLE_THREAD_CHECK(); | 
 |   GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glTraceBeginCHROMIUM(" | 
 |                  << category_name << ", " << trace_name << ")"); | 
 |   SetBucketAsCString(kResultBucketId, category_name); | 
 |   SetBucketAsCString(kResultBucketId + 1, trace_name); | 
 |   helper_->TraceBeginCHROMIUM(kResultBucketId, kResultBucketId + 1); | 
 |   helper_->SetBucketSize(kResultBucketId, 0); | 
 |   helper_->SetBucketSize(kResultBucketId + 1, 0); | 
 |   current_trace_stack_++; | 
 | } | 
 |  | 
 | void GLES2Implementation::TraceEndCHROMIUM() { | 
 |   GPU_CLIENT_SINGLE_THREAD_CHECK(); | 
 |   GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glTraceEndCHROMIUM(" << ")"); | 
 |   if (current_trace_stack_ == 0) { | 
 |     SetGLError(GL_INVALID_OPERATION, "glTraceEndCHROMIUM", | 
 |                "missing begin trace"); | 
 |     return; | 
 |   } | 
 |   helper_->TraceEndCHROMIUM(); | 
 |   current_trace_stack_--; | 
 | } | 
 |  | 
 | 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; | 
 |     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. | 
 |   if (buffer->last_usage_token()) { | 
 |     helper_->WaitForToken(buffer->last_usage_token()); | 
 |     buffer->set_last_usage_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; | 
 | } | 
 |  | 
 | uint64_t GLES2Implementation::ShareGroupTracingGUID() const { | 
 |   return share_group_->TracingGUID(); | 
 | } | 
 |  | 
 | void GLES2Implementation::SetErrorMessageCallback( | 
 |     const base::Callback<void(const char*, int32_t)>& callback) { | 
 |   error_message_callback_ = callback; | 
 | } | 
 |  | 
 | void GLES2Implementation::SetLostContextCallback( | 
 |     const base::Closure& callback) { | 
 |   lost_context_callback_ = callback; | 
 | } | 
 |  | 
 | GLuint64 GLES2Implementation::InsertFenceSyncCHROMIUM() { | 
 |   const uint64_t release = gpu_control_->GenerateFenceSyncRelease(); | 
 |   helper_->InsertFenceSyncCHROMIUM(release); | 
 |   return release; | 
 | } | 
 |  | 
 | void GLES2Implementation::GenSyncTokenCHROMIUM(GLuint64 fence_sync, | 
 |                                                GLbyte* sync_token) { | 
 |   if (!sync_token) { | 
 |     SetGLError(GL_INVALID_VALUE, "glGenSyncTokenCHROMIUM", "empty sync_token"); | 
 |     return; | 
 |   } else if (!gpu_control_->IsFenceSyncRelease(fence_sync)) { | 
 |     SetGLError(GL_INVALID_VALUE, "glGenSyncTokenCHROMIUM", | 
 |                "invalid fence sync"); | 
 |     return; | 
 |   } else if (!gpu_control_->IsFenceSyncFlushReceived(fence_sync)) { | 
 |     SetGLError(GL_INVALID_OPERATION, "glGenSyncTokenCHROMIUM", | 
 |                "fence sync must be flushed before generating sync token"); | 
 |     return; | 
 |   } | 
 |  | 
 |   // Copy the data over after setting the data to ensure alignment. | 
 |   SyncToken sync_token_data(gpu_control_->GetNamespaceID(), | 
 |                             gpu_control_->GetExtraCommandBufferData(), | 
 |                             gpu_control_->GetCommandBufferID(), fence_sync); | 
 |   sync_token_data.SetVerifyFlush(); | 
 |   memcpy(sync_token, &sync_token_data, sizeof(sync_token_data)); | 
 | } | 
 |  | 
 | void GLES2Implementation::GenUnverifiedSyncTokenCHROMIUM(GLuint64 fence_sync, | 
 |                                                          GLbyte* sync_token) { | 
 |   if (!sync_token) { | 
 |     SetGLError(GL_INVALID_VALUE, "glGenNonFlushedSyncTokenCHROMIUM", | 
 |                "empty sync_token"); | 
 |     return; | 
 |   } else if (!gpu_control_->IsFenceSyncRelease(fence_sync)) { | 
 |     SetGLError(GL_INVALID_VALUE, "glGenNonFlushedSyncTokenCHROMIUM", | 
 |                "invalid fence sync"); | 
 |     return; | 
 |   } else if (!gpu_control_->IsFenceSyncFlushed(fence_sync)) { | 
 |     SetGLError(GL_INVALID_OPERATION, "glGenSyncTokenCHROMIUM", | 
 |                "fence sync must be flushed before generating sync token"); | 
 |     return; | 
 |   } | 
 |  | 
 |   // Copy the data over after setting the data to ensure alignment. | 
 |   SyncToken sync_token_data(gpu_control_->GetNamespaceID(), | 
 |                             gpu_control_->GetExtraCommandBufferData(), | 
 |                             gpu_control_->GetCommandBufferID(), fence_sync); | 
 |   memcpy(sync_token, &sync_token_data, sizeof(sync_token_data)); | 
 | } | 
 |  | 
 | void GLES2Implementation::VerifySyncTokensCHROMIUM(GLbyte **sync_tokens, | 
 |                                                    GLsizei count) { | 
 |   bool requires_synchronization = false; | 
 |   for (GLsizei i = 0; i < count; ++i) { | 
 |     if (sync_tokens[i]) { | 
 |       SyncToken sync_token; | 
 |       memcpy(&sync_token, sync_tokens[i], sizeof(sync_token)); | 
 |  | 
 |       if (sync_token.HasData() && !sync_token.verified_flush()) { | 
 |         if (!gpu_control_->CanWaitUnverifiedSyncToken(&sync_token)) { | 
 |           SetGLError(GL_INVALID_VALUE, "glVerifySyncTokensCHROMIUM", | 
 |                      "Cannot verify sync token using this context."); | 
 |           return; | 
 |         } | 
 |         requires_synchronization = true; | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   // This step must be done after all unverified tokens have finished processing | 
 |   // CanWaitUnverifiedSyncToken(), command buffers use that to do any necessary | 
 |   // flushes. | 
 |   if (requires_synchronization) { | 
 |     // Make sure we have no pending ordering barriers by flushing now. | 
 |     FlushHelper(); | 
 |  | 
 |     // Ensure all the fence syncs are visible on GPU service. | 
 |     gpu_control_->EnsureWorkVisible(); | 
 |  | 
 |     // We can automatically mark everything as verified now. | 
 |     for (GLsizei i = 0; i < count; ++i) { | 
 |       if (sync_tokens[i]) { | 
 |         SyncToken sync_token; | 
 |         memcpy(&sync_token, sync_tokens[i], sizeof(sync_token)); | 
 |         if (sync_token.HasData() && !sync_token.verified_flush()) { | 
 |           sync_token.SetVerifyFlush(); | 
 |           memcpy(sync_tokens[i], &sync_token, sizeof(sync_token)); | 
 |         } | 
 |       } | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | void GLES2Implementation::WaitSyncTokenCHROMIUM(const GLbyte* sync_token) { | 
 |   if (sync_token) { | 
 |     // Copy the data over before data access to ensure alignment. | 
 |     SyncToken sync_token_data; | 
 |     memcpy(&sync_token_data, sync_token, sizeof(SyncToken)); | 
 |     if (sync_token_data.HasData()) { | 
 |       if (!sync_token_data.verified_flush() && | 
 |           !gpu_control_->CanWaitUnverifiedSyncToken(&sync_token_data)) { | 
 |         SetGLError(GL_INVALID_VALUE, "glWaitSyncTokenCHROMIUM", | 
 |                    "Cannot wait on sync_token which has not been verified"); | 
 |         return; | 
 |       } | 
 |  | 
 |       helper_->WaitSyncTokenCHROMIUM( | 
 |           static_cast<GLint>(sync_token_data.namespace_id()), | 
 |           sync_token_data.command_buffer_id().GetUnsafeValue(), | 
 |           sync_token_data.release_count()); | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | namespace { | 
 |  | 
 | bool CreateImageValidInternalFormat(GLenum internalformat, | 
 |                                     const Capabilities& capabilities) { | 
 |   switch (internalformat) { | 
 |     case GL_ATC_RGB_AMD: | 
 |     case GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD: | 
 |       return capabilities.texture_format_atc; | 
 |     case GL_COMPRESSED_RGB_S3TC_DXT1_EXT: | 
 |       return capabilities.texture_format_dxt1; | 
 |     case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: | 
 |       return capabilities.texture_format_dxt5; | 
 |     case GL_ETC1_RGB8_OES: | 
 |       return capabilities.texture_format_etc1; | 
 |     case GL_RED: | 
 |     case GL_RGB: | 
 |     case GL_RGBA: | 
 |     case GL_RGB_YCBCR_422_CHROMIUM: | 
 |     case GL_RGB_YCBCR_420V_CHROMIUM: | 
 |     case GL_RGB_YCRCB_420_CHROMIUM: | 
 |     case GL_BGRA_EXT: | 
 |       return true; | 
 |     default: | 
 |       return false; | 
 |   } | 
 | } | 
 |  | 
 | bool CreateGpuMemoryBufferValidInternalFormat(GLenum internalformat) { | 
 |   switch (internalformat) { | 
 |     case GL_RGB: | 
 |     case GL_RGBA: | 
 |       return true; | 
 |     default: | 
 |       return false; | 
 |   } | 
 | } | 
 |  | 
 | bool ValidImageUsage(GLenum usage) { | 
 |   return usage == GL_READ_WRITE_CHROMIUM; | 
 | } | 
 |  | 
 | }  // namespace | 
 |  | 
 | GLuint GLES2Implementation::CreateImageCHROMIUMHelper(ClientBuffer buffer, | 
 |                                                       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; | 
 |   } | 
 |  | 
 |   if (!CreateImageValidInternalFormat(internalformat, capabilities_)) { | 
 |     SetGLError(GL_INVALID_VALUE, "glCreateImageCHROMIUM", "invalid format"); | 
 |     return 0; | 
 |   } | 
 |  | 
 |   // CreateImage creates a fence sync so we must flush first to ensure all | 
 |   // previously created fence syncs are flushed first. | 
 |   FlushHelper(); | 
 |  | 
 |   int32_t image_id = | 
 |       gpu_control_->CreateImage(buffer, width, height, internalformat); | 
 |   if (image_id < 0) { | 
 |     SetGLError(GL_OUT_OF_MEMORY, "glCreateImageCHROMIUM", "image_id < 0"); | 
 |     return 0; | 
 |   } | 
 |   return image_id; | 
 | } | 
 |  | 
 | GLuint GLES2Implementation::CreateImageCHROMIUM(ClientBuffer buffer, | 
 |                                                 GLsizei width, | 
 |                                                 GLsizei height, | 
 |                                                 GLenum internalformat) { | 
 |   GPU_CLIENT_SINGLE_THREAD_CHECK(); | 
 |   GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glCreateImageCHROMIUM(" << width | 
 |                      << ", " << height << ", " | 
 |                      << GLES2Util::GetStringImageInternalFormat(internalformat) | 
 |                      << ")"); | 
 |   GLuint image_id = | 
 |       CreateImageCHROMIUMHelper(buffer, width, height, internalformat); | 
 |   CheckGLError(); | 
 |   return image_id; | 
 | } | 
 |  | 
 | void GLES2Implementation::DestroyImageCHROMIUMHelper(GLuint image_id) { | 
 |   // 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_control_->DestroyImage(image_id); | 
 | } | 
 |  | 
 | void GLES2Implementation::DestroyImageCHROMIUM(GLuint image_id) { | 
 |   GPU_CLIENT_SINGLE_THREAD_CHECK(); | 
 |   GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glDestroyImageCHROMIUM(" | 
 |       << image_id << ")"); | 
 |   DestroyImageCHROMIUMHelper(image_id); | 
 |   CheckGLError(); | 
 | } | 
 |  | 
 | GLuint GLES2Implementation::CreateGpuMemoryBufferImageCHROMIUMHelper( | 
 |     GLsizei width, | 
 |     GLsizei height, | 
 |     GLenum internalformat, | 
 |     GLenum usage) { | 
 |   if (width <= 0) { | 
 |     SetGLError( | 
 |         GL_INVALID_VALUE, "glCreateGpuMemoryBufferImageCHROMIUM", "width <= 0"); | 
 |     return 0; | 
 |   } | 
 |  | 
 |   if (height <= 0) { | 
 |     SetGLError(GL_INVALID_VALUE, | 
 |                "glCreateGpuMemoryBufferImageCHROMIUM", | 
 |                "height <= 0"); | 
 |     return 0; | 
 |   } | 
 |  | 
 |   if (!CreateGpuMemoryBufferValidInternalFormat(internalformat)) { | 
 |     SetGLError(GL_INVALID_VALUE, | 
 |                "glCreateGpuMemoryBufferImageCHROMIUM", | 
 |                "invalid format"); | 
 |     return 0; | 
 |   } | 
 |  | 
 |   if (!ValidImageUsage(usage)) { | 
 |     SetGLError(GL_INVALID_VALUE, | 
 |                "glCreateGpuMemoryBufferImageCHROMIUM", | 
 |                "invalid usage"); | 
 |     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(); | 
 |   int32_t image_id = gpu_control_->CreateGpuMemoryBufferImage( | 
 |       width, height, internalformat, usage); | 
 |   if (image_id < 0) { | 
 |     SetGLError(GL_OUT_OF_MEMORY, | 
 |                "glCreateGpuMemoryBufferImageCHROMIUM", | 
 |                "image_id < 0"); | 
 |     return 0; | 
 |   } | 
 |   return image_id; | 
 | } | 
 |  | 
 | GLuint GLES2Implementation::CreateGpuMemoryBufferImageCHROMIUM( | 
 |     GLsizei width, | 
 |     GLsizei height, | 
 |     GLenum internalformat, | 
 |     GLenum usage) { | 
 |   GPU_CLIENT_SINGLE_THREAD_CHECK(); | 
 |   GPU_CLIENT_LOG("[" << GetLogPrefix() | 
 |                      << "] glCreateGpuMemoryBufferImageCHROMIUM(" << width | 
 |                      << ", " << height << ", " | 
 |                      << GLES2Util::GetStringImageInternalFormat(internalformat) | 
 |                      << ", " << GLES2Util::GetStringImageUsage(usage) << ")"); | 
 |   GLuint image_id = CreateGpuMemoryBufferImageCHROMIUMHelper( | 
 |       width, height, internalformat, usage); | 
 |   CheckGLError(); | 
 |   return image_id; | 
 | } | 
 |  | 
 | bool GLES2Implementation::ValidateSize(const char* func, GLsizeiptr size) { | 
 |   if (size < 0) { | 
 |     SetGLError(GL_INVALID_VALUE, func, "size < 0"); | 
 |     return false; | 
 |   } | 
 |   if (!base::IsValueInRangeForNumericType<int32_t>(size)) { | 
 |     SetGLError(GL_INVALID_OPERATION, func, "size more than 32-bit"); | 
 |     return false; | 
 |   } | 
 |   return true; | 
 | } | 
 |  | 
 | bool GLES2Implementation::ValidateOffset(const char* func, GLintptr offset) { | 
 |   if (offset < 0) { | 
 |     SetGLError(GL_INVALID_VALUE, func, "offset < 0"); | 
 |     return false; | 
 |   } | 
 |   if (!base::IsValueInRangeForNumericType<int32_t>(offset)) { | 
 |     SetGLError(GL_INVALID_OPERATION, func, "offset more than 32-bit"); | 
 |     return false; | 
 |   } | 
 |   return true; | 
 | } | 
 |  | 
 | bool GLES2Implementation::GetSamplerParameterfvHelper( | 
 |     GLuint /* sampler */, GLenum /* pname */, GLfloat* /* params */) { | 
 |   // TODO(zmo): Implement client side caching. | 
 |   return false; | 
 | } | 
 |  | 
 | bool GLES2Implementation::GetSamplerParameterivHelper( | 
 |     GLuint /* sampler */, GLenum /* pname */, GLint* /* params */) { | 
 |   // TODO(zmo): Implement client side caching. | 
 |   return false; | 
 | } | 
 |  | 
 | bool GLES2Implementation::PackStringsToBucket(GLsizei count, | 
 |                                               const char* const* str, | 
 |                                               const GLint* length, | 
 |                                               const char* func_name) { | 
 |   DCHECK_LE(0, count); | 
 |   // Compute the total size. | 
 |   base::CheckedNumeric<size_t> total_size = count; | 
 |   total_size += 1; | 
 |   total_size *= sizeof(GLint); | 
 |   if (!total_size.IsValid()) { | 
 |     SetGLError(GL_INVALID_VALUE, func_name, "overflow"); | 
 |     return false; | 
 |   } | 
 |   size_t header_size = total_size.ValueOrDefault(0); | 
 |   std::vector<GLint> header(count + 1); | 
 |   header[0] = static_cast<GLint>(count); | 
 |   for (GLsizei ii = 0; ii < count; ++ii) { | 
 |     GLint len = 0; | 
 |     if (str[ii]) { | 
 |       len = (length && length[ii] >= 0) | 
 |                 ? length[ii] | 
 |                 : base::checked_cast<GLint>(strlen(str[ii])); | 
 |     } | 
 |     total_size += len; | 
 |     total_size += 1;  // NULL at the end of each char array. | 
 |     if (!total_size.IsValid()) { | 
 |       SetGLError(GL_INVALID_VALUE, func_name, "overflow"); | 
 |       return false; | 
 |     } | 
 |     header[ii + 1] = len; | 
 |   } | 
 |   // Pack data into a bucket on the service. | 
 |   helper_->SetBucketSize(kResultBucketId, total_size.ValueOrDefault(0)); | 
 |   size_t offset = 0; | 
 |   for (GLsizei ii = 0; ii <= count; ++ii) { | 
 |     const char* src = | 
 |         (ii == 0) ? reinterpret_cast<const char*>(&header[0]) : str[ii - 1]; | 
 |     base::CheckedNumeric<size_t> checked_size = | 
 |         (ii == 0) ? header_size : static_cast<size_t>(header[ii]); | 
 |     if (ii > 0) { | 
 |       checked_size += 1;  // NULL in the end. | 
 |     } | 
 |     if (!checked_size.IsValid()) { | 
 |       SetGLError(GL_INVALID_VALUE, func_name, "overflow"); | 
 |       return false; | 
 |     } | 
 |     size_t size = checked_size.ValueOrDefault(0); | 
 |     while (size) { | 
 |       ScopedTransferBufferPtr buffer(size, helper_, transfer_buffer_); | 
 |       if (!buffer.valid() || buffer.size() == 0) { | 
 |         SetGLError(GL_OUT_OF_MEMORY, func_name, "too large"); | 
 |         return false; | 
 |       } | 
 |       size_t copy_size = buffer.size(); | 
 |       if (ii > 0 && buffer.size() == size) | 
 |         --copy_size; | 
 |       if (copy_size) | 
 |         memcpy(buffer.address(), src, copy_size); | 
 |       if (copy_size < buffer.size()) { | 
 |         // Append NULL in the end. | 
 |         DCHECK(copy_size + 1 == buffer.size()); | 
 |         char* str = reinterpret_cast<char*>(buffer.address()); | 
 |         str[copy_size] = 0; | 
 |       } | 
 |       helper_->SetBucketData(kResultBucketId, offset, buffer.size(), | 
 |                              buffer.shm_id(), buffer.offset()); | 
 |       offset += buffer.size(); | 
 |       src += buffer.size(); | 
 |       size -= buffer.size(); | 
 |     } | 
 |   } | 
 |   DCHECK_EQ(total_size.ValueOrDefault(0), offset); | 
 |   return true; | 
 | } | 
 |  | 
 | void GLES2Implementation::UniformBlockBinding(GLuint program, | 
 |                                               GLuint index, | 
 |                                               GLuint binding) { | 
 |   GPU_CLIENT_SINGLE_THREAD_CHECK(); | 
 |   GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glUniformBlockBinding(" << program | 
 |                      << ", " << index << ", " << binding << ")"); | 
 |   share_group_->program_info_manager()->UniformBlockBinding( | 
 |       this, program, index, binding); | 
 |   helper_->UniformBlockBinding(program, index, binding); | 
 |   CheckGLError(); | 
 | } | 
 |  | 
 | GLenum GLES2Implementation::ClientWaitSync( | 
 |     GLsync sync, GLbitfield flags, GLuint64 timeout) { | 
 |   GPU_CLIENT_SINGLE_THREAD_CHECK(); | 
 |   GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glClientWaitSync(" << sync | 
 |                  << ", " << flags << ", " << timeout << ")"); | 
 |   typedef cmds::ClientWaitSync::Result Result; | 
 |   Result* result = GetResultAs<Result*>(); | 
 |   if (!result) { | 
 |     SetGLError(GL_OUT_OF_MEMORY, "ClientWaitSync", ""); | 
 |     return GL_WAIT_FAILED; | 
 |   } | 
 |   *result = GL_WAIT_FAILED; | 
 |   helper_->ClientWaitSync( | 
 |       ToGLuint(sync), flags, timeout, GetResultShmId(), GetResultShmOffset()); | 
 |   WaitForCmd(); | 
 |   GPU_CLIENT_LOG("returned " << *result); | 
 |   CheckGLError(); | 
 |   return *result; | 
 | } | 
 |  | 
 | void GLES2Implementation::WaitSync( | 
 |     GLsync sync, GLbitfield flags, GLuint64 timeout) { | 
 |   GPU_CLIENT_SINGLE_THREAD_CHECK(); | 
 |   GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glWaitSync(" << sync << ", " | 
 |                  << flags << ", " << timeout << ")"); | 
 |   helper_->WaitSync(ToGLuint(sync), flags, timeout); | 
 |   CheckGLError(); | 
 | } | 
 |  | 
 | void GLES2Implementation::GetInternalformativ( | 
 |     GLenum target, GLenum format, GLenum pname, | 
 |     GLsizei buf_size, GLint* params) { | 
 |   GPU_CLIENT_SINGLE_THREAD_CHECK(); | 
 |   GPU_CLIENT_VALIDATE_DESTINATION_INITALIZATION(GLint, params); | 
 |   GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glGetInternalformativ(" | 
 |                      << GLES2Util::GetStringRenderBufferTarget(target) << ", " | 
 |                      << GLES2Util::GetStringRenderBufferFormat(format) << ", " | 
 |                      << GLES2Util::GetStringInternalFormatParameter(pname) | 
 |                      << ", " << buf_size << ", " | 
 |                      << static_cast<const void*>(params) << ")"); | 
 |   if (buf_size < 0) { | 
 |     SetGLError(GL_INVALID_VALUE, "glGetInternalformativ", "bufSize < 0"); | 
 |     return; | 
 |   } | 
 |   TRACE_EVENT0("gpu", "GLES2Implementation::GetInternalformativ"); | 
 |   if (GetInternalformativHelper(target, format, pname, buf_size, params)) { | 
 |     return; | 
 |   } | 
 |   typedef cmds::GetInternalformativ::Result Result; | 
 |   Result* result = GetResultAs<Result*>(); | 
 |   if (!result) { | 
 |     return; | 
 |   } | 
 |   result->SetNumResults(0); | 
 |   helper_->GetInternalformativ(target, format, pname, | 
 |                                GetResultShmId(), GetResultShmOffset()); | 
 |   WaitForCmd(); | 
 |   GPU_CLIENT_LOG_CODE_BLOCK({ | 
 |     for (int32_t i = 0; i < result->GetNumResults(); ++i) { | 
 |       GPU_CLIENT_LOG("  " << i << ": " << result->GetData()[i]); | 
 |     } | 
 |   }); | 
 |   if (buf_size > 0 && params) { | 
 |     GLint* data = result->GetData(); | 
 |     if (buf_size >= result->GetNumResults()) { | 
 |       buf_size = result->GetNumResults(); | 
 |     } | 
 |     for (GLsizei ii = 0; ii < buf_size; ++ii) { | 
 |       params[ii] = data[ii]; | 
 |     } | 
 |   } | 
 |   CheckGLError(); | 
 | } | 
 |  | 
 | GLuint GLES2Implementation::GenPathsCHROMIUM(GLsizei range) { | 
 |   GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glGenPathsCHROMIUM(" << range | 
 |                      << ")"); | 
 |   GPU_CLIENT_SINGLE_THREAD_CHECK(); | 
 |   static const char kFunctionName[] = "glGenPathsCHROMIUM"; | 
 |   if (range < 0) { | 
 |     SetGLError(GL_INVALID_VALUE, kFunctionName, "range < 0"); | 
 |     return 0; | 
 |   } | 
 |   if (!base::IsValueInRangeForNumericType<int32_t>(range)) { | 
 |     SetGLError(GL_INVALID_OPERATION, kFunctionName, "range more than 32-bit"); | 
 |     return 0; | 
 |   } | 
 |   if (range == 0) | 
 |     return 0; | 
 |  | 
 |   GLuint first_client_id = 0; | 
 |   GetRangeIdHandler(id_namespaces::kPaths) | 
 |       ->MakeIdRange(this, range, &first_client_id); | 
 |  | 
 |   if (first_client_id == 0) { | 
 |     // Ran out of id space. Is not specified to raise any gl errors. | 
 |     return 0; | 
 |   } | 
 |  | 
 |   helper_->GenPathsCHROMIUM(first_client_id, range); | 
 |  | 
 |   GPU_CLIENT_LOG_CODE_BLOCK({ | 
 |     for (GLsizei i = 0; i < range; ++i) { | 
 |       GPU_CLIENT_LOG("  " << i << ": " << (first_client_id + i)); | 
 |     } | 
 |   }); | 
 |   CheckGLError(); | 
 |   return first_client_id; | 
 | } | 
 |  | 
 | void GLES2Implementation::DeletePathsCHROMIUM(GLuint first_client_id, | 
 |                                               GLsizei range) { | 
 |   GPU_CLIENT_SINGLE_THREAD_CHECK(); | 
 |   GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glDeletePathsCHROMIUM(" | 
 |                      << first_client_id << ", " << range << ")"); | 
 |   static const char kFunctionName[] = "glDeletePathsCHROMIUM"; | 
 |  | 
 |   if (range < 0) { | 
 |     SetGLError(GL_INVALID_VALUE, kFunctionName, "range < 0"); | 
 |     return; | 
 |   } | 
 |   if (!base::IsValueInRangeForNumericType<int32_t>(range)) { | 
 |     SetGLError(GL_INVALID_OPERATION, kFunctionName, "range more than 32-bit"); | 
 |     return; | 
 |   } | 
 |   if (range == 0) | 
 |     return; | 
 |  | 
 |   GLuint last_client_id; | 
 |   if (!SafeAddUint32(first_client_id, range - 1, &last_client_id)) { | 
 |     SetGLError(GL_INVALID_OPERATION, kFunctionName, "overflow"); | 
 |     return; | 
 |   } | 
 |  | 
 |   GetRangeIdHandler(id_namespaces::kPaths) | 
 |       ->FreeIdRange(this, first_client_id, range, | 
 |                     &GLES2Implementation::DeletePathsCHROMIUMStub); | 
 |   CheckGLError(); | 
 | } | 
 |  | 
 | void GLES2Implementation::DeletePathsCHROMIUMStub(GLuint first_client_id, | 
 |                                                   GLsizei range) { | 
 |   helper_->DeletePathsCHROMIUM(first_client_id, range); | 
 | } | 
 |  | 
 | void GLES2Implementation::PathCommandsCHROMIUM(GLuint path, | 
 |                                                GLsizei num_commands, | 
 |                                                const GLubyte* commands, | 
 |                                                GLsizei num_coords, | 
 |                                                GLenum coord_type, | 
 |                                                const void* coords) { | 
 |   GPU_CLIENT_SINGLE_THREAD_CHECK(); | 
 |   GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glPathCommandsCHROMIUM(" << path | 
 |                      << ", " << num_commands << ", " << commands << ", " | 
 |                      << num_coords << ", " << coords << ")"); | 
 |   static const char kFunctionName[] = "glPathCommandsCHROMIUM"; | 
 |   if (path == 0) { | 
 |     SetGLError(GL_INVALID_VALUE, kFunctionName, "invalid path object"); | 
 |     return; | 
 |   } | 
 |   if (num_commands < 0) { | 
 |     SetGLError(GL_INVALID_VALUE, kFunctionName, "numCommands < 0"); | 
 |     return; | 
 |   } | 
 |   if (num_commands != 0 && !commands) { | 
 |     SetGLError(GL_INVALID_VALUE, kFunctionName, "missing commands"); | 
 |     return; | 
 |   } | 
 |   if (num_coords < 0) { | 
 |     SetGLError(GL_INVALID_VALUE, kFunctionName, "numCoords < 0"); | 
 |     return; | 
 |   } | 
 |   if (num_coords != 0 && !coords) { | 
 |     SetGLError(GL_INVALID_VALUE, kFunctionName, "missing coords"); | 
 |     return; | 
 |   } | 
 |   uint32_t coord_type_size = | 
 |       GLES2Util::GetGLTypeSizeForPathCoordType(coord_type); | 
 |   if (coord_type_size == 0) { | 
 |     SetGLError(GL_INVALID_ENUM, kFunctionName, "invalid coordType"); | 
 |     return; | 
 |   } | 
 |   if (num_commands == 0) { | 
 |     // No commands must mean no coords, thus nothing to memcpy. Let | 
 |     // the service validate the call. Validate coord_type above, so | 
 |     // that the parameters will be checked the in the same order | 
 |     // regardless of num_commands. | 
 |     helper_->PathCommandsCHROMIUM(path, num_commands, 0, 0, num_coords, | 
 |                                   coord_type, 0, 0); | 
 |     CheckGLError(); | 
 |     return; | 
 |   } | 
 |  | 
 |   uint32_t coords_size; | 
 |   if (!SafeMultiplyUint32(num_coords, coord_type_size, &coords_size)) { | 
 |     SetGLError(GL_INVALID_OPERATION, kFunctionName, "overflow"); | 
 |     return; | 
 |   } | 
 |  | 
 |   uint32_t required_buffer_size; | 
 |   if (!SafeAddUint32(coords_size, num_commands, &required_buffer_size)) { | 
 |     SetGLError(GL_INVALID_OPERATION, kFunctionName, "overflow"); | 
 |     return; | 
 |   } | 
 |  | 
 |   ScopedTransferBufferPtr buffer(required_buffer_size, helper_, | 
 |                                  transfer_buffer_); | 
 |   if (!buffer.valid() || buffer.size() < required_buffer_size) { | 
 |     SetGLError(GL_OUT_OF_MEMORY, kFunctionName, "too large"); | 
 |     return; | 
 |   } | 
 |  | 
 |   uint32_t coords_shm_id = 0; | 
 |   uint32_t coords_shm_offset = 0; | 
 |   // Copy coords first because they need more strict alignment. | 
 |   if (coords_size > 0) { | 
 |     unsigned char* coords_addr = static_cast<unsigned char*>(buffer.address()); | 
 |     memcpy(coords_addr, coords, coords_size); | 
 |     coords_shm_id = buffer.shm_id(); | 
 |     coords_shm_offset = buffer.offset(); | 
 |   } | 
 |  | 
 |   DCHECK(num_commands > 0); | 
 |   unsigned char* commands_addr = | 
 |       static_cast<unsigned char*>(buffer.address()) + coords_size; | 
 |   memcpy(commands_addr, commands, num_commands); | 
 |  | 
 |   helper_->PathCommandsCHROMIUM(path, num_commands, buffer.shm_id(), | 
 |                                 buffer.offset() + coords_size, num_coords, | 
 |                                 coord_type, coords_shm_id, coords_shm_offset); | 
 |   CheckGLError(); | 
 | } | 
 |  | 
 | bool GLES2Implementation::PrepareInstancedPathCommand( | 
 |     const char* function_name, | 
 |     GLsizei num_paths, | 
 |     GLenum path_name_type, | 
 |     const void* paths, | 
 |     GLenum transform_type, | 
 |     const GLfloat* transform_values, | 
 |     ScopedTransferBufferPtr* buffer, | 
 |     uint32_t* out_paths_shm_id, | 
 |     size_t* out_paths_offset, | 
 |     uint32_t* out_transforms_shm_id, | 
 |     size_t* out_transforms_offset) { | 
 |   if (num_paths < 0) { | 
 |     SetGLError(GL_INVALID_VALUE, function_name, "numPaths < 0"); | 
 |     return false; | 
 |   } | 
 |   uint32_t path_name_size = | 
 |       GLES2Util::GetGLTypeSizeForGLPathNameType(path_name_type); | 
 |  | 
 |   if (path_name_size == 0) { | 
 |     SetGLError(GL_INVALID_ENUM, function_name, "invalid pathNameType"); | 
 |     return false; | 
 |   } | 
 |  | 
 |   uint32_t transforms_component_count = | 
 |       GLES2Util::GetComponentCountForGLTransformType(transform_type); | 
 |  | 
 |   if (transform_type != GL_NONE && transforms_component_count == 0) { | 
 |     SetGLError(GL_INVALID_ENUM, function_name, "invalid transformType"); | 
 |     return false; | 
 |   } | 
 |  | 
 |   if (num_paths == 0) { | 
 |     // This might still be a valid or an invalid GL call. Make an empty call to | 
 |     // the service side to check the rest of the parameters. We check the above | 
 |     // parameters client-side, in order to get same GL errors whether num_paths | 
 |     // == 0 or not. We do not check the parameters below, as they are anyway | 
 |     // checked by the service side. We can not check all the parameters | 
 |     // client-side, since the validators are not available. | 
 |     *out_paths_shm_id = 0; | 
 |     *out_paths_offset = 0; | 
 |     *out_transforms_shm_id = 0; | 
 |     *out_transforms_offset = 0; | 
 |     return true; | 
 |   } | 
 |  | 
 |   if (!paths) { | 
 |     SetGLError(GL_INVALID_VALUE, function_name, "missing paths"); | 
 |     return false; | 
 |   } | 
 |  | 
 |   if (transform_type != GL_NONE && !transform_values) { | 
 |     SetGLError(GL_INVALID_VALUE, function_name, "missing transforms"); | 
 |     return false; | 
 |   } | 
 |  | 
 |   uint32_t paths_size; | 
 |   if (!SafeMultiplyUint32(path_name_size, num_paths, &paths_size)) { | 
 |     SetGLError(GL_INVALID_OPERATION, function_name, "overflow"); | 
 |     return false; | 
 |   } | 
 |  | 
 |   // The multiplication below will not overflow. | 
 |   DCHECK(transforms_component_count <= 12); | 
 |   uint32_t one_transform_size = sizeof(GLfloat) * transforms_component_count; | 
 |  | 
 |   uint32_t transforms_size; | 
 |   if (!SafeMultiplyUint32(one_transform_size, num_paths, &transforms_size)) { | 
 |     SetGLError(GL_INVALID_OPERATION, function_name, "overflow"); | 
 |     return false; | 
 |   } | 
 |  | 
 |   uint32_t required_buffer_size; | 
 |   if (!SafeAddUint32(transforms_size, paths_size, &required_buffer_size)) { | 
 |     SetGLError(GL_INVALID_OPERATION, function_name, "overflow"); | 
 |     return false; | 
 |   } | 
 |  | 
 |   buffer->Reset(required_buffer_size); | 
 |  | 
 |   if (!buffer->valid() || buffer->size() < required_buffer_size) { | 
 |     SetGLError(GL_OUT_OF_MEMORY, function_name, "too large"); | 
 |     return false; | 
 |   } | 
 |  | 
 |   // Copy transforms first, they may have more strict alignment. | 
 |   if (transforms_size > 0) { | 
 |     unsigned char* transforms_addr = | 
 |         static_cast<unsigned char*>(buffer->address()); | 
 |     memcpy(transforms_addr, transform_values, transforms_size); | 
 |     *out_transforms_shm_id = buffer->shm_id(); | 
 |     *out_transforms_offset = buffer->offset(); | 
 |   } else { | 
 |     *out_transforms_shm_id = 0; | 
 |     *out_transforms_offset = 0; | 
 |   } | 
 |  | 
 |   DCHECK(paths_size > 0); | 
 |   unsigned char* paths_addr = | 
 |       static_cast<unsigned char*>(buffer->address()) + transforms_size; | 
 |   memcpy(paths_addr, paths, paths_size); | 
 |   *out_paths_shm_id = buffer->shm_id(); | 
 |   *out_paths_offset = buffer->offset() + transforms_size; | 
 |  | 
 |   return true; | 
 | } | 
 |  | 
 | void GLES2Implementation::StencilFillPathInstancedCHROMIUM( | 
 |     GLsizei num_paths, | 
 |     GLenum path_name_type, | 
 |     const GLvoid* paths, | 
 |     GLuint path_base, | 
 |     GLenum fill_mode, | 
 |     GLuint mask, | 
 |     GLenum transform_type, | 
 |     const GLfloat* transform_values) { | 
 |   GPU_CLIENT_SINGLE_THREAD_CHECK(); | 
 |   GPU_CLIENT_LOG("[" << GetLogPrefix() | 
 |                      << "] glStencilFillPathInstancedCHROMIUM(" << num_paths | 
 |                      << ", " << path_name_type << ", " << paths << ", " | 
 |                      << path_base << ", " << fill_mode << ", " << mask << ", " | 
 |                      << transform_type << ", " << transform_values << ")"); | 
 |  | 
 |   ScopedTransferBufferPtr buffer(helper_, transfer_buffer_); | 
 |   uint32_t paths_shm_id = 0; | 
 |   size_t paths_offset = 0; | 
 |   uint32_t transforms_shm_id = 0; | 
 |   size_t transforms_offset = 0; | 
 |   if (!PrepareInstancedPathCommand( | 
 |           "glStencilFillPathInstancedCHROMIUM", num_paths, path_name_type, | 
 |           paths, transform_type, transform_values, &buffer, &paths_shm_id, | 
 |           &paths_offset, &transforms_shm_id, &transforms_offset)) { | 
 |     return; | 
 |   } | 
 |  | 
 |   helper_->StencilFillPathInstancedCHROMIUM( | 
 |       num_paths, path_name_type, paths_shm_id, paths_offset, path_base, | 
 |       fill_mode, mask, transform_type, transforms_shm_id, transforms_offset); | 
 |  | 
 |   CheckGLError(); | 
 | } | 
 |  | 
 | void GLES2Implementation::StencilStrokePathInstancedCHROMIUM( | 
 |     GLsizei num_paths, | 
 |     GLenum path_name_type, | 
 |     const GLvoid* paths, | 
 |     GLuint path_base, | 
 |     GLint ref, | 
 |     GLuint mask, | 
 |     GLenum transform_type, | 
 |     const GLfloat* transform_values) { | 
 |   GPU_CLIENT_SINGLE_THREAD_CHECK(); | 
 |   GPU_CLIENT_LOG("[" << GetLogPrefix() | 
 |                      << "] glStencilStrokePathInstancedCHROMIUM(" << num_paths | 
 |                      << ", " << path_name_type << ", " << paths << ", " | 
 |                      << path_base << ", " << ref << ", " << mask << ", " | 
 |                      << transform_type << ", " << transform_values << ")"); | 
 |  | 
 |   ScopedTransferBufferPtr buffer(helper_, transfer_buffer_); | 
 |   uint32_t paths_shm_id = 0; | 
 |   size_t paths_offset = 0; | 
 |   uint32_t transforms_shm_id = 0; | 
 |   size_t transforms_offset = 0; | 
 |   if (!PrepareInstancedPathCommand( | 
 |           "glStencilStrokePathInstancedCHROMIUM", num_paths, path_name_type, | 
 |           paths, transform_type, transform_values, &buffer, &paths_shm_id, | 
 |           &paths_offset, &transforms_shm_id, &transforms_offset)) { | 
 |     return; | 
 |   } | 
 |  | 
 |   helper_->StencilStrokePathInstancedCHROMIUM( | 
 |       num_paths, path_name_type, paths_shm_id, paths_offset, path_base, ref, | 
 |       mask, transform_type, transforms_shm_id, transforms_offset); | 
 |  | 
 |   CheckGLError(); | 
 | } | 
 |  | 
 | void GLES2Implementation::CoverFillPathInstancedCHROMIUM( | 
 |     GLsizei num_paths, | 
 |     GLenum path_name_type, | 
 |     const GLvoid* paths, | 
 |     GLuint path_base, | 
 |     GLenum cover_mode, | 
 |     GLenum transform_type, | 
 |     const GLfloat* transform_values) { | 
 |   GPU_CLIENT_SINGLE_THREAD_CHECK(); | 
 |   GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glCoverFillPathInstancedCHROMIUM(" | 
 |                      << num_paths << ", " << path_name_type << ", " << paths | 
 |                      << ", " << path_base << ", " << cover_mode << ", " | 
 |                      << transform_type << ", " << transform_values << ")"); | 
 |  | 
 |   ScopedTransferBufferPtr buffer(helper_, transfer_buffer_); | 
 |   uint32_t paths_shm_id = 0; | 
 |   size_t paths_offset = 0; | 
 |   uint32_t transforms_shm_id = 0; | 
 |   size_t transforms_offset = 0; | 
 |   if (!PrepareInstancedPathCommand( | 
 |           "glCoverFillPathInstancedCHROMIUM", num_paths, path_name_type, paths, | 
 |           transform_type, transform_values, &buffer, &paths_shm_id, | 
 |           &paths_offset, &transforms_shm_id, &transforms_offset)) { | 
 |     return; | 
 |   } | 
 |  | 
 |   helper_->CoverFillPathInstancedCHROMIUM( | 
 |       num_paths, path_name_type, paths_shm_id, paths_offset, path_base, | 
 |       cover_mode, transform_type, transforms_shm_id, transforms_offset); | 
 |  | 
 |   CheckGLError(); | 
 | } | 
 |  | 
 | void GLES2Implementation::CoverStrokePathInstancedCHROMIUM( | 
 |     GLsizei num_paths, | 
 |     GLenum path_name_type, | 
 |     const GLvoid* paths, | 
 |     GLuint path_base, | 
 |     GLenum cover_mode, | 
 |     GLenum transform_type, | 
 |     const GLfloat* transform_values) { | 
 |   GPU_CLIENT_SINGLE_THREAD_CHECK(); | 
 |   GPU_CLIENT_LOG("[" << GetLogPrefix() | 
 |                      << "] glCoverStrokePathInstancedCHROMIUM(" << num_paths | 
 |                      << ", " << path_name_type << ", " << paths << ", " | 
 |                      << path_base << ", " << cover_mode << ", " | 
 |                      << transform_type << ", " << transform_values << ")"); | 
 |  | 
 |   ScopedTransferBufferPtr buffer(helper_, transfer_buffer_); | 
 |   uint32_t paths_shm_id = 0; | 
 |   size_t paths_offset = 0; | 
 |   uint32_t transforms_shm_id = 0; | 
 |   size_t transforms_offset = 0; | 
 |   if (!PrepareInstancedPathCommand( | 
 |           "glCoverStrokePathInstancedCHROMIUM", num_paths, path_name_type, | 
 |           paths, transform_type, transform_values, &buffer, &paths_shm_id, | 
 |           &paths_offset, &transforms_shm_id, &transforms_offset)) { | 
 |     return; | 
 |   } | 
 |  | 
 |   helper_->CoverStrokePathInstancedCHROMIUM( | 
 |       num_paths, path_name_type, paths_shm_id, paths_offset, path_base, | 
 |       cover_mode, transform_type, transforms_shm_id, transforms_offset); | 
 |  | 
 |   CheckGLError(); | 
 | } | 
 |  | 
 | void GLES2Implementation::StencilThenCoverFillPathInstancedCHROMIUM( | 
 |     GLsizei num_paths, | 
 |     GLenum path_name_type, | 
 |     const GLvoid* paths, | 
 |     GLuint path_base, | 
 |     GLenum fill_mode, | 
 |     GLuint mask, | 
 |     GLenum cover_mode, | 
 |     GLenum transform_type, | 
 |     const GLfloat* transform_values) { | 
 |   GPU_CLIENT_SINGLE_THREAD_CHECK(); | 
 |   GPU_CLIENT_LOG( | 
 |       "[" << GetLogPrefix() << "] glStencilThenCoverFillPathInstancedCHROMIUM(" | 
 |           << num_paths << ", " << path_name_type << ", " << paths << ", " | 
 |           << path_base << ", " << cover_mode << ", " << fill_mode << ", " | 
 |           << mask << ", " << transform_type << ", " << transform_values << ")"); | 
 |  | 
 |   ScopedTransferBufferPtr buffer(helper_, transfer_buffer_); | 
 |   uint32_t paths_shm_id = 0; | 
 |   size_t paths_offset = 0; | 
 |   uint32_t transforms_shm_id = 0; | 
 |   size_t transforms_offset = 0; | 
 |   if (!PrepareInstancedPathCommand( | 
 |           "glStencilThenCoverFillPathInstancedCHROMIUM", num_paths, | 
 |           path_name_type, paths, transform_type, transform_values, &buffer, | 
 |           &paths_shm_id, &paths_offset, &transforms_shm_id, | 
 |           &transforms_offset)) { | 
 |     return; | 
 |   } | 
 |  | 
 |   helper_->StencilThenCoverFillPathInstancedCHROMIUM( | 
 |       num_paths, path_name_type, paths_shm_id, paths_offset, path_base, | 
 |       fill_mode, mask, cover_mode, transform_type, transforms_shm_id, | 
 |       transforms_offset); | 
 |  | 
 |   CheckGLError(); | 
 | } | 
 |  | 
 | void GLES2Implementation::StencilThenCoverStrokePathInstancedCHROMIUM( | 
 |     GLsizei num_paths, | 
 |     GLenum path_name_type, | 
 |     const GLvoid* paths, | 
 |     GLuint path_base, | 
 |     GLint ref, | 
 |     GLuint mask, | 
 |     GLenum cover_mode, | 
 |     GLenum transform_type, | 
 |     const GLfloat* transform_values) { | 
 |   GPU_CLIENT_SINGLE_THREAD_CHECK(); | 
 |   GPU_CLIENT_LOG("[" << GetLogPrefix() | 
 |                      << "] glStencilThenCoverStrokePathInstancedCHROMIUM(" | 
 |                      << num_paths << ", " << path_name_type << ", " << paths | 
 |                      << ", " << path_base << ", " << cover_mode << ", " << ref | 
 |                      << ", " << mask << ", " << transform_type << ", " | 
 |                      << transform_values << ")"); | 
 |  | 
 |   ScopedTransferBufferPtr buffer(helper_, transfer_buffer_); | 
 |   uint32_t paths_shm_id = 0; | 
 |   size_t paths_offset = 0; | 
 |   uint32_t transforms_shm_id = 0; | 
 |   size_t transforms_offset = 0; | 
 |   if (!PrepareInstancedPathCommand( | 
 |           "glStencilThenCoverStrokePathInstancedCHROMIUM", num_paths, | 
 |           path_name_type, paths, transform_type, transform_values, &buffer, | 
 |           &paths_shm_id, &paths_offset, &transforms_shm_id, | 
 |           &transforms_offset)) { | 
 |     return; | 
 |   } | 
 |  | 
 |   helper_->StencilThenCoverStrokePathInstancedCHROMIUM( | 
 |       num_paths, path_name_type, paths_shm_id, paths_offset, path_base, ref, | 
 |       mask, cover_mode, transform_type, transforms_shm_id, transforms_offset); | 
 |  | 
 |   CheckGLError(); | 
 | } | 
 |  | 
 | void GLES2Implementation::BindFragmentInputLocationCHROMIUM(GLuint program, | 
 |                                                             GLint location, | 
 |                                                             const char* name) { | 
 |   GPU_CLIENT_SINGLE_THREAD_CHECK(); | 
 |   GPU_CLIENT_LOG("[" << GetLogPrefix() | 
 |                      << "] glBindFragmentInputLocationCHROMIUM(" << program | 
 |                      << ", " << location << ", " << name << ")"); | 
 |   SetBucketAsString(kResultBucketId, name); | 
 |   helper_->BindFragmentInputLocationCHROMIUMBucket(program, location, | 
 |                                                    kResultBucketId); | 
 |   helper_->SetBucketSize(kResultBucketId, 0); | 
 |   CheckGLError(); | 
 | } | 
 |  | 
 | void GLES2Implementation::ProgramPathFragmentInputGenCHROMIUM( | 
 |     GLuint program, | 
 |     GLint location, | 
 |     GLenum gen_mode, | 
 |     GLint components, | 
 |     const GLfloat* coeffs) { | 
 |   GPU_CLIENT_SINGLE_THREAD_CHECK(); | 
 |   GPU_CLIENT_LOG("[" << GetLogPrefix() | 
 |                      << "] glProgramPathFragmentInputGenCHROMIUM(" << program | 
 |                      << ", " << gen_mode << ", " << components << ", " << coeffs | 
 |                      << ")"); | 
 |  | 
 |   uint32_t coeffs_per_component = | 
 |       GLES2Util::GetCoefficientCountForGLPathFragmentInputGenMode(gen_mode); | 
 |  | 
 |   if (components <= 0 || components > 4 || gen_mode == GL_NONE || | 
 |       coeffs_per_component == 0 || location == -1) { | 
 |     helper_->ProgramPathFragmentInputGenCHROMIUM(program, location, gen_mode, | 
 |                                                  components, 0, 0); | 
 |   } else { | 
 |     // The multiplication below will not overflow. | 
 |     DCHECK(coeffs_per_component > 0 && coeffs_per_component <= 4); | 
 |     DCHECK(components > 0 && components <= 4); | 
 |     uint32_t coeffs_size = sizeof(GLfloat) * coeffs_per_component * components; | 
 |  | 
 |     ScopedTransferBufferPtr buffer(coeffs_size, helper_, transfer_buffer_); | 
 |     if (!buffer.valid() || buffer.size() < coeffs_size) { | 
 |       SetGLError(GL_OUT_OF_MEMORY, "glProgramPathFragmentInputGenCHROMIUM", | 
 |                  "no room in transfer buffer"); | 
 |       return; | 
 |     } | 
 |  | 
 |     DCHECK(coeffs_size > 0); | 
 |     unsigned char* addr = static_cast<unsigned char*>(buffer.address()); | 
 |     memcpy(addr, coeffs, coeffs_size); | 
 |  | 
 |     helper_->ProgramPathFragmentInputGenCHROMIUM(program, location, gen_mode, | 
 |                                                  components, buffer.shm_id(), | 
 |                                                  buffer.offset()); | 
 |   } | 
 |   CheckGLError(); | 
 | } | 
 |  | 
 | void GLES2Implementation::UpdateCachedExtensionsIfNeeded() { | 
 |   if (cached_extension_string_) { | 
 |     return; | 
 |   } | 
 |   GetStringHelper(GL_EXTENSIONS); | 
 | } | 
 |  | 
 | void GLES2Implementation::InvalidateCachedExtensions() { | 
 |   cached_extension_string_ = nullptr; | 
 |   cached_extensions_.clear(); | 
 | } | 
 |  | 
 | // 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 |