wined3d: Make IWineD3DVolumeImpl_GetResource() static.
[wine] / dlls / wined3d / cubetexture.c
1 /*
2  * IWineD3DCubeTexture implementation
3  *
4  * Copyright 2002-2005 Jason Edmeades
5  * Copyright 2002-2005 Raphael Junqueira
6  * Copyright 2005 Oliver Stieber
7  * Copyright 2007-2008 Stefan Dösinger for CodeWeavers
8  * Copyright 2009-2010 Henri Verbeet for CodeWeavers
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Lesser General Public
12  * License as published by the Free Software Foundation; either
13  * version 2.1 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public
21  * License along with this library; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23  */
24
25 #include "config.h"
26 #include "wined3d_private.h"
27
28 WINE_DEFAULT_DEBUG_CHANNEL(d3d_texture);
29
30 /* Context activation is done by the caller. */
31 static HRESULT cubetexture_bind(IWineD3DBaseTextureImpl *texture,
32         const struct wined3d_gl_info *gl_info, BOOL srgb)
33 {
34     BOOL set_gl_texture_desc;
35     HRESULT hr;
36
37     TRACE("texture %p, gl_info %p, srgb %#x.\n", texture, gl_info, srgb);
38
39     hr = basetexture_bind(texture, gl_info, srgb, &set_gl_texture_desc);
40     if (set_gl_texture_desc && SUCCEEDED(hr))
41     {
42         UINT sub_count = texture->baseTexture.level_count * texture->baseTexture.layer_count;
43         BOOL srgb_tex = !gl_info->supported[EXT_TEXTURE_SRGB_DECODE] && texture->baseTexture.is_srgb;
44         GLuint name = srgb_tex ? texture->baseTexture.texture_srgb.name : texture->baseTexture.texture_rgb.name;
45         UINT i;
46
47         for (i = 0; i < sub_count; ++i)
48         {
49             IWineD3DSurfaceImpl *surface = surface_from_resource(texture->baseTexture.sub_resources[i]);
50             surface_set_texture_name(surface, name, srgb_tex);
51         }
52     }
53
54     return hr;
55 }
56
57 /* Do not call while under the GL lock. */
58 static void cubetexture_preload(IWineD3DBaseTextureImpl *texture, enum WINED3DSRGB srgb)
59 {
60     UINT sub_count = texture->baseTexture.level_count * texture->baseTexture.layer_count;
61     IWineD3DDeviceImpl *device = texture->resource.device;
62     const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
63     struct wined3d_context *context = NULL;
64     struct gl_texture *gl_tex;
65     BOOL srgb_mode;
66     UINT i;
67
68     TRACE("texture %p, srgb %#x.\n", texture, srgb);
69
70     switch (srgb)
71     {
72         case SRGB_RGB:
73             srgb_mode = FALSE;
74             break;
75
76         case SRGB_BOTH:
77             cubetexture_preload(texture, SRGB_RGB);
78             /* Fallthrough */
79
80         case SRGB_SRGB:
81             srgb_mode = TRUE;
82             break;
83
84         default:
85             srgb_mode = texture->baseTexture.is_srgb;
86             break;
87     }
88
89     gl_tex = basetexture_get_gl_texture(texture, gl_info, srgb_mode);
90
91     /* We only have to activate a context for gl when we're not drawing.
92      * In most cases PreLoad will be called during draw and a context was
93      * activated at the beginning of drawPrimitive. */
94     if (!device->isInDraw)
95     {
96         /* No danger of recursive calls, context_acquire() sets isInDraw to true
97          * when loading offscreen render targets into their texture. */
98         context = context_acquire(device, NULL);
99     }
100
101     if (texture->resource.format->id == WINED3DFMT_P8_UINT
102             || texture->resource.format->id == WINED3DFMT_P8_UINT_A8_UNORM)
103     {
104         for (i = 0; i < sub_count; ++i)
105         {
106             IWineD3DSurfaceImpl *surface = surface_from_resource(texture->baseTexture.sub_resources[i]);
107
108             if (palette9_changed(surface))
109             {
110                 TRACE("Reloading surface %p because the d3d8/9 palette was changed.\n", surface);
111                 /* TODO: This is not necessarily needed with hw palettized texture support. */
112                 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
113                 /* Make sure the texture is reloaded because of the palette change,
114                  * this kills performance though :( */
115                 surface_modify_location(surface, SFLAG_INTEXTURE, FALSE);
116             }
117         }
118     }
119
120     /* If the texture is marked dirty or the srgb sampler setting has changed
121      * since the last load then reload the surfaces. */
122     if (gl_tex->dirty)
123     {
124         for (i = 0; i < sub_count; ++i)
125         {
126             surface_load(surface_from_resource(texture->baseTexture.sub_resources[i]), srgb_mode);
127         }
128     }
129     else
130     {
131         TRACE("Texture %p not dirty, nothing to do.\n" , texture);
132     }
133
134     /* No longer dirty. */
135     gl_tex->dirty = FALSE;
136
137     if (context) context_release(context);
138 }
139
140 /* Do not call while under the GL lock. */
141 static void cubetexture_unload(struct wined3d_resource *resource)
142 {
143     IWineD3DBaseTextureImpl *texture = basetexture_from_resource(resource);
144     UINT sub_count = texture->baseTexture.level_count * texture->baseTexture.layer_count;
145     UINT i;
146
147     TRACE("texture %p.\n", texture);
148
149     for (i = 0; i < sub_count; ++i)
150     {
151         struct wined3d_resource *sub_resource = texture->baseTexture.sub_resources[i];
152         IWineD3DSurfaceImpl *surface = surface_from_resource(sub_resource);
153
154         sub_resource->resource_ops->resource_unload(sub_resource);
155         surface_set_texture_name(surface, 0, TRUE);
156         surface_set_texture_name(surface, 0, FALSE);
157     }
158
159     basetexture_unload(texture);
160 }
161
162 static const struct wined3d_texture_ops cubetexture_ops =
163 {
164     cubetexture_bind,
165     cubetexture_preload,
166 };
167
168 static const struct wined3d_resource_ops cubetexture_resource_ops =
169 {
170     cubetexture_unload,
171 };
172
173 static void cubetexture_cleanup(IWineD3DCubeTextureImpl *This)
174 {
175     UINT sub_count = This->baseTexture.level_count * This->baseTexture.layer_count;
176     UINT i;
177
178     TRACE("(%p) : Cleaning up.\n", This);
179
180     for (i = 0; i < sub_count; ++i)
181     {
182         struct wined3d_resource *sub_resource = This->baseTexture.sub_resources[i];
183
184         if (sub_resource)
185         {
186             IWineD3DSurfaceImpl *surface = surface_from_resource(sub_resource);
187
188             /* Clean out the texture name we gave to the surface so that the
189              * surface doesn't try and release it. */
190             surface_set_texture_name(surface, 0, TRUE);
191             surface_set_texture_name(surface, 0, FALSE);
192             surface_set_texture_target(surface, 0);
193             surface_set_container(surface, WINED3D_CONTAINER_NONE, NULL);
194             IWineD3DSurface_Release((IWineD3DSurface *)surface);
195         }
196     }
197     basetexture_cleanup((IWineD3DBaseTextureImpl *)This);
198 }
199
200 /* *******************************************
201    IWineD3DCubeTexture IUnknown parts follow
202    ******************************************* */
203
204 static HRESULT WINAPI IWineD3DCubeTextureImpl_QueryInterface(IWineD3DCubeTexture *iface, REFIID riid, LPVOID *ppobj)
205 {
206     IWineD3DCubeTextureImpl *This = (IWineD3DCubeTextureImpl *)iface;
207     TRACE("(%p)->(%s,%p)\n",This,debugstr_guid(riid),ppobj);
208     if (IsEqualGUID(riid, &IID_IUnknown)
209         || IsEqualGUID(riid, &IID_IWineD3DBase)
210         || IsEqualGUID(riid, &IID_IWineD3DResource)
211         || IsEqualGUID(riid, &IID_IWineD3DBaseTexture)
212         || IsEqualGUID(riid, &IID_IWineD3DCubeTexture)) {
213         IUnknown_AddRef(iface);
214         *ppobj = This;
215         return S_OK;
216     }
217     *ppobj = NULL;
218     return E_NOINTERFACE;
219 }
220
221 static ULONG WINAPI IWineD3DCubeTextureImpl_AddRef(IWineD3DCubeTexture *iface) {
222     IWineD3DCubeTextureImpl *This = (IWineD3DCubeTextureImpl *)iface;
223     TRACE("(%p) : AddRef increasing from %d\n", This, This->resource.ref);
224     return InterlockedIncrement(&This->resource.ref);
225 }
226
227 /* Do not call while under the GL lock. */
228 static ULONG WINAPI IWineD3DCubeTextureImpl_Release(IWineD3DCubeTexture *iface) {
229     IWineD3DCubeTextureImpl *This = (IWineD3DCubeTextureImpl *)iface;
230     ULONG ref;
231     TRACE("(%p) : Releasing from %d\n", This, This->resource.ref);
232     ref = InterlockedDecrement(&This->resource.ref);
233     if (!ref)
234     {
235         cubetexture_cleanup(This);
236         This->resource.parent_ops->wined3d_object_destroyed(This->resource.parent);
237         HeapFree(GetProcessHeap(), 0, This);
238     }
239     return ref;
240 }
241
242 /* ****************************************************
243    IWineD3DCubeTexture IWineD3DResource parts follow
244    **************************************************** */
245 static HRESULT WINAPI IWineD3DCubeTextureImpl_SetPrivateData(IWineD3DCubeTexture *iface,
246         REFGUID riid, const void *data, DWORD data_size, DWORD flags)
247 {
248     return resource_set_private_data(&((IWineD3DCubeTextureImpl *)iface)->resource, riid, data, data_size, flags);
249 }
250
251 static HRESULT WINAPI IWineD3DCubeTextureImpl_GetPrivateData(IWineD3DCubeTexture *iface,
252         REFGUID guid, void *data, DWORD *data_size)
253 {
254     return resource_get_private_data(&((IWineD3DCubeTextureImpl *)iface)->resource, guid, data, data_size);
255 }
256
257 static HRESULT WINAPI IWineD3DCubeTextureImpl_FreePrivateData(IWineD3DCubeTexture *iface, REFGUID refguid)
258 {
259     return resource_free_private_data(&((IWineD3DCubeTextureImpl *)iface)->resource, refguid);
260 }
261
262 static DWORD WINAPI IWineD3DCubeTextureImpl_SetPriority(IWineD3DCubeTexture *iface, DWORD priority)
263 {
264     return resource_set_priority(&((IWineD3DCubeTextureImpl *)iface)->resource, priority);
265 }
266
267 static DWORD WINAPI IWineD3DCubeTextureImpl_GetPriority(IWineD3DCubeTexture *iface)
268 {
269     return resource_get_priority(&((IWineD3DCubeTextureImpl *)iface)->resource);
270 }
271
272 /* Do not call while under the GL lock. */
273 static void WINAPI IWineD3DCubeTextureImpl_PreLoad(IWineD3DCubeTexture *iface)
274 {
275     cubetexture_preload((IWineD3DBaseTextureImpl *)iface, SRGB_ANY);
276 }
277
278 static WINED3DRESOURCETYPE WINAPI IWineD3DCubeTextureImpl_GetType(IWineD3DCubeTexture *iface)
279 {
280     return resource_get_type(&((IWineD3DCubeTextureImpl *)iface)->resource);
281 }
282
283 static void * WINAPI IWineD3DCubeTextureImpl_GetParent(IWineD3DCubeTexture *iface)
284 {
285     TRACE("iface %p.\n", iface);
286
287     return ((IWineD3DCubeTextureImpl *)iface)->resource.parent;
288 }
289
290 /* ******************************************************
291    IWineD3DCubeTexture IWineD3DBaseTexture parts follow
292    ****************************************************** */
293 static DWORD WINAPI IWineD3DCubeTextureImpl_SetLOD(IWineD3DCubeTexture *iface, DWORD LODNew) {
294     return basetexture_set_lod((IWineD3DBaseTextureImpl *)iface, LODNew);
295 }
296
297 static DWORD WINAPI IWineD3DCubeTextureImpl_GetLOD(IWineD3DCubeTexture *iface) {
298     return basetexture_get_lod((IWineD3DBaseTextureImpl *)iface);
299 }
300
301 static DWORD WINAPI IWineD3DCubeTextureImpl_GetLevelCount(IWineD3DCubeTexture *iface)
302 {
303     return basetexture_get_level_count((IWineD3DBaseTextureImpl *)iface);
304 }
305
306 static HRESULT WINAPI IWineD3DCubeTextureImpl_SetAutoGenFilterType(IWineD3DCubeTexture *iface,
307         WINED3DTEXTUREFILTERTYPE FilterType)
308 {
309   return basetexture_set_autogen_filter_type((IWineD3DBaseTextureImpl *)iface, FilterType);
310 }
311
312 static WINED3DTEXTUREFILTERTYPE WINAPI IWineD3DCubeTextureImpl_GetAutoGenFilterType(IWineD3DCubeTexture *iface)
313 {
314   return basetexture_get_autogen_filter_type((IWineD3DBaseTextureImpl *)iface);
315 }
316
317 static void WINAPI IWineD3DCubeTextureImpl_GenerateMipSubLevels(IWineD3DCubeTexture *iface)
318 {
319     basetexture_generate_mipmaps((IWineD3DBaseTextureImpl *)iface);
320 }
321
322 static BOOL WINAPI IWineD3DCubeTextureImpl_IsCondNP2(IWineD3DCubeTexture *iface)
323 {
324     TRACE("iface %p.\n", iface);
325
326     return FALSE;
327 }
328
329 static struct wined3d_resource * WINAPI IWineD3DCubeTextureImpl_GetSubResource(IWineD3DCubeTexture *iface,
330         UINT sub_resource_idx)
331 {
332     IWineD3DBaseTextureImpl *texture = (IWineD3DBaseTextureImpl *)iface;
333
334     TRACE("iface %p, sub_resource_idx %u.\n", iface, sub_resource_idx);
335
336     return basetexture_get_sub_resource(texture, sub_resource_idx);
337 }
338
339 static HRESULT WINAPI IWineD3DCubeTextureImpl_AddDirtyRegion(IWineD3DCubeTexture *iface,
340         UINT layer, const WINED3DBOX *dirty_region)
341 {
342     IWineD3DBaseTextureImpl *texture = (IWineD3DBaseTextureImpl *)iface;
343     struct wined3d_resource *sub_resource;
344
345     TRACE("iface %p, layer %u, dirty_region %p.\n", iface, layer, dirty_region);
346
347     if (!(sub_resource = basetexture_get_sub_resource(texture, layer * texture->baseTexture.level_count)))
348     {
349         WARN("Failed to get sub-resource.\n");
350         return WINED3DERR_INVALIDCALL;
351     }
352
353     basetexture_set_dirty(texture, TRUE);
354     surface_add_dirty_rect(surface_from_resource(sub_resource), dirty_region);
355
356     return WINED3D_OK;
357 }
358
359 static const IWineD3DCubeTextureVtbl IWineD3DCubeTexture_Vtbl =
360 {
361     /* IUnknown */
362     IWineD3DCubeTextureImpl_QueryInterface,
363     IWineD3DCubeTextureImpl_AddRef,
364     IWineD3DCubeTextureImpl_Release,
365     /* IWineD3DResource */
366     IWineD3DCubeTextureImpl_GetParent,
367     IWineD3DCubeTextureImpl_SetPrivateData,
368     IWineD3DCubeTextureImpl_GetPrivateData,
369     IWineD3DCubeTextureImpl_FreePrivateData,
370     IWineD3DCubeTextureImpl_SetPriority,
371     IWineD3DCubeTextureImpl_GetPriority,
372     IWineD3DCubeTextureImpl_PreLoad,
373     IWineD3DCubeTextureImpl_GetType,
374     /* IWineD3DBaseTexture */
375     IWineD3DCubeTextureImpl_SetLOD,
376     IWineD3DCubeTextureImpl_GetLOD,
377     IWineD3DCubeTextureImpl_GetLevelCount,
378     IWineD3DCubeTextureImpl_SetAutoGenFilterType,
379     IWineD3DCubeTextureImpl_GetAutoGenFilterType,
380     IWineD3DCubeTextureImpl_GenerateMipSubLevels,
381     IWineD3DCubeTextureImpl_IsCondNP2,
382     IWineD3DCubeTextureImpl_GetSubResource,
383     IWineD3DCubeTextureImpl_AddDirtyRegion,
384 };
385
386 HRESULT cubetexture_init(IWineD3DCubeTextureImpl *texture, UINT edge_length, UINT levels,
387         IWineD3DDeviceImpl *device, DWORD usage, enum wined3d_format_id format_id, WINED3DPOOL pool,
388         void *parent, const struct wined3d_parent_ops *parent_ops)
389 {
390     const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
391     const struct wined3d_format *format = wined3d_get_format(gl_info, format_id);
392     UINT pow2_edge_length;
393     unsigned int i, j;
394     UINT tmp_w;
395     HRESULT hr;
396
397     /* TODO: It should only be possible to create textures for formats
398      * that are reported as supported. */
399     if (WINED3DFMT_UNKNOWN >= format_id)
400     {
401         WARN("(%p) : Texture cannot be created with a format of WINED3DFMT_UNKNOWN.\n", texture);
402         return WINED3DERR_INVALIDCALL;
403     }
404
405     if (!gl_info->supported[ARB_TEXTURE_CUBE_MAP] && pool != WINED3DPOOL_SCRATCH)
406     {
407         WARN("(%p) : Tried to create not supported cube texture.\n", texture);
408         return WINED3DERR_INVALIDCALL;
409     }
410
411     /* Calculate levels for mip mapping */
412     if (usage & WINED3DUSAGE_AUTOGENMIPMAP)
413     {
414         if (!gl_info->supported[SGIS_GENERATE_MIPMAP])
415         {
416             WARN("No mipmap generation support, returning D3DERR_INVALIDCALL.\n");
417             return WINED3DERR_INVALIDCALL;
418         }
419
420         if (levels > 1)
421         {
422             WARN("D3DUSAGE_AUTOGENMIPMAP is set, and level count > 1, returning D3DERR_INVALIDCALL.\n");
423             return WINED3DERR_INVALIDCALL;
424         }
425
426         levels = 1;
427     }
428     else if (!levels)
429     {
430         levels = wined3d_log2i(edge_length) + 1;
431         TRACE("Calculated levels = %u.\n", levels);
432     }
433
434     texture->lpVtbl = &IWineD3DCubeTexture_Vtbl;
435
436     hr = basetexture_init((IWineD3DBaseTextureImpl *)texture, &cubetexture_ops,
437             6, levels, WINED3DRTYPE_CUBETEXTURE, device, usage, format, pool,
438             parent, parent_ops, &cubetexture_resource_ops);
439     if (FAILED(hr))
440     {
441         WARN("Failed to initialize basetexture, returning %#x\n", hr);
442         return hr;
443     }
444
445     /* Find the nearest pow2 match. */
446     pow2_edge_length = 1;
447     while (pow2_edge_length < edge_length) pow2_edge_length <<= 1;
448
449     if (gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO] || (edge_length == pow2_edge_length))
450     {
451         /* Precalculated scaling for 'faked' non power of two texture coords. */
452         texture->baseTexture.pow2Matrix[0] = 1.0f;
453         texture->baseTexture.pow2Matrix[5] = 1.0f;
454         texture->baseTexture.pow2Matrix[10] = 1.0f;
455         texture->baseTexture.pow2Matrix[15] = 1.0f;
456     }
457     else
458     {
459         /* Precalculated scaling for 'faked' non power of two texture coords. */
460         texture->baseTexture.pow2Matrix[0] = ((float)edge_length) / ((float)pow2_edge_length);
461         texture->baseTexture.pow2Matrix[5] = ((float)edge_length) / ((float)pow2_edge_length);
462         texture->baseTexture.pow2Matrix[10] = ((float)edge_length) / ((float)pow2_edge_length);
463         texture->baseTexture.pow2Matrix[15] = 1.0f;
464         texture->baseTexture.pow2Matrix_identity = FALSE;
465     }
466     texture->baseTexture.target = GL_TEXTURE_CUBE_MAP_ARB;
467
468     /* Generate all the surfaces. */
469     tmp_w = edge_length;
470     for (i = 0; i < texture->baseTexture.level_count; ++i)
471     {
472         /* Create the 6 faces. */
473         for (j = 0; j < 6; ++j)
474         {
475             static const GLenum cube_targets[6] =
476             {
477                 GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB,
478                 GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB,
479                 GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB,
480                 GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB,
481                 GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB,
482                 GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB,
483             };
484             UINT idx = j * texture->baseTexture.level_count + i;
485             IWineD3DSurface *surface;
486
487             hr = IWineD3DDeviceParent_CreateSurface(device->device_parent, parent, tmp_w, tmp_w,
488                     format_id, usage, pool, i /* Level */, j, &surface);
489             if (FAILED(hr))
490             {
491                 FIXME("(%p) Failed to create surface, hr %#x.\n", texture, hr);
492                 cubetexture_cleanup(texture);
493                 return hr;
494             }
495
496             surface_set_container((IWineD3DSurfaceImpl *)surface, WINED3D_CONTAINER_TEXTURE, (IWineD3DBase *)texture);
497             surface_set_texture_target((IWineD3DSurfaceImpl *)surface, cube_targets[j]);
498             texture->baseTexture.sub_resources[idx] = &((IWineD3DSurfaceImpl *)surface)->resource;
499             TRACE("Created surface level %u @ %p.\n", i, surface);
500         }
501         tmp_w = max(1, tmp_w >> 1);
502     }
503
504     return WINED3D_OK;
505 }