Flutter Impeller
vulkan_screenshotter.mm
Go to the documentation of this file.
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
6 
7 #include "flutter/fml/synchronization/waitable_event.h"
9 #define GLFW_INCLUDE_NONE
10 #include "third_party/glfw/include/GLFW/glfw3.h"
11 
12 namespace impeller {
13 namespace testing {
14 
15 namespace {
16 
17 using CGContextPtr = std::unique_ptr<std::remove_pointer<CGContextRef>::type,
18  decltype(&CGContextRelease)>;
19 using CGImagePtr = std::unique_ptr<std::remove_pointer<CGImageRef>::type,
20  decltype(&CGImageRelease)>;
21 using CGColorSpacePtr =
22  std::unique_ptr<std::remove_pointer<CGColorSpaceRef>::type,
23  decltype(&CGColorSpaceRelease)>;
24 
25 std::unique_ptr<Screenshot> ReadTexture(
26  const std::shared_ptr<Context>& surface_context,
27  const std::shared_ptr<Texture>& texture) {
28  DeviceBufferDescriptor buffer_desc;
29  buffer_desc.storage_mode = StorageMode::kHostVisible;
30  buffer_desc.size =
31  texture->GetTextureDescriptor().GetByteSizeOfBaseMipLevel();
32  buffer_desc.readback = true;
33  std::shared_ptr<DeviceBuffer> device_buffer =
34  surface_context->GetResourceAllocator()->CreateBuffer(buffer_desc);
35  FML_CHECK(device_buffer);
36 
37  auto command_buffer = surface_context->CreateCommandBuffer();
38  auto blit_pass = command_buffer->CreateBlitPass();
39  bool success = blit_pass->AddCopy(texture, device_buffer);
40  FML_CHECK(success);
41 
42  success = blit_pass->EncodeCommands(surface_context->GetResourceAllocator());
43  FML_CHECK(success);
44 
45  fml::AutoResetWaitableEvent latch;
46  success =
47  surface_context->GetCommandQueue()
48  ->Submit({command_buffer},
49  [&latch](CommandBuffer::Status status) {
50  FML_CHECK(status == CommandBuffer::Status::kCompleted);
51  latch.Signal();
52  })
53  .ok();
54  FML_CHECK(success);
55  latch.Wait();
56  device_buffer->Invalidate();
57 
58  // TODO(gaaclarke): Replace CoreImage requirement with something
59  // crossplatform.
60 
61  CGColorSpacePtr color_space(CGColorSpaceCreateDeviceRGB(),
62  &CGColorSpaceRelease);
63  CGBitmapInfo bitmap_info =
64  texture->GetTextureDescriptor().format == PixelFormat::kB8G8R8A8UNormInt
65  ? kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little
66  : kCGImageAlphaPremultipliedLast;
67  CGContextPtr context(
68  CGBitmapContextCreate(
69  device_buffer->OnGetContents(), texture->GetSize().width,
70  texture->GetSize().height,
71  /*bitsPerComponent=*/8,
72  /*bytesPerRow=*/texture->GetTextureDescriptor().GetBytesPerRow(),
73  color_space.get(), bitmap_info),
74  &CGContextRelease);
75  FML_CHECK(context);
76  CGImagePtr image(CGBitmapContextCreateImage(context.get()), &CGImageRelease);
77  FML_CHECK(image);
78 
79  // TODO(https://github.com/flutter/flutter/issues/142641): Perform the flip at
80  // the blit stage to avoid this slow copy.
81  if (texture->GetYCoordScale() == -1) {
82  CGContextPtr flipped_context(
83  CGBitmapContextCreate(
84  nullptr, texture->GetSize().width, texture->GetSize().height,
85  /*bitsPerComponent=*/8,
86  /*bytesPerRow=*/0, color_space.get(), bitmap_info),
87  &CGContextRelease);
88  CGContextTranslateCTM(flipped_context.get(), 0, texture->GetSize().height);
89  CGContextScaleCTM(flipped_context.get(), 1.0, -1.0);
90  CGContextDrawImage(
91  flipped_context.get(),
92  CGRectMake(0, 0, texture->GetSize().width, texture->GetSize().height),
93  image.get());
94  CGImagePtr flipped_image(CGBitmapContextCreateImage(flipped_context.get()),
95  &CGImageRelease);
96  image.swap(flipped_image);
97  }
98 
99  return std::make_unique<MetalScreenshot>(image.release());
100 }
101 } // namespace
102 
104  const std::unique_ptr<PlaygroundImpl>& playground)
105  : playground_(playground) {
106  FML_CHECK(playground_);
107 }
108 
109 std::unique_ptr<Screenshot> VulkanScreenshotter::MakeScreenshot(
110  AiksContext& aiks_context,
111  const Picture& picture,
112  const ISize& size,
113  bool scale_content) {
114  Vector2 content_scale =
115  scale_content ? playground_->GetContentScale() : Vector2{1, 1};
116  std::shared_ptr<Image> image = picture.ToImage(
117  aiks_context,
118  ISize(size.width * content_scale.x, size.height * content_scale.y));
119  std::shared_ptr<Texture> texture = image->GetTexture();
120  return ReadTexture(aiks_context.GetContext(), texture);
121 }
122 
123 } // namespace testing
124 } // namespace impeller
impeller::TPoint::y
Type y
Definition: point.h:31
impeller::AiksContext
Definition: aiks_context.h:20
vulkan_screenshotter.h
impeller::testing::VulkanScreenshotter::MakeScreenshot
std::unique_ptr< Screenshot > MakeScreenshot(AiksContext &aiks_context, const Picture &picture, const ISize &size={300, 300}, bool scale_content=true) override
Definition: vulkan_screenshotter.mm:109
impeller::StorageMode::kHostVisible
@ kHostVisible
impeller::Picture
Definition: picture.h:20
impeller::TSize< int64_t >
impeller::testing::VulkanScreenshotter::VulkanScreenshotter
VulkanScreenshotter(const std::unique_ptr< PlaygroundImpl > &playground)
Definition: vulkan_screenshotter.mm:103
metal_screenshot.h
impeller::TSize::width
Type width
Definition: size.h:22
impeller::TPoint::x
Type x
Definition: point.h:30
impeller::ISize
TSize< int64_t > ISize
Definition: size.h:138
impeller::AiksContext::GetContext
std::shared_ptr< Context > GetContext() const
Definition: aiks_context.cc:40
impeller::Picture::ToImage
std::shared_ptr< Image > ToImage(AiksContext &context, ISize size) const
Definition: picture.cc:31
impeller::CommandBuffer::Status::kCompleted
@ kCompleted
impeller::TPoint< Scalar >
impeller::TSize::height
Type height
Definition: size.h:23
impeller::CommandBuffer::Status
Status
Definition: command_buffer.h:49
impeller::PixelFormat::kB8G8R8A8UNormInt
@ kB8G8R8A8UNormInt
impeller
Definition: aiks_blur_unittests.cc:20