Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions MarathonRecomp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -493,6 +493,7 @@ compile_pixel_shader(copy_depth_ps)
compile_pixel_shader(downsample_ps)
compile_vertex_shader(enhanced_burnout_blur_vs)
compile_pixel_shader(enhanced_burnout_blur_ps)
compile_pixel_shader(particle_depth_fade_ps)
compile_pixel_shader(movie_ps)
compile_pixel_shader(gamma_correction_ps)
compile_pixel_shader(imgui_ps)
Expand Down
8 changes: 8 additions & 0 deletions MarathonRecomp/gpu/guest_shader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ static std::unique_ptr<RenderShader> g_downsamplePSShader;

std::unique_ptr<GuestShader> g_conditionalSurveyPSShader;

static std::unique_ptr<RenderShader> g_particleDepthFadePSShader;

void LoadEmbeddedResources()
{
switch (g_backend)
Expand Down Expand Up @@ -54,6 +56,8 @@ void CreateReplacementShaders()
g_conditionalSurveyPSShader = std::make_unique<GuestShader>();
g_conditionalSurveyPSShader->type = ResourceType::PixelShader;
g_conditionalSurveyPSShader->shader = CREATE_SHADER(conditional_survey_ps);

g_particleDepthFadePSShader = CREATE_SHADER(particle_depth_fade_ps);
}

RenderShader* GetReplacementShader(const GuestShader* guestShader)
Expand All @@ -79,6 +83,10 @@ RenderShader* GetReplacementShader(const GuestShader* guestShader)
return g_MoviePSShader.get();
case 0x62573AB4C9EC44EE:
return g_downsamplePSShader.get();
case 0x402F6C3CD7D5B52E:
if (Config::ParticleFade == EParticleFade::Soft)
return g_particleDepthFadePSShader.get();
break;
default:
break;
}
Expand Down
44 changes: 44 additions & 0 deletions MarathonRecomp/gpu/shader/hlsl/particle_depth_fade_ps.hlsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#include "../../../../tools/XenosRecomp/XenosRecomp/shader_common.h"

#ifdef __spirv__

#define s0_Texture2DDescriptorIndex vk::RawBufferLoad<uint>(g_PushConstants.SharedConstants + 0)
#define s0_SamplerDescriptorIndex vk::RawBufferLoad<uint>(g_PushConstants.SharedConstants + 192)
#define g_DepthFadeParams vk::RawBufferLoad<float4>(g_PushConstants.SharedConstants + 320)
#define g_DepthFadeTextureIndex vk::RawBufferLoad<uint>(g_PushConstants.SharedConstants + 336)

#else

cbuffer SharedConstants : register(b2, space4)
{
uint s0_Texture2DDescriptorIndex : packoffset(c0.x);
uint s0_SamplerDescriptorIndex : packoffset(c12.x);
DEFINE_SHARED_CONSTANTS();
float4 g_DepthFadeParams : packoffset(c20);
uint g_DepthFadeTextureIndex : packoffset(c21.x);
};

#endif

struct Interpolators
{
float4 iPos : SV_Position;
float4 iTexCoord0 : TEXCOORD0;
float4 iTexCoord1 : TEXCOORD1;
};

float4 shaderMain(Interpolators input) : SV_Target0
{
float4 color = tfetch2D(s0_Texture2DDescriptorIndex, s0_SamplerDescriptorIndex, input.iTexCoord1.xy, float2(0, 0)) * input.iTexCoord0;

float4 depthFadeParams = g_DepthFadeParams;
float sceneDepth = g_Texture2DDescriptorHeap[g_DepthFadeTextureIndex].Load(int3(input.iPos.xy, 0)).x;
float sceneZ = depthFadeParams.y / (sceneDepth - depthFadeParams.x);
float fragmentZ = depthFadeParams.y / (input.iPos.z - depthFadeParams.x);
// https://developer.download.nvidia.com/whitepapers/2007/SDK10/SoftParticles_hi.pdf
float d = saturate((sceneZ - fragmentZ) * depthFadeParams.z);
float c = 0.5 * pow(saturate(2.0 * (d > 0.5 ? 1.0 - d : d)), 2.0);
color.w *= d > 0.5 ? 1.0 - c : c;

return color;
}
34 changes: 34 additions & 0 deletions MarathonRecomp/gpu/shader/msl/particle_depth_fade_ps.metal
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#include "../../../../tools/XenosRecomp/XenosRecomp/shader_common.h"

