st/nine: Do not leak private data in volume9.
[mesa.git] / src / gallium / state_trackers / nine / volume9.c
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 "nine_helpers.h"
27 #include "nine_pipe.h"
28 #include "nine_dump.h"
29
30 #include "util/u_hash_table.h"
31 #include "util/u_format.h"
32 #include "util/u_surface.h"
33 #include "nine_pdata.h"
34
35 #define DBG_CHANNEL DBG_VOLUME
36
37
38 static HRESULT
39 NineVolume9_AllocateData( struct NineVolume9 *This )
40 {
41 unsigned size = This->layer_stride * This->desc.Depth;
42
43 DBG("(%p(This=%p),level=%u) Allocating 0x%x bytes of system memory.\n",
44 This->base.container, This, This->level, size);
45
46 This->data = (uint8_t *)MALLOC(size);
47 if (!This->data)
48 return E_OUTOFMEMORY;
49 return D3D_OK;
50 }
51
52 static HRESULT
53 NineVolume9_ctor( struct NineVolume9 *This,
54 struct NineUnknownParams *pParams,
55 struct NineUnknown *pContainer,
56 struct pipe_resource *pResource,
57 unsigned Level,
58 D3DVOLUME_DESC *pDesc )
59 {
60 HRESULT hr;
61
62 assert(pContainer); /* stand-alone volumes can't be created */
63
64 DBG("This=%p pContainer=%p pDevice=%p pResource=%p Level=%u pDesc=%p\n",
65 This, pContainer, pParams->device, pResource, Level, pDesc);
66
67 /* Mark this as a special surface held by another internal resource. */
68 pParams->container = pContainer;
69
70 user_assert(!(pDesc->Usage & D3DUSAGE_DYNAMIC) ||
71 (pDesc->Pool != D3DPOOL_MANAGED), D3DERR_INVALIDCALL);
72
73 assert(pResource || pDesc->Pool != D3DPOOL_DEFAULT);
74
75 hr = NineUnknown_ctor(&This->base, pParams);
76 if (FAILED(hr))
77 return hr;
78
79 This->pdata = util_hash_table_create(ht_guid_hash, ht_guid_compare);
80 if (!This->pdata)
81 return E_OUTOFMEMORY;
82
83 pipe_resource_reference(&This->resource, pResource);
84
85 This->pipe = pParams->device->pipe;
86 This->transfer = NULL;
87 This->lock_count = 0;
88
89 This->level = Level;
90 This->level_actual = Level;
91 This->desc = *pDesc;
92
93 This->info.screen = pParams->device->screen;
94 This->info.target = PIPE_TEXTURE_3D;
95 This->info.format = d3d9_to_pipe_format(pDesc->Format);
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
106 This->stride = util_format_get_stride(This->info.format, pDesc->Width);
107 This->stride = align(This->stride, 4);
108 This->layer_stride = util_format_get_2d_size(This->info.format,
109 This->stride, pDesc->Height);
110
111 if (pDesc->Pool == D3DPOOL_SYSTEMMEM)
112 This->info.usage = PIPE_USAGE_STAGING;
113
114 if (!This->resource) {
115 hr = NineVolume9_AllocateData(This);
116 if (FAILED(hr))
117 return hr;
118 }
119 return D3D_OK;
120 }
121
122 static void
123 NineVolume9_dtor( struct NineVolume9 *This )
124 {
125 DBG("This=%p\n", This);
126
127 if (This->transfer)
128 NineVolume9_UnlockBox(This);
129
130 if (This->data)
131 FREE(This->data);
132
133 pipe_resource_reference(&This->resource, NULL);
134
135 NineUnknown_dtor(&This->base);
136 }
137
138 HRESULT WINAPI
139 NineVolume9_GetContainer( struct NineVolume9 *This,
140 REFIID riid,
141 void **ppContainer )
142 {
143 if (!NineUnknown(This)->container)
144 return E_NOINTERFACE;
145 return NineUnknown_QueryInterface(NineUnknown(This)->container, riid, ppContainer);
146 }
147
148 static INLINE void
149 NineVolume9_MarkContainerDirty( struct NineVolume9 *This )
150 {
151 struct NineBaseTexture9 *tex;
152 #ifdef DEBUG
153 /* This is always contained by a NineVolumeTexture9. */
154 GUID id = IID_IDirect3DVolumeTexture9;
155 REFIID ref = &id;
156 assert(NineUnknown_QueryInterface(This->base.container, ref, (void **)&tex)
157 == S_OK);
158 assert(NineUnknown_Release(NineUnknown(tex)) != 0);
159 #endif
160
161 tex = NineBaseTexture9(This->base.container);
162 assert(tex);
163 if (This->desc.Pool == D3DPOOL_MANAGED)
164 tex->dirty = TRUE;
165 else
166 if (This->desc.Usage & D3DUSAGE_AUTOGENMIPMAP)
167 tex->dirty_mip = TRUE;
168
169 BASETEX_REGISTER_UPDATE(tex);
170 }
171
172 HRESULT WINAPI
173 NineVolume9_GetDesc( struct NineVolume9 *This,
174 D3DVOLUME_DESC *pDesc )
175 {
176 user_assert(pDesc != NULL, E_POINTER);
177 *pDesc = This->desc;
178 return D3D_OK;
179 }
180
181 static INLINE boolean
182 NineVolume9_IsDirty(struct NineVolume9 *This)
183 {
184 return This->dirty_box[0].width != 0;
185 }
186
187 INLINE void
188 NineVolume9_AddDirtyRegion( struct NineVolume9 *This,
189 const struct pipe_box *box )
190 {
191 struct pipe_box cover_a, cover_b;
192 float vol[2];
193
194 if (!box) {
195 u_box_3d(0, 0, 0, This->desc.Width, This->desc.Height,
196 This->desc.Depth, &This->dirty_box[0]);
197 memset(&This->dirty_box[1], 0, sizeof(This->dirty_box[1]));
198 return;
199 }
200 if (!This->dirty_box[0].width) {
201 This->dirty_box[0] = *box;
202 return;
203 }
204
205 u_box_union_3d(&cover_a, &This->dirty_box[0], box);
206 vol[0] = u_box_volume_3d(&cover_a);
207
208 if (This->dirty_box[1].width == 0) {
209 vol[1] = u_box_volume_3d(&This->dirty_box[0]);
210 if (vol[0] > (vol[1] * 1.5f))
211 This->dirty_box[1] = *box;
212 else
213 This->dirty_box[0] = cover_a;
214 } else {
215 u_box_union_3d(&cover_b, &This->dirty_box[1], box);
216 vol[1] = u_box_volume_3d(&cover_b);
217
218 if (vol[0] > vol[1])
219 This->dirty_box[1] = cover_b;
220 else
221 This->dirty_box[0] = cover_a;
222 }
223 }
224
225 static INLINE uint8_t *
226 NineVolume9_GetSystemMemPointer(struct NineVolume9 *This, int x, int y, int z)
227 {
228 unsigned x_offset = util_format_get_stride(This->info.format, x);
229
230 y = util_format_get_nblocksy(This->info.format, y);
231
232 assert(This->data);
233 return This->data + (z * This->layer_stride + y * This->stride + x_offset);
234 }
235
236 HRESULT WINAPI
237 NineVolume9_LockBox( struct NineVolume9 *This,
238 D3DLOCKED_BOX *pLockedVolume,
239 const D3DBOX *pBox,
240 DWORD Flags )
241 {
242 struct pipe_resource *resource = This->resource;
243 struct pipe_box box;
244 unsigned usage;
245
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));
252
253 user_assert(This->desc.Pool != D3DPOOL_DEFAULT ||
254 (This->desc.Usage & D3DUSAGE_DYNAMIC), D3DERR_INVALIDCALL);
255
256 user_assert(!((Flags & D3DLOCK_DISCARD) && (Flags & D3DLOCK_READONLY)),
257 D3DERR_INVALIDCALL);
258
259 user_assert(This->lock_count == 0, D3DERR_INVALIDCALL);
260 user_assert(pLockedVolume, E_POINTER);
261
262 if (pBox && This->desc.Pool == D3DPOOL_DEFAULT &&
263 util_format_is_compressed(This->info.format)) {
264 const unsigned w = util_format_get_blockwidth(This->info.format);
265 const unsigned h = util_format_get_blockheight(This->info.format);
266 user_assert(!(pBox->Left % w) && !(pBox->Right % w) &&
267 !(pBox->Top % h) && !(pBox->Bottom % h),
268 D3DERR_INVALIDCALL);
269 }
270
271 if (Flags & D3DLOCK_DISCARD) {
272 usage = PIPE_TRANSFER_WRITE | PIPE_TRANSFER_DISCARD_RANGE;
273 } else {
274 usage = (Flags & D3DLOCK_READONLY) ?
275 PIPE_TRANSFER_READ : PIPE_TRANSFER_READ_WRITE;
276 }
277 if (Flags & D3DLOCK_DONOTWAIT)
278 usage |= PIPE_TRANSFER_DONTBLOCK;
279
280 if (pBox) {
281 d3dbox_to_pipe_box(&box, pBox);
282 if (u_box_clip_2d(&box, &box, This->desc.Width, This->desc.Height) < 0) {
283 DBG("Locked volume intersection empty.\n");
284 return D3DERR_INVALIDCALL;
285 }
286 } else {
287 u_box_3d(0, 0, 0, This->desc.Width, This->desc.Height, This->desc.Depth,
288 &box);
289 }
290
291 if (This->data) {
292 pLockedVolume->RowPitch = This->stride;
293 pLockedVolume->SlicePitch = This->layer_stride;
294 pLockedVolume->pBits =
295 NineVolume9_GetSystemMemPointer(This, box.x, box.y, box.z);
296 } else {
297 pLockedVolume->pBits =
298 This->pipe->transfer_map(This->pipe, resource, This->level, usage,
299 &box, &This->transfer);
300 if (!This->transfer) {
301 if (Flags & D3DLOCK_DONOTWAIT)
302 return D3DERR_WASSTILLDRAWING;
303 return D3DERR_DRIVERINTERNALERROR;
304 }
305 pLockedVolume->RowPitch = This->transfer->stride;
306 pLockedVolume->SlicePitch = This->transfer->layer_stride;
307 }
308
309 if (!(Flags & (D3DLOCK_NO_DIRTY_UPDATE | D3DLOCK_READONLY))) {
310 NineVolume9_MarkContainerDirty(This);
311 if (This->desc.Pool == D3DPOOL_MANAGED)
312 NineVolume9_AddDirtyRegion(This, &box);
313 }
314
315 ++This->lock_count;
316 return D3D_OK;
317 }
318
319 HRESULT WINAPI
320 NineVolume9_UnlockBox( struct NineVolume9 *This )
321 {
322 DBG("This=%p lock_count=%u\n", This, This->lock_count);
323 user_assert(This->lock_count, D3DERR_INVALIDCALL);
324 if (This->transfer) {
325 This->pipe->transfer_unmap(This->pipe, This->transfer);
326 This->transfer = NULL;
327 }
328 --This->lock_count;
329 return D3D_OK;
330 }
331
332
333 HRESULT
334 NineVolume9_CopyVolume( struct NineVolume9 *This,
335 struct NineVolume9 *From,
336 unsigned dstx, unsigned dsty, unsigned dstz,
337 struct pipe_box *pSrcBox )
338 {
339 struct pipe_context *pipe = This->pipe;
340 struct pipe_resource *r_dst = This->resource;
341 struct pipe_resource *r_src = From->resource;
342 struct pipe_transfer *transfer;
343 struct pipe_box src_box;
344 struct pipe_box dst_box;
345 uint8_t *p_dst;
346 const uint8_t *p_src;
347
348 DBG("This=%p From=%p dstx=%u dsty=%u dstz=%u pSrcBox=%p\n",
349 This, From, dstx, dsty, dstz, pSrcBox);
350
351 user_assert(This->desc.Format == From->desc.Format, D3DERR_INVALIDCALL);
352
353 dst_box.x = dstx;
354 dst_box.y = dsty;
355 dst_box.z = dstz;
356
357 if (pSrcBox) {
358 /* make sure it doesn't range outside the source volume */
359 user_assert(pSrcBox->x >= 0 &&
360 (pSrcBox->width - pSrcBox->x) <= From->desc.Width &&
361 pSrcBox->y >= 0 &&
362 (pSrcBox->height - pSrcBox->y) <= From->desc.Height &&
363 pSrcBox->z >= 0 &&
364 (pSrcBox->depth - pSrcBox->z) <= From->desc.Depth,
365 D3DERR_INVALIDCALL);
366 src_box = *pSrcBox;
367 } else {
368 src_box.x = 0;
369 src_box.y = 0;
370 src_box.z = 0;
371 src_box.width = From->desc.Width;
372 src_box.height = From->desc.Height;
373 src_box.depth = From->desc.Depth;
374 }
375 /* limits */
376 dst_box.width = This->desc.Width - dst_box.x;
377 dst_box.height = This->desc.Height - dst_box.y;
378 dst_box.depth = This->desc.Depth - dst_box.z;
379
380 user_assert(src_box.width <= dst_box.width &&
381 src_box.height <= dst_box.height &&
382 src_box.depth <= dst_box.depth, D3DERR_INVALIDCALL);
383
384 dst_box.width = src_box.width;
385 dst_box.height = src_box.height;
386 dst_box.depth = src_box.depth;
387
388 /* Don't copy to device memory of managed resources.
389 * We don't want to download it back again later.
390 */
391 if (This->desc.Pool == D3DPOOL_MANAGED)
392 r_dst = NULL;
393
394 /* Don't copy from stale device memory of managed resources.
395 * Also, don't copy between system and device if we don't have to.
396 */
397 if (From->desc.Pool == D3DPOOL_MANAGED) {
398 if (!r_dst || NineVolume9_IsDirty(From))
399 r_src = NULL;
400 }
401
402 if (r_dst && r_src) {
403 pipe->resource_copy_region(pipe,
404 r_dst, This->level,
405 dst_box.x, dst_box.y, dst_box.z,
406 r_src, From->level,
407 &src_box);
408 } else
409 if (r_dst) {
410 p_src = NineVolume9_GetSystemMemPointer(From,
411 src_box.x, src_box.y, src_box.z);
412
413 pipe->transfer_inline_write(pipe, r_dst, This->level,
414 0, /* WRITE|DISCARD are implicit */
415 &dst_box, p_src,
416 From->stride, From->layer_stride);
417 } else
418 if (r_src) {
419 p_dst = NineVolume9_GetSystemMemPointer(This, 0, 0, 0);
420 p_src = pipe->transfer_map(pipe, r_src, From->level,
421 PIPE_TRANSFER_READ,
422 &src_box, &transfer);
423 if (!p_src)
424 return D3DERR_DRIVERINTERNALERROR;
425
426 util_copy_box(p_dst, This->info.format,
427 This->stride, This->layer_stride,
428 dst_box.x, dst_box.y, dst_box.z,
429 dst_box.width, dst_box.height, dst_box.depth,
430 p_src,
431 transfer->stride, transfer->layer_stride,
432 src_box.x, src_box.y, src_box.z);
433
434 pipe->transfer_unmap(pipe, transfer);
435 } else {
436 p_dst = NineVolume9_GetSystemMemPointer(This, 0, 0, 0);
437 p_src = NineVolume9_GetSystemMemPointer(From, 0, 0, 0);
438
439 util_copy_box(p_dst, This->info.format,
440 This->stride, This->layer_stride,
441 dst_box.x, dst_box.y, dst_box.z,
442 dst_box.width, dst_box.height, dst_box.depth,
443 p_src,
444 From->stride, From->layer_stride,
445 src_box.x, src_box.y, src_box.z);
446 }
447
448 if (This->desc.Pool == D3DPOOL_DEFAULT ||
449 This->desc.Pool == D3DPOOL_MANAGED)
450 NineVolume9_MarkContainerDirty(This);
451 if (!r_dst && This->resource)
452 NineVolume9_AddDirtyRegion(This, &dst_box);
453
454 return D3D_OK;
455 }
456
457 HRESULT
458 NineVolume9_UploadSelf( struct NineVolume9 *This )
459 {
460 struct pipe_context *pipe = This->pipe;
461 struct pipe_resource *res = This->resource;
462 uint8_t *ptr;
463 unsigned i;
464
465 DBG("This=%p dirty=%i data=%p res=%p\n", This, NineVolume9_IsDirty(This),
466 This->data, res);
467
468 assert(This->desc.Pool == D3DPOOL_MANAGED);
469
470 if (!NineVolume9_IsDirty(This))
471 return D3D_OK;
472 assert(res);
473
474 for (i = 0; i < Elements(This->dirty_box); ++i) {
475 const struct pipe_box *box = &This->dirty_box[i];
476 if (box->width == 0)
477 break;
478 ptr = NineVolume9_GetSystemMemPointer(This, box->x, box->y, box->z);
479
480 pipe->transfer_inline_write(pipe, res, This->level,
481 0,
482 box, ptr, This->stride, This->layer_stride);
483 }
484 NineVolume9_ClearDirtyRegion(This);
485
486 return D3D_OK;
487 }
488
489
490 IDirect3DVolume9Vtbl NineVolume9_vtable = {
491 (void *)NineUnknown_QueryInterface,
492 (void *)NineUnknown_AddRef,
493 (void *)NineUnknown_Release,
494 (void *)NineUnknown_GetDevice, /* actually part of Volume9 iface */
495 (void *)NineVolume9_SetPrivateData,
496 (void *)NineVolume9_GetPrivateData,
497 (void *)NineVolume9_FreePrivateData,
498 (void *)NineVolume9_GetContainer,
499 (void *)NineVolume9_GetDesc,
500 (void *)NineVolume9_LockBox,
501 (void *)NineVolume9_UnlockBox
502 };
503
504 static const GUID *NineVolume9_IIDs[] = {
505 &IID_IDirect3DVolume9,
506 &IID_IUnknown,
507 NULL
508 };
509
510 HRESULT
511 NineVolume9_new( struct NineDevice9 *pDevice,
512 struct NineUnknown *pContainer,
513 struct pipe_resource *pResource,
514 unsigned Level,
515 D3DVOLUME_DESC *pDesc,
516 struct NineVolume9 **ppOut )
517 {
518 NINE_DEVICE_CHILD_NEW(Volume9, ppOut, pDevice, /* args */
519 pContainer, pResource, Level, pDesc);
520 }
521
522
523 /*** The boring stuff. TODO: Unify with Resource. ***/
524
525 HRESULT WINAPI
526 NineVolume9_SetPrivateData( struct NineVolume9 *This,
527 REFGUID refguid,
528 const void *pData,
529 DWORD SizeOfData,
530 DWORD Flags )
531 {
532 enum pipe_error err;
533 struct pheader *header;
534 const void *user_data = pData;
535
536 DBG("This=%p refguid=%p pData=%p SizeOfData=%d Flags=%d\n",
537 This, refguid, pData, SizeOfData, Flags);
538
539 if (Flags & D3DSPD_IUNKNOWN)
540 user_assert(SizeOfData == sizeof(IUnknown *), D3DERR_INVALIDCALL);
541
542 /* data consists of a header and the actual data. avoiding 2 mallocs */
543 header = CALLOC_VARIANT_LENGTH_STRUCT(pheader, SizeOfData-1);
544 if (!header) { return E_OUTOFMEMORY; }
545 header->unknown = (Flags & D3DSPD_IUNKNOWN) ? TRUE : FALSE;
546
547 /* if the refguid already exists, delete it */
548 NineVolume9_FreePrivateData(This, refguid);
549
550 /* IUnknown special case */
551 if (header->unknown) {
552 /* here the pointer doesn't point to the data we want, so point at the
553 * pointer making what we eventually copy is the pointer itself */
554 user_data = &pData;
555 }
556
557 header->size = SizeOfData;
558 memcpy(header->data, user_data, header->size);
559
560 err = util_hash_table_set(This->pdata, refguid, header);
561 if (err == PIPE_OK) {
562 if (header->unknown) { IUnknown_AddRef(*(IUnknown **)header->data); }
563 return D3D_OK;
564 }
565
566 FREE(header);
567 if (err == PIPE_ERROR_OUT_OF_MEMORY) { return E_OUTOFMEMORY; }
568
569 return D3DERR_DRIVERINTERNALERROR;
570 }
571
572 HRESULT WINAPI
573 NineVolume9_GetPrivateData( struct NineVolume9 *This,
574 REFGUID refguid,
575 void *pData,
576 DWORD *pSizeOfData )
577 {
578 struct pheader *header;
579
580 user_assert(pSizeOfData, E_POINTER);
581
582 header = util_hash_table_get(This->pdata, refguid);
583 if (!header) { return D3DERR_NOTFOUND; }
584
585 if (!pData) {
586 *pSizeOfData = header->size;
587 return D3D_OK;
588 }
589 if (*pSizeOfData < header->size) {
590 return D3DERR_MOREDATA;
591 }
592
593 if (header->unknown) { IUnknown_AddRef(*(IUnknown **)header->data); }
594 memcpy(pData, header->data, header->size);
595
596 return D3D_OK;
597 }
598
599 HRESULT WINAPI
600 NineVolume9_FreePrivateData( struct NineVolume9 *This,
601 REFGUID refguid )
602 {
603 struct pheader *header;
604
605 DBG("This=%p refguid=%p\n", This, refguid);
606
607 header = util_hash_table_get(This->pdata, refguid);
608 if (!header) { return D3DERR_NOTFOUND; }
609
610 ht_guid_delete(NULL, header, NULL);
611 util_hash_table_remove(This->pdata, refguid);
612
613 return D3D_OK;
614 }
615