texture.c 48.1 KB
Newer Older
1 2
/*
 * Copyright 2002-2005 Jason Edmeades
3 4
 * Copyright 2002-2005 Raphael Junqueira
 * Copyright 2005 Oliver Stieber
5
 * Copyright 2007-2008 Stefan Dösinger for CodeWeavers
6
 * Copyright 2009-2011 Henri Verbeet for CodeWeavers
7 8 9 10 11 12 13 14 15 16 17 18 19
 *
 * 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
20
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 22 23 24 25
 */

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

26
WINE_DEFAULT_DEBUG_CHANNEL(d3d_texture);
27

28
static HRESULT wined3d_texture_init(struct wined3d_texture *texture, const struct wined3d_texture_ops *texture_ops,
29
        UINT layer_count, UINT level_count, WINED3DRESOURCETYPE resource_type, struct wined3d_device *device,
30 31 32 33 34 35 36 37 38 39 40 41 42 43
        DWORD usage, const struct wined3d_format *format, WINED3DPOOL pool, void *parent,
        const struct wined3d_parent_ops *parent_ops, const struct wined3d_resource_ops *resource_ops)
{
    HRESULT hr;

    hr = resource_init(&texture->resource, device, resource_type, format,
            WINED3DMULTISAMPLE_NONE, 0, usage, pool, 0, 0, 0, 0,
            parent, parent_ops, resource_ops);
    if (FAILED(hr))
    {
        WARN("Failed to initialize resource, returning %#x\n", hr);
        return hr;
    }

44 45 46 47
    texture->texture_ops = texture_ops;
    texture->sub_resources = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
            level_count * layer_count * sizeof(*texture->sub_resources));
    if (!texture->sub_resources)
48 49 50 51 52 53
    {
        ERR("Failed to allocate sub-resource array.\n");
        resource_cleanup(&texture->resource);
        return E_OUTOFMEMORY;
    }

54 55 56 57 58 59
    texture->layer_count = layer_count;
    texture->level_count = level_count;
    texture->filter_type = (usage & WINED3DUSAGE_AUTOGENMIPMAP) ? WINED3DTEXF_LINEAR : WINED3DTEXF_NONE;
    texture->lod = 0;
    texture->texture_rgb.dirty = TRUE;
    texture->texture_srgb.dirty = TRUE;
60
    texture->flags = WINED3D_TEXTURE_POW2_MAT_IDENT;
61 62 63

    if (texture->resource.format->flags & WINED3DFMT_FLAG_FILTERING)
    {
64 65
        texture->min_mip_lookup = minMipLookup;
        texture->mag_lookup = magLookup;
66 67 68
    }
    else
    {
69 70
        texture->min_mip_lookup = minMipLookup_noFilter;
        texture->mag_lookup = magLookup_noFilter;
71 72 73 74 75 76 77 78 79 80 81 82 83 84
    }

    return WINED3D_OK;
}

/* A GL context is provided by the caller */
static void gltexture_delete(struct gl_texture *tex)
{
    ENTER_GL();
    glDeleteTextures(1, &tex->name);
    LEAVE_GL();
    tex->name = 0;
}

85
static void wined3d_texture_unload(struct wined3d_texture *texture)
86
{
87
    struct wined3d_device *device = texture->resource.device;
88 89
    struct wined3d_context *context = NULL;

90
    if (texture->texture_rgb.name || texture->texture_srgb.name)
91 92 93 94
    {
        context = context_acquire(device, NULL);
    }

95 96
    if (texture->texture_rgb.name)
        gltexture_delete(&texture->texture_rgb);
97

98 99
    if (texture->texture_srgb.name)
        gltexture_delete(&texture->texture_srgb);
100 101 102

    if (context) context_release(context);

103
    wined3d_texture_set_dirty(texture, TRUE);
104 105 106 107

    resource_unload(&texture->resource);
}

108
static void wined3d_texture_cleanup(struct wined3d_texture *texture)
109
{
110
    UINT sub_count = texture->level_count * texture->layer_count;
111 112 113 114 115 116
    UINT i;

    TRACE("texture %p.\n", texture);

    for (i = 0; i < sub_count; ++i)
    {
117
        struct wined3d_resource *sub_resource = texture->sub_resources[i];
118 119

        if (sub_resource)
120
            texture->texture_ops->texture_sub_resource_cleanup(sub_resource);
121 122
    }

123
    wined3d_texture_unload(texture);
124
    HeapFree(GetProcessHeap(), 0, texture->sub_resources);
125 126 127
    resource_cleanup(&texture->resource);
}

128
void wined3d_texture_set_dirty(struct wined3d_texture *texture, BOOL dirty)
129
{
130 131
    texture->texture_rgb.dirty = dirty;
    texture->texture_srgb.dirty = dirty;
132 133
}

