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