cubetexture.c 21.3 KB
Newer Older
1
/*
2
 * IWineD3DCubeTexture implementation
3 4
 *
 * Copyright 2002-2005 Jason Edmeades
5 6
 * Copyright 2002-2005 Raphael Junqueira
 * Copyright 2005 Oliver Stieber
7
 * Copyright 2007-2008 Stefan Dösinger for CodeWeavers
8
 * Copyright 2009 Henri Verbeet for CodeWeavers
9 10 11 12 13 14 15 16 17 18 19 20 21
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
22
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23 24 25 26 27
 */

#include "config.h"
#include "wined3d_private.h"

28
WINE_DEFAULT_DEBUG_CHANNEL(d3d_texture);
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58

#define GLINFO_LOCATION (*gl_info)

static void cubetexture_internal_preload(IWineD3DBaseTexture *iface, enum WINED3DSRGB srgb)
{
    /* Override the IWineD3DResource Preload method. */
    IWineD3DCubeTextureImpl *This = (IWineD3DCubeTextureImpl *)iface;
    IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
    unsigned int i, j;
    BOOL srgb_mode;
    BOOL *dirty;

    switch (srgb)
    {
        case SRGB_RGB:
            srgb_mode = FALSE;
            break;

        case SRGB_BOTH:
            cubetexture_internal_preload(iface, SRGB_RGB);
            /* Fallthrough */

        case SRGB_SRGB:
            srgb_mode = TRUE;
            break;

        default:
            srgb_mode = This->baseTexture.is_srgb;
            break;
    }
59
    dirty = srgb_mode ? &This->baseTexture.texture_srgb.dirty : &This->baseTexture.texture_rgb.dirty;
60 61 62 63 64 65 66 67 68 69

    TRACE("(%p) : About to load texture: dirtified(%u).\n", This, *dirty);

    /* We only have to activate a context for gl when we're not drawing.
     * In most cases PreLoad will be called during draw and a context was
     * activated at the beginning of drawPrimitive. */
    if (!device->isInDraw)
    {
        /* No danger of recursive calls, ActivateContext sets isInDraw to true
         * when loading offscreen render targets into their texture. */
70
        ActivateContext(device, NULL, CTXUSAGE_RESOURCELOAD);
71 72
    }

73 74
    if (This->resource.format_desc->format == WINED3DFMT_P8_UINT
            || This->resource.format_desc->format == WINED3DFMT_P8_UINT_A8_UNORM)
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
    {
        for (i = 0; i < This->baseTexture.levels; ++i)
        {
            for (j = WINED3DCUBEMAP_FACE_POSITIVE_X; j <= WINED3DCUBEMAP_FACE_NEGATIVE_Z; ++j)
            {
                if (palette9_changed((IWineD3DSurfaceImpl *)This->surfaces[j][i]))
                {
                    TRACE("Reloading surface because the d3d8/9 palette was changed.\n");
                    /* TODO: This is not necessarily needed with hw palettized texture support. */
                    IWineD3DSurface_LoadLocation(This->surfaces[j][i], SFLAG_INSYSMEM, NULL);
                    /* Make sure the texture is reloaded because of the palette change,
                     * this kills performance though :( */
                    IWineD3DSurface_ModifyLocation(This->surfaces[j][i], SFLAG_INTEXTURE, FALSE);
                }
            }
        }
    }

    /* If the texture is marked dirty or the srgb sampler setting has changed
     * since the last load then reload the surfaces. */
    if (*dirty)
    {
        for (i = 0; i < This->baseTexture.levels; ++i)
        {
            for (j = WINED3DCUBEMAP_FACE_POSITIVE_X; j <= WINED3DCUBEMAP_FACE_NEGATIVE_Z; ++j)
            {
                IWineD3DSurface_LoadTexture(This->surfaces[j][i], srgb_mode);
            }
        }
    }
    else
    {
        TRACE("(%p) Texture not dirty, nothing to do.\n" , iface);
    }

    /* No longer dirty. */
    *dirty = FALSE;
}
113

