Introduce gfx::shader_source to simplify creating shaders from concatenated sources

This commit is contained in:
Nikita Lisitsa 2026-07-21 13:22:27 +03:00
parent 11d96829b6
commit 1338aa05ed
8 changed files with 80 additions and 45 deletions

View file

@ -14,13 +14,39 @@
namespace psemek::gfx
{
struct shader_source
{
std::vector<std::string_view> sources;
template <std::size_t N>
shader_source(char const (&source)[N])
: sources{std::string_view{source}}
{}
shader_source(char const * source)
: sources{std::string_view{source}}
{}
shader_source(std::string_view source)
: sources{source}
{}
shader_source(std::initializer_list<std::string_view> sources)
: sources(sources)
{}
explicit shader_source(std::vector<std::string_view> sources)
: sources{std::move(sources)}
{}
};
struct program
{
program(std::string_view vertex_source, std::string_view fragment_source);
program(shader_source const & vertex_source, shader_source const & fragment_source);
#ifndef __EMSCRIPTEN__
program(std::string_view compute_source);
program(std::string_view vertex_source, std::string_view geometry_source, std::string_view fragment_source);
program(shader_source const & compute_source);
program(shader_source const & vertex_source, shader_source const & geometry_source, shader_source const & fragment_source);
#endif
program(program && other);

View file

@ -117,7 +117,7 @@ void main()
gfx::array array;
blur_impl(int size, float sigma, float gamma, bool horizontal)
: program(std::string(gl::sys::shader_prelude()) + blur_vs, std::string(gl::sys::shader_prelude()) + generate_blur_fs_source(size, sigma, horizontal))
: program({gl::sys::shader_prelude(), blur_vs}, {gl::sys::shader_prelude(), generate_blur_fs_source(size, sigma, horizontal)})
{
program.bind();
program["u_texture"] = 0;

View file

@ -206,7 +206,7 @@ void main()
struct fxaa::impl
{
gfx::program program{std::string(gl::sys::shader_prelude()) + fxaa_vs, std::string(gl::sys::shader_prelude()) + fxaa_fs};
gfx::program program{{gl::sys::shader_prelude(), fxaa_vs}, {gl::sys::shader_prelude(), fxaa_fs}};
gfx::array array;
};

View file

@ -48,7 +48,7 @@ void main()
struct gamma_correction::impl
{
gfx::program program{std::string(gl::sys::shader_prelude()) + gamma_vs, std::string(gl::sys::shader_prelude()) + gamma_fs};
gfx::program program{{gl::sys::shader_prelude(), gamma_vs}, {gl::sys::shader_prelude(), gamma_fs}};
gfx::array array;
};

View file

@ -45,7 +45,7 @@ void main()
struct overlay::impl
{
gfx::program program{std::string(gl::sys::shader_prelude()) + overlay_vs, std::string(gl::sys::shader_prelude()) + overlay_fs};
gfx::program program{{gl::sys::shader_prelude(), overlay_vs}, {gl::sys::shader_prelude(), overlay_fs}};
gfx::array array;
};

View file

@ -107,7 +107,7 @@ void main()
namespace psemek::gfx
{
static std::string shader_prefix = std::string{gl::sys::shader_prelude()};
static auto const shader_prefix = gl::sys::shader_prelude();
struct painter::impl
{
@ -130,9 +130,9 @@ namespace psemek::gfx
math::point<std::uint16_t, 2> texcoord;
};
gfx::program program{shader_prefix + vertex_source, shader_prefix + fragment_source};
gfx::program text_program{shader_prefix + text_vertex_source, shader_prefix + text_fragment_source};
gfx::program texture_program{shader_prefix + texture_vertex_source, shader_prefix + texture_fragment_source};
gfx::program program{{shader_prefix, vertex_source}, {shader_prefix, fragment_source}};
gfx::program text_program{{shader_prefix, text_vertex_source}, {shader_prefix, text_fragment_source}};
gfx::program texture_program{{shader_prefix, texture_vertex_source}, {shader_prefix, texture_fragment_source}};
gfx::mesh mesh;
gfx::mesh text_mesh;
gfx::mesh texture_mesh;

View file

@ -216,37 +216,46 @@ namespace psemek::gfx
(*this) = q.coords;
}
static std::string annotated_source(std::string_view source)
static std::string annotated_source(shader_source const & sources)
{
std::ostringstream os;
bool first = true;
int line = 1;
for (char c : source)
for (auto const & source : sources.sources)
{
if (first)
for (char c : source)
{
os << line << ": ";
first = false;
}
if (first)
{
os << line << ": ";
first = false;
}
os.put(c);
if (c == '\n')
{
++line;
first = true;
os.put(c);
if (c == '\n')
{
++line;
first = true;
}
}
}
return os.str();
}
static void load_shader(GLuint shader, std::string_view source)
static void load_shader(GLuint shader, shader_source const & sources)
{
char const * vert_sources[1] { source.data() };
GLint vert_sources_len[1] { static_cast<GLint>(source.size()) };
gl::ShaderSource(shader, 1, vert_sources, vert_sources_len);
std::vector<char const *> source_ptrs;
std::vector<GLint> source_lens;
for (auto const & source : sources.sources)
{
source_ptrs.push_back(source.data());
source_lens.push_back(source.size());
}
gl::ShaderSource(shader, sources.sources.size(), source_ptrs.data(), source_lens.data());
gl::CompileShader(shader);
GLint status;
@ -257,7 +266,7 @@ namespace psemek::gfx
gl::GetShaderiv(shader, gl::INFO_LOG_LENGTH, &log_len);
std::unique_ptr<char[]> log(new char [log_len]);
gl::GetShaderInfoLog(shader, log_len, nullptr, log.get());
throw util::exception(util::to_string("Shader compilation failed: ", log.get(), "\nShader source: \n", annotated_source(source)));
throw util::exception(util::to_string("Shader compilation failed: ", log.get(), "\nShader source: \n", annotated_source(sources)));
}
}
@ -282,7 +291,7 @@ namespace psemek::gfx
gl::DetachShader(program, s);
}
static GLuint create_program(std::vector<std::pair<GLenum, std::string_view>> const & sources)
static GLuint create_program(std::vector<std::pair<GLenum, shader_source const *>> const & sources)
{
std::vector<GLuint> shaders;
@ -295,7 +304,7 @@ namespace psemek::gfx
{
GLuint sh = gl::CreateShader(p.first);
shaders.push_back(sh);
load_shader(sh, p.second);
load_shader(sh, *p.second);
}
GLuint program = gl::CreateProgram();
@ -313,20 +322,20 @@ namespace psemek::gfx
return program;
}
program::program(std::string_view vertex_source, std::string_view fragment_source)
program::program(shader_source const & vertex_source, shader_source const & fragment_source)
{
program_ = create_program({{gl::VERTEX_SHADER, vertex_source}, {gl::FRAGMENT_SHADER, fragment_source}});
program_ = create_program({{gl::VERTEX_SHADER, &vertex_source}, {gl::FRAGMENT_SHADER, &fragment_source}});
}
#ifndef __EMSCRIPTEN__
program::program(std::string_view compute_source)
program::program(shader_source const & compute_source)
{
program_ = create_program({{gl::COMPUTE_SHADER, compute_source}});
program_ = create_program({{gl::COMPUTE_SHADER, &compute_source}});
}
program::program(std::string_view vertex_source, std::string_view geometry_source, std::string_view fragment_source)
program::program(shader_source const & vertex_source, shader_source const & geometry_source, shader_source const & fragment_source)
{
program_ = create_program({{gl::VERTEX_SHADER, vertex_source}, {gl::GEOMETRY_SHADER, geometry_source}, {gl::FRAGMENT_SHADER, fragment_source}});
program_ = create_program({{gl::VERTEX_SHADER, &vertex_source}, {gl::GEOMETRY_SHADER, &geometry_source}, {gl::FRAGMENT_SHADER, &fragment_source}});
}
#endif

View file

@ -811,16 +811,16 @@ void main(){}
deferred_renderer::position_mode position_mode = deferred_renderer::position_mode::float32;
bool position_mode_changed = true;
gfx::program occlusion_pass_program{occlusion_pass_vs, occlusion_pass_fs};
gfx::program g_buffer_pass_program{std::string(g_buffer_pass_common) + g_buffer_pass_vs, std::string(g_buffer_pass_common) + g_buffer_pass_fs};
gfx::program ambient_pass_program{fullscreen_vs, std::string(light_common) + ambient_pass_fs};
gfx::program directional_light_pass_program{screen_vs, std::string(light_common) + directional_light_pass_fs};
gfx::program point_light_pass_program{screen_vs, std::string(light_common) + point_light_pass_fs};
gfx::program shadow_builder_program{std::string(g_buffer_pass_common) + shadow_builder_vs, shadow_builder_fs};
gfx::program cubemap_shadow_builder_program{std::string(g_buffer_pass_common) + shadow_builder_vs, shadow_builder_gs, shadow_builder_fs};
gfx::program transparent_pass_program{std::string(g_buffer_pass_common) + transparent_pass_vs, std::string(g_buffer_pass_common) + transparent_pass_fs};
gfx::program bloom_pass_program{std::string(g_buffer_pass_common) + transparent_pass_vs, std::string(g_buffer_pass_common) + transparent_pass_fs};
gfx::program ssao_pass_program{fullscreen_vs, std::string(light_common) + ssao_pass_fs};
gfx::program occlusion_pass_program{{occlusion_pass_vs}, {occlusion_pass_fs}};
gfx::program g_buffer_pass_program{{g_buffer_pass_common, g_buffer_pass_vs}, {g_buffer_pass_common, g_buffer_pass_fs}};
gfx::program ambient_pass_program{{fullscreen_vs}, {light_common, ambient_pass_fs}};
gfx::program directional_light_pass_program{{screen_vs}, {light_common, directional_light_pass_fs}};
gfx::program point_light_pass_program{{screen_vs}, {light_common, point_light_pass_fs}};
gfx::program shadow_builder_program{{g_buffer_pass_common, shadow_builder_vs}, {shadow_builder_fs}};
gfx::program cubemap_shadow_builder_program{{g_buffer_pass_common, shadow_builder_vs}, {shadow_builder_gs}, {shadow_builder_fs}};
gfx::program transparent_pass_program{{g_buffer_pass_common, transparent_pass_vs}, {g_buffer_pass_common, transparent_pass_fs}};
gfx::program bloom_pass_program{{g_buffer_pass_common, transparent_pass_vs}, {g_buffer_pass_common, transparent_pass_fs}};
gfx::program ssao_pass_program{{fullscreen_vs}, {light_common, ssao_pass_fs}};
// G-buffer attachments:
// 0 - position (rbg)