From e204b3c8ee46653ca1c9d2aea6877ec3d11f7472 Mon Sep 17 00:00:00 2001 From: iwubcode Date: Sun, 15 Oct 2023 16:34:06 -0500 Subject: [PATCH] VideoCommon: add additional data types (int, int2, float, bool, etc) as properties of materials and some helper functions to support sending the data to the GPU --- .../Core/VideoCommon/Assets/MaterialAsset.cpp | 225 ++++++++++++++++-- .../Core/VideoCommon/Assets/MaterialAsset.h | 27 +-- .../Runtime/Actions/CustomPipelineAction.cpp | 20 +- 3 files changed, 219 insertions(+), 53 deletions(-) diff --git a/Source/Core/VideoCommon/Assets/MaterialAsset.cpp b/Source/Core/VideoCommon/Assets/MaterialAsset.cpp index c5e5718492..b754683f2a 100644 --- a/Source/Core/VideoCommon/Assets/MaterialAsset.cpp +++ b/Source/Core/VideoCommon/Assets/MaterialAsset.cpp @@ -3,32 +3,137 @@ #include "VideoCommon/Assets/MaterialAsset.h" +#include +#include #include #include "Common/Logging/Log.h" #include "Common/StringUtil.h" +#include "Common/VariantUtil.h" #include "VideoCommon/Assets/CustomAssetLibrary.h" +#include "VideoCommon/ShaderGenCommon.h" namespace VideoCommon { namespace { -bool ParsePropertyValue(const CustomAssetLibrary::AssetID& asset_id, MaterialProperty::Type type, - const picojson::value& json_value, - std::optional* value) +// While not optimal, we pad our data to match std140 shader requirements +// this memory constant indicates the memory stride for a single uniform +// regardless of data type +constexpr std::size_t MemorySize = sizeof(float) * 4; + +template +bool ParseNumeric(const CustomAssetLibrary::AssetID& asset_id, const picojson::value& json_value, + std::string_view code_name, MaterialProperty::Value* value) { - switch (type) + static_assert(ElementCount <= 4, "Numeric data expected to be four elements or less"); + if constexpr (ElementCount == 1) { - case MaterialProperty::Type::Type_TextureAsset: - { - if (json_value.is()) + if (!json_value.is()) { - *value = json_value.to_str(); + ERROR_LOG_FMT(VIDEO, + "Asset id '{}' material has attribute '{}' where " + "a double was expected but not provided.", + asset_id, code_name); + return false; + } + + *value = static_cast(json_value.get()); + } + else + { + if (!json_value.is()) + { + ERROR_LOG_FMT(VIDEO, + "Asset id '{}' material has attribute '{}' where " + "an array was expected but not provided.", + asset_id, code_name); + return false; + } + + const auto json_data = json_value.get(); + + if (json_data.size() != ElementCount) + { + ERROR_LOG_FMT(VIDEO, + "Asset id '{}' material has attribute '{}' with incorrect number " + "of elements, expected {}", + asset_id, code_name, ElementCount); + return false; + } + + if (!std::all_of(json_data.begin(), json_data.end(), + [](const picojson::value& v) { return v.is(); })) + { + ERROR_LOG_FMT(VIDEO, + "Asset id '{}' material has attribute '{}' where " + "all elements are not of type double.", + asset_id, code_name); + return false; + } + + std::array data; + for (std::size_t i = 0; i < ElementCount; i++) + { + data[i] = static_cast(json_data[i].get()); + } + *value = std::move(data); + } + + return true; +} +bool ParsePropertyValue(const CustomAssetLibrary::AssetID& asset_id, + const picojson::value& json_value, std::string_view code_name, + std::string_view type, MaterialProperty::Value* value) +{ + if (type == "int") + { + return ParseNumeric(asset_id, json_value, code_name, value); + } + else if (type == "int2") + { + return ParseNumeric(asset_id, json_value, code_name, value); + } + else if (type == "int3") + { + return ParseNumeric(asset_id, json_value, code_name, value); + } + else if (type == "int4") + { + return ParseNumeric(asset_id, json_value, code_name, value); + } + else if (type == "float") + { + return ParseNumeric(asset_id, json_value, code_name, value); + } + else if (type == "float2") + { + return ParseNumeric(asset_id, json_value, code_name, value); + } + else if (type == "float3") + { + return ParseNumeric(asset_id, json_value, code_name, value); + } + else if (type == "float4") + { + return ParseNumeric(asset_id, json_value, code_name, value); + } + else if (type == "bool") + { + if (json_value.is()) + { + *value = json_value.get(); + return true; + } + } + else if (type == "texture_asset") + { + if (json_value.is()) + { + *value = json_value.get(); return true; } } - break; - }; ERROR_LOG_FMT(VIDEO, "Asset '{}' failed to parse the json, value is not valid for type '{}'", asset_id, type); @@ -67,18 +172,6 @@ bool ParseMaterialProperties(const CustomAssetLibrary::AssetID& asset_id, } std::string type = type_iter->second.to_str(); Common::ToLower(&type); - if (type == "texture_asset") - { - property.m_type = MaterialProperty::Type::Type_TextureAsset; - } - else - { - ERROR_LOG_FMT(VIDEO, - "Asset '{}' failed to parse the json, value entry 'type' is " - "an invalid option", - asset_id); - return false; - } const auto code_name_iter = value_data_obj.find("code_name"); if (code_name_iter == value_data_obj.end()) @@ -102,8 +195,11 @@ bool ParseMaterialProperties(const CustomAssetLibrary::AssetID& asset_id, const auto value_iter = value_data_obj.find("value"); if (value_iter != value_data_obj.end()) { - if (!ParsePropertyValue(asset_id, property.m_type, value_iter->second, &property.m_value)) + if (!ParsePropertyValue(asset_id, value_iter->second, property.m_code_name, type, + &property.m_value)) + { return false; + } } material_property->push_back(std::move(property)); @@ -111,7 +207,90 @@ bool ParseMaterialProperties(const CustomAssetLibrary::AssetID& asset_id, return true; } + +template +picojson::array ArrayToPicoArray(const std::array& value) +{ + picojson::array result; + for (std::size_t i = 0; i < N; i++) + { + result.push_back(picojson::value{static_cast(value[i])}); + } + return result; +} } // namespace + +void MaterialProperty::WriteToMemory(u8*& buffer, const MaterialProperty& property) +{ + const auto write_memory = [&](const void* raw_value, std::size_t data_size) { + std::memcpy(buffer, raw_value, data_size); + std::memset(buffer + data_size, 0, MemorySize - data_size); + buffer += MemorySize; + }; + std::visit( + overloaded{ + [&](const CustomAssetLibrary::AssetID&) {}, + [&](s32 value) { write_memory(&value, sizeof(s32)); }, + [&](const std::array& value) { write_memory(value.data(), sizeof(s32) * 2); }, + [&](const std::array& value) { write_memory(value.data(), sizeof(s32) * 3); }, + [&](const std::array& value) { write_memory(value.data(), sizeof(s32) * 4); }, + [&](float value) { write_memory(&value, sizeof(float)); }, + [&](const std::array& value) { write_memory(value.data(), sizeof(float) * 2); }, + [&](const std::array& value) { write_memory(value.data(), sizeof(float) * 3); }, + [&](const std::array& value) { write_memory(value.data(), sizeof(float) * 4); }, + [&](bool value) { write_memory(&value, sizeof(bool)); }}, + property.m_value); +} + +std::size_t MaterialProperty::GetMemorySize(const MaterialProperty& property) +{ + std::size_t result = 0; + std::visit(overloaded{[&](const CustomAssetLibrary::AssetID&) {}, + [&](s32) { result = MemorySize; }, + [&](const std::array&) { result = MemorySize; }, + [&](const std::array&) { result = MemorySize; }, + [&](const std::array&) { result = MemorySize; }, + [&](float) { result = MemorySize; }, + [&](const std::array&) { result = MemorySize; }, + [&](const std::array&) { result = MemorySize; }, + [&](const std::array&) { result = MemorySize; }, + [&](bool) { result = MemorySize; }}, + property.m_value); + + return result; +} + +void MaterialProperty::WriteAsShaderCode(ShaderCode& shader_source, + const MaterialProperty& property) +{ + const auto write_shader = [&](std::string_view type, u32 element_count) { + if (element_count == 1) + { + shader_source.Write("{} {};\n", type, property.m_code_name); + } + else + { + shader_source.Write("{}{} {};\n", type, element_count, property.m_code_name); + } + + for (std::size_t i = element_count; i < 4; i++) + { + shader_source.Write("{} {}_padding_{};\n", type, property.m_code_name, i + 1); + } + }; + std::visit(overloaded{[&](const CustomAssetLibrary::AssetID&) {}, + [&](s32 value) { write_shader("int", 1); }, + [&](const std::array& value) { write_shader("int", 2); }, + [&](const std::array& value) { write_shader("int", 3); }, + [&](const std::array& value) { write_shader("int", 4); }, + [&](float value) { write_shader("float", 1); }, + [&](const std::array& value) { write_shader("float", 2); }, + [&](const std::array& value) { write_shader("float", 3); }, + [&](const std::array& value) { write_shader("float", 4); }, + [&](bool value) { write_shader("bool", 1); }}, + property.m_value); +} + bool MaterialData::FromJson(const CustomAssetLibrary::AssetID& asset_id, const picojson::object& json, MaterialData* data) { diff --git a/Source/Core/VideoCommon/Assets/MaterialAsset.h b/Source/Core/VideoCommon/Assets/MaterialAsset.h index 498d9dae0f..58731cd02f 100644 --- a/Source/Core/VideoCommon/Assets/MaterialAsset.h +++ b/Source/Core/VideoCommon/Assets/MaterialAsset.h @@ -3,7 +3,7 @@ #pragma once -#include +#include #include #include #include @@ -14,20 +14,20 @@ #include "Common/EnumFormatter.h" #include "VideoCommon/Assets/CustomAsset.h" +class ShaderCode; + namespace VideoCommon { struct MaterialProperty { - using Value = std::variant; - enum class Type - { - Type_Undefined, - Type_TextureAsset, - Type_Max = Type_TextureAsset - }; + static void WriteToMemory(u8*& buffer, const MaterialProperty& property); + static std::size_t GetMemorySize(const MaterialProperty& property); + static void WriteAsShaderCode(ShaderCode& shader_source, const MaterialProperty& property); + using Value = std::variant, + std::array, std::array, float, std::array, + std::array, std::array, bool>; std::string m_code_name; - Type m_type = Type::Type_Undefined; - std::optional m_value; + Value m_value; }; struct MaterialData @@ -52,10 +52,3 @@ private: }; } // namespace VideoCommon - -template <> -struct fmt::formatter - : EnumFormatter -{ - constexpr formatter() : EnumFormatter({"Undefined", "Texture"}) {} -}; diff --git a/Source/Core/VideoCommon/GraphicsModSystem/Runtime/Actions/CustomPipelineAction.cpp b/Source/Core/VideoCommon/GraphicsModSystem/Runtime/Actions/CustomPipelineAction.cpp index 267f88bb4c..0a51bb995e 100644 --- a/Source/Core/VideoCommon/GraphicsModSystem/Runtime/Actions/CustomPipelineAction.cpp +++ b/Source/Core/VideoCommon/GraphicsModSystem/Runtime/Actions/CustomPipelineAction.cpp @@ -393,21 +393,15 @@ void CustomPipelineAction::OnTextureCreate(GraphicsModActionData::TextureCreate* return; } - if (property.m_type == VideoCommon::MaterialProperty::Type::Type_TextureAsset) + if (auto* value = std::get_if(&property.m_value)) { - if (property.m_value) + auto asset = loader.LoadGameTexture(*value, m_library); + if (asset) { - if (auto* value = std::get_if(&*property.m_value)) - { - auto asset = loader.LoadGameTexture(*value, m_library); - if (asset) - { - const auto loaded_time = asset->GetLastLoadedTime(); - game_assets.push_back(VideoCommon::CachedAsset{ - std::move(asset), loaded_time}); - m_texture_code_names.push_back(property.m_code_name); - } - } + const auto loaded_time = asset->GetLastLoadedTime(); + game_assets.push_back( + VideoCommon::CachedAsset{std::move(asset), loaded_time}); + m_texture_code_names.push_back(property.m_code_name); } } }