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