mirror of
https://github.com/PCSX2/pcsx2.git
synced 2026-07-19 21:45:42 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
37f2837a55 | ||
|
|
4b76157b9e | ||
|
|
3b2962fe1c | ||
|
|
20721b05fb | ||
|
|
75187d69c6 | ||
|
|
3df128d9f9 | ||
|
|
426d77a932 | ||
|
|
b3b1ff0f50 | ||
|
|
88029a1411 | ||
|
|
57e46f07fe | ||
|
|
254d955f89 | ||
|
|
e490685b42 | ||
|
|
7b0c39a20d | ||
|
|
059d631a6d | ||
|
|
7c255bd58d | ||
|
|
bbcd5ec25e | ||
|
|
5369d6b38e | ||
|
|
b3834427bc | ||
|
|
d7002433a2 | ||
|
|
e1291d5b7c |
@@ -27,7 +27,7 @@ LIBJPEGTURBO=3.1.4.1
|
||||
LIBPNG=1.6.58
|
||||
LIBWEBP=1.6.0
|
||||
NVENC=13.0.19.0
|
||||
SDL=SDL3-3.4.8
|
||||
SDL=SDL3-3.4.10
|
||||
LZ4=1.10.0
|
||||
VULKAN=1.4.328.1
|
||||
ZSTD=1.5.7
|
||||
@@ -60,7 +60,7 @@ b072aed6871998cce9b36e7774033105ca29e33632be5b6347f3206898e0756a ffmpeg-$FFMPEG
|
||||
ecae8008e2cc9ade2f2c1bb9d5e6d4fb73e7c433866a056bd82980741571a022 libjpeg-turbo-$LIBJPEGTURBO.tar.gz
|
||||
28eb403f51f0f7405249132cecfe82ea5c0ef97f1b32c5a65828814ae0d34775 libpng-$LIBPNG.tar.xz
|
||||
e4ab7009bf0629fd11982d4c2aa83964cf244cffba7347ecd39019a9e38c4564 libwebp-$LIBWEBP.tar.gz
|
||||
e9fff7467fb60f037e6708da18b25560649e4c63edc2a69bb871b960d9cbfbba $SDL.tar.gz
|
||||
12b34280415ec8418c864408b93d008a20a6530687ee613d60bfbd20411f2785 $SDL.tar.gz
|
||||
eee7dea22ed502868017971c86c63c4ed1e6085de0baebfdcc3d3322f00f3eb0 libpng-$LIBPNG-apng.patch.gz
|
||||
537512904744b35e232912055ccf8ec66d768639ff3abe5788d90d792ec5f48b lz4-$LZ4.tar.gz
|
||||
13da39edb3a40ed9713ae390ca89faa2f1202c9dda869ef306a8d4383e242bee nv-codec-headers-$NVENC.tar.gz
|
||||
|
||||
@@ -14,8 +14,8 @@
|
||||
"sources": [
|
||||
{
|
||||
"type": "archive",
|
||||
"url": "https://libsdl.org/release/SDL3-3.4.8.tar.gz",
|
||||
"sha256": "e9fff7467fb60f037e6708da18b25560649e4c63edc2a69bb871b960d9cbfbba"
|
||||
"url": "https://libsdl.org/release/SDL3-3.4.10.tar.gz",
|
||||
"sha256": "12b34280415ec8418c864408b93d008a20a6530687ee613d60bfbd20411f2785"
|
||||
}
|
||||
],
|
||||
"cleanup": [
|
||||
|
||||
@@ -47,7 +47,7 @@ QT=6.11.1
|
||||
QTAPNG=1.3.0
|
||||
|
||||
FREETYPE=2.14.3
|
||||
SDL=SDL3-3.4.8
|
||||
SDL=SDL3-3.4.10
|
||||
HARFBUZZ=14.2.0
|
||||
ZSTD=1.5.7
|
||||
LZ4=1.10.0
|
||||
@@ -93,7 +93,7 @@ b2bf6c6845ac175ed7f819145483ba4676f617aaa6a5012c8efee63c8bbac413 qtimageformats
|
||||
f1d3be3489f758efe1a8f12118a212febbe611aa670af32e0159fa3c1feab2a6 QtApng-$QTAPNG.tar.gz
|
||||
|
||||
36bc4f1cc413335368ee656c42afca65c5a3987e8768cc28cf11ba775e785a5f freetype-$FREETYPE.tar.xz
|
||||
e9fff7467fb60f037e6708da18b25560649e4c63edc2a69bb871b960d9cbfbba $SDL.tar.gz
|
||||
12b34280415ec8418c864408b93d008a20a6530687ee613d60bfbd20411f2785 $SDL.tar.gz
|
||||
c652d5d94971031654ab3989891a490a895d3e3f2b71171c62692b28e94b1b93 harfbuzz-$HARFBUZZ.tar.gz
|
||||
eb33e51f49a15e023950cd7825ca74a4a2b43db8354825ac24fc1b7ee09e6fa3 zstd-$ZSTD.tar.gz
|
||||
537512904744b35e232912055ccf8ec66d768639ff3abe5788d90d792ec5f48b lz4-$LZ4.tar.gz
|
||||
|
||||
@@ -24,7 +24,7 @@ QT=6.11.1
|
||||
QTAPNG=1.3.0
|
||||
|
||||
FREETYPE=2.14.3
|
||||
SDL=SDL3-3.4.8
|
||||
SDL=SDL3-3.4.10
|
||||
HARFBUZZ=14.2.0
|
||||
ZSTD=1.5.7
|
||||
LZ4=1.10.0
|
||||
@@ -69,7 +69,7 @@ b2bf6c6845ac175ed7f819145483ba4676f617aaa6a5012c8efee63c8bbac413 qtimageformats
|
||||
f1d3be3489f758efe1a8f12118a212febbe611aa670af32e0159fa3c1feab2a6 QtApng-$QTAPNG.tar.gz
|
||||
|
||||
36bc4f1cc413335368ee656c42afca65c5a3987e8768cc28cf11ba775e785a5f freetype-$FREETYPE.tar.xz
|
||||
e9fff7467fb60f037e6708da18b25560649e4c63edc2a69bb871b960d9cbfbba $SDL.tar.gz
|
||||
12b34280415ec8418c864408b93d008a20a6530687ee613d60bfbd20411f2785 $SDL.tar.gz
|
||||
c652d5d94971031654ab3989891a490a895d3e3f2b71171c62692b28e94b1b93 harfbuzz-$HARFBUZZ.tar.gz
|
||||
eb33e51f49a15e023950cd7825ca74a4a2b43db8354825ac24fc1b7ee09e6fa3 zstd-$ZSTD.tar.gz
|
||||
537512904744b35e232912055ccf8ec66d768639ff3abe5788d90d792ec5f48b lz4-$LZ4.tar.gz
|
||||
|
||||
@@ -60,7 +60,7 @@ set QTAPNG=1.3.0
|
||||
|
||||
set FREETYPE=2.14.3
|
||||
set HARFBUZZ=14.2.0
|
||||
set SDL=SDL3-3.4.8
|
||||
set SDL=SDL3-3.4.10
|
||||
set LIBJPEGTURBO=3.1.4.1
|
||||
set LIBPNG=1658
|
||||
set LIBPNGLONG=1.6.58
|
||||
@@ -94,7 +94,7 @@ call :downloadfile "lpng%LIBPNG%.zip" https://download.sourceforge.net/libpng/lp
|
||||
call :downloadfile "lpng%LIBPNG%-apng.patch.gz" https://download.sourceforge.net/libpng-apng/libpng-%LIBPNGLONG%-apng.patch.gz eee7dea22ed502868017971c86c63c4ed1e6085de0baebfdcc3d3322f00f3eb0 || goto error
|
||||
call :downloadfile "libjpeg-turbo-%LIBJPEGTURBO%.tar.gz" "https://github.com/libjpeg-turbo/libjpeg-turbo/releases/download/%LIBJPEGTURBO%/libjpeg-turbo-%LIBJPEGTURBO%.tar.gz" ecae8008e2cc9ade2f2c1bb9d5e6d4fb73e7c433866a056bd82980741571a022 || goto error
|
||||
call :downloadfile "libwebp-%WEBP%.tar.gz" "https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-%WEBP%.tar.gz" e4ab7009bf0629fd11982d4c2aa83964cf244cffba7347ecd39019a9e38c4564 || goto error
|
||||
call :downloadfile "%SDL%.zip" "https://libsdl.org/release/%SDL%.zip" 506206e02f90c1f37e048eacaf9e8a3a7dc682fd27783eb0ff15a7d2dcc9c2af || goto error
|
||||
call :downloadfile "%SDL%.zip" "https://libsdl.org/release/%SDL%.zip" be8dafef3ecc956e736cfd1c242c10737f3ad589a866f25ba437b7f2f6d5ced2 || goto error
|
||||
call :downloadfile "lz4-%LZ4%.zip" "https://github.com/lz4/lz4/archive/refs/tags/v%LZ4%.zip" 3224b4c80f351f194984526ef396f6079bd6332dd9825c72ac0d7a37b3cdc565 || goto error
|
||||
call :downloadfile "zlib%ZLIBSHORT%.zip" "https://github.com/madler/zlib/releases/download/v%ZLIB%/zlib%ZLIBSHORT%.zip" e8bf55f3017aa181690990cb58a994e77885da140609fc8f94abe9b65d2cae28 || goto error
|
||||
call :downloadfile "zstd-%ZSTD%.zip" "https://github.com/facebook/zstd/archive/refs/tags/v%ZSTD%.zip" 7897bc5d620580d9b7cd3539c44b59d78f3657d33663fe97a145e07b4ebd69a4 || goto error
|
||||
|
||||
@@ -77,7 +77,7 @@ set LIBX264=b35605ace3ddf7c1a5d67a2eb553f034aef41d55
|
||||
|
||||
set FREETYPE=2.14.3
|
||||
set HARFBUZZ=14.2.0
|
||||
set SDL=SDL3-3.4.8
|
||||
set SDL=SDL3-3.4.10
|
||||
set LIBJPEGTURBO=3.1.4.1
|
||||
set LIBPNG=1658
|
||||
set LIBPNGLONG=1.6.58
|
||||
@@ -121,7 +121,7 @@ call :downloadfile "lpng%LIBPNG%.zip" https://download.sourceforge.net/libpng/lp
|
||||
call :downloadfile "lpng%LIBPNG%-apng.patch.gz" https://download.sourceforge.net/libpng-apng/libpng-%LIBPNGLONG%-apng.patch.gz eee7dea22ed502868017971c86c63c4ed1e6085de0baebfdcc3d3322f00f3eb0 || goto error
|
||||
call :downloadfile "libjpeg-turbo-%LIBJPEGTURBO%.tar.gz" "https://github.com/libjpeg-turbo/libjpeg-turbo/releases/download/%LIBJPEGTURBO%/libjpeg-turbo-%LIBJPEGTURBO%.tar.gz" ecae8008e2cc9ade2f2c1bb9d5e6d4fb73e7c433866a056bd82980741571a022 || goto error
|
||||
call :downloadfile "libwebp-%WEBP%.tar.gz" "https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-%WEBP%.tar.gz" e4ab7009bf0629fd11982d4c2aa83964cf244cffba7347ecd39019a9e38c4564 || goto error
|
||||
call :downloadfile "%SDL%.zip" "https://libsdl.org/release/%SDL%.zip" 506206e02f90c1f37e048eacaf9e8a3a7dc682fd27783eb0ff15a7d2dcc9c2af || goto error
|
||||
call :downloadfile "%SDL%.zip" "https://libsdl.org/release/%SDL%.zip" be8dafef3ecc956e736cfd1c242c10737f3ad589a866f25ba437b7f2f6d5ced2 || goto error
|
||||
call :downloadfile "lz4-%LZ4%.zip" "https://github.com/lz4/lz4/archive/refs/tags/v%LZ4%.zip" 3224b4c80f351f194984526ef396f6079bd6332dd9825c72ac0d7a37b3cdc565 || goto error
|
||||
call :downloadfile "zlib%ZLIBSHORT%.zip" "https://github.com/madler/zlib/releases/download/v%ZLIB%/zlib%ZLIBSHORT%.zip" e8bf55f3017aa181690990cb58a994e77885da140609fc8f94abe9b65d2cae28 || goto error
|
||||
call :downloadfile "zstd-%ZSTD%.zip" "https://github.com/facebook/zstd/archive/refs/tags/v%ZSTD%.zip" 7897bc5d620580d9b7cd3539c44b59d78f3657d33663fe97a145e07b4ebd69a4 || goto error
|
||||
|
||||
@@ -704,7 +704,7 @@
|
||||
03000000730700000601000000000000,Sega Saturn Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,platform:Windows,
|
||||
03000000b40400000a01000000000000,Sega Saturn Controller,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,platform:Windows,
|
||||
030000003b07000004a1000000000000,SFX,a:b0,b:b2,back:b7,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b9,righttrigger:b5,start:b8,x:b1,y:b3,platform:Windows,
|
||||
03000000632500002705000000000000,ShanWan Q36,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,rightshoulder:b7,righttrigger:b9,start:b11,x:b3,y:b4,platform:Windows,
|
||||
03000000632500002705000000000000,ShanWan Gamepad,a:b0,b:b1,x:b3,y:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,-lefty:-a1,+lefty:+a2,leftshoulder:b6,lefttrigger:b8,rightshoulder:b7,righttrigger:b9,start:b11,leftstick:b13,rightstick:b14,platform:Windows,
|
||||
03000000f82100001900000000000000,Shogun Bros Chameleon X1,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows,
|
||||
03000000120c00001c1e000000000000,SnakeByte 4S PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,
|
||||
03000000140300000918000000000000,SNES Controller,a:b0,b:b1,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Windows,
|
||||
@@ -776,6 +776,7 @@
|
||||
030000005f140000c501000000000000,Trust Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,
|
||||
03000000b80500000210000000000000,Trust Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,
|
||||
030000005f1400002a01000000000000,Trust Predator GM1200,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b5,righttrigger:b2,x:b3,y:b4,platform:Windows,
|
||||
03000000580400000a10000000000000,Trust Sight Fighter,a:b0,b:b1,x:b3,y:b4,back:b5,start:b2,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a1,lefttrigger:b8,righttrigger:b9,platform:Windows,
|
||||
030000004f04000087b6000000000000,TWCS Throttle,dpdown:b8,dpleft:b9,dpright:b7,dpup:b6,leftstick:b5,lefttrigger:-a5,leftx:a0,lefty:a1,righttrigger:+a5,platform:Windows,
|
||||
03000000411200000450000000000000,Twin Shock,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a3,righty:a4,start:b11,x:b3,y:b0,platform:Windows,
|
||||
03000000d90400000200000000000000,TwinShock PS2 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows,
|
||||
|
||||
@@ -50,6 +50,12 @@
|
||||
#define PS_AA1_TRIANGLE_SW_Z 3
|
||||
#endif
|
||||
|
||||
#ifndef PS_ROV_DEPTH_NONE
|
||||
#define PS_ROV_DEPTH_NONE 0
|
||||
#define PS_ROV_DEPTH_READ_WRITE 1
|
||||
#define PS_ROV_DEPTH_READ_ONLY 2
|
||||
#endif
|
||||
|
||||
#ifndef PS_FST
|
||||
#define PS_IIP 0
|
||||
#define PS_FST 0
|
||||
@@ -111,6 +117,8 @@
|
||||
#define PS_TEX_IS_FB 0
|
||||
#define PS_AA1 0
|
||||
#define PS_ABE 0
|
||||
#define PS_ROV_COLOR 0
|
||||
#define PS_ROV_DEPTH 0
|
||||
#endif
|
||||
|
||||
#ifndef VS_EXPAND_NONE
|
||||
@@ -132,6 +140,12 @@
|
||||
#define SW_DEPTH (NEEDS_DEPTH_FOR_AFAIL || NEEDS_DEPTH_FOR_ZTST || NEEDS_DEPTH_FOR_AA1)
|
||||
#define ZWRITE (PS_ZFLOOR || PS_ZCLAMP || SW_DEPTH)
|
||||
|
||||
#define PS_RETURN_COLOR_ROV (!PS_NO_COLOR && PS_ROV_COLOR)
|
||||
#define PS_RETURN_COLOR (!PS_NO_COLOR && !PS_ROV_COLOR)
|
||||
#define PS_RETURN_DEPTH_ROV (PS_ROV_DEPTH == PS_ROV_DEPTH_READ_WRITE)
|
||||
#define PS_RETURN_DEPTH (ZWRITE && !PS_ROV_DEPTH)
|
||||
#define PS_ROV_EARLYDEPTHSTENCIL (PS_ROV_COLOR && !PS_ROV_DEPTH && !ZWRITE)
|
||||
|
||||
struct VS_INPUT
|
||||
{
|
||||
float2 st : TEXCOORD0;
|
||||
@@ -178,22 +192,27 @@ struct PS_INPUT
|
||||
|
||||
#ifdef PIXEL_SHADER
|
||||
|
||||
struct PS_OUTPUT
|
||||
struct PS_OUTPUT_REAL
|
||||
{
|
||||
#define NUM_RTS 0
|
||||
#if !PS_NO_COLOR
|
||||
#if PS_DATE == 1 || PS_DATE == 2
|
||||
float c : SV_Target;
|
||||
#else
|
||||
float4 c0 : SV_Target0;
|
||||
#undef NUM_RTS
|
||||
#define NUM_RTS 1
|
||||
#if !PS_NO_COLOR1
|
||||
float4 c1 : SV_Target1;
|
||||
|
||||
#if PS_RETURN_COLOR
|
||||
#if PS_DATE == 1 || PS_DATE == 2
|
||||
float c : SV_Target;
|
||||
#else
|
||||
|
||||
float4 c0 : SV_Target0;
|
||||
|
||||
#undef NUM_RTS
|
||||
#define NUM_RTS 1
|
||||
|
||||
#if !PS_NO_COLOR1
|
||||
float4 c1 : SV_Target1;
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
#if ZWRITE
|
||||
|
||||
#if PS_RETURN_DEPTH
|
||||
// In DX12 we do depth feedback loops with a color copy.
|
||||
#if SW_DEPTH && PS_NO_COLOR1 && DX12
|
||||
#if NUM_RTS > 0
|
||||
@@ -208,16 +227,45 @@ struct PS_OUTPUT
|
||||
float depth : SV_Depth;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#undef NUM_RTS
|
||||
};
|
||||
|
||||
struct PS_OUTPUT
|
||||
{
|
||||
#if !PS_NO_COLOR
|
||||
#if PS_DATE == 1 || PS_DATE == 2
|
||||
float c;
|
||||
#else
|
||||
float4 c0;
|
||||
#if !PS_NO_COLOR1
|
||||
float4 c1;
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
};
|
||||
|
||||
Texture2D<float4> Texture : register(t0);
|
||||
Texture2D<float4> Palette : register(t1);
|
||||
#if !PS_ROV_COLOR
|
||||
Texture2D<float4> RtTexture : register(t2);
|
||||
#endif
|
||||
Texture2D<float> PrimMinTexture : register(t3);
|
||||
#if !PS_ROV_DEPTH
|
||||
Texture2D<float> DepthTexture : register(t4);
|
||||
#endif
|
||||
SamplerState TextureSampler : register(s0);
|
||||
|
||||
#if PS_ROV_COLOR
|
||||
RasterizerOrderedTexture2D<unorm float4> RtTextureRov : register(u0);
|
||||
static float4 rov_rt_value;
|
||||
#endif
|
||||
|
||||
#if PS_ROV_DEPTH
|
||||
RasterizerOrderedTexture2D<float> DepthTextureRov : register(u1);
|
||||
static float rov_depth_value;
|
||||
#endif
|
||||
|
||||
#ifdef DX12
|
||||
cbuffer cb1 : register(b1)
|
||||
#else
|
||||
@@ -244,6 +292,38 @@ cbuffer cb1
|
||||
float RcpScaleFactor;
|
||||
};
|
||||
|
||||
float4 RtLoad(int2 xy)
|
||||
{
|
||||
#if PS_ROV_COLOR
|
||||
return rov_rt_value;
|
||||
#else
|
||||
return RtTexture.Load(int3(int2(xy), 0));
|
||||
#endif
|
||||
}
|
||||
|
||||
float DepthLoad(int2 xy)
|
||||
{
|
||||
#if PS_ROV_DEPTH
|
||||
return rov_depth_value;
|
||||
#else
|
||||
return DepthTexture.Load(int3(int2(xy), 0));
|
||||
#endif
|
||||
}
|
||||
|
||||
void RtWrite(int2 xy, float4 c)
|
||||
{
|
||||
#if PS_ROV_COLOR
|
||||
RtTextureRov[xy] = c;
|
||||
#endif
|
||||
}
|
||||
|
||||
void DepthWrite(int2 xy, float d)
|
||||
{
|
||||
#if PS_ROV_DEPTH
|
||||
DepthTextureRov[xy] = d;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if (PS_AUTOMATIC_LOD != 1) && (PS_MANUAL_LOD == 1)
|
||||
float manual_lod(float uv_w)
|
||||
{
|
||||
@@ -407,7 +487,7 @@ float4 sample_c_af(float2 uv, float uv_w)
|
||||
float4 sample_c(float2 uv, float uv_w, int2 xy)
|
||||
{
|
||||
#if PS_TEX_IS_FB == 1
|
||||
return RtTexture.Load(int3(int2(xy), 0));
|
||||
return RtLoad(xy);
|
||||
#elif PS_REGION_RECT == 1
|
||||
return Texture.Load(int3(int2(uv), 0));
|
||||
#else
|
||||
@@ -602,7 +682,7 @@ float4x4 sample_4p(uint4 u)
|
||||
uint fetch_raw_depth(int2 xy)
|
||||
{
|
||||
#if PS_TEX_IS_FB == 1
|
||||
float4 col = RtTexture.Load(int3(xy, 0));
|
||||
float4 col = RtLoad(xy);
|
||||
#else
|
||||
float4 col = Texture.Load(int3(xy, 0));
|
||||
#endif
|
||||
@@ -612,7 +692,7 @@ uint fetch_raw_depth(int2 xy)
|
||||
float4 fetch_raw_color(int2 xy)
|
||||
{
|
||||
#if PS_TEX_IS_FB == 1
|
||||
return RtTexture.Load(int3(xy, 0));
|
||||
return RtLoad(xy);
|
||||
#else
|
||||
return Texture.Load(int3(xy, 0));
|
||||
#endif
|
||||
@@ -621,7 +701,7 @@ float4 fetch_raw_color(int2 xy)
|
||||
float4 fetch_c(int2 uv)
|
||||
{
|
||||
#if PS_TEX_IS_FB == 1
|
||||
return RtTexture.Load(int3(uv, 0));
|
||||
return RtLoad(uv);
|
||||
#else
|
||||
return Texture.Load(int3(uv, 0));
|
||||
#endif
|
||||
@@ -1027,7 +1107,7 @@ void ps_fbmask(inout float4 C, float2 pos_xy)
|
||||
if (PS_FBMASK)
|
||||
{
|
||||
float multi = PS_COLCLIP_HW ? 65535.0f : 255.0f;
|
||||
float4 RT = trunc(RtTexture.Load(int3(pos_xy, 0)) * multi + 0.1f);
|
||||
float4 RT = trunc(RtLoad(int2(pos_xy)) * multi + 0.1f);
|
||||
C = (float4)(((uint4)C & ~FbMask) | ((uint4)RT & FbMask));
|
||||
}
|
||||
}
|
||||
@@ -1103,7 +1183,7 @@ void ps_blend(inout float4 Color, inout float4 As_rgba, float2 pos_xy)
|
||||
As_rgba.rgb = (float3)1.0f;
|
||||
}
|
||||
|
||||
float4 RT = SW_BLEND_NEEDS_RT ? RtTexture.Load(int3(pos_xy, 0)) : (float4)0.0f;
|
||||
float4 RT = SW_BLEND_NEEDS_RT ? RtLoad(int2(pos_xy)) : (float4)0.0f;
|
||||
|
||||
if (PS_SHUFFLE && SW_BLEND_NEEDS_RT)
|
||||
{
|
||||
@@ -1236,20 +1316,49 @@ void ps_blend(inout float4 Color, inout float4 As_rgba, float2 pos_xy)
|
||||
}
|
||||
}
|
||||
|
||||
PS_OUTPUT ps_main(PS_INPUT input)
|
||||
#if PS_ROV_EARLYDEPTHSTENCIL
|
||||
[earlydepthstencil]
|
||||
#endif
|
||||
|
||||
#if PS_ROV_COLOR || PS_ROV_DEPTH
|
||||
#define DISCARD rov_discard = true
|
||||
#else
|
||||
#define DISCARD discard
|
||||
#endif
|
||||
|
||||
#if (PS_RETURN_COLOR || PS_RETURN_DEPTH)
|
||||
PS_OUTPUT_REAL ps_main(PS_INPUT input)
|
||||
#else
|
||||
void ps_main(PS_INPUT input)
|
||||
#endif
|
||||
{
|
||||
// Must floor before depth testing.
|
||||
#if PS_ZFLOOR
|
||||
input.p.z = floor(input.p.z * exp2(32.0f)) * exp2(-32.0f);
|
||||
#endif
|
||||
|
||||
#if PS_ZTST == ZTST_GEQUAL
|
||||
if (input.p.z < DepthTexture.Load(int3(input.p.xy, 0)).r)
|
||||
discard;
|
||||
#elif PS_ZTST == ZTST_GREATER
|
||||
if (input.p.z <= DepthTexture.Load(int3(input.p.xy, 0)).r)
|
||||
discard;
|
||||
#if PS_ROV_COLOR
|
||||
rov_rt_value = RtTextureRov[input.p.xy];
|
||||
#endif
|
||||
|
||||
#if PS_ROV_DEPTH
|
||||
rov_depth_value = DepthTextureRov[input.p.xy];
|
||||
#endif
|
||||
|
||||
#if PS_ROV_COLOR || PS_ROV_DEPTH
|
||||
bool rov_discard = false;
|
||||
#endif
|
||||
|
||||
// Use ROV discard macro for since we cannot do
|
||||
// conditional discard based on value read from ROV.
|
||||
#if PS_ZTST == ZTST_GEQUAL
|
||||
if (input.p.z < DepthLoad(input.p.xy))
|
||||
DISCARD;
|
||||
#elif PS_ZTST == ZTST_GREATER
|
||||
if (input.p.z <= DepthLoad(input.p.xy))
|
||||
DISCARD;
|
||||
#endif
|
||||
|
||||
float4 C = ps_color(input);
|
||||
|
||||
#if PS_AA1
|
||||
@@ -1267,13 +1376,11 @@ PS_OUTPUT ps_main(PS_INPUT input)
|
||||
|
||||
bool atst_pass = atst(C);
|
||||
|
||||
#if PS_AFAIL == AFAIL_KEEP
|
||||
#if PS_ATST != PS_ATST_NONE && PS_AFAIL == AFAIL_KEEP
|
||||
if (!atst_pass)
|
||||
discard;
|
||||
#endif
|
||||
|
||||
PS_OUTPUT output;
|
||||
|
||||
if (PS_SCANMSK & 2)
|
||||
{
|
||||
// fail depth test on prohibited lines
|
||||
@@ -1284,7 +1391,7 @@ PS_OUTPUT ps_main(PS_INPUT input)
|
||||
float4 alpha_blend = (float4)0.0f;
|
||||
if (SW_AD_TO_HW)
|
||||
{
|
||||
float4 RT = PS_RTA_CORRECTION ? trunc(RtTexture.Load(int3(input.p.xy, 0)) * 128.0f + 0.1f) : trunc(RtTexture.Load(int3(input.p.xy, 0)) * 255.0f + 0.1f);
|
||||
float4 RT = PS_RTA_CORRECTION ? trunc(RtLoad(input.p.xy) * 128.0f + 0.1f) : trunc(RtLoad(input.p.xy) * 255.0f + 0.1f);
|
||||
alpha_blend = (float4)(RT.a / 128.0f);
|
||||
}
|
||||
else
|
||||
@@ -1308,9 +1415,9 @@ PS_OUTPUT ps_main(PS_INPUT input)
|
||||
|
||||
#if PS_WRITE_RG == 1
|
||||
// Pseudo 16 bits access.
|
||||
float rt_a = RtTexture.Load(int3(input.p.xy, 0)).g;
|
||||
float rt_a = RtLoad(input.p.xy).g;
|
||||
#else
|
||||
float rt_a = RtTexture.Load(int3(input.p.xy, 0)).a;
|
||||
float rt_a = RtLoad(input.p.xy).a;
|
||||
#endif
|
||||
|
||||
#if (PS_DATE & 3) == 1
|
||||
@@ -1329,9 +1436,8 @@ PS_OUTPUT ps_main(PS_INPUT input)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if (bad)
|
||||
discard;
|
||||
|
||||
if (bad)
|
||||
discard;
|
||||
#endif
|
||||
|
||||
#if PS_DATE == 3
|
||||
@@ -1342,6 +1448,8 @@ PS_OUTPUT ps_main(PS_INPUT input)
|
||||
discard;
|
||||
#endif
|
||||
|
||||
PS_OUTPUT output;
|
||||
|
||||
// Get first primitive that will write a failling alpha value
|
||||
#if PS_DATE == 1
|
||||
// DATM == 0
|
||||
@@ -1427,32 +1535,32 @@ PS_OUTPUT ps_main(PS_INPUT input)
|
||||
alpha_blend.a = float(atst_pass);
|
||||
#endif
|
||||
|
||||
// Output color scaling
|
||||
#if !PS_NO_COLOR
|
||||
output.c0.a = PS_RTA_CORRECTION ? C.a / 128.0f : C.a / 255.0f;
|
||||
output.c0.rgb = PS_COLCLIP_HW ? float3(C.rgb / 65535.0f) : C.rgb / 255.0f;
|
||||
#if !PS_NO_COLOR1
|
||||
output.c1 = alpha_blend;
|
||||
#endif
|
||||
#endif // !PS_NO_COLOR
|
||||
|
||||
// Alpha test with feedback
|
||||
#if PS_AFAIL == AFAIL_FB_ONLY
|
||||
if (!atst_pass)
|
||||
input.p.z = DepthTexture.Load(int3(input.p.xy, 0)).r;
|
||||
input.p.z = DepthLoad(input.p.xy);
|
||||
#elif PS_AFAIL == AFAIL_ZB_ONLY
|
||||
if (!atst_pass)
|
||||
output.c0 = RtTexture.Load(int3(input.p.xy, 0));
|
||||
output.c0 = RtLoad(input.p.xy);
|
||||
#elif PS_AFAIL == AFAIL_RGB_ONLY || PS_AFAIL == AFAIL_RGB_ONLY_SW_Z
|
||||
if (!atst_pass)
|
||||
{
|
||||
output.c0.a = RtTexture.Load(int3(input.p.xy, 0)).a;
|
||||
output.c0.a = RtLoad(input.p.xy).a;
|
||||
#if PS_AFAIL == AFAIL_RGB_ONLY_SW_Z
|
||||
input.p.z = DepthTexture.Load(int3(input.p.xy, 0)).r;
|
||||
input.p.z = DepthLoad(input.p.xy);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // !PS_NO_COLOR
|
||||
|
||||
#endif // PS_DATE != 1/2
|
||||
|
||||
#if PS_ZCLAMP
|
||||
@@ -1461,18 +1569,47 @@ PS_OUTPUT ps_main(PS_INPUT input)
|
||||
|
||||
#if PS_AA1 == PS_AA1_TRIANGLE_SW_Z
|
||||
if (!bool(input.interior))
|
||||
input.p.z = DepthTexture.Load(int3(input.p.xy, 0)).r; // No depth update for triangle edges.
|
||||
input.p.z = DepthLoad(input.p.xy); // No depth update for triangle edges.
|
||||
#endif
|
||||
|
||||
#if ZWRITE
|
||||
#if SW_DEPTH && PS_NO_COLOR1 && DX12
|
||||
// Output color clone for feedback as well as real depth.
|
||||
output.depth_color = input.p.z;
|
||||
#endif
|
||||
output.depth = input.p.z;
|
||||
#if (PS_RETURN_COLOR || PS_RETURN_DEPTH)
|
||||
// Output struct with the actual system values output semantics.
|
||||
PS_OUTPUT_REAL output_real;
|
||||
#endif
|
||||
|
||||
return output;
|
||||
// Color write back
|
||||
#if PS_RETURN_COLOR
|
||||
#if PS_DATE == 1 || PS_DATE == 2
|
||||
output_real.c = output.c;
|
||||
#else
|
||||
output_real.c0 = output.c0;
|
||||
#if !PS_NO_COLOR1
|
||||
output_real.c1 = output.c1;
|
||||
#endif
|
||||
#endif
|
||||
#elif PS_RETURN_COLOR_ROV
|
||||
output.c0 = (rov_discard | (FbMask == 0xFFu)) ? RtLoad(input.p.xy) : output.c0;
|
||||
|
||||
RtWrite(input.p.xy, output.c0);
|
||||
#endif
|
||||
|
||||
// Depth write back
|
||||
#if PS_RETURN_DEPTH
|
||||
output_real.depth = input.p.z;
|
||||
#if SW_DEPTH && PS_NO_COLOR1 && DX12
|
||||
// Output color clone for feedback.
|
||||
output_real.depth_color = input.p.z;
|
||||
#endif
|
||||
#elif PS_RETURN_DEPTH_ROV
|
||||
#if SW_DEPTH
|
||||
input.p.z = rov_discard ? DepthLoad(input.p.xy) : input.p.z;
|
||||
#endif
|
||||
DepthWrite(input.p.xy, input.p.z);
|
||||
#endif
|
||||
|
||||
#if (PS_RETURN_COLOR || PS_RETURN_DEPTH)
|
||||
return output_real;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // PIXEL_SHADER
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
#define NEEDS_DEPTH_FOR_ZTST (PS_ZTST == ZTST_GEQUAL || PS_ZTST == ZTST_GREATER)
|
||||
#define NEEDS_DEPTH_FOR_AA1 (PS_AA1 == PS_AA1_TRIANGLE_SW_Z)
|
||||
|
||||
#define NEEDS_RT (NEEDS_RT_EARLY || NEEDS_RT_FOR_AFAIL || (!PS_PRIMID_INIT && (PS_FBMASK || SW_BLEND_NEEDS_RT || SW_AD_TO_HW)) || PS_COLOR_FEEDBACK)
|
||||
#define NEEDS_RT (NEEDS_RT_EARLY || NEEDS_RT_FOR_AFAIL || (!PS_PRIMID_INIT && (PS_FBMASK || SW_BLEND_NEEDS_RT || SW_AD_TO_HW)))
|
||||
#define NEEDS_TEX (PS_TFX != 4)
|
||||
#define SW_DEPTH (NEEDS_DEPTH_FOR_AFAIL || NEEDS_DEPTH_FOR_ZTST || NEEDS_DEPTH_FOR_AA1)
|
||||
#define ZWRITE (SW_DEPTH || PS_ZCLAMP || PS_ZFLOOR)
|
||||
@@ -121,7 +121,7 @@ in SHADER
|
||||
#if defined(GL_EXT_shader_framebuffer_fetch)
|
||||
#undef TARGET_0_QUALIFIER
|
||||
#define TARGET_0_QUALIFIER inout
|
||||
#define LAST_FRAG_COLOR SV_Target0
|
||||
#define LAST_FRAG_COLOR o_col0
|
||||
#elif defined(GL_ARM_shader_framebuffer_fetch)
|
||||
#define LAST_FRAG_COLOR gl_LastFragColorARM
|
||||
#endif
|
||||
@@ -129,20 +129,20 @@ in SHADER
|
||||
|
||||
#if !PS_NO_COLOR && !PS_NO_COLOR1
|
||||
// Same buffer but 2 colors for dual source blending
|
||||
layout(location = 0, index = 0) TARGET_0_QUALIFIER vec4 SV_Target0;
|
||||
layout(location = 0, index = 1) out vec4 SV_Target1;
|
||||
layout(location = 0, index = 0) TARGET_0_QUALIFIER vec4 o_col0;
|
||||
layout(location = 0, index = 1) out vec4 o_col1;
|
||||
#elif !PS_NO_COLOR
|
||||
layout(location = 0) TARGET_0_QUALIFIER vec4 SV_Target0;
|
||||
layout(location = 0) TARGET_0_QUALIFIER vec4 o_col0;
|
||||
#endif
|
||||
|
||||
// Depth feedback mode 2 is for depth as color.
|
||||
// Use FB fetch for the feedback if it's available.
|
||||
#if SW_DEPTH && PS_NO_COLOR1 && (DEPTH_FEEDBACK_SUPPORT == 2)
|
||||
#if HAS_FRAMEBUFFER_FETCH
|
||||
layout(location = 1) inout float SV_Target1;
|
||||
#else
|
||||
layout(location = 1) out float SV_Target1;
|
||||
#endif
|
||||
#if HAS_FRAMEBUFFER_FETCH
|
||||
layout(location = 1) inout float o_col1;
|
||||
#else
|
||||
layout(location = 1) out float o_col1;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if NEEDS_TEX
|
||||
@@ -180,14 +180,14 @@ vec4 sample_from_rt()
|
||||
#endif
|
||||
}
|
||||
|
||||
vec4 sample_from_depth()
|
||||
float sample_from_depth()
|
||||
{
|
||||
#if !SW_DEPTH
|
||||
return vec4(0.0);
|
||||
return 0.0f;
|
||||
#elif HAS_FRAMEBUFFER_FETCH && (DEPTH_FEEDBACK_SUPPORT == 2)
|
||||
return SV_Target1;
|
||||
return o_col1;
|
||||
#else
|
||||
return texelFetch(DepthSampler, ivec2(gl_FragCoord.xy), 0);
|
||||
return texelFetch(DepthSampler, ivec2(gl_FragCoord.xy), 0).r;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -1193,10 +1193,10 @@ void ps_main()
|
||||
#endif
|
||||
|
||||
#if PS_ZTST == ZTST_GEQUAL
|
||||
if (input_z < sample_from_depth().r)
|
||||
if (input_z < sample_from_depth())
|
||||
discard;
|
||||
#elif PS_ZTST == ZTST_GREATER
|
||||
if (input_z <= sample_from_depth().r)
|
||||
if (input_z <= sample_from_depth())
|
||||
discard;
|
||||
#endif
|
||||
|
||||
@@ -1264,7 +1264,7 @@ void ps_main()
|
||||
|
||||
bool atst_pass = atst(C);
|
||||
|
||||
#if PS_AFAIL == AFAIL_KEEP
|
||||
#if PS_ATST != PS_ATST_NONE && PS_AFAIL == AFAIL_KEEP
|
||||
if (!atst_pass)
|
||||
discard;
|
||||
#endif
|
||||
@@ -1293,12 +1293,12 @@ void ps_main()
|
||||
#if PS_DATE == 1
|
||||
// DATM == 0
|
||||
// Pixel with alpha equal to 1 will failed (128-255)
|
||||
SV_Target0 = (C.a > 127.5f) ? vec4(gl_PrimitiveID) : vec4(0x7FFFFFFF);
|
||||
o_col0 = (C.a > 127.5f) ? vec4(gl_PrimitiveID) : vec4(0x7FFFFFFF);
|
||||
return;
|
||||
#elif PS_DATE == 2
|
||||
// DATM == 1
|
||||
// Pixel with alpha equal to 0 will failed (0-127)
|
||||
SV_Target0 = (C.a < 127.5f) ? vec4(gl_PrimitiveID) : vec4(0x7FFFFFFF);
|
||||
o_col0 = (C.a < 127.5f) ? vec4(gl_PrimitiveID) : vec4(0x7FFFFFFF);
|
||||
return;
|
||||
#endif
|
||||
|
||||
@@ -1372,7 +1372,7 @@ void ps_main()
|
||||
// Alpha test with feedback
|
||||
#if PS_AFAIL == AFAIL_FB_ONLY
|
||||
if (!atst_pass)
|
||||
input_z = sample_from_depth().r;
|
||||
input_z = sample_from_depth();
|
||||
#elif PS_AFAIL == AFAIL_ZB_ONLY
|
||||
if (!atst_pass)
|
||||
C = sample_from_rt();
|
||||
@@ -1381,17 +1381,17 @@ void ps_main()
|
||||
{
|
||||
C.a = sample_from_rt().a;
|
||||
#if PS_AFAIL == AFAIL_RGB_ONLY_SW_Z
|
||||
input_z = sample_from_depth().r;
|
||||
input_z = sample_from_depth();
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
// Warning: do not write SV_Target0 until the end since the value might be needed for
|
||||
// Warning: do not write o_col0 until the end since the value might be needed for
|
||||
// FB fetch in sample_from_rt().
|
||||
SV_Target0 = C;
|
||||
o_col0 = C;
|
||||
|
||||
#if !PS_NO_COLOR1
|
||||
SV_Target1 = alpha_blend;
|
||||
o_col1 = alpha_blend;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -1401,16 +1401,17 @@ void ps_main()
|
||||
|
||||
#if PS_AA1 == PS_AA1_TRIANGLE_SW_Z
|
||||
if (!bool(PSin.interior))
|
||||
input_z = sample_from_depth().r; // No depth update for triangle edges.
|
||||
input_z = sample_from_depth(); // No depth update for triangle edges.
|
||||
#endif
|
||||
|
||||
// Writing back depth
|
||||
#if ZWRITE
|
||||
#if SW_DEPTH && PS_NO_COLOR1 && (DEPTH_FEEDBACK_SUPPORT == 2)
|
||||
// Depth as color write. For depth as color feedback we write to both
|
||||
// color copy and real depth to avoid having to copy back to real depth.
|
||||
// Warning: do not write SV_Target1 until the end since the value might
|
||||
// Warning: do not write o_col1 until the end since the value might
|
||||
// be needed for FB fetch in sample_from_depth().
|
||||
SV_Target1 = input_z;
|
||||
o_col1 = input_z;
|
||||
#endif
|
||||
// Standard depth write.
|
||||
gl_FragDepth = input_z;
|
||||
|
||||
@@ -522,6 +522,12 @@ void main()
|
||||
#define PS_AA1_TRIANGLE_SW_Z 3
|
||||
#endif
|
||||
|
||||
#ifndef PS_ROV_DEPTH_NONE
|
||||
#define PS_ROV_DEPTH_NONE 0
|
||||
#define PS_ROV_DEPTH_READ_WRITE 1
|
||||
#define PS_ROV_DEPTH_READ_ONLY 2
|
||||
#endif
|
||||
|
||||
#ifndef PS_FST
|
||||
#define PS_FST 0
|
||||
#define PS_WMS 0
|
||||
@@ -574,6 +580,8 @@ void main()
|
||||
#define PS_NO_COLOR1 0
|
||||
#define PS_DATE 0
|
||||
#define PS_TEX_IS_FB 0
|
||||
#define PS_ROV_COLOR 0
|
||||
#define PS_ROV_DEPTH 0
|
||||
#endif
|
||||
|
||||
#define SW_BLEND (PS_BLEND_A || PS_BLEND_B || PS_BLEND_D)
|
||||
@@ -586,7 +594,13 @@ void main()
|
||||
|
||||
#define PS_FEEDBACK_LOOP_IS_NEEDED_RT (PS_TEX_IS_FB == 1 || AFAIL_NEEDS_RT || PS_FBMASK || SW_BLEND_NEEDS_RT || SW_AD_TO_HW || (PS_DATE >= 5))
|
||||
#define PS_FEEDBACK_LOOP_IS_NEEDED_DEPTH (AFAIL_NEEDS_DEPTH || ZTST_NEEDS_DEPTH || AA1_NEEDS_DEPTH)
|
||||
#define ZWRITE (PS_ZCLAMP || PS_ZFLOOR || PS_FEEDBACK_LOOP_IS_NEEDED_DEPTH)
|
||||
#define ZWRITE (PS_ZCLAMP || PS_ZFLOOR || SW_DEPTH || PS_FEEDBACK_LOOP_IS_NEEDED_DEPTH)
|
||||
|
||||
#define PS_RETURN_COLOR_ROV (!PS_NO_COLOR && PS_ROV_COLOR)
|
||||
#define PS_RETURN_COLOR (!PS_NO_COLOR && !PS_ROV_COLOR)
|
||||
#define PS_RETURN_DEPTH_ROV (PS_ROV_DEPTH == PS_ROV_DEPTH_READ_WRITE)
|
||||
#define PS_RETURN_DEPTH (ZWRITE && !PS_ROV_DEPTH)
|
||||
#define PS_ROV_EARLYDEPTHSTENCIL (PS_ROV_COLOR && !PS_ROV_DEPTH && !ZWRITE)
|
||||
|
||||
#define NEEDS_TEX (PS_TFX != 4)
|
||||
|
||||
@@ -625,11 +639,27 @@ layout(location = 0) in VSOutput
|
||||
flat uint interior; // 1 for triangle interior; 0 for edge;
|
||||
} vsIn;
|
||||
|
||||
#if !PS_NO_COLOR && !PS_NO_COLOR1
|
||||
layout(location = 0, index = 0) out vec4 o_col0;
|
||||
layout(location = 0, index = 1) out vec4 o_col1;
|
||||
#elif !PS_NO_COLOR
|
||||
layout(location = 0) out vec4 o_col0;
|
||||
#if PS_RETURN_COLOR
|
||||
#if !PS_NO_COLOR1
|
||||
layout(location = 0, index = 0) out vec4 o_col0;
|
||||
layout(location = 0, index = 1) out vec4 o_col1;
|
||||
#elif !PS_NO_COLOR
|
||||
layout(location = 0) out vec4 o_col0;
|
||||
#endif
|
||||
#elif PS_RETURN_COLOR_ROV
|
||||
vec4 o_col0;
|
||||
#endif
|
||||
|
||||
#if PS_ROV_COLOR
|
||||
layout(set = 1, binding = 5, rgba8) uniform restrict coherent image2D RtImageRov;
|
||||
vec4 rov_rt_value;
|
||||
vec4 sample_from_rt() { return rov_rt_value; }
|
||||
#endif
|
||||
|
||||
#if PS_ROV_DEPTH
|
||||
layout(set = 1, binding = 6, r32f) uniform restrict coherent image2D DepthImageRov;
|
||||
float rov_depth_value;
|
||||
float sample_from_depth() { return rov_depth_value; }
|
||||
#endif
|
||||
|
||||
#if NEEDS_TEX
|
||||
@@ -639,27 +669,27 @@ layout(set = 1, binding = 1) uniform texture2D Palette;
|
||||
|
||||
#if PS_FEEDBACK_LOOP_IS_NEEDED_RT || PS_FEEDBACK_LOOP_IS_NEEDED_DEPTH
|
||||
#if defined(DISABLE_TEXTURE_BARRIER) || defined(HAS_FEEDBACK_LOOP_LAYOUT)
|
||||
#if PS_FEEDBACK_LOOP_IS_NEEDED_RT
|
||||
#if (PS_FEEDBACK_LOOP_IS_NEEDED_RT && !PS_ROV_COLOR)
|
||||
layout(set = 1, binding = 2) uniform texture2D RtSampler;
|
||||
vec4 sample_from_rt() { return texelFetch(RtSampler, ivec2(gl_FragCoord.xy), 0); }
|
||||
#endif
|
||||
#if PS_FEEDBACK_LOOP_IS_NEEDED_DEPTH
|
||||
#if (PS_FEEDBACK_LOOP_IS_NEEDED_DEPTH && !PS_ROV_DEPTH)
|
||||
layout(set = 1, binding = 4) uniform texture2D DepthSampler;
|
||||
vec4 sample_from_depth() { return texelFetch(DepthSampler, ivec2(gl_FragCoord.xy), 0); }
|
||||
float sample_from_depth() { return texelFetch(DepthSampler, ivec2(gl_FragCoord.xy), 0).r; }
|
||||
#endif
|
||||
#else
|
||||
// Must consider each case separately since the input attachment indices must be consecutive.
|
||||
#if PS_FEEDBACK_LOOP_IS_NEEDED_RT && PS_FEEDBACK_LOOP_IS_NEEDED_DEPTH
|
||||
#if (PS_FEEDBACK_LOOP_IS_NEEDED_RT && !PS_ROV_COLOR) && (PS_FEEDBACK_LOOP_IS_NEEDED_DEPTH && !PS_ROV_DEPTH)
|
||||
layout(input_attachment_index = 0, set = 1, binding = 2) uniform subpassInput RtSampler;
|
||||
layout(input_attachment_index = 1, set = 1, binding = 4) uniform subpassInput DepthSampler;
|
||||
vec4 sample_from_rt() { return subpassLoad(RtSampler); }
|
||||
vec4 sample_from_depth() { return subpassLoad(DepthSampler); }
|
||||
#elif PS_FEEDBACK_LOOP_IS_NEEDED_RT
|
||||
float sample_from_depth() { return subpassLoad(DepthSampler).r; }
|
||||
#elif (PS_FEEDBACK_LOOP_IS_NEEDED_RT && !PS_ROV_COLOR)
|
||||
layout(input_attachment_index = 0, set = 1, binding = 2) uniform subpassInput RtSampler;
|
||||
vec4 sample_from_rt() { return subpassLoad(RtSampler); }
|
||||
#elif PS_FEEDBACK_LOOP_IS_NEEDED_DEPTH
|
||||
#elif (PS_FEEDBACK_LOOP_IS_NEEDED_DEPTH && !PS_ROV_DEPTH)
|
||||
layout(input_attachment_index = 0, set = 1, binding = 4) uniform subpassInput DepthSampler;
|
||||
vec4 sample_from_depth() { return subpassLoad(DepthSampler); }
|
||||
float sample_from_depth() { return subpassLoad(DepthSampler).r; }
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
@@ -1604,7 +1634,7 @@ void ps_blend(inout vec4 Color, inout vec4 As_rgba)
|
||||
#elif PS_BLEND_MIX == 1
|
||||
Color.rgb = ((A - B) * C_clamped + D) - (124.0f/256.0f);
|
||||
#else
|
||||
Color.rgb = trunc((A - B) * C + D);
|
||||
Color.rgb = trunc((A - B) * C + D);
|
||||
#endif
|
||||
|
||||
#if PS_BLEND_HW == 1
|
||||
@@ -1678,6 +1708,20 @@ void ps_blend(inout vec4 Color, inout vec4 As_rgba)
|
||||
#endif
|
||||
}
|
||||
|
||||
#if PS_ROV_COLOR || PS_ROV_DEPTH
|
||||
layout(pixel_interlock_ordered) in;
|
||||
#endif
|
||||
|
||||
#if PS_ROV_EARLYDEPTHSTENCIL
|
||||
layout(early_fragment_tests) in;
|
||||
#endif
|
||||
|
||||
#if PS_ROV_COLOR || PS_ROV_DEPTH
|
||||
#define DISCARD rov_discard = true
|
||||
#else
|
||||
#define DISCARD discard
|
||||
#endif
|
||||
|
||||
void main()
|
||||
{
|
||||
float input_z = gl_FragCoord.z;
|
||||
@@ -1687,18 +1731,34 @@ void main()
|
||||
input_z = floor(input_z * exp2(32.0f)) * exp2(-32.0f);
|
||||
#endif
|
||||
|
||||
#if PS_ROV_COLOR || PS_ROV_DEPTH
|
||||
beginInvocationInterlockARB();
|
||||
#endif
|
||||
|
||||
#if PS_ROV_COLOR
|
||||
rov_rt_value = imageLoad(RtImageRov, ivec2(gl_FragCoord.xy));
|
||||
#endif
|
||||
|
||||
#if PS_ROV_DEPTH
|
||||
rov_depth_value = imageLoad(DepthImageRov, ivec2(gl_FragCoord.xy)).r;
|
||||
#endif
|
||||
|
||||
#if PS_ROV_COLOR || PS_ROV_DEPTH
|
||||
bool rov_discard = gl_HelperInvocation;
|
||||
#endif
|
||||
|
||||
#if PS_ZTST == ZTST_GEQUAL
|
||||
if (input_z < sample_from_depth().r)
|
||||
discard;
|
||||
if (input_z < sample_from_depth())
|
||||
DISCARD;
|
||||
#elif PS_ZTST == ZTST_GREATER
|
||||
if (input_z <= sample_from_depth().r)
|
||||
discard;
|
||||
if (input_z <= sample_from_depth())
|
||||
DISCARD;
|
||||
#endif
|
||||
|
||||
#if PS_SCANMSK & 2
|
||||
// fail depth test on prohibited lines
|
||||
if ((int(gl_FragCoord.y) & 1) == (PS_SCANMSK & 1))
|
||||
discard;
|
||||
DISCARD;
|
||||
#endif
|
||||
#if PS_DATE >= 5
|
||||
|
||||
@@ -1726,7 +1786,7 @@ void main()
|
||||
#endif
|
||||
|
||||
if (bad) {
|
||||
discard;
|
||||
DISCARD;
|
||||
}
|
||||
|
||||
#endif // PS_DATE >= 5
|
||||
@@ -1737,7 +1797,7 @@ void main()
|
||||
// the bad alpha value so we must keep it.
|
||||
|
||||
if (gl_PrimitiveID > stencil_ceil) {
|
||||
discard;
|
||||
DISCARD;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1758,9 +1818,10 @@ void main()
|
||||
|
||||
bool atst_pass = atst(C);
|
||||
|
||||
#if PS_AFAIL == AFAIL_KEEP
|
||||
if (!atst_pass)
|
||||
discard;
|
||||
#if PS_ATST != PS_ATST_NONE && PS_AFAIL == AFAIL_KEEP
|
||||
if (!atst_pass) {
|
||||
DISCARD;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if SW_AD_TO_HW
|
||||
@@ -1852,6 +1913,7 @@ void main()
|
||||
alpha_blend.a = float(atst_pass);
|
||||
#endif
|
||||
|
||||
// Output color scaling
|
||||
#if !PS_NO_COLOR
|
||||
#if PS_RTA_CORRECTION
|
||||
o_col0.a = C.a / 128.0f;
|
||||
@@ -1870,7 +1932,7 @@ void main()
|
||||
// Alpha test with feedback
|
||||
#if PS_AFAIL == AFAIL_FB_ONLY
|
||||
if (!atst_pass)
|
||||
input_z = sample_from_depth().r;
|
||||
input_z = sample_from_depth();
|
||||
#elif PS_AFAIL == AFAIL_ZB_ONLY
|
||||
if (!atst_pass)
|
||||
o_col0 = sample_from_rt();
|
||||
@@ -1879,7 +1941,7 @@ void main()
|
||||
{
|
||||
o_col0.a = sample_from_rt().a;
|
||||
#if PS_AFAIL == AFAIL_RGB_ONLY_SW_Z
|
||||
input_z = sample_from_depth().r;
|
||||
input_z = sample_from_depth();
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
@@ -1891,11 +1953,28 @@ void main()
|
||||
|
||||
#if PS_AA1 == PS_AA1_TRIANGLE_SW_Z
|
||||
if (!bool(vsIn.interior))
|
||||
input_z = sample_from_depth().r; // No depth update for triangle edges.
|
||||
input_z = sample_from_depth(); // No depth update for triangle edges.
|
||||
#endif
|
||||
|
||||
#if ZWRITE
|
||||
// Writing back color (result already written to o_col0 for non-ROV)
|
||||
#if PS_RETURN_COLOR_ROV
|
||||
bvec4 discard_channels = bvec4(uvec4(rov_discard) | uvec4(equal(FbMask, uvec4(0xFFu))));
|
||||
o_col0 = mix(o_col0, sample_from_rt(), discard_channels);
|
||||
|
||||
imageStore(RtImageRov, ivec2(gl_FragCoord.xy), o_col0);
|
||||
#endif
|
||||
|
||||
// Writing back depth
|
||||
#if PS_RETURN_DEPTH
|
||||
gl_FragDepth = input_z;
|
||||
#elif PS_RETURN_DEPTH_ROV
|
||||
input_z = rov_discard ? sample_from_depth() : input_z;
|
||||
|
||||
imageStore(DepthImageRov, ivec2(gl_FragCoord.xy), vec4(input_z, 0, 0, 1.0f));
|
||||
#endif
|
||||
|
||||
#if PS_ROV_COLOR || PS_ROV_DEPTH
|
||||
endInvocationInterlockARB();
|
||||
#endif
|
||||
#endif // PS_DATE
|
||||
}
|
||||
|
||||
@@ -300,8 +300,8 @@ else()
|
||||
if(CMAKE_INTERPROCEDURAL_OPTIMIZATION)
|
||||
message(WARNING
|
||||
"The CMAKE_INTERPROCEDURAL_OPTIMIZATION option is enabled but the "
|
||||
"CMAKE_POSITION_INDEPENDENT_CODE option is disabled. This has been "
|
||||
"found to result in broken builds on certain platforms.")
|
||||
"POSITION_INDEPENDENT_CODE option is disabled. This has been found "
|
||||
"to result in broken builds on certain platforms.")
|
||||
endif()
|
||||
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE OFF)
|
||||
|
||||
@@ -91,6 +91,9 @@ static double s_last_barriers = 0;
|
||||
static double s_last_copies = 0;
|
||||
static double s_last_uploads = 0;
|
||||
static double s_last_readbacks = 0;
|
||||
static double s_last_depth_copies_rov = 0;
|
||||
static double s_last_draws_rov = 0;
|
||||
static double s_last_barriers_rov = 0;
|
||||
static u64 s_total_internal_draws = 0;
|
||||
static u64 s_total_draws = 0;
|
||||
static u64 s_total_render_passes = 0;
|
||||
@@ -98,6 +101,9 @@ static u64 s_total_barriers = 0;
|
||||
static u64 s_total_copies = 0;
|
||||
static u64 s_total_uploads = 0;
|
||||
static u64 s_total_readbacks = 0;
|
||||
static u64 s_total_depth_copies_rov = 0;
|
||||
static u64 s_total_draws_rov = 0;
|
||||
static u64 s_total_barriers_rov = 0;
|
||||
static u32 s_total_frames = 0;
|
||||
static u32 s_total_drawn_frames = 0;
|
||||
|
||||
@@ -287,6 +293,9 @@ void Host::BeginPresentFrame()
|
||||
update_stat(GSPerfMon::TextureCopies, s_total_copies, s_last_copies);
|
||||
update_stat(GSPerfMon::TextureUploads, s_total_uploads, s_last_uploads);
|
||||
update_stat(GSPerfMon::Readbacks, s_total_readbacks, s_last_readbacks);
|
||||
update_stat(GSPerfMon::DepthCopiesROV, s_total_depth_copies_rov, s_last_depth_copies_rov);
|
||||
update_stat(GSPerfMon::DrawCallsROV, s_total_draws_rov, s_last_draws_rov);
|
||||
update_stat(GSPerfMon::BarriersROV, s_total_barriers_rov, s_last_barriers_rov);
|
||||
|
||||
const bool idle_frame = s_total_frames && (last_draws == s_total_internal_draws && last_uploads == s_total_uploads);
|
||||
|
||||
@@ -913,6 +922,9 @@ void GSRunner::DumpStats()
|
||||
Console.WriteLn(fmt::format("@HWSTAT@ Copies: {} (avg {})", s_total_copies, static_cast<u64>(std::ceil(s_total_copies / static_cast<double>(s_total_drawn_frames)))));
|
||||
Console.WriteLn(fmt::format("@HWSTAT@ Uploads: {} (avg {})", s_total_uploads, static_cast<u64>(std::ceil(s_total_uploads / static_cast<double>(s_total_drawn_frames)))));
|
||||
Console.WriteLn(fmt::format("@HWSTAT@ Readbacks: {} (avg {})", s_total_readbacks, static_cast<u64>(std::ceil(s_total_readbacks / static_cast<double>(s_total_drawn_frames)))));
|
||||
Console.WriteLn(fmt::format("@HWSTAT@ Depth Copies (ROV): {} (avg {})", s_total_depth_copies_rov, static_cast<u64>(std::ceil(s_total_depth_copies_rov / static_cast<double>(s_total_drawn_frames)))));
|
||||
Console.WriteLn(fmt::format("@HWSTAT@ Draws Calls (ROV): {} (avg {})", s_total_draws_rov, static_cast<u64>(std::ceil(s_total_draws_rov / static_cast<double>(s_total_drawn_frames)))));
|
||||
Console.WriteLn(fmt::format("@HWSTAT@ Barriers (ROV): {} (avg {})", s_total_barriers_rov, static_cast<u64>(std::ceil(s_total_barriers_rov / static_cast<double>(s_total_drawn_frames)))));
|
||||
if (s_perf_enable)
|
||||
{
|
||||
Console.WriteLn(fmt::format("@HWSTAT@ Minimum Frame Time: {:.3f} ms ({:.3f} FPS)", PerformanceMetrics::GetMinimumFrameTime(), 1000.0f / PerformanceMetrics::GetMinimumFrameTime()));
|
||||
|
||||
+1
-1
@@ -180,7 +180,7 @@ namespace QtUtils
|
||||
}
|
||||
|
||||
wi.surface_refresh_rate = surface_refresh_rate.value();
|
||||
INFO_LOG("Surface refresh rate: {} hz", wi.surface_refresh_rate);
|
||||
INFO_LOG("Surface refresh rate: {} Hz", wi.surface_refresh_rate);
|
||||
|
||||
return wi;
|
||||
}
|
||||
|
||||
@@ -43,6 +43,11 @@
|
||||
<string>Accurate (Recommended)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Accurate Force Full (Can Reduce Readbacks)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Disable Readbacks (Synchronize GS Thread)</string>
|
||||
@@ -91,27 +96,6 @@
|
||||
</item>
|
||||
<item row="10" column="0" colspan="2">
|
||||
<layout class="QGridLayout" name="advancedLayout">
|
||||
<item row="1" column="1">
|
||||
<widget class="QCheckBox" name="extendedUpscales">
|
||||
<property name="text">
|
||||
<string>Extended Upscaling Multipliers</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QCheckBox" name="disableMailboxPresentation">
|
||||
<property name="text">
|
||||
<string extracomment="Mailbox Presentation: a type of graphics-rendering technique that has not been exposed to the public that often, so chances are you will need to keep the word mailbox in English. It does not have anything to do with postal mailboxes or email inboxes/outboxes.">Disable Mailbox Presentation</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QCheckBox" name="useBlitSwapChain">
|
||||
<property name="text">
|
||||
<string extracomment="Blit = a data operation. You might want to write it as-is, but fully uppercased. More information: https://en.wikipedia.org/wiki/Bit_blit \nSwap chain: see Microsoft's Terminology Portal.">Use Blit Swap Chain</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QCheckBox" name="spinCPUDuringReadbacks">
|
||||
<property name="text">
|
||||
@@ -119,13 +103,48 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<item row="2" column="0">
|
||||
<widget class="QCheckBox" name="spinGPUDuringReadbacks">
|
||||
<property name="text">
|
||||
<string>Spin GPU During Readbacks</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QCheckBox" name="useBlitSwapChain">
|
||||
<property name="text">
|
||||
<string extracomment="Blit = a data operation. You might want to write it as-is, but fully uppercased. More information: https://en.wikipedia.org/wiki/Bit_blit \nSwap chain: see Microsoft's Terminology Portal.">Use Blit Swap Chain</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QCheckBox" name="rov">
|
||||
<property name="text">
|
||||
<string>Rasterizer Ordered View</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QCheckBox" name="disableMailboxPresentation">
|
||||
<property name="text">
|
||||
<string extracomment="Mailbox Presentation: a type of graphics-rendering technique that has not been exposed to the public that often, so chances are you will need to keep the word mailbox in English. It does not have anything to do with postal mailboxes or email inboxes/outboxes.">Disable Mailbox Presentation</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QCheckBox" name="extendedUpscales">
|
||||
<property name="text">
|
||||
<string>Extended Upscaling Multipliers</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QCheckBox" name="rovBarriersVK">
|
||||
<property name="text">
|
||||
<string>ROV Barriers Vulkan</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
@@ -353,19 +372,21 @@
|
||||
<tabstop>gsDumpCompression</tabstop>
|
||||
<tabstop>texturePreloading</tabstop>
|
||||
<tabstop>exclusiveFullscreenControl</tabstop>
|
||||
<tabstop>useBlitSwapChain</tabstop>
|
||||
<tabstop>rov</tabstop>
|
||||
<tabstop>extendedUpscales</tabstop>
|
||||
<tabstop>disableMailboxPresentation</tabstop>
|
||||
<tabstop>spinCPUDuringReadbacks</tabstop>
|
||||
<tabstop>spinGPUDuringReadbacks</tabstop>
|
||||
<tabstop>spinCPUDuringReadbacks</tabstop>
|
||||
<tabstop>useBlitSwapChain</tabstop>
|
||||
<tabstop>disableMailboxPresentation</tabstop>
|
||||
<tabstop>rovBarriersVK</tabstop>
|
||||
<tabstop>ntscFrameRate</tabstop>
|
||||
<tabstop>palFrameRate</tabstop>
|
||||
<tabstop>overrideTextureBarriers</tabstop>
|
||||
<tabstop>useDebugDevice</tabstop>
|
||||
<tabstop>useDebugBlend</tabstop>
|
||||
<tabstop>disableFramebufferFetch</tabstop>
|
||||
<tabstop>disableShaderCache</tabstop>
|
||||
<tabstop>disableVertexShaderExpand</tabstop>
|
||||
<tabstop>useDebugBlend</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections/>
|
||||
|
||||
@@ -223,6 +223,7 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsWindow* settings_dialog,
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Advanced Settings
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_advanced.rov, "EmuCore/GS", "HWROV", true);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_advanced.useBlitSwapChain, "EmuCore/GS", "UseBlitSwapChain", false);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_advanced.useDebugDevice, "EmuCore/GS", "UseDebugDevice", false);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_advanced.useDebugBlend, "EmuCore/GS", "UseDebugBlend", false);
|
||||
@@ -239,6 +240,7 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsWindow* settings_dialog,
|
||||
SettingWidgetBinder::BindWidgetToFloatSetting(sif, m_advanced.palFrameRate, "EmuCore/GS", "FrameRatePAL", 50.00f);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_advanced.spinCPUDuringReadbacks, "EmuCore/GS", "HWSpinCPUForReadbacks", false);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_advanced.spinGPUDuringReadbacks, "EmuCore/GS", "HWSpinGPUForReadbacks", false);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_advanced.rovBarriersVK, "EmuCore/GS", "HWROVBarriersVK", false);
|
||||
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_advanced.texturePreloading, "EmuCore/GS", "texture_preloading", static_cast<int>(TexturePreloadingLevel::Off));
|
||||
|
||||
setTabVisible(m_advanced_tab, QtHost::ShouldShowAdvancedSettings());
|
||||
@@ -757,6 +759,12 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsWindow* settings_dialog,
|
||||
tr("Overrides the driver's heuristics for enabling exclusive fullscreen, or direct flip/scanout.<br>"
|
||||
"Disallowing exclusive fullscreen may enable smoother task switching and overlays, but increase input latency."));
|
||||
|
||||
dialog()->registerWidgetHelp(
|
||||
m_advanced.rov, tr("Rasterizer Ordered View"), tr("Checked"), tr("Enables Rasterizer Ordered View (ROV), which allows feedback loops to be executed with fewer draw calls. Can improve performance in feedback heavy games with higher accuracy settings."));
|
||||
|
||||
dialog()->registerWidgetHelp(m_advanced.rovBarriersVK, tr("ROV Barriers Vulkan"), tr("Unchecked"),
|
||||
tr("Forces extra barriers when using ROV with Vulkan to fix graphical issues present in some games and hardware configurations."));
|
||||
|
||||
dialog()->registerWidgetHelp(m_advanced.disableMailboxPresentation, tr("Disable Mailbox Presentation"), tr("Unchecked"),
|
||||
tr("Forces the use of FIFO over Mailbox presentation, i.e. double buffering instead of triple buffering. "
|
||||
"Usually results in worse frame pacing."));
|
||||
@@ -1002,6 +1010,7 @@ void GraphicsSettingsWidget::updateRendererDependentOptions()
|
||||
const bool is_software = (type == GSRendererType::SW);
|
||||
const bool is_auto = (type == GSRendererType::Auto);
|
||||
const bool is_vk = (type == GSRendererType::VK);
|
||||
const bool is_ogl = (type == GSRendererType::OGL);
|
||||
const bool is_disable_barriers = (type == GSRendererType::Metal || type == GSRendererType::SW);
|
||||
const bool hw_fixes = (is_hardware && m_hw.enableHWFixes && m_hw.enableHWFixes->checkState() == Qt::Checked);
|
||||
|
||||
@@ -1044,6 +1053,9 @@ void GraphicsSettingsWidget::updateRendererDependentOptions()
|
||||
if (m_advanced.exclusiveFullscreenControl)
|
||||
m_advanced.exclusiveFullscreenControl->setEnabled(is_auto || is_vk);
|
||||
|
||||
if (m_advanced.rov)
|
||||
m_advanced.rov->setDisabled(!is_hardware || is_ogl);
|
||||
|
||||
// populate adapters
|
||||
std::vector<GSAdapterInfo> adapters = GSGetAdapterInfo(type);
|
||||
const GSAdapterInfo* current_adapter_info = nullptr;
|
||||
|
||||
+1393
-1341
File diff suppressed because it is too large
Load Diff
+2
-2
@@ -6,8 +6,8 @@
|
||||
#include "common/Pcsx2Defs.h"
|
||||
|
||||
static const u32 BIAS = 2; // Bus is half of the actual ps2 speed
|
||||
static const u32 PS2CLK = 294912000; //hz /* 294.912 mhz */
|
||||
extern u32 PSXCLK; /* 36.864 Mhz */
|
||||
static const u32 PS2CLK = 294912000; //Hz /* 294.912 MHz */
|
||||
extern u32 PSXCLK; /* 36.864 MHz */
|
||||
|
||||
|
||||
#include "Memory.h"
|
||||
|
||||
@@ -399,6 +399,7 @@ enum class SavestateCompressionLevel : u8
|
||||
enum class GSHardwareDownloadMode : u8
|
||||
{
|
||||
Enabled,
|
||||
EnabledForceFull,
|
||||
NoReadbacks,
|
||||
Unsynchronized,
|
||||
Disabled
|
||||
@@ -793,6 +794,9 @@ struct Pcsx2Config
|
||||
HWMipmap : 1,
|
||||
HWAccurateAlphaTest : 1,
|
||||
HWAA1 : 1,
|
||||
HWROV : 1,
|
||||
HWROVLogging : 1,
|
||||
HWROVBarriersVK : 1,
|
||||
ManualUserHacks : 1,
|
||||
UserHacks_AlignSpriteX : 1,
|
||||
UserHacks_CPUFBConversion : 1,
|
||||
|
||||
+30
-10
@@ -701,16 +701,36 @@ void GSgetStats(SmallStringBase& info)
|
||||
}
|
||||
else
|
||||
{
|
||||
info.format("{} HW | {} PRIM | {} DRW | {} DRWC | {} BAR | {} RP | {} RB | {} TC | {} TU",
|
||||
api_name,
|
||||
(int)pm.Get(GSPerfMon::Prim),
|
||||
(int)pm.Get(GSPerfMon::Draw),
|
||||
(int)std::ceil(pm.Get(GSPerfMon::DrawCalls)),
|
||||
(int)std::ceil(pm.Get(GSPerfMon::Barriers)),
|
||||
(int)std::ceil(pm.Get(GSPerfMon::RenderPasses)),
|
||||
(int)std::ceil(pm.Get(GSPerfMon::Readbacks)),
|
||||
(int)std::ceil(pm.Get(GSPerfMon::TextureCopies)),
|
||||
(int)std::ceil(pm.Get(GSPerfMon::TextureUploads)));
|
||||
if (!GSConfig.HWROV)
|
||||
{
|
||||
info.format("{} HW | {} PRIM | {} DRW | {} DRWC | {} BAR | {} RP | {} RB | {} TC | {} TU",
|
||||
api_name,
|
||||
(int)pm.Get(GSPerfMon::Prim),
|
||||
(int)pm.Get(GSPerfMon::Draw),
|
||||
(int)std::ceil(pm.Get(GSPerfMon::DrawCalls)),
|
||||
(int)std::ceil(pm.Get(GSPerfMon::Barriers)),
|
||||
(int)std::ceil(pm.Get(GSPerfMon::RenderPasses)),
|
||||
(int)std::ceil(pm.Get(GSPerfMon::Readbacks)),
|
||||
(int)std::ceil(pm.Get(GSPerfMon::TextureCopies)),
|
||||
(int)std::ceil(pm.Get(GSPerfMon::TextureUploads)));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Add ROV stats along standard stats.
|
||||
info.format("{} HW | {} PRIM | {} DRW | {}/{} DRWC | {}/{} BAR | {} RP | {} RB | {}/{} TC | {} TU",
|
||||
api_name,
|
||||
(int)pm.Get(GSPerfMon::Prim),
|
||||
(int)pm.Get(GSPerfMon::Draw),
|
||||
(int)std::ceil(pm.Get(GSPerfMon::DrawCalls)),
|
||||
(int)std::ceil(pm.Get(GSPerfMon::DrawCallsROV)),
|
||||
(int)std::ceil(pm.Get(GSPerfMon::Barriers)),
|
||||
(int)std::ceil(pm.Get(GSPerfMon::BarriersROV)),
|
||||
(int)std::ceil(pm.Get(GSPerfMon::RenderPasses)),
|
||||
(int)std::ceil(pm.Get(GSPerfMon::Readbacks)),
|
||||
(int)std::ceil(pm.Get(GSPerfMon::TextureCopies)),
|
||||
(int)std::ceil(pm.Get(GSPerfMon::DepthCopiesROV)),
|
||||
(int)std::ceil(pm.Get(GSPerfMon::TextureUploads)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -46,10 +46,15 @@
|
||||
#define GL_POP() g_gs_device->PopDebugGroup()
|
||||
#define GL_INS(...) g_gs_device->InsertDebugMessage(GSDevice::DebugMessageCategory::Message, __VA_ARGS__)
|
||||
#define GL_PERF(...) g_gs_device->InsertDebugMessage(GSDevice::DebugMessageCategory::Performance, __VA_ARGS__)
|
||||
#define GL_ROV(...) \
|
||||
if (GSConfig.HWROVLogging) \
|
||||
Console.Warning(__VA_ARGS__); \
|
||||
g_gs_device->InsertDebugMessage(GSDevice::DebugMessageCategory::Message, __VA_ARGS__);
|
||||
#else
|
||||
#define GL_PUSH_(...) (void)(0)
|
||||
#define GL_PUSH(...) (void)(0)
|
||||
#define GL_POP() (void)(0)
|
||||
#define GL_INS(...) (void)(0)
|
||||
#define GL_PERF(...) (void)(0)
|
||||
#define GL_ROV(...) (void)(0)
|
||||
#endif
|
||||
|
||||
@@ -23,6 +23,9 @@ public:
|
||||
SyncPoint,
|
||||
Barriers,
|
||||
RenderPasses,
|
||||
DepthCopiesROV, // Overlaps with regular texture copies.
|
||||
DrawCallsROV, // Overlaps with regular draw calls.
|
||||
BarriersROV, // Overlaps with regular barriers.
|
||||
CounterLast,
|
||||
|
||||
// Reused counters for HW.
|
||||
|
||||
+2
-2
@@ -203,8 +203,8 @@ enum GS_AFAIL
|
||||
|
||||
enum GS_ALPHA_BITS
|
||||
{
|
||||
ALPHA_ABC_CS = 0,
|
||||
ALPHA_ABC_CD = 1,
|
||||
ALPHA_ABD_CS = 0,
|
||||
ALPHA_ABD_CD = 1,
|
||||
ALPHA_C_AS = 0,
|
||||
ALPHA_C_AD = 1,
|
||||
ALPHA_C_FIX = 2,
|
||||
|
||||
+65
-32
@@ -355,11 +355,15 @@ void GSState::ResetDrawBufferIdx()
|
||||
m_vertex = &m_vertex_buffers[m_current_buffer_idx];
|
||||
|
||||
if (m_index->tail == 0)
|
||||
{
|
||||
m_backed_up_ctx = -1;
|
||||
|
||||
m_dirty_gs_regs = 0;
|
||||
|
||||
//DevCon.Warning("New round of draws buffer %d vertex tail %d index tail %d TME %d TBP0 0x%x draw %d", m_current_buffer_idx, m_vertex->tail, m_index->tail, m_env.PRIM.TME, m_env.CTXT[m_env.PRIM.CTXT].TEX0.TBP0, s_n);
|
||||
m_dirty_gs_regs = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_dirty_gs_regs = m_env_buffers[m_current_buffer_idx].m_dirty_regs;
|
||||
temp_draw_rect = m_env_buffers[m_current_buffer_idx].draw_rect;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -398,8 +402,7 @@ void GSState::FlushBuffers(bool flush_base_only, bool use_flush_reason, GSFlushR
|
||||
else if (m_index_buffers[0].tail == 0)
|
||||
return;
|
||||
|
||||
int max_flushes = flush_base_only ? 1 : m_used_buffers_idx;
|
||||
//DevCon.Warning("Flushing %d draw buffers from draw %d", m_used_buffers_idx, s_n);
|
||||
const int max_flushes = flush_base_only ? 1 : m_used_buffers_idx;
|
||||
for (int i = 0; i < max_flushes; i++)
|
||||
{
|
||||
m_current_buffer_idx = i;
|
||||
@@ -431,7 +434,7 @@ void GSState::FlushBuffers(bool flush_base_only, bool use_flush_reason, GSFlushR
|
||||
}
|
||||
else if (restore_env)
|
||||
memcpy(&m_env, &m_temp_env, sizeof(m_env));
|
||||
//DevCon.Warning("Flushing position %d ABE is %d TME %d TEX0 TBP %x", i, m_prev_env.PRIM.ABE, m_prev_env.PRIM.TME, m_prev_env.CTXT[m_prev_env.PRIM.CTXT].TEX0.TBP0);
|
||||
|
||||
if (use_flush_reason && (i == current_idx || flush_reason == VSYNC))
|
||||
FlushDraw(flush_reason);
|
||||
else
|
||||
@@ -443,7 +446,6 @@ void GSState::FlushBuffers(bool flush_base_only, bool use_flush_reason, GSFlushR
|
||||
m_current_buffer_idx = current_idx;
|
||||
m_index = &m_index_buffers[m_current_buffer_idx];
|
||||
m_vertex = &m_vertex_buffers[m_current_buffer_idx];
|
||||
m_dirty_gs_regs = 0;
|
||||
|
||||
const int ctx = m_env_buffers[m_current_buffer_idx].m_backed_up_ctx;
|
||||
std::memcpy(&m_prev_env, &m_env_buffers[m_current_buffer_idx].m_env, 88);
|
||||
@@ -451,8 +453,6 @@ void GSState::FlushBuffers(bool flush_base_only, bool use_flush_reason, GSFlushR
|
||||
std::memcpy(&m_prev_env.CTXT[1], &m_env_buffers[m_current_buffer_idx].m_env.CTXT[1], 96);
|
||||
std::memcpy(&m_prev_env.CTXT[ctx].offset, &m_env_buffers[m_current_buffer_idx].m_env.CTXT[ctx].offset, sizeof(m_env_buffers[m_current_buffer_idx].m_env.CTXT[ctx].offset));
|
||||
std::memcpy(&m_prev_env.CTXT[ctx].scissor, &m_env_buffers[m_current_buffer_idx].m_env.CTXT[ctx].scissor, sizeof(m_env_buffers[m_current_buffer_idx].m_env.CTXT[ctx].scissor));
|
||||
//DevCon.Warning("Flush complete, draw now %d", s_n);
|
||||
//UpdateContext();
|
||||
}
|
||||
|
||||
void GSState::PushBuffer()
|
||||
@@ -460,7 +460,6 @@ void GSState::PushBuffer()
|
||||
// Just in case it tries to overflow.
|
||||
if (m_used_buffers_idx >= MAX_DRAW_BUFFERS)
|
||||
{
|
||||
//DevCon.Warning("Attempted to add a draw to buffer when full. Flushing");
|
||||
FlushBuffers(false, false);
|
||||
ResetDrawBufferIdx();
|
||||
return;
|
||||
@@ -502,7 +501,6 @@ void GSState::PushBuffer()
|
||||
m_dirty_gs_regs = 0;
|
||||
m_used_buffers_idx++;
|
||||
m_recent_buffer_switch = true;
|
||||
//DevCon.Warning("Pushing new buffer %d vertex tail %d index tail %d TME %d TBP0 0x%x draw %d", m_current_buffer_idx, m_vertex->tail, m_index->tail, m_env.PRIM.TME, m_env.CTXT[m_env.PRIM.CTXT].TEX0.TBP0, s_n);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -516,10 +514,11 @@ bool GSState::CanBufferNewDraw()
|
||||
|
||||
// If the base draw isn't writing to the Z buffer, but following draws do, we can't use it.
|
||||
// Also the base draw needs to be solid, not an alpha blend.
|
||||
if (base_context.ZBUF.ZMSK || cur_context.FRAME.FBP != base_context.FRAME.FBP || cur_context.ZBUF.ZBP != base_context.ZBUF.ZBP || (m_env_buffers[0].m_env.PRIM.TME && base_context.TEX0.TFX > TFX_DECAL) ||
|
||||
if (base_context.ZBUF.ZMSK || cur_context.FRAME.FBP != base_context.FRAME.FBP || cur_context.ZBUF.ZBP != base_context.ZBUF.ZBP || (m_env_buffers[0].m_env.PRIM.TME &&
|
||||
(base_context.TEX0.TFX > TFX_DECAL || (m_env_buffers[0].m_env.PRIM.ABE && !base_context.TEX0.TCC && m_v.RGBAQ.A != 128))) ||
|
||||
((base_context.TEST.ATE && base_context.TEST.ATST > ATST_ALWAYS && base_context.TEST.AREF != 0) && (base_context.TEST.AFAIL & AFAIL_FB_ONLY) == AFAIL_KEEP))
|
||||
{
|
||||
//DevCon.Warning("Flushing, cannot buffer draw due to incompatible base");
|
||||
// Incompatible base.
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -538,6 +537,8 @@ bool GSState::CanBufferNewDraw()
|
||||
if (!std::memcmp(&m_env_buffers[i].m_env, &m_env, 88))
|
||||
{
|
||||
GSDrawingEnvironment& buffered_ctx = m_env_buffers[i].m_env;
|
||||
if (m_index_buffers[i].tail > m_index_buffers[0].tail)
|
||||
return false;
|
||||
|
||||
if (buffered_ctx.CTXT[ctx].SCISSOR.U64 ^ cur_context.SCISSOR.U64)
|
||||
continue;
|
||||
@@ -561,6 +562,9 @@ bool GSState::CanBufferNewDraw()
|
||||
continue;
|
||||
if (buffered_ctx.CTXT[ctx].TEX1.U32[0] ^ cur_context.TEX1.U32[0])
|
||||
continue;
|
||||
if (buffered_ctx.CTXT[ctx].TEX1.MXL != cur_context.TEX1.MXL)
|
||||
continue;
|
||||
|
||||
if (cur_context.TEX1.MXL)
|
||||
{
|
||||
if (buffered_ctx.CTXT[ctx].TEX1.U32[1] ^ cur_context.TEX1.U32[1])
|
||||
@@ -596,27 +600,32 @@ bool GSState::CanBufferNewDraw()
|
||||
if (i == 1 && !m_env_buffers[i].draw_rect.eq(m_env_buffers[0].draw_rect))
|
||||
{
|
||||
FlushWrite();
|
||||
|
||||
FlushBuffers(true, false);
|
||||
const bool base_only = m_env_buffers[i].draw_rect.rintersect(m_env_buffers[0].draw_rect).rempty();
|
||||
FlushBuffers(base_only, false);
|
||||
ResetDrawBufferIdx();
|
||||
i = -1;
|
||||
continue;
|
||||
if (!base_only)
|
||||
break;
|
||||
else
|
||||
{
|
||||
i = -1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
/*if (i != (m_current_buffer_idx + 1) && i != 0)
|
||||
return false;*/
|
||||
// We found a matching draw
|
||||
//DevCon.Warning("Matching buffered draw detected in index %d, using", i);
|
||||
m_index = &m_index_buffers[i];
|
||||
m_vertex = &m_vertex_buffers[i];
|
||||
|
||||
const u32 copy_amt = m_vertex_buffers[m_current_buffer_idx].tail - m_vertex_buffers[m_current_buffer_idx].head;
|
||||
|
||||
m_recent_buffer_switch = m_vertex->tail == m_vertex->head;
|
||||
m_vertex->tail = m_index->buff[m_index->tail - 1] + 1;
|
||||
if (m_index->tail)
|
||||
m_vertex->tail = m_index->buff[m_index->tail - 1] + 1;
|
||||
else
|
||||
m_vertex->tail = 0;
|
||||
|
||||
if (copy_amt)
|
||||
memcpy(&m_vertex->buff[m_vertex->tail], &m_vertex_buffers[m_current_buffer_idx].buff[m_vertex_buffers[m_current_buffer_idx].head], sizeof(GSVertex) * copy_amt);
|
||||
@@ -654,7 +663,7 @@ bool GSState::CanBufferNewDraw()
|
||||
}
|
||||
|
||||
m_dirty_gs_regs = 0;
|
||||
//DevCon.Warning("Picking buffer %d vertex tail %d index tail %d TME %d TBP0 0x%x dirty %x draw %d", m_current_buffer_idx, m_vertex->tail, m_index->tail, m_env.PRIM.TME, m_env.CTXT[m_env.PRIM.CTXT].TEX0.TBP0, m_dirty_gs_regs, s_n);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -679,7 +688,6 @@ bool GSState::CanBufferNewDraw()
|
||||
return false;
|
||||
|
||||
PushBuffer();
|
||||
//DevCon.Warning("Buffering new draw! now buffering %d", m_used_buffers_idx);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -4226,7 +4234,7 @@ bool GSState::TrianglesAreQuads(bool shuffle_check)
|
||||
}
|
||||
|
||||
template<u32 primclass>
|
||||
GSState::PRIM_OVERLAP GSState::GetPrimitiveOverlapDrawlistImpl(bool save_drawlist, bool save_bbox, float bbox_scale)
|
||||
GSState::PRIM_OVERLAP GSState::GetPrimitiveOverlapDrawlistImpl(bool save_drawlist, bool save_bbox, float bbox_scale, u32* max_size)
|
||||
{
|
||||
const GSVector4i xyof = m_context->scissor.xyof.xyxy();
|
||||
|
||||
@@ -4274,7 +4282,14 @@ GSState::PRIM_OVERLAP GSState::GetPrimitiveOverlapDrawlistImpl(bool save_drawlis
|
||||
if (primclass == GS_TRIANGLE_CLASS && m_quad_check_valid && m_are_quads && !using_aa1)
|
||||
{
|
||||
// The triangles-are-quads check already ensures that there is no overlap.
|
||||
m_drawlist.push_back(m_index->tail / n);
|
||||
if (save_drawlist)
|
||||
{
|
||||
m_drawlist.push_back(m_index->tail / n);
|
||||
}
|
||||
else if (max_size)
|
||||
{
|
||||
*max_size = 1;
|
||||
}
|
||||
if (save_bbox)
|
||||
{
|
||||
const GSVector4i draw_area = GSVector4i(m_vt.m_min.p.upld(m_vt.m_max.p) * GSVector4(16.0f)) + xyof;
|
||||
@@ -4286,6 +4301,7 @@ GSState::PRIM_OVERLAP GSState::GetPrimitiveOverlapDrawlistImpl(bool save_drawlis
|
||||
PRIM_OVERLAP overlap = PRIM_OVERLAP_NO;
|
||||
bool check_quads = (primclass == GS_TRIANGLE_CLASS) && !using_aa1;
|
||||
|
||||
u32 drawlist_size = 0;
|
||||
u32 i = 0;
|
||||
u32 skip = 0; // Number of indices to skip if we have the bbox from the previous iteration.
|
||||
|
||||
@@ -4683,6 +4699,17 @@ GSState::PRIM_OVERLAP GSState::GetPrimitiveOverlapDrawlistImpl(bool save_drawlis
|
||||
{
|
||||
m_drawlist.push_back((j - i) / n); // Prim count
|
||||
}
|
||||
else if (max_size)
|
||||
{
|
||||
// If the max size pointer is passed it means we just want to peek at
|
||||
// the drawlist size up to the given limit to avoid unecessary work.
|
||||
drawlist_size++;
|
||||
if (drawlist_size >= *max_size)
|
||||
{
|
||||
*max_size = drawlist_size;
|
||||
return drawlist_size > 0 ? PRIM_OVERLAP_YES : PRIM_OVERLAP_UNKNOW;
|
||||
}
|
||||
}
|
||||
else if (j < count)
|
||||
{
|
||||
return PRIM_OVERLAP_YES; // Early exit if not saving drawlist.
|
||||
@@ -4696,21 +4723,27 @@ GSState::PRIM_OVERLAP GSState::GetPrimitiveOverlapDrawlistImpl(bool save_drawlis
|
||||
all = bbox;
|
||||
i = j;
|
||||
}
|
||||
|
||||
if (max_size)
|
||||
{
|
||||
*max_size = drawlist_size;
|
||||
}
|
||||
|
||||
return overlap;
|
||||
}
|
||||
|
||||
GSState::PRIM_OVERLAP GSState::GetPrimitiveOverlapDrawlist(bool save_drawlist, bool save_bbox, float bbox_scale)
|
||||
GSState::PRIM_OVERLAP GSState::GetPrimitiveOverlapDrawlist(bool save_drawlist, bool save_bbox, float bbox_scale, u32* max_size)
|
||||
{
|
||||
switch (m_vt.m_primclass)
|
||||
{
|
||||
case GS_POINT_CLASS:
|
||||
return GetPrimitiveOverlapDrawlistImpl<GS_POINT_CLASS>(save_drawlist, save_bbox, bbox_scale);
|
||||
return GetPrimitiveOverlapDrawlistImpl<GS_POINT_CLASS>(save_drawlist, save_bbox, bbox_scale, max_size);
|
||||
case GS_LINE_CLASS:
|
||||
return GetPrimitiveOverlapDrawlistImpl<GS_LINE_CLASS>(save_drawlist, save_bbox, bbox_scale);
|
||||
return GetPrimitiveOverlapDrawlistImpl<GS_LINE_CLASS>(save_drawlist, save_bbox, bbox_scale, max_size);
|
||||
case GS_TRIANGLE_CLASS:
|
||||
return GetPrimitiveOverlapDrawlistImpl<GS_TRIANGLE_CLASS>(save_drawlist, save_bbox, bbox_scale);
|
||||
return GetPrimitiveOverlapDrawlistImpl<GS_TRIANGLE_CLASS>(save_drawlist, save_bbox, bbox_scale, max_size);
|
||||
case GS_SPRITE_CLASS:
|
||||
return GetPrimitiveOverlapDrawlistImpl<GS_SPRITE_CLASS>(save_drawlist, save_bbox, bbox_scale);
|
||||
return GetPrimitiveOverlapDrawlistImpl<GS_SPRITE_CLASS>(save_drawlist, save_bbox, bbox_scale, max_size);
|
||||
default:
|
||||
pxFail("Invalid primclass."); // Impossible.
|
||||
return PRIM_OVERLAP_UNKNOW;
|
||||
@@ -6138,7 +6171,7 @@ __forceinline void GSState::VertexKick(u32 skip)
|
||||
|
||||
// Update rectangle for the current draw. Needs exclusive endpoints.
|
||||
const GSVector4i draw_rect = bbox.sra32<4>() + GSVector4i(0, 0, 1, 1);
|
||||
if (m_vertex->tail != n)
|
||||
if (m_index->tail != n)
|
||||
temp_draw_rect = temp_draw_rect.runion(draw_rect);
|
||||
else
|
||||
temp_draw_rect = draw_rect;
|
||||
|
||||
+4
-2
@@ -533,8 +533,10 @@ public:
|
||||
bool TrianglesAreQuadsImpl();
|
||||
bool TrianglesAreQuads(bool shuffle_check = false);
|
||||
template <u32 primclass>
|
||||
PRIM_OVERLAP GetPrimitiveOverlapDrawlistImpl(bool save_drawlist = false, bool save_bbox = false, float bbox_scale = 1.0f);
|
||||
PRIM_OVERLAP GetPrimitiveOverlapDrawlist(bool save_drawlist = false, bool save_bbox = false, float bbox_scale = 1.0f);
|
||||
PRIM_OVERLAP GetPrimitiveOverlapDrawlistImpl(bool save_drawlist = false, bool save_bbox = false,
|
||||
float bbox_scale = 1.0f, u32* max_size = nullptr);
|
||||
PRIM_OVERLAP GetPrimitiveOverlapDrawlist(bool save_drawlist = false, bool save_bbox = false,
|
||||
float bbox_scale = 1.0f, u32* max_size = nullptr);
|
||||
PRIM_OVERLAP PrimitiveOverlap(bool save_drawlist = false);
|
||||
template <u32 primclass, bool fst>
|
||||
void GetPrimitiveOverlapDrawlistTextureBBoxImpl(float bbox_scale = 1.0f);
|
||||
|
||||
@@ -315,7 +315,7 @@ bool GSDevice::GetRequestedExclusiveFullscreenMode(u32* width, u32* height, floa
|
||||
|
||||
std::string GSDevice::GetFullscreenModeString(u32 width, u32 height, float refresh_rate)
|
||||
{
|
||||
return StringUtil::StdStringFromFormat("%u x %u @ %f hz", width, height, refresh_rate);
|
||||
return StringUtil::StdStringFromFormat("%u x %u @ %f Hz", width, height, refresh_rate);
|
||||
}
|
||||
|
||||
void GSDevice::GenerateExpansionIndexBuffer(void* buffer)
|
||||
@@ -693,6 +693,12 @@ void GSDevice::Recycle(GSTexture* t)
|
||||
|
||||
t->SetLastFrameUsed(m_frame);
|
||||
|
||||
t->ClearUnorderedAccess();
|
||||
|
||||
#ifdef PCSX2_DEVBUILD
|
||||
t->SetDebugName("");
|
||||
#endif
|
||||
|
||||
FastList<GSTexture*>& pool = m_pool[!t->IsTexture()];
|
||||
pool.push_front(t);
|
||||
m_pool_memory_usage += t->GetMemUsage();
|
||||
@@ -776,6 +782,18 @@ GSTexture* GSDevice::CreateDepthStencil(const GSVector2i& size, bool clear, bool
|
||||
clear, !prefer_reuse);
|
||||
}
|
||||
|
||||
GSTexture* GSDevice::CreateDepthColor(int w, int h, bool clear, bool prefer_reuse)
|
||||
{
|
||||
return FetchSurface(GSTexture::Type::RenderTarget, w, h, 1, GSTexture::Format::DepthColor,
|
||||
clear, !prefer_reuse);
|
||||
}
|
||||
|
||||
GSTexture* GSDevice::CreateDepthColor(const GSVector2i& size, bool clear, bool prefer_reuse)
|
||||
{
|
||||
return FetchSurface(GSTexture::Type::RenderTarget, size.x, size.y, 1, GSTexture::Format::DepthColor,
|
||||
clear, !prefer_reuse);
|
||||
}
|
||||
|
||||
GSTexture* GSDevice::CreateTexture(int w, int h, int mipmap_levels, GSTexture::Format format, bool prefer_reuse /* = false */)
|
||||
{
|
||||
pxAssert(mipmap_levels != 0 && (mipmap_levels < 0 || mipmap_levels <= GetMipmapLevelsForSize(w, h)));
|
||||
@@ -1486,9 +1504,9 @@ static const char* GetSetDATMName(SetDATM datm)
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
static const char* GetPSAA1Name(u32 aa1)
|
||||
static const char* GetPSAA1Name(GSHWDrawConfig::PS_AA1 aa1)
|
||||
{
|
||||
switch (static_cast<GSHWDrawConfig::PS_AA1>(aa1))
|
||||
switch (aa1)
|
||||
{
|
||||
case GSHWDrawConfig::PS_AA1::NONE: return "NONE";
|
||||
case GSHWDrawConfig::PS_AA1::LINE: return "LINE";
|
||||
@@ -1509,6 +1527,17 @@ static const char* GetTexHazardName(u32 tex_hazard)
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
static const char* GetPSROVDepthname(GSHWDrawConfig::PS_ROV_DEPTH rov_depth)
|
||||
{
|
||||
switch (rov_depth)
|
||||
{
|
||||
case GSHWDrawConfig::PS_ROV_DEPTH::NONE: return "NONE";
|
||||
case GSHWDrawConfig::PS_ROV_DEPTH::READ_ONLY: return "READ_ONLY";
|
||||
case GSHWDrawConfig::PS_ROV_DEPTH::READ_WRITE: return "READ_WRITE";
|
||||
}
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
static void DumpPSSelector(DrawConfigWriter& out, const GSHWDrawConfig::PSSelector& ps)
|
||||
{
|
||||
out.WriteLn("aem_fmt: {}", ps.aem_fmt);
|
||||
@@ -1564,9 +1593,13 @@ static void DumpPSSelector(DrawConfigWriter& out, const GSHWDrawConfig::PSSelect
|
||||
out.WriteLn("point_sampler: {}", ps.point_sampler);
|
||||
out.WriteLn("region_rect: {}", ps.region_rect);
|
||||
out.WriteLn("scanmsk: {} ({})", GSUtil::GetSCANMSKName(ps.scanmsk), ps.scanmsk);
|
||||
out.WriteLn("aa1: {} ({})", static_cast<u32>(ps.aa1), GetPSAA1Name(static_cast<u32>(ps.aa1)));
|
||||
out.WriteLn("aa1: {} ({})", GetPSAA1Name(ps.aa1), static_cast<u32>(ps.aa1));
|
||||
out.WriteLn("abe: {}", static_cast<u32>(ps.abe));
|
||||
out.WriteLn("sw_aniso: {}", ps.sw_aniso);
|
||||
out.WriteLn("rov_color: {}", ps.rov_color);
|
||||
out.WriteLn("rov_depth: {} ({})", GetPSROVDepthname(ps.rov_depth), static_cast<u32>(ps.rov_depth));
|
||||
out.WriteLn("ztst: {} ({})", GSUtil::GetZTSTName(ps.ztst), static_cast<u32>(ps.ztst));
|
||||
out.WriteLn("zfloor: {}", static_cast<u32>(ps.zfloor));
|
||||
}
|
||||
|
||||
static void DumpVSSelector(DrawConfigWriter& out, const GSHWDrawConfig::VSSelector& vs)
|
||||
@@ -1690,6 +1723,8 @@ static void DumpConfig(DrawConfigWriter& out, const GSHWDrawConfig& conf,
|
||||
out.WriteLn("topology: {} ({})", GetTopologyName(conf.topology), static_cast<u32>(conf.topology));
|
||||
out.WriteLn("require_one_barrier: {}", conf.require_one_barrier);
|
||||
out.WriteLn("require_full_barrier: {}", conf.require_full_barrier);
|
||||
DumpVector4(out, "drawarea", conf.drawarea);
|
||||
DumpVector4(out, "samplearea", conf.samplearea);
|
||||
out.WriteLn("tex_hazard: {}", GetTexHazardName(conf.tex_hazard));
|
||||
|
||||
out.WriteLn("destination_alpha: {} ({})", GetDestinationAlphaModeName(conf.destination_alpha), static_cast<u32>(conf.destination_alpha));
|
||||
|
||||
@@ -666,6 +666,7 @@ struct alignas(16) GSHWDrawConfig
|
||||
using PS_ATST = GSShader::PS_ATST;
|
||||
using PS_AFAIL = GSShader::PS_AFAIL;
|
||||
using PS_AA1 = GSShader::PS_AA1;
|
||||
using PS_ROV_DEPTH = GSShader::PS_ROV_DEPTH;
|
||||
#pragma pack(push, 1)
|
||||
struct VSSelector
|
||||
{
|
||||
@@ -786,12 +787,16 @@ struct alignas(16) GSHWDrawConfig
|
||||
|
||||
// Anisotropic filtering
|
||||
u32 sw_aniso : 5;
|
||||
|
||||
// ROVs
|
||||
u32 rov_color : 1;
|
||||
PS_ROV_DEPTH rov_depth : 2;
|
||||
};
|
||||
|
||||
struct
|
||||
{
|
||||
u64 key_lo;
|
||||
u32 key_hi;
|
||||
u64 key_hi;
|
||||
};
|
||||
};
|
||||
__fi PSSelector() : key_lo(0), key_hi(0) {}
|
||||
@@ -800,6 +805,21 @@ struct alignas(16) GSHWDrawConfig
|
||||
__fi bool operator!=(const PSSelector& rhs) const { return (key_lo != rhs.key_lo || key_hi != rhs.key_hi); }
|
||||
__fi bool operator<(const PSSelector& rhs) const { return (key_lo < rhs.key_lo || key_hi < rhs.key_hi); }
|
||||
|
||||
__fi bool IsSWBlending() const
|
||||
{
|
||||
return blend_a || blend_b || blend_d;
|
||||
}
|
||||
|
||||
__fi bool IsZTesting() const
|
||||
{
|
||||
return ztst == ZTST_GEQUAL || ztst == ZTST_GREATER;
|
||||
}
|
||||
|
||||
__fi bool IsAlphaTesting() const
|
||||
{
|
||||
return atst != PS_ATST::NONE;
|
||||
}
|
||||
|
||||
__fi bool IsFeedbackLoopRT() const
|
||||
{
|
||||
const u32 sw_blend_bits = blend_a | blend_b | blend_d;
|
||||
@@ -816,6 +836,11 @@ struct alignas(16) GSHWDrawConfig
|
||||
return afail_needs_depth || ztst_needs_depth || aa1_needs_depth;
|
||||
}
|
||||
|
||||
__fi bool HasShaderDiscard() const
|
||||
{
|
||||
return (IsAlphaTesting() && afail == PS_AFAIL::KEEP) || scanmsk || date || IsZTesting();
|
||||
}
|
||||
|
||||
/// Disables color output from the pixel shader, this is done when all channels are masked.
|
||||
__fi void DisableColorOutput()
|
||||
{
|
||||
@@ -844,9 +869,39 @@ struct alignas(16) GSHWDrawConfig
|
||||
{
|
||||
aa1 = PS_AA1::TRIANGLE;
|
||||
}
|
||||
|
||||
if (rov_depth == PS_ROV_DEPTH::READ_WRITE)
|
||||
{
|
||||
rov_depth = PS_ROV_DEPTH::READ_ONLY;
|
||||
}
|
||||
}
|
||||
|
||||
__fi bool HasColorOutput() const
|
||||
{
|
||||
return !no_color;
|
||||
}
|
||||
|
||||
__fi bool HasDepthOutput() const
|
||||
{
|
||||
return zfloor || zclamp || IsFeedbackLoopDepth() || (rov_depth == PS_ROV_DEPTH::READ_WRITE);
|
||||
}
|
||||
|
||||
__fi bool HasColorROV() const
|
||||
{
|
||||
return rov_color != 0;
|
||||
}
|
||||
|
||||
__fi bool HasDepthROV() const
|
||||
{
|
||||
return rov_depth == PS_ROV_DEPTH::READ_ONLY || rov_depth == PS_ROV_DEPTH::READ_WRITE;
|
||||
}
|
||||
|
||||
__fi bool HasDepthROVWrite() const
|
||||
{
|
||||
return rov_depth == PS_ROV_DEPTH::READ_WRITE;
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(PSSelector) == 12, "PSSelector is 12 bytes");
|
||||
static_assert(sizeof(PSSelector) == 16, "PSSelector is 12 bytes");
|
||||
#pragma pack(pop)
|
||||
struct PSSelectorHash
|
||||
{
|
||||
@@ -1255,6 +1310,11 @@ struct alignas(16) GSHWDrawConfig
|
||||
{
|
||||
return ps.IsFeedbackLoopDepth() || (tex_hazard == TEX_HAZARD_DEPTH);
|
||||
}
|
||||
|
||||
bool IsBlending()
|
||||
{
|
||||
return blend.enable || blend_multi_pass.enable || ps.IsSWBlending();
|
||||
}
|
||||
|
||||
// Dumping
|
||||
static void DumpConfig(const std::string& path, const GSHWDrawConfig& conf,
|
||||
@@ -1330,6 +1390,7 @@ public:
|
||||
bool test_and_sample_depth: 1; ///< Supports concurrently binding the depth-stencil buffer for sampling and depth testing.
|
||||
bool depth_feedback : 1; ///< Depth feedback loops can be done with DS directly (otherwise need to copy to separate RT). Implies `feedback_loops`.
|
||||
bool aa1 : 1; ///< Supports the GS AA1 feature.
|
||||
bool rov : 1; ///< Supports rasterizer ordered views for both depth and color.
|
||||
FeatureSupport()
|
||||
{
|
||||
memset(this, 0, sizeof(*this));
|
||||
@@ -1464,7 +1525,7 @@ public:
|
||||
/// Returns a string representing the specified API.
|
||||
static const char* RenderAPIToString(RenderAPI api);
|
||||
|
||||
/// Parses the configured fullscreen mode into its components (width * height @ refresh hz)
|
||||
/// Parses the configured fullscreen mode into its components (width * height @ refresh Hz)
|
||||
static bool GetRequestedExclusiveFullscreenMode(u32* width, u32* height, float* refresh_rate);
|
||||
|
||||
/// Converts a fullscreen mode to a string.
|
||||
@@ -1563,9 +1624,11 @@ public:
|
||||
|
||||
GSTexture* CreateRenderTarget(int w, int h, GSTexture::Format format, bool clear = true, bool prefer_reuse = true);
|
||||
GSTexture* CreateDepthStencil(int w, int h, bool clear = true, bool prefer_reuse = true);
|
||||
GSTexture* CreateDepthColor(int w, int h, bool clear = true, bool prefer_reuse = true);
|
||||
GSTexture* CreateTexture(int w, int h, int mipmap_levels, GSTexture::Format format, bool prefer_reuse = false);
|
||||
GSTexture* CreateRenderTarget(const GSVector2i& size, GSTexture::Format format, bool clear = true, bool prefer_reuse = true);
|
||||
GSTexture* CreateDepthStencil(const GSVector2i& size, bool clear = true, bool prefer_reuse = true);
|
||||
GSTexture* CreateDepthColor(const GSVector2i& size, bool clear = true, bool prefer_reuse = true);
|
||||
GSTexture* CreateTexture(const GSVector2i& size, int mipmap_levels, GSTexture::Format format, bool prefer_reuse = false);
|
||||
GSTexture* CreateCompatible(GSTexture* tex, bool clear = true, bool prefer_reuse = true);
|
||||
GSTexture* CreateCompatible(GSTexture* tex, const GSVector2i& size, bool clear = true, bool prefer_reuse = true);
|
||||
|
||||
@@ -56,4 +56,11 @@ enum class PS_AA1 : uint32_t
|
||||
TRIANGLE_SW_Z = 3, ///< AA1 triangles with software Z discard
|
||||
};
|
||||
|
||||
enum class PS_ROV_DEPTH : uint32_t
|
||||
{
|
||||
NONE = 0,
|
||||
READ_WRITE = 1,
|
||||
READ_ONLY = 2,
|
||||
};
|
||||
|
||||
} // namespace GSShader
|
||||
|
||||
@@ -73,6 +73,13 @@ protected:
|
||||
bool m_needs_mipmaps_generated = true;
|
||||
ClearValue m_clear_value = {};
|
||||
|
||||
// For GL/DX11 since they don't track layouts.
|
||||
// Used heuristically to decide whether to use ROV for a draw.
|
||||
bool m_unordered_access = false;
|
||||
|
||||
#ifdef PCSX2_DEVBUILD
|
||||
std::string m_debug_name;
|
||||
#endif
|
||||
public:
|
||||
GSTexture();
|
||||
virtual ~GSTexture();
|
||||
@@ -87,6 +94,7 @@ public:
|
||||
|
||||
#ifdef PCSX2_DEVBUILD
|
||||
virtual void SetDebugName(std::string_view name) = 0;
|
||||
const std::string& GetDebugName() { return m_debug_name; }
|
||||
#endif
|
||||
|
||||
bool Save(const std::string& fn);
|
||||
@@ -173,6 +181,12 @@ public:
|
||||
// Typical size of a RGBA texture
|
||||
u32 GetMemUsage() const { return m_size.x * m_size.y * (m_format == Format::UNorm8 ? 1 : 4); }
|
||||
|
||||
// The unordered access flag is sticky, so once it's set we keep using ROV for the target
|
||||
// until it's recycled/destroyed. Only used for DX11/GL, which don't track actual resource layout/state.
|
||||
virtual bool IsUnorderedAccess() const { return m_unordered_access; }
|
||||
void SetUnorderedAccess() { m_unordered_access = true; }
|
||||
void ClearUnorderedAccess() { m_unordered_access = false; }
|
||||
|
||||
// Helper routines for formats/types
|
||||
static bool IsCompressedFormat(Format format) { return (format >= Format::BC1 && format <= Format::BC7); }
|
||||
};
|
||||
|
||||
@@ -65,6 +65,7 @@ bool D3D11ShaderCache::Open(D3D_FEATURE_LEVEL feature_level, bool debug)
|
||||
m_shader_model = D3D::ShaderModel::SM41;
|
||||
break;
|
||||
case D3D_FEATURE_LEVEL_11_0:
|
||||
case D3D_FEATURE_LEVEL_11_1:
|
||||
m_shader_model = D3D::ShaderModel::SM50;
|
||||
break;
|
||||
default:
|
||||
|
||||
@@ -109,7 +109,8 @@ bool GSDevice11::Create(GSVSyncMode vsync_mode, bool allow_present_throttle)
|
||||
|
||||
wil::com_ptr_nothrow<IDXGIAdapter1> dxgi_adapter = D3D::GetAdapterByName(m_dxgi_factory.get(), GSConfig.Adapter);
|
||||
|
||||
static constexpr std::array<D3D_FEATURE_LEVEL, 2> requested_feature_levels = {{
|
||||
static constexpr std::array<D3D_FEATURE_LEVEL, 3> requested_feature_levels = {{
|
||||
D3D_FEATURE_LEVEL_11_1,
|
||||
D3D_FEATURE_LEVEL_11_0,
|
||||
D3D_FEATURE_LEVEL_10_0,
|
||||
}};
|
||||
@@ -592,11 +593,18 @@ bool GSDevice11::Create(GSVSyncMode vsync_mode, bool allow_present_throttle)
|
||||
Host::OSD_WARNING_DURATION);
|
||||
}
|
||||
|
||||
// 1x1 dummy texture.
|
||||
m_null_texture = CreateSurface(GSTexture::Type::RenderTarget, 1, 1, 1, GSTexture::Format::Color);
|
||||
if (!m_null_texture)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GSDevice11::Destroy()
|
||||
{
|
||||
delete m_null_texture;
|
||||
|
||||
GSDevice::Destroy();
|
||||
DestroySwapChain();
|
||||
DestroyTimestampQueries();
|
||||
@@ -683,6 +691,12 @@ void GSDevice11::SetFeatures(IDXGIAdapter1* adapter)
|
||||
m_conservative_depth = (m_feature_level >= D3D_FEATURE_LEVEL_11_0);
|
||||
m_rgba16_unorm_hw_blend = IsTextureFormatHWBlendable(m_dev.get(), DXGI_FORMAT_R16G16B16A16_UNORM);
|
||||
|
||||
m_uav_texture = (m_feature_level >= D3D_FEATURE_LEVEL_11_0);
|
||||
|
||||
D3D11_FEATURE_DATA_D3D11_OPTIONS2 options2{};
|
||||
m_dev->CheckFeatureSupport(D3D11_FEATURE_D3D11_OPTIONS2, &options2, sizeof(options2));
|
||||
m_features.rov = m_uav_texture && options2.ROVsSupported;
|
||||
|
||||
// Let the user know if said features are available.
|
||||
Console.WriteLnFmt("D3D11: DXTn Texture Compression: {}", m_features.dxt_textures ? "Supported" : "Not Supported");
|
||||
Console.WriteLnFmt("D3D11: BC6/7 Texture Compression: {}", m_features.bptc_textures ? "Supported" : "Not Supported");
|
||||
@@ -1322,6 +1336,8 @@ GSTexture* GSDevice11::CreateSurface(GSTexture::Type type, int width, int height
|
||||
{
|
||||
case GSTexture::Type::RenderTarget:
|
||||
desc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
|
||||
if (m_uav_texture)
|
||||
desc.BindFlags |= D3D11_BIND_UNORDERED_ACCESS;
|
||||
break;
|
||||
case GSTexture::Type::DepthStencil:
|
||||
desc.BindFlags = D3D11_BIND_DEPTH_STENCIL | D3D11_BIND_SHADER_RESOURCE;
|
||||
@@ -1331,6 +1347,7 @@ GSTexture* GSDevice11::CreateSurface(GSTexture::Type type, int width, int height
|
||||
desc.MiscFlags = (levels > 1 && !GSTexture::IsCompressedFormat(format)) ? D3D11_RESOURCE_MISC_GENERATE_MIPS : 0;
|
||||
break;
|
||||
case GSTexture::Type::RWTexture:
|
||||
pxAssert(m_uav_texture);
|
||||
desc.BindFlags = D3D11_BIND_UNORDERED_ACCESS | D3D11_BIND_SHADER_RESOURCE;
|
||||
break;
|
||||
default:
|
||||
@@ -1482,7 +1499,7 @@ void GSDevice11::DoStretchRect(GSTexture* sTex, const GSVector4& sRect, GSTextur
|
||||
|
||||
// ps
|
||||
|
||||
PSSetShaderResource(0, sTex);
|
||||
PSSetShaderResource(TEXTURE_TEXTURE, sTex);
|
||||
PSSetSamplerState(filter == Biln ? m_convert.ln.get() : m_convert.pt.get());
|
||||
PSSetShader(ps, ps_cb);
|
||||
|
||||
@@ -1547,7 +1564,7 @@ void GSDevice11::PresentRect(GSTexture* sTex, const GSVector4& sRect, GSTexture*
|
||||
|
||||
// ps
|
||||
|
||||
PSSetShaderResource(0, sTex);
|
||||
PSSetShaderResource(TEXTURE_TEXTURE, sTex);
|
||||
PSSetSamplerState(filter == Biln ? m_convert.ln.get() : m_convert.pt.get());
|
||||
PSSetShader(m_present.ps[static_cast<u32>(shader)].get(), m_present.ps_cb.get());
|
||||
|
||||
@@ -1698,7 +1715,7 @@ void GSDevice11::DoMultiStretchRects(const MultiStretchRect* rects, u32 num_rect
|
||||
IASetIndexBuffer(m_ib.get());
|
||||
|
||||
CommitClear(rects[0].src);
|
||||
PSSetShaderResource(0, rects[0].src);
|
||||
PSSetShaderResource(TEXTURE_TEXTURE, rects[0].src);
|
||||
PSSetSamplerState(rects[0].filter == Biln ? m_convert.ln.get() : m_convert.pt.get());
|
||||
|
||||
OMSetBlendState(m_convert.bs[rects[0].wmask.wrgba].get(), 0.0f);
|
||||
@@ -1921,6 +1938,8 @@ void GSDevice11::SetupPS(const PSSelector& sel, const GSHWDrawConfig::PSConstant
|
||||
sm.AddMacro("PS_AA1", static_cast<u32>(sel.aa1));
|
||||
sm.AddMacro("PS_ABE", sel.abe);
|
||||
sm.AddMacro("PS_ANISOTROPIC_FILTERING", sel.sw_aniso);
|
||||
sm.AddMacro("PS_ROV_COLOR", sel.rov_color);
|
||||
sm.AddMacro("PS_ROV_DEPTH", static_cast<u32>(sel.rov_depth));
|
||||
|
||||
wil::com_ptr_nothrow<ID3D11PixelShader> ps = m_shader_cache.GetPixelShader(m_dev.get(), m_tfx_source, sm.GetPtr(), "ps_main");
|
||||
i = m_ps.try_emplace(sel, std::move(ps)).first;
|
||||
@@ -2339,7 +2358,7 @@ void GSDevice11::SetupDATE(GSTexture* rt, GSTexture* ds, SetDATM datm, const GSV
|
||||
|
||||
// ps
|
||||
|
||||
PSSetShaderResource(0, rt);
|
||||
PSSetShaderResource(TEXTURE_TEXTURE, rt);
|
||||
PSSetSamplerState(m_convert.pt.get());
|
||||
PSSetShader(GetConvertShader(SetDATMShader(datm)), nullptr);
|
||||
|
||||
@@ -2707,10 +2726,16 @@ void GSDevice11::OMSetBlendState(ID3D11BlendState* bs, u8 bf)
|
||||
}
|
||||
}
|
||||
|
||||
void GSDevice11::OMSetRenderTargets(GSTexture* rt, GSTexture* ds, const GSVector4i* scissor, ID3D11DepthStencilView* read_only_dsv)
|
||||
void GSDevice11::OMSetRenderTargets(GSTexture* rt, GSTexture* ds, GSTexture* rt_uav_tex, GSTexture* ds_uav_tex,
|
||||
const GSVector4i* scissor, ID3D11DepthStencilView* read_only_dsv)
|
||||
{
|
||||
if (!(rt || rt_uav_tex) && ds_uav_tex)
|
||||
rt_uav_tex = m_null_texture; // Fill in the first UAV slot with the null texture.
|
||||
|
||||
ID3D11RenderTargetView* rtv = nullptr;
|
||||
ID3D11DepthStencilView* dsv = nullptr;
|
||||
ID3D11UnorderedAccessView* rt_uav = nullptr;
|
||||
ID3D11UnorderedAccessView* ds_uav = nullptr;
|
||||
|
||||
if (rt)
|
||||
{
|
||||
@@ -2722,9 +2747,21 @@ void GSDevice11::OMSetRenderTargets(GSTexture* rt, GSTexture* ds, const GSVector
|
||||
CommitClear(ds);
|
||||
dsv = read_only_dsv ? read_only_dsv : *static_cast<GSTexture11*>(ds);
|
||||
}
|
||||
if (rt_uav_tex)
|
||||
{
|
||||
CommitClear(rt_uav_tex);
|
||||
rt_uav = *static_cast<GSTexture11*>(rt_uav_tex);
|
||||
}
|
||||
if (ds_uav_tex)
|
||||
{
|
||||
CommitClear(ds_uav_tex);
|
||||
ds_uav = *static_cast<GSTexture11*>(ds_uav_tex);
|
||||
}
|
||||
|
||||
const bool changed = (m_state.rtv != rtv || m_state.dsv != dsv);
|
||||
g_perfmon.Put(GSPerfMon::RenderPasses, static_cast<double>(changed));
|
||||
const bool changed = (m_state.rtv != rtv || m_state.dsv != dsv || m_state.rt_uav != rt_uav || m_state.ds_uav != ds_uav);
|
||||
|
||||
if (changed)
|
||||
g_perfmon.Put(GSPerfMon::RenderPasses, 1.0);
|
||||
|
||||
if (m_state.rtv != rtv)
|
||||
{
|
||||
@@ -2744,15 +2781,49 @@ void GSDevice11::OMSetRenderTargets(GSTexture* rt, GSTexture* ds, const GSVector
|
||||
m_state.dsv = dsv;
|
||||
m_state.current_ds = ds;
|
||||
}
|
||||
if (m_state.rt_uav != rt_uav)
|
||||
{
|
||||
if (m_state.rt_uav)
|
||||
m_state.rt_uav->Release();
|
||||
if (rt_uav)
|
||||
rt_uav->AddRef();
|
||||
m_state.rt_uav = rt_uav;
|
||||
m_state.current_rt_uav = rt_uav_tex;
|
||||
}
|
||||
if (m_state.ds_uav != ds_uav)
|
||||
{
|
||||
if (m_state.ds_uav)
|
||||
m_state.ds_uav->Release();
|
||||
if (ds_uav)
|
||||
ds_uav->AddRef();
|
||||
m_state.ds_uav = ds_uav;
|
||||
m_state.current_ds_uav = ds_uav_tex;
|
||||
}
|
||||
|
||||
PSUnbindConflictingSRVs(m_state.current_rt, read_only_dsv ? nullptr : m_state.current_ds);
|
||||
PSUnbindConflictingSRVs(m_state.current_rt_uav, m_state.current_ds_uav);
|
||||
|
||||
if (changed)
|
||||
m_ctx->OMSetRenderTargets(1, &rtv, dsv);
|
||||
|
||||
if (rt || ds)
|
||||
{
|
||||
const GSVector2i size = rt ? rt->GetSize() : ds->GetSize();
|
||||
// OM targets and UAVs share the same namespace in DX11, so we need to pack them into contiguous slots.
|
||||
u32 num_rtvs = rt ? 1 : 0;
|
||||
u32 num_uavs = 0;
|
||||
ID3D11UnorderedAccessView* uavs[2];
|
||||
if (rt_uav)
|
||||
uavs[num_uavs++] = rt_uav;
|
||||
if (ds_uav)
|
||||
uavs[num_uavs++] = ds_uav;
|
||||
ID3D11RenderTargetView* rtvs[] = { rtv };
|
||||
m_ctx->OMSetRenderTargetsAndUnorderedAccessViews(num_rtvs, rtvs, dsv, num_rtvs, num_uavs, uavs, nullptr);
|
||||
}
|
||||
|
||||
if (rt || ds || rt_uav_tex || ds_uav_tex)
|
||||
{
|
||||
const GSVector2i size =
|
||||
rt ? rt->GetSize() :
|
||||
ds ? ds->GetSize() :
|
||||
(rt_uav_tex && rt_uav_tex != m_null_texture) ? rt_uav_tex->GetSize() :
|
||||
ds_uav_tex->GetSize();
|
||||
SetViewport(size);
|
||||
SetScissor(scissor ? *scissor : GSVector4i::loadh(size));
|
||||
}
|
||||
@@ -2815,6 +2886,10 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
|
||||
{
|
||||
const GSVector2i rtsize = (config.rt ? config.rt : config.ds)->GetSize();
|
||||
GSTexture* colclip_rt = g_gs_device->GetColorClipTexture();
|
||||
GSTexture* draw_rt = config.ps.HasColorROV() ? nullptr : config.rt;
|
||||
GSTexture* draw_ds = config.ps.HasDepthROV() ? nullptr : config.ds;
|
||||
GSTexture* draw_rt_rov = config.ps.HasColorROV() ? config.rt : nullptr;
|
||||
GSTexture* draw_ds_rov = config.ps.HasDepthROV() ? config.ds : nullptr;
|
||||
GSTexture* draw_rt_clone = nullptr;
|
||||
GSTexture* draw_ds_clone = nullptr;
|
||||
GSTexture* primid_texture = nullptr;
|
||||
@@ -2865,6 +2940,8 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
|
||||
const GSVector4 sRect = dRect / GSVector4(rtsize.x, rtsize.y).xyxy();
|
||||
StretchRect(config.rt, sRect, colclip_rt, dRect, ShaderConvert::COLCLIP_INIT, Nearest);
|
||||
}
|
||||
|
||||
draw_rt = colclip_rt ? colclip_rt : draw_rt;
|
||||
}
|
||||
|
||||
// Destination Alpha Setup
|
||||
@@ -2950,7 +3027,7 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
|
||||
const OMBlendSelector blend(GSHWDrawConfig::ColorMaskSelector(1),
|
||||
GSHWDrawConfig::BlendState(true, CONST_ONE, CONST_ONE, 3 /* MIN */, CONST_ONE, CONST_ZERO, false, 0));
|
||||
SetupOM(dss, blend, 0);
|
||||
OMSetRenderTargets(primid_texture, config.ds, &config.scissor, read_only_dsv);
|
||||
OMSetRenderTargets(primid_texture, config.ds, nullptr, nullptr, &config.scissor, read_only_dsv);
|
||||
SetRenderHWShaderResources(config, nullptr);
|
||||
Draw(config);
|
||||
|
||||
@@ -2960,16 +3037,14 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
|
||||
}
|
||||
|
||||
// Avoid changing framebuffer just to switch from rt+depth to rt and vice versa.
|
||||
GSTexture* draw_rt = colclip_rt ? colclip_rt : config.rt;
|
||||
GSTexture* draw_ds = config.ds;
|
||||
// Make sure no tex is bound as both rtv and srv at the same time.
|
||||
// All conflicts should've been taken care of by PSUnbindConflictingSRVs.
|
||||
if (!draw_rt && draw_ds && m_state.rtv && m_state.current_rt && m_state.rtv == *static_cast<GSTexture11*>(m_state.current_rt) &&
|
||||
if (!(draw_rt || draw_rt_rov) && draw_ds && m_state.rtv && m_state.current_rt && m_state.rtv == *static_cast<GSTexture11*>(m_state.current_rt) &&
|
||||
m_state.current_ds == draw_ds && config.tex != m_state.current_rt && m_state.current_rt->GetSize() == draw_ds->GetSize())
|
||||
{
|
||||
draw_rt = m_state.current_rt;
|
||||
}
|
||||
else if (!draw_ds && draw_rt && m_state.dsv && m_state.current_ds && m_state.dsv == *static_cast<GSTexture11*>(m_state.current_ds) &&
|
||||
else if (!(draw_ds || draw_ds_rov) && draw_rt && m_state.dsv && m_state.current_ds && m_state.dsv == *static_cast<GSTexture11*>(m_state.current_ds) &&
|
||||
m_state.current_rt == draw_rt && config.tex != m_state.current_ds && m_state.current_ds->GetSize() == draw_rt->GetSize())
|
||||
{
|
||||
draw_ds = m_state.current_ds;
|
||||
@@ -2977,7 +3052,7 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
|
||||
|
||||
const bool rt_feedbackloop_pass1 = config.IsFeedbackLoopRT(config.ps);
|
||||
const bool rt_feedbackloop_pass2 = config.IsFeedbackLoopRT(config.alpha_second_pass.ps);
|
||||
if (draw_rt && (((config.require_one_barrier || (config.require_full_barrier && m_features.multidraw_fb_copy)) &&
|
||||
if (draw_rt && !draw_rt_rov && (((config.require_one_barrier || (config.require_full_barrier && m_features.multidraw_fb_copy)) &&
|
||||
(rt_feedbackloop_pass1 || rt_feedbackloop_pass2))))
|
||||
{
|
||||
// Requires a copy of the RT.
|
||||
@@ -2989,7 +3064,7 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
|
||||
|
||||
const bool ds_feedbackloop_pass1 = config.IsFeedbackLoopDepth(config.ps);
|
||||
const bool ds_feedbackloop_pass2 = config.IsFeedbackLoopDepth(config.alpha_second_pass.ps);
|
||||
if (draw_ds && m_features.depth_feedback && (config.require_one_barrier || (config.require_full_barrier && m_features.multidraw_fb_copy)) &&
|
||||
if (draw_ds && !draw_ds_rov && m_features.depth_feedback && (config.require_one_barrier || (config.require_full_barrier && m_features.multidraw_fb_copy)) &&
|
||||
(ds_feedbackloop_pass1 || ds_feedbackloop_pass2))
|
||||
{
|
||||
// Requires a copy of the DS.
|
||||
@@ -2999,7 +3074,7 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
|
||||
Console.Warning("D3D11: Failed to allocate temp texture for DS copy.");
|
||||
}
|
||||
|
||||
OMSetRenderTargets(draw_rt, draw_ds, &config.scissor, read_only_dsv);
|
||||
OMSetRenderTargets(draw_rt, draw_ds, draw_rt_rov, draw_ds_rov, &config.scissor, read_only_dsv);
|
||||
SetRenderHWShaderResources(config, primid_texture);
|
||||
SetupOM(config.depth, OMBlendSelector(config.colormask, config.blend), config.blend.constant);
|
||||
|
||||
@@ -3008,7 +3083,7 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
|
||||
m_ctx->ClearDepthStencilView(*static_cast<GSTexture11*>(draw_ds), D3D11_CLEAR_STENCIL, 0.0f, 1);
|
||||
|
||||
SendHWDraw(config, rt_feedbackloop_pass1 ? draw_rt_clone : nullptr, draw_rt, ds_feedbackloop_pass1 ? draw_ds_clone : nullptr, draw_ds,
|
||||
config.require_one_barrier, config.require_full_barrier);
|
||||
config.require_one_barrier, config.require_full_barrier, rtsize);
|
||||
|
||||
if (config.blend_multi_pass.enable)
|
||||
{
|
||||
@@ -3036,7 +3111,7 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
|
||||
const bool one_barrier = config.alpha_second_pass.require_one_barrier && m_features.multidraw_fb_copy;
|
||||
SetupOM(config.alpha_second_pass.depth, OMBlendSelector(config.alpha_second_pass.colormask, config.blend), config.blend.constant);
|
||||
SendHWDraw(config, rt_feedbackloop_pass2 ? draw_rt_clone : nullptr, draw_rt, ds_feedbackloop_pass2 ? draw_ds_clone : nullptr, draw_ds,
|
||||
one_barrier, config.alpha_second_pass.require_full_barrier);
|
||||
one_barrier, config.alpha_second_pass.require_full_barrier, rtsize);
|
||||
}
|
||||
|
||||
if (colclip_rt)
|
||||
@@ -3110,7 +3185,7 @@ void GSDevice11::FeedbackCopyAndBind(const GSHWDrawConfig& config,
|
||||
|
||||
void GSDevice11::SendHWDraw(const GSHWDrawConfig& config,
|
||||
GSTexture* draw_rt_clone, GSTexture* draw_rt, GSTexture* draw_ds_clone, GSTexture* draw_ds,
|
||||
const bool one_barrier, const bool full_barrier)
|
||||
const bool one_barrier, const bool full_barrier, GSVector2i rtsize)
|
||||
{
|
||||
#ifdef PCSX2_DEVBUILD
|
||||
if ((one_barrier || full_barrier) && !(config.IsFeedbackLoopRT(config.ps) || config.IsFeedbackLoopDepth(config.ps))) [[unlikely]]
|
||||
@@ -3149,6 +3224,9 @@ void GSDevice11::SendHWDraw(const GSHWDrawConfig& config,
|
||||
}
|
||||
|
||||
Draw(config);
|
||||
|
||||
if (config.ps.HasColorROV() || config.ps.HasDepthROV())
|
||||
g_perfmon.Put(GSPerfMon::DrawCallsROV, 1);
|
||||
}
|
||||
|
||||
void GSDevice11::SetRenderHWShaderResources(const GSHWDrawConfig& config, GSTexture* primid_texture)
|
||||
@@ -3156,13 +3234,13 @@ void GSDevice11::SetRenderHWShaderResources(const GSHWDrawConfig& config, GSText
|
||||
if (config.tex && config.tex_hazard == GSHWDrawConfig::TEX_HAZARD_NONE)
|
||||
{
|
||||
CommitClear(config.tex);
|
||||
PSSetShaderResource(0, config.tex);
|
||||
PSSetShaderResource(TEXTURE_TEXTURE, config.tex);
|
||||
}
|
||||
if (config.pal)
|
||||
{
|
||||
CommitClear(config.pal);
|
||||
PSSetShaderResource(1, config.pal);
|
||||
PSSetShaderResource(TEXTURE_PALETTE, config.pal);
|
||||
}
|
||||
if (primid_texture)
|
||||
PSSetShaderResource(3, primid_texture);
|
||||
PSSetShaderResource(TEXTURE_PRIMID, primid_texture);
|
||||
}
|
||||
|
||||
@@ -83,6 +83,11 @@ public:
|
||||
private:
|
||||
enum : u32
|
||||
{
|
||||
TEXTURE_TEXTURE = 0,
|
||||
TEXTURE_PALETTE = 1,
|
||||
TEXTURE_RT = 2,
|
||||
TEXTURE_PRIMID = 3,
|
||||
TEXTURE_DEPTH = 4,
|
||||
MAX_TEXTURES = 5,
|
||||
MAX_SAMPLERS = 1,
|
||||
VERTEX_BUFFER_SIZE = 32 * 1024 * 1024,
|
||||
@@ -129,6 +134,8 @@ private:
|
||||
wil::com_ptr_nothrow<ID3D11ShaderResourceView> m_expand_vb_srv;
|
||||
wil::com_ptr_nothrow<ID3D11ShaderResourceView> m_expand_ib_vs_srv;
|
||||
|
||||
GSTexture* m_null_texture;
|
||||
|
||||
D3D_FEATURE_LEVEL m_feature_level = D3D_FEATURE_LEVEL_10_0;
|
||||
u32 m_vb_pos = 0; // bytes
|
||||
u32 m_ib_pos = 0; // indices/sizeof(u16)
|
||||
@@ -141,6 +148,7 @@ private:
|
||||
bool m_is_exclusive_fullscreen = false;
|
||||
bool m_conservative_depth = false;
|
||||
bool m_rgba16_unorm_hw_blend = false;
|
||||
bool m_uav_texture = false;
|
||||
|
||||
struct
|
||||
{
|
||||
@@ -166,8 +174,12 @@ private:
|
||||
u8 bf;
|
||||
ID3D11RenderTargetView* rtv;
|
||||
ID3D11DepthStencilView* dsv;
|
||||
ID3D11UnorderedAccessView* rt_uav;
|
||||
ID3D11UnorderedAccessView* ds_uav;
|
||||
GSTexture* current_rt;
|
||||
GSTexture* current_ds;
|
||||
GSTexture* current_rt_uav;
|
||||
GSTexture* current_ds_uav;
|
||||
} m_state;
|
||||
|
||||
std::array<std::array<wil::com_ptr_nothrow<ID3D11Query>, 3>, NUM_TIMESTAMP_QUERIES> m_timestamp_queries = {};
|
||||
@@ -371,7 +383,8 @@ public:
|
||||
|
||||
void OMSetDepthStencilState(ID3D11DepthStencilState* dss, u8 sref);
|
||||
void OMSetBlendState(ID3D11BlendState* bs, u8 bf);
|
||||
void OMSetRenderTargets(GSTexture* rt, GSTexture* ds, const GSVector4i* scissor = nullptr, ID3D11DepthStencilView* read_only_dsv = nullptr);
|
||||
void OMSetRenderTargets(GSTexture* rt, GSTexture* ds, GSTexture* rt_uav = nullptr, GSTexture* ds_uav = nullptr,
|
||||
const GSVector4i* scissor = nullptr, ID3D11DepthStencilView* read_only_dsv = nullptr);
|
||||
void SetViewport(const GSVector2i& viewport);
|
||||
void SetScissor(const GSVector4i& scissor);
|
||||
|
||||
@@ -390,7 +403,7 @@ public:
|
||||
const GSVector4i& copyarea, const GSVector4i& samplearea);
|
||||
void SendHWDraw(const GSHWDrawConfig& config,
|
||||
GSTexture* draw_rt_clone, GSTexture* draw_rt, GSTexture* draw_ds_clone, GSTexture* draw_ds,
|
||||
const bool one_barrier, const bool full_barrier);
|
||||
const bool one_barrier, const bool full_barrier, GSVector2i rtsize);
|
||||
void SetRenderHWShaderResources(const GSHWDrawConfig& config, GSTexture* primid_texture);
|
||||
|
||||
void ClearSamplerCache() override;
|
||||
|
||||
@@ -102,6 +102,8 @@ void GSTexture11::SetDebugName(std::string_view name)
|
||||
GSDevice11::SetD3DDebugObjectName(m_srv.get(), fmt::format("{} SRV", name));
|
||||
if (m_rtv)
|
||||
GSDevice11::SetD3DDebugObjectName(m_rtv.get(), fmt::format("{} RTV", name));
|
||||
|
||||
m_debug_name = name;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -19,7 +19,6 @@ class GSTexture11 final : public GSTexture
|
||||
wil::com_ptr_nothrow<ID3D11UnorderedAccessView> m_uav;
|
||||
wil::com_ptr_nothrow<ID3D11DepthStencilView> m_read_only_dsv;
|
||||
D3D11_TEXTURE2D_DESC m_desc;
|
||||
|
||||
public:
|
||||
explicit GSTexture11(wil::com_ptr_nothrow<ID3D11Texture2D> texture, const D3D11_TEXTURE2D_DESC& desc,
|
||||
GSTexture::Type type, GSTexture::Format format);
|
||||
|
||||
@@ -354,6 +354,31 @@ u32 D3D12::RootSignatureBuilder::AddDescriptorTable(
|
||||
return index;
|
||||
}
|
||||
|
||||
// Allows using ranges of non-contiguous shader registers in a single descriptor table.
|
||||
u32 D3D12::RootSignatureBuilder::AddDescriptorTableMultiRange(u32 num_ranges,
|
||||
D3D12_DESCRIPTOR_RANGE_TYPE* rt, u32* start_shader_reg, u32* num_shader_regs, D3D12_SHADER_VISIBILITY visibility)
|
||||
{
|
||||
const u32 index = m_desc.NumParameters++;
|
||||
const u32 dr_index = m_num_descriptor_ranges;
|
||||
m_num_descriptor_ranges += num_ranges;
|
||||
|
||||
for (u32 i = 0; i < num_ranges; i++)
|
||||
{
|
||||
m_descriptor_ranges[dr_index + i].RangeType = rt[i];
|
||||
m_descriptor_ranges[dr_index + i].NumDescriptors = num_shader_regs[i];
|
||||
m_descriptor_ranges[dr_index + i].BaseShaderRegister = start_shader_reg[i];
|
||||
m_descriptor_ranges[dr_index + i].RegisterSpace = 0;
|
||||
m_descriptor_ranges[dr_index + i].OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
|
||||
}
|
||||
|
||||
m_params[index].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
|
||||
m_params[index].DescriptorTable.pDescriptorRanges = &m_descriptor_ranges[dr_index];
|
||||
m_params[index].DescriptorTable.NumDescriptorRanges = num_ranges;
|
||||
m_params[index].ShaderVisibility = visibility;
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
#ifdef PCSX2_DEVBUILD
|
||||
#include "common/StringUtil.h"
|
||||
|
||||
|
||||
@@ -37,6 +37,8 @@ namespace D3D12
|
||||
u32 AddSRVParameter(u32 shader_reg, D3D12_SHADER_VISIBILITY visibility);
|
||||
u32 AddDescriptorTable(D3D12_DESCRIPTOR_RANGE_TYPE rt, u32 start_shader_reg, u32 num_shader_regs,
|
||||
D3D12_SHADER_VISIBILITY visibility);
|
||||
u32 AddDescriptorTableMultiRange(u32 num_ranges, D3D12_DESCRIPTOR_RANGE_TYPE* rt, u32* start_shader_reg,
|
||||
u32* num_shader_regs, D3D12_SHADER_VISIBILITY visibility);
|
||||
|
||||
private:
|
||||
D3D12_ROOT_SIGNATURE_DESC m_desc{};
|
||||
|
||||
@@ -397,17 +397,6 @@ bool GSDevice12::CreateDescriptorHeaps()
|
||||
return false;
|
||||
}
|
||||
|
||||
// Allocate null SRV descriptor for unbound textures.
|
||||
constexpr D3D12_SHADER_RESOURCE_VIEW_DESC null_srv_desc = {
|
||||
DXGI_FORMAT_R8G8B8A8_UNORM, D3D12_SRV_DIMENSION_TEXTURE2D, D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING};
|
||||
|
||||
if (!m_descriptor_heap_manager.Allocate(&m_null_srv_descriptor))
|
||||
{
|
||||
pxFailRel("Failed to allocate null descriptor");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_device->CreateShaderResourceView(nullptr, &null_srv_desc, m_null_srv_descriptor.cpu_handle);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1449,6 +1438,10 @@ bool GSDevice12::CheckFeatures(const u32& vendor_id)
|
||||
m_enhanced_barriers = false;
|
||||
}
|
||||
|
||||
D3D12_FEATURE_DATA_D3D12_OPTIONS options{};
|
||||
m_device->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS, &options, sizeof(D3D12_FEATURE_DATA_D3D12_OPTIONS));
|
||||
m_features.rov = options.ROVsSupported;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1505,30 +1498,30 @@ void GSDevice12::Draw(const GSHWDrawConfig& config)
|
||||
}
|
||||
|
||||
void GSDevice12::LookupNativeFormat(GSTexture::Format format, DXGI_FORMAT* d3d_format, DXGI_FORMAT* srv_format,
|
||||
DXGI_FORMAT* rtv_format, DXGI_FORMAT* dsv_format) const
|
||||
DXGI_FORMAT* rtv_format, DXGI_FORMAT* dsv_format, DXGI_FORMAT* uav_format) const
|
||||
{
|
||||
static constexpr std::array<std::array<DXGI_FORMAT, 4>, static_cast<int>(GSTexture::Format::Last) + 1>
|
||||
static constexpr std::array<std::array<DXGI_FORMAT, 5>, static_cast<int>(GSTexture::Format::Last) + 1>
|
||||
s_format_mapping = {{
|
||||
{DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN}, // Invalid
|
||||
{DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM,
|
||||
DXGI_FORMAT_UNKNOWN}, // Color
|
||||
DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_R8G8B8A8_UNORM}, // Color
|
||||
{DXGI_FORMAT_R10G10B10A2_UNORM, DXGI_FORMAT_R10G10B10A2_UNORM, DXGI_FORMAT_R10G10B10A2_UNORM,
|
||||
DXGI_FORMAT_UNKNOWN}, // ColorHQ
|
||||
DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_R10G10B10A2_UNORM}, // ColorHQ
|
||||
{DXGI_FORMAT_R16G16B16A16_FLOAT, DXGI_FORMAT_R16G16B16A16_FLOAT, DXGI_FORMAT_R16G16B16A16_FLOAT,
|
||||
DXGI_FORMAT_UNKNOWN}, // ColorHDR
|
||||
DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_R16G16B16A16_FLOAT}, // ColorHDR
|
||||
{DXGI_FORMAT_R16G16B16A16_UNORM, DXGI_FORMAT_R16G16B16A16_UNORM, DXGI_FORMAT_R16G16B16A16_UNORM,
|
||||
DXGI_FORMAT_UNKNOWN}, // ColorClip
|
||||
DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_R16G16B16A16_UNORM}, // ColorClip
|
||||
{DXGI_FORMAT_D32_FLOAT_S8X24_UINT, DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS, DXGI_FORMAT_UNKNOWN,
|
||||
DXGI_FORMAT_D32_FLOAT_S8X24_UINT}, // DepthStencil
|
||||
{DXGI_FORMAT_R32_FLOAT, DXGI_FORMAT_R32_FLOAT, DXGI_FORMAT_R32_FLOAT, DXGI_FORMAT_UNKNOWN}, // DepthColor
|
||||
{DXGI_FORMAT_A8_UNORM, DXGI_FORMAT_A8_UNORM, DXGI_FORMAT_A8_UNORM, DXGI_FORMAT_UNKNOWN}, // UNorm8
|
||||
{DXGI_FORMAT_R16_UINT, DXGI_FORMAT_R16_UINT, DXGI_FORMAT_R16_UINT, DXGI_FORMAT_UNKNOWN}, // UInt16
|
||||
{DXGI_FORMAT_R32_UINT, DXGI_FORMAT_R32_UINT, DXGI_FORMAT_R32_UINT, DXGI_FORMAT_UNKNOWN}, // UInt32
|
||||
{DXGI_FORMAT_R32_FLOAT, DXGI_FORMAT_R32_FLOAT, DXGI_FORMAT_R32_FLOAT, DXGI_FORMAT_UNKNOWN}, // Int32
|
||||
{DXGI_FORMAT_BC1_UNORM, DXGI_FORMAT_BC1_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN}, // BC1
|
||||
{DXGI_FORMAT_BC2_UNORM, DXGI_FORMAT_BC2_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN}, // BC2
|
||||
{DXGI_FORMAT_BC3_UNORM, DXGI_FORMAT_BC3_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN}, // BC3
|
||||
{DXGI_FORMAT_BC7_UNORM, DXGI_FORMAT_BC7_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN}, // BC7
|
||||
DXGI_FORMAT_D32_FLOAT_S8X24_UINT, DXGI_FORMAT_R32_FLOAT}, // DepthStencil
|
||||
{DXGI_FORMAT_R32_FLOAT, DXGI_FORMAT_R32_FLOAT, DXGI_FORMAT_R32_FLOAT, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_R32_FLOAT}, // DepthColor
|
||||
{DXGI_FORMAT_A8_UNORM, DXGI_FORMAT_A8_UNORM, DXGI_FORMAT_A8_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_A8_UNORM}, // UNorm8
|
||||
{DXGI_FORMAT_R16_UINT, DXGI_FORMAT_R16_UINT, DXGI_FORMAT_R16_UINT, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_R16_UINT}, // UInt16
|
||||
{DXGI_FORMAT_R32_UINT, DXGI_FORMAT_R32_UINT, DXGI_FORMAT_R32_UINT, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_R32_UINT}, // UInt32
|
||||
{DXGI_FORMAT_R32_FLOAT, DXGI_FORMAT_R32_FLOAT, DXGI_FORMAT_R32_FLOAT, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_R32_FLOAT}, // Int32
|
||||
{DXGI_FORMAT_BC1_UNORM, DXGI_FORMAT_BC1_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN}, // BC1
|
||||
{DXGI_FORMAT_BC2_UNORM, DXGI_FORMAT_BC2_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN}, // BC2
|
||||
{DXGI_FORMAT_BC3_UNORM, DXGI_FORMAT_BC3_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN}, // BC3
|
||||
{DXGI_FORMAT_BC7_UNORM, DXGI_FORMAT_BC7_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN}, // BC7
|
||||
}};
|
||||
|
||||
const auto& mapping = s_format_mapping[static_cast<int>(format)];
|
||||
@@ -1540,14 +1533,17 @@ void GSDevice12::LookupNativeFormat(GSTexture::Format format, DXGI_FORMAT* d3d_f
|
||||
*rtv_format = mapping[2];
|
||||
if (dsv_format)
|
||||
*dsv_format = mapping[3];
|
||||
if (uav_format)
|
||||
*uav_format = mapping[4];
|
||||
}
|
||||
|
||||
GSTexture* GSDevice12::CreateSurface(GSTexture::Type type, int width, int height, int levels, GSTexture::Format format)
|
||||
{
|
||||
DXGI_FORMAT dxgi_format, srv_format, rtv_format, dsv_format;
|
||||
LookupNativeFormat(format, &dxgi_format, &srv_format, &rtv_format, &dsv_format);
|
||||
DXGI_FORMAT dxgi_format, srv_format, rtv_format, dsv_format, uav_format;
|
||||
LookupNativeFormat(format, &dxgi_format, &srv_format, &rtv_format, &dsv_format, &uav_format);
|
||||
|
||||
const DXGI_FORMAT uav_format = (type == GSTexture::Type::RWTexture) ? dxgi_format : DXGI_FORMAT_UNKNOWN;
|
||||
if (type != GSTexture::Type::RWTexture && type != GSTexture::Type::RenderTarget)
|
||||
uav_format = DXGI_FORMAT_UNKNOWN; // We don't need the UAV descriptor.
|
||||
|
||||
std::unique_ptr<GSTexture12> tex(GSTexture12::Create(type, format, width, height, levels,
|
||||
dxgi_format, srv_format, rtv_format, dsv_format, uav_format));
|
||||
@@ -2446,24 +2442,23 @@ void GSDevice12::VSSetIndexBuffer(const void* index, size_t count)
|
||||
}
|
||||
|
||||
void GSDevice12::OMSetRenderTargets(GSTexture* rt, GSTexture* ds_as_rt, GSTexture* ds, const GSVector4i& scissor,
|
||||
bool depth_read)
|
||||
bool depth_read, const GSVector2i& viewport_size)
|
||||
{
|
||||
GSTexture12* d12Rt = static_cast<GSTexture12*>(rt);
|
||||
GSTexture12* d12DsRt = static_cast<GSTexture12*>(ds_as_rt);
|
||||
GSTexture12* d12Ds = static_cast<GSTexture12*>(ds);
|
||||
pxAssert(d12Rt || d12DsRt || d12Ds);
|
||||
|
||||
if (m_current_render_target != d12Rt || m_current_depth_render_target != d12DsRt || m_current_depth_target != d12Ds ||
|
||||
m_current_depth_read_only != depth_read)
|
||||
// Check if framebuffer changed
|
||||
if (m_current_render_target != d12Rt || m_current_depth_render_target != d12DsRt ||
|
||||
m_current_depth_target != d12Ds || m_current_depth_read_only != depth_read)
|
||||
{
|
||||
// framebuffer change
|
||||
EndRenderPass();
|
||||
}
|
||||
else if (InRenderPass())
|
||||
{
|
||||
// Framebuffer unchanged, but check for clears. Have to restart render pass, unlike Vulkan.
|
||||
// We'll take care of issuing the actual clear there, because we have to start one anyway.
|
||||
for (GSTexture12* tex : std::array{d12Rt, d12DsRt, d12Ds})
|
||||
for (GSTexture12* tex : std::array{ d12Rt, d12DsRt, d12Ds })
|
||||
{
|
||||
if (tex && tex->GetState() != GSTexture::State::Dirty)
|
||||
{
|
||||
@@ -2491,7 +2486,8 @@ void GSDevice12::OMSetRenderTargets(GSTexture* rt, GSTexture* ds_as_rt, GSTextur
|
||||
}
|
||||
|
||||
// This is used to set/initialize the framebuffer for tfx rendering.
|
||||
const GSVector2i size = d12Rt ? d12Rt->GetSize() : (d12DsRt ? d12DsRt->GetSize() : d12Ds->GetSize());
|
||||
const GSVector2i size = d12Rt ? d12Rt->GetSize() :
|
||||
(d12DsRt ? d12DsRt->GetSize() : (d12Ds ? d12Ds->GetSize() : viewport_size));
|
||||
const D3D12_VIEWPORT vp{0.0f, 0.0f, static_cast<float>(size.x), static_cast<float>(size.y), 0.0f, 1.0f};
|
||||
|
||||
SetViewport(vp);
|
||||
@@ -2608,8 +2604,8 @@ GSDevice12::ComPtr<ID3DBlob> GSDevice12::GetUtilityPixelShader(const std::string
|
||||
bool GSDevice12::CreateNullTexture()
|
||||
{
|
||||
m_null_texture =
|
||||
GSTexture12::Create(GSTexture::Type::Texture, GSTexture::Format::Color, 1, 1, 1, DXGI_FORMAT_R8G8B8A8_UNORM,
|
||||
DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN);
|
||||
GSTexture12::Create(GSTexture::Type::RenderTarget, GSTexture::Format::Color, 1, 1, 1, DXGI_FORMAT_R8G8B8A8_UNORM,
|
||||
DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_R8G8B8A8_UNORM);
|
||||
if (!m_null_texture)
|
||||
return false;
|
||||
|
||||
@@ -2689,9 +2685,13 @@ bool GSDevice12::CreateRootSignatures()
|
||||
rsb.AddCBVParameter(1, D3D12_SHADER_VISIBILITY_PIXEL);
|
||||
rsb.AddSRVParameter(0, D3D12_SHADER_VISIBILITY_VERTEX);
|
||||
rsb.AddSRVParameter(5, D3D12_SHADER_VISIBILITY_VERTEX);
|
||||
rsb.AddDescriptorTable(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 0, 2, D3D12_SHADER_VISIBILITY_PIXEL); // Source / Palette
|
||||
rsb.AddDescriptorTable(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 0, 2, D3D12_SHADER_VISIBILITY_PIXEL); // Source (t0) / Palette (t2)
|
||||
rsb.AddDescriptorTable(D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER, 0, NUM_TFX_SAMPLERS, D3D12_SHADER_VISIBILITY_PIXEL);
|
||||
rsb.AddDescriptorTable(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 2, 3, D3D12_SHADER_VISIBILITY_PIXEL); // RT / PrimID / Depth
|
||||
// RT (t2) / PrimID (t3) / Depth (t4) / RT UAV (u0) / Depth UAV (u1)
|
||||
D3D12_DESCRIPTOR_RANGE_TYPE rt_types[2] = { D3D12_DESCRIPTOR_RANGE_TYPE_SRV, D3D12_DESCRIPTOR_RANGE_TYPE_UAV };
|
||||
u32 rt_start_regs[2] = { 2, 0 };
|
||||
u32 rt_num_regs[2] = { 3, 2 };
|
||||
rsb.AddDescriptorTableMultiRange(2, rt_types, rt_start_regs, rt_num_regs, D3D12_SHADER_VISIBILITY_PIXEL);
|
||||
rsb.Add32BitConstants(2, sizeof(m_vs_pc_cache) / sizeof(u32), D3D12_SHADER_VISIBILITY_VERTEX);
|
||||
if (!(m_tfx_root_signature = rsb.Create()))
|
||||
return false;
|
||||
@@ -2728,7 +2728,7 @@ bool GSDevice12::CompileConvertPipelines()
|
||||
|
||||
GSTexture::Format format = shader.OutputFormat();
|
||||
DXGI_FORMAT dxgi_format;
|
||||
LookupNativeFormat(format, nullptr, nullptr, &dxgi_format, nullptr);
|
||||
LookupNativeFormat(format, nullptr, nullptr, &dxgi_format, nullptr, nullptr);
|
||||
|
||||
if (shader.DATMConvertShader() || shader.DepthOutput())
|
||||
{
|
||||
@@ -3067,7 +3067,6 @@ void GSDevice12::DestroyResources()
|
||||
|
||||
m_shader_cache.Close();
|
||||
|
||||
m_descriptor_heap_manager.Free(&m_null_srv_descriptor);
|
||||
m_timestamp_query_buffer.reset();
|
||||
m_timestamp_query_allocation.reset();
|
||||
m_sampler_heap_manager.Destroy();
|
||||
@@ -3178,6 +3177,8 @@ const ID3DBlob* GSDevice12::GetTFXPixelShader(const GSHWDrawConfig::PSSelector&
|
||||
sm.AddMacro("PS_AA1", static_cast<u32>(sel.aa1));
|
||||
sm.AddMacro("PS_ABE", sel.abe);
|
||||
sm.AddMacro("PS_ANISOTROPIC_FILTERING", sel.sw_aniso);
|
||||
sm.AddMacro("PS_ROV_COLOR", sel.rov_color);
|
||||
sm.AddMacro("PS_ROV_DEPTH", static_cast<u32>(sel.rov_depth));
|
||||
|
||||
ComPtr<ID3DBlob> ps(m_shader_cache.GetPixelShader(m_tfx_source, sm.GetPtr(), "ps_main"));
|
||||
it = m_tfx_pixel_shaders.emplace(sel, std::move(ps)).first;
|
||||
@@ -3216,11 +3217,11 @@ GSDevice12::ComPtr<ID3D12PipelineState> GSDevice12::CreateTFXPipeline(const Pipe
|
||||
if (p.rt)
|
||||
{
|
||||
const GSTexture::Format format = IsDATEModePrimIDInit(p.ps.date) ?
|
||||
GSTexture::Format::PrimID :
|
||||
(p.ps.colclip_hw ? GSTexture::Format::ColorClip : GSTexture::Format::Color);
|
||||
GSTexture::Format::PrimID :
|
||||
(p.ps.colclip_hw ? GSTexture::Format::ColorClip : GSTexture::Format::Color);
|
||||
|
||||
DXGI_FORMAT native_format;
|
||||
LookupNativeFormat(format, nullptr, nullptr, &native_format, nullptr);
|
||||
LookupNativeFormat(format, nullptr, nullptr, &native_format, nullptr, nullptr);
|
||||
gpb.SetRenderTarget(num_rts++, native_format);
|
||||
}
|
||||
if (p.ds_as_rt)
|
||||
@@ -3305,7 +3306,7 @@ GSDevice12::ComPtr<ID3D12PipelineState> GSDevice12::CreateTFXPipeline(const Pipe
|
||||
if (pipeline)
|
||||
{
|
||||
D3D12::SetObjectName(
|
||||
pipeline.get(), TinyString::from_format("TFX Pipeline {:08X}/{:08X}{:016X}", p.vs.key, p.ps.key_hi, p.ps.key_lo));
|
||||
pipeline.get(), TinyString::from_format("TFX Pipeline {:08X}/{:016X}_{:016X}", p.vs.key, p.ps.key_hi, p.ps.key_lo));
|
||||
}
|
||||
|
||||
return pipeline;
|
||||
@@ -3335,8 +3336,10 @@ bool GSDevice12::BindDrawPipeline(const PipelineSelector& p)
|
||||
|
||||
void GSDevice12::InitializeState()
|
||||
{
|
||||
for (u32 i = 0; i < NUM_TOTAL_TFX_TEXTURES; i++)
|
||||
for (u32 i = 0; i < TEXTURE_RT_UAV; i++)
|
||||
m_tfx_textures[i] = m_null_texture->GetSRVDescriptor();
|
||||
for (u32 i = TEXTURE_RT_UAV; i < NUM_TOTAL_TFX_TEXTURES; i++)
|
||||
m_tfx_textures[i] = m_null_texture->GetUAVDescriptor();
|
||||
m_tfx_sampler_sel = GSHWDrawConfig::SamplerSelector::Point().key;
|
||||
|
||||
InvalidateCachedState();
|
||||
@@ -3475,25 +3478,30 @@ void GSDevice12::SetStencilRef(u8 ref)
|
||||
m_dirty_flags |= DIRTY_FLAG_STENCIL_REF;
|
||||
}
|
||||
|
||||
void GSDevice12::PSSetShaderResource(int i, GSTexture* sr, bool check_state, bool feedback)
|
||||
void GSDevice12::PSSetShaderResource(int i, GSTexture* sr, bool check_state, ResourceType type)
|
||||
{
|
||||
pxAssert(i < NUM_TFX_TEXTURES + NUM_TFX_RT_TEXTURES + NUM_TFX_UAV_TEXTURES);
|
||||
|
||||
D3D12DescriptorHandle handle;
|
||||
if (sr)
|
||||
{
|
||||
const GSTexture12::ResourceState state = GetResourceState(type);
|
||||
|
||||
GSTexture12* dtex = static_cast<GSTexture12*>(sr);
|
||||
if (check_state)
|
||||
{
|
||||
if (dtex->GetResourceState() != GSTexture12::ResourceState::PixelShaderResource && InRenderPass())
|
||||
if (dtex->GetResourceState() != state && InRenderPass())
|
||||
{
|
||||
GL_INS("Ending render pass due to resource transition");
|
||||
EndRenderPass();
|
||||
}
|
||||
|
||||
dtex->CommitClear();
|
||||
dtex->TransitionToState(GSTexture12::ResourceState::PixelShaderResource);
|
||||
dtex->TransitionToState(state);
|
||||
}
|
||||
dtex->SetUseFenceCounter(GetCurrentFenceValue());
|
||||
handle = (feedback && !m_enhanced_barriers) ? dtex->GetFBLDescriptor() : dtex->GetSRVDescriptor();
|
||||
|
||||
handle = GetResourceDescriptor(dtex, type);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -3504,7 +3512,7 @@ void GSDevice12::PSSetShaderResource(int i, GSTexture* sr, bool check_state, boo
|
||||
return;
|
||||
|
||||
m_tfx_textures[i] = handle;
|
||||
m_dirty_flags |= (i < 2) ? DIRTY_FLAG_TFX_TEXTURES : DIRTY_FLAG_TFX_RT_TEXTURES;
|
||||
m_dirty_flags |= (i < NUM_TFX_TEXTURES) ? DIRTY_FLAG_TFX_TEXTURES : DIRTY_FLAG_TFX_RT_TEXTURES;
|
||||
}
|
||||
|
||||
void GSDevice12::PSSetSampler(GSHWDrawConfig::SamplerSelector sel)
|
||||
@@ -3517,6 +3525,68 @@ void GSDevice12::PSSetSampler(GSHWDrawConfig::SamplerSelector sel)
|
||||
m_dirty_flags |= DIRTY_FLAG_TFX_SAMPLERS;
|
||||
}
|
||||
|
||||
void GSDevice12::PSSetUnorderedAccess(GSTexture* rt, GSTexture* ds, bool write_rt, bool write_ds)
|
||||
{
|
||||
GSTexture12* d12Rt = static_cast<GSTexture12*>(rt);
|
||||
GSTexture12* d12Ds = static_cast<GSTexture12*>(ds);
|
||||
GSTexture12* oldD12Rt = m_tfx_textures_uav[0] != m_null_texture.get() ? m_tfx_textures_uav[0] : nullptr;
|
||||
GSTexture12* oldD12Ds = m_tfx_textures_uav[1] != m_null_texture.get() ? m_tfx_textures_uav[1] : nullptr;
|
||||
|
||||
if (!(d12Rt || d12Ds || oldD12Rt || oldD12Ds))
|
||||
return;
|
||||
|
||||
pxAssert(!(d12Rt || d12Ds) || m_features.rov);
|
||||
|
||||
if (d12Rt)
|
||||
{
|
||||
PSSetShaderResource(TEXTURE_RT_UAV, d12Rt, true, ResourceType::UAV);
|
||||
|
||||
// Unbind conflicting RT texture
|
||||
PSSetShaderResource(TEXTURE_RT, nullptr, false);
|
||||
|
||||
// Unbind conflicting source texture
|
||||
if (m_tfx_textures[TEXTURE_TEXTURE] == d12Rt->GetSRVDescriptor() ||
|
||||
m_tfx_textures[TEXTURE_TEXTURE] == d12Rt->GetFBLDescriptor())
|
||||
{
|
||||
PSSetShaderResource(TEXTURE_TEXTURE, nullptr, false);
|
||||
}
|
||||
|
||||
if (write_rt)
|
||||
{
|
||||
d12Rt->SetState(GSTexture::State::Dirty);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Unbind to avoid conflicts with OM targets.
|
||||
PSSetShaderResource(TEXTURE_RT_UAV, nullptr, false);
|
||||
}
|
||||
|
||||
if (d12Ds)
|
||||
{
|
||||
PSSetShaderResource(TEXTURE_DEPTH_UAV, d12Ds, true, ResourceType::UAV);
|
||||
|
||||
// Unbind conflicting depth texture
|
||||
PSSetShaderResource(TEXTURE_DEPTH, nullptr, false);
|
||||
|
||||
// Unbind conflicting source texture
|
||||
if (m_tfx_textures[TEXTURE_TEXTURE] == d12Ds->GetSRVDescriptor())
|
||||
{
|
||||
PSSetShaderResource(TEXTURE_TEXTURE, nullptr, false);
|
||||
}
|
||||
|
||||
if (write_ds)
|
||||
{
|
||||
d12Ds->SetState(GSTexture::State::Dirty);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Unbind to avoid conflicts with OM targets.
|
||||
PSSetShaderResource(TEXTURE_DEPTH_UAV, nullptr, false);
|
||||
}
|
||||
}
|
||||
|
||||
void GSDevice12::SetUtilityRootSignature()
|
||||
{
|
||||
if (m_current_root_signature == RootSignature::Utility)
|
||||
@@ -3578,7 +3648,8 @@ void GSDevice12::SetUtilityPushConstants(const void* data, u32 size)
|
||||
|
||||
void GSDevice12::UnbindTexture(GSTexture12* tex)
|
||||
{
|
||||
for (u32 i = 0; i < NUM_TOTAL_TFX_TEXTURES; i++)
|
||||
// Source / palette
|
||||
for (u32 i = 0; i <= TEXTURE_PALETTE; i++)
|
||||
{
|
||||
if (m_tfx_textures[i] == tex->GetSRVDescriptor() || m_tfx_textures[i] == tex->GetFBLDescriptor())
|
||||
{
|
||||
@@ -3586,6 +3657,28 @@ void GSDevice12::UnbindTexture(GSTexture12* tex)
|
||||
m_dirty_flags |= DIRTY_FLAG_TFX_TEXTURES;
|
||||
}
|
||||
}
|
||||
|
||||
// RT / primid / depth
|
||||
for (u32 i = TEXTURE_RT; i <= TEXTURE_DEPTH; i++)
|
||||
{
|
||||
if (m_tfx_textures[i] == tex->GetSRVDescriptor() || m_tfx_textures[i] == tex->GetFBLDescriptor())
|
||||
{
|
||||
m_tfx_textures[i] = m_null_texture->GetSRVDescriptor();
|
||||
m_dirty_flags |= DIRTY_FLAG_TFX_RT_TEXTURES;
|
||||
}
|
||||
}
|
||||
|
||||
// RT UAV / depth UAV
|
||||
for (u32 i = TEXTURE_RT_UAV; i <= TEXTURE_DEPTH_UAV; i++)
|
||||
{
|
||||
if (m_tfx_textures_uav[i - TEXTURE_RT_UAV] == tex)
|
||||
{
|
||||
m_tfx_textures_uav[i - TEXTURE_RT_UAV] = nullptr;
|
||||
m_tfx_textures[i] = m_null_texture->GetUAVDescriptor();
|
||||
m_dirty_flags |= DIRTY_FLAG_TFX_RT_TEXTURES;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_current_render_target == tex)
|
||||
{
|
||||
EndRenderPass();
|
||||
@@ -3714,7 +3807,7 @@ void GSDevice12::BeginRenderPass(D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE color_b
|
||||
if (color_begin == D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_CLEAR)
|
||||
{
|
||||
LookupNativeFormat(m_current_render_target->GetFormat(), nullptr,
|
||||
&rt[num_rts].BeginningAccess.Clear.ClearValue.Format, nullptr, nullptr);
|
||||
&rt[num_rts].BeginningAccess.Clear.ClearValue.Format, nullptr, nullptr, nullptr);
|
||||
GSVector4::store<false>(rt[num_rts].BeginningAccess.Clear.ClearValue.Color, clear_color);
|
||||
}
|
||||
num_rts++;
|
||||
@@ -3737,7 +3830,7 @@ void GSDevice12::BeginRenderPass(D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE color_b
|
||||
if (depth_begin == D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_CLEAR)
|
||||
{
|
||||
LookupNativeFormat(m_current_depth_target->GetFormat(), nullptr, nullptr, nullptr,
|
||||
&ds.DepthBeginningAccess.Clear.ClearValue.Format);
|
||||
&ds.DepthBeginningAccess.Clear.ClearValue.Format, nullptr);
|
||||
ds.DepthBeginningAccess.Clear.ClearValue.DepthStencil.Depth = clear_depth;
|
||||
}
|
||||
ds.StencilEndingAccess.Type = stencil_end;
|
||||
@@ -3745,7 +3838,7 @@ void GSDevice12::BeginRenderPass(D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE color_b
|
||||
if (stencil_begin == D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_CLEAR)
|
||||
{
|
||||
LookupNativeFormat(m_current_depth_target->GetFormat(), nullptr, nullptr, nullptr,
|
||||
&ds.StencilBeginningAccess.Clear.ClearValue.Format);
|
||||
&ds.StencilBeginningAccess.Clear.ClearValue.Format, nullptr);
|
||||
ds.StencilBeginningAccess.Clear.ClearValue.DepthStencil.Stencil = clear_stencil;
|
||||
}
|
||||
}
|
||||
@@ -3847,6 +3940,10 @@ __ri void GSDevice12::ApplyBaseState(u32 flags, ID3D12GraphicsCommandList* cmdli
|
||||
{
|
||||
cmdlist->OMSetRenderTargets(0, nullptr, FALSE, m_current_depth_read_only ? &m_current_depth_target->GetReadDepthViewDescriptor().cpu_handle : &m_current_depth_target->GetWriteDescriptor().cpu_handle);
|
||||
}
|
||||
else
|
||||
{
|
||||
cmdlist->OMSetRenderTargets(0, nullptr, FALSE, nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3925,7 +4022,7 @@ bool GSDevice12::ApplyTFXState(bool already_execed)
|
||||
|
||||
if (flags & DIRTY_FLAG_TFX_RT_TEXTURES)
|
||||
{
|
||||
if (!GetTextureGroupDescriptors(&m_tfx_rt_textures_handle_gpu, m_tfx_textures.data() + 2, 3))
|
||||
if (!GetTextureGroupDescriptors(&m_tfx_rt_textures_handle_gpu, m_tfx_textures.data() + 2, NUM_TFX_RT_TEXTURES + NUM_TFX_UAV_TEXTURES))
|
||||
{
|
||||
ExecuteCommandListAndRestartRenderPass(false, "Ran out of TFX RT descriptor descriptor groups");
|
||||
return ApplyTFXState(true);
|
||||
@@ -4161,10 +4258,14 @@ void GSDevice12::FeedbackBarrier(const GSTexture12* texture)
|
||||
void GSDevice12::RenderHW(GSHWDrawConfig& config)
|
||||
{
|
||||
GSTexture12* colclip_rt = static_cast<GSTexture12*>(g_gs_device->GetColorClipTexture());
|
||||
GSTexture12* draw_rt = static_cast<GSTexture12*>(config.rt);
|
||||
GSTexture12* draw_ds = static_cast<GSTexture12*>(config.ds);
|
||||
GSTexture12* draw_rt = config.ps.HasColorROV() ? nullptr : static_cast<GSTexture12*>(config.rt);
|
||||
GSTexture12* draw_ds = config.ps.HasDepthROV() ? nullptr : static_cast<GSTexture12*>(config.ds);
|
||||
GSTexture12* draw_rt_rov = config.ps.HasColorROV() ? static_cast<GSTexture12*>(config.rt) : nullptr;
|
||||
GSTexture12* draw_ds_rov = config.ps.HasDepthROV() ? static_cast<GSTexture12*>(config.ds) : nullptr;
|
||||
GSTexture12* draw_rt_clone = nullptr;
|
||||
|
||||
const bool feedback = draw_rt && (config.require_one_barrier || (config.require_full_barrier && m_features.texture_barrier) || (config.tex && config.tex == config.rt));
|
||||
|
||||
// Align the render area to 128x128, hopefully avoiding render pass restarts for small render area changes (e.g. Ratchet and Clank).
|
||||
const GSVector2i rtsize(config.rt ? config.rt->GetSize() : config.ds->GetSize());
|
||||
|
||||
@@ -4200,6 +4301,9 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
|
||||
|
||||
Recycle(colclip_rt);
|
||||
g_gs_device->SetColorClipTexture(nullptr);
|
||||
|
||||
colclip_rt = nullptr;
|
||||
draw_rt = config.ps.HasColorROV() ? nullptr : static_cast<GSTexture12*>(config.rt);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -4314,18 +4418,18 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
|
||||
{
|
||||
// avoid restarting the render pass just to switch from rt+depth to rt and vice versa
|
||||
// keep the depth even if doing colclip hw draws, because the next draw will probably re-enable depth
|
||||
if (!draw_rt && m_current_render_target && config.tex != m_current_render_target &&
|
||||
m_current_render_target->GetSize() == draw_ds->GetSize())
|
||||
if (!(draw_rt || draw_rt_rov) && m_current_render_target && config.tex != m_current_render_target &&
|
||||
draw_ds && m_current_render_target->GetSize() == draw_ds->GetSize())
|
||||
{
|
||||
draw_rt = m_current_render_target;
|
||||
m_pipeline_selector.rt = true;
|
||||
}
|
||||
else if (!draw_ds && m_current_depth_target && config.tex != m_current_depth_target &&
|
||||
m_current_depth_target->GetSize() == draw_rt->GetSize())
|
||||
{
|
||||
draw_ds = m_current_depth_target;
|
||||
m_pipeline_selector.ds = true;
|
||||
}
|
||||
}
|
||||
else if (!(draw_ds || draw_ds_rov) && m_current_depth_target && config.tex != m_current_depth_target &&
|
||||
draw_rt && m_current_depth_target->GetSize() == draw_rt->GetSize())
|
||||
{
|
||||
draw_ds = m_current_depth_target;
|
||||
m_pipeline_selector.ds = true;
|
||||
}
|
||||
|
||||
GSTexture12* draw_ds_as_rt = static_cast<GSTexture12*>(m_ds_as_rt);
|
||||
@@ -4377,10 +4481,13 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
|
||||
else
|
||||
Console.Warning("D3D12: Failed to allocate temp texture for RT copy.");
|
||||
}
|
||||
|
||||
|
||||
PSSetUnorderedAccess(draw_rt_rov, draw_ds_rov, config.ps.HasColorOutput(), config.ps.HasDepthROVWrite());
|
||||
|
||||
// For depth testing and sampling, use a read only dsv, otherwise use a write dsv
|
||||
OMSetRenderTargets(draw_rt, draw_ds_as_rt, draw_ds, config.scissor,
|
||||
config.tex && (config.tex == config.ds || config.ps.IsFeedbackLoopDepth()) && !config.depth.zwe);
|
||||
config.tex && (config.tex == draw_ds || config.ps.IsFeedbackLoopDepth()) && !config.depth.zwe && !config.ps.HasDepthROV(),
|
||||
config.rt ? config.rt->GetSize() : config.ds->GetSize());
|
||||
|
||||
// DX12 equivalent of vkCmdClearAttachments for StencilOne
|
||||
if (config.destination_alpha == GSHWDrawConfig::DestinationAlphaMode::StencilOne)
|
||||
@@ -4435,14 +4542,15 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
|
||||
// Restore original scissor, not sure if needed since the render pass has already been started. But to be safe.
|
||||
OMSetRenderTargets(draw_rt, nullptr, draw_ds, config.scissor);
|
||||
}
|
||||
|
||||
// VB/IB upload, if we did DATE setup and it's not colclip hw this has already been done
|
||||
SetPrimitiveTopology(s_primitive_topology_mapping[static_cast<u8>(config.topology)]);
|
||||
if (!date_image || colclip_rt)
|
||||
UploadHWDrawVerticesAndIndices(config);
|
||||
|
||||
// now we can do the actual draw
|
||||
SendHWDraw(pipe, config, draw_rt, draw_ds_as_rt, feedback_rt, feedback_depth,
|
||||
config.require_one_barrier, config.require_full_barrier);
|
||||
SendHWDraw(pipe, config, draw_rt, draw_ds_as_rt, draw_rt_rov, draw_ds_rov,
|
||||
feedback_rt, feedback_depth, config.require_one_barrier, config.require_full_barrier);
|
||||
|
||||
// blend second pass
|
||||
if (config.blend_multi_pass.enable)
|
||||
@@ -4472,8 +4580,9 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
|
||||
pipe.cms = config.alpha_second_pass.colormask;
|
||||
pipe.dss = config.alpha_second_pass.depth;
|
||||
pipe.bs = config.blend;
|
||||
SendHWDraw(pipe, config, draw_rt, draw_ds_as_rt, feedback_rt, feedback_depth,
|
||||
config.alpha_second_pass.require_one_barrier, config.alpha_second_pass.require_full_barrier);
|
||||
SendHWDraw(pipe, config, draw_rt, draw_ds_as_rt, draw_rt_rov, draw_ds_rov,
|
||||
feedback_rt, feedback_depth, config.alpha_second_pass.require_one_barrier,
|
||||
config.alpha_second_pass.require_full_barrier);
|
||||
}
|
||||
|
||||
if (date_image)
|
||||
@@ -4516,9 +4625,13 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
|
||||
}
|
||||
|
||||
void GSDevice12::SendHWDraw(const PipelineSelector& pipe, const GSHWDrawConfig& config, GSTexture12* draw_rt,
|
||||
GSTexture12* draw_ds, const bool feedback_rt, const bool feedback_depth, const bool one_barrier,
|
||||
const bool full_barrier)
|
||||
GSTexture12* draw_ds, GSTexture12* draw_rt_rov, GSTexture12* draw_ds_rov,
|
||||
const bool feedback_rt, const bool feedback_depth,
|
||||
const bool one_barrier, const bool full_barrier)
|
||||
{
|
||||
// Should not be mixing ROVs with barriers.
|
||||
pxAssert(!(draw_rt_rov || draw_ds_rov) || !(one_barrier || full_barrier));
|
||||
|
||||
const int n_barriers = static_cast<int>(feedback_rt) + static_cast<int>(feedback_depth);
|
||||
|
||||
if (!m_features.texture_barrier) [[unlikely]]
|
||||
@@ -4535,11 +4648,11 @@ void GSDevice12::SendHWDraw(const PipelineSelector& pipe, const GSHWDrawConfig&
|
||||
Console.Warning("D3D12: Possible unnecessary barrier detected.");
|
||||
#endif
|
||||
if ((one_barrier || full_barrier) && feedback_rt)
|
||||
PSSetShaderResource(2, draw_rt, false, true);
|
||||
PSSetShaderResource(2, draw_rt, false, ResourceType::FBL);
|
||||
if (config.tex_hazard == GSHWDrawConfig::TEX_HAZARD_RT)
|
||||
PSSetShaderResource(0, draw_rt, false, true);
|
||||
PSSetShaderResource(0, draw_rt, false, ResourceType::FBL);
|
||||
if ((one_barrier || full_barrier) && feedback_depth)
|
||||
PSSetShaderResource(4, draw_ds, false, true);
|
||||
PSSetShaderResource(4, draw_ds, false, ResourceType::FBL);
|
||||
|
||||
if (full_barrier)
|
||||
{
|
||||
@@ -4580,6 +4693,9 @@ void GSDevice12::SendHWDraw(const PipelineSelector& pipe, const GSHWDrawConfig&
|
||||
|
||||
if (BindDrawPipeline(pipe))
|
||||
Draw(config);
|
||||
|
||||
if (config.ps.HasColorROV() || config.ps.HasDepthROV())
|
||||
g_perfmon.Put(GSPerfMon::DrawCallsROV, 1);
|
||||
}
|
||||
|
||||
void GSDevice12::UpdateHWPipelineSelector(GSHWDrawConfig& config)
|
||||
@@ -4587,14 +4703,14 @@ void GSDevice12::UpdateHWPipelineSelector(GSHWDrawConfig& config)
|
||||
m_pipeline_selector.vs.key = config.vs.key;
|
||||
m_pipeline_selector.ps.key_hi = config.ps.key_hi;
|
||||
m_pipeline_selector.ps.key_lo = config.ps.key_lo;
|
||||
m_pipeline_selector.dss.key = config.depth.key;
|
||||
m_pipeline_selector.bs.key = config.blend.key;
|
||||
m_pipeline_selector.dss.key = config.ps.HasDepthROV() ? GSHWDrawConfig::DepthStencilSelector::NoDepth().key : config.depth.key;
|
||||
m_pipeline_selector.bs.key = config.ps.HasColorROV() ? GSHWDrawConfig::BlendState().key : config.blend.key;
|
||||
m_pipeline_selector.bs.constant = 0; // don't dupe states with different alpha values
|
||||
m_pipeline_selector.cms.key = config.colormask.key;
|
||||
m_pipeline_selector.cms.key = config.ps.HasColorROV() ? GSHWDrawConfig::ColorMaskSelector().key : config.colormask.key;
|
||||
m_pipeline_selector.topology = static_cast<u32>(config.topology);
|
||||
m_pipeline_selector.rt = config.rt != nullptr;
|
||||
m_pipeline_selector.ds = config.ds != nullptr;
|
||||
m_pipeline_selector.ds_as_rt = m_ds_as_rt != nullptr;
|
||||
m_pipeline_selector.rt = config.rt != nullptr && !config.ps.HasColorROV();
|
||||
m_pipeline_selector.ds = config.ds != nullptr && !config.ps.HasDepthROV();
|
||||
m_pipeline_selector.ds_as_rt = m_ds_as_rt != nullptr && !config.ps.HasDepthROV();
|
||||
}
|
||||
|
||||
void GSDevice12::UploadHWDrawVerticesAndIndices(GSHWDrawConfig& config)
|
||||
|
||||
@@ -56,6 +56,44 @@ public:
|
||||
D3D12_RESOURCE_DESC desc;
|
||||
};
|
||||
|
||||
enum class ResourceType
|
||||
{
|
||||
SRV, // Shader resource view
|
||||
FBL, // Feedback loop
|
||||
UAV, // Unordered access
|
||||
};
|
||||
|
||||
D3D12DescriptorHandle GetResourceDescriptor(GSTexture12* tex, ResourceType type) const
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case ResourceType::SRV:
|
||||
return tex->GetSRVDescriptor();
|
||||
case ResourceType::FBL:
|
||||
return m_enhanced_barriers ? tex->GetSRVDescriptor() : tex->GetFBLDescriptor();
|
||||
case ResourceType::UAV:
|
||||
return tex->GetUAVDescriptor();
|
||||
default:
|
||||
pxFailRel("Impossible.");
|
||||
return D3D12DescriptorHandle{ 0, 0 };
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr GSTexture12::ResourceState GetResourceState(ResourceType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case ResourceType::SRV:
|
||||
case ResourceType::FBL:
|
||||
return GSTexture12::ResourceState::PixelShaderResource;
|
||||
case ResourceType::UAV:
|
||||
return GSTexture12::ResourceState::PixelShaderUAV;
|
||||
default:
|
||||
pxFailRel("Impossible.");
|
||||
return static_cast<GSTexture12::ResourceState>(-1);
|
||||
}
|
||||
}
|
||||
|
||||
__fi IDXGIAdapter1* GetAdapter() const { return m_adapter.get(); }
|
||||
__fi ID3D12Device* GetDevice() const { return m_device.get(); }
|
||||
__fi ID3D12CommandQueue* GetCommandQueue() const { return m_command_queue.get(); }
|
||||
@@ -96,7 +134,6 @@ public:
|
||||
D3D12DescriptorHeapManager& GetRTVHeapManager() { return m_rtv_heap_manager; }
|
||||
D3D12DescriptorHeapManager& GetDSVHeapManager() { return m_dsv_heap_manager; }
|
||||
D3D12DescriptorHeapManager& GetSamplerHeapManager() { return m_sampler_heap_manager; }
|
||||
const D3D12DescriptorHandle& GetNullSRVDescriptor() const { return m_null_srv_descriptor; }
|
||||
D3D12StreamBuffer& GetTextureStreamBuffer() { return m_texture_stream_buffer; }
|
||||
|
||||
// Root signature access.
|
||||
@@ -198,7 +235,6 @@ private:
|
||||
D3D12DescriptorHeapManager m_rtv_heap_manager;
|
||||
D3D12DescriptorHeapManager m_dsv_heap_manager;
|
||||
D3D12DescriptorHeapManager m_sampler_heap_manager;
|
||||
D3D12DescriptorHandle m_null_srv_descriptor;
|
||||
|
||||
D3D_FEATURE_LEVEL m_feature_level = D3D_FEATURE_LEVEL_11_0;
|
||||
|
||||
@@ -224,14 +260,13 @@ public:
|
||||
GSHWDrawConfig::VSSelector vs;
|
||||
GSHWDrawConfig::DepthStencilSelector dss;
|
||||
GSHWDrawConfig::ColorMaskSelector cms;
|
||||
u8 pad;
|
||||
|
||||
__fi bool operator==(const PipelineSelector& p) const { return BitEqual(*this, p); }
|
||||
__fi bool operator!=(const PipelineSelector& p) const { return !BitEqual(*this, p); }
|
||||
|
||||
__fi PipelineSelector() { std::memset(this, 0, sizeof(*this)); }
|
||||
};
|
||||
static_assert(sizeof(PipelineSelector) == 24, "Pipeline selector is 24 bytes");
|
||||
static_assert(sizeof(PipelineSelector) == 32, "Pipeline selector is 32 bytes");
|
||||
|
||||
struct PipelineSelectorHash
|
||||
{
|
||||
@@ -282,11 +317,14 @@ public:
|
||||
TEXTURE_RT = 2,
|
||||
TEXTURE_PRIMID = 3,
|
||||
TEXTURE_DEPTH = 4,
|
||||
TEXTURE_RT_UAV = 5,
|
||||
TEXTURE_DEPTH_UAV = 6,
|
||||
|
||||
NUM_TFX_CONSTANT_BUFFERS = 2,
|
||||
NUM_TFX_TEXTURES = 2,
|
||||
NUM_TFX_RT_TEXTURES = 3,
|
||||
NUM_TOTAL_TFX_TEXTURES = NUM_TFX_TEXTURES + NUM_TFX_RT_TEXTURES,
|
||||
NUM_TFX_UAV_TEXTURES = 2,
|
||||
NUM_TOTAL_TFX_TEXTURES = NUM_TFX_TEXTURES + NUM_TFX_RT_TEXTURES + NUM_TFX_UAV_TEXTURES,
|
||||
NUM_TFX_SAMPLERS = 1,
|
||||
NUM_UTILITY_TEXTURES = 1,
|
||||
NUM_UTILITY_SAMPLERS = 1,
|
||||
@@ -385,7 +423,7 @@ private:
|
||||
std::string m_tfx_source;
|
||||
|
||||
void LookupNativeFormat(GSTexture::Format format, DXGI_FORMAT* d3d_format, DXGI_FORMAT* srv_format,
|
||||
DXGI_FORMAT* rtv_format, DXGI_FORMAT* dsv_format) const;
|
||||
DXGI_FORMAT* rtv_format, DXGI_FORMAT* dsv_format, DXGI_FORMAT* uav_format) const;
|
||||
|
||||
u32 GetSwapChainBufferCount() const;
|
||||
bool CreateSwapChain();
|
||||
@@ -513,11 +551,12 @@ public:
|
||||
void IASetIndexBuffer(const void* index, size_t count);
|
||||
void VSSetIndexBuffer(const void* index, size_t count);
|
||||
|
||||
void PSSetShaderResource(int i, GSTexture* sr, bool check_state, bool feedback = false);
|
||||
void PSSetShaderResource(int i, GSTexture* sr, bool check_state, ResourceType type = ResourceType::SRV);
|
||||
void PSSetSampler(GSHWDrawConfig::SamplerSelector sel);
|
||||
void PSSetUnorderedAccess(GSTexture* rt, GSTexture* ds, bool write_rt, bool write_ds);
|
||||
|
||||
void OMSetRenderTargets(GSTexture* rt, GSTexture* ds, GSTexture* ds_as_rt, const GSVector4i& scissor,
|
||||
bool depth_read = false);
|
||||
bool depth_read = false, const GSVector2i& viewport_size = {});
|
||||
|
||||
void SetVSConstantBuffer(const GSHWDrawConfig::VSConstantBuffer& cb);
|
||||
void SetPSConstantBuffer(const GSHWDrawConfig::PSConstantBuffer& cb);
|
||||
@@ -526,8 +565,8 @@ public:
|
||||
|
||||
void RenderHW(GSHWDrawConfig& config) override;
|
||||
void SendHWDraw(const PipelineSelector& pipe, const GSHWDrawConfig& config, GSTexture12* draw_rt,
|
||||
GSTexture12* draw_ds, const bool feedback_rt, const bool feedback_depth, const bool one_barrier,
|
||||
const bool full_barrier);
|
||||
GSTexture12* draw_ds, GSTexture12* draw_rt_rov, GSTexture12* draw_ds_rov,
|
||||
const bool feedback_rt, const bool feedback_depth, const bool one_barrier, const bool full_barrier);
|
||||
|
||||
void UpdateHWPipelineSelector(GSHWDrawConfig& config);
|
||||
void UploadHWDrawVerticesAndIndices(GSHWDrawConfig& config);
|
||||
@@ -593,8 +632,8 @@ private:
|
||||
DIRTY_FLAG_PS_CONSTANT_BUFFER_BINDING = (1 << 6),
|
||||
DIRTY_FLAG_VS_VERTEX_BUFFER_BINDING = (1 << 7),
|
||||
DIRTY_FLAG_VS_INDEX_BUFFER_BINDING = (1 << 8),
|
||||
DIRTY_FLAG_TEXTURES_DESCRIPTOR_TABLE = (1 << 9),
|
||||
DIRTY_FLAG_SAMPLERS_DESCRIPTOR_TABLE = (1 << 10),
|
||||
DIRTY_FLAG_SAMPLERS_DESCRIPTOR_TABLE = (1 << 9),
|
||||
DIRTY_FLAG_TEXTURES_DESCRIPTOR_TABLE = (1 << 10),
|
||||
DIRTY_FLAG_TEXTURES_DESCRIPTOR_TABLE_2 = (1 << 11),
|
||||
DIRTY_FLAG_VS_PUSH_CONSTANTS = (1 << 12),
|
||||
|
||||
@@ -657,6 +696,7 @@ private:
|
||||
|
||||
std::array<D3D12_GPU_VIRTUAL_ADDRESS, NUM_TFX_CONSTANT_BUFFERS> m_tfx_constant_buffers{};
|
||||
std::array<D3D12DescriptorHandle, NUM_TOTAL_TFX_TEXTURES> m_tfx_textures{};
|
||||
std::array<GSTexture12*, NUM_TFX_UAV_TEXTURES> m_tfx_textures_uav{};
|
||||
D3D12DescriptorHandle m_tfx_sampler;
|
||||
u32 m_tfx_sampler_sel = 0;
|
||||
D3D12DescriptorHandle m_tfx_textures_handle_gpu;
|
||||
|
||||
@@ -110,6 +110,10 @@ void GSTexture12::Destroy(bool defer)
|
||||
}
|
||||
|
||||
m_write_descriptor_type = WriteDescriptorType::None;
|
||||
|
||||
#ifdef PCSX2_DEVBUILD
|
||||
m_debug_name.clear();
|
||||
#endif
|
||||
}
|
||||
|
||||
// For use with non-simultaneous textures only.
|
||||
@@ -203,6 +207,7 @@ std::unique_ptr<GSTexture12> GSTexture12::Create(Type type, Format format, int w
|
||||
desc.desc1.Flags = (levels > 1 && !IsCompressedFormat(format)) ? D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET :
|
||||
D3D12_RESOURCE_FLAG_NONE;
|
||||
state = ResourceState::CopyDst;
|
||||
pxAssert(uav_format == DXGI_FORMAT_UNKNOWN);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -217,6 +222,10 @@ std::unique_ptr<GSTexture12> GSTexture12::Create(Type type, Format format, int w
|
||||
desc.desc1.Layout = D3D12_TEXTURE_LAYOUT_64KB_UNDEFINED_SWIZZLE;
|
||||
optimized_clear_value.Format = rtv_format;
|
||||
state = ResourceState::RenderTarget;
|
||||
if (uav_format != DXGI_FORMAT_UNKNOWN)
|
||||
{
|
||||
desc.desc1.Flags |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -227,6 +236,7 @@ std::unique_ptr<GSTexture12> GSTexture12::Create(Type type, Format format, int w
|
||||
desc.desc1.Flags = D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL;
|
||||
optimized_clear_value.Format = dsv_format;
|
||||
state = ResourceState::DepthWriteStencil;
|
||||
pxAssert(uav_format == DXGI_FORMAT_UNKNOWN);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -235,6 +245,8 @@ std::unique_ptr<GSTexture12> GSTexture12::Create(Type type, Format format, int w
|
||||
pxAssert(levels == 1);
|
||||
allocationDesc.Flags |= D3D12MA::ALLOCATION_FLAG_COMMITTED;
|
||||
state = ResourceState::PixelShaderResource;
|
||||
pxAssert(uav_format != DXGI_FORMAT_UNKNOWN);
|
||||
desc.desc1.Flags |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -242,9 +254,6 @@ std::unique_ptr<GSTexture12> GSTexture12::Create(Type type, Format format, int w
|
||||
return {};
|
||||
}
|
||||
|
||||
if (uav_format != DXGI_FORMAT_UNKNOWN)
|
||||
desc.desc1.Flags |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
|
||||
|
||||
wil::com_ptr_nothrow<ID3D12Resource> resource;
|
||||
wil::com_ptr_nothrow<ID3D12Resource> resource_fbl;
|
||||
wil::com_ptr_nothrow<D3D12MA::Allocation> allocation;
|
||||
@@ -330,6 +339,12 @@ std::unique_ptr<GSTexture12> GSTexture12::Create(Type type, Format format, int w
|
||||
dev->GetDescriptorHeapManager().Free(&srv_descriptor);
|
||||
return {};
|
||||
}
|
||||
if (uav_format != DXGI_FORMAT_UNKNOWN && !CreateUAVDescriptor(resource.get(), uav_format, &uav_descriptor))
|
||||
{
|
||||
dev->GetRTVHeapManager().Free(&write_descriptor);
|
||||
dev->GetDescriptorHeapManager().Free(&srv_descriptor);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -350,26 +365,15 @@ std::unique_ptr<GSTexture12> GSTexture12::Create(Type type, Format format, int w
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (uav_format != DXGI_FORMAT_UNKNOWN && !CreateUAVDescriptor(resource.get(), dsv_format, &uav_descriptor))
|
||||
{
|
||||
switch (write_descriptor_type)
|
||||
case Type::RWTexture:
|
||||
{
|
||||
case WriteDescriptorType::RTV:
|
||||
dev->GetRTVHeapManager().Free(&write_descriptor);
|
||||
break;
|
||||
case WriteDescriptorType::DSV:
|
||||
dev->GetDSVHeapManager().Free(&ro_dsv_descriptor);
|
||||
dev->GetDSVHeapManager().Free(&write_descriptor);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
if (uav_format != DXGI_FORMAT_UNKNOWN && !CreateUAVDescriptor(resource.get(), uav_format, &uav_descriptor))
|
||||
{
|
||||
dev->GetDescriptorHeapManager().Free(&srv_descriptor);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
dev->GetDescriptorHeapManager().Free(&srv_descriptor);
|
||||
return {};
|
||||
break;
|
||||
}
|
||||
|
||||
// Feedback descriptor used with legacy barriers
|
||||
@@ -442,7 +446,7 @@ std::unique_ptr<GSTexture12> GSTexture12::Adopt(wil::com_ptr_nothrow<ID3D12Resou
|
||||
|
||||
if (uav_format != DXGI_FORMAT_UNKNOWN)
|
||||
{
|
||||
if (!CreateUAVDescriptor(resource.get(), srv_format, &uav_descriptor))
|
||||
if (!CreateUAVDescriptor(resource.get(), uav_format, &uav_descriptor))
|
||||
{
|
||||
switch (write_descriptor_type)
|
||||
{
|
||||
@@ -796,6 +800,8 @@ void GSTexture12::SetDebugName(std::string_view name)
|
||||
return;
|
||||
|
||||
D3D12::SetObjectName(m_resource.get(), name);
|
||||
|
||||
m_debug_name = name;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1137,6 +1143,14 @@ void GSTexture12::TransitionSubresourceToState(const D3D12CommandList& cmdlist,
|
||||
|
||||
cmdlist.list4->ResourceBarrier(num_barriers, barriers);
|
||||
}
|
||||
|
||||
// Count as a UAV barrier if we transition to/from UAV.
|
||||
if (IsRenderTargetOrDepthStencil() &&
|
||||
(before_state == ResourceState::PixelShaderUAV || after_state == ResourceState::PixelShaderUAV))
|
||||
{
|
||||
g_perfmon.Put(GSPerfMon::Barriers, 1);
|
||||
g_perfmon.Put(GSPerfMon::BarriersROV, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void GSTexture12::CommitClear()
|
||||
|
||||
@@ -56,6 +56,8 @@ public:
|
||||
__fi ID3D12Resource* GetResource() const { return m_resource.get(); }
|
||||
__fi ID3D12Resource* GetFBLResource() const { return m_resource_fbl.get(); }
|
||||
|
||||
virtual bool IsUnorderedAccess() const override { return GetResourceState() == ResourceState::PixelShaderUAV; }
|
||||
|
||||
void* GetNativeHandle() const override;
|
||||
|
||||
bool Update(const GSVector4i& r, const void* data, int pitch, int layer = 0) override;
|
||||
|
||||
@@ -6825,6 +6825,13 @@ void GSRendererHW::EmulateBlending(int rt_alpha_min, int rt_alpha_max, DATEOptio
|
||||
m_conf.ps.blend_d = 2;
|
||||
}
|
||||
|
||||
// Save in case needed for ROV setup.
|
||||
m_optimized_blend.A = m_conf.ps.blend_a;
|
||||
m_optimized_blend.B = m_conf.ps.blend_b;
|
||||
m_optimized_blend.C = m_conf.ps.blend_c;
|
||||
m_optimized_blend.D = m_conf.ps.blend_d;
|
||||
m_optimized_blend.FIX = AFIX;
|
||||
|
||||
// TODO: blend_ad_alpha_masked, as well as other blend cases can be optimized on dx11/dx12/gl to use
|
||||
// blend multipass more which might be faster, vk likely won't benefit as barriers are already fast.
|
||||
|
||||
@@ -7478,6 +7485,396 @@ void GSRendererHW::EmulateBlending(int rt_alpha_min, int rt_alpha_max, DATEOptio
|
||||
}
|
||||
}
|
||||
|
||||
// In certain cases using a ROV with depth or color will force the other one
|
||||
// because of how color and depth interact via testing.
|
||||
__fi void GSRendererHW::GetForcedROVUsage(bool& rov_color, bool& rov_depth)
|
||||
{
|
||||
// Cannot force if both are already enabled or disabled.
|
||||
if (rov_color == rov_depth)
|
||||
return;
|
||||
|
||||
// If depth and color have feedback and one uses ROV, the other must also.
|
||||
// We currently don't have a way of using barriers in one and ROV in the other.
|
||||
if ((m_conf.ps.IsFeedbackLoopRT() && rov_depth) || (m_conf.ps.IsFeedbackLoopDepth() && rov_color))
|
||||
{
|
||||
GL_INS("ROV: Feedback compatibility forces color and depth ROV");
|
||||
rov_color = true;
|
||||
rov_depth = true;
|
||||
}
|
||||
|
||||
// If we use color ROV with discard or the pixel shader writes to depth,
|
||||
// we cannot use early depth stencil, so must use depth ROV with feedback.
|
||||
// Same applies in reverse for depth ROV forcing color ROV with feedback.
|
||||
const bool color_write = (m_conf.colormask.wrgba != 0);
|
||||
const bool depth_test = m_cached_ctx.DepthRead();
|
||||
|
||||
// Separate flag for DATE since they are many methods and the interaction with ROV is not clear.
|
||||
const bool date = m_cached_ctx.TEST.DATE;
|
||||
|
||||
if (m_conf.ds && rov_color && (m_conf.ps.HasShaderDiscard() || m_conf.ps.HasDepthOutput() || date))
|
||||
{
|
||||
GL_INS("ROV: Color ROV with shader discard/depth write forces depth ROV");
|
||||
rov_depth = true;
|
||||
}
|
||||
else if (m_conf.rt && color_write && rov_depth && (m_conf.ps.HasShaderDiscard() || depth_test || date))
|
||||
{
|
||||
GL_INS("ROV: Depth ROV with shader discard forces color ROV");
|
||||
rov_color = true;
|
||||
}
|
||||
}
|
||||
|
||||
void GSRendererHW::DetermineROVUsage(GSTextureCache::Target* rt, GSTextureCache::Target* ds)
|
||||
{
|
||||
const GSDevice::FeatureSupport& features = g_gs_device->Features();
|
||||
|
||||
if (!(GSConfig.HWROV && features.rov))
|
||||
return;
|
||||
|
||||
GL_PUSH("HW: ROV Setup");
|
||||
|
||||
if (features.framebuffer_fetch)
|
||||
{
|
||||
GL_INS("ROV: Disabled because have FB-fetch");
|
||||
return;
|
||||
}
|
||||
|
||||
if (rt && rt->m_texture == m_conf.tex && !m_conf.ps.tex_is_fb)
|
||||
{
|
||||
GL_INS("ROV: Disabled because tex is RT and not sampling from current pixel");
|
||||
return;
|
||||
}
|
||||
|
||||
if (ds && ds->m_texture == m_conf.tex)
|
||||
{
|
||||
GL_INS("ROV: Disabled because tex is depth");
|
||||
return;
|
||||
}
|
||||
|
||||
const bool color_write = rt && m_conf.colormask.wrgba != 0;
|
||||
const bool depth_write = ds && m_cached_ctx.DepthWrite();
|
||||
|
||||
const u32 colormask = GSUtil::GetChannelMask(m_cached_ctx.FRAME.PSM) & m_conf.colormask.wrgba;
|
||||
const bool colormask_needs_rt = colormask != 0xF;
|
||||
|
||||
const u32 ate = m_cached_ctx.TEST.ATE;
|
||||
const u32 atst = m_cached_ctx.TEST.ATST;
|
||||
const u32 afail = m_cached_ctx.TEST.AFAIL;
|
||||
|
||||
const bool afail_needs_rt = ate && ((afail == AFAIL_ZB_ONLY) || (afail == AFAIL_RGB_ONLY));
|
||||
const bool afail_needs_depth = ate && ((afail == AFAIL_FB_ONLY) || (afail == AFAIL_RGB_ONLY));
|
||||
|
||||
const bool blend = m_conf.IsBlending();
|
||||
const bool blend_needs_rt = blend &&
|
||||
(m_optimized_blend.A == ALPHA_ABD_CD || m_optimized_blend.B == ALPHA_ABD_CD ||
|
||||
m_optimized_blend.C == ALPHA_C_AD || m_optimized_blend.D == ALPHA_ABD_CD);
|
||||
|
||||
const bool two_pass_alpha = GSHWDrawConfig::HasAlphaTestSecondPass(m_conf.alpha_test);
|
||||
|
||||
const bool date = m_conf.destination_alpha != GSHWDrawConfig::DestinationAlphaMode::Off;
|
||||
|
||||
const bool ztst = m_cached_ctx.DepthRead();
|
||||
|
||||
const bool full_barrier = m_conf.require_full_barrier;
|
||||
|
||||
// Heuristically determine what ROVs would be needed to eliminate passes based on the current config.
|
||||
const bool multipass_color = (full_barrier && m_conf.ps.IsFeedbackLoopRT()) ||
|
||||
two_pass_alpha ||
|
||||
m_conf.blend_multi_pass.enable;
|
||||
|
||||
const bool multipass_depth = (full_barrier && m_conf.ps.IsFeedbackLoopDepth()) || two_pass_alpha;
|
||||
|
||||
// If already ROV, just continue the usage.
|
||||
const bool color_is_rov = rt && rt->m_texture->IsUnorderedAccess();
|
||||
const bool depth_is_rov = ds && ds->m_texture->IsDepthColor();
|
||||
|
||||
bool use_rov_color = (color_write && multipass_color) || color_is_rov;
|
||||
bool use_rov_depth = (depth_write && multipass_depth) || depth_is_rov;
|
||||
|
||||
// In certain cases, ROV in color or depth will force ROV in the other for correctness.
|
||||
GetForcedROVUsage(use_rov_color, use_rov_depth);
|
||||
|
||||
// Get the number of barriers that would be used with the current config.
|
||||
u32 barriers = 1;
|
||||
if (full_barrier)
|
||||
{
|
||||
if (m_drawlist.size() > 0)
|
||||
{
|
||||
barriers = static_cast<u32>(m_drawlist.size()); // Already computed
|
||||
}
|
||||
else
|
||||
{
|
||||
barriers = 2; // Tells drawlist computation to stop after reaching 2.
|
||||
GetPrimitiveOverlapDrawlist(false, false, 1.0f, &barriers);
|
||||
}
|
||||
}
|
||||
|
||||
const u32 multiplier = m_conf.alpha_second_pass.enable ? 2 : 1; // Alpha second pass doubles the barriers.
|
||||
barriers *= multiplier;
|
||||
|
||||
// Heuristic: only activate ROV if we save at least one draw call by doing so.
|
||||
const bool activate = (use_rov_color != color_is_rov || use_rov_depth != depth_is_rov) && barriers >= 2;
|
||||
|
||||
if (!color_is_rov && !depth_is_rov && !activate)
|
||||
{
|
||||
// Not enough barriers or no feedback to activate, and ROV is not already active.
|
||||
GL_ROV("No ROV usage: Draw=%05lld | C=%016p | D=%016p | BAR=%d.",
|
||||
s_n, rt ? rt->m_texture : nullptr, ds ? ds->m_texture : nullptr, barriers);
|
||||
return;
|
||||
}
|
||||
|
||||
if (activate)
|
||||
{
|
||||
GL_ROV("ROV activated: Draw=%05lld | C=%016p | D=%016p | BAR=%d | C=[%d=>%d] | D=[%d=>%d].",
|
||||
s_n, rt ? rt->m_texture : nullptr, ds ? ds->m_texture : nullptr, barriers,
|
||||
color_is_rov, use_rov_color, depth_is_rov, use_rov_depth);
|
||||
}
|
||||
else
|
||||
{
|
||||
GL_ROV("ROV continued: Draw=%05lld | C=%016p | D=%016p | BAR=%d | C=%d | D=%d.",
|
||||
s_n, rt ? rt->m_texture : nullptr, ds ? ds->m_texture : nullptr, barriers,
|
||||
color_is_rov, depth_is_rov);
|
||||
}
|
||||
|
||||
GL_INS("ROV: Color ROV %s / depth ROV %s",
|
||||
use_rov_color ? "enabled" : "disabled", use_rov_depth ? "enabled" : "disabled");
|
||||
|
||||
// Do the actual pipeline config.
|
||||
ConfigureROV(use_rov_color, use_rov_depth);
|
||||
}
|
||||
|
||||
void GSRendererHW::ConfigureROV(bool color_rov, bool depth_rov)
|
||||
{
|
||||
// Do the actual config for depth.
|
||||
if (depth_rov)
|
||||
{
|
||||
m_conf.depth = GSHWDrawConfig::DepthStencilSelector::NoDepth(); // Disable real depth.
|
||||
const bool depth_write = m_cached_ctx.DepthWrite();
|
||||
GL_INS("ROV: Using %s depth ROV", depth_write ? "read/write" : "read-only");
|
||||
ConfigureDepthFeedback(true);
|
||||
m_conf.ps.rov_depth = depth_write ? GSHWDrawConfig::PS_ROV_DEPTH::READ_WRITE : GSHWDrawConfig::PS_ROV_DEPTH::READ_ONLY;
|
||||
}
|
||||
|
||||
// Do the actual config for color.
|
||||
if (color_rov)
|
||||
{
|
||||
// FbMask setup
|
||||
if (m_conf.colormask.wrgba != 0)
|
||||
{
|
||||
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;
|
||||
}
|
||||
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())
|
||||
{
|
||||
GL_INS("ROV: Using SW blend%s", m_conf.blend.enable ? " and disabling HW" : "");
|
||||
m_conf.ps.blend_a = m_optimized_blend.A;
|
||||
m_conf.ps.blend_b = m_optimized_blend.B;
|
||||
m_conf.ps.blend_c = m_optimized_blend.C;
|
||||
m_conf.ps.blend_d = m_optimized_blend.D;
|
||||
|
||||
if (m_conf.ps.blend_c == ALPHA_C_FIX)
|
||||
m_conf.cb_ps.TA_MaxDepth_Af.a = m_optimized_blend.FIX / 128.0f;
|
||||
|
||||
// Disable HW or mixed blend or multipass blend
|
||||
m_conf.blend = {};
|
||||
m_conf.ps.blend_hw = false;
|
||||
m_conf.ps.blend_mix = false;
|
||||
m_conf.blend_multi_pass = {};
|
||||
|
||||
if (!m_conf.ps.no_color1)
|
||||
{
|
||||
// We should never need dual source with SW blend
|
||||
GL_INS("ROV: Disabling dual source blending");
|
||||
m_conf.ps.no_color1 = true;
|
||||
}
|
||||
|
||||
// Only needed with HW blend.
|
||||
m_conf.ps.round_inv = false;
|
||||
m_conf.ps.a_masked = false;
|
||||
}
|
||||
|
||||
// Dither setup
|
||||
if (m_conf.ps.dither)
|
||||
{
|
||||
// Only needed with HW blend.
|
||||
m_conf.ps.dither_adjust = false;
|
||||
}
|
||||
|
||||
// Destination alpha test setup
|
||||
if (m_conf.destination_alpha != GSHWDrawConfig::DestinationAlphaMode::Off)
|
||||
{
|
||||
GL_INS("ROV: Using DATE Full%s",
|
||||
(m_conf.destination_alpha != GSHWDrawConfig::DestinationAlphaMode::Full) ? " and replace current method" : "");
|
||||
|
||||
if (m_conf.destination_alpha != GSHWDrawConfig::DestinationAlphaMode::Full)
|
||||
{
|
||||
m_conf.destination_alpha = GSHWDrawConfig::DestinationAlphaMode::Full;
|
||||
m_conf.depth.date = false; // Don't use stencil with ROV
|
||||
m_conf.depth.date_one = false; // Don't use stencil with ROV
|
||||
m_conf.ps.date = 5 + m_cached_ctx.TEST.DATM; // Shader discard DATM.
|
||||
m_conf.datm = static_cast<SetDATM>(0); // Not needed
|
||||
}
|
||||
}
|
||||
|
||||
// Colclip setup
|
||||
if (m_conf.ps.colclip_hw)
|
||||
{
|
||||
// Remove HW colclip texture if needed
|
||||
GL_INS("ROV: Replacing colclip HW with SW");
|
||||
const bool has_colclip_texture = g_gs_device->GetColorClipTexture() != nullptr;
|
||||
m_conf.ps.colclip_hw = 0;
|
||||
m_conf.ps.colclip = true;
|
||||
m_conf.colclip_mode = has_colclip_texture ? GSHWDrawConfig::ColClipMode::EarlyResolve : GSHWDrawConfig::ColClipMode::NoModify;
|
||||
}
|
||||
|
||||
// PABE setup
|
||||
const bool PABE = m_draw_env->PABE.PABE && GetAlphaMinMax().min < 128;
|
||||
if (m_conf.IsBlending() && PABE && !m_conf.ps.pabe)
|
||||
{
|
||||
GL_INS("ROV: Enabling PABE");
|
||||
m_conf.ps.pabe = true;
|
||||
}
|
||||
|
||||
// Alpha test setup. KEEP will already be fine.
|
||||
if (m_cached_ctx.TEST.ATE && m_conf.alpha_test != GSHWDrawConfig::AlphaTestMode::KEEP)
|
||||
{
|
||||
GL_INS("ROV: Using SW feedback alpha test%s", m_conf.alpha_second_pass.enable ?
|
||||
" and disabling alpha second pass" : "");
|
||||
|
||||
m_conf.alpha_test = GSHWDrawConfig::AlphaTestMode::FEEDBACK;
|
||||
|
||||
GSHWDrawConfig::PS_ATST ps_atst;
|
||||
float ps_aref;
|
||||
GetAlphaTestConfigPS(m_cached_ctx.TEST.ATST, m_cached_ctx.TEST.AREF, false, ps_atst, ps_aref);
|
||||
m_conf.ps.atst = ps_atst;
|
||||
m_conf.ps.afail = static_cast<GSHWDrawConfig::PS_AFAIL>(m_cached_ctx.TEST.AFAIL);
|
||||
if (m_cached_ctx.DepthWrite() && m_cached_ctx.TEST.AFAIL == AFAIL_RGB_ONLY)
|
||||
{
|
||||
pxAssert(depth_rov); // Should have enabled depth ROV for depth feedback loop.
|
||||
m_conf.ps.afail = PS_AFAIL::RGB_ONLY_SW_Z;
|
||||
}
|
||||
m_conf.cb_ps.FogColor_AREF.a = ps_aref;
|
||||
|
||||
GL_INS("ROV: Using ATST=%d, AFAIL=%d, AREF=%.2f", ps_atst, static_cast<u32>(m_conf.ps.afail), ps_aref);
|
||||
|
||||
if (m_conf.alpha_second_pass.enable)
|
||||
{
|
||||
m_conf.alpha_second_pass = {};
|
||||
}
|
||||
}
|
||||
|
||||
m_conf.ps.rov_color = true;
|
||||
}
|
||||
|
||||
// Remove regular barriers.
|
||||
if (color_rov || depth_rov)
|
||||
{
|
||||
m_conf.require_full_barrier = false;
|
||||
m_conf.require_one_barrier = false;
|
||||
}
|
||||
}
|
||||
|
||||
void GSRendererHW::SetUnorderedAccessFlag(GSTextureCache::Target* rt)
|
||||
{
|
||||
// Set flag for ROV activation heuristic. Only used by DX11.
|
||||
// Only needed for RT, as we use a different method for depth.
|
||||
if (rt)
|
||||
{
|
||||
if (m_conf.ps.HasColorROV())
|
||||
rt->m_texture->SetUnorderedAccess();
|
||||
else
|
||||
rt->m_texture->ClearUnorderedAccess();
|
||||
}
|
||||
}
|
||||
|
||||
static void CopyDepthTextureROV(GSTexture* src, GSTexture* dst)
|
||||
{
|
||||
switch (src->GetState())
|
||||
{
|
||||
case GSTexture::State::Cleared:
|
||||
g_gs_device->ClearDepth(dst, src->GetClearDepth());
|
||||
break;
|
||||
case GSTexture::State::Invalidated:
|
||||
g_gs_device->InvalidateRenderTarget(dst);
|
||||
break;
|
||||
case GSTexture::State::Dirty:
|
||||
g_gs_device->StretchRectAuto(src, dst, Nearest);
|
||||
break;
|
||||
}
|
||||
|
||||
// These stats are counted both as part of ROV and non-ROV stats.
|
||||
g_perfmon.Put(GSPerfMon::DepthCopiesROV, 1.0);
|
||||
g_perfmon.Put(GSPerfMon::DrawCallsROV, 1.0);
|
||||
};
|
||||
|
||||
void GSRendererHW::ConvertDepthFormatROV(GSTextureCache::Target* ds)
|
||||
{
|
||||
if (!ds)
|
||||
return;
|
||||
|
||||
GSTexture* ds_tex_old = m_conf.ds;
|
||||
GSTexture* ds_tex_new = nullptr;
|
||||
|
||||
// Convert depth to depth color or vice versa if needed.
|
||||
bool depth_to_color;
|
||||
if (m_conf.ps.HasDepthROV() && !ds_tex_old->IsDepthColor())
|
||||
{
|
||||
depth_to_color = true;
|
||||
ds_tex_new = g_gs_device->CreateDepthColor(ds_tex_old->GetSize(), false, true);
|
||||
}
|
||||
else if (!m_conf.ps.HasDepthROV() && ds_tex_old->IsDepthColor())
|
||||
{
|
||||
depth_to_color = false;
|
||||
ds_tex_new = g_gs_device->CreateDepthStencil(ds->m_texture->GetSize(), false, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
GL_PUSH("HW: Convert %s for ROV.", depth_to_color ? "DepthStencil -> DepthColor" : "DepthColor -> DepthStencil");
|
||||
|
||||
CopyDepthTextureROV(ds_tex_old, ds_tex_new);
|
||||
|
||||
#if PCSX2_DEVBUILD
|
||||
ds_tex_new->SetDebugName(ds->m_texture->GetDebugName());
|
||||
#endif
|
||||
|
||||
// Fix up the texture cache.
|
||||
if (ds->m_texture == ds_tex_old)
|
||||
{
|
||||
GL_CACHE("HW: Replaced texture for DS @ 0x%04x", ds->m_TEX0.TBP0);
|
||||
ds->m_texture = ds_tex_new;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Must be the temporary Z.
|
||||
pxAssert(g_texture_cache->GetTemporaryZ() == ds_tex_old);
|
||||
GL_CACHE("HW: Replaced texture for temporary Z @ 0x%04x", g_texture_cache->GetTemporaryZInfo().ZBP);
|
||||
g_texture_cache->SetTemporaryZ(ds_tex_new);
|
||||
}
|
||||
|
||||
g_gs_device->Recycle(ds_tex_old);
|
||||
|
||||
m_conf.ds = ds_tex_new;
|
||||
}
|
||||
|
||||
__ri static constexpr bool IsRedundantClamp(u8 clamp, u32 clamp_min, u32 clamp_max, u32 tsize)
|
||||
{
|
||||
// Don't shader sample when the clamp/repeat is configured to the texture size.
|
||||
@@ -8780,14 +9177,14 @@ void GSRendererHW::EmulateAlphaTestSecondPass()
|
||||
}
|
||||
|
||||
// Setup barriers and/or SW depth testing for depth feedback.
|
||||
void GSRendererHW::ConfigureDepthFeedback()
|
||||
void GSRendererHW::ConfigureDepthFeedback(bool rov_depth)
|
||||
{
|
||||
if (m_conf.ps.IsFeedbackLoopDepth())
|
||||
if (m_conf.ps.IsFeedbackLoopDepth() || rov_depth)
|
||||
{
|
||||
const GSDevice::FeatureSupport& features = g_gs_device->Features();
|
||||
|
||||
// We need barriers for the feedback.
|
||||
if (features.feedback_loops())
|
||||
// We need barriers for the feedback (except with ROV).
|
||||
if (features.feedback_loops() && !rov_depth)
|
||||
{
|
||||
m_conf.require_one_barrier |= (m_prim_overlap == PRIM_OVERLAP_NO);
|
||||
m_conf.require_full_barrier |= (m_prim_overlap != PRIM_OVERLAP_NO);
|
||||
@@ -9037,7 +9434,13 @@ __ri void GSRendererHW::DrawPrims(GSTextureCache::Target* rt, GSTextureCache::Ta
|
||||
m_conf.ps.rta_correction = rt->m_rt_alpha_scale;
|
||||
}
|
||||
|
||||
// Call before computing the full drawlist in case ROV is used and we don't need it.
|
||||
DetermineROVUsage(rt, ds);
|
||||
ConvertDepthFormatROV(ds);
|
||||
SetUnorderedAccessFlag(rt);
|
||||
|
||||
// Barriers must be determined before indices are modified via HandleProvokingVertexFirst/SetupIA.
|
||||
// This also computes the drawlist if needed.
|
||||
DetermineBarriers(rt, tex);
|
||||
|
||||
// Perform second pass setup here once barriers are determined.
|
||||
@@ -9065,7 +9468,7 @@ __ri void GSRendererHW::DrawPrims(GSTextureCache::Target* rt, GSTextureCache::Ta
|
||||
|
||||
SetupIA(rtscale, vs_scale_x, vs_scale_y, m_channel_shuffle_width != 0, no_rt);
|
||||
|
||||
if (m_conf.ds && m_conf.ps.IsFeedbackLoopDepth() && !g_gs_device->Features().depth_feedback)
|
||||
if (m_conf.ds && m_conf.ps.IsFeedbackLoopDepth() && !g_gs_device->Features().depth_feedback && !m_conf.ps.HasDepthROV())
|
||||
{
|
||||
GL_PUSH("HW: Creating temporary R32 RT for depth feedback");
|
||||
|
||||
|
||||
@@ -236,7 +236,7 @@ private:
|
||||
static void GetAlphaTestConfigPS(const u32 atst, const u8 aref, const bool invert_test, PS_ATST& ps_atst_out, float& aref_out);
|
||||
void EmulateAlphaTest(DATEOptions& date_options);
|
||||
void EmulateAlphaTestSecondPass();
|
||||
void ConfigureDepthFeedback();
|
||||
void ConfigureDepthFeedback(bool rov_depth = false);
|
||||
|
||||
void CalculateAlphaRange(GSTextureCache::Target* rt, GSTextureCache::Target* ds, DATEOptions& date_options,
|
||||
int& blend_alpha_min, int& blend_alpha_max, int& rt_new_alpha_min, int& rt_new_alpha_max);
|
||||
@@ -253,6 +253,12 @@ private:
|
||||
const GSVector2i& unscaled_size, float& vs_scale_x, float& vs_scale_y);
|
||||
void DetermineBarriers(GSTextureCache::Target* rt, GSTextureCache::Source* tex);
|
||||
|
||||
void GetForcedROVUsage(bool& color_cov, bool& depth_rov); // Whether having color or depth with the current config forces the other.
|
||||
void DetermineROVUsage(GSTextureCache::Target* rt, GSTextureCache::Target* ds); // Heuristics to determine whether to enable/disable ROV
|
||||
void ConfigureROV(bool color_rov, bool depth_rov); // Actual config for ROV
|
||||
void SetUnorderedAccessFlag(GSTextureCache::Target* rt); // Set flag for DX11 ROV heuristic to work.
|
||||
void ConvertDepthFormatROV(GSTextureCache::Target* ds); // Convert between depth and depth color if needed.
|
||||
|
||||
void SetTCOffset();
|
||||
bool NextDrawColClip() const;
|
||||
bool IsPossibleChannelShuffle() const;
|
||||
@@ -330,6 +336,8 @@ private:
|
||||
|
||||
GSVector2i m_lod = {}; // Min & Max level of detail
|
||||
|
||||
GIFRegALPHA m_optimized_blend = {}; // Save for ROV setup
|
||||
|
||||
GSHWDrawConfig m_conf = {};
|
||||
HWCachedCtx m_cached_ctx;
|
||||
|
||||
|
||||
@@ -4893,7 +4893,7 @@ void GSTextureCache::InvalidateLocalMem(const GSOffset& off, const GSVector4i& r
|
||||
// Could be reading Z24/32 back as CT32 (Gundam Battle Assault 3)
|
||||
if (GSLocalMemory::m_psm[psm].bpp >= 16)
|
||||
{
|
||||
if (GSConfig.HWDownloadMode != GSHardwareDownloadMode::Enabled)
|
||||
if (GSConfig.HWDownloadMode > GSHardwareDownloadMode::EnabledForceFull)
|
||||
{
|
||||
DevCon.Error("TC: Skipping depth readback of %ux%u @ %u,%u", r.width(), r.height(), r.left, r.top);
|
||||
return;
|
||||
@@ -4926,7 +4926,7 @@ void GSTextureCache::InvalidateLocalMem(const GSOffset& off, const GSVector4i& r
|
||||
const bool swizzle_match = GSLocalMemory::m_psm[psm].depth == GSLocalMemory::m_psm[t->m_TEX0.PSM].depth;
|
||||
// Calculate the rect offset if the BP doesn't match.
|
||||
GSVector4i targetr = {};
|
||||
if (full_flush || t->readbacks_since_draw > 1)
|
||||
if (full_flush || GSConfig.HWDownloadMode == GSHardwareDownloadMode::EnabledForceFull || t->readbacks_since_draw > 1)
|
||||
{
|
||||
targetr = t->m_drawn_since_read;
|
||||
}
|
||||
@@ -5163,7 +5163,7 @@ void GSTextureCache::InvalidateLocalMem(const GSOffset& off, const GSVector4i& r
|
||||
}
|
||||
}
|
||||
|
||||
if (GSConfig.HWDownloadMode != GSHardwareDownloadMode::Enabled)
|
||||
if (GSConfig.HWDownloadMode > GSHardwareDownloadMode::EnabledForceFull)
|
||||
{
|
||||
DevCon.Error("TC: Skipping depth readback of %ux%u @ %u,%u", targetr.width(), targetr.height(), targetr.left, targetr.top);
|
||||
continue;
|
||||
@@ -6426,7 +6426,7 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con
|
||||
}
|
||||
|
||||
const u32 destination_tbw = (dst->m_TEX0.TBP0 == TEX0.TBP0) ? (std::max<u32>(TEX0.TBW, 1u) * 64) : std::max<u32>(dst->m_TEX0.TBW, 1u) * 128;
|
||||
if (dst->GetScale() > 1.0f)
|
||||
if (!GSConfig.UserHacks_NativePaletteDraw && dst->GetScale() > 1.0f)
|
||||
{
|
||||
GSTexture* tmpTex = use_texture ?
|
||||
g_gs_device->CreateTexture(dst->m_unscaled_size, 1, GSTexture::Format::Color, PreferReusedLabelledTexture()) :
|
||||
|
||||
@@ -101,7 +101,7 @@ struct PipelineSelectorMTL
|
||||
}
|
||||
};
|
||||
|
||||
static_assert(sizeof(PipelineSelectorMTL) == 24);
|
||||
static_assert(sizeof(PipelineSelectorMTL) == 28);
|
||||
|
||||
template <>
|
||||
struct std::hash<PipelineSelectorMTL>
|
||||
|
||||
@@ -2005,7 +2005,7 @@ void GSDeviceMTL::MRESetHWPipelineState(GSHWDrawConfig::VSSelector vssel, GSHWDr
|
||||
MTLRenderPipelineColorAttachmentDescriptor* color1 = [[pdesc colorAttachments] objectAtIndexedSubscript:1];
|
||||
[color1 setPixelFormat:MTLPixelFormatR32Float];
|
||||
}
|
||||
NSString* pname = [NSString stringWithFormat:@"HW Render %x.%x.%llx.%x", vssel_mtl.key, pssel.key_hi, pssel.key_lo, extras.fullkey];
|
||||
NSString* pname = [NSString stringWithFormat:@"HW Render %x.%x.%llx.%llx", vssel_mtl.key, pssel.key_hi, pssel.key_lo, extras.fullkey];
|
||||
auto pipeline = MakePipeline(pdesc, vs, ps, pname);
|
||||
|
||||
[m_current_render.encoder setRenderPipelineState:pipeline];
|
||||
|
||||
@@ -45,7 +45,6 @@ namespace GLState
|
||||
u32 draw_buffers;
|
||||
|
||||
GLuint tex_unit[8];
|
||||
GLuint64 tex_handle[8];
|
||||
|
||||
u32 UpdateDrawBuffers()
|
||||
{
|
||||
@@ -90,6 +89,5 @@ namespace GLState
|
||||
draw_buffers = 0;
|
||||
|
||||
std::fill(std::begin(tex_unit), std::end(tex_unit), 0);
|
||||
std::fill(std::begin(tex_handle), std::end(tex_handle), 0);
|
||||
}
|
||||
} // namespace GLState
|
||||
|
||||
@@ -49,7 +49,6 @@ namespace GLState
|
||||
extern bool ds_written; // Depth Stencil written
|
||||
|
||||
extern GLuint tex_unit[8]; // shader input texture
|
||||
extern GLuint64 tex_handle[8]; // shader input texture
|
||||
|
||||
extern u32 UpdateDrawBuffers();
|
||||
extern void Clear();
|
||||
|
||||
@@ -87,6 +87,12 @@ namespace Emulate_DSA
|
||||
glTexSubImage2D(GL_TEXTURE_2D, level, xoffset, yoffset, width, height, format, type, pixels);
|
||||
}
|
||||
|
||||
static void GLAPIENTRY CopyTextureSubImage(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height)
|
||||
{
|
||||
BindTextureUnit(7, texture);
|
||||
glCopyTexSubImage2D(GL_TEXTURE_2D, level, xoffset, yoffset, x, y, width, height);
|
||||
}
|
||||
|
||||
static void GLAPIENTRY CompressedTextureSubImage(GLuint texture, GLint level, GLint xoffset, GLint yoffset,
|
||||
GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void* data)
|
||||
{
|
||||
@@ -126,6 +132,7 @@ namespace Emulate_DSA
|
||||
glCreateTextures = CreateTexture;
|
||||
glTextureStorage2D = TextureStorage;
|
||||
glTextureSubImage2D = TextureSubImage;
|
||||
glCopyTextureSubImage2D = CopyTextureSubImage;
|
||||
glCompressedTextureSubImage2D = CompressedTextureSubImage;
|
||||
glGetTextureImage = GetTexureImage;
|
||||
glTextureParameteri = TextureParameteri;
|
||||
@@ -650,7 +657,7 @@ bool GSDeviceOGL::Create(GSVSyncMode vsync_mode, bool allow_present_throttle)
|
||||
|
||||
// Basic to ensure structures are correctly packed
|
||||
static_assert(sizeof(VSSelector) == 1, "Wrong VSSelector size");
|
||||
static_assert(sizeof(PSSelector) == 12, "Wrong PSSelector size");
|
||||
static_assert(sizeof(PSSelector) == 16, "Wrong PSSelector size");
|
||||
static_assert(sizeof(PSSamplerSelector) == 1, "Wrong PSSamplerSelector size");
|
||||
static_assert(sizeof(OMDepthStencilSelector) == 1, "Wrong OMDepthStencilSelector size");
|
||||
static_assert(sizeof(OMColorMaskSelector) == 1, "Wrong OMColorMaskSelector size");
|
||||
@@ -769,12 +776,12 @@ bool GSDeviceOGL::CheckFeatures()
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!GLAD_GL_VERSION_4_3 && !GLAD_GL_ARB_copy_image && !GLAD_GL_EXT_copy_image)
|
||||
if (!GLAD_GL_VERSION_4_3 && !GLAD_GL_ARB_copy_image && !GLAD_GL_EXT_copy_image && !GLAD_GL_NV_copy_image)
|
||||
{
|
||||
Host::ReportFormattedErrorAsync(
|
||||
"GS", "GL_ARB_copy_image is not supported, this is required for the OpenGL renderer.");
|
||||
return false;
|
||||
"GS", "GL_ARB_copy_image is not supported, copies will be slower.");
|
||||
}
|
||||
|
||||
if (!GLAD_GL_VERSION_4_5 && !GLAD_GL_ARB_clip_control)
|
||||
{
|
||||
Host::ReportFormattedErrorAsync(
|
||||
@@ -901,6 +908,11 @@ bool GSDeviceOGL::CheckFeatures()
|
||||
|
||||
m_features.aa1 = GSConfig.HWAA1 && m_features.vs_expand && m_features.feedback_loops();
|
||||
|
||||
if (GSConfig.HWROV)
|
||||
{
|
||||
Console.Warning("GL: ROV is not implemented for GL and will be disabled.");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1332,8 +1344,9 @@ void GSDeviceOGL::CommitClear(GSTexture* t, bool use_write_fbo)
|
||||
if (use_write_fbo)
|
||||
{
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER,
|
||||
(t->GetType() == GSTexture::Type::RenderTarget) ? GL_COLOR_ATTACHMENT0 :
|
||||
(m_features.framebuffer_fetch ? GL_DEPTH_ATTACHMENT : GL_DEPTH_STENCIL_ATTACHMENT),
|
||||
(t->GetType() == GSTexture::Type::RenderTarget) ?
|
||||
GL_COLOR_ATTACHMENT0 :
|
||||
(m_features.framebuffer_fetch ? GL_DEPTH_ATTACHMENT : GL_DEPTH_STENCIL_ATTACHMENT),
|
||||
GL_TEXTURE_2D, 0, 0);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, GLState::fbo);
|
||||
}
|
||||
@@ -1439,9 +1452,9 @@ std::string GSDeviceOGL::GenGlslHeader(const std::string_view entry, GLenum type
|
||||
{
|
||||
std::string header;
|
||||
|
||||
// Intel's GL driver doesn't like the readonly qualifier with 3.3 GLSL.
|
||||
if (m_features.vs_expand && GLAD_GL_VERSION_4_3)
|
||||
{
|
||||
// Intel's GL driver doesn't like the readonly qualifier with 3.3 GLSL.
|
||||
header = "#version 430 core\n";
|
||||
}
|
||||
else
|
||||
@@ -1522,7 +1535,7 @@ std::string GSDeviceOGL::GetVSSource(VSSelector sel)
|
||||
std::string macro = fmt::format("#define VS_FST {}\n", static_cast<u32>(sel.fst))
|
||||
+ fmt::format("#define VS_IIP {}\n", static_cast<u32>(sel.iip))
|
||||
+ fmt::format("#define VS_POINT_SIZE {}\n", static_cast<u32>(sel.point_size))
|
||||
+ fmt::format("#define VS_EXPAND {}\n", static_cast<int>(sel.expand));
|
||||
+ fmt::format("#define VS_EXPAND {}\n", static_cast<int>(sel.expand));
|
||||
|
||||
std::string src = GenGlslHeader("vs_main", GL_VERTEX_SHADER, macro);
|
||||
src += m_shader_tfx_vgs;
|
||||
@@ -1531,7 +1544,7 @@ std::string GSDeviceOGL::GetVSSource(VSSelector sel)
|
||||
|
||||
std::string GSDeviceOGL::GetPSSource(const PSSelector& sel)
|
||||
{
|
||||
DevCon.WriteLn("GL: Compiling new pixel shader with selector 0x%" PRIX64 "%08X", sel.key_hi, sel.key_lo);
|
||||
DevCon.WriteLn("GL: Compiling new pixel shader with selector 0x%016" PRIX64 "_%016" PRIX64, sel.key_hi, sel.key_lo);
|
||||
|
||||
std::string macro = fmt::format("#define PS_FST {}\n", sel.fst)
|
||||
+ fmt::format("#define PS_WMS {}\n", sel.wms)
|
||||
@@ -1592,7 +1605,9 @@ std::string GSDeviceOGL::GetPSSource(const PSSelector& sel)
|
||||
+ fmt::format("#define PS_ZTST {}\n", sel.ztst)
|
||||
+ fmt::format("#define PS_AA1 {}\n", static_cast<u32>(sel.aa1))
|
||||
+ fmt::format("#define PS_ABE {}\n", sel.abe)
|
||||
+ fmt::format("#define PS_ANISOTROPIC_FILTERING {}", sel.sw_aniso)
|
||||
+ fmt::format("#define PS_ANISOTROPIC_FILTERING {}\n", sel.sw_aniso)
|
||||
+ fmt::format("#define PS_ROV_COLOR {}\n", 0)
|
||||
+ fmt::format("#define PS_ROV_DEPTH {}\n", 0)
|
||||
;
|
||||
|
||||
std::string src = GenGlslHeader("ps_main", GL_FRAGMENT_SHADER, macro);
|
||||
@@ -1619,7 +1634,7 @@ void GSDeviceOGL::BlitRect(GSTexture* sTex, const GSVector4i& r, const GSVector2
|
||||
OMSetDepthStencilState(m_convert.dss);
|
||||
OMSetBlendState();
|
||||
OMSetColorMaskState();
|
||||
PSSetShaderResource(0, sTex);
|
||||
PSSetShaderResource(TEXTURE_TEXTURE, sTex);
|
||||
PSSetSamplerState(filter == Biln ? m_convert.ln : m_convert.pt);
|
||||
DrawStretchRect(float_r / (GSVector4(sTex->GetSize()).xyxy()), float_r, dsize);
|
||||
|
||||
@@ -1668,6 +1683,30 @@ void GSDeviceOGL::CopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r
|
||||
glCopyImageSubDataEXT(sid, GL_TEXTURE_2D, 0, r.x, r.y, 0, did, GL_TEXTURE_2D,
|
||||
0, destX, destY, 0, r.width(), r.height(), 1);
|
||||
}
|
||||
else if (GLAD_GL_NV_copy_image)
|
||||
{
|
||||
glCopyImageSubDataNV(sid, GL_TEXTURE_2D, 0, r.x, r.y, 0, did, GL_TEXTURE_2D,
|
||||
0, destX, destY, 0, r.width(), r.height(), 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
const bool draw_in_depth = sTex->IsDepthStencil();
|
||||
const GLenum attachment = draw_in_depth ? GL_DEPTH_STENCIL_ATTACHMENT : GL_COLOR_ATTACHMENT0;
|
||||
|
||||
// Bind attachments.
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, m_fbo_read);
|
||||
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, attachment, GL_TEXTURE_2D, sid, 0);
|
||||
glReadBuffer(draw_in_depth ? GL_NONE : GL_COLOR_ATTACHMENT0);
|
||||
|
||||
// Do copy.
|
||||
glCopyTextureSubImage2D(did, 0, destX, destY, r.x, r.y, r.width(), r.height());
|
||||
|
||||
// Unbind attachments.
|
||||
if (draw_in_depth)
|
||||
glReadBuffer(GL_COLOR_ATTACHMENT0);
|
||||
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, attachment, GL_TEXTURE_2D, 0, 0);
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
||||
}
|
||||
|
||||
dTex->SetState(GSTexture::State::Dirty);
|
||||
}
|
||||
@@ -1722,7 +1761,7 @@ void GSDeviceOGL::DoStretchRect(GSTexture* sTex, const GSVector4& sRect, GSTextu
|
||||
// Texture
|
||||
// ************************************
|
||||
|
||||
PSSetShaderResource(0, sTex);
|
||||
PSSetShaderResource(TEXTURE_TEXTURE, sTex);
|
||||
PSSetSamplerState(filter == Biln ? m_convert.ln : m_convert.pt);
|
||||
|
||||
// ************************************
|
||||
@@ -1757,7 +1796,7 @@ void GSDeviceOGL::PresentRect(GSTexture* sTex, const GSVector4& sRect, GSTexture
|
||||
OMSetBlendState(false);
|
||||
OMSetColorMaskState();
|
||||
|
||||
PSSetShaderResource(0, sTex);
|
||||
PSSetShaderResource(TEXTURE_TEXTURE, sTex);
|
||||
PSSetSamplerState(filter == Biln ? m_convert.ln : m_convert.pt);
|
||||
|
||||
// Flip y axis only when we render in the backbuffer
|
||||
@@ -1784,7 +1823,7 @@ void GSDeviceOGL::UpdateCLUTTexture(GSTexture* sTex, float sScale, u32 offsetX,
|
||||
OMSetColorMaskState();
|
||||
OMSetRenderTargets(dTex, nullptr, nullptr);
|
||||
|
||||
PSSetShaderResource(0, sTex);
|
||||
PSSetShaderResource(TEXTURE_TEXTURE, sTex);
|
||||
PSSetSamplerState(m_convert.pt);
|
||||
|
||||
const GSVector4 dRect(0, 0, dSize, 1);
|
||||
@@ -1808,7 +1847,7 @@ void GSDeviceOGL::ConvertToIndexedTexture(GSTexture* sTex, float sScale, u32 off
|
||||
OMSetColorMaskState();
|
||||
OMSetRenderTargets(dTex, nullptr, nullptr);
|
||||
|
||||
PSSetShaderResource(0, sTex);
|
||||
PSSetShaderResource(TEXTURE_TEXTURE, sTex);
|
||||
PSSetSamplerState(m_convert.pt);
|
||||
|
||||
const GSVector4 dRect(0, 0, dTex->GetWidth(), dTex->GetHeight());
|
||||
@@ -1832,7 +1871,7 @@ void GSDeviceOGL::FilteredDownsampleTexture(GSTexture* sTex, GSTexture* dTex, u3
|
||||
OMSetColorMaskState();
|
||||
OMSetRenderTargets(dTex, nullptr, nullptr);
|
||||
|
||||
PSSetShaderResource(0, sTex);
|
||||
PSSetShaderResource(TEXTURE_TEXTURE, sTex);
|
||||
PSSetSamplerState(m_convert.pt);
|
||||
|
||||
//const GSVector4 dRect = GSVector4(dTex->GetRect());
|
||||
@@ -1958,7 +1997,7 @@ void GSDeviceOGL::DoMultiStretchRects(const MultiStretchRect* rects, u32 num_rec
|
||||
m_vertex_stream_buffer->Unmap(vcount * sizeof(GSVertexPT1));
|
||||
m_index_stream_buffer->Unmap(icount * sizeof(u16));
|
||||
|
||||
PSSetShaderResource(0, rects[0].src);
|
||||
PSSetShaderResource(TEXTURE_TEXTURE, rects[0].src);
|
||||
PSSetSamplerState(rects[0].filter == Biln ? m_convert.ln : m_convert.pt);
|
||||
OMSetColorMaskState(rects[0].wmask);
|
||||
DrawIndexedPrimitive();
|
||||
@@ -2149,7 +2188,7 @@ void GSDeviceOGL::SetupDATE(GSTexture* rt, GSTexture* ds, SetDATM datm, const GS
|
||||
|
||||
// Texture
|
||||
|
||||
PSSetShaderResource(0, rt);
|
||||
PSSetShaderResource(TEXTURE_TEXTURE, rt);
|
||||
PSSetSamplerState(m_convert.pt);
|
||||
|
||||
DrawPrimitive();
|
||||
@@ -2308,7 +2347,7 @@ bool GSDeviceOGL::DoCAS(GSTexture* sTex, GSTexture* dTex, bool sharpen_only, con
|
||||
prog.Uniform4uiv(1, &constants[4]);
|
||||
prog.Uniform2iv(2, reinterpret_cast<const s32*>(&constants[8]));
|
||||
|
||||
PSSetShaderResource(0, sTex);
|
||||
PSSetShaderResource(TEXTURE_TEXTURE, sTex);
|
||||
glBindImageTexture(0, static_cast<GSTextureOGL*>(dTex)->GetID(), 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA8);
|
||||
|
||||
static const int threadGroupWorkRegionDim = 16;
|
||||
@@ -2706,6 +2745,9 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
|
||||
|
||||
const GSVector2i rtsize = (config.rt ? config.rt : config.ds)->GetSize();
|
||||
GSTexture* colclip_rt = g_gs_device->GetColorClipTexture();
|
||||
GSTexture* draw_rt = config.rt;
|
||||
GSTexture* draw_ds = config.ds;
|
||||
GSTexture* draw_ds_as_rt = m_ds_as_rt;
|
||||
GSTexture* draw_rt_clone = nullptr;
|
||||
GSTexture* draw_ds_clone = nullptr;
|
||||
GSTexture* primid_texture = nullptr;
|
||||
@@ -2762,6 +2804,8 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
|
||||
const GSVector4 sRect = dRect / GSVector4(rtsize.x, rtsize.y).xyxy();
|
||||
StretchRect(config.rt, sRect, colclip_rt, dRect, ShaderConvert::COLCLIP_INIT, Nearest);
|
||||
}
|
||||
|
||||
draw_rt = colclip_rt ? colclip_rt : config.rt;
|
||||
}
|
||||
|
||||
// Destination Alpha Setup
|
||||
@@ -2820,13 +2864,13 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
|
||||
IASetPrimitiveTopology(topology);
|
||||
|
||||
if (config.tex && (m_features.texture_barrier || config.tex_hazard == GSHWDrawConfig::TEX_HAZARD_NONE))
|
||||
PSSetShaderResource(0, config.tex);
|
||||
PSSetShaderResource(TEXTURE_TEXTURE, config.tex);
|
||||
if (config.pal)
|
||||
PSSetShaderResource(1, config.pal);
|
||||
PSSetShaderResource(TEXTURE_PALETTE, config.pal);
|
||||
if (m_features.texture_barrier && (config.require_one_barrier || config.require_full_barrier))
|
||||
PSSetShaderResource(2, colclip_rt ? colclip_rt : config.rt);
|
||||
PSSetShaderResource(TEXTURE_RT, colclip_rt ? colclip_rt : config.rt);
|
||||
if (m_features.texture_barrier && (config.require_one_barrier || config.require_full_barrier) && config.ps.IsFeedbackLoopDepth())
|
||||
PSSetShaderResource(4, m_features.depth_feedback ? config.ds : m_ds_as_rt);
|
||||
PSSetShaderResource(TEXTURE_DEPTH, m_features.depth_feedback ? config.ds : m_ds_as_rt);
|
||||
|
||||
SetupSampler(config.sampler);
|
||||
|
||||
@@ -2896,10 +2940,10 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
|
||||
psel.ps.date = 3;
|
||||
config.alpha_second_pass.ps.date = 3;
|
||||
SetupPipeline(psel);
|
||||
PSSetShaderResource(3, primid_texture);
|
||||
PSSetShaderResource(TEXTURE_PRIMID, primid_texture);
|
||||
}
|
||||
|
||||
if (m_ds_as_rt)
|
||||
if (draw_ds_as_rt)
|
||||
{
|
||||
// We must clear the blend equation of any dual source blending factors or
|
||||
// it may interact badly with MRTs, even if blending is disabled.
|
||||
@@ -2918,25 +2962,21 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
|
||||
OMSetBlendState();
|
||||
}
|
||||
|
||||
GSTexture* draw_rt = colclip_rt ? colclip_rt : config.rt;
|
||||
GSTexture* draw_ds_as_rt = m_ds_as_rt;
|
||||
GSTexture* draw_ds = config.ds;
|
||||
|
||||
// Clear texture binding when it's bound to RT or DS.
|
||||
if (!config.tex && ((draw_rt && static_cast<GSTextureOGL*>(draw_rt)->GetID() == GLState::tex_unit[0]) ||
|
||||
(draw_ds && static_cast<GSTextureOGL*>(draw_ds)->GetID() == GLState::tex_unit[0])))
|
||||
PSSetShaderResource(0, nullptr);
|
||||
PSSetShaderResource(TEXTURE_TEXTURE, nullptr);
|
||||
|
||||
// Avoid changing framebuffer just to switch from rt+depth to rt and vice versa.
|
||||
bool fb_optimization_needs_barrier = false;
|
||||
if (!draw_rt && GLState::rt && GLState::ds == draw_ds && config.tex != GLState::rt &&
|
||||
GLState::rt->GetSize() == draw_ds->GetSize() && !draw_ds_as_rt)
|
||||
draw_ds && GLState::rt->GetSize() == draw_ds->GetSize() && !draw_ds_as_rt)
|
||||
{
|
||||
draw_rt = GLState::rt;
|
||||
fb_optimization_needs_barrier = !GLState::rt_written;
|
||||
}
|
||||
else if (!draw_ds && GLState::ds && GLState::rt == draw_rt && config.tex != GLState::ds &&
|
||||
GLState::ds->GetSize() == draw_rt->GetSize() && !draw_ds_as_rt)
|
||||
draw_rt && GLState::ds->GetSize() == draw_rt->GetSize() && !draw_ds_as_rt)
|
||||
{
|
||||
draw_ds = GLState::ds;
|
||||
fb_optimization_needs_barrier = !GLState::ds_written;
|
||||
|
||||
@@ -115,6 +115,15 @@ public:
|
||||
using OMDepthStencilSelector = GSHWDrawConfig::DepthStencilSelector;
|
||||
using OMColorMaskSelector = GSHWDrawConfig::ColorMaskSelector;
|
||||
|
||||
enum TextureUnit : u32
|
||||
{
|
||||
TEXTURE_TEXTURE,
|
||||
TEXTURE_PALETTE,
|
||||
TEXTURE_RT,
|
||||
TEXTURE_PRIMID,
|
||||
TEXTURE_DEPTH,
|
||||
};
|
||||
|
||||
struct alignas(16) ProgramSelector
|
||||
{
|
||||
PSSelector ps;
|
||||
@@ -124,7 +133,7 @@ public:
|
||||
__fi bool operator==(const ProgramSelector& p) const { return BitEqual(*this, p); }
|
||||
__fi bool operator!=(const ProgramSelector& p) const { return !BitEqual(*this, p); }
|
||||
};
|
||||
static_assert(sizeof(ProgramSelector) == 16, "Program selector is 16 bytes");
|
||||
static_assert(sizeof(ProgramSelector) == 32, "Program selector is 32 bytes");
|
||||
|
||||
struct ProgramSelectorHash
|
||||
{
|
||||
|
||||
@@ -30,26 +30,25 @@ GSTextureOGL::GSTextureOGL(Type type, int width, int height, int levels, Format
|
||||
m_type = type;
|
||||
m_texture_id = 0;
|
||||
m_mipmap_levels = 1;
|
||||
int gl_fmt = 0;
|
||||
|
||||
// Bunch of constant parameter
|
||||
switch (m_format)
|
||||
{
|
||||
// 1 Channel integer
|
||||
case Format::PrimID:
|
||||
gl_fmt = GL_R32F;
|
||||
m_gl_format = GL_R32F;
|
||||
m_int_format = GL_RED;
|
||||
m_int_type = GL_INT;
|
||||
m_int_shift = 2;
|
||||
break;
|
||||
case Format::UInt32:
|
||||
gl_fmt = GL_R32UI;
|
||||
m_gl_format = GL_R32UI;
|
||||
m_int_format = GL_RED_INTEGER;
|
||||
m_int_type = GL_UNSIGNED_INT;
|
||||
m_int_shift = 2;
|
||||
break;
|
||||
case Format::UInt16:
|
||||
gl_fmt = GL_R16UI;
|
||||
m_gl_format = GL_R16UI;
|
||||
m_int_format = GL_RED_INTEGER;
|
||||
m_int_type = GL_UNSIGNED_SHORT;
|
||||
m_int_shift = 1;
|
||||
@@ -57,7 +56,7 @@ GSTextureOGL::GSTextureOGL(Type type, int width, int height, int levels, Format
|
||||
|
||||
// 1 Channel normalized
|
||||
case Format::UNorm8:
|
||||
gl_fmt = GL_R8;
|
||||
m_gl_format = GL_R8;
|
||||
m_int_format = GL_RED;
|
||||
m_int_type = GL_UNSIGNED_BYTE;
|
||||
m_int_shift = 0;
|
||||
@@ -65,7 +64,7 @@ GSTextureOGL::GSTextureOGL(Type type, int width, int height, int levels, Format
|
||||
|
||||
// 1 channel float
|
||||
case Format::DepthColor:
|
||||
gl_fmt = GL_R32F;
|
||||
m_gl_format = GL_R32F;
|
||||
m_int_format = GL_RED;
|
||||
m_int_type = GL_FLOAT;
|
||||
m_int_shift = 2;
|
||||
@@ -75,7 +74,7 @@ GSTextureOGL::GSTextureOGL(Type type, int width, int height, int levels, Format
|
||||
case Format::Color:
|
||||
case Format::ColorHQ:
|
||||
case Format::ColorHDR:
|
||||
gl_fmt = GL_RGBA8;
|
||||
m_gl_format = GL_RGBA8;
|
||||
m_int_format = GL_RGBA;
|
||||
m_int_type = GL_UNSIGNED_BYTE;
|
||||
m_int_shift = 2;
|
||||
@@ -83,7 +82,7 @@ GSTextureOGL::GSTextureOGL(Type type, int width, int height, int levels, Format
|
||||
|
||||
// 4 channel float
|
||||
case Format::ColorClip:
|
||||
gl_fmt = GL_RGBA16;
|
||||
m_gl_format = GL_RGBA16;
|
||||
m_int_format = GL_RGBA;
|
||||
m_int_type = GL_UNSIGNED_SHORT;
|
||||
m_int_shift = 3;
|
||||
@@ -94,14 +93,14 @@ GSTextureOGL::GSTextureOGL(Type type, int width, int height, int levels, Format
|
||||
{
|
||||
if (!g_gs_device->Features().framebuffer_fetch)
|
||||
{
|
||||
gl_fmt = GL_DEPTH32F_STENCIL8;
|
||||
m_gl_format = GL_DEPTH32F_STENCIL8;
|
||||
m_int_format = GL_DEPTH_STENCIL;
|
||||
m_int_type = GL_FLOAT_32_UNSIGNED_INT_24_8_REV;
|
||||
m_int_shift = 3; // 4 bytes for depth + 4 bytes for stencil by texels
|
||||
}
|
||||
else
|
||||
{
|
||||
gl_fmt = GL_DEPTH_COMPONENT32F;
|
||||
m_gl_format = GL_DEPTH_COMPONENT32F;
|
||||
m_int_format = GL_DEPTH_COMPONENT;
|
||||
m_int_type = GL_FLOAT;
|
||||
m_int_shift = 2;
|
||||
@@ -110,28 +109,28 @@ GSTextureOGL::GSTextureOGL(Type type, int width, int height, int levels, Format
|
||||
break;
|
||||
|
||||
case Format::BC1:
|
||||
gl_fmt = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
|
||||
m_gl_format = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
|
||||
m_int_format = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
|
||||
m_int_type = GL_UNSIGNED_BYTE;
|
||||
m_int_shift = 1;
|
||||
break;
|
||||
|
||||
case Format::BC2:
|
||||
gl_fmt = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
|
||||
m_gl_format = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
|
||||
m_int_format = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
|
||||
m_int_type = GL_UNSIGNED_BYTE;
|
||||
m_int_shift = 1;
|
||||
break;
|
||||
|
||||
case Format::BC3:
|
||||
gl_fmt = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
|
||||
m_gl_format = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
|
||||
m_int_format = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
|
||||
m_int_type = GL_UNSIGNED_BYTE;
|
||||
m_int_shift = 1;
|
||||
break;
|
||||
|
||||
case Format::BC7:
|
||||
gl_fmt = GL_COMPRESSED_RGBA_BPTC_UNORM_ARB;
|
||||
m_gl_format = GL_COMPRESSED_RGBA_BPTC_UNORM_ARB;
|
||||
m_int_format = GL_COMPRESSED_RGBA_BPTC_UNORM_ARB;
|
||||
m_int_type = GL_UNSIGNED_BYTE;
|
||||
m_int_shift = 1;
|
||||
@@ -157,7 +156,7 @@ GSTextureOGL::GSTextureOGL(Type type, int width, int height, int levels, Format
|
||||
glTextureParameteri(m_texture_id, GL_TEXTURE_SWIZZLE_A, GL_RED);
|
||||
}
|
||||
|
||||
glTextureStorage2D(m_texture_id, m_mipmap_levels, gl_fmt, m_size.x, m_size.y);
|
||||
glTextureStorage2D(m_texture_id, m_mipmap_levels, m_gl_format, m_size.x, m_size.y);
|
||||
}
|
||||
|
||||
GSTextureOGL::~GSTextureOGL()
|
||||
@@ -335,6 +334,8 @@ void GSTextureOGL::SetDebugName(std::string_view name)
|
||||
|
||||
if (glObjectLabel)
|
||||
glObjectLabel(GL_TEXTURE, m_texture_id, static_cast<GLsizei>(name.length()), static_cast<const GLchar*>(name.data()));
|
||||
|
||||
m_debug_name = name;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -22,6 +22,7 @@ private:
|
||||
u32 m_map_offset = 0;
|
||||
|
||||
// internal opengl format/type/alignment
|
||||
GLenum m_gl_format = 0;
|
||||
GLenum m_int_format = 0;
|
||||
GLenum m_int_type = 0;
|
||||
u32 m_int_shift = 0;
|
||||
@@ -33,6 +34,7 @@ public:
|
||||
__fi GLenum GetIntFormat() const { return m_int_format; }
|
||||
__fi GLenum GetIntType() const { return m_int_type; }
|
||||
__fi u32 GetIntShift() const { return m_int_shift; }
|
||||
__fi u32 GetGLFormat() const { return m_gl_format; }
|
||||
|
||||
void* GetNativeHandle() const override;
|
||||
|
||||
@@ -55,6 +57,7 @@ public:
|
||||
}
|
||||
|
||||
__fi u32 GetID() { return m_texture_id; }
|
||||
__fi u32 GetGLFormat() { return m_gl_format; }
|
||||
};
|
||||
|
||||
class GSDownloadTextureOGL final : public GSDownloadTexture
|
||||
|
||||
@@ -444,6 +444,8 @@ bool GSDeviceVK::SelectDeviceExtensions(ExtensionList* extension_list, bool enab
|
||||
enable_surface && SupportsExtension(VK_EXT_FULL_SCREEN_EXCLUSIVE_EXTENSION_NAME, false);
|
||||
#endif
|
||||
|
||||
m_optional_extensions.vk_ext_fragment_shader_interlock = SupportsExtension(VK_EXT_FRAGMENT_SHADER_INTERLOCK_EXTENSION_NAME, false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -459,6 +461,7 @@ bool GSDeviceVK::SelectDeviceFeatures()
|
||||
m_device_features.fragmentStoresAndAtomics = available_features.fragmentStoresAndAtomics;
|
||||
m_device_features.textureCompressionBC = available_features.textureCompressionBC;
|
||||
m_device_features.geometryShader = available_features.geometryShader;
|
||||
m_device_features.fragmentStoresAndAtomics = available_features.fragmentStoresAndAtomics;
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -628,6 +631,8 @@ bool GSDeviceVK::CreateDevice(VkSurfaceKHR surface, bool enable_validation_layer
|
||||
// VK_EXT_swapchain_maintenance1 types/enums are aliases of VK_KHR_swapchain_maintenance1 types/enums.
|
||||
VkPhysicalDeviceSwapchainMaintenance1FeaturesKHR swapchain_maintenance1_feature = {
|
||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SWAPCHAIN_MAINTENANCE_1_FEATURES_KHR};
|
||||
VkPhysicalDeviceFragmentShaderInterlockFeaturesEXT fragment_shader_interlock_ext_feature = {
|
||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADER_INTERLOCK_FEATURES_EXT};
|
||||
|
||||
if (m_optional_extensions.vk_ext_provoking_vertex)
|
||||
{
|
||||
@@ -654,6 +659,11 @@ bool GSDeviceVK::CreateDevice(VkSurfaceKHR surface, bool enable_validation_layer
|
||||
swapchain_maintenance1_feature.swapchainMaintenance1 = VK_TRUE;
|
||||
Vulkan::AddPointerToChain(&device_info, &swapchain_maintenance1_feature);
|
||||
}
|
||||
if (m_optional_extensions.vk_ext_fragment_shader_interlock)
|
||||
{
|
||||
fragment_shader_interlock_ext_feature.fragmentShaderPixelInterlock = VK_TRUE;
|
||||
Vulkan::AddPointerToChain(&device_info, &fragment_shader_interlock_ext_feature);
|
||||
}
|
||||
|
||||
VkResult res = vkCreateDevice(m_physical_device, &device_info, nullptr, &m_device);
|
||||
if (res != VK_SUCCESS)
|
||||
@@ -724,6 +734,8 @@ bool GSDeviceVK::ProcessDeviceExtensions()
|
||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SWAPCHAIN_MAINTENANCE_1_FEATURES_KHR, nullptr, VK_TRUE};
|
||||
VkPhysicalDeviceAttachmentFeedbackLoopLayoutFeaturesEXT attachment_feedback_loop_feature = {
|
||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ATTACHMENT_FEEDBACK_LOOP_LAYOUT_FEATURES_EXT};
|
||||
VkPhysicalDeviceFragmentShaderInterlockFeaturesEXT fragment_shader_interlock_ext_feature = {
|
||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADER_INTERLOCK_FEATURES_EXT };
|
||||
|
||||
// add in optional feature structs
|
||||
if (m_optional_extensions.vk_ext_provoking_vertex)
|
||||
@@ -736,6 +748,8 @@ bool GSDeviceVK::ProcessDeviceExtensions()
|
||||
Vulkan::AddPointerToChain(&features2, &attachment_feedback_loop_feature);
|
||||
if (m_optional_extensions.vk_swapchain_maintenance1)
|
||||
Vulkan::AddPointerToChain(&features2, &swapchain_maintenance1_feature);
|
||||
if (m_optional_extensions.vk_ext_fragment_shader_interlock)
|
||||
Vulkan::AddPointerToChain(&features2, &fragment_shader_interlock_ext_feature);
|
||||
|
||||
// query
|
||||
vkGetPhysicalDeviceFeatures2(m_physical_device, &features2);
|
||||
@@ -813,6 +827,9 @@ bool GSDeviceVK::ProcessDeviceExtensions()
|
||||
m_optional_extensions.vk_swapchain_maintenance1 &=
|
||||
(swapchain_maintenance1_feature.swapchainMaintenance1 == VK_TRUE);
|
||||
|
||||
m_optional_extensions.vk_ext_fragment_shader_interlock &=
|
||||
(fragment_shader_interlock_ext_feature.fragmentShaderPixelInterlock == VK_TRUE);
|
||||
|
||||
Console.WriteLn(
|
||||
"VK_EXT_provoking_vertex is %s", m_optional_extensions.vk_ext_provoking_vertex ? "supported" : "NOT supported");
|
||||
Console.WriteLn(
|
||||
@@ -830,6 +847,8 @@ bool GSDeviceVK::ProcessDeviceExtensions()
|
||||
m_optional_extensions.vk_khr_driver_properties ? "supported" : "NOT supported");
|
||||
Console.WriteLn("VK_EXT_attachment_feedback_loop_layout is %s",
|
||||
m_optional_extensions.vk_ext_attachment_feedback_loop_layout ? "supported" : "NOT supported");
|
||||
Console.WriteLn("VK_EXT_fragment_shader_interlock is %s",
|
||||
m_optional_extensions.vk_ext_fragment_shader_interlock ? "supported" : "NOT supported");
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -2100,6 +2119,12 @@ bool GSDeviceVK::Create(GSVSyncMode vsync_mode, bool allow_present_throttle)
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((m_null_framebuffer = GSTextureVK::CreateNullFramebuffer()) == VK_NULL_HANDLE)
|
||||
{
|
||||
Host::ReportErrorAsync("GS", "Failed to create dummy framebuffer");
|
||||
return false;
|
||||
}
|
||||
|
||||
{
|
||||
std::optional<std::string> shader = ReadShaderSource("shaders/vulkan/tfx.glsl");
|
||||
if (!shader.has_value())
|
||||
@@ -2743,6 +2768,9 @@ bool GSDeviceVK::CheckFeatures()
|
||||
|
||||
m_max_texture_size = m_device_properties.limits.maxImageDimension2D;
|
||||
|
||||
m_features.rov = m_optional_extensions.vk_ext_fragment_shader_interlock &&
|
||||
m_device_features.fragmentStoresAndAtomics;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -2997,7 +3025,7 @@ void GSDeviceVK::DoMultiStretchRects(
|
||||
const MultiStretchRect* rects, u32 num_rects, GSTextureVK* dTex, ShaderConvertSelector shader)
|
||||
{
|
||||
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
|
||||
|
||||
|
||||
// Set up vertices first.
|
||||
const u32 vertex_reserve_size = num_rects * 4 * sizeof(GSVertexPT1);
|
||||
const u32 index_reserve_size = num_rects * 6 * sizeof(u16);
|
||||
@@ -3487,14 +3515,15 @@ void GSDeviceVK::VSSetIndexBuffer(const void* index, size_t count)
|
||||
}
|
||||
|
||||
void GSDeviceVK::OMSetRenderTargets(
|
||||
GSTexture* rt, GSTexture* ds, const GSVector4i& scissor, FeedbackLoopFlag feedback_loop)
|
||||
GSTexture* rt, GSTexture* ds, const GSVector4i& scissor, FeedbackLoopFlag feedback_loop,
|
||||
const GSVector2i& viewport_size)
|
||||
{
|
||||
GSTextureVK* vkRt = static_cast<GSTextureVK*>(rt);
|
||||
GSTextureVK* vkDs = static_cast<GSTextureVK*>(ds);
|
||||
pxAssert(vkRt || vkDs);
|
||||
|
||||
if (m_current_render_target != vkRt || m_current_depth_target != vkDs ||
|
||||
m_current_framebuffer_feedback_loop != feedback_loop)
|
||||
m_current_framebuffer_feedback_loop != feedback_loop ||
|
||||
m_current_framebuffer == VK_NULL_HANDLE)
|
||||
{
|
||||
// framebuffer change or feedback loop enabled/disabled
|
||||
EndRenderPass();
|
||||
@@ -3506,12 +3535,16 @@ void GSDeviceVK::OMSetRenderTargets(
|
||||
(feedback_loop & FeedbackLoopFlag_ReadAndWriteRT) != 0,
|
||||
(feedback_loop & (FeedbackLoopFlag_ReadAndWriteDepth | FeedbackLoopFlag_ReadDepth)) != 0);
|
||||
}
|
||||
else
|
||||
else if (vkDs)
|
||||
{
|
||||
pxAssert(!(feedback_loop & FeedbackLoopFlag_ReadAndWriteRT));
|
||||
m_current_framebuffer = vkDs->GetLinkedFramebuffer(
|
||||
nullptr, false, (feedback_loop & (FeedbackLoopFlag_ReadAndWriteDepth | FeedbackLoopFlag_ReadDepth)) != 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_current_framebuffer = m_null_framebuffer;
|
||||
}
|
||||
}
|
||||
else if (InRenderPass())
|
||||
{
|
||||
@@ -3656,7 +3689,7 @@ void GSDeviceVK::OMSetRenderTargets(
|
||||
}
|
||||
|
||||
// This is used to set/initialize the framebuffer for tfx rendering.
|
||||
const GSVector2i size = vkRt ? vkRt->GetSize() : vkDs->GetSize();
|
||||
const GSVector2i size = (vkRt ? vkRt->GetSize() : (vkDs ? vkDs->GetSize() : viewport_size));
|
||||
const VkViewport vp{0.0f, 0.0f, static_cast<float>(size.x), static_cast<float>(size.y), 0.0f, 1.0f};
|
||||
|
||||
SetViewport(vp);
|
||||
@@ -3732,6 +3765,11 @@ static void AddShaderHeader(std::stringstream& ss)
|
||||
ss << "#define DISABLE_TEXTURE_BARRIER 1\n";
|
||||
if (features.texture_barrier && dev->UseFeedbackLoopLayout())
|
||||
ss << "#define HAS_FEEDBACK_LOOP_LAYOUT 1\n";
|
||||
if (features.rov)
|
||||
{
|
||||
ss << "#extension GL_ARB_fragment_shader_interlock : require\n";
|
||||
ss << "#extension GL_ARB_shader_image_load_store : require\n";
|
||||
}
|
||||
}
|
||||
|
||||
static void AddShaderStageMacro(std::stringstream& ss, bool vs, bool gs, bool fs)
|
||||
@@ -3905,6 +3943,8 @@ bool GSDeviceVK::CreatePipelineLayouts()
|
||||
(m_features.texture_barrier && !UseFeedbackLoopLayout()) ? VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT :
|
||||
VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE,
|
||||
1, VK_SHADER_STAGE_FRAGMENT_BIT);
|
||||
dslb.AddBinding(TFX_TEXTURE_RT_ROV, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1, VK_SHADER_STAGE_FRAGMENT_BIT);
|
||||
dslb.AddBinding(TFX_TEXTURE_DEPTH_ROV, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1, VK_SHADER_STAGE_FRAGMENT_BIT);
|
||||
if ((m_tfx_texture_ds_layout = dslb.Create(dev)) == VK_NULL_HANDLE)
|
||||
return false;
|
||||
Vulkan::SetObjectName(dev, m_tfx_texture_ds_layout, "TFX texture descriptor layout");
|
||||
@@ -4739,6 +4779,11 @@ void GSDeviceVK::DestroyResources()
|
||||
if (m_utility_ds_layout != VK_NULL_HANDLE)
|
||||
vkDestroyDescriptorSetLayout(m_device, m_utility_ds_layout, nullptr);
|
||||
|
||||
if (m_null_framebuffer != VK_NULL_HANDLE)
|
||||
{
|
||||
vkDestroyFramebuffer(m_device, m_null_framebuffer, nullptr);
|
||||
}
|
||||
|
||||
if (m_null_texture)
|
||||
{
|
||||
m_null_texture->Destroy(false);
|
||||
@@ -4870,12 +4915,13 @@ VkShaderModule GSDeviceVK::GetTFXFragmentShader(const GSHWDrawConfig::PSSelector
|
||||
AddMacro(ss, "PS_AA1", static_cast<u32>(sel.aa1));
|
||||
AddMacro(ss, "PS_ABE", sel.abe);
|
||||
AddMacro(ss, "PS_ANISOTROPIC_FILTERING", sel.sw_aniso);
|
||||
|
||||
AddMacro(ss, "PS_ROV_COLOR", sel.rov_color);
|
||||
AddMacro(ss, "PS_ROV_DEPTH", static_cast<u32>(sel.rov_depth));
|
||||
ss << m_tfx_source;
|
||||
|
||||
VkShaderModule mod = g_vulkan_shader_cache->GetFragmentShader(ss.str());
|
||||
if (mod)
|
||||
Vulkan::SetObjectName(m_device, mod, "TFX Fragment %" PRIX64 "%08X", sel.key_hi, sel.key_lo);
|
||||
Vulkan::SetObjectName(m_device, mod, "TFX Fragment %016" PRIX64 "_%016" PRIX64, sel.key_hi, sel.key_lo);
|
||||
|
||||
m_tfx_fragment_shaders.emplace(sel, mod);
|
||||
return mod;
|
||||
@@ -5002,7 +5048,7 @@ VkPipeline GSDeviceVK::CreateTFXPipeline(const PipelineSelector& p)
|
||||
if (pipeline)
|
||||
{
|
||||
Vulkan::SetObjectName(
|
||||
m_device, pipeline, "TFX Pipeline %08X/%" PRIX64 "%08X", p.vs.key, p.ps.key_hi, p.ps.key_lo);
|
||||
m_device, pipeline, "TFX Pipeline %08X/%016" PRIX64 "_%016" PRIX64, p.vs.key, p.ps.key_hi, p.ps.key_lo);
|
||||
}
|
||||
|
||||
return pipeline;
|
||||
@@ -5223,7 +5269,95 @@ void GSDeviceVK::SetLineWidth(float width)
|
||||
m_dirty_flags |= DIRTY_FLAG_LINE_WIDTH;
|
||||
}
|
||||
|
||||
void GSDeviceVK::PSSetShaderResource(int i, GSTexture* sr, bool check_state)
|
||||
void GSDeviceVK::PSSetUnorderedAccess(GSTexture* rt, GSTexture* ds, bool write_rt, bool write_ds)
|
||||
{
|
||||
GSTextureVK* vkRt = static_cast<GSTextureVK*>(rt);
|
||||
GSTextureVK* vkDs = static_cast<GSTextureVK*>(ds);
|
||||
GSTextureVK* oldVkRt = m_tfx_textures[TFX_TEXTURE_RT_ROV] != m_null_texture.get() ?
|
||||
m_tfx_textures[TFX_TEXTURE_RT_ROV] : nullptr;
|
||||
GSTextureVK* oldVkDs = m_tfx_textures[TFX_TEXTURE_DEPTH_ROV] != m_null_texture.get() ?
|
||||
m_tfx_textures[TFX_TEXTURE_DEPTH_ROV] : nullptr;
|
||||
|
||||
if (!(vkRt || vkDs || oldVkRt || oldVkDs))
|
||||
return;
|
||||
|
||||
pxAssert(!(vkRt || vkDs) || m_features.rov);
|
||||
|
||||
if (vkRt)
|
||||
{
|
||||
PSSetShaderResource(TFX_TEXTURE_RT_ROV, vkRt, true, ResourceType::UAV);
|
||||
|
||||
// Unbind conflicting RT texture
|
||||
PSSetShaderResource(TFX_TEXTURE_RT, nullptr, false);
|
||||
|
||||
// Unbind conflicting source texture
|
||||
if (m_tfx_textures[TFX_TEXTURE_TEXTURE] == vkRt)
|
||||
{
|
||||
PSSetShaderResource(TFX_TEXTURE_TEXTURE, nullptr, false);
|
||||
}
|
||||
|
||||
if (write_rt)
|
||||
{
|
||||
vkRt->SetState(GSTexture::State::Dirty);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Unbind to avoid conflicts with OM targets.
|
||||
PSSetShaderResource(TFX_TEXTURE_RT_ROV, nullptr, false);
|
||||
}
|
||||
|
||||
if (vkDs)
|
||||
{
|
||||
PSSetShaderResource(TFX_TEXTURE_DEPTH_ROV, vkDs, true, ResourceType::UAV);
|
||||
|
||||
// Unbind conflicting depth texture
|
||||
PSSetShaderResource(TFX_TEXTURE_DEPTH, nullptr, false);
|
||||
|
||||
// Unbind conflicting source texture
|
||||
if (m_tfx_textures[TFX_TEXTURE_TEXTURE] == vkDs)
|
||||
{
|
||||
PSSetShaderResource(TFX_TEXTURE_TEXTURE, nullptr, false);
|
||||
}
|
||||
|
||||
if (write_ds)
|
||||
{
|
||||
vkDs->SetState(GSTexture::State::Dirty);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Unbind to avoid conflicts with OM targets.
|
||||
PSSetShaderResource(TFX_TEXTURE_DEPTH_ROV, nullptr, false);
|
||||
}
|
||||
|
||||
if (GSConfig.HWROVBarriersVK)
|
||||
{
|
||||
// This is to fix issues with some systems that seem to require barriers even with FSI.
|
||||
// Adds a barriers to the UAVs before every draw. Can result in a large performance hit.
|
||||
if ((vkRt || vkDs) && InRenderPass())
|
||||
{
|
||||
GL_INS("Ending render pass due to UAV barrier");
|
||||
EndRenderPass();
|
||||
}
|
||||
for (GSTextureVK* tex : std::array{ vkRt, vkDs })
|
||||
{
|
||||
if (tex)
|
||||
{
|
||||
if (InRenderPass())
|
||||
{
|
||||
GL_INS("Ending render pass due to UAV barrier");
|
||||
EndRenderPass();
|
||||
}
|
||||
// The subresources version does the barrier even when the before/after layout
|
||||
// are the same since we want a memory barrier here, not just a layout change.
|
||||
tex->TransitionSubresourcesToLayout(GetCurrentCommandBuffer(), 0, 1, tex->GetLayout(), tex->GetLayout());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GSDeviceVK::PSSetShaderResource(int i, GSTexture* sr, bool check_state, ResourceType type)
|
||||
{
|
||||
GSTextureVK* vkTex = static_cast<GSTextureVK*>(sr);
|
||||
bool update_layout = false;
|
||||
@@ -5231,15 +5365,20 @@ void GSDeviceVK::PSSetShaderResource(int i, GSTexture* sr, bool check_state)
|
||||
{
|
||||
if (check_state)
|
||||
{
|
||||
if (vkTex->GetLayout() != GSTextureVK::Layout::ShaderReadOnly && InRenderPass())
|
||||
{
|
||||
GL_INS("Ending render pass due to resource transition");
|
||||
EndRenderPass();
|
||||
update_layout = true;
|
||||
}
|
||||
const GSTextureVK::Layout layout = GetResourceLayout(type);
|
||||
|
||||
vkTex->CommitClear();
|
||||
vkTex->TransitionToLayout(GSTextureVK::Layout::ShaderReadOnly);
|
||||
|
||||
if (vkTex->GetLayout() != layout)
|
||||
{
|
||||
update_layout = true;
|
||||
if (InRenderPass())
|
||||
{
|
||||
GL_INS("Ending render pass due to resource transition");
|
||||
EndRenderPass();
|
||||
}
|
||||
vkTex->TransitionToLayout(layout);
|
||||
}
|
||||
}
|
||||
vkTex->SetUseFenceCounter(GetCurrentFenceCounter());
|
||||
}
|
||||
@@ -5567,6 +5706,16 @@ bool GSDeviceVK::ApplyTFXState(bool already_execed)
|
||||
m_tfx_textures[TFX_TEXTURE_DEPTH]->GetVkLayout());
|
||||
}
|
||||
}
|
||||
if (flags & DIRTY_FLAG_TFX_TEXTURE_RT_ROV)
|
||||
{
|
||||
dsub.AddImageDescriptorWrite(VK_NULL_HANDLE, TFX_TEXTURE_RT_ROV, m_tfx_textures[TFX_TEXTURE_RT_ROV]->GetView(),
|
||||
m_tfx_textures[TFX_TEXTURE_RT_ROV]->GetVkLayout(), true);
|
||||
}
|
||||
if (flags & DIRTY_FLAG_TFX_TEXTURE_DEPTH_ROV)
|
||||
{
|
||||
dsub.AddImageDescriptorWrite(VK_NULL_HANDLE, TFX_TEXTURE_DEPTH_ROV, m_tfx_textures[TFX_TEXTURE_DEPTH_ROV]->GetView(),
|
||||
m_tfx_textures[TFX_TEXTURE_DEPTH_ROV]->GetVkLayout(), true);
|
||||
}
|
||||
|
||||
dsub.PushUpdate(cmdbuf, VK_PIPELINE_BIND_POINT_GRAPHICS, m_tfx_pipeline_layout, TFX_DESCRIPTOR_SET_TEXTURES);
|
||||
}
|
||||
@@ -5744,8 +5893,10 @@ GSTextureVK* GSDeviceVK::SetupPrimitiveTrackingDATE(GSHWDrawConfig& config)
|
||||
void GSDeviceVK::RenderHW(GSHWDrawConfig& config)
|
||||
{
|
||||
const GSVector2i rtsize(config.rt ? config.rt->GetSize() : config.ds->GetSize());
|
||||
GSTextureVK* draw_rt = static_cast<GSTextureVK*>(config.rt);
|
||||
GSTextureVK* draw_ds = static_cast<GSTextureVK*>(config.ds);
|
||||
GSTextureVK* draw_rt = config.ps.HasColorROV() ? nullptr : static_cast<GSTextureVK*>(config.rt);
|
||||
GSTextureVK* draw_ds = config.ps.HasDepthROV() ? nullptr : static_cast<GSTextureVK*>(config.ds);
|
||||
GSTextureVK* draw_rt_rov = config.ps.HasColorROV() ? static_cast<GSTextureVK*>(config.rt) : nullptr;
|
||||
GSTextureVK* draw_ds_rov = config.ps.HasDepthROV() ? static_cast<GSTextureVK*>(config.ds) : nullptr;
|
||||
GSTextureVK* draw_rt_clone = nullptr;
|
||||
GSTextureVK* colclip_rt = static_cast<GSTextureVK*>(g_gs_device->GetColorClipTexture());
|
||||
|
||||
@@ -5756,11 +5907,11 @@ void GSDeviceVK::RenderHW(GSHWDrawConfig& config)
|
||||
// bind textures before checking the render pass, in case we need to transition them
|
||||
if (config.tex)
|
||||
{
|
||||
PSSetShaderResource(0, config.tex, config.tex != config.rt && config.tex != config.ds);
|
||||
PSSetShaderResource(TFX_TEXTURE_TEXTURE, config.tex, config.tex != config.rt && config.tex != config.ds);
|
||||
PSSetSampler(config.sampler);
|
||||
}
|
||||
if (config.pal)
|
||||
PSSetShaderResource(1, config.pal, true);
|
||||
PSSetShaderResource(TFX_TEXTURE_PALETTE, config.pal, true);
|
||||
|
||||
if (config.blend.constant_enable)
|
||||
SetBlendConstants(config.blend.constant);
|
||||
@@ -5839,6 +5990,7 @@ void GSDeviceVK::RenderHW(GSHWDrawConfig& config)
|
||||
g_gs_device->SetColorClipTexture(nullptr);
|
||||
|
||||
colclip_rt = nullptr;
|
||||
draw_rt = config.ps.HasColorROV() ? nullptr : static_cast<GSTextureVK*>(config.rt);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -5927,25 +6079,31 @@ void GSDeviceVK::RenderHW(GSHWDrawConfig& config)
|
||||
// colclip hw requires blitting.
|
||||
EndRenderPass();
|
||||
}
|
||||
else if (InRenderPass() && (m_current_render_target == draw_rt || m_current_depth_target == draw_ds))
|
||||
else if (InRenderPass() &&
|
||||
((draw_rt && m_current_render_target == draw_rt) ||
|
||||
(draw_ds && m_current_depth_target == draw_ds)))
|
||||
{
|
||||
// avoid restarting the render pass just to switch from rt+depth to rt and vice versa
|
||||
// keep the depth even if doing colclip hw draws, because the next draw will probably re-enable depth
|
||||
if (!draw_rt && m_current_render_target && config.tex != m_current_render_target &&
|
||||
m_current_render_target->GetSize() == draw_ds->GetSize())
|
||||
// keep the depth even if doing colclip hw draws, because the next draw will probably re-enable depth.
|
||||
if (!(draw_rt || draw_rt_rov) && m_current_render_target && config.tex != m_current_render_target &&
|
||||
draw_ds && m_current_render_target->GetSize() == draw_ds->GetSize())
|
||||
{
|
||||
draw_rt = m_current_render_target;
|
||||
m_pipeline_selector.rt = true;
|
||||
}
|
||||
else if (!draw_ds && m_current_depth_target && config.tex != m_current_depth_target &&
|
||||
m_current_depth_target->GetSize() == draw_rt->GetSize())
|
||||
else if (!(draw_ds || draw_ds_rov) && m_current_depth_target && config.tex != m_current_depth_target &&
|
||||
draw_rt && m_current_depth_target->GetSize() == draw_rt->GetSize())
|
||||
{
|
||||
draw_ds = m_current_depth_target;
|
||||
m_pipeline_selector.ds = true;
|
||||
}
|
||||
|
||||
// Prefer keeping feedback loop enabled, that way we're not constantly restarting render passes
|
||||
pipe.feedback_loop_flags |= m_current_framebuffer_feedback_loop;
|
||||
if (draw_rt)
|
||||
pipe.feedback_loop_flags |= m_current_framebuffer_feedback_loop & FeedbackLoopFlag_ReadAndWriteRT;
|
||||
if (draw_ds)
|
||||
pipe.feedback_loop_flags |= (m_current_framebuffer_feedback_loop &
|
||||
(FeedbackLoopFlag_ReadAndWriteDepth | FeedbackLoopFlag_ReadDepth));
|
||||
}
|
||||
|
||||
if (draw_rt && ((config.require_one_barrier && (config.IsFeedbackLoopRT(config.ps) || config.IsFeedbackLoopRT(config.alpha_second_pass.ps)))) && !m_features.texture_barrier)
|
||||
@@ -5992,25 +6150,46 @@ void GSDeviceVK::RenderHW(GSHWDrawConfig& config)
|
||||
Console.Warning("VK: Failed to allocate temp texture for RT copy.");
|
||||
}
|
||||
|
||||
OMSetRenderTargets(draw_rt, draw_ds, config.scissor, static_cast<FeedbackLoopFlag>(pipe.feedback_loop_flags));
|
||||
if (pipe.IsRTFeedbackLoop())
|
||||
if (pipe.IsRTFeedbackLoop() && draw_rt)
|
||||
{
|
||||
pxAssertMsg(m_features.texture_barrier, "Texture barriers enabled");
|
||||
PSSetShaderResource(TFX_TEXTURE_RT, draw_rt, false);
|
||||
|
||||
if (m_tfx_textures[TFX_TEXTURE_RT_ROV] == draw_rt)
|
||||
{
|
||||
// Unbind to avoid conflicts.
|
||||
PSSetShaderResource(TFX_TEXTURE_RT_ROV, nullptr, false);
|
||||
m_dirty_flags |= static_cast<u32>(DIRTY_FLAG_TFX_TEXTURE_RT_ROV);
|
||||
}
|
||||
}
|
||||
else if (!draw_rt_clone)
|
||||
{
|
||||
PSSetShaderResource(TFX_TEXTURE_RT, nullptr, false); // Unbind to avoid layout conflicts
|
||||
// Unbind to avoid conflicts with framebuffer
|
||||
PSSetShaderResource(TFX_TEXTURE_RT, nullptr, false);
|
||||
}
|
||||
if (pipe.IsDepthFeedbackLoop())
|
||||
|
||||
if (pipe.IsDepthFeedbackLoop() && draw_ds)
|
||||
{
|
||||
pxAssertMsg(m_features.texture_barrier, "Texture barriers enabled");
|
||||
PSSetShaderResource(TFX_TEXTURE_DEPTH, draw_ds, false);
|
||||
|
||||
if (m_tfx_textures[TFX_TEXTURE_DEPTH_ROV] == draw_ds)
|
||||
{
|
||||
// Unbind to avoid conflicts.
|
||||
PSSetShaderResource(TFX_TEXTURE_DEPTH_ROV, nullptr, false);
|
||||
m_dirty_flags |= static_cast<u32>(DIRTY_FLAG_TFX_TEXTURE_DEPTH_ROV);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
PSSetShaderResource(TFX_TEXTURE_DEPTH, nullptr, false); // Unbind to avoid layout conflicts
|
||||
// Unbind to avoid conflicts with framebuffer
|
||||
PSSetShaderResource(TFX_TEXTURE_DEPTH, nullptr, false);
|
||||
}
|
||||
|
||||
PSSetUnorderedAccess(draw_rt_rov, draw_ds_rov, config.ps.HasColorOutput(), config.ps.HasDepthROVWrite());
|
||||
|
||||
OMSetRenderTargets(draw_rt, draw_ds, config.scissor, static_cast<FeedbackLoopFlag>(pipe.feedback_loop_flags), rtsize);
|
||||
|
||||
// Begin render pass if new target or out of the area.
|
||||
if (!InRenderPass())
|
||||
{
|
||||
@@ -6188,13 +6367,13 @@ void GSDeviceVK::UpdateHWPipelineSelector(GSHWDrawConfig& config, PipelineSelect
|
||||
pipe.vs.key = config.vs.key;
|
||||
pipe.ps.key_hi = config.ps.key_hi;
|
||||
pipe.ps.key_lo = config.ps.key_lo;
|
||||
pipe.dss.key = config.depth.key;
|
||||
pipe.bs.key = config.blend.key;
|
||||
pipe.dss.key = config.ps.HasDepthROV() ? GSHWDrawConfig::DepthStencilSelector::NoDepth().key : config.depth.key;
|
||||
pipe.bs.key = config.ps.HasColorROV() ? GSHWDrawConfig::BlendState().key : config.blend.key;
|
||||
pipe.bs.constant = 0; // don't dupe states with different alpha values
|
||||
pipe.cms.key = config.colormask.key;
|
||||
pipe.cms.key = config.ps.HasColorROV() ? GSHWDrawConfig::ColorMaskSelector().key : config.colormask.key;
|
||||
pipe.topology = static_cast<u32>(config.topology);
|
||||
pipe.rt = config.rt != nullptr;
|
||||
pipe.ds = config.ds != nullptr;
|
||||
pipe.rt = config.rt != nullptr && !config.ps.HasColorROV();
|
||||
pipe.ds = config.ds != nullptr && !config.ps.HasDepthROV();
|
||||
pipe.line_width = config.line_expand;
|
||||
pipe.feedback_loop_flags = FeedbackLoopFlag_None;
|
||||
if (m_features.texture_barrier && (config.require_one_barrier || config.require_full_barrier))
|
||||
@@ -6205,7 +6384,7 @@ void GSDeviceVK::UpdateHWPipelineSelector(GSHWDrawConfig& config, PipelineSelect
|
||||
if (config.IsFeedbackLoopDepth(config.ps))
|
||||
pipe.feedback_loop_flags |= FeedbackLoopFlag_ReadAndWriteDepth;
|
||||
}
|
||||
if (!(pipe.feedback_loop_flags & FeedbackLoopFlag_ReadAndWriteDepth))
|
||||
if (pipe.ds && !(pipe.feedback_loop_flags & FeedbackLoopFlag_ReadAndWriteDepth))
|
||||
{
|
||||
pipe.feedback_loop_flags |= (config.tex && config.tex == config.ds) ? FeedbackLoopFlag_ReadDepth : FeedbackLoopFlag_None;
|
||||
}
|
||||
@@ -6339,4 +6518,7 @@ void GSDeviceVK::SendHWDraw(const GSHWDrawConfig& config, GSTextureVK* draw_rt,
|
||||
}
|
||||
|
||||
Draw(config);
|
||||
|
||||
if (config.ps.HasColorROV() || config.ps.HasDepthROV())
|
||||
g_perfmon.Put(GSPerfMon::DrawCallsROV, 1);
|
||||
}
|
||||
|
||||
@@ -46,6 +46,7 @@ public:
|
||||
bool vk_khr_driver_properties : 1;
|
||||
bool vk_khr_shader_non_semantic_info : 1;
|
||||
bool vk_ext_attachment_feedback_loop_layout : 1;
|
||||
bool vk_ext_fragment_shader_interlock : 1;
|
||||
};
|
||||
|
||||
// Global state accessors
|
||||
@@ -61,8 +62,8 @@ public:
|
||||
// The interaction between raster order attachment access and fbfetch is unclear.
|
||||
__fi bool UseFeedbackLoopLayout() const
|
||||
{
|
||||
return (m_optional_extensions.vk_ext_attachment_feedback_loop_layout &&
|
||||
!m_optional_extensions.vk_ext_rasterization_order_attachment_access);
|
||||
return m_optional_extensions.vk_ext_attachment_feedback_loop_layout &&
|
||||
!m_optional_extensions.vk_ext_rasterization_order_attachment_access;
|
||||
}
|
||||
|
||||
// Helpers for getting constants
|
||||
@@ -301,6 +302,25 @@ public:
|
||||
FeedbackLoopFlag_ReadAndWriteDepth = 4,
|
||||
};
|
||||
|
||||
enum class ResourceType
|
||||
{
|
||||
SRV, // Shader resource view (read only)
|
||||
UAV, // Unordered access (read/write)
|
||||
};
|
||||
|
||||
static constexpr GSTextureVK::Layout GetResourceLayout(ResourceType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
default:
|
||||
pxFailRel("Impossible.");
|
||||
case ResourceType::SRV:
|
||||
return GSTextureVK::Layout::ShaderReadOnly;
|
||||
case ResourceType::UAV:
|
||||
return GSTextureVK::Layout::ReadWriteImage;
|
||||
}
|
||||
}
|
||||
|
||||
struct alignas(8) PipelineSelector
|
||||
{
|
||||
GSHWDrawConfig::PSSelector ps;
|
||||
@@ -334,7 +354,7 @@ public:
|
||||
__fi bool IsDepthFeedbackLoop() const { return ((feedback_loop_flags & FeedbackLoopFlag_ReadAndWriteDepth) != 0); }
|
||||
__fi bool IsTestingAndSamplingDepth() const { return ((feedback_loop_flags & (FeedbackLoopFlag_ReadDepth | FeedbackLoopFlag_ReadAndWriteDepth)) != 0); }
|
||||
};
|
||||
static_assert(sizeof(PipelineSelector) == 24, "Pipeline selector is 24 bytes");
|
||||
static_assert(sizeof(PipelineSelector) == 32, "Pipeline selector is 32 bytes");
|
||||
|
||||
struct PipelineSelectorHash
|
||||
{
|
||||
@@ -368,6 +388,8 @@ public:
|
||||
TFX_TEXTURE_RT,
|
||||
TFX_TEXTURE_PRIMID,
|
||||
TFX_TEXTURE_DEPTH,
|
||||
TFX_TEXTURE_RT_ROV,
|
||||
TFX_TEXTURE_DEPTH_ROV,
|
||||
|
||||
NUM_TFX_TEXTURES
|
||||
};
|
||||
@@ -586,11 +608,12 @@ public:
|
||||
void IASetIndexBuffer(const void* index, size_t count);
|
||||
void VSSetIndexBuffer(const void* index, size_t count);
|
||||
|
||||
void PSSetShaderResource(int i, GSTexture* sr, bool check_state);
|
||||
void PSSetUnorderedAccess(GSTexture* rt, GSTexture* ds, bool write_rt, bool write_ds);
|
||||
void PSSetShaderResource(int i, GSTexture* sr, bool check_state, ResourceType type = ResourceType::SRV);
|
||||
void PSSetSampler(GSHWDrawConfig::SamplerSelector sel);
|
||||
|
||||
void OMSetRenderTargets(GSTexture* rt, GSTexture* ds, const GSVector4i& scissor,
|
||||
FeedbackLoopFlag feedback_loop = FeedbackLoopFlag_None);
|
||||
FeedbackLoopFlag feedback_loop = FeedbackLoopFlag_None, const GSVector2i& viewport_size = {});
|
||||
|
||||
void SetVSConstantBuffer(const GSHWDrawConfig::VSConstantBuffer& cb);
|
||||
void SetPSConstantBuffer(const GSHWDrawConfig::PSConstantBuffer& cb);
|
||||
@@ -654,28 +677,31 @@ public:
|
||||
private:
|
||||
enum DIRTY_FLAG : u32
|
||||
{
|
||||
DIRTY_FLAG_TFX_TEXTURE_0 = (1 << 0), // 0, 1, 2, 3, 4
|
||||
DIRTY_FLAG_TFX_UBO = (1 << 5),
|
||||
DIRTY_FLAG_UTILITY_TEXTURE = (1 << 6),
|
||||
DIRTY_FLAG_BLEND_CONSTANTS = (1 << 7),
|
||||
DIRTY_FLAG_LINE_WIDTH = (1 << 8),
|
||||
DIRTY_FLAG_INDEX_BUFFER = (1 << 9),
|
||||
DIRTY_FLAG_VIEWPORT = (1 << 10),
|
||||
DIRTY_FLAG_SCISSOR = (1 << 11),
|
||||
DIRTY_FLAG_PIPELINE = (1 << 12),
|
||||
DIRTY_FLAG_VS_CONSTANT_BUFFER = (1 << 13),
|
||||
DIRTY_FLAG_PS_CONSTANT_BUFFER = (1 << 14),
|
||||
DIRTY_FLAG_VS_PUSH_CONSTANTS = (1 << 15),
|
||||
DIRTY_FLAG_TFX_TEXTURE_0 = (1 << 0), // 0, 1, 2, 3, 4, 5, 6
|
||||
DIRTY_FLAG_TFX_UBO = (1 << 7),
|
||||
DIRTY_FLAG_UTILITY_TEXTURE = (1 << 8),
|
||||
DIRTY_FLAG_BLEND_CONSTANTS = (1 << 9),
|
||||
DIRTY_FLAG_LINE_WIDTH = (1 << 10),
|
||||
DIRTY_FLAG_INDEX_BUFFER = (1 << 11),
|
||||
DIRTY_FLAG_VIEWPORT = (1 << 12),
|
||||
DIRTY_FLAG_SCISSOR = (1 << 13),
|
||||
DIRTY_FLAG_PIPELINE = (1 << 14),
|
||||
DIRTY_FLAG_VS_CONSTANT_BUFFER = (1 << 15),
|
||||
DIRTY_FLAG_PS_CONSTANT_BUFFER = (1 << 16),
|
||||
DIRTY_FLAG_VS_PUSH_CONSTANTS = (1 << 17),
|
||||
|
||||
DIRTY_FLAG_TFX_TEXTURE_TEX = (DIRTY_FLAG_TFX_TEXTURE_0 << 0),
|
||||
DIRTY_FLAG_TFX_TEXTURE_PALETTE = (DIRTY_FLAG_TFX_TEXTURE_0 << 1),
|
||||
DIRTY_FLAG_TFX_TEXTURE_RT = (DIRTY_FLAG_TFX_TEXTURE_0 << 2),
|
||||
DIRTY_FLAG_TFX_TEXTURE_PRIMID = (DIRTY_FLAG_TFX_TEXTURE_0 << 3),
|
||||
DIRTY_FLAG_TFX_TEXTURE_DEPTH = (DIRTY_FLAG_TFX_TEXTURE_0 << 4),
|
||||
DIRTY_FLAG_TFX_TEXTURE_RT_ROV = (DIRTY_FLAG_TFX_TEXTURE_0 << 5),
|
||||
DIRTY_FLAG_TFX_TEXTURE_DEPTH_ROV = (DIRTY_FLAG_TFX_TEXTURE_0 << 6),
|
||||
|
||||
DIRTY_FLAG_TFX_TEXTURES = DIRTY_FLAG_TFX_TEXTURE_TEX | DIRTY_FLAG_TFX_TEXTURE_PALETTE |
|
||||
DIRTY_FLAG_TFX_TEXTURE_RT | DIRTY_FLAG_TFX_TEXTURE_PRIMID |
|
||||
DIRTY_FLAG_TFX_TEXTURE_DEPTH,
|
||||
DIRTY_FLAG_TFX_TEXTURE_DEPTH | DIRTY_FLAG_TFX_TEXTURE_RT_ROV |
|
||||
DIRTY_FLAG_TFX_TEXTURE_DEPTH_ROV,
|
||||
|
||||
DIRTY_BASE_STATE = DIRTY_FLAG_INDEX_BUFFER | DIRTY_FLAG_PIPELINE | DIRTY_FLAG_VIEWPORT | DIRTY_FLAG_SCISSOR |
|
||||
DIRTY_FLAG_BLEND_CONSTANTS | DIRTY_FLAG_LINE_WIDTH,
|
||||
@@ -716,7 +742,7 @@ private:
|
||||
float m_current_line_width = 1.0f;
|
||||
u8 m_blend_constant_color = 0;
|
||||
|
||||
std::array<const GSTextureVK*, NUM_TFX_TEXTURES> m_tfx_textures{};
|
||||
std::array<GSTextureVK*, NUM_TFX_TEXTURES> m_tfx_textures{};
|
||||
VkSampler m_tfx_sampler = VK_NULL_HANDLE;
|
||||
u32 m_tfx_sampler_sel = 0;
|
||||
VkDescriptorSet m_tfx_ubo_descriptor_set = VK_NULL_HANDLE;
|
||||
@@ -732,6 +758,7 @@ private:
|
||||
VkPipeline m_current_pipeline = VK_NULL_HANDLE;
|
||||
|
||||
std::unique_ptr<GSTextureVK> m_null_texture;
|
||||
VkFramebuffer m_null_framebuffer;
|
||||
|
||||
// current pipeline selector - we save this in the struct to avoid re-zeroing it every draw
|
||||
PipelineSelector m_pipeline_selector = {};
|
||||
|
||||
@@ -11,6 +11,24 @@
|
||||
#include "common/Console.h"
|
||||
#include "common/BitUtils.h"
|
||||
|
||||
VkFramebuffer GSTextureVK::CreateNullFramebuffer()
|
||||
{
|
||||
const VkRenderPass rp = GSDeviceVK::GetInstance()->GetRenderPass(
|
||||
VK_FORMAT_UNDEFINED,
|
||||
VK_FORMAT_UNDEFINED,
|
||||
VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_DONT_CARE, VK_ATTACHMENT_LOAD_OP_DONT_CARE,
|
||||
VK_ATTACHMENT_STORE_OP_DONT_CARE, VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_DONT_CARE, false, false);
|
||||
|
||||
if (!rp)
|
||||
return VK_NULL_HANDLE;
|
||||
|
||||
Vulkan::FramebufferBuilder fbb;
|
||||
fbb.SetSize(16384, 16384, 1);
|
||||
fbb.SetRenderPass(rp);
|
||||
|
||||
return fbb.Create(GSDeviceVK::GetInstance()->GetDevice());
|
||||
}
|
||||
|
||||
static constexpr const VkComponentMapping s_identity_swizzle{VK_COMPONENT_SWIZZLE_IDENTITY,
|
||||
VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY};
|
||||
|
||||
@@ -101,7 +119,7 @@ std::unique_ptr<GSTextureVK> GSTextureVK::Create(Type type, Format format, int w
|
||||
pxAssert(levels == 1);
|
||||
ici.usage =
|
||||
VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT |
|
||||
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT |
|
||||
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT |
|
||||
(GSDeviceVK::GetInstance()->UseFeedbackLoopLayout() ? VK_IMAGE_USAGE_ATTACHMENT_FEEDBACK_LOOP_BIT_EXT
|
||||
: VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT);
|
||||
}
|
||||
@@ -239,6 +257,10 @@ void GSTextureVK::Destroy(bool defer)
|
||||
m_image = VK_NULL_HANDLE;
|
||||
m_allocation = VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
#ifdef PCSX2_DEVBUILD
|
||||
m_debug_name.clear();
|
||||
#endif
|
||||
}
|
||||
|
||||
VkImageLayout GSTextureVK::GetVkLayout() const
|
||||
@@ -523,6 +545,8 @@ void GSTextureVK::SetDebugName(std::string_view name)
|
||||
|
||||
Vulkan::SetObjectName(GSDeviceVK::GetInstance()->GetDevice(), m_image, "%.*s", static_cast<int>(name.size()), name.data());
|
||||
Vulkan::SetObjectName(GSDeviceVK::GetInstance()->GetDevice(), m_view, "%.*s", static_cast<int>(name.size()), name.data());
|
||||
|
||||
m_debug_name = name;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -547,13 +571,17 @@ void GSTextureVK::CommitClear(VkCommandBuffer cmdbuf)
|
||||
const VkImageSubresourceRange srr = {VK_IMAGE_ASPECT_DEPTH_BIT, 0u, 1u, 0u, 1u};
|
||||
vkCmdClearDepthStencilImage(cmdbuf, m_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &cv, 1, &srr);
|
||||
}
|
||||
else
|
||||
else if (IsRenderTarget())
|
||||
{
|
||||
alignas(16) VkClearColorValue cv;
|
||||
GSVector4::store<true>(cv.float32, GetClearForFormat());
|
||||
const VkImageSubresourceRange srr = {VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u};
|
||||
vkCmdClearColorImage(cmdbuf, m_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &cv, 1, &srr);
|
||||
}
|
||||
else
|
||||
{
|
||||
pxFailRel("Illegal texture type for clear.");
|
||||
}
|
||||
|
||||
SetState(GSTexture::State::Dirty);
|
||||
}
|
||||
@@ -581,8 +609,22 @@ void GSTextureVK::TransitionToLayout(VkCommandBuffer command_buffer, Layout new_
|
||||
void GSTextureVK::TransitionSubresourcesToLayout(
|
||||
VkCommandBuffer command_buffer, int start_level, int num_levels, Layout old_layout, Layout new_layout)
|
||||
{
|
||||
// Windows RDNA2 drivers don't always correctly transition the layout(?) when ROV is involved.
|
||||
// ReadWriteImage -> Feedback transitions are broken.
|
||||
// ReadWriteImage -> Read only layout -> Feedback transitions are broken.
|
||||
// ReadWriteImage -> General layout -> Feedback transitions are broken.
|
||||
// ReadWriteImage -> Write only layout -> Feedback transitions works fine.
|
||||
// Not every broken transition gives broken rendering, the Shadow of the colossus eagle dump is fine after the 1st frame.
|
||||
// Transition to a write only layout using an extra barrier, then to feedback fixes this issue.
|
||||
if (old_layout == Layout::ReadWriteImage && new_layout != Layout::ColorAttachment && new_layout != Layout::ReadWriteImage)
|
||||
{
|
||||
GL_INS("VK: Doing extra transition for broken RDNA2 feedback transitions");
|
||||
TransitionSubresourcesToLayout(command_buffer, 0, num_levels, old_layout, Layout::ColorAttachment);
|
||||
old_layout = Layout::ColorAttachment;
|
||||
}
|
||||
|
||||
VkImageAspectFlags aspect;
|
||||
if (m_type == Type::DepthStencil)
|
||||
if (IsDepthStencil())
|
||||
{
|
||||
aspect = g_gs_device->Features().stencil_buffer ? (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT) :
|
||||
VK_IMAGE_ASPECT_DEPTH_BIT;
|
||||
@@ -754,6 +796,14 @@ void GSTextureVK::TransitionSubresourcesToLayout(
|
||||
break;
|
||||
}
|
||||
vkCmdPipelineBarrier(command_buffer, srcStageMask, dstStageMask, 0, 0, nullptr, 0, nullptr, 1, &barrier);
|
||||
|
||||
// Count as a UAV barrier if we transition to/from UAV.
|
||||
if (IsRenderTargetOrDepthStencil() &&
|
||||
(old_layout == Layout::ReadWriteImage || new_layout == Layout::ReadWriteImage))
|
||||
{
|
||||
g_perfmon.Put(GSPerfMon::Barriers, 1);
|
||||
g_perfmon.Put(GSPerfMon::BarriersROV, 1);
|
||||
}
|
||||
}
|
||||
|
||||
VkFramebuffer GSTextureVK::GetFramebuffer(bool feedback_loop)
|
||||
|
||||
@@ -42,6 +42,8 @@ public:
|
||||
__fi VkImage GetImage() const { return m_image; }
|
||||
__fi VkImageView GetView() const { return m_view; }
|
||||
__fi Layout GetLayout() const { return m_layout; }
|
||||
virtual bool IsUnorderedAccess() const override { return GetLayout() == Layout::ReadWriteImage; }
|
||||
|
||||
__fi VkFormat GetVkFormat() const { return m_vk_format; }
|
||||
|
||||
VkImageLayout GetVkLayout() const;
|
||||
@@ -70,6 +72,8 @@ public:
|
||||
void TransitionSubresourcesToLayout(
|
||||
VkCommandBuffer command_buffer, int start_level, int num_levels, Layout old_layout, Layout new_layout);
|
||||
|
||||
static VkFramebuffer CreateNullFramebuffer();
|
||||
|
||||
/// Framebuffers are lazily allocated.
|
||||
VkFramebuffer GetFramebuffer(bool feedback_loop);
|
||||
|
||||
|
||||
@@ -733,7 +733,7 @@ void Vulkan::DescriptorSetUpdateBuilder::PushUpdate(
|
||||
}
|
||||
|
||||
void Vulkan::DescriptorSetUpdateBuilder::AddImageDescriptorWrite(VkDescriptorSet set, u32 binding, VkImageView view,
|
||||
VkImageLayout layout /*= VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL*/)
|
||||
VkImageLayout layout /*= VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL*/, bool storage_image /*= false*/)
|
||||
{
|
||||
pxAssert(m_num_writes < MAX_WRITES && m_num_image_infos < MAX_IMAGE_INFOS);
|
||||
|
||||
@@ -747,7 +747,7 @@ void Vulkan::DescriptorSetUpdateBuilder::AddImageDescriptorWrite(VkDescriptorSet
|
||||
dw.dstSet = set;
|
||||
dw.dstBinding = binding;
|
||||
dw.descriptorCount = 1;
|
||||
dw.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
|
||||
dw.descriptorType = storage_image ? VK_DESCRIPTOR_TYPE_STORAGE_IMAGE : VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
|
||||
dw.pImageInfo = ⅈ
|
||||
}
|
||||
|
||||
|
||||
@@ -241,7 +241,7 @@ namespace Vulkan
|
||||
bool clear = true);
|
||||
|
||||
void AddImageDescriptorWrite(VkDescriptorSet set, u32 binding, VkImageView view,
|
||||
VkImageLayout layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
||||
VkImageLayout layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, bool storage_image = false);
|
||||
void AddSamplerDescriptorWrite(VkDescriptorSet set, u32 binding, VkSampler sampler);
|
||||
void AddSamplerDescriptorWrites(VkDescriptorSet set, u32 binding, const VkSampler* samplers, u32 num_samplers);
|
||||
void AddCombinedImageSamplerDescriptorWrite(VkDescriptorSet set, u32 binding, VkImageView view,
|
||||
|
||||
@@ -2862,6 +2862,7 @@ void FullscreenUI::DrawGraphicsSettingsPage(SettingsInterface* bsi, bool show_ad
|
||||
};
|
||||
static constexpr const char* s_hw_download[] = {
|
||||
FSUI_NSTR("Accurate (Recommended)"),
|
||||
FSUI_NSTR("Accurate Force Full (Can Reduce Readbacks)"),
|
||||
FSUI_NSTR("Disable Readbacks (Synchronize GS Thread)"),
|
||||
FSUI_NSTR("Unsynchronized (Non-Deterministic)"),
|
||||
FSUI_NSTR("Disabled (Ignore Transfers)"),
|
||||
@@ -3283,6 +3284,13 @@ void FullscreenUI::DrawGraphicsSettingsPage(SettingsInterface* bsi, bool show_ad
|
||||
if (show_advanced_settings)
|
||||
{
|
||||
MenuHeading(FSUI_CSTR("Advanced"));
|
||||
if (is_hardware && effective_renderer != GSRendererType::OGL)
|
||||
{
|
||||
DrawToggleSetting(bsi, FSUI_ICONSTR(ICON_FA_LAYER_GROUP, "Rasterizer Ordered View"),
|
||||
FSUI_CSTR("Enables Rasterizer Ordered View (ROV), which allows feedback loops to be executed with fewer draw calls. Can improve performance in feedback heavy games "
|
||||
"with higher accuracy settings."),
|
||||
"EmuCore/GS", "HWROV", true);
|
||||
}
|
||||
DrawToggleSetting(bsi, FSUI_ICONSTR(ICON_FA_FORWARD, "Skip Presenting Duplicate Frames"),
|
||||
FSUI_CSTR("Skips displaying frames that don't change in 25/30fps games. Can improve speed, but increase input lag/make frame pacing "
|
||||
"worse."),
|
||||
@@ -3315,6 +3323,9 @@ void FullscreenUI::DrawGraphicsSettingsPage(SettingsInterface* bsi, bool show_ad
|
||||
"EmuCore/GS", "DisableShaderCache", false);
|
||||
DrawToggleSetting(bsi, FSUI_ICONSTR(ICON_FA_BAN, "Disable Vertex Shader Expand"), FSUI_CSTR("Falls back to the CPU for expanding sprites/lines."),
|
||||
"EmuCore/GS", "DisableVertexShaderExpand", false);
|
||||
DrawToggleSetting(bsi, FSUI_ICONSTR(ICON_FA_ROAD_BARRIER, "ROV Barriers Vulkan"),
|
||||
FSUI_CSTR("Forces extra barriers when using ROV with Vulkan to fix graphical issues present in some games and hardware configurations."),
|
||||
"EmuCore/GS", "HWROVBarriersVK", false);
|
||||
DrawIntListSetting(bsi, FSUI_ICONSTR(ICON_FA_DOWNLOAD, "Texture Preloading"),
|
||||
FSUI_CSTR(
|
||||
"Uploads full textures to the GPU on use, rather than only the utilized regions. Can improve performance in some games."),
|
||||
@@ -5320,6 +5331,7 @@ TRANSLATE_NOOP("FullscreenUI", "Adjusts gamma. 50 is normal.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Adjusts saturation. 50 is normal.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Applies a shader which replicates the visual effects of different styles of television set.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Advanced");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Enables Rasterizer Ordered View (ROV), which allows feedback loops to be executed with fewer draw calls. Can improve performance in feedback heavy games with higher accuracy settings.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Skips displaying frames that don't change in 25/30fps games. Can improve speed, but increase input lag/make frame pacing worse.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Forces the use of FIFO over Mailbox presentation, i.e. double buffering instead of triple buffering. Usually results in worse frame pacing.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Displays additional, very high upscaling multipliers dependent on GPU and driver capability.");
|
||||
@@ -5330,6 +5342,7 @@ TRANSLATE_NOOP("FullscreenUI", "Sets the compression algorithm for GS dumps.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Prevents the usage of framebuffer fetch when supported by host GPU.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Prevents the loading and saving of shaders/pipelines to disk.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Falls back to the CPU for expanding sprites/lines.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Forces extra barriers when using ROV with Vulkan to fix graphical issues present in some games and hardware configurations.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Uploads full textures to the GPU on use, rather than only the utilized regions. Can improve performance in some games.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Determines what frame rate NTSC games run at.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Determines what frame rate PAL games run at.");
|
||||
@@ -5859,6 +5872,7 @@ TRANSLATE_NOOP("FullscreenUI", "Shade Boost Contrast");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Shade Boost Gamma");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Shade Boost Saturation");
|
||||
TRANSLATE_NOOP("FullscreenUI", "TV Shaders");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Rasterizer Ordered View");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Skip Presenting Duplicate Frames");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Disable Mailbox Presentation");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Extended Upscaling Multipliers");
|
||||
@@ -5869,6 +5883,7 @@ TRANSLATE_NOOP("FullscreenUI", "GS Dump Compression");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Disable Framebuffer Fetch");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Disable Shader Cache");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Disable Vertex Shader Expand");
|
||||
TRANSLATE_NOOP("FullscreenUI", "ROV Barriers Vulkan");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Texture Preloading");
|
||||
TRANSLATE_NOOP("FullscreenUI", "NTSC Frame Rate");
|
||||
TRANSLATE_NOOP("FullscreenUI", "PAL Frame Rate");
|
||||
|
||||
@@ -916,6 +916,12 @@ __ri void ImGuiManager::DrawSettingsOverlay(float scale, float margin, float spa
|
||||
if (GSConfig.HWAA1)
|
||||
APPEND("AA1 ");
|
||||
|
||||
if (GSConfig.HWROV)
|
||||
APPEND("ROV ");
|
||||
|
||||
if (GSConfig.HWROVBarriersVK)
|
||||
APPEND("RBVK ");
|
||||
|
||||
// deliberately test global and print local here for auto values
|
||||
if (EmuConfig.GS.TextureFiltering != BiFiltering::PS2)
|
||||
APPEND("BF={} ", static_cast<unsigned>(GSConfig.TextureFiltering));
|
||||
|
||||
+18
-20
@@ -384,11 +384,22 @@ std::vector<u8>& PINEServer::MakeFailIPC(std::vector<u8>& ret_buffer, uint32_t s
|
||||
bool PINEServer::AcceptClient()
|
||||
{
|
||||
s_msgsock = accept(s_sock, 0, 0);
|
||||
if (s_msgsock >= 0)
|
||||
if (s_msgsock < 0)
|
||||
{
|
||||
// Gross C-style cast, but SOCKET is a handle on Windows.
|
||||
Console.WriteLn("PINE: New client with FD %d connected.", (int)s_msgsock);
|
||||
return true;
|
||||
// everything else is non recoverable in our scope
|
||||
// we also mark as recoverable socket errors where it would block a
|
||||
// non blocking socket, even though our socket is blocking, in case
|
||||
// we ever have to implement a non blocking socket.
|
||||
#ifdef _WIN32
|
||||
const int errno_w = WSAGetLastError();
|
||||
if (!(errno_w == WSAECONNRESET || errno_w == WSAEINTR || errno_w == WSAEINPROGRESS || errno_w == WSAEMFILE || errno_w == WSAEWOULDBLOCK) && s_sock != INVALID_SOCKET)
|
||||
Console.Error("PINE: accept() returned error %d", errno_w);
|
||||
#else
|
||||
if (!(errno == ECONNABORTED || errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) && s_sock >= 0)
|
||||
Console.Error("PINE: accept() returned error %d", errno);
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
@@ -396,20 +407,9 @@ bool PINEServer::AcceptClient()
|
||||
setsockopt(s_msgsock, SOL_SOCKET, SO_NOSIGPIPE, &nosigpipe, sizeof(nosigpipe));
|
||||
#endif
|
||||
|
||||
// everything else is non recoverable in our scope
|
||||
// we also mark as recoverable socket errors where it would block a
|
||||
// non blocking socket, even though our socket is blocking, in case
|
||||
// we ever have to implement a non blocking socket.
|
||||
#ifdef _WIN32
|
||||
const int errno_w = WSAGetLastError();
|
||||
if (!(errno_w == WSAECONNRESET || errno_w == WSAEINTR || errno_w == WSAEINPROGRESS || errno_w == WSAEMFILE || errno_w == WSAEWOULDBLOCK) && s_sock != INVALID_SOCKET)
|
||||
Console.Error("PINE: accept() returned error %d", errno_w);
|
||||
#else
|
||||
if (!(errno == ECONNABORTED || errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) && s_sock >= 0)
|
||||
Console.Error("PINE: accept() returned error %d", errno);
|
||||
#endif
|
||||
|
||||
return false;
|
||||
// Gross C-style cast, but SOCKET is a handle on Windows.
|
||||
Console.WriteLn("PINE: New client with FD %d connected.", (int)s_msgsock);
|
||||
return true;
|
||||
}
|
||||
|
||||
void PINEServer::MainLoop()
|
||||
@@ -629,8 +629,6 @@ PINEServer::IPCBuffer PINEServer::ParseCommand(std::span<u8> buf, std::vector<u8
|
||||
}
|
||||
case MsgVersion:
|
||||
{
|
||||
if (!VMManager::HasValidVM())
|
||||
goto error;
|
||||
u32 size = strlen(BuildVersion::GitRev) + 7;
|
||||
if (!SafetyChecks(buf_cnt, 0, ret_cnt, size + 4, buf_size)) [[unlikely]]
|
||||
goto error;
|
||||
|
||||
@@ -755,6 +755,9 @@ Pcsx2Config::GSOptions::GSOptions()
|
||||
HWAccurateAlphaTest = false;
|
||||
HWAA1 = false;
|
||||
UseDebugBlend = false;
|
||||
HWROV = true;
|
||||
HWROVLogging = false;
|
||||
HWROVBarriersVK = false;
|
||||
|
||||
ManualUserHacks = false;
|
||||
UserHacks_AlignSpriteX = false;
|
||||
@@ -1043,6 +1046,9 @@ void Pcsx2Config::GSOptions::LoadSave(SettingsWrapper& wrap)
|
||||
SettingsWrapBitBool(HWAccurateAlphaTest);
|
||||
SettingsWrapBitBool(HWAA1);
|
||||
SettingsWrapBitBool(UseDebugBlend);
|
||||
SettingsWrapBitBool(HWROV);
|
||||
SettingsWrapBitBool(HWROVLogging);
|
||||
SettingsWrapBitBool(HWROVBarriersVK);
|
||||
SettingsWrapIntEnumEx(AccurateBlendingUnit, "accurate_blending_unit");
|
||||
SettingsWrapIntEnumEx(TextureFiltering, "filter");
|
||||
SettingsWrapIntEnumEx(TexturePreloading, "texture_preloading");
|
||||
|
||||
@@ -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 = 98; // Last changed in PR 14477
|
||||
static constexpr u32 SHADER_CACHE_VERSION = 100; // Last changed in PR 14503
|
||||
|
||||
+1
-1
@@ -20,7 +20,7 @@
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
#define PSXCLK 36864000 /* 36.864 Mhz */
|
||||
#define PSXCLK 36864000 /* 36.864 MHz */
|
||||
|
||||
namespace USB
|
||||
{
|
||||
|
||||
+1
-1
@@ -3252,7 +3252,7 @@ void VMManager::WarnAboutUnsafeSettings()
|
||||
append(ICON_FA_PAINTBRUSH,
|
||||
TRANSLATE_SV("VMManager", "Blending Accuracy is below Basic, this may break effects in some games."));
|
||||
}
|
||||
if (EmuConfig.GS.HWDownloadMode != GSHardwareDownloadMode::Enabled)
|
||||
if (EmuConfig.GS.HWDownloadMode > GSHardwareDownloadMode::EnabledForceFull)
|
||||
{
|
||||
append(ICON_FA_DOWNLOAD,
|
||||
TRANSLATE_SV("VMManager", "Hardware Download Mode is not set to Accurate, this may break rendering in some games."));
|
||||
|
||||
Reference in New Issue
Block a user