134
/* Context activation is done by the caller. */
135
static HRESULT wined3d_texture_bind(struct wined3d_texture *texture,
136 137 138 139 140 141 142 143 144
        const struct wined3d_gl_info *gl_info, BOOL srgb, BOOL *set_surface_desc)
{
    struct gl_texture *gl_tex;
    BOOL new_texture = FALSE;
    HRESULT hr = WINED3D_OK;
    GLenum target;

    TRACE("texture %p, srgb %#x, set_surface_desc %p.\n", texture, srgb, set_surface_desc);

145 146 147 148 149 150
    /* sRGB mode cache for preload() calls outside drawprim. */
    if (srgb)
        texture->flags |= WINED3D_TEXTURE_IS_SRGB;
    else
        texture->flags &= ~WINED3D_TEXTURE_IS_SRGB;

151
    gl_tex = wined3d_texture_get_gl_texture(texture, gl_info, srgb);
152
    target = texture->target;
153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183

    ENTER_GL();
    /* Generate a texture name if we don't already have one. */
    if (!gl_tex->name)
    {
        *set_surface_desc = TRUE;
        glGenTextures(1, &gl_tex->name);
        checkGLcall("glGenTextures");
        TRACE("Generated texture %d.\n", gl_tex->name);
        if (texture->resource.pool == WINED3DPOOL_DEFAULT)
        {
            /* Tell OpenGL to try and keep this texture in video ram (well mostly). */
            GLclampf tmp = 0.9f;
            glPrioritizeTextures(1, &gl_tex->name, &tmp);
        }
        /* Initialise the state of the texture object to the OpenGL defaults,
         * not the D3D defaults. */
        gl_tex->states[WINED3DTEXSTA_ADDRESSU] = WINED3DTADDRESS_WRAP;
        gl_tex->states[WINED3DTEXSTA_ADDRESSV] = WINED3DTADDRESS_WRAP;
        gl_tex->states[WINED3DTEXSTA_ADDRESSW] = WINED3DTADDRESS_WRAP;
        gl_tex->states[WINED3DTEXSTA_BORDERCOLOR] = 0;
        gl_tex->states[WINED3DTEXSTA_MAGFILTER] = WINED3DTEXF_LINEAR;
        gl_tex->states[WINED3DTEXSTA_MINFILTER] = WINED3DTEXF_POINT; /* GL_NEAREST_MIPMAP_LINEAR */
        gl_tex->states[WINED3DTEXSTA_MIPFILTER] = WINED3DTEXF_LINEAR; /* GL_NEAREST_MIPMAP_LINEAR */
        gl_tex->states[WINED3DTEXSTA_MAXMIPLEVEL] = 0;
        gl_tex->states[WINED3DTEXSTA_MAXANISOTROPY] = 1;
        if (gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
            gl_tex->states[WINED3DTEXSTA_SRGBTEXTURE] = TRUE;
        else
            gl_tex->states[WINED3DTEXSTA_SRGBTEXTURE] = srgb;
        gl_tex->states[WINED3DTEXSTA_SHADOW] = FALSE;
184
        wined3d_texture_set_dirty(texture, TRUE);
185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216
        new_texture = TRUE;

        if (texture->resource.usage & WINED3DUSAGE_AUTOGENMIPMAP)
        {
            /* This means double binding the texture at creation, but keeps
             * the code simpler all in all, and the run-time path free from
             * additional checks. */
            glBindTexture(target, gl_tex->name);
            checkGLcall("glBindTexture");
            glTexParameteri(target, GL_GENERATE_MIPMAP_SGIS, GL_TRUE);
            checkGLcall("glTexParameteri(target, GL_GENERATE_MIPMAP_SGIS, GL_TRUE)");
        }
    }
    else
    {
        *set_surface_desc = FALSE;
    }

    if (gl_tex->name)
    {
        glBindTexture(target, gl_tex->name);
        checkGLcall("glBindTexture");
        if (new_texture)
        {
            /* For a new texture we have to set the texture levels after
             * binding the texture. Beware that texture rectangles do not
             * support mipmapping, but set the maxmiplevel if we're relying
             * on the partial GL_ARB_texture_non_power_of_two emulation with
             * texture rectangles. (I.e., do not care about cond_np2 here,
             * just look for GL_TEXTURE_RECTANGLE_ARB.) */
            if (target != GL_TEXTURE_RECTANGLE_ARB)
            {
217 218 219
                TRACE("Setting GL_TEXTURE_MAX_LEVEL to %u.\n", texture->level_count - 1);
                glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, texture->level_count - 1);
                checkGLcall("glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, texture->level_count)");
220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264
            }
            if (target == GL_TEXTURE_CUBE_MAP_ARB)
            {
                /* Cubemaps are always set to clamp, regardless of the sampler state. */
                glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
                glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
                glTexParameteri(target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
            }
        }
    }
    else
    {
        ERR("This texture doesn't have an OpenGL texture assigned to it.\n");
        hr = WINED3DERR_INVALIDCALL;
    }

    LEAVE_GL();
    return hr;
}

/* GL locking is done by the caller */
static void apply_wrap(const struct wined3d_gl_info *gl_info, GLenum target,
        WINED3DTEXTUREADDRESS d3d_wrap, GLenum param, BOOL cond_np2)
{
    GLint gl_wrap;

    if (d3d_wrap < WINED3DTADDRESS_WRAP || d3d_wrap > WINED3DTADDRESS_MIRRORONCE)
    {
        FIXME("Unrecognized or unsupported WINED3DTEXTUREADDRESS %#x.\n", d3d_wrap);
        return;
    }

    /* Cubemaps are always set to clamp, regardless of the sampler state. */
    if (target == GL_TEXTURE_CUBE_MAP_ARB
            || (cond_np2 && d3d_wrap == WINED3DTADDRESS_WRAP))
        gl_wrap = GL_CLAMP_TO_EDGE;
    else
        gl_wrap = gl_info->wrap_lookup[d3d_wrap - WINED3DTADDRESS_WRAP];

    TRACE("Setting param %#x to %#x for target %#x.\n", param, gl_wrap, target);
    glTexParameteri(target, param, gl_wrap);
    checkGLcall("glTexParameteri(target, param, gl_wrap)");
}

/* GL locking is done by the caller (state handler) */
265
void wined3d_texture_apply_state_changes(struct wined3d_texture *texture,
266 267 268
        const DWORD sampler_states[WINED3D_HIGHEST_SAMPLER_STATE + 1],
        const struct wined3d_gl_info *gl_info)
{
269
    BOOL cond_np2 = texture->flags & WINED3D_TEXTURE_COND_NP2;
270
    GLenum target = texture->target;
271 272 273 274 275 276
    struct gl_texture *gl_tex;
    DWORD state;
    DWORD aniso;

    TRACE("texture %p, sampler_states %p.\n", texture, sampler_states);

277 278
    gl_tex = wined3d_texture_get_gl_texture(texture, gl_info,
            texture->flags & WINED3D_TEXTURE_IS_SRGB);
279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322

    /* This function relies on the correct texture being bound and loaded. */

    if (sampler_states[WINED3DSAMP_ADDRESSU] != gl_tex->states[WINED3DTEXSTA_ADDRESSU])
    {
        state = sampler_states[WINED3DSAMP_ADDRESSU];
        apply_wrap(gl_info, target, state, GL_TEXTURE_WRAP_S, cond_np2);
        gl_tex->states[WINED3DTEXSTA_ADDRESSU] = state;
    }

    if (sampler_states[WINED3DSAMP_ADDRESSV] != gl_tex->states[WINED3DTEXSTA_ADDRESSV])
    {
        state = sampler_states[WINED3DSAMP_ADDRESSV];
        apply_wrap(gl_info, target, state, GL_TEXTURE_WRAP_T, cond_np2);
        gl_tex->states[WINED3DTEXSTA_ADDRESSV] = state;
    }

    if (sampler_states[WINED3DSAMP_ADDRESSW] != gl_tex->states[WINED3DTEXSTA_ADDRESSW])
    {
        state = sampler_states[WINED3DSAMP_ADDRESSW];
        apply_wrap(gl_info, target, state, GL_TEXTURE_WRAP_R, cond_np2);
        gl_tex->states[WINED3DTEXSTA_ADDRESSW] = state;
    }

    if (sampler_states[WINED3DSAMP_BORDERCOLOR] != gl_tex->states[WINED3DTEXSTA_BORDERCOLOR])
    {
        float col[4];

        state = sampler_states[WINED3DSAMP_BORDERCOLOR];
        D3DCOLORTOGLFLOAT4(state, col);
        TRACE("Setting border color for %#x to %#x.\n", target, state);
        glTexParameterfv(target, GL_TEXTURE_BORDER_COLOR, &col[0]);
        checkGLcall("glTexParameterfv(..., GL_TEXTURE_BORDER_COLOR, ...)");
        gl_tex->states[WINED3DTEXSTA_BORDERCOLOR] = state;
    }

    if (sampler_states[WINED3DSAMP_MAGFILTER] != gl_tex->states[WINED3DTEXSTA_MAGFILTER])
    {
        GLint gl_value;

        state = sampler_states[WINED3DSAMP_MAGFILTER];
        if (state > WINED3DTEXF_ANISOTROPIC)
            FIXME("Unrecognized or unsupported MAGFILTER* value %d.\n", state);

323
        gl_value = wined3d_gl_mag_filter(texture->mag_lookup,
324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347
                min(max(state, WINED3DTEXF_POINT), WINED3DTEXF_LINEAR));
        TRACE("ValueMAG=%#x setting MAGFILTER to %#x.\n", state, gl_value);
        glTexParameteri(target, GL_TEXTURE_MAG_FILTER, gl_value);

        gl_tex->states[WINED3DTEXSTA_MAGFILTER] = state;
    }

    if ((sampler_states[WINED3DSAMP_MINFILTER] != gl_tex->states[WINED3DTEXSTA_MINFILTER]
            || sampler_states[WINED3DSAMP_MIPFILTER] != gl_tex->states[WINED3DTEXSTA_MIPFILTER]
            || sampler_states[WINED3DSAMP_MAXMIPLEVEL] != gl_tex->states[WINED3DTEXSTA_MAXMIPLEVEL]))
    {
        GLint gl_value;

        gl_tex->states[WINED3DTEXSTA_MIPFILTER] = sampler_states[WINED3DSAMP_MIPFILTER];
        gl_tex->states[WINED3DTEXSTA_MINFILTER] = sampler_states[WINED3DSAMP_MINFILTER];
        gl_tex->states[WINED3DTEXSTA_MAXMIPLEVEL] = sampler_states[WINED3DSAMP_MAXMIPLEVEL];

        if (gl_tex->states[WINED3DTEXSTA_MINFILTER] > WINED3DTEXF_ANISOTROPIC
            || gl_tex->states[WINED3DTEXSTA_MIPFILTER] > WINED3DTEXF_ANISOTROPIC)
        {
            FIXME("Unrecognized or unsupported D3DSAMP_MINFILTER value %#x D3DSAMP_MIPFILTER value %#x.\n",
                  gl_tex->states[WINED3DTEXSTA_MINFILTER],
                  gl_tex->states[WINED3DTEXSTA_MIPFILTER]);
        }
348
        gl_value = wined3d_gl_min_mip_filter(texture->min_mip_lookup,
349 350 351 352 353 354 355 356 357 358 359 360
                min(max(sampler_states[WINED3DSAMP_MINFILTER], WINED3DTEXF_POINT), WINED3DTEXF_LINEAR),
                min(max(sampler_states[WINED3DSAMP_MIPFILTER], WINED3DTEXF_NONE), WINED3DTEXF_LINEAR));

        TRACE("ValueMIN=%#x, ValueMIP=%#x, setting MINFILTER to %#x.\n",
              sampler_states[WINED3DSAMP_MINFILTER],
              sampler_states[WINED3DSAMP_MIPFILTER], gl_value);
        glTexParameteri(target, GL_TEXTURE_MIN_FILTER, gl_value);
        checkGLcall("glTexParameter GL_TEXTURE_MIN_FILTER, ...");

        if (!cond_np2)
        {
            if (gl_tex->states[WINED3DTEXSTA_MIPFILTER] == WINED3DTEXF_NONE)
361 362 363 364 365 366
                gl_value = texture->lod;
            else if (gl_tex->states[WINED3DTEXSTA_MAXMIPLEVEL] >= texture->level_count)
                gl_value = texture->level_count - 1;
            else if (gl_tex->states[WINED3DTEXSTA_MAXMIPLEVEL] < texture->lod)
                /* texture->lod is already clamped in the setter. */
                gl_value = texture->lod;
367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426
            else
                gl_value = gl_tex->states[WINED3DTEXSTA_MAXMIPLEVEL];

            /* Note that WINED3DSAMP_MAXMIPLEVEL specifies the largest mipmap
             * (default 0), while GL_TEXTURE_MAX_LEVEL specifies the smallest
             * mimap used (default 1000). So WINED3DSAMP_MAXMIPLEVEL
             * corresponds to GL_TEXTURE_BASE_LEVEL. */
            glTexParameteri(target, GL_TEXTURE_BASE_LEVEL, gl_value);
        }
    }

    if ((gl_tex->states[WINED3DTEXSTA_MAGFILTER] != WINED3DTEXF_ANISOTROPIC
         && gl_tex->states[WINED3DTEXSTA_MINFILTER] != WINED3DTEXF_ANISOTROPIC
         && gl_tex->states[WINED3DTEXSTA_MIPFILTER] != WINED3DTEXF_ANISOTROPIC)
            || cond_np2)
        aniso = 1;
    else
        aniso = sampler_states[WINED3DSAMP_MAXANISOTROPY];

    if (gl_tex->states[WINED3DTEXSTA_MAXANISOTROPY] != aniso)
    {
        if (gl_info->supported[EXT_TEXTURE_FILTER_ANISOTROPIC])
        {
            glTexParameteri(target, GL_TEXTURE_MAX_ANISOTROPY_EXT, aniso);
            checkGLcall("glTexParameteri(GL_TEXTURE_MAX_ANISOTROPY_EXT, aniso)");
        }
        else
        {
            WARN("Anisotropic filtering not supported.\n");
        }
        gl_tex->states[WINED3DTEXSTA_MAXANISOTROPY] = aniso;
    }

    /* These should always be the same unless EXT_texture_sRGB_decode is supported. */
    if (sampler_states[WINED3DSAMP_SRGBTEXTURE] != gl_tex->states[WINED3DTEXSTA_SRGBTEXTURE])
    {
        glTexParameteri(target, GL_TEXTURE_SRGB_DECODE_EXT,
                sampler_states[WINED3DSAMP_SRGBTEXTURE] ? GL_DECODE_EXT : GL_SKIP_DECODE_EXT);
        checkGLcall("glTexParameteri(GL_TEXTURE_SRGB_DECODE_EXT)");
    }

    if (!(texture->resource.format->flags & WINED3DFMT_FLAG_SHADOW)
            != !gl_tex->states[WINED3DTEXSTA_SHADOW])
    {
        if (texture->resource.format->flags & WINED3DFMT_FLAG_SHADOW)
        {
            glTexParameteri(target, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);
            glTexParameteri(target, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE_ARB);
            checkGLcall("glTexParameteri(target, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE_ARB)");
            gl_tex->states[WINED3DTEXSTA_SHADOW] = TRUE;
        }
        else
        {
            glTexParameteri(target, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);
            checkGLcall("glTexParameteri(target, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE)");
            gl_tex->states[WINED3DTEXSTA_SHADOW] = FALSE;
        }
    }
}

427
ULONG CDECL wined3d_texture_incref(struct wined3d_texture *texture)
428 429 430 431 432 433 434 435 436
{
    ULONG refcount = InterlockedIncrement(&texture->resource.ref);

    TRACE("%p increasing refcount to %u.\n", texture, refcount);

    return refcount;
}

/* Do not call while under the GL lock. */
437
ULONG CDECL wined3d_texture_decref(struct wined3d_texture *texture)
438 439 440 441 442 443 444
{
    ULONG refcount = InterlockedDecrement(&texture->resource.ref);

    TRACE("%p decreasing refcount to %u.\n", texture, refcount);

    if (!refcount)
    {
445
        wined3d_texture_cleanup(texture);
446 447 448 449 450 451 452
        texture->resource.parent_ops->wined3d_object_destroyed(texture->resource.parent);
        HeapFree(GetProcessHeap(), 0, texture);
    }

    return refcount;
}

453 454 455 456 457 458 459
struct wined3d_resource * CDECL wined3d_texture_get_resource(struct wined3d_texture *texture)
{
    TRACE("texture %p.\n", texture);

    return &texture->resource;
}

460
DWORD CDECL wined3d_texture_set_priority(struct wined3d_texture *texture, DWORD priority)
461
{
462
    return resource_set_priority(&texture->resource, priority);
463 464
}

465
DWORD CDECL wined3d_texture_get_priority(const struct wined3d_texture *texture)
466
{
467
    return resource_get_priority(&texture->resource);
468 469 470
}

/* Do not call while under the GL lock. */
471
void CDECL wined3d_texture_preload(struct wined3d_texture *texture)
472
{
473
    texture->texture_ops->texture_preload(texture, SRGB_ANY);
474 475
}

476
void * CDECL wined3d_texture_get_parent(const struct wined3d_texture *texture)
477
{
478
    TRACE("texture %p.\n", texture);
479

480
    return texture->resource.parent;
481 482
}

483
DWORD CDECL wined3d_texture_set_lod(struct wined3d_texture *texture, DWORD lod)
484
{
485
    DWORD old = texture->lod;
486

487
    TRACE("texture %p, lod %u.\n", texture, lod);
488 489 490 491 492 493 494 495 496

    /* The d3d9:texture test shows that SetLOD is ignored on non-managed
     * textures. The call always returns 0, and GetLOD always returns 0. */
    if (texture->resource.pool != WINED3DPOOL_MANAGED)
    {
        TRACE("Ignoring SetLOD on %s texture, returning 0.\n", debug_d3dpool(texture->resource.pool));
        return 0;
    }

497 498
    if (lod >= texture->level_count)
        lod = texture->level_count - 1;
499

500
    if (texture->lod != lod)
501
    {
502
        texture->lod = lod;
503

504 505 506
        texture->texture_rgb.states[WINED3DTEXSTA_MAXMIPLEVEL] = ~0U;
        texture->texture_srgb.states[WINED3DTEXSTA_MAXMIPLEVEL] = ~0U;
        if (texture->bind_count)
507
            device_invalidate_state(texture->resource.device, STATE_SAMPLER(texture->sampler));
508 509 510 511 512
    }

    return old;
}

513
DWORD CDECL wined3d_texture_get_lod(const struct wined3d_texture *texture)
514
{
515
    TRACE("texture %p, returning %u.\n", texture, texture->lod);
516

517
    return texture->lod;
518 519
}

520
DWORD CDECL wined3d_texture_get_level_count(const struct wined3d_texture *texture)
521
{
522
    TRACE("texture %p, returning %u.\n", texture, texture->level_count);
523

524
    return texture->level_count;
525 526
}

527
HRESULT CDECL wined3d_texture_set_autogen_filter_type(struct wined3d_texture *texture,
528 529
        WINED3DTEXTUREFILTERTYPE filter_type)
{
530
    FIXME("texture %p, filter_type %s stub!\n", texture, debug_d3dtexturefiltertype(filter_type));
531 532 533 534 535 536 537

    if (!(texture->resource.usage & WINED3DUSAGE_AUTOGENMIPMAP))
    {
        WARN("Texture doesn't have AUTOGENMIPMAP usage.\n");
        return WINED3DERR_INVALIDCALL;
    }

538
    texture->filter_type = filter_type;
539 540 541 542

    return WINED3D_OK;
}

543
WINED3DTEXTUREFILTERTYPE CDECL wined3d_texture_get_autogen_filter_type(const struct wined3d_texture *texture)
544
{
545
    TRACE("texture %p.\n", texture);
546

547
    return texture->filter_type;
548 549
}

550
void CDECL wined3d_texture_generate_mipmaps(struct wined3d_texture *texture)
551 552
{
    /* TODO: Implement filters using GL_SGI_generate_mipmaps. */
553
    FIXME("texture %p stub!\n", texture);
554 555
}

556
struct wined3d_resource * CDECL wined3d_texture_get_sub_resource(struct wined3d_texture *texture,
557 558
        UINT sub_resource_idx)
{
559
    UINT sub_count = texture->level_count * texture->layer_count;
560 561 562 563 564 565 566 567

    TRACE("texture %p, sub_resource_idx %u.\n", texture, sub_resource_idx);

    if (sub_resource_idx >= sub_count)
    {
        WARN("sub_resource_idx %u >= sub_count %u.\n", sub_resource_idx, sub_count);
        return NULL;
    }
568

569
    return texture->sub_resources[sub_resource_idx];
570 571
}

572
HRESULT CDECL wined3d_texture_add_dirty_region(struct wined3d_texture *texture,
573 574 575 576 577 578
        UINT layer, const WINED3DBOX *dirty_region)
{
    struct wined3d_resource *sub_resource;

    TRACE("texture %p, layer %u, dirty_region %p.\n", texture, layer, dirty_region);

579
    if (!(sub_resource = wined3d_texture_get_sub_resource(texture, layer * texture->level_count)))
580 581 582 583 584
    {
        WARN("Failed to get sub-resource.\n");
        return WINED3DERR_INVALIDCALL;
    }

585
    wined3d_texture_set_dirty(texture, TRUE);
586
    texture->texture_ops->texture_sub_resource_add_dirty_region(sub_resource, dirty_region);
587 588 589 590 591

    return WINED3D_OK;
}

/* Context activation is done by the caller. */
592
static HRESULT texture2d_bind(struct wined3d_texture *texture,
593
        const struct wined3d_gl_info *gl_info, BOOL srgb)
594 595 596 597
{
    BOOL set_gl_texture_desc;
    HRESULT hr;

598
    TRACE("texture %p, gl_info %p, srgb %#x.\n", texture, gl_info, srgb);
599

600
    hr = wined3d_texture_bind(texture, gl_info, srgb, &set_gl_texture_desc);
601 602
    if (set_gl_texture_desc && SUCCEEDED(hr))
    {
603
        UINT sub_count = texture->level_count * texture->layer_count;
604 605
        BOOL srgb_tex = !gl_info->supported[EXT_TEXTURE_SRGB_DECODE]
                && (texture->flags & WINED3D_TEXTURE_IS_SRGB);
606
        struct gl_texture *gl_tex;
607
        UINT i;
608

609
        gl_tex = wined3d_texture_get_gl_texture(texture, gl_info, srgb_tex);
610

611
        for (i = 0; i < sub_count; ++i)
612
        {
613
            struct wined3d_surface *surface = surface_from_resource(texture->sub_resources[i]);
614
            surface_set_texture_name(surface, gl_tex->name, srgb_tex);
615 616 617 618 619 620 621 622 623 624
        }

        /* Conditinal non power of two textures use a different clamping
         * default. If we're using the GL_WINE_normalized_texrect partial
         * driver emulation, we're dealing with a GL_TEXTURE_2D texture which
         * has the address mode set to repeat - something that prevents us
         * from hitting the accelerated codepath. Thus manually set the GL
         * state. The same applies to filtering. Even if the texture has only
         * one mip level, the default LINEAR_MIPMAP_LINEAR filter causes a SW
         * fallback on macos. */
625
        if (texture->flags & WINED3D_TEXTURE_COND_NP2)
626
        {
627
            GLenum target = texture->target;
628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649

            ENTER_GL();
            glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
            checkGLcall("glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)");
            glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
            checkGLcall("glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)");
            glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
            checkGLcall("glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST)");
            glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
            checkGLcall("glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST)");
            LEAVE_GL();
            gl_tex->states[WINED3DTEXSTA_ADDRESSU]      = WINED3DTADDRESS_CLAMP;
            gl_tex->states[WINED3DTEXSTA_ADDRESSV]      = WINED3DTADDRESS_CLAMP;
            gl_tex->states[WINED3DTEXSTA_MAGFILTER]     = WINED3DTEXF_POINT;
            gl_tex->states[WINED3DTEXSTA_MINFILTER]     = WINED3DTEXF_POINT;
            gl_tex->states[WINED3DTEXSTA_MIPFILTER]     = WINED3DTEXF_NONE;
        }
    }

    return hr;
}

650 651 652 653 654 655 656 657 658 659 660 661 662 663 664
static BOOL texture_srgb_mode(struct wined3d_texture *texture, enum WINED3DSRGB srgb)
{
    switch (srgb)
    {
        case SRGB_RGB:
            return FALSE;

        case SRGB_SRGB:
            return TRUE;

        default:
            return texture->flags & WINED3D_TEXTURE_IS_SRGB;
    }
}

665
/* Do not call while under the GL lock. */
666
static void texture2d_preload(struct wined3d_texture *texture, enum WINED3DSRGB srgb)
667
{
668
    UINT sub_count = texture->level_count * texture->layer_count;
669
    struct wined3d_device *device = texture->resource.device;
670
    const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
671
    struct wined3d_context *context = NULL;
672
    struct gl_texture *gl_tex;
673
    BOOL srgb_mode;
674
    UINT i;
675

676
    TRACE("texture %p, srgb %#x.\n", texture, srgb);
677

678
    srgb_mode = texture_srgb_mode(texture, srgb);
679
    gl_tex = wined3d_texture_get_gl_texture(texture, gl_info, srgb_mode);
680 681 682

    if (!device->isInDraw)
    {
683 684
        /* No danger of recursive calls, context_acquire() sets isInDraw to TRUE
         * when loading offscreen render targets into the texture. */
685
        context = context_acquire(device, NULL);
686 687
    }

688 689
    if (texture->resource.format->id == WINED3DFMT_P8_UINT
            || texture->resource.format->id == WINED3DFMT_P8_UINT_A8_UNORM)
690
    {
691
        for (i = 0; i < sub_count; ++i)
692
        {
693
            struct wined3d_surface *surface = surface_from_resource(texture->sub_resources[i]);
694

695
            if (palette9_changed(surface))
696
            {
697
                TRACE("Reloading surface %p because the d3d8/9 palette was changed.\n", surface);
698
                /* TODO: This is not necessarily needed with hw palettized texture support. */
699
                surface_load_location(surface, SFLAG_INSYSMEM, NULL);
700 701
                /* Make sure the texture is reloaded because of the palette
                 * change, this kills performance though :( */
702
                surface_modify_location(surface, SFLAG_INTEXTURE, FALSE);
703 704 705 706
            }
        }
    }

707
    if (gl_tex->dirty)
708
    {
709 710
        /* Reload the surfaces if the texture is marked dirty. */
        for (i = 0; i < sub_count; ++i)
711
        {
712
            surface_load(surface_from_resource(texture->sub_resources[i]), srgb_mode);
713 714 715 716
        }
    }
    else
    {
717
        TRACE("Texture %p not dirty, nothing to do.\n", texture);
718 719 720
    }

    /* No longer dirty. */
721
    gl_tex->dirty = FALSE;
722 723

    if (context) context_release(context);
724
}
725

726
static void texture2d_sub_resource_add_dirty_region(struct wined3d_resource *sub_resource,
727 728 729 730 731
        const WINED3DBOX *dirty_region)
{
    surface_add_dirty_rect(surface_from_resource(sub_resource), dirty_region);
}

732
static void texture2d_sub_resource_cleanup(struct wined3d_resource *sub_resource)
733
{
734
    struct wined3d_surface *surface = surface_from_resource(sub_resource);
735 736

    /* Clean out the texture name we gave to the surface so that the
737
     * surface doesn't try and release it. */
738 739 740 741
    surface_set_texture_name(surface, 0, TRUE);
    surface_set_texture_name(surface, 0, FALSE);
    surface_set_texture_target(surface, 0);
    surface_set_container(surface, WINED3D_CONTAINER_NONE, NULL);
742
    wined3d_surface_decref(surface);
743 744
}

745
/* Do not call while under the GL lock. */
746
static void texture2d_unload(struct wined3d_resource *resource)
747
{
748
    struct wined3d_texture *texture = wined3d_texture_from_resource(resource);
749
    UINT sub_count = texture->level_count * texture->layer_count;
750
    UINT i;
751 752 753

    TRACE("texture %p.\n", texture);

754
    for (i = 0; i < sub_count; ++i)
755
    {
756
        struct wined3d_resource *sub_resource = texture->sub_resources[i];
757
        struct wined3d_surface *surface = surface_from_resource(sub_resource);
758

759
        sub_resource->resource_ops->resource_unload(sub_resource);
760 761
        surface_set_texture_name(surface, 0, FALSE); /* Delete RGB name */
        surface_set_texture_name(surface, 0, TRUE); /* Delete sRGB name */
762 763
    }

764
    wined3d_texture_unload(texture);
765 766
}

767
static const struct wined3d_texture_ops texture2d_ops =
768
{
769 770 771 772 773
    texture2d_bind,
    texture2d_preload,
    texture2d_sub_resource_add_dirty_region,
    texture2d_sub_resource_cleanup,
};
774

775
static const struct wined3d_resource_ops texture2d_resource_ops =
776
{
777 778
    texture2d_unload,
};
779

780
static HRESULT cubetexture_init(struct wined3d_texture *texture, UINT edge_length, UINT levels,
781
        struct wined3d_device *device, DWORD usage, enum wined3d_format_id format_id, WINED3DPOOL pool,
782
        void *parent, const struct wined3d_parent_ops *parent_ops)
783
{
784 785 786 787 788 789
    const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
    const struct wined3d_format *format = wined3d_get_format(gl_info, format_id);
    UINT pow2_edge_length;
    unsigned int i, j;
    UINT tmp_w;
    HRESULT hr;
790

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

799 800 801 802 803
    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;
    }
804

805 806 807 808 809 810 811 812
    /* 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;
        }
813

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

820 821 822 823 824 825 826
        levels = 1;
    }
    else if (!levels)
    {
        levels = wined3d_log2i(edge_length) + 1;
        TRACE("Calculated levels = %u.\n", levels);
    }
827

828 829
    hr = wined3d_texture_init(texture, &texture2d_ops, 6, levels,
            WINED3DRTYPE_CUBETEXTURE, device, usage, format, pool,
830 831 832
            parent, parent_ops, &texture2d_resource_ops);
    if (FAILED(hr))
    {
833
        WARN("Failed to initialize texture, returning %#x\n", hr);
834 835
        return hr;
    }
836

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

841 842 843
    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. */
844 845 846 847
        texture->pow2_matrix[0] = 1.0f;
        texture->pow2_matrix[5] = 1.0f;
        texture->pow2_matrix[10] = 1.0f;
        texture->pow2_matrix[15] = 1.0f;
848 849 850 851
    }
    else
    {
        /* Precalculated scaling for 'faked' non power of two texture coords. */
852 853 854 855
        texture->pow2_matrix[0] = ((float)edge_length) / ((float)pow2_edge_length);
        texture->pow2_matrix[5] = ((float)edge_length) / ((float)pow2_edge_length);
        texture->pow2_matrix[10] = ((float)edge_length) / ((float)pow2_edge_length);
        texture->pow2_matrix[15] = 1.0f;
856
        texture->flags &= ~WINED3D_TEXTURE_POW2_MAT_IDENT;
857
    }