#define s0_Texture2DDescriptorIndex (*(reinterpret_cast<device uint*>(g_PushConstants.SharedConstants + 0)))
#define s0_SamplerDescriptorIndex (*(reinterpret_cast<device uint*>(g_PushConstants.SharedConstants + 192)))
#define g_DepthFadeParams (*(reinterpret_cast<device float4*>(g_PushConstants.SharedConstants + 320)))
#define g_DepthFadeTextureIndex (*(reinterpret_cast<device uint*>(g_PushConstants.SharedConstants + 336)))

struct Interpolators
{
float4 iPos [[position]];
float4 iTexCoord0 [[user(TEXCOORD0)]];
float4 iTexCoord1 [[user(TEXCOORD1)]];
};

[[fragment]]
float4 shaderMain(Interpolators input [[stage_in]],
constant Texture2DDescriptorHeap* g_Texture2DDescriptorHeap [[buffer(0)]],
constant SamplerDescriptorHeap* g_SamplerDescriptorHeap [[buffer(3)]],
constant PushConstants& g_PushConstants [[buffer(8)]])
{
float4 color = tfetch2D(g_Texture2DDescriptorHeap, g_SamplerDescriptorHeap,
s0_Texture2DDescriptorIndex, s0_SamplerDescriptorIndex, input.iTexCoord1.xy, float2(0, 0)) * input.iTexCoord0;

float4 depthFadeParams = g_DepthFadeParams;
float sceneDepth = g_Texture2DDescriptorHeap[g_DepthFadeTextureIndex].tex.read(uint2(input.iPos.xy)).x;
float sceneZ = depthFadeParams.y / (sceneDepth - depthFadeParams.x);
float fragmentZ = depthFadeParams.y / (input.iPos.z - depthFadeParams.x);
// https://developer.download.nvidia.com/whitepapers/2007/SDK10/SoftParticles_hi.pdf
float d = saturate((sceneZ - fragmentZ) * depthFadeParams.z);
float c = 0.5 * pow(saturate(2.0 * (d > 0.5 ? 1.0 - d : d)), 2.0);
color.w *= d > 0.5 ? 1.0 - c : c;

return color;
}
124 changes: 123 additions & 1 deletion MarathonRecomp/gpu/video.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include <patches/aspect_ratio_patches.h>
#include <plume_render_interface.h>
#include <ranges>
#include <shader/shader_cache.h>
#include <ui/achievement_menu.h>
#include <ui/achievement_overlay.h>
#include <ui/black_bar.h>
Expand Down Expand Up @@ -95,6 +96,8 @@ struct SharedConstants
float alphaThreshold{};
uint32_t conditionalSurveyIndex{};
uint32_t conditionalRenderingIndex{};
float depthFadeParams[4]{};
uint32_t depthFadeTextureIndex{};
};

// Depth bias values here are only used when the render device has
Expand Down Expand Up @@ -277,6 +280,10 @@ TextureDescriptorAllocator g_textureDescriptorAllocator;
std::unique_ptr<RenderPipelineLayout> g_pipelineLayout;
static xxHashMap<std::unique_ptr<RenderPipeline>> g_pipelines;

static constexpr float DEPTH_FADE_DISTANCE = 100.0f;
static bool g_depthFadeTextureDirty = true;
static GuestTexture* g_depthFadeTexture;

