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