-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathUniformStructLayout.h
More file actions
197 lines (184 loc) · 6.23 KB
/
Copy pathUniformStructLayout.h
File metadata and controls
197 lines (184 loc) · 6.23 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
#pragma once
#include "Export.h"
#include "ShaderEnum.h"
#include "Texture.h"
#include "Core/Reflection.hpp"
#include "Core/Util.h"
#include "Core/ISerializable.h"
#include <glm/fwd.hpp>
#include <string>
#include <vector>
#include <cstdint>
#include <type_traits>
namespace sh::render
{
/// @brief 셰이더 유니폼/스토리지 버퍼 구조체 레이아웃
class UniformStructLayout
{
public:
enum class Layout : uint8_t
{
STD140, // UBO
STD430 // SSBO
};
/// @brief 버퍼 타입
enum class Kind : uint8_t
{
Uniform, // UBO
Storage, // SSBO
Sampler,
PushConstant // 푸시 상수
};
/// @brief 용도
enum class Usage : uint8_t
{
Camera = 0,
Object = 1,
Material = 2,
};
struct UniformMember
{
std::string name;
std::size_t typeHash = 0;
uint32_t offset = 0;
uint32_t layoutSize = 0; // 바이트 (가변 배열이면 0)
uint32_t baseAlignment = 0;
uint32_t count = 1; // 배열 원소 개수 (단일이면 1)
bool bDynamicArray = false; // SSBO
auto IsArray() const -> bool { return count > 1 || bDynamicArray; }
};
struct LayoutInfo
{
uint32_t baseAlignment; // 오프셋의 정렬 단위
uint32_t bytes; // 실제 차지하는 총 크기(패딩 포함)
};
public:
SH_RENDER_API UniformStructLayout(std::string name, uint32_t binding, Usage type, ShaderStage stage, Kind kind = Kind::Uniform);
SH_RENDER_API UniformStructLayout(std::string name, uint32_t binding, Usage type, ShaderStage stage, Kind kind, Layout layout);
/// @brief 구조체의 맴버 변수를 추가. 구조체의 변수 순서대로 호출해야 한다.
template<typename T>
void AddMember(const std::string& name);
/// @brief 구조체의 고정 배열 맴버를 추가. 구조체의 변수 순서대로 호출해야 한다.
/// @param count 원소 개수 (>= 1)
template<typename T>
void AddArrayMember(const std::string& name, uint32_t count);
/// @brief SSBO 끝에 들어가는 가변 배열 맴버 추가. 반드시 마지막에 호출.
template<typename T>
void AddDynamicArrayMember(const std::string& name);
SH_RENDER_API auto GetMember(const std::string& name) const -> const UniformMember*;
SH_RENDER_API auto GetMembers() const -> const std::vector<UniformMember>& { return members; }
SH_RENDER_API auto HasMember(const std::string& name) const -> bool;
/// @brief 구조체의 사이즈(바이트). 가변 배열 맴버가 있으면 고정 부분까지의 크기만 반환.
SH_RENDER_API auto GetSize() const -> std::size_t;
SH_RENDER_API auto GetLayout() const -> Layout { return layout; }
SH_RENDER_API auto GetKind() const -> Kind { return kind; }
SH_RENDER_API auto IsSampler() const -> bool { return kind == Kind::Sampler; }
SH_RENDER_API auto IsPushConstant() const -> bool { return kind == Kind::PushConstant; }
private:
template<typename T>
static auto GetLayoutInfo() -> LayoutInfo;
template<typename T>
void AddMemberImpl(const std::string& name, uint32_t count, bool bDynamic);
public:
const Kind kind;
const Layout layout;
const Usage usage;
const ShaderStage stage;
const uint32_t binding;
const std::string name;
private:
std::vector<UniformMember> members;
};
template<typename T>
inline auto UniformStructLayout::GetLayoutInfo() -> LayoutInfo
{
if constexpr (std::is_same_v<T, float>)
return { 4, 4 };
else if constexpr (std::is_same_v<T, int>)
return { 4, 4 };
else if constexpr (std::is_same_v<T, glm::vec2>)
return { 8, 8 };
else if constexpr (std::is_same_v<T, glm::vec3>)
return { 16, 16 };
else if constexpr (std::is_same_v<T, glm::vec4>)
return { 16, 16 };
else if constexpr (std::is_same_v<T, glm::mat2>)
return { 16, 32 };
else if constexpr (std::is_same_v<T, glm::mat3>)
return { 16, 48 };
else if constexpr (std::is_same_v<T, glm::mat4>)
return { 16, 64 };
else if constexpr (std::is_class_v<T>)
return { alignof(T), sizeof(T)};
#if defined(_MSC_VER)
else
static_assert(std::_Always_false<T>, "Unknown type for GetLayoutInfo: " __FUNCSIG__);
#elif defined(__GNUC__) || defined(__clang__)
else
static_assert(core::alwaysFalse<T>, "Unknown type for GetLayoutInfo");
# else
else
static_assert(always_false<T>, "Unknown type for GetLayoutInfo: Unknown compiler");
#endif
}
template<typename T>
inline void UniformStructLayout::AddMember(const std::string& name)
{
AddMemberImpl<T>(name, 1, false);
}
template<typename T>
inline void UniformStructLayout::AddArrayMember(const std::string& name, uint32_t count)
{
AddMemberImpl<T>(name, count, false);
}
template<typename T>
inline void UniformStructLayout::AddDynamicArrayMember(const std::string& name)
{
AddMemberImpl<T>(name, 0, true);
}
template<typename T>
inline void UniformStructLayout::AddMemberImpl(const std::string& name, uint32_t count, bool bDynamic)
{
if constexpr (std::is_same_v<T, Texture>)
{
UniformMember member{};
member.name = name;
member.typeHash = core::reflection::GetType<Texture>().hash;
member.count = count;
member.bDynamicArray = bDynamic;
members.push_back(std::move(member));
return;
}
else
{
LayoutInfo info = GetLayoutInfo<T>();
if (layout == Layout::STD140 && std::is_class_v<T>)
info.baseAlignment = 16;
const bool isArray = (count > 1 || bDynamic);
// STD140: 배열 원소 stride는 최소 16바이트 정렬
// STD430: 타입 고유 alignment 사용
const uint32_t baseAlignment = (layout == Layout::STD140 && isArray)
? std::max(info.baseAlignment, 16u)
: info.baseAlignment;
// 배열은 stride를 baseAlignment에 맞춤
const uint32_t elementStride = isArray
? core::Util::AlignTo(info.bytes, baseAlignment)
: info.bytes;
// 가변 배열은 런타임에 크기 결정
const uint32_t totalSize = bDynamic ? 0 : (elementStride * count);
UniformMember member{};
member.name = name;
member.typeHash = core::reflection::GetType<T>().hash;
member.layoutSize = totalSize;
member.baseAlignment = baseAlignment;
member.count = bDynamic ? 0 : count;
member.bDynamicArray = bDynamic;
if (!members.empty())
{
const UniformMember& prev = members.back();
member.offset = core::Util::AlignTo(prev.offset + prev.layoutSize, baseAlignment);
}
members.push_back(std::move(member));
}
}
}//namespace