114
static void cubetexture_cleanup(IWineD3DCubeTextureImpl *This)
115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
{
    unsigned int i, j;

    TRACE("(%p) : Cleaning up.\n", This);

    for (i = 0; i < This->baseTexture.levels; ++i)
    {
        for (j = 0; j < 6; ++j)
        {
            IWineD3DSurface *surface = This->surfaces[j][i];

            if (surface)
            {
                /* Clean out the texture name we gave to the surface so that the
                 * surface doesn't try and release it. */
                surface_set_texture_name(surface, 0, TRUE);
                surface_set_texture_name(surface, 0, FALSE);
                surface_set_texture_target(surface, 0);
                IWineD3DSurface_SetContainer(surface, NULL);
134
                IWineD3DSurface_Release(surface);
135 136 137 138 139 140
            }
        }
    }
    basetexture_cleanup((IWineD3DBaseTexture *)This);
}

141 142
#undef GLINFO_LOCATION

143 144 145
/* *******************************************
   IWineD3DCubeTexture IUnknown parts follow
   ******************************************* */
146 147 148

#define GLINFO_LOCATION This->resource.wineD3DDevice->adapter->gl_info

149
static HRESULT WINAPI IWineD3DCubeTextureImpl_QueryInterface(IWineD3DCubeTexture *iface, REFIID riid, LPVOID *ppobj)
150 151
{
    IWineD3DCubeTextureImpl *This = (IWineD3DCubeTextureImpl *)iface;
152 153
    TRACE("(%p)->(%s,%p)\n",This,debugstr_guid(riid),ppobj);
    if (IsEqualGUID(riid, &IID_IUnknown)
154
        || IsEqualGUID(riid, &IID_IWineD3DBase)
155 156
        || IsEqualGUID(riid, &IID_IWineD3DResource)
        || IsEqualGUID(riid, &IID_IWineD3DBaseTexture)
157
        || IsEqualGUID(riid, &IID_IWineD3DCubeTexture)) {
158 159
        IUnknown_AddRef(iface);
        *ppobj = This;
160
        return S_OK;
161
    }
162
    *ppobj = NULL;
163 164 165
    return E_NOINTERFACE;
}

166
static ULONG WINAPI IWineD3DCubeTextureImpl_AddRef(IWineD3DCubeTexture *iface) {
167
    IWineD3DCubeTextureImpl *This = (IWineD3DCubeTextureImpl *)iface;
168
    TRACE("(%p) : AddRef increasing from %d\n", This, This->resource.ref);
169 170 171
    return InterlockedIncrement(&This->resource.ref);
}

172
static ULONG WINAPI IWineD3DCubeTextureImpl_Release(IWineD3DCubeTexture *iface) {
173 174
    IWineD3DCubeTextureImpl *This = (IWineD3DCubeTextureImpl *)iface;
    ULONG ref;
175
    TRACE("(%p) : Releasing from %d\n", This, This->resource.ref);
176
    ref = InterlockedDecrement(&This->resource.ref);
177 178 179
    if (!ref)
    {
        cubetexture_cleanup(This);
180
        This->resource.parent_ops->wined3d_object_destroyed(This->resource.parent);
181
        HeapFree(GetProcessHeap(), 0, This);
182 183 184 185 186 187 188
    }
    return ref;
}

/* ****************************************************
   IWineD3DCubeTexture IWineD3DResource parts follow
   **************************************************** */
189
static HRESULT WINAPI IWineD3DCubeTextureImpl_GetDevice(IWineD3DCubeTexture *iface, IWineD3DDevice** ppDevice) {
190
    return resource_get_device((IWineD3DResource *)iface, ppDevice);
191 192
}

193
static HRESULT WINAPI IWineD3DCubeTextureImpl_SetPrivateData(IWineD3DCubeTexture *iface, REFGUID refguid, CONST void* pData, DWORD SizeOfData, DWORD Flags) {
194
    return resource_set_private_data((IWineD3DResource *)iface, refguid, pData, SizeOfData, Flags);
195 196
}

