volume9.c revision dfeca90419ac50e0776a3fdb102a2975c8075391
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 "nine_helpers.h" 27#include "nine_pipe.h" 28#include "nine_dump.h" 29 30#include "util/u_hash_table.h" 31#include "util/u_format.h" 32#include "util/u_surface.h" 33#include "nine_pdata.h" 34 35#define DBG_CHANNEL DBG_VOLUME 36 37 38static HRESULT 39NineVolume9_AllocateData( struct NineVolume9 *This ) 40{ 41 unsigned size = This->layer_stride * This->desc.Depth; 42 43 DBG("(%p(This=%p),level=%u) Allocating 0x%x bytes of system memory.\n", 44 This->base.container, This, This->level, size); 45 46 This->data = (uint8_t *)MALLOC(size); 47 if (!This->data) 48 return E_OUTOFMEMORY; 49 return D3D_OK; 50} 51 52static HRESULT 53NineVolume9_ctor( struct NineVolume9 *This, 54 struct NineUnknownParams *pParams, 55 struct NineUnknown *pContainer, 56 struct pipe_resource *pResource, 57 unsigned Level, 58 D3DVOLUME_DESC *pDesc ) 59{ 60 HRESULT hr; 61 62 assert(pContainer); /* stand-alone volumes can't be created */ 63 64 DBG("This=%p pContainer=%p pDevice=%p pResource=%p Level=%u pDesc=%p\n", 65 This, pContainer, pParams->device, pResource, Level, pDesc); 66 67 /* Mark this as a special surface held by another internal resource. */ 68 pParams->container = pContainer; 69 70 user_assert(!(pDesc->Usage & D3DUSAGE_DYNAMIC) || 71 (pDesc->Pool != D3DPOOL_MANAGED), D3DERR_INVALIDCALL); 72 73 assert(pResource || pDesc->Pool != D3DPOOL_DEFAULT); 74 75 hr = NineUnknown_ctor(&This->base, pParams); 76 if (FAILED(hr)) 77 return hr; 78 79 This->pdata = util_hash_table_create(ht_guid_hash, ht_guid_compare); 80 if (!This->pdata) 81 return E_OUTOFMEMORY; 82 83 pipe_resource_reference(&This->resource, pResource); 84 85 This->pipe = pParams->device->pipe; 86 This->transfer = NULL; 87 This->lock_count = 0; 88 89 This->level = Level; 90 This->level_actual = Level; 91 This->desc = *pDesc; 92 93 This->info.screen = pParams->device->screen; 94 This->info.target = PIPE_TEXTURE_3D; 95 This->info.format = d3d9_to_pipe_format(pDesc->Format); 96 This->info.width0 = pDesc->Width; 97 This->info.height0 = pDesc->Height; 98 This->info.depth0 = pDesc->Depth; 99 This->info.last_level = 0; 100 This->info.array_size = 1; 101 This->info.nr_samples = 0; 102 This->info.usage = PIPE_USAGE_DEFAULT; 103 This->info.bind = PIPE_BIND_SAMPLER_VIEW; 104 This->info.flags = 0; 105 106 This->stride = util_format_get_stride(This->info.format, pDesc->Width); 107 This->stride = align(This->stride, 4); 108 This->layer_stride = util_format_get_2d_size(This->info.format, 109 This->stride, pDesc->Height); 110 111 if (pDesc->Pool == D3DPOOL_SYSTEMMEM) 112 This->info.usage = PIPE_USAGE_STAGING; 113 114 if (!This->resource) { 115 hr = NineVolume9_AllocateData(This); 116 if (FAILED(hr)) 117 return hr; 118 } 119 return D3D_OK; 120} 121 122static void 123NineVolume9_dtor( struct NineVolume9 *This ) 124{ 125 DBG("This=%p\n", This); 126 127 if (This->transfer) 128 NineVolume9_UnlockBox(This); 129 130 if (This->data) 131 FREE(This->data); 132 133 pipe_resource_reference(&This->resource, NULL); 134 135 NineUnknown_dtor(&This->base); 136} 137 138HRESULT WINAPI 139NineVolume9_GetContainer( struct NineVolume9 *This, 140 REFIID riid, 141 void **ppContainer ) 142{ 143 if (!NineUnknown(This)->container) 144 return E_NOINTERFACE; 145 return NineUnknown_QueryInterface(NineUnknown(This)->container, riid, ppContainer); 146} 147 148static INLINE void 149NineVolume9_MarkContainerDirty( struct NineVolume9 *This ) 150{ 151 struct NineBaseTexture9 *tex; 152#ifdef DEBUG 153 /* This is always contained by a NineVolumeTexture9. */ 154 GUID id = IID_IDirect3DVolumeTexture9; 155 REFIID ref = &id; 156 assert(NineUnknown_QueryInterface(This->base.container, ref, (void **)&tex) 157 == S_OK); 158 assert(NineUnknown_Release(NineUnknown(tex)) != 0); 159#endif 160 161 tex = NineBaseTexture9(This->base.container); 162 assert(tex); 163 if (This->desc.Pool == D3DPOOL_MANAGED) 164 tex->dirty = TRUE; 165 else 166 if (This->desc.Usage & D3DUSAGE_AUTOGENMIPMAP) 167 tex->dirty_mip = TRUE; 168 169 BASETEX_REGISTER_UPDATE(tex); 170} 171 172HRESULT WINAPI 173NineVolume9_GetDesc( struct NineVolume9 *This, 174 D3DVOLUME_DESC *pDesc ) 175{ 176 user_assert(pDesc != NULL, E_POINTER); 177 *pDesc = This->desc; 178 return D3D_OK; 179} 180 181static INLINE boolean 182NineVolume9_IsDirty(struct NineVolume9 *This) 183{ 184 return This->dirty_box[0].width != 0; 185} 186 187INLINE void 188NineVolume9_AddDirtyRegion( struct NineVolume9 *This, 189 const struct pipe_box *box ) 190{ 191 struct pipe_box cover_a, cover_b; 192 float vol[2]; 193 194 if (!box) { 195 u_box_3d(0, 0, 0, This->desc.Width, This->desc.Height, 196 This->desc.Depth, &This->dirty_box[0]); 197 memset(&This->dirty_box[1], 0, sizeof(This->dirty_box[1])); 198 return; 199 } 200 if (!This->dirty_box[0].width) { 201 This->dirty_box[0] = *box; 202 return; 203 } 204 205 u_box_union_3d(&cover_a, &This->dirty_box[0], box); 206 vol[0] = u_box_volume_3d(&cover_a); 207 208 if (This->dirty_box[1].width == 0) { 209 vol[1] = u_box_volume_3d(&This->dirty_box[0]); 210 if (vol[0] > (vol[1] * 1.5f)) 211 This->dirty_box[1] = *box; 212 else 213 This->dirty_box[0] = cover_a; 214 } else { 215 u_box_union_3d(&cover_b, &This->dirty_box[1], box); 216 vol[1] = u_box_volume_3d(&cover_b); 217 218 if (vol[0] > vol[1]) 219 This->dirty_box[1] = cover_b; 220 else 221 This->dirty_box[0] = cover_a; 222 } 223} 224 225static INLINE uint8_t * 226NineVolume9_GetSystemMemPointer(struct NineVolume9 *This, int x, int y, int z) 227{ 228 unsigned x_offset = util_format_get_stride(This->info.format, x); 229 230 y = util_format_get_nblocksy(This->info.format, y); 231 232 assert(This->data); 233 return This->data + (z * This->layer_stride + y * This->stride + x_offset); 234} 235 236HRESULT WINAPI 237NineVolume9_LockBox( struct NineVolume9 *This, 238 D3DLOCKED_BOX *pLockedVolume, 239 const D3DBOX *pBox, 240 DWORD Flags ) 241{ 242 struct pipe_resource *resource = This->resource; 243 struct pipe_box box; 244 unsigned usage; 245 246 DBG("This=%p(%p) pLockedVolume=%p pBox=%p[%u..%u,%u..%u,%u..%u] Flags=%s\n", 247 This, This->base.container, pLockedVolume, pBox, 248 pBox ? pBox->Left : 0, pBox ? pBox->Right : 0, 249 pBox ? pBox->Top : 0, pBox ? pBox->Bottom : 0, 250 pBox ? pBox->Front : 0, pBox ? pBox->Back : 0, 251 nine_D3DLOCK_to_str(Flags)); 252 253 user_assert(This->desc.Pool != D3DPOOL_DEFAULT || 254 (This->desc.Usage & D3DUSAGE_DYNAMIC), D3DERR_INVALIDCALL); 255 256 user_assert(!((Flags & D3DLOCK_DISCARD) && (Flags & D3DLOCK_READONLY)), 257 D3DERR_INVALIDCALL); 258 259 user_assert(This->lock_count == 0, D3DERR_INVALIDCALL); 260 user_assert(pLockedVolume, E_POINTER); 261 262 if (pBox && This->desc.Pool == D3DPOOL_DEFAULT && 263 util_format_is_compressed(This->info.format)) { 264 const unsigned w = util_format_get_blockwidth(This->info.format); 265 const unsigned h = util_format_get_blockheight(This->info.format); 266 user_assert(!(pBox->Left % w) && !(pBox->Right % w) && 267 !(pBox->Top % h) && !(pBox->Bottom % h), 268 D3DERR_INVALIDCALL); 269 } 270 271 if (Flags & D3DLOCK_DISCARD) { 272 usage = PIPE_TRANSFER_WRITE | PIPE_TRANSFER_DISCARD_RANGE; 273 } else { 274 usage = (Flags & D3DLOCK_READONLY) ? 275 PIPE_TRANSFER_READ : PIPE_TRANSFER_READ_WRITE; 276 } 277 if (Flags & D3DLOCK_DONOTWAIT) 278 usage |= PIPE_TRANSFER_DONTBLOCK; 279 280 if (pBox) { 281 d3dbox_to_pipe_box(&box, pBox); 282 if (u_box_clip_2d(&box, &box, This->desc.Width, This->desc.Height) < 0) { 283 DBG("Locked volume intersection empty.\n"); 284 return D3DERR_INVALIDCALL; 285 } 286 } else { 287 u_box_3d(0, 0, 0, This->desc.Width, This->desc.Height, This->desc.Depth, 288 &box); 289 } 290 291 if (This->data) { 292 pLockedVolume->RowPitch = This->stride; 293 pLockedVolume->SlicePitch = This->layer_stride; 294 pLockedVolume->pBits = 295 NineVolume9_GetSystemMemPointer(This, box.x, box.y, box.z); 296 } else { 297 pLockedVolume->pBits = 298 This->pipe->transfer_map(This->pipe, resource, This->level, usage, 299 &box, &This->transfer); 300 if (!This->transfer) { 301 if (Flags & D3DLOCK_DONOTWAIT) 302 return D3DERR_WASSTILLDRAWING; 303 return D3DERR_DRIVERINTERNALERROR; 304 } 305 pLockedVolume->RowPitch = This->transfer->stride; 306 pLockedVolume->SlicePitch = This->transfer->layer_stride; 307 } 308 309 if (!(Flags & (D3DLOCK_NO_DIRTY_UPDATE | D3DLOCK_READONLY))) { 310 NineVolume9_MarkContainerDirty(This); 311 if (This->desc.Pool == D3DPOOL_MANAGED) 312 NineVolume9_AddDirtyRegion(This, &box); 313 } 314 315 ++This->lock_count; 316 return D3D_OK; 317} 318 319HRESULT WINAPI 320NineVolume9_UnlockBox( struct NineVolume9 *This ) 321{ 322 DBG("This=%p lock_count=%u\n", This, This->lock_count); 323 user_assert(This->lock_count, D3DERR_INVALIDCALL); 324 if (This->transfer) { 325 This->pipe->transfer_unmap(This->pipe, This->transfer); 326 This->transfer = NULL; 327 } 328 --This->lock_count; 329 return D3D_OK; 330} 331 332 333HRESULT 334NineVolume9_CopyVolume( struct NineVolume9 *This, 335 struct NineVolume9 *From, 336 unsigned dstx, unsigned dsty, unsigned dstz, 337 struct pipe_box *pSrcBox ) 338{ 339 struct pipe_context *pipe = This->pipe; 340 struct pipe_resource *r_dst = This->resource; 341 struct pipe_resource *r_src = From->resource; 342 struct pipe_transfer *transfer; 343 struct pipe_box src_box; 344 struct pipe_box dst_box; 345 uint8_t *p_dst; 346 const uint8_t *p_src; 347 348 DBG("This=%p From=%p dstx=%u dsty=%u dstz=%u pSrcBox=%p\n", 349 This, From, dstx, dsty, dstz, pSrcBox); 350 351 user_assert(This->desc.Format == From->desc.Format, D3DERR_INVALIDCALL); 352 353 dst_box.x = dstx; 354 dst_box.y = dsty; 355 dst_box.z = dstz; 356 357 if (pSrcBox) { 358 /* make sure it doesn't range outside the source volume */ 359 user_assert(pSrcBox->x >= 0 && 360 (pSrcBox->width - pSrcBox->x) <= From->desc.Width && 361 pSrcBox->y >= 0 && 362 (pSrcBox->height - pSrcBox->y) <= From->desc.Height && 363 pSrcBox->z >= 0 && 364 (pSrcBox->depth - pSrcBox->z) <= From->desc.Depth, 365 D3DERR_INVALIDCALL); 366 src_box = *pSrcBox; 367 } else { 368 src_box.x = 0; 369 src_box.y = 0; 370 src_box.z = 0; 371 src_box.width = From->desc.Width; 372 src_box.height = From->desc.Height; 373 src_box.depth = From->desc.Depth; 374 } 375 /* limits */ 376 dst_box.width = This->desc.Width - dst_box.x; 377 dst_box.height = This->desc.Height - dst_box.y; 378 dst_box.depth = This->desc.Depth - dst_box.z; 379 380 user_assert(src_box.width <= dst_box.width && 381 src_box.height <= dst_box.height && 382 src_box.depth <= dst_box.depth, D3DERR_INVALIDCALL); 383 384 dst_box.width = src_box.width; 385 dst_box.height = src_box.height; 386 dst_box.depth = src_box.depth; 387 388 /* Don't copy to device memory of managed resources. 389 * We don't want to download it back again later. 390 */ 391 if (This->desc.Pool == D3DPOOL_MANAGED) 392 r_dst = NULL; 393 394 /* Don't copy from stale device memory of managed resources. 395 * Also, don't copy between system and device if we don't have to. 396 */ 397 if (From->desc.Pool == D3DPOOL_MANAGED) { 398 if (!r_dst || NineVolume9_IsDirty(From)) 399 r_src = NULL; 400 } 401 402 if (r_dst && r_src) { 403 pipe->resource_copy_region(pipe, 404 r_dst, This->level, 405 dst_box.x, dst_box.y, dst_box.z, 406 r_src, From->level, 407 &src_box); 408 } else 409 if (r_dst) { 410 p_src = NineVolume9_GetSystemMemPointer(From, 411 src_box.x, src_box.y, src_box.z); 412 413 pipe->transfer_inline_write(pipe, r_dst, This->level, 414 0, /* WRITE|DISCARD are implicit */ 415 &dst_box, p_src, 416 From->stride, From->layer_stride); 417 } else 418 if (r_src) { 419 p_dst = NineVolume9_GetSystemMemPointer(This, 0, 0, 0); 420 p_src = pipe->transfer_map(pipe, r_src, From->level, 421 PIPE_TRANSFER_READ, 422 &src_box, &transfer); 423 if (!p_src) 424 return D3DERR_DRIVERINTERNALERROR; 425 426 util_copy_box(p_dst, This->info.format, 427 This->stride, This->layer_stride, 428 dst_box.x, dst_box.y, dst_box.z, 429 dst_box.width, dst_box.height, dst_box.depth, 430 p_src, 431 transfer->stride, transfer->layer_stride, 432 src_box.x, src_box.y, src_box.z); 433 434 pipe->transfer_unmap(pipe, transfer); 435 } else { 436 p_dst = NineVolume9_GetSystemMemPointer(This, 0, 0, 0); 437 p_src = NineVolume9_GetSystemMemPointer(From, 0, 0, 0); 438 439 util_copy_box(p_dst, This->info.format, 440 This->stride, This->layer_stride, 441 dst_box.x, dst_box.y, dst_box.z, 442 dst_box.width, dst_box.height, dst_box.depth, 443 p_src, 444 From->stride, From->layer_stride, 445 src_box.x, src_box.y, src_box.z); 446 } 447 448 if (This->desc.Pool == D3DPOOL_DEFAULT || 449 This->desc.Pool == D3DPOOL_MANAGED) 450 NineVolume9_MarkContainerDirty(This); 451 if (!r_dst && This->resource) 452 NineVolume9_AddDirtyRegion(This, &dst_box); 453 454 return D3D_OK; 455} 456 457HRESULT 458NineVolume9_UploadSelf( struct NineVolume9 *This ) 459{ 460 struct pipe_context *pipe = This->pipe; 461 struct pipe_resource *res = This->resource; 462 uint8_t *ptr; 463 unsigned i; 464 465 DBG("This=%p dirty=%i data=%p res=%p\n", This, NineVolume9_IsDirty(This), 466 This->data, res); 467 468 assert(This->desc.Pool == D3DPOOL_MANAGED); 469 470 if (!NineVolume9_IsDirty(This)) 471 return D3D_OK; 472 assert(res); 473 474 for (i = 0; i < Elements(This->dirty_box); ++i) { 475 const struct pipe_box *box = &This->dirty_box[i]; 476 if (box->width == 0) 477 break; 478 ptr = NineVolume9_GetSystemMemPointer(This, box->x, box->y, box->z); 479 480 pipe->transfer_inline_write(pipe, res, This->level, 481 0, 482 box, ptr, This->stride, This->layer_stride); 483 } 484 NineVolume9_ClearDirtyRegion(This); 485 486 return D3D_OK; 487} 488 489 490IDirect3DVolume9Vtbl NineVolume9_vtable = { 491 (void *)NineUnknown_QueryInterface, 492 (void *)NineUnknown_AddRef, 493 (void *)NineUnknown_Release, 494 (void *)NineUnknown_GetDevice, /* actually part of Volume9 iface */ 495 (void *)NineVolume9_SetPrivateData, 496 (void *)NineVolume9_GetPrivateData, 497 (void *)NineVolume9_FreePrivateData, 498 (void *)NineVolume9_GetContainer, 499 (void *)NineVolume9_GetDesc, 500 (void *)NineVolume9_LockBox, 501 (void *)NineVolume9_UnlockBox 502}; 503 504static const GUID *NineVolume9_IIDs[] = { 505 &IID_IDirect3DVolume9, 506 &IID_IUnknown, 507 NULL 508}; 509 510HRESULT 511NineVolume9_new( struct NineDevice9 *pDevice, 512 struct NineUnknown *pContainer, 513 struct pipe_resource *pResource, 514 unsigned Level, 515 D3DVOLUME_DESC *pDesc, 516 struct NineVolume9 **ppOut ) 517{ 518 NINE_DEVICE_CHILD_NEW(Volume9, ppOut, pDevice, /* args */ 519 pContainer, pResource, Level, pDesc); 520} 521 522 523/*** The boring stuff. TODO: Unify with Resource. ***/ 524 525HRESULT WINAPI 526NineVolume9_SetPrivateData( struct NineVolume9 *This, 527 REFGUID refguid, 528 const void *pData, 529 DWORD SizeOfData, 530 DWORD Flags ) 531{ 532 enum pipe_error err; 533 struct pheader *header; 534 const void *user_data = pData; 535 536 DBG("This=%p refguid=%p pData=%p SizeOfData=%d Flags=%d\n", 537 This, refguid, pData, SizeOfData, Flags); 538 539 if (Flags & D3DSPD_IUNKNOWN) 540 user_assert(SizeOfData == sizeof(IUnknown *), D3DERR_INVALIDCALL); 541 542 /* data consists of a header and the actual data. avoiding 2 mallocs */ 543 header = CALLOC_VARIANT_LENGTH_STRUCT(pheader, SizeOfData-1); 544 if (!header) { return E_OUTOFMEMORY; } 545 header->unknown = (Flags & D3DSPD_IUNKNOWN) ? TRUE : FALSE; 546 547 /* if the refguid already exists, delete it */ 548 NineVolume9_FreePrivateData(This, refguid); 549 550 /* IUnknown special case */ 551 if (header->unknown) { 552 /* here the pointer doesn't point to the data we want, so point at the 553 * pointer making what we eventually copy is the pointer itself */ 554 user_data = &pData; 555 } 556 557 header->size = SizeOfData; 558 memcpy(header->data, user_data, header->size); 559 560 err = util_hash_table_set(This->pdata, refguid, header); 561 if (err == PIPE_OK) { 562 if (header->unknown) { IUnknown_AddRef(*(IUnknown **)header->data); } 563 return D3D_OK; 564 } 565 566 FREE(header); 567 if (err == PIPE_ERROR_OUT_OF_MEMORY) { return E_OUTOFMEMORY; } 568 569 return D3DERR_DRIVERINTERNALERROR; 570} 571 572HRESULT WINAPI 573NineVolume9_GetPrivateData( struct NineVolume9 *This, 574 REFGUID refguid, 575 void *pData, 576 DWORD *pSizeOfData ) 577{ 578 struct pheader *header; 579 580 user_assert(pSizeOfData, E_POINTER); 581 582 header = util_hash_table_get(This->pdata, refguid); 583 if (!header) { return D3DERR_NOTFOUND; } 584 585 if (!pData) { 586 *pSizeOfData = header->size; 587 return D3D_OK; 588 } 589 if (*pSizeOfData < header->size) { 590 return D3DERR_MOREDATA; 591 } 592 593 if (header->unknown) { IUnknown_AddRef(*(IUnknown **)header->data); } 594 memcpy(pData, header->data, header->size); 595 596 return D3D_OK; 597} 598 599HRESULT WINAPI 600NineVolume9_FreePrivateData( struct NineVolume9 *This, 601 REFGUID refguid ) 602{ 603 struct pheader *header; 604 605 DBG("This=%p refguid=%p\n", This, refguid); 606 607 header = util_hash_table_get(This->pdata, refguid); 608 if (!header) { return D3DERR_NOTFOUND; } 609 610 ht_guid_delete(NULL, header, NULL); 611 util_hash_table_remove(This->pdata, refguid); 612 613 return D3D_OK; 614} 615 616