858
    texture->target = GL_TEXTURE_CUBE_MAP_ARB;
859

860 861
    /* Generate all the surfaces. */
    tmp_w = edge_length;
862
    for (i = 0; i < texture->level_count; ++i)
863 864 865 866 867 868 869 870 871 872 873 874 875
    {
        /* 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,
            };
876
            UINT idx = j * texture->level_count + i;
877
            struct wined3d_surface *surface;
878

879
            hr = device->device_parent->ops->create_surface(device->device_parent, parent, tmp_w, tmp_w,
880 881 882 883
                    format_id, usage, pool, i /* Level */, j, &surface);
            if (FAILED(hr))
            {
                FIXME("(%p) Failed to create surface, hr %#x.\n", texture, hr);
884
                wined3d_texture_cleanup(texture);
885 886
                return hr;
            }
887

888 889 890
            surface_set_container(surface, WINED3D_CONTAINER_TEXTURE, texture);
            surface_set_texture_target(surface, cube_targets[j]);
            texture->sub_resources[idx] = &surface->resource;
891 892 893 894
            TRACE("Created surface level %u @ %p.\n", i, surface);
        }
        tmp_w = max(1, tmp_w >> 1);
    }
895

896
    return WINED3D_OK;
897 898
}

899
static HRESULT texture_init(struct wined3d_texture *texture, UINT width, UINT height, UINT levels,
900
        struct wined3d_device *device, DWORD usage, enum wined3d_format_id format_id, WINED3DPOOL pool,
901
        void *parent, const struct wined3d_parent_ops *parent_ops)
