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