st/nine: Add asserts for output/input packing
[mesa.git] / src / gallium / state_trackers / nine / swapchain9.c
1 /*
2 * Copyright 2011 Joakim Sindholt <opensource@zhasha.com>
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * on the rights to use, copy, modify, merge, publish, distribute, sub
8 * license, and/or sell copies of the Software, and to permit persons to whom
9 * the Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
19 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
20 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
21 * USE OR OTHER DEALINGS IN THE SOFTWARE. */
22
23 #include "swapchain9.h"
24 #include "surface9.h"
25 #include "device9.h"
26
27 #include "nine_helpers.h"
28 #include "nine_pipe.h"
29 #include "nine_dump.h"
30
31 #include "util/u_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
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->pipe = NineDevice9_GetPipe(This->base.device);
63 This->cso = NineDevice9_GetCSO(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
71 if (!pPresentationParameters->hDeviceWindow)
72 pPresentationParameters->hDeviceWindow = hFocusWindow;
73
74 This->rendering_done = FALSE;
75 This->pool = NULL;
76 return NineSwapChain9_Resize(This, pPresentationParameters, mode);
77 }
78
79 static D3DWindowBuffer *
80 D3DWindowBuffer_create(struct NineSwapChain9 *This,
81 struct pipe_resource *resource,
82 int depth,
83 int for_frontbuffer_reading)
84 {
85 D3DWindowBuffer *ret;
86 struct winsys_handle whandle;
87 int stride, dmaBufFd;
88
89 memset(&whandle, 0, sizeof(whandle));
90 whandle.type = DRM_API_HANDLE_TYPE_FD;
91 This->screen->resource_get_handle(This->screen, resource, &whandle,
92 for_frontbuffer_reading ?
93 PIPE_HANDLE_USAGE_WRITE :
94 PIPE_HANDLE_USAGE_EXPLICIT_FLUSH |
95 PIPE_HANDLE_USAGE_READ);
96 stride = whandle.stride;
97 dmaBufFd = whandle.handle;
98 ID3DPresent_NewD3DWindowBufferFromDmaBuf(This->present,
99 dmaBufFd,
100 resource->width0,
101 resource->height0,
102 stride,
103 depth,
104 32,
105 &ret);
106 return ret;
107 }
108
109 HRESULT
110 NineSwapChain9_Resize( struct NineSwapChain9 *This,
111 D3DPRESENT_PARAMETERS *pParams,
112 D3DDISPLAYMODEEX *mode )
113 {
114 struct NineDevice9 *pDevice = This->base.device;
115 struct NineSurface9 **bufs;
116 D3DSURFACE_DESC desc;
117 HRESULT hr;
118 struct pipe_resource *resource, tmplt;
119 enum pipe_format pf;
120 BOOL has_present_buffers = FALSE;
121 int depth;
122 unsigned i, oldBufferCount, newBufferCount;
123
124 DBG("This=%p pParams=%p\n", This, pParams);
125 user_assert(pParams != NULL, E_POINTER);
126 user_assert(pParams->SwapEffect, D3DERR_INVALIDCALL);
127 user_assert((pParams->SwapEffect != D3DSWAPEFFECT_COPY) ||
128 (pParams->BackBufferCount <= 1), D3DERR_INVALIDCALL);
129 user_assert(pDevice->ex || pParams->BackBufferCount <= 3, D3DERR_INVALIDCALL);
130 user_assert(pDevice->ex ||
131 (pParams->SwapEffect == D3DSWAPEFFECT_FLIP) ||
132 (pParams->SwapEffect == D3DSWAPEFFECT_COPY) ||
133 (pParams->SwapEffect == D3DSWAPEFFECT_DISCARD), D3DERR_INVALIDCALL);
134
135 DBG("pParams(%p):\n"
136 "BackBufferWidth: %u\n"
137 "BackBufferHeight: %u\n"
138 "BackBufferFormat: %s\n"
139 "BackBufferCount: %u\n"
140 "MultiSampleType: %u\n"
141 "MultiSampleQuality: %u\n"
142 "SwapEffect: %u\n"
143 "hDeviceWindow: %p\n"
144 "Windowed: %i\n"
145 "EnableAutoDepthStencil: %i\n"
146 "AutoDepthStencilFormat: %s\n"
147 "Flags: %s\n"
148 "FullScreen_RefreshRateInHz: %u\n"
149 "PresentationInterval: %x\n", pParams,
150 pParams->BackBufferWidth, pParams->BackBufferHeight,
151 d3dformat_to_string(pParams->BackBufferFormat),
152 pParams->BackBufferCount,
153 pParams->MultiSampleType, pParams->MultiSampleQuality,
154 pParams->SwapEffect, pParams->hDeviceWindow, pParams->Windowed,
155 pParams->EnableAutoDepthStencil,
156 d3dformat_to_string(pParams->AutoDepthStencilFormat),
157 nine_D3DPRESENTFLAG_to_str(pParams->Flags),
158 pParams->FullScreen_RefreshRateInHz,
159 pParams->PresentationInterval);
160
161 if (pParams->BackBufferCount > 3) {
162 pParams->BackBufferCount = 3;
163 }
164
165 if (pParams->BackBufferCount == 0) {
166 pParams->BackBufferCount = 1;
167 }
168
169 if (pParams->BackBufferFormat == D3DFMT_UNKNOWN) {
170 pParams->BackBufferFormat = D3DFMT_A8R8G8B8;
171 }
172
173 This->desired_fences = This->actx->throttling ? This->actx->throttling_value + 1 : 0;
174 /* +1 because we add the fence of the current buffer before popping an old one */
175 if (This->desired_fences > DRI_SWAP_FENCES_MAX)
176 This->desired_fences = DRI_SWAP_FENCES_MAX;
177
178 if (This->actx->vblank_mode == 0)
179 pParams->PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
180 else if (This->actx->vblank_mode == 3)
181 pParams->PresentationInterval = D3DPRESENT_INTERVAL_ONE;
182
183 if (mode && This->mode) {
184 *(This->mode) = *mode;
185 } else if (mode) {
186 This->mode = malloc(sizeof(D3DDISPLAYMODEEX));
187 memcpy(This->mode, mode, sizeof(D3DDISPLAYMODEEX));
188 } else {
189 free(This->mode);
190 This->mode = NULL;
191 }
192
193 /* Note: It is the role of the backend to fill if necessary
194 * BackBufferWidth and BackBufferHeight */
195 hr = ID3DPresent_SetPresentParameters(This->present, pParams, This->mode);
196 if (hr != D3D_OK)
197 return hr;
198
199 /* When we have flip behaviour, d3d9 expects we get back the screen buffer when we flip.
200 * Here we don't get back the initial content of the screen. To emulate the behaviour
201 * we allocate an additional buffer */
202 oldBufferCount = This->params.BackBufferCount ?
203 (This->params.BackBufferCount +
204 (This->params.SwapEffect != D3DSWAPEFFECT_COPY)) : 0;
205 newBufferCount = pParams->BackBufferCount +
206 (pParams->SwapEffect != D3DSWAPEFFECT_COPY);
207
208 pf = d3d9_to_pipe_format_checked(This->screen, pParams->BackBufferFormat,
209 PIPE_TEXTURE_2D, pParams->MultiSampleType,
210 PIPE_BIND_RENDER_TARGET, FALSE, FALSE);
211
212 if (This->actx->linear_framebuffer ||
213 (pf != PIPE_FORMAT_B8G8R8X8_UNORM &&
214 pf != PIPE_FORMAT_B8G8R8A8_UNORM) ||
215 pParams->SwapEffect != D3DSWAPEFFECT_DISCARD ||
216 pParams->MultiSampleType >= 2 ||
217 (This->actx->ref && This->actx->ref == This->screen))
218 has_present_buffers = TRUE;
219
220 /* Note: the buffer depth has to match the window depth.
221 * In practice, ARGB buffers can be used with windows
222 * of depth 24. Windows of depth 32 are extremely rare.
223 * So even if the buffer is ARGB, say it is depth 24.
224 * It is common practice, for example that's how
225 * glamor implements depth 24.
226 * TODO: handle windows with other depths. Not possible in the short term.
227 * For example 16 bits.*/
228 depth = 24;
229
230 tmplt.target = PIPE_TEXTURE_2D;
231 tmplt.width0 = pParams->BackBufferWidth;
232 tmplt.height0 = pParams->BackBufferHeight;
233 tmplt.depth0 = 1;
234 tmplt.last_level = 0;
235 tmplt.array_size = 1;
236 tmplt.usage = PIPE_USAGE_DEFAULT;
237 tmplt.flags = 0;
238
239 desc.Type = D3DRTYPE_SURFACE;
240 desc.Pool = D3DPOOL_DEFAULT;
241 desc.MultiSampleType = pParams->MultiSampleType;
242 desc.MultiSampleQuality = 0;
243 desc.Width = pParams->BackBufferWidth;
244 desc.Height = pParams->BackBufferHeight;
245
246 if (This->pool) {
247 _mesa_threadpool_destroy(This, This->pool);
248 This->pool = NULL;
249 }
250 This->enable_threadpool = This->actx->thread_submit && (pParams->SwapEffect != D3DSWAPEFFECT_COPY);
251 if (This->enable_threadpool)
252 This->pool = _mesa_threadpool_create(This);
253 if (!This->pool)
254 This->enable_threadpool = FALSE;
255
256 This->tasks = REALLOC(This->tasks,
257 oldBufferCount * sizeof(struct threadpool_task *),
258 newBufferCount * sizeof(struct threadpool_task *));
259 memset(This->tasks, 0, newBufferCount * sizeof(struct threadpool_task *));
260
261 for (i = 0; i < oldBufferCount; i++) {
262 ID3DPresent_DestroyD3DWindowBuffer(This->present, This->present_handles[i]);
263 This->present_handles[i] = NULL;
264 if (This->present_buffers)
265 pipe_resource_reference(&(This->present_buffers[i]), NULL);
266 }
267
268 if (!has_present_buffers && This->present_buffers) {
269 FREE(This->present_buffers);
270 This->present_buffers = NULL;
271 }
272
273 if (newBufferCount != oldBufferCount) {
274 for (i = newBufferCount; i < oldBufferCount;
275 ++i)
276 NineUnknown_Detach(NineUnknown(This->buffers[i]));
277
278 bufs = REALLOC(This->buffers,
279 oldBufferCount * sizeof(This->buffers[0]),
280 newBufferCount * sizeof(This->buffers[0]));
281 if (!bufs)
282 return E_OUTOFMEMORY;
283 This->buffers = bufs;
284 This->present_handles = REALLOC(This->present_handles,
285 oldBufferCount * sizeof(D3DWindowBuffer *),
286 newBufferCount * sizeof(D3DWindowBuffer *));
287 for (i = oldBufferCount; i < newBufferCount; ++i) {
288 This->buffers[i] = NULL;
289 This->present_handles[i] = NULL;
290 }
291 }
292
293 if (has_present_buffers &&
294 (newBufferCount != oldBufferCount || !This->present_buffers)) {
295 This->present_buffers = REALLOC(This->present_buffers,
296 This->present_buffers == NULL ? 0 :
297 oldBufferCount * sizeof(struct pipe_resource *),
298 newBufferCount * sizeof(struct pipe_resource *));
299 memset(This->present_buffers, 0, newBufferCount * sizeof(struct pipe_resource *));
300 }
301
302 for (i = 0; i < newBufferCount; ++i) {
303 tmplt.bind = PIPE_BIND_SAMPLER_VIEW | PIPE_BIND_TRANSFER_READ |
304 PIPE_BIND_TRANSFER_WRITE | PIPE_BIND_RENDER_TARGET;
305 tmplt.nr_samples = pParams->MultiSampleType;
306 if (!has_present_buffers)
307 tmplt.bind |= PIPE_BIND_SHARED | PIPE_BIND_SCANOUT | PIPE_BIND_DISPLAY_TARGET;
308 tmplt.format = d3d9_to_pipe_format_checked(This->screen,
309 pParams->BackBufferFormat,
310 PIPE_TEXTURE_2D,
311 tmplt.nr_samples,
312 tmplt.bind, FALSE, FALSE);
313 if (tmplt.format == PIPE_FORMAT_NONE)
314 return D3DERR_INVALIDCALL;
315 resource = This->screen->resource_create(This->screen, &tmplt);
316 if (!resource) {
317 DBG("Failed to create pipe_resource.\n");
318 return D3DERR_OUTOFVIDEOMEMORY;
319 }
320 if (pParams->Flags & D3DPRESENTFLAG_LOCKABLE_BACKBUFFER)
321 resource->flags |= NINE_RESOURCE_FLAG_LOCKABLE;
322 if (This->buffers[i]) {
323 NineSurface9_SetResourceResize(This->buffers[i], resource);
324 if (has_present_buffers)
325 pipe_resource_reference(&resource, NULL);
326 } else {
327 desc.Format = pParams->BackBufferFormat;
328 desc.Usage = D3DUSAGE_RENDERTARGET;
329 hr = NineSurface9_new(pDevice, NineUnknown(This), resource, NULL, 0,
330 0, 0, &desc, &This->buffers[i]);
331 if (has_present_buffers)
332 pipe_resource_reference(&resource, NULL);
333 if (FAILED(hr)) {
334 DBG("Failed to create RT surface.\n");
335 return hr;
336 }
337 This->buffers[i]->base.base.forward = FALSE;
338 }
339 if (has_present_buffers) {
340 tmplt.format = PIPE_FORMAT_B8G8R8X8_UNORM;
341 tmplt.bind = PIPE_BIND_SAMPLER_VIEW | PIPE_BIND_SHARED | PIPE_BIND_SCANOUT | PIPE_BIND_DISPLAY_TARGET;
342 tmplt.nr_samples = 0;
343 if (This->actx->linear_framebuffer)
344 tmplt.bind |= PIPE_BIND_LINEAR;
345 if (pParams->SwapEffect != D3DSWAPEFFECT_DISCARD)
346 tmplt.bind |= PIPE_BIND_RENDER_TARGET;
347 resource = This->screen->resource_create(This->screen, &tmplt);
348 pipe_resource_reference(&(This->present_buffers[i]), resource);
349 }
350 This->present_handles[i] = D3DWindowBuffer_create(This, resource, depth, false);
351 pipe_resource_reference(&resource, NULL);
352 }
353 if (pParams->EnableAutoDepthStencil) {
354 tmplt.bind = d3d9_get_pipe_depth_format_bindings(pParams->AutoDepthStencilFormat);
355 /* Checking the d3d9 depth format for texture support indicates the app if it can use
356 * the format for shadow mapping or texturing. If the check returns true, then the app
357 * is allowed to use this functionnality, so try first to create the buffer
358 * with PIPE_BIND_SAMPLER_VIEW. If the format can't be created with it, try without.
359 * If it fails with PIPE_BIND_SAMPLER_VIEW, then the app check for texture support
360 * would fail too, so we are fine. */
361 tmplt.bind |= PIPE_BIND_SAMPLER_VIEW;
362 tmplt.nr_samples = pParams->MultiSampleType;
363 tmplt.format = d3d9_to_pipe_format_checked(This->screen,
364 pParams->AutoDepthStencilFormat,
365 PIPE_TEXTURE_2D,
366 tmplt.nr_samples,
367 tmplt.bind,
368 FALSE, FALSE);
369 if (tmplt.format == PIPE_FORMAT_NONE) {
370 tmplt.bind &= ~PIPE_BIND_SAMPLER_VIEW;
371 tmplt.format = d3d9_to_pipe_format_checked(This->screen,
372 pParams->AutoDepthStencilFormat,
373 PIPE_TEXTURE_2D,
374 tmplt.nr_samples,
375 tmplt.bind,
376 FALSE, FALSE);
377 }
378
379 if (tmplt.format == PIPE_FORMAT_NONE)
380 return D3DERR_INVALIDCALL;
381
382 resource = This->screen->resource_create(This->screen, &tmplt);
383 if (!resource) {
384 DBG("Failed to create pipe_resource for depth buffer.\n");
385 return D3DERR_OUTOFVIDEOMEMORY;
386 }
387 if (This->zsbuf) {
388 NineSurface9_SetResourceResize(This->zsbuf, resource);
389 pipe_resource_reference(&resource, NULL);
390 } else {
391 /* XXX wine thinks the container of this should be the device */
392 desc.Format = pParams->AutoDepthStencilFormat;
393 desc.Usage = D3DUSAGE_DEPTHSTENCIL;
394 hr = NineSurface9_new(pDevice, NineUnknown(pDevice), resource, NULL, 0,
395 0, 0, &desc, &This->zsbuf);
396 pipe_resource_reference(&resource, NULL);
397 if (FAILED(hr)) {
398 DBG("Failed to create ZS surface.\n");
399 return hr;
400 }
401 This->zsbuf->base.base.forward = FALSE;
402 }
403 }
404
405 This->params = *pParams;
406
407 return D3D_OK;
408 }
409
410 /* Throttling: code adapted from the dri state tracker */
411
412 /**
413 * swap_fences_pop_front - pull a fence from the throttle queue
414 *
415 * If the throttle queue is filled to the desired number of fences,
416 * pull fences off the queue until the number is less than the desired
417 * number of fences, and return the last fence pulled.
418 */
419 static struct pipe_fence_handle *
420 swap_fences_pop_front(struct NineSwapChain9 *This)
421 {
422 struct pipe_screen *screen = This->screen;
423 struct pipe_fence_handle *fence = NULL;
424
425 if (This->desired_fences == 0)
426 return NULL;
427
428 if (This->cur_fences >= This->desired_fences) {
429 screen->fence_reference(screen, &fence, This->swap_fences[This->tail]);
430 screen->fence_reference(screen, &This->swap_fences[This->tail++], NULL);
431 This->tail &= DRI_SWAP_FENCES_MASK;
432 --This->cur_fences;
433 }
434 return fence;
435 }
436
437
438 /**
439 * swap_fences_see_front - same than swap_fences_pop_front without
440 * pulling
441 *
442 */
443
444 static struct pipe_fence_handle *
445 swap_fences_see_front(struct NineSwapChain9 *This)
446 {
447 struct pipe_screen *screen = This->screen;
448 struct pipe_fence_handle *fence = NULL;
449
450 if (This->desired_fences == 0)
451 return NULL;
452
453 if (This->cur_fences >= This->desired_fences) {
454 screen->fence_reference(screen, &fence, This->swap_fences[This->tail]);
455 }
456 return fence;
457 }
458
459
460 /**
461 * swap_fences_push_back - push a fence onto the throttle queue at the back
462 *
463 * push a fence onto the throttle queue and pull fences of the queue
464 * so that the desired number of fences are on the queue.
465 */
466 static void
467 swap_fences_push_back(struct NineSwapChain9 *This,
468 struct pipe_fence_handle *fence)
469 {
470 struct pipe_screen *screen = This->screen;
471
472 if (!fence || This->desired_fences == 0)
473 return;
474
475 while(This->cur_fences == This->desired_fences)
476 swap_fences_pop_front(This);
477
478 This->cur_fences++;
479 screen->fence_reference(screen, &This->swap_fences[This->head++],
480 fence);
481 This->head &= DRI_SWAP_FENCES_MASK;
482 }
483
484
485 /**
486 * swap_fences_unref - empty the throttle queue
487 *
488 * pulls fences of the throttle queue until it is empty.
489 */
490 static void
491 swap_fences_unref(struct NineSwapChain9 *This)
492 {
493 struct pipe_screen *screen = This->screen;
494
495 while(This->cur_fences) {
496 screen->fence_reference(screen, &This->swap_fences[This->tail++], NULL);
497 This->tail &= DRI_SWAP_FENCES_MASK;
498 --This->cur_fences;
499 }
500 }
501
502 void
503 NineSwapChain9_dtor( struct NineSwapChain9 *This )
504 {
505 unsigned i;
506
507 DBG("This=%p\n", This);
508
509 if (This->pool)
510 _mesa_threadpool_destroy(This, This->pool);
511
512 if (This->buffers) {
513 for (i = 0; i < This->params.BackBufferCount; i++) {
514 NineUnknown_Release(NineUnknown(This->buffers[i]));
515 ID3DPresent_DestroyD3DWindowBuffer(This->present, This->present_handles[i]);
516 if (This->present_buffers)
517 pipe_resource_reference(&(This->present_buffers[i]), NULL);
518 }
519 FREE(This->buffers);
520 FREE(This->present_buffers);
521 }
522 if (This->zsbuf)
523 NineUnknown_Destroy(NineUnknown(This->zsbuf));
524
525 if (This->present)
526 ID3DPresent_Release(This->present);
527
528 swap_fences_unref(This);
529 NineUnknown_dtor(&This->base);
530 }
531
532 static void
533 create_present_buffer( struct NineSwapChain9 *This,
534 unsigned int width, unsigned int height,
535 struct pipe_resource **resource,
536 D3DWindowBuffer **present_handle)
537 {
538 struct pipe_resource tmplt;
539
540 tmplt.target = PIPE_TEXTURE_2D;
541 tmplt.width0 = width;
542 tmplt.height0 = height;
543 tmplt.depth0 = 1;
544 tmplt.last_level = 0;
545 tmplt.array_size = 1;
546 tmplt.usage = PIPE_USAGE_DEFAULT;
547 tmplt.flags = 0;
548 tmplt.format = PIPE_FORMAT_B8G8R8X8_UNORM;
549 tmplt.bind = PIPE_BIND_SAMPLER_VIEW | PIPE_BIND_TRANSFER_READ |
550 PIPE_BIND_TRANSFER_WRITE | PIPE_BIND_RENDER_TARGET |
551 PIPE_BIND_SHARED | PIPE_BIND_SCANOUT | PIPE_BIND_DISPLAY_TARGET;
552 tmplt.nr_samples = 0;
553 if (This->actx->linear_framebuffer)
554 tmplt.bind |= PIPE_BIND_LINEAR;
555 *resource = This->screen->resource_create(This->screen, &tmplt);
556
557 *present_handle = D3DWindowBuffer_create(This, *resource, 24, true);
558 }
559
560 static void
561 handle_draw_cursor_and_hud( struct NineSwapChain9 *This, struct pipe_resource *resource)
562 {
563 struct NineDevice9 *device = This->base.device;
564 struct pipe_blit_info blit;
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 This->pipe->blit(This->pipe, &blit);
603 }
604
605 if (device->hud && resource) {
606 hud_draw(device->hud, resource); /* XXX: no offset */
607 /* HUD doesn't clobber stipple */
608 nine_state_restore_non_cso(device);
609 }
610 }
611
612 struct end_present_struct {
613 struct pipe_screen *screen;
614 struct pipe_fence_handle *fence_to_wait;
615 ID3DPresent *present;
616 D3DWindowBuffer *present_handle;
617 HWND hDestWindowOverride;
618 };
619
620 static void work_present(void *data)
621 {
622 struct end_present_struct *work = data;
623 if (work->fence_to_wait) {
624 (void) work->screen->fence_finish(work->screen, work->fence_to_wait, PIPE_TIMEOUT_INFINITE);
625 work->screen->fence_reference(work->screen, &(work->fence_to_wait), NULL);
626 }
627 ID3DPresent_PresentBuffer(work->present, work->present_handle, work->hDestWindowOverride, NULL, NULL, NULL, 0);
628 free(work);
629 }
630
631 static void pend_present(struct NineSwapChain9 *This,
632 HWND hDestWindowOverride)
633 {
634 struct end_present_struct *work = calloc(1, sizeof(struct end_present_struct));
635
636 work->screen = This->screen;
637 work->fence_to_wait = swap_fences_pop_front(This);
638 work->present = This->present;
639 work->present_handle = This->present_handles[0];
640 work->hDestWindowOverride = hDestWindowOverride;
641 This->tasks[0] = _mesa_threadpool_queue_task(This->pool, work_present, work);
642
643 return;
644 }
645
646 static inline HRESULT
647 present( struct NineSwapChain9 *This,
648 const RECT *pSourceRect,
649 const RECT *pDestRect,
650 HWND hDestWindowOverride,
651 const RGNDATA *pDirtyRegion,
652 DWORD dwFlags )
653 {
654 struct pipe_resource *resource;
655 struct pipe_fence_handle *fence;
656 HRESULT hr;
657 struct pipe_blit_info blit;
658
659 DBG("present: This=%p pSourceRect=%p pDestRect=%p "
660 "pDirtyRegion=%p hDestWindowOverride=%p"
661 "dwFlags=%d resource=%p\n",
662 This, pSourceRect, pDestRect, pDirtyRegion,
663 hDestWindowOverride, (int)dwFlags, This->buffers[0]->base.resource);
664
665 if (pSourceRect)
666 DBG("pSourceRect = (%u..%u)x(%u..%u)\n",
667 pSourceRect->left, pSourceRect->right,
668 pSourceRect->top, pSourceRect->bottom);
669 if (pDestRect)
670 DBG("pDestRect = (%u..%u)x(%u..%u)\n",
671 pDestRect->left, pDestRect->right,
672 pDestRect->top, pDestRect->bottom);
673
674 /* TODO: in the case the source and destination rect have different size:
675 * We need to allocate a new buffer, and do a blit to it to resize.
676 * We can't use the present_buffer for that since when we created it,
677 * we couldn't guess which size would have been needed.
678 * If pDestRect or pSourceRect is null, we have to check the sizes
679 * from the source size, and the destination window size.
680 * In this case, either resize rngdata, or pass NULL instead
681 */
682 /* Note: This->buffers[0]->level should always be 0 */
683
684 if (This->rendering_done)
685 goto bypass_rendering;
686
687 resource = This->buffers[0]->base.resource;
688
689 if (This->params.SwapEffect == D3DSWAPEFFECT_DISCARD)
690 handle_draw_cursor_and_hud(This, resource);
691
692 if (This->present_buffers) {
693 memset(&blit, 0, sizeof(blit));
694 blit.src.resource = resource;
695 blit.src.level = 0;
696 blit.src.format = resource->format;
697 blit.src.box.z = 0;
698 blit.src.box.depth = 1;
699 blit.src.box.x = 0;
700 blit.src.box.y = 0;
701 blit.src.box.width = resource->width0;
702 blit.src.box.height = resource->height0;
703
704 resource = This->present_buffers[0];
705
706 blit.dst.resource = resource;
707 blit.dst.level = 0;
708 blit.dst.format = resource->format;
709 blit.dst.box.z = 0;
710 blit.dst.box.depth = 1;
711 blit.dst.box.x = 0;
712 blit.dst.box.y = 0;
713 blit.dst.box.width = resource->width0;
714 blit.dst.box.height = resource->height0;
715
716 blit.mask = PIPE_MASK_RGBA;
717 blit.filter = PIPE_TEX_FILTER_NEAREST;
718 blit.scissor_enable = FALSE;
719 blit.alpha_blend = FALSE;
720
721 This->pipe->blit(This->pipe, &blit);
722 }
723
724 /* The resource we present has to resolve fast clears
725 * if needed (and other things) */
726 This->pipe->flush_resource(This->pipe, resource);
727
728 if (This->params.SwapEffect != D3DSWAPEFFECT_DISCARD)
729 handle_draw_cursor_and_hud(This, resource);
730
731 fence = NULL;
732 This->pipe->flush(This->pipe, &fence, PIPE_FLUSH_END_OF_FRAME);
733 if (fence) {
734 swap_fences_push_back(This, fence);
735 This->screen->fence_reference(This->screen, &fence, NULL);
736 }
737
738 This->rendering_done = TRUE;
739 bypass_rendering:
740
741 if (dwFlags & D3DPRESENT_DONOTWAIT) {
742 UNTESTED(2);
743 BOOL still_draw = FALSE;
744 fence = swap_fences_see_front(This);
745 if (fence) {
746 still_draw = !This->screen->fence_finish(This->screen, fence, 0);
747 This->screen->fence_reference(This->screen, &fence, NULL);
748 }
749 if (still_draw)
750 return D3DERR_WASSTILLDRAWING;
751 }
752
753 if (!This->enable_threadpool) {
754 This->tasks[0]=NULL;
755 fence = swap_fences_pop_front(This);
756 if (fence) {
757 (void) This->screen->fence_finish(This->screen, fence, PIPE_TIMEOUT_INFINITE);
758 This->screen->fence_reference(This->screen, &fence, NULL);
759 }
760
761 hr = ID3DPresent_PresentBuffer(This->present, This->present_handles[0], hDestWindowOverride, pSourceRect, pDestRect, pDirtyRegion, dwFlags);
762
763 if (FAILED(hr)) { UNTESTED(3);return hr; }
764 } else {
765 pend_present(This, hDestWindowOverride);
766 }
767 This->rendering_done = FALSE;
768
769 return D3D_OK;
770 }
771
772 HRESULT NINE_WINAPI
773 NineSwapChain9_Present( struct NineSwapChain9 *This,
774 const RECT *pSourceRect,
775 const RECT *pDestRect,
776 HWND hDestWindowOverride,
777 const RGNDATA *pDirtyRegion,
778 DWORD dwFlags )
779 {
780 struct pipe_resource *res = NULL;
781 D3DWindowBuffer *handle_temp;
782 struct threadpool_task *task_temp;
783 int i;
784 HRESULT hr;
785
786 DBG("This=%p pSourceRect=%p pDestRect=%p hDestWindowOverride=%p "
787 "pDirtyRegion=%p dwFlags=%d\n",
788 This, pSourceRect, pDestRect, hDestWindowOverride,
789 pDirtyRegion,dwFlags);
790
791 if (This->base.device->ex) {
792 if (NineSwapChain9_GetOccluded(This)) {
793 return S_PRESENT_OCCLUDED;
794 }
795 } else {
796 if (NineSwapChain9_GetOccluded(This) ||
797 NineSwapChain9_ResolutionMismatch(This)) {
798 This->base.device->device_needs_reset = TRUE;
799 }
800 if (This->base.device->device_needs_reset) {
801 return D3DERR_DEVICELOST;
802 }
803 }
804
805 hr = present(This, pSourceRect, pDestRect,
806 hDestWindowOverride, pDirtyRegion, dwFlags);
807 if (hr == D3DERR_WASSTILLDRAWING)
808 return hr;
809
810 switch (This->params.SwapEffect) {
811 case D3DSWAPEFFECT_FLIP:
812 UNTESTED(4);
813 case D3DSWAPEFFECT_DISCARD:
814 /* rotate the queue */
815 pipe_resource_reference(&res, This->buffers[0]->base.resource);
816 for (i = 1; i <= This->params.BackBufferCount; i++) {
817 NineSurface9_SetResourceResize(This->buffers[i - 1],
818 This->buffers[i]->base.resource);
819 }
820 NineSurface9_SetResourceResize(
821 This->buffers[This->params.BackBufferCount], res);
822 pipe_resource_reference(&res, NULL);
823
824 if (This->present_buffers) {
825 pipe_resource_reference(&res, This->present_buffers[0]);
826 for (i = 1; i <= This->params.BackBufferCount; i++)
827 pipe_resource_reference(&(This->present_buffers[i-1]), This->present_buffers[i]);
828 pipe_resource_reference(&(This->present_buffers[This->params.BackBufferCount]), res);
829 pipe_resource_reference(&res, NULL);
830 }
831
832 handle_temp = This->present_handles[0];
833 for (i = 1; i <= This->params.BackBufferCount; i++) {
834 This->present_handles[i-1] = This->present_handles[i];
835 }
836 This->present_handles[This->params.BackBufferCount] = handle_temp;
837 task_temp = This->tasks[0];
838 for (i = 1; i <= This->params.BackBufferCount; i++) {
839 This->tasks[i-1] = This->tasks[i];
840 }
841 This->tasks[This->params.BackBufferCount] = task_temp;
842 break;
843
844 case D3DSWAPEFFECT_COPY:
845 UNTESTED(5);
846 /* do nothing */
847 break;
848
849 case D3DSWAPEFFECT_OVERLAY:
850 /* XXX not implemented */
851 break;
852
853 case D3DSWAPEFFECT_FLIPEX:
854 /* XXX not implemented */
855 break;
856 }
857
858 if (This->tasks[0])
859 _mesa_threadpool_wait_for_task(This->pool, &(This->tasks[0]));
860
861 ID3DPresent_WaitBufferReleased(This->present, This->present_handles[0]);
862
863 This->base.device->state.changed.group |= NINE_STATE_FB;
864
865 return hr;
866 }
867
868 HRESULT NINE_WINAPI
869 NineSwapChain9_GetFrontBufferData( struct NineSwapChain9 *This,
870 IDirect3DSurface9 *pDestSurface )
871 {
872 struct NineSurface9 *dest_surface = NineSurface9(pDestSurface);
873 struct NineDevice9 *pDevice = This->base.device;
874 unsigned int width, height;
875 struct pipe_resource *temp_resource;
876 struct NineSurface9 *temp_surface;
877 D3DWindowBuffer *temp_handle;
878 D3DSURFACE_DESC desc;
879 HRESULT hr;
880
881 DBG("GetFrontBufferData: This=%p pDestSurface=%p\n",
882 This, pDestSurface);
883
884 user_assert(dest_surface->base.pool == D3DPOOL_SYSTEMMEM, D3DERR_INVALIDCALL);
885
886 width = dest_surface->desc.Width;
887 height = dest_surface->desc.Height;
888
889 /* Note: front window size and destination size are supposed
890 * to match. However it's not very clear what should get taken in Windowed
891 * mode. It may need a fix */
892 create_present_buffer(This, width, height, &temp_resource, &temp_handle);
893
894 desc.Type = D3DRTYPE_SURFACE;
895 desc.Pool = D3DPOOL_DEFAULT;
896 desc.MultiSampleType = D3DMULTISAMPLE_NONE;
897 desc.MultiSampleQuality = 0;
898 desc.Width = width;
899 desc.Height = height;
900 /* NineSurface9_CopyDefaultToMem needs same format. */
901 desc.Format = dest_surface->desc.Format;
902 desc.Usage = D3DUSAGE_RENDERTARGET;
903 hr = NineSurface9_new(pDevice, NineUnknown(This), temp_resource, NULL, 0,
904 0, 0, &desc, &temp_surface);
905 pipe_resource_reference(&temp_resource, NULL);
906 if (FAILED(hr)) {
907 DBG("Failed to create temp FrontBuffer surface.\n");
908 return hr;
909 }
910
911 ID3DPresent_FrontBufferCopy(This->present, temp_handle);
912
913 NineSurface9_CopyDefaultToMem(dest_surface, temp_surface);
914
915 ID3DPresent_DestroyD3DWindowBuffer(This->present, temp_handle);
916 NineUnknown_Destroy(NineUnknown(temp_surface));
917
918 return D3D_OK;
919 }
920
921 HRESULT NINE_WINAPI
922 NineSwapChain9_GetBackBuffer( struct NineSwapChain9 *This,
923 UINT iBackBuffer,
924 D3DBACKBUFFER_TYPE Type,
925 IDirect3DSurface9 **ppBackBuffer )
926 {
927 DBG("GetBackBuffer: This=%p iBackBuffer=%d Type=%d ppBackBuffer=%p\n",
928 This, iBackBuffer, Type, ppBackBuffer);
929 (void)user_error(Type == D3DBACKBUFFER_TYPE_MONO);
930 /* don't touch ppBackBuffer on error */
931 user_assert(ppBackBuffer != NULL, D3DERR_INVALIDCALL);
932 user_assert(iBackBuffer < This->params.BackBufferCount, D3DERR_INVALIDCALL);
933
934 NineUnknown_AddRef(NineUnknown(This->buffers[iBackBuffer]));
935 *ppBackBuffer = (IDirect3DSurface9 *)This->buffers[iBackBuffer];
936 return D3D_OK;
937 }
938
939 HRESULT NINE_WINAPI
940 NineSwapChain9_GetRasterStatus( struct NineSwapChain9 *This,
941 D3DRASTER_STATUS *pRasterStatus )
942 {
943 DBG("GetRasterStatus: This=%p pRasterStatus=%p\n",
944 This, pRasterStatus);
945 user_assert(pRasterStatus != NULL, E_POINTER);
946 return ID3DPresent_GetRasterStatus(This->present, pRasterStatus);
947 }
948
949 HRESULT NINE_WINAPI
950 NineSwapChain9_GetDisplayMode( struct NineSwapChain9 *This,
951 D3DDISPLAYMODE *pMode )
952 {
953 D3DDISPLAYMODEEX mode;
954 D3DDISPLAYROTATION rot;
955 HRESULT hr;
956
957 DBG("GetDisplayMode: This=%p pMode=%p\n",
958 This, pMode);
959 user_assert(pMode != NULL, E_POINTER);
960
961 hr = ID3DPresent_GetDisplayMode(This->present, &mode, &rot);
962 if (SUCCEEDED(hr)) {
963 pMode->Width = mode.Width;
964 pMode->Height = mode.Height;
965 pMode->RefreshRate = mode.RefreshRate;
966 pMode->Format = mode.Format;
967 }
968 return hr;
969 }
970
971 HRESULT NINE_WINAPI
972 NineSwapChain9_GetPresentParameters( struct NineSwapChain9 *This,
973 D3DPRESENT_PARAMETERS *pPresentationParameters )
974 {
975 DBG("GetPresentParameters: This=%p pPresentationParameters=%p\n",
976 This, pPresentationParameters);
977 user_assert(pPresentationParameters != NULL, E_POINTER);
978 *pPresentationParameters = This->params;
979 return D3D_OK;
980 }
981
982 IDirect3DSwapChain9Vtbl NineSwapChain9_vtable = {
983 (void *)NineUnknown_QueryInterface,
984 (void *)NineUnknown_AddRef,
985 (void *)NineUnknown_Release,
986 (void *)NineSwapChain9_Present,
987 (void *)NineSwapChain9_GetFrontBufferData,
988 (void *)NineSwapChain9_GetBackBuffer,
989 (void *)NineSwapChain9_GetRasterStatus,
990 (void *)NineSwapChain9_GetDisplayMode,
991 (void *)NineUnknown_GetDevice, /* actually part of SwapChain9 iface */
992 (void *)NineSwapChain9_GetPresentParameters
993 };
994
995 static const GUID *NineSwapChain9_IIDs[] = {
996 &IID_IDirect3DSwapChain9,
997 &IID_IUnknown,
998 NULL
999 };
1000
1001 HRESULT
1002 NineSwapChain9_new( struct NineDevice9 *pDevice,
1003 BOOL implicit,
1004 ID3DPresent *pPresent,
1005 D3DPRESENT_PARAMETERS *pPresentationParameters,
1006 struct d3dadapter9_context *pCTX,
1007 HWND hFocusWindow,
1008 struct NineSwapChain9 **ppOut )
1009 {
1010 NINE_DEVICE_CHILD_NEW(SwapChain9, ppOut, pDevice, /* args */
1011 implicit, pPresent, pPresentationParameters,
1012 pCTX, hFocusWindow, NULL);
1013 }
1014
1015 BOOL
1016 NineSwapChain9_GetOccluded( struct NineSwapChain9 *This )
1017 {
1018 if (This->base.device->minor_version_num > 0) {
1019 return ID3DPresent_GetWindowOccluded(This->present);
1020 }
1021
1022 return FALSE;
1023 }
1024
1025 BOOL
1026 NineSwapChain9_ResolutionMismatch( struct NineSwapChain9 *This )
1027 {
1028 if (This->base.device->minor_version_num > 1) {
1029 return ID3DPresent_ResolutionMismatch(This->present);
1030 }
1031
1032 return FALSE;
1033 }
1034
1035 HANDLE
1036 NineSwapChain9_CreateThread( struct NineSwapChain9 *This,
1037 void *pFuncAddress,
1038 void *pParam )
1039 {
1040 if (This->base.device->minor_version_num > 1) {
1041 return ID3DPresent_CreateThread(This->present, pFuncAddress, pParam);
1042 }
1043
1044 return NULL;
1045 }
1046
1047 void
1048 NineSwapChain9_WaitForThread( struct NineSwapChain9 *This,
1049 HANDLE thread )
1050 {
1051 if (This->base.device->minor_version_num > 1) {
1052 (void) ID3DPresent_WaitForThread(This->present, thread);
1053 }
1054 }