| // Copyright 2015 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. |
| |
| // This file defines tests that implementations of GpuMemoryBufferFactory should |
| // pass in order to be conformant. |
| |
| #ifndef GPU_IPC_CLIENT_GPU_MEMORY_BUFFER_IMPL_TEST_TEMPLATE_H_ |
| #define GPU_IPC_CLIENT_GPU_MEMORY_BUFFER_IMPL_TEST_TEMPLATE_H_ |
| |
| #include <stddef.h> |
| #include <string.h> |
| |
| #include <memory> |
| |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "ui/gfx/buffer_format_util.h" |
| |
| namespace gpu { |
| |
| template <typename GpuMemoryBufferImplType> |
| class GpuMemoryBufferImplTest : public testing::Test { |
| public: |
| GpuMemoryBufferImpl::DestructionCallback AllocateGpuMemoryBuffer( |
| const gfx::Size& size, |
| gfx::BufferFormat format, |
| gfx::BufferUsage usage, |
| gfx::GpuMemoryBufferHandle* handle, |
| bool* destroyed) { |
| return base::Bind(&GpuMemoryBufferImplTest::FreeGpuMemoryBuffer, |
| base::Unretained(this), |
| GpuMemoryBufferImplType::AllocateForTesting( |
| size, format, usage, handle), |
| base::Unretained(destroyed)); |
| } |
| |
| private: |
| void FreeGpuMemoryBuffer(const base::Closure& free_callback, |
| bool* destroyed, |
| const gpu::SyncToken& sync_token) { |
| free_callback.Run(); |
| if (destroyed) |
| *destroyed = true; |
| } |
| }; |
| |
| TYPED_TEST_CASE_P(GpuMemoryBufferImplTest); |
| |
| TYPED_TEST_P(GpuMemoryBufferImplTest, CreateFromHandle) { |
| const gfx::Size kBufferSize(8, 8); |
| |
| for (auto format : gfx::GetBufferFormatsForTesting()) { |
| gfx::BufferUsage usages[] = { |
| gfx::BufferUsage::GPU_READ, gfx::BufferUsage::SCANOUT, |
| gfx::BufferUsage::GPU_READ_CPU_READ_WRITE, |
| gfx::BufferUsage::GPU_READ_CPU_READ_WRITE_PERSISTENT}; |
| for (auto usage : usages) { |
| if (!TypeParam::IsConfigurationSupported(format, usage)) |
| continue; |
| |
| bool destroyed = false; |
| gfx::GpuMemoryBufferHandle handle; |
| GpuMemoryBufferImpl::DestructionCallback destroy_callback = |
| TestFixture::AllocateGpuMemoryBuffer(kBufferSize, format, usage, |
| &handle, &destroyed); |
| std::unique_ptr<TypeParam> buffer(TypeParam::CreateFromHandle( |
| handle, kBufferSize, format, usage, destroy_callback)); |
| ASSERT_TRUE(buffer); |
| EXPECT_EQ(buffer->GetFormat(), format); |
| |
| // Check if destruction callback is executed when deleting the buffer. |
| buffer.reset(); |
| ASSERT_TRUE(destroyed); |
| } |
| } |
| } |
| |
| TYPED_TEST_P(GpuMemoryBufferImplTest, Map) { |
| // Use a multiple of 4 for both dimensions to support compressed formats. |
| const gfx::Size kBufferSize(4, 4); |
| |
| for (auto format : gfx::GetBufferFormatsForTesting()) { |
| if (!TypeParam::IsConfigurationSupported( |
| format, gfx::BufferUsage::GPU_READ_CPU_READ_WRITE)) { |
| continue; |
| } |
| |
| gfx::GpuMemoryBufferHandle handle; |
| GpuMemoryBufferImpl::DestructionCallback destroy_callback = |
| TestFixture::AllocateGpuMemoryBuffer( |
| kBufferSize, format, gfx::BufferUsage::GPU_READ_CPU_READ_WRITE, |
| &handle, nullptr); |
| std::unique_ptr<TypeParam> buffer(TypeParam::CreateFromHandle( |
| handle, kBufferSize, format, gfx::BufferUsage::GPU_READ_CPU_READ_WRITE, |
| destroy_callback)); |
| ASSERT_TRUE(buffer); |
| |
| const size_t num_planes = gfx::NumberOfPlanesForBufferFormat(format); |
| |
| // Map buffer into user space. |
| ASSERT_TRUE(buffer->Map()); |
| |
| // Copy and compare mapped buffers. |
| for (size_t plane = 0; plane < num_planes; ++plane) { |
| const size_t row_size_in_bytes = |
| gfx::RowSizeForBufferFormat(kBufferSize.width(), format, plane); |
| EXPECT_GT(row_size_in_bytes, 0u); |
| |
| std::unique_ptr<char[]> data(new char[row_size_in_bytes]); |
| memset(data.get(), 0x2a + plane, row_size_in_bytes); |
| |
| size_t height = kBufferSize.height() / |
| gfx::SubsamplingFactorForBufferFormat(format, plane); |
| for (size_t y = 0; y < height; ++y) { |
| memcpy(static_cast<char*>(buffer->memory(plane)) + |
| y * buffer->stride(plane), |
| data.get(), row_size_in_bytes); |
| EXPECT_EQ(0, memcmp(static_cast<char*>(buffer->memory(plane)) + |
| y * buffer->stride(plane), |
| data.get(), row_size_in_bytes)); |
| } |
| } |
| |
| buffer->Unmap(); |
| } |
| } |
| |
| TYPED_TEST_P(GpuMemoryBufferImplTest, PersistentMap) { |
| // Use a multiple of 4 for both dimensions to support compressed formats. |
| const gfx::Size kBufferSize(4, 4); |
| |
| for (auto format : gfx::GetBufferFormatsForTesting()) { |
| if (!TypeParam::IsConfigurationSupported( |
| format, gfx::BufferUsage::GPU_READ_CPU_READ_WRITE_PERSISTENT)) { |
| continue; |
| } |
| |
| gfx::GpuMemoryBufferHandle handle; |
| GpuMemoryBufferImpl::DestructionCallback destroy_callback = |
| TestFixture::AllocateGpuMemoryBuffer( |
| kBufferSize, format, |
| gfx::BufferUsage::GPU_READ_CPU_READ_WRITE_PERSISTENT, &handle, |
| nullptr); |
| std::unique_ptr<TypeParam> buffer(TypeParam::CreateFromHandle( |
| handle, kBufferSize, format, |
| gfx::BufferUsage::GPU_READ_CPU_READ_WRITE_PERSISTENT, |
| destroy_callback)); |
| ASSERT_TRUE(buffer); |
| |
| // Map buffer into user space. |
| ASSERT_TRUE(buffer->Map()); |
| |
| // Copy and compare mapped buffers. |
| size_t num_planes = gfx::NumberOfPlanesForBufferFormat(format); |
| for (size_t plane = 0; plane < num_planes; ++plane) { |
| const size_t row_size_in_bytes = |
| gfx::RowSizeForBufferFormat(kBufferSize.width(), format, plane); |
| EXPECT_GT(row_size_in_bytes, 0u); |
| |
| std::unique_ptr<char[]> data(new char[row_size_in_bytes]); |
| memset(data.get(), 0x2a + plane, row_size_in_bytes); |
| |
| size_t height = kBufferSize.height() / |
| gfx::SubsamplingFactorForBufferFormat(format, plane); |
| for (size_t y = 0; y < height; ++y) { |
| memcpy(static_cast<char*>(buffer->memory(plane)) + |
| y * buffer->stride(plane), |
| data.get(), row_size_in_bytes); |
| EXPECT_EQ(0, memcmp(static_cast<char*>(buffer->memory(plane)) + |
| y * buffer->stride(plane), |
| data.get(), row_size_in_bytes)); |
| } |
| } |
| |
| buffer->Unmap(); |
| |
| // Remap the buffer, and compare again. It should contain the same data. |
| ASSERT_TRUE(buffer->Map()); |
| |
| for (size_t plane = 0; plane < num_planes; ++plane) { |
| const size_t row_size_in_bytes = |
| gfx::RowSizeForBufferFormat(kBufferSize.width(), format, plane); |
| |
| std::unique_ptr<char[]> data(new char[row_size_in_bytes]); |
| memset(data.get(), 0x2a + plane, row_size_in_bytes); |
| |
| size_t height = kBufferSize.height() / |
| gfx::SubsamplingFactorForBufferFormat(format, plane); |
| for (size_t y = 0; y < height; ++y) { |
| EXPECT_EQ(0, memcmp(static_cast<char*>(buffer->memory(plane)) + |
| y * buffer->stride(plane), |
| data.get(), row_size_in_bytes)); |
| } |
| } |
| |
| buffer->Unmap(); |
| } |
| } |
| |
| // The GpuMemoryBufferImplTest test case verifies behavior that is expected |
| // from a GpuMemoryBuffer implementation in order to be conformant. |
| REGISTER_TYPED_TEST_CASE_P(GpuMemoryBufferImplTest, |
| CreateFromHandle, |
| Map, |
| PersistentMap); |
| |
| } // namespace gpu |
| |
| #endif // GPU_IPC_CLIENT_GPU_MEMORY_BUFFER_IMPL_TEST_TEMPLATE_H_ |