blob: a0a59cb62f55185e564e3dea7aba498272d156d8 [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 "ui/gl/dc_layer_tree.h"
#include "base/trace_event/trace_event.h"
#include "ui/gl/direct_composition_child_surface_win.h"
#include "ui/gl/swap_chain_presenter.h"
namespace gl {
namespace {
bool SizeContains(const gfx::Size& a, const gfx::Size& b) {
return gfx::Rect(a).Contains(gfx::Rect(b));
}
} // namespace
DCLayerTree::DCLayerTree(bool disable_nv12_dynamic_textures,
bool disable_larger_than_screen_overlays)
: disable_nv12_dynamic_textures_(disable_nv12_dynamic_textures),
disable_larger_than_screen_overlays_(
disable_larger_than_screen_overlays) {}
DCLayerTree::~DCLayerTree() = default;
bool DCLayerTree::Initialize(
HWND window,
Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device,
Microsoft::WRL::ComPtr<IDCompositionDevice2> dcomp_device) {
DCHECK(d3d11_device);
d3d11_device_ = std::move(d3d11_device);
DCHECK(dcomp_device);
dcomp_device_ = std::move(dcomp_device);
Microsoft::WRL::ComPtr<IDCompositionDesktopDevice> desktop_device;
dcomp_device_.As(&desktop_device);
DCHECK(desktop_device);
HRESULT hr =
desktop_device->CreateTargetForHwnd(window, TRUE, &dcomp_target_);
if (FAILED(hr)) {
DLOG(ERROR) << "CreateTargetForHwnd failed with error 0x" << std::hex << hr;
return false;
}
dcomp_device_->CreateVisual(&dcomp_root_visual_);
DCHECK(dcomp_root_visual_);
dcomp_target_->SetRoot(dcomp_root_visual_.Get());
// A visual inherits the interpolation mode of the parent visual by default.
// If no visuals set the interpolation mode, the default for the entire visual
// tree is nearest neighbor interpolation.
// Set the interpolation mode to Linear to get a better upscaling quality.
dcomp_root_visual_->SetBitmapInterpolationMode(
DCOMPOSITION_BITMAP_INTERPOLATION_MODE_LINEAR);
return true;
}
bool DCLayerTree::InitializeVideoProcessor(const gfx::Size& input_size,
const gfx::Size& output_size) {
if (!video_device_) {
// This can fail if the D3D device is "Microsoft Basic Display Adapter".
if (FAILED(d3d11_device_.As(&video_device_))) {
DLOG(ERROR) << "Failed to retrieve video device from D3D11 device";
return false;
}
DCHECK(video_device_);
Microsoft::WRL::ComPtr<ID3D11DeviceContext> context;
d3d11_device_->GetImmediateContext(&context);
DCHECK(context);
context.As(&video_context_);
DCHECK(video_context_);
}
if (video_processor_ && SizeContains(video_input_size_, input_size) &&
SizeContains(video_output_size_, output_size))
return true;
TRACE_EVENT2("gpu", "DCLayerTree::InitializeVideoProcessor", "input_size",
input_size.ToString(), "output_size", output_size.ToString());
video_input_size_ = input_size;
video_output_size_ = output_size;
video_processor_.Reset();
video_processor_enumerator_.Reset();
D3D11_VIDEO_PROCESSOR_CONTENT_DESC desc = {};
desc.InputFrameFormat = D3D11_VIDEO_FRAME_FORMAT_PROGRESSIVE;
desc.InputFrameRate.Numerator = 60;
desc.InputFrameRate.Denominator = 1;
desc.InputWidth = input_size.width();
desc.InputHeight = input_size.height();
desc.OutputFrameRate.Numerator = 60;
desc.OutputFrameRate.Denominator = 1;
desc.OutputWidth = output_size.width();
desc.OutputHeight = output_size.height();
desc.Usage = D3D11_VIDEO_USAGE_PLAYBACK_NORMAL;
HRESULT hr = video_device_->CreateVideoProcessorEnumerator(
&desc, &video_processor_enumerator_);
if (FAILED(hr)) {
DLOG(ERROR) << "CreateVideoProcessorEnumerator failed with error 0x"
<< std::hex << hr;
return false;
}
hr = video_device_->CreateVideoProcessor(video_processor_enumerator_.Get(), 0,
&video_processor_);
if (FAILED(hr)) {
DLOG(ERROR) << "CreateVideoProcessor failed with error 0x" << std::hex
<< hr;
return false;
}
// Auto stream processing (the default) can hurt power consumption.
video_context_->VideoProcessorSetStreamAutoProcessingMode(
video_processor_.Get(), 0, FALSE);
return true;
}
Microsoft::WRL::ComPtr<IDXGISwapChain1>
DCLayerTree::GetLayerSwapChainForTesting(size_t index) const {
if (index < video_swap_chains_.size())
return video_swap_chains_[index]->swap_chain();
return nullptr;
}
bool DCLayerTree::CommitAndClearPendingOverlays(
DirectCompositionChildSurfaceWin* root_surface) {
TRACE_EVENT1("gpu", "DCLayerTree::CommitAndClearPendingOverlays",
"num_pending_overlays", pending_overlays_.size());
DCHECK(!needs_commit_);
// Check if root surface visual needs a commit first.
if (!root_surface_visual_) {
dcomp_device_->CreateVisual(&root_surface_visual_);
needs_commit_ = true;
}
if (root_surface->swap_chain() != root_swap_chain_ ||
root_surface->dcomp_surface() != root_dcomp_surface_ ||
root_surface->dcomp_surface_serial() != root_dcomp_surface_serial_) {
root_swap_chain_ = root_surface->swap_chain();
root_dcomp_surface_ = root_surface->dcomp_surface();
root_dcomp_surface_serial_ = root_surface->dcomp_surface_serial();
root_surface_visual_->SetContent(
root_swap_chain_ ? static_cast<IUnknown*>(root_swap_chain_.Get())
: static_cast<IUnknown*>(root_dcomp_surface_.Get()));
needs_commit_ = true;
}
std::vector<std::unique_ptr<ui::DCRendererLayerParams>> overlays;
std::swap(pending_overlays_, overlays);
// Sort layers by z-order.
std::sort(overlays.begin(), overlays.end(),
[](const auto& a, const auto& b) -> bool {
return a->z_order < b->z_order;
});
// If we need to grow or shrink swap chain presenters, we'll need to add or
// remove visuals.
if (video_swap_chains_.size() != overlays.size()) {
// Grow or shrink list of swap chain presenters to match pending overlays.
std::vector<std::unique_ptr<SwapChainPresenter>> new_video_swap_chains;
for (size_t i = 0; i < overlays.size(); ++i) {
// TODO(sunnyps): Try to find a matching swap chain based on size, type of
// swap chain, gl image, etc.
if (i < video_swap_chains_.size()) {
new_video_swap_chains.emplace_back(std::move(video_swap_chains_[i]));
} else {
new_video_swap_chains.emplace_back(std::make_unique<SwapChainPresenter>(
this, d3d11_device_, dcomp_device_));
}
}
video_swap_chains_.swap(new_video_swap_chains);
needs_commit_ = true;
}
// Present to each swap chain.
for (size_t i = 0; i < overlays.size(); ++i) {
auto& video_swap_chain = video_swap_chains_[i];
if (!video_swap_chain->PresentToSwapChain(*overlays[i])) {
DLOG(ERROR) << "PresentToSwapChain failed";
return false;
}
}
// Rebuild visual tree and commit if any visual changed.
if (needs_commit_) {
TRACE_EVENT0("gpu", "DCLayerTree::CommitAndClearPendingOverlays::Commit");
needs_commit_ = false;
dcomp_root_visual_->RemoveAllVisuals();
// Add layers with negative z-order first.
size_t i = 0;
for (; i < overlays.size() && overlays[i]->z_order < 0; ++i) {
IDCompositionVisual2* visual = video_swap_chains_[i]->visual().Get();
// We call AddVisual with insertAbove FALSE and referenceVisual nullptr
// which is equivalent to saying that the visual should be below no other
// visual, or in other words it should be above all other visuals.
dcomp_root_visual_->AddVisual(visual, FALSE, nullptr);
}
// Add root surface visual at z-order 0.
dcomp_root_visual_->AddVisual(root_surface_visual_.Get(), FALSE, nullptr);
// Add visuals with positive z-order.
for (; i < overlays.size(); ++i) {
// There shouldn't be a layer with z-order 0. Otherwise, we can't tell
// its order with respect to root surface.
DCHECK_GT(overlays[i]->z_order, 0);
IDCompositionVisual2* visual = video_swap_chains_[i]->visual().Get();
dcomp_root_visual_->AddVisual(visual, FALSE, nullptr);
}
HRESULT hr = dcomp_device_->Commit();
if (FAILED(hr)) {
DLOG(ERROR) << "Commit failed with error 0x" << std::hex << hr;
return false;
}
}
return true;
}
bool DCLayerTree::ScheduleDCLayer(const ui::DCRendererLayerParams& params) {
pending_overlays_.push_back(
std::make_unique<ui::DCRendererLayerParams>(params));
return true;
}
} // namespace gl