902 903
{
    const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
904
    const struct wined3d_format *format = wined3d_get_format(gl_info, format_id);
905 906 907 908 909 910 911
    UINT pow2_width, pow2_height;
    UINT tmp_w, tmp_h;
    unsigned int i;
    HRESULT hr;

    /* TODO: It should only be possible to create textures for formats
     * that are reported as supported. */
912
    if (WINED3DFMT_UNKNOWN >= format_id)
913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964
    {
        WARN("(%p) : Texture cannot be created with a format of WINED3DFMT_UNKNOWN.\n", texture);
        return WINED3DERR_INVALIDCALL;
    }

    /* Non-power2 support. */
    if (gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO])
    {
        pow2_width = width;
        pow2_height = height;
    }
    else
    {
        /* Find the nearest pow2 match. */
        pow2_width = pow2_height = 1;
        while (pow2_width < width) pow2_width <<= 1;
        while (pow2_height < height) pow2_height <<= 1;

        if (pow2_width != width || pow2_height != height)
        {
            if (levels > 1)
            {
                WARN("Attempted to create a mipmapped np2 texture without unconditional np2 support.\n");
                return WINED3DERR_INVALIDCALL;
            }
            levels = 1;
        }
    }

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

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

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

965 966
    hr = wined3d_texture_init(texture, &texture2d_ops, 1, levels,
            WINED3DRTYPE_TEXTURE, device, usage, format, pool,
967
            parent, parent_ops, &texture2d_resource_ops);
