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