]> git.tdb.fi Git - libs/gl.git/blob - source/texture2d.cpp
Implement automatic unloading when a total size limit is exceeded
[libs/gl.git] / source / texture2d.cpp
1 #include <msp/io/memory.h>
2 #include "bindable.h"
3 #include "buffer.h"
4 #include "error.h"
5 #include "pixelstore.h"
6 #include "resources.h"
7 #include "texture2d.h"
8
9 using namespace std;
10
11 namespace Msp {
12 namespace GL {
13
14 class Texture2D::AsyncLoader: public Resource::AsyncLoader
15 {
16 private:
17         Texture2D &texture;
18         IO::Seekable &io;
19         Buffer pixel_buffer;
20         char *mapped_address;
21         Graphics::Image image;
22         unsigned n_bytes;
23         int phase;
24
25 public:
26         AsyncLoader(Texture2D &, IO::Seekable &);
27
28         virtual bool needs_sync() const;
29         virtual bool process();
30 };
31
32
33 Texture2D::Texture2D(ResourceManager *m):
34         Texture(GL_TEXTURE_2D, m),
35         ifmt(RGB),
36         width(0),
37         height(0),
38         allocated(0)
39 { }
40
41 void Texture2D::storage(PixelFormat fmt, unsigned wd, unsigned ht)
42 {
43         if(width>0)
44                 throw invalid_operation("Texture2D::storage");
45         if(wd==0 || ht==0)
46                 throw invalid_argument("Texture2D::storage");
47         require_pixelformat(fmt);
48
49         ifmt = fmt;
50         width = wd;
51         height = ht;
52 }
53
54 void Texture2D::allocate(unsigned level)
55 {
56         if(allocated&(1<<level))
57                 return;
58
59         image(level, get_base_pixelformat(ifmt), UNSIGNED_BYTE, 0);
60 }
61
62 void Texture2D::image(unsigned level, PixelFormat fmt, DataType type, const void *data)
63 {
64         if(width==0 || height==0)
65                 throw invalid_operation("Texture2D::image");
66
67         unsigned w = width;
68         unsigned h = height;
69         get_level_size(level, w, h);
70
71         BindRestore _bind(this);
72         glTexImage2D(target, level, ifmt, w, h, 0, fmt, type, data);
73
74         allocated |= 1<<level;
75         if(gen_mipmap && level==0)
76         {
77                 for(; (w || h); w>>=1, h>>=1, ++level) ;
78                 allocated |= (1<<level)-1;
79         }
80 }
81
82 void Texture2D::sub_image(unsigned level, int x, int y, unsigned wd, unsigned ht, PixelFormat fmt, DataType type, const void *data)
83 {
84         if(width==0 || height==0)
85                 throw invalid_operation("Texture2D::sub_image");
86
87         allocate(level);
88
89         BindRestore _bind(this);
90         glTexSubImage2D(target, level, x, y, wd, ht, fmt, type, data);
91 }
92
93 void Texture2D::load_image(const string &fn, bool srgb)
94 {
95         Graphics::Image img;
96         img.load_file(fn);
97
98         image(img, srgb);
99 }
100
101 void Texture2D::image(const Graphics::Image &img, bool srgb)
102 {
103         image(img, srgb, false);
104 }
105
106 void Texture2D::image(const Graphics::Image &img, bool srgb, bool from_buffer)
107 {
108         unsigned w = img.get_width();
109         unsigned h = img.get_height();
110         PixelFormat fmt = pixelformat_from_graphics(img.get_format());
111         if(width==0)
112         {
113                 PixelFormat f = storage_pixelformat_from_graphics(img.get_format());
114                 if(srgb)
115                         f = get_srgb_pixelformat(f);
116                 storage(f, w, h);
117         }
118         else if(w!=width || h!=height)
119                 throw incompatible_data("Texture2D::image");
120
121         PixelStore pstore = PixelStore::from_image(img);
122         BindRestore _bind_ps(pstore);
123
124         image(0, fmt, UNSIGNED_BYTE, from_buffer ? 0 : img.get_data());
125 }
126
127 void Texture2D::get_level_size(unsigned level, unsigned &w, unsigned &h)
128 {
129         w >>= level;
130         h >>= level;
131
132         if(!w && h)
133                 w = 1;
134         else if(!h && w)
135                 h = 1;
136 }
137
138 Resource::AsyncLoader *Texture2D::load(IO::Seekable &io)
139 {
140         return new AsyncLoader(*this, io);
141 }
142
143 UInt64 Texture2D::get_data_size() const
144 {
145         return id ? width*height*get_component_count(ifmt) : 0;
146 }
147
148 void Texture2D::unload()
149 {
150         glDeleteTextures(1, &id);
151         id = 0;
152         // TODO check which params actually need refreshing
153         dirty_params = -1;
154 }
155
156
157 Texture2D::Loader::Loader(Texture2D &t):
158         DataFile::DerivedObjectLoader<Texture2D, Texture::Loader>(t)
159 {
160         init();
161 }
162
163 Texture2D::Loader::Loader(Texture2D &t, Collection &c):
164         DataFile::DerivedObjectLoader<Texture2D, Texture::Loader>(t, c)
165 {
166         init();
167 }
168
169 void Texture2D::Loader::init()
170 {
171         add("image_data", &Loader::image_data);
172         add("raw_data", &Loader::raw_data);
173         add("storage", &Loader::storage);
174         add("storage", &Loader::storage_b);
175 }
176
177 void Texture2D::Loader::image_data(const string &data)
178 {
179         Graphics::Image img;
180         IO::Memory mem(data.data(), data.size());
181         img.load_io(mem);
182
183         obj.image(img, srgb);
184 }
185
186 void Texture2D::Loader::raw_data(const string &data)
187 {
188         obj.image(0, get_base_pixelformat(obj.ifmt), UNSIGNED_BYTE, data.data());
189 }
190
191 void Texture2D::Loader::storage(PixelFormat fmt, unsigned w, unsigned h)
192 {
193         if(srgb)
194                 fmt = get_srgb_pixelformat(fmt);
195         obj.storage(fmt, w, h);
196 }
197
198 void Texture2D::Loader::storage_b(PixelFormat fmt, unsigned w, unsigned h, unsigned)
199 {
200         storage(fmt, w, h);
201 }
202
203
204 Texture2D::AsyncLoader::AsyncLoader(Texture2D &t, IO::Seekable &i):
205         texture(t),
206         io(i),
207         pixel_buffer(PIXEL_UNPACK_BUFFER),
208         mapped_address(0),
209         phase(0)
210 {
211         if(!texture.id)
212                 glGenTextures(1, &texture.id);
213 }
214
215 bool Texture2D::AsyncLoader::needs_sync() const
216 {
217         return phase%2;
218 }
219
220 bool Texture2D::AsyncLoader::process()
221 {
222         if(phase==0)
223         {
224                 /* TODO Enhance the ImageLoader system so that the image can be loaded
225                 directly to the buffer */
226                 image.load_io(io);
227                 n_bytes = image.get_stride()*image.get_height();
228         }
229         else if(phase==1)
230         {
231                 pixel_buffer.data(n_bytes, 0);
232                 mapped_address = reinterpret_cast<char *>(pixel_buffer.map(WRITE_ONLY));
233         }
234         else if(phase==2)
235         {
236                 const char *data = reinterpret_cast<const char *>(image.get_data());
237                 copy(data, data+n_bytes, mapped_address);
238         }
239         else if(phase==3)
240         {
241                 Bind _bind_buf(pixel_buffer, PIXEL_UNPACK_BUFFER);
242                 if(!pixel_buffer.unmap())
243                 {
244                         phase = 1;
245                         return false;
246                 }
247
248                 texture.image(image, false, true);
249         }
250
251         ++phase;
252         return phase>3;
253 }
254
255 } // namespace GL
256 } // namespace Msp