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