// 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_dawn.h"
#include <utility>
#include "base/check_op.h"
#include "base/notreached.h"
#include "base/time/time.h"
#include "gpu/command_buffer/service/dawn_context_provider.h"
#include "gpu/command_buffer/service/shared_context_state.h"
#include "third_party/skia/include/gpu/graphite/BackendTexture.h"
#include "third_party/skia/include/gpu/graphite/Surface.h"
#include "third_party/skia/include/gpu/graphite/dawn/DawnTypes.h"
#include "ui/gfx/presentation_feedback.h"
#include "ui/gfx/vsync_provider.h"
#include "ui/gl/vsync_provider_win.h"
#if BUILDFLAG(IS_ANDROID)
#include "gpu/ipc/common/gpu_surface_lookup.h"
#endif
namespace viz {
namespace {
// TODO(crbug.com/dawn/286): Dawn requires that surface format is BGRA8Unorm for
// desktop and RGBA8Unorm for Android. Use GetPreferredSurfaceFormat when ready.
#if BUILDFLAG(IS_ANDROID)
constexpr SkColorType kSurfaceColorType = kRGBA_8888_SkColorType;
constexpr wgpu::TextureFormat kSwapChainFormat =
wgpu::TextureFormat::RGBA8Unorm;
#else
constexpr SkColorType kSurfaceColorType = kBGRA_8888_SkColorType;
constexpr wgpu::TextureFormat kSwapChainFormat =
wgpu::TextureFormat::BGRA8Unorm;
#endif
constexpr wgpu::TextureUsage kUsage =
wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::TextureBinding |
wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst;
} // namespace
std::unique_ptr<SkiaOutputDeviceDawn> SkiaOutputDeviceDawn::Create(
scoped_refptr<gpu::SharedContextState> context_state,
gfx::SurfaceOrigin origin,
gpu::SurfaceHandle surface_handle,
gpu::MemoryTracker* memory_tracker,
DidSwapBufferCompleteCallback did_swap_buffer_complete_callback) {
auto output_device = std::make_unique<SkiaOutputDeviceDawn>(
context_state, origin, memory_tracker,
std::move(did_swap_buffer_complete_callback), PassKey());
if (!output_device->Initialize(surface_handle)) {
return nullptr;
}
return output_device;
}
SkiaOutputDeviceDawn::SkiaOutputDeviceDawn(
scoped_refptr<gpu::SharedContextState> context_state,
gfx::SurfaceOrigin origin,
gpu::MemoryTracker* memory_tracker,
DidSwapBufferCompleteCallback did_swap_buffer_complete_callback,
base::PassKey<SkiaOutputDeviceDawn>)
: SkiaOutputDevice(
/*gr_context=*/nullptr,
context_state->graphite_context(),
memory_tracker,
did_swap_buffer_complete_callback),
context_state_(std::move(context_state)) {
capabilities_.output_surface_origin = origin;
capabilities_.uses_default_gl_framebuffer = false;
capabilities_.supports_post_sub_buffer = false;
capabilities_.sk_color_type_map[SinglePlaneFormat::kRGBA_8888] =
kSurfaceColorType;
capabilities_.sk_color_type_map[SinglePlaneFormat::kRGBX_8888] =
kSurfaceColorType;
capabilities_.sk_color_type_map[SinglePlaneFormat::kBGRA_8888] =
kSurfaceColorType;
capabilities_.sk_color_type_map[SinglePlaneFormat::kBGRX_8888] =
kSurfaceColorType;
}
bool SkiaOutputDeviceDawn::Initialize(gpu::SurfaceHandle surface_handle) {
wgpu::SurfaceDescriptor surface_desc;
#if BUILDFLAG(IS_WIN)
gpu::SurfaceHandle window_handle_to_draw_to;
// Only D3D swapchain requires that the rendering windows are owned by the
// process that's currently doing the rendering.
switch (context_state_->dawn_context_provider()->backend_type()) {
case wgpu::BackendType::D3D11:
case wgpu::BackendType::D3D12:
child_window_.Initialize();
window_handle_to_draw_to = child_window_.window();
break;
default:
window_handle_to_draw_to = surface_handle;
}
vsync_provider_ =
std::make_unique<gl::VSyncProviderWin>(window_handle_to_draw_to);
// Create the wgpu::Surface from our HWND.
wgpu::SurfaceDescriptorFromWindowsHWND hwnd_desc;
hwnd_desc.hwnd = window_handle_to_draw_to;
hwnd_desc.hinstance = GetModuleHandle(nullptr);
surface_desc.nextInChain = &hwnd_desc;
#endif
#if BUILDFLAG(IS_ANDROID)
bool can_be_used_with_surface_control = false;
auto surface_variant =
gpu::GpuSurfaceLookup::GetInstance()->AcquireJavaSurface(
surface_handle, &can_be_used_with_surface_control);
// Should only reach here if surface control is disabled. In which case
// browser should not be sending ScopedJavaSurfaceControl variant.
CHECK(absl::holds_alternative<gl::ScopedJavaSurface>(surface_variant));
auto& scoped_java_surface = absl::get<gl::ScopedJavaSurface>(surface_variant);
android_native_window_ = gl::ScopedANativeWindow(scoped_java_surface);
wgpu::SurfaceDescriptorFromAndroidNativeWindow android_native_window_desc;
android_native_window_desc.window = android_native_window_.a_native_window();
surface_desc.nextInChain = &android_native_window_desc;
#endif
auto* context_provider = context_state_->dawn_context_provider();
CHECK(context_provider && context_provider->GetDevice());
surface_ = context_provider->GetInstance().CreateSurface(&surface_desc);
wgpu::SurfaceCapabilities caps;
wgpu::Status result =
surface_.GetCapabilities(context_provider->GetAdapter(), &caps);
if (result == wgpu::Status::Error) {
// With Dawn/Vulkan the Vulkan surface is created lazily when needed, like
// here for GetCapabilities(), and not when `surface_` is created.
LOG(ERROR) << "Surface::GetCapabilities() failed";
return false;
}
// Verify `surface_` supports all the required usage for the swap chain.
CHECK_EQ(~caps.usages & kUsage, 0);
return true;
}
SkiaOutputDeviceDawn::~SkiaOutputDeviceDawn() = default;
bool SkiaOutputDeviceDawn::Reshape(const ReshapeParams& params) {
DCHECK_EQ(params.transform, gfx::OVERLAY_TRANSFORM_NONE);
size_ = params.GfxSize();
sk_color_space_ = params.image_info.refColorSpace();
sample_count_ = params.sample_count;
#if BUILDFLAG(IS_WIN)
if (child_window_.window()) {
child_window_.Resize(size_);
}
#endif
wgpu::SurfaceConfiguration config;
config.device = context_state_->dawn_context_provider()->GetDevice();
config.format = kSwapChainFormat;
config.usage = kUsage;
config.viewFormatCount = 0;
config.viewFormats = nullptr;
config.alphaMode = wgpu::CompositeAlphaMode::Auto;
config.width = size_.width();
config.height = size_.height();
config.presentMode = wgpu::PresentMode::Mailbox;
surface_.Configure(&config);
return true;
}
void SkiaOutputDeviceDawn::Present(const std::optional<gfx::Rect>& update_rect,
BufferPresentedCallback feedback,
OutputSurfaceFrame frame) {
DCHECK(!update_rect);
StartSwapBuffers({});
surface_.Present();
FinishSwapBuffers(gfx::SwapCompletionResult(gfx::SwapResult::SWAP_ACK),
gfx::Size(size_.width(), size_.height()), std::move(frame));
base::TimeTicks timestamp = base::TimeTicks::Now();
base::TimeTicks vsync_timebase;
base::TimeDelta vsync_interval;
uint32_t flags = 0;
// TODO(rivr): Add an async path for getting vsync parameters. The sync
// path is sufficient for VSyncProviderWin.
if (vsync_provider_ && vsync_provider_->GetVSyncParametersIfAvailable(
&vsync_timebase, &vsync_interval)) {
// Assume the buffer will be presented at the next vblank.
timestamp = timestamp.SnappedToNextTick(vsync_timebase, vsync_interval);
// kHWClock allows future timestamps to be accepted.
flags =
gfx::PresentationFeedback::kVSync | gfx::PresentationFeedback::kHWClock;
}
std::move(feedback).Run(
gfx::PresentationFeedback(timestamp, vsync_interval, flags));
}
SkSurface* SkiaOutputDeviceDawn::BeginPaint(
std::vector<GrBackendSemaphore>* end_semaphores) {
wgpu::SurfaceTexture texture;
surface_.GetCurrentTexture(&texture);
auto backend_texture =
skgpu::graphite::BackendTextures::MakeDawn(texture.texture.Get());
SkSurfaceProps surface_props;
sk_surface_ = SkSurfaces::WrapBackendTexture(
context_state_->gpu_main_graphite_recorder(), backend_texture,
kSurfaceColorType, sk_color_space_, &surface_props);
return sk_surface_.get();
}
void SkiaOutputDeviceDawn::EndPaint() {
CHECK(sk_surface_);
if (GrDirectContext* direct_context =
GrAsDirectContext(sk_surface_->recordingContext())) {
direct_context->flush(sk_surface_.get(),
SkSurfaces::BackendSurfaceAccess::kPresent, {});
}
sk_surface_.reset();
}
} // namespace viz