18ecb05e94f198a581771bf7e04768f97db0642b
[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 "pipe/p_format.h"
31 #include "pipe/p_screen.h"
32 #include "util/u_format.h"
33 #include "util/u_memory.h"
34 #include "hud/hud_context.h"
35 #include "state_tracker/st_api.h"
36
37 #include "stw_icd.h"
38 #include "stw_framebuffer.h"
39 #include "stw_device.h"
40 #include "stw_winsys.h"
41 #include "stw_tls.h"
42 #include "stw_context.h"
43 #include "stw_st.h"
44
45
46 /**
47 * Search the framebuffer with the matching HWND while holding the
48 * stw_dev::fb_mutex global lock.
49 */
50 static INLINE struct stw_framebuffer *
51 stw_framebuffer_from_hwnd_locked(
52 HWND hwnd )
53 {
54 struct stw_framebuffer *fb;
55
56 for (fb = stw_dev->fb_head; fb != NULL; fb = fb->next)
57 if (fb->hWnd == hwnd) {
58 pipe_mutex_lock(fb->mutex);
59 break;
60 }
61
62 return fb;
63 }
64
65
66 /**
67 * Destroy this framebuffer. Both stw_dev::fb_mutex and stw_framebuffer::mutex
68 * must be held, by this order. If there are still references to the
69 * framebuffer, nothing will happen.
70 */
71 static INLINE void
72 stw_framebuffer_destroy_locked(
73 struct stw_framebuffer *fb )
74 {
75 struct stw_framebuffer **link;
76
77 /* check the reference count */
78 fb->refcnt--;
79 if (fb->refcnt) {
80 pipe_mutex_unlock( fb->mutex );
81 return;
82 }
83
84 link = &stw_dev->fb_head;
85 while (*link != fb)
86 link = &(*link)->next;
87 assert(*link);
88 *link = fb->next;
89 fb->next = NULL;
90
91 if(fb->shared_surface)
92 stw_dev->stw_winsys->shared_surface_close(stw_dev->screen, fb->shared_surface);
93
94 stw_st_destroy_framebuffer_locked(fb->stfb);
95
96 pipe_mutex_unlock( fb->mutex );
97
98 pipe_mutex_destroy( fb->mutex );
99
100 FREE( fb );
101 }
102
103
104 void
105 stw_framebuffer_release(
106 struct stw_framebuffer *fb)
107 {
108 assert(fb);
109 pipe_mutex_unlock( fb->mutex );
110 }
111
112
113 static INLINE void
114 stw_framebuffer_get_size( struct stw_framebuffer *fb )
115 {
116 LONG width, height;
117 RECT client_rect;
118 RECT window_rect;
119 POINT client_pos;
120
121 /*
122 * Sanity checking.
123 */
124
125 assert(fb->hWnd);
126 assert(fb->width && fb->height);
127 assert(fb->client_rect.right == fb->client_rect.left + fb->width);
128 assert(fb->client_rect.bottom == fb->client_rect.top + fb->height);
129
130 /*
131 * Get the client area size.
132 */
133
134 if (!GetClientRect(fb->hWnd, &client_rect)) {
135 return;
136 }
137
138 assert(client_rect.left == 0);
139 assert(client_rect.top == 0);
140 width = client_rect.right - client_rect.left;
141 height = client_rect.bottom - client_rect.top;
142
143 fb->minimized = width == 0 || height == 0;
144
145 if (width <= 0 || height <= 0) {
146 /*
147 * When the window is minimized GetClientRect will return zeros. Simply
148 * preserve the current window size, until the window is restored or
149 * maximized again.
150 */
151
152 return;
153 }
154
155 if (width != fb->width || height != fb->height) {
156 fb->must_resize = TRUE;
157 fb->width = width;
158 fb->height = height;
159 }
160
161 client_pos.x = 0;
162 client_pos.y = 0;
163 if (ClientToScreen(fb->hWnd, &client_pos) &&
164 GetWindowRect(fb->hWnd, &window_rect)) {
165 fb->client_rect.left = client_pos.x - window_rect.left;
166 fb->client_rect.top = client_pos.y - window_rect.top;
167 }
168
169 fb->client_rect.right = fb->client_rect.left + fb->width;
170 fb->client_rect.bottom = fb->client_rect.top + fb->height;
171
172 #if 0
173 debug_printf("\n");
174 debug_printf("%s: hwnd = %p\n", __FUNCTION__, fb->hWnd);
175 debug_printf("%s: client_position = (%li, %li)\n",
176 __FUNCTION__, client_pos.x, client_pos.y);
177 debug_printf("%s: window_rect = (%li, %li) - (%li, %li)\n",
178 __FUNCTION__,
179 window_rect.left, window_rect.top,
180 window_rect.right, window_rect.bottom);
181 debug_printf("%s: client_rect = (%li, %li) - (%li, %li)\n",
182 __FUNCTION__,
183 fb->client_rect.left, fb->client_rect.top,
184 fb->client_rect.right, fb->client_rect.bottom);
185 #endif
186 }
187
188
189 /**
190 * @sa http://msdn.microsoft.com/en-us/library/ms644975(VS.85).aspx
191 * @sa http://msdn.microsoft.com/en-us/library/ms644960(VS.85).aspx
192 */
193 LRESULT CALLBACK
194 stw_call_window_proc(
195 int nCode,
196 WPARAM wParam,
197 LPARAM lParam )
198 {
199 struct stw_tls_data *tls_data;
200 PCWPSTRUCT pParams = (PCWPSTRUCT)lParam;
201 struct stw_framebuffer *fb;
202
203 tls_data = stw_tls_get_data();
204 if(!tls_data)
205 return 0;
206
207 if (nCode < 0 || !stw_dev)
208 return CallNextHookEx(tls_data->hCallWndProcHook, nCode, wParam, lParam);
209
210 if (pParams->message == WM_WINDOWPOSCHANGED) {
211 /* We handle WM_WINDOWPOSCHANGED instead of WM_SIZE because according to
212 * http://blogs.msdn.com/oldnewthing/archive/2008/01/15/7113860.aspx
213 * WM_SIZE is generated from WM_WINDOWPOSCHANGED by DefWindowProc so it
214 * can be masked out by the application. */
215 LPWINDOWPOS lpWindowPos = (LPWINDOWPOS)pParams->lParam;
216 if((lpWindowPos->flags & SWP_SHOWWINDOW) ||
217 !(lpWindowPos->flags & SWP_NOMOVE) ||
218 !(lpWindowPos->flags & SWP_NOSIZE)) {
219 fb = stw_framebuffer_from_hwnd( pParams->hwnd );
220 if(fb) {
221 /* Size in WINDOWPOS includes the window frame, so get the size
222 * of the client area via GetClientRect. */
223 stw_framebuffer_get_size(fb);
224 stw_framebuffer_release(fb);
225 }
226 }
227 }
228 else if (pParams->message == WM_DESTROY) {
229 pipe_mutex_lock( stw_dev->fb_mutex );
230 fb = stw_framebuffer_from_hwnd_locked( pParams->hwnd );
231 if(fb)
232 stw_framebuffer_destroy_locked(fb);
233 pipe_mutex_unlock( stw_dev->fb_mutex );
234 }
235
236 return CallNextHookEx(tls_data->hCallWndProcHook, nCode, wParam, lParam);
237 }
238
239
240 struct stw_framebuffer *
241 stw_framebuffer_create(
242 HDC hdc,
243 int iPixelFormat )
244 {
245 HWND hWnd;
246 struct stw_framebuffer *fb;
247 const struct stw_pixelformat_info *pfi;
248
249 /* We only support drawing to a window. */
250 hWnd = WindowFromDC( hdc );
251 if(!hWnd)
252 return NULL;
253
254 fb = CALLOC_STRUCT( stw_framebuffer );
255 if (fb == NULL)
256 return NULL;
257
258 fb->hWnd = hWnd;
259 fb->iPixelFormat = iPixelFormat;
260
261 /*
262 * We often need a displayable pixel format to make GDI happy. Set it here (always 1, i.e.,
263 * out first pixel format) where appropriat.
264 */
265 fb->iDisplayablePixelFormat = iPixelFormat <= stw_dev->pixelformat_count ? iPixelFormat : 1;
266
267 fb->pfi = pfi = stw_pixelformat_get_info( iPixelFormat );
268 fb->stfb = stw_st_create_framebuffer( fb );
269 if (!fb->stfb) {
270 FREE( fb );
271 return NULL;
272 }
273
274 fb->refcnt = 1;
275
276 /*
277 * Windows can be sometimes have zero width and or height, but we ensure
278 * a non-zero framebuffer size at all times.
279 */
280
281 fb->must_resize = TRUE;
282 fb->width = 1;
283 fb->height = 1;
284 fb->client_rect.left = 0;
285 fb->client_rect.top = 0;
286 fb->client_rect.right = fb->client_rect.left + fb->width;
287 fb->client_rect.bottom = fb->client_rect.top + fb->height;
288
289 stw_framebuffer_get_size(fb);
290
291 pipe_mutex_init( fb->mutex );
292
293 /* This is the only case where we lock the stw_framebuffer::mutex before
294 * stw_dev::fb_mutex, since no other thread can know about this framebuffer
295 * and we must prevent any other thread from destroying it before we return.
296 */
297 pipe_mutex_lock( fb->mutex );
298
299 pipe_mutex_lock( stw_dev->fb_mutex );
300 fb->next = stw_dev->fb_head;
301 stw_dev->fb_head = fb;
302 pipe_mutex_unlock( stw_dev->fb_mutex );
303
304 return fb;
305 }
306
307 /**
308 * Have ptr reference fb. The referenced framebuffer should be locked.
309 */
310 void
311 stw_framebuffer_reference(
312 struct stw_framebuffer **ptr,
313 struct stw_framebuffer *fb)
314 {
315 struct stw_framebuffer *old_fb = *ptr;
316
317 if (old_fb == fb)
318 return;
319
320 if (fb)
321 fb->refcnt++;
322 if (old_fb) {
323 pipe_mutex_lock(stw_dev->fb_mutex);
324
325 pipe_mutex_lock(old_fb->mutex);
326 stw_framebuffer_destroy_locked(old_fb);
327
328 pipe_mutex_unlock(stw_dev->fb_mutex);
329 }
330
331 *ptr = fb;
332 }
333
334
335 /**
336 * Update the framebuffer's size if necessary.
337 */
338 void
339 stw_framebuffer_update(
340 struct stw_framebuffer *fb)
341 {
342 assert(fb->stfb);
343 assert(fb->height);
344 assert(fb->width);
345
346 /* XXX: It would be nice to avoid checking the size again -- in theory
347 * stw_call_window_proc would have cought the resize and stored the right
348 * size already, but unfortunately threads created before the DllMain is
349 * called don't get a DLL_THREAD_ATTACH notification, and there is no way
350 * to know of their existing without using the not very portable PSAPI.
351 */
352 stw_framebuffer_get_size(fb);
353 }
354
355
356 void
357 stw_framebuffer_cleanup( void )
358 {
359 struct stw_framebuffer *fb;
360 struct stw_framebuffer *next;
361
362 if (!stw_dev)
363 return;
364
365 pipe_mutex_lock( stw_dev->fb_mutex );
366
367 fb = stw_dev->fb_head;
368 while (fb) {
369 next = fb->next;
370
371 pipe_mutex_lock(fb->mutex);
372 stw_framebuffer_destroy_locked(fb);
373
374 fb = next;
375 }
376 stw_dev->fb_head = NULL;
377
378 pipe_mutex_unlock( stw_dev->fb_mutex );
379 }
380
381
382 /**
383 * Given an hdc, return the corresponding stw_framebuffer.
384 */
385 static INLINE struct stw_framebuffer *
386 stw_framebuffer_from_hdc_locked(
387 HDC hdc )
388 {
389 HWND hwnd;
390
391 hwnd = WindowFromDC(hdc);
392 if (!hwnd) {
393 return NULL;
394 }
395
396 return stw_framebuffer_from_hwnd_locked(hwnd);
397 }
398
399
400 /**
401 * Given an hdc, return the corresponding stw_framebuffer.
402 */
403 struct stw_framebuffer *
404 stw_framebuffer_from_hdc(
405 HDC hdc )
406 {
407 struct stw_framebuffer *fb;
408
409 if (!stw_dev)
410 return NULL;
411
412 pipe_mutex_lock( stw_dev->fb_mutex );
413 fb = stw_framebuffer_from_hdc_locked(hdc);
414 pipe_mutex_unlock( stw_dev->fb_mutex );
415
416 return fb;
417 }
418
419
420 /**
421 * Given an hdc, return the corresponding stw_framebuffer.
422 */
423 struct stw_framebuffer *
424 stw_framebuffer_from_hwnd(
425 HWND hwnd )
426 {
427 struct stw_framebuffer *fb;
428
429 pipe_mutex_lock( stw_dev->fb_mutex );
430 fb = stw_framebuffer_from_hwnd_locked(hwnd);
431 pipe_mutex_unlock( stw_dev->fb_mutex );
432
433 return fb;
434 }
435
436
437 BOOL APIENTRY
438 DrvSetPixelFormat(
439 HDC hdc,
440 LONG iPixelFormat )
441 {
442 uint count;
443 uint index;
444 struct stw_framebuffer *fb;
445
446 if (!stw_dev)
447 return FALSE;
448
449 index = (uint) iPixelFormat - 1;
450 count = stw_pixelformat_get_count();
451 if (index >= count)
452 return FALSE;
453
454 fb = stw_framebuffer_from_hdc_locked(hdc);
455 if(fb) {
456 /*
457 * SetPixelFormat must be called only once. However ignore
458 * pbuffers, for which the framebuffer object is created first.
459 */
460 boolean bPbuffer = fb->bPbuffer;
461
462 stw_framebuffer_release( fb );
463
464 return bPbuffer;
465 }
466
467 fb = stw_framebuffer_create(hdc, iPixelFormat);
468 if(!fb) {
469 return FALSE;
470 }
471
472 stw_framebuffer_release( fb );
473
474 /* Some applications mistakenly use the undocumented wglSetPixelFormat
475 * function instead of SetPixelFormat, so we call SetPixelFormat here to
476 * avoid opengl32.dll's wglCreateContext to fail */
477 if (GetPixelFormat(hdc) == 0) {
478 BOOL bRet = SetPixelFormat(hdc, iPixelFormat, NULL);
479 assert(bRet);
480 }
481
482 return TRUE;
483 }
484
485
486 int
487 stw_pixelformat_get(
488 HDC hdc )
489 {
490 int iPixelFormat = 0;
491 struct stw_framebuffer *fb;
492
493 fb = stw_framebuffer_from_hdc(hdc);
494 if(fb) {
495 iPixelFormat = fb->iPixelFormat;
496 stw_framebuffer_release(fb);
497 }
498
499 return iPixelFormat;
500 }
501
502
503 BOOL APIENTRY
504 DrvPresentBuffers(HDC hdc, PGLPRESENTBUFFERSDATA data)
505 {
506 struct stw_framebuffer *fb;
507 struct pipe_screen *screen;
508 struct pipe_resource *res;
509
510 if (!stw_dev)
511 return FALSE;
512
513 fb = stw_framebuffer_from_hdc( hdc );
514 if (fb == NULL)
515 return FALSE;
516
517 screen = stw_dev->screen;
518
519 res = (struct pipe_resource *)data->pPrivateData;
520
521 if(data->hSharedSurface != fb->hSharedSurface) {
522 if(fb->shared_surface) {
523 stw_dev->stw_winsys->shared_surface_close(screen, fb->shared_surface);
524 fb->shared_surface = NULL;
525 }
526
527 fb->hSharedSurface = data->hSharedSurface;
528
529 if(data->hSharedSurface &&
530 stw_dev->stw_winsys->shared_surface_open) {
531 fb->shared_surface = stw_dev->stw_winsys->shared_surface_open(screen, fb->hSharedSurface);
532 }
533 }
534
535 if (!fb->minimized) {
536 if (fb->shared_surface) {
537 stw_dev->stw_winsys->compose(screen,
538 res,
539 fb->shared_surface,
540 &fb->client_rect,
541 data->PresentHistoryToken);
542 }
543 else {
544 stw_dev->stw_winsys->present( screen, res, hdc );
545 }
546 }
547
548 stw_framebuffer_update(fb);
549 stw_notify_current_locked(fb);
550
551 stw_framebuffer_release(fb);
552
553 return TRUE;
554 }
555
556
557 /**
558 * Queue a composition.
559 *
560 * It will drop the lock on success.
561 */
562 BOOL
563 stw_framebuffer_present_locked(HDC hdc,
564 struct stw_framebuffer *fb,
565 struct pipe_resource *res)
566 {
567 if(stw_dev->callbacks.wglCbPresentBuffers &&
568 stw_dev->stw_winsys->compose) {
569 GLCBPRESENTBUFFERSDATA data;
570
571 memset(&data, 0, sizeof data);
572 data.magic1 = 2;
573 data.magic2 = 0;
574 data.AdapterLuid = stw_dev->AdapterLuid;
575 data.rect = fb->client_rect;
576 data.pPrivateData = (void *)res;
577
578 stw_notify_current_locked(fb);
579 stw_framebuffer_release(fb);
580
581 return stw_dev->callbacks.wglCbPresentBuffers(hdc, &data);
582 }
583 else {
584 struct pipe_screen *screen = stw_dev->screen;
585
586 stw_dev->stw_winsys->present( screen, res, hdc );
587
588 stw_framebuffer_update(fb);
589 stw_notify_current_locked(fb);
590 stw_framebuffer_release(fb);
591
592 return TRUE;
593 }
594 }
595
596
597 BOOL APIENTRY
598 DrvSwapBuffers(
599 HDC hdc )
600 {
601 struct stw_context *ctx;
602 struct stw_framebuffer *fb;
603
604 if (!stw_dev)
605 return FALSE;
606
607 fb = stw_framebuffer_from_hdc( hdc );
608 if (fb == NULL)
609 return FALSE;
610
611 if (!(fb->pfi->pfd.dwFlags & PFD_DOUBLEBUFFER)) {
612 stw_framebuffer_release(fb);
613 return TRUE;
614 }
615
616 /* Display the HUD */
617 ctx = stw_current_context();
618 if (ctx && ctx->hud) {
619 struct pipe_resource *back =
620 stw_get_framebuffer_resource(fb->stfb, ST_ATTACHMENT_BACK_LEFT);
621 hud_draw(ctx->hud, back);
622 }
623
624 stw_flush_current_locked(fb);
625
626 return stw_st_swap_framebuffer_locked(hdc, fb->stfb);
627 }
628
629
630 BOOL APIENTRY
631 DrvSwapLayerBuffers(
632 HDC hdc,
633 UINT fuPlanes )
634 {
635 if(fuPlanes & WGL_SWAP_MAIN_PLANE)
636 return DrvSwapBuffers(hdc);
637
638 return FALSE;
639 }