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