This commit is contained in:
ZY4N
2025-03-23 21:11:22 +01:00
parent 510398423a
commit c609d49f0d
49 changed files with 1412 additions and 924 deletions

View File

@@ -0,0 +1,133 @@
#include "opengl/data_managers/mesh_vertex_buffer_manager.hpp"
void zgl::mesh_vertex_buffer_manager::process(store_type& store)
{
for (const auto& [ id, mesh ] : store)
{
// The ireator must reutrn a pair containing the components and a tuple of spans
}
}
template<typename C, typename... Ts>
void dynamic_vertex_buffer<C, Ts...>::build_vertex_buffer(
std::vector<ztu::u8>& vertex_buffer,
std::size_t& component_count,
std::array<GLenum, sizeof...(Ts)>& component_types,
std::array<GLint, sizeof...(Ts)>& component_lengths,
GLsizei& stride
) const {
const auto for_all_components = [&]<typename T>(auto&& f, const T default_value)
{
return std::apply(
[&](const auto&... component_buffer)
{
std::array<T, sizeof...(component_buffer)> results{};
auto i = std::size_t{};
(
(
results[i] = [&](const auto& buffer, const auto index) -> T
{
if ((m_components & C{ 1 << index }) != C{})
{
return f(buffer, index);
}
return default_value;
}(component_buffer, i),
++i
),
...
);
return results;
},
m_component_buffers
);
};
component_count = 0;
component_types = for_all_components(
ztu::specialised_lambda
{
[&component_count]<numeric_type Component, std::size_t Count>(const std::vector<std::array<Component, Count>>&, std::size_t)
{
++component_count;
return zgl::type_utils::to_gl_type<Component>();
},
[&component_count]<numeric_type Component>(const std::vector<Component>&, std::size_t)
{
++component_count;
return zgl::type_utils::to_gl_type<Component>();
}
},
GLenum{ GL_INVALID_VALUE }
);
const auto element_counts = for_all_components(
[]<class Component>(const std::vector<Component>& buffer, std::size_t)
{
return buffer.size();
},
std::numeric_limits<std::size_t>::max()
);
const auto minimum_element_count = std::ranges::min(element_counts);
component_lengths = for_all_components(
ztu::specialised_lambda
{
[]<class Component>(const std::vector<Component>&, std::size_t)
{
return 1;
},
[]<class Component, std::size_t Count>(const std::vector<std::array<Component, Count>>&, std::size_t)
{
return Count;
}
},
GLsizei{ 0 }
);
auto component_sizes = std::array<GLsizei, sizeof...(Ts)>{};
for (std::size_t i{}; i != component_sizes.size(); ++i)
{
component_sizes[i] = component_lengths[i] * zgl::type_utils::size_of(component_types[i]);
}
const auto total_size = minimum_element_count * std::accumulate(
component_sizes.begin(),
component_sizes.end(),
GLsizei{ 0 }
);
vertex_buffer.resize(total_size);
// Calculate offsets and stride
auto component_offsets = component_sizes;
stride = 0;
for (std::size_t i{}; i != component_offsets.size(); ++i) {
component_offsets[i] = stride;
stride += component_sizes[i];
}
// Copy all the components over one by one
for_all_components(
[&]<class Component>(const std::vector<Component>& buffer, std::size_t index)
{
std::size_t pos = component_offsets[index];
for (std::size_t i{}; i != minimum_element_count; ++i)
{
std::memcpy(
&vertex_buffer[pos],
buffer[i].data(),
component_sizes[index]
);
pos += stride;
}
return 0;
},
0
);
// remove values of unused components
std::ignore = std::ranges::remove(component_lengths, 0);
std::ignore = std::ranges::remove(component_types, GL_INVALID_VALUE);
}

View File