197
static HRESULT WINAPI IWineD3DCubeTextureImpl_GetPrivateData(IWineD3DCubeTexture *iface, REFGUID refguid, void* pData, DWORD* pSizeOfData) {
198
    return resource_get_private_data((IWineD3DResource *)iface, refguid, pData, pSizeOfData);
199 200
}

201
static HRESULT WINAPI IWineD3DCubeTextureImpl_FreePrivateData(IWineD3DCubeTexture *iface, REFGUID refguid) {
202
    return resource_free_private_data((IWineD3DResource *)iface, refguid);
203 204
}

205
static DWORD WINAPI IWineD3DCubeTextureImpl_SetPriority(IWineD3DCubeTexture *iface, DWORD PriorityNew) {
206
    return resource_set_priority((IWineD3DResource *)iface, PriorityNew);
207 208
}

209
static DWORD WINAPI IWineD3DCubeTextureImpl_GetPriority(IWineD3DCubeTexture *iface) {
210
    return resource_get_priority((IWineD3DResource *)iface);
211 212
}

213 214 215 216
static void WINAPI IWineD3DCubeTextureImpl_PreLoad(IWineD3DCubeTexture *iface) {
    cubetexture_internal_preload((IWineD3DBaseTexture *) iface, SRGB_ANY);
}

217
static void WINAPI IWineD3DCubeTextureImpl_UnLoad(IWineD3DCubeTexture *iface) {
218 219 220 221 222 223 224 225 226 227 228
    unsigned int i, j;
    IWineD3DCubeTextureImpl *This = (IWineD3DCubeTextureImpl *)iface;
    TRACE("(%p)\n", This);

    /* Unload all the surfaces and reset the texture name. If UnLoad was called on the
     * surface before, this one will be a NOP and vice versa. Unloading an unloaded
     * surface is fine
     */
    for (i = 0; i < This->baseTexture.levels; i++) {
        for (j = WINED3DCUBEMAP_FACE_POSITIVE_X; j <= WINED3DCUBEMAP_FACE_NEGATIVE_Z ; j++) {
            IWineD3DSurface_UnLoad(This->surfaces[j][i]);
229 230
            surface_set_texture_name(This->surfaces[j][i], 0, TRUE);
            surface_set_texture_name(This->surfaces[j][i], 0, FALSE);
231 232 233
        }
    }

234
    basetexture_unload((IWineD3DBaseTexture *)iface);
235 236
}

237
static WINED3DRESOURCETYPE WINAPI IWineD3DCubeTextureImpl_GetType(IWineD3DCubeTexture *iface) {
238
    return resource_get_type((IWineD3DResource *)iface);
239 240
}

241
static HRESULT WINAPI IWineD3DCubeTextureImpl_GetParent(IWineD3DCubeTexture *iface, IUnknown **pParent) {
242
    return resource_get_parent((IWineD3DResource *)iface, pParent);
243 244 245 246 247
}

/* ******************************************************
   IWineD3DCubeTexture IWineD3DBaseTexture parts follow
   ****************************************************** */
248
static DWORD WINAPI IWineD3DCubeTextureImpl_SetLOD(IWineD3DCubeTexture *iface, DWORD LODNew) {
249
    return basetexture_set_lod((IWineD3DBaseTexture *)iface, LODNew);
250 251
}

252
static DWORD WINAPI IWineD3DCubeTextureImpl_GetLOD(IWineD3DCubeTexture *iface) {
253
    return basetexture_get_lod((IWineD3DBaseTexture *)iface);
254 255
}

256
static DWORD WINAPI IWineD3DCubeTextureImpl_GetLevelCount(IWineD3DCubeTexture *iface) {
257
    return basetexture_get_level_count((IWineD3DBaseTexture *)iface);
258 259
}

