Chromium Code Reviews| Index: gpu/ipc/service/direct_composition_child_surface_win.cc |
| diff --git a/gpu/ipc/service/direct_composition_child_surface_win.cc b/gpu/ipc/service/direct_composition_child_surface_win.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..48f65af993ecd275999d7ce485e6c1c8d32e6f6b |
| --- /dev/null |
| +++ b/gpu/ipc/service/direct_composition_child_surface_win.cc |
| @@ -0,0 +1,315 @@ |
| +// Copyright 2017 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 "gpu/ipc/service/direct_composition_child_surface_win.h" |
| + |
| +#include <d3d11_1.h> |
| +#include <dcomptypes.h> |
| + |
| +#include "base/memory/ptr_util.h" |
| +#include "base/metrics/histogram_macros.h" |
| +#include "base/optional.h" |
| +#include "base/synchronization/waitable_event.h" |
| +#include "base/trace_event/trace_event.h" |
| +#include "ui/display/display_switches.h" |
| +#include "ui/gfx/native_widget_types.h" |
| +#include "ui/gl/egl_util.h" |
| +#include "ui/gl/gl_angle_util_win.h" |
| +#include "ui/gl/gl_context.h" |
| +#include "ui/gl/gl_surface_egl.h" |
| +#include "ui/gl/scoped_make_current.h" |
| + |
| +#ifndef EGL_ANGLE_flexible_surface_compatibility |
| +#define EGL_ANGLE_flexible_surface_compatibility 1 |
| +#define EGL_FLEXIBLE_SURFACE_COMPATIBILITY_SUPPORTED_ANGLE 0x33A6 |
| +#endif /* EGL_ANGLE_flexible_surface_compatibility */ |
| + |
| +#ifndef EGL_ANGLE_d3d_texture_client_buffer |
| +#define EGL_ANGLE_d3d_texture_client_buffer 1 |
| +#define EGL_D3D_TEXTURE_ANGLE 0x33A3 |
| +#endif /* EGL_ANGLE_d3d_texture_client_buffer */ |
| + |
| +namespace gpu { |
| + |
| +namespace { |
| +// Only one DirectComposition surface can be rendered into at a time. Track |
| +// here which IDCompositionSurface is being rendered into. If another context |
| +// is made current, then this surface will be suspended. |
| +IDCompositionSurface* g_current_surface; |
| + |
| +class ScopedReleaseCurrent { |
|
sunnyps
2017/06/10 00:11:11
nit: This is duplicated both here and in the root
|
| + public: |
| + explicit ScopedReleaseCurrent(gl::GLSurface* this_surface) { |
| + gl::GLContext* current_context = gl::GLContext::GetCurrent(); |
| + bool was_current = |
| + current_context && current_context->IsCurrent(this_surface); |
| + if (was_current) { |
| + make_current_.emplace(current_context, this_surface); |
| + current_context->ReleaseCurrent(this_surface); |
| + } |
| + } |
| + |
| + private: |
| + base::Optional<ui::ScopedMakeCurrent> make_current_; |
| +}; |
| +} |
| + |
| +DirectCompositionChildSurfaceWin::DirectCompositionChildSurfaceWin( |
| + const gfx::Size& size, |
| + bool has_alpha, |
| + bool enable_dc_layers) |
| + : gl::GLSurfaceEGL(), |
| + size_(size), |
| + has_alpha_(has_alpha), |
| + enable_dc_layers_(enable_dc_layers) {} |
| + |
| +DirectCompositionChildSurfaceWin::~DirectCompositionChildSurfaceWin() { |
| + Destroy(); |
| +} |
| + |
| +bool DirectCompositionChildSurfaceWin::Initialize(gl::GLSurfaceFormat format) { |
| + d3d11_device_ = gl::QueryD3D11DeviceObjectFromANGLE(); |
| + dcomp_device_ = gl::QueryDirectCompositionDevice(d3d11_device_); |
| + if (!dcomp_device_) |
| + return false; |
| + |
| + EGLDisplay display = GetDisplay(); |
| + |
| + std::vector<EGLint> pbuffer_attribs; |
| + pbuffer_attribs.push_back(EGL_WIDTH); |
| + pbuffer_attribs.push_back(1); |
| + pbuffer_attribs.push_back(EGL_HEIGHT); |
| + pbuffer_attribs.push_back(1); |
| + |
| + pbuffer_attribs.push_back(EGL_NONE); |
| + default_surface_ = |
| + eglCreatePbufferSurface(display, GetConfig(), &pbuffer_attribs[0]); |
| + CHECK(!!default_surface_); |
| + |
| + return true; |
| +} |
| + |
| +void DirectCompositionChildSurfaceWin::ReleaseCurrentSurface() { |
| + ReleaseDrawTexture(true); |
| + dcomp_surface_.Reset(); |
| + swap_chain_.Reset(); |
| +} |
| + |
| +void DirectCompositionChildSurfaceWin::InitializeSurface() { |
| + TRACE_EVENT1("gpu", "DirectCompositionChildSurfaceWin::InitializeSurface()", |
| + "enable_dc_layers_", enable_dc_layers_); |
| + DCHECK(!dcomp_surface_); |
| + DCHECK(!swap_chain_); |
| + DXGI_FORMAT output_format = |
| + base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnableHDR) |
| + ? DXGI_FORMAT_R16G16B16A16_FLOAT |
| + : DXGI_FORMAT_B8G8R8A8_UNORM; |
| + if (enable_dc_layers_) { |
| + // Always treat as premultiplied, because an underlay could cause it to |
| + // become transparent. |
| + HRESULT hr = dcomp_device_->CreateSurface( |
| + size_.width(), size_.height(), output_format, |
| + DXGI_ALPHA_MODE_PREMULTIPLIED, dcomp_surface_.GetAddressOf()); |
| + has_been_rendered_to_ = false; |
| + CHECK(SUCCEEDED(hr)); |
| + } else { |
| + DXGI_ALPHA_MODE alpha_mode = |
| + has_alpha_ ? DXGI_ALPHA_MODE_PREMULTIPLIED : DXGI_ALPHA_MODE_IGNORE; |
| + base::win::ScopedComPtr<IDXGIDevice> dxgi_device; |
| + d3d11_device_.CopyTo(dxgi_device.GetAddressOf()); |
| + base::win::ScopedComPtr<IDXGIAdapter> dxgi_adapter; |
| + dxgi_device->GetAdapter(dxgi_adapter.GetAddressOf()); |
| + base::win::ScopedComPtr<IDXGIFactory2> dxgi_factory; |
| + dxgi_adapter->GetParent(IID_PPV_ARGS(dxgi_factory.GetAddressOf())); |
| + |
| + DXGI_SWAP_CHAIN_DESC1 desc = {}; |
| + desc.Width = size_.width(); |
| + desc.Height = size_.height(); |
| + desc.Format = output_format; |
| + desc.Stereo = FALSE; |
| + desc.SampleDesc.Count = 1; |
| + desc.BufferCount = 2; |
| + desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; |
| + desc.Scaling = DXGI_SCALING_STRETCH; |
| + desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; |
| + desc.AlphaMode = alpha_mode; |
| + desc.Flags = 0; |
| + HRESULT hr = dxgi_factory->CreateSwapChainForComposition( |
| + d3d11_device_.Get(), &desc, nullptr, swap_chain_.GetAddressOf()); |
| + has_been_rendered_to_ = false; |
| + first_swap_ = true; |
| + CHECK(SUCCEEDED(hr)); |
| + } |
| +} |
| + |
| +void DirectCompositionChildSurfaceWin::ReleaseDrawTexture(bool will_discard) { |
| + if (real_surface_) { |
| + eglDestroySurface(GetDisplay(), real_surface_); |
| + real_surface_ = nullptr; |
| + } |
| + if (draw_texture_) { |
| + draw_texture_.Reset(); |
| + if (dcomp_surface_) { |
| + HRESULT hr = dcomp_surface_->EndDraw(); |
| + CHECK(SUCCEEDED(hr)); |
| + } else if (!will_discard) { |
| + DXGI_PRESENT_PARAMETERS params = {}; |
| + RECT dirty_rect = swap_rect_.ToRECT(); |
| + params.DirtyRectsCount = 1; |
| + params.pDirtyRects = &dirty_rect; |
| + swap_chain_->Present1(first_swap_ ? 0 : 1, 0, ¶ms); |
| + if (first_swap_) { |
| + // Wait for the GPU to finish executing its commands before |
| + // committing the DirectComposition tree, or else the swapchain |
| + // may flicker black when it's first presented. |
| + base::win::ScopedComPtr<IDXGIDevice2> dxgi_device2; |
| + HRESULT hr = d3d11_device_.CopyTo(dxgi_device2.GetAddressOf()); |
| + DCHECK(SUCCEEDED(hr)); |
| + base::WaitableEvent event( |
| + base::WaitableEvent::ResetPolicy::AUTOMATIC, |
| + base::WaitableEvent::InitialState::NOT_SIGNALED); |
| + dxgi_device2->EnqueueSetEvent(event.handle()); |
| + event.Wait(); |
| + first_swap_ = false; |
| + } |
| + } |
| + } |
| + if (dcomp_surface_ == g_current_surface) |
| + g_current_surface = nullptr; |
| +} |
| + |
| +void DirectCompositionChildSurfaceWin::Destroy() { |
| + if (default_surface_) { |
| + if (!eglDestroySurface(GetDisplay(), default_surface_)) { |
| + DLOG(ERROR) << "eglDestroySurface failed with error " |
| + << ui::GetLastEGLErrorString(); |
| + } |
| + default_surface_ = nullptr; |
| + } |
| + if (real_surface_) { |
| + if (!eglDestroySurface(GetDisplay(), real_surface_)) { |
| + DLOG(ERROR) << "eglDestroySurface failed with error " |
| + << ui::GetLastEGLErrorString(); |
| + } |
| + real_surface_ = nullptr; |
| + } |
| + if (dcomp_surface_ && (dcomp_surface_ == g_current_surface)) { |
| + HRESULT hr = dcomp_surface_->EndDraw(); |
| + CHECK(SUCCEEDED(hr)); |
| + g_current_surface = nullptr; |
| + } |
| + draw_texture_.Reset(); |
| + dcomp_surface_.Reset(); |
| +} |
| + |
| +gfx::Size DirectCompositionChildSurfaceWin::GetSize() { |
| + return size_; |
| +} |
| + |
| +bool DirectCompositionChildSurfaceWin::IsOffscreen() { |
| + return false; |
| +} |
| + |
| +void* DirectCompositionChildSurfaceWin::GetHandle() { |
| + return real_surface_ ? real_surface_ : default_surface_; |
| +} |
| + |
| +gfx::SwapResult DirectCompositionChildSurfaceWin::SwapBuffers() { |
| + ReleaseDrawTexture(false); |
| + return gfx::SwapResult::SWAP_ACK; |
| +} |
| + |
| +bool DirectCompositionChildSurfaceWin::FlipsVertically() const { |
| + return true; |
| +} |
| + |
| +bool DirectCompositionChildSurfaceWin::SupportsPostSubBuffer() { |
| + return true; |
| +} |
| + |
| +bool DirectCompositionChildSurfaceWin::OnMakeCurrent(gl::GLContext* context) { |
| + if (g_current_surface != dcomp_surface_) { |
| + if (g_current_surface) { |
| + HRESULT hr = g_current_surface->SuspendDraw(); |
| + CHECK(SUCCEEDED(hr)); |
| + g_current_surface = nullptr; |
| + } |
| + if (draw_texture_) { |
| + HRESULT hr = dcomp_surface_->ResumeDraw(); |
| + CHECK(SUCCEEDED(hr)); |
| + g_current_surface = dcomp_surface_.Get(); |
| + } |
| + } |
| + return true; |
| +} |
| + |
| +bool DirectCompositionChildSurfaceWin::SupportsDCLayers() const { |
| + return true; |
| +} |
| + |
| +bool DirectCompositionChildSurfaceWin::SetDrawRectangle( |
| + const gfx::Rect& rectangle) { |
| + if (draw_texture_) |
| + return false; |
| + DCHECK(!real_surface_); |
| + ScopedReleaseCurrent release_current(this); |
| + |
| + if ((enable_dc_layers_ && !dcomp_surface_) || |
| + (!enable_dc_layers_ && !swap_chain_)) { |
| + ReleaseCurrentSurface(); |
| + InitializeSurface(); |
| + } |
| + |
| + if (!gfx::Rect(size_).Contains(rectangle)) { |
| + DLOG(ERROR) << "Draw rectangle must be contained within size of surface"; |
| + return false; |
| + } |
| + if (gfx::Rect(size_) != rectangle && !has_been_rendered_to_) { |
| + DLOG(ERROR) << "First draw to surface must draw to everything"; |
| + return false; |
| + } |
| + |
| + CHECK(!g_current_surface); |
| + |
| + RECT rect = rectangle.ToRECT(); |
| + if (dcomp_surface_) { |
| + POINT update_offset; |
| + HRESULT hr = dcomp_surface_->BeginDraw( |
| + &rect, IID_PPV_ARGS(draw_texture_.GetAddressOf()), &update_offset); |
| + draw_offset_ = gfx::Point(update_offset) - gfx::Rect(rect).origin(); |
| + CHECK(SUCCEEDED(hr)); |
| + } else { |
| + HRESULT hr = |
| + swap_chain_->GetBuffer(0, IID_PPV_ARGS(draw_texture_.GetAddressOf())); |
| + swap_rect_ = rectangle; |
| + draw_offset_ = gfx::Vector2d(); |
| + CHECK(SUCCEEDED(hr)); |
| + } |
| + has_been_rendered_to_ = true; |
| + |
| + g_current_surface = dcomp_surface_.Get(); |
| + |
| + std::vector<EGLint> pbuffer_attribs{ |
| + EGL_WIDTH, |
| + size_.width(), |
| + EGL_HEIGHT, |
| + size_.height(), |
| + EGL_FLEXIBLE_SURFACE_COMPATIBILITY_SUPPORTED_ANGLE, |
| + EGL_TRUE, |
| + EGL_NONE}; |
| + |
| + EGLClientBuffer buffer = |
| + reinterpret_cast<EGLClientBuffer>(draw_texture_.Get()); |
| + real_surface_ = eglCreatePbufferFromClientBuffer( |
| + GetDisplay(), EGL_D3D_TEXTURE_ANGLE, buffer, GetConfig(), |
| + &pbuffer_attribs[0]); |
| + |
| + return true; |
| +} |
| + |
| +gfx::Vector2d DirectCompositionChildSurfaceWin::GetDrawOffset() const { |
| + return draw_offset_; |
| +} |
| + |
| +} // namespace gpu |