stw_framebuffer.c revision a67ecfeebc522bd2021160ba68cf59299cd4d9de
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 /* 121 * Sanity checking. 122 */ 123 124 assert(fb->hWnd); 125 assert(fb->width && fb->height); 126 assert(fb->client_rect.right == fb->client_rect.left + fb->width); 127 assert(fb->client_rect.bottom == fb->client_rect.top + fb->height); 128 129 /* 130 * Get the client area size. 131 */ 132 133 if (!GetClientRect(fb->hWnd, &client_rect)) { 134 return; 135 } 136 137 assert(client_rect.left == 0); 138 assert(client_rect.top == 0); 139 width = client_rect.right - client_rect.left; 140 height = client_rect.bottom - client_rect.top; 141 142 if (width <= 0 || height <= 0) { 143 /* 144 * When the window is minimized GetClientRect will return zeros. Simply 145 * preserve the current window size, until the window is restored or 146 * maximized again. 147 */ 148 149 return; 150 } 151 152 if (width != fb->width || height != fb->height) { 153 fb->must_resize = TRUE; 154 fb->width = width; 155 fb->height = height; 156 } 157 158 client_pos.x = 0; 159 client_pos.y = 0; 160 if (ClientToScreen(fb->hWnd, &client_pos) && 161 GetWindowRect(fb->hWnd, &window_rect)) { 162 fb->client_rect.left = client_pos.x - window_rect.left; 163 fb->client_rect.top = client_pos.y - window_rect.top; 164 } 165 166 fb->client_rect.right = fb->client_rect.left + fb->width; 167 fb->client_rect.bottom = fb->client_rect.top + fb->height; 168 169#if 0 170 debug_printf("\n"); 171 debug_printf("%s: client_position = (%li, %li)\n", 172 __FUNCTION__, client_pos.x, client_pos.y); 173 debug_printf("%s: window_rect = (%li, %li) - (%li, %li)\n", 174 __FUNCTION__, 175 window_rect.left, window_rect.top, 176 window_rect.right, window_rect.bottom); 177 debug_printf("%s: client_rect = (%li, %li) - (%li, %li)\n", 178 __FUNCTION__, 179 fb->client_rect.left, fb->client_rect.top, 180 fb->client_rect.right, fb->client_rect.bottom); 181#endif 182} 183 184 185/** 186 * @sa http://msdn.microsoft.com/en-us/library/ms644975(VS.85).aspx 187 * @sa http://msdn.microsoft.com/en-us/library/ms644960(VS.85).aspx 188 */ 189LRESULT CALLBACK 190stw_call_window_proc( 191 int nCode, 192 WPARAM wParam, 193 LPARAM lParam ) 194{ 195 struct stw_tls_data *tls_data; 196 PCWPSTRUCT pParams = (PCWPSTRUCT)lParam; 197 struct stw_framebuffer *fb; 198 199 tls_data = stw_tls_get_data(); 200 if(!tls_data) 201 return 0; 202 203 if (nCode < 0 || !stw_dev) 204 return CallNextHookEx(tls_data->hCallWndProcHook, nCode, wParam, lParam); 205 206 if (pParams->message == WM_WINDOWPOSCHANGED) { 207 /* We handle WM_WINDOWPOSCHANGED instead of WM_SIZE because according to 208 * http://blogs.msdn.com/oldnewthing/archive/2008/01/15/7113860.aspx 209 * WM_SIZE is generated from WM_WINDOWPOSCHANGED by DefWindowProc so it 210 * can be masked out by the application. */ 211 LPWINDOWPOS lpWindowPos = (LPWINDOWPOS)pParams->lParam; 212 if((lpWindowPos->flags & SWP_SHOWWINDOW) || 213 !(lpWindowPos->flags & SWP_NOMOVE) || 214 !(lpWindowPos->flags & SWP_NOSIZE)) { 215 fb = stw_framebuffer_from_hwnd( pParams->hwnd ); 216 if(fb) { 217 /* Size in WINDOWPOS includes the window frame, so get the size 218 * of the client area via GetClientRect. */ 219 stw_framebuffer_get_size(fb); 220 stw_framebuffer_release(fb); 221 } 222 } 223 } 224 else if (pParams->message == WM_DESTROY) { 225 pipe_mutex_lock( stw_dev->fb_mutex ); 226 fb = stw_framebuffer_from_hwnd_locked( pParams->hwnd ); 227 if(fb) 228 stw_framebuffer_destroy_locked(fb); 229 pipe_mutex_unlock( stw_dev->fb_mutex ); 230 } 231 232 return CallNextHookEx(tls_data->hCallWndProcHook, nCode, wParam, lParam); 233} 234 235 236struct stw_framebuffer * 237stw_framebuffer_create( 238 HDC hdc, 239 int iPixelFormat ) 240{ 241 HWND hWnd; 242 struct stw_framebuffer *fb; 243 const struct stw_pixelformat_info *pfi; 244 245 /* We only support drawing to a window. */ 246 hWnd = WindowFromDC( hdc ); 247 if(!hWnd) 248 return NULL; 249 250 fb = CALLOC_STRUCT( stw_framebuffer ); 251 if (fb == NULL) 252 return NULL; 253 254 fb->hDC = hdc; 255 fb->hWnd = hWnd; 256 fb->iPixelFormat = iPixelFormat; 257 258 fb->pfi = pfi = stw_pixelformat_get_info( iPixelFormat - 1 ); 259 fb->stfb = stw_st_create_framebuffer( fb ); 260 if (!fb->stfb) { 261 FREE( fb ); 262 return NULL; 263 } 264 265 fb->refcnt = 1; 266 267 /* 268 * Windows can be sometimes have zero width and or height, but we ensure 269 * a non-zero framebuffer size at all times. 270 */ 271 272 fb->must_resize = TRUE; 273 fb->width = 1; 274 fb->height = 1; 275 fb->client_rect.left = 0; 276 fb->client_rect.top = 0; 277 fb->client_rect.right = fb->client_rect.left + fb->width; 278 fb->client_rect.bottom = fb->client_rect.top + fb->height; 279 280 stw_framebuffer_get_size(fb); 281 282 pipe_mutex_init( fb->mutex ); 283 284 /* This is the only case where we lock the stw_framebuffer::mutex before 285 * stw_dev::fb_mutex, since no other thread can know about this framebuffer 286 * and we must prevent any other thread from destroying it before we return. 287 */ 288 pipe_mutex_lock( fb->mutex ); 289 290 pipe_mutex_lock( stw_dev->fb_mutex ); 291 fb->next = stw_dev->fb_head; 292 stw_dev->fb_head = fb; 293 pipe_mutex_unlock( stw_dev->fb_mutex ); 294 295 return fb; 296} 297 298/** 299 * Have ptr reference fb. The referenced framebuffer should be locked. 300 */ 301void 302stw_framebuffer_reference( 303 struct stw_framebuffer **ptr, 304 struct stw_framebuffer *fb) 305{ 306 struct stw_framebuffer *old_fb = *ptr; 307 308 if (old_fb == fb) 309 return; 310 311 if (fb) 312 fb->refcnt++; 313 if (old_fb) { 314 pipe_mutex_lock(stw_dev->fb_mutex); 315 316 pipe_mutex_lock(old_fb->mutex); 317 stw_framebuffer_destroy_locked(old_fb); 318 319 pipe_mutex_unlock(stw_dev->fb_mutex); 320 } 321 322 *ptr = fb; 323} 324 325 326/** 327 * Update the framebuffer's size if necessary. 328 */ 329void 330stw_framebuffer_update( 331 struct stw_framebuffer *fb) 332{ 333 assert(fb->stfb); 334 assert(fb->height); 335 assert(fb->width); 336 337 /* XXX: It would be nice to avoid checking the size again -- in theory 338 * stw_call_window_proc would have cought the resize and stored the right 339 * size already, but unfortunately threads created before the DllMain is 340 * called don't get a DLL_THREAD_ATTACH notification, and there is no way 341 * to know of their existing without using the not very portable PSAPI. 342 */ 343 stw_framebuffer_get_size(fb); 344} 345 346 347void 348stw_framebuffer_cleanup( void ) 349{ 350 struct stw_framebuffer *fb; 351 struct stw_framebuffer *next; 352 353 if (!stw_dev) 354 return; 355 356 pipe_mutex_lock( stw_dev->fb_mutex ); 357 358 fb = stw_dev->fb_head; 359 while (fb) { 360 next = fb->next; 361 362 pipe_mutex_lock(fb->mutex); 363 stw_framebuffer_destroy_locked(fb); 364 365 fb = next; 366 } 367 stw_dev->fb_head = NULL; 368 369 pipe_mutex_unlock( stw_dev->fb_mutex ); 370} 371 372 373/** 374 * Given an hdc, return the corresponding stw_framebuffer. 375 */ 376static INLINE struct stw_framebuffer * 377stw_framebuffer_from_hdc_locked( 378 HDC hdc ) 379{ 380 HWND hwnd; 381 struct stw_framebuffer *fb; 382 383 /* 384 * Some applications create and use several HDCs for the same window, so 385 * looking up the framebuffer by the HDC is not reliable. Use HWND whenever 386 * possible. 387 */ 388 hwnd = WindowFromDC(hdc); 389 if(hwnd) 390 return stw_framebuffer_from_hwnd_locked(hwnd); 391 392 for (fb = stw_dev->fb_head; fb != NULL; fb = fb->next) 393 if (fb->hDC == hdc) { 394 pipe_mutex_lock(fb->mutex); 395 break; 396 } 397 398 return fb; 399} 400 401 402/** 403 * Given an hdc, return the corresponding stw_framebuffer. 404 */ 405struct stw_framebuffer * 406stw_framebuffer_from_hdc( 407 HDC hdc ) 408{ 409 struct stw_framebuffer *fb; 410 411 if (!stw_dev) 412 return NULL; 413 414 pipe_mutex_lock( stw_dev->fb_mutex ); 415 fb = stw_framebuffer_from_hdc_locked(hdc); 416 pipe_mutex_unlock( stw_dev->fb_mutex ); 417 418 return fb; 419} 420 421 422/** 423 * Given an hdc, return the corresponding stw_framebuffer. 424 */ 425struct stw_framebuffer * 426stw_framebuffer_from_hwnd( 427 HWND hwnd ) 428{ 429 struct stw_framebuffer *fb; 430 431 pipe_mutex_lock( stw_dev->fb_mutex ); 432 fb = stw_framebuffer_from_hwnd_locked(hwnd); 433 pipe_mutex_unlock( stw_dev->fb_mutex ); 434 435 return fb; 436} 437 438 439BOOL APIENTRY 440DrvSetPixelFormat( 441 HDC hdc, 442 LONG iPixelFormat ) 443{ 444 uint count; 445 uint index; 446 struct stw_framebuffer *fb; 447 448 if (!stw_dev) 449 return FALSE; 450 451 index = (uint) iPixelFormat - 1; 452 count = stw_pixelformat_get_extended_count(); 453 if (index >= count) 454 return FALSE; 455 456 fb = stw_framebuffer_from_hdc_locked(hdc); 457 if(fb) { 458 /* SetPixelFormat must be called only once */ 459 stw_framebuffer_release( fb ); 460 return FALSE; 461 } 462 463 fb = stw_framebuffer_create(hdc, iPixelFormat); 464 if(!fb) { 465 return FALSE; 466 } 467 468 stw_framebuffer_release( fb ); 469 470 /* Some applications mistakenly use the undocumented wglSetPixelFormat 471 * function instead of SetPixelFormat, so we call SetPixelFormat here to 472 * avoid opengl32.dll's wglCreateContext to fail */ 473 if (GetPixelFormat(hdc) == 0) { 474 SetPixelFormat(hdc, iPixelFormat, NULL); 475 } 476 477 return TRUE; 478} 479 480 481int 482stw_pixelformat_get( 483 HDC hdc ) 484{ 485 int iPixelFormat = 0; 486 struct stw_framebuffer *fb; 487 488 fb = stw_framebuffer_from_hdc(hdc); 489 if(fb) { 490 iPixelFormat = fb->iPixelFormat; 491 stw_framebuffer_release(fb); 492 } 493 494 return iPixelFormat; 495} 496 497 498BOOL APIENTRY 499DrvPresentBuffers(HDC hdc, PGLPRESENTBUFFERSDATA data) 500{ 501 struct stw_framebuffer *fb; 502 struct pipe_screen *screen; 503 struct pipe_resource *res; 504 505 if (!stw_dev) 506 return FALSE; 507 508 fb = stw_framebuffer_from_hdc( hdc ); 509 if (fb == NULL) 510 return FALSE; 511 512 screen = stw_dev->screen; 513 514 res = (struct pipe_resource *)data->pPrivateData; 515 516 if(data->hSharedSurface != fb->hSharedSurface) { 517 if(fb->shared_surface) { 518 stw_dev->stw_winsys->shared_surface_close(screen, fb->shared_surface); 519 fb->shared_surface = NULL; 520 } 521 522 fb->hSharedSurface = data->hSharedSurface; 523 524 if(data->hSharedSurface && 525 stw_dev->stw_winsys->shared_surface_open) { 526 fb->shared_surface = stw_dev->stw_winsys->shared_surface_open(screen, fb->hSharedSurface); 527 } 528 } 529 530 if(fb->shared_surface) { 531 stw_dev->stw_winsys->compose(screen, 532 res, 533 fb->shared_surface, 534 &fb->client_rect, 535 data->PresentHistoryToken); 536 } 537 else { 538 stw_dev->stw_winsys->present( screen, res, hdc ); 539 } 540 541 stw_framebuffer_update(fb); 542 stw_notify_current_locked(fb); 543 544 stw_framebuffer_release(fb); 545 546 return TRUE; 547} 548 549 550/** 551 * Queue a composition. 552 * 553 * It will drop the lock on success. 554 */ 555BOOL 556stw_framebuffer_present_locked(HDC hdc, 557 struct stw_framebuffer *fb, 558 struct pipe_resource *res) 559{ 560 if(stw_dev->callbacks.wglCbPresentBuffers && 561 stw_dev->stw_winsys->compose) { 562 GLCBPRESENTBUFFERSDATA data; 563 564 memset(&data, 0, sizeof data); 565 data.magic1 = 2; 566 data.magic2 = 0; 567 data.AdapterLuid = stw_dev->AdapterLuid; 568 data.rect = fb->client_rect; 569 data.pPrivateData = (void *)res; 570 571 stw_notify_current_locked(fb); 572 stw_framebuffer_release(fb); 573 574 return stw_dev->callbacks.wglCbPresentBuffers(hdc, &data); 575 } 576 else { 577 struct pipe_screen *screen = stw_dev->screen; 578 579 stw_dev->stw_winsys->present( screen, res, hdc ); 580 581 stw_framebuffer_update(fb); 582 stw_notify_current_locked(fb); 583 stw_framebuffer_release(fb); 584 585 return TRUE; 586 } 587} 588 589 590BOOL APIENTRY 591DrvSwapBuffers( 592 HDC hdc ) 593{ 594 struct stw_framebuffer *fb; 595 596 if (!stw_dev) 597 return FALSE; 598 599 fb = stw_framebuffer_from_hdc( hdc ); 600 if (fb == NULL) 601 return FALSE; 602 603 if (!(fb->pfi->pfd.dwFlags & PFD_DOUBLEBUFFER)) { 604 stw_framebuffer_release(fb); 605 return TRUE; 606 } 607 608 stw_flush_current_locked(fb); 609 610 return stw_st_swap_framebuffer_locked(fb->stfb); 611} 612 613 614BOOL APIENTRY 615DrvSwapLayerBuffers( 616 HDC hdc, 617 UINT fuPlanes ) 618{ 619 if(fuPlanes & WGL_SWAP_MAIN_PLANE) 620 return DrvSwapBuffers(hdc); 621 622 return FALSE; 623} 624