260
static HRESULT WINAPI IWineD3DCubeTextureImpl_SetAutoGenFilterType(IWineD3DCubeTexture *iface, WINED3DTEXTUREFILTERTYPE FilterType) {
261
  return basetexture_set_autogen_filter_type((IWineD3DBaseTexture *)iface, FilterType);
262 263
}

264
static WINED3DTEXTUREFILTERTYPE WINAPI IWineD3DCubeTextureImpl_GetAutoGenFilterType(IWineD3DCubeTexture *iface) {
265
  return basetexture_get_autogen_filter_type((IWineD3DBaseTexture *)iface);
266 267
}

268
static void WINAPI IWineD3DCubeTextureImpl_GenerateMipSubLevels(IWineD3DCubeTexture *iface) {
269
    basetexture_generate_mipmaps((IWineD3DBaseTexture *)iface);
270 271 272
}

/* Internal function, No d3d mapping */
273
static BOOL WINAPI IWineD3DCubeTextureImpl_SetDirty(IWineD3DCubeTexture *iface, BOOL dirty) {
274
    return basetexture_set_dirty((IWineD3DBaseTexture *)iface, dirty);
275 276
}

277
/* Internal function, No d3d mapping */
278
static BOOL WINAPI IWineD3DCubeTextureImpl_GetDirty(IWineD3DCubeTexture *iface) {
279
    return basetexture_get_dirty((IWineD3DBaseTexture *)iface);
280 281
}

282
/* Context activation is done by the caller. */
283
static HRESULT WINAPI IWineD3DCubeTextureImpl_BindTexture(IWineD3DCubeTexture *iface, BOOL srgb) {
284
    IWineD3DCubeTextureImpl *This = (IWineD3DCubeTextureImpl *)iface;
285
    BOOL set_gl_texture_desc;
286 287
    HRESULT hr;

288
    TRACE("(%p) : relay to BaseTexture\n", This);
289

290
    hr = basetexture_bind((IWineD3DBaseTexture *)iface, srgb, &set_gl_texture_desc);
291 292 293 294
    if (set_gl_texture_desc && SUCCEEDED(hr)) {
        UINT i, j;
        for (i = 0; i < This->baseTexture.levels; ++i) {
            for (j = WINED3DCUBEMAP_FACE_POSITIVE_X; j <= WINED3DCUBEMAP_FACE_NEGATIVE_Z; ++j) {
295
                if(This->baseTexture.is_srgb) {
296
                    surface_set_texture_name(This->surfaces[j][i], This->baseTexture.texture_srgb.name, TRUE);
297
                } else {
298
                    surface_set_texture_name(This->surfaces[j][i], This->baseTexture.texture_rgb.name, FALSE);
299
                }
300 301 302 303 304
            }
        }
    }

    return hr;
305 306
}

307
static UINT WINAPI IWineD3DCubeTextureImpl_GetTextureDimensions(IWineD3DCubeTexture *iface){
308
    IWineD3DCubeTextureImpl *This = (IWineD3DCubeTextureImpl *)iface;
309
    TRACE("(%p)\n", This);
310

311
    return GL_TEXTURE_CUBE_MAP_ARB;
312 313
}

314 315 316 317 318 319 320
static BOOL WINAPI IWineD3DCubeTextureImpl_IsCondNP2(IWineD3DCubeTexture *iface) {
    IWineD3DCubeTextureImpl *This = (IWineD3DCubeTextureImpl *)iface;
    TRACE("(%p)\n", This);

    return FALSE;
}

321 322 323
/* *******************************************
   IWineD3DCubeTexture IWineD3DCubeTexture parts follow
   ******************************************* */
324
static HRESULT WINAPI IWineD3DCubeTextureImpl_GetLevelDesc(IWineD3DCubeTexture *iface, UINT Level, WINED3DSURFACE_DESC* pDesc) {
325
    IWineD3DCubeTextureImpl *This = (IWineD3DCubeTextureImpl *)iface;
326

327 328
    if (Level < This->baseTexture.levels) {
        TRACE("(%p) level (%d)\n", This, Level);
329
        return IWineD3DSurface_GetDesc(This->surfaces[0][Level], pDesc);
330
    }
331
    WARN("(%p) level(%d) overflow Levels(%d)\n", This, Level, This->baseTexture.levels);
332
    return WINED3DERR_INVALIDCALL;
333 334
}

