--- /dev/null
+#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