1 /* 2 * Copyright 2011 Joakim Sindholt <opensource (at) 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->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 * 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 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 * 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 * 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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