rhi: Rewrite shader loading

Multiple shader sources, simpler macro definition, and slightly more
data driven.
This commit is contained in:
Eidolon 2023-04-01 17:32:34 -05:00
parent 171a285caa
commit a9fcff852d
7 changed files with 219 additions and 113 deletions

View file

@ -2,6 +2,8 @@ target_sources(SRB2SDL2 PRIVATE
handle.hpp handle.hpp
rhi.cpp rhi.cpp
rhi.hpp rhi.hpp
shader_load_context.cpp
shader_load_context.hpp
) )
add_subdirectory(gl3_core) add_subdirectory(gl3_core)

View file

@ -20,6 +20,8 @@
#include <glad/gl.h> #include <glad/gl.h>
#include <glm/gtc/type_ptr.hpp> #include <glm/gtc/type_ptr.hpp>
#include "../shader_load_context.hpp"
using namespace srb2; using namespace srb2;
using namespace rhi; using namespace rhi;
@ -871,112 +873,82 @@ rhi::Handle<rhi::Pipeline> GlCoreRhi::create_pipeline(const PipelineDesc& desc)
GLuint program = 0; GLuint program = 0;
GlCorePipeline pipeline; GlCorePipeline pipeline;
auto [vert_src, frag_src] = platform_->find_shader_sources(desc.program); auto [vert_srcs, frag_srcs] = platform_->find_shader_sources(desc.program);
// TODO make use of multiple source files. // Process vertex shader sources
// In Khronos Group's brilliance, #version is required to be the first directive, std::vector<const char*> vert_sources;
// but we need to insert #defines in-between. ShaderLoadContext vert_ctx;
std::string vert_src_processed; vert_ctx.set_version("150 core");
std::string::size_type string_i = 0; for (auto& attribute : desc.vertex_input.attr_layouts)
do
{ {
std::string::size_type new_i = vert_src.find('\n', string_i); for (auto const& require_attr : reqs.vertex_input.attributes)
if (new_i == std::string::npos)
{ {
break; if (require_attr.name == attribute.name && !require_attr.required)
}
std::string_view line_view(vert_src.c_str() + string_i, new_i - string_i + 1);
vert_src_processed.append(line_view);
if (line_view.rfind("#version ", 0) == 0)
{
for (auto& attribute : desc.vertex_input.attr_layouts)
{ {
for (auto const& require_attr : reqs.vertex_input.attributes) vert_ctx.define(map_vertex_attribute_enable_define(attribute.name));
{
if (require_attr.name == attribute.name && !require_attr.required)
{
vert_src_processed.append("#define ");
vert_src_processed.append(map_vertex_attribute_enable_define(attribute.name));
vert_src_processed.append("\n");
}
}
}
for (auto& uniform_group : desc.uniform_input.enabled_uniforms)
{
for (auto& uniform : uniform_group)
{
for (auto const& req_uni_group : reqs.uniforms.uniform_groups)
{
for (auto const& req_uni : req_uni_group)
{
if (req_uni.name == uniform && !req_uni.required)
{
vert_src_processed.append("#define ");
vert_src_processed.append(map_uniform_enable_define(uniform));
vert_src_processed.append("\n");
}
}
}
}
} }
} }
string_i = new_i + 1; }
} while (string_i != std::string::npos); for (auto& uniform_group : desc.uniform_input.enabled_uniforms)
std::string frag_src_processed;
string_i = 0;
do
{ {
std::string::size_type new_i = frag_src.find('\n', string_i); for (auto& uniform : uniform_group)
if (new_i == std::string::npos)
{ {
break; for (auto const& req_uni_group : reqs.uniforms.uniform_groups)
}
std::string_view line_view(frag_src.c_str() + string_i, new_i - string_i + 1);
frag_src_processed.append(line_view);
if (line_view.rfind("#version ", 0) == 0)
{
for (auto& sampler : desc.sampler_input.enabled_samplers)
{ {
for (auto const& require_sampler : reqs.samplers.samplers) for (auto const& req_uni : req_uni_group)
{ {
if (sampler == require_sampler.name && !require_sampler.required) if (req_uni.name == uniform && !req_uni.required)
{ {
frag_src_processed.append("#define "); vert_ctx.define(map_uniform_enable_define(uniform));
frag_src_processed.append(map_sampler_enable_define(sampler));
frag_src_processed.append("\n");
}
}
}
for (auto& uniform_group : desc.uniform_input.enabled_uniforms)
{
for (auto& uniform : uniform_group)
{
for (auto const& req_uni_group : reqs.uniforms.uniform_groups)
{
for (auto const& req_uni : req_uni_group)
{
if (req_uni.name == uniform && !req_uni.required)
{
frag_src_processed.append("#define ");
frag_src_processed.append(map_uniform_enable_define(uniform));
frag_src_processed.append("\n");
}
}
} }
} }
} }
} }
string_i = new_i + 1; }
} while (string_i != std::string::npos); for (auto& src : vert_srcs)
{
vert_ctx.add_source(std::move(src));
}
vert_sources = vert_ctx.get_sources_array();
const char* vert_src_arr[1] = {vert_src_processed.c_str()}; // Process vertex shader sources
const GLint vert_src_arr_lens[1] = {static_cast<GLint>(vert_src_processed.size())}; std::vector<const char*> frag_sources;
const char* frag_src_arr[1] = {frag_src_processed.c_str()}; ShaderLoadContext frag_ctx;
const GLint frag_src_arr_lens[1] = {static_cast<GLint>(frag_src_processed.size())}; frag_ctx.set_version("150 core");
for (auto& sampler : desc.sampler_input.enabled_samplers)
{
for (auto const& require_sampler : reqs.samplers.samplers)
{
if (sampler == require_sampler.name && !require_sampler.required)
{
frag_ctx.define(map_sampler_enable_define(sampler));
}
}
}
for (auto& uniform_group : desc.uniform_input.enabled_uniforms)
{
for (auto& uniform : uniform_group)
{
for (auto const& req_uni_group : reqs.uniforms.uniform_groups)
{
for (auto const& req_uni : req_uni_group)
{
if (req_uni.name == uniform && !req_uni.required)
{
frag_ctx.define(map_uniform_enable_define(uniform));
}
}
}
}
}
for (auto& src : frag_srcs)
{
frag_ctx.add_source(std::move(src));
}
frag_sources = frag_ctx.get_sources_array();
vertex = gl_->CreateShader(GL_VERTEX_SHADER); vertex = gl_->CreateShader(GL_VERTEX_SHADER);
gl_->ShaderSource(vertex, 1, vert_src_arr, vert_src_arr_lens); gl_->ShaderSource(vertex, vert_sources.size(), vert_sources.data(), NULL);
gl_->CompileShader(vertex); gl_->CompileShader(vertex);
GLint is_compiled = 0; GLint is_compiled = 0;
gl_->GetShaderiv(vertex, GL_COMPILE_STATUS, &is_compiled); gl_->GetShaderiv(vertex, GL_COMPILE_STATUS, &is_compiled);
@ -992,7 +964,7 @@ rhi::Handle<rhi::Pipeline> GlCoreRhi::create_pipeline(const PipelineDesc& desc)
); );
} }
fragment = gl_->CreateShader(GL_FRAGMENT_SHADER); fragment = gl_->CreateShader(GL_FRAGMENT_SHADER);
gl_->ShaderSource(fragment, 1, frag_src_arr, frag_src_arr_lens); gl_->ShaderSource(fragment, frag_sources.size(), frag_sources.data(), NULL);
gl_->CompileShader(fragment); gl_->CompileShader(fragment);
gl_->GetShaderiv(vertex, GL_COMPILE_STATUS, &is_compiled); gl_->GetShaderiv(vertex, GL_COMPILE_STATUS, &is_compiled);
if (is_compiled == GL_FALSE) if (is_compiled == GL_FALSE)

