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