mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-09-05 09:05:54 +00:00
Merge branch 'master' of https://github.com/dolphin-emu/dolphin
This commit is contained in:
commit
0af9a3ae8f
212 changed files with 50301 additions and 41718 deletions
|
@ -13,6 +13,7 @@
|
|||
#include "VideoCommon/Assets/MaterialAsset.h"
|
||||
#include "VideoCommon/Assets/ShaderAsset.h"
|
||||
#include "VideoCommon/Assets/TextureAsset.h"
|
||||
#include "VideoCommon/RenderState.h"
|
||||
|
||||
namespace VideoCommon
|
||||
{
|
||||
|
@ -289,6 +290,7 @@ CustomAssetLibrary::LoadInfo DirectFilesystemAssetLibrary::LoadTexture(const Ass
|
|||
}
|
||||
else
|
||||
{
|
||||
data->m_sampler = RenderState::GetLinearSamplerState();
|
||||
data->m_type = TextureData::Type::Type_Texture2D;
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include "Common/JsonUtil.h"
|
||||
#include "Common/Logging/Log.h"
|
||||
#include "Common/StringUtil.h"
|
||||
#include "Common/VariantUtil.h"
|
||||
|
@ -207,17 +208,6 @@ 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)
|
||||
|
@ -329,6 +319,68 @@ bool MaterialData::FromJson(const CustomAssetLibrary::AssetID& asset_id,
|
|||
return true;
|
||||
}
|
||||
|
||||
void MaterialData::ToJson(picojson::object* obj, const MaterialData& data)
|
||||
{
|
||||
if (!obj) [[unlikely]]
|
||||
return;
|
||||
|
||||
auto& json_obj = *obj;
|
||||
|
||||
picojson::array json_properties;
|
||||
for (const auto& property : data.properties)
|
||||
{
|
||||
picojson::object json_property;
|
||||
json_property["code_name"] = picojson::value{property.m_code_name};
|
||||
|
||||
std::visit(overloaded{[&](const CustomAssetLibrary::AssetID& value) {
|
||||
json_property["type"] = picojson::value{"texture_asset"};
|
||||
json_property["value"] = picojson::value{value};
|
||||
},
|
||||
[&](s32 value) {
|
||||
json_property["type"] = picojson::value{"int"};
|
||||
json_property["value"] = picojson::value{static_cast<double>(value)};
|
||||
},
|
||||
[&](const std::array<s32, 2>& value) {
|
||||
json_property["type"] = picojson::value{"int2"};
|
||||
json_property["value"] = picojson::value{ToJsonArray(value)};
|
||||
},
|
||||
[&](const std::array<s32, 3>& value) {
|
||||
json_property["type"] = picojson::value{"int3"};
|
||||
json_property["value"] = picojson::value{ToJsonArray(value)};
|
||||
},
|
||||
[&](const std::array<s32, 4>& value) {
|
||||
json_property["type"] = picojson::value{"int4"};
|
||||
json_property["value"] = picojson::value{ToJsonArray(value)};
|
||||
},
|
||||
[&](float value) {
|
||||
json_property["type"] = picojson::value{"float"};
|
||||
json_property["value"] = picojson::value{static_cast<double>(value)};
|
||||
},
|
||||
[&](const std::array<float, 2>& value) {
|
||||
json_property["type"] = picojson::value{"float2"};
|
||||
json_property["value"] = picojson::value{ToJsonArray(value)};
|
||||
},
|
||||
[&](const std::array<float, 3>& value) {
|
||||
json_property["type"] = picojson::value{"float3"};
|
||||
json_property["value"] = picojson::value{ToJsonArray(value)};
|
||||
},
|
||||
[&](const std::array<float, 4>& value) {
|
||||
json_property["type"] = picojson::value{"float4"};
|
||||
json_property["value"] = picojson::value{ToJsonArray(value)};
|
||||
},
|
||||
[&](bool value) {
|
||||
json_property["type"] = picojson::value{"bool"};
|
||||
json_property["value"] = picojson::value{value};
|
||||
}},
|
||||
property.m_value);
|
||||
|
||||
json_properties.emplace_back(std::move(json_property));
|
||||
}
|
||||
|
||||
json_obj["values"] = picojson::value{std::move(json_properties)};
|
||||
json_obj["shader_asset"] = picojson::value{data.shader_asset};
|
||||
}
|
||||
|
||||
CustomAssetLibrary::LoadInfo MaterialAsset::LoadImpl(const CustomAssetLibrary::AssetID& asset_id)
|
||||
{
|
||||
auto potential_data = std::make_shared<MaterialData>();
|
||||
|
|
|
@ -34,6 +34,7 @@ struct MaterialData
|
|||
{
|
||||
static bool FromJson(const CustomAssetLibrary::AssetID& asset_id, const picojson::object& json,
|
||||
MaterialData* data);
|
||||
static void ToJson(picojson::object* obj, const MaterialData& data);
|
||||
std::string shader_asset;
|
||||
std::vector<MaterialProperty> properties;
|
||||
};
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include <algorithm>
|
||||
#include <utility>
|
||||
|
||||
#include "Common/JsonUtil.h"
|
||||
#include "Common/Logging/Log.h"
|
||||
#include "Common/StringUtil.h"
|
||||
#include "Common/VariantUtil.h"
|
||||
|
@ -291,6 +292,150 @@ bool PixelShaderData::FromJson(const VideoCommon::CustomAssetLibrary::AssetID& a
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
void PixelShaderData::ToJson(picojson::object& obj, const PixelShaderData& data)
|
||||
{
|
||||
picojson::array json_properties;
|
||||
for (const auto& [name, property] : data.m_properties)
|
||||
{
|
||||
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("type", "int");
|
||||
json_property.emplace("default", static_cast<double>(default_value));
|
||||
},
|
||||
[&](const std::array<s32, 2>& default_value) {
|
||||
json_property.emplace("type", "int2");
|
||||
json_property.emplace("default", ToJsonArray(default_value));
|
||||
},
|
||||
[&](const std::array<s32, 3>& default_value) {
|
||||
json_property.emplace("type", "int3");
|
||||
json_property.emplace("default", ToJsonArray(default_value));
|
||||
},
|
||||
[&](const std::array<s32, 4>& default_value) {
|
||||
json_property.emplace("type", "int4");
|
||||
json_property.emplace("default", ToJsonArray(default_value));
|
||||
},
|
||||
[&](float default_value) {
|
||||
json_property.emplace("type", "float");
|
||||
json_property.emplace("default", static_cast<double>(default_value));
|
||||
},
|
||||
[&](const std::array<float, 2>& default_value) {
|
||||
json_property.emplace("type", "float2");
|
||||
json_property.emplace("default", ToJsonArray(default_value));
|
||||
},
|
||||
[&](const std::array<float, 3>& default_value) {
|
||||
json_property.emplace("type", "float3");
|
||||
json_property.emplace("default", ToJsonArray(default_value));
|
||||
},
|
||||
[&](const std::array<float, 4>& default_value) {
|
||||
json_property.emplace("type", "float4");
|
||||
json_property.emplace("default", ToJsonArray(default_value));
|
||||
},
|
||||
[&](const ShaderProperty::RGB& default_value) {
|
||||
json_property.emplace("type", "rgb");
|
||||
json_property.emplace("default", ToJsonArray(default_value.value));
|
||||
},
|
||||
[&](const ShaderProperty::RGBA& default_value) {
|
||||
json_property.emplace("type", "rgba");
|
||||
json_property.emplace("default", ToJsonArray(default_value.value));
|
||||
},
|
||||
[&](bool default_value) {
|
||||
json_property.emplace("type", "bool");
|
||||
json_property.emplace("default", default_value);
|
||||
}},
|
||||
property.m_default);
|
||||
|
||||
json_properties.emplace_back(std::move(json_property));
|
||||
}
|
||||
|
||||
obj.emplace("properties", std::move(json_properties));
|
||||
}
|
||||
|
||||
std::span<const std::string_view> ShaderProperty::GetValueTypeNames()
|
||||
{
|
||||
static constexpr std::array<std::string_view, 14> values = {
|
||||
"sampler2d", "sampler2darray", "samplercube", "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")
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else if (name == "int2")
|
||||
{
|
||||
return std::array<s32, 2>{};
|
||||
}
|
||||
else if (name == "int3")
|
||||
{
|
||||
return std::array<s32, 3>{};
|
||||
}
|
||||
else if (name == "int4")
|
||||
{
|
||||
return std::array<s32, 4>{};
|
||||
}
|
||||
else if (name == "float")
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
else if (name == "float2")
|
||||
{
|
||||
return std::array<float, 2>{};
|
||||
}
|
||||
else if (name == "float3")
|
||||
{
|
||||
return std::array<float, 3>{};
|
||||
}
|
||||
else if (name == "float4")
|
||||
{
|
||||
return std::array<float, 4>{};
|
||||
}
|
||||
else if (name == "rgb")
|
||||
{
|
||||
return RGB{};
|
||||
}
|
||||
else if (name == "rgba")
|
||||
{
|
||||
return RGBA{};
|
||||
}
|
||||
else if (name == "bool")
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return Value{};
|
||||
}
|
||||
|
||||
CustomAssetLibrary::LoadInfo PixelShaderAsset::LoadImpl(const CustomAssetLibrary::AssetID& asset_id)
|
||||
{
|
||||
auto potential_data = std::make_shared<PixelShaderData>();
|
||||
|
|
|
@ -5,7 +5,9 @@
|
|||
|
||||
#include <array>
|
||||
#include <map>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <variant>
|
||||
|
||||
#include <picojson.h>
|
||||
|
@ -44,6 +46,8 @@ struct ShaderProperty
|
|||
using Value = std::variant<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,
|
||||
RGB, RGBA, Sampler2D, Sampler2DArray, SamplerCube>;
|
||||
static std::span<const std::string_view> GetValueTypeNames();
|
||||
static Value GetDefaultValueFromTypeName(std::string_view name);
|
||||
|
||||
Value m_default;
|
||||
std::string m_description;
|
||||
|
@ -52,6 +56,7 @@ struct PixelShaderData
|
|||
{
|
||||
static bool FromJson(const CustomAssetLibrary::AssetID& asset_id, const picojson::object& json,
|
||||
PixelShaderData* data);
|
||||
static void ToJson(picojson::object& obj, const PixelShaderData& data);
|
||||
|
||||
// These shader properties describe the input that the
|
||||
// shader expects to expose. The key is text
|
||||
|
|
|
@ -355,17 +355,18 @@ static void BPWritten(PixelShaderManager& pixel_shader_manager, XFStateManager&
|
|||
// Might also clean up some issues with games doing XFB copies they don't intend to
|
||||
// display.
|
||||
|
||||
auto& system = Core::System::GetInstance();
|
||||
if (g_ActiveConfig.bImmediateXFB)
|
||||
{
|
||||
// below div two to convert from bytes to pixels - it expects width, not stride
|
||||
u64 ticks = Core::System::GetInstance().GetCoreTiming().GetTicks();
|
||||
u64 ticks = system.GetCoreTiming().GetTicks();
|
||||
g_presenter->ImmediateSwap(destAddr, destStride / 2, destStride, height, ticks);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (FifoPlayer::GetInstance().IsRunningWithFakeVideoInterfaceUpdates())
|
||||
if (system.GetFifoPlayer().IsRunningWithFakeVideoInterfaceUpdates())
|
||||
{
|
||||
auto& vi = Core::System::GetInstance().GetVideoInterface();
|
||||
auto& vi = system.GetVideoInterface();
|
||||
vi.FakeVIUpdate(destAddr, srcRect.GetWidth(), destStride, height);
|
||||
}
|
||||
}
|
||||
|
@ -404,7 +405,7 @@ static void BPWritten(PixelShaderManager& pixel_shader_manager, XFStateManager&
|
|||
memory.CopyFromEmu(texMem + tmem_addr, addr, tmem_transfer_count);
|
||||
|
||||
if (OpcodeDecoder::g_record_fifo_data)
|
||||
FifoRecorder::GetInstance().UseMemory(addr, tmem_transfer_count, MemoryUpdate::Type::TMEM);
|
||||
system.GetFifoRecorder().UseMemory(addr, tmem_transfer_count, MemoryUpdate::Type::TMEM);
|
||||
|
||||
TMEM::InvalidateAll();
|
||||
|
||||
|
@ -623,7 +624,10 @@ static void BPWritten(PixelShaderManager& pixel_shader_manager, XFStateManager&
|
|||
}
|
||||
|
||||
if (OpcodeDecoder::g_record_fifo_data)
|
||||
FifoRecorder::GetInstance().UseMemory(src_addr, bytes_read, MemoryUpdate::Type::TMEM);
|
||||
{
|
||||
Core::System::GetInstance().GetFifoRecorder().UseMemory(src_addr, bytes_read,
|
||||
MemoryUpdate::Type::TMEM);
|
||||
}
|
||||
|
||||
TMEM::InvalidateAll();
|
||||
}
|
||||
|
|
|
@ -7,13 +7,6 @@
|
|||
|
||||
namespace VideoCommon
|
||||
{
|
||||
#ifdef ANDROID
|
||||
// Some devices seem to have graphical errors when providing 16 pixel samplers
|
||||
// given the logic is for a performance heavy feature (custom shaders), will just disable for now
|
||||
// TODO: handle this more elegantly
|
||||
constexpr u32 MAX_PIXEL_SHADER_SAMPLERS = 8;
|
||||
#else
|
||||
constexpr u32 MAX_PIXEL_SHADER_SAMPLERS = 16;
|
||||
#endif
|
||||
constexpr u32 MAX_COMPUTE_SHADER_SAMPLERS = 8;
|
||||
} // namespace VideoCommon
|
||||
|
|
|
@ -356,9 +356,10 @@ void FFMpegFrameDump::AddFrame(const FrameData& frame)
|
|||
return;
|
||||
|
||||
// Calculate presentation timestamp from ticks since start.
|
||||
const s64 pts = av_rescale_q(frame.state.ticks - m_context->start_ticks,
|
||||
AVRational{1, int(SystemTimers::GetTicksPerSecond())},
|
||||
m_context->codec->time_base);
|
||||
const s64 pts = av_rescale_q(
|
||||
frame.state.ticks - m_context->start_ticks,
|
||||
AVRational{1, int(Core::System::GetInstance().GetSystemTimers().GetTicksPerSecond())},
|
||||
m_context->codec->time_base);
|
||||
|
||||
if (!IsFirstFrameInCurrentFile())
|
||||
{
|
||||
|
|
|
@ -112,6 +112,42 @@ std::string GraphicsModConfig::GetAbsolutePath() const
|
|||
}
|
||||
}
|
||||
|
||||
void GraphicsModConfig::SerializeToConfig(picojson::object& json_obj) const
|
||||
{
|
||||
picojson::object serialized_metadata;
|
||||
serialized_metadata.emplace("title", m_title);
|
||||
serialized_metadata.emplace("author", m_author);
|
||||
serialized_metadata.emplace("description", m_description);
|
||||
json_obj.emplace("meta", std::move(serialized_metadata));
|
||||
|
||||
picojson::array serialized_groups;
|
||||
for (const auto& group : m_groups)
|
||||
{
|
||||
picojson::object serialized_group;
|
||||
group.SerializeToConfig(serialized_group);
|
||||
serialized_groups.emplace_back(std::move(serialized_group));
|
||||
}
|
||||
json_obj.emplace("groups", std::move(serialized_groups));
|
||||
|
||||
picojson::array serialized_features;
|
||||
for (const auto& feature : m_features)
|
||||
{
|
||||
picojson::object serialized_feature;
|
||||
feature.SerializeToConfig(serialized_feature);
|
||||
serialized_features.emplace_back(std::move(serialized_feature));
|
||||
}
|
||||
json_obj.emplace("features", std::move(serialized_features));
|
||||
|
||||
picojson::array serialized_assets;
|
||||
for (const auto& asset : m_assets)
|
||||
{
|
||||
picojson::object serialized_asset;
|
||||
asset.SerializeToConfig(serialized_asset);
|
||||
serialized_assets.emplace_back(std::move(serialized_asset));
|
||||
}
|
||||
json_obj.emplace("assets", std::move(serialized_assets));
|
||||
}
|
||||
|
||||
bool GraphicsModConfig::DeserializeFromConfig(const picojson::value& value)
|
||||
{
|
||||
const auto& meta = value.get("meta");
|
||||
|
@ -153,7 +189,7 @@ bool GraphicsModConfig::DeserializeFromConfig(const picojson::value& value)
|
|||
return false;
|
||||
}
|
||||
|
||||
m_groups.push_back(group);
|
||||
m_groups.push_back(std::move(group));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -174,7 +210,7 @@ bool GraphicsModConfig::DeserializeFromConfig(const picojson::value& value)
|
|||
return false;
|
||||
}
|
||||
|
||||
m_features.push_back(feature);
|
||||
m_features.push_back(std::move(feature));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -211,40 +247,36 @@ void GraphicsModConfig::SerializeToProfile(picojson::object* obj) const
|
|||
switch (m_source)
|
||||
{
|
||||
case Source::User:
|
||||
{
|
||||
json_obj["source"] = picojson::value{"user"};
|
||||
}
|
||||
break;
|
||||
json_obj.emplace("source", "user");
|
||||
break;
|
||||
case Source::System:
|
||||
{
|
||||
json_obj["source"] = picojson::value{"system"};
|
||||
json_obj.emplace("source", "system");
|
||||
break;
|
||||
}
|
||||
break;
|
||||
};
|
||||
|
||||
json_obj["path"] = picojson::value{m_relative_path};
|
||||
json_obj.emplace("path", m_relative_path);
|
||||
|
||||
picojson::array serialized_groups;
|
||||
for (const auto& group : m_groups)
|
||||
{
|
||||
picojson::object serialized_group;
|
||||
group.SerializeToProfile(&serialized_group);
|
||||
serialized_groups.push_back(picojson::value{serialized_group});
|
||||
serialized_groups.emplace_back(std::move(serialized_group));
|
||||
}
|
||||
json_obj["groups"] = picojson::value{serialized_groups};
|
||||
json_obj.emplace("groups", std::move(serialized_groups));
|
||||
|
||||
picojson::array serialized_features;
|
||||
for (const auto& feature : m_features)
|
||||
{
|
||||
picojson::object serialized_feature;
|
||||
feature.SerializeToProfile(&serialized_feature);
|
||||
serialized_features.push_back(picojson::value{serialized_feature});
|
||||
serialized_features.emplace_back(std::move(serialized_feature));
|
||||
}
|
||||
json_obj["features"] = picojson::value{serialized_features};
|
||||
json_obj.emplace("features", std::move(serialized_features));
|
||||
|
||||
json_obj["enabled"] = picojson::value{m_enabled};
|
||||
json_obj.emplace("enabled", m_enabled);
|
||||
|
||||
json_obj["weight"] = picojson::value{static_cast<double>(m_weight)};
|
||||
json_obj.emplace("weight", static_cast<double>(m_weight));
|
||||
}
|
||||
|
||||
void GraphicsModConfig::DeserializeFromProfile(const picojson::object& obj)
|
||||
|
@ -253,7 +285,7 @@ void GraphicsModConfig::DeserializeFromProfile(const picojson::object& obj)
|
|||
{
|
||||
if (it->second.is<picojson::array>())
|
||||
{
|
||||
auto serialized_groups = it->second.get<picojson::array>();
|
||||
const auto& serialized_groups = it->second.get<picojson::array>();
|
||||
if (serialized_groups.size() != m_groups.size())
|
||||
return;
|
||||
|
||||
|
@ -273,7 +305,7 @@ void GraphicsModConfig::DeserializeFromProfile(const picojson::object& obj)
|
|||
{
|
||||
if (it->second.is<picojson::array>())
|
||||
{
|
||||
auto serialized_features = it->second.get<picojson::array>();
|
||||
const auto& serialized_features = it->second.get<picojson::array>();
|
||||
if (serialized_features.size() != m_features.size())
|
||||
return;
|
||||
|
||||
|
|
|
@ -38,6 +38,7 @@ struct GraphicsModConfig
|
|||
|
||||
std::string GetAbsolutePath() const;
|
||||
|
||||
void SerializeToConfig(picojson::object& json_obj) const;
|
||||
bool DeserializeFromConfig(const picojson::value& value);
|
||||
|
||||
void SerializeToProfile(picojson::object* value) const;
|
||||
|
|
|
@ -4,6 +4,19 @@
|
|||
#include "VideoCommon/GraphicsModSystem/Config/GraphicsModAsset.h"
|
||||
|
||||
#include "Common/Logging/Log.h"
|
||||
#include "Common/StringUtil.h"
|
||||
|
||||
void GraphicsModAssetConfig::SerializeToConfig(picojson::object& json_obj) const
|
||||
{
|
||||
json_obj.emplace("name", m_asset_id);
|
||||
|
||||
picojson::object serialized_data;
|
||||
for (const auto& [name, path] : m_map)
|
||||
{
|
||||
serialized_data.emplace(name, PathToString(path));
|
||||
}
|
||||
json_obj.emplace("data", std::move(serialized_data));
|
||||
}
|
||||
|
||||
bool GraphicsModAssetConfig::DeserializeFromConfig(const picojson::object& obj)
|
||||
{
|
||||
|
@ -19,13 +32,13 @@ bool GraphicsModAssetConfig::DeserializeFromConfig(const picojson::object& obj)
|
|||
"that is not a string");
|
||||
return false;
|
||||
}
|
||||
m_name = name_iter->second.to_str();
|
||||
m_asset_id = name_iter->second.to_str();
|
||||
|
||||
auto data_iter = obj.find("data");
|
||||
if (data_iter == obj.end())
|
||||
{
|
||||
ERROR_LOG_FMT(VIDEO, "Failed to load mod configuration file, specified asset '{}' has no data",
|
||||
m_name);
|
||||
m_asset_id);
|
||||
return false;
|
||||
}
|
||||
if (!data_iter->second.is<picojson::object>())
|
||||
|
@ -33,7 +46,7 @@ bool GraphicsModAssetConfig::DeserializeFromConfig(const picojson::object& obj)
|
|||
ERROR_LOG_FMT(VIDEO,
|
||||
"Failed to load mod configuration file, specified asset '{}' has data "
|
||||
"that is not an object",
|
||||
m_name);
|
||||
m_asset_id);
|
||||
return false;
|
||||
}
|
||||
for (const auto& [key, value] : data_iter->second.get<picojson::object>())
|
||||
|
@ -43,7 +56,7 @@ bool GraphicsModAssetConfig::DeserializeFromConfig(const picojson::object& obj)
|
|||
ERROR_LOG_FMT(VIDEO,
|
||||
"Failed to load mod configuration file, specified asset '{}' has data "
|
||||
"with a value for key '{}' that is not a string",
|
||||
m_name, key);
|
||||
m_asset_id, key);
|
||||
return false;
|
||||
}
|
||||
m_map[key] = value.to_str();
|
||||
|
|
|
@ -11,8 +11,9 @@
|
|||
|
||||
struct GraphicsModAssetConfig
|
||||
{
|
||||
std::string m_name;
|
||||
VideoCommon::CustomAssetLibrary::AssetID m_asset_id;
|
||||
VideoCommon::DirectFilesystemAssetLibrary::AssetMap m_map;
|
||||
|
||||
void SerializeToConfig(picojson::object& json_obj) const;
|
||||
bool DeserializeFromConfig(const picojson::object& obj);
|
||||
};
|
||||
|
|
|
@ -5,6 +5,13 @@
|
|||
|
||||
#include "Common/Logging/Log.h"
|
||||
|
||||
void GraphicsModFeatureConfig::SerializeToConfig(picojson::object& json_obj) const
|
||||
{
|
||||
json_obj.emplace("group", m_group);
|
||||
json_obj.emplace("action", m_action);
|
||||
json_obj.emplace("action_data", m_action_data);
|
||||
}
|
||||
|
||||
bool GraphicsModFeatureConfig::DeserializeFromConfig(const picojson::object& obj)
|
||||
{
|
||||
if (auto group_iter = obj.find("group"); group_iter != obj.end())
|
||||
|
|
|
@ -13,6 +13,7 @@ struct GraphicsModFeatureConfig
|
|||
std::string m_action;
|
||||
picojson::value m_action_data;
|
||||
|
||||
void SerializeToConfig(picojson::object& json_obj) const;
|
||||
bool DeserializeFromConfig(const picojson::object& value);
|
||||
|
||||
void SerializeToProfile(picojson::object* value) const;
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
#include "VideoCommon/GraphicsModSystem/Constants.h"
|
||||
#include "VideoCommon/HiresTextures.h"
|
||||
|
||||
GraphicsModGroupConfig::GraphicsModGroupConfig(const std::string& game_id) : m_game_id(game_id)
|
||||
GraphicsModGroupConfig::GraphicsModGroupConfig(std::string game_id) : m_game_id(std::move(game_id))
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -26,11 +26,12 @@ GraphicsModGroupConfig::~GraphicsModGroupConfig() = default;
|
|||
|
||||
GraphicsModGroupConfig::GraphicsModGroupConfig(const GraphicsModGroupConfig&) = default;
|
||||
|
||||
GraphicsModGroupConfig::GraphicsModGroupConfig(GraphicsModGroupConfig&&) = default;
|
||||
GraphicsModGroupConfig::GraphicsModGroupConfig(GraphicsModGroupConfig&&) noexcept = default;
|
||||
|
||||
GraphicsModGroupConfig& GraphicsModGroupConfig::operator=(const GraphicsModGroupConfig&) = default;
|
||||
|
||||
GraphicsModGroupConfig& GraphicsModGroupConfig::operator=(GraphicsModGroupConfig&&) = default;
|
||||
GraphicsModGroupConfig&
|
||||
GraphicsModGroupConfig::operator=(GraphicsModGroupConfig&&) noexcept = default;
|
||||
|
||||
void GraphicsModGroupConfig::Load()
|
||||
{
|
||||
|
@ -145,9 +146,9 @@ void GraphicsModGroupConfig::Save() const
|
|||
{
|
||||
picojson::object serialized_mod;
|
||||
mod.SerializeToProfile(&serialized_mod);
|
||||
serialized_mods.push_back(picojson::value{serialized_mod});
|
||||
serialized_mods.emplace_back(std::move(serialized_mod));
|
||||
}
|
||||
serialized_root["mods"] = picojson::value{serialized_mods};
|
||||
serialized_root.emplace("mods", std::move(serialized_mods));
|
||||
|
||||
const auto output = picojson::value{serialized_root}.serialize(true);
|
||||
json_stream << output;
|
||||
|
|
|
@ -16,14 +16,14 @@ struct GraphicsModConfig;
|
|||
class GraphicsModGroupConfig
|
||||
{
|
||||
public:
|
||||
explicit GraphicsModGroupConfig(const std::string& game_id);
|
||||
explicit GraphicsModGroupConfig(std::string game_id);
|
||||
~GraphicsModGroupConfig();
|
||||
|
||||
GraphicsModGroupConfig(const GraphicsModGroupConfig&);
|
||||
GraphicsModGroupConfig(GraphicsModGroupConfig&&);
|
||||
GraphicsModGroupConfig(GraphicsModGroupConfig&&) noexcept;
|
||||
|
||||
GraphicsModGroupConfig& operator=(const GraphicsModGroupConfig&);
|
||||
GraphicsModGroupConfig& operator=(GraphicsModGroupConfig&&);
|
||||
GraphicsModGroupConfig& operator=(GraphicsModGroupConfig&&) noexcept;
|
||||
|
||||
void Load();
|
||||
void Save() const;
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
#include "Common/Logging/Log.h"
|
||||
#include "Common/StringUtil.h"
|
||||
#include "Common/VariantUtil.h"
|
||||
#include "VideoCommon/TextureCacheBase.h"
|
||||
|
||||
namespace
|
||||
|
@ -152,6 +153,51 @@ std::optional<std::string> ExtractTextureFilenameForConfig(const picojson::objec
|
|||
}
|
||||
} // namespace
|
||||
|
||||
void SerializeTargetToConfig(picojson::object& json_obj, const GraphicsTargetConfig& target)
|
||||
{
|
||||
std::visit(overloaded{
|
||||
[&](const DrawStartedTextureTarget& the_target) {
|
||||
json_obj.emplace("type", "draw_started");
|
||||
json_obj.emplace("texture_filename", the_target.m_texture_info_string);
|
||||
},
|
||||
[&](const LoadTextureTarget& the_target) {
|
||||
json_obj.emplace("type", "load_texture");
|
||||
json_obj.emplace("texture_filename", the_target.m_texture_info_string);
|
||||
},
|
||||
[&](const CreateTextureTarget& the_target) {
|
||||
json_obj.emplace("type", "create_texture");
|
||||
json_obj.emplace("texture_filename", the_target.m_texture_info_string);
|
||||
},
|
||||
[&](const EFBTarget& the_target) {
|
||||
json_obj.emplace("type", "efb");
|
||||
json_obj.emplace("texture_filename",
|
||||
fmt::format("{}_{}x{}_{}", EFB_DUMP_PREFIX, the_target.m_width,
|
||||
the_target.m_height,
|
||||
static_cast<int>(the_target.m_texture_format)));
|
||||
},
|
||||
[&](const XFBTarget& the_target) {
|
||||
json_obj.emplace("type", "xfb");
|
||||
json_obj.emplace("texture_filename",
|
||||
fmt::format("{}_{}x{}_{}", XFB_DUMP_PREFIX, the_target.m_width,
|
||||
the_target.m_height,
|
||||
static_cast<int>(the_target.m_texture_format)));
|
||||
},
|
||||
[&](const ProjectionTarget& the_target) {
|
||||
const char* type_name = "3d";
|
||||
if (the_target.m_projection_type == ProjectionType::Orthographic)
|
||||
type_name = "2d";
|
||||
|
||||
json_obj.emplace("type", type_name);
|
||||
|
||||
if (the_target.m_texture_info_string)
|
||||
{
|
||||
json_obj.emplace("texture_filename", *the_target.m_texture_info_string);
|
||||
}
|
||||
},
|
||||
},
|
||||
target);
|
||||
}
|
||||
|
||||
std::optional<GraphicsTargetConfig> DeserializeTargetFromConfig(const picojson::object& obj)
|
||||
{
|
||||
const auto type_iter = obj.find("type");
|
||||
|
|
|
@ -55,6 +55,7 @@ using GraphicsTargetConfig =
|
|||
std::variant<DrawStartedTextureTarget, LoadTextureTarget, CreateTextureTarget, EFBTarget,
|
||||
XFBTarget, ProjectionTarget>;
|
||||
|
||||
void SerializeTargetToConfig(picojson::object& json_obj, const GraphicsTargetConfig& target);
|
||||
std::optional<GraphicsTargetConfig> DeserializeTargetFromConfig(const picojson::object& obj);
|
||||
|
||||
void SerializeTargetToProfile(picojson::object* obj, const GraphicsTargetConfig& target);
|
||||
|
|
|
@ -5,6 +5,19 @@
|
|||
|
||||
#include "Common/Logging/Log.h"
|
||||
|
||||
void GraphicsTargetGroupConfig::SerializeToConfig(picojson::object& json_obj) const
|
||||
{
|
||||
picojson::array serialized_targets;
|
||||
for (const auto& target : m_targets)
|
||||
{
|
||||
picojson::object serialized_target;
|
||||
SerializeTargetToConfig(serialized_target, target);
|
||||
serialized_targets.emplace_back(std::move(serialized_target));
|
||||
}
|
||||
json_obj.emplace("targets", std::move(serialized_targets));
|
||||
json_obj.emplace("name", m_name);
|
||||
}
|
||||
|
||||
bool GraphicsTargetGroupConfig::DeserializeFromConfig(const picojson::object& obj)
|
||||
{
|
||||
if (auto name_iter = obj.find("name"); name_iter != obj.end())
|
||||
|
@ -59,9 +72,9 @@ void GraphicsTargetGroupConfig::SerializeToProfile(picojson::object* obj) const
|
|||
{
|
||||
picojson::object serialized_target;
|
||||
SerializeTargetToProfile(&serialized_target, target);
|
||||
serialized_targets.push_back(picojson::value{serialized_target});
|
||||
serialized_targets.emplace_back(std::move(serialized_target));
|
||||
}
|
||||
json_obj["targets"] = picojson::value{serialized_targets};
|
||||
json_obj.emplace("targets", std::move(serialized_targets));
|
||||
}
|
||||
|
||||
void GraphicsTargetGroupConfig::DeserializeFromProfile(const picojson::object& obj)
|
||||
|
@ -70,7 +83,7 @@ void GraphicsTargetGroupConfig::DeserializeFromProfile(const picojson::object& o
|
|||
{
|
||||
if (it->second.is<picojson::array>())
|
||||
{
|
||||
auto serialized_targets = it->second.get<picojson::array>();
|
||||
const auto& serialized_targets = it->second.get<picojson::array>();
|
||||
if (serialized_targets.size() != m_targets.size())
|
||||
return;
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ struct GraphicsTargetGroupConfig
|
|||
std::string m_name;
|
||||
std::vector<GraphicsTargetConfig> m_targets;
|
||||
|
||||
void SerializeToConfig(picojson::object& json_obj) const;
|
||||
bool DeserializeFromConfig(const picojson::object& obj);
|
||||
|
||||
void SerializeToProfile(picojson::object* obj) const;
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "Common/FileUtil.h"
|
||||
#include "Common/Logging/Log.h"
|
||||
#include "Common/StringUtil.h"
|
||||
#include "Common/VariantUtil.h"
|
||||
#include "Core/System.h"
|
||||
|
||||
#include "VideoCommon/AbstractGfx.h"
|
||||
|
@ -175,20 +176,6 @@ std::vector<std::string> GlobalConflicts(std::string_view source)
|
|||
return global_result;
|
||||
}
|
||||
|
||||
void WriteDefines(ShaderCode* out, const std::vector<std::string>& texture_code_names,
|
||||
u32 texture_unit)
|
||||
{
|
||||
for (std::size_t i = 0; i < texture_code_names.size(); i++)
|
||||
{
|
||||
const auto& code_name = texture_code_names[i];
|
||||
out->Write("#define {}_UNIT_{{0}} {}\n", code_name, texture_unit);
|
||||
out->Write(
|
||||
"#define {0}_COORD_{{0}} float3(data.texcoord[data.texmap_to_texcoord_index[{1}]].xy, "
|
||||
"{2})\n",
|
||||
code_name, texture_unit, i + 1);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
std::unique_ptr<CustomPipelineAction>
|
||||
|
@ -267,186 +254,251 @@ void CustomPipelineAction::OnDrawStarted(GraphicsModActionData::DrawStarted* dra
|
|||
if (!draw_started->custom_pixel_shader) [[unlikely]]
|
||||
return;
|
||||
|
||||
if (!m_valid)
|
||||
if (!draw_started->material_uniform_buffer) [[unlikely]]
|
||||
return;
|
||||
|
||||
if (m_passes.empty()) [[unlikely]]
|
||||
return;
|
||||
|
||||
// For now assume a single pass
|
||||
auto& pass = m_passes[0];
|
||||
|
||||
if (!pass.m_pixel_shader.m_asset) [[unlikely]]
|
||||
return;
|
||||
|
||||
const auto shader_data = pass.m_pixel_shader.m_asset->GetData();
|
||||
if (shader_data)
|
||||
{
|
||||
if (m_last_generated_shader_code.GetBuffer().empty())
|
||||
{
|
||||
// Calculate shader details
|
||||
std::string color_shader_data =
|
||||
ReplaceAll(shader_data->m_shader_source, "custom_main", CUSTOM_PIXELSHADER_COLOR_FUNC);
|
||||
const auto global_conflicts = GlobalConflicts(color_shader_data);
|
||||
color_shader_data = ReplaceAll(color_shader_data, "\r\n", "\n");
|
||||
color_shader_data = ReplaceAll(color_shader_data, "{", "{{");
|
||||
color_shader_data = ReplaceAll(color_shader_data, "}", "}}");
|
||||
// First replace global conflicts with dummy strings
|
||||
// This avoids the problem where a shorter word
|
||||
// is in a longer word, ex two functions: 'execute' and 'execute_fast'
|
||||
for (std::size_t i = 0; i < global_conflicts.size(); i++)
|
||||
{
|
||||
const std::string& identifier = global_conflicts[i];
|
||||
color_shader_data =
|
||||
ReplaceAll(color_shader_data, identifier, fmt::format("_{0}_DOLPHIN_TEMP_{0}_", i));
|
||||
}
|
||||
// Now replace the temporaries with the actual value
|
||||
for (std::size_t i = 0; i < global_conflicts.size(); i++)
|
||||
{
|
||||
const std::string& identifier = global_conflicts[i];
|
||||
color_shader_data = ReplaceAll(color_shader_data, fmt::format("_{0}_DOLPHIN_TEMP_{0}_", i),
|
||||
fmt::format("{}_{{0}}", identifier));
|
||||
}
|
||||
|
||||
for (const auto& texture_code_name : m_texture_code_names)
|
||||
{
|
||||
color_shader_data =
|
||||
ReplaceAll(color_shader_data, fmt::format("{}_COORD", texture_code_name),
|
||||
fmt::format("{}_COORD_{{0}}", texture_code_name));
|
||||
color_shader_data = ReplaceAll(color_shader_data, fmt::format("{}_UNIT", texture_code_name),
|
||||
fmt::format("{}_UNIT_{{0}}", texture_code_name));
|
||||
}
|
||||
|
||||
WriteDefines(&m_last_generated_shader_code, m_texture_code_names, draw_started->texture_unit);
|
||||
m_last_generated_shader_code.Write("{}", color_shader_data);
|
||||
}
|
||||
CustomPixelShader custom_pixel_shader;
|
||||
custom_pixel_shader.custom_shader = m_last_generated_shader_code.GetBuffer();
|
||||
*draw_started->custom_pixel_shader = custom_pixel_shader;
|
||||
}
|
||||
}
|
||||
|
||||
void CustomPipelineAction::OnTextureCreate(GraphicsModActionData::TextureCreate* create)
|
||||
{
|
||||
if (!create->custom_textures) [[unlikely]]
|
||||
return;
|
||||
|
||||
if (!create->additional_dependencies) [[unlikely]]
|
||||
return;
|
||||
|
||||
if (m_passes_config.empty()) [[unlikely]]
|
||||
return;
|
||||
|
||||
if (m_passes.empty()) [[unlikely]]
|
||||
return;
|
||||
|
||||
m_valid = true;
|
||||
auto& loader = Core::System::GetInstance().GetCustomAssetLoader();
|
||||
|
||||
// For now assume a single pass
|
||||
const auto& pass_config = m_passes_config[0];
|
||||
auto& pass = m_passes[0];
|
||||
|
||||
if (!pass.m_pixel_material.m_asset)
|
||||
if (!pass.m_pixel_material.m_asset ||
|
||||
pass_config.m_pixel_material_asset != pass.m_pixel_material.m_asset->GetAssetId())
|
||||
{
|
||||
pass.m_pixel_material.m_asset =
|
||||
loader.LoadMaterial(pass_config.m_pixel_material_asset, m_library);
|
||||
pass.m_pixel_material.m_cached_write_time = pass.m_pixel_material.m_asset->GetLastLoadedTime();
|
||||
}
|
||||
create->additional_dependencies->push_back(VideoCommon::CachedAsset<VideoCommon::CustomAsset>{
|
||||
pass.m_pixel_material.m_asset, pass.m_pixel_material.m_asset->GetLastLoadedTime()});
|
||||
|
||||
const auto material_data = pass.m_pixel_material.m_asset->GetData();
|
||||
if (!material_data)
|
||||
return;
|
||||
|
||||
if (!pass.m_pixel_shader.m_asset || pass.m_pixel_material.m_asset->GetLastLoadedTime() >
|
||||
pass.m_pixel_material.m_cached_write_time)
|
||||
{
|
||||
m_last_generated_shader_code = ShaderCode{};
|
||||
return;
|
||||
}
|
||||
|
||||
std::size_t max_material_data_size = 0;
|
||||
if (pass.m_pixel_material.m_asset->GetLastLoadedTime() >
|
||||
pass.m_pixel_material.m_cached_write_time)
|
||||
{
|
||||
m_last_generated_material_code = ShaderCode{};
|
||||
pass.m_pixel_material.m_cached_write_time = pass.m_pixel_material.m_asset->GetLastLoadedTime();
|
||||
std::size_t texture_count = 0;
|
||||
for (const auto& property : material_data->properties)
|
||||
{
|
||||
max_material_data_size += VideoCommon::MaterialProperty::GetMemorySize(property);
|
||||
VideoCommon::MaterialProperty::WriteAsShaderCode(m_last_generated_material_code, property);
|
||||
if (auto* texture_asset_id =
|
||||
std::get_if<VideoCommon::CustomAssetLibrary::AssetID>(&property.m_value))
|
||||
{
|
||||
texture_count++;
|
||||
}
|
||||
}
|
||||
m_material_data.resize(max_material_data_size);
|
||||
pass.m_game_textures.resize(texture_count);
|
||||
}
|
||||
|
||||
if (!pass.m_pixel_shader.m_asset ||
|
||||
pass.m_pixel_shader.m_asset->GetLastLoadedTime() > pass.m_pixel_shader.m_cached_write_time ||
|
||||
material_data->shader_asset != pass.m_pixel_shader.m_asset->GetAssetId())
|
||||
{
|
||||
pass.m_pixel_shader.m_asset = loader.LoadPixelShader(material_data->shader_asset, m_library);
|
||||
pass.m_pixel_shader.m_cached_write_time = pass.m_pixel_shader.m_asset->GetLastLoadedTime();
|
||||
|
||||
m_last_generated_shader_code = ShaderCode{};
|
||||
}
|
||||
create->additional_dependencies->push_back(VideoCommon::CachedAsset<VideoCommon::CustomAsset>{
|
||||
pass.m_pixel_shader.m_asset, pass.m_pixel_shader.m_asset->GetLastLoadedTime()});
|
||||
|
||||
const auto shader_data = pass.m_pixel_shader.m_asset->GetData();
|
||||
if (!shader_data)
|
||||
{
|
||||
m_valid = false;
|
||||
return;
|
||||
}
|
||||
|
||||
m_texture_code_names.clear();
|
||||
std::vector<VideoCommon::CachedAsset<VideoCommon::GameTextureAsset>> game_assets;
|
||||
for (const auto& property : material_data->properties)
|
||||
if (shader_data->m_properties.size() != material_data->properties.size())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
u8* material_buffer = m_material_data.data();
|
||||
u32 sampler_index = 8;
|
||||
for (std::size_t index = 0; index < material_data->properties.size(); index++)
|
||||
{
|
||||
auto& property = material_data->properties[index];
|
||||
const auto shader_it = shader_data->m_properties.find(property.m_code_name);
|
||||
if (shader_it == shader_data->m_properties.end())
|
||||
{
|
||||
ERROR_LOG_FMT(VIDEO,
|
||||
"Custom pipeline for texture '{}' has material asset '{}' that uses a "
|
||||
"Custom pipeline, has material asset '{}' that uses a "
|
||||
"code name of '{}' but that can't be found on shader asset '{}'!",
|
||||
create->texture_name, pass.m_pixel_material.m_asset->GetAssetId(),
|
||||
property.m_code_name, pass.m_pixel_shader.m_asset->GetAssetId());
|
||||
m_valid = false;
|
||||
pass.m_pixel_material.m_asset->GetAssetId(), property.m_code_name,
|
||||
pass.m_pixel_shader.m_asset->GetAssetId());
|
||||
return;
|
||||
}
|
||||
|
||||
if (auto* value = std::get_if<std::string>(&property.m_value))
|
||||
if (auto* texture_asset_id =
|
||||
std::get_if<VideoCommon::CustomAssetLibrary::AssetID>(&property.m_value))
|
||||
{
|
||||
auto asset = loader.LoadGameTexture(*value, m_library);
|
||||
if (asset)
|
||||
if (*texture_asset_id != "")
|
||||
{
|
||||
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);
|
||||
auto asset = loader.LoadGameTexture(*texture_asset_id, m_library);
|
||||
if (!asset)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto& texture_asset = pass.m_game_textures[index];
|
||||
if (!texture_asset ||
|
||||
texture_asset->m_cached_asset.m_asset->GetLastLoadedTime() >
|
||||
texture_asset->m_cached_asset.m_cached_write_time ||
|
||||
*texture_asset_id != texture_asset->m_cached_asset.m_asset->GetAssetId())
|
||||
{
|
||||
if (!texture_asset)
|
||||
{
|
||||
texture_asset = PipelinePass::CachedTextureAsset{};
|
||||
}
|
||||
const auto loaded_time = asset->GetLastLoadedTime();
|
||||
texture_asset->m_cached_asset = VideoCommon::CachedAsset<VideoCommon::GameTextureAsset>{
|
||||
std::move(asset), loaded_time};
|
||||
texture_asset->m_texture.reset();
|
||||
|
||||
if (std::holds_alternative<VideoCommon::ShaderProperty::Sampler2D>(
|
||||
shader_it->second.m_default))
|
||||
{
|
||||
texture_asset->m_sampler_code =
|
||||
fmt::format("SAMPLER_BINDING({}) uniform sampler2D samp_{};\n", sampler_index,
|
||||
property.m_code_name);
|
||||
texture_asset->m_define_code = fmt::format("#define HAS_{} 1\n", property.m_code_name);
|
||||
}
|
||||
else if (std::holds_alternative<VideoCommon::ShaderProperty::Sampler2DArray>(
|
||||
shader_it->second.m_default))
|
||||
{
|
||||
texture_asset->m_sampler_code =
|
||||
fmt::format("SAMPLER_BINDING({}) uniform sampler2DArray samp_{};\n", sampler_index,
|
||||
property.m_code_name);
|
||||
texture_asset->m_define_code = fmt::format("#define HAS_{} 1\n", property.m_code_name);
|
||||
}
|
||||
else if (std::holds_alternative<VideoCommon::ShaderProperty::SamplerCube>(
|
||||
shader_it->second.m_default))
|
||||
{
|
||||
texture_asset->m_sampler_code =
|
||||
fmt::format("SAMPLER_BINDING({}) uniform samplerCube samp_{};\n", sampler_index,
|
||||
property.m_code_name);
|
||||
texture_asset->m_define_code = fmt::format("#define HAS_{} 1\n", property.m_code_name);
|
||||
}
|
||||
}
|
||||
|
||||
const auto texture_data = texture_asset->m_cached_asset.m_asset->GetData();
|
||||
if (!texture_data)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (texture_asset->m_texture)
|
||||
{
|
||||
g_gfx->SetTexture(sampler_index, texture_asset->m_texture.get());
|
||||
g_gfx->SetSamplerState(sampler_index, texture_data->m_sampler);
|
||||
}
|
||||
else
|
||||
{
|
||||
AbstractTextureType texture_usage = AbstractTextureType::Texture_2DArray;
|
||||
if (std::holds_alternative<VideoCommon::ShaderProperty::SamplerCube>(
|
||||
shader_it->second.m_default))
|
||||
{
|
||||
texture_usage = AbstractTextureType::Texture_CubeMap;
|
||||
}
|
||||
else if (std::holds_alternative<VideoCommon::ShaderProperty::Sampler2D>(
|
||||
shader_it->second.m_default))
|
||||
{
|
||||
texture_usage = AbstractTextureType::Texture_2D;
|
||||
}
|
||||
|
||||
if (texture_data->m_texture.m_slices.empty() ||
|
||||
texture_data->m_texture.m_slices[0].m_levels.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto& first_slice = texture_data->m_texture.m_slices[0];
|
||||
const TextureConfig texture_config(
|
||||
first_slice.m_levels[0].width, first_slice.m_levels[0].height,
|
||||
static_cast<u32>(first_slice.m_levels.size()),
|
||||
static_cast<u32>(texture_data->m_texture.m_slices.size()), 1,
|
||||
first_slice.m_levels[0].format, 0, texture_usage);
|
||||
texture_asset->m_texture = g_gfx->CreateTexture(
|
||||
texture_config, fmt::format("Custom shader texture '{}'", property.m_code_name));
|
||||
for (std::size_t slice_index = 0; slice_index < texture_data->m_texture.m_slices.size();
|
||||
slice_index++)
|
||||
{
|
||||
auto& slice = texture_data->m_texture.m_slices[slice_index];
|
||||
for (u32 level_index = 0; level_index < static_cast<u32>(slice.m_levels.size());
|
||||
++level_index)
|
||||
{
|
||||
auto& level = slice.m_levels[level_index];
|
||||
texture_asset->m_texture->Load(level_index, level.width, level.height,
|
||||
level.row_length, level.data.data(), level.data.size(),
|
||||
static_cast<u32>(slice_index));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sampler_index++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
VideoCommon::MaterialProperty::WriteToMemory(material_buffer, property);
|
||||
}
|
||||
}
|
||||
// Note: we swap here instead of doing a clear + append of the member
|
||||
// variable so that any loaded assets from previous iterations
|
||||
// won't be let go
|
||||
std::swap(pass.m_game_textures, game_assets);
|
||||
|
||||
for (auto& game_texture : pass.m_game_textures)
|
||||
if (m_last_generated_shader_code.GetBuffer().empty())
|
||||
{
|
||||
if (game_texture.m_asset)
|
||||
// Calculate shader details
|
||||
std::string color_shader_data =
|
||||
ReplaceAll(shader_data->m_shader_source, "custom_main", CUSTOM_PIXELSHADER_COLOR_FUNC);
|
||||
const auto global_conflicts = GlobalConflicts(color_shader_data);
|
||||
color_shader_data = ReplaceAll(color_shader_data, "\r\n", "\n");
|
||||
color_shader_data = ReplaceAll(color_shader_data, "{", "{{");
|
||||
color_shader_data = ReplaceAll(color_shader_data, "}", "}}");
|
||||
// First replace global conflicts with dummy strings
|
||||
// This avoids the problem where a shorter word
|
||||
// is in a longer word, ex two functions: 'execute' and 'execute_fast'
|
||||
for (std::size_t i = 0; i < global_conflicts.size(); i++)
|
||||
{
|
||||
auto data = game_texture.m_asset->GetData();
|
||||
if (data)
|
||||
{
|
||||
if (data->m_texture.m_slices.empty() || data->m_texture.m_slices[0].m_levels.empty())
|
||||
{
|
||||
ERROR_LOG_FMT(
|
||||
VIDEO,
|
||||
"Custom pipeline for texture '{}' has asset '{}' that does not have any texture data",
|
||||
create->texture_name, game_texture.m_asset->GetAssetId());
|
||||
m_valid = false;
|
||||
}
|
||||
else if (create->texture_width != data->m_texture.m_slices[0].m_levels[0].width ||
|
||||
create->texture_height != data->m_texture.m_slices[0].m_levels[0].height)
|
||||
{
|
||||
ERROR_LOG_FMT(VIDEO,
|
||||
"Custom pipeline for texture '{}' has asset '{}' that does not match "
|
||||
"the width/height of the texture loaded. Texture {}x{} vs asset {}x{}",
|
||||
create->texture_name, game_texture.m_asset->GetAssetId(),
|
||||
create->texture_width, create->texture_height,
|
||||
data->m_texture.m_slices[0].m_levels[0].width,
|
||||
data->m_texture.m_slices[0].m_levels[0].height);
|
||||
m_valid = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_valid = false;
|
||||
}
|
||||
const std::string& identifier = global_conflicts[i];
|
||||
color_shader_data =
|
||||
ReplaceAll(color_shader_data, identifier, fmt::format("_{0}_DOLPHIN_TEMP_{0}_", i));
|
||||
}
|
||||
// Now replace the temporaries with the actual value
|
||||
for (std::size_t i = 0; i < global_conflicts.size(); i++)
|
||||
{
|
||||
const std::string& identifier = global_conflicts[i];
|
||||
color_shader_data = ReplaceAll(color_shader_data, fmt::format("_{0}_DOLPHIN_TEMP_{0}_", i),
|
||||
fmt::format("{}_{{0}}", identifier));
|
||||
}
|
||||
|
||||
for (const auto& game_texture : pass.m_game_textures)
|
||||
{
|
||||
if (!game_texture)
|
||||
continue;
|
||||
|
||||
m_last_generated_shader_code.Write("{}", game_texture->m_sampler_code);
|
||||
m_last_generated_shader_code.Write("{}", game_texture->m_define_code);
|
||||
}
|
||||
|
||||
for (std::size_t i = 0; i < draw_started->texture_units.size(); i++)
|
||||
{
|
||||
const auto& texture_unit = draw_started->texture_units[i];
|
||||
m_last_generated_shader_code.Write(
|
||||
"#define TEX_COORD{} data.texcoord[data.texmap_to_texcoord_index[{}]].xy\n", i,
|
||||
texture_unit);
|
||||
}
|
||||
m_last_generated_shader_code.Write("{}", color_shader_data);
|
||||
}
|
||||
|
||||
// TODO: compare game textures and shader requirements
|
||||
|
||||
create->custom_textures->insert(create->custom_textures->end(), pass.m_game_textures.begin(),
|
||||
pass.m_game_textures.end());
|
||||
CustomPixelShader custom_pixel_shader;
|
||||
custom_pixel_shader.custom_shader = m_last_generated_shader_code.GetBuffer();
|
||||
custom_pixel_shader.material_uniform_block = m_last_generated_material_code.GetBuffer();
|
||||
*draw_started->custom_pixel_shader = custom_pixel_shader;
|
||||
*draw_started->material_uniform_buffer = m_material_data;
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
@ -33,7 +34,6 @@ public:
|
|||
std::vector<PipelinePassPassDescription> pass_descriptions);
|
||||
~CustomPipelineAction();
|
||||
void OnDrawStarted(GraphicsModActionData::DrawStarted*) override;
|
||||
void OnTextureCreate(GraphicsModActionData::TextureCreate*) override;
|
||||
|
||||
private:
|
||||
std::shared_ptr<VideoCommon::CustomAssetLibrary> m_library;
|
||||
|
@ -42,13 +42,20 @@ private:
|
|||
{
|
||||
VideoCommon::CachedAsset<VideoCommon::MaterialAsset> m_pixel_material;
|
||||
VideoCommon::CachedAsset<VideoCommon::PixelShaderAsset> m_pixel_shader;
|
||||
std::vector<VideoCommon::CachedAsset<VideoCommon::GameTextureAsset>> m_game_textures;
|
||||
|
||||
struct CachedTextureAsset
|
||||
{
|
||||
VideoCommon::CachedAsset<VideoCommon::GameTextureAsset> m_cached_asset;
|
||||
std::unique_ptr<AbstractTexture> m_texture;
|
||||
std::string m_sampler_code;
|
||||
std::string m_define_code;
|
||||
};
|
||||
std::vector<std::optional<CachedTextureAsset>> m_game_textures;
|
||||
};
|
||||
std::vector<PipelinePass> m_passes;
|
||||
|
||||
ShaderCode m_last_generated_shader_code;
|
||||
ShaderCode m_last_generated_material_code;
|
||||
|
||||
bool m_valid = true;
|
||||
|
||||
std::vector<std::string> m_texture_code_names;
|
||||
std::vector<u8> m_material_data;
|
||||
};
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/Matrix.h"
|
||||
#include "Common/SmallVector.h"
|
||||
#include "VideoCommon/Assets/TextureAsset.h"
|
||||
#include "VideoCommon/PixelShaderGen.h"
|
||||
|
||||
|
@ -18,7 +19,7 @@ namespace GraphicsModActionData
|
|||
{
|
||||
struct DrawStarted
|
||||
{
|
||||
u32 texture_unit;
|
||||
const Common::SmallVector<u32, 8>& texture_units;
|
||||
bool* skip;
|
||||
std::optional<CustomPixelShader>* custom_pixel_shader;
|
||||
std::span<u8>* material_uniform_buffer;
|
||||
|
|
|
@ -3,9 +3,6 @@
|
|||
|
||||
#include "VideoCommon/GraphicsModSystem/Runtime/GraphicsModActionFactory.h"
|
||||
|
||||
#include "Common/Logging/Log.h"
|
||||
|
||||
#include "VideoCommon/Constants.h"
|
||||
#include "VideoCommon/GraphicsModSystem/Runtime/Actions/CustomPipelineAction.h"
|
||||
#include "VideoCommon/GraphicsModSystem/Runtime/Actions/MoveAction.h"
|
||||
#include "VideoCommon/GraphicsModSystem/Runtime/Actions/PrintAction.h"
|
||||
|
@ -35,16 +32,7 @@ std::unique_ptr<GraphicsModAction> Create(std::string_view name, const picojson:
|
|||
}
|
||||
else if (name == "custom_pipeline")
|
||||
{
|
||||
#ifdef ANDROID
|
||||
// Custom shaders currently need more than 8 pixel samplers
|
||||
// to be used with textures, rather than make things complicated
|
||||
// just disable the feature for Android which has issues
|
||||
ERROR_LOG_FMT(VIDEO, "Android needs more than 8 pixel samplers to function, {} provided",
|
||||
VideoCommon::MAX_PIXEL_SHADER_SAMPLERS);
|
||||
return nullptr;
|
||||
#else
|
||||
return CustomPipelineAction::Create(json_data, std::move(library));
|
||||
#endif
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
|
|
|
@ -225,7 +225,7 @@ void GraphicsModManager::Load(const GraphicsModGroupConfig& config)
|
|||
WARN_LOG_FMT(VIDEO,
|
||||
"Specified graphics mod asset '{}' for mod '{}' has an absolute path, you "
|
||||
"shouldn't release this to users.",
|
||||
asset.m_name, mod.m_title);
|
||||
asset.m_asset_id, mod.m_title);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -233,7 +233,7 @@ void GraphicsModManager::Load(const GraphicsModGroupConfig& config)
|
|||
}
|
||||
}
|
||||
|
||||
filesystem_library->SetAssetIDMapData(asset.m_name, std::move(asset_map));
|
||||
filesystem_library->SetAssetIDMapData(asset.m_asset_id, std::move(asset_map));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "Core/Config/MainSettings.h"
|
||||
#include "Core/Config/NetplaySettings.h"
|
||||
#include "Core/Movie.h"
|
||||
#include "Core/System.h"
|
||||
|
||||
#include "VideoCommon/AbstractGfx.h"
|
||||
#include "VideoCommon/AbstractPipeline.h"
|
||||
|
@ -284,26 +285,27 @@ void OnScreenUI::DrawDebugText()
|
|||
ImGui::GetIO().DisplaySize);
|
||||
if (ImGui::Begin("Movie", nullptr, ImGuiWindowFlags_NoFocusOnAppearing))
|
||||
{
|
||||
if (Movie::IsPlayingInput())
|
||||
auto& movie = Core::System::GetInstance().GetMovie();
|
||||
if (movie.IsPlayingInput())
|
||||
{
|
||||
ImGui::Text("Frame: %" PRIu64 " / %" PRIu64, Movie::GetCurrentFrame(),
|
||||
Movie::GetTotalFrames());
|
||||
ImGui::Text("Input: %" PRIu64 " / %" PRIu64, Movie::GetCurrentInputCount(),
|
||||
Movie::GetTotalInputCount());
|
||||
ImGui::Text("Frame: %" PRIu64 " / %" PRIu64, movie.GetCurrentFrame(),
|
||||
movie.GetTotalFrames());
|
||||
ImGui::Text("Input: %" PRIu64 " / %" PRIu64, movie.GetCurrentInputCount(),
|
||||
movie.GetTotalInputCount());
|
||||
}
|
||||
else if (Config::Get(Config::MAIN_SHOW_FRAME_COUNT))
|
||||
{
|
||||
ImGui::Text("Frame: %" PRIu64, Movie::GetCurrentFrame());
|
||||
ImGui::Text("Input: %" PRIu64, Movie::GetCurrentInputCount());
|
||||
ImGui::Text("Frame: %" PRIu64, movie.GetCurrentFrame());
|
||||
ImGui::Text("Input: %" PRIu64, movie.GetCurrentInputCount());
|
||||
}
|
||||
if (Config::Get(Config::MAIN_SHOW_LAG))
|
||||
ImGui::Text("Lag: %" PRIu64 "\n", Movie::GetCurrentLagCount());
|
||||
ImGui::Text("Lag: %" PRIu64 "\n", movie.GetCurrentLagCount());
|
||||
if (Config::Get(Config::MAIN_MOVIE_SHOW_INPUT_DISPLAY))
|
||||
ImGui::TextUnformatted(Movie::GetInputDisplay().c_str());
|
||||
ImGui::TextUnformatted(movie.GetInputDisplay().c_str());
|
||||
if (Config::Get(Config::MAIN_MOVIE_SHOW_RTC))
|
||||
ImGui::TextUnformatted(Movie::GetRTCDisplay().c_str());
|
||||
ImGui::TextUnformatted(movie.GetRTCDisplay().c_str());
|
||||
if (Config::Get(Config::MAIN_MOVIE_SHOW_RERECORD))
|
||||
ImGui::TextUnformatted(Movie::GetRerecords().c_str());
|
||||
ImGui::TextUnformatted(movie.GetRerecords().c_str());
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
|
|
|
@ -234,7 +234,7 @@ public:
|
|||
// process them.
|
||||
if (g_record_fifo_data && static_cast<Opcode>(data[0]) != Opcode::GX_CMD_CALL_DL)
|
||||
{
|
||||
FifoRecorder::GetInstance().WriteGPCommand(data, size);
|
||||
Core::System::GetInstance().GetFifoRecorder().WriteGPCommand(data, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
|
||||
#include "Core/DolphinAnalytics.h"
|
||||
#include "Core/HW/SystemTimers.h"
|
||||
#include "Core/System.h"
|
||||
|
||||
#include "VideoCommon/BPFunctions.h"
|
||||
#include "VideoCommon/VideoCommon.h"
|
||||
|
@ -24,7 +25,8 @@ static Common::EventHook s_before_frame_event =
|
|||
static Common::EventHook s_after_frame_event = AfterFrameEvent::Register(
|
||||
[] {
|
||||
DolphinAnalytics::PerformanceSample perf_sample;
|
||||
perf_sample.speed_ratio = SystemTimers::GetEstimatedEmulationPerformance();
|
||||
perf_sample.speed_ratio =
|
||||
Core::System::GetInstance().GetSystemTimers().GetEstimatedEmulationPerformance();
|
||||
perf_sample.num_prims = g_stats.this_frame.num_prims + g_stats.this_frame.num_dl_prims;
|
||||
perf_sample.num_draw_calls = g_stats.this_frame.num_draw_calls;
|
||||
DolphinAnalytics::Instance().ReportPerformanceInfo(std::move(perf_sample));
|
||||
|
|
|
@ -1349,9 +1349,9 @@ RcTcacheEntry TextureCacheBase::GetTexture(const int textureCacheSafetyColorSamp
|
|||
// its own memory modification tracking independent of the texture hashing below.
|
||||
if (OpcodeDecoder::g_record_fifo_data && !texture_info.IsFromTmem())
|
||||
{
|
||||
FifoRecorder::GetInstance().UseMemory(texture_info.GetRawAddress(),
|
||||
texture_info.GetFullLevelSize(),
|
||||
MemoryUpdate::Type::TextureMap);
|
||||
Core::System::GetInstance().GetFifoRecorder().UseMemory(texture_info.GetRawAddress(),
|
||||
texture_info.GetFullLevelSize(),
|
||||
MemoryUpdate::Type::TextureMap);
|
||||
}
|
||||
|
||||
// TODO: This doesn't hash GB tiles for preloaded RGBA8 textures (instead, it's hashing more data
|
||||
|
@ -2534,8 +2534,8 @@ void TextureCacheBase::CopyRenderTargetToTexture(
|
|||
u32 address = dstAddr;
|
||||
for (u32 i = 0; i < num_blocks_y; i++)
|
||||
{
|
||||
FifoRecorder::GetInstance().UseMemory(address, bytes_per_row, MemoryUpdate::Type::TextureMap,
|
||||
true);
|
||||
Core::System::GetInstance().GetFifoRecorder().UseMemory(address, bytes_per_row,
|
||||
MemoryUpdate::Type::TextureMap, true);
|
||||
address += dstStride;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "Common/EnumMap.h"
|
||||
#include "Common/Logging/Log.h"
|
||||
#include "Common/MathUtil.h"
|
||||
#include "Common/SmallVector.h"
|
||||
|
||||
#include "Core/ConfigManager.h"
|
||||
#include "Core/DolphinAnalytics.h"
|
||||
|
@ -546,7 +547,7 @@ void VertexManagerBase::Flush()
|
|||
if (g_ActiveConfig.bGraphicMods)
|
||||
{
|
||||
const double seconds_elapsed =
|
||||
static_cast<double>(m_ticks_elapsed) / SystemTimers::GetTicksPerSecond();
|
||||
static_cast<double>(m_ticks_elapsed) / system.GetSystemTimers().GetTicksPerSecond();
|
||||
pixel_shader_manager.constants.time_ms = seconds_elapsed * 1000;
|
||||
}
|
||||
|
||||
|
@ -554,7 +555,7 @@ void VertexManagerBase::Flush()
|
|||
// Calculate ZSlope for zfreeze
|
||||
const auto used_textures = UsedTextures();
|
||||
std::vector<std::string> texture_names;
|
||||
std::vector<u32> texture_units;
|
||||
Common::SmallVector<u32, 8> texture_units;
|
||||
if (!m_cull_all)
|
||||
{
|
||||
if (!g_ActiveConfig.bGraphicMods)
|
||||
|
@ -599,20 +600,18 @@ void VertexManagerBase::Flush()
|
|||
std::optional<CustomPixelShader> custom_pixel_shader;
|
||||
std::vector<std::string> custom_pixel_texture_names;
|
||||
std::span<u8> custom_pixel_shader_uniforms;
|
||||
bool skip = false;
|
||||
for (size_t i = 0; i < texture_names.size(); i++)
|
||||
{
|
||||
const std::string& texture_name = texture_names[i];
|
||||
const u32 texture_unit = texture_units[i];
|
||||
bool skip = false;
|
||||
GraphicsModActionData::DrawStarted draw_started{texture_unit, &skip, &custom_pixel_shader,
|
||||
GraphicsModActionData::DrawStarted draw_started{texture_units, &skip, &custom_pixel_shader,
|
||||
&custom_pixel_shader_uniforms};
|
||||
for (const auto& action : g_graphics_mod_manager->GetDrawStartedActions(texture_name))
|
||||
for (const auto& action : g_graphics_mod_manager->GetDrawStartedActions(texture_names[i]))
|
||||
{
|
||||
action->OnDrawStarted(&draw_started);
|
||||
if (custom_pixel_shader)
|
||||
{
|
||||
custom_pixel_shader_contents.shaders.push_back(*custom_pixel_shader);
|
||||
custom_pixel_texture_names.push_back(texture_name);
|
||||
custom_pixel_texture_names.push_back(texture_names[i]);
|
||||
}
|
||||
custom_pixel_shader = std::nullopt;
|
||||
}
|
||||
|
|
|
@ -369,7 +369,8 @@ bool VideoBackendBase::InitializeShared(std::unique_ptr<AbstractGfx> gfx,
|
|||
if (!g_vertex_manager->Initialize() || !g_shader_cache->Initialize() ||
|
||||
!g_perf_query->Initialize() || !g_presenter->Initialize() ||
|
||||
!g_framebuffer_manager->Initialize() || !g_texture_cache->Initialize() ||
|
||||
!g_bounding_box->Initialize() || !g_graphics_mod_manager->Initialize())
|
||||
(g_ActiveConfig.backend_info.bSupportsBBox && !g_bounding_box->Initialize()) ||
|
||||
!g_graphics_mod_manager->Initialize())
|
||||
{
|
||||
PanicAlertFmtT("Failed to initialize renderer classes");
|
||||
Shutdown();
|
||||
|
|
|
@ -47,8 +47,9 @@ static bool IsVSyncActive(bool enabled)
|
|||
|
||||
void UpdateActiveConfig()
|
||||
{
|
||||
if (Movie::IsPlayingInput() && Movie::IsConfigSaved())
|
||||
Movie::SetGraphicsConfig();
|
||||
auto& movie = Core::System::GetInstance().GetMovie();
|
||||
if (movie.IsPlayingInput() && movie.IsConfigSaved())
|
||||
movie.SetGraphicsConfig();
|
||||
g_ActiveConfig = g_Config;
|
||||
g_ActiveConfig.bVSyncActive = IsVSyncActive(g_ActiveConfig.bVSync);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue