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