Skip to content

Commit

Permalink
Zephyr: Renderer: implement STD430 and STD140 layouting code
Browse files Browse the repository at this point in the history
  • Loading branch information
fleroviux committed Sep 21, 2024
1 parent 1cc01e7 commit b309ce6
Show file tree
Hide file tree
Showing 5 changed files with 425 additions and 0 deletions.
4 changes: 4 additions & 0 deletions zephyr/renderer/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ set(HEADERS_PUBLIC
include/zephyr/renderer/component/camera.hpp
include/zephyr/renderer/component/mesh.hpp
include/zephyr/renderer/engine/geometry_cache.hpp
include/zephyr/renderer/glsl/std140_buffer_layout.hpp
include/zephyr/renderer/glsl/std430_buffer_layout.hpp
include/zephyr/renderer/glsl/type.hpp
include/zephyr/renderer/glsl/variable_list.hpp
include/zephyr/renderer/resource/geometry.hpp
include/zephyr/renderer/resource/material.hpp
include/zephyr/renderer/resource/resource.hpp
Expand Down
142 changes: 142 additions & 0 deletions zephyr/renderer/include/zephyr/renderer/glsl/std140_buffer_layout.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@

#pragma once

#include <zephyr/renderer/glsl/variable_list.hpp>
#include <zephyr/panic.hpp>
#include <algorithm>
#include <string>
#include <span>
#include <unordered_map>
#include <unordered_set>
#include <vector>

namespace zephyr::glsl {

class STD140BufferLayout {
public:
struct Variable : VariableList::Variable {
size_t buffer_offset;
size_t data_size;
size_t data_alignment;
};

STD140BufferLayout() = default;

explicit STD140BufferLayout(const VariableList& variable_list) {
Build(variable_list);
}

[[nodiscard]] std::span<const Variable> GetVariables() const {
return m_variables;
}

[[nodiscard]] size_t Size() const {
return m_size;
}

const Variable& GetVariable(const std::string& name) const {
if(auto match = m_variable_map.find(name); match != m_variable_map.end()) {
return *match->second;
}

ZEPHYR_PANIC("No variable named '{}' found in std140 buffer layout", name);
}

private:

void Build(const VariableList& variable_list) {
BuildVariableArrayWithOptimalSorting(variable_list);
ComputeBufferSizeAndVariableOffsets();
}

void BuildVariableArrayWithOptimalSorting(const VariableList& variable_list) {
constexpr size_t k_vec4_size = sizeof(f32) * 4;

for(const VariableList::Variable& list_variable : variable_list.GetVariables()) {
Variable& variable = m_variables.emplace_back(list_variable);

const bool is_array = variable.array_size != 0u;

size_t type_size = GLSLTypeToSize(variable.type);

if(is_array) {
const size_t type_size_mod_vec4 = type_size % k_vec4_size;
if(type_size_mod_vec4 != 0) {
type_size += k_vec4_size - type_size_mod_vec4;
}

const size_t array_byte_size = type_size * variable.array_size;
variable.data_size = array_byte_size;
variable.data_alignment = type_size / GetNumberOfVectorsFromGrade(variable.type.GetGrade());
} else {
variable.data_size = type_size;
variable.data_alignment = GLSLTypeToAlignment(variable.type);
}
}

std::sort(m_variables.begin(), m_variables.end(), [](const Variable& a, const Variable& b) {
return a.data_alignment > b.data_alignment;
});

for(auto& variable : m_variables) {
m_variable_map[variable.name] = &variable;
}
}

void ComputeBufferSizeAndVariableOffsets() {
size_t buffer_offset = 0u;

for(auto& variable : m_variables) {
const size_t remainder = buffer_offset % variable.data_alignment;

if(remainder != 0u) {
buffer_offset += variable.data_alignment - remainder;
}

variable.buffer_offset = buffer_offset;
buffer_offset += variable.data_size;
}

m_size = buffer_offset;
}

[[nodiscard]] static size_t GLSLTypeToSize(const Type& type) {
return GetNumberOfComponentsFromGrade(type.GetGrade()) * GetScalarTypeSizeInBytes(type.GetScalarType());
}

[[nodiscard]] static size_t GLSLTypeToAlignment(const Type& type) {
return GLSLTypeToSize(type) / GetNumberOfVectorsFromGrade(type.GetGrade());
}

[[nodiscard]] static size_t GetScalarTypeSizeInBytes(Type::ScalarType type) {
return type == Type::ScalarType::F64 ? 8u : 4u;
}

[[nodiscard]] static size_t GetNumberOfComponentsFromGrade(Type::Grade grade) {
switch(grade) {
case Type::Grade::Scalar: return 1u;
case Type::Grade::Vec2: return 2u;
case Type::Grade::Vec3: return 4u;
case Type::Grade::Vec4: return 4u;
case Type::Grade::Mat4: return 16u;
default: ZEPHYR_PANIC("Unhandled GLSL type grade: {}", (int)grade);
}
}

[[nodiscard]] static size_t GetNumberOfVectorsFromGrade(Type::Grade grade) {
switch(grade) {
case Type::Grade::Scalar: return 1u;
case Type::Grade::Vec2: return 1u;
case Type::Grade::Vec3: return 1u;
case Type::Grade::Vec4: return 1u;
case Type::Grade::Mat4: return 4u;
default: ZEPHYR_PANIC("Unhandled GLSL type grade: {}", (int)grade);
}
}

std::vector<Variable> m_variables;
std::unordered_map<std::string, Variable*> m_variable_map;
std::size_t m_size{};
};

} // namespace zephyr::glsl
130 changes: 130 additions & 0 deletions zephyr/renderer/include/zephyr/renderer/glsl/std430_buffer_layout.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@

#pragma once

#include <zephyr/renderer/glsl/variable_list.hpp>
#include <zephyr/panic.hpp>
#include <algorithm>
#include <string>
#include <span>
#include <unordered_map>
#include <unordered_set>
#include <vector>

namespace zephyr::glsl {

class STD430BufferLayout {
public:
struct Variable : VariableList::Variable {
size_t buffer_offset;
size_t data_size;
size_t data_alignment;
};

STD430BufferLayout() = default;

explicit STD430BufferLayout(const VariableList& variable_list) {
Build(variable_list);
}

[[nodiscard]] std::span<const Variable> GetVariables() const {
return m_variables;
}

[[nodiscard]] size_t Size() const {
return m_size;
}

const Variable& GetVariable(const std::string& name) const {
if(auto match = m_variable_map.find(name); match != m_variable_map.end()) {
return *match->second;
}

ZEPHYR_PANIC("No variable named '{}' found in std430 buffer layout", name);
}

private:

void Build(const VariableList& variable_list) {
BuildVariableArrayWithOptimalSorting(variable_list);
ComputeBufferSizeAndVariableOffsets();
}

void BuildVariableArrayWithOptimalSorting(const VariableList& variable_list) {
for(const VariableList::Variable& list_variable : variable_list.GetVariables()) {
Variable& variable = m_variables.emplace_back(list_variable);

const bool is_array = variable.array_size != 0u;

variable.data_size = GLSLTypeToSize(variable.type, is_array);
variable.data_alignment = GLSLTypeToAlignment(variable.type, is_array);

if(is_array) {
variable.data_size *= variable.array_size;
}
}

std::sort(m_variables.begin(), m_variables.end(), [](const Variable& a, const Variable& b) {
return a.data_alignment > b.data_alignment;
});

for(auto& variable : m_variables) {
m_variable_map[variable.name] = &variable;
}
}

void ComputeBufferSizeAndVariableOffsets() {
size_t buffer_offset = 0u;

for(auto& variable : m_variables) {
const size_t remainder = buffer_offset % variable.data_alignment;
if(remainder != 0u) {
buffer_offset += variable.data_alignment - remainder;
}
variable.buffer_offset = buffer_offset;

buffer_offset += variable.data_size;
}

m_size = buffer_offset;
}

[[nodiscard]] static size_t GLSLTypeToSize(const Type& type, bool is_inside_array) {
return GetNumberOfComponentsFromGrade(type.GetGrade(), is_inside_array) * GetScalarTypeSizeInBytes(type.GetScalarType());
}

[[nodiscard]] static size_t GLSLTypeToAlignment(const Type& type, bool is_inside_array) {
return GLSLTypeToSize(type, is_inside_array) / GetNumberOfVectorsFromGrade(type.GetGrade());
}

[[nodiscard]] static size_t GetScalarTypeSizeInBytes(Type::ScalarType type) {
return type == Type::ScalarType::F64 ? 8u : 4u;
}

[[nodiscard]] static size_t GetNumberOfComponentsFromGrade(Type::Grade grade, bool is_inside_array) {
switch(grade) {
case Type::Grade::Scalar: return 1u;
case Type::Grade::Vec2: return 2u;
case Type::Grade::Vec3: return is_inside_array ? 3u : 4u;
case Type::Grade::Vec4: return 4u;
case Type::Grade::Mat4: return 16u;
default: ZEPHYR_PANIC("Unhandled GLSL type grade: {}", (int)grade);
}
}

[[nodiscard]] static size_t GetNumberOfVectorsFromGrade(Type::Grade grade) {
switch(grade) {
case Type::Grade::Scalar: return 1u;
case Type::Grade::Vec2: return 1u;
case Type::Grade::Vec3: return 1u;
case Type::Grade::Vec4: return 1u;
case Type::Grade::Mat4: return 4u;
default: ZEPHYR_PANIC("Unhandled GLSL type grade: {}", (int)grade);
}
}

std::vector<Variable> m_variables;
std::unordered_map<std::string, Variable*> m_variable_map;
std::size_t m_size{};
};

} // namespace zephyr::glsl
106 changes: 106 additions & 0 deletions zephyr/renderer/include/zephyr/renderer/glsl/type.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@

#pragma once

#include <zephyr/math/matrix4.hpp>
#include <zephyr/math/vector.hpp>
#include <zephyr/float.hpp>
#include <zephyr/integer.hpp>
#include <zephyr/panic.hpp>
#include <string>
#include <typeinfo>
#include <type_traits>

namespace zephyr::glsl {

class Type {
public:
enum class ScalarType {
Bool,
I32,
U32,
F32,
F64
};

enum class Grade {
Scalar,
Vec2,
Vec3,
Vec4,
Mat4
};

Type(ScalarType scalar_type, Grade grade)
: m_scalar_type{scalar_type}, m_grade{grade} {
}

[[nodiscard]] ScalarType GetScalarType() const {
return m_scalar_type;
}

[[nodiscard]] Grade GetGrade() const {
return m_grade;
}

[[nodiscard]] bool operator==(const Type& other) const {
return m_scalar_type == other.m_scalar_type && m_grade == other.m_grade;
}

[[nodiscard]] explicit operator std::string() const;

template<typename T>
[[nodiscard]] static Type FromCPPType() {
using U = std::remove_const<T>::type;

if constexpr(std::is_same_v<U, bool>) return {Type::ScalarType::Bool, Type::Grade::Scalar};
if constexpr(std::is_same_v<U, i32>) return {Type::ScalarType::I32, Type::Grade::Scalar};
if constexpr(std::is_same_v<U, u32>) return {Type::ScalarType::U32, Type::Grade::Scalar};
if constexpr(std::is_same_v<U, f32>) return {Type::ScalarType::F32, Type::Grade::Scalar};
if constexpr(std::is_same_v<U, f64>) return {Type::ScalarType::F64, Type::Grade::Scalar};

if constexpr(std::is_same_v<U, Vector2>) return {Type::ScalarType::F32, Type::Grade::Vec2};
if constexpr(std::is_same_v<U, Vector3>) return {Type::ScalarType::F32, Type::Grade::Vec3};
if constexpr(std::is_same_v<U, Vector4>) return {Type::ScalarType::F32, Type::Grade::Vec4};
if constexpr(std::is_same_v<U, Matrix4>) return {Type::ScalarType::F32, Type::Grade::Mat4};

ZEPHYR_PANIC("Unsupported C++ type: {}", typeid(T).name());
}

private:
ScalarType m_scalar_type{};
Grade m_grade{};
};

inline Type::operator std::string() const {
if(GetGrade() == Grade::Scalar) {
switch(GetScalarType()) {
case ScalarType::Bool: return "bool";
case ScalarType::I32: return "int";
case ScalarType::U32: return "uint";
case ScalarType::F32: return "float";
case ScalarType::F64: return "double";
default: ZEPHYR_PANIC("Unhandled scalar type: {}", (int)GetScalarType())
}
} else {
std::string prefix;

switch(GetScalarType()) {
case ScalarType::Bool: prefix = "b"; break;
case ScalarType::I32: prefix = "i"; break;
case ScalarType::U32: prefix = "u"; break;
case ScalarType::F32: break;
case ScalarType::F64: prefix = "d"; break;
default: ZEPHYR_PANIC("Unhandled scalar type: {}", (int)GetScalarType());
}

switch(GetGrade()) {
case Grade::Vec2: return prefix + "vec2";
case Grade::Vec3: return prefix + "vec3";
case Grade::Vec4: return prefix + "vec4";
case Grade::Mat4: return prefix + "mat4";
default: ZEPHYR_PANIC("Unhandled grade: {}", (int)GetGrade());
}
}
}

} // namespace zephyr::glsl
Loading

0 comments on commit b309ce6

Please sign in to comment.