335
static HRESULT WINAPI IWineD3DCubeTextureImpl_GetCubeMapSurface(IWineD3DCubeTexture *iface, WINED3DCUBEMAP_FACES FaceType, UINT Level, IWineD3DSurface** ppCubeMapSurface) {
336
    IWineD3DCubeTextureImpl *This = (IWineD3DCubeTextureImpl *)iface;
337
    HRESULT hr = WINED3DERR_INVALIDCALL;
338

339
    if (Level < This->baseTexture.levels && FaceType <= WINED3DCUBEMAP_FACE_NEGATIVE_Z) {
340 341 342
        *ppCubeMapSurface = This->surfaces[FaceType][Level];
        IWineD3DSurface_AddRef(*ppCubeMapSurface);

343
        hr = WINED3D_OK;
344
    }
345
    if (WINED3D_OK == hr) {
346
        TRACE("(%p) -> faceType(%d) level(%d) returning surface@%p\n", This, FaceType, Level, This->surfaces[FaceType][Level]);
347
    } else {
348
        WARN("(%p) level(%d) overflow Levels(%d) Or FaceType(%d)\n", This, Level, This->baseTexture.levels, FaceType);
349
    }
350 351

    return hr;
352 353
}

354
static HRESULT WINAPI IWineD3DCubeTextureImpl_LockRect(IWineD3DCubeTexture *iface, WINED3DCUBEMAP_FACES FaceType, UINT Level, WINED3DLOCKED_RECT* pLockedRect, CONST RECT* pRect, DWORD Flags) {
355
    HRESULT hr = WINED3DERR_INVALIDCALL;
356 357
    IWineD3DCubeTextureImpl *This = (IWineD3DCubeTextureImpl *)iface;

358
    if (Level < This->baseTexture.levels && FaceType <= WINED3DCUBEMAP_FACE_NEGATIVE_Z) {
359 360 361
        hr = IWineD3DSurface_LockRect(This->surfaces[FaceType][Level], pLockedRect, pRect, Flags);
    }

362
    if (WINED3D_OK == hr) {
363
        TRACE("(%p) -> faceType(%d) level(%d) returning memory@%p success(%u)\n", This, FaceType, Level, pLockedRect->pBits, hr);
364
    } else {
365
        WARN("(%p) level(%d) overflow Levels(%d)  Or FaceType(%d)\n", This, Level, This->baseTexture.levels, FaceType);
366
    }
367

368 369 370
    return hr;
}

371
static HRESULT WINAPI IWineD3DCubeTextureImpl_UnlockRect(IWineD3DCubeTexture *iface, WINED3DCUBEMAP_FACES FaceType, UINT Level) {
372
    HRESULT hr = WINED3DERR_INVALIDCALL;
373 374
    IWineD3DCubeTextureImpl *This = (IWineD3DCubeTextureImpl *)iface;

375
    if (Level < This->baseTexture.levels && FaceType <= WINED3DCUBEMAP_FACE_NEGATIVE_Z) {
376
        hr = IWineD3DSurface_UnlockRect(This->surfaces[FaceType][Level]);
377 378
    }

379
    if (WINED3D_OK == hr) {
380
        TRACE("(%p) -> faceType(%d) level(%d) success(%u)\n", This, FaceType, Level, hr);
381
    } else {
382
        WARN("(%p) level(%d) overflow Levels(%d) Or FaceType(%d)\n", This, Level, This->baseTexture.levels, FaceType);
383 384 385 386
    }
    return hr;
}

