• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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