Extract OpenGL shader uniform catalog
This commit is contained in:
@@ -133,7 +133,9 @@ Known local toolchain state:
|
||||
framebuffer status naming used by `RTT` diagnostics. It also validates
|
||||
Shape index-type and fill/stroke primitive-mode mapping used by the legacy
|
||||
mesh draw path, plus the PanoPainter shader attribute binding catalog used
|
||||
by legacy `Shader` creation.
|
||||
by legacy `Shader` creation. It also owns the PanoPainter shader uniform
|
||||
catalog and legacy hash mapping used by `Shader` active-uniform discovery and
|
||||
the uniform uniqueness check.
|
||||
- `windows-msvc-vcpkg-headless` validates manifest install/configure/build/test
|
||||
for the current headless component matrix; see DEBT-0007 for remaining app
|
||||
and platform triplet migration.
|
||||
|
||||
@@ -395,8 +395,10 @@ format mapping for `Texture2D` image uploads and framebuffer status naming for
|
||||
extension, upload-format, framebuffer diagnostic, and mesh draw-mode
|
||||
interpretation to that backend library. The PanoPainter shader attribute
|
||||
binding catalog also lives in `pp_renderer_gl` and is consumed by legacy
|
||||
`Shader` creation. The existing renderer classes are not yet fully behind the
|
||||
renderer interfaces.
|
||||
`Shader` creation. Shader uniform hashing, catalog validation, active-uniform
|
||||
mapping, and the legacy uniform uniqueness check now delegate to
|
||||
`pp_renderer_gl` as well. The existing renderer classes are not yet fully
|
||||
behind the renderer interfaces.
|
||||
|
||||
Implementation tasks:
|
||||
|
||||
@@ -641,7 +643,9 @@ Results:
|
||||
mode mapping. Shader attribute binding catalog validation covers the current
|
||||
`pos`, `uvs`, `uvs2`, `col`, and `nor` bindings and rejects empty, unnamed,
|
||||
null-name, and duplicate-name catalogs while preserving legacy shared
|
||||
locations.
|
||||
locations. Shader uniform catalog validation covers the 43 legacy uniform
|
||||
names used by `Shader`, preserves the legacy hash ids, and rejects empty,
|
||||
unnamed, null-name, mismatched-hash, and duplicate-name catalogs.
|
||||
- PowerShell analyze automation returns JSON summaries and includes the shader
|
||||
validation target.
|
||||
- `windows-msvc-vcpkg-headless` configured through the Visual Studio bundled
|
||||
|
||||
@@ -2,9 +2,23 @@
|
||||
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
#include <string_view>
|
||||
|
||||
namespace pp::renderer::gl {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr std::uint16_t djb2_hash(std::string_view text) noexcept
|
||||
{
|
||||
std::uint16_t value = 5381U;
|
||||
for (std::size_t i = text.size(); i > 0U; --i) {
|
||||
value = static_cast<std::uint16_t>(text[i - 1U]) + static_cast<std::uint16_t>(33U * value);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
std::span<const OpenGlAttributeBinding> panopainter_shader_attribute_bindings() noexcept
|
||||
{
|
||||
static constexpr std::array<OpenGlAttributeBinding, 5> bindings {
|
||||
@@ -40,4 +54,90 @@ pp::foundation::Status validate_shader_attribute_bindings(
|
||||
return pp::foundation::Status::success();
|
||||
}
|
||||
|
||||
std::span<const OpenGlUniformName> panopainter_shader_uniform_names() noexcept
|
||||
{
|
||||
static constexpr std::array<OpenGlUniformName, 43> uniforms {
|
||||
OpenGlUniformName { .name = "mvp", .id = djb2_hash("mvp") },
|
||||
OpenGlUniformName { .name = "tex", .id = djb2_hash("tex") },
|
||||
OpenGlUniformName { .name = "tex_alpha", .id = djb2_hash("tex_alpha") },
|
||||
OpenGlUniformName { .name = "tex_fg", .id = djb2_hash("tex_fg") },
|
||||
OpenGlUniformName { .name = "tex_bg", .id = djb2_hash("tex_bg") },
|
||||
OpenGlUniformName { .name = "tex_mix", .id = djb2_hash("tex_mix") },
|
||||
OpenGlUniformName { .name = "tex_mix_alpha", .id = djb2_hash("tex_mix_alpha") },
|
||||
OpenGlUniformName { .name = "tex_mask", .id = djb2_hash("tex_mask") },
|
||||
OpenGlUniformName { .name = "tex_dual", .id = djb2_hash("tex_dual") },
|
||||
OpenGlUniformName { .name = "tex_stroke", .id = djb2_hash("tex_stroke") },
|
||||
OpenGlUniformName { .name = "tex_pattern", .id = djb2_hash("tex_pattern") },
|
||||
OpenGlUniformName { .name = "pattern_offset", .id = djb2_hash("pattern_offset") },
|
||||
OpenGlUniformName { .name = "pattern_alpha", .id = djb2_hash("pattern_alpha") },
|
||||
OpenGlUniformName { .name = "mix_alpha", .id = djb2_hash("mix_alpha") },
|
||||
OpenGlUniformName { .name = "opacity", .id = djb2_hash("opacity") },
|
||||
OpenGlUniformName { .name = "wet", .id = djb2_hash("wet") },
|
||||
OpenGlUniformName { .name = "lock", .id = djb2_hash("lock") },
|
||||
OpenGlUniformName { .name = "col", .id = djb2_hash("col") },
|
||||
OpenGlUniformName { .name = "tof", .id = djb2_hash("tof") },
|
||||
OpenGlUniformName { .name = "tsz", .id = djb2_hash("tsz") },
|
||||
OpenGlUniformName { .name = "alpha", .id = djb2_hash("alpha") },
|
||||
OpenGlUniformName { .name = "mask", .id = djb2_hash("mask") },
|
||||
OpenGlUniformName { .name = "resolution", .id = djb2_hash("resolution") },
|
||||
OpenGlUniformName { .name = "highlight", .id = djb2_hash("highlight") },
|
||||
OpenGlUniformName { .name = "blend_mode", .id = djb2_hash("blend_mode") },
|
||||
OpenGlUniformName { .name = "dual_blend_mode", .id = djb2_hash("dual_blend_mode") },
|
||||
OpenGlUniformName { .name = "noise", .id = djb2_hash("noise") },
|
||||
OpenGlUniformName { .name = "dir", .id = djb2_hash("dir") },
|
||||
OpenGlUniformName { .name = "use_dual", .id = djb2_hash("use_dual") },
|
||||
OpenGlUniformName { .name = "use_pattern", .id = djb2_hash("use_pattern") },
|
||||
OpenGlUniformName { .name = "light_dir", .id = djb2_hash("light_dir") },
|
||||
OpenGlUniformName { .name = "mode", .id = djb2_hash("mode") },
|
||||
OpenGlUniformName { .name = "ambient", .id = djb2_hash("ambient") },
|
||||
OpenGlUniformName { .name = "pattern_invert", .id = djb2_hash("pattern_invert") },
|
||||
OpenGlUniformName { .name = "pattern_scale", .id = djb2_hash("pattern_scale") },
|
||||
OpenGlUniformName { .name = "pattern_bright", .id = djb2_hash("pattern_bright") },
|
||||
OpenGlUniformName { .name = "pattern_contr", .id = djb2_hash("pattern_contr") },
|
||||
OpenGlUniformName { .name = "pattern_depth", .id = djb2_hash("pattern_depth") },
|
||||
OpenGlUniformName { .name = "patt_blend_mode", .id = djb2_hash("patt_blend_mode") },
|
||||
OpenGlUniformName { .name = "colorize", .id = djb2_hash("colorize") },
|
||||
OpenGlUniformName { .name = "dual_alpha", .id = djb2_hash("dual_alpha") },
|
||||
OpenGlUniformName { .name = "use_fragcoord", .id = djb2_hash("use_fragcoord") },
|
||||
OpenGlUniformName { .name = "draw_outline", .id = djb2_hash("draw_outline") },
|
||||
};
|
||||
|
||||
return uniforms;
|
||||
}
|
||||
|
||||
std::uint16_t shader_uniform_id(std::string_view name) noexcept
|
||||
{
|
||||
return djb2_hash(name);
|
||||
}
|
||||
|
||||
pp::foundation::Status validate_shader_uniform_names(
|
||||
std::span<const OpenGlUniformName> uniforms) noexcept
|
||||
{
|
||||
if (uniforms.empty()) {
|
||||
return pp::foundation::Status::invalid_argument("shader uniform catalog is empty");
|
||||
}
|
||||
|
||||
for (std::size_t i = 0; i < uniforms.size(); ++i) {
|
||||
if (uniforms[i].name == nullptr || uniforms[i].name[0] == '\0') {
|
||||
return pp::foundation::Status::invalid_argument("shader uniform has no name");
|
||||
}
|
||||
|
||||
if (uniforms[i].id != shader_uniform_id(uniforms[i].name)) {
|
||||
return pp::foundation::Status::invalid_argument("shader uniform hash does not match its name");
|
||||
}
|
||||
|
||||
for (std::size_t j = i + 1; j < uniforms.size(); ++j) {
|
||||
if (uniforms[j].name != nullptr && std::strcmp(uniforms[i].name, uniforms[j].name) == 0) {
|
||||
return pp::foundation::Status::invalid_argument("shader uniform name is duplicated");
|
||||
}
|
||||
|
||||
if (uniforms[i].id == uniforms[j].id) {
|
||||
return pp::foundation::Status::invalid_argument("shader uniform hash is duplicated");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return pp::foundation::Status::success();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include <cstdint>
|
||||
#include <span>
|
||||
#include <string_view>
|
||||
|
||||
namespace pp::renderer::gl {
|
||||
|
||||
@@ -12,8 +13,17 @@ struct OpenGlAttributeBinding {
|
||||
std::uint32_t location = 0;
|
||||
};
|
||||
|
||||
struct OpenGlUniformName {
|
||||
const char* name = "";
|
||||
std::uint16_t id = 0;
|
||||
};
|
||||
|
||||
[[nodiscard]] std::span<const OpenGlAttributeBinding> panopainter_shader_attribute_bindings() noexcept;
|
||||
[[nodiscard]] pp::foundation::Status validate_shader_attribute_bindings(
|
||||
std::span<const OpenGlAttributeBinding> bindings) noexcept;
|
||||
[[nodiscard]] std::span<const OpenGlUniformName> panopainter_shader_uniform_names() noexcept;
|
||||
[[nodiscard]] std::uint16_t shader_uniform_id(std::string_view name) noexcept;
|
||||
[[nodiscard]] pp::foundation::Status validate_shader_uniform_names(
|
||||
std::span<const OpenGlUniformName> uniforms) noexcept;
|
||||
|
||||
}
|
||||
|
||||
@@ -262,7 +262,7 @@ bool Shader::create(const std::string& vertex, const std::string& fragment)
|
||||
{
|
||||
glGetActiveUniform(ps, (GLuint)i, bufSize, &length, &size, &type, name);
|
||||
name[length] = 0;
|
||||
kShaderUniform id = (kShaderUniform)const_hash(name);
|
||||
kShaderUniform id = static_cast<kShaderUniform>(pp::renderer::gl::shader_uniform_id(name));
|
||||
if (m_umap.find(id) != m_umap.end())
|
||||
LOG("UNIFORM ALREADY DEFINED: %s", name);
|
||||
m_umap[id] = glGetUniformLocation(ps, name);
|
||||
@@ -422,58 +422,7 @@ void ShaderManager::invalidate()
|
||||
|
||||
bool check_uniform_uniqueness()
|
||||
{
|
||||
std::vector<uint16_t> v = {
|
||||
const_hash("mvp"),
|
||||
const_hash("tex"),
|
||||
const_hash("tex_alpha"),
|
||||
const_hash("tex_fg"),
|
||||
const_hash("tex_bg"),
|
||||
const_hash("tex_mix"),
|
||||
const_hash("tex_mix_alpha"),
|
||||
const_hash("tex_mask"),
|
||||
const_hash("tex_dual"),
|
||||
const_hash("tex_stroke"),
|
||||
const_hash("tex_pattern"),
|
||||
const_hash("pattern_offset"),
|
||||
const_hash("pattern_alpha"),
|
||||
const_hash("mix_alpha"),
|
||||
const_hash("opacity"),
|
||||
const_hash("wet"),
|
||||
const_hash("lock"),
|
||||
const_hash("col"),
|
||||
const_hash("tof"),
|
||||
const_hash("tsz"),
|
||||
const_hash("alpha"),
|
||||
const_hash("mask"),
|
||||
const_hash("resolution"),
|
||||
const_hash("highlight"),
|
||||
const_hash("blend_mode"),
|
||||
const_hash("dual_blend_mode"),
|
||||
const_hash("noise"),
|
||||
const_hash("dir"),
|
||||
const_hash("use_dual"),
|
||||
const_hash("use_pattern"),
|
||||
const_hash("light_dir"),
|
||||
const_hash("mode"),
|
||||
const_hash("ambient"),
|
||||
const_hash("pattern_invert"),
|
||||
const_hash("pattern_scale"),
|
||||
const_hash("pattern_bright"),
|
||||
const_hash("pattern_contr"),
|
||||
const_hash("pattern_depth"),
|
||||
const_hash("patt_blend_mode"),
|
||||
const_hash("colorize"),
|
||||
const_hash("dual_alpha"),
|
||||
const_hash("use_fragcoord"),
|
||||
const_hash("draw_outline"),
|
||||
};
|
||||
std::sort(v.begin(), v.end());
|
||||
int last = 0;
|
||||
for (auto o : v)
|
||||
{
|
||||
if (o == last)
|
||||
return false;
|
||||
last = o;
|
||||
}
|
||||
return true;
|
||||
return pp::renderer::gl::validate_shader_uniform_names(
|
||||
pp::renderer::gl::panopainter_shader_uniform_names())
|
||||
.ok();
|
||||
}
|
||||
|
||||
@@ -201,6 +201,52 @@ void rejects_invalid_shader_attribute_binding_catalogs(pp::tests::Harness& h)
|
||||
PP_EXPECT(h, pp::renderer::gl::validate_shader_attribute_bindings(duplicate_location).ok());
|
||||
}
|
||||
|
||||
void exposes_shader_uniform_catalog(pp::tests::Harness& h)
|
||||
{
|
||||
const auto uniforms = pp::renderer::gl::panopainter_shader_uniform_names();
|
||||
|
||||
PP_EXPECT(h, uniforms.size() == 43U);
|
||||
PP_EXPECT(h, pp::renderer::gl::validate_shader_uniform_names(uniforms).ok());
|
||||
PP_EXPECT(h, std::strcmp(uniforms[0].name, "mvp") == 0);
|
||||
PP_EXPECT(h, uniforms[0].id == pp::renderer::gl::shader_uniform_id("mvp"));
|
||||
PP_EXPECT(h, uniforms[0].id == 40696U);
|
||||
PP_EXPECT(h, std::strcmp(uniforms[1].name, "tex") == 0);
|
||||
PP_EXPECT(h, uniforms[1].id == pp::renderer::gl::shader_uniform_id("tex"));
|
||||
PP_EXPECT(h, uniforms[1].id == 48854U);
|
||||
PP_EXPECT(h, pp::renderer::gl::shader_uniform_id("pattern_contr") == 54920U);
|
||||
PP_EXPECT(h, pp::renderer::gl::shader_uniform_id("draw_outline") == 36178U);
|
||||
}
|
||||
|
||||
void rejects_invalid_shader_uniform_catalogs(pp::tests::Harness& h)
|
||||
{
|
||||
const std::array<pp::renderer::gl::OpenGlUniformName, 0> empty {};
|
||||
const std::array unnamed {
|
||||
pp::renderer::gl::OpenGlUniformName { .name = "", .id = 0 },
|
||||
};
|
||||
const std::array null_named {
|
||||
pp::renderer::gl::OpenGlUniformName { .name = nullptr, .id = 0 },
|
||||
};
|
||||
const std::array mismatched_hash {
|
||||
pp::renderer::gl::OpenGlUniformName { .name = "mvp", .id = 1 },
|
||||
};
|
||||
const std::array duplicate_name {
|
||||
pp::renderer::gl::OpenGlUniformName {
|
||||
.name = "mvp",
|
||||
.id = pp::renderer::gl::shader_uniform_id("mvp"),
|
||||
},
|
||||
pp::renderer::gl::OpenGlUniformName {
|
||||
.name = "mvp",
|
||||
.id = pp::renderer::gl::shader_uniform_id("mvp"),
|
||||
},
|
||||
};
|
||||
|
||||
PP_EXPECT(h, !pp::renderer::gl::validate_shader_uniform_names(empty).ok());
|
||||
PP_EXPECT(h, !pp::renderer::gl::validate_shader_uniform_names(unnamed).ok());
|
||||
PP_EXPECT(h, !pp::renderer::gl::validate_shader_uniform_names(null_named).ok());
|
||||
PP_EXPECT(h, !pp::renderer::gl::validate_shader_uniform_names(mismatched_hash).ok());
|
||||
PP_EXPECT(h, !pp::renderer::gl::validate_shader_uniform_names(duplicate_name).ok());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int main()
|
||||
@@ -216,5 +262,7 @@ int main()
|
||||
harness.run("maps_shape_index_and_primitive_modes", maps_shape_index_and_primitive_modes);
|
||||
harness.run("exposes_shader_attribute_binding_catalog", exposes_shader_attribute_binding_catalog);
|
||||
harness.run("rejects_invalid_shader_attribute_binding_catalogs", rejects_invalid_shader_attribute_binding_catalogs);
|
||||
harness.run("exposes_shader_uniform_catalog", exposes_shader_uniform_catalog);
|
||||
harness.run("rejects_invalid_shader_uniform_catalogs", rejects_invalid_shader_uniform_catalogs);
|
||||
return harness.finish();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user