summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--gpu/command_buffer/common/gles2_cmd_format.h47
-rw-r--r--gpu/command_buffer/service/program_manager.cc145
-rw-r--r--gpu/command_buffer/service/program_manager.h4
-rw-r--r--gpu/command_buffer/service/program_manager_unittest.cc164
4 files changed, 350 insertions, 10 deletions
diff --git a/gpu/command_buffer/common/gles2_cmd_format.h b/gpu/command_buffer/common/gles2_cmd_format.h
index 55fa4a1..e4d38f0 100644
--- a/gpu/command_buffer/common/gles2_cmd_format.h
+++ b/gpu/command_buffer/common/gles2_cmd_format.h
@@ -150,6 +150,27 @@ struct ProgramInfoHeader {
// ProgramInput inputs[num_attribs + num_uniforms];
};
+// The data for one UniformBlock from GetProgramInfoCHROMIUM
+struct UniformBlockInfo {
+ uint32_t binding; // UNIFORM_BLOCK_BINDING
+ uint32_t data_size; // UNIFORM_BLOCK_DATA_SIZE
+ uint32_t name_offset; // offset from UniformBlocksHeader to start of name.
+ uint32_t name_length; // UNIFORM_BLOCK_BLOCK_NAME_LENGTH
+ uint32_t active_uniforms; // UNIFORM_BLOCK_ACTIVE_UNIFORMS
+ // offset from UniformBlocksHeader to |active_uniforms| indices.
+ uint32_t active_uniform_offset;
+ // UNIFORM_BLOCK_REFERENDED_BY_VERTEX_SHADER
+ uint32_t referenced_by_vertex_shader;
+ // UNIFORM_BLOCK_REFERENDED_BY_FRAGMENT_SHADER
+ uint32_t referenced_by_fragment_shader;
+};
+
+// The format of the bucket filled out by GetUniformBlocksCHROMIUM
+struct UniformBlocksHeader {
+ uint32_t num_uniform_blocks;
+ // UniformBlockInfo uniform_blocks[num_uniform_blocks];
+};
+
// The format of QuerySync used by EXT_occlusion_query_boolean
struct QuerySync {
void Reset() {
@@ -201,6 +222,32 @@ static_assert(offsetof(ProgramInfoHeader, num_attribs) == 4,
static_assert(offsetof(ProgramInfoHeader, num_uniforms) == 8,
"offset of ProgramInfoHeader.num_uniforms should be 8");
+static_assert(sizeof(UniformBlockInfo) == 32,
+ "size of UniformBlockInfo should be 32");
+static_assert(offsetof(UniformBlockInfo, binding) == 0,
+ "offset of UniformBlockInfo.binding should be 0");
+static_assert(offsetof(UniformBlockInfo, data_size) == 4,
+ "offset of UniformBlockInfo.data_size should be 4");
+static_assert(offsetof(UniformBlockInfo, name_offset) == 8,
+ "offset of UniformBlockInfo.name_offset should be 8");
+static_assert(offsetof(UniformBlockInfo, name_length) == 12,
+ "offset of UniformBlockInfo.name_length should be 12");
+static_assert(offsetof(UniformBlockInfo, active_uniforms) == 16,
+ "offset of UniformBlockInfo.active_uniforms should be 16");
+static_assert(offsetof(UniformBlockInfo, active_uniform_offset) == 20,
+ "offset of UniformBlockInfo.active_uniform_offset should be 20");
+static_assert(offsetof(UniformBlockInfo, referenced_by_vertex_shader) == 24,
+ "offset of UniformBlockInfo.referenced_by_vertex_shader "
+ "should be 24");
+static_assert(offsetof(UniformBlockInfo, referenced_by_fragment_shader) == 28,
+ "offset of UniformBlockInfo.referenced_by_fragment_shader "
+ "should be 28");
+
+static_assert(sizeof(UniformBlocksHeader) == 4,
+ "size of UniformBlocksHeader should be 4");
+static_assert(offsetof(UniformBlocksHeader, num_uniform_blocks) == 0,
+ "offset of UniformBlocksHeader.num_uniform_blocks should be 0");
+
namespace cmds {
#include "../common/gles2_cmd_format_autogen.h"
diff --git a/gpu/command_buffer/service/program_manager.cc b/gpu/command_buffer/service/program_manager.cc
index d776350..d866504 100644
--- a/gpu/command_buffer/service/program_manager.cc
+++ b/gpu/command_buffer/service/program_manager.cc
@@ -107,6 +107,11 @@ bool IsBuiltInInvariant(
return hit->second.isInvariant;
}
+uint32 ComputeOffset(const void* start, const void* position) {
+ return static_cast<const uint8*>(position) -
+ static_cast<const uint8*>(start);
+}
+
} // anonymous namespace.
Program::UniformInfo::UniformInfo()
@@ -1201,11 +1206,6 @@ bool Program::CheckVaryingsPacking(
combined_map.size());
}
-static uint32 ComputeOffset(const void* start, const void* position) {
- return static_cast<const uint8*>(position) -
- static_cast<const uint8*>(start);
-}
-
void Program::GetProgramInfo(
ProgramManager* manager, CommonDecoder::Bucket* bucket) const {
// NOTE: It seems to me the math in here does not need check for overflow
@@ -1290,6 +1290,141 @@ void Program::GetProgramInfo(
DCHECK_EQ(ComputeOffset(header, strings), size);
}
+bool Program::GetUniformBlocks(CommonDecoder::Bucket* bucket) const {
+ // The data is packed into the bucket in the following order
+ // 1) header
+ // 2) N entries of block data (except for name and indices)
+ // 3) name1, indices1, name2, indices2, ..., nameN, indicesN
+ //
+ // We query all the data directly through GL calls, assuming they are
+ // cheap through MANGLE.
+
+ DCHECK(bucket);
+ GLuint program = service_id();
+
+ uint32_t header_size = sizeof(UniformBlocksHeader);
+
+ uint32_t num_uniform_blocks = 0;
+ GLint param = GL_FALSE;
+ // We assume program is a valid program service id.
+ glGetProgramiv(program, GL_LINK_STATUS, &param);
+ if (param == GL_TRUE) {
+ param = 0;
+ glGetProgramiv(program, GL_ACTIVE_UNIFORM_BLOCKS, &param);
+ num_uniform_blocks = static_cast<uint32_t>(param);
+ }
+ if (num_uniform_blocks == 0) {
+ // Although spec allows an implementation to return uniform block info
+ // even if a link fails, for consistency, we disallow that.
+ bucket->SetSize(header_size);
+ UniformBlocksHeader* header =
+ bucket->GetDataAs<UniformBlocksHeader*>(0, header_size);
+ header->num_uniform_blocks = 0;
+ return true;
+ }
+
+ std::vector<UniformBlockInfo> blocks(num_uniform_blocks);
+ base::CheckedNumeric<uint32_t> size = sizeof(UniformBlockInfo);
+ size *= num_uniform_blocks;
+ uint32_t entry_size = size.ValueOrDefault(0);
+ size += header_size;
+ std::vector<std::string> names(num_uniform_blocks);
+ GLint max_name_length = 0;
+ glGetProgramiv(
+ program, GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH, &max_name_length);
+ std::vector<GLchar> buffer(max_name_length);
+ GLsizei length;
+ for (uint32_t ii = 0; ii < num_uniform_blocks; ++ii) {
+ param = 0;
+ glGetActiveUniformBlockiv(program, ii, GL_UNIFORM_BLOCK_BINDING, &param);
+ blocks[ii].binding = static_cast<uint32_t>(param);
+
+ param = 0;
+ glGetActiveUniformBlockiv(program, ii, GL_UNIFORM_BLOCK_DATA_SIZE, &param);
+ blocks[ii].data_size = static_cast<uint32_t>(param);
+
+ blocks[ii].name_offset = size.ValueOrDefault(0);
+ param = 0;
+ glGetActiveUniformBlockiv(
+ program, ii, GL_UNIFORM_BLOCK_NAME_LENGTH, &param);
+ DCHECK_GE(max_name_length, param);
+ memset(&buffer[0], 0, param);
+ length = 0;
+ glGetActiveUniformBlockName(
+ program, ii, static_cast<GLsizei>(param), &length, &buffer[0]);
+ DCHECK_EQ(param, length + 1);
+ names[ii] = std::string(&buffer[0], length);
+ // TODO(zmo): optimize the name mapping lookup.
+ const std::string* original_name = GetOriginalNameFromHashedName(names[ii]);
+ if (original_name)
+ names[ii] = *original_name;
+ blocks[ii].name_length = names[ii].size() + 1;
+ size += blocks[ii].name_length;
+
+ param = 0;
+ glGetActiveUniformBlockiv(
+ program, ii, GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, &param);
+ blocks[ii].active_uniforms = static_cast<uint32_t>(param);
+ blocks[ii].active_uniform_offset = size.ValueOrDefault(0);
+ base::CheckedNumeric<uint32_t> indices_size = blocks[ii].active_uniforms;
+ indices_size *= sizeof(uint32_t);
+ if (!indices_size.IsValid())
+ return false;
+ size += indices_size.ValueOrDefault(0);
+
+ param = 0;
+ glGetActiveUniformBlockiv(
+ program, ii, GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER, &param);
+ blocks[ii].referenced_by_vertex_shader = static_cast<uint32_t>(param);
+
+ param = 0;
+ glGetActiveUniformBlockiv(
+ program, ii, GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER, &param);
+ blocks[ii].referenced_by_fragment_shader = static_cast<uint32_t>(param);
+ }
+ if (!size.IsValid())
+ return false;
+ uint32_t total_size = size.ValueOrDefault(0);
+ DCHECK_LE(header_size + entry_size, total_size);
+ uint32_t data_size = total_size - header_size - entry_size;
+
+ bucket->SetSize(total_size);
+ UniformBlocksHeader* header =
+ bucket->GetDataAs<UniformBlocksHeader*>(0, total_size);
+ UniformBlockInfo* entries = bucket->GetDataAs<UniformBlockInfo*>(
+ header_size, entry_size);
+ char* data = bucket->GetDataAs<char*>(header_size + entry_size, data_size);
+ DCHECK(header);
+ DCHECK(entries);
+ DCHECK(data);
+
+ // Copy over data for the header and entries.
+ header->num_uniform_blocks = num_uniform_blocks;
+ memcpy(entries, &blocks[0], entry_size);
+
+ std::vector<GLint> params;
+ for (uint32_t ii = 0; ii < num_uniform_blocks; ++ii) {
+ // Get active uniform name.
+ memcpy(data, names[ii].c_str(), blocks[ii].name_length);
+ data += blocks[ii].name_length;
+
+ // Get active uniform indices.
+ if (params.size() < blocks[ii].active_uniforms)
+ params.resize(blocks[ii].active_uniforms);
+ uint32_t num_bytes = blocks[ii].active_uniforms * sizeof(GLint);
+ memset(&params[0], 0, num_bytes);
+ glGetActiveUniformBlockiv(
+ program, ii, GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES, &params[0]);
+ uint32_t* indices = reinterpret_cast<uint32_t*>(data);
+ for (uint32_t uu = 0; uu < blocks[ii].active_uniforms; ++uu) {
+ indices[uu] = static_cast<uint32_t>(params[uu]);
+ }
+ data += num_bytes;
+ }
+ DCHECK_EQ(ComputeOffset(header, data), total_size);
+ return true;
+}
+
Program::~Program() {
if (manager_) {
if (manager_->have_context_) {
diff --git a/gpu/command_buffer/service/program_manager.h b/gpu/command_buffer/service/program_manager.h
index 2f5deae..6f7e3a9 100644
--- a/gpu/command_buffer/service/program_manager.h
+++ b/gpu/command_buffer/service/program_manager.h
@@ -149,6 +149,10 @@ class GPU_EXPORT Program : public base::RefCounted<Program> {
void GetProgramInfo(
ProgramManager* manager, CommonDecoder::Bucket* bucket) const;
+ // Gets all the UniformBlock info.
+ // Return false on overflow.
+ bool GetUniformBlocks(CommonDecoder::Bucket* bucket) const;
+
// Sets the sampler values for a uniform.
// This is safe to call for any location. If the location is not
// a sampler uniform nothing will happen.
diff --git a/gpu/command_buffer/service/program_manager_unittest.cc b/gpu/command_buffer/service/program_manager_unittest.cc
index 4acd220..7d9ea9d 100644
--- a/gpu/command_buffer/service/program_manager_unittest.cc
+++ b/gpu/command_buffer/service/program_manager_unittest.cc
@@ -28,7 +28,7 @@ using ::testing::Pointee;
using ::testing::Return;
using ::testing::ReturnRef;
using ::testing::SetArrayArgument;
-using ::testing::SetArgumentPointee;
+using ::testing::SetArgPointee;
using ::testing::StrEq;
namespace gpu {
@@ -38,6 +38,12 @@ namespace {
const uint32 kMaxVaryingVectors = 8;
void ShaderCacheCb(const std::string& key, const std::string& shader) {}
+
+uint32 ComputeOffset(const void* start, const void* position) {
+ return static_cast<const uint8*>(position) -
+ static_cast<const uint8*>(start);
+}
+
} // namespace anonymous
class ProgramManagerTest : public GpuServiceTest {
@@ -214,7 +220,8 @@ class ProgramManagerWithShaderTest : public GpuServiceTest {
} VarInfo;
void SetUp() override {
- GpuServiceTest::SetUp();
+ // Need to be at leat 3.1 for UniformBlock related GL APIs.
+ GpuServiceTest::SetUpWithGLVersion("3.1", NULL);
SetupDefaultShaderExpectations();
@@ -1073,6 +1080,153 @@ TEST_F(ProgramManagerWithShaderTest, ProgramInfoGetProgramInfo) {
static_cast<uint32>(input - inputs));
}
+TEST_F(ProgramManagerWithShaderTest, ProgramInfoGetUniformBlocksNone) {
+ CommonDecoder::Bucket bucket;
+ const Program* program = manager_.GetProgram(kClientProgramId);
+ ASSERT_TRUE(program != NULL);
+ // The program's previous link failed.
+ EXPECT_CALL(*(gl_.get()),
+ GetProgramiv(kServiceProgramId, GL_LINK_STATUS, _))
+ .WillOnce(SetArgPointee<2>(GL_FALSE))
+ .RetiresOnSaturation();
+ EXPECT_TRUE(program->GetUniformBlocks(&bucket));
+ EXPECT_EQ(sizeof(UniformBlocksHeader), bucket.size());
+ UniformBlocksHeader* header =
+ bucket.GetDataAs<UniformBlocksHeader*>(0, sizeof(UniformBlocksHeader));
+ EXPECT_TRUE(header != NULL);
+ EXPECT_EQ(0u, header->num_uniform_blocks);
+ // Zero uniform blocks.
+ EXPECT_CALL(*(gl_.get()),
+ GetProgramiv(kServiceProgramId, GL_LINK_STATUS, _))
+ .WillOnce(SetArgPointee<2>(GL_TRUE))
+ .RetiresOnSaturation();
+ EXPECT_CALL(*(gl_.get()),
+ GetProgramiv(kServiceProgramId, GL_ACTIVE_UNIFORM_BLOCKS, _))
+ .WillOnce(SetArgPointee<2>(0))
+ .RetiresOnSaturation();
+ EXPECT_TRUE(program->GetUniformBlocks(&bucket));
+ EXPECT_EQ(sizeof(UniformBlocksHeader), bucket.size());
+ header =
+ bucket.GetDataAs<UniformBlocksHeader*>(0, sizeof(UniformBlocksHeader));
+ EXPECT_TRUE(header != NULL);
+ EXPECT_EQ(0u, header->num_uniform_blocks);
+}
+
+TEST_F(ProgramManagerWithShaderTest, ProgramInfoGetUniformBlocksValid) {
+ CommonDecoder::Bucket bucket;
+ const Program* program = manager_.GetProgram(kClientProgramId);
+ ASSERT_TRUE(program != NULL);
+ struct Data {
+ UniformBlocksHeader header;
+ UniformBlockInfo entry[2];
+ char name0[4];
+ uint32_t indices0[2];
+ char name1[8];
+ uint32_t indices1[1];
+ };
+ Data data;
+ // The names needs to be of size 4*k-1 to avoid padding in the struct Data.
+ // This is a testing only problem.
+ const char* kName[] = { "cow", "chicken" };
+ const uint32_t kIndices0[] = { 1, 2 };
+ const uint32_t kIndices1[] = { 3 };
+ const uint32_t* kIndices[] = { kIndices0, kIndices1 };
+ data.header.num_uniform_blocks = 2;
+ data.entry[0].binding = 0;
+ data.entry[0].data_size = 8;
+ data.entry[0].name_offset = ComputeOffset(&data, data.name0);
+ data.entry[0].name_length = arraysize(data.name0);
+ data.entry[0].active_uniforms = arraysize(data.indices0);
+ data.entry[0].active_uniform_offset = ComputeOffset(&data, data.indices0);
+ data.entry[0].referenced_by_vertex_shader = static_cast<uint32_t>(true);
+ data.entry[0].referenced_by_fragment_shader = static_cast<uint32_t>(false);
+ data.entry[1].binding = 1;
+ data.entry[1].data_size = 4;
+ data.entry[1].name_offset = ComputeOffset(&data, data.name1);
+ data.entry[1].name_length = arraysize(data.name1);
+ data.entry[1].active_uniforms = arraysize(data.indices1);
+ data.entry[1].active_uniform_offset = ComputeOffset(&data, data.indices1);
+ data.entry[1].referenced_by_vertex_shader = static_cast<uint32_t>(false);
+ data.entry[1].referenced_by_fragment_shader = static_cast<uint32_t>(true);
+ memcpy(data.name0, kName[0], arraysize(data.name0));
+ data.indices0[0] = kIndices[0][0];
+ data.indices0[1] = kIndices[0][1];
+ memcpy(data.name1, kName[1], arraysize(data.name1));
+ data.indices1[0] = kIndices[1][0];
+
+ EXPECT_CALL(*(gl_.get()),
+ GetProgramiv(kServiceProgramId, GL_LINK_STATUS, _))
+ .WillOnce(SetArgPointee<2>(GL_TRUE))
+ .RetiresOnSaturation();
+ EXPECT_CALL(*(gl_.get()),
+ GetProgramiv(kServiceProgramId, GL_ACTIVE_UNIFORM_BLOCKS, _))
+ .WillOnce(SetArgPointee<2>(data.header.num_uniform_blocks))
+ .RetiresOnSaturation();
+ EXPECT_CALL(*(gl_.get()),
+ GetProgramiv(kServiceProgramId,
+ GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH, _))
+ .WillOnce(SetArgPointee<2>(
+ 1 + std::max(strlen(kName[0]), strlen(kName[1]))))
+ .RetiresOnSaturation();
+ for (uint32_t ii = 0; ii < data.header.num_uniform_blocks; ++ii) {
+ EXPECT_CALL(*(gl_.get()),
+ GetActiveUniformBlockiv(
+ kServiceProgramId, ii, GL_UNIFORM_BLOCK_BINDING, _))
+ .WillOnce(SetArgPointee<3>(data.entry[ii].binding))
+ .RetiresOnSaturation();
+ EXPECT_CALL(*(gl_.get()),
+ GetActiveUniformBlockiv(
+ kServiceProgramId, ii, GL_UNIFORM_BLOCK_DATA_SIZE, _))
+ .WillOnce(SetArgPointee<3>(data.entry[ii].data_size))
+ .RetiresOnSaturation();
+ EXPECT_CALL(*(gl_.get()),
+ GetActiveUniformBlockiv(
+ kServiceProgramId, ii, GL_UNIFORM_BLOCK_NAME_LENGTH, _))
+ .WillOnce(SetArgPointee<3>(data.entry[ii].name_length))
+ .RetiresOnSaturation();
+ EXPECT_CALL(*(gl_.get()),
+ GetActiveUniformBlockName(
+ kServiceProgramId, ii, data.entry[ii].name_length, _, _))
+ .WillOnce(DoAll(
+ SetArgPointee<3>(strlen(kName[ii])),
+ SetArrayArgument<4>(
+ kName[ii], kName[ii] + data.entry[ii].name_length)))
+ .RetiresOnSaturation();
+ EXPECT_CALL(*(gl_.get()),
+ GetActiveUniformBlockiv(
+ kServiceProgramId, ii, GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, _))
+ .WillOnce(SetArgPointee<3>(data.entry[ii].active_uniforms))
+ .RetiresOnSaturation();
+ EXPECT_CALL(*(gl_.get()),
+ GetActiveUniformBlockiv(
+ kServiceProgramId, ii,
+ GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER, _))
+ .WillOnce(SetArgPointee<3>(data.entry[ii].referenced_by_vertex_shader))
+ .RetiresOnSaturation();
+ EXPECT_CALL(*(gl_.get()),
+ GetActiveUniformBlockiv(
+ kServiceProgramId, ii,
+ GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER, _))
+ .WillOnce(SetArgPointee<3>(
+ data.entry[ii].referenced_by_fragment_shader))
+ .RetiresOnSaturation();
+ }
+ for (uint32_t ii = 0; ii < data.header.num_uniform_blocks; ++ii) {
+ EXPECT_CALL(*(gl_.get()),
+ GetActiveUniformBlockiv(
+ kServiceProgramId, ii,
+ GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES, _))
+ .WillOnce(SetArrayArgument<3>(
+ kIndices[ii], kIndices[ii] + data.entry[ii].active_uniforms))
+ .RetiresOnSaturation();
+ }
+ program->GetUniformBlocks(&bucket);
+ EXPECT_EQ(sizeof(Data), bucket.size());
+ Data* bucket_data = bucket.GetDataAs<Data*>(0, sizeof(Data));
+ EXPECT_TRUE(bucket_data != NULL);
+ EXPECT_EQ(0, memcmp(&data, bucket_data, sizeof(Data)));
+}
+
// Some drivers optimize out unused uniform array elements, so their
// location would be -1.
TEST_F(ProgramManagerWithShaderTest, UnusedUniformArrayElements) {
@@ -1717,7 +1871,7 @@ class ProgramManagerWithCacheTest : public GpuServiceTest {
ShaderSource(shader_id, 1, Pointee(src), NULL)).Times(1);
EXPECT_CALL(*gl_.get(), CompileShader(shader_id)).Times(1);
EXPECT_CALL(*gl_.get(), GetShaderiv(shader_id, GL_COMPILE_STATUS, _))
- .WillOnce(SetArgumentPointee<2>(GL_TRUE));
+ .WillOnce(SetArgPointee<2>(GL_TRUE));
}
void SetExpectationsForNoCompile(const Shader* shader) {
@@ -1737,9 +1891,9 @@ class ProgramManagerWithCacheTest : public GpuServiceTest {
ShaderSource(shader_id, 1, Pointee(src), NULL)).Times(1);
EXPECT_CALL(*gl_.get(), CompileShader(shader_id)).Times(1);
EXPECT_CALL(*gl_.get(), GetShaderiv(shader_id, GL_COMPILE_STATUS, _))
- .WillOnce(SetArgumentPointee<2>(GL_FALSE));
+ .WillOnce(SetArgPointee<2>(GL_FALSE));
EXPECT_CALL(*gl_.get(), GetShaderiv(shader_id, GL_INFO_LOG_LENGTH, _))
- .WillOnce(SetArgumentPointee<2>(0));
+ .WillOnce(SetArgPointee<2>(0));
EXPECT_CALL(*gl_.get(), GetShaderInfoLog(shader_id, 0, _, _))
.Times(1);
}