st/mesa: add a notify_before_flush callback param to flush
[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_screen.h"
31 #include "util/u_memory.h"
32 #include "hud/hud_context.h"
33 #include "util/os_time.h"
34 #include "state_tracker/st_api.h"
35
36 #include "stw_icd.h"
37 #include "stw_framebuffer.h"
38 #include "stw_device.h"
39 #include "stw_winsys.h"
40 #include "stw_tls.h"
41 #include "stw_context.h"
42 #include "stw_st.h"
43
44
45 /**
46 * Search the framebuffer with the matching HWND while holding the
47 * stw_dev::fb_mutex global lock.
48 * If a stw_framebuffer is found, lock it and return the pointer.
49 * Else, return NULL.
50 */
51 static struct stw_framebuffer *
52 stw_framebuffer_from_hwnd_locked(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 stw_framebuffer_lock(fb);
59 assert(fb->mutex.RecursionCount == 1);
60 return fb;
61 }
62
63 return NULL;
64 }
65
66
67 /**
68 * Decrement the reference count on the given stw_framebuffer object.
69 * If the reference count hits zero, destroy the object.
70 *
71 * Note: Both stw_dev::fb_mutex and stw_framebuffer::mutex must already be
72 * locked. After this function completes, the fb's mutex will be unlocked.
73 */
74 void
75 stw_framebuffer_release_locked(struct stw_framebuffer *fb)
76 {
77 struct stw_framebuffer **link;
78
79 assert(fb);
80 assert(stw_own_mutex(&fb->mutex));
81 assert(stw_own_mutex(&stw_dev->fb_mutex));
82
83 /* check the reference count */
84 fb->refcnt--;
85 if (fb->refcnt) {
86 stw_framebuffer_unlock(fb);
87 return;
88 }
89
90 /* remove this stw_framebuffer from the device's linked list */
91 link = &stw_dev->fb_head;
92 while (*link != fb)
93 link = &(*link)->next;
94 assert(*link);
95 *link = fb->next;
96 fb->next = NULL;
97
98 if (fb->shared_surface)
99 stw_dev->stw_winsys->shared_surface_close(stw_dev->screen,
100 fb->shared_surface);
101
102 stw_st_destroy_framebuffer_locked(fb->stfb);
103
104 stw_framebuffer_unlock(fb);
105
106 DeleteCriticalSection(&fb->mutex);
107
108 FREE( fb );
109 }
110
111
112 /**
113 * Query the size of the given framebuffer's on-screen window and update
114 * the stw_framebuffer's width/height.
115 */
116 static void
117 stw_framebuffer_get_size(struct stw_framebuffer *fb)
118 {
119 LONG width, height;
120 RECT client_rect;
121 RECT window_rect;
122 POINT client_pos;
123
124 /*
125 * Sanity checking.
126 */
127 assert(fb->hWnd);
128 assert(fb->width && fb->height);
129 assert(fb->client_rect.right == fb->client_rect.left + fb->width);
130 assert(fb->client_rect.bottom == fb->client_rect.top + fb->height);
131
132 /*
133 * Get the client area size.
134 */
135 if (!GetClientRect(fb->hWnd, &client_rect)) {
136 return;
137 }
138
139 assert(client_rect.left == 0);
140 assert(client_rect.top == 0);
141 width = client_rect.right - client_rect.left;
142 height = client_rect.bottom - client_rect.top;
143
144 fb->minimized = width == 0 || height == 0;
145
146 if (width <= 0 || height <= 0) {
147 /*
148 * When the window is minimized GetClientRect will return zeros. Simply
149 * preserve the current window size, until the window is restored or
150 * maximized again.
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(int nCode, WPARAM wParam, LPARAM lParam)
195 {
196 struct stw_tls_data *tls_data;
197 PCWPSTRUCT pParams = (PCWPSTRUCT)lParam;
198 struct stw_framebuffer *fb;
199
200 tls_data = stw_tls_get_data();
201 if (!tls_data)
202 return 0;
203
204 if (nCode < 0 || !stw_dev)
205 return CallNextHookEx(tls_data->hCallWndProcHook, nCode, wParam, lParam);
206
207 /* We check that the stw_dev object is initialized before we try to do
208 * anything with it. Otherwise, in multi-threaded programs there's a
209 * chance of executing this code before the stw_dev object is fully
210 * initialized.
211 */
212 if (stw_dev && stw_dev->initialized) {
213 if (pParams->message == WM_WINDOWPOSCHANGED) {
214 /* We handle WM_WINDOWPOSCHANGED instead of WM_SIZE because according
215 * to http://blogs.msdn.com/oldnewthing/archive/2008/01/15/7113860.aspx
216 * WM_SIZE is generated from WM_WINDOWPOSCHANGED by DefWindowProc so it
217 * can be masked out by the application.
218 */
219 LPWINDOWPOS lpWindowPos = (LPWINDOWPOS)pParams->lParam;
220 if ((lpWindowPos->flags & SWP_SHOWWINDOW) ||
221 !(lpWindowPos->flags & SWP_NOMOVE) ||
222 !(lpWindowPos->flags & SWP_NOSIZE)) {
223 fb = stw_framebuffer_from_hwnd( pParams->hwnd );
224 if (fb) {
225 /* Size in WINDOWPOS includes the window frame, so get the size
226 * of the client area via GetClientRect.
227 */
228 stw_framebuffer_get_size(fb);
229 stw_framebuffer_unlock(fb);
230 }
231 }
232 }
233 else if (pParams->message == WM_DESTROY) {
234 stw_lock_framebuffers(stw_dev);
235 fb = stw_framebuffer_from_hwnd_locked( pParams->hwnd );
236 if (fb)
237 stw_framebuffer_release_locked(fb);
238 stw_unlock_framebuffers(stw_dev);
239 }
240 }
241
242 return CallNextHookEx(tls_data->hCallWndProcHook, nCode, wParam, lParam);
243 }
244
245
246 /**
247 * Create a new stw_framebuffer object which corresponds to the given
248 * HDC/window. If successful, we return the new stw_framebuffer object
249 * with its mutex locked.
250 */
251 struct stw_framebuffer *
252 stw_framebuffer_create(HDC hdc, int iPixelFormat)
253 {
254 HWND hWnd;
255 struct stw_framebuffer *fb;
256 const struct stw_pixelformat_info *pfi;
257
258 /* We only support drawing to a window. */
259 hWnd = WindowFromDC( hdc );
260 if (!hWnd)
261 return NULL;
262
263 fb = CALLOC_STRUCT( stw_framebuffer );
264 if (fb == NULL)
265 return NULL;
266
267 fb->hWnd = hWnd;
268 fb->iPixelFormat = iPixelFormat;
269
270 /*
271 * We often need a displayable pixel format to make GDI happy. Set it
272 * here (always 1, i.e., out first pixel format) where appropriate.
273 */
274 fb->iDisplayablePixelFormat = iPixelFormat <= stw_dev->pixelformat_count
275 ? iPixelFormat : 1;
276
277 fb->pfi = pfi = stw_pixelformat_get_info( iPixelFormat );
278 fb->stfb = stw_st_create_framebuffer( fb );
279 if (!fb->stfb) {
280 FREE( fb );
281 return NULL;
282 }
283
284 fb->refcnt = 1;
285
286 /*
287 * Windows can be sometimes have zero width and or height, but we ensure
288 * a non-zero framebuffer size at all times.
289 */
290
291 fb->must_resize = TRUE;
292 fb->width = 1;
293 fb->height = 1;
294 fb->client_rect.left = 0;
295 fb->client_rect.top = 0;
296 fb->client_rect.right = fb->client_rect.left + fb->width;
297 fb->client_rect.bottom = fb->client_rect.top + fb->height;
298
299 stw_framebuffer_get_size(fb);
300
301 InitializeCriticalSection(&fb->mutex);
302
303 /* This is the only case where we lock the stw_framebuffer::mutex before
304 * stw_dev::fb_mutex, since no other thread can know about this framebuffer
305 * and we must prevent any other thread from destroying it before we return.
306 */
307 stw_framebuffer_lock(fb);
308
309 stw_lock_framebuffers(stw_dev);
310 fb->next = stw_dev->fb_head;
311 stw_dev->fb_head = fb;
312 stw_unlock_framebuffers(stw_dev);
313
314 return fb;
315 }
316
317
318 /**
319 * Update the framebuffer's size if necessary.
320 */
321 void
322 stw_framebuffer_update(struct stw_framebuffer *fb)
323 {
324 assert(fb->stfb);
325 assert(fb->height);
326 assert(fb->width);
327
328 /* XXX: It would be nice to avoid checking the size again -- in theory
329 * stw_call_window_proc would have cought the resize and stored the right
330 * size already, but unfortunately threads created before the DllMain is
331 * called don't get a DLL_THREAD_ATTACH notification, and there is no way
332 * to know of their existing without using the not very portable PSAPI.
333 */
334 stw_framebuffer_get_size(fb);
335 }
336
337
338 /**
339 * Try to free all stw_framebuffer objects associated with the device.
340 */
341 void
342 stw_framebuffer_cleanup(void)
343 {
344 struct stw_framebuffer *fb;
345 struct stw_framebuffer *next;
346
347 if (!stw_dev)
348 return;
349
350 stw_lock_framebuffers(stw_dev);
351
352 fb = stw_dev->fb_head;
353 while (fb) {
354 next = fb->next;
355
356 stw_framebuffer_lock(fb);
357 stw_framebuffer_release_locked(fb);
358
359 fb = next;
360 }
361 stw_dev->fb_head = NULL;
362
363 stw_unlock_framebuffers(stw_dev);
364 }
365
366
367 /**
368 * Given an hdc, return the corresponding stw_framebuffer.
369 * The returned stw_framebuffer will have its mutex locked.
370 */
371 static struct stw_framebuffer *
372 stw_framebuffer_from_hdc_locked(HDC hdc)
373 {
374 HWND hwnd;
375
376 hwnd = WindowFromDC(hdc);
377 if (!hwnd) {
378 return NULL;
379 }
380
381 return stw_framebuffer_from_hwnd_locked(hwnd);
382 }
383
384
385 /**
386 * Given an HDC, return the corresponding stw_framebuffer.
387 * The returned stw_framebuffer will have its mutex locked.
388 */
389 struct stw_framebuffer *
390 stw_framebuffer_from_hdc(HDC hdc)
391 {
392 struct stw_framebuffer *fb;
393
394 if (!stw_dev)
395 return NULL;
396
397 stw_lock_framebuffers(stw_dev);
398 fb = stw_framebuffer_from_hdc_locked(hdc);
399 stw_unlock_framebuffers(stw_dev);
400
401 return fb;
402 }
403
404
405 /**
406 * Given an HWND, return the corresponding stw_framebuffer.
407 * The returned stw_framebuffer will have its mutex locked.
408 */
409 struct stw_framebuffer *
410 stw_framebuffer_from_hwnd(HWND hwnd)
411 {
412 struct stw_framebuffer *fb;
413
414 stw_lock_framebuffers(stw_dev);
415 fb = stw_framebuffer_from_hwnd_locked(hwnd);
416 stw_unlock_framebuffers(stw_dev);
417
418 return fb;
419 }
420
421
422 BOOL APIENTRY
423 DrvSetPixelFormat(HDC hdc, LONG iPixelFormat)
424 {
425 uint count;
426 uint index;
427 struct stw_framebuffer *fb;
428
429 if (!stw_dev)
430 return FALSE;
431
432 index = (uint) iPixelFormat - 1;
433 count = stw_pixelformat_get_count();
434 if (index >= count)
435 return FALSE;
436
437 fb = stw_framebuffer_from_hdc_locked(hdc);
438 if (fb) {
439 /*
440 * SetPixelFormat must be called only once. However ignore
441 * pbuffers, for which the framebuffer object is created first.
442 */
443 boolean bPbuffer = fb->bPbuffer;
444
445 stw_framebuffer_unlock( fb );
446
447 return bPbuffer;
448 }
449
450 fb = stw_framebuffer_create(hdc, iPixelFormat);
451 if (!fb) {
452 return FALSE;
453 }
454
455 stw_framebuffer_unlock( fb );
456
457 /* Some applications mistakenly use the undocumented wglSetPixelFormat
458 * function instead of SetPixelFormat, so we call SetPixelFormat here to
459 * avoid opengl32.dll's wglCreateContext to fail */
460 if (GetPixelFormat(hdc) == 0) {
461 BOOL bRet = SetPixelFormat(hdc, iPixelFormat, NULL);
462 if (!bRet) {
463 debug_printf("SetPixelFormat failed\n");
464 }
465 }
466
467 return TRUE;
468 }
469
470
471 int
472 stw_pixelformat_get(HDC hdc)
473 {
474 int iPixelFormat = 0;
475 struct stw_framebuffer *fb;
476
477 fb = stw_framebuffer_from_hdc(hdc);
478 if (fb) {
479 iPixelFormat = fb->iPixelFormat;
480 stw_framebuffer_unlock(fb);
481 }
482
483 return iPixelFormat;
484 }
485
486
487 BOOL APIENTRY
488 DrvPresentBuffers(HDC hdc, PGLPRESENTBUFFERSDATA data)
489 {
490 struct stw_framebuffer *fb;
491 struct pipe_screen *screen;
492 struct pipe_resource *res;
493
494 if (!stw_dev)
495 return FALSE;
496
497 fb = stw_framebuffer_from_hdc( hdc );
498 if (fb == NULL)
499 return FALSE;
500
501 screen = stw_dev->screen;
502
503 res = (struct pipe_resource *)data->pPrivateData;
504
505 if (data->hSharedSurface != fb->hSharedSurface) {
506 if (fb->shared_surface) {
507 stw_dev->stw_winsys->shared_surface_close(screen, fb->shared_surface);
508 fb->shared_surface = NULL;
509 }
510
511 fb->hSharedSurface = data->hSharedSurface;
512
513 if (data->hSharedSurface &&
514 stw_dev->stw_winsys->shared_surface_open) {
515 fb->shared_surface =
516 stw_dev->stw_winsys->shared_surface_open(screen,
517 fb->hSharedSurface);
518 }
519 }
520
521 if (!fb->minimized) {
522 if (fb->shared_surface) {
523 stw_dev->stw_winsys->compose(screen,
524 res,
525 fb->shared_surface,
526 &fb->client_rect,
527 data->PresentHistoryToken);
528 }
529 else {
530 stw_dev->stw_winsys->present( screen, res, hdc );
531 }
532 }
533
534 stw_framebuffer_update(fb);
535 stw_notify_current_locked(fb);
536
537 stw_framebuffer_unlock(fb);
538
539 return TRUE;
540 }
541
542
543 /**
544 * Queue a composition.
545 *
546 * The stw_framebuffer object must have its mutex locked. The mutex will
547 * be unlocked here before returning.
548 */
549 BOOL
550 stw_framebuffer_present_locked(HDC hdc,
551 struct stw_framebuffer *fb,
552 struct pipe_resource *res)
553 {
554 if (stw_dev->callbacks.wglCbPresentBuffers &&
555 stw_dev->stw_winsys->compose) {
556 GLCBPRESENTBUFFERSDATA data;
557
558 memset(&data, 0, sizeof data);
559 data.magic1 = 2;
560 data.magic2 = 0;
561 data.AdapterLuid = stw_dev->AdapterLuid;
562 data.rect = fb->client_rect;
563 data.pPrivateData = (void *)res;
564
565 stw_notify_current_locked(fb);
566 stw_framebuffer_unlock(fb);
567
568 return stw_dev->callbacks.wglCbPresentBuffers(hdc, &data);
569 }
570 else {
571 struct pipe_screen *screen = stw_dev->screen;
572
573 stw_dev->stw_winsys->present( screen, res, hdc );
574
575 stw_framebuffer_update(fb);
576 stw_notify_current_locked(fb);
577 stw_framebuffer_unlock(fb);
578
579 return TRUE;
580 }
581 }
582
583
584 /**
585 * This is called just before issuing the buffer swap/present.
586 * We query the current time and determine if we should sleep before
587 * issuing the swap/present.
588 * This is a bit of a hack and is certainly not very accurate but it
589 * basically works.
590 * This is for the WGL_ARB_swap_interval extension.
591 */
592 static void
593 wait_swap_interval(struct stw_framebuffer *fb)
594 {
595 /* Note: all time variables here are in units of microseconds */
596 int64_t cur_time = os_time_get_nano() / 1000;
597
598 if (fb->prev_swap_time != 0) {
599 /* Compute time since previous swap */
600 int64_t delta = cur_time - fb->prev_swap_time;
601 int64_t min_swap_period =
602 1.0e6 / stw_dev->refresh_rate * stw_dev->swap_interval;
603
604 /* If time since last swap is less than wait period, wait.
605 * Note that it's possible for the delta to be negative because of
606 * rollover. See https://bugs.freedesktop.org/show_bug.cgi?id=102241
607 */
608 if ((delta >= 0) && (delta < min_swap_period)) {
609 float fudge = 1.75f; /* emperical fudge factor */
610 int64_t wait = (min_swap_period - delta) * fudge;
611 os_time_sleep(wait);
612 }
613 }
614
615 fb->prev_swap_time = cur_time;
616 }
617
618
619 BOOL APIENTRY
620 DrvSwapBuffers(HDC hdc)
621 {
622 struct stw_context *ctx;
623 struct stw_framebuffer *fb;
624
625 if (!stw_dev)
626 return FALSE;
627
628 fb = stw_framebuffer_from_hdc( hdc );
629 if (fb == NULL)
630 return FALSE;
631
632 if (!(fb->pfi->pfd.dwFlags & PFD_DOUBLEBUFFER)) {
633 stw_framebuffer_unlock(fb);
634 return TRUE;
635 }
636
637 ctx = stw_current_context();
638 if (ctx) {
639 if (ctx->hud) {
640 /* Display the HUD */
641 struct pipe_resource *back =
642 stw_get_framebuffer_resource(fb->stfb, ST_ATTACHMENT_BACK_LEFT);
643 if (back) {
644 hud_run(ctx->hud, NULL, back);
645 }
646 }
647
648 if (ctx->current_framebuffer == fb) {
649 /* flush current context */
650 ctx->st->flush(ctx->st, ST_FLUSH_END_OF_FRAME, NULL, NULL, NULL);
651 }
652 }
653
654 if (stw_dev->swap_interval != 0) {
655 wait_swap_interval(fb);
656 }
657
658 return stw_st_swap_framebuffer_locked(hdc, fb->stfb);
659 }
660
661
662 BOOL APIENTRY
663 DrvSwapLayerBuffers(HDC hdc, UINT fuPlanes)
664 {
665 if (fuPlanes & WGL_SWAP_MAIN_PLANE)
666 return DrvSwapBuffers(hdc);
667
668 return FALSE;
669 }