| // Copyright 2019 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. |
| |
| #include "gpu/command_buffer/service/shared_image_backing_factory_gl_image.h" |
| |
| #include <memory> |
| #include <utility> |
| |
| #include "base/callback_helpers.h" |
| #include "gpu/command_buffer/common/shared_image_usage.h" |
| #include "gpu/command_buffer/service/mailbox_manager_impl.h" |
| #include "gpu/command_buffer/service/shared_context_state.h" |
| #include "gpu/command_buffer/service/shared_image_factory.h" |
| #include "gpu/command_buffer/service/shared_image_manager.h" |
| #include "gpu/command_buffer/service/shared_image_representation.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 "gpu/ipc/service/gpu_memory_buffer_factory_io_surface.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "third_party/skia/include/core/SkImage.h" |
| #include "third_party/skia/include/core/SkPromiseImageTexture.h" |
| #include "third_party/skia/include/core/SkSurface.h" |
| #include "third_party/skia/include/gpu/GrBackendSemaphore.h" |
| #include "third_party/skia/include/gpu/GrBackendSurface.h" |
| #include "ui/gl/buildflags.h" |
| #include "ui/gl/gl_context.h" |
| #include "ui/gl/gl_surface.h" |
| #include "ui/gl/init/gl_factory.h" |
| |
| #if BUILDFLAG(USE_DAWN) |
| #include <dawn/dawn_proc.h> |
| #include <dawn/webgpu_cpp.h> |
| #include <dawn_native/DawnNative.h> |
| #endif // BUILDFLAG(USE_DAWN) |
| |
| namespace gpu { |
| namespace { |
| |
| class SharedImageBackingFactoryIOSurfaceTest : public testing::Test { |
| public: |
| void SetUp() override { |
| surface_ = gl::init::CreateOffscreenGLSurface(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); |
| |
| GpuPreferences preferences; |
| preferences.texture_target_exception_list.push_back( |
| gfx::BufferUsageAndFormat(gfx::BufferUsage::SCANOUT, |
| gfx::BufferFormat::RGBA_8888)); |
| |
| GpuDriverBugWorkarounds workarounds; |
| scoped_refptr<gl::GLShareGroup> share_group = new gl::GLShareGroup(); |
| context_state_ = base::MakeRefCounted<SharedContextState>( |
| std::move(share_group), surface_, context_, |
| false /* use_virtualized_gl_contexts */, base::DoNothing()); |
| context_state_->InitializeGrContext(preferences, workarounds, nullptr); |
| auto feature_info = |
| base::MakeRefCounted<gles2::FeatureInfo>(workarounds, GpuFeatureInfo()); |
| context_state_->InitializeGL(preferences, std::move(feature_info)); |
| |
| backing_factory_ = std::make_unique<SharedImageBackingFactoryGLImage>( |
| preferences, workarounds, GpuFeatureInfo(), &image_factory_, |
| /*progress_reporter=*/nullptr); |
| |
| memory_type_tracker_ = std::make_unique<MemoryTypeTracker>(nullptr); |
| shared_image_representation_factory_ = |
| std::make_unique<SharedImageRepresentationFactory>( |
| &shared_image_manager_, nullptr); |
| } |
| |
| GrDirectContext* gr_context() { return context_state_->gr_context(); } |
| |
| protected: |
| scoped_refptr<gl::GLSurface> surface_; |
| scoped_refptr<gl::GLContext> context_; |
| scoped_refptr<SharedContextState> context_state_; |
| std::unique_ptr<SharedImageBackingFactoryGLImage> backing_factory_; |
| gles2::MailboxManagerImpl mailbox_manager_; |
| SharedImageManager shared_image_manager_; |
| std::unique_ptr<MemoryTypeTracker> memory_type_tracker_; |
| std::unique_ptr<SharedImageRepresentationFactory> |
| shared_image_representation_factory_; |
| GpuMemoryBufferFactoryIOSurface image_factory_; |
| |
| 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<SharedImageRepresentationSkia::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 = SkImage::MakeFromTexture( |
| 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]); |
| } |
| } |
| }; |
| |
| // Basic test to check creation and deletion of IOSurface backed shared image. |
| TEST_F(SharedImageBackingFactoryIOSurfaceTest, Basic) { |
| // TODO(jonahr): Test crashes on Mac with ANGLE/passthrough |
| // (crbug.com/1100980) |
| gpu::GPUTestBotConfig bot_config; |
| if (bot_config.LoadCurrentConfig(nullptr) && |
| bot_config.Matches("mac passthrough")) { |
| return; |
| } |
| |
| Mailbox mailbox = Mailbox::GenerateForSharedImage(); |
| viz::ResourceFormat format = viz::ResourceFormat::RGBA_8888; |
| gfx::Size size(256, 256); |
| gfx::ColorSpace color_space = gfx::ColorSpace::CreateSRGB(); |
| GrSurfaceOrigin surface_origin = kTopLeft_GrSurfaceOrigin; |
| SkAlphaType alpha_type = kPremul_SkAlphaType; |
| gpu::SurfaceHandle surface_handle = gpu::kNullSurfaceHandle; |
| uint32_t usage = SHARED_IMAGE_USAGE_GLES2 | SHARED_IMAGE_USAGE_SCANOUT; |
| |
| auto backing = backing_factory_->CreateSharedImage( |
| mailbox, format, surface_handle, size, color_space, surface_origin, |
| alpha_type, usage, false /* is_thread_safe */); |
| EXPECT_TRUE(backing); |
| |
| // Check clearing. |
| if (!backing->IsCleared()) { |
| backing->SetCleared(); |
| EXPECT_TRUE(backing->IsCleared()); |
| } |
| |
| // First, validate via a legacy mailbox. |
| GLenum expected_target = GL_TEXTURE_RECTANGLE; |
| EXPECT_TRUE(backing->ProduceLegacyMailbox(&mailbox_manager_)); |
| TextureBase* texture_base = mailbox_manager_.ConsumeTexture(mailbox); |
| |
| // Currently there is no support for passthrough texture on Mac and hence |
| // in IOSurface backing. So the TextureBase* should be pointing to a Texture |
| // object. |
| auto* texture = gles2::Texture::CheckedCast(texture_base); |
| ASSERT_TRUE(texture); |
| EXPECT_EQ(texture->target(), expected_target); |
| EXPECT_TRUE(texture->IsImmutable()); |
| int width, height, depth; |
| bool has_level = |
| texture->GetLevelSize(GL_TEXTURE_2D, 0, &width, &height, &depth); |
| EXPECT_TRUE(has_level); |
| EXPECT_EQ(width, size.width()); |
| EXPECT_EQ(height, size.height()); |
| |
| // Next validate via a SharedImageRepresentationGLTexture. |
| std::unique_ptr<SharedImageRepresentationFactoryRef> factory_ref = |
| shared_image_manager_.Register(std::move(backing), |
| memory_type_tracker_.get()); |
| 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(); |
| |
| // Finally, validate a SharedImageRepresentationSkia. |
| 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<SharedImageRepresentationSkia::ScopedWriteAccess> |
| scoped_write_access; |
| |
| scoped_write_access = skia_representation->BeginScopedWriteAccess( |
| &begin_semaphores, &end_semaphores, |
| SharedImageRepresentation::AllowUnclearedAccess::kYes); |
| auto* surface = scoped_write_access->surface(); |
| EXPECT_TRUE(surface); |
| 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<SharedImageRepresentationSkia::ScopedReadAccess> |
| scoped_read_access; |
| scoped_read_access = |
| skia_representation->BeginScopedReadAccess(nullptr, nullptr); |
| auto* promise_texture = scoped_read_access->promise_image_texture(); |
| EXPECT_TRUE(promise_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()); |
| scoped_read_access.reset(); |
| skia_representation.reset(); |
| |
| factory_ref.reset(); |
| EXPECT_FALSE(mailbox_manager_.ConsumeTexture(mailbox)); |
| } |
| |
| // Test to check interaction between Gl and skia GL representations. |
| // We write to a GL texture using gl representation and then read from skia |
| // representation. |
| TEST_F(SharedImageBackingFactoryIOSurfaceTest, GL_SkiaGL) { |
| // TODO(jonahr): Test crashes on Mac with ANGLE/passthrough |
| // (crbug.com/1100980) |
| gpu::GPUTestBotConfig bot_config; |
| if (bot_config.LoadCurrentConfig(nullptr) && |
| bot_config.Matches("mac passthrough")) { |
| return; |
| } |
| |
| // Create a backing using mailbox. |
| auto mailbox = Mailbox::GenerateForSharedImage(); |
| auto format = viz::ResourceFormat::RGBA_8888; |
| gfx::Size size(1, 1); |
| auto color_space = gfx::ColorSpace::CreateSRGB(); |
| GrSurfaceOrigin surface_origin = kTopLeft_GrSurfaceOrigin; |
| SkAlphaType alpha_type = kPremul_SkAlphaType; |
| gpu::SurfaceHandle surface_handle = gpu::kNullSurfaceHandle; |
| uint32_t usage = SHARED_IMAGE_USAGE_GLES2 | SHARED_IMAGE_USAGE_SCANOUT; |
| auto backing = backing_factory_->CreateSharedImage( |
| mailbox, format, surface_handle, size, color_space, surface_origin, |
| alpha_type, usage, false /* is_thread_safe */); |
| EXPECT_TRUE(backing); |
| |
| GLenum expected_target = GL_TEXTURE_RECTANGLE; |
| std::unique_ptr<SharedImageRepresentationFactoryRef> factory_ref = |
| shared_image_manager_.Register(std::move(backing), |
| memory_type_tracker_.get()); |
| |
| // Create a SharedImageRepresentationGLTexture. |
| { |
| auto gl_representation = |
| shared_image_representation_factory_->ProduceGLTexture(mailbox); |
| EXPECT_TRUE(gl_representation); |
| EXPECT_EQ(expected_target, gl_representation->GetTexture()->target()); |
| |
| // Access the SharedImageRepresentationGLTexutre |
| auto scoped_write_access = gl_representation->BeginScopedAccess( |
| GL_SHARED_IMAGE_ACCESS_MODE_READWRITE_CHROMIUM, |
| SharedImageRepresentation::AllowUnclearedAccess::kYes); |
| |
| // 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); |
| } |
| |
| CheckSkiaPixels(mailbox, size, {0, 255, 0, 255}); |
| factory_ref.reset(); |
| EXPECT_FALSE(mailbox_manager_.ConsumeTexture(mailbox)); |
| } |
| |
| // Test which ensures that legacy texture clear status is kept in sync with the |
| // SharedImageBacking. |
| TEST_F(SharedImageBackingFactoryIOSurfaceTest, LegacyClearing) { |
| Mailbox mailbox = Mailbox::GenerateForSharedImage(); |
| viz::ResourceFormat format = viz::ResourceFormat::RGBA_8888; |
| gfx::Size size(256, 256); |
| gfx::ColorSpace color_space = gfx::ColorSpace::CreateSRGB(); |
| GrSurfaceOrigin surface_origin = kTopLeft_GrSurfaceOrigin; |
| SkAlphaType alpha_type = kPremul_SkAlphaType; |
| uint32_t usage = SHARED_IMAGE_USAGE_GLES2 | SHARED_IMAGE_USAGE_SCANOUT; |
| gpu::SurfaceHandle surface_handle = gpu::kNullSurfaceHandle; |
| |
| // Create a backing. |
| auto backing = backing_factory_->CreateSharedImage( |
| mailbox, format, surface_handle, size, color_space, surface_origin, |
| alpha_type, usage, false /* is_thread_safe */); |
| EXPECT_TRUE(backing); |
| backing->SetCleared(); |
| EXPECT_TRUE(backing->IsCleared()); |
| |
| // Also create a legacy mailbox. |
| EXPECT_TRUE(backing->ProduceLegacyMailbox(&mailbox_manager_)); |
| TextureBase* texture_base = mailbox_manager_.ConsumeTexture(mailbox); |
| auto* texture = gles2::Texture::CheckedCast(texture_base); |
| EXPECT_TRUE(texture); |
| GLenum target = texture->target(); |
| |
| // Check initial state. |
| EXPECT_TRUE(texture->IsLevelCleared(target, 0)); |
| EXPECT_TRUE(backing->IsCleared()); |
| |
| // Un-clear the representation. |
| backing->SetClearedRect(gfx::Rect()); |
| EXPECT_FALSE(texture->IsLevelCleared(target, 0)); |
| EXPECT_FALSE(backing->IsCleared()); |
| |
| // Partially clear the representation. |
| gfx::Rect partial_clear_rect(0, 0, 128, 128); |
| backing->SetClearedRect(partial_clear_rect); |
| EXPECT_EQ(partial_clear_rect, texture->GetLevelClearedRect(target, 0)); |
| EXPECT_EQ(partial_clear_rect, backing->ClearedRect()); |
| |
| // Fully clear the representation. |
| backing->SetCleared(); |
| EXPECT_TRUE(texture->IsLevelCleared(target, 0)); |
| EXPECT_TRUE(backing->IsCleared()); |
| |
| // Un-clear the texture. |
| texture->SetLevelClearedRect(target, 0, gfx::Rect()); |
| EXPECT_FALSE(texture->IsLevelCleared(target, 0)); |
| EXPECT_FALSE(backing->IsCleared()); |
| |
| // Partially clear the texture. |
| texture->SetLevelClearedRect(target, 0, partial_clear_rect); |
| EXPECT_EQ(partial_clear_rect, texture->GetLevelClearedRect(target, 0)); |
| EXPECT_EQ(partial_clear_rect, backing->ClearedRect()); |
| |
| // Fully clear the representation. |
| texture->SetLevelCleared(target, 0, true); |
| EXPECT_TRUE(texture->IsLevelCleared(target, 0)); |
| EXPECT_TRUE(backing->IsCleared()); |
| } |
| |
| #if BUILDFLAG(USE_DAWN) |
| // Test to check interaction between Dawn and skia GL representations. |
| TEST_F(SharedImageBackingFactoryIOSurfaceTest, Dawn_SkiaGL) { |
| // Create a Dawn Metal device |
| dawn_native::Instance instance; |
| instance.DiscoverDefaultAdapters(); |
| |
| std::vector<dawn_native::Adapter> adapters = instance.GetAdapters(); |
| auto adapter_it = std::find_if( |
| adapters.begin(), adapters.end(), [](dawn_native::Adapter adapter) { |
| return adapter.GetBackendType() == dawn_native::BackendType::Metal; |
| }); |
| ASSERT_NE(adapter_it, adapters.end()); |
| |
| dawn_native::DeviceDescriptor device_descriptor; |
| // We need to request internal usage to be able to do operations with |
| // internal methods that would need specific usages. |
| device_descriptor.requiredFeatures.push_back("dawn-internal-usages"); |
| |
| wgpu::Device device = |
| wgpu::Device::Acquire(adapter_it->CreateDevice(&device_descriptor)); |
| DawnProcTable procs = dawn_native::GetProcs(); |
| dawnProcSetProcs(&procs); |
| |
| // Create a backing using mailbox. |
| auto mailbox = Mailbox::GenerateForSharedImage(); |
| auto format = viz::ResourceFormat::RGBA_8888; |
| gfx::Size size(1, 1); |
| auto color_space = gfx::ColorSpace::CreateSRGB(); |
| GrSurfaceOrigin surface_origin = kTopLeft_GrSurfaceOrigin; |
| SkAlphaType alpha_type = kPremul_SkAlphaType; |
| gpu::SurfaceHandle surface_handle = gpu::kNullSurfaceHandle; |
| uint32_t usage = SHARED_IMAGE_USAGE_WEBGPU | SHARED_IMAGE_USAGE_SCANOUT; |
| auto backing = backing_factory_->CreateSharedImage( |
| mailbox, format, surface_handle, size, color_space, surface_origin, |
| alpha_type, usage, false /* is_thread_safe */); |
| EXPECT_TRUE(backing); |
| |
| std::unique_ptr<SharedImageRepresentationFactoryRef> factory_ref = |
| shared_image_manager_.Register(std::move(backing), |
| memory_type_tracker_.get()); |
| |
| // Create a SharedImageRepresentationDawn. |
| auto dawn_representation = shared_image_representation_factory_->ProduceDawn( |
| mailbox, device.Get(), WGPUBackendType_Metal); |
| EXPECT_TRUE(dawn_representation); |
| |
| // Clear the shared image to green using Dawn. |
| { |
| auto scoped_access = dawn_representation->BeginScopedAccess( |
| WGPUTextureUsage_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.clearColor = {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.EndPass(); |
| 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(); |
| EXPECT_FALSE(mailbox_manager_.ConsumeTexture(mailbox)); |
| } |
| |
| // 1. Draw a color to texture through GL |
| // 2. Do not call SetCleared so we can test Dawn Lazy clear |
| // 3. Begin render pass in Dawn, but do not do anything |
| // 4. Verify through CheckSkiaPixel that GL drawn color not seen |
| TEST_F(SharedImageBackingFactoryIOSurfaceTest, GL_Dawn_Skia_UnclearTexture) { |
| // TODO(jonahr): Test crashes on Mac with ANGLE/passthrough |
| // (crbug.com/1100980) |
| gpu::GPUTestBotConfig bot_config; |
| if (bot_config.LoadCurrentConfig(nullptr) && |
| bot_config.Matches("mac passthrough")) { |
| return; |
| } |
| |
| // Create a backing using mailbox. |
| auto mailbox = Mailbox::GenerateForSharedImage(); |
| const auto format = viz::ResourceFormat::RGBA_8888; |
| const gfx::Size size(1, 1); |
| const auto color_space = gfx::ColorSpace::CreateSRGB(); |
| GrSurfaceOrigin surface_origin = kTopLeft_GrSurfaceOrigin; |
| SkAlphaType alpha_type = kPremul_SkAlphaType; |
| const gpu::SurfaceHandle surface_handle = gpu::kNullSurfaceHandle; |
| const uint32_t usage = SHARED_IMAGE_USAGE_GLES2 | SHARED_IMAGE_USAGE_SCANOUT | |
| SHARED_IMAGE_USAGE_WEBGPU; |
| auto backing = backing_factory_->CreateSharedImage( |
| mailbox, format, surface_handle, size, color_space, surface_origin, |
| alpha_type, usage, false /* is_thread_safe */); |
| EXPECT_TRUE(backing); |
| |
| GLenum expected_target = GL_TEXTURE_RECTANGLE; |
| std::unique_ptr<SharedImageRepresentationFactoryRef> factory_ref = |
| shared_image_manager_.Register(std::move(backing), |
| memory_type_tracker_.get()); |
| |
| { |
| // Create a SharedImageRepresentationGLTexture. |
| auto gl_representation = |
| shared_image_representation_factory_->ProduceGLTexture(mailbox); |
| EXPECT_TRUE(gl_representation); |
| EXPECT_EQ(expected_target, gl_representation->GetTexture()->target()); |
| |
| std::unique_ptr<SharedImageRepresentationGLTexturePassthrough::ScopedAccess> |
| gl_scoped_access = gl_representation->BeginScopedAccess( |
| GL_SHARED_IMAGE_ACCESS_MODE_READWRITE_CHROMIUM, |
| SharedImageRepresentation::AllowUnclearedAccess::kYes); |
| EXPECT_TRUE(gl_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); |
| |
| // Don't set cleared, we want to see if Dawn will lazy clear the texture |
| EXPECT_FALSE(factory_ref->IsCleared()); |
| } |
| |
| // Create a Dawn Metal device |
| dawn_native::Instance instance; |
| instance.DiscoverDefaultAdapters(); |
| |
| std::vector<dawn_native::Adapter> adapters = instance.GetAdapters(); |
| auto adapter_it = std::find_if( |
| adapters.begin(), adapters.end(), [](dawn_native::Adapter adapter) { |
| return adapter.GetBackendType() == dawn_native::BackendType::Metal; |
| }); |
| ASSERT_NE(adapter_it, adapters.end()); |
| |
| dawn_native::DeviceDescriptor device_descriptor; |
| // We need to request internal usage to be able to do operations with |
| // internal methods that would need specific usages. |
| device_descriptor.requiredFeatures.push_back("dawn-internal-usages"); |
| |
| wgpu::Device device = |
| wgpu::Device::Acquire(adapter_it->CreateDevice(&device_descriptor)); |
| DawnProcTable procs = dawn_native::GetProcs(); |
| dawnProcSetProcs(&procs); |
| { |
| auto dawn_representation = |
| shared_image_representation_factory_->ProduceDawn( |
| mailbox, device.Get(), WGPUBackendType_Metal); |
| ASSERT_TRUE(dawn_representation); |
| |
| auto dawn_scoped_access = dawn_representation->BeginScopedAccess( |
| WGPUTextureUsage_RenderAttachment, |
| SharedImageRepresentation::AllowUnclearedAccess::kYes); |
| ASSERT_TRUE(dawn_scoped_access); |
| |
| wgpu::Texture texture(dawn_scoped_access->texture()); |
| wgpu::RenderPassColorAttachment color_desc; |
| color_desc.view = texture.CreateView(); |
| color_desc.resolveTarget = nullptr; |
| color_desc.loadOp = wgpu::LoadOp::Load; |
| color_desc.storeOp = wgpu::StoreOp::Store; |
| |
| 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.EndPass(); |
| wgpu::CommandBuffer commands = encoder.Finish(); |
| |
| wgpu::Queue queue = device.GetQueue(); |
| queue.Submit(1, &commands); |
| } |
| |
| // Check skia pixels returns black since texture was lazy cleared in Dawn |
| EXPECT_TRUE(factory_ref->IsCleared()); |
| CheckSkiaPixels(mailbox, size, {0, 0, 0, 0}); |
| |
| // Shut down Dawn |
| device = wgpu::Device(); |
| dawnProcSetProcs(nullptr); |
| |
| factory_ref.reset(); |
| } |
| |
| // 1. Draw a color to texture through Dawn |
| // 2. Set the renderpass storeOp = Clear |
| // 3. Texture in Dawn will stay as uninitialized |
| // 3. Expect skia to fail to access the texture because texture is not |
| // initialized |
| TEST_F(SharedImageBackingFactoryIOSurfaceTest, UnclearDawn_SkiaFails) { |
| // Create a backing using mailbox. |
| auto mailbox = Mailbox::GenerateForSharedImage(); |
| const auto format = viz::ResourceFormat::RGBA_8888; |
| const gfx::Size size(1, 1); |
| const auto color_space = gfx::ColorSpace::CreateSRGB(); |
| GrSurfaceOrigin surface_origin = kTopLeft_GrSurfaceOrigin; |
| SkAlphaType alpha_type = kPremul_SkAlphaType; |
| const uint32_t usage = SHARED_IMAGE_USAGE_GLES2 | SHARED_IMAGE_USAGE_SCANOUT | |
| SHARED_IMAGE_USAGE_WEBGPU; |
| const gpu::SurfaceHandle surface_handle = gpu::kNullSurfaceHandle; |
| auto backing = backing_factory_->CreateSharedImage( |
| mailbox, format, surface_handle, size, color_space, surface_origin, |
| alpha_type, usage, false /* is_thread_safe */); |
| ASSERT_NE(backing, nullptr); |
| |
| std::unique_ptr<SharedImageRepresentationFactoryRef> factory_ref = |
| shared_image_manager_.Register(std::move(backing), |
| memory_type_tracker_.get()); |
| |
| // Create dawn device |
| dawn_native::Instance instance; |
| instance.DiscoverDefaultAdapters(); |
| |
| std::vector<dawn_native::Adapter> adapters = instance.GetAdapters(); |
| auto adapter_it = std::find_if( |
| adapters.begin(), adapters.end(), [](dawn_native::Adapter adapter) { |
| return adapter.GetBackendType() == dawn_native::BackendType::Metal; |
| }); |
| ASSERT_NE(adapter_it, adapters.end()); |
| |
| dawn_native::DeviceDescriptor device_descriptor; |
| // We need to request internal usage to be able to do operations with |
| // internal methods that would need specific usages. |
| device_descriptor.requiredFeatures.push_back("dawn-internal-usages"); |
| |
| wgpu::Device device = |
| wgpu::Device::Acquire(adapter_it->CreateDevice(&device_descriptor)); |
| DawnProcTable procs = dawn_native::GetProcs(); |
| dawnProcSetProcs(&procs); |
| { |
| auto dawn_representation = |
| shared_image_representation_factory_->ProduceDawn( |
| mailbox, device.Get(), WGPUBackendType_Metal); |
| ASSERT_TRUE(dawn_representation); |
| |
| auto dawn_scoped_access = dawn_representation->BeginScopedAccess( |
| WGPUTextureUsage_RenderAttachment, |
| SharedImageRepresentation::AllowUnclearedAccess::kYes); |
| ASSERT_TRUE(dawn_scoped_access); |
| |
| wgpu::Texture texture(dawn_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::Discard; |
| color_desc.clearColor = {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.EndPass(); |
| wgpu::CommandBuffer commands = encoder.Finish(); |
| |
| wgpu::Queue queue = device.GetQueue(); |
| queue.Submit(1, &commands); |
| } |
| |
| // Shut down Dawn |
| device = wgpu::Device(); |
| dawnProcSetProcs(nullptr); |
| |
| EXPECT_FALSE(factory_ref->IsCleared()); |
| |
| // Produce skia representation |
| auto skia_representation = shared_image_representation_factory_->ProduceSkia( |
| mailbox, context_state_); |
| ASSERT_NE(skia_representation, nullptr); |
| |
| // Expect BeginScopedReadAccess to fail because sharedImage is uninitialized |
| std::unique_ptr<SharedImageRepresentationSkia::ScopedReadAccess> |
| scoped_read_access = |
| skia_representation->BeginScopedReadAccess(nullptr, nullptr); |
| EXPECT_EQ(scoped_read_access, nullptr); |
| } |
| #endif // BUILDFLAG(USE_DAWN) |
| |
| // Test that Skia trying to access uninitialized SharedImage will fail |
| TEST_F(SharedImageBackingFactoryIOSurfaceTest, SkiaAccessFirstFails) { |
| // Create a mailbox. |
| auto mailbox = Mailbox::GenerateForSharedImage(); |
| const auto format = viz::ResourceFormat::RGBA_8888; |
| const gfx::Size size(1, 1); |
| const auto color_space = gfx::ColorSpace::CreateSRGB(); |
| GrSurfaceOrigin surface_origin = kTopLeft_GrSurfaceOrigin; |
| SkAlphaType alpha_type = kPremul_SkAlphaType; |
| const uint32_t usage = SHARED_IMAGE_USAGE_GLES2 | SHARED_IMAGE_USAGE_SCANOUT; |
| const gpu::SurfaceHandle surface_handle = gpu::kNullSurfaceHandle; |
| auto backing = backing_factory_->CreateSharedImage( |
| mailbox, format, surface_handle, size, color_space, surface_origin, |
| alpha_type, usage, false /* is_thread_safe */); |
| ASSERT_NE(backing, nullptr); |
| |
| std::unique_ptr<SharedImageRepresentationFactoryRef> factory_ref = |
| shared_image_manager_.Register(std::move(backing), |
| memory_type_tracker_.get()); |
| |
| auto skia_representation = shared_image_representation_factory_->ProduceSkia( |
| mailbox, context_state_); |
| ASSERT_NE(skia_representation, nullptr); |
| EXPECT_FALSE(skia_representation->IsCleared()); |
| |
| std::unique_ptr<SharedImageRepresentationSkia::ScopedReadAccess> |
| scoped_read_access = |
| skia_representation->BeginScopedReadAccess(nullptr, nullptr); |
| // Expect BeginScopedReadAccess to fail because sharedImage is uninitialized |
| EXPECT_EQ(scoped_read_access, nullptr); |
| } |
| } // anonymous namespace |
| } // namespace gpu |