@@ -0,0 +1,264 @@
#include "opengl/data_managers/shader_manager.hpp"
#include <numeric>
#include <bits/ranges_algobase.h>
#include "opengl/error.hpp"
#include "util/logger.hpp"
struct prioritized_metadata_comparator
{
using type = zgl::shader_metadata;
bool operator()(const type& a, const type& b) const noexcept
{
if (a.geometry != b.geometry)
{
return a.geometry > b.geometry;
}
if (a.stage != b.stage)
{
return a.stage > b.stage;
}
static constexpr auto more_features = std::popcount<zgl::shading::features::generic::type>;
return std::ranges::lexicographical_compare(
std::array{ a.dynamic_enable, a.static_enabled },
std::array{ b.dynamic_enable, b.static_enabled },
std::greater{},
more_features,
more_features
);
}
};
static constexpr auto gl_shader_types = std::array<GLenum, zgl::shading::stage::count>{
GL_VERTEX_SHADER,
GL_TESS_CONTROL_SHADER,
GL_TESS_EVALUATION_SHADER,
GL_GEOMETRY_SHADER,
GL_FRAGMENT_SHADER
};
std::optional<std::pair<zgl::shader_metadata, zgl::shader_handle>> zgl::shader_manager::find_shader(
const shading::shader_requirements& requirements
) {
auto shader_it = std::ranges::lower_bound(
m_shader_lookup,
std::pair{ requirements.geometry, requirements.stage },
std::greater{},
[](const shader_lookup_entry_type& entry)
{
const auto& meta = entry.first;
return std::pair{ meta.geometry, meta.stage };
}
);
while (
shader_it != m_shader_lookup.end() and
shader_it->first.geometry == requirements.geometry and
shader_it->first.stage == requirements.stage
) {
const auto& [ meta, data ] = *shader_it;
const auto unwanted_static_features = meta.static_enabled & ~requirements.features;
const auto required_dynamic_features = requirements.features & ~meta.static_enabled;
const auto missing_dynamic_features = required_dynamic_features & ~meta.dynamic_enable;
if (unwanted_static_features == 0 and missing_dynamic_features == 0)
{
return std::pair{ meta, data.handle };
}
++shader_it;
}
return std::nullopt;
}
void zgl::shader_manager::get_handles(
const dynamic_shader_source_store& shader_sources,
std::span<const shading::shader_set_requirements> requirements,
std::span<shader_set_metadata> metadata,
std::span<shader_handle_set> shader_sets
) {
for (auto [ req, shader_set_meta, shader_set ] : std::ranges::views::zip(
requirements,
metadata,
shader_sets
)) {
shader_set = {};
shader_set_meta = {};
auto shader_req = shading::shader_requirements{
.geometry = req.geometry,
.stage = {},
.features = req.features
};
for (auto [ stage_index, handle ] : std::ranges::views::enumerate(shader_set.stages))
{
shader_req.stage = static_cast<shading::stage::types>(stage_index);
if (auto shader_match = find_shader(shader_req))
{
const auto& [ shader_meta, shader_handle ] = *shader_match;
shader_set_meta.static_enabled |= shader_meta.static_enabled;
shader_set_meta.dynamic_enable |= shader_meta.dynamic_enable;
handle = shader_handle;
}
else
{
m_source_requirement_buffer.push_back(shader_req);
}
}
}
m_preprocessed_shader_source_metadata_buffer.clear();
m_preprocessed_shader_source_metadata_buffer.resize(m_source_requirement_buffer.size());
m_source_strings_buffer.clear();
m_preprocessor.get_shader_sources(
shader_sources,
m_source_requirement_buffer,
m_preprocessed_shader_source_metadata_buffer,
m_source_strings_buffer
);
auto source_strings_it = m_source_strings_buffer.begin();
auto source_req_it = m_source_requirement_buffer.begin();
auto source_meta_it = m_preprocessed_shader_source_metadata_buffer.begin();
const auto prev_shader_count = m_shader_lookup.size();
for (auto [ shader_set_meta, shader_set ] : std::ranges::views::zip( metadata, shader_sets))
{
auto shader_missing = false;
for (auto [ stage_index, handle ] : std::ranges::views::enumerate(shader_set.stages))
{
if (not handle.valid())
{
if (not shader_missing)
{
if (source_meta_it->string_count > 0)
{
shader_data shader{};
if (compile_shader(
gl_shader_types[stage_index],
std::span(source_strings_it.base(), source_meta_it->string_count),
shader
)) {
handle = shader.handle;
auto shader_meta = shader_metadata{
.geometry = source_req_it->geometry,
.stage = source_req_it->stage,
.static_enabled = source_meta_it->static_enabled,
.dynamic_enable = source_meta_it->dynamic_enable
};
shader_set_meta.static_enabled |= shader_meta.static_enabled;
shader_set_meta.dynamic_enable |= shader_meta.dynamic_enable;
m_shader_lookup.emplace_back(shader_meta, std::move(shader));
}
}
shader_missing = not handle.valid() and shading::stage::mandatory[stage_index];
}
source_strings_it += source_meta_it->string_count;
++source_meta_it;
++source_req_it;
}
}
if (shader_missing)
{
shader_set.stages = {};
}
}
const auto new_shaders = std::span(m_shader_lookup).subspan(prev_shader_count);
std::ranges::sort(
new_shaders,
prioritized_metadata_comparator{},
&shader_lookup_entry_type::first
);
std::ranges::inplace_merge(
m_shader_lookup,
m_shader_lookup.begin() + prev_shader_count,
prioritized_metadata_comparator{},
&shader_lookup_entry_type::first
);
}
bool zgl::shader_manager::compile_shader(
GLenum shader_type,
std::span<const char*> source_strings,
shader_data& shader
) {
shader = shader_data(glCreateShader(shader_type));
if (const auto e = get_error())
{
ztu::logger::error("Error while creating shader: %", e.message());
return false;
}
glShaderSource(
shader.handle.id,
static_cast<GLsizei>(source_strings.size()),
source_strings.data(),
nullptr
);
if (const auto e = get_error())
{
ztu::logger::error("Error while setting shader source: %", e.message());
return false;
}
glCompileShader(shader.handle.id);
if (const auto e = get_error())
{
ztu::logger::error("Error while compiling shader: %", e.message());
return false;
}
auto status = GLint{ GL_FALSE };
glGetShaderiv(shader.handle.id, GL_COMPILE_STATUS, &status);
if (const auto e = get_error())
{
ztu::logger::error("Error while retrieving shader compilation status: %", e.message());
return false;
}
if (status == GL_FALSE)
{
GLint log_length{};
glGetShaderiv(shader.handle.id, GL_INFO_LOG_LENGTH, &log_length);
auto log = std::string(log_length, ' ');
glGetShaderInfoLog(shader.handle.id, log_length, nullptr, log.data());
ztu::logger::error("Error while compiling shader:\n%", log);
return false;
}
return true;
}
void zgl::shader_manager::process(
const dynamic_shader_source_store& shader_sources
) {
m_preprocessor.process(shader_sources);
}

