]> git.tdb.fi Git - libs/gl.git/commitdiff
Move all OpenGL-specific code to a separate directory
authorMikko Rasa <tdb@tdb.fi>
Fri, 1 Oct 2021 16:58:11 +0000 (19:58 +0300)
committerMikko Rasa <tdb@tdb.fi>
Fri, 1 Oct 2021 18:31:00 +0000 (21:31 +0300)
110 files changed:
Build
source/backends/opengl/backend_opengl.cpp [new file with mode: 0644]
source/backends/opengl/batch_backend.cpp [new file with mode: 0644]
source/backends/opengl/batch_backend.h [new file with mode: 0644]
source/backends/opengl/blend_backend.cpp [new file with mode: 0644]
source/backends/opengl/blend_backend.h [new file with mode: 0644]
source/backends/opengl/buffer_backend.cpp [new file with mode: 0644]
source/backends/opengl/buffer_backend.h [new file with mode: 0644]
source/backends/opengl/commands_backend.cpp [new file with mode: 0644]
source/backends/opengl/commands_backend.h [new file with mode: 0644]
source/backends/opengl/datatype_backend.cpp [new file with mode: 0644]
source/backends/opengl/datatype_backend.h [new file with mode: 0644]
source/backends/opengl/deviceinfo_backend.cpp [new file with mode: 0644]
source/backends/opengl/extension.cpp [new file with mode: 0644]
source/backends/opengl/extension.h [new file with mode: 0644]
source/backends/opengl/framebuffer_backend.cpp [new file with mode: 0644]
source/backends/opengl/framebuffer_backend.h [new file with mode: 0644]
source/backends/opengl/frameformat_backend.cpp [new file with mode: 0644]
source/backends/opengl/frameformat_backend.h [new file with mode: 0644]
source/backends/opengl/gl.h [new file with mode: 0644]
source/backends/opengl/pipelinestate_backend.cpp [new file with mode: 0644]
source/backends/opengl/pipelinestate_backend.h [new file with mode: 0644]
source/backends/opengl/pixelformat_backend.cpp [new file with mode: 0644]
source/backends/opengl/pixelformat_backend.h [new file with mode: 0644]
source/backends/opengl/predicate_backend.cpp [new file with mode: 0644]
source/backends/opengl/predicate_backend.h [new file with mode: 0644]
source/backends/opengl/primitivetype_backend.cpp [new file with mode: 0644]
source/backends/opengl/primitivetype_backend.h [new file with mode: 0644]
source/backends/opengl/program_backend.cpp [new file with mode: 0644]
source/backends/opengl/program_backend.h [new file with mode: 0644]
source/backends/opengl/query_backend.cpp [new file with mode: 0644]
source/backends/opengl/query_backend.h [new file with mode: 0644]
source/backends/opengl/sampler_backend.cpp [new file with mode: 0644]
source/backends/opengl/sampler_backend.h [new file with mode: 0644]
source/backends/opengl/stenciltest_backend.cpp [new file with mode: 0644]
source/backends/opengl/stenciltest_backend.h [new file with mode: 0644]
source/backends/opengl/texture1d_backend.cpp [new file with mode: 0644]
source/backends/opengl/texture1d_backend.h [new file with mode: 0644]
source/backends/opengl/texture2d_backend.cpp [new file with mode: 0644]
source/backends/opengl/texture2d_backend.h [new file with mode: 0644]
source/backends/opengl/texture2darray_backend.cpp [new file with mode: 0644]
source/backends/opengl/texture2darray_backend.h [new file with mode: 0644]
source/backends/opengl/texture2dmultisample_backend.cpp [new file with mode: 0644]
source/backends/opengl/texture2dmultisample_backend.h [new file with mode: 0644]
source/backends/opengl/texture3d_backend.cpp [new file with mode: 0644]
source/backends/opengl/texture3d_backend.h [new file with mode: 0644]
source/backends/opengl/texture_backend.cpp [new file with mode: 0644]
source/backends/opengl/texture_backend.h [new file with mode: 0644]
source/backends/opengl/texturecube_backend.cpp [new file with mode: 0644]
source/backends/opengl/texturecube_backend.h [new file with mode: 0644]
source/backends/opengl/uniformblock_backend.cpp [new file with mode: 0644]
source/backends/opengl/uniformblock_backend.h [new file with mode: 0644]
source/backends/opengl/vertexsetup_backend.cpp [new file with mode: 0644]
source/backends/opengl/vertexsetup_backend.h [new file with mode: 0644]
source/builders/font.cpp
source/core/backend.cpp
source/core/batch.cpp
source/core/batch.h
source/core/blend.cpp
source/core/blend.h
source/core/buffer.cpp
source/core/buffer.h
source/core/commands.cpp [deleted file]
source/core/commands.h
source/core/datatype.cpp [deleted file]
source/core/datatype.h
source/core/deviceinfo.cpp
source/core/extension.cpp [deleted file]
source/core/extension.h [deleted file]
source/core/framebuffer.cpp
source/core/framebuffer.h
source/core/frameformat.cpp
source/core/frameformat.h
source/core/gl.h [deleted file]
source/core/pipelinestate.cpp
source/core/pipelinestate.h
source/core/pixelformat.cpp
source/core/pixelformat.h
source/core/predicate.cpp
source/core/predicate.h
source/core/primitivetype.cpp
source/core/primitivetype.h
source/core/program.cpp
source/core/program.h
source/core/query.cpp
source/core/query.h
source/core/sampler.cpp
source/core/sampler.h
source/core/stenciltest.cpp
source/core/stenciltest.h
source/core/texture.cpp
source/core/texture.h
source/core/texture1d.cpp
source/core/texture1d.h
source/core/texture2d.cpp
source/core/texture2d.h
source/core/texture2darray.cpp
source/core/texture2darray.h
source/core/texture2dmultisample.cpp
source/core/texture2dmultisample.h
source/core/texture3d.cpp
source/core/texture3d.h
source/core/texturecube.cpp
source/core/texturecube.h
source/core/uniformblock.cpp
source/core/uniformblock.h
source/core/vertexsetup.cpp
source/core/vertexsetup.h
source/materials/lighting.h
source/materials/material.cpp

diff --git a/Build b/Build
index 22aedd4f89d0eb36eac1be2e26778099f1d55718..6bf5ebb03a11208134dcf638ae001a36c735c663 100644 (file)
--- a/Build
+++ b/Build
@@ -51,6 +51,7 @@ package "mspgl"
                source "source/resources";
                source "source/glsl";
                source "source/builders";
+               source "source/backends/opengl";
                source "extensions";
                source "builtin_data";
                source "shaderlib";
@@ -64,6 +65,7 @@ package "mspgl"
                        incpath "source/animation";
                        incpath "source/resources";
                        incpath "source/builders";
+                       incpath "source/backends/opengl";
                        standard CXX "c++11";
                };
                install true;
@@ -77,6 +79,7 @@ package "mspgl"
                        map "source/resources" "include/msp/gl";
                        map "source/glsl" "include/msp/gl/glsl";
                        map "source/builders" "include/msp/gl";
+                       map "source/backends/opengl" "include/msp/gl";
                        map "extensions" "include/msp/gl/extensions";
                };
        };
