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