diff options
Diffstat (limited to 'src/gallium/state_trackers/nine/volume9.c')
-rw-r--r-- | src/gallium/state_trackers/nine/volume9.c | 604 |
1 files changed, 604 insertions, 0 deletions
diff --git a/src/gallium/state_trackers/nine/volume9.c b/src/gallium/state_trackers/nine/volume9.c new file mode 100644 index 00000000000..c46a9d1c2e0 --- /dev/null +++ b/src/gallium/state_trackers/nine/volume9.c @@ -0,0 +1,604 @@ +/* + * Copyright 2011 Joakim Sindholt <[email protected]> + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * on the rights to use, copy, modify, merge, publish, distribute, sub + * license, and/or sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +#include "device9.h" +#include "volume9.h" +#include "basetexture9.h" /* for marking dirty */ +#include "nine_helpers.h" +#include "nine_pipe.h" +#include "nine_dump.h" + +#include "util/u_hash_table.h" +#include "util/u_format.h" +#include "util/u_surface.h" +#include "nine_pdata.h" + +#define DBG_CHANNEL DBG_VOLUME + + +static HRESULT +NineVolume9_AllocateData( struct NineVolume9 *This ) +{ + unsigned size = This->layer_stride * This->desc.Depth; + + DBG("(%p(This=%p),level=%u) Allocating 0x%x bytes of system memory.\n", + This->base.container, This, This->level, size); + + This->data = (uint8_t *)MALLOC(size); + if (!This->data) + return E_OUTOFMEMORY; + return D3D_OK; +} + +static HRESULT +NineVolume9_ctor( struct NineVolume9 *This, + struct NineUnknownParams *pParams, + struct NineUnknown *pContainer, + struct pipe_resource *pResource, + unsigned Level, + D3DVOLUME_DESC *pDesc ) +{ + HRESULT hr; + + assert(pContainer); /* stand-alone volumes can't be created */ + + DBG("This=%p pContainer=%p pDevice=%p pResource=%p Level=%u pDesc=%p\n", + This, pContainer, pParams->device, pResource, Level, pDesc); + + /* Mark this as a special surface held by another internal resource. */ + pParams->container = pContainer; + + user_assert(!(pDesc->Usage & D3DUSAGE_DYNAMIC) || + (pDesc->Pool != D3DPOOL_MANAGED), D3DERR_INVALIDCALL); + + assert(pResource || pDesc->Pool != D3DPOOL_DEFAULT); + + hr = NineUnknown_ctor(&This->base, pParams); + if (FAILED(hr)) + return hr; + + This->pdata = util_hash_table_create(ht_guid_hash, ht_guid_compare); + if (!This->pdata) + return E_OUTOFMEMORY; + + pipe_resource_reference(&This->resource, pResource); + + This->pipe = pParams->device->pipe; + This->transfer = NULL; + This->lock_count = 0; + + This->level = Level; + This->level_actual = Level; + This->desc = *pDesc; + + This->info.screen = pParams->device->screen; + This->info.target = PIPE_TEXTURE_3D; + This->info.format = d3d9_to_pipe_format(pDesc->Format); + This->info.width0 = pDesc->Width; + This->info.height0 = pDesc->Height; + This->info.depth0 = pDesc->Depth; + This->info.last_level = 0; + This->info.array_size = 1; + This->info.nr_samples = 0; + This->info.usage = PIPE_USAGE_DEFAULT; + This->info.bind = PIPE_BIND_SAMPLER_VIEW; + This->info.flags = 0; + + This->stride = util_format_get_stride(This->info.format, pDesc->Width); + This->stride = align(This->stride, 4); + This->layer_stride = util_format_get_2d_size(This->info.format, + This->stride, pDesc->Height); + + if (pDesc->Pool == D3DPOOL_SYSTEMMEM) + This->info.usage = PIPE_USAGE_STAGING; + + if (!This->resource) { + hr = NineVolume9_AllocateData(This); + if (FAILED(hr)) + return hr; + } + return D3D_OK; +} + +static void +NineVolume9_dtor( struct NineVolume9 *This ) +{ + DBG("This=%p\n", This); + + if (This->transfer) + NineVolume9_UnlockBox(This); + + pipe_resource_reference(&This->resource, NULL); + + NineUnknown_dtor(&This->base); +} + +HRESULT WINAPI +NineVolume9_GetContainer( struct NineVolume9 *This, + REFIID riid, + void **ppContainer ) +{ + if (!NineUnknown(This)->container) + return E_NOINTERFACE; + return NineUnknown_QueryInterface(NineUnknown(This)->container, riid, ppContainer); +} + +static INLINE void +NineVolume9_MarkContainerDirty( struct NineVolume9 *This ) +{ + struct NineBaseTexture9 *tex; +#ifdef DEBUG + /* This is always contained by a NineVolumeTexture9. */ + GUID id = IID_IDirect3DVolumeTexture9; + REFIID ref = &id; + assert(NineUnknown_QueryInterface(This->base.container, ref, (void **)&tex) + == S_OK); + assert(NineUnknown_Release(NineUnknown(tex)) != 0); +#endif + + tex = NineBaseTexture9(This->base.container); + assert(tex); + if (This->desc.Pool == D3DPOOL_MANAGED) + tex->dirty = TRUE; + else + if (This->desc.Usage & D3DUSAGE_AUTOGENMIPMAP) + tex->dirty_mip = TRUE; + + BASETEX_REGISTER_UPDATE(tex); +} + +HRESULT WINAPI +NineVolume9_GetDesc( struct NineVolume9 *This, + D3DVOLUME_DESC *pDesc ) +{ + user_assert(pDesc != NULL, E_POINTER); + *pDesc = This->desc; + return D3D_OK; +} + +static INLINE boolean +NineVolume9_IsDirty(struct NineVolume9 *This) +{ + return This->dirty_box[0].width != 0; +} + +INLINE void +NineVolume9_AddDirtyRegion( struct NineVolume9 *This, + const struct pipe_box *box ) +{ + struct pipe_box cover_a, cover_b; + float vol[2]; + + if (!box) { + u_box_3d(0, 0, 0, This->desc.Width, This->desc.Height, + This->desc.Depth, &This->dirty_box[0]); + memset(&This->dirty_box[1], 0, sizeof(This->dirty_box[1])); + return; + } + if (!This->dirty_box[0].width) { + This->dirty_box[0] = *box; + return; + } + + u_box_union_3d(&cover_a, &This->dirty_box[0], box); + vol[0] = u_box_volume_3d(&cover_a); + + if (This->dirty_box[1].width == 0) { + vol[1] = u_box_volume_3d(&This->dirty_box[0]); + if (vol[0] > (vol[1] * 1.5f)) + This->dirty_box[1] = *box; + else + This->dirty_box[0] = cover_a; + } else { + u_box_union_3d(&cover_b, &This->dirty_box[1], box); + vol[1] = u_box_volume_3d(&cover_b); + + if (vol[0] > vol[1]) + This->dirty_box[1] = cover_b; + else + This->dirty_box[0] = cover_a; + } +} + +static INLINE uint8_t * +NineVolume9_GetSystemMemPointer(struct NineVolume9 *This, int x, int y, int z) +{ + unsigned x_offset = util_format_get_stride(This->info.format, x); + + y = util_format_get_nblocksy(This->info.format, y); + + assert(This->data); + return This->data + (z * This->layer_stride + y * This->stride + x_offset); +} + +HRESULT WINAPI +NineVolume9_LockBox( struct NineVolume9 *This, + D3DLOCKED_BOX *pLockedVolume, + const D3DBOX *pBox, + DWORD Flags ) +{ + struct pipe_resource *resource = This->resource; + struct pipe_box box; + unsigned usage; + + DBG("This=%p(%p) pLockedVolume=%p pBox=%p[%u..%u,%u..%u,%u..%u] Flags=%s\n", + This, This->base.container, pLockedVolume, pBox, + pBox ? pBox->Left : 0, pBox ? pBox->Right : 0, + pBox ? pBox->Top : 0, pBox ? pBox->Bottom : 0, + pBox ? pBox->Front : 0, pBox ? pBox->Back : 0, + nine_D3DLOCK_to_str(Flags)); + + user_assert(This->desc.Pool != D3DPOOL_DEFAULT || + (This->desc.Usage & D3DUSAGE_DYNAMIC), D3DERR_INVALIDCALL); + + user_assert(!((Flags & D3DLOCK_DISCARD) && (Flags & D3DLOCK_READONLY)), + D3DERR_INVALIDCALL); + + user_assert(This->lock_count == 0, D3DERR_INVALIDCALL); + user_assert(pLockedVolume, E_POINTER); + + if (pBox && This->desc.Pool == D3DPOOL_DEFAULT && + util_format_is_compressed(This->info.format)) { + const unsigned w = util_format_get_blockwidth(This->info.format); + const unsigned h = util_format_get_blockheight(This->info.format); + user_assert(!(pBox->Left % w) && !(pBox->Right % w) && + !(pBox->Top % h) && !(pBox->Bottom % h), + D3DERR_INVALIDCALL); + } + + if (Flags & D3DLOCK_DISCARD) { + usage = PIPE_TRANSFER_WRITE | PIPE_TRANSFER_DISCARD_RANGE; + } else { + usage = (Flags & D3DLOCK_READONLY) ? + PIPE_TRANSFER_READ : PIPE_TRANSFER_READ_WRITE; + } + if (Flags & D3DLOCK_DONOTWAIT) + usage |= PIPE_TRANSFER_DONTBLOCK; + + if (pBox) { + d3dbox_to_pipe_box(&box, pBox); + if (u_box_clip_2d(&box, &box, This->desc.Width, This->desc.Height) < 0) { + DBG("Locked volume intersection empty.\n"); + return D3DERR_INVALIDCALL; + } + } else { + u_box_3d(0, 0, 0, This->desc.Width, This->desc.Height, This->desc.Depth, + &box); + } + + if (This->data) { + pLockedVolume->RowPitch = This->stride; + pLockedVolume->SlicePitch = This->layer_stride; + pLockedVolume->pBits = + NineVolume9_GetSystemMemPointer(This, box.x, box.y, box.z); + } else { + pLockedVolume->pBits = + This->pipe->transfer_map(This->pipe, resource, This->level, usage, + &box, &This->transfer); + if (!This->transfer) { + if (Flags & D3DLOCK_DONOTWAIT) + return D3DERR_WASSTILLDRAWING; + return D3DERR_DRIVERINTERNALERROR; + } + pLockedVolume->RowPitch = This->transfer->stride; + pLockedVolume->SlicePitch = This->transfer->layer_stride; + } + + if (!(Flags & (D3DLOCK_NO_DIRTY_UPDATE | D3DLOCK_READONLY))) { + NineVolume9_MarkContainerDirty(This); + if (This->desc.Pool == D3DPOOL_MANAGED) + NineVolume9_AddDirtyRegion(This, &box); + } + + ++This->lock_count; + return D3D_OK; +} + +HRESULT WINAPI +NineVolume9_UnlockBox( struct NineVolume9 *This ) +{ + DBG("This=%p lock_count=%u\n", This, This->lock_count); + user_assert(This->lock_count, D3DERR_INVALIDCALL); + if (This->transfer) { + This->pipe->transfer_unmap(This->pipe, This->transfer); + This->transfer = NULL; + } + --This->lock_count; + return D3D_OK; +} + + +HRESULT +NineVolume9_CopyVolume( struct NineVolume9 *This, + struct NineVolume9 *From, + unsigned dstx, unsigned dsty, unsigned dstz, + struct pipe_box *pSrcBox ) +{ + struct pipe_context *pipe = This->pipe; + struct pipe_resource *r_dst = This->resource; + struct pipe_resource *r_src = From->resource; + struct pipe_transfer *transfer; + struct pipe_box src_box; + struct pipe_box dst_box; + uint8_t *p_dst; + const uint8_t *p_src; + + user_assert(This->desc.Format == From->desc.Format, D3DERR_INVALIDCALL); + + dst_box.x = dstx; + dst_box.y = dsty; + dst_box.z = dstz; + + if (pSrcBox) { + /* make sure it doesn't range outside the source volume */ + user_assert(pSrcBox->x >= 0 && + (pSrcBox->width - pSrcBox->x) <= From->desc.Width && + pSrcBox->y >= 0 && + (pSrcBox->height - pSrcBox->y) <= From->desc.Height && + pSrcBox->z >= 0 && + (pSrcBox->depth - pSrcBox->z) <= From->desc.Depth, + D3DERR_INVALIDCALL); + src_box = *pSrcBox; + } else { + src_box.x = 0; + src_box.y = 0; + src_box.z = 0; + src_box.width = From->desc.Width; + src_box.height = From->desc.Height; + src_box.depth = From->desc.Depth; + } + /* limits */ + dst_box.width = This->desc.Width - dst_box.x; + dst_box.height = This->desc.Height - dst_box.y; + dst_box.depth = This->desc.Depth - dst_box.z; + + user_assert(src_box.width <= dst_box.width && + src_box.height <= dst_box.height && + src_box.depth <= dst_box.depth, D3DERR_INVALIDCALL); + + dst_box.width = src_box.width; + dst_box.height = src_box.height; + dst_box.depth = src_box.depth; + + /* Don't copy to device memory of managed resources. + * We don't want to download it back again later. + */ + if (This->desc.Pool == D3DPOOL_MANAGED) + r_dst = NULL; + + /* Don't copy from stale device memory of managed resources. + * Also, don't copy between system and device if we don't have to. + */ + if (From->desc.Pool == D3DPOOL_MANAGED) { + if (!r_dst || NineVolume9_IsDirty(From)) + r_src = NULL; + } + + if (r_dst && r_src) { + pipe->resource_copy_region(pipe, + r_dst, This->level, + dst_box.x, dst_box.y, dst_box.z, + r_src, From->level, + &src_box); + } else + if (r_dst) { + p_src = NineVolume9_GetSystemMemPointer(From, + src_box.x, src_box.y, src_box.z); + + pipe->transfer_inline_write(pipe, r_dst, This->level, + 0, /* WRITE|DISCARD are implicit */ + &dst_box, p_src, + From->stride, From->layer_stride); + } else + if (r_src) { + p_dst = NineVolume9_GetSystemMemPointer(This, 0, 0, 0); + p_src = pipe->transfer_map(pipe, r_src, From->level, + PIPE_TRANSFER_READ, + &src_box, &transfer); + if (!p_src) + return D3DERR_DRIVERINTERNALERROR; + + util_copy_box(p_dst, This->info.format, + This->stride, This->layer_stride, + dst_box.x, dst_box.y, dst_box.z, + dst_box.width, dst_box.height, dst_box.depth, + p_src, + transfer->stride, transfer->layer_stride, + src_box.x, src_box.y, src_box.z); + + pipe->transfer_unmap(pipe, transfer); + } else { + p_dst = NineVolume9_GetSystemMemPointer(This, 0, 0, 0); + p_src = NineVolume9_GetSystemMemPointer(From, 0, 0, 0); + + util_copy_box(p_dst, This->info.format, + This->stride, This->layer_stride, + dst_box.x, dst_box.y, dst_box.z, + dst_box.width, dst_box.height, dst_box.depth, + p_src, + From->stride, From->layer_stride, + src_box.x, src_box.y, src_box.z); + } + + if (This->desc.Pool == D3DPOOL_DEFAULT || + This->desc.Pool == D3DPOOL_MANAGED) + NineVolume9_MarkContainerDirty(This); + if (!r_dst && This->resource) + NineVolume9_AddDirtyRegion(This, &dst_box); + + return D3D_OK; +} + +HRESULT +NineVolume9_UploadSelf( struct NineVolume9 *This ) +{ + struct pipe_context *pipe = This->pipe; + struct pipe_resource *res = This->resource; + uint8_t *ptr; + unsigned i; + + DBG("This=%p dirty=%i data=%p res=%p\n", This, NineVolume9_IsDirty(This), + This->data, res); + + assert(This->desc.Pool == D3DPOOL_MANAGED); + + if (!NineVolume9_IsDirty(This)) + return D3D_OK; + assert(res); + + for (i = 0; i < Elements(This->dirty_box); ++i) { + const struct pipe_box *box = &This->dirty_box[i]; + if (box->width == 0) + break; + ptr = NineVolume9_GetSystemMemPointer(This, box->x, box->y, box->z); + + pipe->transfer_inline_write(pipe, res, This->level, + 0, + box, ptr, This->stride, This->layer_stride); + } + NineVolume9_ClearDirtyRegion(This); + + return D3D_OK; +} + + +IDirect3DVolume9Vtbl NineVolume9_vtable = { + (void *)NineUnknown_QueryInterface, + (void *)NineUnknown_AddRef, + (void *)NineUnknown_Release, + (void *)NineUnknown_GetDevice, /* actually part of Volume9 iface */ + (void *)NineVolume9_SetPrivateData, + (void *)NineVolume9_GetPrivateData, + (void *)NineVolume9_FreePrivateData, + (void *)NineVolume9_GetContainer, + (void *)NineVolume9_GetDesc, + (void *)NineVolume9_LockBox, + (void *)NineVolume9_UnlockBox +}; + +static const GUID *NineVolume9_IIDs[] = { + &IID_IDirect3DVolume9, + &IID_IUnknown, + NULL +}; + +HRESULT +NineVolume9_new( struct NineDevice9 *pDevice, + struct NineUnknown *pContainer, + struct pipe_resource *pResource, + unsigned Level, + D3DVOLUME_DESC *pDesc, + struct NineVolume9 **ppOut ) +{ + NINE_DEVICE_CHILD_NEW(Volume9, ppOut, pDevice, /* args */ + pContainer, pResource, Level, pDesc); +} + + +/*** The boring stuff. TODO: Unify with Resource. ***/ + +HRESULT WINAPI +NineVolume9_SetPrivateData( struct NineVolume9 *This, + REFGUID refguid, + const void *pData, + DWORD SizeOfData, + DWORD Flags ) +{ + enum pipe_error err; + struct pheader *header; + const void *user_data = pData; + + if (Flags & D3DSPD_IUNKNOWN) + user_assert(SizeOfData == sizeof(IUnknown *), D3DERR_INVALIDCALL); + + /* data consists of a header and the actual data. avoiding 2 mallocs */ + header = CALLOC_VARIANT_LENGTH_STRUCT(pheader, SizeOfData-1); + if (!header) { return E_OUTOFMEMORY; } + header->unknown = (Flags & D3DSPD_IUNKNOWN) ? TRUE : FALSE; + + /* if the refguid already exists, delete it */ + NineVolume9_FreePrivateData(This, refguid); + + /* IUnknown special case */ + if (header->unknown) { + /* here the pointer doesn't point to the data we want, so point at the + * pointer making what we eventually copy is the pointer itself */ + user_data = &pData; + } + + header->size = SizeOfData; + memcpy(header->data, user_data, header->size); + + err = util_hash_table_set(This->pdata, refguid, header); + if (err == PIPE_OK) { + if (header->unknown) { IUnknown_AddRef(*(IUnknown **)header->data); } + return D3D_OK; + } + + FREE(header); + if (err == PIPE_ERROR_OUT_OF_MEMORY) { return E_OUTOFMEMORY; } + + return D3DERR_DRIVERINTERNALERROR; +} + +HRESULT WINAPI +NineVolume9_GetPrivateData( struct NineVolume9 *This, + REFGUID refguid, + void *pData, + DWORD *pSizeOfData ) +{ + struct pheader *header; + + user_assert(pSizeOfData, E_POINTER); + + header = util_hash_table_get(This->pdata, refguid); + if (!header) { return D3DERR_NOTFOUND; } + + if (!pData) { + *pSizeOfData = header->size; + return D3D_OK; + } + if (*pSizeOfData < header->size) { + return D3DERR_MOREDATA; + } + + if (header->unknown) { IUnknown_AddRef(*(IUnknown **)header->data); } + memcpy(pData, header->data, header->size); + + return D3D_OK; +} + +HRESULT WINAPI +NineVolume9_FreePrivateData( struct NineVolume9 *This, + REFGUID refguid ) +{ + struct pheader *header; + + header = util_hash_table_get(This->pdata, refguid); + if (!header) { return D3DERR_NOTFOUND; } + + ht_guid_delete(NULL, header, NULL); + util_hash_table_remove(This->pdata, refguid); + + return D3D_OK; +} + |