blob: fa42c114afbf574309236341576e92512b47da1a [file] [log] [blame]
// 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 "components/viz/test/fake_skia_output_surface.h"
#include <utility>
#include "base/bind.h"
#include "base/logging.h"
#include "base/threading/thread_task_runner_handle.h"
#include "build/build_config.h"
#include "components/viz/common/frame_sinks/copy_output_request.h"
#include "components/viz/common/frame_sinks/copy_output_util.h"
#include "components/viz/common/resources/resource_format_utils.h"
#include "components/viz/service/display/output_surface_client.h"
#include "components/viz/service/display/output_surface_frame.h"
#include "components/viz/service/display/texture_deleter.h"
#include "gpu/GLES2/gl2extchromium.h"
#include "gpu/command_buffer/common/mailbox_holder.h"
#include "gpu/command_buffer/common/shared_image_usage.h"
#include "third_party/khronos/GLES2/gl2ext.h"
#include "third_party/skia/include/core/SkPixelRef.h"
#include "third_party/skia/include/gpu/GrBackendSurface.h"
#include "third_party/skia/include/gpu/GrDirectContext.h"
#include "third_party/skia/include/gpu/gl/GrGLTypes.h"
#include "ui/gfx/swap_result.h"
#include "ui/gl/gl_utils.h"
namespace viz {
FakeSkiaOutputSurface::FakeSkiaOutputSurface(
scoped_refptr<ContextProvider> context_provider)
: SkiaOutputSurface(SkiaOutputSurface::Type::kOpenGL),
context_provider_(std::move(context_provider)) {
texture_deleter_ =
std::make_unique<TextureDeleter>(base::ThreadTaskRunnerHandle::Get());
}
FakeSkiaOutputSurface::~FakeSkiaOutputSurface() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
}
void FakeSkiaOutputSurface::BindToClient(OutputSurfaceClient* client) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(client);
DCHECK(!client_);
client_ = client;
}
void FakeSkiaOutputSurface::EnsureBackbuffer() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
NOTIMPLEMENTED();
}
void FakeSkiaOutputSurface::DiscardBackbuffer() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
NOTIMPLEMENTED();
}
void FakeSkiaOutputSurface::BindFramebuffer() {
// TODO(penghuang): remove this method when GLRenderer is removed.
}
void FakeSkiaOutputSurface::Reshape(const gfx::Size& size,
float device_scale_factor,
const gfx::ColorSpace& color_space,
gfx::BufferFormat format,
bool use_stencil) {
auto& sk_surface = sk_surfaces_[AggregatedRenderPassId{0}];
SkColorType color_type = kRGBA_8888_SkColorType;
SkImageInfo image_info =
SkImageInfo::Make(size.width(), size.height(), color_type,
kPremul_SkAlphaType, color_space.ToSkColorSpace());
sk_surface =
SkSurface::MakeRenderTarget(gr_context(), SkBudgeted::kNo, image_info);
DCHECK(sk_surface);
}
void FakeSkiaOutputSurface::SwapBuffers(OutputSurfaceFrame frame) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (frame.delegated_ink_metadata)
last_delegated_ink_metadata_ = std::move(frame.delegated_ink_metadata);
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&FakeSkiaOutputSurface::SwapBuffersAck,
weak_ptr_factory_.GetWeakPtr()));
}
void FakeSkiaOutputSurface::ScheduleOutputSurfaceAsOverlay(
OverlayProcessorInterface::OutputSurfaceOverlayPlane output_surface_plane) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
NOTIMPLEMENTED();
}
uint32_t FakeSkiaOutputSurface::GetFramebufferCopyTextureFormat() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
return GL_RGB;
}
bool FakeSkiaOutputSurface::IsDisplayedAsOverlayPlane() const {
return false;
}
unsigned FakeSkiaOutputSurface::GetOverlayTextureId() const {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
return 0;
}
bool FakeSkiaOutputSurface::HasExternalStencilTest() const {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
return false;
}
void FakeSkiaOutputSurface::ApplyExternalStencil() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
}
unsigned FakeSkiaOutputSurface::UpdateGpuFence() {
NOTIMPLEMENTED();
return 0;
}
void FakeSkiaOutputSurface::SetNeedsSwapSizeNotifications(
bool needs_swap_size_notifications) {
NOTIMPLEMENTED();
}
void FakeSkiaOutputSurface::SetUpdateVSyncParametersCallback(
UpdateVSyncParametersCallback callback) {
NOTIMPLEMENTED();
}
gfx::OverlayTransform FakeSkiaOutputSurface::GetDisplayTransform() {
return gfx::OVERLAY_TRANSFORM_NONE;
}
SkCanvas* FakeSkiaOutputSurface::BeginPaintCurrentFrame() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
auto& sk_surface = sk_surfaces_[AggregatedRenderPassId{0}];
DCHECK(sk_surface);
DCHECK_EQ(current_render_pass_id_, AggregatedRenderPassId{0u});
return sk_surface->getCanvas();
}
void FakeSkiaOutputSurface::MakePromiseSkImage(ImageContext* image_context) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
GrBackendTexture backend_texture;
if (!GetGrBackendTexture(*image_context, &backend_texture)) {
DLOG(ERROR) << "Failed to GetGrBackendTexture from mailbox.";
return;
}
auto sk_color_type = ResourceFormatToClosestSkColorType(
true /* gpu_compositing */, image_context->resource_format());
image_context->SetImage(
SkImage::MakeFromTexture(gr_context(), backend_texture,
kTopLeft_GrSurfaceOrigin, sk_color_type,
image_context->alpha_type(),
image_context->color_space()),
backend_texture.getBackendFormat());
}
sk_sp<SkImage> FakeSkiaOutputSurface::MakePromiseSkImageFromYUV(
const std::vector<ImageContext*>& contexts,
sk_sp<SkColorSpace> image_color_space,
SkYUVAInfo::PlaneConfig plane_config,
SkYUVAInfo::Subsampling subsampling) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
NOTIMPLEMENTED();
return nullptr;
}
gpu::SyncToken FakeSkiaOutputSurface::ReleaseImageContexts(
std::vector<std::unique_ptr<ImageContext>> image_contexts) {
return gpu::SyncToken();
}
std::unique_ptr<ExternalUseClient::ImageContext>
FakeSkiaOutputSurface::CreateImageContext(
const gpu::MailboxHolder& holder,
const gfx::Size& size,
ResourceFormat format,
bool concurrent_reads,
const absl::optional<gpu::VulkanYCbCrInfo>& ycbcr_info,
sk_sp<SkColorSpace> color_space) {
return std::make_unique<ExternalUseClient::ImageContext>(
holder, size, format, ycbcr_info, std::move(color_space));
}
SkCanvas* FakeSkiaOutputSurface::BeginPaintRenderPass(
const AggregatedRenderPassId& id,
const gfx::Size& surface_size,
ResourceFormat format,
bool mipmap,
sk_sp<SkColorSpace> color_space,
const gpu::Mailbox& mailbox) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
// Make sure there is no unsubmitted PaintFrame or PaintRenderPass.
DCHECK_EQ(current_render_pass_id_, AggregatedRenderPassId{0u});
auto& sk_surface = sk_surfaces_[id];
if (!sk_surface) {
SkColorType color_type =
ResourceFormatToClosestSkColorType(true /* gpu_compositing */, format);
SkImageInfo image_info = SkImageInfo::Make(
surface_size.width(), surface_size.height(), color_type,
kPremul_SkAlphaType, std::move(color_space));
sk_surface =
SkSurface::MakeRenderTarget(gr_context(), SkBudgeted::kNo, image_info);
}
return sk_surface->getCanvas();
}
void FakeSkiaOutputSurface::EndPaint(base::OnceClosure on_finished) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
sk_surfaces_[current_render_pass_id_]->flushAndSubmit();
current_render_pass_id_ = AggregatedRenderPassId{0};
if (on_finished)
std::move(on_finished).Run();
}
sk_sp<SkImage> FakeSkiaOutputSurface::MakePromiseSkImageFromRenderPass(
const AggregatedRenderPassId& id,
const gfx::Size& size,
ResourceFormat format,
bool mipmap,
sk_sp<SkColorSpace> color_space,
const gpu::Mailbox& mailbox) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
auto it = sk_surfaces_.find(id);
DCHECK(it != sk_surfaces_.end());
return it->second->makeImageSnapshot();
}
void FakeSkiaOutputSurface::RemoveRenderPassResource(
std::vector<AggregatedRenderPassId> ids) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(!ids.empty());
for (const auto& id : ids) {
auto it = sk_surfaces_.find(id);
DCHECK(it != sk_surfaces_.end());
sk_surfaces_.erase(it);
}
}
void FakeSkiaOutputSurface::CopyOutput(
AggregatedRenderPassId id,
const copy_output::RenderPassGeometry& geometry,
const gfx::ColorSpace& color_space,
std::unique_ptr<CopyOutputRequest> request,
const gpu::Mailbox& mailbox) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(sk_surfaces_.find(id) != sk_surfaces_.end());
auto* surface = sk_surfaces_[id].get();
if (request->result_format() != CopyOutputResult::Format::RGBA ||
request->is_scaled() ||
geometry.result_bounds != geometry.result_selection) {
// TODO(crbug.com/644851): Complete the implementation for all request
// types, scaling, etc.
NOTIMPLEMENTED();
return;
}
if (request->result_destination() ==
CopyOutputResult::Destination::kNativeTextures) {
// TODO(rivr): This implementation is incomplete and doesn't copy
// anything into the mailbox, but currently the only tests that use this
// don't actually check the returned texture data.
auto* sii = context_provider_->SharedImageInterface();
gpu::Mailbox local_mailbox = sii->CreateSharedImage(
ResourceFormat::RGBA_8888, geometry.result_selection.size(),
color_space, kTopLeft_GrSurfaceOrigin, kPremul_SkAlphaType,
gpu::SHARED_IMAGE_USAGE_GLES2, gpu::kNullSurfaceHandle);
auto* gl = context_provider_->ContextGL();
gpu::SyncToken sync_token;
gl->GenSyncTokenCHROMIUM(sync_token.GetData());
CopyOutputResult::ReleaseCallbacks release_callbacks;
release_callbacks.push_back(
texture_deleter_->GetReleaseCallback(context_provider_, local_mailbox));
request->SendResult(std::make_unique<CopyOutputTextureResult>(
CopyOutputResult::Format::RGBA, geometry.result_bounds,
CopyOutputResult::TextureResult(local_mailbox, sync_token, color_space),
std::move(release_callbacks)));
return;
}
GrDirectContext* direct = GrAsDirectContext(gr_context());
auto copy_image = surface->makeImageSnapshot()->makeSubset(
RectToSkIRect(geometry.sampling_bounds), direct);
// Send copy request by copying into a bitmap.
SkBitmap bitmap;
copy_image->asLegacyBitmap(&bitmap);
// TODO(crbug.com/795132): Plumb color space throughout SkiaRenderer up to
// the SkSurface/SkImage here. Until then, play "musical chairs" with the
// SkPixelRef to hack-in the RenderPass's |color_space|.
sk_sp<SkPixelRef> pixels(SkSafeRef(bitmap.pixelRef()));
SkIPoint origin = bitmap.pixelRefOrigin();
bitmap.setInfo(bitmap.info().makeColorSpace(color_space.ToSkColorSpace()),
bitmap.rowBytes());
bitmap.setPixelRef(std::move(pixels), origin.x(), origin.y());
request->SendResult(std::make_unique<CopyOutputSkBitmapResult>(
geometry.result_bounds, std::move(bitmap)));
}
gpu::SharedImageInterface* FakeSkiaOutputSurface::GetSharedImageInterface() {
return context_provider_->SharedImageInterface();
}
void FakeSkiaOutputSurface::AddContextLostObserver(
ContextLostObserver* observer) {
NOTIMPLEMENTED();
}
void FakeSkiaOutputSurface::RemoveContextLostObserver(
ContextLostObserver* observer) {
NOTIMPLEMENTED();
}
gpu::SyncToken FakeSkiaOutputSurface::Flush() {
gpu::SyncToken sync_token;
context_provider()->ContextGL()->GenSyncTokenCHROMIUM(sync_token.GetData());
return sync_token;
}
#if BUILDFLAG(IS_APPLE) || defined(USE_OZONE)
SkCanvas* FakeSkiaOutputSurface::BeginPaintRenderPassOverlay(
const gfx::Size& size,
ResourceFormat format,
bool mipmap,
sk_sp<SkColorSpace> color_space) {
NOTIMPLEMENTED();
return nullptr;
}
sk_sp<SkDeferredDisplayList>
FakeSkiaOutputSurface::EndPaintRenderPassOverlay() {
NOTIMPLEMENTED();
return nullptr;
}
#endif
void FakeSkiaOutputSurface::SetOutOfOrderCallbacks(
bool out_of_order_callbacks) {
TestContextSupport* support =
static_cast<TestContextSupport*>(context_provider()->ContextSupport());
support->set_out_of_order_callbacks(out_of_order_callbacks);
}
bool FakeSkiaOutputSurface::GetGrBackendTexture(
const ImageContext& image_context,
GrBackendTexture* backend_texture) {
DCHECK(!image_context.mailbox_holder().mailbox.IsZero());
auto* gl = context_provider()->ContextGL();
gl->WaitSyncTokenCHROMIUM(
image_context.mailbox_holder().sync_token.GetConstData());
auto texture_id = gl->CreateAndConsumeTextureCHROMIUM(
image_context.mailbox_holder().mailbox.name);
auto gl_format = TextureStorageFormat(image_context.resource_format());
GrGLTextureInfo gl_texture_info = {
image_context.mailbox_holder().texture_target, texture_id, gl_format};
*backend_texture = GrBackendTexture(image_context.size().width(),
image_context.size().height(),
GrMipMapped::kNo, gl_texture_info);
return true;
}
void FakeSkiaOutputSurface::SwapBuffersAck() {
base::TimeTicks now = base::TimeTicks::Now();
client_->DidReceiveSwapBuffersAck({now, now},
/*release_fence=*/gfx::GpuFenceHandle());
client_->DidReceivePresentationFeedback({now, base::TimeDelta(), 0});
}
void FakeSkiaOutputSurface::ScheduleGpuTaskForTesting(
base::OnceClosure callback,
std::vector<gpu::SyncToken> sync_tokens) {
NOTIMPLEMENTED();
}
void FakeSkiaOutputSurface::InitDelegatedInkPointRendererReceiver(
mojo::PendingReceiver<gfx::mojom::DelegatedInkPointRenderer>
pending_receiver) {
delegated_ink_renderer_receiver_arrived_ = true;
}
} // namespace viz