| // Copyright 2014 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/gles2_cmd_decoder.h" | 
 |  | 
 | #include "base/command_line.h" | 
 | #include "base/strings/string_number_conversions.h" | 
 | #include "gpu/command_buffer/common/gles2_cmd_format.h" | 
 | #include "gpu/command_buffer/common/gles2_cmd_utils.h" | 
 | #include "gpu/command_buffer/common/id_allocator.h" | 
 | #include "gpu/command_buffer/service/async_pixel_transfer_delegate_mock.h" | 
 | #include "gpu/command_buffer/service/async_pixel_transfer_manager.h" | 
 | #include "gpu/command_buffer/service/async_pixel_transfer_manager_mock.h" | 
 | #include "gpu/command_buffer/service/cmd_buffer_engine.h" | 
 | #include "gpu/command_buffer/service/context_group.h" | 
 | #include "gpu/command_buffer/service/context_state.h" | 
 | #include "gpu/command_buffer/service/gl_surface_mock.h" | 
 | #include "gpu/command_buffer/service/gles2_cmd_decoder_unittest.h" | 
 |  | 
 | #include "gpu/command_buffer/service/gpu_switches.h" | 
 | #include "gpu/command_buffer/service/image_manager.h" | 
 | #include "gpu/command_buffer/service/mailbox_manager.h" | 
 | #include "gpu/command_buffer/service/mocks.h" | 
 | #include "gpu/command_buffer/service/program_manager.h" | 
 | #include "gpu/command_buffer/service/test_helper.h" | 
 | #include "testing/gtest/include/gtest/gtest.h" | 
 | #include "ui/gl/gl_implementation.h" | 
 | #include "ui/gl/gl_mock.h" | 
 | #include "ui/gl/gl_surface_stub.h" | 
 |  | 
 | #if !defined(GL_DEPTH24_STENCIL8) | 
 | #define GL_DEPTH24_STENCIL8 0x88F0 | 
 | #endif | 
 |  | 
 | using ::gfx::MockGLInterface; | 
 | using ::testing::_; | 
 | using ::testing::DoAll; | 
 | using ::testing::InSequence; | 
 | using ::testing::Invoke; | 
 | using ::testing::MatcherCast; | 
 | using ::testing::Mock; | 
 | using ::testing::Pointee; | 
 | using ::testing::Return; | 
 | using ::testing::SaveArg; | 
 | using ::testing::SetArrayArgument; | 
 | using ::testing::SetArgumentPointee; | 
 | using ::testing::SetArgPointee; | 
 | using ::testing::StrEq; | 
 | using ::testing::StrictMock; | 
 |  | 
 | namespace gpu { | 
 | namespace gles2 { | 
 |  | 
 | using namespace cmds; | 
 |  | 
 | TEST_P(GLES2DecoderManualInitTest, AsyncPixelTransfers) { | 
 |   InitState init; | 
 |   init.extensions = "GL_CHROMIUM_async_pixel_transfers"; | 
 |   init.bind_generates_resource = true; | 
 |   InitDecoder(init); | 
 |  | 
 |   // Set up the texture. | 
 |   DoBindTexture(GL_TEXTURE_2D, client_texture_id_, kServiceTextureId); | 
 |   TextureRef* texture_ref = GetTexture(client_texture_id_); | 
 |   Texture* texture = texture_ref->texture(); | 
 |  | 
 |   // Set a mock Async delegate | 
 |   StrictMock<gpu::MockAsyncPixelTransferManager>* manager = | 
 |       new StrictMock<gpu::MockAsyncPixelTransferManager>; | 
 |   manager->Initialize(group().texture_manager()); | 
 |   decoder_->SetAsyncPixelTransferManagerForTest(manager); | 
 |   StrictMock<gpu::MockAsyncPixelTransferDelegate>* delegate = NULL; | 
 |  | 
 |   // Tex(Sub)Image2D upload commands. | 
 |   AsyncTexImage2DCHROMIUM teximage_cmd; | 
 |   teximage_cmd.Init(GL_TEXTURE_2D, | 
 |                     0, | 
 |                     GL_RGBA, | 
 |                     8, | 
 |                     8, | 
 |                     GL_RGBA, | 
 |                     GL_UNSIGNED_BYTE, | 
 |                     kSharedMemoryId, | 
 |                     kSharedMemoryOffset, | 
 |                     0, | 
 |                     0, | 
 |                     0); | 
 |   AsyncTexSubImage2DCHROMIUM texsubimage_cmd; | 
 |   texsubimage_cmd.Init(GL_TEXTURE_2D, | 
 |                        0, | 
 |                        0, | 
 |                        0, | 
 |                        8, | 
 |                        8, | 
 |                        GL_RGBA, | 
 |                        GL_UNSIGNED_BYTE, | 
 |                        kSharedMemoryId, | 
 |                        kSharedMemoryOffset, | 
 |                        0, | 
 |                        0, | 
 |                        0); | 
 |   WaitAsyncTexImage2DCHROMIUM wait_cmd; | 
 |   wait_cmd.Init(GL_TEXTURE_2D); | 
 |   WaitAllAsyncTexImage2DCHROMIUM wait_all_cmd; | 
 |   wait_all_cmd.Init(); | 
 |  | 
 |   // No transfer state exists initially. | 
 |   EXPECT_FALSE( | 
 |       decoder_->GetAsyncPixelTransferManager()->GetPixelTransferDelegate( | 
 |           texture_ref)); | 
 |  | 
 |   base::Closure bind_callback; | 
 |  | 
 |   // AsyncTexImage2D | 
 |   { | 
 |     // Create transfer state since it doesn't exist. | 
 |     EXPECT_EQ(texture_ref->num_observers(), 0); | 
 |     EXPECT_CALL(*manager, CreatePixelTransferDelegateImpl(texture_ref, _)) | 
 |         .WillOnce(Return( | 
 |             delegate = new StrictMock<gpu::MockAsyncPixelTransferDelegate>)) | 
 |         .RetiresOnSaturation(); | 
 |     EXPECT_CALL(*delegate, AsyncTexImage2D(_, _, _)) | 
 |         .WillOnce(SaveArg<2>(&bind_callback)) | 
 |         .RetiresOnSaturation(); | 
 |     // Command succeeds. | 
 |     EXPECT_EQ(error::kNoError, ExecuteCmd(teximage_cmd)); | 
 |     EXPECT_EQ(GL_NO_ERROR, GetGLError()); | 
 |     EXPECT_EQ( | 
 |         delegate, | 
 |         decoder_->GetAsyncPixelTransferManager()->GetPixelTransferDelegate( | 
 |             texture_ref)); | 
 |     EXPECT_TRUE(texture->IsImmutable()); | 
 |     // The texture is safe but the level has not been defined yet. | 
 |     EXPECT_TRUE(texture->SafeToRenderFrom()); | 
 |     GLsizei width, height; | 
 |     EXPECT_FALSE(texture->GetLevelSize(GL_TEXTURE_2D, 0, &width, &height)); | 
 |     EXPECT_EQ(texture_ref->num_observers(), 1); | 
 |   } | 
 |   { | 
 |     // Async redefinitions are not allowed! | 
 |     // Command fails. | 
 |     EXPECT_EQ(error::kNoError, ExecuteCmd(teximage_cmd)); | 
 |     EXPECT_EQ(GL_INVALID_OPERATION, GetGLError()); | 
 |     EXPECT_EQ( | 
 |         delegate, | 
 |         decoder_->GetAsyncPixelTransferManager()->GetPixelTransferDelegate( | 
 |             texture_ref)); | 
 |     EXPECT_TRUE(texture->IsImmutable()); | 
 |     EXPECT_TRUE(texture->SafeToRenderFrom()); | 
 |   } | 
 |  | 
 |   // Binding/defining of the async transfer | 
 |   { | 
 |     // TODO(epenner): We should check that the manager gets the | 
 |     // BindCompletedAsyncTransfers() call, which is required to | 
 |     // guarantee the delegate calls the bind callback. | 
 |  | 
 |     // Simulate the bind callback from the delegate. | 
 |     bind_callback.Run(); | 
 |  | 
 |     // After the bind callback is run, the texture is safe, | 
 |     // and has the right size etc. | 
 |     EXPECT_TRUE(texture->SafeToRenderFrom()); | 
 |     GLsizei width, height; | 
 |     EXPECT_TRUE(texture->GetLevelSize(GL_TEXTURE_2D, 0, &width, &height)); | 
 |     EXPECT_EQ(width, 8); | 
 |     EXPECT_EQ(height, 8); | 
 |   } | 
 |  | 
 |   // AsyncTexSubImage2D | 
 |   EXPECT_CALL(*delegate, Destroy()).RetiresOnSaturation(); | 
 |   decoder_->GetAsyncPixelTransferManager()->ClearPixelTransferDelegateForTest( | 
 |       texture_ref); | 
 |   EXPECT_EQ(texture_ref->num_observers(), 0); | 
 |   texture->SetImmutable(false); | 
 |   { | 
 |     // Create transfer state since it doesn't exist. | 
 |     EXPECT_CALL(*manager, CreatePixelTransferDelegateImpl(texture_ref, _)) | 
 |         .WillOnce(Return( | 
 |             delegate = new StrictMock<gpu::MockAsyncPixelTransferDelegate>)) | 
 |         .RetiresOnSaturation(); | 
 |     EXPECT_CALL(*delegate, AsyncTexSubImage2D(_, _)).RetiresOnSaturation(); | 
 |     // Command succeeds. | 
 |     EXPECT_EQ(error::kNoError, ExecuteCmd(texsubimage_cmd)); | 
 |     EXPECT_EQ(GL_NO_ERROR, GetGLError()); | 
 |     EXPECT_EQ( | 
 |         delegate, | 
 |         decoder_->GetAsyncPixelTransferManager()->GetPixelTransferDelegate( | 
 |             texture_ref)); | 
 |     EXPECT_TRUE(texture->IsImmutable()); | 
 |     EXPECT_TRUE(texture->SafeToRenderFrom()); | 
 |   } | 
 |   { | 
 |     // No transfer is in progress. | 
 |     EXPECT_CALL(*delegate, TransferIsInProgress()) | 
 |         .WillOnce(Return(false))  // texSubImage validation | 
 |         .WillOnce(Return(false))  // async validation | 
 |         .RetiresOnSaturation(); | 
 |     EXPECT_CALL(*delegate, AsyncTexSubImage2D(_, _)).RetiresOnSaturation(); | 
 |     // Command succeeds. | 
 |     EXPECT_EQ(error::kNoError, ExecuteCmd(texsubimage_cmd)); | 
 |     EXPECT_EQ(GL_NO_ERROR, GetGLError()); | 
 |     EXPECT_EQ( | 
 |         delegate, | 
 |         decoder_->GetAsyncPixelTransferManager()->GetPixelTransferDelegate( | 
 |             texture_ref)); | 
 |     EXPECT_TRUE(texture->IsImmutable()); | 
 |     EXPECT_TRUE(texture->SafeToRenderFrom()); | 
 |   } | 
 |   { | 
 |     // A transfer is still in progress! | 
 |     EXPECT_CALL(*delegate, TransferIsInProgress()) | 
 |         .WillOnce(Return(true)) | 
 |         .RetiresOnSaturation(); | 
 |     // No async call, command fails. | 
 |     EXPECT_EQ(error::kNoError, ExecuteCmd(texsubimage_cmd)); | 
 |     EXPECT_EQ(GL_INVALID_OPERATION, GetGLError()); | 
 |     EXPECT_EQ( | 
 |         delegate, | 
 |         decoder_->GetAsyncPixelTransferManager()->GetPixelTransferDelegate( | 
 |             texture_ref)); | 
 |     EXPECT_TRUE(texture->IsImmutable()); | 
 |     EXPECT_TRUE(texture->SafeToRenderFrom()); | 
 |   } | 
 |  | 
 |   // Delete delegate on DeleteTexture. | 
 |   { | 
 |     EXPECT_EQ(texture_ref->num_observers(), 1); | 
 |     EXPECT_CALL(*delegate, Destroy()).RetiresOnSaturation(); | 
 |     DoDeleteTexture(client_texture_id_, kServiceTextureId); | 
 |     EXPECT_FALSE( | 
 |         decoder_->GetAsyncPixelTransferManager()->GetPixelTransferDelegate( | 
 |             texture_ref)); | 
 |     texture = NULL; | 
 |     texture_ref = NULL; | 
 |     delegate = NULL; | 
 |   } | 
 |  | 
 |   // WaitAsyncTexImage2D | 
 |   { | 
 |     // Get a fresh texture since the existing texture cannot be respecified | 
 |     // asynchronously and AsyncTexSubImage2D does not involve binding. | 
 |     EXPECT_CALL(*gl_, GenTextures(1, _)) | 
 |         .WillOnce(SetArgumentPointee<1>(kServiceTextureId)); | 
 |     DoBindTexture(GL_TEXTURE_2D, client_texture_id_, kServiceTextureId); | 
 |     texture_ref = GetTexture(client_texture_id_); | 
 |     texture = texture_ref->texture(); | 
 |     texture->SetImmutable(false); | 
 |     // Create transfer state since it doesn't exist. | 
 |     EXPECT_CALL(*manager, CreatePixelTransferDelegateImpl(texture_ref, _)) | 
 |         .WillOnce(Return( | 
 |             delegate = new StrictMock<gpu::MockAsyncPixelTransferDelegate>)) | 
 |         .RetiresOnSaturation(); | 
 |     EXPECT_CALL(*delegate, AsyncTexImage2D(_, _, _)).RetiresOnSaturation(); | 
 |     // Start async transfer. | 
 |     EXPECT_EQ(error::kNoError, ExecuteCmd(teximage_cmd)); | 
 |     EXPECT_EQ(GL_NO_ERROR, GetGLError()); | 
 |     EXPECT_EQ( | 
 |         delegate, | 
 |         decoder_->GetAsyncPixelTransferManager()->GetPixelTransferDelegate( | 
 |             texture_ref)); | 
 |  | 
 |     EXPECT_TRUE(texture->IsImmutable()); | 
 |     // Wait for completion. | 
 |     EXPECT_CALL(*delegate, WaitForTransferCompletion()); | 
 |     EXPECT_CALL(*manager, BindCompletedAsyncTransfers()); | 
 |     EXPECT_EQ(error::kNoError, ExecuteCmd(wait_cmd)); | 
 |     EXPECT_EQ(GL_NO_ERROR, GetGLError()); | 
 |   } | 
 |  | 
 |   // WaitAllAsyncTexImage2D | 
 |   EXPECT_CALL(*delegate, Destroy()).RetiresOnSaturation(); | 
 |   DoDeleteTexture(client_texture_id_, kServiceTextureId); | 
 |   EXPECT_FALSE( | 
 |       decoder_->GetAsyncPixelTransferManager()->GetPixelTransferDelegate( | 
 |           texture_ref)); | 
 |   texture = NULL; | 
 |   texture_ref = NULL; | 
 |   delegate = NULL; | 
 |   { | 
 |     // Get a fresh texture since the existing texture cannot be respecified | 
 |     // asynchronously and AsyncTexSubImage2D does not involve binding. | 
 |     EXPECT_CALL(*gl_, GenTextures(1, _)) | 
 |         .WillOnce(SetArgumentPointee<1>(kServiceTextureId)); | 
 |     DoBindTexture(GL_TEXTURE_2D, client_texture_id_, kServiceTextureId); | 
 |     texture_ref = GetTexture(client_texture_id_); | 
 |     texture = texture_ref->texture(); | 
 |     texture->SetImmutable(false); | 
 |     // Create transfer state since it doesn't exist. | 
 |     EXPECT_CALL(*manager, CreatePixelTransferDelegateImpl(texture_ref, _)) | 
 |         .WillOnce(Return( | 
 |             delegate = new StrictMock<gpu::MockAsyncPixelTransferDelegate>)) | 
 |         .RetiresOnSaturation(); | 
 |     EXPECT_CALL(*delegate, AsyncTexImage2D(_, _, _)).RetiresOnSaturation(); | 
 |     // Start async transfer. | 
 |     EXPECT_EQ(error::kNoError, ExecuteCmd(teximage_cmd)); | 
 |     EXPECT_EQ(GL_NO_ERROR, GetGLError()); | 
 |     EXPECT_EQ( | 
 |         delegate, | 
 |         decoder_->GetAsyncPixelTransferManager()->GetPixelTransferDelegate( | 
 |             texture_ref)); | 
 |  | 
 |     EXPECT_TRUE(texture->IsImmutable()); | 
 |     // Wait for completion of all uploads. | 
 |     EXPECT_CALL(*manager, WaitAllAsyncTexImage2D()).RetiresOnSaturation(); | 
 |     EXPECT_CALL(*manager, BindCompletedAsyncTransfers()); | 
 |     EXPECT_EQ(error::kNoError, ExecuteCmd(wait_all_cmd)); | 
 |     EXPECT_EQ(GL_NO_ERROR, GetGLError()); | 
 |   } | 
 |  | 
 |   // Remove PixelTransferManager before the decoder destroys. | 
 |   EXPECT_CALL(*delegate, Destroy()).RetiresOnSaturation(); | 
 |   decoder_->ResetAsyncPixelTransferManagerForTest(); | 
 |   manager = NULL; | 
 | } | 
 |  | 
 | TEST_P(GLES2DecoderManualInitTest, AsyncPixelTransferManager) { | 
 |   InitState init; | 
 |   init.extensions = "GL_CHROMIUM_async_pixel_transfers"; | 
 |   init.bind_generates_resource = true; | 
 |   InitDecoder(init); | 
 |  | 
 |   // Set up the texture. | 
 |   DoBindTexture(GL_TEXTURE_2D, client_texture_id_, kServiceTextureId); | 
 |   TextureRef* texture_ref = GetTexture(client_texture_id_); | 
 |  | 
 |   // Set a mock Async delegate. | 
 |   StrictMock<gpu::MockAsyncPixelTransferManager>* manager = | 
 |       new StrictMock<gpu::MockAsyncPixelTransferManager>; | 
 |   manager->Initialize(group().texture_manager()); | 
 |   decoder_->SetAsyncPixelTransferManagerForTest(manager); | 
 |   StrictMock<gpu::MockAsyncPixelTransferDelegate>* delegate = NULL; | 
 |  | 
 |   AsyncTexImage2DCHROMIUM teximage_cmd; | 
 |   teximage_cmd.Init(GL_TEXTURE_2D, | 
 |                     0, | 
 |                     GL_RGBA, | 
 |                     8, | 
 |                     8, | 
 |                     GL_RGBA, | 
 |                     GL_UNSIGNED_BYTE, | 
 |                     kSharedMemoryId, | 
 |                     kSharedMemoryOffset, | 
 |                     0, | 
 |                     0, | 
 |                     0); | 
 |  | 
 |   // No transfer delegate exists initially. | 
 |   EXPECT_FALSE( | 
 |       decoder_->GetAsyncPixelTransferManager()->GetPixelTransferDelegate( | 
 |           texture_ref)); | 
 |  | 
 |   // Create delegate on AsyncTexImage2D. | 
 |   { | 
 |     EXPECT_CALL(*manager, CreatePixelTransferDelegateImpl(texture_ref, _)) | 
 |         .WillOnce(Return( | 
 |             delegate = new StrictMock<gpu::MockAsyncPixelTransferDelegate>)) | 
 |         .RetiresOnSaturation(); | 
 |     EXPECT_CALL(*delegate, AsyncTexImage2D(_, _, _)).RetiresOnSaturation(); | 
 |  | 
 |     // Command succeeds. | 
 |     EXPECT_EQ(error::kNoError, ExecuteCmd(teximage_cmd)); | 
 |     EXPECT_EQ(GL_NO_ERROR, GetGLError()); | 
 |   } | 
 |  | 
 |   // Delegate is cached. | 
 |   EXPECT_EQ(delegate, | 
 |             decoder_->GetAsyncPixelTransferManager()->GetPixelTransferDelegate( | 
 |                 texture_ref)); | 
 |  | 
 |   // Delete delegate on manager teardown. | 
 |   { | 
 |     EXPECT_EQ(texture_ref->num_observers(), 1); | 
 |     EXPECT_CALL(*delegate, Destroy()).RetiresOnSaturation(); | 
 |     decoder_->ResetAsyncPixelTransferManagerForTest(); | 
 |     manager = NULL; | 
 |  | 
 |     // Texture ref still valid. | 
 |     EXPECT_EQ(texture_ref, GetTexture(client_texture_id_)); | 
 |     EXPECT_EQ(texture_ref->num_observers(), 0); | 
 |   } | 
 | } | 
 |  | 
 | }  // namespace gles2 | 
 | }  // namespace gpu |