14a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com// Copyright (c) 2012 The Chromium Authors. All rights reserved. 24a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com// Use of this source code is governed by a BSD-style license that can be 34a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com// found in the LICENSE file. 44a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com 54a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com#include "ui/gl/gl_surface_wgl.h" 64a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com 74a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com#include "base/debug/trace_event.h" 84a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com#include "base/logging.h" 94a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com#include "base/memory/scoped_ptr.h" 104a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com#include "ui/gl/gl_bindings.h" 114a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com#include "ui/gl/gl_gl_api_implementation.h" 124a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com#include "ui/gl/gl_wgl_api_implementation.h" 134a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com 144a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.comnamespace gfx { 154a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com 164a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.comnamespace { 174a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.comconst PIXELFORMATDESCRIPTOR kPixelFormatDescriptor = { 184a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com sizeof(kPixelFormatDescriptor), // Size of structure. 194a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com 1, // Default version. 204a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com PFD_DRAW_TO_WINDOW | // Window drawing support. 214a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com PFD_SUPPORT_OPENGL | // OpenGL support. 224a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com PFD_DOUBLEBUFFER, // Double buffering support (not stereo). 234a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com PFD_TYPE_RGBA, // RGBA color mode (not indexed). 244a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com 24, // 24 bit color mode. 254a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com 0, 0, 0, 0, 0, 0, // Don't set RGB bits & shifts. 264a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com 8, 0, // 8 bit alpha 274a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com 0, // No accumulation buffer. 284a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com 0, 0, 0, 0, // Ignore accumulation bits. 294a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com 0, // no z-buffer. 304a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com 0, // no stencil buffer. 314a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com 0, // No aux buffer. 324a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com PFD_MAIN_PLANE, // Main drawing plane (not overlay). 334a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com 0, // Reserved. 344a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com 0, 0, 0, // Layer masks ignored. 354a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com}; 364a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com 374a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.comLRESULT CALLBACK IntermediateWindowProc(HWND window, 384a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com UINT message, 394a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com WPARAM w_param, 404a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com LPARAM l_param) { 414a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com switch (message) { 424a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com case WM_ERASEBKGND: 434a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com // Prevent windows from erasing the background. 444a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com return 1; 454a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com case WM_PAINT: 464a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com // Do not paint anything. 474a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com PAINTSTRUCT paint; 484a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com if (BeginPaint(window, &paint)) 49 EndPaint(window, &paint); 50 return 0; 51 default: 52 return DefWindowProc(window, message, w_param, l_param); 53 } 54} 55 56class DisplayWGL { 57 public: 58 DisplayWGL() 59 : module_handle_(0), 60 window_class_(0), 61 window_handle_(0), 62 device_context_(0), 63 pixel_format_(0) { 64 } 65 66 ~DisplayWGL() { 67 if (window_handle_) 68 DestroyWindow(window_handle_); 69 if (window_class_) 70 UnregisterClass(reinterpret_cast<wchar_t*>(window_class_), 71 module_handle_); 72 } 73 74 bool Init() { 75 // We must initialize a GL context before we can bind to extension entry 76 // points. This requires the device context for a window. 77 if (!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT | 78 GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, 79 reinterpret_cast<wchar_t*>(IntermediateWindowProc), 80 &module_handle_)) { 81 LOG(ERROR) << "GetModuleHandleEx failed."; 82 return false; 83 } 84 85 WNDCLASS intermediate_class; 86 intermediate_class.style = CS_OWNDC; 87 intermediate_class.lpfnWndProc = IntermediateWindowProc; 88 intermediate_class.cbClsExtra = 0; 89 intermediate_class.cbWndExtra = 0; 90 intermediate_class.hInstance = module_handle_; 91 intermediate_class.hIcon = LoadIcon(NULL, IDI_APPLICATION); 92 intermediate_class.hCursor = LoadCursor(NULL, IDC_ARROW); 93 intermediate_class.hbrBackground = NULL; 94 intermediate_class.lpszMenuName = NULL; 95 intermediate_class.lpszClassName = L"Intermediate GL Window"; 96 window_class_ = RegisterClass(&intermediate_class); 97 if (!window_class_) { 98 LOG(ERROR) << "RegisterClass failed."; 99 return false; 100 } 101 102 window_handle_ = CreateWindow( 103 reinterpret_cast<wchar_t*>(window_class_), 104 L"", 105 WS_OVERLAPPEDWINDOW, 106 0, 0, 107 100, 100, 108 NULL, 109 NULL, 110 NULL, 111 NULL); 112 if (!window_handle_) { 113 LOG(ERROR) << "CreateWindow failed."; 114 return false; 115 } 116 117 device_context_ = GetDC(window_handle_); 118 pixel_format_ = ChoosePixelFormat(device_context_, 119 &kPixelFormatDescriptor); 120 if (pixel_format_ == 0) { 121 LOG(ERROR) << "Unable to get the pixel format for GL context."; 122 return false; 123 } 124 if (!SetPixelFormat(device_context_, 125 pixel_format_, 126 &kPixelFormatDescriptor)) { 127 LOG(ERROR) << "Unable to set the pixel format for temporary GL context."; 128 return false; 129 } 130 131 return true; 132 } 133 134 ATOM window_class() const { return window_class_; } 135 HDC device_context() const { return device_context_; } 136 int pixel_format() const { return pixel_format_; } 137 138 private: 139 HINSTANCE module_handle_; 140 ATOM window_class_; 141 HWND window_handle_; 142 HDC device_context_; 143 int pixel_format_; 144}; 145DisplayWGL* g_display; 146} // namespace 147 148GLSurfaceWGL::GLSurfaceWGL() { 149} 150 151GLSurfaceWGL::~GLSurfaceWGL() { 152} 153 154void* GLSurfaceWGL::GetDisplay() { 155 return GetDisplayDC(); 156} 157 158bool GLSurfaceWGL::InitializeOneOff() { 159 static bool initialized = false; 160 if (initialized) 161 return true; 162 163 DCHECK(g_display == NULL); 164 scoped_ptr<DisplayWGL> wgl_display(new DisplayWGL); 165 if (!wgl_display->Init()) 166 return false; 167 168 g_display = wgl_display.release(); 169 initialized = true; 170 return true; 171} 172 173HDC GLSurfaceWGL::GetDisplayDC() { 174 return g_display->device_context(); 175} 176 177NativeViewGLSurfaceWGL::NativeViewGLSurfaceWGL(gfx::AcceleratedWidget window) 178 : window_(window), 179 child_window_(NULL), 180 device_context_(NULL) { 181 DCHECK(window); 182} 183 184NativeViewGLSurfaceWGL::~NativeViewGLSurfaceWGL() { 185 Destroy(); 186} 187 188bool NativeViewGLSurfaceWGL::Initialize() { 189 DCHECK(!device_context_); 190 191 RECT rect; 192 if (!GetClientRect(window_, &rect)) { 193 LOG(ERROR) << "GetClientRect failed.\n"; 194 Destroy(); 195 return false; 196 } 197 198 // Create a child window. WGL has problems using a window handle owned by 199 // another process. 200 child_window_ = CreateWindow( 201 reinterpret_cast<wchar_t*>(g_display->window_class()), 202 L"", 203 WS_CHILDWINDOW | WS_DISABLED | WS_VISIBLE, 204 0, 0, 205 rect.right - rect.left, 206 rect.bottom - rect.top, 207 window_, 208 NULL, 209 NULL, 210 NULL); 211 if (!child_window_) { 212 LOG(ERROR) << "CreateWindow failed.\n"; 213 Destroy(); 214 return false; 215 } 216 217 // The GL context will render to this window. 218 device_context_ = GetDC(child_window_); 219 if (!device_context_) { 220 LOG(ERROR) << "Unable to get device context for window."; 221 Destroy(); 222 return false; 223 } 224 225 if (!SetPixelFormat(device_context_, 226 g_display->pixel_format(), 227 &kPixelFormatDescriptor)) { 228 LOG(ERROR) << "Unable to set the pixel format for GL context."; 229 Destroy(); 230 return false; 231 } 232 233 return true; 234} 235 236void NativeViewGLSurfaceWGL::Destroy() { 237 if (child_window_ && device_context_) 238 ReleaseDC(child_window_, device_context_); 239 240 if (child_window_) 241 DestroyWindow(child_window_); 242 243 child_window_ = NULL; 244 device_context_ = NULL; 245} 246 247bool NativeViewGLSurfaceWGL::IsOffscreen() { 248 return false; 249} 250 251bool NativeViewGLSurfaceWGL::SwapBuffers() { 252 TRACE_EVENT2("gpu", "NativeViewGLSurfaceWGL:RealSwapBuffers", 253 "width", GetSize().width(), 254 "height", GetSize().height()); 255 256 // Resize the child window to match the parent before swapping. Do not repaint 257 // it as it moves. 258 RECT rect; 259 if (!GetClientRect(window_, &rect)) 260 return false; 261 if (!MoveWindow(child_window_, 262 0, 0, 263 rect.right - rect.left, 264 rect.bottom - rect.top, 265 FALSE)) { 266 return false; 267 } 268 269 DCHECK(device_context_); 270 return ::SwapBuffers(device_context_) == TRUE; 271} 272 273gfx::Size NativeViewGLSurfaceWGL::GetSize() { 274 RECT rect; 275 BOOL result = GetClientRect(child_window_, &rect); 276 DCHECK(result); 277 return gfx::Size(rect.right - rect.left, rect.bottom - rect.top); 278} 279 280void* NativeViewGLSurfaceWGL::GetHandle() { 281 return device_context_; 282} 283 284PbufferGLSurfaceWGL::PbufferGLSurfaceWGL(const gfx::Size& size) 285 : size_(size), 286 device_context_(NULL), 287 pbuffer_(NULL) { 288 // Some implementations of Pbuffer do not support having a 0 size. For such 289 // cases use a (1, 1) surface. 290 if (size_.GetArea() == 0) 291 size_.SetSize(1, 1); 292} 293 294PbufferGLSurfaceWGL::~PbufferGLSurfaceWGL() { 295 Destroy(); 296} 297 298bool PbufferGLSurfaceWGL::Initialize() { 299 DCHECK(!device_context_); 300 301 if (!gfx::g_driver_wgl.fn.wglCreatePbufferARBFn) { 302 LOG(ERROR) << "wglCreatePbufferARB not available."; 303 Destroy(); 304 return false; 305 } 306 307 const int kNoAttributes[] = { 0 }; 308 pbuffer_ = wglCreatePbufferARB(g_display->device_context(), 309 g_display->pixel_format(), 310 size_.width(), size_.height(), 311 kNoAttributes); 312 313 if (!pbuffer_) { 314 LOG(ERROR) << "Unable to create pbuffer."; 315 Destroy(); 316 return false; 317 } 318 319 device_context_ = wglGetPbufferDCARB(static_cast<HPBUFFERARB>(pbuffer_)); 320 if (!device_context_) { 321 LOG(ERROR) << "Unable to get pbuffer device context."; 322 Destroy(); 323 return false; 324 } 325 326 return true; 327} 328 329void PbufferGLSurfaceWGL::Destroy() { 330 if (pbuffer_ && device_context_) 331 wglReleasePbufferDCARB(static_cast<HPBUFFERARB>(pbuffer_), device_context_); 332 333 device_context_ = NULL; 334 335 if (pbuffer_) { 336 wglDestroyPbufferARB(static_cast<HPBUFFERARB>(pbuffer_)); 337 pbuffer_ = NULL; 338 } 339} 340 341bool PbufferGLSurfaceWGL::IsOffscreen() { 342 return true; 343} 344 345bool PbufferGLSurfaceWGL::SwapBuffers() { 346 NOTREACHED() << "Attempted to call SwapBuffers on a pbuffer."; 347 return false; 348} 349 350gfx::Size PbufferGLSurfaceWGL::GetSize() { 351 return size_; 352} 353 354void* PbufferGLSurfaceWGL::GetHandle() { 355 return device_context_; 356} 357 358} // namespace gfx 359