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