Compare commits

...

2 commits

Author SHA1 Message Date
squidbus
4744a5e1a2
Merge 1c3ffe0f41 into 3c1badf183 2025-10-02 02:11:32 +00:00
squidbus
1c3ffe0f41 Add support for Metal on macOS.
Co-authored-by: Isaac Marovitz <isaacryu@icloud.com>
2025-10-01 18:50:40 -07:00
74 changed files with 1226 additions and 159 deletions

View file

@ -233,11 +233,6 @@ jobs:
token: ${{ secrets.ASSET_REPO_TOKEN }}
path: ./private
- name: Setup latest Xcode
uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: latest-stable
- name: Setup ccache
uses: hendrikmuhs/ccache-action@v1.2
with:

2
.gitmodules vendored
View file

@ -6,7 +6,7 @@
url = https://github.com/redorav/ddspp.git
[submodule "tools/XenosRecomp"]
path = tools/XenosRecomp
url = https://github.com/hedge-dev/XenosRecomp.git
url = https://github.com/squidbus/XenosRecomp.git
[submodule "UnleashedRecompResources"]
path = UnleashedRecompResources
url = https://github.com/hedge-dev/UnleashedRecompResources.git

View file

@ -40,6 +40,11 @@ if (UNLEASHED_RECOMP_ARCHITECTURE STREQUAL "x86_64" OR UNLEASHED_RECOMP_ARCHITEC
)
endif()
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
# Normally only defined by Visual Studio, added for consistency
add_compile_definitions(_DEBUG)
endif()
add_subdirectory(${UNLEASHED_RECOMP_THIRDPARTY_ROOT})
add_subdirectory(${UNLEASHED_RECOMP_TOOLS_ROOT})

View file

