mshtml: Implement IHTMLDOMNode previousSibling.
[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-2010 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 "wined3d_private.h"
25
26 WINE_DEFAULT_DEBUG_CHANNEL(d3d_texture);
27
28 /* Context activation is done by the caller. */
29 static HRESULT texture_bind(IWineD3DBaseTextureImpl *texture,
30         const struct wined3d_gl_info *gl_info, BOOL srgb)
31 {
32     BOOL set_gl_texture_desc;
33     HRESULT hr;
34
35     TRACE("texture %p, gl_info %p, srgb %#x.\n", texture, gl_info, srgb);
36
37     hr = basetexture_bind(texture, gl_info, srgb, &set_gl_texture_desc);
38     if (set_gl_texture_desc && SUCCEEDED(hr))
39     {
40         BOOL srgb_tex = !gl_info->supported[EXT_TEXTURE_SRGB_DECODE] && texture->baseTexture.is_srgb;
41         struct gl_texture *gl_tex;
42         UINT i;
43
44         gl_tex = basetexture_get_gl_texture(texture, gl_info, srgb_tex);
45
46         for (i = 0; i < texture->baseTexture.level_count; ++i)
47         {
48             IWineD3DSurfaceImpl *surface = surface_from_resource(texture->baseTexture.sub_resources[i]);
49             surface_set_texture_name(surface, gl_tex->name, srgb_tex);
50         }
51
52         /* Conditinal non power of two textures use a different clamping
53          * default. If we're using the GL_WINE_normalized_texrect partial
54          * driver emulation, we're dealing with a GL_TEXTURE_2D texture which
55          * has the address mode set to repeat - something that prevents us
56          * from hitting the accelerated codepath. Thus manually set the GL
57          * state. The same applies to filtering. Even if the texture has only
58          * one mip level, the default LINEAR_MIPMAP_LINEAR filter causes a SW
59          * fallback on macos. */
60         if (texture->baseTexture.cond_np2)
61         {
62             GLenum target = texture->baseTexture.target;
63
64             ENTER_GL();
65             glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
66             checkGLcall("glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)");
67             glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
68             checkGLcall("glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)");
69             glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
70             checkGLcall("glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST)");
71             glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
72             checkGLcall("glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST)");
73             LEAVE_GL();
74             gl_tex->states[WINED3DTEXSTA_ADDRESSU]      = WINED3DTADDRESS_CLAMP;
75             gl_tex->states[WINED3DTEXSTA_ADDRESSV]      = WINED3DTADDRESS_CLAMP;
76             gl_tex->states[WINED3DTEXSTA_MAGFILTER]     = WINED3DTEXF_POINT;
77             gl_tex->states[WINED3DTEXSTA_MINFILTER]     = WINED3DTEXF_POINT;
78             gl_tex->states[WINED3DTEXSTA_MIPFILTER]     = WINED3DTEXF_NONE;
79         }
80     }
81
82     return hr;
83 }
84
85 /* Do not call while under the GL lock. */
86 static void texture_preload(IWineD3DBaseTextureImpl *texture, enum WINED3DSRGB srgb)
87 {
88     IWineD3DDeviceImpl *device = texture->resource.device;
89     const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
90     struct wined3d_context *context = NULL;
91     struct gl_texture *gl_tex;
92     unsigned int i;
93     BOOL srgb_mode;
94
95     TRACE("texture %p, srgb %#x.\n", texture, srgb);
96
97     switch (srgb)
98     {
99         case SRGB_RGB:
100             srgb_mode = FALSE;
101             break;
102
103         case SRGB_BOTH:
104             texture_preload(texture, SRGB_RGB);
105             /* Fallthrough */
106
107         case SRGB_SRGB:
108             srgb_mode = TRUE;
109             break;
110
111         default:
112             srgb_mode = texture->baseTexture.is_srgb;
113             break;
114     }
115
116     gl_tex = basetexture_get_gl_texture(texture, gl_info, srgb_mode);
117
118     if (!device->isInDraw)
119     {
120         /* context_acquire() sets isInDraw to TRUE when loading a pbuffer into a texture,
121          * thus no danger of recursive calls. */
122         context = context_acquire(device, NULL);
123     }
124
125     if (texture->resource.format->id == WINED3DFMT_P8_UINT
126             || texture->resource.format->id == WINED3DFMT_P8_UINT_A8_UNORM)
127     {
128         for (i = 0; i < texture->baseTexture.level_count; ++i)
129         {
130             IWineD3DSurfaceImpl *surface = surface_from_resource(texture->baseTexture.sub_resources[i]);
131             if (palette9_changed(surface))
132             {
133                 TRACE("Reloading surface because the d3d8/9 palette was changed.\n");
134                 /* TODO: This is not necessarily needed with hw palettized texture support. */
135                 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
136                 /* Make sure the texture is reloaded because of the palette change, this kills performance though :( */
137                 surface_modify_location(surface, SFLAG_INTEXTURE, FALSE);
138             }
139         }
140     }
141
142     /* If the texture is marked dirty or the srgb sampler setting has changed
143      * since the last load then reload the surfaces. */
144     if (gl_tex->dirty)
145     {
146         for (i = 0; i < texture->baseTexture.level_count; ++i)
147         {
148             surface_load(surface_from_resource(texture->baseTexture.sub_resources[i]), srgb_mode);
149         }
150     }
151     else
152     {
153         TRACE("Texture %p not dirty, nothing to do.\n", texture);
154     }
155
156     if (context) context_release(context);
157
158     /* No longer dirty. */
159     gl_tex->dirty = FALSE;
160 }
161
162 static void texture_sub_resource_add_dirty_region(struct wined3d_resource *sub_resource,
163         const WINED3DBOX *dirty_region)
164 {
165     surface_add_dirty_rect(surface_from_resource(sub_resource), dirty_region);
166 }
167
168 static void texture_sub_resource_cleanup(struct wined3d_resource *sub_resource)
169 {
170     IWineD3DSurfaceImpl *surface = surface_from_resource(sub_resource);
171
172     /* Clean out the texture name we gave to the surface so that the
173      * surface doesn't try and release it */
174     surface_set_texture_name(surface, 0, TRUE);
175     surface_set_texture_name(surface, 0, FALSE);
176     surface_set_texture_target(surface, 0);
177     surface_set_container(surface, WINED3D_CONTAINER_NONE, NULL);
178     IWineD3DSurface_Release((IWineD3DSurface *)surface);
179 }
180
181 /* Do not call while under the GL lock. */
182 static void texture_unload(struct wined3d_resource *resource)
183 {
184     IWineD3DBaseTextureImpl *texture = basetexture_from_resource(resource);
185     unsigned int i;
186
187     TRACE("texture %p.\n", texture);
188
189     for (i = 0; i < texture->baseTexture.level_count; ++i)
190     {
191         struct wined3d_resource *sub_resource = texture->baseTexture.sub_resources[i];
192         IWineD3DSurfaceImpl *surface = surface_from_resource(sub_resource);
193
194         sub_resource->resource_ops->resource_unload(sub_resource);
195         surface_set_texture_name(surface, 0, FALSE); /* Delete rgb name */
196         surface_set_texture_name(surface, 0, TRUE); /* delete srgb name */
197     }
198
199     basetexture_unload(texture);
200 }
201
202 static const struct wined3d_texture_ops texture_ops =
203 {
204     texture_bind,
205     texture_preload,
206     texture_sub_resource_add_dirty_region,
207     texture_sub_resource_cleanup,
208 };
209
210 static const struct wined3d_resource_ops texture_resource_ops =
211 {
212     texture_unload,
213 };
214
215 static HRESULT WINAPI IWineD3DTextureImpl_QueryInterface(IWineD3DBaseTexture *iface, REFIID riid, LPVOID *ppobj)
216 {
217     IWineD3DBaseTextureImpl *This = (IWineD3DBaseTextureImpl *)iface;
218     TRACE("(%p)->(%s,%p)\n",This,debugstr_guid(riid),ppobj);
219     if (IsEqualGUID(riid, &IID_IUnknown)
220         || IsEqualGUID(riid, &IID_IWineD3DBase)
221         || IsEqualGUID(riid, &IID_IWineD3DResource)
222         || IsEqualGUID(riid, &IID_IWineD3DBaseTexture))
223     {
224         IUnknown_AddRef(iface);
225         *ppobj = This;
226         return WINED3D_OK;
227     }
228     *ppobj = NULL;
229     return E_NOINTERFACE;
230 }
231
232 static ULONG WINAPI IWineD3DTextureImpl_AddRef(IWineD3DBaseTexture *iface)
233 {
234     IWineD3DBaseTextureImpl *This = (IWineD3DBaseTextureImpl *)iface;
235     TRACE("(%p) : AddRef increasing from %d\n", This, This->resource.ref);
236     return InterlockedIncrement(&This->resource.ref);
237 }
238
239 /* Do not call while under the GL lock. */
240 static ULONG WINAPI IWineD3DTextureImpl_Release(IWineD3DBaseTexture *iface)
241 {
242     IWineD3DBaseTextureImpl *This = (IWineD3DBaseTextureImpl *)iface;
243     ULONG ref;
244     TRACE("(%p) : Releasing from %d\n", This, This->resource.ref);
245     ref = InterlockedDecrement(&This->resource.ref);
246     if (!ref)
247     {
248         basetexture_cleanup(This);
249         This->resource.parent_ops->wined3d_object_destroyed(This->resource.parent);
250         HeapFree(GetProcessHeap(), 0, This);
251     }
252     return ref;
253 }
254
255 static HRESULT WINAPI IWineD3DTextureImpl_SetPrivateData(IWineD3DBaseTexture *iface,
256         REFGUID riid, const void *data, DWORD data_size, DWORD flags)
257 {
258     return resource_set_private_data(&((IWineD3DBaseTextureImpl *)iface)->resource, riid, data, data_size, flags);
259 }
260
261 static HRESULT WINAPI IWineD3DTextureImpl_GetPrivateData(IWineD3DBaseTexture *iface,
262         REFGUID guid, void *data, DWORD *data_size)
263 {
264     return resource_get_private_data(&((IWineD3DBaseTextureImpl *)iface)->resource, guid, data, data_size);
265 }
266
267 static HRESULT WINAPI IWineD3DTextureImpl_FreePrivateData(IWineD3DBaseTexture *iface, REFGUID refguid)
268 {
269     return resource_free_private_data(&((IWineD3DBaseTextureImpl *)iface)->resource, refguid);
270 }
271
272 static DWORD WINAPI IWineD3DTextureImpl_SetPriority(IWineD3DBaseTexture *iface, DWORD priority)
273 {
274     return resource_set_priority(&((IWineD3DBaseTextureImpl *)iface)->resource, priority);
275 }
276
277 static DWORD WINAPI IWineD3DTextureImpl_GetPriority(IWineD3DBaseTexture *iface)
278 {
279     return resource_get_priority(&((IWineD3DBaseTextureImpl *)iface)->resource);
280 }
281
282 /* Do not call while under the GL lock. */
283 static void WINAPI IWineD3DTextureImpl_PreLoad(IWineD3DBaseTexture *iface)
284 {
285     texture_preload((IWineD3DBaseTextureImpl *)iface, SRGB_ANY);
286 }
287
288 static WINED3DRESOURCETYPE WINAPI IWineD3DTextureImpl_GetType(IWineD3DBaseTexture *iface)
289 {
290     return resource_get_type(&((IWineD3DBaseTextureImpl *)iface)->resource);
291 }
292
293 static void * WINAPI IWineD3DTextureImpl_GetParent(IWineD3DBaseTexture *iface)
294 {
295     TRACE("iface %p.\n", iface);
296
297     return ((IWineD3DBaseTextureImpl *)iface)->resource.parent;
298 }
299
300 static DWORD WINAPI IWineD3DTextureImpl_SetLOD(IWineD3DBaseTexture *iface, DWORD LODNew)
301 {
302     return basetexture_set_lod((IWineD3DBaseTextureImpl *)iface, LODNew);
303 }
304
305 static DWORD WINAPI IWineD3DTextureImpl_GetLOD(IWineD3DBaseTexture *iface)
306 {
307     return basetexture_get_lod((IWineD3DBaseTextureImpl *)iface);
308 }
309
310 static DWORD WINAPI IWineD3DTextureImpl_GetLevelCount(IWineD3DBaseTexture *iface)
311 {
312     return basetexture_get_level_count((IWineD3DBaseTextureImpl *)iface);
313 }
314
315 static HRESULT WINAPI IWineD3DTextureImpl_SetAutoGenFilterType(IWineD3DBaseTexture *iface,
316         WINED3DTEXTUREFILTERTYPE FilterType)
317 {
318   return basetexture_set_autogen_filter_type((IWineD3DBaseTextureImpl *)iface, FilterType);
319 }
320
321 static WINED3DTEXTUREFILTERTYPE WINAPI IWineD3DTextureImpl_GetAutoGenFilterType(IWineD3DBaseTexture *iface)
322 {
323   return basetexture_get_autogen_filter_type((IWineD3DBaseTextureImpl *)iface);
324 }
325
326 static void WINAPI IWineD3DTextureImpl_GenerateMipSubLevels(IWineD3DBaseTexture *iface)
327 {
328     basetexture_generate_mipmaps((IWineD3DBaseTextureImpl *)iface);
329 }
330
331 static struct wined3d_resource * WINAPI IWineD3DTextureImpl_GetSubResource(IWineD3DBaseTexture *iface,
332         UINT sub_resource_idx)
333 {
334     IWineD3DBaseTextureImpl *texture = (IWineD3DBaseTextureImpl *)iface;
335
336     TRACE("iface %p, sub_resource_idx %u.\n", iface, sub_resource_idx);
337
338     return basetexture_get_sub_resource(texture, sub_resource_idx);
339 }
340
341 static HRESULT WINAPI IWineD3DTextureImpl_AddDirtyRegion(IWineD3DBaseTexture *iface,
342         UINT layer, const WINED3DBOX *dirty_region)
343 {
344     return basetexture_add_dirty_region((IWineD3DBaseTextureImpl *)iface, layer, dirty_region);
345 }
346
347 static const IWineD3DBaseTextureVtbl IWineD3DTexture_Vtbl =
348 {
349     /* IUnknown */
350     IWineD3DTextureImpl_QueryInterface,
351     IWineD3DTextureImpl_AddRef,
352     IWineD3DTextureImpl_Release,
353     /* IWineD3DResource */
354     IWineD3DTextureImpl_GetParent,
355     IWineD3DTextureImpl_SetPrivateData,
356     IWineD3DTextureImpl_GetPrivateData,
357     IWineD3DTextureImpl_FreePrivateData,
358     IWineD3DTextureImpl_SetPriority,
359     IWineD3DTextureImpl_GetPriority,
360     IWineD3DTextureImpl_PreLoad,
361     IWineD3DTextureImpl_GetType,
362     /* IWineD3DBaseTexture */
363     IWineD3DTextureImpl_SetLOD,
364     IWineD3DTextureImpl_GetLOD,
365     IWineD3DTextureImpl_GetLevelCount,
366     IWineD3DTextureImpl_SetAutoGenFilterType,
367     IWineD3DTextureImpl_GetAutoGenFilterType,
368     IWineD3DTextureImpl_GenerateMipSubLevels,
369     IWineD3DTextureImpl_GetSubResource,
370     IWineD3DTextureImpl_AddDirtyRegion,
371 };
372
373 HRESULT texture_init(IWineD3DBaseTextureImpl *texture, UINT width, UINT height, UINT levels,
374         IWineD3DDeviceImpl *device, DWORD usage, enum wined3d_format_id format_id, WINED3DPOOL pool,
375         void *parent, const struct wined3d_parent_ops *parent_ops)
376 {
377     const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
378     const struct wined3d_format *format = wined3d_get_format(gl_info, format_id);
379     UINT pow2_width, pow2_height;
380     UINT tmp_w, tmp_h;
381     unsigned int i;
382     HRESULT hr;
383
384     /* TODO: It should only be possible to create textures for formats
385      * that are reported as supported. */
386     if (WINED3DFMT_UNKNOWN >= format_id)
387     {
388         WARN("(%p) : Texture cannot be created with a format of WINED3DFMT_UNKNOWN.\n", texture);
389         return WINED3DERR_INVALIDCALL;
390     }
391
392     /* Non-power2 support. */
393     if (gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO])
394     {
395         pow2_width = width;
396         pow2_height = height;
397     }
398     else
399     {
400         /* Find the nearest pow2 match. */
401         pow2_width = pow2_height = 1;
402         while (pow2_width < width) pow2_width <<= 1;
403         while (pow2_height < height) pow2_height <<= 1;
404
405         if (pow2_width != width || pow2_height != height)
406         {
407             if (levels > 1)
408             {
409                 WARN("Attempted to create a mipmapped np2 texture without unconditional np2 support.\n");
410                 return WINED3DERR_INVALIDCALL;
411             }
412             levels = 1;
413         }
414     }
415
416     /* Calculate levels for mip mapping. */
417     if (usage & WINED3DUSAGE_AUTOGENMIPMAP)
418     {
419         if (!gl_info->supported[SGIS_GENERATE_MIPMAP])
420         {
421             WARN("No mipmap generation support, returning WINED3DERR_INVALIDCALL.\n");
422             return WINED3DERR_INVALIDCALL;
423         }
424
425         if (levels > 1)
426         {
427             WARN("D3DUSAGE_AUTOGENMIPMAP is set, and level count > 1, returning WINED3DERR_INVALIDCALL.\n");
428             return WINED3DERR_INVALIDCALL;
429         }
430
431         levels = 1;
432     }
433     else if (!levels)
434     {
435         levels = wined3d_log2i(max(width, height)) + 1;
436         TRACE("Calculated levels = %u.\n", levels);
437     }
438
439     texture->lpVtbl = &IWineD3DTexture_Vtbl;
440
441     hr = basetexture_init((IWineD3DBaseTextureImpl *)texture, &texture_ops,
442             1, levels, WINED3DRTYPE_TEXTURE, device, usage, format, pool,
443             parent, parent_ops, &texture_resource_ops);
444     if (FAILED(hr))
445     {
446         WARN("Failed to initialize basetexture, returning %#x.\n", hr);
447         return hr;
448     }
449
450     /* Precalculated scaling for 'faked' non power of two texture coords.
451      * Second also don't use ARB_TEXTURE_RECTANGLE in case the surface format is P8 and EXT_PALETTED_TEXTURE
452      * is used in combination with texture uploads (RTL_READTEX). The reason is that EXT_PALETTED_TEXTURE
453      * doesn't work in combination with ARB_TEXTURE_RECTANGLE. */
454     if (gl_info->supported[WINED3D_GL_NORMALIZED_TEXRECT] && (width != pow2_width || height != pow2_height))
455     {
456         texture->baseTexture.pow2Matrix[0] = 1.0f;
457         texture->baseTexture.pow2Matrix[5] = 1.0f;
458         texture->baseTexture.pow2Matrix[10] = 1.0f;
459         texture->baseTexture.pow2Matrix[15] = 1.0f;
460         texture->baseTexture.target = GL_TEXTURE_2D;
461         texture->baseTexture.cond_np2 = TRUE;
462         texture->baseTexture.minMipLookup = minMipLookup_noFilter;
463     }
464     else if (gl_info->supported[ARB_TEXTURE_RECTANGLE] && (width != pow2_width || height != pow2_height)
465             && !(format->id == WINED3DFMT_P8_UINT && gl_info->supported[EXT_PALETTED_TEXTURE]
466             && wined3d_settings.rendertargetlock_mode == RTL_READTEX))
467     {
468         if ((width != 1) || (height != 1)) texture->baseTexture.pow2Matrix_identity = FALSE;
469
470         texture->baseTexture.pow2Matrix[0] = (float)width;
471         texture->baseTexture.pow2Matrix[5] = (float)height;
472         texture->baseTexture.pow2Matrix[10] = 1.0f;
473         texture->baseTexture.pow2Matrix[15] = 1.0f;
474         texture->baseTexture.target = GL_TEXTURE_RECTANGLE_ARB;
475         texture->baseTexture.cond_np2 = TRUE;
476
477         if (texture->resource.format->flags & WINED3DFMT_FLAG_FILTERING)
478         {
479             texture->baseTexture.minMipLookup = minMipLookup_noMip;
480         }
481         else
482         {
483             texture->baseTexture.minMipLookup = minMipLookup_noFilter;
484         }
485     }
486     else
487     {
488         if ((width != pow2_width) || (height != pow2_height))
489         {
490             texture->baseTexture.pow2Matrix_identity = FALSE;
491             texture->baseTexture.pow2Matrix[0] = (((float)width) / ((float)pow2_width));
492             texture->baseTexture.pow2Matrix[5] = (((float)height) / ((float)pow2_height));
493         }
494         else
495         {
496             texture->baseTexture.pow2Matrix[0] = 1.0f;
497             texture->baseTexture.pow2Matrix[5] = 1.0f;
498         }
499
500         texture->baseTexture.pow2Matrix[10] = 1.0f;
501         texture->baseTexture.pow2Matrix[15] = 1.0f;
502         texture->baseTexture.target = GL_TEXTURE_2D;
503         texture->baseTexture.cond_np2 = FALSE;
504     }
505     TRACE("xf(%f) yf(%f)\n", texture->baseTexture.pow2Matrix[0], texture->baseTexture.pow2Matrix[5]);
506
507     /* Generate all the surfaces. */
508     tmp_w = width;
509     tmp_h = height;
510     for (i = 0; i < texture->baseTexture.level_count; ++i)
511     {
512         IWineD3DSurface *surface;
513
514         /* Use the callback to create the texture surface. */
515         hr = IWineD3DDeviceParent_CreateSurface(device->device_parent, parent, tmp_w, tmp_h,
516                 format->id, usage, pool, i, 0, &surface);
517         if (FAILED(hr))
518         {
519             FIXME("Failed to create surface %p, hr %#x\n", texture, hr);
520             basetexture_cleanup(texture);
521             return hr;
522         }
523
524         surface_set_container((IWineD3DSurfaceImpl *)surface, WINED3D_CONTAINER_TEXTURE, (IWineD3DBase *)texture);
525         surface_set_texture_target((IWineD3DSurfaceImpl *)surface, texture->baseTexture.target);
526         texture->baseTexture.sub_resources[i] = &((IWineD3DSurfaceImpl *)surface)->resource;
527         TRACE("Created surface level %u @ %p.\n", i, surface);
528         /* Calculate the next mipmap level. */
529         tmp_w = max(1, tmp_w >> 1);
530         tmp_h = max(1, tmp_h >> 1);
531     }
532
533     return WINED3D_OK;
534 }