387
static HRESULT  WINAPI IWineD3DCubeTextureImpl_AddDirtyRect(IWineD3DCubeTexture *iface, WINED3DCUBEMAP_FACES FaceType, CONST RECT* pDirtyRect) {
388
    HRESULT hr = WINED3DERR_INVALIDCALL;
389
    IWineD3DCubeTextureImpl *This = (IWineD3DCubeTextureImpl *)iface;
390 391
    This->baseTexture.texture_rgb.dirty = TRUE;
    This->baseTexture.texture_srgb.dirty = TRUE;
392
    TRACE("(%p) : dirtyfication of faceType(%d) Level (0)\n", This, FaceType);
393
    if (FaceType <= WINED3DCUBEMAP_FACE_NEGATIVE_Z) {
394 395
        surface_add_dirty_rect(This->surfaces[FaceType][0], pDirtyRect);
        hr = WINED3D_OK;
396 397 398 399
    } else {
        WARN("(%p) overflow FaceType(%d)\n", This, FaceType);
    }
    return hr;
400 401
}

402
static const IWineD3DCubeTextureVtbl IWineD3DCubeTexture_Vtbl =
403
{
404
    /* IUnknown */
405 406 407
    IWineD3DCubeTextureImpl_QueryInterface,
    IWineD3DCubeTextureImpl_AddRef,
    IWineD3DCubeTextureImpl_Release,
408
    /* IWineD3DResource */
409 410 411 412 413 414 415 416
    IWineD3DCubeTextureImpl_GetParent,
    IWineD3DCubeTextureImpl_GetDevice,
    IWineD3DCubeTextureImpl_SetPrivateData,
    IWineD3DCubeTextureImpl_GetPrivateData,
    IWineD3DCubeTextureImpl_FreePrivateData,
    IWineD3DCubeTextureImpl_SetPriority,
    IWineD3DCubeTextureImpl_GetPriority,
    IWineD3DCubeTextureImpl_PreLoad,
417
    IWineD3DCubeTextureImpl_UnLoad,
418
    IWineD3DCubeTextureImpl_GetType,
419
    /* IWineD3DBaseTexture */
420 421 422 423 424 425 426 427
    IWineD3DCubeTextureImpl_SetLOD,
    IWineD3DCubeTextureImpl_GetLOD,
    IWineD3DCubeTextureImpl_GetLevelCount,
    IWineD3DCubeTextureImpl_SetAutoGenFilterType,
    IWineD3DCubeTextureImpl_GetAutoGenFilterType,
    IWineD3DCubeTextureImpl_GenerateMipSubLevels,
    IWineD3DCubeTextureImpl_SetDirty,
    IWineD3DCubeTextureImpl_GetDirty,
428
    IWineD3DCubeTextureImpl_BindTexture,
429
    IWineD3DCubeTextureImpl_GetTextureDimensions,
430
    IWineD3DCubeTextureImpl_IsCondNP2,
431
    /* IWineD3DCubeTexture */
432 433 434 435 436 437
    IWineD3DCubeTextureImpl_GetLevelDesc,
    IWineD3DCubeTextureImpl_GetCubeMapSurface,
    IWineD3DCubeTextureImpl_LockRect,
    IWineD3DCubeTextureImpl_UnlockRect,
    IWineD3DCubeTextureImpl_AddDirtyRect
};
438 439

HRESULT cubetexture_init(IWineD3DCubeTextureImpl *texture, UINT edge_length, UINT levels,
440 441
        IWineD3DDeviceImpl *device, DWORD usage, WINED3DFORMAT format, WINED3DPOOL pool,
        IUnknown *parent, const struct wined3d_parent_ops *parent_ops)
