wined3d: Pass an IWineD3DResourceImpl pointer to resource_get_private_data().
[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, BOOL srgb)
31 {
32     BOOL dummy;
33
34     TRACE("texture %p, srgb %#x.\n", texture, srgb);
35
36     return basetexture_bind(texture, srgb, &dummy);
37 }
38
39 /* Do not call while under the GL lock. */
40 static void volumetexture_preload(IWineD3DBaseTextureImpl *texture, enum WINED3DSRGB srgb)
41 {
42     IWineD3DDeviceImpl *device = texture->resource.device;
43     const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
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 (gl_info->supported[EXT_TEXTURE_SRGB] && 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             IWineD3DVolume *volume = (IWineD3DVolume *)texture->baseTexture.sub_resources[i];
66             IWineD3DVolume_LoadTexture(volume, i, srgb_mode);
67         }
68     }
69     else if (srgb_was_toggled)
70     {
71         for (i = 0; i < texture->baseTexture.level_count; ++i)
72         {
73             IWineD3DVolume *volume = (IWineD3DVolume *)texture->baseTexture.sub_resources[i];
74             volume_add_dirty_box(volume, NULL);
75             IWineD3DVolume_LoadTexture(volume, i, srgb_mode);
76         }
77     }
78     else
79     {
80         TRACE("Texture %p not dirty, nothing to do.\n", texture);
81     }
82
83     if (context) context_release(context);
84
85     /* No longer dirty */
86     texture->baseTexture.texture_rgb.dirty = FALSE;
87 }
88
89 const struct wined3d_texture_ops volumetexture_ops =
90 {
91     volumetexture_bind,
92     volumetexture_preload,
93 };
94
95 static void volumetexture_cleanup(IWineD3DVolumeTextureImpl *This)
96 {
97     unsigned int i;
98
99     TRACE("(%p) : Cleaning up.\n", This);
100
101     for (i = 0; i < This->baseTexture.level_count; ++i)
102     {
103         IWineD3DVolumeImpl *volume = (IWineD3DVolumeImpl *)This->baseTexture.sub_resources[i];
104
105         if (volume)
106         {
107             /* Cleanup the container. */
108             volume_set_container(volume, NULL);
109             IWineD3DVolume_Release((IWineD3DVolume *)volume);
110         }
111     }
112     basetexture_cleanup((IWineD3DBaseTextureImpl *)This);
113 }
114
115 /* *******************************************
116    IWineD3DTexture IUnknown parts follow
117    ******************************************* */
118
119 static HRESULT WINAPI IWineD3DVolumeTextureImpl_QueryInterface(IWineD3DVolumeTexture *iface, REFIID riid, LPVOID *ppobj)
120 {
121     IWineD3DVolumeTextureImpl *This = (IWineD3DVolumeTextureImpl *)iface;
122     TRACE("(%p)->(%s,%p)\n",This,debugstr_guid(riid),ppobj);
123     if (IsEqualGUID(riid, &IID_IUnknown)
124         || IsEqualGUID(riid, &IID_IWineD3DBase)
125         || IsEqualGUID(riid, &IID_IWineD3DResource)
126         || IsEqualGUID(riid, &IID_IWineD3DBaseTexture)
127         || IsEqualGUID(riid, &IID_IWineD3DVolumeTexture)) {
128         IUnknown_AddRef(iface);
129         *ppobj = This;
130         return S_OK;
131     }
132     *ppobj = NULL;
133     return E_NOINTERFACE;
134 }
135
136 static ULONG WINAPI IWineD3DVolumeTextureImpl_AddRef(IWineD3DVolumeTexture *iface) {
137     IWineD3DVolumeTextureImpl *This = (IWineD3DVolumeTextureImpl *)iface;
138     TRACE("(%p) : AddRef increasing from %d\n", This, This->resource.ref);
139     return InterlockedIncrement(&This->resource.ref);
140 }
141
142 /* Do not call while under the GL lock. */
143 static ULONG WINAPI IWineD3DVolumeTextureImpl_Release(IWineD3DVolumeTexture *iface) {
144     IWineD3DVolumeTextureImpl *This = (IWineD3DVolumeTextureImpl *)iface;
145     ULONG ref;
146     TRACE("(%p) : Releasing from %d\n", This, This->resource.ref);
147     ref = InterlockedDecrement(&This->resource.ref);
148     if (!ref)
149     {
150         volumetexture_cleanup(This);
151         This->resource.parent_ops->wined3d_object_destroyed(This->resource.parent);
152         HeapFree(GetProcessHeap(), 0, This);
153     }
154     return ref;
155 }
156
157 /* ****************************************************
158    IWineD3DVolumeTexture IWineD3DResource parts follow
159    **************************************************** */
160 static HRESULT WINAPI IWineD3DVolumeTextureImpl_SetPrivateData(IWineD3DVolumeTexture *iface,
161         REFGUID riid, const void *data, DWORD data_size, DWORD flags)
162 {
163     return resource_set_private_data((IWineD3DResource *)iface, riid, data, data_size, flags);
164 }
165
166 static HRESULT WINAPI IWineD3DVolumeTextureImpl_GetPrivateData(IWineD3DVolumeTexture *iface,
167         REFGUID guid, void *data, DWORD *data_size)
168 {
169     return resource_get_private_data((IWineD3DResourceImpl *)iface, guid, data, data_size);
170 }
171
172 static HRESULT WINAPI IWineD3DVolumeTextureImpl_FreePrivateData(IWineD3DVolumeTexture *iface, REFGUID refguid)
173 {
174     return resource_free_private_data((IWineD3DResourceImpl *)iface, refguid);
175 }
176
177 static DWORD WINAPI IWineD3DVolumeTextureImpl_SetPriority(IWineD3DVolumeTexture *iface, DWORD PriorityNew) {
178     return resource_set_priority((IWineD3DResource *)iface, PriorityNew);
179 }
180
181 static DWORD WINAPI IWineD3DVolumeTextureImpl_GetPriority(IWineD3DVolumeTexture *iface) {
182     return resource_get_priority((IWineD3DResource *)iface);
183 }
184
185 static void WINAPI IWineD3DVolumeTextureImpl_PreLoad(IWineD3DVolumeTexture *iface)
186 {
187     volumetexture_preload((IWineD3DBaseTextureImpl *)iface, SRGB_ANY);
188 }
189
190 /* Do not call while under the GL lock. */
191 static void WINAPI IWineD3DVolumeTextureImpl_UnLoad(IWineD3DVolumeTexture *iface) {
192     unsigned int i;
193     IWineD3DVolumeTextureImpl *This = (IWineD3DVolumeTextureImpl *)iface;
194     TRACE("(%p)\n", This);
195
196     /* Unload all the surfaces and reset the texture name. If UnLoad was called on the
197      * surface before, this one will be a NOP and vice versa. Unloading an unloaded
198      * surface is fine
199      */
200     for (i = 0; i < This->baseTexture.level_count; ++i)
201     {
202         IWineD3DVolume_UnLoad((IWineD3DVolume *)This->baseTexture.sub_resources[i]);
203     }
204
205     basetexture_unload((IWineD3DBaseTextureImpl *)This);
206 }
207
208 static WINED3DRESOURCETYPE WINAPI IWineD3DVolumeTextureImpl_GetType(IWineD3DVolumeTexture *iface) {
209     return resource_get_type((IWineD3DResource *)iface);
210 }
211
212 static void * WINAPI IWineD3DVolumeTextureImpl_GetParent(IWineD3DVolumeTexture *iface)
213 {
214     TRACE("iface %p\n", iface);
215
216     return ((IWineD3DVolumeTextureImpl *)iface)->resource.parent;
217 }
218
219 /* ******************************************************
220    IWineD3DVolumeTexture IWineD3DBaseTexture parts follow
221    ****************************************************** */
222 static DWORD WINAPI IWineD3DVolumeTextureImpl_SetLOD(IWineD3DVolumeTexture *iface, DWORD LODNew) {
223     return basetexture_set_lod((IWineD3DBaseTextureImpl *)iface, LODNew);
224 }
225
226 static DWORD WINAPI IWineD3DVolumeTextureImpl_GetLOD(IWineD3DVolumeTexture *iface) {
227     return basetexture_get_lod((IWineD3DBaseTextureImpl *)iface);
228 }
229
230 static DWORD WINAPI IWineD3DVolumeTextureImpl_GetLevelCount(IWineD3DVolumeTexture *iface)
231 {
232     return basetexture_get_level_count((IWineD3DBaseTextureImpl *)iface);
233 }
234
235 static HRESULT WINAPI IWineD3DVolumeTextureImpl_SetAutoGenFilterType(IWineD3DVolumeTexture *iface,
236         WINED3DTEXTUREFILTERTYPE FilterType)
237 {
238   return basetexture_set_autogen_filter_type((IWineD3DBaseTextureImpl *)iface, FilterType);
239 }
240
241 static WINED3DTEXTUREFILTERTYPE WINAPI IWineD3DVolumeTextureImpl_GetAutoGenFilterType(IWineD3DVolumeTexture *iface)
242 {
243   return basetexture_get_autogen_filter_type((IWineD3DBaseTextureImpl *)iface);
244 }
245
246 static void WINAPI IWineD3DVolumeTextureImpl_GenerateMipSubLevels(IWineD3DVolumeTexture *iface)
247 {
248     basetexture_generate_mipmaps((IWineD3DBaseTextureImpl *)iface);
249 }
250
251 static BOOL WINAPI IWineD3DVolumeTextureImpl_IsCondNP2(IWineD3DVolumeTexture *iface)
252 {
253     TRACE("iface %p.\n", iface);
254
255     return FALSE;
256 }
257
258 static HRESULT WINAPI IWineD3DVolumeTextureImpl_GetLevelDesc(IWineD3DVolumeTexture *iface,
259         UINT sub_resource_idx, WINED3DVOLUME_DESC *desc)
260 {
261     IWineD3DBaseTextureImpl *texture = (IWineD3DBaseTextureImpl *)iface;
262     IWineD3DVolume *volume;
263
264     TRACE("iface %p, sub_resource_idx %u, desc %p.\n", iface, sub_resource_idx, desc);
265
266     if (!(volume = (IWineD3DVolume *)basetexture_get_sub_resource(texture, sub_resource_idx)))
267     {
268         WARN("Failed to get sub-resource.\n");
269         return WINED3DERR_INVALIDCALL;
270     }
271
272     IWineD3DVolume_GetDesc(volume, desc);
273
274     return WINED3D_OK;
275 }
276
277 static HRESULT WINAPI IWineD3DVolumeTextureImpl_GetVolumeLevel(IWineD3DVolumeTexture *iface,
278         UINT sub_resource_idx, IWineD3DVolume **volume)
279 {
280     IWineD3DBaseTextureImpl *texture = (IWineD3DBaseTextureImpl *)iface;
281     IWineD3DVolume *v;
282
283     TRACE("iface %p, sub_resource_idx %u, volume %p.\n", iface, sub_resource_idx, volume);
284
285     if (!(v= (IWineD3DVolume *)basetexture_get_sub_resource(texture, sub_resource_idx)))
286     {
287         WARN("Failed to get sub-resource.\n");
288         return WINED3DERR_INVALIDCALL;
289     }
290
291     IWineD3DVolume_AddRef(v);
292     *volume = v;
293
294     TRACE("Returning volume %p.\n", *volume);
295
296     return WINED3D_OK;
297 }
298
299 static HRESULT WINAPI IWineD3DVolumeTextureImpl_Map(IWineD3DVolumeTexture *iface,
300         UINT sub_resource_idx, WINED3DLOCKED_BOX *locked_box, const WINED3DBOX *box, DWORD flags)
301 {
302     IWineD3DBaseTextureImpl *texture = (IWineD3DBaseTextureImpl *)iface;
303     IWineD3DVolume *volume;
304
305     TRACE("iface %p, sub_resource_idx %u, locked_box %p, box %p, flags %#x.\n",
306             iface, sub_resource_idx, locked_box, box, flags);
307
308     if (!(volume = (IWineD3DVolume *)basetexture_get_sub_resource(texture, sub_resource_idx)))
309     {
310         WARN("Failed to get sub-resource.\n");
311         return WINED3DERR_INVALIDCALL;
312     }
313
314     return IWineD3DVolume_Map(volume, locked_box, box, flags);
315 }
316
317 static HRESULT WINAPI IWineD3DVolumeTextureImpl_Unmap(IWineD3DVolumeTexture *iface, UINT sub_resource_idx)
318 {
319     IWineD3DBaseTextureImpl *texture = (IWineD3DBaseTextureImpl *)iface;
320     IWineD3DVolume *volume;
321
322     TRACE("iface %p, sub_resource_idx %u.\n", iface, sub_resource_idx);
323
324     if (!(volume = (IWineD3DVolume *)basetexture_get_sub_resource(texture, sub_resource_idx)))
325     {
326         WARN("Failed to get sub-resource.\n");
327         return WINED3DERR_INVALIDCALL;
328     }
329
330     return IWineD3DVolume_Unmap(volume);
331 }
332
333 static HRESULT WINAPI IWineD3DVolumeTextureImpl_AddDirtyBox(IWineD3DVolumeTexture *iface, const WINED3DBOX *dirty_box)
334 {
335     IWineD3DBaseTextureImpl *texture = (IWineD3DBaseTextureImpl *)iface;
336     IWineD3DVolume *volume;
337
338     TRACE("iface %p, dirty_box %p.\n", iface, dirty_box);
339
340     if (!(volume = (IWineD3DVolume *)basetexture_get_sub_resource(texture, 0)))
341     {
342         WARN("Failed to get sub-resource.\n");
343         return WINED3DERR_INVALIDCALL;
344     }
345
346     texture->baseTexture.texture_rgb.dirty = TRUE;
347     texture->baseTexture.texture_srgb.dirty = TRUE;
348     volume_add_dirty_box(volume, dirty_box);
349
350     return WINED3D_OK;
351 }
352
353 static const IWineD3DVolumeTextureVtbl IWineD3DVolumeTexture_Vtbl =
354 {
355     /* IUnknown */
356     IWineD3DVolumeTextureImpl_QueryInterface,
357     IWineD3DVolumeTextureImpl_AddRef,
358     IWineD3DVolumeTextureImpl_Release,
359     /* resource */
360     IWineD3DVolumeTextureImpl_GetParent,
361     IWineD3DVolumeTextureImpl_SetPrivateData,
362     IWineD3DVolumeTextureImpl_GetPrivateData,
363     IWineD3DVolumeTextureImpl_FreePrivateData,
364     IWineD3DVolumeTextureImpl_SetPriority,
365     IWineD3DVolumeTextureImpl_GetPriority,
366     IWineD3DVolumeTextureImpl_PreLoad,
367     IWineD3DVolumeTextureImpl_UnLoad,
368     IWineD3DVolumeTextureImpl_GetType,
369     /* BaseTexture */
370     IWineD3DVolumeTextureImpl_SetLOD,
371     IWineD3DVolumeTextureImpl_GetLOD,
372     IWineD3DVolumeTextureImpl_GetLevelCount,
373     IWineD3DVolumeTextureImpl_SetAutoGenFilterType,
374     IWineD3DVolumeTextureImpl_GetAutoGenFilterType,
375     IWineD3DVolumeTextureImpl_GenerateMipSubLevels,
376     /* not in d3d */
377     IWineD3DVolumeTextureImpl_IsCondNP2,
378     /* volume texture */
379     IWineD3DVolumeTextureImpl_GetLevelDesc,
380     IWineD3DVolumeTextureImpl_GetVolumeLevel,
381     IWineD3DVolumeTextureImpl_Map,
382     IWineD3DVolumeTextureImpl_Unmap,
383     IWineD3DVolumeTextureImpl_AddDirtyBox
384 };
385
386 HRESULT volumetexture_init(IWineD3DVolumeTextureImpl *texture, UINT width, UINT height,
387         UINT depth, UINT levels, IWineD3DDeviceImpl *device, DWORD usage, enum wined3d_format_id format_id,
388         WINED3DPOOL pool, 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 tmp_w, tmp_h, tmp_d;
393     unsigned int i;
394     HRESULT hr;
395
396     /* TODO: It should only be possible to create textures for formats
397      * that are reported as supported. */
398     if (WINED3DFMT_UNKNOWN >= format_id)
399     {
400         WARN("(%p) : Texture cannot be created with a format of WINED3DFMT_UNKNOWN.\n", texture);
401         return WINED3DERR_INVALIDCALL;
402     }
403
404     if (!gl_info->supported[EXT_TEXTURE3D])
405     {
406         WARN("(%p) : Texture cannot be created - no volume texture support.\n", texture);
407         return WINED3DERR_INVALIDCALL;
408     }
409
410     /* Calculate levels for mip mapping. */
411     if (usage & WINED3DUSAGE_AUTOGENMIPMAP)
412     {
413         if (!gl_info->supported[SGIS_GENERATE_MIPMAP])
414         {
415             WARN("No mipmap generation support, returning D3DERR_INVALIDCALL.\n");
416             return WINED3DERR_INVALIDCALL;
417         }
418
419         if (levels > 1)
420         {
421             WARN("D3DUSAGE_AUTOGENMIPMAP is set, and level count > 1, returning D3DERR_INVALIDCALL.\n");
422             return WINED3DERR_INVALIDCALL;
423         }
424
425         levels = 1;
426     }
427     else if (!levels)
428     {
429         levels = wined3d_log2i(max(max(width, height), depth)) + 1;
430         TRACE("Calculated levels = %u.\n", levels);
431     }
432
433     texture->lpVtbl = &IWineD3DVolumeTexture_Vtbl;
434
435     hr = basetexture_init((IWineD3DBaseTextureImpl *)texture, &volumetexture_ops,
436             1, levels, WINED3DRTYPE_VOLUMETEXTURE, device, usage, format, pool,
437             parent, parent_ops);
438     if (FAILED(hr))
439     {
440         WARN("Failed to initialize basetexture, returning %#x.\n", hr);
441         return hr;
442     }
443
444     /* Is NP2 support for volumes needed? */
445     texture->baseTexture.pow2Matrix[0] = 1.0f;
446     texture->baseTexture.pow2Matrix[5] = 1.0f;
447     texture->baseTexture.pow2Matrix[10] = 1.0f;
448     texture->baseTexture.pow2Matrix[15] = 1.0f;
449     texture->baseTexture.target = GL_TEXTURE_3D;
450
451     /* Generate all the surfaces. */
452     tmp_w = width;
453     tmp_h = height;
454     tmp_d = depth;
455
456     for (i = 0; i < texture->baseTexture.level_count; ++i)
457     {
458         IWineD3DVolume *volume;
459
460         /* Create the volume. */
461         hr = IWineD3DDeviceParent_CreateVolume(device->device_parent, parent,
462                 tmp_w, tmp_h, tmp_d, format_id, pool, usage, &volume);
463         if (FAILED(hr))
464         {
465             ERR("Creating a volume for the volume texture failed, hr %#x.\n", hr);
466             volumetexture_cleanup(texture);
467             return hr;
468         }
469
470         /* Set its container to this texture. */
471         volume_set_container((IWineD3DVolumeImpl *)volume, texture);
472         texture->baseTexture.sub_resources[i] = (IWineD3DResourceImpl *)volume;
473
474         /* Calculate the next mipmap level. */
475         tmp_w = max(1, tmp_w >> 1);
476         tmp_h = max(1, tmp_h >> 1);
477         tmp_d = max(1, tmp_d >> 1);
478     }
479
480     return WINED3D_OK;
481 }