]> git.tdb.fi Git - libs/gl.git/blob - source/core/texture2d.cpp
Refactor uploading texture data from a buffer
[libs/gl.git] / source / core / texture2d.cpp
1 #include <msp/gl/extensions/arb_direct_state_access.h>
2 #include <msp/gl/extensions/arb_texture_storage.h>
3 #include <msp/gl/extensions/arb_vertex_buffer_object.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 { }
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         {
51                 if(fmt!=format || wd!=width || ht!=height || (lv && lv!=levels))
52                         throw incompatible_data("Texture2D::storage");
53                 return;
54         }
55         if(wd==0 || ht==0)
56                 throw invalid_argument("Texture2D::storage");
57
58         set_format(fmt);
59         width = wd;
60         height = ht;
61         levels = get_n_levels();
62         if(lv>0)
63                 levels = min(levels, lv);
64
65         GLenum gl_fmt = get_gl_pixelformat(storage_fmt);
66         if(ARB_texture_storage)
67         {
68                 if(ARB_direct_state_access)
69                         glTextureStorage2D(id, levels, gl_fmt, width, height);
70                 else
71                 {
72                         bind_scratch();
73                         glTexStorage2D(target, levels, gl_fmt, width, height);
74                 }
75         }
76         else
77         {
78                 bind_scratch();
79                 glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, levels-1);
80                 GLenum comp = get_gl_components(get_components(storage_fmt));
81                 GLenum type = get_gl_type(get_component_type(storage_fmt));
82                 for(unsigned i=0; i<levels; ++i)
83                 {
84                         LinAl::Vector<unsigned, 2> lv_size = get_level_size(i);
85                         glTexImage2D(target, i, gl_fmt, lv_size.x, lv_size.y, 0, comp, type, 0);
86                 }
87         }
88
89         apply_swizzle();
90 }
91
92 void Texture2D::image(unsigned level, const void *data)
93 {
94         LinAl::Vector<unsigned, 2> size = get_level_size(level);
95         return sub_image(level, 0, 0, size.x, size.y, data);
96 }
97
98 void Texture2D::sub_image(unsigned level, int x, int y, unsigned wd, unsigned ht, const void *data)
99 {
100         if(width==0 || height==0)
101                 throw invalid_operation("Texture2D::sub_image");
102         if(level>=levels)
103                 throw out_of_range("Texture2D::sub_image");
104
105         GLenum comp = get_gl_components(get_components(storage_fmt));
106         GLenum type = get_gl_type(get_component_type(storage_fmt));
107         if(ARB_direct_state_access)
108                 glTextureSubImage2D(id, level, x, y, wd, ht, comp, type, data);
109         else
110         {
111                 bind_scratch();
112                 glTexSubImage2D(target, level, x, y, wd, ht, comp, type, data);
113         }
114 }
115
116 void Texture2D::sub_image(unsigned level, int x, int y, unsigned wd, unsigned ht, const Buffer &buffer, unsigned offset)
117 {
118         glBindBuffer(GL_PIXEL_UNPACK_BUFFER, buffer.id);
119         sub_image(level, x, y, wd, ht, reinterpret_cast<void *>(offset));
120         glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
121 }
122
123 void Texture2D::image(const Graphics::Image &img, unsigned lv)
124 {
125         storage(pixelformat_from_image(img, use_srgb_format), img.get_width(), img.get_height(), lv);
126         image(0, img.get_pixels());
127 }
128
129 unsigned Texture2D::get_n_levels() const
130 {
131         unsigned n = 0;
132         for(unsigned s=max(width, height); s; s>>=1, ++n) ;
133         return n;
134 }
135
136 LinAl::Vector<unsigned, 2> Texture2D::get_level_size(unsigned level) const
137 {
138         unsigned w = width>>level;
139         unsigned h = height>>level;
140
141         if(!w && h)
142                 w = 1;
143         else if(!h && w)
144                 h = 1;
145
146         return LinAl::Vector<unsigned, 2>(w, h);
147 }
148
149 Resource::AsyncLoader *Texture2D::load(IO::Seekable &io, const Resources *)
150 {
151         AsyncLoader *ldr = new AsyncLoader(*this, io);
152         return ldr;
153 }
154
155 uint64_t Texture2D::get_data_size() const
156 {
157         return id ? width*height*get_pixel_size(format) : 0;
158 }
159
160 void Texture2D::unload()
161 {
162         glDeleteTextures(1, &id);
163         id = 0;
164 }
165
166
167 Texture2D::Loader::Loader(Texture2D &t):
168         DataFile::DerivedObjectLoader<Texture2D, Texture::Loader>(t)
169 {
170         init();
171 }
172
173 Texture2D::Loader::Loader(Texture2D &t, Collection &c):
174         DataFile::DerivedObjectLoader<Texture2D, Texture::Loader>(t, c)
175 {
176         init();
177 }
178
179 void Texture2D::Loader::init()
180 {
181         add("raw_data", &Loader::raw_data);
182         add("storage", &Loader::storage);
183         add("storage", &Loader::storage_levels);
184 }
185
186 void Texture2D::Loader::raw_data(const string &data)
187 {
188         if(obj.manager)
189         {
190                 obj.set_manager(0);
191                 if(!obj.id)
192                         obj.generate_id();
193         }
194         obj.image(0, data.data());
195 }
196
197 void Texture2D::Loader::storage(PixelFormat fmt, unsigned w, unsigned h)
198 {
199         obj.storage(fmt, w, h);
200 }
201
202 void Texture2D::Loader::storage_levels(PixelFormat fmt, unsigned w, unsigned h, unsigned l)
203 {
204         obj.storage(fmt, w, h, l);
205 }
206
207
208 Texture2D::AsyncLoader::AsyncLoader(Texture2D &t, IO::Seekable &i):
209         texture(t),
210         io(i),
211         mapped_address(0),
212         img_loader(Graphics::ImageLoader::open_io(io)),
213         phase(0)
214 { }
215
216 Texture2D::AsyncLoader::~AsyncLoader()
217 {
218         if(mapped_address)
219                 pixel_buffer.unmap();
220         delete img_loader;
221 }
222
223 bool Texture2D::AsyncLoader::needs_sync() const
224 {
225         return phase%2;
226 }
227
228 bool Texture2D::AsyncLoader::process()
229 {
230         if(phase==0)
231         {
232                 image.load_headers(*img_loader);
233                 n_bytes = image.get_stride()*image.get_height();
234         }
235         else if(phase==1)
236         {
237                 pixel_buffer.storage(n_bytes);
238                 mapped_address = reinterpret_cast<char *>(pixel_buffer.map());
239         }
240         else if(phase==2)
241                 image.load_into(*img_loader, mapped_address);
242         else if(phase==3)
243         {
244                 mapped_address = 0;
245                 if(!pixel_buffer.unmap())
246                 {
247                         phase = 1;
248                         return false;
249                 }
250
251                 if(!texture.id)
252                         texture.generate_id();
253
254                 unsigned w = image.get_width();
255                 unsigned h = image.get_height();
256                 texture.storage(pixelformat_from_image(image, texture.use_srgb_format), w, h);
257                 texture.sub_image(0, 0, 0, w, h, pixel_buffer, 0);
258
259                 if(texture.auto_gen_mipmap)
260                         texture.generate_mipmap();
261         }
262
263         ++phase;
264         return phase>3;
265 }
266
267 } // namespace GL
268 } // namespace Msp