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