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