View File

@@ -0,0 +1,252 @@
#include "opengl/data_managers/shader_program_manager.hpp"
struct prioritized_metadata_comparator
{
using type = zgl::shader_program_metadata;
bool operator()(const type& a, const type& b) const noexcept
{
if (a.geometry != b.geometry)
{
return a.geometry > b.geometry;
}
static constexpr auto more_features = std::popcount<zgl::shading::features::generic::type>;
return std::ranges::lexicographical_compare(
std::array{ a.dynamic_enable, a.static_enabled },
std::array{ b.dynamic_enable, b.static_enabled },
std::greater{},
more_features,
more_features
);
}
};
void zgl::shader_program_manager::process(
const dynamic_shader_source_store& shader_sources
) {
m_shader_manager.preprocess(shader_sources);
}
void zgl::shader_program_manager::get_handles(
const dynamic_shader_source_store& shader_sources,
std::span<const shading::shader_program_requirements> requirements,
std::span<shader_program_metadata> metadata,
std::span<shader_program_handle> shader_programs
) {
m_shader_requirements_buffer.clear();
for (auto [ req, program_meta, program_handle ] : std::ranges::views::zip(
requirements,
metadata,
shader_programs
)) {
if (auto shader_match = find_shader_program(req))
{
const auto& [ meta, handle ] = *shader_match;
program_meta.static_enabled = meta.static_enabled;
program_meta.dynamic_enable = meta.dynamic_enable;
program_handle = handle;
}
else
{
program_meta = {};
program_handle = {};
m_shader_requirements_buffer.emplace_back(
req.geometry,
req.features
);
}
}
m_shader_metadata_buffer.clear();
m_shader_metadata_buffer.resize(m_shader_requirements_buffer.size());
shader_set_buffer.clear();
m_shader_manager.get_handles(
shader_sources,
m_shader_requirements_buffer,
m_shader_metadata_buffer,
shader_set_buffer
);
auto shader_set_req_it = m_shader_requirements_buffer.begin();
auto shader_set_meta_it = m_shader_metadata_buffer.begin();
auto shader_set_it = shader_set_buffer.begin();
const auto prev_shader_program_count = m_shader_program_lookup.size();
for (auto [ program_meta, program_handle ] : std::ranges::views::zip( metadata, shader_programs))
{
if (not program_handle.valid())
{
if (std::ranges::any_of(shader_set_it->stages, &shader_handle::valid))
{
shader_program_data program{};
if (link_shader_program(*shader_set_it))
{
program_handle = program.handle;
program_meta = shader_program_metadata{
.geometry = shader_set_req_it->geometry,
.static_enabled = shader_set_meta_it->static_enabled,
.dynamic_enable = shader_set_meta_it->dynamic_enable
};
m_shader_program_lookup.emplace_back(program_meta, std::move(program));
}
}
++shader_set_req_it;
++shader_set_meta_it;
++shader_set_it;
}
}
const auto new_shader_programs = std::span(m_shader_program_lookup).subspan(prev_shader_program_count);
std::ranges::sort(
new_shader_programs,
prioritized_metadata_comparator{},
&shader_program_lookup_entry_type::first
);
std::ranges::inplace_merge(
m_shader_program_lookup,
m_shader_program_lookup.begin() + prev_shader_program_count,
prioritized_metadata_comparator{},
&shader_program_lookup_entry_type::first
);
}
std::optional<std::pair<zgl::shader_program_metadata, zgl::shader_program_handle>> zgl::shader_program_manager::find_shader_program(
const shading::shader_program_requirements& requirements
) {
auto shader_program_it = std::ranges::lower_bound(
m_shader_program_lookup,
requirements.geometry,
std::greater{},
[](const shader_program_lookup_entry_type& entry)
{
const auto& meta = entry.first;
return meta.geometry;
}
);
while (
shader_program_it != m_shader_program_lookup.end() and
shader_program_it->first.geometry == requirements.geometry
) {
const auto& [ meta, data ] = *shader_program_it;
const auto unwanted_static_features = meta.static_enabled & ~requirements.features;
const auto required_dynamic_features = requirements.features & ~meta.static_enabled;
const auto missing_dynamic_features = required_dynamic_features & ~meta.dynamic_enable;
if (unwanted_static_features == 0 and missing_dynamic_features == 0)
{
return std::pair{ meta, data.handle };
}
++shader_program_it;
}
return std::nullopt;
}
bool zgl::shader_program_manager::link_shader_program(
const shader_handle_set& shaders
) {
const auto program = shader_program_data{ glCreateProgram() };
if (const auto e = get_error())
{
ztu::logger::error("Error while creating shader program: %.", e.message());
return false;
}
for (const auto [ index, entry ] : std::ranges::views::enumerate(std::ranges::views::zip(
shaders.stages,
shading::stage::names
))) {
const auto& [ shader, name ] = entry;
auto attached = false;
if (shader.id)
{
glAttachShader(program.handle.id, shader.id);
if (const auto e = get_error())
{
if (shading::stage::mandatory[index])
{
ztu::logger::error("Error while attaching the mandatory % shader: %.", name, e.message());
return false;
}
ztu::logger::warn("Error while attaching the optional % shader: %.", name, e.message());
}
else
{
attached = true;
}
}
if (not attached)
{
ztu::logger::warn("Using default % shader.", name);
}
}
glLinkProgram(program.handle.id);
if (const auto e = get_error())
{
ztu::logger::error("Error while linking shader program: %.", e.message());
return false;
}
auto status = GLint{ GL_FALSE };
glGetProgramiv(program.handle.id, GL_LINK_STATUS, &status);
if (const auto e = get_error())
{
ztu::logger::error("Error while retrieving shader program link status: %.", e.message());
return false;
}
if (status == GL_FALSE) {
GLint log_length{};
glGetShaderiv(program.handle.id, GL_INFO_LOG_LENGTH, &log_length);
auto log = std::string(log_length, ' ');
glGetProgramInfoLog(program.handle.id, log_length, nullptr, log.data());
ztu::logger::error("Error while linking program:\n%", log);
return false;
}
glUseProgram(0);
if (const auto e = get_error())
{
ztu::logger::warn("Error while resetting active shader program: %.", e.message());
}
for (const auto& shader : shaders.stages)
{
if (shader.id)
{
glDetachShader(program.handle.id, shader.id);
if (const auto e = get_error())
{
ztu::logger::warn("Error while detaching shader: %.", e.message());
}
}
}
return true;
}

