]> git.tdb.fi Git - libs/gl.git/blob - source/texture.cpp
Use ARB_direct_state_access to avoid some bind calls
[libs/gl.git] / source / texture.cpp
1 #include <msp/gl/extensions/arb_direct_state_access.h>
2 #include <msp/gl/extensions/arb_shadow.h>
3 #include <msp/gl/extensions/ext_framebuffer_object.h>
4 #include <msp/gl/extensions/ext_texture3d.h>
5 #include <msp/gl/extensions/ext_texture_filter_anisotropic.h>
6 #include <msp/gl/extensions/sgis_generate_mipmap.h>
7 #include <msp/io/memory.h>
8 #include <msp/strings/format.h>
9 #include "error.h"
10 #include "resourcemanager.h"
11 #include "resources.h"
12 #include "texture.h"
13 #include "texunit.h"
14
15 using namespace std;
16
17 namespace Msp {
18 namespace GL {
19
20 void operator>>(const LexicalConverter &c, TextureFilter &tf)
21 {
22         if(c.get()=="NEAREST")
23                 tf = NEAREST;
24         else if(c.get()=="LINEAR")
25                 tf = LINEAR;
26         else if(c.get()=="NEAREST_MIPMAP_NEAREST")
27                 tf = NEAREST_MIPMAP_NEAREST;
28         else if(c.get()=="NEAREST_MIPMAP_LINEAR")
29                 tf = NEAREST_MIPMAP_LINEAR;
30         else if(c.get()=="LINEAR_MIPMAP_NEAREST")
31                 tf = LINEAR_MIPMAP_NEAREST;
32         else if(c.get()=="LINEAR_MIPMAP_LINEAR")
33                 tf = LINEAR_MIPMAP_LINEAR;
34         else
35                 throw lexical_error(format("conversion of '%s' to TextureFilter", c.get()));
36 }
37
38
39 void operator>>(const LexicalConverter &c, TextureWrap &tw)
40 {
41         if(c.get()=="REPEAT")
42                 tw = REPEAT;
43         else if(c.get()=="CLAMP_TO_EDGE")
44                 tw = CLAMP_TO_EDGE;
45         else if(c.get()=="MIRRORED_REPEAT")
46                 tw = MIRRORED_REPEAT;
47         else
48                 throw lexical_error(format("conversion of '%s' to TextureWrap", c.get()));
49 }
50
51
52 Texture::Texture(GLenum t, ResourceManager *m):
53         id(0),
54         target(t),
55         min_filter(NEAREST_MIPMAP_LINEAR),
56         mag_filter(LINEAR),
57         wrap_s(REPEAT),
58         wrap_t(REPEAT),
59         wrap_r(REPEAT),
60         gen_mipmap(false),
61         compare(false),
62         cmp_func(LEQUAL),
63         dirty_params(0)
64 {
65         if(m)
66                 set_manager(m);
67         else if(ARB_direct_state_access)
68                 glCreateTextures(target, 1, &id);
69         else
70                 glGenTextures(1, &id);
71 }
72
73 Texture::~Texture()
74 {
75         while(TexUnit *unit = TexUnit::find_unit(this))
76                 unbind_from(unit->get_index());
77
78         if(id)
79                 glDeleteTextures(1, &id);
80 }
81
82 DataType Texture::get_alloc_type(PixelFormat fmt)
83 {
84         return (get_base_pixelformat(fmt)==DEPTH_COMPONENT ? UNSIGNED_SHORT : UNSIGNED_BYTE);
85 }
86
87 void Texture::update_parameter(int mask) const
88 {
89         if(!ARB_direct_state_access && TexUnit::current().get_texture()!=this)
90         {
91                 TexUnit *unit = TexUnit::find_unit(this);
92                 if(!unit)
93                 {
94                         dirty_params |= mask;
95                         return;
96                 }
97
98                 unit->bind();
99         }
100
101         if(mask&MIN_FILTER)
102                 set_parameter_i(GL_TEXTURE_MIN_FILTER, min_filter);
103         if(mask&MAG_FILTER)
104                 set_parameter_i(GL_TEXTURE_MAG_FILTER, mag_filter);
105         if(mask&MAX_ANISOTROPY)
106                 set_parameter_f(GL_TEXTURE_MAX_ANISOTROPY_EXT, max_anisotropy);
107         if(mask&WRAP_S)
108                 set_parameter_i(GL_TEXTURE_WRAP_S, wrap_s);
109         if(mask&WRAP_T)
110                 set_parameter_i(GL_TEXTURE_WRAP_T, wrap_t);
111         if(mask&WRAP_R)
112                 set_parameter_i(GL_TEXTURE_WRAP_R, wrap_r);
113         if(mask&GENERATE_MIPMAP)
114                 set_parameter_i(GL_GENERATE_MIPMAP, gen_mipmap);
115         if(mask&COMPARE)
116                 set_parameter_i(GL_TEXTURE_COMPARE_MODE, (compare ? GL_COMPARE_R_TO_TEXTURE : GL_NONE));
117         if(mask&COMPARE_FUNC)
118                 set_parameter_i(GL_TEXTURE_COMPARE_FUNC, cmp_func);
119 }
120
121 void Texture::set_parameter_i(GLenum param, int value) const
122 {
123         if(ARB_direct_state_access)
124                 glTextureParameteri(id, param, value);
125         else
126                 glTexParameteri(target, param, value);
127 }
128
129 void Texture::set_parameter_f(GLenum param, float value) const
130 {
131         if(ARB_direct_state_access)
132                 glTextureParameterf(id, param, value);
133         else
134                 glTexParameterf(target, param, value);
135 }
136
137 void Texture::set_min_filter(TextureFilter f)
138 {
139         min_filter = f;
140         update_parameter(MIN_FILTER);
141 }
142
143 void Texture::set_mag_filter(TextureFilter f)
144 {
145         mag_filter = f;
146         update_parameter(MAG_FILTER);
147 }
148
149 void Texture::set_filter(TextureFilter f)
150 {
151         set_min_filter(f);
152         set_mag_filter(f==NEAREST ? NEAREST : LINEAR);
153 }
154
155 void Texture::set_max_anisotropy(float a)
156 {
157         if(a<1.0f)
158                 throw invalid_argument("Texture::set_max_anisotropy");
159         else if(a>1.0f)
160                 static Require _req(EXT_texture_filter_anisotropic);
161         max_anisotropy = a;
162         update_parameter(MAX_ANISOTROPY);
163 }
164
165 void Texture::set_wrap(TextureWrap w)
166 {
167         set_wrap_s(w);
168         set_wrap_t(w);
169         if(EXT_texture3D)
170                 set_wrap_r(w);
171 }
172
173 void Texture::set_wrap_s(TextureWrap w)
174 {
175         wrap_s = w;
176         update_parameter(WRAP_S);
177 }
178
179 void Texture::set_wrap_t(TextureWrap w)
180 {
181         wrap_t = w;
182         update_parameter(WRAP_T);
183 }
184
185 void Texture::set_wrap_r(TextureWrap w)
186 {
187         static Require _req(EXT_texture3D);
188         wrap_r = w;
189         update_parameter(WRAP_R);
190 }
191
192 void Texture::set_generate_mipmap(bool gm)
193 {
194         if(gm && !EXT_framebuffer_object)
195                 static Require _req(SGIS_generate_mipmap);
196         gen_mipmap = gm;
197         if(!EXT_framebuffer_object)
198                 update_parameter(GENERATE_MIPMAP);
199 }
200
201 void Texture::auto_generate_mipmap()
202 {
203         // glGenerateMipmap is defined here
204         if(EXT_framebuffer_object)
205         {
206                 if(ARB_direct_state_access)
207                         glGenerateTextureMipmap(id);
208                 else
209                         glGenerateMipmap(target);
210         }
211 }
212
213 void Texture::set_compare_enabled(bool c)
214 {
215         if(c)
216                 static Require _req(ARB_shadow);
217         compare = c;
218         update_parameter(COMPARE);
219 }
220
221 void Texture::set_compare_func(Predicate f)
222 {
223         static Require _req(ARB_shadow);
224         cmp_func = f;
225         update_parameter(COMPARE_FUNC);
226 }
227
228 void Texture::load_image(const string &fn, bool srgb)
229 {
230         Graphics::Image img;
231         img.load_file(fn);
232
233         image(img, srgb);
234 }
235
236 void Texture::bind_to(unsigned i) const
237 {
238         if(!id)
239         {
240                 if(manager)
241                         manager->resource_used(*this);
242                 if(!id)
243                 {
244                         unbind_from(i);
245                         return;
246                 }
247         }
248
249         TexUnit &unit = TexUnit::get_unit(i);
250         const Texture *old = unit.get_texture();
251         if(unit.set_texture(this))
252         {
253                 if(manager)
254                         manager->resource_used(*this);
255
256                 unit.bind();
257                 if(unit.supports_legacy())
258                 {
259                         if(old && old->target!=target)
260                                 glDisable(old->target);
261                         if(!old || old->target!=target)
262                                 glEnable(target);
263                 }
264                 glBindTexture(target, id);
265
266                 if(dirty_params)
267                 {
268                         update_parameter(dirty_params);
269                         dirty_params = 0;
270                 }
271         }
272 }
273
274 const Texture *Texture::current(unsigned i)
275 {
276         return TexUnit::get_unit(i).get_texture();
277 }
278
279 void Texture::unbind_from(unsigned i)
280 {
281         TexUnit &unit = TexUnit::get_unit(i);
282         const Texture *cur = unit.get_texture();
283         if(unit.set_texture(0))
284         {
285                 unit.bind();
286                 glBindTexture(cur->target, 0);
287                 if(unit.supports_legacy())
288                         glDisable(cur->target);
289         }
290 }
291
292
293 Texture::Loader::Loader(Texture &t):
294         DataFile::CollectionObjectLoader<Texture>(t, 0)
295 {
296         init();
297 }
298
299 Texture::Loader::Loader(Texture &t, Collection &c):
300         DataFile::CollectionObjectLoader<Texture>(t, &c)
301 {
302         init();
303 }
304
305 void Texture::Loader::init()
306 {
307         if(Resources *res = dynamic_cast<Resources *>(coll))
308                 srgb = res->get_srgb_conversion();
309         else
310                 srgb = false;
311
312         add("external_image", &Loader::external_image);
313         add("filter", &Loader::filter);
314         add("generate_mipmap", &Loader::generate_mipmap);
315         add("image_data", &Loader::image_data);
316         add("mag_filter", &Loader::mag_filter);
317         add("max_anisotropy", &Loader::max_anisotropy);
318         add("min_filter", &Loader::min_filter);
319         add("wrap",       &Loader::wrap);
320         add("wrap_r",     &Loader::wrap_r);
321         add("wrap_s",     &Loader::wrap_s);
322         add("wrap_t",     &Loader::wrap_t);
323 }
324
325 void Texture::Loader::external_image(const string &fn)
326 {
327         Graphics::Image img;
328         RefPtr<IO::Seekable> io = get_collection().open_raw(fn);
329         img.load_io(*io);
330
331         obj.image(img, srgb);
332 }
333
334 void Texture::Loader::filter(TextureFilter f)
335 {
336         obj.set_filter(f);
337 }
338
339 void Texture::Loader::generate_mipmap(bool gm)
340 {
341         obj.set_generate_mipmap(gm);
342 }
343
344 void Texture::Loader::image_data(const string &data)
345 {
346         Graphics::Image img;
347         IO::Memory mem(data.data(), data.size());
348         img.load_io(mem);
349
350         obj.image(img, srgb);
351 }
352
353 void Texture::Loader::mag_filter(TextureFilter f)
354 {
355         obj.set_mag_filter(f);
356 }
357
358 void Texture::Loader::max_anisotropy(float a)
359 {
360         obj.set_max_anisotropy(a);
361 }
362
363 void Texture::Loader::min_filter(TextureFilter f)
364 {
365         obj.set_min_filter(f);
366 }
367
368 void Texture::Loader::wrap(TextureWrap w)
369 {
370         obj.set_wrap(w);
371 }
372
373 void Texture::Loader::wrap_r(TextureWrap w)
374 {
375         obj.set_wrap_r(w);
376 }
377
378 void Texture::Loader::wrap_s(TextureWrap w)
379 {
380         obj.set_wrap_s(w);
381 }
382
383 void Texture::Loader::wrap_t(TextureWrap w)
384 {
385         obj.set_wrap_t(w);
386 }
387
388
389 bool is_mipmapped(TextureFilter filter)
390 {
391         return (filter==NEAREST_MIPMAP_NEAREST || filter==NEAREST_MIPMAP_LINEAR ||
392                 filter==LINEAR_MIPMAP_NEAREST || filter==LINEAR_MIPMAP_LINEAR);
393 }
394
395 } // namespace GL
396 } // namespace Msp