123b841c8f3eda25d77676be469ab7b3825b61c1
[mesa.git] / src / gallium / state_trackers / wgl / stw_framebuffer.c
1 /**************************************************************************
2 *
3 * Copyright 2008 Tungsten Graphics, Inc., Cedar Park, Texas.
4 * All Rights Reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sub license, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
13 *
14 * The above copyright notice and this permission notice (including the
15 * next paragraph) shall be included in all copies or substantial portions
16 * of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
21 * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR
22 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 *
26 **************************************************************************/
27
28 #include <windows.h>
29
30 #include "main/context.h"
31 #include "pipe/p_format.h"
32 #include "pipe/p_screen.h"
33 #include "state_tracker/st_context.h"
34 #include "state_tracker/st_public.h"
35
36 #ifdef DEBUG
37 #include "trace/tr_screen.h"
38 #include "trace/tr_texture.h"
39 #endif
40
41 #include "stw_icd.h"
42 #include "stw_framebuffer.h"
43 #include "stw_device.h"
44 #include "stw_winsys.h"
45 #include "stw_tls.h"
46
47
48 /**
49 * Search the framebuffer with the matching HWND while holding the
50 * stw_dev::fb_mutex global lock.
51 */
52 static INLINE struct stw_framebuffer *
53 stw_framebuffer_from_hwnd_locked(
54 HWND hwnd )
55 {
56 struct stw_framebuffer *fb;
57
58 for (fb = stw_dev->fb_head; fb != NULL; fb = fb->next)
59 if (fb->hWnd == hwnd) {
60 pipe_mutex_lock(fb->mutex);
61 break;
62 }
63
64 return fb;
65 }
66
67
68 /**
69 * Destroy this framebuffer. Both stw_dev::fb_mutex and stw_framebuffer::mutex
70 * must be held, by this order. Obviously no further access to fb can be done
71 * after this.
72 */
73 static INLINE void
74 stw_framebuffer_destroy_locked(
75 struct stw_framebuffer *fb )
76 {
77 struct stw_framebuffer **link;
78
79 link = &stw_dev->fb_head;
80 while (*link != fb)
81 link = &(*link)->next;
82 assert(*link);
83 *link = fb->next;
84 fb->next = NULL;
85
86 st_unreference_framebuffer(fb->stfb);
87
88 pipe_mutex_unlock( fb->mutex );
89
90 pipe_mutex_destroy( fb->mutex );
91
92 FREE( fb );
93 }
94
95
96 void
97 stw_framebuffer_release(
98 struct stw_framebuffer *fb)
99 {
100 assert(fb);
101 pipe_mutex_unlock( fb->mutex );
102 }
103
104
105 static INLINE void
106 stw_framebuffer_get_size( struct stw_framebuffer *fb )
107 {
108 unsigned width, height;
109 RECT rect;
110
111 assert(fb->hWnd);
112
113 GetClientRect( fb->hWnd, &rect );
114 width = rect.right - rect.left;
115 height = rect.bottom - rect.top;
116
117 if(width < 1)
118 width = 1;
119 if(height < 1)
120 height = 1;
121
122 if(width != fb->width || height != fb->height) {
123 fb->must_resize = TRUE;
124 fb->width = width;
125 fb->height = height;
126 }
127 }
128
129
130 /**
131 * @sa http://msdn.microsoft.com/en-us/library/ms644975(VS.85).aspx
132 * @sa http://msdn.microsoft.com/en-us/library/ms644960(VS.85).aspx
133 */
134 LRESULT CALLBACK
135 stw_call_window_proc(
136 int nCode,
137 WPARAM wParam,
138 LPARAM lParam )
139 {
140 struct stw_tls_data *tls_data;
141 PCWPSTRUCT pParams = (PCWPSTRUCT)lParam;
142 struct stw_framebuffer *fb;
143
144 tls_data = stw_tls_get_data();
145 if(!tls_data)
146 return 0;
147
148 if (nCode < 0)
149 return CallNextHookEx(tls_data->hCallWndProcHook, nCode, wParam, lParam);
150
151 if (pParams->message == WM_WINDOWPOSCHANGED) {
152 /* We handle WM_WINDOWPOSCHANGED instead of WM_SIZE because according to
153 * http://blogs.msdn.com/oldnewthing/archive/2008/01/15/7113860.aspx
154 * WM_SIZE is generated from WM_WINDOWPOSCHANGED by DefWindowProc so it
155 * can be masked out by the application. */
156 LPWINDOWPOS lpWindowPos = (LPWINDOWPOS)pParams->lParam;
157 if((lpWindowPos->flags & SWP_SHOWWINDOW) ||
158 !(lpWindowPos->flags & SWP_NOSIZE)) {
159 fb = stw_framebuffer_from_hwnd( pParams->hwnd );
160 if(fb) {
161 /* Size in WINDOWPOS includes the window frame, so get the size
162 * of the client area via GetClientRect. */
163 stw_framebuffer_get_size(fb);
164 stw_framebuffer_release(fb);
165 }
166 }
167 }
168 else if (pParams->message == WM_DESTROY) {
169 pipe_mutex_lock( stw_dev->fb_mutex );
170 fb = stw_framebuffer_from_hwnd_locked( pParams->hwnd );
171 if(fb)
172 stw_framebuffer_destroy_locked(fb);
173 pipe_mutex_unlock( stw_dev->fb_mutex );
174 }
175
176 return CallNextHookEx(tls_data->hCallWndProcHook, nCode, wParam, lParam);
177 }
178
179
180 struct stw_framebuffer *
181 stw_framebuffer_create(
182 HDC hdc,
183 int iPixelFormat )
184 {
185 HWND hWnd;
186 struct stw_framebuffer *fb;
187 const struct stw_pixelformat_info *pfi;
188
189 /* We only support drawing to a window. */
190 hWnd = WindowFromDC( hdc );
191 if(!hWnd)
192 return NULL;
193
194 fb = CALLOC_STRUCT( stw_framebuffer );
195 if (fb == NULL)
196 return NULL;
197
198 fb->hDC = hdc;
199 fb->hWnd = hWnd;
200 fb->iPixelFormat = iPixelFormat;
201
202 fb->pfi = pfi = stw_pixelformat_get_info( iPixelFormat - 1 );
203
204 stw_pixelformat_visual(&fb->visual, pfi);
205
206 stw_framebuffer_get_size(fb);
207
208 pipe_mutex_init( fb->mutex );
209
210 /* This is the only case where we lock the stw_framebuffer::mutex before
211 * stw_dev::fb_mutex, since no other thread can know about this framebuffer
212 * and we must prevent any other thread from destroying it before we return.
213 */
214 pipe_mutex_lock( fb->mutex );
215
216 pipe_mutex_lock( stw_dev->fb_mutex );
217 fb->next = stw_dev->fb_head;
218 stw_dev->fb_head = fb;
219 pipe_mutex_unlock( stw_dev->fb_mutex );
220
221 return fb;
222 }
223
224
225 BOOL
226 stw_framebuffer_allocate(
227 struct stw_framebuffer *fb)
228 {
229 assert(fb);
230
231 if(!fb->stfb) {
232 const struct stw_pixelformat_info *pfi = fb->pfi;
233 enum pipe_format colorFormat, depthFormat, stencilFormat;
234
235 colorFormat = pfi->color_format;
236
237 assert(pf_layout( pfi->depth_stencil_format ) == PIPE_FORMAT_LAYOUT_RGBAZS );
238
239 if(pf_get_component_bits( pfi->depth_stencil_format, PIPE_FORMAT_COMP_Z ))
240 depthFormat = pfi->depth_stencil_format;
241 else
242 depthFormat = PIPE_FORMAT_NONE;
243
244 if(pf_get_component_bits( pfi->depth_stencil_format, PIPE_FORMAT_COMP_S ))
245 stencilFormat = pfi->depth_stencil_format;
246 else
247 stencilFormat = PIPE_FORMAT_NONE;
248
249 assert(fb->must_resize);
250 assert(fb->width);
251 assert(fb->height);
252
253 fb->stfb = st_create_framebuffer(
254 &fb->visual,
255 colorFormat,
256 depthFormat,
257 stencilFormat,
258 fb->width,
259 fb->height,
260 (void *) fb );
261
262 // to notify the context
263 fb->must_resize = TRUE;
264 }
265
266 return fb->stfb ? TRUE : FALSE;
267 }
268
269
270 /**
271 * Update the framebuffer's size if necessary.
272 */
273 void
274 stw_framebuffer_update(
275 struct stw_framebuffer *fb)
276 {
277 assert(fb->stfb);
278 assert(fb->height);
279 assert(fb->width);
280
281 /* XXX: It would be nice to avoid checking the size again -- in theory
282 * stw_call_window_proc would have cought the resize and stored the right
283 * size already, but unfortunately threads created before the DllMain is
284 * called don't get a DLL_THREAD_ATTACH notification, and there is no way
285 * to know of their existing without using the not very portable PSAPI.
286 */
287 stw_framebuffer_get_size(fb);
288
289 if(fb->must_resize) {
290 st_resize_framebuffer(fb->stfb, fb->width, fb->height);
291 fb->must_resize = FALSE;
292 }
293 }
294
295
296 void
297 stw_framebuffer_cleanup( void )
298 {
299 struct stw_framebuffer *fb;
300 struct stw_framebuffer *next;
301
302 pipe_mutex_lock( stw_dev->fb_mutex );
303
304 fb = stw_dev->fb_head;
305 while (fb) {
306 next = fb->next;
307
308 pipe_mutex_lock(fb->mutex);
309 stw_framebuffer_destroy_locked(fb);
310
311 fb = next;
312 }
313 stw_dev->fb_head = NULL;
314
315 pipe_mutex_unlock( stw_dev->fb_mutex );
316 }
317
318
319 /**
320 * Given an hdc, return the corresponding stw_framebuffer.
321 */
322 static INLINE struct stw_framebuffer *
323 stw_framebuffer_from_hdc_locked(
324 HDC hdc )
325 {
326 HWND hwnd;
327 struct stw_framebuffer *fb;
328
329 /*
330 * Some applications create and use several HDCs for the same window, so
331 * looking up the framebuffer by the HDC is not reliable. Use HWND whenever
332 * possible.
333 */
334 hwnd = WindowFromDC(hdc);
335 if(hwnd)
336 return stw_framebuffer_from_hwnd_locked(hwnd);
337
338 for (fb = stw_dev->fb_head; fb != NULL; fb = fb->next)
339 if (fb->hDC == hdc) {
340 pipe_mutex_lock(fb->mutex);
341 break;
342 }
343
344 return fb;
345 }
346
347
348 /**
349 * Given an hdc, return the corresponding stw_framebuffer.
350 */
351 struct stw_framebuffer *
352 stw_framebuffer_from_hdc(
353 HDC hdc )
354 {
355 struct stw_framebuffer *fb;
356
357 pipe_mutex_lock( stw_dev->fb_mutex );
358 fb = stw_framebuffer_from_hdc_locked(hdc);
359 pipe_mutex_unlock( stw_dev->fb_mutex );
360
361 return fb;
362 }
363
364
365 /**
366 * Given an hdc, return the corresponding stw_framebuffer.
367 */
368 struct stw_framebuffer *
369 stw_framebuffer_from_hwnd(
370 HWND hwnd )
371 {
372 struct stw_framebuffer *fb;
373
374 pipe_mutex_lock( stw_dev->fb_mutex );
375 fb = stw_framebuffer_from_hwnd_locked(hwnd);
376 pipe_mutex_unlock( stw_dev->fb_mutex );
377
378 return fb;
379 }
380
381
382 BOOL APIENTRY
383 DrvSetPixelFormat(
384 HDC hdc,
385 LONG iPixelFormat )
386 {
387 uint count;
388 uint index;
389 struct stw_framebuffer *fb;
390
391 index = (uint) iPixelFormat - 1;
392 count = stw_pixelformat_get_extended_count();
393 if (index >= count)
394 return FALSE;
395
396 fb = stw_framebuffer_from_hdc_locked(hdc);
397 if(fb) {
398 /* SetPixelFormat must be called only once */
399 stw_framebuffer_release( fb );
400 return FALSE;
401 }
402
403 fb = stw_framebuffer_create(hdc, iPixelFormat);
404 if(!fb) {
405 return FALSE;
406 }
407
408 stw_framebuffer_release( fb );
409
410 /* Some applications mistakenly use the undocumented wglSetPixelFormat
411 * function instead of SetPixelFormat, so we call SetPixelFormat here to
412 * avoid opengl32.dll's wglCreateContext to fail */
413 if (GetPixelFormat(hdc) == 0) {
414 SetPixelFormat(hdc, iPixelFormat, NULL);
415 }
416
417 return TRUE;
418 }
419
420
421 int
422 stw_pixelformat_get(
423 HDC hdc )
424 {
425 int iPixelFormat = 0;
426 struct stw_framebuffer *fb;
427
428 fb = stw_framebuffer_from_hdc(hdc);
429 if(fb) {
430 iPixelFormat = fb->iPixelFormat;
431 stw_framebuffer_release(fb);
432 }
433
434 return iPixelFormat;
435 }
436
437
438 BOOL APIENTRY
439 DrvSwapBuffers(
440 HDC hdc )
441 {
442 struct stw_framebuffer *fb;
443 struct pipe_screen *screen;
444 struct pipe_surface *surface;
445
446 fb = stw_framebuffer_from_hdc( hdc );
447 if (fb == NULL)
448 return FALSE;
449
450 if (!(fb->pfi->pfd.dwFlags & PFD_DOUBLEBUFFER)) {
451 stw_framebuffer_release(fb);
452 return TRUE;
453 }
454
455 /* If we're swapping the buffer associated with the current context
456 * we have to flush any pending rendering commands first.
457 */
458 st_notify_swapbuffers( fb->stfb );
459
460 screen = stw_dev->screen;
461
462 if(!st_get_framebuffer_surface( fb->stfb, ST_SURFACE_BACK_LEFT, &surface )) {
463 /* FIXME: this shouldn't happen, but does on glean */
464 stw_framebuffer_release(fb);
465 return FALSE;
466 }
467
468 #ifdef DEBUG
469 if(stw_dev->trace_running) {
470 screen = trace_screen(screen)->screen;
471 surface = trace_surface(surface)->surface;
472 }
473 #endif
474
475 stw_dev->stw_winsys->flush_frontbuffer( screen, surface, hdc );
476
477 stw_framebuffer_update(fb);
478 stw_framebuffer_release(fb);
479
480 return TRUE;
481 }
482
483
484 BOOL APIENTRY
485 DrvSwapLayerBuffers(
486 HDC hdc,
487 UINT fuPlanes )
488 {
489 if(fuPlanes & WGL_SWAP_MAIN_PLANE)
490 return DrvSwapBuffers(hdc);
491
492 return FALSE;
493 }