diff options
author | zmo <zmo@chromium.org> | 2015-01-29 17:32:57 -0800 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-01-30 01:33:39 +0000 |
commit | b2285c12f91b4ded610cde29763417c3a9797d8b (patch) | |
tree | 81e44fbe8a34e2101c0ae88b32de7956707043cc /gpu | |
parent | c4893d87299560b1e899eba67c64f07cdcb0fe82 (diff) | |
download | chromium_src-b2285c12f91b4ded610cde29763417c3a9797d8b.zip chromium_src-b2285c12f91b4ded610cde29763417c3a9797d8b.tar.gz chromium_src-b2285c12f91b4ded610cde29763417c3a9797d8b.tar.bz2 |
Implement glGetUniformBlocksCHROMIUM: service side data packing
The plan is to implement a Chromium internal command glGetUniformBlocksCHROMIUM,
like what we did with glGetProgramInfoCHROMIUM. So we could obtain all uniform
block related information from a linked program in one command. This is to
optimize the below commands:
* glGetUniformBlockIndex
* glGetActiveUniformBlockiv
* glGetActiveUniformBlockName
By separating glGetUniformBlocksCHROMIUM from glGetProgramInfoCHROMIUM,
command buffer doesn't need to respond differently for glGetProgramInfoCHROMIUM
depending on whether it's ES2 or ES3 command. The drawback is we add an extra
client/service side round trip for some ES3 programs. If this becomes a
critical perf issue, we can easily merge these two commands into one at a future
time.
BUG=453127
TEST=gpu_unittests
R=piman@chromium.org
Review URL: https://codereview.chromium.org/887513002
Cr-Commit-Position: refs/heads/master@{#313854}
Diffstat (limited to 'gpu')
-rw-r--r-- | gpu/command_buffer/common/gles2_cmd_format.h | 47 | ||||
-rw-r--r-- | gpu/command_buffer/service/program_manager.cc | 145 | ||||
-rw-r--r-- | gpu/command_buffer/service/program_manager.h | 4 | ||||
-rw-r--r-- | gpu/command_buffer/service/program_manager_unittest.cc | 164 |
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, ¶m); + if (param == GL_TRUE) { + param = 0; + glGetProgramiv(program, GL_ACTIVE_UNIFORM_BLOCKS, ¶m); + 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, ¶m); + blocks[ii].binding = static_cast<uint32_t>(param); + + param = 0; + glGetActiveUniformBlockiv(program, ii, GL_UNIFORM_BLOCK_DATA_SIZE, ¶m); + 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, ¶m); + 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, ¶m); + 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, ¶m); + blocks[ii].referenced_by_vertex_shader = static_cast<uint32_t>(param); + + param = 0; + glGetActiveUniformBlockiv( + program, ii, GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER, ¶m); + 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(¶ms[0], 0, num_bytes); + glGetActiveUniformBlockiv( + program, ii, GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES, ¶ms[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); } |