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