]> git.tdb.fi Git - libs/gl.git/blob - source/core/texture2d.cpp
ed0b206c1e05069ada3d8e677c84995b706e920b
[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         if(allocated&(1<<level))
74                 return;
75
76         if(ARB_texture_storage)
77         {
78                 GLenum fmt = get_gl_pixelformat(storage_fmt);
79                 if(ARB_direct_state_access)
80                         glTextureStorage2D(id, levels, fmt, width, height);
81                 else
82                 {
83                         bind_scratch();
84                         glTexStorage2D(target, levels, fmt, width, height);
85                 }
86                 apply_swizzle();
87                 allocated |= (1<<levels)-1;
88         }
89         else
90                 image(level, 0);
91 }
92
93 void Texture2D::image(unsigned level, const void *data)
94 {
95         if(width==0 || height==0)
96                 throw invalid_operation("Texture2D::image");
97         if(level>=levels)
98                 throw out_of_range("Texture2D::image");
99
100         LinAl::Vector<unsigned, 2> size = get_level_size(level);
101
102         if(ARB_texture_storage)
103                 return sub_image(level, 0, 0, size.x, size.y, data);
104
105         bind_scratch();
106
107         if(!allocated)
108         {
109                 glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, levels-1);
110                 apply_swizzle();
111         }
112
113         GLenum fmt = get_gl_pixelformat(storage_fmt);
114         GLenum comp = get_gl_components(get_components(storage_fmt));
115         GLenum type = get_gl_type(get_component_type(storage_fmt));
116         glTexImage2D(target, level, fmt, size.x, size.y, 0, comp, type, data);
117
118         allocated |= 1<<level;
119         if(auto_gen_mipmap && level==0)
120         {
121                 generate_mipmap();
122                 allocated |= (1<<levels)-1;
123         }
124 }
125
126 void Texture2D::image(unsigned level, PixelComponents comp, DataType type, const void *data)
127 {
128         if(comp!=get_components(format) || type!=get_component_type(format))
129                 throw incompatible_data("Texture2D::image");
130         image(level, data);
131 }
132
133 void Texture2D::sub_image(unsigned level, int x, int y, unsigned wd, unsigned ht, const void *data)
134 {
135         if(width==0 || height==0)
136                 throw invalid_operation("Texture2D::sub_image");
137         if(level>=levels)
138                 throw out_of_range("Texture2D::sub_image");
139
140         allocate(level);
141
142         GLenum comp = get_gl_components(get_components(storage_fmt));
143         GLenum type = get_gl_type(get_component_type(storage_fmt));
144         if(ARB_direct_state_access)
145                 glTextureSubImage2D(id, level, x, y, wd, ht, comp, type, data);
146         else
147         {
148                 bind_scratch();
149                 glTexSubImage2D(target, level, x, y, wd, ht, comp, type, data);
150         }
151
152         if(auto_gen_mipmap && level==0)
153                 generate_mipmap();
154 }
155
156 void Texture2D::sub_image(unsigned level, int x, int y, unsigned wd, unsigned ht, PixelComponents comp, DataType type, const void *data)
157 {
158         if(comp!=get_components(format) || type!=get_component_type(format))
159                 throw incompatible_data("Texture2D::sub_image");
160         sub_image(level, x, y, wd, ht, data);
161 }
162
163 void Texture2D::image(const Graphics::Image &img, unsigned lv)
164 {
165         image(img, lv, false);
166 }
167
168 void Texture2D::image(const Graphics::Image &img, unsigned lv, bool from_buffer)
169 {
170         unsigned w = img.get_width();
171         unsigned h = img.get_height();
172         PixelFormat fmt = pixelformat_from_image(img);
173         storage(make_pixelformat(get_components(fmt), get_component_type(fmt), use_srgb_format), w, h, lv);
174
175         image(0, from_buffer ? 0 : img.get_pixels());
176 }
177
178 unsigned Texture2D::get_n_levels() const
179 {
180         unsigned n = 0;
181         for(unsigned s=max(width, height); s; s>>=1, ++n) ;
182         return n;
183 }
184
185 LinAl::Vector<unsigned, 2> Texture2D::get_level_size(unsigned level) const
186 {
187         unsigned w = width>>level;
188         unsigned h = height>>level;
189
190         if(!w && h)
191                 w = 1;
192         else if(!h && w)
193                 h = 1;
194
195         return LinAl::Vector<unsigned, 2>(w, h);
196 }
197
198 Resource::AsyncLoader *Texture2D::load(IO::Seekable &io, const Resources *)
199 {
200         AsyncLoader *ldr = new AsyncLoader(*this, io);
201         return ldr;
202 }
203
204 UInt64 Texture2D::get_data_size() const
205 {
206         return id ? width*height*get_pixel_size(format) : 0;
207 }
208
209 void Texture2D::unload()
210 {
211         glDeleteTextures(1, &id);
212         id = 0;
213         allocated = 0;
214 }
215
216
217 Texture2D::Loader::Loader(Texture2D &t):
218         DataFile::DerivedObjectLoader<Texture2D, Texture::Loader>(t)
219 {
220         init();
221 }
222
223 Texture2D::Loader::Loader(Texture2D &t, Collection &c):
224         DataFile::DerivedObjectLoader<Texture2D, Texture::Loader>(t, c)
225 {
226         init();
227 }
228
229 void Texture2D::Loader::init()
230 {
231         add("raw_data", &Loader::raw_data);
232         add("storage", &Loader::storage);
233         add("storage", &Loader::storage_levels);
234 }
235
236 void Texture2D::Loader::raw_data(const string &data)
237 {
238         if(obj.manager)
239         {
240                 obj.set_manager(0);
241                 if(!obj.id)
242                         obj.generate_id();
243         }
244         obj.image(0, data.data());
245 }
246
247 void Texture2D::Loader::storage(PixelFormat fmt, unsigned w, unsigned h)
248 {
249         obj.storage(fmt, w, h);
250 }
251
252 void Texture2D::Loader::storage_levels(PixelFormat fmt, unsigned w, unsigned h, unsigned l)
253 {
254         obj.storage(fmt, w, h, l);
255 }
256
257
258 Texture2D::AsyncLoader::AsyncLoader(Texture2D &t, IO::Seekable &i):
259         texture(t),
260         io(i),
261         mapped_address(0),
262         img_loader(Graphics::ImageLoader::open_io(io)),
263         phase(0)
264 { }
265
266 Texture2D::AsyncLoader::~AsyncLoader()
267 {
268         if(mapped_address)
269                 pixel_buffer.unmap();
270         delete img_loader;
271 }
272
273 bool Texture2D::AsyncLoader::needs_sync() const
274 {
275         return phase%2;
276 }
277
278 bool Texture2D::AsyncLoader::process()
279 {
280         if(phase==0)
281         {
282                 image.load_headers(*img_loader);
283                 n_bytes = image.get_stride()*image.get_height();
284         }
285         else if(phase==1)
286         {
287                 pixel_buffer.storage(n_bytes);
288                 mapped_address = reinterpret_cast<char *>(pixel_buffer.map());
289         }
290         else if(phase==2)
291                 image.load_into(*img_loader, mapped_address);
292         else if(phase==3)
293         {
294                 mapped_address = 0;
295                 if(!pixel_buffer.unmap())
296                 {
297                         phase = 1;
298                         return false;
299                 }
300
301                 if(!texture.id)
302                         texture.generate_id();
303                 glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pixel_buffer.get_id());
304                 texture.image(image, 0, true);
305                 glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
306         }
307
308         ++phase;
309         return phase>3;
310 }
311
312 } // namespace GL
313 } // namespace Msp