#ifdef ASYNC_PSO_DEBUG
static std::atomic<uint32_t> g_pipelinesCreatedInRenderThread;
static std::atomic<uint32_t> g_pipelinesCreatedAsynchronously;
Expand Down Expand Up @@ -2967,6 +2974,7 @@ static void ProcRenderCommand(const RenderCommands::SetDepthStencilSurface& args
{
SetDirtyValue(g_dirtyStates.renderTargetAndDepthStencil, g_depthStencil, args.depthStencil);
SetDirtyValue(g_dirtyStates.pipelineState, g_pipelineState.depthStencilFormat, args.depthStencil != nullptr ? args.depthStencil->format : RenderFormat::UNKNOWN);
g_depthFadeTextureDirty = true;
}

static bool UseShaderResolve(GuestSurface* surface)
Expand Down Expand Up @@ -3289,6 +3297,7 @@ static void ProcRenderCommand(const RenderCommands::Clear& args)
}

commandList->clearDepthStencil(clearDepth, clearStencil, args.z, args.stencil);
g_depthFadeTextureDirty = true;
}

PropagateDeferredClear(g_renderTarget, g_depthStencil, args);
Expand Down Expand Up @@ -3580,11 +3589,108 @@ static void ProcRenderCommand(const RenderCommands::AddPipeline& args)
static constexpr int32_t COMMON_DEPTH_BIAS_VALUE = static_cast<int32_t>((1 << 24) * 0.002f);
static constexpr float COMMON_SLOPE_SCALED_DEPTH_BIAS_VALUE = 1.0f;

static void CopyDepthForDepthFade(GuestSurface* depthStencil)
{
if (g_depthFadeTexture == nullptr ||
g_depthFadeTexture->width != depthStencil->width ||
g_depthFadeTexture->height != depthStencil->height)
{
if (g_depthFadeTexture != nullptr)
g_tempResources[g_renderFrame].push_back(g_depthFadeTexture);

g_depthFadeTexture = CreateTexture(depthStencil->width, depthStencil->height, 1, 1, 0, D3DFMT_D24FS8, 0, 3);
}

AddBarrier(depthStencil, RenderTextureLayout::SHADER_READ);
AddBarrier(g_depthFadeTexture, RenderTextureLayout::DEPTH_WRITE);
FlushBarriers();

RenderPipeline* pipeline;

switch (depthStencil->sampleCount)
{
case RenderSampleCount::COUNT_2:
pipeline = g_resolveMsaaDepthPipelines[0].get();
break;
case RenderSampleCount::COUNT_4:
pipeline = g_resolveMsaaDepthPipelines[1].get();
break;
case RenderSampleCount::COUNT_8:
pipeline = g_resolveMsaaDepthPipelines[2].get();
break;
default:
pipeline = g_copyDepthPipeline.get();
break;
}

auto& framebuffer = g_depthFadeTexture->framebuffers[0];
if (framebuffer == nullptr)
{
RenderFramebufferDesc desc;
desc.depthAttachment = g_depthFadeTexture->texture;
framebuffer = g_device->createFramebuffer(desc);
}

auto& commandList = g_commandLists[g_frame];

if (g_framebuffer != framebuffer.get())
{
commandList->setFramebuffer(framebuffer.get());
g_framebuffer = framebuffer.get();
}

commandList->setPipeline(pipeline);
commandList->setViewports(RenderViewport(0.0f, 0.0f, float(g_depthFadeTexture->width), float(g_depthFadeTexture->height), 0.0f, 1.0f));
commandList->setScissors(RenderRect(0, 0, g_depthFadeTexture->width, g_depthFadeTexture->height));
commandList->setGraphicsPushConstants(0, &depthStencil->descriptorIndex, 0, sizeof(uint32_t));
commandList->drawInstanced(6, 1, 0, 0);

g_dirtyStates.renderTargetAndDepthStencil = true;
g_dirtyStates.viewport = true;
g_dirtyStates.pipelineState = true;
g_dirtyStates.scissorRect = true;

if (g_backend != Backend::D3D12)
{
g_dirtyStates.vertexShaderConstants = true; // The push constant call invalidates vertex shader constants.
g_dirtyStates.depthBias = true; // Static depth bias in copy pipeline invalidates dynamic depth bias.
}
}

