diff options
Diffstat (limited to 'src/gallium/state_trackers/wgl/shared/stw_framebuffer.c')
-rw-r--r-- | src/gallium/state_trackers/wgl/shared/stw_framebuffer.c | 291 |
1 files changed, 162 insertions, 129 deletions
diff --git a/src/gallium/state_trackers/wgl/shared/stw_framebuffer.c b/src/gallium/state_trackers/wgl/shared/stw_framebuffer.c index 58f1830319..b8956bb550 100644 --- a/src/gallium/state_trackers/wgl/shared/stw_framebuffer.c +++ b/src/gallium/state_trackers/wgl/shared/stw_framebuffer.c @@ -45,6 +45,10 @@ #include "stw_tls.h" +/** + * Search the framebuffer with the matching HWND while holding the + * stw_dev::fb_mutex global lock. + */ static INLINE struct stw_framebuffer * stw_framebuffer_from_hwnd_locked( HWND hwnd ) @@ -52,13 +56,20 @@ stw_framebuffer_from_hwnd_locked( struct stw_framebuffer *fb; for (fb = stw_dev->fb_head; fb != NULL; fb = fb->next) - if (fb->hWnd == hwnd) + if (fb->hWnd == hwnd) { + pipe_mutex_lock(fb->mutex); break; + } return fb; } +/** + * Destroy this framebuffer. Both stw_dev::fb_mutex and stw_framebuffer::mutex + * must be held, by this order. Obviously no further access to fb can be done + * after this. + */ static INLINE void stw_framebuffer_destroy_locked( struct stw_framebuffer *fb ) @@ -74,17 +85,53 @@ stw_framebuffer_destroy_locked( st_unreference_framebuffer(fb->stfb); + pipe_mutex_unlock( fb->mutex ); + pipe_mutex_destroy( fb->mutex ); FREE( fb ); } +void +stw_framebuffer_release( + struct stw_framebuffer *fb) +{ + assert(fb); + pipe_mutex_unlock( fb->mutex ); +} + + +static INLINE void +stw_framebuffer_get_size( struct stw_framebuffer *fb ) +{ + unsigned width, height; + RECT rect; + + assert(fb->hWnd); + + GetClientRect( fb->hWnd, &rect ); + width = rect.right - rect.left; + height = rect.bottom - rect.top; + + if(width < 1) + width = 1; + if(height < 1) + height = 1; + + if(width != fb->width || height != fb->height) { + fb->must_resize = TRUE; + fb->width = width; + fb->height = height; + } +} + + /** * @sa http://msdn.microsoft.com/en-us/library/ms644975(VS.85).aspx * @sa http://msdn.microsoft.com/en-us/library/ms644960(VS.85).aspx */ -static LRESULT CALLBACK +LRESULT CALLBACK stw_call_window_proc( int nCode, WPARAM wParam, @@ -92,6 +139,7 @@ stw_call_window_proc( { struct stw_tls_data *tls_data; PCWPSTRUCT pParams = (PCWPSTRUCT)lParam; + struct stw_framebuffer *fb; tls_data = stw_tls_get_data(); if(!tls_data) @@ -100,51 +148,37 @@ stw_call_window_proc( if (nCode < 0) return CallNextHookEx(tls_data->hCallWndProcHook, nCode, wParam, lParam); - if (pParams->message == WM_SIZE && pParams->wParam != SIZE_MINIMIZED) { - struct stw_framebuffer *fb; - - pipe_mutex_lock( stw_dev->mutex ); - fb = stw_framebuffer_from_hwnd_locked( pParams->hwnd ); - pipe_mutex_unlock( stw_dev->mutex ); - - if(fb) { - unsigned width = LOWORD( pParams->lParam ); - unsigned height = HIWORD( pParams->lParam ); - - /* FIXME: The mesa statetracker makes the assumptions that only - * one context is using the framebuffer, and that that context is the - * current one. However neither holds true, as WGL allows more than - * one context to be bound to the same drawable, and this function can - * be called from any thread. - */ - pipe_mutex_lock( fb->mutex ); - if (fb->stfb) - st_resize_framebuffer( fb->stfb, width, height ); - pipe_mutex_unlock( fb->mutex ); + if (pParams->message == WM_WINDOWPOSCHANGED) { + /* We handle WM_WINDOWPOSCHANGED instead of WM_SIZE because according to + * http://blogs.msdn.com/oldnewthing/archive/2008/01/15/7113860.aspx + * WM_SIZE is generated from WM_WINDOWPOSCHANGED by DefWindowProc so it + * can be masked out by the application. */ + LPWINDOWPOS lpWindowPos = (LPWINDOWPOS)pParams->lParam; + if((lpWindowPos->flags & SWP_SHOWWINDOW) || + !(lpWindowPos->flags & SWP_NOSIZE)) { + fb = stw_framebuffer_from_hwnd( pParams->hwnd ); + if(fb) { + /* Size in WINDOWPOS includes the window frame, so get the size + * of the client area via GetClientRect. */ + stw_framebuffer_get_size(fb); + stw_framebuffer_release(fb); + } } } - - if (pParams->message == WM_DESTROY) { - struct stw_framebuffer *fb; - - pipe_mutex_lock( stw_dev->mutex ); - + else if (pParams->message == WM_DESTROY) { + pipe_mutex_lock( stw_dev->fb_mutex ); fb = stw_framebuffer_from_hwnd_locked( pParams->hwnd ); if(fb) stw_framebuffer_destroy_locked(fb); - - pipe_mutex_unlock( stw_dev->mutex ); + pipe_mutex_unlock( stw_dev->fb_mutex ); } return CallNextHookEx(tls_data->hCallWndProcHook, nCode, wParam, lParam); } -/** - * Create a new framebuffer object which will correspond to the given HDC. - */ struct stw_framebuffer * -stw_framebuffer_create_locked( +stw_framebuffer_create( HDC hdc, int iPixelFormat ) { @@ -169,51 +203,34 @@ stw_framebuffer_create_locked( stw_pixelformat_visual(&fb->visual, pfi); + stw_framebuffer_get_size(fb); + pipe_mutex_init( fb->mutex ); + /* This is the only case where we lock the stw_framebuffer::mutex before + * stw_dev::fb_mutex, since no other thread can know about this framebuffer + * and we must prevent any other thread from destroying it before we return. + */ + pipe_mutex_lock( fb->mutex ); + + pipe_mutex_lock( stw_dev->fb_mutex ); fb->next = stw_dev->fb_head; stw_dev->fb_head = fb; + pipe_mutex_unlock( stw_dev->fb_mutex ); return fb; } -static void -stw_framebuffer_get_size( struct stw_framebuffer *fb, GLuint *pwidth, GLuint *pheight ) -{ - GLuint width, height; - - if (fb->hWnd) { - RECT rect; - GetClientRect( fb->hWnd, &rect ); - width = rect.right - rect.left; - height = rect.bottom - rect.top; - } - else { - width = GetDeviceCaps( fb->hDC, HORZRES ); - height = GetDeviceCaps( fb->hDC, VERTRES ); - } - - if(width < 1) - width = 1; - if(height < 1) - height = 1; - - *pwidth = width; - *pheight = height; -} - - BOOL stw_framebuffer_allocate( struct stw_framebuffer *fb) { - pipe_mutex_lock( fb->mutex ); - + assert(fb); + if(!fb->stfb) { const struct stw_pixelformat_info *pfi = fb->pfi; enum pipe_format colorFormat, depthFormat, stencilFormat; - GLuint width, height; colorFormat = pfi->color_format; @@ -229,32 +246,50 @@ stw_framebuffer_allocate( else stencilFormat = PIPE_FORMAT_NONE; - stw_framebuffer_get_size(fb, &width, &height); - + assert(fb->must_resize); + assert(fb->width); + assert(fb->height); + fb->stfb = st_create_framebuffer( &fb->visual, colorFormat, depthFormat, stencilFormat, - width, - height, + fb->width, + fb->height, (void *) fb ); + + // to notify the context + fb->must_resize = TRUE; } - pipe_mutex_unlock( fb->mutex ); - return fb->stfb ? TRUE : FALSE; } +/** + * Update the framebuffer's size if necessary. + */ void -stw_framebuffer_resize( +stw_framebuffer_update( struct stw_framebuffer *fb) { - GLuint width, height; assert(fb->stfb); - stw_framebuffer_get_size(fb, &width, &height); - st_resize_framebuffer(fb->stfb, width, height); + assert(fb->height); + assert(fb->width); + + /* XXX: It would be nice to avoid checking the size again -- in theory + * stw_call_window_proc would have cought the resize and stored the right + * size already, but unfortunately threads created before the DllMain is + * called don't get a DLL_THREAD_ATTACH notification, and there is no way + * to know of their existing without using the not very portable PSAPI. + */ + stw_framebuffer_get_size(fb); + + if(fb->must_resize) { + st_resize_framebuffer(fb->stfb, fb->width, fb->height); + fb->must_resize = FALSE; + } } @@ -264,32 +299,47 @@ stw_framebuffer_cleanup( void ) struct stw_framebuffer *fb; struct stw_framebuffer *next; - pipe_mutex_lock( stw_dev->mutex ); + pipe_mutex_lock( stw_dev->fb_mutex ); fb = stw_dev->fb_head; while (fb) { next = fb->next; + + pipe_mutex_lock(fb->mutex); stw_framebuffer_destroy_locked(fb); + fb = next; } stw_dev->fb_head = NULL; - pipe_mutex_unlock( stw_dev->mutex ); + pipe_mutex_unlock( stw_dev->fb_mutex ); } /** * Given an hdc, return the corresponding stw_framebuffer. */ -struct stw_framebuffer * +static INLINE struct stw_framebuffer * stw_framebuffer_from_hdc_locked( HDC hdc ) { + HWND hwnd; struct stw_framebuffer *fb; + /* + * Some applications create and use several HDCs for the same window, so + * looking up the framebuffer by the HDC is not reliable. Use HWND whenever + * possible. + */ + hwnd = WindowFromDC(hdc); + if(hwnd) + return stw_framebuffer_from_hwnd_locked(hwnd); + for (fb = stw_dev->fb_head; fb != NULL; fb = fb->next) - if (fb->hDC == hdc) + if (fb->hDC == hdc) { + pipe_mutex_lock(fb->mutex); break; + } return fb; } @@ -304,9 +354,26 @@ stw_framebuffer_from_hdc( { struct stw_framebuffer *fb; - pipe_mutex_lock( stw_dev->mutex ); + pipe_mutex_lock( stw_dev->fb_mutex ); fb = stw_framebuffer_from_hdc_locked(hdc); - pipe_mutex_unlock( stw_dev->mutex ); + pipe_mutex_unlock( stw_dev->fb_mutex ); + + return fb; +} + + +/** + * Given an hdc, return the corresponding stw_framebuffer. + */ +struct stw_framebuffer * +stw_framebuffer_from_hwnd( + HWND hwnd ) +{ + struct stw_framebuffer *fb; + + pipe_mutex_lock( stw_dev->fb_mutex ); + fb = stw_framebuffer_from_hwnd_locked(hwnd); + pipe_mutex_unlock( stw_dev->fb_mutex ); return fb; } @@ -326,22 +393,19 @@ stw_pixelformat_set( if (index >= count) return FALSE; - pipe_mutex_lock( stw_dev->mutex ); - fb = stw_framebuffer_from_hdc_locked(hdc); if(fb) { /* SetPixelFormat must be called only once */ - pipe_mutex_unlock( stw_dev->mutex ); + stw_framebuffer_release( fb ); return FALSE; } - fb = stw_framebuffer_create_locked(hdc, iPixelFormat); + fb = stw_framebuffer_create(hdc, iPixelFormat); if(!fb) { - pipe_mutex_unlock( stw_dev->mutex ); return FALSE; } - pipe_mutex_unlock( stw_dev->mutex ); + stw_framebuffer_release( fb ); /* Some applications mistakenly use the undocumented wglSetPixelFormat * function instead of SetPixelFormat, so we call SetPixelFormat here to @@ -358,13 +422,16 @@ int stw_pixelformat_get( HDC hdc ) { + int iPixelFormat = 0; struct stw_framebuffer *fb; fb = stw_framebuffer_from_hdc(hdc); - if(!fb) - return 0; + if(fb) { + iPixelFormat = fb->iPixelFormat; + stw_framebuffer_release(fb); + } - return fb->iPixelFormat; + return iPixelFormat; } @@ -380,10 +447,10 @@ stw_swap_buffers( if (fb == NULL) return FALSE; - if (!(fb->pfi->pfd.dwFlags & PFD_DOUBLEBUFFER)) + if (!(fb->pfi->pfd.dwFlags & PFD_DOUBLEBUFFER)) { + stw_framebuffer_release(fb); return TRUE; - - pipe_mutex_lock( fb->mutex ); + } /* If we're swapping the buffer associated with the current context * we have to flush any pending rendering commands first. @@ -394,7 +461,7 @@ stw_swap_buffers( if(!st_get_framebuffer_surface( fb->stfb, ST_SURFACE_BACK_LEFT, &surface )) { /* FIXME: this shouldn't happen, but does on glean */ - pipe_mutex_unlock( fb->mutex ); + stw_framebuffer_release(fb); return FALSE; } @@ -407,7 +474,8 @@ stw_swap_buffers( stw_dev->stw_winsys->flush_frontbuffer( screen, surface, hdc ); - pipe_mutex_unlock( fb->mutex ); + stw_framebuffer_update(fb); + stw_framebuffer_release(fb); return TRUE; } @@ -423,38 +491,3 @@ stw_swap_layer_buffers( return FALSE; } - - -boolean -stw_framebuffer_init_thread(void) -{ - struct stw_tls_data *tls_data; - - tls_data = stw_tls_get_data(); - if(!tls_data) - return FALSE; - - tls_data->hCallWndProcHook = SetWindowsHookEx(WH_CALLWNDPROC, - stw_call_window_proc, - NULL, - GetCurrentThreadId()); - if(tls_data->hCallWndProcHook == NULL) - return FALSE; - - return TRUE; -} - -void -stw_framebuffer_cleanup_thread(void) -{ - struct stw_tls_data *tls_data; - - tls_data = stw_tls_get_data(); - if(!tls_data) - return; - - if(tls_data->hCallWndProcHook) { - UnhookWindowsHookEx(tls_data->hCallWndProcHook); - tls_data->hCallWndProcHook = NULL; - } -} |