Skip to content

Commit

Permalink
Implement AmberScript COPY command (google#371)
Browse files Browse the repository at this point in the history
This change introduce the COPY command to amberscript,
which enables to copy a buffer content into an other
buffer.
  • Loading branch information
hevrard authored and dj2 committed Mar 26, 2019
1 parent fb6849f commit bc902d4
Show file tree
Hide file tree
Showing 11 changed files with 271 additions and 1 deletion.
14 changes: 14 additions & 0 deletions docs/amber_script.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,20 @@ FILL <value>
SERIES_FROM <start> INC_BY <inc>
```

#### Buffer Copy

The COPY command copy all data, values and memory from <buffer_from> to
<buffer_to>.

```
COPY <buffer_from> TO <buffer_to>
```

Both buffers must be declared, and of the same type.

Buffers used as copy destination can be used only as copy destination, and as
argument to an EXPECT command.

### Pipelines

#### Pipeline type
Expand Down
59 changes: 58 additions & 1 deletion src/amberscript/parser.cc
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ Result Parser::Parse(const std::string& data) {
r = ParseBuffer();
} else if (tok == "CLEAR") {
r = ParseClear();
} else if (tok == "COPY") {
r = ParseCopy();
} else if (tok == "EXPECT") {
r = ParseExpect();
} else if (tok == "PIPELINE") {
Expand All @@ -91,7 +93,7 @@ Result Parser::Parse(const std::string& data) {
return Result(make_error(r.Error()));
}

// Generate any needed color and depth attachments. This is done before
// Generate any needed color and depth attachments. This is done before
// validating in case one of the pipelines specifies the framebuffer size
// it needs to be verified against all other pipelines.
for (const auto& pipeline : script_->GetPipelines()) {
Expand Down Expand Up @@ -1178,5 +1180,60 @@ Result Parser::ParseExpect() {
return ValidateEndOfStatement("EXPECT command");
}

Result Parser::ParseCopy() {
auto token = tokenizer_->NextToken();
if (token->IsEOL() || token->IsEOS())
return Result("missing buffer name after COPY");
if (!token->IsString())
return Result("invalid buffer name after COPY");

auto name = token->AsString();
if (name == "TO")
return Result("missing buffer name between COPY and TO");

Buffer* buffer_from = script_->GetBuffer(name);
if (!buffer_from)
return Result("COPY origin buffer was not declared");

token = tokenizer_->NextToken();
if (token->IsEOL() || token->IsEOS())
return Result("missing 'TO' after COPY and buffer name");
if (!token->IsString())
return Result("expected 'TO' after COPY and buffer name");

name = token->AsString();
if (name != "TO")
return Result("expected 'TO' after COPY and buffer name");

token = tokenizer_->NextToken();
if (token->IsEOL() || token->IsEOS())
return Result("missing buffer name after TO");
if (!token->IsString())
return Result("invalid buffer name after TO");

name = token->AsString();
Buffer* buffer_to = script_->GetBuffer(name);
if (!buffer_to)
return Result("COPY destination buffer was not declared");

if (buffer_to->GetBufferType() == amber::BufferType::kUnknown) {
// Set destination buffer to mirror origin buffer
buffer_to->SetBufferType(buffer_from->GetBufferType());
buffer_to->SetWidth(buffer_from->GetWidth());
buffer_to->SetHeight(buffer_from->GetHeight());
buffer_to->SetSize(buffer_from->GetSize());
}

if (buffer_from->GetBufferType() != buffer_to->GetBufferType())
return Result("cannot COPY between buffers of different types");
if (buffer_from == buffer_to)
return Result("COPY origin and destination buffers are identical");

auto cmd = MakeUnique<CopyCommand>(buffer_from, buffer_to);
script_->AddCommand(std::move(cmd));

return ValidateEndOfStatement("COPY command");
}

} // namespace amberscript
} // namespace amber
1 change: 1 addition & 0 deletions src/amberscript/parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ class Parser : public amber::Parser {
Result ParseRun();
Result ParseClear();
Result ParseExpect();
Result ParseCopy();

// Parses a set of values out of the token stream. |name| is the name of the
// current command we're parsing for error purposes. The |type| is the type
Expand Down
93 changes: 93 additions & 0 deletions src/amberscript/parser_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4164,5 +4164,98 @@ EXPECT dest_buf IDX 0 EQ 22
ASSERT_TRUE(r.IsSuccess()) << r.Error();
}

TEST_F(AmberScriptParserTest, Copy) {
std::string in = R"(
BUFFER from FORMAT R32G32B32A32_SFLOAT
BUFFER dest FORMAT R32G32B32A32_SFLOAT
COPY from TO dest)";

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->IsCopy());
}

TEST_F(AmberScriptParserTest, CopyUndeclaredOriginBuffer) {
std::string in = R"(
COPY from)";

Parser parser;
Result r = parser.Parse(in);
ASSERT_FALSE(r.IsSuccess());
EXPECT_EQ("2: COPY origin buffer was not declared", r.Error());
}

TEST_F(AmberScriptParserTest, CopyInvalidOriginBufferName) {
std::string in = R"(
COPY 123)";

Parser parser;
Result r = parser.Parse(in);
ASSERT_FALSE(r.IsSuccess());
EXPECT_EQ("2: invalid buffer name after COPY", r.Error());
}

TEST_F(AmberScriptParserTest, CopyUndeclaredDestinationBuffer) {
std::string in = R"(
BUFFER from FORMAT R32G32B32A32_SFLOAT
COPY from TO dest)";

Parser parser;
Result r = parser.Parse(in);
ASSERT_FALSE(r.IsSuccess());
EXPECT_EQ("3: COPY destination buffer was not declared", r.Error());
}

TEST_F(AmberScriptParserTest, CopyMissingOriginBuffer) {
std::string in = R"(
COPY)";

Parser parser;
Result r = parser.Parse(in);
ASSERT_FALSE(r.IsSuccess());
EXPECT_EQ("2: missing buffer name after COPY", r.Error());
}

TEST_F(AmberScriptParserTest, CopyMissingDestinationBuffer) {
std::string in = R"(
BUFFER from FORMAT R32G32B32A32_SFLOAT
COPY from TO)";

Parser parser;
Result r = parser.Parse(in);
ASSERT_FALSE(r.IsSuccess());
EXPECT_EQ("3: missing buffer name after TO", r.Error());
}

TEST_F(AmberScriptParserTest, CopyToSameBuffer) {
std::string in = R"(
BUFFER from FORMAT R32G32B32A32_SFLOAT
COPY from TO from)";

Parser parser;
Result r = parser.Parse(in);
ASSERT_FALSE(r.IsSuccess());
EXPECT_EQ("3: COPY origin and destination buffers are identical", r.Error());
}

TEST_F(AmberScriptParserTest, CopyMissingToKeyword) {
std::string in = R"(
BUFFER from FORMAT R32G32B32A32_SFLOAT
BUFFER dest FORMAT R32G32B32A32_SFLOAT
COPY from dest)";

Parser parser;
Result r = parser.Parse(in);
ASSERT_FALSE(r.IsSuccess());
EXPECT_EQ("4: expected 'TO' after COPY and buffer name", r.Error());
}

} // namespace amberscript
} // namespace amber
11 changes: 11 additions & 0 deletions src/buffer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,17 @@ FormatBuffer* Buffer::AsFormatBuffer() {
return static_cast<FormatBuffer*>(this);
}

Result Buffer::CopyTo(Buffer* buffer) const {
if (buffer->width_ != width_)
return Result("Buffer::CopyBaseFields() buffers have a different width");
if (buffer->height_ != height_)
return Result("Buffer::CopyBaseFields() buffers have a different height");
if (buffer->size_ != size_)
return Result("Buffer::CopyBaseFields() buffers have a different size");
buffer->values_ = values_;
return {};
}

DataBuffer::DataBuffer() = default;

DataBuffer::DataBuffer(BufferType type) : Buffer(type) {}
Expand Down
3 changes: 3 additions & 0 deletions src/buffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,9 @@ class Buffer {
return reinterpret_cast<const T*>(values_.data());
}

/// Copy the buffer values to an other one
Result CopyTo(Buffer* buffer) const;

protected:
/// Create an un-typed buffer.
Buffer();
Expand Down
9 changes: 9 additions & 0 deletions src/command.cc
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ ComputeCommand* Command::AsCompute() {
return static_cast<ComputeCommand*>(this);
}

CopyCommand* Command::AsCopy() {
return static_cast<CopyCommand*>(this);
}

DrawArraysCommand* Command::AsDrawArrays() {
return static_cast<DrawArraysCommand*>(this);
}
Expand Down Expand Up @@ -108,6 +112,11 @@ BufferCommand::BufferCommand(BufferType type, Pipeline* pipeline)

BufferCommand::~BufferCommand() = default;

CopyCommand::CopyCommand(Buffer* buffer_from, Buffer* buffer_to)
: Command(Type::kCopy), buffer_from_(buffer_from), buffer_to_(buffer_to) {}

CopyCommand::~CopyCommand() = default;

ClearCommand::ClearCommand(Pipeline* pipeline)
: PipelineCommand(Type::kClear, pipeline) {}

Expand Down
17 changes: 17 additions & 0 deletions src/command.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ class ClearColorCommand;
class ClearDepthCommand;
class ClearStencilCommand;
class ComputeCommand;
class CopyCommand;
class DrawArraysCommand;
class DrawRectCommand;
class EntryPointCommand;
Expand All @@ -51,6 +52,7 @@ class Command {
kClearDepth,
kClearStencil,
kCompute,
kCopy,
kDrawArrays,
kDrawRect,
kEntryPoint,
Expand All @@ -66,6 +68,7 @@ class Command {
bool IsDrawRect() const { return command_type_ == Type::kDrawRect; }
bool IsDrawArrays() const { return command_type_ == Type::kDrawArrays; }
bool IsCompute() const { return command_type_ == Type::kCompute; }
bool IsCopy() const { return command_type_ == Type::kCopy; }
bool IsProbe() const { return command_type_ == Type::kProbe; }
bool IsProbeSSBO() const { return command_type_ == Type::kProbeSSBO; }
bool IsBuffer() const { return command_type_ == Type::kBuffer; }
Expand All @@ -83,6 +86,7 @@ class Command {
ClearDepthCommand* AsClearDepth();
ClearStencilCommand* AsClearStencil();
ComputeCommand* AsCompute();
CopyCommand* AsCopy();
DrawArraysCommand* AsDrawArrays();
DrawRectCommand* AsDrawRect();
EntryPointCommand* AsEntryPoint();
Expand Down Expand Up @@ -203,6 +207,19 @@ class ComputeCommand : public PipelineCommand {
uint32_t z_ = 0;
};

class CopyCommand : public Command {
public:
CopyCommand(Buffer* buffer_from, Buffer* buffer_to);
~CopyCommand() override;

Buffer* GetBufferFrom() const { return buffer_from_; }
Buffer* GetBufferTo() const { return buffer_to_; }

private:
Buffer* buffer_from_;
Buffer* buffer_to_;
};

class Probe : public Command {
public:
struct Tolerance {
Expand Down
5 changes: 5 additions & 0 deletions src/executor.cc
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,11 @@ Result Executor::Execute(Engine* engine,
r = engine->DoClearDepth(cmd->AsClearDepth());
} else if (cmd->IsClearStencil()) {
r = engine->DoClearStencil(cmd->AsClearStencil());
} else if (cmd->IsCopy()) {
auto copy = cmd->AsCopy();
auto buffer_from = copy->GetBufferFrom();
auto buffer_to = copy->GetBufferTo();
r = buffer_from->CopyTo(buffer_to);
} else if (cmd->IsDrawRect()) {
r = engine->DoDrawRect(cmd->AsDrawRect());
} else if (cmd->IsDrawArrays()) {
Expand Down
22 changes: 22 additions & 0 deletions tests/cases/copy_data_buffer.amber
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!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 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
COPY orig_buf TO dest_buf
EXPECT dest_buf IDX 0 EQ 11
38 changes: 38 additions & 0 deletions tests/cases/copy_format_buffer.amber
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#!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.

SHADER vertex vert_shader PASSTHROUGH
SHADER fragment frag_shader GLSL
#version 430
layout(location = 0) out vec4 color_out;
void main() {
color_out = vec4(1.0, 0.0, 0.0, 1.0);
}
END

BUFFER img_buf FORMAT B8G8R8A8_UNORM
BUFFER dest_buf FORMAT B8G8R8A8_UNORM

PIPELINE graphics my_pipeline
ATTACH vert_shader
ATTACH frag_shader
FRAMEBUFFER_SIZE 256 256
BIND BUFFER img_buf AS color LOCATION 0
END

RUN my_pipeline DRAW_RECT POS 0 0 SIZE 256 256
EXPECT img_buf IDX 0 0 SIZE 256 256 EQ_RGBA 1 0 0 1
COPY img_buf TO dest_buf
EXPECT dest_buf IDX 0 0 SIZE 256 256 EQ_RGBA 1 0 0 1

0 comments on commit bc902d4

Please sign in to comment.