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