stw_framebuffer.c revision de27e272f42f8f453588edbcb98f3e510dad869b
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 "main/context.h" 31#include "pipe/p_format.h" 32#include "pipe/p_screen.h" 33#include "util/u_format.h" 34#include "state_tracker/st_context.h" 35#include "state_tracker/st_public.h" 36 37#include "stw_icd.h" 38#include "stw_framebuffer.h" 39#include "stw_device.h" 40#include "stw_winsys.h" 41#include "stw_tls.h" 42 43 44/** 45 * Search the framebuffer with the matching HWND while holding the 46 * stw_dev::fb_mutex global lock. 47 */ 48static INLINE struct stw_framebuffer * 49stw_framebuffer_from_hwnd_locked( 50 HWND hwnd ) 51{ 52 struct stw_framebuffer *fb; 53 54 for (fb = stw_dev->fb_head; fb != NULL; fb = fb->next) 55 if (fb->hWnd == hwnd) { 56 pipe_mutex_lock(fb->mutex); 57 break; 58 } 59 60 return fb; 61} 62 63 64/** 65 * Destroy this framebuffer. Both stw_dev::fb_mutex and stw_framebuffer::mutex 66 * must be held, by this order. Obviously no further access to fb can be done 67 * after this. 68 */ 69static INLINE void 70stw_framebuffer_destroy_locked( 71 struct stw_framebuffer *fb ) 72{ 73 struct stw_framebuffer **link; 74 75 link = &stw_dev->fb_head; 76 while (*link != fb) 77 link = &(*link)->next; 78 assert(*link); 79 *link = fb->next; 80 fb->next = NULL; 81 82 if(fb->shared_surface) 83 stw_dev->stw_winsys->shared_surface_close(stw_dev->screen, fb->shared_surface); 84 85 st_unreference_framebuffer(fb->stfb); 86 87 pipe_mutex_unlock( fb->mutex ); 88 89 pipe_mutex_destroy( fb->mutex ); 90 91 FREE( fb ); 92} 93 94 95void 96stw_framebuffer_release( 97 struct stw_framebuffer *fb) 98{ 99 assert(fb); 100 pipe_mutex_unlock( fb->mutex ); 101} 102 103 104static INLINE void 105stw_framebuffer_get_size( struct stw_framebuffer *fb ) 106{ 107 unsigned width, height; 108 RECT client_rect; 109 RECT window_rect; 110 POINT client_pos; 111 112 assert(fb->hWnd); 113 114 /* Get the client area size. */ 115 GetClientRect( fb->hWnd, &client_rect ); 116 assert(client_rect.left == 0); 117 assert(client_rect.top == 0); 118 width = client_rect.right - client_rect.left; 119 height = client_rect.bottom - client_rect.top; 120 121 if(width < 1) 122 width = 1; 123 if(height < 1) 124 height = 1; 125 126 if(width != fb->width || height != fb->height) { 127 fb->must_resize = TRUE; 128 fb->width = width; 129 fb->height = height; 130 } 131 132 client_pos.x = 0; 133 client_pos.y = 0; 134 ClientToScreen(fb->hWnd, &client_pos); 135 136 GetWindowRect(fb->hWnd, &window_rect); 137 138 fb->client_rect.left = client_pos.x - window_rect.left; 139 fb->client_rect.top = client_pos.y - window_rect.top; 140 fb->client_rect.right = fb->client_rect.left + fb->width; 141 fb->client_rect.bottom = fb->client_rect.top + fb->height; 142 143#if 0 144 debug_printf("\n"); 145 debug_printf("%s: client_position = (%i, %i)\n", 146 __FUNCTION__, client_pos.x, client_pos.y); 147 debug_printf("%s: window_rect = (%i, %i) - (%i, %i)\n", 148 __FUNCTION__, 149 window_rect.left, window_rect.top, 150 window_rect.right, window_rect.bottom); 151 debug_printf("%s: client_rect = (%i, %i) - (%i, %i)\n", 152 __FUNCTION__, 153 fb->client_rect.left, fb->client_rect.top, 154 fb->client_rect.right, fb->client_rect.bottom); 155#endif 156} 157 158 159/** 160 * @sa http://msdn.microsoft.com/en-us/library/ms644975(VS.85).aspx 161 * @sa http://msdn.microsoft.com/en-us/library/ms644960(VS.85).aspx 162 */ 163LRESULT CALLBACK 164stw_call_window_proc( 165 int nCode, 166 WPARAM wParam, 167 LPARAM lParam ) 168{ 169 struct stw_tls_data *tls_data; 170 PCWPSTRUCT pParams = (PCWPSTRUCT)lParam; 171 struct stw_framebuffer *fb; 172 173 tls_data = stw_tls_get_data(); 174 if(!tls_data) 175 return 0; 176 177 if (nCode < 0 || !stw_dev) 178 return CallNextHookEx(tls_data->hCallWndProcHook, nCode, wParam, lParam); 179 180 if (pParams->message == WM_WINDOWPOSCHANGED) { 181 /* We handle WM_WINDOWPOSCHANGED instead of WM_SIZE because according to 182 * http://blogs.msdn.com/oldnewthing/archive/2008/01/15/7113860.aspx 183 * WM_SIZE is generated from WM_WINDOWPOSCHANGED by DefWindowProc so it 184 * can be masked out by the application. */ 185 LPWINDOWPOS lpWindowPos = (LPWINDOWPOS)pParams->lParam; 186 if((lpWindowPos->flags & SWP_SHOWWINDOW) || 187 !(lpWindowPos->flags & SWP_NOMOVE) || 188 !(lpWindowPos->flags & SWP_NOSIZE)) { 189 fb = stw_framebuffer_from_hwnd( pParams->hwnd ); 190 if(fb) { 191 /* Size in WINDOWPOS includes the window frame, so get the size 192 * of the client area via GetClientRect. */ 193 stw_framebuffer_get_size(fb); 194 stw_framebuffer_release(fb); 195 } 196 } 197 } 198 else if (pParams->message == WM_DESTROY) { 199 pipe_mutex_lock( stw_dev->fb_mutex ); 200 fb = stw_framebuffer_from_hwnd_locked( pParams->hwnd ); 201 if(fb) 202 stw_framebuffer_destroy_locked(fb); 203 pipe_mutex_unlock( stw_dev->fb_mutex ); 204 } 205 206 return CallNextHookEx(tls_data->hCallWndProcHook, nCode, wParam, lParam); 207} 208 209 210struct stw_framebuffer * 211stw_framebuffer_create( 212 HDC hdc, 213 int iPixelFormat ) 214{ 215 HWND hWnd; 216 struct stw_framebuffer *fb; 217 const struct stw_pixelformat_info *pfi; 218 219 /* We only support drawing to a window. */ 220 hWnd = WindowFromDC( hdc ); 221 if(!hWnd) 222 return NULL; 223 224 fb = CALLOC_STRUCT( stw_framebuffer ); 225 if (fb == NULL) 226 return NULL; 227 228 fb->hDC = hdc; 229 fb->hWnd = hWnd; 230 fb->iPixelFormat = iPixelFormat; 231 232 fb->pfi = pfi = stw_pixelformat_get_info( iPixelFormat - 1 ); 233 234 stw_pixelformat_visual(&fb->visual, pfi); 235 236 stw_framebuffer_get_size(fb); 237 238 pipe_mutex_init( fb->mutex ); 239 240 /* This is the only case where we lock the stw_framebuffer::mutex before 241 * stw_dev::fb_mutex, since no other thread can know about this framebuffer 242 * and we must prevent any other thread from destroying it before we return. 243 */ 244 pipe_mutex_lock( fb->mutex ); 245 246 pipe_mutex_lock( stw_dev->fb_mutex ); 247 fb->next = stw_dev->fb_head; 248 stw_dev->fb_head = fb; 249 pipe_mutex_unlock( stw_dev->fb_mutex ); 250 251 return fb; 252} 253 254 255BOOL 256stw_framebuffer_allocate( 257 struct stw_framebuffer *fb) 258{ 259 assert(fb); 260 261 if(!fb->stfb) { 262 const struct stw_pixelformat_info *pfi = fb->pfi; 263 enum pipe_format colorFormat, depthFormat, stencilFormat; 264 265 colorFormat = pfi->color_format; 266 267 if(util_format_get_component_bits(pfi->depth_stencil_format, UTIL_FORMAT_COLORSPACE_ZS, 0)) 268 depthFormat = pfi->depth_stencil_format; 269 else 270 depthFormat = PIPE_FORMAT_NONE; 271 272 if(util_format_get_component_bits(pfi->depth_stencil_format, UTIL_FORMAT_COLORSPACE_ZS, 1)) 273 stencilFormat = pfi->depth_stencil_format; 274 else 275 stencilFormat = PIPE_FORMAT_NONE; 276 277 assert(fb->must_resize); 278 assert(fb->width); 279 assert(fb->height); 280 281 fb->stfb = st_create_framebuffer( 282 &fb->visual, 283 colorFormat, 284 depthFormat, 285 stencilFormat, 286 fb->width, 287 fb->height, 288 (void *) fb ); 289 290 // to notify the context 291 fb->must_resize = TRUE; 292 } 293 294 return fb->stfb ? TRUE : FALSE; 295} 296 297 298/** 299 * Update the framebuffer's size if necessary. 300 */ 301void 302stw_framebuffer_update( 303 struct stw_framebuffer *fb) 304{ 305 assert(fb->stfb); 306 assert(fb->height); 307 assert(fb->width); 308 309 /* XXX: It would be nice to avoid checking the size again -- in theory 310 * stw_call_window_proc would have cought the resize and stored the right 311 * size already, but unfortunately threads created before the DllMain is 312 * called don't get a DLL_THREAD_ATTACH notification, and there is no way 313 * to know of their existing without using the not very portable PSAPI. 314 */ 315 stw_framebuffer_get_size(fb); 316 317 if(fb->must_resize) { 318 st_resize_framebuffer(fb->stfb, fb->width, fb->height); 319 fb->must_resize = FALSE; 320 } 321} 322 323 324void 325stw_framebuffer_cleanup( void ) 326{ 327 struct stw_framebuffer *fb; 328 struct stw_framebuffer *next; 329 330 if (!stw_dev) 331 return; 332 333 pipe_mutex_lock( stw_dev->fb_mutex ); 334 335 fb = stw_dev->fb_head; 336 while (fb) { 337 next = fb->next; 338 339 pipe_mutex_lock(fb->mutex); 340 stw_framebuffer_destroy_locked(fb); 341 342 fb = next; 343 } 344 stw_dev->fb_head = NULL; 345 346 pipe_mutex_unlock( stw_dev->fb_mutex ); 347} 348 349 350/** 351 * Given an hdc, return the corresponding stw_framebuffer. 352 */ 353static INLINE struct stw_framebuffer * 354stw_framebuffer_from_hdc_locked( 355 HDC hdc ) 356{ 357 HWND hwnd; 358 struct stw_framebuffer *fb; 359 360 /* 361 * Some applications create and use several HDCs for the same window, so 362 * looking up the framebuffer by the HDC is not reliable. Use HWND whenever 363 * possible. 364 */ 365 hwnd = WindowFromDC(hdc); 366 if(hwnd) 367 return stw_framebuffer_from_hwnd_locked(hwnd); 368 369 for (fb = stw_dev->fb_head; fb != NULL; fb = fb->next) 370 if (fb->hDC == hdc) { 371 pipe_mutex_lock(fb->mutex); 372 break; 373 } 374 375 return fb; 376} 377 378 379/** 380 * Given an hdc, return the corresponding stw_framebuffer. 381 */ 382struct stw_framebuffer * 383stw_framebuffer_from_hdc( 384 HDC hdc ) 385{ 386 struct stw_framebuffer *fb; 387 388 if (!stw_dev) 389 return NULL; 390 391 pipe_mutex_lock( stw_dev->fb_mutex ); 392 fb = stw_framebuffer_from_hdc_locked(hdc); 393 pipe_mutex_unlock( stw_dev->fb_mutex ); 394 395 return fb; 396} 397 398 399/** 400 * Given an hdc, return the corresponding stw_framebuffer. 401 */ 402struct stw_framebuffer * 403stw_framebuffer_from_hwnd( 404 HWND hwnd ) 405{ 406 struct stw_framebuffer *fb; 407 408 pipe_mutex_lock( stw_dev->fb_mutex ); 409 fb = stw_framebuffer_from_hwnd_locked(hwnd); 410 pipe_mutex_unlock( stw_dev->fb_mutex ); 411 412 return fb; 413} 414 415 416BOOL APIENTRY 417DrvSetPixelFormat( 418 HDC hdc, 419 LONG iPixelFormat ) 420{ 421 uint count; 422 uint index; 423 struct stw_framebuffer *fb; 424 425 if (!stw_dev) 426 return FALSE; 427 428 index = (uint) iPixelFormat - 1; 429 count = stw_pixelformat_get_extended_count(); 430 if (index >= count) 431 return FALSE; 432 433 fb = stw_framebuffer_from_hdc_locked(hdc); 434 if(fb) { 435 /* SetPixelFormat must be called only once */ 436 stw_framebuffer_release( fb ); 437 return FALSE; 438 } 439 440 fb = stw_framebuffer_create(hdc, iPixelFormat); 441 if(!fb) { 442 return FALSE; 443 } 444 445 stw_framebuffer_release( fb ); 446 447 /* Some applications mistakenly use the undocumented wglSetPixelFormat 448 * function instead of SetPixelFormat, so we call SetPixelFormat here to 449 * avoid opengl32.dll's wglCreateContext to fail */ 450 if (GetPixelFormat(hdc) == 0) { 451 SetPixelFormat(hdc, iPixelFormat, NULL); 452 } 453 454 return TRUE; 455} 456 457 458int 459stw_pixelformat_get( 460 HDC hdc ) 461{ 462 int iPixelFormat = 0; 463 struct stw_framebuffer *fb; 464 465 fb = stw_framebuffer_from_hdc(hdc); 466 if(fb) { 467 iPixelFormat = fb->iPixelFormat; 468 stw_framebuffer_release(fb); 469 } 470 471 return iPixelFormat; 472} 473 474 475BOOL APIENTRY 476DrvPresentBuffers(HDC hdc, PGLPRESENTBUFFERSDATA data) 477{ 478 struct stw_framebuffer *fb; 479 struct pipe_screen *screen; 480 struct pipe_surface *surface; 481 482 if (!stw_dev) 483 return FALSE; 484 485 fb = stw_framebuffer_from_hdc( hdc ); 486 if (fb == NULL) 487 return FALSE; 488 489 screen = stw_dev->screen; 490 491 surface = (struct pipe_surface *)data->pPrivateData; 492 493 if(data->hSharedSurface != fb->hSharedSurface) { 494 if(fb->shared_surface) { 495 stw_dev->stw_winsys->shared_surface_close(screen, fb->shared_surface); 496 fb->shared_surface = NULL; 497 } 498 499 fb->hSharedSurface = data->hSharedSurface; 500 501 if(data->hSharedSurface && 502 stw_dev->stw_winsys->shared_surface_open) { 503 fb->shared_surface = stw_dev->stw_winsys->shared_surface_open(screen, fb->hSharedSurface); 504 } 505 } 506 507 if(fb->shared_surface) { 508 stw_dev->stw_winsys->compose(screen, 509 surface, 510 fb->shared_surface, 511 &fb->client_rect, 512 data->PresentHistoryToken); 513 } 514 else { 515 stw_dev->stw_winsys->present( screen, surface, hdc ); 516 } 517 518 stw_framebuffer_update(fb); 519 520 stw_framebuffer_release(fb); 521 522 return TRUE; 523} 524 525 526/** 527 * Queue a composition. 528 * 529 * It will drop the lock on success. 530 */ 531BOOL 532stw_framebuffer_present_locked(HDC hdc, 533 struct stw_framebuffer *fb, 534 struct pipe_surface *surface) 535{ 536 if(stw_dev->callbacks.wglCbPresentBuffers && 537 stw_dev->stw_winsys->compose) { 538 GLCBPRESENTBUFFERSDATA data; 539 540 memset(&data, 0, sizeof data); 541 data.magic1 = 2; 542 data.magic2 = 0; 543 data.AdapterLuid = stw_dev->AdapterLuid; 544 data.rect = fb->client_rect; 545 data.pPrivateData = (void *)surface; 546 547 stw_framebuffer_release(fb); 548 549 return stw_dev->callbacks.wglCbPresentBuffers(hdc, &data); 550 } 551 else { 552 struct pipe_screen *screen = stw_dev->screen; 553 554 stw_dev->stw_winsys->present( screen, surface, hdc ); 555 556 stw_framebuffer_update(fb); 557 558 stw_framebuffer_release(fb); 559 560 return TRUE; 561 } 562} 563 564 565BOOL APIENTRY 566DrvSwapBuffers( 567 HDC hdc ) 568{ 569 struct stw_framebuffer *fb; 570 struct pipe_surface *surface = NULL; 571 572 if (!stw_dev) 573 return FALSE; 574 575 fb = stw_framebuffer_from_hdc( hdc ); 576 if (fb == NULL) 577 return FALSE; 578 579 if (!(fb->pfi->pfd.dwFlags & PFD_DOUBLEBUFFER)) { 580 stw_framebuffer_release(fb); 581 return TRUE; 582 } 583 584 st_swapbuffers(fb->stfb, &surface, NULL); 585 586 return stw_framebuffer_present_locked(hdc, fb, surface); 587} 588 589 590BOOL APIENTRY 591DrvSwapLayerBuffers( 592 HDC hdc, 593 UINT fuPlanes ) 594{ 595 if(fuPlanes & WGL_SWAP_MAIN_PLANE) 596 return DrvSwapBuffers(hdc); 597 598 return FALSE; 599} 600