diff options
-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); } |