blob: c9b9b69a1d57af61186ea7147b960f665ce3f20f [file] [log] [blame]
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/viz/service/display_embedder/skia_output_device_offscreen.h"
#include <memory>
#include <utility>
#include "base/check_is_test.h"
#include "components/viz/common/resources/shared_image_format_utils.h"
#include "gpu/command_buffer/service/graphite_shared_context.h"
#include "gpu/command_buffer/service/service_utils.h"
#include "gpu/command_buffer/service/shared_image/shared_image_format_service_utils.h"
#include "gpu/command_buffer/service/skia_utils.h"
#include "third_party/skia/include/core/SkSurface.h"
#include "third_party/skia/include/gpu/GpuTypes.h"
#include "third_party/skia/include/gpu/ganesh/SkSurfaceGanesh.h"
#include "third_party/skia/include/gpu/ganesh/gl/GrGLBackendSurface.h"
#include "third_party/skia/include/gpu/graphite/Context.h"
#include "third_party/skia/include/gpu/graphite/Surface.h"
#include "third_party/skia/include/gpu/graphite/TextureInfo.h"
#if BUILDFLAG(ENABLE_VULKAN)
#include "third_party/skia/include/gpu/ganesh/vk/GrVkBackendSurface.h"
#endif
namespace viz {
SkiaOutputDeviceOffscreen::SkiaOutputDeviceOffscreen(
scoped_refptr<gpu::SharedContextState> context_state,
gfx::SurfaceOrigin origin,
bool has_alpha,
scoped_refptr<gpu::MemoryTracker> memory_tracker,
DidSwapBufferCompleteCallback did_swap_buffer_complete_callback)
: SkiaOutputDevice(context_state->gr_context(),
context_state->graphite_shared_context(),
std::move(memory_tracker),
did_swap_buffer_complete_callback),
context_state_(context_state),
has_alpha_(has_alpha) {
capabilities_.uses_default_gl_framebuffer = false;
capabilities_.output_surface_origin = origin;
capabilities_.supports_post_sub_buffer = true;
// Some Vulkan drivers do not support kRGB_888x_SkColorType. Always use
// kRGBA/BGRA_8888_SkColorType instead and initialize surface to opaque as
// necessary.
// TODO(crbug.com/40141277): use the right color types base on GPU
// capabilities.
capabilities_.sk_color_type_map[SinglePlaneFormat::kRGBA_8888] =
kRGBA_8888_SkColorType;
capabilities_.sk_color_type_map[SinglePlaneFormat::kRGBX_8888] =
kRGBA_8888_SkColorType;
capabilities_.sk_color_type_map[SinglePlaneFormat::kBGRA_8888] =
kBGRA_8888_SkColorType;
capabilities_.sk_color_type_map[SinglePlaneFormat::kBGRX_8888] =
kBGRA_8888_SkColorType;
capabilities_.sk_color_type_map[SinglePlaneFormat::kRGBA_F16] =
kRGBA_F16_SkColorType;
}
SkiaOutputDeviceOffscreen::~SkiaOutputDeviceOffscreen() {
DiscardBackbuffer();
}
bool SkiaOutputDeviceOffscreen::Reshape(const ReshapeParams& params) {
DCHECK_EQ(params.transform, gfx::OVERLAY_TRANSFORM_NONE);
DiscardBackbuffer();
size_ = params.GfxSize();
if (size_.width() > capabilities_.max_texture_size ||
size_.height() > capabilities_.max_texture_size) {
LOG(ERROR) << "The requested size (" << size_.ToString()
<< ") exceeds the max texture size ("
<< capabilities_.max_texture_size << ")";
return false;
}
sk_color_type_ = params.image_info.colorType();
sk_color_space_ = params.image_info.refColorSpace();
sample_count_ = params.sample_count;
EnsureBackbuffer();
return true;
}
void SkiaOutputDeviceOffscreen::Present(
const std::optional<gfx::Rect>& update_rect,
BufferPresentedCallback feedback,
OutputSurfaceFrame frame) {
// Reshape should have been called first.
DCHECK(backend_texture_.isValid() || graphite_texture_.isValid());
StartSwapBuffers(std::move(feedback));
FinishSwapBuffers(gfx::SwapCompletionResult(gfx::SwapResult::SWAP_ACK),
gfx::Size(size_.width(), size_.height()), std::move(frame));
}
void SkiaOutputDeviceOffscreen::EnsureBackbuffer() {
// Ignore EnsureBackbuffer if Reshape has not been called yet.
if (size_.IsEmpty()) {
return;
}
CHECK(!backbuffer_estimated_size_);
if (auto* gr_context = context_state_->gr_context()) {
auto backend_format =
gr_context->defaultBackendFormat(sk_color_type_, GrRenderable::kYes);
#if BUILDFLAG(IS_MAC)
DCHECK_EQ(context_state_->gr_context_type(), gpu::GrContextType::kGL);
// Because SkiaOutputSurface may use IOSurface, we need to ensure that we
// are using the correct texture target for IOSurfaces (which depends on the
// GL implementation). Otherwise the validateSurface will fail because of
// the textureType mismatch.
backend_format = GrBackendFormats::MakeGL(
GrBackendFormats::AsGLFormatEnum(backend_format),
gpu::GetTextureTargetForIOSurfaces());
#endif
DCHECK(backend_format.isValid())
<< "GrBackendFormat is invalid for color_type: " << sk_color_type_;
if (has_alpha_) {
backend_texture_ = gr_context->createBackendTexture(
size_.width(), size_.height(), backend_format, skgpu::Mipmapped::kNo,
GrRenderable::kYes);
} else {
is_emulated_rgbx_ = true;
// Initialize alpha channel to opaque.
backend_texture_ = gr_context->createBackendTexture(
size_.width(), size_.height(), backend_format, SkColors::kBlack,
skgpu::Mipmapped::kNo, GrRenderable::kYes);
}
DCHECK(backend_texture_.isValid());
if (backend_texture_.backend() == GrBackendApi::kVulkan) {
#if BUILDFLAG(ENABLE_VULKAN)
GrVkImageInfo vk_image_info;
bool result =
GrBackendTextures::GetVkImageInfo(backend_texture_, &vk_image_info);
DCHECK(result);
backbuffer_estimated_size_ = vk_image_info.fAlloc.fSize;
#else
DCHECK(false);
#endif
} else {
auto info = SkImageInfo::Make(size_.width(), size_.height(),
sk_color_type_, kUnpremul_SkAlphaType);
size_t estimated_size = info.computeMinByteSize();
backbuffer_estimated_size_ = estimated_size;
}
} else {
CHECK(context_state_->graphite_shared_context());
if (!has_alpha_) {
is_emulated_rgbx_ = true;
}
// Get backend texture info needed for creating backend textures for
// offscreen.
skgpu::graphite::TextureInfo texture_info = gpu::GraphiteBackendTextureInfo(
context_state_->gr_context_type(),
SkColorTypeToSinglePlaneSharedImageFormat(sk_color_type_),
/*readonly=*/false,
/*plane_index=*/0,
/*is_yuv_plane=*/false, /*mipmapped=*/false,
/*scanout_dcomp_surface=*/false,
/*supports_multiplanar_rendering=*/false,
/*supports_multiplanar_copy=*/false);
graphite_texture_ =
context_state_->gpu_main_graphite_recorder()->createBackendTexture(
gfx::SizeToSkISize(size_), texture_info);
CHECK(graphite_texture_.isValid());
auto info = SkImageInfo::Make(size_.width(), size_.height(), sk_color_type_,
kUnpremul_SkAlphaType);
size_t estimated_size = info.computeMinByteSize();
backbuffer_estimated_size_ = estimated_size;
}
memory_type_tracker_->TrackMemAlloc(backbuffer_estimated_size_);
}
void SkiaOutputDeviceOffscreen::DiscardBackbuffer() {
if (backend_texture_.isValid()) {
sk_surface_.reset();
DeleteGrBackendTexture(context_state_.get(), &backend_texture_);
backend_texture_ = GrBackendTexture();
memory_type_tracker_->TrackMemFree(backbuffer_estimated_size_);
backbuffer_estimated_size_ = 0u;
} else if (graphite_texture_.isValid()) {
auto* graphite_shared_context = context_state_->graphite_shared_context();
CHECK(graphite_shared_context);
sk_surface_.reset();
graphite_shared_context->deleteBackendTexture(graphite_texture_);
graphite_texture_ = skgpu::graphite::BackendTexture();
memory_type_tracker_->TrackMemFree(backbuffer_estimated_size_);
backbuffer_estimated_size_ = 0u;
}
}
SkSurface* SkiaOutputDeviceOffscreen::BeginPaint(
std::vector<GrBackendSemaphore>* end_semaphores) {
DCHECK(backend_texture_.isValid() || graphite_texture_.isValid());
if (!sk_surface_) {
SkSurfaceProps surface_props;
if (auto* gr_context = context_state_->gr_context()) {
sk_surface_ = SkSurfaces::WrapBackendTexture(
gr_context, backend_texture_,
capabilities_.output_surface_origin == gfx::SurfaceOrigin::kTopLeft
? kTopLeft_GrSurfaceOrigin
: kBottomLeft_GrSurfaceOrigin,
sample_count_, sk_color_type_, sk_color_space_, &surface_props);
} else {
CHECK(context_state_->graphite_shared_context());
sk_surface_ = SkSurfaces::WrapBackendTexture(
context_state_->gpu_main_graphite_recorder(), graphite_texture_,
sk_color_type_, sk_color_space_, &surface_props);
}
}
return sk_surface_.get();
}
void SkiaOutputDeviceOffscreen::EndPaint() {}
void SkiaOutputDeviceOffscreen::ReadbackForTesting(
base::OnceCallback<void(SkBitmap)> callback) {
CHECK_IS_TEST();
struct ReadPixelsContext {
std::unique_ptr<const SkImage::AsyncReadResult> async_result;
bool finished = false;
static void OnReadPixelsDone(
void* raw_ctx,
std::unique_ptr<const SkImage::AsyncReadResult> async_result) {
ReadPixelsContext* context =
reinterpret_cast<ReadPixelsContext*>(raw_ctx);
context->async_result = std::move(async_result);
context->finished = true;
}
};
ReadPixelsContext context;
if (auto* graphite_shared_context =
context_state_->graphite_shared_context()) {
context_state_->FlushAndSubmit(true);
// asyncRescaleAndReadPixels is a context operation that inserts its own
// recording internally.
graphite_shared_context->asyncRescaleAndReadPixelsAndSubmit(
sk_surface_.get(), sk_surface_->imageInfo(),
SkIRect::MakeSize(sk_surface_->imageInfo().dimensions()),
SkImage::RescaleGamma::kSrc, SkImage::RescaleMode::kRepeatedLinear,
base::BindOnce(&ReadPixelsContext::OnReadPixelsDone), &context);
} else {
CHECK(context_state_->gr_context());
sk_surface_->asyncRescaleAndReadPixels(
sk_surface_->imageInfo(),
SkIRect::MakeSize(sk_surface_->imageInfo().dimensions()),
SkImage::RescaleGamma::kSrc, SkImage::RescaleMode::kRepeatedLinear,
&ReadPixelsContext::OnReadPixelsDone, &context);
context_state_->FlushAndSubmit(true);
}
CHECK(context.finished);
CHECK(context.async_result);
CHECK_EQ(1, context.async_result->count());
const SkPixmap src_pixmap(sk_surface_->imageInfo(),
const_cast<void*>(context.async_result->data(0)),
context.async_result->rowBytes(0));
// Copy the pixels so we don't need to keep |context.async_result| alive.
SkBitmap bitmap;
bitmap.allocPixels(src_pixmap.info());
CHECK(bitmap.writePixels(src_pixmap));
std::move(callback).Run(std::move(bitmap));
}
} // namespace viz