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