blob: fb0c1f3f9406f487e66cdd828ab7009d305bd4f3 [file] [log] [blame]
// 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.
#include <map>
#include "base/compiler_specific.h"
#include "gpu/command_buffer/client/atomicops.h"
#include "gpu/command_buffer/client/gles2_implementation.h"
#include "gpu/command_buffer/client/program_info_manager.h"
#include "gpu/command_buffer/common/gles2_cmd_utils.h"
namespace gpu {
namespace gles2 {
class NonCachedProgramInfoManager : public ProgramInfoManager {
public:
NonCachedProgramInfoManager();
virtual ~NonCachedProgramInfoManager();
virtual void CreateInfo(GLuint program) OVERRIDE;
virtual void DeleteInfo(GLuint program) OVERRIDE;
virtual bool GetProgramiv(GLES2Implementation* gl,
GLuint program,
GLenum pname,
GLint* params) OVERRIDE;
virtual GLint GetAttribLocation(GLES2Implementation* gl,
GLuint program,
const char* name) OVERRIDE;
virtual GLint GetUniformLocation(GLES2Implementation* gl,
GLuint program,
const char* name) OVERRIDE;
virtual bool GetActiveAttrib(GLES2Implementation* gl,
GLuint program,
GLuint index,
GLsizei bufsize,
GLsizei* length,
GLint* size,
GLenum* type,
char* name) OVERRIDE;
virtual bool GetActiveUniform(GLES2Implementation* gl,
GLuint program,
GLuint index,
GLsizei bufsize,
GLsizei* length,
GLint* size,
GLenum* type,
char* name) OVERRIDE;
};
NonCachedProgramInfoManager::NonCachedProgramInfoManager() {
}
NonCachedProgramInfoManager::~NonCachedProgramInfoManager() {
}
void NonCachedProgramInfoManager::CreateInfo(GLuint /* program */) {
}
void NonCachedProgramInfoManager::DeleteInfo(GLuint /* program */) {
}
bool NonCachedProgramInfoManager::GetProgramiv(
GLES2Implementation* /* gl */,
GLuint /* program */,
GLenum /* pname */,
GLint* /* params */) {
return false;
}
GLint NonCachedProgramInfoManager::GetAttribLocation(
GLES2Implementation* gl, GLuint program, const char* name) {
return gl->GetAttribLocationHelper(program, name);
}
GLint NonCachedProgramInfoManager::GetUniformLocation(
GLES2Implementation* gl, GLuint program, const char* name) {
return gl->GetUniformLocationHelper(program, name);
}
bool NonCachedProgramInfoManager::GetActiveAttrib(
GLES2Implementation* gl,
GLuint program, GLuint index, GLsizei bufsize, GLsizei* length,
GLint* size, GLenum* type, char* name) {
return gl->GetActiveAttribHelper(
program, index, bufsize, length, size, type, name);
}
bool NonCachedProgramInfoManager::GetActiveUniform(
GLES2Implementation* gl,
GLuint program, GLuint index, GLsizei bufsize, GLsizei* length,
GLint* size, GLenum* type, char* name) {
return gl->GetActiveUniformHelper(
program, index, bufsize, length, size, type, name);
}
class CachedProgramInfoManager : public ProgramInfoManager {
public:
CachedProgramInfoManager();
virtual ~CachedProgramInfoManager();
virtual void CreateInfo(GLuint program) OVERRIDE;
virtual void DeleteInfo(GLuint program) OVERRIDE;
virtual bool GetProgramiv(GLES2Implementation* gl,
GLuint program,
GLenum pname,
GLint* params) OVERRIDE;
virtual GLint GetAttribLocation(GLES2Implementation* gl,
GLuint program,
const char* name) OVERRIDE;
virtual GLint GetUniformLocation(GLES2Implementation* gl,
GLuint program,
const char* name) OVERRIDE;
virtual bool GetActiveAttrib(GLES2Implementation* gl,
GLuint program,
GLuint index,
GLsizei bufsize,
GLsizei* length,
GLint* size,
GLenum* type,
char* name) OVERRIDE;
virtual bool GetActiveUniform(GLES2Implementation* gl,
GLuint program,
GLuint index,
GLsizei bufsize,
GLsizei* length,
GLint* size,
GLenum* type,
char* name) OVERRIDE;
private:
class Program {
public:
struct UniformInfo {
UniformInfo(GLsizei _size, GLenum _type, const std::string& _name);
GLsizei size;
GLenum type;
bool is_array;
std::string name;
std::vector<GLint> element_locations;
};
struct VertexAttrib {
VertexAttrib(GLsizei _size, GLenum _type, const std::string& _name,
GLint _location)
: size(_size),
type(_type),
location(_location),
name(_name) {
}
GLsizei size;
GLenum type;
GLint location;
std::string name;
};
typedef std::vector<UniformInfo> UniformInfoVector;
typedef std::vector<VertexAttrib> AttribInfoVector;
Program();
const AttribInfoVector& GetAttribInfos() const {
return attrib_infos_;
}
const VertexAttrib* GetAttribInfo(GLint index) const {
return (static_cast<size_t>(index) < attrib_infos_.size()) ?
&attrib_infos_[index] : NULL;
}
GLint GetAttribLocation(const std::string& name) const;
const UniformInfo* GetUniformInfo(GLint index) const {
return (static_cast<size_t>(index) < uniform_infos_.size()) ?
&uniform_infos_[index] : NULL;
}
// Gets the location of a uniform by name.
GLint GetUniformLocation(const std::string& name) const;
bool GetProgramiv(GLenum pname, GLint* params);
// Updates the program info after a successful link.
void Update(GLES2Implementation* gl, GLuint program);
private:
bool cached_;
GLsizei max_attrib_name_length_;
// Attrib by index.
AttribInfoVector attrib_infos_;
GLsizei max_uniform_name_length_;
// Uniform info by index.
UniformInfoVector uniform_infos_;
// This is true if glLinkProgram was successful last time it was called.
bool link_status_;
};
Program* GetProgramInfo(GLES2Implementation* gl, GLuint program);
// TODO(gman): Switch to a faster container.
typedef std::map<GLuint, Program> ProgramInfoMap;
ProgramInfoMap program_infos_;
mutable Lock lock_;
};
CachedProgramInfoManager::Program::UniformInfo::UniformInfo(
GLsizei _size, GLenum _type, const std::string& _name)
: size(_size),
type(_type),
name(_name) {
is_array = (!name.empty() && name[name.size() - 1] == ']');
GPU_DCHECK(!(size > 1 && !is_array));
}
CachedProgramInfoManager::Program::Program()
: cached_(false),
max_attrib_name_length_(0),
max_uniform_name_length_(0),
link_status_(false) {
}
// TODO(gman): Add a faster lookup.
GLint CachedProgramInfoManager::Program::GetAttribLocation(
const std::string& name) const {
for (GLuint ii = 0; ii < attrib_infos_.size(); ++ii) {
const VertexAttrib& info = attrib_infos_[ii];
if (info.name == name) {
return info.location;
}
}
return -1;
}
GLint CachedProgramInfoManager::Program::GetUniformLocation(
const std::string& name) const {
bool getting_array_location = false;
size_t open_pos = std::string::npos;
int index = 0;
if (!GLES2Util::ParseUniformName(
name, &open_pos, &index, &getting_array_location)) {
return -1;
}
for (GLuint ii = 0; ii < uniform_infos_.size(); ++ii) {
const UniformInfo& info = uniform_infos_[ii];
if (info.name == name ||
(info.is_array &&
info.name.compare(0, info.name.size() - 3, name) == 0)) {
return info.element_locations[0];
} else if (getting_array_location && info.is_array) {
// Look for an array specification.
size_t open_pos_2 = info.name.find_last_of('[');
if (open_pos_2 == open_pos &&
name.compare(0, open_pos, info.name, 0, open_pos) == 0) {
if (index >= 0 && index < info.size) {
return info.element_locations[index];
}
}
}
}
return -1;
}
bool CachedProgramInfoManager::Program::GetProgramiv(
GLenum pname, GLint* params) {
switch (pname) {
case GL_LINK_STATUS:
*params = link_status_;
return true;
case GL_ACTIVE_ATTRIBUTES:
*params = attrib_infos_.size();
return true;
case GL_ACTIVE_ATTRIBUTE_MAX_LENGTH:
*params = max_attrib_name_length_;
return true;
case GL_ACTIVE_UNIFORMS:
*params = uniform_infos_.size();
return true;
case GL_ACTIVE_UNIFORM_MAX_LENGTH:
*params = max_uniform_name_length_;
return true;
default:
break;
}
return false;
}
template<typename T> static T LocalGetAs(
const std::vector<int8>& data, uint32 offset, size_t size) {
const int8* p = &data[0] + offset;
if (offset + size > data.size()) {
GPU_NOTREACHED();
return NULL;
}
return static_cast<T>(static_cast<const void*>(p));
}
void CachedProgramInfoManager::Program::Update(
GLES2Implementation* gl, GLuint program) {
if (cached_) {
return;
}
std::vector<int8> result;
gl->GetProgramInfoCHROMIUMHelper(program, &result);
if (result.empty()) {
// This should only happen on a lost context.
return;
}
GPU_DCHECK_GE(result.size(), sizeof(ProgramInfoHeader));
const ProgramInfoHeader* header = LocalGetAs<const ProgramInfoHeader*>(
result, 0, sizeof(header));
link_status_ = header->link_status != 0;
if (!link_status_) {
return;
}
attrib_infos_.clear();
uniform_infos_.clear();
max_attrib_name_length_ = 0;
max_uniform_name_length_ = 0;
const ProgramInput* inputs = LocalGetAs<const ProgramInput*>(
result, sizeof(*header),
sizeof(ProgramInput) * (header->num_attribs + header->num_uniforms));
const ProgramInput* input = inputs;
for (uint32 ii = 0; ii < header->num_attribs; ++ii) {
const int32* location = LocalGetAs<const int32*>(
result, input->location_offset, sizeof(int32));
const char* name_buf = LocalGetAs<const char*>(
result, input->name_offset, input->name_length);
std::string name(name_buf, input->name_length);
attrib_infos_.push_back(
VertexAttrib(input->size, input->type, name, *location));
max_attrib_name_length_ = std::max(
static_cast<GLsizei>(name.size() + 1), max_attrib_name_length_);
++input;
}
for (uint32 ii = 0; ii < header->num_uniforms; ++ii) {
const int32* locations = LocalGetAs<const int32*>(
result, input->location_offset, sizeof(int32) * input->size);
const char* name_buf = LocalGetAs<const char*>(
result, input->name_offset, input->name_length);
std::string name(name_buf, input->name_length);
UniformInfo info(input->size, input->type, name);
max_uniform_name_length_ = std::max(
static_cast<GLsizei>(name.size() + 1), max_uniform_name_length_);
for (int32 jj = 0; jj < input->size; ++jj) {
info.element_locations.push_back(locations[jj]);
}
uniform_infos_.push_back(info);
++input;
}
GPU_DCHECK_EQ(header->num_attribs + header->num_uniforms,
static_cast<uint32>(input - inputs));
cached_ = true;
}
CachedProgramInfoManager::CachedProgramInfoManager() {
}
CachedProgramInfoManager::~CachedProgramInfoManager() {
}
CachedProgramInfoManager::Program*
CachedProgramInfoManager::GetProgramInfo(
GLES2Implementation* gl, GLuint program) {
ProgramInfoMap::iterator it = program_infos_.find(program);
if (it == program_infos_.end()) {
return NULL;
}
Program* info = &it->second;
info->Update(gl, program);
return info;
}
void CachedProgramInfoManager::CreateInfo(GLuint program) {
AutoLock auto_lock(lock_);
DeleteInfo(program);
std::pair<ProgramInfoMap::iterator, bool> result =
program_infos_.insert(std::make_pair(program, Program()));
GPU_DCHECK(result.second);
}
void CachedProgramInfoManager::DeleteInfo(GLuint program) {
program_infos_.erase(program);
}
bool CachedProgramInfoManager::GetProgramiv(
GLES2Implementation* gl, GLuint program, GLenum pname, GLint* params) {
AutoLock auto_lock(lock_);
Program* info = GetProgramInfo(gl, program);
if (!info) {
return false;
}
return info->GetProgramiv(pname, params);
}
GLint CachedProgramInfoManager::GetAttribLocation(
GLES2Implementation* gl, GLuint program, const char* name) {
AutoLock auto_lock(lock_);
Program* info = GetProgramInfo(gl, program);
if (info) {
return info->GetAttribLocation(name);
}
return gl->GetAttribLocationHelper(program, name);
}
GLint CachedProgramInfoManager::GetUniformLocation(
GLES2Implementation* gl, GLuint program, const char* name) {
AutoLock auto_lock(lock_);
Program* info = GetProgramInfo(gl, program);
if (info) {
return info->GetUniformLocation(name);
}
return gl->GetUniformLocationHelper(program, name);
}
bool CachedProgramInfoManager::GetActiveAttrib(
GLES2Implementation* gl,
GLuint program, GLuint index, GLsizei bufsize, GLsizei* length,
GLint* size, GLenum* type, char* name) {
AutoLock auto_lock(lock_);
Program* info = GetProgramInfo(gl, program);
if (info) {
const Program::VertexAttrib* attrib_info =
info->GetAttribInfo(index);
if (attrib_info) {
if (size) {
*size = attrib_info->size;
}
if (type) {
*type = attrib_info->type;
}
if (length || name) {
GLsizei max_size = std::min(static_cast<size_t>(bufsize) - 1,
std::max(static_cast<size_t>(0),
attrib_info->name.size()));
if (length) {
*length = max_size;
}
if (name && bufsize > 0) {
memcpy(name, attrib_info->name.c_str(), max_size);
name[max_size] = '\0';
}
}
return true;
}
}
return gl->GetActiveAttribHelper(
program, index, bufsize, length, size, type, name);
}
bool CachedProgramInfoManager::GetActiveUniform(
GLES2Implementation* gl,
GLuint program, GLuint index, GLsizei bufsize, GLsizei* length,
GLint* size, GLenum* type, char* name) {
AutoLock auto_lock(lock_);
Program* info = GetProgramInfo(gl, program);
if (info) {
const Program::UniformInfo* uniform_info = info->GetUniformInfo(index);
if (uniform_info) {
if (size) {
*size = uniform_info->size;
}
if (type) {
*type = uniform_info->type;
}
if (length || name) {
GLsizei max_size = std::min(static_cast<size_t>(bufsize) - 1,
std::max(static_cast<size_t>(0),
uniform_info->name.size()));
if (length) {
*length = max_size;
}
if (name && bufsize > 0) {
memcpy(name, uniform_info->name.c_str(), max_size);
name[max_size] = '\0';
}
}
return true;
}
}
return gl->GetActiveUniformHelper(
program, index, bufsize, length, size, type, name);
}
ProgramInfoManager::ProgramInfoManager() {
}
ProgramInfoManager::~ProgramInfoManager() {
}
ProgramInfoManager* ProgramInfoManager::Create(
bool shared_resources_across_processes) {
if (shared_resources_across_processes) {
return new NonCachedProgramInfoManager();
} else {
return new CachedProgramInfoManager();
}
}
} // namespace gles2
} // namespace gpu