1 /**************************************************************************
3 * Copyright 2008-2009 Vmware, Inc.
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:
14 * The above copyright notice and this permission notice (including the
15 * next paragraph) shall be included in all copies or substantial portions
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.
26 **************************************************************************/
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"
38 #include "stw_framebuffer.h"
39 #include "stw_device.h"
40 #include "stw_winsys.h"
42 #include "stw_context.h"
47 * Search the framebuffer with the matching HWND while holding the
48 * stw_dev::fb_mutex global lock.
49 * If a stw_framebuffer is found, lock it and return the pointer.
52 static struct stw_framebuffer
*
53 stw_framebuffer_from_hwnd_locked(HWND hwnd
)
55 struct stw_framebuffer
*fb
;
57 for (fb
= stw_dev
->fb_head
; fb
!= NULL
; fb
= fb
->next
)
58 if (fb
->hWnd
== hwnd
) {
59 stw_framebuffer_lock(fb
);
60 assert(fb
->mutex
.RecursionCount
== 1);
69 * Decrement the reference count on the given stw_framebuffer object.
70 * If the reference count hits zero, destroy the object.
72 * Note: Both stw_dev::fb_mutex and stw_framebuffer::mutex must already be
73 * locked. After this function completes, the fb's mutex will be unlocked.
76 stw_framebuffer_release_locked(struct stw_framebuffer
*fb
)
78 struct stw_framebuffer
**link
;
81 assert(stw_own_mutex(&fb
->mutex
));
82 assert(stw_own_mutex(&stw_dev
->fb_mutex
));
84 /* check the reference count */
87 stw_framebuffer_unlock(fb
);
91 /* remove this stw_framebuffer from the device's linked list */
92 link
= &stw_dev
->fb_head
;
94 link
= &(*link
)->next
;
99 if (fb
->shared_surface
)
100 stw_dev
->stw_winsys
->shared_surface_close(stw_dev
->screen
,
103 stw_st_destroy_framebuffer_locked(fb
->stfb
);
105 stw_framebuffer_unlock(fb
);
107 DeleteCriticalSection(&fb
->mutex
);
114 * Query the size of the given framebuffer's on-screen window and update
115 * the stw_framebuffer's width/height.
118 stw_framebuffer_get_size(struct stw_framebuffer
*fb
)
129 assert(fb
->width
&& fb
->height
);
130 assert(fb
->client_rect
.right
== fb
->client_rect
.left
+ fb
->width
);
131 assert(fb
->client_rect
.bottom
== fb
->client_rect
.top
+ fb
->height
);
134 * Get the client area size.
136 if (!GetClientRect(fb
->hWnd
, &client_rect
)) {
140 assert(client_rect
.left
== 0);
141 assert(client_rect
.top
== 0);
142 width
= client_rect
.right
- client_rect
.left
;
143 height
= client_rect
.bottom
- client_rect
.top
;
145 fb
->minimized
= width
== 0 || height
== 0;
147 if (width
<= 0 || height
<= 0) {
149 * When the window is minimized GetClientRect will return zeros. Simply
150 * preserve the current window size, until the window is restored or
156 if (width
!= fb
->width
|| height
!= fb
->height
) {
157 fb
->must_resize
= TRUE
;
164 if (ClientToScreen(fb
->hWnd
, &client_pos
) &&
165 GetWindowRect(fb
->hWnd
, &window_rect
)) {
166 fb
->client_rect
.left
= client_pos
.x
- window_rect
.left
;
167 fb
->client_rect
.top
= client_pos
.y
- window_rect
.top
;
170 fb
->client_rect
.right
= fb
->client_rect
.left
+ fb
->width
;
171 fb
->client_rect
.bottom
= fb
->client_rect
.top
+ fb
->height
;
175 debug_printf("%s: hwnd = %p\n", __FUNCTION__
, fb
->hWnd
);
176 debug_printf("%s: client_position = (%li, %li)\n",
177 __FUNCTION__
, client_pos
.x
, client_pos
.y
);
178 debug_printf("%s: window_rect = (%li, %li) - (%li, %li)\n",
180 window_rect
.left
, window_rect
.top
,
181 window_rect
.right
, window_rect
.bottom
);
182 debug_printf("%s: client_rect = (%li, %li) - (%li, %li)\n",
184 fb
->client_rect
.left
, fb
->client_rect
.top
,
185 fb
->client_rect
.right
, fb
->client_rect
.bottom
);
191 * @sa http://msdn.microsoft.com/en-us/library/ms644975(VS.85).aspx
192 * @sa http://msdn.microsoft.com/en-us/library/ms644960(VS.85).aspx
195 stw_call_window_proc(int nCode
, WPARAM wParam
, LPARAM lParam
)
197 struct stw_tls_data
*tls_data
;
198 PCWPSTRUCT pParams
= (PCWPSTRUCT
)lParam
;
199 struct stw_framebuffer
*fb
;
201 tls_data
= stw_tls_get_data();
205 if (nCode
< 0 || !stw_dev
)
206 return CallNextHookEx(tls_data
->hCallWndProcHook
, nCode
, wParam
, lParam
);
208 /* We check that the stw_dev object is initialized before we try to do
209 * anything with it. Otherwise, in multi-threaded programs there's a
210 * chance of executing this code before the stw_dev object is fully
213 if (stw_dev
&& stw_dev
->initialized
) {
214 if (pParams
->message
== WM_WINDOWPOSCHANGED
) {
215 /* We handle WM_WINDOWPOSCHANGED instead of WM_SIZE because according
216 * to http://blogs.msdn.com/oldnewthing/archive/2008/01/15/7113860.aspx
217 * WM_SIZE is generated from WM_WINDOWPOSCHANGED by DefWindowProc so it
218 * can be masked out by the application.
220 LPWINDOWPOS lpWindowPos
= (LPWINDOWPOS
)pParams
->lParam
;
221 if ((lpWindowPos
->flags
& SWP_SHOWWINDOW
) ||
222 !(lpWindowPos
->flags
& SWP_NOMOVE
) ||
223 !(lpWindowPos
->flags
& SWP_NOSIZE
)) {
224 fb
= stw_framebuffer_from_hwnd( pParams
->hwnd
);
226 /* Size in WINDOWPOS includes the window frame, so get the size
227 * of the client area via GetClientRect.
229 stw_framebuffer_get_size(fb
);
230 stw_framebuffer_unlock(fb
);
234 else if (pParams
->message
== WM_DESTROY
) {
235 stw_lock_framebuffers(stw_dev
);
236 fb
= stw_framebuffer_from_hwnd_locked( pParams
->hwnd
);
238 stw_framebuffer_release_locked(fb
);
239 stw_unlock_framebuffers(stw_dev
);
243 return CallNextHookEx(tls_data
->hCallWndProcHook
, nCode
, wParam
, lParam
);
248 * Create a new stw_framebuffer object which corresponds to the given
249 * HDC/window. If successful, we return the new stw_framebuffer object
250 * with its mutex locked.
252 struct stw_framebuffer
*
253 stw_framebuffer_create(HDC hdc
, int iPixelFormat
)
256 struct stw_framebuffer
*fb
;
257 const struct stw_pixelformat_info
*pfi
;
259 /* We only support drawing to a window. */
260 hWnd
= WindowFromDC( hdc
);
264 fb
= CALLOC_STRUCT( stw_framebuffer
);
269 fb
->iPixelFormat
= iPixelFormat
;
272 * We often need a displayable pixel format to make GDI happy. Set it
273 * here (always 1, i.e., out first pixel format) where appropriate.
275 fb
->iDisplayablePixelFormat
= iPixelFormat
<= stw_dev
->pixelformat_count
278 fb
->pfi
= pfi
= stw_pixelformat_get_info( iPixelFormat
);
279 fb
->stfb
= stw_st_create_framebuffer( fb
);
288 * Windows can be sometimes have zero width and or height, but we ensure
289 * a non-zero framebuffer size at all times.
292 fb
->must_resize
= TRUE
;
295 fb
->client_rect
.left
= 0;
296 fb
->client_rect
.top
= 0;
297 fb
->client_rect
.right
= fb
->client_rect
.left
+ fb
->width
;
298 fb
->client_rect
.bottom
= fb
->client_rect
.top
+ fb
->height
;
300 stw_framebuffer_get_size(fb
);
302 InitializeCriticalSection(&fb
->mutex
);
304 /* This is the only case where we lock the stw_framebuffer::mutex before
305 * stw_dev::fb_mutex, since no other thread can know about this framebuffer
306 * and we must prevent any other thread from destroying it before we return.
308 stw_framebuffer_lock(fb
);
310 stw_lock_framebuffers(stw_dev
);
311 fb
->next
= stw_dev
->fb_head
;
312 stw_dev
->fb_head
= fb
;
313 stw_unlock_framebuffers(stw_dev
);
320 * Update the framebuffer's size if necessary.
323 stw_framebuffer_update(struct stw_framebuffer
*fb
)
329 /* XXX: It would be nice to avoid checking the size again -- in theory
330 * stw_call_window_proc would have cought the resize and stored the right
331 * size already, but unfortunately threads created before the DllMain is
332 * called don't get a DLL_THREAD_ATTACH notification, and there is no way
333 * to know of their existing without using the not very portable PSAPI.
335 stw_framebuffer_get_size(fb
);
340 * Try to free all stw_framebuffer objects associated with the device.
343 stw_framebuffer_cleanup(void)
345 struct stw_framebuffer
*fb
;
346 struct stw_framebuffer
*next
;
351 stw_lock_framebuffers(stw_dev
);
353 fb
= stw_dev
->fb_head
;
357 stw_framebuffer_lock(fb
);
358 stw_framebuffer_release_locked(fb
);
362 stw_dev
->fb_head
= NULL
;
364 stw_unlock_framebuffers(stw_dev
);
369 * Given an hdc, return the corresponding stw_framebuffer.
370 * The returned stw_framebuffer will have its mutex locked.
372 static struct stw_framebuffer
*
373 stw_framebuffer_from_hdc_locked(HDC hdc
)
377 hwnd
= WindowFromDC(hdc
);
382 return stw_framebuffer_from_hwnd_locked(hwnd
);
387 * Given an HDC, return the corresponding stw_framebuffer.
388 * The returned stw_framebuffer will have its mutex locked.
390 struct stw_framebuffer
*
391 stw_framebuffer_from_hdc(HDC hdc
)
393 struct stw_framebuffer
*fb
;
398 stw_lock_framebuffers(stw_dev
);
399 fb
= stw_framebuffer_from_hdc_locked(hdc
);
400 stw_unlock_framebuffers(stw_dev
);
407 * Given an HWND, return the corresponding stw_framebuffer.
408 * The returned stw_framebuffer will have its mutex locked.
410 struct stw_framebuffer
*
411 stw_framebuffer_from_hwnd(HWND hwnd
)
413 struct stw_framebuffer
*fb
;
415 stw_lock_framebuffers(stw_dev
);
416 fb
= stw_framebuffer_from_hwnd_locked(hwnd
);
417 stw_unlock_framebuffers(stw_dev
);
424 DrvSetPixelFormat(HDC hdc
, LONG iPixelFormat
)
428 struct stw_framebuffer
*fb
;
433 index
= (uint
) iPixelFormat
- 1;
434 count
= stw_pixelformat_get_count();
438 fb
= stw_framebuffer_from_hdc_locked(hdc
);
441 * SetPixelFormat must be called only once. However ignore
442 * pbuffers, for which the framebuffer object is created first.
444 boolean bPbuffer
= fb
->bPbuffer
;
446 stw_framebuffer_unlock( fb
);
451 fb
= stw_framebuffer_create(hdc
, iPixelFormat
);
456 stw_framebuffer_unlock( fb
);
458 /* Some applications mistakenly use the undocumented wglSetPixelFormat
459 * function instead of SetPixelFormat, so we call SetPixelFormat here to
460 * avoid opengl32.dll's wglCreateContext to fail */
461 if (GetPixelFormat(hdc
) == 0) {
462 BOOL bRet
= SetPixelFormat(hdc
, iPixelFormat
, NULL
);
464 debug_printf("SetPixelFormat failed\n");
473 stw_pixelformat_get(HDC hdc
)
475 int iPixelFormat
= 0;
476 struct stw_framebuffer
*fb
;
478 fb
= stw_framebuffer_from_hdc(hdc
);
480 iPixelFormat
= fb
->iPixelFormat
;
481 stw_framebuffer_unlock(fb
);
489 DrvPresentBuffers(HDC hdc
, LPPRESENTBUFFERS data
)
491 struct stw_framebuffer
*fb
;
492 struct pipe_screen
*screen
;
493 struct pipe_resource
*res
;
498 fb
= stw_framebuffer_from_hdc( hdc
);
502 screen
= stw_dev
->screen
;
504 res
= (struct pipe_resource
*)data
->pPrivData
;
506 if (data
->hSurface
!= fb
->hSharedSurface
) {
507 if (fb
->shared_surface
) {
508 stw_dev
->stw_winsys
->shared_surface_close(screen
, fb
->shared_surface
);
509 fb
->shared_surface
= NULL
;
512 fb
->hSharedSurface
= data
->hSurface
;
514 if (data
->hSurface
&&
515 stw_dev
->stw_winsys
->shared_surface_open
) {
517 stw_dev
->stw_winsys
->shared_surface_open(screen
,
522 if (!fb
->minimized
) {
523 if (fb
->shared_surface
) {
524 stw_dev
->stw_winsys
->compose(screen
,
528 data
->ullPresentToken
);
531 stw_dev
->stw_winsys
->present( screen
, res
, hdc
);
535 stw_framebuffer_update(fb
);
536 stw_notify_current_locked(fb
);
538 stw_framebuffer_unlock(fb
);
545 * Queue a composition.
547 * The stw_framebuffer object must have its mutex locked. The mutex will
548 * be unlocked here before returning.
551 stw_framebuffer_present_locked(HDC hdc
,
552 struct stw_framebuffer
*fb
,
553 struct pipe_resource
*res
)
555 if (stw_dev
->callbacks
.pfnPresentBuffers
&&
556 stw_dev
->stw_winsys
->compose
) {
557 PRESENTBUFFERSCB data
;
559 memset(&data
, 0, sizeof data
);
561 data
.syncType
= PRESCB_SYNCTYPE_NONE
;
562 data
.luidAdapter
= stw_dev
->AdapterLuid
;
563 data
.updateRect
= fb
->client_rect
;
564 data
.pPrivData
= (void *)res
;
566 stw_notify_current_locked(fb
);
567 stw_framebuffer_unlock(fb
);
569 return stw_dev
->callbacks
.pfnPresentBuffers(hdc
, &data
);
572 struct pipe_screen
*screen
= stw_dev
->screen
;
574 stw_dev
->stw_winsys
->present( screen
, res
, hdc
);
576 stw_framebuffer_update(fb
);
577 stw_notify_current_locked(fb
);
578 stw_framebuffer_unlock(fb
);
586 * This is called just before issuing the buffer swap/present.
587 * We query the current time and determine if we should sleep before
588 * issuing the swap/present.
589 * This is a bit of a hack and is certainly not very accurate but it
591 * This is for the WGL_ARB_swap_interval extension.
594 wait_swap_interval(struct stw_framebuffer
*fb
)
596 /* Note: all time variables here are in units of microseconds */
597 int64_t cur_time
= os_time_get_nano() / 1000;
599 if (fb
->prev_swap_time
!= 0) {
600 /* Compute time since previous swap */
601 int64_t delta
= cur_time
- fb
->prev_swap_time
;
602 int64_t min_swap_period
=
603 1.0e6
/ stw_dev
->refresh_rate
* stw_dev
->swap_interval
;
605 /* If time since last swap is less than wait period, wait.
606 * Note that it's possible for the delta to be negative because of
607 * rollover. See https://bugs.freedesktop.org/show_bug.cgi?id=102241
609 if ((delta
>= 0) && (delta
< min_swap_period
)) {
610 float fudge
= 1.75f
; /* emperical fudge factor */
611 int64_t wait
= (min_swap_period
- delta
) * fudge
;
616 fb
->prev_swap_time
= cur_time
;
621 DrvSwapBuffers(HDC hdc
)
623 struct stw_context
*ctx
;
624 struct stw_framebuffer
*fb
;
629 fb
= stw_framebuffer_from_hdc( hdc
);
633 if (!(fb
->pfi
->pfd
.dwFlags
& PFD_DOUBLEBUFFER
)) {
634 stw_framebuffer_unlock(fb
);
638 ctx
= stw_current_context();
641 /* Display the HUD */
642 struct pipe_resource
*back
=
643 stw_get_framebuffer_resource(fb
->stfb
, ST_ATTACHMENT_BACK_LEFT
);
645 hud_run(ctx
->hud
, NULL
, back
);
649 if (ctx
->current_framebuffer
== fb
) {
650 /* flush current context */
651 ctx
->st
->flush(ctx
->st
, ST_FLUSH_END_OF_FRAME
, NULL
, NULL
, NULL
);
655 if (stw_dev
->swap_interval
!= 0) {
656 wait_swap_interval(fb
);
659 return stw_st_swap_framebuffer_locked(hdc
, fb
->stfb
);
664 DrvSwapLayerBuffers(HDC hdc
, UINT fuPlanes
)
666 if (fuPlanes
& WGL_SWAP_MAIN_PLANE
)
667 return DrvSwapBuffers(hdc
);