summaryrefslogtreecommitdiff
path: root/Source/Core
diff options
context:
space:
mode:
authoriwubcode <iwubcode@users.noreply.github.com>2023-10-15 16:34:06 -0500
committeriwubcode <iwubcode@users.noreply.github.com>2023-12-04 00:12:31 -0600
commite204b3c8ee46653ca1c9d2aea6877ec3d11f7472 (patch)
treebbaa0f192c4e72843b06f45563fc930e7e2395c3 /Source/Core
parent96c256f97ad2729431380172d619fc2567b3341e (diff)
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
Diffstat (limited to 'Source/Core')
-rw-r--r--Source/Core/VideoCommon/Assets/MaterialAsset.cpp221
-rw-r--r--Source/Core/VideoCommon/Assets/MaterialAsset.h27
-rw-r--r--Source/Core/VideoCommon/GraphicsModSystem/Runtime/Actions/CustomPipelineAction.cpp20
3 files changed, 217 insertions, 51 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 <algorithm>
+#include <string_view>
#include <vector>
#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<MaterialProperty::Value>* 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 <typename ElementType, std::size_t ElementCount>
+bool ParseNumeric(const CustomAssetLibrary::AssetID& asset_id, const picojson::value& json_value,
+ std::string_view code_name, MaterialProperty::Value* value)
+{
+ static_assert(ElementCount <= 4, "Numeric data expected to be four elements or less");
+ if constexpr (ElementCount == 1)
+ {
+ if (!json_value.is<double>())
+ {
+ 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<ElementType>(json_value.get<double>());
+ }
+ else
+ {
+ if (!json_value.is<picojson::array>())
+ {
+ 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<picojson::array>();
+
+ 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<double>(); }))
+ {
+ 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<ElementType, ElementCount> data;
+ for (std::size_t i = 0; i < ElementCount; i++)
+ {
+ data[i] = static_cast<ElementType>(json_data[i].get<double>());
+ }
+ *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)
{
- switch (type)
+ if (type == "int")
+ {
+ return ParseNumeric<s32, 1>(asset_id, json_value, code_name, value);
+ }
+ else if (type == "int2")
+ {
+ return ParseNumeric<s32, 2>(asset_id, json_value, code_name, value);
+ }
+ else if (type == "int3")
{
- case MaterialProperty::Type::Type_TextureAsset:
+ return ParseNumeric<s32, 3>(asset_id, json_value, code_name, value);
+ }
+ else if (type == "int4")
+ {
+ return ParseNumeric<s32, 4>(asset_id, json_value, code_name, value);
+ }
+ else if (type == "float")
+ {
+ return ParseNumeric<float, 1>(asset_id, json_value, code_name, value);
+ }
+ else if (type == "float2")
+ {
+ return ParseNumeric<float, 2>(asset_id, json_value, code_name, value);
+ }
+ else if (type == "float3")
+ {
+ return ParseNumeric<float, 3>(asset_id, json_value, code_name, value);
+ }
+ else if (type == "float4")
+ {
+ return ParseNumeric<float, 4>(asset_id, json_value, code_name, value);
+ }
+ else if (type == "bool")
+ {
+ if (json_value.is<bool>())
+ {
+ *value = json_value.get<bool>();
+ return true;
+ }
+ }
+ else if (type == "texture_asset")
{
if (json_value.is<std::string>())
{
- *value = json_value.to_str();
+ *value = json_value.get<std::string>();
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 <typename T, std::size_t N>
+picojson::array ArrayToPicoArray(const std::array<T, N>& value)
+{
+ picojson::array result;
+ for (std::size_t i = 0; i < N; i++)
+ {
+ result.push_back(picojson::value{static_cast<double>(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<s32, 2>& value) { write_memory(value.data(), sizeof(s32) * 2); },
+ [&](const std::array<s32, 3>& value) { write_memory(value.data(), sizeof(s32) * 3); },
+ [&](const std::array<s32, 4>& value) { write_memory(value.data(), sizeof(s32) * 4); },
+ [&](float value) { write_memory(&value, sizeof(float)); },
+ [&](const std::array<float, 2>& value) { write_memory(value.data(), sizeof(float) * 2); },
+ [&](const std::array<float, 3>& value) { write_memory(value.data(), sizeof(float) * 3); },
+ [&](const std::array<float, 4>& 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<s32, 2>&) { result = MemorySize; },
+ [&](const std::array<s32, 3>&) { result = MemorySize; },
+ [&](const std::array<s32, 4>&) { result = MemorySize; },
+ [&](float) { result = MemorySize; },
+ [&](const std::array<float, 2>&) { result = MemorySize; },
+ [&](const std::array<float, 3>&) { result = MemorySize; },
+ [&](const std::array<float, 4>&) { 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<s32, 2>& value) { write_shader("int", 2); },
+ [&](const std::array<s32, 3>& value) { write_shader("int", 3); },
+ [&](const std::array<s32, 4>& value) { write_shader("int", 4); },
+ [&](float value) { write_shader("float", 1); },
+ [&](const std::array<float, 2>& value) { write_shader("float", 2); },
+ [&](const std::array<float, 3>& value) { write_shader("float", 3); },
+ [&](const std::array<float, 4>& 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 <optional>
+#include <array>
#include <string>
#include <variant>
#include <vector>
@@ -14,20 +14,20 @@
#include "Common/EnumFormatter.h"
#include "VideoCommon/Assets/CustomAsset.h"
+class ShaderCode;
+
namespace VideoCommon
{
struct MaterialProperty
{
- using Value = std::variant<std::string>;
- 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<CustomAssetLibrary::AssetID, s32, std::array<s32, 2>,
+ std::array<s32, 3>, std::array<s32, 4>, float, std::array<float, 2>,
+ std::array<float, 3>, std::array<float, 4>, bool>;
std::string m_code_name;
- Type m_type = Type::Type_Undefined;
- std::optional<Value> m_value;
+ Value m_value;
};
struct MaterialData
@@ -52,10 +52,3 @@ private:
};
} // namespace VideoCommon
-
-template <>
-struct fmt::formatter<VideoCommon::MaterialProperty::Type>
- : EnumFormatter<VideoCommon::MaterialProperty::Type::Type_Max>
-{
- 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<std::string>(&property.m_value))
{
- if (property.m_value)
+ auto asset = loader.LoadGameTexture(*value, m_library);
+ if (asset)
{
- if (auto* value = std::get_if<std::string>(&*property.m_value))
- {
- auto asset = loader.LoadGameTexture(*value, m_library);
- if (asset)
- {
- const auto loaded_time = asset->GetLastLoadedTime();
- game_assets.push_back(VideoCommon::CachedAsset<VideoCommon::GameTextureAsset>{
- 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<VideoCommon::GameTextureAsset>{std::move(asset), loaded_time});
+ m_texture_code_names.push_back(property.m_code_name);
}
}
}