@ -4,6 +4,10 @@ if (WIN32)
option(UNLEASHED_RECOMP_D3D12 "Add D3D12 support for rendering" ON)
endif()
if (APPLE)
option(UNLEASHED_RECOMP_METAL "Add Metal support for rendering" ON)
endif()
if (CMAKE_SYSTEM_NAME MATCHES "Linux")
option(UNLEASHED_RECOMP_FLATPAK "Configure the build for Flatpak compatibility." OFF)
endif()
@ -255,6 +259,10 @@ if (NOT ${CMAKE_BUILD_TYPE} MATCHES "Release")
set(SHOW_GIT_INFO_AND_BUILD_TYPE 1)
endif()
if (UNLEASHED_RECOMP_METAL)
set(XCRUN_TOOL "/usr/bin/xcrun")
endif()
GenerateVersionSources(
OUTPUT_DIR ${PROJECT_SOURCE_DIR}
VERSION_TXT ${VERSION_TXT}
@ -375,6 +383,10 @@ if (UNLEASHED_RECOMP_D3D12)
)
endif()
if (UNLEASHED_RECOMP_METAL)
target_compile_definitions(UnleashedRecomp PRIVATE UNLEASHED_RECOMP_METAL)
endif()
if (WIN32)
target_link_libraries(UnleashedRecomp PRIVATE
comctl32
@ -418,22 +430,37 @@ endif()
target_precompile_headers(UnleashedRecomp PUBLIC ${UNLEASHED_RECOMP_PRECOMPILED_HEADERS})
function(compile_shader FILE_PATH TARGET_NAME)
set(FILE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/gpu/shader/${FILE_PATH}.hlsl)
cmake_path(GET FILE_PATH STEM VARIABLE_NAME)
set(HLSL_FILE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/gpu/shader/hlsl/${FILE_PATH}.hlsl)
set(MSL_FILE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/gpu/shader/msl/${FILE_PATH}.metal)
cmake_path(GET HLSL_FILE_PATH STEM HLSL_NAME)
cmake_path(GET MSL_FILE_PATH STEM MSL_NAME)
if (UNLEASHED_RECOMP_METAL)
add_custom_command(
OUTPUT ${MSL_FILE_PATH}.ir
COMMAND ${XCRUN_TOOL} -sdk macosx metal -o ${MSL_FILE_PATH}.ir -c ${MSL_FILE_PATH} -D__air__ -frecord-sources -gline-tables-only
DEPENDS ${MSL_FILE_PATH}
)
add_custom_command(
OUTPUT ${MSL_FILE_PATH}.metallib
COMMAND ${XCRUN_TOOL} -sdk macosx metallib -o ${MSL_FILE_PATH}.metallib ${MSL_FILE_PATH}.ir
DEPENDS ${MSL_FILE_PATH}.ir
)
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${MSL_FILE_PATH}.metallib" DEST_FILE "${MSL_FILE_PATH}.metallib" ARRAY_NAME "g_${MSL_NAME}_air")
endif()
if (UNLEASHED_RECOMP_D3D12)
add_custom_command(
OUTPUT ${FILE_PATH}.dxil.h
COMMAND ${DIRECTX_DXC_TOOL} -T ${TARGET_NAME} -HV 2021 -all-resources-bound -Wno-ignored-attributes -Fh ${FILE_PATH}.dxil.h ${FILE_PATH} -Vn g_${VARIABLE_NAME}_dxil
DEPENDS ${FILE_PATH}
OUTPUT ${HLSL_FILE_PATH}.dxil.h
COMMAND ${DIRECTX_DXC_TOOL} -T ${TARGET_NAME} -HV 2021 -all-resources-bound -Wno-ignored-attributes -E shaderMain -Fh ${HLSL_FILE_PATH}.dxil.h ${HLSL_FILE_PATH} -Vn g_${HLSL_NAME}_dxil
DEPENDS ${HLSL_FILE_PATH}
)
target_sources(UnleashedRecomp PRIVATE ${FILE_PATH}.dxil.h)
target_sources(UnleashedRecomp PRIVATE ${HLSL_FILE_PATH}.dxil.h)
endif()
add_custom_command(
OUTPUT ${FILE_PATH}.spirv.h
COMMAND ${DIRECTX_DXC_TOOL} -T ${TARGET_NAME} -HV 2021 -all-resources-bound -spirv -fvk-use-dx-layout ${ARGN} -Fh ${FILE_PATH}.spirv.h ${FILE_PATH} -Vn g_${VARIABLE_NAME}_spirv
DEPENDS ${FILE_PATH}
OUTPUT ${HLSL_FILE_PATH}.spirv.h
COMMAND ${DIRECTX_DXC_TOOL} -T ${TARGET_NAME} -HV 2021 -all-resources-bound -spirv -fvk-use-dx-layout ${ARGN} -E shaderMain -Fh ${HLSL_FILE_PATH}.spirv.h ${HLSL_FILE_PATH} -Vn g_${HLSL_NAME}_spirv
DEPENDS ${HLSL_FILE_PATH}
)
target_sources(UnleashedRecomp PRIVATE ${FILE_PATH}.spirv.h)
target_sources(UnleashedRecomp PRIVATE ${HLSL_FILE_PATH}.spirv.h)
endfunction()
function(compile_vertex_shader FILE_PATH)

View file

@ -13,7 +13,7 @@
#define IMGUI_SHADER_MODIFIER_RECTANGLE_BEVEL 10
#define IMGUI_SHADER_MODIFIER_LOW_QUALITY_TEXT 11
#ifdef __cplusplus
#if defined(__cplusplus) && !defined(__air__)
enum class ImGuiCallback : int32_t
{

View file

@ -1,4 +1,4 @@
#include "../../../tools/XenosRecomp/XenosRecomp/shader_common.h"
#include "../../../../tools/XenosRecomp/XenosRecomp/shader_common.h"
#ifdef __spirv__
@ -22,7 +22,7 @@ cbuffer SharedConstants : register(b2, space4)
#endif
float4 main(
float4 shaderMain(
in float4 iPos : SV_Position,
in float4 iTexCoord0 : TEXCOORD0) : SV_Target0
{

View file

@ -2,7 +2,7 @@
Texture2D<float4> g_Texture2DDescriptorHeap[] : register(t0, space0);
float4 main(in float4 position : SV_Position) : SV_Target
float4 shaderMain(in float4 position : SV_Position) : SV_Target
{
return g_Texture2DDescriptorHeap[g_PushConstants.ResourceDescriptorIndex].Load(int3(position.xy, 0));
}

View file

@ -2,7 +2,7 @@
Texture2D<float> g_Texture2DDescriptorHeap[] : register(t0, space0);
float main(in float4 position : SV_Position) : SV_Depth
float shaderMain(in float4 position : SV_Position) : SV_Depth
{
return g_Texture2DDescriptorHeap[g_PushConstants.ResourceDescriptorIndex].Load(int3(position.xy, 0));
}

View file

@ -1,4 +1,4 @@
void main(in uint vertexId : SV_VertexID, out float4 position : SV_Position, out float2 texCoord : TEXCOORD)
void shaderMain(in uint vertexId : SV_VertexID, out float4 position : SV_Position, out float2 texCoord : TEXCOORD)
{
texCoord = float2((vertexId << 1) & 2, vertexId & 2);
position = float4(texCoord * float2(2.0, -2.0) + float2(-1.0, 1.0), 0.0, 1.0);

View file

@ -1,4 +1,4 @@
#include "../../../tools/XenosRecomp/XenosRecomp/shader_common.h"
#include "../../../../tools/XenosRecomp/XenosRecomp/shader_common.h"
#ifdef __spirv__
@ -16,7 +16,7 @@ cbuffer SharedConstants : register(b2, space4)
#endif
float4 main(
float4 shaderMain(
in float4 iPosition : SV_Position,
in float4 iTexCoord0 : TEXCOORD0,
in float4 iTexCoord1 : TEXCOORD1) : SV_Target

View file

@ -1,4 +1,4 @@
#include "../../../tools/XenosRecomp/XenosRecomp/shader_common.h"
#include "../../../../tools/XenosRecomp/XenosRecomp/shader_common.h"
#ifdef __spirv__
@ -20,7 +20,7 @@ cbuffer SharedConstants : register(b2, space4)
#endif
void main(
void shaderMain(
[[vk::location(0)]] in float4 iPosition0 : POSITION0,
[[vk::location(8)]] in float4 iColor0 : COLOR0,
out float4 oPos : SV_Position,

View file

@ -1,4 +1,4 @@
#include "../../../tools/XenosRecomp/XenosRecomp/shader_common.h"
#include "../../../../tools/XenosRecomp/XenosRecomp/shader_common.h"
#ifdef __spirv__
@ -20,7 +20,7 @@ cbuffer SharedConstants : register(b2, space4)
#endif
void main(
void shaderMain(
[[vk::location(0)]] in float4 iPosition0 : POSITION0,
[[vk::location(8)]] in float4 iColor0 : COLOR0,
[[vk::location(4)]] in float4 iTexCoord0 : TEXCOORD0,

View file

@ -1,4 +1,4 @@
#include "../../../tools/XenosRecomp/XenosRecomp/shader_common.h"
#include "../../../../tools/XenosRecomp/XenosRecomp/shader_common.h"
#ifdef __spirv__
@ -38,7 +38,7 @@ cbuffer SharedConstants : register(b2, space4)
#endif
float4 main(in float4 position : SV_Position, in float4 texCoord : TEXCOORD0) : SV_Target
float4 shaderMain(in float4 position : SV_Position, in float4 texCoord : TEXCOORD0) : SV_Target
{
Texture2D<float4> sampColor = g_Texture2DDescriptorHeap[sampColor_Texture2DDescriptorIndex];
Texture2D<float4> sampVelocityMap = g_Texture2DDescriptorHeap[sampVelocityMap_Texture2DDescriptorIndex];

View file

@ -1,4 +1,4 @@
#include "../../../tools/XenosRecomp/XenosRecomp/shader_common.h"
#include "../../../../tools/XenosRecomp/XenosRecomp/shader_common.h"
#ifdef __spirv__
@ -20,7 +20,7 @@ cbuffer SharedConstants : register(b2, space4)
#endif
float4 main(in float4 position : SV_Position) : SV_Target
float4 shaderMain(in float4 position : SV_Position) : SV_Target
{
Texture2D<float4> texture = g_Texture2DDescriptorHeap[g_TextureDescriptorIndex];

View file

@ -1,4 +1,4 @@
#include "../../../tools/XenosRecomp/XenosRecomp/shader_common.h"
#include "../../../../tools/XenosRecomp/XenosRecomp/shader_common.h"
#ifdef __spirv__
@ -40,7 +40,7 @@ float ComputeWeight(float x)
return exp(-(x * x) / (2.0 * std * std)) / (std * sqrt(2.0 * PI));
}
float4 main(in float4 iPosition : SV_Position, in float4 iTexCoord0 : TEXCOORD0) : SV_Target
float4 shaderMain(in float4 iPosition : SV_Position, in float4 iTexCoord0 : TEXCOORD0) : SV_Target
{
Texture2D<float4> texture = g_Texture2DDescriptorHeap[s0_Texture2DDescriptorIndex];
SamplerState samplerState = g_SamplerDescriptorHeap[s0_SamplerDescriptorIndex];

View file

@ -1,6 +1,6 @@
#pragma once
#include "../imgui/imgui_common.h"
#include "../../imgui/imgui_common.h"
struct PushConstants
{

View file

@ -123,7 +123,7 @@ float4 SampleSdfFont(float4 color, Texture2D<float4> texture, float2 uv, float2
return color;
}
float4 main(in Interpolators interpolators) : SV_Target
float4 shaderMain(in Interpolators interpolators) : SV_Target
{
float4 color = interpolators.Color;
color *= PixelAntialiasing(interpolators.Position.xy - g_PushConstants.ProceduralOrigin);

View file

@ -1,6 +1,6 @@
#include "imgui_common.hlsli"
void main(in float2 position : POSITION, in float2 uv : TEXCOORD, in float4 color : COLOR, out Interpolators interpolators)
void shaderMain(in float2 position : POSITION, in float2 uv : TEXCOORD, in float4 color : COLOR, out Interpolators interpolators)
{
if (g_PushConstants.ShaderModifier == IMGUI_SHADER_MODIFIER_TEXT_SKEW)
{

View file

@ -1,6 +1,6 @@
#pragma once
#include "../../../tools/XenosRecomp/XenosRecomp/shader_common.h"
#include "../../../../tools/XenosRecomp/XenosRecomp/shader_common.h"
#ifdef __spirv__

View file

@ -1,6 +1,6 @@
#include "movie_common.hlsli"
PixelShaderOutput main(in Interpolators In)
PixelShaderOutput shaderMain(in Interpolators In)
{
Texture2D<float4> Tex0 = g_Texture2DDescriptorHeap[Tex0_ResourceDescriptorIndex];
Texture2D<float4> Tex1 = g_Texture2DDescriptorHeap[Tex1_ResourceDescriptorIndex];

View file

@ -1,6 +1,6 @@
#include "movie_common.hlsli"
Interpolators main(in VertexShaderInput In)
Interpolators shaderMain(in VertexShaderInput In)
{
Interpolators Out;
Out.ProjPos = In.ObjPos;

View file

@ -4,7 +4,7 @@
Texture2DMS<float4, SAMPLE_COUNT> g_Texture2DMSDescriptorHeap[] : register(t0, space0);
float4 main(in float4 position : SV_Position) : SV_Target
float4 shaderMain(in float4 position : SV_Position) : SV_Target
{
float4 result = g_Texture2DMSDescriptorHeap[g_PushConstants.ResourceDescriptorIndex].Load(int2(position.xy), 0);

View file

@ -4,7 +4,7 @@
Texture2DMS<float, SAMPLE_COUNT> g_Texture2DMSDescriptorHeap[] : register(t0, space0);
float main(in float4 position : SV_Position) : SV_Depth
float shaderMain(in float4 position : SV_Position) : SV_Depth
{
float result = g_Texture2DMSDescriptorHeap[g_PushConstants.ResourceDescriptorIndex].Load(int2(position.xy), 0);

View file

@ -0,0 +1,4 @@
*.ir
*.metallib
*.metal.*.c
*.metal.*.h

View file

@ -0,0 +1,31 @@
#include "../../../../tools/XenosRecomp/XenosRecomp/shader_common.h"
#define g_SrcAlpha_DestAlpha (*(reinterpret_cast<device float4*>(g_PushConstants.PixelShaderConstants + 2400)))
#define s0_Texture2DDescriptorIndex (*(reinterpret_cast<device uint*>(g_PushConstants.SharedConstants + 0)))
#define s0_SamplerDescriptorIndex (*(reinterpret_cast<device uint*>(g_PushConstants.SharedConstants + 192)))
struct Interpolators
{
float4 iTexCoord0 [[user(TEXCOORD0)]];
};
[[fragment]]
float4 shaderMain(float4 iPos [[position]],
Interpolators input [[stage_in]],
constant Texture2DDescriptorHeap* g_Texture2DDescriptorHeap [[buffer(0)]],
constant SamplerDescriptorHeap* g_SamplerDescriptorHeap [[buffer(3)]],
constant PushConstants& g_PushConstants [[buffer(8)]])
{
texture2d<float> texture = g_Texture2DDescriptorHeap[s0_Texture2DDescriptorIndex].tex;
sampler samplerState = g_SamplerDescriptorHeap[s0_SamplerDescriptorIndex].samp;
float4 color = texture.sample(samplerState, input.iTexCoord0.xy);
if (any(input.iTexCoord0.xy < 0.0 || input.iTexCoord0.xy > 1.0))
color = float4(0.0, 0.0, 0.0, 1.0);
color.rgb *= color.a * g_SrcAlpha_DestAlpha.x;
color.a = g_SrcAlpha_DestAlpha.y + (1.0 - color.a) * g_SrcAlpha_DestAlpha.x;
return color;
}

View file

@ -0,0 +1,14 @@
#include "copy_common.metali"
struct Texture2DDescriptorHeap
{
texture2d<float> tex;
};
[[fragment]]
float4 shaderMain(float4 position [[position]],
constant Texture2DDescriptorHeap* g_Texture2DDescriptorHeap [[buffer(0)]],
constant PushConstants& g_PushConstants [[buffer(8)]])
{
return g_Texture2DDescriptorHeap[g_PushConstants.ResourceDescriptorIndex].tex.read(uint2(position.xy), 0);
}

View file

@ -0,0 +1,9 @@
#pragma once
#include <metal_stdlib>
using namespace metal;
struct PushConstants
{
uint ResourceDescriptorIndex;
};

View file

@ -0,0 +1,23 @@
#include "copy_common.metali"
struct Texture2DDescriptorHeap
{
texture2d<float> tex;
};
struct PixelShaderOutput
{
float oDepth [[depth(any)]];
};
[[fragment]]
PixelShaderOutput shaderMain(float4 position [[position]],
constant Texture2DDescriptorHeap* g_Texture2DDescriptorHeap [[buffer(0)]],
constant PushConstants& g_PushConstants [[buffer(8)]])
{
PixelShaderOutput output = PixelShaderOutput{};
output.oDepth = g_Texture2DDescriptorHeap[g_PushConstants.ResourceDescriptorIndex].tex.read(uint2(position.xy), 0).x;
return output;
}

View file

@ -0,0 +1,16 @@
struct Interpolators
{
float4 position [[position]];
float2 texCoord [[user(TEXCOORD)]];
};
[[vertex]]
Interpolators shaderMain(uint vertexId [[vertex_id]])
{
Interpolators interpolators;
interpolators.texCoord = float2((vertexId << 1) & 2, vertexId & 2);
interpolators.position = float4(interpolators.texCoord * float2(2.0, -2.0) + float2(-1.0, 1.0), 0.0, 1.0);
return interpolators;
}

View file

@ -0,0 +1,38 @@
#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)))
struct Interpolators
{
float4 iPosition [[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)]])
{
texture2d<float> texture = g_Texture2DDescriptorHeap[s0_Texture2DDescriptorIndex].tex;
sampler samplerState = g_SamplerDescriptorHeap[s0_SamplerDescriptorIndex].samp;
uint2 dimensions = getTexture2DDimensions(texture);
// https://www.shadertoy.com/view/csX3RH
float2 uvTexspace = input.iTexCoord1.xy * float2(dimensions);
float2 seam = floor(uvTexspace + 0.5);
uvTexspace = (uvTexspace - seam) / fwidth(uvTexspace) + seam;
uvTexspace = clamp(uvTexspace, seam - 0.5, seam + 0.5);
float2 texCoord = uvTexspace / float2(dimensions);
float4 color = texture.sample(samplerState, texCoord);
color *= input.iTexCoord0;
// The game enables alpha test for CSD, but the alpha threshold doesn't seem to be assigned anywhere? Weird.
clip(color.a - g_AlphaThreshold);
return color;
}

View file

@ -0,0 +1,64 @@
#include "../../../../tools/XenosRecomp/XenosRecomp/shader_common.h"
#define g_ViewportSize (*(reinterpret_cast<device float4*>(g_PushConstants.VertexShaderConstants + 2880)))
#define g_Z (*(reinterpret_cast<device float4*>(g_PushConstants.VertexShaderConstants + 3936)))
struct VertexShaderInput
{
float4 iPosition0 [[attribute(0)]];
float4 iColor0 [[attribute(8)]];
};
struct Interpolators
{
float4 oPos [[position]];
float4 oTexCoord0 [[user(TEXCOORD0)]];
float4 oTexCoord1 [[user(TEXCOORD1)]];
float4 oTexCoord2 [[user(TEXCOORD2)]];
float4 oTexCoord3 [[user(TEXCOORD3)]];
float4 oTexCoord4 [[user(TEXCOORD4)]];
float4 oTexCoord5 [[user(TEXCOORD5)]];
float4 oTexCoord6 [[user(TEXCOORD6)]];
float4 oTexCoord7 [[user(TEXCOORD7)]];
float4 oTexCoord8 [[user(TEXCOORD8)]];
float4 oTexCoord9 [[user(TEXCOORD9)]];
float4 oTexCoord10 [[user(TEXCOORD10)]];
float4 oTexCoord11 [[user(TEXCOORD11)]];
float4 oTexCoord12 [[user(TEXCOORD12)]];
float4 oTexCoord13 [[user(TEXCOORD13)]];
float4 oTexCoord14 [[user(TEXCOORD14)]];
float4 oTexCoord15 [[user(TEXCOORD15)]];
float4 oColor0 [[user(COLOR0)]];
float4 oColor1 [[user(COLOR1)]];
};
[[vertex]]
Interpolators shaderMain(VertexShaderInput input [[stage_in]],
constant PushConstants& g_PushConstants [[buffer(8)]])
{
Interpolators output;
output.oPos.xy = input.iPosition0.xy * g_ViewportSize.zw * float2(2.0, -2.0) + float2(-1.0, 1.0);
output.oPos.z = g_Z.x;
output.oPos.w = 1.0;
output.oTexCoord0 = input.iColor0.wxyz;
output.oTexCoord1 = 0.0;
output.oTexCoord2 = 0.0;
output.oTexCoord3 = 0.0;
output.oTexCoord4 = 0.0;
output.oTexCoord5 = 0.0;
output.oTexCoord6 = 0.0;
output.oTexCoord7 = 0.0;
output.oTexCoord8 = 0.0;
output.oTexCoord9 = 0.0;
output.oTexCoord10 = 0.0;
output.oTexCoord11 = 0.0;
output.oTexCoord12 = 0.0;
output.oTexCoord13 = 0.0;
output.oTexCoord14 = 0.0;
output.oTexCoord15 = 0.0;
output.oColor0 = 0.0;
output.oColor1 = 0.0;
return output;
}

View file

@ -0,0 +1,66 @@
#include "../../../../tools/XenosRecomp/XenosRecomp/shader_common.h"
#define g_ViewportSize (*(reinterpret_cast<device float4*>(g_PushConstants.VertexShaderConstants + 2880)))
#define g_Z (*(reinterpret_cast<device float4*>(g_PushConstants.VertexShaderConstants + 3936)))
struct VertexShaderInput
{
float4 iPosition0 [[attribute(0)]];
float4 iColor0 [[attribute(8)]];
float4 iTexCoord0 [[attribute(4)]];
};
struct Interpolators
{
float4 oPos [[position]];
float4 oTexCoord0 [[user(TEXCOORD0)]];
float4 oTexCoord1 [[user(TEXCOORD1)]];
float4 oTexCoord2 [[user(TEXCOORD2)]];
float4 oTexCoord3 [[user(TEXCOORD3)]];
float4 oTexCoord4 [[user(TEXCOORD4)]];
float4 oTexCoord5 [[user(TEXCOORD5)]];
float4 oTexCoord6 [[user(TEXCOORD6)]];
float4 oTexCoord7 [[user(TEXCOORD7)]];
float4 oTexCoord8 [[user(TEXCOORD8)]];
float4 oTexCoord9 [[user(TEXCOORD9)]];
float4 oTexCoord10 [[user(TEXCOORD10)]];
float4 oTexCoord11 [[user(TEXCOORD11)]];
float4 oTexCoord12 [[user(TEXCOORD12)]];
float4 oTexCoord13 [[user(TEXCOORD13)]];
float4 oTexCoord14 [[user(TEXCOORD14)]];
float4 oTexCoord15 [[user(TEXCOORD15)]];
float4 oColor0 [[user(COLOR0)]];
float4 oColor1 [[user(COLOR1)]];
};
[[vertex]]
Interpolators shaderMain(VertexShaderInput input [[stage_in]],
constant PushConstants& g_PushConstants [[buffer(8)]])
{
Interpolators output;
output.oPos.xy = input.iPosition0.xy * g_ViewportSize.zw * float2(2.0, -2.0) + float2(-1.0, 1.0);
output.oPos.z = g_Z.x;
output.oPos.w = 1.0;
output.oTexCoord0 = input.iColor0.wxyz;
output.oTexCoord1.xy = input.iTexCoord0.xy;
output.oTexCoord1.zw = 0.0;
output.oTexCoord2 = 0.0;
output.oTexCoord3 = 0.0;
output.oTexCoord4 = 0.0;
output.oTexCoord5 = 0.0;
output.oTexCoord6 = 0.0;
output.oTexCoord7 = 0.0;
output.oTexCoord8 = 0.0;
output.oTexCoord9 = 0.0;
output.oTexCoord10 = 0.0;
output.oTexCoord11 = 0.0;
output.oTexCoord12 = 0.0;
output.oTexCoord13 = 0.0;
output.oTexCoord14 = 0.0;
output.oTexCoord15 = 0.0;
output.oColor0 = 0.0;
output.oColor1 = 0.0;
return output;
}

View file

@ -0,0 +1,59 @@
#include "../../../../tools/XenosRecomp/XenosRecomp/shader_common.h"
#define g_BlurRate (*(reinterpret_cast<device float4*>(g_PushConstants.PixelShaderConstants + 2400)))
#define g_ViewportSize (*(reinterpret_cast<device float4*>(g_PushConstants.PixelShaderConstants + 384)))
#define sampColor_Texture2DDescriptorIndex (*(reinterpret_cast<device uint*>(g_PushConstants.SharedConstants + 0)))
#define sampColor_SamplerDescriptorIndex (*(reinterpret_cast<device uint*>(g_PushConstants.SharedConstants + 192)))
#define sampVelocityMap_Texture2DDescriptorIndex (*(reinterpret_cast<device uint*>(g_PushConstants.SharedConstants + 4)))
#define sampVelocityMap_SamplerDescriptorIndex (*(reinterpret_cast<device uint*>(g_PushConstants.SharedConstants + 196)))
#define sampZBuffer_Texture2DDescriptorIndex (*(reinterpret_cast<device uint*>(g_PushConstants.SharedConstants + 8)))
#define sampZBuffer_SamplerDescriptorIndex (*(reinterpret_cast<device uint*>(g_PushConstants.SharedConstants + 200)))
struct Interpolators
{
float4 texCoord [[user(TEXCOORD0)]];
};
[[fragment]]
float4 shaderMain(float4 position [[position]],
Interpolators input [[stage_in]],
constant Texture2DDescriptorHeap* g_Texture2DDescriptorHeap [[buffer(0)]],
constant SamplerDescriptorHeap* g_SamplerDescriptorHeap [[buffer(3)]],
constant PushConstants& g_PushConstants [[buffer(8)]])
{
texture2d<float> sampColor = g_Texture2DDescriptorHeap[sampColor_Texture2DDescriptorIndex].tex;
texture2d<float> sampVelocityMap = g_Texture2DDescriptorHeap[sampVelocityMap_Texture2DDescriptorIndex].tex;
texture2d<float> sampZBuffer = g_Texture2DDescriptorHeap[sampZBuffer_Texture2DDescriptorIndex].tex;
sampler sampColor_s = g_SamplerDescriptorHeap[sampColor_SamplerDescriptorIndex].samp;
sampler sampVelocityMap_s = g_SamplerDescriptorHeap[sampVelocityMap_SamplerDescriptorIndex].samp;
sampler sampZBuffer_s = g_SamplerDescriptorHeap[sampZBuffer_SamplerDescriptorIndex].samp;
float depth = sampZBuffer.sample(sampZBuffer_s, input.texCoord.xy, level(0)).x;
float4 velocityMap = sampVelocityMap.sample(sampVelocityMap_s, input.texCoord.xy, level(0));
float2 velocity = (velocityMap.xz + velocityMap.yw / 255.0) * 2.0 - 1.0;
int sampleCount = min(64, int(round(length(velocity * g_ViewportSize.xy))));
float2 sampleOffset = velocity / (float) sampleCount;
float3 color = sampColor.sample(sampColor_s, input.texCoord.xy, level(0)).rgb;
int count = 1;
for (int i = 1; i <= sampleCount; i++)
{
float2 sampleCoord = input.texCoord.xy + sampleOffset * i;
float3 sampleColor = sampColor.sample(sampColor_s, sampleCoord, level(0)).rgb;
float sampleDepth = sampZBuffer.sample(sampZBuffer_s, sampleCoord, 0).x;
if (sampleDepth - depth < 0.01)
{
color += sampleColor;
count += 1;
}
}
return float4(color / count, g_BlurRate.x * saturate(dot(abs(velocity), g_ViewportSize.xy) / 8.0) * saturate(float(count - 1)));
}

View file

@ -0,0 +1,23 @@
#include "../../../../tools/XenosRecomp/XenosRecomp/shader_common.h"
#define g_Gamma (*(reinterpret_cast<device float3*>(g_PushConstants.SharedConstants + 0)))
#define g_TextureDescriptorIndex (*(reinterpret_cast<device uint*>(g_PushConstants.SharedConstants + 12)))
#define g_ViewportOffset (*(reinterpret_cast<device int2*>(g_PushConstants.SharedConstants + 16)))
#define g_ViewportSize (*(reinterpret_cast<device int2*>(g_PushConstants.SharedConstants + 24)))
[[fragment]]
float4 shaderMain(float4 position [[position]],
constant Texture2DDescriptorHeap* g_Texture2DDescriptorHeap [[buffer(0)]],
constant PushConstants& g_PushConstants [[buffer(8)]])
{
texture2d<float> texture = g_Texture2DDescriptorHeap[g_TextureDescriptorIndex].tex;
int2 movedPosition = int2(position.xy) - g_ViewportOffset;
bool boxed = any(movedPosition < 0) || any(movedPosition >= g_ViewportSize);
if (boxed) movedPosition = 0;
float4 color = boxed ? 0.0 : texture.read(uint2(movedPosition), 0);
color.rgb = pow(color.rgb, g_Gamma);
return color;
}

View file

@ -0,0 +1,63 @@
#include "../../../../tools/XenosRecomp/XenosRecomp/shader_common.h"
#define g_ViewportSize (*(reinterpret_cast<device float4*>(g_PushConstants.PixelShaderConstants + 384)))
#define g_offsets(INDEX) selectWrapper((INDEX) < 74,(*(reinterpret_cast<device float4*>(g_PushConstants.PixelShaderConstants + (150 + min(INDEX, 73)) * 16))), 0.0)
#define g_weights (*(reinterpret_cast<device float4*>(g_PushConstants.PixelShaderConstants + 2656)))
#define s0_Texture2DDescriptorIndex (*(reinterpret_cast<device uint*>(g_PushConstants.SharedConstants + 0)))
#define s0_SamplerDescriptorIndex (*(reinterpret_cast<device uint*>(g_PushConstants.SharedConstants + 192)))
#ifdef __INTELLISENSE__
#define KERNEL_SIZE 5
#endif
#define PI 3.14159265358979323846
float ComputeWeight(float x)
{
float std = 0.952;
return exp(-(x * x) / (2.0 * std * std)) / (std * sqrt(2.0 * PI));
}
struct Interpolators
{
float4 iTexCoord0 [[user(TEXCOORD0)]];
};
[[fragment]]
float4 shaderMain(float4 iPosition [[position]],
Interpolators input [[stage_in]],
constant Texture2DDescriptorHeap* g_Texture2DDescriptorHeap [[buffer(0)]],
constant SamplerDescriptorHeap* g_SamplerDescriptorHeap [[buffer(3)]],
constant PushConstants& g_PushConstants [[buffer(8)]])
{
texture2d<float> texture = g_Texture2DDescriptorHeap[s0_Texture2DDescriptorIndex].tex;
sampler samplerState = g_SamplerDescriptorHeap[s0_SamplerDescriptorIndex].samp;
float scale;
if ((g_ViewportSize.x * g_ViewportSize.w) >= (16.0 / 9.0))
scale = g_ViewportSize.y / 360.0;
else
scale = g_ViewportSize.x / 640.0;
float2 offsets[3];
offsets[0] = g_offsets(0).xy * scale;
offsets[1] = g_offsets(0).zw * scale;
offsets[2] = g_offsets(1).xy * scale;
float4 color = 0.0;
float weightSum = 0.0;
for (int i = 0; i < KERNEL_SIZE; i++)
{
float step = i / float(KERNEL_SIZE - 1);
float scaled = step * 2;
float2 offset = mix(offsets[int(scaled)], offsets[min(int(scaled) + 1, 2)], frac(scaled));
float offsetScale = 1.0 / 0.75;
float weight = ComputeWeight(mix(-offsetScale, offsetScale, step));
color += texture.sample(samplerState, input.iTexCoord0.xy + offset, level(0)) * weight;
weightSum += weight;
}
return color / weightSum;
}

View file

@ -0,0 +1,2 @@
#define KERNEL_SIZE 3
#include "gaussian_blur.metali"

View file

@ -0,0 +1,2 @@
#define KERNEL_SIZE 5
#include "gaussian_blur.metali"

View file

@ -0,0 +1,2 @@
#define KERNEL_SIZE 7
#include "gaussian_blur.metali"

View file

@ -0,0 +1,2 @@
#define KERNEL_SIZE 9
#include "gaussian_blur.metali"

View file

@ -0,0 +1,41 @@
#pragma once
#include <metal_stdlib>
using namespace metal;
#include "../../imgui/imgui_common.h"
struct PushConstants
{
float2 BoundsMin;
float2 BoundsMax;
uint GradientTopLeft;
uint GradientTopRight;
uint GradientBottomRight;
uint GradientBottomLeft;
uint ShaderModifier;
uint Texture2DDescriptorIndex;
float2 DisplaySize;
float2 InverseDisplaySize;
float2 Origin;
float2 Scale;
float2 ProceduralOrigin;
float Outline;
};
struct Interpolators
{
float4 Position [[position]];
float2 UV;
float4 Color;
};
struct Texture2DDescriptorHeap
{
texture2d<float> tex;
};
struct SamplerDescriptorHeap
{
sampler samp;
};

View file

@ -0,0 +1,235 @@
#include "imgui_common.metali"
float4 DecodeColor(uint color)
{
return float4(color & 0xFF, (color >> 8) & 0xFF, (color >> 16) & 0xFF, (color >> 24) & 0xFF) / 255.0;
}
float4 SamplePoint(int2 position, constant PushConstants& g_PushConstants)
{
switch (g_PushConstants.ShaderModifier)
{
case IMGUI_SHADER_MODIFIER_SCANLINE:
{
if (int(position.y) % 2 == 0)
return float4(1.0, 1.0, 1.0, 0.0);
break;
}
case IMGUI_SHADER_MODIFIER_CHECKERBOARD:
{
int remnantX = int(position.x) % 9;
int remnantY = int(position.y) % 9;
float4 color = 1.0;
if (remnantX == 0 || remnantY == 0)
color.a = 0.0;
if ((remnantY % 2) == 0)
color.rgb = 0.5;
return color;
}
case IMGUI_SHADER_MODIFIER_SCANLINE_BUTTON:
{
if (int(position.y) % 2 == 0)
return float4(1.0, 1.0, 1.0, 0.5);
break;
}
}
return 1.0;
}
float4 SampleLinear(float2 uvTexspace, constant PushConstants& g_PushConstants)
{
int2 integerPart = int2(floor(uvTexspace));
float2 fracPart = fract(uvTexspace);
float4 topLeft = SamplePoint(integerPart + int2(0, 0), g_PushConstants);
float4 topRight = SamplePoint(integerPart + int2(1, 0), g_PushConstants);
float4 bottomLeft = SamplePoint(integerPart + int2(0, 1), g_PushConstants);
float4 bottomRight = SamplePoint(integerPart + int2(1, 1), g_PushConstants);
float4 top = mix(topLeft, topRight, fracPart.x);
float4 bottom = mix(bottomLeft, bottomRight, fracPart.x);
return mix(top, bottom, fracPart.y);
}
float4 PixelAntialiasing(float2 uvTexspace, constant PushConstants& g_PushConstants)
{
if ((g_PushConstants.DisplaySize.x * g_PushConstants.InverseDisplaySize.y) >= (4.0 / 3.0))
uvTexspace *= g_PushConstants.InverseDisplaySize.y * 720.0;
else
uvTexspace *= g_PushConstants.InverseDisplaySize.x * 960.0;
float2 seam = floor(uvTexspace + 0.5);
uvTexspace = (uvTexspace - seam) / fwidth(uvTexspace) + seam;
uvTexspace = clamp(uvTexspace, seam - 0.5, seam + 0.5);
return SampleLinear(uvTexspace - 0.5, g_PushConstants);
}
float median(float r, float g, float b)
{
return max(min(r, g), min(max(r, g), b));
}
float4 SampleSdfFont(float4 color, texture2d<float> texture, float2 uv, float2 screenTexSize,
constant SamplerDescriptorHeap* g_SamplerDescriptorHeap,
constant PushConstants& g_PushConstants)
{
float4 textureColor = texture.sample(g_SamplerDescriptorHeap[0].samp, uv);
uint width = texture.get_width();
uint height = texture.get_height();
float pxRange = 8.0;
float2 unitRange = pxRange / float2(width, height);
float screenPxRange = max(0.5 * dot(unitRange, screenTexSize), 1.0);
float sd = median(textureColor.r, textureColor.g, textureColor.b) - 0.5;
float screenPxDistance = screenPxRange * (sd + g_PushConstants.Outline / (pxRange * 1.5));
if (g_PushConstants.ShaderModifier == IMGUI_SHADER_MODIFIER_TITLE_BEVEL)
{
float2 normal = normalize(float3(dfdx(sd), dfdy(sd), 0.01)).xy;
float3 rimColor = float3(1, 0.8, 0.29);
float3 shadowColor = float3(0.84, 0.57, 0);
float cosTheta = dot(normal, normalize(float2(1, 1)));
float3 gradient = mix(color.rgb, cosTheta >= 0.0 ? rimColor : shadowColor, abs(cosTheta));
color.rgb = mix(gradient, color.rgb, pow(saturate(sd + 0.77), 32.0));
}
else if (g_PushConstants.ShaderModifier == IMGUI_SHADER_MODIFIER_CATEGORY_BEVEL)
{
float2 normal = normalize(float3(dfdx(sd), dfdy(sd), 0.25)).xy;
float cosTheta = dot(normal, normalize(float2(1, 1)));
float gradient = 1.0 + cosTheta * 0.5;
color.rgb = saturate(color.rgb * gradient);
}
else if (g_PushConstants.ShaderModifier == IMGUI_SHADER_MODIFIER_TEXT_SKEW)
{
float2 normal = normalize(float3(dfdx(sd), dfdy(sd), 0.5)).xy;
float cosTheta = dot(normal, normalize(float2(1, 1)));
float gradient = saturate(1.0 + cosTheta);
color.rgb = mix(color.rgb * gradient, color.rgb, pow(saturate(sd + 0.77), 32.0));
}
color.a *= saturate(screenPxDistance + 0.5);
color.a *= textureColor.a;
return color;
}
[[fragment]]
float4 shaderMain(Interpolators interpolators [[stage_in]],
constant Texture2DDescriptorHeap* g_Texture2DDescriptorHeap [[buffer(0)]],
constant SamplerDescriptorHeap* g_SamplerDescriptorHeap [[buffer(1)]],
constant PushConstants& g_PushConstants [[buffer(8)]])
{
float4 color = interpolators.Color;
color *= PixelAntialiasing(interpolators.Position.xy - g_PushConstants.ProceduralOrigin, g_PushConstants);
if (g_PushConstants.Texture2DDescriptorIndex != 0)
{
texture2d<float> texture = g_Texture2DDescriptorHeap[g_PushConstants.Texture2DDescriptorIndex & 0x7FFFFFFF].tex;
if ((g_PushConstants.Texture2DDescriptorIndex & 0x80000000) != 0)
{
if (g_PushConstants.ShaderModifier == IMGUI_SHADER_MODIFIER_LOW_QUALITY_TEXT)
{
float scale;
float invScale;
if ((g_PushConstants.DisplaySize.x * g_PushConstants.InverseDisplaySize.y) >= (4.0 / 3.0))
{
scale = g_PushConstants.InverseDisplaySize.y * 720.0;
invScale = g_PushConstants.DisplaySize.y / 720.0;
}
else
{
scale = g_PushConstants.InverseDisplaySize.x * 960.0;
invScale = g_PushConstants.DisplaySize.x / 960.0;
}
float2 lowQualityPosition = (interpolators.Position.xy - 0.5) * scale;
float2 fracPart = fract(lowQualityPosition);
float2 uvStep = fwidth(interpolators.UV) * invScale;
float2 lowQualityUV = interpolators.UV - fracPart * uvStep;
float2 screenTexSize = 1.0 / uvStep;
float4 topLeft = SampleSdfFont(color, texture, lowQualityUV + float2(0, 0), screenTexSize, g_SamplerDescriptorHeap, g_PushConstants);
float4 topRight = SampleSdfFont(color, texture, lowQualityUV + float2(uvStep.x, 0), screenTexSize, g_SamplerDescriptorHeap, g_PushConstants);
float4 bottomLeft = SampleSdfFont(color, texture, lowQualityUV + float2(0, uvStep.y), screenTexSize, g_SamplerDescriptorHeap, g_PushConstants);
float4 bottomRight = SampleSdfFont(color, texture, lowQualityUV + uvStep.xy, screenTexSize, g_SamplerDescriptorHeap, g_PushConstants);
float4 top = mix(topLeft, topRight, fracPart.x);
float4 bottom = mix(bottomLeft, bottomRight, fracPart.x);
color = mix(top, bottom, fracPart.y);
}
else
{
color = SampleSdfFont(color, texture, interpolators.UV, 1.0 / fwidth(interpolators.UV), g_SamplerDescriptorHeap, g_PushConstants);
}
}
else
{
color *= texture.sample(g_SamplerDescriptorHeap[0].samp, interpolators.UV);
}
}
if (g_PushConstants.ShaderModifier == IMGUI_SHADER_MODIFIER_HORIZONTAL_MARQUEE_FADE)
{
float minAlpha = saturate((interpolators.Position.x - g_PushConstants.BoundsMin.x) / g_PushConstants.Scale.x);
float maxAlpha = saturate((g_PushConstants.BoundsMax.x - interpolators.Position.x) / g_PushConstants.Scale.y);
color.a *= minAlpha;
color.a *= maxAlpha;
}
else if (g_PushConstants.ShaderModifier == IMGUI_SHADER_MODIFIER_VERTICAL_MARQUEE_FADE)
{
float minAlpha = saturate((interpolators.Position.y - g_PushConstants.BoundsMin.y) / g_PushConstants.Scale.x);
float maxAlpha = saturate((g_PushConstants.BoundsMax.y - interpolators.Position.y) / g_PushConstants.Scale.y);
color.a *= minAlpha;
color.a *= maxAlpha;
}
else if (any(g_PushConstants.BoundsMin != g_PushConstants.BoundsMax))
{
float2 factor = saturate((interpolators.Position.xy - g_PushConstants.BoundsMin) / (g_PushConstants.BoundsMax - g_PushConstants.BoundsMin));
if (g_PushConstants.ShaderModifier == IMGUI_SHADER_MODIFIER_RECTANGLE_BEVEL)
{
float bevelSize = 0.9;
float shadow = saturate((factor.x - bevelSize) / (1.0 - bevelSize));
shadow = max(shadow, saturate((factor.y - bevelSize) / (1.0 - bevelSize)));
float rim = saturate((1.0 - factor.x - bevelSize) / (1.0 - bevelSize));
rim = max(rim, saturate((1.0 - factor.y - bevelSize) / (1.0 - bevelSize)));
float3 rimColor = float3(1, 0.8, 0.29);
float3 shadowColor = float3(0.84, 0.57, 0);
color.rgb = mix(color.rgb, rimColor, smoothstep(0.0, 1.0, rim));
color.rgb = mix(color.rgb, shadowColor, smoothstep(0.0, 1.0, shadow));
}
else
{
float4 top = mix(DecodeColor(g_PushConstants.GradientTopLeft), DecodeColor(g_PushConstants.GradientTopRight), smoothstep(0.0, 1.0, factor.x));
float4 bottom = mix(DecodeColor(g_PushConstants.GradientBottomLeft), DecodeColor(g_PushConstants.GradientBottomRight), smoothstep(0.0, 1.0, factor.x));
color *= mix(top, bottom, smoothstep(0.0, 1.0, factor.y));
}
}
if (g_PushConstants.ShaderModifier == IMGUI_SHADER_MODIFIER_GRAYSCALE)
color.rgb = dot(color.rgb, float3(0.2126, 0.7152, 0.0722));
return color;
}

View file

@ -0,0 +1,32 @@
#include "imgui_common.metali"
struct VertexStageIn
{
float2 position [[attribute(0)]];
float2 uv [[attribute(1)]];
float4 color [[attribute(2)]];
};
[[vertex]]
Interpolators shaderMain(VertexStageIn input [[stage_in]],
constant PushConstants& g_PushConstants [[buffer(8)]])
{
Interpolators interpolators = Interpolators{};
if (g_PushConstants.ShaderModifier == IMGUI_SHADER_MODIFIER_TEXT_SKEW)
{
if (input.position.y < g_PushConstants.Origin.y)
input.position.x += g_PushConstants.Scale.x;
}
else if (g_PushConstants.ShaderModifier != IMGUI_SHADER_MODIFIER_HORIZONTAL_MARQUEE_FADE &&
g_PushConstants.ShaderModifier != IMGUI_SHADER_MODIFIER_VERTICAL_MARQUEE_FADE)
{
input.position.xy = g_PushConstants.Origin + (input.position.xy - g_PushConstants.Origin) * g_PushConstants.Scale;
}
interpolators.Position = float4(input.position.xy * g_PushConstants.InverseDisplaySize * float2(2.0, -2.0) + float2(-1.0, 1.0), 0.0, 1.0);
interpolators.UV = input.uv;
interpolators.Color = input.color;
return interpolators;
}

View file

@ -0,0 +1,39 @@
#pragma once
#include "../../../../tools/XenosRecomp/XenosRecomp/shader_common.h"
#define fZmin (*(reinterpret_cast<device float*>(g_PushConstants.PixelShaderConstants + 0)))
#define fZmax (*(reinterpret_cast<device float*>(g_PushConstants.PixelShaderConstants + 16)))
#define Tex0_ResourceDescriptorIndex (*(reinterpret_cast<device uint*>(g_PushConstants.SharedConstants + 0)))
#define Tex1_ResourceDescriptorIndex (*(reinterpret_cast<device uint*>(g_PushConstants.SharedConstants + 4)))
#define Tex2_ResourceDescriptorIndex (*(reinterpret_cast<device uint*>(g_PushConstants.SharedConstants + 8)))
#define Tex3_ResourceDescriptorIndex (*(reinterpret_cast<device uint*>(g_PushConstants.SharedConstants + 12)))
#define Tex4_ResourceDescriptorIndex (*(reinterpret_cast<device uint*>(g_PushConstants.SharedConstants + 16)))
#define Tex0_SamplerDescriptorIndex (*(reinterpret_cast<device uint*>(g_PushConstants.SharedConstants + 64)))
#define Tex1_SamplerDescriptorIndex (*(reinterpret_cast<device uint*>(g_PushConstants.SharedConstants + 68)))
#define Tex2_SamplerDescriptorIndex (*(reinterpret_cast<device uint*>(g_PushConstants.SharedConstants + 72)))
#define Tex3_SamplerDescriptorIndex (*(reinterpret_cast<device uint*>(g_PushConstants.SharedConstants + 76)))
#define Tex4_SamplerDescriptorIndex (*(reinterpret_cast<device uint*>(g_PushConstants.SharedConstants + 80)))
#define bCsc (g_Booleans & (1 << (16 + 0)))
#define bAmv (g_Booleans & (1 << (16 + 1)))
#define bZmv (g_Booleans & (1 << (16 + 2)))
struct VertexShaderInput
{
float4 ObjPos [[attribute(0)]];
float2 UV [[attribute(4)]];
};
struct Interpolators
{
float4 ProjPos [[position]];
float2 UV;
};
struct PixelShaderOutput
{
float4 Color [[color(0)]];
};

View file

@ -0,0 +1,104 @@
#include "movie_common.metali"
[[fragment]]
PixelShaderOutput shaderMain(Interpolators In [[stage_in]],
constant Texture2DDescriptorHeap* g_Texture2DDescriptorHeap [[buffer(0)]],
constant SamplerDescriptorHeap* g_SamplerDescriptorHeap [[buffer(3)]],
constant PushConstants& g_PushConstants [[buffer(8)]])
{
texture2d<float> Tex0 = g_Texture2DDescriptorHeap[Tex0_ResourceDescriptorIndex].tex;
texture2d<float> Tex1 = g_Texture2DDescriptorHeap[Tex1_ResourceDescriptorIndex].tex;
texture2d<float> Tex2 = g_Texture2DDescriptorHeap[Tex2_ResourceDescriptorIndex].tex;
texture2d<float> Tex3 = g_Texture2DDescriptorHeap[Tex3_ResourceDescriptorIndex].tex;
texture2d<float> Tex4 = g_Texture2DDescriptorHeap[Tex4_ResourceDescriptorIndex].tex;
sampler Tex0_s = g_SamplerDescriptorHeap[Tex0_SamplerDescriptorIndex].samp;
sampler Tex1_s = g_SamplerDescriptorHeap[Tex1_SamplerDescriptorIndex].samp;
sampler Tex2_s = g_SamplerDescriptorHeap[Tex2_SamplerDescriptorIndex].samp;
sampler Tex3_s = g_SamplerDescriptorHeap[Tex3_SamplerDescriptorIndex].samp;
sampler Tex4_s = g_SamplerDescriptorHeap[Tex4_SamplerDescriptorIndex].samp;
PixelShaderOutput Out;
float ValY = Tex0.sample(Tex0_s, In.UV).r;
float ValU = Tex1.sample(Tex1_s, In.UV).r - 0.5;
float ValV = Tex2.sample(Tex2_s, In.UV).r - 0.5;
float ValA = 1.0;
float ValD = 0.0;
if (bAmv)
ValA = (Tex3.sample(Tex3_s, In.UV).r - 0.0625) * 1.164;
if (bZmv)
{
ValD = (Tex4.sample(Tex4_s, In.UV).r - 0.0625) * 1.164;
if (ValD < 9.0 / 255.0)
{
ValD = 0.0;
}
else if (ValD < 17.0 / 255.0)
{
ValD = fZmin;
}
else if (ValD < 224.0 / 255.0)
{
ValD = (ValD - 17.0 / 255.0) / (223.0 / 255.0 - 17.0 / 255.0) * (fZmax - fZmin) + fZmin;
}
else if (ValD < 240.0 / 255.0)
{
ValD = fZmax;
}
else
{
ValD = 1.0;
}
}
if (bCsc)
{
if (ValY < 16.0 / 255.0)
{
ValY = ValY * 3.0 / 2.0;
}
else if (ValY < 176.0 / 255.0)
{
ValY = 24.0 / 255.0 + (ValY - 16.0 / 255.0) / 2.0;
}
else if (ValY < 192.0 / 255.0)
{
ValY = 104.0 / 255.0 + (ValY - 176.0 / 255.0) / 1.0;
}
else
{
ValY = 120.0 / 255.0 + (ValY - 192.0 / 255.0) * 2.0;
}
if (abs(ValU) < 24.0 / 255.0)
{
ValU /= 3.0;
}
else
{
ValU = (8.0 / 255.0 + (abs(ValU) - 24.0 / 255.0) * (120.0 / 104.0)) * sign(ValU);
}
if (abs(ValV) < 24.0 / 255.0)
{
ValV /= 3.0;
}
else
{
ValV = (8.0 / 255.0 + (abs(ValV) - 24.0 / 255.0) * (120.0 / 104.0)) * sign(ValV);
}
Out.Color.r = ValY + ValV * 1.402;
Out.Color.g = ValY - ValU * 0.344 - ValV * 0.714;
Out.Color.b = ValY + ValU * 1.772;
}
else
{
ValY = (ValY - 0.0625) * 1.164;
Out.Color.r = ValY + ValV * 1.596;
Out.Color.g = ValY - ValU * 0.392 - ValV * 0.813;
Out.Color.b = ValY + ValU * 2.017;
}
Out.Color.a = ValA;
if (any(In.UV < 0.0) || any(In.UV > 1.0))
Out.Color.rgb = 0.0;
return Out;
}

View file

@ -0,0 +1,12 @@
#include "movie_common.metali"
[[vertex]]
Interpolators shaderMain(VertexShaderInput In [[stage_in]],
constant PushConstants& g_PushConstants [[buffer(8)]])
{
Interpolators Out;
Out.ProjPos = In.ObjPos;
Out.ProjPos.xy += g_HalfPixelOffset * Out.ProjPos.w;
Out.UV = In.UV;
return Out;
}

View file

@ -0,0 +1,21 @@
#pragma once
#include "copy_common.metali"
struct Texture2DMSDescriptorHeap
{
texture2d_ms<float> tex;
};
[[fragment]]
float4 shaderMain(float4 position [[position]],
constant Texture2DMSDescriptorHeap* g_Texture2DMSDescriptorHeap [[buffer(0)]],
constant PushConstants& g_PushConstants [[buffer(8)]])
{
float4 result = g_Texture2DMSDescriptorHeap[g_PushConstants.ResourceDescriptorIndex].tex.read(uint2(position.xy), 0);
for (int i = 1; i < SAMPLE_COUNT; i++)
result += g_Texture2DMSDescriptorHeap[g_PushConstants.ResourceDescriptorIndex].tex.read(uint2(position.xy), i);
return result / SAMPLE_COUNT;
}

View file

@ -0,0 +1,2 @@
#define SAMPLE_COUNT 2
#include "resolve_msaa_color.metali"

View file

@ -0,0 +1,2 @@
#define SAMPLE_COUNT 4
#include "resolve_msaa_color.metali"

View file

@ -0,0 +1,2 @@
#define SAMPLE_COUNT 8
#include "resolve_msaa_color.metali"

View file

@ -0,0 +1,30 @@
#pragma once
#include "copy_common.metali"
struct Texture2DMSDescriptorHeap
{
texture2d_ms<float> tex;
};
struct PixelShaderOutput
{
float oDepth [[depth(any)]];
};
[[fragment]]
PixelShaderOutput shaderMain(float4 position [[position]],
constant Texture2DMSDescriptorHeap* g_Texture2DMSDescriptorHeap [[buffer(0)]],
constant PushConstants& g_PushConstants [[buffer(8)]])
{
PixelShaderOutput output = PixelShaderOutput{};
float result = g_Texture2DMSDescriptorHeap[g_PushConstants.ResourceDescriptorIndex].tex.read(uint2(position.xy), 0).x;
for (int i = 1; i < SAMPLE_COUNT; i++)
result = min(result, g_Texture2DMSDescriptorHeap[g_PushConstants.ResourceDescriptorIndex].tex.read(uint2(position.xy), i).x);
output.oDepth = result;
return output;
}

View file

@ -0,0 +1,2 @@
#define SAMPLE_COUNT 2
#include "resolve_msaa_depth.metali"

View file

@ -0,0 +1,2 @@
#define SAMPLE_COUNT 4
#include "resolve_msaa_depth.metali"

View file

@ -0,0 +1,2 @@
#define SAMPLE_COUNT 8
#include "resolve_msaa_depth.metali"

View file

@ -41,54 +41,80 @@
#include "../../tools/XenosRecomp/XenosRecomp/shader_common.h"
#ifdef UNLEASHED_RECOMP_D3D12
#include "shader/blend_color_alpha_ps.hlsl.dxil.h"
#include "shader/copy_vs.hlsl.dxil.h"
#include "shader/copy_color_ps.hlsl.dxil.h"
#include "shader/copy_depth_ps.hlsl.dxil.h"
#include "shader/csd_filter_ps.hlsl.dxil.h"
#include "shader/csd_no_tex_vs.hlsl.dxil.h"
#include "shader/csd_vs.hlsl.dxil.h"
#include "shader/enhanced_motion_blur_ps.hlsl.dxil.h"
#include "shader/gamma_correction_ps.hlsl.dxil.h"
#include "shader/gaussian_blur_3x3.hlsl.dxil.h"
#include "shader/gaussian_blur_5x5.hlsl.dxil.h"
#include "shader/gaussian_blur_7x7.hlsl.dxil.h"
#include "shader/gaussian_blur_9x9.hlsl.dxil.h"
#include "shader/imgui_ps.hlsl.dxil.h"
#include "shader/imgui_vs.hlsl.dxil.h"
#include "shader/movie_ps.hlsl.dxil.h"
#include "shader/movie_vs.hlsl.dxil.h"
#include "shader/resolve_msaa_color_2x.hlsl.dxil.h"
#include "shader/resolve_msaa_color_4x.hlsl.dxil.h"
#include "shader/resolve_msaa_color_8x.hlsl.dxil.h"
#include "shader/resolve_msaa_depth_2x.hlsl.dxil.h"
#include "shader/resolve_msaa_depth_4x.hlsl.dxil.h"
#include "shader/resolve_msaa_depth_8x.hlsl.dxil.h"
#include "shader/hlsl/blend_color_alpha_ps.hlsl.dxil.h"
#include "shader/hlsl/copy_vs.hlsl.dxil.h"
#include "shader/hlsl/copy_color_ps.hlsl.dxil.h"
#include "shader/hlsl/copy_depth_ps.hlsl.dxil.h"
#include "shader/hlsl/csd_filter_ps.hlsl.dxil.h"
#include "shader/hlsl/csd_no_tex_vs.hlsl.dxil.h"
#include "shader/hlsl/csd_vs.hlsl.dxil.h"
#include "shader/hlsl/enhanced_motion_blur_ps.hlsl.dxil.h"
#include "shader/hlsl/gamma_correction_ps.hlsl.dxil.h"
#include "shader/hlsl/gaussian_blur_3x3.hlsl.dxil.h"
#include "shader/hlsl/gaussian_blur_5x5.hlsl.dxil.h"
#include "shader/hlsl/gaussian_blur_7x7.hlsl.dxil.h"
#include "shader/hlsl/gaussian_blur_9x9.hlsl.dxil.h"
#include "shader/hlsl/imgui_ps.hlsl.dxil.h"
#include "shader/hlsl/imgui_vs.hlsl.dxil.h"
#include "shader/hlsl/movie_ps.hlsl.dxil.h"
#include "shader/hlsl/movie_vs.hlsl.dxil.h"
#include "shader/hlsl/resolve_msaa_color_2x.hlsl.dxil.h"
#include "shader/hlsl/resolve_msaa_color_4x.hlsl.dxil.h"
#include "shader/hlsl/resolve_msaa_color_8x.hlsl.dxil.h"
#include "shader/hlsl/resolve_msaa_depth_2x.hlsl.dxil.h"
#include "shader/hlsl/resolve_msaa_depth_4x.hlsl.dxil.h"
#include "shader/hlsl/resolve_msaa_depth_8x.hlsl.dxil.h"
#endif
#include "shader/blend_color_alpha_ps.hlsl.spirv.h"
#include "shader/copy_vs.hlsl.spirv.h"
#include "shader/copy_color_ps.hlsl.spirv.h"
#include "shader/copy_depth_ps.hlsl.spirv.h"
#include "shader/csd_filter_ps.hlsl.spirv.h"
#include "shader/csd_no_tex_vs.hlsl.spirv.h"
#include "shader/csd_vs.hlsl.spirv.h"
#include "shader/enhanced_motion_blur_ps.hlsl.spirv.h"
#include "shader/gamma_correction_ps.hlsl.spirv.h"
#include "shader/gaussian_blur_3x3.hlsl.spirv.h"
#include "shader/gaussian_blur_5x5.hlsl.spirv.h"
#include "shader/gaussian_blur_7x7.hlsl.spirv.h"
#include "shader/gaussian_blur_9x9.hlsl.spirv.h"
#include "shader/imgui_ps.hlsl.spirv.h"
#include "shader/imgui_vs.hlsl.spirv.h"
#include "shader/movie_ps.hlsl.spirv.h"
#include "shader/movie_vs.hlsl.spirv.h"
#include "shader/resolve_msaa_color_2x.hlsl.spirv.h"
#include "shader/resolve_msaa_color_4x.hlsl.spirv.h"
#include "shader/resolve_msaa_color_8x.hlsl.spirv.h"
#include "shader/resolve_msaa_depth_2x.hlsl.spirv.h"
#include "shader/resolve_msaa_depth_4x.hlsl.spirv.h"
#include "shader/resolve_msaa_depth_8x.hlsl.spirv.h"
#ifdef UNLEASHED_RECOMP_METAL
#include "shader/msl/blend_color_alpha_ps.metal.metallib.h"
#include "shader/msl/copy_vs.metal.metallib.h"
#include "shader/msl/copy_color_ps.metal.metallib.h"
#include "shader/msl/copy_depth_ps.metal.metallib.h"
#include "shader/msl/csd_filter_ps.metal.metallib.h"
#include "shader/msl/csd_no_tex_vs.metal.metallib.h"
#include "shader/msl/csd_vs.metal.metallib.h"
#include "shader/msl/enhanced_motion_blur_ps.metal.metallib.h"
#include "shader/msl/gamma_correction_ps.metal.metallib.h"
#include "shader/msl/gaussian_blur_3x3.metal.metallib.h"
#include "shader/msl/gaussian_blur_5x5.metal.metallib.h"
#include "shader/msl/gaussian_blur_7x7.metal.metallib.h"
#include "shader/msl/gaussian_blur_9x9.metal.metallib.h"
#include "shader/msl/imgui_ps.metal.metallib.h"
#include "shader/msl/imgui_vs.metal.metallib.h"
#include "shader/msl/movie_ps.metal.metallib.h"
#include "shader/msl/movie_vs.metal.metallib.h"
#include "shader/msl/resolve_msaa_color_2x.metal.metallib.h"
#include "shader/msl/resolve_msaa_color_4x.metal.metallib.h"
#include "shader/msl/resolve_msaa_color_8x.metal.metallib.h"
#include "shader/msl/resolve_msaa_depth_2x.metal.metallib.h"
#include "shader/msl/resolve_msaa_depth_4x.metal.metallib.h"
#include "shader/msl/resolve_msaa_depth_8x.metal.metallib.h"
#endif
#include "shader/hlsl/blend_color_alpha_ps.hlsl.spirv.h"
#include "shader/hlsl/copy_vs.hlsl.spirv.h"
#include "shader/hlsl/copy_color_ps.hlsl.spirv.h"
#include "shader/hlsl/copy_depth_ps.hlsl.spirv.h"
#include "shader/hlsl/csd_filter_ps.hlsl.spirv.h"
#include "shader/hlsl/csd_no_tex_vs.hlsl.spirv.h"
#include "shader/hlsl/csd_vs.hlsl.spirv.h"
#include "shader/hlsl/enhanced_motion_blur_ps.hlsl.spirv.h"
#include "shader/hlsl/gamma_correction_ps.hlsl.spirv.h"
#include "shader/hlsl/gaussian_blur_3x3.hlsl.spirv.h"
#include "shader/hlsl/gaussian_blur_5x5.hlsl.spirv.h"
#include "shader/hlsl/gaussian_blur_7x7.hlsl.spirv.h"
#include "shader/hlsl/gaussian_blur_9x9.hlsl.spirv.h"
#include "shader/hlsl/imgui_ps.hlsl.spirv.h"
#include "shader/hlsl/imgui_vs.hlsl.spirv.h"
#include "shader/hlsl/movie_ps.hlsl.spirv.h"
#include "shader/hlsl/movie_vs.hlsl.spirv.h"
#include "shader/hlsl/resolve_msaa_color_2x.hlsl.spirv.h"
#include "shader/hlsl/resolve_msaa_color_4x.hlsl.spirv.h"
#include "shader/hlsl/resolve_msaa_color_8x.hlsl.spirv.h"
#include "shader/hlsl/resolve_msaa_depth_2x.hlsl.spirv.h"
#include "shader/hlsl/resolve_msaa_depth_4x.hlsl.spirv.h"
#include "shader/hlsl/resolve_msaa_depth_8x.hlsl.spirv.h"
#ifdef _WIN32
extern "C"
@ -103,6 +129,9 @@ namespace plume
#ifdef UNLEASHED_RECOMP_D3D12
extern std::unique_ptr<RenderInterface> CreateD3D12Interface();
#endif
#ifdef UNLEASHED_RECOMP_METAL
extern std::unique_ptr<RenderInterface> CreateMetalInterface();
#endif
#ifdef SDL_VULKAN_ENABLED
extern std::unique_ptr<RenderInterface> CreateVulkanInterface(RenderWindow sdlWindow);
#else
@ -283,17 +312,14 @@ static Profiler g_swapChainAcquireProfiler;
static bool g_profilerVisible;
static bool g_profilerWasToggled;
#ifdef UNLEASHED_RECOMP_D3D12
static bool g_vulkan = false;
#if !defined(UNLEASHED_RECOMP_D3D12) && !defined(UNLEASHED_RECOMP_METAL)
static constexpr Backend g_backend = Backend::VULKAN;
#else
static constexpr bool g_vulkan = true;
static Backend g_backend;
#endif
static bool g_triangleStripWorkaround = false;
static bool g_hardwareResolve = true;
static bool g_hardwareDepthResolve = true;
static std::unique_ptr<RenderInterface> g_interface;
static std::unique_ptr<RenderDevice> g_device;
@ -486,7 +512,7 @@ struct UploadAllocator
auto& buffer = buffers[index];
if (buffer.buffer == nullptr)
{
buffer.buffer = g_device->createBuffer(RenderBufferDesc::UploadBuffer(UploadBuffer::SIZE, RenderBufferFlag::CONSTANT | RenderBufferFlag::VERTEX | RenderBufferFlag::INDEX));
buffer.buffer = g_device->createBuffer(RenderBufferDesc::UploadBuffer(UploadBuffer::SIZE, RenderBufferFlag::CONSTANT | RenderBufferFlag::VERTEX | RenderBufferFlag::INDEX | RenderBufferFlag::DEVICE_ADDRESSABLE));
buffer.memory = reinterpret_cast<uint8_t*>(buffer.buffer->map());
buffer.deviceAddress = buffer.buffer->getDeviceAddress();
}
@ -780,18 +806,26 @@ static std::unique_ptr<uint8_t[]> g_buttonBcDiff;
static void LoadEmbeddedResources()
{
if (g_vulkan)
switch (g_backend)
{
case Backend::VULKAN:
g_shaderCache = std::make_unique<uint8_t[]>(g_spirvCacheDecompressedSize);
ZSTD_decompress(g_shaderCache.get(), g_spirvCacheDecompressedSize, g_compressedSpirvCache, g_spirvCacheCompressedSize);
}
#ifdef UNLEASHED_RECOMP_D3D12
else
{
break;
#if defined(UNLEASHED_RECOMP_D3D12)
case Backend::D3D12:
g_shaderCache = std::make_unique<uint8_t[]>(g_dxilCacheDecompressedSize);
ZSTD_decompress(g_shaderCache.get(), g_dxilCacheDecompressedSize, g_compressedDxilCache, g_dxilCacheCompressedSize);
}
break;
#elif defined(UNLEASHED_RECOMP_METAL)
case Backend::METAL:
g_shaderCache = std::make_unique<uint8_t[]>(g_airCacheDecompressedSize);
ZSTD_decompress(g_shaderCache.get(), g_airCacheDecompressedSize, g_compressedAirCache, g_airCacheCompressedSize);
break;
#endif
default:
assert(false);
}
g_buttonBcDiff = decompressZstd(g_button_bc_diff, g_button_bc_diff_uncompressed_size);
}
@ -1187,7 +1221,7 @@ static void ProcSetRenderState(const RenderCommand& cmd)
}
case D3DRS_ALPHAREF:
{
SetDirtyValue(g_dirtyStates.pipelineState, g_sharedConstants.alphaThreshold, float(value) / 256.0f);
SetDirtyValue(g_dirtyStates.sharedConstants, g_sharedConstants.alphaThreshold, float(value) / 256.0f);
break;
}
case D3DRS_ALPHABLENDENABLE:
@ -1294,19 +1328,28 @@ static GuestShader* g_csdShader;
static std::unique_ptr<GuestShader> g_enhancedMotionBlurShader;
#ifdef UNLEASHED_RECOMP_D3D12
#if defined(UNLEASHED_RECOMP_D3D12)
#define CREATE_SHADER(NAME) \
g_device->createShader( \
g_vulkan ? g_##NAME##_spirv : g_##NAME##_dxil, \
g_vulkan ? sizeof(g_##NAME##_spirv) : sizeof(g_##NAME##_dxil), \
"main", \
g_vulkan ? RenderShaderFormat::SPIRV : RenderShaderFormat::DXIL)
(g_backend == Backend::VULKAN) ? g_##NAME##_spirv : g_##NAME##_dxil, \
(g_backend == Backend::VULKAN) ? sizeof(g_##NAME##_spirv) : sizeof(g_##NAME##_dxil), \
"shaderMain", \
(g_backend == Backend::VULKAN) ? RenderShaderFormat::SPIRV : RenderShaderFormat::DXIL)
#elif defined(UNLEASHED_RECOMP_METAL)
#define CREATE_SHADER(NAME) \
g_device->createShader( \
(g_backend == Backend::VULKAN) ? g_##NAME##_spirv : g_##NAME##_air, \
(g_backend == Backend::VULKAN) ? sizeof(g_##NAME##_spirv) : sizeof(g_##NAME##_air), \
"shaderMain", \
(g_backend == Backend::VULKAN) ? RenderShaderFormat::SPIRV : RenderShaderFormat::METAL)
#else
#define CREATE_SHADER(NAME) \
g_device->createShader(g_##NAME##_spirv, sizeof(g_##NAME##_spirv), "main", RenderShaderFormat::SPIRV)
g_device->createShader(g_##NAME##_spirv, sizeof(g_##NAME##_spirv), "shaderMain", RenderShaderFormat::SPIRV)
#endif
@ -1671,8 +1714,10 @@ bool Video::CreateHostDevice(const char *sdlVideoDriver, bool graphicsApiRetry)
GameWindow::Init(sdlVideoDriver);
#ifdef UNLEASHED_RECOMP_D3D12
g_vulkan = DetectWine() || Config::GraphicsAPI == EGraphicsAPI::Vulkan;
#if defined(UNLEASHED_RECOMP_D3D12)
g_backend = (DetectWine() || Config::GraphicsAPI == EGraphicsAPI::Vulkan) ? Backend::VULKAN : Backend::D3D12;
#elif defined(UNLEASHED_RECOMP_METAL)
g_backend = Config::GraphicsAPI == EGraphicsAPI::Vulkan ? Backend::VULKAN : Backend::METAL;
#endif
// Attempt to create the possible backends using a vector of function pointers. Whichever succeeds first will be the chosen API.
@ -1685,15 +1730,18 @@ bool Video::CreateHostDevice(const char *sdlVideoDriver, bool graphicsApiRetry)
if (graphicsApiRetry)
{
// If we are attempting to create again after a reboot due to a crash, swap the order.
g_vulkan = !g_vulkan;
g_backend = (g_backend == Backend::VULKAN) ? Backend::D3D12 : Backend::VULKAN;
// Don't allow redirection to Vulkan if we are retrying after a crash,
// so the user can at least boot the game with D3D12 if Vulkan fails to work.
allowVulkanRedirection = false;
}
interfaceFunctions.push_back(g_vulkan ? CreateVulkanInterfaceWrapper : CreateD3D12Interface);
interfaceFunctions.push_back(g_vulkan ? CreateD3D12Interface : CreateVulkanInterfaceWrapper);
interfaceFunctions.push_back((g_backend == Backend::VULKAN) ? CreateVulkanInterfaceWrapper : CreateD3D12Interface);
interfaceFunctions.push_back((g_backend == Backend::VULKAN) ? CreateD3D12Interface : CreateVulkanInterfaceWrapper);
#elif defined(UNLEASHED_RECOMP_METAL)
interfaceFunctions.push_back((g_backend == Backend::VULKAN) ? CreateVulkanInterfaceWrapper : CreateMetalInterface);
interfaceFunctions.push_back((g_backend == Backend::VULKAN) ? CreateMetalInterface : CreateVulkanInterfaceWrapper);
#else
interfaceFunctions.push_back(CreateVulkanInterfaceWrapper);
#endif
@ -1718,7 +1766,7 @@ bool Video::CreateHostDevice(const char *sdlVideoDriver, bool graphicsApiRetry)
{
const RenderDeviceDescription &deviceDescription = g_device->getDescription();
#ifdef UNLEASHED_RECOMP_D3D12
#if defined(UNLEASHED_RECOMP_D3D12)
if (interfaceFunction == CreateD3D12Interface)
{
if (allowVulkanRedirection)
@ -1749,7 +1797,7 @@ bool Video::CreateHostDevice(const char *sdlVideoDriver, bool graphicsApiRetry)
// In case Vulkan fails to initialize, we will try D3D12 again afterwards,
// just to get the game to boot. This only really happens in very old Intel GPU drivers.
if (!g_vulkan)
if (g_backend != Backend::VULKAN)
{
interfaceFunctions.push_back(CreateD3D12Interface);
allowVulkanRedirection = false;
@ -1758,13 +1806,11 @@ bool Video::CreateHostDevice(const char *sdlVideoDriver, bool graphicsApiRetry)
continue;
}
}
// Hardware resolve seems to be completely bugged on Intel D3D12 drivers.
g_hardwareResolve = (deviceDescription.vendor != RenderDeviceVendor::INTEL);
g_hardwareDepthResolve = (deviceDescription.vendor != RenderDeviceVendor::INTEL);
}
g_vulkan = (interfaceFunction == CreateVulkanInterfaceWrapper);
g_backend = (interfaceFunction == CreateVulkanInterfaceWrapper) ? Backend::VULKAN : Backend::D3D12;
#elif defined(UNLEASHED_RECOMP_METAL)
g_backend = (interfaceFunction == CreateVulkanInterfaceWrapper) ? Backend::VULKAN : Backend::METAL;
#endif
// Enable triangle strip workaround if we are on AMD, as there is a bug where
// restart indices cause triangles to be culled incorrectly. Converting them to degenerate triangles fixes it.
@ -1800,7 +1846,7 @@ bool Video::CreateHostDevice(const char *sdlVideoDriver, bool graphicsApiRetry)
if (graphicsApiRetry)
{
// If we managed to create a device after retrying it in a reboot, remember the one we picked.
Config::GraphicsAPI = g_vulkan ? EGraphicsAPI::Vulkan : EGraphicsAPI::D3D12;
Config::GraphicsAPI = g_backend == Backend::VULKAN ? EGraphicsAPI::Vulkan : EGraphicsAPI::D3D12;
}
#endif
@ -1855,15 +1901,18 @@ bool Video::CreateHostDevice(const char *sdlVideoDriver, bool graphicsApiRetry)
switch (Config::TripleBuffering)
{
case ETripleBuffering::Auto:
if (g_vulkan)
{
switch (g_backend) {
case Backend::VULKAN:
// Defaulting to 3 is fine if presentWait as supported, as the maximum frame latency allowed is only 1.
bufferCount = g_device->getCapabilities().presentWait ? 3 : 2;
}
else
{
break;
case Backend::D3D12:
// Defaulting to 3 is fine on D3D12 thanks to flip discard model.
bufferCount = 3;
break;
case Backend::METAL:
bufferCount = 2;
break;
}
break;
@ -1960,7 +2009,7 @@ bool Video::CreateHostDevice(const char *sdlVideoDriver, bool graphicsApiRetry)
pipelineLayoutBuilder.addDescriptorSet(descriptorSetBuilder);
if (g_vulkan)
if (g_backend != Backend::D3D12)
{
pipelineLayoutBuilder.addPushConstant(0, 4, 24, RenderShaderStageFlag::VERTEX | RenderShaderStageFlag::PIXEL);
}
@ -2459,12 +2508,27 @@ static void DrawProfiler()
ImGui::Text("Present Wait: %s", g_capabilities.presentWait ? "Supported" : "Unsupported");
ImGui::Text("Triangle Fan: %s", g_capabilities.triangleFan ? "Supported" : "Unsupported");
ImGui::Text("Dynamic Depth Bias: %s", g_capabilities.dynamicDepthBias ? "Supported" : "Unsupported");
ImGui::Text("Hardware Resolve Modes: %s", g_capabilities.resolveModes ? "Supported" : "Unsupported");
ImGui::Text("Triangle Strip Workaround: %s", g_triangleStripWorkaround ? "Enabled" : "Disabled");
ImGui::Text("Hardware Resolve: %s", g_hardwareResolve ? "Enabled" : "Disabled");
ImGui::Text("Hardware Depth Resolve: %s", g_hardwareDepthResolve ? "Enabled" : "Disabled");
ImGui::NewLine();
ImGui::Text("API: %s", g_vulkan ? "Vulkan" : "D3D12");
std::string backend;
switch (g_backend) {
case Backend::VULKAN:
backend = "Vulkan";
break;
case Backend::D3D12:
backend = "D3D12";
break;
case Backend::METAL:
backend = "Metal";
break;
default:
assert(false && "Unknown graphics backend");
}
ImGui::Text("API: %s", backend.c_str());
ImGui::Text("Device: %s", g_device->getDescription().name.c_str());
ImGui::Text("Device Type: %s", DeviceTypeName(g_device->getDescription().type));
ImGui::Text("VRAM: %.2f MiB", (double)(g_device->getDescription().dedicatedVideoMemory) / (1024.0 * 1024.0));
@ -2888,7 +2952,7 @@ static void SetRootDescriptor(const UploadAllocation& allocation, size_t index)
{
auto& commandList = g_commandLists[g_frame];
if (g_vulkan)
if (g_backend != Backend::D3D12)
commandList->setGraphicsPushConstants(0, &allocation.deviceAddress, 8 * index, 8);
else
commandList->setGraphicsRootDescriptor(allocation.buffer->at(allocation.offset), index);
@ -3375,12 +3439,9 @@ static bool PopulateBarriersForStretchRect(GuestSurface* renderTarget, GuestSurf
RenderTextureLayout dstLayout;
bool shaderResolve = true;
if (multiSampling && g_hardwareResolve)
if (multiSampling)
{
// Hardware depth resolve is only supported on D3D12 when programmable sample positions are available.
bool hardwareDepthResolveAvailable = g_hardwareDepthResolve && !g_vulkan && g_capabilities.sampleLocations;
if (surface->format != RenderFormat::D32_FLOAT || hardwareDepthResolveAvailable)
if (surface->format != RenderFormat::D32_FLOAT || g_capabilities.resolveModes)
{
srcLayout = RenderTextureLayout::RESOLVE_SOURCE;
dstLayout = RenderTextureLayout::RESOLVE_DEST;
@ -3420,11 +3481,9 @@ static void ExecutePendingStretchRectCommands(GuestSurface* renderTarget, GuestS
{
bool shaderResolve = true;
if (multiSampling && g_hardwareResolve)
if (multiSampling)
{
bool hardwareDepthResolveAvailable = g_hardwareDepthResolve && !g_vulkan && g_capabilities.sampleLocations;
if (surface->format != RenderFormat::D32_FLOAT || hardwareDepthResolveAvailable)
if (surface->format != RenderFormat::D32_FLOAT || g_capabilities.resolveModes)
{
if (surface->format == RenderFormat::D32_FLOAT)
commandList->resolveTextureRegion(texture->texture, 0, 0, surface->texture, nullptr, RenderResolveMode::MIN);
@ -3540,7 +3599,7 @@ static void ExecutePendingStretchRectCommands(GuestSurface* renderTarget, GuestS
g_dirtyStates.pipelineState = true;
g_dirtyStates.scissorRect = true;
if (g_vulkan)
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.
@ -3853,7 +3912,7 @@ static void ProcSetScissorRect(const RenderCommand& cmd)
static RenderShader* GetOrLinkShader(GuestShader* guestShader, uint32_t specConstants)
{
if (g_vulkan ||
if (g_backend != Backend::D3D12 ||
guestShader->shaderCacheEntry == nullptr ||
guestShader->shaderCacheEntry->specConstantsMask == 0)
{
@ -3863,7 +3922,8 @@ static RenderShader* GetOrLinkShader(GuestShader* guestShader, uint32_t specCons
{
assert(guestShader->shaderCacheEntry != nullptr);
if (g_vulkan)
switch (g_backend) {
case Backend::VULKAN:
{
auto compressedSpirvData = g_shaderCache.get() + guestShader->shaderCacheEntry->spirvOffset;
@ -3871,13 +3931,26 @@ static RenderShader* GetOrLinkShader(GuestShader* guestShader, uint32_t specCons
bool result = smolv::Decode(compressedSpirvData, guestShader->shaderCacheEntry->spirvSize, decoded.data(), decoded.size());
assert(result);
guestShader->shader = g_device->createShader(decoded.data(), decoded.size(), "main", RenderShaderFormat::SPIRV);
guestShader->shader = g_device->createShader(decoded.data(), decoded.size(), "shaderMain", RenderShaderFormat::SPIRV);
break;
}
else
case Backend::D3D12:
{
guestShader->shader = g_device->createShader(g_shaderCache.get() + guestShader->shaderCacheEntry->dxilOffset,
guestShader->shaderCacheEntry->dxilSize, "main", RenderShaderFormat::DXIL);
guestShader->shader = g_device->createShader(g_shaderCache.get() + guestShader->shaderCacheEntry->dxilOffset,
guestShader->shaderCacheEntry->dxilSize, "shaderMain", RenderShaderFormat::DXIL);
break;
}
case Backend::METAL:
{
guestShader->shader = g_device->createShader(g_shaderCache.get() + guestShader->shaderCacheEntry->airOffset,
guestShader->shaderCacheEntry->airSize, "shaderMain", RenderShaderFormat::METAL);
break;
}
}
#ifdef _DEBUG
guestShader->shader->setName(fmt::format("{}:{:x}", guestShader->shaderCacheEntry->filename, guestShader->shaderCacheEntry->hash));
#endif
}
return guestShader->shader.get();
@ -3981,7 +4054,7 @@ static RenderShader* GetOrLinkShader(GuestShader* guestShader, uint32_t specCons
const wchar_t* libraryNames[] = { specConstantsLibName, shaderLibName };
ComPtr<IDxcOperationResult> result;
HRESULT hr = s_dxcLinker->Link(L"main", guestShader->type == ResourceType::VertexShader ? L"vs_6_0" : L"ps_6_0",
HRESULT hr = s_dxcLinker->Link(L"shaderMain", guestShader->type == ResourceType::VertexShader ? L"vs_6_0" : L"ps_6_0",
libraryNames, std::size(libraryNames), nullptr, 0, result.GetAddressOf());
assert(SUCCEEDED(hr) && result != nullptr);
@ -3996,11 +4069,15 @@ static RenderShader* GetOrLinkShader(GuestShader* guestShader, uint32_t specCons
auto& linkedShader = guestShader->linkedShaders[specConstants];
if (linkedShader == nullptr)
{
linkedShader = g_device->createShader(blob->GetBufferPointer(), blob->GetBufferSize(), "main", RenderShaderFormat::DXIL);
linkedShader = g_device->createShader(blob->GetBufferPointer(), blob->GetBufferSize(), "shaderMain", RenderShaderFormat::DXIL);
guestShader->shaderBlobs.push_back(std::move(blob));
}
shader = linkedShader.get();
#ifdef _DEBUG
shader->setName(fmt::format("{}:{:x}", guestShader->shaderCacheEntry->filename, guestShader->shaderCacheEntry->hash));
#endif
}
}
#endif
@ -4494,7 +4571,7 @@ static void FlushRenderStateForRenderThread()
// D3D12 resets depth bias values to the pipeline values, even if they are dynamic.
// We can reduce unnecessary calls by making common depth bias values part of the pipeline.
if (g_capabilities.dynamicDepthBias && !g_vulkan)
if (g_capabilities.dynamicDepthBias && g_backend == Backend::D3D12)
{
bool useDepthBias = (g_depthBias != 0) || (g_slopeScaledDepthBias != 0.0f);
@ -4510,7 +4587,7 @@ static void FlushRenderStateForRenderThread()
commandList->setPipeline(CreateGraphicsPipelineInRenderThread(g_pipelineState));
// D3D12 resets the depth bias values. Check if they need to be set again.
if (g_capabilities.dynamicDepthBias && !g_vulkan)
if (g_capabilities.dynamicDepthBias && g_backend == Backend::D3D12)
g_dirtyStates.depthBias = (g_depthBias != g_pipelineState.depthBias) || (g_slopeScaledDepthBias != g_pipelineState.slopeScaledDepthBias);
}
@ -4544,7 +4621,7 @@ static void FlushRenderStateForRenderThread()
g_inputSlots + g_dirtyStates.vertexStreamFirst);
}
if (g_dirtyStates.indices && (!g_vulkan || g_indexBufferView.buffer.ref != nullptr))
if (g_dirtyStates.indices && (g_backend == Backend::D3D12 || g_indexBufferView.buffer.ref != nullptr))
commandList->setIndexBuffer(&g_indexBufferView);
g_dirtyStates = DirtyStates(false);
@ -6479,7 +6556,7 @@ static void CompileMeshPipeline(const Mesh& mesh, CompilationArgs& args)
if (g_capabilities.dynamicDepthBias)
{
// Put common depth bias values for reducing unnecessary calls.
if (!g_vulkan)
if (g_backend == Backend::D3D12)
{
pipelineState.depthBias = COMMON_DEPTH_BIAS_VALUE;
pipelineState.slopeScaledDepthBias = COMMON_SLOPE_SCALED_DEPTH_BIAS_VALUE;
@ -7240,8 +7317,8 @@ static void PipelineTaskConsumerThread()
if (!g_capabilities.triangleFan && pipelineState.primitiveTopology == RenderPrimitiveTopology::TRIANGLE_FAN)
pipelineState.primitiveTopology = RenderPrimitiveTopology::TRIANGLE_LIST;
// Zero out depth bias for Vulkan, we only store common values for D3D12.
if (g_capabilities.dynamicDepthBias && g_vulkan)
// Zero out depth bias for Vulkan/Metal, we only store common values for D3D12.
if (g_capabilities.dynamicDepthBias && g_backend != Backend::D3D12)
{
pipelineState.depthBias = 0;
pipelineState.slopeScaledDepthBias = 0.0f;

View file

@ -26,6 +26,12 @@ struct Video
static void ComputeViewportDimensions();
};
enum class Backend {
VULKAN,
D3D12,
METAL
};
struct GuestSamplerState
{
be<uint32_t> data[6];

View file

@ -305,6 +305,9 @@ CONFIG_DEFINE_ENUM_TEMPLATE(EGraphicsAPI)
{ "Auto", EGraphicsAPI::Auto },
#ifdef UNLEASHED_RECOMP_D3D12
{ "D3D12", EGraphicsAPI::D3D12 },
#endif
#ifdef UNLEASHED_RECOMP_METAL
{ "Metal", EGraphicsAPI::Metal },
#endif
{ "Vulkan", EGraphicsAPI::Vulkan }
};

View file

@ -70,6 +70,9 @@ enum class EGraphicsAPI : uint32_t
Auto,
#ifdef UNLEASHED_RECOMP_D3D12
D3D12,
#endif
#ifdef UNLEASHED_RECOMP_METAL
Metal,
#endif
Vulkan
};

View file

@ -7,7 +7,10 @@ struct ShaderCacheEntry
const uint32_t dxilSize;
const uint32_t spirvOffset;
const uint32_t spirvSize;
const uint32_t airOffset;
const uint32_t airSize;
const uint32_t specConstantsMask;
char filename[256];
struct GuestShader* guestShader;
};
@ -18,6 +21,10 @@ extern const uint8_t g_compressedDxilCache[];
extern const size_t g_dxilCacheCompressedSize;
extern const size_t g_dxilCacheDecompressedSize;
extern const uint8_t g_compressedAirCache[];
extern const size_t g_airCacheCompressedSize;
extern const size_t g_airCacheDecompressedSize;
extern const uint8_t g_compressedSpirvCache[];
extern const size_t g_spirvCacheCompressedSize;
extern const size_t g_spirvCacheDecompressedSize;

@ -1 +1 @@
Subproject commit 3a0b07a24a4a681ffe70b461b1f4333b2729e2ef
Subproject commit 3e935eb5642813f0506518771528aee37539e601

@ -1 +1 @@
Subproject commit 22b22f5685d868828be01c9ac00c31902e60afd9
Subproject commit 7affe74d77f93a622bb5002789d5332d32e512ee

2
thirdparty/plume vendored

@ -1 +1 @@
Subproject commit 11926860e878e68626ea99ec88562ce2b8badc4f
Subproject commit 61e19e19dcf4f44fbe7f7445f21904b0e27fbb3d

@ -1 +1 @@
Subproject commit 990d03b28a27b50277ee5d8d942e1c5f873869d1
Subproject commit 490699203914fe240b86ccf2401fe1f7d37b1bef