| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| // A class to emulate GLES2 over command buffers. |
| |
| #include "gpu/command_buffer/client/gles2_implementation.h" |
| |
| #include <algorithm> |
| #include <map> |
| #include <queue> |
| #include <set> |
| #include <limits> |
| #include <stdio.h> |
| #include <string.h> |
| #include <GLES2/gl2ext.h> |
| #include <GLES2/gl2extchromium.h> |
| #include "gpu/command_buffer/client/buffer_tracker.h" |
| #include "gpu/command_buffer/client/gpu_memory_buffer_tracker.h" |
| #include "gpu/command_buffer/client/program_info_manager.h" |
| #include "gpu/command_buffer/client/query_tracker.h" |
| #include "gpu/command_buffer/client/transfer_buffer.h" |
| #include "gpu/command_buffer/client/vertex_array_object_manager.h" |
| #include "gpu/command_buffer/common/gles2_cmd_utils.h" |
| #include "gpu/command_buffer/common/trace_event.h" |
| #include "ui/gfx/gpu_memory_buffer.h" |
| |
| #if defined(__native_client__) && !defined(GLES2_SUPPORT_CLIENT_SIDE_ARRAYS) |
| #define GLES2_SUPPORT_CLIENT_SIDE_ARRAYS |
| #endif |
| |
| #if defined(GPU_CLIENT_DEBUG) |
| #include "ui/gl/gl_switches.h" |
| #include "base/command_line.h" |
| #endif |
| |
| namespace gpu { |
| namespace gles2 { |
| |
| // A 32-bit and 64-bit compatible way of converting a pointer to a GLuint. |
| static GLuint ToGLuint(const void* ptr) { |
| return static_cast<GLuint>(reinterpret_cast<size_t>(ptr)); |
| } |
| |
| #if !defined(_MSC_VER) |
| const size_t GLES2Implementation::kMaxSizeOfSimpleResult; |
| const unsigned int GLES2Implementation::kStartingOffset; |
| #endif |
| |
| GLES2Implementation::GLStaticState::GLStaticState() { |
| } |
| |
| GLES2Implementation::GLStaticState::~GLStaticState() { |
| } |
| |
| GLES2Implementation::GLStaticState::IntState::IntState() |
| : max_combined_texture_image_units(0), |
| max_cube_map_texture_size(0), |
| max_fragment_uniform_vectors(0), |
| max_renderbuffer_size(0), |
| max_texture_image_units(0), |
| max_texture_size(0), |
| max_varying_vectors(0), |
| max_vertex_attribs(0), |
| max_vertex_texture_image_units(0), |
| max_vertex_uniform_vectors(0), |
| num_compressed_texture_formats(0), |
| num_shader_binary_formats(0) { |
| } |
| |
| GLES2Implementation::SingleThreadChecker::SingleThreadChecker( |
| GLES2Implementation* gles2_implementation) |
| : gles2_implementation_(gles2_implementation) { |
| GPU_CHECK_EQ(0, gles2_implementation_->use_count_); |
| ++gles2_implementation_->use_count_; |
| } |
| |
| GLES2Implementation::SingleThreadChecker::~SingleThreadChecker() { |
| --gles2_implementation_->use_count_; |
| GPU_CHECK_EQ(0, gles2_implementation_->use_count_); |
| } |
| |
| GLES2Implementation::GLES2Implementation( |
| GLES2CmdHelper* helper, |
| ShareGroup* share_group, |
| TransferBufferInterface* transfer_buffer, |
| bool bind_generates_resource, |
| GpuControl* gpu_control) |
| : helper_(helper), |
| transfer_buffer_(transfer_buffer), |
| angle_pack_reverse_row_order_status_(kUnknownExtensionStatus), |
| chromium_framebuffer_multisample_(kUnknownExtensionStatus), |
| pack_alignment_(4), |
| unpack_alignment_(4), |
| unpack_flip_y_(false), |
| unpack_row_length_(0), |
| unpack_skip_rows_(0), |
| unpack_skip_pixels_(0), |
| pack_reverse_row_order_(false), |
| active_texture_unit_(0), |
| bound_framebuffer_(0), |
| bound_read_framebuffer_(0), |
| bound_renderbuffer_(0), |
| current_program_(0), |
| bound_array_buffer_id_(0), |
| bound_pixel_pack_transfer_buffer_id_(0), |
| bound_pixel_unpack_transfer_buffer_id_(0), |
| error_bits_(0), |
| debug_(false), |
| use_count_(0), |
| current_query_(NULL), |
| error_message_callback_(NULL), |
| gpu_control_(gpu_control) { |
| GPU_DCHECK(helper); |
| GPU_DCHECK(transfer_buffer); |
| |
| char temp[128]; |
| sprintf(temp, "%p", static_cast<void*>(this)); |
| this_in_hex_ = std::string(temp); |
| |
| GPU_CLIENT_LOG_CODE_BLOCK({ |
| debug_ = CommandLine::ForCurrentProcess()->HasSwitch( |
| switches::kEnableGPUClientLogging); |
| }); |
| |
| share_group_ = |
| (share_group ? share_group : new ShareGroup(bind_generates_resource)); |
| |
| memset(&reserved_ids_, 0, sizeof(reserved_ids_)); |
| } |
| |
| bool GLES2Implementation::Initialize( |
| unsigned int starting_transfer_buffer_size, |
| unsigned int min_transfer_buffer_size, |
| unsigned int max_transfer_buffer_size, |
| unsigned int mapped_memory_limit) { |
| GPU_DCHECK_GE(starting_transfer_buffer_size, min_transfer_buffer_size); |
| GPU_DCHECK_LE(starting_transfer_buffer_size, max_transfer_buffer_size); |
| GPU_DCHECK_GE(min_transfer_buffer_size, kStartingOffset); |
| |
| if (!transfer_buffer_->Initialize( |
| starting_transfer_buffer_size, |
| kStartingOffset, |
| min_transfer_buffer_size, |
| max_transfer_buffer_size, |
| kAlignment, |
| kSizeToFlush)) { |
| return false; |
| } |
| |
| mapped_memory_.reset(new MappedMemoryManager(helper_, mapped_memory_limit)); |
| mapped_memory_->set_chunk_size_multiple(2 * 1024 * 1024); |
| |
| if (!QueryAndCacheStaticState()) |
| return false; |
| |
| util_.set_num_compressed_texture_formats( |
| static_state_.int_state.num_compressed_texture_formats); |
| util_.set_num_shader_binary_formats( |
| static_state_.int_state.num_shader_binary_formats); |
| |
| texture_units_.reset( |
| new TextureUnit[ |
| static_state_.int_state.max_combined_texture_image_units]); |
| |
| query_tracker_.reset(new QueryTracker(mapped_memory_.get())); |
| buffer_tracker_.reset(new BufferTracker(mapped_memory_.get())); |
| gpu_memory_buffer_tracker_.reset(new GpuMemoryBufferTracker(gpu_control_)); |
| |
| #if defined(GLES2_SUPPORT_CLIENT_SIDE_ARRAYS) |
| GetIdHandler(id_namespaces::kBuffers)->MakeIds( |
| this, kClientSideArrayId, arraysize(reserved_ids_), &reserved_ids_[0]); |
| #endif |
| |
| vertex_array_object_manager_.reset(new VertexArrayObjectManager( |
| static_state_.int_state.max_vertex_attribs, |
| reserved_ids_[0], |
| reserved_ids_[1])); |
| |
| return true; |
| } |
| |
| bool GLES2Implementation::QueryAndCacheStaticState() { |
| // Setup query for multiple GetIntegerv's |
| static const GLenum pnames[] = { |
| GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, |
| GL_MAX_CUBE_MAP_TEXTURE_SIZE, |
| GL_MAX_FRAGMENT_UNIFORM_VECTORS, |
| GL_MAX_RENDERBUFFER_SIZE, |
| GL_MAX_TEXTURE_IMAGE_UNITS, |
| GL_MAX_TEXTURE_SIZE, |
| GL_MAX_VARYING_VECTORS, |
| GL_MAX_VERTEX_ATTRIBS, |
| GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, |
| GL_MAX_VERTEX_UNIFORM_VECTORS, |
| GL_NUM_COMPRESSED_TEXTURE_FORMATS, |
| GL_NUM_SHADER_BINARY_FORMATS, |
| }; |
| |
| GetMultipleIntegervState integerv_state( |
| pnames, arraysize(pnames), |
| &static_state_.int_state.max_combined_texture_image_units, |
| sizeof(static_state_.int_state)); |
| if (!GetMultipleIntegervSetup(&integerv_state)) { |
| return false; |
| } |
| |
| // Setup query for multiple GetShaderPrecisionFormat's |
| static const GLenum precision_params[][2] = { |
| { GL_VERTEX_SHADER, GL_LOW_INT }, |
| { GL_VERTEX_SHADER, GL_MEDIUM_INT }, |
| { GL_VERTEX_SHADER, GL_HIGH_INT }, |
| { GL_VERTEX_SHADER, GL_LOW_FLOAT }, |
| { GL_VERTEX_SHADER, GL_MEDIUM_FLOAT }, |
| { GL_VERTEX_SHADER, GL_HIGH_FLOAT }, |
| { GL_FRAGMENT_SHADER, GL_LOW_INT }, |
| { GL_FRAGMENT_SHADER, GL_MEDIUM_INT }, |
| { GL_FRAGMENT_SHADER, GL_HIGH_INT }, |
| { GL_FRAGMENT_SHADER, GL_LOW_FLOAT }, |
| { GL_FRAGMENT_SHADER, GL_MEDIUM_FLOAT }, |
| { GL_FRAGMENT_SHADER, GL_HIGH_FLOAT }, |
| }; |
| |
| GetAllShaderPrecisionFormatsState precision_state( |
| precision_params, arraysize(precision_params)); |
| GetAllShaderPrecisionFormatsSetup(&precision_state); |
| |
| // Allocate and partition transfer buffer for all requests |
| void* buffer = transfer_buffer_->Alloc( |
| integerv_state.transfer_buffer_size_needed + |
| precision_state.transfer_buffer_size_needed); |
| if (!buffer) { |
| SetGLError(GL_OUT_OF_MEMORY, "QueryAndCacheStaticState", |
| "Transfer buffer allocation failed."); |
| return false; |
| } |
| integerv_state.buffer = buffer; |
| precision_state.results_buffer = |
| static_cast<char*>(buffer) + integerv_state.transfer_buffer_size_needed; |
| |
| // Make all the requests and wait once for all the results. |
| GetMultipleIntegervRequest(&integerv_state); |
| GetAllShaderPrecisionFormatsRequest(&precision_state); |
| WaitForCmd(); |
| GetMultipleIntegervOnCompleted(&integerv_state); |
| GetAllShaderPrecisionFormatsOnCompleted(&precision_state); |
| |
| // TODO(gman): We should be able to free without a token. |
| transfer_buffer_->FreePendingToken(buffer, helper_->InsertToken()); |
| CheckGLError(); |
| |
| return true; |
| } |
| |
| GLES2Implementation::~GLES2Implementation() { |
| // Make sure the queries are finished otherwise we'll delete the |
| // shared memory (mapped_memory_) which will free the memory used |
| // by the queries. The GPU process when validating that memory is still |
| // shared will fail and abort (ie, it will stop running). |
| WaitForCmd(); |
| query_tracker_.reset(); |
| |
| #if defined(GLES2_SUPPORT_CLIENT_SIDE_ARRAYS) |
| DeleteBuffers(arraysize(reserved_ids_), &reserved_ids_[0]); |
| #endif |
| buffer_tracker_.reset(); |
| |
| // Make sure the commands make it the service. |
| WaitForCmd(); |
| } |
| |
| GLES2CmdHelper* GLES2Implementation::helper() const { |
| return helper_; |
| } |
| |
| IdHandlerInterface* GLES2Implementation::GetIdHandler(int namespace_id) const { |
| return share_group_->GetIdHandler(namespace_id); |
| } |
| |
| void* GLES2Implementation::GetResultBuffer() { |
| return transfer_buffer_->GetResultBuffer(); |
| } |
| |
| int32 GLES2Implementation::GetResultShmId() { |
| return transfer_buffer_->GetShmId(); |
| } |
| |
| uint32 GLES2Implementation::GetResultShmOffset() { |
| return transfer_buffer_->GetResultOffset(); |
| } |
| |
| void GLES2Implementation::FreeUnusedSharedMemory() { |
| mapped_memory_->FreeUnused(); |
| } |
| |
| void GLES2Implementation::FreeEverything() { |
| WaitForCmd(); |
| query_tracker_->Shrink(); |
| FreeUnusedSharedMemory(); |
| transfer_buffer_->Free(); |
| helper_->FreeRingBuffer(); |
| } |
| |
| void GLES2Implementation::WaitForCmd() { |
| TRACE_EVENT0("gpu", "GLES2::WaitForCmd"); |
| helper_->CommandBufferHelper::Finish(); |
| } |
| |
| bool GLES2Implementation::IsExtensionAvailable(const char* ext) { |
| const char* extensions = |
| reinterpret_cast<const char*>(GetStringHelper(GL_EXTENSIONS)); |
| if (!extensions) |
| return false; |
| |
| int length = strlen(ext); |
| while (true) { |
| int n = strcspn(extensions, " "); |
| if (n == length && 0 == strncmp(ext, extensions, length)) { |
| return true; |
| } |
| if ('\0' == extensions[n]) { |
| return false; |
| } |
| extensions += n + 1; |
| } |
| } |
| |
| bool GLES2Implementation::IsExtensionAvailableHelper( |
| const char* extension, ExtensionStatus* status) { |
| switch (*status) { |
| case kAvailableExtensionStatus: |
| return true; |
| case kUnavailableExtensionStatus: |
| return false; |
| default: { |
| bool available = IsExtensionAvailable(extension); |
| *status = available ? kAvailableExtensionStatus : |
| kUnavailableExtensionStatus; |
| return available; |
| } |
| } |
| } |
| |
| bool GLES2Implementation::IsAnglePackReverseRowOrderAvailable() { |
| return IsExtensionAvailableHelper( |
| "GL_ANGLE_pack_reverse_row_order", |
| &angle_pack_reverse_row_order_status_); |
| } |
| |
| bool GLES2Implementation::IsChromiumFramebufferMultisampleAvailable() { |
| return IsExtensionAvailableHelper( |
| "GL_CHROMIUM_framebuffer_multisample", |
| &chromium_framebuffer_multisample_); |
| } |
| |
| const std::string& GLES2Implementation::GetLogPrefix() const { |
| const std::string& prefix(debug_marker_manager_.GetMarker()); |
| return prefix.empty() ? this_in_hex_ : prefix; |
| } |
| |
| GLenum GLES2Implementation::GetError() { |
| GPU_CLIENT_SINGLE_THREAD_CHECK(); |
| GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glGetError()"); |
| GLenum err = GetGLError(); |
| GPU_CLIENT_LOG("returned " << GLES2Util::GetStringError(err)); |
| return err; |
| } |
| |
| GLenum GLES2Implementation::GetClientSideGLError() { |
| if (error_bits_ == 0) { |
| return GL_NO_ERROR; |
| } |
| |
| GLenum error = GL_NO_ERROR; |
| for (uint32 mask = 1; mask != 0; mask = mask << 1) { |
| if ((error_bits_ & mask) != 0) { |
| error = GLES2Util::GLErrorBitToGLError(mask); |
| break; |
| } |
| } |
| error_bits_ &= ~GLES2Util::GLErrorToErrorBit(error); |
| return error; |
| } |
| |
| GLenum GLES2Implementation::GetGLError() { |
| TRACE_EVENT0("gpu", "GLES2::GetGLError"); |
| // Check the GL error first, then our wrapped error. |
| typedef cmds::GetError::Result Result; |
| Result* result = GetResultAs<Result*>(); |
| // If we couldn't allocate a result the context is lost. |
| if (!result) { |
| return GL_NO_ERROR; |
| } |
| *result = GL_NO_ERROR; |
| helper_->GetError(GetResultShmId(), GetResultShmOffset()); |
| WaitForCmd(); |
| GLenum error = *result; |
| if (error == GL_NO_ERROR) { |
| error = GetClientSideGLError(); |
| } else { |
| // There was an error, clear the corresponding wrapped error. |
| error_bits_ &= ~GLES2Util::GLErrorToErrorBit(error); |
| } |
| return error; |
| } |
| |
| #if defined(GL_CLIENT_FAIL_GL_ERRORS) |
| void GLES2Implementation::FailGLError(GLenum error) { |
| if (error != GL_NO_ERROR) { |
| GPU_NOTREACHED() << "Error"; |
| } |
| } |
| // NOTE: Calling GetGLError overwrites data in the result buffer. |
| void GLES2Implementation::CheckGLError() { |
| FailGLError(GetGLError()); |
| } |
| #endif // defined(GPU_CLIENT_FAIL_GL_ERRORS) |
| |
| void GLES2Implementation::SetGLError( |
| GLenum error, const char* function_name, const char* msg) { |
| GPU_CLIENT_LOG("[" << GetLogPrefix() << "] Client Synthesized Error: " |
| << GLES2Util::GetStringError(error) << ": " |
| << function_name << ": " << msg); |
| FailGLError(error); |
| if (msg) { |
| last_error_ = msg; |
| } |
| if (error_message_callback_) { |
| std::string temp(GLES2Util::GetStringError(error) + " : " + |
| function_name + ": " + (msg ? msg : "")); |
| error_message_callback_->OnErrorMessage(temp.c_str(), 0); |
| } |
| error_bits_ |= GLES2Util::GLErrorToErrorBit(error); |
| } |
| |
| void GLES2Implementation::SetGLErrorInvalidEnum( |
| const char* function_name, GLenum value, const char* label) { |
| SetGLError(GL_INVALID_ENUM, function_name, |
| (std::string(label) + " was " + |
| GLES2Util::GetStringEnum(value)).c_str()); |
| } |
| |
| bool GLES2Implementation::GetBucketContents(uint32 bucket_id, |
| std::vector<int8>* data) { |
| TRACE_EVENT0("gpu", "GLES2::GetBucketContents"); |
| GPU_DCHECK(data); |
| const uint32 kStartSize = 32 * 1024; |
| ScopedTransferBufferPtr buffer(kStartSize, helper_, transfer_buffer_); |
| if (!buffer.valid()) { |
| return false; |
| } |
| typedef cmd::GetBucketStart::Result Result; |
| Result* result = GetResultAs<Result*>(); |
| if (!result) { |
| return false; |
| } |
| *result = 0; |
| helper_->GetBucketStart( |
| bucket_id, GetResultShmId(), GetResultShmOffset(), |
| buffer.size(), buffer.shm_id(), buffer.offset()); |
| WaitForCmd(); |
| uint32 size = *result; |
| data->resize(size); |
| if (size > 0u) { |
| uint32 offset = 0; |
| while (size) { |
| if (!buffer.valid()) { |
| buffer.Reset(size); |
| if (!buffer.valid()) { |
| return false; |
| } |
| helper_->GetBucketData( |
| bucket_id, offset, buffer.size(), buffer.shm_id(), buffer.offset()); |
| WaitForCmd(); |
| } |
| uint32 size_to_copy = std::min(size, buffer.size()); |
| memcpy(&(*data)[offset], buffer.address(), size_to_copy); |
| offset += size_to_copy; |
| size -= size_to_copy; |
| buffer.Release(); |
| }; |
| // Free the bucket. This is not required but it does free up the memory. |
| // and we don't have to wait for the result so from the client's perspective |
| // it's cheap. |
| helper_->SetBucketSize(bucket_id, 0); |
| } |
| return true; |
| } |
| |
| void GLES2Implementation::SetBucketContents( |
| uint32 bucket_id, const void* data, size_t size) { |
| GPU_DCHECK(data); |
| helper_->SetBucketSize(bucket_id, size); |
| if (size > 0u) { |
| uint32 offset = 0; |
| while (size) { |
| ScopedTransferBufferPtr buffer(size, helper_, transfer_buffer_); |
| if (!buffer.valid()) { |
| return; |
| } |
| memcpy(buffer.address(), static_cast<const int8*>(data) + offset, |
| buffer.size()); |
| helper_->SetBucketData( |
| bucket_id, offset, buffer.size(), buffer.shm_id(), buffer.offset()); |
| offset += buffer.size(); |
| size -= buffer.size(); |
| } |
| } |
| } |
| |
| void GLES2Implementation::SetBucketAsCString( |
| uint32 bucket_id, const char* str) { |
| // NOTE: strings are passed NULL terminated. That means the empty |
| // string will have a size of 1 and no-string will have a size of 0 |
| if (str) { |
| SetBucketContents(bucket_id, str, strlen(str) + 1); |
| } else { |
| helper_->SetBucketSize(bucket_id, 0); |
| } |
| } |
| |
| bool GLES2Implementation::GetBucketAsString( |
| uint32 bucket_id, std::string* str) { |
| GPU_DCHECK(str); |
| std::vector<int8> data; |
| // NOTE: strings are passed NULL terminated. That means the empty |
| // string will have a size of 1 and no-string will have a size of 0 |
| if (!GetBucketContents(bucket_id, &data)) { |
| return false; |
| } |
| if (data.empty()) { |
| return false; |
| } |
| str->assign(&data[0], &data[0] + data.size() - 1); |
| return true; |
| } |
| |
| void GLES2Implementation::SetBucketAsString( |
| uint32 bucket_id, const std::string& str) { |
| // NOTE: strings are passed NULL terminated. That means the empty |
| // string will have a size of 1 and no-string will have a size of 0 |
| SetBucketContents(bucket_id, str.c_str(), str.size() + 1); |
| } |
| |
| void GLES2Implementation::Disable(GLenum cap) { |
| GPU_CLIENT_SINGLE_THREAD_CHECK(); |
| GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glDisable(" |
| << GLES2Util::GetStringCapability(cap) << ")"); |
| bool changed = false; |
| if (!state_.SetCapabilityState(cap, false, &changed) || changed) { |
| helper_->Disable(cap); |
| } |
| CheckGLError(); |
| } |
| |
| void GLES2Implementation::Enable(GLenum cap) { |
| GPU_CLIENT_SINGLE_THREAD_CHECK(); |
| GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glEnable(" |
| << GLES2Util::GetStringCapability(cap) << ")"); |
| bool changed = false; |
| if (!state_.SetCapabilityState(cap, true, &changed) || changed) { |
| helper_->Enable(cap); |
| } |
| CheckGLError(); |
| } |
| |
| GLboolean GLES2Implementation::IsEnabled(GLenum cap) { |
| GPU_CLIENT_SINGLE_THREAD_CHECK(); |
| GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glIsEnabled(" |
| << GLES2Util::GetStringCapability(cap) << ")"); |
| bool state = false; |
| if (!state_.GetEnabled(cap, &state)) { |
| typedef cmds::IsEnabled::Result Result; |
| Result* result = GetResultAs<Result*>(); |
| if (!result) { |
| return GL_FALSE; |
| } |
| *result = 0; |
| helper_->IsEnabled(cap, GetResultShmId(), GetResultShmOffset()); |
| WaitForCmd(); |
| state = (*result) != 0; |
| } |
| |
| GPU_CLIENT_LOG("returned " << state); |
| CheckGLError(); |
| return state; |
| } |
| |
| bool GLES2Implementation::GetHelper(GLenum pname, GLint* params) { |
| switch (pname) { |
| case GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS: |
| *params = static_state_.int_state.max_combined_texture_image_units; |
| return true; |
| case GL_MAX_CUBE_MAP_TEXTURE_SIZE: |
| *params = static_state_.int_state.max_cube_map_texture_size; |
| return true; |
| case GL_MAX_FRAGMENT_UNIFORM_VECTORS: |
| *params = static_state_.int_state.max_fragment_uniform_vectors; |
| return true; |
| case GL_MAX_RENDERBUFFER_SIZE: |
| *params = static_state_.int_state.max_renderbuffer_size; |
| return true; |
| case GL_MAX_TEXTURE_IMAGE_UNITS: |
| *params = static_state_.int_state.max_texture_image_units; |
| return true; |
| case GL_MAX_TEXTURE_SIZE: |
| *params = static_state_.int_state.max_texture_size; |
| return true; |
| case GL_MAX_VARYING_VECTORS: |
| *params = static_state_.int_state.max_varying_vectors; |
| return true; |
| case GL_MAX_VERTEX_ATTRIBS: |
| *params = static_state_.int_state.max_vertex_attribs; |
| return true; |
| case GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS: |
| *params = static_state_.int_state.max_vertex_texture_image_units; |
| return true; |
| case GL_MAX_VERTEX_UNIFORM_VECTORS: |
| *params = static_state_.int_state.max_vertex_uniform_vectors; |
| return true; |
| case GL_NUM_COMPRESSED_TEXTURE_FORMATS: |
| *params = static_state_.int_state.num_compressed_texture_formats; |
| return true; |
| case GL_NUM_SHADER_BINARY_FORMATS: |
| *params = static_state_.int_state.num_shader_binary_formats; |
| return true; |
| case GL_ARRAY_BUFFER_BINDING: |
| if (share_group_->bind_generates_resource()) { |
| *params = bound_array_buffer_id_; |
| return true; |
| } |
| return false; |
| case GL_ELEMENT_ARRAY_BUFFER_BINDING: |
| if (share_group_->bind_generates_resource()) { |
| *params = |
| vertex_array_object_manager_->bound_element_array_buffer(); |
| return true; |
| } |
| return false; |
| case GL_PIXEL_PACK_TRANSFER_BUFFER_BINDING_CHROMIUM: |
| *params = bound_pixel_pack_transfer_buffer_id_; |
| return true; |
| case GL_PIXEL_UNPACK_TRANSFER_BUFFER_BINDING_CHROMIUM: |
| *params = bound_pixel_unpack_transfer_buffer_id_; |
| return true; |
| case GL_ACTIVE_TEXTURE: |
| *params = active_texture_unit_ + GL_TEXTURE0; |
| return true; |
| case GL_TEXTURE_BINDING_2D: |
| if (share_group_->bind_generates_resource()) { |
| *params = texture_units_[active_texture_unit_].bound_texture_2d; |
| return true; |
| } |
| return false; |
| case GL_TEXTURE_BINDING_CUBE_MAP: |
| if (share_group_->bind_generates_resource()) { |
| *params = texture_units_[active_texture_unit_].bound_texture_cube_map; |
| return true; |
| } |
| return false; |
| case GL_FRAMEBUFFER_BINDING: |
| if (share_group_->bind_generates_resource()) { |
| *params = bound_framebuffer_; |
| return true; |
| } |
| return false; |
| case GL_READ_FRAMEBUFFER_BINDING: |
| if (IsChromiumFramebufferMultisampleAvailable() && |
| share_group_->bind_generates_resource()) { |
| *params = bound_read_framebuffer_; |
| return true; |
| } |
| return false; |
| case GL_RENDERBUFFER_BINDING: |
| if (share_group_->bind_generates_resource()) { |
| *params = bound_renderbuffer_; |
| return true; |
| } |
| return false; |
| default: |
| return false; |
| } |
| } |
| |
| bool GLES2Implementation::GetBooleanvHelper(GLenum pname, GLboolean* params) { |
| // TODO(gman): Make this handle pnames that return more than 1 value. |
| GLint value; |
| if (!GetHelper(pname, &value)) { |
| return false; |
| } |
| *params = static_cast<GLboolean>(value); |
| return true; |
| } |
| |
| bool GLES2Implementation::GetFloatvHelper(GLenum pname, GLfloat* params) { |
| // TODO(gman): Make this handle pnames that return more than 1 value. |
| GLint value; |
| if (!GetHelper(pname, &value)) { |
| return false; |
| } |
| *params = static_cast<GLfloat>(value); |
| return true; |
| } |
| |
| bool GLES2Implementation::GetIntegervHelper(GLenum pname, GLint* params) { |
| return GetHelper(pname, params); |
| } |
| |
| GLuint GLES2Implementation::GetMaxValueInBufferCHROMIUMHelper( |
| GLuint buffer_id, GLsizei count, GLenum type, GLuint offset) { |
| typedef cmds::GetMaxValueInBufferCHROMIUM::Result Result; |
| Result* result = GetResultAs<Result*>(); |
| if (!result) { |
| return 0; |
| } |
| *result = 0; |
| helper_->GetMaxValueInBufferCHROMIUM( |
| buffer_id, count, type, offset, GetResultShmId(), GetResultShmOffset()); |
| WaitForCmd(); |
| return *result; |
| } |
| |
| GLuint GLES2Implementation::GetMaxValueInBufferCHROMIUM( |
| GLuint buffer_id, GLsizei count, GLenum type, GLuint offset) { |
| GPU_CLIENT_SINGLE_THREAD_CHECK(); |
| GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glGetMaxValueInBufferCHROMIUM(" |
| << buffer_id << ", " << count << ", " |
| << GLES2Util::GetStringGetMaxIndexType(type) |
| << ", " << offset << ")"); |
| GLuint result = GetMaxValueInBufferCHROMIUMHelper( |
| buffer_id, count, type, offset); |
| GPU_CLIENT_LOG("returned " << result); |
| CheckGLError(); |
| return result; |
| } |
| |
| void GLES2Implementation::RestoreElementAndArrayBuffers(bool restore) { |
| if (restore) { |
| RestoreArrayBuffer(restore); |
| // Restore the element array binding. |
| // We only need to restore it if it wasn't a client side array. |
| if (vertex_array_object_manager_->bound_element_array_buffer() == 0) { |
| helper_->BindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); |
| } |
| } |
| } |
| |
| void GLES2Implementation::RestoreArrayBuffer(bool restore) { |
| if (restore) { |
| // Restore the user's current binding. |
| helper_->BindBuffer(GL_ARRAY_BUFFER, bound_array_buffer_id_); |
| } |
| } |
| |
| void GLES2Implementation::DrawElements( |
| GLenum mode, GLsizei count, GLenum type, const void* indices) { |
| GPU_CLIENT_SINGLE_THREAD_CHECK(); |
| GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glDrawElements(" |
| << GLES2Util::GetStringDrawMode(mode) << ", " |
| << count << ", " |
| << GLES2Util::GetStringIndexType(type) << ", " |
| << static_cast<const void*>(indices) << ")"); |
| if (count < 0) { |
| SetGLError(GL_INVALID_VALUE, "glDrawElements", "count less than 0."); |
| return; |
| } |
| if (count == 0) { |
| return; |
| } |
| GLuint offset = 0; |
| bool simulated = false; |
| if (!vertex_array_object_manager_->SetupSimulatedIndexAndClientSideBuffers( |
| "glDrawElements", this, helper_, count, type, 0, indices, |
| &offset, &simulated)) { |
| return; |
| } |
| helper_->DrawElements(mode, count, type, offset); |
| RestoreElementAndArrayBuffers(simulated); |
| CheckGLError(); |
| } |
| |
| void GLES2Implementation::Flush() { |
| GPU_CLIENT_SINGLE_THREAD_CHECK(); |
| GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glFlush()"); |
| // Insert the cmd to call glFlush |
| helper_->Flush(); |
| // Flush our command buffer |
| // (tell the service to execute up to the flush cmd.) |
| helper_->CommandBufferHelper::Flush(); |
| } |
| |
| void GLES2Implementation::ShallowFlushCHROMIUM() { |
| GPU_CLIENT_SINGLE_THREAD_CHECK(); |
| GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glShallowFlushCHROMIUM()"); |
| // Flush our command buffer |
| // (tell the service to execute up to the flush cmd.) |
| helper_->CommandBufferHelper::Flush(); |
| } |
| |
| void GLES2Implementation::Finish() { |
| GPU_CLIENT_SINGLE_THREAD_CHECK(); |
| FinishHelper(); |
| } |
| |
| void GLES2Implementation::ShallowFinishCHROMIUM() { |
| GPU_CLIENT_SINGLE_THREAD_CHECK(); |
| // Flush our command buffer (tell the service to execute up to the flush cmd |
| // and don't return until it completes). |
| helper_->CommandBufferHelper::Finish(); |
| } |
| |
| bool GLES2Implementation::MustBeContextLost() { |
| bool context_lost = helper_->IsContextLost(); |
| if (!context_lost) { |
| WaitForCmd(); |
| context_lost = helper_->IsContextLost(); |
| } |
| GPU_CHECK(context_lost); |
| return context_lost; |
| } |
| |
| void GLES2Implementation::FinishHelper() { |
| GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glFinish()"); |
| TRACE_EVENT0("gpu", "GLES2::Finish"); |
| // Insert the cmd to call glFinish |
| helper_->Finish(); |
| // Finish our command buffer |
| // (tell the service to execute up to the Finish cmd and wait for it to |
| // execute.) |
| helper_->CommandBufferHelper::Finish(); |
| } |
| |
| void GLES2Implementation::SwapBuffers() { |
| GPU_CLIENT_SINGLE_THREAD_CHECK(); |
| GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glSwapBuffers()"); |
| // TODO(piman): Strictly speaking we'd want to insert the token after the |
| // swap, but the state update with the updated token might not have happened |
| // by the time the SwapBuffer callback gets called, forcing us to synchronize |
| // with the GPU process more than needed. So instead, make it happen before. |
| // All it means is that we could be slightly looser on the kMaxSwapBuffers |
| // semantics if the client doesn't use the callback mechanism, and by chance |
| // the scheduler yields between the InsertToken and the SwapBuffers. |
| swap_buffers_tokens_.push(helper_->InsertToken()); |
| helper_->SwapBuffers(); |
| helper_->CommandBufferHelper::Flush(); |
| // Wait if we added too many swap buffers. Add 1 to kMaxSwapBuffers to |
| // compensate for TODO above. |
| if (swap_buffers_tokens_.size() > kMaxSwapBuffers + 1) { |
| helper_->WaitForToken(swap_buffers_tokens_.front()); |
| swap_buffers_tokens_.pop(); |
| } |
| } |
| |
| void GLES2Implementation::GenSharedIdsCHROMIUM( |
| GLuint namespace_id, GLuint id_offset, GLsizei n, GLuint* ids) { |
| GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glGenSharedIdsCHROMIUM(" |
| << namespace_id << ", " << id_offset << ", " << n << ", " << |
| static_cast<void*>(ids) << ")"); |
| TRACE_EVENT0("gpu", "GLES2::GenSharedIdsCHROMIUM"); |
| GLsizei num = n; |
| GLuint* dst = ids; |
| while (num) { |
| ScopedTransferBufferArray<GLint> id_buffer(num, helper_, transfer_buffer_); |
| if (!id_buffer.valid()) { |
| return; |
| } |
| helper_->GenSharedIdsCHROMIUM( |
| namespace_id, id_offset, id_buffer.num_elements(), |
| id_buffer.shm_id(), id_buffer.offset()); |
| WaitForCmd(); |
| memcpy(dst, id_buffer.address(), sizeof(*dst) * id_buffer.num_elements()); |
| num -= id_buffer.num_elements(); |
| dst += id_buffer.num_elements(); |
| } |
| GPU_CLIENT_LOG_CODE_BLOCK({ |
| for (GLsizei i = 0; i < n; ++i) { |
| GPU_CLIENT_LOG(" " << i << ": " << namespace_id << ", " << ids[i]); |
| } |
| }); |
| } |
| |
| void GLES2Implementation::DeleteSharedIdsCHROMIUM( |
| GLuint namespace_id, GLsizei n, const GLuint* ids) { |
| GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glDeleteSharedIdsCHROMIUM(" |
| << namespace_id << ", " << n << ", " |
| << static_cast<const void*>(ids) << ")"); |
| GPU_CLIENT_LOG_CODE_BLOCK({ |
| for (GLsizei i = 0; i < n; ++i) { |
| GPU_CLIENT_LOG(" " << i << ": " << namespace_id << ", " << ids[i]); |
| } |
| }); |
| TRACE_EVENT0("gpu", "GLES2::DeleteSharedIdsCHROMIUM"); |
| while (n) { |
| ScopedTransferBufferArray<GLint> id_buffer(n, helper_, transfer_buffer_); |
| if (!id_buffer.valid()) { |
| return; |
| } |
| memcpy(id_buffer.address(), ids, sizeof(*ids) * id_buffer.num_elements()); |
| helper_->DeleteSharedIdsCHROMIUM( |
| namespace_id, id_buffer.num_elements(), |
| id_buffer.shm_id(), id_buffer.offset()); |
| WaitForCmd(); |
| n -= id_buffer.num_elements(); |
| ids += id_buffer.num_elements(); |
| } |
| } |
| |
| void GLES2Implementation::RegisterSharedIdsCHROMIUM( |
| GLuint namespace_id, GLsizei n, const GLuint* ids) { |
| GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glRegisterSharedIdsCHROMIUM(" |
| << namespace_id << ", " << n << ", " |
| << static_cast<const void*>(ids) << ")"); |
| GPU_CLIENT_LOG_CODE_BLOCK({ |
| for (GLsizei i = 0; i < n; ++i) { |
| GPU_CLIENT_LOG(" " << i << ": " << namespace_id << ", " << ids[i]); |
| } |
| }); |
| TRACE_EVENT0("gpu", "GLES2::RegisterSharedIdsCHROMIUM"); |
| while (n) { |
| ScopedTransferBufferArray<GLint> id_buffer(n, helper_, transfer_buffer_); |
| if (!id_buffer.valid()) { |
| return; |
| } |
| memcpy(id_buffer.address(), ids, sizeof(*ids) * id_buffer.num_elements()); |
| helper_->RegisterSharedIdsCHROMIUM( |
| namespace_id, id_buffer.num_elements(), |
| id_buffer.shm_id(), id_buffer.offset()); |
| WaitForCmd(); |
| n -= id_buffer.num_elements(); |
| ids += id_buffer.num_elements(); |
| } |
| } |
| |
| void GLES2Implementation::BindAttribLocation( |
| GLuint program, GLuint index, const char* name) { |
| GPU_CLIENT_SINGLE_THREAD_CHECK(); |
| GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glBindAttribLocation(" |
| << program << ", " << index << ", " << name << ")"); |
| SetBucketAsString(kResultBucketId, name); |
| helper_->BindAttribLocationBucket(program, index, kResultBucketId); |
| helper_->SetBucketSize(kResultBucketId, 0); |
| CheckGLError(); |
| } |
| |
| void GLES2Implementation::BindUniformLocationCHROMIUM( |
| GLuint program, GLint location, const char* name) { |
| GPU_CLIENT_SINGLE_THREAD_CHECK(); |
| GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glBindUniformLocationCHROMIUM(" |
| << program << ", " << location << ", " << name << ")"); |
| SetBucketAsString(kResultBucketId, name); |
| helper_->BindUniformLocationCHROMIUMBucket( |
| program, location, kResultBucketId); |
| helper_->SetBucketSize(kResultBucketId, 0); |
| CheckGLError(); |
| } |
| |
| void GLES2Implementation::GetVertexAttribPointerv( |
| GLuint index, GLenum pname, void** ptr) { |
| GPU_CLIENT_SINGLE_THREAD_CHECK(); |
| GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glGetVertexAttribPointer(" |
| << index << ", " << GLES2Util::GetStringVertexPointer(pname) << ", " |
| << static_cast<void*>(ptr) << ")"); |
| GPU_CLIENT_LOG_CODE_BLOCK(int32 num_results = 1); |
| if (!vertex_array_object_manager_->GetAttribPointer(index, pname, ptr)) { |
| TRACE_EVENT0("gpu", "GLES2::GetVertexAttribPointerv"); |
| typedef cmds::GetVertexAttribPointerv::Result Result; |
| Result* result = GetResultAs<Result*>(); |
| if (!result) { |
| return; |
| } |
| result->SetNumResults(0); |
| helper_->GetVertexAttribPointerv( |
| index, pname, GetResultShmId(), GetResultShmOffset()); |
| WaitForCmd(); |
| result->CopyResult(ptr); |
| GPU_CLIENT_LOG_CODE_BLOCK(num_results = result->GetNumResults()); |
| } |
| GPU_CLIENT_LOG_CODE_BLOCK({ |
| for (int32 i = 0; i < num_results; ++i) { |
| GPU_CLIENT_LOG(" " << i << ": " << ptr[i]); |
| } |
| }); |
| CheckGLError(); |
| } |
| |
| bool GLES2Implementation::DeleteProgramHelper(GLuint program) { |
| if (!GetIdHandler(id_namespaces::kProgramsAndShaders)->FreeIds( |
| this, 1, &program, &GLES2Implementation::DeleteProgramStub)) { |
| SetGLError( |
| GL_INVALID_VALUE, |
| "glDeleteProgram", "id not created by this context."); |
| return false; |
| } |
| if (program == current_program_) { |
| current_program_ = 0; |
| } |
| return true; |
| } |
| |
| void GLES2Implementation::DeleteProgramStub( |
| GLsizei n, const GLuint* programs) { |
| GPU_DCHECK_EQ(1, n); |
| share_group_->program_info_manager()->DeleteInfo(programs[0]); |
| helper_->DeleteProgram(programs[0]); |
| } |
| |
| bool GLES2Implementation::DeleteShaderHelper(GLuint shader) { |
| if (!GetIdHandler(id_namespaces::kProgramsAndShaders)->FreeIds( |
| this, 1, &shader, &GLES2Implementation::DeleteShaderStub)) { |
| SetGLError( |
| GL_INVALID_VALUE, |
| "glDeleteShader", "id not created by this context."); |
| return false; |
| } |
| return true; |
| } |
| |
| void GLES2Implementation::DeleteShaderStub( |
| GLsizei n, const GLuint* shaders) { |
| GPU_DCHECK_EQ(1, n); |
| share_group_->program_info_manager()->DeleteInfo(shaders[0]); |
| helper_->DeleteShader(shaders[0]); |
| } |
| |
| |
| GLint GLES2Implementation::GetAttribLocationHelper( |
| GLuint program, const char* name) { |
| typedef cmds::GetAttribLocationBucket::Result Result; |
| Result* result = GetResultAs<Result*>(); |
| if (!result) { |
| return -1; |
| } |
| *result = -1; |
| SetBucketAsCString(kResultBucketId, name); |
| helper_->GetAttribLocationBucket( |
| program, kResultBucketId, GetResultShmId(), GetResultShmOffset()); |
| WaitForCmd(); |
| helper_->SetBucketSize(kResultBucketId, 0); |
| return *result; |
| } |
| |
| GLint GLES2Implementation::GetAttribLocation( |
| GLuint program, const char* name) { |
| GPU_CLIENT_SINGLE_THREAD_CHECK(); |
| GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glGetAttribLocation(" << program |
| << ", " << name << ")"); |
| TRACE_EVENT0("gpu", "GLES2::GetAttribLocation"); |
| GLint loc = share_group_->program_info_manager()->GetAttribLocation( |
| this, program, name); |
| GPU_CLIENT_LOG("returned " << loc); |
| CheckGLError(); |
| return loc; |
| } |
| |
| GLint GLES2Implementation::GetUniformLocationHelper( |
| GLuint program, const char* name) { |
| typedef cmds::GetUniformLocationBucket::Result Result; |
| Result* result = GetResultAs<Result*>(); |
| if (!result) { |
| return -1; |
| } |
| *result = -1; |
| SetBucketAsCString(kResultBucketId, name); |
| helper_->GetUniformLocationBucket(program, kResultBucketId, |
| GetResultShmId(), GetResultShmOffset()); |
| WaitForCmd(); |
| helper_->SetBucketSize(kResultBucketId, 0); |
| return *result; |
| } |
| |
| GLint GLES2Implementation::GetUniformLocation( |
| GLuint program, const char* name) { |
| GPU_CLIENT_SINGLE_THREAD_CHECK(); |
| GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glGetUniformLocation(" << program |
| << ", " << name << ")"); |
| TRACE_EVENT0("gpu", "GLES2::GetUniformLocation"); |
| GLint loc = share_group_->program_info_manager()->GetUniformLocation( |
| this, program, name); |
| GPU_CLIENT_LOG("returned " << loc); |
| CheckGLError(); |
| return loc; |
| } |
| |
| void GLES2Implementation::UseProgram(GLuint program) { |
| GPU_CLIENT_SINGLE_THREAD_CHECK(); |
| GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glUseProgram(" << program << ")"); |
| if (current_program_ != program) { |
| current_program_ = program; |
| helper_->UseProgram(program); |
| } |
| CheckGLError(); |
| } |
| |
| bool GLES2Implementation::GetProgramivHelper( |
| GLuint program, GLenum pname, GLint* params) { |
| bool got_value = share_group_->program_info_manager()->GetProgramiv( |
| this, program, pname, params); |
| GPU_CLIENT_LOG_CODE_BLOCK({ |
| if (got_value) { |
| GPU_CLIENT_LOG(" 0: " << *params); |
| } |
| }); |
| return got_value; |
| } |
| |
| void GLES2Implementation::LinkProgram(GLuint program) { |
| GPU_CLIENT_SINGLE_THREAD_CHECK(); |
| GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glLinkProgram(" << program << ")"); |
| helper_->LinkProgram(program); |
| share_group_->program_info_manager()->CreateInfo(program); |
| CheckGLError(); |
| } |
| |
| void GLES2Implementation::ShaderBinary( |
| GLsizei n, const GLuint* shaders, GLenum binaryformat, const void* binary, |
| GLsizei length) { |
| GPU_CLIENT_SINGLE_THREAD_CHECK(); |
| GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glShaderBinary(" << n << ", " |
| << static_cast<const void*>(shaders) << ", " |
| << GLES2Util::GetStringEnum(binaryformat) << ", " |
| << static_cast<const void*>(binary) << ", " |
| << length << ")"); |
| if (n < 0) { |
| SetGLError(GL_INVALID_VALUE, "glShaderBinary", "n < 0."); |
| return; |
| } |
| if (length < 0) { |
| SetGLError(GL_INVALID_VALUE, "glShaderBinary", "length < 0."); |
| return; |
| } |
| // TODO(gman): ShaderBinary should use buckets. |
| unsigned int shader_id_size = n * sizeof(*shaders); |
| ScopedTransferBufferArray<GLint> buffer( |
| shader_id_size + length, helper_, transfer_buffer_); |
| if (!buffer.valid() || buffer.num_elements() != shader_id_size + length) { |
| SetGLError(GL_OUT_OF_MEMORY, "glShaderBinary", "out of memory."); |
| return; |
| } |
| void* shader_ids = buffer.elements(); |
| void* shader_data = buffer.elements() + shader_id_size; |
| memcpy(shader_ids, shaders, shader_id_size); |
| memcpy(shader_data, binary, length); |
| helper_->ShaderBinary( |
| n, |
| buffer.shm_id(), |
| buffer.offset(), |
| binaryformat, |
| buffer.shm_id(), |
| buffer.offset() + shader_id_size, |
| length); |
| CheckGLError(); |
| } |
| |
| void GLES2Implementation::PixelStorei(GLenum pname, GLint param) { |
| GPU_CLIENT_SINGLE_THREAD_CHECK(); |
| GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glPixelStorei(" |
| << GLES2Util::GetStringPixelStore(pname) << ", " |
| << param << ")"); |
| switch (pname) { |
| case GL_PACK_ALIGNMENT: |
| pack_alignment_ = param; |
| break; |
| case GL_UNPACK_ALIGNMENT: |
| unpack_alignment_ = param; |
| break; |
| case GL_UNPACK_ROW_LENGTH: |
| unpack_row_length_ = param; |
| return; |
| case GL_UNPACK_SKIP_ROWS: |
| unpack_skip_rows_ = param; |
| return; |
| case GL_UNPACK_SKIP_PIXELS: |
| unpack_skip_pixels_ = param; |
| return; |
| case GL_UNPACK_FLIP_Y_CHROMIUM: |
| unpack_flip_y_ = (param != 0); |
| break; |
| case GL_PACK_REVERSE_ROW_ORDER_ANGLE: |
| pack_reverse_row_order_ = |
| IsAnglePackReverseRowOrderAvailable() ? (param != 0) : false; |
| break; |
| default: |
| break; |
| } |
| helper_->PixelStorei(pname, param); |
| CheckGLError(); |
| } |
| |
| |
| void GLES2Implementation::VertexAttribPointer( |
| GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, |
| const void* ptr) { |
| GPU_CLIENT_SINGLE_THREAD_CHECK(); |
| GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glVertexAttribPointer(" |
| << index << ", " |
| << size << ", " |
| << GLES2Util::GetStringVertexAttribType(type) << ", " |
| << GLES2Util::GetStringBool(normalized) << ", " |
| << stride << ", " |
| << static_cast<const void*>(ptr) << ")"); |
| // Record the info on the client side. |
| if (!vertex_array_object_manager_->SetAttribPointer( |
| bound_array_buffer_id_, index, size, type, normalized, stride, ptr)) { |
| SetGLError(GL_INVALID_OPERATION, "glVertexAttribPointer", |
| "client side arrays are not allowed in vertex array objects."); |
| return; |
| } |
| #if defined(GLES2_SUPPORT_CLIENT_SIDE_ARRAYS) |
| if (bound_array_buffer_id_ != 0) { |
| // Only report NON client side buffers to the service. |
| helper_->VertexAttribPointer(index, size, type, normalized, stride, |
| ToGLuint(ptr)); |
| } |
| #else // !defined(GLES2_SUPPORT_CLIENT_SIDE_ARRAYS) |
| helper_->VertexAttribPointer(index, size, type, normalized, stride, |
| ToGLuint(ptr)); |
| #endif // !defined(GLES2_SUPPORT_CLIENT_SIDE_ARRAYS) |
| CheckGLError(); |
| } |
| |
| void GLES2Implementation::VertexAttribDivisorANGLE( |
| GLuint index, GLuint divisor) { |
| GPU_CLIENT_SINGLE_THREAD_CHECK(); |
| GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glVertexAttribDivisorANGLE(" |
| << index << ", " |
| << divisor << ") "); |
| // Record the info on the client side. |
| vertex_array_object_manager_->SetAttribDivisor(index, divisor); |
| helper_->VertexAttribDivisorANGLE(index, divisor); |
| CheckGLError(); |
| } |
| |
| void GLES2Implementation::ShaderSource( |
| GLuint shader, GLsizei count, const GLchar* const* source, const GLint* length) { |
| GPU_CLIENT_SINGLE_THREAD_CHECK(); |
| GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glShaderSource(" |
| << shader << ", " << count << ", " |
| << static_cast<const void*>(source) << ", " |
| << static_cast<const void*>(length) << ")"); |
| GPU_CLIENT_LOG_CODE_BLOCK({ |
| for (GLsizei ii = 0; ii < count; ++ii) { |
| if (source[ii]) { |
| if (length && length[ii] >= 0) { |
| std::string str(source[ii], length[ii]); |
| GPU_CLIENT_LOG(" " << ii << ": ---\n" << str << "\n---"); |
| } else { |
| GPU_CLIENT_LOG(" " << ii << ": ---\n" << source[ii] << "\n---"); |
| } |
| } else { |
| GPU_CLIENT_LOG(" " << ii << ": NULL"); |
| } |
| } |
| }); |
| if (count < 0) { |
| SetGLError(GL_INVALID_VALUE, "glShaderSource", "count < 0"); |
| return; |
| } |
| if (shader == 0) { |
| SetGLError(GL_INVALID_VALUE, "glShaderSource", "shader == 0"); |
| return; |
| } |
| |
| // Compute the total size. |
| uint32 total_size = 1; |
| for (GLsizei ii = 0; ii < count; ++ii) { |
| if (source[ii]) { |
| total_size += (length && length[ii] >= 0) ? |
| static_cast<size_t>(length[ii]) : strlen(source[ii]); |
| } |
| } |
| |
| // Concatenate all the strings in to a bucket on the service. |
| helper_->SetBucketSize(kResultBucketId, total_size); |
| uint32 offset = 0; |
| for (GLsizei ii = 0; ii <= count; ++ii) { |
| const char* src = ii < count ? source[ii] : ""; |
| if (src) { |
| uint32 size = ii < count ? |
| (length ? static_cast<size_t>(length[ii]) : strlen(src)) : 1; |
| while (size) { |
| ScopedTransferBufferPtr buffer(size, helper_, transfer_buffer_); |
| if (!buffer.valid()) { |
| return; |
| } |
| memcpy(buffer.address(), src, buffer.size()); |
| helper_->SetBucketData(kResultBucketId, offset, buffer.size(), |
| buffer.shm_id(), buffer.offset()); |
| offset += buffer.size(); |
| src += buffer.size(); |
| size -= buffer.size(); |
| } |
| } |
| } |
| |
| GPU_DCHECK_EQ(total_size, offset); |
| |
| helper_->ShaderSourceBucket(shader, kResultBucketId); |
| helper_->SetBucketSize(kResultBucketId, 0); |
| CheckGLError(); |
| } |
| |
| void GLES2Implementation::BufferDataHelper( |
| GLenum target, GLsizeiptr size, const void* data, GLenum usage) { |
| if (size < 0) { |
| SetGLError(GL_INVALID_VALUE, "glBufferData", "size < 0"); |
| return; |
| } |
| |
| GLuint buffer_id; |
| if (GetBoundPixelTransferBuffer(target, "glBufferData", &buffer_id)) { |
| if (!buffer_id) { |
| return; |
| } |
| |
| BufferTracker::Buffer* buffer = buffer_tracker_->GetBuffer(buffer_id); |
| if (buffer) { |
| // Free buffer memory, pending the passage of a token. |
| buffer_tracker_->FreePendingToken(buffer, helper_->InsertToken()); |
| |
| // Remove old buffer. |
| buffer_tracker_->RemoveBuffer(buffer_id); |
| } |
| |
| // Create new buffer. |
| buffer = buffer_tracker_->CreateBuffer(buffer_id, size); |
| GPU_DCHECK(buffer); |
| if (buffer->address() && data) |
| memcpy(buffer->address(), data, size); |
| return; |
| } |
| |
| if (size == 0) { |
| return; |
| } |
| |
| // If there is no data just send BufferData |
| if (!data) { |
| helper_->BufferData(target, size, 0, 0, usage); |
| return; |
| } |
| |
| // See if we can send all at once. |
| ScopedTransferBufferPtr buffer(size, helper_, transfer_buffer_); |
| if (!buffer.valid()) { |
| return; |
| } |
| |
| if (buffer.size() >= static_cast<unsigned int>(size)) { |
| memcpy(buffer.address(), data, size); |
| helper_->BufferData( |
| target, |
| size, |
| buffer.shm_id(), |
| buffer.offset(), |
| usage); |
| return; |
| } |
| |
| // Make the buffer with BufferData then send via BufferSubData |
| helper_->BufferData(target, size, 0, 0, usage); |
| BufferSubDataHelperImpl(target, 0, size, data, &buffer); |
| CheckGLError(); |
| } |
| |
| void GLES2Implementation::BufferData( |
| GLenum target, GLsizeiptr size, const void* data, GLenum usage) { |
| GPU_CLIENT_SINGLE_THREAD_CHECK(); |
| GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glBufferData(" |
| << GLES2Util::GetStringBufferTarget(target) << ", " |
| << size << ", " |
| << static_cast<const void*>(data) << ", " |
| << GLES2Util::GetStringBufferUsage(usage) << ")"); |
| BufferDataHelper(target, size, data, usage); |
| CheckGLError(); |
| } |
| |
| void GLES2Implementation::BufferSubDataHelper( |
| GLenum target, GLintptr offset, GLsizeiptr size, const void* data) { |
| if (size == 0) { |
| return; |
| } |
| |
| if (size < 0) { |
| SetGLError(GL_INVALID_VALUE, "glBufferSubData", "size < 0"); |
| return; |
| } |
| |
| GLuint buffer_id; |
| if (GetBoundPixelTransferBuffer(target, "glBufferSubData", &buffer_id)) { |
| if (!buffer_id) { |
| return; |
| } |
| BufferTracker::Buffer* buffer = buffer_tracker_->GetBuffer(buffer_id); |
| if (!buffer) { |
| SetGLError(GL_INVALID_VALUE, "glBufferSubData", "unknown buffer"); |
| return; |
| } |
| |
| int32 end = 0; |
| int32 buffer_size = buffer->size(); |
| if (!SafeAddInt32(offset, size, &end) || end > buffer_size) { |
| SetGLError(GL_INVALID_VALUE, "glBufferSubData", "out of range"); |
| return; |
| } |
| |
| if (buffer->address() && data) |
| memcpy(static_cast<uint8*>(buffer->address()) + offset, data, size); |
| return; |
| } |
| |
| ScopedTransferBufferPtr buffer(size, helper_, transfer_buffer_); |
| BufferSubDataHelperImpl(target, offset, size, data, &buffer); |
| } |
| |
| void GLES2Implementation::BufferSubDataHelperImpl( |
| GLenum target, GLintptr offset, GLsizeiptr size, const void* data, |
| ScopedTransferBufferPtr* buffer) { |
| GPU_DCHECK(buffer); |
| GPU_DCHECK_GT(size, 0); |
| |
| const int8* source = static_cast<const int8*>(data); |
| while (size) { |
| if (!buffer->valid() || buffer->size() == 0) { |
| buffer->Reset(size); |
| if (!buffer->valid()) { |
| return; |
| } |
| } |
| memcpy(buffer->address(), source, buffer->size()); |
| helper_->BufferSubData( |
| target, offset, buffer->size(), buffer->shm_id(), buffer->offset()); |
| offset += buffer->size(); |
| source += buffer->size(); |
| size -= buffer->size(); |
| buffer->Release(); |
| } |
| } |
| |
| void GLES2Implementation::BufferSubData( |
| GLenum target, GLintptr offset, GLsizeiptr size, const void* data) { |
| GPU_CLIENT_SINGLE_THREAD_CHECK(); |
| GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glBufferSubData(" |
| << GLES2Util::GetStringBufferTarget(target) << ", " |
| << offset << ", " << size << ", " |
| << static_cast<const void*>(data) << ")"); |
| BufferSubDataHelper(target, offset, size, data); |
| CheckGLError(); |
| } |
| |
| bool GLES2Implementation::GetBoundPixelTransferBuffer( |
| GLenum target, |
| const char* function_name, |
| GLuint* buffer_id) { |
| *buffer_id = 0; |
| |
| switch (target) { |
| case GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM: |
| *buffer_id = bound_pixel_pack_transfer_buffer_id_; |
| break; |
| case GL_PIXEL_UNPACK_TRANSFER_BUFFER_CHROMIUM: |
| *buffer_id = bound_pixel_unpack_transfer_buffer_id_; |
| break; |
| default: |
| // Unknown target |
| return false; |
| } |
| if (!*buffer_id) { |
| SetGLError(GL_INVALID_OPERATION, function_name, "no buffer bound"); |
| } |
| return true; |
| } |
| |
| BufferTracker::Buffer* |
| GLES2Implementation::GetBoundPixelUnpackTransferBufferIfValid( |
| GLuint buffer_id, |
| const char* function_name, |
| GLuint offset, GLsizei size) |
| { |
| DCHECK(buffer_id); |
| BufferTracker::Buffer* buffer = buffer_tracker_->GetBuffer(buffer_id); |
| if (!buffer) { |
| SetGLError(GL_INVALID_OPERATION, function_name, "invalid buffer"); |
| return NULL; |
| } |
| if (buffer->mapped()) { |
| SetGLError(GL_INVALID_OPERATION, function_name, "buffer mapped"); |
| return NULL; |
| } |
| if ((buffer->size() - offset) < static_cast<GLuint>(size)) { |
| SetGLError(GL_INVALID_VALUE, function_name, "unpack size to large"); |
| return NULL; |
| } |
| return buffer; |
| } |
| |
| void GLES2Implementation::CompressedTexImage2D( |
| GLenum target, GLint level, GLenum internalformat, GLsizei width, |
| GLsizei height, GLint border, GLsizei image_size, const void* data) { |
| GPU_CLIENT_SINGLE_THREAD_CHECK(); |
| GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glCompressedTexImage2D(" |
| << GLES2Util::GetStringTextureTarget(target) << ", " |
| << level << ", " |
| << GLES2Util::GetStringCompressedTextureFormat(internalformat) << ", " |
| << width << ", " << height << ", " << border << ", " |
| << image_size << ", " |
| << static_cast<const void*>(data) << ")"); |
| if (width < 0 || height < 0 || level < 0) { |
| SetGLError(GL_INVALID_VALUE, "glCompressedTexImage2D", "dimension < 0"); |
| return; |
| } |
| if (height == 0 || width == 0) { |
| return; |
| } |
| // If there's a pixel unpack buffer bound use it when issuing |
| // CompressedTexImage2D. |
| if (bound_pixel_unpack_transfer_buffer_id_) { |
| GLuint offset = ToGLuint(data); |
| BufferTracker::Buffer* buffer = GetBoundPixelUnpackTransferBufferIfValid( |
| bound_pixel_unpack_transfer_buffer_id_, |
| "glCompressedTexImage2D", offset, image_size); |
| if (buffer && buffer->shm_id() != -1) { |
| helper_->CompressedTexImage2D( |
| target, level, internalformat, width, height, border, image_size, |
| buffer->shm_id(), buffer->shm_offset() + offset); |
| buffer->set_transfer_ready_token(helper_->InsertToken()); |
| } |
| return; |
| } |
| SetBucketContents(kResultBucketId, data, image_size); |
| helper_->CompressedTexImage2DBucket( |
| target, level, internalformat, width, height, border, kResultBucketId); |
| // Free the bucket. This is not required but it does free up the memory. |
| // and we don't have to wait for the result so from the client's perspective |
| // it's cheap. |
| helper_->SetBucketSize(kResultBucketId, 0); |
| CheckGLError(); |
| } |
| |
| void GLES2Implementation::CompressedTexSubImage2D( |
| GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, |
| GLsizei height, GLenum format, GLsizei image_size, const void* data) { |
| GPU_CLIENT_SINGLE_THREAD_CHECK(); |
| GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glCompressedTexSubImage2D(" |
| << GLES2Util::GetStringTextureTarget(target) << ", " |
| << level << ", " |
| << xoffset << ", " << yoffset << ", " |
| << width << ", " << height << ", " |
| << GLES2Util::GetStringCompressedTextureFormat(format) << ", " |
| << image_size << ", " |
| << static_cast<const void*>(data) << ")"); |
| if (width < 0 || height < 0 || level < 0) { |
| SetGLError(GL_INVALID_VALUE, "glCompressedTexSubImage2D", "dimension < 0"); |
| return; |
| } |
| // If there's a pixel unpack buffer bound use it when issuing |
| // CompressedTexSubImage2D. |
| if (bound_pixel_unpack_transfer_buffer_id_) { |
| GLuint offset = ToGLuint(data); |
| BufferTracker::Buffer* buffer = GetBoundPixelUnpackTransferBufferIfValid( |
| bound_pixel_unpack_transfer_buffer_id_, |
| "glCompressedTexSubImage2D", offset, image_size); |
| if (buffer && buffer->shm_id() != -1) { |
| helper_->CompressedTexSubImage2D( |
| target, level, xoffset, yoffset, width, height, format, image_size, |
| buffer->shm_id(), buffer->shm_offset() + offset); |
| buffer->set_transfer_ready_token(helper_->InsertToken()); |
| CheckGLError(); |
| } |
| return; |
| } |
| SetBucketContents(kResultBucketId, data, image_size); |
| helper_->CompressedTexSubImage2DBucket( |
| target, level, xoffset, yoffset, width, height, format, kResultBucketId); |
| // Free the bucket. This is not required but it does free up the memory. |
| // and we don't have to wait for the result so from the client's perspective |
| // it's cheap. |
| helper_->SetBucketSize(kResultBucketId, 0); |
| CheckGLError(); |
| } |
| |
| namespace { |
| |
| void CopyRectToBuffer( |
| const void* pixels, |
| uint32 height, |
| uint32 unpadded_row_size, |
| uint32 pixels_padded_row_size, |
| bool flip_y, |
| void* buffer, |
| uint32 buffer_padded_row_size) { |
| const int8* source = static_cast<const int8*>(pixels); |
| int8* dest = static_cast<int8*>(buffer); |
| if (flip_y || pixels_padded_row_size != buffer_padded_row_size) { |
| if (flip_y) { |
| dest += buffer_padded_row_size * (height - 1); |
| } |
| // the last row is copied unpadded at the end |
| for (; height > 1; --height) { |
| memcpy(dest, source, buffer_padded_row_size); |
| if (flip_y) { |
| dest -= buffer_padded_row_size; |
| } else { |
| dest += buffer_padded_row_size; |
| } |
| source += pixels_padded_row_size; |
| } |
| memcpy(dest, source, unpadded_row_size); |
| } else { |
| uint32 size = (height - 1) * pixels_padded_row_size + unpadded_row_size; |
| memcpy(dest, source, size); |
| } |
| } |
| |
| } // anonymous namespace |
| |
| void GLES2Implementation::TexImage2D( |
| GLenum target, GLint level, GLint internalformat, GLsizei width, |
| GLsizei height, GLint border, GLenum format, GLenum type, |
| const void* pixels) { |
| GPU_CLIENT_SINGLE_THREAD_CHECK(); |
| GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glTexImage2D(" |
| << GLES2Util::GetStringTextureTarget(target) << ", " |
| << level << ", " |
| << GLES2Util::GetStringTextureInternalFormat(internalformat) << ", " |
| << width << ", " << height << ", " << border << ", " |
| << GLES2Util::GetStringTextureFormat(format) << ", " |
| << GLES2Util::GetStringPixelType(type) << ", " |
| << static_cast<const void*>(pixels) << ")"); |
| if (level < 0 || height < 0 || width < 0) { |
| SetGLError(GL_INVALID_VALUE, "glTexImage2D", "dimension < 0"); |
| return; |
| } |
| uint32 size; |
| uint32 unpadded_row_size; |
| uint32 padded_row_size; |
| if (!GLES2Util::ComputeImageDataSizes( |
| width, height, format, type, unpack_alignment_, &size, |
| &unpadded_row_size, &padded_row_size)) { |
| SetGLError(GL_INVALID_VALUE, "glTexImage2D", "image size too large"); |
| return; |
| } |
| |
| // If there's a pixel unpack buffer bound use it when issuing TexImage2D. |
| if (bound_pixel_unpack_transfer_buffer_id_) { |
| GLuint offset = ToGLuint(pixels); |
| BufferTracker::Buffer* buffer = GetBoundPixelUnpackTransferBufferIfValid( |
| bound_pixel_unpack_transfer_buffer_id_, |
| "glTexImage2D", offset, size); |
| if (buffer && buffer->shm_id() != -1) { |
| helper_->TexImage2D( |
| target, level, internalformat, width, height, border, format, type, |
| buffer->shm_id(), buffer->shm_offset() + offset); |
| buffer->set_transfer_ready_token(helper_->InsertToken()); |
| CheckGLError(); |
| } |
| return; |
| } |
| |
| // If there's no data just issue TexImage2D |
| if (!pixels) { |
| helper_->TexImage2D( |
| target, level, internalformat, width, height, border, format, type, |
| 0, 0); |
| CheckGLError(); |
| return; |
| } |
| |
| // compute the advance bytes per row for the src pixels |
| uint32 src_padded_row_size; |
| if (unpack_row_length_ > 0) { |
| if (!GLES2Util::ComputeImagePaddedRowSize( |
| unpack_row_length_, format, type, unpack_alignment_, |
| &src_padded_row_size)) { |
| SetGLError( |
| GL_INVALID_VALUE, "glTexImage2D", "unpack row length too large"); |
| return; |
| } |
| } else { |
| src_padded_row_size = padded_row_size; |
| } |
| |
| // advance pixels pointer past the skip rows and skip pixels |
| pixels = reinterpret_cast<const int8*>(pixels) + |
| unpack_skip_rows_ * src_padded_row_size; |
| if (unpack_skip_pixels_) { |
| uint32 group_size = GLES2Util::ComputeImageGroupSize(format, type); |
| pixels = reinterpret_cast<const int8*>(pixels) + |
| unpack_skip_pixels_ * group_size; |
| } |
| |
| // Check if we can send it all at once. |
| ScopedTransferBufferPtr buffer(size, helper_, transfer_buffer_); |
| if (!buffer.valid()) { |
| return; |
| } |
| |
| if (buffer.size() >= size) { |
| CopyRectToBuffer( |
| pixels, height, unpadded_row_size, src_padded_row_size, unpack_flip_y_, |
| buffer.address(), padded_row_size); |
| helper_->TexImage2D( |
| target, level, internalformat, width, height, border, format, type, |
| buffer.shm_id(), buffer.offset()); |
| CheckGLError(); |
| return; |
| } |
| |
| // No, so send it using TexSubImage2D. |
| helper_->TexImage2D( |
| target, level, internalformat, width, height, border, format, type, |
| 0, 0); |
| TexSubImage2DImpl( |
| target, level, 0, 0, width, height, format, type, unpadded_row_size, |
| pixels, src_padded_row_size, GL_TRUE, &buffer, padded_row_size); |
| CheckGLError(); |
| } |
| |
| void GLES2Implementation::TexSubImage2D( |
| GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, |
| GLsizei height, GLenum format, GLenum type, const void* pixels) { |
| GPU_CLIENT_SINGLE_THREAD_CHECK(); |
| GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glTexSubImage2D(" |
| << GLES2Util::GetStringTextureTarget(target) << ", " |
| << level << ", " |
| << xoffset << ", " << yoffset << ", " |
| << width << ", " << height << ", " |
| << GLES2Util::GetStringTextureFormat(format) << ", " |
| << GLES2Util::GetStringPixelType(type) << ", " |
| << static_cast<const void*>(pixels) << ")"); |
| |
| if (level < 0 || height < 0 || width < 0) { |
| SetGLError(GL_INVALID_VALUE, "glTexSubImage2D", "dimension < 0"); |
| return; |
| } |
| if (height == 0 || width == 0) { |
| return; |
| } |
| |
| uint32 temp_size; |
| uint32 unpadded_row_size; |
| uint32 padded_row_size; |
| if (!GLES2Util::ComputeImageDataSizes( |
| width, height, format, type, unpack_alignment_, &temp_size, |
| &unpadded_row_size, &padded_row_size)) { |
| SetGLError(GL_INVALID_VALUE, "glTexSubImage2D", "size to large"); |
| return; |
| } |
| |
| // If there's a pixel unpack buffer bound use it when issuing TexSubImage2D. |
| if (bound_pixel_unpack_transfer_buffer_id_) { |
| GLuint offset = ToGLuint(pixels); |
| BufferTracker::Buffer* buffer = GetBoundPixelUnpackTransferBufferIfValid( |
| bound_pixel_unpack_transfer_buffer_id_, |
| "glTexSubImage2D", offset, temp_size); |
| if (buffer && buffer->shm_id() != -1) { |
| helper_->TexSubImage2D( |
| target, level, xoffset, yoffset, width, height, format, type, |
| buffer->shm_id(), buffer->shm_offset() + offset, false); |
| buffer->set_transfer_ready_token(helper_->InsertToken()); |
| CheckGLError(); |
| } |
| return; |
| } |
| |
| // compute the advance bytes per row for the src pixels |
| uint32 src_padded_row_size; |
| if (unpack_row_length_ > 0) { |
| if (!GLES2Util::ComputeImagePaddedRowSize( |
| unpack_row_length_, format, type, unpack_alignment_, |
| &src_padded_row_size)) { |
| SetGLError( |
| GL_INVALID_VALUE, "glTexImage2D", "unpack row length too large"); |
| return; |
| } |
| } else { |
| src_padded_row_size = padded_row_size; |
| } |
| |
| // advance pixels pointer past the skip rows and skip pixels |
| pixels = reinterpret_cast<const int8*>(pixels) + |
| unpack_skip_rows_ * src_padded_row_size; |
| if (unpack_skip_pixels_) { |
| uint32 group_size = GLES2Util::ComputeImageGroupSize(format, type); |
| pixels = reinterpret_cast<const int8*>(pixels) + |
| unpack_skip_pixels_ * group_size; |
| } |
| |
| ScopedTransferBufferPtr buffer(temp_size, helper_, transfer_buffer_); |
| TexSubImage2DImpl( |
| target, level, xoffset, yoffset, width, height, format, type, |
| unpadded_row_size, pixels, src_padded_row_size, GL_FALSE, &buffer, |
| padded_row_size); |
| CheckGLError(); |
| } |
| |
| static GLint ComputeNumRowsThatFitInBuffer( |
| GLsizeiptr padded_row_size, GLsizeiptr unpadded_row_size, |
| unsigned int size) { |
| GPU_DCHECK_GE(unpadded_row_size, 0); |
| if (padded_row_size == 0) { |
| return 1; |
| } |
| GLint num_rows = size / padded_row_size; |
| return num_rows + (size - num_rows * padded_row_size) / unpadded_row_size; |
| } |
| |
| void GLES2Implementation::TexSubImage2DImpl( |
| GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, |
| GLsizei height, GLenum format, GLenum type, uint32 unpadded_row_size, |
| const void* pixels, uint32 pixels_padded_row_size, GLboolean internal, |
| ScopedTransferBufferPtr* buffer, uint32 buffer_padded_row_size) { |
| GPU_DCHECK(buffer); |
| GPU_DCHECK_GE(level, 0); |
| GPU_DCHECK_GT(height, 0); |
| GPU_DCHECK_GT(width, 0); |
| |
| const int8* source = reinterpret_cast<const int8*>(pixels); |
| GLint original_yoffset = yoffset; |
| // Transfer by rows. |
| while (height) { |
| unsigned int desired_size = |
| buffer_padded_row_size * (height - 1) + unpadded_row_size; |
| if (!buffer->valid() || buffer->size() == 0) { |
| buffer->Reset(desired_size); |
| if (!buffer->valid()) { |
| return; |
| } |
| } |
| |
| GLint num_rows = ComputeNumRowsThatFitInBuffer( |
| buffer_padded_row_size, unpadded_row_size, buffer->size()); |
| num_rows = std::min(num_rows, height); |
| CopyRectToBuffer( |
| source, num_rows, unpadded_row_size, pixels_padded_row_size, |
| unpack_flip_y_, buffer->address(), buffer_padded_row_size); |
| GLint y = unpack_flip_y_ ? original_yoffset + height - num_rows : yoffset; |
| helper_->TexSubImage2D( |
| target, level, xoffset, y, width, num_rows, format, type, |
| buffer->shm_id(), buffer->offset(), internal); |
| buffer->Release(); |
| yoffset += num_rows; |
| source += num_rows * pixels_padded_row_size; |
| height -= num_rows; |
| } |
| } |
| |
| bool GLES2Implementation::GetActiveAttribHelper( |
| GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, |
| GLenum* type, char* name) { |
| // Clear the bucket so if the command fails nothing will be in it. |
| helper_->SetBucketSize(kResultBucketId, 0); |
| typedef cmds::GetActiveAttrib::Result Result; |
| Result* result = GetResultAs<Result*>(); |
| if (!result) { |
| return false; |
| } |
| // Set as failed so if the command fails we'll recover. |
| result->success = false; |
| helper_->GetActiveAttrib(program, index, kResultBucketId, |
| GetResultShmId(), GetResultShmOffset()); |
| WaitForCmd(); |
| if (result->success) { |
| if (size) { |
| *size = result->size; |
| } |
| if (type) { |
| *type = result->type; |
| } |
| if (length || name) { |
| std::vector<int8> str; |
| GetBucketContents(kResultBucketId, &str); |
| GLsizei max_size = std::min(static_cast<size_t>(bufsize) - 1, |
| std::max(static_cast<size_t>(0), |
| str.size() - 1)); |
| if (length) { |
| *length = max_size; |
| } |
| if (name && bufsize > 0) { |
| memcpy(name, &str[0], max_size); |
| name[max_size] = '\0'; |
| } |
| } |
| } |
| return result->success != 0; |
| } |
| |
| void GLES2Implementation::GetActiveAttrib( |
| GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, |
| GLenum* type, char* name) { |
| GPU_CLIENT_SINGLE_THREAD_CHECK(); |
| GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glGetActiveAttrib(" |
| << program << ", " << index << ", " << bufsize << ", " |
| << static_cast<const void*>(length) << ", " |
| << static_cast<const void*>(size) << ", " |
| << static_cast<const void*>(type) << ", " |
| << static_cast<const void*>(name) << ", "); |
| if (bufsize < 0) { |
| SetGLError(GL_INVALID_VALUE, "glGetActiveAttrib", "bufsize < 0"); |
| return; |
| } |
| TRACE_EVENT0("gpu", "GLES2::GetActiveAttrib"); |
| bool success = share_group_->program_info_manager()->GetActiveAttrib( |
| this, program, index, bufsize, length, size, type, name); |
| if (success) { |
| if (size) { |
| GPU_CLIENT_LOG(" size: " << *size); |
| } |
| if (type) { |
| GPU_CLIENT_LOG(" type: " << GLES2Util::GetStringEnum(*type)); |
| } |
| if (name) { |
| GPU_CLIENT_LOG(" name: " << name); |
| } |
| } |
| CheckGLError(); |
| } |
| |
| bool GLES2Implementation::GetActiveUniformHelper( |
| GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, |
| GLenum* type, char* name) { |
| // Clear the bucket so if the command fails nothing will be in it. |
| helper_->SetBucketSize(kResultBucketId, 0); |
| typedef cmds::GetActiveUniform::Result Result; |
| Result* result = GetResultAs<Result*>(); |
| if (!result) { |
| return false; |
| } |
| // Set as failed so if the command fails we'll recover. |
| result->success = false; |
| helper_->GetActiveUniform(program, index, kResultBucketId, |
| GetResultShmId(), GetResultShmOffset()); |
| WaitForCmd(); |
| if (result->success) { |
| if (size) { |
| *size = result->size; |
| } |
| if (type) { |
| *type = result->type; |
| } |
| if (length || name) { |
| std::vector<int8> str; |
| GetBucketContents(kResultBucketId, &str); |
| GLsizei max_size = std::min(static_cast<size_t>(bufsize) - 1, |
| std::max(static_cast<size_t>(0), |
| str.size() - 1)); |
| if (length) { |
| *length = max_size; |
| } |
| if (name && bufsize > 0) { |
| memcpy(name, &str[0], max_size); |
| name[max_size] = '\0'; |
| } |
| } |
| } |
| return result->success != 0; |
| } |
| |
| void GLES2Implementation::GetActiveUniform( |
| GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, |
| GLenum* type, char* name) { |
| GPU_CLIENT_SINGLE_THREAD_CHECK(); |
| GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glGetActiveUniform(" |
| << program << ", " << index << ", " << bufsize << ", " |
| << static_cast<const void*>(length) << ", " |
| << static_cast<const void*>(size) << ", " |
| << static_cast<const void*>(type) << ", " |
| << static_cast<const void*>(name) << ", "); |
| if (bufsize < 0) { |
| SetGLError(GL_INVALID_VALUE, "glGetActiveUniform", "bufsize < 0"); |
| return; |
| } |
| TRACE_EVENT0("gpu", "GLES2::GetActiveUniform"); |
| bool success = share_group_->program_info_manager()->GetActiveUniform( |
| this, program, index, bufsize, length, size, type, name); |
| if (success) { |
| if (size) { |
| GPU_CLIENT_LOG(" size: " << *size); |
| } |
| if (type) { |
| GPU_CLIENT_LOG(" type: " << GLES2Util::GetStringEnum(*type)); |
| } |
| if (name) { |
| GPU_CLIENT_LOG(" name: " << name); |
| } |
| } |
| CheckGLError(); |
| } |
| |
| void GLES2Implementation::GetAttachedShaders( |
| GLuint program, GLsizei maxcount, GLsizei* count, GLuint* shaders) { |
| GPU_CLIENT_SINGLE_THREAD_CHECK(); |
| GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glGetAttachedShaders(" |
| << program << ", " << maxcount << ", " |
| << static_cast<const void*>(count) << ", " |
| << static_cast<const void*>(shaders) << ", "); |
| if (maxcount < 0) { |
| SetGLError(GL_INVALID_VALUE, "glGetAttachedShaders", "maxcount < 0"); |
| return; |
| } |
| TRACE_EVENT0("gpu", "GLES2::GetAttachedShaders"); |
| typedef cmds::GetAttachedShaders::Result Result; |
| uint32 size = Result::ComputeSize(maxcount); |
| Result* result = static_cast<Result*>(transfer_buffer_->Alloc(size)); |
| if (!result) { |
| return; |
| } |
| result->SetNumResults(0); |
| helper_->GetAttachedShaders( |
| program, |
| transfer_buffer_->GetShmId(), |
| transfer_buffer_->GetOffset(result), |
| size); |
| int32 token = helper_->InsertToken(); |
| WaitForCmd(); |
| if (count) { |
| *count = result->GetNumResults(); |
| } |
| result->CopyResult(shaders); |
| GPU_CLIENT_LOG_CODE_BLOCK({ |
| for (int32 i = 0; i < result->GetNumResults(); ++i) { |
| GPU_CLIENT_LOG(" " << i << ": " << result->GetData()[i]); |
| } |
| }); |
| transfer_buffer_->FreePendingToken(result, token); |
| CheckGLError(); |
| } |
| |
| void GLES2Implementation::GetShaderPrecisionFormat( |
| GLenum shadertype, GLenum precisiontype, GLint* range, GLint* precision) { |
| GPU_CLIENT_SINGLE_THREAD_CHECK(); |
| GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glGetShaderPrecisionFormat(" |
| << GLES2Util::GetStringShaderType(shadertype) << ", " |
| << GLES2Util::GetStringShaderPrecision(precisiontype) << ", " |
| << static_cast<const void*>(range) << ", " |
| << static_cast<const void*>(precision) << ", "); |
| TRACE_EVENT0("gpu", "GLES2::GetShaderPrecisionFormat"); |
| typedef cmds::GetShaderPrecisionFormat::Result Result; |
| Result* result = GetResultAs<Result*>(); |
| if (!result) { |
| return; |
| } |
| |
| GLStaticState::ShaderPrecisionKey key(shadertype, precisiontype); |
| GLStaticState::ShaderPrecisionMap::iterator i = |
| static_state_.shader_precisions.find(key); |
| if (i != static_state_.shader_precisions.end()) { |
| *result = i->second; |
| } else { |
| result->success = false; |
| helper_->GetShaderPrecisionFormat( |
| shadertype, precisiontype, GetResultShmId(), GetResultShmOffset()); |
| WaitForCmd(); |
| if (result->success) |
| static_state_.shader_precisions[key] = *result; |
| } |
| |
| if (result->success) { |
| if (range) { |
| range[0] = result->min_range; |
| range[1] = result->max_range; |
| GPU_CLIENT_LOG(" min_range: " << range[0]); |
| GPU_CLIENT_LOG(" min_range: " << range[1]); |
| } |
| if (precision) { |
| precision[0] = result->precision; |
| GPU_CLIENT_LOG(" min_range: " << precision[0]); |
| } |
| } |
| CheckGLError(); |
| } |
| |
| const GLubyte* GLES2Implementation::GetStringHelper(GLenum name) { |
| const char* result = NULL; |
| // Clears the bucket so if the command fails nothing will be in it. |
| helper_->SetBucketSize(kResultBucketId, 0); |
| helper_->GetString(name, kResultBucketId); |
| std::string str; |
| if (GetBucketAsString(kResultBucketId, &str)) { |
| // Adds extensions implemented on client side only. |
| switch (name) { |
| case GL_EXTENSIONS: |
| str += std::string(str.empty() ? "" : " ") + |
| "GL_CHROMIUM_flipy " |
| "GL_CHROMIUM_map_sub " |
| "GL_CHROMIUM_shallow_flush " |
| "GL_EXT_unpack_subimage"; |
| if (gpu_control_ != NULL) { |
| // The first space character is intentional. |
| str += " GL_CHROMIUM_map_image"; |
| } |
| break; |
| default: |
| break; |
| } |
| |
| // Because of WebGL the extensions can change. We have to cache each unique |
| // result since we don't know when the client will stop referring to a |
| // previous one it queries. |
| GLStringMap::iterator it = gl_strings_.find(name); |
| if (it == gl_strings_.end()) { |
| std::set<std::string> strings; |
| std::pair<GLStringMap::iterator, bool> insert_result = |
| gl_strings_.insert(std::make_pair(name, strings)); |
| GPU_DCHECK(insert_result.second); |
| it = insert_result.first; |
| } |
| std::set<std::string>& string_set = it->second; |
| std::set<std::string>::const_iterator sit = string_set.find(str); |
| if (sit != string_set.end()) { |
| result = sit->c_str(); |
| } else { |
| std::pair<std::set<std::string>::const_iterator, bool> insert_result = |
| string_set.insert(str); |
| GPU_DCHECK(insert_result.second); |
| result = insert_result.first->c_str(); |
| } |
| } |
| return reinterpret_cast<const GLubyte*>(result); |
| } |
| |
| const GLubyte* GLES2Implementation::GetString(GLenum name) { |
| GPU_CLIENT_SINGLE_THREAD_CHECK(); |
| GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glGetString(" |
| << GLES2Util::GetStringStringType(name) << ")"); |
| TRACE_EVENT0("gpu", "GLES2::GetString"); |
| const GLubyte* result = GetStringHelper(name); |
| GPU_CLIENT_LOG(" returned " << reinterpret_cast<const char*>(result)); |
| CheckGLError(); |
| return result; |
| } |
| |
| void GLES2Implementation::GetUniformfv( |
| GLuint program, GLint location, GLfloat* params) { |
| GPU_CLIENT_SINGLE_THREAD_CHECK(); |
| GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glGetUniformfv(" |
| << program << ", " << location << ", " |
| << static_cast<const void*>(params) << ")"); |
| TRACE_EVENT0("gpu", "GLES2::GetUniformfv"); |
| typedef cmds::GetUniformfv::Result Result; |
| Result* result = GetResultAs<Result*>(); |
| if (!result) { |
| return; |
| } |
| result->SetNumResults(0); |
| helper_->GetUniformfv( |
| program, location, GetResultShmId(), GetResultShmOffset()); |
| WaitForCmd(); |
| result->CopyResult(params); |
| GPU_CLIENT_LOG_CODE_BLOCK({ |
| for (int32 i = 0; i < result->GetNumResults(); ++i) { |
| GPU_CLIENT_LOG(" " << i << ": " << result->GetData()[i]); |
| } |
| }); |
| CheckGLError(); |
| } |
| |
| void GLES2Implementation::GetUniformiv( |
| GLuint program, GLint location, GLint* params) { |
| GPU_CLIENT_SINGLE_THREAD_CHECK(); |
| GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glGetUniformiv(" |
| << program << ", " << location << ", " |
| << static_cast<const void*>(params) << ")"); |
| TRACE_EVENT0("gpu", "GLES2::GetUniformiv"); |
| typedef cmds::GetUniformiv::Result Result; |
| Result* result = GetResultAs<Result*>(); |
| if (!result) { |
| return; |
| } |
| result->SetNumResults(0); |
| helper_->GetUniformiv( |
| program, location, GetResultShmId(), GetResultShmOffset()); |
| WaitForCmd(); |
| GetResultAs<cmds::GetUniformfv::Result*>()->CopyResult(params); |
| GPU_CLIENT_LOG_CODE_BLOCK({ |
| for (int32 i = 0; i < result->GetNumResults(); ++i) { |
| GPU_CLIENT_LOG(" " << i << ": " << result->GetData()[i]); |
| } |
| }); |
| CheckGLError(); |
| } |
| |
| void GLES2Implementation::ReadPixels( |
| GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, |
| GLenum type, void* pixels) { |
| GPU_CLIENT_SINGLE_THREAD_CHECK(); |
| GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glReadPixels(" |
| << xoffset << ", " << yoffset << ", " |
| << width << ", " << height << ", " |
| << GLES2Util::GetStringReadPixelFormat(format) << ", " |
| << GLES2Util::GetStringPixelType(type) << ", " |
| << static_cast<const void*>(pixels) << ")"); |
| if (width < 0 || height < 0) { |
| SetGLError(GL_INVALID_VALUE, "glReadPixels", "dimensions < 0"); |
| return; |
| } |
| if (width == 0 || height == 0) { |
| return; |
| } |
| |
| // glReadPixel pads the size of each row of pixels by an amount specified by |
| // glPixelStorei. So, we have to take that into account both in the fact that |
| // the pixels returned from the ReadPixel command will include that padding |
| // and that when we copy the results to the user's buffer we need to not |
| // write those padding bytes but leave them as they are. |
| |
| TRACE_EVENT0("gpu", "GLES2::ReadPixels"); |
| typedef cmds::ReadPixels::Result Result; |
| |
| int8* dest = reinterpret_cast<int8*>(pixels); |
| uint32 temp_size; |
| uint32 unpadded_row_size; |
| uint32 padded_row_size; |
| if (!GLES2Util::ComputeImageDataSizes( |
| width, 2, format, type, pack_alignment_, &temp_size, &unpadded_row_size, |
| &padded_row_size)) { |
| SetGLError(GL_INVALID_VALUE, "glReadPixels", "size too large."); |
| return; |
| } |
| |
| if (bound_pixel_pack_transfer_buffer_id_) { |
| GLuint offset = ToGLuint(pixels); |
| BufferTracker::Buffer* buffer = GetBoundPixelUnpackTransferBufferIfValid( |
| bound_pixel_pack_transfer_buffer_id_, |
| "glReadPixels", offset, padded_row_size * height); |
| if (buffer && buffer->shm_id() != -1) { |
| helper_->ReadPixels(xoffset, yoffset, width, height, format, type, |
| buffer->shm_id(), buffer->shm_offset(), |
| 0, 0, true); |
| CheckGLError(); |
| } |
| return; |
| } |
| |
| if (!pixels) { |
| SetGLError(GL_INVALID_OPERATION, "glReadPixels", "pixels = NULL"); |
| return; |
| } |
| |
| // Transfer by rows. |
| // The max rows we can transfer. |
| while (height) { |
| GLsizei desired_size = padded_row_size * height - 1 + unpadded_row_size; |
| ScopedTransferBufferPtr buffer(desired_size, helper_, transfer_buffer_); |
| if (!buffer.valid()) { |
| return; |
| } |
| GLint num_rows = ComputeNumRowsThatFitInBuffer( |
| padded_row_size, unpadded_row_size, buffer.size()); |
| num_rows = std::min(num_rows, height); |
| // NOTE: We must look up the address of the result area AFTER allocation |
| // of the transfer buffer since the transfer buffer may be reallocated. |
| Result* result = GetResultAs<Result*>(); |
| if (!result) { |
| return; |
| } |
| *result = 0; // mark as failed. |
| helper_->ReadPixels( |
| xoffset, yoffset, width, num_rows, format, type, |
| buffer.shm_id(), buffer.offset(), |
| GetResultShmId(), GetResultShmOffset(), |
| false); |
| WaitForCmd(); |
| if (*result != 0) { |
| // when doing a y-flip we have to iterate through top-to-bottom chunks |
| // of the dst. The service side handles reversing the rows within a |
| // chunk. |
| int8* rows_dst; |
| if (pack_reverse_row_order_) { |
| rows_dst = dest + (height - num_rows) * padded_row_size; |
| } else { |
| rows_dst = dest; |
| } |
| // We have to copy 1 row at a time to avoid writing pad bytes. |
| const int8* src = static_cast<const int8*>(buffer.address()); |
| for (GLint yy = 0; yy < num_rows; ++yy) { |
| memcpy(rows_dst, src, unpadded_row_size); |
| rows_dst += padded_row_size; |
| src += padded_row_size; |
| } |
| if (!pack_reverse_row_order_) { |
| dest = rows_dst; |
| } |
| } |
| // If it was not marked as successful exit. |
| if (*result == 0) { |
| return; |
| } |
| yoffset += num_rows; |
| height -= num_rows; |
| } |
| CheckGLError(); |
| } |
| |
| void GLES2Implementation::ActiveTexture(GLenum texture) { |
| GPU_CLIENT_SINGLE_THREAD_CHECK(); |
| GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glActiveTexture(" |
| << GLES2Util::GetStringEnum(texture) << ")"); |
| GLuint texture_index = texture - GL_TEXTURE0; |
| if (texture_index >= static_cast<GLuint>( |
| static_state_.int_state.max_combined_texture_image_units)) { |
| SetGLErrorInvalidEnum( |
| "glActiveTexture", texture, "texture"); |
| return; |
| } |
| |
| active_texture_unit_ = texture_index; |
| helper_->ActiveTexture(texture); |
| CheckGLError(); |
| } |
| |
| void GLES2Implementation::GenBuffersHelper( |
| GLsizei /* n */, const GLuint* /* buffers */) { |
| } |
| |
| void GLES2Implementation::GenFramebuffersHelper( |
| GLsizei /* n */, const GLuint* /* framebuffers */) { |
| } |
| |
| void GLES2Implementation::GenRenderbuffersHelper( |
| GLsizei /* n */, const GLuint* /* renderbuffers */) { |
| } |
| |
| void GLES2Implementation::GenTexturesHelper( |
| GLsizei /* n */, const GLuint* /* textures */) { |
| } |
| |
| void GLES2Implementation::GenVertexArraysOESHelper( |
| GLsizei n, const GLuint* arrays) { |
| vertex_array_object_manager_->GenVertexArrays(n, arrays); |
| } |
| |
| void GLES2Implementation::GenQueriesEXTHelper( |
| GLsizei /* n */, const GLuint* /* queries */) { |
| } |
| |
| // NOTE #1: On old versions of OpenGL, calling glBindXXX with an unused id |
| // generates a new resource. On newer versions of OpenGL they don't. The code |
| // related to binding below will need to change if we switch to the new OpenGL |
| // model. Specifically it assumes a bind will succeed which is always true in |
| // the old model but possibly not true in the new model if another context has |
| // deleted the resource. |
| |
| bool GLES2Implementation::BindBufferHelper( |
| GLenum target, GLuint buffer) { |
| // TODO(gman): See note #1 above. |
| bool changed = false; |
| switch (target) { |
| case GL_ARRAY_BUFFER: |
| if (bound_array_buffer_id_ != buffer) { |
| bound_array_buffer_id_ = buffer; |
| changed = true; |
| } |
| break; |
| case GL_ELEMENT_ARRAY_BUFFER: |
| changed = vertex_array_object_manager_->BindElementArray(buffer); |
| break; |
| case GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM: |
| bound_pixel_pack_transfer_buffer_id_ = buffer; |
| break; |
| case GL_PIXEL_UNPACK_TRANSFER_BUFFER_CHROMIUM: |
| bound_pixel_unpack_transfer_buffer_id_ = buffer; |
| break; |
| default: |
| changed = true; |
| break; |
| } |
| // TODO(gman): There's a bug here. If the target is invalid the ID will not be |
| // used even though it's marked it as used here. |
| GetIdHandler(id_namespaces::kBuffers)->MarkAsUsedForBind(buffer); |
| return changed; |
| } |
| |
| bool GLES2Implementation::BindFramebufferHelper( |
| GLenum target, GLuint framebuffer) { |
| // TODO(gman): See note #1 above. |
| bool changed = false; |
| switch (target) { |
| case GL_FRAMEBUFFER: |
| if (bound_framebuffer_ != framebuffer || |
| bound_read_framebuffer_ != framebuffer) { |
| bound_framebuffer_ = framebuffer; |
| bound_read_framebuffer_ = framebuffer; |
| changed = true; |
| } |
| break; |
| case GL_READ_FRAMEBUFFER: |
| if (!IsChromiumFramebufferMultisampleAvailable()) { |
| SetGLErrorInvalidEnum("glBindFramebuffer", target, "target"); |
| return false; |
| } |
| if (bound_read_framebuffer_ != framebuffer) { |
| bound_read_framebuffer_ = framebuffer; |
| changed = true; |
| } |
| break; |
| case GL_DRAW_FRAMEBUFFER: |
| if (!IsChromiumFramebufferMultisampleAvailable()) { |
| SetGLErrorInvalidEnum("glBindFramebuffer", target, "target"); |
| return false; |
| } |
| if (bound_framebuffer_ != framebuffer) { |
| bound_framebuffer_ = framebuffer; |
| changed = true; |
| } |
| break; |
| default: |
| SetGLErrorInvalidEnum("glBindFramebuffer", target, "target"); |
| return false; |
| } |
| GetIdHandler(id_namespaces::kFramebuffers)->MarkAsUsedForBind(framebuffer); |
| return changed; |
| } |
| |
| bool GLES2Implementation::BindRenderbufferHelper( |
| GLenum target, GLuint renderbuffer) { |
| // TODO(gman): See note #1 above. |
| bool changed = false; |
| switch (target) { |
| case GL_RENDERBUFFER: |
| if (bound_renderbuffer_ != renderbuffer) { |
| bound_renderbuffer_ = renderbuffer; |
| changed = true; |
| } |
| break; |
| default: |
| changed = true; |
| break; |
| } |
| // TODO(gman): There's a bug here. If the target is invalid the ID will not be |
| // used even though it's marked it as used here. |
| GetIdHandler(id_namespaces::kRenderbuffers)->MarkAsUsedForBind(renderbuffer); |
| return changed; |
| } |
| |
| bool GLES2Implementation::BindTextureHelper(GLenum target, GLuint texture) { |
| // TODO(gman): See note #1 above. |
| // TODO(gman): Change this to false once we figure out why it's failing |
| // on daisy. |
| bool changed = true; |
| TextureUnit& unit = texture_units_[active_texture_unit_]; |
| switch (target) { |
| case GL_TEXTURE_2D: |
| if (unit.bound_texture_2d != texture) { |
| unit.bound_texture_2d = texture; |
| changed = true; |
| } |
| break; |
| case GL_TEXTURE_CUBE_MAP: |
| if (unit.bound_texture_cube_map != texture) { |
| unit.bound_texture_cube_map = texture; |
| changed = true; |
| } |
| break; |
| default: |
| changed = true; |
| break; |
| } |
| // TODO(gman): There's a bug here. If the target is invalid the ID will not be |
| // used. even though it's marked it as used here. |
| GetIdHandler(id_namespaces::kTextures)->MarkAsUsedForBind(texture); |
| return changed; |
| } |
| |
| bool GLES2Implementation::BindVertexArrayHelper(GLuint array) { |
| // TODO(gman): See note #1 above. |
| bool changed = false; |
| if (!vertex_array_object_manager_->BindVertexArray(array, &changed)) { |
| SetGLError( |
| GL_INVALID_OPERATION, "glBindVertexArrayOES", |
| "id was not generated with glGenVertexArrayOES"); |
| } |
| // Unlike other BindXXXHelpers we don't call MarkAsUsedForBind |
| // because unlike other resources VertexArrayObject ids must |
| // be generated by GenVertexArrays. A random id to Bind will not |
| // generate a new object. |
| return changed; |
| } |
| |
| bool GLES2Implementation::IsBufferReservedId(GLuint id) { |
| return vertex_array_object_manager_->IsReservedId(id); |
| } |
| |
| void GLES2Implementation::DeleteBuffersHelper( |
| GLsizei n, const GLuint* buffers) { |
| if (!GetIdHandler(id_namespaces::kBuffers)->FreeIds( |
| this, n, buffers, &GLES2Implementation::DeleteBuffersStub)) { |
| SetGLError( |
| GL_INVALID_VALUE, |
| "glDeleteBuffers", "id not created by this context."); |
| return; |
| } |
| for (GLsizei ii = 0; ii < n; ++ii) { |
| if (buffers[ii] == bound_array_buffer_id_) { |
| bound_array_buffer_id_ = 0; |
| } |
| vertex_array_object_manager_->UnbindBuffer(buffers[ii]); |
| BufferTracker::Buffer* buffer = buffer_tracker_->GetBuffer(buffers[ii]); |
| if (buffer) { |
| // Free buffer memory, pending the passage of a token. |
| buffer_tracker_->FreePendingToken(buffer, helper_->InsertToken()); |
| // Remove buffer. |
| buffer_tracker_->RemoveBuffer(buffers[ii]); |
| } |
| if (buffers[ii] == bound_pixel_unpack_transfer_buffer_id_) { |
| bound_pixel_unpack_transfer_buffer_id_ = 0; |
| } |
| } |
| } |
| |
| void GLES2Implementation::DeleteBuffersStub( |
| GLsizei n, const GLuint* buffers) { |
| helper_->DeleteBuffersImmediate(n, buffers); |
| } |
| |
| |
| void GLES2Implementation::DeleteFramebuffersHelper( |
| GLsizei n, const GLuint* framebuffers) { |
| if (!GetIdHandler(id_namespaces::kFramebuffers)->FreeIds( |
| this, n, framebuffers, &GLES2Implementation::DeleteFramebuffersStub)) { |
| SetGLError( |
| GL_INVALID_VALUE, |
| "glDeleteFramebuffers", "id not created by this context."); |
| return; |
| } |
| for (GLsizei ii = 0; ii < n; ++ii) { |
| if (framebuffers[ii] == bound_framebuffer_) { |
| bound_framebuffer_ = 0; |
| } |
| if (framebuffers[ii] == bound_read_framebuffer_) { |
| bound_read_framebuffer_ = 0; |
| } |
| } |
| } |
| |
| void GLES2Implementation::DeleteFramebuffersStub( |
| GLsizei n, const GLuint* framebuffers) { |
| helper_->DeleteFramebuffersImmediate(n, framebuffers); |
| } |
| |
| void GLES2Implementation::DeleteRenderbuffersHelper( |
| GLsizei n, const GLuint* renderbuffers) { |
| if (!GetIdHandler(id_namespaces::kRenderbuffers)->FreeIds( |
| this, n, renderbuffers, &GLES2Implementation::DeleteRenderbuffersStub)) { |
| SetGLError( |
| GL_INVALID_VALUE, |
| "glDeleteRenderbuffers", "id not created by this context."); |
| return; |
| } |
| for (GLsizei ii = 0; ii < n; ++ii) { |
| if (renderbuffers[ii] == bound_renderbuffer_) { |
| bound_renderbuffer_ = 0; |
| } |
| } |
| } |
| |
| void GLES2Implementation::DeleteRenderbuffersStub( |
| GLsizei n, const GLuint* renderbuffers) { |
| helper_->DeleteRenderbuffersImmediate(n, renderbuffers); |
| } |
| |
| void GLES2Implementation::DeleteTexturesHelper( |
| GLsizei n, const GLuint* textures) { |
| if (!GetIdHandler(id_namespaces::kTextures)->FreeIds( |
| this, n, textures, &GLES2Implementation::DeleteTexturesStub)) { |
| SetGLError( |
| GL_INVALID_VALUE, |
| "glDeleteTextures", "id not created by this context."); |
| return; |
| } |
| for (GLsizei ii = 0; ii < n; ++ii) { |
| for (GLint tt = 0; |
| tt < static_state_.int_state.max_combined_texture_image_units; |
| ++tt) { |
| TextureUnit& unit = texture_units_[tt]; |
| if (textures[ii] == unit.bound_texture_2d) { |
| unit.bound_texture_2d = 0; |
| } |
| if (textures[ii] == unit.bound_texture_cube_map) { |
| unit.bound_texture_cube_map = 0; |
| } |
| } |
| } |
| } |
| |
| void GLES2Implementation::DeleteVertexArraysOESHelper( |
| GLsizei n, const GLuint* arrays) { |
| vertex_array_object_manager_->DeleteVertexArrays(n, arrays); |
| if (!GetIdHandler(id_namespaces::kVertexArrays)->FreeIds( |
| this, n, arrays, &GLES2Implementation::DeleteVertexArraysOESStub)) { |
| SetGLError( |
| GL_INVALID_VALUE, |
| "glDeleteVertexArraysOES", "id not created by this context."); |
| return; |
| } |
| } |
| |
| void GLES2Implementation::DeleteVertexArraysOESStub( |
| GLsizei n, const GLuint* arrays) { |
| helper_->DeleteVertexArraysOESImmediate(n, arrays); |
| } |
| |
| void GLES2Implementation::DeleteTexturesStub( |
| GLsizei n, const GLuint* textures) { |
| helper_->DeleteTexturesImmediate(n, textures); |
| } |
| |
| void GLES2Implementation::DisableVertexAttribArray(GLuint index) { |
| GPU_CLIENT_SINGLE_THREAD_CHECK(); |
| GPU_CLIENT_LOG( |
| "[" << GetLogPrefix() << "] glDisableVertexAttribArray(" << index << ")"); |
| vertex_array_object_manager_->SetAttribEnable(index, false); |
| helper_->DisableVertexAttribArray(index); |
| CheckGLError(); |
| } |
| |
| void GLES2Implementation::EnableVertexAttribArray(GLuint index) { |
| GPU_CLIENT_SINGLE_THREAD_CHECK(); |
| GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glEnableVertexAttribArray(" |
| << index << ")"); |
| vertex_array_object_manager_->SetAttribEnable(index, true); |
| helper_->EnableVertexAttribArray(index); |
| CheckGLError(); |
| } |
| |
| void GLES2Implementation::DrawArrays(GLenum mode, GLint first, GLsizei count) { |
| GPU_CLIENT_SINGLE_THREAD_CHECK(); |
| GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glDrawArrays(" |
| << GLES2Util::GetStringDrawMode(mode) << ", " |
| << first << ", " << count << ")"); |
| if (count < 0) { |
| SetGLError(GL_INVALID_VALUE, "glDrawArrays", "count < 0"); |
| return; |
| } |
| bool simulated = false; |
| if (!vertex_array_object_manager_->SetupSimulatedClientSideBuffers( |
| "glDrawArrays", this, helper_, first + count, 0, &simulated)) { |
| return; |
| } |
| helper_->DrawArrays(mode, first, count); |
| RestoreArrayBuffer(simulated); |
| CheckGLError(); |
| } |
| |
| void GLES2Implementation::GetVertexAttribfv( |
| GLuint index, GLenum pname, GLfloat* params) { |
| GPU_CLIENT_SINGLE_THREAD_CHECK(); |
| GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glGetVertexAttribfv(" |
| << index << ", " |
| << GLES2Util::GetStringVertexAttribute(pname) << ", " |
| << static_cast<const void*>(params) << ")"); |
| uint32 value = 0; |
| if (vertex_array_object_manager_->GetVertexAttrib(index, pname, &value)) { |
| *params = static_cast<float>(value); |
| return; |
| } |
| TRACE_EVENT0("gpu", "GLES2::GetVertexAttribfv"); |
| typedef cmds::GetVertexAttribfv::Result Result; |
| Result* result = GetResultAs<Result*>(); |
| if (!result) { |
| return; |
| } |
| result->SetNumResults(0); |
| helper_->GetVertexAttribfv( |
| index, pname, GetResultShmId(), GetResultShmOffset()); |
| WaitForCmd(); |
| result->CopyResult(params); |
| GPU_CLIENT_LOG_CODE_BLOCK({ |
| for (int32 i = 0; i < result->GetNumResults(); ++i) { |
| GPU_CLIENT_LOG(" " << i << ": " << result->GetData()[i]); |
| } |
| }); |
| CheckGLError(); |
| } |
| |
| void GLES2Implementation::GetVertexAttribiv( |
| GLuint index, GLenum pname, GLint* params) { |
| GPU_CLIENT_SINGLE_THREAD_CHECK(); |
| GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glGetVertexAttribiv(" |
| << index << ", " |
| << GLES2Util::GetStringVertexAttribute(pname) << ", " |
| << static_cast<const void*>(params) << ")"); |
| uint32 value = 0; |
| if (vertex_array_object_manager_->GetVertexAttrib(index, pname, &value)) { |
| *params = value; |
| return; |
| } |
| TRACE_EVENT0("gpu", "GLES2::GetVertexAttribiv"); |
| typedef cmds::GetVertexAttribiv::Result Result; |
| Result* result = GetResultAs<Result*>(); |
| if (!result) { |
| return; |
| } |
| result->SetNumResults(0); |
| helper_->GetVertexAttribiv( |
| index, pname, GetResultShmId(), GetResultShmOffset()); |
| WaitForCmd(); |
| result->CopyResult(params); |
| GPU_CLIENT_LOG_CODE_BLOCK({ |
| for (int32 i = 0; i < result->GetNumResults(); ++i) { |
| GPU_CLIENT_LOG(" " << i << ": " << result->GetData()[i]); |
| } |
| }); |
| CheckGLError(); |
| } |
| |
| GLboolean GLES2Implementation::EnableFeatureCHROMIUM( |
| const char* feature) { |
| GPU_CLIENT_SINGLE_THREAD_CHECK(); |
| GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glEnableFeatureCHROMIUM(" |
| << feature << ")"); |
| TRACE_EVENT0("gpu", "GLES2::EnableFeatureCHROMIUM"); |
| typedef cmds::EnableFeatureCHROMIUM::Result Result; |
| Result* result = GetResultAs<Result*>(); |
| if (!result) { |
| return false; |
| } |
| *result = 0; |
| SetBucketAsCString(kResultBucketId, feature); |
| helper_->EnableFeatureCHROMIUM( |
| kResultBucketId, GetResultShmId(), GetResultShmOffset()); |
| WaitForCmd(); |
| helper_->SetBucketSize(kResultBucketId, 0); |
| GPU_CLIENT_LOG(" returned " << GLES2Util::GetStringBool(*result)); |
| return *result; |
| } |
| |
| void* GLES2Implementation::MapBufferSubDataCHROMIUM( |
| GLuint target, GLintptr offset, GLsizeiptr size, GLenum access) { |
| GPU_CLIENT_SINGLE_THREAD_CHECK(); |
| GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glMapBufferSubDataCHROMIUM(" |
| << target << ", " << offset << ", " << size << ", " |
| << GLES2Util::GetStringEnum(access) << ")"); |
| // NOTE: target is NOT checked because the service will check it |
| // and we don't know what targets are valid. |
| if (access != GL_WRITE_ONLY) { |
| SetGLErrorInvalidEnum( |
| "glMapBufferSubDataCHROMIUM", access, "access"); |
| return NULL; |
| } |
| if (offset < 0 || size < 0) { |
| SetGLError(GL_INVALID_VALUE, "glMapBufferSubDataCHROMIUM", "bad range"); |
| return NULL; |
| } |
| int32 shm_id; |
| unsigned int shm_offset; |
| void* mem = mapped_memory_->Alloc(size, &shm_id, &shm_offset); |
| if (!mem) { |
| SetGLError(GL_OUT_OF_MEMORY, "glMapBufferSubDataCHROMIUM", "out of memory"); |
| return NULL; |
| } |
| |
| std::pair<MappedBufferMap::iterator, bool> result = |
| mapped_buffers_.insert(std::make_pair( |
| mem, |
| MappedBuffer( |
| access, shm_id, mem, shm_offset, target, offset, size))); |
| GPU_DCHECK(result.second); |
| GPU_CLIENT_LOG(" returned " << mem); |
| return mem; |
| } |
| |
| void GLES2Implementation |