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