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 "c99_alloca.h"
24
25#include "device9.h"
26#include "cubetexture9.h"
27#include "nine_memory_helper.h"
28#include "nine_helpers.h"
29#include "nine_pipe.h"
30
31#define DBG_CHANNEL DBG_CUBETEXTURE
32
33
34static HRESULT
35NineCubeTexture9_ctor( struct NineCubeTexture9 *This,
36                       struct NineUnknownParams *pParams,
37                       UINT EdgeLength, UINT Levels,
38                       DWORD Usage,
39                       D3DFORMAT Format,
40                       D3DPOOL Pool,
41                       HANDLE *pSharedHandle )
42{
43    struct pipe_resource *info = &This->base.base.info;
44    struct pipe_screen *screen = pParams->device->screen;
45    enum pipe_format pf;
46    unsigned i, l, f, offset, face_size = 0;
47    unsigned *level_offsets = NULL;
48    D3DSURFACE_DESC sfdesc;
49    struct nine_allocation *p;
50    HRESULT hr;
51
52    DBG("This=%p pParams=%p EdgeLength=%u Levels=%u Usage=%d "
53        "Format=%d Pool=%d pSharedHandle=%p\n",
54        This, pParams, EdgeLength, Levels, Usage,
55        Format, Pool, pSharedHandle);
56
57    This->base.base.base.device = pParams->device; /* Early fill this field in case of failure */
58
59    user_assert(EdgeLength, D3DERR_INVALIDCALL);
60
61    /* user_assert(!pSharedHandle || Pool == D3DPOOL_DEFAULT, D3DERR_INVALIDCALL); */
62    user_assert(!pSharedHandle, D3DERR_INVALIDCALL); /* TODO */
63
64    user_assert(!(Usage & D3DUSAGE_AUTOGENMIPMAP) ||
65                (Pool != D3DPOOL_SYSTEMMEM && Levels <= 1), D3DERR_INVALIDCALL);
66
67    if (Usage & D3DUSAGE_AUTOGENMIPMAP)
68        Levels = 0;
69
70    pf = d3d9_to_pipe_format_checked(screen, Format, PIPE_TEXTURE_CUBE, 0,
71                                     PIPE_BIND_SAMPLER_VIEW, FALSE,
72                                     Pool == D3DPOOL_SCRATCH);
73
74    if (pf == PIPE_FORMAT_NONE)
75        return D3DERR_INVALIDCALL;
76
77    if (compressed_format(Format)) {
78        const unsigned w = util_format_get_blockwidth(pf);
79        const unsigned h = util_format_get_blockheight(pf);
80
81        user_assert(!(EdgeLength % w) && !(EdgeLength % h), D3DERR_INVALIDCALL);
82    }
83
84    info->screen = pParams->device->screen;
85    info->target = PIPE_TEXTURE_CUBE;
86    info->format = pf;
87    info->width0 = EdgeLength;
88    info->height0 = EdgeLength;
89    info->depth0 = 1;
90    if (Levels)
91        info->last_level = Levels - 1;
92    else
93        info->last_level = util_logbase2(EdgeLength);
94    info->array_size = 6;
95    info->nr_samples = 0;
96    info->nr_storage_samples = 0;
97    info->bind = PIPE_BIND_SAMPLER_VIEW;
98    info->usage = PIPE_USAGE_DEFAULT;
99    info->flags = 0;
100
101    if (Usage & D3DUSAGE_RENDERTARGET)
102        info->bind |= PIPE_BIND_RENDER_TARGET;
103    if (Usage & D3DUSAGE_DEPTHSTENCIL)
104        info->bind |= PIPE_BIND_DEPTH_STENCIL;
105
106    if (Usage & D3DUSAGE_DYNAMIC) {
107        info->usage = PIPE_USAGE_DYNAMIC;
108    }
109    if (Usage & D3DUSAGE_SOFTWAREPROCESSING)
110        DBG("Application asked for Software Vertex Processing, "
111            "but this is unimplemented\n");
112
113    hr = NineBaseTexture9_ctor(&This->base, pParams, NULL, D3DRTYPE_CUBETEXTURE,
114                               Format, Pool, Usage);
115    if (FAILED(hr))
116        return hr;
117    This->base.pstype = 2;
118
119    if (Pool != D3DPOOL_DEFAULT) {
120        level_offsets = alloca(sizeof(unsigned) * This->base.level_count);
121        face_size = nine_format_get_size_and_offsets(pf, level_offsets,
122                                                     EdgeLength, EdgeLength,
123                                                     This->base.level_count-1);
124        This->managed_buffer = nine_allocate(pParams->device->allocator, 6 * face_size);
125        if (!This->managed_buffer)
126            return E_OUTOFMEMORY;
127    }
128
129    This->surfaces = CALLOC(6 * This->base.level_count, sizeof(*This->surfaces));
130    if (!This->surfaces)
131        return E_OUTOFMEMORY;
132
133    /* Create all the surfaces right away.
134     * They manage backing storage, and transfers (LockRect) are deferred
135     * to them.
136     */
137    sfdesc.Format = Format;
138    sfdesc.Type = D3DRTYPE_SURFACE;
139    sfdesc.Usage = Usage;
140    sfdesc.Pool = Pool;
141    sfdesc.MultiSampleType = D3DMULTISAMPLE_NONE;
142    sfdesc.MultiSampleQuality = 0;
143    /* We allocate the memory for the surfaces as continous blocks.
144     * This is the expected behaviour, however we haven't tested for
145     * cube textures in which order the faces/levels should be in memory
146     */
147    for (f = 0; f < 6; f++) {
148        offset = f * face_size;
149        for (l = 0; l < This->base.level_count; l++) {
150            sfdesc.Width = sfdesc.Height = u_minify(EdgeLength, l);
151            p = This->managed_buffer ?
152                nine_suballocate(pParams->device->allocator, This->managed_buffer, offset + level_offsets[l]) : NULL;
153
154            hr = NineSurface9_new(This->base.base.base.device, NineUnknown(This),
155                                  This->base.base.resource, p, D3DRTYPE_CUBETEXTURE,
156                                  l, f, &sfdesc, &This->surfaces[f + 6 * l]);
157            if (FAILED(hr))
158                return hr;
159        }
160    }
161
162    for (i = 0; i < 6; ++i) {
163        /* Textures start initially dirty */
164        This->dirty_rect[i].width = EdgeLength;
165        This->dirty_rect[i].height = EdgeLength;
166        This->dirty_rect[i].depth = 1;
167    }
168
169    return D3D_OK;
170}
171
172static void
173NineCubeTexture9_dtor( struct NineCubeTexture9 *This )
174{
175    unsigned i;
176    bool is_worker = nine_context_is_worker(This->base.base.base.device);
177
178    DBG("This=%p\n", This);
179
180    if (This->surfaces) {
181        for (i = 0; i < This->base.level_count * 6; ++i)
182            if (This->surfaces[i])
183                NineUnknown_Destroy(&This->surfaces[i]->base.base);
184        FREE(This->surfaces);
185    }
186
187    if (This->managed_buffer) {
188        if (is_worker)
189            nine_free_worker(This->base.base.base.device->allocator, This->managed_buffer);
190        else
191            nine_free(This->base.base.base.device->allocator, This->managed_buffer);
192    }
193
194    NineBaseTexture9_dtor(&This->base);
195}
196
197HRESULT NINE_WINAPI
198NineCubeTexture9_GetLevelDesc( struct NineCubeTexture9 *This,
199                               UINT Level,
200                               D3DSURFACE_DESC *pDesc )
201{
202    DBG("This=%p Level=%u pDesc=%p\n", This, Level, pDesc);
203
204    user_assert(Level < This->base.level_count, D3DERR_INVALIDCALL);
205
206    *pDesc = This->surfaces[Level * 6]->desc;
207
208    return D3D_OK;
209}
210
211HRESULT NINE_WINAPI
212NineCubeTexture9_GetCubeMapSurface( struct NineCubeTexture9 *This,
213                                    D3DCUBEMAP_FACES FaceType,
214                                    UINT Level,
215                                    IDirect3DSurface9 **ppCubeMapSurface )
216{
217    const unsigned s = Level * 6 + FaceType;
218
219    DBG("This=%p FaceType=%d Level=%u ppCubeMapSurface=%p\n",
220        This, FaceType, Level, ppCubeMapSurface);
221
222    user_assert(Level < This->base.level_count, D3DERR_INVALIDCALL);
223    user_assert(FaceType < 6, D3DERR_INVALIDCALL);
224
225    NineUnknown_AddRef(NineUnknown(This->surfaces[s]));
226    *ppCubeMapSurface = (IDirect3DSurface9 *)This->surfaces[s];
227
228    return D3D_OK;
229}
230
231HRESULT NINE_WINAPI
232NineCubeTexture9_LockRect( struct NineCubeTexture9 *This,
233                           D3DCUBEMAP_FACES FaceType,
234                           UINT Level,
235                           D3DLOCKED_RECT *pLockedRect,
236                           const RECT *pRect,
237                           DWORD Flags )
238{
239    const unsigned s = Level * 6 + FaceType;
240
241    DBG("This=%p FaceType=%d Level=%u pLockedRect=%p pRect=%p Flags=%d\n",
242        This, FaceType, Level, pLockedRect, pRect, Flags);
243
244    user_assert(Level < This->base.level_count, D3DERR_INVALIDCALL);
245    user_assert(FaceType < 6, D3DERR_INVALIDCALL);
246
247    return NineSurface9_LockRect(This->surfaces[s], pLockedRect, pRect, Flags);
248}
249
250HRESULT NINE_WINAPI
251NineCubeTexture9_UnlockRect( struct NineCubeTexture9 *This,
252                             D3DCUBEMAP_FACES FaceType,
253                             UINT Level )
254{
255    const unsigned s = Level * 6 + FaceType;
256
257    DBG("This=%p FaceType=%d Level=%u\n", This, FaceType, Level);
258
259    user_assert(Level < This->base.level_count, D3DERR_INVALIDCALL);
260    user_assert(FaceType < 6, D3DERR_INVALIDCALL);
261
262    return NineSurface9_UnlockRect(This->surfaces[s]);
263}
264
265HRESULT NINE_WINAPI
266NineCubeTexture9_AddDirtyRect( struct NineCubeTexture9 *This,
267                               D3DCUBEMAP_FACES FaceType,
268                               const RECT *pDirtyRect )
269{
270    DBG("This=%p FaceType=%d pDirtyRect=%p\n", This, FaceType, pDirtyRect);
271
272    user_assert(FaceType < 6, D3DERR_INVALIDCALL);
273
274    if (This->base.base.pool != D3DPOOL_MANAGED) {
275        if (This->base.base.usage & D3DUSAGE_AUTOGENMIPMAP) {
276            This->base.dirty_mip = TRUE;
277            BASETEX_REGISTER_UPDATE(&This->base);
278        }
279        return D3D_OK;
280    }
281
282    if (This->base.base.pool == D3DPOOL_MANAGED) {
283        This->base.managed.dirty = TRUE;
284        BASETEX_REGISTER_UPDATE(&This->base);
285    }
286
287    if (!pDirtyRect) {
288        u_box_origin_2d(This->base.base.info.width0,
289                        This->base.base.info.height0,
290                        &This->dirty_rect[FaceType]);
291    } else {
292        if (This->dirty_rect[FaceType].width == 0) {
293            rect_to_pipe_box_clamp(&This->dirty_rect[FaceType], pDirtyRect);
294        } else {
295            struct pipe_box box;
296            rect_to_pipe_box_clamp(&box, pDirtyRect);
297            u_box_union_2d(&This->dirty_rect[FaceType], &This->dirty_rect[FaceType],
298                           &box);
299        }
300        (void) u_box_clip_2d(&This->dirty_rect[FaceType],
301                             &This->dirty_rect[FaceType],
302                             This->base.base.info.width0,
303                             This->base.base.info.height0);
304    }
305    return D3D_OK;
306}
307
308IDirect3DCubeTexture9Vtbl NineCubeTexture9_vtable = {
309    (void *)NineUnknown_QueryInterface,
310    (void *)NineUnknown_AddRef,
311    (void *)NineUnknown_Release,
312    (void *)NineUnknown_GetDevice, /* actually part of Resource9 iface */
313    (void *)NineUnknown_SetPrivateData,
314    (void *)NineUnknown_GetPrivateData,
315    (void *)NineUnknown_FreePrivateData,
316    (void *)NineResource9_SetPriority,
317    (void *)NineResource9_GetPriority,
318    (void *)NineBaseTexture9_PreLoad,
319    (void *)NineResource9_GetType,
320    (void *)NineBaseTexture9_SetLOD,
321    (void *)NineBaseTexture9_GetLOD,
322    (void *)NineBaseTexture9_GetLevelCount,
323    (void *)NineBaseTexture9_SetAutoGenFilterType,
324    (void *)NineBaseTexture9_GetAutoGenFilterType,
325    (void *)NineBaseTexture9_GenerateMipSubLevels,
326    (void *)NineCubeTexture9_GetLevelDesc,
327    (void *)NineCubeTexture9_GetCubeMapSurface,
328    (void *)NineCubeTexture9_LockRect,
329    (void *)NineCubeTexture9_UnlockRect,
330    (void *)NineCubeTexture9_AddDirtyRect
331};
332
333static const GUID *NineCubeTexture9_IIDs[] = {
334    &IID_IDirect3DCubeTexture9,
335    &IID_IDirect3DBaseTexture9,
336    &IID_IDirect3DResource9,
337    &IID_IUnknown,
338    NULL
339};
340
341HRESULT
342NineCubeTexture9_new( struct NineDevice9 *pDevice,
343                      UINT EdgeLength, UINT Levels,
344                      DWORD Usage,
345                      D3DFORMAT Format,
346                      D3DPOOL Pool,
347                      struct NineCubeTexture9 **ppOut,
348                      HANDLE *pSharedHandle )
349{
350    NINE_DEVICE_CHILD_NEW(CubeTexture9, ppOut, pDevice,
351                          EdgeLength, Levels,
352                          Usage, Format, Pool, pSharedHandle);
353}
354