aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordan sinclair <dsinclair@chromium.org>2019-08-03 13:51:11 -0400
committerGitHub <noreply@github.com>2019-08-03 13:51:11 -0400
commite9e2a52ea8282b482c8e4026ed23e986c1fb6177 (patch)
treef66927b5ef85fdec9e6f4ea3553562883bc557f1
parent5bb9c644ec17a298d901152e9a8e10f8d08c2ee3 (diff)
downloadamber-e9e2a52ea8282b482c8e4026ed23e986c1fb6177.tar.gz
Add root mean square error buffer comparison (#602)
This CL adds a `RMSE_BUFFER` comparator which can be used with two buffers to verify their values are within a given tolerance using the root mean square error comparison method. Issue #600
-rw-r--r--docs/amber_script.md6
-rw-r--r--src/amberscript/parser.cc55
-rw-r--r--src/amberscript/parser_expect_test.cc188
-rw-r--r--src/buffer.cc92
-rw-r--r--src/buffer.h8
-rw-r--r--src/command.h10
-rw-r--r--src/executor.cc7
-rw-r--r--src/format.h3
-rw-r--r--tests/cases/buffer_rmse.amber19
-rw-r--r--tests/cases/buffer_rmse_tolerance_too_large.expect_fail.amber20
-rw-r--r--tools/amber-syntax.vim2
-rw-r--r--tools/amber.sublime-syntax2
12 files changed, 358 insertions, 54 deletions
diff --git a/docs/amber_script.md b/docs/amber_script.md
index 16d1e41..3349270 100644
--- a/docs/amber_script.md
+++ b/docs/amber_script.md
@@ -418,6 +418,7 @@ CLEAR {pipeline}
* `EQ_RGB`
* `EQ_RGBA`
* `EQ_BUFFER`
+ * `RMSE_BUFFER`
```groovy
# Checks that |buffer_name| at |x| has the given |value|s when compared
@@ -446,6 +447,11 @@ EXPECT {buffer_name} IDX _x_in_pixels_ _y_in_pixels_ \
# Checks that |buffer_1| contents are equal to those of |buffer_2|
EXPECT {buffer_1} EQ_BUFFER {buffer_2}
+
+# Checks that the Root Mean Square Error when comparing |buffer_1| to
+# |buffer_2| is less than or equal too |tolerance|. Note, |tolerance| is a
+# unit-less number.
+EXPECT {buffer_1} RMSE_BUFFER {buffer_2} TOLERANCE _value_
```
## Examples
diff --git a/src/amberscript/parser.cc b/src/amberscript/parser.cc
index de43731..8a49264 100644
--- a/src/amberscript/parser.cc
+++ b/src/amberscript/parser.cc
@@ -1359,6 +1359,8 @@ Result Parser::ParseExpect() {
return Result("missing buffer name between EXPECT and IDX");
if (token->AsString() == "EQ_BUFFER")
return Result("missing buffer name between EXPECT and EQ_BUFFER");
+ if (token->AsString() == "RMSE_BUFFER")
+ return Result("missing buffer name between EXPECT and RMSE_BUFFER");
size_t line = tokenizer_->GetCurrentLine();
auto* buffer = script_->GetBuffer(token->AsString());
@@ -1371,44 +1373,61 @@ Result Parser::ParseExpect() {
if (!token->IsString())
return Result("Invalid comparator in EXPECT command");
- if (token->AsString() == "EQ_BUFFER") {
+ if (token->AsString() == "EQ_BUFFER" || token->AsString() == "RMSE_BUFFER") {
+ auto type = token->AsString();
+
token = tokenizer_->NextToken();
if (!token->IsString())
- return Result("invalid buffer name in EXPECT EQ_BUFFER command");
+ return Result("invalid buffer name in EXPECT " + type + " command");
auto* buffer_2 = script_->GetBuffer(token->AsString());
if (!buffer_2) {
- return Result("unknown buffer name for EXPECT EQ_BUFFER command: " +
- token->AsString());
+ return Result("unknown buffer name for EXPECT " + type +
+ " command: " + token->AsString());
}
if (!buffer->GetFormat()->Equal(buffer_2->GetFormat())) {
- return Result(
- "EXPECT EQ_BUFFER command cannot compare buffers of differing "
- "format");
+ return Result("EXPECT " + type +
+ " command cannot compare buffers of differing format");
}
if (buffer->ElementCount() != buffer_2->ElementCount()) {
- return Result(
- "EXPECT EQ_BUFFER command cannot compare buffers of different "
- "size: " +
- std::to_string(buffer->ElementCount()) + " vs " +
- std::to_string(buffer_2->ElementCount()));
+ return Result("EXPECT " + type +
+ " command cannot compare buffers of different size: " +
+ std::to_string(buffer->ElementCount()) + " vs " +
+ std::to_string(buffer_2->ElementCount()));
}
if (buffer->GetWidth() != buffer_2->GetWidth()) {
- return Result(
- "EXPECT EQ_BUFFER command cannot compare buffers of different width");
+ return Result("EXPECT " + type +
+ " command cannot compare buffers of different width");
}
if (buffer->GetHeight() != buffer_2->GetHeight()) {
- return Result(
- "EXPECT EQ_BUFFER command cannot compare buffers of different "
- "height");
+ return Result("EXPECT " + type +
+ " command cannot compare buffers of different height");
}
auto cmd = MakeUnique<CompareBufferCommand>(buffer, buffer_2);
+ if (type == "RMSE_BUFFER") {
+ cmd->SetComparator(CompareBufferCommand::Comparator::kRmse);
+
+ token = tokenizer_->NextToken();
+ if (!token->IsString() && token->AsString() == "TOLERANCE")
+ return Result("Missing TOLERANCE for EXPECT RMSE_BUFFER");
+
+ token = tokenizer_->NextToken();
+ if (!token->IsInteger() && !token->IsDouble())
+ return Result("Invalid TOLERANCE for EXPECT RMSE_BUFFER");
+
+ Result r = token->ConvertToDouble();
+ if (!r.IsSuccess())
+ return r;
+
+ cmd->SetTolerance(token->AsFloat());
+ }
+
command_list_.push_back(std::move(cmd));
// Early return
- return ValidateEndOfStatement("EXPECT EQ_BUFFER command");
+ return ValidateEndOfStatement("EXPECT " + type + " command");
}
if (token->AsString() != "IDX")
diff --git a/src/amberscript/parser_expect_test.cc b/src/amberscript/parser_expect_test.cc
index 7bb03e3..0c345a7 100644
--- a/src/amberscript/parser_expect_test.cc
+++ b/src/amberscript/parser_expect_test.cc
@@ -687,8 +687,7 @@ BUFFER orig_buf DATA_TYPE int32 SIZE 100 FILL 11
BUFFER dest_buf DATA_TYPE int32 SIZE 100 FILL 22
EXPECT orig_buf IDX 0 EQ 11
-EXPECT dest_buf IDX 0 EQ 22
-)";
+EXPECT dest_buf IDX 0 EQ 22)";
Parser parser;
Result r = parser.Parse(in);
@@ -699,69 +698,74 @@ TEST_F(AmberScriptParserTest, ExpectEqBuffer) {
std::string in = R"(
BUFFER buf_1 DATA_TYPE int32 SIZE 10 FILL 11
BUFFER buf_2 DATA_TYPE int32 SIZE 10 FILL 11
-
-EXPECT buf_1 EQ_BUFFER buf_2
-)";
+EXPECT buf_1 EQ_BUFFER buf_2)";
Parser parser;
Result r = parser.Parse(in);
ASSERT_TRUE(r.IsSuccess()) << r.Error();
+
+ auto script = parser.GetScript();
+ const auto& commands = script->GetCommands();
+ ASSERT_EQ(1U, commands.size());
+
+ auto* cmd = commands[0].get();
+ ASSERT_TRUE(cmd->IsCompareBuffer());
+
+ auto* cmp = cmd->AsCompareBuffer();
+ EXPECT_EQ(cmp->GetComparator(), CompareBufferCommand::Comparator::kEq);
+
+ ASSERT_TRUE(cmp->GetBuffer1() != nullptr);
+ EXPECT_EQ(cmp->GetBuffer1()->GetName(), "buf_1");
+
+ ASSERT_TRUE(cmp->GetBuffer2() != nullptr);
+ EXPECT_EQ(cmp->GetBuffer2()->GetName(), "buf_2");
}
TEST_F(AmberScriptParserTest, ExpectEqBufferMissingFirstBuffer) {
std::string in = R"(
BUFFER buf_2 DATA_TYPE int32 SIZE 10 FILL 22
-
-EXPECT EQ_BUFFER buf_2
-)";
+EXPECT EQ_BUFFER buf_2)";
Parser parser;
Result r = parser.Parse(in);
ASSERT_FALSE(r.IsSuccess());
- EXPECT_EQ("4: missing buffer name between EXPECT and EQ_BUFFER", r.Error());
+ EXPECT_EQ("3: missing buffer name between EXPECT and EQ_BUFFER", r.Error());
}
TEST_F(AmberScriptParserTest, ExpectEqBufferMissingSecondBuffer) {
std::string in = R"(
BUFFER buf_1 DATA_TYPE int32 SIZE 10 FILL 11
-
-EXPECT buf_1 EQ_BUFFER
-)";
+EXPECT buf_1 EQ_BUFFER)";
Parser parser;
Result r = parser.Parse(in);
ASSERT_FALSE(r.IsSuccess());
- EXPECT_EQ("5: invalid buffer name in EXPECT EQ_BUFFER command", r.Error());
+ EXPECT_EQ("3: invalid buffer name in EXPECT EQ_BUFFER command", r.Error());
}
TEST_F(AmberScriptParserTest, ExpectEqBufferInvalidFirstBuffer) {
- std::string in = R"(
-EXPECT 123 EQ_BUFFER
-)";
+ std::string in = R"(EXPECT 123 EQ_BUFFER)";
Parser parser;
Result r = parser.Parse(in);
ASSERT_FALSE(r.IsSuccess());
- EXPECT_EQ("2: invalid buffer name in EXPECT command", r.Error());
+ EXPECT_EQ("1: invalid buffer name in EXPECT command", r.Error());
}
TEST_F(AmberScriptParserTest, ExpectEqBufferUnknownFirstBuffer) {
- std::string in = R"(
-EXPECT unknown_buffer EQ_BUFFER
-)";
+ std::string in = R"(EXPECT unknown_buffer EQ_BUFFER)";
Parser parser;
Result r = parser.Parse(in);
ASSERT_FALSE(r.IsSuccess());
- EXPECT_EQ("2: unknown buffer name for EXPECT command: unknown_buffer",
+ EXPECT_EQ("1: unknown buffer name for EXPECT command: unknown_buffer",
r.Error());
}
TEST_F(AmberScriptParserTest, ExpectEqBufferInvalidSecondBuffer) {
std::string in = R"(
BUFFER buf DATA_TYPE int32 SIZE 10 FILL 11
-EXPECT buf EQ_BUFFER 123
-)";
+EXPECT buf EQ_BUFFER 123)";
Parser parser;
Result r = parser.Parse(in);
@@ -772,8 +776,7 @@ EXPECT buf EQ_BUFFER 123
TEST_F(AmberScriptParserTest, ExpectEqBufferUnknownSecondBuffer) {
std::string in = R"(
BUFFER buf DATA_TYPE int32 SIZE 10 FILL 11
-EXPECT buf EQ_BUFFER unknown_buffer
-)";
+EXPECT buf EQ_BUFFER unknown_buffer)";
Parser parser;
Result r = parser.Parse(in);
@@ -787,15 +790,13 @@ TEST_F(AmberScriptParserTest, ExpectEqBufferDifferentSize) {
std::string in = R"(
BUFFER buf_1 DATA_TYPE int32 SIZE 10 FILL 11
BUFFER buf_2 DATA_TYPE int32 SIZE 99 FILL 11
-
-EXPECT buf_1 EQ_BUFFER buf_2
-)";
+EXPECT buf_1 EQ_BUFFER buf_2)";
Parser parser;
Result r = parser.Parse(in);
ASSERT_FALSE(r.IsSuccess());
EXPECT_EQ(
- "5: EXPECT EQ_BUFFER command cannot compare buffers of different size: "
+ "4: EXPECT EQ_BUFFER command cannot compare buffers of different size: "
"10 vs 99",
r.Error());
}
@@ -804,15 +805,13 @@ TEST_F(AmberScriptParserTest, ExpectEqBufferDifferentType) {
std::string in = R"(
BUFFER buf_1 DATA_TYPE int32 SIZE 10 FILL 11
BUFFER buf_2 FORMAT R32G32B32A32_SFLOAT
-
-EXPECT buf_1 EQ_BUFFER buf_2
-)";
+EXPECT buf_1 EQ_BUFFER buf_2)";
Parser parser;
Result r = parser.Parse(in);
ASSERT_FALSE(r.IsSuccess());
EXPECT_EQ(
- "5: EXPECT EQ_BUFFER command cannot compare buffers of differing format",
+ "4: EXPECT EQ_BUFFER command cannot compare buffers of differing format",
r.Error());
}
@@ -949,5 +948,128 @@ EXPECT orig_buf IDX 5 TOLERANCE 1 2 3 4 NE 11)";
EXPECT_EQ("3: TOLERANCE only available with EQ probes", r.Error());
}
+TEST_F(AmberScriptParserTest, ExpectRMSEBuffer) {
+ std::string in = R"(
+BUFFER buf_1 DATA_TYPE int32 SIZE 10 FILL 11
+BUFFER buf_2 DATA_TYPE int32 SIZE 10 FILL 12
+EXPECT buf_1 RMSE_BUFFER buf_2 TOLERANCE 0.1)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_TRUE(r.IsSuccess()) << r.Error();
+
+ auto script = parser.GetScript();
+ const auto& commands = script->GetCommands();
+ ASSERT_EQ(1U, commands.size());
+
+ auto* cmd = commands[0].get();
+ ASSERT_TRUE(cmd->IsCompareBuffer());
+
+ auto* cmp = cmd->AsCompareBuffer();
+ EXPECT_EQ(cmp->GetComparator(), CompareBufferCommand::Comparator::kRmse);
+ EXPECT_FLOAT_EQ(cmp->GetTolerance(), 0.1f);
+
+ ASSERT_TRUE(cmp->GetBuffer1() != nullptr);
+ EXPECT_EQ(cmp->GetBuffer1()->GetName(), "buf_1");
+
+ ASSERT_TRUE(cmp->GetBuffer2() != nullptr);
+ EXPECT_EQ(cmp->GetBuffer2()->GetName(), "buf_2");
+}
+
+TEST_F(AmberScriptParserTest, ExpectRMSEBufferMissingFirstBuffer) {
+ std::string in = R"(
+BUFFER buf_2 DATA_TYPE int32 SIZE 10 FILL 22
+EXPECT RMSE_BUFFER buf_2)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("3: missing buffer name between EXPECT and RMSE_BUFFER", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, ExpectRMSEBufferMissingSecondBuffer) {
+ std::string in = R"(
+BUFFER buf_1 DATA_TYPE int32 SIZE 10 FILL 11
+EXPECT buf_1 RMSE_BUFFER)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("3: invalid buffer name in EXPECT RMSE_BUFFER command", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, ExpectRMSEBufferInvalidFirstBuffer) {
+ std::string in = R"(EXPECT 123 RMSE_BUFFER)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("1: invalid buffer name in EXPECT command", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, ExpectRMSEBufferUnknownFirstBuffer) {
+ std::string in = R"(EXPECT unknown_buffer RMSE_BUFFER)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("1: unknown buffer name for EXPECT command: unknown_buffer",
+ r.Error());
+}
+
+TEST_F(AmberScriptParserTest, ExpectRMSEBufferInvalidSecondBuffer) {
+ std::string in = R"(
+BUFFER buf DATA_TYPE int32 SIZE 10 FILL 11
+EXPECT buf RMSE_BUFFER 123)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("3: invalid buffer name in EXPECT RMSE_BUFFER command", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, ExpectRMSEBufferUnknownSecondBuffer) {
+ std::string in = R"(
+BUFFER buf DATA_TYPE int32 SIZE 10 FILL 11
+EXPECT buf RMSE_BUFFER unknown_buffer)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ(
+ "3: unknown buffer name for EXPECT RMSE_BUFFER command: unknown_buffer",
+ r.Error());
+}
+
+TEST_F(AmberScriptParserTest, ExpectRMSEBufferDifferentSize) {
+ std::string in = R"(
+BUFFER buf_1 DATA_TYPE int32 SIZE 10 FILL 11
+BUFFER buf_2 DATA_TYPE int32 SIZE 99 FILL 11
+EXPECT buf_1 RMSE_BUFFER buf_2)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ(
+ "4: EXPECT RMSE_BUFFER command cannot compare buffers of different size: "
+ "10 vs 99",
+ r.Error());
+}
+
+TEST_F(AmberScriptParserTest, ExpectRMSEBufferDifferentType) {
+ std::string in = R"(
+BUFFER buf_1 DATA_TYPE int32 SIZE 10 FILL 11
+BUFFER buf_2 FORMAT R32G32B32A32_SFLOAT
+EXPECT buf_1 RMSE_BUFFER buf_2)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ(
+ "4: EXPECT RMSE_BUFFER command cannot compare buffers of differing "
+ "format",
+ r.Error());
+}
+
} // namespace amberscript
} // namespace amber
diff --git a/src/buffer.cc b/src/buffer.cc
index 617ff7a..f160923 100644
--- a/src/buffer.cc
+++ b/src/buffer.cc
@@ -15,6 +15,7 @@
#include "src/buffer.h"
#include <cassert>
+#include <cmath>
#include <cstring>
namespace amber {
@@ -53,6 +54,45 @@ T* ValuesAs(uint8_t* values) {
return reinterpret_cast<T*>(values);
}
+template <typename T>
+double Sub(const uint8_t* buf1, const uint8_t* buf2) {
+ return static_cast<double>(*reinterpret_cast<const T*>(buf1) -
+ *reinterpret_cast<const T*>(buf2));
+}
+
+double CalculateDiff(const Format::Component& comp,
+ const uint8_t* buf1,
+ const uint8_t* buf2) {
+ if (comp.IsInt8())
+ return Sub<int8_t>(buf1, buf2);
+ if (comp.IsInt16())
+ return Sub<int16_t>(buf1, buf2);
+ if (comp.IsInt32())
+ return Sub<int32_t>(buf1, buf2);
+ if (comp.IsInt64())
+ return Sub<int64_t>(buf1, buf2);
+ if (comp.IsUint8())
+ return Sub<uint8_t>(buf1, buf2);
+ if (comp.IsUint16())
+ return Sub<uint16_t>(buf1, buf2);
+ if (comp.IsUint32())
+ return Sub<uint32_t>(buf1, buf2);
+ if (comp.IsUint64())
+ return Sub<uint64_t>(buf1, buf2);
+ // TOOD(dsinclair): Handle float16 ...
+ if (comp.IsFloat16()) {
+ assert(false && "Float16 suppport not implemented");
+ return 0.0;
+ }
+ if (comp.IsFloat())
+ return Sub<float>(buf1, buf2);
+ if (comp.IsDouble())
+ return Sub<double>(buf1, buf2);
+
+ assert(false && "NOTREACHED");
+ return 0.0;
+}
+
} // namespace
Buffer::Buffer() = default;
@@ -111,6 +151,58 @@ Result Buffer::IsEqual(Buffer* buffer) const {
return {};
}
+std::vector<double> Buffer::CalculateDiffs(const Buffer* buffer) const {
+ std::vector<double> diffs;
+
+ auto* buf_1_ptr = GetValues<uint8_t>();
+ auto* buf_2_ptr = buffer->GetValues<uint8_t>();
+ auto comps = format_->GetComponents();
+
+ for (size_t i = 0; i < ElementCount(); ++i) {
+ for (size_t j = 0; j < format_->ColumnCount(); ++j) {
+ auto* buf_1_row_ptr = buf_1_ptr;
+ auto* buf_2_row_ptr = buf_2_ptr;
+ for (size_t k = 0; k < format_->RowCount(); ++k) {
+ diffs.push_back(CalculateDiff(comps[k], buf_1_row_ptr, buf_2_row_ptr));
+
+ buf_1_row_ptr += comps[k].SizeInBytes();
+ buf_2_row_ptr += comps[k].SizeInBytes();
+ }
+ buf_1_ptr += format_->SizeInBytesPerRow();
+ buf_2_ptr += format_->SizeInBytesPerRow();
+ }
+ }
+
+ return diffs;
+}
+
+Result Buffer::CompareRMSE(Buffer* buffer, float tolerance) const {
+ if (!buffer->format_->Equal(format_.get()))
+ return Result{"Buffers have a different format"};
+ if (buffer->element_count_ != element_count_)
+ return Result{"Buffers have a different size"};
+ if (buffer->width_ != width_)
+ return Result{"Buffers have a different width"};
+ if (buffer->height_ != height_)
+ return Result{"Buffers have a different height"};
+ if (buffer->ValueCount() != ValueCount())
+ return Result{"Buffers have a different number of values"};
+
+ auto diffs = CalculateDiffs(buffer);
+ double sum = 0.0;
+ for (const auto val : diffs)
+ sum += (val * val);
+
+ sum /= diffs.size();
+ double rmse = std::sqrt(sum);
+ if (rmse > static_cast<double>(tolerance)) {
+ return Result("Root Mean Square Error of " + std::to_string(rmse) +
+ " is greater then tolerance of " + std::to_string(tolerance));
+ }
+
+ return {};
+}
+
Result Buffer::SetData(const std::vector<Value>& data) {
return SetDataWithOffset(data, 0);
}
diff --git a/src/buffer.h b/src/buffer.h
index 906454d..c9b1db9 100644
--- a/src/buffer.h
+++ b/src/buffer.h
@@ -193,11 +193,19 @@ class Buffer {
/// Succeeds only if both buffer contents are equal
Result IsEqual(Buffer* buffer) const;
+ /// Compare the RMSE of this buffer against |buffer|. The RMSE must be
+ /// less than |tolerance|.
+ Result CompareRMSE(Buffer* buffer, float tolerance) const;
+
private:
uint32_t WriteValueFromComponent(const Value& value,
const Format::Component& comp,
uint8_t* ptr);
+ // Calculates the difference between the value stored in this buffer and
+ // those stored in |buffer| and returns all the values.
+ std::vector<double> CalculateDiffs(const Buffer* buffer) const;
+
BufferType buffer_type_ = BufferType::kUnknown;
std::string name_;
/// max_size_in_bytes_ is the total size in bytes needed to hold the buffer
diff --git a/src/command.h b/src/command.h
index 24fde27..f10f8fe 100644
--- a/src/command.h
+++ b/src/command.h
@@ -212,17 +212,27 @@ class DrawArraysCommand : public PipelineCommand {
/// A command to compare two buffers.
class CompareBufferCommand : public Command {
public:
+ enum class Comparator { kEq, kRmse };
+
CompareBufferCommand(Buffer* buffer_1, Buffer* buffer_2);
~CompareBufferCommand() override;
Buffer* GetBuffer1() const { return buffer_1_; }
Buffer* GetBuffer2() const { return buffer_2_; }
+ void SetComparator(Comparator type) { comparator_ = type; }
+ Comparator GetComparator() const { return comparator_; }
+
+ void SetTolerance(float tolerance) { tolerance_ = tolerance; }
+ float GetTolerance() const { return tolerance_; }
+
std::string ToString() const override { return "CompareBufferCommand"; }
private:
Buffer* buffer_1_;
Buffer* buffer_2_;
+ float tolerance_ = 0.0;
+ Comparator comparator_ = Comparator::kEq;
};
/// Command to execute a compute command.
diff --git a/src/executor.cc b/src/executor.cc
index f3c829f..3cd67c3 100644
--- a/src/executor.cc
+++ b/src/executor.cc
@@ -125,7 +125,12 @@ Result Executor::ExecuteCommand(Engine* engine, Command* cmd) {
auto compare = cmd->AsCompareBuffer();
auto buffer_1 = compare->GetBuffer1();
auto buffer_2 = compare->GetBuffer2();
- return buffer_1->IsEqual(buffer_2);
+ switch (compare->GetComparator()) {
+ case CompareBufferCommand::Comparator::kRmse:
+ return buffer_1->CompareRMSE(buffer_2, compare->GetTolerance());
+ case CompareBufferCommand::Comparator::kEq:
+ return buffer_1->IsEqual(buffer_2);
+ }
}
if (cmd->IsCopy()) {
auto copy = cmd->AsCopy();
diff --git a/src/format.h b/src/format.h
index 0888a43..d4dbe04 100644
--- a/src/format.h
+++ b/src/format.h
@@ -45,6 +45,9 @@ class Format {
FormatMode mode;
uint8_t num_bits;
+ /// Returns the number of bytes used to store this component.
+ size_t SizeInBytes() const { return num_bits / 8; }
+
/// Is this component represented by an 8 bit signed integer. (This includes
/// int, scaled, rgb and norm values).
bool IsInt8() const {
diff --git a/tests/cases/buffer_rmse.amber b/tests/cases/buffer_rmse.amber
new file mode 100644
index 0000000..bad1aa5
--- /dev/null
+++ b/tests/cases/buffer_rmse.amber
@@ -0,0 +1,19 @@
+#!amber
+# Copyright 2019 The Amber Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+BUFFER in_buf DATA_TYPE float DATA 1.1 2.2 3.3 4.4 END
+BUFFER out_buf DATA_TYPE float DATA 1.0 2.1 3.2 4.3 END
+
+EXPECT in_buf RMSE_BUFFER out_buf TOLERANCE 0.1
diff --git a/tests/cases/buffer_rmse_tolerance_too_large.expect_fail.amber b/tests/cases/buffer_rmse_tolerance_too_large.expect_fail.amber
new file mode 100644
index 0000000..af5a5fa
--- /dev/null
+++ b/tests/cases/buffer_rmse_tolerance_too_large.expect_fail.amber
@@ -0,0 +1,20 @@
+#!amber
+# Copyright 2019 The Amber Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+BUFFER in_buf DATA_TYPE float DATA 1.1 2.2 3.3 4.4 END
+BUFFER out_buf DATA_TYPE float DATA 1.0 2.1 3.2 4.3 END
+
+EXPECT in_buf RMSE_BUFFER out_buf TOLERANCE 0.01
+
diff --git a/tools/amber-syntax.vim b/tools/amber-syntax.vim
index 0d47c6c..b474cb7 100644
--- a/tools/amber-syntax.vim
+++ b/tools/amber-syntax.vim
@@ -38,7 +38,7 @@ syn keyword amberBlockCmd VERTEX_DATA INDEX_DATA INDEXED IMAGE_ATTACHMENT
syn keyword amberBlockCmd DEPTH_STENCIL_ATTACHMENT DEVICE_FEATURE TOLERANCE
syn keyword amberBlockCmd REPEAT COPY DERIVE_PIPELINE FROM
-syn keyword amberComparator EQ NE LT LE GT GE EQ_RGB EQ_RGBA EQ_BUFFER
+syn keyword amberComparator EQ NE LT LE GT GE EQ_RGB EQ_RGBA EQ_BUFFER RMSE_BUFFER
syn keyword amberKeyword compute vertex geometry fragment graphics
syn keyword amberKeyword tessellation_evaulation tessellation_control multi
diff --git a/tools/amber.sublime-syntax b/tools/amber.sublime-syntax
index 92ac566..8fe18ff 100644
--- a/tools/amber.sublime-syntax
+++ b/tools/amber.sublime-syntax
@@ -40,7 +40,7 @@ contexts:
scope: constant.character.escape.amber
- match: '\b(uniform|storage|push_constant|color|depth_stencil)\b'
scope: constant.character.escape.amber
- - match: '\b(EQ|NE|LT|LE|GT|GE|EQ_RGB|EQ_RGBA|EQ_BUFFER)\b'
+ - match: '\b(EQ|NE|LT|LE|GT|GE|EQ_RGB|EQ_RGBA|EQ_BUFFER|RMSE_BUFFER)\b'
scope: constant.character.esape.amber
- match: '\b(GLSL|HLSL|SPIRV-ASM|SPIRV-HEX|OPENCL-C)\b'
scope: constant.character.escape.amber