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