]> git.tdb.fi Git - libs/gl.git/blob - source/core/texture2d.cpp
Simplify Program by removing transient data
[libs/gl.git] / source / core / texture2d.cpp
1 #include <msp/datafile/rawdata.h>
2 #include <msp/graphics/imageloader.h>
3 #include "error.h"
4 #include "texture2d.h"
5
6 using namespace std;
7
8 namespace Msp {
9 namespace GL {
10
11 class Texture2D::AsyncLoader: public Resource::AsyncLoader
12 {
13 private:
14         Texture2D &texture;
15         IO::Seekable &io;
16         Texture2D::AsyncTransfer transfer;
17         Graphics::Image image;
18         Graphics::ImageLoader *img_loader = 0;
19         DataFile::RawData *raw_data = 0;
20         unsigned n_bytes = 0;
21         int phase = 0;
22
23 public:
24         AsyncLoader(Texture2D &, IO::Seekable &);
25         ~AsyncLoader();
26
27         virtual bool needs_sync() const;
28         virtual bool process();
29 };
30
31
32 Texture2D::~Texture2D()
33 {
34         set_manager(0);
35 }
36
37 void Texture2D::storage(PixelFormat fmt, unsigned wd, unsigned ht, unsigned lv)
38 {
39         if(width>0)
40         {
41                 if(fmt!=format || wd!=width || ht!=height || (lv && lv!=n_levels))
42                         throw incompatible_data("Texture2D::storage");
43                 return;
44         }
45         if(wd==0 || ht==0)
46                 throw invalid_argument("Texture2D::storage");
47
48         set_format(fmt);
49         width = wd;
50         height = ht;
51         n_levels = count_levels(max(width, height));
52         if(lv>0)
53                 n_levels = min(n_levels, lv);
54
55         allocate();
56 }
57
58 void Texture2D::image(unsigned level, const void *data)
59 {
60         LinAl::Vector<unsigned, 2> size = get_level_size(level);
61         return sub_image(level, 0, 0, size.x, size.y, data);
62 }
63
64 void Texture2D::sub_image(unsigned level, unsigned x, unsigned y, unsigned wd, unsigned ht, const void *data)
65 {
66         if(width==0 || height==0)
67                 throw invalid_operation("Texture2D::sub_image");
68         if(level>=n_levels || x>width || x+wd>width || y>height || y+ht>height)
69                 throw out_of_range("Texture2D::sub_image");
70
71         Texture2DBackend::sub_image(level, x, y, wd, ht, data);
72 }
73
74 Texture2D::AsyncTransfer Texture2D::sub_image_async(unsigned level, unsigned x, unsigned y, unsigned wd, unsigned ht)
75 {
76         return AsyncTransfer(*this, level, x, y, wd, ht);
77 }
78
79 void Texture2D::image(const Graphics::Image &img, unsigned lv)
80 {
81         storage(pixelformat_from_image(img, use_srgb_format), img.get_width(), img.get_height(), lv);
82         image(0, img.get_pixels());
83 }
84
85 LinAl::Vector<unsigned, 2> Texture2D::get_level_size(unsigned level) const
86 {
87         unsigned w = width>>level;
88         unsigned h = height>>level;
89
90         if(!w && h)
91                 w = 1;
92         else if(!h && w)
93                 h = 1;
94
95         return LinAl::Vector<unsigned, 2>(w, h);
96 }
97
98 Resource::AsyncLoader *Texture2D::load(IO::Seekable &io, const Resources *)
99 {
100         return new AsyncLoader(static_cast<Texture2D &>(*this), io);
101 }
102
103
104 Texture2D::Loader::Loader(Texture2D &t):
105         DataFile::DerivedObjectLoader<Texture2D, Texture::Loader>(t)
106 {
107         init();
108 }
109
110 Texture2D::Loader::Loader(Texture2D &t, Collection &c):
111         DataFile::DerivedObjectLoader<Texture2D, Texture::Loader>(t, c)
112 {
113         init();
114 }
115
116 void Texture2D::Loader::init()
117 {
118         add("storage", &Loader::storage);
119         add("storage", &Loader::storage_levels);
120 }
121
122 void Texture2D::Loader::storage(PixelFormat fmt, unsigned w, unsigned h)
123 {
124         obj.storage(fmt, w, h);
125 }
126
127 void Texture2D::Loader::storage_levels(PixelFormat fmt, unsigned w, unsigned h, unsigned l)
128 {
129         obj.storage(fmt, w, h, l);
130 }
131
132
133 Texture2D::AsyncTransfer::AsyncTransfer(Texture2D &t, unsigned l, unsigned x_, unsigned y_, unsigned wd, unsigned ht):
134         texture(&t),
135         level(l),
136         x(x_),
137         y(y_),
138         width(wd),
139         height(ht),
140         data_size(width*height*get_pixel_size(texture->storage_fmt)),
141         dest_addr(0)
142 {
143         dest_addr = allocate();
144 }
145
146 Texture2D::AsyncTransfer::AsyncTransfer(AsyncTransfer &&other):
147         Texture2DBackend::AsyncTransfer(move(other)),
148         texture(other.texture),
149         level(other.level),
150         x(other.x),
151         y(other.y),
152         width(other.width),
153         height(other.height),
154         data_size(other.data_size),
155         dest_addr(other.dest_addr)
156 {
157         other.dest_addr = 0;
158 }
159
160 Texture2D::AsyncTransfer &Texture2D::AsyncTransfer::operator=(AsyncTransfer &&other)
161 {
162         if(dest_addr)
163                 finalize();
164
165         Texture2DBackend::AsyncTransfer::operator=(move(other));
166
167         texture = other.texture;
168         level = other.level;
169         x = other.x;
170         y = other.y;
171         width = other.width;
172         height = other.height;
173         data_size = other.data_size;
174         dest_addr = other.dest_addr;
175
176         other.dest_addr = 0;
177
178         return *this;
179 }
180
181 Texture2D::AsyncTransfer::~AsyncTransfer()
182 {
183         if(dest_addr)
184                 finalize();
185 }
186
187
188 Texture2D::AsyncLoader::AsyncLoader(Texture2D &t, IO::Seekable &i):
189         texture(t),
190         io(i)
191 {
192         char magic[4] = { };
193         io.read(magic, 4);
194         io.seek(0, IO::S_BEG);
195
196         if(DataFile::RawData::detect_signature(string(magic, 4)))
197         {
198                 raw_data = new DataFile::RawData;
199                 raw_data->open_io(io, "async");
200         }
201         else
202                 img_loader = Graphics::ImageLoader::open_io(io);
203 }
204
205 Texture2D::AsyncLoader::~AsyncLoader()
206 {
207         delete img_loader;
208         delete raw_data;
209 }
210
211 bool Texture2D::AsyncLoader::needs_sync() const
212 {
213         return phase%2;
214 }
215
216 bool Texture2D::AsyncLoader::process()
217 {
218         if(phase==0)
219         {
220                 if(raw_data)
221                         n_bytes = raw_data->get_size();
222                 else
223                 {
224                         image.load_headers(*img_loader);
225                         n_bytes = image.get_stride()*image.get_height();
226                 }
227         }
228         else if(phase==1)
229         {
230                 if(img_loader)
231                 {
232                         unsigned w = image.get_width();
233                         unsigned h = image.get_height();
234                         texture.storage(pixelformat_from_image(image, texture.use_srgb_format), w, h);
235                 }
236
237                 transfer = texture.sub_image_async(0, 0, 0, texture.width, texture.height);
238         }
239         else if(phase==2)
240         {
241                 if(texture.swizzle==RGBA_TO_RGB)
242                 {
243                         const void *data;
244                         if(raw_data)
245                         {
246                                 raw_data->load();
247                                 data = raw_data->get_data();
248                         }
249                         else
250                         {
251                                 image.load(*img_loader);
252                                 data = image.get_pixels();
253                         }
254                         texture.stage_pixels(transfer.get_address(), data, texture.width*texture.height);
255                 }
256                 else if(raw_data)
257                         raw_data->load_into(transfer.get_address());
258                 else
259                         image.load_into(*img_loader, transfer.get_address());
260         }
261         else if(phase==3)
262         {
263                 transfer = Texture2D::AsyncTransfer();
264
265                 if(texture.auto_gen_mipmap)
266                         texture.generate_mipmap();
267         }
268
269         ++phase;
270         return phase>3;
271 }
272
273 } // namespace GL
274 } // namespace Msp