diff options
author | gman@chromium.org <gman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-12-13 20:06:14 +0000 |
---|---|---|
committer | gman@chromium.org <gman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-12-13 20:06:14 +0000 |
commit | ca488e19c17583ada1af51e2e27393981e4be772 (patch) | |
tree | 9dc186beeffea7cb3d5e35a868fb92fc76f62820 | |
parent | 1ad2a1dbcde42412bb92c83fe5e0d6999ed00311 (diff) | |
download | chromium_src-ca488e19c17583ada1af51e2e27393981e4be772.zip chromium_src-ca488e19c17583ada1af51e2e27393981e4be772.tar.gz chromium_src-ca488e19c17583ada1af51e2e27393981e4be772.tar.bz2 |
Make shader and program object lifetimes match OpenGL ES spec.
TEST=unit tests
BUG=65705
Review URL: http://codereview.chromium.org/5676003
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@69039 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | gpu/command_buffer/client/gles2_implementation.cc | 30 | ||||
-rw-r--r-- | gpu/command_buffer/service/gles2_cmd_decoder.cc | 53 | ||||
-rw-r--r-- | gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc | 18 | ||||
-rw-r--r-- | gpu/command_buffer/service/gles2_cmd_decoder_unittest_1.cc | 16 | ||||
-rw-r--r-- | gpu/command_buffer/service/program_manager.cc | 68 | ||||
-rw-r--r-- | gpu/command_buffer/service/program_manager.h | 39 | ||||
-rw-r--r-- | gpu/command_buffer/service/program_manager_unittest.cc | 142 | ||||
-rw-r--r-- | gpu/command_buffer/service/shader_manager.cc | 39 | ||||
-rw-r--r-- | gpu/command_buffer/service/shader_manager.h | 35 | ||||
-rw-r--r-- | gpu/command_buffer/service/shader_manager_unittest.cc | 86 |
10 files changed, 429 insertions, 97 deletions
diff --git a/gpu/command_buffer/client/gles2_implementation.cc b/gpu/command_buffer/client/gles2_implementation.cc index 4e75be9..aa7f69d 100644 --- a/gpu/command_buffer/client/gles2_implementation.cc +++ b/gpu/command_buffer/client/gles2_implementation.cc @@ -59,6 +59,34 @@ class NonSharedIdHandler : public IdHandlerInterface { IdAllocator id_allocator_; }; +// An id handler for non-shared ids that are never reused. +class NonSharedNonReusedIdHandler : public IdHandlerInterface { + public: + NonSharedNonReusedIdHandler() : last_id_(0) { } + virtual ~NonSharedNonReusedIdHandler() { } + + // Overridden from IdHandlerInterface. + virtual void MakeIds(GLuint id_offset, GLsizei n, GLuint* ids) { + for (GLsizei ii = 0; ii < n; ++ii) { + ids[ii] = ++last_id_ + id_offset; + } + } + + // Overridden from IdHandlerInterface. + virtual void FreeIds(GLsizei /* n */, const GLuint* /* ids */) { + // Ids are never freed. + } + + // Overridden from IdHandlerInterface. + virtual bool MarkAsUsedForBind(GLuint /* id */) { + // This is only used for Shaders and Programs which have no bind. + return false; + } + + private: + GLuint last_id_; +}; + // An id handler for shared ids. class SharedIdHandler : public IdHandlerInterface { public: @@ -439,7 +467,7 @@ GLES2Implementation::GLES2Implementation( buffer_id_handler_.reset(new NonSharedIdHandler()); framebuffer_id_handler_.reset(new NonSharedIdHandler()); renderbuffer_id_handler_.reset(new NonSharedIdHandler()); - program_and_shader_id_handler_.reset(new NonSharedIdHandler()); + program_and_shader_id_handler_.reset(new NonSharedNonReusedIdHandler()); texture_id_handler_.reset(new NonSharedIdHandler()); } diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc index 4d13583..73cc483 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder.cc +++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc @@ -850,9 +850,7 @@ class GLES2DecoderImpl : public base::SupportsWeakPtr<GLES2DecoderImpl>, // Gets the program info for the given program. Returns NULL if none exists. ProgramManager::ProgramInfo* GetProgramInfo(GLuint client_id) { - ProgramManager::ProgramInfo* info = - program_manager()->GetProgramInfo(client_id); - return (info && !info->IsDeleted()) ? info : NULL; + return program_manager()->GetProgramInfo(client_id); } // Gets the program info for the given program. If it's not a program @@ -874,11 +872,6 @@ class GLES2DecoderImpl : public base::SupportsWeakPtr<GLES2DecoderImpl>, } - // Deletes the program info for the given program. - void RemoveProgramInfo(GLuint client_id) { - program_manager()->RemoveProgramInfo(client_id); - } - // Creates a ShaderInfo for the given shader. void CreateShaderInfo(GLuint client_id, GLuint service_id, @@ -888,9 +881,7 @@ class GLES2DecoderImpl : public base::SupportsWeakPtr<GLES2DecoderImpl>, // Gets the shader info for the given shader. Returns NULL if none exists. ShaderManager::ShaderInfo* GetShaderInfo(GLuint client_id) { - ShaderManager::ShaderInfo* info = - shader_manager()->GetShaderInfo(client_id); - return (info && !info->IsDeleted()) ? info : NULL; + return shader_manager()->GetShaderInfo(client_id); } // Gets the shader info for the given shader. If it's not a shader generates a @@ -912,11 +903,6 @@ class GLES2DecoderImpl : public base::SupportsWeakPtr<GLES2DecoderImpl>, return info; } - // Deletes the shader info for the given shader. - void RemoveShaderInfo(GLuint client_id) { - shader_manager()->RemoveShaderInfo(client_id); - } - // Creates a buffer info for the given buffer. void CreateBufferInfo(GLuint client_id, GLuint service_id) { return buffer_manager()->CreateBufferInfo(client_id, service_id); @@ -2474,6 +2460,11 @@ void GLES2DecoderImpl::Destroy() { group_->set_have_context(have_context); if (have_context) { + if (current_program_) { + program_manager()->UnuseProgram(shader_manager(), current_program_); + current_program_ = NULL; + } + if (attrib_0_buffer_id_) { glDeleteBuffersARB(1, &attrib_0_buffer_id_); } @@ -3157,8 +3148,10 @@ error::Error GLES2DecoderImpl::HandleDeleteShader( if (client_id) { ShaderManager::ShaderInfo* info = GetShaderInfo(client_id); if (info) { - glDeleteShader(info->service_id()); - RemoveShaderInfo(client_id); + if (!info->IsDeleted()) { + glDeleteShader(info->service_id()); + shader_manager()->MarkAsDeleted(info); + } } else { SetGLError(GL_INVALID_VALUE, "glDeleteShader: unknown shader"); } @@ -3172,8 +3165,10 @@ error::Error GLES2DecoderImpl::HandleDeleteProgram( if (client_id) { ProgramManager::ProgramInfo* info = GetProgramInfo(client_id); if (info) { - glDeleteProgram(info->service_id()); - RemoveProgramInfo(client_id); + if (!info->IsDeleted()) { + glDeleteProgram(info->service_id()); + program_manager()->MarkAsDeleted(shader_manager(), info); + } } else { SetGLError(GL_INVALID_VALUE, "glDeleteProgram: unknown program"); } @@ -3695,13 +3690,13 @@ void GLES2DecoderImpl::DoTexParameteriv( } bool GLES2DecoderImpl::CheckCurrentProgram(const char* function_name) { - if (!current_program_ || current_program_->IsDeleted()) { + if (!current_program_) { // The program does not exist. SetGLError(GL_INVALID_OPERATION, (std::string(function_name) + ": no program in use").c_str()); return false; } - if (!current_program_->IsValid()) { + if (!current_program_->InUse()) { SetGLError(GL_INVALID_OPERATION, (std::string(function_name) + ": program not linked").c_str()); return false; @@ -3834,7 +3829,13 @@ void GLES2DecoderImpl::DoUseProgram(GLuint program) { } service_id = info->service_id(); } + if (current_program_) { + program_manager()->UnuseProgram(shader_manager(), current_program_); + } current_program_ = info; + if (current_program_) { + program_manager()->UseProgram(current_program_); + } glUseProgram(service_id); } @@ -3881,7 +3882,6 @@ void GLES2DecoderImpl::ClearRealGLErrors() { bool GLES2DecoderImpl::SetBlackTextureForNonRenderableTextures() { DCHECK(current_program_); - DCHECK(!current_program_->IsDeleted()); // Only check if there are some unrenderable textures. if (!texture_manager()->HaveUnrenderableTextures()) { return false; @@ -3918,7 +3918,6 @@ bool GLES2DecoderImpl::SetBlackTextureForNonRenderableTextures() { void GLES2DecoderImpl::RestoreStateForNonRenderableTextures() { DCHECK(current_program_); - DCHECK(!current_program_->IsDeleted()); const ProgramManager::ProgramInfo::SamplerIndices& sampler_indices = current_program_->sampler_indices(); for (size_t ii = 0; ii < sampler_indices.size(); ++ii) { @@ -3954,7 +3953,7 @@ bool GLES2DecoderImpl::IsDrawValid(GLuint max_vertex_accessed) { // it could never be invalid since glUseProgram would have failed. While // glLinkProgram could later mark the program as invalid the previous // valid program will still function if it is still the current program. - if (!current_program_ || current_program_->IsDeleted()) { + if (!current_program_) { // The program does not exist. // But GL says no ERROR. return false; @@ -4418,7 +4417,7 @@ void GLES2DecoderImpl::DoAttachShader( if (!shader_info) { return; } - if (!program_info->AttachShader(shader_info)) { + if (!program_info->AttachShader(shader_manager(), shader_info)) { SetGLError(GL_INVALID_OPERATION, "glAttachShader: can not attach more than" " one shader of the same type."); @@ -4439,8 +4438,8 @@ void GLES2DecoderImpl::DoDetachShader( if (!shader_info) { return; } - program_info->DetachShader(shader_info); glDetachShader(program_info->service_id(), shader_info->service_id()); + program_info->DetachShader(shader_manager(), shader_info); } void GLES2DecoderImpl::DoValidateProgram(GLuint program_client_id) { diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc index 6129525..46a70ae 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc +++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc @@ -172,14 +172,14 @@ TEST_F(GLES2DecoderWithShaderTest, DrawArraysDeletedBufferFails) { EXPECT_EQ(GL_INVALID_OPERATION, GetGLError()); } -TEST_F(GLES2DecoderWithShaderTest, - DrawArraysDeletedProgramSucceedsWithoutGLCall) { - SetupVertexBuffer(); - DoVertexAttribPointer(1, 2, GL_FLOAT, 0, 0); +TEST_F(GLES2DecoderWithShaderTest, DrawArraysDeletedProgramSucceeds) { + SetupTexture(); + AddExpectationsForSimulatedAttrib0(kNumVertices, 0); DoDeleteProgram(client_program_id_, kServiceProgramId); EXPECT_CALL(*gl_, DrawArrays(_, _, _)) - .Times(0); + .Times(1) + .RetiresOnSaturation(); DrawArrays cmd; cmd.Init(GL_TRIANGLES, 0, kNumVertices); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); @@ -330,14 +330,14 @@ TEST_F(GLES2DecoderWithShaderTest, DrawElementsDeletedBufferFails) { EXPECT_EQ(GL_INVALID_OPERATION, GetGLError()); } -TEST_F(GLES2DecoderWithShaderTest, DrawElementsDeletedProgramSucceedsNoGLCall) { - SetupVertexBuffer(); +TEST_F(GLES2DecoderWithShaderTest, DrawElementsDeletedProgramSucceeds) { + SetupTexture(); SetupIndexBuffer(); - DoVertexAttribPointer(1, 2, GL_FLOAT, 0, 0); + AddExpectationsForSimulatedAttrib0(kMaxValidIndex + 1, 0); DoDeleteProgram(client_program_id_, kServiceProgramId); EXPECT_CALL(*gl_, DrawElements(_, _, _, _)) - .Times(0); + .Times(1); DrawElements cmd; cmd.Init(GL_TRIANGLES, kValidIndexRangeCount, GL_UNSIGNED_SHORT, kValidIndexRangeStart * 2); diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_1.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_1.cc index 75597fa..e589e6d 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_1.cc +++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_1.cc @@ -60,8 +60,7 @@ void GLES2DecoderTestBase::SpecializedSetup<CopyTexImage2D, 0>( }; template <> -void GLES2DecoderTestBase::SpecializedSetup<CopyTexSubImage2D, 0>( - bool valid) { +void GLES2DecoderTestBase::SpecializedSetup<CopyTexSubImage2D, 0>(bool valid) { if (valid) { DoBindTexture(GL_TEXTURE_2D, client_texture_id_, kServiceTextureId); DoTexImage2D( @@ -71,6 +70,19 @@ void GLES2DecoderTestBase::SpecializedSetup<CopyTexSubImage2D, 0>( }; template <> +void GLES2DecoderTestBase::SpecializedSetup<DetachShader, 0>(bool valid) { + if (valid) { + EXPECT_CALL(*gl_, + AttachShader(kServiceProgramId, kServiceShaderId)) + .Times(1) + .RetiresOnSaturation(); + AttachShader attach_cmd; + attach_cmd.Init(client_program_id_, client_shader_id_); + EXPECT_EQ(error::kNoError, ExecuteCmd(attach_cmd)); + } +}; + +template <> void GLES2DecoderTestBase::SpecializedSetup<FramebufferRenderbuffer, 0>( bool valid) { DoBindFramebuffer(GL_FRAMEBUFFER, client_framebuffer_id_, diff --git a/gpu/command_buffer/service/program_manager.cc b/gpu/command_buffer/service/program_manager.cc index 596c8c9..339aff5 100644 --- a/gpu/command_buffer/service/program_manager.cc +++ b/gpu/command_buffer/service/program_manager.cc @@ -44,7 +44,8 @@ bool ProgramManager::IsInvalidPrefix(const char* name, size_t length) { } ProgramManager::ProgramInfo::ProgramInfo(GLuint service_id) - : max_attrib_name_length_(0), + : use_count_(0), + max_attrib_name_length_(0), max_uniform_name_length_(0), service_id_(service_id), valid_(false) { @@ -356,18 +357,35 @@ void ProgramManager::ProgramInfo::GetProgramiv(GLenum pname, GLint* params) { } bool ProgramManager::ProgramInfo::AttachShader( + ShaderManager* shader_manager, ShaderManager::ShaderInfo* info) { + DCHECK(shader_manager); + DCHECK(info); int index = ShaderTypeToIndex(info->shader_type()); if (attached_shaders_[index] != NULL) { return false; } attached_shaders_[index] = ShaderManager::ShaderInfo::Ref(info); + shader_manager->UseShader(info); return true; } void ProgramManager::ProgramInfo::DetachShader( + ShaderManager* shader_manager, ShaderManager::ShaderInfo* info) { + DCHECK(shader_manager); + DCHECK(info); attached_shaders_[ShaderTypeToIndex(info->shader_type())] = NULL; + shader_manager->UnuseShader(info); +} + +void ProgramManager::ProgramInfo::DetachShaders(ShaderManager* shader_manager) { + DCHECK(shader_manager); + for (int ii = 0; ii < kMaxAttachedShaders; ++ii) { + if (attached_shaders_[ii]) { + DetachShader(shader_manager, attached_shaders_[ii]); + } + } } bool ProgramManager::ProgramInfo::CanLink() const { @@ -413,14 +431,6 @@ ProgramManager::ProgramInfo* ProgramManager::GetProgramInfo(GLuint client_id) { return it != program_infos_.end() ? it->second : NULL; } -void ProgramManager::RemoveProgramInfo(GLuint client_id) { - ProgramInfoMap::iterator it = program_infos_.find(client_id); - if (it != program_infos_.end()) { - it->second->MarkAsDeleted(); - program_infos_.erase(it); - } -} - bool ProgramManager::GetClientId(GLuint service_id, GLuint* client_id) const { // This doesn't need to be fast. It's only used during slow queries. for (ProgramInfoMap::const_iterator it = program_infos_.begin(); @@ -433,6 +443,46 @@ bool ProgramManager::GetClientId(GLuint service_id, GLuint* client_id) const { return false; } +void ProgramManager::RemoveProgramInfoIfUnused( + ShaderManager* shader_manager, ProgramInfo* info) { + DCHECK(shader_manager); + DCHECK(info); + if (info->IsDeleted() && !info->InUse()) { + info->DetachShaders(shader_manager); + for (ProgramInfoMap::iterator it = program_infos_.begin(); + it != program_infos_.end(); ++it) { + if (it->second->service_id() == info->service_id()) { + program_infos_.erase(it); + return; + } + } + NOTREACHED(); + } +} + +void ProgramManager::MarkAsDeleted( + ShaderManager* shader_manager, + ProgramManager::ProgramInfo* info) { + DCHECK(shader_manager); + DCHECK(info); + info->MarkAsDeleted(); + RemoveProgramInfoIfUnused(shader_manager, info); +} + +void ProgramManager::UseProgram(ProgramManager::ProgramInfo* info) { + DCHECK(info); + info->IncUseCount(); +} + +void ProgramManager::UnuseProgram( + ShaderManager* shader_manager, + ProgramManager::ProgramInfo* info) { + DCHECK(shader_manager); + DCHECK(info); + info->DecUseCount(); + RemoveProgramInfoIfUnused(shader_manager, info); +} + } // namespace gles2 } // namespace gpu diff --git a/gpu/command_buffer/service/program_manager.h b/gpu/command_buffer/service/program_manager.h index 53893b3..b7229d9 100644 --- a/gpu/command_buffer/service/program_manager.h +++ b/gpu/command_buffer/service/program_manager.h @@ -9,6 +9,7 @@ #include <string> #include <vector> #include "base/basictypes.h" +#include "base/logging.h" #include "base/ref_counted.h" #include "gpu/command_buffer/service/gl_utils.h" #include "gpu/command_buffer/service/shader_manager.h" @@ -130,8 +131,8 @@ class ProgramManager { return valid_; } - bool AttachShader(ShaderManager::ShaderInfo* info); - void DetachShader(ShaderManager::ShaderInfo* info); + bool AttachShader(ShaderManager* manager, ShaderManager::ShaderInfo* info); + void DetachShader(ShaderManager* manager, ShaderManager::ShaderInfo* info); bool CanLink() const; @@ -143,13 +144,28 @@ class ProgramManager { log_info_ = str; } + bool InUse() const { + DCHECK_GE(use_count_, 0); + return use_count_ != 0; + } + private: friend class base::RefCounted<ProgramInfo>; friend class ProgramManager; ~ProgramInfo(); + void IncUseCount() { + ++use_count_; + } + + void DecUseCount() { + --use_count_; + DCHECK_GE(use_count_, 0); + } + void MarkAsDeleted() { + DCHECK_NE(service_id_, 0u); service_id_ = 0; } @@ -160,6 +176,10 @@ class ProgramManager { bool use_uniforms, const std::string& name, std::string* corrected_name, GLsizei* size, GLenum* type) const; + void DetachShaders(ShaderManager* manager); + + int use_count_; + GLsizei max_attrib_name_length_; // Attrib by index. @@ -204,12 +224,18 @@ class ProgramManager { // Gets a program info ProgramInfo* GetProgramInfo(GLuint client_id); - // Deletes the program info for the given program. - void RemoveProgramInfo(GLuint client_id); - // Gets a client id for a given service id. bool GetClientId(GLuint service_id, GLuint* client_id) const; + // Marks a program as deleted. If it is not used the info will be deleted. + void MarkAsDeleted(ShaderManager* shader_manager, ProgramInfo* info); + + // Marks a program as used. + void UseProgram(ProgramInfo* info); + + // Makes a program as unused. If deleted the program info will be removed. + void UnuseProgram(ShaderManager* shader_manager, ProgramInfo* info); + // Returns true if prefix is invalid for gl. static bool IsInvalidPrefix(const char* name, size_t length); @@ -219,6 +245,9 @@ class ProgramManager { typedef std::map<GLuint, ProgramInfo::Ref> ProgramInfoMap; ProgramInfoMap program_infos_; + void RemoveProgramInfoIfUnused( + ShaderManager* shader_manager, ProgramInfo* info); + DISALLOW_COPY_AND_ASSIGN(ProgramManager); }; diff --git a/gpu/command_buffer/service/program_manager_unittest.cc b/gpu/command_buffer/service/program_manager_unittest.cc index 7f4fa21..788e18c 100644 --- a/gpu/command_buffer/service/program_manager_unittest.cc +++ b/gpu/command_buffer/service/program_manager_unittest.cc @@ -61,19 +61,11 @@ TEST_F(ProgramManagerTest, Basic) { // Check program got created. ProgramManager::ProgramInfo* info1 = manager_.GetProgramInfo(kClient1Id); ASSERT_TRUE(info1 != NULL); - EXPECT_EQ(kService1Id, info1->service_id()); - EXPECT_FALSE(info1->CanLink()); - EXPECT_STREQ("", info1->log_info().c_str()); GLuint client_id = 0; EXPECT_TRUE(manager_.GetClientId(info1->service_id(), &client_id)); EXPECT_EQ(kClient1Id, client_id); // Check we get nothing for a non-existent program. EXPECT_TRUE(manager_.GetProgramInfo(kClient2Id) == NULL); - // Check trying to a remove non-existent programs does not crash. - manager_.RemoveProgramInfo(kClient2Id); - // Check we can't get the program after we remove it. - manager_.RemoveProgramInfo(kClient1Id); - EXPECT_TRUE(manager_.GetProgramInfo(kClient1Id) == NULL); } TEST_F(ProgramManagerTest, Destroy) { @@ -94,6 +86,22 @@ TEST_F(ProgramManagerTest, Destroy) { ASSERT_TRUE(info1 == NULL); } +TEST_F(ProgramManagerTest, ProgramInfo) { + const GLuint kClient1Id = 1; + const GLuint kService1Id = 11; + // Check we can create program. + manager_.CreateProgramInfo(kClient1Id, kService1Id); + // Check program got created. + ProgramManager::ProgramInfo* info1 = manager_.GetProgramInfo(kClient1Id); + ASSERT_TRUE(info1 != NULL); + EXPECT_EQ(kService1Id, info1->service_id()); + EXPECT_FALSE(info1->InUse()); + EXPECT_FALSE(info1->IsValid()); + EXPECT_FALSE(info1->IsDeleted()); + EXPECT_FALSE(info1->CanLink()); + EXPECT_STREQ("", info1->log_info().c_str()); +} + class ProgramManagerWithShaderTest : public testing::Test { public: ProgramManagerWithShaderTest() @@ -431,19 +439,19 @@ TEST_F(ProgramManagerWithShaderTest, AttachDetachShader) { ShaderManager::ShaderInfo* fshader = shader_manager.GetShaderInfo( kFShaderClientId); fshader->SetStatus(true, "", NULL); - EXPECT_TRUE(program_info->AttachShader(vshader)); + EXPECT_TRUE(program_info->AttachShader(&shader_manager, vshader)); EXPECT_FALSE(program_info->CanLink()); - EXPECT_TRUE(program_info->AttachShader(fshader)); + EXPECT_TRUE(program_info->AttachShader(&shader_manager, fshader)); EXPECT_TRUE(program_info->CanLink()); - program_info->DetachShader(vshader); + program_info->DetachShader(&shader_manager, vshader); EXPECT_FALSE(program_info->CanLink()); - EXPECT_TRUE(program_info->AttachShader(vshader)); + EXPECT_TRUE(program_info->AttachShader(&shader_manager, vshader)); EXPECT_TRUE(program_info->CanLink()); - program_info->DetachShader(fshader); + program_info->DetachShader(&shader_manager, fshader); EXPECT_FALSE(program_info->CanLink()); - EXPECT_FALSE(program_info->AttachShader(vshader)); + EXPECT_FALSE(program_info->AttachShader(&shader_manager, vshader)); EXPECT_FALSE(program_info->CanLink()); - EXPECT_TRUE(program_info->AttachShader(fshader)); + EXPECT_TRUE(program_info->AttachShader(&shader_manager, fshader)); EXPECT_TRUE(program_info->CanLink()); vshader->SetStatus(false, "", NULL); EXPECT_FALSE(program_info->CanLink()); @@ -585,7 +593,7 @@ TEST_F(ProgramManagerWithShaderTest, GLDriverReturnsWrongTypeInfo) { ProgramManager::ProgramInfo* program_info = manager_.GetProgramInfo(kClientProgramId); ASSERT_TRUE(program_info != NULL); - EXPECT_TRUE(program_info->AttachShader(shader_info)); + EXPECT_TRUE(program_info->AttachShader(&shader_manager, shader_info)); program_info->Update(); // Check that we got the good type, not the bad. // Check Attribs @@ -615,6 +623,108 @@ TEST_F(ProgramManagerWithShaderTest, GLDriverReturnsWrongTypeInfo) { shader_manager.Destroy(false); } +TEST_F(ProgramManagerWithShaderTest, ProgramInfoUseCount) { + ShaderManager shader_manager; + ProgramManager::ProgramInfo* program_info = + manager_.GetProgramInfo(kClientProgramId); + ASSERT_TRUE(program_info != NULL); + EXPECT_FALSE(program_info->CanLink()); + const GLuint kVShaderClientId = 2001; + const GLuint kFShaderClientId = 2002; + const GLuint kVShaderServiceId = 3001; + const GLuint kFShaderServiceId = 3002; + shader_manager.CreateShaderInfo( + kVShaderClientId, kVShaderServiceId, GL_VERTEX_SHADER); + ShaderManager::ShaderInfo* vshader = shader_manager.GetShaderInfo( + kVShaderClientId); + ASSERT_TRUE(vshader != NULL); + vshader->SetStatus(true, "", NULL); + shader_manager.CreateShaderInfo( + kFShaderClientId, kFShaderServiceId, GL_FRAGMENT_SHADER); + ShaderManager::ShaderInfo* fshader = shader_manager.GetShaderInfo( + kFShaderClientId); + ASSERT_TRUE(fshader != NULL); + fshader->SetStatus(true, "", NULL); + EXPECT_FALSE(vshader->InUse()); + EXPECT_FALSE(fshader->InUse()); + EXPECT_TRUE(program_info->AttachShader(&shader_manager, vshader)); + EXPECT_TRUE(vshader->InUse()); + EXPECT_TRUE(program_info->AttachShader(&shader_manager, fshader)); + EXPECT_TRUE(fshader->InUse()); + EXPECT_TRUE(program_info->CanLink()); + EXPECT_FALSE(program_info->InUse()); + EXPECT_FALSE(program_info->IsDeleted()); + manager_.UseProgram(program_info); + EXPECT_TRUE(program_info->InUse()); + manager_.UseProgram(program_info); + EXPECT_TRUE(program_info->InUse()); + manager_.MarkAsDeleted(&shader_manager, program_info); + EXPECT_TRUE(program_info->IsDeleted()); + ProgramManager::ProgramInfo* info2 = + manager_.GetProgramInfo(kClientProgramId); + EXPECT_EQ(program_info, info2); + manager_.UnuseProgram(&shader_manager, program_info); + EXPECT_TRUE(program_info->InUse()); + // this should delete the info. + manager_.UnuseProgram(&shader_manager, program_info); + info2 = manager_.GetProgramInfo(kClientProgramId); + EXPECT_TRUE(info2 == NULL); + EXPECT_FALSE(vshader->InUse()); + EXPECT_FALSE(fshader->InUse()); + shader_manager.Destroy(false); +} + +TEST_F(ProgramManagerWithShaderTest, ProgramInfoUseCount2) { + ShaderManager shader_manager; + ProgramManager::ProgramInfo* program_info = + manager_.GetProgramInfo(kClientProgramId); + ASSERT_TRUE(program_info != NULL); + EXPECT_FALSE(program_info->CanLink()); + const GLuint kVShaderClientId = 2001; + const GLuint kFShaderClientId = 2002; + const GLuint kVShaderServiceId = 3001; + const GLuint kFShaderServiceId = 3002; + shader_manager.CreateShaderInfo( + kVShaderClientId, kVShaderServiceId, GL_VERTEX_SHADER); + ShaderManager::ShaderInfo* vshader = shader_manager.GetShaderInfo( + kVShaderClientId); + ASSERT_TRUE(vshader != NULL); + vshader->SetStatus(true, "", NULL); + shader_manager.CreateShaderInfo( + kFShaderClientId, kFShaderServiceId, GL_FRAGMENT_SHADER); + ShaderManager::ShaderInfo* fshader = shader_manager.GetShaderInfo( + kFShaderClientId); + ASSERT_TRUE(fshader != NULL); + fshader->SetStatus(true, "", NULL); + EXPECT_FALSE(vshader->InUse()); + EXPECT_FALSE(fshader->InUse()); + EXPECT_TRUE(program_info->AttachShader(&shader_manager, vshader)); + EXPECT_TRUE(vshader->InUse()); + EXPECT_TRUE(program_info->AttachShader(&shader_manager, fshader)); + EXPECT_TRUE(fshader->InUse()); + EXPECT_TRUE(program_info->CanLink()); + EXPECT_FALSE(program_info->InUse()); + EXPECT_FALSE(program_info->IsDeleted()); + manager_.UseProgram(program_info); + EXPECT_TRUE(program_info->InUse()); + manager_.UseProgram(program_info); + EXPECT_TRUE(program_info->InUse()); + manager_.UnuseProgram(&shader_manager, program_info); + EXPECT_TRUE(program_info->InUse()); + manager_.UnuseProgram(&shader_manager, program_info); + EXPECT_FALSE(program_info->InUse()); + ProgramManager::ProgramInfo* info2 = + manager_.GetProgramInfo(kClientProgramId); + EXPECT_EQ(program_info, info2); + // this should delete the program. + manager_.MarkAsDeleted(&shader_manager, program_info); + info2 = manager_.GetProgramInfo(kClientProgramId); + EXPECT_TRUE(info2 == NULL); + EXPECT_FALSE(vshader->InUse()); + EXPECT_FALSE(fshader->InUse()); + shader_manager.Destroy(false); +} + } // namespace gles2 } // namespace gpu diff --git a/gpu/command_buffer/service/shader_manager.cc b/gpu/command_buffer/service/shader_manager.cc index 794c75e..59fad72 100644 --- a/gpu/command_buffer/service/shader_manager.cc +++ b/gpu/command_buffer/service/shader_manager.cc @@ -68,14 +68,6 @@ ShaderManager::ShaderInfo* ShaderManager::GetShaderInfo(GLuint client_id) { return it != shader_infos_.end() ? it->second : NULL; } -void ShaderManager::RemoveShaderInfo(GLuint client_id) { - ShaderInfoMap::iterator it = shader_infos_.find(client_id); - if (it != shader_infos_.end()) { - it->second->MarkAsDeleted(); - shader_infos_.erase(it); - } -} - bool ShaderManager::GetClientId(GLuint service_id, GLuint* client_id) const { // This doesn't need to be fast. It's only used during slow queries. for (ShaderInfoMap::const_iterator it = shader_infos_.begin(); @@ -88,6 +80,37 @@ bool ShaderManager::GetClientId(GLuint service_id, GLuint* client_id) const { return false; } +void ShaderManager::RemoveShaderInfoIfUnused(ShaderManager::ShaderInfo* info) { + DCHECK(info); + if (info->IsDeleted() && !info->InUse()) { + for (ShaderInfoMap::iterator it = shader_infos_.begin(); + it != shader_infos_.end(); ++it) { + if (it->second->service_id() == info->service_id()) { + shader_infos_.erase(it); + return; + } + } + NOTREACHED(); + } +} + +void ShaderManager::MarkAsDeleted(ShaderManager::ShaderInfo* info) { + DCHECK(info); + info->MarkAsDeleted(); + RemoveShaderInfoIfUnused(info); +} + +void ShaderManager::UseShader(ShaderManager::ShaderInfo* info) { + DCHECK(info); + info->IncUseCount(); +} + +void ShaderManager::UnuseShader(ShaderManager::ShaderInfo* info) { + DCHECK(info); + info->DecUseCount(); + RemoveShaderInfoIfUnused(info); +} + } // namespace gles2 } // namespace gpu diff --git a/gpu/command_buffer/service/shader_manager.h b/gpu/command_buffer/service/shader_manager.h index 0b173c3..60aca6c 100644 --- a/gpu/command_buffer/service/shader_manager.h +++ b/gpu/command_buffer/service/shader_manager.h @@ -8,6 +8,7 @@ #include <map> #include <string> #include "base/basictypes.h" +#include "base/logging.h" #include "base/ref_counted.h" #include "gpu/command_buffer/service/gl_utils.h" #include "gpu/command_buffer/service/shader_translator.h" @@ -31,7 +32,8 @@ class ShaderManager { typedef ShaderTranslator::VariableInfo VariableInfo; explicit ShaderInfo(GLuint service_id, GLenum shader_type) - : service_id_(service_id), + : use_count_(0), + service_id_(service_id), shader_type_(shader_type), valid_(false) { } @@ -71,6 +73,11 @@ class ShaderManager { return service_id_ == 0; } + bool InUse() const { + DCHECK_GE(use_count_, 0); + return use_count_ != 0; + } + private: typedef ShaderTranslator::VariableMap VariableMap; @@ -78,10 +85,22 @@ class ShaderManager { friend class ShaderManager; ~ShaderInfo() { } + void IncUseCount() { + ++use_count_; + } + + void DecUseCount() { + --use_count_; + DCHECK_GE(use_count_, 0); + } + void MarkAsDeleted() { + DCHECK_NE(service_id_, 0u); service_id_ = 0; } + int use_count_; + // The shader this ShaderInfo is tracking. GLuint service_id_; // Type of shader - GL_VERTEX_SHADER or GL_FRAGMENT_SHADER. @@ -116,17 +135,25 @@ class ShaderManager { // exists. ShaderInfo* GetShaderInfo(GLuint client_id); - // Deletes the shader info for the given shader. - void RemoveShaderInfo(GLuint client_id); - // Gets a client id for a given service id. bool GetClientId(GLuint service_id, GLuint* client_id) const; + void MarkAsDeleted(ShaderInfo* info); + + // Mark a shader as used + void UseShader(ShaderInfo* info); + + // Unmark a shader as used. If it has been deleted and is not used + // then we free the info. + void UnuseShader(ShaderInfo* info); + private: // Info for each shader by service side shader Id. typedef std::map<GLuint, ShaderInfo::Ref> ShaderInfoMap; ShaderInfoMap shader_infos_; + void RemoveShaderInfoIfUnused(ShaderInfo* info); + DISALLOW_COPY_AND_ASSIGN(ShaderManager); }; diff --git a/gpu/command_buffer/service/shader_manager_unittest.cc b/gpu/command_buffer/service/shader_manager_unittest.cc index c069257..344cefd 100644 --- a/gpu/command_buffer/service/shader_manager_unittest.cc +++ b/gpu/command_buffer/service/shader_manager_unittest.cc @@ -44,31 +44,16 @@ TEST_F(ShaderManagerTest, Basic) { const GLuint kClient1Id = 1; const GLuint kService1Id = 11; const GLenum kShader1Type = GL_VERTEX_SHADER; - const std::string kClient1Source("hello world"); const GLuint kClient2Id = 2; // Check we can create shader. manager_.CreateShaderInfo(kClient1Id, kService1Id, kShader1Type); // Check shader got created. ShaderManager::ShaderInfo* info1 = manager_.GetShaderInfo(kClient1Id); ASSERT_TRUE(info1 != NULL); - EXPECT_EQ(kService1Id, info1->service_id()); - // Check if the shader has correct type. - EXPECT_EQ(kShader1Type, info1->shader_type()); - EXPECT_FALSE(info1->IsValid()); - EXPECT_STREQ("", info1->log_info().c_str()); - const char* kLog = "foo"; - info1->SetStatus(true, kLog, NULL); - EXPECT_TRUE(info1->IsValid()); - EXPECT_STREQ(kLog, info1->log_info().c_str()); - // Check we can set its source. - info1->Update(kClient1Source); - EXPECT_STREQ(kClient1Source.c_str(), info1->source().c_str()); // Check we get nothing for a non-existent shader. EXPECT_TRUE(manager_.GetShaderInfo(kClient2Id) == NULL); - // Check trying to a remove non-existent shaders does not crash. - manager_.RemoveShaderInfo(kClient2Id); // Check we can't get the shader after we remove it. - manager_.RemoveShaderInfo(kClient1Id); + manager_.MarkAsDeleted(info1); EXPECT_TRUE(manager_.GetShaderInfo(kClient1Id) == NULL); } @@ -90,6 +75,31 @@ TEST_F(ShaderManagerTest, Destroy) { ASSERT_TRUE(info1 == NULL); } +TEST_F(ShaderManagerTest, ShaderInfo) { + const GLuint kClient1Id = 1; + const GLuint kService1Id = 11; + const GLenum kShader1Type = GL_VERTEX_SHADER; + const std::string kClient1Source("hello world"); + // Check we can create shader. + manager_.CreateShaderInfo(kClient1Id, kService1Id, kShader1Type); + // Check shader got created. + ShaderManager::ShaderInfo* info1 = manager_.GetShaderInfo(kClient1Id); + ASSERT_TRUE(info1 != NULL); + EXPECT_EQ(kService1Id, info1->service_id()); + // Check if the shader has correct type. + EXPECT_EQ(kShader1Type, info1->shader_type()); + EXPECT_FALSE(info1->IsValid()); + EXPECT_FALSE(info1->InUse()); + EXPECT_STREQ("", info1->log_info().c_str()); + const char* kLog = "foo"; + info1->SetStatus(true, kLog, NULL); + EXPECT_TRUE(info1->IsValid()); + EXPECT_STREQ(kLog, info1->log_info().c_str()); + // Check we can set its source. + info1->Update(kClient1Source); + EXPECT_STREQ(kClient1Source.c_str(), info1->source().c_str()); +} + TEST_F(ShaderManagerTest, GetInfo) { const GLuint kClient1Id = 1; const GLuint kService1Id = 11; @@ -160,6 +170,50 @@ TEST_F(ShaderManagerTest, GetInfo) { } } +TEST_F(ShaderManagerTest, ShaderInfoUseCount) { + const GLuint kClient1Id = 1; + const GLuint kService1Id = 11; + const GLenum kShader1Type = GL_VERTEX_SHADER; + // Check we can create shader. + manager_.CreateShaderInfo(kClient1Id, kService1Id, kShader1Type); + // Check shader got created. + ShaderManager::ShaderInfo* info1 = manager_.GetShaderInfo(kClient1Id); + ASSERT_TRUE(info1 != NULL); + EXPECT_FALSE(info1->InUse()); + EXPECT_FALSE(info1->IsDeleted()); + manager_.UseShader(info1); + EXPECT_TRUE(info1->InUse()); + manager_.UseShader(info1); + EXPECT_TRUE(info1->InUse()); + manager_.MarkAsDeleted(info1); + EXPECT_TRUE(info1->IsDeleted()); + ShaderManager::ShaderInfo* info2 = manager_.GetShaderInfo(kClient1Id); + EXPECT_EQ(info1, info2); + manager_.UnuseShader(info1); + EXPECT_TRUE(info1->InUse()); + manager_.UnuseShader(info1); // this should delete the info. + info2 = manager_.GetShaderInfo(kClient1Id); + EXPECT_TRUE(info2 == NULL); + + manager_.CreateShaderInfo(kClient1Id, kService1Id, kShader1Type); + info1 = manager_.GetShaderInfo(kClient1Id); + ASSERT_TRUE(info1 != NULL); + EXPECT_FALSE(info1->InUse()); + manager_.UseShader(info1); + EXPECT_TRUE(info1->InUse()); + manager_.UseShader(info1); + EXPECT_TRUE(info1->InUse()); + manager_.UnuseShader(info1); + EXPECT_TRUE(info1->InUse()); + manager_.UnuseShader(info1); + EXPECT_FALSE(info1->InUse()); + info2 = manager_.GetShaderInfo(kClient1Id); + EXPECT_EQ(info1, info2); + manager_.MarkAsDeleted(info1); // this should delete the shader. + info2 = manager_.GetShaderInfo(kClient1Id); + EXPECT_TRUE(info2 == NULL); +} + } // namespace gles2 } // namespace gpu |