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