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