diff options
Diffstat (limited to 'chromium/gpu/command_buffer/service/texture_manager_unittest.cc')
-rw-r--r-- | chromium/gpu/command_buffer/service/texture_manager_unittest.cc | 2353 |
1 files changed, 2353 insertions, 0 deletions
diff --git a/chromium/gpu/command_buffer/service/texture_manager_unittest.cc b/chromium/gpu/command_buffer/service/texture_manager_unittest.cc new file mode 100644 index 00000000000..402cc8d0eff --- /dev/null +++ b/chromium/gpu/command_buffer/service/texture_manager_unittest.cc @@ -0,0 +1,2353 @@ +// 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/texture_manager.h" + +#include "base/memory/scoped_ptr.h" +#include "gpu/command_buffer/service/error_state_mock.h" +#include "gpu/command_buffer/service/feature_info.h" +#include "gpu/command_buffer/service/framebuffer_manager.h" +#include "gpu/command_buffer/service/gles2_cmd_decoder_mock.h" +#include "gpu/command_buffer/service/mailbox_manager.h" +#include "gpu/command_buffer/service/memory_tracking.h" +#include "gpu/command_buffer/service/mocks.h" +#include "gpu/command_buffer/service/test_helper.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/gl/gl_mock.h" + +using ::testing::AtLeast; +using ::testing::Pointee; +using ::testing::Return; +using ::testing::SetArgumentPointee; +using ::testing::StrictMock; +using ::testing::_; + +namespace gpu { +namespace gles2 { + +class TextureTestHelper { + public: + static bool IsNPOT(const Texture* texture) { + return texture->npot(); + } + static bool IsTextureComplete(const Texture* texture) { + return texture->texture_complete(); + } + static bool IsCubeComplete(const Texture* texture) { + return texture->cube_complete(); + } +}; + +class TextureManagerTest : public testing::Test { + public: + static const GLint kMaxTextureSize = 16; + static const GLint kMaxCubeMapTextureSize = 8; + static const GLint kMaxExternalTextureSize = 16; + static const GLint kMax2dLevels = 5; + static const GLint kMaxCubeMapLevels = 4; + static const GLint kMaxExternalLevels = 1; + + TextureManagerTest() + : feature_info_(new FeatureInfo()) { + } + + virtual ~TextureManagerTest() { + } + + protected: + virtual void SetUp() { + gl_.reset(new ::testing::StrictMock< ::gfx::MockGLInterface>()); + ::gfx::GLInterface::SetGLInterface(gl_.get()); + + manager_.reset(new TextureManager( + NULL, feature_info_.get(), + kMaxTextureSize, kMaxCubeMapTextureSize)); + TestHelper::SetupTextureManagerInitExpectations(gl_.get(), ""); + manager_->Initialize(); + error_state_.reset(new ::testing::StrictMock<gles2::MockErrorState>()); + } + + virtual void TearDown() { + manager_->Destroy(false); + manager_.reset(); + ::gfx::GLInterface::SetGLInterface(NULL); + gl_.reset(); + } + + void SetParameter( + TextureRef* texture_ref, GLenum pname, GLint value, GLenum error) { + TestHelper::SetTexParameterWithExpectations( + gl_.get(), error_state_.get(), manager_.get(), + texture_ref, pname, value, error); + } + + // Use StrictMock to make 100% sure we know how GL will be called. + scoped_ptr< ::testing::StrictMock< ::gfx::MockGLInterface> > gl_; + scoped_refptr<FeatureInfo> feature_info_; + scoped_ptr<TextureManager> manager_; + scoped_ptr<MockErrorState> error_state_; +}; + +// GCC requires these declarations, but MSVC requires they not be present +#ifndef COMPILER_MSVC +const GLint TextureManagerTest::kMaxTextureSize; +const GLint TextureManagerTest::kMaxCubeMapTextureSize; +const GLint TextureManagerTest::kMaxExternalTextureSize; +const GLint TextureManagerTest::kMax2dLevels; +const GLint TextureManagerTest::kMaxCubeMapLevels; +const GLint TextureManagerTest::kMaxExternalLevels; +#endif + +TEST_F(TextureManagerTest, Basic) { + const GLuint kClient1Id = 1; + const GLuint kService1Id = 11; + const GLuint kClient2Id = 2; + EXPECT_FALSE(manager_->HaveUnrenderableTextures()); + EXPECT_FALSE(manager_->HaveUnsafeTextures()); + EXPECT_FALSE(manager_->HaveUnclearedMips()); + // Check we can create texture. + manager_->CreateTexture(kClient1Id, kService1Id); + // Check texture got created. + scoped_refptr<TextureRef> texture = manager_->GetTexture(kClient1Id); + ASSERT_TRUE(texture.get() != NULL); + EXPECT_EQ(kService1Id, texture->service_id()); + EXPECT_EQ(kClient1Id, texture->client_id()); + EXPECT_EQ(texture->texture(), manager_->GetTextureForServiceId( + texture->service_id())); + // Check we get nothing for a non-existent texture. + EXPECT_TRUE(manager_->GetTexture(kClient2Id) == NULL); + // Check trying to a remove non-existent textures does not crash. + manager_->RemoveTexture(kClient2Id); + // Check that it gets deleted when the last reference is released. + EXPECT_CALL(*gl_, DeleteTextures(1, ::testing::Pointee(kService1Id))) + .Times(1) + .RetiresOnSaturation(); + // Check we can't get the texture after we remove it. + manager_->RemoveTexture(kClient1Id); + EXPECT_TRUE(manager_->GetTexture(kClient1Id) == NULL); + EXPECT_EQ(0u, texture->client_id()); +} + +TEST_F(TextureManagerTest, SetParameter) { + const GLuint kClient1Id = 1; + const GLuint kService1Id = 11; + // Check we can create texture. + manager_->CreateTexture(kClient1Id, kService1Id); + // Check texture got created. + TextureRef* texture_ref = manager_->GetTexture(kClient1Id); + ASSERT_TRUE(texture_ref != NULL); + Texture* texture = texture_ref->texture(); + manager_->SetTarget(texture_ref, GL_TEXTURE_2D); + SetParameter(texture_ref, GL_TEXTURE_MIN_FILTER, GL_NEAREST, GL_NO_ERROR); + EXPECT_EQ(static_cast<GLenum>(GL_NEAREST), texture->min_filter()); + SetParameter(texture_ref, GL_TEXTURE_MAG_FILTER, GL_NEAREST, GL_NO_ERROR); + EXPECT_EQ(static_cast<GLenum>(GL_NEAREST), texture->mag_filter()); + SetParameter(texture_ref, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE, GL_NO_ERROR); + EXPECT_EQ(static_cast<GLenum>(GL_CLAMP_TO_EDGE), texture->wrap_s()); + SetParameter(texture_ref, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE, GL_NO_ERROR); + EXPECT_EQ(static_cast<GLenum>(GL_CLAMP_TO_EDGE), texture->wrap_t()); + SetParameter(texture_ref, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1, GL_NO_ERROR); + SetParameter(texture_ref, GL_TEXTURE_MAX_ANISOTROPY_EXT, 2, GL_NO_ERROR); + SetParameter( + texture_ref, GL_TEXTURE_MIN_FILTER, GL_CLAMP_TO_EDGE, GL_INVALID_ENUM); + EXPECT_EQ(static_cast<GLenum>(GL_NEAREST), texture->min_filter()); + SetParameter( + texture_ref, GL_TEXTURE_MAG_FILTER, GL_CLAMP_TO_EDGE, GL_INVALID_ENUM); + EXPECT_EQ(static_cast<GLenum>(GL_NEAREST), texture->min_filter()); + SetParameter(texture_ref, GL_TEXTURE_WRAP_S, GL_NEAREST, GL_INVALID_ENUM); + EXPECT_EQ(static_cast<GLenum>(GL_CLAMP_TO_EDGE), texture->wrap_s()); + SetParameter(texture_ref, GL_TEXTURE_WRAP_T, GL_NEAREST, GL_INVALID_ENUM); + EXPECT_EQ(static_cast<GLenum>(GL_CLAMP_TO_EDGE), texture->wrap_t()); + SetParameter(texture_ref, GL_TEXTURE_MAX_ANISOTROPY_EXT, 0, GL_INVALID_VALUE); +} + +TEST_F(TextureManagerTest, TextureUsageExt) { + TestHelper::SetupTextureManagerInitExpectations(gl_.get(), + "GL_ANGLE_texture_usage"); + TextureManager manager( + NULL, feature_info_.get(), kMaxTextureSize, kMaxCubeMapTextureSize); + manager.Initialize(); + const GLuint kClient1Id = 1; + const GLuint kService1Id = 11; + // Check we can create texture. + manager.CreateTexture(kClient1Id, kService1Id); + // Check texture got created. + TextureRef* texture_ref = manager.GetTexture(kClient1Id); + ASSERT_TRUE(texture_ref != NULL); + TestHelper::SetTexParameterWithExpectations( + gl_.get(), error_state_.get(), &manager, texture_ref, + GL_TEXTURE_USAGE_ANGLE, GL_FRAMEBUFFER_ATTACHMENT_ANGLE,GL_NO_ERROR); + EXPECT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_ATTACHMENT_ANGLE), + texture_ref->texture()->usage()); + manager.Destroy(false); +} + +TEST_F(TextureManagerTest, Destroy) { + const GLuint kClient1Id = 1; + const GLuint kService1Id = 11; + TestHelper::SetupTextureManagerInitExpectations(gl_.get(), ""); + TextureManager manager( + NULL, feature_info_.get(), kMaxTextureSize, kMaxCubeMapTextureSize); + manager.Initialize(); + // Check we can create texture. + manager.CreateTexture(kClient1Id, kService1Id); + // Check texture got created. + TextureRef* texture = manager.GetTexture(kClient1Id); + ASSERT_TRUE(texture != NULL); + EXPECT_CALL(*gl_, DeleteTextures(1, ::testing::Pointee(kService1Id))) + .Times(1) + .RetiresOnSaturation(); + TestHelper::SetupTextureManagerDestructionExpectations(gl_.get(), ""); + manager.Destroy(true); + // Check that resources got freed. + texture = manager.GetTexture(kClient1Id); + ASSERT_TRUE(texture == NULL); +} + +TEST_F(TextureManagerTest, MaxValues) { + // Check we get the right values for the max sizes. + EXPECT_EQ(kMax2dLevels, manager_->MaxLevelsForTarget(GL_TEXTURE_2D)); + EXPECT_EQ(kMaxCubeMapLevels, + manager_->MaxLevelsForTarget(GL_TEXTURE_CUBE_MAP)); + EXPECT_EQ(kMaxCubeMapLevels, + manager_->MaxLevelsForTarget(GL_TEXTURE_CUBE_MAP_POSITIVE_X)); + EXPECT_EQ(kMaxCubeMapLevels, + manager_->MaxLevelsForTarget(GL_TEXTURE_CUBE_MAP_NEGATIVE_X)); + EXPECT_EQ(kMaxCubeMapLevels, + manager_->MaxLevelsForTarget(GL_TEXTURE_CUBE_MAP_POSITIVE_Y)); + EXPECT_EQ(kMaxCubeMapLevels, + manager_->MaxLevelsForTarget(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y)); + EXPECT_EQ(kMaxCubeMapLevels, + manager_->MaxLevelsForTarget(GL_TEXTURE_CUBE_MAP_POSITIVE_Z)); + EXPECT_EQ(kMaxCubeMapLevels, + manager_->MaxLevelsForTarget(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z)); + EXPECT_EQ(kMaxExternalLevels, + manager_->MaxLevelsForTarget(GL_TEXTURE_EXTERNAL_OES)); + EXPECT_EQ(kMaxTextureSize, manager_->MaxSizeForTarget(GL_TEXTURE_2D)); + EXPECT_EQ(kMaxCubeMapTextureSize, + manager_->MaxSizeForTarget(GL_TEXTURE_CUBE_MAP)); + EXPECT_EQ(kMaxCubeMapTextureSize, + manager_->MaxSizeForTarget(GL_TEXTURE_CUBE_MAP_POSITIVE_X)); + EXPECT_EQ(kMaxCubeMapTextureSize, + manager_->MaxSizeForTarget(GL_TEXTURE_CUBE_MAP_NEGATIVE_X)); + EXPECT_EQ(kMaxCubeMapTextureSize, + manager_->MaxSizeForTarget(GL_TEXTURE_CUBE_MAP_POSITIVE_Y)); + EXPECT_EQ(kMaxCubeMapTextureSize, + manager_->MaxSizeForTarget(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y)); + EXPECT_EQ(kMaxCubeMapTextureSize, + manager_->MaxSizeForTarget(GL_TEXTURE_CUBE_MAP_POSITIVE_Z)); + EXPECT_EQ(kMaxCubeMapTextureSize, + manager_->MaxSizeForTarget(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z)); + EXPECT_EQ(kMaxExternalTextureSize, + manager_->MaxSizeForTarget(GL_TEXTURE_EXTERNAL_OES)); +} + +TEST_F(TextureManagerTest, ValidForTarget) { + // check 2d + EXPECT_TRUE(manager_->ValidForTarget( + GL_TEXTURE_2D, 0, kMaxTextureSize, kMaxTextureSize, 1)); + EXPECT_TRUE(manager_->ValidForTarget( + GL_TEXTURE_2D, kMax2dLevels - 1, 1, 1, 1)); + EXPECT_FALSE(manager_->ValidForTarget( + GL_TEXTURE_2D, kMax2dLevels - 1, 1, 2, 1)); + EXPECT_FALSE(manager_->ValidForTarget( + GL_TEXTURE_2D, kMax2dLevels - 1, 2, 1, 1)); + // check level out of range. + EXPECT_FALSE(manager_->ValidForTarget( + GL_TEXTURE_2D, kMax2dLevels, kMaxTextureSize, 1, 1)); + // check has depth. + EXPECT_FALSE(manager_->ValidForTarget( + GL_TEXTURE_2D, kMax2dLevels, kMaxTextureSize, 1, 2)); + // Check NPOT width on level 0 + EXPECT_TRUE(manager_->ValidForTarget(GL_TEXTURE_2D, 0, 5, 2, 1)); + // Check NPOT height on level 0 + EXPECT_TRUE(manager_->ValidForTarget(GL_TEXTURE_2D, 0, 2, 5, 1)); + // Check NPOT width on level 1 + EXPECT_FALSE(manager_->ValidForTarget(GL_TEXTURE_2D, 1, 5, 2, 1)); + // Check NPOT height on level 1 + EXPECT_FALSE(manager_->ValidForTarget(GL_TEXTURE_2D, 1, 2, 5, 1)); + + // check cube + EXPECT_TRUE(manager_->ValidForTarget( + GL_TEXTURE_CUBE_MAP, 0, + kMaxCubeMapTextureSize, kMaxCubeMapTextureSize, 1)); + EXPECT_TRUE(manager_->ValidForTarget( + GL_TEXTURE_CUBE_MAP, kMaxCubeMapLevels - 1, 1, 1, 1)); + EXPECT_FALSE(manager_->ValidForTarget( + GL_TEXTURE_CUBE_MAP, kMaxCubeMapLevels - 1, 2, 2, 1)); + // check level out of range. + EXPECT_FALSE(manager_->ValidForTarget( + GL_TEXTURE_CUBE_MAP, kMaxCubeMapLevels, + kMaxCubeMapTextureSize, 1, 1)); + // check not square. + EXPECT_FALSE(manager_->ValidForTarget( + GL_TEXTURE_CUBE_MAP, kMaxCubeMapLevels, + kMaxCubeMapTextureSize, 1, 1)); + // check has depth. + EXPECT_FALSE(manager_->ValidForTarget( + GL_TEXTURE_CUBE_MAP, kMaxCubeMapLevels, + kMaxCubeMapTextureSize, 1, 2)); + + for (GLint level = 0; level < kMax2dLevels; ++level) { + EXPECT_TRUE(manager_->ValidForTarget( + GL_TEXTURE_2D, level, kMaxTextureSize >> level, 1, 1)); + EXPECT_TRUE(manager_->ValidForTarget( + GL_TEXTURE_2D, level, 1, kMaxTextureSize >> level, 1)); + EXPECT_FALSE(manager_->ValidForTarget( + GL_TEXTURE_2D, level, (kMaxTextureSize >> level) + 1, 1, 1)); + EXPECT_FALSE(manager_->ValidForTarget( + GL_TEXTURE_2D, level, 1, (kMaxTextureSize >> level) + 1, 1)); + } + + for (GLint level = 0; level < kMaxCubeMapLevels; ++level) { + EXPECT_TRUE(manager_->ValidForTarget( + GL_TEXTURE_CUBE_MAP, level, + kMaxCubeMapTextureSize >> level, + kMaxCubeMapTextureSize >> level, + 1)); + EXPECT_FALSE(manager_->ValidForTarget( + GL_TEXTURE_CUBE_MAP, level, + (kMaxCubeMapTextureSize >> level) * 2, + (kMaxCubeMapTextureSize >> level) * 2, + 1)); + } +} + +TEST_F(TextureManagerTest, ValidForTargetNPOT) { + TestHelper::SetupFeatureInfoInitExpectations( + gl_.get(), "GL_OES_texture_npot"); + scoped_refptr<FeatureInfo> feature_info(new FeatureInfo()); + feature_info->Initialize(NULL); + TextureManager manager( + NULL, feature_info.get(), kMaxTextureSize, kMaxCubeMapTextureSize); + // Check NPOT width on level 0 + EXPECT_TRUE(manager.ValidForTarget(GL_TEXTURE_2D, 0, 5, 2, 1)); + // Check NPOT height on level 0 + EXPECT_TRUE(manager.ValidForTarget(GL_TEXTURE_2D, 0, 2, 5, 1)); + // Check NPOT width on level 1 + EXPECT_TRUE(manager.ValidForTarget(GL_TEXTURE_2D, 1, 5, 2, 1)); + // Check NPOT height on level 1 + EXPECT_TRUE(manager.ValidForTarget(GL_TEXTURE_2D, 1, 2, 5, 1)); + manager.Destroy(false); +} + +class TextureTestBase : public testing::Test { + public: + static const GLint kMaxTextureSize = 16; + static const GLint kMaxCubeMapTextureSize = 8; + static const GLint kMax2dLevels = 5; + static const GLint kMaxCubeMapLevels = 4; + static const GLuint kClient1Id = 1; + static const GLuint kService1Id = 11; + + TextureTestBase() + : feature_info_(new FeatureInfo()) { + } + virtual ~TextureTestBase() { + texture_ref_ = NULL; + } + + protected: + void SetUpBase(MemoryTracker* memory_tracker, std::string extensions) { + gl_.reset(new ::testing::StrictMock< ::gfx::MockGLInterface>()); + ::gfx::GLInterface::SetGLInterface(gl_.get()); + + if (!extensions.empty()) { + TestHelper::SetupFeatureInfoInitExpectations(gl_.get(), + extensions.c_str()); + feature_info_->Initialize(NULL); + } + + manager_.reset(new TextureManager( + memory_tracker, feature_info_.get(), + kMaxTextureSize, kMaxCubeMapTextureSize)); + decoder_.reset(new ::testing::StrictMock<gles2::MockGLES2Decoder>()); + error_state_.reset(new ::testing::StrictMock<gles2::MockErrorState>()); + manager_->CreateTexture(kClient1Id, kService1Id); + texture_ref_ = manager_->GetTexture(kClient1Id); + ASSERT_TRUE(texture_ref_.get() != NULL); + } + + virtual void TearDown() { + if (texture_ref_.get()) { + // If it's not in the manager then setting texture_ref_ to NULL will + // delete the texture. + if (!texture_ref_->client_id()) { + // Check that it gets deleted when the last reference is released. + EXPECT_CALL(*gl_, + DeleteTextures(1, ::testing::Pointee(texture_ref_->service_id()))) + .Times(1) + .RetiresOnSaturation(); + } + texture_ref_ = NULL; + } + manager_->Destroy(false); + manager_.reset(); + ::gfx::GLInterface::SetGLInterface(NULL); + gl_.reset(); + } + + void SetParameter( + TextureRef* texture_ref, GLenum pname, GLint value, GLenum error) { + TestHelper::SetTexParameterWithExpectations( + gl_.get(), error_state_.get(), manager_.get(), + texture_ref, pname, value, error); + } + + scoped_ptr<MockGLES2Decoder> decoder_; + scoped_ptr<MockErrorState> error_state_; + // Use StrictMock to make 100% sure we know how GL will be called. + scoped_ptr< ::testing::StrictMock< ::gfx::MockGLInterface> > gl_; + scoped_refptr<FeatureInfo> feature_info_; + scoped_ptr<TextureManager> manager_; + scoped_refptr<TextureRef> texture_ref_; +}; + +class TextureTest : public TextureTestBase { + protected: + virtual void SetUp() { + SetUpBase(NULL, std::string()); + } +}; + +class TextureMemoryTrackerTest : public TextureTestBase { + protected: + virtual void SetUp() { + mock_memory_tracker_ = new StrictMock<MockMemoryTracker>(); + SetUpBase(mock_memory_tracker_.get(), std::string()); + } + + scoped_refptr<MockMemoryTracker> mock_memory_tracker_; +}; + +#define EXPECT_MEMORY_ALLOCATION_CHANGE(old_size, new_size, pool) \ + EXPECT_CALL(*mock_memory_tracker_.get(), \ + TrackMemoryAllocatedChange(old_size, new_size, pool)) \ + .Times(1).RetiresOnSaturation() + +TEST_F(TextureTest, Basic) { + Texture* texture = texture_ref_->texture(); + EXPECT_EQ(0u, texture->target()); + EXPECT_FALSE(TextureTestHelper::IsTextureComplete(texture)); + EXPECT_FALSE(TextureTestHelper::IsCubeComplete(texture)); + EXPECT_FALSE(manager_->CanGenerateMipmaps(texture_ref_.get())); + EXPECT_FALSE(TextureTestHelper::IsNPOT(texture)); + EXPECT_EQ(0, texture->num_uncleared_mips()); + EXPECT_TRUE(manager_->CanRender(texture_ref_.get())); + EXPECT_TRUE(texture->SafeToRenderFrom()); + EXPECT_FALSE(texture->IsImmutable()); + EXPECT_EQ(static_cast<GLenum>(GL_NEAREST_MIPMAP_LINEAR), + texture->min_filter()); + EXPECT_EQ(static_cast<GLenum>(GL_LINEAR), texture->mag_filter()); + EXPECT_EQ(static_cast<GLenum>(GL_REPEAT), texture->wrap_s()); + EXPECT_EQ(static_cast<GLenum>(GL_REPEAT), texture->wrap_t()); + EXPECT_FALSE(manager_->HaveUnrenderableTextures()); + EXPECT_FALSE(manager_->HaveUnsafeTextures()); + EXPECT_EQ(0u, texture->estimated_size()); +} + +TEST_F(TextureTest, SetTargetTexture2D) { + Texture* texture = texture_ref_->texture(); + manager_->SetTarget(texture_ref_.get(), GL_TEXTURE_2D); + EXPECT_FALSE(TextureTestHelper::IsTextureComplete(texture)); + EXPECT_FALSE(TextureTestHelper::IsCubeComplete(texture)); + EXPECT_FALSE(manager_->CanGenerateMipmaps(texture_ref_.get())); + EXPECT_FALSE(TextureTestHelper::IsNPOT(texture)); + EXPECT_FALSE(manager_->CanRender(texture_ref_.get())); + EXPECT_TRUE(texture->SafeToRenderFrom()); + EXPECT_FALSE(texture->IsImmutable()); +} + +TEST_F(TextureTest, SetTargetTextureExternalOES) { + Texture* texture = texture_ref_->texture(); + manager_->SetTarget(texture_ref_.get(), GL_TEXTURE_EXTERNAL_OES); + EXPECT_FALSE(TextureTestHelper::IsTextureComplete(texture)); + EXPECT_FALSE(TextureTestHelper::IsCubeComplete(texture)); + EXPECT_FALSE(manager_->CanGenerateMipmaps(texture_ref_.get())); + EXPECT_TRUE(TextureTestHelper::IsNPOT(texture)); + EXPECT_FALSE(manager_->CanRender(texture_ref_.get())); + EXPECT_TRUE(texture->SafeToRenderFrom()); + EXPECT_TRUE(texture->IsImmutable()); + manager_->SetStreamTexture(texture_ref_.get(), true); + EXPECT_TRUE(manager_->CanRender(texture_ref_.get())); +} + +TEST_F(TextureTest, ZeroSizeCanNotRender) { + manager_->SetTarget(texture_ref_.get(), GL_TEXTURE_2D); + EXPECT_FALSE(manager_->CanRender(texture_ref_.get())); + manager_->SetLevelInfo(texture_ref_.get(), + GL_TEXTURE_2D, + 0, + GL_RGBA, + 1, + 1, + 1, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + true); + EXPECT_TRUE(manager_->CanRender(texture_ref_.get())); + manager_->SetLevelInfo(texture_ref_.get(), + GL_TEXTURE_2D, + 0, + GL_RGBA, + 0, + 0, + 1, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + true); + EXPECT_FALSE(manager_->CanRender(texture_ref_.get())); +} + +TEST_F(TextureTest, EstimatedSize) { + manager_->SetTarget(texture_ref_.get(), GL_TEXTURE_2D); + manager_->SetLevelInfo(texture_ref_.get(), + GL_TEXTURE_2D, + 0, + GL_RGBA, + 8, + 4, + 1, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + true); + EXPECT_EQ(8u * 4u * 4u, texture_ref_->texture()->estimated_size()); + manager_->SetLevelInfo(texture_ref_.get(), + GL_TEXTURE_2D, + 2, + GL_RGBA, + 8, + 4, + 1, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + true); + EXPECT_EQ(8u * 4u * 4u * 2u, texture_ref_->texture()->estimated_size()); +} + +TEST_F(TextureMemoryTrackerTest, EstimatedSize) { + manager_->SetTarget(texture_ref_.get(), GL_TEXTURE_2D); + EXPECT_MEMORY_ALLOCATION_CHANGE(0, 128, MemoryTracker::kUnmanaged); + manager_->SetLevelInfo(texture_ref_.get(), + GL_TEXTURE_2D, + 0, + GL_RGBA, + 8, + 4, + 1, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + true); + EXPECT_MEMORY_ALLOCATION_CHANGE(128, 0, MemoryTracker::kUnmanaged); + EXPECT_MEMORY_ALLOCATION_CHANGE(0, 256, MemoryTracker::kUnmanaged); + manager_->SetLevelInfo(texture_ref_.get(), + GL_TEXTURE_2D, + 2, + GL_RGBA, + 8, + 4, + 1, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + true); + // Add expectation for texture deletion. + EXPECT_MEMORY_ALLOCATION_CHANGE(256, 0, MemoryTracker::kUnmanaged); + EXPECT_MEMORY_ALLOCATION_CHANGE(0, 0, MemoryTracker::kUnmanaged); +} + +TEST_F(TextureMemoryTrackerTest, SetParameterPool) { + manager_->SetTarget(texture_ref_.get(), GL_TEXTURE_2D); + EXPECT_MEMORY_ALLOCATION_CHANGE(0, 128, MemoryTracker::kUnmanaged); + manager_->SetLevelInfo(texture_ref_.get(), + GL_TEXTURE_2D, + 0, + GL_RGBA, + 8, + 4, + 1, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + true); + EXPECT_MEMORY_ALLOCATION_CHANGE(128, 0, MemoryTracker::kUnmanaged); + EXPECT_MEMORY_ALLOCATION_CHANGE(0, 128, MemoryTracker::kManaged); + SetParameter(texture_ref_.get(), + GL_TEXTURE_POOL_CHROMIUM, + GL_TEXTURE_POOL_MANAGED_CHROMIUM, + GL_NO_ERROR); + // Add expectation for texture deletion. + EXPECT_MEMORY_ALLOCATION_CHANGE(128, 0, MemoryTracker::kManaged); + EXPECT_MEMORY_ALLOCATION_CHANGE(0, 0, MemoryTracker::kUnmanaged); + EXPECT_MEMORY_ALLOCATION_CHANGE(0, 0, MemoryTracker::kManaged); +} + +TEST_F(TextureTest, POT2D) { + manager_->SetTarget(texture_ref_.get(), GL_TEXTURE_2D); + Texture* texture = texture_ref_->texture(); + EXPECT_EQ(static_cast<GLenum>(GL_TEXTURE_2D), texture->target()); + // Check Setting level 0 to POT + manager_->SetLevelInfo(texture_ref_.get(), + GL_TEXTURE_2D, + 0, + GL_RGBA, + 4, + 4, + 1, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + true); + EXPECT_FALSE(TextureTestHelper::IsNPOT(texture)); + EXPECT_FALSE(TextureTestHelper::IsTextureComplete(texture)); + EXPECT_FALSE(manager_->CanRender(texture_ref_.get())); + EXPECT_EQ(0, texture->num_uncleared_mips()); + EXPECT_TRUE(manager_->HaveUnrenderableTextures()); + // Set filters to something that will work with a single mip. + SetParameter( + texture_ref_.get(), GL_TEXTURE_MIN_FILTER, GL_LINEAR, GL_NO_ERROR); + EXPECT_TRUE(manager_->CanRender(texture_ref_.get())); + EXPECT_FALSE(manager_->HaveUnrenderableTextures()); + // Set them back. + SetParameter(texture_ref_.get(), + GL_TEXTURE_MIN_FILTER, + GL_LINEAR_MIPMAP_LINEAR, + GL_NO_ERROR); + EXPECT_TRUE(manager_->HaveUnrenderableTextures()); + + EXPECT_TRUE(manager_->CanGenerateMipmaps(texture_ref_.get())); + // Make mips. + EXPECT_TRUE(manager_->MarkMipmapsGenerated(texture_ref_.get())); + EXPECT_TRUE(TextureTestHelper::IsTextureComplete(texture)); + EXPECT_TRUE(manager_->CanRender(texture_ref_.get())); + EXPECT_FALSE(manager_->HaveUnrenderableTextures()); + // Change a mip. + manager_->SetLevelInfo(texture_ref_.get(), + GL_TEXTURE_2D, + 1, + GL_RGBA, + 4, + 4, + 1, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + true); + EXPECT_FALSE(TextureTestHelper::IsNPOT(texture)); + EXPECT_FALSE(TextureTestHelper::IsTextureComplete(texture)); + EXPECT_TRUE(manager_->CanGenerateMipmaps(texture_ref_.get())); + EXPECT_FALSE(manager_->CanRender(texture_ref_.get())); + EXPECT_TRUE(manager_->HaveUnrenderableTextures()); + // Set a level past the number of mips that would get generated. + manager_->SetLevelInfo(texture_ref_.get(), + GL_TEXTURE_2D, + 3, + GL_RGBA, + 4, + 4, + 1, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + true); + EXPECT_TRUE(manager_->CanGenerateMipmaps(texture_ref_.get())); + // Make mips. + EXPECT_TRUE(manager_->MarkMipmapsGenerated(texture_ref_.get())); + EXPECT_TRUE(manager_->CanRender(texture_ref_.get())); + EXPECT_TRUE(TextureTestHelper::IsTextureComplete(texture)); + EXPECT_FALSE(manager_->HaveUnrenderableTextures()); +} + +TEST_F(TextureMemoryTrackerTest, MarkMipmapsGenerated) { + manager_->SetTarget(texture_ref_.get(), GL_TEXTURE_2D); + EXPECT_MEMORY_ALLOCATION_CHANGE(0, 64, MemoryTracker::kUnmanaged); + manager_->SetLevelInfo(texture_ref_.get(), + GL_TEXTURE_2D, + 0, + GL_RGBA, + 4, + 4, + 1, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + true); + EXPECT_MEMORY_ALLOCATION_CHANGE(64, 0, MemoryTracker::kUnmanaged); + EXPECT_MEMORY_ALLOCATION_CHANGE(0, 84, MemoryTracker::kUnmanaged); + EXPECT_TRUE(manager_->MarkMipmapsGenerated(texture_ref_.get())); + EXPECT_MEMORY_ALLOCATION_CHANGE(84, 0, MemoryTracker::kUnmanaged); + EXPECT_MEMORY_ALLOCATION_CHANGE(0, 0, MemoryTracker::kUnmanaged); +} + +TEST_F(TextureTest, UnusedMips) { + manager_->SetTarget(texture_ref_.get(), GL_TEXTURE_2D); + Texture* texture = texture_ref_->texture(); + EXPECT_EQ(static_cast<GLenum>(GL_TEXTURE_2D), texture->target()); + // Set level zero to large size. + manager_->SetLevelInfo(texture_ref_.get(), + GL_TEXTURE_2D, + 0, + GL_RGBA, + 4, + 4, + 1, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + true); + EXPECT_TRUE(manager_->MarkMipmapsGenerated(texture_ref_.get())); + EXPECT_FALSE(TextureTestHelper::IsNPOT(texture)); + EXPECT_TRUE(TextureTestHelper::IsTextureComplete(texture)); + EXPECT_TRUE(manager_->CanRender(texture_ref_.get())); + EXPECT_FALSE(manager_->HaveUnrenderableTextures()); + // Set level zero to large smaller (levels unused mips) + manager_->SetLevelInfo(texture_ref_.get(), + GL_TEXTURE_2D, + 0, + GL_RGBA, + 2, + 2, + 1, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + true); + EXPECT_TRUE(manager_->MarkMipmapsGenerated(texture_ref_.get())); + EXPECT_FALSE(TextureTestHelper::IsNPOT(texture)); + EXPECT_TRUE(TextureTestHelper::IsTextureComplete(texture)); + EXPECT_TRUE(manager_->CanRender(texture_ref_.get())); + EXPECT_FALSE(manager_->HaveUnrenderableTextures()); + // Set an unused level to some size + manager_->SetLevelInfo(texture_ref_.get(), + GL_TEXTURE_2D, + 4, + GL_RGBA, + 16, + 16, + 1, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + true); + EXPECT_FALSE(TextureTestHelper::IsNPOT(texture)); + EXPECT_TRUE(TextureTestHelper::IsTextureComplete(texture)); + EXPECT_TRUE(manager_->CanRender(texture_ref_.get())); + EXPECT_FALSE(manager_->HaveUnrenderableTextures()); +} + +TEST_F(TextureTest, NPOT2D) { + manager_->SetTarget(texture_ref_.get(), GL_TEXTURE_2D); + Texture* texture = texture_ref_->texture(); + EXPECT_EQ(static_cast<GLenum>(GL_TEXTURE_2D), texture->target()); + // Check Setting level 0 to NPOT + manager_->SetLevelInfo(texture_ref_.get(), + GL_TEXTURE_2D, + 0, + GL_RGBA, + 4, + 5, + 1, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + true); + EXPECT_TRUE(TextureTestHelper::IsNPOT(texture)); + EXPECT_FALSE(TextureTestHelper::IsTextureComplete(texture)); + EXPECT_FALSE(manager_->CanGenerateMipmaps(texture_ref_.get())); + EXPECT_FALSE(manager_->CanRender(texture_ref_.get())); + EXPECT_TRUE(manager_->HaveUnrenderableTextures()); + SetParameter( + texture_ref_.get(), GL_TEXTURE_MIN_FILTER, GL_LINEAR, GL_NO_ERROR); + EXPECT_FALSE(manager_->CanRender(texture_ref_.get())); + EXPECT_TRUE(manager_->HaveUnrenderableTextures()); + SetParameter( + texture_ref_.get(), GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE, GL_NO_ERROR); + EXPECT_FALSE(manager_->CanRender(texture_ref_.get())); + EXPECT_TRUE(manager_->HaveUnrenderableTextures()); + SetParameter( + texture_ref_.get(), GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE, GL_NO_ERROR); + EXPECT_TRUE(manager_->CanRender(texture_ref_.get())); + EXPECT_FALSE(manager_->HaveUnrenderableTextures()); + // Change it to POT. + manager_->SetLevelInfo(texture_ref_.get(), + GL_TEXTURE_2D, + 0, + GL_RGBA, + 4, + 4, + 1, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + true); + EXPECT_FALSE(TextureTestHelper::IsNPOT(texture)); + EXPECT_FALSE(TextureTestHelper::IsTextureComplete(texture)); + EXPECT_TRUE(manager_->CanGenerateMipmaps(texture_ref_.get())); + EXPECT_FALSE(manager_->HaveUnrenderableTextures()); +} + +TEST_F(TextureTest, NPOT2DNPOTOK) { + TestHelper::SetupFeatureInfoInitExpectations( + gl_.get(), "GL_OES_texture_npot"); + scoped_refptr<FeatureInfo> feature_info(new FeatureInfo()); + feature_info->Initialize(NULL); + TextureManager manager( + NULL, feature_info.get(), kMaxTextureSize, kMaxCubeMapTextureSize); + manager.CreateTexture(kClient1Id, kService1Id); + TextureRef* texture_ref = manager.GetTexture(kClient1Id); + ASSERT_TRUE(texture_ref != NULL); + Texture* texture = texture_ref->texture(); + + manager.SetTarget(texture_ref, GL_TEXTURE_2D); + EXPECT_EQ(static_cast<GLenum>(GL_TEXTURE_2D), texture->target()); + // Check Setting level 0 to NPOT + manager.SetLevelInfo(texture_ref, + GL_TEXTURE_2D, 0, GL_RGBA, 4, 5, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, true); + EXPECT_TRUE(TextureTestHelper::IsNPOT(texture)); + EXPECT_FALSE(TextureTestHelper::IsTextureComplete(texture)); + EXPECT_TRUE(manager.CanGenerateMipmaps(texture_ref)); + EXPECT_FALSE(manager.CanRender(texture_ref)); + EXPECT_TRUE(manager.HaveUnrenderableTextures()); + EXPECT_TRUE(manager.MarkMipmapsGenerated(texture_ref)); + EXPECT_TRUE(TextureTestHelper::IsTextureComplete(texture)); + EXPECT_TRUE(manager.CanRender(texture_ref)); + EXPECT_FALSE(manager.HaveUnrenderableTextures()); + manager.Destroy(false); +} + +TEST_F(TextureTest, POTCubeMap) { + manager_->SetTarget(texture_ref_.get(), GL_TEXTURE_CUBE_MAP); + Texture* texture = texture_ref_->texture(); + EXPECT_EQ(static_cast<GLenum>(GL_TEXTURE_CUBE_MAP), texture->target()); + // Check Setting level 0 each face to POT + manager_->SetLevelInfo(texture_ref_.get(), + GL_TEXTURE_CUBE_MAP_POSITIVE_X, + 0, + GL_RGBA, + 4, + 4, + 1, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + true); + EXPECT_FALSE(TextureTestHelper::IsNPOT(texture)); + EXPECT_FALSE(TextureTestHelper::IsTextureComplete(texture)); + EXPECT_FALSE(TextureTestHelper::IsCubeComplete(texture)); + EXPECT_FALSE(manager_->CanGenerateMipmaps(texture_ref_.get())); + EXPECT_FALSE(manager_->CanRender(texture_ref_.get())); + EXPECT_TRUE(manager_->HaveUnrenderableTextures()); + manager_->SetLevelInfo(texture_ref_.get(), + GL_TEXTURE_CUBE_MAP_NEGATIVE_X, + 0, + GL_RGBA, + 4, + 4, + 1, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + true); + EXPECT_FALSE(TextureTestHelper::IsNPOT(texture)); + EXPECT_FALSE(TextureTestHelper::IsTextureComplete(texture)); + EXPECT_FALSE(TextureTestHelper::IsCubeComplete(texture)); + EXPECT_FALSE(manager_->CanGenerateMipmaps(texture_ref_.get())); + EXPECT_FALSE(manager_->CanRender(texture_ref_.get())); + EXPECT_TRUE(manager_->HaveUnrenderableTextures()); + manager_->SetLevelInfo(texture_ref_.get(), + GL_TEXTURE_CUBE_MAP_POSITIVE_Y, + 0, + GL_RGBA, + 4, + 4, + 1, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + true); + EXPECT_FALSE(TextureTestHelper::IsNPOT(texture)); + EXPECT_FALSE(TextureTestHelper::IsTextureComplete(texture)); + EXPECT_FALSE(TextureTestHelper::IsCubeComplete(texture)); + EXPECT_FALSE(manager_->CanGenerateMipmaps(texture_ref_.get())); + EXPECT_FALSE(manager_->CanRender(texture_ref_.get())); + EXPECT_TRUE(manager_->HaveUnrenderableTextures()); + manager_->SetLevelInfo(texture_ref_.get(), + GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, + 0, + GL_RGBA, + 4, + 4, + 1, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + true); + EXPECT_FALSE(TextureTestHelper::IsNPOT(texture)); + EXPECT_FALSE(TextureTestHelper::IsTextureComplete(texture)); + EXPECT_FALSE(TextureTestHelper::IsCubeComplete(texture)); + EXPECT_FALSE(manager_->CanRender(texture_ref_.get())); + EXPECT_FALSE(manager_->CanGenerateMipmaps(texture_ref_.get())); + EXPECT_TRUE(manager_->HaveUnrenderableTextures()); + manager_->SetLevelInfo(texture_ref_.get(), + GL_TEXTURE_CUBE_MAP_POSITIVE_Z, + 0, + GL_RGBA, + 4, + 4, + 1, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + true); + EXPECT_FALSE(TextureTestHelper::IsNPOT(texture)); + EXPECT_FALSE(TextureTestHelper::IsTextureComplete(texture)); + EXPECT_FALSE(TextureTestHelper::IsCubeComplete(texture)); + EXPECT_FALSE(manager_->CanGenerateMipmaps(texture_ref_.get())); + EXPECT_FALSE(manager_->CanRender(texture_ref_.get())); + EXPECT_TRUE(manager_->HaveUnrenderableTextures()); + manager_->SetLevelInfo(texture_ref_.get(), + GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, + 0, + GL_RGBA, + 4, + 4, + 1, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + true); + EXPECT_FALSE(TextureTestHelper::IsNPOT(texture)); + EXPECT_FALSE(TextureTestHelper::IsTextureComplete(texture)); + EXPECT_TRUE(TextureTestHelper::IsCubeComplete(texture)); + EXPECT_TRUE(manager_->CanGenerateMipmaps(texture_ref_.get())); + EXPECT_FALSE(manager_->CanRender(texture_ref_.get())); + EXPECT_TRUE(manager_->HaveUnrenderableTextures()); + + // Make mips. + EXPECT_TRUE(manager_->MarkMipmapsGenerated(texture_ref_.get())); + EXPECT_TRUE(TextureTestHelper::IsTextureComplete(texture)); + EXPECT_TRUE(TextureTestHelper::IsCubeComplete(texture)); + EXPECT_TRUE(manager_->CanRender(texture_ref_.get())); + EXPECT_FALSE(manager_->HaveUnrenderableTextures()); + + // Change a mip. + manager_->SetLevelInfo(texture_ref_.get(), + GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, + 1, + GL_RGBA, + 4, + 4, + 1, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + true); + EXPECT_FALSE(TextureTestHelper::IsNPOT(texture)); + EXPECT_FALSE(TextureTestHelper::IsTextureComplete(texture)); + EXPECT_TRUE(TextureTestHelper::IsCubeComplete(texture)); + EXPECT_TRUE(manager_->CanGenerateMipmaps(texture_ref_.get())); + // Set a level past the number of mips that would get generated. + manager_->SetLevelInfo(texture_ref_.get(), + GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, + 3, + GL_RGBA, + 4, + 4, + 1, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + true); + EXPECT_TRUE(manager_->CanGenerateMipmaps(texture_ref_.get())); + // Make mips. + EXPECT_TRUE(manager_->MarkMipmapsGenerated(texture_ref_.get())); + EXPECT_TRUE(TextureTestHelper::IsTextureComplete(texture)); + EXPECT_TRUE(TextureTestHelper::IsCubeComplete(texture)); +} + +TEST_F(TextureTest, GetLevelSize) { + manager_->SetTarget(texture_ref_.get(), GL_TEXTURE_2D); + manager_->SetLevelInfo(texture_ref_.get(), + GL_TEXTURE_2D, + 1, + GL_RGBA, + 4, + 5, + 1, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + true); + GLsizei width = -1; + GLsizei height = -1; + Texture* texture = texture_ref_->texture(); + EXPECT_FALSE(texture->GetLevelSize(GL_TEXTURE_2D, -1, &width, &height)); + EXPECT_FALSE(texture->GetLevelSize(GL_TEXTURE_2D, 1000, &width, &height)); + EXPECT_FALSE(texture->GetLevelSize(GL_TEXTURE_2D, 0, &width, &height)); + EXPECT_TRUE(texture->GetLevelSize(GL_TEXTURE_2D, 1, &width, &height)); + EXPECT_EQ(4, width); + EXPECT_EQ(5, height); + manager_->RemoveTexture(kClient1Id); + EXPECT_TRUE(texture->GetLevelSize(GL_TEXTURE_2D, 1, &width, &height)); + EXPECT_EQ(4, width); + EXPECT_EQ(5, height); +} + +TEST_F(TextureTest, GetLevelType) { + manager_->SetTarget(texture_ref_.get(), GL_TEXTURE_2D); + manager_->SetLevelInfo(texture_ref_.get(), + GL_TEXTURE_2D, + 1, + GL_RGBA, + 4, + 5, + 1, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + true); + GLenum type = -1; + GLenum format = -1; + Texture* texture = texture_ref_->texture(); + EXPECT_FALSE(texture->GetLevelType(GL_TEXTURE_2D, -1, &type, &format)); + EXPECT_FALSE(texture->GetLevelType(GL_TEXTURE_2D, 1000, &type, &format)); + EXPECT_FALSE(texture->GetLevelType(GL_TEXTURE_2D, 0, &type, &format)); + EXPECT_TRUE(texture->GetLevelType(GL_TEXTURE_2D, 1, &type, &format)); + EXPECT_EQ(static_cast<GLenum>(GL_UNSIGNED_BYTE), type); + EXPECT_EQ(static_cast<GLenum>(GL_RGBA), format); + manager_->RemoveTexture(kClient1Id); + EXPECT_TRUE(texture->GetLevelType(GL_TEXTURE_2D, 1, &type, &format)); + EXPECT_EQ(static_cast<GLenum>(GL_UNSIGNED_BYTE), type); + EXPECT_EQ(static_cast<GLenum>(GL_RGBA), format); +} + +TEST_F(TextureTest, ValidForTexture) { + manager_->SetTarget(texture_ref_.get(), GL_TEXTURE_2D); + manager_->SetLevelInfo(texture_ref_.get(), + GL_TEXTURE_2D, + 1, + GL_RGBA, + 4, + 5, + 1, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + true); + // Check bad face. + Texture* texture = texture_ref_->texture(); + EXPECT_FALSE(texture->ValidForTexture( + GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, + 1, 0, 0, 4, 5, GL_RGBA, GL_UNSIGNED_BYTE)); + // Check bad level. + EXPECT_FALSE(texture->ValidForTexture( + GL_TEXTURE_2D, 0, 0, 0, 4, 5, GL_RGBA, GL_UNSIGNED_BYTE)); + // Check bad xoffset. + EXPECT_FALSE(texture->ValidForTexture( + GL_TEXTURE_2D, 1, -1, 0, 4, 5, GL_RGBA, GL_UNSIGNED_BYTE)); + // Check bad xoffset + width > width. + EXPECT_FALSE(texture->ValidForTexture( + GL_TEXTURE_2D, 1, 1, 0, 4, 5, GL_RGBA, GL_UNSIGNED_BYTE)); + // Check bad yoffset. + EXPECT_FALSE(texture->ValidForTexture( + GL_TEXTURE_2D, 1, 0, -1, 4, 5, GL_RGBA, GL_UNSIGNED_BYTE)); + // Check bad yoffset + height > height. + EXPECT_FALSE(texture->ValidForTexture( + GL_TEXTURE_2D, 1, 0, 1, 4, 5, GL_RGBA, GL_UNSIGNED_BYTE)); + // Check bad width. + EXPECT_FALSE(texture->ValidForTexture( + GL_TEXTURE_2D, 1, 0, 0, 5, 5, GL_RGBA, GL_UNSIGNED_BYTE)); + // Check bad height. + EXPECT_FALSE(texture->ValidForTexture( + GL_TEXTURE_2D, 1, 0, 0, 4, 6, GL_RGBA, GL_UNSIGNED_BYTE)); + // Check bad format. + EXPECT_FALSE(texture->ValidForTexture( + GL_TEXTURE_2D, 1, 0, 0, 4, 5, GL_RGB, GL_UNSIGNED_BYTE)); + // Check bad type. + EXPECT_FALSE(texture->ValidForTexture( + GL_TEXTURE_2D, 1, 0, 0, 4, 5, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4)); + // Check valid full size + EXPECT_TRUE(texture->ValidForTexture( + GL_TEXTURE_2D, 1, 0, 0, 4, 5, GL_RGBA, GL_UNSIGNED_BYTE)); + // Check valid particial size. + EXPECT_TRUE(texture->ValidForTexture( + GL_TEXTURE_2D, 1, 1, 1, 2, 3, GL_RGBA, GL_UNSIGNED_BYTE)); + manager_->RemoveTexture(kClient1Id); + EXPECT_TRUE(texture->ValidForTexture( + GL_TEXTURE_2D, 1, 0, 0, 4, 5, GL_RGBA, GL_UNSIGNED_BYTE)); +} + +TEST_F(TextureTest, FloatNotLinear) { + TestHelper::SetupFeatureInfoInitExpectations( + gl_.get(), "GL_OES_texture_float"); + scoped_refptr<FeatureInfo> feature_info(new FeatureInfo()); + feature_info->Initialize(NULL); + TextureManager manager( + NULL, feature_info.get(), kMaxTextureSize, kMaxCubeMapTextureSize); + manager.CreateTexture(kClient1Id, kService1Id); + TextureRef* texture_ref = manager.GetTexture(kClient1Id); + ASSERT_TRUE(texture_ref != NULL); + manager.SetTarget(texture_ref, GL_TEXTURE_2D); + Texture* texture = texture_ref->texture(); + EXPECT_EQ(static_cast<GLenum>(GL_TEXTURE_2D), texture->target()); + manager.SetLevelInfo(texture_ref, + GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 1, 0, GL_RGBA, GL_FLOAT, true); + EXPECT_FALSE(TextureTestHelper::IsTextureComplete(texture)); + TestHelper::SetTexParameterWithExpectations( + gl_.get(), error_state_.get(), &manager, + texture_ref, GL_TEXTURE_MAG_FILTER, GL_NEAREST, GL_NO_ERROR); + EXPECT_FALSE(TextureTestHelper::IsTextureComplete(texture)); + TestHelper::SetTexParameterWithExpectations( + gl_.get(), error_state_.get(), &manager, texture_ref, + GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST, GL_NO_ERROR); + EXPECT_TRUE(TextureTestHelper::IsTextureComplete(texture)); + manager.Destroy(false); +} + +TEST_F(TextureTest, FloatLinear) { + TestHelper::SetupFeatureInfoInitExpectations( + gl_.get(), "GL_OES_texture_float GL_OES_texture_float_linear"); + scoped_refptr<FeatureInfo> feature_info(new FeatureInfo()); + feature_info->Initialize(NULL); + TextureManager manager( + NULL, feature_info.get(), kMaxTextureSize, kMaxCubeMapTextureSize); + manager.CreateTexture(kClient1Id, kService1Id); + TextureRef* texture_ref = manager.GetTexture(kClient1Id); + ASSERT_TRUE(texture_ref != NULL); + manager.SetTarget(texture_ref, GL_TEXTURE_2D); + Texture* texture = texture_ref->texture(); + EXPECT_EQ(static_cast<GLenum>(GL_TEXTURE_2D), texture->target()); + manager.SetLevelInfo(texture_ref, + GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 1, 0, GL_RGBA, GL_FLOAT, true); + EXPECT_TRUE(TextureTestHelper::IsTextureComplete(texture)); + manager.Destroy(false); +} + +TEST_F(TextureTest, HalfFloatNotLinear) { + TestHelper::SetupFeatureInfoInitExpectations( + gl_.get(), "GL_OES_texture_half_float"); + scoped_refptr<FeatureInfo> feature_info(new FeatureInfo()); + feature_info->Initialize(NULL); + TextureManager manager( + NULL, feature_info.get(), kMaxTextureSize, kMaxCubeMapTextureSize); + manager.CreateTexture(kClient1Id, kService1Id); + TextureRef* texture_ref = manager.GetTexture(kClient1Id); + ASSERT_TRUE(texture_ref != NULL); + manager.SetTarget(texture_ref, GL_TEXTURE_2D); + Texture* texture = texture_ref->texture(); + EXPECT_EQ(static_cast<GLenum>(GL_TEXTURE_2D), texture->target()); + manager.SetLevelInfo(texture_ref, + GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 1, 0, GL_RGBA, GL_HALF_FLOAT_OES, true); + EXPECT_FALSE(TextureTestHelper::IsTextureComplete(texture)); + TestHelper::SetTexParameterWithExpectations( + gl_.get(), error_state_.get(), &manager, + texture_ref, GL_TEXTURE_MAG_FILTER, GL_NEAREST, GL_NO_ERROR); + EXPECT_FALSE(TextureTestHelper::IsTextureComplete(texture)); + TestHelper::SetTexParameterWithExpectations( + gl_.get(), error_state_.get(), &manager, texture_ref, + GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST, GL_NO_ERROR); + EXPECT_TRUE(TextureTestHelper::IsTextureComplete(texture)); + manager.Destroy(false); +} + +TEST_F(TextureTest, HalfFloatLinear) { + TestHelper::SetupFeatureInfoInitExpectations( + gl_.get(), "GL_OES_texture_half_float GL_OES_texture_half_float_linear"); + scoped_refptr<FeatureInfo> feature_info(new FeatureInfo()); + feature_info->Initialize(NULL); + TextureManager manager( + NULL, feature_info.get(), kMaxTextureSize, kMaxCubeMapTextureSize); + manager.CreateTexture(kClient1Id, kService1Id); + TextureRef* texture_ref = manager.GetTexture(kClient1Id); + ASSERT_TRUE(texture_ref != NULL); + manager.SetTarget(texture_ref, GL_TEXTURE_2D); + Texture* texture = texture_ref->texture(); + EXPECT_EQ(static_cast<GLenum>(GL_TEXTURE_2D), texture->target()); + manager.SetLevelInfo(texture_ref, + GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 1, 0, GL_RGBA, GL_HALF_FLOAT_OES, true); + EXPECT_TRUE(TextureTestHelper::IsTextureComplete(texture)); + manager.Destroy(false); +} + +TEST_F(TextureTest, EGLImageExternal) { + TestHelper::SetupFeatureInfoInitExpectations( + gl_.get(), "GL_OES_EGL_image_external"); + scoped_refptr<FeatureInfo> feature_info(new FeatureInfo()); + feature_info->Initialize(NULL); + TextureManager manager( + NULL, feature_info.get(), kMaxTextureSize, kMaxCubeMapTextureSize); + manager.CreateTexture(kClient1Id, kService1Id); + TextureRef* texture_ref = manager.GetTexture(kClient1Id); + ASSERT_TRUE(texture_ref != NULL); + manager.SetTarget(texture_ref, GL_TEXTURE_EXTERNAL_OES); + Texture* texture = texture_ref->texture(); + EXPECT_EQ(static_cast<GLenum>(GL_TEXTURE_EXTERNAL_OES), texture->target()); + EXPECT_FALSE(manager.CanGenerateMipmaps(texture_ref)); + manager.Destroy(false); +} + +TEST_F(TextureTest, DepthTexture) { + TestHelper::SetupFeatureInfoInitExpectations( + gl_.get(), "GL_ANGLE_depth_texture"); + scoped_refptr<FeatureInfo> feature_info(new FeatureInfo()); + feature_info->Initialize(NULL); + TextureManager manager( + NULL, feature_info.get(), kMaxTextureSize, kMaxCubeMapTextureSize); + manager.CreateTexture(kClient1Id, kService1Id); + TextureRef* texture_ref = manager.GetTexture(kClient1Id); + ASSERT_TRUE(texture_ref != NULL); + manager.SetTarget(texture_ref, GL_TEXTURE_2D); + manager.SetLevelInfo( + texture_ref, GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, 4, 4, 1, 0, + GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, false); + EXPECT_FALSE(manager.CanGenerateMipmaps(texture_ref)); + manager.Destroy(false); +} + +TEST_F(TextureTest, SafeUnsafe) { + static const GLuint kClient2Id = 2; + static const GLuint kService2Id = 12; + static const GLuint kClient3Id = 3; + static const GLuint kService3Id = 13; + EXPECT_FALSE(manager_->HaveUnclearedMips()); + Texture* texture = texture_ref_->texture(); + EXPECT_EQ(0, texture->num_uncleared_mips()); + manager_->SetTarget(texture_ref_.get(), GL_TEXTURE_2D); + manager_->SetLevelInfo(texture_ref_.get(), + GL_TEXTURE_2D, + 0, + GL_RGBA, + 4, + 4, + 1, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + false); + EXPECT_FALSE(texture->SafeToRenderFrom()); + EXPECT_TRUE(manager_->HaveUnsafeTextures()); + EXPECT_TRUE(manager_->HaveUnclearedMips()); + EXPECT_EQ(1, texture->num_uncleared_mips()); + manager_->SetLevelCleared(texture_ref_.get(), GL_TEXTURE_2D, 0, true); + EXPECT_TRUE(texture->SafeToRenderFrom()); + EXPECT_FALSE(manager_->HaveUnsafeTextures()); + EXPECT_FALSE(manager_->HaveUnclearedMips()); + EXPECT_EQ(0, texture->num_uncleared_mips()); + manager_->SetLevelInfo(texture_ref_.get(), + GL_TEXTURE_2D, + 1, + GL_RGBA, + 8, + 8, + 1, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + false); + EXPECT_FALSE(texture->SafeToRenderFrom()); + EXPECT_TRUE(manager_->HaveUnsafeTextures()); + EXPECT_TRUE(manager_->HaveUnclearedMips()); + EXPECT_EQ(1, texture->num_uncleared_mips()); + manager_->SetLevelCleared(texture_ref_.get(), GL_TEXTURE_2D, 1, true); + EXPECT_TRUE(texture->SafeToRenderFrom()); + EXPECT_FALSE(manager_->HaveUnsafeTextures()); + EXPECT_FALSE(manager_->HaveUnclearedMips()); + EXPECT_EQ(0, texture->num_uncleared_mips()); + manager_->SetLevelInfo(texture_ref_.get(), + GL_TEXTURE_2D, + 0, + GL_RGBA, + 4, + 4, + 1, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + false); + manager_->SetLevelInfo(texture_ref_.get(), + GL_TEXTURE_2D, + 1, + GL_RGBA, + 8, + 8, + 1, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + false); + EXPECT_FALSE(texture->SafeToRenderFrom()); + EXPECT_TRUE(manager_->HaveUnsafeTextures()); + EXPECT_TRUE(manager_->HaveUnclearedMips()); + EXPECT_EQ(2, texture->num_uncleared_mips()); + manager_->SetLevelCleared(texture_ref_.get(), GL_TEXTURE_2D, 0, true); + EXPECT_FALSE(texture->SafeToRenderFrom()); + EXPECT_TRUE(manager_->HaveUnsafeTextures()); + EXPECT_TRUE(manager_->HaveUnclearedMips()); + EXPECT_EQ(1, texture->num_uncleared_mips()); + manager_->SetLevelCleared(texture_ref_.get(), GL_TEXTURE_2D, 1, true); + EXPECT_TRUE(texture->SafeToRenderFrom()); + EXPECT_FALSE(manager_->HaveUnsafeTextures()); + EXPECT_FALSE(manager_->HaveUnclearedMips()); + EXPECT_EQ(0, texture->num_uncleared_mips()); + manager_->SetLevelInfo(texture_ref_.get(), + GL_TEXTURE_2D, + 1, + GL_RGBA, + 8, + 8, + 1, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + false); + EXPECT_FALSE(texture->SafeToRenderFrom()); + EXPECT_TRUE(manager_->HaveUnsafeTextures()); + EXPECT_TRUE(manager_->HaveUnclearedMips()); + EXPECT_EQ(1, texture->num_uncleared_mips()); + manager_->MarkMipmapsGenerated(texture_ref_.get()); + EXPECT_TRUE(texture->SafeToRenderFrom()); + EXPECT_FALSE(manager_->HaveUnsafeTextures()); + EXPECT_FALSE(manager_->HaveUnclearedMips()); + EXPECT_EQ(0, texture->num_uncleared_mips()); + + manager_->CreateTexture(kClient2Id, kService2Id); + scoped_refptr<TextureRef> texture_ref2( + manager_->GetTexture(kClient2Id)); + ASSERT_TRUE(texture_ref2.get() != NULL); + manager_->SetTarget(texture_ref2.get(), GL_TEXTURE_2D); + EXPECT_FALSE(manager_->HaveUnsafeTextures()); + EXPECT_FALSE(manager_->HaveUnclearedMips()); + Texture* texture2 = texture_ref2->texture(); + EXPECT_EQ(0, texture2->num_uncleared_mips()); + manager_->SetLevelInfo(texture_ref2.get(), + GL_TEXTURE_2D, + 0, + GL_RGBA, + 8, + 8, + 1, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + true); + EXPECT_FALSE(manager_->HaveUnsafeTextures()); + EXPECT_FALSE(manager_->HaveUnclearedMips()); + EXPECT_EQ(0, texture2->num_uncleared_mips()); + manager_->SetLevelInfo(texture_ref2.get(), + GL_TEXTURE_2D, + 0, + GL_RGBA, + 8, + 8, + 1, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + false); + EXPECT_TRUE(manager_->HaveUnsafeTextures()); + EXPECT_TRUE(manager_->HaveUnclearedMips()); + EXPECT_EQ(1, texture2->num_uncleared_mips()); + + manager_->CreateTexture(kClient3Id, kService3Id); + scoped_refptr<TextureRef> texture_ref3( + manager_->GetTexture(kClient3Id)); + ASSERT_TRUE(texture_ref3.get() != NULL); + manager_->SetTarget(texture_ref3.get(), GL_TEXTURE_2D); + manager_->SetLevelInfo(texture_ref3.get(), + GL_TEXTURE_2D, + 0, + GL_RGBA, + 8, + 8, + 1, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + false); + EXPECT_TRUE(manager_->HaveUnsafeTextures()); + EXPECT_TRUE(manager_->HaveUnclearedMips()); + Texture* texture3 = texture_ref3->texture(); + EXPECT_EQ(1, texture3->num_uncleared_mips()); + manager_->SetLevelCleared(texture_ref2.get(), GL_TEXTURE_2D, 0, true); + EXPECT_TRUE(manager_->HaveUnsafeTextures()); + EXPECT_TRUE(manager_->HaveUnclearedMips()); + EXPECT_EQ(0, texture2->num_uncleared_mips()); + manager_->SetLevelCleared(texture_ref3.get(), GL_TEXTURE_2D, 0, true); + EXPECT_FALSE(manager_->HaveUnsafeTextures()); + EXPECT_FALSE(manager_->HaveUnclearedMips()); + EXPECT_EQ(0, texture3->num_uncleared_mips()); + + manager_->SetLevelInfo(texture_ref2.get(), + GL_TEXTURE_2D, + 0, + GL_RGBA, + 8, + 8, + 1, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + false); + manager_->SetLevelInfo(texture_ref3.get(), + GL_TEXTURE_2D, + 0, + GL_RGBA, + 8, + 8, + 1, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + false); + EXPECT_TRUE(manager_->HaveUnsafeTextures()); + EXPECT_TRUE(manager_->HaveUnclearedMips()); + EXPECT_EQ(1, texture2->num_uncleared_mips()); + EXPECT_EQ(1, texture3->num_uncleared_mips()); + manager_->RemoveTexture(kClient3Id); + EXPECT_TRUE(manager_->HaveUnsafeTextures()); + EXPECT_TRUE(manager_->HaveUnclearedMips()); + manager_->RemoveTexture(kClient2Id); + EXPECT_TRUE(manager_->HaveUnsafeTextures()); + EXPECT_TRUE(manager_->HaveUnclearedMips()); + EXPECT_CALL(*gl_, DeleteTextures(1, ::testing::Pointee(kService2Id))) + .Times(1) + .RetiresOnSaturation(); + texture_ref2 = NULL; + EXPECT_TRUE(manager_->HaveUnsafeTextures()); + EXPECT_TRUE(manager_->HaveUnclearedMips()); + EXPECT_CALL(*gl_, DeleteTextures(1, ::testing::Pointee(kService3Id))) + .Times(1) + .RetiresOnSaturation(); + texture_ref3 = NULL; + EXPECT_FALSE(manager_->HaveUnsafeTextures()); + EXPECT_FALSE(manager_->HaveUnclearedMips()); +} + +TEST_F(TextureTest, ClearTexture) { + EXPECT_CALL(*decoder_, ClearLevel(_, _, _, _, _, _, _, _, _)) + .WillRepeatedly(Return(true)); + manager_->SetTarget(texture_ref_.get(), GL_TEXTURE_2D); + manager_->SetLevelInfo(texture_ref_.get(), + GL_TEXTURE_2D, + 0, + GL_RGBA, + 4, + 4, + 1, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + false); + manager_->SetLevelInfo(texture_ref_.get(), + GL_TEXTURE_2D, + 1, + GL_RGBA, + 4, + 4, + 1, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + false); + Texture* texture = texture_ref_->texture(); + EXPECT_FALSE(texture->SafeToRenderFrom()); + EXPECT_TRUE(manager_->HaveUnsafeTextures()); + EXPECT_TRUE(manager_->HaveUnclearedMips()); + EXPECT_EQ(2, texture->num_uncleared_mips()); + manager_->ClearRenderableLevels(decoder_.get(), texture_ref_.get()); + EXPECT_TRUE(texture->SafeToRenderFrom()); + EXPECT_FALSE(manager_->HaveUnsafeTextures()); + EXPECT_FALSE(manager_->HaveUnclearedMips()); + EXPECT_EQ(0, texture->num_uncleared_mips()); + manager_->SetLevelInfo(texture_ref_.get(), + GL_TEXTURE_2D, + 0, + GL_RGBA, + 4, + 4, + 1, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + false); + manager_->SetLevelInfo(texture_ref_.get(), + GL_TEXTURE_2D, + 1, + GL_RGBA, + 4, + 4, + 1, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + false); + EXPECT_FALSE(texture->SafeToRenderFrom()); + EXPECT_TRUE(manager_->HaveUnsafeTextures()); + EXPECT_TRUE(manager_->HaveUnclearedMips()); + EXPECT_EQ(2, texture->num_uncleared_mips()); + manager_->ClearTextureLevel( + decoder_.get(), texture_ref_.get(), GL_TEXTURE_2D, 0); + EXPECT_FALSE(texture->SafeToRenderFrom()); + EXPECT_TRUE(manager_->HaveUnsafeTextures()); + EXPECT_TRUE(manager_->HaveUnclearedMips()); + EXPECT_EQ(1, texture->num_uncleared_mips()); + manager_->ClearTextureLevel( + decoder_.get(), texture_ref_.get(), GL_TEXTURE_2D, 1); + EXPECT_TRUE(texture->SafeToRenderFrom()); + EXPECT_FALSE(manager_->HaveUnsafeTextures()); + EXPECT_FALSE(manager_->HaveUnclearedMips()); + EXPECT_EQ(0, texture->num_uncleared_mips()); +} + +TEST_F(TextureTest, UseDeletedTexture) { + static const GLuint kClient2Id = 2; + static const GLuint kService2Id = 12; + // Make the default texture renderable + manager_->SetTarget(texture_ref_.get(), GL_TEXTURE_2D); + manager_->SetLevelInfo(texture_ref_.get(), + GL_TEXTURE_2D, + 0, + GL_RGBA, + 1, + 1, + 1, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + false); + EXPECT_FALSE(manager_->HaveUnrenderableTextures()); + // Make a new texture + manager_->CreateTexture(kClient2Id, kService2Id); + scoped_refptr<TextureRef> texture_ref( + manager_->GetTexture(kClient2Id)); + manager_->SetTarget(texture_ref.get(), GL_TEXTURE_2D); + EXPECT_FALSE(manager_->CanRender(texture_ref.get())); + EXPECT_TRUE(manager_->HaveUnrenderableTextures()); + // Remove it. + manager_->RemoveTexture(kClient2Id); + EXPECT_FALSE(manager_->CanRender(texture_ref.get())); + EXPECT_TRUE(manager_->HaveUnrenderableTextures()); + // Check that we can still manipulate it and it effects the manager. + manager_->SetLevelInfo(texture_ref.get(), + GL_TEXTURE_2D, + 0, + GL_RGBA, + 1, + 1, + 1, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + false); + EXPECT_TRUE(manager_->CanRender(texture_ref.get())); + EXPECT_FALSE(manager_->HaveUnrenderableTextures()); + EXPECT_CALL(*gl_, DeleteTextures(1, ::testing::Pointee(kService2Id))) + .Times(1) + .RetiresOnSaturation(); + texture_ref = NULL; +} + +TEST_F(TextureTest, GetLevelImage) { + manager_->SetTarget(texture_ref_.get(), GL_TEXTURE_2D); + manager_->SetLevelInfo(texture_ref_.get(), + GL_TEXTURE_2D, + 1, + GL_RGBA, + 2, + 2, + 1, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + true); + Texture* texture = texture_ref_->texture(); + EXPECT_TRUE(texture->GetLevelImage(GL_TEXTURE_2D, 1) == NULL); + // Set image. + manager_->SetLevelImage(texture_ref_.get(), + GL_TEXTURE_2D, + 1, + gfx::GLImage::CreateGLImage(0).get()); + EXPECT_FALSE(texture->GetLevelImage(GL_TEXTURE_2D, 1) == NULL); + // Remove it. + manager_->SetLevelImage(texture_ref_.get(), GL_TEXTURE_2D, 1, NULL); + EXPECT_TRUE(texture->GetLevelImage(GL_TEXTURE_2D, 1) == NULL); + manager_->SetLevelImage(texture_ref_.get(), + GL_TEXTURE_2D, + 1, + gfx::GLImage::CreateGLImage(0).get()); + // Image should be reset when SetLevelInfo is called. + manager_->SetLevelInfo(texture_ref_.get(), + GL_TEXTURE_2D, + 1, + GL_RGBA, + 2, + 2, + 1, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + true); + EXPECT_TRUE(texture->GetLevelImage(GL_TEXTURE_2D, 1) == NULL); +} + +namespace { + +bool InSet(std::set<std::string>* string_set, const std::string& str) { + std::pair<std::set<std::string>::iterator, bool> result = + string_set->insert(str); + return !result.second; +} + +} // anonymous namespace + +TEST_F(TextureTest, AddToSignature) { + manager_->SetTarget(texture_ref_.get(), GL_TEXTURE_2D); + manager_->SetLevelInfo(texture_ref_.get(), + GL_TEXTURE_2D, + 1, + GL_RGBA, + 2, + 2, + 1, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + true); + std::string signature1; + std::string signature2; + manager_->AddToSignature(texture_ref_.get(), GL_TEXTURE_2D, 1, &signature1); + + std::set<std::string> string_set; + EXPECT_FALSE(InSet(&string_set, signature1)); + + // check changing 1 thing makes a different signature. + manager_->SetLevelInfo(texture_ref_.get(), + GL_TEXTURE_2D, + 1, + GL_RGBA, + 4, + 2, + 1, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + true); + manager_->AddToSignature(texture_ref_.get(), GL_TEXTURE_2D, 1, &signature2); + EXPECT_FALSE(InSet(&string_set, signature2)); + + // check putting it back makes the same signature. + manager_->SetLevelInfo(texture_ref_.get(), + GL_TEXTURE_2D, + 1, + GL_RGBA, + 2, + 2, + 1, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + true); + signature2.clear(); + manager_->AddToSignature(texture_ref_.get(), GL_TEXTURE_2D, 1, &signature2); + EXPECT_EQ(signature1, signature2); + + // Check setting cleared status does not change signature. + manager_->SetLevelInfo(texture_ref_.get(), + GL_TEXTURE_2D, + 1, + GL_RGBA, + 2, + 2, + 1, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + false); + signature2.clear(); + manager_->AddToSignature(texture_ref_.get(), GL_TEXTURE_2D, 1, &signature2); + EXPECT_EQ(signature1, signature2); + + // Check changing other settings changes signature. + manager_->SetLevelInfo(texture_ref_.get(), + GL_TEXTURE_2D, + 1, + GL_RGBA, + 2, + 4, + 1, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + false); + signature2.clear(); + manager_->AddToSignature(texture_ref_.get(), GL_TEXTURE_2D, 1, &signature2); + EXPECT_FALSE(InSet(&string_set, signature2)); + + manager_->SetLevelInfo(texture_ref_.get(), + GL_TEXTURE_2D, + 1, + GL_RGBA, + 2, + 2, + 2, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + false); + signature2.clear(); + manager_->AddToSignature(texture_ref_.get(), GL_TEXTURE_2D, 1, &signature2); + EXPECT_FALSE(InSet(&string_set, signature2)); + + manager_->SetLevelInfo(texture_ref_.get(), + GL_TEXTURE_2D, + 1, + GL_RGBA, + 2, + 2, + 1, + 1, + GL_RGBA, + GL_UNSIGNED_BYTE, + false); + signature2.clear(); + manager_->AddToSignature(texture_ref_.get(), GL_TEXTURE_2D, 1, &signature2); + EXPECT_FALSE(InSet(&string_set, signature2)); + + manager_->SetLevelInfo(texture_ref_.get(), + GL_TEXTURE_2D, + 1, + GL_RGBA, + 2, + 2, + 1, + 0, + GL_RGB, + GL_UNSIGNED_BYTE, + false); + signature2.clear(); + manager_->AddToSignature(texture_ref_.get(), GL_TEXTURE_2D, 1, &signature2); + EXPECT_FALSE(InSet(&string_set, signature2)); + + manager_->SetLevelInfo(texture_ref_.get(), + GL_TEXTURE_2D, + 1, + GL_RGBA, + 2, + 2, + 1, + 0, + GL_RGBA, + GL_FLOAT, + false); + signature2.clear(); + manager_->AddToSignature(texture_ref_.get(), GL_TEXTURE_2D, 1, &signature2); + EXPECT_FALSE(InSet(&string_set, signature2)); + + // put it back + manager_->SetLevelInfo(texture_ref_.get(), + GL_TEXTURE_2D, + 1, + GL_RGBA, + 2, + 2, + 1, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + false); + signature2.clear(); + manager_->AddToSignature(texture_ref_.get(), GL_TEXTURE_2D, 1, &signature2); + EXPECT_EQ(signature1, signature2); + + // check changing parameters changes signature. + SetParameter( + texture_ref_.get(), GL_TEXTURE_MIN_FILTER, GL_NEAREST, GL_NO_ERROR); + signature2.clear(); + manager_->AddToSignature(texture_ref_.get(), GL_TEXTURE_2D, 1, &signature2); + EXPECT_FALSE(InSet(&string_set, signature2)); + + SetParameter(texture_ref_.get(), + GL_TEXTURE_MIN_FILTER, + GL_NEAREST_MIPMAP_LINEAR, + GL_NO_ERROR); + SetParameter( + texture_ref_.get(), GL_TEXTURE_MAG_FILTER, GL_NEAREST, GL_NO_ERROR); + signature2.clear(); + manager_->AddToSignature(texture_ref_.get(), GL_TEXTURE_2D, 1, &signature2); + EXPECT_FALSE(InSet(&string_set, signature2)); + + SetParameter( + texture_ref_.get(), GL_TEXTURE_MAG_FILTER, GL_LINEAR, GL_NO_ERROR); + SetParameter( + texture_ref_.get(), GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE, GL_NO_ERROR); + signature2.clear(); + manager_->AddToSignature(texture_ref_.get(), GL_TEXTURE_2D, 1, &signature2); + EXPECT_FALSE(InSet(&string_set, signature2)); + + SetParameter(texture_ref_.get(), GL_TEXTURE_WRAP_S, GL_REPEAT, GL_NO_ERROR); + SetParameter( + texture_ref_.get(), GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE, GL_NO_ERROR); + signature2.clear(); + manager_->AddToSignature(texture_ref_.get(), GL_TEXTURE_2D, 1, &signature2); + EXPECT_FALSE(InSet(&string_set, signature2)); + + // Check putting it back genenerates the same signature + SetParameter(texture_ref_.get(), GL_TEXTURE_WRAP_T, GL_REPEAT, GL_NO_ERROR); + signature2.clear(); + manager_->AddToSignature(texture_ref_.get(), GL_TEXTURE_2D, 1, &signature2); + EXPECT_EQ(signature1, signature2); + + // Check the set was acutally getting different signatures. + EXPECT_EQ(11u, string_set.size()); +} + +class ProduceConsumeTextureTest : public TextureTest { + public: + virtual void SetUp() { + TextureTest::SetUpBase(NULL, "GL_OES_EGL_image_external"); + manager_->CreateTexture(kClient2Id, kService2Id); + texture2_ = manager_->GetTexture(kClient2Id); + + EXPECT_CALL(*decoder_.get(), GetErrorState()) + .WillRepeatedly(Return(error_state_.get())); + } + + virtual void TearDown() { + if (texture2_.get()) { + // If it's not in the manager then setting texture2_ to NULL will + // delete the texture. + if (!texture2_->client_id()) { + // Check that it gets deleted when the last reference is released. + EXPECT_CALL( + *gl_, + DeleteTextures(1, ::testing::Pointee(texture2_->service_id()))) + .Times(1).RetiresOnSaturation(); + } + texture2_ = NULL; + } + TextureTest::TearDown(); + } + + protected: + struct LevelInfo { + LevelInfo(GLenum target, + GLenum format, + GLsizei width, + GLsizei height, + GLsizei depth, + GLint border, + GLenum type, + bool cleared) + : target(target), + format(format), + width(width), + height(height), + depth(depth), + border(border), + type(type), + cleared(cleared) {} + + LevelInfo() + : target(0), + format(0), + width(-1), + height(-1), + depth(1), + border(0), + type(0), + cleared(false) {} + + bool operator==(const LevelInfo& other) const { + return target == other.target && format == other.format && + width == other.width && height == other.height && + depth == other.depth && border == other.border && + type == other.type && cleared == other.cleared; + } + + GLenum target; + GLenum format; + GLsizei width; + GLsizei height; + GLsizei depth; + GLint border; + GLenum type; + bool cleared; + }; + + void SetLevelInfo(TextureRef* texture_ref, + GLint level, + const LevelInfo& info) { + manager_->SetLevelInfo(texture_ref, + info.target, + level, + info.format, + info.width, + info.height, + info.depth, + info.border, + info.format, + info.type, + info.cleared); + } + + static LevelInfo GetLevelInfo(const TextureRef* texture_ref, + GLint target, + GLint level) { + const Texture* texture = texture_ref->texture(); + LevelInfo info; + info.target = target; + EXPECT_TRUE(texture->GetLevelSize(target, level, &info.width, + &info.height)); + EXPECT_TRUE(texture->GetLevelType(target, level, &info.type, + &info.format)); + info.cleared = texture->IsLevelCleared(target, level); + return info; + } + + Texture* Produce(TextureRef* texture_ref) { + Texture* texture = manager_->Produce(texture_ref); + EXPECT_TRUE(texture != NULL); + return texture; + } + + void Consume(GLuint client_id, Texture* texture) { + EXPECT_TRUE(manager_->Consume(client_id, texture)); + } + + scoped_refptr<TextureRef> texture2_; + + private: + static const GLuint kClient2Id; + static const GLuint kService2Id; +}; + +const GLuint ProduceConsumeTextureTest::kClient2Id = 2; +const GLuint ProduceConsumeTextureTest::kService2Id = 12; + +TEST_F(ProduceConsumeTextureTest, ProduceConsume2D) { + manager_->SetTarget(texture_ref_.get(), GL_TEXTURE_2D); + Texture* texture = texture_ref_->texture(); + EXPECT_EQ(static_cast<GLenum>(GL_TEXTURE_2D), texture->target()); + LevelInfo level0( + GL_TEXTURE_2D, GL_RGBA, 4, 4, 1, 0, GL_UNSIGNED_BYTE, true); + SetLevelInfo(texture_ref_.get(), 0, level0); + EXPECT_TRUE(manager_->MarkMipmapsGenerated(texture_ref_.get())); + EXPECT_TRUE(TextureTestHelper::IsTextureComplete(texture)); + LevelInfo level1 = GetLevelInfo(texture_ref_.get(), GL_TEXTURE_2D, 1); + LevelInfo level2 = GetLevelInfo(texture_ref_.get(), GL_TEXTURE_2D, 2); + Texture* produced_texture = Produce(texture_ref_.get()); + EXPECT_EQ(produced_texture, texture); + + // Make this texture bigger with more levels, and make sure they get + // clobbered correctly during Consume(). + manager_->SetTarget(texture2_.get(), GL_TEXTURE_2D); + SetLevelInfo( + texture2_.get(), + 0, + LevelInfo(GL_TEXTURE_2D, GL_RGBA, 16, 16, 1, 0, GL_UNSIGNED_BYTE, false)); + EXPECT_TRUE(manager_->MarkMipmapsGenerated(texture2_.get())); + texture = texture2_->texture(); + EXPECT_TRUE(TextureTestHelper::IsTextureComplete(texture)); + EXPECT_EQ(1024U + 256U + 64U + 16U + 4U, texture->estimated_size()); + + GLuint client_id = texture2_->client_id(); + manager_->RemoveTexture(client_id); + Consume(client_id, produced_texture); + scoped_refptr<TextureRef> restored_texture = manager_->GetTexture(client_id); + EXPECT_EQ(produced_texture, restored_texture->texture()); + EXPECT_EQ(level0, GetLevelInfo(restored_texture.get(), GL_TEXTURE_2D, 0)); + EXPECT_EQ(level1, GetLevelInfo(restored_texture.get(), GL_TEXTURE_2D, 1)); + EXPECT_EQ(level2, GetLevelInfo(restored_texture.get(), GL_TEXTURE_2D, 2)); + texture = restored_texture->texture(); + EXPECT_EQ(64U + 16U + 4U, texture->estimated_size()); + GLint w, h; + EXPECT_FALSE(texture->GetLevelSize(GL_TEXTURE_2D, 3, &w, &h)); + + // However the old texture ref still exists if it was referenced somewhere. + EXPECT_EQ(1024U + 256U + 64U + 16U + 4U, + texture2_->texture()->estimated_size()); +} + +TEST_F(ProduceConsumeTextureTest, ProduceConsumeClearRectangle) { + manager_->SetTarget(texture_ref_.get(), GL_TEXTURE_RECTANGLE_ARB); + Texture* texture = texture_ref_->texture(); + EXPECT_EQ(static_cast<GLenum>(GL_TEXTURE_RECTANGLE_ARB), texture->target()); + LevelInfo level0( + GL_TEXTURE_RECTANGLE_ARB, GL_RGBA, 1, 1, 1, 0, GL_UNSIGNED_BYTE, false); + SetLevelInfo(texture_ref_.get(), 0, level0); + EXPECT_TRUE(TextureTestHelper::IsTextureComplete(texture)); + Texture* produced_texture = Produce(texture_ref_.get()); + EXPECT_EQ(produced_texture, texture); + EXPECT_EQ(static_cast<GLenum>(GL_TEXTURE_RECTANGLE_ARB), + produced_texture->target()); + + GLuint client_id = texture2_->client_id(); + manager_->RemoveTexture(client_id); + Consume(client_id, produced_texture); + scoped_refptr<TextureRef> restored_texture = manager_->GetTexture(client_id); + EXPECT_EQ(produced_texture, restored_texture->texture()); + + // See if we can clear the previously uncleared level now. + EXPECT_EQ(level0, + GetLevelInfo(restored_texture.get(), GL_TEXTURE_RECTANGLE_ARB, 0)); + EXPECT_CALL(*decoder_, ClearLevel(_, _, _, _, _, _, _, _, _)) + .WillRepeatedly(Return(true)); + EXPECT_TRUE(manager_->ClearTextureLevel( + decoder_.get(), restored_texture.get(), GL_TEXTURE_RECTANGLE_ARB, 0)); +} + +TEST_F(ProduceConsumeTextureTest, ProduceConsumeStreamTexture) { + manager_->SetTarget(texture_ref_.get(), GL_TEXTURE_EXTERNAL_OES); + Texture* texture = texture_ref_->texture(); + EXPECT_EQ(static_cast<GLenum>(GL_TEXTURE_EXTERNAL_OES), texture->target()); + manager_->SetStreamTexture(texture_ref_.get(), true); + GLuint service_id = texture->service_id(); + Texture* produced_texture = Produce(texture_ref_.get()); + EXPECT_TRUE(texture->IsStreamTexture()); + + GLuint client_id = texture2_->client_id(); + manager_->RemoveTexture(client_id); + Consume(client_id, produced_texture); + scoped_refptr<TextureRef> restored_texture = manager_->GetTexture(client_id); + EXPECT_EQ(produced_texture, restored_texture->texture()); + EXPECT_TRUE(restored_texture->texture()->IsStreamTexture()); + EXPECT_TRUE(restored_texture->texture()->IsImmutable()); + EXPECT_EQ(service_id, restored_texture->service_id()); +} + +TEST_F(ProduceConsumeTextureTest, ProduceConsumeCube) { + manager_->SetTarget(texture_ref_.get(), GL_TEXTURE_CUBE_MAP); + Texture* texture = texture_ref_->texture(); + EXPECT_EQ(static_cast<GLenum>(GL_TEXTURE_CUBE_MAP), texture->target()); + LevelInfo face0(GL_TEXTURE_CUBE_MAP_POSITIVE_X, + GL_RGBA, + 1, + 1, + 1, + 0, + GL_UNSIGNED_BYTE, + true); + LevelInfo face5(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, + GL_RGBA, + 3, + 3, + 1, + 0, + GL_UNSIGNED_BYTE, + true); + SetLevelInfo(texture_ref_.get(), 0, face0); + SetLevelInfo(texture_ref_.get(), 0, face5); + EXPECT_TRUE(TextureTestHelper::IsTextureComplete(texture)); + Texture* produced_texture = Produce(texture_ref_.get()); + EXPECT_EQ(produced_texture, texture); + + GLuint client_id = texture2_->client_id(); + manager_->RemoveTexture(client_id); + Consume(client_id, produced_texture); + scoped_refptr<TextureRef> restored_texture = manager_->GetTexture(client_id); + EXPECT_EQ(produced_texture, restored_texture->texture()); + EXPECT_EQ( + face0, + GetLevelInfo(restored_texture.get(), GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0)); + EXPECT_EQ( + face5, + GetLevelInfo(restored_texture.get(), GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0)); +} + +class CountingMemoryTracker : public MemoryTracker { + public: + CountingMemoryTracker() { + current_size_[0] = 0; + current_size_[1] = 0; + } + + virtual void TrackMemoryAllocatedChange(size_t old_size, + size_t new_size, + Pool pool) OVERRIDE { + DCHECK_LT(static_cast<size_t>(pool), arraysize(current_size_)); + current_size_[pool] += new_size - old_size; + } + + virtual bool EnsureGPUMemoryAvailable(size_t size_needed) OVERRIDE { + return true; + } + + size_t GetSize(Pool pool) { + DCHECK_LT(static_cast<size_t>(pool), arraysize(current_size_)); + return current_size_[pool]; + } + + private: + virtual ~CountingMemoryTracker() {} + + size_t current_size_[2]; + DISALLOW_COPY_AND_ASSIGN(CountingMemoryTracker); +}; + +class SharedTextureTest : public testing::Test { + public: + SharedTextureTest() + : feature_info_(new FeatureInfo()) { + } + + virtual ~SharedTextureTest() { + } + + virtual void SetUp() { + gl_.reset(new ::gfx::MockGLInterface()); + ::gfx::GLInterface::SetGLInterface(gl_.get()); + + memory_tracker1_ = new CountingMemoryTracker; + texture_manager1_.reset( + new TextureManager(memory_tracker1_.get(), + feature_info_.get(), + TextureManagerTest::kMaxTextureSize, + TextureManagerTest::kMaxCubeMapTextureSize)); + memory_tracker2_ = new CountingMemoryTracker; + texture_manager2_.reset( + new TextureManager(memory_tracker2_.get(), + feature_info_.get(), + TextureManagerTest::kMaxTextureSize, + TextureManagerTest::kMaxCubeMapTextureSize)); + TestHelper::SetupTextureManagerInitExpectations(gl_.get(), ""); + texture_manager1_->Initialize(); + TestHelper::SetupTextureManagerInitExpectations(gl_.get(), ""); + texture_manager2_->Initialize(); + } + + virtual void TearDown() { + texture_manager2_->Destroy(false); + texture_manager2_.reset(); + texture_manager1_->Destroy(false); + texture_manager1_.reset(); + ::gfx::GLInterface::SetGLInterface(NULL); + gl_.reset(); + } + + protected: + scoped_ptr< ::gfx::MockGLInterface > gl_; + scoped_refptr<FeatureInfo> feature_info_; + scoped_refptr<CountingMemoryTracker> memory_tracker1_; + scoped_ptr<TextureManager> texture_manager1_; + scoped_refptr<CountingMemoryTracker> memory_tracker2_; + scoped_ptr<TextureManager> texture_manager2_; +}; + +TEST_F(SharedTextureTest, DeleteTextures) { + scoped_refptr<TextureRef> ref1 = texture_manager1_->CreateTexture(10, 10); + scoped_refptr<TextureRef> ref2 = + texture_manager2_->Consume(20, ref1->texture()); + EXPECT_CALL(*gl_, DeleteTextures(1, _)) + .Times(0); + ref1 = NULL; + texture_manager1_->RemoveTexture(10); + testing::Mock::VerifyAndClearExpectations(gl_.get()); + + EXPECT_CALL(*gl_, DeleteTextures(1, _)) + .Times(1) + .RetiresOnSaturation(); + ref2 = NULL; + texture_manager2_->RemoveTexture(20); + testing::Mock::VerifyAndClearExpectations(gl_.get()); +} + +TEST_F(SharedTextureTest, TextureSafetyAccounting) { + EXPECT_FALSE(texture_manager1_->HaveUnrenderableTextures()); + EXPECT_FALSE(texture_manager1_->HaveUnsafeTextures()); + EXPECT_FALSE(texture_manager1_->HaveUnclearedMips()); + EXPECT_FALSE(texture_manager2_->HaveUnrenderableTextures()); + EXPECT_FALSE(texture_manager2_->HaveUnsafeTextures()); + EXPECT_FALSE(texture_manager2_->HaveUnclearedMips()); + + // Newly created texture is renderable. + scoped_refptr<TextureRef> ref1 = texture_manager1_->CreateTexture(10, 10); + EXPECT_FALSE(texture_manager1_->HaveUnrenderableTextures()); + EXPECT_FALSE(texture_manager1_->HaveUnsafeTextures()); + EXPECT_FALSE(texture_manager1_->HaveUnclearedMips()); + + // Associate new texture ref to other texture manager, should account for it + // too. + scoped_refptr<TextureRef> ref2 = + texture_manager2_->Consume(20, ref1->texture()); + EXPECT_FALSE(texture_manager2_->HaveUnrenderableTextures()); + EXPECT_FALSE(texture_manager2_->HaveUnsafeTextures()); + EXPECT_FALSE(texture_manager2_->HaveUnclearedMips()); + + // Make texture renderable but uncleared on one texture manager, should affect + // other one. + texture_manager1_->SetTarget(ref1.get(), GL_TEXTURE_2D); + EXPECT_TRUE(texture_manager1_->HaveUnrenderableTextures()); + EXPECT_FALSE(texture_manager1_->HaveUnsafeTextures()); + EXPECT_FALSE(texture_manager1_->HaveUnclearedMips()); + EXPECT_TRUE(texture_manager2_->HaveUnrenderableTextures()); + EXPECT_FALSE(texture_manager2_->HaveUnsafeTextures()); + EXPECT_FALSE(texture_manager2_->HaveUnclearedMips()); + + texture_manager1_->SetLevelInfo(ref1.get(), + GL_TEXTURE_2D, + 0, + GL_RGBA, + 1, + 1, + 1, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + false); + EXPECT_FALSE(texture_manager1_->HaveUnrenderableTextures()); + EXPECT_TRUE(texture_manager1_->HaveUnsafeTextures()); + EXPECT_TRUE(texture_manager1_->HaveUnclearedMips()); + EXPECT_FALSE(texture_manager2_->HaveUnrenderableTextures()); + EXPECT_TRUE(texture_manager2_->HaveUnsafeTextures()); + EXPECT_TRUE(texture_manager2_->HaveUnclearedMips()); + + // Make texture cleared on one texture manager, should affect other one. + texture_manager1_->SetLevelCleared(ref1.get(), GL_TEXTURE_2D, 0, true); + EXPECT_FALSE(texture_manager1_->HaveUnsafeTextures()); + EXPECT_FALSE(texture_manager1_->HaveUnclearedMips()); + EXPECT_FALSE(texture_manager2_->HaveUnsafeTextures()); + EXPECT_FALSE(texture_manager2_->HaveUnclearedMips()); + + EXPECT_CALL(*gl_, DeleteTextures(1, _)) + .Times(1) + .RetiresOnSaturation(); + texture_manager1_->RemoveTexture(10); + texture_manager2_->RemoveTexture(20); +} + +TEST_F(SharedTextureTest, FBOCompletenessCheck) { + const GLenum kCompleteValue = GL_FRAMEBUFFER_COMPLETE; + FramebufferManager framebuffer_manager1(1, 1); + texture_manager1_->set_framebuffer_manager(&framebuffer_manager1); + FramebufferManager framebuffer_manager2(1, 1); + texture_manager2_->set_framebuffer_manager(&framebuffer_manager2); + + scoped_refptr<TextureRef> ref1 = texture_manager1_->CreateTexture(10, 10); + framebuffer_manager1.CreateFramebuffer(10, 10); + scoped_refptr<Framebuffer> framebuffer1 = + framebuffer_manager1.GetFramebuffer(10); + framebuffer1->AttachTexture( + GL_COLOR_ATTACHMENT0, ref1.get(), GL_TEXTURE_2D, 0, 0); + EXPECT_FALSE(framebuffer_manager1.IsComplete(framebuffer1.get())); + EXPECT_NE(kCompleteValue, framebuffer1->IsPossiblyComplete()); + + // Make FBO complete in manager 1. + texture_manager1_->SetTarget(ref1.get(), GL_TEXTURE_2D); + texture_manager1_->SetLevelInfo(ref1.get(), + GL_TEXTURE_2D, + 0, + GL_RGBA, + 1, + 1, + 1, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + true); + EXPECT_EQ(kCompleteValue, framebuffer1->IsPossiblyComplete()); + framebuffer_manager1.MarkAsComplete(framebuffer1.get()); + EXPECT_TRUE(framebuffer_manager1.IsComplete(framebuffer1.get())); + + // Share texture with manager 2. + scoped_refptr<TextureRef> ref2 = + texture_manager2_->Consume(20, ref1->texture()); + framebuffer_manager2.CreateFramebuffer(20, 20); + scoped_refptr<Framebuffer> framebuffer2 = + framebuffer_manager2.GetFramebuffer(20); + framebuffer2->AttachTexture( + GL_COLOR_ATTACHMENT0, ref2.get(), GL_TEXTURE_2D, 0, 0); + EXPECT_FALSE(framebuffer_manager2.IsComplete(framebuffer2.get())); + EXPECT_EQ(kCompleteValue, framebuffer2->IsPossiblyComplete()); + framebuffer_manager2.MarkAsComplete(framebuffer2.get()); + EXPECT_TRUE(framebuffer_manager2.IsComplete(framebuffer2.get())); + + // Change level for texture, both FBOs should be marked incomplete + texture_manager1_->SetLevelInfo(ref1.get(), + GL_TEXTURE_2D, + 0, + GL_RGBA, + 1, + 1, + 1, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + true); + EXPECT_FALSE(framebuffer_manager1.IsComplete(framebuffer1.get())); + EXPECT_EQ(kCompleteValue, framebuffer1->IsPossiblyComplete()); + framebuffer_manager1.MarkAsComplete(framebuffer1.get()); + EXPECT_TRUE(framebuffer_manager1.IsComplete(framebuffer1.get())); + EXPECT_FALSE(framebuffer_manager2.IsComplete(framebuffer2.get())); + EXPECT_EQ(kCompleteValue, framebuffer2->IsPossiblyComplete()); + framebuffer_manager2.MarkAsComplete(framebuffer2.get()); + EXPECT_TRUE(framebuffer_manager2.IsComplete(framebuffer2.get())); + + EXPECT_CALL(*gl_, DeleteFramebuffersEXT(1, _)) + .Times(2) + .RetiresOnSaturation(); + framebuffer_manager1.RemoveFramebuffer(10); + framebuffer_manager2.RemoveFramebuffer(20); + EXPECT_CALL(*gl_, DeleteTextures(1, _)) + .Times(1) + .RetiresOnSaturation(); + texture_manager1_->RemoveTexture(10); + texture_manager2_->RemoveTexture(20); +} + +TEST_F(SharedTextureTest, Memory) { + size_t initial_memory1 = memory_tracker1_->GetSize(MemoryTracker::kUnmanaged); + size_t initial_memory2 = memory_tracker2_->GetSize(MemoryTracker::kUnmanaged); + + // Newly created texture is unrenderable. + scoped_refptr<TextureRef> ref1 = texture_manager1_->CreateTexture(10, 10); + texture_manager1_->SetTarget(ref1.get(), GL_TEXTURE_2D); + texture_manager1_->SetLevelInfo(ref1.get(), + GL_TEXTURE_2D, + 0, + GL_RGBA, + 10, + 10, + 1, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + false); + + EXPECT_LT(0u, ref1->texture()->estimated_size()); + EXPECT_EQ(initial_memory1 + ref1->texture()->estimated_size(), + memory_tracker1_->GetSize(MemoryTracker::kUnmanaged)); + + // Associate new texture ref to other texture manager, it doesn't account for + // the texture memory, the first memory tracker still has it. + scoped_refptr<TextureRef> ref2 = + texture_manager2_->Consume(20, ref1->texture()); + EXPECT_EQ(initial_memory1 + ref1->texture()->estimated_size(), + memory_tracker1_->GetSize(MemoryTracker::kUnmanaged)); + EXPECT_EQ(initial_memory2, + memory_tracker2_->GetSize(MemoryTracker::kUnmanaged)); + + // Delete the texture, memory should go to the remaining tracker. + texture_manager1_->RemoveTexture(10); + ref1 = NULL; + EXPECT_EQ(initial_memory1, + memory_tracker1_->GetSize(MemoryTracker::kUnmanaged)); + EXPECT_EQ(initial_memory2 + ref2->texture()->estimated_size(), + memory_tracker2_->GetSize(MemoryTracker::kUnmanaged)); + + EXPECT_CALL(*gl_, DeleteTextures(1, _)) + .Times(1) + .RetiresOnSaturation(); + ref2 = NULL; + texture_manager2_->RemoveTexture(20); + EXPECT_EQ(initial_memory2, + memory_tracker2_->GetSize(MemoryTracker::kUnmanaged)); +} + +} // namespace gles2 +} // namespace gpu |