| // 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 |