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