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