]> git.tdb.fi Git - libs/gl.git/blob - source/backends/opengl/texture2d_backend.cpp
ab0c6d8cfb48ccf484da7d596fe8adf84ec07cf1
[libs/gl.git] / source / backends / opengl / texture2d_backend.cpp
1 #include <msp/datafile/rawdata.h>
2 #include <msp/gl/extensions/arb_direct_state_access.h>
3 #include <msp/gl/extensions/arb_texture_storage.h>
4 #include <msp/gl/extensions/arb_vertex_buffer_object.h>
5 #include <msp/graphics/imageloader.h>
6 #include "buffer.h"
7 #include "gl.h"
8 #include "texture2d.h"
9 #include "texture2d_backend.h"
10
11 using namespace std;
12
13 namespace Msp {
14 namespace GL {
15
16 class OpenGLTexture2D::AsyncLoader: public Resource::AsyncLoader
17 {
18 private:
19         Texture2D &texture;
20         IO::Seekable &io;
21         Buffer pixel_buffer;
22         char *mapped_address = 0;
23         Graphics::Image image;
24         Graphics::ImageLoader *img_loader = 0;
25         DataFile::RawData *raw_data = 0;
26         unsigned n_bytes = 0;
27         int phase = 0;
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 OpenGLTexture2D::OpenGLTexture2D():
39         Texture(GL_TEXTURE_2D)
40 { }
41
42 void OpenGLTexture2D::allocate()
43 {
44         const Texture2D &self = *static_cast<const Texture2D *>(this);
45
46         if(!id)
47                 create();
48
49         GLenum gl_fmt = get_gl_pixelformat(storage_fmt);
50         if(ARB_texture_storage)
51         {
52                 if(ARB_direct_state_access)
53                         glTextureStorage2D(id, n_levels, gl_fmt, self.width, self.height);
54                 else
55                 {
56                         bind_scratch();
57                         glTexStorage2D(target, n_levels, gl_fmt, self.width, self.height);
58                 }
59         }
60         else
61         {
62                 bind_scratch();
63                 glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, n_levels-1);
64                 GLenum comp = get_gl_components(get_components(storage_fmt));
65                 GLenum type = get_gl_type(get_component_type(storage_fmt));
66                 for(unsigned i=0; i<n_levels; ++i)
67                 {
68                         auto lv_size = self.get_level_size(i);
69                         glTexImage2D(target, i, gl_fmt, lv_size.x, lv_size.y, 0, comp, type, 0);
70                 }
71         }
72
73         apply_swizzle();
74 }
75
76 void OpenGLTexture2D::sub_image(unsigned level, int x, int y, unsigned wd, unsigned ht, const void *data)
77 {
78         GLenum comp = get_gl_components(get_components(storage_fmt));
79         GLenum type = get_gl_type(get_component_type(storage_fmt));
80         if(ARB_direct_state_access)
81                 glTextureSubImage2D(id, level, x, y, wd, ht, comp, type, data);
82         else
83         {
84                 bind_scratch();
85                 glTexSubImage2D(target, level, x, y, wd, ht, comp, type, data);
86         }
87 }
88
89 void OpenGLTexture2D::sub_image(unsigned level, int x, int y, unsigned wd, unsigned ht, const Buffer &buffer, unsigned offset)
90 {
91         glBindBuffer(GL_PIXEL_UNPACK_BUFFER, buffer.id);
92         sub_image(level, x, y, wd, ht, reinterpret_cast<void *>(offset));
93         glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
94 }
95
96 Resource::AsyncLoader *OpenGLTexture2D::load(IO::Seekable &io, const Resources *)
97 {
98         return new AsyncLoader(static_cast<Texture2D &>(*this), io);
99 }
100
101 uint64_t OpenGLTexture2D::get_data_size() const
102 {
103         if(!id)
104                 return 0;
105
106         const Texture2D &self = *static_cast<const Texture2D *>(this);
107
108         size_t level_size = self.width*self.height*get_pixel_size(format);
109         size_t total_size = level_size;
110         for(unsigned i=0; i<n_levels; ++i, level_size>>=2)
111                 total_size += level_size;
112         return total_size;
113 }
114
115 void OpenGLTexture2D::unload()
116 {
117         glDeleteTextures(1, &id);
118         id = 0;
119 }
120
121
122 OpenGLTexture2D::AsyncTransfer::AsyncTransfer(AsyncTransfer &&other):
123         pixel_buffer(other.pixel_buffer)
124 {
125         other.pixel_buffer = 0;
126 }
127
128 OpenGLTexture2D::AsyncTransfer &OpenGLTexture2D::AsyncTransfer::operator=(AsyncTransfer &&other)
129 {
130         delete pixel_buffer;
131         pixel_buffer = other.pixel_buffer;
132         other.pixel_buffer = 0;
133
134         return *this;
135 }
136
137 OpenGLTexture2D::AsyncTransfer::~AsyncTransfer()
138 {
139         delete pixel_buffer;
140 }
141
142 void *OpenGLTexture2D::AsyncTransfer::allocate()
143 {
144         const Texture2D::AsyncTransfer &self = *static_cast<const Texture2D::AsyncTransfer *>(this);
145
146         pixel_buffer = new Buffer;
147         pixel_buffer->storage(self.data_size, STREAMING);
148         return pixel_buffer->map();
149 }
150
151 void OpenGLTexture2D::AsyncTransfer::finalize()
152 {
153         const Texture2D::AsyncTransfer &self = *static_cast<const Texture2D::AsyncTransfer *>(this);
154
155         pixel_buffer->unmap();
156
157         glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pixel_buffer->id);
158         self.texture->OpenGLTexture2D::sub_image(self.level, self.x, self.y, self.width, self.height, 0);
159         glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
160 }
161
162
163 OpenGLTexture2D::AsyncLoader::AsyncLoader(Texture2D &t, IO::Seekable &i):
164         texture(t),
165         io(i)
166 {
167         char magic[4] = { };
168         io.read(magic, 4);
169         io.seek(0, IO::S_BEG);
170
171         if(DataFile::RawData::detect_signature(string(magic, 4)))
172         {
173                 raw_data = new DataFile::RawData;
174                 raw_data->open_io(io, "async");
175         }
176         else
177                 img_loader = Graphics::ImageLoader::open_io(io);
178 }
179
180 OpenGLTexture2D::AsyncLoader::~AsyncLoader()
181 {
182         if(mapped_address)
183                 pixel_buffer.unmap();
184         delete img_loader;
185         delete raw_data;
186 }
187
188 bool OpenGLTexture2D::AsyncLoader::needs_sync() const
189 {
190         return phase%2;
191 }
192
193 bool OpenGLTexture2D::AsyncLoader::process()
194 {
195         if(phase==0)
196         {
197                 if(raw_data)
198                         n_bytes = raw_data->get_size();
199                 else
200                 {
201                         image.load_headers(*img_loader);
202                         n_bytes = image.get_stride()*image.get_height();
203                 }
204         }
205         else if(phase==1)
206         {
207                 pixel_buffer.storage(n_bytes, STREAMING);
208                 mapped_address = reinterpret_cast<char *>(pixel_buffer.map());
209         }
210         else if(phase==2)
211         {
212                 if(raw_data)
213                         raw_data->load_into(mapped_address);
214                 else
215                         image.load_into(*img_loader, mapped_address);
216         }
217         else if(phase==3)
218         {
219                 mapped_address = 0;
220                 if(!pixel_buffer.unmap())
221                 {
222                         phase = 1;
223                         return false;
224                 }
225
226                 if(!texture.id)
227                         texture.create();
228
229                 if(img_loader)
230                 {
231                         unsigned w = image.get_width();
232                         unsigned h = image.get_height();
233                         texture.storage(pixelformat_from_image(image, texture.use_srgb_format), w, h);
234                 }
235                 texture.OpenGLTexture2D::sub_image(0, 0, 0, texture.width, texture.height, pixel_buffer, 0);
236
237                 if(texture.auto_gen_mipmap)
238                         texture.generate_mipmap();
239         }
240
241         ++phase;
242         return phase>3;
243 }
244
245 } // namespace GL
246 } // namespace Msp