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