| // Copyright 2018 The Chromium Authors | |
| // 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/raster_decoder.h" | |
| #include <limits> | |
| #include <memory> | |
| #include <string> | |
| #include <utility> | |
| #include "base/command_line.h" | |
| #include "base/compiler_specific.h" | |
| #include "base/functional/callback_helpers.h" | |
| #include "base/memory/ptr_util.h" | |
| #include "base/memory/raw_ptr.h" | |
| #include "cc/paint/paint_op_buffer.h" | |
| #include "gpu/command_buffer/common/gles2_cmd_utils.h" | |
| #include "gpu/command_buffer/common/mailbox.h" | |
| #include "gpu/command_buffer/common/raster_cmd_format.h" | |
| #include "gpu/command_buffer/common/shared_image_usage.h" | |
| #include "gpu/command_buffer/service/query_manager.h" | |
| #include "gpu/command_buffer/service/raster_decoder_unittest_base.h" | |
| #include "gpu/command_buffer/service/shared_context_state.h" | |
| #include "gpu/command_buffer/service/shared_image/gl_texture_image_backing_factory.h" | |
| #include "gpu/command_buffer/service/shared_image/shared_image_factory.h" | |
| #include "gpu/command_buffer/service/shared_image/shared_image_manager.h" | |
| #include "gpu/command_buffer/service/shared_image/shared_image_representation.h" | |
| #include "gpu/command_buffer/service/shared_image/test_image_backing.h" | |
| #include "gpu/command_buffer/service/test_helper.h" | |
| #include "gpu/config/gpu_preferences.h" | |
| #include "testing/gtest/include/gtest/gtest.h" | |
| #include "third_party/skia/include/core/SkBitmap.h" | |
| #include "ui/gl/gl_mock.h" | |
| #include "ui/gl/gl_surface_stub.h" | |
| #include "ui/gl/init/gl_factory.h" | |
| #include "ui/gl/test/gl_surface_test_support.h" | |
| using ::testing::_; | |
| using ::testing::Return; | |
| using ::testing::SetArgPointee; | |
| namespace gpu { | |
| namespace raster { | |
| namespace { | |
| void CopyMailboxes(GLbyte (&output)[sizeof(Mailbox) * 2], | |
| const Mailbox& source, | |
| const Mailbox& dest) { | |
| UNSAFE_TODO(memcpy(output, source.name, sizeof(source.name))); | |
| UNSAFE_TODO( | |
| memcpy(output + sizeof(source.name), dest.name, sizeof(dest.name))); | |
| } | |
| } // anonymous namespace | |
| class RasterDecoderTest : public RasterDecoderTestBase { | |
| public: | |
| RasterDecoderTest() = default; | |
| }; | |
| INSTANTIATE_TEST_SUITE_P(Service, RasterDecoderTest, ::testing::Bool()); | |
| INSTANTIATE_TEST_SUITE_P(Service, | |
| RasterDecoderManualInitTest, | |
| ::testing::Bool()); | |
| const GLsync kGlSync = reinterpret_cast<GLsync>(0xdeadbeef); | |
| TEST_P(RasterDecoderTest, BeginEndQueryEXTCommandsCompletedCHROMIUM) { | |
| GenHelper<cmds::GenQueriesEXTImmediate>(kNewClientId); | |
| cmds::BeginQueryEXT begin_cmd; | |
| begin_cmd.Init(GL_COMMANDS_COMPLETED_CHROMIUM, kNewClientId, | |
| shared_memory_id_, kSharedMemoryOffset); | |
| EXPECT_EQ(error::kNoError, ExecuteCmd(begin_cmd)); | |
| EXPECT_EQ(GL_NO_ERROR, GetGLError()); | |
| QueryManager* query_manager = decoder_->GetQueryManager(); | |
| ASSERT_TRUE(query_manager != nullptr); | |
| QueryManager::Query* query = query_manager->GetQuery(kNewClientId); | |
| ASSERT_TRUE(query != nullptr); | |
| EXPECT_FALSE(query->IsPending()); | |
| EXPECT_TRUE(query->IsActive()); | |
| EXPECT_CALL(*gl_, Flush()).RetiresOnSaturation(); | |
| EXPECT_CALL(*gl_, FenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0)) | |
| .WillOnce(Return(kGlSync)) | |
| .RetiresOnSaturation(); | |
| #if DCHECK_IS_ON() | |
| EXPECT_CALL(*gl_, IsSync(kGlSync)) | |
| .WillOnce(Return(GL_TRUE)) | |
| .RetiresOnSaturation(); | |
| #endif | |
| cmds::EndQueryEXT end_cmd; | |
| end_cmd.Init(GL_COMMANDS_COMPLETED_CHROMIUM, 1); | |
| EXPECT_EQ(error::kNoError, ExecuteCmd(end_cmd)); | |
| EXPECT_EQ(GL_NO_ERROR, GetGLError()); | |
| EXPECT_TRUE(query->IsPending()); | |
| EXPECT_FALSE(query->IsActive()); | |
| #if DCHECK_IS_ON() | |
| EXPECT_CALL(*gl_, IsSync(kGlSync)) | |
| .WillOnce(Return(GL_TRUE)) | |
| .RetiresOnSaturation(); | |
| #endif | |
| EXPECT_CALL(*gl_, ClientWaitSync(kGlSync, _, _)) | |
| .WillOnce(Return(GL_TIMEOUT_EXPIRED)) | |
| .RetiresOnSaturation(); | |
| query_manager->ProcessPendingQueries(false); | |
| EXPECT_TRUE(query->IsPending()); | |
| #if DCHECK_IS_ON() | |
| EXPECT_CALL(*gl_, IsSync(kGlSync)) | |
| .WillOnce(Return(GL_TRUE)) | |
| .RetiresOnSaturation(); | |
| #endif | |
| EXPECT_CALL(*gl_, ClientWaitSync(kGlSync, _, _)) | |
| .WillOnce(Return(GL_ALREADY_SIGNALED)) | |
| .RetiresOnSaturation(); | |
| query_manager->ProcessPendingQueries(false); | |
| EXPECT_FALSE(query->IsPending()); | |
| #if DCHECK_IS_ON() | |
| EXPECT_CALL(*gl_, IsSync(kGlSync)) | |
| .WillOnce(Return(GL_TRUE)) | |
| .RetiresOnSaturation(); | |
| #endif | |
| EXPECT_CALL(*gl_, DeleteSync(kGlSync)).Times(1).RetiresOnSaturation(); | |
| ResetDecoder(); | |
| } | |
| TEST_P(RasterDecoderTest, BeginEndQueryEXTCommandsIssuedCHROMIUM) { | |
| cmds::BeginQueryEXT begin_cmd; | |
| GenHelper<cmds::GenQueriesEXTImmediate>(kNewClientId); | |
| // Test valid parameters work. | |
| begin_cmd.Init(GL_COMMANDS_ISSUED_CHROMIUM, kNewClientId, shared_memory_id_, | |
| kSharedMemoryOffset); | |
| EXPECT_EQ(error::kNoError, ExecuteCmd(begin_cmd)); | |
| EXPECT_EQ(GL_NO_ERROR, GetGLError()); | |
| QueryManager* query_manager = decoder_->GetQueryManager(); | |
| ASSERT_TRUE(query_manager != nullptr); | |
| QueryManager::Query* query = query_manager->GetQuery(kNewClientId); | |
| ASSERT_TRUE(query != nullptr); | |
| EXPECT_FALSE(query->IsPending()); | |
| EXPECT_TRUE(query->IsActive()); | |
| // Test end succeeds. | |
| cmds::EndQueryEXT end_cmd; | |
| end_cmd.Init(GL_COMMANDS_ISSUED_CHROMIUM, 1); | |
| EXPECT_EQ(error::kNoError, ExecuteCmd(end_cmd)); | |
| EXPECT_EQ(GL_NO_ERROR, GetGLError()); | |
| EXPECT_FALSE(query->IsPending()); | |
| EXPECT_FALSE(query->IsActive()); | |
| } | |
| TEST_P(RasterDecoderManualInitTest, GetCapabilitiesHalfFloatLinear) { | |
| InitState init; | |
| init.extensions.push_back("GL_OES_texture_half_float_linear"); | |
| InitDecoder(init); | |
| AddExpectationsForGetCapabilities(); | |
| const auto& caps = decoder_->GetCapabilities(); | |
| EXPECT_TRUE(caps.texture_half_float_linear); | |
| } | |
| TEST_P(RasterDecoderManualInitTest, GetCapabilitiesNorm16) { | |
| // R16 requires an ES3 context plus the extension to be available. | |
| InitState init; | |
| init.context_type = CONTEXT_TYPE_OPENGLES3; | |
| init.gl_version = "OpenGL ES 3.0"; | |
| init.extensions.push_back("GL_EXT_texture_norm16"); | |
| InitDecoder(init); | |
| AddExpectationsForGetCapabilities(); | |
| const auto& caps = decoder_->GetCapabilities(); | |
| EXPECT_TRUE(caps.texture_norm16); | |
| } | |
| class RasterDecoderOOPTest : public testing::Test, public DecoderClient { | |
| public: | |
| void SetUp() override { | |
| display_ = gl::GLSurfaceTestSupport::InitializeOneOff(); | |
| gpu::GpuDriverBugWorkarounds workarounds; | |
| scoped_refptr<gl::GLShareGroup> share_group = new gl::GLShareGroup(); | |
| scoped_refptr<gl::GLSurface> surface = | |
| gl::init::CreateOffscreenGLSurface(display_, gfx::Size()); | |
| scoped_refptr<gl::GLContext> context = gl::init::CreateGLContext( | |
| share_group.get(), surface.get(), gl::GLContextAttribs()); | |
| ASSERT_TRUE(context->MakeCurrent(surface.get())); | |
| gpu_feature_info_.status_values[GPU_FEATURE_TYPE_GPU_TILE_RASTERIZATION] = | |
| kGpuFeatureStatusEnabled; | |
| context_state_ = base::MakeRefCounted<SharedContextState>( | |
| std::move(share_group), std::move(surface), std::move(context), | |
| false /* use_virtualized_gl_contexts */, base::DoNothing(), | |
| GpuPreferences().gr_context_type); | |
| context_state_->InitializeSkia(GpuPreferences(), workarounds); | |
| context_state_->InitializeGL(GpuPreferences(), workarounds, | |
| gpu_feature_info_); | |
| decoder_ = CreateDecoder(); | |
| scoped_refptr<gpu::Buffer> buffer = | |
| command_buffer_service_->CreateTransferBufferHelper(kSharedBufferSize, | |
| &shared_memory_id_); | |
| shared_memory_offset_ = kSharedMemoryOffset; | |
| shared_memory_address_ = UNSAFE_TODO( | |
| static_cast<int8_t*>(buffer->memory()) + shared_memory_offset_); | |
| workarounds.webgl_or_caps_max_texture_size = INT_MAX - 1; | |
| shared_image_factory_ = std::make_unique<SharedImageFactory>( | |
| GpuPreferences(), workarounds, GpuFeatureInfo(), context_state_.get(), | |
| &shared_image_manager_, nullptr, | |
| /*is_for_display_compositor=*/false); | |
| client_texture_mailbox_ = | |
| CreateMailbox(viz::SinglePlaneFormat::kRGBA_8888, /*width=*/2, | |
| /*height=*/2, /*cleared=*/false); | |
| // When creating the mailbox, we create a WrappedSkImage shared image which | |
| // sets this flag to true. Some tests expect this flag to be false when | |
| // testing so we reset it back here to false. | |
| context_state_->set_need_context_state_reset(/*reset=*/false); | |
| } | |
| void TearDown() override { | |
| context_state_->MakeCurrent(nullptr); | |
| decoder_->EndDecoding(); | |
| decoder_->Destroy(!decoder_->WasContextLost()); | |
| decoder_.reset(); | |
| command_buffer_service_.reset(); | |
| shared_image_factory_->DestroyAllSharedImages(true); | |
| shared_image_factory_.reset(); | |
| context_state_.reset(); | |
| context_state_ = nullptr; | |
| gl::GLSurfaceTestSupport::ShutdownGL(display_); | |
| } | |
| RasterDecoderOOPTest() : memory_tracker_(nullptr) { | |
| UNSAFE_TODO(memset(immediate_buffer_, 0xEE, sizeof(immediate_buffer_))); | |
| } | |
| // DecoderClient implementation. | |
| void OnConsoleMessage(int32_t id, const std::string& message) override {} | |
| void CacheBlob(gpu::GpuDiskCacheType type, | |
| const std::string& key, | |
| const std::string& blob) override {} | |
| void OnFenceSyncRelease(uint64_t release) override {} | |
| void OnDescheduleUntilFinished() override {} | |
| void OnRescheduleAfterFinished() override {} | |
| void ScheduleGrContextCleanup() override {} | |
| void HandleReturnData(base::span<const uint8_t> data) override {} | |
| std::unique_ptr<RasterDecoder> CreateDecoder() { | |
| command_buffer_service_ = std::make_unique<FakeCommandBufferServiceBase>(); | |
| auto decoder = RasterDecoder::Create( | |
| this, command_buffer_service_.get(), &outputter_, gpu_feature_info_, | |
| GpuPreferences(), /*memory_tracker=*/nullptr, &shared_image_manager_, | |
| context_state_, /*is_privileged=*/true); | |
| CHECK_EQ(decoder->Initialize(/*lose_context_when_out_of_memory=*/false), | |
| ContextResult::kSuccess); | |
| return decoder; | |
| } | |
| gpu::Mailbox CreateMailbox(viz::SharedImageFormat format, | |
| GLsizei width, | |
| GLsizei height, | |
| bool cleared) { | |
| gpu::Mailbox mailbox = gpu::Mailbox::Generate(); | |
| gfx::Size size(width, height); | |
| auto color_space = gfx::ColorSpace::CreateSRGB(); | |
| // Via this function, this test creates mailboxes that are used as both the | |
| // sources of reads and destinations of writes via the raster interface. | |
| shared_image_factory_->CreateSharedImage( | |
| mailbox, | |
| SharedImageInfo( | |
| format, size, color_space, kTopLeft_GrSurfaceOrigin, | |
| kPremul_SkAlphaType, | |
| SHARED_IMAGE_USAGE_RASTER_READ | SHARED_IMAGE_USAGE_RASTER_WRITE, | |
| "TestLabel"), | |
| gpu::kNullSurfaceHandle); | |
| if (cleared) { | |
| SharedImageRepresentationFactory repr_factory(shared_image_manager(), | |
| nullptr); | |
| auto representation = | |
| repr_factory.ProduceSkia(mailbox, context_state_.get()); | |
| representation->SetCleared(); | |
| } | |
| return mailbox; | |
| } | |
| template <typename T> | |
| T* GetImmediateAs() { | |
| return reinterpret_cast<T*>(immediate_buffer_); | |
| } | |
| template <typename T> | |
| error::Error ExecuteCmd(RasterDecoder* decoder, const T& cmd) { | |
| static_assert(T::kArgFlags == cmd::kFixed, | |
| "T::kArgFlags should equal cmd::kFixed"); | |
| int entries_processed = 0; | |
| return decoder->DoCommands(1, reinterpret_cast<const void*>(&cmd), | |
| ComputeNumEntries(sizeof(cmd)), | |
| &entries_processed); | |
| } | |
| template <typename T> | |
| error::Error ExecuteImmediateCmd(const T& cmd, size_t data_size) { | |
| static_assert(T::kArgFlags == cmd::kAtLeastN, | |
| "T::kArgFlags should equal cmd::kAtLeastN"); | |
| int entries_processed = 0; | |
| return decoder_->DoCommands(1, reinterpret_cast<const void*>(&cmd), | |
| ComputeNumEntries(sizeof(cmd) + data_size), | |
| &entries_processed); | |
| } | |
| template <typename T> | |
| T GetSharedMemoryAs() { | |
| return reinterpret_cast<T>(shared_memory_address_.get()); | |
| } | |
| GLint GetGLError() { | |
| cmds::GetError cmd; | |
| cmd.Init(shared_memory_id_, shared_memory_offset_); | |
| EXPECT_EQ(error::kNoError, ExecuteCmd(decoder_.get(), cmd)); | |
| return static_cast<GLint>(*GetSharedMemoryAs<GLenum*>()); | |
| } | |
| SharedImageManager* shared_image_manager() { return &shared_image_manager_; } | |
| protected: | |
| GpuFeatureInfo gpu_feature_info_; | |
| gles2::TraceOutputter outputter_; | |
| std::unique_ptr<FakeCommandBufferServiceBase> command_buffer_service_; | |
| MemoryTypeTracker memory_tracker_; | |
| scoped_refptr<SharedContextState> context_state_; | |
| gpu::Mailbox client_texture_mailbox_; | |
| std::unique_ptr<RasterDecoder> decoder_; | |
| int32_t shared_memory_id_ = 0; | |
| uint32_t shared_memory_offset_ = 0; | |
| raw_ptr<void> shared_memory_address_ = nullptr; | |
| const size_t kSharedBufferSize = 2048; | |
| const uint32_t kSharedMemoryOffset = 132; | |
| uint32_t immediate_buffer_[64]; | |
| std::unique_ptr<SharedImageFactory> shared_image_factory_; | |
| SharedImageManager shared_image_manager_; | |
| raw_ptr<gl::GLDisplay> display_ = nullptr; | |
| }; | |
| TEST_F(RasterDecoderOOPTest, CopyTexSubImage2DSizeMismatch) { | |
| context_state_->set_need_context_state_reset(true); | |
| // Create uninitialized source texture mailbox. | |
| gpu::Mailbox source_texture_mailbox = | |
| CreateMailbox(viz::SinglePlaneFormat::kRGBA_8888, | |
| /*width=*/1, /*height=*/1, | |
| /*cleared=*/true); | |
| GLbyte mailboxes[sizeof(gpu::Mailbox) * 2]; | |
| CopyMailboxes(mailboxes, source_texture_mailbox, client_texture_mailbox_); | |
| SharedImageRepresentationFactory repr_factory(shared_image_manager(), | |
| nullptr); | |
| auto representation = | |
| repr_factory.ProduceSkia(client_texture_mailbox_, context_state_.get()); | |
| { | |
| // This will initialize the bottom right corner of destination. | |
| auto& cmd = *GetImmediateAs<cmds::CopySharedImageINTERNALImmediate>(); | |
| cmd.Init(1, 1, 0, 0, 1, 1, 1, 1, mailboxes); | |
| EXPECT_EQ(error::kNoError, ExecuteImmediateCmd(cmd, sizeof(mailboxes))); | |
| EXPECT_EQ(GL_NO_ERROR, GetGLError()); | |
| EXPECT_EQ(representation->ClearedRect(), gfx::Rect(1, 1, 1, 1)); | |
| } | |
| { | |
| // Dest rect outside of dest bounds | |
| auto& cmd = *GetImmediateAs<cmds::CopySharedImageINTERNALImmediate>(); | |
| cmd.Init(2, 2, 0, 0, 1, 1, 1, 1, mailboxes); | |
| EXPECT_EQ(error::kNoError, ExecuteImmediateCmd(cmd, sizeof(mailboxes))); | |
| EXPECT_EQ(GL_INVALID_VALUE, GetGLError()); | |
| EXPECT_EQ(representation->ClearedRect(), gfx::Rect(1, 1, 1, 1)); | |
| } | |
| { | |
| // Source rect outside of source bounds | |
| auto& cmd = *GetImmediateAs<cmds::CopySharedImageINTERNALImmediate>(); | |
| cmd.Init(0, 0, 0, 0, 2, 2, 2, 2, mailboxes); | |
| EXPECT_EQ(error::kNoError, ExecuteImmediateCmd(cmd, sizeof(mailboxes))); | |
| EXPECT_EQ(GL_INVALID_VALUE, GetGLError()); | |
| EXPECT_EQ(representation->ClearedRect(), gfx::Rect(1, 1, 1, 1)); | |
| } | |
| { | |
| // Empty source rect (zero width) with non-empty dest rect. | |
| auto& cmd = *GetImmediateAs<cmds::CopySharedImageINTERNALImmediate>(); | |
| cmd.Init(0, 0, 0, 0, 0, 1, 2, 2, mailboxes); | |
| EXPECT_EQ(error::kNoError, ExecuteImmediateCmd(cmd, sizeof(mailboxes))); | |
| EXPECT_EQ(GL_INVALID_VALUE, GetGLError()); | |
| EXPECT_EQ(representation->ClearedRect(), gfx::Rect(1, 1, 1, 1)); | |
| } | |
| { | |
| // Empty dest rect (zero height) with non-empty source rect. | |
| auto& cmd = *GetImmediateAs<cmds::CopySharedImageINTERNALImmediate>(); | |
| cmd.Init(0, 0, 0, 0, 1, 1, 2, 0, mailboxes); | |
| EXPECT_EQ(error::kNoError, ExecuteImmediateCmd(cmd, sizeof(mailboxes))); | |
| EXPECT_EQ(GL_INVALID_VALUE, GetGLError()); | |
| EXPECT_EQ(representation->ClearedRect(), gfx::Rect(1, 1, 1, 1)); | |
| } | |
| } | |
| TEST_F(RasterDecoderOOPTest, CopySharedImageEmptyRect) { | |
| context_state_->set_need_context_state_reset(true); | |
| gpu::Mailbox source_texture_mailbox = | |
| CreateMailbox(viz::SinglePlaneFormat::kRGBA_8888, | |
| /*width=*/2, /*height=*/2, | |
| /*cleared=*/true); | |
| GLbyte mailboxes[sizeof(gpu::Mailbox) * 2]; | |
| CopyMailboxes(mailboxes, source_texture_mailbox, client_texture_mailbox_); | |
| SharedImageRepresentationFactory repr_factory(shared_image_manager(), | |
| nullptr); | |
| auto representation = | |
| repr_factory.ProduceSkia(client_texture_mailbox_, context_state_.get()); | |
| ASSERT_TRUE(representation); | |
| EXPECT_FALSE(representation->IsCleared()); | |
| auto& cmd = *GetImmediateAs<cmds::CopySharedImageINTERNALImmediate>(); | |
| // Empty source width with non-empty destination rect. | |
| cmd.Init(0, 0, 0, 0, 0, 2, 2, 2, mailboxes); | |
| EXPECT_EQ(error::kNoError, ExecuteImmediateCmd(cmd, sizeof(mailboxes))); | |
| EXPECT_EQ(GL_INVALID_VALUE, GetGLError()); | |
| EXPECT_EQ(representation->ClearedRect(), gfx::Rect()); | |
| EXPECT_FALSE(representation->IsCleared()); | |
| // Empty source height with non-empty destination rect. | |
| cmd.Init(0, 0, 0, 0, 2, 0, 2, 2, mailboxes); | |
| EXPECT_EQ(error::kNoError, ExecuteImmediateCmd(cmd, sizeof(mailboxes))); | |
| EXPECT_EQ(GL_INVALID_VALUE, GetGLError()); | |
| EXPECT_EQ(representation->ClearedRect(), gfx::Rect()); | |
| EXPECT_FALSE(representation->IsCleared()); | |
| // Empty destination width with non-empty source rect. | |
| cmd.Init(0, 0, 0, 0, 2, 2, 0, 2, mailboxes); | |
| EXPECT_EQ(error::kNoError, ExecuteImmediateCmd(cmd, sizeof(mailboxes))); | |
| EXPECT_EQ(GL_INVALID_VALUE, GetGLError()); | |
| EXPECT_EQ(representation->ClearedRect(), gfx::Rect()); | |
| EXPECT_FALSE(representation->IsCleared()); | |
| // Empty destination height with non-empty source rect. | |
| cmd.Init(0, 0, 0, 0, 2, 2, 2, 0, mailboxes); | |
| EXPECT_EQ(error::kNoError, ExecuteImmediateCmd(cmd, sizeof(mailboxes))); | |
| EXPECT_EQ(GL_INVALID_VALUE, GetGLError()); | |
| EXPECT_EQ(representation->ClearedRect(), gfx::Rect()); | |
| EXPECT_FALSE(representation->IsCleared()); | |
| // Both source and destination rects empty. | |
| cmd.Init(1, 1, 0, 0, 0, 0, 0, 0, mailboxes); | |
| EXPECT_EQ(error::kNoError, ExecuteImmediateCmd(cmd, sizeof(mailboxes))); | |
| EXPECT_EQ(GL_INVALID_VALUE, GetGLError()); | |
| EXPECT_EQ(representation->ClearedRect(), gfx::Rect()); | |
| EXPECT_FALSE(representation->IsCleared()); | |
| } | |
| TEST_F(RasterDecoderOOPTest, CopyTexSubImage2DTwiceClearsUnclearedTexture) { | |
| context_state_->set_need_context_state_reset(true); | |
| // Create uninitialized source texture mailbox. | |
| gpu::Mailbox source_texture_mailbox = | |
| CreateMailbox(viz::SinglePlaneFormat::kRGBA_8888, | |
| /*width=*/2, /*height=*/2, | |
| /*cleared=*/true); | |
| GLbyte mailboxes[sizeof(gpu::Mailbox) * 2]; | |
| CopyMailboxes(mailboxes, source_texture_mailbox, client_texture_mailbox_); | |
| SharedImageRepresentationFactory repr_factory(shared_image_manager(), | |
| nullptr); | |
| auto representation = | |
| repr_factory.ProduceSkia(client_texture_mailbox_, context_state_.get()); | |
| EXPECT_FALSE(representation->IsCleared()); | |
| // This will initialize the top half of destination. | |
| { | |
| auto& cmd = *GetImmediateAs<cmds::CopySharedImageINTERNALImmediate>(); | |
| cmd.Init(0, 0, 0, 0, 2, 1, 2, 1, mailboxes); | |
| EXPECT_EQ(error::kNoError, ExecuteImmediateCmd(cmd, sizeof(mailboxes))); | |
| } | |
| EXPECT_EQ(gfx::Rect(0, 0, 2, 1), representation->ClearedRect()); | |
| EXPECT_FALSE(representation->IsCleared()); | |
| // This will initialize bottom half of the destination. | |
| { | |
| auto& cmd = *GetImmediateAs<cmds::CopySharedImageINTERNALImmediate>(); | |
| cmd.Init(0, 1, 0, 0, 2, 1, 2, 1, mailboxes); | |
| EXPECT_EQ(error::kNoError, ExecuteImmediateCmd(cmd, sizeof(mailboxes))); | |
| } | |
| EXPECT_TRUE(representation->IsCleared()); | |
| } | |
| // Unlike the GLES2 version, RasterInterface's CopySharedImage does not allow | |
| // initializing a texture in parts *unless* the rectangles being cleared | |
| // can be trivially combined into a larger rectangle. | |
| TEST_F(RasterDecoderOOPTest, CopyTexSubImage2DPartialFailsWithUnalignedRect) { | |
| context_state_->set_need_context_state_reset(true); | |
| // Create uninitialized source texture mailbox. | |
| gpu::Mailbox source_texture_mailbox = | |
| CreateMailbox(viz::SinglePlaneFormat::kRGBA_8888, | |
| /*width=*/2, /*height=*/2, | |
| /*cleared=*/true); | |
| GLbyte mailboxes[sizeof(gpu::Mailbox) * 2]; | |
| CopyMailboxes(mailboxes, source_texture_mailbox, client_texture_mailbox_); | |
| SharedImageRepresentationFactory repr_factory(shared_image_manager(), | |
| nullptr); | |
| auto representation = | |
| repr_factory.ProduceSkia(client_texture_mailbox_, context_state_.get()); | |
| EXPECT_FALSE(representation->IsCleared()); | |
| // This will initialize the top half of destination. | |
| { | |
| auto& cmd = *GetImmediateAs<cmds::CopySharedImageINTERNALImmediate>(); | |
| cmd.Init(0, 0, 0, 0, 2, 1, 2, 1, mailboxes); | |
| EXPECT_EQ(error::kNoError, ExecuteImmediateCmd(cmd, sizeof(mailboxes))); | |
| } | |
| EXPECT_EQ(gfx::Rect(0, 0, 2, 1), representation->ClearedRect()); | |
| EXPECT_FALSE(representation->IsCleared()); | |
| // This will attempt to initialize the bottom corner of the destination. As | |
| // the new rect cannot be trivially combined with the previous cleared rect, | |
| // this will fail. | |
| { | |
| auto& cmd = *GetImmediateAs<cmds::CopySharedImageINTERNALImmediate>(); | |
| cmd.Init(1, 1, 0, 0, 1, 1, 1, 1, mailboxes); | |
| EXPECT_EQ(error::kNoError, ExecuteImmediateCmd(cmd, sizeof(mailboxes))); | |
| EXPECT_EQ(GL_INVALID_VALUE, GetGLError()); | |
| } | |
| EXPECT_EQ(gfx::Rect(0, 0, 2, 1), representation->ClearedRect()); | |
| EXPECT_FALSE(representation->IsCleared()); | |
| } | |
| TEST_F(RasterDecoderOOPTest, StateRestoreAcrossDecoders) { | |
| // First decoder receives a skia command requiring context state reset. | |
| auto decoder1 = CreateDecoder(); | |
| EXPECT_FALSE(context_state_->need_context_state_reset()); | |
| decoder1->MakeCurrent(); | |
| decoder1->SetUpForRasterCHROMIUMForTest(); | |
| cmds::EndRasterCHROMIUM end_raster_cmd; | |
| end_raster_cmd.Init(); | |
| EXPECT_FALSE(error::IsError(ExecuteCmd(decoder1.get(), end_raster_cmd))); | |
| EXPECT_TRUE(context_state_->need_context_state_reset()); | |
| // Another decoder receives a command which does not require consistent state, | |
| // it should be processed without state restoration. | |
| auto decoder2 = CreateDecoder(); | |
| decoder2->MakeCurrent(); | |
| decoder2->SetUpForRasterCHROMIUMForTest(); | |
| EXPECT_FALSE(error::IsError(ExecuteCmd(decoder2.get(), end_raster_cmd))); | |
| EXPECT_TRUE(context_state_->need_context_state_reset()); | |
| decoder1->Destroy(true); | |
| context_state_->MakeCurrent(nullptr); | |
| decoder2->Destroy(true); | |
| // Make sure the context is preserved across decoders. | |
| EXPECT_FALSE(context_state_->gr_context()->abandoned()); | |
| } | |
| TEST_F(RasterDecoderOOPTest, SharedImageProviderRejectsActiveOutputMailbox) { | |
| context_state_->set_need_context_state_reset(true); | |
| gpu::Mailbox mailbox = | |
| CreateMailbox(viz::SinglePlaneFormat::kRGBA_8888, | |
| /*width=*/2, /*height=*/2, | |
| /*cleared=*/true); | |
| auto& cmd = *GetImmediateAs<cmds::BeginRasterCHROMIUMImmediate>(); | |
| cmd.Init(0.0f, 0.0f, 0.0f, 0.0f, false, 0, kNoMSAA, false, 1.0f, | |
| mailbox.name); | |
| EXPECT_EQ(error::kNoError, ExecuteImmediateCmd(cmd, sizeof(mailbox.name))); | |
| cc::SharedImageProvider* provider = decoder_->GetSharedImageProviderForTest(); | |
| ASSERT_TRUE(provider); | |
| cc::SharedImageProvider::Error error; | |
| sk_sp<SkImage> image = provider->OpenSharedImageForRead(mailbox, error); | |
| EXPECT_FALSE(image); | |
| EXPECT_EQ(error, cc::SharedImageProvider::Error::kNoAccess); | |
| } | |
| namespace { | |
| // Source backing which raises a GL error when accessed. ProduceMemory() | |
| // returns a working CPU representation, while ProduceSkiaGanesh() fails. | |
| class GLErrorOnAccessTestImageBacking : public TestImageBacking { | |
| public: | |
| GLErrorOnAccessTestImageBacking(const Mailbox& mailbox, | |
| const SharedImageInfo& si_info, | |
| bool raise_gl_error) | |
| : TestImageBacking(mailbox, si_info, /*estimated_size=*/0), | |
| raise_gl_error_(raise_gl_error) { | |
| bitmap_.allocN32Pixels(si_info.size.width(), si_info.size.height()); | |
| bitmap_.eraseColor(SK_ColorRED); | |
| SetCleared(); | |
| } | |
| void MaybeRaiseGLError() { | |
| if (raise_gl_error_) { | |
| glEnable(GL_NONE); | |
| } | |
| } | |
| SkPixmap pixmap() const { return bitmap_.pixmap(); } | |
| protected: | |
| std::unique_ptr<MemoryImageRepresentation> ProduceMemory( | |
| SharedImageManager* manager, | |
| MemoryTypeTracker* tracker) override; | |
| std::unique_ptr<SkiaGaneshImageRepresentation> ProduceSkiaGanesh( | |
| SharedImageManager* manager, | |
| MemoryTypeTracker* tracker, | |
| scoped_refptr<SharedContextState> context_state) override { | |
| MaybeRaiseGLError(); | |
| return nullptr; | |
| } | |
| private: | |
| const bool raise_gl_error_; | |
| SkBitmap bitmap_; | |
| }; | |
| class GLErrorOnAccessMemoryImageRepresentation | |
| : public MemoryImageRepresentation { | |
| public: | |
| GLErrorOnAccessMemoryImageRepresentation( | |
| SharedImageManager* manager, | |
| GLErrorOnAccessTestImageBacking* backing, | |
| MemoryTypeTracker* tracker) | |
| : MemoryImageRepresentation(manager, backing, tracker) {} | |
| protected: | |
| SkPixmap BeginReadAccess() override { | |
| auto* backing = | |
| static_cast<GLErrorOnAccessTestImageBacking*>(this->backing()); | |
| backing->MaybeRaiseGLError(); | |
| return backing->pixmap(); | |
| } | |
| }; | |
| std::unique_ptr<MemoryImageRepresentation> | |
| GLErrorOnAccessTestImageBacking::ProduceMemory(SharedImageManager* manager, | |
| MemoryTypeTracker* tracker) { | |
| return std::make_unique<GLErrorOnAccessMemoryImageRepresentation>( | |
| manager, this, tracker); | |
| } | |
| } // namespace | |
| class RasterDecoderOOPCopyGLErrorTest | |
| : public RasterDecoderOOPTest, | |
| public testing::WithParamInterface<bool> { | |
| public: | |
| bool raise_gl_error() const { return GetParam(); } | |
| std::unique_ptr<SharedImageRepresentationFactoryRef> CreateSourceImage( | |
| const Mailbox& mailbox) { | |
| SharedImageInfo si_info(viz::SinglePlaneFormat::kRGBA_8888, gfx::Size(2, 2), | |
| gfx::ColorSpace::CreateSRGB(), | |
| kTopLeft_GrSurfaceOrigin, kPremul_SkAlphaType, | |
| SHARED_IMAGE_USAGE_RASTER_READ, "TestLabel"); | |
| return shared_image_manager()->Register( | |
| std::make_unique<GLErrorOnAccessTestImageBacking>(mailbox, si_info, | |
| raise_gl_error()), | |
| &memory_tracker_); | |
| } | |
| }; | |
| INSTANTIATE_TEST_SUITE_P(All, RasterDecoderOOPCopyGLErrorTest, testing::Bool()); | |
| // A GL error raised while copying from a CPU-backed source must leave the | |
| // destination uncleared. | |
| TEST_P(RasterDecoderOOPCopyGLErrorTest, CopyFromMemory) { | |
| if (!context_state_->GrContextIsGL()) { | |
| GTEST_SKIP() << "GL error checks only apply to Ganesh-GL."; | |
| } | |
| Mailbox source_mailbox = Mailbox::Generate(); | |
| auto source_ref = CreateSourceImage(source_mailbox); | |
| GLbyte mailboxes[sizeof(gpu::Mailbox) * 2]; | |
| CopyMailboxes(mailboxes, source_mailbox, client_texture_mailbox_); | |
| SharedImageRepresentationFactory repr_factory(shared_image_manager(), | |
| nullptr); | |
| auto representation = | |
| repr_factory.ProduceSkia(client_texture_mailbox_, context_state_.get()); | |
| EXPECT_FALSE(representation->IsCleared()); | |
| auto& cmd = *GetImmediateAs<cmds::CopySharedImageINTERNALImmediate>(); | |
| cmd.Init(0, 0, 0, 0, 2, 2, 2, 2, mailboxes); | |
| EXPECT_EQ(error::kNoError, ExecuteImmediateCmd(cmd, sizeof(mailboxes))); | |
| EXPECT_EQ(raise_gl_error() ? GL_INVALID_ENUM : GL_NO_ERROR, GetGLError()); | |
| EXPECT_EQ(!raise_gl_error(), representation->IsCleared()); | |
| } | |
| // A GL error raised on the path that clears the destination when the source | |
| // can't be read must leave the destination uncleared. | |
| TEST_P(RasterDecoderOOPCopyGLErrorTest, ClearWhenSourceUnavailable) { | |
| if (!context_state_->GrContextIsGL()) { | |
| GTEST_SKIP() << "GL error checks only apply to Ganesh-GL."; | |
| } | |
| Mailbox source_mailbox = Mailbox::Generate(); | |
| auto source_ref = CreateSourceImage(source_mailbox); | |
| GLbyte mailboxes[sizeof(gpu::Mailbox) * 2]; | |
| CopyMailboxes(mailboxes, source_mailbox, client_texture_mailbox_); | |
| SharedImageRepresentationFactory repr_factory(shared_image_manager(), | |
| nullptr); | |
| auto representation = | |
| repr_factory.ProduceSkia(client_texture_mailbox_, context_state_.get()); | |
| EXPECT_FALSE(representation->IsCleared()); | |
| // Scaling skips the CPU upload path. | |
| auto& cmd = *GetImmediateAs<cmds::CopySharedImageINTERNALImmediate>(); | |
| cmd.Init(0, 0, 0, 0, 1, 1, 2, 2, mailboxes); | |
| EXPECT_EQ(error::kNoError, ExecuteImmediateCmd(cmd, sizeof(mailboxes))); | |
| EXPECT_EQ(GL_INVALID_VALUE, GetGLError()); | |
| EXPECT_EQ(!raise_gl_error(), representation->IsCleared()); | |
| } | |
| TEST_F(RasterDecoderOOPTest, MakeCurrentAndDestroyWithGrContextTypeNone) { | |
| gpu::GpuDriverBugWorkarounds workarounds; | |
| scoped_refptr<gl::GLShareGroup> share_group = new gl::GLShareGroup(); | |
| scoped_refptr<gl::GLSurface> surface = | |
| gl::init::CreateOffscreenGLSurface(display_, gfx::Size()); | |
| scoped_refptr<gl::GLContext> context = gl::init::CreateGLContext( | |
| share_group.get(), surface.get(), gl::GLContextAttribs()); | |
| ASSERT_TRUE(context->MakeCurrent(surface.get())); | |
| auto none_context_state = base::MakeRefCounted<SharedContextState>( | |
| std::move(share_group), std::move(surface), std::move(context), | |
| /*use_virtualized_gl_contexts=*/false, base::DoNothing(), | |
| GrContextType::kNone); | |
| ASSERT_TRUE( | |
| none_context_state->InitializeSkia(GpuPreferences(), workarounds)); | |
| ASSERT_TRUE(none_context_state->InitializeGL(GpuPreferences(), workarounds, | |
| gpu_feature_info_)); | |
| FakeCommandBufferServiceBase command_buffer_service; | |
| std::unique_ptr<RasterDecoder> decoder = RasterDecoder::Create( | |
| this, &command_buffer_service, &outputter_, gpu_feature_info_, | |
| GpuPreferences(), /*memory_tracker=*/nullptr, &shared_image_manager_, | |
| none_context_state, /*is_privileged=*/true); | |
| ASSERT_EQ(decoder->Initialize(/*lose_context_when_out_of_memory=*/false), | |
| ContextResult::kSuccess); | |
| // Simulate another GL context becoming current on the thread before | |
| // RasterDecoder is destroyed. | |
| ASSERT_TRUE(context_state_->MakeCurrent(nullptr)); | |
| EXPECT_FALSE(none_context_state->IsCurrent(nullptr)); | |
| EXPECT_TRUE(decoder->MakeCurrent()); | |
| EXPECT_TRUE(none_context_state->IsCurrent(nullptr)); | |
| decoder->Destroy(/*have_context=*/true); | |
| } | |
| } // namespace raster | |
| } // namespace gpu |