diff options
author | Paul Thomson <paulthomson@google.com> | 2021-10-12 13:38:21 +0100 |
---|---|---|
committer | Paul Thomson <paulthomson@google.com> | 2021-10-12 13:38:42 +0100 |
commit | f3b1d8fd9a3dfb99df5b8ba7900b3493ee0329a8 (patch) | |
tree | 17024a345cc88eba5ba0a838d8fd11af3aef8350 | |
parent | 69207cd4d09b81e2b6d350d8a7a929904e4aa66f (diff) | |
parent | 8797ee109e7a6ea4d1f58f387f757545fa35325b (diff) | |
download | amber-f3b1d8fd9a3dfb99df5b8ba7900b3493ee0329a8.tar.gz |
Merge commit '8797ee109e7a6ea4d1f58f387f757545fa35325b' into master
Bug: b/201652781
Change-Id: Ibb8fbe9137d18fa6f6109ad5f51ec8e7c5f9cfbb
35 files changed, 1237 insertions, 117 deletions
diff --git a/docs/amber_script.md b/docs/amber_script.md index 57e0632..295cdc7 100644 --- a/docs/amber_script.md +++ b/docs/amber_script.md @@ -485,6 +485,12 @@ The following commands are all specified within the `PIPELINE` command. ``` ```groovy + # Set the viewport size. If no viewport is provided then it defaults to the + # whole framebuffer size. Depth range defaults to 0 to 1. + VIEWPORT {x} {y} SIZE {width} {height} [MIN_DEPTH {mind}] [MAX_DEPTH {maxd}] +``` + +```groovy # Set subgroup size control setting. Require that subgroups must be launched # with all invocations active for given shader. Allow SubgroupSize to vary # for given shader. Require a specific SubgroupSize the for given shader. @@ -494,7 +500,7 @@ The following commands are all specified within the `PIPELINE` command. # and be less than or equal to maxSubgroupSize # - MIN to set the required subgroup size to the minSubgroupSize # - MAX to set the required subgroup size to the maxSubgroupSize - SUBROUP {name_of_shader} + SUBGROUP {name_of_shader} FULLY_POPULATED {fully_populated_enable} VARYING_SIZE {varying_size_enable} REQUIRED_SIZE {subgroup_size} @@ -559,13 +565,18 @@ All BIND BUFFER and BIND SAMPLER commands below define a descriptor set and bind These commands can be replaced with BIND BUFFER_ARRAY and BIND SAMPLER_ARRAY commands. In these cases multiple buffer or sampler names need to be provided, separated by spaces. This creates a descriptor array of buffers or samplers bound to the same descriptor set -and binding ID. An array of offsets should be provided via `OFFSET offset1 offset2 ...` -when using dynamic buffers with BUFFER_ARRAY. +and binding ID. An array of dynamic offsets should be provided via `OFFSET offset1 offset2 ...` +when using dynamic buffers with BUFFER_ARRAY. Optional descriptor binding offset(s) and range(s) +can be defined via `DESCRIPTOR_OFFSET offset1 offset2 ...` and +`DESCRIPTOR_RANGE range1 range2 ...` when using uniform or storage buffers. Offsets and +ranges can be used also with dynamic buffers. ```groovy # Bind the buffer of the given |buffer_type| at the given descriptor set - # and binding. The buffer will use a start index of 0. + # and binding. The buffer will use a byte offset |descriptor_offset| + # with range |range|. BIND {BUFFER | BUFFER_ARRAY} {buffer_name} AS {buffer_type} DESCRIPTOR_SET _id_ \ - BINDING _id_ + BINDING _id_ [ DESCRIPTOR_OFFSET _descriptor_offset_ (default 0) ] \ + [ DESCRIPTOR_RANGE _range_ (default -1 == VK_WHOLE_SIZE) ] # Attach |buffer_name| as a storage image. The MIP level will have a base # value of |level|. @@ -586,9 +597,12 @@ when using dynamic buffers with BUFFER_ARRAY. BIND {SAMPLER | SAMPLER_ARRAY} {sampler_name} DESCRIPTOR_SET _id_ BINDING _id_ # Bind |buffer_name| as dynamic uniform/storage buffer at the given descriptor set - # and binding. The buffer will use a start index of |offset|. + # and binding. The buffer will use a byte offset |offset| + |descriptor_offset| + # with range |range|. BIND {BUFFER | BUFFER_ARRAY} {buffer_name} AS {uniform_dynamic | storage_dynamic} \ - DESCRIPTOR_SET _id_ BINDING _id_ OFFSET _offset_ + DESCRIPTOR_SET _id_ BINDING _id_ OFFSET _offset_ \ + [ DESCRIPTOR_OFFSET _descriptor_offset_ (default 0) ] \ + [ DESCRIPTOR_RANGE _range_ (default -1 == VK_WHOLE_SIZE) ] ``` ```groovy diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 00e3e54..901681f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -159,6 +159,7 @@ if (${AMBER_ENABLE_TESTS}) amberscript/parser_struct_test.cc amberscript/parser_subgroup_size_control_test.cc amberscript/parser_test.cc + amberscript/parser_viewport_test.cc buffer_test.cc command_data_test.cc descriptor_set_and_binding_parser_test.cc diff --git a/src/amberscript/parser.cc b/src/amberscript/parser.cc index 50760f0..809baa1 100644 --- a/src/amberscript/parser.cc +++ b/src/amberscript/parser.cc @@ -602,6 +602,8 @@ Result Parser::ParsePipelineBody(const std::string& cmd_name, r = ParsePipelineShaderOptimizations(pipeline.get()); } else if (tok == "FRAMEBUFFER_SIZE") { r = ParsePipelineFramebufferSize(pipeline.get()); + } else if (tok == "VIEWPORT") { + r = ParsePipelineViewport(pipeline.get()); } else if (tok == "BIND") { r = ParsePipelineBind(pipeline.get()); } else if (tok == "VERTEX_DATA") { @@ -949,6 +951,75 @@ Result Parser::ParsePipelineFramebufferSize(Pipeline* pipeline) { return ValidateEndOfStatement("FRAMEBUFFER_SIZE command"); } +Result Parser::ParsePipelineViewport(Pipeline* pipeline) { + Viewport vp; + vp.mind = 0.0f; + vp.maxd = 1.0f; + + float val[2]; + for (int i = 0; i < 2; i++) { + auto token = tokenizer_->NextToken(); + if (token->IsEOL() || token->IsEOS()) + return Result("missing offset for VIEWPORT command"); + Result r = token->ConvertToDouble(); + if (!r.IsSuccess()) + return Result("invalid offset for VIEWPORT command"); + + val[i] = token->AsFloat(); + } + vp.x = val[0]; + vp.y = val[1]; + + auto token = tokenizer_->NextToken(); + if (!token->IsIdentifier() || token->AsString() != "SIZE") + return Result("missing SIZE for VIEWPORT command"); + + for (int i = 0; i < 2; i++) { + token = tokenizer_->NextToken(); + if (token->IsEOL() || token->IsEOS()) + return Result("missing size for VIEWPORT command"); + Result r = token->ConvertToDouble(); + if (!r.IsSuccess()) + return Result("invalid size for VIEWPORT command"); + + val[i] = token->AsFloat(); + } + vp.w = val[0]; + vp.h = val[1]; + + token = tokenizer_->PeekNextToken(); + while (token->IsIdentifier()) { + if (token->AsString() == "MIN_DEPTH") { + tokenizer_->NextToken(); + token = tokenizer_->NextToken(); + if (token->IsEOL() || token->IsEOS()) + return Result("missing min_depth for VIEWPORT command"); + Result r = token->ConvertToDouble(); + if (!r.IsSuccess()) + return Result("invalid min_depth for VIEWPORT command"); + + vp.mind = token->AsFloat(); + } + if (token->AsString() == "MAX_DEPTH") { + tokenizer_->NextToken(); + token = tokenizer_->NextToken(); + if (token->IsEOL() || token->IsEOS()) + return Result("missing max_depth for VIEWPORT command"); + Result r = token->ConvertToDouble(); + if (!r.IsSuccess()) + return Result("invalid max_depth for VIEWPORT command"); + + vp.maxd = token->AsFloat(); + } + + token = tokenizer_->PeekNextToken(); + } + + pipeline->GetPipelineData()->SetViewport(vp); + + return ValidateEndOfStatement("VIEWPORT command"); +} + Result Parser::ToBufferType(const std::string& name, BufferType* type) { assert(type); if (name == "color") @@ -1172,10 +1243,60 @@ Result Parser::ParsePipelineBind(Pipeline* pipeline) { } } + // Set default descriptor buffer offsets to 0 and descriptor buffer + // ranges to VK_WHOLE_SIZE (~0ULL). + std::vector<uint64_t> descriptor_offsets(buffers.size(), 0); + std::vector<uint64_t> descriptor_ranges(buffers.size(), ~0ULL); + if (buffer_type == BufferType::kUniformDynamic || + buffer_type == BufferType::kStorageDynamic || + buffer_type == BufferType::kStorage || + buffer_type == BufferType::kUniform) { + token = tokenizer_->PeekNextToken(); + if (token->IsIdentifier() && + token->AsString() == "DESCRIPTOR_OFFSET") { + token = tokenizer_->NextToken(); + for (size_t i = 0; i < buffers.size(); i++) { + token = tokenizer_->NextToken(); + if (!token->IsInteger()) { + if (i > 0) { + return Result( + "expecting a DESCRIPTOR_OFFSET value for each buffer in " + "the array"); + } else { + return Result( + "expecting an integer value for DESCRIPTOR_OFFSET"); + } + } + descriptor_offsets[i] = token->AsUint64(); + } + } + + token = tokenizer_->PeekNextToken(); + if (token->IsIdentifier() && + token->AsString() == "DESCRIPTOR_RANGE") { + token = tokenizer_->NextToken(); + for (size_t i = 0; i < buffers.size(); i++) { + token = tokenizer_->NextToken(); + if (!token->IsInteger()) { + if (i > 0) { + return Result( + "expecting a DESCRIPTOR_RANGE value for each buffer in " + "the array"); + } else { + return Result( + "expecting an integer value for DESCRIPTOR_RANGE"); + } + } + descriptor_ranges[i] = token->AsUint64(); + } + } + } + pipeline->ClearBuffers(descriptor_set, binding); for (size_t i = 0; i < buffers.size(); i++) { pipeline->AddBuffer(buffers[i], buffer_type, descriptor_set, binding, - base_mip_level, dynamic_offsets[i]); + base_mip_level, dynamic_offsets[i], + descriptor_offsets[i], descriptor_ranges[i]); } } else if (token->IsIdentifier() && token->AsString() == "KERNEL") { token = tokenizer_->NextToken(); diff --git a/src/amberscript/parser.h b/src/amberscript/parser.h index e8cb835..6fb25ce 100644 --- a/src/amberscript/parser.h +++ b/src/amberscript/parser.h @@ -65,6 +65,7 @@ class Parser : public amber::Parser { Result ParsePipelineShaderCompileOptions(Pipeline*); Result ParsePipelineSubgroup(Pipeline* pipeline); Result ParsePipelineFramebufferSize(Pipeline*); + Result ParsePipelineViewport(Pipeline*); Result ParsePipelineBind(Pipeline*); Result ParsePipelineVertexData(Pipeline*); Result ParsePipelineIndexData(Pipeline*); diff --git a/src/amberscript/parser_bind_test.cc b/src/amberscript/parser_bind_test.cc index 64f3948..a39b69e 100644 --- a/src/amberscript/parser_bind_test.cc +++ b/src/amberscript/parser_bind_test.cc @@ -1393,11 +1393,53 @@ END EXPECT_EQ(BufferType::kUniform, bufs[0].type); EXPECT_EQ(1U, bufs[0].descriptor_set); EXPECT_EQ(2U, bufs[0].binding); + EXPECT_EQ(0U, bufs[0].descriptor_offset); + EXPECT_EQ(~0ULL, bufs[0].descriptor_range); EXPECT_EQ(static_cast<uint32_t>(0), bufs[0].location); EXPECT_EQ(FormatType::kR32G32B32A32_SFLOAT, bufs[0].buffer->GetFormat()->GetFormatType()); } +TEST_F(AmberScriptParserTest, BindBufferDescriptorOffsetAndRange) { + std::string in = R"( +SHADER vertex my_shader PASSTHROUGH +SHADER fragment my_fragment GLSL +# GLSL Shader +END +BUFFER my_buf FORMAT R32G32B32A32_SFLOAT + +PIPELINE graphics my_pipeline + ATTACH my_shader + ATTACH my_fragment + + BIND BUFFER my_buf AS uniform DESCRIPTOR_SET 1 BINDING 2 DESCRIPTOR_OFFSET 256 DESCRIPTOR_RANGE 512 + BIND BUFFER my_buf AS uniform DESCRIPTOR_SET 1 BINDING 3 DESCRIPTOR_OFFSET 256 DESCRIPTOR_RANGE -1 +END +)"; + + Parser parser; + Result r = parser.Parse(in); + ASSERT_TRUE(r.IsSuccess()) << r.Error(); + + auto script = parser.GetScript(); + const auto& pipelines = script->GetPipelines(); + ASSERT_EQ(1U, pipelines.size()); + + const auto* pipeline = pipelines[0].get(); + const auto& bufs = pipeline->GetBuffers(); + ASSERT_EQ(2U, bufs.size()); + EXPECT_EQ(BufferType::kUniform, bufs[0].type); + EXPECT_EQ(1U, bufs[0].descriptor_set); + EXPECT_EQ(2U, bufs[0].binding); + EXPECT_EQ(256U, bufs[0].descriptor_offset); + EXPECT_EQ(512U, bufs[0].descriptor_range); + EXPECT_EQ(static_cast<uint32_t>(0), bufs[0].location); + EXPECT_EQ(FormatType::kR32G32B32A32_SFLOAT, + bufs[0].buffer->GetFormat()->GetFormatType()); + // Verify the descriptor range from the second buffer. + EXPECT_EQ(~0ULL, bufs[1].descriptor_range); +} + TEST_F(AmberScriptParserTest, BindBufferMissingBindingValue) { std::string in = R"( SHADER vertex my_shader PASSTHROUGH @@ -2859,6 +2901,33 @@ END)"; EXPECT_EQ("13: missing BINDING for BIND command", r.Error()); } +TEST_F(AmberScriptParserTest, BindDescriptorOffsetWithUnsupportedBufferType) { + std::string unsupported_buffer_types[] = {"uniform_texel_buffer", + "storage_texel_buffer", + "sampled_image", "storage_image"}; + + for (const auto& buffer_type : unsupported_buffer_types) { + std::ostringstream in; + in << R"( +SHADER compute compute_shader GLSL +# GLSL Shader +END + +BUFFER texture FORMAT R8G8B8A8_UNORM + +PIPELINE compute pipeline + ATTACH compute_shader + BIND BUFFER texture AS )" + << buffer_type << R"( DESCRIPTOR_SET 0 BINDING 0 DESCRIPTOR_OFFSET 0 +END)"; + Parser parser; + Result r = parser.Parse(in.str()); + ASSERT_FALSE(r.IsSuccess()); + EXPECT_EQ("10: extra parameters after BIND command: DESCRIPTOR_OFFSET", + r.Error()); + } +} + TEST_F(AmberScriptParserTest, BindBufferArray) { std::string in = R"( SHADER vertex my_shader PASSTHROUGH @@ -2891,12 +2960,200 @@ END EXPECT_EQ(BufferType::kUniform, bufs[i].type); EXPECT_EQ(1U, bufs[i].descriptor_set); EXPECT_EQ(2U, bufs[i].binding); + EXPECT_EQ(0U, bufs[i].descriptor_offset); + EXPECT_EQ(~0ULL, bufs[i].descriptor_range); + EXPECT_EQ(static_cast<uint32_t>(0), bufs[i].location); + EXPECT_EQ(FormatType::kR32G32B32A32_SFLOAT, + bufs[i].buffer->GetFormat()->GetFormatType()); + } +} + +TEST_F(AmberScriptParserTest, BindBufferArrayWithDescriptorOffsetAndRange) { + std::string in = R"( +SHADER vertex my_shader PASSTHROUGH +SHADER fragment my_fragment GLSL +# GLSL Shader +END +BUFFER my_buf1 FORMAT R32G32B32A32_SFLOAT + +PIPELINE graphics my_pipeline + ATTACH my_shader + ATTACH my_fragment + + BIND BUFFER_ARRAY my_buf1 my_buf1 AS uniform DESCRIPTOR_SET 1 BINDING 2 DESCRIPTOR_OFFSET 256 512 DESCRIPTOR_RANGE 1024 2048 +END +)"; + + Parser parser; + Result r = parser.Parse(in); + ASSERT_TRUE(r.IsSuccess()) << r.Error(); + + auto script = parser.GetScript(); + const auto& pipelines = script->GetPipelines(); + ASSERT_EQ(1U, pipelines.size()); + + const auto* pipeline = pipelines[0].get(); + const auto& bufs = pipeline->GetBuffers(); + ASSERT_EQ(2U, bufs.size()); + for (size_t i = 0; i < 2; i++) { + EXPECT_EQ(BufferType::kUniform, bufs[i].type); + EXPECT_EQ(1U, bufs[i].descriptor_set); + EXPECT_EQ(2U, bufs[i].binding); + EXPECT_EQ(256U * (i + 1), bufs[i].descriptor_offset); + EXPECT_EQ(1024U * (i + 1), bufs[i].descriptor_range); EXPECT_EQ(static_cast<uint32_t>(0), bufs[i].location); EXPECT_EQ(FormatType::kR32G32B32A32_SFLOAT, bufs[i].buffer->GetFormat()->GetFormatType()); } } +TEST_F(AmberScriptParserTest, + BindDynamicBufferArrayWithDescriptorOffsetAndRange) { + std::string in = R"( +SHADER vertex my_shader PASSTHROUGH +SHADER fragment my_fragment GLSL +# GLSL Shader +END +BUFFER my_buf1 FORMAT R32G32B32A32_SFLOAT + +PIPELINE graphics my_pipeline + ATTACH my_shader + ATTACH my_fragment + + BIND BUFFER_ARRAY my_buf1 my_buf1 AS uniform_dynamic DESCRIPTOR_SET 1 BINDING 2 OFFSET 16 32 DESCRIPTOR_OFFSET 256 512 DESCRIPTOR_RANGE 1024 2048 +END +)"; + + Parser parser; + Result r = parser.Parse(in); + ASSERT_TRUE(r.IsSuccess()) << r.Error(); + + auto script = parser.GetScript(); + const auto& pipelines = script->GetPipelines(); + ASSERT_EQ(1U, pipelines.size()); + + const auto* pipeline = pipelines[0].get(); + const auto& bufs = pipeline->GetBuffers(); + ASSERT_EQ(2U, bufs.size()); + for (size_t i = 0; i < 2; i++) { + EXPECT_EQ(BufferType::kUniformDynamic, bufs[i].type); + EXPECT_EQ(1U, bufs[i].descriptor_set); + EXPECT_EQ(2U, bufs[i].binding); + EXPECT_EQ(16U * (i + 1), bufs[i].dynamic_offset); + EXPECT_EQ(256U * (i + 1), bufs[i].descriptor_offset); + EXPECT_EQ(1024U * (i + 1), bufs[i].descriptor_range); + EXPECT_EQ(static_cast<uint32_t>(0), bufs[i].location); + EXPECT_EQ(FormatType::kR32G32B32A32_SFLOAT, + bufs[i].buffer->GetFormat()->GetFormatType()); + } +} + +TEST_F(AmberScriptParserTest, BindBufferArrayOnlyOneDescriptorOffset) { + std::string in = R"( +SHADER vertex my_shader PASSTHROUGH +SHADER fragment my_fragment GLSL +# GLSL Shader +END +BUFFER my_buf FORMAT R32G32B32A32_SFLOAT + +PIPELINE graphics my_pipeline + ATTACH my_shader + ATTACH my_fragment + + BIND BUFFER_ARRAY my_buf my_buf AS uniform DESCRIPTOR_SET 1 BINDING 2 DESCRIPTOR_OFFSET 256 +END +)"; + + Parser parser; + Result r = parser.Parse(in); + + ASSERT_FALSE(r.IsSuccess()); + EXPECT_EQ( + "13: expecting a DESCRIPTOR_OFFSET value for each buffer in the array", + r.Error()); +} + +TEST_F(AmberScriptParserTest, BindBufferArrayOnlyOneDescriptorRange) { + std::string in = R"( +SHADER vertex my_shader PASSTHROUGH +SHADER fragment my_fragment GLSL +# GLSL Shader +END +BUFFER my_buf FORMAT R32G32B32A32_SFLOAT + +PIPELINE graphics my_pipeline + ATTACH my_shader + ATTACH my_fragment + + BIND BUFFER_ARRAY my_buf my_buf AS uniform DESCRIPTOR_SET 1 BINDING 2 DESCRIPTOR_RANGE 256 +END +)"; + + Parser parser; + Result r = parser.Parse(in); + + ASSERT_FALSE(r.IsSuccess()); + EXPECT_EQ( + "13: expecting a DESCRIPTOR_RANGE value for each buffer in the array", + r.Error()); +} + +TEST_F(AmberScriptParserTest, BindUniformBufferEmptyDescriptorOffset) { + std::string in = R"( +BUFFER my_buf FORMAT R32G32B32A32_SFLOAT +PIPELINE graphics my_pipeline + BIND BUFFER my_buf AS uniform DESCRIPTOR_SET 1 BINDING 2 DESCRIPTOR_OFFSET +END +)"; + + Parser parser; + Result r = parser.Parse(in); + ASSERT_FALSE(r.IsSuccess()); + EXPECT_EQ("5: expecting an integer value for DESCRIPTOR_OFFSET", r.Error()); +} + +TEST_F(AmberScriptParserTest, BindUniformBufferInvalidDescriptorOffset) { + std::string in = R"( +BUFFER my_buf FORMAT R32G32B32A32_SFLOAT +PIPELINE graphics my_pipeline + BIND BUFFER my_buf AS uniform DESCRIPTOR_SET 1 BINDING 2 DESCRIPTOR_OFFSET foo +END +)"; + + Parser parser; + Result r = parser.Parse(in); + ASSERT_FALSE(r.IsSuccess()); + EXPECT_EQ("4: expecting an integer value for DESCRIPTOR_OFFSET", r.Error()); +} + +TEST_F(AmberScriptParserTest, BindUniformBufferEmptyDescriptorRange) { + std::string in = R"( +BUFFER my_buf FORMAT R32G32B32A32_SFLOAT +PIPELINE graphics my_pipeline + BIND BUFFER my_buf AS uniform DESCRIPTOR_SET 1 BINDING 2 DESCRIPTOR_RANGE +END +)"; + + Parser parser; + Result r = parser.Parse(in); + ASSERT_FALSE(r.IsSuccess()); + EXPECT_EQ("5: expecting an integer value for DESCRIPTOR_RANGE", r.Error()); +} + +TEST_F(AmberScriptParserTest, BindUniformBufferInvalidDescriptorRange) { + std::string in = R"( +BUFFER my_buf FORMAT R32G32B32A32_SFLOAT +PIPELINE graphics my_pipeline + BIND BUFFER my_buf AS uniform DESCRIPTOR_SET 1 BINDING 2 DESCRIPTOR_RANGE foo +END +)"; + + Parser parser; + Result r = parser.Parse(in); + ASSERT_FALSE(r.IsSuccess()); + EXPECT_EQ("4: expecting an integer value for DESCRIPTOR_RANGE", r.Error()); +} + TEST_F(AmberScriptParserTest, BindBufferArrayOnlyOneBuffer) { std::string in = R"( SHADER vertex my_shader PASSTHROUGH diff --git a/src/amberscript/parser_viewport_test.cc b/src/amberscript/parser_viewport_test.cc new file mode 100644 index 0000000..ec19fb2 --- /dev/null +++ b/src/amberscript/parser_viewport_test.cc @@ -0,0 +1,388 @@ +// Copyright 2021 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 +// +// http://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 parseried. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "gtest/gtest.h" +#include "src/amberscript/parser.h" + +namespace amber { +namespace amberscript { + +using AmberScriptParserTest = testing::Test; + +TEST_F(AmberScriptParserTest, NoViewport) { + std::string in = R"( +SHADER vertex my_shader PASSTHROUGH +SHADER fragment my_fragment GLSL +# GLSL Shader +END +BUFFER my_fb FORMAT R32G32B32A32_SFLOAT +BUFFER my_ds FORMAT D32_SFLOAT_S8_UINT + +PIPELINE graphics my_pipeline + ATTACH my_shader + ATTACH my_fragment + BIND BUFFER my_fb AS color LOCATION 0 + BIND BUFFER my_ds AS depth_stencil +END)"; + + Parser parser; + Result r = parser.Parse(in); + ASSERT_TRUE(r.IsSuccess()) << r.Error(); + + auto script = parser.GetScript(); + const auto& pipelines = script->GetPipelines(); + ASSERT_EQ(1U, pipelines.size()); + + auto* pipeline = pipelines[0].get(); + ASSERT_FALSE(pipeline->GetPipelineData()->HasViewportData()); +} + +TEST_F(AmberScriptParserTest, ViewportNoDepth) { + std::string in = R"( +SHADER vertex my_shader PASSTHROUGH +SHADER fragment my_fragment GLSL +# GLSL Shader +END +BUFFER my_fb FORMAT R32G32B32A32_SFLOAT +BUFFER my_ds FORMAT D32_SFLOAT_S8_UINT + +PIPELINE graphics my_pipeline + ATTACH my_shader + ATTACH my_fragment + BIND BUFFER my_fb AS color LOCATION 0 + BIND BUFFER my_ds AS depth_stencil + + VIEWPORT 5.0 7.0 SIZE 10.0 12.0 +END)"; + + Parser parser; + Result r = parser.Parse(in); + ASSERT_TRUE(r.IsSuccess()) << r.Error(); + + auto script = parser.GetScript(); + const auto& pipelines = script->GetPipelines(); + ASSERT_EQ(1U, pipelines.size()); + + auto* pipeline = pipelines[0].get(); + ASSERT_TRUE(pipeline->GetPipelineData()->HasViewportData()); + ASSERT_FLOAT_EQ(5.0f, pipeline->GetPipelineData()->GetViewport().x); + ASSERT_FLOAT_EQ(7.0f, pipeline->GetPipelineData()->GetViewport().y); + ASSERT_FLOAT_EQ(10.0f, pipeline->GetPipelineData()->GetViewport().w); + ASSERT_FLOAT_EQ(12.0f, pipeline->GetPipelineData()->GetViewport().h); + ASSERT_FLOAT_EQ(0.0f, pipeline->GetPipelineData()->GetViewport().mind); + ASSERT_FLOAT_EQ(1.0f, pipeline->GetPipelineData()->GetViewport().maxd); +} + +TEST_F(AmberScriptParserTest, ViewportMinDepth) { + std::string in = R"( +SHADER vertex my_shader PASSTHROUGH +SHADER fragment my_fragment GLSL +# GLSL Shader +END +BUFFER my_fb FORMAT R32G32B32A32_SFLOAT +BUFFER my_ds FORMAT D32_SFLOAT_S8_UINT + +PIPELINE graphics my_pipeline + ATTACH my_shader + ATTACH my_fragment + BIND BUFFER my_fb AS color LOCATION 0 + BIND BUFFER my_ds AS depth_stencil + + VIEWPORT 12.2 9.7 SIZE 0.5 106.1 MIN_DEPTH 0.3 +END)"; + + Parser parser; + Result r = parser.Parse(in); + ASSERT_TRUE(r.IsSuccess()) << r.Error(); + + auto script = parser.GetScript(); + const auto& pipelines = script->GetPipelines(); + ASSERT_EQ(1U, pipelines.size()); + + auto* pipeline = pipelines[0].get(); + ASSERT_TRUE(pipeline->GetPipelineData()->HasViewportData()); + ASSERT_FLOAT_EQ(12.2f, pipeline->GetPipelineData()->GetViewport().x); + ASSERT_FLOAT_EQ(9.7f, pipeline->GetPipelineData()->GetViewport().y); + ASSERT_FLOAT_EQ(0.5f, pipeline->GetPipelineData()->GetViewport().w); + ASSERT_FLOAT_EQ(106.1f, pipeline->GetPipelineData()->GetViewport().h); + ASSERT_FLOAT_EQ(0.3f, pipeline->GetPipelineData()->GetViewport().mind); + ASSERT_FLOAT_EQ(1.0f, pipeline->GetPipelineData()->GetViewport().maxd); +} + +TEST_F(AmberScriptParserTest, ViewportMaxDepth) { + std::string in = R"( +SHADER vertex my_shader PASSTHROUGH +SHADER fragment my_fragment GLSL +# GLSL Shader +END +BUFFER my_fb FORMAT R32G32B32A32_SFLOAT +BUFFER my_ds FORMAT D32_SFLOAT_S8_UINT + +PIPELINE graphics my_pipeline + ATTACH my_shader + ATTACH my_fragment + BIND BUFFER my_fb AS color LOCATION 0 + BIND BUFFER my_ds AS depth_stencil + + VIEWPORT 12.2 9.7 SIZE 0.5 106.1 MAX_DEPTH 0.456 +END)"; + + Parser parser; + Result r = parser.Parse(in); + ASSERT_TRUE(r.IsSuccess()) << r.Error(); + + auto script = parser.GetScript(); + const auto& pipelines = script->GetPipelines(); + ASSERT_EQ(1U, pipelines.size()); + + auto* pipeline = pipelines[0].get(); + ASSERT_TRUE(pipeline->GetPipelineData()->HasViewportData()); + ASSERT_FLOAT_EQ(12.2f, pipeline->GetPipelineData()->GetViewport().x); + ASSERT_FLOAT_EQ(9.7f, pipeline->GetPipelineData()->GetViewport().y); + ASSERT_FLOAT_EQ(0.5f, pipeline->GetPipelineData()->GetViewport().w); + ASSERT_FLOAT_EQ(106.1f, pipeline->GetPipelineData()->GetViewport().h); + ASSERT_FLOAT_EQ(0.0f, pipeline->GetPipelineData()->GetViewport().mind); + ASSERT_FLOAT_EQ(0.456f, pipeline->GetPipelineData()->GetViewport().maxd); +} + +TEST_F(AmberScriptParserTest, ViewportAllValues) { + std::string in = R"( +SHADER vertex my_shader PASSTHROUGH +SHADER fragment my_fragment GLSL +# GLSL Shader +END +BUFFER my_fb FORMAT R32G32B32A32_SFLOAT +BUFFER my_ds FORMAT D32_SFLOAT_S8_UINT + +PIPELINE graphics my_pipeline + ATTACH my_shader + ATTACH my_fragment + BIND BUFFER my_fb AS color LOCATION 0 + BIND BUFFER my_ds AS depth_stencil + + VIEWPORT -0.6 5.2 SIZE 13.8 9.4 MIN_DEPTH 0.5 MAX_DEPTH 0.6 +END)"; + + Parser parser; + Result r = parser.Parse(in); + ASSERT_TRUE(r.IsSuccess()) << r.Error(); + + auto script = parser.GetScript(); + const auto& pipelines = script->GetPipelines(); + ASSERT_EQ(1U, pipelines.size()); + + auto* pipeline = pipelines[0].get(); + ASSERT_TRUE(pipeline->GetPipelineData()->HasViewportData()); + ASSERT_FLOAT_EQ(-0.6f, pipeline->GetPipelineData()->GetViewport().x); + ASSERT_FLOAT_EQ(5.2f, pipeline->GetPipelineData()->GetViewport().y); + ASSERT_FLOAT_EQ(13.8f, pipeline->GetPipelineData()->GetViewport().w); + ASSERT_FLOAT_EQ(9.4f, pipeline->GetPipelineData()->GetViewport().h); + ASSERT_FLOAT_EQ(0.5f, pipeline->GetPipelineData()->GetViewport().mind); + ASSERT_FLOAT_EQ(0.6f, pipeline->GetPipelineData()->GetViewport().maxd); +} + +TEST_F(AmberScriptParserTest, ViewportIntegers) { + std::string in = R"( +SHADER vertex my_shader PASSTHROUGH +SHADER fragment my_fragment GLSL +# GLSL Shader +END +BUFFER my_fb FORMAT R32G32B32A32_SFLOAT +BUFFER my_ds FORMAT D32_SFLOAT_S8_UINT + +PIPELINE graphics my_pipeline + ATTACH my_shader + ATTACH my_fragment + BIND BUFFER my_fb AS color LOCATION 0 + BIND BUFFER my_ds AS depth_stencil + + VIEWPORT -2 7 SIZE 15 20 MIN_DEPTH 1 MAX_DEPTH 2 +END)"; + + Parser parser; + Result r = parser.Parse(in); + ASSERT_TRUE(r.IsSuccess()) << r.Error(); + + auto script = parser.GetScript(); + const auto& pipelines = script->GetPipelines(); + ASSERT_EQ(1U, pipelines.size()); + + auto* pipeline = pipelines[0].get(); + ASSERT_TRUE(pipeline->GetPipelineData()->HasViewportData()); + ASSERT_FLOAT_EQ(-2.0f, pipeline->GetPipelineData()->GetViewport().x); + ASSERT_FLOAT_EQ(7.0f, pipeline->GetPipelineData()->GetViewport().y); + ASSERT_FLOAT_EQ(15.0f, pipeline->GetPipelineData()->GetViewport().w); + ASSERT_FLOAT_EQ(20.0f, pipeline->GetPipelineData()->GetViewport().h); + ASSERT_FLOAT_EQ(1.0f, pipeline->GetPipelineData()->GetViewport().mind); + ASSERT_FLOAT_EQ(2.0f, pipeline->GetPipelineData()->GetViewport().maxd); +} + +TEST_F(AmberScriptParserTest, ViewportMixedIntegers) { + std::string in = R"( +SHADER vertex my_shader PASSTHROUGH +SHADER fragment my_fragment GLSL +# GLSL Shader +END +BUFFER my_fb FORMAT R32G32B32A32_SFLOAT +BUFFER my_ds FORMAT D32_SFLOAT_S8_UINT + +PIPELINE graphics my_pipeline + ATTACH my_shader + ATTACH my_fragment + BIND BUFFER my_fb AS color LOCATION 0 + BIND BUFFER my_ds AS depth_stencil + + VIEWPORT -2 13.1 SIZE 15.9 20 +END)"; + + Parser parser; + Result r = parser.Parse(in); + ASSERT_TRUE(r.IsSuccess()) << r.Error(); + + auto script = parser.GetScript(); + const auto& pipelines = script->GetPipelines(); + ASSERT_EQ(1U, pipelines.size()); + + auto* pipeline = pipelines[0].get(); + ASSERT_TRUE(pipeline->GetPipelineData()->HasViewportData()); + ASSERT_FLOAT_EQ(-2.0f, pipeline->GetPipelineData()->GetViewport().x); + ASSERT_FLOAT_EQ(13.1f, pipeline->GetPipelineData()->GetViewport().y); + ASSERT_FLOAT_EQ(15.9f, pipeline->GetPipelineData()->GetViewport().w); + ASSERT_FLOAT_EQ(20.0f, pipeline->GetPipelineData()->GetViewport().h); + ASSERT_FLOAT_EQ(0.0f, pipeline->GetPipelineData()->GetViewport().mind); + ASSERT_FLOAT_EQ(1.0f, pipeline->GetPipelineData()->GetViewport().maxd); +} + +TEST_F(AmberScriptParserTest, ViewportInvalidMissingSize) { + std::string in = R"( +SHADER vertex my_shader PASSTHROUGH +SHADER fragment my_fragment GLSL +# GLSL Shader +END +BUFFER my_fb FORMAT R32G32B32A32_SFLOAT +BUFFER my_ds FORMAT D32_SFLOAT_S8_UINT + +PIPELINE graphics my_pipeline + ATTACH my_shader + ATTACH my_fragment + BIND BUFFER my_fb AS color LOCATION 0 + BIND BUFFER my_ds AS depth_stencil + + VIEWPORT 0.0 2.0 12.0 24.0 +END)"; + + Parser parser; + Result r = parser.Parse(in); + ASSERT_FALSE(r.IsSuccess()) << r.Error(); + EXPECT_EQ("15: missing SIZE for VIEWPORT command", r.Error()); +} + +TEST_F(AmberScriptParserTest, ViewportInvalidSizeNotOptional) { + std::string in = R"( +SHADER vertex my_shader PASSTHROUGH +SHADER fragment my_fragment GLSL +# GLSL Shader +END +BUFFER my_fb FORMAT R32G32B32A32_SFLOAT +BUFFER my_ds FORMAT D32_SFLOAT_S8_UINT + +PIPELINE graphics my_pipeline + ATTACH my_shader + ATTACH my_fragment + BIND BUFFER my_fb AS color LOCATION 0 + BIND BUFFER my_ds AS depth_stencil + + VIEWPORT 0.0 2.0 +END)"; + + Parser parser; + Result r = parser.Parse(in); + ASSERT_FALSE(r.IsSuccess()) << r.Error(); + EXPECT_EQ("16: missing SIZE for VIEWPORT command", r.Error()); +} + +TEST_F(AmberScriptParserTest, ViewportInvalidMissingOffset) { + std::string in = R"( +SHADER vertex my_shader PASSTHROUGH +SHADER fragment my_fragment GLSL +# GLSL Shader +END +BUFFER my_fb FORMAT R32G32B32A32_SFLOAT +BUFFER my_ds FORMAT D32_SFLOAT_S8_UINT + +PIPELINE graphics my_pipeline + ATTACH my_shader + ATTACH my_fragment + BIND BUFFER my_fb AS color LOCATION 0 + BIND BUFFER my_ds AS depth_stencil + + VIEWPORT 0.0 SIZE 12.0 24.0 +END)"; + + Parser parser; + Result r = parser.Parse(in); + ASSERT_FALSE(r.IsSuccess()) << r.Error(); + EXPECT_EQ("15: invalid offset for VIEWPORT command", r.Error()); +} + +TEST_F(AmberScriptParserTest, ViewportInvalidMissingSizeValue) { + std::string in = R"( +SHADER vertex my_shader PASSTHROUGH +SHADER fragment my_fragment GLSL +# GLSL Shader +END +BUFFER my_fb FORMAT R32G32B32A32_SFLOAT +BUFFER my_ds FORMAT D32_SFLOAT_S8_UINT + +PIPELINE graphics my_pipeline + ATTACH my_shader + ATTACH my_fragment + BIND BUFFER my_fb AS color LOCATION 0 + BIND BUFFER my_ds AS depth_stencil + + VIEWPORT 0.0 2.0 SIZE 12.0 +END)"; + + Parser parser; + Result r = parser.Parse(in); + ASSERT_FALSE(r.IsSuccess()) << r.Error(); + EXPECT_EQ("16: missing size for VIEWPORT command", r.Error()); +} + +TEST_F(AmberScriptParserTest, ViewportInvalidMissingDepthValue) { + std::string in = R"( +SHADER vertex my_shader PASSTHROUGH +SHADER fragment my_fragment GLSL +# GLSL Shader +END +BUFFER my_fb FORMAT R32G32B32A32_SFLOAT +BUFFER my_ds FORMAT D32_SFLOAT_S8_UINT + +PIPELINE graphics my_pipeline + ATTACH my_shader + ATTACH my_fragment + BIND BUFFER my_fb AS color LOCATION 0 + BIND BUFFER my_ds AS depth_stencil + + VIEWPORT 0.0 2.0 SIZE 12.0 24.0 MIN_DEPTH MAX_DEPTH 1.0 +END)"; + + Parser parser; + Result r = parser.Parse(in); + ASSERT_FALSE(r.IsSuccess()) << r.Error(); + EXPECT_EQ("15: invalid min_depth for VIEWPORT command", r.Error()); +} + +} // namespace amberscript +} // namespace amber diff --git a/src/command.h b/src/command.h index 207c336..26b9dc2 100644 --- a/src/command.h +++ b/src/command.h @@ -546,6 +546,16 @@ class BufferCommand : public BindableResourceCommand { } uint32_t GetDynamicOffset() const { return dynamic_offset_; } + void SetDescriptorOffset(uint64_t descriptor_offset) { + descriptor_offset_ = descriptor_offset; + } + uint64_t GetDescriptorOffset() const { return descriptor_offset_; } + + void SetDescriptorRange(uint64_t descriptor_range) { + descriptor_range_ = descriptor_range; + } + uint64_t GetDescriptorRange() const { return descriptor_range_; } + void SetValues(std::vector<Value>&& values) { values_ = std::move(values); } const std::vector<Value>& GetValues() const { return values_; } @@ -565,6 +575,8 @@ class BufferCommand : public BindableResourceCommand { uint32_t offset_ = 0; uint32_t base_mip_level_ = 0; uint32_t dynamic_offset_ = 0; + uint64_t descriptor_offset_ = 0; + uint64_t descriptor_range_ = ~0ULL; std::vector<Value> values_; }; diff --git a/src/float16_helper.cc b/src/float16_helper.cc index 617bd72..5cb35e7 100644 --- a/src/float16_helper.cc +++ b/src/float16_helper.cc @@ -15,6 +15,7 @@ #include "src/float16_helper.h" #include <cassert> +#include <cstring> // Float10 // | 9 8 7 6 5 | 4 3 2 1 0 | @@ -75,8 +76,11 @@ float HexFloat16ToFloat(const uint8_t* value) { } uint32_t hex = sign | exponent | mantissa; - float* hex_float = reinterpret_cast<float*>(&hex); - return *hex_float; + float hex_float; + static_assert((sizeof(uint32_t) == sizeof(float)), + "sizeof(uint32_t) != sizeof(float)"); + memcpy(&hex_float, &hex, sizeof(float)); + return hex_float; } // Convert float |value| whose size is 11 bits to 32 bits float @@ -89,8 +93,11 @@ float HexFloat11ToFloat(const uint8_t* value) { uint32_t mantissa = (static_cast<uint32_t>(value[0]) & 0x3f) << 17U; uint32_t hex = exponent | mantissa; - float* hex_float = reinterpret_cast<float*>(&hex); - return *hex_float; + float hex_float; + static_assert((sizeof(uint32_t) == sizeof(float)), + "sizeof(uint32_t) != sizeof(float)"); + memcpy(&hex_float, &hex, sizeof(float)); + return hex_float; } // Convert float |value| whose size is 10 bits to 32 bits float @@ -103,8 +110,11 @@ float HexFloat10ToFloat(const uint8_t* value) { uint32_t mantissa = (static_cast<uint32_t>(value[0]) & 0x1f) << 18U; uint32_t hex = exponent | mantissa; - float* hex_float = reinterpret_cast<float*>(&hex); - return *hex_float; + float hex_float; + static_assert((sizeof(uint32_t) == sizeof(float)), + "sizeof(uint32_t) != sizeof(float)"); + memcpy(&hex_float, &hex, sizeof(float)); + return hex_float; } } // namespace diff --git a/src/pipeline.cc b/src/pipeline.cc index 55c6bae..9b73041 100644 --- a/src/pipeline.cc +++ b/src/pipeline.cc @@ -516,7 +516,9 @@ void Pipeline::AddBuffer(Buffer* buf, uint32_t descriptor_set, uint32_t binding, uint32_t base_mip_level, - uint32_t dynamic_offset) { + uint32_t dynamic_offset, + uint64_t descriptor_offset, + uint64_t descriptor_range) { buffers_.push_back(BufferInfo{buf}); auto& info = buffers_.back(); @@ -526,6 +528,8 @@ void Pipeline::AddBuffer(Buffer* buf, info.base_mip_level = base_mip_level; info.dynamic_offset = dynamic_offset; info.sampler = buf->GetSampler(); + info.descriptor_offset = descriptor_offset; + info.descriptor_range = descriptor_range; } void Pipeline::AddBuffer(Buffer* buf, @@ -862,7 +866,7 @@ Result Pipeline::GenerateOpenCLPodBuffers() { opencl_pod_buffer_map_.insert( buf_iter, std::make_pair(std::make_pair(descriptor_set, binding), buffer)); - AddBuffer(buffer, buffer_type, descriptor_set, binding, 0, 0); + AddBuffer(buffer, buffer_type, descriptor_set, binding, 0, 0, 0, ~0ULL); } else { buffer = buf_iter->second; } diff --git a/src/pipeline.h b/src/pipeline.h index ec38a3a..7a80ad4 100644 --- a/src/pipeline.h +++ b/src/pipeline.h @@ -203,6 +203,8 @@ class Pipeline { uint32_t offset = 0; uint32_t stride = 0; Sampler* sampler = nullptr; + uint64_t descriptor_offset = 0; + uint64_t descriptor_range = ~0ULL; // ~0ULL == VK_WHOLE_SIZE }; /// Information on a sampler attached to the pipeline. @@ -335,13 +337,16 @@ class Pipeline { Buffer* GetIndexBuffer() const { return index_buffer_; } /// Adds |buf| of |type| to the pipeline at the given |descriptor_set|, - /// |binding|, |base_mip_level|, and |dynamic_offset|. + /// |binding|, |base_mip_level|, |descriptor_offset|, |descriptor_range| and + /// |dynamic_offset|. void AddBuffer(Buffer* buf, BufferType type, uint32_t descriptor_set, uint32_t binding, uint32_t base_mip_level, - uint32_t dynamic_offset); + uint32_t dynamic_offset, + uint64_t descriptor_offset, + uint64_t descriptor_range); /// Adds |buf| to the pipeline at the given |arg_name|. void AddBuffer(Buffer* buf, BufferType type, const std::string& arg_name); /// Adds |buf| to the pipeline at the given |arg_no|. diff --git a/src/pipeline_data.h b/src/pipeline_data.h index dc67c03..9a3b4d1 100644 --- a/src/pipeline_data.h +++ b/src/pipeline_data.h @@ -21,6 +21,15 @@ namespace amber { +struct Viewport { + float x; + float y; + float w; + float h; + float mind; + float maxd; +}; + /// Stores information used to configure a pipeline. class PipelineData { public: @@ -161,6 +170,14 @@ class PipelineData { void SetAlphaBlendOp(BlendOp op) { alpha_blend_op_ = op; } BlendOp GetAlphaBlendOp() const { return alpha_blend_op_; } + void SetViewport(const Viewport& v) { + has_viewport_data = true; + vp = v; + } + + bool HasViewportData() const { return has_viewport_data; } + const Viewport& GetViewport() const { return vp; } + private: StencilOp front_fail_op_ = StencilOp::kKeep; StencilOp front_pass_op_ = StencilOp::kKeep; @@ -213,6 +230,9 @@ class PipelineData { float depth_bias_slope_factor_ = 0.0f; float min_depth_bounds_ = 0.0f; float max_depth_bounds_ = 0.0f; + + bool has_viewport_data = false; + Viewport vp; }; } // namespace amber diff --git a/src/pipeline_test.cc b/src/pipeline_test.cc index e16313c..c0d2f67 100644 --- a/src/pipeline_test.cc +++ b/src/pipeline_test.cc @@ -262,7 +262,7 @@ TEST_F(PipelineTest, PipelineBufferWithoutFormat) { auto buf = MakeUnique<Buffer>(); buf->SetName("MyBuffer"); - p.AddBuffer(buf.get(), BufferType::kStorage, 0, 0, 0, 0); + p.AddBuffer(buf.get(), BufferType::kStorage, 0, 0, 0, 0, 0, 0); Result r = p.Validate(); EXPECT_FALSE(r.IsSuccess()) << r.Error(); @@ -365,11 +365,11 @@ TEST_F(PipelineTest, Clone) { auto buf1 = MakeUnique<Buffer>(); buf1->SetName("buf1"); - p.AddBuffer(buf1.get(), BufferType::kStorage, 1, 1, 0, 0); + p.AddBuffer(buf1.get(), BufferType::kStorage, 1, 1, 0, 0, 0, 0); auto buf2 = MakeUnique<Buffer>(); buf2->SetName("buf2"); - p.AddBuffer(buf2.get(), BufferType::kStorage, 1, 2, 0, 16); + p.AddBuffer(buf2.get(), BufferType::kStorage, 1, 2, 0, 16, 256, 512); auto clone = p.Clone(); EXPECT_EQ("", clone->GetName()); @@ -400,11 +400,15 @@ TEST_F(PipelineTest, Clone) { EXPECT_EQ(1U, bufs[0].descriptor_set); EXPECT_EQ(1U, bufs[0].binding); EXPECT_EQ(0U, bufs[0].dynamic_offset); + EXPECT_EQ(0U, bufs[0].descriptor_offset); + EXPECT_EQ(0U, bufs[0].descriptor_range); EXPECT_EQ("buf2", bufs[1].buffer->GetName()); EXPECT_EQ(1U, bufs[1].descriptor_set); EXPECT_EQ(2U, bufs[1].binding); EXPECT_EQ(16U, bufs[1].dynamic_offset); + EXPECT_EQ(256U, bufs[1].descriptor_offset); + EXPECT_EQ(512U, bufs[1].descriptor_range); } TEST_F(PipelineTest, OpenCLUpdateBindings) { diff --git a/src/vkscript/command_parser.cc b/src/vkscript/command_parser.cc index 0953344..610c7e6 100644 --- a/src/vkscript/command_parser.cc +++ b/src/vkscript/command_parser.cc @@ -577,7 +577,8 @@ Result CommandParser::ProcessSSBO() { buffer = b.get(); script_->AddBuffer(std::move(b)); pipeline_->ClearBuffers(set, binding); - pipeline_->AddBuffer(buffer, BufferType::kStorage, set, binding, 0, 0); + pipeline_->AddBuffer(buffer, BufferType::kStorage, set, binding, 0, 0, 0, + ~0ULL); } cmd->SetBuffer(buffer); } @@ -729,7 +730,8 @@ Result CommandParser::ProcessUniform() { buffer = b.get(); script_->AddBuffer(std::move(b)); pipeline_->ClearBuffers(set, binding); - pipeline_->AddBuffer(buffer, BufferType::kUniform, set, binding, 0, 0); + pipeline_->AddBuffer(buffer, BufferType::kUniform, set, binding, 0, 0, 0, + ~0ULL); } cmd->SetBuffer(buffer); diff --git a/src/vulkan/buffer_backed_descriptor.cc b/src/vulkan/buffer_backed_descriptor.cc index 2f7aa98..d1b1aed 100644 --- a/src/vulkan/buffer_backed_descriptor.cc +++ b/src/vulkan/buffer_backed_descriptor.cc @@ -35,26 +35,18 @@ BufferBackedDescriptor::~BufferBackedDescriptor() = default; Result BufferBackedDescriptor::RecordCopyDataToResourceIfNeeded( CommandBuffer* command) { - auto resources = GetResources(); - auto buffers = GetAmberBuffers(); - if (resources.size() != buffers.size()) - return Result( - "Vulkan: BufferBackedDescriptor::RecordCopyDataToResourceIfNeeded() " - "resource and buffer vector sizes are not matching"); - - for (size_t i = 0; i < resources.size(); i++) { - if (!buffers[i]->ValuePtr()->empty()) { - resources[i]->UpdateMemoryWithRawData(*buffers[i]->ValuePtr()); + for (const auto& resource : GetResources()) { + if (!resource.first->ValuePtr()->empty()) { + resource.second->UpdateMemoryWithRawData(*resource.first->ValuePtr()); // If the resource is read-only, keep the buffer data; Amber won't copy // read-only resources back into the host buffers, so it makes sense to // leave the buffer intact. if (!IsReadOnly()) - buffers[i]->ValuePtr()->clear(); + resource.first->ValuePtr()->clear(); } - resources[i]->CopyToDevice(command); + resource.second->CopyToDevice(command); } - return {}; } @@ -67,7 +59,7 @@ Result BufferBackedDescriptor::RecordCopyDataToHost(CommandBuffer* command) { } for (const auto& r : GetResources()) - r->CopyToHost(command); + r.second->CopyToHost(command); } return {}; @@ -79,11 +71,6 @@ Result BufferBackedDescriptor::MoveResourceToBufferOutput() { return {}; auto resources = GetResources(); - auto buffers = GetAmberBuffers(); - if (resources.size() != buffers.size()) - return Result( - "Vulkan: BufferBackedDescriptor::MoveResourceToBufferOutput() resource " - "and buffer vector sizes are not matching"); if (resources.empty()) { return Result( @@ -91,26 +78,25 @@ Result BufferBackedDescriptor::MoveResourceToBufferOutput() { "transfer resource"); } - for (size_t i = 0; i < resources.size(); i++) { - void* resource_memory_ptr = resources[i]->HostAccessibleMemoryPtr(); + for (const auto& resource : resources) { + void* resource_memory_ptr = resource.second->HostAccessibleMemoryPtr(); + auto* buffer = resource.first; if (!resource_memory_ptr) { return Result( "Vulkan: BufferBackedDescriptor::MoveResourceToBufferOutput() " "no host accessible memory pointer"); } - if (!buffers[i]->ValuePtr()->empty()) { + if (!buffer->ValuePtr()->empty()) { return Result( "Vulkan: BufferBackedDescriptor::MoveResourceToBufferOutput() " "output buffer is not empty"); } - auto size_in_bytes = resources[i]->GetSizeInBytes(); - buffers[i]->SetElementCount(size_in_bytes / - buffers[i]->GetFormat()->SizeInBytes()); - buffers[i]->ValuePtr()->resize(size_in_bytes); - std::memcpy(buffers[i]->ValuePtr()->data(), resource_memory_ptr, - size_in_bytes); + auto size_in_bytes = resource.second->GetSizeInBytes(); + buffer->SetElementCount(size_in_bytes / buffer->GetFormat()->SizeInBytes()); + buffer->ValuePtr()->resize(size_in_bytes); + std::memcpy(buffer->ValuePtr()->data(), resource_memory_ptr, size_in_bytes); } return {}; diff --git a/src/vulkan/buffer_backed_descriptor.h b/src/vulkan/buffer_backed_descriptor.h index c626dd8..8f9df41 100644 --- a/src/vulkan/buffer_backed_descriptor.h +++ b/src/vulkan/buffer_backed_descriptor.h @@ -16,6 +16,7 @@ #define SRC_VULKAN_BUFFER_BACKED_DESCRIPTOR_H_ #include <memory> +#include <utility> #include <vector> #include "amber/result.h" @@ -51,7 +52,10 @@ class BufferBackedDescriptor : public Descriptor { bool IsReadOnly() const; protected: - virtual std::vector<Resource*> GetResources() = 0; + /// Returns a list of unique transfer buffer resources. Note that this list + /// may contain less items than the |amber_buffers| vector contains if two or + /// more amber buffers use same Vulkan buffer. + virtual std::vector<std::pair<Buffer*, Resource*>> GetResources() = 0; private: std::vector<Buffer*> amber_buffers_; diff --git a/src/vulkan/buffer_descriptor.cc b/src/vulkan/buffer_descriptor.cc index 510565d..aa8b2dd 100644 --- a/src/vulkan/buffer_descriptor.cc +++ b/src/vulkan/buffer_descriptor.cc @@ -14,6 +14,8 @@ #include "src/vulkan/buffer_descriptor.h" +#include <algorithm> +#include <utility> #include <vector> #include "src/make_unique.h" @@ -39,17 +41,23 @@ Result BufferDescriptor::CreateResourceIfNeeded() { "only when |transfer_buffers| is empty"); } - transfer_buffers_.reserve(GetAmberBuffers().size()); + descriptor_offsets_.reserve(GetAmberBuffers().size()); + descriptor_ranges_.reserve(GetAmberBuffers().size()); for (const auto& amber_buffer : GetAmberBuffers()) { if (amber_buffer->ValuePtr()->empty()) continue; - uint32_t size_in_bytes = - amber_buffer ? static_cast<uint32_t>(amber_buffer->ValuePtr()->size()) - : 0; - transfer_buffers_.emplace_back(MakeUnique<TransferBuffer>( - device_, size_in_bytes, amber_buffer->GetFormat())); + // Check if the transfer buffer is already created. + if (transfer_buffers_.count(amber_buffer) > 0) + continue; + + auto size_in_bytes = + static_cast<uint32_t>(amber_buffer->ValuePtr()->size()); + + auto transfer_buffer = MakeUnique<TransferBuffer>( + device_, size_in_bytes, amber_buffer->GetFormat()); + VkBufferUsageFlags flags = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; if (IsUniformBuffer() || IsUniformBufferDynamic()) { @@ -64,9 +72,10 @@ Result BufferDescriptor::CreateResourceIfNeeded() { return Result("Unexpected buffer type when deciding usage flags"); } - Result r = transfer_buffers_.back()->Initialize(flags); + Result r = transfer_buffer->Initialize(flags); if (!r.IsSuccess()) return r; + transfer_buffers_[amber_buffer] = std::move(transfer_buffer); } is_descriptor_set_update_needed_ = true; @@ -89,12 +98,33 @@ void BufferDescriptor::UpdateDescriptorSetIfNeeded( std::vector<VkDescriptorBufferInfo> buffer_infos; std::vector<VkBufferView> buffer_views; - for (const auto& buffer : transfer_buffers_) { - VkDescriptorBufferInfo buffer_info; - buffer_info.buffer = buffer->GetVkBuffer(); - buffer_info.offset = 0; - buffer_info.range = VK_WHOLE_SIZE; - buffer_infos.push_back(buffer_info); + // Create VkDescriptorBufferInfo for every descriptor buffer. + for (uint32_t i = 0; i < GetAmberBuffers().size(); i++) { + const auto& buffer = transfer_buffers_[GetAmberBuffers()[i]]; + assert(buffer->GetVkBuffer() && "Unexpected descriptor type"); + // Add buffer infos for uniform and storage buffers. + if (IsUniformBuffer() || IsUniformBufferDynamic() || IsStorageBuffer() || + IsStorageBufferDynamic()) { + uint64_t range = descriptor_ranges_[i]; + // If dynamic offset is used, we must change range with VK_WHOLE_SIZE to + // an actual range. + // From vulkan spec: For each dynamic uniform or storage buffer binding in + // pDescriptorSets, the sum of the effective offset, as defined above, and + // the range of the binding must be less than or equal to the size of the + // buffer. + if ((IsUniformBufferDynamic() || IsStorageBufferDynamic()) && + descriptor_ranges_[i] == VK_WHOLE_SIZE) { + range = buffer->GetSizeInBytes() - dynamic_offsets_[i] - + descriptor_offsets_[i]; + } + + VkDescriptorBufferInfo buffer_info; + buffer_info.buffer = buffer->GetVkBuffer(); + buffer_info.offset = descriptor_offsets_[i]; + buffer_info.range = range; + + buffer_infos.push_back(buffer_info); + } if (IsUniformTexelBuffer() || IsStorageTexelBuffer()) { buffer_views.push_back(*buffer->GetVkBufferView()); @@ -106,7 +136,7 @@ void BufferDescriptor::UpdateDescriptorSetIfNeeded( write.dstSet = descriptor_set; write.dstBinding = binding_; write.dstArrayElement = 0; - write.descriptorCount = static_cast<uint32_t>(buffer_infos.size()); + write.descriptorCount = static_cast<uint32_t>(GetAmberBuffers().size()); write.descriptorType = GetVkDescriptorType(); write.pBufferInfo = buffer_infos.data(); write.pTexelBufferView = buffer_views.data(); @@ -116,10 +146,20 @@ void BufferDescriptor::UpdateDescriptorSetIfNeeded( is_descriptor_set_update_needed_ = false; } -std::vector<Resource*> BufferDescriptor::GetResources() { - std::vector<Resource*> ret; - for (auto& b : transfer_buffers_) { - ret.push_back(b.get()); +std::vector<std::pair<Buffer*, Resource*>> BufferDescriptor::GetResources() { + std::vector<std::pair<Buffer*, Resource*>> ret; + // Add unique amber buffers and related transfer buffers to the vector. + for (const auto& amber_buffer : GetAmberBuffers()) { + // Skip duplicate values. + const auto& image = + std::find_if(ret.begin(), ret.end(), + [&](const std::pair<Buffer*, Resource*>& buffer) { + return buffer.first == amber_buffer; + }); + if (image != ret.end()) + continue; + + ret.emplace_back(amber_buffer, transfer_buffers_[amber_buffer].get()); } return ret; } diff --git a/src/vulkan/buffer_descriptor.h b/src/vulkan/buffer_descriptor.h index d481ae5..2c3f390 100644 --- a/src/vulkan/buffer_descriptor.h +++ b/src/vulkan/buffer_descriptor.h @@ -16,6 +16,8 @@ #define SRC_VULKAN_BUFFER_DESCRIPTOR_H_ #include <memory> +#include <unordered_map> +#include <utility> #include <vector> #include "amber/result.h" @@ -50,14 +52,30 @@ class BufferDescriptor : public BufferBackedDescriptor { return dynamic_offsets_; } void AddDynamicOffset(uint32_t offset) { dynamic_offsets_.push_back(offset); } + std::vector<uint64_t> GetDescriptorOffsets() override { + return descriptor_offsets_; + } + void AddDescriptorOffset(uint64_t descriptor_offset) { + descriptor_offsets_.push_back(descriptor_offset); + } + std::vector<uint64_t> GetDescriptorRanges() override { + return descriptor_ranges_; + } + void AddDescriptorRange(uint64_t descriptor_range) { + descriptor_ranges_.push_back(descriptor_range); + } + BufferDescriptor* AsBufferDescriptor() override { return this; } protected: - std::vector<Resource*> GetResources() override; + std::vector<std::pair<Buffer*, Resource*>> GetResources() override; private: - std::vector<std::unique_ptr<TransferBuffer>> transfer_buffers_; + std::unordered_map<Buffer*, std::unique_ptr<TransferBuffer>> + transfer_buffers_; std::vector<uint32_t> dynamic_offsets_; + std::vector<VkDeviceSize> descriptor_offsets_; + std::vector<VkDeviceSize> descriptor_ranges_; }; } // namespace vulkan diff --git a/src/vulkan/descriptor.h b/src/vulkan/descriptor.h index ba702ff..03434a3 100644 --- a/src/vulkan/descriptor.h +++ b/src/vulkan/descriptor.h @@ -64,6 +64,8 @@ class Descriptor { virtual Result AddToBuffer(const std::vector<Value>&, uint32_t) { return {}; } virtual uint32_t GetDescriptorCount() { return 1; } virtual std::vector<uint32_t> GetDynamicOffsets() { return {}; } + virtual std::vector<VkDeviceSize> GetDescriptorOffsets() { return {}; } + virtual std::vector<VkDeviceSize> GetDescriptorRanges() { return {}; } virtual BufferDescriptor* AsBufferDescriptor() { return nullptr; } virtual BufferBackedDescriptor* AsBufferBackedDescriptor() { return nullptr; } virtual SamplerDescriptor* AsSamplerDescriptor() { return nullptr; } diff --git a/src/vulkan/engine_vulkan.cc b/src/vulkan/engine_vulkan.cc index 536993f..6bed716 100644 --- a/src/vulkan/engine_vulkan.cc +++ b/src/vulkan/engine_vulkan.cc @@ -254,6 +254,8 @@ Result EngineVulkan::CreatePipeline(amber::Pipeline* pipeline) { cmd->SetBinding(buf_info.binding); cmd->SetBaseMipLevel(buf_info.base_mip_level); cmd->SetDynamicOffset(buf_info.dynamic_offset); + cmd->SetDescriptorOffset(buf_info.descriptor_offset); + cmd->SetDescriptorRange(buf_info.descriptor_range); cmd->SetBuffer(buf_info.buffer); cmd->SetSampler(buf_info.sampler); diff --git a/src/vulkan/graphics_pipeline.cc b/src/vulkan/graphics_pipeline.cc index b83bea1..5536440 100644 --- a/src/vulkan/graphics_pipeline.cc +++ b/src/vulkan/graphics_pipeline.cc @@ -579,6 +579,16 @@ Result GraphicsPipeline::CreateVkGraphicsPipeline( 0, 0, static_cast<float>(frame_width_), static_cast<float>(frame_height_), 0, 1}; + if (pipeline_data->HasViewportData()) { + Viewport vp = pipeline_data->GetViewport(); + viewport.x = vp.x; + viewport.y = vp.y; + viewport.width = vp.w; + viewport.height = vp.h; + viewport.minDepth = vp.mind; + viewport.maxDepth = vp.maxd; + } + VkRect2D scissor = {{0, 0}, {frame_width_, frame_height_}}; VkPipelineViewportStateCreateInfo viewport_info = diff --git a/src/vulkan/image_descriptor.cc b/src/vulkan/image_descriptor.cc index 550ce0b..94e67d0 100644 --- a/src/vulkan/image_descriptor.cc +++ b/src/vulkan/image_descriptor.cc @@ -14,6 +14,10 @@ #include "src/vulkan/image_descriptor.h" +#include <algorithm> +#include <unordered_map> +#include <utility> + #include "src/vulkan/device.h" #include "src/vulkan/resource.h" @@ -34,9 +38,12 @@ ImageDescriptor::~ImageDescriptor() = default; Result ImageDescriptor::RecordCopyDataToResourceIfNeeded( CommandBuffer* command) { - for (auto& image : transfer_images_) { - image->ImageBarrier(command, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - VK_PIPELINE_STAGE_TRANSFER_BIT); + const auto transfer_images = GetResources(); + for (const auto& image : transfer_images) { + // Static cast is safe, because the type is known to be TransferImage*. + static_cast<TransferImage*>(image.second) + ->ImageBarrier(command, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + VK_PIPELINE_STAGE_TRANSFER_BIT); } Result r = BufferBackedDescriptor::RecordCopyDataToResourceIfNeeded(command); @@ -44,9 +51,10 @@ Result ImageDescriptor::RecordCopyDataToResourceIfNeeded( return r; // Just do this as early as possible. - for (auto& image : transfer_images_) { - image->ImageBarrier(command, VK_IMAGE_LAYOUT_GENERAL, - VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT); + for (const auto& image : transfer_images) { + static_cast<TransferImage*>(image.second) + ->ImageBarrier(command, VK_IMAGE_LAYOUT_GENERAL, + VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT); } return {}; @@ -59,12 +67,14 @@ Result ImageDescriptor::CreateResourceIfNeeded() { "only when |transfer_images| is empty"); } - transfer_images_.reserve(GetAmberBuffers().size()); - for (const auto& amber_buffer : GetAmberBuffers()) { if (amber_buffer->ValuePtr()->empty()) continue; + // Check if the transfer image is already created. + if (transfer_images_.count(amber_buffer) > 0) + continue; + // Default to 2D image. VkImageType image_type = VK_IMAGE_TYPE_2D; switch (amber_buffer->GetImageDimension()) { @@ -93,11 +103,11 @@ Result ImageDescriptor::CreateResourceIfNeeded() { aspect = VK_IMAGE_ASPECT_COLOR_BIT; } - transfer_images_.emplace_back(MakeUnique<TransferImage>( + auto transfer_image = MakeUnique<TransferImage>( device_, *fmt, aspect, image_type, amber_buffer->GetWidth(), amber_buffer->GetHeight(), amber_buffer->GetDepth(), amber_buffer->GetMipLevels(), base_mip_level_, VK_REMAINING_MIP_LEVELS, - amber_buffer->GetSamples())); + amber_buffer->GetSamples()); VkImageUsageFlags usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; @@ -109,10 +119,11 @@ Result ImageDescriptor::CreateResourceIfNeeded() { usage |= VK_IMAGE_USAGE_SAMPLED_BIT; } - Result r = transfer_images_.back()->Initialize(usage); - + Result r = transfer_image->Initialize(usage); if (!r.IsSuccess()) return r; + + transfer_images_[amber_buffer] = std::move(transfer_image); } if (amber_sampler_) { @@ -127,9 +138,10 @@ Result ImageDescriptor::CreateResourceIfNeeded() { Result ImageDescriptor::RecordCopyDataToHost(CommandBuffer* command) { if (!IsReadOnly()) { - for (auto& image : transfer_images_) { - image->ImageBarrier(command, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, - VK_PIPELINE_STAGE_TRANSFER_BIT); + for (auto& image : GetResources()) { + static_cast<TransferImage*>(image.second) + ->ImageBarrier(command, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + VK_PIPELINE_STAGE_TRANSFER_BIT); } BufferBackedDescriptor::RecordCopyDataToHost(command); @@ -156,7 +168,9 @@ void ImageDescriptor::UpdateDescriptorSetIfNeeded( std::vector<VkDescriptorImageInfo> image_infos; - for (const auto& image : transfer_images_) { + // Create VkDescriptorImageInfo for every descriptor image. + for (const auto& amber_buffer : GetAmberBuffers()) { + const auto& image = transfer_images_[amber_buffer]; VkDescriptorImageInfo image_info = {vulkan_sampler_.GetVkSampler(), image->GetVkImageView(), layout}; image_infos.push_back(image_info); @@ -177,10 +191,20 @@ void ImageDescriptor::UpdateDescriptorSetIfNeeded( is_descriptor_set_update_needed_ = false; } -std::vector<Resource*> ImageDescriptor::GetResources() { - std::vector<Resource*> ret; - for (auto& i : transfer_images_) { - ret.push_back(i.get()); +std::vector<std::pair<Buffer*, Resource*>> ImageDescriptor::GetResources() { + std::vector<std::pair<Buffer*, Resource*>> ret; + // Add unique amber buffers and related transfer images to the vector. + for (const auto& amber_buffer : GetAmberBuffers()) { + // Skip duplicate values. + const auto& image = + std::find_if(ret.begin(), ret.end(), + [&](const std::pair<Buffer*, Resource*>& buffer) { + return buffer.first == amber_buffer; + }); + if (image != ret.end()) + continue; + + ret.emplace_back(amber_buffer, transfer_images_[amber_buffer].get()); } return ret; } diff --git a/src/vulkan/image_descriptor.h b/src/vulkan/image_descriptor.h index 5f8dd0e..908ed17 100644 --- a/src/vulkan/image_descriptor.h +++ b/src/vulkan/image_descriptor.h @@ -16,6 +16,8 @@ #define SRC_VULKAN_IMAGE_DESCRIPTOR_H_ #include <memory> +#include <unordered_map> +#include <utility> #include <vector> #include "src/vulkan/buffer_backed_descriptor.h" @@ -43,11 +45,11 @@ class ImageDescriptor : public BufferBackedDescriptor { void SetAmberSampler(amber::Sampler* sampler) { amber_sampler_ = sampler; } protected: - std::vector<Resource*> GetResources() override; + std::vector<std::pair<Buffer*, Resource*>> GetResources() override; private: uint32_t base_mip_level_ = 0; - std::vector<std::unique_ptr<TransferImage>> transfer_images_; + std::unordered_map<Buffer*, std::unique_ptr<TransferImage>> transfer_images_; amber::Sampler* amber_sampler_ = nullptr; amber::vulkan::Sampler vulkan_sampler_; }; diff --git a/src/vulkan/pipeline.cc b/src/vulkan/pipeline.cc index e0b875c..259f375 100644 --- a/src/vulkan/pipeline.cc +++ b/src/vulkan/pipeline.cc @@ -322,6 +322,7 @@ Result Pipeline::AddBufferDescriptor(const BufferCommand* cmd) { cmd->GetDescriptorSet(), cmd->GetBinding()); if (cmd->IsCombinedImageSampler()) image_desc->SetAmberSampler(cmd->GetSampler()); + descriptors.push_back(std::move(image_desc)); } else { auto buffer_desc = MakeUnique<BufferDescriptor>( @@ -336,18 +337,18 @@ Result Pipeline::AddBufferDescriptor(const BufferCommand* cmd) { "Descriptors bound to the same binding needs to have matching " "descriptor types"); } - // Check that the buffer is not added already. - const auto& buffers = desc->AsBufferBackedDescriptor()->GetAmberBuffers(); - if (std::find(buffers.begin(), buffers.end(), cmd->GetBuffer()) != - buffers.end()) { - return Result("Buffer has been added already"); - } desc->AsBufferBackedDescriptor()->AddAmberBuffer(cmd->GetBuffer()); } if (cmd->IsUniformDynamic() || cmd->IsSSBODynamic()) desc->AsBufferDescriptor()->AddDynamicOffset(cmd->GetDynamicOffset()); + if (cmd->IsUniform() || cmd->IsUniformDynamic() || cmd->IsSSBO() || + cmd->IsSSBODynamic()) { + desc->AsBufferDescriptor()->AddDescriptorOffset(cmd->GetDescriptorOffset()); + desc->AsBufferDescriptor()->AddDescriptorRange(cmd->GetDescriptorRange()); + } + if (cmd->IsSSBO() && !desc->IsStorageBuffer()) { return Result( "Vulkan::AddBufferDescriptor BufferCommand for SSBO uses wrong " diff --git a/src/vulkan/pipeline_test.cc b/src/vulkan/pipeline_test.cc index 53eca44..9f60174 100644 --- a/src/vulkan/pipeline_test.cc +++ b/src/vulkan/pipeline_test.cc @@ -44,9 +44,9 @@ TEST_F(VulkanPipelineTest, AddBufferDescriptorAddBufferTwice) { &amber_pipeline); Result r = pipeline.AddBufferDescriptor(cmd.get()); ASSERT_TRUE(r.IsSuccess()) << r.Error(); - // Adding same buffer again should fail. + // Adding same buffer again shouldn't fail. r = pipeline.AddBufferDescriptor(cmd.get()); - ASSERT_FALSE(r.IsSuccess()); + ASSERT_TRUE(r.IsSuccess()); } } // namespace vulkan diff --git a/tests/cases/compute_descriptor_array_ssbo.amber b/tests/cases/compute_descriptor_array_ssbo.amber index bd4c773..0eb6b7d 100644 --- a/tests/cases/compute_descriptor_array_ssbo.amber +++ b/tests/cases/compute_descriptor_array_ssbo.amber @@ -20,12 +20,19 @@ layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; layout(binding = 0) buffer block0 { int data; -} ssbo_array[2]; +} ssbo_array_0[2]; + +layout(binding = 1) buffer block1 +{ + int data; +} ssbo_array_1[2]; void main() { - ssbo_array[0].data = 1; - ssbo_array[1].data = 2; + ssbo_array_0[0].data = 1; + ssbo_array_0[1].data = 2; + ssbo_array_1[0].data = 3; + ssbo_array_1[1].data = 4; } END @@ -37,13 +44,21 @@ BUFFER buf1 DATA_TYPE int32 DATA 0 END +# The Vulkan spec lists the maximum value of minStorageBufferOffsetAlignment +# (i.e. the maximum possible alignment requirement) as 256 bytes, so we will use +# buffer with size of 65 int32 values = 260 bytes. +BUFFER buf2 DATA_TYPE int32 SIZE 65 FILL 0 + PIPELINE compute pipeline ATTACH compute_shader BIND BUFFER_ARRAY buf0 buf1 AS storage DESCRIPTOR_SET 0 BINDING 0 + BIND BUFFER_ARRAY buf2 buf2 AS storage DESCRIPTOR_SET 0 BINDING 1 DESCRIPTOR_OFFSET 0 256 DESCRIPTOR_RANGE 256 4 END RUN pipeline 1 1 1 EXPECT buf0 IDX 0 EQ 1 EXPECT buf1 IDX 0 EQ 2 +EXPECT buf2 IDX 0 EQ 3 +EXPECT buf2 IDX 256 EQ 4 diff --git a/tests/cases/compute_descriptor_array_storagetexelbuffer.amber b/tests/cases/compute_descriptor_array_storagetexelbuffer.amber index d4a1994..3ddae3c 100644 --- a/tests/cases/compute_descriptor_array_storagetexelbuffer.amber +++ b/tests/cases/compute_descriptor_array_storagetexelbuffer.amber @@ -17,13 +17,20 @@ SHADER compute compute_shader GLSL #version 430 layout(local_size_x=4,local_size_y=1) in; uniform layout(set=0, binding=0) samplerBuffer texelsIn; -uniform layout(set=0, binding=1, rgba32f) imageBuffer texelsOut[2]; +uniform layout(set=0, binding=1, rgba32f) imageBuffer texelsOut_0[2]; +uniform layout(set=0, binding=2, rgba32f) imageBuffer texelsOut_1[2]; void main () { vec4 color = texelFetch(texelsIn, int(gl_GlobalInvocationID)); - imageStore(texelsOut[0], int(gl_GlobalInvocationID), color); - imageStore(texelsOut[1], int(gl_GlobalInvocationID), color * 2.0); + imageStore(texelsOut_0[0], int(gl_GlobalInvocationID), color); + imageStore(texelsOut_0[1], int(gl_GlobalInvocationID), color * 2.0); + if (int(gl_GlobalInvocationID) < 2) { + imageStore(texelsOut_1[0], int(gl_GlobalInvocationID), color * 3.0); + } + else { + imageStore(texelsOut_1[1], int(gl_GlobalInvocationID), color * 4.0); + } } END @@ -36,6 +43,7 @@ END BUFFER texel_buffer_out0 DATA_TYPE R32G32B32A32_SFLOAT SIZE 4 FILL 0 BUFFER texel_buffer_out1 DATA_TYPE R32G32B32A32_SFLOAT SIZE 4 FILL 0 +BUFFER texel_buffer_out2 DATA_TYPE R32G32B32A32_SFLOAT SIZE 4 FILL 0 BUFFER ref0 DATA_TYPE R32G32B32A32_SFLOAT DATA 1.0 0.0 0.0 1.0 @@ -51,13 +59,22 @@ BUFFER ref1 DATA_TYPE R32G32B32A32_SFLOAT DATA 0.0 2.0 2.0 2.0 END +BUFFER ref2 DATA_TYPE R32G32B32A32_SFLOAT DATA +3.0 0.0 0.0 3.0 +0.0 3.0 0.0 3.0 +0.0 0.0 4.0 4.0 +0.0 4.0 4.0 4.0 +END + PIPELINE compute pipeline ATTACH compute_shader BIND BUFFER texel_buffer_in AS uniform_texel_buffer DESCRIPTOR_SET 0 BINDING 0 BIND BUFFER_ARRAY texel_buffer_out0 texel_buffer_out1 AS storage_texel_buffer DESCRIPTOR_SET 0 BINDING 1 + BIND BUFFER_ARRAY texel_buffer_out2 texel_buffer_out2 AS storage_texel_buffer DESCRIPTOR_SET 0 BINDING 2 END RUN pipeline 1 1 1 EXPECT texel_buffer_out0 EQ_BUFFER ref0 EXPECT texel_buffer_out1 EQ_BUFFER ref1 +EXPECT texel_buffer_out2 EQ_BUFFER ref2 diff --git a/tests/cases/compute_dynamic_buffers.amber b/tests/cases/compute_dynamic_buffers.amber index 9412e30..9dd4149 100644 --- a/tests/cases/compute_dynamic_buffers.amber +++ b/tests/cases/compute_dynamic_buffers.amber @@ -25,11 +25,16 @@ layout(set = 0, binding = 1) buffer block1 { vec4 data1; }; +layout(set = 0, binding = 2) buffer block2 +{ + vec4 data2; +}; void main() { data0 = vec4(1, 2, 3, 4); data1 = vec4(5, 6, 7, 8); + data2 = vec4(9, 10, 11, 12); } END @@ -39,6 +44,7 @@ END # after the alignment (256 / 16 + 1). BUFFER buf0 DATA_TYPE vec4<float> SIZE 17 FILL 0.0 BUFFER buf1 DATA_TYPE vec4<float> SIZE 17 FILL 0.0 +BUFFER buf2 DATA_TYPE vec4<float> SIZE 33 FILL 0.0 PIPELINE compute pipeline ATTACH compute_shader @@ -46,9 +52,12 @@ PIPELINE compute pipeline BIND BUFFER buf0 AS storage_dynamic DESCRIPTOR_SET 0 BINDING 0 OFFSET 0 # Using the maximum possible required offset alignment of 256 bytes to support all implementations. BIND BUFFER buf1 AS storage_dynamic DESCRIPTOR_SET 0 BINDING 1 OFFSET 256 + # Same as above, but with buffer offset of 256 bytes (total offset of 512). + BIND BUFFER buf2 AS storage_dynamic DESCRIPTOR_SET 0 BINDING 2 OFFSET 256 DESCRIPTOR_OFFSET 256 END RUN pipeline 1 1 1 EXPECT buf0 IDX 0 EQ 1.0 2.0 3.0 4.0 EXPECT buf1 IDX 256 EQ 5.0 6.0 7.0 8.0 +EXPECT buf2 IDX 512 EQ 9.0 10.0 11.0 12.0 diff --git a/tests/cases/compute_dynamic_buffers_descriptor_array.amber b/tests/cases/compute_dynamic_buffers_descriptor_array.amber index fe07b6c..ccc4c42 100644 --- a/tests/cases/compute_dynamic_buffers_descriptor_array.amber +++ b/tests/cases/compute_dynamic_buffers_descriptor_array.amber @@ -19,12 +19,18 @@ SHADER compute compute_shader GLSL layout(set = 0, binding = 0) buffer block0 { vec4 data; -} ssbo_array[2]; +} ssbo_array_0[2]; +layout(set = 0, binding = 1) buffer block1 +{ + int data; +} ssbo_array_1[2]; void main() { - ssbo_array[0].data = vec4(1, 2, 3, 4); - ssbo_array[1].data = vec4(5, 6, 7, 8); + ssbo_array_0[0].data = vec4(1, 2, 3, 4); + ssbo_array_0[1].data = vec4(5, 6, 7, 8); + ssbo_array_1[0].data = 9; + ssbo_array_1[1].data = 10; } END @@ -34,15 +40,20 @@ END # after the alignment (256 / 16 + 1). BUFFER buf0 DATA_TYPE vec4<float> SIZE 17 FILL 0.0 BUFFER buf1 DATA_TYPE vec4<float> SIZE 17 FILL 0.0 +# Same as above, but with int32 and double size: 512 / 4 + 1 +BUFFER buf2 DATA_TYPE int32 SIZE 129 FILL 0 PIPELINE compute pipeline ATTACH compute_shader # Using the maximum possible required offset alignment of 256 bytes to support all implementations. BIND BUFFER_ARRAY buf0 buf1 AS storage_dynamic DESCRIPTOR_SET 0 BINDING 0 OFFSET 0 256 + BIND BUFFER_ARRAY buf2 buf2 AS storage_dynamic DESCRIPTOR_SET 0 BINDING 1 OFFSET 0 256 DESCRIPTOR_OFFSET 0 256 END RUN pipeline 1 1 1 EXPECT buf0 IDX 0 EQ 1.0 2.0 3.0 4.0 EXPECT buf1 IDX 256 EQ 5.0 6.0 7.0 8.0 +EXPECT buf2 IDX 0 EQ 9 +EXPECT buf2 IDX 512 EQ 10 diff --git a/tests/cases/graphics_descriptor_array_combined_image_sampler.amber b/tests/cases/graphics_descriptor_array_combined_image_sampler.amber index d7fe3c0..e80ca1e 100644 --- a/tests/cases/graphics_descriptor_array_combined_image_sampler.amber +++ b/tests/cases/graphics_descriptor_array_combined_image_sampler.amber @@ -40,10 +40,16 @@ 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[2]; +uniform layout(set=0, binding=0) sampler2D tex_sampler_0[2]; +uniform layout(set=0, binding=1) sampler2D tex_sampler_1[2]; void main() { - color_out = texture(tex_sampler[0], texcoords_in); - color_out = color_out + texture(tex_sampler[1], texcoords_in); + vec4 tex_0_color = texture(tex_sampler_0[0], texcoords_in); + color_out = tex_0_color + texture(tex_sampler_0[1], texcoords_in); + // tex_sampler_1[0] and tex_sampler_1[1] should be equal to tex_sampler_0[0] + if (tex_0_color != texture(tex_sampler_1[0], texcoords_in) || + tex_0_color != texture(tex_sampler_1[1], texcoords_in)) { + color_out = vec4(1.0); + } } END @@ -75,6 +81,7 @@ PIPELINE graphics pipeline ATTACH vert_shader_tex ATTACH frag_shader_tex BIND BUFFER_ARRAY texture0 texture1 AS combined_image_sampler SAMPLER sampler DESCRIPTOR_SET 0 BINDING 0 + BIND BUFFER_ARRAY texture0 texture0 AS combined_image_sampler SAMPLER sampler DESCRIPTOR_SET 0 BINDING 1 VERTEX_DATA position LOCATION 0 VERTEX_DATA texcoords LOCATION 1 BIND BUFFER framebuffer AS color LOCATION 0 diff --git a/tests/cases/graphics_descriptor_array_sampled_image.amber b/tests/cases/graphics_descriptor_array_sampled_image.amber index 497f6da..3342054 100644 --- a/tests/cases/graphics_descriptor_array_sampled_image.amber +++ b/tests/cases/graphics_descriptor_array_sampled_image.amber @@ -40,11 +40,18 @@ 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) texture2D tex[2]; +uniform layout(set=0, binding=0) texture2D tex_0[2]; uniform layout(set=0, binding=1) sampler tex_sampler; +uniform layout(set=0, binding=2) texture2D tex_1[2]; void main() { - color_out = texture(sampler2D(tex[0], tex_sampler), texcoords_in); - color_out += texture(sampler2D(tex[1], tex_sampler), texcoords_in); + vec4 tex_0_color = texture(sampler2D(tex_0[0], tex_sampler), texcoords_in); + color_out = tex_0_color; + color_out += texture(sampler2D(tex_0[1], tex_sampler), texcoords_in); + // tex_1[0] and tex_1[1] should be equal to tex_0[0]. + if (tex_0_color != texture(sampler2D(tex_1[0], tex_sampler), texcoords_in) || + tex_0_color != texture(sampler2D(tex_1[1], tex_sampler), texcoords_in)) { + color_out = vec4(1.0); + } } END @@ -77,6 +84,7 @@ PIPELINE graphics pipeline ATTACH frag_shader_tex BIND BUFFER_ARRAY texture0 texture1 AS sampled_image DESCRIPTOR_SET 0 BINDING 0 BIND SAMPLER sampler DESCRIPTOR_SET 0 BINDING 1 + BIND BUFFER_ARRAY texture0 texture0 AS sampled_image DESCRIPTOR_SET 0 BINDING 2 VERTEX_DATA position LOCATION 0 VERTEX_DATA texcoords LOCATION 1 BIND BUFFER framebuffer AS color LOCATION 0 diff --git a/tests/cases/non_default_entry_point.amber b/tests/cases/non_default_entry_point.amber index 36b2135..3a0892f 100644 --- a/tests/cases/non_default_entry_point.amber +++ b/tests/cases/non_default_entry_point.amber @@ -13,6 +13,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +DEVICE_EXTENSION VK_KHR_storage_buffer_storage_class + SHADER compute my_shader SPIRV-ASM OpCapability Shader OpExtension "SPV_KHR_storage_buffer_storage_class" diff --git a/tests/cases/shader_specialization.amber b/tests/cases/shader_specialization.amber index 1db2a4f..4514b6f 100644 --- a/tests/cases/shader_specialization.amber +++ b/tests/cases/shader_specialization.amber @@ -13,6 +13,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +DEVICE_EXTENSION VK_KHR_storage_buffer_storage_class + SHADER compute my_compute SPIRV-ASM OpCapability Shader OpExtension "SPV_KHR_storage_buffer_storage_class" diff --git a/tests/cases/storage16.amber b/tests/cases/storage16.amber index 5cf8113..36a62dc 100644 --- a/tests/cases/storage16.amber +++ b/tests/cases/storage16.amber @@ -19,6 +19,7 @@ DEVICE_EXTENSION VK_KHR_16bit_storage DEVICE_FEATURE shaderInt16 DEVICE_FEATURE Storage16BitFeatures.uniformAndStorageBuffer16BitAccess DEVICE_FEATURE Storage16BitFeatures.storagePushConstant16 +DEVICE_FEATURE Storage16BitFeatures.storageBuffer16BitAccess SHADER compute comp_shader GLSL #version 450 diff --git a/tests/cases/viewport.amber b/tests/cases/viewport.amber new file mode 100644 index 0000000..ca9b0a5 --- /dev/null +++ b/tests/cases/viewport.amber @@ -0,0 +1,59 @@ +#!amber +# +# Copyright 2021 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 GLSL +#version 430 + +layout(location = 0) in vec4 position; + +void main() { + gl_Position = vec4(position.xy, 0.5, 1.0); +} +END + +SHADER fragment frag_shader GLSL +#version 430 + +layout(location = 0) out vec4 final_color; + +void main() { + final_color = vec4(0, 1, 0, 1); +} +END + +BUFFER framebuffer FORMAT B8G8R8A8_UNORM + +PIPELINE graphics pipeline1 + ATTACH vert_shader + ATTACH frag_shader + + FRAMEBUFFER_SIZE 256 256 + VIEWPORT 10.0 10.0 SIZE 100.0 100.0 + + BIND BUFFER framebuffer AS color LOCATION 0 +END + +CLEAR_COLOR pipeline1 255 255 255 255 +CLEAR pipeline1 +RUN pipeline1 DRAW_RECT POS 0 0 SIZE 256 256 + +# Check within the viewport +EXPECT framebuffer IDX 10 10 SIZE 100 100 EQ_RGBA 0 255 0 255 +# Check the borders were untouched +EXPECT framebuffer IDX 0 0 SIZE 10 256 EQ_RGBA 255 255 255 255 +EXPECT framebuffer IDX 110 0 SIZE 146 256 EQ_RGBA 255 255 255 255 +EXPECT framebuffer IDX 10 0 SIZE 100 10 EQ_RGBA 255 255 255 255 +EXPECT framebuffer IDX 10 110 SIZE 100 146 EQ_RGBA 255 255 255 255 diff --git a/tests/cases/viewport_depth.amber b/tests/cases/viewport_depth.amber new file mode 100644 index 0000000..2bf7a9e --- /dev/null +++ b/tests/cases/viewport_depth.amber @@ -0,0 +1,61 @@ +#!amber +# +# Copyright 2021 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 GLSL +#version 430 + +layout(location = 0) in vec4 position; + +void main() { + gl_Position = vec4(position.xy, 0.5, 1.0); +} +END + +SHADER fragment frag_shader GLSL +#version 430 + +layout(location = 0) out vec4 final_color; + +void main() { + final_color = vec4(0, 1, 0, 1); +} +END + +BUFFER framebuffer FORMAT B8G8R8A8_UNORM +BUFFER depth FORMAT D32_SFLOAT + +PIPELINE graphics pipeline1 + ATTACH vert_shader + ATTACH frag_shader + + DEPTH + TEST on + WRITE on + END + + FRAMEBUFFER_SIZE 256 256 + VIEWPORT 0.0 0.0 SIZE 256.0 256.0 MIN_DEPTH 0.3 MAX_DEPTH 0.9 + + BIND BUFFER framebuffer AS color LOCATION 0 + BIND BUFFER depth AS depth_stencil +END + +CLEAR_DEPTH pipeline1 1.0 +CLEAR_COLOR pipeline1 255 255 255 255 +CLEAR pipeline1 +RUN pipeline1 DRAW_RECT POS 0 0 SIZE 256 256 + +EXPECT depth IDX 0 TOLERANCE 1.0e-6 EQ 0.6 |