diff options
Diffstat (limited to 'chromium/ui/gl/gl_surface_wgl.cc')
-rw-r--r-- | chromium/ui/gl/gl_surface_wgl.cc | 433 |
1 files changed, 433 insertions, 0 deletions
diff --git a/chromium/ui/gl/gl_surface_wgl.cc b/chromium/ui/gl/gl_surface_wgl.cc new file mode 100644 index 00000000000..0bce93b5134 --- /dev/null +++ b/chromium/ui/gl/gl_surface_wgl.cc @@ -0,0 +1,433 @@ +// Copyright (c) 2012 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 "ui/gl/gl_surface_wgl.h" + +#include <memory> +#include <utility> + +#include "base/logging.h" +#include "base/trace_event/trace_event.h" +#include "ui/gl/gl_bindings.h" +#include "ui/gl/gl_context.h" +#include "ui/gl/gl_gl_api_implementation.h" +#include "ui/gl/gl_wgl_api_implementation.h" + +namespace gl { + +namespace { +const PIXELFORMATDESCRIPTOR kPixelFormatDescriptor = { + sizeof(kPixelFormatDescriptor), // Size of structure. + 1, // Default version. + PFD_DRAW_TO_WINDOW | // Window drawing support. + PFD_SUPPORT_OPENGL | // OpenGL support. + PFD_DOUBLEBUFFER, // Double buffering support (not stereo). + PFD_TYPE_RGBA, // RGBA color mode (not indexed). + 24, // 24 bit color mode. + 0, 0, 0, 0, 0, 0, // Don't set RGB bits & shifts. + 8, 0, // 8 bit alpha + 0, // No accumulation buffer. + 0, 0, 0, 0, // Ignore accumulation bits. + 0, // no z-buffer. + 0, // no stencil buffer. + 0, // No aux buffer. + PFD_MAIN_PLANE, // Main drawing plane (not overlay). + 0, // Reserved. + 0, 0, 0, // Layer masks ignored. +}; + +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: + // Do not paint anything. + PAINTSTRUCT paint; + if (BeginPaint(window, &paint)) + EndPaint(window, &paint); + return 0; + default: + return DefWindowProc(window, message, w_param, l_param); + } +} + +class DisplayWGL { + public: + DisplayWGL() + : module_handle_(0), + window_class_(0), + window_handle_(0), + device_context_(0), + pixel_format_(0) { + } + + ~DisplayWGL() { + if (window_handle_) + DestroyWindow(window_handle_); + if (window_class_) + UnregisterClass(reinterpret_cast<wchar_t*>(window_class_), + module_handle_); + } + + bool Init() { + // We must initialize a GL context before we can bind to extension entry + // points. This requires the device context for a window. + if (!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT | + GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, + reinterpret_cast<wchar_t*>(IntermediateWindowProc), + &module_handle_)) { + LOG(ERROR) << "GetModuleHandleEx failed."; + return false; + } + + WNDCLASS intermediate_class; + intermediate_class.style = CS_OWNDC; + intermediate_class.lpfnWndProc = IntermediateWindowProc; + intermediate_class.cbClsExtra = 0; + intermediate_class.cbWndExtra = 0; + intermediate_class.hInstance = module_handle_; + intermediate_class.hIcon = LoadIcon(NULL, IDI_APPLICATION); + intermediate_class.hCursor = LoadCursor(NULL, IDC_ARROW); + intermediate_class.hbrBackground = NULL; + intermediate_class.lpszMenuName = NULL; + intermediate_class.lpszClassName = L"Intermediate GL Window"; + window_class_ = RegisterClass(&intermediate_class); + if (!window_class_) { + LOG(ERROR) << "RegisterClass failed."; + return false; + } + + window_handle_ = CreateWindowEx(WS_EX_NOPARENTNOTIFY, + reinterpret_cast<wchar_t*>(window_class_), + L"", + WS_OVERLAPPEDWINDOW, + 0, + 0, + 100, + 100, + NULL, + NULL, + NULL, + NULL); + if (!window_handle_) { + LOG(ERROR) << "CreateWindow failed."; + return false; + } + + device_context_ = GetDC(window_handle_); + pixel_format_ = ChoosePixelFormat(device_context_, + &kPixelFormatDescriptor); + if (pixel_format_ == 0) { + LOG(ERROR) << "Unable to get the pixel format for GL context."; + return false; + } + if (!SetPixelFormat(device_context_, + pixel_format_, + &kPixelFormatDescriptor)) { + LOG(ERROR) << "Unable to set the pixel format for temporary GL context."; + return false; + } + + return true; + } + + ATOM window_class() const { return window_class_; } + HDC device_context() const { return device_context_; } + int pixel_format() const { return pixel_format_; } + + private: + HINSTANCE module_handle_; + ATOM window_class_; + HWND window_handle_; + HDC device_context_; + int pixel_format_; +}; +DisplayWGL* g_wgl_display; +} // namespace + +// static +bool GLSurfaceWGL::initialized_ = false; + +GLSurfaceWGL::GLSurfaceWGL() { +} + +GLSurfaceWGL::~GLSurfaceWGL() { +} + +void* GLSurfaceWGL::GetDisplay() { + return GetDisplayDC(); +} + +// static +bool GLSurfaceWGL::InitializeOneOff() { + if (initialized_) + return true; + + DCHECK(g_wgl_display == NULL); + std::unique_ptr<DisplayWGL> wgl_display(new DisplayWGL); + if (!wgl_display->Init()) + return false; + + g_wgl_display = wgl_display.release(); + initialized_ = true; + return true; +} + +// static +bool GLSurfaceWGL::InitializeExtensionSettingsOneOff() { + if (!initialized_) + return false; + g_driver_wgl.InitializeExtensionBindings(); + return true; +} + +void GLSurfaceWGL::InitializeOneOffForTesting() { + if (g_wgl_display == NULL) { + g_wgl_display = new DisplayWGL; + } +} + +HDC GLSurfaceWGL::GetDisplayDC() { + return g_wgl_display->device_context(); +} + +NativeViewGLSurfaceWGL::NativeViewGLSurfaceWGL(gfx::AcceleratedWidget window) + : window_(window), child_window_(NULL), device_context_(NULL) { + DCHECK(window); +} + +NativeViewGLSurfaceWGL::~NativeViewGLSurfaceWGL() { + Destroy(); +} + +bool NativeViewGLSurfaceWGL::Initialize(GLSurfaceFormat format) { + DCHECK(!device_context_); + + RECT rect; + if (!GetClientRect(window_, &rect)) { + LOG(ERROR) << "GetClientRect failed.\n"; + Destroy(); + return false; + } + + // Create a child window. WGL has problems using a window handle owned by + // another process. + child_window_ = CreateWindowEx( + WS_EX_NOPARENTNOTIFY, + reinterpret_cast<wchar_t*>(g_wgl_display->window_class()), L"", + WS_CHILDWINDOW | WS_DISABLED | WS_VISIBLE, 0, 0, rect.right - rect.left, + rect.bottom - rect.top, window_, NULL, NULL, NULL); + if (!child_window_) { + LOG(ERROR) << "CreateWindow failed.\n"; + Destroy(); + return false; + } + + // The GL context will render to this window. + device_context_ = GetDC(child_window_); + if (!device_context_) { + LOG(ERROR) << "Unable to get device context for window."; + Destroy(); + return false; + } + + if (!SetPixelFormat(device_context_, g_wgl_display->pixel_format(), + &kPixelFormatDescriptor)) { + LOG(ERROR) << "Unable to set the pixel format for GL context."; + Destroy(); + return false; + } + + format_ = format; + + return true; +} + +void NativeViewGLSurfaceWGL::Destroy() { + if (child_window_ && device_context_) + ReleaseDC(child_window_, device_context_); + + if (child_window_) + DestroyWindow(child_window_); + + child_window_ = NULL; + device_context_ = NULL; +} + +bool NativeViewGLSurfaceWGL::Resize(const gfx::Size& size, + float scale_factor, + const gfx::ColorSpace& color_space, + bool has_alpha) { + RECT rect; + if (!GetClientRect(window_, &rect)) { + LOG(ERROR) << "Failed to get parent window size."; + return false; + } + DCHECK(size.width() == (rect.right - rect.left) && + size.height() == (rect.bottom - rect.top)); + if (!MoveWindow(child_window_, 0, 0, size.width(), size.height(), FALSE)) { + LOG(ERROR) << "Failed to resize child window."; + return false; + } + return true; +} + +bool NativeViewGLSurfaceWGL::Recreate() { + Destroy(); + if (!Initialize(format_)) { + LOG(ERROR) << "Failed to create surface."; + return false; + } + return true; +} + +bool NativeViewGLSurfaceWGL::IsOffscreen() { + return false; +} + +gfx::SwapResult NativeViewGLSurfaceWGL::SwapBuffers( + PresentationCallback callback) { + // TODO(penghuang): Provide presentation feedback. https://crbug.com/776877 + TRACE_EVENT2("gpu", "NativeViewGLSurfaceWGL:RealSwapBuffers", + "width", GetSize().width(), + "height", GetSize().height()); + + // Resize the child window to match the parent before swapping. Do not repaint + // it as it moves. + RECT rect; + if (!GetClientRect(window_, &rect)) + return gfx::SwapResult::SWAP_FAILED; + if (!MoveWindow(child_window_, + 0, + 0, + rect.right - rect.left, + rect.bottom - rect.top, + FALSE)) { + return gfx::SwapResult::SWAP_FAILED; + } + + DCHECK(device_context_); + if (::SwapBuffers(device_context_) == TRUE) { + // TODO(penghuang): Provide more accurate values for presentation feedback. + constexpr int64_t kRefreshIntervalInMicroseconds = + base::Time::kMicrosecondsPerSecond / 60; + std::move(callback).Run(gfx::PresentationFeedback( + base::TimeTicks::Now(), + base::TimeDelta::FromMicroseconds(kRefreshIntervalInMicroseconds), + 0 /* flags */)); + return gfx::SwapResult::SWAP_ACK; + } else { + std::move(callback).Run(gfx::PresentationFeedback::Failure()); + return gfx::SwapResult::SWAP_FAILED; + } +} + +gfx::Size NativeViewGLSurfaceWGL::GetSize() { + RECT rect; + BOOL result = GetClientRect(child_window_, &rect); + DCHECK(result); + return gfx::Size(rect.right - rect.left, rect.bottom - rect.top); +} + +void* NativeViewGLSurfaceWGL::GetHandle() { + return device_context_; +} + +GLSurfaceFormat NativeViewGLSurfaceWGL::GetFormat() { + return GLSurfaceFormat(); +} + +void NativeViewGLSurfaceWGL::SetVSyncEnabled(bool enabled) { + DCHECK(GLContext::GetCurrent() && GLContext::GetCurrent()->IsCurrent(this)); + if (g_driver_wgl.ext.b_WGL_EXT_swap_control) { + wglSwapIntervalEXT(enabled ? 1 : 0); + } else { + LOG(WARNING) << "Could not disable vsync: driver does not " + "support WGL_EXT_swap_control"; + } +} + +PbufferGLSurfaceWGL::PbufferGLSurfaceWGL(const gfx::Size& size) + : size_(size), + device_context_(NULL), + pbuffer_(NULL) { + // Some implementations of Pbuffer do not support having a 0 size. For such + // cases use a (1, 1) surface. + if (size_.GetArea() == 0) + size_.SetSize(1, 1); +} + +PbufferGLSurfaceWGL::~PbufferGLSurfaceWGL() { + Destroy(); +} + +bool PbufferGLSurfaceWGL::Initialize(GLSurfaceFormat format) { + DCHECK(!device_context_); + + if (!g_driver_wgl.fn.wglCreatePbufferARBFn) { + LOG(ERROR) << "wglCreatePbufferARB not available."; + Destroy(); + return false; + } + + const int kNoAttributes[] = { 0 }; + pbuffer_ = wglCreatePbufferARB(g_wgl_display->device_context(), + g_wgl_display->pixel_format(), size_.width(), + size_.height(), kNoAttributes); + + if (!pbuffer_) { + LOG(ERROR) << "Unable to create pbuffer."; + Destroy(); + return false; + } + + device_context_ = wglGetPbufferDCARB(static_cast<HPBUFFERARB>(pbuffer_)); + if (!device_context_) { + LOG(ERROR) << "Unable to get pbuffer device context."; + Destroy(); + return false; + } + + return true; +} + +void PbufferGLSurfaceWGL::Destroy() { + if (pbuffer_ && device_context_) + wglReleasePbufferDCARB(static_cast<HPBUFFERARB>(pbuffer_), device_context_); + + device_context_ = NULL; + + if (pbuffer_) { + wglDestroyPbufferARB(static_cast<HPBUFFERARB>(pbuffer_)); + pbuffer_ = NULL; + } +} + +bool PbufferGLSurfaceWGL::IsOffscreen() { + return true; +} + +gfx::SwapResult PbufferGLSurfaceWGL::SwapBuffers( + PresentationCallback callback) { + NOTREACHED() << "Attempted to call SwapBuffers on a pbuffer."; + return gfx::SwapResult::SWAP_FAILED; +} + +gfx::Size PbufferGLSurfaceWGL::GetSize() { + return size_; +} + +void* PbufferGLSurfaceWGL::GetHandle() { + return device_context_; +} + +GLSurfaceFormat PbufferGLSurfaceWGL::GetFormat() { + return GLSurfaceFormat(); +} + +} // namespace gl |