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