1 // Copyright 2018 The Amber Authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "src/format.h"
16
17 #include <algorithm>
18 #include <utility>
19
20 #include "src/make_unique.h"
21 #include "src/type_parser.h"
22
23 namespace amber {
24 namespace {
25
FormatModeToName(FormatMode mode)26 std::string FormatModeToName(FormatMode mode) {
27 switch (mode) {
28 case FormatMode::kUNorm:
29 return "UNORM";
30 case FormatMode::kUInt:
31 return "UINT";
32 case FormatMode::kUScaled:
33 return "USCALED";
34 case FormatMode::kSInt:
35 return "SINT";
36 case FormatMode::kSNorm:
37 return "SNORM";
38 case FormatMode::kSScaled:
39 return "SSCALED";
40 case FormatMode::kSRGB:
41 return "SRGB";
42 case FormatMode::kSFloat:
43 return "SFLOAT";
44 case FormatMode::kUFloat:
45 return "UFLOAT";
46 }
47
48 return "";
49 }
50
CalculatePad(uint32_t val)51 uint32_t CalculatePad(uint32_t val) {
52 if ((val % 16) == 0)
53 return 0;
54 return 16 - (val % 16);
55 }
56
57 } // namespace
58
Format(type::Type * type)59 Format::Format(type::Type* type) : type_(type) {
60 auto name = GenerateName();
61 if (name == "")
62 format_type_ = FormatType::kUnknown;
63 else
64 format_type_ = TypeParser::NameToFormatType(name);
65 RebuildSegments();
66 }
67
68 Format::~Format() = default;
69
SizeInBytes() const70 uint32_t Format::SizeInBytes() const {
71 uint32_t size = 0;
72 for (const auto& seg : segments_) {
73 if (seg.IsPadding()) {
74 size += seg.PaddingBytes();
75 continue;
76 }
77 size += static_cast<uint32_t>(seg.SizeInBytes());
78 }
79
80 return size;
81 }
82
Equal(const Format * b) const83 bool Format::Equal(const Format* b) const {
84 return format_type_ == b->format_type_ && layout_ == b->layout_ &&
85 type_->Equal(b->type_);
86 }
87
InputNeededPerElement() const88 uint32_t Format::InputNeededPerElement() const {
89 uint32_t count = 0;
90 for (const auto& seg : segments_) {
91 if (seg.IsPadding())
92 continue;
93
94 count += 1;
95 }
96 return count;
97 }
98
SetLayout(Layout layout)99 void Format::SetLayout(Layout layout) {
100 if (layout == layout_)
101 return;
102
103 layout_ = layout;
104 RebuildSegments();
105 }
106
RebuildSegments()107 void Format::RebuildSegments() {
108 segments_.clear();
109 AddSegmentsForType(type_);
110 }
111
AddPaddedSegment(uint32_t size)112 void Format::AddPaddedSegment(uint32_t size) {
113 // If the last item was already padding we just extend by the |size| bytes
114 if (!segments_.empty() && segments_.back().IsPadding()) {
115 segments_[segments_.size() - 1] =
116 Segment{size + segments_.back().SizeInBytes()};
117 } else {
118 segments_.push_back(Segment{size});
119 }
120 }
121
AddPaddedSegmentPackable(uint32_t size)122 void Format::AddPaddedSegmentPackable(uint32_t size) {
123 AddPaddedSegment(size);
124 segments_.back().SetPackable(true);
125 }
126
AddSegment(const Segment & seg)127 bool Format::AddSegment(const Segment& seg) {
128 if (!segments_.empty()) {
129 auto last = segments_.back();
130
131 if (last.IsPackable() && last.IsPadding() &&
132 last.SizeInBytes() >= seg.SizeInBytes()) {
133 segments_.back() = seg;
134 auto pad = last.SizeInBytes() - seg.SizeInBytes();
135 if (pad > 0)
136 AddPaddedSegmentPackable(pad);
137
138 return false;
139 }
140 }
141 segments_.push_back(seg);
142 return true;
143 }
144
NeedsPadding(type::Type * t) const145 bool Format::NeedsPadding(type::Type* t) const {
146 if (layout_ == Layout::kStd140 && (t->IsMatrix() || t->IsArray()))
147 return true;
148 if (t->IsVec3() || (t->IsMatrix() && t->RowCount() == 3))
149 return true;
150 return false;
151 }
152
CalcVecBaseAlignmentInBytes(type::Number * n) const153 uint32_t Format::CalcVecBaseAlignmentInBytes(type::Number* n) const {
154 // vec3 rounds up to a Vec4, so 4 * N
155 if (n->IsVec3())
156 return 4 * n->SizeInBytes();
157
158 // vec2 and vec4 are 2 * N and 4 * N respectively
159 return n->RowCount() * n->SizeInBytes();
160 }
161
CalcArrayBaseAlignmentInBytes(type::Type * t) const162 uint32_t Format::CalcArrayBaseAlignmentInBytes(type::Type* t) const {
163 uint32_t align = 0;
164 if (t->IsStruct()) {
165 align = CalcStructBaseAlignmentInBytes(t->AsStruct());
166 } else if (t->IsMatrix()) {
167 align = CalcMatrixBaseAlignmentInBytes(t->AsNumber());
168 } else if (t->IsVec()) {
169 align = CalcVecBaseAlignmentInBytes(t->AsNumber());
170 } else if (t->IsList()) {
171 align = CalcListBaseAlignmentInBytes(t->AsList());
172 } else if (t->IsNumber()) {
173 align = t->SizeInBytes();
174 }
175
176 // In std140 array elements round up to multiple of vec4.
177 if (layout_ == Layout::kStd140)
178 align += CalculatePad(align);
179
180 return align;
181 }
182
CalcStructBaseAlignmentInBytes(type::Struct * s) const183 uint32_t Format::CalcStructBaseAlignmentInBytes(type::Struct* s) const {
184 uint32_t base_alignment = 0;
185 for (const auto& member : s->Members()) {
186 base_alignment =
187 std::max(base_alignment, CalcTypeBaseAlignmentInBytes(member.type));
188 }
189
190 return base_alignment;
191 }
192
CalcMatrixBaseAlignmentInBytes(type::Number * m) const193 uint32_t Format::CalcMatrixBaseAlignmentInBytes(type::Number* m) const {
194 // TODO(dsinclair): Deal with row major when needed. Currently this assumes
195 // the matrix is column major.
196
197 uint32_t align = 0;
198 if (m->RowCount() == 3)
199 align = 4 * m->SizeInBytes();
200 else
201 align = m->RowCount() * m->SizeInBytes();
202
203 // STD140 rounds up to 16 byte alignment
204 if (layout_ == Layout::kStd140)
205 align += CalculatePad(align);
206
207 return align;
208 }
209
CalcListBaseAlignmentInBytes(type::List * l) const210 uint32_t Format::CalcListBaseAlignmentInBytes(type::List* l) const {
211 return l->SizeInBytes();
212 }
213
CalcTypeBaseAlignmentInBytes(type::Type * t) const214 uint32_t Format::CalcTypeBaseAlignmentInBytes(type::Type* t) const {
215 if (t->IsArray())
216 return CalcArrayBaseAlignmentInBytes(t);
217 if (t->IsVec())
218 return CalcVecBaseAlignmentInBytes(t->AsNumber());
219 if (t->IsMatrix())
220 return CalcMatrixBaseAlignmentInBytes(t->AsNumber());
221 if (t->IsNumber())
222 return t->SizeInBytes();
223 if (t->IsList())
224 return CalcListBaseAlignmentInBytes(t->AsList());
225 if (t->IsStruct()) {
226 // Pad struct to 16 bytes in STD140
227 uint32_t base = CalcStructBaseAlignmentInBytes(t->AsStruct());
228 if (layout_ == Layout::kStd140)
229 base += CalculatePad(base);
230 return base;
231 }
232
233 assert(false && "Not reached");
234 return 0;
235 }
236
AddSegmentsForType(type::Type * type)237 uint32_t Format::AddSegmentsForType(type::Type* type) {
238 if (type->IsList() && type->AsList()->IsPacked()) {
239 auto l = type->AsList();
240 if (AddSegment(Segment(FormatComponentType::kR, FormatMode::kUInt,
241 l->PackSizeInBits()))) {
242 return l->SizeInBytes();
243 }
244 return 0;
245 }
246
247 // Remove packable from previous packing for types which can't pack back.
248 if (type->IsStruct() || type->IsVec() || type->IsMatrix() ||
249 type->IsArray()) {
250 if (!segments_.empty() && segments_.back().IsPadding())
251 segments_.back().SetPackable(false);
252 }
253
254 // TODO(dsinclair): How to handle matrix stride .... Stride comes from parent
255 // member ....
256
257 if (type->IsStruct()) {
258 auto s = type->AsStruct();
259 auto base_alignment_in_bytes = CalcStructBaseAlignmentInBytes(s);
260
261 uint32_t cur_offset = 0;
262 for (const auto& member : s->Members()) {
263 if (member.HasOffset()) {
264 assert(static_cast<uint32_t>(member.offset_in_bytes) >= cur_offset);
265
266 AddPaddedSegment(static_cast<uint32_t>(member.offset_in_bytes) -
267 cur_offset);
268 cur_offset = static_cast<uint32_t>(member.offset_in_bytes);
269 }
270
271 uint32_t seg_size = 0;
272 if (member.type->IsSizedArray()) {
273 for (size_t i = 0; i < member.type->ArraySize(); ++i) {
274 auto ary_seg_size = AddSegmentsForType(member.type);
275 // Don't allow array members to pack together
276 if (!segments_.empty() && segments_.back().IsPadding())
277 segments_.back().SetPackable(false);
278
279 if (member.HasArrayStride()) {
280 uint32_t array_stride =
281 static_cast<uint32_t>(member.array_stride_in_bytes);
282 assert(ary_seg_size <= array_stride &&
283 "Array element larger than stride");
284
285 seg_size += array_stride;
286 } else {
287 seg_size += ary_seg_size;
288 }
289 }
290 } else {
291 seg_size = AddSegmentsForType(member.type);
292 }
293
294 if (seg_size > 0 && seg_size < base_alignment_in_bytes) {
295 AddPaddedSegmentPackable(base_alignment_in_bytes - seg_size);
296 seg_size += base_alignment_in_bytes - seg_size;
297 }
298
299 cur_offset += seg_size;
300 }
301 if (s->HasStride()) {
302 assert(cur_offset <= s->StrideInBytes() &&
303 "Struct has more members then fit within stride");
304 AddPaddedSegment(s->StrideInBytes() - cur_offset);
305 cur_offset = s->StrideInBytes();
306 } else if (layout_ == Layout::kStd140) {
307 // Round struct up to 16 byte alignment in STD140.
308 auto pad = CalculatePad(cur_offset);
309 if (pad > 0) {
310 AddPaddedSegment(pad);
311 cur_offset += pad;
312 }
313 }
314 return cur_offset;
315 }
316
317 // List members are only numbers and must not be vecs or matrices.
318 if (type->IsList()) {
319 uint32_t size = 0;
320 auto l = type->AsList();
321 for (uint32_t i = 0; i < type->ColumnCount(); ++i) {
322 for (const auto& m : l->Members()) {
323 if (AddSegment(Segment{m.name, m.mode, m.num_bits}))
324 size += m.SizeInBytes();
325 }
326
327 if (NeedsPadding(type)) {
328 auto& seg = l->Members().back();
329 for (size_t k = 0; k < (4 - type->RowCount()); ++k) {
330 AddPaddedSegment(seg.SizeInBytes());
331 size += seg.SizeInBytes();
332 }
333 }
334 }
335 return size;
336 }
337
338 auto n = type->AsNumber();
339 uint32_t size = 0;
340 for (uint32_t i = 0; i < type->ColumnCount(); ++i) {
341 for (uint32_t k = 0; k < type->RowCount(); ++k) {
342 if (AddSegment(Segment{static_cast<FormatComponentType>(i),
343 n->GetFormatMode(), n->NumBits()})) {
344 size += type->SizeInBytes();
345 }
346 }
347
348 // In std140 a matrix (column count > 1) has each row stored like an array
349 // which rounds up to a vec4.
350 //
351 // In std140 and std430 a vector of size 3N will round up to a vector of 4N.
352 if (NeedsPadding(type)) {
353 for (size_t k = 0; k < (4 - type->RowCount()); ++k) {
354 AddPaddedSegmentPackable(type->SizeInBytes());
355 size += type->SizeInBytes();
356 }
357 }
358
359 // Make sure matrix rows don't accidentally pack together.
360 if (type->IsMatrix() && segments_.back().IsPadding())
361 segments_.back().SetPackable(false);
362 }
363 return size;
364 }
365
GenerateName() const366 std::string Format::GenerateName() const {
367 if (type_->IsMatrix())
368 return "";
369
370 static const char NAME_PARTS[] = "RGBAXDS";
371 if (type_->IsList()) {
372 std::vector<std::pair<std::string, std::string>> parts;
373 const auto& members = type_->AsList()->Members();
374 for (size_t i = 0; i < members.size(); ++i) {
375 const auto& member = members[i];
376 std::string name(1, NAME_PARTS[static_cast<uint8_t>(member.name)]);
377 name += std::to_string(member.num_bits);
378
379 std::string type = FormatModeToName(member.mode);
380 parts.push_back({name, type});
381 }
382
383 std::string name = "";
384 for (size_t i = 0; i < parts.size(); ++i) {
385 name += parts[i].first;
386
387 if (i + 1 < parts.size() && parts[i].second != parts[i + 1].second)
388 name += "_" + parts[i].second + "_";
389
390 // Handle the X8_D24 underscore.
391 if (parts[i].first[0] == 'X')
392 name += "_";
393 }
394 name += "_" + parts.back().second;
395 if (type_->AsList()->IsPacked())
396 name += "_PACK" + std::to_string(type_->AsList()->PackSizeInBits());
397
398 return name;
399 }
400
401 if (type_->IsNumber()) {
402 std::string name = "";
403 for (uint32_t i = 0; i < type_->RowCount(); ++i)
404 name += NAME_PARTS[i] + std::to_string(type_->SizeInBytes() * 8);
405
406 name += "_" + FormatModeToName(type_->AsNumber()->GetFormatMode());
407 return name;
408 }
409
410 return "";
411 }
412
413 } // namespace amber
414