View File

@@ -0,0 +1,476 @@
#include "opengl/data_managers/shader_source_manager.hpp"
#include "util/logger.hpp"
#include <algorithm>
#include <cassert>
#include <numeric>
#include "opengl/shading/shader_metadata_language.hpp"
static auto mesh_feature_defines = std::array{
"#define FACE\n",
"#define LINE\n",
"#define POINT\n",
"#define V_L\n",
"#define V_RGB\n",
"#define V_A\n",
"#define LIGHTING\n",
"#define TEXTURE\n",
"#define U_RGBA\n",
};
static auto point_cloud_feature_defines = std::array{
"#define SQUARE\n",
"#define LIGHTING\n",
"#define V_L\n",
"#define V_RGB\n",
"#define V_A\n",
"#define U_RGBA\n",
"#define RAINBOW\n"
};
struct prioritized_feature_set_comparator
{
using type = zgl::shader_features_set<zgl::shading::features::generic::type>;
bool operator()(const type& a, const type& b) const noexcept
{
static constexpr auto more_features = std::popcount<zgl::shading::features::generic::type>;
return std::ranges::lexicographical_compare(
std::array{ a.dynamic_enable, a.features, a.static_enable },
std::array{ b.dynamic_enable, b.features, b.static_enable },
std::greater{},
more_features,
more_features
);
}
};
struct prioritized_metadata_comparator
{
using type = zgl::shader_source_metadata;
bool operator()(const type& a, const type& b) const noexcept
{
if (a.geometry != b.geometry)
{
return a.geometry > b.geometry;
}
if (a.stage != b.stage)
{
return a.stage > b.stage;
}
const auto features_a = a.generic_feature_set();
const auto features_b = b.generic_feature_set();
return feature_set_comparator(features_a, features_b);
}
private:
prioritized_feature_set_comparator feature_set_comparator{};
};
void zgl::shader_source_manager::process(
const dynamic_shader_source_store& shader_sources
) {
namespace language = shading::shader_metadata_language;
for (const auto& [ id, shader_source ] : shader_sources)
{
m_value_token_buffer.clear();
m_declaration_token_count_buffer.clear();
std::ranges::fill(
m_declaration_type_index_buffer,
static_cast<std::size_t>(language::declaration_type::invalid)
);
tokenize_declarations(shader_source);
const auto metadata = parse_metadata_from_tokens();
if (not metadata)
{
ztu::logger::warn("Ignoring shader % as it contains malformed metadata.", id);
continue;
}
// Sorted insert should be faster than std::sort and std::unique
// for small numbers of elements and high numbers of duplicates.
const auto it = std::ranges::upper_bound(
m_shader_source_lookup,
*metadata,
prioritized_metadata_comparator{},
&std::pair<shader_source_metadata, dynamic_shader_source_store::id_type>::first
);
if (it != m_shader_source_lookup.end() and it->first == *metadata)
{
continue;
}
m_shader_source_lookup.emplace(it, *metadata, id);
}
}
void zgl::shader_source_manager::get_shader_sources(
const dynamic_shader_source_store& shader_sources,
std::span<const shading::shader_source_requirements> requirements,
std::span<preprocessed_shader_source_metadata> metadata,
std::vector<const char*>& shader_strings
) {
assert(requirements.size() == metadata.size());
static constexpr auto max_shader_strings = std::max(
mesh_feature_defines.size(),
point_cloud_feature_defines.size()
) + 1;
shader_strings.reserve(max_shader_strings);
std::ranges::transform(
requirements,
metadata.begin(),
[&](const shading::shader_source_requirements& req)
{
auto res = preprocessed_shader_source_metadata{};
auto source_it = std::ranges::lower_bound(
m_shader_source_lookup,
std::pair{ req.geometry, req.stage },
std::greater{},
[](const source_lookup_entry_type& entry)
{
const auto& meta = entry.first;
return std::pair{ meta.geometry, meta.stage };
}
);
dynamic_shader_source_store::id_type source_id{};
shading::features::generic::type to_be_enabled{};
while (
source_it != m_shader_source_lookup.end() and
source_it->first.geometry == req.geometry and
source_it->first.stage == req.stage
) {
const auto& [ meta, id ] = *source_it;
const auto& [ features, static_enable, dynamic_enable ] = meta.generic_feature_set();
const auto missing_features = req.features & ~features;
const auto unwanted_features = ~req.features & features;
const auto fixed_unwanted_features = unwanted_features & ~static_enable & ~dynamic_enable;
if (missing_features == 0 and fixed_unwanted_features == 0)
{
to_be_enabled = req.features & static_enable;
source_id = id;
res.static_enabled = features & ~dynamic_enable & ~unwanted_features;
res.dynamic_enable = dynamic_enable;
break;
}
++source_it;
}
if (source_id)
{
const auto [ shader_source_it, source_found ] = shader_sources.find(source_id);
if (source_found)
{
get_define_strings(
req.geometry,
to_be_enabled,
res.string_count,
shader_strings
);
}
}
return res;
}
);
}
void zgl::shader_source_manager::get_define_strings(
const shading::model_geometry::types geometry,
shading::features::generic::type features,
shading::features::generic::type& feature_count,
std::vector<const char*>& defines
) {
std::span<const char*> all_defines;
switch (geometry)
{
case shading::model_geometry::types::mesh:
all_defines = mesh_feature_defines;
break;
case shading::model_geometry::types::point_cloud:
all_defines = point_cloud_feature_defines;
break;
default:
std::unreachable();
}
auto index = std::size_t{};
while (features != 0)
{
if ((features & 1) != 0)
{
defines.push_back(all_defines[index]);
++feature_count;
}
features >>= 1;
++index;
}
}
void zgl::shader_source_manager::tokenize_declarations(
std::string_view source_rest
) {
namespace language = shading::shader_metadata_language;
auto offset = std::string_view::size_type{};
auto keyword = language::declaration_prefix;
while ((offset = source_rest.find(keyword)) != std::string_view::npos)
{
const auto current_token_count = m_value_token_buffer.size();
auto line_end = source_rest.find('\n', offset);
if (line_end == std::string_view::npos)
{
line_end = source_rest.length();
}
auto declaration = source_rest.substr(offset, line_end - offset);
if ((offset = declaration.find(language::title_separator)) == std::string_view::npos)
{
continue;
}
const auto title = declaration.substr(0, offset);
if (const auto it = language::declaration_lookup.find(title); it != language::declaration_lookup.end())
{
const auto declaration_type = static_cast<std::size_t>(it->second);
m_declaration_type_index_buffer[declaration_type] = m_declaration_token_count_buffer.size();
}
else
{
continue;
}
declaration = declaration.substr(offset);
if (not declaration.empty() and declaration.front() == language::value_separator)
{
declaration = declaration.substr(sizeof(language::value_separator), declaration.length());
}
while ((offset = declaration.find(language::value_separator)) != std::string_view::npos)
{
m_value_token_buffer.emplace_back(declaration.substr(0, offset));
declaration = declaration.substr(offset + sizeof(language::value_separator));
}
if (not declaration.empty())
{
m_value_token_buffer.emplace_back(declaration);
}
m_declaration_token_count_buffer.emplace_back(
m_value_token_buffer.size() - current_token_count
);
source_rest = source_rest.substr(line_end + sizeof('\n'));
keyword = language::declaration_prefix.substr(sizeof('\n'));
}
}
bool zgl::shader_source_manager::parse_stage_declaration(
std::span<const std::string_view> values,
shader_source_metadata& metadata
) {
namespace language = shading::shader_metadata_language;
if (values.size() != 1)
{
ztu::logger::warn("Invalid stage declaration: Expected exactly one token but got %.", values.size());
return false;
}
const auto value = values.front();
if (const auto it = language::stage_lookup.find(value); it != language::stage_lookup.end())
{
metadata.stage = it->second;
}
else
{
ztu::logger::warn("Invalid stage declaration: Unknown stage %.", value);
return false;
}
return true;
}
bool zgl::shader_source_manager::parse_geometry_declaration(
std::span<const std::string_view> values,
shader_source_metadata& metadata
) {
namespace language = shading::shader_metadata_language;
if (values.size() != 1)
{
ztu::logger::warn("Invalid geometry declaration: Expected exactly one token but got %.", values.size());
return false;
}
const auto value = values.front();
if (const auto it = language::geometry_lookup.find(value); it != language::geometry_lookup.end())
{
metadata.geometry = it->second;
}
else
{
ztu::logger::warn("Invalid geometry declaration: Unknown geometry %.", value);
return false;
}
return true;
}
template<typename T>
void zgl::shader_source_manager::parse_feature_tokens(
std::span<const std::string_view> values,
const ztu::string_lookup<T>& feature_lookup,
T& features
) {
features = {};
for (const auto value : values)
{
if (const auto it = feature_lookup.find(value); it != feature_lookup.end())
{
features |= it->second;
}
else
{
ztu::logger::warn("Ignoring unknown feature token %.", value);
}
}
}
bool zgl::shader_source_manager::parse_features_declaration(
std::span<const std::string_view> values,
shader_source_metadata& metadata
) {
namespace language = shading::shader_metadata_language;
switch (metadata.geometry)
{
case shading::model_geometry::types::mesh:
parse_feature_tokens(values, language::mesh_feature_lookup, metadata.feature_set.mesh.features);
break;
case shading::model_geometry::types::point_cloud:
parse_feature_tokens(values, language::point_cloud_feature_lookup, metadata.feature_set.point_cloud.features);
break;
default:
ztu::logger::warn("Internal error: Unknown geometry index %.", static_cast<int>(metadata.geometry));
return false;
}
return true;
}
bool zgl::shader_source_manager::parse_static_enable_declaration(
std::span<const std::string_view> values,
shader_source_metadata& metadata
) {
namespace language = shading::shader_metadata_language;
switch (metadata.geometry)
{
case shading::model_geometry::types::mesh:
parse_feature_tokens(values, language::mesh_feature_lookup, metadata.feature_set.mesh.static_enable);
break;
case shading::model_geometry::types::point_cloud:
parse_feature_tokens(values, language::point_cloud_feature_lookup, metadata.feature_set.point_cloud.static_enable);
break;
default:
ztu::logger::warn("Internal error: Unknown geometry index %.", static_cast<int>(metadata.geometry));
return false;
}
return true;
}
bool zgl::shader_source_manager::parse_dynamic_enable_declaration(
std::span<const std::string_view> values,
shader_source_metadata& metadata
) {
namespace language = shading::shader_metadata_language;
switch (metadata.geometry)
{
case shading::model_geometry::types::mesh:
parse_feature_tokens(values, language::mesh_feature_lookup, metadata.feature_set.mesh.dynamic_enable);
break;
case shading::model_geometry::types::point_cloud:
parse_feature_tokens(values, language::point_cloud_feature_lookup, metadata.feature_set.point_cloud.dynamic_enable);
break;
default:
ztu::logger::warn("Internal error: Unknown geometry index %.", static_cast<int>(metadata.geometry));
return false;
}
return true;
}
std::optional<zgl::shader_source_metadata> zgl::shader_source_manager::parse_metadata_from_tokens()
{
namespace language = shading::shader_metadata_language;
using namespace std::string_view_literals;
shader_source_metadata data;
for (const auto [ type, name, parser ] :
{
std::make_tuple(language::declaration_type::stage, "stage"sv, &parse_stage_declaration),
std::make_tuple(language::declaration_type::geometry, "geometry"sv, &parse_geometry_declaration),
std::make_tuple(language::declaration_type::features, "features"sv, &parse_features_declaration),
std::make_tuple(language::declaration_type::static_enable, "static_enable"sv, &parse_static_enable_declaration),
std::make_tuple(language::declaration_type::dynamic_enable, "dynamic_enable"sv, &parse_dynamic_enable_declaration)
}
) {
const auto index = m_declaration_type_index_buffer[static_cast<std::size_t>(type)];
if (index == static_cast<std::size_t>(language::declaration_type::invalid))
{
ztu::logger::warn("Shader metadata error: Missing % declaration.", name);
return std::nullopt;
}
const auto value_token_offset = std::accumulate(
m_declaration_token_count_buffer.begin(),
m_declaration_token_count_buffer.begin() + index,
std::size_t{}
);
const auto value_token_count = m_declaration_token_count_buffer[index];
if (not parser(std::span(m_value_token_buffer).subspan(value_token_offset, value_token_count), data))
{
return std::nullopt;
}
}
return data;
}