static void SetDepthFadeConstants()
{
// Clip z and clip w rows of g_MatVP (c90, c91) in the particle vertex shader.
auto clipZ = [](uint32_t i) { return std::bit_cast<float>(ByteSwap(g_vertexShaderConstants[90 * 4 + i])); };
auto clipW = [](uint32_t i) { return std::bit_cast<float>(ByteSwap(g_vertexShaderConstants[91 * 4 + i])); };

// clip.z = A * clip.w + B, take A from the largest component for precision.
uint32_t component = 0;
for (uint32_t i = 1; i < 3; i++)
{
if (std::abs(clipW(i)) > std::abs(clipW(component)))
component = i;
}

float a = clipZ(component) / clipW(component);
float b = clipZ(3) - a * clipW(3);

// Fold the viewport depth range in so the shader can linearize window depth directly.
float depthRange = g_viewport.maxDepth - g_viewport.minDepth;

SetDirtyValue(g_dirtyStates.sharedConstants, g_sharedConstants.depthFadeParams[0], g_viewport.minDepth + a * depthRange);
SetDirtyValue(g_dirtyStates.sharedConstants, g_sharedConstants.depthFadeParams[1], b * depthRange);
SetDirtyValue(g_dirtyStates.sharedConstants, g_sharedConstants.depthFadeParams[2], 1.0f / DEPTH_FADE_DISTANCE);
SetDirtyValue(g_dirtyStates.sharedConstants, g_sharedConstants.depthFadeTextureIndex, g_depthFadeTexture->descriptorIndex);
}

static void FlushRenderStateForRenderThread()
{
auto renderTarget = g_pipelineState.colorWriteEnable ? g_renderTarget : nullptr;
auto depthStencil = g_pipelineState.zEnable || g_pipelineState.stencilEnable ? g_depthStencil : nullptr;

if (depthStencil != nullptr && g_pipelineState.zWriteEnable)
g_depthFadeTextureDirty = true;

bool foundAny = PopulateBarriersForStretchRect(renderTarget, depthStencil);

auto aliasesBoundStorage = [&](const GuestSurface* surface)
Expand Down Expand Up @@ -3632,6 +3738,21 @@ static void FlushRenderStateForRenderThread()
if (!g_pendingResolves.empty())
g_pendingResolves.clear();


if (Config::ParticleFade == EParticleFade::Soft &&
g_pipelineState.pixelShader != nullptr &&
g_pipelineState.pixelShader->shaderCacheEntry->hash == 0x402F6C3CD7D5B52E)
{
if (g_depthFadeTextureDirty)
{
CopyDepthForDepthFade(depthStencil);
g_depthFadeTextureDirty = false;
}

AddBarrier(g_depthFadeTexture, RenderTextureLayout::SHADER_READ);
SetDepthFadeConstants();
}

AddBarrier(renderTarget, RenderTextureLayout::COLOR_WRITE);
AddBarrier(depthStencil, RenderTextureLayout::DEPTH_WRITE);

Expand Down Expand Up @@ -3884,7 +4005,8 @@ void VideoConfigValueChangedCallback(const IConfigDef* config)
bool shouldRecompile =
config == &Config::AntiAliasing ||
config == &Config::TransparencyAntiAliasing ||
config == &Config::RadialBlur;
config == &Config::RadialBlur ||
config == &Config::ParticleFade;

if (shouldRecompile)
EnqueuePipelineRecompilation();
Expand Down
56 changes: 56 additions & 0 deletions MarathonRecomp/locale/config_locale.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -837,6 +837,62 @@ CONFIG_DEFINE_ENUM_LOCALE(ERadialBlur)
}
};

