]> git.tdb.fi Git - libs/gl.git/blobdiff - source/backends/opengl/texture2d_backend.cpp
Move all OpenGL-specific code to a separate directory
[libs/gl.git] / source / backends / opengl / texture2d_backend.cpp
diff --git a/source/backends/opengl/texture2d_backend.cpp b/source/backends/opengl/texture2d_backend.cpp
new file mode 100644 (file)
index 0000000..71a998d
--- /dev/null
@@ -0,0 +1,163 @@
+#include <msp/gl/extensions/arb_direct_state_access.h>
+#include <msp/gl/extensions/arb_texture_storage.h>
+#include <msp/gl/extensions/arb_vertex_buffer_object.h>
+#include <msp/graphics/imageloader.h>
+#include "buffer.h"
+#include "gl.h"
+#include "texture2d.h"
+#include "texture2d_backend.h"
+
+namespace Msp {
+namespace GL {
+
+class OpenGLTexture2D::AsyncLoader: public Resource::AsyncLoader
+{
+private:
+       Texture2D &texture;
+       IO::Seekable &io;
+       Buffer pixel_buffer;
+       char *mapped_address;
+       Graphics::Image image;
+       Graphics::ImageLoader *img_loader;
+       unsigned n_bytes;
+       int phase;
+
+public:
+       AsyncLoader(Texture2D &, IO::Seekable &);
+       ~AsyncLoader();
+
+       virtual bool needs_sync() const;
+       virtual bool process();
+};
+
+
+OpenGLTexture2D::OpenGLTexture2D(ResourceManager *m):
+       Texture(GL_TEXTURE_2D, m)
+{ }
+
+void OpenGLTexture2D::allocate()
+{
+       unsigned width = static_cast<const Texture2D *>(this)->width;
+       unsigned height = static_cast<const Texture2D *>(this)->height;
+       unsigned levels = static_cast<const Texture2D *>(this)->levels;
+
+       GLenum gl_fmt = get_gl_pixelformat(storage_fmt);
+       if(ARB_texture_storage)
+       {
+               if(ARB_direct_state_access)
+                       glTextureStorage2D(id, levels, gl_fmt, width, height);
+               else
+               {
+                       bind_scratch();
+                       glTexStorage2D(target, levels, gl_fmt, width, height);
+               }
+       }
+       else
+       {
+               bind_scratch();
+               glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, levels-1);
+               GLenum comp = get_gl_components(get_components(storage_fmt));
+               GLenum type = get_gl_type(get_component_type(storage_fmt));
+               for(unsigned i=0; i<levels; ++i)
+               {
+                       auto lv_size = static_cast<const Texture2D *>(this)->get_level_size(i);
+                       glTexImage2D(target, i, gl_fmt, lv_size.x, lv_size.y, 0, comp, type, 0);
+               }
+       }
+
+       apply_swizzle();
+}
+
+void OpenGLTexture2D::sub_image(unsigned level, int x, int y, unsigned wd, unsigned ht, const void *data)
+{
+       GLenum comp = get_gl_components(get_components(storage_fmt));
+       GLenum type = get_gl_type(get_component_type(storage_fmt));
+       if(ARB_direct_state_access)
+               glTextureSubImage2D(id, level, x, y, wd, ht, comp, type, data);
+       else
+       {
+               bind_scratch();
+               glTexSubImage2D(target, level, x, y, wd, ht, comp, type, data);
+       }
+}
+
+void OpenGLTexture2D::sub_image(unsigned level, int x, int y, unsigned wd, unsigned ht, const Buffer &buffer, unsigned offset)
+{
+       glBindBuffer(GL_PIXEL_UNPACK_BUFFER, buffer.id);
+       sub_image(level, x, y, wd, ht, reinterpret_cast<void *>(offset));
+       glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
+}
+
+Resource::AsyncLoader *OpenGLTexture2D::create_async_loader(IO::Seekable &io)
+{
+       return new AsyncLoader(static_cast<Texture2D &>(*this), io);
+}
+
+void OpenGLTexture2D::unload()
+{
+       glDeleteTextures(1, &id);
+       id = 0;
+}
+
+
+OpenGLTexture2D::AsyncLoader::AsyncLoader(Texture2D &t, IO::Seekable &i):
+       texture(t),
+       io(i),
+       mapped_address(0),
+       img_loader(Graphics::ImageLoader::open_io(io)),
+       phase(0)
+{ }
+
+OpenGLTexture2D::AsyncLoader::~AsyncLoader()
+{
+       if(mapped_address)
+               pixel_buffer.unmap();
+       delete img_loader;
+}
+
+bool OpenGLTexture2D::AsyncLoader::needs_sync() const
+{
+       return phase%2;
+}
+
+bool OpenGLTexture2D::AsyncLoader::process()
+{
+       if(phase==0)
+       {
+               image.load_headers(*img_loader);
+               n_bytes = image.get_stride()*image.get_height();
+       }
+       else if(phase==1)
+       {
+               pixel_buffer.storage(n_bytes);
+               mapped_address = reinterpret_cast<char *>(pixel_buffer.map());
+       }
+       else if(phase==2)
+               image.load_into(*img_loader, mapped_address);
+       else if(phase==3)
+       {
+               mapped_address = 0;
+               if(!pixel_buffer.unmap())
+               {
+                       phase = 1;
+                       return false;
+               }
+
+               if(!texture.id)
+                       texture.generate_id();
+
+               unsigned w = image.get_width();
+               unsigned h = image.get_height();
+               texture.storage(pixelformat_from_image(image, texture.use_srgb_format), w, h);
+               texture.OpenGLTexture2D::sub_image(0, 0, 0, w, h, pixel_buffer, 0);
+
+               if(texture.auto_gen_mipmap)
+                       texture.generate_mipmap();
+       }
+
+       ++phase;
+       return phase>3;
+}
+
+} // namespace GL
+} // namespace Msp