View File

@@ -0,0 +1,150 @@
#include "opengl/data_managers/texture_manager.hpp"
#include <ranges>
#include "util/logger.hpp"
#include "opengl/error.hpp"
void zgl::texture_manager::process(
dynamic_texture_store& store
) {
m_texture_buffer.clear();
for (const auto& [ id, texture ] : store)
{
if (not m_resource_manager.has_resource(id))
{
m_texture_buffer.emplace_back(id, texture);
}
}
m_texture_id_buffer.resize(m_texture_buffer.size());
glGenTextures(m_texture_id_buffer.size(), m_texture_id_buffer.data());
if (const auto e = get_error())
{
ztu::logger::error("Error while creating % textures: %.", m_texture_id_buffer.size(), e.message());
return;
}
for (auto [ entry, texture_id ] : std::ranges::views::zip(m_texture_buffer, m_texture_id_buffer))
{
auto [ store_id, texture ] = entry;
GLenum format;
switch (texture.components()) {
using enum components::texture::flags;
case luminance:
format = GL_LUMINANCE;
break;
case luminance | alpha:
format = GL_LUMINANCE_ALPHA;
break;
case red | green | blue:
format = GL_RGB;
break;
case red | green | blue | alpha:
format = GL_RGBA;
break;
default:
// TODO create a print statement for these enum flaggy thingies
ztu::logger::error("Unsupported texture component configuration: %.", texture.components());
continue;
}
glBindTexture(GL_TEXTURE_2D, texture_id);
if (const auto e = get_error())
{
ztu::logger::error("Error while binding texture %: %.", texture_id, e.message());
return;
}
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
if (const auto e = get_error())
{
ztu::logger::warn("Error while setting texture parameters: %.", e.message());
}
glTexImage2D(
GL_TEXTURE_2D,
0,
GL_RGBA8,
texture.width(),
texture.height(),
0,
format,
GL_UNSIGNED_BYTE,
texture.data()
);
if (const auto e = get_error())
{
ztu::logger::error("Error while setting texture data: %.", e.message());
continue;
}
glGenerateMipmap(GL_TEXTURE_2D);
if (const auto e = get_error())
{
ztu::logger::warn("Error while generating texture mipmaps: %.", e.message());
}
const auto meta = metadata_type{
.components = texture.components()
};
m_resource_manager.add_resource(store_id, texture_id, meta);
texture_id = {};
}
glBindTexture(GL_TEXTURE_2D, 0);
const auto valid_texture_ids = std::ranges::remove(
m_texture_id_buffer,
0
);
const auto invalid_texture_count = m_texture_id_buffer.size() - valid_texture_ids.size();
glDeleteTextures(
invalid_texture_count,
m_texture_id_buffer.data()
);
if (const auto e = get_error())
{
ztu::logger::error("Error while deleting % textures: %.", invalid_texture_count, e.message());
}
}
std::optional<zgl::texture_manager::handle_type> zgl::texture_manager::get_handle(
const store_id_type id
) {
return m_resource_manager
.get_resource(id)
.transform(
[](auto& resource)
{
return *reinterpret_cast<handle_type*>(resource);
}
);
}
void zgl::texture_manager::collect_garbage(const bool force)
{
m_resource_manager.collect_garbage();
if (force or m_resource_manager.count_garbage() >= min_garbage_collection_count)
{
m_texture_id_buffer.clear();
m_resource_manager.extract_garbage(m_texture_id_buffer);
glDeleteTextures(
m_texture_id_buffer.size(),
m_texture_id_buffer.data()
);
}
}