| // Copyright 2021 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "components/viz/service/display/display_resource_provider_software.h" |
| |
| #include <stddef.h> |
| #include <stdint.h> |
| |
| #include <memory> |
| #include <set> |
| #include <unordered_map> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/check.h" |
| #include "base/functional/bind.h" |
| #include "base/functional/callback_helpers.h" |
| #include "base/memory/read_only_shared_memory_region.h" |
| #include "base/memory/ref_counted.h" |
| #include "base/memory/shared_memory_mapping.h" |
| #include "build/build_config.h" |
| #include "components/viz/client/client_resource_provider.h" |
| #include "components/viz/common/resources/release_callback.h" |
| #include "components/viz/common/resources/returned_resource.h" |
| #include "components/viz/service/gl/gpu_service_impl.h" |
| #include "components/viz/test/test_in_process_context_provider.h" |
| #include "gpu/command_buffer/client/client_shared_image.h" |
| #include "gpu/command_buffer/client/shared_image_interface.h" |
| #include "gpu/command_buffer/service/scheduler.h" |
| #include "gpu/command_buffer/service/shared_image/shared_image_manager.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "ui/gfx/geometry/rect.h" |
| |
| using testing::_; |
| using testing::ByMove; |
| using testing::DoAll; |
| using testing::Return; |
| using testing::SaveArg; |
| |
| namespace viz { |
| namespace { |
| |
| class MockReleaseCallback { |
| public: |
| MOCK_METHOD3(Released, |
| void(scoped_refptr<gpu::ClientSharedImage> shared_image, |
| const gpu::SyncToken& token, |
| bool lost)); |
| }; |
| |
| static void CollectResources(std::vector<ReturnedResource>* array, |
| std::vector<ReturnedResource> returned) { |
| array->insert(array->end(), std::make_move_iterator(returned.begin()), |
| std::make_move_iterator(returned.end())); |
| } |
| |
| class DisplayResourceProviderSoftwareTest : public testing::Test { |
| public: |
| DisplayResourceProviderSoftwareTest() = default; |
| |
| ~DisplayResourceProviderSoftwareTest() override { |
| child_resource_provider_->ShutdownAndReleaseAllResources(); |
| } |
| |
| void InitializeProvider() { |
| auto context_provider = base::MakeRefCounted<TestInProcessContextProvider>( |
| TestContextType::kSoftwareRaster, /*support_locking=*/false); |
| gpu::ContextResult result = context_provider->BindToCurrentSequence(); |
| CHECK_EQ(result, gpu::ContextResult::kSuccess); |
| auto* gpu_service = context_provider->GpuService(); |
| child_context_provider_ = std::move(context_provider); |
| |
| resource_provider_ = std::make_unique<DisplayResourceProviderSoftware>( |
| gpu_service->shared_image_manager(), gpu_service->gpu_scheduler()); |
| |
| child_resource_provider_ = std::make_unique<ClientResourceProvider>(); |
| } |
| |
| ResourceId AllocateAndFillSoftwareResource( |
| MockReleaseCallback& release_callback, |
| const gfx::Size& size, |
| const uint32_t value) { |
| auto* shared_image_interface = |
| child_context_provider_->SharedImageInterface(); |
| auto shared_image = |
| shared_image_interface->CreateSharedImageForSoftwareCompositor( |
| {SinglePlaneFormat::kBGRA_8888, size, gfx::ColorSpace(), |
| gpu::SHARED_IMAGE_USAGE_CPU_WRITE_ONLY, |
| "DisplayResourceProviderSoftwareTest"}); |
| auto mapping = shared_image->Map(); |
| |
| uint32_t* ptr = |
| reinterpret_cast<uint32_t*>(mapping->GetMemoryForPlane(0).data()); |
| base::span<uint32_t> span = |
| UNSAFE_BUFFERS(base::span(ptr, static_cast<uint32_t>(size.GetArea()))); |
| |
| std::fill(span.begin(), span.end(), value); |
| |
| auto transferable_resource = TransferableResource::Make( |
| shared_image, TransferableResource::ResourceSource::kTileRasterTask, |
| shared_image_interface->GenVerifiedSyncToken()); |
| |
| auto callback = base::BindOnce(&MockReleaseCallback::Released, |
| base::Unretained(&release_callback), |
| std::move(shared_image)); |
| |
| return child_resource_provider_->ImportResource( |
| std::move(transferable_resource), std::move(callback)); |
| } |
| |
| protected: |
| scoped_refptr<RasterContextProvider> child_context_provider_; |
| std::unique_ptr<DisplayResourceProviderSoftware> resource_provider_; |
| std::unique_ptr<ClientResourceProvider> child_resource_provider_; |
| }; |
| |
| TEST_F(DisplayResourceProviderSoftwareTest, ReadSoftwareResources) { |
| InitializeProvider(); |
| |
| gfx::Size size(64, 64); |
| const uint32_t kBadBeef = 0xbadbeef; |
| |
| MockReleaseCallback release_callback; |
| ResourceId resource_id = |
| AllocateAndFillSoftwareResource(release_callback, size, kBadBeef); |
| |
| // Transfer resources to the parent. |
| std::vector<TransferableResource> send_to_parent; |
| std::vector<ReturnedResource> returned_to_child; |
| int child_id = resource_provider_->CreateChild( |
| base::BindRepeating(&CollectResources, &returned_to_child), SurfaceId()); |
| child_resource_provider_->PrepareSendToParent( |
| {resource_id}, &send_to_parent, |
| static_cast<RasterContextProvider*>(nullptr)); |
| resource_provider_->ReceiveFromChild(child_id, send_to_parent); |
| |
| // In DisplayResourceProvider's namespace, use the mapped resource id. |
| std::unordered_map<ResourceId, ResourceId, ResourceIdHasher> resource_map = |
| resource_provider_->GetChildToParentMap(child_id); |
| ResourceId mapped_resource_id = resource_map[resource_id]; |
| { |
| SkBitmap dstBitmap; |
| dstBitmap.allocPixels(SkImageInfo::Make(size.width(), size.height(), |
| kBGRA_8888_SkColorType, |
| kPremul_SkAlphaType)); |
| |
| DisplayResourceProviderSoftware::ScopedReadLockSkImage lock( |
| resource_provider_.get(), mapped_resource_id); |
| const SkImage* sk_image = lock.sk_image(); |
| bool result = sk_image->readPixels(nullptr, dstBitmap.pixmap(), |
| /*srcX=*/0, /*srcY=*/0); |
| |
| EXPECT_TRUE(result); |
| EXPECT_EQ(sk_image->width(), size.width()); |
| EXPECT_EQ(sk_image->height(), size.height()); |
| EXPECT_EQ(*dstBitmap.getAddr32(16, 16), kBadBeef); |
| } |
| |
| EXPECT_EQ(0u, returned_to_child.size()); |
| // Transfer resources back from the parent to the child. Set no resources as |
| // being in use. |
| resource_provider_->DeclareUsedResourcesFromChild(child_id, ResourceIdSet()); |
| EXPECT_EQ(1u, returned_to_child.size()); |
| child_resource_provider_->ReceiveReturnsFromParent( |
| std::move(returned_to_child)); |
| |
| EXPECT_CALL(release_callback, Released(_, _, false)); |
| child_resource_provider_->RemoveImportedResource(resource_id); |
| } |
| |
| } // namespace |
| } // namespace viz |