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/verifier.h"
16
17 #include <cassert>
18 #include <cmath>
19 #include <cstring>
20 #include <string>
21 #include <vector>
22
23 #include "src/command.h"
24 #include "src/float16_helper.h"
25
26 namespace amber {
27 namespace {
28
29 const uint32_t kBitsPerByte = 8;
30 const double kEpsilon = 0.000001;
31 const double kDefaultTexelTolerance = 0.002;
32
33 // Copy [src_bit_offset, src_bit_offset + bits) bits of |src| to
34 // [0, bits) of |dst|.
CopyBitsOfMemoryToBuffer(uint8_t * dst,const uint8_t * src,uint32_t src_bit_offset,uint32_t bits)35 void CopyBitsOfMemoryToBuffer(uint8_t* dst,
36 const uint8_t* src,
37 uint32_t src_bit_offset,
38 uint32_t bits) {
39 while (src_bit_offset > 7) {
40 ++src;
41 src_bit_offset = src_bit_offset - kBitsPerByte;
42 }
43
44 // Number of bytes greater than or equal to |(src_bit_offset + bits) / 8|.
45 const uint32_t size_in_bytes = (src_bit_offset + bits + 7) / kBitsPerByte;
46 assert(size_in_bytes <= kBitsPerByte);
47
48 uint64_t data = 0;
49 uint8_t* ptr = reinterpret_cast<uint8_t*>(&data);
50 for (uint32_t i = 0; i < size_in_bytes; ++i) {
51 ptr[i] = src[i];
52 }
53
54 data >>= src_bit_offset;
55 if (bits != 64)
56 data &= (1ULL << bits) - 1ULL;
57
58 std::memcpy(dst, &data, static_cast<size_t>((bits + 7) / kBitsPerByte));
59 }
60
61 // This is based on "18.3. sRGB transfer functions" of
62 // https://www.khronos.org/registry/DataFormat/specs/1.2/dataformat.1.2.html
SRGBToLinearValue(double sRGB)63 double SRGBToLinearValue(double sRGB) {
64 if (sRGB <= 0.04045)
65 return sRGB / 12.92;
66
67 return pow((sRGB + 0.055) / 1.055, 2.4);
68 }
69
70 // It returns true if the difference is within the given error.
71 // If |is_tolerance_percent| is true, the actual tolerance will be
72 // relative value i.e., |tolerance| / 100 * fabs(expected).
73 // Otherwise, this method uses the absolute value i.e., |tolerance|.
IsEqualWithTolerance(const double actual,const double expected,double tolerance,const bool is_tolerance_percent=true)74 bool IsEqualWithTolerance(const double actual,
75 const double expected,
76 double tolerance,
77 const bool is_tolerance_percent = true) {
78 // Special case for NaN.
79 if (std::isunordered(actual, expected)) {
80 return std::isnan(actual) == std::isnan(expected);
81 }
82
83 double difference = std::fabs(actual - expected);
84 if (is_tolerance_percent) {
85 if (difference > ((tolerance / 100.0) * std::fabs(expected))) {
86 return false;
87 }
88 } else if (difference > tolerance) {
89 return false;
90 }
91 return true;
92 }
93
94 template <typename T>
CheckActualValue(const ProbeSSBOCommand * command,const T actual_value,const Value & value)95 Result CheckActualValue(const ProbeSSBOCommand* command,
96 const T actual_value,
97 const Value& value) {
98 const auto comp = command->GetComparator();
99 const auto& tolerance = command->GetTolerances();
100 const T val = value.IsInteger() ? static_cast<T>(value.AsUint64())
101 : static_cast<T>(value.AsDouble());
102 switch (comp) {
103 case ProbeSSBOCommand::Comparator::kEqual:
104 if (value.IsInteger()) {
105 if (static_cast<uint64_t>(actual_value) != static_cast<uint64_t>(val)) {
106 return Result(std::to_string(actual_value) +
107 " == " + std::to_string(val));
108 }
109 } else {
110 if (!IsEqualWithTolerance(static_cast<const double>(actual_value),
111 static_cast<const double>(val), kEpsilon)) {
112 return Result(std::to_string(actual_value) +
113 " == " + std::to_string(val));
114 }
115 }
116 break;
117 case ProbeSSBOCommand::Comparator::kNotEqual:
118 if (value.IsInteger()) {
119 if (static_cast<uint64_t>(actual_value) == static_cast<uint64_t>(val)) {
120 return Result(std::to_string(actual_value) +
121 " != " + std::to_string(val));
122 }
123 } else {
124 if (IsEqualWithTolerance(static_cast<const double>(actual_value),
125 static_cast<const double>(val), kEpsilon)) {
126 return Result(std::to_string(actual_value) +
127 " != " + std::to_string(val));
128 }
129 }
130 break;
131 case ProbeSSBOCommand::Comparator::kFuzzyEqual:
132 if (!IsEqualWithTolerance(
133 static_cast<const double>(actual_value),
134 static_cast<const double>(val),
135 command->HasTolerances() ? tolerance[0].value : kEpsilon,
136 command->HasTolerances() ? tolerance[0].is_percent : true)) {
137 return Result(std::to_string(actual_value) +
138 " ~= " + std::to_string(val));
139 }
140 break;
141 case ProbeSSBOCommand::Comparator::kLess:
142 if (actual_value >= val)
143 return Result(std::to_string(actual_value) + " < " +
144 std::to_string(val));
145 break;
146 case ProbeSSBOCommand::Comparator::kLessOrEqual:
147 if (actual_value > val)
148 return Result(std::to_string(actual_value) +
149 " <= " + std::to_string(val));
150 break;
151 case ProbeSSBOCommand::Comparator::kGreater:
152 if (actual_value <= val)
153 return Result(std::to_string(actual_value) + " > " +
154 std::to_string(val));
155 break;
156 case ProbeSSBOCommand::Comparator::kGreaterOrEqual:
157 if (actual_value < val)
158 return Result(std::to_string(actual_value) +
159 " >= " + std::to_string(val));
160 break;
161 }
162 return {};
163 }
164
165 template <typename T>
CheckValue(const ProbeSSBOCommand * command,const uint8_t * memory,const Value & value)166 Result CheckValue(const ProbeSSBOCommand* command,
167 const uint8_t* memory,
168 const Value& value) {
169 const T* ptr = reinterpret_cast<const T*>(memory);
170 return CheckActualValue<T>(command, *ptr, value);
171 }
172
SetupToleranceForTexels(const ProbeCommand * command,double * tolerance,bool * is_tolerance_percent)173 void SetupToleranceForTexels(const ProbeCommand* command,
174 double* tolerance,
175 bool* is_tolerance_percent) {
176 if (command->HasTolerances()) {
177 const auto& tol = command->GetTolerances();
178 if (tol.size() == 4) {
179 tolerance[0] = tol[0].value;
180 tolerance[1] = tol[1].value;
181 tolerance[2] = tol[2].value;
182 tolerance[3] = tol[3].value;
183 is_tolerance_percent[0] = tol[0].is_percent;
184 is_tolerance_percent[1] = tol[1].is_percent;
185 is_tolerance_percent[2] = tol[2].is_percent;
186 is_tolerance_percent[3] = tol[3].is_percent;
187 } else {
188 tolerance[0] = tol[0].value;
189 tolerance[1] = tol[0].value;
190 tolerance[2] = tol[0].value;
191 tolerance[3] = tol[0].value;
192 is_tolerance_percent[0] = tol[0].is_percent;
193 is_tolerance_percent[1] = tol[0].is_percent;
194 is_tolerance_percent[2] = tol[0].is_percent;
195 is_tolerance_percent[3] = tol[0].is_percent;
196 }
197 } else {
198 tolerance[0] = kDefaultTexelTolerance;
199 tolerance[1] = kDefaultTexelTolerance;
200 tolerance[2] = kDefaultTexelTolerance;
201 tolerance[3] = kDefaultTexelTolerance;
202 is_tolerance_percent[0] = false;
203 is_tolerance_percent[1] = false;
204 is_tolerance_percent[2] = false;
205 is_tolerance_percent[3] = false;
206 }
207 }
208
209 // Convert data of |texel| into double values based on the
210 // information given in |fmt|.
GetActualValuesFromTexel(const uint8_t * texel,const Format * fmt)211 std::vector<double> GetActualValuesFromTexel(const uint8_t* texel,
212 const Format* fmt) {
213 assert(fmt && !fmt->GetSegments().empty());
214
215 std::vector<double> actual_values(fmt->GetSegments().size());
216 uint32_t bit_offset = 0;
217
218 for (size_t i = 0; i < fmt->GetSegments().size(); ++i) {
219 const auto& seg = fmt->GetSegments()[i];
220 if (seg.IsPadding()) {
221 bit_offset += seg.GetNumBits();
222 continue;
223 }
224
225 uint8_t actual[8] = {0, 0, 0, 0, 0, 0, 0, 0};
226 uint32_t num_bits = seg.GetNumBits();
227 CopyBitsOfMemoryToBuffer(actual, texel, bit_offset, num_bits);
228
229 FormatMode mode = seg.GetFormatMode();
230 if (type::Type::IsInt8(mode, num_bits)) {
231 int8_t* ptr8 = nullptr;
232 ptr8 = reinterpret_cast<int8_t*>(actual);
233 actual_values[i] = static_cast<double>(*ptr8);
234 } else if (type::Type::IsInt16(mode, num_bits)) {
235 int16_t* ptr16 = nullptr;
236 ptr16 = reinterpret_cast<int16_t*>(actual);
237 actual_values[i] = static_cast<double>(*ptr16);
238 } else if (type::Type::IsInt32(mode, num_bits)) {
239 int32_t* ptr32 = nullptr;
240 ptr32 = reinterpret_cast<int32_t*>(actual);
241 actual_values[i] = static_cast<double>(*ptr32);
242 } else if (type::Type::IsInt64(mode, num_bits)) {
243 int64_t* ptr64 = nullptr;
244 ptr64 = reinterpret_cast<int64_t*>(actual);
245 actual_values[i] = static_cast<double>(*ptr64);
246 } else if (type::Type::IsUint8(mode, num_bits)) {
247 actual_values[i] = static_cast<double>(*actual);
248 } else if (type::Type::IsUint16(mode, num_bits)) {
249 uint16_t* ptr16 = nullptr;
250 ptr16 = reinterpret_cast<uint16_t*>(actual);
251 actual_values[i] = static_cast<double>(*ptr16);
252 } else if (type::Type::IsUint32(mode, num_bits)) {
253 uint32_t* ptr32 = nullptr;
254 ptr32 = reinterpret_cast<uint32_t*>(actual);
255 actual_values[i] = static_cast<double>(*ptr32);
256 } else if (type::Type::IsUint64(mode, num_bits)) {
257 uint64_t* ptr64 = nullptr;
258 ptr64 = reinterpret_cast<uint64_t*>(actual);
259 actual_values[i] = static_cast<double>(*ptr64);
260 } else if (type::Type::IsFloat32(mode, num_bits)) {
261 float* ptr = reinterpret_cast<float*>(actual);
262 actual_values[i] = static_cast<double>(*ptr);
263 } else if (type::Type::IsFloat64(mode, num_bits)) {
264 double* ptr = reinterpret_cast<double*>(actual);
265 actual_values[i] = *ptr;
266 } else if (type::Type::IsFloat(mode) && num_bits < 32) {
267 actual_values[i] = static_cast<double>(
268 float16::HexFloatToFloat(actual, static_cast<uint8_t>(num_bits)));
269 } else {
270 assert(false && "Incorrect number of bits for number.");
271 }
272
273 bit_offset += num_bits;
274 }
275
276 return actual_values;
277 }
278
279 // If component mode of |fmt| is FormatMode::kUNorm or
280 // ::kSNorm or ::kSRGB, scale the corresponding value in |texel|.
281 // Note that we do not scale values with FormatMode::kUInt, ::kSInt,
282 // ::kUFloat, ::kSFloat.
ScaleTexelValuesIfNeeded(std::vector<double> * texel,const Format * fmt)283 void ScaleTexelValuesIfNeeded(std::vector<double>* texel, const Format* fmt) {
284 assert(fmt->GetSegments().size() == texel->size());
285
286 for (size_t i = 0; i < fmt->GetSegments().size(); ++i) {
287 const auto& seg = fmt->GetSegments()[i];
288 if (seg.IsPadding())
289 continue;
290
291 double scaled_value = (*texel)[i];
292 if (seg.GetFormatMode() == FormatMode::kUNorm) {
293 scaled_value /= static_cast<double>((1 << seg.GetNumBits()) - 1);
294 } else if (seg.GetFormatMode() == FormatMode::kSNorm) {
295 scaled_value /= static_cast<double>((1 << (seg.GetNumBits() - 1)) - 1);
296 } else if (seg.GetFormatMode() == FormatMode::kSRGB) {
297 scaled_value /= static_cast<double>((1 << seg.GetNumBits()) - 1);
298 if (seg.GetName() != FormatComponentType::kA)
299 scaled_value = SRGBToLinearValue(scaled_value);
300 } else if (seg.GetFormatMode() == FormatMode::kSScaled ||
301 seg.GetFormatMode() == FormatMode::kUScaled) {
302 assert(false && "UScaled and SScaled are not implemented");
303 }
304
305 (*texel)[i] = scaled_value;
306 }
307 }
308
309 /// Check |texel| with |texel_format| is the same with the expected
310 /// RGB(A) values given via |command|. This method allow error
311 /// smaller than |tolerance|. If an element of
312 /// |is_tolerance_percent| is true, we assume that the corresponding
313 /// |tolerance| is relative i.e., percentage allowed error.
IsTexelEqualToExpected(const std::vector<double> & texel,const Format * fmt,const ProbeCommand * command,const double * tolerance,const bool * is_tolerance_percent)314 bool IsTexelEqualToExpected(const std::vector<double>& texel,
315 const Format* fmt,
316 const ProbeCommand* command,
317 const double* tolerance,
318 const bool* is_tolerance_percent) {
319 for (size_t i = 0; i < fmt->GetSegments().size(); ++i) {
320 const auto& seg = fmt->GetSegments()[i];
321 if (seg.IsPadding())
322 continue;
323
324 double texel_for_component = texel[i];
325 double expected = 0;
326 double current_tolerance = 0;
327 bool is_current_tolerance_percent = false;
328 switch (seg.GetName()) {
329 case FormatComponentType::kA:
330 if (!command->IsRGBA())
331 continue;
332
333 expected = static_cast<double>(command->GetA());
334 current_tolerance = tolerance[3];
335 is_current_tolerance_percent = is_tolerance_percent[3];
336 break;
337 case FormatComponentType::kR:
338 expected = static_cast<double>(command->GetR());
339 current_tolerance = tolerance[0];
340 is_current_tolerance_percent = is_tolerance_percent[0];
341 break;
342 case FormatComponentType::kG:
343 expected = static_cast<double>(command->GetG());
344 current_tolerance = tolerance[1];
345 is_current_tolerance_percent = is_tolerance_percent[1];
346 break;
347 case FormatComponentType::kB:
348 expected = static_cast<double>(command->GetB());
349 current_tolerance = tolerance[2];
350 is_current_tolerance_percent = is_tolerance_percent[2];
351 break;
352 default:
353 continue;
354 }
355
356 if (!IsEqualWithTolerance(expected, texel_for_component, current_tolerance,
357 is_current_tolerance_percent)) {
358 return false;
359 }
360 }
361
362 return true;
363 }
364
GetTexelInRGBA(const std::vector<double> & texel,const Format * fmt)365 std::vector<double> GetTexelInRGBA(const std::vector<double>& texel,
366 const Format* fmt) {
367 std::vector<double> texel_in_rgba(texel.size());
368 for (size_t i = 0; i < fmt->GetSegments().size(); ++i) {
369 const auto& seg = fmt->GetSegments()[i];
370 if (seg.IsPadding())
371 continue;
372
373 switch (seg.GetName()) {
374 case FormatComponentType::kR:
375 texel_in_rgba[0] = texel[i];
376 break;
377 case FormatComponentType::kG:
378 texel_in_rgba[1] = texel[i];
379 break;
380 case FormatComponentType::kB:
381 texel_in_rgba[2] = texel[i];
382 break;
383 case FormatComponentType::kA:
384 texel_in_rgba[3] = texel[i];
385 break;
386 default:
387 continue;
388 }
389 }
390 return texel_in_rgba;
391 }
392
393 } // namespace
394
395 Verifier::Verifier() = default;
396
397 Verifier::~Verifier() = default;
398
Probe(const ProbeCommand * command,const Format * fmt,uint32_t texel_stride,uint32_t row_stride,uint32_t frame_width,uint32_t frame_height,const void * buf)399 Result Verifier::Probe(const ProbeCommand* command,
400 const Format* fmt,
401 uint32_t texel_stride,
402 uint32_t row_stride,
403 uint32_t frame_width,
404 uint32_t frame_height,
405 const void* buf) {
406 if (!command)
407 return Result("Verifier::Probe given ProbeCommand is nullptr");
408 if (!fmt)
409 return Result("Verifier::Probe given texel's Format is nullptr");
410 if (!buf)
411 return Result("Verifier::Probe given buffer to probe is nullptr");
412
413 uint32_t x = 0;
414 uint32_t y = 0;
415 uint32_t width = 1;
416 uint32_t height = 1;
417
418 if (command->IsWholeWindow()) {
419 width = frame_width;
420 height = frame_height;
421 } else if (command->IsRelative()) {
422 x = static_cast<uint32_t>(static_cast<float>(frame_width) *
423 command->GetX());
424 y = static_cast<uint32_t>(static_cast<float>(frame_height) *
425 command->GetY());
426 if (command->IsProbeRect()) {
427 width = static_cast<uint32_t>(static_cast<float>(frame_width) *
428 command->GetWidth());
429 height = static_cast<uint32_t>(static_cast<float>(frame_height) *
430 command->GetHeight());
431 }
432 } else {
433 x = static_cast<uint32_t>(command->GetX());
434 y = static_cast<uint32_t>(command->GetY());
435 width = static_cast<uint32_t>(command->GetWidth());
436 height = static_cast<uint32_t>(command->GetHeight());
437 }
438
439 if (x + width > frame_width || y + height > frame_height) {
440 return Result(
441 "Line " + std::to_string(command->GetLine()) +
442 ": Verifier::Probe Position(" + std::to_string(x + width - 1) + ", " +
443 std::to_string(y + height - 1) + ") is out of framebuffer scope (" +
444 std::to_string(frame_width) + "," + std::to_string(frame_height) + ")");
445 }
446
447 if (row_stride < frame_width * texel_stride) {
448 return Result("Line " + std::to_string(command->GetLine()) +
449 ": Verifier::Probe Row stride of " +
450 std::to_string(row_stride) + " is too small for " +
451 std::to_string(frame_width) + " texels of " +
452 std::to_string(texel_stride) + " bytes each");
453 }
454
455 double tolerance[4] = {0, 0, 0, 0};
456 bool is_tolerance_percent[4] = {0, 0, 0, 0};
457 SetupToleranceForTexels(command, tolerance, is_tolerance_percent);
458
459 const uint8_t* ptr = static_cast<const uint8_t*>(buf);
460 uint32_t count_of_invalid_pixels = 0;
461 uint32_t first_invalid_i = 0;
462 uint32_t first_invalid_j = 0;
463 std::vector<double> failure_values;
464 for (uint32_t j = 0; j < height; ++j) {
465 const uint8_t* p = ptr + row_stride * (j + y) + texel_stride * x;
466 for (uint32_t i = 0; i < width; ++i) {
467 auto actual_texel_values =
468 GetActualValuesFromTexel(p + texel_stride * i, fmt);
469 ScaleTexelValuesIfNeeded(&actual_texel_values, fmt);
470 if (!IsTexelEqualToExpected(actual_texel_values, fmt, command, tolerance,
471 is_tolerance_percent)) {
472 if (!count_of_invalid_pixels) {
473 failure_values = GetTexelInRGBA(actual_texel_values, fmt);
474 first_invalid_i = i;
475 first_invalid_j = j;
476 }
477 ++count_of_invalid_pixels;
478 }
479 }
480 }
481
482 if (count_of_invalid_pixels) {
483 float scale = fmt->IsNormalized() ? 255.f : 1.f;
484 std::string reason =
485 "Line " + std::to_string(command->GetLine()) +
486 ": Probe failed at: " + std::to_string(x + first_invalid_i) + ", " +
487 std::to_string(first_invalid_j + y) + "\n" +
488 " Expected: " + std::to_string(command->GetR() * scale) + ", " +
489 std::to_string(command->GetG() * scale) + ", " +
490 std::to_string(command->GetB() * scale);
491
492 if (command->IsRGBA()) {
493 reason += ", " + std::to_string(command->GetA() * scale);
494 }
495
496 reason +=
497 "\n Actual: " +
498 std::to_string(static_cast<float>(failure_values[0]) * scale) + ", " +
499 std::to_string(static_cast<float>(failure_values[1]) * scale) + ", " +
500 std::to_string(static_cast<float>(failure_values[2]) * scale);
501
502 if (command->IsRGBA()) {
503 reason +=
504 ", " + std::to_string(static_cast<float>(failure_values[3]) * scale);
505 }
506
507 reason += "\nProbe failed in " + std::to_string(count_of_invalid_pixels) +
508 " pixels";
509
510 return Result(reason);
511 }
512
513 return {};
514 }
515
ProbeSSBO(const ProbeSSBOCommand * command,uint32_t buffer_element_count,const void * buffer)516 Result Verifier::ProbeSSBO(const ProbeSSBOCommand* command,
517 uint32_t buffer_element_count,
518 const void* buffer) {
519 const auto& values = command->GetValues();
520 if (!buffer) {
521 if (values.empty())
522 return {};
523 return Result(
524 "Verifier::ProbeSSBO actual data is empty while expected "
525 "data is not");
526 }
527
528 auto* fmt = command->GetFormat();
529 size_t elem_count = values.size() / fmt->InputNeededPerElement();
530 size_t offset = static_cast<size_t>(command->GetOffset());
531 size_t size_in_bytes = buffer_element_count * fmt->SizeInBytes();
532 if ((elem_count * fmt->SizeInBytes()) + offset > size_in_bytes) {
533 return Result("Line " + std::to_string(command->GetLine()) +
534 ": Verifier::ProbeSSBO request to access to byte " +
535 std::to_string((elem_count * fmt->SizeInBytes()) + offset) +
536 " would read outside buffer of size " +
537 std::to_string(size_in_bytes) + " bytes");
538 }
539
540 if (offset % fmt->SizeInBytes() != 0) {
541 return Result("Line " + std::to_string(command->GetLine()) +
542 ": Verifier::ProbeSSBO given offset (" +
543 std::to_string(offset) + ") " +
544 "is not multiple of element size (" +
545 std::to_string(fmt->SizeInBytes()) + ")");
546 }
547
548 auto& segments = fmt->GetSegments();
549
550 const uint8_t* ptr = static_cast<const uint8_t*>(buffer) + offset;
551 for (size_t i = 0, k = 0; i < values.size(); ++i, ++k) {
552 if (k >= segments.size())
553 k = 0;
554
555 const auto& value = values[i];
556 auto segment = segments[k];
557 // Skip over any padding bytes.
558 while (segment.IsPadding()) {
559 ptr += segment.PaddingBytes();
560 ++k;
561 if (k >= segments.size())
562 k = 0;
563
564 segment = segments[k];
565 }
566
567 Result r;
568 FormatMode mode = segment.GetFormatMode();
569 uint32_t num_bits = segment.GetNumBits();
570 if (type::Type::IsInt8(mode, num_bits)) {
571 r = CheckValue<int8_t>(command, ptr, value);
572 } else if (type::Type::IsUint8(mode, num_bits)) {
573 r = CheckValue<uint8_t>(command, ptr, value);
574 } else if (type::Type::IsInt16(mode, num_bits)) {
575 r = CheckValue<int16_t>(command, ptr, value);
576 } else if (type::Type::IsUint16(mode, num_bits)) {
577 r = CheckValue<uint16_t>(command, ptr, value);
578 } else if (type::Type::IsInt32(mode, num_bits)) {
579 r = CheckValue<int32_t>(command, ptr, value);
580 } else if (type::Type::IsUint32(mode, num_bits)) {
581 r = CheckValue<uint32_t>(command, ptr, value);
582 } else if (type::Type::IsInt64(mode, num_bits)) {
583 r = CheckValue<int64_t>(command, ptr, value);
584 } else if (type::Type::IsUint64(mode, num_bits)) {
585 r = CheckValue<uint64_t>(command, ptr, value);
586 } else if (type::Type::IsFloat16(mode, num_bits)) {
587 r = CheckActualValue<float>(command, float16::HexFloatToFloat(ptr, 16),
588 value);
589 } else if (type::Type::IsFloat32(mode, num_bits)) {
590 r = CheckValue<float>(command, ptr, value);
591 } else if (type::Type::IsFloat64(mode, num_bits)) {
592 r = CheckValue<double>(command, ptr, value);
593 } else {
594 return Result("Unknown datum type");
595 }
596
597 if (!r.IsSuccess()) {
598 return Result("Line " + std::to_string(command->GetLine()) +
599 ": Verifier failed: " + r.Error() + ", at index " +
600 std::to_string(i));
601 }
602
603 ptr += segment.SizeInBytes();
604 }
605
606 return {};
607 }
608
609 } // namespace amber
610