Merge branch 'mesa_7_6_branch' into mesa_7_7_branch
[mesa.git] / src / gallium / state_trackers / wgl / stw_framebuffer.c
1 /**************************************************************************
2 *
3 * Copyright 2008-2009 Vmware, Inc.
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 VMWARE 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 if(fb->shared_surface)
87 stw_dev->stw_winsys->shared_surface_close(stw_dev->screen, fb->shared_surface);
88
89 st_unreference_framebuffer(fb->stfb);
90
91 pipe_mutex_unlock( fb->mutex );
92
93 pipe_mutex_destroy( fb->mutex );
94
95 FREE( fb );
96 }
97
98
99 void
100 stw_framebuffer_release(
101 struct stw_framebuffer *fb)
102 {
103 assert(fb);
104 pipe_mutex_unlock( fb->mutex );
105 }
106
107
108 static INLINE void
109 stw_framebuffer_get_size( struct stw_framebuffer *fb )
110 {
111 unsigned width, height;
112 RECT client_rect;
113 RECT window_rect;
114 POINT client_pos;
115
116 assert(fb->hWnd);
117
118 /* Get the client area size. */
119 GetClientRect( fb->hWnd, &client_rect );
120 assert(client_rect.left == 0);
121 assert(client_rect.top == 0);
122 width = client_rect.right - client_rect.left;
123 height = client_rect.bottom - client_rect.top;
124
125 if(width < 1)
126 width = 1;
127 if(height < 1)
128 height = 1;
129
130 if(width != fb->width || height != fb->height) {
131 fb->must_resize = TRUE;
132 fb->width = width;
133 fb->height = height;
134 }
135
136 client_pos.x = 0;
137 client_pos.y = 0;
138 ClientToScreen(fb->hWnd, &client_pos);
139
140 GetWindowRect(fb->hWnd, &window_rect);
141
142 fb->client_rect.left = client_pos.x - window_rect.left;
143 fb->client_rect.top = client_pos.y - window_rect.top;
144 fb->client_rect.right = fb->client_rect.left + fb->width;
145 fb->client_rect.bottom = fb->client_rect.top + fb->height;
146
147 #if 0
148 debug_printf("\n");
149 debug_printf("%s: client_position = (%i, %i)\n",
150 __FUNCTION__, client_pos.x, client_pos.y);
151 debug_printf("%s: window_rect = (%i, %i) - (%i, %i)\n",
152 __FUNCTION__,
153 window_rect.left, window_rect.top,
154 window_rect.right, window_rect.bottom);
155 debug_printf("%s: client_rect = (%i, %i) - (%i, %i)\n",
156 __FUNCTION__,
157 fb->client_rect.left, fb->client_rect.top,
158 fb->client_rect.right, fb->client_rect.bottom);
159 #endif
160 }
161
162
163 /**
164 * @sa http://msdn.microsoft.com/en-us/library/ms644975(VS.85).aspx
165 * @sa http://msdn.microsoft.com/en-us/library/ms644960(VS.85).aspx
166 */
167 LRESULT CALLBACK
168 stw_call_window_proc(
169 int nCode,
170 WPARAM wParam,
171 LPARAM lParam )
172 {
173 struct stw_tls_data *tls_data;
174 PCWPSTRUCT pParams = (PCWPSTRUCT)lParam;
175 struct stw_framebuffer *fb;
176
177 tls_data = stw_tls_get_data();
178 if(!tls_data)
179 return 0;
180
181 if (nCode < 0)
182 return CallNextHookEx(tls_data->hCallWndProcHook, nCode, wParam, lParam);
183
184 if (pParams->message == WM_WINDOWPOSCHANGED) {
185 /* We handle WM_WINDOWPOSCHANGED instead of WM_SIZE because according to
186 * http://blogs.msdn.com/oldnewthing/archive/2008/01/15/7113860.aspx
187 * WM_SIZE is generated from WM_WINDOWPOSCHANGED by DefWindowProc so it
188 * can be masked out by the application. */
189 LPWINDOWPOS lpWindowPos = (LPWINDOWPOS)pParams->lParam;
190 if((lpWindowPos->flags & SWP_SHOWWINDOW) ||
191 !(lpWindowPos->flags & SWP_NOMOVE) ||
192 !(lpWindowPos->flags & SWP_NOSIZE)) {
193 fb = stw_framebuffer_from_hwnd( pParams->hwnd );
194 if(fb) {
195 /* Size in WINDOWPOS includes the window frame, so get the size
196 * of the client area via GetClientRect. */
197 stw_framebuffer_get_size(fb);
198 stw_framebuffer_release(fb);
199 }
200 }
201 }
202 else if (pParams->message == WM_DESTROY) {
203 pipe_mutex_lock( stw_dev->fb_mutex );
204 fb = stw_framebuffer_from_hwnd_locked( pParams->hwnd );
205 if(fb)
206 stw_framebuffer_destroy_locked(fb);
207 pipe_mutex_unlock( stw_dev->fb_mutex );
208 }
209
210 return CallNextHookEx(tls_data->hCallWndProcHook, nCode, wParam, lParam);
211 }
212
213
214 struct stw_framebuffer *
215 stw_framebuffer_create(
216 HDC hdc,
217 int iPixelFormat )
218 {
219 HWND hWnd;
220 struct stw_framebuffer *fb;
221 const struct stw_pixelformat_info *pfi;
222
223 /* We only support drawing to a window. */
224 hWnd = WindowFromDC( hdc );
225 if(!hWnd)
226 return NULL;
227
228 fb = CALLOC_STRUCT( stw_framebuffer );
229 if (fb == NULL)
230 return NULL;
231
232 fb->hDC = hdc;
233 fb->hWnd = hWnd;
234 fb->iPixelFormat = iPixelFormat;
235
236 fb->pfi = pfi = stw_pixelformat_get_info( iPixelFormat - 1 );
237
238 stw_pixelformat_visual(&fb->visual, pfi);
239
240 stw_framebuffer_get_size(fb);
241
242 pipe_mutex_init( fb->mutex );
243
244 /* This is the only case where we lock the stw_framebuffer::mutex before
245 * stw_dev::fb_mutex, since no other thread can know about this framebuffer
246 * and we must prevent any other thread from destroying it before we return.
247 */
248 pipe_mutex_lock( fb->mutex );
249
250 pipe_mutex_lock( stw_dev->fb_mutex );
251 fb->next = stw_dev->fb_head;
252 stw_dev->fb_head = fb;
253 pipe_mutex_unlock( stw_dev->fb_mutex );
254
255 return fb;
256 }
257
258
259 BOOL
260 stw_framebuffer_allocate(
261 struct stw_framebuffer *fb)
262 {
263 assert(fb);
264
265 if(!fb->stfb) {
266 const struct stw_pixelformat_info *pfi = fb->pfi;
267 enum pipe_format colorFormat, depthFormat, stencilFormat;
268
269 colorFormat = pfi->color_format;
270
271 assert(pf_layout( pfi->depth_stencil_format ) == PIPE_FORMAT_LAYOUT_RGBAZS );
272
273 if(pf_get_component_bits( pfi->depth_stencil_format, PIPE_FORMAT_COMP_Z ))
274 depthFormat = pfi->depth_stencil_format;
275 else
276 depthFormat = PIPE_FORMAT_NONE;
277
278 if(pf_get_component_bits( pfi->depth_stencil_format, PIPE_FORMAT_COMP_S ))
279 stencilFormat = pfi->depth_stencil_format;
280 else
281 stencilFormat = PIPE_FORMAT_NONE;
282
283 assert(fb->must_resize);
284 assert(fb->width);
285 assert(fb->height);
286
287 fb->stfb = st_create_framebuffer(
288 &fb->visual,
289 colorFormat,
290 depthFormat,
291 stencilFormat,
292 fb->width,
293 fb->height,
294 (void *) fb );
295
296 // to notify the context
297 fb->must_resize = TRUE;
298 }
299
300 return fb->stfb ? TRUE : FALSE;
301 }
302
303
304 /**
305 * Update the framebuffer's size if necessary.
306 */
307 void
308 stw_framebuffer_update(
309 struct stw_framebuffer *fb)
310 {
311 assert(fb->stfb);
312 assert(fb->height);
313 assert(fb->width);
314
315 /* XXX: It would be nice to avoid checking the size again -- in theory
316 * stw_call_window_proc would have cought the resize and stored the right
317 * size already, but unfortunately threads created before the DllMain is
318 * called don't get a DLL_THREAD_ATTACH notification, and there is no way
319 * to know of their existing without using the not very portable PSAPI.
320 */
321 stw_framebuffer_get_size(fb);
322
323 if(fb->must_resize) {
324 st_resize_framebuffer(fb->stfb, fb->width, fb->height);
325 fb->must_resize = FALSE;
326 }
327 }
328
329
330 void
331 stw_framebuffer_cleanup( void )
332 {
333 struct stw_framebuffer *fb;
334 struct stw_framebuffer *next;
335
336 pipe_mutex_lock( stw_dev->fb_mutex );
337
338 fb = stw_dev->fb_head;
339 while (fb) {
340 next = fb->next;
341
342 pipe_mutex_lock(fb->mutex);
343 stw_framebuffer_destroy_locked(fb);
344
345 fb = next;
346 }
347 stw_dev->fb_head = NULL;
348
349 pipe_mutex_unlock( stw_dev->fb_mutex );
350 }
351
352
353 /**
354 * Given an hdc, return the corresponding stw_framebuffer.
355 */
356 static INLINE struct stw_framebuffer *
357 stw_framebuffer_from_hdc_locked(
358 HDC hdc )
359 {
360 HWND hwnd;
361 struct stw_framebuffer *fb;
362
363 /*
364 * Some applications create and use several HDCs for the same window, so
365 * looking up the framebuffer by the HDC is not reliable. Use HWND whenever
366 * possible.
367 */
368 hwnd = WindowFromDC(hdc);
369 if(hwnd)
370 return stw_framebuffer_from_hwnd_locked(hwnd);
371
372 for (fb = stw_dev->fb_head; fb != NULL; fb = fb->next)
373 if (fb->hDC == hdc) {
374 pipe_mutex_lock(fb->mutex);
375 break;
376 }
377
378 return fb;
379 }
380
381
382 /**
383 * Given an hdc, return the corresponding stw_framebuffer.
384 */
385 struct stw_framebuffer *
386 stw_framebuffer_from_hdc(
387 HDC hdc )
388 {
389 struct stw_framebuffer *fb;
390
391 pipe_mutex_lock( stw_dev->fb_mutex );
392 fb = stw_framebuffer_from_hdc_locked(hdc);
393 pipe_mutex_unlock( stw_dev->fb_mutex );
394
395 return fb;
396 }
397
398
399 /**
400 * Given an hdc, return the corresponding stw_framebuffer.
401 */
402 struct stw_framebuffer *
403 stw_framebuffer_from_hwnd(
404 HWND hwnd )
405 {
406 struct stw_framebuffer *fb;
407
408 pipe_mutex_lock( stw_dev->fb_mutex );
409 fb = stw_framebuffer_from_hwnd_locked(hwnd);
410 pipe_mutex_unlock( stw_dev->fb_mutex );
411
412 return fb;
413 }
414
415
416 BOOL APIENTRY
417 DrvSetPixelFormat(
418 HDC hdc,
419 LONG iPixelFormat )
420 {
421 uint count;
422 uint index;
423 struct stw_framebuffer *fb;
424
425 index = (uint) iPixelFormat - 1;
426 count = stw_pixelformat_get_extended_count();
427 if (index >= count)
428 return FALSE;
429
430 fb = stw_framebuffer_from_hdc_locked(hdc);
431 if(fb) {
432 /* SetPixelFormat must be called only once */
433 stw_framebuffer_release( fb );
434 return FALSE;
435 }
436
437 fb = stw_framebuffer_create(hdc, iPixelFormat);
438 if(!fb) {
439 return FALSE;
440 }
441
442 stw_framebuffer_release( fb );
443
444 /* Some applications mistakenly use the undocumented wglSetPixelFormat
445 * function instead of SetPixelFormat, so we call SetPixelFormat here to
446 * avoid opengl32.dll's wglCreateContext to fail */
447 if (GetPixelFormat(hdc) == 0) {
448 SetPixelFormat(hdc, iPixelFormat, NULL);
449 }
450
451 return TRUE;
452 }
453
454
455 int
456 stw_pixelformat_get(
457 HDC hdc )
458 {
459 int iPixelFormat = 0;
460 struct stw_framebuffer *fb;
461
462 fb = stw_framebuffer_from_hdc(hdc);
463 if(fb) {
464 iPixelFormat = fb->iPixelFormat;
465 stw_framebuffer_release(fb);
466 }
467
468 return iPixelFormat;
469 }
470
471
472 BOOL APIENTRY
473 DrvPresentBuffers(HDC hdc, PGLPRESENTBUFFERSDATA data)
474 {
475 struct stw_framebuffer *fb;
476 struct pipe_screen *screen;
477 struct pipe_surface *surface;
478
479 fb = stw_framebuffer_from_hdc( hdc );
480 if (fb == NULL)
481 return FALSE;
482
483 screen = stw_dev->screen;
484
485 surface = (struct pipe_surface *)data->pPrivateData;
486
487 #ifdef DEBUG
488 if(stw_dev->trace_running) {
489 screen = trace_screen(screen)->screen;
490 surface = trace_surface(surface)->surface;
491 }
492 #endif
493
494 if(data->hSharedSurface != fb->hSharedSurface) {
495 if(fb->shared_surface) {
496 stw_dev->stw_winsys->shared_surface_close(screen, fb->shared_surface);
497 fb->shared_surface = NULL;
498 }
499
500 fb->hSharedSurface = data->hSharedSurface;
501
502 if(data->hSharedSurface &&
503 stw_dev->stw_winsys->shared_surface_open) {
504 fb->shared_surface = stw_dev->stw_winsys->shared_surface_open(screen, fb->hSharedSurface);
505 }
506 }
507
508 if(fb->shared_surface) {
509 stw_dev->stw_winsys->compose(screen,
510 surface,
511 fb->shared_surface,
512 &fb->client_rect,
513 data->PresentHistoryToken);
514 }
515 else {
516 stw_dev->stw_winsys->present( screen, surface, hdc );
517 }
518
519 stw_framebuffer_update(fb);
520
521 stw_framebuffer_release(fb);
522
523 return TRUE;
524 }
525
526
527 /**
528 * Queue a composition.
529 *
530 * It will drop the lock on success.
531 */
532 BOOL
533 stw_framebuffer_present_locked(HDC hdc,
534 struct stw_framebuffer *fb,
535 struct pipe_surface *surface)
536 {
537 if(stw_dev->callbacks.wglCbPresentBuffers &&
538 stw_dev->stw_winsys->compose) {
539 GLCBPRESENTBUFFERSDATA data;
540
541 memset(&data, 0, sizeof data);
542 data.magic1 = 2;
543 data.magic2 = 0;
544 data.AdapterLuid = stw_dev->AdapterLuid;
545 data.rect = fb->client_rect;
546 data.pPrivateData = (void *)surface;
547
548 stw_framebuffer_release(fb);
549
550 return stw_dev->callbacks.wglCbPresentBuffers(hdc, &data);
551 }
552 else {
553 struct pipe_screen *screen = stw_dev->screen;
554
555 #ifdef DEBUG
556 if(stw_dev->trace_running) {
557 screen = trace_screen(screen)->screen;
558 surface = trace_surface(surface)->surface;
559 }
560 #endif
561
562 stw_dev->stw_winsys->present( screen, surface, hdc );
563
564 stw_framebuffer_update(fb);
565
566 stw_framebuffer_release(fb);
567
568 return TRUE;
569 }
570 }
571
572
573 BOOL APIENTRY
574 DrvSwapBuffers(
575 HDC hdc )
576 {
577 struct stw_framebuffer *fb;
578 struct pipe_surface *surface = NULL;
579
580 fb = stw_framebuffer_from_hdc( hdc );
581 if (fb == NULL)
582 return FALSE;
583
584 if (!(fb->pfi->pfd.dwFlags & PFD_DOUBLEBUFFER)) {
585 stw_framebuffer_release(fb);
586 return TRUE;
587 }
588
589 st_swapbuffers(fb->stfb, &surface, NULL);
590
591 return stw_framebuffer_present_locked(hdc, fb, surface);
592 }
593
594
595 BOOL APIENTRY
596 DrvSwapLayerBuffers(
597 HDC hdc,
598 UINT fuPlanes )
599 {
600 if(fuPlanes & WGL_SWAP_MAIN_PLANE)
601 return DrvSwapBuffers(hdc);
602
603 return FALSE;
604 }