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