CONFIG_DEFINE_LOCALE(ParticleFade)
{
{ ELanguage::English, { "Particle Fade", "Change how particle edges blend." } },
{ ELanguage::Japanese, { "Particle Fade", "Change how particle edges blend." } },
{ ELanguage::German, { "Particle Fade", "Change how particle edges blend." } },
{ ELanguage::French, { "Particle Fade", "Change how particle edges blend." } },
{ ELanguage::Spanish, { "Particle Fade", "Change how particle edges blend." } },
{ ELanguage::Italian, { "Particle Fade", "Change how particle edges blend." } },
};

CONFIG_DEFINE_ENUM_LOCALE(EParticleFade)
{
{
ELanguage::English,
{
{ EParticleFade::Original, { "Original", "" } },
{ EParticleFade::Soft, { "Soft", "Soft: softens the edges of particles to reduce clipping artifacts." } }
}
},
{
ELanguage::Japanese,
{
{ EParticleFade::Original, { "Original", "" } },
{ EParticleFade::Soft, { "Soft", "Soft: softens the edges of particles to reduce clipping artifacts." } }
}
},
{
ELanguage::German,
{
{ EParticleFade::Original, { "Original", "" } },
{ EParticleFade::Soft, { "Soft", "Soft: softens the edges of particles to reduce clipping artifacts." } }
}
},
{
ELanguage::French,
{
{ EParticleFade::Original, { "Original", "" } },
{ EParticleFade::Soft, { "Soft", "Soft: softens the edges of particles to reduce clipping artifacts." } }
}
},
{
ELanguage::Spanish,
{
{ EParticleFade::Original, { "Original", "" } },
{ EParticleFade::Soft, { "Soft", "Soft: softens the edges of particles to reduce clipping artifacts." } }
}
},
{
ELanguage::Italian,
{
{ EParticleFade::Original, { "Original", "" } },
{ EParticleFade::Soft, { "Soft", "Soft: softens the edges of particles to reduce clipping artifacts." } }
}
}
};

CONFIG_DEFINE_LOCALE(CutsceneAspectRatio)
{
{ ELanguage::English, { "Cutscene Aspect Ratio", "Change the aspect ratio of the real-time cutscenes." } },
Expand Down
1 change: 1 addition & 0 deletions MarathonRecomp/ui/options_menu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -867,6 +867,7 @@ static void DrawOptions(ImVec2 min, ImVec2 max)
DrawOption(rowCount++, &Config::ShadowResolution, true);
DrawOption(rowCount++, &Config::ReflectionResolution, true);
DrawOption(rowCount++, &Config::RadialBlur, true);
DrawOption(rowCount++, &Config::ParticleFade, true);
DrawOption(rowCount++, &Config::CutsceneAspectRatio, true);
DrawOption(rowCount++, &Config::UIAlignmentMode, true);

Expand Down
6 changes: 6 additions & 0 deletions MarathonRecomp/user/config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,12 @@ CONFIG_DEFINE_ENUM_TEMPLATE(ERadialBlur)
{ "Enhanced", ERadialBlur::Enhanced }
};

CONFIG_DEFINE_ENUM_TEMPLATE(EParticleFade)
{
{ "Original", EParticleFade::Original },
{ "Soft", EParticleFade::Soft },
};

CONFIG_DEFINE_ENUM_TEMPLATE(ECutsceneAspectRatio)
{
{ "Original", ECutsceneAspectRatio::Original },
Expand Down
6 changes: 6 additions & 0 deletions MarathonRecomp/user/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,12 @@ enum class ERadialBlur : uint32_t
Enhanced
};

enum class EParticleFade : uint32_t
{
Original,
Soft
};

enum class ECutsceneAspectRatio : uint32_t
{
Original,
Expand Down
Loading
Loading