View file

@ -70,7 +70,7 @@ struct GlCorePlatform
virtual ~GlCorePlatform(); virtual ~GlCorePlatform();
virtual void present() = 0; virtual void present() = 0;
virtual std::tuple<std::string, std::string> find_shader_sources(PipelineProgram program) = 0; virtual std::tuple<std::vector<std::string>, std::vector<std::string>> find_shader_sources(PipelineProgram program) = 0;
virtual Rect get_default_framebuffer_dimensions() = 0; virtual Rect get_default_framebuffer_dimensions() = 0;
}; };

View file

@ -0,0 +1,51 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 2023 by Ronald "Eidolon" Kinard
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
#include "shader_load_context.hpp"
#include <fmt/core.h>
using namespace srb2;
using namespace rhi;
ShaderLoadContext::ShaderLoadContext() = default;
void ShaderLoadContext::set_version(std::string_view version)
{
version_ = fmt::format("#version {}\n", version);
}
void ShaderLoadContext::add_source(const std::string& source)
{
sources_.push_back(source);
}
void ShaderLoadContext::add_source(std::string&& source)
{
sources_.push_back(std::move(source));
}
void ShaderLoadContext::define(std::string_view name)
{
defines_.append(fmt::format("#define {}\n", name));
}
std::vector<const char*> ShaderLoadContext::get_sources_array()
{
std::vector<const char*> ret;
ret.push_back(version_.c_str());
ret.push_back(defines_.c_str());
for (auto& source : sources_)
{
ret.push_back(source.c_str());
}
return ret;
}

