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