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