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 "device9.h" 24#include "volume9.h" 25#include "basetexture9.h" /* for marking dirty */ 26#include "volumetexture9.h" 27#include "nine_helpers.h" 28#include "nine_pipe.h" 29#include "nine_dump.h" 30 31#include "util/u_format.h" 32#include "util/u_surface.h" 33 34#define DBG_CHANNEL DBG_VOLUME 35 36 37static HRESULT 38NineVolume9_AllocateData( struct NineVolume9 *This ) 39{ 40 unsigned size = This->layer_stride * This->desc.Depth; 41 42 DBG("(%p(This=%p),level=%u) Allocating 0x%x bytes of system memory.\n", 43 This->base.container, This, This->level, size); 44 45 This->data = (uint8_t *)align_calloc(size, 32); 46 if (!This->data) 47 return E_OUTOFMEMORY; 48 return D3D_OK; 49} 50 51static HRESULT 52NineVolume9_ctor( struct NineVolume9 *This, 53 struct NineUnknownParams *pParams, 54 struct NineUnknown *pContainer, 55 struct pipe_resource *pResource, 56 unsigned Level, 57 D3DVOLUME_DESC *pDesc ) 58{ 59 HRESULT hr; 60 61 assert(pContainer); /* stand-alone volumes can't be created */ 62 63 DBG("This=%p pContainer=%p pDevice=%p pResource=%p Level=%u pDesc=%p\n", 64 This, pContainer, pParams->device, pResource, Level, pDesc); 65 66 /* Mark this as a special surface held by another internal resource. */ 67 pParams->container = pContainer; 68 69 user_assert(!(pDesc->Usage & D3DUSAGE_DYNAMIC) || 70 (pDesc->Pool != D3DPOOL_MANAGED), D3DERR_INVALIDCALL); 71 72 assert(pResource || pDesc->Pool != D3DPOOL_DEFAULT); 73 74 hr = NineUnknown_ctor(&This->base, pParams); 75 if (FAILED(hr)) 76 return hr; 77 78 pipe_resource_reference(&This->resource, pResource); 79 80 This->transfer = NULL; 81 This->lock_count = 0; 82 83 This->level = Level; 84 This->level_actual = Level; 85 This->desc = *pDesc; 86 87 This->info.screen = pParams->device->screen; 88 This->info.target = PIPE_TEXTURE_3D; 89 This->info.width0 = pDesc->Width; 90 This->info.height0 = pDesc->Height; 91 This->info.depth0 = pDesc->Depth; 92 This->info.last_level = 0; 93 This->info.array_size = 1; 94 This->info.nr_samples = 0; 95 This->info.nr_storage_samples = 0; 96 This->info.usage = PIPE_USAGE_DEFAULT; 97 This->info.bind = PIPE_BIND_SAMPLER_VIEW; 98 This->info.flags = 0; 99 This->info.format = d3d9_to_pipe_format_checked(This->info.screen, 100 pDesc->Format, 101 This->info.target, 102 This->info.nr_samples, 103 This->info.bind, FALSE, 104 pDesc->Pool == D3DPOOL_SCRATCH); 105 106 if (This->info.format == PIPE_FORMAT_NONE) 107 return D3DERR_DRIVERINTERNALERROR; 108 109 This->stride = util_format_get_stride(This->info.format, pDesc->Width); 110 This->stride = align(This->stride, 4); 111 This->layer_stride = util_format_get_2d_size(This->info.format, 112 This->stride, pDesc->Height); 113 114 /* Get true format */ 115 This->format_internal = d3d9_to_pipe_format_checked(This->info.screen, 116 pDesc->Format, 117 This->info.target, 118 This->info.nr_samples, 119 This->info.bind, FALSE, 120 TRUE); 121 if (This->info.format != This->format_internal || 122 /* DYNAMIC Textures requires same stride as ram buffers. 123 * Do not use workaround by default as it eats more virtual space */ 124 (pParams->device->workarounds.dynamic_texture_workaround && 125 pDesc->Pool == D3DPOOL_DEFAULT && pDesc->Usage & D3DUSAGE_DYNAMIC)) { 126 This->stride_internal = nine_format_get_stride(This->format_internal, 127 pDesc->Width); 128 This->layer_stride_internal = util_format_get_2d_size(This->format_internal, 129 This->stride_internal, 130 pDesc->Height); 131 This->data_internal = align_calloc(This->layer_stride_internal * 132 This->desc.Depth, 32); 133 if (!This->data_internal) 134 return E_OUTOFMEMORY; 135 } 136 137 if (!This->resource) { 138 hr = NineVolume9_AllocateData(This); 139 if (FAILED(hr)) 140 return hr; 141 } 142 return D3D_OK; 143} 144 145static void 146NineVolume9_dtor( struct NineVolume9 *This ) 147{ 148 DBG("This=%p\n", This); 149 150 if (This->transfer) { 151 struct pipe_context *pipe = nine_context_get_pipe_multithread(This->base.device); 152 pipe->transfer_unmap(pipe, This->transfer); 153 This->transfer = NULL; 154 } 155 156 /* Note: Following condition cannot happen currently, since we 157 * refcount the volume in the functions increasing 158 * pending_uploads_counter. */ 159 if (p_atomic_read(&This->pending_uploads_counter)) 160 nine_csmt_process(This->base.device); 161 162 if (This->data) 163 align_free(This->data); 164 if (This->data_internal) 165 align_free(This->data_internal); 166 167 pipe_resource_reference(&This->resource, NULL); 168 169 NineUnknown_dtor(&This->base); 170} 171 172HRESULT NINE_WINAPI 173NineVolume9_GetContainer( struct NineVolume9 *This, 174 REFIID riid, 175 void **ppContainer ) 176{ 177 char guid_str[64]; 178 179 DBG("This=%p riid=%p id=%s ppContainer=%p\n", 180 This, riid, riid ? GUID_sprintf(guid_str, riid) : "", ppContainer); 181 182 (void)guid_str; 183 184 if (!NineUnknown(This)->container) 185 return E_NOINTERFACE; 186 return NineUnknown_QueryInterface(NineUnknown(This)->container, riid, ppContainer); 187} 188 189static inline void 190NineVolume9_MarkContainerDirty( struct NineVolume9 *This ) 191{ 192 struct NineBaseTexture9 *tex; 193#if defined(DEBUG) || !defined(NDEBUG) 194 /* This is always contained by a NineVolumeTexture9. */ 195 GUID id = IID_IDirect3DVolumeTexture9; 196 REFIID ref = &id; 197 assert(NineUnknown_QueryInterface(This->base.container, ref, (void **)&tex) 198 == S_OK); 199 assert(NineUnknown_Release(NineUnknown(tex)) != 0); 200#endif 201 202 tex = NineBaseTexture9(This->base.container); 203 assert(tex); 204 if (This->desc.Pool == D3DPOOL_MANAGED) 205 tex->managed.dirty = TRUE; 206 207 BASETEX_REGISTER_UPDATE(tex); 208} 209 210HRESULT NINE_WINAPI 211NineVolume9_GetDesc( struct NineVolume9 *This, 212 D3DVOLUME_DESC *pDesc ) 213{ 214 user_assert(pDesc != NULL, E_POINTER); 215 *pDesc = This->desc; 216 return D3D_OK; 217} 218 219inline void 220NineVolume9_AddDirtyRegion( struct NineVolume9 *This, 221 const struct pipe_box *box ) 222{ 223 D3DBOX dirty_region; 224 struct NineVolumeTexture9 *tex = NineVolumeTexture9(This->base.container); 225 226 if (!box) { 227 NineVolumeTexture9_AddDirtyBox(tex, NULL); 228 } else { 229 dirty_region.Left = box->x << This->level_actual; 230 dirty_region.Top = box->y << This->level_actual; 231 dirty_region.Front = box->z << This->level_actual; 232 dirty_region.Right = dirty_region.Left + (box->width << This->level_actual); 233 dirty_region.Bottom = dirty_region.Top + (box->height << This->level_actual); 234 dirty_region.Back = dirty_region.Front + (box->depth << This->level_actual); 235 NineVolumeTexture9_AddDirtyBox(tex, &dirty_region); 236 } 237} 238 239static inline unsigned 240NineVolume9_GetSystemMemOffset(enum pipe_format format, unsigned stride, 241 unsigned layer_stride, 242 int x, int y, int z) 243{ 244 unsigned x_offset = util_format_get_stride(format, x); 245 246 y = util_format_get_nblocksy(format, y); 247 248 return z * layer_stride + y * stride + x_offset; 249} 250 251HRESULT NINE_WINAPI 252NineVolume9_LockBox( struct NineVolume9 *This, 253 D3DLOCKED_BOX *pLockedVolume, 254 const D3DBOX *pBox, 255 DWORD Flags ) 256{ 257 struct pipe_context *pipe; 258 struct pipe_resource *resource = This->resource; 259 struct pipe_box box; 260 unsigned usage; 261 262 DBG("This=%p(%p) pLockedVolume=%p pBox=%p[%u..%u,%u..%u,%u..%u] Flags=%s\n", 263 This, This->base.container, pLockedVolume, pBox, 264 pBox ? pBox->Left : 0, pBox ? pBox->Right : 0, 265 pBox ? pBox->Top : 0, pBox ? pBox->Bottom : 0, 266 pBox ? pBox->Front : 0, pBox ? pBox->Back : 0, 267 nine_D3DLOCK_to_str(Flags)); 268 269 /* check if it's already locked */ 270 user_assert(This->lock_count == 0, D3DERR_INVALIDCALL); 271 272 /* set pBits to NULL after lock_count check */ 273 user_assert(pLockedVolume, E_POINTER); 274 pLockedVolume->pBits = NULL; 275 276 user_assert(This->desc.Pool != D3DPOOL_DEFAULT || 277 (This->desc.Usage & D3DUSAGE_DYNAMIC), D3DERR_INVALIDCALL); 278 279 user_assert(!((Flags & D3DLOCK_DISCARD) && (Flags & D3DLOCK_READONLY)), 280 D3DERR_INVALIDCALL); 281 282 if (pBox && compressed_format (This->desc.Format)) { /* For volume all pools are checked */ 283 const unsigned w = util_format_get_blockwidth(This->info.format); 284 const unsigned h = util_format_get_blockheight(This->info.format); 285 user_assert((pBox->Left == 0 && pBox->Right == This->desc.Width && 286 pBox->Top == 0 && pBox->Bottom == This->desc.Height) || 287 (!(pBox->Left % w) && !(pBox->Right % w) && 288 !(pBox->Top % h) && !(pBox->Bottom % h)), 289 D3DERR_INVALIDCALL); 290 } 291 292 if (Flags & D3DLOCK_DISCARD) { 293 usage = PIPE_TRANSFER_WRITE | PIPE_TRANSFER_DISCARD_RANGE; 294 } else { 295 usage = (Flags & D3DLOCK_READONLY) ? 296 PIPE_TRANSFER_READ : PIPE_TRANSFER_READ_WRITE; 297 } 298 if (Flags & D3DLOCK_DONOTWAIT) 299 usage |= PIPE_TRANSFER_DONTBLOCK; 300 301 if (pBox) { 302 user_assert(pBox->Right > pBox->Left, D3DERR_INVALIDCALL); 303 user_assert(pBox->Bottom > pBox->Top, D3DERR_INVALIDCALL); 304 user_assert(pBox->Back > pBox->Front, D3DERR_INVALIDCALL); 305 user_assert(pBox->Right <= This->desc.Width, D3DERR_INVALIDCALL); 306 user_assert(pBox->Bottom <= This->desc.Height, D3DERR_INVALIDCALL); 307 user_assert(pBox->Back <= This->desc.Depth, D3DERR_INVALIDCALL); 308 309 d3dbox_to_pipe_box(&box, pBox); 310 if (u_box_clip_2d(&box, &box, This->desc.Width, This->desc.Height) < 0) { 311 DBG("Locked volume intersection empty.\n"); 312 return D3DERR_INVALIDCALL; 313 } 314 } else { 315 u_box_3d(0, 0, 0, This->desc.Width, This->desc.Height, This->desc.Depth, 316 &box); 317 } 318 319 if (p_atomic_read(&This->pending_uploads_counter)) 320 nine_csmt_process(This->base.device); 321 322 if (This->data_internal || This->data) { 323 enum pipe_format format = This->info.format; 324 unsigned stride = This->stride; 325 unsigned layer_stride = This->layer_stride; 326 uint8_t *data = This->data; 327 if (This->data_internal) { 328 format = This->format_internal; 329 stride = This->stride_internal; 330 layer_stride = This->layer_stride_internal; 331 data = This->data_internal; 332 } 333 pLockedVolume->RowPitch = stride; 334 pLockedVolume->SlicePitch = layer_stride; 335 pLockedVolume->pBits = data + 336 NineVolume9_GetSystemMemOffset(format, stride, 337 layer_stride, 338 box.x, box.y, box.z); 339 } else { 340 bool no_refs = !p_atomic_read(&This->base.bind) && 341 !p_atomic_read(&This->base.container->bind); 342 if (no_refs) 343 pipe = nine_context_get_pipe_acquire(This->base.device); 344 else 345 pipe = NineDevice9_GetPipe(This->base.device); 346 pLockedVolume->pBits = 347 pipe->transfer_map(pipe, resource, This->level, usage, 348 &box, &This->transfer); 349 if (no_refs) 350 nine_context_get_pipe_release(This->base.device); 351 if (!This->transfer) { 352 if (Flags & D3DLOCK_DONOTWAIT) 353 return D3DERR_WASSTILLDRAWING; 354 return D3DERR_DRIVERINTERNALERROR; 355 } 356 pLockedVolume->RowPitch = This->transfer->stride; 357 pLockedVolume->SlicePitch = This->transfer->layer_stride; 358 } 359 360 if (!(Flags & (D3DLOCK_NO_DIRTY_UPDATE | D3DLOCK_READONLY))) { 361 NineVolume9_MarkContainerDirty(This); 362 NineVolume9_AddDirtyRegion(This, &box); 363 } 364 365 ++This->lock_count; 366 return D3D_OK; 367} 368 369HRESULT NINE_WINAPI 370NineVolume9_UnlockBox( struct NineVolume9 *This ) 371{ 372 struct pipe_context *pipe; 373 374 DBG("This=%p lock_count=%u\n", This, This->lock_count); 375 user_assert(This->lock_count, D3DERR_INVALIDCALL); 376 if (This->transfer) { 377 pipe = nine_context_get_pipe_acquire(This->base.device); 378 pipe->transfer_unmap(pipe, This->transfer); 379 This->transfer = NULL; 380 nine_context_get_pipe_release(This->base.device); 381 } 382 --This->lock_count; 383 384 if (This->data_internal) { 385 struct pipe_box box; 386 387 u_box_3d(0, 0, 0, This->desc.Width, This->desc.Height, This->desc.Depth, 388 &box); 389 390 391 if (This->data) { 392 (void) util_format_translate_3d(This->info.format, 393 This->data, This->stride, 394 This->layer_stride, 395 0, 0, 0, 396 This->format_internal, 397 This->data_internal, 398 This->stride_internal, 399 This->layer_stride_internal, 400 0, 0, 0, 401 This->desc.Width, This->desc.Height, 402 This->desc.Depth); 403 } else { 404 nine_context_box_upload(This->base.device, 405 &This->pending_uploads_counter, 406 (struct NineUnknown *)This, 407 This->resource, 408 This->level, 409 &box, 410 This->format_internal, 411 This->data_internal, 412 This->stride_internal, 413 This->layer_stride_internal, 414 &box); 415 } 416 } 417 418 return D3D_OK; 419} 420 421/* When this function is called, we have already checked 422 * The copy regions fit the volumes */ 423void 424NineVolume9_CopyMemToDefault( struct NineVolume9 *This, 425 struct NineVolume9 *From, 426 unsigned dstx, unsigned dsty, unsigned dstz, 427 struct pipe_box *pSrcBox ) 428{ 429 struct pipe_resource *r_dst = This->resource; 430 struct pipe_box src_box; 431 struct pipe_box dst_box; 432 433 DBG("This=%p From=%p dstx=%u dsty=%u dstz=%u pSrcBox=%p\n", 434 This, From, dstx, dsty, dstz, pSrcBox); 435 436 assert(This->desc.Pool == D3DPOOL_DEFAULT && 437 From->desc.Pool == D3DPOOL_SYSTEMMEM); 438 439 dst_box.x = dstx; 440 dst_box.y = dsty; 441 dst_box.z = dstz; 442 443 if (pSrcBox) { 444 src_box = *pSrcBox; 445 } else { 446 src_box.x = 0; 447 src_box.y = 0; 448 src_box.z = 0; 449 src_box.width = From->desc.Width; 450 src_box.height = From->desc.Height; 451 src_box.depth = From->desc.Depth; 452 } 453 454 dst_box.width = src_box.width; 455 dst_box.height = src_box.height; 456 dst_box.depth = src_box.depth; 457 458 nine_context_box_upload(This->base.device, 459 &From->pending_uploads_counter, 460 (struct NineUnknown *)From, 461 r_dst, 462 This->level, 463 &dst_box, 464 From->info.format, 465 From->data, From->stride, 466 From->layer_stride, 467 &src_box); 468 469 if (This->data_internal) 470 (void) util_format_translate_3d(This->format_internal, 471 This->data_internal, 472 This->stride_internal, 473 This->layer_stride_internal, 474 dstx, dsty, dstz, 475 From->info.format, 476 From->data, From->stride, 477 From->layer_stride, 478 src_box.x, src_box.y, 479 src_box.z, 480 src_box.width, 481 src_box.height, 482 src_box.depth); 483 484 NineVolume9_MarkContainerDirty(This); 485 486 return; 487} 488 489HRESULT 490NineVolume9_UploadSelf( struct NineVolume9 *This, 491 const struct pipe_box *damaged ) 492{ 493 struct pipe_resource *res = This->resource; 494 struct pipe_box box; 495 496 DBG("This=%p damaged=%p data=%p res=%p\n", This, damaged, 497 This->data, res); 498 499 assert(This->desc.Pool == D3DPOOL_MANAGED); 500 assert(res); 501 502 if (damaged) { 503 box = *damaged; 504 } else { 505 box.x = 0; 506 box.y = 0; 507 box.z = 0; 508 box.width = This->desc.Width; 509 box.height = This->desc.Height; 510 box.depth = This->desc.Depth; 511 } 512 513 nine_context_box_upload(This->base.device, 514 &This->pending_uploads_counter, 515 (struct NineUnknown *)This, 516 res, 517 This->level, 518 &box, 519 res->format, 520 This->data, This->stride, 521 This->layer_stride, 522 &box); 523 524 return D3D_OK; 525} 526 527 528IDirect3DVolume9Vtbl NineVolume9_vtable = { 529 (void *)NineUnknown_QueryInterface, 530 (void *)NineUnknown_AddRef, 531 (void *)NineUnknown_Release, 532 (void *)NineUnknown_GetDevice, /* actually part of Volume9 iface */ 533 (void *)NineUnknown_SetPrivateData, 534 (void *)NineUnknown_GetPrivateData, 535 (void *)NineUnknown_FreePrivateData, 536 (void *)NineVolume9_GetContainer, 537 (void *)NineVolume9_GetDesc, 538 (void *)NineVolume9_LockBox, 539 (void *)NineVolume9_UnlockBox 540}; 541 542static const GUID *NineVolume9_IIDs[] = { 543 &IID_IDirect3DVolume9, 544 &IID_IUnknown, 545 NULL 546}; 547 548HRESULT 549NineVolume9_new( struct NineDevice9 *pDevice, 550 struct NineUnknown *pContainer, 551 struct pipe_resource *pResource, 552 unsigned Level, 553 D3DVOLUME_DESC *pDesc, 554 struct NineVolume9 **ppOut ) 555{ 556 NINE_DEVICE_CHILD_NEW(Volume9, ppOut, pDevice, /* args */ 557 pContainer, pResource, Level, pDesc); 558} 559