Release 1.5.29.
[wine] / dlls / wined3d / texture.c
1 /*
2  * Copyright 2002-2005 Jason Edmeades
3  * Copyright 2002-2005 Raphael Junqueira
4  * Copyright 2005 Oliver Stieber
5  * Copyright 2007-2009, 2013 Stefan Dösinger for CodeWeavers
6  * Copyright 2009-2011 Henri Verbeet for CodeWeavers
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * 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 #include "config.h"
24 #include "wine/port.h"
25 #include "wined3d_private.h"
26
27 WINE_DEFAULT_DEBUG_CHANNEL(d3d_texture);
28
29 static HRESULT wined3d_texture_init(struct wined3d_texture *texture, const struct wined3d_texture_ops *texture_ops,
30         UINT layer_count, UINT level_count, enum wined3d_resource_type resource_type, struct wined3d_device *device,
31         DWORD usage, const struct wined3d_format *format, enum wined3d_pool pool, void *parent,
32         const struct wined3d_parent_ops *parent_ops, const struct wined3d_resource_ops *resource_ops)
33 {
34     HRESULT hr;
35
36     hr = resource_init(&texture->resource, device, resource_type, format,
37             WINED3D_MULTISAMPLE_NONE, 0, usage, pool, 0, 0, 0, 0,
38             parent, parent_ops, resource_ops);
39     if (FAILED(hr))
40     {
41         WARN("Failed to initialize resource, returning %#x\n", hr);
42         return hr;
43     }
44
45     texture->texture_ops = texture_ops;
46     texture->sub_resources = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
47             level_count * layer_count * sizeof(*texture->sub_resources));
48     if (!texture->sub_resources)
49     {
50         ERR("Failed to allocate sub-resource array.\n");
51         resource_cleanup(&texture->resource);
52         return E_OUTOFMEMORY;
53     }
54
55     texture->layer_count = layer_count;
56     texture->level_count = level_count;
57     texture->filter_type = (usage & WINED3DUSAGE_AUTOGENMIPMAP) ? WINED3D_TEXF_LINEAR : WINED3D_TEXF_NONE;
58     texture->lod = 0;
59     texture->texture_rgb.dirty = TRUE;
60     texture->texture_srgb.dirty = TRUE;
61     texture->flags = WINED3D_TEXTURE_POW2_MAT_IDENT;
62
63     if (texture->resource.format->flags & WINED3DFMT_FLAG_FILTERING)
64     {
65         texture->min_mip_lookup = minMipLookup;
66         texture->mag_lookup = magLookup;
67     }
68     else
69     {
70         texture->min_mip_lookup = minMipLookup_noFilter;
71         texture->mag_lookup = magLookup_noFilter;
72     }
73
74     return WINED3D_OK;
75 }
76
77 /* A GL context is provided by the caller */
78 static void gltexture_delete(const struct wined3d_gl_info *gl_info, struct gl_texture *tex)
79 {
80     gl_info->gl_ops.gl.p_glDeleteTextures(1, &tex->name);
81     tex->name = 0;
82 }
83
84 static void wined3d_texture_unload(struct wined3d_texture *texture)
85 {
86     struct wined3d_device *device = texture->resource.device;
87     struct wined3d_context *context = NULL;
88
89     if (texture->texture_rgb.name || texture->texture_srgb.name)
90     {
91         context = context_acquire(device, NULL);
92     }
93
94     if (texture->texture_rgb.name)
95         gltexture_delete(context->gl_info, &texture->texture_rgb);
96
97     if (texture->texture_srgb.name)
98         gltexture_delete(context->gl_info, &texture->texture_srgb);
99
100     if (context) context_release(context);
101
102     wined3d_texture_set_dirty(texture, TRUE);
103
104     resource_unload(&texture->resource);
105 }
106
107 static void wined3d_texture_cleanup(struct wined3d_texture *texture)
108 {
109     UINT sub_count = texture->level_count * texture->layer_count;
110     UINT i;
111
112     TRACE("texture %p.\n", texture);
113
114     for (i = 0; i < sub_count; ++i)
115     {
116         struct wined3d_resource *sub_resource = texture->sub_resources[i];
117
118         if (sub_resource)
119             texture->texture_ops->texture_sub_resource_cleanup(sub_resource);
120     }
121
122     wined3d_texture_unload(texture);
123     HeapFree(GetProcessHeap(), 0, texture->sub_resources);
124     resource_cleanup(&texture->resource);
125 }
126
127 void wined3d_texture_set_dirty(struct wined3d_texture *texture, BOOL dirty)
128 {
129     texture->texture_rgb.dirty = dirty;
130     texture->texture_srgb.dirty = dirty;
131 }
132
133 /* Context activation is done by the caller. */
134 static HRESULT wined3d_texture_bind(struct wined3d_texture *texture,
135         struct wined3d_context *context, BOOL srgb, BOOL *set_surface_desc)
136 {
137     const struct wined3d_gl_info *gl_info = context->gl_info;
138     struct gl_texture *gl_tex;
139     BOOL new_texture = FALSE;
140     HRESULT hr = WINED3D_OK;
141     GLenum target;
142
143     TRACE("texture %p, context %p, srgb %#x, set_surface_desc %p.\n", texture, context, srgb, set_surface_desc);
144
145     /* sRGB mode cache for preload() calls outside drawprim. */
146     if (srgb)
147         texture->flags |= WINED3D_TEXTURE_IS_SRGB;
148     else
149         texture->flags &= ~WINED3D_TEXTURE_IS_SRGB;
150
151     gl_tex = wined3d_texture_get_gl_texture(texture, context->gl_info, srgb);
152     target = texture->target;
153
154     /* Generate a texture name if we don't already have one. */
155     if (!gl_tex->name)
156     {
157         *set_surface_desc = TRUE;
158         gl_info->gl_ops.gl.p_glGenTextures(1, &gl_tex->name);
159         checkGLcall("glGenTextures");
160         TRACE("Generated texture %d.\n", gl_tex->name);
161         if (texture->resource.pool == WINED3D_POOL_DEFAULT)
162         {
163             /* Tell OpenGL to try and keep this texture in video ram (well mostly). */
164             GLclampf tmp = 0.9f;
165             gl_info->gl_ops.gl.p_glPrioritizeTextures(1, &gl_tex->name, &tmp);
166         }
167         /* Initialise the state of the texture object to the OpenGL defaults,
168          * not the D3D defaults. */
169         gl_tex->states[WINED3DTEXSTA_ADDRESSU] = WINED3D_TADDRESS_WRAP;
170         gl_tex->states[WINED3DTEXSTA_ADDRESSV] = WINED3D_TADDRESS_WRAP;
171         gl_tex->states[WINED3DTEXSTA_ADDRESSW] = WINED3D_TADDRESS_WRAP;
172         gl_tex->states[WINED3DTEXSTA_BORDERCOLOR] = 0;
173         gl_tex->states[WINED3DTEXSTA_MAGFILTER] = WINED3D_TEXF_LINEAR;
174         gl_tex->states[WINED3DTEXSTA_MINFILTER] = WINED3D_TEXF_POINT; /* GL_NEAREST_MIPMAP_LINEAR */
175         gl_tex->states[WINED3DTEXSTA_MIPFILTER] = WINED3D_TEXF_LINEAR; /* GL_NEAREST_MIPMAP_LINEAR */
176         gl_tex->states[WINED3DTEXSTA_MAXMIPLEVEL] = 0;
177         gl_tex->states[WINED3DTEXSTA_MAXANISOTROPY] = 1;
178         if (context->gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
179             gl_tex->states[WINED3DTEXSTA_SRGBTEXTURE] = TRUE;
180         else
181             gl_tex->states[WINED3DTEXSTA_SRGBTEXTURE] = srgb;
182         gl_tex->states[WINED3DTEXSTA_SHADOW] = FALSE;
183         wined3d_texture_set_dirty(texture, TRUE);
184         new_texture = TRUE;
185
186         if (texture->resource.usage & WINED3DUSAGE_AUTOGENMIPMAP)
187         {
188             /* This means double binding the texture at creation, but keeps
189              * the code simpler all in all, and the run-time path free from
190              * additional checks. */
191             context_bind_texture(context, target, gl_tex->name);
192             gl_info->gl_ops.gl.p_glTexParameteri(target, GL_GENERATE_MIPMAP_SGIS, GL_TRUE);
193             checkGLcall("glTexParameteri(target, GL_GENERATE_MIPMAP_SGIS, GL_TRUE)");
194         }
195     }
196     else
197     {
198         *set_surface_desc = FALSE;
199     }
200
201     if (gl_tex->name)
202     {
203         context_bind_texture(context, target, gl_tex->name);
204         if (new_texture)
205         {
206             /* For a new texture we have to set the texture levels after
207              * binding the texture. Beware that texture rectangles do not
208              * support mipmapping, but set the maxmiplevel if we're relying
209              * on the partial GL_ARB_texture_non_power_of_two emulation with
210              * texture rectangles. (I.e., do not care about cond_np2 here,
211              * just look for GL_TEXTURE_RECTANGLE_ARB.) */
212             if (target != GL_TEXTURE_RECTANGLE_ARB)
213             {
214                 TRACE("Setting GL_TEXTURE_MAX_LEVEL to %u.\n", texture->level_count - 1);
215                 gl_info->gl_ops.gl.p_glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, texture->level_count - 1);
216                 checkGLcall("glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, texture->level_count)");
217             }
218             if (target == GL_TEXTURE_CUBE_MAP_ARB)
219             {
220                 /* Cubemaps are always set to clamp, regardless of the sampler state. */
221                 gl_info->gl_ops.gl.p_glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
222                 gl_info->gl_ops.gl.p_glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
223                 gl_info->gl_ops.gl.p_glTexParameteri(target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
224             }
225         }
226     }
227     else
228     {
229         ERR("This texture doesn't have an OpenGL texture assigned to it.\n");
230         hr = WINED3DERR_INVALIDCALL;
231     }
232
233     return hr;
234 }
235
236 /* Context activation is done by the caller. */
237 static void apply_wrap(const struct wined3d_gl_info *gl_info, GLenum target,
238         enum wined3d_texture_address d3d_wrap, GLenum param, BOOL cond_np2)
239 {
240     GLint gl_wrap;
241
242     if (d3d_wrap < WINED3D_TADDRESS_WRAP || d3d_wrap > WINED3D_TADDRESS_MIRROR_ONCE)
243     {
244         FIXME("Unrecognized or unsupported texture address mode %#x.\n", d3d_wrap);
245         return;
246     }
247
248     /* Cubemaps are always set to clamp, regardless of the sampler state. */
249     if (target == GL_TEXTURE_CUBE_MAP_ARB
250             || (cond_np2 && d3d_wrap == WINED3D_TADDRESS_WRAP))
251         gl_wrap = GL_CLAMP_TO_EDGE;
252     else
253         gl_wrap = gl_info->wrap_lookup[d3d_wrap - WINED3D_TADDRESS_WRAP];
254
255     TRACE("Setting param %#x to %#x for target %#x.\n", param, gl_wrap, target);
256     gl_info->gl_ops.gl.p_glTexParameteri(target, param, gl_wrap);
257     checkGLcall("glTexParameteri(target, param, gl_wrap)");
258 }
259
260 /* Context activation is done by the caller (state handler). */
261 void wined3d_texture_apply_state_changes(struct wined3d_texture *texture,
262         const DWORD sampler_states[WINED3D_HIGHEST_SAMPLER_STATE + 1],
263         const struct wined3d_gl_info *gl_info)
264 {
265     BOOL cond_np2 = texture->flags & WINED3D_TEXTURE_COND_NP2;
266     GLenum target = texture->target;
267     struct gl_texture *gl_tex;
268     DWORD state;
269     DWORD aniso;
270
271     TRACE("texture %p, sampler_states %p.\n", texture, sampler_states);
272
273     gl_tex = wined3d_texture_get_gl_texture(texture, gl_info,
274             texture->flags & WINED3D_TEXTURE_IS_SRGB);
275
276     /* This function relies on the correct texture being bound and loaded. */
277
278     if (sampler_states[WINED3D_SAMP_ADDRESS_U] != gl_tex->states[WINED3DTEXSTA_ADDRESSU])
279     {
280         state = sampler_states[WINED3D_SAMP_ADDRESS_U];
281         apply_wrap(gl_info, target, state, GL_TEXTURE_WRAP_S, cond_np2);
282         gl_tex->states[WINED3DTEXSTA_ADDRESSU] = state;
283     }
284
285     if (sampler_states[WINED3D_SAMP_ADDRESS_V] != gl_tex->states[WINED3DTEXSTA_ADDRESSV])
286     {
287         state = sampler_states[WINED3D_SAMP_ADDRESS_V];
288         apply_wrap(gl_info, target, state, GL_TEXTURE_WRAP_T, cond_np2);
289         gl_tex->states[WINED3DTEXSTA_ADDRESSV] = state;
290     }
291
292     if (sampler_states[WINED3D_SAMP_ADDRESS_W] != gl_tex->states[WINED3DTEXSTA_ADDRESSW])
293     {
294         state = sampler_states[WINED3D_SAMP_ADDRESS_W];
295         apply_wrap(gl_info, target, state, GL_TEXTURE_WRAP_R, cond_np2);
296         gl_tex->states[WINED3DTEXSTA_ADDRESSW] = state;
297     }
298
299     if (sampler_states[WINED3D_SAMP_BORDER_COLOR] != gl_tex->states[WINED3DTEXSTA_BORDERCOLOR])
300     {
301         float col[4];
302
303         state = sampler_states[WINED3D_SAMP_BORDER_COLOR];
304         D3DCOLORTOGLFLOAT4(state, col);
305         TRACE("Setting border color for %#x to %#x.\n", target, state);
306         gl_info->gl_ops.gl.p_glTexParameterfv(target, GL_TEXTURE_BORDER_COLOR, &col[0]);
307         checkGLcall("glTexParameterfv(..., GL_TEXTURE_BORDER_COLOR, ...)");
308         gl_tex->states[WINED3DTEXSTA_BORDERCOLOR] = state;
309     }
310
311     if (sampler_states[WINED3D_SAMP_MAG_FILTER] != gl_tex->states[WINED3DTEXSTA_MAGFILTER])
312     {
313         GLint gl_value;
314
315         state = sampler_states[WINED3D_SAMP_MAG_FILTER];
316         if (state > WINED3D_TEXF_ANISOTROPIC)
317             FIXME("Unrecognized or unsupported MAGFILTER* value %d.\n", state);
318
319         gl_value = wined3d_gl_mag_filter(texture->mag_lookup,
320                 min(max(state, WINED3D_TEXF_POINT), WINED3D_TEXF_LINEAR));
321         TRACE("ValueMAG=%#x setting MAGFILTER to %#x.\n", state, gl_value);
322         gl_info->gl_ops.gl.p_glTexParameteri(target, GL_TEXTURE_MAG_FILTER, gl_value);
323
324         gl_tex->states[WINED3DTEXSTA_MAGFILTER] = state;
325     }
326
327     if ((sampler_states[WINED3D_SAMP_MIN_FILTER] != gl_tex->states[WINED3DTEXSTA_MINFILTER]
328             || sampler_states[WINED3D_SAMP_MIP_FILTER] != gl_tex->states[WINED3DTEXSTA_MIPFILTER]
329             || sampler_states[WINED3D_SAMP_MAX_MIP_LEVEL] != gl_tex->states[WINED3DTEXSTA_MAXMIPLEVEL]))
330     {
331         GLint gl_value;
332
333         gl_tex->states[WINED3DTEXSTA_MIPFILTER] = sampler_states[WINED3D_SAMP_MIP_FILTER];
334         gl_tex->states[WINED3DTEXSTA_MINFILTER] = sampler_states[WINED3D_SAMP_MIN_FILTER];
335         gl_tex->states[WINED3DTEXSTA_MAXMIPLEVEL] = sampler_states[WINED3D_SAMP_MAX_MIP_LEVEL];
336
337         if (gl_tex->states[WINED3DTEXSTA_MINFILTER] > WINED3D_TEXF_ANISOTROPIC
338             || gl_tex->states[WINED3DTEXSTA_MIPFILTER] > WINED3D_TEXF_ANISOTROPIC)
339         {
340             FIXME("Unrecognized or unsupported MIN_FILTER value %#x MIP_FILTER value %#x.\n",
341                   gl_tex->states[WINED3DTEXSTA_MINFILTER],
342                   gl_tex->states[WINED3DTEXSTA_MIPFILTER]);
343         }
344         gl_value = wined3d_gl_min_mip_filter(texture->min_mip_lookup,
345                 min(max(sampler_states[WINED3D_SAMP_MIN_FILTER], WINED3D_TEXF_POINT), WINED3D_TEXF_LINEAR),
346                 min(max(sampler_states[WINED3D_SAMP_MIP_FILTER], WINED3D_TEXF_NONE), WINED3D_TEXF_LINEAR));
347
348         TRACE("ValueMIN=%#x, ValueMIP=%#x, setting MINFILTER to %#x.\n",
349               sampler_states[WINED3D_SAMP_MIN_FILTER],
350               sampler_states[WINED3D_SAMP_MIP_FILTER], gl_value);
351         gl_info->gl_ops.gl.p_glTexParameteri(target, GL_TEXTURE_MIN_FILTER, gl_value);
352         checkGLcall("glTexParameter GL_TEXTURE_MIN_FILTER, ...");
353
354         if (!cond_np2)
355         {
356             if (gl_tex->states[WINED3DTEXSTA_MIPFILTER] == WINED3D_TEXF_NONE)
357                 gl_value = texture->lod;
358             else if (gl_tex->states[WINED3DTEXSTA_MAXMIPLEVEL] >= texture->level_count)
359                 gl_value = texture->level_count - 1;
360             else if (gl_tex->states[WINED3DTEXSTA_MAXMIPLEVEL] < texture->lod)
361                 /* texture->lod is already clamped in the setter. */
362                 gl_value = texture->lod;
363             else
364                 gl_value = gl_tex->states[WINED3DTEXSTA_MAXMIPLEVEL];
365
366             /* Note that WINED3D_SAMP_MAX_MIP_LEVEL specifies the largest mipmap
367              * (default 0), while GL_TEXTURE_MAX_LEVEL specifies the smallest
368              * mimap used (default 1000). So WINED3D_SAMP_MAX_MIP_LEVEL
369              * corresponds to GL_TEXTURE_BASE_LEVEL. */
370             gl_info->gl_ops.gl.p_glTexParameteri(target, GL_TEXTURE_BASE_LEVEL, gl_value);
371         }
372     }
373
374     if ((gl_tex->states[WINED3DTEXSTA_MAGFILTER] != WINED3D_TEXF_ANISOTROPIC
375             && gl_tex->states[WINED3DTEXSTA_MINFILTER] != WINED3D_TEXF_ANISOTROPIC
376             && gl_tex->states[WINED3DTEXSTA_MIPFILTER] != WINED3D_TEXF_ANISOTROPIC)
377             || cond_np2)
378         aniso = 1;
379     else
380         aniso = sampler_states[WINED3D_SAMP_MAX_ANISOTROPY];
381
382     if (gl_tex->states[WINED3DTEXSTA_MAXANISOTROPY] != aniso)
383     {
384         if (gl_info->supported[EXT_TEXTURE_FILTER_ANISOTROPIC])
385         {
386             gl_info->gl_ops.gl.p_glTexParameteri(target, GL_TEXTURE_MAX_ANISOTROPY_EXT, aniso);
387             checkGLcall("glTexParameteri(GL_TEXTURE_MAX_ANISOTROPY_EXT, aniso)");
388         }
389         else
390         {
391             WARN("Anisotropic filtering not supported.\n");
392         }
393         gl_tex->states[WINED3DTEXSTA_MAXANISOTROPY] = aniso;
394     }
395
396     /* These should always be the same unless EXT_texture_sRGB_decode is supported. */
397     if (sampler_states[WINED3D_SAMP_SRGB_TEXTURE] != gl_tex->states[WINED3DTEXSTA_SRGBTEXTURE])
398     {
399         gl_info->gl_ops.gl.p_glTexParameteri(target, GL_TEXTURE_SRGB_DECODE_EXT,
400                 sampler_states[WINED3D_SAMP_SRGB_TEXTURE] ? GL_DECODE_EXT : GL_SKIP_DECODE_EXT);
401         checkGLcall("glTexParameteri(GL_TEXTURE_SRGB_DECODE_EXT)");
402         gl_tex->states[WINED3DTEXSTA_SRGBTEXTURE] = sampler_states[WINED3D_SAMP_SRGB_TEXTURE];
403     }
404
405     if (!(texture->resource.format->flags & WINED3DFMT_FLAG_SHADOW)
406             != !gl_tex->states[WINED3DTEXSTA_SHADOW])
407     {
408         if (texture->resource.format->flags & WINED3DFMT_FLAG_SHADOW)
409         {
410             gl_info->gl_ops.gl.p_glTexParameteri(target, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);
411             gl_info->gl_ops.gl.p_glTexParameteri(target, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE_ARB);
412             checkGLcall("glTexParameteri(target, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE_ARB)");
413             gl_tex->states[WINED3DTEXSTA_SHADOW] = TRUE;
414         }
415         else
416         {
417             gl_info->gl_ops.gl.p_glTexParameteri(target, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);
418             checkGLcall("glTexParameteri(target, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE)");
419             gl_tex->states[WINED3DTEXSTA_SHADOW] = FALSE;
420         }
421     }
422 }
423
424 ULONG CDECL wined3d_texture_incref(struct wined3d_texture *texture)
425 {
426     ULONG refcount = InterlockedIncrement(&texture->resource.ref);
427
428     TRACE("%p increasing refcount to %u.\n", texture, refcount);
429
430     return refcount;
431 }
432
433 /* Do not call while under the GL lock. */
434 ULONG CDECL wined3d_texture_decref(struct wined3d_texture *texture)
435 {
436     ULONG refcount = InterlockedDecrement(&texture->resource.ref);
437
438     TRACE("%p decreasing refcount to %u.\n", texture, refcount);
439
440     if (!refcount)
441     {
442         wined3d_texture_cleanup(texture);
443         texture->resource.parent_ops->wined3d_object_destroyed(texture->resource.parent);
444         HeapFree(GetProcessHeap(), 0, texture);
445     }
446
447     return refcount;
448 }
449
450 struct wined3d_resource * CDECL wined3d_texture_get_resource(struct wined3d_texture *texture)
451 {
452     TRACE("texture %p.\n", texture);
453
454     return &texture->resource;
455 }
456
457 DWORD CDECL wined3d_texture_set_priority(struct wined3d_texture *texture, DWORD priority)
458 {
459     return resource_set_priority(&texture->resource, priority);
460 }
461
462 DWORD CDECL wined3d_texture_get_priority(const struct wined3d_texture *texture)
463 {
464     return resource_get_priority(&texture->resource);
465 }
466
467 /* Do not call while under the GL lock. */
468 void CDECL wined3d_texture_preload(struct wined3d_texture *texture)
469 {
470     texture->texture_ops->texture_preload(texture, SRGB_ANY);
471 }
472
473 void * CDECL wined3d_texture_get_parent(const struct wined3d_texture *texture)
474 {
475     TRACE("texture %p.\n", texture);
476
477     return texture->resource.parent;
478 }
479
480 DWORD CDECL wined3d_texture_set_lod(struct wined3d_texture *texture, DWORD lod)
481 {
482     DWORD old = texture->lod;
483
484     TRACE("texture %p, lod %u.\n", texture, lod);
485
486     /* The d3d9:texture test shows that SetLOD is ignored on non-managed
487      * textures. The call always returns 0, and GetLOD always returns 0. */
488     if (texture->resource.pool != WINED3D_POOL_MANAGED)
489     {
490         TRACE("Ignoring SetLOD on %s texture, returning 0.\n", debug_d3dpool(texture->resource.pool));
491         return 0;
492     }
493
494     if (lod >= texture->level_count)
495         lod = texture->level_count - 1;
496
497     if (texture->lod != lod)
498     {
499         texture->lod = lod;
500
501         texture->texture_rgb.states[WINED3DTEXSTA_MAXMIPLEVEL] = ~0U;
502         texture->texture_srgb.states[WINED3DTEXSTA_MAXMIPLEVEL] = ~0U;
503         if (texture->resource.bind_count)
504             device_invalidate_state(texture->resource.device, STATE_SAMPLER(texture->sampler));
505     }
506
507     return old;
508 }
509
510 DWORD CDECL wined3d_texture_get_lod(const struct wined3d_texture *texture)
511 {
512     TRACE("texture %p, returning %u.\n", texture, texture->lod);
513
514     return texture->lod;
515 }
516
517 DWORD CDECL wined3d_texture_get_level_count(const struct wined3d_texture *texture)
518 {
519     TRACE("texture %p, returning %u.\n", texture, texture->level_count);
520
521     return texture->level_count;
522 }
523
524 HRESULT CDECL wined3d_texture_set_autogen_filter_type(struct wined3d_texture *texture,
525         enum wined3d_texture_filter_type filter_type)
526 {
527     FIXME("texture %p, filter_type %s stub!\n", texture, debug_d3dtexturefiltertype(filter_type));
528
529     if (!(texture->resource.usage & WINED3DUSAGE_AUTOGENMIPMAP))
530     {
531         WARN("Texture doesn't have AUTOGENMIPMAP usage.\n");
532         return WINED3DERR_INVALIDCALL;
533     }
534
535     texture->filter_type = filter_type;
536
537     return WINED3D_OK;
538 }
539
540 enum wined3d_texture_filter_type CDECL wined3d_texture_get_autogen_filter_type(const struct wined3d_texture *texture)
541 {
542     TRACE("texture %p.\n", texture);
543
544     return texture->filter_type;
545 }
546
547 void CDECL wined3d_texture_generate_mipmaps(struct wined3d_texture *texture)
548 {
549     /* TODO: Implement filters using GL_SGI_generate_mipmaps. */
550     FIXME("texture %p stub!\n", texture);
551 }
552
553 struct wined3d_resource * CDECL wined3d_texture_get_sub_resource(struct wined3d_texture *texture,
554         UINT sub_resource_idx)
555 {
556     UINT sub_count = texture->level_count * texture->layer_count;
557
558     TRACE("texture %p, sub_resource_idx %u.\n", texture, sub_resource_idx);
559
560     if (sub_resource_idx >= sub_count)
561     {
562         WARN("sub_resource_idx %u >= sub_count %u.\n", sub_resource_idx, sub_count);
563         return NULL;
564     }
565
566     return texture->sub_resources[sub_resource_idx];
567 }
568
569 HRESULT CDECL wined3d_texture_add_dirty_region(struct wined3d_texture *texture,
570         UINT layer, const struct wined3d_box *dirty_region)
571 {
572     struct wined3d_resource *sub_resource;
573
574     TRACE("texture %p, layer %u, dirty_region %p.\n", texture, layer, dirty_region);
575
576     if (!(sub_resource = wined3d_texture_get_sub_resource(texture, layer * texture->level_count)))
577     {
578         WARN("Failed to get sub-resource.\n");
579         return WINED3DERR_INVALIDCALL;
580     }
581
582     wined3d_texture_set_dirty(texture, TRUE);
583     texture->texture_ops->texture_sub_resource_add_dirty_region(sub_resource, dirty_region);
584
585     return WINED3D_OK;
586 }
587
588 /* Context activation is done by the caller. */
589 static HRESULT texture2d_bind(struct wined3d_texture *texture,
590         struct wined3d_context *context, BOOL srgb)
591 {
592     const struct wined3d_gl_info *gl_info = context->gl_info;
593     BOOL set_gl_texture_desc;
594     HRESULT hr;
595
596     TRACE("texture %p, context %p, srgb %#x.\n", texture, context, srgb);
597
598     hr = wined3d_texture_bind(texture, context, srgb, &set_gl_texture_desc);
599     if (set_gl_texture_desc && SUCCEEDED(hr))
600     {
601         UINT sub_count = texture->level_count * texture->layer_count;
602         BOOL srgb_tex = !context->gl_info->supported[EXT_TEXTURE_SRGB_DECODE]
603                 && (texture->flags & WINED3D_TEXTURE_IS_SRGB);
604         struct gl_texture *gl_tex;
605         UINT i;
606
607         gl_tex = wined3d_texture_get_gl_texture(texture, context->gl_info, srgb_tex);
608
609         for (i = 0; i < sub_count; ++i)
610         {
611             struct wined3d_surface *surface = surface_from_resource(texture->sub_resources[i]);
612             surface_set_texture_name(surface, gl_tex->name, srgb_tex);
613         }
614
615         /* Conditinal non power of two textures use a different clamping
616          * default. If we're using the GL_WINE_normalized_texrect partial
617          * driver emulation, we're dealing with a GL_TEXTURE_2D texture which
618          * has the address mode set to repeat - something that prevents us
619          * from hitting the accelerated codepath. Thus manually set the GL
620          * state. The same applies to filtering. Even if the texture has only
621          * one mip level, the default LINEAR_MIPMAP_LINEAR filter causes a SW
622          * fallback on macos. */
623         if (texture->flags & WINED3D_TEXTURE_COND_NP2)
624         {
625             GLenum target = texture->target;
626
627             gl_info->gl_ops.gl.p_glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
628             checkGLcall("glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)");
629             gl_info->gl_ops.gl.p_glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
630             checkGLcall("glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)");
631             gl_info->gl_ops.gl.p_glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
632             checkGLcall("glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST)");
633             gl_info->gl_ops.gl.p_glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
634             checkGLcall("glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST)");
635             gl_tex->states[WINED3DTEXSTA_ADDRESSU] = WINED3D_TADDRESS_CLAMP;
636             gl_tex->states[WINED3DTEXSTA_ADDRESSV] = WINED3D_TADDRESS_CLAMP;
637             gl_tex->states[WINED3DTEXSTA_MAGFILTER] = WINED3D_TEXF_POINT;
638             gl_tex->states[WINED3DTEXSTA_MINFILTER] = WINED3D_TEXF_POINT;
639             gl_tex->states[WINED3DTEXSTA_MIPFILTER] = WINED3D_TEXF_NONE;
640         }
641     }
642
643     return hr;
644 }
645
646 static BOOL texture_srgb_mode(const struct wined3d_texture *texture, enum WINED3DSRGB srgb)
647 {
648     switch (srgb)
649     {
650         case SRGB_RGB:
651             return FALSE;
652
653         case SRGB_SRGB:
654             return TRUE;
655
656         default:
657             return texture->flags & WINED3D_TEXTURE_IS_SRGB;
658     }
659 }
660
661 /* Do not call while under the GL lock. */
662 static void texture2d_preload(struct wined3d_texture *texture, enum WINED3DSRGB srgb)
663 {
664     UINT sub_count = texture->level_count * texture->layer_count;
665     struct wined3d_device *device = texture->resource.device;
666     const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
667     struct wined3d_context *context = NULL;
668     struct gl_texture *gl_tex;
669     BOOL srgb_mode;
670     UINT i;
671
672     TRACE("texture %p, srgb %#x.\n", texture, srgb);
673
674     srgb_mode = texture_srgb_mode(texture, srgb);
675     gl_tex = wined3d_texture_get_gl_texture(texture, gl_info, srgb_mode);
676
677     if (!device->isInDraw)
678     {
679         /* No danger of recursive calls, context_acquire() sets isInDraw to TRUE
680          * when loading offscreen render targets into the texture. */
681         context = context_acquire(device, NULL);
682     }
683
684     if (gl_tex->dirty)
685     {
686         /* Reload the surfaces if the texture is marked dirty. */
687         for (i = 0; i < sub_count; ++i)
688         {
689             surface_load(surface_from_resource(texture->sub_resources[i]), srgb_mode);
690         }
691     }
692     else
693     {
694         TRACE("Texture %p not dirty, nothing to do.\n", texture);
695     }
696
697     /* No longer dirty. */
698     gl_tex->dirty = FALSE;
699
700     if (context) context_release(context);
701 }
702
703 static void texture2d_sub_resource_add_dirty_region(struct wined3d_resource *sub_resource,
704         const struct wined3d_box *dirty_region)
705 {
706     surface_add_dirty_rect(surface_from_resource(sub_resource), dirty_region);
707 }
708
709 static void texture2d_sub_resource_cleanup(struct wined3d_resource *sub_resource)
710 {
711     struct wined3d_surface *surface = surface_from_resource(sub_resource);
712
713     /* Clean out the texture name we gave to the surface so that the
714      * surface doesn't try and release it. */
715     surface_set_texture_name(surface, 0, TRUE);
716     surface_set_texture_name(surface, 0, FALSE);
717     surface_set_texture_target(surface, 0, 0);
718     surface_set_container(surface, WINED3D_CONTAINER_NONE, NULL);
719     wined3d_surface_decref(surface);
720 }
721
722 /* Do not call while under the GL lock. */
723 static void texture2d_unload(struct wined3d_resource *resource)
724 {
725     struct wined3d_texture *texture = wined3d_texture_from_resource(resource);
726     UINT sub_count = texture->level_count * texture->layer_count;
727     UINT i;
728
729     TRACE("texture %p.\n", texture);
730
731     for (i = 0; i < sub_count; ++i)
732     {
733         struct wined3d_resource *sub_resource = texture->sub_resources[i];
734         struct wined3d_surface *surface = surface_from_resource(sub_resource);
735
736         sub_resource->resource_ops->resource_unload(sub_resource);
737         surface_set_texture_name(surface, 0, FALSE); /* Delete RGB name */
738         surface_set_texture_name(surface, 0, TRUE); /* Delete sRGB name */
739     }
740
741     wined3d_texture_unload(texture);
742 }
743
744 static const struct wined3d_texture_ops texture2d_ops =
745 {
746     texture2d_bind,
747     texture2d_preload,
748     texture2d_sub_resource_add_dirty_region,
749     texture2d_sub_resource_cleanup,
750 };
751
752 static const struct wined3d_resource_ops texture2d_resource_ops =
753 {
754     texture2d_unload,
755 };
756
757 static HRESULT cubetexture_init(struct wined3d_texture *texture, UINT edge_length, UINT levels,
758         struct wined3d_device *device, DWORD usage, enum wined3d_format_id format_id, enum wined3d_pool pool,
759         void *parent, const struct wined3d_parent_ops *parent_ops)
760 {
761     const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
762     const struct wined3d_format *format = wined3d_get_format(gl_info, format_id);
763     unsigned int i, j;
764     UINT tmp_w;
765     HRESULT hr;
766
767     /* TODO: It should only be possible to create textures for formats
768      * that are reported as supported. */
769     if (WINED3DFMT_UNKNOWN >= format_id)
770     {
771         WARN("(%p) : Texture cannot be created with a format of WINED3DFMT_UNKNOWN.\n", texture);
772         return WINED3DERR_INVALIDCALL;
773     }
774
775     if (!gl_info->supported[ARB_TEXTURE_CUBE_MAP] && pool != WINED3D_POOL_SCRATCH)
776     {
777         WARN("(%p) : Tried to create not supported cube texture.\n", texture);
778         return WINED3DERR_INVALIDCALL;
779     }
780
781     /* Calculate levels for mip mapping */
782     if (usage & WINED3DUSAGE_AUTOGENMIPMAP)
783     {
784         if (!gl_info->supported[SGIS_GENERATE_MIPMAP])
785         {
786             WARN("No mipmap generation support, returning D3DERR_INVALIDCALL.\n");
787             return WINED3DERR_INVALIDCALL;
788         }
789
790         if (levels > 1)
791         {
792             WARN("D3DUSAGE_AUTOGENMIPMAP is set, and level count > 1, returning D3DERR_INVALIDCALL.\n");
793             return WINED3DERR_INVALIDCALL;
794         }
795
796         levels = 1;
797     }
798     else if (!levels)
799     {
800         levels = wined3d_log2i(edge_length) + 1;
801         TRACE("Calculated levels = %u.\n", levels);
802     }
803
804     if (!gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO])
805     {
806         UINT pow2_edge_length = 1;
807         while (pow2_edge_length < edge_length) pow2_edge_length <<= 1;
808
809         if (edge_length != pow2_edge_length)
810         {
811             if (pool == WINED3D_POOL_SCRATCH)
812             {
813                 /* SCRATCH textures cannot be used for texturing */
814                 WARN("Creating a scratch NPOT cube texture despite lack of HW support.\n");
815             }
816             else
817             {
818                 WARN("Attempted to create a NPOT cube texture (edge_length=%u) without GL support.\n",
819                         edge_length);
820                 return WINED3DERR_INVALIDCALL;
821             }
822         }
823     }
824
825     hr = wined3d_texture_init(texture, &texture2d_ops, 6, levels,
826             WINED3D_RTYPE_CUBE_TEXTURE, device, usage, format, pool,
827             parent, parent_ops, &texture2d_resource_ops);
828     if (FAILED(hr))
829     {
830         WARN("Failed to initialize texture, returning %#x\n", hr);
831         return hr;
832     }
833
834     texture->pow2_matrix[0] = 1.0f;
835     texture->pow2_matrix[5] = 1.0f;
836     texture->pow2_matrix[10] = 1.0f;
837     texture->pow2_matrix[15] = 1.0f;
838     texture->target = GL_TEXTURE_CUBE_MAP_ARB;
839
840     /* Generate all the surfaces. */
841     tmp_w = edge_length;
842     for (i = 0; i < texture->level_count; ++i)
843     {
844         /* Create the 6 faces. */
845         for (j = 0; j < 6; ++j)
846         {
847             static const GLenum cube_targets[6] =
848             {
849                 GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB,
850                 GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB,
851                 GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB,
852                 GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB,
853                 GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB,
854                 GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB,
855             };
856             UINT idx = j * texture->level_count + i;
857             struct wined3d_surface *surface;
858
859             if (FAILED(hr = device->device_parent->ops->create_texture_surface(device->device_parent,
860                     parent, tmp_w, tmp_w, format_id, usage, pool, idx, &surface)))
861             {
862                 FIXME("(%p) Failed to create surface, hr %#x.\n", texture, hr);
863                 wined3d_texture_cleanup(texture);
864                 return hr;
865             }
866
867             surface_set_container(surface, WINED3D_CONTAINER_TEXTURE, texture);
868             surface_set_texture_target(surface, cube_targets[j], i);
869             texture->sub_resources[idx] = &surface->resource;
870             TRACE("Created surface level %u @ %p.\n", i, surface);
871         }
872         tmp_w = max(1, tmp_w >> 1);
873     }
874
875     return WINED3D_OK;
876 }
877
878 static HRESULT texture_init(struct wined3d_texture *texture, UINT width, UINT height, UINT levels,
879         struct wined3d_device *device, DWORD usage, enum wined3d_format_id format_id, enum wined3d_pool pool,
880         void *parent, const struct wined3d_parent_ops *parent_ops)
881 {
882     const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
883     const struct wined3d_format *format = wined3d_get_format(gl_info, format_id);
884     UINT pow2_width, pow2_height;
885     UINT tmp_w, tmp_h;
886     unsigned int i;
887     HRESULT hr;
888
889     /* TODO: It should only be possible to create textures for formats
890      * that are reported as supported. */
891     if (WINED3DFMT_UNKNOWN >= format_id)
892     {
893         WARN("(%p) : Texture cannot be created with a format of WINED3DFMT_UNKNOWN.\n", texture);
894         return WINED3DERR_INVALIDCALL;
895     }
896
897     /* Non-power2 support. */
898     if (gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO])
899     {
900         pow2_width = width;
901         pow2_height = height;
902     }
903     else
904     {
905         /* Find the nearest pow2 match. */
906         pow2_width = pow2_height = 1;
907         while (pow2_width < width) pow2_width <<= 1;
908         while (pow2_height < height) pow2_height <<= 1;
909
910         if (pow2_width != width || pow2_height != height)
911         {
912             /* levels == 0 returns an error as well */
913             if (levels != 1)
914             {
915                 if (pool == WINED3D_POOL_SCRATCH)
916                 {
917                     WARN("Creating a scratch mipmapped NPOT texture despite lack of HW support.\n");
918                 }
919                 else
920                 {
921                     WARN("Attempted to create a mipmapped NPOT texture without unconditional NPOT support.\n");
922                     return WINED3DERR_INVALIDCALL;
923                 }
924             }
925         }
926     }
927
928     /* Calculate levels for mip mapping. */
929     if (usage & WINED3DUSAGE_AUTOGENMIPMAP)
930     {
931         if (!gl_info->supported[SGIS_GENERATE_MIPMAP])
932         {
933             WARN("No mipmap generation support, returning WINED3DERR_INVALIDCALL.\n");
934             return WINED3DERR_INVALIDCALL;
935         }
936
937         if (levels > 1)
938         {
939             WARN("D3DUSAGE_AUTOGENMIPMAP is set, and level count > 1, returning WINED3DERR_INVALIDCALL.\n");
940             return WINED3DERR_INVALIDCALL;
941         }
942
943         levels = 1;
944     }
945     else if (!levels)
946     {
947         levels = wined3d_log2i(max(width, height)) + 1;
948         TRACE("Calculated levels = %u.\n", levels);
949     }
950
951     hr = wined3d_texture_init(texture, &texture2d_ops, 1, levels,
952             WINED3D_RTYPE_TEXTURE, device, usage, format, pool,
953             parent, parent_ops, &texture2d_resource_ops);
954     if (FAILED(hr))
955     {
956         WARN("Failed to initialize texture, returning %#x.\n", hr);
957         return hr;
958     }
959
960     /* Precalculated scaling for 'faked' non power of two texture coords.
961      * Second also don't use ARB_TEXTURE_RECTANGLE in case the surface format is P8 and EXT_PALETTED_TEXTURE
962      * is used in combination with texture uploads (RTL_READTEX). The reason is that EXT_PALETTED_TEXTURE
963      * doesn't work in combination with ARB_TEXTURE_RECTANGLE. */
964     if (gl_info->supported[WINED3D_GL_NORMALIZED_TEXRECT] && (width != pow2_width || height != pow2_height))
965     {
966         texture->pow2_matrix[0] = 1.0f;
967         texture->pow2_matrix[5] = 1.0f;
968         texture->pow2_matrix[10] = 1.0f;
969         texture->pow2_matrix[15] = 1.0f;
970         texture->target = GL_TEXTURE_2D;
971         texture->flags |= WINED3D_TEXTURE_COND_NP2;
972         texture->min_mip_lookup = minMipLookup_noFilter;
973     }
974     else if (gl_info->supported[ARB_TEXTURE_RECTANGLE] && (width != pow2_width || height != pow2_height)
975             && !(format->id == WINED3DFMT_P8_UINT && gl_info->supported[EXT_PALETTED_TEXTURE]
976             && wined3d_settings.rendertargetlock_mode == RTL_READTEX))
977     {
978         texture->pow2_matrix[0] = (float)width;
979         texture->pow2_matrix[5] = (float)height;
980         texture->pow2_matrix[10] = 1.0f;
981         texture->pow2_matrix[15] = 1.0f;
982         texture->target = GL_TEXTURE_RECTANGLE_ARB;
983         texture->flags |= WINED3D_TEXTURE_COND_NP2;
984         texture->flags &= ~WINED3D_TEXTURE_POW2_MAT_IDENT;
985
986         if (texture->resource.format->flags & WINED3DFMT_FLAG_FILTERING)
987             texture->min_mip_lookup = minMipLookup_noMip;
988         else
989             texture->min_mip_lookup = minMipLookup_noFilter;
990     }
991     else
992     {
993         if ((width != pow2_width) || (height != pow2_height))
994         {
995             texture->pow2_matrix[0] = (((float)width) / ((float)pow2_width));
996             texture->pow2_matrix[5] = (((float)height) / ((float)pow2_height));
997             texture->flags &= ~WINED3D_TEXTURE_POW2_MAT_IDENT;
998         }
999         else
1000         {
1001             texture->pow2_matrix[0] = 1.0f;
1002             texture->pow2_matrix[5] = 1.0f;
1003         }
1004
1005         texture->pow2_matrix[10] = 1.0f;
1006         texture->pow2_matrix[15] = 1.0f;
1007         texture->target = GL_TEXTURE_2D;
1008     }
1009     TRACE("xf(%f) yf(%f)\n", texture->pow2_matrix[0], texture->pow2_matrix[5]);
1010
1011     /* Generate all the surfaces. */
1012     tmp_w = width;
1013     tmp_h = height;
1014     for (i = 0; i < texture->level_count; ++i)
1015     {
1016         struct wined3d_surface *surface;
1017
1018         /* Use the callback to create the texture surface. */
1019         if (FAILED(hr = device->device_parent->ops->create_texture_surface(device->device_parent,
1020                 parent, tmp_w, tmp_h, format->id, usage, pool, i, &surface)))
1021         {
1022             FIXME("Failed to create surface %p, hr %#x\n", texture, hr);
1023             wined3d_texture_cleanup(texture);
1024             return hr;
1025         }
1026
1027         surface_set_container(surface, WINED3D_CONTAINER_TEXTURE, texture);
1028         surface_set_texture_target(surface, texture->target, i);
1029         texture->sub_resources[i] = &surface->resource;
1030         TRACE("Created surface level %u @ %p.\n", i, surface);
1031         /* Calculate the next mipmap level. */
1032         tmp_w = max(1, tmp_w >> 1);
1033         tmp_h = max(1, tmp_h >> 1);
1034     }
1035
1036     return WINED3D_OK;
1037 }
1038
1039 /* Context activation is done by the caller. */
1040 static HRESULT texture3d_bind(struct wined3d_texture *texture,
1041         struct wined3d_context *context, BOOL srgb)
1042 {
1043     BOOL dummy;
1044
1045     TRACE("texture %p, context %p, srgb %#x.\n", texture, context, srgb);
1046
1047     return wined3d_texture_bind(texture, context, srgb, &dummy);
1048 }
1049
1050 /* Do not call while under the GL lock. */
1051 static void texture3d_preload(struct wined3d_texture *texture, enum WINED3DSRGB srgb)
1052 {
1053     struct wined3d_device *device = texture->resource.device;
1054     struct wined3d_context *context;
1055     BOOL srgb_was_toggled = FALSE;
1056     unsigned int i;
1057
1058     TRACE("texture %p, srgb %#x.\n", texture, srgb);
1059
1060     /* TODO: Use already acquired context when possible. */
1061     context = context_acquire(device, NULL);
1062     if (texture->resource.bind_count > 0)
1063     {
1064         BOOL texture_srgb = texture->flags & WINED3D_TEXTURE_IS_SRGB;
1065         BOOL sampler_srgb = texture_srgb_mode(texture, srgb);
1066         srgb_was_toggled = !texture_srgb != !sampler_srgb;
1067
1068         if (srgb_was_toggled)
1069         {
1070             if (sampler_srgb)
1071                 texture->flags |= WINED3D_TEXTURE_IS_SRGB;
1072             else
1073                 texture->flags &= ~WINED3D_TEXTURE_IS_SRGB;
1074         }
1075     }
1076
1077     /* If the texture is marked dirty or the sRGB sampler setting has changed
1078      * since the last load then reload the volumes. */
1079     if (texture->texture_rgb.dirty)
1080     {
1081         for (i = 0; i < texture->level_count; ++i)
1082         {
1083             volume_load(volume_from_resource(texture->sub_resources[i]), context, i,
1084                     texture->flags & WINED3D_TEXTURE_IS_SRGB);
1085         }
1086     }
1087     else if (srgb_was_toggled)
1088     {
1089         for (i = 0; i < texture->level_count; ++i)
1090         {
1091             struct wined3d_volume *volume = volume_from_resource(texture->sub_resources[i]);
1092             volume_add_dirty_box(volume, NULL);
1093             volume_load(volume, context, i, texture->flags & WINED3D_TEXTURE_IS_SRGB);
1094         }
1095     }
1096     else
1097     {
1098         TRACE("Texture %p not dirty, nothing to do.\n", texture);
1099     }
1100
1101     context_release(context);
1102
1103     /* No longer dirty */
1104     texture->texture_rgb.dirty = FALSE;
1105 }
1106
1107 static void texture3d_sub_resource_add_dirty_region(struct wined3d_resource *sub_resource,
1108         const struct wined3d_box *dirty_region)
1109 {
1110     volume_add_dirty_box(volume_from_resource(sub_resource), dirty_region);
1111 }
1112
1113 static void texture3d_sub_resource_cleanup(struct wined3d_resource *sub_resource)
1114 {
1115     struct wined3d_volume *volume = volume_from_resource(sub_resource);
1116
1117     /* Cleanup the container. */
1118     volume_set_container(volume, NULL);
1119     wined3d_volume_decref(volume);
1120 }
1121
1122 /* Do not call while under the GL lock. */
1123 static void texture3d_unload(struct wined3d_resource *resource)
1124 {
1125     struct wined3d_texture *texture = wined3d_texture_from_resource(resource);
1126     UINT i;
1127
1128     TRACE("texture %p.\n", texture);
1129
1130     for (i = 0; i < texture->level_count; ++i)
1131     {
1132         struct wined3d_resource *sub_resource = texture->sub_resources[i];
1133         sub_resource->resource_ops->resource_unload(sub_resource);
1134     }
1135
1136     wined3d_texture_unload(texture);
1137 }
1138
1139 static const struct wined3d_texture_ops texture3d_ops =
1140 {
1141     texture3d_bind,
1142     texture3d_preload,
1143     texture3d_sub_resource_add_dirty_region,
1144     texture3d_sub_resource_cleanup,
1145 };
1146
1147 static const struct wined3d_resource_ops texture3d_resource_ops =
1148 {
1149     texture3d_unload,
1150 };
1151
1152 static HRESULT volumetexture_init(struct wined3d_texture *texture, UINT width, UINT height,
1153         UINT depth, UINT levels, struct wined3d_device *device, DWORD usage, enum wined3d_format_id format_id,
1154         enum wined3d_pool pool, void *parent, const struct wined3d_parent_ops *parent_ops)
1155 {
1156     const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
1157     const struct wined3d_format *format = wined3d_get_format(gl_info, format_id);
1158     UINT tmp_w, tmp_h, tmp_d;
1159     unsigned int i;
1160     HRESULT hr;
1161
1162     /* TODO: It should only be possible to create textures for formats
1163      * that are reported as supported. */
1164     if (WINED3DFMT_UNKNOWN >= format_id)
1165     {
1166         WARN("(%p) : Texture cannot be created with a format of WINED3DFMT_UNKNOWN.\n", texture);
1167         return WINED3DERR_INVALIDCALL;
1168     }
1169
1170     if (!gl_info->supported[EXT_TEXTURE3D])
1171     {
1172         WARN("(%p) : Texture cannot be created - no volume texture support.\n", texture);
1173         return WINED3DERR_INVALIDCALL;
1174     }
1175
1176     /* Calculate levels for mip mapping. */
1177     if (usage & WINED3DUSAGE_AUTOGENMIPMAP)
1178     {
1179         if (!gl_info->supported[SGIS_GENERATE_MIPMAP])
1180         {
1181             WARN("No mipmap generation support, returning D3DERR_INVALIDCALL.\n");
1182             return WINED3DERR_INVALIDCALL;
1183         }
1184
1185         if (levels > 1)
1186         {
1187             WARN("D3DUSAGE_AUTOGENMIPMAP is set, and level count > 1, returning D3DERR_INVALIDCALL.\n");
1188             return WINED3DERR_INVALIDCALL;
1189         }
1190
1191         levels = 1;
1192     }
1193     else if (!levels)
1194     {
1195         levels = wined3d_log2i(max(max(width, height), depth)) + 1;
1196         TRACE("Calculated levels = %u.\n", levels);
1197     }
1198
1199     if (!gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO])
1200     {
1201         UINT pow2_w, pow2_h, pow2_d;
1202         pow2_w = 1;
1203         while (pow2_w < width) pow2_w <<= 1;
1204         pow2_h = 1;
1205         while (pow2_h < height) pow2_h <<= 1;
1206         pow2_d = 1;
1207         while (pow2_d < depth) pow2_d <<= 1;
1208
1209         if (pow2_w != width || pow2_h != height || pow2_d != depth)
1210         {
1211             if (pool == WINED3D_POOL_SCRATCH)
1212             {
1213                 WARN("Creating a scratch NPOT volume texture despite lack of HW support.\n");
1214             }
1215             else
1216             {
1217                 WARN("Attempted to create a NPOT volume texture (%u,%u,%u) without GL support.\n",
1218                         width, height, depth);
1219                 return WINED3DERR_INVALIDCALL;
1220             }
1221         }
1222     }
1223
1224     hr = wined3d_texture_init(texture, &texture3d_ops, 1, levels,
1225             WINED3D_RTYPE_VOLUME_TEXTURE, device, usage, format, pool,
1226             parent, parent_ops, &texture3d_resource_ops);
1227     if (FAILED(hr))
1228     {
1229         WARN("Failed to initialize texture, returning %#x.\n", hr);
1230         return hr;
1231     }
1232
1233     texture->pow2_matrix[0] = 1.0f;
1234     texture->pow2_matrix[5] = 1.0f;
1235     texture->pow2_matrix[10] = 1.0f;
1236     texture->pow2_matrix[15] = 1.0f;
1237     texture->target = GL_TEXTURE_3D;
1238
1239     /* Generate all the surfaces. */
1240     tmp_w = width;
1241     tmp_h = height;
1242     tmp_d = depth;
1243
1244     for (i = 0; i < texture->level_count; ++i)
1245     {
1246         struct wined3d_volume *volume;
1247
1248         /* Create the volume. */
1249         hr = device->device_parent->ops->create_volume(device->device_parent, parent,
1250                 tmp_w, tmp_h, tmp_d, format_id, pool, usage, &volume);
1251         if (FAILED(hr))
1252         {
1253             ERR("Creating a volume for the volume texture failed, hr %#x.\n", hr);
1254             wined3d_texture_cleanup(texture);
1255             return hr;
1256         }
1257
1258         /* Set its container to this texture. */
1259         volume_set_container(volume, texture);
1260         texture->sub_resources[i] = &volume->resource;
1261
1262         /* Calculate the next mipmap level. */
1263         tmp_w = max(1, tmp_w >> 1);
1264         tmp_h = max(1, tmp_h >> 1);
1265         tmp_d = max(1, tmp_d >> 1);
1266     }
1267
1268     return WINED3D_OK;
1269 }
1270
1271 HRESULT CDECL wined3d_texture_create_2d(struct wined3d_device *device, UINT width, UINT height,
1272         UINT level_count, DWORD usage, enum wined3d_format_id format_id, enum wined3d_pool pool, void *parent,
1273         const struct wined3d_parent_ops *parent_ops, struct wined3d_texture **texture)
1274 {
1275     struct wined3d_texture *object;
1276     HRESULT hr;
1277
1278     TRACE("device %p, width %u, height %u, level_count %u, usage %#x\n",
1279             device, width, height, level_count, usage);
1280     TRACE("format %s, pool %#x, parent %p, parent_ops %p, texture %p.\n",
1281             debug_d3dformat(format_id), pool, parent, parent_ops, texture);
1282
1283     object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object));
1284     if (!object)
1285     {
1286         *texture = NULL;
1287         return WINED3DERR_OUTOFVIDEOMEMORY;
1288     }
1289
1290     hr = texture_init(object, width, height, level_count,
1291             device, usage, format_id, pool, parent, parent_ops);
1292     if (FAILED(hr))
1293     {
1294         WARN("Failed to initialize texture, returning %#x.\n", hr);
1295         HeapFree(GetProcessHeap(), 0, object);
1296         *texture = NULL;
1297         return hr;
1298     }
1299
1300     TRACE("Created texture %p.\n", object);
1301     *texture = object;
1302
1303     return WINED3D_OK;
1304 }
1305
1306 HRESULT CDECL wined3d_texture_create_3d(struct wined3d_device *device, UINT width, UINT height, UINT depth,
1307         UINT level_count, DWORD usage, enum wined3d_format_id format_id, enum wined3d_pool pool, void *parent,
1308         const struct wined3d_parent_ops *parent_ops, struct wined3d_texture **texture)
1309 {
1310     struct wined3d_texture *object;
1311     HRESULT hr;
1312
1313     TRACE("device %p, width %u, height %u, depth %u, level_count %u, usage %#x\n",
1314             device, width, height, depth, level_count, usage);
1315     TRACE("format %s, pool %#x, parent %p, parent_ops %p, texture %p.\n",
1316             debug_d3dformat(format_id), pool, parent, parent_ops, texture);
1317
1318     object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object));
1319     if (!object)
1320     {
1321         *texture = NULL;
1322         return WINED3DERR_OUTOFVIDEOMEMORY;
1323     }
1324
1325     hr = volumetexture_init(object, width, height, depth, level_count,
1326             device, usage, format_id, pool, parent, parent_ops);
1327     if (FAILED(hr))
1328     {
1329         WARN("Failed to initialize volumetexture, returning %#x\n", hr);
1330         HeapFree(GetProcessHeap(), 0, object);
1331         *texture = NULL;
1332         return hr;
1333     }
1334
1335     TRACE("Created texture %p.\n", object);
1336     *texture = object;
1337
1338     return WINED3D_OK;
1339 }
1340
1341 HRESULT CDECL wined3d_texture_create_cube(struct wined3d_device *device, UINT edge_length,
1342         UINT level_count, DWORD usage, enum wined3d_format_id format_id, enum wined3d_pool pool, void *parent,
1343         const struct wined3d_parent_ops *parent_ops, struct wined3d_texture **texture)
1344 {
1345     struct wined3d_texture *object;
1346     HRESULT hr;
1347
1348     TRACE("device %p, edge_length %u, level_count %u, usage %#x\n",
1349             device, edge_length, level_count, usage);
1350     TRACE("format %s, pool %#x, parent %p, parent_ops %p, texture %p.\n",
1351             debug_d3dformat(format_id), pool, parent, parent_ops, texture);
1352
1353     object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object));
1354     if (!object)
1355     {
1356         *texture = NULL;
1357         return WINED3DERR_OUTOFVIDEOMEMORY;
1358     }
1359
1360     hr = cubetexture_init(object, edge_length, level_count,
1361             device, usage, format_id, pool, parent, parent_ops);
1362     if (FAILED(hr))
1363     {
1364         WARN("Failed to initialize cubetexture, returning %#x\n", hr);
1365         HeapFree(GetProcessHeap(), 0, object);
1366         *texture = NULL;
1367         return hr;
1368     }
1369
1370     TRACE("Created texture %p.\n", object);
1371     *texture = object;
1372
1373     return WINED3D_OK;
1374 }