2 * Copyright 2011 Joakim Sindholt <opensource@zhasha.com>
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:
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
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. */
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"
31 #include "util/u_hash_table.h"
32 #include "util/u_format.h"
33 #include "util/u_surface.h"
34 #include "nine_pdata.h"
36 #define DBG_CHANNEL DBG_VOLUME
40 NineVolume9_AllocateData( struct NineVolume9
*This
)
42 unsigned size
= This
->layer_stride
* This
->desc
.Depth
;
44 DBG("(%p(This=%p),level=%u) Allocating 0x%x bytes of system memory.\n",
45 This
->base
.container
, This
, This
->level
, size
);
47 This
->data
= (uint8_t *)align_malloc(size
, 32);
54 NineVolume9_ctor( struct NineVolume9
*This
,
55 struct NineUnknownParams
*pParams
,
56 struct NineUnknown
*pContainer
,
57 struct pipe_resource
*pResource
,
59 D3DVOLUME_DESC
*pDesc
)
63 assert(pContainer
); /* stand-alone volumes can't be created */
65 DBG("This=%p pContainer=%p pDevice=%p pResource=%p Level=%u pDesc=%p\n",
66 This
, pContainer
, pParams
->device
, pResource
, Level
, pDesc
);
68 /* Mark this as a special surface held by another internal resource. */
69 pParams
->container
= pContainer
;
71 user_assert(!(pDesc
->Usage
& D3DUSAGE_DYNAMIC
) ||
72 (pDesc
->Pool
!= D3DPOOL_MANAGED
), D3DERR_INVALIDCALL
);
74 assert(pResource
|| pDesc
->Pool
!= D3DPOOL_DEFAULT
);
76 hr
= NineUnknown_ctor(&This
->base
, pParams
);
80 This
->pdata
= util_hash_table_create(ht_guid_hash
, ht_guid_compare
);
84 pipe_resource_reference(&This
->resource
, pResource
);
86 This
->pipe
= pParams
->device
->pipe
;
87 This
->transfer
= NULL
;
91 This
->level_actual
= Level
;
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
,
108 This
->info
.nr_samples
,
109 This
->info
.bind
, FALSE
,
110 pDesc
->Pool
== D3DPOOL_SCRATCH
);
112 if (This
->info
.format
== PIPE_FORMAT_NONE
)
113 return D3DERR_DRIVERINTERNALERROR
;
115 This
->stride
= util_format_get_stride(This
->info
.format
, pDesc
->Width
);
116 This
->stride
= align(This
->stride
, 4);
117 This
->layer_stride
= util_format_get_2d_size(This
->info
.format
,
118 This
->stride
, pDesc
->Height
);
120 /* Get true format */
121 This
->format_conversion
= d3d9_to_pipe_format_checked(This
->info
.screen
,
124 This
->info
.nr_samples
,
125 This
->info
.bind
, FALSE
,
127 if (This
->info
.format
!= This
->format_conversion
) {
128 This
->stride_conversion
= nine_format_get_stride(This
->format_conversion
,
130 This
->layer_stride_conversion
= util_format_get_2d_size(This
->format_conversion
,
131 This
->stride_conversion
,
133 This
->data_conversion
= align_malloc(This
->layer_stride_conversion
*
134 This
->desc
.Depth
, 32);
135 if (!This
->data_conversion
)
136 return E_OUTOFMEMORY
;
139 if (!This
->resource
) {
140 hr
= NineVolume9_AllocateData(This
);
148 NineVolume9_dtor( struct NineVolume9
*This
)
150 DBG("This=%p\n", This
);
153 NineVolume9_UnlockBox(This
);
156 align_free(This
->data
);
157 if (This
->data_conversion
)
158 align_free(This
->data_conversion
);
160 pipe_resource_reference(&This
->resource
, NULL
);
162 NineUnknown_dtor(&This
->base
);
166 NineVolume9_GetContainer( struct NineVolume9
*This
,
170 if (!NineUnknown(This
)->container
)
171 return E_NOINTERFACE
;
172 return NineUnknown_QueryInterface(NineUnknown(This
)->container
, riid
, ppContainer
);
176 NineVolume9_MarkContainerDirty( struct NineVolume9
*This
)
178 struct NineBaseTexture9
*tex
;
180 /* This is always contained by a NineVolumeTexture9. */
181 GUID id
= IID_IDirect3DVolumeTexture9
;
183 assert(NineUnknown_QueryInterface(This
->base
.container
, ref
, (void **)&tex
)
185 assert(NineUnknown_Release(NineUnknown(tex
)) != 0);
188 tex
= NineBaseTexture9(This
->base
.container
);
190 if (This
->desc
.Pool
== D3DPOOL_MANAGED
)
191 tex
->managed
.dirty
= TRUE
;
193 BASETEX_REGISTER_UPDATE(tex
);
197 NineVolume9_GetDesc( struct NineVolume9
*This
,
198 D3DVOLUME_DESC
*pDesc
)
200 user_assert(pDesc
!= NULL
, E_POINTER
);
206 NineVolume9_AddDirtyRegion( struct NineVolume9
*This
,
207 const struct pipe_box
*box
)
210 struct NineVolumeTexture9
*tex
= NineVolumeTexture9(This
->base
.container
);
213 NineVolumeTexture9_AddDirtyBox(tex
, NULL
);
215 dirty_region
.Left
= box
->x
<< This
->level_actual
;
216 dirty_region
.Top
= box
->y
<< This
->level_actual
;
217 dirty_region
.Front
= box
->z
<< This
->level_actual
;
218 dirty_region
.Right
= dirty_region
.Left
+ (box
->width
<< This
->level_actual
);
219 dirty_region
.Bottom
= dirty_region
.Top
+ (box
->height
<< This
->level_actual
);
220 dirty_region
.Back
= dirty_region
.Front
+ (box
->depth
<< This
->level_actual
);
221 NineVolumeTexture9_AddDirtyBox(tex
, &dirty_region
);
225 static inline uint8_t *
226 NineVolume9_GetSystemMemPointer(struct NineVolume9
*This
, int x
, int y
, int z
)
228 unsigned x_offset
= util_format_get_stride(This
->info
.format
, x
);
230 y
= util_format_get_nblocksy(This
->info
.format
, y
);
233 return This
->data
+ (z
* This
->layer_stride
+ y
* This
->stride
+ x_offset
);
237 NineVolume9_LockBox( struct NineVolume9
*This
,
238 D3DLOCKED_BOX
*pLockedVolume
,
242 struct pipe_resource
*resource
= This
->resource
;
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
));
253 /* check if it's already locked */
254 user_assert(This
->lock_count
== 0, D3DERR_INVALIDCALL
);
256 /* set pBits to NULL after lock_count check */
257 user_assert(pLockedVolume
, E_POINTER
);
258 pLockedVolume
->pBits
= NULL
;
260 user_assert(This
->desc
.Pool
!= D3DPOOL_DEFAULT
||
261 (This
->desc
.Usage
& D3DUSAGE_DYNAMIC
), D3DERR_INVALIDCALL
);
263 user_assert(!((Flags
& D3DLOCK_DISCARD
) && (Flags
& D3DLOCK_READONLY
)),
266 if (pBox
&& compressed_format (This
->desc
.Format
)) { /* For volume all pools are checked */
267 const unsigned w
= util_format_get_blockwidth(This
->info
.format
);
268 const unsigned h
= util_format_get_blockheight(This
->info
.format
);
269 user_assert((pBox
->Left
== 0 && pBox
->Right
== This
->desc
.Width
&&
270 pBox
->Top
== 0 && pBox
->Bottom
== This
->desc
.Height
) ||
271 (!(pBox
->Left
% w
) && !(pBox
->Right
% w
) &&
272 !(pBox
->Top
% h
) && !(pBox
->Bottom
% h
)),
276 if (Flags
& D3DLOCK_DISCARD
) {
277 usage
= PIPE_TRANSFER_WRITE
| PIPE_TRANSFER_DISCARD_RANGE
;
279 usage
= (Flags
& D3DLOCK_READONLY
) ?
280 PIPE_TRANSFER_READ
: PIPE_TRANSFER_READ_WRITE
;
282 if (Flags
& D3DLOCK_DONOTWAIT
)
283 usage
|= PIPE_TRANSFER_DONTBLOCK
;
286 user_assert(pBox
->Right
> pBox
->Left
, D3DERR_INVALIDCALL
);
287 user_assert(pBox
->Bottom
> pBox
->Top
, D3DERR_INVALIDCALL
);
288 user_assert(pBox
->Back
> pBox
->Front
, D3DERR_INVALIDCALL
);
289 user_assert(pBox
->Right
<= This
->desc
.Width
, D3DERR_INVALIDCALL
);
290 user_assert(pBox
->Bottom
<= This
->desc
.Height
, D3DERR_INVALIDCALL
);
291 user_assert(pBox
->Back
<= This
->desc
.Depth
, D3DERR_INVALIDCALL
);
293 d3dbox_to_pipe_box(&box
, pBox
);
294 if (u_box_clip_2d(&box
, &box
, This
->desc
.Width
, This
->desc
.Height
) < 0) {
295 DBG("Locked volume intersection empty.\n");
296 return D3DERR_INVALIDCALL
;
299 u_box_3d(0, 0, 0, This
->desc
.Width
, This
->desc
.Height
, This
->desc
.Depth
,
303 if (This
->data_conversion
) {
304 /* For now we only have uncompressed formats here */
305 pLockedVolume
->RowPitch
= This
->stride_conversion
;
306 pLockedVolume
->SlicePitch
= This
->layer_stride_conversion
;
307 pLockedVolume
->pBits
= This
->data_conversion
+ box
.z
* This
->layer_stride_conversion
+
308 box
.y
* This
->stride_conversion
+
309 util_format_get_stride(This
->format_conversion
, box
.x
);
310 } else if (This
->data
) {
311 pLockedVolume
->RowPitch
= This
->stride
;
312 pLockedVolume
->SlicePitch
= This
->layer_stride
;
313 pLockedVolume
->pBits
=
314 NineVolume9_GetSystemMemPointer(This
, box
.x
, box
.y
, box
.z
);
316 pLockedVolume
->pBits
=
317 This
->pipe
->transfer_map(This
->pipe
, resource
, This
->level
, usage
,
318 &box
, &This
->transfer
);
319 if (!This
->transfer
) {
320 if (Flags
& D3DLOCK_DONOTWAIT
)
321 return D3DERR_WASSTILLDRAWING
;
322 return D3DERR_DRIVERINTERNALERROR
;
324 pLockedVolume
->RowPitch
= This
->transfer
->stride
;
325 pLockedVolume
->SlicePitch
= This
->transfer
->layer_stride
;
328 if (!(Flags
& (D3DLOCK_NO_DIRTY_UPDATE
| D3DLOCK_READONLY
))) {
329 NineVolume9_MarkContainerDirty(This
);
330 NineVolume9_AddDirtyRegion(This
, &box
);
338 NineVolume9_UnlockBox( struct NineVolume9
*This
)
340 DBG("This=%p lock_count=%u\n", This
, This
->lock_count
);
341 user_assert(This
->lock_count
, D3DERR_INVALIDCALL
);
342 if (This
->transfer
) {
343 This
->pipe
->transfer_unmap(This
->pipe
, This
->transfer
);
344 This
->transfer
= NULL
;
348 if (This
->data_conversion
) {
349 struct pipe_transfer
*transfer
;
350 uint8_t *dst
= This
->data
;
353 u_box_3d(0, 0, 0, This
->desc
.Width
, This
->desc
.Height
, This
->desc
.Depth
,
357 dst
= This
->pipe
->transfer_map(This
->pipe
,
360 PIPE_TRANSFER_WRITE
|
361 PIPE_TRANSFER_DISCARD_WHOLE_RESOURCE
,
367 (void) util_format_translate_3d(This
->info
.format
,
368 dst
, This
->data
? This
->stride
: transfer
->stride
,
369 This
->data
? This
->layer_stride
: transfer
->layer_stride
,
371 This
->format_conversion
,
372 This
->data_conversion
,
373 This
->stride_conversion
,
374 This
->layer_stride_conversion
,
376 This
->desc
.Width
, This
->desc
.Height
,
380 pipe_transfer_unmap(This
->pipe
, transfer
);
386 /* When this function is called, we have already checked
387 * The copy regions fit the volumes */
389 NineVolume9_CopyMemToDefault( struct NineVolume9
*This
,
390 struct NineVolume9
*From
,
391 unsigned dstx
, unsigned dsty
, unsigned dstz
,
392 struct pipe_box
*pSrcBox
)
394 struct pipe_context
*pipe
= This
->pipe
;
395 struct pipe_transfer
*transfer
= NULL
;
396 struct pipe_resource
*r_dst
= This
->resource
;
397 struct pipe_box src_box
;
398 struct pipe_box dst_box
;
401 DBG("This=%p From=%p dstx=%u dsty=%u dstz=%u pSrcBox=%p\n",
402 This
, From
, dstx
, dsty
, dstz
, pSrcBox
);
404 assert(This
->desc
.Pool
== D3DPOOL_DEFAULT
&&
405 From
->desc
.Pool
== D3DPOOL_SYSTEMMEM
);
417 src_box
.width
= From
->desc
.Width
;
418 src_box
.height
= From
->desc
.Height
;
419 src_box
.depth
= From
->desc
.Depth
;
422 dst_box
.width
= src_box
.width
;
423 dst_box
.height
= src_box
.height
;
424 dst_box
.depth
= src_box
.depth
;
426 map
= pipe
->transfer_map(pipe
,
429 PIPE_TRANSFER_WRITE
| PIPE_TRANSFER_DISCARD_RANGE
,
430 &dst_box
, &transfer
);
434 /* Note: if formats are the sames, it will revert
435 * to normal memcpy */
436 (void) util_format_translate_3d(r_dst
->format
,
437 map
, transfer
->stride
,
438 transfer
->layer_stride
,
441 From
->data
, From
->stride
,
443 src_box
.x
, src_box
.y
,
449 pipe_transfer_unmap(pipe
, transfer
);
451 if (This
->data_conversion
)
452 (void) util_format_translate_3d(This
->format_conversion
,
453 This
->data_conversion
,
454 This
->stride_conversion
,
455 This
->layer_stride_conversion
,
458 From
->data
, From
->stride
,
460 src_box
.x
, src_box
.y
,
466 NineVolume9_MarkContainerDirty(This
);
472 NineVolume9_UploadSelf( struct NineVolume9
*This
,
473 const struct pipe_box
*damaged
)
475 struct pipe_context
*pipe
= This
->pipe
;
476 struct pipe_resource
*res
= This
->resource
;
480 DBG("This=%p damaged=%p data=%p res=%p\n", This
, damaged
,
483 assert(This
->desc
.Pool
== D3DPOOL_MANAGED
);
492 box
.width
= This
->desc
.Width
;
493 box
.height
= This
->desc
.Height
;
494 box
.depth
= This
->desc
.Depth
;
497 ptr
= NineVolume9_GetSystemMemPointer(This
, box
.x
, box
.y
, box
.z
);
499 pipe
->transfer_inline_write(pipe
, res
, This
->level
, 0, &box
,
500 ptr
, This
->stride
, This
->layer_stride
);
506 IDirect3DVolume9Vtbl NineVolume9_vtable
= {
507 (void *)NineUnknown_QueryInterface
,
508 (void *)NineUnknown_AddRef
,
509 (void *)NineUnknown_Release
,
510 (void *)NineUnknown_GetDevice
, /* actually part of Volume9 iface */
511 (void *)NineVolume9_SetPrivateData
,
512 (void *)NineVolume9_GetPrivateData
,
513 (void *)NineVolume9_FreePrivateData
,
514 (void *)NineVolume9_GetContainer
,
515 (void *)NineVolume9_GetDesc
,
516 (void *)NineVolume9_LockBox
,
517 (void *)NineVolume9_UnlockBox
520 static const GUID
*NineVolume9_IIDs
[] = {
521 &IID_IDirect3DVolume9
,
527 NineVolume9_new( struct NineDevice9
*pDevice
,
528 struct NineUnknown
*pContainer
,
529 struct pipe_resource
*pResource
,
531 D3DVOLUME_DESC
*pDesc
,
532 struct NineVolume9
**ppOut
)
534 NINE_DEVICE_CHILD_NEW(Volume9
, ppOut
, pDevice
, /* args */
535 pContainer
, pResource
, Level
, pDesc
);
539 /*** The boring stuff. TODO: Unify with Resource. ***/
542 NineVolume9_SetPrivateData( struct NineVolume9
*This
,
549 struct pheader
*header
;
550 const void *user_data
= pData
;
552 DBG("This=%p refguid=%p pData=%p SizeOfData=%d Flags=%d\n",
553 This
, refguid
, pData
, SizeOfData
, Flags
);
555 if (Flags
& D3DSPD_IUNKNOWN
)
556 user_assert(SizeOfData
== sizeof(IUnknown
*), D3DERR_INVALIDCALL
);
558 /* data consists of a header and the actual data. avoiding 2 mallocs */
559 header
= CALLOC_VARIANT_LENGTH_STRUCT(pheader
, SizeOfData
-1);
560 if (!header
) { return E_OUTOFMEMORY
; }
561 header
->unknown
= (Flags
& D3DSPD_IUNKNOWN
) ? TRUE
: FALSE
;
563 /* if the refguid already exists, delete it */
564 NineVolume9_FreePrivateData(This
, refguid
);
566 /* IUnknown special case */
567 if (header
->unknown
) {
568 /* here the pointer doesn't point to the data we want, so point at the
569 * pointer making what we eventually copy is the pointer itself */
573 header
->size
= SizeOfData
;
574 memcpy(header
->data
, user_data
, header
->size
);
576 err
= util_hash_table_set(This
->pdata
, refguid
, header
);
577 if (err
== PIPE_OK
) {
578 if (header
->unknown
) { IUnknown_AddRef(*(IUnknown
**)header
->data
); }
583 if (err
== PIPE_ERROR_OUT_OF_MEMORY
) { return E_OUTOFMEMORY
; }
585 return D3DERR_DRIVERINTERNALERROR
;
589 NineVolume9_GetPrivateData( struct NineVolume9
*This
,
594 struct pheader
*header
;
596 user_assert(pSizeOfData
, E_POINTER
);
598 header
= util_hash_table_get(This
->pdata
, refguid
);
599 if (!header
) { return D3DERR_NOTFOUND
; }
602 *pSizeOfData
= header
->size
;
605 if (*pSizeOfData
< header
->size
) {
606 return D3DERR_MOREDATA
;
609 if (header
->unknown
) { IUnknown_AddRef(*(IUnknown
**)header
->data
); }
610 memcpy(pData
, header
->data
, header
->size
);
616 NineVolume9_FreePrivateData( struct NineVolume9
*This
,
619 struct pheader
*header
;
621 DBG("This=%p refguid=%p\n", This
, refguid
);
623 header
= util_hash_table_get(This
->pdata
, refguid
);
624 if (!header
) { return D3DERR_NOTFOUND
; }
626 ht_guid_delete(NULL
, header
, NULL
);
627 util_hash_table_remove(This
->pdata
, refguid
);