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