968 969
    if (FAILED(hr))
    {
970
        WARN("Failed to initialize texture, returning %#x.\n", hr);
971 972 973 974 975 976 977
        return hr;
    }

    /* Precalculated scaling for 'faked' non power of two texture coords.
     * Second also don't use ARB_TEXTURE_RECTANGLE in case the surface format is P8 and EXT_PALETTED_TEXTURE
     * is used in combination with texture uploads (RTL_READTEX). The reason is that EXT_PALETTED_TEXTURE
     * doesn't work in combination with ARB_TEXTURE_RECTANGLE. */
978
    if (gl_info->supported[WINED3D_GL_NORMALIZED_TEXRECT] && (width != pow2_width || height != pow2_height))
979
    {
980 981 982 983 984
        texture->pow2_matrix[0] = 1.0f;
        texture->pow2_matrix[5] = 1.0f;
        texture->pow2_matrix[10] = 1.0f;
        texture->pow2_matrix[15] = 1.0f;
        texture->target = GL_TEXTURE_2D;
985
        texture->flags |= WINED3D_TEXTURE_COND_NP2;
986
        texture->min_mip_lookup = minMipLookup_noFilter;
987 988
    }
    else if (gl_info->supported[ARB_TEXTURE_RECTANGLE] && (width != pow2_width || height != pow2_height)
989
            && !(format->id == WINED3DFMT_P8_UINT && gl_info->supported[EXT_PALETTED_TEXTURE]
990
            && wined3d_settings.rendertargetlock_mode == RTL_READTEX))
