Merge remote-tracking branch 'mesa-public/master' into vulkan
[mesa.git] / src / gallium / state_trackers / wgl / stw_framebuffer.c
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 "util/u_memory.h"
32 #include "hud/hud_context.h"
33 #include "state_tracker/st_api.h"
34
35 #include "stw_icd.h"
36 #include "stw_framebuffer.h"
37 #include "stw_device.h"
38 #include "stw_winsys.h"
39 #include "stw_tls.h"
40 #include "stw_context.h"
41 #include "stw_st.h"
42
43
44 /**
45 * Search the framebuffer with the matching HWND while holding the
46 * stw_dev::fb_mutex global lock.
47 */
48 static inline struct stw_framebuffer *
49 stw_framebuffer_from_hwnd_locked(
50 HWND hwnd )
51 {
52 struct stw_framebuffer *fb;
53
54 for (fb = stw_dev->fb_head; fb != NULL; fb = fb->next)
55 if (fb->hWnd == hwnd) {
56 pipe_mutex_lock(fb->mutex);
57 break;
58 }
59
60 return fb;
61 }
62
63
64 /**
65 * Destroy this framebuffer. Both stw_dev::fb_mutex and stw_framebuffer::mutex
66 * must be held, by this order. If there are still references to the
67 * framebuffer, nothing will happen.
68 */
69 static void
70 stw_framebuffer_destroy_locked(struct stw_framebuffer *fb)
71 {
72 struct stw_framebuffer **link;
73
74 /* check the reference count */
75 fb->refcnt--;
76 if (fb->refcnt) {
77 pipe_mutex_unlock( fb->mutex );
78 return;
79 }
80
81 link = &stw_dev->fb_head;
82 while (*link != fb)
83 link = &(*link)->next;
84 assert(*link);
85 *link = fb->next;
86 fb->next = NULL;
87
88 if (fb->shared_surface)
89 stw_dev->stw_winsys->shared_surface_close(stw_dev->screen,
90 fb->shared_surface);
91
92 stw_st_destroy_framebuffer_locked(fb->stfb);
93
94 pipe_mutex_unlock( fb->mutex );
95
96 pipe_mutex_destroy( fb->mutex );
97
98 FREE( fb );
99 }
100
101
102 void
103 stw_framebuffer_release(struct stw_framebuffer *fb)
104 {
105 assert(fb);
106 pipe_mutex_unlock( fb->mutex );
107 }
108
109
110 static void
111 stw_framebuffer_get_size(struct stw_framebuffer *fb)
112 {
113 LONG width, height;
114 RECT client_rect;
115 RECT window_rect;
116 POINT client_pos;
117
118 /*
119 * Sanity checking.
120 */
121
122 assert(fb->hWnd);
123 assert(fb->width && fb->height);
124 assert(fb->client_rect.right == fb->client_rect.left + fb->width);
125 assert(fb->client_rect.bottom == fb->client_rect.top + fb->height);
126
127 /*
128 * Get the client area size.
129 */
130
131 if (!GetClientRect(fb->hWnd, &client_rect)) {
132 return;
133 }
134
135 assert(client_rect.left == 0);
136 assert(client_rect.top == 0);
137 width = client_rect.right - client_rect.left;
138 height = client_rect.bottom - client_rect.top;
139
140 fb->minimized = width == 0 || height == 0;
141
142 if (width <= 0 || height <= 0) {
143 /*
144 * When the window is minimized GetClientRect will return zeros. Simply
145 * preserve the current window size, until the window is restored or
146 * maximized again.
147 */
148
149 return;
150 }
151
152 if (width != fb->width || height != fb->height) {
153 fb->must_resize = TRUE;
154 fb->width = width;
155 fb->height = height;
156 }
157
158 client_pos.x = 0;
159 client_pos.y = 0;
160 if (ClientToScreen(fb->hWnd, &client_pos) &&
161 GetWindowRect(fb->hWnd, &window_rect)) {
162 fb->client_rect.left = client_pos.x - window_rect.left;
163 fb->client_rect.top = client_pos.y - window_rect.top;
164 }
165
166 fb->client_rect.right = fb->client_rect.left + fb->width;
167 fb->client_rect.bottom = fb->client_rect.top + fb->height;
168
169 #if 0
170 debug_printf("\n");
171 debug_printf("%s: hwnd = %p\n", __FUNCTION__, fb->hWnd);
172 debug_printf("%s: client_position = (%li, %li)\n",
173 __FUNCTION__, client_pos.x, client_pos.y);
174 debug_printf("%s: window_rect = (%li, %li) - (%li, %li)\n",
175 __FUNCTION__,
176 window_rect.left, window_rect.top,
177 window_rect.right, window_rect.bottom);
178 debug_printf("%s: client_rect = (%li, %li) - (%li, %li)\n",
179 __FUNCTION__,
180 fb->client_rect.left, fb->client_rect.top,
181 fb->client_rect.right, fb->client_rect.bottom);
182 #endif
183 }
184
185
186 /**
187 * @sa http://msdn.microsoft.com/en-us/library/ms644975(VS.85).aspx
188 * @sa http://msdn.microsoft.com/en-us/library/ms644960(VS.85).aspx
189 */
190 LRESULT CALLBACK
191 stw_call_window_proc(int nCode, WPARAM wParam, LPARAM lParam)
192 {
193 struct stw_tls_data *tls_data;
194 PCWPSTRUCT pParams = (PCWPSTRUCT)lParam;
195 struct stw_framebuffer *fb;
196
197 tls_data = stw_tls_get_data();
198 if (!tls_data)
199 return 0;
200
201 if (nCode < 0 || !stw_dev)
202 return CallNextHookEx(tls_data->hCallWndProcHook, nCode, wParam, lParam);
203
204 if (pParams->message == WM_WINDOWPOSCHANGED) {
205 /* We handle WM_WINDOWPOSCHANGED instead of WM_SIZE because according to
206 * http://blogs.msdn.com/oldnewthing/archive/2008/01/15/7113860.aspx
207 * WM_SIZE is generated from WM_WINDOWPOSCHANGED by DefWindowProc so it
208 * can be masked out by the application.
209 */
210 LPWINDOWPOS lpWindowPos = (LPWINDOWPOS)pParams->lParam;
211 if ((lpWindowPos->flags & SWP_SHOWWINDOW) ||
212 !(lpWindowPos->flags & SWP_NOMOVE) ||
213 !(lpWindowPos->flags & SWP_NOSIZE)) {
214 fb = stw_framebuffer_from_hwnd( pParams->hwnd );
215 if (fb) {
216 /* Size in WINDOWPOS includes the window frame, so get the size
217 * of the client area via GetClientRect.
218 */
219 stw_framebuffer_get_size(fb);
220 stw_framebuffer_release(fb);
221 }
222 }
223 }
224 else if (pParams->message == WM_DESTROY) {
225 pipe_mutex_lock( stw_dev->fb_mutex );
226 fb = stw_framebuffer_from_hwnd_locked( pParams->hwnd );
227 if (fb)
228 stw_framebuffer_destroy_locked(fb);
229 pipe_mutex_unlock( stw_dev->fb_mutex );
230 }
231
232 return CallNextHookEx(tls_data->hCallWndProcHook, nCode, wParam, lParam);
233 }
234
235
236 struct stw_framebuffer *
237 stw_framebuffer_create(HDC hdc, int iPixelFormat)
238 {
239 HWND hWnd;
240 struct stw_framebuffer *fb;
241 const struct stw_pixelformat_info *pfi;
242
243 /* We only support drawing to a window. */
244 hWnd = WindowFromDC( hdc );
245 if (!hWnd)
246 return NULL;
247
248 fb = CALLOC_STRUCT( stw_framebuffer );
249 if (fb == NULL)
250 return NULL;
251
252 fb->hWnd = hWnd;
253 fb->iPixelFormat = iPixelFormat;
254
255 /*
256 * We often need a displayable pixel format to make GDI happy. Set it
257 * here (always 1, i.e., out first pixel format) where appropriate.
258 */
259 fb->iDisplayablePixelFormat = iPixelFormat <= stw_dev->pixelformat_count
260 ? iPixelFormat : 1;
261
262 fb->pfi = pfi = stw_pixelformat_get_info( iPixelFormat );
263 fb->stfb = stw_st_create_framebuffer( fb );
264 if (!fb->stfb) {
265 FREE( fb );
266 return NULL;
267 }
268
269 fb->refcnt = 1;
270
271 /*
272 * Windows can be sometimes have zero width and or height, but we ensure
273 * a non-zero framebuffer size at all times.
274 */
275
276 fb->must_resize = TRUE;
277 fb->width = 1;
278 fb->height = 1;
279 fb->client_rect.left = 0;
280 fb->client_rect.top = 0;
281 fb->client_rect.right = fb->client_rect.left + fb->width;
282 fb->client_rect.bottom = fb->client_rect.top + fb->height;
283
284 stw_framebuffer_get_size(fb);
285
286 pipe_mutex_init( fb->mutex );
287
288 /* This is the only case where we lock the stw_framebuffer::mutex before
289 * stw_dev::fb_mutex, since no other thread can know about this framebuffer
290 * and we must prevent any other thread from destroying it before we return.
291 */
292 pipe_mutex_lock( fb->mutex );
293
294 pipe_mutex_lock( stw_dev->fb_mutex );
295 fb->next = stw_dev->fb_head;
296 stw_dev->fb_head = fb;
297 pipe_mutex_unlock( stw_dev->fb_mutex );
298
299 return fb;
300 }
301
302
303 /**
304 * Have ptr reference fb. The referenced framebuffer should be locked.
305 */
306 void
307 stw_framebuffer_reference(struct stw_framebuffer **ptr,
308 struct stw_framebuffer *fb)
309 {
310 struct stw_framebuffer *old_fb = *ptr;
311
312 if (old_fb == fb)
313 return;
314
315 if (fb)
316 fb->refcnt++;
317 if (old_fb) {
318 pipe_mutex_lock(stw_dev->fb_mutex);
319
320 pipe_mutex_lock(old_fb->mutex);
321 stw_framebuffer_destroy_locked(old_fb);
322
323 pipe_mutex_unlock(stw_dev->fb_mutex);
324 }
325
326 *ptr = fb;
327 }
328
329
330 /**
331 * Update the framebuffer's size if necessary.
332 */
333 void
334 stw_framebuffer_update(struct stw_framebuffer *fb)
335 {
336 assert(fb->stfb);
337 assert(fb->height);
338 assert(fb->width);
339
340 /* XXX: It would be nice to avoid checking the size again -- in theory
341 * stw_call_window_proc would have cought the resize and stored the right
342 * size already, but unfortunately threads created before the DllMain is
343 * called don't get a DLL_THREAD_ATTACH notification, and there is no way
344 * to know of their existing without using the not very portable PSAPI.
345 */
346 stw_framebuffer_get_size(fb);
347 }
348
349
350 void
351 stw_framebuffer_cleanup(void)
352 {
353 struct stw_framebuffer *fb;
354 struct stw_framebuffer *next;
355
356 if (!stw_dev)
357 return;
358
359 pipe_mutex_lock( stw_dev->fb_mutex );
360
361 fb = stw_dev->fb_head;
362 while (fb) {
363 next = fb->next;
364
365 pipe_mutex_lock(fb->mutex);
366 stw_framebuffer_destroy_locked(fb);
367
368 fb = next;
369 }
370 stw_dev->fb_head = NULL;
371
372 pipe_mutex_unlock( stw_dev->fb_mutex );
373 }
374
375
376 /**
377 * Given an hdc, return the corresponding stw_framebuffer.
378 */
379 static inline struct stw_framebuffer *
380 stw_framebuffer_from_hdc_locked(
381 HDC hdc )
382 {
383 HWND hwnd;
384
385 hwnd = WindowFromDC(hdc);
386 if (!hwnd) {
387 return NULL;
388 }
389
390 return stw_framebuffer_from_hwnd_locked(hwnd);
391 }
392
393
394 /**
395 * Given an hdc, return the corresponding stw_framebuffer.
396 */
397 struct stw_framebuffer *
398 stw_framebuffer_from_hdc(HDC hdc)
399 {
400 struct stw_framebuffer *fb;
401
402 if (!stw_dev)
403 return NULL;
404
405 pipe_mutex_lock( stw_dev->fb_mutex );
406 fb = stw_framebuffer_from_hdc_locked(hdc);
407 pipe_mutex_unlock( stw_dev->fb_mutex );
408
409 return fb;
410 }
411
412
413 /**
414 * Given an hdc, return the corresponding stw_framebuffer.
415 */
416 struct stw_framebuffer *
417 stw_framebuffer_from_hwnd(HWND hwnd)
418 {
419 struct stw_framebuffer *fb;
420
421 pipe_mutex_lock( stw_dev->fb_mutex );
422 fb = stw_framebuffer_from_hwnd_locked(hwnd);
423 pipe_mutex_unlock( stw_dev->fb_mutex );
424
425 return fb;
426 }
427
428
429 BOOL APIENTRY
430 DrvSetPixelFormat(HDC hdc, LONG iPixelFormat)
431 {
432 uint count;
433 uint index;
434 struct stw_framebuffer *fb;
435
436 if (!stw_dev)
437 return FALSE;
438
439 index = (uint) iPixelFormat - 1;
440 count = stw_pixelformat_get_count();
441 if (index >= count)
442 return FALSE;
443
444 fb = stw_framebuffer_from_hdc_locked(hdc);
445 if (fb) {
446 /*
447 * SetPixelFormat must be called only once. However ignore
448 * pbuffers, for which the framebuffer object is created first.
449 */
450 boolean bPbuffer = fb->bPbuffer;
451
452 stw_framebuffer_release( fb );
453
454 return bPbuffer;
455 }
456
457 fb = stw_framebuffer_create(hdc, iPixelFormat);
458 if (!fb) {
459 return FALSE;
460 }
461
462 stw_framebuffer_release( fb );
463
464 /* Some applications mistakenly use the undocumented wglSetPixelFormat
465 * function instead of SetPixelFormat, so we call SetPixelFormat here to
466 * avoid opengl32.dll's wglCreateContext to fail */
467 if (GetPixelFormat(hdc) == 0) {
468 BOOL bRet = SetPixelFormat(hdc, iPixelFormat, NULL);
469 assert(bRet);
470 }
471
472 return TRUE;
473 }
474
475
476 int
477 stw_pixelformat_get(HDC hdc)
478 {
479 int iPixelFormat = 0;
480 struct stw_framebuffer *fb;
481
482 fb = stw_framebuffer_from_hdc(hdc);
483 if (fb) {
484 iPixelFormat = fb->iPixelFormat;
485 stw_framebuffer_release(fb);
486 }
487
488 return iPixelFormat;
489 }
490
491
492 BOOL APIENTRY
493 DrvPresentBuffers(HDC hdc, PGLPRESENTBUFFERSDATA data)
494 {
495 struct stw_framebuffer *fb;
496 struct pipe_screen *screen;
497 struct pipe_resource *res;
498
499 if (!stw_dev)
500 return FALSE;
501
502 fb = stw_framebuffer_from_hdc( hdc );
503 if (fb == NULL)
504 return FALSE;
505
506 screen = stw_dev->screen;
507
508 res = (struct pipe_resource *)data->pPrivateData;
509
510 if (data->hSharedSurface != fb->hSharedSurface) {
511 if (fb->shared_surface) {
512 stw_dev->stw_winsys->shared_surface_close(screen, fb->shared_surface);
513 fb->shared_surface = NULL;
514 }
515
516 fb->hSharedSurface = data->hSharedSurface;
517
518 if (data->hSharedSurface &&
519 stw_dev->stw_winsys->shared_surface_open) {
520 fb->shared_surface =
521 stw_dev->stw_winsys->shared_surface_open(screen,
522 fb->hSharedSurface);
523 }
524 }
525
526 if (!fb->minimized) {
527 if (fb->shared_surface) {
528 stw_dev->stw_winsys->compose(screen,
529 res,
530 fb->shared_surface,
531 &fb->client_rect,
532 data->PresentHistoryToken);
533 }
534 else {
535 stw_dev->stw_winsys->present( screen, res, hdc );
536 }
537 }
538
539 stw_framebuffer_update(fb);
540 stw_notify_current_locked(fb);
541
542 stw_framebuffer_release(fb);
543
544 return TRUE;
545 }
546
547
548 /**
549 * Queue a composition.
550 *
551 * It will drop the lock on success.
552 */
553 BOOL
554 stw_framebuffer_present_locked(HDC hdc,
555 struct stw_framebuffer *fb,
556 struct pipe_resource *res)
557 {
558 if (stw_dev->callbacks.wglCbPresentBuffers &&
559 stw_dev->stw_winsys->compose) {
560 GLCBPRESENTBUFFERSDATA data;
561
562 memset(&data, 0, sizeof data);
563 data.magic1 = 2;
564 data.magic2 = 0;
565 data.AdapterLuid = stw_dev->AdapterLuid;
566 data.rect = fb->client_rect;
567 data.pPrivateData = (void *)res;
568
569 stw_notify_current_locked(fb);
570 stw_framebuffer_release(fb);
571
572 return stw_dev->callbacks.wglCbPresentBuffers(hdc, &data);
573 }
574 else {
575 struct pipe_screen *screen = stw_dev->screen;
576
577 stw_dev->stw_winsys->present( screen, res, hdc );
578
579 stw_framebuffer_update(fb);
580 stw_notify_current_locked(fb);
581 stw_framebuffer_release(fb);
582
583 return TRUE;
584 }
585 }
586
587
588 BOOL APIENTRY
589 DrvSwapBuffers(HDC hdc)
590 {
591 struct stw_context *ctx;
592 struct stw_framebuffer *fb;
593
594 if (!stw_dev)
595 return FALSE;
596
597 fb = stw_framebuffer_from_hdc( hdc );
598 if (fb == NULL)
599 return FALSE;
600
601 if (!(fb->pfi->pfd.dwFlags & PFD_DOUBLEBUFFER)) {
602 stw_framebuffer_release(fb);
603 return TRUE;
604 }
605
606 /* Display the HUD */
607 ctx = stw_current_context();
608 if (ctx && ctx->hud) {
609 struct pipe_resource *back =
610 stw_get_framebuffer_resource(fb->stfb, ST_ATTACHMENT_BACK_LEFT);
611 hud_draw(ctx->hud, back);
612 }
613
614 stw_flush_current_locked(fb);
615
616 return stw_st_swap_framebuffer_locked(hdc, fb->stfb);
617 }
618
619
620 BOOL APIENTRY
621 DrvSwapLayerBuffers(HDC hdc, UINT fuPlanes)
622 {
623 if (fuPlanes & WGL_SWAP_MAIN_PLANE)
624 return DrvSwapBuffers(hdc);
625
626 return FALSE;
627 }