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