]> git.tdb.fi Git - libs/gl.git/blob - source/core/texture2d.cpp
Add support for integer vertex attributes
[libs/gl.git] / source / core / texture2d.cpp
1 #include <msp/core/raii.h>
2 #include <msp/gl/extensions/arb_direct_state_access.h>
3 #include <msp/gl/extensions/arb_texture_storage.h>
4 #include <msp/graphics/imageloader.h>
5 #include "buffer.h"
6 #include "error.h"
7 #include "resources.h"
8 #include "texture2d.h"
9
10 using namespace std;
11
12 namespace Msp {
13 namespace GL {
14
15 class Texture2D::AsyncLoader: public Resource::AsyncLoader
16 {
17 private:
18         Texture2D &texture;
19         IO::Seekable &io;
20         Buffer pixel_buffer;
21         char *mapped_address;
22         Graphics::Image image;
23         Graphics::ImageLoader *img_loader;
24         unsigned n_bytes;
25         int phase;
26
27 public:
28         AsyncLoader(Texture2D &, IO::Seekable &);
29         ~AsyncLoader();
30
31         virtual bool needs_sync() const;
32         virtual bool process();
33 };
34
35
36 Texture2D::Texture2D(ResourceManager *m):
37         Texture(GL_TEXTURE_2D, m),
38         width(0),
39         height(0),
40         allocated(0)
41 { }
42
43 Texture2D::~Texture2D()
44 {
45         set_manager(0);
46 }
47
48 void Texture2D::storage(PixelFormat fmt, unsigned wd, unsigned ht, unsigned lv)
49 {
50         if(width>0)
51         {
52                 if(fmt!=format || wd!=width || ht!=height || (lv && lv!=levels))
53                         throw incompatible_data("Texture2D::storage");
54                 return;
55         }
56         if(wd==0 || ht==0)
57                 throw invalid_argument("Texture2D::storage");
58
59         set_format(fmt);
60         width = wd;
61         height = ht;
62         levels = get_n_levels();
63         if(lv>0)
64                 levels = min(levels, lv);
65 }
66
67 void Texture2D::allocate(unsigned level)
68 {
69         if(width==0 || height==0)
70                 throw invalid_operation("Texture2D::allocate");
71         if(level>=levels)
72                 throw invalid_argument("Texture2D::allocate");
73
74         bool direct = ARB_texture_storage && ARB_direct_state_access;
75         if(!direct)
76         {
77                 glActiveTexture(GL_TEXTURE0);
78                 glBindTexture(target, id);
79         }
80
81         allocate_(level);
82
83         if(!direct)
84                 glBindTexture(target, 0);
85 }
86
87 void Texture2D::allocate_(unsigned level)
88 {
89         if(allocated&(1<<level))
90                 return;
91
92         if(ARB_texture_storage)
93         {
94                 GLenum fmt = get_gl_pixelformat(storage_fmt);
95                 if(ARB_direct_state_access)
96                         glTextureStorage2D(id, levels, fmt, width, height);
97                 else
98                         glTexStorage2D(target, levels, fmt, width, height);
99                 apply_swizzle();
100                 allocated |= (1<<levels)-1;
101         }
102         else
103                 image_(level, 0);
104 }
105
106 void Texture2D::image(unsigned level, const void *data)
107 {
108         if(width==0 || height==0)
109                 throw invalid_operation("Texture2D::image");
110         if(level>=levels)
111                 throw out_of_range("Texture2D::image");
112
113         if(ARB_texture_storage)
114         {
115                 LinAl::Vector<unsigned, 2> size = get_level_size(level);
116                 return sub_image(level, 0, 0, size.x, size.y, data);
117         }
118
119         glActiveTexture(GL_TEXTURE0);
120         glBindTexture(target, id);
121
122         image_(level, data);
123
124         if(auto_gen_mipmap && level==0)
125         {
126                 generate_mipmap_();
127                 allocated |= (1<<levels)-1;
128         }
129
130         glBindTexture(target, 0);
131 }
132
133 void Texture2D::image_(unsigned level, const void *data)
134 {
135         if(!allocated)
136         {
137                 glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, levels-1);
138                 apply_swizzle();
139         }
140
141         LinAl::Vector<unsigned, 2> size = get_level_size(level);
142         GLenum fmt = get_gl_pixelformat(storage_fmt);
143         GLenum comp = get_gl_components(get_components(storage_fmt));
144         GLenum type = get_gl_type(get_component_type(storage_fmt));
145         glTexImage2D(target, level, fmt, size.x, size.y, 0, comp, type, data);
146
147         allocated |= 1<<level;
148 }
149
150 void Texture2D::image(unsigned level, PixelComponents comp, DataType type, const void *data)
151 {
152         if(comp!=get_components(format) || type!=get_component_type(format))
153                 throw incompatible_data("Texture2D::image");
154         image(level, data);
155 }
156
157 void Texture2D::sub_image(unsigned level, int x, int y, unsigned wd, unsigned ht, const void *data)
158 {
159         if(width==0 || height==0)
160                 throw invalid_operation("Texture2D::sub_image");
161         if(level>=levels)
162                 throw out_of_range("Texture2D::sub_image");
163
164         bool direct = (ARB_direct_state_access && (ARB_texture_storage || (allocated&(1<<level))));
165         if(!direct)
166         {
167                 glActiveTexture(GL_TEXTURE0);
168                 glBindTexture(target, id);
169         }
170
171         allocate_(level);
172
173         GLenum comp = get_gl_components(get_components(storage_fmt));
174         GLenum type = get_gl_type(get_component_type(storage_fmt));
175         if(ARB_direct_state_access)
176                 glTextureSubImage2D(id, level, x, y, wd, ht, comp, type, data);
177         else
178                 glTexSubImage2D(target, level, x, y, wd, ht, comp, type, data);
179
180         if(auto_gen_mipmap && level==0)
181                 generate_mipmap_();
182
183         if(!direct)
184                 glBindTexture(target, 0);
185 }
186
187 void Texture2D::sub_image(unsigned level, int x, int y, unsigned wd, unsigned ht, PixelComponents comp, DataType type, const void *data)
188 {
189         if(comp!=get_components(format) || type!=get_component_type(format))
190                 throw incompatible_data("Texture2D::sub_image");
191         sub_image(level, x, y, wd, ht, data);
192 }
193
194 void Texture2D::image(const Graphics::Image &img, unsigned lv)
195 {
196         image(img, lv, false);
197 }
198
199 void Texture2D::image(const Graphics::Image &img, unsigned lv, bool from_buffer)
200 {
201         unsigned w = img.get_width();
202         unsigned h = img.get_height();
203         PixelFormat fmt = pixelformat_from_image(img);
204         storage(make_pixelformat(get_components(fmt), get_component_type(fmt), use_srgb_format), w, h, lv);
205
206         image(0, from_buffer ? 0 : img.get_pixels());
207 }
208
209 unsigned Texture2D::get_n_levels() const
210 {
211         unsigned n = 0;
212         for(unsigned s=max(width, height); s; s>>=1, ++n) ;
213         return n;
214 }
215
216 LinAl::Vector<unsigned, 2> Texture2D::get_level_size(unsigned level) const
217 {
218         unsigned w = width>>level;
219         unsigned h = height>>level;
220
221         if(!w && h)
222                 w = 1;
223         else if(!h && w)
224                 h = 1;
225
226         return LinAl::Vector<unsigned, 2>(w, h);
227 }
228
229 Resource::AsyncLoader *Texture2D::load(IO::Seekable &io, const Resources *)
230 {
231         AsyncLoader *ldr = new AsyncLoader(*this, io);
232         return ldr;
233 }
234
235 UInt64 Texture2D::get_data_size() const
236 {
237         return id ? width*height*get_pixel_size(format) : 0;
238 }
239
240 void Texture2D::unload()
241 {
242         glDeleteTextures(1, &id);
243         id = 0;
244         allocated = 0;
245 }
246
247
248 Texture2D::Loader::Loader(Texture2D &t):
249         DataFile::DerivedObjectLoader<Texture2D, Texture::Loader>(t)
250 {
251         init();
252 }
253
254 Texture2D::Loader::Loader(Texture2D &t, Collection &c):
255         DataFile::DerivedObjectLoader<Texture2D, Texture::Loader>(t, c)
256 {
257         init();
258 }
259
260 void Texture2D::Loader::init()
261 {
262         add("raw_data", &Loader::raw_data);
263         add("storage", &Loader::storage);
264         add("storage", &Loader::storage_levels);
265 }
266
267 void Texture2D::Loader::raw_data(const string &data)
268 {
269         if(obj.manager)
270         {
271                 obj.set_manager(0);
272                 if(!obj.id)
273                         obj.generate_id();
274         }
275         obj.image(0, data.data());
276 }
277
278 void Texture2D::Loader::storage(PixelFormat fmt, unsigned w, unsigned h)
279 {
280         obj.storage(fmt, w, h);
281 }
282
283 void Texture2D::Loader::storage_levels(PixelFormat fmt, unsigned w, unsigned h, unsigned l)
284 {
285         obj.storage(fmt, w, h, l);
286 }
287
288
289 Texture2D::AsyncLoader::AsyncLoader(Texture2D &t, IO::Seekable &i):
290         texture(t),
291         io(i),
292         mapped_address(0),
293         img_loader(Graphics::ImageLoader::open_io(io)),
294         phase(0)
295 { }
296
297 Texture2D::AsyncLoader::~AsyncLoader()
298 {
299         if(mapped_address)
300                 pixel_buffer.unmap();
301         delete img_loader;
302 }
303
304 bool Texture2D::AsyncLoader::needs_sync() const
305 {
306         return phase%2;
307 }
308
309 bool Texture2D::AsyncLoader::process()
310 {
311         if(phase==0)
312         {
313                 image.load_headers(*img_loader);
314                 n_bytes = image.get_stride()*image.get_height();
315         }
316         else if(phase==1)
317         {
318                 pixel_buffer.storage(n_bytes);
319                 mapped_address = reinterpret_cast<char *>(pixel_buffer.map());
320         }
321         else if(phase==2)
322                 image.load_into(*img_loader, mapped_address);
323         else if(phase==3)
324         {
325                 mapped_address = 0;
326                 if(!pixel_buffer.unmap())
327                 {
328                         phase = 1;
329                         return false;
330                 }
331
332                 if(!texture.id)
333                         texture.generate_id();
334                 glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pixel_buffer.get_id());
335                 texture.image(image, 0, true);
336                 glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
337         }
338
339         ++phase;
340         return phase>3;
341 }
342
343 } // namespace GL
344 } // namespace Msp