| // 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 "gpu/command_buffer/service/program_manager.h" |
| |
| #include <stddef.h> |
| #include <stdint.h> |
| |
| #include <algorithm> |
| #include <memory> |
| |
| #include "base/command_line.h" |
| #include "base/stl_util.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/strings/string_util.h" |
| #include "gpu/command_buffer/common/gles2_cmd_format.h" |
| #include "gpu/command_buffer/common/gles2_cmd_utils.h" |
| #include "gpu/command_buffer/service/common_decoder.h" |
| #include "gpu/command_buffer/service/decoder_client.h" |
| #include "gpu/command_buffer/service/feature_info.h" |
| #include "gpu/command_buffer/service/gpu_service_test.h" |
| #include "gpu/command_buffer/service/gpu_switches.h" |
| #include "gpu/command_buffer/service/mocks.h" |
| #include "gpu/command_buffer/service/shader_manager.h" |
| #include "gpu/command_buffer/service/test_helper.h" |
| #include "gpu/config/gpu_preferences.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "ui/gl/gl_mock.h" |
| #include "ui/gl/gl_version_info.h" |
| |
| using ::testing::_; |
| using ::testing::DoAll; |
| using ::testing::Exactly; |
| using ::testing::InSequence; |
| using ::testing::MatcherCast; |
| using ::testing::Pointee; |
| using ::testing::Return; |
| using ::testing::ReturnRef; |
| using ::testing::SetArrayArgument; |
| using ::testing::SetArgPointee; |
| using ::testing::StrEq; |
| |
| namespace gpu { |
| namespace gles2 { |
| |
| namespace { |
| const uint32_t kMaxVaryingVectors = 8; |
| const uint32_t kMaxDrawBuffers = 8; |
| const uint32_t kMaxDualSourceDrawBuffers = 8; |
| const uint32_t kMaxVertexAttribs = 8; |
| |
| uint32_t ComputeOffset(const void* start, const void* position) { |
| return static_cast<const uint8_t*>(position) - |
| static_cast<const uint8_t*>(start); |
| } |
| |
| } // namespace anonymous |
| |
| class ProgramManagerTestBase : public GpuServiceTest, public DecoderClient { |
| protected: |
| virtual void SetupProgramManager() { |
| manager_.reset(new ProgramManager( |
| nullptr, kMaxVaryingVectors, kMaxDrawBuffers, kMaxDualSourceDrawBuffers, |
| kMaxVertexAttribs, gpu_preferences_, feature_info_.get(), nullptr)); |
| } |
| void SetUpBase(const char* gl_version, |
| const char* gl_extensions, |
| FeatureInfo* feature_info = nullptr) { |
| GpuServiceTest::SetUpWithGLVersion(gl_version, gl_extensions); |
| if (!feature_info) |
| feature_info = new FeatureInfo(); |
| TestHelper::SetupFeatureInfoInitExpectationsWithGLVersion( |
| gl_.get(), gl_extensions, "", gl_version, feature_info->context_type()); |
| feature_info->InitializeForTesting(); |
| feature_info_ = feature_info; |
| SetupProgramManager(); |
| } |
| void SetUp() override { |
| // Parameters same as GpuServiceTest::SetUp |
| SetUpBase("2.0", "GL_EXT_framebuffer_object"); |
| } |
| void TearDown() override { |
| manager_->Destroy(false); |
| manager_.reset(); |
| feature_info_ = nullptr; |
| GpuServiceTest::TearDown(); |
| } |
| |
| void OnConsoleMessage(int32_t id, const std::string& message) override {} |
| void CacheShader(const std::string& key, const std::string& shader) override { |
| } |
| void OnFenceSyncRelease(uint64_t release) override {} |
| void OnDescheduleUntilFinished() override {} |
| void OnRescheduleAfterFinished() override {} |
| void OnSwapBuffers(uint64_t swap_id, uint32_t flags) override {} |
| void ScheduleGrContextCleanup() override {} |
| |
| std::unique_ptr<ProgramManager> manager_; |
| GpuPreferences gpu_preferences_; |
| scoped_refptr<FeatureInfo> feature_info_; |
| }; |
| |
| class ProgramManagerTest : public ProgramManagerTestBase {}; |
| |
| TEST_F(ProgramManagerTest, Basic) { |
| const GLuint kClient1Id = 1; |
| const GLuint kService1Id = 11; |
| const GLuint kClient2Id = 2; |
| // Check we can create program. |
| manager_->CreateProgram(kClient1Id, kService1Id); |
| // Check program got created. |
| Program* program1 = manager_->GetProgram(kClient1Id); |
| ASSERT_TRUE(program1 != nullptr); |
| GLuint client_id = 0; |
| EXPECT_TRUE(manager_->GetClientId(program1->service_id(), &client_id)); |
| EXPECT_EQ(kClient1Id, client_id); |
| // Check we get nothing for a non-existent program. |
| EXPECT_TRUE(manager_->GetProgram(kClient2Id) == nullptr); |
| } |
| |
| TEST_F(ProgramManagerTest, Destroy) { |
| const GLuint kClient1Id = 1; |
| const GLuint kService1Id = 11; |
| // Check we can create program. |
| Program* program0 = manager_->CreateProgram(kClient1Id, kService1Id); |
| ASSERT_TRUE(program0 != nullptr); |
| // Check program got created. |
| Program* program1 = manager_->GetProgram(kClient1Id); |
| ASSERT_EQ(program0, program1); |
| EXPECT_CALL(*gl_, DeleteProgram(kService1Id)) |
| .Times(1) |
| .RetiresOnSaturation(); |
| manager_->Destroy(true); |
| // Check the resources were released. |
| program1 = manager_->GetProgram(kClient1Id); |
| ASSERT_TRUE(program1 == nullptr); |
| } |
| |
| TEST_F(ProgramManagerTest, DeleteBug) { |
| ShaderManager shader_manager(nullptr); |
| const GLuint kClient1Id = 1; |
| const GLuint kClient2Id = 2; |
| const GLuint kService1Id = 11; |
| const GLuint kService2Id = 12; |
| // Check we can create program. |
| scoped_refptr<Program> program1( |
| manager_->CreateProgram(kClient1Id, kService1Id)); |
| scoped_refptr<Program> program2( |
| manager_->CreateProgram(kClient2Id, kService2Id)); |
| // Check program got created. |
| ASSERT_TRUE(program1.get()); |
| ASSERT_TRUE(program2.get()); |
| manager_->UseProgram(program1.get()); |
| manager_->MarkAsDeleted(&shader_manager, program1.get()); |
| // Program will be deleted when last ref is released. |
| EXPECT_CALL(*gl_, DeleteProgram(kService2Id)) |
| .Times(1) |
| .RetiresOnSaturation(); |
| manager_->MarkAsDeleted(&shader_manager, program2.get()); |
| EXPECT_TRUE(manager_->IsOwned(program1.get())); |
| EXPECT_FALSE(manager_->IsOwned(program2.get())); |
| } |
| |
| TEST_F(ProgramManagerTest, Program) { |
| const GLuint kClient1Id = 1; |
| const GLuint kService1Id = 11; |
| // Check we can create program. |
| Program* program1 = manager_->CreateProgram(kClient1Id, kService1Id); |
| ASSERT_TRUE(program1); |
| EXPECT_EQ(kService1Id, program1->service_id()); |
| EXPECT_FALSE(program1->InUse()); |
| EXPECT_FALSE(program1->IsValid()); |
| EXPECT_FALSE(program1->IsDeleted()); |
| EXPECT_FALSE(program1->CanLink()); |
| EXPECT_TRUE(program1->log_info() == nullptr); |
| } |
| |
| class ProgramManagerWithShaderTest : public ProgramManagerTestBase { |
| public: |
| static const GLint kNumVertexAttribs = 16; |
| |
| static const GLuint kClientProgramId = 123; |
| static const GLuint kServiceProgramId = 456; |
| static const GLuint kVertexShaderClientId = 201; |
| static const GLuint kFragmentShaderClientId = 202; |
| static const GLuint kVertexShaderServiceId = 301; |
| static const GLuint kFragmentShaderServiceId = 302; |
| |
| static const char* kAttrib1Name; |
| static const char* kAttrib2Name; |
| static const char* kAttrib3Name; |
| static const char* kAttrib4Name; |
| static const GLint kAttrib1Size = 1; |
| static const GLint kAttrib2Size = 1; |
| static const GLint kAttrib3Size = 1; |
| static const GLint kAttrib4Size = 1; |
| static const GLenum kAttrib1Precision = GL_MEDIUM_FLOAT; |
| static const GLenum kAttrib2Precision = GL_HIGH_FLOAT; |
| static const GLenum kAttrib3Precision = GL_LOW_INT; |
| static const GLenum kAttrib4Precision = GL_HIGH_FLOAT; |
| static const bool kAttribStaticUse = true; |
| static const GLint kAttrib1Location = 0; |
| static const GLint kAttrib2Location = 1; |
| static const GLint kAttrib3Location = 2; |
| static const GLint kAttrib4Location = 3; |
| static const GLenum kAttrib1Type = GL_FLOAT_VEC4; |
| static const GLenum kAttrib2Type = GL_FLOAT_VEC2; |
| static const GLenum kAttrib3Type = GL_INT_VEC3; |
| static const GLenum kAttrib4Type = GL_FLOAT_MAT3x2; |
| static const GLint kInvalidAttribLocation = 30; |
| static const GLint kBadAttribIndex = kNumVertexAttribs; |
| |
| static const char* kUniform1Name; |
| static const char* kUniform2Name; |
| static const char* kUniform2NameWithArrayIndex; |
| static const char* kUniform3Name; |
| static const char* kUniform3NameWithArrayIndex; |
| static const GLint kUniform1Size = 1; |
| static const GLint kUniform2Size = 3; |
| static const GLint kUniform3Size = 2; |
| static const int kUniform1Precision = GL_LOW_FLOAT; |
| static const int kUniform2Precision = GL_MEDIUM_INT; |
| static const int kUniform3Precision = GL_HIGH_FLOAT; |
| static const int kUniform1StaticUse = 1; |
| static const int kUniform2StaticUse = 1; |
| static const int kUniform3StaticUse = 1; |
| static const GLint kUniform1FakeLocation = 0; // These are hard coded |
| static const GLint kUniform2FakeLocation = 1; // to match |
| static const GLint kUniform3FakeLocation = 2; // ProgramManager. |
| static const GLint kUniform1RealLocation = 11; |
| static const GLint kUniform2RealLocation = 22; |
| static const GLint kUniform3RealLocation = 33; |
| static const GLint kUniform1DesiredLocation = -1; |
| static const GLint kUniform2DesiredLocation = -1; |
| static const GLint kUniform3DesiredLocation = -1; |
| static const GLenum kUniform1Type = GL_FLOAT_VEC4; |
| static const GLenum kUniform2Type = GL_INT_VEC2; |
| static const GLenum kUniform3Type = GL_FLOAT_VEC3; |
| static const GLint kInvalidUniformLocation = 30; |
| static const GLint kBadUniformIndex = 1000; |
| |
| static const char* kOutputVariable1Name; |
| static const GLint kOutputVariable1Size = 1; |
| static const GLenum kOutputVariable1Precision = GL_MEDIUM_FLOAT; |
| static const bool kOutputVariable1StaticUse = true; |
| static const GLint kOutputVariable1Location = -1; |
| static const GLenum kOutputVariable1Type = GL_FLOAT_VEC4; |
| |
| static const size_t kNumAttribs; |
| static const size_t kNumUniforms; |
| |
| ProgramManagerWithShaderTest() : shader_manager_(nullptr) {} |
| |
| protected: |
| typedef TestHelper::AttribInfo AttribInfo; |
| typedef TestHelper::UniformInfo UniformInfo; |
| |
| typedef enum { |
| kVarUniform, |
| kVarVarying, |
| kVarAttribute, |
| kVarOutput, |
| } VarCategory; |
| |
| typedef struct { |
| GLenum type; |
| GLint size; |
| GLenum precision; |
| bool static_use; |
| std::string name; |
| VarCategory category; |
| } VarInfo; |
| |
| void SetUp() override { |
| // Need to be at leat 3.1 for UniformBlock related GL APIs. |
| SetUpBase("3.1", ""); |
| } |
| |
| Program* SetupDefaultProgram() { |
| SetupShaderExpectations(kAttribs, kNumAttribs, kUniforms, kNumUniforms, |
| kServiceProgramId); |
| |
| Shader* vertex_shader = shader_manager_.CreateShader( |
| kVertexShaderClientId, kVertexShaderServiceId, GL_VERTEX_SHADER); |
| Shader* fragment_shader = |
| shader_manager_.CreateShader( |
| kFragmentShaderClientId, kFragmentShaderServiceId, |
| GL_FRAGMENT_SHADER); |
| EXPECT_TRUE(vertex_shader != nullptr); |
| EXPECT_TRUE(fragment_shader != nullptr); |
| TestHelper::SetShaderStates(gl_.get(), vertex_shader, true); |
| TestHelper::SetShaderStates(gl_.get(), fragment_shader, true); |
| |
| Program* program = |
| manager_->CreateProgram(kClientProgramId, kServiceProgramId); |
| EXPECT_TRUE(program != nullptr); |
| |
| program->AttachShader(&shader_manager_, vertex_shader); |
| program->AttachShader(&shader_manager_, fragment_shader); |
| program->Link(nullptr, Program::kCountOnlyStaticallyUsed, this); |
| return program; |
| } |
| |
| void SetupShaderExpectations(AttribInfo* attribs, |
| size_t num_attribs, |
| UniformInfo* uniforms, |
| size_t num_uniforms, |
| GLuint service_id) { |
| TestHelper::SetupShaderExpectations(gl_.get(), feature_info_.get(), attribs, |
| num_attribs, uniforms, num_uniforms, |
| service_id); |
| } |
| |
| void SetupExpectationsForClearingUniforms( |
| UniformInfo* uniforms, size_t num_uniforms) { |
| TestHelper::SetupExpectationsForClearingUniforms( |
| gl_.get(), uniforms, num_uniforms); |
| } |
| |
| // Return true if link status matches expected_link_status |
| bool LinkAsExpected(Program* program, |
| bool expected_link_status) { |
| GLuint service_id = program->service_id(); |
| if (expected_link_status) { |
| SetupShaderExpectations(kAttribs, kNumAttribs, kUniforms, kNumUniforms, |
| service_id); |
| } |
| program->Link(nullptr, Program::kCountOnlyStaticallyUsed, this); |
| GLint link_status; |
| program->GetProgramiv(GL_LINK_STATUS, &link_status); |
| return (static_cast<bool>(link_status) == expected_link_status); |
| } |
| |
| Program* SetupProgramForVariables(const VarInfo* vertex_variables, |
| size_t vertex_variable_size, |
| const VarInfo* fragment_variables, |
| size_t fragment_variable_size, |
| const int* const shader_version = nullptr) { |
| // Set up shader |
| AttributeMap vertex_attrib_map; |
| UniformMap vertex_uniform_map; |
| VaryingMap vertex_varying_map; |
| OutputVariableList vertex_output_variable_list; |
| for (size_t ii = 0; ii < vertex_variable_size; ++ii) { |
| switch (vertex_variables[ii].category) { |
| case kVarAttribute: |
| vertex_attrib_map[vertex_variables[ii].name] = |
| TestHelper::ConstructAttribute( |
| vertex_variables[ii].type, |
| vertex_variables[ii].size, |
| vertex_variables[ii].precision, |
| vertex_variables[ii].static_use, |
| vertex_variables[ii].name); |
| break; |
| case kVarUniform: |
| vertex_uniform_map[vertex_variables[ii].name] = |
| TestHelper::ConstructUniform( |
| vertex_variables[ii].type, |
| vertex_variables[ii].size, |
| vertex_variables[ii].precision, |
| vertex_variables[ii].static_use, |
| vertex_variables[ii].name); |
| break; |
| case kVarVarying: |
| vertex_varying_map[vertex_variables[ii].name] = |
| TestHelper::ConstructVarying( |
| vertex_variables[ii].type, |
| vertex_variables[ii].size, |
| vertex_variables[ii].precision, |
| vertex_variables[ii].static_use, |
| vertex_variables[ii].name); |
| break; |
| case kVarOutput: |
| vertex_output_variable_list.push_back( |
| TestHelper::ConstructOutputVariable( |
| vertex_variables[ii].type, vertex_variables[ii].size, |
| vertex_variables[ii].precision, |
| vertex_variables[ii].static_use, vertex_variables[ii].name)); |
| break; |
| default: |
| NOTREACHED(); |
| } |
| } |
| |
| AttributeMap frag_attrib_map; |
| UniformMap frag_uniform_map; |
| VaryingMap frag_varying_map; |
| OutputVariableList frag_output_variable_list; |
| for (size_t ii = 0; ii < fragment_variable_size; ++ii) { |
| switch (fragment_variables[ii].category) { |
| case kVarAttribute: |
| frag_attrib_map[fragment_variables[ii].name] = |
| TestHelper::ConstructAttribute( |
| fragment_variables[ii].type, |
| fragment_variables[ii].size, |
| fragment_variables[ii].precision, |
| fragment_variables[ii].static_use, |
| fragment_variables[ii].name); |
| break; |
| case kVarUniform: |
| frag_uniform_map[fragment_variables[ii].name] = |
| TestHelper::ConstructUniform( |
| fragment_variables[ii].type, |
| fragment_variables[ii].size, |
| fragment_variables[ii].precision, |
| fragment_variables[ii].static_use, |
| fragment_variables[ii].name); |
| break; |
| case kVarVarying: |
| frag_varying_map[fragment_variables[ii].name] = |
| TestHelper::ConstructVarying( |
| fragment_variables[ii].type, |
| fragment_variables[ii].size, |
| fragment_variables[ii].precision, |
| fragment_variables[ii].static_use, |
| fragment_variables[ii].name); |
| break; |
| case kVarOutput: |
| frag_output_variable_list.push_back( |
| TestHelper::ConstructOutputVariable( |
| fragment_variables[ii].type, fragment_variables[ii].size, |
| fragment_variables[ii].precision, |
| fragment_variables[ii].static_use, |
| fragment_variables[ii].name)); |
| break; |
| default: |
| NOTREACHED(); |
| } |
| } |
| |
| // Check we can create shader. |
| Shader* vshader = shader_manager_.CreateShader( |
| kVertexShaderClientId, kVertexShaderServiceId, GL_VERTEX_SHADER); |
| Shader* fshader = shader_manager_.CreateShader( |
| kFragmentShaderClientId, kFragmentShaderServiceId, GL_FRAGMENT_SHADER); |
| // Check shader got created. |
| EXPECT_TRUE(vshader != nullptr && fshader != nullptr); |
| // Set Status |
| TestHelper::SetShaderStates(gl_.get(), vshader, true, nullptr, nullptr, |
| shader_version, &vertex_attrib_map, |
| &vertex_uniform_map, &vertex_varying_map, |
| nullptr, &vertex_output_variable_list, nullptr); |
| TestHelper::SetShaderStates(gl_.get(), fshader, true, nullptr, nullptr, |
| shader_version, &frag_attrib_map, |
| &frag_uniform_map, &frag_varying_map, nullptr, |
| &frag_output_variable_list, nullptr); |
| |
| // Set up program |
| Program* program = |
| manager_->CreateProgram(kClientProgramId, kServiceProgramId); |
| EXPECT_TRUE(program != nullptr); |
| EXPECT_TRUE(program->AttachShader(&shader_manager_, vshader)); |
| EXPECT_TRUE(program->AttachShader(&shader_manager_, fshader)); |
| return program; |
| } |
| |
| void TearDown() override { |
| shader_manager_.Destroy(false); |
| ProgramManagerTestBase::TearDown(); |
| } |
| |
| static AttribInfo kAttribs[]; |
| static UniformInfo kUniforms[]; |
| ShaderManager shader_manager_; |
| }; |
| |
| ProgramManagerWithShaderTest::AttribInfo |
| ProgramManagerWithShaderTest::kAttribs[] = { |
| { |
| kAttrib1Name, kAttrib1Size, kAttrib1Type, kAttrib1Location, |
| }, |
| { |
| kAttrib2Name, kAttrib2Size, kAttrib2Type, kAttrib2Location, |
| }, |
| { |
| kAttrib3Name, kAttrib3Size, kAttrib3Type, kAttrib3Location, |
| }, |
| { |
| kAttrib4Name, kAttrib4Size, kAttrib4Type, kAttrib4Location, |
| }, |
| }; |
| |
| // GCC requires these declarations, but MSVC requires they not be present |
| #ifndef COMPILER_MSVC |
| const GLint ProgramManagerWithShaderTest::kNumVertexAttribs; |
| const GLuint ProgramManagerWithShaderTest::kClientProgramId; |
| const GLuint ProgramManagerWithShaderTest::kServiceProgramId; |
| const GLuint ProgramManagerWithShaderTest::kVertexShaderClientId; |
| const GLuint ProgramManagerWithShaderTest::kFragmentShaderClientId; |
| const GLuint ProgramManagerWithShaderTest::kVertexShaderServiceId; |
| const GLuint ProgramManagerWithShaderTest::kFragmentShaderServiceId; |
| const GLint ProgramManagerWithShaderTest::kAttrib1Size; |
| const GLint ProgramManagerWithShaderTest::kAttrib2Size; |
| const GLint ProgramManagerWithShaderTest::kAttrib3Size; |
| const GLint ProgramManagerWithShaderTest::kAttrib4Size; |
| const GLint ProgramManagerWithShaderTest::kAttrib1Location; |
| const GLint ProgramManagerWithShaderTest::kAttrib2Location; |
| const GLint ProgramManagerWithShaderTest::kAttrib3Location; |
| const GLint ProgramManagerWithShaderTest::kAttrib4Location; |
| const GLenum ProgramManagerWithShaderTest::kAttrib1Type; |
| const GLenum ProgramManagerWithShaderTest::kAttrib2Type; |
| const GLenum ProgramManagerWithShaderTest::kAttrib3Type; |
| const GLenum ProgramManagerWithShaderTest::kAttrib4Type; |
| const GLint ProgramManagerWithShaderTest::kInvalidAttribLocation; |
| const GLint ProgramManagerWithShaderTest::kBadAttribIndex; |
| const GLint ProgramManagerWithShaderTest::kUniform1Size; |
| const GLint ProgramManagerWithShaderTest::kUniform2Size; |
| const GLint ProgramManagerWithShaderTest::kUniform3Size; |
| const GLint ProgramManagerWithShaderTest::kUniform1FakeLocation; |
| const GLint ProgramManagerWithShaderTest::kUniform2FakeLocation; |
| const GLint ProgramManagerWithShaderTest::kUniform3FakeLocation; |
| const GLint ProgramManagerWithShaderTest::kUniform1RealLocation; |
| const GLint ProgramManagerWithShaderTest::kUniform2RealLocation; |
| const GLint ProgramManagerWithShaderTest::kUniform3RealLocation; |
| const GLint ProgramManagerWithShaderTest::kUniform1DesiredLocation; |
| const GLint ProgramManagerWithShaderTest::kUniform2DesiredLocation; |
| const GLint ProgramManagerWithShaderTest::kUniform3DesiredLocation; |
| const GLenum ProgramManagerWithShaderTest::kUniform1Type; |
| const GLenum ProgramManagerWithShaderTest::kUniform2Type; |
| const GLenum ProgramManagerWithShaderTest::kUniform3Type; |
| const GLint ProgramManagerWithShaderTest::kInvalidUniformLocation; |
| const GLint ProgramManagerWithShaderTest::kBadUniformIndex; |
| #endif |
| |
| const size_t ProgramManagerWithShaderTest::kNumAttribs = |
| base::size(ProgramManagerWithShaderTest::kAttribs); |
| |
| ProgramManagerWithShaderTest::UniformInfo |
| ProgramManagerWithShaderTest::kUniforms[] = { |
| { kUniform1Name, |
| kUniform1Size, |
| kUniform1Type, |
| kUniform1FakeLocation, |
| kUniform1RealLocation, |
| kUniform1DesiredLocation, |
| kUniform1Name, |
| }, |
| { kUniform2Name, |
| kUniform2Size, |
| kUniform2Type, |
| kUniform2FakeLocation, |
| kUniform2RealLocation, |
| kUniform2DesiredLocation, |
| kUniform2NameWithArrayIndex, |
| }, |
| { kUniform3Name, |
| kUniform3Size, |
| kUniform3Type, |
| kUniform3FakeLocation, |
| kUniform3RealLocation, |
| kUniform3DesiredLocation, |
| kUniform3NameWithArrayIndex, |
| }, |
| }; |
| |
| const size_t ProgramManagerWithShaderTest::kNumUniforms = |
| base::size(ProgramManagerWithShaderTest::kUniforms); |
| |
| const char* ProgramManagerWithShaderTest::kAttrib1Name = "attrib1"; |
| const char* ProgramManagerWithShaderTest::kAttrib2Name = "attrib2"; |
| const char* ProgramManagerWithShaderTest::kAttrib3Name = "attrib3"; |
| const char* ProgramManagerWithShaderTest::kAttrib4Name = "attrib4"; |
| const char* ProgramManagerWithShaderTest::kUniform1Name = "uniform1"; |
| const char* ProgramManagerWithShaderTest::kUniform2Name = "uniform2"; |
| const char* ProgramManagerWithShaderTest::kUniform2NameWithArrayIndex = |
| "uniform2[0]"; |
| const char* ProgramManagerWithShaderTest::kUniform3Name = "uniform3"; |
| const char* ProgramManagerWithShaderTest::kUniform3NameWithArrayIndex = |
| "uniform3[0]"; |
| const char* ProgramManagerWithShaderTest::kOutputVariable1Name = "outputVar1"; |
| |
| TEST_F(ProgramManagerWithShaderTest, GetAttribInfos) { |
| const Program* program = SetupDefaultProgram(); |
| ASSERT_TRUE(program != nullptr); |
| const Program::AttribInfoVector& infos = |
| program->GetAttribInfos(); |
| ASSERT_EQ(kNumAttribs, infos.size()); |
| for (size_t ii = 0; ii < kNumAttribs; ++ii) { |
| const Program::VertexAttrib& info = infos[ii]; |
| const AttribInfo& expected = kAttribs[ii]; |
| EXPECT_EQ(expected.size, info.size); |
| EXPECT_EQ(expected.type, info.type); |
| EXPECT_EQ(expected.location, info.location); |
| EXPECT_STREQ(expected.name, info.name.c_str()); |
| } |
| } |
| |
| TEST_F(ProgramManagerWithShaderTest, GetAttribInfo) { |
| const GLint kValidIndex = 1; |
| const GLint kInvalidIndex = 1000; |
| const Program* program = SetupDefaultProgram(); |
| ASSERT_TRUE(program != nullptr); |
| const Program::VertexAttrib* info = |
| program->GetAttribInfo(kValidIndex); |
| ASSERT_TRUE(info != nullptr); |
| EXPECT_EQ(kAttrib2Size, info->size); |
| EXPECT_EQ(kAttrib2Type, info->type); |
| EXPECT_EQ(kAttrib2Location, info->location); |
| EXPECT_STREQ(kAttrib2Name, info->name.c_str()); |
| EXPECT_TRUE(program->GetAttribInfo(kInvalidIndex) == nullptr); |
| } |
| |
| TEST_F(ProgramManagerWithShaderTest, GetAttribInfoByLocation) { |
| const GLint kInvalidLocation = 1000; |
| const Program* program = SetupDefaultProgram(); |
| ASSERT_TRUE(program != nullptr); |
| |
| // attrib2 is a vec2, takes 1 location |
| const Program::VertexAttrib* expected_info = program->GetAttribInfo(1); |
| EXPECT_EQ(1u, expected_info->location_count); |
| const Program::VertexAttrib* info = |
| program->GetAttribInfoByLocation(kAttrib2Location); |
| EXPECT_EQ(expected_info, info); |
| |
| // attrib4 is a mat3x2, takes 3 locations (1 per column) |
| expected_info = program->GetAttribInfo(3); |
| EXPECT_EQ(3u, expected_info->location_count); |
| info = program->GetAttribInfoByLocation(kAttrib4Location); |
| EXPECT_EQ(expected_info, info); |
| info = program->GetAttribInfoByLocation(kAttrib4Location + 1); |
| EXPECT_EQ(expected_info, info); |
| info = program->GetAttribInfoByLocation(kAttrib4Location + 2); |
| EXPECT_EQ(expected_info, info); |
| |
| EXPECT_TRUE(program->GetAttribInfoByLocation(kInvalidLocation) == nullptr); |
| } |
| |
| TEST_F(ProgramManagerWithShaderTest, GetAttribLocation) { |
| const char* kInvalidName = "foo"; |
| const Program* program = SetupDefaultProgram(); |
| ASSERT_TRUE(program != nullptr); |
| EXPECT_EQ(kAttrib2Location, program->GetAttribLocation(kAttrib2Name)); |
| EXPECT_EQ(-1, program->GetAttribLocation(kInvalidName)); |
| } |
| |
| TEST_F(ProgramManagerWithShaderTest, VertexArrayMasks) { |
| const Program* program = SetupDefaultProgram(); |
| ASSERT_TRUE(program != nullptr); |
| |
| std::vector<uint32_t> active_mask = program->vertex_input_active_mask(); |
| ASSERT_EQ(1u, active_mask.size()); |
| uint32_t expected = 0x3 << 0 | // attrib1 |
| 0x3 << 2 | // attrib2 |
| 0x3 << 4 | // attrib3 |
| (0x3 * 0b010101) << 6; // attrib4 |
| EXPECT_EQ(expected, active_mask[0]); |
| |
| std::vector<uint32_t> base_type_mask = program->vertex_input_base_type_mask(); |
| ASSERT_EQ(1u, base_type_mask.size()); |
| expected = SHADER_VARIABLE_FLOAT << 0 | // attrib1 |
| SHADER_VARIABLE_FLOAT << 2 | // attrib2 |
| SHADER_VARIABLE_INT << 4 | // attrib3 |
| (SHADER_VARIABLE_FLOAT * 0b010101) << 6; // attrib4 |
| EXPECT_EQ(expected, base_type_mask[0]); |
| } |
| |
| TEST_F(ProgramManagerWithShaderTest, GetUniformInfo) { |
| const GLint kInvalidIndex = 1000; |
| const Program* program = SetupDefaultProgram(); |
| ASSERT_TRUE(program != nullptr); |
| const Program::UniformInfo* info = |
| program->GetUniformInfo(0); |
| ASSERT_TRUE(info != nullptr); |
| EXPECT_EQ(kUniform1Size, info->size); |
| EXPECT_EQ(kUniform1Type, info->type); |
| EXPECT_EQ(kUniform1RealLocation, info->element_locations[0]); |
| EXPECT_STREQ(kUniform1Name, info->name.c_str()); |
| info = program->GetUniformInfo(1); |
| ASSERT_TRUE(info != nullptr); |
| EXPECT_EQ(kUniform2Size, info->size); |
| EXPECT_EQ(kUniform2Type, info->type); |
| EXPECT_EQ(kUniform2RealLocation, info->element_locations[0]); |
| EXPECT_STREQ(kUniform2NameWithArrayIndex, info->name.c_str()); |
| info = program->GetUniformInfo(2); |
| // We emulate certain OpenGL drivers by supplying the name without |
| // the array spec. Our implementation should correctly add the required spec. |
| ASSERT_TRUE(info != nullptr); |
| EXPECT_EQ(kUniform3Size, info->size); |
| EXPECT_EQ(kUniform3Type, info->type); |
| EXPECT_EQ(kUniform3RealLocation, info->element_locations[0]); |
| EXPECT_STREQ(kUniform3NameWithArrayIndex, info->name.c_str()); |
| EXPECT_TRUE(program->GetUniformInfo(kInvalidIndex) == nullptr); |
| } |
| |
| TEST_F(ProgramManagerWithShaderTest, AttachDetachShader) { |
| Program* program = |
| manager_->CreateProgram(kClientProgramId, kServiceProgramId); |
| ASSERT_TRUE(program != nullptr); |
| EXPECT_FALSE(program->CanLink()); |
| Shader* vshader = shader_manager_.CreateShader( |
| kVertexShaderClientId, kVertexShaderServiceId, GL_VERTEX_SHADER); |
| ASSERT_TRUE(vshader != nullptr); |
| TestHelper::SetShaderStates(gl_.get(), vshader, true); |
| Shader* fshader = shader_manager_.CreateShader( |
| kFragmentShaderClientId, kFragmentShaderServiceId, GL_FRAGMENT_SHADER); |
| ASSERT_TRUE(fshader != nullptr); |
| TestHelper::SetShaderStates(gl_.get(), fshader, true); |
| EXPECT_TRUE(program->AttachShader(&shader_manager_, vshader)); |
| EXPECT_FALSE(program->CanLink()); |
| EXPECT_TRUE(program->AttachShader(&shader_manager_, fshader)); |
| EXPECT_TRUE(program->CanLink()); |
| program->DetachShader(&shader_manager_, vshader); |
| EXPECT_FALSE(program->CanLink()); |
| EXPECT_TRUE(program->AttachShader(&shader_manager_, vshader)); |
| EXPECT_TRUE(program->CanLink()); |
| program->DetachShader(&shader_manager_, fshader); |
| EXPECT_FALSE(program->CanLink()); |
| EXPECT_FALSE(program->AttachShader(&shader_manager_, vshader)); |
| EXPECT_FALSE(program->CanLink()); |
| EXPECT_TRUE(program->AttachShader(&shader_manager_, fshader)); |
| EXPECT_TRUE(program->CanLink()); |
| TestHelper::SetShaderStates(gl_.get(), vshader, false); |
| EXPECT_FALSE(program->CanLink()); |
| TestHelper::SetShaderStates(gl_.get(), vshader, true); |
| EXPECT_TRUE(program->CanLink()); |
| TestHelper::SetShaderStates(gl_.get(), fshader, false); |
| EXPECT_FALSE(program->CanLink()); |
| TestHelper::SetShaderStates(gl_.get(), fshader, true); |
| EXPECT_TRUE(program->CanLink()); |
| EXPECT_TRUE(program->IsShaderAttached(fshader)); |
| program->DetachShader(&shader_manager_, fshader); |
| EXPECT_FALSE(program->IsShaderAttached(fshader)); |
| } |
| |
| TEST_F(ProgramManagerWithShaderTest, GetUniformFakeLocation) { |
| const Program* program = SetupDefaultProgram(); |
| ASSERT_TRUE(program != nullptr); |
| // Emulate the situation that uniform3[1] isn't used and optimized out by |
| // a driver, so it's location is -1. |
| Program::UniformInfo* uniform = const_cast<Program::UniformInfo*>( |
| program->GetUniformInfo(2)); |
| ASSERT_TRUE(uniform != nullptr && kUniform3Size == 2); |
| EXPECT_EQ(kUniform3Size, uniform->size); |
| uniform->element_locations[1] = -1; |
| EXPECT_EQ(kUniform1FakeLocation, |
| program->GetUniformFakeLocation(kUniform1Name)); |
| EXPECT_EQ(kUniform2FakeLocation, |
| program->GetUniformFakeLocation(kUniform2Name)); |
| EXPECT_EQ(kUniform3FakeLocation, |
| program->GetUniformFakeLocation(kUniform3Name)); |
| // Check we can get uniform2 as "uniform2" even though the name is |
| // "uniform2[0]" |
| EXPECT_EQ(kUniform2FakeLocation, |
| program->GetUniformFakeLocation("uniform2")); |
| // Check we can get uniform3 as "uniform3[0]" even though we simulated GL |
| // returning "uniform3" |
| EXPECT_EQ(kUniform3FakeLocation, |
| program->GetUniformFakeLocation(kUniform3NameWithArrayIndex)); |
| // Check that we can get the locations of the array elements > 1 |
| EXPECT_EQ(ProgramManager::MakeFakeLocation(kUniform2FakeLocation, 1), |
| program->GetUniformFakeLocation("uniform2[1]")); |
| EXPECT_EQ(ProgramManager::MakeFakeLocation(kUniform2FakeLocation, 2), |
| program->GetUniformFakeLocation("uniform2[2]")); |
| EXPECT_EQ(-1, program->GetUniformFakeLocation("uniform2[3]")); |
| EXPECT_EQ(-1, program->GetUniformFakeLocation("uniform3[1]")); |
| EXPECT_EQ(-1, program->GetUniformFakeLocation("uniform3[2]")); |
| } |
| |
| TEST_F(ProgramManagerWithShaderTest, GetUniformInfoByFakeLocation) { |
| const GLint kInvalidLocation = 1234; |
| const Program::UniformInfo* info; |
| const Program* program = SetupDefaultProgram(); |
| GLint real_location = -1; |
| GLint array_index = -1; |
| ASSERT_TRUE(program != nullptr); |
| info = program->GetUniformInfoByFakeLocation( |
| kUniform2FakeLocation, &real_location, &array_index); |
| EXPECT_EQ(kUniform2RealLocation, real_location); |
| EXPECT_EQ(0, array_index); |
| ASSERT_TRUE(info != nullptr); |
| EXPECT_EQ(kUniform2Type, info->type); |
| real_location = -1; |
| array_index = -1; |
| info = program->GetUniformInfoByFakeLocation( |
| kInvalidLocation, &real_location, &array_index); |
| EXPECT_TRUE(info == nullptr); |
| EXPECT_EQ(-1, real_location); |
| EXPECT_EQ(-1, array_index); |
| GLint loc = program->GetUniformFakeLocation("uniform2[2]"); |
| info = program->GetUniformInfoByFakeLocation( |
| loc, &real_location, &array_index); |
| ASSERT_TRUE(info != nullptr); |
| EXPECT_EQ(kUniform2RealLocation + 2 * 2, real_location); |
| EXPECT_EQ(2, array_index); |
| } |
| |
| // Ensure that when GL drivers correctly return gl_DepthRange, or other |
| // builtin uniforms, our implementation passes them back to the client. |
| TEST_F(ProgramManagerWithShaderTest, GLDriverReturnsGLUnderscoreUniform) { |
| static const char* kUniform2Name = "gl_longNameWeCanCheckFor"; |
| static ProgramManagerWithShaderTest::UniformInfo kUniforms[] = { |
| { |
| kUniform1Name, |
| kUniform1Size, |
| kUniform1Type, |
| kUniform1FakeLocation, |
| kUniform1RealLocation, |
| kUniform1DesiredLocation, |
| kUniform1Name, |
| }, |
| { |
| kUniform2Name, |
| kUniform2Size, |
| kUniform2Type, |
| kUniform2FakeLocation, |
| -1, |
| kUniform2DesiredLocation, |
| kUniform2NameWithArrayIndex, |
| }, |
| { |
| kUniform3Name, |
| kUniform3Size, |
| kUniform3Type, |
| kUniform3FakeLocation, |
| kUniform3RealLocation, |
| kUniform3DesiredLocation, |
| kUniform3NameWithArrayIndex, |
| }, |
| }; |
| const size_t kNumUniforms = base::size(kUniforms); |
| SetupShaderExpectations(kAttribs, kNumAttribs, kUniforms, kNumUniforms, |
| kServiceProgramId); |
| Shader* vshader = shader_manager_.CreateShader( |
| kVertexShaderClientId, kVertexShaderServiceId, GL_VERTEX_SHADER); |
| ASSERT_TRUE(vshader != nullptr); |
| TestHelper::SetShaderStates(gl_.get(), vshader, true); |
| Shader* fshader = shader_manager_.CreateShader( |
| kFragmentShaderClientId, kFragmentShaderServiceId, GL_FRAGMENT_SHADER); |
| ASSERT_TRUE(fshader != nullptr); |
| TestHelper::SetShaderStates(gl_.get(), fshader, true); |
| Program* program = |
| manager_->CreateProgram(kClientProgramId, kServiceProgramId); |
| ASSERT_TRUE(program != nullptr); |
| EXPECT_TRUE(program->AttachShader(&shader_manager_, vshader)); |
| EXPECT_TRUE(program->AttachShader(&shader_manager_, fshader)); |
| program->Link(nullptr, Program::kCountOnlyStaticallyUsed, this); |
| GLint value = 0; |
| program->GetProgramiv(GL_ACTIVE_ATTRIBUTES, &value); |
| EXPECT_EQ(4, value); |
| // Check that we didn't skip the "gl_" uniform. |
| program->GetProgramiv(GL_ACTIVE_UNIFORMS, &value); |
| EXPECT_EQ(3, value); |
| // Check that our max length adds room for the array spec and is as long |
| // as the "gl_" uniform we did not skip. |
| program->GetProgramiv(GL_ACTIVE_UNIFORM_MAX_LENGTH, &value); |
| EXPECT_EQ(strlen(kUniform2Name) + 4, static_cast<size_t>(value)); |
| // Verify the uniform has a "real" location of -1 |
| const auto* info = program->GetUniformInfo(kUniform2FakeLocation); |
| EXPECT_EQ(-1, info->element_locations[0]); |
| } |
| |
| // Test the bug comparing similar array names is fixed. |
| TEST_F(ProgramManagerWithShaderTest, SimilarArrayNames) { |
| static const char* kUniform2Name = "u_nameLong[0]"; |
| static const char* kUniform3Name = "u_name[0]"; |
| static const GLint kUniform2Size = 2; |
| static const GLint kUniform3Size = 2; |
| static ProgramManagerWithShaderTest::UniformInfo kUniforms[] = { |
| { kUniform1Name, |
| kUniform1Size, |
| kUniform1Type, |
| kUniform1FakeLocation, |
| kUniform1RealLocation, |
| kUniform1DesiredLocation, |
| kUniform1Name, |
| }, |
| { kUniform2Name, |
| kUniform2Size, |
| kUniform2Type, |
| kUniform2FakeLocation, |
| kUniform2RealLocation, |
| kUniform2DesiredLocation, |
| kUniform2Name, |
| }, |
| { kUniform3Name, |
| kUniform3Size, |
| kUniform3Type, |
| kUniform3FakeLocation, |
| kUniform3RealLocation, |
| kUniform3DesiredLocation, |
| kUniform3Name, |
| }, |
| }; |
| const size_t kNumUniforms = base::size(kUniforms); |
| SetupShaderExpectations(kAttribs, kNumAttribs, kUniforms, kNumUniforms, |
| kServiceProgramId); |
| Shader* vshader = shader_manager_.CreateShader( |
| kVertexShaderClientId, kVertexShaderServiceId, GL_VERTEX_SHADER); |
| ASSERT_TRUE(vshader != nullptr); |
| TestHelper::SetShaderStates(gl_.get(), vshader, true); |
| Shader* fshader = shader_manager_.CreateShader( |
| kFragmentShaderClientId, kFragmentShaderServiceId, GL_FRAGMENT_SHADER); |
| ASSERT_TRUE(fshader != nullptr); |
| TestHelper::SetShaderStates(gl_.get(), fshader, true); |
| Program* program = |
| manager_->CreateProgram(kClientProgramId, kServiceProgramId); |
| ASSERT_TRUE(program != nullptr); |
| EXPECT_TRUE(program->AttachShader(&shader_manager_, vshader)); |
| EXPECT_TRUE(program->AttachShader(&shader_manager_, fshader)); |
| program->Link(nullptr, Program::kCountOnlyStaticallyUsed, this); |
| |
| // Check that we get the correct locations. |
| EXPECT_EQ(kUniform2FakeLocation, |
| program->GetUniformFakeLocation(kUniform2Name)); |
| EXPECT_EQ(kUniform3FakeLocation, |
| program->GetUniformFakeLocation(kUniform3Name)); |
| } |
| |
| // Some GL drivers incorrectly return the wrong type. For example they return |
| // GL_FLOAT_VEC2 when they should return GL_FLOAT_MAT2. Check we handle this. |
| TEST_F(ProgramManagerWithShaderTest, GLDriverReturnsWrongTypeInfo) { |
| static GLenum kAttrib2BadType = GL_FLOAT_VEC2; |
| static GLenum kAttrib2GoodType = GL_FLOAT_MAT2; |
| static GLenum kUniform2BadType = GL_FLOAT_VEC3; |
| static GLenum kUniform2GoodType = GL_FLOAT_MAT3; |
| AttributeMap attrib_map; |
| UniformMap uniform_map; |
| VaryingMap varying_map; |
| OutputVariableList output_variable_list; |
| attrib_map[kAttrib1Name] = TestHelper::ConstructAttribute( |
| kAttrib1Type, kAttrib1Size, kAttrib1Precision, |
| kAttribStaticUse, kAttrib1Name); |
| attrib_map[kAttrib2Name] = TestHelper::ConstructAttribute( |
| kAttrib2GoodType, kAttrib2Size, kAttrib2Precision, |
| kAttribStaticUse, kAttrib2Name); |
| attrib_map[kAttrib3Name] = TestHelper::ConstructAttribute( |
| kAttrib3Type, kAttrib3Size, kAttrib3Precision, |
| kAttribStaticUse, kAttrib3Name); |
| uniform_map[kUniform1Name] = TestHelper::ConstructUniform( |
| kUniform1Type, kUniform1Size, kUniform1Precision, |
| kUniform1StaticUse, kUniform1Name); |
| uniform_map[kUniform2Name] = TestHelper::ConstructUniform( |
| kUniform2GoodType, kUniform2Size, kUniform2Precision, |
| kUniform2StaticUse, kUniform2Name); |
| uniform_map[kUniform3Name] = TestHelper::ConstructUniform( |
| kUniform3Type, kUniform3Size, kUniform3Precision, |
| kUniform3StaticUse, kUniform3Name); |
| output_variable_list.push_back(TestHelper::ConstructOutputVariable( |
| kOutputVariable1Type, kOutputVariable1Size, kOutputVariable1Precision, |
| kOutputVariable1StaticUse, kOutputVariable1Name)); |
| |
| Shader* vshader = shader_manager_.CreateShader( |
| kVertexShaderClientId, kVertexShaderServiceId, GL_VERTEX_SHADER); |
| ASSERT_TRUE(vshader != nullptr); |
| TestHelper::SetShaderStates(gl_.get(), vshader, true, nullptr, nullptr, |
| nullptr, &attrib_map, &uniform_map, &varying_map, |
| nullptr, &output_variable_list, nullptr); |
| Shader* fshader = shader_manager_.CreateShader( |
| kFragmentShaderClientId, kFragmentShaderServiceId, GL_FRAGMENT_SHADER); |
| ASSERT_TRUE(fshader != nullptr); |
| TestHelper::SetShaderStates(gl_.get(), fshader, true, nullptr, nullptr, |
| nullptr, &attrib_map, &uniform_map, &varying_map, |
| nullptr, &output_variable_list, nullptr); |
| static ProgramManagerWithShaderTest::AttribInfo kAttribs[] = { |
| { kAttrib1Name, kAttrib1Size, kAttrib1Type, kAttrib1Location, }, |
| { kAttrib2Name, kAttrib2Size, kAttrib2BadType, kAttrib2Location, }, |
| { kAttrib3Name, kAttrib3Size, kAttrib3Type, kAttrib3Location, }, |
| }; |
| static ProgramManagerWithShaderTest::UniformInfo kUniforms[] = { |
| { kUniform1Name, |
| kUniform1Size, |
| kUniform1Type, |
| kUniform1FakeLocation, |
| kUniform1RealLocation, |
| kUniform1DesiredLocation, |
| kUniform1Name, |
| }, |
| { kUniform2Name, |
| kUniform2Size, |
| kUniform2BadType, |
| kUniform2FakeLocation, |
| kUniform2RealLocation, |
| kUniform2DesiredLocation, |
| kUniform2NameWithArrayIndex, |
| }, |
| { kUniform3Name, |
| kUniform3Size, |
| kUniform3Type, |
| kUniform3FakeLocation, |
| kUniform3RealLocation, |
| kUniform3DesiredLocation, |
| kUniform3NameWithArrayIndex, |
| }, |
| }; |
| const size_t kNumAttribs = base::size(kAttribs); |
| const size_t kNumUniforms = base::size(kUniforms); |
| SetupShaderExpectations(kAttribs, kNumAttribs, kUniforms, kNumUniforms, |
| kServiceProgramId); |
| Program* program = |
| manager_->CreateProgram(kClientProgramId, kServiceProgramId); |
| ASSERT_TRUE(program != nullptr); |
| EXPECT_TRUE(program->AttachShader(&shader_manager_, vshader)); |
| EXPECT_TRUE(program->AttachShader(&shader_manager_, fshader)); |
| program->Link(nullptr, Program::kCountOnlyStaticallyUsed, this); |
| // Check that we got the good type, not the bad. |
| // Check Attribs |
| for (unsigned index = 0; index < kNumAttribs; ++index) { |
| const Program::VertexAttrib* attrib_info = |
| program->GetAttribInfo(index); |
| ASSERT_TRUE(attrib_info != nullptr); |
| size_t pos = attrib_info->name.find_first_of("[."); |
| std::string top_name; |
| if (pos == std::string::npos) |
| top_name = attrib_info->name; |
| else |
| top_name = attrib_info->name.substr(0, pos); |
| AttributeMap::const_iterator it = attrib_map.find(top_name); |
| ASSERT_TRUE(it != attrib_map.end()); |
| const sh::ShaderVariable* info; |
| std::string original_name; |
| EXPECT_TRUE(it->second.findInfoByMappedName( |
| attrib_info->name, &info, &original_name)); |
| EXPECT_EQ(info->type, attrib_info->type); |
| EXPECT_EQ(static_cast<GLint>(info->getOutermostArraySize()), |
| attrib_info->size); |
| EXPECT_EQ(original_name, attrib_info->name); |
| } |
| // Check Uniforms |
| for (unsigned index = 0; index < kNumUniforms; ++index) { |
| const Program::UniformInfo* uniform_info = program->GetUniformInfo(index); |
| ASSERT_TRUE(uniform_info != nullptr); |
| size_t pos = uniform_info->name.find_first_of("[."); |
| std::string top_name; |
| if (pos == std::string::npos) |
| top_name = uniform_info->name; |
| else |
| top_name = uniform_info->name.substr(0, pos); |
| UniformMap::const_iterator it = uniform_map.find(top_name); |
| ASSERT_TRUE(it != uniform_map.end()); |
| const sh::ShaderVariable* info; |
| std::string original_name; |
| EXPECT_TRUE(it->second.findInfoByMappedName( |
| uniform_info->name, &info, &original_name)); |
| EXPECT_EQ(info->type, uniform_info->type); |
| EXPECT_EQ(static_cast<GLint>(info->getOutermostArraySize()), |
| uniform_info->size); |
| EXPECT_EQ(original_name, uniform_info->name); |
| } |
| } |
| |
| TEST_F(ProgramManagerWithShaderTest, ProgramInfoUseCount) { |
| Program* program = |
| manager_->CreateProgram(kClientProgramId, kServiceProgramId); |
| ASSERT_TRUE(program != nullptr); |
| EXPECT_FALSE(program->CanLink()); |
| Shader* vshader = shader_manager_.CreateShader( |
| kVertexShaderClientId, kVertexShaderServiceId, GL_VERTEX_SHADER); |
| ASSERT_TRUE(vshader != nullptr); |
| TestHelper::SetShaderStates(gl_.get(), vshader, true); |
| Shader* fshader = shader_manager_.CreateShader( |
| kFragmentShaderClientId, kFragmentShaderServiceId, GL_FRAGMENT_SHADER); |
| ASSERT_TRUE(fshader != nullptr); |
| TestHelper::SetShaderStates(gl_.get(), fshader, true); |
| EXPECT_FALSE(vshader->InUse()); |
| EXPECT_FALSE(fshader->InUse()); |
| EXPECT_TRUE(program->AttachShader(&shader_manager_, vshader)); |
| EXPECT_TRUE(vshader->InUse()); |
| EXPECT_TRUE(program->AttachShader(&shader_manager_, fshader)); |
| EXPECT_TRUE(fshader->InUse()); |
| EXPECT_TRUE(program->CanLink()); |
| EXPECT_FALSE(program->InUse()); |
| EXPECT_FALSE(program->IsDeleted()); |
| manager_->UseProgram(program); |
| EXPECT_TRUE(program->InUse()); |
| manager_->UseProgram(program); |
| EXPECT_TRUE(program->InUse()); |
| manager_->MarkAsDeleted(&shader_manager_, program); |
| EXPECT_TRUE(program->IsDeleted()); |
| Program* info2 = manager_->GetProgram(kClientProgramId); |
| EXPECT_EQ(program, info2); |
| manager_->UnuseProgram(&shader_manager_, program); |
| EXPECT_TRUE(program->InUse()); |
| // this should delete the info. |
| EXPECT_CALL(*gl_, DeleteProgram(kServiceProgramId)) |
| .Times(1) |
| .RetiresOnSaturation(); |
| manager_->UnuseProgram(&shader_manager_, program); |
| info2 = manager_->GetProgram(kClientProgramId); |
| EXPECT_TRUE(info2 == nullptr); |
| EXPECT_FALSE(vshader->InUse()); |
| EXPECT_FALSE(fshader->InUse()); |
| } |
| |
| TEST_F(ProgramManagerWithShaderTest, ProgramInfoUseCount2) { |
| Program* program = |
| manager_->CreateProgram(kClientProgramId, kServiceProgramId); |
| ASSERT_TRUE(program != nullptr); |
| EXPECT_FALSE(program->CanLink()); |
| Shader* vshader = shader_manager_.CreateShader( |
| kVertexShaderClientId, kVertexShaderServiceId, GL_VERTEX_SHADER); |
| ASSERT_TRUE(vshader != nullptr); |
| TestHelper::SetShaderStates(gl_.get(), vshader, true); |
| Shader* fshader = shader_manager_.CreateShader( |
| kFragmentShaderClientId, kFragmentShaderServiceId, GL_FRAGMENT_SHADER); |
| ASSERT_TRUE(fshader != nullptr); |
| TestHelper::SetShaderStates(gl_.get(), fshader, true); |
| EXPECT_FALSE(vshader->InUse()); |
| EXPECT_FALSE(fshader->InUse()); |
| EXPECT_TRUE(program->AttachShader(&shader_manager_, vshader)); |
| EXPECT_TRUE(vshader->InUse()); |
| EXPECT_TRUE(program->AttachShader(&shader_manager_, fshader)); |
| EXPECT_TRUE(fshader->InUse()); |
| EXPECT_TRUE(program->CanLink()); |
| EXPECT_FALSE(program->InUse()); |
| EXPECT_FALSE(program->IsDeleted()); |
| manager_->UseProgram(program); |
| EXPECT_TRUE(program->InUse()); |
| manager_->UseProgram(program); |
| EXPECT_TRUE(program->InUse()); |
| manager_->UnuseProgram(&shader_manager_, program); |
| EXPECT_TRUE(program->InUse()); |
| manager_->UnuseProgram(&shader_manager_, program); |
| EXPECT_FALSE(program->InUse()); |
| Program* info2 = manager_->GetProgram(kClientProgramId); |
| EXPECT_EQ(program, info2); |
| // this should delete the program. |
| EXPECT_CALL(*gl_, DeleteProgram(kServiceProgramId)) |
| .Times(1) |
| .RetiresOnSaturation(); |
| manager_->MarkAsDeleted(&shader_manager_, program); |
| info2 = manager_->GetProgram(kClientProgramId); |
| EXPECT_TRUE(info2 == nullptr); |
| EXPECT_FALSE(vshader->InUse()); |
| EXPECT_FALSE(fshader->InUse()); |
| } |
| |
| TEST_F(ProgramManagerWithShaderTest, ProgramInfoGetProgramInfo) { |
| CommonDecoder::Bucket bucket; |
| const Program* program = SetupDefaultProgram(); |
| ASSERT_TRUE(program != nullptr); |
| program->GetProgramInfo(manager_.get(), &bucket); |
| ProgramInfoHeader* header = |
| bucket.GetDataAs<ProgramInfoHeader*>(0, sizeof(ProgramInfoHeader)); |
| ASSERT_TRUE(header != nullptr); |
| EXPECT_EQ(1u, header->link_status); |
| EXPECT_EQ(base::size(kAttribs), header->num_attribs); |
| EXPECT_EQ(base::size(kUniforms), header->num_uniforms); |
| const ProgramInput* inputs = bucket.GetDataAs<const ProgramInput*>( |
| sizeof(*header), |
| sizeof(ProgramInput) * (header->num_attribs + header->num_uniforms)); |
| ASSERT_TRUE(inputs != nullptr); |
| const ProgramInput* input = inputs; |
| // TODO(gman): Don't assume these are in order. |
| for (uint32_t ii = 0; ii < header->num_attribs; ++ii) { |
| const AttribInfo& expected = kAttribs[ii]; |
| EXPECT_EQ(expected.size, input->size); |
| EXPECT_EQ(expected.type, input->type); |
| const int32_t* location = bucket.GetDataAs<const int32_t*>( |
| input->location_offset, sizeof(int32_t)); |
| ASSERT_TRUE(location != nullptr); |
| EXPECT_EQ(expected.location, *location); |
| const char* name_buf = bucket.GetDataAs<const char*>( |
| input->name_offset, input->name_length); |
| ASSERT_TRUE(name_buf != nullptr); |
| std::string name(name_buf, input->name_length); |
| EXPECT_STREQ(expected.name, name.c_str()); |
| ++input; |
| } |
| // TODO(gman): Don't assume these are in order. |
| for (uint32_t ii = 0; ii < header->num_uniforms; ++ii) { |
| const UniformInfo& expected = kUniforms[ii]; |
| EXPECT_EQ(expected.size, input->size); |
| EXPECT_EQ(expected.type, input->type); |
| const int32_t* locations = bucket.GetDataAs<const int32_t*>( |
| input->location_offset, sizeof(int32_t) * input->size); |
| ASSERT_TRUE(locations != nullptr); |
| for (int32_t jj = 0; jj < input->size; ++jj) { |
| EXPECT_EQ( |
| ProgramManager::MakeFakeLocation(expected.fake_location, jj), |
| locations[jj]); |
| } |
| const char* name_buf = bucket.GetDataAs<const char*>( |
| input->name_offset, input->name_length); |
| ASSERT_TRUE(name_buf != nullptr); |
| std::string name(name_buf, input->name_length); |
| EXPECT_STREQ(expected.good_name, name.c_str()); |
| ++input; |
| } |
| EXPECT_EQ(header->num_attribs + header->num_uniforms, |
| static_cast<uint32_t>(input - inputs)); |
| } |
| |
| TEST_F(ProgramManagerWithShaderTest, ProgramInfoGetUniformBlocksNone) { |
| CommonDecoder::Bucket bucket; |
| const Program* program = SetupDefaultProgram(); |
| ASSERT_TRUE(program != nullptr); |
| // 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 != nullptr); |
| 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 != nullptr); |
| EXPECT_EQ(0u, header->num_uniform_blocks); |
| } |
| |
| TEST_F(ProgramManagerWithShaderTest, ProgramInfoGetUniformBlocksValid) { |
| CommonDecoder::Bucket bucket; |
| const Program* program = SetupDefaultProgram(); |
| ASSERT_TRUE(program != nullptr); |
| 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 = base::size(data.name0); |
| data.entry[0].active_uniforms = base::size(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 = base::size(data.name1); |
| data.entry[1].active_uniforms = base::size(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], base::size(data.name0)); |
| data.indices0[0] = kIndices[0][0]; |
| data.indices0[1] = kIndices[0][1]; |
| memcpy(data.name1, kName[1], base::size(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 != nullptr); |
| EXPECT_EQ(0, memcmp(&data, bucket_data, sizeof(Data))); |
| } |
| |
| TEST_F(ProgramManagerWithShaderTest, |
| ProgramInfoGetTransformFeedbackVaryingsNone) { |
| CommonDecoder::Bucket bucket; |
| const Program* program = SetupDefaultProgram(); |
| ASSERT_TRUE(program != nullptr); |
| // The program's previous link failed. |
| EXPECT_CALL(*(gl_.get()), |
| GetProgramiv(kServiceProgramId, |
| GL_TRANSFORM_FEEDBACK_BUFFER_MODE, |
| _)) |
| .WillOnce(SetArgPointee<2>(GL_INTERLEAVED_ATTRIBS)) |
| .RetiresOnSaturation(); |
| EXPECT_CALL(*(gl_.get()), |
| GetProgramiv(kServiceProgramId, GL_LINK_STATUS, _)) |
| .WillOnce(SetArgPointee<2>(GL_FALSE)) |
| .RetiresOnSaturation(); |
| EXPECT_TRUE(program->GetTransformFeedbackVaryings(&bucket)); |
| EXPECT_EQ(sizeof(TransformFeedbackVaryingsHeader), bucket.size()); |
| TransformFeedbackVaryingsHeader* header = |
| bucket.GetDataAs<TransformFeedbackVaryingsHeader*>( |
| 0, sizeof(TransformFeedbackVaryingsHeader)); |
| EXPECT_TRUE(header != nullptr); |
| EXPECT_EQ(0u, header->num_transform_feedback_varyings); |
| EXPECT_EQ(static_cast<uint32_t>(GL_INTERLEAVED_ATTRIBS), |
| header->transform_feedback_buffer_mode); |
| // Zero transform feedback blocks. |
| EXPECT_CALL(*(gl_.get()), |
| GetProgramiv(kServiceProgramId, |
| GL_TRANSFORM_FEEDBACK_BUFFER_MODE, |
| _)) |
| .WillOnce(SetArgPointee<2>(GL_SEPARATE_ATTRIBS)) |
| .RetiresOnSaturation(); |
| EXPECT_CALL(*(gl_.get()), |
| GetProgramiv(kServiceProgramId, GL_LINK_STATUS, _)) |
| .WillOnce(SetArgPointee<2>(GL_TRUE)) |
| .RetiresOnSaturation(); |
| EXPECT_CALL(*(gl_.get()), |
| GetProgramiv( |
| kServiceProgramId, GL_TRANSFORM_FEEDBACK_VARYINGS, _)) |
| .WillOnce(SetArgPointee<2>(0)) |
| .RetiresOnSaturation(); |
| EXPECT_TRUE(program->GetTransformFeedbackVaryings(&bucket)); |
| EXPECT_EQ(sizeof(TransformFeedbackVaryingsHeader), bucket.size()); |
| header = bucket.GetDataAs<TransformFeedbackVaryingsHeader*>( |
| 0, sizeof(TransformFeedbackVaryingsHeader)); |
| EXPECT_TRUE(header != nullptr); |
| EXPECT_EQ(static_cast<uint32_t>(GL_SEPARATE_ATTRIBS), |
| header->transform_feedback_buffer_mode); |
| EXPECT_EQ(0u, header->num_transform_feedback_varyings); |
| } |
| |
| TEST_F(ProgramManagerWithShaderTest, |
| ProgramInfoGetTransformFeedbackVaryingsValid) { |
| CommonDecoder::Bucket bucket; |
| const Program* program = SetupDefaultProgram(); |
| ASSERT_TRUE(program != nullptr); |
| struct Data { |
| TransformFeedbackVaryingsHeader header; |
| TransformFeedbackVaryingInfo entry[2]; |
| char name0[4]; |
| char name1[8]; |
| }; |
| 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" }; |
| data.header.transform_feedback_buffer_mode = GL_INTERLEAVED_ATTRIBS; |
| data.header.num_transform_feedback_varyings = 2; |
| data.entry[0].size = 1; |
| data.entry[0].type = GL_FLOAT_VEC2; |
| data.entry[0].name_offset = ComputeOffset(&data, data.name0); |
| data.entry[0].name_length = base::size(data.name0); |
| data.entry[1].size = 2; |
| data.entry[1].type = GL_FLOAT; |
| data.entry[1].name_offset = ComputeOffset(&data, data.name1); |
| data.entry[1].name_length = base::size(data.name1); |
| memcpy(data.name0, kName[0], base::size(data.name0)); |
| memcpy(data.name1, kName[1], base::size(data.name1)); |
| |
| EXPECT_CALL(*(gl_.get()), |
| GetProgramiv(kServiceProgramId, |
| GL_TRANSFORM_FEEDBACK_BUFFER_MODE, |
| _)) |
| .WillOnce(SetArgPointee<2>(GL_INTERLEAVED_ATTRIBS)) |
| .RetiresOnSaturation(); |
| EXPECT_CALL(*(gl_.get()), |
| GetProgramiv(kServiceProgramId, GL_LINK_STATUS, _)) |
| .WillOnce(SetArgPointee<2>(GL_TRUE)) |
| .RetiresOnSaturation(); |
| EXPECT_CALL(*(gl_.get()), |
| GetProgramiv( |
| kServiceProgramId, GL_TRANSFORM_FEEDBACK_VARYINGS, _)) |
| .WillOnce(SetArgPointee<2>(data.header.num_transform_feedback_varyings)) |
| .RetiresOnSaturation(); |
| GLsizei max_length = 1 + std::max(strlen(kName[0]), strlen(kName[1])); |
| EXPECT_CALL(*(gl_.get()), |
| GetProgramiv(kServiceProgramId, |
| GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH, _)) |
| .WillOnce(SetArgPointee<2>(max_length)) |
| .RetiresOnSaturation(); |
| for (uint32_t ii = 0; ii < data.header.num_transform_feedback_varyings; |
| ++ii) { |
| EXPECT_CALL(*(gl_.get()), |
| GetTransformFeedbackVarying( |
| kServiceProgramId, ii, max_length, _, _, _, _)) |
| .WillOnce(DoAll( |
| SetArgPointee<3>(data.entry[ii].name_length - 1), |
| SetArgPointee<4>(data.entry[ii].size), |
| SetArgPointee<5>(data.entry[ii].type), |
| SetArrayArgument<6>( |
| kName[ii], kName[ii] + data.entry[ii].name_length))) |
| .RetiresOnSaturation(); |
| } |
| program->GetTransformFeedbackVaryings(&bucket); |
| EXPECT_EQ(sizeof(Data), bucket.size()); |
| Data* bucket_data = bucket.GetDataAs<Data*>(0, sizeof(Data)); |
| EXPECT_TRUE(bucket_data != nullptr); |
| EXPECT_EQ(0, memcmp(&data, bucket_data, sizeof(Data))); |
| } |
| |
| TEST_F(ProgramManagerWithShaderTest, ProgramInfoGetUniformsES3None) { |
| CommonDecoder::Bucket bucket; |
| const Program* program = SetupDefaultProgram(); |
| ASSERT_TRUE(program != nullptr); |
| // The program's previous link failed. |
| EXPECT_CALL(*(gl_.get()), |
| GetProgramiv(kServiceProgramId, GL_LINK_STATUS, _)) |
| .WillOnce(SetArgPointee<2>(GL_FALSE)) |
| .RetiresOnSaturation(); |
| EXPECT_TRUE(program->GetUniformsES3(&bucket)); |
| EXPECT_EQ(sizeof(UniformsES3Header), bucket.size()); |
| UniformsES3Header* header = |
| bucket.GetDataAs<UniformsES3Header*>(0, sizeof(UniformsES3Header)); |
| EXPECT_TRUE(header != nullptr); |
| EXPECT_EQ(0u, header->num_uniforms); |
| // 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_UNIFORMS, _)) |
| .WillOnce(SetArgPointee<2>(0)) |
| .RetiresOnSaturation(); |
| EXPECT_TRUE(program->GetUniformsES3(&bucket)); |
| EXPECT_EQ(sizeof(UniformsES3Header), bucket.size()); |
| header = |
| bucket.GetDataAs<UniformsES3Header*>(0, sizeof(UniformsES3Header)); |
| EXPECT_TRUE(header != nullptr); |
| EXPECT_EQ(0u, header->num_uniforms); |
| } |
| |
| TEST_F(ProgramManagerWithShaderTest, ProgramInfoGetUniformsES3Valid) { |
| CommonDecoder::Bucket bucket; |
| const Program* program = SetupDefaultProgram(); |
| ASSERT_TRUE(program != nullptr); |
| struct Data { |
| UniformsES3Header header; |
| UniformES3Info entry[2]; |
| }; |
| Data data; |
| const GLint kBlockIndex[] = { -1, 2 }; |
| const GLint kOffset[] = { 3, 4 }; |
| const GLint kArrayStride[] = { 7, 8 }; |
| const GLint kMatrixStride[] = { 9, 10 }; |
| const GLint kIsRowMajor[] = { 0, 1 }; |
| data.header.num_uniforms = 2; |
| for (uint32_t ii = 0; ii < data.header.num_uniforms; ++ii) { |
| data.entry[ii].block_index = kBlockIndex[ii]; |
| data.entry[ii].offset = kOffset[ii]; |
| data.entry[ii].array_stride = kArrayStride[ii]; |
| data.entry[ii].matrix_stride = kMatrixStride[ii]; |
| data.entry[ii].is_row_major = kIsRowMajor[ii]; |
| } |
| |
| EXPECT_CALL(*(gl_.get()), |
| GetProgramiv(kServiceProgramId, GL_LINK_STATUS, _)) |
| .WillOnce(SetArgPointee<2>(GL_TRUE)) |
| .RetiresOnSaturation(); |
| EXPECT_CALL(*(gl_.get()), |
| GetProgramiv(kServiceProgramId, GL_ACTIVE_UNIFORMS, _)) |
| .WillOnce(SetArgPointee<2>(data.header.num_uniforms)) |
| .RetiresOnSaturation(); |
| |
| const GLenum kPname[] = { |
| GL_UNIFORM_BLOCK_INDEX, |
| GL_UNIFORM_OFFSET, |
| GL_UNIFORM_ARRAY_STRIDE, |
| GL_UNIFORM_MATRIX_STRIDE, |
| GL_UNIFORM_IS_ROW_MAJOR, |
| }; |
| const GLint* kParams[] = { |
| kBlockIndex, |
| kOffset, |
| kArrayStride, |
| kMatrixStride, |
| kIsRowMajor, |
| }; |
| const size_t kNumIterations = base::size(kPname); |
| for (size_t ii = 0; ii < kNumIterations; ++ii) { |
| EXPECT_CALL(*(gl_.get()), |
| GetActiveUniformsiv( |
| kServiceProgramId, data.header.num_uniforms, _, |
| kPname[ii], _)) |
| .WillOnce(SetArrayArgument<4>( |
| kParams[ii], kParams[ii] + data.header.num_uniforms)) |
| .RetiresOnSaturation(); |
| } |
| |
| program->GetUniformsES3(&bucket); |
| EXPECT_EQ(sizeof(Data), bucket.size()); |
| Data* bucket_data = bucket.GetDataAs<Data*>(0, sizeof(Data)); |
| EXPECT_TRUE(bucket_data != nullptr); |
| 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) { |
| CommonDecoder::Bucket bucket; |
| const Program* program = SetupDefaultProgram(); |
| ASSERT_TRUE(program != nullptr); |
| // Emulate the situation that only the first element has a valid location. |
| // TODO(zmo): Don't assume these are in order. |
| for (size_t ii = 0; ii < base::size(kUniforms); ++ii) { |
| Program::UniformInfo* uniform = const_cast<Program::UniformInfo*>( |
| program->GetUniformInfo(ii)); |
| ASSERT_TRUE(uniform != nullptr); |
| EXPECT_EQ(static_cast<size_t>(kUniforms[ii].size), |
| uniform->element_locations.size()); |
| for (GLsizei jj = 1; jj < uniform->size; ++jj) |
| uniform->element_locations[jj] = -1; |
| } |
| program->GetProgramInfo(manager_.get(), &bucket); |
| ProgramInfoHeader* header = |
| bucket.GetDataAs<ProgramInfoHeader*>(0, sizeof(ProgramInfoHeader)); |
| ASSERT_TRUE(header != nullptr); |
| EXPECT_EQ(1u, header->link_status); |
| EXPECT_EQ(base::size(kAttribs), header->num_attribs); |
| EXPECT_EQ(base::size(kUniforms), header->num_uniforms); |
| const ProgramInput* inputs = bucket.GetDataAs<const ProgramInput*>( |
| sizeof(*header), |
| sizeof(ProgramInput) * (header->num_attribs + header->num_uniforms)); |
| ASSERT_TRUE(inputs != nullptr); |
| const ProgramInput* input = inputs + header->num_attribs; |
| for (uint32_t ii = 0; ii < header->num_uniforms; ++ii) { |
| const UniformInfo& expected = kUniforms[ii]; |
| EXPECT_EQ(expected.size, input->size); |
| const int32_t* locations = bucket.GetDataAs<const int32_t*>( |
| input->location_offset, sizeof(int32_t) * input->size); |
| ASSERT_TRUE(locations != nullptr); |
| EXPECT_EQ( |
| ProgramManager::MakeFakeLocation(expected.fake_location, 0), |
| locations[0]); |
| for (int32_t jj = 1; jj < input->size; ++jj) |
| EXPECT_EQ(-1, locations[jj]); |
| ++input; |
| } |
| } |
| |
| TEST_F(ProgramManagerWithShaderTest, BindAttribLocationConflicts) { |
| // Set up shader |
| AttributeMap attrib_map; |
| for (uint32_t ii = 0; ii < kNumAttribs; ++ii) { |
| attrib_map[kAttribs[ii].name] = TestHelper::ConstructAttribute( |
| kAttribs[ii].type, |
| kAttribs[ii].size, |
| GL_MEDIUM_FLOAT, |
| kAttribStaticUse, |
| kAttribs[ii].name); |
| } |
| const char kAttribMatName[] = "matAttrib"; |
| attrib_map[kAttribMatName] = TestHelper::ConstructAttribute( |
| GL_FLOAT_MAT2, |
| 1, |
| GL_MEDIUM_FLOAT, |
| kAttribStaticUse, |
| kAttribMatName); |
| // Check we can create shader. |
| Shader* vshader = shader_manager_.CreateShader( |
| kVertexShaderClientId, kVertexShaderServiceId, GL_VERTEX_SHADER); |
| Shader* fshader = shader_manager_.CreateShader( |
| kFragmentShaderClientId, kFragmentShaderServiceId, GL_FRAGMENT_SHADER); |
| // Check shader got created. |
| ASSERT_TRUE(vshader != nullptr && fshader != nullptr); |
| // Set Status |
| TestHelper::SetShaderStates(gl_.get(), vshader, true, nullptr, nullptr, |
| nullptr, &attrib_map, nullptr, nullptr, nullptr, |
| nullptr, nullptr); |
| // Check attrib infos got copied. |
| for (AttributeMap::const_iterator it = attrib_map.begin(); |
| it != attrib_map.end(); ++it) { |
| const sh::Attribute* variable_info = |
| vshader->GetAttribInfo(it->first); |
| ASSERT_TRUE(variable_info != nullptr); |
| EXPECT_EQ(it->second.type, variable_info->type); |
| EXPECT_EQ(it->second.getOutermostArraySize(), |
| variable_info->getOutermostArraySize()); |
| EXPECT_EQ(it->second.precision, variable_info->precision); |
| EXPECT_EQ(it->second.staticUse, variable_info->staticUse); |
| EXPECT_EQ(it->second.name, variable_info->name); |
| } |
| TestHelper::SetShaderStates(gl_.get(), fshader, true, nullptr, nullptr, |
| nullptr, &attrib_map, nullptr, nullptr, nullptr, |
| nullptr, nullptr); |
| // Set up program |
| Program* program = |
| manager_->CreateProgram(kClientProgramId, kServiceProgramId); |
| ASSERT_TRUE(program != nullptr); |
| EXPECT_TRUE(program->AttachShader(&shader_manager_, vshader)); |
| EXPECT_TRUE(program->AttachShader(&shader_manager_, fshader)); |
| |
| EXPECT_FALSE(program->DetectAttribLocationBindingConflicts()); |
| EXPECT_TRUE(LinkAsExpected(program, true)); |
| |
| program->SetAttribLocationBinding(kAttrib1Name, 0); |
| EXPECT_FALSE(program->DetectAttribLocationBindingConflicts()); |
| EXPECT_CALL(*(gl_.get()), BindAttribLocation(_, 0, _)) |
| .Times(1) |
| .RetiresOnSaturation(); |
| EXPECT_TRUE(LinkAsExpected(program, true)); |
| |
| program->SetAttribLocationBinding("xxx", 0); |
| EXPECT_FALSE(program->DetectAttribLocationBindingConflicts()); |
| EXPECT_CALL(*(gl_.get()), BindAttribLocation(_, 0, _)) |
| .Times(1) |
| .RetiresOnSaturation(); |
| EXPECT_TRUE(LinkAsExpected(program, true)); |
| |
| program->SetAttribLocationBinding(kAttrib2Name, 1); |
| EXPECT_FALSE(program->DetectAttribLocationBindingConflicts()); |
| EXPECT_CALL(*(gl_.get()), BindAttribLocation(_, _, _)) |
| .Times(2) |
| .RetiresOnSaturation(); |
| EXPECT_TRUE(LinkAsExpected(program, true)); |
| |
| program->SetAttribLocationBinding(kAttrib2Name, 0); |
| EXPECT_TRUE(program->DetectAttribLocationBindingConflicts()); |
| EXPECT_TRUE(LinkAsExpected(program, false)); |
| |
| program->SetAttribLocationBinding(kAttribMatName, 1); |
| program->SetAttribLocationBinding(kAttrib2Name, 3); |
| EXPECT_CALL(*(gl_.get()), BindAttribLocation(_, _, _)) |
| .Times(3) |
| .RetiresOnSaturation(); |
| EXPECT_FALSE(program->DetectAttribLocationBindingConflicts()); |
| EXPECT_TRUE(LinkAsExpected(program, true)); |
| |
| program->SetAttribLocationBinding(kAttrib2Name, 2); |
| EXPECT_TRUE(program->DetectAttribLocationBindingConflicts()); |
| EXPECT_TRUE(LinkAsExpected(program, false)); |
| } |
| |
| TEST_F(ProgramManagerWithShaderTest, UniformsPrecisionMismatch) { |
| // Set up shader |
| UniformMap vertex_uniform_map; |
| vertex_uniform_map["a"] = TestHelper::ConstructUniform( |
| GL_FLOAT, 3, GL_MEDIUM_FLOAT, true, "a"); |
| UniformMap frag_uniform_map; |
| frag_uniform_map["a"] = TestHelper::ConstructUniform( |
| GL_FLOAT, 3, GL_LOW_FLOAT, true, "a"); |
| |
| // Check we can create shader. |
| Shader* vshader = shader_manager_.CreateShader( |
| kVertexShaderClientId, kVertexShaderServiceId, GL_VERTEX_SHADER); |
| Shader* fshader = shader_manager_.CreateShader( |
| kFragmentShaderClientId, kFragmentShaderServiceId, GL_FRAGMENT_SHADER); |
| // Check shader got created. |
| ASSERT_TRUE(vshader != nullptr && fshader != nullptr); |
| // Set Status |
| TestHelper::SetShaderStates(gl_.get(), vshader, true, nullptr, nullptr, |
| nullptr, nullptr, &vertex_uniform_map, nullptr, |
| nullptr, nullptr, nullptr); |
| TestHelper::SetShaderStates(gl_.get(), fshader, true, nullptr, nullptr, |
| nullptr, nullptr, &frag_uniform_map, nullptr, |
| nullptr, nullptr, nullptr); |
| // Set up program |
| Program* program = |
| manager_->CreateProgram(kClientProgramId, kServiceProgramId); |
| ASSERT_TRUE(program != nullptr); |
| EXPECT_TRUE(program->AttachShader(&shader_manager_, vshader)); |
| EXPECT_TRUE(program->AttachShader(&shader_manager_, fshader)); |
| |
| std::string conflicting_name; |
| |
| EXPECT_TRUE(program->DetectUniformsMismatch(&conflicting_name)); |
| EXPECT_EQ("a", conflicting_name); |
| EXPECT_TRUE(LinkAsExpected(program, false)); |
| } |
| |
| // If a varying has different type in the vertex and fragment |
| // shader, linking should fail. |
| TEST_F(ProgramManagerWithShaderTest, VaryingTypeMismatch) { |
| const VarInfo kVertexVarying = |
| { GL_FLOAT_VEC3, 1, GL_MEDIUM_FLOAT, true, "a", kVarVarying }; |
| const VarInfo kFragmentVarying = |
| { GL_FLOAT_VEC4, 1, GL_MEDIUM_FLOAT, true, "a", kVarVarying }; |
| Program* program = |
| SetupProgramForVariables(&kVertexVarying, 1, &kFragmentVarying, 1); |
| |
| std::string conflicting_name; |
| |
| EXPECT_TRUE(program->DetectVaryingsMismatch(&conflicting_name)); |
| EXPECT_EQ("a", conflicting_name); |
| EXPECT_TRUE(LinkAsExpected(program, false)); |
| } |
| |
| // If a varying has different array size in the vertex and fragment |
| // shader, linking should fail. |
| TEST_F(ProgramManagerWithShaderTest, VaryingArraySizeMismatch) { |
| const VarInfo kVertexVarying = |
| { GL_FLOAT, 2, GL_MEDIUM_FLOAT, true, "a", kVarVarying }; |
| const VarInfo kFragmentVarying = |
| { GL_FLOAT, 3, GL_MEDIUM_FLOAT, true, "a", kVarVarying }; |
| Program* program = |
| SetupProgramForVariables(&kVertexVarying, 1, &kFragmentVarying, 1); |
| |
| std::string conflicting_name; |
| |
| EXPECT_TRUE(program->DetectVaryingsMismatch(&conflicting_name)); |
| EXPECT_EQ("a", conflicting_name); |
| EXPECT_TRUE(LinkAsExpected(program, false)); |
| } |
| |
| // If a varying has different precision in the vertex and fragment |
| // shader, linking should succeed. |
| TEST_F(ProgramManagerWithShaderTest, VaryingPrecisionMismatch) { |
| const VarInfo kVertexVarying = |
| { GL_FLOAT, 2, GL_HIGH_FLOAT, true, "a", kVarVarying }; |
| const VarInfo kFragmentVarying = |
| { GL_FLOAT, 2, GL_MEDIUM_FLOAT, true, "a", kVarVarying }; |
| Program* program = |
| SetupProgramForVariables(&kVertexVarying, 1, &kFragmentVarying, 1); |
| |
| std::string conflicting_name; |
| |
| EXPECT_FALSE(program->DetectVaryingsMismatch(&conflicting_name)); |
| EXPECT_TRUE(conflicting_name.empty()); |
| EXPECT_TRUE(LinkAsExpected(program, true)); |
| } |
| |
| // If a varying is statically used in fragment shader but not |
| // declared in vertex shader, link should fail. |
| TEST_F(ProgramManagerWithShaderTest, VaryingMissing) { |
| const VarInfo kFragmentVarying = |
| { GL_FLOAT, 3, GL_MEDIUM_FLOAT, true, "a", kVarVarying }; |
| Program* program = SetupProgramForVariables(nullptr, 0, &kFragmentVarying, 1); |
| |
| std::string conflicting_name; |
| |
| EXPECT_TRUE(program->DetectVaryingsMismatch(&conflicting_name)); |
| EXPECT_EQ("a", conflicting_name); |
| EXPECT_TRUE(LinkAsExpected(program, false)); |
| } |
| |
| // If a varying is declared but not statically used in fragment |
| // shader, even if it's not declared in vertex shader, link should |
| // succeed. |
| TEST_F(ProgramManagerWithShaderTest, InactiveVarying) { |
| const VarInfo kFragmentVarying = |
| { GL_FLOAT, 3, GL_MEDIUM_FLOAT, false, "a", kVarVarying }; |
| Program* program = SetupProgramForVariables(nullptr, 0, &kFragmentVarying, 1); |
| |
| std::string conflicting_name; |
| |
| EXPECT_FALSE(program->DetectVaryingsMismatch(&conflicting_name)); |
| EXPECT_TRUE(conflicting_name.empty()); |
| EXPECT_TRUE(LinkAsExpected(program, true)); |
| } |
| |
| // Uniforms and attributes are both global variables, thus sharing |
| // the same namespace. Any name conflicts should cause link |
| // failure. |
| TEST_F(ProgramManagerWithShaderTest, AttribUniformNameConflict) { |
| const VarInfo kVertexAttribute = |
| { GL_FLOAT_VEC4, 1, GL_MEDIUM_FLOAT, true, "a", kVarAttribute }; |
| const VarInfo kFragmentUniform = |
| { GL_FLOAT_VEC4, 1, GL_MEDIUM_FLOAT, true, "a", kVarUniform }; |
| Program* program = |
| SetupProgramForVariables(&kVertexAttribute, 1, &kFragmentUniform, 1); |
| |
| std::string conflicting_name; |
| |
| EXPECT_TRUE(program->DetectGlobalNameConflicts(&conflicting_name)); |
| EXPECT_EQ("a", conflicting_name); |
| EXPECT_TRUE(LinkAsExpected(program, false)); |
| } |
| |
| TEST_F(ProgramManagerWithShaderTest, FragmentOutputTypes) { |
| // Set up program |
| Shader* vshader = shader_manager_.CreateShader( |
| kVertexShaderClientId, kVertexShaderServiceId, GL_VERTEX_SHADER); |
| TestHelper::SetShaderStates(gl_.get(), vshader, true, nullptr, nullptr, |
| nullptr, nullptr, nullptr, nullptr, nullptr, |
| nullptr, nullptr); |
| Shader* fshader = shader_manager_.CreateShader( |
| kFragmentShaderClientId, kFragmentShaderServiceId, GL_FRAGMENT_SHADER); |
| ASSERT_TRUE(vshader && fshader); |
| Program* program = |
| manager_->CreateProgram(kClientProgramId, kServiceProgramId); |
| ASSERT_TRUE(program); |
| EXPECT_TRUE(program->AttachShader(&shader_manager_, vshader)); |
| EXPECT_TRUE(program->AttachShader(&shader_manager_, fshader)); |
| |
| { // No outputs. |
| TestHelper::SetShaderStates(gl_.get(), fshader, true, nullptr, nullptr, |
| nullptr, nullptr, nullptr, nullptr, nullptr, |
| nullptr, nullptr); |
| EXPECT_TRUE(LinkAsExpected(program, true)); |
| EXPECT_EQ(0u, program->fragment_output_type_mask()); |
| EXPECT_EQ(0u, program->fragment_output_written_mask()); |
| } |
| |
| { // gl_FragColor |
| OutputVariableList fragment_outputs; |
| sh::OutputVariable var = TestHelper::ConstructOutputVariable( |
| GL_FLOAT_VEC4, 0, GL_MEDIUM_FLOAT, true, "gl_FragColor"); |
| var.location = -1; |
| fragment_outputs.push_back(var); |
| TestHelper::SetShaderStates(gl_.get(), fshader, true, nullptr, nullptr, |
| nullptr, nullptr, nullptr, nullptr, nullptr, |
| &fragment_outputs, nullptr); |
| EXPECT_TRUE(LinkAsExpected(program, true)); |
| EXPECT_EQ(0x3u, program->fragment_output_type_mask()); |
| EXPECT_EQ(0x3u, program->fragment_output_written_mask()); |
| } |
| |
| { // gl_FragData |
| OutputVariableList fragment_outputs; |
| sh::OutputVariable var = TestHelper::ConstructOutputVariable( |
| GL_FLOAT_VEC4, 8, GL_MEDIUM_FLOAT, true, "gl_FragData"); |
| var.location = -1; |
| fragment_outputs.push_back(var); |
| TestHelper::SetShaderStates(gl_.get(), fshader, true, nullptr, nullptr, |
| nullptr, nullptr, nullptr, nullptr, nullptr, |
| &fragment_outputs, nullptr); |
| EXPECT_TRUE(LinkAsExpected(program, true)); |
| EXPECT_EQ(0xFFFFu, program->fragment_output_type_mask()); |
| EXPECT_EQ(0xFFFFu, program->fragment_output_written_mask()); |
| } |
| |
| { // gl_FragColor, gl_FragDepth |
| OutputVariableList fragment_outputs; |
| sh::OutputVariable var = TestHelper::ConstructOutputVariable( |
| GL_FLOAT_VEC4, 0, GL_MEDIUM_FLOAT, true, "gl_FragColor"); |
| var.location = -1; |
| fragment_outputs.push_back(var); |
| var = TestHelper::ConstructOutputVariable( |
| GL_FLOAT, 0, GL_MEDIUM_FLOAT, true, "gl_FragDepth"); |
| var.location = -1; |
| fragment_outputs.push_back(var); |
| TestHelper::SetShaderStates(gl_.get(), fshader, true, nullptr, nullptr, |
| nullptr, nullptr, nullptr, nullptr, nullptr, |
| &fragment_outputs, nullptr); |
| EXPECT_TRUE(LinkAsExpected(program, true)); |
| EXPECT_EQ(0x3u, program->fragment_output_type_mask()); |
| EXPECT_EQ(0x3u, program->fragment_output_written_mask()); |
| } |
| |
| { // Single user defined output. |
| OutputVariableList fragment_outputs; |
| sh::OutputVariable var = TestHelper::ConstructOutputVariable( |
| GL_UNSIGNED_INT_VEC4, 0, GL_MEDIUM_INT, true, "myOutput"); |
| var.location = -1; |
| fragment_outputs.push_back(var); |
| TestHelper::SetShaderStates(gl_.get(), fshader, true, nullptr, nullptr, |
| nullptr, nullptr, nullptr, nullptr, nullptr, |
| &fragment_outputs, nullptr); |
| EXPECT_TRUE(LinkAsExpected(program, true)); |
| EXPECT_EQ(0x2u, program->fragment_output_type_mask()); |
| EXPECT_EQ(0x3u, program->fragment_output_written_mask()); |
| } |
| |
| { // Single user defined output - no static use. |
| OutputVariableList fragment_outputs; |
| sh::OutputVariable var = TestHelper::ConstructOutputVariable( |
| GL_UNSIGNED_INT_VEC4, 0, GL_MEDIUM_INT, false, "myOutput"); |
| var.location = -1; |
| fragment_outputs.push_back(var); |
| TestHelper::SetShaderStates(gl_.get(), fshader, true, nullptr, nullptr, |
| nullptr, nullptr, nullptr, nullptr, nullptr, |
| &fragment_outputs, nullptr); |
| EXPECT_TRUE(LinkAsExpected(program, true)); |
| EXPECT_EQ(0x2u, program->fragment_output_type_mask()); |
| EXPECT_EQ(0x3u, program->fragment_output_written_mask()); |
| } |
| |
| { // Multiple user defined outputs. |
| OutputVariableList fragment_outputs; |
| sh::OutputVariable var = TestHelper::ConstructOutputVariable( |
| GL_INT_VEC4, 0, GL_MEDIUM_INT, true, "myOutput"); |
| var.location = 0; |
| fragment_outputs.push_back(var); |
| var = TestHelper::ConstructOutputVariable( |
| GL_FLOAT_VEC4, 2, GL_MEDIUM_FLOAT, true, "myOutputArray"); |
| var.location = 2; |
| fragment_outputs.push_back(var); |
| TestHelper::SetShaderStates(gl_.get(), fshader, true, nullptr, nullptr, |
| nullptr, nullptr, nullptr, nullptr, nullptr, |
| &fragment_outputs, nullptr); |
| EXPECT_TRUE(LinkAsExpected(program, true)); |
| EXPECT_EQ(0xF1u, program->fragment_output_type_mask()); |
| EXPECT_EQ(0xF3u, program->fragment_output_written_mask()); |
| } |
| } |
| |
| // Varyings go over 8 rows. |
| TEST_F(ProgramManagerWithShaderTest, TooManyVaryings) { |
| const VarInfo kVertexVaryings[] = { |
| { GL_FLOAT_VEC4, 4, GL_MEDIUM_FLOAT, true, "a", kVarVarying }, |
| { GL_FLOAT_VEC4, 5, GL_MEDIUM_FLOAT, true, "b", kVarVarying } |
| }; |
| const VarInfo kFragmentVaryings[] = { |
| { GL_FLOAT_VEC4, 4, GL_MEDIUM_FLOAT, true, "a", kVarVarying }, |
| { GL_FLOAT_VEC4, 5, GL_MEDIUM_FLOAT, true, "b", kVarVarying } |
| }; |
| Program* program = |
| SetupProgramForVariables(kVertexVaryings, 2, kFragmentVaryings, 2); |
| |
| EXPECT_FALSE( |
| program->CheckVaryingsPacking(Program::kCountOnlyStaticallyUsed)); |
| EXPECT_TRUE(LinkAsExpected(program, false)); |
| } |
| |
| // Varyings go over 8 rows but some are inactive |
| TEST_F(ProgramManagerWithShaderTest, TooManyInactiveVaryings) { |
| const VarInfo kVertexVaryings[] = { |
| { GL_FLOAT_VEC4, 4, GL_MEDIUM_FLOAT, true, "a", kVarVarying }, |
| { GL_FLOAT_VEC4, 5, GL_MEDIUM_FLOAT, true, "b", kVarVarying } |
| }; |
| const VarInfo kFragmentVaryings[] = { |
| { GL_FLOAT_VEC4, 4, GL_MEDIUM_FLOAT, false, "a", kVarVarying }, |
| { GL_FLOAT_VEC4, 5, GL_MEDIUM_FLOAT, true, "b", kVarVarying } |
| }; |
| Program* program = |
| SetupProgramForVariables(kVertexVaryings, 2, kFragmentVaryings, 2); |
| |
| EXPECT_TRUE( |
| program->CheckVaryingsPacking(Program::kCountOnlyStaticallyUsed)); |
| EXPECT_TRUE(LinkAsExpected(program, true)); |
| } |
| |
| // Varyings go over 8 rows but some are inactive. |
| // However, we still fail the check if kCountAll option is used. |
| TEST_F(ProgramManagerWithShaderTest, CountAllVaryingsInPacking) { |
| const VarInfo kVertexVaryings[] = { |
| { GL_FLOAT_VEC4, 4, GL_MEDIUM_FLOAT, true, "a", kVarVarying }, |
| { GL_FLOAT_VEC4, 5, GL_MEDIUM_FLOAT, true, "b", kVarVarying } |
| }; |
| const VarInfo kFragmentVaryings[] = { |
| { GL_FLOAT_VEC4, 4, GL_MEDIUM_FLOAT, false, "a", kVarVarying }, |
| { GL_FLOAT_VEC4, 5, GL_MEDIUM_FLOAT, true, "b", kVarVarying } |
| }; |
| Program* program = |
| SetupProgramForVariables(kVertexVaryings, 2, kFragmentVaryings, 2); |
| |
| EXPECT_FALSE(program->CheckVaryingsPacking(Program::kCountAll)); |
| } |
| |
| TEST_F(ProgramManagerWithShaderTest, ClearWithSamplerTypes) { |
| Shader* vshader = shader_manager_.CreateShader( |
| kVertexShaderClientId, kVertexShaderServiceId, GL_VERTEX_SHADER); |
| ASSERT_TRUE(vshader != nullptr); |
| TestHelper::SetShaderStates(gl_.get(), vshader, true); |
| Shader* fshader = shader_manager_.CreateShader( |
| kFragmentShaderClientId, kFragmentShaderServiceId, GL_FRAGMENT_SHADER); |
| ASSERT_TRUE(fshader != nullptr); |
| TestHelper::SetShaderStates(gl_.get(), fshader, true); |
| Program* program = |
| manager_->CreateProgram(kClientProgramId, kServiceProgramId); |
| ASSERT_TRUE(program != nullptr); |
| EXPECT_TRUE(program->AttachShader(&shader_manager_, vshader)); |
| EXPECT_TRUE(program->AttachShader(&shader_manager_, fshader)); |
| |
| static const GLenum kSamplerTypes[] = { |
| GL_SAMPLER_2D, |
| GL_SAMPLER_CUBE, |
| GL_SAMPLER_EXTERNAL_OES, |
| GL_SAMPLER_3D_OES, |
| GL_SAMPLER_2D_RECT_ARB, |
| }; |
| const size_t kNumSamplerTypes = base::size(kSamplerTypes); |
| for (size_t ii = 0; ii < kNumSamplerTypes; ++ii) { |
| static ProgramManagerWithShaderTest::AttribInfo kAttribs[] = { |
| { kAttrib1Name, kAttrib1Size, kAttrib1Type, kAttrib1Location, }, |
| { kAttrib2Name, kAttrib2Size, kAttrib2Type, kAttrib2Location, }, |
| { kAttrib3Name, kAttrib3Size, kAttrib3Type, kAttrib3Location, }, |
| }; |
| ProgramManagerWithShaderTest::UniformInfo kUniforms[] = { |
| { kUniform1Name, |
| kUniform1Size, |
| kUniform1Type, |
| kUniform1FakeLocation, |
| kUniform1RealLocation, |
| kUniform1DesiredLocation, |
| kUniform1Name, |
| }, |
| { kUniform2Name, |
| kUniform2Size, |
| kSamplerTypes[ii], |
| kUniform2FakeLocation, |
| kUniform2RealLocation, |
| kUniform2DesiredLocation, |
| kUniform2NameWithArrayIndex, |
| }, |
| { kUniform3Name, |
| kUniform3Size, |
| kUniform3Type, |
| kUniform3FakeLocation, |
| kUniform3RealLocation, |
| kUniform3DesiredLocation, |
| kUniform3NameWithArrayIndex, |
| }, |
| }; |
| const size_t kNumAttribs = base::size(kAttribs); |
| const size_t kNumUniforms = base::size(kUniforms); |
| SetupShaderExpectations(kAttribs, kNumAttribs, kUniforms, kNumUniforms, |
| kServiceProgramId); |
| program->Link(nullptr, Program::kCountOnlyStaticallyUsed, this); |
| SetupExpectationsForClearingUniforms(kUniforms, kNumUniforms); |
| manager_->ClearUniforms(program); |
| } |
| } |
| |
| TEST_F(ProgramManagerWithShaderTest, BindUniformLocation) { |
| const GLint kUniform1DesiredLocation = 10; |
| const GLint kUniform2DesiredLocation = -1; |
| const GLint kUniform3DesiredLocation = 5; |
| |
| Shader* vshader = shader_manager_.CreateShader( |
| kVertexShaderClientId, kVertexShaderServiceId, GL_VERTEX_SHADER); |
| ASSERT_TRUE(vshader != nullptr); |
| TestHelper::SetShaderStates(gl_.get(), vshader, true); |
| Shader* fshader = shader_manager_.CreateShader( |
| kFragmentShaderClientId, kFragmentShaderServiceId, GL_FRAGMENT_SHADER); |
| ASSERT_TRUE(fshader != nullptr); |
| TestHelper::SetShaderStates(gl_.get(), fshader, true); |
| Program* program = |
| manager_->CreateProgram(kClientProgramId, kServiceProgramId); |
| ASSERT_TRUE(program != nullptr); |
| EXPECT_TRUE(program->AttachShader(&shader_manager_, vshader)); |
| EXPECT_TRUE(program->AttachShader(&shader_manager_, fshader)); |
| EXPECT_TRUE(program->SetUniformLocationBinding( |
| kUniform1Name, kUniform1DesiredLocation)); |
| EXPECT_TRUE(program->SetUniformLocationBinding( |
| kUniform3Name, kUniform3DesiredLocation)); |
| |
| static ProgramManagerWithShaderTest::AttribInfo kAttribs[] = { |
| { kAttrib1Name, kAttrib1Size, kAttrib1Type, kAttrib1Location, }, |
| { kAttrib2Name, kAttrib2Size, kAttrib2Type, kAttrib2Location, }, |
| { kAttrib3Name, kAttrib3Size, kAttrib3Type, kAttrib3Location, }, |
| }; |
| ProgramManagerWithShaderTest::UniformInfo kUniforms[] = { |
| { kUniform1Name, |
| kUniform1Size, |
| kUniform1Type, |
| kUniform1FakeLocation, |
| kUniform1RealLocation, |
| kUniform1DesiredLocation, |
| kUniform1Name, |
| }, |
| { kUniform2Name, |
| kUniform2Size, |
| kUniform2Type, |
| kUniform2FakeLocation, |
| kUniform2RealLocation, |
| kUniform2DesiredLocation, |
| kUniform2NameWithArrayIndex, |
| }, |
| { kUniform3Name, |
| kUniform3Size, |
| kUniform3Type, |
| kUniform3FakeLocation, |
| kUniform3RealLocation, |
| kUniform3DesiredLocation, |
| kUniform3NameWithArrayIndex, |
| }, |
| }; |
| |
| const size_t kNumAttribs = base::size(kAttribs); |
| const size_t kNumUniforms = base::size(kUniforms); |
| SetupShaderExpectations(kAttribs, kNumAttribs, kUniforms, kNumUniforms, |
| kServiceProgramId); |
| program->Link(nullptr, Program::kCountOnlyStaticallyUsed, this); |
| |
| EXPECT_EQ(kUniform1DesiredLocation, |
| program->GetUniformFakeLocation(kUniform1Name)); |
| EXPECT_EQ(kUniform3DesiredLocation, |
| program->GetUniformFakeLocation(kUniform3Name)); |
| EXPECT_EQ(kUniform3DesiredLocation, |
| program->GetUniformFakeLocation(kUniform3NameWithArrayIndex)); |
| } |
| |
| TEST_F(ProgramManagerWithShaderTest, ZeroSizeUniformMarkedInvalid) { |
| UniformInfo kInvalidUniforms[] = { |
| { |
| kUniform1Name, 0 /* invalid size */, kUniform1Type, |
| kUniform1FakeLocation, kUniform1RealLocation, |
| kUniform1DesiredLocation, kUniform1Name, |
| }, |
| }; |
| const size_t kNumInvalidUniforms = base::size(kInvalidUniforms); |
| |
| SetupShaderExpectations(kAttribs, kNumAttribs, kInvalidUniforms, |
| kNumInvalidUniforms, kServiceProgramId); |
| |
| Shader* vertex_shader = shader_manager_.CreateShader( |
| kVertexShaderClientId, kVertexShaderServiceId, GL_VERTEX_SHADER); |
| Shader* fragment_shader = shader_manager_.CreateShader( |
| kFragmentShaderClientId, kFragmentShaderServiceId, GL_FRAGMENT_SHADER); |
| EXPECT_TRUE(vertex_shader != nullptr); |
| EXPECT_TRUE(fragment_shader != nullptr); |
| TestHelper::SetShaderStates(gl_.get(), vertex_shader, true); |
| TestHelper::SetShaderStates(gl_.get(), fragment_shader, true); |
| |
| Program* program = |
| manager_->CreateProgram(kClientProgramId, kServiceProgramId); |
| ASSERT_TRUE(program != nullptr); |
| program->AttachShader(&shader_manager_, vertex_shader); |
| program->AttachShader(&shader_manager_, fragment_shader); |
| program->Link(nullptr, Program::kCountOnlyStaticallyUsed, this); |
| |
| EXPECT_FALSE(program->IsValid()); |
| } |
| |
| class ProgramManagerWithCacheTest : public ProgramManagerTestBase { |
| public: |
| static const GLuint kClientProgramId = 1; |
| static const GLuint kServiceProgramId = 10; |
| static const GLuint kVertexShaderClientId = 2; |
| static const GLuint kFragmentShaderClientId = 20; |
| static const GLuint kVertexShaderServiceId = 3; |
| static const GLuint kFragmentShaderServiceId = 30; |
| |
| ProgramManagerWithCacheTest() |
| : cache_(new MockProgramCache()), |
| vertex_shader_(nullptr), |
| fragment_shader_(nullptr), |
| program_(nullptr), |
| shader_manager_(nullptr) {} |
| |
| protected: |
| void SetupProgramManager() override { |
| manager_.reset( |
| new ProgramManager(cache_.get(), kMaxVaryingVectors, kMaxDrawBuffers, |
| kMaxDualSourceDrawBuffers, kMaxVertexAttribs, |
| gpu_preferences_, feature_info_.get(), nullptr)); |
| } |
| |
| void SetUp() override { |
| ProgramManagerTestBase::SetUp(); |
| |
| vertex_shader_ = shader_manager_.CreateShader( |
| kVertexShaderClientId, kVertexShaderServiceId, GL_VERTEX_SHADER); |
| fragment_shader_ = shader_manager_.CreateShader( |
| kFragmentShaderClientId, kFragmentShaderServiceId, GL_FRAGMENT_SHADER); |
| ASSERT_TRUE(vertex_shader_ != nullptr); |
| ASSERT_TRUE(fragment_shader_ != nullptr); |
| vertex_shader_->set_source("lka asjf bjajsdfj"); |
| fragment_shader_->set_source("lka asjf a fasgag 3rdsf3 bjajsdfj"); |
| |
| program_ = manager_->CreateProgram(kClientProgramId, kServiceProgramId); |
| ASSERT_TRUE(program_ != nullptr); |
| |
| program_->AttachShader(&shader_manager_, vertex_shader_); |
| program_->AttachShader(&shader_manager_, fragment_shader_); |
| } |
| |
| void TearDown() override { |
| shader_manager_.Destroy(false); |
| ProgramManagerTestBase::TearDown(); |
| } |
| |
| void SetShadersCompiled() { |
| TestHelper::SetShaderStates(gl_.get(), vertex_shader_, true); |
| TestHelper::SetShaderStates(gl_.get(), fragment_shader_, true); |
| } |
| |
| void SetShadersCompiled(const std::string& compilation_options_string) { |
| TestHelper::SetShaderStates(gl_.get(), vertex_shader_, true, |
| compilation_options_string); |
| TestHelper::SetShaderStates(gl_.get(), fragment_shader_, true, |
| compilation_options_string); |
| } |
| |
| void SetProgramCached() { |
| cache_->LinkedProgramCacheSuccess( |
| vertex_shader_->source(), fragment_shader_->source(), |
| &program_->bind_attrib_location_map(), |
| program_->effective_transform_feedback_varyings(), |
| program_->effective_transform_feedback_buffer_mode()); |
| } |
| |
| void SetExpectationsForProgramCached() { |
| SetExpectationsForProgramCached(program_, |
| vertex_shader_, |
| fragment_shader_); |
| } |
| |
| void SetExpectationsForProgramCached( |
| Program* program, |
| Shader* vertex_shader, |
| Shader* fragment_shader) { |
| EXPECT_CALL(*cache_.get(), |
| SaveLinkedProgram( |
| program->service_id(), vertex_shader, fragment_shader, |
| &program->bind_attrib_location_map(), |
| program_->effective_transform_feedback_varyings(), |
| program_->effective_transform_feedback_buffer_mode(), _)) |
| .Times(1); |
| } |
| |
| void SetExpectationsForNotCachingProgram() { |
| SetExpectationsForNotCachingProgram(program_, |
| vertex_shader_, |
| fragment_shader_); |
| } |
| |
| void SetExpectationsForNotCachingProgram( |
| Program* program, |
| Shader* vertex_shader, |
| Shader* fragment_shader) { |
| EXPECT_CALL(*cache_.get(), |
| SaveLinkedProgram( |
| program->service_id(), vertex_shader, fragment_shader, |
| &program->bind_attrib_location_map(), |
| program_->effective_transform_feedback_varyings(), |
| program_->effective_transform_feedback_buffer_mode(), _)) |
| .Times(0); |
| } |
| |
| void SetExpectationsForProgramLoad(ProgramCache::ProgramLoadResult result) { |
| SetExpectationsForProgramLoad(kServiceProgramId, |
| program_, |
| vertex_shader_, |
| fragment_shader_, |
| result); |
| } |
| |
| void SetExpectationsForProgramLoad( |
| GLuint service_program_id, |
| Program* program, |
| Shader* vertex_shader, |
| Shader* fragment_shader, |
| ProgramCache::ProgramLoadResult result) { |
| EXPECT_CALL(*cache_.get(), |
| LoadLinkedProgram( |
| service_program_id, vertex_shader, fragment_shader, |
| &program->bind_attrib_location_map(), |
| program_->effective_transform_feedback_varyings(), |
| program_->effective_transform_feedback_buffer_mode(), _)) |
| .WillOnce(Return(result)); |
| } |
| |
| void SetExpectationsForProgramLoadSuccess() { |
| SetExpectationsForProgramLoadSuccess(kServiceProgramId); |
| } |
| |
| void SetExpectationsForProgramLoadSuccess(GLuint service_program_id) { |
| TestHelper::SetupProgramSuccessExpectations( |
| gl_.get(), feature_info_.get(), nullptr, 0, nullptr, 0, nullptr, 0, |
| nullptr, 0, service_program_id); |
| } |
| |
| void SetExpectationsForProgramNotLoaded() { |
| EXPECT_CALL(*cache_.get(), |
| LoadLinkedProgram( |
| program_->service_id(), vertex_shader_, fragment_shader_, |
| &program_->bind_attrib_location_map(), |
| program_->effective_transform_feedback_varyings(), |
| program_->effective_transform_feedback_buffer_mode(), _)) |
| .Times(Exactly(0)); |
| } |
| |
| void SetExpectationsForProgramLink() { |
| SetExpectationsForProgramLink(kServiceProgramId); |
| } |
| |
| void SetExpectationsForProgramLink(GLuint service_program_id) { |
| TestHelper::SetupShaderExpectations(gl_.get(), feature_info_.get(), nullptr, |
| 0, nullptr, 0, service_program_id); |
| if (gl::g_current_gl_driver->ext.b_GL_ARB_get_program_binary) { |
| EXPECT_CALL(*gl_.get(), |
| ProgramParameteri(service_program_id, |
| PROGRAM_BINARY_RETRIEVABLE_HINT, |
| GL_TRUE)).Times(1); |
| } |
| } |
| |
| void SetExpectationsForSuccessCompile( |
| const Shader* shader) { |
| const GLuint shader_id = shader->service_id(); |
| const char* src = shader->source().c_str(); |
| EXPECT_CALL(*gl_.get(), ShaderSource(shader_id, 1, Pointee(src), nullptr)) |
| .Times(1); |
| EXPECT_CALL(*gl_.get(), CompileShader(shader_id)).Times(1); |
| EXPECT_CALL(*gl_.get(), GetShaderiv(shader_id, GL_COMPILE_STATUS, _)) |
| .WillOnce(SetArgPointee<2>(GL_TRUE)); |
| } |
| |
| void SetExpectationsForNoCompile(const Shader* shader) { |
| const GLuint shader_id = shader->service_id(); |
| const char* src = shader->source().c_str(); |
| EXPECT_CALL(*gl_.get(), ShaderSource(shader_id, 1, Pointee(src), nullptr)) |
| .Times(0); |
| EXPECT_CALL(*gl_.get(), CompileShader(shader_id)).Times(0); |
| EXPECT_CALL(*gl_.get(), GetShaderiv(shader_id, GL_COMPILE_STATUS, _)) |
| .Times(0); |
| } |
| |
| void SetExpectationsForErrorCompile(const Shader* shader) { |
| const GLuint shader_id = shader->service_id(); |
| const char* src = shader->source().c_str(); |
| EXPECT_CALL(*gl_.get(), ShaderSource(shader_id, 1, Pointee(src), nullptr)) |
| .Times(1); |
| EXPECT_CALL(*gl_.get(), CompileShader(shader_id)).Times(1); |
| EXPECT_CALL(*gl_.get(), GetShaderiv(shader_id, GL_COMPILE_STATUS, _)) |
| .WillOnce(SetArgPointee<2>(GL_FALSE)); |
| EXPECT_CALL(*gl_.get(), GetShaderiv(shader_id, GL_INFO_LOG_LENGTH, _)) |
| .WillOnce(SetArgPointee<2>(0)); |
| EXPECT_CALL(*gl_.get(), GetShaderInfoLog(shader_id, 0, _, _)) |
| .Times(1); |
| } |
| |
| std::unique_ptr<MockProgramCache> cache_; |
| |
| Shader* vertex_shader_; |
| Shader* fragment_shader_; |
| Program* program_; |
| ShaderManager shader_manager_; |
| }; |
| |
| // GCC requires these declarations, but MSVC requires they not be present |
| #ifndef COMPILER_MSVC |
| const GLuint ProgramManagerWithCacheTest::kClientProgramId; |
| const GLuint ProgramManagerWithCacheTest::kServiceProgramId; |
| const GLuint ProgramManagerWithCacheTest::kVertexShaderClientId; |
| const GLuint ProgramManagerWithCacheTest::kFragmentShaderClientId; |
| const GLuint ProgramManagerWithCacheTest::kVertexShaderServiceId; |
| const GLuint ProgramManagerWithCacheTest::kFragmentShaderServiceId; |
| #endif |
| |
| TEST_F(ProgramManagerWithCacheTest, CacheProgramOnSuccessfulLink) { |
| SetShadersCompiled(); |
| SetExpectationsForProgramLink(); |
| SetExpectationsForProgramCached(); |
| EXPECT_TRUE(program_->Link(nullptr, Program::kCountOnlyStaticallyUsed, this)); |
| } |
| |
| TEST_F(ProgramManagerWithCacheTest, LoadProgramOnProgramCacheHit) { |
| SetShadersCompiled(); |
| SetProgramCached(); |
| |
| SetExpectationsForNoCompile(vertex_shader_); |
| SetExpectationsForNoCompile(fragment_shader_); |
| SetExpectationsForProgramLoad(ProgramCache::PROGRAM_LOAD_SUCCESS); |
| SetExpectationsForNotCachingProgram(); |
| SetExpectationsForProgramLoadSuccess(); |
| |
| EXPECT_TRUE(program_->Link(nullptr, Program::kCountOnlyStaticallyUsed, this)); |
| } |
| |
| TEST_F(ProgramManagerWithCacheTest, RelinkOnChangedCompileOptions) { |
| SetShadersCompiled("a"); |
| SetProgramCached(); |
| SetExpectationsForProgramCached(); |
| |
| SetShadersCompiled("b"); |
| SetExpectationsForProgramLink(); |
| SetExpectationsForProgramNotLoaded(); |
| EXPECT_TRUE(program_->Link(nullptr, Program::kCountOnlyStaticallyUsed, this)); |
| } |
| |
| class ProgramManagerWithPathRenderingTest |
| : public ProgramManagerWithShaderTest, |
| public testing::WithParamInterface< |
| testing::tuple<const char*, const char*>> { |
| protected: |
| void SetUp() override { |
| SetUpBase(testing::get<0>(GetParam()), testing::get<1>(GetParam())); |
| } |
| static const char* kFragmentInput1Name; |
| static const char* kFragmentInput2Name; |
| // Name that GL reports for input 2. Needed because input 2 is an |
| // array. |
| static const char* kFragmentInput2GLName; |
| static const char* kFragmentInput3Name; |
| static const char* kFragmentInput3GLName; |
| static const GLint kFragmentInput1Size = 1; |
| static const GLint kFragmentInput2Size = 3; |
| static const GLint kFragmentInput3Size = 2; |
| static const int kFragmentInput1Precision = GL_LOW_FLOAT; |
| static const int kFragmentInput2Precision = GL_MEDIUM_INT; |
| static const int kFragmentInput3Precision = GL_HIGH_FLOAT; |
| static const int kFragmentInput1StaticUse = 1; |
| static const int kFragmentInput2StaticUse = 1; |
| static const int kFragmentInput3StaticUse = 1; |
| static const GLint kFragmentInput1FakeLocation = 0; |
| static const GLint kFragmentInput2FakeLocation = 1; |
| static const GLint kFragmentInput3FakeLocation = 2; |
| static const GLint kFragmentInput1RealLocation = 11; |
| static const GLint kFragmentInput2RealLocation = 22; |
| static const GLint kFragmentInput3RealLocation = 33; |
| static const GLenum kFragmentInput1Type = GL_FLOAT_VEC4; |
| static const GLenum kFragmentInput2Type = GL_INT_VEC2; |
| static const GLenum kFragmentInput3Type = GL_FLOAT_VEC3; |
| }; |
| #ifndef COMPILER_MSVC |
| const GLint ProgramManagerWithPathRenderingTest::kFragmentInput1Size; |
| const GLint ProgramManagerWithPathRenderingTest::kFragmentInput2Size; |
| const GLint ProgramManagerWithPathRenderingTest::kFragmentInput3Size; |
| const int ProgramManagerWithPathRenderingTest::kFragmentInput1Precision; |
| const int ProgramManagerWithPathRenderingTest::kFragmentInput2Precision; |
| const int ProgramManagerWithPathRenderingTest::kFragmentInput3Precision; |
| const int ProgramManagerWithPathRenderingTest::kFragmentInput1StaticUse; |
| const int ProgramManagerWithPathRenderingTest::kFragmentInput2StaticUse; |
| const int ProgramManagerWithPathRenderingTest::kFragmentInput3StaticUse; |
| const GLint ProgramManagerWithPathRenderingTest::kFragmentInput1FakeLocation; |
| const GLint ProgramManagerWithPathRenderingTest::kFragmentInput2FakeLocation; |
| const GLint ProgramManagerWithPathRenderingTest::kFragmentInput3FakeLocation; |
| const GLint ProgramManagerWithPathRenderingTest::kFragmentInput1RealLocation; |
| const GLint ProgramManagerWithPathRenderingTest::kFragmentInput2RealLocation; |
| const GLint ProgramManagerWithPathRenderingTest::kFragmentInput3RealLocation; |
| const GLenum ProgramManagerWithPathRenderingTest::kFragmentInput1Type; |
| const GLenum ProgramManagerWithPathRenderingTest::kFragmentInput2Type; |
| const GLenum ProgramManagerWithPathRenderingTest::kFragmentInput3Type; |
| #endif |
| |
| const char* ProgramManagerWithPathRenderingTest::kFragmentInput1Name = "color1"; |
| const char* ProgramManagerWithPathRenderingTest::kFragmentInput2Name = "color2"; |
| const char* ProgramManagerWithPathRenderingTest::kFragmentInput2GLName = |
| "color2[0]"; |
| const char* ProgramManagerWithPathRenderingTest::kFragmentInput3Name = "color3"; |
| const char* ProgramManagerWithPathRenderingTest::kFragmentInput3GLName = |
| "color3[0]"; |
| |
| TEST_P(ProgramManagerWithPathRenderingTest, BindFragmentInputLocation) { |
| const GLint kFragmentInput1DesiredLocation = 10; |
| const GLint kFragmentInput2DesiredLocation = -1; |
| const GLint kFragmentInput3DesiredLocation = 5; |
| |
| Shader* vshader = shader_manager_.CreateShader( |
| kVertexShaderClientId, kVertexShaderServiceId, GL_VERTEX_SHADER); |
| ASSERT_TRUE(vshader != nullptr); |
| Shader* fshader = shader_manager_.CreateShader( |
| kFragmentShaderClientId, kFragmentShaderServiceId, GL_FRAGMENT_SHADER); |
| ASSERT_TRUE(fshader != nullptr); |
| VaryingMap varying_map; |
| varying_map[kFragmentInput1Name] = TestHelper::ConstructVarying( |
| kFragmentInput1Type, kFragmentInput1Size, kFragmentInput1Precision, |
| kFragmentInput1StaticUse, kFragmentInput1Name); |
| varying_map[kFragmentInput2Name] = TestHelper::ConstructVarying( |
| kFragmentInput2Type, kFragmentInput2Size, kFragmentInput2Precision, |
| kFragmentInput2StaticUse, kFragmentInput2Name); |
| varying_map[kFragmentInput3Name] = TestHelper::ConstructVarying( |
| kFragmentInput3Type, kFragmentInput3Size, kFragmentInput3Precision, |
| kFragmentInput3StaticUse, kFragmentInput3Name); |
| TestHelper::SetShaderStates(gl_.get(), vshader, true, nullptr, nullptr, |
| nullptr, nullptr, nullptr, &varying_map, nullptr, |
| nullptr, nullptr); |
| TestHelper::SetShaderStates(gl_.get(), fshader, true, nullptr, nullptr, |
| nullptr, nullptr, nullptr, &varying_map, nullptr, |
| nullptr, nullptr); |
| Program* program = |
| manager_->CreateProgram(kClientProgramId, kServiceProgramId); |
| ASSERT_TRUE(program != nullptr); |
| EXPECT_TRUE(program->AttachShader(&shader_manager_, vshader)); |
| EXPECT_TRUE(program->AttachShader(&shader_manager_, fshader)); |
| program->SetFragmentInputLocationBinding(kFragmentInput1Name, |
| kFragmentInput1DesiredLocation); |
| program->SetFragmentInputLocationBinding(kFragmentInput3Name, |
| kFragmentInput3DesiredLocation); |
| TestHelper::VaryingInfo kFragmentInputExpectationInfos[] = { |
| { |
| kFragmentInput1Name, kFragmentInput1Size, kFragmentInput1Type, |
| kFragmentInput1FakeLocation, kFragmentInput1RealLocation, |
| kFragmentInput1DesiredLocation, |
| }, |
| { |
| kFragmentInput2GLName, kFragmentInput2Size, kFragmentInput2Type, |
| kFragmentInput2FakeLocation, kFragmentInput2RealLocation, |
| kFragmentInput2DesiredLocation, |
| }, |
| { |
| kFragmentInput3GLName, kFragmentInput3Size, kFragmentInput3Type, |
| kFragmentInput3FakeLocation, kFragmentInput3RealLocation, |
| kFragmentInput3DesiredLocation, |
| }, |
| }; |
| TestHelper::SetupShaderExpectationsWithVaryings( |
| gl_.get(), feature_info_.get(), nullptr, 0, nullptr, 0, |
| kFragmentInputExpectationInfos, |
| base::size(kFragmentInputExpectationInfos), nullptr, 0, |
| kServiceProgramId); |
| program->Link(nullptr, Program::kCountOnlyStaticallyUsed, this); |
| const Program::FragmentInputInfo* info1 = |
| program->GetFragmentInputInfoByFakeLocation( |
| kFragmentInput1DesiredLocation); |
| ASSERT_NE(info1, nullptr); |
| EXPECT_EQ(kFragmentInput1RealLocation, static_cast<GLint>(info1->location)); |
| const Program::FragmentInputInfo* info3 = |
| program->GetFragmentInputInfoByFakeLocation( |
| kFragmentInput3DesiredLocation); |
| ASSERT_NE(info3, nullptr); |
| EXPECT_EQ(kFragmentInput3RealLocation, static_cast<GLint>(info3->location)); |
| } |
| |
| // For some compilers, using make_tuple("a", "bb") would end up |
| // instantiating make_tuple<char[1], char[2]>. This does not work. |
| namespace { |
| testing::tuple<const char*, const char*> make_gl_ext_tuple( |
| const char* gl_version, |
| const char* gl_extensions) { |
| return testing::make_tuple(gl_version, gl_extensions); |
| } |
| } |
| |
| INSTANTIATE_TEST_CASE_P( |
| SupportedContexts, |
| ProgramManagerWithPathRenderingTest, |
| testing::Values( |
| make_gl_ext_tuple("3.2", |
| "GL_ARB_program_interface_query " |
| "GL_EXT_direct_state_access GL_NV_path_rendering " |
| "GL_NV_framebuffer_mixed_samples"), |
| make_gl_ext_tuple("4.5", |
| "GL_NV_path_rendering " |
| "GL_NV_framebuffer_mixed_samples"), |
| make_gl_ext_tuple("OpenGL ES 3.1", |
| "GL_NV_path_rendering " |
| "GL_NV_framebuffer_mixed_samples"))); |
| |
| class ProgramManagerDualSourceBlendingTest |
| : public ProgramManagerWithShaderTest, |
| public testing::WithParamInterface< |
| testing::tuple<const char*, const char*>> { |
| public: |
| ProgramManagerDualSourceBlendingTest() = default; |
| |
| protected: |
| void SetUpWithFeatureInfo(FeatureInfo* feature_info) { |
| const char* gl_version = testing::get<0>(GetParam()); |
| const char* gl_extensions = testing::get<1>(GetParam()); |
| SetUpBase(gl_version, gl_extensions, feature_info); |
| } |
| |
| void SetUp() override { SetUpWithFeatureInfo(nullptr); } |
| }; |
| |
| class ProgramManagerDualSourceBlendingES2Test |
| : public ProgramManagerDualSourceBlendingTest {}; |
| |
| TEST_P(ProgramManagerDualSourceBlendingES2Test, UseSecondaryFragCoord) { |
| DCHECK(feature_info_->feature_flags().ext_blend_func_extended); |
| |
| const VarInfo kFragmentVaryings[] = { |
| {GL_FLOAT_VEC4, 0, GL_MEDIUM_FLOAT, true, "gl_SecondaryFragColorEXT", |
| kVarOutput}, |
| {GL_FLOAT_VEC4, 0, GL_MEDIUM_FLOAT, true, "gl_FragColor", kVarOutput}, |
| }; |
| |
| int shader_version = 100; |
| Program* program = |
| SetupProgramForVariables(nullptr, 0, kFragmentVaryings, |
| base::size(kFragmentVaryings), &shader_version); |
| |
| const gl::GLVersionInfo& gl_version = feature_info_->gl_version_info(); |
| if (!gl_version.is_es) { |
| // The call is expected only for OpenGL. OpenGL ES expects to |
| // output GLES SL 1.00, which does not bind. |
| EXPECT_CALL(*(gl_.get()), |
| BindFragDataLocationIndexed(kServiceProgramId, 0, 1, |
| StrEq("angle_SecondaryFragColor"))) |
| .Times(1) |
| .RetiresOnSaturation(); |
| } |
| |
| EXPECT_TRUE(LinkAsExpected(program, true)); |
| } |
| |
| TEST_P(ProgramManagerDualSourceBlendingES2Test, UseSecondaryFragData) { |
| const VarInfo kFragmentVaryings[] = { |
| {GL_FLOAT_VEC4, kMaxDualSourceDrawBuffers, GL_MEDIUM_FLOAT, true, |
| "gl_SecondaryFragDataEXT", kVarOutput}, |
| {GL_FLOAT_VEC4, kMaxDrawBuffers, GL_MEDIUM_FLOAT, true, "gl_FragData", |
| kVarOutput}, |
| }; |
| |
| int shader_version = 100; |
| Program* program = |
| SetupProgramForVariables(nullptr, 0, kFragmentVaryings, |
| base::size(kFragmentVaryings), &shader_version); |
| |
| const gl::GLVersionInfo& gl_version = feature_info_->gl_version_info(); |
| if (!gl_version.is_es) { |
| // The call is expected only for OpenGL. OpenGL ES expects to |
| // output GLES SL 1.00, which does not bind. |
| EXPECT_CALL(*(gl_.get()), |
| BindFragDataLocationIndexed(kServiceProgramId, 0, 1, |
| StrEq("angle_SecondaryFragData"))) |
| .Times(1) |
| .RetiresOnSaturation(); |
| } |
| |
| EXPECT_TRUE(LinkAsExpected(program, true)); |
| } |
| |
| INSTANTIATE_TEST_CASE_P( |
| SupportedContexts, |
| ProgramManagerDualSourceBlendingES2Test, |
| testing::Values( |
| make_gl_ext_tuple("3.2", |
| "GL_ARB_draw_buffers GL_ARB_blend_func_extended " |
| "GL_ARB_program_interface_query"), |
| make_gl_ext_tuple("OpenGL ES 3.1", |
| "GL_EXT_draw_buffers GL_EXT_blend_func_extended"))); |
| |
| } // namespace gles2 |
| } // namespace gpu |