blob: a896c72f6975dc4553cb104041de7fb861439d74 [file] [log] [blame]
// 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