[go: nahoru, domu]

blob: 2d30e88b40e5fdaea4816ddc7befb819e63b4dfa [file] [log] [blame]
// Copyright 2022 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/shared_image/compound_image_backing.h"
#include "components/viz/common/resources/shared_image_format.h"
#include "gpu/command_buffer/common/shared_image_usage.h"
#include "gpu/command_buffer/service/memory_tracking.h"
#include "gpu/command_buffer/service/shared_context_state.h"
#include "gpu/command_buffer/service/shared_image/shared_image_backing.h"
#include "gpu/command_buffer/service/shared_image/shared_image_backing_factory.h"
#include "gpu/command_buffer/service/shared_image/shared_image_manager.h"
#include "gpu/command_buffer/service/shared_image/shared_memory_image_backing.h"
#include "gpu/command_buffer/service/shared_image/test_image_backing.h"
#include "gpu/command_buffer/service/test_memory_tracker.h"
#include "gpu/ipc/common/gpu_memory_buffer_impl_shared_memory.h"
#include "gpu/ipc/common/surface_handle.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/skia/include/core/SkAlphaType.h"
#include "third_party/skia/include/gpu/GrBackendSemaphore.h"
#include "third_party/skia/include/gpu/GrTypes.h"
#include "ui/gfx/buffer_types.h"
namespace gpu {
namespace {
constexpr uint32_t kTestBackingSize = 40000;
class TestSharedImageBackingFactory : public SharedImageBackingFactory {
public:
TestSharedImageBackingFactory() : SharedImageBackingFactory(kUsageAll) {}
// SharedImageBackingFactory implementation.
std::unique_ptr<SharedImageBacking> CreateSharedImage(
const Mailbox& mailbox,
viz::SharedImageFormat format,
SurfaceHandle surface_handle,
const gfx::Size& size,
const gfx::ColorSpace& color_space,
GrSurfaceOrigin surface_origin,
SkAlphaType alpha_type,
uint32_t usage,
std::string debug_label,
bool is_thread_safe) override {
if (allocations_should_fail_)
return nullptr;
return std::make_unique<TestImageBacking>(
mailbox, format, size, color_space, surface_origin, alpha_type, usage,
kTestBackingSize);
}
std::unique_ptr<SharedImageBacking> CreateSharedImage(
const Mailbox& mailbox,
viz::SharedImageFormat format,
const gfx::Size& size,
const gfx::ColorSpace& color_space,
GrSurfaceOrigin surface_origin,
SkAlphaType alpha_type,
uint32_t usage,
std::string debug_label,
base::span<const uint8_t> pixel_data) override {
return nullptr;
}
std::unique_ptr<SharedImageBacking> CreateSharedImage(
const Mailbox& mailbox,
viz::SharedImageFormat format,
const gfx::Size& size,
const gfx::ColorSpace& color_space,
GrSurfaceOrigin surface_origin,
SkAlphaType alpha_type,
uint32_t usage,
std::string debug_label,
gfx::GpuMemoryBufferHandle handle) override {
return nullptr;
}
std::unique_ptr<SharedImageBacking> CreateSharedImage(
const Mailbox& mailbox,
gfx::GpuMemoryBufferHandle handle,
gfx::BufferFormat format,
gfx::BufferPlane plane,
const gfx::Size& size,
const gfx::ColorSpace& color_space,
GrSurfaceOrigin surface_origin,
SkAlphaType alpha_type,
uint32_t usage,
std::string debug_label) override {
return nullptr;
}
bool IsSupported(uint32_t usage,
viz::SharedImageFormat format,
const gfx::Size& size,
bool thread_safe,
gfx::GpuMemoryBufferType gmb_type,
GrContextType gr_context_type,
base::span<const uint8_t> pixel_data) override {
return true;
}
using SharedImageBackingFactory::InvalidateWeakPtrsForTesting;
void SetAllocationsShouldFail(bool allocations_should_fail) {
allocations_should_fail_ = allocations_should_fail;
}
private:
bool allocations_should_fail_ = false;
};
} // namespace
class CompoundImageBackingTest : public testing::Test {
public:
bool HasGpuBacking(CompoundImageBacking* backing) {
return !!backing->elements_[1].backing;
}
bool HasGpuCreateBackingCallback(CompoundImageBacking* backing) {
return !backing->elements_[1].create_callback.is_null();
}
TestImageBacking* GetGpuBacking(CompoundImageBacking* backing) {
auto* gpu_backing = backing->elements_[1].backing.get();
DCHECK_EQ(gpu_backing->GetType(), SharedImageBackingType::kTest);
return static_cast<TestImageBacking*>(gpu_backing);
}
SharedMemoryImageBacking* GetShmImageBacking(CompoundImageBacking* backing) {
auto* shm_backing = backing->elements_[0].backing.get();
DCHECK_EQ(shm_backing->GetType(), SharedImageBackingType::kSharedMemory);
return static_cast<SharedMemoryImageBacking*>(shm_backing);
}
bool GetShmHasLatestContent(CompoundImageBacking* backing) {
return backing->elements_[0].content_id_ == backing->latest_content_id_;
}
bool GetGpuHasLatestContent(CompoundImageBacking* backing) {
return backing->elements_[1].content_id_ == backing->latest_content_id_;
}
// Create a compound backing containing shared memory + GPU backing.
std::unique_ptr<SharedImageBacking> CreateCompoundBacking(
bool allow_shm_overlays = false) {
constexpr gfx::Size size(100, 100);
constexpr gfx::BufferFormat buffer_format = gfx::BufferFormat::RGBA_8888;
constexpr gfx::BufferUsage buffer_usage =
gfx::BufferUsage::SCANOUT_CPU_READ_WRITE;
gfx::GpuMemoryBufferHandle handle =
GpuMemoryBufferImplSharedMemory::CreateGpuMemoryBuffer(
static_cast<gfx::GpuMemoryBufferId>(1), size, buffer_format,
buffer_usage);
return CompoundImageBacking::CreateSharedMemory(
&test_factory_, allow_shm_overlays, Mailbox::GenerateForSharedImage(),
std::move(handle), buffer_format, gfx::BufferPlane::DEFAULT, size,
gfx::ColorSpace(), kTopLeft_GrSurfaceOrigin, kOpaque_SkAlphaType,
SHARED_IMAGE_USAGE_DISPLAY_READ, "TestLabel");
}
std::unique_ptr<SharedImageBacking> CreateMultiplanarCompoundBacking(
bool allow_shm_overlays = false) {
constexpr gfx::Size size(100, 100);
constexpr gfx::BufferFormat buffer_format =
gfx::BufferFormat::YUV_420_BIPLANAR;
constexpr gfx::BufferUsage buffer_usage =
gfx::BufferUsage::SCANOUT_CPU_READ_WRITE;
gfx::GpuMemoryBufferHandle handle =
GpuMemoryBufferImplSharedMemory::CreateGpuMemoryBuffer(
static_cast<gfx::GpuMemoryBufferId>(1), size, buffer_format,
buffer_usage);
return CompoundImageBacking::CreateSharedMemory(
&test_factory_, allow_shm_overlays, Mailbox::GenerateForSharedImage(),
std::move(handle), viz::MultiPlaneFormat::kNV12, size,
gfx::ColorSpace(), kTopLeft_GrSurfaceOrigin, kOpaque_SkAlphaType,
SHARED_IMAGE_USAGE_DISPLAY_READ, "TestLabel");
}
protected:
SharedImageManager manager_;
TestSharedImageBackingFactory test_factory_;
TestMemoryTracker memory_tracker_;
MemoryTypeTracker tracker_{&memory_tracker_};
};
TEST_F(CompoundImageBackingTest, References) {
auto backing = CreateCompoundBacking();
auto* compound_backing = static_cast<CompoundImageBacking*>(backing.get());
EXPECT_NE(compound_backing, nullptr);
// The compound backing hasn't been added to the manager yet so it should
// have zero references.
EXPECT_FALSE(compound_backing->HasAnyRefs());
EXPECT_FALSE(HasGpuBacking(compound_backing));
auto factory_rep = manager_.Register(std::move(backing), &tracker_);
// When the compound backing is first registered it will get a reference
// and add shared memory backing size to the memory tracker.
EXPECT_EQ(memory_tracker_.GetSize(), kTestBackingSize);
// After register compound backing it should have a reference. The GPU
// backing should never have any reference as it's owned by the compound
// backing and isn't reference counted.
EXPECT_TRUE(compound_backing->HasAnyRefs());
EXPECT_FALSE(HasGpuBacking(compound_backing));
auto overlay_rep =
manager_.ProduceOverlay(compound_backing->mailbox(), &tracker_);
// On overlay access a GPU backing will be allocated and the recorded size
// will increase.
EXPECT_EQ(memory_tracker_.GetSize(), kTestBackingSize * 2);
auto* gpu_backing = GetGpuBacking(compound_backing);
// GPU backing still shouldn't have any references after a wrapped
// representation is created.
EXPECT_TRUE(compound_backing->HasAnyRefs());
EXPECT_FALSE(gpu_backing->HasAnyRefs());
overlay_rep.reset();
EXPECT_TRUE(compound_backing->HasAnyRefs());
// All the backings will be destroyed after this point.
factory_rep.reset();
// When all references are dropped the total size of shared memory and gpu
// backings will be subtracted from memory tracker.
EXPECT_EQ(memory_tracker_.GetSize(), 0u);
}
TEST_F(CompoundImageBackingTest, UploadOnAccess) {
auto backing = CreateCompoundBacking();
auto* compound_backing = static_cast<CompoundImageBacking*>(backing.get());
auto factory_rep = manager_.Register(std::move(backing), &tracker_);
auto overlay_rep =
manager_.ProduceOverlay(compound_backing->mailbox(), &tracker_);
auto* gpu_backing = GetGpuBacking(compound_backing);
// The compound backing hasn't been accessed yet.
EXPECT_FALSE(gpu_backing->GetUploadFromMemoryCalledAndReset());
// Only shared memory should have latest content initially.
EXPECT_TRUE(GetShmHasLatestContent(compound_backing));
EXPECT_FALSE(GetGpuHasLatestContent(compound_backing));
// First access should trigger upload from memory to GPU.
overlay_rep->BeginScopedReadAccess();
EXPECT_TRUE(gpu_backing->GetUploadFromMemoryCalledAndReset());
// After GPU read access both should have latest content.
EXPECT_TRUE(GetShmHasLatestContent(compound_backing));
EXPECT_TRUE(GetGpuHasLatestContent(compound_backing));
// Second access shouldn't trigger upload since no shared memory updates
// happened.
overlay_rep->BeginScopedReadAccess();
EXPECT_FALSE(gpu_backing->GetUploadFromMemoryCalledAndReset());
// Notify compound backing of shared memory update. Next access should
// trigger a new upload.
compound_backing->Update(nullptr);
overlay_rep->BeginScopedReadAccess();
EXPECT_TRUE(gpu_backing->GetUploadFromMemoryCalledAndReset());
// Test that GLTexturePassthrough access causes upload.
auto gl_passthrough_rep = manager_.ProduceGLTexturePassthrough(
compound_backing->mailbox(), &tracker_);
compound_backing->Update(nullptr);
gl_passthrough_rep->BeginScopedAccess(
0, SharedImageRepresentation::AllowUnclearedAccess::kNo);
EXPECT_TRUE(gpu_backing->GetUploadFromMemoryCalledAndReset());
// Test that GLTexture access causes upload.
auto gl_rep =
manager_.ProduceGLTexture(compound_backing->mailbox(), &tracker_);
compound_backing->Update(nullptr);
gl_rep->BeginScopedAccess(
0, SharedImageRepresentation::AllowUnclearedAccess::kNo);
EXPECT_TRUE(gpu_backing->GetUploadFromMemoryCalledAndReset());
// Test that both read and write access by Skia triggers upload.
std::vector<GrBackendSemaphore> begin_semaphores;
std::vector<GrBackendSemaphore> end_semaphores;
auto skia_rep =
manager_.ProduceSkia(compound_backing->mailbox(), &tracker_, nullptr);
compound_backing->Update(nullptr);
// After Update() only shared memory should have latest content.
EXPECT_TRUE(GetShmHasLatestContent(compound_backing));
EXPECT_FALSE(GetGpuHasLatestContent(compound_backing));
skia_rep->BeginScopedWriteAccess(
&begin_semaphores, &end_semaphores,
SharedImageRepresentation::AllowUnclearedAccess::kNo);
EXPECT_TRUE(gpu_backing->GetUploadFromMemoryCalledAndReset());
// On GPU write access only GPU should have latest content.
EXPECT_FALSE(GetShmHasLatestContent(compound_backing));
EXPECT_TRUE(GetGpuHasLatestContent(compound_backing));
compound_backing->Update(nullptr);
skia_rep->BeginScopedReadAccess(&begin_semaphores, &end_semaphores);
EXPECT_TRUE(gpu_backing->GetUploadFromMemoryCalledAndReset());
}
TEST_F(CompoundImageBackingTest, ReadbackToMemory) {
auto backing = CreateCompoundBacking();
auto* compound_backing = static_cast<CompoundImageBacking*>(backing.get());
auto factory_rep = manager_.Register(std::move(backing), &tracker_);
auto gl_passthrough_rep = manager_.ProduceGLTexturePassthrough(
compound_backing->mailbox(), &tracker_);
compound_backing->Update(nullptr);
gl_passthrough_rep->BeginScopedAccess(
0, SharedImageRepresentation::AllowUnclearedAccess::kNo);
auto* gpu_backing = GetGpuBacking(compound_backing);
// On write access UploadFromMemory() should be called and then only GPU
// texture has latest.
EXPECT_TRUE(gpu_backing->GetUploadFromMemoryCalledAndReset());
EXPECT_FALSE(GetShmHasLatestContent(compound_backing));
EXPECT_TRUE(GetGpuHasLatestContent(compound_backing));
compound_backing->CopyToGpuMemoryBuffer();
// On CopyToGpuMemoryBuffer() ReadbackToMemory() should have been called and
// shared memory has latest content again.
EXPECT_TRUE(gpu_backing->GetReadbackToMemoryCalledAndReset());
EXPECT_TRUE(GetShmHasLatestContent(compound_backing));
EXPECT_TRUE(GetGpuHasLatestContent(compound_backing));
}
TEST_F(CompoundImageBackingTest, NoUploadOnOverlayMemoryAccess) {
auto backing = CreateCompoundBacking(/*allow_shm_overlays=*/true);
auto* compound_backing = static_cast<CompoundImageBacking*>(backing.get());
auto factory_rep = manager_.Register(std::move(backing), &tracker_);
auto overlay_rep =
manager_.ProduceOverlay(compound_backing->mailbox(), &tracker_);
auto access = overlay_rep->BeginScopedReadAccess();
#if BUILDFLAG(IS_WIN)
absl::optional<gl::DCLayerOverlayImage> overlay_image =
access->GetDCLayerOverlayImage();
ASSERT_TRUE(overlay_image);
EXPECT_EQ(overlay_image->type(), gl::DCLayerOverlayType::kNV12Pixmap);
#endif
EXPECT_FALSE(HasGpuBacking(compound_backing));
}
TEST_F(CompoundImageBackingTest, LazyAllocationFailsCreate) {
auto backing = CreateCompoundBacking();
auto* compound_backing = static_cast<CompoundImageBacking*>(backing.get());
auto factory_rep = manager_.Register(std::move(backing), &tracker_);
// The compound backing shouldn't have GPU backing yet and should have
// a valid factory to create one.
EXPECT_FALSE(HasGpuBacking(compound_backing));
EXPECT_TRUE(HasGpuCreateBackingCallback(compound_backing));
test_factory_.SetAllocationsShouldFail(true);
auto gl_passthrough_rep = manager_.ProduceGLTexturePassthrough(
compound_backing->mailbox(), &tracker_);
// Creating the GPU backing should fail so representation should be null.
EXPECT_FALSE(gl_passthrough_rep);
EXPECT_FALSE(HasGpuBacking(compound_backing));
// The backing factory to create GPU backing should be reset so creation is
// not retried.
EXPECT_FALSE(HasGpuCreateBackingCallback(compound_backing));
}
TEST_F(CompoundImageBackingTest, LazyAllocationFailsFactoryInvalidated) {
auto backing = CreateCompoundBacking();
auto* compound_backing = static_cast<CompoundImageBacking*>(backing.get());
auto factory_rep = manager_.Register(std::move(backing), &tracker_);
// The compound backing shouldn't have GPU backing yet and should have
// a valid factory to create one.
EXPECT_FALSE(HasGpuBacking(compound_backing));
EXPECT_TRUE(HasGpuCreateBackingCallback(compound_backing));
test_factory_.InvalidateWeakPtrsForTesting();
auto gl_passthrough_rep = manager_.ProduceGLTexturePassthrough(
compound_backing->mailbox(), &tracker_);
// Creating the GPU backing should fail so representation should be null.
EXPECT_FALSE(gl_passthrough_rep);
EXPECT_FALSE(HasGpuBacking(compound_backing));
// The backing factory to create GPU backing should be reset to avoid logging
// about destroyed image multiple times.
EXPECT_FALSE(HasGpuCreateBackingCallback(compound_backing));
}
TEST_F(CompoundImageBackingTest, Multiplanar) {
auto backing = CreateMultiplanarCompoundBacking();
auto* compound_backing = static_cast<CompoundImageBacking*>(backing.get());
auto factory_rep = manager_.Register(std::move(backing), &tracker_);
// There should be two planes stored in shared memory backing.
auto* shm_backing = GetShmImageBacking(compound_backing);
EXPECT_EQ(shm_backing->pixmaps().size(), 2u);
auto overlay_rep =
manager_.ProduceOverlay(compound_backing->mailbox(), &tracker_);
auto* gpu_backing = GetGpuBacking(compound_backing);
// The compound backing hasn't been accessed yet.
EXPECT_FALSE(gpu_backing->GetUploadFromMemoryCalledAndReset());
// First access should trigger upload from memory to GPU.
overlay_rep->BeginScopedReadAccess();
EXPECT_TRUE(gpu_backing->GetUploadFromMemoryCalledAndReset());
}
} // namespace gpu