[go: nahoru, domu]

blob: 7879b67e0d54fef15819b7f85553d1209436234f [file] [log] [blame]
// 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 "gpu/command_buffer/service/shared_image/egl_image_backing_factory.h"
#include <thread>
#include "base/bits.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/raw_ptr.h"
#include "base/strings/stringprintf.h"
#include "base/test/test_timeouts.h"
#include "build/build_config.h"
#include "components/viz/common/resources/resource_sizes.h"
#include "gpu/command_buffer/common/gpu_memory_buffer_support.h"
#include "gpu/command_buffer/common/mailbox.h"
#include "gpu/command_buffer/common/shared_image_usage.h"
#include "gpu/command_buffer/service/mailbox_manager_impl.h"
#include "gpu/command_buffer/service/service_utils.h"
#include "gpu/command_buffer/service/shared_context_state.h"
#include "gpu/command_buffer/service/shared_image/dawn_image_representation_unittest_common.h"
#include "gpu/command_buffer/service/shared_image/shared_image_backing.h"
#include "gpu/command_buffer/service/shared_image/shared_image_factory.h"
#include "gpu/command_buffer/service/shared_image/shared_image_format_service_utils.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_utils.h"
#include "gpu/config/gpu_driver_bug_workarounds.h"
#include "gpu/config/gpu_feature_info.h"
#include "gpu/config/gpu_preferences.h"
#include "gpu/config/gpu_test_config.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "third_party/skia/include/core/SkImage.h"
#include "third_party/skia/include/gpu/GrBackendSemaphore.h"
#include "third_party/skia/include/gpu/GrBackendSurface.h"
#include "third_party/skia/include/gpu/ganesh/SkImageGanesh.h"
#include "third_party/skia/include/private/chromium/GrPromiseImageTexture.h"
#include "ui/gfx/buffer_format_util.h"
#include "ui/gfx/color_space.h"
#include "ui/gl/gl_bindings.h"
#include "ui/gl/gl_context.h"
#include "ui/gl/gl_surface.h"
#include "ui/gl/gl_surface_egl.h"
#include "ui/gl/gl_utils.h"
#include "ui/gl/init/gl_factory.h"
#if BUILDFLAG(USE_DAWN) && BUILDFLAG(DAWN_ENABLE_BACKEND_OPENGLES)
#include <dawn/dawn_proc.h>
#include <dawn/native/DawnNative.h>
#include <dawn/webgpu_cpp.h>
#endif // BUILDFLAG(USE_DAWN) && BUILDFLAG(DAWN_ENABLE_BACKEND_OPENGLES)
using testing::AtLeast;
namespace gpu {
namespace {
bool IsEglImageSupported() {
// Creating a context and making it current to initialize dynamic bindings
// which is needed to query extensions from current gl driver.
scoped_refptr<gl::GLSurface> surface = gl::init::CreateOffscreenGLSurface(
gl::GetDefaultDisplayEGL(), gfx::Size());
DCHECK(surface);
scoped_refptr<gl::GLContext> context =
gl::init::CreateGLContext(nullptr, surface.get(), gl::GLContextAttribs());
DCHECK(context);
bool result = context->MakeCurrent(surface.get());
DCHECK(result);
// Check the required extensions to support egl images.
auto* egl_display = gl::GetDefaultDisplayEGL();
if (egl_display && egl_display->ext->b_EGL_KHR_image_base &&
egl_display->ext->b_EGL_KHR_gl_texture_2D_image &&
egl_display->ext->b_EGL_KHR_fence_sync &&
gl::g_current_gl_driver->ext.b_GL_OES_EGL_image) {
return true;
}
return false;
}
void CreateSharedContext(const GpuDriverBugWorkarounds& workarounds,
scoped_refptr<gl::GLSurface>& surface,
scoped_refptr<gl::GLContext>& context,
scoped_refptr<SharedContextState>& context_state,
scoped_refptr<gles2::FeatureInfo>& feature_info) {
surface = gl::init::CreateOffscreenGLSurface(gl::GetDefaultDisplayEGL(),
gfx::Size());
ASSERT_TRUE(surface);
context =
gl::init::CreateGLContext(nullptr, surface.get(), gl::GLContextAttribs());
ASSERT_TRUE(context);
bool result = context->MakeCurrent(surface.get());
ASSERT_TRUE(result);
scoped_refptr<gl::GLShareGroup> share_group = new gl::GLShareGroup();
feature_info =
base::MakeRefCounted<gles2::FeatureInfo>(workarounds, GpuFeatureInfo());
context_state = base::MakeRefCounted<SharedContextState>(
std::move(share_group), surface, context,
/*use_virtualized_gl_contexts=*/false, base::DoNothing(),
GrContextType::kGL);
context_state->InitializeSkia(GpuPreferences(), workarounds);
context_state->InitializeGL(GpuPreferences(), feature_info);
}
class EGLImageBackingFactoryThreadSafeTest
: public testing::TestWithParam<std::tuple<bool, viz::SharedImageFormat>> {
public:
EGLImageBackingFactoryThreadSafeTest()
: shared_image_manager_(std::make_unique<SharedImageManager>(true)) {}
~EGLImageBackingFactoryThreadSafeTest() override {
// |context_state_| and |context_state2_| must be destroyed on its own
// context.
if (context_state2_) {
context_state2_->MakeCurrent(surface2_.get(), /*needs_gl=*/true);
context_state2_.reset();
}
if (context_state_) {
context_state_->MakeCurrent(surface_.get(), /*needs_gl=*/true);
context_state_.reset();
}
}
void SetUp() override {
if (!IsEglImageSupported()) {
GTEST_SKIP();
}
#if BUILDFLAG(IS_ANDROID)
auto* command_line = base::CommandLine::ForCurrentProcess();
if (gles2::UsePassthroughCommandDecoder(command_line)) {
// TODO(crbug.com/1472516): fix this tests to work with passthrough.
GTEST_SKIP();
}
#endif
GpuDriverBugWorkarounds workarounds;
scoped_refptr<gles2::FeatureInfo> feature_info;
CreateSharedContext(workarounds, surface_, context_, context_state_,
feature_info);
GpuPreferences preferences;
preferences.use_passthrough_cmd_decoder = use_passthrough();
backing_factory_ = std::make_unique<EGLImageBackingFactory>(
preferences, workarounds, context_state_->feature_info());
memory_type_tracker_ = std::make_unique<MemoryTypeTracker>(nullptr);
shared_image_representation_factory_ =
std::make_unique<SharedImageRepresentationFactory>(
shared_image_manager_.get(), nullptr);
// Create 2nd context/context_state which are not part of same shared group.
scoped_refptr<gles2::FeatureInfo> feature_info2;
CreateSharedContext(workarounds, surface2_, context2_, context_state2_,
feature_info2);
feature_info2.reset();
}
bool use_passthrough() {
return std::get<0>(GetParam()) &&
gles2::PassthroughCommandDecoderSupported();
}
viz::SharedImageFormat get_format() { return std::get<1>(GetParam()); }
protected:
#if BUILDFLAG(USE_DAWN) && BUILDFLAG(DAWN_ENABLE_BACKEND_OPENGLES)
void CheckSkiaPixels(const Mailbox& mailbox,
const gfx::Size& size,
const std::vector<uint8_t> expected_color) {
auto skia_representation =
shared_image_representation_factory_->ProduceSkia(mailbox,
context_state_);
ASSERT_NE(skia_representation, nullptr);
std::unique_ptr<SkiaImageRepresentation::ScopedReadAccess>
scoped_read_access =
skia_representation->BeginScopedReadAccess(nullptr, nullptr);
EXPECT_TRUE(scoped_read_access);
auto* promise_texture = scoped_read_access->promise_image_texture();
GrBackendTexture backend_texture = promise_texture->backendTexture();
EXPECT_TRUE(backend_texture.isValid());
EXPECT_EQ(size.width(), backend_texture.width());
EXPECT_EQ(size.height(), backend_texture.height());
// Create an Sk Image from GrBackendTexture.
auto sk_image = SkImages::BorrowTextureFrom(
context_state_->gr_context(), backend_texture, kTopLeft_GrSurfaceOrigin,
kRGBA_8888_SkColorType, kOpaque_SkAlphaType, nullptr);
const SkImageInfo dst_info =
SkImageInfo::Make(size.width(), size.height(), kRGBA_8888_SkColorType,
kOpaque_SkAlphaType, nullptr);
const int num_pixels = size.width() * size.height();
std::vector<uint8_t> dst_pixels(num_pixels * 4);
// Read back pixels from Sk Image.
EXPECT_TRUE(sk_image->readPixels(dst_info, dst_pixels.data(),
dst_info.minRowBytes(), 0, 0));
for (int i = 0; i < num_pixels; i++) {
// Compare the pixel values.
const uint8_t* pixel = dst_pixels.data() + (i * 4);
EXPECT_EQ(pixel[0], expected_color[0]);
EXPECT_EQ(pixel[1], expected_color[1]);
EXPECT_EQ(pixel[2], expected_color[2]);
EXPECT_EQ(pixel[3], expected_color[3]);
}
}
void CheckDawnPixels(wgpu::Texture texture,
const wgpu::Device& device,
const gfx::Size& size,
const std::vector<uint8_t>& expected_color) const {
uint32_t buffer_stride =
static_cast<uint32_t>(base::bits::AlignUp(size.width() * 4, 256));
size_t buffer_size = static_cast<size_t>(size.height()) * buffer_stride;
wgpu::BufferDescriptor buffer_desc{
.usage = wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::MapRead,
.size = buffer_size};
wgpu::Buffer buffer = device.CreateBuffer(&buffer_desc);
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
auto src = wgpu::ImageCopyTexture{.texture = texture, .origin = {0, 0, 0}};
auto dst = wgpu::ImageCopyBuffer{.layout = {.bytesPerRow = buffer_stride},
.buffer = buffer};
auto copy_size = wgpu::Extent3D{static_cast<uint32_t>(size.width()),
static_cast<uint32_t>(size.height(), 1)};
encoder.CopyTextureToBuffer(&src, &dst, &copy_size);
wgpu::CommandBuffer commands = encoder.Finish();
wgpu::Queue queue = device.GetQueue();
queue.Submit(1, &commands);
WGPUBufferMapAsyncStatus map_status = WGPUBufferMapAsyncStatus_Unknown;
auto map_callback = [](WGPUBufferMapAsyncStatus status, void* userdata) {
WGPUBufferMapAsyncStatus* status_out =
reinterpret_cast<WGPUBufferMapAsyncStatus*>(userdata);
*status_out = status;
};
buffer.MapAsync(wgpu::MapMode::Read, 0, buffer_desc.size, map_callback,
&map_status);
// Tick device until async map operation completes.
while (map_status == WGPUBufferMapAsyncStatus_Unknown) {
device.Tick();
base::PlatformThread::Sleep(TestTimeouts::tiny_timeout());
}
const uint8_t* dst_pixels =
reinterpret_cast<const uint8_t*>(buffer.GetConstMappedRange());
for (int row = 0; row < size.height(); row++) {
for (int col = 0; col < size.width(); col++) {
// Compare the pixel values.
const uint8_t* pixel = dst_pixels + (row * buffer_stride) + col * 4;
EXPECT_EQ(pixel[0], expected_color[0]);
EXPECT_EQ(pixel[1], expected_color[1]);
EXPECT_EQ(pixel[2], expected_color[2]);
EXPECT_EQ(pixel[3], expected_color[3]);
}
}
}
#endif // BUILDFLAG(USE_DAWN) && BUILDFLAG(DAWN_ENABLE_BACKEND_OPENGLES)
scoped_refptr<gl::GLSurface> surface_;
scoped_refptr<gl::GLContext> context_;
scoped_refptr<SharedContextState> context_state_;
std::unique_ptr<EGLImageBackingFactory> backing_factory_;
gles2::MailboxManagerImpl mailbox_manager_;
std::unique_ptr<SharedImageManager> shared_image_manager_;
std::unique_ptr<MemoryTypeTracker> memory_type_tracker_;
std::unique_ptr<SharedImageRepresentationFactory>
shared_image_representation_factory_;
scoped_refptr<gl::GLSurface> surface2_;
scoped_refptr<gl::GLContext> context2_;
scoped_refptr<SharedContextState> context_state2_;
};
class CreateAndValidateSharedImageRepresentations {
public:
CreateAndValidateSharedImageRepresentations(
EGLImageBackingFactory* backing_factory,
viz::SharedImageFormat format,
bool is_thread_safe,
gles2::MailboxManagerImpl* mailbox_manager,
SharedImageManager* shared_image_manager,
MemoryTypeTracker* memory_type_tracker,
SharedImageRepresentationFactory* shared_image_representation_factory,
SharedContextState* context_state,
bool upload_initial_data);
~CreateAndValidateSharedImageRepresentations();
gfx::Size size() { return size_; }
Mailbox mailbox() { return mailbox_; }
private:
raw_ptr<gles2::MailboxManagerImpl> mailbox_manager_;
gfx::Size size_;
Mailbox mailbox_;
std::unique_ptr<SharedImageBacking> backing_;
std::unique_ptr<SharedImageRepresentationFactoryRef> shared_image_;
};
// Intent of this test is to create at thread safe backing and test if all
// representations are working.
TEST_P(EGLImageBackingFactoryThreadSafeTest, BasicThreadSafe) {
CreateAndValidateSharedImageRepresentations shared_image(
backing_factory_.get(), get_format(), /*is_thread_safe=*/true,
&mailbox_manager_, shared_image_manager_.get(),
memory_type_tracker_.get(), shared_image_representation_factory_.get(),
context_state_.get(), /*upload_initial_data=*/false);
}
// Intent of this test is to create at thread safe backing with initial pixel
// data and test if all representations are working.
TEST_P(EGLImageBackingFactoryThreadSafeTest, BasicInitialData) {
CreateAndValidateSharedImageRepresentations shared_image(
backing_factory_.get(), get_format(), /*is_thread_safe=*/true,
&mailbox_manager_, shared_image_manager_.get(),
memory_type_tracker_.get(), shared_image_representation_factory_.get(),
context_state_.get(), /*upload_initial_data=*/true);
}
// Intent of this test is to use the shared image mailbox system by 2 different
// threads each running their own GL context which are not part of same shared
// group. One thread will be writing to the backing and other thread will be
// reading from it.
TEST_P(EGLImageBackingFactoryThreadSafeTest, OneWriterOneReader) {
// Create it on 1st SharedContextState |context_state_|.
CreateAndValidateSharedImageRepresentations shared_image(
backing_factory_.get(), get_format(), /*is_thread_safe=*/true,
&mailbox_manager_, shared_image_manager_.get(),
memory_type_tracker_.get(), shared_image_representation_factory_.get(),
context_state_.get(), /*upload_initial_data=*/false);
auto mailbox = shared_image.mailbox();
auto size = shared_image.size();
// Writer will write to the backing. We will create a GLTexture representation
// and write green color to it.
auto gl_representation =
shared_image_representation_factory_->ProduceGLTexture(mailbox);
EXPECT_TRUE(gl_representation);
// Begin writing to the underlying texture of the backing via ScopedAccess.
std::unique_ptr<GLTextureImageRepresentation::ScopedAccess>
writer_scoped_access = gl_representation->BeginScopedAccess(
GL_SHARED_IMAGE_ACCESS_MODE_READ_CHROMIUM,
SharedImageRepresentation::AllowUnclearedAccess::kNo);
DCHECK(writer_scoped_access);
// Create an FBO.
GLuint fbo = 0;
gl::GLApi* api = gl::g_current_gl_context;
api->glGenFramebuffersEXTFn(1, &fbo);
api->glBindFramebufferEXTFn(GL_FRAMEBUFFER, fbo);
// Attach the texture to FBO.
api->glFramebufferTexture2DEXTFn(
GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
gl_representation->GetTexture()->target(),
gl_representation->GetTexture()->service_id(), 0);
// Set the clear color to green.
api->glClearColorFn(0.0f, 1.0f, 0.0f, 1.0f);
api->glClearFn(GL_COLOR_BUFFER_BIT);
gl_representation->GetTexture()->SetLevelCleared(
gl_representation->GetTexture()->target(), 0, true);
// End writing.
writer_scoped_access.reset();
gl_representation.reset();
// Read from the backing in a separate thread. Read is done via
// SkiaGLImageRepresentation. ReadPixels() creates/produces a
// SkiaGLImageRepresentation which in turn wraps a
// GLTextureImageRepresentation when for GL mode. Hence testing reading via
// SkiaGLImageRepresentation is equivalent to testing via
// GLTextureImageRepresentation.
std::vector<uint8_t> dst_pixels;
// Launch 2nd thread.
std::thread second_thread([&]() {
// Do ReadPixels() on 2nd SharedContextState |context_state2_|.
dst_pixels = ReadPixels(mailbox, size, context_state2_.get(),
shared_image_representation_factory_.get());
});
// Wait for this thread to be done.
second_thread.join();
// Compare the pixel values.
EXPECT_EQ(dst_pixels[0], 0);
EXPECT_EQ(dst_pixels[1], 255);
EXPECT_EQ(dst_pixels[2], 0);
EXPECT_EQ(dst_pixels[3], 255);
}
#if BUILDFLAG(USE_DAWN) && BUILDFLAG(DAWN_ENABLE_BACKEND_OPENGLES)
// Test to check interaction between Dawn and skia GL representations.
TEST_F(EGLImageBackingFactoryThreadSafeTest, Dawn_SkiaGL) {
// Find a Dawn GLES adapter
dawn::native::Instance instance;
wgpu::RequestAdapterOptions adapter_options;
adapter_options.backendType = wgpu::BackendType::OpenGLES;
std::vector<dawn::native::Adapter> adapters =
instance.EnumerateAdapters(&adapter_options);
ASSERT_GT(adapters.size(), 0u);
// We need to request internal usage to be able to do operations with
// internal methods that would need specific usages.
wgpu::FeatureName dawn_internal_usage = wgpu::FeatureName::DawnInternalUsages;
wgpu::DeviceDescriptor device_descriptor;
device_descriptor.requiredFeatureCount = 1;
device_descriptor.requiredFeatures = &dawn_internal_usage;
wgpu::Device device =
wgpu::Device::Acquire(adapters[0].CreateDevice(&device_descriptor));
DawnProcTable procs = dawn::native::GetProcs();
dawnProcSetProcs(&procs);
// Create a backing using mailbox.
const auto mailbox = Mailbox::GenerateForSharedImage();
const auto format = viz::SinglePlaneFormat::kRGBA_8888;
const gfx::Size size(1, 1);
const auto color_space = gfx::ColorSpace::CreateSRGB();
const gpu::SurfaceHandle surface_handle = gpu::kNullSurfaceHandle;
const uint32_t usage =
SHARED_IMAGE_USAGE_WEBGPU | SHARED_IMAGE_USAGE_DISPLAY_READ;
// Note that this backing is always thread safe by default even if it is not
// requested to be.
auto backing = backing_factory_->CreateSharedImage(
mailbox, format, surface_handle, size, color_space,
kTopLeft_GrSurfaceOrigin, kPremul_SkAlphaType, usage, "TestLabel",
/* is_thread_safe=*/true);
ASSERT_NE(backing, nullptr);
std::unique_ptr<SharedImageRepresentationFactoryRef> factory_ref =
shared_image_manager_->Register(std::move(backing),
memory_type_tracker_.get());
// Clear the shared image to green using Dawn.
{
// Create a DawnImageRepresentation using WGPUBackendType_OpenGLES backend.
auto dawn_representation =
shared_image_representation_factory_->ProduceDawn(
mailbox, device, wgpu::BackendType::OpenGLES, {});
ASSERT_TRUE(dawn_representation);
auto scoped_access = dawn_representation->BeginScopedAccess(
wgpu::TextureUsage::RenderAttachment,
SharedImageRepresentation::AllowUnclearedAccess::kYes);
ASSERT_TRUE(scoped_access);
wgpu::Texture texture(scoped_access->texture());
wgpu::RenderPassColorAttachment color_desc;
color_desc.view = texture.CreateView();
color_desc.resolveTarget = nullptr;
color_desc.loadOp = wgpu::LoadOp::Clear;
color_desc.storeOp = wgpu::StoreOp::Store;
color_desc.clearValue = {0, 255, 0, 255};
wgpu::RenderPassDescriptor renderPassDesc = {};
renderPassDesc.colorAttachmentCount = 1;
renderPassDesc.colorAttachments = &color_desc;
renderPassDesc.depthStencilAttachment = nullptr;
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDesc);
pass.End();
wgpu::CommandBuffer commands = encoder.Finish();
wgpu::Queue queue = device.GetQueue();
queue.Submit(1, &commands);
}
CheckSkiaPixels(mailbox, size, {0, 255, 0, 255});
// Shut down Dawn
device = wgpu::Device();
dawnProcSetProcs(nullptr);
factory_ref.reset();
}
// Check an EGLImage wrapped and sampled in Dawn.
TEST_P(EGLImageBackingFactoryThreadSafeTest, Dawn_SampledTexture) {
DawnProcTable procs = dawn::native::GetProcs();
dawnProcSetProcs(&procs);
// Create a Dawm OpenGLES device.
dawn::native::Instance instance;
wgpu::RequestAdapterOptions adapter_options;
adapter_options.backendType = wgpu::BackendType::OpenGLES;
adapter_options.compatibilityMode = true;
std::vector<dawn::native::Adapter> adapters =
instance.EnumerateAdapters(&adapter_options);
ASSERT_FALSE(adapters.empty());
// Allocate all the Dawn objects in their own scope so they are freed before
// dawnProcSetProcs(null) is called (below).
{
wgpu::Adapter adapter(adapters[0].Get());
wgpu::DeviceDescriptor device_descriptor;
wgpu::Device device = adapter.CreateDevice(&device_descriptor);
// Create a backing using mailbox.
const auto mailbox = Mailbox::GenerateForSharedImage();
const auto format = viz::SinglePlaneFormat::kRGBA_8888;
const gfx::Size size(1, 1);
const auto color_space = gfx::ColorSpace::CreateSRGB();
const uint32_t usage = SHARED_IMAGE_USAGE_WEBGPU;
std::vector<uint8_t> pixel_data = {0x80, 0x40, 0x20, 0x10};
auto backing = backing_factory_->CreateSharedImage(
mailbox, format, size, color_space, kTopLeft_GrSurfaceOrigin,
kPremul_SkAlphaType, usage, "Dawn_SampledTexture", pixel_data);
ASSERT_NE(backing, nullptr);
std::unique_ptr<SharedImageRepresentationFactoryRef> factory_ref =
shared_image_manager_->Register(std::move(backing),
memory_type_tracker_.get());
// Create a DawnImageRepresentation using the OpenGLES backend.
auto dawn_representation =
shared_image_representation_factory_->ProduceDawn(
mailbox, device, wgpu::BackendType::OpenGLES, {});
ASSERT_TRUE(dawn_representation);
auto scoped_access = dawn_representation->BeginScopedAccess(
wgpu::TextureUsage::TextureBinding,
SharedImageRepresentation::AllowUnclearedAccess::kYes);
ASSERT_TRUE(scoped_access);
wgpu::Texture texture(scoped_access->texture());
wgpu::TextureDescriptor attachmentDesc;
attachmentDesc.usage =
wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc;
attachmentDesc.size = {1, 1, 1};
attachmentDesc.format = wgpu::TextureFormat::RGBA8Unorm;
wgpu::Texture attachment = device.CreateTexture(&attachmentDesc);
wgpu::RenderPassColorAttachment colorAttachmentDesc;
colorAttachmentDesc.view = attachment.CreateView();
colorAttachmentDesc.resolveTarget = nullptr;
colorAttachmentDesc.loadOp = wgpu::LoadOp::Clear;
colorAttachmentDesc.storeOp = wgpu::StoreOp::Store;
colorAttachmentDesc.clearValue = {0, 255, 0, 255};
wgpu::RenderPassDescriptor renderPassDesc = {};
renderPassDesc.colorAttachmentCount = 1;
renderPassDesc.colorAttachments = &colorAttachmentDesc;
renderPassDesc.depthStencilAttachment = nullptr;
// Render pipeline
constexpr char kVS[] = R"(
struct VertexOut {
@location(0) tex_coord : vec2 <f32>,
@builtin(position) position : vec4f,
}
@vertex fn main(
@builtin(vertex_index) vertex_index : u32,
) -> VertexOut {
const pos = array(
vec2f(-1.0, -1.0),
vec2f( 3.0, -1.0),
vec2f(-1.0, 3.0));
var out_vert: VertexOut;
out_vert.position = vec4f(pos[vertex_index], 0.0, 1.0);
out_vert.tex_coord = vec2f(out_vert.position.xy * 0.5) + vec2f(0.5, 0.5);
return out_vert;
}
)";
constexpr char kFS[] = R"(
@group(0) @binding(0) var smp : sampler;
@group(0) @binding(1) var tex : texture_2d<f32>;
@fragment fn main(@location(0) tex_coord : vec2f) -> @location(0) vec4f {
return textureSample(tex, smp, tex_coord);
}
)";
auto render_pipeline = CreateRenderPipeline(
device, CreateShaderModule(device, kVS),
CreateShaderModule(device, kFS), wgpu::TextureFormat::RGBA8Unorm);
ASSERT_NE(render_pipeline, nullptr);
wgpu::SamplerDescriptor sampler_desc;
auto sampler = device.CreateSampler(&sampler_desc);
ASSERT_NE(sampler, nullptr);
std::array<wgpu::BindGroupEntry, 2> bind_group_entries;
bind_group_entries[0].binding = 0;
bind_group_entries[0].sampler = sampler;
bind_group_entries[1].binding = 1;
bind_group_entries[1].textureView = scoped_access->texture().CreateView();
wgpu::BindGroupDescriptor bind_group_desc;
bind_group_desc.entryCount = 2;
bind_group_desc.entries = bind_group_entries.data();
bind_group_desc.layout = render_pipeline.GetBindGroupLayout(0);
auto bind_group = device.CreateBindGroup(&bind_group_desc);
ASSERT_NE(bind_group, nullptr);
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDesc);
pass.SetPipeline(render_pipeline);
pass.SetBindGroup(0, bind_group);
pass.Draw(6, 1, 0, 0);
pass.End();
wgpu::CommandBuffer commands = encoder.Finish();
wgpu::Queue queue = device.GetQueue();
queue.Submit(1, &commands);
CheckDawnPixels(attachment, device, size, pixel_data);
}
// Shut down Dawn
dawnProcSetProcs(nullptr);
}
#endif // BUILDFLAG(USE_DAWN) && BUILDFLAG(DAWN_ENABLE_BACKEND_OPENGLES)
CreateAndValidateSharedImageRepresentations::
CreateAndValidateSharedImageRepresentations(
EGLImageBackingFactory* backing_factory,
viz::SharedImageFormat format,
bool is_thread_safe,
gles2::MailboxManagerImpl* mailbox_manager,
SharedImageManager* shared_image_manager,
MemoryTypeTracker* memory_type_tracker,
SharedImageRepresentationFactory* shared_image_representation_factory,
SharedContextState* context_state,
bool upload_initial_data)
: mailbox_manager_(mailbox_manager), size_(256, 256) {
// Make the context current.
DCHECK(context_state);
EXPECT_TRUE(
context_state->MakeCurrent(context_state->surface(), true /* needs_gl*/));
mailbox_ = Mailbox::GenerateForSharedImage();
auto color_space = gfx::ColorSpace::CreateSRGB();
GrSurfaceOrigin surface_origin = kTopLeft_GrSurfaceOrigin;
SkAlphaType alpha_type = kPremul_SkAlphaType;
gpu::SurfaceHandle surface_handle = gpu::kNullSurfaceHandle;
// SHARED_IMAGE_USAGE_DISPLAY_READ for skia read and SHARED_IMAGE_USAGE_RASTER
// for skia write.
uint32_t usage = SHARED_IMAGE_USAGE_GLES2 | SHARED_IMAGE_USAGE_RASTER;
if (!is_thread_safe)
usage |= SHARED_IMAGE_USAGE_DISPLAY_READ;
if (upload_initial_data) {
std::vector<uint8_t> initial_data(
viz::ResourceSizes::CheckedSizeInBytes<unsigned int>(size_, format));
backing_ = backing_factory->CreateSharedImage(
mailbox_, format, size_, color_space, surface_origin, alpha_type, usage,
"TestLabel", initial_data);
} else {
backing_ = backing_factory->CreateSharedImage(
mailbox_, format, surface_handle, size_, color_space, surface_origin,
alpha_type, usage, "TestLabel", is_thread_safe);
}
// As long as either |chromium_image_ar30| or |chromium_image_ab30| is
// enabled, we can create a non-scanout SharedImage with format
// viz::SinglePlaneFormat::{BGRA,RGBA}_1010102.
const bool supports_ar30 =
context_state->feature_info()->feature_flags().chromium_image_ar30;
const bool supports_ab30 =
context_state->feature_info()->feature_flags().chromium_image_ab30;
if ((format == viz::SinglePlaneFormat::kBGRA_1010102 ||
format == viz::SinglePlaneFormat::kRGBA_1010102) &&
!supports_ar30 && !supports_ab30) {
EXPECT_FALSE(backing_);
return;
}
EXPECT_TRUE(backing_);
if (!backing_)
return;
// Check clearing.
if (!backing_->IsCleared()) {
backing_->SetCleared();
EXPECT_TRUE(backing_->IsCleared());
}
GLenum expected_target = GL_TEXTURE_2D;
shared_image_ =
shared_image_manager->Register(std::move(backing_), memory_type_tracker);
// Create and validate GLTexture representation.
auto gl_representation =
shared_image_representation_factory->ProduceGLTexture(mailbox_);
EXPECT_TRUE(gl_representation);
EXPECT_TRUE(gl_representation->GetTexture()->service_id());
EXPECT_EQ(expected_target, gl_representation->GetTexture()->target());
EXPECT_EQ(size_, gl_representation->size());
EXPECT_EQ(format, gl_representation->format());
EXPECT_EQ(color_space, gl_representation->color_space());
EXPECT_EQ(usage, gl_representation->usage());
gl_representation.reset();
// Create and Validate Skia Representations.
auto skia_representation =
shared_image_representation_factory->ProduceSkia(mailbox_, context_state);
EXPECT_TRUE(skia_representation);
std::vector<GrBackendSemaphore> begin_semaphores;
std::vector<GrBackendSemaphore> end_semaphores;
std::unique_ptr<SkiaImageRepresentation::ScopedWriteAccess>
scoped_write_access;
scoped_write_access = skia_representation->BeginScopedWriteAccess(
&begin_semaphores, &end_semaphores,
SharedImageRepresentation::AllowUnclearedAccess::kNo);
// We use |supports_ar30| and |supports_ab30| to detect RGB10A2/BGR10A2
// support. It's possible Skia might support these formats even if the Chrome
// feature flags are false. We just check here that the feature flags don't
// allow Chrome to do something that Skia doesn't support.
if ((format != viz::SinglePlaneFormat::kBGRA_1010102 || supports_ar30) &&
(format != viz::SinglePlaneFormat::kRGBA_1010102 || supports_ab30)) {
EXPECT_TRUE(scoped_write_access);
if (!scoped_write_access)
return;
auto* surface = scoped_write_access->surface();
EXPECT_TRUE(surface);
if (!surface)
return;
EXPECT_EQ(size_.width(), surface->width());
EXPECT_EQ(size_.height(), surface->height());
}
EXPECT_TRUE(begin_semaphores.empty());
EXPECT_TRUE(end_semaphores.empty());
scoped_write_access.reset();
std::unique_ptr<SkiaImageRepresentation::ScopedReadAccess> scoped_read_access;
scoped_read_access = skia_representation->BeginScopedReadAccess(
&begin_semaphores, &end_semaphores);
auto* promise_texture = scoped_read_access->promise_image_texture();
EXPECT_TRUE(promise_texture);
EXPECT_TRUE(begin_semaphores.empty());
EXPECT_TRUE(end_semaphores.empty());
GrBackendTexture backend_texture = promise_texture->backendTexture();
EXPECT_TRUE(backend_texture.isValid());
EXPECT_EQ(size_.width(), backend_texture.width());
EXPECT_EQ(size_.height(), backend_texture.height());
scoped_read_access.reset();
skia_representation.reset();
}
CreateAndValidateSharedImageRepresentations::
~CreateAndValidateSharedImageRepresentations() {
shared_image_.reset();
EXPECT_FALSE(mailbox_manager_->ConsumeTexture(mailbox_));
}
// High bit depth rendering is not supported on Android.
const auto kSharedImageFormats =
::testing::Values(viz::SinglePlaneFormat::kRGBA_8888);
std::string TestParamToString(
const testing::TestParamInfo<std::tuple<bool, viz::SharedImageFormat>>&
param_info) {
const bool allow_passthrough = std::get<0>(param_info.param);
const viz::SharedImageFormat format = std::get<1>(param_info.param);
return base::StringPrintf(
"%s_%s", (allow_passthrough ? "AllowPassthrough" : "DisallowPassthrough"),
format.ToString().c_str());
}
INSTANTIATE_TEST_SUITE_P(Service,
EGLImageBackingFactoryThreadSafeTest,
::testing::Combine(::testing::Bool(),
kSharedImageFormats),
TestParamToString);
} // anonymous namespace
} // namespace gpu