| // Copyright 2013 The Flutter 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 "flutter/impeller/golden_tests/vulkan_screenshotter.h" |
| |
| #include "flutter/fml/synchronization/waitable_event.h" |
| #include "flutter/impeller/golden_tests/metal_screenshot.h" |
| #define GLFW_INCLUDE_NONE |
| #include "third_party/glfw/include/GLFW/glfw3.h" |
| |
| namespace impeller { |
| namespace testing { |
| |
| namespace { |
| |
| using CGContextPtr = std::unique_ptr<std::remove_pointer<CGContextRef>::type, |
| decltype(&CGContextRelease)>; |
| using CGImagePtr = std::unique_ptr<std::remove_pointer<CGImageRef>::type, |
| decltype(&CGImageRelease)>; |
| using CGColorSpacePtr = |
| std::unique_ptr<std::remove_pointer<CGColorSpaceRef>::type, |
| decltype(&CGColorSpaceRelease)>; |
| |
| std::unique_ptr<Screenshot> ReadTexture( |
| const std::shared_ptr<Context>& surface_context, |
| const std::shared_ptr<Texture>& texture) { |
| DeviceBufferDescriptor buffer_desc; |
| buffer_desc.storage_mode = StorageMode::kHostVisible; |
| buffer_desc.size = |
| texture->GetTextureDescriptor().GetByteSizeOfBaseMipLevel(); |
| buffer_desc.readback = true; |
| std::shared_ptr<DeviceBuffer> device_buffer = |
| surface_context->GetResourceAllocator()->CreateBuffer(buffer_desc); |
| FML_CHECK(device_buffer); |
| |
| auto command_buffer = surface_context->CreateCommandBuffer(); |
| auto blit_pass = command_buffer->CreateBlitPass(); |
| bool success = blit_pass->AddCopy(texture, device_buffer); |
| FML_CHECK(success); |
| |
| success = blit_pass->EncodeCommands(surface_context->GetResourceAllocator()); |
| FML_CHECK(success); |
| |
| fml::AutoResetWaitableEvent latch; |
| success = |
| surface_context->GetCommandQueue() |
| ->Submit({command_buffer}, |
| [&latch](CommandBuffer::Status status) { |
| FML_CHECK(status == CommandBuffer::Status::kCompleted); |
| latch.Signal(); |
| }) |
| .ok(); |
| FML_CHECK(success); |
| latch.Wait(); |
| device_buffer->Invalidate(); |
| |
| // TODO(gaaclarke): Replace CoreImage requirement with something |
| // crossplatform. |
| |
| CGColorSpacePtr color_space(CGColorSpaceCreateDeviceRGB(), |
| &CGColorSpaceRelease); |
| CGBitmapInfo bitmap_info = |
| texture->GetTextureDescriptor().format == PixelFormat::kB8G8R8A8UNormInt |
| ? kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little |
| : kCGImageAlphaPremultipliedLast; |
| CGContextPtr context( |
| CGBitmapContextCreate( |
| device_buffer->OnGetContents(), texture->GetSize().width, |
| texture->GetSize().height, |
| /*bitsPerComponent=*/8, |
| /*bytesPerRow=*/texture->GetTextureDescriptor().GetBytesPerRow(), |
| color_space.get(), bitmap_info), |
| &CGContextRelease); |
| FML_CHECK(context); |
| CGImagePtr image(CGBitmapContextCreateImage(context.get()), &CGImageRelease); |
| FML_CHECK(image); |
| |
| // TODO(https://github.com/flutter/flutter/issues/142641): Perform the flip at |
| // the blit stage to avoid this slow copy. |
| if (texture->GetYCoordScale() == -1) { |
| CGContextPtr flipped_context( |
| CGBitmapContextCreate( |
| nullptr, texture->GetSize().width, texture->GetSize().height, |
| /*bitsPerComponent=*/8, |
| /*bytesPerRow=*/0, color_space.get(), bitmap_info), |
| &CGContextRelease); |
| CGContextTranslateCTM(flipped_context.get(), 0, texture->GetSize().height); |
| CGContextScaleCTM(flipped_context.get(), 1.0, -1.0); |
| CGContextDrawImage( |
| flipped_context.get(), |
| CGRectMake(0, 0, texture->GetSize().width, texture->GetSize().height), |
| image.get()); |
| CGImagePtr flipped_image(CGBitmapContextCreateImage(flipped_context.get()), |
| &CGImageRelease); |
| image.swap(flipped_image); |
| } |
| |
| return std::make_unique<MetalScreenshot>(image.release()); |
| } |
| } // namespace |
| |
| VulkanScreenshotter::VulkanScreenshotter( |
| const std::unique_ptr<PlaygroundImpl>& playground) |
| : playground_(playground) { |
| FML_CHECK(playground_); |
| } |
| |
| std::unique_ptr<Screenshot> VulkanScreenshotter::MakeScreenshot( |
| AiksContext& aiks_context, |
| const Picture& picture, |
| const ISize& size, |
| bool scale_content) { |
| Vector2 content_scale = |
| scale_content ? playground_->GetContentScale() : Vector2{1, 1}; |
| std::shared_ptr<Image> image = picture.ToImage( |
| aiks_context, |
| ISize(size.width * content_scale.x, size.height * content_scale.y)); |
| std::shared_ptr<Texture> texture = image->GetTexture(); |
| return ReadTexture(aiks_context.GetContext(), texture); |
| } |
| |
| } // namespace testing |
| } // namespace impeller |