xref: /aosp_15_r20/external/mesa3d/src/gallium/frontends/wgl/stw_framebuffer.c (revision 6104692788411f58d303aa86923a9ff6ecaded22)
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    if (fb->winsys_framebuffer && fb->winsys_framebuffer->set_latency) {
328       int latency = driQueryOptioni(&stw_dev->option_cache, "wgl_frame_latency");
329       fb->winsys_framebuffer->set_latency(fb->winsys_framebuffer, latency);
330    }
331 
332 #ifdef _GAMING_XBOX
333    fb->prev_wndproc = (WNDPROC)SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)&stw_call_window_proc_xbox);
334 #endif
335 
336    /*
337     * We often need a displayable pixel format to make GDI happy. Set it
338     * here (always 1, i.e., out first pixel format) where appropriate.
339     */
340    fb->iDisplayablePixelFormat = pfi->iPixelFormat <= stw_dev->pixelformat_count
341       ? pfi->iPixelFormat : 1;
342    fb->owner = owner;
343 
344    fb->pfi = pfi;
345    fb->drawable = stw_st_create_framebuffer( fb, fscreen );
346    if (!fb->drawable) {
347       FREE( fb );
348       return NULL;
349    }
350 
351    fb->refcnt = 1;
352 
353    /* A -1 means defer to the global stw_dev->swap_interval */
354    fb->swap_interval = -1;
355 
356    /*
357     * Windows can be sometimes have zero width and or height, but we ensure
358     * a non-zero framebuffer size at all times.
359     */
360 
361    fb->must_resize = true;
362    fb->width  = 1;
363    fb->height = 1;
364    fb->client_rect.left   = 0;
365    fb->client_rect.top    = 0;
366    fb->client_rect.right  = fb->client_rect.left + fb->width;
367    fb->client_rect.bottom = fb->client_rect.top  + fb->height;
368 
369    stw_framebuffer_get_size(fb);
370 
371    InitializeCriticalSection(&fb->mutex);
372 
373    /* This is the only case where we lock the stw_framebuffer::mutex before
374     * stw_dev::fb_mutex, since no other thread can know about this framebuffer
375     * and we must prevent any other thread from destroying it before we return.
376     */
377    stw_framebuffer_lock(fb);
378 
379    if (owner != STW_FRAMEBUFFER_EGL_WINDOW) {
380       stw_lock_framebuffers(stw_dev);
381       fb->next = stw_dev->fb_head;
382       stw_dev->fb_head = fb;
383       stw_unlock_framebuffers(stw_dev);
384    }
385 
386    return fb;
387 }
388 
389 /**
390  * Increase fb reference count.  The referenced framebuffer should be locked.
391  *
392  * It's not necessary to hold stw_dev::fb_mutex global lock.
393  */
394 void
stw_framebuffer_reference_locked(struct stw_framebuffer * fb)395 stw_framebuffer_reference_locked(struct stw_framebuffer *fb)
396 {
397    if (fb) {
398       assert(stw_own_mutex(&fb->mutex));
399       fb->refcnt++;
400    }
401 }
402 
403 /**
404  * Release stw_framebuffer::mutex lock. This framebuffer must not be accessed
405  * after calling this function, as it may have been deleted by another thread
406  * in the meanwhile.
407  */
408 void
stw_framebuffer_unlock(struct stw_framebuffer * fb)409 stw_framebuffer_unlock(struct stw_framebuffer *fb)
410 {
411    assert(fb);
412    assert(stw_own_mutex(&fb->mutex));
413    LeaveCriticalSection(&fb->mutex);
414 }
415 
416 
417 /**
418  * Update the framebuffer's size if necessary.
419  */
420 void
stw_framebuffer_update(struct stw_framebuffer * fb)421 stw_framebuffer_update(struct stw_framebuffer *fb)
422 {
423    assert(fb->drawable);
424    assert(fb->height);
425    assert(fb->width);
426 
427    /* XXX: It would be nice to avoid checking the size again -- in theory
428     * stw_call_window_proc would have cought the resize and stored the right
429     * size already, but unfortunately threads created before the DllMain is
430     * called don't get a DLL_THREAD_ATTACH notification, and there is no way
431     * to know of their existing without using the not very portable PSAPI.
432     */
433    stw_framebuffer_get_size(fb);
434 }
435 
436 
437 /**
438  * Try to free all stw_framebuffer objects associated with the device.
439  */
440 void
stw_framebuffer_cleanup(void)441 stw_framebuffer_cleanup(void)
442 {
443    struct stw_framebuffer *fb;
444    struct stw_framebuffer *next;
445 
446    if (!stw_dev)
447       return;
448 
449    stw_lock_framebuffers(stw_dev);
450 
451    fb = stw_dev->fb_head;
452    while (fb) {
453       next = fb->next;
454 
455       stw_framebuffer_lock(fb);
456       stw_framebuffer_release_locked(fb, NULL);
457 
458       fb = next;
459    }
460    stw_dev->fb_head = NULL;
461 
462    stw_unlock_framebuffers(stw_dev);
463 }
464 
465 
466 /**
467  * Given an hdc, return the corresponding stw_framebuffer.
468  * The returned stw_framebuffer will have its mutex locked.
469  */
470 static struct stw_framebuffer *
stw_framebuffer_from_hdc_locked(HDC hdc)471 stw_framebuffer_from_hdc_locked(HDC hdc)
472 {
473    HWND hwnd;
474 
475    hwnd = WindowFromDC(hdc);
476    if (!hwnd) {
477       return NULL;
478    }
479 
480    return stw_framebuffer_from_hwnd_locked(hwnd);
481 }
482 
483 
484 /**
485  * Given an HDC, return the corresponding stw_framebuffer.
486  * The returned stw_framebuffer will have its mutex locked.
487  */
488 struct stw_framebuffer *
stw_framebuffer_from_hdc(HDC hdc)489 stw_framebuffer_from_hdc(HDC hdc)
490 {
491    struct stw_framebuffer *fb;
492 
493    if (!stw_dev)
494       return NULL;
495 
496    stw_lock_framebuffers(stw_dev);
497    fb = stw_framebuffer_from_hdc_locked(hdc);
498    stw_unlock_framebuffers(stw_dev);
499 
500    return fb;
501 }
502 
503 
504 /**
505  * Given an HWND, return the corresponding stw_framebuffer.
506  * The returned stw_framebuffer will have its mutex locked.
507  */
508 struct stw_framebuffer *
stw_framebuffer_from_hwnd(HWND hwnd)509 stw_framebuffer_from_hwnd(HWND hwnd)
510 {
511    struct stw_framebuffer *fb;
512 
513    stw_lock_framebuffers(stw_dev);
514    fb = stw_framebuffer_from_hwnd_locked(hwnd);
515    stw_unlock_framebuffers(stw_dev);
516 
517    return fb;
518 }
519 
520 
521 BOOL APIENTRY
DrvSetPixelFormat(HDC hdc,LONG iPixelFormat)522 DrvSetPixelFormat(HDC hdc, LONG iPixelFormat)
523 {
524    uint count;
525    uint index;
526    struct stw_framebuffer *fb;
527 
528    if (!stw_dev)
529       return false;
530 
531    index = (uint) iPixelFormat - 1;
532    count = stw_pixelformat_get_count(hdc);
533    if (index >= count)
534       return false;
535 
536    fb = stw_framebuffer_from_hdc_locked(hdc);
537    if (fb) {
538       /*
539        * SetPixelFormat must be called only once.  However ignore
540        * pbuffers, for which the framebuffer object is created first.
541        */
542       bool bPbuffer = fb->owner == STW_FRAMEBUFFER_PBUFFER;
543 
544       stw_framebuffer_unlock( fb );
545 
546       return bPbuffer;
547    }
548 
549    const struct stw_pixelformat_info *pfi = stw_pixelformat_get_info(iPixelFormat);
550 
551    fb = stw_framebuffer_create(WindowFromDC(hdc), pfi, STW_FRAMEBUFFER_WGL_WINDOW, stw_dev->fscreen);
552    if (!fb) {
553       return false;
554    }
555 
556    stw_framebuffer_unlock( fb );
557 
558    /* Some applications mistakenly use the undocumented wglSetPixelFormat
559     * function instead of SetPixelFormat, so we call SetPixelFormat here to
560     * avoid opengl32.dll's wglCreateContext to fail */
561    if (GetPixelFormat(hdc) == 0) {
562       BOOL bRet = SetPixelFormat(hdc, iPixelFormat, NULL);
563       if (!bRet) {
564 	  debug_printf("SetPixelFormat failed\n");
565       }
566    }
567 
568    return true;
569 }
570 
571 
572 int
stw_pixelformat_get(HDC hdc)573 stw_pixelformat_get(HDC hdc)
574 {
575    int iPixelFormat = 0;
576    struct stw_framebuffer *fb;
577 
578    fb = stw_framebuffer_from_hdc(hdc);
579    if (fb) {
580       iPixelFormat = fb->pfi->iPixelFormat;
581       stw_framebuffer_unlock(fb);
582    }
583 
584    return iPixelFormat;
585 }
586 
587 
588 BOOL APIENTRY
DrvPresentBuffers(HDC hdc,LPPRESENTBUFFERS data)589 DrvPresentBuffers(HDC hdc, LPPRESENTBUFFERS data)
590 {
591    struct stw_framebuffer *fb;
592    struct stw_context *ctx;
593    struct pipe_screen *screen;
594    struct pipe_context *pipe;
595    struct pipe_resource *res;
596 
597    if (!stw_dev)
598       return false;
599 
600    fb = stw_framebuffer_from_hdc( hdc );
601    if (fb == NULL)
602       return false;
603 
604    screen = stw_dev->screen;
605    ctx = stw_current_context();
606    pipe = ctx ? ctx->st->pipe : NULL;
607 
608    res = (struct pipe_resource *)data->pPrivData;
609 
610    if (data->hSurface != fb->hSharedSurface) {
611       if (fb->shared_surface) {
612          stw_dev->stw_winsys->shared_surface_close(screen, fb->shared_surface);
613          fb->shared_surface = NULL;
614       }
615 
616       fb->hSharedSurface = data->hSurface;
617 
618       if (data->hSurface &&
619          stw_dev->stw_winsys->shared_surface_open) {
620          fb->shared_surface =
621             stw_dev->stw_winsys->shared_surface_open(screen,
622                                                      fb->hSharedSurface);
623       }
624    }
625 
626    if (!fb->minimized) {
627       if (fb->shared_surface) {
628          stw_dev->stw_winsys->compose(screen,
629                                       res,
630                                       fb->shared_surface,
631                                       &fb->client_rect,
632                                       data->ullPresentToken);
633       }
634       else {
635          stw_dev->stw_winsys->present( screen, pipe, res, hdc );
636       }
637    }
638 
639    stw_framebuffer_update(fb);
640    stw_notify_current_locked(fb);
641 
642    stw_framebuffer_unlock(fb);
643 
644    return true;
645 }
646 
647 
648 /**
649  * Queue a composition.
650  *
651  * The stw_framebuffer object must have its mutex locked.  The mutex will
652  * be unlocked here before returning.
653  */
654 BOOL
stw_framebuffer_present_locked(HDC hdc,struct stw_framebuffer * fb,struct pipe_resource * res)655 stw_framebuffer_present_locked(HDC hdc,
656                                struct stw_framebuffer *fb,
657                                struct pipe_resource *res)
658 {
659    if (fb->winsys_framebuffer) {
660       int interval = fb->swap_interval == -1 ? stw_dev->swap_interval : fb->swap_interval;
661       BOOL result = fb->winsys_framebuffer->present(fb->winsys_framebuffer, interval);
662 
663       stw_framebuffer_update(fb);
664       stw_notify_current_locked(fb);
665       stw_framebuffer_unlock(fb);
666 
667       return result;
668    }
669    else if (stw_dev->callbacks.pfnPresentBuffers &&
670             stw_dev->stw_winsys->compose) {
671       PRESENTBUFFERSCB data;
672 
673       memset(&data, 0, sizeof data);
674       data.nVersion = 2;
675       data.syncType = PRESCB_SYNCTYPE_NONE;
676       data.luidAdapter = stw_dev->AdapterLuid;
677       data.updateRect = fb->client_rect;
678       data.pPrivData = (void *)res;
679 
680       stw_notify_current_locked(fb);
681       stw_framebuffer_unlock(fb);
682 
683       return stw_dev->callbacks.pfnPresentBuffers(hdc, &data);
684    }
685    else {
686       struct pipe_screen *screen = stw_dev->screen;
687       struct stw_context *ctx = stw_current_context();
688       struct pipe_context *pipe = ctx ? ctx->st->pipe : NULL;
689 
690       stw_dev->stw_winsys->present( screen, pipe, res, hdc );
691 
692       stw_framebuffer_update(fb);
693       stw_notify_current_locked(fb);
694       stw_framebuffer_unlock(fb);
695 
696       return true;
697    }
698 }
699 
700 
701 /**
702  * This is called just before issuing the buffer swap/present.
703  * We query the current time and determine if we should sleep before
704  * issuing the swap/present.
705  * This is a bit of a hack and is certainly not very accurate but it
706  * basically works.
707  * This is for the WGL_ARB_swap_interval extension.
708  */
709 static void
wait_swap_interval(struct stw_framebuffer * fb,int interval)710 wait_swap_interval(struct stw_framebuffer *fb, int interval)
711 {
712    /* Note: all time variables here are in units of microseconds */
713    int64_t cur_time = os_time_get_nano() / 1000;
714 
715    if (fb->prev_swap_time != 0) {
716       /* Compute time since previous swap */
717       int64_t delta = cur_time - fb->prev_swap_time;
718       int64_t min_swap_period =
719          1.0e6 / stw_dev->refresh_rate * interval;
720 
721       /* If time since last swap is less than wait period, wait.
722        * Note that it's possible for the delta to be negative because of
723        * rollover.  See https://bugs.freedesktop.org/show_bug.cgi?id=102241
724        */
725       if ((delta >= 0) && (delta < min_swap_period)) {
726          float fudge = 1.75f;  /* emperical fudge factor */
727          int64_t wait = (min_swap_period - delta) * fudge;
728          os_time_sleep(wait);
729       }
730    }
731 
732    fb->prev_swap_time = cur_time;
733 }
734 
735 BOOL
stw_framebuffer_swap_locked(HDC hdc,struct stw_framebuffer * fb)736 stw_framebuffer_swap_locked(HDC hdc, struct stw_framebuffer *fb)
737 {
738    struct stw_context *ctx = stw_current_context();
739    if (!(fb->pfi->pfd.dwFlags & PFD_DOUBLEBUFFER)) {
740       stw_framebuffer_unlock(fb);
741       if (ctx)
742          stw_st_flush(ctx->st, fb->drawable, ST_FLUSH_END_OF_FRAME | ST_FLUSH_FRONT);
743       return true;
744    }
745 
746    if (ctx) {
747       if (ctx->hud) {
748          /* Display the HUD */
749          struct pipe_resource *back =
750             stw_get_framebuffer_resource(fb->drawable, ST_ATTACHMENT_BACK_LEFT);
751          if (back) {
752             hud_run(ctx->hud, NULL, back);
753          }
754       }
755 
756       if (ctx->current_framebuffer == fb) {
757          /* flush current context */
758          stw_st_flush(ctx->st, fb->drawable, ST_FLUSH_END_OF_FRAME);
759       }
760    }
761 
762    int interval = fb->swap_interval == -1 ? stw_dev->swap_interval : fb->swap_interval;
763    if (interval != 0 && !fb->winsys_framebuffer) {
764       wait_swap_interval(fb, interval);
765    }
766 
767    return stw_st_swap_framebuffer_locked(hdc, fb->drawable);
768 }
769 
770 BOOL APIENTRY
DrvSwapBuffers(HDC hdc)771 DrvSwapBuffers(HDC hdc)
772 {
773    struct stw_framebuffer *fb;
774 
775    if (!stw_dev)
776       return false;
777 
778    fb = stw_framebuffer_from_hdc( hdc );
779    if (fb == NULL)
780       return false;
781 
782    return stw_framebuffer_swap_locked(hdc, fb);
783 }
784 
785 
786 BOOL APIENTRY
DrvSwapLayerBuffers(HDC hdc,UINT fuPlanes)787 DrvSwapLayerBuffers(HDC hdc, UINT fuPlanes)
788 {
789    if (fuPlanes & WGL_SWAP_MAIN_PLANE)
790       return DrvSwapBuffers(hdc);
791 
792    return false;
793 }
794