mirror of
https://github.com/PCSX2/pcsx2.git
synced 2026-09-11 23:32:56 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4e9c5dcc88 | ||
|
|
450a39a37c | ||
|
|
ca0145e6aa | ||
|
|
20c51dd80e | ||
|
|
aaf03c18b6 | ||
|
|
77bec5e0f5 | ||
|
|
f0cd834ed2 | ||
|
|
77cfac7dbe | ||
|
|
ca5942151a | ||
|
|
0be8947a33 | ||
|
|
33127ad0df | ||
|
|
c475c752bf | ||
|
|
bd9b6daaad | ||
|
|
9120e6fb6a | ||
|
|
65dc286e9b | ||
|
|
4545e24f7d | ||
|
|
deb6edc076 |
@@ -3,6 +3,7 @@ name: Application Bug Report
|
||||
description: Found a problem with the application itself (ie. bad file path handling, UX issue)? Help us improve it.
|
||||
title: "[BUG]: "
|
||||
labels: [Bug]
|
||||
type: Bug
|
||||
# assignees:
|
||||
# - octocat
|
||||
body:
|
||||
|
||||
@@ -3,6 +3,7 @@ name: Emulation Bug Report
|
||||
description: Problem in a game (ie. graphical artifacts, crashes)? Help us improve it.
|
||||
title: "[BUG]: "
|
||||
labels: [Bug]
|
||||
type: Bug
|
||||
# assignees:
|
||||
# - octocat
|
||||
body:
|
||||
|
||||
@@ -3,6 +3,7 @@ name: Feature request
|
||||
description: Suggest a new feature or improve an existing one
|
||||
title: "[Feature Request]: "
|
||||
labels: ["Enhancement / Feature Request", "FR: Awaiting Consideration"]
|
||||
type: Feature Request
|
||||
# assignees:
|
||||
# - octocat
|
||||
body:
|
||||
|
||||
@@ -45,7 +45,7 @@ jobs:
|
||||
name: ${{ inputs.jobName }}
|
||||
runs-on: ${{ inputs.os }}
|
||||
container:
|
||||
image: ghcr.io/flathub-infra/flatpak-github-actions:kde-6.9
|
||||
image: ghcr.io/flathub-infra/flatpak-github-actions:kde-6.10
|
||||
options: --privileged
|
||||
timeout-minutes: 60
|
||||
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
{
|
||||
"app-id": "net.pcsx2.PCSX2",
|
||||
"runtime": "org.kde.Platform",
|
||||
"runtime-version": "6.10",
|
||||
"runtime-version": "6.11",
|
||||
"sdk": "org.kde.Sdk",
|
||||
"sdk-extensions": [
|
||||
"org.freedesktop.Sdk.Extension.llvm20"
|
||||
"org.freedesktop.Sdk.Extension.llvm22"
|
||||
],
|
||||
"add-extensions": {
|
||||
"org.freedesktop.Platform.ffmpeg-full": {
|
||||
"directory": "lib/ffmpeg",
|
||||
"version": "25.08",
|
||||
"version": "26.08",
|
||||
"add-ld-path": ".",
|
||||
"autodownload": true
|
||||
}
|
||||
@@ -52,8 +52,8 @@
|
||||
"-DCMAKE_PREFIX_PATH=\"${FLATPAK_DEST}\"",
|
||||
"-DCMAKE_BUILD_TYPE=Release",
|
||||
"-DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON",
|
||||
"-DCMAKE_C_COMPILER=/usr/lib/sdk/llvm20/bin/clang",
|
||||
"-DCMAKE_CXX_COMPILER=/usr/lib/sdk/llvm20/bin/clang++",
|
||||
"-DCMAKE_C_COMPILER=/usr/lib/sdk/llvm22/bin/clang",
|
||||
"-DCMAKE_CXX_COMPILER=/usr/lib/sdk/llvm22/bin/clang++",
|
||||
"-DCMAKE_EXE_LINKER_FLAGS_INIT=-fuse-ld=lld",
|
||||
"-DCMAKE_MODULE_LINKER_FLAGS_INIT=-fuse-ld=lld",
|
||||
"-DCMAKE_SHARED_LINKER_FLAGS_INIT=-fuse-ld=lld",
|
||||
|
||||
@@ -0,0 +1,159 @@
|
||||
From 4cf93f0f16b1503580cdb7ffdedb056321446677 Mon Sep 17 00:00:00 2001
|
||||
From: squidbus <175574877+squidbus@users.noreply.github.com>
|
||||
Date: Sat, 9 May 2026 16:53:22 -0700
|
||||
Subject: [PATCH] Restore some checks for Mac1 GPUs.
|
||||
|
||||
---
|
||||
MoltenVK/MoltenVK/GPUObjects/MVKDevice.h | 1 +
|
||||
MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm | 34 +++++++++++--------
|
||||
MoltenVK/MoltenVK/GPUObjects/MVKImage.mm | 2 +-
|
||||
.../MoltenVK/GPUObjects/MVKPixelFormats.mm | 2 +-
|
||||
4 files changed, 23 insertions(+), 16 deletions(-)
|
||||
|
||||
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h
|
||||
index ba3914de5..8923d23f0 100644
|
||||
--- a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h
|
||||
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h
|
||||
@@ -112,6 +112,7 @@ typedef struct MVKMTLDeviceCapabilities {
|
||||
bool supportsApple8;
|
||||
bool supportsApple9;
|
||||
bool supportsApple10;
|
||||
+ bool supportsMac1;
|
||||
bool supportsMac2;
|
||||
bool supportsMetal3;
|
||||
bool supportsMetal4;
|
||||
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm
|
||||
index 2c930b004..2e212645a 100644
|
||||
--- a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm
|
||||
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm
|
||||
@@ -110,7 +110,8 @@
|
||||
#if MVK_XCODE_26
|
||||
supportsApple10 = supportsGPUFam(Apple10, mtlDev);
|
||||
#endif
|
||||
- supportsMac2 = MVK_MACOS; // Incl Mac2 & MacCatalyst2
|
||||
+ supportsMac1 = MVK_MACOS;
|
||||
+ supportsMac2 = supportsGPUFam(Mac2, mtlDev) || supportsGPUFam(MacCatalyst2, mtlDev);
|
||||
|
||||
isAppleGPU = supportsApple1;
|
||||
|
||||
@@ -1547,7 +1548,7 @@
|
||||
const auto placementHeapFlags = VK_IMAGE_CREATE_2D_VIEW_COMPATIBLE_BIT_EXT |
|
||||
VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT |
|
||||
VK_IMAGE_CREATE_BLOCK_TEXEL_VIEW_COMPATIBLE_BIT;
|
||||
- if (!getMVKConfig().useMTLHeap && mvkIsAnyFlagEnabled(flags, placementHeapFlags)) {
|
||||
+ if (!_metalFeatures.placementHeaps && mvkIsAnyFlagEnabled(flags, placementHeapFlags)) {
|
||||
return VK_ERROR_FORMAT_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
@@ -2456,9 +2457,10 @@
|
||||
|
||||
// AMD support for MTLHeap is buggy.
|
||||
auto cfgUseMTLHeap = getMVKConfig().useMTLHeap;
|
||||
- _metalFeatures.placementHeaps = (_properties.vendorID == kAMDVendorId
|
||||
- ? cfgUseMTLHeap == MVK_CONFIG_USE_MTLHEAP_ALWAYS
|
||||
- : cfgUseMTLHeap != MVK_CONFIG_USE_MTLHEAP_NEVER);
|
||||
+ auto allowMTLHeap = (_properties.vendorID == kAMDVendorId
|
||||
+ ? cfgUseMTLHeap == MVK_CONFIG_USE_MTLHEAP_ALWAYS
|
||||
+ : cfgUseMTLHeap != MVK_CONFIG_USE_MTLHEAP_NEVER);
|
||||
+ _metalFeatures.placementHeaps = allowMTLHeap && (supportsMTLGPUFamily(Apple2) || supportsMTLGPUFamily(Mac2));
|
||||
_metalFeatures.multisampleArrayTextures = !MVK_TVOS || mvkOSVersionIsAtLeast(16.0);
|
||||
|
||||
// Dynamic vertex stride needs to have everything aligned - compiled with support for vertex stride calls, and supported by both runtime OS and GPU.
|
||||
@@ -2467,7 +2469,7 @@
|
||||
|
||||
_metalFeatures.renderLinearTextures = _gpuCapabilities.supportsRenderLinearTextures;
|
||||
|
||||
- if (supportsMTLGPUFamily(Mac2)) {
|
||||
+ if (supportsMTLGPUFamily(Mac1)) {
|
||||
_metalFeatures.mtlBufferAlignment = 256;
|
||||
_metalFeatures.mtlConstantBufferAlignment = 32;
|
||||
_metalFeatures.mtlCopyBufferAlignment = 4;
|
||||
@@ -2489,15 +2491,18 @@
|
||||
_metalFeatures.presentModeImmediate = true;
|
||||
_metalFeatures.nonUniformThreadgroups = true;
|
||||
_metalFeatures.native3DCompressedTextures = true;
|
||||
- _metalFeatures.multisampleLayeredRendering = _metalFeatures.layeredRendering;
|
||||
+ _metalFeatures.simdPermute = true;
|
||||
+ _metalFeatures.memoryBarriers = true;
|
||||
+ }
|
||||
+
|
||||
+ if (supportsMTLGPUFamily(Mac2)) {
|
||||
+ _metalFeatures.multisampleLayeredRendering = true;
|
||||
_metalFeatures.stencilFeedback = true;
|
||||
_metalFeatures.depthResolve = true;
|
||||
_metalFeatures.stencilResolve = true;
|
||||
- _metalFeatures.simdPermute = true;
|
||||
_metalFeatures.quadPermute = true;
|
||||
_metalFeatures.simdReduction = true;
|
||||
- _metalFeatures.memoryBarriers = true;
|
||||
- }
|
||||
+ }
|
||||
|
||||
if (supportsMTLGPUFamily(Apple1)) {
|
||||
_metalFeatures.mtlBufferAlignment = 64;
|
||||
@@ -2798,7 +2803,7 @@
|
||||
_features.shaderResourceMinLod = true;
|
||||
}
|
||||
|
||||
- if (supportsMTLGPUFamily(Mac2)) {
|
||||
+ if (supportsMTLGPUFamily(Mac1)) {
|
||||
_features.occlusionQueryPrecise = true;
|
||||
_features.imageCubeArray = true;
|
||||
_features.tessellationShader = true;
|
||||
@@ -2970,19 +2975,19 @@
|
||||
|
||||
if (supportsMTLGPUFamily(Apple3)) {
|
||||
_properties.limits.optimalBufferCopyOffsetAlignment = 16;
|
||||
- } else if (supportsMTLGPUFamily(Mac2)) {
|
||||
+ } else if (supportsMTLGPUFamily(Mac1)) {
|
||||
_properties.limits.optimalBufferCopyOffsetAlignment = 256;
|
||||
} else {
|
||||
_properties.limits.optimalBufferCopyOffsetAlignment = 64;
|
||||
}
|
||||
|
||||
- if (supportsMTLGPUFamily(Apple4) || supportsMTLGPUFamily(Mac2)) {
|
||||
+ if (supportsMTLGPUFamily(Apple4) || supportsMTLGPUFamily(Mac1)) {
|
||||
_properties.limits.maxFragmentInputComponents = 124;
|
||||
} else {
|
||||
_properties.limits.maxFragmentInputComponents = 60;
|
||||
}
|
||||
|
||||
- if (supportsMTLGPUFamily(Apple5) || supportsMTLGPUFamily(Mac2)) {
|
||||
+ if (supportsMTLGPUFamily(Apple5) || supportsMTLGPUFamily(Mac1)) {
|
||||
_properties.limits.maxTessellationGenerationLevel = 64;
|
||||
_properties.limits.maxTessellationPatchSize = 32;
|
||||
} else if (supportsMTLGPUFamily(Apple3)) {
|
||||
@@ -3653,6 +3658,7 @@ static uint32_t mvkGetEntryProperty(io_registry_entry_t entry, CFStringRef prope
|
||||
if (supportsMTLGPUFamily(Apple2)) { logMsg += "\n\t\tGPU Family Apple 2"; } else
|
||||
if (supportsMTLGPUFamily(Apple1)) { logMsg += "\n\t\tGPU Family Apple 1"; }
|
||||
|
||||
+ if (supportsMTLGPUFamily(Mac1)) { logMsg += "\n\t\tGPU Family Mac 1"; }
|
||||
if (supportsMTLGPUFamily(Mac2)) { logMsg += "\n\t\tGPU Family Mac 2"; }
|
||||
|
||||
logMsg += "\n\t\tRead-Write Texture Tier ";
|
||||
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm b/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm
|
||||
index d2fdd4020..696ec4467 100644
|
||||
--- a/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm
|
||||
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm
|
||||
@@ -1382,7 +1382,7 @@ static MTLRegion getMTLRegion(const ImgRgn& imgRgn) {
|
||||
const auto placementHeapFlags = VK_IMAGE_CREATE_2D_VIEW_COMPATIBLE_BIT_EXT |
|
||||
VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT |
|
||||
VK_IMAGE_CREATE_BLOCK_TEXEL_VIEW_COMPATIBLE_BIT;
|
||||
- if (!getMVKConfig().useMTLHeap && mvkIsAnyFlagEnabled(pCreateInfo->flags, placementHeapFlags)) {
|
||||
+ if (!getMetalFeatures().placementHeaps && mvkIsAnyFlagEnabled(pCreateInfo->flags, placementHeapFlags)) {
|
||||
setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCreateImage() : MTLHeap must be enabled to create 2D-on-3D or block texel view compatible images."));
|
||||
}
|
||||
|
||||
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.mm b/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.mm
|
||||
index d16d6c8f6..11ca10a03 100644
|
||||
--- a/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.mm
|
||||
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.mm
|
||||
@@ -1343,7 +1343,7 @@
|
||||
|
||||
addMTLVertexFormatDesc( UChar4Normalized_BGRA );
|
||||
|
||||
- if (gpuCaps.supportsApple5 || gpuCaps.supportsMac2) {
|
||||
+ if (gpuCaps.supportsApple5 || gpuCaps.supportsMac1) {
|
||||
addMTLVertexFormatDesc( FloatRG11B10 );
|
||||
addMTLVertexFormatDesc( FloatRGB9E5 );
|
||||
}
|
||||
@@ -289,6 +289,8 @@ echo "Installing MoltenVK..."
|
||||
rm -fr "MoltenVK-${MOLTENVK}"
|
||||
tar xf "MoltenVK-$MOLTENVK.tar.gz"
|
||||
cd "MoltenVK-${MOLTENVK}"
|
||||
patch -p1 < "$SCRIPTDIR/4cf93f0f16b1503580cdb7ffdedb056321446677.patch"
|
||||
patch -p1 < "$SCRIPTDIR/mvk-texture-swizzle.patch"
|
||||
./fetchDependencies --macos
|
||||
make macos MVK_CONFIG_USE_METAL_ARGUMENT_BUFFERS=0 MVK_CONFIG_USE_METAL_PRIVATE_API=1
|
||||
cp Package/Latest/MoltenVK/dynamic/dylib/macOS/libMoltenVK.dylib "$INSTALLDIR/lib/"
|
||||
|
||||
@@ -78,7 +78,7 @@ e4ab7009bf0629fd11982d4c2aa83964cf244cffba7347ecd39019a9e38c4564 libwebp-$LIBWE
|
||||
eee7dea22ed502868017971c86c63c4ed1e6085de0baebfdcc3d3322f00f3eb0 libpng-$LIBPNG-apng.patch.gz
|
||||
6f30092cef9fb839779646608f4ee14ae3cbac989c47fa05e841b0841f09878e libjpeg-turbo-$LIBJPEGTURBO.tar.gz
|
||||
cf38e0e28c7e5605942c4a77755349b0145804a397af37eb1fb4c77cb237f635 ffmpeg-$FFMPEG.tar.xz
|
||||
9985f141902a17de818e264d17c1ce334b748e499ee02fcb4703e4dc0038f89c v$MOLTENVK.tar.gz
|
||||
9985f141902a17de818e264d17c1ce334b748e499ee02fcb4703e4dc0038f89c MoltenVK-$MOLTENVK.tar.gz
|
||||
5cd9495d9316cf6cc937bb328a7fd491c3248ef2469cfa42511f1039f6148aca KDDockWidgets-$KDDOCKWIDGETS.tar.gz
|
||||
7bd4e79ce18b1d47517e7e91fbb7cf19d4f01942804a519bc7c0bf32b6325dd5 plutovg-$PLUTOVG.tar.gz
|
||||
78561b571ac224030cdc450ca2986b4de915c2ba7616004a6d71a379bffd15f3 plutosvg-$PLUTOSVG.tar.gz
|
||||
@@ -102,7 +102,7 @@ if ! shasum -sa 256 --check SHASUMS 2> /dev/null; then
|
||||
-O "https://github.com/libjpeg-turbo/libjpeg-turbo/releases/download/$LIBJPEGTURBO/libjpeg-turbo-$LIBJPEGTURBO.tar.gz" \
|
||||
-O "https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-$LIBWEBP.tar.gz" \
|
||||
-O "https://ffmpeg.org/releases/ffmpeg-$FFMPEG.tar.xz" \
|
||||
-O "https://github.com/KhronosGroup/MoltenVK/archive/refs/tags/v$MOLTENVK.tar.gz" \
|
||||
-O "https://github.com/KhronosGroup/MoltenVK/archive/v$MOLTENVK/MoltenVK-$MOLTENVK.tar.gz" \
|
||||
-O "https://download.qt.io/archive/qt/${QT%.*}/$QT/submodules/qtbase-everywhere-src-$QT.tar.xz" \
|
||||
-O "https://download.qt.io/archive/qt/${QT%.*}/$QT/submodules/qtimageformats-everywhere-src-$QT.tar.xz" \
|
||||
-O "https://download.qt.io/archive/qt/${QT%.*}/$QT/submodules/qtsvg-everywhere-src-$QT.tar.xz" \
|
||||
@@ -228,8 +228,10 @@ cd ..
|
||||
# MoltenVK already builds universal binaries, nothing special to do here.
|
||||
echo "Installing MoltenVK..."
|
||||
rm -fr "MoltenVK-${MOLTENVK}"
|
||||
tar xf "v$MOLTENVK.tar.gz"
|
||||
tar xf "MoltenVK-$MOLTENVK.tar.gz"
|
||||
cd "MoltenVK-${MOLTENVK}"
|
||||
patch -p1 < "$SCRIPTDIR/4cf93f0f16b1503580cdb7ffdedb056321446677.patch"
|
||||
patch -p1 < "$SCRIPTDIR/mvk-texture-swizzle.patch"
|
||||
sed -i '' 's/xcodebuild "$@"/xcodebuild $XCODEBUILD_EXTRA_ARGS "$@"/g' fetchDependencies
|
||||
sed -i '' 's/XCODEBUILD :=/XCODEBUILD ?=/g' Makefile
|
||||
XCODEBUILD_EXTRA_ARGS="VALID_ARCHS=x86_64" ./fetchDependencies --macos
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
From f6be31a110761ea494a938d62473abc085ea447b Mon Sep 17 00:00:00 2001
|
||||
From: TellowKrinkle <tellowkrinkle@gmail.com>
|
||||
Date: Mon, 31 Aug 2026 02:52:38 -0500
|
||||
Subject: [PATCH] Re-add native texture swizzle checks
|
||||
|
||||
---
|
||||
MoltenVK/MoltenVK/API/mvk_deprecated_api.h | 2 +-
|
||||
MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm | 3 +-
|
||||
MoltenVK/MoltenVK/GPUObjects/MVKImage.mm | 51 ++++++++++++++++++-
|
||||
.../MoltenVK/GPUObjects/MVKPixelFormats.mm | 3 ++
|
||||
4 files changed, 56 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/MoltenVK/MoltenVK/API/mvk_deprecated_api.h b/MoltenVK/MoltenVK/API/mvk_deprecated_api.h
|
||||
index 74fef844..8d16f86c 100644
|
||||
--- a/MoltenVK/MoltenVK/API/mvk_deprecated_api.h
|
||||
+++ b/MoltenVK/MoltenVK/API/mvk_deprecated_api.h
|
||||
@@ -118,7 +118,7 @@ typedef struct {
|
||||
VkBool32 fences; /**< If true, Metal synchronization fences (MTLFence) are supported. Deprecated. Will always be true on all platforms. */
|
||||
VkBool32 rasterOrderGroups; /**< If true, Raster order groups in fragment shaders are supported. */
|
||||
VkBool32 native3DCompressedTextures; /**< If true, 3D compressed images are supported natively, without manual decompression. */
|
||||
- VkBool32 nativeTextureSwizzle; /**< If true, component swizzle is supported natively, without manual swizzling in shaders. Deprecated. Will always be true on all platforms. */
|
||||
+ VkBool32 nativeTextureSwizzle; /**< If true, component swizzle is supported natively, without manual swizzling in shaders. */
|
||||
VkBool32 placementHeaps; /**< If true, MTLHeap objects support placement of resources. */
|
||||
VkDeviceSize pushConstantSizeAlignment; /**< The alignment used internally when allocating memory for push constants. Must be PoT. */
|
||||
uint32_t maxTextureLayers; /**< The maximum number of layers in an array texture. */
|
||||
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm
|
||||
index aaf5945b..c7da8256 100644
|
||||
--- a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm
|
||||
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm
|
||||
@@ -2496,6 +2496,7 @@
|
||||
_metalFeatures.stencilResolve = true;
|
||||
_metalFeatures.quadPermute = true;
|
||||
_metalFeatures.simdReduction = true;
|
||||
+ _metalFeatures.nativeTextureSwizzle = true;
|
||||
}
|
||||
|
||||
if (supportsMTLGPUFamily(Apple1)) {
|
||||
@@ -2504,6 +2505,7 @@
|
||||
_metalFeatures.maxPerStageTextureCount = 31;
|
||||
_metalFeatures.maxTextureDimension = (8 * KIBI);
|
||||
_metalFeatures.maxQueryBufferSize = (64 * KIBI);
|
||||
+ _metalFeatures.nativeTextureSwizzle = true;
|
||||
|
||||
_metalFeatures.maxPerStageDynamicMTLBufferCount = _metalFeatures.maxPerStageBufferCount;
|
||||
_metalFeatures.renderLinearTextures = true;
|
||||
@@ -2727,7 +2729,6 @@
|
||||
_metalFeatures.events = true;
|
||||
_metalFeatures.ioSurfaces = true;
|
||||
_metalFeatures.renderWithoutAttachments = true;
|
||||
- _metalFeatures.nativeTextureSwizzle = true;
|
||||
}
|
||||
|
||||
bool MVKPhysicalDevice::isTier2MetalArgumentBuffers() {
|
||||
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm b/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm
|
||||
index 8eda5515..392a4f98 100644
|
||||
--- a/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm
|
||||
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm
|
||||
@@ -1925,7 +1925,7 @@ static void signalAndUntrack(const MVKSwapchainSignaler& signaler) {
|
||||
}
|
||||
|
||||
id<MTLTexture> texView = nil;
|
||||
- if (_useSwizzle) {
|
||||
+ if (_useSwizzle && getMetalFeatures().nativeTextureSwizzle) {
|
||||
texView = [mtlTex newTextureViewWithPixelFormat: _mtlPixFmt
|
||||
textureType: _imageView->_mtlTextureType
|
||||
levels: levelRange
|
||||
@@ -2103,6 +2103,55 @@ static void signalAndUntrack(const MVKSwapchainSignaler& signaler) {
|
||||
}
|
||||
}
|
||||
|
||||
+#define SWIZZLE_MATCHES(R, G, B, A) mvkVkComponentMappingsMatch(_componentSwizzle, {VK_COMPONENT_SWIZZLE_ ##R, VK_COMPONENT_SWIZZLE_ ##G, VK_COMPONENT_SWIZZLE_ ##B, VK_COMPONENT_SWIZZLE_ ##A} )
|
||||
+#define VK_COMPONENT_SWIZZLE_ANY VK_COMPONENT_SWIZZLE_MAX_ENUM
|
||||
+
|
||||
+ if (_imageView->_image->hasPixelFormatView(_planeIndex)) {
|
||||
+ switch (_mtlPixFmt) {
|
||||
+ case MTLPixelFormatR8Unorm:
|
||||
+ if (SWIZZLE_MATCHES(ZERO, ANY, ANY, R)) {
|
||||
+ _mtlPixFmt = MTLPixelFormatA8Unorm;
|
||||
+ return VK_SUCCESS;
|
||||
+ }
|
||||
+ break;
|
||||
+
|
||||
+ case MTLPixelFormatA8Unorm:
|
||||
+ if (SWIZZLE_MATCHES(A, ANY, ANY, ZERO)) {
|
||||
+ _mtlPixFmt = MTLPixelFormatR8Unorm;
|
||||
+ return VK_SUCCESS;
|
||||
+ }
|
||||
+ break;
|
||||
+
|
||||
+ case MTLPixelFormatRGBA8Unorm:
|
||||
+ if (SWIZZLE_MATCHES(B, G, R, A)) {
|
||||
+ _mtlPixFmt = MTLPixelFormatBGRA8Unorm;
|
||||
+ return VK_SUCCESS;
|
||||
+ }
|
||||
+ break;
|
||||
+
|
||||
+ case MTLPixelFormatRGBA8Unorm_sRGB:
|
||||
+ if (SWIZZLE_MATCHES(B, G, R, A)) {
|
||||
+ _mtlPixFmt = MTLPixelFormatBGRA8Unorm_sRGB;
|
||||
+ return VK_SUCCESS;
|
||||
+ }
|
||||
+ break;
|
||||
+
|
||||
+ case MTLPixelFormatBGRA8Unorm:
|
||||
+ if (SWIZZLE_MATCHES(B, G, R, A)) {
|
||||
+ _mtlPixFmt = MTLPixelFormatRGBA8Unorm;
|
||||
+ return VK_SUCCESS;
|
||||
+ }
|
||||
+ break;
|
||||
+
|
||||
+ case MTLPixelFormatBGRA8Unorm_sRGB:
|
||||
+ if (SWIZZLE_MATCHES(B, G, R, A)) {
|
||||
+ _mtlPixFmt = MTLPixelFormatRGBA8Unorm_sRGB;
|
||||
+ return VK_SUCCESS;
|
||||
+ }
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
_useSwizzle = !mvkVkComponentMappingsMatch(_componentSwizzle, {VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A});
|
||||
return VK_SUCCESS;
|
||||
}
|
||||
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.mm b/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.mm
|
||||
index 316d7c6f..31a2c6a1 100644
|
||||
--- a/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.mm
|
||||
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.mm
|
||||
@@ -654,6 +654,9 @@
|
||||
|
||||
bool pfv = false;
|
||||
|
||||
+ // Swizzle emulation may need to reinterpret
|
||||
+ needsReinterpretation |= !_physicalDevice->getMetalFeatures()->nativeTextureSwizzle;
|
||||
+
|
||||
pfv |= isColorFormat && needsReinterpretation &&
|
||||
mvkIsAnyFlagEnabled(vkImageUsageFlags, (VK_IMAGE_USAGE_SAMPLED_BIT |
|
||||
VK_IMAGE_USAGE_STORAGE_BIT |
|
||||
--
|
||||
2.39.5 (Apple Git-154)
|
||||
|
||||
@@ -4706,6 +4706,7 @@ SCES-50000:
|
||||
gpuPaletteConversion: 2 # Lots of CLUTs in large textures.
|
||||
cpuSpriteRenderBW: 1 # Fixes car textures.
|
||||
cpuSpriteRenderLevel: 1 # Needed for above.
|
||||
rewriteLargeST: 1 # Fix reflections on cars.
|
||||
SCES-50001:
|
||||
name: "Tekken Tag Tournament"
|
||||
region: "PAL-M5"
|
||||
@@ -35387,6 +35388,7 @@ SLPM-60109:
|
||||
gpuPaletteConversion: 2 # Lots of CLUTs in large textures.
|
||||
cpuSpriteRenderBW: 1 # Fixes car textures.
|
||||
cpuSpriteRenderLevel: 1 # Needed for above.
|
||||
rewriteLargeST: 1 # Fix reflections on cars.
|
||||
SLPM-60112:
|
||||
name: "フレースヴェルグ [体験版]"
|
||||
name-sort: "ふれーすヴぇるぐ [たいけんばん]"
|
||||
@@ -54809,6 +54811,7 @@ SLPS-20001:
|
||||
gpuPaletteConversion: 2 # Lots of CLUTs in large textures.
|
||||
cpuSpriteRenderBW: 1 # Fixes car textures.
|
||||
cpuSpriteRenderLevel: 1 # Needed for above.
|
||||
rewriteLargeST: 1 # Fix reflections on cars.
|
||||
SLPS-20002:
|
||||
name: "撞球 ビリヤードマスター2"
|
||||
name-sort: "どうきゅう びりやーどますたー2"
|
||||
@@ -63501,6 +63504,7 @@ SLPS-71502:
|
||||
gpuPaletteConversion: 2 # Lots of CLUTs in large textures.
|
||||
cpuSpriteRenderBW: 1 # Fixes car textures.
|
||||
cpuSpriteRenderLevel: 1 # Needed for above.
|
||||
rewriteLargeST: 1 # Fix reflections on cars.
|
||||
SLPS-72501:
|
||||
name: "ファイナルファンタジーⅩ [MEGA HITS!]"
|
||||
name-sort: "ふぁいなるふぁんたじー10 [MEGA HITS!]"
|
||||
@@ -64503,6 +64507,7 @@ SLUS-20002:
|
||||
gpuPaletteConversion: 2 # Lots of CLUTs in large textures.
|
||||
cpuSpriteRenderBW: 1 # Fixes car textures.
|
||||
cpuSpriteRenderLevel: 1 # Needed for above.
|
||||
rewriteLargeST: 1 # Fix reflections on cars.
|
||||
SLUS-20003:
|
||||
name: "Portal Runner"
|
||||
region: "NTSC-U"
|
||||
|
||||
@@ -169,6 +169,11 @@ float depth32_to_depth24(float d)
|
||||
return uint_to_depth24(depth_to_uint(d));
|
||||
}
|
||||
|
||||
float4 primid_to_rgba8(float p)
|
||||
{
|
||||
return uint_to_rgba8(asuint(p));
|
||||
}
|
||||
|
||||
#if defined(__ps_copy__)
|
||||
OUTPUT_TYPE ps_copy(PS_INPUT input) : OUTPUT_SV
|
||||
{
|
||||
@@ -323,6 +328,13 @@ OUTPUT_TYPE ps_convert_depth32_depth24(PS_INPUT input) : OUTPUT_SV
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(__ps_convert_primid_rgba8__)
|
||||
OUTPUT_TYPE ps_convert_primid_rgba8(PS_INPUT input) : OUTPUT_SV
|
||||
{
|
||||
return primid_to_rgba8(sample_c(input.t));
|
||||
}
|
||||
#endif
|
||||
|
||||
#define SAMPLE_RGBA_DEPTH_BILN(CONVERT_FN) \
|
||||
uint width, height; \
|
||||
Texture.GetDimensions(width, height); \
|
||||
@@ -650,9 +662,9 @@ float ps_primid_image_init_0(PS_INPUT input) : SV_Target
|
||||
{
|
||||
float c;
|
||||
if ((127.5f / 255.0f) < sample_c(input.t).a) // < 0x80 pass (== 0x80 should not pass)
|
||||
c = float(-1);
|
||||
c = float(PRIMID_MIN);
|
||||
else
|
||||
c = float(0x7FFFFFFF);
|
||||
c = float(PRIMID_MAX);
|
||||
return c;
|
||||
}
|
||||
#endif
|
||||
@@ -662,9 +674,9 @@ float ps_primid_image_init_1(PS_INPUT input) : SV_Target
|
||||
{
|
||||
float c;
|
||||
if (sample_c(input.t).a < (127.5f / 255.0f)) // >= 0x80 pass
|
||||
c = float(-1);
|
||||
c = float(PRIMID_MIN);
|
||||
else
|
||||
c = float(0x7FFFFFFF);
|
||||
c = float(PRIMID_MAX);
|
||||
return c;
|
||||
}
|
||||
#endif
|
||||
@@ -674,9 +686,9 @@ float ps_primid_image_init_2(PS_INPUT input) : SV_Target
|
||||
{
|
||||
float c;
|
||||
if ((254.5f / 255.0f) < sample_c(input.t).a) // < 0x80 pass (== 0x80 should not pass)
|
||||
c = float(-1);
|
||||
c = float(PRIMID_MIN);
|
||||
else
|
||||
c = float(0x7FFFFFFF);
|
||||
c = float(PRIMID_MAX);
|
||||
return c;
|
||||
}
|
||||
#endif
|
||||
@@ -686,9 +698,9 @@ float ps_primid_image_init_3(PS_INPUT input) : SV_Target
|
||||
{
|
||||
float c;
|
||||
if (sample_c(input.t).a < (254.5f / 255.0f)) // >= 0x80 pass
|
||||
c = float(-1);
|
||||
c = float(PRIMID_MIN);
|
||||
else
|
||||
c = float(0x7FFFFFFF);
|
||||
c = float(PRIMID_MAX);
|
||||
return c;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1097,8 +1097,11 @@ void ps_fbmask(inout float4 C, float2 pos_xy)
|
||||
{
|
||||
if (PS_FBMASK)
|
||||
{
|
||||
float multi = PS_COLCLIP_HW ? 65535.0f : 255.0f;
|
||||
float4 RT = trunc(RtLoad(int2(pos_xy)) * multi + 0.1f);
|
||||
float multi_rgb = PS_COLCLIP_HW ? 65535.0f : 255.0f;
|
||||
float multi_a = PS_RTA_CORRECTION ? 128.0f : 255.0f;
|
||||
float4 RT = RtLoad(int2(pos_xy));
|
||||
RT.rgb = trunc(RT.rgb * multi_rgb + 0.1f);
|
||||
RT.a = round(RT.a * multi_a);
|
||||
C = (float4)(((uint4)C & ~FbMask) | ((uint4)RT & FbMask));
|
||||
}
|
||||
}
|
||||
@@ -1594,7 +1597,6 @@ if (bad)
|
||||
output.c1 = o_col1;
|
||||
#endif
|
||||
#elif PS_RETURN_COLOR_ROV
|
||||
o_col0 = (FbMask == 0xFFu) ? RtLoad(input.p.xy) : o_col0; // channel masking
|
||||
if (!rov_discard_color)
|
||||
RtWrite(input.p.xy, o_col0);
|
||||
#endif
|
||||
|
||||
@@ -139,6 +139,11 @@ float depth32_to_depth24(float d)
|
||||
return uint_to_depth24(depth_to_uint(d));
|
||||
}
|
||||
|
||||
vec4 primid_to_rgba8(float p)
|
||||
{
|
||||
return uint_to_rgba8(floatBitsToUint(p));
|
||||
}
|
||||
|
||||
#ifdef ps_copy
|
||||
void ps_copy()
|
||||
{
|
||||
@@ -212,6 +217,13 @@ void ps_convert_depth32_depth24()
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ps_convert_primid_rgba8
|
||||
void ps_convert_primid_rgba8()
|
||||
{
|
||||
OUTPUT = primid_to_rgba8(sample_c());
|
||||
}
|
||||
#endif
|
||||
|
||||
#define SAMPLE_RGBA_DEPTH_BILN(CONVERT_FN) \
|
||||
ivec2 dims = textureSize(TextureSampler, 0); \
|
||||
vec2 top_left_f = PSin_t * vec2(dims) - 0.5f; \
|
||||
@@ -615,23 +627,23 @@ void ps_yuv()
|
||||
|
||||
void main()
|
||||
{
|
||||
o_col0 = vec4(0x7FFFFFFF);
|
||||
o_col0 = vec4(PRIMID_MAX);
|
||||
|
||||
#ifdef ps_primid_image_init_0
|
||||
if((127.5f / 255.0f) < sample_c().a) // < 0x80 pass (== 0x80 should not pass)
|
||||
o_col0 = vec4(-1);
|
||||
o_col0 = vec4(PRIMID_MIN);
|
||||
#endif
|
||||
#ifdef ps_primid_image_init_1
|
||||
if(sample_c().a < (127.5f / 255.0f)) // >= 0x80 pass
|
||||
o_col0 = vec4(-1);
|
||||
o_col0 = vec4(PRIMID_MIN);
|
||||
#endif
|
||||
#ifdef ps_primid_image_init_2
|
||||
if((254.5f / 255.0f) < sample_c().a) // < 0x80 pass (== 0x80 should not pass)
|
||||
o_col0 = vec4(-1);
|
||||
o_col0 = vec4(PRIMID_MIN);
|
||||
#endif
|
||||
#ifdef ps_primid_image_init_3
|
||||
if(sample_c().a < (254.5f / 255.0f)) // >= 0x80 pass
|
||||
o_col0 = vec4(-1);
|
||||
o_col0 = vec4(PRIMID_MIN);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -932,11 +932,11 @@ void ps_fbmask(inout vec4 C)
|
||||
{
|
||||
// FIXME do I need special case for 16 bits
|
||||
#if PS_FBMASK
|
||||
#if PS_COLCLIP_HW == 1
|
||||
vec4 RT = trunc(sample_from_rt() * 65535.0f);
|
||||
#else
|
||||
vec4 RT = trunc(sample_from_rt() * 255.0f + 0.1f);
|
||||
#endif
|
||||
float multi_rgb = PS_COLCLIP_HW != 0 ? 65535.0f : 255.0f;
|
||||
float multi_a = PS_RTA_CORRECTION != 0 ? 128.0f : 255.0f;
|
||||
vec4 RT = sample_from_rt();
|
||||
RT.rgb = trunc(RT.rgb * multi_rgb + 0.1f);
|
||||
RT.a = round(RT.a * multi_a);
|
||||
C = vec4((uvec4(C) & ~FbMask) | (uvec4(RT) & FbMask));
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -130,6 +130,11 @@ float depth32_to_depth24(float d)
|
||||
return uint_to_depth24(depth_to_uint(d));
|
||||
}
|
||||
|
||||
vec4 primid_to_rgba8(float p)
|
||||
{
|
||||
return uint_to_rgba8(floatBitsToUint(p));
|
||||
}
|
||||
|
||||
#ifdef ps_copy
|
||||
void ps_copy()
|
||||
{
|
||||
@@ -281,6 +286,13 @@ void ps_convert_depth32_depth24()
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ps_convert_primid_rgba8
|
||||
void ps_convert_primid_rgba8()
|
||||
{
|
||||
OUTPUT = primid_to_rgba8(sample_c(v_tex));
|
||||
}
|
||||
#endif
|
||||
|
||||
#define SAMPLE_RGBA_DEPTH_BILN(CONVERT_FN) \
|
||||
ivec2 dims = textureSize(samp0, 0); \
|
||||
vec2 top_left_f = v_tex * vec2(dims) - 0.5f; \
|
||||
@@ -628,23 +640,23 @@ void ps_yuv()
|
||||
|
||||
void main()
|
||||
{
|
||||
o_col0 = vec4(0x7FFFFFFF);
|
||||
o_col0 = vec4(PRIMID_MAX);
|
||||
|
||||
#ifdef ps_primid_image_init_0
|
||||
if((127.5f / 255.0f) < sample_c(v_tex).a) // < 0x80 pass (== 0x80 should not pass)
|
||||
o_col0 = vec4(-1);
|
||||
o_col0 = vec4(PRIMID_MIN);
|
||||
#endif
|
||||
#ifdef ps_primid_image_init_1
|
||||
if(sample_c(v_tex).a < (127.5f / 255.0f)) // >= 0x80 pass
|
||||
o_col0 = vec4(-1);
|
||||
o_col0 = vec4(PRIMID_MIN);
|
||||
#endif
|
||||
#ifdef ps_primid_image_init_2
|
||||
if((254.5f / 255.0f) < sample_c(v_tex).a) // < 0x80 pass (== 0x80 should not pass)
|
||||
o_col0 = vec4(-1);
|
||||
o_col0 = vec4(PRIMID_MIN);
|
||||
#endif
|
||||
#ifdef ps_primid_image_init_3
|
||||
if(sample_c(v_tex).a < (254.5f / 255.0f)) // >= 0x80 pass
|
||||
o_col0 = vec4(-1);
|
||||
o_col0 = vec4(PRIMID_MIN);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1448,11 +1448,11 @@ vec4 ps_color()
|
||||
void ps_fbmask(inout vec4 C)
|
||||
{
|
||||
#if PS_FBMASK
|
||||
#if PS_COLCLIP_HW == 1
|
||||
vec4 RT = trunc(sample_from_rt() * 65535.0f);
|
||||
#else
|
||||
vec4 RT = trunc(sample_from_rt() * 255.0f + 0.1f);
|
||||
#endif
|
||||
float multi_rgb = PS_COLCLIP_HW != 0 ? 65535.0f : 255.0f;
|
||||
float multi_a = PS_RTA_CORRECTION != 0 ? 128.0f : 255.0f;
|
||||
vec4 RT = sample_from_rt();
|
||||
RT.rgb = trunc(RT.rgb * multi_rgb + 0.1f);
|
||||
RT.a = round(RT.a * multi_a);
|
||||
C = vec4((uvec4(C) & ~FbMask) | (uvec4(RT) & FbMask));
|
||||
#endif
|
||||
}
|
||||
@@ -1966,8 +1966,6 @@ void main()
|
||||
|
||||
// Writing back color (result already written to o_col0 for non-ROV)
|
||||
#if PS_RETURN_COLOR_ROV
|
||||
o_col0 = mix(o_col0, sample_from_rt(), equal(FbMask, uvec4(0xFFu))); // channel masking
|
||||
|
||||
if (!rov_discard_color)
|
||||
imageStore(RtImageRov, ivec2(gl_FragCoord.xy), o_col0);
|
||||
#endif
|
||||
|
||||
@@ -104,7 +104,7 @@ disable_compiler_warnings_for_target(speex)
|
||||
|
||||
# Find the Qt components that we need.
|
||||
if(ENABLE_QT_UI)
|
||||
find_package(Qt6 6.10.1 COMPONENTS CoreTools Core GuiTools Gui WidgetsTools Widgets LinguistTools REQUIRED)
|
||||
find_package(Qt6 6.11.1 COMPONENTS CoreTools Core GuiTools Gui WidgetsTools Widgets LinguistTools REQUIRED)
|
||||
|
||||
if(NOT WIN32 AND NOT APPLE)
|
||||
if (Qt6_VERSION VERSION_GREATER_EQUAL 6.10.0)
|
||||
|
||||
@@ -271,6 +271,13 @@
|
||||
</item>
|
||||
<item row="7" column="0" colspan="2">
|
||||
<layout class="QGridLayout" name="hwFixesLayout">
|
||||
<item row="5" column="0">
|
||||
<widget class="QCheckBox" name="rewriteLargeST">
|
||||
<property name="text">
|
||||
<string>Rewrite Large ST</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QCheckBox" name="drawBuffering">
|
||||
<property name="text">
|
||||
@@ -389,6 +396,7 @@
|
||||
<tabstop>disableRenderFixes</tabstop>
|
||||
<tabstop>readTCOnClose</tabstop>
|
||||
<tabstop>estimateTextureRegion</tabstop>
|
||||
<tabstop>rewriteLargeST</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections/>
|
||||
|
||||
@@ -155,6 +155,7 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsWindow* settings_dialog,
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_fixes.readTCOnClose, "EmuCore/GS", "UserHacks_ReadTCOnClose", false);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_fixes.estimateTextureRegion, "EmuCore/GS", "UserHacks_EstimateTextureRegion", false);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_fixes.drawBuffering, "EmuCore/GS", "UserHacks_DrawBuffering", false);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_fixes.rewriteLargeST, "EmuCore/GS", "UserHacks_RewriteLargeST", false);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_fixes.gpuPaletteConversion, "EmuCore/GS", "paltex", false);
|
||||
connect(m_fixes.cpuSpriteRenderBW, &QComboBox::currentIndexChanged, this,
|
||||
&GraphicsSettingsWidget::onCPUSpriteRenderBWChanged);
|
||||
@@ -636,6 +637,9 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsWindow* settings_dialog,
|
||||
|
||||
dialog()->registerWidgetHelp(m_fixes.drawBuffering, tr("Draw Buffering"), tr("Unchecked"),
|
||||
tr("Attempts to reduce draw calls in games which do heavy context switching for blending purposes."));
|
||||
|
||||
dialog()->registerWidgetHelp(m_fixes.rewriteLargeST, tr("Rewrite Large ST"), tr("Unchecked"),
|
||||
tr("Rewrite large ST coordinates and clamp the values (mainly for Ridge Racer V)."));
|
||||
}
|
||||
|
||||
// Upscaling Fixes tab
|
||||
|
||||
+1713
-1682
File diff suppressed because it is too large
Load Diff
+7
-4
@@ -468,7 +468,7 @@ u32 cdvdGetElfCRC(const std::string& path)
|
||||
return elfo.GetCRC();
|
||||
}
|
||||
|
||||
static CDVDDiscType GetPS2ElfName(IsoReader& isor, std::string* name, std::string* version, Error* error)
|
||||
static CDVDDiscType GetPS2ElfName(IsoReader& isor, std::string* name, std::string* version, std::string* vmode, Error* error)
|
||||
{
|
||||
CDVDDiscType retype = CDVDDiscType::Other;
|
||||
name->clear();
|
||||
@@ -510,6 +510,7 @@ static CDVDDiscType GetPS2ElfName(IsoReader& isor, std::string* name, std::strin
|
||||
else if (key == "VMODE")
|
||||
{
|
||||
DevCon.WriteLn(Color_Blue, fmt::format("(SYSTEM.CNF) Disc region type = {}", value));
|
||||
*vmode = value;
|
||||
}
|
||||
else if (key == "VER")
|
||||
{
|
||||
@@ -574,15 +575,15 @@ static std::string ExecutablePathToSerial(const std::string& path)
|
||||
return serial;
|
||||
}
|
||||
|
||||
void cdvdGetDiscInfo(std::string* out_serial, std::string* out_elf_path, std::string* out_version, u32* out_crc,
|
||||
void cdvdGetDiscInfo(std::string* out_serial, std::string* out_elf_path, std::string* out_version, std::string* out_region, u32* out_crc,
|
||||
CDVDDiscType* out_disc_type)
|
||||
{
|
||||
Error error;
|
||||
IsoReader isor;
|
||||
|
||||
std::string elfpath, version;
|
||||
std::string elfpath, version, vmode;
|
||||
CDVDDiscType disc_type = CDVDDiscType::Other;
|
||||
if (!isor.Open(&error) || (disc_type = GetPS2ElfName(isor, &elfpath, &version, &error)) == CDVDDiscType::Other)
|
||||
if (!isor.Open(&error) || (disc_type = GetPS2ElfName(isor, &elfpath, &version, &vmode, &error)) == CDVDDiscType::Other)
|
||||
Console.Error(fmt::format("Failed to get ELF name: {}", error.GetDescription()));
|
||||
|
||||
// Don't bother parsing it if we don't need the CRC.
|
||||
@@ -616,6 +617,8 @@ void cdvdGetDiscInfo(std::string* out_serial, std::string* out_elf_path, std::st
|
||||
*out_version = std::move(version);
|
||||
if (out_disc_type)
|
||||
*out_disc_type = disc_type;
|
||||
if (out_region)
|
||||
*out_region = vmode;
|
||||
}
|
||||
|
||||
void cdvdReadKey(u8, u16, u32 arg2, u8* key)
|
||||
|
||||
+1
-1
@@ -179,7 +179,7 @@ extern void cdvdNewDiskCB();
|
||||
extern u8 cdvdRead(u8 key);
|
||||
extern void cdvdWrite(u8 key, u8 rt);
|
||||
|
||||
extern void cdvdGetDiscInfo(std::string* out_serial, std::string* out_elf_path, std::string* out_version, u32* out_crc,
|
||||
extern void cdvdGetDiscInfo(std::string* out_serial, std::string* out_elf_path, std::string* out_version, std::string* out_region, u32* out_crc,
|
||||
CDVDDiscType* out_disc_type);
|
||||
extern u32 cdvdGetElfCRC(const std::string& path);
|
||||
extern bool cdvdLoadElf(ElfObject* elfo, const std::string_view elfpath, bool isPSXElf, Error* error);
|
||||
|
||||
@@ -1263,7 +1263,7 @@ function(setup_main_executable target)
|
||||
if("${file}" MATCHES "^\\.") # Don't copy macOS garbage (mainly Finder's .DS_Store files) into application
|
||||
continue()
|
||||
endif()
|
||||
if (NOT WIN32 AND "${path}" MATCHES "/dx11/") # Don't include unneccessary stuff
|
||||
if (NOT WIN32 AND "${path}" MATCHES "/dx11/") # Don't include unnecessary stuff
|
||||
continue()
|
||||
endif()
|
||||
if (NOT BUNDLE_EMOJI_FONT AND "${path}" MATCHES "fonts/Twemoji")
|
||||
|
||||
@@ -811,6 +811,7 @@ struct Pcsx2Config
|
||||
UserHacks_NativePaletteDraw : 1,
|
||||
UserHacks_EstimateTextureRegion : 1,
|
||||
UserHacks_DrawBuffering : 1,
|
||||
UserHacks_RewriteLargeST : 1,
|
||||
FXAA : 1,
|
||||
ShadeBoost : 1,
|
||||
DumpGSData : 1,
|
||||
|
||||
+6
-4
@@ -215,7 +215,7 @@ static void vSyncInfoCalc(vSyncTimingInfo* info, double framesPerSecond, u32 sca
|
||||
// Dynasty Warriors 3 Xtreme Legends - fake save corruption when loading save
|
||||
// Jak II - random speedups
|
||||
// Shadow of Rome - FMV audio issues
|
||||
const bool ntsc_hblank = gsVideoMode != GS_VideoMode::PAL && gsVideoMode != GS_VideoMode::DVD_PAL;
|
||||
const bool ntsc_hblank = gsVideoMode != GS_VideoMode::PAL && gsVideoMode != GS_VideoMode::DVD_PAL && (gsVideoMode != GS_VideoMode::Uninitialized || framesPerSecond != (Pcsx2Config::GSOptions::DEFAULT_FRAME_RATE_PAL / 2));
|
||||
const u64 HalfFrame = Frame / 2;
|
||||
const float extra_scanlines = static_cast<float>(IsProgressiveVideoMode()) * (ntsc_hblank ? 0.5f : 1.5f);
|
||||
const u64 Blank = Scanline * ((ntsc_hblank ? 22.5f : 24.5f) + extra_scanlines);
|
||||
@@ -307,10 +307,12 @@ double GetVerticalFrequency()
|
||||
// https://web.archive.org/web/20120629231826fw_/http://ntsc-tv.com/index.html
|
||||
// https://web.archive.org/web/20200831051302/https://www.hdretrovision.com/240p/
|
||||
|
||||
std::string cur_region = VMManager::Internal::GetCurrentRegion();
|
||||
|
||||
switch (gsVideoMode)
|
||||
{
|
||||
case GS_VideoMode::Uninitialized: // SetGsCrt hasn't executed yet, give some temporary values.
|
||||
return 60.00;
|
||||
return (cur_region == "PAL") ? Pcsx2Config::GSOptions::DEFAULT_FRAME_RATE_PAL : Pcsx2Config::GSOptions::DEFAULT_FRAME_RATE_NTSC;
|
||||
case GS_VideoMode::PAL:
|
||||
case GS_VideoMode::DVD_PAL:
|
||||
return (IsProgressiveVideoMode() == false) ? EmuConfig.GS.FrameratePAL : EmuConfig.GS.FrameratePAL - 0.24f;
|
||||
@@ -352,9 +354,9 @@ void UpdateVSyncRate(bool force)
|
||||
{
|
||||
case GS_VideoMode::Uninitialized: // SYSCALL instruction hasn't executed yet, give some temporary values.
|
||||
if (gsIsInterlaced)
|
||||
total_scanlines = SCANLINES_TOTAL_NTSC_I;
|
||||
total_scanlines = (vertical_frequency == Pcsx2Config::GSOptions::DEFAULT_FRAME_RATE_PAL) ? SCANLINES_TOTAL_PAL_I : SCANLINES_TOTAL_NTSC_I;
|
||||
else
|
||||
total_scanlines = SCANLINES_TOTAL_NTSC_NI;
|
||||
total_scanlines = (vertical_frequency == Pcsx2Config::GSOptions::DEFAULT_FRAME_RATE_PAL) ? SCANLINES_TOTAL_PAL_NI : SCANLINES_TOTAL_NTSC_NI;
|
||||
break;
|
||||
case GS_VideoMode::PAL:
|
||||
case GS_VideoMode::DVD_PAL:
|
||||
|
||||
@@ -174,7 +174,8 @@ The clamp modes are also numerically based.
|
||||
* cpuSpriteRenderBW [Value between `0` to `10`] {Disabled, 1 (64), 2 (128), 3 (192), 4 (256), 5 (320), 6 (384), 7 (448), 8 (512), 9 (576), 10 (640)} Default: Off (`0`)
|
||||
* cpuSpriteRenderLevel [`0` or `1` or `2`] {Sprites only, Sprites/Triangles, Blended Sprites/Triangles} Default: Off unless cpuSpriteRenderBW has value other than Off then it is 'Sprites only' (`0`)
|
||||
* estimateTextureRegion [`0` or `1`] {Off, On} Default: Off (`0`)
|
||||
* drawBuffering [`0` or `1`] {Off, On} Default: Off (`0`)
|
||||
* drawBuffering [`0` or `1`] {Off, On} Default: Off (`0`)
|
||||
* rewriteLargeST [`0` or `1`] {Off, On} Default: Off (`0`)
|
||||
* getSkipCount {`GSC` with suffix } {None unless specific game GSC} Default: Disabled (`0`) unless valid variable name (ex. GSC_PolyphonyDigitalGames, GSC_UrbanReign, ...)
|
||||
* gpuPaletteConversion [`0` or `1`] {Off, On} Default: Off (`0`)
|
||||
* gpuTargetCLUT [`0` or `1` or `2`] {Disabled, Enabled (Exact Match), Enabled (Check Inside Target)} Default: Disabled (`0`)
|
||||
|
||||
@@ -204,7 +204,12 @@
|
||||
"minimum": 0,
|
||||
"maximum": 1
|
||||
},
|
||||
"drawBuffering": {
|
||||
"drawBuffering": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 1
|
||||
},
|
||||
"rewriteLargeST": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 1
|
||||
|
||||
@@ -6838,6 +6838,159 @@ GIFRegTEX0 GSState::GetTex0Layer(u32 lod)
|
||||
return TEX0;
|
||||
}
|
||||
|
||||
// Fix large ST coorindates that may cause graphical glitches.
|
||||
template <u32 primclass>
|
||||
void GSState::RewriteVerticesIfLargeSTImpl(const GSVector4& large_val, bool check_clamp_mode)
|
||||
{
|
||||
// Need to be texture mapping with ST and not have so many vertices that rewritten indices will overflow.
|
||||
if (PRIM->TME && PRIM->FST == 0)
|
||||
{
|
||||
const float tw = static_cast<float>(1 << m_context->TEX0.TW);
|
||||
const float th = static_cast<float>(1 << m_context->TEX0.TH);
|
||||
const GSVector4 tsize = GSVector4(tw, th, 1.0f, 1.0f);
|
||||
|
||||
// Only rewrite big/small S or T when the clamping mode is CLAMP or REGION_CLAMP (if requested).
|
||||
const GIFRegCLAMP& clamp = m_context->CLAMP;
|
||||
const GSVector4 clamp_mask =
|
||||
check_clamp_mode ?
|
||||
GSVector4::cast(GSVector4i(
|
||||
(clamp.WMS == CLAMP_CLAMP || clamp.WMS == CLAMP_REGION_CLAMP) ? 0xFFFFFFFF : 0,
|
||||
(clamp.WMT == CLAMP_CLAMP || clamp.WMT == CLAMP_REGION_CLAMP) ? 0xFFFFFFFF : 0,
|
||||
0,
|
||||
0)) :
|
||||
GSVector4::cast(GSVector4i::cxpr(0xFFFFFFFF));
|
||||
|
||||
const GSVector4 tex_bbox = m_vt.m_min.t.xyxy(m_vt.m_max.t) / tsize.xyxy();
|
||||
const bool large_st = ((tex_bbox.abs() >= large_val) & clamp_mask).mask() || m_vt.nan.value;
|
||||
|
||||
if (large_st)
|
||||
{
|
||||
GL_PUSH("Large ST detected, rewriting vertices.");
|
||||
if (m_index->tail > UINT16_MAX)
|
||||
{
|
||||
GL_INS("Too many vertices to rewrite safely, exiting.");
|
||||
DevCon.Warning("Large ST detected but too many vertices to rewrite safely.");
|
||||
return;
|
||||
}
|
||||
|
||||
constexpr int n = GSUtil::GetClassVertexCount(primclass);
|
||||
|
||||
// Make sure the copy buffer is large enough.
|
||||
while (m_max_vertex_count < m_index->tail)
|
||||
GrowVertexBuffer();
|
||||
|
||||
GSVertex* RESTRICT vertex = m_vertex->buff;
|
||||
GSVertex* RESTRICT vertex_copy = m_vertex->buff_copy;
|
||||
u16* RESTRICT index = m_index->buff;
|
||||
const int count = static_cast<int>(m_index->tail);
|
||||
|
||||
for (int i = 0; i < count; i += n)
|
||||
{
|
||||
GSVector4 stcq[n];
|
||||
|
||||
// Load STQ for this primitive.
|
||||
for (int j = 0; j < n; j++)
|
||||
stcq[j] = GSVector4::cast(GSVector4i(vertex[index[i + j]].m[0]));
|
||||
|
||||
// Perform Q division and see which values need to be rewritten.
|
||||
GSVector4 uv[n];
|
||||
GSVector4 small{}, big{}, nan{};
|
||||
for (int j = 0; j < n; j++)
|
||||
{
|
||||
// For sprites always use Q of second vertex.
|
||||
const GSVector4 q = primclass == GS_SPRITE_CLASS ? stcq[1].wwww() : stcq[j].wwww();
|
||||
uv[j] = (stcq[j] / q).xyzw(GSVector4::zero());
|
||||
small |= (uv[j] <= -large_val);
|
||||
big |= (uv[j] >= large_val);
|
||||
nan |= (uv[j] != uv[j]);
|
||||
}
|
||||
|
||||
// Get the new values for fields that will be rewritten.
|
||||
// The follows rules are used:
|
||||
// 1. If there are small values but not big or nans, make all vertices small.
|
||||
// 2. If there are big values but not small or nans, make all vertices big.
|
||||
// 3. If there are both big and small values, or nans, make all vertices zero.
|
||||
GSVector4 uv_new = GSVector4::zero();
|
||||
uv_new = uv_new.blend32(-large_val, small);
|
||||
uv_new = uv_new.blend32(large_val, big);
|
||||
uv_new = uv_new.blend32(GSVector4::zero(), (small & big) | nan);
|
||||
|
||||
const GSVector4 rewrite = (((small | big) & clamp_mask) | nan).xyxy(GSVector4::zero());
|
||||
|
||||
// If both S and T are rewritten, no point in keeping Q. Just set it to 1.0f;
|
||||
if ((rewrite.mask() & 3) == 3)
|
||||
{
|
||||
for (int j = 0; j < n; j++)
|
||||
stcq[j] = stcq[j].template insert32<0, 3>(GSVector4::m_one);
|
||||
}
|
||||
|
||||
// Rewrite the fields that require it and write to the copy buffer.
|
||||
for (int j = 0; j < n; j++)
|
||||
{
|
||||
// For sprites always use Q of second vertex.
|
||||
const GSVector4 q = (primclass == GS_SPRITE_CLASS) ? stcq[1].wwww() : stcq[j].wwww();
|
||||
stcq[j] = stcq[j].blend32(uv_new * q, rewrite);
|
||||
|
||||
vertex_copy[i + j].m[0] = GSVector4i::cast(stcq[j]);
|
||||
vertex_copy[i + j].m[1] = vertex[index[i + j]].m[1];
|
||||
index[i + j] = i + j;
|
||||
}
|
||||
}
|
||||
|
||||
// Swap the buffers and fix the counts.
|
||||
std::swap(m_vertex->buff, m_vertex->buff_copy);
|
||||
m_vertex->head = m_vertex->next = m_vertex->tail = m_index->tail;
|
||||
|
||||
// Recalculate ST min/max/eq in the vertex trace.
|
||||
GSVector4 tmin = GSVector4::cxpr(FLT_MAX);
|
||||
GSVector4 tmax = GSVector4::cxpr(-FLT_MAX);
|
||||
for (int i = 0; i < count; i += n)
|
||||
{
|
||||
for (int j = 0; j < n; j++)
|
||||
{
|
||||
GSVector4 stcq = GSVector4::cast(GSVector4i(m_vertex->buff[i + j].m[0]));
|
||||
const float q = (primclass == GS_SPRITE_CLASS) ? m_vertex->buff[i + 1].RGBAQ.Q : stcq.w;
|
||||
stcq = (stcq / q).xyzw(stcq);
|
||||
|
||||
tmin = tmin.min(stcq);
|
||||
tmax = tmax.max(stcq);
|
||||
}
|
||||
}
|
||||
m_vt.m_min.t = tmin.xyww() * tsize;
|
||||
m_vt.m_max.t = tmax.xyww() * tsize;
|
||||
m_vt.m_eq.stq = (m_vt.m_min.t == m_vt.m_max.t).mask();
|
||||
|
||||
// Dump vertices if needed.
|
||||
if (GSConfig.ShouldDump(s_n, g_perfmon.GetFrame()) && GSConfig.SaveInfo)
|
||||
{
|
||||
DumpVertices(GetDrawDumpPath("%05d_vertices_st_rewrite.txt", s_n));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GSState::RewriteVerticesIfLargeST(const GSVector4& large_val, bool check_clamp_mode)
|
||||
{
|
||||
switch (m_vt.m_primclass)
|
||||
{
|
||||
case GS_POINT_CLASS:
|
||||
RewriteVerticesIfLargeSTImpl<GS_POINT_CLASS>(large_val, check_clamp_mode);
|
||||
break;
|
||||
case GS_LINE_CLASS:
|
||||
RewriteVerticesIfLargeSTImpl<GS_LINE_CLASS>(large_val, check_clamp_mode);
|
||||
break;
|
||||
case GS_SPRITE_CLASS:
|
||||
RewriteVerticesIfLargeSTImpl<GS_SPRITE_CLASS>(large_val, check_clamp_mode);
|
||||
break;
|
||||
case GS_TRIANGLE_CLASS:
|
||||
RewriteVerticesIfLargeSTImpl<GS_TRIANGLE_CLASS>(large_val, check_clamp_mode);
|
||||
break;
|
||||
default:
|
||||
pxFail("Invalid primclass"); // Impossible
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// GSTransferBuffer
|
||||
|
||||
GSState::GSTransferBuffer::GSTransferBuffer()
|
||||
|
||||
@@ -541,6 +541,9 @@ public:
|
||||
bool SpriteDrawWithoutGaps();
|
||||
void CalculatePrimitiveCoversWithoutGaps();
|
||||
GIFRegTEX0 GetTex0Layer(u32 lod);
|
||||
template <u32 primclass>
|
||||
void RewriteVerticesIfLargeSTImpl(const GSVector4& large_val, bool check_clamp_mode);
|
||||
void RewriteVerticesIfLargeST(const GSVector4& large_val, bool check_clamp_mode);
|
||||
};
|
||||
|
||||
// We put this in the header because of Multi-ISA.
|
||||
|
||||
@@ -49,6 +49,7 @@ const char* ShaderEntryPoint(ShaderConvert value)
|
||||
case ShaderConvert::RGB5A1_TO_DEPTH16: return "ps_convert_rgb5a1_depth16";
|
||||
case ShaderConvert::DEPTH32_TO_DEPTH24: return "ps_convert_depth32_depth24";
|
||||
case ShaderConvert::DEPTH_COPY: return "ps_depth_copy";
|
||||
case ShaderConvert::PRIMID_TO_RGBA8: return "ps_convert_primid_rgba8";
|
||||
case ShaderConvert::DOWNSAMPLE_COPY: return "ps_downsample_copy";
|
||||
case ShaderConvert::RGBA_TO_8I: return "ps_convert_rgba_8i";
|
||||
case ShaderConvert::RGB5A1_TO_8I: return "ps_convert_rgb5a1_8i";
|
||||
@@ -109,6 +110,7 @@ const char* ShaderConvertName(ShaderConvert shader)
|
||||
ENTRY(RGBA8_TO_DEPTH16);
|
||||
ENTRY(RGB5A1_TO_DEPTH16);
|
||||
ENTRY(DEPTH32_TO_DEPTH24);
|
||||
ENTRY(PRIMID_TO_RGBA8);
|
||||
ENTRY(DOWNSAMPLE_COPY);
|
||||
ENTRY(RGBA_TO_8I);
|
||||
ENTRY(RGB5A1_TO_8I);
|
||||
|
||||
@@ -59,6 +59,7 @@ enum class ShaderConvert
|
||||
RGBA8_TO_DEPTH16,
|
||||
RGB5A1_TO_DEPTH16,
|
||||
DEPTH32_TO_DEPTH24,
|
||||
PRIMID_TO_RGBA8,
|
||||
DOWNSAMPLE_COPY,
|
||||
RGBA_TO_8I,
|
||||
RGB5A1_TO_8I,
|
||||
@@ -122,6 +123,7 @@ static inline constexpr bool HasColorOutput(ShaderConvert shader)
|
||||
case ShaderConvert::DEPTH32_TO_RGBA8:
|
||||
case ShaderConvert::DEPTH32_TO_RGB8:
|
||||
case ShaderConvert::DEPTH16_TO_RGB5A1:
|
||||
case ShaderConvert::PRIMID_TO_RGBA8:
|
||||
case ShaderConvert::DOWNSAMPLE_COPY:
|
||||
case ShaderConvert::RGBA_TO_8I:
|
||||
case ShaderConvert::RGB5A1_TO_8I:
|
||||
@@ -162,6 +164,7 @@ static inline constexpr bool HasFloat32Input(ShaderConvert shader)
|
||||
case ShaderConvert::DEPTH32_TO_RGB8:
|
||||
case ShaderConvert::DEPTH16_TO_RGB5A1:
|
||||
case ShaderConvert::DEPTH32_TO_DEPTH24:
|
||||
case ShaderConvert::PRIMID_TO_RGBA8:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
@@ -533,6 +536,10 @@ static inline ShaderConvertSelector GetConvertShader(GSTexture::Format src, GSTe
|
||||
pxAssert(false);
|
||||
}
|
||||
break;
|
||||
case GSTexture::Format::PrimID:
|
||||
pxAssert(dst == GSTexture::Format::Color);
|
||||
shader = ShaderConvert::PRIMID_TO_RGBA8;
|
||||
break;
|
||||
default:
|
||||
pxAssert(false);
|
||||
break;
|
||||
|
||||
@@ -63,4 +63,15 @@ enum class PS_ROV_DEPTH : uint32_t
|
||||
READ_ONLY = 2,
|
||||
};
|
||||
|
||||
#if defined(__METAL_VERSION__)
|
||||
#define CONSTANT constant
|
||||
#else
|
||||
#define CONSTANT
|
||||
#endif
|
||||
|
||||
static constexpr CONSTANT int PRIMID_MAX = (1 << 24) - 1;
|
||||
static constexpr CONSTANT int PRIMID_MIN = -1;
|
||||
|
||||
#undef CONSTANT
|
||||
|
||||
} // namespace GSShader
|
||||
|
||||
@@ -44,7 +44,7 @@ bool GSTexture::Save(const std::string& fn)
|
||||
{
|
||||
// Depth textures need special treatment - we have a stencil component.
|
||||
// Just re-use the existing conversion shader instead.
|
||||
if (m_format == Format::DepthStencil || m_format == Format::DepthColor)
|
||||
if (m_format == Format::DepthStencil || m_format == Format::DepthColor || m_format == Format::PrimID)
|
||||
{
|
||||
GSTexture* temp = g_gs_device->CreateRenderTarget(GetWidth(), GetHeight(), Format::Color, false);
|
||||
if (!temp)
|
||||
|
||||
@@ -249,6 +249,8 @@ bool GSDevice11::Create(GSVSyncMode vsync_mode, bool allow_present_throttle)
|
||||
|
||||
ShaderMacro sm_ps;
|
||||
sm_ps.AddMacro("PIXEL_SHADER", 1);
|
||||
sm_ps.AddMacro("PRIMID_MAX", GSShader::PRIMID_MAX);
|
||||
sm_ps.AddMacro("PRIMID_MIN", GSShader::PRIMID_MIN);
|
||||
sm_ps.AddMacro("HAS_BILN", static_cast<int>(shader.Biln()));
|
||||
sm_ps.AddMacro("HAS_STENCIL_OUTPUT", static_cast<int>(shader.StencilOutput()));
|
||||
sm_ps.AddMacro("HAS_INTEGER_OUTPUT", static_cast<int>(shader.IntegerOutputBpp() != 0));
|
||||
@@ -582,6 +584,8 @@ bool GSDevice11::Create(GSVSyncMode vsync_mode, bool allow_present_throttle)
|
||||
const std::string entry_point_macro = WrapEntryPointMacro(entry_point);
|
||||
ShaderMacro sm_ps;
|
||||
sm_ps.AddMacro("PIXEL_SHADER", 1);
|
||||
sm_ps.AddMacro("PRIMID_MAX", GSShader::PRIMID_MAX);
|
||||
sm_ps.AddMacro("PRIMID_MIN", GSShader::PRIMID_MIN);
|
||||
sm_ps.AddMacro(entry_point_macro.c_str(), 1);
|
||||
m_date.primid_init_ps[i] = m_shader_cache.GetPixelShader(m_dev.get(), *convert_hlsl, sm_ps.GetPtr(), entry_point.c_str());
|
||||
if (!m_date.primid_init_ps[i])
|
||||
|
||||
@@ -2877,6 +2877,8 @@ bool GSDevice12::CompileConvertPipelines()
|
||||
|
||||
ShaderMacro sm;
|
||||
sm.AddMacro("PIXEL_SHADER", 1);
|
||||
sm.AddMacro("PRIMID_MAX", GSShader::PRIMID_MAX);
|
||||
sm.AddMacro("PRIMID_MIN", GSShader::PRIMID_MIN);
|
||||
sm.AddMacro("HAS_BILN", static_cast<int>(shader.Biln()));
|
||||
sm.AddMacro("HAS_STENCIL_OUTPUT", static_cast<int>(shader.StencilOutput()));
|
||||
sm.AddMacro("HAS_INTEGER_OUTPUT", static_cast<int>(shader.IntegerOutputBpp() != 0));
|
||||
@@ -2930,6 +2932,8 @@ bool GSDevice12::CompileConvertPipelines()
|
||||
|
||||
ShaderMacro sm;
|
||||
sm.AddMacro("PIXEL_SHADER", "1");
|
||||
sm.AddMacro("PRIMID_MAX", GSShader::PRIMID_MAX);
|
||||
sm.AddMacro("PRIMID_MIN", GSShader::PRIMID_MIN);
|
||||
sm.AddMacro(entry_point_macro.c_str(), "1");
|
||||
|
||||
ComPtr<ID3DBlob> ps(m_shader_cache.GetPixelShader(*source, sm.GetPtr(), entry_point.c_str()));
|
||||
|
||||
@@ -2901,6 +2901,15 @@ void GSRendererHW::Draw()
|
||||
|
||||
// First pass texture shuffle detection using context/vertices.
|
||||
DetectTextureShuffle();
|
||||
|
||||
// Ridge Racer V and Destruction Derby Arena have large ST coordinates in reflection map draws that cause
|
||||
// speckled artifacts on cars. Detect and rewrite such vertices here.
|
||||
if (GSConfig.UserHacks_RewriteLargeST)
|
||||
{
|
||||
// The threshold is chosen to be low enough to fix the artifacts in Ridge Racer V and Destruction Derby Arena
|
||||
// while not breaking anything.
|
||||
RewriteVerticesIfLargeST(GSVector4::cxpr(16.0f), false);
|
||||
}
|
||||
|
||||
// When the format is 24bit (Z or C), DATE ceases to function.
|
||||
// It was believed that in 24bit mode all pixels pass because alpha doesn't exist
|
||||
@@ -3526,13 +3535,14 @@ void GSRendererHW::Draw()
|
||||
const u32 color_mask = (m_vt.m_max.c > GSVector4i::zero()).mask();
|
||||
const bool texture_function_color = m_cached_ctx.TEX0.TFX == TFX_DECAL || (color_mask & 0xFFF) || (m_cached_ctx.TEX0.TFX > TFX_DECAL && (color_mask & 0xF000));
|
||||
const bool texture_function_alpha = m_cached_ctx.TEX0.TFX != TFX_MODULATE || (color_mask & 0xF000);
|
||||
const bool req_color = (is_possible_channel_shuffle && channel_shuffle_targets != ChannelFetch_ALPHA) || (!is_possible_channel_shuffle && ((texture_function_color && (!PRIM->ABE || GSLocalMemory::m_psm[m_cached_ctx.TEX0.PSM].bpp < 16 || (NeedsBlending() && IsUsingCsInBlend())) && (possible_shuffle || (m_cached_ctx.FRAME.FBMSK & (fm_mask & 0x00FFFFFF)) != (fm_mask & 0x00FFFFFF))) || need_aem_color));
|
||||
const bool alpha_used = (GSUtil::GetChannelMask(m_context->TEX0.PSM) == 0x8 || (m_context->TEX0.TCC && texture_function_alpha)) && ((NeedsBlending() && IsUsingAsInBlend()) || (m_cached_ctx.TEST.ATE && m_cached_ctx.TEST.ATST > ATST_ALWAYS) || (possible_shuffle || (m_cached_ctx.FRAME.FBMSK & (fm_mask & 0xFF000000)) != (fm_mask & 0xFF000000)));
|
||||
const bool req_alpha = (is_possible_channel_shuffle && channel_shuffle_targets == ChannelFetch_ALPHA) || (!is_possible_channel_shuffle && (GSUtil::GetChannelMask(m_context->TEX0.PSM) & 0x8) && alpha_used);
|
||||
const bool color_used = (is_possible_channel_shuffle && channel_shuffle_targets != ChannelFetch_ALPHA) || (!is_possible_channel_shuffle && ((texture_function_color && (!PRIM->ABE || (NeedsBlending() && IsUsingCsInBlend())) && ((m_cached_ctx.FRAME.FBMSK & (fm_mask & 0x00FFFFFF)) != (fm_mask & 0x00FFFFFF))) || need_aem_color));
|
||||
const bool alpha_used = (is_possible_channel_shuffle && channel_shuffle_targets == ChannelFetch_ALPHA) || (!is_possible_channel_shuffle && (m_context->TEX0.TCC && texture_function_alpha) && ((NeedsBlending() && IsUsingAsInBlend()) || (m_cached_ctx.TEST.ATE && m_cached_ctx.TEST.ATST > ATST_ALWAYS) || (m_cached_ctx.FRAME.FBMSK & (fm_mask & 0xFF000000)) != (fm_mask & 0xFF000000)));
|
||||
const bool req_color = (GSUtil::GetChannelMask(m_context->TEX0.PSM) & 0x7) && color_used;
|
||||
const bool req_alpha = (GSUtil::GetChannelMask(m_context->TEX0.PSM) == 0x8 && (color_used || alpha_used)) || ((GSUtil::GetChannelMask(m_context->TEX0.PSM) & 0x8) && alpha_used);
|
||||
|
||||
// TODO: Be able to send an alpha of 1.0 (blended with vertex alpha maybe?) so we can avoid sending the texture, since we don't always need it.
|
||||
// Example games: Evolution Snowboarding, Final Fantasy Dirge of Cerberus, Red Dead Revolver, Stuntman, Tony Hawk's Underground 2, Ultimate Spider-Man.
|
||||
if (!req_color && !alpha_used)
|
||||
if (!req_color && !req_alpha)
|
||||
{
|
||||
m_process_texture = false;
|
||||
possible_shuffle = false;
|
||||
@@ -7689,26 +7699,17 @@ void GSRendererHW::ConfigureROV(bool color_rov, bool depth_rov)
|
||||
if (color_rov)
|
||||
{
|
||||
// FbMask setup
|
||||
if (m_conf.colormask.wrgba != 0)
|
||||
if (m_conf.colormask.wrgba != 0 && m_conf.colormask.wrgba != 0xF)
|
||||
{
|
||||
if (!m_conf.ps.fbmask)
|
||||
m_conf.cb_ps.FbMask = GSVector4i::zero();
|
||||
const GSVector4i fbmask = GSVector4i(m_conf.colormask.wr ? 0 : 0xFF, m_conf.colormask.wg ? 0 : 0xFF,
|
||||
m_conf.colormask.wb ? 0 : 0xFF, m_conf.colormask.wa ? 0 : 0xFF);
|
||||
if (!m_conf.ps.fbmask)
|
||||
{
|
||||
// Don't enable FB mask emulation, just use the mask for ROV.
|
||||
m_conf.cb_ps.FbMask = fbmask;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_conf.cb_ps.FbMask |= fbmask;
|
||||
}
|
||||
m_conf.ps.fbmask = true;
|
||||
m_conf.cb_ps.FbMask |= fbmask;
|
||||
GL_INS("ROV: FbMask={R=%x, G=%x, B=%x, A=%x}",
|
||||
m_conf.cb_ps.FbMask.r, m_conf.cb_ps.FbMask.g, m_conf.cb_ps.FbMask.b, m_conf.cb_ps.FbMask.a);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_conf.ps.no_color = true;
|
||||
}
|
||||
|
||||
// Blend setup
|
||||
if (m_conf.IsBlending())
|
||||
|
||||
@@ -1735,8 +1735,8 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const bool is_color, const
|
||||
match = false;
|
||||
|
||||
// Situations where font has been uploaded to the alpha channel and they use a different buffer width, this can't currently be translated correctly.
|
||||
GSLocalMemory::psm_t req_psm = GSLocalMemory::m_psm[psm];
|
||||
if (!possible_shuffle && req_psm.trbpp <= 8 && req_psm.bpp == 32 && t->m_TEX0.TBW != bw && (bw * 64) != t->m_valid.z && r.w > req_psm.pgs.y)
|
||||
const GSLocalMemory::psm_t req_psm = GSLocalMemory::m_psm[psm];
|
||||
if (!possible_shuffle && req_psm.trbpp <= 8 && req_psm.bpp == 32 && t->m_TEX0.TBW != bw && static_cast<int>(bw * 64) != t->m_valid.z && r.w > req_psm.pgs.y)
|
||||
match = false;
|
||||
|
||||
// Different swizzle, different width, and dirty, so probably not what we want.
|
||||
|
||||
@@ -112,22 +112,22 @@ fragment void ps_datm0(float4 p [[position]], DirectReadTextureIn<float> tex)
|
||||
|
||||
fragment float4 ps_primid_init_datm1(float4 p [[position]], DirectReadTextureIn<float> tex)
|
||||
{
|
||||
return tex.read(p).a < (127.5f / 255.f) ? -1 : FLT_MAX;
|
||||
return tex.read(p).a < (127.5f / 255.f) ? GSShader::PRIMID_MIN : GSShader::PRIMID_MAX;
|
||||
}
|
||||
|
||||
fragment float4 ps_primid_init_datm0(float4 p [[position]], DirectReadTextureIn<float> tex)
|
||||
{
|
||||
return tex.read(p).a > (127.5f / 255.f) ? -1 : FLT_MAX;
|
||||
return tex.read(p).a > (127.5f / 255.f) ? GSShader::PRIMID_MIN : GSShader::PRIMID_MAX;
|
||||
}
|
||||
|
||||
fragment float4 ps_primid_rta_init_datm1(float4 p [[position]], DirectReadTextureIn<float> tex)
|
||||
{
|
||||
return tex.read(p).a < (254.5f / 255.f) ? -1 : FLT_MAX;
|
||||
return tex.read(p).a < (254.5f / 255.f) ? GSShader::PRIMID_MIN : GSShader::PRIMID_MAX;
|
||||
}
|
||||
|
||||
fragment float4 ps_primid_rta_init_datm0(float4 p [[position]], DirectReadTextureIn<float> tex)
|
||||
{
|
||||
return tex.read(p).a > (254.5f / 255.f) ? -1 : FLT_MAX;
|
||||
return tex.read(p).a > (254.5f / 255.f) ? GSShader::PRIMID_MIN : GSShader::PRIMID_MAX;
|
||||
}
|
||||
|
||||
fragment float4 ps_rta_correction(ConvertShaderData data [[stage_in]], ConvertPSRes res)
|
||||
@@ -175,11 +175,6 @@ fragment float4 ps_convert_depth16_rgb5a1(ConvertShaderData data [[stage_in]], C
|
||||
return convert_depth16_rgba8(res.sample(data.t)) / 255.f;
|
||||
}
|
||||
|
||||
fragment float ps_convert_float32_depth_to_color(ConvertShaderData data [[stage_in]], ConvertPSDepthRes res)
|
||||
{
|
||||
return res.sample(data.t);
|
||||
}
|
||||
|
||||
fragment float4 ps_downsample_copy(ConvertShaderData data [[stage_in]],
|
||||
texture2d<float> texture [[texture(GSMTLTextureIndexNonHW)]],
|
||||
constant GSMTLDownsamplePSUniform& uniform [[buffer(GSMTLBufferIndexUniforms)]])
|
||||
@@ -273,6 +268,16 @@ struct ConvertToDepthRes
|
||||
}
|
||||
};
|
||||
|
||||
static float4 primid_to_rgba8(float p)
|
||||
{
|
||||
return float4(as_type<uchar4>(p)) / 255.f;
|
||||
}
|
||||
|
||||
fragment float4 ps_convert_primid_rgba8(ConvertShaderData data [[stage_in]], ConvertPSDepthOrColorRes res)
|
||||
{
|
||||
return primid_to_rgba8(res.sample(data.t));
|
||||
}
|
||||
|
||||
fragment DepthOrColorOut ps_depth_copy(ConvertShaderData data [[stage_in]], ConvertPSDepthOrColorRes res)
|
||||
{
|
||||
return res.sample(data.t);
|
||||
|
||||
@@ -1304,8 +1304,12 @@ struct PSMain
|
||||
{
|
||||
if (PS_FBMASK)
|
||||
{
|
||||
float multi = PS_COLCLIP_HW ? 65535.0 : 255.5;
|
||||
C = float4((uint4(int4(C)) & (cb.fbmask ^ 0xff)) | (uint4(current_color * float4(multi, multi, multi, 255)) & cb.fbmask));
|
||||
float multi_rgb = PS_COLCLIP_HW ? 65535.0 : 255.5;
|
||||
float multi_a = PS_RTA_CORRECTION ? 128.0 : 255.0;
|
||||
float4 RT = current_color;
|
||||
RT.rgb = RT.rgb * multi_rgb;
|
||||
RT.a = round(RT.a * multi_a);
|
||||
C = float4((uint4(int4(C)) & (cb.fbmask ^ 0xff)) | (uint4(RT) & cb.fbmask));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1769,7 +1773,7 @@ fragment MainPSOut ps_main(
|
||||
main.current_depth = ds_tex.read(coord).x;
|
||||
}
|
||||
|
||||
if (NEEDS_RT || (PS_ROV_COLOR && any(cb.fbmask == 0xff)))
|
||||
if (NEEDS_RT)
|
||||
{
|
||||
if (PS_ROV_COLOR)
|
||||
{
|
||||
@@ -1797,8 +1801,6 @@ fragment MainPSOut ps_main(
|
||||
ds_rov.write(out.depth, coord);
|
||||
if (PS_ROV_COLOR && !main.color_discarded)
|
||||
{
|
||||
if (!PS_FBMASK)
|
||||
out.c0 = select(out.c0, main.current_color, cb.fbmask == 0xff);
|
||||
if (ROV_NEEDS_R32)
|
||||
rt_u32.write(pack_float_to_unorm4x8(out.c0), coord);
|
||||
else
|
||||
|
||||
@@ -432,6 +432,8 @@ bool GSDeviceOGL::Create(GSVSyncMode vsync_mode, bool allow_present_throttle)
|
||||
const char* name = shader.EntryPoint();
|
||||
|
||||
std::string macro;
|
||||
macro += fmt::format("#define PRIMID_MAX {}\n", GSShader::PRIMID_MAX);
|
||||
macro += fmt::format("#define PRIMID_MIN {}\n", GSShader::PRIMID_MIN);
|
||||
macro += fmt::format("#define HAS_BILN {}\n", static_cast<int>(shader.Biln()));
|
||||
macro += fmt::format("#define HAS_STENCIL_OUTPUT {}\n", static_cast<int>(shader.StencilOutput()));
|
||||
macro += fmt::format("#define HAS_INTEGER_OUTPUT {}\n", static_cast<int>(shader.IntegerOutputBpp() != 0));
|
||||
@@ -609,9 +611,13 @@ bool GSDeviceOGL::Create(GSVSyncMode vsync_mode, bool allow_present_throttle)
|
||||
|
||||
for (size_t i = 0; i < std::size(m_date.primid_ps); i++)
|
||||
{
|
||||
std::string macro;
|
||||
macro += fmt::format("#define PRIMID_MAX {}\n", GSShader::PRIMID_MAX);
|
||||
macro += fmt::format("#define PRIMID_MIN {}\n", GSShader::PRIMID_MIN);
|
||||
|
||||
const std::string ps(GetShaderSource(
|
||||
fmt::format("ps_primid_image_init_{}", i),
|
||||
GL_FRAGMENT_SHADER, *convert_glsl));
|
||||
GL_FRAGMENT_SHADER, *convert_glsl, macro));
|
||||
m_shader_cache.GetProgram(&m_date.primid_ps[i], m_convert.vs, ps);
|
||||
m_date.primid_ps[i].SetFormattedName("PrimID Destination Alpha Init %d", i);
|
||||
}
|
||||
|
||||
@@ -287,125 +287,6 @@ void ConvertVertexBuffer(const GSDrawingContext* RESTRICT ctx, GSVertexSW* RESTR
|
||||
}
|
||||
}
|
||||
|
||||
// Fix ST coordinates that would overflow the rasterizer fixed point format by rewriting the vertices.
|
||||
template <u32 primclass>
|
||||
void GSRendererSW::RewriteVerticesIfSTOverflow()
|
||||
{
|
||||
if (PRIM->TME && PRIM->FST == 0)
|
||||
{
|
||||
const GSVector4 tsize = GSVector4(
|
||||
static_cast<float>(1 << m_context->TEX0.TW),
|
||||
static_cast<float>(1 << m_context->TEX0.TH),
|
||||
1.0f,
|
||||
1.0f);
|
||||
|
||||
// SW rasterizer stores UV in 1.15.16 format so clamp to +/- (2^15 - 2) (-2 so bilinear doesn't overflow).
|
||||
// Do the division by texture size here to avoid divisions for each vertex.
|
||||
const GSVector4 OVERFLOW_VAL = GSVector4::cxpr(static_cast<float>((1 << 15) - 2)) / tsize;
|
||||
|
||||
// Only rewrite big/small S or T when the clamping mode is CLAMP or REGION_CLAMP.
|
||||
const GSVector4i clamp_mode = GSVector4i(
|
||||
(m_context->CLAMP.WMS == CLAMP_CLAMP || m_context->CLAMP.WMS == CLAMP_REGION_CLAMP) ? 0xFFFFFFFF : 0,
|
||||
(m_context->CLAMP.WMT == CLAMP_CLAMP || m_context->CLAMP.WMT == CLAMP_REGION_CLAMP) ? 0xFFFFFFFF : 0,
|
||||
0,
|
||||
0);
|
||||
|
||||
const bool st_overflow =
|
||||
((GSVector4i::cast(m_vt.m_min.t <= -OVERFLOW_VAL * tsize) & clamp_mode).mask() & 3) ||
|
||||
((GSVector4i::cast(m_vt.m_max.t >= OVERFLOW_VAL * tsize) & clamp_mode).mask() & 3) ||
|
||||
m_vt.nan.value;
|
||||
|
||||
if (st_overflow)
|
||||
{
|
||||
constexpr int n = GSUtil::GetClassVertexCount(primclass);
|
||||
|
||||
// Make sure the copy buffer is large enough.
|
||||
while (m_max_vertex_count < m_index->tail)
|
||||
GrowVertexBuffer();
|
||||
|
||||
GSVertex* RESTRICT vertex = m_vertex->buff;
|
||||
GSVertex* RESTRICT vertex_copy = m_vertex->buff_copy;
|
||||
u16* RESTRICT index = m_index->buff;
|
||||
|
||||
for (int i = 0; i < static_cast<int>(m_index->tail); i += n)
|
||||
{
|
||||
GSVector4 stcq[n];
|
||||
|
||||
// Load STQ for this primitive.
|
||||
for (int j = 0; j < n; j++)
|
||||
stcq[j] = GSVector4::cast(GSVector4i(vertex[index[i + j]].m[0]));
|
||||
|
||||
// Perform Q division and see which values need to be rewritten.
|
||||
GSVector4 uv[n];
|
||||
GSVector4i small{}, big{}, nan{};
|
||||
for (int j = 0; j < n; j++)
|
||||
{
|
||||
// For sprites always use Q of second vertex.
|
||||
const GSVector4 q = primclass == GS_SPRITE_CLASS ? stcq[1].wwww() : stcq[j].wwww();
|
||||
uv[j] = (stcq[j] / q).xyzw(GSVector4::zero());
|
||||
small |= GSVector4i::cast(uv[j] <= -OVERFLOW_VAL);
|
||||
big |= GSVector4i::cast(uv[j] >= OVERFLOW_VAL);
|
||||
nan |= GSVector4i::cast(uv[j] != uv[j]);
|
||||
}
|
||||
|
||||
// Get the new values for fields that will be rewritten.
|
||||
// The follows rules are used:
|
||||
// 1. If there are small values but not big or nans, make all vertices small.
|
||||
// 2. If there are big values but not small or nans, make all vertices big.
|
||||
// 3. If there are both big and small values, or nans, make all vertices zero.
|
||||
GSVector4 uv_new = GSVector4::zero();
|
||||
uv_new = uv_new.blend32(-OVERFLOW_VAL, GSVector4::cast(small));
|
||||
uv_new = uv_new.blend32(OVERFLOW_VAL, GSVector4::cast(big));
|
||||
uv_new = uv_new.blend32(GSVector4::zero(), GSVector4::cast((small & big) | nan));
|
||||
|
||||
const GSVector4i rewrite = (((small | big) & clamp_mode) | nan).upl64(GSVector4i::zero());
|
||||
|
||||
// If both S and T are rewritten, no point in keeping Q. Just set it to 1.0f;
|
||||
if ((GSVector4::cast(rewrite).mask() & 3) == 3)
|
||||
{
|
||||
for (int j = 0; j < n; j++)
|
||||
stcq[j] = stcq[j].template insert32<0, 3>(GSVector4::m_one);
|
||||
}
|
||||
|
||||
// Rewrite the fields that require it and write to the copy buffer.
|
||||
for (int j = 0; j < n; j++)
|
||||
{
|
||||
// For sprites always use Q of second vertex.
|
||||
const GSVector4 q = (primclass == GS_SPRITE_CLASS) ? stcq[1].wwww() : stcq[j].wwww();
|
||||
stcq[j] = stcq[j].blend32(uv_new * q, GSVector4::cast(rewrite));
|
||||
|
||||
vertex_copy[i + j].m[0] = GSVector4i::cast(stcq[j]);
|
||||
vertex_copy[i + j].m[1] = vertex[index[i + j]].m[1];
|
||||
index[i + j] = i + j;
|
||||
}
|
||||
}
|
||||
|
||||
// Swap the buffers and fix the counts.
|
||||
std::swap(m_vertex->buff, m_vertex->buff_copy);
|
||||
m_vertex->head = m_vertex->next = m_vertex->tail = m_index->tail;
|
||||
|
||||
// Recalculate ST min/max/eq in the vertex trace.
|
||||
GSVector4 tmin = GSVector4::cxpr(FLT_MAX);
|
||||
GSVector4 tmax = GSVector4::cxpr(-FLT_MAX);
|
||||
for (int i = 0; i < static_cast<int>(m_index->tail); i += n)
|
||||
{
|
||||
for (int j = 0; j < n; j++)
|
||||
{
|
||||
GSVector4 stcq = GSVector4::cast(GSVector4i(m_vertex->buff[i + j].m[0]));
|
||||
const float Q = (primclass == GS_SPRITE_CLASS) ? stcq.w : m_vertex->buff[i + 1].RGBAQ.Q;
|
||||
stcq = (stcq / Q).xyzw(stcq);
|
||||
|
||||
tmin = tmin.min(stcq);
|
||||
tmax = tmax.max(stcq);
|
||||
}
|
||||
}
|
||||
m_vt.m_min.t = tmin.xyww() * tsize;
|
||||
m_vt.m_max.t = tmax.xyww() * tsize;
|
||||
m_vt.m_eq.stq = (m_vt.m_min.t == m_vt.m_max.t).mask();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GSVertexSWInitStatic()
|
||||
{
|
||||
#define InitCVB4(P, T, F, Q) GSVertexSW::s_cvb[P][T][F][Q] = ConvertVertexBuffer<P, T, F, Q>;
|
||||
@@ -427,24 +308,25 @@ MULTI_ISA_UNSHARED_END
|
||||
void GSRendererSW::Draw()
|
||||
{
|
||||
const GSDrawingContext* context = m_context;
|
||||
|
||||
switch (m_vt.m_primclass)
|
||||
if (!GSConfig.UserHacks_RewriteLargeST)
|
||||
{
|
||||
case GS_POINT_CLASS:
|
||||
RewriteVerticesIfSTOverflow<GS_POINT_CLASS>();
|
||||
break;
|
||||
case GS_LINE_CLASS:
|
||||
RewriteVerticesIfSTOverflow<GS_LINE_CLASS>();
|
||||
break;
|
||||
case GS_TRIANGLE_CLASS:
|
||||
RewriteVerticesIfSTOverflow<GS_TRIANGLE_CLASS>();
|
||||
break;
|
||||
case GS_SPRITE_CLASS:
|
||||
RewriteVerticesIfSTOverflow<GS_SPRITE_CLASS>();
|
||||
break;
|
||||
default:
|
||||
pxFailRel("Unknown primitive class.");
|
||||
break;
|
||||
// SW rasterizer stores UV in 1.15.16 format so clamp to +/- (2^15 - 2) (-2 so bilinear doesn't overflow).
|
||||
// Also only do this for CLAMP and CLAMP_REGION modes to reduce the performance impact.
|
||||
const GSVector4 tsize = GSVector4(
|
||||
static_cast<float>(1 << m_context->TEX0.TW),
|
||||
static_cast<float>(1 << m_context->TEX0.TH),
|
||||
1.0f,
|
||||
1.0f);
|
||||
|
||||
// Large value is given in terms of normalized ST / Q coordinates.
|
||||
const GSVector4 large_val = GSVector4::cxpr(static_cast<float>((1 << 15) - 2)) / tsize;
|
||||
RewriteVerticesIfLargeST(large_val, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
// More sensitive as the rewrite threshold is lower and we do it for all clamp mode.
|
||||
// The performance cost will also be higher.
|
||||
RewriteVerticesIfLargeST(GSVector4::cxpr(16.0f), false);
|
||||
}
|
||||
|
||||
auto data = m_vertex_heap.make_shared<SharedData>().cast<GSRasterizerData>();
|
||||
|
||||
@@ -4200,6 +4200,8 @@ bool GSDeviceVK::CompileConvertPipelines()
|
||||
gpb.SetColorWriteMask(0, shader.Mask());
|
||||
|
||||
std::string macro;
|
||||
macro += fmt::format("#define PRIMID_MAX {}\n", GSShader::PRIMID_MAX);
|
||||
macro += fmt::format("#define PRIMID_MIN {}\n", GSShader::PRIMID_MIN);
|
||||
macro += fmt::format("#define HAS_BILN {}\n", static_cast<int>(shader.Biln()));
|
||||
macro += fmt::format("#define HAS_STENCIL_OUTPUT {}\n", static_cast<int>(shader.StencilOutput()));
|
||||
macro += fmt::format("#define HAS_INTEGER_OUTPUT {}\n", static_cast<int>(shader.IntegerOutputBpp() != 0));
|
||||
@@ -4267,9 +4269,14 @@ bool GSDeviceVK::CompileConvertPipelines()
|
||||
|
||||
for (u32 datm = 0; datm < 4; datm++)
|
||||
{
|
||||
std::string macro;
|
||||
macro += fmt::format("#define PRIMID_MAX {}\n", GSShader::PRIMID_MAX);
|
||||
macro += fmt::format("#define PRIMID_MIN {}\n", GSShader::PRIMID_MIN);
|
||||
|
||||
const std::string source_with_header = macro + *source;
|
||||
|
||||
const std::string entry_point(StringUtil::StdStringFromFormat("ps_primid_image_init_%d", datm));
|
||||
VkShaderModule ps =
|
||||
GetUtilityFragmentShader(*source, entry_point.c_str());
|
||||
VkShaderModule ps = GetUtilityFragmentShader(source_with_header, entry_point.c_str());
|
||||
if (ps == VK_NULL_HANDLE)
|
||||
return false;
|
||||
|
||||
|
||||
@@ -104,9 +104,9 @@ std::unique_ptr<GSTextureVK> GSTextureVK::Create(Usage usage, Format format, int
|
||||
|
||||
if (format == Format::UNorm8)
|
||||
{
|
||||
// for r8 textures, swizzle it across all 4 components. the shaders depend on it being in alpha.. why?
|
||||
// UNorm8 wants value in alpha, not red.
|
||||
static constexpr const VkComponentMapping r8_swizzle = {
|
||||
VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_R };
|
||||
VK_COMPONENT_SWIZZLE_ZERO, VK_COMPONENT_SWIZZLE_ZERO, VK_COMPONENT_SWIZZLE_ZERO, VK_COMPONENT_SWIZZLE_R };
|
||||
vci.components = r8_swizzle;
|
||||
}
|
||||
|
||||
|
||||
@@ -376,6 +376,7 @@ static const char* s_gs_hw_fix_names[] = {
|
||||
"nativePaletteDraw",
|
||||
"estimateTextureRegion",
|
||||
"drawBuffering",
|
||||
"rewriteLargeST",
|
||||
"PCRTCOffsets",
|
||||
"PCRTCOverscan",
|
||||
"trilinearFiltering",
|
||||
@@ -622,6 +623,9 @@ bool GameDatabaseSchema::GameEntry::configMatchesHWFix(const Pcsx2Config::GSOpti
|
||||
|
||||
case GSHWFixId::DrawBuffering:
|
||||
return (static_cast<int>(config.UserHacks_DrawBuffering) == value);
|
||||
|
||||
case GSHWFixId::RewriteLargeST:
|
||||
return (static_cast<int>(config.UserHacks_RewriteLargeST) == value);
|
||||
|
||||
case GSHWFixId::PCRTCOffsets:
|
||||
return (static_cast<int>(config.PCRTCOffsets) == value);
|
||||
@@ -793,6 +797,10 @@ void GameDatabaseSchema::GameEntry::applyGSHardwareFixes(Pcsx2Config::GSOptions&
|
||||
config.UserHacks_DrawBuffering = (value > 0);
|
||||
break;
|
||||
|
||||
case GSHWFixId::RewriteLargeST:
|
||||
config.UserHacks_RewriteLargeST = (value > 0);
|
||||
break;
|
||||
|
||||
case GSHWFixId::PCRTCOffsets:
|
||||
config.PCRTCOffsets = (value > 0);
|
||||
break;
|
||||
|
||||
@@ -61,6 +61,7 @@ namespace GameDatabaseSchema
|
||||
NativePaletteDraw,
|
||||
EstimateTextureRegion,
|
||||
DrawBuffering,
|
||||
RewriteLargeST,
|
||||
PCRTCOffsets,
|
||||
PCRTCOverscan,
|
||||
|
||||
|
||||
+1
-1
@@ -267,7 +267,7 @@ bool GameList::GetIsoSerialAndCRC(const std::string& path, s32* disc_type, std::
|
||||
|
||||
// TODO: we could include the version in the game list?
|
||||
*disc_type = DoCDVDdetectDiskType();
|
||||
cdvdGetDiscInfo(serial, nullptr, nullptr, crc, nullptr);
|
||||
cdvdGetDiscInfo(serial, nullptr, nullptr, nullptr, crc, nullptr);
|
||||
DoCDVDclose();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -3236,6 +3236,9 @@ void FullscreenUI::DrawGraphicsSettingsPage(SettingsInterface* bsi, bool show_ad
|
||||
DrawToggleSetting(bsi, FSUI_ICONSTR(ICON_FA_CROP, "Estimate Texture Region"),
|
||||
FSUI_CSTR("Attempts to reduce the texture size when games do not set it themselves (e.g. Snowblind games)."), "EmuCore/GS",
|
||||
"UserHacks_EstimateTextureRegion", false, manual_hw_fixes);
|
||||
DrawToggleSetting(bsi, FSUI_CSTR("Rewrite Large ST"),
|
||||
FSUI_CSTR("Rewrite large ST coordinates and clamp the values."), "EmuCore/GS",
|
||||
"UserHacks_RewriteLargeST", false, manual_hw_fixes);
|
||||
DrawToggleSetting(bsi, FSUI_ICONSTR(ICON_FA_PALETTE, "GPU Palette Conversion"),
|
||||
FSUI_CSTR("When enabled GPU converts colormap-textures, otherwise the CPU will. It is a trade-off between GPU and CPU."),
|
||||
"EmuCore/GS", "paltex", false, manual_hw_fixes);
|
||||
@@ -6014,6 +6017,8 @@ TRANSLATE_NOOP("FullscreenUI", "Removes texture cache entries when there is any
|
||||
TRANSLATE_NOOP("FullscreenUI", "Allows the texture cache to reuse as an input texture the inner portion of a previous framebuffer.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Flushes all targets in the texture cache back to local memory when shutting down.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Attempts to reduce the texture size when games do not set it themselves (e.g. Snowblind games).");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Rewrite Large ST");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Rewrite large ST coordinates and clamp the values.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "When enabled GPU converts colormap-textures, otherwise the CPU will. It is a trade-off between GPU and CPU.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Attempts to reduce draw calls in games which do heavy context switching for blending purposes.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Truncate 32-bit depth values to 24 bits. Helps games struggling with Z-fighting.");
|
||||
|
||||
@@ -1021,6 +1021,8 @@ __ri void ImGuiManager::DrawSettingsOverlay(float scale, float margin, float spa
|
||||
APPEND("ETR ");
|
||||
if (GSConfig.UserHacks_DrawBuffering)
|
||||
APPEND("DRWB ");
|
||||
if (GSConfig.UserHacks_RewriteLargeST)
|
||||
APPEND("RWST ");
|
||||
if (GSConfig.HWSpinGPUForReadbacks)
|
||||
APPEND("RBSG ");
|
||||
if (GSConfig.HWSpinCPUForReadbacks)
|
||||
|
||||
@@ -1005,6 +1005,7 @@ void Pcsx2Config::GSOptions::LoadSave(SettingsWrapper& wrap)
|
||||
SettingsWrapIntEnumEx(UserHacks_Limit24BitDepth, "UserHacks_Limit24BitDepth");
|
||||
SettingsWrapBitBoolEx(UserHacks_EstimateTextureRegion, "UserHacks_EstimateTextureRegion");
|
||||
SettingsWrapBitBoolEx(UserHacks_DrawBuffering, "UserHacks_DrawBuffering");
|
||||
SettingsWrapBitBoolEx(UserHacks_RewriteLargeST, "UserHacks_RewriteLargeST");
|
||||
SettingsWrapBitBoolEx(FXAA, "fxaa");
|
||||
SettingsWrapBitBool(ShadeBoost);
|
||||
SettingsWrapBitBoolEx(DumpGSData, "DumpGSData");
|
||||
@@ -1145,6 +1146,7 @@ void Pcsx2Config::GSOptions::MaskUserHacks()
|
||||
UserHacks_Limit24BitDepth = GSLimit24BitDepth::Disabled;
|
||||
UserHacks_EstimateTextureRegion = false;
|
||||
UserHacks_DrawBuffering = false;
|
||||
UserHacks_RewriteLargeST = false;
|
||||
UserHacks_TCOffsetX = 0;
|
||||
UserHacks_TCOffsetY = 0;
|
||||
UserHacks_CPUSpriteRenderBW = 0;
|
||||
|
||||
+1
-1
@@ -679,7 +679,7 @@ void eeloadHook()
|
||||
{
|
||||
CDVDDiscType disc_type;
|
||||
std::string disc_elf;
|
||||
cdvdGetDiscInfo(nullptr, &disc_elf, nullptr, nullptr, &disc_type);
|
||||
cdvdGetDiscInfo(nullptr, &disc_elf, nullptr, nullptr, nullptr, &disc_type);
|
||||
if (disc_type == CDVDDiscType::PS2Disc)
|
||||
{
|
||||
// only allow fast boot for PS2 games
|
||||
|
||||
@@ -23,8 +23,6 @@ bool SaveStateBase::InputRecordingFreeze()
|
||||
|
||||
#include "common/FileSystem.h"
|
||||
#include "common/StringUtil.h"
|
||||
#include "Counters.h"
|
||||
#include "SaveState.h"
|
||||
#include "VMManager.h"
|
||||
#include "Host.h"
|
||||
#include "ImGui/ImGuiOverlays.h"
|
||||
@@ -32,7 +30,7 @@ bool SaveStateBase::InputRecordingFreeze()
|
||||
#include "GameDatabase.h"
|
||||
#include "fmt/format.h"
|
||||
#include "GS.h"
|
||||
#include "Host.h"
|
||||
#include "SIO/Pad/Pad.h"
|
||||
|
||||
InputRecording g_InputRecording;
|
||||
|
||||
@@ -139,6 +137,7 @@ void InputRecording::closeActiveFile()
|
||||
if (m_file.close())
|
||||
{
|
||||
m_is_active = false;
|
||||
Pad::ResetAllControllerInputs();
|
||||
InputRec::log(TRANSLATE_STR("InputRecording", "Input recording stopped"), Host::OSD_ERROR_DURATION);
|
||||
MTGS::PresentCurrentFrame();
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "InputRecording.h"
|
||||
#include "InputRecordingControls.h"
|
||||
#include "Utilities/InputRecordingLogger.h"
|
||||
#include "SIO/Pad/Pad.h"
|
||||
|
||||
#include "Host.h"
|
||||
#include "MTGS.h"
|
||||
@@ -29,6 +30,7 @@ void InputRecordingControls::setRecordMode(bool waitForFrameToEnd)
|
||||
if (!waitForFrameToEnd || VMManager::GetState() == VMState::Paused)
|
||||
{
|
||||
m_state = Mode::Recording;
|
||||
Pad::ResetAllControllerInputs();
|
||||
InputRec::log(TRANSLATE("InputRecordingControls","Record Mode Enabled"), Host::OSD_INFO_DURATION);
|
||||
MTGS::PresentCurrentFrame();
|
||||
}
|
||||
@@ -36,6 +38,7 @@ void InputRecordingControls::setRecordMode(bool waitForFrameToEnd)
|
||||
{
|
||||
m_controlQueue.push([&]() {
|
||||
m_state = Mode::Recording;
|
||||
Pad::ResetAllControllerInputs();
|
||||
InputRec::log(TRANSLATE("InputRecordingControls","Record Mode Enabled"), Host::OSD_INFO_DURATION);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -554,6 +554,32 @@ void Pad::SetControllerState(u32 controller, u32 bind, float value)
|
||||
s_controllers[controller]->Set(bind, value);
|
||||
}
|
||||
|
||||
void Pad::ResetControllerInputs(u32 controller)
|
||||
{
|
||||
if (controller >= NUM_CONTROLLER_PORTS || !HasConnectedPad(controller))
|
||||
return;
|
||||
|
||||
for (InputBindingInfo binding : s_controllers[controller]->GetInfo().bindings)
|
||||
{
|
||||
switch (binding.bind_type)
|
||||
{
|
||||
case InputBindingInfo::Type::Button:
|
||||
case InputBindingInfo::Type::Axis:
|
||||
case InputBindingInfo::Type::HalfAxis:
|
||||
s_controllers[controller]->Set(binding.bind_index, 0);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Pad::ResetAllControllerInputs()
|
||||
{
|
||||
for (u32 port = 0; port < NUM_CONTROLLER_PORTS; port++)
|
||||
ResetControllerInputs(port);
|
||||
}
|
||||
|
||||
bool Pad::Freeze(StateWrapper& sw)
|
||||
{
|
||||
if (sw.IsReading())
|
||||
|
||||
@@ -66,6 +66,10 @@ namespace Pad
|
||||
// Sets the specified bind on a controller to the specified pressure (normalized to 0..1).
|
||||
void SetControllerState(u32 controller, u32 bind, float value);
|
||||
|
||||
// Resets all input binds on a controller to their default state.
|
||||
void ResetControllerInputs(u32 controller);
|
||||
void ResetAllControllerInputs();
|
||||
|
||||
bool Freeze(StateWrapper& sw);
|
||||
|
||||
// Sets the state of the specified macro button.
|
||||
|
||||
@@ -3,4 +3,4 @@
|
||||
|
||||
/// Version number for GS and other shaders. Increment whenever any of the contents of the
|
||||
/// shaders change, to invalidate the cache.
|
||||
static constexpr u32 SHADER_CACHE_VERSION = 110; // Last changed in PR 14897
|
||||
static constexpr u32 SHADER_CACHE_VERSION = 113; // Last changed in PR 14743
|
||||
|
||||
+15
-1
@@ -171,6 +171,7 @@ static std::string s_disc_version;
|
||||
static std::string s_title;
|
||||
static std::string s_title_en_search;
|
||||
static std::string s_title_en_replace;
|
||||
static std::string s_cur_region;
|
||||
static u32 s_disc_crc;
|
||||
static u32 s_current_crc;
|
||||
static u32 s_elf_entry_point = 0xFFFFFFFFu;
|
||||
@@ -1051,11 +1052,12 @@ void VMManager::UpdateDiscDetails(bool booting)
|
||||
s_disc_crc = GSDumpReplayer::GetDumpCRC();
|
||||
s_disc_elf = {};
|
||||
s_disc_version = {};
|
||||
s_cur_region = "NTSC";
|
||||
serial_is_valid = !s_disc_serial.empty();
|
||||
}
|
||||
else if (CDVDsys_GetSourceType() != CDVD_SourceType::NoDisc)
|
||||
{
|
||||
cdvdGetDiscInfo(&s_disc_serial, &s_disc_elf, &s_disc_version, &s_disc_crc, nullptr);
|
||||
cdvdGetDiscInfo(&s_disc_serial, &s_disc_elf, &s_disc_version, &s_cur_region, &s_disc_crc, nullptr);
|
||||
serial_is_valid = !s_disc_serial.empty();
|
||||
}
|
||||
else if (!s_elf_override.empty())
|
||||
@@ -1063,12 +1065,14 @@ void VMManager::UpdateDiscDetails(bool booting)
|
||||
s_disc_serial = Path::GetFileTitle(s_elf_override);
|
||||
s_disc_version = {};
|
||||
s_disc_crc = 0; // set below
|
||||
s_cur_region = "NTSC";
|
||||
}
|
||||
else
|
||||
{
|
||||
s_disc_serial = BiosSerial;
|
||||
s_disc_version = {};
|
||||
s_disc_crc = 0;
|
||||
s_cur_region = (BiosZone == "Europe") ? "PAL" : "NTSC";
|
||||
title = fmt::format(TRANSLATE_FS("VMManager", "PS2 BIOS ({})"), BiosZone);
|
||||
}
|
||||
|
||||
@@ -2818,6 +2822,11 @@ bool VMManager::Internal::IsFastBootInProgress()
|
||||
return s_fast_boot_requested && !HasBootedELF();
|
||||
}
|
||||
|
||||
std::string VMManager::Internal::GetCurrentRegion()
|
||||
{
|
||||
return s_cur_region;
|
||||
}
|
||||
|
||||
void VMManager::Internal::DisableFastBoot()
|
||||
{
|
||||
if (!s_fast_boot_requested)
|
||||
@@ -3270,6 +3279,11 @@ void VMManager::WarnAboutUnsafeSettings()
|
||||
append(ICON_FA_CIRCLE_EXCLAMATION,
|
||||
TRANSLATE_SV("VMManager", "Draw Buffering is enabled, this may result in graphical errors."));
|
||||
}
|
||||
if (EmuConfig.GS.UserHacks_RewriteLargeST)
|
||||
{
|
||||
append(ICON_FA_CIRCLE_EXCLAMATION,
|
||||
TRANSLATE_SV("VMManager", "Rewrite large ST is enabled, this may reduce performance."));
|
||||
}
|
||||
if (EmuConfig.GS.DumpReplaceableTextures)
|
||||
{
|
||||
append(ICON_FA_CIRCLE_EXCLAMATION,
|
||||
|
||||
@@ -306,6 +306,9 @@ namespace VMManager
|
||||
/// Returns true if fast booting is active (requested but ELF not started).
|
||||
bool IsFastBootInProgress();
|
||||
|
||||
// Returns the current disc/BIOS region, if set.
|
||||
std::string GetCurrentRegion();
|
||||
|
||||
/// Disables fast boot if it was requested, and found to be incompatible.
|
||||
void DisableFastBoot();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user