// Copyright 2015 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 "content/common/gpu/child_window_surface_win.h" #include "base/compiler_specific.h" #include "base/win/scoped_hdc.h" #include "base/win/wrapped_window_proc.h" #include "content/common/gpu/gpu_channel_manager.h" #include "content/common/gpu/gpu_channel_manager_delegate.h" #include "content/common/gpu/gpu_messages.h" #include "ui/base/win/hidden_window.h" #include "ui/gfx/native_widget_types.h" #include "ui/gfx/win/hwnd_util.h" #include "ui/gl/egl_util.h" #include "ui/gl/gl_context.h" #include "ui/gl/gl_surface_egl.h" #include "ui/gl/scoped_make_current.h" namespace content { namespace { ATOM g_window_class; LRESULT CALLBACK IntermediateWindowProc(HWND window, UINT message, WPARAM w_param, LPARAM l_param) { switch (message) { case WM_ERASEBKGND: // Prevent windows from erasing the background. return 1; case WM_PAINT: PAINTSTRUCT paint; if (BeginPaint(window, &paint)) { ChildWindowSurfaceWin* window_surface = reinterpret_cast( gfx::GetWindowUserData(window)); DCHECK(window_surface); // Wait to clear the contents until a GL draw occurs, as otherwise an // unsightly black flash may happen if the GL contents are still // transparent. window_surface->InvalidateWindowRect(gfx::Rect(paint.rcPaint)); EndPaint(window, &paint); } return 0; default: return DefWindowProc(window, message, w_param, l_param); } } void InitializeWindowClass() { if (g_window_class) return; WNDCLASSEX intermediate_class; base::win::InitializeWindowClass( L"Intermediate D3D Window", &base::win::WrappedWindowProc, CS_OWNDC, 0, 0, nullptr, reinterpret_cast(GetStockObject(BLACK_BRUSH)), nullptr, nullptr, nullptr, &intermediate_class); g_window_class = RegisterClassEx(&intermediate_class); if (!g_window_class) { LOG(ERROR) << "RegisterClass failed."; return; } } } ChildWindowSurfaceWin::ChildWindowSurfaceWin(GpuChannelManager* manager, HWND parent_window) : gfx::NativeViewGLSurfaceEGL(0), parent_window_(parent_window), manager_(manager), alpha_(true) { // Don't use EGL_ANGLE_window_fixed_size so that we can avoid recreating the // window surface, which can cause flicker on DirectComposition. enable_fixed_size_angle_ = false; } EGLConfig ChildWindowSurfaceWin::GetConfig() { if (!config_) { int alpha_size = alpha_ ? 8 : EGL_DONT_CARE; EGLint config_attribs[] = {EGL_ALPHA_SIZE, alpha_size, EGL_BLUE_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_RED_SIZE, 8, EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL_SURFACE_TYPE, EGL_WINDOW_BIT | EGL_PBUFFER_BIT, EGL_NONE}; EGLDisplay display = GetHardwareDisplay(); EGLint num_configs; if (!eglChooseConfig(display, config_attribs, &config_, 1, &num_configs)) { LOG(ERROR) << "eglChooseConfig failed with error " << ui::GetLastEGLErrorString(); return NULL; } } return config_; } bool ChildWindowSurfaceWin::InitializeNativeWindow() { if (window_) return true; InitializeWindowClass(); DCHECK(g_window_class); RECT windowRect; GetClientRect(parent_window_, &windowRect); window_ = CreateWindowEx( WS_EX_NOPARENTNOTIFY, reinterpret_cast(g_window_class), L"", WS_CHILDWINDOW | WS_DISABLED | WS_VISIBLE, 0, 0, windowRect.right - windowRect.left, windowRect.bottom - windowRect.top, ui::GetHiddenWindow(), NULL, NULL, NULL); gfx::SetWindowUserData(window_, this); manager_->delegate()->SendAcceleratedSurfaceCreatedChildWindow(parent_window_, window_); return true; } bool ChildWindowSurfaceWin::Resize(const gfx::Size& size, float scale_factor, bool has_alpha) { if (!SupportsPostSubBuffer()) { if (!MoveWindow(window_, 0, 0, size.width(), size.height(), FALSE)) { return false; } alpha_ = has_alpha; return gfx::NativeViewGLSurfaceEGL::Resize(size, scale_factor, has_alpha); } else { if (size == GetSize() && has_alpha == alpha_) return true; // Force a resize and redraw (but not a move, activate, etc.). if (!SetWindowPos(window_, nullptr, 0, 0, size.width(), size.height(), SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOCOPYBITS | SWP_NOOWNERZORDER | SWP_NOZORDER)) { return false; } size_ = size; if (has_alpha == alpha_) { // A 0-size PostSubBuffer doesn't swap but forces the swap chain to resize // to match the window. PostSubBuffer(0, 0, 0, 0); } else { alpha_ = has_alpha; config_ = nullptr; scoped_ptr scoped_make_current; gfx::GLContext* current_context = gfx::GLContext::GetCurrent(); bool was_current = current_context && current_context->IsCurrent(this); if (was_current) { scoped_make_current.reset( new ui::ScopedMakeCurrent(current_context, this)); current_context->ReleaseCurrent(this); } Destroy(); if (!Initialize()) { LOG(ERROR) << "Failed to resize window."; return false; } } return true; } } gfx::SwapResult ChildWindowSurfaceWin::SwapBuffers() { gfx::SwapResult result = NativeViewGLSurfaceEGL::SwapBuffers(); ClearInvalidContents(); return result; } gfx::SwapResult ChildWindowSurfaceWin::PostSubBuffer(int x, int y, int width, int height) { gfx::SwapResult result = NativeViewGLSurfaceEGL::PostSubBuffer(x, y, width, height); ClearInvalidContents(); return result; } void ChildWindowSurfaceWin::InvalidateWindowRect(const gfx::Rect& rect) { rect_to_clear_.Union(rect); } void ChildWindowSurfaceWin::ClearInvalidContents() { if (!rect_to_clear_.IsEmpty()) { base::win::ScopedGetDC dc(window_); RECT rect = rect_to_clear_.ToRECT(); // DirectComposition composites with the contents under the SwapChain, // so ensure that's cleared. GDI treats black as transparent. FillRect(dc, &rect, reinterpret_cast(GetStockObject(BLACK_BRUSH))); rect_to_clear_ = gfx::Rect(); } } ChildWindowSurfaceWin::~ChildWindowSurfaceWin() { gfx::SetWindowUserData(window_, nullptr); DestroyWindow(window_); } } // namespace content