From 38fcbe6700f725b80a68168f046ac86020254500 Mon Sep 17 00:00:00 2001 From: asuonpaa <34128694+asuonpaa@users.noreply.github.com> Date: Tue, 24 Mar 2020 14:43:27 +0200 Subject: Added a support for loading a png file into a buffer (#823) This CL adds the `FILE` parameter to `BUFFER` commands. This allows loading file data into the buffer. Currently only PNG data is supported. --- docs/amber_script.md | 5 +++ include/amber/amber.h | 3 ++ samples/amber.cc | 22 ++++++++++++ samples/png.cc | 19 ++++++++++ samples/png.h | 8 +++++ src/amberscript/parser.cc | 25 +++++++++---- src/amberscript/parser_buffer_test.cc | 25 +++++++++++++ src/buffer.h | 7 ++++ src/executor.cc | 15 ++++++++ tests/cases/draw_png_texture.amber | 66 ++++++++++++++++++++++++++++++++++ tests/cases/texture.png | Bin 0 -> 32776 bytes tests/run_tests.py | 2 +- 12 files changed, 190 insertions(+), 7 deletions(-) create mode 100644 tests/cases/draw_png_texture.amber create mode 100644 tests/cases/texture.png diff --git a/docs/amber_script.md b/docs/amber_script.md index 1191d60..56430f7 100644 --- a/docs/amber_script.md +++ b/docs/amber_script.md @@ -156,6 +156,11 @@ BUFFER {name} DATA_TYPE {type} {STD140 | STD430} WIDTH {w} HEIGHT {h} \ # The buffer will be sized based on the `RENDER_SIZE` of the `PIPELINE`. BUFFER {name} FORMAT {format_string} \ [ MIP_LEVELS _mip_levels_ (default 1) ] + +# Load buffer data from a PNG image with file name specified by `FILE`. +# The file path is relative to the script file being run. Format specified +# by `FORMAT` must match the image format. +BUFFER {name} FORMAT {format_string} FILE {file_name.png} ``` #### Images diff --git a/include/amber/amber.h b/include/amber/amber.h index 1fffd38..b665de2 100644 --- a/include/amber/amber.h +++ b/include/amber/amber.h @@ -85,6 +85,9 @@ class Delegate { virtual uint64_t GetTimestampNs() const = 0; /// Tells whether to log each test as it's executed virtual bool LogExecuteCalls() const = 0; + /// Loads buffer data from a file + virtual amber::Result LoadBufferData(const std::string file_name, + amber::BufferInfo* buffer) const = 0; }; /// Stores configuration options for Amber. diff --git a/samples/amber.cc b/samples/amber.cc index 221da50..31e66f9 100644 --- a/samples/amber.cc +++ b/samples/amber.cc @@ -324,10 +324,29 @@ class SampleDelegate : public amber::Delegate { return timestamp::SampleGetTimestampNs(); } + void SetScriptPath(std::string path) { path_ = path; } + + amber::Result LoadBufferData(const std::string file_name, + amber::BufferInfo* buffer) const override { +#if AMBER_ENABLE_LODEPNG + // Try to load as png first. + amber::Result r = png::LoadPNG(path_ + file_name, &buffer->width, + &buffer->height, &buffer->values); + + if (r.IsSuccess()) + return r; +#endif // AMBER_ENABLE_LODEPNG + + // TODO(asuonpaa): Try to load a binary format. + + return amber::Result("Failed to load buffer data " + file_name); + } + private: bool log_graphics_calls_ = false; bool log_graphics_calls_time_ = false; bool log_execute_calls_ = false; + std::string path_ = ""; }; std::string disassemble(const std::string& env, @@ -531,6 +550,9 @@ int main(int argc, const char** argv) { const auto* recipe = recipe_data_elem.recipe.get(); const auto& file = recipe_data_elem.file; + // Parse file path and set it for delegate to use when loading buffer data. + delegate.SetScriptPath(file.substr(0, file.find_last_of("/\\") + 1)); + amber::Amber am; result = am.Execute(recipe, &amber_options); if (!result.IsSuccess()) { diff --git a/samples/png.cc b/samples/png.cc index 93e2279..3dbf4fd 100644 --- a/samples/png.cc +++ b/samples/png.cc @@ -81,4 +81,23 @@ amber::Result ConvertToPNG(uint32_t width, return {}; } +amber::Result LoadPNG(const std::string file_name, + uint32_t* width, + uint32_t* height, + std::vector* values) { + std::vector decoded_buffer; + if (lodepng::decode(decoded_buffer, *width, *height, file_name, + LodePNGColorType::LCT_RGBA, 8) != 0) { + return amber::Result("lodepng::decode() returned non-zero"); + } + + for (auto d : decoded_buffer) { + amber::Value v; + v.SetIntValue(d); + values->push_back(v); + } + + return {}; +} + } // namespace png diff --git a/samples/png.h b/samples/png.h index 74802e7..b08d74d 100644 --- a/samples/png.h +++ b/samples/png.h @@ -31,6 +31,14 @@ amber::Result ConvertToPNG(uint32_t width, const std::vector& values, std::vector* buffer); +/// Loads a PNG image from |file_name|. Image dimensions of the loaded file are +/// stored into |width| and |height|, and the image data is stored in a one +/// byte per data element format into |values|. +amber::Result LoadPNG(const std::string file_name, + uint32_t* width, + uint32_t* height, + std::vector* values); + } // namespace png #endif // SAMPLES_PNG_H_ diff --git a/src/amberscript/parser.cc b/src/amberscript/parser.cc index 8157476..60292b0 100644 --- a/src/amberscript/parser.cc +++ b/src/amberscript/parser.cc @@ -1224,14 +1224,27 @@ Result Parser::ParseBuffer() { script_->RegisterFormat(std::move(fmt)); token = tokenizer_->PeekNextToken(); - if (token->IsIdentifier() && token->AsString() == "MIP_LEVELS") { - tokenizer_->NextToken(); - token = tokenizer_->NextToken(); + while (token->IsIdentifier()) { + if (token->AsString() == "MIP_LEVELS") { + tokenizer_->NextToken(); + token = tokenizer_->NextToken(); - if (!token->IsInteger()) - return Result("invalid value for MIP_LEVELS"); + if (!token->IsInteger()) + return Result("invalid value for MIP_LEVELS"); - buffer->SetMipLevels(token->AsUint32()); + buffer->SetMipLevels(token->AsUint32()); + } else if (token->AsString() == "FILE") { + tokenizer_->NextToken(); + token = tokenizer_->NextToken(); + + if (!token->IsIdentifier()) + return Result("invalid value for FILE"); + + buffer->SetDataFile(token->AsString()); + } else { + break; + } + token = tokenizer_->PeekNextToken(); } } else { return Result("unknown BUFFER command provided: " + cmd); diff --git a/src/amberscript/parser_buffer_test.cc b/src/amberscript/parser_buffer_test.cc index dcbd982..d2f1eb1 100644 --- a/src/amberscript/parser_buffer_test.cc +++ b/src/amberscript/parser_buffer_test.cc @@ -1067,5 +1067,30 @@ TEST_F(AmberScriptParserTest, BufferMissingMipLevels) { EXPECT_EQ("1: invalid value for MIP_LEVELS", r.Error()); } +TEST_F(AmberScriptParserTest, BufferMissingDataFile) { + std::string in = "BUFFER my_buffer FORMAT R8G8B8A8_UNORM FILE"; + + Parser parser; + Result r = parser.Parse(in); + ASSERT_FALSE(r.IsSuccess()); + + EXPECT_EQ("1: invalid value for FILE", r.Error()); +} + +TEST_F(AmberScriptParserTest, BufferDataFile) { + std::string in = "BUFFER my_buffer FORMAT R8G8B8A8_UNORM FILE foo.png"; + + Parser parser; + Result r = parser.Parse(in); + ASSERT_TRUE(r.IsSuccess()) << r.Error(); + + auto script = parser.GetScript(); + const auto& buffers = script->GetBuffers(); + ASSERT_EQ(1U, buffers.size()); + + ASSERT_TRUE(buffers[0] != nullptr); + EXPECT_EQ("foo.png", buffers[0]->GetDataFile()); +} + } // namespace amberscript } // namespace amber diff --git a/src/buffer.h b/src/buffer.h index d867a5a..9d6f3d6 100644 --- a/src/buffer.h +++ b/src/buffer.h @@ -196,6 +196,12 @@ class Buffer { /// Returns the number of mip levels. uint32_t GetMipLevels() { return mip_levels_; } + /// Sets the file name for loading data into the buffer. + void SetDataFile(std::string data_file) { data_file_ = data_file; } + + /// Returns the file name used to load buffer data from. + std::string GetDataFile() { return data_file_; } + /// Returns a pointer to the internal storage of the buffer. std::vector* ValuePtr() { return &bytes_; } /// Returns a pointer to the internal storage of the buffer. @@ -252,6 +258,7 @@ class Buffer { Format* format_ = nullptr; Sampler* sampler_ = nullptr; ImageDimension image_dim_ = ImageDimension::kUnknown; + std::string data_file_; }; } // namespace amber diff --git a/src/executor.cc b/src/executor.cc index 0d428e9..5ff76d2 100644 --- a/src/executor.cc +++ b/src/executor.cc @@ -88,6 +88,21 @@ Result Executor::Execute(Engine* engine, Engine::Debugger* debugger = nullptr; + // Load data to buffers + for (const auto& buf : script->GetBuffers()) { + if (buf->GetDataFile().empty()) + continue; + + BufferInfo info; + Result r = options->delegate->LoadBufferData(buf->GetDataFile(), &info); + if (!r.IsSuccess()) + return r; + + buf->SetData(info.values); + buf->SetWidth(info.width); + buf->SetHeight(info.height); + } + // Process Commands for (const auto& cmd : script->GetCommands()) { if (options->delegate && options->delegate->LogExecuteCalls()) { diff --git a/tests/cases/draw_png_texture.amber b/tests/cases/draw_png_texture.amber new file mode 100644 index 0000000..c7b9885 --- /dev/null +++ b/tests/cases/draw_png_texture.amber @@ -0,0 +1,66 @@ +#!amber +# Copyright 2020 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_tex GLSL +#version 430 +layout(location = 0) in vec4 position; +layout(location = 1) in vec2 texcoords_in; +layout(location = 0) out vec2 texcoords_out; +void main() { + gl_Position = position; + texcoords_out = texcoords_in; +} +END + +SHADER fragment frag_shader_tex GLSL +#version 430 +layout(location = 0) in vec2 texcoords_in; +layout(location = 0) out vec4 color_out; +uniform layout(set=0, binding=0) sampler2D tex_sampler; +void main() { + color_out = texture(tex_sampler, texcoords_in); +} +END + +BUFFER texture FORMAT R8G8B8A8_UNORM FILE texture.png +BUFFER framebuffer FORMAT B8G8R8A8_UNORM +SAMPLER sampler +BUFFER position DATA_TYPE vec2 DATA +-0.75 -0.75 + 0.75 -0.75 + 0.75 0.75 +-0.75 0.75 +END +BUFFER texcoords DATA_TYPE vec2 DATA +0.0 0.0 +1.0 0.0 +1.0 1.0 +0.0 1.0 +END + +PIPELINE graphics pipeline + ATTACH vert_shader_tex + ATTACH frag_shader_tex + BIND BUFFER texture AS combined_image_sampler SAMPLER sampler DESCRIPTOR_SET 0 BINDING 0 + VERTEX_DATA position LOCATION 0 + VERTEX_DATA texcoords LOCATION 1 + FRAMEBUFFER_SIZE 256 256 + BIND BUFFER framebuffer AS color LOCATION 0 +END + +# Draw the texture using a default sampler. +CLEAR_COLOR pipeline 0 255 0 255 +CLEAR pipeline +RUN pipeline DRAW_ARRAY AS TRIANGLE_FAN START_IDX 0 COUNT 4 diff --git a/tests/cases/texture.png b/tests/cases/texture.png new file mode 100644 index 0000000..465bf9a Binary files /dev/null and b/tests/cases/texture.png differ diff --git a/tests/run_tests.py b/tests/run_tests.py index b3903cd..6b17f56 100755 --- a/tests/run_tests.py +++ b/tests/run_tests.py @@ -323,7 +323,7 @@ class TestRunner: print("--test-prog-path must point to an executable") return 1 - input_file_re = re.compile('^.+[.][amber|vkscript]') + input_file_re = re.compile('^.+[\.]amber|vkscript') self.test_cases = [] if self.args: -- cgit v1.2.3