Chromium Code Reviews| Index: gpu/ipc/service/direct_composition_surface_win.cc |
| diff --git a/gpu/ipc/service/direct_composition_surface_win.cc b/gpu/ipc/service/direct_composition_surface_win.cc |
| index d4255619863f407e078d8a830607130954966946..4f80705405b5763b334b53aa9d30c82d78dd54c4 100644 |
| --- a/gpu/ipc/service/direct_composition_surface_win.cc |
| +++ b/gpu/ipc/service/direct_composition_surface_win.cc |
| @@ -4,14 +4,53 @@ |
| #include "gpu/ipc/service/direct_composition_surface_win.h" |
| +#include "base/optional.h" |
| #include "gpu/ipc/service/gpu_channel_manager.h" |
| #include "gpu/ipc/service/gpu_channel_manager_delegate.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 { |
| + |
| +// This class is used to make sure a specified surface isn't current, and upon |
| +// destruction it will make the surface current again if it had been before. |
| +class ScopedReleaseCurrent { |
| + public: |
| + explicit ScopedReleaseCurrent(gl::GLSurface* this_surface) { |
| + gl::GLContext* current_context = gl::GLContext::GetCurrent(); |
| + bool was_current = |
| + current_context && current_context->IsCurrent(this_surface); |
|
sunnyps
2017/01/28 01:33:59
nit: DCHECK(current_context->IsCurrent(this_surfac
|
| + if (was_current) { |
| + make_current_.emplace(current_context, this_surface); |
| + current_context->ReleaseCurrent(this_surface); |
| + } |
| + } |
| + |
| + private: |
| + base::Optional<ui::ScopedMakeCurrent> make_current_; |
| +}; |
| + |
| +// 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; |
| + |
| +} // namespace |
| DirectCompositionSurfaceWin::DirectCompositionSurfaceWin( |
| base::WeakPtr<ImageTransportSurfaceDelegate> delegate, |
| @@ -19,7 +58,9 @@ DirectCompositionSurfaceWin::DirectCompositionSurfaceWin( |
| : gl::GLSurfaceEGL(), |
| child_window_(delegate, parent_window), |
| window_(nullptr), |
| - surface_(0), |
| + default_surface_(0), |
| + real_surface_(0), |
| + size_(1, 1), |
| first_swap_(true) {} |
| bool DirectCompositionSurfaceWin::InitializeNativeWindow() { |
| @@ -32,6 +73,11 @@ bool DirectCompositionSurfaceWin::InitializeNativeWindow() { |
| } |
| bool DirectCompositionSurfaceWin::Initialize(gl::GLSurfaceFormat format) { |
| + d3d11_device_ = gl::QueryD3D11DeviceObjectFromANGLE(); |
| + dcomp_device_ = gl::QueryDirectCompositionDevice(d3d11_device_); |
| + if (!dcomp_device_) |
| + return false; |
| + |
| EGLDisplay display = GetDisplay(); |
| if (!window_) { |
| if (!InitializeNativeWindow()) { |
| @@ -39,6 +85,21 @@ bool DirectCompositionSurfaceWin::Initialize(gl::GLSurfaceFormat format) { |
| return false; |
| } |
| } |
| + |
| + base::win::ScopedComPtr<IDCompositionDesktopDevice> desktop_device; |
| + dcomp_device_.QueryInterface(desktop_device.Receive()); |
| + |
| + HRESULT hr = desktop_device->CreateTargetForHwnd(window_, TRUE, |
| + dcomp_target_.Receive()); |
| + if (FAILED(hr)) |
| + return false; |
| + |
| + hr = dcomp_device_->CreateVisual(visual_.Receive()); |
| + if (FAILED(hr)) |
| + return false; |
| + |
| + dcomp_target_->SetRoot(visual_.get()); |
| + |
| std::vector<EGLint> pbuffer_attribs; |
| pbuffer_attribs.push_back(EGL_WIDTH); |
| pbuffer_attribs.push_back(1); |
| @@ -46,20 +107,34 @@ bool DirectCompositionSurfaceWin::Initialize(gl::GLSurfaceFormat format) { |
| pbuffer_attribs.push_back(1); |
| pbuffer_attribs.push_back(EGL_NONE); |
| - surface_ = eglCreatePbufferSurface(display, GetConfig(), &pbuffer_attribs[0]); |
| - CHECK(!!surface_); |
| + default_surface_ = |
| + eglCreatePbufferSurface(display, GetConfig(), &pbuffer_attribs[0]); |
| + CHECK(!!default_surface_); |
| + |
| + InitializeSurface(); |
| return true; |
| } |
| void DirectCompositionSurfaceWin::Destroy() { |
| - if (surface_) { |
| - if (!eglDestroySurface(GetDisplay(), surface_)) { |
| + if (default_surface_) { |
| + if (!eglDestroySurface(GetDisplay(), default_surface_)) { |
| + LOG(ERROR) << "eglDestroySurface failed with error " |
| + << ui::GetLastEGLErrorString(); |
| + } |
| + default_surface_ = NULL; |
| + } |
| + if (real_surface_) { |
| + if (!eglDestroySurface(GetDisplay(), real_surface_)) { |
| LOG(ERROR) << "eglDestroySurface failed with error " |
| << ui::GetLastEGLErrorString(); |
| } |
| - surface_ = NULL; |
| + real_surface_ = NULL; |
| } |
| + if (dcomp_surface_ == g_current_surface) |
| + g_current_surface = nullptr; |
| + draw_texture_.Release(); |
| + dcomp_surface_.Release(); |
| } |
| gfx::Size DirectCompositionSurfaceWin::GetSize() { |
| @@ -71,7 +146,7 @@ bool DirectCompositionSurfaceWin::IsOffscreen() { |
| } |
| void* DirectCompositionSurfaceWin::GetHandle() { |
| - return surface_; |
| + return real_surface_ ? real_surface_ : default_surface_; |
| } |
| bool DirectCompositionSurfaceWin::Resize(const gfx::Size& size, |
| @@ -87,11 +162,20 @@ bool DirectCompositionSurfaceWin::Resize(const gfx::Size& size, |
| return false; |
| } |
| size_ = size; |
| + InitializeSurface(); |
| + |
| return true; |
| } |
| gfx::SwapResult DirectCompositionSurfaceWin::SwapBuffers() { |
| - CommitAndClearPendingOverlays(); |
| + { |
| + ScopedReleaseCurrent release_current(this); |
| + ReleaseDrawTexture(); |
| + visual_->SetContent(dcomp_surface_.get()); |
| + |
| + CommitAndClearPendingOverlays(); |
| + dcomp_device_->Commit(); |
| + } |
| // Force the driver to finish drawing before clearing the contents to |
| // transparent, to reduce or eliminate the period of time where the contents |
| // have flashed black. |
| @@ -107,7 +191,11 @@ gfx::SwapResult DirectCompositionSurfaceWin::PostSubBuffer(int x, |
| int y, |
| int width, |
| int height) { |
| + ScopedReleaseCurrent release_current(this); |
| + ReleaseDrawTexture(); |
| + visual_->SetContent(dcomp_surface_.get()); |
| CommitAndClearPendingOverlays(); |
| + dcomp_device_->Commit(); |
| child_window_.ClearInvalidContents(); |
| return gfx::SwapResult::SWAP_ACK; |
| } |
| @@ -133,10 +221,79 @@ bool DirectCompositionSurfaceWin::ScheduleOverlayPlane( |
| return true; |
| } |
| +bool DirectCompositionSurfaceWin::FlipsVertically() const { |
| + return true; |
| +} |
| + |
| bool DirectCompositionSurfaceWin::SupportsPostSubBuffer() { |
| return true; |
| } |
| +bool DirectCompositionSurfaceWin::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 DirectCompositionSurfaceWin::SupportsSetDrawRectangle() const { |
| + return true; |
| +} |
| + |
| +bool DirectCompositionSurfaceWin::SetDrawRectangle(const gfx::Rect& rectangle) { |
| + if (draw_texture_) |
| + return false; |
| + if (!gfx::Rect(size_).Contains(rectangle)) { |
| + LOG(ERROR) << "Draw rectangle must be contained within size of surface"; |
| + return false; |
| + } |
| + if (gfx::Rect(size_) != rectangle && !has_been_rendered_to_) { |
| + LOG(ERROR) << "First draw to surface must draw to everything"; |
| + return false; |
| + } |
| + |
| + DCHECK(!real_surface_); |
| + CHECK(!g_current_surface); |
| + ScopedReleaseCurrent release_current(this); |
| + |
| + RECT rect = rectangle.ToRECT(); |
| + // TODO(jbauman): Use update_offset |
| + POINT update_offset; |
| + |
| + HRESULT hr = dcomp_surface_->BeginDraw( |
| + &rect, IID_PPV_ARGS(draw_texture_.Receive()), &update_offset); |
| + CHECK(SUCCEEDED(hr)); |
| + has_been_rendered_to_ = true; |
| + |
| + g_current_surface = dcomp_surface_.get(); |
| + |
| + std::vector<EGLint> pbuffer_attribs; |
| + pbuffer_attribs.push_back(EGL_WIDTH); |
| + pbuffer_attribs.push_back(size_.width()); |
| + pbuffer_attribs.push_back(EGL_HEIGHT); |
| + pbuffer_attribs.push_back(size_.height()); |
| + pbuffer_attribs.push_back(EGL_FLEXIBLE_SURFACE_COMPATIBILITY_SUPPORTED_ANGLE); |
| + pbuffer_attribs.push_back(EGL_TRUE); |
| + pbuffer_attribs.push_back(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; |
| +} |
| + |
| DirectCompositionSurfaceWin::Overlay::Overlay(int z_order, |
| gfx::OverlayTransform transform, |
| scoped_refptr<gl::GLImage> image, |
| @@ -157,6 +314,34 @@ bool DirectCompositionSurfaceWin::CommitAndClearPendingOverlays() { |
| return true; |
| } |
| -DirectCompositionSurfaceWin::~DirectCompositionSurfaceWin() {} |
| +void DirectCompositionSurfaceWin::InitializeSurface() { |
|
sunnyps
2017/01/28 01:33:59
nit: can you move this and ReleaseDrawTexture next
|
| + ScopedReleaseCurrent release_current(this); |
| + ReleaseDrawTexture(); |
| + dcomp_surface_.Release(); |
| + HRESULT hr = dcomp_device_->CreateSurface( |
| + size_.width(), size_.height(), DXGI_FORMAT_B8G8R8A8_UNORM, |
| + DXGI_ALPHA_MODE_PREMULTIPLIED, dcomp_surface_.Receive()); |
| + has_been_rendered_to_ = false; |
| + |
| + CHECK(SUCCEEDED(hr)); |
| +} |
| + |
| +void DirectCompositionSurfaceWin::ReleaseDrawTexture() { |
| + if (real_surface_) { |
| + eglDestroySurface(GetDisplay(), real_surface_); |
| + real_surface_ = nullptr; |
| + } |
| + if (draw_texture_) { |
| + draw_texture_.Release(); |
| + HRESULT hr = dcomp_surface_->EndDraw(); |
| + CHECK(SUCCEEDED(hr)); |
| + } |
| + if (dcomp_surface_ == g_current_surface) |
| + g_current_surface = nullptr; |
| +} |
| + |
| +DirectCompositionSurfaceWin::~DirectCompositionSurfaceWin() { |
|
sunnyps
2017/01/28 01:33:59
nit: can you move this next to the ctor?
|
| + Destroy(); |
| +} |
| } // namespace gpu |