wined3d: Get rid of ENTER_GL / LEAVE_GL.
[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-2008 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     UINT pow2_edge_length;
764     unsigned int i, j;
765     UINT tmp_w;
766     HRESULT hr;
767
768     /* TODO: It should only be possible to create textures for formats
769      * that are reported as supported. */
770     if (WINED3DFMT_UNKNOWN >= format_id)
771     {
772         WARN("(%p) : Texture cannot be created with a format of WINED3DFMT_UNKNOWN.\n", texture);
773         return WINED3DERR_INVALIDCALL;
774     }
775
776     if (!gl_info->supported[ARB_TEXTURE_CUBE_MAP] && pool != WINED3D_POOL_SCRATCH)
777     {
778         WARN("(%p) : Tried to create not supported cube texture.\n", texture);
779         return WINED3DERR_INVALIDCALL;
780     }
781
782     /* Calculate levels for mip mapping */
783     if (usage & WINED3DUSAGE_AUTOGENMIPMAP)
784     {
785         if (!gl_info->supported[SGIS_GENERATE_MIPMAP])
786         {
787             WARN("No mipmap generation support, returning D3DERR_INVALIDCALL.\n");
788             return WINED3DERR_INVALIDCALL;
789         }
790
791         if (levels > 1)
792         {
793             WARN("D3DUSAGE_AUTOGENMIPMAP is set, and level count > 1, returning D3DERR_INVALIDCALL.\n");
794             return WINED3DERR_INVALIDCALL;
795         }
796
797         levels = 1;
798     }
799     else if (!levels)
800     {
801         levels = wined3d_log2i(edge_length) + 1;
802         TRACE("Calculated levels = %u.\n", levels);
803     }
804
805     hr = wined3d_texture_init(texture, &texture2d_ops, 6, levels,
806             WINED3D_RTYPE_CUBE_TEXTURE, device, usage, format, pool,
807             parent, parent_ops, &texture2d_resource_ops);
808     if (FAILED(hr))
809     {
810         WARN("Failed to initialize texture, returning %#x\n", hr);
811         return hr;
812     }
813
814     /* Find the nearest pow2 match. */
815     pow2_edge_length = 1;
816     while (pow2_edge_length < edge_length) pow2_edge_length <<= 1;
817
818     if (gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO] || (edge_length == pow2_edge_length))
819     {
820         /* Precalculated scaling for 'faked' non power of two texture coords. */
821         texture->pow2_matrix[0] = 1.0f;
822         texture->pow2_matrix[5] = 1.0f;
823         texture->pow2_matrix[10] = 1.0f;
824         texture->pow2_matrix[15] = 1.0f;
825     }
826     else
827     {
828         /* Precalculated scaling for 'faked' non power of two texture coords. */
829         texture->pow2_matrix[0] = ((float)edge_length) / ((float)pow2_edge_length);
830         texture->pow2_matrix[5] = ((float)edge_length) / ((float)pow2_edge_length);
831         texture->pow2_matrix[10] = ((float)edge_length) / ((float)pow2_edge_length);
832         texture->pow2_matrix[15] = 1.0f;
833         texture->flags &= ~WINED3D_TEXTURE_POW2_MAT_IDENT;
834     }
835     texture->target = GL_TEXTURE_CUBE_MAP_ARB;
836
837     /* Generate all the surfaces. */
838     tmp_w = edge_length;
839     for (i = 0; i < texture->level_count; ++i)
840     {
841         /* Create the 6 faces. */
842         for (j = 0; j < 6; ++j)
843         {
844             static const GLenum cube_targets[6] =
845             {
846                 GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB,
847                 GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB,
848                 GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB,
849                 GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB,
850                 GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB,
851                 GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB,
852             };
853             UINT idx = j * texture->level_count + i;
854             struct wined3d_surface *surface;
855
856             if (FAILED(hr = device->device_parent->ops->create_texture_surface(device->device_parent,
857                     parent, tmp_w, tmp_w, format_id, usage, pool, idx, &surface)))
858             {
859                 FIXME("(%p) Failed to create surface, hr %#x.\n", texture, hr);
860                 wined3d_texture_cleanup(texture);
861                 return hr;
862             }
863
864             surface_set_container(surface, WINED3D_CONTAINER_TEXTURE, texture);
865             surface_set_texture_target(surface, cube_targets[j], i);
866             texture->sub_resources[idx] = &surface->resource;
867             TRACE("Created surface level %u @ %p.\n", i, surface);
868         }
869         tmp_w = max(1, tmp_w >> 1);
870     }
871
872     return WINED3D_OK;
873 }
874
875 static HRESULT texture_init(struct wined3d_texture *texture, UINT width, UINT height, UINT levels,
876         struct wined3d_device *device, DWORD usage, enum wined3d_format_id format_id, enum wined3d_pool pool,
877         void *parent, const struct wined3d_parent_ops *parent_ops)
878 {
879     const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
880     const struct wined3d_format *format = wined3d_get_format(gl_info, format_id);
881     UINT pow2_width, pow2_height;
882     UINT tmp_w, tmp_h;
883     unsigned int i;
884     HRESULT hr;
885
886     /* TODO: It should only be possible to create textures for formats
887      * that are reported as supported. */
888     if (WINED3DFMT_UNKNOWN >= format_id)
889     {
890         WARN("(%p) : Texture cannot be created with a format of WINED3DFMT_UNKNOWN.\n", texture);
891         return WINED3DERR_INVALIDCALL;
892     }
893
894     /* Non-power2 support. */
895     if (gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO])
896     {
897         pow2_width = width;
898         pow2_height = height;
899     }
900     else
901     {
902         /* Find the nearest pow2 match. */
903         pow2_width = pow2_height = 1;
904         while (pow2_width < width) pow2_width <<= 1;
905         while (pow2_height < height) pow2_height <<= 1;
906
907         if (pow2_width != width || pow2_height != height)
908         {
909             if (levels > 1)
910             {
911                 WARN("Attempted to create a mipmapped np2 texture without unconditional np2 support.\n");
912                 return WINED3DERR_INVALIDCALL;
913             }
914             levels = 1;
915         }
916     }
917
918     /* Calculate levels for mip mapping. */
919     if (usage & WINED3DUSAGE_AUTOGENMIPMAP)
920     {
921         if (!gl_info->supported[SGIS_GENERATE_MIPMAP])
922         {
923             WARN("No mipmap generation support, returning WINED3DERR_INVALIDCALL.\n");
924             return WINED3DERR_INVALIDCALL;
925         }
926
927         if (levels > 1)
928         {
929             WARN("D3DUSAGE_AUTOGENMIPMAP is set, and level count > 1, returning WINED3DERR_INVALIDCALL.\n");
930             return WINED3DERR_INVALIDCALL;
931         }
932
933         levels = 1;
934     }
935     else if (!levels)
936     {
937         levels = wined3d_log2i(max(width, height)) + 1;
938         TRACE("Calculated levels = %u.\n", levels);
939     }
940
941     hr = wined3d_texture_init(texture, &texture2d_ops, 1, levels,
942             WINED3D_RTYPE_TEXTURE, device, usage, format, pool,
943             parent, parent_ops, &texture2d_resource_ops);
944     if (FAILED(hr))
945     {
946         WARN("Failed to initialize texture, returning %#x.\n", hr);
947         return hr;
948     }
949
950     /* Precalculated scaling for 'faked' non power of two texture coords.
951      * Second also don't use ARB_TEXTURE_RECTANGLE in case the surface format is P8 and EXT_PALETTED_TEXTURE
952      * is used in combination with texture uploads (RTL_READTEX). The reason is that EXT_PALETTED_TEXTURE
953      * doesn't work in combination with ARB_TEXTURE_RECTANGLE. */
954     if (gl_info->supported[WINED3D_GL_NORMALIZED_TEXRECT] && (width != pow2_width || height != pow2_height))
955     {
956         texture->pow2_matrix[0] = 1.0f;
957         texture->pow2_matrix[5] = 1.0f;
958         texture->pow2_matrix[10] = 1.0f;
959         texture->pow2_matrix[15] = 1.0f;
960         texture->target = GL_TEXTURE_2D;
961         texture->flags |= WINED3D_TEXTURE_COND_NP2;
962         texture->min_mip_lookup = minMipLookup_noFilter;
963     }
964     else if (gl_info->supported[ARB_TEXTURE_RECTANGLE] && (width != pow2_width || height != pow2_height)
965             && !(format->id == WINED3DFMT_P8_UINT && gl_info->supported[EXT_PALETTED_TEXTURE]
966             && wined3d_settings.rendertargetlock_mode == RTL_READTEX))
967     {
968         if (width != 1 || height != 1)
969             texture->flags &= ~WINED3D_TEXTURE_POW2_MAT_IDENT;
970
971         texture->pow2_matrix[0] = (float)width;
972         texture->pow2_matrix[5] = (float)height;
973         texture->pow2_matrix[10] = 1.0f;
974         texture->pow2_matrix[15] = 1.0f;
975         texture->target = GL_TEXTURE_RECTANGLE_ARB;
976         texture->flags |= WINED3D_TEXTURE_COND_NP2;
977
978         if (texture->resource.format->flags & WINED3DFMT_FLAG_FILTERING)
979             texture->min_mip_lookup = minMipLookup_noMip;
980         else
981             texture->min_mip_lookup = minMipLookup_noFilter;
982     }
983     else
984     {
985         if ((width != pow2_width) || (height != pow2_height))
986         {
987             texture->pow2_matrix[0] = (((float)width) / ((float)pow2_width));
988             texture->pow2_matrix[5] = (((float)height) / ((float)pow2_height));
989             texture->flags &= ~WINED3D_TEXTURE_POW2_MAT_IDENT;
990         }
991         else
992         {
993             texture->pow2_matrix[0] = 1.0f;
994             texture->pow2_matrix[5] = 1.0f;
995         }
996
997         texture->pow2_matrix[10] = 1.0f;
998         texture->pow2_matrix[15] = 1.0f;
999         texture->target = GL_TEXTURE_2D;
1000     }
1001     TRACE("xf(%f) yf(%f)\n", texture->pow2_matrix[0], texture->pow2_matrix[5]);
1002
1003     /* Generate all the surfaces. */
1004     tmp_w = width;
1005     tmp_h = height;
1006     for (i = 0; i < texture->level_count; ++i)
1007     {
1008         struct wined3d_surface *surface;
1009
1010         /* Use the callback to create the texture surface. */
1011         if (FAILED(hr = device->device_parent->ops->create_texture_surface(device->device_parent,
1012                 parent, tmp_w, tmp_h, format->id, usage, pool, i, &surface)))
1013         {
1014             FIXME("Failed to create surface %p, hr %#x\n", texture, hr);
1015             wined3d_texture_cleanup(texture);
1016             return hr;
1017         }
1018
1019         surface_set_container(surface, WINED3D_CONTAINER_TEXTURE, texture);
1020         surface_set_texture_target(surface, texture->target, i);
1021         texture->sub_resources[i] = &surface->resource;
1022         TRACE("Created surface level %u @ %p.\n", i, surface);
1023         /* Calculate the next mipmap level. */
1024         tmp_w = max(1, tmp_w >> 1);
1025         tmp_h = max(1, tmp_h >> 1);
1026     }
1027
1028     return WINED3D_OK;
1029 }
1030
1031 /* Context activation is done by the caller. */
1032 static HRESULT texture3d_bind(struct wined3d_texture *texture,
1033         struct wined3d_context *context, BOOL srgb)
1034 {
1035     BOOL dummy;
1036
1037     TRACE("texture %p, context %p, srgb %#x.\n", texture, context, srgb);
1038
1039     return wined3d_texture_bind(texture, context, srgb, &dummy);
1040 }
1041
1042 /* Do not call while under the GL lock. */
1043 static void texture3d_preload(struct wined3d_texture *texture, enum WINED3DSRGB srgb)
1044 {
1045     struct wined3d_device *device = texture->resource.device;
1046     struct wined3d_context *context;
1047     BOOL srgb_was_toggled = FALSE;
1048     unsigned int i;
1049
1050     TRACE("texture %p, srgb %#x.\n", texture, srgb);
1051
1052     /* TODO: Use already acquired context when possible. */
1053     context = context_acquire(device, NULL);
1054     if (texture->resource.bind_count > 0)
1055     {
1056         BOOL texture_srgb = texture->flags & WINED3D_TEXTURE_IS_SRGB;
1057         BOOL sampler_srgb = texture_srgb_mode(texture, srgb);
1058         srgb_was_toggled = !texture_srgb != !sampler_srgb;
1059
1060         if (srgb_was_toggled)
1061         {
1062             if (sampler_srgb)
1063                 texture->flags |= WINED3D_TEXTURE_IS_SRGB;
1064             else
1065                 texture->flags &= ~WINED3D_TEXTURE_IS_SRGB;
1066         }
1067     }
1068
1069     /* If the texture is marked dirty or the sRGB sampler setting has changed
1070      * since the last load then reload the volumes. */
1071     if (texture->texture_rgb.dirty)
1072     {
1073         for (i = 0; i < texture->level_count; ++i)
1074         {
1075             volume_load(volume_from_resource(texture->sub_resources[i]), context, i,
1076                     texture->flags & WINED3D_TEXTURE_IS_SRGB);
1077         }
1078     }
1079     else if (srgb_was_toggled)
1080     {
1081         for (i = 0; i < texture->level_count; ++i)
1082         {
1083             struct wined3d_volume *volume = volume_from_resource(texture->sub_resources[i]);
1084             volume_add_dirty_box(volume, NULL);
1085             volume_load(volume, context, i, texture->flags & WINED3D_TEXTURE_IS_SRGB);
1086         }
1087     }
1088     else
1089     {
1090         TRACE("Texture %p not dirty, nothing to do.\n", texture);
1091     }
1092
1093     context_release(context);
1094
1095     /* No longer dirty */
1096     texture->texture_rgb.dirty = FALSE;
1097 }
1098
1099 static void texture3d_sub_resource_add_dirty_region(struct wined3d_resource *sub_resource,
1100         const struct wined3d_box *dirty_region)
1101 {
1102     volume_add_dirty_box(volume_from_resource(sub_resource), dirty_region);
1103 }
1104
1105 static void texture3d_sub_resource_cleanup(struct wined3d_resource *sub_resource)
1106 {
1107     struct wined3d_volume *volume = volume_from_resource(sub_resource);
1108
1109     /* Cleanup the container. */
1110     volume_set_container(volume, NULL);
1111     wined3d_volume_decref(volume);
1112 }
1113
1114 /* Do not call while under the GL lock. */
1115 static void texture3d_unload(struct wined3d_resource *resource)
1116 {
1117     struct wined3d_texture *texture = wined3d_texture_from_resource(resource);
1118     UINT i;
1119
1120     TRACE("texture %p.\n", texture);
1121
1122     for (i = 0; i < texture->level_count; ++i)
1123     {
1124         struct wined3d_resource *sub_resource = texture->sub_resources[i];
1125         sub_resource->resource_ops->resource_unload(sub_resource);
1126     }
1127
1128     wined3d_texture_unload(texture);
1129 }
1130
1131 static const struct wined3d_texture_ops texture3d_ops =
1132 {
1133     texture3d_bind,
1134     texture3d_preload,
1135     texture3d_sub_resource_add_dirty_region,
1136     texture3d_sub_resource_cleanup,
1137 };
1138
1139 static const struct wined3d_resource_ops texture3d_resource_ops =
1140 {
1141     texture3d_unload,
1142 };
1143
1144 static HRESULT volumetexture_init(struct wined3d_texture *texture, UINT width, UINT height,
1145         UINT depth, UINT levels, struct wined3d_device *device, DWORD usage, enum wined3d_format_id format_id,
1146         enum wined3d_pool pool, void *parent, const struct wined3d_parent_ops *parent_ops)
1147 {
1148     const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
1149     const struct wined3d_format *format = wined3d_get_format(gl_info, format_id);
1150     UINT tmp_w, tmp_h, tmp_d;
1151     unsigned int i;
1152     HRESULT hr;
1153
1154     /* TODO: It should only be possible to create textures for formats
1155      * that are reported as supported. */
1156     if (WINED3DFMT_UNKNOWN >= format_id)
1157     {
1158         WARN("(%p) : Texture cannot be created with a format of WINED3DFMT_UNKNOWN.\n", texture);
1159         return WINED3DERR_INVALIDCALL;
1160     }
1161
1162     if (!gl_info->supported[EXT_TEXTURE3D])
1163     {
1164         WARN("(%p) : Texture cannot be created - no volume texture support.\n", texture);
1165         return WINED3DERR_INVALIDCALL;
1166     }
1167
1168     /* Calculate levels for mip mapping. */
1169     if (usage & WINED3DUSAGE_AUTOGENMIPMAP)
1170     {
1171         if (!gl_info->supported[SGIS_GENERATE_MIPMAP])
1172         {
1173             WARN("No mipmap generation support, returning D3DERR_INVALIDCALL.\n");
1174             return WINED3DERR_INVALIDCALL;
1175         }
1176
1177         if (levels > 1)
1178         {
1179             WARN("D3DUSAGE_AUTOGENMIPMAP is set, and level count > 1, returning D3DERR_INVALIDCALL.\n");
1180             return WINED3DERR_INVALIDCALL;
1181         }
1182
1183         levels = 1;
1184     }
1185     else if (!levels)
1186     {
1187         levels = wined3d_log2i(max(max(width, height), depth)) + 1;
1188         TRACE("Calculated levels = %u.\n", levels);
1189     }
1190
1191     hr = wined3d_texture_init(texture, &texture3d_ops, 1, levels,
1192             WINED3D_RTYPE_VOLUME_TEXTURE, device, usage, format, pool,
1193             parent, parent_ops, &texture3d_resource_ops);
1194     if (FAILED(hr))
1195     {
1196         WARN("Failed to initialize texture, returning %#x.\n", hr);
1197         return hr;
1198     }
1199
1200     /* Is NP2 support for volumes needed? */
1201     texture->pow2_matrix[0] = 1.0f;
1202     texture->pow2_matrix[5] = 1.0f;
1203     texture->pow2_matrix[10] = 1.0f;
1204     texture->pow2_matrix[15] = 1.0f;
1205     texture->target = GL_TEXTURE_3D;
1206
1207     /* Generate all the surfaces. */
1208     tmp_w = width;
1209     tmp_h = height;
1210     tmp_d = depth;
1211
1212     for (i = 0; i < texture->level_count; ++i)
1213     {
1214         struct wined3d_volume *volume;
1215
1216         /* Create the volume. */
1217         hr = device->device_parent->ops->create_volume(device->device_parent, parent,
1218                 tmp_w, tmp_h, tmp_d, format_id, pool, usage, &volume);
1219         if (FAILED(hr))
1220         {
1221             ERR("Creating a volume for the volume texture failed, hr %#x.\n", hr);
1222             wined3d_texture_cleanup(texture);
1223             return hr;
1224         }
1225
1226         /* Set its container to this texture. */
1227         volume_set_container(volume, texture);
1228         texture->sub_resources[i] = &volume->resource;
1229
1230         /* Calculate the next mipmap level. */
1231         tmp_w = max(1, tmp_w >> 1);
1232         tmp_h = max(1, tmp_h >> 1);
1233         tmp_d = max(1, tmp_d >> 1);
1234     }
1235
1236     return WINED3D_OK;
1237 }
1238
1239 HRESULT CDECL wined3d_texture_create_2d(struct wined3d_device *device, UINT width, UINT height,
1240         UINT level_count, DWORD usage, enum wined3d_format_id format_id, enum wined3d_pool pool, void *parent,
1241         const struct wined3d_parent_ops *parent_ops, struct wined3d_texture **texture)
1242 {
1243     struct wined3d_texture *object;
1244     HRESULT hr;
1245
1246     TRACE("device %p, width %u, height %u, level_count %u, usage %#x\n",
1247             device, width, height, level_count, usage);
1248     TRACE("format %s, pool %#x, parent %p, parent_ops %p, texture %p.\n",
1249             debug_d3dformat(format_id), pool, parent, parent_ops, texture);
1250
1251     object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object));
1252     if (!object)
1253     {
1254         ERR("Out of memory.\n");
1255         *texture = NULL;
1256         return WINED3DERR_OUTOFVIDEOMEMORY;
1257     }
1258
1259     hr = texture_init(object, width, height, level_count,
1260             device, usage, format_id, pool, parent, parent_ops);
1261     if (FAILED(hr))
1262     {
1263         WARN("Failed to initialize texture, returning %#x.\n", hr);
1264         HeapFree(GetProcessHeap(), 0, object);
1265         *texture = NULL;
1266         return hr;
1267     }
1268
1269     TRACE("Created texture %p.\n", object);
1270     *texture = object;
1271
1272     return WINED3D_OK;
1273 }
1274
1275 HRESULT CDECL wined3d_texture_create_3d(struct wined3d_device *device, UINT width, UINT height, UINT depth,
1276         UINT level_count, DWORD usage, enum wined3d_format_id format_id, enum wined3d_pool pool, void *parent,
1277         const struct wined3d_parent_ops *parent_ops, struct wined3d_texture **texture)
1278 {
1279     struct wined3d_texture *object;
1280     HRESULT hr;
1281
1282     TRACE("device %p, width %u, height %u, depth %u, level_count %u, usage %#x\n",
1283             device, width, height, depth, level_count, usage);
1284     TRACE("format %s, pool %#x, parent %p, parent_ops %p, texture %p.\n",
1285             debug_d3dformat(format_id), pool, parent, parent_ops, texture);
1286
1287     object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object));
1288     if (!object)
1289     {
1290         ERR("Out of memory\n");
1291         *texture = NULL;
1292         return WINED3DERR_OUTOFVIDEOMEMORY;
1293     }
1294
1295     hr = volumetexture_init(object, width, height, depth, level_count,
1296             device, usage, format_id, pool, parent, parent_ops);
1297     if (FAILED(hr))
1298     {
1299         WARN("Failed to initialize volumetexture, returning %#x\n", hr);
1300         HeapFree(GetProcessHeap(), 0, object);
1301         *texture = NULL;
1302         return hr;
1303     }
1304
1305     TRACE("Created texture %p.\n", object);
1306     *texture = object;
1307
1308     return WINED3D_OK;
1309 }
1310
1311 HRESULT CDECL wined3d_texture_create_cube(struct wined3d_device *device, UINT edge_length,
1312         UINT level_count, DWORD usage, enum wined3d_format_id format_id, enum wined3d_pool pool, void *parent,
1313         const struct wined3d_parent_ops *parent_ops, struct wined3d_texture **texture)
1314 {
1315     struct wined3d_texture *object;
1316     HRESULT hr;
1317
1318     TRACE("device %p, edge_length %u, level_count %u, usage %#x\n",
1319             device, edge_length, level_count, usage);
1320     TRACE("format %s, pool %#x, parent %p, parent_ops %p, texture %p.\n",
1321             debug_d3dformat(format_id), pool, parent, parent_ops, texture);
1322
1323     object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object));
1324     if (!object)
1325     {
1326         ERR("Out of memory\n");
1327         *texture = NULL;
1328         return WINED3DERR_OUTOFVIDEOMEMORY;
1329     }
1330
1331     hr = cubetexture_init(object, edge_length, level_count,
1332             device, usage, format_id, pool, parent, parent_ops);
1333     if (FAILED(hr))
1334     {
1335         WARN("Failed to initialize cubetexture, returning %#x\n", hr);
1336         HeapFree(GetProcessHeap(), 0, object);
1337         *texture = NULL;
1338         return hr;
1339     }
1340
1341     TRACE("Created texture %p.\n", object);
1342     *texture = object;
1343
1344     return WINED3D_OK;
1345 }