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