blob: 762fbd117391233704e07375d64474912e305e1c [file] [log] [blame]
// 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 "gpu/ipc/service/child_window_surface_win.h"
#include <memory>
#include "base/compiler_specific.h"
#include "base/win/scoped_hdc.h"
#include "base/win/wrapped_window_proc.h"
#include "gpu/ipc/common/gpu_messages.h"
#include "gpu/ipc/service/gpu_channel_manager.h"
#include "gpu/ipc/service/gpu_channel_manager_delegate.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 gpu {
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<ChildWindowSurfaceWin*>(
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<IntermediateWindowProc>, CS_OWNDC, 0, 0,
nullptr, reinterpret_cast<HBRUSH>(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)
: gl::NativeViewGLSurfaceEGL(0),
parent_window_(parent_window),
manager_(manager),
alpha_(true),
first_swap_(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<wchar_t*>(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 gl::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;
std::unique_ptr<ui::ScopedMakeCurrent> scoped_make_current;
gl::GLContext* current_context = gl::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();
// 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.
if (first_swap_) {
glFinish();
first_swap_ = false;
}
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<HBRUSH>(GetStockObject(BLACK_BRUSH)));
rect_to_clear_ = gfx::Rect();
}
}
ChildWindowSurfaceWin::~ChildWindowSurfaceWin() {
gfx::SetWindowUserData(window_, nullptr);
DestroyWindow(window_);
}
} // namespace gpu