From: Mikko Rasa Date: Fri, 1 Oct 2021 16:58:11 +0000 (+0300) Subject: Move all OpenGL-specific code to a separate directory X-Git-Url: http://git.tdb.fi/?a=commitdiff_plain;h=160e9eea29bd10034733d59507fa1bcca36be401;p=libs%2Fgl.git Move all OpenGL-specific code to a separate directory --- diff --git a/Build b/Build index 22aedd4f..6bf5ebb0 100644 --- a/Build +++ b/Build @@ -51,6 +51,7 @@ package "mspgl" source "source/resources"; source "source/glsl"; source "source/builders"; + source "source/backends/opengl"; source "extensions"; source "builtin_data"; source "shaderlib"; @@ -64,6 +65,7 @@ package "mspgl" incpath "source/animation"; incpath "source/resources"; incpath "source/builders"; + incpath "source/backends/opengl"; standard CXX "c++11"; }; install true; @@ -77,6 +79,7 @@ package "mspgl" map "source/resources" "include/msp/gl"; map "source/glsl" "include/msp/gl/glsl"; map "source/builders" "include/msp/gl"; + map "source/backends/opengl" "include/msp/gl"; map "extensions" "include/msp/gl/extensions"; }; }; diff --git a/source/backends/opengl/backend_opengl.cpp b/source/backends/opengl/backend_opengl.cpp new file mode 100644 index 00000000..ce8fc17b --- /dev/null +++ b/source/backends/opengl/backend_opengl.cpp @@ -0,0 +1,49 @@ +#include +#include +#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(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 +#include "batch_backend.h" + +namespace Msp { +namespace GL { + +OpenGLBatch::OpenGLBatch(PrimitiveType t): + gl_prim_type(GL::get_gl_primitive_type(t)), + gl_index_type(GL_UNSIGNED_SHORT) +{ } + +bool OpenGLBatch::check_restart(bool require) +{ + if(require) + static Require _req(MSP_primitive_restart); + return MSP_primitive_restart; +} + +void OpenGLBatch::set_index_type(DataType t) +{ + gl_index_type = get_gl_type(t); +} + +} // namespace GL +} // namespace Msp diff --git a/source/backends/opengl/batch_backend.h b/source/backends/opengl/batch_backend.h new file mode 100644 index 00000000..b04b1431 --- /dev/null +++ b/source/backends/opengl/batch_backend.h @@ -0,0 +1,30 @@ +#ifndef MSP_GL_BATCH_BACKEND_H_ +#define MSP_GL_BATCH_BACKEND_H_ + +#include "datatype.h" +#include "primitivetype.h" + +namespace Msp { +namespace GL { + +class OpenGLBatch +{ + friend class OpenGLCommands; + +protected: + unsigned gl_prim_type; + unsigned gl_index_type; + + OpenGLBatch(PrimitiveType); + + static bool check_restart(bool); + + void set_index_type(DataType); +}; + +using BatchBackend = OpenGLBatch; + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/backends/opengl/blend_backend.cpp b/source/backends/opengl/blend_backend.cpp new file mode 100644 index 00000000..44267480 --- /dev/null +++ b/source/backends/opengl/blend_backend.cpp @@ -0,0 +1,49 @@ +#include +#include +#include +#include "blend.h" +#include "blend_backend.h" +#include "gl.h" + +using namespace std; + +namespace Msp { +namespace GL { + +unsigned get_gl_blend_equation(BlendEquation eq) +{ + switch(eq) + { + case ADD: return GL_FUNC_ADD; + case SUBTRACT: return GL_FUNC_SUBTRACT; + case REVERSE_SUBTRACT: return GL_FUNC_REVERSE_SUBTRACT; + case MIN: return GL_MIN; + case MAX: return GL_MAX; + default: throw invalid_argument("get_gl_blend_equation"); + } +} + +unsigned get_gl_blend_factor(BlendFactor factor) +{ + switch(factor) + { + case ZERO: return GL_ZERO; + case ONE: return GL_ONE; + case SRC_COLOR: return GL_SRC_COLOR; + case ONE_MINUS_SRC_COLOR: return GL_ONE_MINUS_SRC_COLOR; + case SRC_ALPHA: return GL_SRC_ALPHA; + case ONE_MINUS_SRC_ALPHA: return GL_ONE_MINUS_SRC_ALPHA; + case DST_COLOR: return GL_DST_COLOR; + case ONE_MINUS_DST_COLOR: return GL_ONE_MINUS_DST_COLOR; + case DST_ALPHA: return GL_DST_ALPHA; + case ONE_MINUS_DST_ALPHA: return GL_ONE_MINUS_DST_ALPHA; + case CONSTANT_COLOR: return GL_CONSTANT_COLOR; + case ONE_MINUS_CONSTANT_COLOR: return GL_ONE_MINUS_CONSTANT_COLOR; + case CONSTANT_ALPHA: return GL_CONSTANT_ALPHA; + case ONE_MINUS_CONSTANT_ALPHA: return GL_ONE_MINUS_CONSTANT_ALPHA; + default: throw invalid_argument("get_gl_blend_factor"); + } +} + +} // namespace GL +} // namespace Msp diff --git a/source/backends/opengl/blend_backend.h b/source/backends/opengl/blend_backend.h new file mode 100644 index 00000000..c436538d --- /dev/null +++ b/source/backends/opengl/blend_backend.h @@ -0,0 +1,17 @@ +#ifndef MSP_GL_BLEND_BACKEND_H_ +#define MSP_GL_BLEND_BACKEND_H_ + +#ifndef MSP_GL_BLEND_H_ +#error "blend_backend.h requires blend.h" +#endif + +namespace Msp { +namespace GL { + +unsigned get_gl_blend_equation(BlendEquation); +unsigned get_gl_blend_factor(BlendFactor); + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/backends/opengl/buffer_backend.cpp b/source/backends/opengl/buffer_backend.cpp new file mode 100644 index 00000000..e1bced73 --- /dev/null +++ b/source/backends/opengl/buffer_backend.cpp @@ -0,0 +1,129 @@ +#include +#include +#include +#include +#include +#include +#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(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(this)->size; + + if(ARB_direct_state_access) + return glMapNamedBufferRange(id, 0, size, GL_MAP_READ_BIT|GL_MAP_WRITE_BIT); + else + { + bind_scratch(); + void *result = glMapBufferRange(GL_ARRAY_BUFFER, 0, size, GL_MAP_READ_BIT|GL_MAP_WRITE_BIT); + return result; + } +} + +bool OpenGLBuffer::unmap() +{ + // TODO check if it's mapped + if(ARB_direct_state_access) + return glUnmapNamedBuffer(id); + else if(OES_mapbuffer) + { + bind_scratch(); + bool result = glUnmapBuffer(GL_ARRAY_BUFFER); + return result; + } + else + return true; +} + +void OpenGLBuffer::set_debug_name(const string &name) +{ +#ifdef DEBUG + if(KHR_debug) + glObjectLabel(GL_BUFFER, id, name.size(), name.c_str()); +#else + (void)name; +#endif +} + +void OpenGLBuffer::bind_scratch() +{ + if(scratch_binding!=this) + { + glBindBuffer(GL_ARRAY_BUFFER, id); + scratch_binding = this; + } +} + +void OpenGLBuffer::unbind_scratch() +{ + if(scratch_binding) + { + glBindBuffer(GL_ARRAY_BUFFER, 0); + scratch_binding = 0; + } +} + +} // namespace GL +} // namespace Msp diff --git a/source/backends/opengl/buffer_backend.h b/source/backends/opengl/buffer_backend.h new file mode 100644 index 00000000..7162a26b --- /dev/null +++ b/source/backends/opengl/buffer_backend.h @@ -0,0 +1,40 @@ +#ifndef MSP_GL_BUFFER_BACKEND_H_ +#define MSP_GL_BUFFER_BACKEND_H_ + +#include + +namespace Msp { +namespace GL { + +class OpenGLBuffer: public NonCopyable +{ + friend class OpenGLPipelineState; + friend class OpenGLTexture2D; + friend class OpenGLVertexSetup; + +protected: + unsigned id; + + static OpenGLBuffer *scratch_binding; + + OpenGLBuffer(); + ~OpenGLBuffer(); + + void allocate(); + void sub_data(unsigned, unsigned, const void *); + + void *map(); + bool unmap(); + + void set_debug_name(const std::string &); + + void bind_scratch(); + static void unbind_scratch(); +}; + +using BufferBackend = OpenGLBuffer; + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/backends/opengl/commands_backend.cpp b/source/backends/opengl/commands_backend.cpp new file mode 100644 index 00000000..fd66b99c --- /dev/null +++ b/source/backends/opengl/commands_backend.cpp @@ -0,0 +1,126 @@ +#include +#include +#include +#include +#include +#include +#include +#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(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(batch.get_offset()); + glDrawElementsInstanced(batch.gl_prim_type, batch.size(), batch.gl_index_type, data_ptr, count); +} + +void OpenGLCommands::resolve_multisample(Framebuffer &target) +{ + static Require _req(EXT_framebuffer_blit); + + const Framebuffer *source = pipeline_state->get_framebuffer(); + + unsigned width = min(source->get_width(), target.get_width()); + unsigned height = min(source->get_height(), target.get_height()); + unsigned buffers = get_gl_buffer_bits(source->get_format())&get_gl_buffer_bits(target.get_format()); + + if(ARB_direct_state_access) + glBlitNamedFramebuffer(source->id, target.id, 0, 0, width, height, 0, 0, width, height, buffers, GL_NEAREST); + else + { + glBindFramebuffer(GL_READ_FRAMEBUFFER, source->id); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, target.id); + + target.refresh(); + + glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, buffers, GL_NEAREST); + + glBindFramebuffer(GL_FRAMEBUFFER, source->id); + } +} + +void OpenGLCommands::begin_query(const QueryPool &pool, unsigned index) +{ + if(index>=pool.queries.size()) + throw out_of_range("OpenGLCommands::begin_query"); + glBeginQuery(pool.gl_type, pool.queries[index]); +} + +void OpenGLCommands::end_query(const QueryPool &pool, unsigned) +{ + glEndQuery(pool.gl_type); +} + +} // namespace GL +} // namespace Msp diff --git a/source/backends/opengl/commands_backend.h b/source/backends/opengl/commands_backend.h new file mode 100644 index 00000000..f54b27a1 --- /dev/null +++ b/source/backends/opengl/commands_backend.h @@ -0,0 +1,36 @@ +#ifndef MSP_GL_COMMANDS_BACKEND_H_ +#define MSP_GL_COMMANDS_BACKEND_H_ + +namespace Msp { +namespace GL { + +class Batch; +union ClearValue; +class Framebuffer; +class PipelineState; +class QueryPool; + +class OpenGLCommands +{ +protected: + const PipelineState *pipeline_state; + + OpenGLCommands(); + + void use_pipeline(const PipelineState *); + void clear(const ClearValue *); + void draw(const Batch &); + void draw_instanced(const Batch &, unsigned); + void resolve_multisample(Framebuffer &); + + void begin_query(const QueryPool &, unsigned); + void end_query(const QueryPool &, unsigned); +}; + +using CommandsBackend = OpenGLCommands; + +} // namespace GL +} // namespace Msp + +#endif + diff --git a/source/backends/opengl/datatype_backend.cpp b/source/backends/opengl/datatype_backend.cpp new file mode 100644 index 00000000..9fd986ee --- /dev/null +++ b/source/backends/opengl/datatype_backend.cpp @@ -0,0 +1,119 @@ +#include +#include +#include +#include +#include +#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.typetype!=type) + throw invalid_argument("get_gl_type"); + return ptr->gl_type; +} + +DataType from_gl_type(unsigned gl_type) +{ + for(unsigned i=0; i>12)&3)+1; + unsigned cols = ((type>>14)&4)+1; + if(rows>1 && cols>1 && rows!=cols) + static Require _req(NV_non_square_matrices); + if((type&0x200) && get_type_size(type)/(rows*cols)==8) + static Require _req(ARB_gpu_shader_fp64); +} + +} // namespace GL +} // namespace Msp diff --git a/source/backends/opengl/datatype_backend.h b/source/backends/opengl/datatype_backend.h new file mode 100644 index 00000000..fae37221 --- /dev/null +++ b/source/backends/opengl/datatype_backend.h @@ -0,0 +1,17 @@ +#ifndef MSP_GL_DATATYPE_BACKEND_H_ +#define MSP_GL_DATATYPE_BACKEND_H_ + +#ifndef MSP_GL_DATATYPE_H_ +#error "datatype_backend.h requires datatype.h" +#endif + +namespace Msp { +namespace GL { + +unsigned get_gl_type(DataType); +DataType from_gl_type(unsigned); + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/backends/opengl/deviceinfo_backend.cpp b/source/backends/opengl/deviceinfo_backend.cpp new file mode 100644 index 00000000..7e1f3f48 --- /dev/null +++ b/source/backends/opengl/deviceinfo_backend.cpp @@ -0,0 +1,48 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "deviceinfo.h" +#include "gl.h" + +namespace Msp { +namespace GL { + +Limits::Limits() +{ + glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, reinterpret_cast(&max_vertex_attributes)); + glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, reinterpret_cast(&max_texture_bindings)); + glGetIntegerv(GL_MAX_UNIFORM_BUFFER_BINDINGS, reinterpret_cast(&max_uniform_bindings)); + glGetIntegerv(GL_MAX_CLIP_PLANES, reinterpret_cast(&max_clip_planes)); + glGetIntegerv(GL_MAX_SAMPLES, reinterpret_cast(&max_samples)); + glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, reinterpret_cast(&uniform_buffer_alignment)); + glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, reinterpret_cast(&max_color_attachments)); +} + + +DeviceInfo::DeviceInfo() +{ + glsl_features.target_api = get_backend_api(); + glsl_features.glsl_version = get_glsl_version(); + glsl_features.arb_enhanced_layouts = ARB_enhanced_layouts; + glsl_features.arb_explicit_attrib_location = ARB_explicit_attrib_location; + glsl_features.arb_explicit_uniform_location = ARB_explicit_uniform_location; + glsl_features.arb_gpu_shader5 = ARB_gpu_shader5; + glsl_features.arb_separate_shader_objects = ARB_separate_shader_objects; + glsl_features.arb_uniform_buffer_object = ARB_uniform_buffer_object; + glsl_features.ext_gpu_shader4 = EXT_gpu_shader4; + glsl_features.ext_texture_array = EXT_texture_array; + glsl_features.uniform_binding_range = limits.max_uniform_bindings; + glsl_features.texture_binding_range = limits.max_texture_bindings; +} + +} // namespace Msp +} // namespace GL diff --git a/source/backends/opengl/extension.cpp b/source/backends/opengl/extension.cpp new file mode 100644 index 00000000..a55da9ba --- /dev/null +++ b/source/backends/opengl/extension.cpp @@ -0,0 +1,195 @@ +#include +#include +#if defined(__ANDROID__) +#include +#elif defined(_WIN32) +#include +#elif !defined(__APPLE__) +#define GLX_GLXEXT_PROTOTYPES +#include +#endif +#include +#include +#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 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(get_proc_address("glGetStringi")); + int n_extensions; + glGetIntegerv(GL_NUM_EXTENSIONS, &n_extensions); + for(int i=0; i(glGetStringi(GL_EXTENSIONS, i))); + } + else + { + if(const char *gl_ext = reinterpret_cast(glGetString(GL_EXTENSIONS))) + { + vector 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 disabled_exts; + static bool init_done = false; + + if(!init_done) + { + if(const char *disable_ptr = getenv("MSPGL_DISABLE_EXTENSIONS")) + { + vector disable = split(disable_ptr); + disabled_exts.insert(disable.begin(), disable.end()); + } + + if(const char *renderer_ptr = reinterpret_cast(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(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(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(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(name.c_str())); +#endif +} + +} // namespace GL +} // namespace Msp diff --git a/source/backends/opengl/extension.h b/source/backends/opengl/extension.h new file mode 100644 index 00000000..77996357 --- /dev/null +++ b/source/backends/opengl/extension.h @@ -0,0 +1,79 @@ +#ifndef MSP_GL_EXTENSION_H_ +#define MSP_GL_EXTENSION_H_ + +#include +#include "backend.h" + +namespace Msp { +namespace GL { + +enum GLProfile +{ + CORE_PROFILE, + COMPATIBILITY_PROFILE +}; + + +/** +Holds metadata about an extension. Evaluates to true if the extension is +supported. +*/ +class Extension +{ +public: + enum SupportLevel + { + UNSUPPORTED, + EXTENSION, + CORE + }; + + typedef SupportLevel (*InitFunc)(); + +private: + const char *name; + InitFunc init_func; + mutable bool init_done; + mutable SupportLevel support; + +public: + Extension(const char *, InitFunc); + + const char *get_name() const { return name; } + operator bool() const; +}; + + +struct Require +{ + Require(const Extension &); +}; + + +typedef void ExtFunc(); + +/** Checks for extension support. Only intended for internal use. */ +bool is_supported(const std::string &); + +/** Checks for OpenGL version support. Only intended for internal use. */ +bool is_supported(const Version &, const Version & = Version()); + +/** Indicates whether an extension has been disabled, either explicitly through +the MSPGL_DISABLE_EXTENSIONS environment variable or implicitly as a workaround +for a driver bug. Only intended for internal use. */ +bool is_disabled(const std::string &); + +/** Returns the OpenGL profile for the active context. */ +GLProfile get_gl_profile(); + +/** Returns the GLSL version number, as reported by the implementation. */ +const Version &get_glsl_version(); + +/** Returns the address of an extension function. Only indended for internal +use. */ +ExtFunc *get_proc_address(const std::string &); + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/backends/opengl/framebuffer_backend.cpp b/source/backends/opengl/framebuffer_backend.cpp new file mode 100644 index 00000000..1adcb7f9 --- /dev/null +++ b/source/backends/opengl/framebuffer_backend.cpp @@ -0,0 +1,192 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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(this)->format; + vector 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<(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(attch.layer)), attch.tex->id, attch.level); + } + else if(ARB_direct_state_access) + glNamedFramebufferTexture(id, gl_attach_point, 0, 0); + else + glFramebufferTexture2D(GL_FRAMEBUFFER, gl_attach_point, GL_TEXTURE_2D, 0, 0); + } + + if(gl_attach_point!=GL_DEPTH_ATTACHMENT && gl_attach_point!=GL_STENCIL_ATTACHMENT) + color_bufs.push_back(gl_attach_point); + + ++i; + } + + if(color_bufs.size()>1) + static Require _req(ARB_draw_buffers); + + GLenum first_buffer = (color_bufs.empty() ? GL_NONE : color_bufs.front()); + if(ARB_direct_state_access) + { + /* ARB_direct_state_access ties the availability of these functions to + framebuffers themselves, so no further checks are needed. */ + glNamedFramebufferDrawBuffers(id, color_bufs.size(), &color_bufs[0]); + glNamedFramebufferReadBuffer(id, first_buffer); + } + else + { + if(ARB_draw_buffers) + glDrawBuffers(color_bufs.size(), &color_bufs[0]); + else if(MSP_buffer_control) + glDrawBuffer(first_buffer); + + if(MSP_buffer_control) + glReadBuffer(first_buffer); + } + + if(ARB_direct_state_access) + status = glCheckNamedFramebufferStatus(id, GL_FRAMEBUFFER); + else + status = glCheckFramebufferStatus(GL_FRAMEBUFFER); +} + +void OpenGLFramebuffer::require_complete() const +{ + if(status==GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT) + throw framebuffer_incomplete("incomplete or unsupported attachment"); + if(status==GL_FRAMEBUFFER_UNSUPPORTED) + throw framebuffer_incomplete("unsupported configuration"); + if(status!=GL_FRAMEBUFFER_COMPLETE) + throw framebuffer_incomplete(Msp::format("incomplete (%#x)", status)); +} + +void OpenGLFramebuffer::set_debug_name(const string &name) +{ +#ifdef DEBUG + if(KHR_debug) + glObjectLabel(GL_FRAMEBUFFER, id, name.size(), name.c_str()); +#else + (void)name; +#endif +} + +} // namespace GL +} // namespace Msp diff --git a/source/backends/opengl/framebuffer_backend.h b/source/backends/opengl/framebuffer_backend.h new file mode 100644 index 00000000..84f6eddf --- /dev/null +++ b/source/backends/opengl/framebuffer_backend.h @@ -0,0 +1,38 @@ +#ifndef MSP_GL_FRAMEBUFFER_BACKEND_H_ +#define MSP_GL_FRAMEBUFFER_BACKEND_H_ + +#include +#include "frameformat.h" + +namespace Msp { +namespace GL { + +class OpenGLFramebuffer +{ + friend class OpenGLCommands; + friend class OpenGLPipelineState; + +protected: + unsigned id; + mutable unsigned status; + + OpenGLFramebuffer(bool); + ~OpenGLFramebuffer(); + + static FrameFormat get_system_format(); + static void get_system_size(unsigned &, unsigned &); + static bool is_format_supported(const FrameFormat &); + static void require_layered(); + + void update(unsigned) const; + void require_complete() const; + + void set_debug_name(const std::string &); +}; + +using FramebufferBackend = OpenGLFramebuffer; + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/backends/opengl/frameformat_backend.cpp b/source/backends/opengl/frameformat_backend.cpp new file mode 100644 index 00000000..2342d7eb --- /dev/null +++ b/source/backends/opengl/frameformat_backend.cpp @@ -0,0 +1,35 @@ +#include +#include "frameformat.h" +#include "frameformat_backend.h" +#include "gl.h" + +namespace Msp { +namespace GL { + +unsigned get_gl_attachment(FrameAttachment fa) +{ + if(get_attach_point(fa)==get_attach_point(DEPTH_ATTACHMENT)) + return GL_DEPTH_ATTACHMENT; + else if(get_attach_point(fa)==get_attach_point(STENCIL_ATTACHMENT)) + return GL_STENCIL_ATTACHMENT; + else + return GL_COLOR_ATTACHMENT0+get_attach_point(fa); +} + +unsigned get_gl_buffer_bits(const FrameFormat &format) +{ + unsigned bits = 0; + for(FrameAttachment a: format) + { + if(get_attach_point(a)==get_attach_point(DEPTH_ATTACHMENT)) + bits |= GL_DEPTH_BUFFER_BIT; + else if(get_attach_point(a)==get_attach_point(STENCIL_ATTACHMENT)) + bits |= GL_STENCIL_BUFFER_BIT; + else + bits |= GL_COLOR_BUFFER_BIT; + } + return bits; +} + +} // namespace GL +} // namespace Msp diff --git a/source/backends/opengl/frameformat_backend.h b/source/backends/opengl/frameformat_backend.h new file mode 100644 index 00000000..a0197f5b --- /dev/null +++ b/source/backends/opengl/frameformat_backend.h @@ -0,0 +1,17 @@ +#ifndef MSP_GL_FRAMEFORMAT_BACKEND_H_ +#define MSP_GL_FRAMEFORMAT_BACKEND_H_ + +#ifndef MSP_GL_FRAMEFORMAT_H_ +#error "frameformat_backend.h requires frameformat.h" +#endif + +namespace Msp { +namespace GL { + +unsigned get_gl_attachment(FrameAttachment); +unsigned get_gl_buffer_bits(const FrameFormat &); + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/backends/opengl/gl.h b/source/backends/opengl/gl.h new file mode 100644 index 00000000..f27b63fe --- /dev/null +++ b/source/backends/opengl/gl.h @@ -0,0 +1,44 @@ +#ifndef MSP_GL_GL_H_ +#define MSP_GL_GL_H_ + +#ifdef __APPLE__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wkeyword-macro" +#define extern extern __attribute__((weak_import)) +#include +#include +#undef extern +#pragma clang diagnostic pop +#elif defined(__ANDROID__) +#include +#include +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 +#include +#endif + +#ifndef APIENTRY +#define APIENTRY +#endif + +#endif diff --git a/source/backends/opengl/pipelinestate_backend.cpp b/source/backends/opengl/pipelinestate_backend.cpp new file mode 100644 index 00000000..738de3ac --- /dev/null +++ b/source/backends/opengl/pipelinestate_backend.cpp @@ -0,0 +1,282 @@ +#include +#include +#include +#include +#include +#include +#include +#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 OpenGLPipelineState::bound_tex_targets; +vector 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(this)->changes : ~0U); +} + +void OpenGLPipelineState::apply(unsigned mask) const +{ + const PipelineState *self = static_cast(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; ienabled_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(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(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(last_applied)->enabled_clip_planes; + for(unsigned i=0; i>i)&1) + glDisable(GL_CLIP_PLANE0+i); + + for(unsigned i=0; i +#include + +namespace Msp { +namespace GL { + +class OpenGLPipelineState: public NonCopyable +{ + friend class OpenGLCommands; + +protected: + static const OpenGLPipelineState *last_applied; + static std::vector bound_tex_targets; + static std::vector bound_uniform_blocks; + static unsigned restart_index; + + OpenGLPipelineState(); + ~OpenGLPipelineState(); + + void apply() const; +private: + void apply(unsigned) const; +protected: + static void clear(); +}; + +using PipelineStateBackend = OpenGLPipelineState; + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/backends/opengl/pixelformat_backend.cpp b/source/backends/opengl/pixelformat_backend.cpp new file mode 100644 index 00000000..ee1a2070 --- /dev/null +++ b/source/backends/opengl/pixelformat_backend.cpp @@ -0,0 +1,110 @@ +#include +#include +#include +#include +#include +#include +#include +#include "gl.h" +#include "pixelformat.h" +#include "pixelformat_backend.h" + +using namespace std; + +namespace Msp { +namespace GL { + +void require_pixelformat(PixelFormat pf) +{ + /* TODO These checks are only accurate for textures. On OpenGL ES some + formats are allowed for render buffers earlier than textures. In particular + it's possible to create a 16-bit depth renderbuffer on OpenGL ES 2.0 but + depth textures are only available with 3.0 or the OES_depth_texture + extension.*/ + switch(pf) + { + case RGB8: + case RGBA8: + { static Require _req(OES_required_internalformat); } + break; + case R8: + case RG8: + { static Require _req(ARB_texture_rg); } + break; + case R16F: + case R32F: + case RG16F: + case RG32F: + { static Require _req(ARB_texture_rg); } + { static Require _req(ARB_texture_float); } + break; + case RGB16F: + case RGB32F: + case RGBA16F: + case RGBA32F: + { static Require _req(ARB_texture_float); } + break; + case SRGB8: + case SRGB8_ALPHA8: + { static Require _req(EXT_texture_sRGB); } + break; + case DEPTH_COMPONENT16: + case DEPTH_COMPONENT24: + case DEPTH_COMPONENT32: + { static Require _req(ARB_depth_texture); } + { static Require _req(OES_required_internalformat); } + break; + case DEPTH_COMPONENT32F: + { static Require _req(ARB_depth_buffer_float); } + break; + case STENCIL_INDEX8: + { static Require _req(OES_texture_stencil8); } + break; + default: + throw invalid_argument("require_pixelformat"); + } +} + +GLenum get_gl_components(PixelComponents comp) +{ + switch(comp) + { + case RED: return GL_RED; + case RG: return GL_RG; + case RGB: return GL_RGB; + case RGBA: return GL_RGBA; + case DEPTH_COMPONENT: return GL_DEPTH_COMPONENT; + case STENCIL_INDEX: return GL_STENCIL_INDEX; + default: throw invalid_argument("get_gl_components"); + } +} + +GLenum get_gl_pixelformat(PixelFormat pf) +{ + switch(pf) + { + case R8: return GL_R8; + case R16F: return GL_R16F; + case R32F: return GL_R32F; + case RG8: return GL_RG8; + case RG16F: return GL_RG16F; + case RG32F: return GL_RG32F; + case RGB8: return GL_RGB8; + case RGB16F: return GL_RGB16F; + case RGB32F: return GL_RGB32F; + case RGBA8: return GL_RGBA8; + case RGBA16F: return GL_RGBA16F; + case RGBA32F: return GL_RGBA32F; + case SRGB8: return GL_SRGB8; + case SRGB8_ALPHA8: return GL_SRGB8_ALPHA8; + case DEPTH_COMPONENT16: return GL_DEPTH_COMPONENT16; + case DEPTH_COMPONENT24: return GL_DEPTH_COMPONENT24; + case DEPTH_COMPONENT32: return GL_DEPTH_COMPONENT32; + case DEPTH_COMPONENT32F: return GL_DEPTH_COMPONENT32F; + case STENCIL_INDEX8: return GL_STENCIL_INDEX8; + default: throw invalid_argument("get_gl_pixelformat"); + } +} + +} // namespace GL +} // namespace Msp diff --git a/source/backends/opengl/pixelformat_backend.h b/source/backends/opengl/pixelformat_backend.h new file mode 100644 index 00000000..d17bc8b0 --- /dev/null +++ b/source/backends/opengl/pixelformat_backend.h @@ -0,0 +1,17 @@ +#ifndef MSP_GL_PIXELFORMAT_BACKEND_H_ +#define MSP_GL_PIXELFORMAT_BACKEND_H_ + +#ifndef MSP_GL_PIXELFORMAT_H_ +#error "pixelformat_backend.h requires pixelformat.h" +#endif + +namespace Msp { +namespace GL { + +unsigned get_gl_components(PixelComponents); +unsigned get_gl_pixelformat(PixelFormat); + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/backends/opengl/predicate_backend.cpp b/source/backends/opengl/predicate_backend.cpp new file mode 100644 index 00000000..12a5f5d1 --- /dev/null +++ b/source/backends/opengl/predicate_backend.cpp @@ -0,0 +1,27 @@ +#include "gl.h" +#include "predicate.h" +#include "predicate_backend.h" + +using namespace std; + +namespace Msp { +namespace GL { + +unsigned get_gl_predicate(Predicate pred) +{ + switch(pred) + { + case NEVER: return GL_NEVER; + case ALWAYS: return GL_ALWAYS; + case LESS: return GL_LESS; + case LEQUAL: return GL_LEQUAL; + case EQUAL: return GL_EQUAL; + case GREATER: return GL_GREATER; + case GEQUAL: return GL_GEQUAL; + case NOTEQUAL: return GL_NOTEQUAL; + default: throw invalid_argument("get_gl_predicate"); + } +} + +} // namespace GL +} // namespace Msp diff --git a/source/backends/opengl/predicate_backend.h b/source/backends/opengl/predicate_backend.h new file mode 100644 index 00000000..8aeab1f3 --- /dev/null +++ b/source/backends/opengl/predicate_backend.h @@ -0,0 +1,16 @@ +#ifndef MSP_GL_PREDICATE_BACKEND_H_ +#define MSP_GL_PREDICATE_BACKEND_H_ + +#ifndef MSP_GL_PREDICATE_H_ +#error "predicate_backend.h requires predicate.h" +#endif + +namespace Msp { +namespace GL { + +unsigned get_gl_predicate(Predicate); + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/backends/opengl/primitivetype_backend.cpp b/source/backends/opengl/primitivetype_backend.cpp new file mode 100644 index 00000000..7e92fc1e --- /dev/null +++ b/source/backends/opengl/primitivetype_backend.cpp @@ -0,0 +1,26 @@ +#include "gl.h" +#include "primitivetype.h" +#include "primitivetype_backend.h" + +using namespace std; + +namespace Msp { +namespace GL { + +unsigned get_gl_primitive_type(PrimitiveType pt) +{ + switch(pt) + { + case POINTS: return GL_POINTS; + case LINES: return GL_LINES; + case LINE_STRIP: return GL_LINE_STRIP; + case LINE_LOOP: return GL_LINE_LOOP; + case TRIANGLES: return GL_TRIANGLES; + case TRIANGLE_STRIP: return GL_TRIANGLE_STRIP; + case TRIANGLE_FAN: return GL_TRIANGLE_FAN; + default: throw invalid_argument("get_gl_primitive_type"); + } +} + +} // namespace GL +} // namespace Msp diff --git a/source/backends/opengl/primitivetype_backend.h b/source/backends/opengl/primitivetype_backend.h new file mode 100644 index 00000000..200cbf05 --- /dev/null +++ b/source/backends/opengl/primitivetype_backend.h @@ -0,0 +1,16 @@ +#ifndef MSP_GL_PRIMITIVETYPE_BACKEND_H_ +#define MSP_GL_PRIMITIVETYPE_BACKEND_H_ + +#ifndef MSP_GL_PRIMITIVETYPE_H_ +#error "primitivetype_backend.h requires primitivetype.h" +#endif + +namespace Msp { +namespace GL { + +unsigned get_gl_primitive_type(PrimitiveType); + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/backends/opengl/program_backend.cpp b/source/backends/opengl/program_backend.cpp new file mode 100644 index 00000000..3b1c4293 --- /dev/null +++ b/source/backends/opengl/program_backend.cpp @@ -0,0 +1,505 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "error.h" +#include "program.h" +#include "program_backend.h" +#include "glsl/compiler.h" + +using namespace std; + +namespace { + +template +void uniform_wrapper(unsigned index, unsigned count, const void *data) +{ + func(index, count, static_cast(data)); +} + +template +void uniform_matrix_wrapper(unsigned index, unsigned count, const void *data) +{ + func(index, count, false, static_cast(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 &spec_values, TransientData &transient) +{ + SL::Compiler compiler; + compiler.set_source(mod.get_prepared_source(), ""); + 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 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 &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 &code = mod.get_code(); + glShaderBinary(n_stages, used_stage_ids, GL_SHADER_BINARY_FORMAT_SPIR_V, &code[0], code.size()*4); + + const vector &spec_consts = mod.get_spec_constants(); + vector spec_id_array; + vector 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; iname.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(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(this)->reflect_data; + + unsigned count = 0; + glGetProgramiv(id, GL_ACTIVE_UNIFORMS, reinterpret_cast(&count)); + rd.uniforms.reserve(count); + vector uniform_names(count); + for(unsigned i=0; i3 && !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 uniforms_by_index(count); + for(unsigned i=0; i=0) + { + UniformCall::FuncPtr func = 0; + if(is_image(u.type)) + glGetUniformiv(id, u.location, &u.binding); + else if(u.type==FLOAT) + func = &uniform_wrapper; + else if(u.type==FLOAT_VEC2) + func = &uniform_wrapper; + else if(u.type==FLOAT_VEC3) + func = &uniform_wrapper; + else if(u.type==FLOAT_VEC4) + func = &uniform_wrapper; + else if(u.type==INT) + func = &uniform_wrapper; + else if(u.type==INT_VEC2) + func = &uniform_wrapper; + else if(u.type==INT_VEC3) + func = &uniform_wrapper; + else if(u.type==INT_VEC4) + func = &uniform_wrapper; + else if(u.type==FLOAT_MAT2) + func = &uniform_matrix_wrapper; + else if(u.type==FLOAT_MAT3) + func = &uniform_matrix_wrapper; + else if(u.type==FLOAT_MAT4) + func = &uniform_matrix_wrapper; + else if(u.type==FLOAT_MAT2x3) + func = &uniform_matrix_wrapper; + else if(u.type==FLOAT_MAT3x2) + func = &uniform_matrix_wrapper; + else if(u.type==FLOAT_MAT2x4) + func = &uniform_matrix_wrapper; + else if(u.type==FLOAT_MAT4x2) + func = &uniform_matrix_wrapper; + else if(u.type==FLOAT_MAT3x4) + func = &uniform_matrix_wrapper; + else if(u.type==FLOAT_MAT4x3) + func = &uniform_matrix_wrapper; + + 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 &uniforms_by_index) +{ + ReflectData &rd = static_cast(this)->reflect_data; + + unsigned count = 0; + glGetProgramiv(id, GL_ACTIVE_UNIFORM_BLOCKS, reinterpret_cast(&count)); + // Reserve an extra index for the default block + rd.uniform_blocks.reserve(count+1); + for(unsigned i=0; i 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 query_indices(indices.begin(), indices.end()); + vector values(indices.size()); + glGetActiveUniformsiv(id, query_indices.size(), &query_indices[0], GL_UNIFORM_OFFSET, &values[0]); + for(unsigned j=0; joffset = 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; jarray_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; jmatrix_stride = values[j]; + } + + info.sort_uniforms(); + info.update_layout_hash(); + } +} + +void OpenGLProgram::query_attributes() +{ + ReflectData &rd = static_cast(this)->reflect_data; + + unsigned count = 0; + glGetProgramiv(id, GL_ACTIVE_ATTRIBUTES, reinterpret_cast(&count)); + rd.attributes.reserve(count); + for(unsigned i=0; i3 && !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(this)->reflect_data; + + for(unsigned i=0; isecond); + 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(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(i)); + } +#else + (void)name; +#endif +} + +void OpenGLProgram::set_stage_debug_name(unsigned stage_id, Stage type) +{ +#ifdef DEBUG + static const char *const suffixes[] = { " [VS]", " [GS]", " [FS]" }; + string name = debug_name+suffixes[type]; + glObjectLabel(GL_SHADER, stage_id, name.size(), name.c_str()); +#else + (void)stage_id; (void)type; +#endif +} + +} // namespace GL +} // namespace Msp diff --git a/source/backends/opengl/program_backend.h b/source/backends/opengl/program_backend.h new file mode 100644 index 00000000..4ae729e2 --- /dev/null +++ b/source/backends/opengl/program_backend.h @@ -0,0 +1,73 @@ +#ifndef MSP_GL_PROGRAM_BACKEND_H_ +#define MSP_GL_PROGRAM_BACKEND_H_ + +#include +#include +#include +#include "reflectdata.h" + +namespace Msp { +namespace GL { + +class OpenGLProgram +{ + friend class OpenGLPipelineState; + +protected: + enum Stage + { + VERTEX, + GEOMETRY, + FRAGMENT, + MAX_STAGES + }; + + struct TransientData + { + std::map textures; + std::map blocks; + std::map 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 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 &, TransientData &); + void compile_glsl_stage(const GlslModule &, unsigned); + void add_spirv_stages(const SpirVModule &, const std::map &, TransientData &); + + void finalize(const Module &); + void query_uniforms(); + void query_uniform_blocks(const std::vector &); + void query_attributes(); + void apply_bindings(const TransientData &); + + void set_debug_name(const std::string &); + void set_stage_debug_name(unsigned, Stage); +}; + +using ProgramBackend = OpenGLProgram; + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/backends/opengl/query_backend.cpp b/source/backends/opengl/query_backend.cpp new file mode 100644 index 00000000..b445be37 --- /dev/null +++ b/source/backends/opengl/query_backend.cpp @@ -0,0 +1,56 @@ +#include +#include +#include +#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(sold_size) + glGenQueries(s-old_size, queries.data()+old_size); +} + +unsigned OpenGLQueryPool::get_result(unsigned i) const +{ + unsigned result = 0; + glGetQueryObjectuiv(queries[i], GL_QUERY_RESULT, &result); + return result; +} + + +unsigned get_gl_query_type(unsigned t) +{ + switch(t) + { + case OCCLUSION_QUERY: return GL_ANY_SAMPLES_PASSED; + default: throw invalid_argument("get_gl_query_type"); + } +} + +} // namespace GL +} // namespace Msp diff --git a/source/backends/opengl/query_backend.h b/source/backends/opengl/query_backend.h new file mode 100644 index 00000000..9365b24b --- /dev/null +++ b/source/backends/opengl/query_backend.h @@ -0,0 +1,33 @@ +#ifndef MSP_GL_QUERY_BACKEND_H_ +#define MSP_GL_QUERY_BACKEND_H_ + +#include + +namespace Msp { +namespace GL { + +class OpenGLQueryPool +{ + friend class OpenGLCommands; + +protected: + unsigned gl_type; + std::vector queries; + + OpenGLQueryPool(unsigned); + ~OpenGLQueryPool(); + + void resize(unsigned); + unsigned get_size() const { return queries.size(); } + + unsigned get_result(unsigned) const; +}; + +using QueryPoolBackend = OpenGLQueryPool; + +unsigned get_gl_query_type(unsigned); + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/backends/opengl/sampler_backend.cpp b/source/backends/opengl/sampler_backend.cpp new file mode 100644 index 00000000..915dda68 --- /dev/null +++ b/source/backends/opengl/sampler_backend.cpp @@ -0,0 +1,98 @@ +#include +#include +#include +#include +#include +#include +#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(this); + if(mask&Sampler::MIN_FILTER) + glSamplerParameteri(id, GL_TEXTURE_MIN_FILTER, get_gl_filter(self->min_filter)); + if(mask&Sampler::MAG_FILTER) + glSamplerParameteri(id, GL_TEXTURE_MAG_FILTER, get_gl_filter(self->mag_filter)); + if(mask&Sampler::MAX_ANISOTROPY) + glSamplerParameterf(id, GL_TEXTURE_MAX_ANISOTROPY_EXT, self->max_anisotropy); + if(mask&Sampler::WRAP_S) + glSamplerParameteri(id, GL_TEXTURE_WRAP_S, get_gl_wrap(self->wrap_s)); + if(mask&Sampler::WRAP_T) + glSamplerParameteri(id, GL_TEXTURE_WRAP_T, get_gl_wrap(self->wrap_t)); + if(mask&Sampler::WRAP_R) + glSamplerParameteri(id, GL_TEXTURE_WRAP_R, get_gl_wrap(self->wrap_r)); + if(mask&Sampler::BORDER_COLOR) + glSamplerParameterfv(id, GL_TEXTURE_BORDER_COLOR, &self->border_color.r); + if(mask&Sampler::COMPARE) + { + glSamplerParameteri(id, GL_TEXTURE_COMPARE_MODE, (self->compare ? GL_COMPARE_R_TO_TEXTURE : GL_NONE)); + if(self->compare) + glSamplerParameteri(id, GL_TEXTURE_COMPARE_FUNC, get_gl_predicate(self->cmp_func)); + } +} + +void OpenGLSampler::set_debug_name(const string &name) +{ +#ifdef DEBUG + if(id && KHR_debug) + glObjectLabel(GL_SAMPLER, id, name.size(), name.c_str()); +#else + (void)name; +#endif +} + + +unsigned get_gl_filter(unsigned filter) +{ + switch(filter) + { + case NEAREST: return GL_NEAREST; + case LINEAR: return GL_LINEAR; + case NEAREST_MIPMAP_NEAREST: return GL_NEAREST_MIPMAP_NEAREST; + case NEAREST_MIPMAP_LINEAR: return GL_NEAREST_MIPMAP_LINEAR; + case LINEAR_MIPMAP_NEAREST: return GL_LINEAR_MIPMAP_NEAREST; + case LINEAR_MIPMAP_LINEAR: return GL_LINEAR_MIPMAP_LINEAR; + default: throw invalid_argument("get_gl_filter"); + } +} + +unsigned get_gl_wrap(unsigned wrap) +{ + switch(wrap) + { + case REPEAT: return GL_REPEAT; + case CLAMP_TO_EDGE: return GL_CLAMP_TO_EDGE; + case CLAMP_TO_BORDER: return GL_CLAMP_TO_BORDER; + case MIRRORED_REPEAT: return GL_MIRRORED_REPEAT; + default: throw invalid_argument("get_gl_wrap"); + } +} + +} // namespace GL +} // namespace Msp diff --git a/source/backends/opengl/sampler_backend.h b/source/backends/opengl/sampler_backend.h new file mode 100644 index 00000000..77590c25 --- /dev/null +++ b/source/backends/opengl/sampler_backend.h @@ -0,0 +1,31 @@ +#ifndef MSP_GL_SAMPLER_BACKEND_H_ +#define MSP_GL_SAMPLER_BACKEND_H_ + +namespace Msp { +namespace GL { + +class OpenGLSampler +{ + friend class OpenGLPipelineState; + +protected: + unsigned id; + + OpenGLSampler(); + + static bool check_anisotropic(bool); + + void update(unsigned) const; + + void set_debug_name(const std::string &); +}; + +using SamplerBackend = OpenGLSampler; + +unsigned get_gl_filter(unsigned); +unsigned get_gl_wrap(unsigned); + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/backends/opengl/stenciltest_backend.cpp b/source/backends/opengl/stenciltest_backend.cpp new file mode 100644 index 00000000..258bf9b8 --- /dev/null +++ b/source/backends/opengl/stenciltest_backend.cpp @@ -0,0 +1,27 @@ +#include "gl.h" +#include "stenciltest.h" +#include "stenciltest_backend.h" + +using namespace std; + +namespace Msp { +namespace GL { + +unsigned get_gl_stencil_op(StencilOp op) +{ + switch(op) + { + case KEEP: return GL_KEEP; + case SET_ZERO: return GL_ZERO; + case REPLACE: return GL_REPLACE; + case INCR: return GL_INCR; + case DECR: return GL_DECR; + case INVERT: return GL_INVERT; + case INCR_WRAP: return GL_INCR_WRAP; + case DECR_WRAP: return GL_DECR_WRAP; + default: throw invalid_argument("get_gl_stencil_op"); + } +} + +} // namespace GL +} // namespace Msp diff --git a/source/backends/opengl/stenciltest_backend.h b/source/backends/opengl/stenciltest_backend.h new file mode 100644 index 00000000..4b73a6aa --- /dev/null +++ b/source/backends/opengl/stenciltest_backend.h @@ -0,0 +1,16 @@ +#ifndef MSP_GL_STENCILTEST_BACKEND_H_ +#define MSP_GL_STENCILTEST_BACKEND_H_ + +#ifndef MSP_GL_STENCILTEST_H_ +#error "stenciltest_backend.h requires stenciltest.h" +#endif + +namespace Msp { +namespace GL { + +unsigned get_gl_stencil_op(StencilOp); + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/backends/opengl/texture1d_backend.cpp b/source/backends/opengl/texture1d_backend.cpp new file mode 100644 index 00000000..e44ceb4e --- /dev/null +++ b/source/backends/opengl/texture1d_backend.cpp @@ -0,0 +1,63 @@ +#include +#include +#include +#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(this)->width; + unsigned levels = static_cast(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(this)->get_level_size(i); + glTexImage1D(target, i, gl_fmt, lv_size, 0, comp, type, 0); + } + } + + apply_swizzle(); +} + +void OpenGLTexture1D::sub_image(unsigned level, int x, unsigned wd, const void *data) +{ + GLenum comp = get_gl_components(get_components(storage_fmt)); + GLenum type = get_gl_type(get_component_type(storage_fmt)); + if(ARB_direct_state_access) + glTextureSubImage1D(id, level, x, wd, comp, type, data); + else + { + bind_scratch(); + glTexSubImage1D(target, level, x, wd, comp, type, data); + } +} + +} // namespace GL +} // namespace Msp diff --git a/source/backends/opengl/texture1d_backend.h b/source/backends/opengl/texture1d_backend.h new file mode 100644 index 00000000..449118d9 --- /dev/null +++ b/source/backends/opengl/texture1d_backend.h @@ -0,0 +1,23 @@ +#ifndef MSP_GL_TEXTURE1D_BACKEND_H_ +#define MSP_GL_TEXTURE1D_BACKEND_H_ + +#include "texture.h" + +namespace Msp { +namespace GL { + +class OpenGLTexture1D: public Texture +{ +protected: + OpenGLTexture1D(); + + void allocate(); + void sub_image(unsigned, int, unsigned, const void *); +}; + +using Texture1DBackend = OpenGLTexture1D; + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/backends/opengl/texture2d_backend.cpp b/source/backends/opengl/texture2d_backend.cpp new file mode 100644 index 00000000..71a998dc --- /dev/null +++ b/source/backends/opengl/texture2d_backend.cpp @@ -0,0 +1,163 @@ +#include +#include +#include +#include +#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(this)->width; + unsigned height = static_cast(this)->height; + unsigned levels = static_cast(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(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(offset)); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); +} + +Resource::AsyncLoader *OpenGLTexture2D::create_async_loader(IO::Seekable &io) +{ + return new AsyncLoader(static_cast(*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(pixel_buffer.map()); + } + else if(phase==2) + image.load_into(*img_loader, mapped_address); + else if(phase==3) + { + mapped_address = 0; + if(!pixel_buffer.unmap()) + { + phase = 1; + return false; + } + + if(!texture.id) + texture.generate_id(); + + unsigned w = image.get_width(); + unsigned h = image.get_height(); + texture.storage(pixelformat_from_image(image, texture.use_srgb_format), w, h); + texture.OpenGLTexture2D::sub_image(0, 0, 0, w, h, pixel_buffer, 0); + + if(texture.auto_gen_mipmap) + texture.generate_mipmap(); + } + + ++phase; + return phase>3; +} + +} // namespace GL +} // namespace Msp diff --git a/source/backends/opengl/texture2d_backend.h b/source/backends/opengl/texture2d_backend.h new file mode 100644 index 00000000..8f2fec59 --- /dev/null +++ b/source/backends/opengl/texture2d_backend.h @@ -0,0 +1,31 @@ +#ifndef MSP_GL_TEXTURE2D_BACKEND_H_ +#define MSP_GL_TEXTURE2D_BACKEND_H_ + +#include "texture.h" + +namespace Msp { +namespace GL { + +class Buffer; + +class OpenGLTexture2D: public Texture +{ +protected: + class AsyncLoader; + + OpenGLTexture2D(ResourceManager *); + + void allocate(); + void sub_image(unsigned, int, int, unsigned, unsigned, const void *); + void sub_image(unsigned, int, int, unsigned, unsigned, const Buffer &, unsigned); + + Resource::AsyncLoader *create_async_loader(IO::Seekable &); + void unload(); +}; + +using Texture2DBackend = OpenGLTexture2D; + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/backends/opengl/texture2darray_backend.cpp b/source/backends/opengl/texture2darray_backend.cpp new file mode 100644 index 00000000..610229ba --- /dev/null +++ b/source/backends/opengl/texture2darray_backend.cpp @@ -0,0 +1,14 @@ +#include +#include "texture2darray_backend.h" + +namespace Msp { +namespace GL { + +OpenGLTexture2DArray::OpenGLTexture2DArray(): + Texture3D(GL_TEXTURE_2D_ARRAY) +{ + static Require _req(EXT_texture_array); +} + +} // namespace GL +} // namespace Msp diff --git a/source/backends/opengl/texture2darray_backend.h b/source/backends/opengl/texture2darray_backend.h new file mode 100644 index 00000000..42a33fc0 --- /dev/null +++ b/source/backends/opengl/texture2darray_backend.h @@ -0,0 +1,20 @@ +#ifndef MSP_GL_TEXTURE2DARRAY_BACKEND_H_ +#define MSP_GL_TEXTURE2DARRAY_BACKEND_H_ + +#include "texture3d.h" + +namespace Msp { +namespace GL { + +class OpenGLTexture2DArray: public Texture3D +{ +protected: + OpenGLTexture2DArray(); +}; + +using Texture2DArrayBackend = OpenGLTexture2DArray; + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/backends/opengl/texture2dmultisample_backend.cpp b/source/backends/opengl/texture2dmultisample_backend.cpp new file mode 100644 index 00000000..c4a4e423 --- /dev/null +++ b/source/backends/opengl/texture2dmultisample_backend.cpp @@ -0,0 +1,42 @@ +#include +#include +#include +#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(this)->width; + unsigned height = static_cast(this)->height; + unsigned samples = static_cast(this)->samples; + + GLenum gl_fmt = get_gl_pixelformat(storage_fmt); + if(ARB_texture_storage_multisample) + { + if(ARB_direct_state_access) + glTextureStorage2DMultisample(id, samples, gl_fmt, width, height, false); + else + { + bind_scratch(); + glTexStorage2DMultisample(target, samples, gl_fmt, width, height, false); + } + } + else + { + bind_scratch(); + glTexImage2DMultisample(target, samples, gl_fmt, width, height, false); + } + apply_swizzle(); +} + +} // namespace GL +} // namespace Msp diff --git a/source/backends/opengl/texture2dmultisample_backend.h b/source/backends/opengl/texture2dmultisample_backend.h new file mode 100644 index 00000000..e8a37e4c --- /dev/null +++ b/source/backends/opengl/texture2dmultisample_backend.h @@ -0,0 +1,22 @@ +#ifndef MSP_GL_TEXTURE2DMULTISAMPLE_BACKEND_H_ +#define MSP_GL_TEXTURE2DMULTISAMPLE_BACKEND_H_ + +#include "texture.h" + +namespace Msp { +namespace GL { + +class OpenGLTexture2DMultisample: public Texture +{ +protected: + OpenGLTexture2DMultisample(); + + void allocate(); +}; + +using Texture2DMultisampleBackend = OpenGLTexture2DMultisample; + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/backends/opengl/texture3d_backend.cpp b/source/backends/opengl/texture3d_backend.cpp new file mode 100644 index 00000000..6826993a --- /dev/null +++ b/source/backends/opengl/texture3d_backend.cpp @@ -0,0 +1,75 @@ +#include +#include +#include +#include +#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(this)->width; + unsigned height = static_cast(this)->height; + unsigned depth = static_cast(this)->depth; + unsigned levels = static_cast(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(this)->get_level_size(i); + glTexImage3D(target, i, gl_fmt, lv_size.x, lv_size.y, lv_size.z, 0, comp, type, 0); + } + glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, levels-1); + } + + apply_swizzle(); +} + +void OpenGLTexture3D::sub_image(unsigned level, int x, int y, int z, unsigned wd, unsigned ht, unsigned dp, const void *data) +{ + GLenum comp = get_gl_components(get_components(storage_fmt)); + GLenum type = get_gl_type(get_component_type(storage_fmt)); + if(ARB_direct_state_access) + glTextureSubImage3D(id, level, x, y, z, wd, ht, dp, comp, type, data); + else + { + bind_scratch(); + glTexSubImage3D(target, level, x, y, z, wd, ht, dp, comp, type, data); + } +} + +bool OpenGLTexture3D::is_array() const +{ + return target==GL_TEXTURE_2D_ARRAY; +} + +} // namespace GL +} // namespace Msp diff --git a/source/backends/opengl/texture3d_backend.h b/source/backends/opengl/texture3d_backend.h new file mode 100644 index 00000000..33dcee08 --- /dev/null +++ b/source/backends/opengl/texture3d_backend.h @@ -0,0 +1,26 @@ +#ifndef MSP_GL_TEXTURE3D_BACKEND_H_ +#define MSP_GL_TEXTURE3D_BACKEND_H_ + +#include "texture.h" + +namespace Msp { +namespace GL { + +class OpenGLTexture3D: public Texture +{ +protected: + OpenGLTexture3D(); + OpenGLTexture3D(unsigned); + + void allocate(); + void sub_image(unsigned, int, int, int, unsigned, unsigned, unsigned, const void *); + + bool is_array() const; +}; + +using Texture3DBackend = OpenGLTexture3D; + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/backends/opengl/texture_backend.cpp b/source/backends/opengl/texture_backend.cpp new file mode 100644 index 00000000..fe4679f6 --- /dev/null +++ b/source/backends/opengl/texture_backend.cpp @@ -0,0 +1,146 @@ +#include +#include +#include +#include +#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(this)->swizzle; + if(swizzle==Texture::NO_SWIZZLE) + return; + + if(get_backend_api()==OPENGL_ES) + { + set_parameter_i(GL_TEXTURE_SWIZZLE_R, swizzle_orders[swizzle*4]); + set_parameter_i(GL_TEXTURE_SWIZZLE_G, swizzle_orders[swizzle*4+1]); + set_parameter_i(GL_TEXTURE_SWIZZLE_B, swizzle_orders[swizzle*4+2]); + set_parameter_i(GL_TEXTURE_SWIZZLE_A, swizzle_orders[swizzle*4+3]); + } + else + { + if(ARB_direct_state_access) + glTextureParameteriv(id, GL_TEXTURE_SWIZZLE_RGBA, swizzle_orders+swizzle*4); + else + glTexParameteriv(target, GL_TEXTURE_SWIZZLE_RGBA, swizzle_orders+swizzle*4); + } +} + +void OpenGLTexture::set_parameter_i(unsigned param, int value) const +{ + if(ARB_direct_state_access) + glTextureParameteri(id, param, value); + else + glTexParameteri(target, param, value); +} + +void OpenGLTexture::generate_mipmap() +{ + // glGenerateMipmap is defined here + static Require _req(EXT_framebuffer_object); + + if(ARB_direct_state_access) + glGenerateTextureMipmap(id); + else + { + bind_scratch(); + glGenerateMipmap(target); + } +} + +void OpenGLTexture::set_debug_name(const string &name) +{ +#ifdef DEBUG + debug_name = name; + if(id && KHR_debug) + glObjectLabel(GL_TEXTURE, id, name.size(), name.c_str()); +#else + (void)name; +#endif +} + +void OpenGLTexture::bind_scratch() +{ + if(!scratch_binding) + glActiveTexture(GL_TEXTURE0); + if(scratch_binding!=this) + { + if(scratch_binding && scratch_binding->target!=target) + glBindTexture(scratch_binding->target, 0); + glBindTexture(target, id); + scratch_binding = this; + } +} + +void OpenGLTexture::unbind_scratch() +{ + if(scratch_binding) + { + glBindTexture(scratch_binding->target, 0); + scratch_binding = 0; + } +} + +} // namespace GL +} // namespace Msp diff --git a/source/backends/opengl/texture_backend.h b/source/backends/opengl/texture_backend.h new file mode 100644 index 00000000..f272d3d8 --- /dev/null +++ b/source/backends/opengl/texture_backend.h @@ -0,0 +1,43 @@ +#ifndef MSP_GL_TEXTURE_BACKEND_H_ +#define MSP_GL_TEXTURE_BACKEND_H_ + +#include + +namespace Msp { +namespace GL { + +class OpenGLTexture: public NonCopyable +{ + friend class OpenGLFramebuffer; + friend class OpenGLPipelineState; + +protected: + unsigned id; + unsigned target; + std::string debug_name; + + static int swizzle_orders[]; + static OpenGLTexture *scratch_binding; + + OpenGLTexture(unsigned, bool); + ~OpenGLTexture(); + + void generate_id(); + void require_swizzle(); + void apply_swizzle(); + void set_parameter_i(unsigned, int) const; + + void generate_mipmap(); + + void set_debug_name(const std::string &); + + void bind_scratch(); + static void unbind_scratch(); +}; + +using TextureBackend = OpenGLTexture; + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/backends/opengl/texturecube_backend.cpp b/source/backends/opengl/texturecube_backend.cpp new file mode 100644 index 00000000..b113f1bd --- /dev/null +++ b/source/backends/opengl/texturecube_backend.cpp @@ -0,0 +1,90 @@ +#include +#include +#include +#include +#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(this)->size; + unsigned levels = static_cast(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(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(face)) + { + case POSITIVE_X: return GL_TEXTURE_CUBE_MAP_POSITIVE_X; + case NEGATIVE_X: return GL_TEXTURE_CUBE_MAP_NEGATIVE_X; + case POSITIVE_Y: return GL_TEXTURE_CUBE_MAP_POSITIVE_Y; + case NEGATIVE_Y: return GL_TEXTURE_CUBE_MAP_NEGATIVE_Y; + case POSITIVE_Z: return GL_TEXTURE_CUBE_MAP_POSITIVE_Z; + case NEGATIVE_Z: return GL_TEXTURE_CUBE_MAP_NEGATIVE_Z; + default: throw invalid_argument("get_gl_cube_face"); + } +} + +} // namespace GL +} // namespace Msp diff --git a/source/backends/opengl/texturecube_backend.h b/source/backends/opengl/texturecube_backend.h new file mode 100644 index 00000000..2b789d2f --- /dev/null +++ b/source/backends/opengl/texturecube_backend.h @@ -0,0 +1,25 @@ +#ifndef MSP_GL_TEXTURECUBE_BACKEND_H_ +#define MSP_GL_TEXTURECUBE_BACKEND_H_ + +#include "texture.h" + +namespace Msp { +namespace GL { + +class OpenGLTextureCube: public Texture +{ +protected: + OpenGLTextureCube(); + + void allocate(); + void sub_image(unsigned, unsigned, int, int, unsigned, unsigned, const void *); +}; + +using TextureCubeBackend = OpenGLTextureCube; + +unsigned get_gl_cube_face(unsigned); + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/backends/opengl/uniformblock_backend.cpp b/source/backends/opengl/uniformblock_backend.cpp new file mode 100644 index 00000000..f691bbe2 --- /dev/null +++ b/source/backends/opengl/uniformblock_backend.cpp @@ -0,0 +1,16 @@ +#include +#include +#include "uniformblock_backend.h" + +namespace Msp { +namespace GL { + +OpenGLUniformBlock::OpenGLUniformBlock(bool ubo) +{ + static Require _req(ARB_shader_objects); + if(ubo) + static Require _req2(ARB_uniform_buffer_object); +} + +} // namespace GL +} // namespace Msp diff --git a/source/backends/opengl/uniformblock_backend.h b/source/backends/opengl/uniformblock_backend.h new file mode 100644 index 00000000..c95a3481 --- /dev/null +++ b/source/backends/opengl/uniformblock_backend.h @@ -0,0 +1,19 @@ +#ifndef MSP_GL_UNIFORMBLOCK_BACKEND_H_ +#define MSP_GL_UNIFORMBLOCK_BACKEND_H_ + +namespace Msp { +namespace GL { + +class OpenGLUniformBlock +{ +protected: + OpenGLUniformBlock(bool); + ~OpenGLUniformBlock() { } +}; + +using UniformBlockBackend = OpenGLUniformBlock; + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/backends/opengl/vertexsetup_backend.cpp b/source/backends/opengl/vertexsetup_backend.cpp new file mode 100644 index 00000000..90189db7 --- /dev/null +++ b/source/backends/opengl/vertexsetup_backend.cpp @@ -0,0 +1,153 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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(this)->vertex_array, 0, 0, direct); + + if(mask&VertexSetup::INSTANCE_ARRAY) + update_vertex_array(*static_cast(this)->inst_array, 1, 1, direct); + + if(mask&VertexSetup::INDEX_BUFFER) + { + unsigned buf_id = static_cast(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(offset)); + else + glVertexAttribPointer(sem, cc, type, true, stride, reinterpret_cast(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(this)->vertex_format) + { + unsigned sem = get_attribute_semantic(a); + glDisableVertexAttribArray(sem); + glVertexAttribPointer(sem, 1, GL_FLOAT, false, 0, 0); + } + for(VertexAttribute a: static_cast(this)->inst_format) + { + unsigned sem = get_attribute_semantic(a); + glDisableVertexAttribArray(sem); + glVertexAttribPointer(sem, 1, GL_FLOAT, false, 0, 0); + } + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + } +} + +void OpenGLVertexSetup::set_debug_name(const string &name) +{ +#ifdef DEBUG + if(KHR_debug) + glObjectLabel(GL_VERTEX_ARRAY, id, name.size(), name.c_str()); +#else + (void)name; +#endif +} + +} // namespace GL +} // namespace Msp diff --git a/source/backends/opengl/vertexsetup_backend.h b/source/backends/opengl/vertexsetup_backend.h new file mode 100644 index 00000000..954dcc7f --- /dev/null +++ b/source/backends/opengl/vertexsetup_backend.h @@ -0,0 +1,34 @@ +#ifndef MSP_GL_VERTEXSETUP_BACKEND_H_ +#define MSP_GL_VERTEXSETUP_BACKEND_H_ + +namespace Msp { +namespace GL { + +class VertexArray; +class VertexFormat; + +class OpenGLVertexSetup +{ + friend class OpenGLPipelineState; + +protected: + unsigned id; + + OpenGLVertexSetup(); + ~OpenGLVertexSetup(); + + static void require_format(const VertexFormat &, bool); + void update(unsigned) const; + void update_vertex_array(const VertexArray &, unsigned, unsigned, bool) const; + + void unload(); + + void set_debug_name(const std::string &); +}; + +using VertexSetupBackend = OpenGLVertexSetup; + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/builders/font.cpp b/source/builders/font.cpp index dfa6bf9c..f46475bc 100644 --- a/source/builders/font.cpp +++ b/source/builders/font.cpp @@ -1,7 +1,6 @@ #include #include #include -#include "gl.h" #include "font.h" #include "primitivebuilder.h" #include "texture2d.h" diff --git a/source/core/backend.cpp b/source/core/backend.cpp index c3e159f4..ac9e22b7 100644 --- a/source/core/backend.cpp +++ b/source/core/backend.cpp @@ -1,9 +1,6 @@ -#include -#include #include #include #include "backend.h" -#include "gl.h" using namespace std; @@ -34,43 +31,5 @@ bool Version::operator>=(const Version &other) const return major>other.major || (major==other.major && minor>=other.minor); } - -GraphicsApi get_backend_api() -{ -#ifdef GL_ES_VERSION_2_0 - return OPENGL_ES; -#else - return OPENGL; -#endif -} - -inline Version get_gl_version() -{ - const char *gl_ver_ptr = reinterpret_cast(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 #include "batch.h" #include "error.h" @@ -46,12 +45,13 @@ namespace Msp { namespace GL { Batch::Batch(PrimitiveType t): + BatchBackend(t), prim_type(t), - gl_prim_type(GL::get_gl_primitive_type(prim_type)), - index_type(UNSIGNED_SHORT), - gl_index_type(get_gl_type(index_type)), + index_type(VOID), max_index(0) -{ } +{ + set_index_type(UNSIGNED_SHORT); +} Batch::~Batch() { @@ -72,7 +72,7 @@ void Batch::set_index_type(DataType t) shrink(data); index_type = t; - gl_index_type = get_gl_type(t); + BatchBackend::set_index_type(t); update_offset(); dirty = true; } @@ -107,7 +107,7 @@ bool Batch::can_append(PrimitiveType other_type) if(other_type!=prim_type) return false; else if(prim_type==LINE_STRIP || prim_type==LINE_LOOP || prim_type==TRIANGLE_FAN) - return MSP_primitive_restart; + return check_restart(false); else return true; } @@ -117,7 +117,7 @@ Batch &Batch::append(const Batch &other) if(other.prim_type!=prim_type) throw invalid_argument("Batch::append"); if(prim_type==LINE_STRIP || prim_type==LINE_LOOP || prim_type==TRIANGLE_FAN) - static Require _req(MSP_primitive_restart); + check_restart(true); if(other.data.empty()) return *this; @@ -126,7 +126,7 @@ Batch &Batch::append(const Batch &other) if(prim_type==POINTS || prim_type==LINES || prim_type==TRIANGLES) ; - else if(MSP_primitive_restart) + else if(check_restart(false)) { if(index_type==UNSIGNED_INT) ::append(data, 0xFFFFFFFF); diff --git a/source/core/batch.h b/source/core/batch.h index e0343555..9577a542 100644 --- a/source/core/batch.h +++ b/source/core/batch.h @@ -3,6 +3,7 @@ #include #include +#include "batch_backend.h" #include "bufferable.h" #include "datatype.h" #include "primitivetype.h" @@ -20,10 +21,8 @@ the Batch. This is a pretty low-level class and mainly intended to be used by the Mesh class. */ -class Batch: public Bufferable +class Batch: public BatchBackend, public Bufferable { - friend class Commands; - public: class Loader: public DataFile::ObjectLoader { @@ -35,9 +34,7 @@ public: private: PrimitiveType prim_type; - unsigned gl_prim_type; DataType index_type; - unsigned gl_index_type; std::vector data; unsigned max_index; diff --git a/source/core/blend.cpp b/source/core/blend.cpp index 8ceb2382..85d3e57d 100644 --- a/source/core/blend.cpp +++ b/source/core/blend.cpp @@ -1,5 +1,3 @@ -#include -#include #include #include #include "blend.h" @@ -64,41 +62,6 @@ void Blend::Loader::factors(BlendFactor sf, BlendFactor df) } -unsigned get_gl_blend_equation(BlendEquation eq) -{ - switch(eq) - { - case ADD: return GL_FUNC_ADD; - case SUBTRACT: return GL_FUNC_SUBTRACT; - case REVERSE_SUBTRACT: return GL_FUNC_REVERSE_SUBTRACT; - case MIN: return GL_MIN; - case MAX: return GL_MAX; - default: throw invalid_argument("get_gl_blend_equation"); - } -} - -unsigned get_gl_blend_factor(BlendFactor factor) -{ - switch(factor) - { - case ZERO: return GL_ZERO; - case ONE: return GL_ONE; - case SRC_COLOR: return GL_SRC_COLOR; - case ONE_MINUS_SRC_COLOR: return GL_ONE_MINUS_SRC_COLOR; - case SRC_ALPHA: return GL_SRC_ALPHA; - case ONE_MINUS_SRC_ALPHA: return GL_ONE_MINUS_SRC_ALPHA; - case DST_COLOR: return GL_DST_COLOR; - case ONE_MINUS_DST_COLOR: return GL_ONE_MINUS_DST_COLOR; - case DST_ALPHA: return GL_DST_ALPHA; - case ONE_MINUS_DST_ALPHA: return GL_ONE_MINUS_DST_ALPHA; - case CONSTANT_COLOR: return GL_CONSTANT_COLOR; - case ONE_MINUS_CONSTANT_COLOR: return GL_ONE_MINUS_CONSTANT_COLOR; - case CONSTANT_ALPHA: return GL_CONSTANT_ALPHA; - case ONE_MINUS_CONSTANT_ALPHA: return GL_ONE_MINUS_CONSTANT_ALPHA; - default: throw invalid_argument("get_gl_blend_factor"); - } -} - void operator>>(const LexicalConverter &conv, BlendEquation &eq) { const string &str = conv.get(); diff --git a/source/core/blend.h b/source/core/blend.h index a1180050..e04aef49 100644 --- a/source/core/blend.h +++ b/source/core/blend.h @@ -77,9 +77,6 @@ struct Blend inline ColorWriteMask operator|(ColorWriteMask m1, ColorWriteMask m2) { return static_cast(static_cast(m1)|static_cast(m2)); } -unsigned get_gl_blend_equation(BlendEquation); -unsigned get_gl_blend_factor(BlendFactor); - void operator>>(const LexicalConverter &, BlendEquation &); void operator<<(LexicalConverter &, BlendEquation); @@ -92,4 +89,6 @@ void operator<<(LexicalConverter &, ColorWriteMask); } // namespace GL } // namespace Msp +#include "blend_backend.h" + #endif diff --git a/source/core/buffer.cpp b/source/core/buffer.cpp index dcb83a7a..811b3e41 100644 --- a/source/core/buffer.cpp +++ b/source/core/buffer.cpp @@ -1,10 +1,4 @@ #include -#include -#include -#include -#include -#include -#include #include #include "buffer.h" #include "error.h" @@ -14,25 +8,9 @@ using namespace std; namespace Msp { namespace GL { -Buffer *Buffer::scratch_binding = 0; - Buffer::Buffer(): size(0) -{ - static Require _req(ARB_vertex_buffer_object); - - if(ARB_direct_state_access) - glCreateBuffers(1, &id); - else - glGenBuffers(1, &id); -} - -Buffer::~Buffer() -{ - if(this==scratch_binding) - unbind_scratch(); - glDeleteBuffers(1, &id); -} +{ } void Buffer::storage(unsigned sz) { @@ -47,24 +25,7 @@ void Buffer::storage(unsigned sz) size = sz; - if(ARB_buffer_storage) - { - static const int flags = GL_MAP_READ_BIT|GL_MAP_WRITE_BIT|GL_DYNAMIC_STORAGE_BIT; - if(ARB_direct_state_access) - glNamedBufferStorage(id, size, 0, flags); - else - { - bind_scratch(); - glBufferStorage(GL_ARRAY_BUFFER, size, 0, flags); - } - } - else if(ARB_direct_state_access) - glNamedBufferData(id, size, 0, GL_STATIC_DRAW); - else - { - bind_scratch(); - glBufferData(GL_ARRAY_BUFFER, size, 0, GL_STATIC_DRAW); - } + allocate(); } void Buffer::data(const void *d) @@ -77,13 +38,7 @@ void Buffer::sub_data(unsigned off, unsigned sz, const void *d) if(size==0) throw invalid_operation("Buffer::sub_data"); - if(ARB_direct_state_access) - glNamedBufferSubData(id, off, sz, d); - else - { - bind_scratch(); - glBufferSubData(GL_ARRAY_BUFFER, off, sz, d); - } + BufferBackend::sub_data(off, sz, d); } void Buffer::require_size(unsigned req_sz) const @@ -92,62 +47,5 @@ void Buffer::require_size(unsigned req_sz) const throw buffer_too_small(format("buffer has %d bytes; %d required", size, req_sz)); } -void *Buffer::map() -{ - static Require _req(ARB_map_buffer_range); - - if(ARB_direct_state_access) - return glMapNamedBufferRange(id, 0, size, GL_MAP_READ_BIT|GL_MAP_WRITE_BIT); - else - { - bind_scratch(); - void *result = glMapBufferRange(GL_ARRAY_BUFFER, 0, size, GL_MAP_READ_BIT|GL_MAP_WRITE_BIT); - return result; - } -} - -bool Buffer::unmap() -{ - // TODO check if it's mapped - if(ARB_direct_state_access) - return glUnmapNamedBuffer(id); - else if(OES_mapbuffer) - { - bind_scratch(); - bool result = glUnmapBuffer(GL_ARRAY_BUFFER); - return result; - } - else - return true; -} - -void Buffer::set_debug_name(const string &name) -{ -#ifdef DEBUG - if(KHR_debug) - glObjectLabel(GL_BUFFER, id, name.size(), name.c_str()); -#else - (void)name; -#endif -} - -void Buffer::bind_scratch() -{ - if(scratch_binding!=this) - { - glBindBuffer(GL_ARRAY_BUFFER, id); - scratch_binding = this; - } -} - -void Buffer::unbind_scratch() -{ - if(scratch_binding) - { - glBindBuffer(GL_ARRAY_BUFFER, 0); - scratch_binding = 0; - } -} - } // namespace GL } // namespace Msp diff --git a/source/core/buffer.h b/source/core/buffer.h index df570d7e..9737a2ea 100644 --- a/source/core/buffer.h +++ b/source/core/buffer.h @@ -3,6 +3,7 @@ #include #include +#include "buffer_backend.h" namespace Msp { namespace GL { @@ -21,21 +22,15 @@ A buffer for storing data in GL memory. Putting vertex and index data in buffers can improve rendering performance. The VertexArray, Mesh and UniformBlock classes contain built-in support for buffers. */ -class Buffer +class Buffer: public BufferBackend { - friend class PipelineState; - friend class Texture2D; - friend class VertexSetup; + friend BufferBackend; private: - unsigned id; unsigned size; - static Buffer *scratch_binding; - public: Buffer(); - ~Buffer(); /** Defines the storage size of the buffer. Must be called before data can be uploaded. Storage cannot be changed once set. */ @@ -54,15 +49,10 @@ public: void require_size(unsigned) const; - void *map(); - bool unmap(); - - void set_debug_name(const std::string &); + using BufferBackend::map; + using BufferBackend::unmap; -private: - void bind_scratch(); -public: - static void unbind_scratch(); + using BufferBackend::set_debug_name; }; } // namespace GL diff --git a/source/core/commands.cpp b/source/core/commands.cpp deleted file mode 100644 index ebc6394e..00000000 --- a/source/core/commands.cpp +++ /dev/null @@ -1,125 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#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(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(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 diff --git a/source/core/commands.h b/source/core/commands.h index d290e34e..869c41b6 100644 --- a/source/core/commands.h +++ b/source/core/commands.h @@ -1,31 +1,23 @@ #ifndef MSP_GL_COMMANDS_H_ #define MSP_GL_COMMANDS_H_ -#include "framebuffer.h" +#include "commands_backend.h" namespace Msp { namespace GL { -class Batch; -class PipelineState; -class QueryPool; - -class Commands +class Commands: public CommandsBackend { -private: - const PipelineState *pipeline_state; - public: - Commands(); + using CommandsBackend::use_pipeline; - void use_pipeline(const PipelineState *); - void clear(const ClearValue *); - void draw(const Batch &); - void draw_instanced(const Batch &, unsigned); - void resolve_multisample(Framebuffer &); + using CommandsBackend::clear; + using CommandsBackend::draw; + using CommandsBackend::draw_instanced; + using CommandsBackend::resolve_multisample; - void begin_query(const QueryPool &, unsigned); - void end_query(const QueryPool &, unsigned); + using CommandsBackend::begin_query; + using CommandsBackend::end_query; }; } // namespace GL diff --git a/source/core/datatype.cpp b/source/core/datatype.cpp deleted file mode 100644 index 4dd0b89f..00000000 --- a/source/core/datatype.cpp +++ /dev/null @@ -1,117 +0,0 @@ -#include -#include -#include -#include -#include -#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.typetype!=type) - throw invalid_argument("get_gl_type"); - return ptr->gl_type; -} - -DataType from_gl_type(GLenum gl_type) -{ - for(unsigned i=0; i>12)&3)+1; - unsigned cols = ((type>>14)&4)+1; - if(rows>1 && cols>1 && rows!=cols) - static Require _req(NV_non_square_matrices); - if((type&0x200) && get_type_size(type)/(rows*cols)==8) - static Require _req(ARB_gpu_shader_fp64); -} - -} // namespace GL -} // namespace Msp diff --git a/source/core/datatype.h b/source/core/datatype.h index 1620dc58..8111dd77 100644 --- a/source/core/datatype.h +++ b/source/core/datatype.h @@ -132,12 +132,11 @@ struct TypeTraits> static const DataType type = static_cast((TypeTraits::type&0xF00) | ((TypeTraits::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 diff --git a/source/core/deviceinfo.cpp b/source/core/deviceinfo.cpp index 37a04f8a..8400ffdf 100644 --- a/source/core/deviceinfo.cpp +++ b/source/core/deviceinfo.cpp @@ -1,49 +1,8 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include "deviceinfo.h" -#include "gl.h" namespace Msp { namespace GL { -Limits::Limits() -{ - glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, reinterpret_cast(&max_vertex_attributes)); - glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, reinterpret_cast(&max_texture_bindings)); - glGetIntegerv(GL_MAX_UNIFORM_BUFFER_BINDINGS, reinterpret_cast(&max_uniform_bindings)); - glGetIntegerv(GL_MAX_CLIP_PLANES, reinterpret_cast(&max_clip_planes)); - glGetIntegerv(GL_MAX_SAMPLES, reinterpret_cast(&max_samples)); - glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, reinterpret_cast(&uniform_buffer_alignment)); - glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, reinterpret_cast(&max_color_attachments)); -} - - -DeviceInfo::DeviceInfo() -{ - glsl_features.target_api = get_backend_api(); - glsl_features.glsl_version = get_glsl_version(); - glsl_features.arb_enhanced_layouts = ARB_enhanced_layouts; - glsl_features.arb_explicit_attrib_location = ARB_explicit_attrib_location; - glsl_features.arb_explicit_uniform_location = ARB_explicit_uniform_location; - glsl_features.arb_gpu_shader5 = ARB_gpu_shader5; - glsl_features.arb_separate_shader_objects = ARB_separate_shader_objects; - glsl_features.arb_uniform_buffer_object = ARB_uniform_buffer_object; - glsl_features.ext_gpu_shader4 = EXT_gpu_shader4; - glsl_features.ext_texture_array = EXT_texture_array; - glsl_features.uniform_binding_range = limits.max_uniform_bindings; - glsl_features.texture_binding_range = limits.max_texture_bindings; -} - const DeviceInfo &DeviceInfo::get_global() { static DeviceInfo info; diff --git a/source/core/extension.cpp b/source/core/extension.cpp deleted file mode 100644 index a55da9ba..00000000 --- a/source/core/extension.cpp +++ /dev/null @@ -1,195 +0,0 @@ -#include -#include -#if defined(__ANDROID__) -#include -#elif defined(_WIN32) -#include -#elif !defined(__APPLE__) -#define GLX_GLXEXT_PROTOTYPES -#include -#endif -#include -#include -#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 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(get_proc_address("glGetStringi")); - int n_extensions; - glGetIntegerv(GL_NUM_EXTENSIONS, &n_extensions); - for(int i=0; i(glGetStringi(GL_EXTENSIONS, i))); - } - else - { - if(const char *gl_ext = reinterpret_cast(glGetString(GL_EXTENSIONS))) - { - vector 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 disabled_exts; - static bool init_done = false; - - if(!init_done) - { - if(const char *disable_ptr = getenv("MSPGL_DISABLE_EXTENSIONS")) - { - vector disable = split(disable_ptr); - disabled_exts.insert(disable.begin(), disable.end()); - } - - if(const char *renderer_ptr = reinterpret_cast(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(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(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(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(name.c_str())); -#endif -} - -} // namespace GL -} // namespace Msp diff --git a/source/core/extension.h b/source/core/extension.h deleted file mode 100644 index 77996357..00000000 --- a/source/core/extension.h +++ /dev/null @@ -1,79 +0,0 @@ -#ifndef MSP_GL_EXTENSION_H_ -#define MSP_GL_EXTENSION_H_ - -#include -#include "backend.h" - -namespace Msp { -namespace GL { - -enum GLProfile -{ - CORE_PROFILE, - COMPATIBILITY_PROFILE -}; - - -/** -Holds metadata about an extension. Evaluates to true if the extension is -supported. -*/ -class Extension -{ -public: - enum SupportLevel - { - UNSUPPORTED, - EXTENSION, - CORE - }; - - typedef SupportLevel (*InitFunc)(); - -private: - const char *name; - InitFunc init_func; - mutable bool init_done; - mutable SupportLevel support; - -public: - Extension(const char *, InitFunc); - - const char *get_name() const { return name; } - operator bool() const; -}; - - -struct Require -{ - Require(const Extension &); -}; - - -typedef void ExtFunc(); - -/** Checks for extension support. Only intended for internal use. */ -bool is_supported(const std::string &); - -/** Checks for OpenGL version support. Only intended for internal use. */ -bool is_supported(const Version &, const Version & = Version()); - -/** Indicates whether an extension has been disabled, either explicitly through -the MSPGL_DISABLE_EXTENSIONS environment variable or implicitly as a workaround -for a driver bug. Only intended for internal use. */ -bool is_disabled(const std::string &); - -/** Returns the OpenGL profile for the active context. */ -GLProfile get_gl_profile(); - -/** Returns the GLSL version number, as reported by the implementation. */ -const Version &get_glsl_version(); - -/** Returns the address of an extension function. Only indended for internal -use. */ -ExtFunc *get_proc_address(const std::string &); - -} // namespace GL -} // namespace Msp - -#endif diff --git a/source/core/framebuffer.cpp b/source/core/framebuffer.cpp index 53234cc7..167b403a 100644 --- a/source/core/framebuffer.cpp +++ b/source/core/framebuffer.cpp @@ -1,14 +1,3 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include "error.h" #include "framebuffer.h" #include "texture2d.h" @@ -26,50 +15,32 @@ framebuffer_incomplete::framebuffer_incomplete(const std::string &reason): { } -Framebuffer::Framebuffer(unsigned i): - id(i), - status(GL_FRAMEBUFFER_COMPLETE), +Framebuffer::Framebuffer(bool s): + FramebufferBackend(s), dirty(0) { - if(id) - throw invalid_argument("System framebuffer must have id 0"); - - if(EXT_framebuffer_object) + if(s) { - int value; - glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_BACK, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &value); - if(value==GL_NONE) - glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_FRONT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &value); - if(value!=GL_NONE) - format = (format,COLOR_ATTACHMENT); - - glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_DEPTH, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &value); - if(value!=GL_NONE) - format = (format,DEPTH_ATTACHMENT); - - glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_STENCIL, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &value); - if(value!=GL_NONE) - format = (format,STENCIL_ATTACHMENT); + format = get_system_format(); + get_system_size(width, height); } - - int view[4]; - glGetIntegerv(GL_VIEWPORT, view); - width = view[2]; - height = view[3]; } -Framebuffer::Framebuffer() +Framebuffer::Framebuffer(): + FramebufferBackend(false) { init(); } -Framebuffer::Framebuffer(FrameAttachment fa) +Framebuffer::Framebuffer(FrameAttachment fa): + FramebufferBackend(false) { init(); set_format(fa); } -Framebuffer::Framebuffer(const FrameFormat &f) +Framebuffer::Framebuffer(const FrameFormat &f): + FramebufferBackend(false) { init(); set_format(f); @@ -77,119 +48,25 @@ Framebuffer::Framebuffer(const FrameFormat &f) void Framebuffer::init() { - static Require _req(EXT_framebuffer_object); - width = 0; height = 0; - status = GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT; dirty = 0; - - if(ARB_direct_state_access) - glCreateFramebuffers(1, &id); - else - glGenFramebuffers(1, &id); -} - -Framebuffer::~Framebuffer() -{ - if(id) - glDeleteFramebuffers(1, &id); } void Framebuffer::set_format(const FrameFormat &fmt) { if(!format.empty() || !id) throw invalid_operation("Framebuffer::set_format"); - if(fmt.empty()) + if(fmt.empty() || !is_format_supported(fmt)) throw invalid_argument("Framebuffer::set_format"); - if(ARB_internalformat_query && ARB_internalformat_query2) - { - unsigned target = (fmt.get_samples()>1 ? GL_TEXTURE_2D_MULTISAMPLE : GL_TEXTURE_2D); - for(FrameAttachment a: fmt) - { - unsigned pf = get_gl_pixelformat(get_attachment_pixelformat(a)); - int supported = 0; - glGetInternalformativ(target, pf, GL_FRAMEBUFFER_RENDERABLE, 1, &supported); - if(supported!=GL_FULL_SUPPORT) - throw invalid_argument("Framebuffer::set_format"); - } - } - format = fmt; attachments.resize(format.size()); } void Framebuffer::update() const { - vector 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<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(attch.layer)), attch.tex->id, attch.level); - } - else if(ARB_direct_state_access) - glNamedFramebufferTexture(id, gl_attach_point, 0, 0); - else - glFramebufferTexture2D(GL_FRAMEBUFFER, gl_attach_point, GL_TEXTURE_2D, 0, 0); - } - - if(gl_attach_point!=GL_DEPTH_ATTACHMENT && gl_attach_point!=GL_STENCIL_ATTACHMENT) - color_bufs.push_back(gl_attach_point); - - ++i; - } - - if(color_bufs.size()>1) - static Require _req(ARB_draw_buffers); - - GLenum first_buffer = (color_bufs.empty() ? GL_NONE : color_bufs.front()); - if(ARB_direct_state_access) - { - /* ARB_direct_state_access ties the availability of these functions to - framebuffers themselves, so no further checks are needed. */ - glNamedFramebufferDrawBuffers(id, color_bufs.size(), &color_bufs[0]); - glNamedFramebufferReadBuffer(id, first_buffer); - } - else - { - if(ARB_draw_buffers) - glDrawBuffers(color_bufs.size(), &color_bufs[0]); - else if(MSP_buffer_control) - glDrawBuffer(first_buffer); - - if(MSP_buffer_control) - glReadBuffer(first_buffer); - } - - if(ARB_direct_state_access) - status = glCheckNamedFramebufferStatus(id, GL_FRAMEBUFFER); - else - status = glCheckFramebufferStatus(GL_FRAMEBUFFER); - + FramebufferBackend::update(dirty); dirty = 0; } @@ -201,27 +78,24 @@ void Framebuffer::check_size() { unsigned w = 0; unsigned h = 0; - if(a.tex->target==GL_TEXTURE_2D) + if(const Texture2D *tex2d = dynamic_cast(a.tex)) { - Texture2D *tex = static_cast(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(a.tex)) { - Texture2DMultisample *tex = static_cast(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(a.tex)) { - Texture3D *tex = static_cast(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(a.tex)) { - w = max(static_cast(a.tex)->get_size()>>a.level, 1U); + w = max(tex_cube->get_size()>>a.level, 1U); h = w; } @@ -285,13 +159,13 @@ void Framebuffer::attach(FrameAttachment attch, TextureCube &tex, TextureCubeFac void Framebuffer::attach_layered(FrameAttachment attch, Texture3D &tex, unsigned level) { - static Require _req(ARB_geometry_shader4); + require_layered(); set_attachment(make_typed_attachment(attch, tex.get_format()), tex, level, -1, 0); } void Framebuffer::attach_layered(FrameAttachment attch, TextureCube &tex, unsigned level) { - static Require _req(ARB_geometry_shader4); + require_layered(); set_attachment(make_typed_attachment(attch, tex.get_format()), tex, level, -1, 0); } @@ -332,27 +206,12 @@ void Framebuffer::require_complete() const throw framebuffer_incomplete("inconsistent layering"); } - if(status==GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT) - throw framebuffer_incomplete("incomplete or unsupported attachment"); - if(status==GL_FRAMEBUFFER_UNSUPPORTED) - throw framebuffer_incomplete("unsupported configuration"); - if(status!=GL_FRAMEBUFFER_COMPLETE) - throw framebuffer_incomplete(Msp::format("incomplete (%#x)", status)); -} - -void Framebuffer::set_debug_name(const string &name) -{ -#ifdef DEBUG - if(KHR_debug) - glObjectLabel(GL_FRAMEBUFFER, id, name.size(), name.c_str()); -#else - (void)name; -#endif + FramebufferBackend::require_complete(); } Framebuffer &Framebuffer::system() { - static Framebuffer sys_framebuf(0); + static Framebuffer sys_framebuf(true); return sys_framebuf; } diff --git a/source/core/framebuffer.h b/source/core/framebuffer.h index da828bf5..18e48f20 100644 --- a/source/core/framebuffer.h +++ b/source/core/framebuffer.h @@ -3,6 +3,7 @@ #include #include "color.h" +#include "framebuffer_backend.h" #include "frameformat.h" #include "texturecube.h" @@ -33,10 +34,9 @@ must be attached for the framebuffer to be usable. Requires the GL_EXT_framebuffer_object extension. The blit functions require the GL_EXT_framebuffer_blit extension. */ -class Framebuffer +class Framebuffer: public FramebufferBackend { - friend class Commands; - friend class PipelineState; + friend FramebufferBackend; private: struct Attachment @@ -50,15 +50,13 @@ private: void clear(); }; - unsigned id; FrameFormat format; std::vector attachments; unsigned width; unsigned height; - mutable unsigned status; mutable unsigned dirty; - Framebuffer(unsigned); + Framebuffer(bool); public: /** Creates an empty framebuffer. Format must be set before textures can be attached. */ @@ -72,9 +70,8 @@ public: private: void init(); -public: - ~Framebuffer(); +public: /** Sets the format of the framebuffer. Once the format is set, it can't be changed. */ void set_format(const FrameFormat &); @@ -111,7 +108,7 @@ public: void refresh() const { if(dirty) update(); } - void set_debug_name(const std::string &); + using FramebufferBackend::set_debug_name; static Framebuffer &system(); }; diff --git a/source/core/frameformat.cpp b/source/core/frameformat.cpp index 3568dffa..36b45f61 100644 --- a/source/core/frameformat.cpp +++ b/source/core/frameformat.cpp @@ -124,30 +124,5 @@ PixelFormat get_attachment_pixelformat(FrameAttachment fa) return make_pixelformat(comp, type); } -unsigned get_gl_attachment(FrameAttachment fa) -{ - if(get_attach_point(fa)==get_attach_point(DEPTH_ATTACHMENT)) - return GL_DEPTH_ATTACHMENT; - else if(get_attach_point(fa)==get_attach_point(STENCIL_ATTACHMENT)) - return GL_STENCIL_ATTACHMENT; - else - return GL_COLOR_ATTACHMENT0+get_attach_point(fa); -} - -unsigned get_gl_buffer_bits(const FrameFormat &format) -{ - unsigned bits = 0; - for(FrameAttachment a: format) - { - if(get_attach_point(a)==get_attach_point(DEPTH_ATTACHMENT)) - bits |= GL_DEPTH_BUFFER_BIT; - else if(get_attach_point(a)==get_attach_point(STENCIL_ATTACHMENT)) - bits |= GL_STENCIL_BUFFER_BIT; - else - bits |= GL_COLOR_BUFFER_BIT; - } - return bits; -} - } // namespace GL } // namespace Msp diff --git a/source/core/frameformat.h b/source/core/frameformat.h index c3f6eae7..90132092 100644 --- a/source/core/frameformat.h +++ b/source/core/frameformat.h @@ -76,10 +76,9 @@ inline unsigned get_attach_point(FrameAttachment fa) PixelFormat get_attachment_pixelformat(FrameAttachment); -unsigned get_gl_attachment(FrameAttachment); -unsigned get_gl_buffer_bits(const FrameFormat &); - } // namespace GL } // namespace Msp +#include "frameformat_backend.h" + #endif diff --git a/source/core/gl.h b/source/core/gl.h deleted file mode 100644 index f27b63fe..00000000 --- a/source/core/gl.h +++ /dev/null @@ -1,44 +0,0 @@ -#ifndef MSP_GL_GL_H_ -#define MSP_GL_GL_H_ - -#ifdef __APPLE__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wkeyword-macro" -#define extern extern __attribute__((weak_import)) -#include -#include -#undef extern -#pragma clang diagnostic pop -#elif defined(__ANDROID__) -#include -#include -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 -#include -#endif - -#ifndef APIENTRY -#define APIENTRY -#endif - -#endif diff --git a/source/core/pipelinestate.cpp b/source/core/pipelinestate.cpp index 78db195d..15de0b4d 100644 --- a/source/core/pipelinestate.cpp +++ b/source/core/pipelinestate.cpp @@ -1,36 +1,12 @@ #include #include -#include -#include -#include -#include -#include -#include -#include -#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 PipelineState::bound_tex_targets; -vector PipelineState::bound_uniform_blocks; -unsigned PipelineState::restart_index = 0; - PipelineState::PipelineState(): framebuffer(0), viewport(0), @@ -44,18 +20,7 @@ PipelineState::PipelineState(): stencil_test(0), blend(0), changes(0) -{ - if(bound_tex_targets.empty()) - bound_tex_targets.resize(DeviceInfo::get_global().limits.max_texture_bindings); - if(bound_uniform_blocks.empty()) - bound_uniform_blocks.resize(DeviceInfo::get_global().limits.max_uniform_bindings); -} - -PipelineState::~PipelineState() -{ - if(this==last_applied) - last_applied = 0; -} +{ } template void PipelineState::set(T &target, T value, unsigned flag) @@ -152,231 +117,6 @@ void PipelineState::set_blend(const Blend *b) set(blend, b, BLEND); } -void PipelineState::apply() const -{ - if(!last_applied) - Texture::unbind_scratch(); - - apply(this==last_applied ? changes : ~0U); -} - -void PipelineState::apply(unsigned mask) const -{ - if(mask&FRAMEBUFFER) - { - glBindFramebuffer(GL_FRAMEBUFFER, framebuffer ? framebuffer->id : 0); - if(framebuffer) - { - framebuffer->refresh(); - framebuffer->require_complete(); - } - } - - if(mask&(VIEWPORT|FRAMEBUFFER)) - { - if(viewport) - glViewport(viewport->left, viewport->bottom, viewport->width, viewport->height); - else if(framebuffer) - glViewport(0, 0, framebuffer->get_width(), framebuffer->get_height()); - } - - if(mask&SCISSOR) - { - if(scissor) - { - glEnable(GL_SCISSOR_TEST); - glScissor(scissor->left, scissor->bottom, scissor->width, scissor->height); - } - else - glDisable(GL_SCISSOR_TEST); - } - - if(mask&SHPROG) - glUseProgram(shprog ? shprog->id : 0); - - if(mask&VERTEX_SETUP) - { - glBindVertexArray(vertex_setup ? vertex_setup->id : 0); - if(vertex_setup) - { - static Require _req(MSP_primitive_restart); - - vertex_setup->refresh(); - unsigned ri = (vertex_setup->get_index_type()==UNSIGNED_INT ? 0xFFFFFFFF : 0xFFFF); - if(ri!=restart_index) - { - if(!restart_index) - glEnable(GL_PRIMITIVE_RESTART); - glPrimitiveRestartIndex(ri); - restart_index = ri; - } - } - } - - if(mask&FACE_CULL) - { - glFrontFace(front_face==CLOCKWISE ? GL_CW : GL_CCW); - - if(face_cull!=NO_CULL && front_face!=NON_MANIFOLD) - { - glEnable(GL_CULL_FACE); - glCullFace(face_cull==CULL_FRONT ? GL_FRONT : GL_BACK); - } - else - glDisable(GL_CULL_FACE); - } - - if(mask&CLIP_PLANES) - { - unsigned max_clip_planes = DeviceInfo::get_global().limits.max_clip_planes; - for(unsigned i=0; i>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(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(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; ienabled_clip_planes>>i)&1) - glDisable(GL_CLIP_PLANE0+i); - - for(unsigned i=0; i #include #include "cullface.h" +#include "pipelinestate_backend.h" namespace Msp { namespace GL { @@ -19,8 +20,10 @@ class Texture; class UniformBlock; class VertexSetup; -class PipelineState: public NonCopyable +class PipelineState: public PipelineStateBackend { + friend PipelineStateBackend; + private: struct BoundTexture { @@ -72,14 +75,8 @@ private: const Blend *blend; mutable unsigned changes; - static const PipelineState *last_applied; - static std::vector bound_tex_targets; - static std::vector bound_uniform_blocks; - static unsigned restart_index; - public: PipelineState(); - ~PipelineState(); private: template @@ -104,12 +101,6 @@ public: const VertexSetup *get_vertex_setup() const { return vertex_setup; } FaceWinding get_front_face() const { return front_face; } CullMode get_face_cull() const { return face_cull; } - - void apply() const; -private: - void apply(unsigned) const; -public: - static void clear(); }; } // namespace GL diff --git a/source/core/pixelformat.cpp b/source/core/pixelformat.cpp index 11eb4081..81379ef0 100644 --- a/source/core/pixelformat.cpp +++ b/source/core/pixelformat.cpp @@ -1,10 +1,3 @@ -#include -#include -#include -#include -#include -#include -#include #include #include "pixelformat.h" @@ -117,97 +110,5 @@ PixelFormat make_pixelformat(PixelComponents comp, DataType type, bool srgb) return static_cast(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 diff --git a/source/core/pixelformat.h b/source/core/pixelformat.h index 65331c99..71a8bd4c 100644 --- a/source/core/pixelformat.h +++ b/source/core/pixelformat.h @@ -98,10 +98,9 @@ inline unsigned get_pixel_size(PixelFormat f) { return get_component_count(f)*ge void require_pixelformat(PixelFormat); -unsigned get_gl_components(PixelComponents); -unsigned get_gl_pixelformat(PixelFormat); - } // namespace GL } // namespace Msp +#include "pixelformat_backend.h" + #endif diff --git a/source/core/predicate.cpp b/source/core/predicate.cpp index 7b0c0587..6337e174 100644 --- a/source/core/predicate.cpp +++ b/source/core/predicate.cpp @@ -7,22 +7,6 @@ using namespace std; namespace Msp { namespace GL { -unsigned get_gl_predicate(Predicate pred) -{ - switch(pred) - { - case NEVER: return GL_NEVER; - case ALWAYS: return GL_ALWAYS; - case LESS: return GL_LESS; - case LEQUAL: return GL_LEQUAL; - case EQUAL: return GL_EQUAL; - case GREATER: return GL_GREATER; - case GEQUAL: return GL_GEQUAL; - case NOTEQUAL: return GL_NOTEQUAL; - default: throw invalid_argument("get_gl_predicate"); - } -} - void operator>>(const LexicalConverter &conv, Predicate &pred) { const string &str = conv.get(); diff --git a/source/core/predicate.h b/source/core/predicate.h index 079ef63e..6b0a697b 100644 --- a/source/core/predicate.h +++ b/source/core/predicate.h @@ -18,12 +18,12 @@ enum Predicate NOTEQUAL }; -unsigned get_gl_predicate(Predicate); - void operator>>(const LexicalConverter &, Predicate &); void operator<<(LexicalConverter &, Predicate); } // namespace GL } // namespace Msp +#include "predicate_backend.h" + #endif diff --git a/source/core/primitivetype.cpp b/source/core/primitivetype.cpp index 416ebae2..ca03a67e 100644 --- a/source/core/primitivetype.cpp +++ b/source/core/primitivetype.cpp @@ -7,21 +7,6 @@ using namespace std; namespace Msp { namespace GL { -unsigned get_gl_primitive_type(PrimitiveType pt) -{ - switch(pt) - { - case POINTS: return GL_POINTS; - case LINES: return GL_LINES; - case LINE_STRIP: return GL_LINE_STRIP; - case LINE_LOOP: return GL_LINE_LOOP; - case TRIANGLES: return GL_TRIANGLES; - case TRIANGLE_STRIP: return GL_TRIANGLE_STRIP; - case TRIANGLE_FAN: return GL_TRIANGLE_FAN; - default: throw invalid_argument("get_gl_primitive_type"); - } -} - void operator>>(const LexicalConverter &conv, PrimitiveType &pt) { if(conv.get()=="POINTS") diff --git a/source/core/primitivetype.h b/source/core/primitivetype.h index cbcb187c..4b6a1a3e 100644 --- a/source/core/primitivetype.h +++ b/source/core/primitivetype.h @@ -17,11 +17,11 @@ enum PrimitiveType TRIANGLE_FAN }; -unsigned get_gl_primitive_type(PrimitiveType); - void operator>>(const LexicalConverter &, PrimitiveType &); } // namespace GL } // namespace Msp +#include "primitivetype_backend.h" + #endif diff --git a/source/core/program.cpp b/source/core/program.cpp index b1516e2d..6458b477 100644 --- a/source/core/program.cpp +++ b/source/core/program.cpp @@ -1,70 +1,17 @@ -#include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include "error.h" #include "program.h" -#include "glsl/compiler.h" using namespace std; -namespace { - -template -void uniform_wrapper(unsigned index, unsigned count, const void *data) -{ - func(index, count, static_cast(data)); -} - -template -void uniform_matrix_wrapper(unsigned index, unsigned count, const void *data) -{ - func(index, count, false, static_cast(data)); -} - -} - namespace Msp { namespace GL { -Program::Program() -{ - init(); -} - Program::Program(const Module &mod, const map &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 &spec_values) { if(has_stages()) @@ -83,215 +30,15 @@ void Program::add_stages(const Module &mod, const map &spec_values) throw invalid_argument("Program::add_stages"); } - finalize(mod, transient); -} - -bool Program::has_stages() const -{ - for(unsigned i=0; i &spec_values, TransientData &transient) -{ - SL::Compiler compiler; - compiler.set_source(mod.get_prepared_source(), ""); - 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 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 &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 &code = mod.get_code(); - glShaderBinary(n_stages, used_stage_ids, GL_SHADER_BINARY_FORMAT_SPIR_V, &code[0], code.size()*4); - - const vector &spec_consts = mod.get_spec_constants(); - vector spec_id_array; - vector 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; iname.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(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; isecond); - reflect_data.uniform_blocks[i].bind_point = j->second; - } - } - - if(!ARB_separate_shader_objects) - glUseProgram(id); - for(const auto &kvp: transient.textures) - { - int location = get_uniform_location(kvp.first); - if(location>=0) - { - if(ARB_separate_shader_objects) - glProgramUniform1i(id, location, kvp.second); - else - glUniform1i(location, kvp.second); - } - } + apply_bindings(transient); } else if(mod.get_format()==Module::SPIR_V) { @@ -305,212 +52,6 @@ void Program::finalize(const Module &mod, const TransientData &transient) require_type(a.type); } -void Program::query_uniforms() -{ - unsigned count = 0; - glGetProgramiv(id, GL_ACTIVE_UNIFORMS, reinterpret_cast(&count)); - reflect_data.uniforms.reserve(count); - vector uniform_names(count); - for(unsigned i=0; i3 && !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 uniforms_by_index(count); - for(unsigned i=0; i=0) - { - UniformCall::FuncPtr func = 0; - if(u.type==FLOAT) - func = &uniform_wrapper; - else if(u.type==FLOAT_VEC2) - func = &uniform_wrapper; - else if(u.type==FLOAT_VEC3) - func = &uniform_wrapper; - else if(u.type==FLOAT_VEC4) - func = &uniform_wrapper; - else if(u.type==INT) - func = &uniform_wrapper; - else if(u.type==INT_VEC2) - func = &uniform_wrapper; - else if(u.type==INT_VEC3) - func = &uniform_wrapper; - else if(u.type==INT_VEC4) - func = &uniform_wrapper; - else if(u.type==FLOAT_MAT2) - func = &uniform_matrix_wrapper; - else if(u.type==FLOAT_MAT3) - func = &uniform_matrix_wrapper; - else if(u.type==FLOAT_MAT4) - func = &uniform_matrix_wrapper; - else if(u.type==FLOAT_MAT2x3) - func = &uniform_matrix_wrapper; - else if(u.type==FLOAT_MAT3x2) - func = &uniform_matrix_wrapper; - else if(u.type==FLOAT_MAT2x4) - func = &uniform_matrix_wrapper; - else if(u.type==FLOAT_MAT4x2) - func = &uniform_matrix_wrapper; - else if(u.type==FLOAT_MAT3x4) - func = &uniform_matrix_wrapper; - else if(u.type==FLOAT_MAT4x3) - func = &uniform_matrix_wrapper; - 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 &uniforms_by_index) -{ - unsigned count = 0; - glGetProgramiv(id, GL_ACTIVE_UNIFORM_BLOCKS, reinterpret_cast(&count)); - // Reserve an extra index for the default block - reflect_data.uniform_blocks.reserve(count+1); - for(unsigned i=0; i 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 query_indices(indices.begin(), indices.end()); - vector values(indices.size()); - glGetActiveUniformsiv(id, query_indices.size(), &query_indices[0], GL_UNIFORM_OFFSET, &values[0]); - for(unsigned j=0; joffset = 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; jarray_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; jmatrix_stride = values[j]; - } - - info.sort_uniforms(); - info.update_layout_hash(); - } -} - -void Program::query_attributes() -{ - unsigned count = 0; - glGetProgramiv(id, GL_ACTIVE_ATTRIBUTES, reinterpret_cast(&count)); - reflect_data.attributes.reserve(count); - for(unsigned i=0; i3 && !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 &spec_values) { // Prepare the default block @@ -687,33 +228,6 @@ int Program::get_attribute_location(const string &name) const return i!=reflect_data.attributes.end() && i->name==name ? i->location : -1; } -void Program::set_debug_name(const string &name) -{ -#ifdef DEBUG - debug_name = name; - if(KHR_debug) - { - glObjectLabel(GL_PROGRAM, id, name.size(), name.c_str()); - for(unsigned i=0; i(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(p, &c) diff --git a/source/core/program.h b/source/core/program.h index 41c5897d..f41a7d6d 100644 --- a/source/core/program.h +++ b/source/core/program.h @@ -6,6 +6,7 @@ #include #include #include "module.h" +#include "program_backend.h" #include "reflectdata.h" namespace Msp { @@ -15,9 +16,9 @@ namespace GL { A complete shader program. Programs can be assembled of individual Shaders or generated with a set of standard features. */ -class Program +class Program: public ProgramBackend { - friend class PipelineState; + friend ProgramBackend; public: class Loader: public DataFile::CollectionObjectLoader @@ -47,63 +48,17 @@ private: void specialize_int(const std::string &, int); }; - enum Stage - { - VERTEX, - GEOMETRY, - FRAGMENT, - MAX_STAGES - }; - - struct TransientData - { - std::map textures; - std::map blocks; - std::map 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 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::map()); -private: - void init(); -public: - virtual ~Program(); - void add_stages(const Module &, const std::map & = std::map()); private: - bool has_stages() const; - unsigned add_stage(Stage); - void add_glsl_stages(const GlslModule &, const std::map &, TransientData &); - void compile_glsl_stage(const GlslModule &, unsigned); - void add_spirv_stages(const SpirVModule &, const std::map &, TransientData &); - - void finalize(const Module &, const TransientData &); - void query_uniforms(); - void query_uniform_blocks(const std::vector &); - void query_attributes(); void collect_uniforms(const SpirVModule &, const std::map &); void collect_block_uniforms(const SpirVModule::Structure &, const std::string &, unsigned, const std::map &, std::vector &); void collect_attributes(const SpirVModule &); @@ -122,9 +77,7 @@ public: const ReflectData::AttributeInfo &get_attribute_info(const std::string &) const; int get_attribute_location(const std::string &) const; - void set_debug_name(const std::string &); -private: - void set_stage_debug_name(unsigned, Stage); + using ProgramBackend::set_debug_name; }; } // namespace GL diff --git a/source/core/query.cpp b/source/core/query.cpp index a6d77eda..f075cada 100644 --- a/source/core/query.cpp +++ b/source/core/query.cpp @@ -1,50 +1,16 @@ -#include -#include -#include #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(sold_size) - glGenQueries(s-old_size, queries.data()+old_size); -} - -unsigned QueryPool::get_result(unsigned i) const -{ - unsigned result = 0; - glGetQueryObjectuiv(queries[i], GL_QUERY_RESULT, &result); - return result; -} - QueryPool::Activate::Activate(Renderer &r, const QueryPool &p, unsigned i): renderer(r), @@ -59,15 +25,5 @@ QueryPool::Activate::~Activate() renderer.end_query(pool, index); } - -unsigned get_gl_query_type(QueryType t) -{ - switch(t) - { - case OCCLUSION_QUERY: return GL_ANY_SAMPLES_PASSED; - default: throw invalid_argument("get_gl_query_type"); - } -} - } // namespace GL } // namespace Msp diff --git a/source/core/query.h b/source/core/query.h index aaa993e3..f6b0a2e5 100644 --- a/source/core/query.h +++ b/source/core/query.h @@ -1,8 +1,8 @@ #ifndef MSP_GL_QUERY_H_ #define MSP_GL_QUERY_H_ -#include #include +#include "query_backend.h" namespace Msp { namespace GL { @@ -14,9 +14,9 @@ enum QueryType OCCLUSION_QUERY }; -class QueryPool: public Msp::NonCopyable +class QueryPool: public QueryPoolBackend, public Msp::NonCopyable { - friend class Commands; + friend QueryPoolBackend; public: class Activate @@ -33,21 +33,16 @@ public: private: QueryType type; - unsigned gl_type; - std::vector 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 diff --git a/source/core/sampler.cpp b/source/core/sampler.cpp index 5ea10c43..6f4770ac 100644 --- a/source/core/sampler.cpp +++ b/source/core/sampler.cpp @@ -1,9 +1,3 @@ -#include -#include -#include -#include -#include -#include #include #include "error.h" #include "sampler.h" @@ -24,39 +18,11 @@ Sampler::Sampler(): compare(false), cmp_func(LEQUAL), dirty_params(0) -{ - Require _req(ARB_sampler_objects); - Require _req2(EXT_texture3D); - Require _req3(ARB_shadow); - if(ARB_direct_state_access) - glCreateSamplers(1, &id); - else - glGenSamplers(1, &id); -} +{ } void Sampler::update() const { - if(dirty_params&MIN_FILTER) - glSamplerParameteri(id, GL_TEXTURE_MIN_FILTER, get_gl_filter(min_filter)); - if(dirty_params&MAG_FILTER) - glSamplerParameteri(id, GL_TEXTURE_MAG_FILTER, get_gl_filter(mag_filter)); - if(dirty_params&MAX_ANISOTROPY) - glSamplerParameterf(id, GL_TEXTURE_MAX_ANISOTROPY_EXT, max_anisotropy); - if(dirty_params&WRAP_S) - glSamplerParameteri(id, GL_TEXTURE_WRAP_S, get_gl_wrap(wrap_s)); - if(dirty_params&WRAP_T) - glSamplerParameteri(id, GL_TEXTURE_WRAP_T, get_gl_wrap(wrap_t)); - if(dirty_params&WRAP_R) - glSamplerParameteri(id, GL_TEXTURE_WRAP_R, get_gl_wrap(wrap_r)); - if(dirty_params&BORDER_COLOR) - glSamplerParameterfv(id, GL_TEXTURE_BORDER_COLOR, &border_color.r); - if(dirty_params&COMPARE) - { - glSamplerParameteri(id, GL_TEXTURE_COMPARE_MODE, (compare ? GL_COMPARE_R_TO_TEXTURE : GL_NONE)); - if(compare) - glSamplerParameteri(id, GL_TEXTURE_COMPARE_FUNC, get_gl_predicate(cmp_func)); - } - + SamplerBackend::update(dirty_params); dirty_params = 0; } @@ -82,10 +48,9 @@ void Sampler::set_max_anisotropy(float a) { if(a<1.0f) throw invalid_argument("Sampler::set_max_anisotropy"); - else if(a>1.0f) - static Require _req(EXT_texture_filter_anisotropic); + bool supported = check_anisotropic(a>1.0f); max_anisotropy = a; - if(EXT_texture_filter_anisotropic) + if(supported) dirty_params |= MAX_ANISOTROPY; } @@ -133,16 +98,6 @@ void Sampler::set_compare(Predicate f) dirty_params |= COMPARE; } -void Sampler::set_debug_name(const string &name) -{ -#ifdef DEBUG - if(id && KHR_debug) - glObjectLabel(GL_SAMPLER, id, name.size(), name.c_str()); -#else - (void)name; -#endif -} - Sampler::Loader::Loader(Sampler &s): DataFile::ObjectLoader(s) @@ -216,32 +171,6 @@ bool is_mipmapped(TextureFilter filter) filter==LINEAR_MIPMAP_NEAREST || filter==LINEAR_MIPMAP_LINEAR); } -unsigned get_gl_filter(TextureFilter filter) -{ - switch(filter) - { - case NEAREST: return GL_NEAREST; - case LINEAR: return GL_LINEAR; - case NEAREST_MIPMAP_NEAREST: return GL_NEAREST_MIPMAP_NEAREST; - case NEAREST_MIPMAP_LINEAR: return GL_NEAREST_MIPMAP_LINEAR; - case LINEAR_MIPMAP_NEAREST: return GL_LINEAR_MIPMAP_NEAREST; - case LINEAR_MIPMAP_LINEAR: return GL_LINEAR_MIPMAP_LINEAR; - default: throw invalid_argument("get_gl_filter"); - } -} - -unsigned get_gl_wrap(TextureWrap wrap) -{ - switch(wrap) - { - case REPEAT: return GL_REPEAT; - case CLAMP_TO_EDGE: return GL_CLAMP_TO_EDGE; - case CLAMP_TO_BORDER: return GL_CLAMP_TO_BORDER; - case MIRRORED_REPEAT: return GL_MIRRORED_REPEAT; - default: throw invalid_argument("get_gl_wrap"); - } -} - void operator>>(const LexicalConverter &c, TextureFilter &tf) { if(c.get()=="NEAREST") diff --git a/source/core/sampler.h b/source/core/sampler.h index b2011d3b..bd8db31f 100644 --- a/source/core/sampler.h +++ b/source/core/sampler.h @@ -4,6 +4,7 @@ #include #include "color.h" #include "predicate.h" +#include "sampler_backend.h" namespace Msp { namespace GL { @@ -60,9 +61,9 @@ for magnification and NEAREST_MIPMAP_LINEAR for minification. If texture coordinates fall outside of the principal range of the texture, wrapping is applied. The default for all directions is REPEAT. */ -class Sampler +class Sampler: public SamplerBackend { - friend class PipelineState; + friend SamplerBackend; public: class Loader: public DataFile::ObjectLoader @@ -97,7 +98,6 @@ private: COMPARE = 128 }; - unsigned id; TextureFilter min_filter; TextureFilter mag_filter; float max_anisotropy; @@ -153,13 +153,11 @@ public: void refresh() const { if(dirty_params) update(); } - void set_debug_name(const std::string &); + using SamplerBackend::set_debug_name; }; bool is_mipmapped(TextureFilter); -unsigned get_gl_filter(TextureFilter); -unsigned get_gl_wrap(TextureWrap); void operator>>(const LexicalConverter &, TextureFilter &); void operator>>(const LexicalConverter &, TextureWrap &); diff --git a/source/core/stenciltest.cpp b/source/core/stenciltest.cpp index 57a191cf..2e8bbb4d 100644 --- a/source/core/stenciltest.cpp +++ b/source/core/stenciltest.cpp @@ -40,22 +40,6 @@ void StencilTest::Loader::actions(StencilOp sf, StencilOp df, StencilOp dp) } -unsigned get_gl_stencil_op(StencilOp op) -{ - switch(op) - { - case KEEP: return GL_KEEP; - case SET_ZERO: return GL_ZERO; - case REPLACE: return GL_REPLACE; - case INCR: return GL_INCR; - case DECR: return GL_DECR; - case INVERT: return GL_INVERT; - case INCR_WRAP: return GL_INCR_WRAP; - case DECR_WRAP: return GL_DECR_WRAP; - default: throw invalid_argument("get_gl_stencil_op"); - } -} - void operator>>(const LexicalConverter &conv, StencilOp &op) { const string &str = conv.get(); diff --git a/source/core/stenciltest.h b/source/core/stenciltest.h index e33882df..652f8d40 100644 --- a/source/core/stenciltest.h +++ b/source/core/stenciltest.h @@ -47,12 +47,12 @@ struct StencilTest StencilTest(); }; -unsigned get_gl_stencil_op(StencilOp); - void operator>>(const LexicalConverter &, StencilOp &); void operator<<(LexicalConverter &, StencilOp); } // namespace GL } // namespace Msp +#include "stenciltest_backend.h" + #endif diff --git a/source/core/texture.cpp b/source/core/texture.cpp index c27e8506..4968e2e6 100644 --- a/source/core/texture.cpp +++ b/source/core/texture.cpp @@ -1,7 +1,3 @@ -#include -#include -#include -#include #include #include "error.h" #include "resourcemanager.h" @@ -12,19 +8,8 @@ using namespace std; namespace Msp { namespace GL { -const int Texture::swizzle_orders[] = -{ - GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA, - GL_RED, GL_RED, GL_RED, GL_ONE, - GL_RED, GL_RED, GL_RED, GL_GREEN, - GL_BLUE, GL_GREEN, GL_RED, GL_ALPHA -}; - -Texture *Texture::scratch_binding = 0; - -Texture::Texture(GLenum t, ResourceManager *m): - id(0), - target(t), +Texture::Texture(unsigned t, ResourceManager *m): + TextureBackend(t, !m), format(NO_PIXELFORMAT), storage_fmt(format), swizzle(NO_SWIZZLE), @@ -33,38 +18,6 @@ Texture::Texture(GLenum t, ResourceManager *m): { if(m) set_manager(m); - else - generate_id(); - - static bool alignment_init = false; - if(!alignment_init) - { - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - alignment_init = true; - } -} - -Texture::~Texture() -{ - if(this==scratch_binding) - unbind_scratch(); - if(id) - glDeleteTextures(1, &id); -} - -void Texture::generate_id() -{ - if(id) - throw invalid_operation("Texture::generate_id"); - if(ARB_direct_state_access) - glCreateTextures(target, 1, &id); - else - glGenTextures(1, &id); - -#ifdef DEBUG - if(!debug_name.empty() && KHR_debug) - glObjectLabel(GL_TEXTURE, id, debug_name.size(), debug_name.c_str()); -#endif } void Texture::set_format(PixelFormat fmt) @@ -96,56 +49,13 @@ void Texture::set_format(PixelFormat fmt) PixelFormat st_fmt = make_pixelformat(st_comp, get_component_type(fmt), is_srgb(fmt)); require_pixelformat(st_fmt); if(swiz!=NO_SWIZZLE) - static Require _req(ARB_texture_swizzle); + require_swizzle(); format = fmt; storage_fmt = st_fmt; swizzle = swiz; } -void Texture::apply_swizzle() -{ - if(swizzle==NO_SWIZZLE) - return; - - if(get_backend_api()==OPENGL_ES) - { - set_parameter_i(GL_TEXTURE_SWIZZLE_R, swizzle_orders[swizzle*4]); - set_parameter_i(GL_TEXTURE_SWIZZLE_G, swizzle_orders[swizzle*4+1]); - set_parameter_i(GL_TEXTURE_SWIZZLE_B, swizzle_orders[swizzle*4+2]); - set_parameter_i(GL_TEXTURE_SWIZZLE_A, swizzle_orders[swizzle*4+3]); - } - else - { - if(ARB_direct_state_access) - glTextureParameteriv(id, GL_TEXTURE_SWIZZLE_RGBA, swizzle_orders+swizzle*4); - else - glTexParameteriv(target, GL_TEXTURE_SWIZZLE_RGBA, swizzle_orders+swizzle*4); - } -} - -void Texture::set_parameter_i(unsigned param, int value) const -{ - if(ARB_direct_state_access) - glTextureParameteri(id, param, value); - else - glTexParameteri(target, param, value); -} - -void Texture::generate_mipmap() -{ - // glGenerateMipmap is defined here - static Require _req(EXT_framebuffer_object); - - if(ARB_direct_state_access) - glGenerateTextureMipmap(id); - else - { - bind_scratch(); - glGenerateMipmap(target); - } -} - void Texture::load_image(const string &fn, unsigned lv) { Graphics::Image img; @@ -154,39 +64,6 @@ void Texture::load_image(const string &fn, unsigned lv) image(img, lv); } -void Texture::set_debug_name(const string &name) -{ -#ifdef DEBUG - debug_name = name; - if(id && KHR_debug) - glObjectLabel(GL_TEXTURE, id, name.size(), name.c_str()); -#else - (void)name; -#endif -} - -void Texture::bind_scratch() -{ - if(!scratch_binding) - glActiveTexture(GL_TEXTURE0); - if(scratch_binding!=this) - { - if(scratch_binding && scratch_binding->target!=target) - glBindTexture(scratch_binding->target, 0); - glBindTexture(target, id); - scratch_binding = this; - } -} - -void Texture::unbind_scratch() -{ - if(scratch_binding) - { - glBindTexture(scratch_binding->target, 0); - scratch_binding = 0; - } -} - Texture::Loader::Loader(Texture &t): DataFile::CollectionObjectLoader(t, 0) diff --git a/source/core/texture.h b/source/core/texture.h index 47b2f269..6073cdcb 100644 --- a/source/core/texture.h +++ b/source/core/texture.h @@ -5,6 +5,7 @@ #include #include "pixelformat.h" #include "resource.h" +#include "texture_backend.h" namespace Msp { namespace GL { @@ -18,10 +19,10 @@ A texture can consinst of a stack of images, called a mipmap. The dimensions of each mipmap level are half that of the previous level. The mipmap stack can be used for texture minification; see the Sampler class for details. */ -class Texture: public Resource +class Texture: public TextureBackend, public Resource { - friend class Framebuffer; - friend class PipelineState; + friend TextureBackend; + protected: class Loader: public DataFile::CollectionObjectLoader { @@ -56,34 +57,20 @@ protected: RGB_TO_BGR }; - unsigned id; - unsigned target; PixelFormat format; PixelFormat storage_fmt; FormatSwizzle swizzle; bool use_srgb_format; bool auto_gen_mipmap; - std::string debug_name; - - static const int swizzle_orders[]; - static Texture *scratch_binding; Texture(unsigned, ResourceManager * = 0); - Texture(const Texture &); - Texture &operator=(const Texture &); -public: - ~Texture(); -protected: - void generate_id(); void set_format(PixelFormat); - void apply_swizzle(); - void set_parameter_i(unsigned, int) const; public: PixelFormat get_format() const { return format; } - void generate_mipmap(); + using TextureBackend::generate_mipmap; /// Loads a Graphics::Image from a file and uploads it to the texture. virtual void load_image(const std::string &, unsigned = 0); @@ -95,12 +82,7 @@ public: virtual std::uint64_t get_data_size() const { return 0; } - void set_debug_name(const std::string &); - -protected: - void bind_scratch(); -public: - static void unbind_scratch(); + using TextureBackend::set_debug_name; }; } // namespace GL diff --git a/source/core/texture1d.cpp b/source/core/texture1d.cpp index 76ff946f..1733a2d8 100644 --- a/source/core/texture1d.cpp +++ b/source/core/texture1d.cpp @@ -1,6 +1,3 @@ -#include -#include -#include #include "error.h" #include "texture1d.h" @@ -10,11 +7,8 @@ namespace Msp { namespace GL { Texture1D::Texture1D(): - Texture(GL_TEXTURE_1D), width(0) -{ - static Require _req(MSP_texture1D); -} +{ } void Texture1D::storage(PixelFormat fmt, unsigned wd, unsigned lv) { @@ -33,28 +27,7 @@ void Texture1D::storage(PixelFormat fmt, unsigned wd, unsigned lv) if(lv) levels = min(levels, lv); - GLenum gl_fmt = get_gl_pixelformat(storage_fmt); - if(ARB_texture_storage) - { - if(ARB_direct_state_access) - glTextureStorage1D(id, levels, gl_fmt, width); - else - { - bind_scratch(); - glTexStorage1D(target, levels, gl_fmt, width); - } - } - else - { - bind_scratch(); - glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, levels-1); - GLenum comp = get_gl_components(get_components(storage_fmt)); - GLenum type = get_gl_type(get_component_type(storage_fmt)); - for(unsigned i=0; i=levels) 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) diff --git a/source/core/texture1d.h b/source/core/texture1d.h index e77f6484..1871a17c 100644 --- a/source/core/texture1d.h +++ b/source/core/texture1d.h @@ -2,13 +2,15 @@ #define MSP_GL_TEXTURE1D_H_ #include -#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 { diff --git a/source/core/texture2d.cpp b/source/core/texture2d.cpp index 70a9528d..04ad6ed8 100644 --- a/source/core/texture2d.cpp +++ b/source/core/texture2d.cpp @@ -1,8 +1,3 @@ -#include -#include -#include -#include -#include "buffer.h" #include "error.h" #include "texture2d.h" @@ -11,29 +6,8 @@ using namespace std; namespace Msp { namespace GL { -class Texture2D::AsyncLoader: public Resource::AsyncLoader -{ -private: - Texture2D &texture; - IO::Seekable &io; - Buffer pixel_buffer; - char *mapped_address; - Graphics::Image image; - Graphics::ImageLoader *img_loader; - unsigned n_bytes; - int phase; - -public: - AsyncLoader(Texture2D &, IO::Seekable &); - ~AsyncLoader(); - - virtual bool needs_sync() const; - virtual bool process(); -}; - - Texture2D::Texture2D(ResourceManager *m): - Texture(GL_TEXTURE_2D, m), + Texture2DBackend(m), width(0), height(0) { } @@ -61,31 +35,7 @@ void Texture2D::storage(PixelFormat fmt, unsigned wd, unsigned ht, unsigned lv) if(lv>0) levels = min(levels, lv); - GLenum gl_fmt = get_gl_pixelformat(storage_fmt); - if(ARB_texture_storage) - { - if(ARB_direct_state_access) - glTextureStorage2D(id, levels, gl_fmt, width, height); - else - { - bind_scratch(); - glTexStorage2D(target, levels, gl_fmt, width, height); - } - } - else - { - bind_scratch(); - glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, levels-1); - GLenum comp = get_gl_components(get_components(storage_fmt)); - GLenum type = get_gl_type(get_component_type(storage_fmt)); - for(unsigned i=0; i lv_size = get_level_size(i); - glTexImage2D(target, i, gl_fmt, lv_size.x, lv_size.y, 0, comp, type, 0); - } - } - - apply_swizzle(); + allocate(); } void Texture2D::image(unsigned level, const void *data) @@ -101,22 +51,7 @@ void Texture2D::sub_image(unsigned level, int x, int y, unsigned wd, unsigned ht if(level>=levels) throw out_of_range("Texture2D::sub_image"); - GLenum comp = get_gl_components(get_components(storage_fmt)); - GLenum type = get_gl_type(get_component_type(storage_fmt)); - if(ARB_direct_state_access) - glTextureSubImage2D(id, level, x, y, wd, ht, comp, type, data); - else - { - bind_scratch(); - glTexSubImage2D(target, level, x, y, wd, ht, comp, type, data); - } -} - -void Texture2D::sub_image(unsigned level, int x, int y, unsigned wd, unsigned ht, const Buffer &buffer, unsigned offset) -{ - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, buffer.id); - sub_image(level, x, y, wd, ht, reinterpret_cast(offset)); - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + Texture2DBackend::sub_image(level, x, y, wd, ht, data); } void Texture2D::image(const Graphics::Image &img, unsigned lv) @@ -147,8 +82,7 @@ LinAl::Vector Texture2D::get_level_size(unsigned level) const Resource::AsyncLoader *Texture2D::load(IO::Seekable &io, const Resources *) { - AsyncLoader *ldr = new AsyncLoader(*this, io); - return ldr; + return create_async_loader(io); } uint64_t Texture2D::get_data_size() const @@ -156,12 +90,6 @@ uint64_t Texture2D::get_data_size() const return id ? width*height*get_pixel_size(format) : 0; } -void Texture2D::unload() -{ - glDeleteTextures(1, &id); - id = 0; -} - Texture2D::Loader::Loader(Texture2D &t): DataFile::DerivedObjectLoader(t) @@ -203,65 +131,5 @@ void Texture2D::Loader::storage_levels(PixelFormat fmt, unsigned w, unsigned h, obj.storage(fmt, w, h, l); } - -Texture2D::AsyncLoader::AsyncLoader(Texture2D &t, IO::Seekable &i): - texture(t), - io(i), - mapped_address(0), - img_loader(Graphics::ImageLoader::open_io(io)), - phase(0) -{ } - -Texture2D::AsyncLoader::~AsyncLoader() -{ - if(mapped_address) - pixel_buffer.unmap(); - delete img_loader; -} - -bool Texture2D::AsyncLoader::needs_sync() const -{ - return phase%2; -} - -bool Texture2D::AsyncLoader::process() -{ - if(phase==0) - { - image.load_headers(*img_loader); - n_bytes = image.get_stride()*image.get_height(); - } - else if(phase==1) - { - pixel_buffer.storage(n_bytes); - mapped_address = reinterpret_cast(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 diff --git a/source/core/texture2d.h b/source/core/texture2d.h index 0ef4fc1f..a2454b76 100644 --- a/source/core/texture2d.h +++ b/source/core/texture2d.h @@ -3,20 +3,20 @@ #include #include -#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 { @@ -32,8 +32,6 @@ public: }; private: - class AsyncLoader; - unsigned width; unsigned height; unsigned levels; @@ -59,10 +57,6 @@ public: and the update region must be fully inside the texture. */ void sub_image(unsigned level, int x, int y, unsigned wd, unsigned ht, const void *data); -private: - void sub_image(unsigned, int, int, unsigned, unsigned, const Buffer &, unsigned); - -public: /** Updates the contents of the entire texture from an image. If storage has not been defined, it will be set to match the image. Otherwise the image must match the defined storage. */ @@ -80,7 +74,7 @@ private: public: virtual Resource::AsyncLoader *load(IO::Seekable &, const Resources * = 0); virtual std::uint64_t get_data_size() const; - virtual void unload(); + using Texture2DBackend::unload; }; } // namespace GL diff --git a/source/core/texture2darray.cpp b/source/core/texture2darray.cpp index 1c7d363e..52f5fd71 100644 --- a/source/core/texture2darray.cpp +++ b/source/core/texture2darray.cpp @@ -1,4 +1,3 @@ -#include #include "error.h" #include "texture2darray.h" @@ -7,12 +6,6 @@ using namespace std; namespace Msp { namespace GL { -Texture2DArray::Texture2DArray(): - Texture3D(GL_TEXTURE_2D_ARRAY) -{ - static Require _req(EXT_texture_array); -} - void Texture2DArray::layer_image(unsigned level, unsigned z, const void *data) { if(level>=levels || z>=depth) diff --git a/source/core/texture2darray.h b/source/core/texture2darray.h index 21930f88..662fbad8 100644 --- a/source/core/texture2darray.h +++ b/source/core/texture2darray.h @@ -1,7 +1,7 @@ #ifndef MSP_GL_TEXTURE2DARRAY_H_ #define MSP_GL_TEXTURE2DARRAY_H_ -#include "texture3d.h" +#include "texture2darray_backend.h" namespace Msp { namespace GL { @@ -11,7 +11,7 @@ An array of two-dimensional textures. It's very much like a 3D texture, with two important differences: there's no filtering nor mipmapping along the third dimension. */ -class Texture2DArray: public Texture3D +class Texture2DArray: public Texture2DArrayBackend { public: class Loader: public Msp::DataFile::DerivedObjectLoader @@ -25,8 +25,6 @@ public: void external_image(unsigned, const std::string &); }; - Texture2DArray(); - void layer_image(unsigned, unsigned, const void *); void layer_image(unsigned, unsigned, const Graphics::Image &); diff --git a/source/core/texture2dmultisample.cpp b/source/core/texture2dmultisample.cpp index 782a5569..5ea37e90 100644 --- a/source/core/texture2dmultisample.cpp +++ b/source/core/texture2dmultisample.cpp @@ -1,6 +1,3 @@ -#include -#include -#include #include "deviceinfo.h" #include "error.h" #include "texture2dmultisample.h" @@ -11,12 +8,9 @@ namespace Msp { namespace GL { Texture2DMultisample::Texture2DMultisample(): - Texture(GL_TEXTURE_2D_MULTISAMPLE), width(0), height(0) -{ - static Require _req(ARB_texture_multisample); -} +{ } void Texture2DMultisample::storage(PixelFormat fmt, unsigned wd, unsigned ht, unsigned sm) { @@ -36,23 +30,7 @@ void Texture2DMultisample::storage(PixelFormat fmt, unsigned wd, unsigned ht, un height = ht; samples = sm; - GLenum gl_fmt = get_gl_pixelformat(storage_fmt); - if(ARB_texture_storage_multisample) - { - if(ARB_direct_state_access) - glTextureStorage2DMultisample(id, samples, gl_fmt, width, height, false); - else - { - bind_scratch(); - glTexStorage2DMultisample(target, samples, gl_fmt, width, height, false); - } - } - else - { - bind_scratch(); - glTexImage2DMultisample(target, samples, gl_fmt, width, height, false); - } - apply_swizzle(); + allocate(); } void Texture2DMultisample::image(const Graphics::Image &, unsigned) diff --git a/source/core/texture2dmultisample.h b/source/core/texture2dmultisample.h index 94323eca..9710ad78 100644 --- a/source/core/texture2dmultisample.h +++ b/source/core/texture2dmultisample.h @@ -1,13 +1,15 @@ #ifndef MSP_GL_TEXTURE2DMULTISAMPLE_H_ #define MSP_GL_TEXTURE2DMULTISAMPLE_H_ -#include "texture.h" +#include "texture2dmultisample_backend.h" namespace Msp { namespace GL { -class Texture2DMultisample: public Texture +class Texture2DMultisample: public Texture2DMultisampleBackend { + friend Texture2DMultisampleBackend; + private: unsigned width; unsigned height; diff --git a/source/core/texture3d.cpp b/source/core/texture3d.cpp index c35d4574..0fb26ccd 100644 --- a/source/core/texture3d.cpp +++ b/source/core/texture3d.cpp @@ -1,8 +1,4 @@ #include -#include -#include -#include -#include #include "error.h" #include "texture3d.h" @@ -11,21 +7,18 @@ using namespace std; namespace Msp { namespace GL { -Texture3D::Texture3D(GLenum t): - Texture(t), +Texture3D::Texture3D(unsigned t): + Texture3DBackend(t), width(0), height(0), depth(0) { } Texture3D::Texture3D(): - Texture(GL_TEXTURE_3D), width(0), height(0), depth(0) -{ - static Require _req(EXT_texture3D); -} +{ } void Texture3D::storage(PixelFormat fmt, unsigned wd, unsigned ht, unsigned dp, unsigned lv) { @@ -46,31 +39,7 @@ void Texture3D::storage(PixelFormat fmt, unsigned wd, unsigned ht, unsigned dp, if(lv>0) levels = min(levels, lv); - GLenum gl_fmt = get_gl_pixelformat(storage_fmt); - if(ARB_texture_storage) - { - if(ARB_direct_state_access) - glTextureStorage3D(id, levels, gl_fmt, width, height, depth); - else - { - bind_scratch(); - glTexStorage3D(target, levels, gl_fmt, width, height, depth); - } - } - else - { - bind_scratch(); - GLenum comp = get_gl_components(get_components(storage_fmt)); - GLenum type = get_gl_type(get_component_type(storage_fmt)); - for(unsigned i=0; i lv_size = get_level_size(i); - glTexImage3D(target, i, gl_fmt, lv_size.x, lv_size.y, lv_size.z, 0, comp, type, 0); - } - glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, levels-1); - } - - apply_swizzle(); + allocate(); } void Texture3D::image(unsigned level, const void *data) @@ -86,15 +55,7 @@ void Texture3D::sub_image(unsigned level, int x, int y, int z, unsigned wd, unsi if(level>=levels) throw out_of_range("Texture3D::sub_image"); - GLenum comp = get_gl_components(get_components(storage_fmt)); - GLenum type = get_gl_type(get_component_type(storage_fmt)); - if(ARB_direct_state_access) - glTextureSubImage3D(id, level, x, y, z, wd, ht, dp, comp, type, data); - else - { - bind_scratch(); - glTexSubImage3D(target, level, x, y, z, wd, ht, dp, comp, type, data); - } + Texture3DBackend::sub_image(level, x, y, z, wd, ht, dp, data); } void Texture3D::image(const Graphics::Image &img, unsigned lv) @@ -114,7 +75,7 @@ void Texture3D::image(const Graphics::Image &img, unsigned lv) unsigned Texture3D::get_n_levels() const { unsigned s = max(width, height); - if(target!=GL_TEXTURE_2D_ARRAY) + if(!is_array()) s = max(s, depth); unsigned n = 0; for(; s; s>>=1, ++n) ; @@ -126,7 +87,7 @@ LinAl::Vector Texture3D::get_level_size(unsigned level) const unsigned w = width>>level; unsigned h = height>>level; unsigned d = depth; - if(target!=GL_TEXTURE_2D_ARRAY) + if(!is_array()) d >>= level; if(!w && (h || d)) diff --git a/source/core/texture3d.h b/source/core/texture3d.h index 7b88db50..21893abc 100644 --- a/source/core/texture3d.h +++ b/source/core/texture3d.h @@ -3,7 +3,7 @@ #include #include -#include "texture.h" +#include "texture3d_backend.h" namespace Msp { namespace GL { @@ -12,8 +12,10 @@ namespace GL { Three-dimensional texture. Consists of an array of texels in the shape of a right cuboid. Texture coordinates have a principal range of [0, 1]. */ -class Texture3D: public Texture +class Texture3D: public Texture3DBackend { + friend Texture3DBackend; + public: class Loader: public Msp::DataFile::DerivedObjectLoader { @@ -34,7 +36,7 @@ protected: unsigned depth; unsigned levels; - Texture3D(GLenum); + Texture3D(unsigned); public: Texture3D(); diff --git a/source/core/texturecube.cpp b/source/core/texturecube.cpp index 6239688c..ea294997 100644 --- a/source/core/texturecube.cpp +++ b/source/core/texturecube.cpp @@ -1,8 +1,4 @@ #include -#include -#include -#include -#include #include #include #include "error.h" @@ -34,20 +30,8 @@ const unsigned TextureCube::orientations[12] = }; TextureCube::TextureCube(): - Texture(GL_TEXTURE_CUBE_MAP), size(0) -{ - static Require _req(ARB_texture_cube_map); - if(ARB_seamless_cube_map) - { - static bool seamless_init = false; - if(!seamless_init) - { - glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS); - seamless_init = true; - } - } -} +{ } void TextureCube::storage(PixelFormat fmt, unsigned sz, unsigned lv) { @@ -66,32 +50,7 @@ void TextureCube::storage(PixelFormat fmt, unsigned sz, unsigned lv) if(lv>0) levels = min(levels, lv); - GLenum gl_fmt = get_gl_pixelformat(storage_fmt); - if(ARB_texture_storage) - { - if(ARB_direct_state_access) - glTextureStorage2D(id, levels, gl_fmt, size, size); - else - { - bind_scratch(); - glTexStorage2D(target, levels, gl_fmt, size, size); - } - } - else - { - bind_scratch(); - GLenum comp = get_gl_components(get_components(storage_fmt)); - GLenum type = get_gl_type(get_component_type(storage_fmt)); - for(unsigned i=0; i(j)), i, gl_fmt, lv_size, lv_size, 0, comp, type, 0); - } - glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, levels-1); - } - - apply_swizzle(); + allocate(); } void TextureCube::image(TextureCubeFace face, unsigned level, const void *data) @@ -107,16 +66,7 @@ void TextureCube::sub_image(TextureCubeFace face, unsigned level, int x, int y, if(level>=levels) throw out_of_range("TextureCube::sub_image"); - GLenum gl_face = get_gl_cube_face(face); - GLenum comp = get_gl_components(get_components(storage_fmt)); - GLenum type = get_gl_type(get_component_type(storage_fmt)); - if(ARB_direct_state_access) - glTextureSubImage3D(id, level, x, y, face, wd, ht, 1, comp, type, data); - else - { - bind_scratch(); - glTexSubImage2D(gl_face, level, x, y, wd, ht, comp, type, data); - } + TextureCubeBackend::sub_image(face, level, x, y, wd, ht, data); } void TextureCube::image(TextureCubeFace face, const Graphics::Image &img) @@ -248,20 +198,6 @@ void TextureCube::Loader::storage_levels(PixelFormat fmt, unsigned s, unsigned l } -unsigned get_gl_cube_face(TextureCubeFace face) -{ - switch(face) - { - case POSITIVE_X: return GL_TEXTURE_CUBE_MAP_POSITIVE_X; - case NEGATIVE_X: return GL_TEXTURE_CUBE_MAP_NEGATIVE_X; - case POSITIVE_Y: return GL_TEXTURE_CUBE_MAP_POSITIVE_Y; - case NEGATIVE_Y: return GL_TEXTURE_CUBE_MAP_NEGATIVE_Y; - case POSITIVE_Z: return GL_TEXTURE_CUBE_MAP_POSITIVE_Z; - case NEGATIVE_Z: return GL_TEXTURE_CUBE_MAP_NEGATIVE_Z; - default: throw invalid_argument("get_gl_cube_face"); - } -} - void operator>>(const LexicalConverter &conv, TextureCubeFace &face) { const string &str = conv.get(); diff --git a/source/core/texturecube.h b/source/core/texturecube.h index 8bb5cf43..363ceea3 100644 --- a/source/core/texturecube.h +++ b/source/core/texturecube.h @@ -2,7 +2,7 @@ #define MSP_GL_TEXTURECUBE_H_ #include -#include "texture.h" +#include "texturecube_backend.h" #include "vector.h" namespace Msp { @@ -30,8 +30,10 @@ All faces of a cube map texture must be allocated for it to be usable. Requires OpenGL version 1.3. */ -class TextureCube: public Texture +class TextureCube: public TextureCubeBackend { + friend TextureCubeBackend; + public: class Loader: public Msp::DataFile::DerivedObjectLoader { @@ -103,8 +105,6 @@ public: virtual void unload() { } }; -unsigned get_gl_cube_face(TextureCubeFace); - void operator>>(const LexicalConverter &, TextureCubeFace &); } // namespace GL diff --git a/source/core/uniformblock.cpp b/source/core/uniformblock.cpp index b06ddf92..18580149 100644 --- a/source/core/uniformblock.cpp +++ b/source/core/uniformblock.cpp @@ -1,6 +1,4 @@ #include -#include -#include #include "deviceinfo.h" #include "uniformblock.h" @@ -10,12 +8,9 @@ namespace Msp { namespace GL { UniformBlock::UniformBlock(const ReflectData::UniformBlockInfo &info): + UniformBlockBackend(info.bind_point>=0), data(info.data_size) -{ - static Require _req(ARB_shader_objects); - if(info.bind_point>=0) - static Require _req2(ARB_uniform_buffer_object); -} +{ } unsigned UniformBlock::get_alignment() const { diff --git a/source/core/uniformblock.h b/source/core/uniformblock.h index ec42089a..de8b90e4 100644 --- a/source/core/uniformblock.h +++ b/source/core/uniformblock.h @@ -5,6 +5,7 @@ #include #include "bufferable.h" #include "reflectdata.h" +#include "uniformblock_backend.h" namespace Msp { namespace GL { @@ -13,7 +14,7 @@ namespace GL { Stores uniforms with a specific layout. Both named and default uniform blocks are supported. */ -class UniformBlock: public NonCopyable, public Bufferable +class UniformBlock: public UniformBlockBackend, public NonCopyable, public Bufferable { private: std::vector data; diff --git a/source/core/vertexsetup.cpp b/source/core/vertexsetup.cpp index 892cec86..4fd589a9 100644 --- a/source/core/vertexsetup.cpp +++ b/source/core/vertexsetup.cpp @@ -1,12 +1,3 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include "buffer.h" #include "deviceinfo.h" #include "error.h" #include "vertexarray.h" @@ -23,18 +14,7 @@ VertexSetup::VertexSetup(): inst_array(0), index_buffer(0), index_type(UNSIGNED_SHORT) -{ - static Require req(ARB_vertex_array_object); - if(ARB_direct_state_access) - glCreateVertexArrays(1, &id); - else - glGenVertexArrays(1, &id); -} - -VertexSetup::~VertexSetup() -{ - glDeleteVertexArrays(1, &id); -} +{ } void VertexSetup::set_format(const VertexFormat &vfmt) { @@ -104,112 +84,15 @@ bool VertexSetup::verify_format(const VertexFormat &fmt) return all_of(fmt.begin(), fmt.end(), [](VertexAttribute a){ return get_attribute_semantic(a)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(offset)); - else - glVertexAttribPointer(sem, cc, type, true, stride, reinterpret_cast(offset)); - if(ARB_instanced_arrays) - glVertexAttribDivisor(sem, divisor); - glEnableVertexAttribArray(sem); - } - offset += get_attribute_size(a); - } - - if(!direct) - glBindBuffer(GL_ARRAY_BUFFER, 0); -} - void VertexSetup::unload() { - if(ARB_direct_state_access) - { - glVertexArrayVertexBuffer(id, 0, 0, 0, 0); - glVertexArrayVertexBuffer(id, 1, 0, 0, 0); - glVertexArrayElementBuffer(id, 0); - } - else - { - glBindVertexArray(id); - glBindBuffer(GL_ARRAY_BUFFER, 0); - - for(VertexAttribute a: vertex_format) - { - unsigned sem = get_attribute_semantic(a); - glDisableVertexAttribArray(sem); - glVertexAttribPointer(sem, 1, GL_FLOAT, false, 0, 0); - } - for(VertexAttribute a: inst_format) - { - unsigned sem = get_attribute_semantic(a); - glDisableVertexAttribArray(sem); - glVertexAttribPointer(sem, 1, GL_FLOAT, false, 0, 0); - } - - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - } + VertexSetupBackend::unload(); vertex_array = 0; vertex_format = VertexFormat(); @@ -218,15 +101,5 @@ void VertexSetup::unload() index_buffer = 0; } -void VertexSetup::set_debug_name(const string &name) -{ -#ifdef DEBUG - if(KHR_debug) - glObjectLabel(GL_VERTEX_ARRAY, id, name.size(), name.c_str()); -#else - (void)name; -#endif -} - } // namespace GL } // namespace Msp diff --git a/source/core/vertexsetup.h b/source/core/vertexsetup.h index beade58e..7daef884 100644 --- a/source/core/vertexsetup.h +++ b/source/core/vertexsetup.h @@ -3,6 +3,7 @@ #include "datatype.h" #include "vertexformat.h" +#include "vertexsetup_backend.h" namespace Msp { namespace GL { @@ -14,9 +15,9 @@ class VertexArray; Combines a VertexArray with an index buffer. This wraps OpenGL's vertex array objects. Intended for internal use. */ -class VertexSetup +class VertexSetup: public VertexSetupBackend { - friend class PipelineState; + friend VertexSetupBackend; private: enum ComponentMask @@ -26,7 +27,6 @@ private: INDEX_BUFFER = 4 }; - unsigned id; mutable unsigned dirty; const VertexArray *vertex_array; VertexFormat vertex_format; @@ -37,7 +37,6 @@ private: public: VertexSetup(); - ~VertexSetup(); void set_format(const VertexFormat &); void set_format_instanced(const VertexFormat &, const VertexFormat &); @@ -52,16 +51,14 @@ public: private: static bool verify_format(const VertexFormat &); - static void require_format(const VertexFormat &, bool); void update() const; - void update_vertex_array(const VertexArray &, unsigned, unsigned, bool) const; public: void refresh() const { if(dirty) update(); } void unload(); - void set_debug_name(const std::string &); + using VertexSetupBackend::set_debug_name; }; } // namespace GL diff --git a/source/materials/lighting.h b/source/materials/lighting.h index 9fe37ca6..92a4bcf8 100644 --- a/source/materials/lighting.h +++ b/source/materials/lighting.h @@ -4,7 +4,6 @@ #include #include #include "color.h" -#include "gl.h" #include "programdata.h" namespace Msp { diff --git a/source/materials/material.cpp b/source/materials/material.cpp index da229c41..d8665537 100644 --- a/source/materials/material.cpp +++ b/source/materials/material.cpp @@ -1,7 +1,6 @@ #include #include #include "basicmaterial.h" -#include "gl.h" #include "pbrmaterial.h" #include "program.h" #include "resources.h"