blob: 7601b803975397da62e95e443edd5c5aba9dd3a4 [file] [log] [blame]
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/gl/direct_composition_surface_win.h"
#include <memory>
#include <utility>
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/metrics/histogram_macros.h"
#include "base/task/single_thread_task_runner.h"
#include "base/trace_event/trace_event.h"
#include "ui/gfx/presentation_feedback.h"
#include "ui/gfx/swap_result.h"
#include "ui/gl/dc_layer_tree.h"
#include "ui/gl/direct_composition_child_surface_win.h"
#include "ui/gl/direct_composition_support.h"
#include "ui/gl/gl_angle_util_win.h"
namespace gl {
DirectCompositionSurfaceWin::PendingFrame::PendingFrame(
Microsoft::WRL::ComPtr<ID3D11Query> query,
PresentationCallback callback)
: query(std::move(query)), callback(std::move(callback)) {}
DirectCompositionSurfaceWin::PendingFrame::PendingFrame(PendingFrame&& other) =
default;
DirectCompositionSurfaceWin::PendingFrame::~PendingFrame() = default;
DirectCompositionSurfaceWin::PendingFrame&
DirectCompositionSurfaceWin::PendingFrame::operator=(PendingFrame&& other) =
default;
DirectCompositionSurfaceWin::DirectCompositionSurfaceWin(
GLDisplayEGL* display,
const Settings& settings)
: GLSurfaceEGL(display),
d3d11_device_(GetDirectCompositionD3D11Device()),
task_runner_(base::SingleThreadTaskRunner::GetCurrentDefault()),
max_pending_frames_(settings.max_pending_frames),
root_surface_(new DirectCompositionChildSurfaceWin(
display,
d3d11_device_,
settings.use_angle_texture_offset)),
layer_tree_(std::make_unique<DCLayerTree>(
settings.disable_nv12_dynamic_textures,
settings.disable_vp_auto_hdr,
settings.disable_vp_scaling,
settings.disable_vp_super_resolution,
settings.force_dcomp_triple_buffer_video_swap_chain,
settings.no_downscaled_overlay_promotion)) {}
DirectCompositionSurfaceWin::~DirectCompositionSurfaceWin() {
Destroy();
}
bool DirectCompositionSurfaceWin::Initialize(GLSurfaceFormat format) {
if (!DirectCompositionSupported()) {
DLOG(ERROR) << "Direct composition not supported";
return false;
}
child_window_.Initialize();
if (!layer_tree_->Initialize(window(), d3d11_device_)) {
return false;
}
if (!root_surface_->Initialize(GLSurfaceFormat()))
return false;
return true;
}
void DirectCompositionSurfaceWin::Destroy() {
for (auto& frame : pending_frames_)
std::move(frame.callback).Run(gfx::PresentationFeedback::Failure());
pending_frames_.clear();
if (observing_vsync_) {
VSyncThreadWin::GetInstance()->RemoveObserver(this);
}
root_surface_->Destroy();
// Freeing DComp resources such as visuals and surfaces causes the
// device to become 'dirty'. We must commit the changes to the device
// in order for the objects to actually be destroyed.
// Leaving the device in the dirty state for long periods of time means
// that if DWM.exe crashes, the Chromium window will become black until
// the next Commit.
layer_tree_.reset();
if (auto* dcomp_device = GetDirectCompositionDevice())
dcomp_device->Commit();
}
gfx::Size DirectCompositionSurfaceWin::GetSize() {
return root_surface_->GetSize();
}
bool DirectCompositionSurfaceWin::IsOffscreen() {
return false;
}
void* DirectCompositionSurfaceWin::GetHandle() {
return root_surface_->GetHandle();
}
bool DirectCompositionSurfaceWin::Resize(const gfx::Size& size,
float scale_factor,
const gfx::ColorSpace& color_space,
bool has_alpha) {
if (!child_window_.Resize(size)) {
return false;
}
return root_surface_->Resize(size, scale_factor, color_space, has_alpha);
}
gfx::SwapResult DirectCompositionSurfaceWin::SwapBuffers(
PresentationCallback callback,
gfx::FrameData data) {
TRACE_EVENT0("gpu", "DirectCompositionSurfaceWin::SwapBuffers");
gfx::Rect swap_rect;
bool success = root_surface_->EndDraw(&swap_rect);
// Do not create query for empty damage so that 3D engine is not used when
// only presenting video in overlay. Callback will be dequeued on next vsync.
EnqueuePendingFrame(std::move(callback),
/*create_query=*/!swap_rect.IsEmpty());
if (!success)
return gfx::SwapResult::SWAP_FAILED;
if (!layer_tree_->CommitAndClearPendingOverlays(root_surface_.get()))
return gfx::SwapResult::SWAP_FAILED;
return gfx::SwapResult::SWAP_ACK;
}
gfx::SwapResult DirectCompositionSurfaceWin::PostSubBuffer(
int x,
int y,
int width,
int height,
PresentationCallback callback,
gfx::FrameData data) {
// The arguments are ignored because SetDrawRectangle specified the area to
// be swapped.
return SwapBuffers(std::move(callback), data);
}
gfx::VSyncProvider* DirectCompositionSurfaceWin::GetVSyncProvider() {
return VSyncThreadWin::GetInstance()->vsync_provider();
}
void DirectCompositionSurfaceWin::SetVSyncEnabled(bool enabled) {
root_surface_->SetVSyncEnabled(enabled);
}
void DirectCompositionSurfaceWin::OnVSync(base::TimeTicks vsync_time,
base::TimeDelta interval) {
task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&DirectCompositionSurfaceWin::HandleVSyncOnMainThread,
weak_factory_.GetWeakPtr(), vsync_time, interval));
}
bool DirectCompositionSurfaceWin::ScheduleDCLayer(
std::unique_ptr<DCLayerOverlayParams> params) {
return layer_tree_->ScheduleDCLayer(std::move(params));
}
void DirectCompositionSurfaceWin::SetFrameRate(float frame_rate) {
// Only try to reduce vsync frequency through the video swap chain.
// This allows us to experiment UseSetPresentDuration optimization to
// fullscreen video overlays only and avoid compromising
// UsePreferredIntervalForVideo optimization where we skip compositing
// every other frame when fps <= half the vsync frame rate.
layer_tree_->SetFrameRate(frame_rate);
}
bool DirectCompositionSurfaceWin::SetEnableDCLayers(bool enable) {
return root_surface_->SetEnableDCLayers(enable);
}
gfx::SurfaceOrigin DirectCompositionSurfaceWin::GetOrigin() const {
return gfx::SurfaceOrigin::kTopLeft;
}
bool DirectCompositionSurfaceWin::SupportsPostSubBuffer() {
return true;
}
bool DirectCompositionSurfaceWin::OnMakeCurrent(GLContext* context) {
return root_surface_->OnMakeCurrent(context);
}
bool DirectCompositionSurfaceWin::SupportsDCLayers() const {
return true;
}
bool DirectCompositionSurfaceWin::SupportsProtectedVideo() const {
// TODO(magchen): Check the gpu driver date (or a function) which we know this
// new support is enabled.
return DirectCompositionOverlaysSupported();
}
bool DirectCompositionSurfaceWin::SetDrawRectangle(const gfx::Rect& rect) {
return root_surface_->SetDrawRectangle(rect);
}
gfx::Vector2d DirectCompositionSurfaceWin::GetDrawOffset() const {
return root_surface_->GetDrawOffset();
}
bool DirectCompositionSurfaceWin::SupportsDelegatedInk() {
return layer_tree_->SupportsDelegatedInk();
}
void DirectCompositionSurfaceWin::SetDelegatedInkTrailStartPoint(
std::unique_ptr<gfx::DelegatedInkMetadata> metadata) {
layer_tree_->SetDelegatedInkTrailStartPoint(std::move(metadata));
}
void DirectCompositionSurfaceWin::InitDelegatedInkPointRendererReceiver(
mojo::PendingReceiver<gfx::mojom::DelegatedInkPointRenderer>
pending_receiver) {
layer_tree_->InitDelegatedInkPointRendererReceiver(
std::move(pending_receiver));
}
scoped_refptr<base::TaskRunner>
DirectCompositionSurfaceWin::GetWindowTaskRunnerForTesting() {
return child_window_.GetTaskRunnerForTesting();
}
Microsoft::WRL::ComPtr<IDXGISwapChain1>
DirectCompositionSurfaceWin::GetLayerSwapChainForTesting(size_t index) const {
return layer_tree_->GetLayerSwapChainForTesting(index);
}
Microsoft::WRL::ComPtr<IDXGISwapChain1>
DirectCompositionSurfaceWin::GetBackbufferSwapChainForTesting() const {
return root_surface_->swap_chain();
}
scoped_refptr<DirectCompositionChildSurfaceWin>
DirectCompositionSurfaceWin::GetRootSurfaceForTesting() const {
return root_surface_;
}
void DirectCompositionSurfaceWin::GetSwapChainVisualInfoForTesting(
size_t index,
gfx::Transform* transform,
gfx::Point* offset,
gfx::Rect* clip_rect) const {
layer_tree_->GetSwapChainVisualInfoForTesting( // IN-TEST
index, transform, offset, clip_rect);
}
void DirectCompositionSurfaceWin::HandleVSyncOnMainThread(
base::TimeTicks vsync_time,
base::TimeDelta interval) {
last_vsync_time_ = vsync_time;
last_vsync_interval_ = interval;
CheckPendingFrames();
}
void DirectCompositionSurfaceWin::StartOrStopVSyncThread() {
bool needs_vsync = !pending_frames_.empty();
if (observing_vsync_ == needs_vsync) {
return;
}
observing_vsync_ = needs_vsync;
if (needs_vsync) {
VSyncThreadWin::GetInstance()->AddObserver(this);
} else {
VSyncThreadWin::GetInstance()->RemoveObserver(this);
}
}
void DirectCompositionSurfaceWin::CheckPendingFrames() {
TRACE_EVENT1("gpu", "DirectCompositionSurfaceWin::CheckPendingFrames",
"num_pending_frames", pending_frames_.size());
if (pending_frames_.empty())
return;
Microsoft::WRL::ComPtr<ID3D11DeviceContext> context;
d3d11_device_->GetImmediateContext(&context);
while (!pending_frames_.empty()) {
auto& frame = pending_frames_.front();
// Query isn't created if there was no damage for previous frame.
if (frame.query) {
HRESULT hr = context->GetData(frame.query.Get(), nullptr, 0,
D3D11_ASYNC_GETDATA_DONOTFLUSH);
// When the GPU completes execution past the event query, GetData() will
// return S_OK, and S_FALSE otherwise. Do not use SUCCEEDED() because
// S_FALSE is also a success code.
if (hr != S_OK)
break;
}
std::move(frame.callback)
.Run(
gfx::PresentationFeedback(last_vsync_time_, last_vsync_interval_,
gfx::PresentationFeedback::kVSync |
gfx::PresentationFeedback::kHWClock));
pending_frames_.pop_front();
}
StartOrStopVSyncThread();
}
void DirectCompositionSurfaceWin::EnqueuePendingFrame(
PresentationCallback callback,
bool create_query) {
Microsoft::WRL::ComPtr<ID3D11Query> query;
if (create_query) {
D3D11_QUERY_DESC desc = {};
desc.Query = D3D11_QUERY_EVENT;
HRESULT hr = d3d11_device_->CreateQuery(&desc, &query);
if (SUCCEEDED(hr)) {
Microsoft::WRL::ComPtr<ID3D11DeviceContext> context;
d3d11_device_->GetImmediateContext(&context);
context->End(query.Get());
context->Flush();
} else {
DLOG(ERROR) << "CreateQuery failed with error 0x" << std::hex << hr;
}
}
pending_frames_.emplace_back(std::move(query), std::move(callback));
StartOrStopVSyncThread();
}
} // namespace gl