991
    {
992
        if (width != 1 || height != 1)
993
            texture->flags &= ~WINED3D_TEXTURE_POW2_MAT_IDENT;
994

995 996 997 998 999
        texture->pow2_matrix[0] = (float)width;
        texture->pow2_matrix[5] = (float)height;
        texture->pow2_matrix[10] = 1.0f;
        texture->pow2_matrix[15] = 1.0f;
        texture->target = GL_TEXTURE_RECTANGLE_ARB;
1000
        texture->flags |= WINED3D_TEXTURE_COND_NP2;
1001

1002
        if (texture->resource.format->flags & WINED3DFMT_FLAG_FILTERING)
1003
            texture->min_mip_lookup = minMipLookup_noMip;
1004
        else
1005
            texture->min_mip_lookup = minMipLookup_noFilter;
1006 1007 1008 1009 1010
    }
    else
    {
        if ((width != pow2_width) || (height != pow2_height))
        {
1011 1012
            texture->pow2_matrix[0] = (((float)width) / ((float)pow2_width));
            texture->pow2_matrix[5] = (((float)height) / ((float)pow2_height));
1013
            texture->flags &= ~WINED3D_TEXTURE_POW2_MAT_IDENT;
1014 1015 1016
        }
        else
        {
1017 1018
            texture->pow2_matrix[0] = 1.0f;
            texture->pow2_matrix[5] = 1.0f;
1019 1020
        }

1021 1022 1023
        texture->pow2_matrix[10] = 1.0f;
        texture->pow2_matrix[15] = 1.0f;
        texture->target = GL_TEXTURE_2D;
1024
    }
