• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2011 Joakim Sindholt <opensource@zhasha.com>
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * on the rights to use, copy, modify, merge, publish, distribute, sub
8  * license, and/or sell copies of the Software, and to permit persons to whom
9  * the Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice (including the next
12  * paragraph) shall be included in all copies or substantial portions of the
13  * Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
18  * THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
19  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
20  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
21  * USE OR OTHER DEALINGS IN THE SOFTWARE. */
22 
23 #include "swapchain9.h"
24 #include "surface9.h"
25 #include "device9.h"
26 
27 #include "nine_helpers.h"
28 #include "nine_pipe.h"
29 #include "nine_dump.h"
30 
31 #include "util/u_atomic.h"
32 #include "util/u_inlines.h"
33 #include "util/u_surface.h"
34 #include "hud/hud_context.h"
35 #include "frontend/drm_driver.h"
36 
37 #include "threadpool.h"
38 
39 #define DBG_CHANNEL DBG_SWAPCHAIN
40 
41 #define UNTESTED(n) DBG("UNTESTED point %d. Please tell if it worked\n", n)
42 
43 HRESULT
NineSwapChain9_ctor(struct NineSwapChain9 * This,struct NineUnknownParams * pParams,BOOL implicit,ID3DPresent * pPresent,D3DPRESENT_PARAMETERS * pPresentationParameters,struct d3dadapter9_context * pCTX,HWND hFocusWindow,D3DDISPLAYMODEEX * mode)44 NineSwapChain9_ctor( struct NineSwapChain9 *This,
45                      struct NineUnknownParams *pParams,
46                      BOOL implicit,
47                      ID3DPresent *pPresent,
48                      D3DPRESENT_PARAMETERS *pPresentationParameters,
49                      struct d3dadapter9_context *pCTX,
50                      HWND hFocusWindow,
51                      D3DDISPLAYMODEEX *mode )
52 {
53     HRESULT hr;
54     int i;
55 
56     DBG("This=%p pDevice=%p pPresent=%p pCTX=%p hFocusWindow=%p\n",
57         This, pParams->device, pPresent, pCTX, hFocusWindow);
58 
59     hr = NineUnknown_ctor(&This->base, pParams);
60     if (FAILED(hr))
61         return hr;
62 
63     This->screen = NineDevice9_GetScreen(This->base.device);
64     This->implicit = implicit;
65     This->actx = pCTX;
66     This->present = pPresent;
67     This->mode = NULL;
68 
69     ID3DPresent_AddRef(pPresent);
70     if (This->base.device->minor_version_num > 2) {
71         D3DPRESENT_PARAMETERS2 params2;
72 
73         memset(&params2, 0, sizeof(D3DPRESENT_PARAMETERS2));
74         params2.AllowDISCARDDelayedRelease = This->actx->discard_delayed_release;
75         params2.TearFreeDISCARD = This->actx->tearfree_discard;
76         ID3DPresent_SetPresentParameters2(pPresent, &params2);
77     }
78 
79     if (!pPresentationParameters->hDeviceWindow)
80         pPresentationParameters->hDeviceWindow = hFocusWindow;
81 
82     This->rendering_done = FALSE;
83     This->pool = NULL;
84     for (i = 0; i < D3DPRESENT_BACK_BUFFERS_MAX_EX + 1; i++) {
85         This->pending_presentation[i] = calloc(1, sizeof(BOOL));
86         if (!This->pending_presentation[i])
87             return E_OUTOFMEMORY;
88     }
89     return NineSwapChain9_Resize(This, pPresentationParameters, mode);
90 }
91 
92 static D3DWindowBuffer *
D3DWindowBuffer_create(struct NineSwapChain9 * This,struct pipe_resource * resource,int depth,int for_frontbuffer_reading)93 D3DWindowBuffer_create(struct NineSwapChain9 *This,
94                        struct pipe_resource *resource,
95                        int depth,
96                        int for_frontbuffer_reading)
97 {
98     D3DWindowBuffer *ret;
99     struct pipe_context *pipe = nine_context_get_pipe_acquire(This->base.device);
100     struct winsys_handle whandle;
101     int stride, dmaBufFd;
102     HRESULT hr;
103 
104     memset(&whandle, 0, sizeof(whandle));
105     whandle.type = WINSYS_HANDLE_TYPE_FD;
106     if (!This->screen->resource_get_handle(This->screen, pipe, resource,
107                                            &whandle,
108                                            for_frontbuffer_reading ?
109                                                PIPE_HANDLE_USAGE_FRAMEBUFFER_WRITE :
110                                                PIPE_HANDLE_USAGE_EXPLICIT_FLUSH)) {
111         ERR("Failed to get handle for resource\n");
112         return NULL;
113     }
114     nine_context_get_pipe_release(This->base.device);
115     stride = whandle.stride;
116     dmaBufFd = whandle.handle;
117     hr = ID3DPresent_NewD3DWindowBufferFromDmaBuf(This->present,
118                                                   dmaBufFd,
119                                                   resource->width0,
120                                                   resource->height0,
121                                                   stride,
122                                                   depth,
123                                                   32,
124                                                   &ret);
125     assert (SUCCEEDED(hr));
126 
127     if (FAILED(hr)) {
128         ERR("Failed to create new D3DWindowBufferFromDmaBuf\n");
129         return NULL;
130     }
131     return ret;
132 }
133 
134 static void
D3DWindowBuffer_release(struct NineSwapChain9 * This,D3DWindowBuffer * present_handle)135 D3DWindowBuffer_release(struct NineSwapChain9 *This,
136                         D3DWindowBuffer *present_handle)
137 {
138     int i;
139 
140     /* IsBufferReleased API not available */
141     if (This->base.device->minor_version_num <= 2) {
142         ID3DPresent_DestroyD3DWindowBuffer(This->present, present_handle);
143         return;
144     }
145 
146     /* Add it to the 'pending release' list */
147     for (i = 0; i < D3DPRESENT_BACK_BUFFERS_MAX_EX + 1; i++) {
148         if (!This->present_handles_pending_release[i]) {
149             This->present_handles_pending_release[i] = present_handle;
150             break;
151         }
152     }
153     if (i == (D3DPRESENT_BACK_BUFFERS_MAX_EX + 1)) {
154         ERR("Server not releasing buffers...\n");
155         assert(false);
156     }
157 
158     /* Destroy elements of the list released by the server */
159     for (i = 0; i < D3DPRESENT_BACK_BUFFERS_MAX_EX + 1; i++) {
160         if (This->present_handles_pending_release[i] &&
161             ID3DPresent_IsBufferReleased(This->present, This->present_handles_pending_release[i])) {
162             /* WaitBufferReleased also waits the presentation feedback
163              * (which should arrive at about the same time),
164              * while IsBufferReleased doesn't. DestroyD3DWindowBuffer unfortunately
165              * checks it to release immediately all data, else the release
166              * is postponed for This->present release. To avoid leaks (we may handle
167              * a lot of resize), call WaitBufferReleased. */
168             ID3DPresent_WaitBufferReleased(This->present, This->present_handles_pending_release[i]);
169             ID3DPresent_DestroyD3DWindowBuffer(This->present, This->present_handles_pending_release[i]);
170             This->present_handles_pending_release[i] = NULL;
171         }
172     }
173 }
174 
175 static int
176 NineSwapChain9_GetBackBufferCountForParams( struct NineSwapChain9 *This,
177                                             D3DPRESENT_PARAMETERS *pParams );
178 
179 HRESULT
NineSwapChain9_Resize(struct NineSwapChain9 * This,D3DPRESENT_PARAMETERS * pParams,D3DDISPLAYMODEEX * mode)180 NineSwapChain9_Resize( struct NineSwapChain9 *This,
181                        D3DPRESENT_PARAMETERS *pParams,
182                        D3DDISPLAYMODEEX *mode )
183 {
184     struct NineDevice9 *pDevice = This->base.device;
185     D3DSURFACE_DESC desc;
186     HRESULT hr;
187     struct pipe_resource *resource, tmplt;
188     enum pipe_format pf;
189     BOOL has_present_buffers = FALSE;
190     int depth;
191     unsigned i, oldBufferCount, newBufferCount;
192     D3DMULTISAMPLE_TYPE multisample_type;
193 
194     DBG("This=%p pParams=%p\n", This, pParams);
195     user_assert(pParams != NULL, E_POINTER);
196     user_assert(pParams->SwapEffect, D3DERR_INVALIDCALL);
197     user_assert((pParams->SwapEffect != D3DSWAPEFFECT_COPY) ||
198                 (pParams->BackBufferCount <= 1), D3DERR_INVALIDCALL);
199     user_assert(pDevice->ex || pParams->BackBufferCount <=
200                 D3DPRESENT_BACK_BUFFERS_MAX, D3DERR_INVALIDCALL);
201     user_assert(!pDevice->ex || pParams->BackBufferCount <=
202                 D3DPRESENT_BACK_BUFFERS_MAX_EX, D3DERR_INVALIDCALL);
203     user_assert(pDevice->ex ||
204                 (pParams->SwapEffect == D3DSWAPEFFECT_FLIP) ||
205                 (pParams->SwapEffect == D3DSWAPEFFECT_COPY) ||
206                 (pParams->SwapEffect == D3DSWAPEFFECT_DISCARD), D3DERR_INVALIDCALL);
207 
208     DBG("pParams(%p):\n"
209         "BackBufferWidth: %u\n"
210         "BackBufferHeight: %u\n"
211         "BackBufferFormat: %s\n"
212         "BackBufferCount: %u\n"
213         "MultiSampleType: %u\n"
214         "MultiSampleQuality: %u\n"
215         "SwapEffect: %u\n"
216         "hDeviceWindow: %p\n"
217         "Windowed: %i\n"
218         "EnableAutoDepthStencil: %i\n"
219         "AutoDepthStencilFormat: %s\n"
220         "Flags: %s\n"
221         "FullScreen_RefreshRateInHz: %u\n"
222         "PresentationInterval: %x\n", pParams,
223         pParams->BackBufferWidth, pParams->BackBufferHeight,
224         d3dformat_to_string(pParams->BackBufferFormat),
225         pParams->BackBufferCount,
226         pParams->MultiSampleType, pParams->MultiSampleQuality,
227         pParams->SwapEffect, pParams->hDeviceWindow, pParams->Windowed,
228         pParams->EnableAutoDepthStencil,
229         d3dformat_to_string(pParams->AutoDepthStencilFormat),
230         nine_D3DPRESENTFLAG_to_str(pParams->Flags),
231         pParams->FullScreen_RefreshRateInHz,
232         pParams->PresentationInterval);
233 
234     if (pParams->BackBufferCount == 0) {
235         pParams->BackBufferCount = 1;
236     }
237 
238     if (pParams->BackBufferFormat == D3DFMT_UNKNOWN) {
239         pParams->BackBufferFormat = D3DFMT_A8R8G8B8;
240     }
241 
242     This->desired_fences = This->actx->throttling ? This->actx->throttling_value + 1 : 0;
243     /* +1 because we add the fence of the current buffer before popping an old one */
244     if (This->desired_fences > DRI_SWAP_FENCES_MAX)
245         This->desired_fences = DRI_SWAP_FENCES_MAX;
246 
247     if (This->actx->vblank_mode == 0)
248         pParams->PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
249     else if (This->actx->vblank_mode == 3)
250         pParams->PresentationInterval = D3DPRESENT_INTERVAL_ONE;
251 
252     if (mode && This->mode) {
253         *(This->mode) = *mode;
254     } else if (mode) {
255         This->mode = malloc(sizeof(D3DDISPLAYMODEEX));
256         memcpy(This->mode, mode, sizeof(D3DDISPLAYMODEEX));
257     } else {
258         free(This->mode);
259         This->mode = NULL;
260     }
261 
262     /* Note: It is the role of the backend to fill if necessary
263      * BackBufferWidth and BackBufferHeight */
264     hr = ID3DPresent_SetPresentParameters(This->present, pParams, This->mode);
265     if (hr != D3D_OK)
266         return hr;
267 
268     oldBufferCount = This->num_back_buffers;
269     newBufferCount = NineSwapChain9_GetBackBufferCountForParams(This, pParams);
270 
271     multisample_type = pParams->MultiSampleType;
272 
273     /* Map MultiSampleQuality to MultiSampleType */
274     hr = d3dmultisample_type_check(This->screen, pParams->BackBufferFormat,
275                                    &multisample_type,
276                                    pParams->MultiSampleQuality,
277                                    NULL);
278     if (FAILED(hr)) {
279         return hr;
280     }
281 
282     pf = d3d9_to_pipe_format_checked(This->screen, pParams->BackBufferFormat,
283                                      PIPE_TEXTURE_2D, multisample_type,
284                                      PIPE_BIND_RENDER_TARGET, FALSE, FALSE);
285 
286     if (This->actx->linear_framebuffer ||
287         (pf != PIPE_FORMAT_B8G8R8X8_UNORM &&
288         pf != PIPE_FORMAT_B8G8R8A8_UNORM) ||
289         pParams->SwapEffect != D3DSWAPEFFECT_DISCARD ||
290         multisample_type >= 2 ||
291         (This->actx->ref && This->actx->ref == This->screen))
292         has_present_buffers = TRUE;
293 
294     /* Note: the buffer depth has to match the window depth.
295      * In practice, ARGB buffers can be used with windows
296      * of depth 24. Windows of depth 32 are extremely rare.
297      * So even if the buffer is ARGB, say it is depth 24.
298      * It is common practice, for example that's how
299      * glamor implements depth 24.
300      * TODO: handle windows with other depths. Not possible in the short term.
301      * For example 16 bits.*/
302     depth = 24;
303 
304     memset(&tmplt, 0, sizeof(tmplt));
305     tmplt.target = PIPE_TEXTURE_2D;
306     tmplt.width0 = pParams->BackBufferWidth;
307     tmplt.height0 = pParams->BackBufferHeight;
308     tmplt.depth0 = 1;
309     tmplt.last_level = 0;
310     tmplt.array_size = 1;
311     tmplt.usage = PIPE_USAGE_DEFAULT;
312     tmplt.flags = 0;
313 
314     desc.Type = D3DRTYPE_SURFACE;
315     desc.Pool = D3DPOOL_DEFAULT;
316     desc.MultiSampleType = pParams->MultiSampleType;
317     desc.MultiSampleQuality = pParams->MultiSampleQuality;
318     desc.Width = pParams->BackBufferWidth;
319     desc.Height = pParams->BackBufferHeight;
320 
321     for (i = 0; i < oldBufferCount; i++) {
322         if (This->tasks[i])
323             _mesa_threadpool_wait_for_task(This->pool, &(This->tasks[i]));
324     }
325     memset(This->tasks, 0, sizeof(This->tasks));
326 
327     if (This->pool) {
328         _mesa_threadpool_destroy(This, This->pool);
329         This->pool = NULL;
330     }
331     This->enable_threadpool = This->actx->thread_submit && (pParams->SwapEffect != D3DSWAPEFFECT_COPY);
332     if (This->enable_threadpool)
333         This->pool = _mesa_threadpool_create(This);
334     if (!This->pool)
335         This->enable_threadpool = FALSE;
336 
337     for (i = 0; i < oldBufferCount; i++) {
338         D3DWindowBuffer_release(This, This->present_handles[i]);
339         This->present_handles[i] = NULL;
340         if (This->present_buffers[i])
341             pipe_resource_reference(&(This->present_buffers[i]), NULL);
342     }
343 
344     if (newBufferCount != oldBufferCount) {
345         for (i = newBufferCount; i < oldBufferCount;
346              ++i)
347             NineUnknown_Detach(NineUnknown(This->buffers[i]));
348 
349         for (i = oldBufferCount; i < newBufferCount; ++i) {
350             This->buffers[i] = NULL;
351             This->present_handles[i] = NULL;
352         }
353     }
354     This->num_back_buffers = newBufferCount;
355 
356     for (i = 0; i < newBufferCount; ++i) {
357         tmplt.bind = PIPE_BIND_SAMPLER_VIEW | PIPE_BIND_RENDER_TARGET;
358         tmplt.nr_samples = multisample_type;
359         tmplt.nr_storage_samples = multisample_type;
360         if (!has_present_buffers)
361             tmplt.bind |= NINE_BIND_PRESENTBUFFER_FLAGS;
362         tmplt.format = d3d9_to_pipe_format_checked(This->screen,
363                                                    pParams->BackBufferFormat,
364                                                    PIPE_TEXTURE_2D,
365                                                    tmplt.nr_samples,
366                                                    tmplt.bind, FALSE, FALSE);
367         if (tmplt.format == PIPE_FORMAT_NONE)
368             return D3DERR_INVALIDCALL;
369         resource = nine_resource_create_with_retry(pDevice, This->screen, &tmplt);
370         if (!resource) {
371             DBG("Failed to create pipe_resource.\n");
372             return D3DERR_OUTOFVIDEOMEMORY;
373         }
374         if (pParams->Flags & D3DPRESENTFLAG_LOCKABLE_BACKBUFFER)
375             resource->flags |= NINE_RESOURCE_FLAG_LOCKABLE;
376         if (This->buffers[i]) {
377             NineSurface9_SetMultiSampleType(This->buffers[i], desc.MultiSampleType);
378             NineSurface9_SetResourceResize(This->buffers[i], resource);
379             if (has_present_buffers)
380                 pipe_resource_reference(&resource, NULL);
381         } else {
382             desc.Format = pParams->BackBufferFormat;
383             desc.Usage = D3DUSAGE_RENDERTARGET;
384             hr = NineSurface9_new(pDevice, NineUnknown(This), resource, NULL, 0,
385                                   0, 0, &desc, &This->buffers[i]);
386             if (has_present_buffers)
387                 pipe_resource_reference(&resource, NULL);
388             if (FAILED(hr)) {
389                 DBG("Failed to create RT surface.\n");
390                 return hr;
391             }
392             This->buffers[i]->base.base.forward = FALSE;
393         }
394         if (has_present_buffers) {
395             tmplt.format = PIPE_FORMAT_B8G8R8X8_UNORM;
396             tmplt.bind = NINE_BIND_PRESENTBUFFER_FLAGS;
397             tmplt.nr_samples = 0;
398             tmplt.nr_storage_samples = 0;
399             if (This->actx->linear_framebuffer)
400                 tmplt.bind |= PIPE_BIND_LINEAR;
401             if (pParams->SwapEffect != D3DSWAPEFFECT_DISCARD)
402                 tmplt.bind |= PIPE_BIND_RENDER_TARGET;
403             resource = nine_resource_create_with_retry(pDevice, This->screen, &tmplt);
404             pipe_resource_reference(&(This->present_buffers[i]), resource);
405         }
406         This->present_handles[i] = D3DWindowBuffer_create(This, resource, depth, false);
407         pipe_resource_reference(&resource, NULL);
408         if (!This->present_handles[i]) {
409             return D3DERR_DRIVERINTERNALERROR;
410         }
411     }
412     if (pParams->EnableAutoDepthStencil) {
413         tmplt.bind = d3d9_get_pipe_depth_format_bindings(pParams->AutoDepthStencilFormat);
414         tmplt.nr_samples = multisample_type;
415         tmplt.nr_storage_samples = multisample_type;
416         tmplt.format = d3d9_to_pipe_format_checked(This->screen,
417                                                    pParams->AutoDepthStencilFormat,
418                                                    PIPE_TEXTURE_2D,
419                                                    tmplt.nr_samples,
420                                                    tmplt.bind,
421                                                    FALSE, FALSE);
422 
423         if (tmplt.format == PIPE_FORMAT_NONE)
424             return D3DERR_INVALIDCALL;
425 
426         if (This->zsbuf) {
427             resource = nine_resource_create_with_retry(pDevice, This->screen, &tmplt);
428             if (!resource) {
429                 DBG("Failed to create pipe_resource for depth buffer.\n");
430                 return D3DERR_OUTOFVIDEOMEMORY;
431             }
432 
433             NineSurface9_SetMultiSampleType(This->zsbuf, desc.MultiSampleType);
434             NineSurface9_SetResourceResize(This->zsbuf, resource);
435             pipe_resource_reference(&resource, NULL);
436         } else {
437             hr = NineDevice9_CreateDepthStencilSurface(pDevice,
438                                                        pParams->BackBufferWidth,
439                                                        pParams->BackBufferHeight,
440                                                        pParams->AutoDepthStencilFormat,
441                                                        pParams->MultiSampleType,
442                                                        pParams->MultiSampleQuality,
443                                                        0,
444                                                        (IDirect3DSurface9 **)&This->zsbuf,
445                                                        NULL);
446             if (FAILED(hr)) {
447                 DBG("Failed to create ZS surface.\n");
448                 return hr;
449             }
450             NineUnknown_ConvertRefToBind(NineUnknown(This->zsbuf));
451         }
452     }
453 
454     This->params = *pParams;
455 
456     return D3D_OK;
457 }
458 
459 /* Throttling: code adapted from the dri frontend */
460 
461 /**
462  * swap_fences_pop_front - pull a fence from the throttle queue
463  *
464  * If the throttle queue is filled to the desired number of fences,
465  * pull fences off the queue until the number is less than the desired
466  * number of fences, and return the last fence pulled.
467  */
468 static struct pipe_fence_handle *
swap_fences_pop_front(struct NineSwapChain9 * This)469 swap_fences_pop_front(struct NineSwapChain9 *This)
470 {
471     struct pipe_screen *screen = This->screen;
472     struct pipe_fence_handle *fence = NULL;
473 
474     if (This->desired_fences == 0)
475         return NULL;
476 
477     if (This->cur_fences >= This->desired_fences) {
478         screen->fence_reference(screen, &fence, This->swap_fences[This->tail]);
479         screen->fence_reference(screen, &This->swap_fences[This->tail++], NULL);
480         This->tail &= DRI_SWAP_FENCES_MASK;
481         --This->cur_fences;
482     }
483     return fence;
484 }
485 
486 
487 /**
488  * swap_fences_see_front - same than swap_fences_pop_front without
489  * pulling
490  *
491  */
492 
493 static struct pipe_fence_handle *
swap_fences_see_front(struct NineSwapChain9 * This)494 swap_fences_see_front(struct NineSwapChain9 *This)
495 {
496     struct pipe_screen *screen = This->screen;
497     struct pipe_fence_handle *fence = NULL;
498 
499     if (This->desired_fences == 0)
500         return NULL;
501 
502     if (This->cur_fences >= This->desired_fences) {
503         screen->fence_reference(screen, &fence, This->swap_fences[This->tail]);
504     }
505     return fence;
506 }
507 
508 
509 /**
510  * swap_fences_push_back - push a fence onto the throttle queue at the back
511  *
512  * push a fence onto the throttle queue and pull fences of the queue
513  * so that the desired number of fences are on the queue.
514  */
515 static void
swap_fences_push_back(struct NineSwapChain9 * This,struct pipe_fence_handle * fence)516 swap_fences_push_back(struct NineSwapChain9 *This,
517                       struct pipe_fence_handle *fence)
518 {
519     struct pipe_screen *screen = This->screen;
520 
521     if (!fence || This->desired_fences == 0)
522         return;
523 
524     while(This->cur_fences == This->desired_fences)
525         swap_fences_pop_front(This);
526 
527     This->cur_fences++;
528     screen->fence_reference(screen, &This->swap_fences[This->head++],
529                             fence);
530     This->head &= DRI_SWAP_FENCES_MASK;
531 }
532 
533 
534 /**
535  * swap_fences_unref - empty the throttle queue
536  *
537  * pulls fences of the throttle queue until it is empty.
538  */
539 static void
swap_fences_unref(struct NineSwapChain9 * This)540 swap_fences_unref(struct NineSwapChain9 *This)
541 {
542     struct pipe_screen *screen = This->screen;
543 
544     while(This->cur_fences) {
545         screen->fence_reference(screen, &This->swap_fences[This->tail++], NULL);
546         This->tail &= DRI_SWAP_FENCES_MASK;
547         --This->cur_fences;
548     }
549 }
550 
551 void
NineSwapChain9_dtor(struct NineSwapChain9 * This)552 NineSwapChain9_dtor( struct NineSwapChain9 *This )
553 {
554     unsigned i;
555 
556     DBG("This=%p\n", This);
557 
558     if (This->pool)
559         _mesa_threadpool_destroy(This, This->pool);
560 
561     for (i = 0; i < D3DPRESENT_BACK_BUFFERS_MAX_EX + 1; i++) {
562         if (This->pending_presentation[i])
563             FREE(This->pending_presentation[i]);
564     }
565 
566     for (i = 0; i < D3DPRESENT_BACK_BUFFERS_MAX_EX + 1; i++) {
567         if (This->present_handles_pending_release[i])
568             ID3DPresent_DestroyD3DWindowBuffer(This->present, This->present_handles_pending_release[i]);
569     }
570 
571     for (i = 0; i < This->num_back_buffers; i++) {
572         if (This->buffers[i])
573             NineUnknown_Detach(NineUnknown(This->buffers[i]));
574         if (This->present_handles[i])
575             ID3DPresent_DestroyD3DWindowBuffer(This->present, This->present_handles[i]);
576         if (This->present_buffers[i])
577             pipe_resource_reference(&(This->present_buffers[i]), NULL);
578     }
579     if (This->zsbuf)
580         NineUnknown_Unbind(NineUnknown(This->zsbuf));
581 
582     if (This->present)
583         ID3DPresent_Release(This->present);
584 
585     swap_fences_unref(This);
586     NineUnknown_dtor(&This->base);
587 }
588 
589 static void
create_present_buffer(struct NineSwapChain9 * This,unsigned int width,unsigned int height,struct pipe_resource ** resource,D3DWindowBuffer ** present_handle)590 create_present_buffer( struct NineSwapChain9 *This,
591                        unsigned int width, unsigned int height,
592                        struct pipe_resource **resource,
593                        D3DWindowBuffer **present_handle)
594 {
595     struct pipe_resource tmplt;
596 
597     memset(&tmplt, 0, sizeof(tmplt));
598     tmplt.target = PIPE_TEXTURE_2D;
599     tmplt.width0 = width;
600     tmplt.height0 = height;
601     tmplt.depth0 = 1;
602     tmplt.last_level = 0;
603     tmplt.array_size = 1;
604     tmplt.usage = PIPE_USAGE_DEFAULT;
605     tmplt.flags = 0;
606     tmplt.format = PIPE_FORMAT_B8G8R8X8_UNORM;
607     tmplt.bind = NINE_BIND_BACKBUFFER_FLAGS |
608                  NINE_BIND_PRESENTBUFFER_FLAGS;
609     tmplt.nr_samples = 0;
610     if (This->actx->linear_framebuffer)
611         tmplt.bind |= PIPE_BIND_LINEAR;
612     *resource = nine_resource_create_with_retry(This->base.device, This->screen, &tmplt);
613 
614     *present_handle = D3DWindowBuffer_create(This, *resource, 24, true);
615 
616     if (!*present_handle) {
617         pipe_resource_reference(resource, NULL);
618     }
619 }
620 
621 static void
handle_draw_cursor_and_hud(struct NineSwapChain9 * This,struct pipe_resource * resource)622 handle_draw_cursor_and_hud( struct NineSwapChain9 *This, struct pipe_resource *resource)
623 {
624     struct NineDevice9 *device = This->base.device;
625     struct pipe_blit_info blit;
626     struct pipe_context *pipe;
627 
628     if (device->cursor.software && device->cursor.visible && device->cursor.w) {
629         memset(&blit, 0, sizeof(blit));
630         blit.src.resource = device->cursor.image;
631         blit.src.level = 0;
632         blit.src.format = device->cursor.image->format;
633         blit.src.box.x = 0;
634         blit.src.box.y = 0;
635         blit.src.box.z = 0;
636         blit.src.box.depth = 1;
637         blit.src.box.width = device->cursor.w;
638         blit.src.box.height = device->cursor.h;
639 
640         blit.dst.resource = resource;
641         blit.dst.level = 0;
642         blit.dst.format = resource->format;
643         blit.dst.box.z = 0;
644         blit.dst.box.depth = 1;
645 
646         blit.mask = PIPE_MASK_RGBA;
647         blit.filter = PIPE_TEX_FILTER_NEAREST;
648         blit.scissor_enable = FALSE;
649 
650         /* NOTE: blit messes up when box.x + box.width < 0, fix driver
651          * NOTE2: device->cursor.pos contains coordinates relative to the screen.
652          * This happens to be also the position of the cursor when we are fullscreen.
653          * We don't use sw cursor for Windowed mode */
654         blit.dst.box.x = MAX2(device->cursor.pos.x, 0) - device->cursor.hotspot.x;
655         blit.dst.box.y = MAX2(device->cursor.pos.y, 0) - device->cursor.hotspot.y;
656         blit.dst.box.width = blit.src.box.width;
657         blit.dst.box.height = blit.src.box.height;
658 
659         DBG("Blitting cursor(%ux%u) to (%i,%i).\n",
660             blit.src.box.width, blit.src.box.height,
661             blit.dst.box.x, blit.dst.box.y);
662 
663         blit.alpha_blend = TRUE;
664         pipe = NineDevice9_GetPipe(This->base.device);
665         pipe->blit(pipe, &blit);
666     }
667 
668     if (device->hud && resource) {
669         /* Implicit use of context pipe */
670         (void)NineDevice9_GetPipe(This->base.device);
671         hud_run(device->hud, NULL, resource); /* XXX: no offset */
672         /* HUD doesn't clobber stipple */
673         nine_state_restore_non_cso(device);
674     }
675 }
676 
677 struct end_present_struct {
678     struct pipe_screen *screen;
679     struct pipe_fence_handle *fence_to_wait;
680     ID3DPresent *present;
681     D3DWindowBuffer *present_handle;
682     BOOL *pending_presentation;
683     HWND hDestWindowOverride;
684 };
685 
work_present(void * data)686 static void work_present(void *data)
687 {
688     struct end_present_struct *work = data;
689     if (work->fence_to_wait) {
690         (void) work->screen->fence_finish(work->screen, NULL, work->fence_to_wait, PIPE_TIMEOUT_INFINITE);
691         work->screen->fence_reference(work->screen, &(work->fence_to_wait), NULL);
692     }
693     ID3DPresent_PresentBuffer(work->present, work->present_handle, work->hDestWindowOverride, NULL, NULL, NULL, 0);
694     p_atomic_set(work->pending_presentation, FALSE);
695     free(work);
696 }
697 
pend_present(struct NineSwapChain9 * This,struct pipe_fence_handle * fence,HWND hDestWindowOverride)698 static void pend_present(struct NineSwapChain9 *This,
699                          struct pipe_fence_handle *fence,
700                          HWND hDestWindowOverride)
701 {
702     struct end_present_struct *work = calloc(1, sizeof(struct end_present_struct));
703 
704     work->screen = This->screen;
705     This->screen->fence_reference(This->screen, &work->fence_to_wait, fence);
706     work->present = This->present;
707     work->present_handle = This->present_handles[0];
708     work->hDestWindowOverride = hDestWindowOverride;
709     work->pending_presentation = This->pending_presentation[0];
710     p_atomic_set(work->pending_presentation, TRUE);
711     This->tasks[0] = _mesa_threadpool_queue_task(This->pool, work_present, work);
712 
713     return;
714 }
715 
716 static inline HRESULT
present(struct NineSwapChain9 * This,const RECT * pSourceRect,const RECT * pDestRect,HWND hDestWindowOverride,const RGNDATA * pDirtyRegion,DWORD dwFlags)717 present( struct NineSwapChain9 *This,
718          const RECT *pSourceRect,
719          const RECT *pDestRect,
720          HWND hDestWindowOverride,
721          const RGNDATA *pDirtyRegion,
722          DWORD dwFlags )
723 {
724     struct pipe_context *pipe;
725     struct pipe_resource *resource;
726     struct pipe_fence_handle *fence;
727     HRESULT hr;
728     struct pipe_blit_info blit;
729     int target_width, target_height, target_depth, i;
730     RECT source_rect;
731     RECT dest_rect;
732 
733     DBG("present: This=%p pSourceRect=%p pDestRect=%p "
734         "pDirtyRegion=%p hDestWindowOverride=%p"
735         "dwFlags=%d resource=%p\n",
736         This, pSourceRect, pDestRect, pDirtyRegion,
737         hDestWindowOverride, (int)dwFlags, This->buffers[0]->base.resource);
738 
739     /* We can choose to only update pDirtyRegion, but the backend can choose
740      * to update everything. Let's ignore */
741     (void) pDirtyRegion;
742 
743     resource = This->buffers[0]->base.resource;
744 
745     if (pSourceRect) {
746         DBG("pSourceRect = (%u..%u)x(%u..%u)\n",
747             pSourceRect->left, pSourceRect->right,
748             pSourceRect->top, pSourceRect->bottom);
749         source_rect = *pSourceRect;
750         if (source_rect.top == 0 &&
751             source_rect.left == 0 &&
752             source_rect.bottom == resource->height0 &&
753             source_rect.right == resource->width0)
754             pSourceRect = NULL;
755         /* TODO: Handle more of pSourceRect.
756          * Currently we should support:
757          * . When there is no pSourceRect
758          * . When pSourceRect is the full buffer.
759          */
760     }
761     if (pDestRect) {
762         DBG("pDestRect = (%u..%u)x(%u..%u)\n",
763             pDestRect->left, pDestRect->right,
764             pDestRect->top, pDestRect->bottom);
765         dest_rect = *pDestRect;
766     }
767 
768     if (This->rendering_done)
769         goto bypass_rendering;
770 
771     if (This->params.SwapEffect == D3DSWAPEFFECT_DISCARD)
772         handle_draw_cursor_and_hud(This, resource);
773 
774     hr = ID3DPresent_GetWindowInfo(This->present, hDestWindowOverride, &target_width, &target_height, &target_depth);
775     (void)target_depth;
776 
777     /* Can happen with old Wine (presentation can still succeed),
778      * or at window destruction.
779      * Also disable for very old wine as D3DWindowBuffer_release
780      * cannot do the DestroyD3DWindowBuffer workaround. */
781     if (FAILED(hr) || target_width == 0 || target_height == 0 ||
782         This->base.device->minor_version_num <= 2) {
783         target_width = resource->width0;
784         target_height = resource->height0;
785     }
786 
787     if (pDestRect) {
788         dest_rect.top = MAX2(0, dest_rect.top);
789         dest_rect.left = MAX2(0, dest_rect.left);
790         dest_rect.bottom = MIN2(target_height, dest_rect.bottom);
791         dest_rect.right = MIN2(target_width, dest_rect.right);
792         target_height = dest_rect.bottom - dest_rect.top;
793         target_width = dest_rect.right - dest_rect.left;
794     }
795 
796     /* Switch to using presentation buffers on window resize.
797      * Note: Most apps should resize the d3d back buffers when
798      * a window resize is detected, which will result in a call to
799      * NineSwapChain9_Resize. Thus everything will get released,
800      * and it will switch back to not using separate presentation
801      * buffers. */
802     if (!This->present_buffers[0] &&
803         (target_width != resource->width0 || target_height != resource->height0)) {
804         BOOL failure = false;
805         struct pipe_resource *new_resource[This->num_back_buffers];
806         D3DWindowBuffer *new_handles[This->num_back_buffers];
807         for (i = 0; i < This->num_back_buffers; i++) {
808             /* Note: if (!new_handles[i]), new_resource[i]
809              * gets released and contains NULL */
810             create_present_buffer(This, target_width, target_height, &new_resource[i], &new_handles[i]);
811             if (!new_handles[i])
812                 failure = true;
813         }
814         if (failure) {
815             for (i = 0; i < This->num_back_buffers; i++) {
816                 if (new_resource[i])
817                     pipe_resource_reference(&new_resource[i], NULL);
818                 if (new_handles[i])
819                     D3DWindowBuffer_release(This, new_handles[i]);
820             }
821         } else {
822             for (i = 0; i < This->num_back_buffers; i++) {
823                 D3DWindowBuffer_release(This, This->present_handles[i]);
824                 This->present_handles[i] = new_handles[i];
825                 pipe_resource_reference(&This->present_buffers[i], new_resource[i]);
826                 pipe_resource_reference(&new_resource[i], NULL);
827             }
828         }
829     }
830 
831     pipe = NineDevice9_GetPipe(This->base.device);
832 
833     if (This->present_buffers[0]) {
834         memset(&blit, 0, sizeof(blit));
835         blit.src.resource = resource;
836         blit.src.level = 0; /* Note: This->buffers[0]->level should always be 0 */
837         blit.src.format = resource->format;
838         blit.src.box.z = 0;
839         blit.src.box.depth = 1;
840         blit.src.box.x = 0;
841         blit.src.box.y = 0;
842         blit.src.box.width = resource->width0;
843         blit.src.box.height = resource->height0;
844 
845         /* Reallocate a new presentation buffer if the target window
846          * size has changed */
847         if (target_width != This->present_buffers[0]->width0 ||
848             target_height != This->present_buffers[0]->height0) {
849             struct pipe_resource *new_resource;
850             D3DWindowBuffer *new_handle;
851 
852             create_present_buffer(This, target_width, target_height, &new_resource, &new_handle);
853             /* Switch to the new buffer */
854             if (new_handle) {
855                 D3DWindowBuffer_release(This, This->present_handles[0]);
856                 This->present_handles[0] = new_handle;
857                 pipe_resource_reference(&This->present_buffers[0], new_resource);
858                 pipe_resource_reference(&new_resource, NULL);
859             }
860         }
861 
862         resource = This->present_buffers[0];
863 
864         blit.dst.resource = resource;
865         blit.dst.level = 0;
866         blit.dst.format = resource->format;
867         blit.dst.box.z = 0;
868         blit.dst.box.depth = 1;
869         blit.dst.box.x = 0;
870         blit.dst.box.y = 0;
871         blit.dst.box.width = resource->width0;
872         blit.dst.box.height = resource->height0;
873 
874         blit.mask = PIPE_MASK_RGBA;
875         blit.filter = (blit.dst.box.width == blit.src.box.width &&
876                        blit.dst.box.height == blit.src.box.height) ?
877                           PIPE_TEX_FILTER_NEAREST : PIPE_TEX_FILTER_LINEAR;
878         blit.scissor_enable = FALSE;
879         blit.alpha_blend = FALSE;
880 
881         pipe->blit(pipe, &blit);
882     }
883 
884     /* The resource we present has to resolve fast clears
885      * if needed (and other things) */
886     pipe->flush_resource(pipe, resource);
887 
888     if (This->params.SwapEffect != D3DSWAPEFFECT_DISCARD)
889         handle_draw_cursor_and_hud(This, resource);
890 
891     fence = NULL;
892     /* When threadpool is enabled, we don't submit before the fence
893      * tells us rendering was finished, thus we can flush async there */
894     pipe->flush(pipe, &fence, PIPE_FLUSH_END_OF_FRAME | (This->enable_threadpool ? PIPE_FLUSH_ASYNC : 0));
895 
896     /* Present now for thread_submit, because we have the fence.
897      * It's possible we return WASSTILLDRAWING and still Present,
898      * but it should be fine. */
899     if (This->enable_threadpool)
900         pend_present(This, fence, hDestWindowOverride);
901     if (fence) {
902         swap_fences_push_back(This, fence);
903         This->screen->fence_reference(This->screen, &fence, NULL);
904     }
905 
906     This->rendering_done = TRUE;
907 bypass_rendering:
908 
909     if (dwFlags & D3DPRESENT_DONOTWAIT) {
910         UNTESTED(2);
911         BOOL still_draw = FALSE;
912         fence = swap_fences_see_front(This);
913         if (fence) {
914             still_draw = !This->screen->fence_finish(This->screen, NULL, fence, 0);
915             This->screen->fence_reference(This->screen, &fence, NULL);
916         }
917         if (still_draw)
918             return D3DERR_WASSTILLDRAWING;
919     }
920 
921     /* Throttle rendering if needed */
922     fence = swap_fences_pop_front(This);
923     if (fence) {
924         (void) This->screen->fence_finish(This->screen, NULL, fence, PIPE_TIMEOUT_INFINITE);
925         This->screen->fence_reference(This->screen, &fence, NULL);
926     }
927 
928     This->rendering_done = FALSE;
929 
930     if (!This->enable_threadpool) {
931         This->tasks[0]=NULL;
932 
933         hr = ID3DPresent_PresentBuffer(This->present, This->present_handles[0], hDestWindowOverride, pSourceRect, pDestRect ? &dest_rect : NULL, NULL, dwFlags);
934 
935         if (FAILED(hr)) { UNTESTED(3);return hr; }
936     }
937 
938     This->base.device->end_scene_since_present = 0;
939     This->base.device->frame_count++;
940     return D3D_OK;
941 }
942 
943 HRESULT NINE_WINAPI
NineSwapChain9_Present(struct NineSwapChain9 * This,const RECT * pSourceRect,const RECT * pDestRect,HWND hDestWindowOverride,const RGNDATA * pDirtyRegion,DWORD dwFlags)944 NineSwapChain9_Present( struct NineSwapChain9 *This,
945                         const RECT *pSourceRect,
946                         const RECT *pDestRect,
947                         HWND hDestWindowOverride,
948                         const RGNDATA *pDirtyRegion,
949                         DWORD dwFlags )
950 {
951     struct pipe_resource *res = NULL;
952     D3DWindowBuffer *handle_temp;
953     struct threadpool_task *task_temp;
954     BOOL *pending_presentation_temp;
955     int i;
956     HRESULT hr;
957 
958     DBG("This=%p pSourceRect=%p pDestRect=%p hDestWindowOverride=%p "
959         "pDirtyRegion=%p dwFlags=%d\n",
960         This, pSourceRect, pDestRect, hDestWindowOverride,
961         pDirtyRegion,dwFlags);
962 
963     if (This->base.device->ex) {
964         if (NineSwapChain9_GetOccluded(This)) {
965             DBG("Present is occluded. Returning S_PRESENT_OCCLUDED.\n");
966             return S_PRESENT_OCCLUDED;
967         }
968     } else {
969         if (NineSwapChain9_GetOccluded(This) ||
970             NineSwapChain9_ResolutionMismatch(This)) {
971             This->base.device->device_needs_reset = TRUE;
972         }
973         if (This->base.device->device_needs_reset) {
974             DBG("Device is lost. Returning D3DERR_DEVICELOST.\n");
975             return D3DERR_DEVICELOST;
976         }
977     }
978 
979     nine_csmt_process(This->base.device);
980 
981     hr = present(This, pSourceRect, pDestRect,
982                  hDestWindowOverride, pDirtyRegion, dwFlags);
983     if (hr == D3DERR_WASSTILLDRAWING)
984         return hr;
985 
986     if (This->base.device->minor_version_num > 2 &&
987         This->actx->discard_delayed_release &&
988         This->params.SwapEffect == D3DSWAPEFFECT_DISCARD &&
989         This->params.PresentationInterval == D3DPRESENT_INTERVAL_IMMEDIATE) {
990         int next_buffer = -1;
991 
992         while (next_buffer == -1) {
993             /* Find a free backbuffer */
994             for (i = 1; i < This->num_back_buffers; i++) {
995                 if (!p_atomic_read(This->pending_presentation[i]) &&
996                     ID3DPresent_IsBufferReleased(This->present, This->present_handles[i])) {
997                     DBG("Found buffer released: %d\n", i);
998                     next_buffer = i;
999                     break;
1000                 }
1001             }
1002             if (next_buffer == -1) {
1003                 DBG("Found no buffer released. Waiting for event\n");
1004                 ID3DPresent_WaitBufferReleaseEvent(This->present);
1005             }
1006         }
1007 
1008         /* Free the task (we already checked it is finished) */
1009         if (This->tasks[next_buffer])
1010             _mesa_threadpool_wait_for_task(This->pool, &(This->tasks[next_buffer]));
1011         assert(!*This->pending_presentation[next_buffer] && !This->tasks[next_buffer]);
1012         This->tasks[next_buffer] = This->tasks[0];
1013         This->tasks[0] = NULL;
1014         pending_presentation_temp = This->pending_presentation[next_buffer];
1015         This->pending_presentation[next_buffer] = This->pending_presentation[0];
1016         This->pending_presentation[0] = pending_presentation_temp;
1017 
1018         /* Switch with the released buffer */
1019         pipe_resource_reference(&res, This->buffers[0]->base.resource);
1020         NineSurface9_SetResourceResize(
1021             This->buffers[0], This->buffers[next_buffer]->base.resource);
1022         NineSurface9_SetResourceResize(
1023             This->buffers[next_buffer], res);
1024         pipe_resource_reference(&res, NULL);
1025 
1026         if (This->present_buffers[0]) {
1027             pipe_resource_reference(&res, This->present_buffers[0]);
1028             pipe_resource_reference(&This->present_buffers[0], This->present_buffers[next_buffer]);
1029             pipe_resource_reference(&This->present_buffers[next_buffer], res);
1030             pipe_resource_reference(&res, NULL);
1031         }
1032 
1033         handle_temp = This->present_handles[0];
1034         This->present_handles[0] = This->present_handles[next_buffer];
1035         This->present_handles[next_buffer] = handle_temp;
1036     } else {
1037         switch (This->params.SwapEffect) {
1038             case D3DSWAPEFFECT_OVERLAY: /* Not implemented, fallback to FLIP */
1039             case D3DSWAPEFFECT_FLIPEX: /* Allows optimizations over FLIP for windowed mode. */
1040             case D3DSWAPEFFECT_DISCARD: /* Allows optimizations over FLIP */
1041             case D3DSWAPEFFECT_FLIP:
1042                 /* rotate the queue */
1043                 pipe_resource_reference(&res, This->buffers[0]->base.resource);
1044                 for (i = 1; i < This->num_back_buffers; i++) {
1045                     NineSurface9_SetResourceResize(This->buffers[i - 1],
1046                                                    This->buffers[i]->base.resource);
1047                 }
1048                 NineSurface9_SetResourceResize(
1049                     This->buffers[This->num_back_buffers - 1], res);
1050                 pipe_resource_reference(&res, NULL);
1051 
1052                 if (This->present_buffers[0]) {
1053                     pipe_resource_reference(&res, This->present_buffers[0]);
1054                     for (i = 1; i < This->num_back_buffers; i++)
1055                         pipe_resource_reference(&(This->present_buffers[i-1]), This->present_buffers[i]);
1056                     pipe_resource_reference(&(This->present_buffers[This->num_back_buffers - 1]), res);
1057                     pipe_resource_reference(&res, NULL);
1058                 }
1059 
1060                 handle_temp = This->present_handles[0];
1061                 for (i = 1; i < This->num_back_buffers; i++) {
1062                     This->present_handles[i-1] = This->present_handles[i];
1063                 }
1064                 This->present_handles[This->num_back_buffers - 1] = handle_temp;
1065                 task_temp = This->tasks[0];
1066                 for (i = 1; i < This->num_back_buffers; i++) {
1067                     This->tasks[i-1] = This->tasks[i];
1068                 }
1069                 This->tasks[This->num_back_buffers - 1] = task_temp;
1070                 pending_presentation_temp = This->pending_presentation[0];
1071                 for (i = 1; i < This->num_back_buffers; i++) {
1072                     This->pending_presentation[i-1] = This->pending_presentation[i];
1073                 }
1074                 This->pending_presentation[This->num_back_buffers - 1] = pending_presentation_temp;
1075                 break;
1076 
1077             case D3DSWAPEFFECT_COPY:
1078                 /* do nothing */
1079                 break;
1080         }
1081 
1082         if (This->tasks[0])
1083             _mesa_threadpool_wait_for_task(This->pool, &(This->tasks[0]));
1084         assert(!*This->pending_presentation[0]);
1085 
1086         ID3DPresent_WaitBufferReleased(This->present, This->present_handles[0]);
1087     }
1088 
1089     This->base.device->context.changed.group |= NINE_STATE_FB;
1090 
1091     return hr;
1092 }
1093 
1094 HRESULT NINE_WINAPI
NineSwapChain9_GetFrontBufferData(struct NineSwapChain9 * This,IDirect3DSurface9 * pDestSurface)1095 NineSwapChain9_GetFrontBufferData( struct NineSwapChain9 *This,
1096                                    IDirect3DSurface9 *pDestSurface )
1097 {
1098     struct NineSurface9 *dest_surface = NineSurface9(pDestSurface);
1099     struct NineDevice9 *pDevice = This->base.device;
1100     unsigned int width, height;
1101     struct pipe_resource *temp_resource;
1102     struct NineSurface9 *temp_surface;
1103     D3DWindowBuffer *temp_handle;
1104     D3DSURFACE_DESC desc;
1105     HRESULT hr;
1106 
1107     DBG("GetFrontBufferData: This=%p pDestSurface=%p\n",
1108         This, pDestSurface);
1109 
1110     user_assert(dest_surface->base.pool == D3DPOOL_SYSTEMMEM, D3DERR_INVALIDCALL);
1111 
1112     width = dest_surface->desc.Width;
1113     height = dest_surface->desc.Height;
1114 
1115     /* Note: front window size and destination size are supposed
1116      * to match. However it's not very clear what should get taken in Windowed
1117      * mode. It may need a fix */
1118     create_present_buffer(This, width, height, &temp_resource, &temp_handle);
1119 
1120     if (!temp_resource || !temp_handle) {
1121         return D3DERR_INVALIDCALL;
1122     }
1123 
1124     desc.Type = D3DRTYPE_SURFACE;
1125     desc.Pool = D3DPOOL_DEFAULT;
1126     desc.MultiSampleType = D3DMULTISAMPLE_NONE;
1127     desc.MultiSampleQuality = 0;
1128     desc.Width = width;
1129     desc.Height = height;
1130     /* NineSurface9_CopyDefaultToMem needs same format. */
1131     desc.Format = dest_surface->desc.Format;
1132     desc.Usage = D3DUSAGE_RENDERTARGET;
1133     hr = NineSurface9_new(pDevice, NineUnknown(This), temp_resource, NULL, 0,
1134                           0, 0, &desc, &temp_surface);
1135     pipe_resource_reference(&temp_resource, NULL);
1136     if (FAILED(hr)) {
1137         DBG("Failed to create temp FrontBuffer surface.\n");
1138         return hr;
1139     }
1140 
1141     ID3DPresent_FrontBufferCopy(This->present, temp_handle);
1142 
1143     NineSurface9_CopyDefaultToMem(dest_surface, temp_surface);
1144 
1145     ID3DPresent_DestroyD3DWindowBuffer(This->present, temp_handle);
1146     NineUnknown_Destroy(NineUnknown(temp_surface));
1147 
1148     return D3D_OK;
1149 }
1150 
1151 HRESULT NINE_WINAPI
NineSwapChain9_GetBackBuffer(struct NineSwapChain9 * This,UINT iBackBuffer,D3DBACKBUFFER_TYPE Type,IDirect3DSurface9 ** ppBackBuffer)1152 NineSwapChain9_GetBackBuffer( struct NineSwapChain9 *This,
1153                               UINT iBackBuffer,
1154                               D3DBACKBUFFER_TYPE Type,
1155                               IDirect3DSurface9 **ppBackBuffer )
1156 {
1157     DBG("GetBackBuffer: This=%p iBackBuffer=%d Type=%d ppBackBuffer=%p\n",
1158         This, iBackBuffer, Type, ppBackBuffer);
1159     (void)user_error(Type == D3DBACKBUFFER_TYPE_MONO);
1160     /* don't touch ppBackBuffer on error */
1161     user_assert(ppBackBuffer != NULL, D3DERR_INVALIDCALL);
1162     user_assert(iBackBuffer < This->params.BackBufferCount, D3DERR_INVALIDCALL);
1163 
1164     NineUnknown_AddRef(NineUnknown(This->buffers[iBackBuffer]));
1165     *ppBackBuffer = (IDirect3DSurface9 *)This->buffers[iBackBuffer];
1166     return D3D_OK;
1167 }
1168 
1169 HRESULT NINE_WINAPI
NineSwapChain9_GetRasterStatus(struct NineSwapChain9 * This,D3DRASTER_STATUS * pRasterStatus)1170 NineSwapChain9_GetRasterStatus( struct NineSwapChain9 *This,
1171                                 D3DRASTER_STATUS *pRasterStatus )
1172 {
1173     DBG("GetRasterStatus: This=%p pRasterStatus=%p\n",
1174         This, pRasterStatus);
1175     user_assert(pRasterStatus != NULL, E_POINTER);
1176     return ID3DPresent_GetRasterStatus(This->present, pRasterStatus);
1177 }
1178 
1179 HRESULT NINE_WINAPI
NineSwapChain9_GetDisplayMode(struct NineSwapChain9 * This,D3DDISPLAYMODE * pMode)1180 NineSwapChain9_GetDisplayMode( struct NineSwapChain9 *This,
1181                                D3DDISPLAYMODE *pMode )
1182 {
1183     D3DDISPLAYMODEEX mode;
1184     D3DDISPLAYROTATION rot;
1185     HRESULT hr;
1186 
1187     DBG("GetDisplayMode: This=%p pMode=%p\n",
1188         This, pMode);
1189     user_assert(pMode != NULL, E_POINTER);
1190 
1191     hr = ID3DPresent_GetDisplayMode(This->present, &mode, &rot);
1192     if (SUCCEEDED(hr)) {
1193         pMode->Width = mode.Width;
1194         pMode->Height = mode.Height;
1195         pMode->RefreshRate = mode.RefreshRate;
1196         pMode->Format = mode.Format;
1197     }
1198     return hr;
1199 }
1200 
1201 HRESULT NINE_WINAPI
NineSwapChain9_GetPresentParameters(struct NineSwapChain9 * This,D3DPRESENT_PARAMETERS * pPresentationParameters)1202 NineSwapChain9_GetPresentParameters( struct NineSwapChain9 *This,
1203                                      D3DPRESENT_PARAMETERS *pPresentationParameters )
1204 {
1205     DBG("GetPresentParameters: This=%p pPresentationParameters=%p\n",
1206         This, pPresentationParameters);
1207     user_assert(pPresentationParameters != NULL, E_POINTER);
1208     *pPresentationParameters = This->params;
1209     return D3D_OK;
1210 }
1211 
1212 IDirect3DSwapChain9Vtbl NineSwapChain9_vtable = {
1213     (void *)NineUnknown_QueryInterface,
1214     (void *)NineUnknown_AddRef,
1215     (void *)NineUnknown_Release,
1216     (void *)NineSwapChain9_Present,
1217     (void *)NineSwapChain9_GetFrontBufferData,
1218     (void *)NineSwapChain9_GetBackBuffer,
1219     (void *)NineSwapChain9_GetRasterStatus,
1220     (void *)NineSwapChain9_GetDisplayMode,
1221     (void *)NineUnknown_GetDevice, /* actually part of SwapChain9 iface */
1222     (void *)NineSwapChain9_GetPresentParameters
1223 };
1224 
1225 static const GUID *NineSwapChain9_IIDs[] = {
1226     &IID_IDirect3DSwapChain9,
1227     &IID_IUnknown,
1228     NULL
1229 };
1230 
1231 HRESULT
NineSwapChain9_new(struct NineDevice9 * pDevice,BOOL implicit,ID3DPresent * pPresent,D3DPRESENT_PARAMETERS * pPresentationParameters,struct d3dadapter9_context * pCTX,HWND hFocusWindow,struct NineSwapChain9 ** ppOut)1232 NineSwapChain9_new( struct NineDevice9 *pDevice,
1233                     BOOL implicit,
1234                     ID3DPresent *pPresent,
1235                     D3DPRESENT_PARAMETERS *pPresentationParameters,
1236                     struct d3dadapter9_context *pCTX,
1237                     HWND hFocusWindow,
1238                     struct NineSwapChain9 **ppOut )
1239 {
1240     NINE_DEVICE_CHILD_NEW(SwapChain9, ppOut, pDevice, /* args */
1241                           implicit, pPresent, pPresentationParameters,
1242                           pCTX, hFocusWindow, NULL);
1243 }
1244 
1245 BOOL
NineSwapChain9_GetOccluded(struct NineSwapChain9 * This)1246 NineSwapChain9_GetOccluded( struct NineSwapChain9 *This )
1247 {
1248     if (This->base.device->minor_version_num > 0) {
1249         return ID3DPresent_GetWindowOccluded(This->present);
1250     }
1251 
1252     return FALSE;
1253 }
1254 
1255 BOOL
NineSwapChain9_ResolutionMismatch(struct NineSwapChain9 * This)1256 NineSwapChain9_ResolutionMismatch( struct NineSwapChain9 *This )
1257 {
1258     if (This->base.device->minor_version_num > 1) {
1259         return ID3DPresent_ResolutionMismatch(This->present);
1260     }
1261 
1262     return FALSE;
1263 }
1264 
1265 HANDLE
NineSwapChain9_CreateThread(struct NineSwapChain9 * This,void * pFuncAddress,void * pParam)1266 NineSwapChain9_CreateThread( struct NineSwapChain9 *This,
1267                                  void *pFuncAddress,
1268                                  void *pParam )
1269 {
1270     if (This->base.device->minor_version_num > 1) {
1271         return ID3DPresent_CreateThread(This->present, pFuncAddress, pParam);
1272     }
1273 
1274     return NULL;
1275 }
1276 
1277 void
NineSwapChain9_WaitForThread(struct NineSwapChain9 * This,HANDLE thread)1278 NineSwapChain9_WaitForThread( struct NineSwapChain9 *This,
1279                                   HANDLE thread )
1280 {
1281     if (This->base.device->minor_version_num > 1) {
1282         (void) ID3DPresent_WaitForThread(This->present, thread);
1283     }
1284 }
1285 
1286 static int
NineSwapChain9_GetBackBufferCountForParams(struct NineSwapChain9 * This,D3DPRESENT_PARAMETERS * pParams)1287 NineSwapChain9_GetBackBufferCountForParams( struct NineSwapChain9 *This,
1288                                             D3DPRESENT_PARAMETERS *pParams )
1289 {
1290     int count = pParams->BackBufferCount;
1291 
1292     /* When we have flip behaviour, d3d9 expects we get back the screen buffer when we flip.
1293      * Here we don't get back the initial content of the screen. To emulate the behaviour
1294      * we allocate an additional buffer */
1295     if (pParams->SwapEffect != D3DSWAPEFFECT_COPY)
1296         count++;
1297     /* With DISCARD, as there is no guarantee about the buffer contents, we can use
1298      * an arbitrary number of buffers */
1299     if (pParams->SwapEffect == D3DSWAPEFFECT_DISCARD) {
1300         /* thread_submit's can have maximum count or This->actx->throttling_value + 1
1301          * frames in flight being rendered and not shown.
1302          * Do not let count decrease that number */
1303         if (This->actx->thread_submit && count < This->desired_fences)
1304             count = This->desired_fences;
1305         /* When we enable AllowDISCARDDelayedRelease, we must ensure
1306          * to have at least 4 buffers to meet INTERVAL_IMMEDIATE,
1307          * since the display server/compositor can hold 3 buffers
1308          * without releasing them:
1309          * . Buffer on screen.
1310          * . Buffer scheduled kernel side to be next on screen.
1311          * . Last buffer sent. */
1312         if (This->base.device->minor_version_num > 2 &&
1313             This->actx->discard_delayed_release &&
1314             pParams->PresentationInterval == D3DPRESENT_INTERVAL_IMMEDIATE) {
1315             if (This->actx->thread_submit && count < 4)
1316                 count = 4;
1317             /* When thread_submit is not used, 5 buffers are actually needed,
1318              * because in case a pageflip is missed because rendering wasn't finished,
1319              * the Xserver will hold 4 buffers. */
1320             else if (!This->actx->thread_submit && count < 5)
1321                 count = 5;
1322             /* Somehow this cases needs 5 with thread_submit, or else you get a small performance hit */
1323             if (This->actx->tearfree_discard && count < 5)
1324                 count = 5;
1325         }
1326     }
1327 
1328     return count;
1329 }
1330