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