diff --git a/Source/Core/VideoCommon/Assets/CustomAssetLibrary.h b/Source/Core/VideoCommon/Assets/CustomAssetLibrary.h index 2ab8da408c8..10498618c6d 100644 --- a/Source/Core/VideoCommon/Assets/CustomAssetLibrary.h +++ b/Source/Core/VideoCommon/Assets/CustomAssetLibrary.h @@ -3,9 +3,6 @@ #pragma once -#include -#include -#include #include namespace VideoCommon @@ -13,7 +10,7 @@ namespace VideoCommon class CustomTextureData; struct MaterialData; struct MeshData; -struct PixelShaderData; +struct RasterSurfaceShaderData; struct TextureAndSamplerData; // This class provides functionality to load @@ -38,8 +35,9 @@ public: // Loads a texture, if there are no levels, bytes loaded will be empty virtual LoadInfo LoadTexture(const AssetID& asset_id, CustomTextureData* data) = 0; - // Loads a pixel shader - virtual LoadInfo LoadPixelShader(const AssetID& asset_id, PixelShaderData* data) = 0; + // Loads a raster surface shader + virtual LoadInfo LoadRasterSurfaceShader(const AssetID& asset_id, + RasterSurfaceShaderData* data) = 0; // Loads a material virtual LoadInfo LoadMaterial(const AssetID& asset_id, MaterialData* data) = 0; diff --git a/Source/Core/VideoCommon/Assets/DirectFilesystemAssetLibrary.cpp b/Source/Core/VideoCommon/Assets/DirectFilesystemAssetLibrary.cpp index 79e09bffa81..5e400f3de50 100644 --- a/Source/Core/VideoCommon/Assets/DirectFilesystemAssetLibrary.cpp +++ b/Source/Core/VideoCommon/Assets/DirectFilesystemAssetLibrary.cpp @@ -40,29 +40,37 @@ std::size_t GetAssetSize(const CustomTextureData& data) } } // namespace -CustomAssetLibrary::LoadInfo DirectFilesystemAssetLibrary::LoadPixelShader(const AssetID& asset_id, - PixelShaderData* data) +CustomAssetLibrary::LoadInfo +DirectFilesystemAssetLibrary::LoadRasterSurfaceShader(const AssetID& asset_id, + RasterSurfaceShaderData* data) { const auto asset_map = GetAssetMapForID(asset_id); // Asset map for a pixel shader is the shader and some metadata - if (asset_map.size() != 2) + if (asset_map.size() != 3) { - ERROR_LOG_FMT(VIDEO, "Asset '{}' expected to have two files mapped!", asset_id); + ERROR_LOG_FMT(VIDEO, "Asset '{}' expected to have three files mapped!", asset_id); return {}; } const auto metadata = asset_map.find("metadata"); - const auto shader = asset_map.find("shader"); if (metadata == asset_map.end()) { ERROR_LOG_FMT(VIDEO, "Asset '{}' expected to have a metadata entry mapped!", asset_id); return {}; } - if (shader == asset_map.end()) + const auto vertex_shader = asset_map.find("vertex_shader"); + if (vertex_shader == asset_map.end()) { - ERROR_LOG_FMT(VIDEO, "Asset '{}' expected to have a shader entry mapped!", asset_id); + ERROR_LOG_FMT(VIDEO, "Asset '{}' expected to have a vertex shader entry mapped!", asset_id); + return {}; + } + + const auto pixel_shader = asset_map.find("pixel_shader"); + if (pixel_shader == asset_map.end()) + { + ERROR_LOG_FMT(VIDEO, "Asset '{}' expected to have a pixel shader entry mapped!", asset_id); return {}; } @@ -78,24 +86,43 @@ CustomAssetLibrary::LoadInfo DirectFilesystemAssetLibrary::LoadPixelShader(const return {}; } } - std::size_t shader_size; + std::size_t vertex_shader_size; { std::error_code ec; - shader_size = std::filesystem::file_size(shader->second, ec); + vertex_shader_size = std::filesystem::file_size(vertex_shader->second, ec); if (ec) { - ERROR_LOG_FMT(VIDEO, - "Asset '{}' error - failed to get shader source file size with error '{}'!", - asset_id, ec); + ERROR_LOG_FMT( + VIDEO, "Asset '{}' error - failed to get vertex shader source file size with error '{}'!", + asset_id, ec); return {}; } } - const auto approx_mem_size = metadata_size + shader_size; - - if (!File::ReadFileToString(PathToString(shader->second), data->m_shader_source)) + std::size_t pixel_shader_size; { - ERROR_LOG_FMT(VIDEO, "Asset '{}' error - failed to load the shader file '{}',", asset_id, - PathToString(shader->second)); + std::error_code ec; + pixel_shader_size = std::filesystem::file_size(pixel_shader->second, ec); + if (ec) + { + ERROR_LOG_FMT( + VIDEO, "Asset '{}' error - failed to get pixel shader source file size with error '{}'!", + asset_id, ec); + return {}; + } + } + const auto approx_mem_size = metadata_size + vertex_shader_size + pixel_shader_size; + + if (!File::ReadFileToString(PathToString(vertex_shader->second), data->vertex_source)) + { + ERROR_LOG_FMT(VIDEO, "Asset '{}' error - failed to load the vertex shader file '{}',", + asset_id, PathToString(vertex_shader->second)); + return {}; + } + + if (!File::ReadFileToString(PathToString(pixel_shader->second), data->pixel_source)) + { + ERROR_LOG_FMT(VIDEO, "Asset '{}' error - failed to load the pixel shader file '{}',", asset_id, + PathToString(pixel_shader->second)); return {}; } @@ -120,7 +147,7 @@ CustomAssetLibrary::LoadInfo DirectFilesystemAssetLibrary::LoadPixelShader(const const auto& root_obj = root.get(); - if (!PixelShaderData::FromJson(asset_id, root_obj, data)) + if (!RasterSurfaceShaderData::FromJson(asset_id, root_obj, data)) return {}; return LoadInfo{approx_mem_size}; diff --git a/Source/Core/VideoCommon/Assets/DirectFilesystemAssetLibrary.h b/Source/Core/VideoCommon/Assets/DirectFilesystemAssetLibrary.h index e3a8e813344..3cc20984013 100644 --- a/Source/Core/VideoCommon/Assets/DirectFilesystemAssetLibrary.h +++ b/Source/Core/VideoCommon/Assets/DirectFilesystemAssetLibrary.h @@ -22,7 +22,7 @@ class DirectFilesystemAssetLibrary final : public WatchableFilesystemAssetLibrar public: LoadInfo LoadTexture(const AssetID& asset_id, TextureAndSamplerData* data) override; LoadInfo LoadTexture(const AssetID& asset_id, CustomTextureData* data) override; - LoadInfo LoadPixelShader(const AssetID& asset_id, PixelShaderData* data) override; + LoadInfo LoadRasterSurfaceShader(const AssetID& asset_id, RasterSurfaceShaderData* data) override; LoadInfo LoadMaterial(const AssetID& asset_id, MaterialData* data) override; LoadInfo LoadMesh(const AssetID& asset_id, MeshData* data) override; diff --git a/Source/Core/VideoCommon/Assets/ShaderAsset.cpp b/Source/Core/VideoCommon/Assets/ShaderAsset.cpp index 50a4a8c95de..cd5431e43b3 100644 --- a/Source/Core/VideoCommon/Assets/ShaderAsset.cpp +++ b/Source/Core/VideoCommon/Assets/ShaderAsset.cpp @@ -11,6 +11,7 @@ #include "Common/StringUtil.h" #include "Common/VariantUtil.h" #include "VideoCommon/Assets/CustomAssetLibrary.h" +#include "VideoCommon/ShaderGenCommon.h" namespace VideoCommon { @@ -134,36 +135,6 @@ static bool ParseShaderValue(const CustomAssetLibrary::AssetID& asset_id, return true; } } - else if (type == "sampler2d") - { - if (json_value.is()) - { - ShaderProperty::Sampler2D sampler2d; - sampler2d.value = json_value.get(); - *value = std::move(sampler2d); - return true; - } - } - else if (type == "sampler2darray") - { - if (json_value.is()) - { - ShaderProperty::Sampler2DArray sampler2darray; - sampler2darray.value = json_value.get(); - *value = std::move(sampler2darray); - return true; - } - } - else if (type == "samplercube") - { - if (json_value.is()) - { - ShaderProperty::SamplerCube samplercube; - samplercube.value = json_value.get(); - *value = std::move(samplercube); - return true; - } - } ERROR_LOG_FMT(VIDEO, "Asset '{}' failed to parse the json, value is not valid for type '{}'", asset_id, type); @@ -223,7 +194,7 @@ ParseShaderProperties(const VideoCommon::CustomAssetLibrary::AssetID& asset_id, asset_id); return false; } - property.m_description = description_iter->second.to_str(); + property.description = description_iter->second.to_str(); const auto code_name_iter = property_data_obj.find("code_name"); if (code_name_iter == property_data_obj.end()) @@ -245,11 +216,16 @@ ParseShaderProperties(const VideoCommon::CustomAssetLibrary::AssetID& asset_id, const auto default_iter = property_data_obj.find("default"); if (default_iter != property_data_obj.end()) { - if (!ParseShaderValue(asset_id, default_iter->second, code_name, type, &property.m_default)) + if (!ParseShaderValue(asset_id, default_iter->second, code_name, type, + &property.default_value)) { return false; } } + else + { + property.default_value = ShaderProperty::GetDefaultValueFromTypeName(type); + } shader_properties->try_emplace(std::move(code_name), std::move(property)); } @@ -257,63 +233,124 @@ ParseShaderProperties(const VideoCommon::CustomAssetLibrary::AssetID& asset_id, return true; } -bool PixelShaderData::FromJson(const VideoCommon::CustomAssetLibrary::AssetID& asset_id, - const picojson::object& json, PixelShaderData* data) +bool RasterSurfaceShaderData::FromJson(const VideoCommon::CustomAssetLibrary::AssetID& asset_id, + const picojson::object& json, RasterSurfaceShaderData* data) { - const auto properties_iter = json.find("properties"); - if (properties_iter == json.end()) - { - ERROR_LOG_FMT(VIDEO, "Asset '{}' failed to parse json, 'properties' not found", asset_id); - return false; - } - if (!properties_iter->second.is()) - { - ERROR_LOG_FMT(VIDEO, "Asset '{}' failed to parse json, 'properties' is not the right json type", - asset_id); - return false; - } - const auto& properties_array = properties_iter->second.get(); - if (!ParseShaderProperties(asset_id, properties_array, &data->m_properties)) - return false; - - for (const auto& [name, property] : data->m_properties) - { - if (data->m_shader_source.find(name) == std::string::npos) + const auto parse_properties = [&](const char* name, std::string_view source, + std::map* properties) -> bool { + const auto properties_iter = json.find(name); + if (properties_iter == json.end()) { - ERROR_LOG_FMT( - VIDEO, - "Asset '{}' failed to parse json, the code name '{}' defined in the metadata was not " - "found in the shader source", - asset_id, name); + ERROR_LOG_FMT(VIDEO, "Asset '{}' failed to parse json, '{}' not found", asset_id, name); return false; } - } + if (!properties_iter->second.is()) + { + ERROR_LOG_FMT(VIDEO, "Asset '{}' failed to parse json, '{}' is not the right json type", + asset_id, name); + return false; + } + const auto& properties_array = properties_iter->second.get(); + + if (!ParseShaderProperties(asset_id, properties_array, properties)) + return false; + + for (const auto& [code_name, property] : *properties) + { + if (source.find(code_name) == std::string::npos) + { + ERROR_LOG_FMT( + VIDEO, + "Asset '{}' failed to parse json, the code name '{}' defined in the metadata was not " + "found in the source for '{}'", + asset_id, code_name, name); + return false; + } + } + + return true; + }; + + const auto parse_samplers = + [&](const char* name, + std::vector* samplers) -> bool { + const auto samplers_iter = json.find(name); + if (samplers_iter == json.end()) + { + ERROR_LOG_FMT(VIDEO, "Asset '{}' failed to parse json, '{}' not found", asset_id, name); + return false; + } + if (!samplers_iter->second.is()) + { + ERROR_LOG_FMT(VIDEO, "Asset '{}' failed to parse json, '{}' is not the right json type", + asset_id, name); + return false; + } + const auto& samplers_array = samplers_iter->second.get(); + if (!std::ranges::all_of(samplers_array, [](const picojson::value& json_data) { + return json_data.is(); + })) + { + ERROR_LOG_FMT(VIDEO, "Asset '{}' failed to parse json, '{}' must contain objects", asset_id, + name); + return false; + } + + for (const auto& sampler_json : samplers_array) + { + auto& sampler_json_obj = sampler_json.get(); + + SamplerData sampler; + + if (const auto sampler_name = ReadStringFromJson(sampler_json_obj, "name")) + { + sampler.name = *sampler_name; + } + else + { + ERROR_LOG_FMT(VIDEO, + "Asset '{}' failed to parse sampler json, 'name' not found or wrong type", + asset_id); + return false; + } + + if (const auto sampler_type = + ReadNumericFromJson(sampler_json_obj, "type")) + { + sampler.type = *sampler_type; + } + else + { + ERROR_LOG_FMT(VIDEO, + "Asset '{}' failed to parse sampler json, 'type' not found or wrong type", + asset_id); + return false; + } + samplers->push_back(std::move(sampler)); + } + + return true; + }; + + if (!parse_properties("properties", data->pixel_source, &data->uniform_properties)) + return false; + + if (!parse_samplers("samplers", &data->samplers)) + return false; return true; } -void PixelShaderData::ToJson(picojson::object& obj, const PixelShaderData& data) +void RasterSurfaceShaderData::ToJson(picojson::object& obj, const RasterSurfaceShaderData& data) { - picojson::array json_properties; - for (const auto& [name, property] : data.m_properties) - { + const auto add_property = [](picojson::array* json_properties, const std::string& name, + const ShaderProperty& property) { picojson::object json_property; - json_property.emplace("code_name", name); - json_property.emplace("description", property.m_description); - std::visit(overloaded{[&](const ShaderProperty::Sampler2D& default_value) { - json_property.emplace("type", "sampler2d"); - json_property.emplace("default", default_value.value); - }, - [&](const ShaderProperty::Sampler2DArray& default_value) { - json_property.emplace("type", "sampler2darray"); - json_property.emplace("default", default_value.value); - }, - [&](const ShaderProperty::SamplerCube& default_value) { - json_property.emplace("type", "samplercube"); - json_property.emplace("default", default_value.value); - }, - [&](s32 default_value) { + json_property.emplace("code_name", name); + json_property.emplace("description", property.description); + + std::visit(overloaded{[&](s32 default_value) { json_property.emplace("type", "int"); json_property.emplace("default", static_cast(default_value)); }, @@ -357,37 +394,43 @@ void PixelShaderData::ToJson(picojson::object& obj, const PixelShaderData& data) json_property.emplace("type", "bool"); json_property.emplace("default", default_value); }}, - property.m_default); + property.default_value); - json_properties.emplace_back(std::move(json_property)); + json_properties->emplace_back(std::move(json_property)); + }; + + const auto add_sampler = [](picojson::array* json_samplers, const SamplerData& sampler) { + picojson::object json_sampler; + json_sampler.emplace("name", sampler.name); + json_sampler.emplace("type", static_cast(sampler.type)); + json_samplers->emplace_back(std::move(json_sampler)); + }; + + picojson::array json_properties; + for (const auto& [name, property] : data.uniform_properties) + { + add_property(&json_properties, name, property); } - obj.emplace("properties", std::move(json_properties)); + + picojson::array json_samplers; + for (const auto& sampler : data.samplers) + { + add_sampler(&json_samplers, sampler); + } + obj.emplace("samplers", json_samplers); } std::span ShaderProperty::GetValueTypeNames() { - static constexpr std::array values = { - "sampler2d", "sampler2darray", "samplercube", "int", "int2", "int3", "int4", - "float", "float2", "float3", "float4", "rgb", "rgba", "bool"}; + static constexpr std::array values = { + "int", "int2", "int3", "int4", "float", "float2", "float3", "float4", "rgb", "rgba", "bool"}; return values; } ShaderProperty::Value ShaderProperty::GetDefaultValueFromTypeName(std::string_view name) { - if (name == "sampler2d") - { - return Sampler2D{}; - } - else if (name == "sampler2darray") - { - return Sampler2DArray{}; - } - else if (name == "samplercube") - { - return SamplerCube{}; - } - else if (name == "int") + if (name == "int") { return 0; } @@ -435,10 +478,44 @@ ShaderProperty::Value ShaderProperty::GetDefaultValueFromTypeName(std::string_vi return Value{}; } -CustomAssetLibrary::LoadInfo PixelShaderAsset::LoadImpl(const CustomAssetLibrary::AssetID& asset_id) +void ShaderProperty::WriteAsShaderCode(ShaderCode& shader_source, std::string_view name, + const ShaderProperty& property) { - auto potential_data = std::make_shared(); - const auto loaded_info = m_owning_library->LoadPixelShader(asset_id, potential_data.get()); + const auto write_shader = [&](std::string_view type, u32 element_count) { + if (element_count == 1) + { + shader_source.Write("{} {};\n", type, name); + } + else + { + shader_source.Write("{}{} {};\n", type, element_count, name); + } + + for (std::size_t i = element_count; i < 4; i++) + { + shader_source.Write("{} {}_padding_{};\n", type, name, i + 1); + } + }; + std::visit(overloaded{[&](s32) { write_shader("int", 1); }, + [&](const std::array&) { write_shader("int", 2); }, + [&](const std::array&) { write_shader("int", 3); }, + [&](const std::array&) { write_shader("int", 4); }, + [&](float) { write_shader("float", 1); }, + [&](const std::array&) { write_shader("float", 2); }, + [&](const std::array&) { write_shader("float", 3); }, + [&](const std::array&) { write_shader("float", 4); }, + [&](const ShaderProperty::RGB&) { write_shader("float", 3); }, + [&](const ShaderProperty::RGBA&) { write_shader("float", 4); }, + [&](bool) { write_shader("bool", 1); }}, + property.default_value); +} + +CustomAssetLibrary::LoadInfo +RasterSurfaceShaderAsset::LoadImpl(const CustomAssetLibrary::AssetID& asset_id) +{ + auto potential_data = std::make_shared(); + const auto loaded_info = + m_owning_library->LoadRasterSurfaceShader(asset_id, potential_data.get()); if (loaded_info.bytes_loaded == 0) return {}; { diff --git a/Source/Core/VideoCommon/Assets/ShaderAsset.h b/Source/Core/VideoCommon/Assets/ShaderAsset.h index 6a9c100d444..6637b967fce 100644 --- a/Source/Core/VideoCommon/Assets/ShaderAsset.h +++ b/Source/Core/VideoCommon/Assets/ShaderAsset.h @@ -13,6 +13,9 @@ #include #include "VideoCommon/Assets/CustomAsset.h" +#include "VideoCommon/TextureConfig.h" + +class ShaderCode; namespace VideoCommon { @@ -28,45 +31,43 @@ struct ShaderProperty std::array value; }; - struct Sampler2D - { - CustomAssetLibrary::AssetID value; - }; - - struct Sampler2DArray - { - CustomAssetLibrary::AssetID value; - }; - - struct SamplerCube - { - CustomAssetLibrary::AssetID value; - }; - using Value = std::variant, std::array, std::array, float, std::array, std::array, std::array, bool, - RGB, RGBA, Sampler2D, Sampler2DArray, SamplerCube>; + RGB, RGBA>; static std::span GetValueTypeNames(); static Value GetDefaultValueFromTypeName(std::string_view name); + static void WriteAsShaderCode(ShaderCode& shader_source, std::string_view name, + const ShaderProperty& property); - Value m_default; - std::string m_description; + Value default_value; + std::string description; }; -struct PixelShaderData + +struct RasterSurfaceShaderData { static bool FromJson(const CustomAssetLibrary::AssetID& asset_id, const picojson::object& json, - PixelShaderData* data); - static void ToJson(picojson::object& obj, const PixelShaderData& data); + RasterSurfaceShaderData* data); + static void ToJson(picojson::object& obj, const RasterSurfaceShaderData& data); // These shader properties describe the input that the // shader expects to expose. The key is text // expected to be in the shader code and the propery // describes various details about the input - std::map m_properties; - std::string m_shader_source; + std::map uniform_properties; + std::string vertex_source; + std::string pixel_source; + + struct SamplerData + { + AbstractTextureType type; + std::string name; + + bool operator==(const SamplerData&) const = default; + }; + std::vector samplers; }; -class PixelShaderAsset final : public CustomLoadableAsset +class RasterSurfaceShaderAsset final : public CustomLoadableAsset { public: using CustomLoadableAsset::CustomLoadableAsset; diff --git a/Source/Core/VideoCommon/GraphicsModSystem/Runtime/CustomPipeline.h b/Source/Core/VideoCommon/GraphicsModSystem/Runtime/CustomPipeline.h index 3a904e08589..7165c9291fd 100644 --- a/Source/Core/VideoCommon/GraphicsModSystem/Runtime/CustomPipeline.h +++ b/Source/Core/VideoCommon/GraphicsModSystem/Runtime/CustomPipeline.h @@ -22,21 +22,4 @@ struct CustomPipeline void UpdatePixelData(std::shared_ptr library, std::span texture_units, const VideoCommon::CustomAssetLibrary::AssetID& material_to_load); - - VideoCommon::CachedAsset m_pixel_material; - VideoCommon::CachedAsset m_pixel_shader; - - struct CachedTextureAsset - { - VideoCommon::CachedAsset m_cached_asset; - std::unique_ptr m_texture; - std::string m_sampler_code; - std::string m_define_code; - }; - std::vector> m_game_textures; - - ShaderCode m_last_generated_shader_code; - ShaderCode m_last_generated_material_code; - - std::vector m_material_data; };