wined3d: Generalize GetSurfaceLevel() / GetVolumeLevel() to GetSubResource().
[wine] / dlls / wined3d / volumetexture.c
1 /*
2  * IWineD3DVolumeTexture implementation
3  *
4  * Copyright 2002-2005 Jason Edmeades
5  * Copyright 2002-2005 Raphael Junqueira
6  * Copyright 2005 Oliver Stieber
7  * Copyright 2009-2010 Henri Verbeet for CodeWeavers
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22  */
23
24 #include "config.h"
25 #include "wined3d_private.h"
26
27 WINE_DEFAULT_DEBUG_CHANNEL(d3d_texture);
28
29 /* Context activation is done by the caller. */
30 static HRESULT volumetexture_bind(IWineD3DBaseTextureImpl *texture,
31         const struct wined3d_gl_info *gl_info, BOOL srgb)
32 {
33     BOOL dummy;
34
35     TRACE("texture %p, gl_info %p, srgb %#x.\n", texture, gl_info, srgb);
36
37     return basetexture_bind(texture, gl_info, srgb, &dummy);
38 }
39
40 /* Do not call while under the GL lock. */
41 static void volumetexture_preload(IWineD3DBaseTextureImpl *texture, enum WINED3DSRGB srgb)
42 {
43     IWineD3DDeviceImpl *device = texture->resource.device;
44     struct wined3d_context *context = NULL;
45     BOOL srgb_mode = texture->baseTexture.is_srgb;
46     BOOL srgb_was_toggled = FALSE;
47     unsigned int i;
48
49     TRACE("texture %p, srgb %#x.\n", texture, srgb);
50
51     if (!device->isInDraw) context = context_acquire(device, NULL);
52     else if (texture->baseTexture.bindCount > 0)
53     {
54         srgb_mode = device->stateBlock->state.sampler_states[texture->baseTexture.sampler][WINED3DSAMP_SRGBTEXTURE];
55         srgb_was_toggled = texture->baseTexture.is_srgb != srgb_mode;
56         texture->baseTexture.is_srgb = srgb_mode;
57     }
58
59     /* If the texture is marked dirty or the srgb sampler setting has changed
60      * since the last load then reload the volumes. */
61     if (texture->baseTexture.texture_rgb.dirty)
62     {
63         for (i = 0; i < texture->baseTexture.level_count; ++i)
64         {
65             volume_load(volume_from_resource(texture->baseTexture.sub_resources[i]), i, srgb_mode);
66         }
67     }
68     else if (srgb_was_toggled)
69     {
70         for (i = 0; i < texture->baseTexture.level_count; ++i)
71         {
72             IWineD3DVolumeImpl *volume = volume_from_resource(texture->baseTexture.sub_resources[i]);
73             volume_add_dirty_box(volume, NULL);
74             volume_load(volume, i, srgb_mode);
75         }
76     }
77     else
78     {
79         TRACE("Texture %p not dirty, nothing to do.\n", texture);
80     }
81
82     if (context) context_release(context);
83
84     /* No longer dirty */
85     texture->baseTexture.texture_rgb.dirty = FALSE;
86 }
87
88 /* Do not call while under the GL lock. */
89 static void volumetexture_unload(struct wined3d_resource *resource)
90 {
91     IWineD3DBaseTextureImpl *texture = basetexture_from_resource(resource);
92     unsigned int i;
93
94     TRACE("texture %p.\n", texture);
95
96     for (i = 0; i < texture->baseTexture.level_count; ++i)
97     {
98         struct wined3d_resource *sub_resource = texture->baseTexture.sub_resources[i];
99         sub_resource->resource_ops->resource_unload(sub_resource);
100     }
101
102     basetexture_unload(texture);
103 }
104
105 static const struct wined3d_texture_ops volumetexture_ops =
106 {
107     volumetexture_bind,
108     volumetexture_preload,
109 };
110
111 static const struct wined3d_resource_ops volumetexture_resource_ops =
112 {
113     volumetexture_unload,
114 };
115
116 static void volumetexture_cleanup(IWineD3DVolumeTextureImpl *This)
117 {
118     unsigned int i;
119
120     TRACE("(%p) : Cleaning up.\n", This);
121
122     for (i = 0; i < This->baseTexture.level_count; ++i)
123     {
124         struct wined3d_resource *sub_resource = This->baseTexture.sub_resources[i];
125
126         if (sub_resource)
127         {
128             IWineD3DVolumeImpl *volume = volume_from_resource(sub_resource);
129
130             /* Cleanup the container. */
131             volume_set_container(volume, NULL);
132             IWineD3DVolume_Release((IWineD3DVolume *)volume);
133         }
134     }
135     basetexture_cleanup((IWineD3DBaseTextureImpl *)This);
136 }
137
138 /* *******************************************
139    IWineD3DTexture IUnknown parts follow
140    ******************************************* */
141
142 static HRESULT WINAPI IWineD3DVolumeTextureImpl_QueryInterface(IWineD3DVolumeTexture *iface, REFIID riid, LPVOID *ppobj)
143 {
144     IWineD3DVolumeTextureImpl *This = (IWineD3DVolumeTextureImpl *)iface;
145     TRACE("(%p)->(%s,%p)\n",This,debugstr_guid(riid),ppobj);
146     if (IsEqualGUID(riid, &IID_IUnknown)
147         || IsEqualGUID(riid, &IID_IWineD3DBase)
148         || IsEqualGUID(riid, &IID_IWineD3DResource)
149         || IsEqualGUID(riid, &IID_IWineD3DBaseTexture)
150         || IsEqualGUID(riid, &IID_IWineD3DVolumeTexture)) {
151         IUnknown_AddRef(iface);
152         *ppobj = This;
153         return S_OK;
154     }
155     *ppobj = NULL;
156     return E_NOINTERFACE;
157 }
158
159 static ULONG WINAPI IWineD3DVolumeTextureImpl_AddRef(IWineD3DVolumeTexture *iface) {
160     IWineD3DVolumeTextureImpl *This = (IWineD3DVolumeTextureImpl *)iface;
161     TRACE("(%p) : AddRef increasing from %d\n", This, This->resource.ref);
162     return InterlockedIncrement(&This->resource.ref);
163 }
164
165 /* Do not call while under the GL lock. */
166 static ULONG WINAPI IWineD3DVolumeTextureImpl_Release(IWineD3DVolumeTexture *iface) {
167     IWineD3DVolumeTextureImpl *This = (IWineD3DVolumeTextureImpl *)iface;
168     ULONG ref;
169     TRACE("(%p) : Releasing from %d\n", This, This->resource.ref);
170     ref = InterlockedDecrement(&This->resource.ref);
171     if (!ref)
172     {
173         volumetexture_cleanup(This);
174         This->resource.parent_ops->wined3d_object_destroyed(This->resource.parent);
175         HeapFree(GetProcessHeap(), 0, This);
176     }
177     return ref;
178 }
179
180 /* ****************************************************
181    IWineD3DVolumeTexture IWineD3DResource parts follow
182    **************************************************** */
183 static HRESULT WINAPI IWineD3DVolumeTextureImpl_SetPrivateData(IWineD3DVolumeTexture *iface,
184         REFGUID riid, const void *data, DWORD data_size, DWORD flags)
185 {
186     return resource_set_private_data(&((IWineD3DVolumeTextureImpl *)iface)->resource, riid, data, data_size, flags);
187 }
188
189 static HRESULT WINAPI IWineD3DVolumeTextureImpl_GetPrivateData(IWineD3DVolumeTexture *iface,
190         REFGUID guid, void *data, DWORD *data_size)
191 {
192     return resource_get_private_data(&((IWineD3DVolumeTextureImpl *)iface)->resource, guid, data, data_size);
193 }
194
195 static HRESULT WINAPI IWineD3DVolumeTextureImpl_FreePrivateData(IWineD3DVolumeTexture *iface, REFGUID refguid)
196 {
197     return resource_free_private_data(&((IWineD3DVolumeTextureImpl *)iface)->resource, refguid);
198 }
199
200 static DWORD WINAPI IWineD3DVolumeTextureImpl_SetPriority(IWineD3DVolumeTexture *iface, DWORD priority)
201 {
202     return resource_set_priority(&((IWineD3DVolumeTextureImpl *)iface)->resource, priority);
203 }
204
205 static DWORD WINAPI IWineD3DVolumeTextureImpl_GetPriority(IWineD3DVolumeTexture *iface)
206 {
207     return resource_get_priority(&((IWineD3DVolumeTextureImpl *)iface)->resource);
208 }
209
210 static void WINAPI IWineD3DVolumeTextureImpl_PreLoad(IWineD3DVolumeTexture *iface)
211 {
212     volumetexture_preload((IWineD3DBaseTextureImpl *)iface, SRGB_ANY);
213 }
214
215 static WINED3DRESOURCETYPE WINAPI IWineD3DVolumeTextureImpl_GetType(IWineD3DVolumeTexture *iface)
216 {
217     return resource_get_type(&((IWineD3DVolumeTextureImpl *)iface)->resource);
218 }
219
220 static void * WINAPI IWineD3DVolumeTextureImpl_GetParent(IWineD3DVolumeTexture *iface)
221 {
222     TRACE("iface %p\n", iface);
223
224     return ((IWineD3DVolumeTextureImpl *)iface)->resource.parent;
225 }
226
227 /* ******************************************************
228    IWineD3DVolumeTexture IWineD3DBaseTexture parts follow
229    ****************************************************** */
230 static DWORD WINAPI IWineD3DVolumeTextureImpl_SetLOD(IWineD3DVolumeTexture *iface, DWORD LODNew) {
231     return basetexture_set_lod((IWineD3DBaseTextureImpl *)iface, LODNew);
232 }
233
234 static DWORD WINAPI IWineD3DVolumeTextureImpl_GetLOD(IWineD3DVolumeTexture *iface) {
235     return basetexture_get_lod((IWineD3DBaseTextureImpl *)iface);
236 }
237
238 static DWORD WINAPI IWineD3DVolumeTextureImpl_GetLevelCount(IWineD3DVolumeTexture *iface)
239 {
240     return basetexture_get_level_count((IWineD3DBaseTextureImpl *)iface);
241 }
242
243 static HRESULT WINAPI IWineD3DVolumeTextureImpl_SetAutoGenFilterType(IWineD3DVolumeTexture *iface,
244         WINED3DTEXTUREFILTERTYPE FilterType)
245 {
246   return basetexture_set_autogen_filter_type((IWineD3DBaseTextureImpl *)iface, FilterType);
247 }
248
249 static WINED3DTEXTUREFILTERTYPE WINAPI IWineD3DVolumeTextureImpl_GetAutoGenFilterType(IWineD3DVolumeTexture *iface)
250 {
251   return basetexture_get_autogen_filter_type((IWineD3DBaseTextureImpl *)iface);
252 }
253
254 static void WINAPI IWineD3DVolumeTextureImpl_GenerateMipSubLevels(IWineD3DVolumeTexture *iface)
255 {
256     basetexture_generate_mipmaps((IWineD3DBaseTextureImpl *)iface);
257 }
258
259 static BOOL WINAPI IWineD3DVolumeTextureImpl_IsCondNP2(IWineD3DVolumeTexture *iface)
260 {
261     TRACE("iface %p.\n", iface);
262
263     return FALSE;
264 }
265
266 static HRESULT WINAPI IWineD3DVolumeTextureImpl_GetSubResourceDesc(IWineD3DVolumeTexture *iface,
267         UINT sub_resource_idx, struct wined3d_resource_desc *desc)
268 {
269     IWineD3DBaseTextureImpl *texture = (IWineD3DBaseTextureImpl *)iface;
270     struct wined3d_resource *sub_resource;
271
272     TRACE("iface %p, sub_resource_idx %u, desc %p.\n", iface, sub_resource_idx, desc);
273
274     if (!(sub_resource = basetexture_get_sub_resource(texture, sub_resource_idx)))
275     {
276         WARN("Failed to get sub-resource.\n");
277         return WINED3DERR_INVALIDCALL;
278     }
279
280     wined3d_resource_get_desc(sub_resource, desc);
281
282     return WINED3D_OK;
283 }
284
285 static struct wined3d_resource * WINAPI IWineD3DVolumeTextureImpl_GetSubResource(IWineD3DVolumeTexture *iface,
286         UINT sub_resource_idx)
287 {
288     IWineD3DBaseTextureImpl *texture = (IWineD3DBaseTextureImpl *)iface;
289
290     TRACE("iface %p, sub_resource_idx %u.\n", iface, sub_resource_idx);
291
292     return basetexture_get_sub_resource(texture, sub_resource_idx);
293 }
294
295 static HRESULT WINAPI IWineD3DVolumeTextureImpl_Map(IWineD3DVolumeTexture *iface,
296         UINT sub_resource_idx, WINED3DLOCKED_BOX *locked_box, const WINED3DBOX *box, DWORD flags)
297 {
298     IWineD3DBaseTextureImpl *texture = (IWineD3DBaseTextureImpl *)iface;
299     struct wined3d_resource *sub_resource;
300
301     TRACE("iface %p, sub_resource_idx %u, locked_box %p, box %p, flags %#x.\n",
302             iface, sub_resource_idx, locked_box, box, flags);
303
304     if (!(sub_resource = basetexture_get_sub_resource(texture, sub_resource_idx)))
305     {
306         WARN("Failed to get sub-resource.\n");
307         return WINED3DERR_INVALIDCALL;
308     }
309
310     return IWineD3DVolume_Map((IWineD3DVolume *)volume_from_resource(sub_resource),
311             locked_box, box, flags);
312 }
313
314 static HRESULT WINAPI IWineD3DVolumeTextureImpl_Unmap(IWineD3DVolumeTexture *iface, UINT sub_resource_idx)
315 {
316     IWineD3DBaseTextureImpl *texture = (IWineD3DBaseTextureImpl *)iface;
317     struct wined3d_resource *sub_resource;
318
319     TRACE("iface %p, sub_resource_idx %u.\n", iface, sub_resource_idx);
320
321     if (!(sub_resource = basetexture_get_sub_resource(texture, sub_resource_idx)))
322     {
323         WARN("Failed to get sub-resource.\n");
324         return WINED3DERR_INVALIDCALL;
325     }
326
327     return IWineD3DVolume_Unmap((IWineD3DVolume *)volume_from_resource(sub_resource));
328 }
329
330 static HRESULT WINAPI IWineD3DVolumeTextureImpl_AddDirtyBox(IWineD3DVolumeTexture *iface, const WINED3DBOX *dirty_box)
331 {
332     IWineD3DBaseTextureImpl *texture = (IWineD3DBaseTextureImpl *)iface;
333     struct wined3d_resource *sub_resource;
334
335     TRACE("iface %p, dirty_box %p.\n", iface, dirty_box);
336
337     if (!(sub_resource = basetexture_get_sub_resource(texture, 0)))
338     {
339         WARN("Failed to get sub-resource.\n");
340         return WINED3DERR_INVALIDCALL;
341     }
342
343     basetexture_set_dirty(texture, TRUE);
344     volume_add_dirty_box(volume_from_resource(sub_resource), dirty_box);
345
346     return WINED3D_OK;
347 }
348
349 static const IWineD3DVolumeTextureVtbl IWineD3DVolumeTexture_Vtbl =
350 {
351     /* IUnknown */
352     IWineD3DVolumeTextureImpl_QueryInterface,
353     IWineD3DVolumeTextureImpl_AddRef,
354     IWineD3DVolumeTextureImpl_Release,
355     /* resource */
356     IWineD3DVolumeTextureImpl_GetParent,
357     IWineD3DVolumeTextureImpl_SetPrivateData,
358     IWineD3DVolumeTextureImpl_GetPrivateData,
359     IWineD3DVolumeTextureImpl_FreePrivateData,
360     IWineD3DVolumeTextureImpl_SetPriority,
361     IWineD3DVolumeTextureImpl_GetPriority,
362     IWineD3DVolumeTextureImpl_PreLoad,
363     IWineD3DVolumeTextureImpl_GetType,
364     /* BaseTexture */
365     IWineD3DVolumeTextureImpl_SetLOD,
366     IWineD3DVolumeTextureImpl_GetLOD,
367     IWineD3DVolumeTextureImpl_GetLevelCount,
368     IWineD3DVolumeTextureImpl_SetAutoGenFilterType,
369     IWineD3DVolumeTextureImpl_GetAutoGenFilterType,
370     IWineD3DVolumeTextureImpl_GenerateMipSubLevels,
371     IWineD3DVolumeTextureImpl_IsCondNP2,
372     IWineD3DVolumeTextureImpl_GetSubResourceDesc,
373     IWineD3DVolumeTextureImpl_GetSubResource,
374     /* volume texture */
375     IWineD3DVolumeTextureImpl_Map,
376     IWineD3DVolumeTextureImpl_Unmap,
377     IWineD3DVolumeTextureImpl_AddDirtyBox
378 };
379
380 HRESULT volumetexture_init(IWineD3DVolumeTextureImpl *texture, UINT width, UINT height,
381         UINT depth, UINT levels, IWineD3DDeviceImpl *device, DWORD usage, enum wined3d_format_id format_id,
382         WINED3DPOOL pool, void *parent, const struct wined3d_parent_ops *parent_ops)
383 {
384     const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
385     const struct wined3d_format *format = wined3d_get_format(gl_info, format_id);
386     UINT tmp_w, tmp_h, tmp_d;
387     unsigned int i;
388     HRESULT hr;
389
390     /* TODO: It should only be possible to create textures for formats
391      * that are reported as supported. */
392     if (WINED3DFMT_UNKNOWN >= format_id)
393     {
394         WARN("(%p) : Texture cannot be created with a format of WINED3DFMT_UNKNOWN.\n", texture);
395         return WINED3DERR_INVALIDCALL;
396     }
397
398     if (!gl_info->supported[EXT_TEXTURE3D])
399     {
400         WARN("(%p) : Texture cannot be created - no volume texture support.\n", texture);
401         return WINED3DERR_INVALIDCALL;
402     }
403
404     /* Calculate levels for mip mapping. */
405     if (usage & WINED3DUSAGE_AUTOGENMIPMAP)
406     {
407         if (!gl_info->supported[SGIS_GENERATE_MIPMAP])
408         {
409             WARN("No mipmap generation support, returning D3DERR_INVALIDCALL.\n");
410             return WINED3DERR_INVALIDCALL;
411         }
412
413         if (levels > 1)
414         {
415             WARN("D3DUSAGE_AUTOGENMIPMAP is set, and level count > 1, returning D3DERR_INVALIDCALL.\n");
416             return WINED3DERR_INVALIDCALL;
417         }
418
419         levels = 1;
420     }
421     else if (!levels)
422     {
423         levels = wined3d_log2i(max(max(width, height), depth)) + 1;
424         TRACE("Calculated levels = %u.\n", levels);
425     }
426
427     texture->lpVtbl = &IWineD3DVolumeTexture_Vtbl;
428
429     hr = basetexture_init((IWineD3DBaseTextureImpl *)texture, &volumetexture_ops,
430             1, levels, WINED3DRTYPE_VOLUMETEXTURE, device, usage, format, pool,
431             parent, parent_ops, &volumetexture_resource_ops);
432     if (FAILED(hr))
433     {
434         WARN("Failed to initialize basetexture, returning %#x.\n", hr);
435         return hr;
436     }
437
438     /* Is NP2 support for volumes needed? */
439     texture->baseTexture.pow2Matrix[0] = 1.0f;
440     texture->baseTexture.pow2Matrix[5] = 1.0f;
441     texture->baseTexture.pow2Matrix[10] = 1.0f;
442     texture->baseTexture.pow2Matrix[15] = 1.0f;
443     texture->baseTexture.target = GL_TEXTURE_3D;
444
445     /* Generate all the surfaces. */
446     tmp_w = width;
447     tmp_h = height;
448     tmp_d = depth;
449
450     for (i = 0; i < texture->baseTexture.level_count; ++i)
451     {
452         IWineD3DVolume *volume;
453
454         /* Create the volume. */
455         hr = IWineD3DDeviceParent_CreateVolume(device->device_parent, parent,
456                 tmp_w, tmp_h, tmp_d, format_id, pool, usage, &volume);
457         if (FAILED(hr))
458         {
459             ERR("Creating a volume for the volume texture failed, hr %#x.\n", hr);
460             volumetexture_cleanup(texture);
461             return hr;
462         }
463
464         /* Set its container to this texture. */
465         volume_set_container((IWineD3DVolumeImpl *)volume, texture);
466         texture->baseTexture.sub_resources[i] = &((IWineD3DVolumeImpl *)volume)->resource;
467
468         /* Calculate the next mipmap level. */
469         tmp_w = max(1, tmp_w >> 1);
470         tmp_h = max(1, tmp_h >> 1);
471         tmp_d = max(1, tmp_d >> 1);
472     }
473
474     return WINED3D_OK;
475 }