]> git.tdb.fi Git - libs/gl.git/blob - source/backends/opengl/texture2d_backend.cpp
9efc4351e65b47e8c6a08dda88d5cb3d344e4565
[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 = 0;
20         Graphics::Image image;
21         Graphics::ImageLoader *img_loader = 0;
22         unsigned n_bytes = 0;
23         int phase = 0;
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():
35         Texture(GL_TEXTURE_2D)
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                 create();
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         img_loader(Graphics::ImageLoader::open_io(io))
110 { }
111
112 OpenGLTexture2D::AsyncLoader::~AsyncLoader()
113 {
114         if(mapped_address)
115                 pixel_buffer.unmap();
116         delete img_loader;
117 }
118
119 bool OpenGLTexture2D::AsyncLoader::needs_sync() const
120 {
121         return phase%2;
122 }
123
124 bool OpenGLTexture2D::AsyncLoader::process()
125 {
126         if(phase==0)
127         {
128                 image.load_headers(*img_loader);
129                 n_bytes = image.get_stride()*image.get_height();
130         }
131         else if(phase==1)
132         {
133                 pixel_buffer.storage(n_bytes);
134                 mapped_address = reinterpret_cast<char *>(pixel_buffer.map());
135         }
136         else if(phase==2)
137                 image.load_into(*img_loader, mapped_address);
138         else if(phase==3)
139         {
140                 mapped_address = 0;
141                 if(!pixel_buffer.unmap())
142                 {
143                         phase = 1;
144                         return false;
145                 }
146
147                 if(!texture.id)
148                         texture.create();
149
150                 unsigned w = image.get_width();
151                 unsigned h = image.get_height();
152                 texture.storage(pixelformat_from_image(image, texture.use_srgb_format), w, h);
153                 texture.OpenGLTexture2D::sub_image(0, 0, 0, w, h, pixel_buffer, 0);
154
155                 if(texture.auto_gen_mipmap)
156                         texture.generate_mipmap();
157         }
158
159         ++phase;
160         return phase>3;
161 }
162
163 } // namespace GL
164 } // namespace Msp