summaryrefslogtreecommitdiff
path: root/src/gallium/state_trackers/wgl/shared/stw_framebuffer.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/gallium/state_trackers/wgl/shared/stw_framebuffer.c')
-rw-r--r--src/gallium/state_trackers/wgl/shared/stw_framebuffer.c291
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;
- }
-}