1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "ui/gl/gl_surface_wgl.h"
6
7 #include "base/debug/trace_event.h"
8 #include "base/logging.h"
9 #include "base/memory/scoped_ptr.h"
10 #include "ui/gl/gl_bindings.h"
11 #include "ui/gl/gl_gl_api_implementation.h"
12 #include "ui/gl/gl_wgl_api_implementation.h"
13
14 namespace gfx {
15
16 namespace {
17 const PIXELFORMATDESCRIPTOR kPixelFormatDescriptor = {
18 sizeof(kPixelFormatDescriptor), // Size of structure.
19 1, // Default version.
20 PFD_DRAW_TO_WINDOW | // Window drawing support.
21 PFD_SUPPORT_OPENGL | // OpenGL support.
22 PFD_DOUBLEBUFFER, // Double buffering support (not stereo).
23 PFD_TYPE_RGBA, // RGBA color mode (not indexed).
24 24, // 24 bit color mode.
25 0, 0, 0, 0, 0, 0, // Don't set RGB bits & shifts.
26 8, 0, // 8 bit alpha
27 0, // No accumulation buffer.
28 0, 0, 0, 0, // Ignore accumulation bits.
29 0, // no z-buffer.
30 0, // no stencil buffer.
31 0, // No aux buffer.
32 PFD_MAIN_PLANE, // Main drawing plane (not overlay).
33 0, // Reserved.
34 0, 0, 0, // Layer masks ignored.
35 };
36
IntermediateWindowProc(HWND window,UINT message,WPARAM w_param,LPARAM l_param)37 LRESULT CALLBACK IntermediateWindowProc(HWND window,
38 UINT message,
39 WPARAM w_param,
40 LPARAM l_param) {
41 switch (message) {
42 case WM_ERASEBKGND:
43 // Prevent windows from erasing the background.
44 return 1;
45 case WM_PAINT:
46 // Do not paint anything.
47 PAINTSTRUCT paint;
48 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
56 class DisplayWGL {
57 public:
DisplayWGL()58 DisplayWGL()
59 : module_handle_(0),
60 window_class_(0),
61 window_handle_(0),
62 device_context_(0),
63 pixel_format_(0) {
64 }
65
~DisplayWGL()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
Init()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
window_class() const134 ATOM window_class() const { return window_class_; }
device_context() const135 HDC device_context() const { return device_context_; }
pixel_format() const136 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 };
145 DisplayWGL* g_display;
146 } // namespace
147
GLSurfaceWGL()148 GLSurfaceWGL::GLSurfaceWGL() {
149 }
150
~GLSurfaceWGL()151 GLSurfaceWGL::~GLSurfaceWGL() {
152 }
153
GetDisplay()154 void* GLSurfaceWGL::GetDisplay() {
155 return GetDisplayDC();
156 }
157
InitializeOneOff()158 bool 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
GetDisplayDC()173 HDC GLSurfaceWGL::GetDisplayDC() {
174 return g_display->device_context();
175 }
176
NativeViewGLSurfaceWGL(gfx::AcceleratedWidget window)177 NativeViewGLSurfaceWGL::NativeViewGLSurfaceWGL(gfx::AcceleratedWidget window)
178 : window_(window),
179 child_window_(NULL),
180 device_context_(NULL) {
181 DCHECK(window);
182 }
183
~NativeViewGLSurfaceWGL()184 NativeViewGLSurfaceWGL::~NativeViewGLSurfaceWGL() {
185 Destroy();
186 }
187
Initialize()188 bool 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
Destroy()236 void 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
IsOffscreen()247 bool NativeViewGLSurfaceWGL::IsOffscreen() {
248 return false;
249 }
250
SwapBuffers()251 bool 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
GetSize()273 gfx::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
GetHandle()280 void* NativeViewGLSurfaceWGL::GetHandle() {
281 return device_context_;
282 }
283
PbufferGLSurfaceWGL(const gfx::Size & size)284 PbufferGLSurfaceWGL::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
~PbufferGLSurfaceWGL()294 PbufferGLSurfaceWGL::~PbufferGLSurfaceWGL() {
295 Destroy();
296 }
297
Initialize()298 bool 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
Destroy()329 void 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
IsOffscreen()341 bool PbufferGLSurfaceWGL::IsOffscreen() {
342 return true;
343 }
344
SwapBuffers()345 bool PbufferGLSurfaceWGL::SwapBuffers() {
346 NOTREACHED() << "Attempted to call SwapBuffers on a pbuffer.";
347 return false;
348 }
349
GetSize()350 gfx::Size PbufferGLSurfaceWGL::GetSize() {
351 return size_;
352 }
353
GetHandle()354 void* PbufferGLSurfaceWGL::GetHandle() {
355 return device_context_;
356 }
357
358 } // namespace gfx
359