View file

@ -0,0 +1,40 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 2023 by Ronald "Eidolon" Kinard
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
#ifndef __SRB2_RHI_SHADER_LOAD_CONTEXT_HPP__
#define __SRB2_RHI_SHADER_LOAD_CONTEXT_HPP__
#include <string>
#include <string_view>
#include <vector>
namespace srb2::rhi
{
class ShaderLoadContext
{
std::string version_;
std::string defines_;
std::vector<std::string> sources_;
public:
ShaderLoadContext();
void set_version(std::string_view version);
void add_source(const std::string& source);
void add_source(std::string&& source);
void define(std::string_view name);
std::vector<const char*> get_sources_array();
};
}; // namespace srb2::rhi
#endif // __SRB2_RHI_SHADER_LOAD_CONTEXT_HPP__

View file

@ -9,7 +9,11 @@
#include "rhi_gl3_core_platform.hpp" #include "rhi_gl3_core_platform.hpp"
#include <array>
#include <sstream>
#include <SDL.h> #include <SDL.h>
#include <fmt/core.h>
#include "../cxxutil.hpp" #include "../cxxutil.hpp"
#include "../w_wad.h" #include "../w_wad.h"
@ -28,39 +32,75 @@ void SdlGlCorePlatform::present()
SDL_GL_SwapWindow(window); SDL_GL_SwapWindow(window);
} }
std::tuple<std::string, std::string> SdlGlCorePlatform::find_shader_sources(rhi::PipelineProgram program) static constexpr const char* pipeline_lump_slug(rhi::PipelineProgram program)
{ {
const char* vertex_lump_name = nullptr;
const char* fragment_lump_name = nullptr;
switch (program) switch (program)
{ {
case rhi::PipelineProgram::kUnshaded: case rhi::PipelineProgram::kUnshaded:
vertex_lump_name = "rhi_glcore_vertex_unshaded"; return "unshaded";
fragment_lump_name = "rhi_glcore_fragment_unshaded";
break;
case rhi::PipelineProgram::kUnshadedPaletted: case rhi::PipelineProgram::kUnshadedPaletted:
vertex_lump_name = "rhi_glcore_vertex_unshadedpaletted"; return "unshadedpaletted";
fragment_lump_name = "rhi_glcore_fragment_unshadedpaletted";
break;
case rhi::PipelineProgram::kPostprocessWipe: case rhi::PipelineProgram::kPostprocessWipe:
vertex_lump_name = "rhi_glcore_vertex_postprocesswipe"; return "postprocesswipe";
fragment_lump_name = "rhi_glcore_fragment_postprocesswipe";
break;
default: default:
std::terminate(); return "";
}
}
static std::array<std::string, 2> glsllist_lump_names(rhi::PipelineProgram program)
{
const char* pipeline_slug = pipeline_lump_slug(program);
std::string vertex_list_name = fmt::format("rhi_glsllist_{}_vertex", pipeline_slug);
std::string fragment_list_name = fmt::format("rhi_glsllist_{}_fragment", pipeline_slug);
return {std::move(vertex_list_name), std::move(fragment_list_name)};
}
static std::vector<std::string> get_sources_from_glsllist_lump(const char* lumpname)
{
lumpnum_t glsllist_lump_num = W_GetNumForLongName(lumpname);
void* glsllist_lump = W_CacheLumpNum(glsllist_lump_num, PU_CACHE);
size_t glsllist_lump_length = W_LumpLength(glsllist_lump_num);
std::istringstream glsllist(std::string(static_cast<const char*>(glsllist_lump), glsllist_lump_length));
std::vector<std::string> sources;
for (std::string line; std::getline(glsllist, line); )
{
if (line.empty())
{
continue;
}
if (line[0] == '#')
{
continue;
}
if (line.back() == '\r')
{
line.pop_back();
}
lumpnum_t source_lump_num = W_GetNumForLongName(line.c_str());
void* source_lump = W_CacheLumpNum(source_lump_num, PU_CACHE);
size_t source_lump_length = W_LumpLength(source_lump_num);
sources.emplace_back(static_cast<const char*>(source_lump), source_lump_length);
} }
lumpnum_t vertex_lump_num = W_GetNumForLongName(vertex_lump_name); return sources;
lumpnum_t fragment_lump_num = W_GetNumForLongName(fragment_lump_name); }
size_t vertex_lump_length = W_LumpLength(vertex_lump_num);
size_t fragment_lump_length = W_LumpLength(fragment_lump_num);
void* vertex_lump = W_CacheLumpNum(vertex_lump_num, PU_CACHE);
void* fragment_lump = W_CacheLumpNum(fragment_lump_num, PU_CACHE);
std::string vertex_shader(static_cast<const char*>(vertex_lump), vertex_lump_length); std::tuple<std::vector<std::string>, std::vector<std::string>>
std::string fragment_shader(static_cast<const char*>(fragment_lump), fragment_lump_length); SdlGlCorePlatform::find_shader_sources(rhi::PipelineProgram program)
{
std::array<std::string, 2> glsllist_names = glsllist_lump_names(program);
return std::make_tuple(std::move(vertex_shader), std::move(fragment_shader)); std::vector<std::string> vertex_sources = get_sources_from_glsllist_lump(glsllist_names[0].c_str());
std::vector<std::string> fragment_sources = get_sources_from_glsllist_lump(glsllist_names[1].c_str());
return std::make_tuple(std::move(vertex_sources), std::move(fragment_sources));
} }
rhi::Rect SdlGlCorePlatform::get_default_framebuffer_dimensions() rhi::Rect SdlGlCorePlatform::get_default_framebuffer_dimensions()

View file

@ -25,7 +25,8 @@ struct SdlGlCorePlatform final : public GlCorePlatform
virtual ~SdlGlCorePlatform(); virtual ~SdlGlCorePlatform();
virtual void present() override; virtual void present() override;
virtual std::tuple<std::string, std::string> find_shader_sources(PipelineProgram program) override; virtual std::tuple<std::vector<std::string>, std::vector<std::string>>
find_shader_sources(PipelineProgram program) override;
virtual Rect get_default_framebuffer_dimensions() override; virtual Rect get_default_framebuffer_dimensions() override;
}; };