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 "basetexture9.h"
24#include "device9.h"
25
26/* For UploadSelf: */
27#include "texture9.h"
28#include "cubetexture9.h"
29#include "volumetexture9.h"
30#include "nine_pipe.h"
31
32#if defined(DEBUG) || !defined(NDEBUG)
33#include "nine_dump.h"
34#endif
35
36#include "util/format/u_format.h"
37
38#define DBG_CHANNEL DBG_BASETEXTURE
39
40HRESULT
41NineBaseTexture9_ctor( struct NineBaseTexture9 *This,
42                       struct NineUnknownParams *pParams,
43                       struct pipe_resource *initResource,
44                       D3DRESOURCETYPE Type,
45                       D3DFORMAT format,
46                       D3DPOOL Pool,
47                       DWORD Usage)
48{
49    BOOL alloc = (Pool == D3DPOOL_DEFAULT) && !initResource &&
50        (format != D3DFMT_NULL);
51    HRESULT hr;
52
53    DBG("This=%p, pParams=%p initResource=%p Type=%d format=%d Pool=%d Usage=%d\n",
54        This, pParams, initResource, Type, format, Pool, Usage);
55
56    user_assert(!(Usage & (D3DUSAGE_RENDERTARGET | D3DUSAGE_DEPTHSTENCIL)) ||
57                Pool == D3DPOOL_DEFAULT, D3DERR_INVALIDCALL);
58    user_assert(!(Usage & D3DUSAGE_DYNAMIC) ||
59                !(Pool == D3DPOOL_MANAGED ||
60                  Pool == D3DPOOL_SCRATCH), D3DERR_INVALIDCALL);
61
62    hr = NineResource9_ctor(&This->base, pParams, initResource, alloc, Type, Pool, Usage);
63    if (FAILED(hr))
64        return hr;
65
66    This->format = format;
67    This->mipfilter = (Usage & D3DUSAGE_AUTOGENMIPMAP) ?
68        D3DTEXF_LINEAR : D3DTEXF_NONE;
69    /* In the case of D3DUSAGE_AUTOGENMIPMAP, only the first level is accessible,
70     * and thus needs a surface created. */
71    This->level_count = (Usage & D3DUSAGE_AUTOGENMIPMAP) ? 1 : (This->base.info.last_level+1);
72    This->managed.lod = 0;
73    This->managed.lod_resident = -1;
74    /* Mark the texture as dirty to trigger first upload when we need the texture,
75     * even if it wasn't set by the application */
76    if (Pool == D3DPOOL_MANAGED)
77        This->managed.dirty = TRUE;
78    /* When a depth buffer is sampled, it is for shadow mapping, except for
79     * D3DFMT_INTZ, D3DFMT_DF16 and D3DFMT_DF24.
80     * In addition D3DFMT_INTZ can be used for both texturing and depth buffering
81     * if z write is disabled. This particular feature may not work for us in
82     * practice because OGL doesn't have that. However apparently it is known
83     * some cards have performance issues with this feature, so real apps
84     * shouldn't use it. */
85    This->shadow = (This->format != D3DFMT_INTZ && This->format != D3DFMT_DF16 &&
86                    This->format != D3DFMT_DF24) &&
87                   util_format_has_depth(util_format_description(This->base.info.format));
88    This->fetch4_compatible = fetch4_compatible_format(This->format);
89
90    list_inithead(&This->list);
91    list_inithead(&This->list2);
92    if (Pool == D3DPOOL_MANAGED)
93        list_add(&This->list2, &This->base.base.device->managed_textures);
94
95    return D3D_OK;
96}
97
98void
99NineBaseTexture9_dtor( struct NineBaseTexture9 *This )
100{
101    DBG("This=%p\n", This);
102
103    pipe_sampler_view_reference(&This->view[0], NULL);
104    pipe_sampler_view_reference(&This->view[1], NULL);
105
106    if (list_is_linked(&This->list))
107        list_del(&This->list);
108    if (list_is_linked(&This->list2))
109        list_del(&This->list2);
110
111    NineResource9_dtor(&This->base);
112}
113
114DWORD NINE_WINAPI
115NineBaseTexture9_SetLOD( struct NineBaseTexture9 *This,
116                         DWORD LODNew )
117{
118    DWORD old = This->managed.lod;
119
120    DBG("This=%p LODNew=%d\n", This, LODNew);
121
122    user_assert(This->base.pool == D3DPOOL_MANAGED, 0);
123
124    This->managed.lod = MIN2(LODNew, This->level_count-1);
125
126    if (This->managed.lod != old && This->bind_count && list_is_empty(&This->list))
127       list_add(&This->list, &This->base.base.device->update_textures);
128
129    return old;
130}
131
132DWORD NINE_WINAPI
133NineBaseTexture9_GetLOD( struct NineBaseTexture9 *This )
134{
135    DBG("This=%p\n", This);
136
137    return This->managed.lod;
138}
139
140DWORD NINE_WINAPI
141NineBaseTexture9_GetLevelCount( struct NineBaseTexture9 *This )
142{
143    DBG("This=%p\n", This);
144
145    return This->level_count;
146}
147
148HRESULT NINE_WINAPI
149NineBaseTexture9_SetAutoGenFilterType( struct NineBaseTexture9 *This,
150                                       D3DTEXTUREFILTERTYPE FilterType )
151{
152    DBG("This=%p FilterType=%d\n", This, FilterType);
153
154    if (!(This->base.usage & D3DUSAGE_AUTOGENMIPMAP))
155        return D3D_OK;
156    user_assert(FilterType != D3DTEXF_NONE, D3DERR_INVALIDCALL);
157
158    This->mipfilter = FilterType;
159    This->dirty_mip = TRUE;
160    NineBaseTexture9_GenerateMipSubLevels(This);
161
162    return D3D_OK;
163}
164
165D3DTEXTUREFILTERTYPE NINE_WINAPI
166NineBaseTexture9_GetAutoGenFilterType( struct NineBaseTexture9 *This )
167{
168    DBG("This=%p\n", This);
169
170    return This->mipfilter;
171}
172
173HRESULT
174NineBaseTexture9_UploadSelf( struct NineBaseTexture9 *This )
175{
176    HRESULT hr;
177    unsigned l, min_level_dirty = This->managed.lod;
178    BOOL update_lod;
179
180    DBG("This=%p dirty=%i type=%s\n", This, This->managed.dirty,
181        nine_D3DRTYPE_to_str(This->base.type));
182
183    assert(This->base.pool == D3DPOOL_MANAGED);
184
185    update_lod = This->managed.lod_resident != This->managed.lod;
186    if (!update_lod && !This->managed.dirty)
187        return D3D_OK;
188
189    /* Allocate a new resource with the correct number of levels,
190     * Mark states for update, and tell the nine surfaces/volumes
191     * their new resource. */
192    if (update_lod) {
193        struct pipe_resource *res;
194
195        DBG("updating LOD from %u to %u ...\n", This->managed.lod_resident, This->managed.lod);
196
197        pipe_sampler_view_reference(&This->view[0], NULL);
198        pipe_sampler_view_reference(&This->view[1], NULL);
199
200        /* Allocate a new resource */
201        hr = NineBaseTexture9_CreatePipeResource(This, This->managed.lod_resident != -1);
202        if (FAILED(hr))
203            return hr;
204        res = This->base.resource;
205
206        if (This->managed.lod_resident == -1) {/* no levels were resident */
207            This->managed.dirty = FALSE; /* We are going to upload everything. */
208            This->managed.lod_resident = This->level_count;
209        }
210
211        if (This->base.type == D3DRTYPE_TEXTURE) {
212            struct NineTexture9 *tex = NineTexture9(This);
213
214            /* last content (if apply) has been copied to the new resource.
215             * Note: We cannot render to surfaces of managed textures.
216             * Note2: the level argument passed is to get the level offset
217             * right when the texture is uploaded (the texture first level
218             * corresponds to This->managed.lod).
219             * Note3: We don't care about the value passed for the surfaces
220             * before This->managed.lod, negative with this implementation. */
221            for (l = 0; l < This->level_count; ++l)
222                NineSurface9_SetResource(tex->surfaces[l], res, l - This->managed.lod);
223        } else
224        if (This->base.type == D3DRTYPE_CUBETEXTURE) {
225            struct NineCubeTexture9 *tex = NineCubeTexture9(This);
226            unsigned z;
227
228            for (l = 0; l < This->level_count; ++l) {
229                for (z = 0; z < 6; ++z)
230                    NineSurface9_SetResource(tex->surfaces[l * 6 + z],
231                                             res, l - This->managed.lod);
232            }
233        } else
234        if (This->base.type == D3DRTYPE_VOLUMETEXTURE) {
235            struct NineVolumeTexture9 *tex = NineVolumeTexture9(This);
236
237            for (l = 0; l < This->level_count; ++l)
238                NineVolume9_SetResource(tex->volumes[l], res, l - This->managed.lod);
239        } else {
240            assert(!"invalid texture type");
241        }
242
243        /* We are going to fully upload the new levels,
244         * no need to update dirty parts of the texture for these */
245        min_level_dirty = MAX2(This->managed.lod, This->managed.lod_resident);
246    }
247
248    /* Update dirty parts of the texture */
249    if (This->managed.dirty) {
250        if (This->base.type == D3DRTYPE_TEXTURE) {
251            struct NineTexture9 *tex = NineTexture9(This);
252            struct pipe_box box;
253            box.z = 0;
254            box.depth = 1;
255
256            DBG("TEXTURE: dirty rect=(%u,%u) (%ux%u)\n",
257                tex->dirty_rect.x, tex->dirty_rect.y,
258                tex->dirty_rect.width, tex->dirty_rect.height);
259
260            /* Note: for l < min_level_dirty, the resource is
261             * either non-existing (and thus will be entirely re-uploaded
262             * if the lod changes) or going to have a full upload */
263            if (tex->dirty_rect.width) {
264                for (l = min_level_dirty; l < This->level_count; ++l) {
265                    u_box_minify_2d(&box, &tex->dirty_rect, l);
266                    NineSurface9_UploadSelf(tex->surfaces[l], &box);
267                }
268                memset(&tex->dirty_rect, 0, sizeof(tex->dirty_rect));
269                tex->dirty_rect.depth = 1;
270            }
271        } else
272        if (This->base.type == D3DRTYPE_CUBETEXTURE) {
273            struct NineCubeTexture9 *tex = NineCubeTexture9(This);
274            unsigned z;
275            struct pipe_box box;
276            box.z = 0;
277            box.depth = 1;
278
279            for (z = 0; z < 6; ++z) {
280                DBG("FACE[%u]: dirty rect=(%u,%u) (%ux%u)\n", z,
281                    tex->dirty_rect[z].x, tex->dirty_rect[z].y,
282                    tex->dirty_rect[z].width, tex->dirty_rect[z].height);
283
284                if (tex->dirty_rect[z].width) {
285                    for (l = min_level_dirty; l < This->level_count; ++l) {
286                        u_box_minify_2d(&box, &tex->dirty_rect[z], l);
287                        NineSurface9_UploadSelf(tex->surfaces[l * 6 + z], &box);
288                    }
289                    memset(&tex->dirty_rect[z], 0, sizeof(tex->dirty_rect[z]));
290                    tex->dirty_rect[z].depth = 1;
291                }
292            }
293        } else
294        if (This->base.type == D3DRTYPE_VOLUMETEXTURE) {
295            struct NineVolumeTexture9 *tex = NineVolumeTexture9(This);
296            struct pipe_box box;
297
298            DBG("VOLUME: dirty_box=(%u,%u,%u) (%ux%ux%u)\n",
299                tex->dirty_box.x, tex->dirty_box.y, tex->dirty_box.y,
300                tex->dirty_box.width, tex->dirty_box.height, tex->dirty_box.depth);
301
302            if (tex->dirty_box.width) {
303                for (l = min_level_dirty; l < This->level_count; ++l) {
304                    u_box_minify_3d(&box, &tex->dirty_box, l);
305                    NineVolume9_UploadSelf(tex->volumes[l], &box);
306                }
307                memset(&tex->dirty_box, 0, sizeof(tex->dirty_box));
308            }
309        } else {
310            assert(!"invalid texture type");
311        }
312        This->managed.dirty = FALSE;
313    }
314
315    /* Upload the new levels */
316    if (update_lod) {
317        if (This->base.type == D3DRTYPE_TEXTURE) {
318            struct NineTexture9 *tex = NineTexture9(This);
319            struct pipe_box box;
320
321            box.x = box.y = box.z = 0;
322            box.depth = 1;
323            for (l = This->managed.lod; l < This->managed.lod_resident; ++l) {
324                box.width = u_minify(This->base.info.width0, l);
325                box.height = u_minify(This->base.info.height0, l);
326                NineSurface9_UploadSelf(tex->surfaces[l], &box);
327            }
328        } else
329        if (This->base.type == D3DRTYPE_CUBETEXTURE) {
330            struct NineCubeTexture9 *tex = NineCubeTexture9(This);
331            struct pipe_box box;
332            unsigned z;
333
334            box.x = box.y = box.z = 0;
335            box.depth = 1;
336            for (l = This->managed.lod; l < This->managed.lod_resident; ++l) {
337                box.width = u_minify(This->base.info.width0, l);
338                box.height = u_minify(This->base.info.height0, l);
339                for (z = 0; z < 6; ++z)
340                    NineSurface9_UploadSelf(tex->surfaces[l * 6 + z], &box);
341            }
342        } else
343        if (This->base.type == D3DRTYPE_VOLUMETEXTURE) {
344            struct NineVolumeTexture9 *tex = NineVolumeTexture9(This);
345            struct pipe_box box;
346
347            box.x = box.y = box.z = 0;
348            for (l = This->managed.lod; l < This->managed.lod_resident; ++l) {
349                box.width = u_minify(This->base.info.width0, l);
350                box.height = u_minify(This->base.info.height0, l);
351                box.depth = u_minify(This->base.info.depth0, l);
352                NineVolume9_UploadSelf(tex->volumes[l], &box);
353            }
354        } else {
355            assert(!"invalid texture type");
356        }
357
358        This->managed.lod_resident = This->managed.lod;
359    }
360
361    if (This->base.usage & D3DUSAGE_AUTOGENMIPMAP)
362        This->dirty_mip = TRUE;
363
364    /* Set again the textures currently bound to update the texture data */
365    if (This->bind_count) {
366        struct nine_state *state = &This->base.base.device->state;
367        unsigned s;
368        for (s = 0; s < NINE_MAX_SAMPLERS; ++s)
369            /* Dirty tracking is done in device9 state, not nine_context. */
370            if (state->texture[s] == This)
371                nine_context_set_texture(This->base.base.device, s, This);
372    }
373
374    DBG("DONE, generate mip maps = %i\n", This->dirty_mip);
375    return D3D_OK;
376}
377
378void NINE_WINAPI
379NineBaseTexture9_GenerateMipSubLevels( struct NineBaseTexture9 *This )
380{
381    unsigned base_level = 0;
382    unsigned last_level = This->base.info.last_level - This->managed.lod;
383    unsigned first_layer = 0;
384    unsigned last_layer;
385    unsigned filter = This->mipfilter == D3DTEXF_POINT ? PIPE_TEX_FILTER_NEAREST
386                                                       : PIPE_TEX_FILTER_LINEAR;
387    DBG("This=%p\n", This);
388
389    if (This->base.pool == D3DPOOL_MANAGED)
390        NineBaseTexture9_UploadSelf(This);
391    if (!This->dirty_mip)
392        return;
393    if (This->managed.lod) {
394        ERR("AUTOGENMIPMAP if level 0 is not resident not supported yet !\n");
395        return;
396    }
397
398    if (!This->view[0])
399        NineBaseTexture9_UpdateSamplerView(This, 0);
400
401    last_layer = util_max_layer(This->view[0]->texture, base_level);
402
403    nine_context_gen_mipmap(This->base.base.device, (struct NineUnknown *)This,
404                            This->base.resource,
405                            base_level, last_level,
406                            first_layer, last_layer, filter);
407
408    This->dirty_mip = FALSE;
409}
410
411HRESULT
412NineBaseTexture9_CreatePipeResource( struct NineBaseTexture9 *This,
413                                     BOOL CopyData )
414{
415    struct pipe_context *pipe;
416    struct pipe_screen *screen = This->base.info.screen;
417    struct pipe_resource templ;
418    unsigned l, m;
419    struct pipe_resource *res;
420    struct pipe_resource *old = This->base.resource;
421
422    DBG("This=%p lod=%u last_level=%u\n", This,
423        This->managed.lod, This->base.info.last_level);
424
425    assert(This->base.pool == D3DPOOL_MANAGED);
426
427    templ = This->base.info;
428
429    if (This->managed.lod) {
430        templ.width0 = u_minify(templ.width0, This->managed.lod);
431        templ.height0 = u_minify(templ.height0, This->managed.lod);
432        templ.depth0 = u_minify(templ.depth0, This->managed.lod);
433    }
434    templ.last_level = This->base.info.last_level - This->managed.lod;
435
436    if (old) {
437        /* LOD might have changed. */
438        if (old->width0 == templ.width0 &&
439            old->height0 == templ.height0 &&
440            old->depth0 == templ.depth0)
441            return D3D_OK;
442    }
443
444    res = nine_resource_create_with_retry(This->base.base.device, screen, &templ);
445    if (!res)
446        return D3DERR_OUTOFVIDEOMEMORY;
447    This->base.resource = res;
448
449    if (old && CopyData) { /* Don't return without releasing old ! */
450        struct pipe_box box;
451        box.x = 0;
452        box.y = 0;
453        box.z = 0;
454
455        l = (This->managed.lod < This->managed.lod_resident) ? This->managed.lod_resident - This->managed.lod : 0;
456        m = (This->managed.lod < This->managed.lod_resident) ? 0 : This->managed.lod - This->managed.lod_resident;
457
458        box.width = u_minify(templ.width0, l);
459        box.height = u_minify(templ.height0, l);
460        box.depth = u_minify(templ.depth0, l);
461
462        pipe = nine_context_get_pipe_acquire(This->base.base.device);
463
464        for (; l <= templ.last_level; ++l, ++m) {
465            pipe->resource_copy_region(pipe,
466                                       res, l, 0, 0, 0,
467                                       old, m, &box);
468            box.width = u_minify(box.width, 1);
469            box.height = u_minify(box.height, 1);
470            box.depth = u_minify(box.depth, 1);
471        }
472
473        nine_context_get_pipe_release(This->base.base.device);
474    }
475    pipe_resource_reference(&old, NULL);
476
477    return D3D_OK;
478}
479
480#define SWIZZLE_TO_REPLACE(s) (s == PIPE_SWIZZLE_0 || \
481                               s == PIPE_SWIZZLE_1 || \
482                               s == PIPE_SWIZZLE_NONE)
483
484HRESULT
485NineBaseTexture9_UpdateSamplerView( struct NineBaseTexture9 *This,
486                                    const int sRGB )
487{
488    const struct util_format_description *desc;
489    struct pipe_context *pipe;
490    struct pipe_screen *screen = NineDevice9_GetScreen(This->base.base.device);
491    struct pipe_resource *resource = This->base.resource;
492    struct pipe_sampler_view templ;
493    enum pipe_format srgb_format;
494    unsigned i;
495    uint8_t swizzle[4];
496
497    DBG("This=%p sRGB=%d\n", This, sRGB);
498
499    if (unlikely(!resource)) {
500	if (unlikely(This->format == D3DFMT_NULL))
501            return D3D_OK;
502        NineBaseTexture9_Dump(This);
503    }
504    assert(resource);
505
506    pipe_sampler_view_reference(&This->view[sRGB], NULL);
507
508    swizzle[0] = PIPE_SWIZZLE_X;
509    swizzle[1] = PIPE_SWIZZLE_Y;
510    swizzle[2] = PIPE_SWIZZLE_Z;
511    swizzle[3] = PIPE_SWIZZLE_W;
512    desc = util_format_description(resource->format);
513    if (desc->colorspace == UTIL_FORMAT_COLORSPACE_ZS) {
514        /* msdn doc is incomplete here and wrong.
515         * The only formats that can be read directly here
516         * are DF16, DF24 and INTZ.
517         * Tested on win the swizzle is
518         * R = depth, G = B = 0, A = 1 for DF16 and DF24
519         * R = G = B = A = depth for INTZ
520         * For the other ZS formats that can't be read directly
521         * but can be used as shadow map, the result is duplicated on
522         * all channel */
523        if (This->format == D3DFMT_DF16 ||
524            This->format == D3DFMT_DF24) {
525            swizzle[1] = PIPE_SWIZZLE_0;
526            swizzle[2] = PIPE_SWIZZLE_0;
527            swizzle[3] = PIPE_SWIZZLE_1;
528        } else {
529            swizzle[1] = PIPE_SWIZZLE_X;
530            swizzle[2] = PIPE_SWIZZLE_X;
531            swizzle[3] = PIPE_SWIZZLE_X;
532        }
533    } else if (resource->format == PIPE_FORMAT_RGTC2_UNORM) {
534        swizzle[0] = PIPE_SWIZZLE_Y;
535        swizzle[1] = PIPE_SWIZZLE_X;
536        swizzle[2] = PIPE_SWIZZLE_1;
537        swizzle[3] = PIPE_SWIZZLE_1;
538    } else if (resource->format != PIPE_FORMAT_A8_UNORM &&
539               resource->format != PIPE_FORMAT_RGTC1_UNORM) {
540        /* exceptions:
541         * A8 should have 0.0 as default values for RGB.
542         * ATI1/RGTC1 should be r 0 0 1 (tested on windows).
543         * It is already what gallium does. All the other ones
544         * should have 1.0 for non-defined values */
545        for (i = 0; i < 4; i++) {
546            if (SWIZZLE_TO_REPLACE(desc->swizzle[i]))
547                swizzle[i] = PIPE_SWIZZLE_1;
548        }
549    }
550
551    /* if requested and supported, convert to the sRGB format */
552    srgb_format = util_format_srgb(resource->format);
553    if (sRGB && srgb_format != PIPE_FORMAT_NONE &&
554        screen->is_format_supported(screen, srgb_format,
555                                    resource->target, 0, 0, resource->bind))
556        templ.format = srgb_format;
557    else
558        templ.format = resource->format;
559    templ.u.tex.first_layer = 0;
560    templ.u.tex.last_layer = resource->target == PIPE_TEXTURE_3D ?
561                             0 : resource->array_size - 1;
562    templ.u.tex.first_level = 0;
563    templ.u.tex.last_level = resource->last_level;
564    templ.swizzle_r = swizzle[0];
565    templ.swizzle_g = swizzle[1];
566    templ.swizzle_b = swizzle[2];
567    templ.swizzle_a = swizzle[3];
568    templ.target = resource->target;
569
570    pipe = nine_context_get_pipe_acquire(This->base.base.device);
571    This->view[sRGB] = pipe->create_sampler_view(pipe, resource, &templ);
572    nine_context_get_pipe_release(This->base.base.device);
573
574    DBG("sampler view = %p(resource = %p)\n", This->view[sRGB], resource);
575
576    return This->view[sRGB] ? D3D_OK : D3DERR_DRIVERINTERNALERROR;
577}
578
579void NINE_WINAPI
580NineBaseTexture9_PreLoad( struct NineBaseTexture9 *This )
581{
582    DBG("This=%p\n", This);
583
584    if (This->base.pool == D3DPOOL_MANAGED)
585        NineBaseTexture9_UploadSelf(This);
586}
587
588void
589NineBaseTexture9_UnLoad( struct NineBaseTexture9 *This )
590{
591    DBG("This=%p\n", This);
592
593    if (This->base.pool != D3DPOOL_MANAGED ||
594        This->managed.lod_resident == -1)
595        return;
596
597    DBG("This=%p, releasing resource\n", This);
598    pipe_resource_reference(&This->base.resource, NULL);
599    This->managed.lod_resident = -1;
600    This->managed.dirty = TRUE;
601
602    /* If the texture is bound, we have to re-upload it */
603    BASETEX_REGISTER_UPDATE(This);
604}
605
606#if defined(DEBUG) || !defined(NDEBUG)
607void
608NineBaseTexture9_Dump( struct NineBaseTexture9 *This )
609{
610    DBG("\nNineBaseTexture9(%p->NULL/%p): Pool=%s Type=%s Usage=%s\n"
611        "Format=%s Dims=%ux%ux%u/%u LastLevel=%u Lod=%u(%u)\n", This,
612        This->base.resource,
613        nine_D3DPOOL_to_str(This->base.pool),
614        nine_D3DRTYPE_to_str(This->base.type),
615        nine_D3DUSAGE_to_str(This->base.usage),
616        d3dformat_to_string(This->format),
617        This->base.info.width0, This->base.info.height0, This->base.info.depth0,
618        This->base.info.array_size, This->base.info.last_level,
619        This->managed.lod, This->managed.lod_resident);
620}
621#endif /* DEBUG || !NDEBUG */
622