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