blob: 9a9cd85b411fd3ae1b41df80ee972cdf50e2223d [file] [log] [blame]
// Copyright (c) 2011 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 "../client/gles2_implementation.h"
#include <set>
#include <queue>
#include <GLES2/gl2ext.h>
#include "../client/mapped_memory.h"
#include "../client/program_info_manager.h"
#include "../common/gles2_cmd_utils.h"
#include "../common/id_allocator.h"
#include "../common/trace_event.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/gfx/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));
}
// An id handler for non-shared ids.
class NonSharedIdHandler : public IdHandlerInterface {
public:
NonSharedIdHandler() { }
virtual ~NonSharedIdHandler() { }
// Overridden from IdHandlerInterface.
virtual void MakeIds(GLuint id_offset, GLsizei n, GLuint* ids) {
if (id_offset == 0) {
for (GLsizei ii = 0; ii < n; ++ii) {
ids[ii] = id_allocator_.AllocateID();
}
} else {
for (GLsizei ii = 0; ii < n; ++ii) {
ids[ii] = id_allocator_.AllocateIDAtOrAbove(id_offset);
id_offset = ids[ii] + 1;
}
}
}
// Overridden from IdHandlerInterface.
virtual bool FreeIds(GLsizei n, const GLuint* ids) {
for (GLsizei ii = 0; ii < n; ++ii) {
id_allocator_.FreeID(ids[ii]);
}
return true;
}
// Overridden from IdHandlerInterface.
virtual bool MarkAsUsedForBind(GLuint id) {
return id == 0 ? true : id_allocator_.MarkAsUsed(id);
}
private:
IdAllocator id_allocator_;
};
// An id handler for non-shared ids that are never reused.
class NonSharedNonReusedIdHandler : public IdHandlerInterface {
public:
NonSharedNonReusedIdHandler() : last_id_(0) { }
virtual ~NonSharedNonReusedIdHandler() { }
// Overridden from IdHandlerInterface.
virtual void MakeIds(GLuint id_offset, GLsizei n, GLuint* ids) {
for (GLsizei ii = 0; ii < n; ++ii) {
ids[ii] = ++last_id_ + id_offset;
}
}
// Overridden from IdHandlerInterface.
virtual bool FreeIds(GLsizei /* n */, const GLuint* /* ids */) {
// Ids are never freed.
return true;
}
// Overridden from IdHandlerInterface.
virtual bool MarkAsUsedForBind(GLuint /* id */) {
// This is only used for Shaders and Programs which have no bind.
return false;
}
private:
GLuint last_id_;
};
// An id handler for shared ids.
class SharedIdHandler : public IdHandlerInterface {
public:
SharedIdHandler(
GLES2Implementation* gles2,
id_namespaces::IdNamespaces id_namespace)
: gles2_(gles2),
id_namespace_(id_namespace) {
}
virtual ~SharedIdHandler() { }
virtual void MakeIds(GLuint id_offset, GLsizei n, GLuint* ids) {
gles2_->GenSharedIdsCHROMIUM(id_namespace_, id_offset, n, ids);
}
virtual bool FreeIds(GLsizei n, const GLuint* ids) {
gles2_->DeleteSharedIdsCHROMIUM(id_namespace_, n, ids);
return true;
}
virtual bool MarkAsUsedForBind(GLuint /* id */) {
// This has no meaning for shared resources.
return true;
}
private:
GLES2Implementation* gles2_;
id_namespaces::IdNamespaces id_namespace_;
};
// An id handler for shared ids that requires ids are made before using and
// that only the context that created the id can delete it.
// Assumes the service will enforce that non made ids generate an error.
class StrictSharedIdHandler : public IdHandlerInterface {
public:
StrictSharedIdHandler(
GLES2Implementation* gles2,
id_namespaces::IdNamespaces id_namespace)
: gles2_(gles2),
id_namespace_(id_namespace) {
}
virtual ~StrictSharedIdHandler() { }
virtual void MakeIds(GLuint id_offset, GLsizei n, GLuint* ids) {
for (GLsizei ii = 0; ii < n; ++ii) {
ids[ii] = GetId(id_offset);
}
}
virtual bool FreeIds(GLsizei n, const GLuint* ids) {
// OpenGL sematics. If any id is bad none of them get freed.
for (GLsizei ii = 0; ii < n; ++ii) {
GLuint id = ids[ii];
if (id != 0) {
ResourceIdSet::iterator it = used_ids_.find(id);
if (it == used_ids_.end()) {
return false;
}
}
}
for (GLsizei ii = 0; ii < n; ++ii) {
GLuint id = ids[ii];
if (id != 0) {
ResourceIdSet::iterator it = used_ids_.find(id);
if (it != used_ids_.end()) {
used_ids_.erase(it);
free_ids_.push(id);
}
}
}
return true;
}
virtual bool MarkAsUsedForBind(GLuint /* id */) {
// This has no meaning for shared resources.
return true;
}
private:
static const GLsizei kNumIdsToGet = 2048;
typedef std::queue<GLuint> ResourceIdQueue;
typedef std::set<GLuint> ResourceIdSet;
GLuint GetId(GLuint id_offset) {
if (free_ids_.empty()) {
GLuint ids[kNumIdsToGet];
gles2_->GenSharedIdsCHROMIUM(id_namespace_, id_offset, kNumIdsToGet, ids);
for (GLsizei ii = 0; ii < kNumIdsToGet; ++ii) {
free_ids_.push(ids[ii]);
}
}
GLuint id = free_ids_.front();
free_ids_.pop();
used_ids_.insert(id);
return id;
}
bool FreeId(GLuint id) {
ResourceIdSet::iterator it = used_ids_.find(id);
if (it == used_ids_.end()) {
return false;
}
used_ids_.erase(it);
free_ids_.push(id);
return true;
}
GLES2Implementation* gles2_;
id_namespaces::IdNamespaces id_namespace_;
ResourceIdSet used_ids_;
ResourceIdQueue free_ids_;
};
#ifndef _MSC_VER
const GLsizei StrictSharedIdHandler::kNumIdsToGet;
#endif
static GLsizei RoundUpToMultipleOf4(GLsizei size) {
return (size + 3) & ~3;
}
// This class tracks VertexAttribPointers and helps emulate client side buffers.
//
// The way client side buffers work is we shadow all the Vertex Attribs so we
// know which ones are pointing to client side buffers.
//
// At Draw time, for any attribs pointing to client side buffers we copy them
// to a special VBO and reset the actual vertex attrib pointers to point to this
// VBO.
//
// This also means we have to catch calls to query those values so that when
// an attrib is a client side buffer we pass the info back the user expects.
class ClientSideBufferHelper {
public:
// Info about Vertex Attributes. This is used to track what the user currently
// has bound on each Vertex Attribute so we can simulate client side buffers
// at glDrawXXX time.
class VertexAttribInfo {
public:
VertexAttribInfo()
: enabled_(false),
buffer_id_(0),
size_(4),
type_(GL_FLOAT),
normalized_(GL_FALSE),
pointer_(NULL),
gl_stride_(0) {
}
bool enabled() const {
return enabled_;
}
void set_enabled(bool enabled) {
enabled_ = enabled;
}
GLuint buffer_id() const {
return buffer_id_;
}
GLenum type() const {
return type_;
}
GLint size() const {
return size_;
}
GLsizei stride() const {
return gl_stride_;
}
GLboolean normalized() const {
return normalized_;
}
const GLvoid* pointer() const {
return pointer_;
}
bool IsClientSide() const {
return buffer_id_ == 0;
}
void SetInfo(
GLuint buffer_id,
GLint size,
GLenum type,
GLboolean normalized,
GLsizei gl_stride,
const GLvoid* pointer) {
buffer_id_ = buffer_id;
size_ = size;
type_ = type;
normalized_ = normalized;
gl_stride_ = gl_stride;
pointer_ = pointer;
}
private:
// Whether or not this attribute is enabled.
bool enabled_;
// The id of the buffer. 0 = client side buffer.
GLuint buffer_id_;
// Number of components (1, 2, 3, 4).
GLint size_;
// GL_BYTE, GL_FLOAT, etc. See glVertexAttribPointer.
GLenum type_;
// GL_TRUE or GL_FALSE
GLboolean normalized_;
// The pointer/offset into the buffer.
const GLvoid* pointer_;
// The stride that will be used to access the buffer. This is the bogus GL
// stride where 0 = compute the stride based on size and type.
GLsizei gl_stride_;
};
ClientSideBufferHelper(GLuint max_vertex_attribs,
GLuint array_buffer_id,
GLuint element_array_buffer_id)
: max_vertex_attribs_(max_vertex_attribs),
num_client_side_pointers_enabled_(0),
array_buffer_id_(array_buffer_id),
array_buffer_size_(0),
array_buffer_offset_(0),
element_array_buffer_id_(element_array_buffer_id),
element_array_buffer_size_(0),
collection_buffer_size_(0) {
vertex_attrib_infos_.reset(new VertexAttribInfo[max_vertex_attribs]);
}
bool HaveEnabledClientSideBuffers() const {
return num_client_side_pointers_enabled_ > 0;
}
void SetAttribEnable(GLuint index, bool enabled) {
if (index < max_vertex_attribs_) {
VertexAttribInfo& info = vertex_attrib_infos_[index];
if (info.enabled() != enabled) {
if (info.IsClientSide()) {
num_client_side_pointers_enabled_ += enabled ? 1 : -1;
}
info.set_enabled(enabled);
}
}
}
void SetAttribPointer(
GLuint buffer_id,
GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride,
const void* ptr) {
if (index < max_vertex_attribs_) {
VertexAttribInfo& info = vertex_attrib_infos_[index];
if (info.IsClientSide() && info.enabled()) {
--num_client_side_pointers_enabled_;
}
info.SetInfo(buffer_id, size, type, normalized, stride, ptr);
if (info.IsClientSide() && info.enabled()) {
++num_client_side_pointers_enabled_;
}
}
}
// Gets the Attrib pointer for an attrib but only if it's a client side
// pointer. Returns true if it got the pointer.
bool GetAttribPointer(GLuint index, GLenum pname, void** ptr) const {
const VertexAttribInfo* info = GetAttribInfo(index);
if (info && pname == GL_VERTEX_ATTRIB_ARRAY_POINTER) {
*ptr = const_cast<void*>(info->pointer());
return true;
}
return false;
}
// Gets an attrib info if it's in range and it's client side.
const VertexAttribInfo* GetAttribInfo(GLuint index) const {
if (index < max_vertex_attribs_) {
VertexAttribInfo* info = &vertex_attrib_infos_[index];
if (info->IsClientSide()) {
return info;
}
}
return NULL;
}
// Collects the data into the collection buffer and returns the number of
// bytes collected.
GLsizei CollectData(const void* data,
GLsizei bytes_per_element,
GLsizei real_stride,
GLsizei num_elements) {
GLsizei bytes_needed = bytes_per_element * num_elements;
if (collection_buffer_size_ < bytes_needed) {
collection_buffer_.reset(new int8[bytes_needed]);
collection_buffer_size_ = bytes_needed;
}
const int8* src = static_cast<const int8*>(data);
int8* dst = collection_buffer_.get();
int8* end = dst + bytes_per_element * num_elements;
for (; dst < end; src += real_stride, dst += bytes_per_element) {
memcpy(dst, src, bytes_per_element);
}
return bytes_needed;
}
// Returns true if buffers were setup.
void SetupSimualtedClientSideBuffers(
GLES2Implementation* gl,
GLES2CmdHelper* gl_helper,
GLsizei num_elements) {
GLsizei total_size = 0;
// Compute the size of the buffer we need.
for (GLuint ii = 0; ii < max_vertex_attribs_; ++ii) {
VertexAttribInfo& info = vertex_attrib_infos_[ii];
if (info.IsClientSide() && info.enabled()) {
size_t bytes_per_element =
GLES2Util::GetGLTypeSizeForTexturesAndBuffers(info.type()) *
info.size();
total_size += RoundUpToMultipleOf4(
bytes_per_element * num_elements);
}
}
gl_helper->BindBuffer(GL_ARRAY_BUFFER, array_buffer_id_);
array_buffer_offset_ = 0;
if (total_size > array_buffer_size_) {
gl->BufferData(GL_ARRAY_BUFFER, total_size, NULL, GL_DYNAMIC_DRAW);
array_buffer_size_ = total_size;
}
for (GLuint ii = 0; ii < max_vertex_attribs_; ++ii) {
VertexAttribInfo& info = vertex_attrib_infos_[ii];
if (info.IsClientSide() && info.enabled()) {
size_t bytes_per_element =
GLES2Util::GetGLTypeSizeForTexturesAndBuffers(info.type()) *
info.size();
GLsizei real_stride = info.stride() ?
info.stride() : static_cast<GLsizei>(bytes_per_element);
GLsizei bytes_collected = CollectData(
info.pointer(), bytes_per_element, real_stride, num_elements);
gl->BufferSubData(
GL_ARRAY_BUFFER, array_buffer_offset_, bytes_collected,
collection_buffer_.get());
gl_helper->VertexAttribPointer(
ii, info.size(), info.type(), info.normalized(), 0,
array_buffer_offset_);
array_buffer_offset_ += RoundUpToMultipleOf4(bytes_collected);
GPU_DCHECK_LE(array_buffer_offset_, array_buffer_size_);
}
}
}
// Copies in indices to the service and returns the highest index accessed + 1
GLsizei SetupSimulatedIndexBuffer(
GLES2Implementation* gl,
GLES2CmdHelper* gl_helper,
GLsizei count,
GLenum type,
const void* indices) {
gl_helper->BindBuffer(GL_ELEMENT_ARRAY_BUFFER, element_array_buffer_id_);
GLsizei bytes_per_element =
GLES2Util::GetGLTypeSizeForTexturesAndBuffers(type);
GLsizei bytes_needed = bytes_per_element * count;
if (bytes_needed > element_array_buffer_size_) {
element_array_buffer_size_ = bytes_needed;
gl->BufferData(GL_ELEMENT_ARRAY_BUFFER, bytes_needed, NULL,
GL_DYNAMIC_DRAW);
}
gl->BufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, bytes_needed, indices);
GLsizei max_index = -1;
switch (type) {
case GL_UNSIGNED_BYTE: {
const uint8* src = static_cast<const uint8*>(indices);
for (GLsizei ii = 0; ii < count; ++ii) {
if (src[ii] > max_index) {
max_index = src[ii];
}
}
break;
}
case GL_UNSIGNED_SHORT: {
const uint16* src = static_cast<const uint16*>(indices);
for (GLsizei ii = 0; ii < count; ++ii) {
if (src[ii] > max_index) {
max_index = src[ii];
}
}
break;
}
default:
break;
}
return max_index + 1;
}
private:
GLuint max_vertex_attribs_;
GLuint num_client_side_pointers_enabled_;
GLuint array_buffer_id_;
GLsizei array_buffer_size_;
GLsizei array_buffer_offset_;
GLuint element_array_buffer_id_;
GLsizei element_array_buffer_size_;
scoped_array<VertexAttribInfo> vertex_attrib_infos_;
GLsizei collection_buffer_size_;
scoped_array<int8> collection_buffer_;
DISALLOW_COPY_AND_ASSIGN(ClientSideBufferHelper);
};
#if !defined(_MSC_VER)
const size_t GLES2Implementation::kMaxSizeOfSimpleResult;
#endif
COMPILE_ASSERT(gpu::kInvalidResource == 0,
INVALID_RESOURCE_NOT_0_AS_GL_EXPECTS);
GLES2Implementation::GLES2Implementation(
GLES2CmdHelper* helper,
size_t transfer_buffer_size,
void* transfer_buffer,
int32 transfer_buffer_id,
bool share_resources,
bool bind_generates_resource)
: helper_(helper),
transfer_buffer_(
kStartingOffset,
transfer_buffer_size - kStartingOffset,
helper,
static_cast<char*>(transfer_buffer) + kStartingOffset),
transfer_buffer_id_(transfer_buffer_id),
pack_alignment_(4),
unpack_alignment_(4),
unpack_flip_y_(false),
active_texture_unit_(0),
bound_framebuffer_(0),
bound_renderbuffer_(0),
bound_array_buffer_id_(0),
bound_element_array_buffer_id_(0),
client_side_array_id_(0),
client_side_element_array_id_(0),
error_bits_(0),
debug_(false),
sharing_resources_(share_resources),
bind_generates_resource_(bind_generates_resource) {
GPU_CLIENT_LOG_CODE_BLOCK({
debug_ = CommandLine::ForCurrentProcess()->HasSwitch(
switches::kEnableGPUClientLogging);
});
// Allocate space for simple GL results.
result_buffer_ = transfer_buffer;
result_shm_offset_ = 0;
memset(&reserved_ids_, 0, sizeof(reserved_ids_));
mapped_memory_.reset(new MappedMemoryManager(helper_));
if (share_resources) {
if (!bind_generates_resource) {
buffer_id_handler_.reset(
new StrictSharedIdHandler(this, id_namespaces::kBuffers));
framebuffer_id_handler_.reset(
new StrictSharedIdHandler(this, id_namespaces::kFramebuffers));
renderbuffer_id_handler_.reset(
new StrictSharedIdHandler(this, id_namespaces::kRenderbuffers));
program_and_shader_id_handler_.reset(
new StrictSharedIdHandler(this, id_namespaces::kProgramsAndShaders));
texture_id_handler_.reset(
new StrictSharedIdHandler(this, id_namespaces::kTextures));
} else {
buffer_id_handler_.reset(
new SharedIdHandler(this, id_namespaces::kBuffers));
framebuffer_id_handler_.reset(
new SharedIdHandler(this, id_namespaces::kFramebuffers));
renderbuffer_id_handler_.reset(
new SharedIdHandler(this, id_namespaces::kRenderbuffers));
program_and_shader_id_handler_.reset(
new SharedIdHandler(this, id_namespaces::kProgramsAndShaders));
texture_id_handler_.reset(
new SharedIdHandler(this, id_namespaces::kTextures));
}
} else {
buffer_id_handler_.reset(new NonSharedIdHandler());
framebuffer_id_handler_.reset(new NonSharedIdHandler());
renderbuffer_id_handler_.reset(new NonSharedIdHandler());
program_and_shader_id_handler_.reset(new NonSharedNonReusedIdHandler());
texture_id_handler_.reset(new NonSharedIdHandler());
}
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,
};
util_.set_num_compressed_texture_formats(
gl_state_.num_compressed_texture_formats);
util_.set_num_shader_binary_formats(
gl_state_.num_shader_binary_formats);
GetMultipleIntegervCHROMIUM(
pnames, arraysize(pnames), &gl_state_.max_combined_texture_image_units,
sizeof(gl_state_));
texture_units_.reset(
new TextureUnit[gl_state_.max_combined_texture_image_units]);
program_info_manager_.reset(ProgramInfoManager::Create(sharing_resources_));
#if defined(GLES2_SUPPORT_CLIENT_SIDE_ARRAYS)
buffer_id_handler_->MakeIds(
kClientSideArrayId, arraysize(reserved_ids_), &reserved_ids_[0]);
client_side_buffer_helper_.reset(new ClientSideBufferHelper(
gl_state_.max_vertex_attribs,
reserved_ids_[0],
reserved_ids_[1]));
#endif
}
GLES2Implementation::~GLES2Implementation() {
#if defined(GLES2_SUPPORT_CLIENT_SIDE_ARRAYS)
DeleteBuffers(arraysize(reserved_ids_), &reserved_ids_[0]);
#endif
}
void GLES2Implementation::FreeUnusedSharedMemory() {
mapped_memory_->FreeUnused();
}
void GLES2Implementation::WaitForCmd() {
TRACE_EVENT0("gpu", "GLES2::WaitForCmd");
helper_->CommandBufferHelper::Finish();
}
GLenum GLES2Implementation::GetError() {
GPU_CLIENT_LOG("[" << this << "] glGetError()");
GLenum err = GetGLError();
GPU_CLIENT_LOG("returned " << GLES2Util::GetStringError(err));
return err;
}
GLenum GLES2Implementation::GetGLError() {
TRACE_EVENT0("gpu", "GLES2::GetGLError");
// Check the GL error first, then our wrapped error.
typedef gles2::GetError::Result Result;
Result* result = GetResultAs<Result*>();
*result = GL_NO_ERROR;
helper_->GetError(result_shm_id(), result_shm_offset());
WaitForCmd();
GLenum error = *result;
if (error == GL_NO_ERROR && error_bits_ != 0) {
for (uint32 mask = 1; mask != 0; mask = mask << 1) {
if ((error_bits_ & mask) != 0) {
error = GLES2Util::GLErrorBitToGLError(mask);
break;
}
}
}
if (error != GL_NO_ERROR) {
// There was an error, clear the corresponding wrapped error.
error_bits_ &= ~GLES2Util::GLErrorToErrorBit(error);
}
return error;
}
void GLES2Implementation::SetGLError(GLenum error, const char* msg) {
GPU_CLIENT_LOG("[" << this << "] Client Synthesized Error: "
<< GLES2Util::GetStringError(error) << ": " << msg);
if (msg) {
last_error_ = msg;
}
error_bits_ |= GLES2Util::GLErrorToErrorBit(error);
}
void GLES2Implementation::GetBucketContents(uint32 bucket_id,
std::vector<int8>* data) {
TRACE_EVENT0("gpu", "GLES2::GetBucketContents");
GPU_DCHECK(data);
typedef cmd::GetBucketSize::Result Result;
Result* result = GetResultAs<Result*>();
*result = 0;
helper_->GetBucketSize(bucket_id, result_shm_id(), result_shm_offset());
WaitForCmd();
uint32 size = *result;
data->resize(size);
if (size > 0u) {
uint32 max_size = transfer_buffer_.GetLargestFreeOrPendingSize();
uint32 offset = 0;
while (size) {
uint32 part_size = std::min(max_size, size);
void* buffer = transfer_buffer_.Alloc(part_size);
helper_->GetBucketData(
bucket_id, offset, part_size,
transfer_buffer_id_, transfer_buffer_.GetOffset(buffer));
WaitForCmd();
memcpy(&(*data)[offset], buffer, part_size);
transfer_buffer_.FreePendingToken(buffer, helper_->InsertToken());
offset += part_size;
size -= part_size;
}
// 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);
}
}
void GLES2Implementation::SetBucketContents(
uint32 bucket_id, const void* data, size_t size) {
GPU_DCHECK(data);
helper_->SetBucketSize(bucket_id, size);
if (size > 0u) {
uint32 max_size = transfer_buffer_.GetLargestFreeOrPendingSize();
uint32 offset = 0;
while (size) {
uint32 part_size = std::min(static_cast<size_t>(max_size), size);
void* buffer = transfer_buffer_.Alloc(part_size);
memcpy(buffer, static_cast<const int8*>(data) + offset, part_size);
helper_->SetBucketData(
bucket_id, offset, part_size,
transfer_buffer_id_, transfer_buffer_.GetOffset(buffer));
transfer_buffer_.FreePendingToken(buffer, helper_->InsertToken());
offset += part_size;
size -= part_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
GetBucketContents(bucket_id, &data);
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);
}
bool GLES2Implementation::GetHelper(GLenum pname, GLint* params) {
switch (pname) {
case GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS:
*params = gl_state_.max_combined_texture_image_units;
return true;
case GL_MAX_CUBE_MAP_TEXTURE_SIZE:
*params = gl_state_.max_cube_map_texture_size;
return true;
case GL_MAX_FRAGMENT_UNIFORM_VECTORS:
*params = gl_state_.max_fragment_uniform_vectors;
return true;
case GL_MAX_RENDERBUFFER_SIZE:
*params = gl_state_.max_renderbuffer_size;
return true;
case GL_MAX_TEXTURE_IMAGE_UNITS:
*params = gl_state_.max_texture_image_units;
return true;
case GL_MAX_TEXTURE_SIZE:
*params = gl_state_.max_texture_size;
return true;
case GL_MAX_VARYING_VECTORS:
*params = gl_state_.max_varying_vectors;
return true;
case GL_MAX_VERTEX_ATTRIBS:
*params = gl_state_.max_vertex_attribs;
return true;
case GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS:
*params = gl_state_.max_vertex_texture_image_units;
return true;
case GL_MAX_VERTEX_UNIFORM_VECTORS:
*params = gl_state_.max_vertex_uniform_vectors;
return true;
case GL_NUM_COMPRESSED_TEXTURE_FORMATS:
*params = gl_state_.num_compressed_texture_formats;
return true;
case GL_NUM_SHADER_BINARY_FORMATS:
*params = gl_state_.num_shader_binary_formats;
return true;
case GL_ARRAY_BUFFER_BINDING:
if (bind_generates_resource_) {
*params = bound_array_buffer_id_;
return true;
}
return false;
case GL_ELEMENT_ARRAY_BUFFER_BINDING:
if (bind_generates_resource_) {
*params = bound_element_array_buffer_id_;
return true;
}
return false;
case GL_ACTIVE_TEXTURE:
*params = active_texture_unit_ + GL_TEXTURE0;
return true;
case GL_TEXTURE_BINDING_2D:
if (bind_generates_resource_) {
*params = texture_units_[active_texture_unit_].bound_texture_2d;
return true;
}
return false;
case GL_TEXTURE_BINDING_CUBE_MAP:
if (bind_generates_resource_) {
*params = texture_units_[active_texture_unit_].bound_texture_cube_map;
return true;
}
return false;
case GL_FRAMEBUFFER_BINDING:
if (bind_generates_resource_) {
*params = bound_framebuffer_;
return true;
}
return false;
case GL_RENDERBUFFER_BINDING:
if (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);
}
void GLES2Implementation::DrawElements(
GLenum mode, GLsizei count, GLenum type, const void* indices) {
GPU_CLIENT_LOG("[" << this << "] 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;
}
#if defined(GLES2_SUPPORT_CLIENT_SIDE_ARRAYS)
bool have_client_side =
client_side_buffer_helper_->HaveEnabledClientSideBuffers();
GLsizei num_elements = 0;
GLuint offset = ToGLuint(indices);
if (bound_element_array_buffer_id_ == 0) {
// Index buffer is client side array.
// Copy to buffer, scan for highest index.
num_elements = client_side_buffer_helper_->SetupSimulatedIndexBuffer(
this, helper_, count, type, indices);
offset = 0;
} else {
// Index buffer is GL buffer. Ask the service for the highest vertex
// that will be accessed. Note: It doesn't matter if another context
// changes the contents of any of the buffers. The service will still
// validate the indices. We just need to know how much to copy across.
if (have_client_side) {
num_elements = GetMaxValueInBufferCHROMIUM(
bound_element_array_buffer_id_, count, type, ToGLuint(indices)) + 1;
}
}
if (have_client_side) {
client_side_buffer_helper_->SetupSimualtedClientSideBuffers(
this, helper_, num_elements);
}
helper_->DrawElements(mode, count, type, offset);
if (have_client_side) {
// Restore the user's current binding.
helper_->BindBuffer(GL_ARRAY_BUFFER, bound_array_buffer_id_);
}
if (bound_element_array_buffer_id_ == 0) {
// Restore the element array binding.
helper_->BindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
#else
helper_->DrawElements(mode, count, type, ToGLuint(indices));
#endif
}
void GLES2Implementation::Flush() {
GPU_CLIENT_LOG("[" << this << "] 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::Finish() {
GPU_CLIENT_LOG("[" << this << "] 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_LOG("[" << this << "] 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("[" << this << "] glGenSharedIdsCHROMIUMTextures("
<< namespace_id << ", " << id_offset << ", " << n << ", " <<
static_cast<void*>(ids) << ")");
GPU_CLIENT_LOG_CODE_BLOCK({
for (GLsizei i = 0; i < n; ++i) {
GPU_CLIENT_LOG(" " << i << ": " << ids[i]);
}
});
TRACE_EVENT0("gpu", "GLES2::GenSharedIdsCHROMIUM");
GLsizei max_size = transfer_buffer_.GetLargestFreeOrPendingSize();
GLsizei max_num_per = max_size / sizeof(ids[0]);
while (n) {
GLsizei num = std::min(n, max_num_per);
GLint* id_buffer = transfer_buffer_.AllocTyped<GLint>(num);
helper_->GenSharedIdsCHROMIUM(
namespace_id, id_offset, num,
transfer_buffer_id_,
transfer_buffer_.GetOffset(id_buffer));
WaitForCmd();
memcpy(ids, id_buffer, sizeof(*ids) * num);
transfer_buffer_.FreePendingToken(id_buffer, helper_->InsertToken());
n -= num;
ids += num;
}
}
void GLES2Implementation::DeleteSharedIdsCHROMIUM(
GLuint namespace_id, GLsizei n, const GLuint* ids) {
GPU_CLIENT_LOG("[" << this << "] 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 << ": " << ids[i]);
}
});
TRACE_EVENT0("gpu", "GLES2::DeleteSharedIdsCHROMIUM");
GLsizei max_size = transfer_buffer_.GetLargestFreeOrPendingSize();
GLsizei max_num_per = max_size / sizeof(ids[0]);
while (n) {
GLsizei num = std::min(n, max_num_per);
GLint* id_buffer = transfer_buffer_.AllocTyped<GLint>(num);
memcpy(id_buffer, ids, sizeof(*ids) * num);
helper_->DeleteSharedIdsCHROMIUM(
namespace_id, num,
transfer_buffer_id_,
transfer_buffer_.GetOffset(id_buffer));
WaitForCmd();
transfer_buffer_.FreePendingToken(id_buffer, helper_->InsertToken());
n -= num;
ids += num;
}
}
void GLES2Implementation::RegisterSharedIdsCHROMIUM(
GLuint namespace_id, GLsizei n, const GLuint* ids) {
GPU_CLIENT_LOG("[" << this << "] 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 << ": " << ids[i]);
}
});
TRACE_EVENT0("gpu", "GLES2::RegisterSharedIdsCHROMIUM");
GLsizei max_size = transfer_buffer_.GetLargestFreeOrPendingSize();
GLsizei max_num_per = max_size / sizeof(ids[0]);
while (n) {
GLsizei num = std::min(n, max_num_per);
GLint* id_buffer = transfer_buffer_.AllocTyped<GLint>(n);
memcpy(id_buffer, ids, sizeof(*ids) * n);
helper_->RegisterSharedIdsCHROMIUM(
namespace_id, n,
transfer_buffer_id_,
transfer_buffer_.GetOffset(id_buffer));
WaitForCmd();
transfer_buffer_.FreePendingToken(id_buffer, helper_->InsertToken());
n -= num;
ids += num;
}
}
void GLES2Implementation::BindAttribLocation(
GLuint program, GLuint index, const char* name) {
GPU_CLIENT_LOG("[" << this << "] glBindAttribLocation(" << program << ", "
<< index << ", " << name << ")");
SetBucketAsString(kResultBucketId, name);
helper_->BindAttribLocationBucket(program, index, kResultBucketId);
helper_->SetBucketSize(kResultBucketId, 0);
}
void GLES2Implementation::GetVertexAttribPointerv(
GLuint index, GLenum pname, void** ptr) {
GPU_CLIENT_LOG("[" << this << "] glGetVertexAttribPointer(" << index << ", "
<< GLES2Util::GetStringVertexPointer(pname) << ", "
<< static_cast<void*>(ptr) << ")");
#if defined(GLES2_SUPPORT_CLIENT_SIDE_ARRAYS)
// If it's a client side buffer the client has the data.
if (client_side_buffer_helper_->GetAttribPointer(index, pname, ptr)) {
return;
}
#endif // defined(GLES2_SUPPORT_CLIENT_SIDE_ARRAYS)
TRACE_EVENT0("gpu", "GLES2::GetVertexAttribPointerv");
typedef gles2::GetVertexAttribPointerv::Result Result;
Result* result = GetResultAs<Result*>();
result->SetNumResults(0);
helper_->GetVertexAttribPointerv(
index, pname, result_shm_id(), result_shm_offset());
WaitForCmd();
result->CopyResult(ptr);
GPU_CLIENT_LOG_CODE_BLOCK({
for (int32 i = 0; i < result->GetNumResults(); ++i) {
GPU_CLIENT_LOG(" " << i << ": " << result->GetData()[i]);
}
});
}
bool GLES2Implementation::DeleteProgramHelper(GLuint program) {
if (!program_and_shader_id_handler_->FreeIds(1, &program)) {
SetGLError(
GL_INVALID_VALUE,
"glDeleteProgram: id not created by this context.");
return false;
}
program_info_manager_->DeleteInfo(program);
helper_->DeleteProgram(program);
Flush();
return true;
}
bool GLES2Implementation::DeleteShaderHelper(GLuint shader) {
if (!program_and_shader_id_handler_->FreeIds(1, &shader)) {
SetGLError(
GL_INVALID_VALUE,
"glDeleteShader: id not created by this context.");
return false;
}
program_info_manager_->DeleteInfo(shader);
helper_->DeleteShader(shader);
Flush();
return true;
}
GLint GLES2Implementation::GetAttribLocationHelper(
GLuint program, const char* name) {
typedef GetAttribLocationBucket::Result Result;
Result* result = GetResultAs<Result*>();
*result = -1;
SetBucketAsCString(kResultBucketId, name);
helper_->GetAttribLocationBucket(
program, kResultBucketId, result_shm_id(), result_shm_offset());
WaitForCmd();
helper_->SetBucketSize(kResultBucketId, 0);
return *result;
}
GLint GLES2Implementation::GetAttribLocation(
GLuint program, const char* name) {
GPU_CLIENT_LOG("[" << this << "] glGetAttribLocation(" << program
<< ", " << name << ")");
TRACE_EVENT0("gpu", "GLES2::GetAttribLocation");
GLint loc = program_info_manager_->GetAttribLocation(this, program, name);
GPU_CLIENT_LOG("returned " << loc);
return loc;
}
GLint GLES2Implementation::GetUniformLocationHelper(
GLuint program, const char* name) {
typedef GetUniformLocationBucket::Result Result;
Result* result = GetResultAs<Result*>();
*result = -1;
SetBucketAsCString(kResultBucketId, name);
helper_->GetUniformLocationBucket(program, kResultBucketId,
result_shm_id(), result_shm_offset());
WaitForCmd();
helper_->SetBucketSize(kResultBucketId, 0);
return *result;
}
GLint GLES2Implementation::GetUniformLocation(
GLuint program, const char* name) {
GPU_CLIENT_LOG("[" << this << "] glGetUniformLocation(" << program
<< ", " << name << ")");
TRACE_EVENT0("gpu", "GLES2::GetUniformLocation");
GLint loc = program_info_manager_->GetUniformLocation(this, program, name);
GPU_CLIENT_LOG("returned " << loc);
return loc;
}
bool GLES2Implementation::GetProgramivHelper(
GLuint program, GLenum pname, GLint* params) {
return program_info_manager_->GetProgramiv(this, program, pname, params);
}
void GLES2Implementation::LinkProgram(GLuint program) {
GPU_CLIENT_LOG("[" << this << "] glLinkProgram(" << program << ")");
helper_->LinkProgram(program);
program_info_manager_->CreateInfo(program);
}
void GLES2Implementation::ShaderBinary(
GLsizei n, const GLuint* shaders, GLenum binaryformat, const void* binary,
GLsizei length) {
GPU_CLIENT_LOG("[" << this << "] 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;
}
GLsizei shader_id_size = n * sizeof(*shaders);
int8* buffer = transfer_buffer_.AllocTyped<int8>(shader_id_size + length);
void* shader_ids = buffer;
void* shader_data = buffer + shader_id_size;
memcpy(shader_ids, shaders, shader_id_size);
memcpy(shader_data, binary, length);
helper_->ShaderBinary(
n,
transfer_buffer_id_, transfer_buffer_.GetOffset(shader_ids),
binaryformat,
transfer_buffer_id_, transfer_buffer_.GetOffset(shader_data),
length);
transfer_buffer_.FreePendingToken(buffer, helper_->InsertToken());
}
void GLES2Implementation::PixelStorei(GLenum pname, GLint param) {
GPU_CLIENT_LOG("[" << this << "] 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_FLIP_Y_CHROMIUM:
unpack_flip_y_ = (param != 0);
return;
default:
break;
}
helper_->PixelStorei(pname, param);
}
void GLES2Implementation::VertexAttribPointer(
GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride,
const void* ptr) {
GPU_CLIENT_LOG("[" << this << "] glVertexAttribPointer("
<< index << ", "
<< size << ", "
<< GLES2Util::GetStringVertexAttribType(type) << ", "
<< GLES2Util::GetStringBool(normalized) << ", "
<< stride << ", "
<< static_cast<const void*>(ptr) << ")");
#if defined(GLES2_SUPPORT_CLIENT_SIDE_ARRAYS)
// Record the info on the client side.
client_side_buffer_helper_->SetAttribPointer(
bound_array_buffer_id_, index, size, type, normalized, stride, ptr);
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)
}
void GLES2Implementation::ShaderSource(
GLuint shader, GLsizei count, const char** source, const GLint* length) {
GPU_CLIENT_LOG("[" << this << "] 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 max_size = transfer_buffer_.GetLargestFreeOrPendingSize();
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) {
uint32 part_size = std::min(size, max_size);
void* buffer = transfer_buffer_.Alloc(part_size);
memcpy(buffer, src, part_size);
helper_->SetBucketData(kResultBucketId, offset, part_size,
transfer_buffer_id_,
transfer_buffer_.GetOffset(buffer));
transfer_buffer_.FreePendingToken(buffer, helper_->InsertToken());
offset += part_size;
src += part_size;
size -= part_size;
}
}
}
GPU_DCHECK_EQ(total_size, offset);
helper_->ShaderSourceBucket(shader, kResultBucketId);
helper_->SetBucketSize(kResultBucketId, 0);
}
void GLES2Implementation::BufferData(
GLenum target, GLsizeiptr size, const void* data, GLenum usage) {
GPU_CLIENT_LOG("[" << this << "] glBufferData("
<< GLES2Util::GetStringBufferTarget(target) << ", "
<< size << ", "
<< static_cast<const void*>(data) << ", "
<< GLES2Util::GetStringBufferUsage(usage) << ")");
GLsizeiptr max_size = transfer_buffer_.GetLargestFreeOrPendingSize();
if (size > max_size || !data) {
helper_->BufferData(target, size, 0, 0, usage);
if (data != NULL) {
BufferSubData(target, 0, size, data);
}
return;
}
void* buffer = transfer_buffer_.Alloc(size);
memcpy(buffer, data, size);
helper_->BufferData(
target,
size,
transfer_buffer_id_,
transfer_buffer_.GetOffset(buffer),
usage);
transfer_buffer_.FreePendingToken(buffer, helper_->InsertToken());
}
void GLES2Implementation::BufferSubData(
GLenum target, GLintptr offset, GLsizeiptr size, const void* data) {
GPU_CLIENT_LOG("[" << this << "] glBufferSubData("
<< GLES2Util::GetStringBufferTarget(target) << ", "
<< offset << ", " << size << ", "
<< static_cast<const void*>(data) << ")");
if (size == 0) {
return;
}
if (size < 0) {
SetGLError(GL_INVALID_VALUE, "glBufferSubData: size < 0");
return;
}
const int8* source = static_cast<const int8*>(data);
GLsizeiptr max_size = transfer_buffer_.GetLargestFreeOrPendingSize();
while (size) {
GLsizeiptr part_size = std::min(size, max_size);
void* buffer = transfer_buffer_.Alloc(part_size);
memcpy(buffer, source, part_size);
helper_->BufferSubData(target, offset, part_size,
transfer_buffer_id_,
transfer_buffer_.GetOffset(buffer));
transfer_buffer_.FreePendingToken(buffer, helper_->InsertToken());
offset += part_size;
source += part_size;
size -= part_size;
}
}
void GLES2Implementation::CompressedTexImage2D(
GLenum target, GLint level, GLenum internalformat, GLsizei width,
GLsizei height, GLint border, GLsizei image_size, const void* data) {
GPU_CLIENT_LOG("[" << this << "] 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;
}
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);
}
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_LOG("[" << this << "] 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;
}
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);
}
bool GLES2Implementation::CopyRectToBufferFlipped(
const void* pixels,
GLsizei width,
GLsizei height,
GLenum format,
GLenum type,
void* buffer) {
if (width == 0 || height == 0) {
return true;
}
uint32 temp_size;
if (!GLES2Util::ComputeImageDataSize(
width, 1, format, type, unpack_alignment_, &temp_size)) {
SetGLError(GL_INVALID_VALUE, "glTexSubImage2D: size to large");
return false;
}
GLsizeiptr unpadded_row_size = temp_size;
if (!GLES2Util::ComputeImageDataSize(
width, 2, format, type, unpack_alignment_, &temp_size)) {
SetGLError(GL_INVALID_VALUE, "glTexSubImage2D: size to large");
return false;
}
GLsizeiptr padded_row_size = temp_size - unpadded_row_size;
if (padded_row_size < 0 || unpadded_row_size < 0) {
SetGLError(GL_INVALID_VALUE, "glTexSubImage2D: size to large");
return false;
}
const int8* source = static_cast<const int8*>(pixels);
int8* dest = static_cast<int8*>(buffer) + padded_row_size * (height - 1);
for (; height; --height) {
memcpy(dest, source, unpadded_row_size);
dest -= padded_row_size;
source += padded_row_size;
}
return true;
}
void GLES2Implementation::TexImage2D(
GLenum target, GLint level, GLint internalformat, GLsizei width,
GLsizei height, GLint border, GLenum format, GLenum type,
const void* pixels) {
GPU_CLIENT_LOG("[" << this << "] 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;
if (!GLES2Util::ComputeImageDataSize(
width, height, format, type, unpack_alignment_, &size)) {
SetGLError(GL_INVALID_VALUE, "glTexImage2D: image size too large");
return;
}
// Check if we can send it all at once.
unsigned int max_size = transfer_buffer_.GetLargestFreeOrPendingSize();
if (size > max_size || !pixels) {
// No, so send it using TexSubImage2D.
helper_->TexImage2D(
target, level, internalformat, width, height, border, format, type,
0, 0);
if (pixels) {
TexSubImage2DImpl(
target, level, 0, 0, width, height, format, type, pixels, GL_TRUE);
}
return;
}
void* buffer = transfer_buffer_.Alloc(size);
bool copy_success = true;
if (unpack_flip_y_) {
copy_success = CopyRectToBufferFlipped(
pixels, width, height, format, type, buffer);
} else {
memcpy(buffer, pixels, size);
}
if (copy_success) {
helper_->TexImage2D(
target, level, internalformat, width, height, border, format, type,
transfer_buffer_id_, transfer_buffer_.GetOffset(buffer));
}
transfer_buffer_.FreePendingToken(buffer, helper_->InsertToken());
}
void GLES2Implementation::TexSubImage2D(
GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width,
GLsizei height, GLenum format, GLenum type, const void* pixels) {
GPU_CLIENT_LOG("[" << this << "] glTexSubImage2D("
<< GLES2Util::GetStringTextureTarget(target) << ", "
<< level << ", "
<< xoffset << ", " << yoffset << ", "
<< width << ", " << height << ", "
<< GLES2Util::GetStringTextureFormat(format) << ", "
<< GLES2Util::GetStringPixelType(type) << ", "
<< static_cast<const void*>(pixels) << ")");
TexSubImage2DImpl(
target, level, xoffset, yoffset, width, height, format, type, pixels,
GL_FALSE);
}
void GLES2Implementation::TexSubImage2DImpl(
GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width,
GLsizei height, GLenum format, GLenum type, const void* pixels,
GLboolean internal) {
if (level < 0 || height < 0 || width < 0) {
SetGLError(GL_INVALID_VALUE, "glTexSubImage2D dimension < 0");
return;
}
if (height == 0 || width == 0) {
return;
}
const int8* source = static_cast<const int8*>(pixels);
GLsizeiptr max_size = transfer_buffer_.GetLargestFreeOrPendingSize();
uint32 temp_size;
if (!GLES2Util::ComputeImageDataSize(
width, 1, format, type, unpack_alignment_, &temp_size)) {
SetGLError(GL_INVALID_VALUE, "glTexSubImage2D: size to large");
return;
}
GLsizeiptr unpadded_row_size = temp_size;
if (!GLES2Util::ComputeImageDataSize(
width, 2, format, type, unpack_alignment_, &temp_size)) {
SetGLError(GL_INVALID_VALUE, "glTexSubImage2D: size to large");
return;
}
GLsizeiptr padded_row_size = temp_size - unpadded_row_size;
if (padded_row_size < 0 || unpadded_row_size < 0) {
SetGLError(GL_INVALID_VALUE, "glTexSubImage2D: size to large");
return;
}
GLsizei original_height = height;
if (padded_row_size <= max_size) {
// Transfer by rows.
GLint max_rows = max_size / std::max(padded_row_size,
static_cast<GLsizeiptr>(1));
while (height) {
GLint num_rows = std::min(height, max_rows);
GLsizeiptr part_size =
(num_rows - 1) * padded_row_size + unpadded_row_size;
void* buffer = transfer_buffer_.Alloc(part_size);
GLint y;
if (unpack_flip_y_) {
CopyRectToBufferFlipped(
source, width, num_rows, format, type, buffer);
// GPU_DCHECK(copy_success); // can't check this because bot fails!
y = original_height - yoffset - num_rows;
} else {
memcpy(buffer, source, part_size);
y = yoffset;
}
helper_->TexSubImage2D(
target, level, xoffset, y, width, num_rows, format, type,
transfer_buffer_id_, transfer_buffer_.GetOffset(buffer), internal);
transfer_buffer_.FreePendingToken(buffer, helper_->InsertToken());
yoffset += num_rows;
source += num_rows * padded_row_size;
height -= num_rows;
}
} else {
// Transfer by sub rows. Because GL has no maximum texture dimensions.
uint32 temp;
GLES2Util::ComputeImageDataSize(
1, 1, format, type, unpack_alignment_, &temp);
GLsizeiptr element_size = temp;
max_size -= max_size % element_size;
GLint max_sub_row_pixels = max_size / element_size;
for (; height; --height) {
GLint temp_width = width;
GLint temp_xoffset = xoffset;
const int8* row_source = source;
while (temp_width) {
GLint num_pixels = std::min(width, max_sub_row_pixels);
GLsizeiptr part_size = num_pixels * element_size;
void* buffer = transfer_buffer_.Alloc(part_size);
memcpy(buffer, row_source, part_size);
GLint y = unpack_flip_y_ ? (original_height - yoffset - 1) : yoffset;
helper_->TexSubImage2D(
target, level, temp_xoffset, y, num_pixels, 1, format, type,
transfer_buffer_id_, transfer_buffer_.GetOffset(buffer), internal);
transfer_buffer_.FreePendingToken(buffer, helper_->InsertToken());
row_source += part_size;
temp_xoffset += num_pixels;
temp_width -= num_pixels;
}
++yoffset;
source += padded_row_size;
}
}
}
bool GLES2Implementation::GetActiveAttribHelper(
GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size,
GLenum* type, char* name) {
// Clear the bucket so if the command fails nothing will be in it.
helper_->SetBucketSize(kResultBucketId, 0);
typedef gles2::GetActiveAttrib::Result Result;
Result* result = static_cast<Result*>(result_buffer_);
// Set as failed so if the command fails we'll recover.
result->success = false;
helper_->GetActiveAttrib(program, index, kResultBucketId,
result_shm_id(), result_shm_offset());
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_LOG("[" << this << "] 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 = 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);
}
}
}
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 gles2::GetActiveUniform::Result Result;
Result* result = static_cast<Result*>(result_buffer_);
// Set as failed so if the command fails we'll recover.
result->success = false;
helper_->GetActiveUniform(program, index, kResultBucketId,
result_shm_id(), result_shm_offset());
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_LOG("[" << this << "] 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 = 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);
}
}
}
void GLES2Implementation::GetAttachedShaders(
GLuint program, GLsizei maxcount, GLsizei* count, GLuint* shaders) {
GPU_CLIENT_LOG("[" << this << "] 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 gles2::GetAttachedShaders::Result Result;
uint32 size = Result::ComputeSize(maxcount);
Result* result = transfer_buffer_.AllocTyped<Result>(size);
result->SetNumResults(0);
helper_->GetAttachedShaders(
program,
transfer_buffer_id_,
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);
}
void GLES2Implementation::GetShaderPrecisionFormat(
GLenum shadertype, GLenum precisiontype, GLint* range, GLint* precision) {
GPU_CLIENT_LOG("[" << this << "] glGetShaderPrecisionFormat("
<< GLES2Util::GetStringShaderType(shadertype) << ", "
<< GLES2Util::GetStringShaderPrecision(precisiontype) << ", "
<< static_cast<const void*>(range) << ", "
<< static_cast<const void*>(precision) << ", ");
TRACE_EVENT0("gpu", "GLES2::GetShaderPrecisionFormat");
typedef gles2::GetShaderPrecisionFormat::Result Result;
Result* result = static_cast<Result*>(result_buffer_);
result->success = false;
helper_->GetShaderPrecisionFormat(
shadertype, precisiontype, result_shm_id(), result_shm_offset());
WaitForCmd();
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]);
}
}
}
const GLubyte* GLES2Implementation::GetString(GLenum name) {
GPU_CLIENT_LOG("[" << this << "] glGetString("
<< GLES2Util::GetStringStringType(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_map_sub "
"GL_CHROMIUM_flipy";
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();
}
}
GPU_CLIENT_LOG(" returned " << static_cast<const char*>(result));
return reinterpret_cast<const GLubyte*>(result);
}
void GLES2Implementation::GetUniformfv(
GLuint program, GLint location, GLfloat* params) {
GPU_CLIENT_LOG("[" << this << "] glGetUniformfv("
<< program << ", " << location << ", "
<< static_cast<const void*>(params) << ")");
TRACE_EVENT0("gpu", "GLES2::GetUniformfv");
typedef gles2::GetUniformfv::Result Result;
Result* result = static_cast<Result*>(result_buffer_);
result->SetNumResults(0);
helper_->GetUniformfv(
program, location, result_shm_id(), result_shm_offset());
WaitForCmd();
result->CopyResult(params);
GPU_CLIENT_LOG_CODE_BLOCK({
for (int32 i = 0; i < result->GetNumResults(); ++i) {
GPU_CLIENT_LOG(" " << i << ": " << result->GetData()[i]);
}
});
}
void GLES2Implementation::GetUniformiv(
GLuint program, GLint location, GLint* params) {
GPU_CLIENT_LOG("[" << this << "] glGetUniformiv("
<< program << ", " << location << ", "
<< static_cast<const void*>(params) << ")");
TRACE_EVENT0("gpu", "GLES2::GetUniformiv");
typedef gles2::GetUniformiv::Result Result;
Result* result = static_cast<Result*>(result_buffer_);
result->SetNumResults(0);
helper_->GetUniformiv(
program, location, result_shm_id(), result_shm_offset());
WaitForCmd();
static_cast<gles2::GetUniformfv::Result*>(result_buffer_)->CopyResult(params);
GPU_CLIENT_LOG_CODE_BLOCK({
for (int32 i = 0; i < result->GetNumResults(); ++i) {
GPU_CLIENT_LOG(" " << i << ": " << result->GetData()[i]);
}
});
}
void GLES2Implementation::ReadPixels(
GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format,
GLenum type, void* pixels) {
GPU_CLIENT_LOG("[" << this << "] 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 gles2::ReadPixels::Result Result;
Result* result = static_cast<Result*>(result_buffer_);
int8* dest = reinterpret_cast<int8*>(pixels);
GLsizeiptr max_size = transfer_buffer_.GetLargestFreeOrPendingSize();
uint32 temp_size;
if (!GLES2Util::ComputeImageDataSize(
width, 1, format, type, pack_alignment_, &temp_size)) {
SetGLError(GL_INVALID_VALUE, "glReadPixels: size too large.");
return;
}
GLsizeiptr unpadded_row_size = temp_size;
if (!GLES2Util::ComputeImageDataSize(
width, 2, format, type, pack_alignment_, &temp_size)) {
SetGLError(GL_INVALID_VALUE, "glReadPixels: size too large.");
return;
}
GLsizeiptr padded_row_size = temp_size - unpadded_row_size;
if (padded_row_size < 0 || unpadded_row_size < 0) {
SetGLError(GL_INVALID_VALUE, "glReadPixels: size too large.");
return;
}
// Check if we have enough space to transfer at least an entire row.
if (padded_row_size <= max_size) {
// Transfer by rows.
// The max rows we can transfer.
GLint max_rows = max_size / std::max(padded_row_size,
static_cast<GLsizeiptr>(1));
while (height) {
// Compute how many rows to transfer.
GLint num_rows = std::min(height, max_rows);
// Compute how much space those rows will take. The last row will not
// include padding.
GLsizeiptr part_size =
unpadded_row_size + padded_row_size * (num_rows - 1);
void* buffer = transfer_buffer_.Alloc(part_size);
*result = 0; // mark as failed.
helper_->ReadPixels(
xoffset, yoffset, width, num_rows, format, type,
transfer_buffer_id_, transfer_buffer_.GetOffset(buffer),
result_shm_id(), result_shm_offset());
WaitForCmd();
if (*result != 0) {
// We have to copy 1 row at a time to avoid writing pad bytes.
const int8* src = static_cast<const int8*>(buffer);
for (GLint yy = 0; yy < num_rows; ++yy) {
memcpy(dest, src, unpadded_row_size);
dest += padded_row_size;
src += padded_row_size;
}
}
transfer_buffer_.FreePendingToken(buffer, helper_->InsertToken());
// If it was not marked as successful exit.
if (*result == 0) {
return;
}
yoffset += num_rows;
height -= num_rows;
}
} else {
// Transfer by sub rows. Because GL has no maximum texture dimensions.
GLES2Util::ComputeImageDataSize(
1, 1, format, type, pack_alignment_, &temp_size);
GLsizeiptr element_size = temp_size;
max_size -= max_size % element_size;
GLint max_sub_row_pixels = max_size / element_size;
for (; height; --height) {
GLint temp_width = width;
GLint temp_xoffset = xoffset;
int8* row_dest = dest;
while (temp_width) {
GLint num_pixels = std::min(width, max_sub_row_pixels);
GLsizeiptr part_size = num_pixels * element_size;
void* buffer = transfer_buffer_.Alloc(part_size);
*result = 0; // mark as failed.
helper_->ReadPixels(
temp_xoffset, yoffset, temp_width, 1, format, type,
transfer_buffer_id_, transfer_buffer_.GetOffset(buffer),
result_shm_id(), result_shm_offset());
WaitForCmd();
if (*result != 0) {
memcpy(row_dest, buffer, part_size);
}
transfer_buffer_.FreePendingToken(buffer, helper_->InsertToken());
// If it was not marked as successful exit.
if (*result == 0) {
return;
}
row_dest += part_size;
temp_xoffset += num_pixels;
temp_width -= num_pixels;
}
++yoffset;
dest += padded_row_size;
}
}
}
void GLES2Implementation::ActiveTexture(GLenum texture) {
GPU_CLIENT_LOG("[" << this << "] glActiveTexture("
<< GLES2Util::GetStringEnum(texture) << ")");
GLuint texture_index = texture - GL_TEXTURE0;
if (texture_index >=
static_cast<GLuint>(gl_state_.max_combined_texture_image_units)) {
SetGLError(GL_INVALID_ENUM, "glActiveTexture: texture_unit out of range.");
return;
}
active_texture_unit_ = texture_index;
helper_->ActiveTexture(texture);
}
// 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.
void GLES2Implementation::BindBufferHelper(
GLenum target, GLuint buffer) {
// TODO(gman): See note #1 above.
switch (target) {
case GL_ARRAY_BUFFER:
bound_array_buffer_id_ = buffer;
break;
case GL_ELEMENT_ARRAY_BUFFER:
bound_element_array_buffer_id_ = buffer;
break;
default:
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.
buffer_id_handler_->MarkAsUsedForBind(buffer);
}
void GLES2Implementation::BindFramebufferHelper(
GLenum target, GLuint framebuffer) {
// TODO(gman): See note #1 above.
switch (target) {
case GL_FRAMEBUFFER:
bound_framebuffer_ = framebuffer;
break;
default:
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.
framebuffer_id_handler_->MarkAsUsedForBind(framebuffer);
}
void GLES2Implementation::BindRenderbufferHelper(
GLenum target, GLuint renderbuffer) {
// TODO(gman): See note #1 above.
switch (target) {
case GL_RENDERBUFFER:
bound_renderbuffer_ = renderbuffer;
break;
default:
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.
renderbuffer_id_handler_->MarkAsUsedForBind(renderbuffer);
}
void GLES2Implementation::BindTextureHelper(GLenum target, GLuint texture) {
// TODO(gman): See note #1 above.
TextureUnit& unit = texture_units_[active_texture_unit_];
switch (target) {
case GL_TEXTURE_2D:
unit.bound_texture_2d = texture;
break;
case GL_TEXTURE_CUBE_MAP:
unit.bound_texture_cube_map = texture;
break;
default:
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.
texture_id_handler_->MarkAsUsedForBind(texture);
}
#if defined(GLES2_SUPPORT_CLIENT_SIDE_ARRAYS)
bool GLES2Implementation::IsBufferReservedId(GLuint id) {
for (size_t ii = 0; ii < arraysize(reserved_ids_); ++ii) {
if (id == reserved_ids_[ii]) {
return true;
}
}
return false;
}
#else
bool GLES2Implementation::IsBufferReservedId(GLuint /* id */) {
return false;
}
#endif
void GLES2Implementation::DeleteBuffersHelper(
GLsizei n, const GLuint* buffers) {
if (!buffer_id_handler_->FreeIds(n, buffers)) {
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;
}
if (buffers[ii] == bound_element_array_buffer_id_) {
bound_element_array_buffer_id_ = 0;
}
}
helper_->DeleteBuffersImmediate(n, buffers);
Flush();
}
void GLES2Implementation::DeleteFramebuffersHelper(
GLsizei n, const GLuint* framebuffers) {
if (!framebuffer_id_handler_->FreeIds(n, framebuffers)) {
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;
}
}
helper_->DeleteFramebuffersImmediate(n, framebuffers);
Flush();
}
void GLES2Implementation::DeleteRenderbuffersHelper(
GLsizei n, const GLuint* renderbuffers) {
if (!renderbuffer_id_handler_->FreeIds(n, renderbuffers)) {
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;
}
}
helper_->DeleteRenderbuffersImmediate(n, renderbuffers);
Flush();
}
void GLES2Implementation::DeleteTexturesHelper(
GLsizei n, const GLuint* textures) {
if (!texture_id_handler_->FreeIds(n, textures)) {
SetGLError(
GL_INVALID_VALUE,
"glDeleteTextures: id not created by this context.");
return;
}
for (GLsizei ii = 0; ii < n; ++ii) {
for (GLint tt = 0; tt < gl_state_.max_combined_texture_image_units; ++tt) {
TextureUnit& unit = texture_units_[active_texture_unit_];
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;
}
}
}
helper_->DeleteTexturesImmediate(n, textures);
Flush();
}
void GLES2Implementation::DisableVertexAttribArray(GLuint index) {
GPU_CLIENT_LOG(
"[" << this << "] glDisableVertexAttribArray(" << index << ")");
#if defined(GLES2_SUPPORT_CLIENT_SIDE_ARRAYS)
client_side_buffer_helper_->SetAttribEnable(index, false);
#endif
helper_->DisableVertexAttribArray(index);
}
void GLES2Implementation::EnableVertexAttribArray(GLuint index) {
GPU_CLIENT_LOG("[" << this << "] glEnableVertexAttribArray(" << index << ")");
#if defined(GLES2_SUPPORT_CLIENT_SIDE_ARRAYS)
client_side_buffer_helper_->SetAttribEnable(index, true);
#endif
helper_->EnableVertexAttribArray(index);
}
void GLES2Implementation::DrawArrays(GLenum mode, GLint first, GLsizei count) {
GPU_CLIENT_LOG("[" << this << "] glDrawArrays("
<< GLES2Util::GetStringDrawMode(mode) << ", "
<< first << ", " << count << ")");
if (count < 0) {
SetGLError(GL_INVALID_VALUE, "glDrawArrays: count < 0");
return;
}
#if defined(GLES2_SUPPORT_CLIENT_SIDE_ARRAYS)
bool have_client_side =
client_side_buffer_helper_->HaveEnabledClientSideBuffers();
if (have_client_side) {
client_side_buffer_helper_->SetupSimualtedClientSideBuffers(
this, helper_, first + count);
}
#endif
helper_->DrawArrays(mode, first, count);
#if defined(GLES2_SUPPORT_CLIENT_SIDE_ARRAYS)
if (have_client_side) {
// Restore the user's current binding.
helper_->BindBuffer(GL_ARRAY_BUFFER, bound_array_buffer_id_);
}
#endif
}
#if defined(GLES2_SUPPORT_CLIENT_SIDE_ARRAYS)
bool GLES2Implementation::GetVertexAttribHelper(
GLuint index, GLenum pname, uint32* param) {
const ClientSideBufferHelper::VertexAttribInfo* info =
client_side_buffer_helper_->GetAttribInfo(index);
if (!info) {
return false;
}
switch (pname) {
case GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING:
*param = info->buffer_id();
break;
case GL_VERTEX_ATTRIB_ARRAY_ENABLED:
*param = info->enabled();
break;
case GL_VERTEX_ATTRIB_ARRAY_SIZE:
*param = info->size();
break;
case GL_VERTEX_ATTRIB_ARRAY_STRIDE:
*param = info->stride();
break;
case GL_VERTEX_ATTRIB_ARRAY_TYPE:
*param = info->type();
break;
case GL_VERTEX_ATTRIB_ARRAY_NORMALIZED:
*param = info->normalized();
break;
case GL_CURRENT_VERTEX_ATTRIB:
return false; // pass through to service side.
default:
SetGLError(GL_INVALID_ENUM, "glGetVertexAttrib: invalid enum");
break;
}
return true;
}
#endif // GLES2_SUPPORT_CLIENT_SIDE_ARRAYS
void GLES2Implementation::GetVertexAttribfv(
GLuint index, GLenum pname, GLfloat* params) {
GPU_CLIENT_LOG("[" << this << "] glGetVertexAttribfv("
<< index << ", "
<< GLES2Util::GetStringVertexAttribute(pname) << ", "
<< static_cast<const void*>(params) << ")");
#if defined(GLES2_SUPPORT_CLIENT_SIDE_ARRAYS)
uint32 value = 0;
if (GetVertexAttribHelper(index, pname, &value)) {
*params = static_cast<float>(value);
return;
}
#endif
TRACE_EVENT0("gpu", "GLES2::GetVertexAttribfv");
typedef GetVertexAttribfv::Result Result;
Result* result = GetResultAs<Result*>();
result->SetNumResults(0);
helper_->GetVertexAttribfv(
index, pname, result_shm_id(), result_shm_offset());
WaitForCmd();
result->CopyResult(params);
GPU_CLIENT_LOG_CODE_BLOCK({
for (int32 i = 0; i < result->GetNumResults(); ++i) {
GPU_CLIENT_LOG(" " << i << ": " << result->GetData()[i]);
}
});
}
void GLES2Implementation::GetVertexAttribiv(
GLuint index, GLenum pname, GLint* params) {
GPU_CLIENT_LOG("[" << this << "] glGetVertexAttribiv("
<< index << ", "
<< GLES2Util::GetStringVertexAttribute(pname) << ", "
<< static_cast<const void*>(params) << ")");
#if defined(GLES2_SUPPORT_CLIENT_SIDE_ARRAYS)
uint32 value = 0;
if (GetVertexAttribHelper(index, pname, &value)) {
*params = value;
return;
}
#endif
TRACE_EVENT0("gpu", "GLES2::GetVertexAttribiv");
typedef GetVertexAttribiv::Result Result;
Result* result = GetResultAs<Result*>();
result->SetNumResults(0);
helper_->GetVertexAttribiv(
index, pname, result_shm_id(), result_shm_offset());
WaitForCmd();
result->CopyResult(params);
GPU_CLIENT_LOG_CODE_BLOCK({
for (int32 i = 0; i < result->GetNumResults(); ++i) {
GPU_CLIENT_LOG(" " << i << ": " << result->GetData()[i]);
}
});
}
GLboolean GLES2Implementation::EnableFeatureCHROMIUM(
const char* feature) {
GPU_CLIENT_LOG("[" << this << "] glEnableFeatureCHROMIUM("
<< feature << ")");
TRACE_EVENT0("gpu", "GLES2::EnableFeatureCHROMIUM");
typedef EnableFeatureCHROMIUM::Result Result;
Result* result = GetResultAs<Result*>();
*result = 0;
SetBucketAsCString(kResultBucketId, feature);
helper_->EnableFeatureCHROMIUM(
kResultBucketId, result_shm_id(), result_shm_offset());
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_LOG("[" << this << "] 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) {
SetGLError(GL_INVALID_ENUM, "MapBufferSubDataCHROMIUM: bad access mode");
return NULL;
}
if (offset < 0 || size < 0) {
SetGLError(GL_INVALID_VALUE, "MapBufferSubDataCHROMIUM: 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, "MapBufferSubDataCHROMIUM: out of memory");
return NULL;
}
std::pair<MappedBufferMap::iterator, bool> result =
mapped_buffers_.insert(std::make_pair(
mem,
MappedBuffer(
access, shm_id, mem, shm_offset, target, offset, size)));
GPU_DCHECK(result.second);
GPU_CLIENT_LOG(" returned " << mem);
return mem;
}
void GLES2Implementation::UnmapBufferSubDataCHROMIUM(const void* mem) {
GPU_CLIENT_LOG(
"[" << this << "] glUnmapBufferSubDataCHROMIUM(" << mem << ")");
MappedBufferMap::iterator it = mapped_buffers_.find(mem);
if (it == mapped_buffers_.end()) {
SetGLError(
GL_INVALID_VALUE, "UnmapBufferSubDataCHROMIUM: buffer not mapped");
return;
}
const MappedBuffer& mb = it->second;
helper_->BufferSubData(
mb.target, mb.offset, mb.size, mb.shm_id, mb.shm_offset);
mapped_memory_->FreePendingToken(mb.shm_memory, helper_->InsertToken());
helper_->CommandBufferHelper::Flush();
mapped_buffers_.erase(it);
}
void* GLES2Implementation::MapTexSubImage2DCHROMIUM(
GLenum target,
GLint level,
GLint xoffset,
GLint yoffset,
GLsizei width,
GLsizei height,
GLenum format,
GLenum type,
GLenum access) {
GPU_CLIENT_LOG("[" << this << "] glMapTexSubImage2DCHROMIUM("
<< target << ", " << level << ", "
<< xoffset << ", " << yoffset << ", "
<< width << ", " << height << ", "
<< GLES2Util::GetStringTextureFormat(format) << ", "
<< GLES2Util::GetStringPixelType(type) << ", "
<< GLES2Util::GetStringEnum(access) << ")");
if (access != GL_WRITE_ONLY) {
SetGLError(GL_INVALID_ENUM, "MapTexSubImage2DCHROMIUM: bad access mode");
return NULL;
}
// NOTE: target is NOT checked because the service will check it
// and we don't know what targets are valid.
if (level < 0 || xoffset < 0 || yoffset < 0 || width < 0 || height < 0) {
SetGLError(GL_INVALID_VALUE, "MapTexSubImage2DCHROMIUM: bad dimensions");
return NULL;
}
uint32 size;
if (!GLES2Util::ComputeImageDataSize(
width, height, format, type, unpack_alignment_, &size)) {
SetGLError(
GL_INVALID_VALUE, "MapTexSubImage2DCHROMIUM: image size too large");
return NULL;
}
int32 shm_id;
unsigned int shm_offset;
void* mem = mapped_memory_->Alloc(size, &shm_id, &shm_offset);
if (!mem) {
SetGLError(GL_OUT_OF_MEMORY, "MapTexSubImage2DCHROMIUM: out of memory");
return NULL;
}
std::pair<MappedTextureMap::iterator, bool> result =
mapped_textures_.insert(std::make_pair(
mem,
MappedTexture(
access, shm_id, mem, shm_offset,
target, level, xoffset, yoffset, width, height, format, type)));
GPU_DCHECK(result.second);
GPU_CLIENT_LOG(" returned " << mem);
return mem;
}
void GLES2Implementation::UnmapTexSubImage2DCHROMIUM(const void* mem) {
GPU_CLIENT_LOG(
"[" << this << "] glUnmapTexSubImage2DCHROMIUM(" << mem << ")");
MappedTextureMap::iterator it = mapped_textures_.find(mem);
if (it == mapped_textures_.end()) {
SetGLError(
GL_INVALID_VALUE, "UnmapTexSubImage2DCHROMIUM: texture not mapped");
return;
}
const MappedTexture& mt = it->second;
helper_->TexSubImage2D(
mt.target, mt.level, mt.xoffset, mt.yoffset, mt.width, mt.height,
mt.format, mt.type, mt.shm_id, mt.shm_offset, GL_FALSE);
mapped_memory_->FreePendingToken(mt.shm_memory, helper_->InsertToken());
helper_->CommandBufferHelper::Flush();
mapped_textures_.erase(it);
}
void GLES2Implementation::ResizeCHROMIUM(GLuint width, GLuint height) {
GPU_CLIENT_LOG("[" << this << "] glResizeCHROMIUM("
<< width << ", " << height << ")");
helper_->ResizeCHROMIUM(width, height);
}
const GLchar* GLES2Implementation::GetRequestableExtensionsCHROMIUM() {
GPU_CLIENT_LOG("[" << this << "] glGetRequestableExtensionsCHROMIUM()");
TRACE_EVENT0("gpu",
"GLES2Implementation::GetRequestableExtensionsCHROMIUM()");
const char* result = NULL;
// Clear the bucket so if the command fails nothing will be in it.
helper_->SetBucketSize(kResultBucketId, 0);
helper_->GetRequestableExtensionsCHROMIUM(kResultBucketId);
std::string str;
if (GetBucketAsString(kResultBucketId, &str)) {
// The set of requestable extensions shrinks as we enable
// them. Because we don't know when the client will stop referring
// to a previous one it queries (see GetString) we need to cache
// the unique results.
std::set<std::string>::const_iterator sit =
requestable_extensions_set_.find(str);
if (sit != requestable_extensions_set_.end()) {
result = sit->c_str();
} else {
std::pair<std::set<std::string>::const_iterator, bool> insert_result =
requestable_extensions_set_.insert(str);
GPU_DCHECK(insert_result.second);
result = insert_result.first->c_str();
}
}
GPU_CLIENT_LOG(" returned " << result);
return reinterpret_cast<const GLchar*>(result);
}
void GLES2Implementation::RequestExtensionCHROMIUM(const char* extension) {
GPU_CLIENT_LOG("[" << this << "] glRequestExtensionCHROMIUM("
<< extension << ")");
SetBucketAsCString(kResultBucketId, extension);
helper_->RequestExtensionCHROMIUM(kResultBucketId);
helper_->SetBucketSize(kResultBucketId, 0);
}
void GLES2Implementation::RateLimitOffscreenContextCHROMIUM() {
GPU_CLIENT_LOG("[" << this << "] glRateLimitOffscreenCHROMIUM()");
// Wait if this would add too many rate limit tokens.
if (rate_limit_tokens_.size() == kMaxSwapBuffers) {
helper_->WaitForToken(rate_limit_tokens_.front());
rate_limit_tokens_.pop();
}
rate_limit_tokens_.push(helper_->InsertToken());
}
void GLES2Implementation::GetMultipleIntegervCHROMIUM(
const GLenum* pnames, GLuint count, GLint* results, GLsizeiptr size) {
GPU_CLIENT_LOG("[" << this << "] glGetMultipleIntegervCHROMIUM("
<< static_cast<const void*>(pnames) << ", "
<< count << ", " << results << ", " << size << ")");
GPU_CLIENT_LOG_CODE_BLOCK({
for (GLuint i = 0; i < count; ++i) {
GPU_CLIENT_LOG(
" " << i << ": " << GLES2Util::GetStringGLState(pnames[i]));
}
});
int num_results = 0;
for (GLuint ii = 0; ii < count; ++ii) {
int num = util_.GLGetNumValuesReturned(pnames[ii]);
if (!num) {
SetGLError(GL_INVALID_ENUM, "glGetMultipleIntegervCHROMIUM: bad pname");
return;
}
num_results += num;
}
if (static_cast<size_t>(size) != num_results * sizeof(GLint)) {
SetGLError(GL_INVALID_VALUE, "glGetMultipleIntegervCHROMIUM: bad size");
return;
}
for (int ii = 0; ii < num_results; ++ii) {
if (results[ii] != 0) {
SetGLError(GL_INVALID_VALUE,
"glGetMultipleIntegervCHROMIUM: results not set to zero.");
return;
}
}
uint32 size_needed =
count * sizeof(pnames[0]) + num_results * sizeof(results[0]);
void* buffer = transfer_buffer_.Alloc(size_needed);
GLenum* pnames_buffer = static_cast<GLenum*>(buffer);
void* results_buffer = pnames_buffer + count;
memcpy(pnames_buffer, pnames, count * sizeof(GLenum));
memset(results_buffer, 0, num_results * sizeof(GLint));
helper_->GetMultipleIntegervCHROMIUM(
transfer_buffer_id_, transfer_buffer_.GetOffset(pnames_buffer),
count,
transfer_buffer_id_, transfer_buffer_.GetOffset(results_buffer),
size);
WaitForCmd();
memcpy(results, results_buffer, size);
// TODO(gman): We should be able to free without a token.
transfer_buffer_.FreePendingToken(buffer, helper_->InsertToken());
GPU_CLIENT_LOG(" returned");
GPU_CLIENT_LOG_CODE_BLOCK({
for (int i = 0; i < num_results; ++i) {
GPU_CLIENT_LOG(" " << i << ": " << (results[i]));
}
});
}
void GLES2Implementation::GetProgramInfoCHROMIUMHelper(
GLuint program, std::vector<int8>* result) {
GPU_DCHECK(result);
// Clear the bucket so if the command fails nothing will be in it.
helper_->SetBucketSize(kResultBucketId, 0);
helper_->GetProgramInfoCHROMIUM(program, kResultBucketId);
GetBucketContents(kResultBucketId, result);
}
void GLES2Implementation::GetProgramInfoCHROMIUM(
GLuint program, GLsizei bufsize, GLsizei* size, void* info) {
if (bufsize < 0) {
SetGLError(GL_INVALID_VALUE, "glProgramInfoCHROMIUM: bufsize less than 0.");
return;
}
if (size == NULL) {
SetGLError(GL_INVALID_VALUE, "glProgramInfoCHROMIUM: size is null.");
return;
}
// Make sure they've set size to 0 else the value will be undefined on
// lost context.
GPU_DCHECK(*size == 0);
std::vector<int8> result;
GetProgramInfoCHROMIUMHelper(program, &result);
if (result.empty()) {
return;
}
*size = result.size();
if (!info) {
return;
}
if (static_cast<size_t>(bufsize) < result.size()) {
SetGLError(GL_INVALID_OPERATION,
"glProgramInfoCHROMIUM: bufsize is too small for result.");
return;
}
memcpy(info, &result[0], result.size());
}
GLuint GLES2Implementation::CreateStreamTextureCHROMIUM(GLuint texture) {
GPU_CLIENT_LOG("[" << this << "] CreateStreamTextureCHROMIUM("
<< texture << ")");
TRACE_EVENT0("gpu", "GLES2::CreateStreamTextureCHROMIUM");
typedef CreateStreamTextureCHROMIUM::Result Result;
Result* result = GetResultAs<Result*>();
*result = GL_ZERO;
helper_->CreateStreamTextureCHROMIUM(texture,
result_shm_id(),
result_shm_offset());
WaitForCmd();
return *result;
}
void GLES2Implementation::DestroyStreamTextureCHROMIUM(GLuint texture) {
GPU_CLIENT_LOG("[" << this << "] DestroyStreamTextureCHROMIUM("
<< texture << ")");
TRACE_EVENT0("gpu", "GLES2::DestroyStreamTextureCHROMIUM");
helper_->DestroyStreamTextureCHROMIUM(texture);
}
} // namespace gles2
} // namespace gpu