X-Git-Url: http://git.tdb.fi/?a=blobdiff_plain;f=source%2Fbackends%2Fopengl%2Fprogram_backend.cpp;fp=source%2Fbackends%2Fopengl%2Fprogram_backend.cpp;h=3b1c4293326fd1edfe21e3aa7806e2dde4597df0;hb=160e9eea29bd10034733d59507fa1bcca36be401;hp=0000000000000000000000000000000000000000;hpb=93448d16e72e38afbaecbccf6fdedd46d6a82a73;p=libs%2Fgl.git 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