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_screen.h" 31 #include "util/u_memory.h" 32 #include "hud/hud_context.h" 33 #include "util/os_time.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 * If a stw_framebuffer is found, lock it and return the pointer. 49 * Else, return NULL. 50 */ 51 static struct stw_framebuffer * 52 stw_framebuffer_from_hwnd_locked(HWND hwnd) 53 { 54 struct stw_framebuffer *fb; 55 56 for (fb = stw_dev->fb_head; fb != NULL; fb = fb->next) 57 if (fb->hWnd == hwnd) { 58 stw_framebuffer_lock(fb); 59 assert(fb->mutex.RecursionCount == 1); 60 return fb; 61 } 62 63 return NULL; 64 } 65 66 67 /** 68 * Decrement the reference count on the given stw_framebuffer object. 69 * If the reference count hits zero, destroy the object. 70 * 71 * Note: Both stw_dev::fb_mutex and stw_framebuffer::mutex must already be 72 * locked. After this function completes, the fb's mutex will be unlocked. 73 */ 74 void 75 stw_framebuffer_release_locked(struct stw_framebuffer *fb) 76 { 77 struct stw_framebuffer **link; 78 79 assert(fb); 80 assert(stw_own_mutex(&fb->mutex)); 81 assert(stw_own_mutex(&stw_dev->fb_mutex)); 82 83 /* check the reference count */ 84 fb->refcnt--; 85 if (fb->refcnt) { 86 stw_framebuffer_unlock(fb); 87 return; 88 } 89 90 /* remove this stw_framebuffer from the device's linked list */ 91 link = &stw_dev->fb_head; 92 while (*link != fb) 93 link = &(*link)->next; 94 assert(*link); 95 *link = fb->next; 96 fb->next = NULL; 97 98 if (fb->shared_surface) 99 stw_dev->stw_winsys->shared_surface_close(stw_dev->screen, 100 fb->shared_surface); 101 102 stw_st_destroy_framebuffer_locked(fb->stfb); 103 104 stw_framebuffer_unlock(fb); 105 106 DeleteCriticalSection(&fb->mutex); 107 108 FREE( fb ); 109 } 110 111 112 /** 113 * Query the size of the given framebuffer's on-screen window and update 114 * the stw_framebuffer's width/height. 115 */ 116 static void 117 stw_framebuffer_get_size(struct stw_framebuffer *fb) 118 { 119 LONG width, height; 120 RECT client_rect; 121 RECT window_rect; 122 POINT client_pos; 123 124 /* 125 * Sanity checking. 126 */ 127 assert(fb->hWnd); 128 assert(fb->width && fb->height); 129 assert(fb->client_rect.right == fb->client_rect.left + fb->width); 130 assert(fb->client_rect.bottom == fb->client_rect.top + fb->height); 131 132 /* 133 * Get the client area size. 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 fb->minimized = width == 0 || height == 0; 145 146 if (width <= 0 || height <= 0) { 147 /* 148 * When the window is minimized GetClientRect will return zeros. Simply 149 * preserve the current window size, until the window is restored or 150 * maximized again. 151 */ 152 return; 153 } 154 155 if (width != fb->width || height != fb->height) { 156 fb->must_resize = TRUE; 157 fb->width = width; 158 fb->height = height; 159 } 160 161 client_pos.x = 0; 162 client_pos.y = 0; 163 if (ClientToScreen(fb->hWnd, &client_pos) && 164 GetWindowRect(fb->hWnd, &window_rect)) { 165 fb->client_rect.left = client_pos.x - window_rect.left; 166 fb->client_rect.top = client_pos.y - window_rect.top; 167 } 168 169 fb->client_rect.right = fb->client_rect.left + fb->width; 170 fb->client_rect.bottom = fb->client_rect.top + fb->height; 171 172 #if 0 173 debug_printf("\n"); 174 debug_printf("%s: hwnd = %p\n", __FUNCTION__, fb->hWnd); 175 debug_printf("%s: client_position = (%li, %li)\n", 176 __FUNCTION__, client_pos.x, client_pos.y); 177 debug_printf("%s: window_rect = (%li, %li) - (%li, %li)\n", 178 __FUNCTION__, 179 window_rect.left, window_rect.top, 180 window_rect.right, window_rect.bottom); 181 debug_printf("%s: client_rect = (%li, %li) - (%li, %li)\n", 182 __FUNCTION__, 183 fb->client_rect.left, fb->client_rect.top, 184 fb->client_rect.right, fb->client_rect.bottom); 185 #endif 186 } 187 188 189 /** 190 * @sa http://msdn.microsoft.com/en-us/library/ms644975(VS.85).aspx 191 * @sa http://msdn.microsoft.com/en-us/library/ms644960(VS.85).aspx 192 */ 193 LRESULT CALLBACK 194 stw_call_window_proc(int nCode, WPARAM wParam, LPARAM lParam) 195 { 196 struct stw_tls_data *tls_data; 197 PCWPSTRUCT pParams = (PCWPSTRUCT)lParam; 198 struct stw_framebuffer *fb; 199 200 tls_data = stw_tls_get_data(); 201 if (!tls_data) 202 return 0; 203 204 if (nCode < 0 || !stw_dev) 205 return CallNextHookEx(tls_data->hCallWndProcHook, nCode, wParam, lParam); 206 207 /* We check that the stw_dev object is initialized before we try to do 208 * anything with it. Otherwise, in multi-threaded programs there's a 209 * chance of executing this code before the stw_dev object is fully 210 * initialized. 211 */ 212 if (stw_dev && stw_dev->initialized) { 213 if (pParams->message == WM_WINDOWPOSCHANGED) { 214 /* We handle WM_WINDOWPOSCHANGED instead of WM_SIZE because according 215 * to http://blogs.msdn.com/oldnewthing/archive/2008/01/15/7113860.aspx 216 * WM_SIZE is generated from WM_WINDOWPOSCHANGED by DefWindowProc so it 217 * can be masked out by the application. 218 */ 219 LPWINDOWPOS lpWindowPos = (LPWINDOWPOS)pParams->lParam; 220 if ((lpWindowPos->flags & SWP_SHOWWINDOW) || 221 !(lpWindowPos->flags & SWP_NOMOVE) || 222 !(lpWindowPos->flags & SWP_NOSIZE)) { 223 fb = stw_framebuffer_from_hwnd( pParams->hwnd ); 224 if (fb) { 225 /* Size in WINDOWPOS includes the window frame, so get the size 226 * of the client area via GetClientRect. 227 */ 228 stw_framebuffer_get_size(fb); 229 stw_framebuffer_unlock(fb); 230 } 231 } 232 } 233 else if (pParams->message == WM_DESTROY) { 234 stw_lock_framebuffers(stw_dev); 235 fb = stw_framebuffer_from_hwnd_locked( pParams->hwnd ); 236 if (fb) 237 stw_framebuffer_release_locked(fb); 238 stw_unlock_framebuffers(stw_dev); 239 } 240 } 241 242 return CallNextHookEx(tls_data->hCallWndProcHook, nCode, wParam, lParam); 243 } 244 245 246 /** 247 * Create a new stw_framebuffer object which corresponds to the given 248 * HDC/window. If successful, we return the new stw_framebuffer object 249 * with its mutex locked. 250 */ 251 struct stw_framebuffer * 252 stw_framebuffer_create(HDC hdc, int iPixelFormat) 253 { 254 HWND hWnd; 255 struct stw_framebuffer *fb; 256 const struct stw_pixelformat_info *pfi; 257 258 /* We only support drawing to a window. */ 259 hWnd = WindowFromDC( hdc ); 260 if (!hWnd) 261 return NULL; 262 263 fb = CALLOC_STRUCT( stw_framebuffer ); 264 if (fb == NULL) 265 return NULL; 266 267 fb->hWnd = hWnd; 268 fb->iPixelFormat = iPixelFormat; 269 270 /* 271 * We often need a displayable pixel format to make GDI happy. Set it 272 * here (always 1, i.e., out first pixel format) where appropriate. 273 */ 274 fb->iDisplayablePixelFormat = iPixelFormat <= stw_dev->pixelformat_count 275 ? iPixelFormat : 1; 276 277 fb->pfi = pfi = stw_pixelformat_get_info( iPixelFormat ); 278 fb->stfb = stw_st_create_framebuffer( fb ); 279 if (!fb->stfb) { 280 FREE( fb ); 281 return NULL; 282 } 283 284 fb->refcnt = 1; 285 286 /* 287 * Windows can be sometimes have zero width and or height, but we ensure 288 * a non-zero framebuffer size at all times. 289 */ 290 291 fb->must_resize = TRUE; 292 fb->width = 1; 293 fb->height = 1; 294 fb->client_rect.left = 0; 295 fb->client_rect.top = 0; 296 fb->client_rect.right = fb->client_rect.left + fb->width; 297 fb->client_rect.bottom = fb->client_rect.top + fb->height; 298 299 stw_framebuffer_get_size(fb); 300 301 InitializeCriticalSection(&fb->mutex); 302 303 /* This is the only case where we lock the stw_framebuffer::mutex before 304 * stw_dev::fb_mutex, since no other thread can know about this framebuffer 305 * and we must prevent any other thread from destroying it before we return. 306 */ 307 stw_framebuffer_lock(fb); 308 309 stw_lock_framebuffers(stw_dev); 310 fb->next = stw_dev->fb_head; 311 stw_dev->fb_head = fb; 312 stw_unlock_framebuffers(stw_dev); 313 314 return fb; 315 } 316 317 318 /** 319 * Update the framebuffer's size if necessary. 320 */ 321 void 322 stw_framebuffer_update(struct stw_framebuffer *fb) 323 { 324 assert(fb->stfb); 325 assert(fb->height); 326 assert(fb->width); 327 328 /* XXX: It would be nice to avoid checking the size again -- in theory 329 * stw_call_window_proc would have cought the resize and stored the right 330 * size already, but unfortunately threads created before the DllMain is 331 * called don't get a DLL_THREAD_ATTACH notification, and there is no way 332 * to know of their existing without using the not very portable PSAPI. 333 */ 334 stw_framebuffer_get_size(fb); 335 } 336 337 338 /** 339 * Try to free all stw_framebuffer objects associated with the device. 340 */ 341 void 342 stw_framebuffer_cleanup(void) 343 { 344 struct stw_framebuffer *fb; 345 struct stw_framebuffer *next; 346 347 if (!stw_dev) 348 return; 349 350 stw_lock_framebuffers(stw_dev); 351 352 fb = stw_dev->fb_head; 353 while (fb) { 354 next = fb->next; 355 356 stw_framebuffer_lock(fb); 357 stw_framebuffer_release_locked(fb); 358 359 fb = next; 360 } 361 stw_dev->fb_head = NULL; 362 363 stw_unlock_framebuffers(stw_dev); 364 } 365 366 367 /** 368 * Given an hdc, return the corresponding stw_framebuffer. 369 * The returned stw_framebuffer will have its mutex locked. 370 */ 371 static struct stw_framebuffer * 372 stw_framebuffer_from_hdc_locked(HDC hdc) 373 { 374 HWND hwnd; 375 376 hwnd = WindowFromDC(hdc); 377 if (!hwnd) { 378 return NULL; 379 } 380 381 return stw_framebuffer_from_hwnd_locked(hwnd); 382 } 383 384 385 /** 386 * Given an HDC, return the corresponding stw_framebuffer. 387 * The returned stw_framebuffer will have its mutex locked. 388 */ 389 struct stw_framebuffer * 390 stw_framebuffer_from_hdc(HDC hdc) 391 { 392 struct stw_framebuffer *fb; 393 394 if (!stw_dev) 395 return NULL; 396 397 stw_lock_framebuffers(stw_dev); 398 fb = stw_framebuffer_from_hdc_locked(hdc); 399 stw_unlock_framebuffers(stw_dev); 400 401 return fb; 402 } 403 404 405 /** 406 * Given an HWND, return the corresponding stw_framebuffer. 407 * The returned stw_framebuffer will have its mutex locked. 408 */ 409 struct stw_framebuffer * 410 stw_framebuffer_from_hwnd(HWND hwnd) 411 { 412 struct stw_framebuffer *fb; 413 414 stw_lock_framebuffers(stw_dev); 415 fb = stw_framebuffer_from_hwnd_locked(hwnd); 416 stw_unlock_framebuffers(stw_dev); 417 418 return fb; 419 } 420 421 422 BOOL APIENTRY 423 DrvSetPixelFormat(HDC hdc, LONG iPixelFormat) 424 { 425 uint count; 426 uint index; 427 struct stw_framebuffer *fb; 428 429 if (!stw_dev) 430 return FALSE; 431 432 index = (uint) iPixelFormat - 1; 433 count = stw_pixelformat_get_count(); 434 if (index >= count) 435 return FALSE; 436 437 fb = stw_framebuffer_from_hdc_locked(hdc); 438 if (fb) { 439 /* 440 * SetPixelFormat must be called only once. However ignore 441 * pbuffers, for which the framebuffer object is created first. 442 */ 443 boolean bPbuffer = fb->bPbuffer; 444 445 stw_framebuffer_unlock( fb ); 446 447 return bPbuffer; 448 } 449 450 fb = stw_framebuffer_create(hdc, iPixelFormat); 451 if (!fb) { 452 return FALSE; 453 } 454 455 stw_framebuffer_unlock( fb ); 456 457 /* Some applications mistakenly use the undocumented wglSetPixelFormat 458 * function instead of SetPixelFormat, so we call SetPixelFormat here to 459 * avoid opengl32.dll's wglCreateContext to fail */ 460 if (GetPixelFormat(hdc) == 0) { 461 BOOL bRet = SetPixelFormat(hdc, iPixelFormat, NULL); 462 if (!bRet) { 463 debug_printf("SetPixelFormat failed\n"); 464 } 465 } 466 467 return TRUE; 468 } 469 470 471 int 472 stw_pixelformat_get(HDC hdc) 473 { 474 int iPixelFormat = 0; 475 struct stw_framebuffer *fb; 476 477 fb = stw_framebuffer_from_hdc(hdc); 478 if (fb) { 479 iPixelFormat = fb->iPixelFormat; 480 stw_framebuffer_unlock(fb); 481 } 482 483 return iPixelFormat; 484 } 485 486 487 BOOL APIENTRY 488 DrvPresentBuffers(HDC hdc, PGLPRESENTBUFFERSDATA data) 489 { 490 struct stw_framebuffer *fb; 491 struct pipe_screen *screen; 492 struct pipe_resource *res; 493 494 if (!stw_dev) 495 return FALSE; 496 497 fb = stw_framebuffer_from_hdc( hdc ); 498 if (fb == NULL) 499 return FALSE; 500 501 screen = stw_dev->screen; 502 503 res = (struct pipe_resource *)data->pPrivateData; 504 505 if (data->hSharedSurface != fb->hSharedSurface) { 506 if (fb->shared_surface) { 507 stw_dev->stw_winsys->shared_surface_close(screen, fb->shared_surface); 508 fb->shared_surface = NULL; 509 } 510 511 fb->hSharedSurface = data->hSharedSurface; 512 513 if (data->hSharedSurface && 514 stw_dev->stw_winsys->shared_surface_open) { 515 fb->shared_surface = 516 stw_dev->stw_winsys->shared_surface_open(screen, 517 fb->hSharedSurface); 518 } 519 } 520 521 if (!fb->minimized) { 522 if (fb->shared_surface) { 523 stw_dev->stw_winsys->compose(screen, 524 res, 525 fb->shared_surface, 526 &fb->client_rect, 527 data->PresentHistoryToken); 528 } 529 else { 530 stw_dev->stw_winsys->present( screen, res, hdc ); 531 } 532 } 533 534 stw_framebuffer_update(fb); 535 stw_notify_current_locked(fb); 536 537 stw_framebuffer_unlock(fb); 538 539 return TRUE; 540 } 541 542 543 /** 544 * Queue a composition. 545 * 546 * The stw_framebuffer object must have its mutex locked. The mutex will 547 * be unlocked here before returning. 548 */ 549 BOOL 550 stw_framebuffer_present_locked(HDC hdc, 551 struct stw_framebuffer *fb, 552 struct pipe_resource *res) 553 { 554 if (stw_dev->callbacks.wglCbPresentBuffers && 555 stw_dev->stw_winsys->compose) { 556 GLCBPRESENTBUFFERSDATA data; 557 558 memset(&data, 0, sizeof data); 559 data.magic1 = 2; 560 data.magic2 = 0; 561 data.AdapterLuid = stw_dev->AdapterLuid; 562 data.rect = fb->client_rect; 563 data.pPrivateData = (void *)res; 564 565 stw_notify_current_locked(fb); 566 stw_framebuffer_unlock(fb); 567 568 return stw_dev->callbacks.wglCbPresentBuffers(hdc, &data); 569 } 570 else { 571 struct pipe_screen *screen = stw_dev->screen; 572 573 stw_dev->stw_winsys->present( screen, res, hdc ); 574 575 stw_framebuffer_update(fb); 576 stw_notify_current_locked(fb); 577 stw_framebuffer_unlock(fb); 578 579 return TRUE; 580 } 581 } 582 583 584 /** 585 * This is called just before issuing the buffer swap/present. 586 * We query the current time and determine if we should sleep before 587 * issuing the swap/present. 588 * This is a bit of a hack and is certainly not very accurate but it 589 * basically works. 590 * This is for the WGL_ARB_swap_interval extension. 591 */ 592 static void 593 wait_swap_interval(struct stw_framebuffer *fb) 594 { 595 /* Note: all time variables here are in units of microseconds */ 596 int64_t cur_time = os_time_get_nano() / 1000; 597 598 if (fb->prev_swap_time != 0) { 599 /* Compute time since previous swap */ 600 int64_t delta = cur_time - fb->prev_swap_time; 601 int64_t min_swap_period = 602 1.0e6 / stw_dev->refresh_rate * stw_dev->swap_interval; 603 604 /* If time since last swap is less than wait period, wait. 605 * Note that it's possible for the delta to be negative because of 606 * rollover. See https://bugs.freedesktop.org/show_bug.cgi?id=102241 607 */ 608 if ((delta >= 0) && (delta < min_swap_period)) { 609 float fudge = 1.75f; /* emperical fudge factor */ 610 int64_t wait = (min_swap_period - delta) * fudge; 611 os_time_sleep(wait); 612 } 613 } 614 615 fb->prev_swap_time = cur_time; 616 } 617 618 619 BOOL APIENTRY 620 DrvSwapBuffers(HDC hdc) 621 { 622 struct stw_context *ctx; 623 struct stw_framebuffer *fb; 624 625 if (!stw_dev) 626 return FALSE; 627 628 fb = stw_framebuffer_from_hdc( hdc ); 629 if (fb == NULL) 630 return FALSE; 631 632 if (!(fb->pfi->pfd.dwFlags & PFD_DOUBLEBUFFER)) { 633 stw_framebuffer_unlock(fb); 634 return TRUE; 635 } 636 637 ctx = stw_current_context(); 638 if (ctx) { 639 if (ctx->hud) { 640 /* Display the HUD */ 641 struct pipe_resource *back = 642 stw_get_framebuffer_resource(fb->stfb, ST_ATTACHMENT_BACK_LEFT); 643 if (back) { 644 hud_run(ctx->hud, NULL, back); 645 } 646 } 647 648 if (ctx->current_framebuffer == fb) { 649 /* flush current context */ 650 ctx->st->flush(ctx->st, ST_FLUSH_END_OF_FRAME, NULL); 651 } 652 } 653 654 if (stw_dev->swap_interval != 0) { 655 wait_swap_interval(fb); 656 } 657 658 return stw_st_swap_framebuffer_locked(hdc, fb->stfb); 659 } 660 661 662 BOOL APIENTRY 663 DrvSwapLayerBuffers(HDC hdc, UINT fuPlanes) 664 { 665 if (fuPlanes & WGL_SWAP_MAIN_PLANE) 666 return DrvSwapBuffers(hdc); 667 668 return FALSE; 669 } 670