gdiplus: Add a software implementation of hatch brushes.
[wine] / dlls / wined3d / volume.c
1 /*
2  * IWineD3DVolume 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_surface);
28
29 /* Context activation is done by the caller. */
30 static void volume_bind_and_dirtify(struct IWineD3DVolumeImpl *volume)
31 {
32     const struct wined3d_gl_info *gl_info = &volume->resource.device->adapter->gl_info;
33     IWineD3DBaseTextureImpl *container = (IWineD3DBaseTextureImpl *)volume->container;
34     DWORD active_sampler;
35
36     /* We don't need a specific texture unit, but after binding the texture the current unit is dirty.
37      * Read the unit back instead of switching to 0, this avoids messing around with the state manager's
38      * gl states. The current texture unit should always be a valid one.
39      *
40      * To be more specific, this is tricky because we can implicitly be called
41      * from sampler() in state.c. This means we can't touch anything other than
42      * whatever happens to be the currently active texture, or we would risk
43      * marking already applied sampler states dirty again.
44      *
45      * TODO: Track the current active texture per GL context instead of using glGet
46      */
47     if (gl_info->supported[ARB_MULTITEXTURE])
48     {
49         GLint active_texture;
50         ENTER_GL();
51         glGetIntegerv(GL_ACTIVE_TEXTURE, &active_texture);
52         LEAVE_GL();
53         active_sampler = volume->resource.device->rev_tex_unit_map[active_texture - GL_TEXTURE0_ARB];
54     } else {
55         active_sampler = 0;
56     }
57
58     if (active_sampler != WINED3D_UNMAPPED_STAGE)
59     {
60         IWineD3DDeviceImpl_MarkStateDirty(volume->resource.device, STATE_SAMPLER(active_sampler));
61     }
62
63     container->baseTexture.texture_ops->texture_bind(container, FALSE);
64 }
65
66 void volume_add_dirty_box(struct IWineD3DVolumeImpl *volume, const WINED3DBOX *dirty_box)
67 {
68     volume->dirty = TRUE;
69     if (dirty_box)
70     {
71         volume->lockedBox.Left = min(volume->lockedBox.Left, dirty_box->Left);
72         volume->lockedBox.Top = min(volume->lockedBox.Top, dirty_box->Top);
73         volume->lockedBox.Front = min(volume->lockedBox.Front, dirty_box->Front);
74         volume->lockedBox.Right = max(volume->lockedBox.Right, dirty_box->Right);
75         volume->lockedBox.Bottom = max(volume->lockedBox.Bottom, dirty_box->Bottom);
76         volume->lockedBox.Back = max(volume->lockedBox.Back, dirty_box->Back);
77     }
78     else
79     {
80         volume->lockedBox.Left = 0;
81         volume->lockedBox.Top = 0;
82         volume->lockedBox.Front = 0;
83         volume->lockedBox.Right = volume->currentDesc.Width;
84         volume->lockedBox.Bottom = volume->currentDesc.Height;
85         volume->lockedBox.Back = volume->currentDesc.Depth;
86     }
87 }
88
89 void volume_set_container(IWineD3DVolumeImpl *volume, struct IWineD3DVolumeTextureImpl *container)
90 {
91     TRACE("volume %p, container %p.\n", volume, container);
92
93     volume->container = container;
94 }
95
96 /* Context activation is done by the caller. */
97 void volume_load(IWineD3DVolumeImpl *volume, UINT level, BOOL srgb_mode)
98 {
99     const struct wined3d_gl_info *gl_info = &volume->resource.device->adapter->gl_info;
100     const struct wined3d_format *format = volume->resource.format;
101
102     TRACE("volume %p, level %u, srgb %#x, format %s (%#x).\n",
103             volume, level, srgb_mode, debug_d3dformat(format->id), format->id);
104
105     volume_bind_and_dirtify(volume);
106
107     ENTER_GL();
108     GL_EXTCALL(glTexImage3DEXT(GL_TEXTURE_3D, level, format->glInternal,
109             volume->currentDesc.Width, volume->currentDesc.Height, volume->currentDesc.Depth,
110             0, format->glFormat, format->glType, volume->resource.allocatedMemory));
111     checkGLcall("glTexImage3D");
112     LEAVE_GL();
113
114     /* When adding code releasing volume->resource.allocatedMemory to save
115      * data keep in mind that GL_UNPACK_CLIENT_STORAGE_APPLE is enabled by
116      * default if supported(GL_APPLE_client_storage). Thus do not release
117      * volume->resource.allocatedMemory if GL_APPLE_client_storage is
118      * supported. */
119 }
120
121 /* *******************************************
122    IWineD3DVolume IUnknown parts follow
123    ******************************************* */
124 static HRESULT WINAPI IWineD3DVolumeImpl_QueryInterface(IWineD3DVolume *iface, REFIID riid, void **object)
125 {
126     TRACE("iface %p, riid %s, object %p.\n", iface, debugstr_guid(riid), object);
127
128     if (IsEqualGUID(riid, &IID_IWineD3DVolume)
129             || IsEqualGUID(riid, &IID_IWineD3DResource)
130             || IsEqualGUID(riid, &IID_IWineD3DBase)
131             || IsEqualGUID(riid, &IID_IUnknown))
132     {
133         IUnknown_AddRef(iface);
134         *object = iface;
135         return S_OK;
136     }
137
138     WARN("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(riid));
139
140     *object = NULL;
141     return E_NOINTERFACE;
142 }
143
144 static ULONG WINAPI IWineD3DVolumeImpl_AddRef(IWineD3DVolume *iface) {
145     IWineD3DVolumeImpl *This = (IWineD3DVolumeImpl *)iface;
146     TRACE("(%p) : AddRef increasing from %d\n", This, This->resource.ref);
147     return InterlockedIncrement(&This->resource.ref);
148 }
149
150 /* Do not call while under the GL lock. */
151 static ULONG WINAPI IWineD3DVolumeImpl_Release(IWineD3DVolume *iface) {
152     IWineD3DVolumeImpl *This = (IWineD3DVolumeImpl *)iface;
153     ULONG ref;
154     TRACE("(%p) : Releasing from %d\n", This, This->resource.ref);
155     ref = InterlockedDecrement(&This->resource.ref);
156
157     if (!ref)
158     {
159         resource_cleanup((IWineD3DResourceImpl *)iface);
160         This->resource.parent_ops->wined3d_object_destroyed(This->resource.parent);
161         HeapFree(GetProcessHeap(), 0, This);
162     }
163     return ref;
164 }
165
166 /* ****************************************************
167    IWineD3DVolume IWineD3DResource parts follow
168    **************************************************** */
169 static void * WINAPI IWineD3DVolumeImpl_GetParent(IWineD3DVolume *iface)
170 {
171     TRACE("iface %p.\n", iface);
172
173     return ((IWineD3DVolumeImpl *)iface)->resource.parent;
174 }
175
176 static HRESULT WINAPI IWineD3DVolumeImpl_SetPrivateData(IWineD3DVolume *iface,
177         REFGUID riid, const void *data, DWORD data_size, DWORD flags)
178 {
179     return resource_set_private_data((IWineD3DResourceImpl *)iface, riid, data, data_size, flags);
180 }
181
182 static HRESULT WINAPI IWineD3DVolumeImpl_GetPrivateData(IWineD3DVolume *iface,
183         REFGUID guid, void *data, DWORD *data_size)
184 {
185     return resource_get_private_data((IWineD3DResourceImpl *)iface, guid, data, data_size);
186 }
187
188 static HRESULT WINAPI IWineD3DVolumeImpl_FreePrivateData(IWineD3DVolume *iface, REFGUID refguid)
189 {
190     return resource_free_private_data((IWineD3DResourceImpl *)iface, refguid);
191 }
192
193 static DWORD WINAPI IWineD3DVolumeImpl_SetPriority(IWineD3DVolume *iface, DWORD priority)
194 {
195     return resource_set_priority((IWineD3DResourceImpl *)iface, priority);
196 }
197
198 static DWORD WINAPI IWineD3DVolumeImpl_GetPriority(IWineD3DVolume *iface)
199 {
200     return resource_get_priority((IWineD3DResourceImpl *)iface);
201 }
202
203 /* Do not call while under the GL lock. */
204 static void WINAPI IWineD3DVolumeImpl_PreLoad(IWineD3DVolume *iface) {
205     FIXME("iface %p stub!\n", iface);
206 }
207
208 /* Do not call while under the GL lock. */
209 static void WINAPI IWineD3DVolumeImpl_UnLoad(IWineD3DVolume *iface)
210 {
211     TRACE("iface %p.\n", iface);
212
213     /* The whole content is shadowed on This->resource.allocatedMemory, and
214      * the texture name is managed by the VolumeTexture container. */
215
216     resource_unload((IWineD3DResourceImpl *)iface);
217 }
218
219 static WINED3DRESOURCETYPE WINAPI IWineD3DVolumeImpl_GetType(IWineD3DVolume *iface)
220 {
221     return resource_get_type((IWineD3DResourceImpl *)iface);
222 }
223
224 static void WINAPI IWineD3DVolumeImpl_GetDesc(IWineD3DVolume *iface, WINED3DVOLUME_DESC *desc)
225 {
226     IWineD3DVolumeImpl *volume = (IWineD3DVolumeImpl *)iface;
227
228     TRACE("iface %p, desc %p.\n", iface, desc);
229
230     desc->Format = volume->resource.format->id;
231     desc->Type = volume->resource.resourceType;
232     desc->Usage = volume->resource.usage;
233     desc->Pool = volume->resource.pool;
234     desc->Size = volume->resource.size; /* dx8 only */
235     desc->Width = volume->currentDesc.Width;
236     desc->Height = volume->currentDesc.Height;
237     desc->Depth = volume->currentDesc.Depth;
238 }
239
240 static HRESULT WINAPI IWineD3DVolumeImpl_Map(IWineD3DVolume *iface,
241         WINED3DLOCKED_BOX *pLockedVolume, const WINED3DBOX *pBox, DWORD flags)
242 {
243     IWineD3DVolumeImpl *This = (IWineD3DVolumeImpl *)iface;
244     FIXME("(%p) : pBox=%p stub\n", This, pBox);
245
246     if(!This->resource.allocatedMemory) {
247         This->resource.allocatedMemory = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->resource.size);
248     }
249
250     /* fixme: should we really lock as such? */
251     TRACE("(%p) : box=%p, output pbox=%p, allMem=%p\n", This, pBox, pLockedVolume, This->resource.allocatedMemory);
252
253     pLockedVolume->RowPitch = This->resource.format->byte_count * This->currentDesc.Width; /* Bytes / row   */
254     pLockedVolume->SlicePitch = This->resource.format->byte_count
255             * This->currentDesc.Width * This->currentDesc.Height;                               /* Bytes / slice */
256     if (!pBox) {
257         TRACE("No box supplied - all is ok\n");
258         pLockedVolume->pBits = This->resource.allocatedMemory;
259         This->lockedBox.Left   = 0;
260         This->lockedBox.Top    = 0;
261         This->lockedBox.Front  = 0;
262         This->lockedBox.Right  = This->currentDesc.Width;
263         This->lockedBox.Bottom = This->currentDesc.Height;
264         This->lockedBox.Back   = This->currentDesc.Depth;
265     } else {
266         TRACE("Lock Box (%p) = l %d, t %d, r %d, b %d, fr %d, ba %d\n", pBox, pBox->Left, pBox->Top, pBox->Right, pBox->Bottom, pBox->Front, pBox->Back);
267         pLockedVolume->pBits = This->resource.allocatedMemory
268                 + (pLockedVolume->SlicePitch * pBox->Front)     /* FIXME: is front < back or vica versa? */
269                 + (pLockedVolume->RowPitch * pBox->Top)
270                 + (pBox->Left * This->resource.format->byte_count);
271         This->lockedBox.Left   = pBox->Left;
272         This->lockedBox.Top    = pBox->Top;
273         This->lockedBox.Front  = pBox->Front;
274         This->lockedBox.Right  = pBox->Right;
275         This->lockedBox.Bottom = pBox->Bottom;
276         This->lockedBox.Back   = pBox->Back;
277     }
278
279     if (!(flags & (WINED3DLOCK_NO_DIRTY_UPDATE | WINED3DLOCK_READONLY)))
280     {
281         volume_add_dirty_box(This, &This->lockedBox);
282         This->container->baseTexture.texture_rgb.dirty = TRUE;
283         This->container->baseTexture.texture_srgb.dirty = TRUE;
284     }
285
286     This->locked = TRUE;
287     TRACE("returning memory@%p rpitch(%d) spitch(%d)\n", pLockedVolume->pBits, pLockedVolume->RowPitch, pLockedVolume->SlicePitch);
288     return WINED3D_OK;
289 }
290
291 static HRESULT WINAPI IWineD3DVolumeImpl_Unmap(IWineD3DVolume *iface)
292 {
293     IWineD3DVolumeImpl *This = (IWineD3DVolumeImpl *)iface;
294     if (!This->locked)
295     {
296         WARN("Trying to unlock unlocked volume %p.\n", iface);
297         return WINED3DERR_INVALIDCALL;
298     }
299     TRACE("(%p) : unlocking volume\n", This);
300     This->locked = FALSE;
301     memset(&This->lockedBox, 0, sizeof(This->lockedBox));
302     return WINED3D_OK;
303 }
304
305 static const IWineD3DVolumeVtbl IWineD3DVolume_Vtbl =
306 {
307     /* IUnknown */
308     IWineD3DVolumeImpl_QueryInterface,
309     IWineD3DVolumeImpl_AddRef,
310     IWineD3DVolumeImpl_Release,
311     /* IWineD3DResource */
312     IWineD3DVolumeImpl_GetParent,
313     IWineD3DVolumeImpl_SetPrivateData,
314     IWineD3DVolumeImpl_GetPrivateData,
315     IWineD3DVolumeImpl_FreePrivateData,
316     IWineD3DVolumeImpl_SetPriority,
317     IWineD3DVolumeImpl_GetPriority,
318     IWineD3DVolumeImpl_PreLoad,
319     IWineD3DVolumeImpl_UnLoad,
320     IWineD3DVolumeImpl_GetType,
321     /* IWineD3DVolume */
322     IWineD3DVolumeImpl_GetDesc,
323     IWineD3DVolumeImpl_Map,
324     IWineD3DVolumeImpl_Unmap,
325 };
326
327 HRESULT volume_init(IWineD3DVolumeImpl *volume, IWineD3DDeviceImpl *device, UINT width,
328         UINT height, UINT depth, DWORD usage, enum wined3d_format_id format_id, WINED3DPOOL pool,
329         void *parent, const struct wined3d_parent_ops *parent_ops)
330 {
331     const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
332     const struct wined3d_format *format = wined3d_get_format(gl_info, format_id);
333     HRESULT hr;
334
335     if (!gl_info->supported[EXT_TEXTURE3D])
336     {
337         WARN("Volume cannot be created - no volume texture support.\n");
338         return WINED3DERR_INVALIDCALL;
339     }
340
341     volume->lpVtbl = &IWineD3DVolume_Vtbl;
342
343     hr = resource_init((IWineD3DResourceImpl *)volume, WINED3DRTYPE_VOLUME, device,
344             width * height * depth * format->byte_count, usage, format, pool, parent, parent_ops);
345     if (FAILED(hr))
346     {
347         WARN("Failed to initialize resource, returning %#x.\n", hr);
348         return hr;
349     }
350
351     volume->currentDesc.Width = width;
352     volume->currentDesc.Height = height;
353     volume->currentDesc.Depth = depth;
354     volume->lockable = TRUE;
355     volume->locked = FALSE;
356     memset(&volume->lockedBox, 0, sizeof(volume->lockedBox));
357     volume->dirty = TRUE;
358
359     volume_add_dirty_box(volume, NULL);
360
361     return WINED3D_OK;
362 }