stw_framebuffer.c revision 16d42af618aa6250bedc7e66e0e2c0b061cc6e99
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 "pipe/p_format.h" 31#include "pipe/p_screen.h" 32#include "util/u_format.h" 33#include "util/u_memory.h" 34#include "state_tracker/st_api.h" 35 36#include "stw_icd.h" 37#include "stw_framebuffer.h" 38#include "stw_device.h" 39#include "stw_winsys.h" 40#include "stw_tls.h" 41#include "stw_context.h" 42#include "stw_st.h" 43 44 45/** 46 * Search the framebuffer with the matching HWND while holding the 47 * stw_dev::fb_mutex global lock. 48 */ 49static INLINE struct stw_framebuffer * 50stw_framebuffer_from_hwnd_locked( 51 HWND hwnd ) 52{ 53 struct stw_framebuffer *fb; 54 55 for (fb = stw_dev->fb_head; fb != NULL; fb = fb->next) 56 if (fb->hWnd == hwnd) { 57 pipe_mutex_lock(fb->mutex); 58 break; 59 } 60 61 return fb; 62} 63 64 65/** 66 * Destroy this framebuffer. Both stw_dev::fb_mutex and stw_framebuffer::mutex 67 * must be held, by this order. If there are still references to the 68 * framebuffer, nothing will happen. 69 */ 70static INLINE void 71stw_framebuffer_destroy_locked( 72 struct stw_framebuffer *fb ) 73{ 74 struct stw_framebuffer **link; 75 76 /* check the reference count */ 77 fb->refcnt--; 78 if (fb->refcnt) { 79 pipe_mutex_unlock( fb->mutex ); 80 return; 81 } 82 83 link = &stw_dev->fb_head; 84 while (*link != fb) 85 link = &(*link)->next; 86 assert(*link); 87 *link = fb->next; 88 fb->next = NULL; 89 90 if(fb->shared_surface) 91 stw_dev->stw_winsys->shared_surface_close(stw_dev->screen, fb->shared_surface); 92 93 stw_st_destroy_framebuffer_locked(fb->stfb); 94 95 pipe_mutex_unlock( fb->mutex ); 96 97 pipe_mutex_destroy( fb->mutex ); 98 99 FREE( fb ); 100} 101 102 103void 104stw_framebuffer_release( 105 struct stw_framebuffer *fb) 106{ 107 assert(fb); 108 pipe_mutex_unlock( fb->mutex ); 109} 110 111 112static INLINE void 113stw_framebuffer_get_size( struct stw_framebuffer *fb ) 114{ 115 LONG width, height; 116 RECT client_rect; 117 RECT window_rect; 118 POINT client_pos; 119 120 assert(fb->hWnd); 121 122 /* Get the client area size. */ 123 GetClientRect( fb->hWnd, &client_rect ); 124 assert(client_rect.left == 0); 125 assert(client_rect.top == 0); 126 width = client_rect.right - client_rect.left; 127 height = client_rect.bottom - client_rect.top; 128 129 if (width <= 0 || height <= 0) { 130 /* 131 * When the window is minimized GetClientRect will return zeros. Simply 132 * preserve the current window size, until the window is restored or 133 * maximized again. 134 */ 135 136 assert(width == 0 && height == 0); 137 138 return; 139 } 140 141 if(width != fb->width || height != fb->height) { 142 fb->must_resize = TRUE; 143 fb->width = width; 144 fb->height = height; 145 } 146 147 client_pos.x = 0; 148 client_pos.y = 0; 149 ClientToScreen(fb->hWnd, &client_pos); 150 151 GetWindowRect(fb->hWnd, &window_rect); 152 153 fb->client_rect.left = client_pos.x - window_rect.left; 154 fb->client_rect.top = client_pos.y - window_rect.top; 155 fb->client_rect.right = fb->client_rect.left + fb->width; 156 fb->client_rect.bottom = fb->client_rect.top + fb->height; 157 158#if 0 159 debug_printf("\n"); 160 debug_printf("%s: client_position = (%li, %li)\n", 161 __FUNCTION__, client_pos.x, client_pos.y); 162 debug_printf("%s: window_rect = (%li, %li) - (%li, %li)\n", 163 __FUNCTION__, 164 window_rect.left, window_rect.top, 165 window_rect.right, window_rect.bottom); 166 debug_printf("%s: client_rect = (%li, %li) - (%li, %li)\n", 167 __FUNCTION__, 168 fb->client_rect.left, fb->client_rect.top, 169 fb->client_rect.right, fb->client_rect.bottom); 170#endif 171} 172 173 174/** 175 * @sa http://msdn.microsoft.com/en-us/library/ms644975(VS.85).aspx 176 * @sa http://msdn.microsoft.com/en-us/library/ms644960(VS.85).aspx 177 */ 178LRESULT CALLBACK 179stw_call_window_proc( 180 int nCode, 181 WPARAM wParam, 182 LPARAM lParam ) 183{ 184 struct stw_tls_data *tls_data; 185 PCWPSTRUCT pParams = (PCWPSTRUCT)lParam; 186 struct stw_framebuffer *fb; 187 188 tls_data = stw_tls_get_data(); 189 if(!tls_data) 190 return 0; 191 192 if (nCode < 0 || !stw_dev) 193 return CallNextHookEx(tls_data->hCallWndProcHook, nCode, wParam, lParam); 194 195 if (pParams->message == WM_WINDOWPOSCHANGED) { 196 /* We handle WM_WINDOWPOSCHANGED instead of WM_SIZE because according to 197 * http://blogs.msdn.com/oldnewthing/archive/2008/01/15/7113860.aspx 198 * WM_SIZE is generated from WM_WINDOWPOSCHANGED by DefWindowProc so it 199 * can be masked out by the application. */ 200 LPWINDOWPOS lpWindowPos = (LPWINDOWPOS)pParams->lParam; 201 if((lpWindowPos->flags & SWP_SHOWWINDOW) || 202 !(lpWindowPos->flags & SWP_NOMOVE) || 203 !(lpWindowPos->flags & SWP_NOSIZE)) { 204 fb = stw_framebuffer_from_hwnd( pParams->hwnd ); 205 if(fb) { 206 /* Size in WINDOWPOS includes the window frame, so get the size 207 * of the client area via GetClientRect. */ 208 stw_framebuffer_get_size(fb); 209 stw_framebuffer_release(fb); 210 } 211 } 212 } 213 else if (pParams->message == WM_DESTROY) { 214 pipe_mutex_lock( stw_dev->fb_mutex ); 215 fb = stw_framebuffer_from_hwnd_locked( pParams->hwnd ); 216 if(fb) 217 stw_framebuffer_destroy_locked(fb); 218 pipe_mutex_unlock( stw_dev->fb_mutex ); 219 } 220 221 return CallNextHookEx(tls_data->hCallWndProcHook, nCode, wParam, lParam); 222} 223 224 225struct stw_framebuffer * 226stw_framebuffer_create( 227 HDC hdc, 228 int iPixelFormat ) 229{ 230 HWND hWnd; 231 struct stw_framebuffer *fb; 232 const struct stw_pixelformat_info *pfi; 233 234 /* We only support drawing to a window. */ 235 hWnd = WindowFromDC( hdc ); 236 if(!hWnd) 237 return NULL; 238 239 fb = CALLOC_STRUCT( stw_framebuffer ); 240 if (fb == NULL) 241 return NULL; 242 243 fb->hDC = hdc; 244 fb->hWnd = hWnd; 245 fb->iPixelFormat = iPixelFormat; 246 247 fb->pfi = pfi = stw_pixelformat_get_info( iPixelFormat - 1 ); 248 fb->stfb = stw_st_create_framebuffer( fb ); 249 if (!fb->stfb) { 250 FREE( fb ); 251 return NULL; 252 } 253 254 fb->refcnt = 1; 255 256 stw_framebuffer_get_size(fb); 257 258 pipe_mutex_init( fb->mutex ); 259 260 /* This is the only case where we lock the stw_framebuffer::mutex before 261 * stw_dev::fb_mutex, since no other thread can know about this framebuffer 262 * and we must prevent any other thread from destroying it before we return. 263 */ 264 pipe_mutex_lock( fb->mutex ); 265 266 pipe_mutex_lock( stw_dev->fb_mutex ); 267 fb->next = stw_dev->fb_head; 268 stw_dev->fb_head = fb; 269 pipe_mutex_unlock( stw_dev->fb_mutex ); 270 271 return fb; 272} 273 274/** 275 * Have ptr reference fb. The referenced framebuffer should be locked. 276 */ 277void 278stw_framebuffer_reference( 279 struct stw_framebuffer **ptr, 280 struct stw_framebuffer *fb) 281{ 282 struct stw_framebuffer *old_fb = *ptr; 283 284 if (old_fb == fb) 285 return; 286 287 if (fb) 288 fb->refcnt++; 289 if (old_fb) { 290 pipe_mutex_lock(stw_dev->fb_mutex); 291 292 pipe_mutex_lock(old_fb->mutex); 293 stw_framebuffer_destroy_locked(old_fb); 294 295 pipe_mutex_unlock(stw_dev->fb_mutex); 296 } 297 298 *ptr = fb; 299} 300 301 302/** 303 * Update the framebuffer's size if necessary. 304 */ 305void 306stw_framebuffer_update( 307 struct stw_framebuffer *fb) 308{ 309 assert(fb->stfb); 310 assert(fb->height); 311 assert(fb->width); 312 313 /* XXX: It would be nice to avoid checking the size again -- in theory 314 * stw_call_window_proc would have cought the resize and stored the right 315 * size already, but unfortunately threads created before the DllMain is 316 * called don't get a DLL_THREAD_ATTACH notification, and there is no way 317 * to know of their existing without using the not very portable PSAPI. 318 */ 319 stw_framebuffer_get_size(fb); 320} 321 322 323void 324stw_framebuffer_cleanup( void ) 325{ 326 struct stw_framebuffer *fb; 327 struct stw_framebuffer *next; 328 329 if (!stw_dev) 330 return; 331 332 pipe_mutex_lock( stw_dev->fb_mutex ); 333 334 fb = stw_dev->fb_head; 335 while (fb) { 336 next = fb->next; 337 338 pipe_mutex_lock(fb->mutex); 339 stw_framebuffer_destroy_locked(fb); 340 341 fb = next; 342 } 343 stw_dev->fb_head = NULL; 344 345 pipe_mutex_unlock( stw_dev->fb_mutex ); 346} 347 348 349/** 350 * Given an hdc, return the corresponding stw_framebuffer. 351 */ 352static INLINE struct stw_framebuffer * 353stw_framebuffer_from_hdc_locked( 354 HDC hdc ) 355{ 356 HWND hwnd; 357 struct stw_framebuffer *fb; 358 359 /* 360 * Some applications create and use several HDCs for the same window, so 361 * looking up the framebuffer by the HDC is not reliable. Use HWND whenever 362 * possible. 363 */ 364 hwnd = WindowFromDC(hdc); 365 if(hwnd) 366 return stw_framebuffer_from_hwnd_locked(hwnd); 367 368 for (fb = stw_dev->fb_head; fb != NULL; fb = fb->next) 369 if (fb->hDC == hdc) { 370 pipe_mutex_lock(fb->mutex); 371 break; 372 } 373 374 return fb; 375} 376 377 378/** 379 * Given an hdc, return the corresponding stw_framebuffer. 380 */ 381struct stw_framebuffer * 382stw_framebuffer_from_hdc( 383 HDC hdc ) 384{ 385 struct stw_framebuffer *fb; 386 387 if (!stw_dev) 388 return NULL; 389 390 pipe_mutex_lock( stw_dev->fb_mutex ); 391 fb = stw_framebuffer_from_hdc_locked(hdc); 392 pipe_mutex_unlock( stw_dev->fb_mutex ); 393 394 return fb; 395} 396 397 398/** 399 * Given an hdc, return the corresponding stw_framebuffer. 400 */ 401struct stw_framebuffer * 402stw_framebuffer_from_hwnd( 403 HWND hwnd ) 404{ 405 struct stw_framebuffer *fb; 406 407 pipe_mutex_lock( stw_dev->fb_mutex ); 408 fb = stw_framebuffer_from_hwnd_locked(hwnd); 409 pipe_mutex_unlock( stw_dev->fb_mutex ); 410 411 return fb; 412} 413 414 415BOOL APIENTRY 416DrvSetPixelFormat( 417 HDC hdc, 418 LONG iPixelFormat ) 419{ 420 uint count; 421 uint index; 422 struct stw_framebuffer *fb; 423 424 if (!stw_dev) 425 return FALSE; 426 427 index = (uint) iPixelFormat - 1; 428 count = stw_pixelformat_get_extended_count(); 429 if (index >= count) 430 return FALSE; 431 432 fb = stw_framebuffer_from_hdc_locked(hdc); 433 if(fb) { 434 /* SetPixelFormat must be called only once */ 435 stw_framebuffer_release( fb ); 436 return FALSE; 437 } 438 439 fb = stw_framebuffer_create(hdc, iPixelFormat); 440 if(!fb) { 441 return FALSE; 442 } 443 444 stw_framebuffer_release( fb ); 445 446 /* Some applications mistakenly use the undocumented wglSetPixelFormat 447 * function instead of SetPixelFormat, so we call SetPixelFormat here to 448 * avoid opengl32.dll's wglCreateContext to fail */ 449 if (GetPixelFormat(hdc) == 0) { 450 SetPixelFormat(hdc, iPixelFormat, NULL); 451 } 452 453 return TRUE; 454} 455 456 457int 458stw_pixelformat_get( 459 HDC hdc ) 460{ 461 int iPixelFormat = 0; 462 struct stw_framebuffer *fb; 463 464 fb = stw_framebuffer_from_hdc(hdc); 465 if(fb) { 466 iPixelFormat = fb->iPixelFormat; 467 stw_framebuffer_release(fb); 468 } 469 470 return iPixelFormat; 471} 472 473 474BOOL APIENTRY 475DrvPresentBuffers(HDC hdc, PGLPRESENTBUFFERSDATA data) 476{ 477 struct stw_framebuffer *fb; 478 struct pipe_screen *screen; 479 struct pipe_resource *res; 480 481 if (!stw_dev) 482 return FALSE; 483 484 fb = stw_framebuffer_from_hdc( hdc ); 485 if (fb == NULL) 486 return FALSE; 487 488 screen = stw_dev->screen; 489 490 res = (struct pipe_resource *)data->pPrivateData; 491 492 if(data->hSharedSurface != fb->hSharedSurface) { 493 if(fb->shared_surface) { 494 stw_dev->stw_winsys->shared_surface_close(screen, fb->shared_surface); 495 fb->shared_surface = NULL; 496 } 497 498 fb->hSharedSurface = data->hSharedSurface; 499 500 if(data->hSharedSurface && 501 stw_dev->stw_winsys->shared_surface_open) { 502 fb->shared_surface = stw_dev->stw_winsys->shared_surface_open(screen, fb->hSharedSurface); 503 } 504 } 505 506 if(fb->shared_surface) { 507 stw_dev->stw_winsys->compose(screen, 508 res, 509 fb->shared_surface, 510 &fb->client_rect, 511 data->PresentHistoryToken); 512 } 513 else { 514 stw_dev->stw_winsys->present( screen, res, hdc ); 515 } 516 517 stw_framebuffer_update(fb); 518 stw_notify_current_locked(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_resource *res) 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 *)res; 546 547 stw_notify_current_locked(fb); 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 stw_dev->stw_winsys->present( screen, res, hdc ); 556 557 stw_framebuffer_update(fb); 558 stw_notify_current_locked(fb); 559 stw_framebuffer_release(fb); 560 561 return TRUE; 562 } 563} 564 565 566BOOL APIENTRY 567DrvSwapBuffers( 568 HDC hdc ) 569{ 570 struct stw_framebuffer *fb; 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 stw_flush_current_locked(fb); 585 586 return stw_st_swap_framebuffer_locked(fb->stfb); 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