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