1025
    TRACE("xf(%f) yf(%f)\n", texture->pow2_matrix[0], texture->pow2_matrix[5]);
1026 1027 1028 1029

    /* Generate all the surfaces. */
    tmp_w = width;
    tmp_h = height;
1030
    for (i = 0; i < texture->level_count; ++i)
1031
    {
1032
        struct wined3d_surface *surface;
1033

1034
        /* Use the callback to create the texture surface. */
1035
        hr = device->device_parent->ops->create_surface(device->device_parent, parent, tmp_w, tmp_h,
1036
                format->id, usage, pool, i, 0, &surface);
1037
        if (FAILED(hr))
1038 1039
        {
            FIXME("Failed to create surface %p, hr %#x\n", texture, hr);
1040
            wined3d_texture_cleanup(texture);
1041 1042 1043
            return hr;
        }

1044 1045 1046
        surface_set_container(surface, WINED3D_CONTAINER_TEXTURE, texture);
        surface_set_texture_target(surface, texture->target);
        texture->sub_resources[i] = &surface->resource;
1047
        TRACE("Created surface level %u @ %p.\n", i, surface);
1048 1049 1050 1051 1052 1053 1054
        /* Calculate the next mipmap level. */
        tmp_w = max(1, tmp_w >> 1);
        tmp_h = max(1, tmp_h >> 1);
    }

    return WINED3D_OK;
}
1055 1056

/* Context activation is done by the caller. */
1057
static HRESULT texture3d_bind(struct wined3d_texture *texture,
1058 1059 1060 1061 1062 1063
        const struct wined3d_gl_info *gl_info, BOOL srgb)
{
    BOOL dummy;

    TRACE("texture %p, gl_info %p, srgb %#x.\n", texture, gl_info, srgb);

1064
    return wined3d_texture_bind(texture, gl_info, srgb, &dummy);
1065 1066 1067
}

/* Do not call while under the GL lock. */
1068
static void texture3d_preload(struct wined3d_texture *texture, enum WINED3DSRGB srgb)
1069
{
1070
    struct wined3d_device *device = texture->resource.device;
1071 1072 1073 1074 1075 1076 1077 1078
    struct wined3d_context *context = NULL;
    BOOL srgb_was_toggled = FALSE;
    unsigned int i;

    TRACE("texture %p, srgb %#x.\n", texture, srgb);

    if (!device->isInDraw)
        context = context_acquire(device, NULL);
1079
    else if (texture->bind_count > 0)
1080
    {
1081
        BOOL texture_srgb = texture->flags & WINED3D_TEXTURE_IS_SRGB;
1082
        BOOL sampler_srgb = texture_srgb_mode(texture, srgb);
1083 1084 1085 1086 1087 1088 1089 1090 1091
        srgb_was_toggled = !texture_srgb != !sampler_srgb;

        if (srgb_was_toggled)
        {
            if (sampler_srgb)
                texture->flags |= WINED3D_TEXTURE_IS_SRGB;
            else
                texture->flags &= ~WINED3D_TEXTURE_IS_SRGB;
        }
1092 1093 1094 1095
    }

    /* If the texture is marked dirty or the sRGB sampler setting has changed
     * since the last load then reload the volumes. */
1096
    if (texture->texture_rgb.dirty)
1097
    {
1098
        for (i = 0; i < texture->level_count; ++i)
1099
        {
1100 1101
            volume_load(volume_from_resource(texture->sub_resources[i]), i,
                    texture->flags & WINED3D_TEXTURE_IS_SRGB);
1102 1103 1104 1105
        }
    }
    else if (srgb_was_toggled)
    {
1106
        for (i = 0; i < texture->level_count; ++i)
1107
        {
1108
            struct wined3d_volume *volume = volume_from_resource(texture->sub_resources[i]);
1109
            volume_add_dirty_box(volume, NULL);
1110
            volume_load(volume, i, texture->flags & WINED3D_TEXTURE_IS_SRGB);
1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121
        }
    }
    else
    {
        TRACE("Texture %p not dirty, nothing to do.\n", texture);
    }

    if (context)
        context_release(context);

    /* No longer dirty */
1122
    texture->texture_rgb.dirty = FALSE;
1123 1124 1125 1126 1127 1128 1129 1130 1131 1132
}

static void texture3d_sub_resource_add_dirty_region(struct wined3d_resource *sub_resource,
        const WINED3DBOX *dirty_region)
{
    volume_add_dirty_box(volume_from_resource(sub_resource), dirty_region);
}

static void texture3d_sub_resource_cleanup(struct wined3d_resource *sub_resource)
{
1133
    struct wined3d_volume *volume = volume_from_resource(sub_resource);
1134 1135 1136

    /* Cleanup the container. */
    volume_set_container(volume, NULL);
1137
    wined3d_volume_decref(volume);
1138 1139 1140 1141 1142
}

/* Do not call while under the GL lock. */
static void texture3d_unload(struct wined3d_resource *resource)
{
1143
    struct wined3d_texture *texture = wined3d_texture_from_resource(resource);
1144 1145 1146 1147
    UINT i;

    TRACE("texture %p.\n", texture);

1148
    for (i = 0; i < texture->level_count; ++i)
1149
    {
1150
        struct wined3d_resource *sub_resource = texture->sub_resources[i];
1151 1152 1153
        sub_resource->resource_ops->resource_unload(sub_resource);
    }

1154
    wined3d_texture_unload(texture);
1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169
}

