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 "nine_helpers.h"
27 #include "nine_pipe.h"
28 #include "nine_dump.h"
30 #include "util/u_hash_table.h"
31 #include "util/u_format.h"
32 #include "util/u_surface.h"
33 #include "nine_pdata.h"
35 #define DBG_CHANNEL DBG_VOLUME
39 NineVolume9_AllocateData( struct NineVolume9
*This
)
41 unsigned size
= This
->layer_stride
* This
->desc
.Depth
;
43 DBG("(%p(This=%p),level=%u) Allocating 0x%x bytes of system memory.\n",
44 This
->base
.container
, This
, This
->level
, size
);
46 This
->data
= (uint8_t *)MALLOC(size
);
53 NineVolume9_ctor( struct NineVolume9
*This
,
54 struct NineUnknownParams
*pParams
,
55 struct NineUnknown
*pContainer
,
56 struct pipe_resource
*pResource
,
58 D3DVOLUME_DESC
*pDesc
)
62 assert(pContainer
); /* stand-alone volumes can't be created */
64 DBG("This=%p pContainer=%p pDevice=%p pResource=%p Level=%u pDesc=%p\n",
65 This
, pContainer
, pParams
->device
, pResource
, Level
, pDesc
);
67 /* Mark this as a special surface held by another internal resource. */
68 pParams
->container
= pContainer
;
70 user_assert(!(pDesc
->Usage
& D3DUSAGE_DYNAMIC
) ||
71 (pDesc
->Pool
!= D3DPOOL_MANAGED
), D3DERR_INVALIDCALL
);
73 assert(pResource
|| pDesc
->Pool
!= D3DPOOL_DEFAULT
);
75 hr
= NineUnknown_ctor(&This
->base
, pParams
);
79 This
->pdata
= util_hash_table_create(ht_guid_hash
, ht_guid_compare
);
83 pipe_resource_reference(&This
->resource
, pResource
);
85 This
->pipe
= pParams
->device
->pipe
;
86 This
->transfer
= NULL
;
90 This
->level_actual
= Level
;
93 This
->info
.screen
= pParams
->device
->screen
;
94 This
->info
.target
= PIPE_TEXTURE_3D
;
95 This
->info
.width0
= pDesc
->Width
;
96 This
->info
.height0
= pDesc
->Height
;
97 This
->info
.depth0
= pDesc
->Depth
;
98 This
->info
.last_level
= 0;
99 This
->info
.array_size
= 1;
100 This
->info
.nr_samples
= 0;
101 This
->info
.usage
= PIPE_USAGE_DEFAULT
;
102 This
->info
.bind
= PIPE_BIND_SAMPLER_VIEW
;
103 This
->info
.flags
= 0;
104 This
->info
.format
= d3d9_to_pipe_format_checked(This
->info
.screen
,
107 This
->info
.nr_samples
,
108 This
->info
.bind
, FALSE
);
110 if (This
->info
.format
== PIPE_FORMAT_NONE
)
111 return D3DERR_DRIVERINTERNALERROR
;
113 This
->stride
= util_format_get_stride(This
->info
.format
, pDesc
->Width
);
114 This
->stride
= align(This
->stride
, 4);
115 This
->layer_stride
= util_format_get_2d_size(This
->info
.format
,
116 This
->stride
, pDesc
->Height
);
118 if (pDesc
->Pool
== D3DPOOL_SYSTEMMEM
)
119 This
->info
.usage
= PIPE_USAGE_STAGING
;
121 if (!This
->resource
) {
122 hr
= NineVolume9_AllocateData(This
);
130 NineVolume9_dtor( struct NineVolume9
*This
)
132 DBG("This=%p\n", This
);
135 NineVolume9_UnlockBox(This
);
140 pipe_resource_reference(&This
->resource
, NULL
);
142 NineUnknown_dtor(&This
->base
);
146 NineVolume9_GetContainer( struct NineVolume9
*This
,
150 if (!NineUnknown(This
)->container
)
151 return E_NOINTERFACE
;
152 return NineUnknown_QueryInterface(NineUnknown(This
)->container
, riid
, ppContainer
);
156 NineVolume9_MarkContainerDirty( struct NineVolume9
*This
)
158 struct NineBaseTexture9
*tex
;
160 /* This is always contained by a NineVolumeTexture9. */
161 GUID id
= IID_IDirect3DVolumeTexture9
;
163 assert(NineUnknown_QueryInterface(This
->base
.container
, ref
, (void **)&tex
)
165 assert(NineUnknown_Release(NineUnknown(tex
)) != 0);
168 tex
= NineBaseTexture9(This
->base
.container
);
170 if (This
->desc
.Pool
== D3DPOOL_MANAGED
)
173 if (This
->desc
.Usage
& D3DUSAGE_AUTOGENMIPMAP
)
174 tex
->dirty_mip
= TRUE
;
176 BASETEX_REGISTER_UPDATE(tex
);
180 NineVolume9_GetDesc( struct NineVolume9
*This
,
181 D3DVOLUME_DESC
*pDesc
)
183 user_assert(pDesc
!= NULL
, E_POINTER
);
188 static INLINE boolean
189 NineVolume9_IsDirty(struct NineVolume9
*This
)
191 return This
->dirty_box
[0].width
!= 0;
195 NineVolume9_AddDirtyRegion( struct NineVolume9
*This
,
196 const struct pipe_box
*box
)
198 struct pipe_box cover_a
, cover_b
;
202 u_box_3d(0, 0, 0, This
->desc
.Width
, This
->desc
.Height
,
203 This
->desc
.Depth
, &This
->dirty_box
[0]);
204 memset(&This
->dirty_box
[1], 0, sizeof(This
->dirty_box
[1]));
207 if (!This
->dirty_box
[0].width
) {
208 This
->dirty_box
[0] = *box
;
212 u_box_union_3d(&cover_a
, &This
->dirty_box
[0], box
);
213 vol
[0] = u_box_volume_3d(&cover_a
);
215 if (This
->dirty_box
[1].width
== 0) {
216 vol
[1] = u_box_volume_3d(&This
->dirty_box
[0]);
217 if (vol
[0] > (vol
[1] * 1.5f
))
218 This
->dirty_box
[1] = *box
;
220 This
->dirty_box
[0] = cover_a
;
222 u_box_union_3d(&cover_b
, &This
->dirty_box
[1], box
);
223 vol
[1] = u_box_volume_3d(&cover_b
);
226 This
->dirty_box
[1] = cover_b
;
228 This
->dirty_box
[0] = cover_a
;
232 static INLINE
uint8_t *
233 NineVolume9_GetSystemMemPointer(struct NineVolume9
*This
, int x
, int y
, int z
)
235 unsigned x_offset
= util_format_get_stride(This
->info
.format
, x
);
237 y
= util_format_get_nblocksy(This
->info
.format
, y
);
240 return This
->data
+ (z
* This
->layer_stride
+ y
* This
->stride
+ x_offset
);
244 NineVolume9_LockBox( struct NineVolume9
*This
,
245 D3DLOCKED_BOX
*pLockedVolume
,
249 struct pipe_resource
*resource
= This
->resource
;
253 DBG("This=%p(%p) pLockedVolume=%p pBox=%p[%u..%u,%u..%u,%u..%u] Flags=%s\n",
254 This
, This
->base
.container
, pLockedVolume
, pBox
,
255 pBox
? pBox
->Left
: 0, pBox
? pBox
->Right
: 0,
256 pBox
? pBox
->Top
: 0, pBox
? pBox
->Bottom
: 0,
257 pBox
? pBox
->Front
: 0, pBox
? pBox
->Back
: 0,
258 nine_D3DLOCK_to_str(Flags
));
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 user_assert(This
->lock_count
== 0, D3DERR_INVALIDCALL
);
267 user_assert(pLockedVolume
, E_POINTER
);
269 if (pBox
&& This
->desc
.Pool
== D3DPOOL_DEFAULT
&&
270 util_format_is_compressed(This
->info
.format
)) {
271 const unsigned w
= util_format_get_blockwidth(This
->info
.format
);
272 const unsigned h
= util_format_get_blockheight(This
->info
.format
);
273 user_assert(!(pBox
->Left
% w
) && !(pBox
->Right
% w
) &&
274 !(pBox
->Top
% h
) && !(pBox
->Bottom
% h
),
278 if (Flags
& D3DLOCK_DISCARD
) {
279 usage
= PIPE_TRANSFER_WRITE
| PIPE_TRANSFER_DISCARD_RANGE
;
281 usage
= (Flags
& D3DLOCK_READONLY
) ?
282 PIPE_TRANSFER_READ
: PIPE_TRANSFER_READ_WRITE
;
284 if (Flags
& D3DLOCK_DONOTWAIT
)
285 usage
|= PIPE_TRANSFER_DONTBLOCK
;
288 d3dbox_to_pipe_box(&box
, pBox
);
289 if (u_box_clip_2d(&box
, &box
, This
->desc
.Width
, This
->desc
.Height
) < 0) {
290 DBG("Locked volume intersection empty.\n");
291 return D3DERR_INVALIDCALL
;
294 u_box_3d(0, 0, 0, This
->desc
.Width
, This
->desc
.Height
, This
->desc
.Depth
,
299 pLockedVolume
->RowPitch
= This
->stride
;
300 pLockedVolume
->SlicePitch
= This
->layer_stride
;
301 pLockedVolume
->pBits
=
302 NineVolume9_GetSystemMemPointer(This
, box
.x
, box
.y
, box
.z
);
304 pLockedVolume
->pBits
=
305 This
->pipe
->transfer_map(This
->pipe
, resource
, This
->level
, usage
,
306 &box
, &This
->transfer
);
307 if (!This
->transfer
) {
308 if (Flags
& D3DLOCK_DONOTWAIT
)
309 return D3DERR_WASSTILLDRAWING
;
310 return D3DERR_DRIVERINTERNALERROR
;
312 pLockedVolume
->RowPitch
= This
->transfer
->stride
;
313 pLockedVolume
->SlicePitch
= This
->transfer
->layer_stride
;
316 if (!(Flags
& (D3DLOCK_NO_DIRTY_UPDATE
| D3DLOCK_READONLY
))) {
317 NineVolume9_MarkContainerDirty(This
);
318 if (This
->desc
.Pool
== D3DPOOL_MANAGED
)
319 NineVolume9_AddDirtyRegion(This
, &box
);
327 NineVolume9_UnlockBox( struct NineVolume9
*This
)
329 DBG("This=%p lock_count=%u\n", This
, This
->lock_count
);
330 user_assert(This
->lock_count
, D3DERR_INVALIDCALL
);
331 if (This
->transfer
) {
332 This
->pipe
->transfer_unmap(This
->pipe
, This
->transfer
);
333 This
->transfer
= NULL
;
341 NineVolume9_CopyVolume( struct NineVolume9
*This
,
342 struct NineVolume9
*From
,
343 unsigned dstx
, unsigned dsty
, unsigned dstz
,
344 struct pipe_box
*pSrcBox
)
346 struct pipe_context
*pipe
= This
->pipe
;
347 struct pipe_resource
*r_dst
= This
->resource
;
348 struct pipe_resource
*r_src
= From
->resource
;
349 struct pipe_transfer
*transfer
;
350 struct pipe_box src_box
;
351 struct pipe_box dst_box
;
353 const uint8_t *p_src
;
355 DBG("This=%p From=%p dstx=%u dsty=%u dstz=%u pSrcBox=%p\n",
356 This
, From
, dstx
, dsty
, dstz
, pSrcBox
);
358 user_assert(This
->desc
.Format
== From
->desc
.Format
, D3DERR_INVALIDCALL
);
365 /* make sure it doesn't range outside the source volume */
366 user_assert(pSrcBox
->x
>= 0 &&
367 (pSrcBox
->width
- pSrcBox
->x
) <= From
->desc
.Width
&&
369 (pSrcBox
->height
- pSrcBox
->y
) <= From
->desc
.Height
&&
371 (pSrcBox
->depth
- pSrcBox
->z
) <= From
->desc
.Depth
,
378 src_box
.width
= From
->desc
.Width
;
379 src_box
.height
= From
->desc
.Height
;
380 src_box
.depth
= From
->desc
.Depth
;
383 dst_box
.width
= This
->desc
.Width
- dst_box
.x
;
384 dst_box
.height
= This
->desc
.Height
- dst_box
.y
;
385 dst_box
.depth
= This
->desc
.Depth
- dst_box
.z
;
387 user_assert(src_box
.width
<= dst_box
.width
&&
388 src_box
.height
<= dst_box
.height
&&
389 src_box
.depth
<= dst_box
.depth
, D3DERR_INVALIDCALL
);
391 dst_box
.width
= src_box
.width
;
392 dst_box
.height
= src_box
.height
;
393 dst_box
.depth
= src_box
.depth
;
395 /* Don't copy to device memory of managed resources.
396 * We don't want to download it back again later.
398 if (This
->desc
.Pool
== D3DPOOL_MANAGED
)
401 /* Don't copy from stale device memory of managed resources.
402 * Also, don't copy between system and device if we don't have to.
404 if (From
->desc
.Pool
== D3DPOOL_MANAGED
) {
405 if (!r_dst
|| NineVolume9_IsDirty(From
))
409 if (r_dst
&& r_src
) {
410 pipe
->resource_copy_region(pipe
,
412 dst_box
.x
, dst_box
.y
, dst_box
.z
,
417 p_src
= NineVolume9_GetSystemMemPointer(From
,
418 src_box
.x
, src_box
.y
, src_box
.z
);
420 pipe
->transfer_inline_write(pipe
, r_dst
, This
->level
,
421 0, /* WRITE|DISCARD are implicit */
423 From
->stride
, From
->layer_stride
);
426 p_dst
= NineVolume9_GetSystemMemPointer(This
, 0, 0, 0);
427 p_src
= pipe
->transfer_map(pipe
, r_src
, From
->level
,
429 &src_box
, &transfer
);
431 return D3DERR_DRIVERINTERNALERROR
;
433 util_copy_box(p_dst
, This
->info
.format
,
434 This
->stride
, This
->layer_stride
,
435 dst_box
.x
, dst_box
.y
, dst_box
.z
,
436 dst_box
.width
, dst_box
.height
, dst_box
.depth
,
438 transfer
->stride
, transfer
->layer_stride
,
439 src_box
.x
, src_box
.y
, src_box
.z
);
441 pipe
->transfer_unmap(pipe
, transfer
);
443 p_dst
= NineVolume9_GetSystemMemPointer(This
, 0, 0, 0);
444 p_src
= NineVolume9_GetSystemMemPointer(From
, 0, 0, 0);
446 util_copy_box(p_dst
, This
->info
.format
,
447 This
->stride
, This
->layer_stride
,
448 dst_box
.x
, dst_box
.y
, dst_box
.z
,
449 dst_box
.width
, dst_box
.height
, dst_box
.depth
,
451 From
->stride
, From
->layer_stride
,
452 src_box
.x
, src_box
.y
, src_box
.z
);
455 if (This
->desc
.Pool
== D3DPOOL_DEFAULT
||
456 This
->desc
.Pool
== D3DPOOL_MANAGED
)
457 NineVolume9_MarkContainerDirty(This
);
458 if (!r_dst
&& This
->resource
)
459 NineVolume9_AddDirtyRegion(This
, &dst_box
);
465 NineVolume9_UploadSelf( struct NineVolume9
*This
)
467 struct pipe_context
*pipe
= This
->pipe
;
468 struct pipe_resource
*res
= This
->resource
;
472 DBG("This=%p dirty=%i data=%p res=%p\n", This
, NineVolume9_IsDirty(This
),
475 assert(This
->desc
.Pool
== D3DPOOL_MANAGED
);
477 if (!NineVolume9_IsDirty(This
))
481 for (i
= 0; i
< Elements(This
->dirty_box
); ++i
) {
482 const struct pipe_box
*box
= &This
->dirty_box
[i
];
485 ptr
= NineVolume9_GetSystemMemPointer(This
, box
->x
, box
->y
, box
->z
);
487 pipe
->transfer_inline_write(pipe
, res
, This
->level
,
489 box
, ptr
, This
->stride
, This
->layer_stride
);
491 NineVolume9_ClearDirtyRegion(This
);
497 IDirect3DVolume9Vtbl NineVolume9_vtable
= {
498 (void *)NineUnknown_QueryInterface
,
499 (void *)NineUnknown_AddRef
,
500 (void *)NineUnknown_Release
,
501 (void *)NineUnknown_GetDevice
, /* actually part of Volume9 iface */
502 (void *)NineVolume9_SetPrivateData
,
503 (void *)NineVolume9_GetPrivateData
,
504 (void *)NineVolume9_FreePrivateData
,
505 (void *)NineVolume9_GetContainer
,
506 (void *)NineVolume9_GetDesc
,
507 (void *)NineVolume9_LockBox
,
508 (void *)NineVolume9_UnlockBox
511 static const GUID
*NineVolume9_IIDs
[] = {
512 &IID_IDirect3DVolume9
,
518 NineVolume9_new( struct NineDevice9
*pDevice
,
519 struct NineUnknown
*pContainer
,
520 struct pipe_resource
*pResource
,
522 D3DVOLUME_DESC
*pDesc
,
523 struct NineVolume9
**ppOut
)
525 NINE_DEVICE_CHILD_NEW(Volume9
, ppOut
, pDevice
, /* args */
526 pContainer
, pResource
, Level
, pDesc
);
530 /*** The boring stuff. TODO: Unify with Resource. ***/
533 NineVolume9_SetPrivateData( struct NineVolume9
*This
,
540 struct pheader
*header
;
541 const void *user_data
= pData
;
543 DBG("This=%p refguid=%p pData=%p SizeOfData=%d Flags=%d\n",
544 This
, refguid
, pData
, SizeOfData
, Flags
);
546 if (Flags
& D3DSPD_IUNKNOWN
)
547 user_assert(SizeOfData
== sizeof(IUnknown
*), D3DERR_INVALIDCALL
);
549 /* data consists of a header and the actual data. avoiding 2 mallocs */
550 header
= CALLOC_VARIANT_LENGTH_STRUCT(pheader
, SizeOfData
-1);
551 if (!header
) { return E_OUTOFMEMORY
; }
552 header
->unknown
= (Flags
& D3DSPD_IUNKNOWN
) ? TRUE
: FALSE
;
554 /* if the refguid already exists, delete it */
555 NineVolume9_FreePrivateData(This
, refguid
);
557 /* IUnknown special case */
558 if (header
->unknown
) {
559 /* here the pointer doesn't point to the data we want, so point at the
560 * pointer making what we eventually copy is the pointer itself */
564 header
->size
= SizeOfData
;
565 memcpy(header
->data
, user_data
, header
->size
);
567 err
= util_hash_table_set(This
->pdata
, refguid
, header
);
568 if (err
== PIPE_OK
) {
569 if (header
->unknown
) { IUnknown_AddRef(*(IUnknown
**)header
->data
); }
574 if (err
== PIPE_ERROR_OUT_OF_MEMORY
) { return E_OUTOFMEMORY
; }
576 return D3DERR_DRIVERINTERNALERROR
;
580 NineVolume9_GetPrivateData( struct NineVolume9
*This
,
585 struct pheader
*header
;
587 user_assert(pSizeOfData
, E_POINTER
);
589 header
= util_hash_table_get(This
->pdata
, refguid
);
590 if (!header
) { return D3DERR_NOTFOUND
; }
593 *pSizeOfData
= header
->size
;
596 if (*pSizeOfData
< header
->size
) {
597 return D3DERR_MOREDATA
;
600 if (header
->unknown
) { IUnknown_AddRef(*(IUnknown
**)header
->data
); }
601 memcpy(pData
, header
->data
, header
->size
);
607 NineVolume9_FreePrivateData( struct NineVolume9
*This
,
610 struct pheader
*header
;
612 DBG("This=%p refguid=%p\n", This
, refguid
);
614 header
= util_hash_table_get(This
->pdata
, refguid
);
615 if (!header
) { return D3DERR_NOTFOUND
; }
617 ht_guid_delete(NULL
, header
, NULL
);
618 util_hash_table_remove(This
->pdata
, refguid
);