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