]> git.tdb.fi Git - libs/gl.git/blob - source/backends/opengl/texture2d_backend.cpp
725b603893973f1a52c47ad35d8dd8c8e69bc771
[libs/gl.git] / source / backends / opengl / texture2d_backend.cpp
1 #include <msp/gl/extensions/arb_direct_state_access.h>
2 #include <msp/gl/extensions/arb_texture_storage.h>
3 #include <msp/gl/extensions/arb_vertex_buffer_object.h>
4 #include <msp/graphics/imageloader.h>
5 #include "buffer.h"
6 #include "gl.h"
7 #include "texture2d.h"
8 #include "texture2d_backend.h"
9
10 namespace Msp {
11 namespace GL {
12
13 class OpenGLTexture2D::AsyncLoader: public Resource::AsyncLoader
14 {
15 private:
16         Texture2D &texture;
17         IO::Seekable &io;
18         Buffer pixel_buffer;
19         char *mapped_address;
20         Graphics::Image image;
21         Graphics::ImageLoader *img_loader;
22         unsigned n_bytes;
23         int phase;
24
25 public:
26         AsyncLoader(Texture2D &, IO::Seekable &);
27         ~AsyncLoader();
28
29         virtual bool needs_sync() const;
30         virtual bool process();
31 };
32
33
34 OpenGLTexture2D::OpenGLTexture2D(ResourceManager *m):
35         Texture(GL_TEXTURE_2D, m)
36 { }
37
38 void OpenGLTexture2D::allocate()
39 {
40         unsigned width = static_cast<const Texture2D *>(this)->width;
41         unsigned height = static_cast<const Texture2D *>(this)->height;
42         unsigned levels = static_cast<const Texture2D *>(this)->levels;
43
44         if(!id)
45                 generate_id();
46
47         GLenum gl_fmt = get_gl_pixelformat(storage_fmt);
48         if(ARB_texture_storage)
49         {
50                 if(ARB_direct_state_access)
51                         glTextureStorage2D(id, levels, gl_fmt, width, height);
52                 else
53                 {
54                         bind_scratch();
55                         glTexStorage2D(target, levels, gl_fmt, width, height);
56                 }
57         }
58         else
59         {
60                 bind_scratch();
61                 glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, levels-1);
62                 GLenum comp = get_gl_components(get_components(storage_fmt));
63                 GLenum type = get_gl_type(get_component_type(storage_fmt));
64                 for(unsigned i=0; i<levels; ++i)
65                 {
66                         auto lv_size = static_cast<const Texture2D *>(this)->get_level_size(i);
67                         glTexImage2D(target, i, gl_fmt, lv_size.x, lv_size.y, 0, comp, type, 0);
68                 }
69         }
70
71         apply_swizzle();
72 }
73
74 void OpenGLTexture2D::sub_image(unsigned level, int x, int y, unsigned wd, unsigned ht, const void *data)
75 {
76         GLenum comp = get_gl_components(get_components(storage_fmt));
77         GLenum type = get_gl_type(get_component_type(storage_fmt));
78         if(ARB_direct_state_access)
79                 glTextureSubImage2D(id, level, x, y, wd, ht, comp, type, data);
80         else
81         {
82                 bind_scratch();
83                 glTexSubImage2D(target, level, x, y, wd, ht, comp, type, data);
84         }
85 }
86
87 void OpenGLTexture2D::sub_image(unsigned level, int x, int y, unsigned wd, unsigned ht, const Buffer &buffer, unsigned offset)
88 {
89         glBindBuffer(GL_PIXEL_UNPACK_BUFFER, buffer.id);
90         sub_image(level, x, y, wd, ht, reinterpret_cast<void *>(offset));
91         glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
92 }
93
94 Resource::AsyncLoader *OpenGLTexture2D::create_async_loader(IO::Seekable &io)
95 {
96         return new AsyncLoader(static_cast<Texture2D &>(*this), io);
97 }
98
99 void OpenGLTexture2D::unload()
100 {
101         glDeleteTextures(1, &id);
102         id = 0;
103 }
104
105
106 OpenGLTexture2D::AsyncLoader::AsyncLoader(Texture2D &t, IO::Seekable &i):
107         texture(t),
108         io(i),
109         mapped_address(0),
110         img_loader(Graphics::ImageLoader::open_io(io)),
111         phase(0)
112 { }
113
114 OpenGLTexture2D::AsyncLoader::~AsyncLoader()
115 {
116         if(mapped_address)
117                 pixel_buffer.unmap();
118         delete img_loader;
119 }
120
121 bool OpenGLTexture2D::AsyncLoader::needs_sync() const
122 {
123         return phase%2;
124 }
125
126 bool OpenGLTexture2D::AsyncLoader::process()
127 {
128         if(phase==0)
129         {
130                 image.load_headers(*img_loader);
131                 n_bytes = image.get_stride()*image.get_height();
132         }
133         else if(phase==1)
134         {
135                 pixel_buffer.storage(n_bytes);
136                 mapped_address = reinterpret_cast<char *>(pixel_buffer.map());
137         }
138         else if(phase==2)
139                 image.load_into(*img_loader, mapped_address);
140         else if(phase==3)
141         {
142                 mapped_address = 0;
143                 if(!pixel_buffer.unmap())
144                 {
145                         phase = 1;
146                         return false;
147                 }
148
149                 if(!texture.id)
150                         texture.generate_id();
151
152                 unsigned w = image.get_width();
153                 unsigned h = image.get_height();
154                 texture.storage(pixelformat_from_image(image, texture.use_srgb_format), w, h);
155                 texture.OpenGLTexture2D::sub_image(0, 0, 0, w, h, pixel_buffer, 0);
156
157                 if(texture.auto_gen_mipmap)
158                         texture.generate_mipmap();
159         }
160
161         ++phase;
162         return phase>3;
163 }
164
165 } // namespace GL
166 } // namespace Msp