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
rhi.cpp
rhi.hpp
shader_load_context.cpp
shader_load_context.hpp
)
add_subdirectory(gl3_core)

View file

@ -20,6 +20,8 @@
#include <glad/gl.h>
#include <glm/gtc/type_ptr.hpp>
#include "../shader_load_context.hpp"
using namespace srb2;
using namespace rhi;
@ -871,112 +873,82 @@ rhi::Handle<rhi::Pipeline> GlCoreRhi::create_pipeline(const PipelineDesc& desc)
GLuint program = 0;
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.
// In Khronos Group's brilliance, #version is required to be the first directive,
// but we need to insert #defines in-between.
std::string vert_src_processed;
std::string::size_type string_i = 0;
do
// Process vertex shader sources
std::vector<const char*> vert_sources;
ShaderLoadContext vert_ctx;
vert_ctx.set_version("150 core");
for (auto& attribute : desc.vertex_input.attr_layouts)
{
std::string::size_type new_i = vert_src.find('\n', string_i);
if (new_i == std::string::npos)
for (auto const& require_attr : reqs.vertex_input.attributes)
{
break;
}
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)
if (require_attr.name == attribute.name && !require_attr.required)
{
for (auto const& require_attr : reqs.vertex_input.attributes)
{
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");
}
}
}
}
vert_ctx.define(map_vertex_attribute_enable_define(attribute.name));
}
}
string_i = new_i + 1;
} while (string_i != std::string::npos);
std::string frag_src_processed;
string_i = 0;
do
}
for (auto& uniform_group : desc.uniform_input.enabled_uniforms)
{
std::string::size_type new_i = frag_src.find('\n', string_i);
if (new_i == std::string::npos)
for (auto& uniform : uniform_group)
{
break;
}
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& req_uni_group : reqs.uniforms.uniform_groups)
{
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 ");
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");
}
}
vert_ctx.define(map_uniform_enable_define(uniform));
}
}
}
}
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()};
const GLint vert_src_arr_lens[1] = {static_cast<GLint>(vert_src_processed.size())};
const char* frag_src_arr[1] = {frag_src_processed.c_str()};
const GLint frag_src_arr_lens[1] = {static_cast<GLint>(frag_src_processed.size())};
// Process vertex shader sources
std::vector<const char*> frag_sources;
ShaderLoadContext frag_ctx;
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);
gl_->ShaderSource(vertex, 1, vert_src_arr, vert_src_arr_lens);
gl_->ShaderSource(vertex, vert_sources.size(), vert_sources.data(), NULL);
gl_->CompileShader(vertex);
GLint is_compiled = 0;
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);
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_->GetShaderiv(vertex, GL_COMPILE_STATUS, &is_compiled);
if (is_compiled == GL_FALSE)

View file

@ -70,7 +70,7 @@ struct GlCorePlatform
virtual ~GlCorePlatform();
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;
};

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 <array>
#include <sstream>
#include <SDL.h>
#include <fmt/core.h>
#include "../cxxutil.hpp"
#include "../w_wad.h"
@ -28,39 +32,75 @@ void SdlGlCorePlatform::present()
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)
{
case rhi::PipelineProgram::kUnshaded:
vertex_lump_name = "rhi_glcore_vertex_unshaded";
fragment_lump_name = "rhi_glcore_fragment_unshaded";
break;
return "unshaded";
case rhi::PipelineProgram::kUnshadedPaletted:
vertex_lump_name = "rhi_glcore_vertex_unshadedpaletted";
fragment_lump_name = "rhi_glcore_fragment_unshadedpaletted";
break;
return "unshadedpaletted";
case rhi::PipelineProgram::kPostprocessWipe:
vertex_lump_name = "rhi_glcore_vertex_postprocesswipe";
fragment_lump_name = "rhi_glcore_fragment_postprocesswipe";
break;
return "postprocesswipe";
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);
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);
return sources;
}
std::string vertex_shader(static_cast<const char*>(vertex_lump), vertex_lump_length);
std::string fragment_shader(static_cast<const char*>(fragment_lump), fragment_lump_length);
std::tuple<std::vector<std::string>, std::vector<std::string>>
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()

View file

@ -25,7 +25,8 @@ struct SdlGlCorePlatform final : public GlCorePlatform
virtual ~SdlGlCorePlatform();
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;
};