stw_framebuffer.c revision f724036f0045bd28f323af3666c43b3ef03b6886
1/**************************************************************************
2 *
3 * Copyright 2008 Tungsten Graphics, Inc., Cedar Park, Texas.
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 TUNGSTEN GRAPHICS 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_framebuffer.h"
42#include "stw_device.h"
43#include "stw_public.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   st_unreference_framebuffer(fb->stfb);
87
88   pipe_mutex_unlock( fb->mutex );
89
90   pipe_mutex_destroy( fb->mutex );
91
92   FREE( fb );
93}
94
95
96void
97stw_framebuffer_release(
98   struct stw_framebuffer *fb)
99{
100   assert(fb);
101   pipe_mutex_unlock( fb->mutex );
102}
103
104
105static INLINE void
106stw_framebuffer_get_size( struct stw_framebuffer *fb )
107{
108   unsigned width, height;
109   RECT rect;
110
111   assert(fb->hWnd);
112
113   GetClientRect( fb->hWnd, &rect );
114   width = rect.right - rect.left;
115   height = rect.bottom - rect.top;
116
117   if(width < 1)
118      width = 1;
119   if(height < 1)
120      height = 1;
121
122   if(width != fb->width || height != fb->height) {
123      fb->must_resize = TRUE;
124      fb->width = width;
125      fb->height = height;
126   }
127}
128
129
130/**
131 * @sa http://msdn.microsoft.com/en-us/library/ms644975(VS.85).aspx
132 * @sa http://msdn.microsoft.com/en-us/library/ms644960(VS.85).aspx
133 */
134LRESULT CALLBACK
135stw_call_window_proc(
136   int nCode,
137   WPARAM wParam,
138   LPARAM lParam )
139{
140   struct stw_tls_data *tls_data;
141   PCWPSTRUCT pParams = (PCWPSTRUCT)lParam;
142   struct stw_framebuffer *fb;
143
144   tls_data = stw_tls_get_data();
145   if(!tls_data)
146      return 0;
147
148   if (nCode < 0)
149       return CallNextHookEx(tls_data->hCallWndProcHook, nCode, wParam, lParam);
150
151   if (pParams->message == WM_WINDOWPOSCHANGED) {
152      /* We handle WM_WINDOWPOSCHANGED instead of WM_SIZE because according to
153       * http://blogs.msdn.com/oldnewthing/archive/2008/01/15/7113860.aspx
154       * WM_SIZE is generated from WM_WINDOWPOSCHANGED by DefWindowProc so it
155       * can be masked out by the application. */
156      LPWINDOWPOS lpWindowPos = (LPWINDOWPOS)pParams->lParam;
157      if((lpWindowPos->flags & SWP_SHOWWINDOW) ||
158         !(lpWindowPos->flags & SWP_NOSIZE)) {
159         fb = stw_framebuffer_from_hwnd( pParams->hwnd );
160         if(fb) {
161            /* Size in WINDOWPOS includes the window frame, so get the size
162             * of the client area via GetClientRect.  */
163            stw_framebuffer_get_size(fb);
164            stw_framebuffer_release(fb);
165         }
166      }
167   }
168   else if (pParams->message == WM_DESTROY) {
169      pipe_mutex_lock( stw_dev->fb_mutex );
170      fb = stw_framebuffer_from_hwnd_locked( pParams->hwnd );
171      if(fb)
172         stw_framebuffer_destroy_locked(fb);
173      pipe_mutex_unlock( stw_dev->fb_mutex );
174   }
175
176   return CallNextHookEx(tls_data->hCallWndProcHook, nCode, wParam, lParam);
177}
178
179
180struct stw_framebuffer *
181stw_framebuffer_create(
182   HDC hdc,
183   int iPixelFormat )
184{
185   HWND hWnd;
186   struct stw_framebuffer *fb;
187   const struct stw_pixelformat_info *pfi;
188
189   /* We only support drawing to a window. */
190   hWnd = WindowFromDC( hdc );
191   if(!hWnd)
192      return NULL;
193
194   fb = CALLOC_STRUCT( stw_framebuffer );
195   if (fb == NULL)
196      return NULL;
197
198   fb->hDC = hdc;
199   fb->hWnd = hWnd;
200   fb->iPixelFormat = iPixelFormat;
201
202   fb->pfi = pfi = stw_pixelformat_get_info( iPixelFormat - 1 );
203
204   stw_pixelformat_visual(&fb->visual, pfi);
205
206   stw_framebuffer_get_size(fb);
207
208   pipe_mutex_init( fb->mutex );
209
210   /* This is the only case where we lock the stw_framebuffer::mutex before
211    * stw_dev::fb_mutex, since no other thread can know about this framebuffer
212    * and we must prevent any other thread from destroying it before we return.
213    */
214   pipe_mutex_lock( fb->mutex );
215
216   pipe_mutex_lock( stw_dev->fb_mutex );
217   fb->next = stw_dev->fb_head;
218   stw_dev->fb_head = fb;
219   pipe_mutex_unlock( stw_dev->fb_mutex );
220
221   return fb;
222}
223
224
225BOOL
226stw_framebuffer_allocate(
227   struct stw_framebuffer *fb)
228{
229   assert(fb);
230
231   if(!fb->stfb) {
232      const struct stw_pixelformat_info *pfi = fb->pfi;
233      enum pipe_format colorFormat, depthFormat, stencilFormat;
234
235      colorFormat = pfi->color_format;
236
237      assert(pf_layout( pfi->depth_stencil_format ) == PIPE_FORMAT_LAYOUT_RGBAZS );
238
239      if(pf_get_component_bits( pfi->depth_stencil_format, PIPE_FORMAT_COMP_Z ))
240         depthFormat = pfi->depth_stencil_format;
241      else
242         depthFormat = PIPE_FORMAT_NONE;
243
244      if(pf_get_component_bits( pfi->depth_stencil_format, PIPE_FORMAT_COMP_S ))
245         stencilFormat = pfi->depth_stencil_format;
246      else
247         stencilFormat = PIPE_FORMAT_NONE;
248
249      assert(fb->must_resize);
250      assert(fb->width);
251      assert(fb->height);
252
253      fb->stfb = st_create_framebuffer(
254         &fb->visual,
255         colorFormat,
256         depthFormat,
257         stencilFormat,
258         fb->width,
259         fb->height,
260         (void *) fb );
261
262      // to notify the context
263      fb->must_resize = TRUE;
264   }
265
266   return fb->stfb ? TRUE : FALSE;
267}
268
269
270/**
271 * Update the framebuffer's size if necessary.
272 */
273void
274stw_framebuffer_update(
275   struct stw_framebuffer *fb)
276{
277   assert(fb->stfb);
278   assert(fb->height);
279   assert(fb->width);
280
281   /* XXX: It would be nice to avoid checking the size again -- in theory
282    * stw_call_window_proc would have cought the resize and stored the right
283    * size already, but unfortunately threads created before the DllMain is
284    * called don't get a DLL_THREAD_ATTACH notification, and there is no way
285    * to know of their existing without using the not very portable PSAPI.
286    */
287   stw_framebuffer_get_size(fb);
288
289   if(fb->must_resize) {
290      st_resize_framebuffer(fb->stfb, fb->width, fb->height);
291      fb->must_resize = FALSE;
292   }
293}
294
295
296void
297stw_framebuffer_cleanup( void )
298{
299   struct stw_framebuffer *fb;
300   struct stw_framebuffer *next;
301
302   pipe_mutex_lock( stw_dev->fb_mutex );
303
304   fb = stw_dev->fb_head;
305   while (fb) {
306      next = fb->next;
307
308      pipe_mutex_lock(fb->mutex);
309      stw_framebuffer_destroy_locked(fb);
310
311      fb = next;
312   }
313   stw_dev->fb_head = NULL;
314
315   pipe_mutex_unlock( stw_dev->fb_mutex );
316}
317
318
319/**
320 * Given an hdc, return the corresponding stw_framebuffer.
321 */
322static INLINE struct stw_framebuffer *
323stw_framebuffer_from_hdc_locked(
324   HDC hdc )
325{
326   HWND hwnd;
327   struct stw_framebuffer *fb;
328
329   /*
330    * Some applications create and use several HDCs for the same window, so
331    * looking up the framebuffer by the HDC is not reliable. Use HWND whenever
332    * possible.
333    */
334   hwnd = WindowFromDC(hdc);
335   if(hwnd)
336      return stw_framebuffer_from_hwnd_locked(hwnd);
337
338   for (fb = stw_dev->fb_head; fb != NULL; fb = fb->next)
339      if (fb->hDC == hdc) {
340         pipe_mutex_lock(fb->mutex);
341         break;
342      }
343
344   return fb;
345}
346
347
348/**
349 * Given an hdc, return the corresponding stw_framebuffer.
350 */
351struct stw_framebuffer *
352stw_framebuffer_from_hdc(
353   HDC hdc )
354{
355   struct stw_framebuffer *fb;
356
357   pipe_mutex_lock( stw_dev->fb_mutex );
358   fb = stw_framebuffer_from_hdc_locked(hdc);
359   pipe_mutex_unlock( stw_dev->fb_mutex );
360
361   return fb;
362}
363
364
365/**
366 * Given an hdc, return the corresponding stw_framebuffer.
367 */
368struct stw_framebuffer *
369stw_framebuffer_from_hwnd(
370   HWND hwnd )
371{
372   struct stw_framebuffer *fb;
373
374   pipe_mutex_lock( stw_dev->fb_mutex );
375   fb = stw_framebuffer_from_hwnd_locked(hwnd);
376   pipe_mutex_unlock( stw_dev->fb_mutex );
377
378   return fb;
379}
380
381
382BOOL
383stw_pixelformat_set(
384   HDC hdc,
385   int iPixelFormat )
386{
387   uint count;
388   uint index;
389   struct stw_framebuffer *fb;
390
391   index = (uint) iPixelFormat - 1;
392   count = stw_pixelformat_get_extended_count();
393   if (index >= count)
394      return FALSE;
395
396   fb = stw_framebuffer_from_hdc_locked(hdc);
397   if(fb) {
398      /* SetPixelFormat must be called only once */
399      stw_framebuffer_release( fb );
400      return FALSE;
401   }
402
403   fb = stw_framebuffer_create(hdc, iPixelFormat);
404   if(!fb) {
405      return FALSE;
406   }
407
408   stw_framebuffer_release( fb );
409
410   /* Some applications mistakenly use the undocumented wglSetPixelFormat
411    * function instead of SetPixelFormat, so we call SetPixelFormat here to
412    * avoid opengl32.dll's wglCreateContext to fail */
413   if (GetPixelFormat(hdc) == 0) {
414        SetPixelFormat(hdc, iPixelFormat, NULL);
415   }
416
417   return TRUE;
418}
419
420
421int
422stw_pixelformat_get(
423   HDC hdc )
424{
425   int iPixelFormat = 0;
426   struct stw_framebuffer *fb;
427
428   fb = stw_framebuffer_from_hdc(hdc);
429   if(fb) {
430      iPixelFormat = fb->iPixelFormat;
431      stw_framebuffer_release(fb);
432   }
433
434   return iPixelFormat;
435}
436
437
438BOOL
439stw_swap_buffers(
440   HDC hdc )
441{
442   struct stw_framebuffer *fb;
443   struct pipe_screen *screen;
444   struct pipe_surface *surface;
445
446   fb = stw_framebuffer_from_hdc( hdc );
447   if (fb == NULL)
448      return FALSE;
449
450   if (!(fb->pfi->pfd.dwFlags & PFD_DOUBLEBUFFER)) {
451      stw_framebuffer_release(fb);
452      return TRUE;
453   }
454
455   /* If we're swapping the buffer associated with the current context
456    * we have to flush any pending rendering commands first.
457    */
458   st_notify_swapbuffers( fb->stfb );
459
460   screen = stw_dev->screen;
461
462   if(!st_get_framebuffer_surface( fb->stfb, ST_SURFACE_BACK_LEFT, &surface )) {
463      /* FIXME: this shouldn't happen, but does on glean */
464      stw_framebuffer_release(fb);
465      return FALSE;
466   }
467
468#ifdef DEBUG
469   if(stw_dev->trace_running) {
470      screen = trace_screen(screen)->screen;
471      surface = trace_surface(surface)->surface;
472   }
473#endif
474
475   stw_dev->stw_winsys->flush_frontbuffer( screen, surface, hdc );
476
477   stw_framebuffer_update(fb);
478   stw_framebuffer_release(fb);
479
480   return TRUE;
481}
482
483
484BOOL
485stw_swap_layer_buffers(
486   HDC hdc,
487   UINT fuPlanes )
488{
489   if(fuPlanes & WGL_SWAP_MAIN_PLANE)
490      return stw_swap_buffers(hdc);
491
492   return FALSE;
493}
494