442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488
{
    const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
    const struct GlPixelFormatDesc *format_desc = getFormatDescEntry(format, gl_info);
    UINT pow2_edge_length;
    unsigned int i, j;
    UINT tmp_w;
    HRESULT hr;

    /* TODO: It should only be possible to create textures for formats
     * that are reported as supported. */
    if (WINED3DFMT_UNKNOWN >= format)
    {
        WARN("(%p) : Texture cannot be created with a format of WINED3DFMT_UNKNOWN.\n", texture);
        return WINED3DERR_INVALIDCALL;
    }

    if (!gl_info->supported[ARB_TEXTURE_CUBE_MAP] && pool != WINED3DPOOL_SCRATCH)
    {
        WARN("(%p) : Tried to create not supported cube texture.\n", texture);
        return WINED3DERR_INVALIDCALL;
    }

    /* Calculate levels for mip mapping */
    if (usage & WINED3DUSAGE_AUTOGENMIPMAP)
    {
        if (!gl_info->supported[SGIS_GENERATE_MIPMAP])
        {
            WARN("No mipmap generation support, returning D3DERR_INVALIDCALL.\n");
            return WINED3DERR_INVALIDCALL;
        }

        if (levels > 1)
        {
            WARN("D3DUSAGE_AUTOGENMIPMAP is set, and level count > 1, returning D3DERR_INVALIDCALL.\n");
            return WINED3DERR_INVALIDCALL;
        }

        levels = 1;
    }
    else if (!levels)
    {
        levels = wined3d_log2i(edge_length) + 1;
        TRACE("Calculated levels = %u.\n", levels);
    }

    texture->lpVtbl = &IWineD3DCubeTexture_Vtbl;

489 490
    hr = basetexture_init((IWineD3DBaseTextureImpl *)texture, levels, WINED3DRTYPE_CUBETEXTURE,
            device, 0, usage, format_desc, pool, parent, parent_ops);
491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555
    if (FAILED(hr))
    {
        WARN("Failed to initialize basetexture, returning %#x\n", hr);
        return hr;
    }

    /* Find the nearest pow2 match. */
    pow2_edge_length = 1;
    while (pow2_edge_length < edge_length) pow2_edge_length <<= 1;

    if (gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO] || (edge_length == pow2_edge_length))
    {
        /* Precalculated scaling for 'faked' non power of two texture coords. */
        texture->baseTexture.pow2Matrix[0] = 1.0f;
        texture->baseTexture.pow2Matrix[5] = 1.0f;
        texture->baseTexture.pow2Matrix[10] = 1.0f;
        texture->baseTexture.pow2Matrix[15] = 1.0f;
    }
    else
    {
        /* Precalculated scaling for 'faked' non power of two texture coords. */
        texture->baseTexture.pow2Matrix[0] = ((float)edge_length) / ((float)pow2_edge_length);
        texture->baseTexture.pow2Matrix[5] = ((float)edge_length) / ((float)pow2_edge_length);
        texture->baseTexture.pow2Matrix[10] = ((float)edge_length) / ((float)pow2_edge_length);
        texture->baseTexture.pow2Matrix[15] = 1.0f;
        texture->baseTexture.pow2Matrix_identity = FALSE;
    }

    /* Generate all the surfaces. */
    tmp_w = edge_length;
    for (i = 0; i < texture->baseTexture.levels; ++i)
    {
        /* Create the 6 faces. */
        for (j = 0; j < 6; ++j)
        {
            static const GLenum cube_targets[6] =
            {
                GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB,
                GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB,
                GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB,
                GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB,
                GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB,
                GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB,
            };

            hr = IWineD3DDeviceParent_CreateSurface(device->device_parent, parent, tmp_w, tmp_w,
                    format, usage, pool, i /* Level */, j, &texture->surfaces[j][i]);
            if (FAILED(hr))
            {
                FIXME("(%p) Failed to create surface, hr %#x.\n", texture, hr);
                texture->surfaces[j][i] = NULL;
                cubetexture_cleanup(texture);
                return hr;
            }

            IWineD3DSurface_SetContainer(texture->surfaces[j][i], (IWineD3DBase *)texture);
            TRACE("Created surface level %u @ %p.\n", i, texture->surfaces[j][i]);
            surface_set_texture_target(texture->surfaces[j][i], cube_targets[j]);
        }
        tmp_w = max(1, tmp_w >> 1);
    }
    texture->baseTexture.internal_preload = cubetexture_internal_preload;

    return WINED3D_OK;
}