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 |