source "source/resources";
source "source/glsl";
source "source/builders";
+ source "source/backends/opengl";
source "extensions";
source "builtin_data";
source "shaderlib";
incpath "source/animation";
incpath "source/resources";
incpath "source/builders";
+ incpath "source/backends/opengl";
standard CXX "c++11";
};
install true;
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";
};
};
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
+
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#include <msp/gl/extensions/arb_direct_state_access.h>
+#include <msp/gl/extensions/arb_texture_storage.h>
+#include <msp/gl/extensions/arb_vertex_buffer_object.h>
+#include <msp/graphics/imageloader.h>
+#include "buffer.h"
+#include "gl.h"
+#include "texture2d.h"
+#include "texture2d_backend.h"
+
+namespace Msp {
+namespace GL {
+
+class OpenGLTexture2D::AsyncLoader: public Resource::AsyncLoader
+{
+private:
+ Texture2D &texture;
+ IO::Seekable &io;
+ Buffer pixel_buffer;
+ char *mapped_address;
+ Graphics::Image image;
+ Graphics::ImageLoader *img_loader;
+ unsigned n_bytes;
+ int phase;
+
+public:
+ AsyncLoader(Texture2D &, IO::Seekable &);
+ ~AsyncLoader();
+
+ virtual bool needs_sync() const;
+ virtual bool process();
+};
+
+
+OpenGLTexture2D::OpenGLTexture2D(ResourceManager *m):
+ Texture(GL_TEXTURE_2D, m)
+{ }
+
+void OpenGLTexture2D::allocate()
+{
+ unsigned width = static_cast<const Texture2D *>(this)->width;
+ unsigned height = static_cast<const Texture2D *>(this)->height;
+ unsigned levels = static_cast<const Texture2D *>(this)->levels;
+
+ GLenum gl_fmt = get_gl_pixelformat(storage_fmt);
+ if(ARB_texture_storage)
+ {
+ if(ARB_direct_state_access)
+ glTextureStorage2D(id, levels, gl_fmt, width, height);
+ else
+ {
+ bind_scratch();
+ glTexStorage2D(target, levels, gl_fmt, width, height);
+ }
+ }
+ else
+ {
+ bind_scratch();
+ glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, levels-1);
+ GLenum comp = get_gl_components(get_components(storage_fmt));
+ GLenum type = get_gl_type(get_component_type(storage_fmt));
+ for(unsigned i=0; i<levels; ++i)
+ {
+ auto lv_size = static_cast<const Texture2D *>(this)->get_level_size(i);
+ glTexImage2D(target, i, gl_fmt, lv_size.x, lv_size.y, 0, comp, type, 0);
+ }
+ }
+
+ apply_swizzle();
+}
+
+void OpenGLTexture2D::sub_image(unsigned level, int x, int y, unsigned wd, unsigned ht, const void *data)
+{
+ GLenum comp = get_gl_components(get_components(storage_fmt));
+ GLenum type = get_gl_type(get_component_type(storage_fmt));
+ if(ARB_direct_state_access)
+ glTextureSubImage2D(id, level, x, y, wd, ht, comp, type, data);
+ else
+ {
+ bind_scratch();
+ glTexSubImage2D(target, level, x, y, wd, ht, comp, type, data);
+ }
+}
+
+void OpenGLTexture2D::sub_image(unsigned level, int x, int y, unsigned wd, unsigned ht, const Buffer &buffer, unsigned offset)
+{
+ glBindBuffer(GL_PIXEL_UNPACK_BUFFER, buffer.id);
+ sub_image(level, x, y, wd, ht, reinterpret_cast<void *>(offset));
+ glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
+}
+
+Resource::AsyncLoader *OpenGLTexture2D::create_async_loader(IO::Seekable &io)
+{
+ return new AsyncLoader(static_cast<Texture2D &>(*this), io);
+}
+
+void OpenGLTexture2D::unload()
+{
+ glDeleteTextures(1, &id);
+ id = 0;
+}
+
+
+OpenGLTexture2D::AsyncLoader::AsyncLoader(Texture2D &t, IO::Seekable &i):
+ texture(t),
+ io(i),
+ mapped_address(0),
+ img_loader(Graphics::ImageLoader::open_io(io)),
+ phase(0)
+{ }
+
+OpenGLTexture2D::AsyncLoader::~AsyncLoader()
+{
+ if(mapped_address)
+ pixel_buffer.unmap();
+ delete img_loader;
+}
+
+bool OpenGLTexture2D::AsyncLoader::needs_sync() const
+{
+ return phase%2;
+}
+
+bool OpenGLTexture2D::AsyncLoader::process()
+{
+ if(phase==0)
+ {
+ image.load_headers(*img_loader);
+ n_bytes = image.get_stride()*image.get_height();
+ }
+ else if(phase==1)
+ {
+ pixel_buffer.storage(n_bytes);
+ mapped_address = reinterpret_cast<char *>(pixel_buffer.map());
+ }
+ else if(phase==2)
+ image.load_into(*img_loader, mapped_address);
+ else if(phase==3)
+ {
+ mapped_address = 0;
+ if(!pixel_buffer.unmap())
+ {
+ phase = 1;
+ return false;
+ }
+
+ if(!texture.id)
+ texture.generate_id();
+
+ unsigned w = image.get_width();
+ unsigned h = image.get_height();
+ texture.storage(pixelformat_from_image(image, texture.use_srgb_format), w, h);
+ texture.OpenGLTexture2D::sub_image(0, 0, 0, w, h, pixel_buffer, 0);
+
+ if(texture.auto_gen_mipmap)
+ texture.generate_mipmap();
+ }
+
+ ++phase;
+ return phase>3;
+}
+
+} // namespace GL
+} // namespace Msp
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
#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"
-#include <stdexcept>
-#include <cstdlib>
#include <msp/strings/lexicalcast.h>
#include <msp/strings/utils.h>
#include "backend.h"
-#include "gl.h"
using namespace std;
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
-#include <msp/gl/extensions/msp_primitive_restart.h>
#include "batch.h"
#include "error.h"
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()
{
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;
}
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;
}
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;
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);
#include <vector>
#include <msp/datafile/objectloader.h>
+#include "batch_backend.h"
#include "bufferable.h"
#include "datatype.h"
#include "primitivetype.h"
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>
{
private:
PrimitiveType prim_type;
- unsigned gl_prim_type;
DataType index_type;
- unsigned gl_index_type;
std::vector<std::uint8_t> data;
unsigned max_index;
-#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"
}
-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();
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);
} // namespace GL
} // namespace Msp
+#include "blend_backend.h"
+
#endif
#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"
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)
{
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)
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
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
#include <stdexcept>
#include <string>
+#include "buffer_backend.h"
namespace Msp {
namespace GL {
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. */
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
+++ /dev/null
-#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
#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
+++ /dev/null
-#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
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
-#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;
+++ /dev/null
-#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
+++ /dev/null
-#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
-#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"
{ }
-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);
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;
}
{
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;
}
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);
}
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;
}
#include <vector>
#include "color.h"
+#include "framebuffer_backend.h"
#include "frameformat.h"
#include "texturecube.h"
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
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. */
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 &);
void refresh() const { if(dirty) update(); }
- void set_debug_name(const std::string &);
+ using FramebufferBackend::set_debug_name;
static Framebuffer &system();
};
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
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
+++ /dev/null
-#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
#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),
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)
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),
#include <vector>
#include <msp/core/noncopyable.h>
#include "cullface.h"
+#include "pipelinestate_backend.h"
namespace Msp {
namespace GL {
class UniformBlock;
class VertexSetup;
-class PipelineState: public NonCopyable
+class PipelineState: public PipelineStateBackend
{
+ friend PipelineStateBackend;
+
private:
struct BoundTexture
{
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>
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
-#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"
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
void require_pixelformat(PixelFormat);
-unsigned get_gl_components(PixelComponents);
-unsigned get_gl_pixelformat(PixelFormat);
-
} // namespace GL
} // namespace Msp
+#include "pixelformat_backend.h"
+
#endif
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();
NOTEQUAL
};
-unsigned get_gl_predicate(Predicate);
-
void operator>>(const LexicalConverter &, Predicate &);
void operator<<(LexicalConverter &, Predicate);
} // namespace GL
} // namespace Msp
+#include "predicate_backend.h"
+
#endif
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")
TRIANGLE_FAN
};
-unsigned get_gl_primitive_type(PrimitiveType);
-
void operator>>(const LexicalConverter &, PrimitiveType &);
} // namespace GL
} // namespace Msp
+#include "primitivetype_backend.h"
+
#endif
-#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())
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)
{
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
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)
#include <vector>
#include <msp/datafile/objectloader.h>
#include "module.h"
+#include "program_backend.h"
#include "reflectdata.h"
namespace Msp {
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>
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 &);
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
-#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),
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
#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 {
OCCLUSION_QUERY
};
-class QueryPool: public Msp::NonCopyable
+class QueryPool: public QueryPoolBackend, public Msp::NonCopyable
{
- friend class Commands;
+ friend QueryPoolBackend;
public:
class Activate
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
-#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"
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;
}
{
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;
}
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)
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")
#include <msp/datafile/objectloader.h>
#include "color.h"
#include "predicate.h"
+#include "sampler_backend.h"
namespace Msp {
namespace GL {
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>
COMPARE = 128
};
- unsigned id;
TextureFilter min_filter;
TextureFilter mag_filter;
float max_anisotropy;
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 &);
}
-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();
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
-#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"
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),
{
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)
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;
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)
#include <msp/graphics/image.h>
#include "pixelformat.h"
#include "resource.h"
+#include "texture_backend.h"
namespace Msp {
namespace GL {
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>
{
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);
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
-#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"
namespace GL {
Texture1D::Texture1D():
- Texture(GL_TEXTURE_1D),
width(0)
-{
- static Require _req(MSP_texture1D);
-}
+{ }
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)
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)
#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>
{
-#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"
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)
{ }
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)
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)
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
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)
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
#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>
{
};
private:
- class AsyncLoader;
-
unsigned width;
unsigned height;
unsigned levels;
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. */
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
-#include <msp/gl/extensions/ext_texture_array.h>
#include "error.h"
#include "texture2darray.h"
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)
#ifndef MSP_GL_TEXTURE2DARRAY_H_
#define MSP_GL_TEXTURE2DARRAY_H_
-#include "texture3d.h"
+#include "texture2darray_backend.h"
namespace Msp {
namespace GL {
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>
void external_image(unsigned, const std::string &);
};
- Texture2DArray();
-
void layer_image(unsigned, unsigned, const void *);
void layer_image(unsigned, unsigned, const Graphics::Image &);
-#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"
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)
{
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)
#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;
#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"
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)
{
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)
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)
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) ;
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))
#include <string>
#include <msp/linal/vector.h>
-#include "texture.h"
+#include "texture3d_backend.h"
namespace Msp {
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>
{
unsigned depth;
unsigned levels;
- Texture3D(GLenum);
+ Texture3D(unsigned);
public:
Texture3D();
#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"
};
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)
{
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)
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)
}
-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();
#define MSP_GL_TEXTURECUBE_H_
#include <msp/graphics/image.h>
-#include "texture.h"
+#include "texturecube_backend.h"
#include "vector.h"
namespace Msp {
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>
{
virtual void unload() { }
};
-unsigned get_gl_cube_face(TextureCubeFace);
-
void operator>>(const LexicalConverter &, TextureCubeFace &);
} // namespace GL
#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"
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
{
#include <msp/core/noncopyable.h>
#include "bufferable.h"
#include "reflectdata.h"
+#include "uniformblock_backend.h"
namespace Msp {
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;
-#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"
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)
{
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();
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
#include "datatype.h"
#include "vertexformat.h"
+#include "vertexsetup_backend.h"
namespace Msp {
namespace GL {
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
INDEX_BUFFER = 4
};
- unsigned id;
mutable unsigned dirty;
const VertexArray *vertex_array;
VertexFormat vertex_format;
public:
VertexSetup();
- ~VertexSetup();
void set_format(const VertexFormat &);
void set_format_instanced(const VertexFormat &, const VertexFormat &);
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
#include <vector>
#include <msp/geometry/angle.h>
#include "color.h"
-#include "gl.h"
#include "programdata.h"
namespace Msp {
#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"