diff --git a/source/backends/opengl/backend_opengl.cpp b/source/backends/opengl/backend_opengl.cpp
new file mode 100644 (file)
index 0000000..ce8fc17
--- /dev/null
@@ -0,0 +1,49 @@
+#include <stdexcept>
+#include <cstdlib>
+#include "backend.h"
+#include "gl.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+GraphicsApi get_backend_api()
+{
+#ifdef GL_ES_VERSION_2_0
+       return OPENGL_ES;
+#else
+       return OPENGL;
+#endif
+}
+
+inline Version get_gl_version()
+{
+       const char *gl_ver_ptr = reinterpret_cast<const char *>(glGetString(GL_VERSION));
+       if(!gl_ver_ptr)
+               throw runtime_error("OpenGL version not available");
+
+       string gl_ver = gl_ver_ptr;
+       if(!gl_ver.compare(0, 10, "OpenGL ES "))
+               gl_ver.erase(0, 10);
+
+       Version ver(gl_ver.substr(0, gl_ver.find(' ')));
+
+       if(const char *force_ver_ptr = getenv("MSPGL_FORCE_VERSION"))
+       {
+               Version force_ver(force_ver_ptr);
+               if(force_ver<ver)
+                       ver = force_ver;
+       }
+
+       return ver;
+}
+
+const Version &get_backend_version()
+{
+       static Version version = get_gl_version();
+       return version;
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/backends/opengl/batch_backend.cpp b/source/backends/opengl/batch_backend.cpp
new file mode 100644 (file)
index 0000000..f44d6b8
--- /dev/null
@@ -0,0 +1,25 @@
+#include <msp/gl/extensions/msp_primitive_restart.h>
+#include "batch_backend.h"
+
+namespace Msp {
+namespace GL {
+
+OpenGLBatch::OpenGLBatch(PrimitiveType t):
+       gl_prim_type(GL::get_gl_primitive_type(t)),
+       gl_index_type(GL_UNSIGNED_SHORT)
+{ }
+
+bool OpenGLBatch::check_restart(bool require)
+{
+       if(require)
+               static Require _req(MSP_primitive_restart);
+       return MSP_primitive_restart;
+}
+
+void OpenGLBatch::set_index_type(DataType t)
+{
+       gl_index_type = get_gl_type(t);
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/backends/opengl/batch_backend.h b/source/backends/opengl/batch_backend.h
new file mode 100644 (file)
index 0000000..b04b143
--- /dev/null
@@ -0,0 +1,30 @@
+#ifndef MSP_GL_BATCH_BACKEND_H_
+#define MSP_GL_BATCH_BACKEND_H_
+
+#include "datatype.h"
+#include "primitivetype.h"
+
+namespace Msp {
+namespace GL {
+
+class OpenGLBatch
+{
+       friend class OpenGLCommands;
+
+protected:
+       unsigned gl_prim_type;
+       unsigned gl_index_type;
+
+       OpenGLBatch(PrimitiveType);
+
+       static bool check_restart(bool);
+
+       void set_index_type(DataType);
+};
+
+using BatchBackend = OpenGLBatch;
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/backends/opengl/blend_backend.cpp b/source/backends/opengl/blend_backend.cpp
new file mode 100644 (file)
index 0000000..4426748
--- /dev/null
@@ -0,0 +1,49 @@
+#include <stdexcept>
+#include <msp/gl/extensions/ext_blend_minmax.h>
+#include <msp/gl/extensions/ext_blend_subtract.h>
+#include "blend.h"
+#include "blend_backend.h"
+#include "gl.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+unsigned get_gl_blend_equation(BlendEquation eq)
+{
+       switch(eq)
+       {
+       case ADD: return GL_FUNC_ADD;
+       case SUBTRACT: return GL_FUNC_SUBTRACT;
+       case REVERSE_SUBTRACT: return GL_FUNC_REVERSE_SUBTRACT;
+       case MIN: return GL_MIN;
+       case MAX: return GL_MAX;
+       default: throw invalid_argument("get_gl_blend_equation");
+       }
+}
+
+unsigned get_gl_blend_factor(BlendFactor factor)
+{
+       switch(factor)
+       {
+       case ZERO: return GL_ZERO;
+       case ONE: return GL_ONE;
+       case SRC_COLOR: return GL_SRC_COLOR;
+       case ONE_MINUS_SRC_COLOR: return GL_ONE_MINUS_SRC_COLOR;
+       case SRC_ALPHA: return GL_SRC_ALPHA;
+       case ONE_MINUS_SRC_ALPHA: return GL_ONE_MINUS_SRC_ALPHA;
+       case DST_COLOR: return GL_DST_COLOR;
+       case ONE_MINUS_DST_COLOR: return GL_ONE_MINUS_DST_COLOR;
+       case DST_ALPHA: return GL_DST_ALPHA;
+       case ONE_MINUS_DST_ALPHA: return GL_ONE_MINUS_DST_ALPHA;
+       case CONSTANT_COLOR: return GL_CONSTANT_COLOR;
+       case ONE_MINUS_CONSTANT_COLOR: return GL_ONE_MINUS_CONSTANT_COLOR;
+       case CONSTANT_ALPHA: return GL_CONSTANT_ALPHA;
+       case ONE_MINUS_CONSTANT_ALPHA: return GL_ONE_MINUS_CONSTANT_ALPHA;
+       default: throw invalid_argument("get_gl_blend_factor");
+       }
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/backends/opengl/blend_backend.h b/source/backends/opengl/blend_backend.h
new file mode 100644 (file)
index 0000000..c436538
--- /dev/null
@@ -0,0 +1,17 @@
+#ifndef MSP_GL_BLEND_BACKEND_H_
+#define MSP_GL_BLEND_BACKEND_H_
+
+#ifndef MSP_GL_BLEND_H_
+#error "blend_backend.h requires blend.h"
+#endif
+
+namespace Msp {
+namespace GL {
+
+unsigned get_gl_blend_equation(BlendEquation);
+unsigned get_gl_blend_factor(BlendFactor);
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/backends/opengl/buffer_backend.cpp b/source/backends/opengl/buffer_backend.cpp
new file mode 100644 (file)
index 0000000..e1bced7
--- /dev/null
@@ -0,0 +1,129 @@
+#include <msp/gl/extensions/arb_buffer_storage.h>
+#include <msp/gl/extensions/arb_direct_state_access.h>
+#include <msp/gl/extensions/arb_map_buffer_range.h>
+#include <msp/gl/extensions/arb_vertex_buffer_object.h>
+#include <msp/gl/extensions/khr_debug.h>
+#include <msp/gl/extensions/oes_mapbuffer.h>
+#include "buffer.h"
+#include "buffer_backend.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+OpenGLBuffer *OpenGLBuffer::scratch_binding = 0;
+
+OpenGLBuffer::OpenGLBuffer()
+{
+       static Require _req(ARB_vertex_buffer_object);
+
+       if(ARB_direct_state_access)
+               glCreateBuffers(1, &id);
+       else
+               glGenBuffers(1, &id);
+}
+
+OpenGLBuffer::~OpenGLBuffer()
+{
+       if(this==scratch_binding)
+               unbind_scratch();
+       glDeleteBuffers(1, &id);
+}
+
+void OpenGLBuffer::allocate()
+{
+       unsigned size = static_cast<const Buffer *>(this)->size;
+
+       if(ARB_buffer_storage)
+       {
+               static const int flags = GL_MAP_READ_BIT|GL_MAP_WRITE_BIT|GL_DYNAMIC_STORAGE_BIT;
+               if(ARB_direct_state_access)
+                       glNamedBufferStorage(id, size, 0, flags);
+               else
+               {
+                       bind_scratch();
+                       glBufferStorage(GL_ARRAY_BUFFER, size, 0, flags);
+               }
+       }
+       else if(ARB_direct_state_access)
+               glNamedBufferData(id, size, 0, GL_STATIC_DRAW);
+       else
+       {
+               bind_scratch();
+               glBufferData(GL_ARRAY_BUFFER, size, 0, GL_STATIC_DRAW);
+       }
+}
+
+void OpenGLBuffer::sub_data(unsigned off, unsigned sz, const void *d)
+{
+       if(ARB_direct_state_access)
+               glNamedBufferSubData(id, off, sz, d);
+       else
+       {
+               bind_scratch();
+               glBufferSubData(GL_ARRAY_BUFFER, off, sz, d);
+       }
+}
+
+void *OpenGLBuffer::map()
+{
+       static Require _req(ARB_map_buffer_range);
+
+       unsigned size = static_cast<const Buffer *>(this)->size;
+
+       if(ARB_direct_state_access)
+               return glMapNamedBufferRange(id, 0, size, GL_MAP_READ_BIT|GL_MAP_WRITE_BIT);
+       else
+       {
+               bind_scratch();
+               void *result = glMapBufferRange(GL_ARRAY_BUFFER, 0, size, GL_MAP_READ_BIT|GL_MAP_WRITE_BIT);
+               return result;
+       }
+}
+
+bool OpenGLBuffer::unmap()
+{
+       // TODO check if it's mapped
+       if(ARB_direct_state_access)
+               return glUnmapNamedBuffer(id);
+       else if(OES_mapbuffer)
+       {
+               bind_scratch();
+               bool result = glUnmapBuffer(GL_ARRAY_BUFFER);
+               return result;
+       }
+       else
+               return true;
+}
+
+void OpenGLBuffer::set_debug_name(const string &name)
+{
+#ifdef DEBUG
+       if(KHR_debug)
+               glObjectLabel(GL_BUFFER, id, name.size(), name.c_str());
+#else
+       (void)name;
+#endif
+}
+
+void OpenGLBuffer::bind_scratch()
+{
+       if(scratch_binding!=this)
+       {
+               glBindBuffer(GL_ARRAY_BUFFER, id);
+               scratch_binding = this;
+       }
+}
+
+void OpenGLBuffer::unbind_scratch()
+{
+       if(scratch_binding)
+       {
+               glBindBuffer(GL_ARRAY_BUFFER, 0);
+               scratch_binding = 0;
+       }
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/backends/opengl/buffer_backend.h b/source/backends/opengl/buffer_backend.h
new file mode 100644 (file)
index 0000000..7162a26
--- /dev/null
@@ -0,0 +1,40 @@
+#ifndef MSP_GL_BUFFER_BACKEND_H_
+#define MSP_GL_BUFFER_BACKEND_H_
+
+#include <msp/core/noncopyable.h>
+
+namespace Msp {
+namespace GL {
+
+class OpenGLBuffer: public NonCopyable
+{
+       friend class OpenGLPipelineState;
+       friend class OpenGLTexture2D;
+       friend class OpenGLVertexSetup;
+
+protected:
+       unsigned id;
+
+       static OpenGLBuffer *scratch_binding;
+
+       OpenGLBuffer();
+       ~OpenGLBuffer();
+
+       void allocate();
+       void sub_data(unsigned, unsigned, const void *);
+
+       void *map();
+       bool unmap();
+
+       void set_debug_name(const std::string &);
+
+       void bind_scratch();
+       static void unbind_scratch();
+};
+
+using BufferBackend = OpenGLBuffer;
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/backends/opengl/commands_backend.cpp b/source/backends/opengl/commands_backend.cpp
new file mode 100644 (file)
index 0000000..fd66b99
--- /dev/null
@@ -0,0 +1,126 @@
+#include <algorithm>
+#include <msp/gl/extensions/arb_direct_state_access.h>
+#include <msp/gl/extensions/arb_draw_instanced.h>
+#include <msp/gl/extensions/arb_occlusion_query.h>
+#include <msp/gl/extensions/ext_framebuffer_blit.h>
+#include <msp/gl/extensions/ext_framebuffer_object.h>
+#include <msp/gl/extensions/msp_clear_buffer.h>
+#include "batch.h"
+#include "commands_backend.h"
+#include "error.h"
+#include "framebuffer.h"
+#include "gl.h"
+#include "pipelinestate.h"
+#include "query.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+OpenGLCommands::OpenGLCommands():
+       pipeline_state(0)
+{ }
+
+void OpenGLCommands::use_pipeline(const PipelineState *ps)
+{
+       pipeline_state = ps;
+       if(!pipeline_state)
+               OpenGLPipelineState::clear();
+}
+
+void OpenGLCommands::clear(const ClearValue *values)
+{
+       const Framebuffer *target = pipeline_state->get_framebuffer();
+       if(!target)
+               throw invalid_operation("OpenGLCommands::clear");
+
+       if(!ARB_direct_state_access)
+       {
+               static Require _req(MSP_clear_buffer);
+               pipeline_state->apply();
+       }
+
+       unsigned i = 0;
+       for(FrameAttachment a: target->get_format())
+       {
+               if(get_attach_point(a)==get_attach_point(DEPTH_ATTACHMENT))
+               {
+                       if(ARB_direct_state_access)
+                               glClearNamedFramebufferfv(target->id, GL_DEPTH, 0, &values->depth_stencil.depth);
+                       else
+                               glClearBufferfv(GL_DEPTH, 0, &values->depth_stencil.depth);
+               }
+               else if(get_attach_point(a)==get_attach_point(STENCIL_ATTACHMENT))
+               {
+                       if(ARB_direct_state_access)
+                               glClearNamedFramebufferiv(target->id, GL_STENCIL, 0, &values->depth_stencil.stencil);
+                       else
+                               glClearBufferiv(GL_STENCIL, 0, &values->depth_stencil.stencil);
+               }
+               else
+               {
+                       if(ARB_direct_state_access)
+                               glClearNamedFramebufferfv(target->id, GL_COLOR, i++, &values->color.r);
+                       else
+                               glClearBufferfv(GL_COLOR, i++, &values->color.r);
+               }
+               ++values;
+       }
+}
+
+void OpenGLCommands::draw(const Batch &batch)
+{
+       pipeline_state->apply();
+       void *data_ptr = reinterpret_cast<void *>(batch.get_offset());
+       glDrawElements(batch.gl_prim_type, batch.size(), batch.gl_index_type, data_ptr);
+}
+
+void OpenGLCommands::draw_instanced(const Batch &batch, unsigned count)
+{
+       static Require req(ARB_draw_instanced);
+
+       pipeline_state->apply();
+       void *data_ptr = reinterpret_cast<void *>(batch.get_offset());
+       glDrawElementsInstanced(batch.gl_prim_type, batch.size(), batch.gl_index_type, data_ptr, count);
+}
+
+void OpenGLCommands::resolve_multisample(Framebuffer &target)
+{
+       static Require _req(EXT_framebuffer_blit);
+
+       const Framebuffer *source = pipeline_state->get_framebuffer();
+
+       unsigned width = min(source->get_width(), target.get_width());
+       unsigned height = min(source->get_height(), target.get_height());
+       unsigned buffers = get_gl_buffer_bits(source->get_format())&get_gl_buffer_bits(target.get_format());
+
+       if(ARB_direct_state_access)
+               glBlitNamedFramebuffer(source->id, target.id, 0, 0, width, height, 0, 0, width, height, buffers, GL_NEAREST);
+       else
+       {
+               glBindFramebuffer(GL_READ_FRAMEBUFFER, source->id);
+               glBindFramebuffer(GL_DRAW_FRAMEBUFFER, target.id);
+
+               target.refresh();
+
+               glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, buffers, GL_NEAREST);
+
+               glBindFramebuffer(GL_FRAMEBUFFER, source->id);
+       }
+}
+
+void OpenGLCommands::begin_query(const QueryPool &pool, unsigned index)
+{
+       if(index>=pool.queries.size())
+               throw out_of_range("OpenGLCommands::begin_query");
+       glBeginQuery(pool.gl_type, pool.queries[index]);
+}
+
+void OpenGLCommands::end_query(const QueryPool &pool, unsigned)
+{
+       glEndQuery(pool.gl_type);
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/backends/opengl/commands_backend.h b/source/backends/opengl/commands_backend.h
new file mode 100644 (file)
index 0000000..f54b27a
--- /dev/null
@@ -0,0 +1,36 @@
+#ifndef MSP_GL_COMMANDS_BACKEND_H_
+#define MSP_GL_COMMANDS_BACKEND_H_
+
+namespace Msp {
+namespace GL {
+
+class Batch;
+union ClearValue;
+class Framebuffer;
+class PipelineState;
+class QueryPool;
+
+class OpenGLCommands
+{
+protected:
+       const PipelineState *pipeline_state;
+
+       OpenGLCommands();
+
+       void use_pipeline(const PipelineState *);
+       void clear(const ClearValue *);
+       void draw(const Batch &);
+       void draw_instanced(const Batch &, unsigned);
+       void resolve_multisample(Framebuffer &);
+
+       void begin_query(const QueryPool &, unsigned);
+       void end_query(const QueryPool &, unsigned);
+};
+
+using CommandsBackend = OpenGLCommands;
+
+} // namespace GL
+} // namespace Msp
+
+#endif
+
diff --git a/source/backends/opengl/datatype_backend.cpp b/source/backends/opengl/datatype_backend.cpp
new file mode 100644 (file)
index 0000000..9fd986e
--- /dev/null
@@ -0,0 +1,119 @@
+#include <algorithm>
+#include <stdexcept>
+#include <msp/gl/extensions/arb_gpu_shader_fp64.h>
+#include <msp/gl/extensions/nv_half_float.h>
+#include <msp/gl/extensions/nv_non_square_matrices.h>
+#include "datatype.h"
+#include "datatype_backend.h"
+#include "gl.h"
+
+using namespace std;
+
+namespace {
+
+struct MappedType
+{
+       Msp::GL::DataType type;
+       GLenum gl_type;
+};
+
+// Make sure this is sorted!
+const MappedType type_map[] =
+{
+       { Msp::GL::UNSIGNED_BYTE, GL_UNSIGNED_BYTE },
+       { Msp::GL::UNSIGNED_SHORT, GL_UNSIGNED_SHORT },
+       { Msp::GL::UNSIGNED_INT, GL_UNSIGNED_INT },
+       { Msp::GL::BYTE, GL_BYTE },
+       { Msp::GL::SHORT, GL_SHORT },
+       { Msp::GL::INT, GL_INT },
+       { Msp::GL::HALF_FLOAT, GL_HALF_FLOAT },
+       { Msp::GL::FLOAT, GL_FLOAT },
+       { Msp::GL::DOUBLE, GL_DOUBLE },
+       { Msp::GL::BOOL, GL_BOOL },
+       { Msp::GL::INT_VEC2, GL_INT_VEC2 },
+       { Msp::GL::FLOAT_VEC2, GL_FLOAT_VEC2 },
+       { Msp::GL::BOOL_VEC2, GL_BOOL_VEC2 },
+       { Msp::GL::INT_VEC3, GL_INT_VEC3 },
+       { Msp::GL::FLOAT_VEC3, GL_FLOAT_VEC3 },
+       { Msp::GL::BOOL_VEC3, GL_BOOL_VEC3 },
+       { Msp::GL::INT_VEC4, GL_INT_VEC4 },
+       { Msp::GL::FLOAT_VEC4, GL_FLOAT_VEC4 },
+       { Msp::GL::BOOL_VEC4, GL_BOOL_VEC4 },
+       { Msp::GL::FLOAT_MAT2, GL_FLOAT_MAT2 },
+       { Msp::GL::DOUBLE_MAT2, GL_DOUBLE_MAT2 },
+       { Msp::GL::FLOAT_MAT3, GL_FLOAT_MAT3 },
+       { Msp::GL::DOUBLE_MAT3, GL_DOUBLE_MAT3 },
+       { Msp::GL::FLOAT_MAT4, GL_FLOAT_MAT4 },
+       { Msp::GL::DOUBLE_MAT4, GL_DOUBLE_MAT4 },
+       { Msp::GL::FLOAT_MAT2x3, GL_FLOAT_MAT2x3 },
+       { Msp::GL::DOUBLE_MAT2x3, GL_DOUBLE_MAT2x3 },
+       { Msp::GL::FLOAT_MAT3x2, GL_FLOAT_MAT3x2 },
+       { Msp::GL::DOUBLE_MAT3x2, GL_DOUBLE_MAT3x2 },
+       { Msp::GL::FLOAT_MAT2x4, GL_FLOAT_MAT2x4 },
+       { Msp::GL::DOUBLE_MAT2x4, GL_DOUBLE_MAT2x4 },
+       { Msp::GL::FLOAT_MAT4x2, GL_FLOAT_MAT4x2 },
+       { Msp::GL::DOUBLE_MAT4x2, GL_DOUBLE_MAT4x2 },
+       { Msp::GL::FLOAT_MAT3x4, GL_FLOAT_MAT3x4 },
+       { Msp::GL::DOUBLE_MAT3x4, GL_DOUBLE_MAT3x4 },
+       { Msp::GL::FLOAT_MAT4x3, GL_FLOAT_MAT4x3 },
+       { Msp::GL::DOUBLE_MAT4x3, GL_DOUBLE_MAT4x3 },
+       { Msp::GL::IMAGE_1D, GL_IMAGE_1D },
+       { Msp::GL::IMAGE_2D, GL_IMAGE_2D },
+       { Msp::GL::IMAGE_3D, GL_IMAGE_3D },
+       { Msp::GL::IMAGE_CUBE, GL_IMAGE_CUBE },
+       { Msp::GL::IMAGE_1D_ARRAY, GL_IMAGE_1D_ARRAY },
+       { Msp::GL::IMAGE_2D_ARRAY, GL_IMAGE_2D_ARRAY },
+       { Msp::GL::IMAGE_CUBE_ARRAY, GL_IMAGE_CUBE_MAP_ARRAY },
+       { Msp::GL::SAMPLER_1D, GL_SAMPLER_1D },
+       { Msp::GL::SAMPLER_2D, GL_SAMPLER_2D },
+       { Msp::GL::SAMPLER_3D, GL_SAMPLER_3D },
+       { Msp::GL::SAMPLER_CUBE, GL_SAMPLER_CUBE },
+       { Msp::GL::SAMPLER_1D_ARRAY, GL_SAMPLER_1D_ARRAY },
+       { Msp::GL::SAMPLER_2D_ARRAY, GL_SAMPLER_2D_ARRAY },
+       { Msp::GL::SAMPLER_CUBE_ARRAY, GL_SAMPLER_CUBE_MAP_ARRAY },
+       { Msp::GL::SAMPLER_1D_SHADOW, GL_SAMPLER_1D_SHADOW },
+       { Msp::GL::SAMPLER_2D_SHADOW, GL_SAMPLER_2D_SHADOW },
+       { Msp::GL::SAMPLER_CUBE_SHADOW, GL_SAMPLER_CUBE_SHADOW },
+       { Msp::GL::SAMPLER_1D_ARRAY_SHADOW, GL_SAMPLER_1D_ARRAY_SHADOW },
+       { Msp::GL::SAMPLER_2D_ARRAY_SHADOW, GL_SAMPLER_2D_ARRAY_SHADOW },
+       { Msp::GL::SAMPLER_CUBE_ARRAY_SHADOW, GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW }
+};
+const unsigned type_map_size = sizeof(type_map)/sizeof(MappedType);
+
+bool type_compare(const MappedType &mt, Msp::GL::DataType t)
+{ return mt.type<t; }
+
+}
+
+namespace Msp {
+namespace GL {
+
+unsigned get_gl_type(DataType type)
+{
+       const MappedType *end = type_map+type_map_size;
+       const MappedType *ptr = lower_bound(type_map, end, type, type_compare);
+       if(ptr==end || ptr->type!=type)
+               throw invalid_argument("get_gl_type");
+       return ptr->gl_type;
+}
+
+DataType from_gl_type(unsigned gl_type)
+{
+       for(unsigned i=0; i<type_map_size; ++i)
+               if(type_map[i].gl_type==gl_type)
+                       return type_map[i].type;
+       throw invalid_argument("from_gl_type");
+}
+
+void require_type(DataType type)
+{
+       unsigned rows = ((type>>12)&3)+1;
+       unsigned cols = ((type>>14)&4)+1;
+       if(rows>1 && cols>1 && rows!=cols)
+               static Require _req(NV_non_square_matrices);
+       if((type&0x200) && get_type_size(type)/(rows*cols)==8)
+               static Require _req(ARB_gpu_shader_fp64);
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/backends/opengl/datatype_backend.h b/source/backends/opengl/datatype_backend.h
new file mode 100644 (file)
index 0000000..fae3722
--- /dev/null
@@ -0,0 +1,17 @@
+#ifndef MSP_GL_DATATYPE_BACKEND_H_
+#define MSP_GL_DATATYPE_BACKEND_H_
+
+#ifndef MSP_GL_DATATYPE_H_
+#error "datatype_backend.h requires datatype.h"
+#endif
+
+namespace Msp {
+namespace GL {
+
+unsigned get_gl_type(DataType);
+DataType from_gl_type(unsigned);
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/backends/opengl/deviceinfo_backend.cpp b/source/backends/opengl/deviceinfo_backend.cpp
new file mode 100644 (file)
index 0000000..7e1f3f4
--- /dev/null
@@ -0,0 +1,48 @@
+#include <msp/gl/extensions/arb_enhanced_layouts.h>
+#include <msp/gl/extensions/arb_explicit_attrib_location.h>
+#include <msp/gl/extensions/arb_explicit_uniform_location.h>
+#include <msp/gl/extensions/arb_gpu_shader5.h>
+#include <msp/gl/extensions/arb_separate_shader_objects.h>
+#include <msp/gl/extensions/arb_uniform_buffer_object.h>
+#include <msp/gl/extensions/arb_vertex_shader.h>
+#include <msp/gl/extensions/ext_framebuffer_multisample.h>
+#include <msp/gl/extensions/ext_gpu_shader4.h>
+#include <msp/gl/extensions/ext_texture_array.h>
+#include <msp/gl/extensions/msp_clipping.h>
+#include <msp/gl/extensions/nv_fbo_color_attachments.h>
+#include "deviceinfo.h"
+#include "gl.h"
+
+namespace Msp {
+namespace GL {
+
+Limits::Limits()
+{
+       glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, reinterpret_cast<int *>(&max_vertex_attributes));
+       glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, reinterpret_cast<int *>(&max_texture_bindings));
+       glGetIntegerv(GL_MAX_UNIFORM_BUFFER_BINDINGS, reinterpret_cast<int *>(&max_uniform_bindings));
+       glGetIntegerv(GL_MAX_CLIP_PLANES, reinterpret_cast<int *>(&max_clip_planes));
+       glGetIntegerv(GL_MAX_SAMPLES, reinterpret_cast<int *>(&max_samples));
+       glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, reinterpret_cast<int *>(&uniform_buffer_alignment));
+       glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, reinterpret_cast<int *>(&max_color_attachments));
+}
+
+
+DeviceInfo::DeviceInfo()
+{
+       glsl_features.target_api = get_backend_api();
+       glsl_features.glsl_version = get_glsl_version();
+       glsl_features.arb_enhanced_layouts = ARB_enhanced_layouts;
+       glsl_features.arb_explicit_attrib_location = ARB_explicit_attrib_location;
+       glsl_features.arb_explicit_uniform_location = ARB_explicit_uniform_location;
+       glsl_features.arb_gpu_shader5 = ARB_gpu_shader5;
+       glsl_features.arb_separate_shader_objects = ARB_separate_shader_objects;
+       glsl_features.arb_uniform_buffer_object = ARB_uniform_buffer_object;
+       glsl_features.ext_gpu_shader4 = EXT_gpu_shader4;
+       glsl_features.ext_texture_array = EXT_texture_array;
+       glsl_features.uniform_binding_range = limits.max_uniform_bindings;
+       glsl_features.texture_binding_range = limits.max_texture_bindings;
+}
+
+} // namespace Msp
+} // namespace GL
diff --git a/source/backends/opengl/extension.cpp b/source/backends/opengl/extension.cpp
new file mode 100644 (file)
index 0000000..a55da9b
--- /dev/null
@@ -0,0 +1,195 @@
+#include <set>
+#include <cstdlib>
+#if defined(__ANDROID__)
+#include <EGL/egl.h>
+#elif defined(_WIN32)
+#include <windows.h>
+#elif !defined(__APPLE__)
+#define GLX_GLXEXT_PROTOTYPES
+#include <GL/glx.h>
+#endif
+#include <msp/strings/format.h>
+#include <msp/strings/utils.h>
+#include "error.h"
+#include "extension.h"
+#include "gl.h"
+
+#ifndef GL_VERSION_3_0
+#define GL_NUM_EXTENSIONS 0x821D
+#endif
+
+#ifndef GL_VERSION_3_2
+#define GL_CONTEXT_CORE_PROFILE_BIT 0x00000001
+#define GL_CONTEXT_PROFILE_MASK 0x9126
+#endif
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+Extension::Extension(const char *n, InitFunc f):
+       name(n),
+       init_func(f),
+       init_done(false),
+       support(UNSUPPORTED)
+{ }
+
+Extension::operator bool() const
+{
+       if(!init_done)
+       {
+               support = init_func();
+               init_done = true;
+       }
+
+       return support>UNSUPPORTED;
+}
+
+
+Require::Require(const Extension &ext)
+{
+       if(!ext)
+               throw unsupported_extension(ext.get_name());
+}
+
+
+bool is_supported(const string &ext)
+{
+       if(is_disabled(ext))
+               return false;
+
+       static set<string> extensions;
+       static bool init_done = false;
+
+       if(!init_done)
+       {
+               if(get_backend_api()==OPENGL && get_backend_version()>=Version(3, 0))
+               {
+                       typedef GLubyte *(APIENTRY *FPtr_glGetStringi)(GLenum, GLuint);
+                       FPtr_glGetStringi glGetStringi = reinterpret_cast<FPtr_glGetStringi>(get_proc_address("glGetStringi"));
+                       int n_extensions;
+                       glGetIntegerv(GL_NUM_EXTENSIONS, &n_extensions);
+                       for(int i=0; i<n_extensions; ++i)
+                               extensions.insert(reinterpret_cast<const char *>(glGetStringi(GL_EXTENSIONS, i)));
+               }
+               else
+               {
+                       if(const char *gl_ext = reinterpret_cast<const char *>(glGetString(GL_EXTENSIONS)))
+                       {
+                               vector<string> exts = split(gl_ext);
+                               extensions.insert(exts.begin(), exts.end());
+                       }
+               }
+
+               init_done = true;
+       }
+
+       return extensions.count(ext);
+}
+
+bool is_supported(const Version &core_version, const Version &deprecated_version)
+{
+       const Version &version = get_backend_version();
+       if(deprecated_version && version>=deprecated_version && get_gl_profile()==CORE_PROFILE)
+               return false;
+       return (version>=core_version);
+}
+
+bool is_disabled(const string &ext)
+{
+       static set<string> disabled_exts;
+       static bool init_done = false;
+
+       if(!init_done)
+       {
+               if(const char *disable_ptr = getenv("MSPGL_DISABLE_EXTENSIONS"))
+               {
+                       vector<string> disable = split(disable_ptr);
+                       disabled_exts.insert(disable.begin(), disable.end());
+               }
+
+               if(const char *renderer_ptr = reinterpret_cast<const char *>(glGetString(GL_RENDERER)))
+               {
+                       string renderer = renderer_ptr;
+                       if(renderer.find("Radeon")!=string::npos || renderer.find("AMD")!=string::npos)
+                       {
+                               // The core primitive restart feature does not work either.
+                               disabled_exts.insert("GL_MSP_primitive_restart");
+
+                               /* AMD's uniform buffer objects only work with the core version of
+                               shaders. */
+                               if(get_backend_version()<Version(2, 0))
+                                       disabled_exts.insert("GL_ARB_uniform_buffer_object");
+                       }
+               }
+
+               init_done = true;
+       }
+
+       return disabled_exts.count(ext);
+}
+
+inline GLProfile _get_gl_profile()
+{
+       if(get_backend_api()==OPENGL && get_backend_version()>=Version(3, 0))
+       {
+               int mask;
+               glGetIntegerv(GL_CONTEXT_PROFILE_MASK, &mask);
+               if(mask==GL_CONTEXT_CORE_PROFILE_BIT)
+                       return CORE_PROFILE;
+       }
+
+       return COMPATIBILITY_PROFILE;
+}
+
+GLProfile get_gl_profile()
+{
+       static GLProfile profile = _get_gl_profile();
+       return profile;
+}
+
+inline Version _get_glsl_version()
+{
+       const char *glsl_ver_ptr = reinterpret_cast<const char *>(glGetString(GL_SHADING_LANGUAGE_VERSION));
+       if(!glsl_ver_ptr)
+               throw runtime_error("GLSL version not available");
+
+       string glsl_ver = glsl_ver_ptr;
+       if(!glsl_ver.compare(0, 18, "OpenGL ES GLSL ES "))
+               glsl_ver.erase(0, 18);
+
+       Version ver(glsl_ver.substr(0, glsl_ver.find(' ')));
+
+       if(const char *force_ver_ptr = getenv("MSPGL_FORCE_GLSL_VERSION"))
+       {
+               Version force_ver(force_ver_ptr);
+               if(force_ver<ver)
+                       ver = force_ver;
+       }
+
+       return ver;
+}
+
+const Version &get_glsl_version()
+{
+       static Version version = _get_glsl_version();
+       return version;
+}
+
+ExtFunc *get_proc_address(const string &name)
+{
+#if defined(_WIN32)
+       return reinterpret_cast<ExtFunc *>(wglGetProcAddress(name.c_str()));
+#elif defined(__APPLE__)
+       (void)name;
+       return 0;  // Not supported
+#elif defined(__ANDROID__)
+       return eglGetProcAddress(name.c_str());
+#else
+       return glXGetProcAddressARB(reinterpret_cast<const unsigned char *>(name.c_str()));
+#endif
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/backends/opengl/extension.h b/source/backends/opengl/extension.h
new file mode 100644 (file)
index 0000000..7799635
--- /dev/null
@@ -0,0 +1,79 @@
+#ifndef MSP_GL_EXTENSION_H_
+#define MSP_GL_EXTENSION_H_
+
+#include <string>
+#include "backend.h"
+
+namespace Msp {
+namespace GL {
+
+enum GLProfile
+{
+       CORE_PROFILE,
+       COMPATIBILITY_PROFILE
+};
+
+
+/**
+Holds metadata about an extension.  Evaluates to true if the extension is
+supported.
+*/
+class Extension
+{
+public:
+       enum SupportLevel
+       {
+               UNSUPPORTED,
+               EXTENSION,
+               CORE
+       };
+
+       typedef SupportLevel (*InitFunc)();
+
+private:
+       const char *name;
+       InitFunc init_func;
+       mutable bool init_done;
+       mutable SupportLevel support;
+
+public:
+       Extension(const char *, InitFunc);
+
+       const char *get_name() const { return name; }
+       operator bool() const;
+};
+
+
+struct Require
+{
+       Require(const Extension &);
+};
+
+
+typedef void ExtFunc();
+
+/** Checks for extension support.  Only intended for internal use. */
+bool is_supported(const std::string &);
+
+/** Checks for OpenGL version support.  Only intended for internal use. */
+bool is_supported(const Version &, const Version & = Version());
+
+/** Indicates whether an extension has been disabled, either explicitly through
+the MSPGL_DISABLE_EXTENSIONS environment variable or implicitly as a workaround
+for a driver bug.  Only intended for internal use. */
+bool is_disabled(const std::string &);
+
+/** Returns the OpenGL profile for the active context. */
+GLProfile get_gl_profile();
+
+/** Returns the GLSL version number, as reported by the implementation. */
+const Version &get_glsl_version();
+
+/** Returns the address of an extension function.  Only indended for internal
+use. */
+ExtFunc *get_proc_address(const std::string &);
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/backends/opengl/framebuffer_backend.cpp b/source/backends/opengl/framebuffer_backend.cpp
new file mode 100644 (file)
index 0000000..1adcb7f
--- /dev/null
@@ -0,0 +1,192 @@
+#include <msp/gl/extensions/arb_draw_buffers.h>
+#include <msp/gl/extensions/arb_direct_state_access.h>
+#include <msp/gl/extensions/arb_geometry_shader4.h>
+#include <msp/gl/extensions/arb_internalformat_query.h>
+#include <msp/gl/extensions/arb_internalformat_query2.h>
+#include <msp/gl/extensions/ext_framebuffer_object.h>
+#include <msp/gl/extensions/ext_texture_array.h>
+#include <msp/gl/extensions/ext_texture3d.h>
+#include <msp/gl/extensions/msp_buffer_control.h>
+#include <msp/gl/extensions/khr_debug.h>
+#include <msp/strings/format.h>
+#include "framebuffer.h"
+#include "framebuffer_backend.h"
+#include "gl.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+OpenGLFramebuffer::OpenGLFramebuffer(bool is_system):
+       id(0),
+       status(is_system ? GL_FRAMEBUFFER_COMPLETE : GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT)
+{
+       if(!is_system)
+       {
+               static Require _req(EXT_framebuffer_object);
+
+               if(ARB_direct_state_access)
+                       glCreateFramebuffers(1, &id);
+               else
+                       glGenFramebuffers(1, &id);
+       }
+}
+
+OpenGLFramebuffer::~OpenGLFramebuffer()
+{
+       if(id)
+               glDeleteFramebuffers(1, &id);
+}
+
+FrameFormat OpenGLFramebuffer::get_system_format()
+{
+       FrameFormat format;
+
+       if(EXT_framebuffer_object)
+       {
+               int value;
+               glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_BACK, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &value);
+               if(value==GL_NONE)
+                       glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_FRONT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &value);
+               if(value!=GL_NONE)
+                       format = (format,COLOR_ATTACHMENT);
+
+               glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_DEPTH, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &value);
+               if(value!=GL_NONE)
+                       format = (format,DEPTH_ATTACHMENT);
+
+               glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_STENCIL, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &value);
+               if(value!=GL_NONE)
+                       format = (format,STENCIL_ATTACHMENT);
+       }
+
+       return format;
+}
+
+void OpenGLFramebuffer::get_system_size(unsigned &width, unsigned &height)
+{
+       int view[4];
+       glGetIntegerv(GL_VIEWPORT, view);
+       width = view[2];
+       height = view[3];
+}
+
+bool OpenGLFramebuffer::is_format_supported(const FrameFormat &fmt)
+{
+       // Pretend everything is supported if we can't check
+       if(!ARB_internalformat_query || !ARB_internalformat_query2)
+               return true;
+
+       unsigned target = (fmt.get_samples()>1 ? GL_TEXTURE_2D_MULTISAMPLE : GL_TEXTURE_2D);
+       for(FrameAttachment a: fmt)
+       {
+               unsigned pf = get_gl_pixelformat(get_attachment_pixelformat(a));
+               int supported = 0;
+               glGetInternalformativ(target, pf, GL_FRAMEBUFFER_RENDERABLE, 1, &supported);
+               if(supported!=GL_FULL_SUPPORT)
+                       return false;
+       }
+
+       return true;
+}
+
+void OpenGLFramebuffer::require_layered()
+{
+       static Require _req(ARB_geometry_shader4);
+}
+
+void OpenGLFramebuffer::update(unsigned mask) const
+{
+       const FrameFormat &format = static_cast<const Framebuffer *>(this)->format;
+       vector<GLenum> color_bufs;
+       color_bufs.reserve(format.size());
+       unsigned i = 0;
+       for(FrameAttachment a: format)
+       {
+               GLenum gl_attach_point = get_gl_attachment(a);
+               if(mask&(1<<i))
+               {
+                       const Framebuffer::Attachment &attch = static_cast<const Framebuffer *>(this)->attachments[i];
+                       if(attch.tex)
+                       {
+                               if(ARB_direct_state_access)
+                               {
+                                       if(attch.tex->target==GL_TEXTURE_2D || attch.tex->target==GL_TEXTURE_2D_MULTISAMPLE || attch.layer<0)
+                                               glNamedFramebufferTexture(id, gl_attach_point, attch.tex->id, attch.level);
+                                       else
+                                               glNamedFramebufferTextureLayer(id, gl_attach_point, attch.tex->id, attch.level, attch.layer);
+                               }
+                               else if(attch.tex->target==GL_TEXTURE_2D || attch.tex->target==GL_TEXTURE_2D_MULTISAMPLE)
+                                       glFramebufferTexture2D(GL_FRAMEBUFFER, gl_attach_point, attch.tex->target, attch.tex->id, attch.level);
+                               else if(attch.layer<0)
+                                       glFramebufferTexture(GL_FRAMEBUFFER, gl_attach_point, attch.tex->id, attch.level);
+                               else if(attch.tex->target==GL_TEXTURE_2D_ARRAY)
+                                       glFramebufferTextureLayer(GL_FRAMEBUFFER, gl_attach_point, attch.tex->id, attch.level, attch.layer);
+                               else if(attch.tex->target==GL_TEXTURE_3D)
+                                       glFramebufferTexture3D(GL_FRAMEBUFFER, gl_attach_point, attch.tex->target, attch.tex->id, attch.level, attch.layer);
+                               else if(attch.tex->target==GL_TEXTURE_CUBE_MAP)
+                                       glFramebufferTexture2D(GL_FRAMEBUFFER, gl_attach_point, get_gl_cube_face(static_cast<TextureCubeFace>(attch.layer)), attch.tex->id, attch.level);
+                       }
+                       else if(ARB_direct_state_access)
+                               glNamedFramebufferTexture(id, gl_attach_point, 0, 0);
+                       else
+                               glFramebufferTexture2D(GL_FRAMEBUFFER, gl_attach_point, GL_TEXTURE_2D, 0, 0);
+               }
+
+               if(gl_attach_point!=GL_DEPTH_ATTACHMENT && gl_attach_point!=GL_STENCIL_ATTACHMENT)
+                       color_bufs.push_back(gl_attach_point);
+
+               ++i;
+       }
+
+       if(color_bufs.size()>1)
+               static Require _req(ARB_draw_buffers);
+
+       GLenum first_buffer = (color_bufs.empty() ? GL_NONE : color_bufs.front());
+       if(ARB_direct_state_access)
+       {
+               /* ARB_direct_state_access ties the availability of these functions to
+               framebuffers themselves, so no further checks are needed. */
+               glNamedFramebufferDrawBuffers(id, color_bufs.size(), &color_bufs[0]);
+               glNamedFramebufferReadBuffer(id, first_buffer);
+       }
+       else
+       {
+               if(ARB_draw_buffers)
+                       glDrawBuffers(color_bufs.size(), &color_bufs[0]);
+               else if(MSP_buffer_control)
+                       glDrawBuffer(first_buffer);
+
+               if(MSP_buffer_control)
+                       glReadBuffer(first_buffer);
+       }
+
+       if(ARB_direct_state_access)
+               status = glCheckNamedFramebufferStatus(id, GL_FRAMEBUFFER);
+       else
+               status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+}
+
+void OpenGLFramebuffer::require_complete() const
+{
+       if(status==GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT)
+               throw framebuffer_incomplete("incomplete or unsupported attachment");
+       if(status==GL_FRAMEBUFFER_UNSUPPORTED)
+               throw framebuffer_incomplete("unsupported configuration");
+       if(status!=GL_FRAMEBUFFER_COMPLETE)
+               throw framebuffer_incomplete(Msp::format("incomplete (%#x)", status));
+}
+
+void OpenGLFramebuffer::set_debug_name(const string &name)
+{
+#ifdef DEBUG
+       if(KHR_debug)
+               glObjectLabel(GL_FRAMEBUFFER, id, name.size(), name.c_str());
+#else
+       (void)name;
+#endif
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/backends/opengl/framebuffer_backend.h b/source/backends/opengl/framebuffer_backend.h
new file mode 100644 (file)
index 0000000..84f6edd
--- /dev/null
@@ -0,0 +1,38 @@
+#ifndef MSP_GL_FRAMEBUFFER_BACKEND_H_
+#define MSP_GL_FRAMEBUFFER_BACKEND_H_
+
+#include <string>
+#include "frameformat.h"
+
+namespace Msp {
+namespace GL {
+
+class OpenGLFramebuffer
+{
+       friend class OpenGLCommands;
+       friend class OpenGLPipelineState;
+
+protected:
+       unsigned id;
+       mutable unsigned status;
+
+       OpenGLFramebuffer(bool);
+       ~OpenGLFramebuffer();
+
+       static FrameFormat get_system_format();
+       static void get_system_size(unsigned &, unsigned &);
+       static bool is_format_supported(const FrameFormat &);
+       static void require_layered();
+
+       void update(unsigned) const;
+       void require_complete() const;
+
+       void set_debug_name(const std::string &);
+};
+
+using FramebufferBackend = OpenGLFramebuffer;
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/backends/opengl/frameformat_backend.cpp b/source/backends/opengl/frameformat_backend.cpp
new file mode 100644 (file)
index 0000000..2342d7e
--- /dev/null
@@ -0,0 +1,35 @@
+#include <msp/gl/extensions/ext_framebuffer_object.h>
+#include "frameformat.h"
+#include "frameformat_backend.h"
+#include "gl.h"
+
+namespace Msp {
+namespace GL {
+
+unsigned get_gl_attachment(FrameAttachment fa)
+{
+       if(get_attach_point(fa)==get_attach_point(DEPTH_ATTACHMENT))
+               return GL_DEPTH_ATTACHMENT;
+       else if(get_attach_point(fa)==get_attach_point(STENCIL_ATTACHMENT))
+               return GL_STENCIL_ATTACHMENT;
+       else
+               return GL_COLOR_ATTACHMENT0+get_attach_point(fa);
+}
+
+unsigned get_gl_buffer_bits(const FrameFormat &format)
+{
+       unsigned bits = 0;
+       for(FrameAttachment a: format)
+       {
+               if(get_attach_point(a)==get_attach_point(DEPTH_ATTACHMENT))
+                       bits |= GL_DEPTH_BUFFER_BIT;
+               else if(get_attach_point(a)==get_attach_point(STENCIL_ATTACHMENT))
+                       bits |= GL_STENCIL_BUFFER_BIT;
+               else
+                       bits |= GL_COLOR_BUFFER_BIT;
+       }
+       return bits;
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/backends/opengl/frameformat_backend.h b/source/backends/opengl/frameformat_backend.h
new file mode 100644 (file)
index 0000000..a0197f5
--- /dev/null
@@ -0,0 +1,17 @@
+#ifndef MSP_GL_FRAMEFORMAT_BACKEND_H_
+#define MSP_GL_FRAMEFORMAT_BACKEND_H_
+
+#ifndef MSP_GL_FRAMEFORMAT_H_
+#error "frameformat_backend.h requires frameformat.h"
+#endif
+
+namespace Msp {
+namespace GL {
+
+unsigned get_gl_attachment(FrameAttachment);
+unsigned get_gl_buffer_bits(const FrameFormat &);
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/backends/opengl/gl.h b/source/backends/opengl/gl.h
new file mode 100644 (file)
index 0000000..f27b63f
--- /dev/null
@@ -0,0 +1,44 @@
+#ifndef MSP_GL_GL_H_
+#define MSP_GL_GL_H_
+
+#ifdef __APPLE__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wkeyword-macro"
+#define extern extern __attribute__((weak_import))
+#include <OpenGL/gl.h>
+#include <OpenGL/glext.h>
+#undef extern
+#pragma clang diagnostic pop
+#elif defined(__ANDROID__)
+#include <GLES3/gl3.h>
+#include <GLES3/gl3ext.h>
+typedef double GLdouble;
+typedef long long GLint64;
+#else
+#ifdef _WIN32
+#ifndef WINAPI
+#if defined(_ARM_)
+#define WINAPI
+#else
+#define WINAPI __stdcall
+#endif
+#endif
+#ifndef APIENTRY
+#define APIENTRY WINAPI
+#endif
+#ifndef DECLSPEC_IMPORT
+#define DECLSPEC_IMPORT __declspec(dllimport)
+#endif
+#ifndef WINGDIAPI
+#define WINGDIAPI DECLSPEC_IMPORT
+#endif
+#endif
+#include <GL/gl.h>
+#include <GL/glext.h>
+#endif
+
+#ifndef APIENTRY
+#define APIENTRY
+#endif
+
+#endif
diff --git a/source/backends/opengl/pipelinestate_backend.cpp b/source/backends/opengl/pipelinestate_backend.cpp
new file mode 100644 (file)
index 0000000..738de3a
--- /dev/null
@@ -0,0 +1,282 @@
+#include <msp/gl/extensions/arb_direct_state_access.h>
+#include <msp/gl/extensions/arb_sampler_objects.h>
+#include <msp/gl/extensions/arb_shader_objects.h>
+#include <msp/gl/extensions/arb_uniform_buffer_object.h>
+#include <msp/gl/extensions/arb_vertex_array_object.h>
+#include <msp/gl/extensions/ext_framebuffer_object.h>
+#include <msp/gl/extensions/msp_primitive_restart.h>
+#include "blend.h"
+#include "buffer.h"
+#include "depthtest.h"
+#include "deviceinfo.h"
+#include "framebuffer.h"
+#include "gl.h"
+#include "pipelinestate.h"
+#include "pipelinestate_backend.h"
+#include "program.h"
+#include "rect.h"
+#include "sampler.h"
+#include "stenciltest.h"
+#include "texture.h"
+#include "uniformblock.h"
+#include "vertexsetup.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+const OpenGLPipelineState *OpenGLPipelineState::last_applied = 0;
+vector<int> OpenGLPipelineState::bound_tex_targets;
+vector<char> OpenGLPipelineState::bound_uniform_blocks;
+unsigned OpenGLPipelineState::restart_index = 0;
+
+OpenGLPipelineState::OpenGLPipelineState()
+{
+       if(bound_tex_targets.empty())
+               bound_tex_targets.resize(DeviceInfo::get_global().limits.max_texture_bindings);
+       if(bound_uniform_blocks.empty())
+               bound_uniform_blocks.resize(DeviceInfo::get_global().limits.max_uniform_bindings);
+}
+
+OpenGLPipelineState::~OpenGLPipelineState()
+{
+       if(this==last_applied)
+               last_applied = 0;
+}
+
+void OpenGLPipelineState::apply() const
+{
+       if(!last_applied)
+               OpenGLTexture::unbind_scratch();
+
+       apply(this==last_applied ? static_cast<const PipelineState *>(this)->changes : ~0U);
+}
+
+void OpenGLPipelineState::apply(unsigned mask) const
+{
+       const PipelineState *self = static_cast<const PipelineState *>(this);
+
+       if(mask&PipelineState::FRAMEBUFFER)
+       {
+               const Framebuffer *framebuffer = self->framebuffer;
+               glBindFramebuffer(GL_FRAMEBUFFER, framebuffer ? framebuffer->id : 0);
+               if(framebuffer)
+               {
+                       framebuffer->refresh();
+                       framebuffer->require_complete();
+               }
+       }
+
+       if(mask&(PipelineState::VIEWPORT|PipelineState::FRAMEBUFFER))
+       {
+               if(const Rect *viewport = self->viewport)
+                       glViewport(viewport->left, viewport->bottom, viewport->width, viewport->height);
+               else if(const Framebuffer *framebuffer = self->framebuffer)
+                       glViewport(0, 0, framebuffer->get_width(), framebuffer->get_height());
+       }
+
+       if(mask&PipelineState::SCISSOR)
+       {
+               if(const Rect *scissor = self->scissor)
+               {
+                       glEnable(GL_SCISSOR_TEST);
+                       glScissor(scissor->left, scissor->bottom, scissor->width, scissor->height);
+               }
+               else
+                       glDisable(GL_SCISSOR_TEST);
+       }
+
+       if(mask&PipelineState::SHPROG)
+               glUseProgram(self->shprog ? self->shprog->id : 0);
+
+       if(mask&PipelineState::VERTEX_SETUP)
+       {
+               const VertexSetup *vertex_setup = self->vertex_setup;
+               glBindVertexArray(vertex_setup ? vertex_setup->id : 0);
+               if(vertex_setup)
+               {
+                       static Require _req(MSP_primitive_restart);
+
+                       vertex_setup->refresh();
+                       unsigned ri = (vertex_setup->get_index_type()==UNSIGNED_INT ? 0xFFFFFFFF : 0xFFFF);
+                       if(ri!=restart_index)
+                       {
+                               if(!restart_index)
+                                       glEnable(GL_PRIMITIVE_RESTART);
+                               glPrimitiveRestartIndex(ri);
+                               restart_index = ri;
+                       }
+               }
+       }
+
+       if(mask&PipelineState::FACE_CULL)
+       {
+               glFrontFace(self->front_face==CLOCKWISE ? GL_CW : GL_CCW);
+
+               if(self->face_cull!=NO_CULL && self->front_face!=NON_MANIFOLD)
+               {
+                       glEnable(GL_CULL_FACE);
+                       glCullFace(self->face_cull==CULL_FRONT ? GL_FRONT : GL_BACK);
+               }
+               else
+                       glDisable(GL_CULL_FACE);
+       }
+
+       if(mask&PipelineState::CLIP_PLANES)
+       {
+               unsigned max_clip_planes = DeviceInfo::get_global().limits.max_clip_planes;
+               for(unsigned i=0; i<max_clip_planes; ++i)
+               {
+                       if((self->enabled_clip_planes>>i)&1)
+                               glEnable(GL_CLIP_PLANE0+i);
+                       else
+                               glDisable(GL_CLIP_PLANE0+i);
+               }
+       }
+
+       if(mask&PipelineState::TEXTURES)
+       {
+               for(const PipelineState::BoundTexture &t: self->textures)
+                       if(t.changed || mask==~0U)
+                       {
+                               if(t.texture && t.sampler)
+                               {
+                                       if(ARB_direct_state_access)
+                                               glBindTextureUnit(t.binding, t.texture->id);
+                                       else
+                                       {
+                                               glActiveTexture(GL_TEXTURE0+t.binding);
+                                               if(bound_tex_targets[t.binding] && static_cast<int>(t.texture->target)!=bound_tex_targets[t.binding])
+                                                       glBindTexture(bound_tex_targets[t.binding], 0);
+                                               glBindTexture(t.texture->target, t.texture->id);
+                                       }
+
+                                       bound_tex_targets[t.binding] = t.texture->target;
+
+                                       glBindSampler(t.binding, t.sampler->id);
+                                       t.sampler->refresh();
+                               }
+
+                               t.changed = false;
+                       }
+       }
+
+       if(mask&PipelineState::UNIFORMS)
+       {
+               for(const PipelineState::BoundUniformBlock &u: self->uniform_blocks)
+                       if(u.changed || mask==~0U)
+                       {
+                               if(u.block)
+                               {
+                                       if(u.binding>=0)
+                                       {
+                                               glBindBufferRange(GL_UNIFORM_BUFFER, u.binding, u.block->get_buffer()->id, u.block->get_offset(), u.block->get_data_size());
+                                               bound_uniform_blocks[u.binding] = 1;
+                                       }
+                                       else if(self->shprog)
+                                       {
+                                               const char *data = static_cast<const char *>(u.block->get_data_pointer());
+                                               for(const Program::UniformCall &call: self->shprog->uniform_calls)
+                                                       call.func(call.location, call.size, data+call.location*16);
+                                       }
+                               }
+
+                               u.changed = false;
+                       }
+       }
+
+       if(mask&PipelineState::DEPTH_TEST)
+       {
+               const DepthTest *depth_test = self->depth_test;
+               if(depth_test && depth_test->enabled)
+               {
+                       glEnable(GL_DEPTH_TEST);
+                       glDepthFunc(get_gl_predicate(depth_test->compare));
+               }
+               else
+                       glDisable(GL_DEPTH_TEST);
+
+               glDepthMask(!depth_test || depth_test->write);
+       }
+
+       if(mask&PipelineState::STENCIL_TEST)
+       {
+               const StencilTest *stencil_test = self->stencil_test;
+               if(stencil_test && stencil_test->enabled)
+               {
+                       glEnable(GL_STENCIL_TEST);
+                       glStencilFunc(get_gl_predicate(stencil_test->compare), stencil_test->reference, 0xFFFFFFFF);
+                       glStencilOp(get_gl_stencil_op(stencil_test->stencil_fail_op), get_gl_stencil_op(stencil_test->depth_fail_op), get_gl_stencil_op(stencil_test->depth_pass_op));
+               }
+               else
+                       glDisable(GL_STENCIL_TEST);
+       }
+
+       if(mask&PipelineState::BLEND)
+       {
+               const Blend *blend = self->blend;
+               if(blend && blend->enabled)
+               {
+                       glEnable(GL_BLEND);
+                       glBlendEquation(get_gl_blend_equation(blend->equation));
+                       glBlendFunc(get_gl_blend_factor(blend->src_factor), get_gl_blend_factor(blend->dst_factor));
+                       glBlendColor(blend->constant.r, blend->constant.g, blend->constant.b, blend->constant.a);
+                       ColorWriteMask cw = blend->write_mask;
+                       glColorMask((cw&WRITE_RED)!=0, (cw&WRITE_GREEN)!=0, (cw&WRITE_BLUE)!=0, (cw&WRITE_ALPHA)!=0);
+               }
+               else
+               {
+                       glDisable(GL_BLEND);
+                       glColorMask(true, true, true, true);
+               }
+       }
+
+       last_applied = this;
+       self->changes &= ~mask;
+}
+
+void OpenGLPipelineState::clear()
+{
+       if(last_applied)
+       {
+               glUseProgram(0);
+               glBindVertexArray(0);
+
+               unsigned max_clip_planes = DeviceInfo::get_global().limits.max_clip_planes;
+               unsigned enabled_clip_planes = static_cast<const PipelineState *>(last_applied)->enabled_clip_planes;
+               for(unsigned i=0; i<max_clip_planes; ++i)
+                       if((enabled_clip_planes>>i)&1)
+                               glDisable(GL_CLIP_PLANE0+i);
+
+               for(unsigned i=0; i<bound_tex_targets.size(); ++i)
+                       if(bound_tex_targets[i])
+                       {
+                               if(ARB_direct_state_access)
+                                       glBindTextureUnit(i, 0);
+                               else
+                               {
+                                       glActiveTexture(GL_TEXTURE0+i);
+                                       glBindTexture(bound_tex_targets[i], 0);
+                               }
+                               bound_tex_targets[i] = 0;
+                       }
+
+               for(unsigned i=0; i<bound_uniform_blocks.size(); ++i)
+                       if(bound_uniform_blocks[i])
+                       {
+                               glBindBufferBase(GL_UNIFORM_BUFFER, i, 0);
+                               bound_uniform_blocks[i] = 0;
+                       }
+
+               glDisable(GL_DEPTH_TEST);
+               glDepthMask(true);
+               glDisable(GL_STENCIL_TEST);
+               glDisable(GL_BLEND);
+
+               last_applied = 0;
+       }
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/backends/opengl/pipelinestate_backend.h b/source/backends/opengl/pipelinestate_backend.h
new file mode 100644 (file)
index 0000000..24ef698
--- /dev/null
@@ -0,0 +1,35 @@
+#ifndef MSP_GL_PIPELINESTATE_BACKEND_H_
+#define MSP_GL_PIPELINESTATE_BACKEND_H_
+
+#include <vector>
+#include <msp/core/noncopyable.h>
+
+namespace Msp {
+namespace GL {
+
+class OpenGLPipelineState: public NonCopyable
+{
+       friend class OpenGLCommands;
+
+protected:
+       static const OpenGLPipelineState *last_applied;
+       static std::vector<int> bound_tex_targets;
+       static std::vector<char> bound_uniform_blocks;
+       static unsigned restart_index;
+
+       OpenGLPipelineState();
+       ~OpenGLPipelineState();
+
+       void apply() const;
+private:
+       void apply(unsigned) const;
+protected:
+       static void clear();
+};
+
+using PipelineStateBackend = OpenGLPipelineState;
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/backends/opengl/pixelformat_backend.cpp b/source/backends/opengl/pixelformat_backend.cpp
new file mode 100644 (file)
index 0000000..ee1a207
--- /dev/null
@@ -0,0 +1,110 @@
+#include <msp/gl/extensions/arb_depth_buffer_float.h>
+#include <msp/gl/extensions/arb_depth_texture.h>
+#include <msp/gl/extensions/arb_texture_float.h>
+#include <msp/gl/extensions/arb_texture_rg.h>
+#include <msp/gl/extensions/ext_texture_srgb.h>
+#include <msp/gl/extensions/oes_required_internalformat.h>
+#include <msp/gl/extensions/oes_texture_stencil8.h>
+#include "gl.h"
+#include "pixelformat.h"
+#include "pixelformat_backend.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+void require_pixelformat(PixelFormat pf)
+{
+       /* TODO These checks are only accurate for textures.  On OpenGL ES some
+       formats are allowed for render buffers earlier than textures.  In particular
+       it's possible to create a 16-bit depth renderbuffer on OpenGL ES 2.0 but
+       depth textures are only available with 3.0 or the OES_depth_texture
+       extension.*/
+       switch(pf)
+       {
+       case RGB8:
+       case RGBA8:
+               { static Require _req(OES_required_internalformat); }
+               break;
+       case R8:
+       case RG8:
+               { static Require _req(ARB_texture_rg); }
+               break;
+       case R16F:
+       case R32F:
+       case RG16F:
+       case RG32F:
+               { static Require _req(ARB_texture_rg); }
+               { static Require _req(ARB_texture_float); }
+               break;
+       case RGB16F:
+       case RGB32F:
+       case RGBA16F:
+       case RGBA32F:
+               { static Require _req(ARB_texture_float); }
+               break;
+       case SRGB8:
+       case SRGB8_ALPHA8:
+               { static Require _req(EXT_texture_sRGB); }
+               break;
+       case DEPTH_COMPONENT16:
+       case DEPTH_COMPONENT24:
+       case DEPTH_COMPONENT32:
+               { static Require _req(ARB_depth_texture); }
+               { static Require _req(OES_required_internalformat); }
+               break;
+       case DEPTH_COMPONENT32F:
+               { static Require _req(ARB_depth_buffer_float); }
+               break;
+       case STENCIL_INDEX8:
+               { static Require _req(OES_texture_stencil8); }
+               break;
+       default:
+               throw invalid_argument("require_pixelformat");
+       }
+}
+
+GLenum get_gl_components(PixelComponents comp)
+{
+       switch(comp)
+       {
+       case RED: return GL_RED;
+       case RG: return GL_RG;
+       case RGB: return GL_RGB;
+       case RGBA: return GL_RGBA;
+       case DEPTH_COMPONENT: return GL_DEPTH_COMPONENT;
+       case STENCIL_INDEX: return GL_STENCIL_INDEX;
+       default: throw invalid_argument("get_gl_components");
+       }
+}
+
+GLenum get_gl_pixelformat(PixelFormat pf)
+{
+       switch(pf)
+       {
+       case R8: return GL_R8;
+       case R16F: return GL_R16F;
+       case R32F: return GL_R32F;
+       case RG8: return GL_RG8;
+       case RG16F: return GL_RG16F;
+       case RG32F: return GL_RG32F;
+       case RGB8: return GL_RGB8;
+       case RGB16F: return GL_RGB16F;
+       case RGB32F: return GL_RGB32F;
+       case RGBA8: return GL_RGBA8;
+       case RGBA16F: return GL_RGBA16F;
+       case RGBA32F: return GL_RGBA32F;
+       case SRGB8: return GL_SRGB8;
+       case SRGB8_ALPHA8: return GL_SRGB8_ALPHA8;
+       case DEPTH_COMPONENT16: return GL_DEPTH_COMPONENT16;
+       case DEPTH_COMPONENT24: return GL_DEPTH_COMPONENT24;
+       case DEPTH_COMPONENT32: return GL_DEPTH_COMPONENT32;
+       case DEPTH_COMPONENT32F: return GL_DEPTH_COMPONENT32F;
+       case STENCIL_INDEX8: return GL_STENCIL_INDEX8;
+       default: throw invalid_argument("get_gl_pixelformat");
+       }
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/backends/opengl/pixelformat_backend.h b/source/backends/opengl/pixelformat_backend.h
new file mode 100644 (file)
index 0000000..d17bc8b
--- /dev/null
@@ -0,0 +1,17 @@
+#ifndef MSP_GL_PIXELFORMAT_BACKEND_H_
+#define MSP_GL_PIXELFORMAT_BACKEND_H_
+
+#ifndef MSP_GL_PIXELFORMAT_H_
+#error "pixelformat_backend.h requires pixelformat.h"
+#endif
+
+namespace Msp {
+namespace GL {
+
+unsigned get_gl_components(PixelComponents);
+unsigned get_gl_pixelformat(PixelFormat);
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/backends/opengl/predicate_backend.cpp b/source/backends/opengl/predicate_backend.cpp
new file mode 100644 (file)
index 0000000..12a5f5d
--- /dev/null
@@ -0,0 +1,27 @@
+#include "gl.h"
+#include "predicate.h"
+#include "predicate_backend.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+unsigned get_gl_predicate(Predicate pred)
+{
+       switch(pred)
+       {
+       case NEVER: return GL_NEVER;
+       case ALWAYS: return GL_ALWAYS;
+       case LESS: return GL_LESS;
+       case LEQUAL: return GL_LEQUAL;
+       case EQUAL: return GL_EQUAL;
+       case GREATER: return GL_GREATER;
+       case GEQUAL: return GL_GEQUAL;
+       case NOTEQUAL: return GL_NOTEQUAL;
+       default: throw invalid_argument("get_gl_predicate");
+       }
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/backends/opengl/predicate_backend.h b/source/backends/opengl/predicate_backend.h
new file mode 100644 (file)
index 0000000..8aeab1f
--- /dev/null
@@ -0,0 +1,16 @@
+#ifndef MSP_GL_PREDICATE_BACKEND_H_
+#define MSP_GL_PREDICATE_BACKEND_H_
+
+#ifndef MSP_GL_PREDICATE_H_
+#error "predicate_backend.h requires predicate.h"
+#endif
+
+namespace Msp {
+namespace GL {
+
+unsigned get_gl_predicate(Predicate);
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/backends/opengl/primitivetype_backend.cpp b/source/backends/opengl/primitivetype_backend.cpp
new file mode 100644 (file)
index 0000000..7e92fc1
--- /dev/null
@@ -0,0 +1,26 @@
+#include "gl.h"
+#include "primitivetype.h"
+#include "primitivetype_backend.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+unsigned get_gl_primitive_type(PrimitiveType pt)
+{
+       switch(pt)
+       {
+       case POINTS: return GL_POINTS;
+       case LINES: return GL_LINES;
+       case LINE_STRIP: return GL_LINE_STRIP;
+       case LINE_LOOP: return GL_LINE_LOOP;
+       case TRIANGLES: return GL_TRIANGLES;
+       case TRIANGLE_STRIP: return GL_TRIANGLE_STRIP;
+       case TRIANGLE_FAN: return GL_TRIANGLE_FAN;
+       default: throw invalid_argument("get_gl_primitive_type");
+       }
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/backends/opengl/primitivetype_backend.h b/source/backends/opengl/primitivetype_backend.h
new file mode 100644 (file)
index 0000000..200cbf0
--- /dev/null
@@ -0,0 +1,16 @@
+#ifndef MSP_GL_PRIMITIVETYPE_BACKEND_H_
+#define MSP_GL_PRIMITIVETYPE_BACKEND_H_
+
+#ifndef MSP_GL_PRIMITIVETYPE_H_
+#error "primitivetype_backend.h requires primitivetype.h"
+#endif
+
+namespace Msp {
+namespace GL {
+
+unsigned get_gl_primitive_type(PrimitiveType);
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/backends/opengl/program_backend.cpp b/source/backends/opengl/program_backend.cpp
new file mode 100644 (file)
index 0000000..3b1c429
--- /dev/null
@@ -0,0 +1,505 @@
+#include <cstring>
+#include <msp/core/algorithm.h>
+#include <msp/gl/extensions/arb_es2_compatibility.h>
+#include <msp/gl/extensions/arb_fragment_shader.h>
+#include <msp/gl/extensions/arb_gl_spirv.h>
+#include <msp/gl/extensions/arb_geometry_shader4.h>
+#include <msp/gl/extensions/arb_separate_shader_objects.h>
+#include <msp/gl/extensions/arb_shader_objects.h>
+#include <msp/gl/extensions/arb_uniform_buffer_object.h>
+#include <msp/gl/extensions/arb_vertex_shader.h>
+#include <msp/gl/extensions/ext_gpu_shader4.h>
+#include <msp/gl/extensions/khr_debug.h>
+#include <msp/gl/extensions/nv_non_square_matrices.h>
+#include <msp/io/print.h>
+#include "error.h"
+#include "program.h"
+#include "program_backend.h"
+#include "glsl/compiler.h"
+
+using namespace std;
+
+namespace {
+
+template<typename T, void (*&func)(GLint, GLsizei, const T *)>
+void uniform_wrapper(unsigned index, unsigned count, const void *data)
+{
+       func(index, count, static_cast<const T *>(data));
+}
+
+template<typename T, void (*&func)(GLint, GLsizei, GLboolean, const T *)>
+void uniform_matrix_wrapper(unsigned index, unsigned count, const void *data)
+{
+       func(index, count, false, static_cast<const T *>(data));
+}
+
+}
+
+namespace Msp {
+namespace GL {
+
+OpenGLProgram::OpenGLProgram():
+       linked(false)
+{
+       static Require _req(ARB_shader_objects);
+
+       id = glCreateProgram();
+       fill(stage_ids, stage_ids+MAX_STAGES, 0);
+}
+
+OpenGLProgram::~OpenGLProgram()
+{
+       for(unsigned i=0; i<MAX_STAGES; ++i)
+               if(stage_ids[i])
+                       glDeleteShader(stage_ids[i]);
+       glDeleteProgram(id);
+}
+
+bool OpenGLProgram::has_stages() const
+{
+       for(unsigned i=0; i<MAX_STAGES; ++i)
+               if(stage_ids[i])
+                       return true;
+       return false;
+}
+
+unsigned OpenGLProgram::add_stage(Stage type)
+{
+       GLenum gl_type;
+       switch(type)
+       {
+       case VERTEX: { static Require _req(ARB_vertex_shader); gl_type = GL_VERTEX_SHADER; } break;
+       case GEOMETRY: { static Require _req(ARB_geometry_shader4); gl_type = GL_GEOMETRY_SHADER; } break;
+       case FRAGMENT: { static Require _req(ARB_fragment_shader); gl_type = GL_FRAGMENT_SHADER; } break;
+       default: throw invalid_argument("Program::add_stage");
+       }
+
+       if(stage_ids[type])
+               throw invalid_operation("Program::add_stage");
+
+       unsigned stage_id = glCreateShader(gl_type);
+       stage_ids[type] = stage_id;
+       glAttachShader(id, stage_id);
+
+#ifdef DEBUG
+       if(!debug_name.empty() && KHR_debug)
+               set_stage_debug_name(stage_id, type);
+#endif
+
+       return stage_id;
+}
+
+void OpenGLProgram::add_glsl_stages(const GlslModule &mod, const map<string, int> &spec_values, TransientData &transient)
+{
+       SL::Compiler compiler;
+       compiler.set_source(mod.get_prepared_source(), "<module>");
+       compiler.specialize(spec_values);
+       compiler.compile(SL::Compiler::PROGRAM);
+#ifdef DEBUG
+       string diagnostics = compiler.get_diagnostics();
+       if(!diagnostics.empty())
+               IO::print("Program diagnostics:\n%s\n", diagnostics);
+#endif
+
+       vector<SL::Stage::Type> stages = compiler.get_stages();
+       if(stages.empty())
+               throw invalid_argument("Program::add_glsl_stages");
+
+       for(SL::Stage::Type st: stages)
+       {
+               unsigned stage_id = 0;
+               switch(st)
+               {
+               case SL::Stage::VERTEX: stage_id = add_stage(VERTEX); break;
+               case SL::Stage::GEOMETRY: stage_id = add_stage(GEOMETRY); break;
+               case SL::Stage::FRAGMENT: stage_id = add_stage(FRAGMENT); break;
+               default: throw invalid_operation("Program::add_glsl_stages");
+               }
+
+               string stage_src = compiler.get_stage_glsl(st);
+               const char *src_ptr = stage_src.data();
+               int src_len = stage_src.size();
+               glShaderSource(stage_id, 1, &src_ptr, &src_len);
+
+               if(st==SL::Stage::VERTEX)
+               {
+                       for(const auto &kvp: compiler.get_vertex_attributes())
+                               glBindAttribLocation(id, kvp.second, kvp.first.c_str());
+               }
+
+               if(st==SL::Stage::FRAGMENT && EXT_gpu_shader4)
+               {
+                       for(const auto &kvp: compiler.get_fragment_outputs())
+                               glBindFragDataLocation(id, kvp.second, kvp.first.c_str());
+               }
+
+               compile_glsl_stage(mod, stage_id);
+       }
+
+       transient.textures = compiler.get_texture_bindings();
+       transient.blocks = compiler.get_uniform_block_bindings();
+}
+
+void OpenGLProgram::compile_glsl_stage(const GlslModule &mod, unsigned stage_id)
+{
+       glCompileShader(stage_id);
+       int status = 0;
+       glGetShaderiv(stage_id, GL_COMPILE_STATUS, &status);
+
+       int info_log_len = 0;
+       glGetShaderiv(stage_id, GL_INFO_LOG_LENGTH, &info_log_len);
+       string info_log(info_log_len+1, 0);
+       glGetShaderInfoLog(stage_id, info_log_len+1, &info_log_len, &info_log[0]);
+       info_log.erase(info_log_len);
+       info_log = mod.get_source_map().translate_errors(info_log);
+
+       if(!status)
+               throw compile_error(info_log);
+#ifdef DEBUG
+       if(!info_log.empty())
+               IO::print("Shader compile info log:\n%s", info_log);
+#endif
+}
+
+void OpenGLProgram::add_spirv_stages(const SpirVModule &mod, const map<string, int> &spec_values, TransientData &transient)
+{
+       static Require _req(ARB_gl_spirv);
+       static Require _req2(ARB_ES2_compatibility);
+
+       unsigned n_stages = 0;
+       unsigned used_stage_ids[MAX_STAGES];
+       for(const SpirVModule::EntryPoint &e: mod.get_entry_points())
+       {
+               unsigned stage_id = 0;
+               switch(e.stage)
+               {
+               case SpirVModule::VERTEX: stage_id = add_stage(VERTEX); break;
+               case SpirVModule::GEOMETRY: stage_id = add_stage(GEOMETRY); break;
+               case SpirVModule::FRAGMENT: stage_id = add_stage(FRAGMENT); break;
+               default: throw invalid_operation("Program::add_spirv_stages");
+               }
+
+               used_stage_ids[n_stages++] = stage_id;
+       }
+
+       if(!n_stages)
+               throw invalid_argument("Program::add_spirv_stages");
+
+       const vector<uint32_t> &code = mod.get_code();
+       glShaderBinary(n_stages, used_stage_ids, GL_SHADER_BINARY_FORMAT_SPIR_V, &code[0], code.size()*4);
+
+       const vector<SpirVModule::Constant> &spec_consts = mod.get_spec_constants();
+       vector<unsigned> spec_id_array;
+       vector<unsigned> spec_value_array;
+       spec_id_array.reserve(spec_consts.size());
+       spec_value_array.reserve(spec_consts.size());
+       for(const SpirVModule::Constant &c: spec_consts)
+       {
+               auto i = spec_values.find(c.name);
+               if(i!=spec_values.end())
+               {
+                       spec_id_array.push_back(c.constant_id);
+                       spec_value_array.push_back(i->second);
+                       transient.spec_values[c.constant_id] = i->second;
+               }
+       }
+
+       auto j = mod.get_entry_points().begin();
+       for(unsigned i=0; i<MAX_STAGES; ++i)
+               if(stage_ids[i])
+                       glSpecializeShader(stage_ids[i], j->name.c_str(), spec_id_array.size(), &spec_id_array[0], &spec_value_array[0]);
+}
+
+void OpenGLProgram::finalize(const Module &mod)
+{
+       glLinkProgram(id);
+       int status = 0;
+       glGetProgramiv(id, GL_LINK_STATUS, &status);
+       linked = status;
+
+       int info_log_len = 0;
+       glGetProgramiv(id, GL_INFO_LOG_LENGTH, &info_log_len);
+       string info_log(info_log_len+1, 0);
+       glGetProgramInfoLog(id, info_log_len+1, &info_log_len, &info_log[0]);
+       info_log.erase(info_log_len);
+       if(mod.get_format()==Module::GLSL)
+               info_log = static_cast<const GlslModule &>(mod).get_source_map().translate_errors(info_log);
+
+       if(!linked)
+               throw compile_error(info_log);
+#ifdef DEBUG
+       if(!info_log.empty())
+               IO::print("Program link info log:\n%s", info_log);
+#endif
+}
+
+void OpenGLProgram::query_uniforms()
+{
+       ReflectData &rd = static_cast<Program *>(this)->reflect_data;
+
+       unsigned count = 0;
+       glGetProgramiv(id, GL_ACTIVE_UNIFORMS, reinterpret_cast<int *>(&count));
+       rd.uniforms.reserve(count);
+       vector<string> uniform_names(count);
+       for(unsigned i=0; i<count; ++i)
+       {
+               char name[128];
+               int len = 0;
+               int size;
+               GLenum type;
+               glGetActiveUniform(id, i, sizeof(name), &len, &size, &type, name);
+               if(len && strncmp(name, "gl_", 3))
+               {
+                       /* Some implementations report the first element of a uniform array,
+                       others report just the name of the array itself. */
+                       if(len>3 && !strcmp(name+len-3, "[0]"))
+                               name[len-3] = 0;
+
+                       rd.uniforms.push_back(ReflectData::UniformInfo());
+                       ReflectData::UniformInfo &info = rd.uniforms.back();
+                       info.name = name;
+                       info.tag = name;
+                       info.array_size = size;
+                       info.type = from_gl_type(type);
+                       uniform_names[i] = name;
+               }
+       }
+
+       sort_member(rd.uniforms, &ReflectData::UniformInfo::tag);
+
+       if(ARB_uniform_buffer_object)
+       {
+               vector<ReflectData::UniformInfo *> uniforms_by_index(count);
+               for(unsigned i=0; i<count; ++i)
+                       if(!uniform_names[i].empty())
+                               // The element is already known to be present
+                               uniforms_by_index[i] = &*lower_bound_member(rd.uniforms, Tag(uniform_names[i]), &ReflectData::UniformInfo::tag);
+               query_uniform_blocks(uniforms_by_index);
+       }
+
+       rd.uniform_blocks.push_back(ReflectData::UniformBlockInfo());
+       ReflectData::UniformBlockInfo &default_block = rd.uniform_blocks.back();
+
+       for(ReflectData::UniformInfo &u: rd.uniforms)
+               if(!u.block)
+               {
+                       u.location = glGetUniformLocation(id, u.name.c_str());
+                       u.block = &default_block;
+                       u.array_stride = get_type_size(u.type);
+                       if(is_matrix(u.type))
+                               u.matrix_stride = get_type_size(get_matrix_column_type(u.type));
+                       default_block.uniforms.push_back(&u);
+
+                       if(u.location>=0)
+                       {
+                               UniformCall::FuncPtr func = 0;
+                               if(is_image(u.type))
+                                       glGetUniformiv(id, u.location, &u.binding);
+                               else if(u.type==FLOAT)
+                                       func = &uniform_wrapper<float, glUniform1fv>;
+                               else if(u.type==FLOAT_VEC2)
+                                       func = &uniform_wrapper<float, glUniform2fv>;
+                               else if(u.type==FLOAT_VEC3)
+                                       func = &uniform_wrapper<float, glUniform3fv>;
+                               else if(u.type==FLOAT_VEC4)
+                                       func = &uniform_wrapper<float, glUniform4fv>;
+                               else if(u.type==INT)
+                                       func = &uniform_wrapper<int, glUniform1iv>;
+                               else if(u.type==INT_VEC2)
+                                       func = &uniform_wrapper<int, glUniform2iv>;
+                               else if(u.type==INT_VEC3)
+                                       func = &uniform_wrapper<int, glUniform3iv>;
+                               else if(u.type==INT_VEC4)
+                                       func = &uniform_wrapper<int, glUniform4iv>;
+                               else if(u.type==FLOAT_MAT2)
+                                       func = &uniform_matrix_wrapper<float, glUniformMatrix2fv>;
+                               else if(u.type==FLOAT_MAT3)
+                                       func = &uniform_matrix_wrapper<float, glUniformMatrix3fv>;
+                               else if(u.type==FLOAT_MAT4)
+                                       func = &uniform_matrix_wrapper<float, glUniformMatrix4fv>;
+                               else if(u.type==FLOAT_MAT2x3)
+                                       func = &uniform_matrix_wrapper<float, glUniformMatrix2x3fv>;
+                               else if(u.type==FLOAT_MAT3x2)
+                                       func = &uniform_matrix_wrapper<float, glUniformMatrix3x2fv>;
+                               else if(u.type==FLOAT_MAT2x4)
+                                       func = &uniform_matrix_wrapper<float, glUniformMatrix2x4fv>;
+                               else if(u.type==FLOAT_MAT4x2)
+                                       func = &uniform_matrix_wrapper<float, glUniformMatrix4x2fv>;
+                               else if(u.type==FLOAT_MAT3x4)
+                                       func = &uniform_matrix_wrapper<float, glUniformMatrix3x4fv>;
+                               else if(u.type==FLOAT_MAT4x3)
+                                       func = &uniform_matrix_wrapper<float, glUniformMatrix4x3fv>;
+
+                               if(func)
+                                       uniform_calls.push_back(UniformCall(u.location, u.array_size, func));
+                       }
+               }
+
+       default_block.sort_uniforms();
+       if(!default_block.uniforms.empty())
+       {
+               const ReflectData::UniformInfo &uni = *default_block.uniforms.back();
+               default_block.data_size = uni.location*16+uni.array_size*get_type_size(uni.type);
+       }
+       default_block.update_layout_hash();
+       rd.update_layout_hash();
+}
+
+void OpenGLProgram::query_uniform_blocks(const vector<ReflectData::UniformInfo *> &uniforms_by_index)
+{
+       ReflectData &rd = static_cast<Program *>(this)->reflect_data;
+
+       unsigned count = 0;
+       glGetProgramiv(id, GL_ACTIVE_UNIFORM_BLOCKS, reinterpret_cast<int *>(&count));
+       // Reserve an extra index for the default block
+       rd.uniform_blocks.reserve(count+1);
+       for(unsigned i=0; i<count; ++i)
+       {
+               char name[128];
+               int len;
+               glGetActiveUniformBlockName(id, i, sizeof(name), &len, name);
+               rd.uniform_blocks.push_back(ReflectData::UniformBlockInfo());
+               ReflectData::UniformBlockInfo &info = rd.uniform_blocks.back();
+               info.name = name;
+
+               int value;
+               glGetActiveUniformBlockiv(id, i, GL_UNIFORM_BLOCK_DATA_SIZE, &value);
+               info.data_size = value;
+
+               glGetActiveUniformBlockiv(id, i, GL_UNIFORM_BLOCK_BINDING, &value);
+               info.bind_point = value;
+
+               glGetActiveUniformBlockiv(id, i, GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, &value);
+               vector<int> indices(value);
+               glGetActiveUniformBlockiv(id, i, GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES, &indices[0]);
+               for(int j: indices)
+               {
+                       if(!uniforms_by_index[j])
+                               throw logic_error("Program::link");
+                       info.uniforms.push_back(uniforms_by_index[j]);
+                       uniforms_by_index[j]->block = &info;
+               }
+
+               vector<unsigned> query_indices(indices.begin(), indices.end());
+               vector<int> values(indices.size());
+               glGetActiveUniformsiv(id, query_indices.size(), &query_indices[0], GL_UNIFORM_OFFSET, &values[0]);
+               for(unsigned j=0; j<indices.size(); ++j)
+                       uniforms_by_index[indices[j]]->offset = values[j];
+
+               query_indices.clear();
+               for(int j: indices)
+                       if(uniforms_by_index[j]->array_size>1)
+                               query_indices.push_back(j);
+               if(!query_indices.empty())
+               {
+                       glGetActiveUniformsiv(id, query_indices.size(), &query_indices[0], GL_UNIFORM_ARRAY_STRIDE, &values[0]);
+                       for(unsigned j=0; j<query_indices.size(); ++j)
+                               uniforms_by_index[query_indices[j]]->array_stride = values[j];
+               }
+
+               query_indices.clear();
+               for(int j: indices)
+               {
+                       DataType t = uniforms_by_index[j]->type;
+                       if(is_matrix(t))
+                               query_indices.push_back(j);
+               }
+               if(!query_indices.empty())
+               {
+                       glGetActiveUniformsiv(id, query_indices.size(), &query_indices[0], GL_UNIFORM_MATRIX_STRIDE, &values[0]);
+                       for(unsigned j=0; j<query_indices.size(); ++j)
+                               uniforms_by_index[query_indices[j]]->matrix_stride = values[j];
+               }
+
+               info.sort_uniforms();
+               info.update_layout_hash();
+       }
+}
+
+void OpenGLProgram::query_attributes()
+{
+       ReflectData &rd = static_cast<Program *>(this)->reflect_data;
+
+       unsigned count = 0;
+       glGetProgramiv(id, GL_ACTIVE_ATTRIBUTES, reinterpret_cast<int *>(&count));
+       rd.attributes.reserve(count);
+       for(unsigned i=0; i<count; ++i)
+       {
+               char name[128];
+               int len = 0;
+               int size;
+               GLenum type;
+               glGetActiveAttrib(id, i, sizeof(name), &len, &size, &type, name);
+               if(len && strncmp(name, "gl_", 3))
+               {
+                       if(len>3 && !strcmp(name+len-3, "[0]"))
+                               name[len-3] = 0;
+
+                       rd.attributes.push_back(ReflectData::AttributeInfo());
+                       ReflectData::AttributeInfo &info = rd.attributes.back();
+                       info.name = name;
+                       info.location = glGetAttribLocation(id, name);
+                       info.array_size = size;
+                       info.type = from_gl_type(type);
+               }
+       }
+}
+
+void OpenGLProgram::apply_bindings(const TransientData &transient)
+{
+       ReflectData &rd = static_cast<Program *>(this)->reflect_data;
+
+       for(unsigned i=0; i<rd.uniform_blocks.size(); ++i)
+       {
+               auto j = transient.blocks.find(rd.uniform_blocks[i].name);
+               if(j!=transient.blocks.end())
+               {
+                       glUniformBlockBinding(id, i, j->second);
+                       rd.uniform_blocks[i].bind_point = j->second;
+               }
+       }
+
+       if(!ARB_separate_shader_objects)
+               glUseProgram(id);
+       for(const auto &kvp: transient.textures)
+       {
+               int location = static_cast<const Program *>(this)->get_uniform_location(kvp.first);
+               if(location>=0)
+               {
+                       if(ARB_separate_shader_objects)
+                               glProgramUniform1i(id, location, kvp.second);
+                       else
+                               glUniform1i(location, kvp.second);
+               }
+       }
+}
+
+void OpenGLProgram::set_debug_name(const string &name)
+{
+#ifdef DEBUG
+       debug_name = name;
+       if(KHR_debug)
+       {
+               glObjectLabel(GL_PROGRAM, id, name.size(), name.c_str());
+               for(unsigned i=0; i<MAX_STAGES; ++i)
+                       if(stage_ids[i])
+                               set_stage_debug_name(stage_ids[i], static_cast<Stage>(i));
+       }
+#else
+       (void)name;
+#endif
+}
+
+void OpenGLProgram::set_stage_debug_name(unsigned stage_id, Stage type)
+{
+#ifdef DEBUG
+       static const char *const suffixes[] = { " [VS]", " [GS]", " [FS]" };
+       string name = debug_name+suffixes[type];
+       glObjectLabel(GL_SHADER, stage_id, name.size(), name.c_str());
+#else
+       (void)stage_id; (void)type;
+#endif
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/backends/opengl/program_backend.h b/source/backends/opengl/program_backend.h
new file mode 100644 (file)
index 0000000..4ae729e
--- /dev/null
@@ -0,0 +1,73 @@
+#ifndef MSP_GL_PROGRAM_BACKEND_H_
+#define MSP_GL_PROGRAM_BACKEND_H_
+
+#include <map>
+#include <string>
+#include <vector>
+#include "reflectdata.h"
+
+namespace Msp {
+namespace GL {
+
+class OpenGLProgram
+{
+       friend class OpenGLPipelineState;
+
+protected:
+       enum Stage
+       {
+               VERTEX,
+               GEOMETRY,
+               FRAGMENT,
+               MAX_STAGES
+       };
+
+       struct TransientData
+       {
+               std::map<std::string, unsigned> textures;
+               std::map<std::string, unsigned> blocks;
+               std::map<unsigned, int> spec_values;
+       };
+
+       struct UniformCall
+       {
+               using FuncPtr = void (*)(unsigned, unsigned, const void *);
+
+               unsigned location;
+               unsigned size;
+               FuncPtr func;
+
+               UniformCall(unsigned l, unsigned s, FuncPtr f): location(l), size(s), func(f) { }
+       };
+
+       unsigned id;
+       unsigned stage_ids[MAX_STAGES];
+       bool linked;
+       std::vector<UniformCall> uniform_calls;
+       std::string debug_name;
+
+       OpenGLProgram();
+       ~OpenGLProgram();
+
+       bool has_stages() const;
+       unsigned add_stage(Stage);
+       void add_glsl_stages(const GlslModule &, const std::map<std::string, int> &, TransientData &);
+       void compile_glsl_stage(const GlslModule &, unsigned);
+       void add_spirv_stages(const SpirVModule &, const std::map<std::string, int> &, TransientData &);
+
+       void finalize(const Module &);
+       void query_uniforms();
+       void query_uniform_blocks(const std::vector<ReflectData::UniformInfo *> &);
+       void query_attributes();
+       void apply_bindings(const TransientData &);
+
+       void set_debug_name(const std::string &);
+       void set_stage_debug_name(unsigned, Stage);
+};
+
+using ProgramBackend = OpenGLProgram;
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/backends/opengl/query_backend.cpp b/source/backends/opengl/query_backend.cpp
new file mode 100644 (file)
index 0000000..b445be3
--- /dev/null
@@ -0,0 +1,56 @@
+#include <stdexcept>
+#include <msp/gl/extensions/arb_occlusion_query.h>
+#include <msp/gl/extensions/arb_occlusion_query2.h>
+#include "query.h"
+#include "query_backend.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+OpenGLQueryPool::OpenGLQueryPool(unsigned t):
+       gl_type(get_gl_query_type(t))
+{
+       if(t==OCCLUSION_QUERY)
+       {
+               static Require req(ARB_occlusion_query);
+               static Require req2(ARB_occlusion_query2);
+       }
+}
+
+OpenGLQueryPool::~OpenGLQueryPool()
+{
+       glDeleteQueries(queries.size(), queries.data());
+}
+
+void OpenGLQueryPool::resize(unsigned s)
+{
+       if(s<queries.size())
+               glDeleteQueries(queries.size()-s, queries.data()+s);
+
+       unsigned old_size = queries.size();
+       queries.resize(s);
+       if(s>old_size)
+               glGenQueries(s-old_size, queries.data()+old_size);
+}
+
+unsigned OpenGLQueryPool::get_result(unsigned i) const
+{
+       unsigned result = 0;
+       glGetQueryObjectuiv(queries[i], GL_QUERY_RESULT, &result);
+       return result;
+}
+
+
+unsigned get_gl_query_type(unsigned t)
+{
+       switch(t)
+       {
+       case OCCLUSION_QUERY: return GL_ANY_SAMPLES_PASSED;
+       default: throw invalid_argument("get_gl_query_type");
+       }
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/backends/opengl/query_backend.h b/source/backends/opengl/query_backend.h
new file mode 100644 (file)
index 0000000..9365b24
--- /dev/null
@@ -0,0 +1,33 @@
+#ifndef MSP_GL_QUERY_BACKEND_H_
+#define MSP_GL_QUERY_BACKEND_H_
+
+#include <vector>
+
+namespace Msp {
+namespace GL {
+
+class OpenGLQueryPool
+{
+       friend class OpenGLCommands;
+
+protected:
+       unsigned gl_type;
+       std::vector<unsigned> queries;
+
+       OpenGLQueryPool(unsigned);
+       ~OpenGLQueryPool();
+
+       void resize(unsigned);
+       unsigned get_size() const { return queries.size(); }
+
+       unsigned get_result(unsigned) const;
+};
+
+using QueryPoolBackend = OpenGLQueryPool;
+
+unsigned get_gl_query_type(unsigned);
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/backends/opengl/sampler_backend.cpp b/source/backends/opengl/sampler_backend.cpp
new file mode 100644 (file)
index 0000000..915dda6
--- /dev/null
@@ -0,0 +1,98 @@
+#include <msp/gl/extensions/arb_direct_state_access.h>
+#include <msp/gl/extensions/arb_sampler_objects.h>
+#include <msp/gl/extensions/arb_shadow.h>
+#include <msp/gl/extensions/ext_texture_filter_anisotropic.h>
+#include <msp/gl/extensions/ext_texture3d.h>
+#include <msp/gl/extensions/khr_debug.h>
+#include "gl.h"
+#include "sampler.h"
+#include "sampler_backend.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+OpenGLSampler::OpenGLSampler()
+{
+       static Require _req(ARB_sampler_objects);
+       static Require _req2(EXT_texture3D);
+       static Require _req3(ARB_shadow);
+
+       if(ARB_direct_state_access)
+               glCreateSamplers(1, &id);
+       else
+               glGenSamplers(1, &id);
+}
+
+bool OpenGLSampler::check_anisotropic(bool require)
+{
+       if(require)
+               static Require _req(EXT_texture_filter_anisotropic);
+       return EXT_texture_filter_anisotropic;
+}
+
+void OpenGLSampler::update(unsigned mask) const
+{
+       const Sampler *self = static_cast<const Sampler *>(this);
+       if(mask&Sampler::MIN_FILTER)
+               glSamplerParameteri(id, GL_TEXTURE_MIN_FILTER, get_gl_filter(self->min_filter));
+       if(mask&Sampler::MAG_FILTER)
+               glSamplerParameteri(id, GL_TEXTURE_MAG_FILTER, get_gl_filter(self->mag_filter));
+       if(mask&Sampler::MAX_ANISOTROPY)
+               glSamplerParameterf(id, GL_TEXTURE_MAX_ANISOTROPY_EXT, self->max_anisotropy);
+       if(mask&Sampler::WRAP_S)
+               glSamplerParameteri(id, GL_TEXTURE_WRAP_S, get_gl_wrap(self->wrap_s));
+       if(mask&Sampler::WRAP_T)
+               glSamplerParameteri(id, GL_TEXTURE_WRAP_T, get_gl_wrap(self->wrap_t));
+       if(mask&Sampler::WRAP_R)
+               glSamplerParameteri(id, GL_TEXTURE_WRAP_R, get_gl_wrap(self->wrap_r));
+       if(mask&Sampler::BORDER_COLOR)
+               glSamplerParameterfv(id, GL_TEXTURE_BORDER_COLOR, &self->border_color.r);
+       if(mask&Sampler::COMPARE)
+       {
+               glSamplerParameteri(id, GL_TEXTURE_COMPARE_MODE, (self->compare ? GL_COMPARE_R_TO_TEXTURE : GL_NONE));
+               if(self->compare)
+                       glSamplerParameteri(id, GL_TEXTURE_COMPARE_FUNC, get_gl_predicate(self->cmp_func));
+       }
+}
+
+void OpenGLSampler::set_debug_name(const string &name)
+{
+#ifdef DEBUG
+       if(id && KHR_debug)
+               glObjectLabel(GL_SAMPLER, id, name.size(), name.c_str());
+#else
+       (void)name;
+#endif
+}
+
+
+unsigned get_gl_filter(unsigned filter)
+{
+       switch(filter)
+       {
+       case NEAREST: return GL_NEAREST;
+       case LINEAR: return GL_LINEAR;
+       case NEAREST_MIPMAP_NEAREST: return GL_NEAREST_MIPMAP_NEAREST;
+       case NEAREST_MIPMAP_LINEAR: return GL_NEAREST_MIPMAP_LINEAR;
+       case LINEAR_MIPMAP_NEAREST: return GL_LINEAR_MIPMAP_NEAREST;
+       case LINEAR_MIPMAP_LINEAR: return GL_LINEAR_MIPMAP_LINEAR;
+       default: throw invalid_argument("get_gl_filter");
+       }
+}
+
+unsigned get_gl_wrap(unsigned wrap)
+{
+       switch(wrap)
+       {
+       case REPEAT: return GL_REPEAT;
+       case CLAMP_TO_EDGE: return GL_CLAMP_TO_EDGE;
+       case CLAMP_TO_BORDER: return GL_CLAMP_TO_BORDER;
+       case MIRRORED_REPEAT: return GL_MIRRORED_REPEAT;
+       default: throw invalid_argument("get_gl_wrap");
+       }
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/backends/opengl/sampler_backend.h b/source/backends/opengl/sampler_backend.h
new file mode 100644 (file)
index 0000000..77590c2
--- /dev/null
@@ -0,0 +1,31 @@
+#ifndef MSP_GL_SAMPLER_BACKEND_H_
+#define MSP_GL_SAMPLER_BACKEND_H_
+
+namespace Msp {
+namespace GL {
+
+class OpenGLSampler
+{
+       friend class OpenGLPipelineState;
+
+protected:
+       unsigned id;
+
+       OpenGLSampler();
+
+       static bool check_anisotropic(bool);
+
+       void update(unsigned) const;
+
+       void set_debug_name(const std::string &);
+};
+
+using SamplerBackend = OpenGLSampler;
+
+unsigned get_gl_filter(unsigned);
+unsigned get_gl_wrap(unsigned);
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/backends/opengl/stenciltest_backend.cpp b/source/backends/opengl/stenciltest_backend.cpp
new file mode 100644 (file)
index 0000000..258bf9b
--- /dev/null
@@ -0,0 +1,27 @@
+#include "gl.h"
+#include "stenciltest.h"
+#include "stenciltest_backend.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+unsigned get_gl_stencil_op(StencilOp op)
+{
+       switch(op)
+       {
+       case KEEP: return GL_KEEP;
+       case SET_ZERO: return GL_ZERO;
+       case REPLACE: return GL_REPLACE;
+       case INCR: return GL_INCR;
+       case DECR: return GL_DECR;
+       case INVERT: return GL_INVERT;
+       case INCR_WRAP: return GL_INCR_WRAP;
+       case DECR_WRAP: return GL_DECR_WRAP;
+       default: throw invalid_argument("get_gl_stencil_op");
+       }
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/backends/opengl/stenciltest_backend.h b/source/backends/opengl/stenciltest_backend.h
new file mode 100644 (file)
index 0000000..4b73a6a
--- /dev/null
@@ -0,0 +1,16 @@
+#ifndef MSP_GL_STENCILTEST_BACKEND_H_
+#define MSP_GL_STENCILTEST_BACKEND_H_
+
+#ifndef MSP_GL_STENCILTEST_H_
+#error "stenciltest_backend.h requires stenciltest.h"
+#endif
+
+namespace Msp {
+namespace GL {
+
+unsigned get_gl_stencil_op(StencilOp);
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/backends/opengl/texture1d_backend.cpp b/source/backends/opengl/texture1d_backend.cpp
new file mode 100644 (file)
index 0000000..e44ceb4
--- /dev/null
@@ -0,0 +1,63 @@
+#include <msp/gl/extensions/arb_direct_state_access.h>
+#include <msp/gl/extensions/arb_texture_storage.h>
+#include <msp/gl/extensions/msp_texture1d.h>
+#include "gl.h"
+#include "texture1d.h"
+#include "texture1d_backend.h"
+
+namespace Msp {
+namespace GL {
+
+OpenGLTexture1D::OpenGLTexture1D():
+       Texture(GL_TEXTURE_1D)
+{
+       static Require _req(MSP_texture1D);
+}
+
+void OpenGLTexture1D::allocate()
+{
+       unsigned width = static_cast<const Texture1D *>(this)->width;
+       unsigned levels = static_cast<const Texture1D *>(this)->levels;
+
+       GLenum gl_fmt = get_gl_pixelformat(storage_fmt);
+       if(ARB_texture_storage)
+       {
+               if(ARB_direct_state_access)
+                       glTextureStorage1D(id, levels, gl_fmt, width);
+               else
+               {
+                       bind_scratch();
+                       glTexStorage1D(target, levels, gl_fmt, width);
+               }
+       }
+       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)
+               {
+                       unsigned lv_size = static_cast<const Texture1D *>(this)->get_level_size(i);
+                       glTexImage1D(target, i, gl_fmt, lv_size, 0, comp, type, 0);
+               }
+       }
+
+       apply_swizzle();
+}
+
+void OpenGLTexture1D::sub_image(unsigned level, int x, unsigned wd, 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)
+               glTextureSubImage1D(id, level, x, wd, comp, type, data);
+       else
+       {
+               bind_scratch();
+               glTexSubImage1D(target, level, x, wd, comp, type, data);
+       }
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/backends/opengl/texture1d_backend.h b/source/backends/opengl/texture1d_backend.h
new file mode 100644 (file)
index 0000000..449118d
--- /dev/null
@@ -0,0 +1,23 @@
+#ifndef MSP_GL_TEXTURE1D_BACKEND_H_
+#define MSP_GL_TEXTURE1D_BACKEND_H_
+
+#include "texture.h"
+
+namespace Msp {
+namespace GL {
+
+class OpenGLTexture1D: public Texture
+{
+protected:
+       OpenGLTexture1D();
+
+       void allocate();
+       void sub_image(unsigned, int, unsigned, const void *);
+};
+
+using Texture1DBackend = OpenGLTexture1D;
+
+} // namespace GL
+} // namespace Msp
+
+#endif
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
diff --git a/source/backends/opengl/texture2d_backend.h b/source/backends/opengl/texture2d_backend.h
new file mode 100644 (file)
index 0000000..8f2fec5
--- /dev/null
@@ -0,0 +1,31 @@
+#ifndef MSP_GL_TEXTURE2D_BACKEND_H_
+#define MSP_GL_TEXTURE2D_BACKEND_H_
+
+#include "texture.h"
+
+namespace Msp {
+namespace GL {
+
+class Buffer;
+
+class OpenGLTexture2D: public Texture
+{
+protected:
+       class AsyncLoader;
+
+       OpenGLTexture2D(ResourceManager *);
+
+       void allocate();
+       void sub_image(unsigned, int, int, unsigned, unsigned, const void *);
+       void sub_image(unsigned, int, int, unsigned, unsigned, const Buffer &, unsigned);
+
+       Resource::AsyncLoader *create_async_loader(IO::Seekable &);
+       void unload();
+};
+
+using Texture2DBackend = OpenGLTexture2D;
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/backends/opengl/texture2darray_backend.cpp b/source/backends/opengl/texture2darray_backend.cpp
new file mode 100644 (file)
index 0000000..610229b
--- /dev/null
@@ -0,0 +1,14 @@
+#include <msp/gl/extensions/ext_texture_array.h>
+#include "texture2darray_backend.h"
+
+namespace Msp {
+namespace GL {
+
+OpenGLTexture2DArray::OpenGLTexture2DArray():
+       Texture3D(GL_TEXTURE_2D_ARRAY)
+{
+       static Require _req(EXT_texture_array);
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/backends/opengl/texture2darray_backend.h b/source/backends/opengl/texture2darray_backend.h
new file mode 100644 (file)
index 0000000..42a33fc
--- /dev/null
@@ -0,0 +1,20 @@
+#ifndef MSP_GL_TEXTURE2DARRAY_BACKEND_H_
+#define MSP_GL_TEXTURE2DARRAY_BACKEND_H_
+
+#include "texture3d.h"
+
+namespace Msp {
+namespace GL {
+
+class OpenGLTexture2DArray: public Texture3D
+{
+protected:
+       OpenGLTexture2DArray();
+};
+
+using Texture2DArrayBackend = OpenGLTexture2DArray;
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/backends/opengl/texture2dmultisample_backend.cpp b/source/backends/opengl/texture2dmultisample_backend.cpp
new file mode 100644 (file)
index 0000000..c4a4e42
--- /dev/null
@@ -0,0 +1,42 @@
+#include <msp/gl/extensions/arb_direct_state_access.h>
+#include <msp/gl/extensions/arb_texture_multisample.h>
+#include <msp/gl/extensions/arb_texture_storage_multisample.h>
+#include "texture2dmultisample.h"
+#include "texture2dmultisample_backend.h"
+
+namespace Msp {
+namespace GL {
+
+OpenGLTexture2DMultisample::OpenGLTexture2DMultisample():
+       Texture(GL_TEXTURE_2D_MULTISAMPLE)
+{
+       static Require _req(ARB_texture_multisample);
+}
+
+void OpenGLTexture2DMultisample::allocate()
+{
+       unsigned width = static_cast<const Texture2DMultisample *>(this)->width;
+       unsigned height = static_cast<const Texture2DMultisample *>(this)->height;
+       unsigned samples = static_cast<const Texture2DMultisample *>(this)->samples;
+
+       GLenum gl_fmt = get_gl_pixelformat(storage_fmt);
+       if(ARB_texture_storage_multisample)
+       {
+               if(ARB_direct_state_access)
+                       glTextureStorage2DMultisample(id, samples, gl_fmt, width, height, false);
+               else
+               {
+                       bind_scratch();
+                       glTexStorage2DMultisample(target, samples, gl_fmt, width, height, false);
+               }
+       }
+       else
+       {
+               bind_scratch();
+               glTexImage2DMultisample(target, samples, gl_fmt, width, height, false);
+       }
+       apply_swizzle();
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/backends/opengl/texture2dmultisample_backend.h b/source/backends/opengl/texture2dmultisample_backend.h
new file mode 100644 (file)
index 0000000..e8a37e4
--- /dev/null
@@ -0,0 +1,22 @@
+#ifndef MSP_GL_TEXTURE2DMULTISAMPLE_BACKEND_H_
+#define MSP_GL_TEXTURE2DMULTISAMPLE_BACKEND_H_
+
+#include "texture.h"
+
+namespace Msp {
+namespace GL {
+
+class OpenGLTexture2DMultisample: public Texture
+{
+protected:
+       OpenGLTexture2DMultisample();
+
+       void allocate();
+};
+
+using Texture2DMultisampleBackend = OpenGLTexture2DMultisample;
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/backends/opengl/texture3d_backend.cpp b/source/backends/opengl/texture3d_backend.cpp
new file mode 100644 (file)
index 0000000..6826993
--- /dev/null
@@ -0,0 +1,75 @@
+#include <msp/gl/extensions/arb_direct_state_access.h>
+#include <msp/gl/extensions/arb_texture_storage.h>
+#include <msp/gl/extensions/ext_texture3d.h>
+#include <msp/gl/extensions/ext_texture_array.h>
+#include "gl.h"
+#include "texture3d.h"
+#include "texture3d_backend.h"
+
+namespace Msp {
+namespace GL {
+
+OpenGLTexture3D::OpenGLTexture3D():
+       Texture(GL_TEXTURE_3D)
+{
+       static Require _req(EXT_texture3D);
+}
+
+OpenGLTexture3D::OpenGLTexture3D(unsigned t):
+       Texture(t)
+{ }
+
+void OpenGLTexture3D::allocate()
+{
+       unsigned width = static_cast<const Texture3D *>(this)->width;
+       unsigned height = static_cast<const Texture3D *>(this)->height;
+       unsigned depth = static_cast<const Texture3D *>(this)->depth;
+       unsigned levels = static_cast<const Texture3D *>(this)->levels;
+
+       GLenum gl_fmt = get_gl_pixelformat(storage_fmt);
+       if(ARB_texture_storage)
+       {
+               if(ARB_direct_state_access)
+                       glTextureStorage3D(id, levels, gl_fmt, width, height, depth);
+               else
+               {
+                       bind_scratch();
+                       glTexStorage3D(target, levels, gl_fmt, width, height, depth);
+               }
+       }
+       else
+       {
+               bind_scratch();
+               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 Texture3D *>(this)->get_level_size(i);
+                       glTexImage3D(target, i, gl_fmt, lv_size.x, lv_size.y, lv_size.z, 0, comp, type, 0);
+               }
+               glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, levels-1);
+       }
+
+       apply_swizzle();
+}
+
+void OpenGLTexture3D::sub_image(unsigned level, int x, int y, int z, unsigned wd, unsigned ht, unsigned dp, 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)
+               glTextureSubImage3D(id, level, x, y, z, wd, ht, dp, comp, type, data);
+       else
+       {
+               bind_scratch();
+               glTexSubImage3D(target, level, x, y, z, wd, ht, dp, comp, type, data);
+       }
+}
+
+bool OpenGLTexture3D::is_array() const
+{
+       return target==GL_TEXTURE_2D_ARRAY;
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/backends/opengl/texture3d_backend.h b/source/backends/opengl/texture3d_backend.h
new file mode 100644 (file)
index 0000000..33dcee0
--- /dev/null
@@ -0,0 +1,26 @@
+#ifndef MSP_GL_TEXTURE3D_BACKEND_H_
+#define MSP_GL_TEXTURE3D_BACKEND_H_
+
+#include "texture.h"
+
+namespace Msp {
+namespace GL {
+
+class OpenGLTexture3D: public Texture
+{
+protected:
+       OpenGLTexture3D();
+       OpenGLTexture3D(unsigned);
+
+       void allocate();
+       void sub_image(unsigned, int, int, int, unsigned, unsigned, unsigned, const void *);
+
+       bool is_array() const;
+};
+
+using Texture3DBackend = OpenGLTexture3D;
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/backends/opengl/texture_backend.cpp b/source/backends/opengl/texture_backend.cpp
new file mode 100644 (file)
index 0000000..fe4679f
--- /dev/null
@@ -0,0 +1,146 @@
+#include <msp/gl/extensions/arb_direct_state_access.h>
+#include <msp/gl/extensions/arb_texture_swizzle.h>
+#include <msp/gl/extensions/ext_framebuffer_object.h>
+#include <msp/gl/extensions/khr_debug.h>
+#include "gl.h"
+#include "error.h"
+#include "texture.h"
+#include "texture_backend.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+int OpenGLTexture::swizzle_orders[] =
+{
+       GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA,
+       GL_RED, GL_RED, GL_RED, GL_ONE,
+       GL_RED, GL_RED, GL_RED, GL_GREEN,
+       GL_BLUE, GL_GREEN, GL_RED, GL_ALPHA
+};
+
+OpenGLTexture *OpenGLTexture::scratch_binding = 0;
+
+OpenGLTexture::OpenGLTexture(unsigned t, bool create):
+       id(0),
+       target(t)
+{
+       if(create)
+               generate_id();
+
+       static bool alignment_init = false;
+       if(!alignment_init)
+       {
+               glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+               alignment_init = true;
+       }
+}
+
+OpenGLTexture::~OpenGLTexture()
+{
+       if(this==scratch_binding)
+               unbind_scratch();
+       if(id)
+               glDeleteTextures(1, &id);
+}
+
+void OpenGLTexture::generate_id()
+{
+       if(id)
+               throw invalid_operation("OpenGLTexture::generate_id");
+       if(ARB_direct_state_access)
+               glCreateTextures(target, 1, &id);
+       else
+               glGenTextures(1, &id);
+
+#ifdef DEBUG
+       if(!debug_name.empty() && KHR_debug)
+               glObjectLabel(GL_TEXTURE, id, debug_name.size(), debug_name.c_str());
+#endif
+}
+
+void OpenGLTexture::require_swizzle()
+{
+       static Require _req(ARB_texture_swizzle);
+}
+
+void OpenGLTexture::apply_swizzle()
+{
+       Texture::FormatSwizzle swizzle = static_cast<const Texture *>(this)->swizzle;
+       if(swizzle==Texture::NO_SWIZZLE)
+               return;
+
+       if(get_backend_api()==OPENGL_ES)
+       {
+               set_parameter_i(GL_TEXTURE_SWIZZLE_R, swizzle_orders[swizzle*4]);
+               set_parameter_i(GL_TEXTURE_SWIZZLE_G, swizzle_orders[swizzle*4+1]);
+               set_parameter_i(GL_TEXTURE_SWIZZLE_B, swizzle_orders[swizzle*4+2]);
+               set_parameter_i(GL_TEXTURE_SWIZZLE_A, swizzle_orders[swizzle*4+3]);
+       }
+       else
+       {
+               if(ARB_direct_state_access)
+                       glTextureParameteriv(id, GL_TEXTURE_SWIZZLE_RGBA, swizzle_orders+swizzle*4);
+               else
+                       glTexParameteriv(target, GL_TEXTURE_SWIZZLE_RGBA, swizzle_orders+swizzle*4);
+       }
+}
+
+void OpenGLTexture::set_parameter_i(unsigned param, int value) const
+{
+       if(ARB_direct_state_access)
+               glTextureParameteri(id, param, value);
+       else
+               glTexParameteri(target, param, value);
+}
+
+void OpenGLTexture::generate_mipmap()
+{
+       // glGenerateMipmap is defined here
+       static Require _req(EXT_framebuffer_object);
+
+       if(ARB_direct_state_access)
+               glGenerateTextureMipmap(id);
+       else
+       {
+               bind_scratch();
+               glGenerateMipmap(target);
+       }
+}
+
+void OpenGLTexture::set_debug_name(const string &name)
+{
+#ifdef DEBUG
+       debug_name = name;
+       if(id && KHR_debug)
+               glObjectLabel(GL_TEXTURE, id, name.size(), name.c_str());
+#else
+       (void)name;
+#endif
+}
+
+void OpenGLTexture::bind_scratch()
+{
+       if(!scratch_binding)
+               glActiveTexture(GL_TEXTURE0);
+       if(scratch_binding!=this)
+       {
+               if(scratch_binding && scratch_binding->target!=target)
+                       glBindTexture(scratch_binding->target, 0);
+               glBindTexture(target, id);
+               scratch_binding = this;
+       }
+}
+
+void OpenGLTexture::unbind_scratch()
+{
+       if(scratch_binding)
+       {
+               glBindTexture(scratch_binding->target, 0);
+               scratch_binding = 0;
+       }
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/backends/opengl/texture_backend.h b/source/backends/opengl/texture_backend.h
new file mode 100644 (file)
index 0000000..f272d3d
--- /dev/null
@@ -0,0 +1,43 @@
+#ifndef MSP_GL_TEXTURE_BACKEND_H_
+#define MSP_GL_TEXTURE_BACKEND_H_
+
+#include <msp/core/noncopyable.h>
+
+namespace Msp {
+namespace GL {
+
+class OpenGLTexture: public NonCopyable
+{
+       friend class OpenGLFramebuffer;
+       friend class OpenGLPipelineState;
+
+protected:
+       unsigned id;
+       unsigned target;
+       std::string debug_name;
+
+       static int swizzle_orders[];
+       static OpenGLTexture *scratch_binding;
+
+       OpenGLTexture(unsigned, bool);
+       ~OpenGLTexture();
+
+       void generate_id();
+       void require_swizzle();
+       void apply_swizzle();
+       void set_parameter_i(unsigned, int) const;
+
+       void generate_mipmap();
+
+       void set_debug_name(const std::string &);
+
+       void bind_scratch();
+       static void unbind_scratch();
+};
+
+using TextureBackend = OpenGLTexture;
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/backends/opengl/texturecube_backend.cpp b/source/backends/opengl/texturecube_backend.cpp
new file mode 100644 (file)
index 0000000..b113f1b
--- /dev/null
@@ -0,0 +1,90 @@
+#include <msp/gl/extensions/arb_direct_state_access.h>
+#include <msp/gl/extensions/arb_seamless_cube_map.h>
+#include <msp/gl/extensions/arb_texture_cube_map.h>
+#include <msp/gl/extensions/arb_texture_storage.h>
+#include "gl.h"
+#include "texturecube.h"
+#include "texturecube_backend.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+OpenGLTextureCube::OpenGLTextureCube():
+       Texture(GL_TEXTURE_CUBE_MAP)
+{
+       static Require _req(ARB_texture_cube_map);
+       if(ARB_seamless_cube_map)
+       {
+               static bool seamless_init = false;
+               if(!seamless_init)
+               {
+                       glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
+                       seamless_init = true;
+               }
+       }
+}
+
+void OpenGLTextureCube::allocate()
+{
+       unsigned size = static_cast<const TextureCube *>(this)->size;
+       unsigned levels = static_cast<const TextureCube *>(this)->levels;
+
+       GLenum gl_fmt = get_gl_pixelformat(storage_fmt);
+       if(ARB_texture_storage)
+       {
+               if(ARB_direct_state_access)
+                       glTextureStorage2D(id, levels, gl_fmt, size, size);
+               else
+               {
+                       bind_scratch();
+                       glTexStorage2D(target, levels, gl_fmt, size, size);
+               }
+       }
+       else
+       {
+               bind_scratch();
+               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)
+               {
+                       unsigned lv_size = static_cast<const TextureCube *>(this)->get_level_size(i);
+                       for(unsigned j=0; j<6; ++j)
+                               glTexImage2D(get_gl_cube_face(j), i, gl_fmt, lv_size, lv_size, 0, comp, type, 0);
+               }
+               glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, levels-1);
+       }
+
+       apply_swizzle();
+}
+
+void OpenGLTextureCube::sub_image(unsigned face, 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)
+               glTextureSubImage3D(id, level, x, y, face, wd, ht, 1, comp, type, data);
+       else
+       {
+               bind_scratch();
+               glTexSubImage2D(get_gl_cube_face(face), level, x, y, wd, ht, comp, type, data);
+       }
+}
+
+unsigned get_gl_cube_face(unsigned face)
+{
+       switch(static_cast<TextureCubeFace>(face))
+       {
+       case POSITIVE_X: return GL_TEXTURE_CUBE_MAP_POSITIVE_X;
+       case NEGATIVE_X: return GL_TEXTURE_CUBE_MAP_NEGATIVE_X;
+       case POSITIVE_Y: return GL_TEXTURE_CUBE_MAP_POSITIVE_Y;
+       case NEGATIVE_Y: return GL_TEXTURE_CUBE_MAP_NEGATIVE_Y;
+       case POSITIVE_Z: return GL_TEXTURE_CUBE_MAP_POSITIVE_Z;
+       case NEGATIVE_Z: return GL_TEXTURE_CUBE_MAP_NEGATIVE_Z;
+       default: throw invalid_argument("get_gl_cube_face");
+       }
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/backends/opengl/texturecube_backend.h b/source/backends/opengl/texturecube_backend.h
new file mode 100644 (file)
index 0000000..2b789d2
--- /dev/null
@@ -0,0 +1,25 @@
+#ifndef MSP_GL_TEXTURECUBE_BACKEND_H_
+#define MSP_GL_TEXTURECUBE_BACKEND_H_
+
+#include "texture.h"
+
+namespace Msp {
+namespace GL {
+
+class OpenGLTextureCube: public Texture
+{
+protected:
+       OpenGLTextureCube();
+
+       void allocate();
+       void sub_image(unsigned, unsigned, int, int, unsigned, unsigned, const void *);
+};
+
+using TextureCubeBackend = OpenGLTextureCube;
+
+unsigned get_gl_cube_face(unsigned);
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/backends/opengl/uniformblock_backend.cpp b/source/backends/opengl/uniformblock_backend.cpp
new file mode 100644 (file)
index 0000000..f691bbe
--- /dev/null
@@ -0,0 +1,16 @@
+#include <msp/gl/extensions/arb_shader_objects.h>
+#include <msp/gl/extensions/arb_uniform_buffer_object.h>
+#include "uniformblock_backend.h"
+
+namespace Msp {
+namespace GL {
+
+OpenGLUniformBlock::OpenGLUniformBlock(bool ubo)
+{
+       static Require _req(ARB_shader_objects);
+       if(ubo)
+               static Require _req2(ARB_uniform_buffer_object);
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/backends/opengl/uniformblock_backend.h b/source/backends/opengl/uniformblock_backend.h
new file mode 100644 (file)
index 0000000..c95a348
--- /dev/null
@@ -0,0 +1,19 @@
+#ifndef MSP_GL_UNIFORMBLOCK_BACKEND_H_
+#define MSP_GL_UNIFORMBLOCK_BACKEND_H_
+
+namespace Msp {
+namespace GL {
+
+class OpenGLUniformBlock
+{
+protected:
+       OpenGLUniformBlock(bool);
+       ~OpenGLUniformBlock() { }
+};
+
+using UniformBlockBackend = OpenGLUniformBlock;
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/backends/opengl/vertexsetup_backend.cpp b/source/backends/opengl/vertexsetup_backend.cpp
new file mode 100644 (file)
index 0000000..90189db
--- /dev/null
@@ -0,0 +1,153 @@
+#include <algorithm>
+#include <msp/gl/extensions/arb_direct_state_access.h>
+#include <msp/gl/extensions/arb_instanced_arrays.h>
+#include <msp/gl/extensions/arb_vertex_array_object.h>
+#include <msp/gl/extensions/arb_vertex_attrib_binding.h>
+#include <msp/gl/extensions/arb_vertex_buffer_object.h>
+#include <msp/gl/extensions/arb_vertex_shader.h>
+#include <msp/gl/extensions/ext_gpu_shader4.h>
+#include <msp/gl/extensions/khr_debug.h>
+#include "buffer.h"
+#include "vertexarray.h"
+#include "vertexformat.h"
+#include "vertexsetup.h"
+#include "vertexsetup_backend.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+OpenGLVertexSetup::OpenGLVertexSetup()
+{
+       static Require req(ARB_vertex_array_object);
+       if(ARB_direct_state_access)
+               glCreateVertexArrays(1, &id);
+       else
+               glGenVertexArrays(1, &id);
+}
+
+OpenGLVertexSetup::~OpenGLVertexSetup()
+{
+       glDeleteVertexArrays(1, &id);
+}
+
+void OpenGLVertexSetup::require_format(const VertexFormat &fmt, bool instanced)
+{
+       if(any_of(fmt.begin(), fmt.end(), is_integer_attribute))
+               static Require _req(EXT_gpu_shader4);
+       if(instanced)
+               static Require req(ARB_instanced_arrays);
+}
+
+void OpenGLVertexSetup::update(unsigned mask) const
+{
+       static bool direct = ARB_direct_state_access && ARB_vertex_attrib_binding;
+
+       if(mask&VertexSetup::VERTEX_ARRAY)
+               update_vertex_array(*static_cast<const VertexSetup *>(this)->vertex_array, 0, 0, direct);
+
+       if(mask&VertexSetup::INSTANCE_ARRAY)
+               update_vertex_array(*static_cast<const VertexSetup *>(this)->inst_array, 1, 1, direct);
+
+       if(mask&VertexSetup::INDEX_BUFFER)
+       {
+               unsigned buf_id = static_cast<const VertexSetup *>(this)->index_buffer->id;
+               if(direct)
+                       glVertexArrayElementBuffer(id, buf_id);
+               else
+                       glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buf_id);
+       }
+}
+
+void OpenGLVertexSetup::update_vertex_array(const VertexArray &array, unsigned binding, unsigned divisor, bool direct) const
+{
+       if(!direct)
+       {
+               OpenGLBuffer::unbind_scratch();
+               glBindBuffer(GL_ARRAY_BUFFER, array.get_buffer()->id);
+       }
+
+       const VertexFormat &fmt = array.get_format();
+       unsigned stride = fmt.stride();
+       if(direct)
+       {
+               glVertexArrayVertexBuffer(id, binding, array.get_buffer()->id, 0, stride);
+               glVertexArrayBindingDivisor(id, binding, divisor);
+       }
+
+       unsigned offset = 0;
+       for(VertexAttribute a: fmt)
+       {
+               unsigned sem = get_attribute_semantic(a);
+               bool integer = is_integer_attribute(a);
+               GLenum type = get_gl_type(get_attribute_source_type(a));
+               unsigned cc = get_attribute_component_count(a);
+               if(direct)
+               {
+                       if(integer)
+                               glVertexArrayAttribIFormat(id, sem, cc, type, offset);
+                       else
+                               glVertexArrayAttribFormat(id, sem, cc, type, true, offset);
+                       glVertexArrayAttribBinding(id, sem, binding);
+                       glEnableVertexArrayAttrib(id, sem);
+               }
+               else
+               {
+                       if(integer)
+                               glVertexAttribIPointer(sem, cc, type, stride, reinterpret_cast<void *>(offset));
+                       else
+                               glVertexAttribPointer(sem, cc, type, true, stride, reinterpret_cast<void *>(offset));
+                       if(ARB_instanced_arrays)
+                               glVertexAttribDivisor(sem, divisor);
+                       glEnableVertexAttribArray(sem);
+               }
+               offset += get_attribute_size(a);
+       }
+
+       if(!direct)
+               glBindBuffer(GL_ARRAY_BUFFER, 0);
+}
+
+void OpenGLVertexSetup::unload()
+{
+       if(ARB_direct_state_access)
+       {
+               glVertexArrayVertexBuffer(id, 0, 0, 0, 0);
+               glVertexArrayVertexBuffer(id, 1, 0, 0, 0);
+               glVertexArrayElementBuffer(id, 0);
+       }
+       else
+       {
+               glBindVertexArray(id);
+               glBindBuffer(GL_ARRAY_BUFFER, 0);
+
+               for(VertexAttribute a: static_cast<const VertexSetup *>(this)->vertex_format)
+               {
+                       unsigned sem = get_attribute_semantic(a);
+                       glDisableVertexAttribArray(sem);
+                       glVertexAttribPointer(sem, 1, GL_FLOAT, false, 0, 0);
+               }
+               for(VertexAttribute a: static_cast<const VertexSetup *>(this)->inst_format)
+               {
+                       unsigned sem = get_attribute_semantic(a);
+                       glDisableVertexAttribArray(sem);
+                       glVertexAttribPointer(sem, 1, GL_FLOAT, false, 0, 0);
+               }
+
+               glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+       }
+}
+
+void OpenGLVertexSetup::set_debug_name(const string &name)
+{
+#ifdef DEBUG
+       if(KHR_debug)
+               glObjectLabel(GL_VERTEX_ARRAY, id, name.size(), name.c_str());
+#else
+       (void)name;
+#endif
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/backends/opengl/vertexsetup_backend.h b/source/backends/opengl/vertexsetup_backend.h
new file mode 100644 (file)
index 0000000..954dcc7
--- /dev/null
@@ -0,0 +1,34 @@
+#ifndef MSP_GL_VERTEXSETUP_BACKEND_H_
+#define MSP_GL_VERTEXSETUP_BACKEND_H_
+
+namespace Msp {
+namespace GL {
+
+class VertexArray;
+class VertexFormat;
+
+class OpenGLVertexSetup
+{
+       friend class OpenGLPipelineState;
+
+protected:
+       unsigned id;
+
+       OpenGLVertexSetup();
+       ~OpenGLVertexSetup();
+
+       static void require_format(const VertexFormat &, bool);
+       void update(unsigned) const;
+       void update_vertex_array(const VertexArray &, unsigned, unsigned, bool) const;
+
+       void unload();
+
+       void set_debug_name(const std::string &);
+};
+
+using VertexSetupBackend = OpenGLVertexSetup;
+
+} // namespace GL
+} // namespace Msp
+
+#endif
index dfa6bf9cf953685c2472c29b29788447e53e6642..f46475bcae69c84bc5497f9bd397669ddeefb48a 100644 (file)
@@ -1,7 +1,6 @@
 #include <msp/core/maputils.h>
 #include <msp/datafile/collection.h>
 #include <msp/fs/utils.h>
-#include "gl.h"
 #include "font.h"
 #include "primitivebuilder.h"
 #include "texture2d.h"
index c3e159f427ef339c4a9e46f4418cfb28e17f4ed1..ac9e22b7d2a5ebadc7606d7dc9e065862921c18e 100644 (file)
@@ -1,9 +1,6 @@
-#include <stdexcept>
-#include <cstdlib>
 #include <msp/strings/lexicalcast.h>
 #include <msp/strings/utils.h>
 #include "backend.h"
-#include "gl.h"
 
 using namespace std;
 
@@ -34,43 +31,5 @@ bool Version::operator>=(const Version &other) const
        return major>other.major || (major==other.major && minor>=other.minor);
 }
 
-
-GraphicsApi get_backend_api()
-{
-#ifdef GL_ES_VERSION_2_0
-       return OPENGL_ES;
-#else
-       return OPENGL;
-#endif
-}
-
-inline Version get_gl_version()
-{
-       const char *gl_ver_ptr = reinterpret_cast<const char *>(glGetString(GL_VERSION));
-       if(!gl_ver_ptr)
-               throw runtime_error("OpenGL version not available");
-
-       string gl_ver = gl_ver_ptr;
-       if(!gl_ver.compare(0, 10, "OpenGL ES "))
-               gl_ver.erase(0, 10);
-
-       Version ver(gl_ver.substr(0, gl_ver.find(' ')));
-
-       if(const char *force_ver_ptr = getenv("MSPGL_FORCE_VERSION"))
-       {
-               Version force_ver(force_ver_ptr);
-               if(force_ver<ver)
-                       ver = force_ver;
-       }
-
-       return ver;
-}
-
-const Version &get_backend_version()
-{
-       static Version version = get_gl_version();
-       return version;
-}
-
 } // namespace GL
 } // namespace Msp
index 48a49791370dab47de0e0490336aee24766843a7..fa28e9564d569267b322f8fead7a0761258b8045 100644 (file)
@@ -1,4 +1,3 @@
-#include <msp/gl/extensions/msp_primitive_restart.h>
 #include "batch.h"
 #include "error.h"
 
@@ -46,12 +45,13 @@ namespace Msp {
 namespace GL {
 
 Batch::Batch(PrimitiveType t):
+       BatchBackend(t),
        prim_type(t),
-       gl_prim_type(GL::get_gl_primitive_type(prim_type)),
-       index_type(UNSIGNED_SHORT),
-       gl_index_type(get_gl_type(index_type)),
+       index_type(VOID),
        max_index(0)
-{ }
+{
+       set_index_type(UNSIGNED_SHORT);
+}
 
 Batch::~Batch()
 {
@@ -72,7 +72,7 @@ void Batch::set_index_type(DataType t)
                shrink<uint32_t, uint16_t>(data);
 
        index_type = t;
-       gl_index_type = get_gl_type(t);
+       BatchBackend::set_index_type(t);
        update_offset();
        dirty = true;
 }
@@ -107,7 +107,7 @@ bool Batch::can_append(PrimitiveType other_type)
        if(other_type!=prim_type)
                return false;
        else if(prim_type==LINE_STRIP || prim_type==LINE_LOOP || prim_type==TRIANGLE_FAN)
-               return MSP_primitive_restart;
+               return check_restart(false);
        else
                return true;
 }
@@ -117,7 +117,7 @@ Batch &Batch::append(const Batch &other)
        if(other.prim_type!=prim_type)
                throw invalid_argument("Batch::append");
        if(prim_type==LINE_STRIP || prim_type==LINE_LOOP || prim_type==TRIANGLE_FAN)
-               static Require _req(MSP_primitive_restart);
+               check_restart(true);
 
        if(other.data.empty())
                return *this;
@@ -126,7 +126,7 @@ Batch &Batch::append(const Batch &other)
 
        if(prim_type==POINTS || prim_type==LINES || prim_type==TRIANGLES)
                ;
-       else if(MSP_primitive_restart)
+       else if(check_restart(false))
        {
                if(index_type==UNSIGNED_INT)
                        ::append<uint32_t>(data, 0xFFFFFFFF);
index e03435550aa48857632aff62d4624eb69f871866..9577a542cf2e1c069bca6187a6129d1c0c5a6559 100644 (file)
@@ -3,6 +3,7 @@
 
 #include <vector>
 #include <msp/datafile/objectloader.h>
+#include "batch_backend.h"
 #include "bufferable.h"
 #include "datatype.h"
 #include "primitivetype.h"
@@ -20,10 +21,8 @@ the Batch.
 This is a pretty low-level class and mainly intended to be used by the Mesh
 class.
 */
-class Batch: public Bufferable
+class Batch: public BatchBackend, public Bufferable
 {
-       friend class Commands;
-
 public:
        class Loader: public DataFile::ObjectLoader<Batch>
        {
@@ -35,9 +34,7 @@ public:
 
 private:
        PrimitiveType prim_type;
-       unsigned gl_prim_type;
        DataType index_type;
-       unsigned gl_index_type;
        std::vector<std::uint8_t> data;
        unsigned max_index;
 
index 8ceb23829a76894e978dfad03a1531c3421c215e..85d3e57d5d6bd773c77202089d9a6128532b9a4a 100644 (file)
@@ -1,5 +1,3 @@
-#include <msp/gl/extensions/ext_blend_minmax.h>
-#include <msp/gl/extensions/ext_blend_subtract.h>
 #include <msp/strings/format.h>
 #include <msp/strings/utils.h>
 #include "blend.h"
@@ -64,41 +62,6 @@ void Blend::Loader::factors(BlendFactor sf, BlendFactor df)
 }
 
 
-unsigned get_gl_blend_equation(BlendEquation eq)
-{
-       switch(eq)
-       {
-       case ADD: return GL_FUNC_ADD;
-       case SUBTRACT: return GL_FUNC_SUBTRACT;
-       case REVERSE_SUBTRACT: return GL_FUNC_REVERSE_SUBTRACT;
-       case MIN: return GL_MIN;
-       case MAX: return GL_MAX;
-       default: throw invalid_argument("get_gl_blend_equation");
-       }
-}
-
-unsigned get_gl_blend_factor(BlendFactor factor)
-{
-       switch(factor)
-       {
-       case ZERO: return GL_ZERO;
-       case ONE: return GL_ONE;
-       case SRC_COLOR: return GL_SRC_COLOR;
-       case ONE_MINUS_SRC_COLOR: return GL_ONE_MINUS_SRC_COLOR;
-       case SRC_ALPHA: return GL_SRC_ALPHA;
-       case ONE_MINUS_SRC_ALPHA: return GL_ONE_MINUS_SRC_ALPHA;
-       case DST_COLOR: return GL_DST_COLOR;
-       case ONE_MINUS_DST_COLOR: return GL_ONE_MINUS_DST_COLOR;
-       case DST_ALPHA: return GL_DST_ALPHA;
-       case ONE_MINUS_DST_ALPHA: return GL_ONE_MINUS_DST_ALPHA;
-       case CONSTANT_COLOR: return GL_CONSTANT_COLOR;
-       case ONE_MINUS_CONSTANT_COLOR: return GL_ONE_MINUS_CONSTANT_COLOR;
-       case CONSTANT_ALPHA: return GL_CONSTANT_ALPHA;
-       case ONE_MINUS_CONSTANT_ALPHA: return GL_ONE_MINUS_CONSTANT_ALPHA;
-       default: throw invalid_argument("get_gl_blend_factor");
-       }
-}
-
 void operator>>(const LexicalConverter &conv, BlendEquation &eq)
 {
        const string &str = conv.get();
index a1180050163b31dbd009acd1987d27c8507fb5b3..e04aef49c2474cc7be4c49382044518331a6c9a2 100644 (file)
@@ -77,9 +77,6 @@ struct Blend
 inline ColorWriteMask operator|(ColorWriteMask m1, ColorWriteMask m2)
 { return static_cast<ColorWriteMask>(static_cast<int>(m1)|static_cast<int>(m2)); }
 
-unsigned get_gl_blend_equation(BlendEquation);
-unsigned get_gl_blend_factor(BlendFactor);
-
 void operator>>(const LexicalConverter &, BlendEquation &);
 void operator<<(LexicalConverter &, BlendEquation);
 
@@ -92,4 +89,6 @@ void operator<<(LexicalConverter &, ColorWriteMask);
 } // namespace GL
 } // namespace Msp
 
+#include "blend_backend.h"
+
 #endif
index dcb83a7a5960f1cb48436cb9cff5063327c4d5f2..811b3e41d7cbfb9ed16e613b29a7c28674d07659 100644 (file)
@@ -1,10 +1,4 @@
 #include <stdexcept>
-#include <msp/gl/extensions/arb_buffer_storage.h>
-#include <msp/gl/extensions/arb_direct_state_access.h>
-#include <msp/gl/extensions/arb_map_buffer_range.h>
-#include <msp/gl/extensions/arb_vertex_buffer_object.h>
-#include <msp/gl/extensions/khr_debug.h>
-#include <msp/gl/extensions/oes_mapbuffer.h>
 #include <msp/strings/format.h>
 #include "buffer.h"
 #include "error.h"
@@ -14,25 +8,9 @@ using namespace std;
 namespace Msp {
 namespace GL {
 
-Buffer *Buffer::scratch_binding = 0;
-
 Buffer::Buffer():
        size(0)
-{
-       static Require _req(ARB_vertex_buffer_object);
-
-       if(ARB_direct_state_access)
-               glCreateBuffers(1, &id);
-       else
-               glGenBuffers(1, &id);
-}
-
-Buffer::~Buffer()
-{
-       if(this==scratch_binding)
-               unbind_scratch();
-       glDeleteBuffers(1, &id);
-}
+{ }
 
 void Buffer::storage(unsigned sz)
 {
@@ -47,24 +25,7 @@ void Buffer::storage(unsigned sz)
 
        size = sz;
 
-       if(ARB_buffer_storage)
-       {
-               static const int flags = GL_MAP_READ_BIT|GL_MAP_WRITE_BIT|GL_DYNAMIC_STORAGE_BIT;
-               if(ARB_direct_state_access)
-                       glNamedBufferStorage(id, size, 0, flags);
-               else
-               {
-                       bind_scratch();
-                       glBufferStorage(GL_ARRAY_BUFFER, size, 0, flags);
-               }
-       }
-       else if(ARB_direct_state_access)
-               glNamedBufferData(id, size, 0, GL_STATIC_DRAW);
-       else
-       {
-               bind_scratch();
-               glBufferData(GL_ARRAY_BUFFER, size, 0, GL_STATIC_DRAW);
-       }
+       allocate();
 }
 
 void Buffer::data(const void *d)
@@ -77,13 +38,7 @@ void Buffer::sub_data(unsigned off, unsigned sz, const void *d)
        if(size==0)
                throw invalid_operation("Buffer::sub_data");
 
-       if(ARB_direct_state_access)
-               glNamedBufferSubData(id, off, sz, d);
-       else
-       {
-               bind_scratch();
-               glBufferSubData(GL_ARRAY_BUFFER, off, sz, d);
-       }
+       BufferBackend::sub_data(off, sz, d);
 }
 
 void Buffer::require_size(unsigned req_sz) const
@@ -92,62 +47,5 @@ void Buffer::require_size(unsigned req_sz) const
                throw buffer_too_small(format("buffer has %d bytes; %d required", size, req_sz));
 }
 
-void *Buffer::map()
-{
-       static Require _req(ARB_map_buffer_range);
-
-       if(ARB_direct_state_access)
-               return glMapNamedBufferRange(id, 0, size, GL_MAP_READ_BIT|GL_MAP_WRITE_BIT);
-       else
-       {
-               bind_scratch();
-               void *result = glMapBufferRange(GL_ARRAY_BUFFER, 0, size, GL_MAP_READ_BIT|GL_MAP_WRITE_BIT);
-               return result;
-       }
-}
-
-bool Buffer::unmap()
-{
-       // TODO check if it's mapped
-       if(ARB_direct_state_access)
-               return glUnmapNamedBuffer(id);
-       else if(OES_mapbuffer)
-       {
-               bind_scratch();
-               bool result = glUnmapBuffer(GL_ARRAY_BUFFER);
-               return result;
-       }
-       else
-               return true;
-}
-
-void Buffer::set_debug_name(const string &name)
-{
-#ifdef DEBUG
-       if(KHR_debug)
-               glObjectLabel(GL_BUFFER, id, name.size(), name.c_str());
-#else
-       (void)name;
-#endif
-}
-
-void Buffer::bind_scratch()
-{
-       if(scratch_binding!=this)
-       {
-               glBindBuffer(GL_ARRAY_BUFFER, id);
-               scratch_binding = this;
-       }
-}
-
-void Buffer::unbind_scratch()
-{
-       if(scratch_binding)
-       {
-               glBindBuffer(GL_ARRAY_BUFFER, 0);
-               scratch_binding = 0;
-       }
-}
-
 } // namespace GL
 } // namespace Msp
index df570d7eb01d2b8bae9f6115adfb2345435c4c53..9737a2ea9323ddd0af5c44b3d51d595eff6fc6d1 100644 (file)
@@ -3,6 +3,7 @@
 
 #include <stdexcept>
 #include <string>
+#include "buffer_backend.h"
 
 namespace Msp {
 namespace GL {
@@ -21,21 +22,15 @@ A buffer for storing data in GL memory.  Putting vertex and index data in
 buffers can improve rendering performance.  The VertexArray, Mesh and
 UniformBlock classes contain built-in support for buffers.
 */
-class Buffer
+class Buffer: public BufferBackend
 {
-       friend class PipelineState;
-       friend class Texture2D;
-       friend class VertexSetup;
+       friend BufferBackend;
 
 private:
-       unsigned id;
        unsigned size;
 
-       static Buffer *scratch_binding;
-
 public:
        Buffer();
-       ~Buffer();
 
        /** Defines the storage size of the buffer.  Must be called before data can
        be uploaded.  Storage cannot be changed once set. */
@@ -54,15 +49,10 @@ public:
 
        void require_size(unsigned) const;
 
-       void *map();
-       bool unmap();
-
-       void set_debug_name(const std::string &);
+       using BufferBackend::map;
+       using BufferBackend::unmap;
 
-private:
-       void bind_scratch();
-public:
-       static void unbind_scratch();
+       using BufferBackend::set_debug_name;
 };
 
 } // namespace GL
diff --git a/source/core/commands.cpp b/source/core/commands.cpp
deleted file mode 100644 (file)
index ebc6394..0000000
+++ /dev/null
@@ -1,125 +0,0 @@
-#include <algorithm>
-#include <msp/gl/extensions/arb_direct_state_access.h>
-#include <msp/gl/extensions/arb_draw_instanced.h>
-#include <msp/gl/extensions/arb_occlusion_query.h>
-#include <msp/gl/extensions/ext_framebuffer_blit.h>
-#include <msp/gl/extensions/ext_framebuffer_object.h>
-#include <msp/gl/extensions/msp_clear_buffer.h>
-#include "batch.h"
-#include "commands.h"
-#include "error.h"
-#include "gl.h"
-#include "pipelinestate.h"
-#include "query.h"
-
-using namespace std;
-
-namespace Msp {
-namespace GL {
-
-Commands::Commands():
-       pipeline_state(0)
-{ }
-
-void Commands::use_pipeline(const PipelineState *ps)
-{
-       pipeline_state = ps;
-       if(!pipeline_state)
-               PipelineState::clear();
-}
-
-void Commands::clear(const ClearValue *values)
-{
-       const Framebuffer *target = pipeline_state->get_framebuffer();
-       if(!target)
-               throw invalid_operation("OpenGLCommands::clear");
-
-       if(!ARB_direct_state_access)
-       {
-               static Require _req(MSP_clear_buffer);
-               pipeline_state->apply();
-       }
-
-       unsigned i = 0;
-       for(FrameAttachment a: target->get_format())
-       {
-               if(get_attach_point(a)==get_attach_point(DEPTH_ATTACHMENT))
-               {
-                       if(ARB_direct_state_access)
-                               glClearNamedFramebufferfv(target->id, GL_DEPTH, 0, &values->depth_stencil.depth);
-                       else
-                               glClearBufferfv(GL_DEPTH, 0, &values->depth_stencil.depth);
-               }
-               else if(get_attach_point(a)==get_attach_point(STENCIL_ATTACHMENT))
-               {
-                       if(ARB_direct_state_access)
-                               glClearNamedFramebufferiv(target->id, GL_STENCIL, 0, &values->depth_stencil.stencil);
-                       else
-                               glClearBufferiv(GL_STENCIL, 0, &values->depth_stencil.stencil);
-               }
-               else
-               {
-                       if(ARB_direct_state_access)
-                               glClearNamedFramebufferfv(target->id, GL_COLOR, i++, &values->color.r);
-                       else
-                               glClearBufferfv(GL_COLOR, i++, &values->color.r);
-               }
-               ++values;
-       }
-}
-
-void Commands::draw(const Batch &batch)
-{
-       pipeline_state->apply();
-       void *data_ptr = reinterpret_cast<void *>(batch.get_offset());
-       glDrawElements(batch.gl_prim_type, batch.size(), batch.gl_index_type, data_ptr);
-}
-
-void Commands::draw_instanced(const Batch &batch, unsigned count)
-{
-       static Require req(ARB_draw_instanced);
-
-       pipeline_state->apply();
-       void *data_ptr = reinterpret_cast<void *>(batch.get_offset());
-       glDrawElementsInstanced(batch.gl_prim_type, batch.size(), batch.gl_index_type, data_ptr, count);
-}
-
-void Commands::resolve_multisample(Framebuffer &target)
-{
-       static Require _req(EXT_framebuffer_blit);
-
-       const Framebuffer *source = pipeline_state->get_framebuffer();
-
-       unsigned width = min(source->get_width(), target.get_width());
-       unsigned height = min(source->get_height(), target.get_height());
-       unsigned buffers = get_gl_buffer_bits(source->get_format())&get_gl_buffer_bits(target.get_format());
-
-       if(ARB_direct_state_access)
-               glBlitNamedFramebuffer(source->id, target.id, 0, 0, width, height, 0, 0, width, height, buffers, GL_NEAREST);
-       else
-       {
-               glBindFramebuffer(GL_READ_FRAMEBUFFER, source->id);
-               glBindFramebuffer(GL_DRAW_FRAMEBUFFER, target.id);
-
-               target.refresh();
-
-               glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, buffers, GL_NEAREST);
-
-               glBindFramebuffer(GL_FRAMEBUFFER, source->id);
-       }
-}
-
-void Commands::begin_query(const QueryPool &pool, unsigned index)
-{
-       if(index>=pool.queries.size())
-               throw out_of_range("OpenGLCommands::begin_query");
-       glBeginQuery(pool.gl_type, pool.queries[index]);
-}
-
-void Commands::end_query(const QueryPool &pool, unsigned)
-{
-       glEndQuery(pool.gl_type);
-}
-
-} // namespace GL
-} // namespace Msp
index d290e34ecdfe8a5a4f4f14d4ec7fb51136c85402..869c41b615ea9f66a206156c37e7c586a7157971 100644 (file)
@@ -1,31 +1,23 @@
 #ifndef MSP_GL_COMMANDS_H_
 #define MSP_GL_COMMANDS_H_
 
-#include "framebuffer.h"
+#include "commands_backend.h"
 
 namespace Msp {
 namespace GL {
 
-class Batch;
-class PipelineState;
-class QueryPool;
-
-class Commands
+class Commands: public CommandsBackend
 {
-private:
-       const PipelineState *pipeline_state;
-
 public:
-       Commands();
+       using CommandsBackend::use_pipeline;
 
-       void use_pipeline(const PipelineState *);
-       void clear(const ClearValue *);
-       void draw(const Batch &);
-       void draw_instanced(const Batch &, unsigned);
-       void resolve_multisample(Framebuffer &);
+       using CommandsBackend::clear;
+       using CommandsBackend::draw;
+       using CommandsBackend::draw_instanced;
+       using CommandsBackend::resolve_multisample;
 
-       void begin_query(const QueryPool &, unsigned);
-       void end_query(const QueryPool &, unsigned);
+       using CommandsBackend::begin_query;
+       using CommandsBackend::end_query;
 };
 
 } // namespace GL
diff --git a/source/core/datatype.cpp b/source/core/datatype.cpp
deleted file mode 100644 (file)
index 4dd0b89..0000000
+++ /dev/null
@@ -1,117 +0,0 @@
-#include <algorithm>
-#include <stdexcept>
-#include <msp/gl/extensions/arb_gpu_shader_fp64.h>
-#include <msp/gl/extensions/nv_half_float.h>
-#include <msp/gl/extensions/nv_non_square_matrices.h>
-#include "datatype.h"
-
-using namespace std;
-
-namespace {
-
-struct MappedType
-{
-       Msp::GL::DataType type;
-       GLenum gl_type;
-};
-
-// Make sure this is sorted!
-const MappedType type_map[] =
-{
-       { Msp::GL::UNSIGNED_BYTE, GL_UNSIGNED_BYTE },
-       { Msp::GL::UNSIGNED_SHORT, GL_UNSIGNED_SHORT },
-       { Msp::GL::UNSIGNED_INT, GL_UNSIGNED_INT },
-       { Msp::GL::BYTE, GL_BYTE },
-       { Msp::GL::SHORT, GL_SHORT },
-       { Msp::GL::INT, GL_INT },
-       { Msp::GL::HALF_FLOAT, GL_HALF_FLOAT },
-       { Msp::GL::FLOAT, GL_FLOAT },
-       { Msp::GL::DOUBLE, GL_DOUBLE },
-       { Msp::GL::BOOL, GL_BOOL },
-       { Msp::GL::INT_VEC2, GL_INT_VEC2 },
-       { Msp::GL::FLOAT_VEC2, GL_FLOAT_VEC2 },
-       { Msp::GL::BOOL_VEC2, GL_BOOL_VEC2 },
-       { Msp::GL::INT_VEC3, GL_INT_VEC3 },
-       { Msp::GL::FLOAT_VEC3, GL_FLOAT_VEC3 },
-       { Msp::GL::BOOL_VEC3, GL_BOOL_VEC3 },
-       { Msp::GL::INT_VEC4, GL_INT_VEC4 },
-       { Msp::GL::FLOAT_VEC4, GL_FLOAT_VEC4 },
-       { Msp::GL::BOOL_VEC4, GL_BOOL_VEC4 },
-       { Msp::GL::FLOAT_MAT2, GL_FLOAT_MAT2 },
-       { Msp::GL::DOUBLE_MAT2, GL_DOUBLE_MAT2 },
-       { Msp::GL::FLOAT_MAT3, GL_FLOAT_MAT3 },
-       { Msp::GL::DOUBLE_MAT3, GL_DOUBLE_MAT3 },
-       { Msp::GL::FLOAT_MAT4, GL_FLOAT_MAT4 },
-       { Msp::GL::DOUBLE_MAT4, GL_DOUBLE_MAT4 },
-       { Msp::GL::FLOAT_MAT2x3, GL_FLOAT_MAT2x3 },
-       { Msp::GL::DOUBLE_MAT2x3, GL_DOUBLE_MAT2x3 },
-       { Msp::GL::FLOAT_MAT3x2, GL_FLOAT_MAT3x2 },
-       { Msp::GL::DOUBLE_MAT3x2, GL_DOUBLE_MAT3x2 },
-       { Msp::GL::FLOAT_MAT2x4, GL_FLOAT_MAT2x4 },
-       { Msp::GL::DOUBLE_MAT2x4, GL_DOUBLE_MAT2x4 },
-       { Msp::GL::FLOAT_MAT4x2, GL_FLOAT_MAT4x2 },
-       { Msp::GL::DOUBLE_MAT4x2, GL_DOUBLE_MAT4x2 },
-       { Msp::GL::FLOAT_MAT3x4, GL_FLOAT_MAT3x4 },
-       { Msp::GL::DOUBLE_MAT3x4, GL_DOUBLE_MAT3x4 },
-       { Msp::GL::FLOAT_MAT4x3, GL_FLOAT_MAT4x3 },
-       { Msp::GL::DOUBLE_MAT4x3, GL_DOUBLE_MAT4x3 },
-       { Msp::GL::IMAGE_1D, GL_IMAGE_1D },
-       { Msp::GL::IMAGE_2D, GL_IMAGE_2D },
-       { Msp::GL::IMAGE_3D, GL_IMAGE_3D },
-       { Msp::GL::IMAGE_CUBE, GL_IMAGE_CUBE },
-       { Msp::GL::IMAGE_1D_ARRAY, GL_IMAGE_1D_ARRAY },
-       { Msp::GL::IMAGE_2D_ARRAY, GL_IMAGE_2D_ARRAY },
-       { Msp::GL::IMAGE_CUBE_ARRAY, GL_IMAGE_CUBE_MAP_ARRAY },
-       { Msp::GL::SAMPLER_1D, GL_SAMPLER_1D },
-       { Msp::GL::SAMPLER_2D, GL_SAMPLER_2D },
-       { Msp::GL::SAMPLER_3D, GL_SAMPLER_3D },
-       { Msp::GL::SAMPLER_CUBE, GL_SAMPLER_CUBE },
-       { Msp::GL::SAMPLER_1D_ARRAY, GL_SAMPLER_1D_ARRAY },
-       { Msp::GL::SAMPLER_2D_ARRAY, GL_SAMPLER_2D_ARRAY },
-       { Msp::GL::SAMPLER_CUBE_ARRAY, GL_SAMPLER_CUBE_MAP_ARRAY },
-       { Msp::GL::SAMPLER_1D_SHADOW, GL_SAMPLER_1D_SHADOW },
-       { Msp::GL::SAMPLER_2D_SHADOW, GL_SAMPLER_2D_SHADOW },
-       { Msp::GL::SAMPLER_CUBE_SHADOW, GL_SAMPLER_CUBE_SHADOW },
-       { Msp::GL::SAMPLER_1D_ARRAY_SHADOW, GL_SAMPLER_1D_ARRAY_SHADOW },
-       { Msp::GL::SAMPLER_2D_ARRAY_SHADOW, GL_SAMPLER_2D_ARRAY_SHADOW },
-       { Msp::GL::SAMPLER_CUBE_ARRAY_SHADOW, GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW }
-};
-const unsigned type_map_size = sizeof(type_map)/sizeof(MappedType);
-
-bool type_compare(const MappedType &mt, Msp::GL::DataType t)
-{ return mt.type<t; }
-
-}
-
-namespace Msp {
-namespace GL {
-
-GLenum get_gl_type(DataType type)
-{
-       const MappedType *end = type_map+type_map_size;
-       const MappedType *ptr = lower_bound(type_map, end, type, type_compare);
-       if(ptr==end || ptr->type!=type)
-               throw invalid_argument("get_gl_type");
-       return ptr->gl_type;
-}
-
-DataType from_gl_type(GLenum gl_type)
-{
-       for(unsigned i=0; i<type_map_size; ++i)
-               if(type_map[i].gl_type==gl_type)
-                       return type_map[i].type;
-       throw invalid_argument("from_gl_type");
-}
-
-void require_type(DataType type)
-{
-       unsigned rows = ((type>>12)&3)+1;
-       unsigned cols = ((type>>14)&4)+1;
-       if(rows>1 && cols>1 && rows!=cols)
-               static Require _req(NV_non_square_matrices);
-       if((type&0x200) && get_type_size(type)/(rows*cols)==8)
-               static Require _req(ARB_gpu_shader_fp64);
-}
-
-} // namespace GL
-} // namespace Msp
index 1620dc58933d7fe1b5f9ae130ab7bff7fa104c11..8111dd77f634b341f7c60cab6b1e4fc1533ca727 100644 (file)
@@ -132,12 +132,11 @@ struct TypeTraits<LinAl::Matrix<T, N, M>>
        static const DataType type = static_cast<DataType>((TypeTraits<T>::type&0xF00) | ((TypeTraits<T>::type&0xFF)*N*M) | ((N-1)<<12) | ((M-1)<<14));
 };
 
-unsigned get_gl_type(DataType);
-DataType from_gl_type(unsigned);
-
 void require_type(DataType);
 
 } // namespace GL
 } // namespace Msp
 
+#include "datatype_backend.h"
+
 #endif
index 37a04f8a86a4fe058611cb886f1d32954bc2acd9..8400ffdf0d344c448d3ed69eb132dc5f17c2f97e 100644 (file)
@@ -1,49 +1,8 @@
-#include <msp/gl/extensions/arb_enhanced_layouts.h>
-#include <msp/gl/extensions/arb_explicit_attrib_location.h>
-#include <msp/gl/extensions/arb_explicit_uniform_location.h>
-#include <msp/gl/extensions/arb_gpu_shader5.h>
-#include <msp/gl/extensions/arb_separate_shader_objects.h>
-#include <msp/gl/extensions/arb_uniform_buffer_object.h>
-#include <msp/gl/extensions/arb_vertex_shader.h>
-#include <msp/gl/extensions/ext_framebuffer_multisample.h>
-#include <msp/gl/extensions/ext_gpu_shader4.h>
-#include <msp/gl/extensions/ext_texture_array.h>
-#include <msp/gl/extensions/msp_clipping.h>
-#include <msp/gl/extensions/nv_fbo_color_attachments.h>
 #include "deviceinfo.h"
-#include "gl.h"
 
 namespace Msp {
 namespace GL {
 
-Limits::Limits()
-{
-       glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, reinterpret_cast<int *>(&max_vertex_attributes));
-       glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, reinterpret_cast<int *>(&max_texture_bindings));
-       glGetIntegerv(GL_MAX_UNIFORM_BUFFER_BINDINGS, reinterpret_cast<int *>(&max_uniform_bindings));
-       glGetIntegerv(GL_MAX_CLIP_PLANES, reinterpret_cast<int *>(&max_clip_planes));
-       glGetIntegerv(GL_MAX_SAMPLES, reinterpret_cast<int *>(&max_samples));
-       glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, reinterpret_cast<int *>(&uniform_buffer_alignment));
-       glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, reinterpret_cast<int *>(&max_color_attachments));
-}
-
-
-DeviceInfo::DeviceInfo()
-{
-       glsl_features.target_api = get_backend_api();
-       glsl_features.glsl_version = get_glsl_version();
-       glsl_features.arb_enhanced_layouts = ARB_enhanced_layouts;
-       glsl_features.arb_explicit_attrib_location = ARB_explicit_attrib_location;
-       glsl_features.arb_explicit_uniform_location = ARB_explicit_uniform_location;
-       glsl_features.arb_gpu_shader5 = ARB_gpu_shader5;
-       glsl_features.arb_separate_shader_objects = ARB_separate_shader_objects;
-       glsl_features.arb_uniform_buffer_object = ARB_uniform_buffer_object;
-       glsl_features.ext_gpu_shader4 = EXT_gpu_shader4;
-       glsl_features.ext_texture_array = EXT_texture_array;
-       glsl_features.uniform_binding_range = limits.max_uniform_bindings;
-       glsl_features.texture_binding_range = limits.max_texture_bindings;
-}
-
 const DeviceInfo &DeviceInfo::get_global()
 {
        static DeviceInfo info;
diff --git a/source/core/extension.cpp b/source/core/extension.cpp
deleted file mode 100644 (file)
index a55da9b..0000000
+++ /dev/null
@@ -1,195 +0,0 @@
-#include <set>
-#include <cstdlib>
-#if defined(__ANDROID__)
-#include <EGL/egl.h>
-#elif defined(_WIN32)
-#include <windows.h>
-#elif !defined(__APPLE__)
-#define GLX_GLXEXT_PROTOTYPES
-#include <GL/glx.h>
-#endif
-#include <msp/strings/format.h>
-#include <msp/strings/utils.h>
-#include "error.h"
-#include "extension.h"
-#include "gl.h"
-
-#ifndef GL_VERSION_3_0
-#define GL_NUM_EXTENSIONS 0x821D
-#endif
-
-#ifndef GL_VERSION_3_2
-#define GL_CONTEXT_CORE_PROFILE_BIT 0x00000001
-#define GL_CONTEXT_PROFILE_MASK 0x9126
-#endif
-
-using namespace std;
-
-namespace Msp {
-namespace GL {
-
-Extension::Extension(const char *n, InitFunc f):
-       name(n),
-       init_func(f),
-       init_done(false),
-       support(UNSUPPORTED)
-{ }
-
-Extension::operator bool() const
-{
-       if(!init_done)
-       {
-               support = init_func();
-               init_done = true;
-       }
-
-       return support>UNSUPPORTED;
-}
-
-
-Require::Require(const Extension &ext)
-{
-       if(!ext)
-               throw unsupported_extension(ext.get_name());
-}
-
-
-bool is_supported(const string &ext)
-{
-       if(is_disabled(ext))
-               return false;
-
-       static set<string> extensions;
-       static bool init_done = false;
-
-       if(!init_done)
-       {
-               if(get_backend_api()==OPENGL && get_backend_version()>=Version(3, 0))
-               {
-                       typedef GLubyte *(APIENTRY *FPtr_glGetStringi)(GLenum, GLuint);
-                       FPtr_glGetStringi glGetStringi = reinterpret_cast<FPtr_glGetStringi>(get_proc_address("glGetStringi"));
-                       int n_extensions;
-                       glGetIntegerv(GL_NUM_EXTENSIONS, &n_extensions);
-                       for(int i=0; i<n_extensions; ++i)
-                               extensions.insert(reinterpret_cast<const char *>(glGetStringi(GL_EXTENSIONS, i)));
-               }
-               else
-               {
-                       if(const char *gl_ext = reinterpret_cast<const char *>(glGetString(GL_EXTENSIONS)))
-                       {
-                               vector<string> exts = split(gl_ext);
-                               extensions.insert(exts.begin(), exts.end());
-                       }
-               }
-
-               init_done = true;
-       }
-
-       return extensions.count(ext);
-}
-
-bool is_supported(const Version &core_version, const Version &deprecated_version)
-{
-       const Version &version = get_backend_version();
-       if(deprecated_version && version>=deprecated_version && get_gl_profile()==CORE_PROFILE)
-               return false;
-       return (version>=core_version);
-}
-
-bool is_disabled(const string &ext)
-{
-       static set<string> disabled_exts;
-       static bool init_done = false;
-
-       if(!init_done)
-       {
-               if(const char *disable_ptr = getenv("MSPGL_DISABLE_EXTENSIONS"))
-               {
-                       vector<string> disable = split(disable_ptr);
-                       disabled_exts.insert(disable.begin(), disable.end());
-               }
-
-               if(const char *renderer_ptr = reinterpret_cast<const char *>(glGetString(GL_RENDERER)))
-               {
-                       string renderer = renderer_ptr;
-                       if(renderer.find("Radeon")!=string::npos || renderer.find("AMD")!=string::npos)
-                       {
-                               // The core primitive restart feature does not work either.
-                               disabled_exts.insert("GL_MSP_primitive_restart");
-
-                               /* AMD's uniform buffer objects only work with the core version of
-                               shaders. */
-                               if(get_backend_version()<Version(2, 0))
-                                       disabled_exts.insert("GL_ARB_uniform_buffer_object");
-                       }
-               }
-
-               init_done = true;
-       }
-
-       return disabled_exts.count(ext);
-}
-
-inline GLProfile _get_gl_profile()
-{
-       if(get_backend_api()==OPENGL && get_backend_version()>=Version(3, 0))
-       {
-               int mask;
-               glGetIntegerv(GL_CONTEXT_PROFILE_MASK, &mask);
-               if(mask==GL_CONTEXT_CORE_PROFILE_BIT)
-                       return CORE_PROFILE;
-       }
-
-       return COMPATIBILITY_PROFILE;
-}
-
-GLProfile get_gl_profile()
-{
-       static GLProfile profile = _get_gl_profile();
-       return profile;
-}
-
-inline Version _get_glsl_version()
-{
-       const char *glsl_ver_ptr = reinterpret_cast<const char *>(glGetString(GL_SHADING_LANGUAGE_VERSION));
-       if(!glsl_ver_ptr)
-               throw runtime_error("GLSL version not available");
-
-       string glsl_ver = glsl_ver_ptr;
-       if(!glsl_ver.compare(0, 18, "OpenGL ES GLSL ES "))
-               glsl_ver.erase(0, 18);
-
-       Version ver(glsl_ver.substr(0, glsl_ver.find(' ')));
-
-       if(const char *force_ver_ptr = getenv("MSPGL_FORCE_GLSL_VERSION"))
-       {
-               Version force_ver(force_ver_ptr);
-               if(force_ver<ver)
-                       ver = force_ver;
-       }
-
-       return ver;
-}
-
-const Version &get_glsl_version()
-{
-       static Version version = _get_glsl_version();
-       return version;
-}
-
-ExtFunc *get_proc_address(const string &name)
-{
-#if defined(_WIN32)
-       return reinterpret_cast<ExtFunc *>(wglGetProcAddress(name.c_str()));
-#elif defined(__APPLE__)
-       (void)name;
-       return 0;  // Not supported
-#elif defined(__ANDROID__)
-       return eglGetProcAddress(name.c_str());
-#else
-       return glXGetProcAddressARB(reinterpret_cast<const unsigned char *>(name.c_str()));
-#endif
-}
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/core/extension.h b/source/core/extension.h
deleted file mode 100644 (file)
index 7799635..0000000
+++ /dev/null
@@ -1,79 +0,0 @@
-#ifndef MSP_GL_EXTENSION_H_
-#define MSP_GL_EXTENSION_H_
-
-#include <string>
-#include "backend.h"
-
-namespace Msp {
-namespace GL {
-
-enum GLProfile
-{
-       CORE_PROFILE,
-       COMPATIBILITY_PROFILE
-};
-
-
-/**
-Holds metadata about an extension.  Evaluates to true if the extension is
-supported.
-*/
-class Extension
-{
-public:
-       enum SupportLevel
-       {
-               UNSUPPORTED,
-               EXTENSION,
-               CORE
-       };
-
-       typedef SupportLevel (*InitFunc)();
-
-private:
-       const char *name;
-       InitFunc init_func;
-       mutable bool init_done;
-       mutable SupportLevel support;
-
-public:
-       Extension(const char *, InitFunc);
-
-       const char *get_name() const { return name; }
-       operator bool() const;
-};
-
-
-struct Require
-{
-       Require(const Extension &);
-};
-
-
-typedef void ExtFunc();
-
-/** Checks for extension support.  Only intended for internal use. */
-bool is_supported(const std::string &);
-
-/** Checks for OpenGL version support.  Only intended for internal use. */
-bool is_supported(const Version &, const Version & = Version());
-
-/** Indicates whether an extension has been disabled, either explicitly through
-the MSPGL_DISABLE_EXTENSIONS environment variable or implicitly as a workaround
-for a driver bug.  Only intended for internal use. */
-bool is_disabled(const std::string &);
-
-/** Returns the OpenGL profile for the active context. */
-GLProfile get_gl_profile();
-
-/** Returns the GLSL version number, as reported by the implementation. */
-const Version &get_glsl_version();
-
-/** Returns the address of an extension function.  Only indended for internal
-use. */
-ExtFunc *get_proc_address(const std::string &);
-
-} // namespace GL
-} // namespace Msp
-
-#endif
index 53234cc74696478a630acc11b7fcdcfbfe4e11c9..167b403aec18e142548c9f02f775e453e71e6cb0 100644 (file)
@@ -1,14 +1,3 @@
-#include <msp/gl/extensions/arb_draw_buffers.h>
-#include <msp/gl/extensions/arb_direct_state_access.h>
-#include <msp/gl/extensions/arb_geometry_shader4.h>
-#include <msp/gl/extensions/arb_internalformat_query.h>
-#include <msp/gl/extensions/arb_internalformat_query2.h>
-#include <msp/gl/extensions/ext_framebuffer_object.h>
-#include <msp/gl/extensions/ext_texture_array.h>
-#include <msp/gl/extensions/ext_texture3d.h>
-#include <msp/gl/extensions/msp_buffer_control.h>
-#include <msp/gl/extensions/khr_debug.h>
-#include <msp/strings/format.h>
 #include "error.h"
 #include "framebuffer.h"
 #include "texture2d.h"
@@ -26,50 +15,32 @@ framebuffer_incomplete::framebuffer_incomplete(const std::string &reason):
 { }
 
 
-Framebuffer::Framebuffer(unsigned i):
-       id(i),
-       status(GL_FRAMEBUFFER_COMPLETE),
+Framebuffer::Framebuffer(bool s):
+       FramebufferBackend(s),
        dirty(0)
 {
-       if(id)
-               throw invalid_argument("System framebuffer must have id 0");
-
-       if(EXT_framebuffer_object)
+       if(s)
        {
-               int value;
-               glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_BACK, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &value);
-               if(value==GL_NONE)
-                       glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_FRONT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &value);
-               if(value!=GL_NONE)
-                       format = (format,COLOR_ATTACHMENT);
-
-               glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_DEPTH, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &value);
-               if(value!=GL_NONE)
-                       format = (format,DEPTH_ATTACHMENT);
-
-               glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_STENCIL, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &value);
-               if(value!=GL_NONE)
-                       format = (format,STENCIL_ATTACHMENT);
+               format = get_system_format();
+               get_system_size(width, height);
        }
-
-       int view[4];
-       glGetIntegerv(GL_VIEWPORT, view);
-       width = view[2];
-       height = view[3];
 }
 
-Framebuffer::Framebuffer()
+Framebuffer::Framebuffer():
+       FramebufferBackend(false)
 {
        init();
 }
 
-Framebuffer::Framebuffer(FrameAttachment fa)
+Framebuffer::Framebuffer(FrameAttachment fa):
+       FramebufferBackend(false)
 {
        init();
        set_format(fa);
 }
 
-Framebuffer::Framebuffer(const FrameFormat &f)
+Framebuffer::Framebuffer(const FrameFormat &f):
+       FramebufferBackend(false)
 {
        init();
        set_format(f);
@@ -77,119 +48,25 @@ Framebuffer::Framebuffer(const FrameFormat &f)
 
 void Framebuffer::init()
 {
-       static Require _req(EXT_framebuffer_object);
-
        width = 0;
        height = 0;
-       status = GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT;
        dirty = 0;
-
-       if(ARB_direct_state_access)
-               glCreateFramebuffers(1, &id);
-       else
-               glGenFramebuffers(1, &id);
-}
-
-Framebuffer::~Framebuffer()
-{
-       if(id)
-               glDeleteFramebuffers(1, &id);
 }
 
 void Framebuffer::set_format(const FrameFormat &fmt)
 {
        if(!format.empty() || !id)
                throw invalid_operation("Framebuffer::set_format");
-       if(fmt.empty())
+       if(fmt.empty() || !is_format_supported(fmt))
                throw invalid_argument("Framebuffer::set_format");
 
-       if(ARB_internalformat_query && ARB_internalformat_query2)
-       {
-               unsigned target = (fmt.get_samples()>1 ? GL_TEXTURE_2D_MULTISAMPLE : GL_TEXTURE_2D);
-               for(FrameAttachment a: fmt)
-               {
-                       unsigned pf = get_gl_pixelformat(get_attachment_pixelformat(a));
-                       int supported = 0;
-                       glGetInternalformativ(target, pf, GL_FRAMEBUFFER_RENDERABLE, 1, &supported);
-                       if(supported!=GL_FULL_SUPPORT)
-                               throw invalid_argument("Framebuffer::set_format");
-               }
-       }
-
        format = fmt;
        attachments.resize(format.size());
 }
 
 void Framebuffer::update() const
 {
-       vector<GLenum> color_bufs;
-       color_bufs.reserve(format.size());
-       unsigned i = 0;
-       for(FrameAttachment a: format)
-       {
-               GLenum gl_attach_point = get_gl_attachment(a);
-               if(dirty&(1<<i))
-               {
-                       const Attachment &attch = attachments[i];
-                       if(attch.tex)
-                       {
-                               if(ARB_direct_state_access)
-                               {
-                                       if(attch.tex->target==GL_TEXTURE_2D || attch.tex->target==GL_TEXTURE_2D_MULTISAMPLE || attch.layer<0)
-                                               glNamedFramebufferTexture(id, gl_attach_point, attch.tex->id, attch.level);
-                                       else
-                                               glNamedFramebufferTextureLayer(id, gl_attach_point, attch.tex->id, attch.level, attch.layer);
-                               }
-                               else if(attch.tex->target==GL_TEXTURE_2D || attch.tex->target==GL_TEXTURE_2D_MULTISAMPLE)
-                                       glFramebufferTexture2D(GL_FRAMEBUFFER, gl_attach_point, attch.tex->target, attch.tex->id, attch.level);
-                               else if(attch.layer<0)
-                                       glFramebufferTexture(GL_FRAMEBUFFER, gl_attach_point, attch.tex->id, attch.level);
-                               else if(attch.tex->target==GL_TEXTURE_2D_ARRAY)
-                                       glFramebufferTextureLayer(GL_FRAMEBUFFER, gl_attach_point, attch.tex->id, attch.level, attch.layer);
-                               else if(attch.tex->target==GL_TEXTURE_3D)
-                                       glFramebufferTexture3D(GL_FRAMEBUFFER, gl_attach_point, attch.tex->target, attch.tex->id, attch.level, attch.layer);
-                               else if(attch.tex->target==GL_TEXTURE_CUBE_MAP)
-                                       glFramebufferTexture2D(GL_FRAMEBUFFER, gl_attach_point, get_gl_cube_face(static_cast<TextureCubeFace>(attch.layer)), attch.tex->id, attch.level);
-                       }
-                       else if(ARB_direct_state_access)
-                               glNamedFramebufferTexture(id, gl_attach_point, 0, 0);
-                       else
-                               glFramebufferTexture2D(GL_FRAMEBUFFER, gl_attach_point, GL_TEXTURE_2D, 0, 0);
-               }
-
-               if(gl_attach_point!=GL_DEPTH_ATTACHMENT && gl_attach_point!=GL_STENCIL_ATTACHMENT)
-                       color_bufs.push_back(gl_attach_point);
-
-               ++i;
-       }
-
-       if(color_bufs.size()>1)
-               static Require _req(ARB_draw_buffers);
-
-       GLenum first_buffer = (color_bufs.empty() ? GL_NONE : color_bufs.front());
-       if(ARB_direct_state_access)
-       {
-               /* ARB_direct_state_access ties the availability of these functions to
-               framebuffers themselves, so no further checks are needed. */
-               glNamedFramebufferDrawBuffers(id, color_bufs.size(), &color_bufs[0]);
-               glNamedFramebufferReadBuffer(id, first_buffer);
-       }
-       else
-       {
-               if(ARB_draw_buffers)
-                       glDrawBuffers(color_bufs.size(), &color_bufs[0]);
-               else if(MSP_buffer_control)
-                       glDrawBuffer(first_buffer);
-
-               if(MSP_buffer_control)
-                       glReadBuffer(first_buffer);
-       }
-
-       if(ARB_direct_state_access)
-               status = glCheckNamedFramebufferStatus(id, GL_FRAMEBUFFER);
-       else
-               status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
-
+       FramebufferBackend::update(dirty);
        dirty = 0;
 }
 
@@ -201,27 +78,24 @@ void Framebuffer::check_size()
                {
                        unsigned w = 0;
                        unsigned h = 0;
-                       if(a.tex->target==GL_TEXTURE_2D)
+                       if(const Texture2D *tex2d = dynamic_cast<const Texture2D *>(a.tex))
                        {
-                               Texture2D *tex = static_cast<Texture2D *>(a.tex);
-                               w = max(tex->get_width()>>a.level, 1U);
-                               h = max(tex->get_height()>>a.level, 1U);
+                               w = max(tex2d->get_width()>>a.level, 1U);
+                               h = max(tex2d->get_height()>>a.level, 1U);
                        }
-                       else if(a.tex->target==GL_TEXTURE_2D_MULTISAMPLE)
+                       else if(const Texture2DMultisample *tex2d_ms = dynamic_cast<const Texture2DMultisample *>(a.tex))
                        {
-                               Texture2DMultisample *tex = static_cast<Texture2DMultisample *>(a.tex);
-                               w = tex->get_width();
-                               h = tex->get_height();
+                               w = tex2d_ms->get_width();
+                               h = tex2d_ms->get_height();
                        }
-                       else if(a.tex->target==GL_TEXTURE_3D || a.tex->target==GL_TEXTURE_2D_ARRAY)
+                       else if(const Texture3D *tex3d = dynamic_cast<const Texture3D *>(a.tex))
                        {
-                               Texture3D *tex = static_cast<Texture3D *>(a.tex);
-                               w = max(tex->get_width()>>a.level, 1U);
-                               h = max(tex->get_height()>>a.level, 1U);
+                               w = max(tex3d->get_width()>>a.level, 1U);
+                               h = max(tex3d->get_height()>>a.level, 1U);
                        }
-                       else if(a.tex->target==GL_TEXTURE_CUBE_MAP)
+                       else if(const TextureCube *tex_cube = dynamic_cast<const TextureCube *>(a.tex))
                        {
-                               w = max(static_cast<TextureCube *>(a.tex)->get_size()>>a.level, 1U);
+                               w = max(tex_cube->get_size()>>a.level, 1U);
                                h = w;
                        }
 
@@ -285,13 +159,13 @@ void Framebuffer::attach(FrameAttachment attch, TextureCube &tex, TextureCubeFac
 
 void Framebuffer::attach_layered(FrameAttachment attch, Texture3D &tex, unsigned level)
 {
-       static Require _req(ARB_geometry_shader4);
+       require_layered();
        set_attachment(make_typed_attachment(attch, tex.get_format()), tex, level, -1, 0);
 }
 
 void Framebuffer::attach_layered(FrameAttachment attch, TextureCube &tex, unsigned level)
 {
-       static Require _req(ARB_geometry_shader4);
+       require_layered();
        set_attachment(make_typed_attachment(attch, tex.get_format()), tex, level, -1, 0);
 }
 
@@ -332,27 +206,12 @@ void Framebuffer::require_complete() const
                        throw framebuffer_incomplete("inconsistent layering");
        }
 
-       if(status==GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT)
-               throw framebuffer_incomplete("incomplete or unsupported attachment");
-       if(status==GL_FRAMEBUFFER_UNSUPPORTED)
-               throw framebuffer_incomplete("unsupported configuration");
-       if(status!=GL_FRAMEBUFFER_COMPLETE)
-               throw framebuffer_incomplete(Msp::format("incomplete (%#x)", status));
-}
-
-void Framebuffer::set_debug_name(const string &name)
-{
-#ifdef DEBUG
-       if(KHR_debug)
-               glObjectLabel(GL_FRAMEBUFFER, id, name.size(), name.c_str());
-#else
-       (void)name;
-#endif
+       FramebufferBackend::require_complete();
 }
 
 Framebuffer &Framebuffer::system()
 {
-       static Framebuffer sys_framebuf(0);
+       static Framebuffer sys_framebuf(true);
        return sys_framebuf;
 }
 
index da828bf531f7c70eafc74d297712266fed97fa37..18e48f2022b6b1197129a6de6c57ee8a37cf38b3 100644 (file)
@@ -3,6 +3,7 @@
 
 #include <vector>
 #include "color.h"
+#include "framebuffer_backend.h"
 #include "frameformat.h"
 #include "texturecube.h"
 
@@ -33,10 +34,9 @@ must be attached for the framebuffer to be usable.
 Requires the GL_EXT_framebuffer_object extension.  The blit functions require
 the GL_EXT_framebuffer_blit extension.
 */
-class Framebuffer
+class Framebuffer: public FramebufferBackend
 {
-       friend class Commands;
-       friend class PipelineState;
+       friend FramebufferBackend;
 
 private:
        struct Attachment
@@ -50,15 +50,13 @@ private:
                void clear();
        };
 
-       unsigned id;
        FrameFormat format;
        std::vector<Attachment> attachments;
        unsigned width;
        unsigned height;
-       mutable unsigned status;
        mutable unsigned dirty;
 
-       Framebuffer(unsigned);
+       Framebuffer(bool);
 public:
        /** Creates an empty framebuffer.  Format must be set before textures can
        be attached. */
@@ -72,9 +70,8 @@ public:
 
 private:
        void init();
-public:
-       ~Framebuffer();
 
+public:
        /** Sets the format of the framebuffer.  Once the format is set, it can't
        be changed. */
        void set_format(const FrameFormat &);
@@ -111,7 +108,7 @@ public:
 
        void refresh() const { if(dirty) update(); }
 
-       void set_debug_name(const std::string &);
+       using FramebufferBackend::set_debug_name;
 
        static Framebuffer &system();
 };
index 3568dffa4b11ee7d29e0fe01c94e23e9fc8c1cb6..36b45f6108d4e82f7ce2946d31b19d0c47932c5f 100644 (file)
@@ -124,30 +124,5 @@ PixelFormat get_attachment_pixelformat(FrameAttachment fa)
        return make_pixelformat(comp, type);
 }
 
-unsigned get_gl_attachment(FrameAttachment fa)
-{
-       if(get_attach_point(fa)==get_attach_point(DEPTH_ATTACHMENT))
-               return GL_DEPTH_ATTACHMENT;
-       else if(get_attach_point(fa)==get_attach_point(STENCIL_ATTACHMENT))
-               return GL_STENCIL_ATTACHMENT;
-       else
-               return GL_COLOR_ATTACHMENT0+get_attach_point(fa);
-}
-
-unsigned get_gl_buffer_bits(const FrameFormat &format)
-{
-       unsigned bits = 0;
-       for(FrameAttachment a: format)
-       {
-               if(get_attach_point(a)==get_attach_point(DEPTH_ATTACHMENT))
-                       bits |= GL_DEPTH_BUFFER_BIT;
-               else if(get_attach_point(a)==get_attach_point(STENCIL_ATTACHMENT))
-                       bits |= GL_STENCIL_BUFFER_BIT;
-               else
-                       bits |= GL_COLOR_BUFFER_BIT;
-       }
-       return bits;
-}
-
 } // namespace GL
 } // namespace Msp
index c3f6eae7d510c988c748d4a5f493eb73db59f392..90132092444488415a447b2840d03ad48c5fb58a 100644 (file)
@@ -76,10 +76,9 @@ inline unsigned get_attach_point(FrameAttachment fa)
 
 PixelFormat get_attachment_pixelformat(FrameAttachment);
 
-unsigned get_gl_attachment(FrameAttachment);
-unsigned get_gl_buffer_bits(const FrameFormat &);
-
 } // namespace GL
 } // namespace Msp
 
+#include "frameformat_backend.h"
+
 #endif
diff --git a/source/core/gl.h b/source/core/gl.h
deleted file mode 100644 (file)
index f27b63f..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-#ifndef MSP_GL_GL_H_
-#define MSP_GL_GL_H_
-
-#ifdef __APPLE__
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wkeyword-macro"
-#define extern extern __attribute__((weak_import))
-#include <OpenGL/gl.h>
-#include <OpenGL/glext.h>
-#undef extern
-#pragma clang diagnostic pop
-#elif defined(__ANDROID__)
-#include <GLES3/gl3.h>
-#include <GLES3/gl3ext.h>
-typedef double GLdouble;
-typedef long long GLint64;
-#else
-#ifdef _WIN32
-#ifndef WINAPI
-#if defined(_ARM_)
-#define WINAPI
-#else
-#define WINAPI __stdcall
-#endif
-#endif
-#ifndef APIENTRY
-#define APIENTRY WINAPI
-#endif
-#ifndef DECLSPEC_IMPORT
-#define DECLSPEC_IMPORT __declspec(dllimport)
-#endif
-#ifndef WINGDIAPI
-#define WINGDIAPI DECLSPEC_IMPORT
-#endif
-#endif
-#include <GL/gl.h>
-#include <GL/glext.h>
-#endif
-
-#ifndef APIENTRY
-#define APIENTRY
-#endif
-
-#endif
index 78db195d4b28d891a484b3d42268577973e065be..15de0b4dce3e91f39bc1aea641a0a6d32da7979a 100644 (file)
@@ -1,36 +1,12 @@
 #include <stdexcept>
 #include <msp/core/algorithm.h>
-#include <msp/gl/extensions/arb_direct_state_access.h>
-#include <msp/gl/extensions/arb_sampler_objects.h>
-#include <msp/gl/extensions/arb_shader_objects.h>
-#include <msp/gl/extensions/arb_uniform_buffer_object.h>
-#include <msp/gl/extensions/arb_vertex_array_object.h>
-#include <msp/gl/extensions/ext_framebuffer_object.h>
-#include <msp/gl/extensions/msp_primitive_restart.h>
-#include "blend.h"
-#include "buffer.h"
-#include "deviceinfo.h"
-#include "depthtest.h"
-#include "framebuffer.h"
 #include "pipelinestate.h"
-#include "program.h"
-#include "rect.h"
-#include "sampler.h"
-#include "stenciltest.h"
-#include "texture.h"
-#include "uniformblock.h"
-#include "vertexsetup.h"
 
 using namespace std;
 
 namespace Msp {
 namespace GL {
 
-const PipelineState *PipelineState::last_applied = 0;
-vector<int> PipelineState::bound_tex_targets;
-vector<char> PipelineState::bound_uniform_blocks;
-unsigned PipelineState::restart_index = 0;
-
 PipelineState::PipelineState():
        framebuffer(0),
        viewport(0),
@@ -44,18 +20,7 @@ PipelineState::PipelineState():
        stencil_test(0),
        blend(0),
        changes(0)
-{
-       if(bound_tex_targets.empty())
-               bound_tex_targets.resize(DeviceInfo::get_global().limits.max_texture_bindings);
-       if(bound_uniform_blocks.empty())
-               bound_uniform_blocks.resize(DeviceInfo::get_global().limits.max_uniform_bindings);
-}
-
-PipelineState::~PipelineState()
-{
-       if(this==last_applied)
-               last_applied = 0;
-}
+{ }
 
 template<typename T>
 void PipelineState::set(T &target, T value, unsigned flag)
@@ -152,231 +117,6 @@ void PipelineState::set_blend(const Blend *b)
        set(blend, b, BLEND);
 }
 
-void PipelineState::apply() const
-{
-       if(!last_applied)
-               Texture::unbind_scratch();
-
-       apply(this==last_applied ? changes : ~0U);
-}
-
-void PipelineState::apply(unsigned mask) const
-{
-       if(mask&FRAMEBUFFER)
-       {
-               glBindFramebuffer(GL_FRAMEBUFFER, framebuffer ? framebuffer->id : 0);
-               if(framebuffer)
-               {
-                       framebuffer->refresh();
-                       framebuffer->require_complete();
-               }
-       }
-
-       if(mask&(VIEWPORT|FRAMEBUFFER))
-       {
-               if(viewport)
-                       glViewport(viewport->left, viewport->bottom, viewport->width, viewport->height);
-               else if(framebuffer)
-                       glViewport(0, 0, framebuffer->get_width(), framebuffer->get_height());
-       }
-
-       if(mask&SCISSOR)
-       {
-               if(scissor)
-               {
-                       glEnable(GL_SCISSOR_TEST);
-                       glScissor(scissor->left, scissor->bottom, scissor->width, scissor->height);
-               }
-               else
-                       glDisable(GL_SCISSOR_TEST);
-       }
-
-       if(mask&SHPROG)
-               glUseProgram(shprog ? shprog->id : 0);
-
-       if(mask&VERTEX_SETUP)
-       {
-               glBindVertexArray(vertex_setup ? vertex_setup->id : 0);
-               if(vertex_setup)
-               {
-                       static Require _req(MSP_primitive_restart);
-
-                       vertex_setup->refresh();
-                       unsigned ri = (vertex_setup->get_index_type()==UNSIGNED_INT ? 0xFFFFFFFF : 0xFFFF);
-                       if(ri!=restart_index)
-                       {
-                               if(!restart_index)
-                                       glEnable(GL_PRIMITIVE_RESTART);
-                               glPrimitiveRestartIndex(ri);
-                               restart_index = ri;
-                       }
-               }
-       }
-
-       if(mask&FACE_CULL)
-       {
-               glFrontFace(front_face==CLOCKWISE ? GL_CW : GL_CCW);
-
-               if(face_cull!=NO_CULL && front_face!=NON_MANIFOLD)
-               {
-                       glEnable(GL_CULL_FACE);
-                       glCullFace(face_cull==CULL_FRONT ? GL_FRONT : GL_BACK);
-               }
-               else
-                       glDisable(GL_CULL_FACE);
-       }
-
-       if(mask&CLIP_PLANES)
-       {
-               unsigned max_clip_planes = DeviceInfo::get_global().limits.max_clip_planes;
-               for(unsigned i=0; i<max_clip_planes; ++i)
-               {
-                       if((enabled_clip_planes>>i)&1)
-                               glEnable(GL_CLIP_PLANE0+i);
-                       else
-                               glDisable(GL_CLIP_PLANE0+i);
-               }
-       }
-
-       if(mask&TEXTURES)
-       {
-               for(const BoundTexture &t: textures)
-                       if(t.changed || mask==~0U)
-                       {
-                               if(t.texture && t.sampler)
-                               {
-                                       if(ARB_direct_state_access)
-                                               glBindTextureUnit(t.binding, t.texture->id);
-                                       else
-                                       {
-                                               glActiveTexture(GL_TEXTURE0+t.binding);
-                                               if(bound_tex_targets[t.binding] && static_cast<int>(t.texture->target)!=bound_tex_targets[t.binding])
-                                                       glBindTexture(bound_tex_targets[t.binding], 0);
-                                               glBindTexture(t.texture->target, t.texture->id);
-                                       }
-
-                                       bound_tex_targets[t.binding] = t.texture->target;
-
-                                       glBindSampler(t.binding, t.sampler->id);
-                                       t.sampler->refresh();
-                               }
-
-                               t.changed = false;
-                       }
-       }
-
-       if(mask&UNIFORMS)
-       {
-               for(const BoundUniformBlock &u: uniform_blocks)
-                       if(u.changed || mask==~0U)
-                       {
-                               if(u.block)
-                               {
-                                       if(u.binding>=0)
-                                       {
-                                               glBindBufferRange(GL_UNIFORM_BUFFER, u.binding, u.block->get_buffer()->id, u.block->get_offset(), u.block->get_data_size());
-                                               bound_uniform_blocks[u.binding] = 1;
-                                       }
-                                       else if(shprog)
-                                       {
-                                               const char *data = static_cast<const char *>(u.block->get_data_pointer());
-                                               for(const Program::UniformCall &call: shprog->uniform_calls)
-                                                       call.func(call.location, call.size, data+call.location*16);
-                                       }
-                               }
-
-                               u.changed = false;
-                       }
-       }
-
-       if(mask&DEPTH_TEST)
-       {
-               if(depth_test && depth_test->enabled)
-               {
-                       glEnable(GL_DEPTH_TEST);
-                       glDepthFunc(get_gl_predicate(depth_test->compare));
-               }
-               else
-                       glDisable(GL_DEPTH_TEST);
-
-               glDepthMask(!depth_test || depth_test->write);
-       }
-
-       if(mask&STENCIL_TEST)
-       {
-               if(stencil_test && stencil_test->enabled)
-               {
-                       glEnable(GL_STENCIL_TEST);
-                       glStencilFunc(get_gl_predicate(stencil_test->compare), stencil_test->reference, 0xFFFFFFFF);
-                       glStencilOp(get_gl_stencil_op(stencil_test->stencil_fail_op), get_gl_stencil_op(stencil_test->depth_fail_op), get_gl_stencil_op(stencil_test->depth_pass_op));
-               }
-               else
-                       glDisable(GL_STENCIL_TEST);
-       }
-
-       if(mask&BLEND)
-       {
-               if(blend && blend->enabled)
-               {
-                       glEnable(GL_BLEND);
-                       glBlendEquation(get_gl_blend_equation(blend->equation));
-                       glBlendFunc(get_gl_blend_factor(blend->src_factor), get_gl_blend_factor(blend->dst_factor));
-                       glBlendColor(blend->constant.r, blend->constant.g, blend->constant.b, blend->constant.a);
-                       ColorWriteMask cw = blend->write_mask;
-                       glColorMask((cw&WRITE_RED)!=0, (cw&WRITE_GREEN)!=0, (cw&WRITE_BLUE)!=0, (cw&WRITE_ALPHA)!=0);
-               }
-               else
-               {
-                       glDisable(GL_BLEND);
-                       glColorMask(true, true, true, true);
-               }
-       }
-
-       last_applied = this;
-       changes &= ~mask;
-}
-
-void PipelineState::clear()
-{
-       if(last_applied)
-       {
-               glUseProgram(0);
-               glBindVertexArray(0);
-
-               unsigned max_clip_planes = DeviceInfo::get_global().limits.max_clip_planes;
-               for(unsigned i=0; i<max_clip_planes; ++i)
-                       if((last_applied->enabled_clip_planes>>i)&1)
-                               glDisable(GL_CLIP_PLANE0+i);
-
-               for(unsigned i=0; i<bound_tex_targets.size(); ++i)
-                       if(bound_tex_targets[i])
-                       {
-                               if(ARB_direct_state_access)
-                                       glBindTextureUnit(i, 0);
-                               else
-                               {
-                                       glActiveTexture(GL_TEXTURE0+i);
-                                       glBindTexture(bound_tex_targets[i], 0);
-                               }
-                               bound_tex_targets[i] = 0;
-                       }
-
-               for(unsigned i=0; i<bound_uniform_blocks.size(); ++i)
-                       if(bound_uniform_blocks[i])
-                       {
-                               glBindBufferBase(GL_UNIFORM_BUFFER, i, 0);
-                               bound_uniform_blocks[i] = 0;
-                       }
-
-               glDisable(GL_DEPTH_TEST);
-               glDepthMask(true);
-               glDisable(GL_STENCIL_TEST);
-               glDisable(GL_BLEND);
-
-               last_applied = 0;
-       }
-}
-
 
 PipelineState::BoundTexture::BoundTexture(unsigned b):
        binding(b),
index 68983d11c1d7f3d00ef3d3656bf487c396c6f6ce..42543e4dc2596f5de2e2c0a7c71e85f9b64fa4c8 100644 (file)
@@ -4,6 +4,7 @@
 #include <vector>
 #include <msp/core/noncopyable.h>
 #include "cullface.h"
+#include "pipelinestate_backend.h"
 
 namespace Msp {
 namespace GL {
@@ -19,8 +20,10 @@ class Texture;
 class UniformBlock;
 class VertexSetup;
 
-class PipelineState: public NonCopyable
+class PipelineState: public PipelineStateBackend
 {
+       friend PipelineStateBackend;
+
 private:
        struct BoundTexture
        {
@@ -72,14 +75,8 @@ private:
        const Blend *blend;
        mutable unsigned changes;
 
-       static const PipelineState *last_applied;
-       static std::vector<int> bound_tex_targets;
-       static std::vector<char> bound_uniform_blocks;
-       static unsigned restart_index;
-
 public:
        PipelineState();
-       ~PipelineState();
 
 private:
        template<typename T>
@@ -104,12 +101,6 @@ public:
        const VertexSetup *get_vertex_setup() const { return vertex_setup; }
        FaceWinding get_front_face() const { return front_face; }
        CullMode get_face_cull() const { return face_cull; }
-
-       void apply() const;
-private:
-       void apply(unsigned) const;
-public:
-       static void clear();
 };
 
 } // namespace GL
index 11eb40812ea631ff2421b88d55e39812d9ea09d5..81379ef07062831a85fed2ee8fa1f925f34b450a 100644 (file)
@@ -1,10 +1,3 @@
-#include <msp/gl/extensions/arb_depth_buffer_float.h>
-#include <msp/gl/extensions/arb_depth_texture.h>
-#include <msp/gl/extensions/arb_texture_float.h>
-#include <msp/gl/extensions/arb_texture_rg.h>
-#include <msp/gl/extensions/ext_texture_srgb.h>
-#include <msp/gl/extensions/oes_required_internalformat.h>
-#include <msp/gl/extensions/oes_texture_stencil8.h>
 #include <msp/strings/format.h>
 #include "pixelformat.h"
 
@@ -117,97 +110,5 @@ PixelFormat make_pixelformat(PixelComponents comp, DataType type, bool srgb)
        return static_cast<PixelFormat>(comp | get_type_size(type)<<8 | (type&0x300)<<4 | normalized*0x4000 | srgb*0x8000);
 }
 
-void require_pixelformat(PixelFormat pf)
-{
-       /* TODO These checks are only accurate for textures.  On OpenGL ES some
-       formats are allowed for render buffers earlier than textures.  In particular
-       it's possible to create a 16-bit depth renderbuffer on OpenGL ES 2.0 but
-       depth textures are only available with 3.0 or the OES_depth_texture
-       extension.*/
-       switch(pf)
-       {
-       case RGB8:
-       case RGBA8:
-               { static Require _req(OES_required_internalformat); }
-               break;
-       case R8:
-       case RG8:
-               { static Require _req(ARB_texture_rg); }
-               break;
-       case R16F:
-       case R32F:
-       case RG16F:
-       case RG32F:
-               { static Require _req(ARB_texture_rg); }
-               { static Require _req(ARB_texture_float); }
-               break;
-       case RGB16F:
-       case RGB32F:
-       case RGBA16F:
-       case RGBA32F:
-               { static Require _req(ARB_texture_float); }
-               break;
-       case SRGB8:
-       case SRGB8_ALPHA8:
-               { static Require _req(EXT_texture_sRGB); }
-               break;
-       case DEPTH_COMPONENT16:
-       case DEPTH_COMPONENT24:
-       case DEPTH_COMPONENT32:
-               { static Require _req(ARB_depth_texture); }
-               { static Require _req(OES_required_internalformat); }
-               break;
-       case DEPTH_COMPONENT32F:
-               { static Require _req(ARB_depth_buffer_float); }
-               break;
-       case STENCIL_INDEX8:
-               { static Require _req(OES_texture_stencil8); }
-               break;
-       default:
-               throw invalid_argument("require_pixelformat");
-       }
-}
-
-unsigned get_gl_components(PixelComponents comp)
-{
-       switch(comp)
-       {
-       case RED: return GL_RED;
-       case RG: return GL_RG;
-       case RGB: return GL_RGB;
-       case RGBA: return GL_RGBA;
-       case DEPTH_COMPONENT: return GL_DEPTH_COMPONENT;
-       case STENCIL_INDEX: return GL_STENCIL_INDEX;
-       default: throw invalid_argument("get_gl_components");
-       }
-}
-
-unsigned get_gl_pixelformat(PixelFormat pf)
-{
-       switch(pf)
-       {
-       case R8: return GL_R8;
-       case R16F: return GL_R16F;
-       case R32F: return GL_R32F;
-       case RG8: return GL_RG8;
-       case RG16F: return GL_RG16F;
-       case RG32F: return GL_RG32F;
-       case RGB8: return GL_RGB8;
-       case RGB16F: return GL_RGB16F;
-       case RGB32F: return GL_RGB32F;
-       case RGBA8: return GL_RGBA8;
-       case RGBA16F: return GL_RGBA16F;
-       case RGBA32F: return GL_RGBA32F;
-       case SRGB8: return GL_SRGB8;
-       case SRGB8_ALPHA8: return GL_SRGB8_ALPHA8;
-       case DEPTH_COMPONENT16: return GL_DEPTH_COMPONENT16;
-       case DEPTH_COMPONENT24: return GL_DEPTH_COMPONENT24;
-       case DEPTH_COMPONENT32: return GL_DEPTH_COMPONENT32;
-       case DEPTH_COMPONENT32F: return GL_DEPTH_COMPONENT32F;
-       case STENCIL_INDEX8: return GL_STENCIL_INDEX8;
-       default: throw invalid_argument("get_gl_pixelformat");
-       }
-}
-
 } // namespace GL
 } // namespace Msp
index 65331c99901fcd5aefee7b78e1fef5727579af9e..71a8bd4c3de6c98994e618d3a382be4b9dbc1791 100644 (file)
@@ -98,10 +98,9 @@ inline unsigned get_pixel_size(PixelFormat f) { return get_component_count(f)*ge
 
 void require_pixelformat(PixelFormat);
 
-unsigned get_gl_components(PixelComponents);
-unsigned get_gl_pixelformat(PixelFormat);
-
 } // namespace GL
 } // namespace Msp
 
+#include "pixelformat_backend.h"
+
 #endif
index 7b0c0587f845766dfc73c9f186e0b35501923903..6337e17401d94a6342f3ee0d8b67d5d1c6c2f36b 100644 (file)
@@ -7,22 +7,6 @@ using namespace std;
 namespace Msp {
 namespace GL {
 
-unsigned get_gl_predicate(Predicate pred)
-{
-       switch(pred)
-       {
-       case NEVER: return GL_NEVER;
-       case ALWAYS: return GL_ALWAYS;
-       case LESS: return GL_LESS;
-       case LEQUAL: return GL_LEQUAL;
-       case EQUAL: return GL_EQUAL;
-       case GREATER: return GL_GREATER;
-       case GEQUAL: return GL_GEQUAL;
-       case NOTEQUAL: return GL_NOTEQUAL;
-       default: throw invalid_argument("get_gl_predicate");
-       }
-}
-
 void operator>>(const LexicalConverter &conv, Predicate &pred)
 {
        const string &str = conv.get();
index 079ef63e3daa0fb493d965ed389b74f37ebf254a..6b0a697b6579ed3283f3e4be1d9aa5d21ae5ea88 100644 (file)
@@ -18,12 +18,12 @@ enum Predicate
        NOTEQUAL
 };
 
-unsigned get_gl_predicate(Predicate);
-
 void operator>>(const LexicalConverter &, Predicate &);
 void operator<<(LexicalConverter &, Predicate);
 
 } // namespace GL
 } // namespace Msp
 
+#include "predicate_backend.h"
+
 #endif
index 416ebae2bff1b7d9fdd02ec64cb2a97c69c1780c..ca03a67e599ea0dc9e6b16cea2a1dc274f4bb080 100644 (file)
@@ -7,21 +7,6 @@ using namespace std;
 namespace Msp {
 namespace GL {
 
-unsigned get_gl_primitive_type(PrimitiveType pt)
-{
-       switch(pt)
-       {
-       case POINTS: return GL_POINTS;
-       case LINES: return GL_LINES;
-       case LINE_STRIP: return GL_LINE_STRIP;
-       case LINE_LOOP: return GL_LINE_LOOP;
-       case TRIANGLES: return GL_TRIANGLES;
-       case TRIANGLE_STRIP: return GL_TRIANGLE_STRIP;
-       case TRIANGLE_FAN: return GL_TRIANGLE_FAN;
-       default: throw invalid_argument("get_gl_primitive_type");
-       }
-}
-
 void operator>>(const LexicalConverter &conv, PrimitiveType &pt)
 {
        if(conv.get()=="POINTS")
index cbcb187c942739a6958db9f179e42b90573395bc..4b6a1a3eeb6cdc2130cabdf5824deab599e642d8 100644 (file)
@@ -17,11 +17,11 @@ enum PrimitiveType
        TRIANGLE_FAN
 };
 
-unsigned get_gl_primitive_type(PrimitiveType);
-
 void operator>>(const LexicalConverter &, PrimitiveType &);
 
 } // namespace GL
 } // namespace Msp
 
+#include "primitivetype_backend.h"
+
 #endif
index b1516e2dc329c95d6d26f7cc1ce6d575f990ef45..6458b4779d396ae3fe5072f4ec9381c1ba4b05e3 100644 (file)
@@ -1,70 +1,17 @@
-#include <cstring>
 #include <msp/core/algorithm.h>
-#include <msp/gl/extensions/arb_es2_compatibility.h>
-#include <msp/gl/extensions/arb_fragment_shader.h>
-#include <msp/gl/extensions/arb_gl_spirv.h>
-#include <msp/gl/extensions/arb_geometry_shader4.h>
-#include <msp/gl/extensions/arb_separate_shader_objects.h>
-#include <msp/gl/extensions/arb_shader_objects.h>
-#include <msp/gl/extensions/arb_uniform_buffer_object.h>
-#include <msp/gl/extensions/arb_vertex_shader.h>
-#include <msp/gl/extensions/ext_gpu_shader4.h>
-#include <msp/gl/extensions/khr_debug.h>
-#include <msp/gl/extensions/nv_non_square_matrices.h>
-#include <msp/io/print.h>
 #include "error.h"
 #include "program.h"
-#include "glsl/compiler.h"
 
 using namespace std;
 
-namespace {
-
-template<typename T, void (*&func)(GLint, GLsizei, const T *)>
-void uniform_wrapper(unsigned index, unsigned count, const void *data)
-{
-       func(index, count, static_cast<const T *>(data));
-}
-
-template<typename T, void (*&func)(GLint, GLsizei, GLboolean, const T *)>
-void uniform_matrix_wrapper(unsigned index, unsigned count, const void *data)
-{
-       func(index, count, false, static_cast<const T *>(data));
-}
-
-}
-
 namespace Msp {
 namespace GL {
 
-Program::Program()
-{
-       init();
-}
-
 Program::Program(const Module &mod, const map<string, int> &spec_values)
 {
-       init();
        add_stages(mod, spec_values);
 }
 
-void Program::init()
-{
-       static Require _req(ARB_shader_objects);
-
-       id = glCreateProgram();
-       fill(stage_ids, stage_ids+MAX_STAGES, 0);
-       linked = false;
-}
-
-Program::~Program()
-{
-       for(unsigned i=0; i<MAX_STAGES; ++i)
-               if(stage_ids[i])
-                       glDeleteShader(stage_ids[i]);
-       glDeleteProgram(id);
-}
-
 void Program::add_stages(const Module &mod, const map<string, int> &spec_values)
 {
        if(has_stages())
@@ -83,215 +30,15 @@ void Program::add_stages(const Module &mod, const map<string, int> &spec_values)
                throw invalid_argument("Program::add_stages");
        }
 
-       finalize(mod, transient);
-}
-
-bool Program::has_stages() const
-{
-       for(unsigned i=0; i<MAX_STAGES; ++i)
-               if(stage_ids[i])
-                       return true;
-       return false;
-}
-
-unsigned Program::add_stage(Stage type)
-{
-       GLenum gl_type;
-       switch(type)
-       {
-       case VERTEX: { static Require _req(ARB_vertex_shader); gl_type = GL_VERTEX_SHADER; } break;
-       case GEOMETRY: { static Require _req(ARB_geometry_shader4); gl_type = GL_GEOMETRY_SHADER; } break;
-       case FRAGMENT: { static Require _req(ARB_fragment_shader); gl_type = GL_FRAGMENT_SHADER; } break;
-       default: throw invalid_argument("Program::add_stage");
-       }
-
-       if(stage_ids[type])
-               throw invalid_operation("Program::add_stage");
-
-       unsigned stage_id = glCreateShader(gl_type);
-       stage_ids[type] = stage_id;
-       glAttachShader(id, stage_id);
-
-#ifdef DEBUG
-       if(!debug_name.empty() && KHR_debug)
-               set_stage_debug_name(stage_id, type);
-#endif
-
-       return stage_id;
-}
-
-void Program::add_glsl_stages(const GlslModule &mod, const map<string, int> &spec_values, TransientData &transient)
-{
-       SL::Compiler compiler;
-       compiler.set_source(mod.get_prepared_source(), "<module>");
-       compiler.specialize(spec_values);
-       compiler.compile(SL::Compiler::PROGRAM);
-#ifdef DEBUG
-       string diagnostics = compiler.get_diagnostics();
-       if(!diagnostics.empty())
-               IO::print("Program diagnostics:\n%s\n", diagnostics);
-#endif
-
-       vector<SL::Stage::Type> stages = compiler.get_stages();
-       if(stages.empty())
-               throw invalid_argument("Program::add_glsl_stages");
-
-       for(SL::Stage::Type st: stages)
-       {
-               unsigned stage_id = 0;
-               switch(st)
-               {
-               case SL::Stage::VERTEX: stage_id = add_stage(VERTEX); break;
-               case SL::Stage::GEOMETRY: stage_id = add_stage(GEOMETRY); break;
-               case SL::Stage::FRAGMENT: stage_id = add_stage(FRAGMENT); break;
-               default: throw invalid_operation("Program::add_glsl_stages");
-               }
-
-               string stage_src = compiler.get_stage_glsl(st);
-               const char *src_ptr = stage_src.data();
-               int src_len = stage_src.size();
-               glShaderSource(stage_id, 1, &src_ptr, &src_len);
-
-               if(st==SL::Stage::VERTEX)
-               {
-                       for(const auto &kvp: compiler.get_vertex_attributes())
-                               glBindAttribLocation(id, kvp.second, kvp.first.c_str());
-               }
-
-               if(st==SL::Stage::FRAGMENT && EXT_gpu_shader4)
-               {
-                       for(const auto &kvp: compiler.get_fragment_outputs())
-                               glBindFragDataLocation(id, kvp.second, kvp.first.c_str());
-               }
-
-               compile_glsl_stage(mod, stage_id);
-       }
-
-       transient.textures = compiler.get_texture_bindings();
-       transient.blocks = compiler.get_uniform_block_bindings();
-}
-
-void Program::compile_glsl_stage(const GlslModule &mod, unsigned stage_id)
-{
-       glCompileShader(stage_id);
-       int status = 0;
-       glGetShaderiv(stage_id, GL_COMPILE_STATUS, &status);
-
-       int info_log_len = 0;
-       glGetShaderiv(stage_id, GL_INFO_LOG_LENGTH, &info_log_len);
-       string info_log(info_log_len+1, 0);
-       glGetShaderInfoLog(stage_id, info_log_len+1, &info_log_len, &info_log[0]);
-       info_log.erase(info_log_len);
-       info_log = mod.get_source_map().translate_errors(info_log);
-
-       if(!status)
-               throw compile_error(info_log);
-#ifdef DEBUG
-       if(!info_log.empty())
-               IO::print("Shader compile info log:\n%s", info_log);
-#endif
-}
-
-void Program::add_spirv_stages(const SpirVModule &mod, const map<string, int> &spec_values, TransientData &transient)
-{
-       static Require _req(ARB_gl_spirv);
-       static Require _req2(ARB_ES2_compatibility);
-
-       unsigned n_stages = 0;
-       unsigned used_stage_ids[MAX_STAGES];
-       for(const SpirVModule::EntryPoint &e: mod.get_entry_points())
-       {
-               unsigned stage_id = 0;
-               switch(e.stage)
-               {
-               case SpirVModule::VERTEX: stage_id = add_stage(VERTEX); break;
-               case SpirVModule::GEOMETRY: stage_id = add_stage(GEOMETRY); break;
-               case SpirVModule::FRAGMENT: stage_id = add_stage(FRAGMENT); break;
-               default: throw invalid_operation("Program::add_spirv_stages");
-               }
-
-               used_stage_ids[n_stages++] = stage_id;
-       }
-
-       if(!n_stages)
-               throw invalid_argument("Program::add_spirv_stages");
-
-       const vector<uint32_t> &code = mod.get_code();
-       glShaderBinary(n_stages, used_stage_ids, GL_SHADER_BINARY_FORMAT_SPIR_V, &code[0], code.size()*4);
-
-       const vector<SpirVModule::Constant> &spec_consts = mod.get_spec_constants();
-       vector<unsigned> spec_id_array;
-       vector<unsigned> spec_value_array;
-       spec_id_array.reserve(spec_consts.size());
-       spec_value_array.reserve(spec_consts.size());
-       for(const SpirVModule::Constant &c: spec_consts)
-       {
-               auto i = spec_values.find(c.name);
-               if(i!=spec_values.end())
-               {
-                       spec_id_array.push_back(c.constant_id);
-                       spec_value_array.push_back(i->second);
-                       transient.spec_values[c.constant_id] = i->second;
-               }
-       }
-
-       auto j = mod.get_entry_points().begin();
-       for(unsigned i=0; i<MAX_STAGES; ++i)
-               if(stage_ids[i])
-                       glSpecializeShader(stage_ids[i], j->name.c_str(), spec_id_array.size(), &spec_id_array[0], &spec_value_array[0]);
-}
-
-void Program::finalize(const Module &mod, const TransientData &transient)
-{
        reflect_data = ReflectData();
 
-       glLinkProgram(id);
-       int status = 0;
-       glGetProgramiv(id, GL_LINK_STATUS, &status);
-       linked = status;
-
-       int info_log_len = 0;
-       glGetProgramiv(id, GL_INFO_LOG_LENGTH, &info_log_len);
-       string info_log(info_log_len+1, 0);
-       glGetProgramInfoLog(id, info_log_len+1, &info_log_len, &info_log[0]);
-       info_log.erase(info_log_len);
-       if(mod.get_format()==Module::GLSL)
-               info_log = static_cast<const GlslModule &>(mod).get_source_map().translate_errors(info_log);
-
-       if(!linked)
-               throw compile_error(info_log);
-#ifdef DEBUG
-       if(!info_log.empty())
-               IO::print("Program link info log:\n%s", info_log);
-#endif
+       finalize(mod);
 
        if(mod.get_format()==Module::GLSL)
        {
                query_uniforms();
                query_attributes();
-               for(unsigned i=0; i<reflect_data.uniform_blocks.size(); ++i)
-               {
-                       auto j = transient.blocks.find(reflect_data.uniform_blocks[i].name);
-                       if(j!=transient.blocks.end())
-                       {
-                               glUniformBlockBinding(id, i, j->second);
-                               reflect_data.uniform_blocks[i].bind_point = j->second;
-                       }
-               }
-
-               if(!ARB_separate_shader_objects)
-                       glUseProgram(id);
-               for(const auto &kvp: transient.textures)
-               {
-                       int location = get_uniform_location(kvp.first);
-                       if(location>=0)
-                       {
-                               if(ARB_separate_shader_objects)
-                                       glProgramUniform1i(id, location, kvp.second);
-                               else
-                                       glUniform1i(location, kvp.second);
-                       }
-               }
+               apply_bindings(transient);
        }
        else if(mod.get_format()==Module::SPIR_V)
        {
@@ -305,212 +52,6 @@ void Program::finalize(const Module &mod, const TransientData &transient)
                require_type(a.type);
 }
 
-void Program::query_uniforms()
-{
-       unsigned count = 0;
-       glGetProgramiv(id, GL_ACTIVE_UNIFORMS, reinterpret_cast<int *>(&count));
-       reflect_data.uniforms.reserve(count);
-       vector<string> uniform_names(count);
-       for(unsigned i=0; i<count; ++i)
-       {
-               char name[128];
-               int len = 0;
-               int size;
-               GLenum type;
-               glGetActiveUniform(id, i, sizeof(name), &len, &size, &type, name);
-               if(len && strncmp(name, "gl_", 3))
-               {
-                       /* Some implementations report the first element of a uniform array,
-                       others report just the name of the array itself. */
-                       if(len>3 && !strcmp(name+len-3, "[0]"))
-                               name[len-3] = 0;
-
-                       reflect_data.uniforms.push_back(ReflectData::UniformInfo());
-                       ReflectData::UniformInfo &info = reflect_data.uniforms.back();
-                       info.name = name;
-                       info.tag = name;
-                       info.array_size = size;
-                       info.type = from_gl_type(type);
-                       uniform_names[i] = name;
-               }
-       }
-
-       sort_member(reflect_data.uniforms, &ReflectData::UniformInfo::tag);
-
-       if(ARB_uniform_buffer_object)
-       {
-               vector<ReflectData::UniformInfo *> uniforms_by_index(count);
-               for(unsigned i=0; i<count; ++i)
-                       if(!uniform_names[i].empty())
-                               // The element is already known to be present
-                               uniforms_by_index[i] = &*lower_bound_member(reflect_data.uniforms, Tag(uniform_names[i]), &ReflectData::UniformInfo::tag);
-               query_uniform_blocks(uniforms_by_index);
-       }
-
-       reflect_data.uniform_blocks.push_back(ReflectData::UniformBlockInfo());
-       ReflectData::UniformBlockInfo &default_block = reflect_data.uniform_blocks.back();
-
-       for(ReflectData::UniformInfo &u: reflect_data.uniforms)
-               if(!u.block)
-               {
-                       u.location = glGetUniformLocation(id, u.name.c_str());
-                       u.block = &default_block;
-                       u.array_stride = get_type_size(u.type);
-                       if(is_matrix(u.type))
-                               u.matrix_stride = get_type_size(get_matrix_column_type(u.type));
-                       default_block.uniforms.push_back(&u);
-
-                       if(u.location>=0)
-                       {
-                               UniformCall::FuncPtr func = 0;
-                               if(u.type==FLOAT)
-                                       func = &uniform_wrapper<float, glUniform1fv>;
-                               else if(u.type==FLOAT_VEC2)
-                                       func = &uniform_wrapper<float, glUniform2fv>;
-                               else if(u.type==FLOAT_VEC3)
-                                       func = &uniform_wrapper<float, glUniform3fv>;
-                               else if(u.type==FLOAT_VEC4)
-                                       func = &uniform_wrapper<float, glUniform4fv>;
-                               else if(u.type==INT)
-                                       func = &uniform_wrapper<int, glUniform1iv>;
-                               else if(u.type==INT_VEC2)
-                                       func = &uniform_wrapper<int, glUniform2iv>;
-                               else if(u.type==INT_VEC3)
-                                       func = &uniform_wrapper<int, glUniform3iv>;
-                               else if(u.type==INT_VEC4)
-                                       func = &uniform_wrapper<int, glUniform4iv>;
-                               else if(u.type==FLOAT_MAT2)
-                                       func = &uniform_matrix_wrapper<float, glUniformMatrix2fv>;
-                               else if(u.type==FLOAT_MAT3)
-                                       func = &uniform_matrix_wrapper<float, glUniformMatrix3fv>;
-                               else if(u.type==FLOAT_MAT4)
-                                       func = &uniform_matrix_wrapper<float, glUniformMatrix4fv>;
-                               else if(u.type==FLOAT_MAT2x3)
-                                       func = &uniform_matrix_wrapper<float, glUniformMatrix2x3fv>;
-                               else if(u.type==FLOAT_MAT3x2)
-                                       func = &uniform_matrix_wrapper<float, glUniformMatrix3x2fv>;
-                               else if(u.type==FLOAT_MAT2x4)
-                                       func = &uniform_matrix_wrapper<float, glUniformMatrix2x4fv>;
-                               else if(u.type==FLOAT_MAT4x2)
-                                       func = &uniform_matrix_wrapper<float, glUniformMatrix4x2fv>;
-                               else if(u.type==FLOAT_MAT3x4)
-                                       func = &uniform_matrix_wrapper<float, glUniformMatrix3x4fv>;
-                               else if(u.type==FLOAT_MAT4x3)
-                                       func = &uniform_matrix_wrapper<float, glUniformMatrix4x3fv>;
-                               else if(is_image(u.type))
-                                       glGetUniformiv(id, u.location, &u.binding);
-
-                               if(func)
-                                       uniform_calls.push_back(UniformCall(u.location, u.array_size, func));
-                       }
-               }
-
-       default_block.sort_uniforms();
-       if(!default_block.uniforms.empty())
-       {
-               const ReflectData::UniformInfo &uni = *default_block.uniforms.back();
-               default_block.data_size = uni.location*16+uni.array_size*get_type_size(uni.type);
-       }
-       default_block.update_layout_hash();
-       reflect_data.update_layout_hash();
-}
-
-void Program::query_uniform_blocks(const vector<ReflectData::UniformInfo *> &uniforms_by_index)
-{
-       unsigned count = 0;
-       glGetProgramiv(id, GL_ACTIVE_UNIFORM_BLOCKS, reinterpret_cast<int *>(&count));
-       // Reserve an extra index for the default block
-       reflect_data.uniform_blocks.reserve(count+1);
-       for(unsigned i=0; i<count; ++i)
-       {
-               char name[128];
-               int len;
-               glGetActiveUniformBlockName(id, i, sizeof(name), &len, name);
-               reflect_data.uniform_blocks.push_back(ReflectData::UniformBlockInfo());
-               ReflectData::UniformBlockInfo &info = reflect_data.uniform_blocks.back();
-               info.name = name;
-
-               int value;
-               glGetActiveUniformBlockiv(id, i, GL_UNIFORM_BLOCK_DATA_SIZE, &value);
-               info.data_size = value;
-
-               glGetActiveUniformBlockiv(id, i, GL_UNIFORM_BLOCK_BINDING, &value);
-               info.bind_point = value;
-
-               glGetActiveUniformBlockiv(id, i, GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, &value);
-               vector<int> indices(value);
-               glGetActiveUniformBlockiv(id, i, GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES, &indices[0]);
-               for(int j: indices)
-               {
-                       if(!uniforms_by_index[j])
-                               throw logic_error("Program::link");
-                       info.uniforms.push_back(uniforms_by_index[j]);
-                       uniforms_by_index[j]->block = &info;
-               }
-
-               vector<unsigned> query_indices(indices.begin(), indices.end());
-               vector<int> values(indices.size());
-               glGetActiveUniformsiv(id, query_indices.size(), &query_indices[0], GL_UNIFORM_OFFSET, &values[0]);
-               for(unsigned j=0; j<indices.size(); ++j)
-                       uniforms_by_index[indices[j]]->offset = values[j];
-
-               query_indices.clear();
-               for(int j: indices)
-                       if(uniforms_by_index[j]->array_size>1)
-                               query_indices.push_back(j);
-               if(!query_indices.empty())
-               {
-                       glGetActiveUniformsiv(id, query_indices.size(), &query_indices[0], GL_UNIFORM_ARRAY_STRIDE, &values[0]);
-                       for(unsigned j=0; j<query_indices.size(); ++j)
-                               uniforms_by_index[query_indices[j]]->array_stride = values[j];
-               }
-
-               query_indices.clear();
-               for(int j: indices)
-               {
-                       DataType t = uniforms_by_index[j]->type;
-                       if(is_matrix(t))
-                               query_indices.push_back(j);
-               }
-               if(!query_indices.empty())
-               {
-                       glGetActiveUniformsiv(id, query_indices.size(), &query_indices[0], GL_UNIFORM_MATRIX_STRIDE, &values[0]);
-                       for(unsigned j=0; j<query_indices.size(); ++j)
-                               uniforms_by_index[query_indices[j]]->matrix_stride = values[j];
-               }
-
-               info.sort_uniforms();
-               info.update_layout_hash();
-       }
-}
-
-void Program::query_attributes()
-{
-       unsigned count = 0;
-       glGetProgramiv(id, GL_ACTIVE_ATTRIBUTES, reinterpret_cast<int *>(&count));
-       reflect_data.attributes.reserve(count);
-       for(unsigned i=0; i<count; ++i)
-       {
-               char name[128];
-               int len = 0;
-               int size;
-               GLenum type;
-               glGetActiveAttrib(id, i, sizeof(name), &len, &size, &type, name);
-               if(len && strncmp(name, "gl_", 3))
-               {
-                       if(len>3 && !strcmp(name+len-3, "[0]"))
-                               name[len-3] = 0;
-
-                       reflect_data.attributes.push_back(ReflectData::AttributeInfo());
-                       ReflectData::AttributeInfo &info = reflect_data.attributes.back();
-                       info.name = name;
-                       info.location = glGetAttribLocation(id, name);
-                       info.array_size = size;
-                       info.type = from_gl_type(type);
-               }
-       }
-}
-
 void Program::collect_uniforms(const SpirVModule &mod, const map<unsigned, int> &spec_values)
 {
        // Prepare the default block
@@ -687,33 +228,6 @@ int Program::get_attribute_location(const string &name) const
        return i!=reflect_data.attributes.end() && i->name==name ? i->location : -1;
 }
 
-void Program::set_debug_name(const string &name)
-{
-#ifdef DEBUG
-       debug_name = name;
-       if(KHR_debug)
-       {
-               glObjectLabel(GL_PROGRAM, id, name.size(), name.c_str());
-               for(unsigned i=0; i<MAX_STAGES; ++i)
-                       if(stage_ids[i])
-                               set_stage_debug_name(stage_ids[i], static_cast<Stage>(i));
-       }
-#else
-       (void)name;
-#endif
-}
-
-void Program::set_stage_debug_name(unsigned stage_id, Stage type)
-{
-#ifdef DEBUG
-       static const char *const suffixes[] = { " [VS]", " [GS]", " [FS]" };
-       string name = debug_name+suffixes[type];
-       glObjectLabel(GL_SHADER, stage_id, name.size(), name.c_str());
-#else
-       (void)stage_id; (void)type;
-#endif
-}
-
 
 Program::Loader::Loader(Program &p, Collection &c):
        DataFile::CollectionObjectLoader<Program>(p, &c)
index 41c5897dc08ccb54a5461a798c78b425b5315bf9..f41a7d6ddf99d56218d8b53b6578853c2dca8e71 100644 (file)
@@ -6,6 +6,7 @@
 #include <vector>
 #include <msp/datafile/objectloader.h>
 #include "module.h"
+#include "program_backend.h"
 #include "reflectdata.h"
 
 namespace Msp {
@@ -15,9 +16,9 @@ namespace GL {
 A complete shader program.  Programs can be assembled of individual Shaders or
 generated with a set of standard features.
 */
-class Program
+class Program: public ProgramBackend
 {
-       friend class PipelineState;
+       friend ProgramBackend;
 
 public:
        class Loader: public DataFile::CollectionObjectLoader<Program>
@@ -47,63 +48,17 @@ private:
                void specialize_int(const std::string &, int);
        };
 
-       enum Stage
-       {
-               VERTEX,
-               GEOMETRY,
-               FRAGMENT,
-               MAX_STAGES
-       };
-
-       struct TransientData
-       {
-               std::map<std::string, unsigned> textures;
-               std::map<std::string, unsigned> blocks;
-               std::map<unsigned, int> spec_values;
-       };
-
-       struct UniformCall
-       {
-               using FuncPtr = void (*)(unsigned, unsigned, const void *);
-
-               unsigned location;
-               unsigned size;
-               FuncPtr func;
-
-               UniformCall(unsigned l, unsigned s, FuncPtr f): location(l), size(s), func(f) { }
-       };
-
-       unsigned id;
-       unsigned stage_ids[MAX_STAGES];
-       bool linked;
        ReflectData reflect_data;
-       std::vector<UniformCall> uniform_calls;
-       std::string debug_name;
 
 public:
        /// Constructs an empty Program with no shader stages attached.
-       Program();
+       Program() { }
 
        /// Constructs a Program from a Module, with specialization constants.
        Program(const Module &, const std::map<std::string, int> & = std::map<std::string, int>());
 
-private:
-       void init();
-public:
-       virtual ~Program();
-
        void add_stages(const Module &, const std::map<std::string, int> & = std::map<std::string, int>());
 private:
-       bool has_stages() const;
-       unsigned add_stage(Stage);
-       void add_glsl_stages(const GlslModule &, const std::map<std::string, int> &, TransientData &);
-       void compile_glsl_stage(const GlslModule &, unsigned);
-       void add_spirv_stages(const SpirVModule &, const std::map<std::string, int> &, TransientData &);
-
-       void finalize(const Module &, const TransientData &);
-       void query_uniforms();
-       void query_uniform_blocks(const std::vector<ReflectData::UniformInfo *> &);
-       void query_attributes();
        void collect_uniforms(const SpirVModule &, const std::map<unsigned, int> &);
        void collect_block_uniforms(const SpirVModule::Structure &, const std::string &, unsigned, const std::map<unsigned, int> &, std::vector<std::string> &);
        void collect_attributes(const SpirVModule &);
@@ -122,9 +77,7 @@ public:
        const ReflectData::AttributeInfo &get_attribute_info(const std::string &) const;
        int get_attribute_location(const std::string &) const;
 
-       void set_debug_name(const std::string &);
-private:
-       void set_stage_debug_name(unsigned, Stage);
+       using ProgramBackend::set_debug_name;
 };
 
 } // namespace GL
index a6d77eda0b604863fce53f418f31c47edbb93108..f075cada23c7f9d4ed577d37b1b51c2baabb651c 100644 (file)
@@ -1,50 +1,16 @@
-#include <stdexcept>
-#include <msp/gl/extensions/arb_occlusion_query.h>
-#include <msp/gl/extensions/arb_occlusion_query2.h>
 #include "query.h"
 #include "renderer.h"
 
-using namespace std;
-
 namespace Msp {
 namespace GL {
 
 QueryPool::QueryPool(QueryType t, unsigned s):
-       type(t),
-       gl_type(get_gl_query_type(type))
+       QueryPoolBackend(t),
+       type(t)
 {
-       if(type==OCCLUSION_QUERY)
-       {
-               static Require req(ARB_occlusion_query);
-               static Require req2(ARB_occlusion_query2);
-       }
-
        resize(s);
 }
 
-QueryPool::~QueryPool()
-{
-       glDeleteQueries(queries.size(), queries.data());
-}
-
-void QueryPool::resize(unsigned s)
-{
-       if(s<queries.size())
-               glDeleteQueries(queries.size()-s, queries.data()+s);
-
-       unsigned old_size = queries.size();
-       queries.resize(s);
-       if(s>old_size)
-               glGenQueries(s-old_size, queries.data()+old_size);
-}
-
-unsigned QueryPool::get_result(unsigned i) const
-{
-       unsigned result = 0;
-       glGetQueryObjectuiv(queries[i], GL_QUERY_RESULT, &result);
-       return result;
-}
-
 
 QueryPool::Activate::Activate(Renderer &r, const QueryPool &p, unsigned i):
        renderer(r),
@@ -59,15 +25,5 @@ QueryPool::Activate::~Activate()
        renderer.end_query(pool, index);
 }
 
-
-unsigned get_gl_query_type(QueryType t)
-{
-       switch(t)
-       {
-       case OCCLUSION_QUERY: return GL_ANY_SAMPLES_PASSED;
-       default: throw invalid_argument("get_gl_query_type");
-       }
-}
-
 } // namespace GL
 } // namespace Msp
index aaa993e389f2c17070fe7956d31bdc7fa3bcaa81..f6b0a2e5ecfcb2269cf4027a0e54e7f0fd132d33 100644 (file)
@@ -1,8 +1,8 @@
 #ifndef MSP_GL_QUERY_H_
 #define MSP_GL_QUERY_H_
 
-#include <vector>
 #include <msp/core/noncopyable.h>
+#include "query_backend.h"
 
 namespace Msp {
 namespace GL {
@@ -14,9 +14,9 @@ enum QueryType
        OCCLUSION_QUERY
 };
 
-class QueryPool: public Msp::NonCopyable
+class QueryPool: public QueryPoolBackend, public Msp::NonCopyable
 {
-       friend class Commands;
+       friend QueryPoolBackend;
 
 public:
        class Activate
@@ -33,21 +33,16 @@ public:
 
 private:
        QueryType type;
-       unsigned gl_type;
-       std::vector<unsigned> queries;
 
 public:
        QueryPool(QueryType type, unsigned);
-       ~QueryPool();
 
-       void resize(unsigned);
-       unsigned get_size() const { return queries.size(); }
+       using QueryPoolBackend::resize;
+       using QueryPoolBackend::get_size;
 
-       unsigned get_result(unsigned) const;
+       using QueryPoolBackend::get_result;
 };
 
-unsigned get_gl_query_type(QueryType);
-
 } // namespace Msp
 } // namespace GL
 
index 5ea10c43f6657c13eb3d0fe88824602d09420988..6f4770acb4e40a73ea6270bfeabcbeeec273fcc8 100644 (file)
@@ -1,9 +1,3 @@
-#include <msp/gl/extensions/arb_direct_state_access.h>
-#include <msp/gl/extensions/arb_sampler_objects.h>
-#include <msp/gl/extensions/arb_shadow.h>
-#include <msp/gl/extensions/ext_texture_filter_anisotropic.h>
-#include <msp/gl/extensions/ext_texture3d.h>
-#include <msp/gl/extensions/khr_debug.h>
 #include <msp/strings/format.h>
 #include "error.h"
 #include "sampler.h"
@@ -24,39 +18,11 @@ Sampler::Sampler():
        compare(false),
        cmp_func(LEQUAL),
        dirty_params(0)
-{
-       Require _req(ARB_sampler_objects);
-       Require _req2(EXT_texture3D);
-       Require _req3(ARB_shadow);
-       if(ARB_direct_state_access)
-               glCreateSamplers(1, &id);
-       else
-               glGenSamplers(1, &id);
-}
+{ }
 
 void Sampler::update() const
 {
-       if(dirty_params&MIN_FILTER)
-               glSamplerParameteri(id, GL_TEXTURE_MIN_FILTER, get_gl_filter(min_filter));
-       if(dirty_params&MAG_FILTER)
-               glSamplerParameteri(id, GL_TEXTURE_MAG_FILTER, get_gl_filter(mag_filter));
-       if(dirty_params&MAX_ANISOTROPY)
-               glSamplerParameterf(id, GL_TEXTURE_MAX_ANISOTROPY_EXT, max_anisotropy);
-       if(dirty_params&WRAP_S)
-               glSamplerParameteri(id, GL_TEXTURE_WRAP_S, get_gl_wrap(wrap_s));
-       if(dirty_params&WRAP_T)
-               glSamplerParameteri(id, GL_TEXTURE_WRAP_T, get_gl_wrap(wrap_t));
-       if(dirty_params&WRAP_R)
-               glSamplerParameteri(id, GL_TEXTURE_WRAP_R, get_gl_wrap(wrap_r));
-       if(dirty_params&BORDER_COLOR)
-               glSamplerParameterfv(id, GL_TEXTURE_BORDER_COLOR, &border_color.r);
-       if(dirty_params&COMPARE)
-       {
-               glSamplerParameteri(id, GL_TEXTURE_COMPARE_MODE, (compare ? GL_COMPARE_R_TO_TEXTURE : GL_NONE));
-               if(compare)
-                       glSamplerParameteri(id, GL_TEXTURE_COMPARE_FUNC, get_gl_predicate(cmp_func));
-       }
-
+       SamplerBackend::update(dirty_params);
        dirty_params = 0;
 }
 
@@ -82,10 +48,9 @@ void Sampler::set_max_anisotropy(float a)
 {
        if(a<1.0f)
                throw invalid_argument("Sampler::set_max_anisotropy");
-       else if(a>1.0f)
-               static Require _req(EXT_texture_filter_anisotropic);
+       bool supported = check_anisotropic(a>1.0f);
        max_anisotropy = a;
-       if(EXT_texture_filter_anisotropic)
+       if(supported)
                dirty_params |= MAX_ANISOTROPY;
 }
 
@@ -133,16 +98,6 @@ void Sampler::set_compare(Predicate f)
        dirty_params |= COMPARE;
 }
 
-void Sampler::set_debug_name(const string &name)
-{
-#ifdef DEBUG
-       if(id && KHR_debug)
-               glObjectLabel(GL_SAMPLER, id, name.size(), name.c_str());
-#else
-       (void)name;
-#endif
-}
-
 
 Sampler::Loader::Loader(Sampler &s):
        DataFile::ObjectLoader<Sampler>(s)
@@ -216,32 +171,6 @@ bool is_mipmapped(TextureFilter filter)
                filter==LINEAR_MIPMAP_NEAREST || filter==LINEAR_MIPMAP_LINEAR);
 }
 
-unsigned get_gl_filter(TextureFilter filter)
-{
-       switch(filter)
-       {
-       case NEAREST: return GL_NEAREST;
-       case LINEAR: return GL_LINEAR;
-       case NEAREST_MIPMAP_NEAREST: return GL_NEAREST_MIPMAP_NEAREST;
-       case NEAREST_MIPMAP_LINEAR: return GL_NEAREST_MIPMAP_LINEAR;
-       case LINEAR_MIPMAP_NEAREST: return GL_LINEAR_MIPMAP_NEAREST;
-       case LINEAR_MIPMAP_LINEAR: return GL_LINEAR_MIPMAP_LINEAR;
-       default: throw invalid_argument("get_gl_filter");
-       }
-}
-
-unsigned get_gl_wrap(TextureWrap wrap)
-{
-       switch(wrap)
-       {
-       case REPEAT: return GL_REPEAT;
-       case CLAMP_TO_EDGE: return GL_CLAMP_TO_EDGE;
-       case CLAMP_TO_BORDER: return GL_CLAMP_TO_BORDER;
-       case MIRRORED_REPEAT: return GL_MIRRORED_REPEAT;
-       default: throw invalid_argument("get_gl_wrap");
-       }
-}
-
 void operator>>(const LexicalConverter &c, TextureFilter &tf)
 {
        if(c.get()=="NEAREST")
index b2011d3bed3db8dc7631fc3224c37c6011d39c3f..bd8db31fdc8f9601f5ac2ac55e9762cead38fef4 100644 (file)
@@ -4,6 +4,7 @@
 #include <msp/datafile/objectloader.h>
 #include "color.h"
 #include "predicate.h"
+#include "sampler_backend.h"
 
 namespace Msp {
 namespace GL {
@@ -60,9 +61,9 @@ for magnification and NEAREST_MIPMAP_LINEAR for minification.
 If texture coordinates fall outside of the principal range of the texture,
 wrapping is applied.  The default for all directions is REPEAT.
 */
-class Sampler
+class Sampler: public SamplerBackend
 {
-       friend class PipelineState;
+       friend SamplerBackend;
 
 public:
        class Loader: public DataFile::ObjectLoader<Sampler>
@@ -97,7 +98,6 @@ private:
                COMPARE = 128
        };
 
-       unsigned id;
        TextureFilter min_filter;
        TextureFilter mag_filter;
        float max_anisotropy;
@@ -153,13 +153,11 @@ public:
 
        void refresh() const { if(dirty_params) update(); }
 
-       void set_debug_name(const std::string &);
+       using SamplerBackend::set_debug_name;
 };
 
 
 bool is_mipmapped(TextureFilter);
-unsigned get_gl_filter(TextureFilter);
-unsigned get_gl_wrap(TextureWrap);
 
 void operator>>(const LexicalConverter &, TextureFilter &);
 void operator>>(const LexicalConverter &, TextureWrap &);
index 57a191cfad95f83620b5ac1f35487adf9d49cc2f..2e8bbb4db865d5052885facfd96d25341ba1f906 100644 (file)
@@ -40,22 +40,6 @@ void StencilTest::Loader::actions(StencilOp sf, StencilOp df, StencilOp dp)
 }
 
 
-unsigned get_gl_stencil_op(StencilOp op)
-{
-       switch(op)
-       {
-       case KEEP: return GL_KEEP;
-       case SET_ZERO: return GL_ZERO;
-       case REPLACE: return GL_REPLACE;
-       case INCR: return GL_INCR;
-       case DECR: return GL_DECR;
-       case INVERT: return GL_INVERT;
-       case INCR_WRAP: return GL_INCR_WRAP;
-       case DECR_WRAP: return GL_DECR_WRAP;
-       default: throw invalid_argument("get_gl_stencil_op");
-       }
-}
-
 void operator>>(const LexicalConverter &conv, StencilOp &op)
 {
        const string &str = conv.get();
index e33882dfc566570c38da5b4e3dbb4c581a10d92c..652f8d4093b9ac3bdb82135d2a6fe834c36880ae 100644 (file)
@@ -47,12 +47,12 @@ struct StencilTest
        StencilTest();
 };
 
-unsigned get_gl_stencil_op(StencilOp);
-
 void operator>>(const LexicalConverter &, StencilOp &);
 void operator<<(LexicalConverter &, StencilOp);
 
 } // namespace GL
 } // namespace Msp
 
+#include "stenciltest_backend.h"
+
 #endif
index c27e850676b83153de81ef09b4d76282e481e091..4968e2e678bd6103c239103828cc1c78b0ff4b2b 100644 (file)
@@ -1,7 +1,3 @@
-#include <msp/gl/extensions/arb_direct_state_access.h>
-#include <msp/gl/extensions/arb_texture_swizzle.h>
-#include <msp/gl/extensions/ext_framebuffer_object.h>
-#include <msp/gl/extensions/khr_debug.h>
 #include <msp/io/memory.h>
 #include "error.h"
 #include "resourcemanager.h"
@@ -12,19 +8,8 @@ using namespace std;
 namespace Msp {
 namespace GL {
 
-const int Texture::swizzle_orders[] =
-{
-       GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA,
-       GL_RED, GL_RED, GL_RED, GL_ONE,
-       GL_RED, GL_RED, GL_RED, GL_GREEN,
-       GL_BLUE, GL_GREEN, GL_RED, GL_ALPHA
-};
-
-Texture *Texture::scratch_binding = 0;
-
-Texture::Texture(GLenum t, ResourceManager *m):
-       id(0),
-       target(t),
+Texture::Texture(unsigned t, ResourceManager *m):
+       TextureBackend(t, !m),
        format(NO_PIXELFORMAT),
        storage_fmt(format),
        swizzle(NO_SWIZZLE),
@@ -33,38 +18,6 @@ Texture::Texture(GLenum t, ResourceManager *m):
 {
        if(m)
                set_manager(m);
-       else
-               generate_id();
-
-       static bool alignment_init = false;
-       if(!alignment_init)
-       {
-               glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
-               alignment_init = true;
-       }
-}
-
-Texture::~Texture()
-{
-       if(this==scratch_binding)
-               unbind_scratch();
-       if(id)
-               glDeleteTextures(1, &id);
-}
-
-void Texture::generate_id()
-{
-       if(id)
-               throw invalid_operation("Texture::generate_id");
-       if(ARB_direct_state_access)
-               glCreateTextures(target, 1, &id);
-       else
-               glGenTextures(1, &id);
-
-#ifdef DEBUG
-       if(!debug_name.empty() && KHR_debug)
-               glObjectLabel(GL_TEXTURE, id, debug_name.size(), debug_name.c_str());
-#endif
 }
 
 void Texture::set_format(PixelFormat fmt)
@@ -96,56 +49,13 @@ void Texture::set_format(PixelFormat fmt)
        PixelFormat st_fmt = make_pixelformat(st_comp, get_component_type(fmt), is_srgb(fmt));
        require_pixelformat(st_fmt);
        if(swiz!=NO_SWIZZLE)
-               static Require _req(ARB_texture_swizzle);
+               require_swizzle();
 
        format = fmt;
        storage_fmt = st_fmt;
        swizzle = swiz;
 }
 
-void Texture::apply_swizzle()
-{
-       if(swizzle==NO_SWIZZLE)
-               return;
-
-       if(get_backend_api()==OPENGL_ES)
-       {
-               set_parameter_i(GL_TEXTURE_SWIZZLE_R, swizzle_orders[swizzle*4]);
-               set_parameter_i(GL_TEXTURE_SWIZZLE_G, swizzle_orders[swizzle*4+1]);
-               set_parameter_i(GL_TEXTURE_SWIZZLE_B, swizzle_orders[swizzle*4+2]);
-               set_parameter_i(GL_TEXTURE_SWIZZLE_A, swizzle_orders[swizzle*4+3]);
-       }
-       else
-       {
-               if(ARB_direct_state_access)
-                       glTextureParameteriv(id, GL_TEXTURE_SWIZZLE_RGBA, swizzle_orders+swizzle*4);
-               else
-                       glTexParameteriv(target, GL_TEXTURE_SWIZZLE_RGBA, swizzle_orders+swizzle*4);
-       }
-}
-
-void Texture::set_parameter_i(unsigned param, int value) const
-{
-       if(ARB_direct_state_access)
-               glTextureParameteri(id, param, value);
-       else
-               glTexParameteri(target, param, value);
-}
-
-void Texture::generate_mipmap()
-{
-       // glGenerateMipmap is defined here
-       static Require _req(EXT_framebuffer_object);
-
-       if(ARB_direct_state_access)
-               glGenerateTextureMipmap(id);
-       else
-       {
-               bind_scratch();
-               glGenerateMipmap(target);
-       }
-}
-
 void Texture::load_image(const string &fn, unsigned lv)
 {
        Graphics::Image img;
@@ -154,39 +64,6 @@ void Texture::load_image(const string &fn, unsigned lv)
        image(img, lv);
 }
 
-void Texture::set_debug_name(const string &name)
-{
-#ifdef DEBUG
-       debug_name = name;
-       if(id && KHR_debug)
-               glObjectLabel(GL_TEXTURE, id, name.size(), name.c_str());
-#else
-       (void)name;
-#endif
-}
-
-void Texture::bind_scratch()
-{
-       if(!scratch_binding)
-               glActiveTexture(GL_TEXTURE0);
-       if(scratch_binding!=this)
-       {
-               if(scratch_binding && scratch_binding->target!=target)
-                       glBindTexture(scratch_binding->target, 0);
-               glBindTexture(target, id);
-               scratch_binding = this;
-       }
-}
-
-void Texture::unbind_scratch()
-{
-       if(scratch_binding)
-       {
-               glBindTexture(scratch_binding->target, 0);
-               scratch_binding = 0;
-       }
-}
-
 
 Texture::Loader::Loader(Texture &t):
        DataFile::CollectionObjectLoader<Texture>(t, 0)
index 47b2f2696efd35bd07b0c8c52811cc46359d2d63..6073cdcb2fcb5e071145731e4c1c88abbf1d02b0 100644 (file)
@@ -5,6 +5,7 @@
 #include <msp/graphics/image.h>
 #include "pixelformat.h"
 #include "resource.h"
+#include "texture_backend.h"
 
 namespace Msp {
 namespace GL {
@@ -18,10 +19,10 @@ A texture can consinst of a stack of images, called a mipmap.  The dimensions
 of each mipmap level are half that of the previous level.  The mipmap stack
 can be used for texture minification; see the Sampler class for details.
 */
-class Texture: public Resource
+class Texture: public TextureBackend, public Resource
 {
-       friend class Framebuffer;
-       friend class PipelineState;
+       friend TextureBackend;
+
 protected:
        class Loader: public DataFile::CollectionObjectLoader<Texture>
        {
@@ -56,34 +57,20 @@ protected:
                RGB_TO_BGR
        };
 
-       unsigned id;
-       unsigned target;
        PixelFormat format;
        PixelFormat storage_fmt;
        FormatSwizzle swizzle;
        bool use_srgb_format;
        bool auto_gen_mipmap;
-       std::string debug_name;
-
-       static const int swizzle_orders[];
-       static Texture *scratch_binding;
 
        Texture(unsigned, ResourceManager * = 0);
-       Texture(const Texture &);
-       Texture &operator=(const Texture &);
-public:
-       ~Texture();
 
-protected:
-       void generate_id();
        void set_format(PixelFormat);
-       void apply_swizzle();
-       void set_parameter_i(unsigned, int) const;
 
 public:
        PixelFormat get_format() const { return format; }
 
-       void generate_mipmap();
+       using TextureBackend::generate_mipmap;
 
        /// Loads a Graphics::Image from a file and uploads it to the texture.
        virtual void load_image(const std::string &, unsigned = 0);
@@ -95,12 +82,7 @@ public:
 
        virtual std::uint64_t get_data_size() const { return 0; }
 
-       void set_debug_name(const std::string &);
-
-protected:
-       void bind_scratch();
-public:
-       static void unbind_scratch();
+       using TextureBackend::set_debug_name;
 };
 
 } // namespace GL
index 76ff946fc45959f58af87dfa93851c5c2f0a3327..1733a2d8e252b7c8bc4b330120a9500b3eb00ee5 100644 (file)
@@ -1,6 +1,3 @@
-#include <msp/gl/extensions/arb_direct_state_access.h>
-#include <msp/gl/extensions/arb_texture_storage.h>
-#include <msp/gl/extensions/msp_texture1d.h>
 #include "error.h"
 #include "texture1d.h"
 
@@ -10,11 +7,8 @@ namespace Msp {
 namespace GL {
 
 Texture1D::Texture1D():
-       Texture(GL_TEXTURE_1D),
        width(0)
-{
-       static Require _req(MSP_texture1D);
-}
+{ }
 
 void Texture1D::storage(PixelFormat fmt, unsigned wd, unsigned lv)
 {
@@ -33,28 +27,7 @@ void Texture1D::storage(PixelFormat fmt, unsigned wd, unsigned lv)
        if(lv)
                levels = min(levels, lv);
 
-       GLenum gl_fmt = get_gl_pixelformat(storage_fmt);
-       if(ARB_texture_storage)
-       {
-               if(ARB_direct_state_access)
-                       glTextureStorage1D(id, levels, gl_fmt, width);
-               else
-               {
-                       bind_scratch();
-                       glTexStorage1D(target, levels, gl_fmt, width);
-               }
-       }
-       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)
-                       glTexImage1D(target, i, gl_fmt, get_level_size(i), 0, comp, type, 0);
-       }
-
-       apply_swizzle();
+       allocate();
 }
 
 void Texture1D::image(unsigned level, const void *data)
@@ -69,15 +42,7 @@ void Texture1D::sub_image(unsigned level, int x, unsigned wd, const void *data)
        if(level>=levels)
                throw out_of_range("Texture1D::sub_image");
 
-       GLenum comp = get_gl_components(get_components(storage_fmt));
-       GLenum type = get_gl_type(get_component_type(storage_fmt));
-       if(ARB_direct_state_access)
-               glTextureSubImage1D(id, level, x, wd, comp, type, data);
-       else
-       {
-               bind_scratch();
-               glTexSubImage1D(target, level, x, wd, comp, type, data);
-       }
+       Texture1DBackend::sub_image(level, x, wd, data);
 }
 
 void Texture1D::image(const Graphics::Image &img, unsigned lv)
index e77f6484242d1b177a5acff236245fd7ad6988bf..1871a17caaa4d282ae1fee19fb656c3686808029 100644 (file)
@@ -2,13 +2,15 @@
 #define MSP_GL_TEXTURE1D_H_
 
 #include <string>
-#include "texture.h"
+#include "texture1d_backend.h"
 
 namespace Msp {
 namespace GL {
 
-class Texture1D: public Texture
+class Texture1D: public Texture1DBackend
 {
+       friend Texture1DBackend;
+
 public:
        class Loader: public DataFile::DerivedObjectLoader<Texture1D, Texture::Loader>
        {
index 70a9528d9af9b030dec83c6c88f18dfedce47bd4..04ad6ed8d25fe5f61ef7e7893a33d238fcf31df5 100644 (file)
@@ -1,8 +1,3 @@
-#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 "error.h"
 #include "texture2d.h"
 
@@ -11,29 +6,8 @@ using namespace std;
 namespace Msp {
 namespace GL {
 
-class Texture2D::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();
-};
-
-
 Texture2D::Texture2D(ResourceManager *m):
-       Texture(GL_TEXTURE_2D, m),
+       Texture2DBackend(m),
        width(0),
        height(0)
 { }
@@ -61,31 +35,7 @@ void Texture2D::storage(PixelFormat fmt, unsigned wd, unsigned ht, unsigned lv)
        if(lv>0)
                levels = min(levels, lv);
 
-       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)
-               {
-                       LinAl::Vector<unsigned, 2> lv_size = get_level_size(i);
-                       glTexImage2D(target, i, gl_fmt, lv_size.x, lv_size.y, 0, comp, type, 0);
-               }
-       }
-
-       apply_swizzle();
+       allocate();
 }
 
 void Texture2D::image(unsigned level, const void *data)
@@ -101,22 +51,7 @@ void Texture2D::sub_image(unsigned level, int x, int y, unsigned wd, unsigned ht
        if(level>=levels)
                throw out_of_range("Texture2D::sub_image");
 
-       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 Texture2D::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);
+       Texture2DBackend::sub_image(level, x, y, wd, ht, data);
 }
 
 void Texture2D::image(const Graphics::Image &img, unsigned lv)
@@ -147,8 +82,7 @@ LinAl::Vector<unsigned, 2> Texture2D::get_level_size(unsigned level) const
 
 Resource::AsyncLoader *Texture2D::load(IO::Seekable &io, const Resources *)
 {
-       AsyncLoader *ldr = new AsyncLoader(*this, io);
-       return ldr;
+       return create_async_loader(io);
 }
 
 uint64_t Texture2D::get_data_size() const
@@ -156,12 +90,6 @@ uint64_t Texture2D::get_data_size() const
        return id ? width*height*get_pixel_size(format) : 0;
 }
 
-void Texture2D::unload()
-{
-       glDeleteTextures(1, &id);
-       id = 0;
-}
-
 
 Texture2D::Loader::Loader(Texture2D &t):
        DataFile::DerivedObjectLoader<Texture2D, Texture::Loader>(t)
@@ -203,65 +131,5 @@ void Texture2D::Loader::storage_levels(PixelFormat fmt, unsigned w, unsigned h,
        obj.storage(fmt, w, h, l);
 }
 
-
-Texture2D::AsyncLoader::AsyncLoader(Texture2D &t, IO::Seekable &i):
-       texture(t),
-       io(i),
-       mapped_address(0),
-       img_loader(Graphics::ImageLoader::open_io(io)),
-       phase(0)
-{ }
-
-Texture2D::AsyncLoader::~AsyncLoader()
-{
-       if(mapped_address)
-               pixel_buffer.unmap();
-       delete img_loader;
-}
-
-bool Texture2D::AsyncLoader::needs_sync() const
-{
-       return phase%2;
-}
-
-bool Texture2D::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.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
index 0ef4fc1fbefde4ed730cb41e2e1cd631bf4b8d24..a2454b76b3e613dd552599a2e545d7aef4716d0a 100644 (file)
@@ -3,20 +3,20 @@
 
 #include <string>
 #include <msp/linal/vector.h>
-#include "texture.h"
+#include "texture2d_backend.h"
 
 namespace Msp {
 namespace GL {
 
-class Buffer;
-
 /**
 Two-dimensional texture.  Consists of an array of texels in the shape of a
 rectangle.  Texture coordinate have a range of [0, 1].  Coordinates outside of
 this range are subject to wrapping.  This is the most common type of texture.
 */
-class Texture2D: public Texture
+class Texture2D: public Texture2DBackend
 {
+       friend Texture2DBackend;
+
 public:
        class Loader: public Msp::DataFile::DerivedObjectLoader<Texture2D, Texture::Loader>
        {
@@ -32,8 +32,6 @@ public:
        };
 
 private:
-       class AsyncLoader;
-
        unsigned width;
        unsigned height;
        unsigned levels;
@@ -59,10 +57,6 @@ public:
        and the update region must be fully inside the texture. */
        void sub_image(unsigned level, int x, int y, unsigned wd, unsigned ht, const void *data);
 
-private:
-       void sub_image(unsigned, int, int, unsigned, unsigned, const Buffer &, unsigned);
-
-public:
        /** Updates the contents of the entire texture from an image.  If storage
        has not been defined, it will be set to match the image.  Otherwise the
        image must match the defined storage. */
@@ -80,7 +74,7 @@ private:
 public:
        virtual Resource::AsyncLoader *load(IO::Seekable &, const Resources * = 0);
        virtual std::uint64_t get_data_size() const;
-       virtual void unload();
+       using Texture2DBackend::unload;
 };
 
 } // namespace GL
index 1c7d363e4dcd5b3cdbfd8ab6e4c0b42ca1df7f41..52f5fd7139fab95d8f72c9bcbbefbc76b0640a9a 100644 (file)
@@ -1,4 +1,3 @@
-#include <msp/gl/extensions/ext_texture_array.h>
 #include "error.h"
 #include "texture2darray.h"
 
@@ -7,12 +6,6 @@ using namespace std;
 namespace Msp {
 namespace GL {
 
-Texture2DArray::Texture2DArray():
-       Texture3D(GL_TEXTURE_2D_ARRAY)
-{
-       static Require _req(EXT_texture_array);
-}
-
 void Texture2DArray::layer_image(unsigned level, unsigned z, const void *data)
 {
        if(level>=levels || z>=depth)
index 21930f882e813a1240b5fe30eda5deb87b2c30f0..662fbad8c71c7ac8de37fed1280e47062ccaa0f1 100644 (file)
@@ -1,7 +1,7 @@
 #ifndef MSP_GL_TEXTURE2DARRAY_H_
 #define MSP_GL_TEXTURE2DARRAY_H_
 
-#include "texture3d.h"
+#include "texture2darray_backend.h"
 
 namespace Msp {
 namespace GL {
@@ -11,7 +11,7 @@ An array of two-dimensional textures.  It's very much like a 3D texture, with
 two important differences: there's no filtering nor mipmapping along the third
 dimension.
 */
-class Texture2DArray: public Texture3D
+class Texture2DArray: public Texture2DArrayBackend
 {
 public:
        class Loader: public Msp::DataFile::DerivedObjectLoader<Texture2DArray, Texture3D::Loader>
@@ -25,8 +25,6 @@ public:
                void external_image(unsigned, const std::string &);
        };
 
-       Texture2DArray();
-
        void layer_image(unsigned, unsigned, const void *);
        void layer_image(unsigned, unsigned, const Graphics::Image &);
 
index 782a55694961e55a979ba772c5f28c5e766f5a51..5ea37e90e3d82e81f2674f94691f2051c25f1a69 100644 (file)
@@ -1,6 +1,3 @@
-#include <msp/gl/extensions/arb_direct_state_access.h>
-#include <msp/gl/extensions/arb_texture_multisample.h>
-#include <msp/gl/extensions/arb_texture_storage_multisample.h>
 #include "deviceinfo.h"
 #include "error.h"
 #include "texture2dmultisample.h"
@@ -11,12 +8,9 @@ namespace Msp {
 namespace GL {
 
 Texture2DMultisample::Texture2DMultisample():
-       Texture(GL_TEXTURE_2D_MULTISAMPLE),
        width(0),
        height(0)
-{
-       static Require _req(ARB_texture_multisample);
-}
+{ }
 
 void Texture2DMultisample::storage(PixelFormat fmt, unsigned wd, unsigned ht, unsigned sm)
 {
@@ -36,23 +30,7 @@ void Texture2DMultisample::storage(PixelFormat fmt, unsigned wd, unsigned ht, un
        height = ht;
        samples = sm;
 
-       GLenum gl_fmt = get_gl_pixelformat(storage_fmt);
-       if(ARB_texture_storage_multisample)
-       {
-               if(ARB_direct_state_access)
-                       glTextureStorage2DMultisample(id, samples, gl_fmt, width, height, false);
-               else
-               {
-                       bind_scratch();
-                       glTexStorage2DMultisample(target, samples, gl_fmt, width, height, false);
-               }
-       }
-       else
-       {
-               bind_scratch();
-               glTexImage2DMultisample(target, samples, gl_fmt, width, height, false);
-       }
-       apply_swizzle();
+       allocate();
 }
 
 void Texture2DMultisample::image(const Graphics::Image &, unsigned)
index 94323ecae553c52403b11138d3dad50d2875971a..9710ad7847b131baefc4889c396d54163e53b20f 100644 (file)
@@ -1,13 +1,15 @@
 #ifndef MSP_GL_TEXTURE2DMULTISAMPLE_H_
 #define MSP_GL_TEXTURE2DMULTISAMPLE_H_
 
-#include "texture.h"
+#include "texture2dmultisample_backend.h"
 
 namespace Msp {
 namespace GL {
 
-class Texture2DMultisample: public Texture
+class Texture2DMultisample: public Texture2DMultisampleBackend
 {
+       friend Texture2DMultisampleBackend;
+
 private:
        unsigned width;
        unsigned height;
index c35d45741ed14aa71bca1c1b2420ab785476dc5d..0fb26ccd6e2606eea34f24cc30fc5e9209ae08c8 100644 (file)
@@ -1,8 +1,4 @@
 #include <cmath>
-#include <msp/gl/extensions/arb_direct_state_access.h>
-#include <msp/gl/extensions/arb_texture_storage.h>
-#include <msp/gl/extensions/ext_texture3d.h>
-#include <msp/gl/extensions/ext_texture_array.h>
 #include "error.h"
 #include "texture3d.h"
 
@@ -11,21 +7,18 @@ using namespace std;
 namespace Msp {
 namespace GL {
 
-Texture3D::Texture3D(GLenum t):
-       Texture(t),
+Texture3D::Texture3D(unsigned t):
+       Texture3DBackend(t),
        width(0),
        height(0),
        depth(0)
 { }
 
 Texture3D::Texture3D():
-       Texture(GL_TEXTURE_3D),
        width(0),
        height(0),
        depth(0)
-{
-       static Require _req(EXT_texture3D);
-}
+{ }
 
 void Texture3D::storage(PixelFormat fmt, unsigned wd, unsigned ht, unsigned dp, unsigned lv)
 {
@@ -46,31 +39,7 @@ void Texture3D::storage(PixelFormat fmt, unsigned wd, unsigned ht, unsigned dp,
        if(lv>0)
                levels = min(levels, lv);
 
-       GLenum gl_fmt = get_gl_pixelformat(storage_fmt);
-       if(ARB_texture_storage)
-       {
-               if(ARB_direct_state_access)
-                       glTextureStorage3D(id, levels, gl_fmt, width, height, depth);
-               else
-               {
-                       bind_scratch();
-                       glTexStorage3D(target, levels, gl_fmt, width, height, depth);
-               }
-       }
-       else
-       {
-               bind_scratch();
-               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)
-               {
-                       LinAl::Vector<unsigned, 3> lv_size = get_level_size(i);
-                       glTexImage3D(target, i, gl_fmt, lv_size.x, lv_size.y, lv_size.z, 0, comp, type, 0);
-               }
-               glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, levels-1);
-       }
-
-       apply_swizzle();
+       allocate();
 }
 
 void Texture3D::image(unsigned level, const void *data)
@@ -86,15 +55,7 @@ void Texture3D::sub_image(unsigned level, int x, int y, int z, unsigned wd, unsi
        if(level>=levels)
                throw out_of_range("Texture3D::sub_image");
 
-       GLenum comp = get_gl_components(get_components(storage_fmt));
-       GLenum type = get_gl_type(get_component_type(storage_fmt));
-       if(ARB_direct_state_access)
-               glTextureSubImage3D(id, level, x, y, z, wd, ht, dp, comp, type, data);
-       else
-       {
-               bind_scratch();
-               glTexSubImage3D(target, level, x, y, z, wd, ht, dp, comp, type, data);
-       }
+       Texture3DBackend::sub_image(level, x, y, z, wd, ht, dp, data);
 }
 
 void Texture3D::image(const Graphics::Image &img, unsigned lv)
@@ -114,7 +75,7 @@ void Texture3D::image(const Graphics::Image &img, unsigned lv)
 unsigned Texture3D::get_n_levels() const
 {
        unsigned s = max(width, height);
-       if(target!=GL_TEXTURE_2D_ARRAY)
+       if(!is_array())
                s = max(s, depth);
        unsigned n = 0;
        for(; s; s>>=1, ++n) ;
@@ -126,7 +87,7 @@ LinAl::Vector<unsigned, 3> Texture3D::get_level_size(unsigned level) const
        unsigned w = width>>level;
        unsigned h = height>>level;
        unsigned d = depth;
-       if(target!=GL_TEXTURE_2D_ARRAY)
+       if(!is_array())
                d >>= level;
 
        if(!w && (h || d))
index 7b88db50d36f6afed388d2cae9dd61685a5204ac..21893abc07e5fc19c4915cb34c4882086d5bdf36 100644 (file)
@@ -3,7 +3,7 @@
 
 #include <string>
 #include <msp/linal/vector.h>
-#include "texture.h"
+#include "texture3d_backend.h"
 
 namespace Msp {
 namespace GL {
@@ -12,8 +12,10 @@ namespace GL {
 Three-dimensional texture.  Consists of an array of texels in the shape of a
 right cuboid.  Texture coordinates have a principal range of [0, 1].
 */
-class Texture3D: public Texture
+class Texture3D: public Texture3DBackend
 {
+       friend Texture3DBackend;
+
 public:
        class Loader: public Msp::DataFile::DerivedObjectLoader<Texture3D, Texture::Loader>
        {
@@ -34,7 +36,7 @@ protected:
        unsigned depth;
        unsigned levels;
 
-       Texture3D(GLenum);
+       Texture3D(unsigned);
 public:
        Texture3D();
 
index 6239688cb304d66a381298d8057d4c9d3330a046..ea29499732646897dd9e81c7080c8cc3bc9ff013 100644 (file)
@@ -1,8 +1,4 @@
 #include <msp/datafile/collection.h>
-#include <msp/gl/extensions/arb_direct_state_access.h>
-#include <msp/gl/extensions/arb_seamless_cube_map.h>
-#include <msp/gl/extensions/arb_texture_cube_map.h>
-#include <msp/gl/extensions/arb_texture_storage.h>
 #include <msp/io/memory.h>
 #include <msp/strings/format.h>
 #include "error.h"
@@ -34,20 +30,8 @@ const unsigned TextureCube::orientations[12] =
 };
 
 TextureCube::TextureCube():
-       Texture(GL_TEXTURE_CUBE_MAP),
        size(0)
-{
-       static Require _req(ARB_texture_cube_map);
-       if(ARB_seamless_cube_map)
-       {
-               static bool seamless_init = false;
-               if(!seamless_init)
-               {
-                       glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
-                       seamless_init = true;
-               }
-       }
-}
+{ }
 
 void TextureCube::storage(PixelFormat fmt, unsigned sz, unsigned lv)
 {
@@ -66,32 +50,7 @@ void TextureCube::storage(PixelFormat fmt, unsigned sz, unsigned lv)
        if(lv>0)
                levels = min(levels, lv);
 
-       GLenum gl_fmt = get_gl_pixelformat(storage_fmt);
-       if(ARB_texture_storage)
-       {
-               if(ARB_direct_state_access)
-                       glTextureStorage2D(id, levels, gl_fmt, size, size);
-               else
-               {
-                       bind_scratch();
-                       glTexStorage2D(target, levels, gl_fmt, size, size);
-               }
-       }
-       else
-       {
-               bind_scratch();
-               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)
-               {
-                       unsigned lv_size = get_level_size(i);
-                       for(unsigned j=0; j<6; ++j)
-                               glTexImage2D(get_gl_cube_face(static_cast<TextureCubeFace>(j)), i, gl_fmt, lv_size, lv_size, 0, comp, type, 0);
-               }
-               glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, levels-1);
-       }
-
-       apply_swizzle();
+       allocate();
 }
 
 void TextureCube::image(TextureCubeFace face, unsigned level, const void *data)
@@ -107,16 +66,7 @@ void TextureCube::sub_image(TextureCubeFace face, unsigned level, int x, int y,
        if(level>=levels)
                throw out_of_range("TextureCube::sub_image");
 
-       GLenum gl_face = get_gl_cube_face(face);
-       GLenum comp = get_gl_components(get_components(storage_fmt));
-       GLenum type = get_gl_type(get_component_type(storage_fmt));
-       if(ARB_direct_state_access)
-               glTextureSubImage3D(id, level, x, y, face, wd, ht, 1, comp, type, data);
-       else
-       {
-               bind_scratch();
-               glTexSubImage2D(gl_face, level, x, y, wd, ht, comp, type, data);
-       }
+       TextureCubeBackend::sub_image(face, level, x, y, wd, ht, data);
 }
 
 void TextureCube::image(TextureCubeFace face, const Graphics::Image &img)
@@ -248,20 +198,6 @@ void TextureCube::Loader::storage_levels(PixelFormat fmt, unsigned s, unsigned l
 }
 
 
-unsigned get_gl_cube_face(TextureCubeFace face)
-{
-       switch(face)
-       {
-       case POSITIVE_X: return GL_TEXTURE_CUBE_MAP_POSITIVE_X;
-       case NEGATIVE_X: return GL_TEXTURE_CUBE_MAP_NEGATIVE_X;
-       case POSITIVE_Y: return GL_TEXTURE_CUBE_MAP_POSITIVE_Y;
-       case NEGATIVE_Y: return GL_TEXTURE_CUBE_MAP_NEGATIVE_Y;
-       case POSITIVE_Z: return GL_TEXTURE_CUBE_MAP_POSITIVE_Z;
-       case NEGATIVE_Z: return GL_TEXTURE_CUBE_MAP_NEGATIVE_Z;
-       default: throw invalid_argument("get_gl_cube_face");
-       }
-}
-
 void operator>>(const LexicalConverter &conv, TextureCubeFace &face)
 {
        const string &str = conv.get();
index 8bb5cf43131f101fb5089b7913d091a510ada681..363ceea331e2f453490813741e6631c255701874 100644 (file)
@@ -2,7 +2,7 @@
 #define MSP_GL_TEXTURECUBE_H_
 
 #include <msp/graphics/image.h>
-#include "texture.h"
+#include "texturecube_backend.h"
 #include "vector.h"
 
 namespace Msp {
@@ -30,8 +30,10 @@ All faces of a cube map texture must be allocated for it to be usable.
 
 Requires OpenGL version 1.3.
 */
-class TextureCube: public Texture
+class TextureCube: public TextureCubeBackend
 {
+       friend TextureCubeBackend;
+
 public:
        class Loader: public Msp::DataFile::DerivedObjectLoader<TextureCube, Texture::Loader>
        {
@@ -103,8 +105,6 @@ public:
        virtual void unload() { }
 };
 
-unsigned get_gl_cube_face(TextureCubeFace);
-
 void operator>>(const LexicalConverter &, TextureCubeFace &);
 
 } // namespace GL
index b06ddf926a0f3a34159ed9a2b7921a1ad708d018..18580149ce9f227c7ec00fe94bb573f5059cefcf 100644 (file)
@@ -1,6 +1,4 @@
 #include <algorithm>
-#include <msp/gl/extensions/arb_shader_objects.h>
-#include <msp/gl/extensions/arb_uniform_buffer_object.h>
 #include "deviceinfo.h"
 #include "uniformblock.h"
 
@@ -10,12 +8,9 @@ namespace Msp {
 namespace GL {
 
 UniformBlock::UniformBlock(const ReflectData::UniformBlockInfo &info):
+       UniformBlockBackend(info.bind_point>=0),
        data(info.data_size)
-{
-       static Require _req(ARB_shader_objects);
-       if(info.bind_point>=0)
-               static Require _req2(ARB_uniform_buffer_object);
-}
+{ }
 
 unsigned UniformBlock::get_alignment() const
 {
index ec42089a8823bb46ea75f8e94467b766f2d40459..de8b90e4bc5d2d86fd1ebc3749ec59f7ff108066 100644 (file)
@@ -5,6 +5,7 @@
 #include <msp/core/noncopyable.h>
 #include "bufferable.h"
 #include "reflectdata.h"
+#include "uniformblock_backend.h"
 
 namespace Msp {
 namespace GL {
@@ -13,7 +14,7 @@ namespace GL {
 Stores uniforms with a specific layout.  Both named and default uniform blocks
 are supported.
 */
-class UniformBlock: public NonCopyable, public Bufferable
+class UniformBlock: public UniformBlockBackend, public NonCopyable, public Bufferable
 {
 private:
        std::vector<char> data;
index 892cec86f2d8df5c6825b4ca07f9a6266b8e5e5c..4fd589a9698bfe8a1616a4d097902a984e4f9c49 100644 (file)
@@ -1,12 +1,3 @@
-#include <msp/gl/extensions/arb_direct_state_access.h>
-#include <msp/gl/extensions/arb_instanced_arrays.h>
-#include <msp/gl/extensions/arb_vertex_array_object.h>
-#include <msp/gl/extensions/arb_vertex_attrib_binding.h>
-#include <msp/gl/extensions/arb_vertex_buffer_object.h>
-#include <msp/gl/extensions/arb_vertex_shader.h>
-#include <msp/gl/extensions/ext_gpu_shader4.h>
-#include <msp/gl/extensions/khr_debug.h>
-#include "buffer.h"
 #include "deviceinfo.h"
 #include "error.h"
 #include "vertexarray.h"
@@ -23,18 +14,7 @@ VertexSetup::VertexSetup():
        inst_array(0),
        index_buffer(0),
        index_type(UNSIGNED_SHORT)
-{
-       static Require req(ARB_vertex_array_object);
-       if(ARB_direct_state_access)
-               glCreateVertexArrays(1, &id);
-       else
-               glGenVertexArrays(1, &id);
-}
-
-VertexSetup::~VertexSetup()
-{
-       glDeleteVertexArrays(1, &id);
-}
+{ }
 
 void VertexSetup::set_format(const VertexFormat &vfmt)
 {
@@ -104,112 +84,15 @@ bool VertexSetup::verify_format(const VertexFormat &fmt)
        return all_of(fmt.begin(), fmt.end(), [](VertexAttribute a){ return get_attribute_semantic(a)<max_attribs; });
 }
 
-void VertexSetup::require_format(const VertexFormat &fmt, bool instanced)
-{
-       if(any_of(fmt.begin(), fmt.end(), is_integer_attribute))
-               static Require _req(EXT_gpu_shader4);
-       if(instanced)
-               static Require req(ARB_instanced_arrays);
-}
-
 void VertexSetup::update() const
 {
-       static bool direct = ARB_direct_state_access && ARB_vertex_attrib_binding;
-
-       if(dirty&VERTEX_ARRAY)
-               update_vertex_array(*vertex_array, 0, 0, direct);
-
-       if(dirty&INSTANCE_ARRAY)
-               update_vertex_array(*inst_array, 1, 1, direct);
-
-       if(dirty&INDEX_BUFFER)
-       {
-               if(direct)
-                       glVertexArrayElementBuffer(id, index_buffer->id);
-               else
-                       glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_buffer->id);
-       }
-
+       VertexSetupBackend::update(dirty);
        dirty = 0;
 }
 
-void VertexSetup::update_vertex_array(const VertexArray &array, unsigned binding, unsigned divisor, bool direct) const
-{
-       if(!direct)
-       {
-               Buffer::unbind_scratch();
-               glBindBuffer(GL_ARRAY_BUFFER, array.get_buffer()->id);
-       }
-
-       const VertexFormat &fmt = array.get_format();
-       unsigned stride = fmt.stride();
-       if(direct)
-       {
-               glVertexArrayVertexBuffer(id, binding, array.get_buffer()->id, 0, stride);
-               glVertexArrayBindingDivisor(id, binding, divisor);
-       }
-
-       unsigned offset = 0;
-       for(VertexAttribute a: fmt)
-       {
-               unsigned sem = get_attribute_semantic(a);
-               bool integer = is_integer_attribute(a);
-               GLenum type = get_gl_type(get_attribute_source_type(a));
-               unsigned cc = get_attribute_component_count(a);
-               if(direct)
-               {
-                       if(integer)
-                               glVertexArrayAttribIFormat(id, sem, cc, type, offset);
-                       else
-                               glVertexArrayAttribFormat(id, sem, cc, type, true, offset);
-                       glVertexArrayAttribBinding(id, sem, binding);
-                       glEnableVertexArrayAttrib(id, sem);
-               }
-               else
-               {
-                       if(integer)
-                               glVertexAttribIPointer(sem, cc, type, stride, reinterpret_cast<void *>(offset));
-                       else
-                               glVertexAttribPointer(sem, cc, type, true, stride, reinterpret_cast<void *>(offset));
-                       if(ARB_instanced_arrays)
-                               glVertexAttribDivisor(sem, divisor);
-                       glEnableVertexAttribArray(sem);
-               }
-               offset += get_attribute_size(a);
-       }
-
-       if(!direct)
-               glBindBuffer(GL_ARRAY_BUFFER, 0);
-}
-
 void VertexSetup::unload()
 {
-       if(ARB_direct_state_access)
-       {
-               glVertexArrayVertexBuffer(id, 0, 0, 0, 0);
-               glVertexArrayVertexBuffer(id, 1, 0, 0, 0);
-               glVertexArrayElementBuffer(id, 0);
-       }
-       else
-       {
-               glBindVertexArray(id);
-               glBindBuffer(GL_ARRAY_BUFFER, 0);
-
-               for(VertexAttribute a: vertex_format)
-               {
-                       unsigned sem = get_attribute_semantic(a);
-                       glDisableVertexAttribArray(sem);
-                       glVertexAttribPointer(sem, 1, GL_FLOAT, false, 0, 0);
-               }
-               for(VertexAttribute a: inst_format)
-               {
-                       unsigned sem = get_attribute_semantic(a);
-                       glDisableVertexAttribArray(sem);
-                       glVertexAttribPointer(sem, 1, GL_FLOAT, false, 0, 0);
-               }
-
-               glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
-       }
+       VertexSetupBackend::unload();
 
        vertex_array = 0;
        vertex_format = VertexFormat();
@@ -218,15 +101,5 @@ void VertexSetup::unload()
        index_buffer = 0;
 }
 
-void VertexSetup::set_debug_name(const string &name)
-{
-#ifdef DEBUG
-       if(KHR_debug)
-               glObjectLabel(GL_VERTEX_ARRAY, id, name.size(), name.c_str());
-#else
-       (void)name;
-#endif
-}
-
 } // namespace GL
 } // namespace Msp
index beade58ed3daeb9b4421193a3b5d97524c8265c1..7daef8845ac03688c570af5c40ff10f8a80324dc 100644 (file)
@@ -3,6 +3,7 @@
 
 #include "datatype.h"
 #include "vertexformat.h"
+#include "vertexsetup_backend.h"
 
 namespace Msp {
 namespace GL {
@@ -14,9 +15,9 @@ class VertexArray;
 Combines a VertexArray with an index buffer.  This wraps OpenGL's vertex array
 objects.  Intended for internal use.
 */
-class VertexSetup
+class VertexSetup: public VertexSetupBackend
 {
-       friend class PipelineState;
+       friend VertexSetupBackend;
 
 private:
        enum ComponentMask
@@ -26,7 +27,6 @@ private:
                INDEX_BUFFER = 4
        };
 
-       unsigned id;
        mutable unsigned dirty;
        const VertexArray *vertex_array;
        VertexFormat vertex_format;
@@ -37,7 +37,6 @@ private:
 
 public:
        VertexSetup();
-       ~VertexSetup();
 
        void set_format(const VertexFormat &);
        void set_format_instanced(const VertexFormat &, const VertexFormat &);
@@ -52,16 +51,14 @@ public:
 
 private:
        static bool verify_format(const VertexFormat &);
-       static void require_format(const VertexFormat &, bool);
        void update() const;
-       void update_vertex_array(const VertexArray &, unsigned, unsigned, bool) const;
 
 public:
        void refresh() const { if(dirty) update(); }
 
        void unload();
 
-       void set_debug_name(const std::string &);
+       using VertexSetupBackend::set_debug_name;
 };
 
 } // namespace GL
index 9fe37ca665e943f18c5afd51586b1a8679dcab80..92a4bcf8d9478a93074bd318f03c1ad77e905a91 100644 (file)
@@ -4,7 +4,6 @@
 #include <vector>
 #include <msp/geometry/angle.h>
 #include "color.h"
-#include "gl.h"
 #include "programdata.h"
 
 namespace Msp {
index da229c41d2f7aa638fe3cbf9bc9a447975b24f75..d8665537bcbe6097187628a23363b1e975f6f342 100644 (file)
@@ -1,7 +1,6 @@
 #include <msp/core/hash.h>
 #include <msp/strings/format.h>
 #include "basicmaterial.h"
-#include "gl.h"
 #include "pbrmaterial.h"
 #include "program.h"
 #include "resources.h"