diff --git a/include/amber/amber_vulkan.h b/include/amber/amber_vulkan.h index d0315790..307a58ab 100644 --- a/include/amber/amber_vulkan.h +++ b/include/amber/amber_vulkan.h @@ -49,6 +49,18 @@ struct VulkanEngineConfig : public EngineConfig { /// the extension is not enabled, |available_features| will be used. VkPhysicalDeviceFeatures2KHR available_features2; + /// Physical device properties available for |physical_device|. The + /// |available_properties| will be ignored if + /// VK_KHR_get_physical_device_properties2 is enabled, |available_properties2| + /// will be used in that case. + VkPhysicalDeviceProperties available_properties; + + /// Physical device properties for |physical_device|.The + /// |available_properties2| will only be used if + /// VK_KHR_get_physical_device_properties2 is enabled. If the extension is not + /// enabled, |available_properties| will be used. + VkPhysicalDeviceProperties2KHR available_properties2; + /// Instance extensions available. std::vector available_instance_extensions; diff --git a/include/amber/recipe.h b/include/amber/recipe.h index 0fd2445e..4b8d877f 100644 --- a/include/amber/recipe.h +++ b/include/amber/recipe.h @@ -35,6 +35,9 @@ class RecipeImpl { /// Returns required features in the given recipe. virtual std::vector GetRequiredFeatures() const = 0; + /// Returns required features in the given recipe. + virtual std::vector GetRequiredProperties() const = 0; + /// Returns required device extensions in the given recipe. virtual std::vector GetRequiredDeviceExtensions() const = 0; @@ -67,6 +70,9 @@ class Recipe { /// Returns required features in the given recipe. std::vector GetRequiredFeatures() const; + /// Returns required properties in the given recipe. + std::vector GetRequiredProperties() const; + /// Returns required device extensions in the given recipe. std::vector GetRequiredDeviceExtensions() const; diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3b7a4a5a..1d0b5764 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -141,6 +141,7 @@ if (${AMBER_ENABLE_TESTS}) amberscript/parser_copy_test.cc amberscript/parser_depth_test.cc amberscript/parser_device_feature_test.cc + amberscript/parser_device_property_test.cc amberscript/parser_expect_test.cc amberscript/parser_extension_test.cc amberscript/parser_framebuffer_test.cc diff --git a/src/amber.cc b/src/amber.cc index 9bf806e9..01361709 100644 --- a/src/amber.cc +++ b/src/amber.cc @@ -133,6 +133,7 @@ Result CreateEngineAndCheckRequirements(const Recipe* recipe, // much else. Refactor this if they end up doing to much here. Result r = engine->Initialize(opts->config, delegate, script->GetRequiredFeatures(), + script->GetRequiredProperties(), script->GetRequiredInstanceExtensions(), script->GetRequiredDeviceExtensions()); if (!r.IsSuccess()) diff --git a/src/amberscript/parser.cc b/src/amberscript/parser.cc index a2e837fd..6eb34319 100644 --- a/src/amberscript/parser.cc +++ b/src/amberscript/parser.cc @@ -304,6 +304,8 @@ Result Parser::Parse(const std::string& data) { r = ParseDeviceFeature(); } else if (tok == "DEVICE_EXTENSION") { r = ParseDeviceExtension(); + } else if (tok == "DEVICE_PROPERTY") { + r = ParseDeviceProperty(); } else if (tok == "IMAGE") { r = ParseImage(); } else if (tok == "INSTANCE_EXTENSION") { @@ -3421,6 +3423,20 @@ Result Parser::ParseDeviceFeature() { return ValidateEndOfStatement("DEVICE_FEATURE command"); } +Result Parser::ParseDeviceProperty() { + auto token = tokenizer_->NextToken(); + if (token->IsEOS() || token->IsEOL()) + return Result("missing property name for DEVICE_PROPERTY command"); + if (!token->IsIdentifier()) + return Result("invalid property name for DEVICE_PROPERTY command"); + if (!script_->IsKnownProperty(token->AsString())) + return Result("unknown property name for DEVICE_PROPERTY command"); + + script_->AddRequiredProperty(token->AsString()); + + return ValidateEndOfStatement("DEVICE_PROPERTY command"); +} + Result Parser::ParseRepeat() { auto token = tokenizer_->NextToken(); if (token->IsEOL() || token->IsEOL()) diff --git a/src/amberscript/parser.h b/src/amberscript/parser.h index fb81c827..0f555950 100644 --- a/src/amberscript/parser.h +++ b/src/amberscript/parser.h @@ -84,6 +84,7 @@ class Parser : public amber::Parser { Result ParseCopy(); Result ParseDeviceFeature(); Result ParseDeviceExtension(); + Result ParseDeviceProperty(); Result ParseInstanceExtension(); Result ParseRepeat(); Result ParseSet(); diff --git a/src/amberscript/parser_device_property_test.cc b/src/amberscript/parser_device_property_test.cc new file mode 100644 index 00000000..f42fafeb --- /dev/null +++ b/src/amberscript/parser_device_property_test.cc @@ -0,0 +1,120 @@ +// Copyright 2024 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, DeviceProperty) { + std::string in = R"( +DEVICE_PROPERTY FloatControlsProperties.shaderSignedZeroInfNanPreserveFloat16 +DEVICE_PROPERTY FloatControlsProperties.shaderSignedZeroInfNanPreserveFloat32 +DEVICE_PROPERTY FloatControlsProperties.shaderSignedZeroInfNanPreserveFloat64 +DEVICE_PROPERTY FloatControlsProperties.shaderDenormPreserveFloat16 +DEVICE_PROPERTY FloatControlsProperties.shaderDenormPreserveFloat32 +DEVICE_PROPERTY FloatControlsProperties.shaderDenormPreserveFloat64 +DEVICE_PROPERTY FloatControlsProperties.shaderDenormFlushToZeroFloat16 +DEVICE_PROPERTY FloatControlsProperties.shaderDenormFlushToZeroFloat32 +DEVICE_PROPERTY FloatControlsProperties.shaderDenormFlushToZeroFloat64 +DEVICE_PROPERTY FloatControlsProperties.shaderRoundingModeRTEFloat16 +DEVICE_PROPERTY FloatControlsProperties.shaderRoundingModeRTEFloat32 +DEVICE_PROPERTY FloatControlsProperties.shaderRoundingModeRTEFloat64 +DEVICE_PROPERTY FloatControlsProperties.shaderRoundingModeRTZFloat16 +DEVICE_PROPERTY FloatControlsProperties.shaderRoundingModeRTZFloat32 +DEVICE_PROPERTY FloatControlsProperties.shaderRoundingModeRTZFloat64)"; + + Parser parser; + Result r = parser.Parse(in); + ASSERT_TRUE(r.IsSuccess()) << r.Error(); + + auto script = parser.GetScript(); + const auto& properties = script->GetRequiredProperties(); + ASSERT_EQ(15U, properties.size()); + EXPECT_EQ("FloatControlsProperties.shaderSignedZeroInfNanPreserveFloat16", + properties[0]); + EXPECT_EQ("FloatControlsProperties.shaderSignedZeroInfNanPreserveFloat32", + properties[1]); + EXPECT_EQ("FloatControlsProperties.shaderSignedZeroInfNanPreserveFloat64", + properties[2]); + EXPECT_EQ("FloatControlsProperties.shaderDenormPreserveFloat16", + properties[3]); + EXPECT_EQ("FloatControlsProperties.shaderDenormPreserveFloat32", + properties[4]); + EXPECT_EQ("FloatControlsProperties.shaderDenormPreserveFloat64", + properties[5]); + EXPECT_EQ("FloatControlsProperties.shaderDenormFlushToZeroFloat16", + properties[6]); + EXPECT_EQ("FloatControlsProperties.shaderDenormFlushToZeroFloat32", + properties[7]); + EXPECT_EQ("FloatControlsProperties.shaderDenormFlushToZeroFloat64", + properties[8]); + EXPECT_EQ("FloatControlsProperties.shaderRoundingModeRTEFloat16", + properties[9]); + EXPECT_EQ("FloatControlsProperties.shaderRoundingModeRTEFloat32", + properties[10]); + EXPECT_EQ("FloatControlsProperties.shaderRoundingModeRTEFloat64", + properties[11]); + EXPECT_EQ("FloatControlsProperties.shaderRoundingModeRTZFloat16", + properties[12]); + EXPECT_EQ("FloatControlsProperties.shaderRoundingModeRTZFloat32", + properties[13]); + EXPECT_EQ("FloatControlsProperties.shaderRoundingModeRTZFloat64", + properties[14]); +} + +TEST_F(AmberScriptParserTest, DevicePropertyMissingProperty) { + std::string in = "DEVICE_PROPERTY"; + + Parser parser; + Result r = parser.Parse(in); + ASSERT_FALSE(r.IsSuccess()); + EXPECT_EQ("1: missing property name for DEVICE_PROPERTY command", r.Error()); +} + +TEST_F(AmberScriptParserTest, DevicePropertyUnknown) { + std::string in = "DEVICE_PROPERTY unknown"; + + Parser parser; + Result r = parser.Parse(in); + ASSERT_FALSE(r.IsSuccess()); + EXPECT_EQ("1: unknown property name for DEVICE_PROPERTY command", r.Error()); +} + +TEST_F(AmberScriptParserTest, DevicePropertyInvalid) { + std::string in = "DEVICE_PROPERTY 12345"; + + Parser parser; + Result r = parser.Parse(in); + ASSERT_FALSE(r.IsSuccess()); + EXPECT_EQ("1: invalid property name for DEVICE_PROPERTY command", r.Error()); +} + +TEST_F(AmberScriptParserTest, DevicePropertyExtraParams) { + std::string in = + "DEVICE_PROPERTY FloatControlsProperties.shaderDenormPreserveFloat16 " + "EXTRA"; + + Parser parser; + Result r = parser.Parse(in); + ASSERT_FALSE(r.IsSuccess()); + EXPECT_EQ("1: extra parameters after DEVICE_PROPERTY command: EXTRA", + r.Error()); +} + +} // namespace amberscript +} // namespace amber diff --git a/src/engine.h b/src/engine.h index d4442589..2d1ba61e 100644 --- a/src/engine.h +++ b/src/engine.h @@ -71,6 +71,7 @@ class Engine { EngineConfig* config, Delegate* delegate, const std::vector& features, + const std::vector& properties, const std::vector& instance_extensions, const std::vector& device_extensions) = 0; diff --git a/src/executor_test.cc b/src/executor_test.cc index fa57c743..2625be28 100644 --- a/src/executor_test.cc +++ b/src/executor_test.cc @@ -37,9 +37,11 @@ class EngineStub : public Engine { Result Initialize(EngineConfig*, Delegate*, const std::vector& features, + const std::vector& properties, const std::vector& instance_exts, const std::vector& device_exts) override { features_ = features; + properties_ = properties; instance_extensions_ = instance_exts; device_extensions_ = device_exts; return {}; @@ -193,6 +195,7 @@ class EngineStub : public Engine { bool did_buffer_command_ = false; std::vector features_; + std::vector properties_; std::vector instance_extensions_; std::vector device_extensions_; @@ -207,11 +210,12 @@ class VkScriptExecutorTest : public testing::Test { std::unique_ptr MakeEngine() { return MakeUnique(); } std::unique_ptr MakeAndInitializeEngine( const std::vector& features, + const std::vector& properties, const std::vector& instance_extensions, const std::vector& device_extensions) { std::unique_ptr engine = MakeUnique(); - engine->Initialize(nullptr, nullptr, features, instance_extensions, - device_extensions); + engine->Initialize(nullptr, nullptr, features, properties, + instance_extensions, device_extensions); return engine; } EngineStub* ToStub(Engine* engine) { @@ -233,6 +237,7 @@ logicOp)"; auto script = parser.GetScript(); auto engine = MakeAndInitializeEngine(script->GetRequiredFeatures(), + script->GetRequiredProperties(), script->GetRequiredInstanceExtensions(), script->GetRequiredDeviceExtensions()); @@ -263,6 +268,7 @@ VK_KHR_variable_pointers)"; auto script = parser.GetScript(); auto engine = MakeAndInitializeEngine(script->GetRequiredFeatures(), + script->GetRequiredProperties(), script->GetRequiredInstanceExtensions(), script->GetRequiredDeviceExtensions()); @@ -293,6 +299,7 @@ depthstencil D24_UNORM_S8_UINT)"; auto script = parser.GetScript(); auto engine = MakeAndInitializeEngine(script->GetRequiredFeatures(), + script->GetRequiredProperties(), script->GetRequiredInstanceExtensions(), script->GetRequiredDeviceExtensions()); @@ -320,6 +327,7 @@ fence_timeout 12345)"; auto script = parser.GetScript(); auto engine = MakeAndInitializeEngine(script->GetRequiredFeatures(), + script->GetRequiredProperties(), script->GetRequiredInstanceExtensions(), script->GetRequiredDeviceExtensions()); @@ -355,6 +363,7 @@ fence_timeout 12345)"; auto script = parser.GetScript(); auto engine = MakeAndInitializeEngine(script->GetRequiredFeatures(), + script->GetRequiredProperties(), script->GetRequiredInstanceExtensions(), script->GetRequiredDeviceExtensions()); diff --git a/src/recipe.cc b/src/recipe.cc index 7e22bd47..7d46f05e 100644 --- a/src/recipe.cc +++ b/src/recipe.cc @@ -35,6 +35,10 @@ std::vector Recipe::GetRequiredFeatures() const { return impl_ ? impl_->GetRequiredFeatures() : std::vector(); } +std::vector Recipe::GetRequiredProperties() const { + return impl_ ? impl_->GetRequiredProperties() : std::vector(); +} + std::vector Recipe::GetRequiredDeviceExtensions() const { return impl_ ? impl_->GetRequiredDeviceExtensions() : std::vector(); diff --git a/src/script.cc b/src/script.cc index 091949e2..d5732d55 100644 --- a/src/script.cc +++ b/src/script.cc @@ -134,6 +134,27 @@ bool Script::IsKnownFeature(const std::string& name) const { "ShaderSubgroupExtendedTypesFeatures.shaderSubgroupExtendedTypes"; } +bool Script::IsKnownProperty(const std::string& name) const { + return name == + "FloatControlsProperties.shaderSignedZeroInfNanPreserveFloat16" || + name == + "FloatControlsProperties.shaderSignedZeroInfNanPreserveFloat32" || + name == + "FloatControlsProperties.shaderSignedZeroInfNanPreserveFloat64" || + name == "FloatControlsProperties.shaderDenormPreserveFloat16" || + name == "FloatControlsProperties.shaderDenormPreserveFloat32" || + name == "FloatControlsProperties.shaderDenormPreserveFloat64" || + name == "FloatControlsProperties.shaderDenormFlushToZeroFloat16" || + name == "FloatControlsProperties.shaderDenormFlushToZeroFloat32" || + name == "FloatControlsProperties.shaderDenormFlushToZeroFloat64" || + name == "FloatControlsProperties.shaderRoundingModeRTEFloat16" || + name == "FloatControlsProperties.shaderRoundingModeRTEFloat32" || + name == "FloatControlsProperties.shaderRoundingModeRTEFloat64" || + name == "FloatControlsProperties.shaderRoundingModeRTZFloat16" || + name == "FloatControlsProperties.shaderRoundingModeRTZFloat32" || + name == "FloatControlsProperties.shaderRoundingModeRTZFloat64"; +} + type::Type* Script::ParseType(const std::string& str) { auto type = GetType(str); if (type) diff --git a/src/script.h b/src/script.h index b4c6e1a2..3a8a17a9 100644 --- a/src/script.h +++ b/src/script.h @@ -43,6 +43,7 @@ class Script : public RecipeImpl { ~Script() override; bool IsKnownFeature(const std::string& name) const; + bool IsKnownProperty(const std::string& name) const; /// Retrieves information on the shaders in the given script. std::vector GetShaderInfo() const override; @@ -52,6 +53,10 @@ class Script : public RecipeImpl { return engine_info_.required_features; } + std::vector GetRequiredProperties() const override { + return engine_info_.required_properties; + } + /// Returns required device extensions in the given recipe. std::vector GetRequiredDeviceExtensions() const override { return engine_info_.required_device_extensions; @@ -166,6 +171,12 @@ class Script : public RecipeImpl { engine_info_.required_features.push_back(feature); } + /// Adds |prop| to the list of properties that must be supported by the + /// engine. + void AddRequiredProperty(const std::string& prop) { + engine_info_.required_properties.push_back(prop); + } + /// Checks if |feature| is in required features bool IsRequiredFeature(const std::string& feature) const { return std::find(engine_info_.required_features.begin(), @@ -173,6 +184,13 @@ class Script : public RecipeImpl { feature) != engine_info_.required_features.end(); } + /// Checks if |prop| is in required features + bool IsRequiredProperty(const std::string& prop) const { + return std::find(engine_info_.required_properties.begin(), + engine_info_.required_properties.end(), + prop) != engine_info_.required_properties.end(); + } + /// Adds |ext| to the list of device extensions that must be supported. void AddRequiredDeviceExtension(const std::string& ext) { engine_info_.required_device_extensions.push_back(ext); @@ -257,6 +275,7 @@ class Script : public RecipeImpl { private: struct { std::vector required_features; + std::vector required_properties; std::vector required_device_extensions; std::vector required_instance_extensions; } engine_info_; diff --git a/src/vulkan/device.cc b/src/vulkan/device.cc index 43a1d8b1..a45be690 100644 --- a/src/vulkan/device.cc +++ b/src/vulkan/device.cc @@ -447,9 +447,11 @@ Result Device::Initialize( PFN_vkGetInstanceProcAddr getInstanceProcAddr, Delegate* delegate, const std::vector& required_features, + const std::vector& required_properties, const std::vector& required_device_extensions, const VkPhysicalDeviceFeatures& available_features, const VkPhysicalDeviceFeatures2KHR& available_features2, + const VkPhysicalDeviceProperties2KHR& available_properties2, const std::vector& available_extensions) { Result r = LoadVulkanPointers(getInstanceProcAddr, delegate); if (!r.IsSuccess()) @@ -726,6 +728,69 @@ Result Device::Initialize( "required extensions"); } + VkPhysicalDeviceVulkan12Properties* pv12 = nullptr; + VkPhysicalDeviceFloatControlsProperties* pfc = nullptr; + + ptr = available_properties2.pNext; + while (ptr != nullptr) { + BaseOutStructure* s = static_cast(ptr); + switch (s->sType) { + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_PROPERTIES: + pv12 = static_cast(ptr); + break; + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FLOAT_CONTROLS_PROPERTIES_KHR: + pfc = static_cast(ptr); + break; + default: + break; + } + ptr = s->pNext; + } + +#define CHK_P(R, P, NAME, S1, S2) \ + do { \ + if (R == -1 && P == #NAME) \ + R = ((S1 && S1->NAME) || (S2 && S2->NAME)) ? 1 : 0; \ + } while (false) + + for (const std::string& prop : required_properties) { + const size_t dot_pos = prop.find('.'); + const size_t dot_found = dot_pos != std::string::npos; + const std::string prefix = dot_found ? prop.substr(0, dot_pos) : ""; + const std::string name = dot_found ? prop.substr(dot_pos + 1) : prop; + int supported = -1; + + if (supported == -1 && prefix == "FloatControlsProperties") { + if (pfc == nullptr && pv12 == nullptr) + return Result( + "Vulkan: Device::Initialize given physical device does not support " + "required float control properties"); + + CHK_P(supported, name, shaderSignedZeroInfNanPreserveFloat16, pfc, pv12); + CHK_P(supported, name, shaderSignedZeroInfNanPreserveFloat32, pfc, pv12); + CHK_P(supported, name, shaderSignedZeroInfNanPreserveFloat64, pfc, pv12); + CHK_P(supported, name, shaderDenormPreserveFloat16, pfc, pv12); + CHK_P(supported, name, shaderDenormPreserveFloat32, pfc, pv12); + CHK_P(supported, name, shaderDenormPreserveFloat64, pfc, pv12); + CHK_P(supported, name, shaderDenormFlushToZeroFloat16, pfc, pv12); + CHK_P(supported, name, shaderDenormFlushToZeroFloat32, pfc, pv12); + CHK_P(supported, name, shaderDenormFlushToZeroFloat64, pfc, pv12); + CHK_P(supported, name, shaderRoundingModeRTEFloat16, pfc, pv12); + CHK_P(supported, name, shaderRoundingModeRTEFloat32, pfc, pv12); + CHK_P(supported, name, shaderRoundingModeRTEFloat64, pfc, pv12); + CHK_P(supported, name, shaderRoundingModeRTZFloat16, pfc, pv12); + CHK_P(supported, name, shaderRoundingModeRTZFloat32, pfc, pv12); + CHK_P(supported, name, shaderRoundingModeRTZFloat64, pfc, pv12); + } + + if (supported == 0) + return Result("Vulkan: Device::Initialize missing " + prop + " property"); + + if (supported == -1) + return Result( + "Vulkan: Device::Initialize property not handled " + prop); + } + ptrs_.vkGetPhysicalDeviceMemoryProperties(physical_device_, &physical_memory_properties_); diff --git a/src/vulkan/device.h b/src/vulkan/device.h index ff76c0fa..7298151b 100644 --- a/src/vulkan/device.h +++ b/src/vulkan/device.h @@ -47,9 +47,11 @@ class Device { Result Initialize(PFN_vkGetInstanceProcAddr getInstanceProcAddr, Delegate* delegate, const std::vector& required_features, + const std::vector& required_properties, const std::vector& required_device_extensions, const VkPhysicalDeviceFeatures& available_features, const VkPhysicalDeviceFeatures2KHR& available_features2, + const VkPhysicalDeviceProperties2KHR& available_properties2, const std::vector& available_extensions); /// Returns true if |format| and the |buffer|s buffer type combination is diff --git a/src/vulkan/engine_vulkan.cc b/src/vulkan/engine_vulkan.cc index b0842eca..8f481d56 100644 --- a/src/vulkan/engine_vulkan.cc +++ b/src/vulkan/engine_vulkan.cc @@ -92,6 +92,7 @@ Result EngineVulkan::Initialize( EngineConfig* config, Delegate* delegate, const std::vector& features, + const std::vector& properties, const std::vector& instance_extensions, const std::vector& device_extensions) { if (device_) @@ -118,8 +119,9 @@ Result EngineVulkan::Initialize( vk_config->queue); Result r = device_->Initialize( - vk_config->vkGetInstanceProcAddr, delegate, features, device_extensions, - vk_config->available_features, vk_config->available_features2, + vk_config->vkGetInstanceProcAddr, delegate, features, properties, + device_extensions, vk_config->available_features, + vk_config->available_features2, vk_config->available_properties2, vk_config->available_device_extensions); if (!r.IsSuccess()) return r; diff --git a/src/vulkan/engine_vulkan.h b/src/vulkan/engine_vulkan.h index 76668fb8..427e84f0 100644 --- a/src/vulkan/engine_vulkan.h +++ b/src/vulkan/engine_vulkan.h @@ -45,6 +45,7 @@ class EngineVulkan : public Engine { Result Initialize(EngineConfig* config, Delegate* delegate, const std::vector& features, + const std::vector& properties, const std::vector& instance_extensions, const std::vector& device_extensions) override; Result CreatePipeline(amber::Pipeline* type) override;