static const struct wined3d_texture_ops texture3d_ops =
{
    texture3d_bind,
    texture3d_preload,
    texture3d_sub_resource_add_dirty_region,
    texture3d_sub_resource_cleanup,
};

static const struct wined3d_resource_ops texture3d_resource_ops =
{
    texture3d_unload,
};

1170
static HRESULT volumetexture_init(struct wined3d_texture *texture, UINT width, UINT height,
1171
        UINT depth, UINT levels, struct wined3d_device *device, DWORD usage, enum wined3d_format_id format_id,
1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216
        WINED3DPOOL pool, void *parent, const struct wined3d_parent_ops *parent_ops)
{
    const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
    const struct wined3d_format *format = wined3d_get_format(gl_info, format_id);
    UINT tmp_w, tmp_h, tmp_d;
    unsigned int i;
    HRESULT hr;

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

    if (!gl_info->supported[EXT_TEXTURE3D])
    {
        WARN("(%p) : Texture cannot be created - no volume texture support.\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(max(max(width, height), depth)) + 1;
        TRACE("Calculated levels = %u.\n", levels);
    }

1217 1218
    hr = wined3d_texture_init(texture, &texture3d_ops, 1, levels,
            WINED3DRTYPE_VOLUMETEXTURE, device, usage, format, pool,
1219 1220 1221
            parent, parent_ops, &texture3d_resource_ops);
    if (FAILED(hr))
    {
1222
        WARN("Failed to initialize texture, returning %#x.\n", hr);
1223 1224 1225 1226
        return hr;
    }

    /* Is NP2 support for volumes needed? */
1227 1228 1229 1230 1231
    texture->pow2_matrix[0] = 1.0f;
    texture->pow2_matrix[5] = 1.0f;
    texture->pow2_matrix[10] = 1.0f;
    texture->pow2_matrix[15] = 1.0f;
    texture->target = GL_TEXTURE_3D;
1232 1233 1234 1235 1236 1237

    /* Generate all the surfaces. */
    tmp_w = width;
    tmp_h = height;
    tmp_d = depth;

1238
    for (i = 0; i < texture->level_count; ++i)
1239
    {
1240
        struct wined3d_volume *volume;
1241 1242

        /* Create the volume. */
1243
        hr = device->device_parent->ops->create_volume(device->device_parent, parent,
1244 1245 1246 1247
                tmp_w, tmp_h, tmp_d, format_id, pool, usage, &volume);
        if (FAILED(hr))
        {
            ERR("Creating a volume for the volume texture failed, hr %#x.\n", hr);
1248
            wined3d_texture_cleanup(texture);
1249 1250 1251 1252
            return hr;
        }

        /* Set its container to this texture. */
1253 1254
        volume_set_container(volume, texture);
        texture->sub_resources[i] = &volume->resource;
1255 1256 1257 1258 1259 1260 1261 1262 1263

        /* Calculate the next mipmap level. */
        tmp_w = max(1, tmp_w >> 1);
        tmp_h = max(1, tmp_h >> 1);
        tmp_d = max(1, tmp_d >> 1);
    }

    return WINED3D_OK;
}
1264

1265
HRESULT CDECL wined3d_texture_create_2d(struct wined3d_device *device, UINT width, UINT height,
1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300
        UINT level_count, DWORD usage, enum wined3d_format_id format_id, WINED3DPOOL pool, void *parent,
        const struct wined3d_parent_ops *parent_ops, struct wined3d_texture **texture)
{
    struct wined3d_texture *object;
    HRESULT hr;

    TRACE("device %p, width %u, height %u, level_count %u, usage %#x\n",
            device, width, height, level_count, usage);
    TRACE("format %s, pool %#x, parent %p, parent_ops %p, texture %p.\n",
            debug_d3dformat(format_id), pool, parent, parent_ops, texture);

    object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object));
    if (!object)
    {
        ERR("Out of memory.\n");
        *texture = NULL;
        return WINED3DERR_OUTOFVIDEOMEMORY;
    }

    hr = texture_init(object, width, height, level_count,
            device, usage, format_id, pool, parent, parent_ops);
    if (FAILED(hr))
    {
        WARN("Failed to initialize texture, returning %#x.\n", hr);
        HeapFree(GetProcessHeap(), 0, object);
        *texture = NULL;
        return hr;
    }

    TRACE("Created texture %p.\n", object);
    *texture = object;

    return WINED3D_OK;
}

1301
HRESULT CDECL wined3d_texture_create_3d(struct wined3d_device *device, UINT width, UINT height, UINT depth,
1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336
        UINT level_count, DWORD usage, enum wined3d_format_id format_id, WINED3DPOOL pool, void *parent,
        const struct wined3d_parent_ops *parent_ops, struct wined3d_texture **texture)
{
    struct wined3d_texture *object;
    HRESULT hr;

    TRACE("device %p, width %u, height %u, depth %u, level_count %u, usage %#x\n",
            device, width, height, depth, level_count, usage);
    TRACE("format %s, pool %#x, parent %p, parent_ops %p, texture %p.\n",
            debug_d3dformat(format_id), pool, parent, parent_ops, texture);

    object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object));
    if (!object)
    {
        ERR("Out of memory\n");
        *texture = NULL;
        return WINED3DERR_OUTOFVIDEOMEMORY;
    }

    hr = volumetexture_init(object, width, height, depth, level_count,
            device, usage, format_id, pool, parent, parent_ops);
    if (FAILED(hr))
    {
        WARN("Failed to initialize volumetexture, returning %#x\n", hr);
        HeapFree(GetProcessHeap(), 0, object);
        *texture = NULL;
        return hr;
    }

    TRACE("Created texture %p.\n", object);
    *texture = object;

    return WINED3D_OK;
}

1337
HRESULT CDECL wined3d_texture_create_cube(struct wined3d_device *device, UINT edge_length,
1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371
        UINT level_count, DWORD usage, enum wined3d_format_id format_id, WINED3DPOOL pool, void *parent,
        const struct wined3d_parent_ops *parent_ops, struct wined3d_texture **texture)
{
    struct wined3d_texture *object;
    HRESULT hr;

    TRACE("device %p, edge_length %u, level_count %u, usage %#x\n",
            device, edge_length, level_count, usage);
    TRACE("format %s, pool %#x, parent %p, parent_ops %p, texture %p.\n",
            debug_d3dformat(format_id), pool, parent, parent_ops, texture);

    object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object));
    if (!object)
    {
        ERR("Out of memory\n");
        *texture = NULL;
        return WINED3DERR_OUTOFVIDEOMEMORY;
    }

    hr = cubetexture_init(object, edge_length, level_count,
            device, usage, format_id, pool, parent, parent_ops);
    if (FAILED(hr))
    {
        WARN("Failed to initialize cubetexture, returning %#x\n", hr);
        HeapFree(GetProcessHeap(), 0, object);
        *texture = NULL;
        return hr;
    }

    TRACE("Created texture %p.\n", object);
    *texture = object;

    return WINED3D_OK;
}