Compare commits

..
35 Commits
Author SHA1 Message Date
arcum42 60047e8029 common: Remove some unneeded includes. 2023-04-08 20:26:39 +01:00
arcum42 e37fff1213 common: Remove SafeList and SafeAllignedArray as unused. 2023-04-08 20:26:39 +01:00
JordanTheToaster 64a6d8027b GS: Reset performance counters on renderer switch
Resets performance counters on renderer switch.
2023-04-08 13:37:21 +01:00
Stenzek cd4434135e GSRunner: Don't skip dumps with dots in their filename 2023-04-08 13:36:45 +01:00
Stenzek 747e11d7dd GS/HW: Allow region targets for half-right fix 2023-04-08 12:44:33 +01:00
Stenzek 3cae728aba Qt: Recreate new window immediately when switching APIs 2023-04-08 12:42:34 +02:00
Stenzek 7f24a5cf82 GS: Fix crash when resizing window 2023-04-08 04:05:45 +01:00
refractionpcsx2 1b8f5f232a GS-HW: Fix bug with looping invalidation 2023-04-08 00:08:08 +01:00
refractionpcsx2 77d37de18c GameDB: Add mipmapping to Hasbro Family Game Night 2023-04-07 20:34:03 +01:00
Mrlinkwii 5ea3fb8e1e Github: Update issue template 2023-04-07 19:52:50 +01:00
refractionpcsx2 d683aa43d8 GS: Clean up Host->Local code 2023-04-07 17:18:33 +01:00
refractionpcsx2 d4c3501bb8 GS: Retool downloads to buffer full read 2023-04-07 17:18:33 +01:00
Stenzek f2229a0007 GS/OGL: Don't leak shader objects when compiling 2023-04-07 17:18:21 +01:00
Stenzek 2a06bb6e2c GS/OGL: Use VS/GS selectors from base class 2023-04-07 17:18:21 +01:00
lightningterror a06f890ab9 GS: Fix Wunused-variable warning. 2023-04-07 01:26:00 +02:00
refractionpcsx2 bbe22f95d1 GS: Avoid modifying single QW downloads as they go to a register
Also avoid downloading more than requested
2023-04-06 23:26:59 +01:00
Stenzek 631f75a79c GS/HW: Don't break out of target search loop on dirty target 2023-04-06 14:28:19 +01:00
Stenzek c121aae8f1 GS: Add VS natvis files for FastList and TC Targets 2023-04-06 14:27:36 +01:00
refractionpcsx2 fed7629632 GS: Fix typo in ResizeTexture which was causing a crash 2023-04-06 14:27:08 +01:00
Stenzek 0825ca736f GS/HW: Add other StretchRect() calls to texture copies counter
Better reflects GPU work.
2023-04-06 14:20:20 +01:00
Stenzek 660974c9d1 GS/HW: Update target in new format when reinterpreting
Fixes block scrambling in Budokai 3 during screen transitions.
2023-04-06 14:20:20 +01:00
Stenzek 4404b06d2a Qt: Fix lockup when cancelling shutdown and fullscreen 2023-04-06 08:48:07 +01:00
Stenzek daef69099b Qt: Fix improper shutdown with CTRL+C 2023-04-06 08:48:07 +01:00
Stenzek 6362994fd8 GS: Default to Vulkan for suitable GPUs on Linux 2023-04-06 08:48:07 +01:00
Stenzek 398cf43782 GS: Combine HostDisplay with GSDevice
GS/DX11: Don't throw bad_alloc on surface creation fail

GS: Link device and host display construction/destruction

FullscreenUI: Replace HostDisplayTexture with GSTexture

GS: Purge HostDisplayTexture

GS: Move everything in HostDisplay to GSDevice

GS: Move ImGui rendering to GSDevice

GS: Get rid of reset/store API state
2023-04-06 08:48:07 +01:00
refractionpcsx2 77f8a0f5f6 Counters: Retool UpdateVSyncRate 2023-04-06 08:47:35 +01:00
lightningterror d7e54ccbed CDVD: Fix struct member variables not used.
Codacy.
2023-04-05 20:15:18 +02:00
lightningterror 53a8855696 INISettingsInterface: Fix scope of variable warning.
Codacy.
2023-04-05 20:15:18 +02:00
lightningterror bd16ed1340 iR5900: Fix scope of variable warning.
Codacy.
2023-04-05 20:15:18 +02:00
lightningterror dd365fe334 GS-hw/tc: Fix Wsign-compare warnings. 2023-04-05 20:15:18 +02:00
refractionpcsx2 2a6ba739bc GS-HW: Fix stretch rects errantly discarding data 2023-04-05 16:29:36 +01:00
refractionpcsx2 a863466f70 GS-HW: Better handle double half clears + masked channels + invalidate
Also add Auto Flush to Power Drome US, apparently it wasn't there.
2023-04-05 16:29:36 +01:00
refractionpcsx2 32f07f4aae GS-HW: Read back TC on GS CSR Reset 2023-04-05 16:29:36 +01:00
refractionpcsx2 3ed0e010be GS-TC: Improve handing of wrapping targets in tex in rt 2023-04-05 16:29:36 +01:00
GiladNir fb3c0c8138 GameDB: Added fixes for Fatal Frame and Siren 2023-04-05 15:10:48 +01:00
99 changed files with 4837 additions and 8062 deletions
+4 -1
View File
@@ -20,11 +20,14 @@ body:
### Please Avoid Issues Pertaining to the Following:
- We are **not** accepting bug reports for **PSX mode** at this time
- If you are interested in helping contribute to PSX mode please do so on the forums. Otherwise our recommendation is that you use a [proper PSX emulator](https://emulation.gametechwiki.com/index.php/PlayStation_emulators)
- If you are interested in helping contribute to PSX mode please do so on the forums. Otherwise our recommendation is that you use [Duckstation](https://github.com/stenzek/duckstation/releases/tag/latest)
- We do **not** accept issues relating to **upscaling** at this time
- We are aware of the various problems with upscaling. The issue spans many games and having hundreds of issues for the same fundamental issues isn't particularly helpful. There are several workarounds for graphical problems that come as a result of upscaling
- Please try your game at native resolution before creating an issue
- If your bug is the result of upscaling please use the forums or discord for assistance with various upscaling workarounds. Additionally, the unofficial PCSX2 [Wiki](https://wiki.pcsx2.net/Main_Page) often lists various fixes for upscaling issues
- We do **not** accept issues relating to Widescreen/no-interlace patches at this time
- Any issues pertaining to Widescreen/no-interlace patches please forward them to the [patches repository](https://github.com/PCSX2/pcsx2_patches).
- type: textarea
id: desc
attributes:
+82
View File
@@ -675,6 +675,10 @@ SCAJ-20045:
SCAJ-20046:
name: "Siren"
region: "NTSC-Unk"
gsHWFixes:
roundSprite: 1 # Fixes gaps between menu options.
cpuSpriteRenderBW: 4 # Fixes sky rendering.
cpuSpriteRenderLevel: 2 # Needed for above.
SCAJ-20047:
name: "Time Crisis 3"
region: "NTSC-Unk"
@@ -1559,6 +1563,10 @@ SCAJ-30002:
SCAJ-30003:
name: "Siren"
region: "NTSC-Unk"
gsHWFixes:
roundSprite: 1 # Fixes gaps between menu options.
cpuSpriteRenderBW: 4 # Fixes sky rendering.
cpuSpriteRenderLevel: 2 # Needed for above.
SCAJ-30004:
name: "Kan Wo Long Xian Shen Wei"
region: "NTSC-CH"
@@ -2178,6 +2186,10 @@ SCED-52057:
SCED-52260:
name: "Forbidden Siren [Demo]"
region: "PAL-M5"
gsHWFixes:
roundSprite: 1 # Fixes gaps between menu options.
cpuSpriteRenderBW: 4 # Fixes sky rendering.
cpuSpriteRenderLevel: 2 # Needed for above.
SCED-52261:
name: "Jet Li - Rise to Honor [Demo]"
region: "PAL-M5"
@@ -3586,6 +3598,10 @@ SCES-51920:
name: "Forbidden Siren"
region: "PAL-E"
compat: 5
gsHWFixes:
roundSprite: 1 # Fixes gaps between menu options.
cpuSpriteRenderBW: 4 # Fixes sky rendering.
cpuSpriteRenderLevel: 2 # Needed for above.
SCES-51971:
name: "Jet Li - Rise to Honor"
region: "PAL-E"
@@ -3666,16 +3682,32 @@ SCES-52306:
SCES-52327:
name: "Forbidden Siren"
region: "PAL-F"
gsHWFixes:
roundSprite: 1 # Fixes gaps between menu options.
cpuSpriteRenderBW: 4 # Fixes sky rendering.
cpuSpriteRenderLevel: 2 # Needed for above.
SCES-52328:
name: "Forbidden Siren"
region: "PAL-G"
compat: 5
gsHWFixes:
roundSprite: 1 # Fixes gaps between menu options.
cpuSpriteRenderBW: 4 # Fixes sky rendering.
cpuSpriteRenderLevel: 2 # Needed for above.
SCES-52329:
name: "Forbidden Siren"
region: "PAL-I"
gsHWFixes:
roundSprite: 1 # Fixes gaps between menu options.
cpuSpriteRenderBW: 4 # Fixes sky rendering.
cpuSpriteRenderLevel: 2 # Needed for above.
SCES-52330:
name: "Forbidden Siren - Viviendo la Pesadilla"
region: "PAL-S"
gsHWFixes:
roundSprite: 1 # Fixes gaps between menu options.
cpuSpriteRenderBW: 4 # Fixes sky rendering.
cpuSpriteRenderLevel: 2 # Needed for above.
SCES-52389:
name: "WRC 4"
region: "PAL-M8"
@@ -5025,6 +5057,10 @@ SCKA-20018:
SCKA-20019:
name: "Siren"
region: "NTSC-K"
gsHWFixes:
roundSprite: 1 # Fixes gaps between menu options.
cpuSpriteRenderBW: 4 # Fixes sky rendering.
cpuSpriteRenderLevel: 2 # Needed for above.
SCKA-20020:
name: "SOCOM II - U.S. Navy SEALs"
region: "NTSC-K"
@@ -5903,6 +5939,10 @@ SCPS-15053:
name: "Siren"
region: "NTSC-J"
compat: 5
gsHWFixes:
roundSprite: 1 # Fixes gaps between menu options.
cpuSpriteRenderBW: 4 # Fixes sky rendering.
cpuSpriteRenderLevel: 2 # Needed for above.
SCPS-15054:
name: "ChainDive"
region: "NTSC-J"
@@ -6559,6 +6599,10 @@ SCPS-19304:
SCPS-19305:
name: "Siren [PlayStation 2 The Best]"
region: "NTSC-J"
gsHWFixes:
roundSprite: 1 # Fixes gaps between menu options.
cpuSpriteRenderBW: 4 # Fixes sky rendering.
cpuSpriteRenderLevel: 2 # Needed for above.
SCPS-19306:
name: "Dark Chronicle [PlayStation 2 The Best]"
region: "NTSC-J"
@@ -6604,6 +6648,10 @@ SCPS-19311:
SCPS-19312:
name: "Siren [PlayStation 2 The Best]"
region: "NTSC-J"
gsHWFixes:
roundSprite: 1 # Fixes gaps between menu options.
cpuSpriteRenderBW: 4 # Fixes sky rendering.
cpuSpriteRenderLevel: 2 # Needed for above.
SCPS-19313:
name: "Wild ARMs - The 4th Detonator [PlayStation 2 The Best]"
region: "NTSC-J"
@@ -6840,6 +6888,7 @@ SCPS-55002:
region: "NTSC-J"
gsHWFixes:
halfPixelOffset: 2 # Reduces blurriness.
alignSprite: 1 # Fixes vertical lines on hidden ghosts.
SCPS-55003:
name: "Vampire Night"
region: "NTSC-J"
@@ -7021,6 +7070,7 @@ SCPS-55043:
region: "NTSC-J"
gsHWFixes:
halfPixelOffset: 2 # Reduces blurriness.
alignSprite: 1 # Fixes vertical lines on hidden ghosts.
SCPS-55044:
name: "Energy Airforce"
region: "NTSC-J"
@@ -7123,6 +7173,7 @@ SCPS-56008:
compat: 5
gsHWFixes:
halfPixelOffset: 2 # Reduces blurriness.
alignSprite: 1 # Fixes vertical lines on hidden ghosts.
SCPS-56009:
name: "Smash Court Pro Tournament"
region: "NTSC-K"
@@ -8055,6 +8106,10 @@ SCUS-97355:
name: "Siren"
region: "NTSC-U"
compat: 5
gsHWFixes:
roundSprite: 1 # Fixes gaps between menu options.
cpuSpriteRenderBW: 4 # Fixes sky rendering.
cpuSpriteRenderLevel: 2 # Needed for above.
SCUS-97356:
name: "Network Adapter Start-Up Disc Ver.2.5"
region: "NTSC-U"
@@ -8160,6 +8215,10 @@ SCUS-97396:
SCUS-97398:
name: "Siren [Demo]"
region: "NTSC-U"
gsHWFixes:
roundSprite: 1 # Fixes gaps between menu options.
cpuSpriteRenderBW: 4 # Fixes sky rendering.
cpuSpriteRenderLevel: 2 # Needed for above.
SCUS-97399:
name: "God of War"
region: "NTSC-U"
@@ -11538,6 +11597,7 @@ SLES-50821:
compat: 5
gsHWFixes:
halfPixelOffset: 2 # Fixes blurriness.
alignSprite: 1 # Fixes vertical lines on hidden ghosts.
patches:
"22E91837":
content: |-
@@ -22474,6 +22534,9 @@ SLES-55218:
SLES-55219:
name: "Hasbro Family Game Night"
region: "PAL-M4"
gsHWFixes:
mipmap: 2 # Mipmap + trilinear, fixes textures on battleships model.
trilinearFiltering: 1
SLES-55220:
name: "Summer Athletics"
region: "PAL-M5"
@@ -22523,6 +22586,9 @@ SLES-55238:
SLES-55239:
name: "Hasbro Family Game Night"
region: "PAL-I"
gsHWFixes:
mipmap: 2 # Mipmap + trilinear, fixes textures on battleships model.
trilinearFiltering: 1
SLES-55240:
name: "Pipe Mania"
region: "PAL-M5"
@@ -22718,9 +22784,15 @@ SLES-55340:
SLES-55341:
name: "Hasbro Family Game Night"
region: "PAL-G"
gsHWFixes:
mipmap: 2 # Mipmap + trilinear, fixes textures on battleships model.
trilinearFiltering: 1
SLES-55342:
name: "Hasbro Family Game Night"
region: "PAL-E"
gsHWFixes:
mipmap: 2 # Mipmap + trilinear, fixes textures on battleships model.
trilinearFiltering: 1
SLES-55343:
name: "Tube Mania"
region: "PAL-F"
@@ -25651,6 +25723,7 @@ SLPM-60167:
region: "NTSC-J"
gsHWFixes:
halfPixelOffset: 2 # Reduces blurriness.
alignSprite: 1 # Fixes vertical lines on hidden ghosts.
SLPM-60149:
name: "Ace Combat 04 - Shattered Skies [Trial]"
region: "NTSC-J"
@@ -37875,6 +37948,7 @@ SLPS-25074:
compat: 5
gsHWFixes:
halfPixelOffset: 2 # Fixes blurriness.
alignSprite: 1 # Fixes vertical lines on hidden ghosts.
patches:
9883194E:
content: |-
@@ -41508,6 +41582,7 @@ SLPS-73255:
region: "NTSC-J"
gsHWFixes:
halfPixelOffset: 2 # Reduces blurriness.
alignSprite: 1 # Fixes vertical lines on hidden ghosts.
SLPS-73256:
name: "Fatal Frame II - Crimson Butterfly [PlayStation 2 the Best - Reprint]"
region: "NTSC-J"
@@ -41567,6 +41642,7 @@ SLPS-73405:
region: "NTSC-J"
gsHWFixes:
halfPixelOffset: 2 # Reduces blurriness.
alignSprite: 1 # Fixes vertical lines on hidden ghosts.
SLPS-73406:
name: "DOA 2 - Hardcore [PlayStation 2 The Best]"
region: "NTSC-J"
@@ -43290,6 +43366,7 @@ SLUS-20388:
compat: 5
gsHWFixes:
halfPixelOffset: 2 # Reduces blurriness.
alignSprite: 1 # Fixes vertical lines on hidden ghosts.
patches:
339A0B8C:
content: |-
@@ -46231,6 +46308,8 @@ SLUS-20978:
name: "Power Drome"
region: "NTSC-U"
compat: 4
gsHWFixes:
autoFlush: 1
patches:
42E15DEF:
content: |-
@@ -50554,6 +50633,9 @@ SLUS-21805:
name: "Hasbro Family Game Night"
region: "NTSC-U"
compat: 5
gsHWFixes:
mipmap: 2 # Mipmap + trilinear, fixes textures on battleships model.
trilinearFiltering: 1
SLUS-21806:
name: "Barbie Horse Adventures - Riding Camp"
region: "NTSC-U"
+36
View File
@@ -0,0 +1,36 @@
cbuffer vertexBuffer : register(b0)
{
float4x4 ProjectionMatrix;
};
struct VS_INPUT
{
float2 pos : POSITION;
float4 col : COLOR0;
float2 uv : TEXCOORD0;
};
struct PS_INPUT
{
float4 pos : SV_POSITION;
float4 col : COLOR0;
float2 uv : TEXCOORD0;
};
PS_INPUT vs_main(VS_INPUT input)
{
PS_INPUT output;
output.pos = mul(ProjectionMatrix, float4(input.pos.xy, 0.f, 1.f));
output.col = input.col;
output.uv = input.uv;
return output;
}
sampler sampler0 : register(s0);
Texture2D texture0 : register(t0);
float4 ps_main(PS_INPUT input) : SV_Target
{
float4 out_col = input.col * texture0.Sample(sampler0, input.uv);
return out_col;
}
+35
View File
@@ -0,0 +1,35 @@
#ifdef VERTEX_SHADER
layout(location = 0) in vec2 Position;
layout(location = 1) in vec2 UV;
layout(location = 2) in vec4 Color;
uniform mat4 ProjMtx;
out vec2 Frag_UV;
out vec4 Frag_Color;
void vs_main()
{
Frag_UV = UV;
Frag_Color = Color;
gl_Position = ProjMtx * vec4(Position.xy, 0.0, 1.0);
}
#endif
#ifdef FRAGMENT_SHADER
layout(binding = 0) uniform sampler2D Texture;
in vec2 Frag_UV;
in vec4 Frag_Color;
layout(location = 0) out vec4 Out_Color;
void ps_main()
{
Out_Color = Frag_Color * texture(Texture, Frag_UV.st);
}
#endif
+9 -9
View File
@@ -46,12 +46,12 @@ void texture_coord()
// Integer coordinate => normalized
VSout.t_int.xy = uv * TextureScale;
#if VS_INT_FST == 1
// Some games uses float coordinate for post-processing effect
VSout.t_int.zw = st / TextureScale;
#else
#if VS_FST
// Integer coordinate => integral
VSout.t_int.zw = uv;
#else
// Some games uses float coordinate for post-processing effect
VSout.t_int.zw = st / TextureScale;
#endif
}
@@ -129,7 +129,7 @@ void out_vertex(in vec4 position, in vertex v)
GSout.t_float = v.t_float;
GSout.t_int = v.t_int;
// Flat output
#if GS_POINT == 1
#if GS_PRIM == 0
GSout.c = GSin[0].c;
#else
GSout.c = GSin[1].c;
@@ -139,14 +139,14 @@ void out_vertex(in vec4 position, in vertex v)
EmitVertex();
}
#if GS_POINT == 1
#if GS_PRIM == 0
layout(points) in;
#else
layout(lines) in;
#endif
layout(triangle_strip, max_vertices = 4) out;
#if GS_POINT == 1
#if GS_PRIM == 0
void gs_main()
{
@@ -172,7 +172,7 @@ void gs_main()
EndPrimitive();
}
#elif GS_LINE == 1
#elif GS_PRIM == 1
void gs_main()
{
@@ -203,7 +203,7 @@ void gs_main()
EndPrimitive();
}
#else
#else // GS_PRIM == 3
void gs_main()
{
+39
View File
@@ -0,0 +1,39 @@
#ifdef VERTEX_SHADER
layout(location = 0) in vec2 Position;
layout(location = 1) in vec2 UV;
layout(location = 2) in vec4 Color;
layout(push_constant) uniform PushConstants
{
vec2 uScale;
vec2 uTranslate;
};
layout(location = 0) out vec2 Frag_UV;
layout(location = 1) out vec4 Frag_Color;
void vs_main()
{
Frag_UV = UV;
Frag_Color = Color;
gl_Position = vec4(Position * uScale + uTranslate, 0.0f, 1.0f);
}
#endif
#ifdef FRAGMENT_SHADER
layout(binding = 0) uniform sampler2D Texture;
layout(location = 0) in vec2 Frag_UV;
layout(location = 1) in vec4 Frag_Color;
layout(location = 0) out vec4 Out_Color;
void ps_main()
{
Out_Color = Frag_Color * texture(Texture, Frag_UV.st);
}
#endif
+15 -4
View File
@@ -41,10 +41,7 @@ D3D11::ShaderCache::ShaderCache() = default;
D3D11::ShaderCache::~ShaderCache()
{
if (m_index_file)
std::fclose(m_index_file);
if (m_blob_file)
std::fclose(m_blob_file);
Close();
}
bool D3D11::ShaderCache::CacheIndexKey::operator==(const CacheIndexKey& key) const
@@ -82,6 +79,20 @@ bool D3D11::ShaderCache::Open(std::string_view base_path, D3D_FEATURE_LEVEL feat
return true;
}
void D3D11::ShaderCache::Close()
{
if (m_index_file)
{
std::fclose(m_index_file);
m_index_file = nullptr;
}
if (m_blob_file)
{
std::fclose(m_blob_file);
m_blob_file = nullptr;
}
}
bool D3D11::ShaderCache::CreateNew(const std::string& index_filename, const std::string& blob_filename)
{
if (FileSystem::FileExists(index_filename.c_str()))
+1 -1
View File
@@ -38,6 +38,7 @@ namespace D3D11
bool UsingDebugShaders() const { return m_debug; }
bool Open(std::string_view base_path, D3D_FEATURE_LEVEL feature_level, u32 version, bool debug);
void Close();
wil::com_ptr_nothrow<ID3DBlob> GetShaderBlob(ShaderCompiler::Type type, const std::string_view& shader_code,
const D3D_SHADER_MACRO* macros = nullptr, const char* entry_point = "main");
@@ -103,7 +104,6 @@ namespace D3D11
bool CreateNew(const std::string& index_filename, const std::string& blob_filename);
bool ReadExisting(const std::string& index_filename, const std::string& blob_filename);
void Close();
wil::com_ptr_nothrow<ID3DBlob> CompileAndAddShaderBlob(const CacheIndexKey& key, const std::string_view& shader_code,
const D3D_SHADER_MACRO* macros, const char* entry_point);
+6
View File
@@ -130,7 +130,10 @@ bool Context::Create(IDXGIFactory5* dxgi_factory, IDXGIAdapter1* adapter, bool e
pxAssertRel(!g_d3d12_context, "No context exists");
if (!LoadD3D12Library())
{
Console.Error("Failed to load D3D12 library");
return false;
}
g_d3d12_context.reset(new Context());
if (!g_d3d12_context->CreateDevice(dxgi_factory, adapter, enable_debug_layer) ||
@@ -188,7 +191,10 @@ bool Context::CreateDevice(IDXGIFactory5* dxgi_factory, IDXGIAdapter1* adapter,
// Create the actual device.
hr = s_d3d12_create_device(adapter, D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&m_device));
if (FAILED(hr))
{
Console.Error("Failed to create D3D12 device: %08X", hr);
return false;
}
// get adapter
const LUID luid(m_device->GetAdapterLuid());
+27 -8
View File
@@ -45,14 +45,7 @@ ShaderCache::ShaderCache() = default;
ShaderCache::~ShaderCache()
{
if (m_pipeline_index_file)
std::fclose(m_pipeline_index_file);
if (m_pipeline_blob_file)
std::fclose(m_pipeline_blob_file);
if (m_shader_index_file)
std::fclose(m_shader_index_file);
if (m_shader_blob_file)
std::fclose(m_shader_blob_file);
Close();
}
bool ShaderCache::CacheIndexKey::operator==(const CacheIndexKey& key) const
@@ -109,6 +102,32 @@ bool ShaderCache::Open(std::string_view base_path, D3D_FEATURE_LEVEL feature_lev
return result;
}
void ShaderCache::Close()
{
if (m_pipeline_index_file)
{
std::fclose(m_pipeline_index_file);
m_pipeline_index_file = nullptr;
}
if (m_pipeline_blob_file)
{
std::fclose(m_pipeline_blob_file);
m_pipeline_blob_file = nullptr;
}
if (m_shader_index_file)
{
std::fclose(m_shader_index_file);
m_shader_index_file = nullptr;
}
if (m_shader_blob_file)
{
std::fclose(m_shader_blob_file);
m_shader_blob_file = nullptr;
}
m_base_path = {};
}
void ShaderCache::InvalidatePipelineCache()
{
m_pipeline_index.clear();
+1 -1
View File
@@ -52,6 +52,7 @@ namespace D3D12
__fi bool UsingDebugShaders() const { return m_debug; }
bool Open(std::string_view base_path, D3D_FEATURE_LEVEL feature_level, u32 version, bool debug);
void Close();
__fi ComPtr<ID3DBlob> GetVertexShader(std::string_view shader_code,
const D3D_SHADER_MACRO* macros = nullptr, const char* entry_point = "main")
@@ -129,7 +130,6 @@ namespace D3D12
bool ReadExisting(const std::string& index_filename, const std::string& blob_filename, std::FILE*& index_file,
std::FILE*& blob_file, CacheIndex& index);
void InvalidatePipelineCache();
void Close();
ComPtr<ID3DBlob> CompileAndAddShaderBlob(const CacheIndexKey& key, std::string_view shader_code,
const D3D_SHADER_MACRO* macros, const char* entry_point);
+31 -153
View File
@@ -35,6 +35,8 @@ namespace GL
prog.m_program_id = 0;
m_vertex_shader_id = prog.m_vertex_shader_id;
prog.m_vertex_shader_id = 0;
m_geometry_shader_id = prog.m_geometry_shader_id;
prog.m_geometry_shader_id = 0;
m_fragment_shader_id = prog.m_fragment_shader_id;
prog.m_fragment_shader_id = 0;
m_uniform_locations = std::move(prog.m_uniform_locations);
@@ -103,40 +105,34 @@ namespace GL
bool Program::Compile(const std::string_view vertex_shader, const std::string_view geometry_shader,
const std::string_view fragment_shader)
{
GLuint vertex_shader_id = 0;
if (!vertex_shader.empty())
{
vertex_shader_id = CompileShader(GL_VERTEX_SHADER, vertex_shader);
if (vertex_shader_id == 0)
m_vertex_shader_id = CompileShader(GL_VERTEX_SHADER, vertex_shader);
if (m_vertex_shader_id == 0)
return false;
}
GLuint geometry_shader_id = 0;
if (!geometry_shader.empty())
{
geometry_shader_id = CompileShader(GL_GEOMETRY_SHADER, geometry_shader);
if (geometry_shader_id == 0)
m_geometry_shader_id = CompileShader(GL_GEOMETRY_SHADER, geometry_shader);
if (m_geometry_shader_id == 0)
return false;
}
GLuint fragment_shader_id = 0;
if (!fragment_shader.empty())
{
fragment_shader_id = CompileShader(GL_FRAGMENT_SHADER, fragment_shader);
if (fragment_shader_id == 0)
{
glDeleteShader(vertex_shader_id);
m_fragment_shader_id = CompileShader(GL_FRAGMENT_SHADER, fragment_shader);
if (m_fragment_shader_id == 0)
return false;
}
}
m_program_id = glCreateProgram();
if (vertex_shader_id != 0)
glAttachShader(m_program_id, vertex_shader_id);
if (geometry_shader_id != 0)
glAttachShader(m_program_id, geometry_shader_id);
if (fragment_shader_id != 0)
glAttachShader(m_program_id, fragment_shader_id);
if (m_vertex_shader_id != 0)
glAttachShader(m_program_id, m_vertex_shader_id);
if (m_geometry_shader_id != 0)
glAttachShader(m_program_id, m_geometry_shader_id);
if (m_fragment_shader_id != 0)
glAttachShader(m_program_id, m_fragment_shader_id);
return true;
}
@@ -244,6 +240,9 @@ namespace GL
if (m_vertex_shader_id != 0)
glDeleteShader(m_vertex_shader_id);
m_vertex_shader_id = 0;
if (m_geometry_shader_id != 0)
glDeleteShader(m_geometry_shader_id);
m_geometry_shader_id = 0;
if (m_fragment_shader_id != 0)
glDeleteShader(m_fragment_shader_id);
m_fragment_shader_id = 0;
@@ -485,151 +484,28 @@ namespace GL
glUniform4fv(location, 1, v);
}
void Program::Uniform1ui(const char* name, u32 x) const
void Program::UniformMatrix2fv(int index, const float* v)
{
const GLint location = glGetUniformLocation(m_program_id, name);
pxAssert(static_cast<size_t>(index) < m_uniform_locations.size());
const GLint location = m_uniform_locations[index];
if (location >= 0)
glUniform1ui(location, x);
glUniformMatrix2fv(location, 1, GL_FALSE, v);
}
void Program::Uniform2ui(const char* name, u32 x, u32 y) const
void Program::UniformMatrix3fv(int index, const float* v)
{
const GLint location = glGetUniformLocation(m_program_id, name);
pxAssert(static_cast<size_t>(index) < m_uniform_locations.size());
const GLint location = m_uniform_locations[index];
if (location >= 0)
glUniform2ui(location, x, y);
glUniformMatrix3fv(location, 1, GL_FALSE, v);
}
void Program::Uniform3ui(const char* name, u32 x, u32 y, u32 z) const
void Program::UniformMatrix4fv(int index, const float* v)
{
const GLint location = glGetUniformLocation(m_program_id, name);
pxAssert(static_cast<size_t>(index) < m_uniform_locations.size());
const GLint location = m_uniform_locations[index];
if (location >= 0)
glUniform3ui(location, x, y, z);
}
void Program::Uniform4ui(const char* name, u32 x, u32 y, u32 z, u32 w) const
{
const GLint location = glGetUniformLocation(m_program_id, name);
if (location >= 0)
glUniform4ui(location, x, y, z, w);
}
void Program::Uniform1i(const char* name, s32 x) const
{
const GLint location = glGetUniformLocation(m_program_id, name);
if (location >= 0)
glUniform1i(location, x);
}
void Program::Uniform2i(const char* name, s32 x, s32 y) const
{
const GLint location = glGetUniformLocation(m_program_id, name);
if (location >= 0)
glUniform2i(location, x, y);
}
void Program::Uniform3i(const char* name, s32 x, s32 y, s32 z) const
{
const GLint location = glGetUniformLocation(m_program_id, name);
if (location >= 0)
glUniform3i(location, x, y, z);
}
void Program::Uniform4i(const char* name, s32 x, s32 y, s32 z, s32 w) const
{
const GLint location = glGetUniformLocation(m_program_id, name);
if (location >= 0)
glUniform4i(location, x, y, z, w);
}
void Program::Uniform1f(const char* name, float x) const
{
const GLint location = glGetUniformLocation(m_program_id, name);
if (location >= 0)
glUniform1f(location, x);
}
void Program::Uniform2f(const char* name, float x, float y) const
{
const GLint location = glGetUniformLocation(m_program_id, name);
if (location >= 0)
glUniform2f(location, x, y);
}
void Program::Uniform3f(const char* name, float x, float y, float z) const
{
const GLint location = glGetUniformLocation(m_program_id, name);
if (location >= 0)
glUniform3f(location, x, y, z);
}
void Program::Uniform4f(const char* name, float x, float y, float z, float w) const
{
const GLint location = glGetUniformLocation(m_program_id, name);
if (location >= 0)
glUniform4f(location, x, y, z, w);
}
void Program::Uniform2uiv(const char* name, const u32* v) const
{
const GLint location = glGetUniformLocation(m_program_id, name);
if (location >= 0)
glUniform2uiv(location, 1, v);
}
void Program::Uniform3uiv(const char* name, const u32* v) const
{
const GLint location = glGetUniformLocation(m_program_id, name);
if (location >= 0)
glUniform3uiv(location, 1, v);
}
void Program::Uniform4uiv(const char* name, const u32* v) const
{
const GLint location = glGetUniformLocation(m_program_id, name);
if (location >= 0)
glUniform4uiv(location, 1, v);
}
void Program::Uniform2iv(const char* name, const s32* v) const
{
const GLint location = glGetUniformLocation(m_program_id, name);
if (location >= 0)
glUniform2iv(location, 1, v);
}
void Program::Uniform3iv(const char* name, const s32* v) const
{
const GLint location = glGetUniformLocation(m_program_id, name);
if (location >= 0)
glUniform3iv(location, 1, v);
}
void Program::Uniform4iv(const char* name, const s32* v) const
{
const GLint location = glGetUniformLocation(m_program_id, name);
if (location >= 0)
glUniform4iv(location, 1, v);
}
void Program::Uniform2fv(const char* name, const float* v) const
{
const GLint location = glGetUniformLocation(m_program_id, name);
if (location >= 0)
glUniform2fv(location, 1, v);
}
void Program::Uniform3fv(const char* name, const float* v) const
{
const GLint location = glGetUniformLocation(m_program_id, name);
if (location >= 0)
glUniform3fv(location, 1, v);
}
void Program::Uniform4fv(const char* name, const float* v) const
{
const GLint location = glGetUniformLocation(m_program_id, name);
if (location >= 0)
glUniform4fv(location, 1, v);
glUniformMatrix4fv(location, 1, GL_FALSE, v);
}
void Program::BindUniformBlock(const char* name, u32 index)
@@ -665,6 +541,8 @@ namespace GL
prog.m_program_id = 0;
m_vertex_shader_id = prog.m_vertex_shader_id;
prog.m_vertex_shader_id = 0;
m_geometry_shader_id = prog.m_geometry_shader_id;
prog.m_geometry_shader_id = 0;
m_fragment_shader_id = prog.m_fragment_shader_id;
prog.m_fragment_shader_id = 0;
m_uniform_locations = std::move(prog.m_uniform_locations);
+4 -21
View File
@@ -79,27 +79,9 @@ namespace GL
void Uniform3fv(int index, const float* v) const;
void Uniform4fv(int index, const float* v) const;
void Uniform1ui(const char* name, u32 x) const;
void Uniform2ui(const char* name, u32 x, u32 y) const;
void Uniform3ui(const char* name, u32 x, u32 y, u32 z) const;
void Uniform4ui(const char* name, u32 x, u32 y, u32 z, u32 w) const;
void Uniform1i(const char* name, s32 x) const;
void Uniform2i(const char* name, s32 x, s32 y) const;
void Uniform3i(const char* name, s32 x, s32 y, s32 z) const;
void Uniform4i(const char* name, s32 x, s32 y, s32 z, s32 w) const;
void Uniform1f(const char* name, float x) const;
void Uniform2f(const char* name, float x, float y) const;
void Uniform3f(const char* name, float x, float y, float z) const;
void Uniform4f(const char* name, float x, float y, float z, float w) const;
void Uniform2uiv(const char* name, const u32* v) const;
void Uniform3uiv(const char* name, const u32* v) const;
void Uniform4uiv(const char* name, const u32* v) const;
void Uniform2iv(const char* name, const s32* v) const;
void Uniform3iv(const char* name, const s32* v) const;
void Uniform4iv(const char* name, const s32* v) const;
void Uniform2fv(const char* name, const float* v) const;
void Uniform3fv(const char* name, const float* v) const;
void Uniform4fv(const char* name, const float* v) const;
void UniformMatrix2fv(int index, const float* v);
void UniformMatrix3fv(int index, const float* v);
void UniformMatrix4fv(int index, const float* v);
void BindUniformBlock(const char* name, u32 index);
@@ -117,6 +99,7 @@ namespace GL
GLuint m_program_id = 0;
GLuint m_vertex_shader_id = 0;
GLuint m_geometry_shader_id = 0;
GLuint m_fragment_shader_id = 0;
std::vector<GLint> m_uniform_locations;
+8
View File
@@ -218,9 +218,17 @@ namespace GL
{
m_index.clear();
if (m_index_file)
{
std::fclose(m_index_file);
m_index_file = nullptr;
}
if (m_blob_file)
{
std::fclose(m_blob_file);
m_blob_file = nullptr;
}
m_base_path = {};
}
bool ShaderCache::Recreate()
+1 -1
View File
@@ -36,6 +36,7 @@ namespace GL
~ShaderCache();
bool Open(bool is_gles, std::string_view base_path, u32 version);
void Close();
std::optional<Program> GetProgram(const std::string_view vertex_shader, const std::string_view geometry_shader,
const std::string_view fragment_shader, const PreLinkCallback& callback = {});
@@ -94,7 +95,6 @@ namespace GL
bool CreateNew(const std::string& index_filename, const std::string& blob_filename);
bool ReadExisting(const std::string& index_filename, const std::string& blob_filename);
void Close();
bool Recreate();
bool WriteToBlobFile(const CacheIndexKey& key, const std::vector<u8>& prog_data, u32 prog_format);
-111
View File
@@ -97,114 +97,3 @@ public:
virtual SafeArray<T>* Clone() const;
};
//////////////////////////////////////////////////////////////////////////////////////////
// SafeList - Simple growable container without all the mess or hassle of std containers.
//
// This container is intended for reasonably simple class types only. Things which this
// container does not handle with desired robustness:
//
// * Classes with non-trivial constructors (such that construction creates much overhead)
// * Classes with copy constructors (copying is done using performance memcpy)
// * Classes with destructors (they're not called, sorry!)
//
template <typename T>
class SafeList
{
DeclareNoncopyableObject(SafeList);
public:
static const int DefaultChunkSize = 0x80 * sizeof(T);
public:
std::string Name; // user-assigned block name
int ChunkSize; // assigned DefaultChunkSize on init, reconfigurable at any time.
protected:
T* m_ptr;
int m_allocsize; // size of the allocation of memory
uint m_length; // length of the array (active items, not buffer allocation)
protected:
virtual T* _virtual_realloc(int newsize);
void _MakeRoomFor_threshold(int newsize);
T* _getPtr(uint i) const;
public:
virtual ~SafeList();
explicit SafeList(const char* name = "Unnamed");
explicit SafeList(int initialSize, const char* name = "Unnamed");
virtual SafeList<T>* Clone() const;
void Remove(int index);
void MakeRoomFor(int blockSize);
T& New();
int Add(const T& src);
T& AddNew(const T& src);
// Returns the size of the list, as according to the array type. This includes
// mapped items only. The actual size of the allocation may differ.
int GetLength() const { return m_length; }
// Returns the size of the list, in bytes. This includes mapped items only.
// The actual size of the allocation may differ.
int GetSizeInBytes() const { return m_length * sizeof(T); }
void MatchLengthToAllocatedSize()
{
m_length = m_allocsize;
}
void GrowBy(int items)
{
MakeRoomFor(m_length + ChunkSize + items + 1);
}
// Sets the item length to zero. Does not free memory allocations.
void Clear()
{
m_length = 0;
}
// Gets an element of this memory allocation much as if it were an array.
// DevBuilds : Generates assertion if the index is invalid.
T& operator[](int idx) { return *_getPtr((uint)idx); }
const T& operator[](int idx) const { return *_getPtr((uint)idx); }
T* GetPtr() { return m_ptr; }
const T* GetPtr() const { return m_ptr; }
T& GetLast() { return m_ptr[m_length - 1]; }
const T& GetLast() const { return m_ptr[m_length - 1]; }
};
// --------------------------------------------------------------------------------------
// SafeAlignedArray<T>
// --------------------------------------------------------------------------------------
// Handy little class for allocating a resizable memory block, complete with
// exception-based error handling and automatic cleanup.
// This one supports aligned data allocations too!
template <typename T, uint Alignment>
class SafeAlignedArray : public SafeArray<T>
{
typedef SafeArray<T> _parent;
protected:
T* _virtual_realloc(int newsize);
public:
using _parent::operator[];
virtual ~SafeAlignedArray();
explicit SafeAlignedArray(std::string name = "Unnamed")
: SafeArray<T>::SafeArray(name)
{
}
explicit SafeAlignedArray(int initialSize, std::string name = "Unnamed");
virtual SafeAlignedArray<T, Alignment>* Clone() const;
};
-166
View File
@@ -15,13 +15,9 @@
#pragma once
#include "common/AlignedMalloc.h"
#include "common/Assertions.h"
#include "common/Exceptions.h"
#include "common/SafeArray.h"
#include "fmt/core.h"
// Internal constructor for use by derived classes. This allows a derived class to
// use its own memory allocation (with an aligned memory, for example).
// Throws:
@@ -123,165 +119,3 @@ SafeArray<T>* SafeArray<T>::Clone() const
memcpy(retval->GetPtr(), m_ptr, sizeof(T) * m_size);
return retval;
}
// --------------------------------------------------------------------------------------
// SafeAlignedArray<T> (implementations)
// --------------------------------------------------------------------------------------
template <typename T, uint Alignment>
T* SafeAlignedArray<T, Alignment>::_virtual_realloc(int newsize)
{
return (T*)((this->m_ptr == NULL) ?
_aligned_malloc(newsize * sizeof(T), Alignment) :
pcsx2_aligned_realloc(this->m_ptr, newsize * sizeof(T), Alignment, this->m_size * sizeof(T)));
}
// Appends "(align: xx)" to the name of the allocation in devel builds.
// Maybe useful,maybe not... no harm in attaching it. :D
template <typename T, uint Alignment>
SafeAlignedArray<T, Alignment>::~SafeAlignedArray()
{
safe_aligned_free(this->m_ptr);
// mptr is set to null, so the parent class's destructor won't re-free it.
}
template <typename T, uint Alignment>
SafeAlignedArray<T, Alignment>::SafeAlignedArray(int initialSize, std::string name)
: SafeArray<T>::SafeArray(
std::move(name),
(T*)_aligned_malloc(initialSize * sizeof(T), Alignment),
initialSize)
{
}
template <typename T, uint Alignment>
SafeAlignedArray<T, Alignment>* SafeAlignedArray<T, Alignment>::Clone() const
{
SafeAlignedArray<T, Alignment>* retval = new SafeAlignedArray<T, Alignment>(this->m_size);
memcpy(retval->GetPtr(), this->m_ptr, sizeof(T) * this->m_size);
return retval;
}
// --------------------------------------------------------------------------------------
// SafeList<T> (implementations)
// --------------------------------------------------------------------------------------
template <typename T>
T* SafeList<T>::_virtual_realloc(int newsize)
{
return (T*)realloc(m_ptr, newsize * sizeof(T));
}
template <typename T>
SafeList<T>::~SafeList()
{
safe_free(m_ptr);
}
template <typename T>
SafeList<T>::SafeList(const char* name)
: Name(name)
{
ChunkSize = DefaultChunkSize;
m_ptr = NULL;
m_allocsize = 0;
m_length = 0;
}
template <typename T>
SafeList<T>::SafeList(int initialSize, const char* name)
: Name(name)
{
ChunkSize = DefaultChunkSize;
m_allocsize = initialSize;
m_length = 0;
m_ptr = (T*)malloc(initialSize * sizeof(T));
if (m_ptr == NULL)
pxFailRel("SafeList exact alloc failed");
for (int i = 0; i < m_allocsize; ++i)
{
new (&m_ptr[i]) T();
}
}
template <typename T>
T* SafeList<T>::_getPtr(uint i) const
{
pxAssumeDev(i < m_length, "Index in bounds");
return &m_ptr[i];
}
// Ensures that the allocation is large enough to fit data of the
// amount requested. The memory allocation is not resized smaller.
template <typename T>
void SafeList<T>::MakeRoomFor(int blockSize)
{
if (blockSize > m_allocsize)
{
const int newalloc = blockSize + ChunkSize;
m_ptr = _virtual_realloc(newalloc);
if (m_ptr == NULL)
pxFailRel("SafeList MakeRoomFor failed");
for (; m_allocsize < newalloc; ++m_allocsize)
{
new (&m_ptr[m_allocsize]) T();
}
}
}
// Appends an item to the end of the list and returns a handle to it.
template <typename T>
T& SafeList<T>::New()
{
_MakeRoomFor_threshold(m_length + 1);
return m_ptr[m_length++];
}
template <typename T>
int SafeList<T>::Add(const T& src)
{
_MakeRoomFor_threshold(m_length + 1);
m_ptr[m_length] = src;
return m_length++;
}
// Same as Add, but returns the handle of the new object instead of it's array index.
template <typename T>
T& SafeList<T>::AddNew(const T& src)
{
_MakeRoomFor_threshold(m_length + 1);
m_ptr[m_length] = src;
return m_ptr[m_length];
}
// Performs a standard array-copy removal of the given item. All items past the
// given item are copied over.
// DevBuilds : Generates assertion if the index is invalid.
template <typename T>
void SafeList<T>::Remove(int index)
{
pxAssert(index < m_length);
int copylen = m_length - index;
if (copylen > 0)
memcpy(&m_ptr[index], &m_ptr[index + 1], copylen);
}
template <typename T>
SafeList<T>* SafeList<T>::Clone() const
{
SafeList<T>* retval = new SafeList<T>(m_length);
memcpy(retval->m_ptr, m_ptr, sizeof(T) * m_length);
return retval;
}
template <typename T>
void SafeList<T>::_MakeRoomFor_threshold(int newsize)
{
MakeRoomFor(newsize + ChunkSize);
}
+11 -52
View File
@@ -45,7 +45,7 @@
#include "pcsx2/GS.h"
#include "pcsx2/GS/GS.h"
#include "pcsx2/GSDumpReplayer.h"
#include "pcsx2/HostDisplay.h"
#include "pcsx2/Host.h"
#include "pcsx2/HostSettings.h"
#include "pcsx2/INISettingsInterface.h"
#include "pcsx2/PAD/Host/PAD.h"
@@ -254,36 +254,22 @@ void Host::SetRelativeMouseMode(bool enabled)
{
}
bool Host::AcquireHostDisplay(RenderAPI api, bool clear_state_on_fail)
std::optional<WindowInfo> Host::AcquireRenderWindow()
{
const std::optional<WindowInfo> wi(GSRunner::GetPlatformWindowInfo());
if (!wi.has_value())
return false;
g_host_display = HostDisplay::CreateForAPI(api);
if (!g_host_display)
return false;
if (!g_host_display->CreateDevice(wi.value(), Host::GetEffectiveVSyncMode()) ||
!g_host_display->MakeCurrent() || !g_host_display->SetupDevice() || !ImGuiManager::Initialize())
{
ReleaseHostDisplay(clear_state_on_fail);
return false;
}
Console.WriteLn(Color_StrongGreen, "%s Graphics Driver Info:", HostDisplay::RenderAPIToString(g_host_display->GetRenderAPI()));
Console.Indent().WriteLn(g_host_display->GetDriverInfo());
return g_host_display.get();
return GSRunner::GetPlatformWindowInfo();
}
void Host::ReleaseHostDisplay(bool clear_state)
std::optional<WindowInfo> Host::UpdateRenderWindow(bool recreate_window)
{
ImGuiManager::Shutdown(clear_state);
g_host_display.reset();
// We shouldn't ever recreate with the runner, so this is okay..
return GSRunner::GetPlatformWindowInfo();
}
HostDisplay::PresentResult Host::BeginPresentFrame(bool frame_skip)
void Host::ReleaseRenderWindow()
{
}
void Host::BeginPresentFrame()
{
if (s_loop_number == 0 && !s_output_prefix.empty())
{
@@ -294,33 +280,6 @@ HostDisplay::PresentResult Host::BeginPresentFrame(bool frame_skip)
std::string dump_path(fmt::format("{}_frame{}.png", s_output_prefix, s_dump_frame_number));
GSQueueSnapshot(dump_path);
}
const HostDisplay::PresentResult result = g_host_display->BeginPresent(frame_skip);
if (result != HostDisplay::PresentResult::OK)
{
// don't render imgui
ImGuiManager::SkipFrame();
}
return result;
}
void Host::EndPresentFrame()
{
if (GSDumpReplayer::IsReplayingDump())
GSDumpReplayer::RenderUI();
ImGuiManager::RenderOSD();
g_host_display->EndPresent();
ImGuiManager::NewFrame();
}
void Host::ResizeHostDisplay(u32 new_window_width, u32 new_window_height, float new_window_scale)
{
}
void Host::UpdateHostDisplay()
{
}
void Host::RequestResizeHostDisplay(s32 width, s32 height)
+9 -10
View File
@@ -7,20 +7,19 @@ import multiprocessing
from pathlib import Path
from functools import partial
def is_gs_path(path):
ppath = Path(path)
for extension in [[".gs"], [".gs", ".xz"], [".gs", ".zst"]]:
if ppath.suffixes == extension:
return True
def get_gs_name(path):
lpath = path.lower()
return False
for extension in [".gs", ".gs.xz", ".gs.zst"]:
if lpath.endswith(extension):
return os.path.basename(path)[:-len(extension)]
return None
def run_regression_test(runner, dumpdir, renderer, upscale, renderhacks, parallel, gspath):
args = [runner]
gsname = Path(gspath).name
while gsname.rfind('.') >= 0:
gsname = gsname[:gsname.rfind('.')]
gsname = get_gs_name(gspath)
real_dumpdir = os.path.join(dumpdir, gsname).strip()
if not os.path.exists(real_dumpdir):
@@ -62,7 +61,7 @@ def run_regression_test(runner, dumpdir, renderer, upscale, renderhacks, paralle
def run_regression_tests(runner, gsdir, dumpdir, renderer, upscale, renderhacks, parallel=1):
paths = glob.glob(gsdir + "/*.*", recursive=True)
gamepaths = list(filter(is_gs_path, paths))
gamepaths = list(filter(lambda x: get_gs_name(x) is not None, paths))
if not os.path.isdir(dumpdir):
os.mkdir(dumpdir)
+37 -78
View File
@@ -32,9 +32,9 @@
#include "pcsx2/CDVD/CDVDdiscReader.h"
#include "pcsx2/Frontend/GameList.h"
#include "pcsx2/Frontend/LogSink.h"
#include "pcsx2/GS.h"
#include "pcsx2/GS/GS.h"
#include "pcsx2/GSDumpReplayer.h"
#include "pcsx2/HostDisplay.h"
#include "pcsx2/HostSettings.h"
#include "pcsx2/PerformanceMetrics.h"
#include "pcsx2/Recording/InputRecording.h"
@@ -415,8 +415,8 @@ void MainWindow::connectVMThreadSignals(EmuThread* thread)
connect(m_ui.actionStartFullscreenUI, &QAction::triggered, thread, &EmuThread::startFullscreenUI);
connect(m_ui.actionStartFullscreenUI2, &QAction::triggered, thread, &EmuThread::startFullscreenUI);
connect(thread, &EmuThread::messageConfirmed, this, &MainWindow::confirmMessage, Qt::BlockingQueuedConnection);
connect(thread, &EmuThread::onCreateDisplayRequested, this, &MainWindow::createDisplay, Qt::BlockingQueuedConnection);
connect(thread, &EmuThread::onUpdateDisplayRequested, this, &MainWindow::updateDisplay, Qt::BlockingQueuedConnection);
connect(thread, &EmuThread::onCreateDisplayRequested, this, &MainWindow::createDisplayWindow, Qt::BlockingQueuedConnection);
connect(thread, &EmuThread::onUpdateDisplayRequested, this, &MainWindow::updateDisplayWindow, Qt::BlockingQueuedConnection);
connect(thread, &EmuThread::onDestroyDisplayRequested, this, &MainWindow::destroyDisplay, Qt::BlockingQueuedConnection);
connect(thread, &EmuThread::onResizeDisplayRequested, this, &MainWindow::displayResizeRequested);
connect(thread, &EmuThread::onRelativeMouseModeRequested, this, &MainWindow::relativeMouseModeRequested);
@@ -1167,7 +1167,7 @@ void MainWindow::updateEmulationActions(bool starting, bool running, bool stoppi
void MainWindow::updateDisplayRelatedActions(bool has_surface, bool render_to_main, bool fullscreen)
{
// rendering to main, or switched to gamelist/grid
m_ui.actionViewSystemDisplay->setEnabled((has_surface && render_to_main) || (!has_surface && g_host_display));
m_ui.actionViewSystemDisplay->setEnabled((has_surface && render_to_main) || (!has_surface && GetMTGS().IsOpen()));
m_ui.menuWindowSize->setEnabled(has_surface && !fullscreen);
m_ui.actionFullscreen->setEnabled(has_surface);
@@ -1279,10 +1279,10 @@ bool MainWindow::isShowingGameList() const
bool MainWindow::isRenderingFullscreen() const
{
if (!g_host_display || !m_display_widget)
if (!GetMTGS().IsOpen() || !m_display_widget)
return false;
return getDisplayContainer()->isFullScreen() || g_host_display->IsFullscreen();
return getDisplayContainer()->isFullScreen();
}
bool MainWindow::isRenderingToMain() const
@@ -1430,13 +1430,16 @@ bool MainWindow::requestShutdown(bool allow_confirm, bool allow_save_to_state, b
void MainWindow::requestExit(bool allow_confirm)
{
// requestShutdown() clears this flag.
const bool vm_was_valid = QtHost::IsVMValid();
// this is block, because otherwise closeEvent() will also prompt
if (!requestShutdown(allow_confirm, true, EmuConfig.SaveStateOnShutdown))
return;
// VM stopped signal won't have fired yet, so queue an exit if we still have one.
// Otherwise, immediately exit, because there's no VM to exit us later.
if (QtHost::IsVMValid())
if (vm_was_valid)
m_is_closing = true;
else
QGuiApplication::quit();
@@ -2197,17 +2200,12 @@ bool MainWindow::nativeEvent(const QByteArray& eventType, void* message, qintptr
#endif
DisplayWidget* MainWindow::createDisplay(bool fullscreen, bool render_to_main)
std::optional<WindowInfo> MainWindow::createDisplayWindow(bool fullscreen, bool render_to_main)
{
DevCon.WriteLn("createDisplay(%u, %u)", static_cast<u32>(fullscreen), static_cast<u32>(render_to_main));
DevCon.WriteLn(
"createDisplayWindow() fullscreen=%s render_to_main=%s", fullscreen ? "true" : "false", render_to_main ? "true" : "false");
if (!g_host_display)
return nullptr;
const std::string fullscreen_mode(Host::GetBaseStringSettingValue("EmuCore/GS", "FullscreenMode", ""));
const bool is_exclusive_fullscreen = (fullscreen && !fullscreen_mode.empty() && g_host_display->SupportsFullscreen());
createDisplayWidget(fullscreen, render_to_main, is_exclusive_fullscreen);
createDisplayWidget(fullscreen, render_to_main);
// we need the surface visible.. this might be able to be replaced with something else
QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
@@ -2217,26 +2215,12 @@ DisplayWidget* MainWindow::createDisplay(bool fullscreen, bool render_to_main)
{
QMessageBox::critical(this, tr("Error"), tr("Failed to get window info from widget"));
destroyDisplayWidget(true);
return nullptr;
return std::nullopt;
}
g_emu_thread->connectDisplaySignals(m_display_widget);
if (!g_host_display->CreateDevice(wi.value(), Host::GetEffectiveVSyncMode()))
{
QMessageBox::critical(this, tr("Error"),
tr("Failed to create host display device. This may be due to your GPU not supporting the chosen renderer (%1), or because your "
"graphics drivers need to be updated.")
.arg(QString::fromUtf8(Pcsx2Config::GSOptions::GetRendererName(EmuConfig.GS.Renderer))));
destroyDisplayWidget(true);
return nullptr;
}
m_display_created = true;
if (is_exclusive_fullscreen)
setDisplayFullscreen(fullscreen_mode);
updateWindowTitle();
updateWindowState();
@@ -2246,34 +2230,29 @@ DisplayWidget* MainWindow::createDisplay(bool fullscreen, bool render_to_main)
updateDisplayWidgetCursor();
m_display_widget->setFocus();
g_host_display->DoneCurrent();
return m_display_widget;
return wi;
}
DisplayWidget* MainWindow::updateDisplay(bool fullscreen, bool render_to_main, bool surfaceless)
std::optional<WindowInfo> MainWindow::updateDisplayWindow(bool recreate_window, bool fullscreen, bool render_to_main, bool surfaceless)
{
DevCon.WriteLn("updateDisplay() fullscreen=%s render_to_main=%s surfaceless=%s", fullscreen ? "true" : "false",
render_to_main ? "true" : "false", surfaceless ? "true" : "false");
DevCon.WriteLn("updateDisplayWindow() recreate=%s fullscreen=%s render_to_main=%s surfaceless=%s", recreate_window ? "true" : "false",
fullscreen ? "true" : "false", render_to_main ? "true" : "false", surfaceless ? "true" : "false");
QWidget* container = m_display_container ? static_cast<QWidget*>(m_display_container) : static_cast<QWidget*>(m_display_widget);
const bool is_fullscreen = isRenderingFullscreen();
const bool is_rendering_to_main = isRenderingToMain();
const std::string fullscreen_mode(Host::GetBaseStringSettingValue("EmuCore/GS", "FullscreenMode", ""));
const bool is_exclusive_fullscreen = (fullscreen && !fullscreen_mode.empty() && g_host_display->SupportsFullscreen());
const bool changing_surfaceless = (!m_display_widget != surfaceless);
if (fullscreen == is_fullscreen && is_rendering_to_main == render_to_main && !changing_surfaceless)
return m_display_widget;
if (!recreate_window && fullscreen == is_fullscreen && is_rendering_to_main == render_to_main && !changing_surfaceless)
return m_display_widget->getWindowInfo();
// Skip recreating the surface if we're just transitioning between fullscreen and windowed with render-to-main off.
// .. except on Wayland, where everything tends to break if you don't recreate.
const bool has_container = (m_display_container != nullptr);
const bool needs_container = DisplayContainer::isNeeded(fullscreen, render_to_main);
if (!is_rendering_to_main && !render_to_main && !is_exclusive_fullscreen && has_container == needs_container && !needs_container &&
if (!recreate_window && !is_rendering_to_main && !render_to_main && has_container == needs_container && !needs_container &&
!changing_surfaceless)
{
DevCon.WriteLn("Toggling to %s without recreating surface", (fullscreen ? "fullscreen" : "windowed"));
if (g_host_display->IsFullscreen())
g_host_display->SetFullscreen(false, 0, 0, 0.0f);
// since we don't destroy the display widget, we need to save it here
if (!is_fullscreen && !is_rendering_to_main)
@@ -2294,45 +2273,37 @@ DisplayWidget* MainWindow::updateDisplay(bool fullscreen, bool render_to_main, b
updateWindowState();
QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
return m_display_widget;
return m_display_widget->getWindowInfo();
}
g_host_display->DestroySurface();
destroyDisplayWidget(surfaceless);
// if we're going to surfaceless, we're done here
if (surfaceless)
return nullptr;
return WindowInfo();
createDisplayWidget(fullscreen, render_to_main, is_exclusive_fullscreen);
createDisplayWidget(fullscreen, render_to_main);
std::optional<WindowInfo> wi = m_display_widget->getWindowInfo();
if (!wi.has_value())
{
QMessageBox::critical(this, tr("Error"), tr("Failed to get new window info from widget"));
destroyDisplayWidget(true);
return nullptr;
return std::nullopt;
}
g_emu_thread->connectDisplaySignals(m_display_widget);
if (!g_host_display->ChangeWindow(wi.value()))
pxFailRel("Failed to recreate surface on new widget.");
if (is_exclusive_fullscreen)
setDisplayFullscreen(fullscreen_mode);
updateWindowTitle();
updateWindowState();
updateDisplayWidgetCursor();
m_display_widget->setFocus();
return m_display_widget;
return wi;
}
void MainWindow::createDisplayWidget(bool fullscreen, bool render_to_main, bool is_exclusive_fullscreen)
void MainWindow::createDisplayWidget(bool fullscreen, bool render_to_main)
{
// If we're rendering to main and were hidden (e.g. coming back from fullscreen),
// make sure we're visible before trying to add ourselves. Otherwise Wayland breaks.
@@ -2374,10 +2345,7 @@ void MainWindow::createDisplayWidget(bool fullscreen, bool render_to_main, bool
restoreDisplayWindowGeometryFromConfig();
}
if (!is_exclusive_fullscreen)
container->showFullScreen();
else
container->showNormal();
container->showFullScreen();
}
else if (!render_to_main)
{
@@ -2558,23 +2526,6 @@ void MainWindow::restoreDisplayWindowGeometryFromConfig()
}
}
void MainWindow::setDisplayFullscreen(const std::string& fullscreen_mode)
{
u32 width, height;
float refresh_rate;
if (HostDisplay::ParseFullscreenMode(fullscreen_mode, &width, &height, &refresh_rate))
{
if (g_host_display->SetFullscreen(true, width, height, refresh_rate))
{
Host::AddOSDMessage("Acquired exclusive fullscreen.", Host::OSD_INFO_DURATION);
}
else
{
Host::AddOSDMessage("Failed to acquire exclusive fullscreen.", Host::OSD_WARNING_DURATION);
}
}
}
SettingsDialog* MainWindow::getSettingsDialog()
{
if (!m_settings_dialog)
@@ -3050,8 +3001,16 @@ MainWindow::VMLock::~VMLock()
{
if (m_was_fullscreen)
g_emu_thread->setSurfaceless(false);
if (!m_was_paused)
g_emu_thread->setVMPaused(false);
if (m_was_fullscreen)
{
// Wait until we get the display widget back, so we're not the last window left and closing.
while (QtHost::IsVMValid() && !g_main_window->m_display_widget)
QApplication::processEvents(QEventLoop::ExcludeUserInputEvents, 1);
}
}
void MainWindow::VMLock::cancelResume()
+3 -4
View File
@@ -128,8 +128,8 @@ public Q_SLOTS:
private Q_SLOTS:
void onUpdateCheckComplete();
DisplayWidget* createDisplay(bool fullscreen, bool render_to_main);
DisplayWidget* updateDisplay(bool fullscreen, bool render_to_main, bool surfaceless);
std::optional<WindowInfo> createDisplayWindow(bool fullscreen, bool render_to_main);
std::optional<WindowInfo> updateDisplayWindow(bool recreate_window, bool fullscreen, bool render_to_main, bool surfaceless);
void displayResizeRequested(qint32 width, qint32 height);
void relativeMouseModeRequested(bool enabled);
void destroyDisplay();
@@ -233,10 +233,9 @@ private:
QWidget* getDisplayContainer() const;
void saveDisplayWindowGeometryToConfig();
void restoreDisplayWindowGeometryFromConfig();
void createDisplayWidget(bool fullscreen, bool render_to_main, bool is_exclusive_fullscreen);
void createDisplayWidget(bool fullscreen, bool render_to_main);
void destroyDisplayWidget(bool show_game_list);
void updateDisplayWidgetCursor();
void setDisplayFullscreen(const std::string& fullscreen_mode);
SettingsDialog* getSettingsDialog();
void doSettings(const char* category = nullptr);
+22 -101
View File
@@ -41,7 +41,6 @@
#include "pcsx2/GS.h"
#include "pcsx2/GS/GS.h"
#include "pcsx2/GSDumpReplayer.h"
#include "pcsx2/HostDisplay.h"
#include "pcsx2/HostSettings.h"
#include "pcsx2/INISettingsInterface.h"
#include "pcsx2/PAD/Host/PAD.h"
@@ -191,9 +190,11 @@ void EmuThread::startFullscreenUI(bool fullscreen)
return;
}
if (VMManager::HasValidVM())
if (VMManager::HasValidVM() || GetMTGS().IsOpen())
return;
// this should just set the flag so it gets automatically started
ImGuiManager::InitializeFullscreenUI();
m_run_fullscreen_ui = true;
if (fullscreen)
m_is_fullscreen = true;
@@ -216,13 +217,13 @@ void EmuThread::stopFullscreenUI()
QMetaObject::invokeMethod(this, &EmuThread::stopFullscreenUI, Qt::QueuedConnection);
// wait until the host display is gone
while (g_host_display)
while (GetMTGS().IsOpen())
QApplication::processEvents(QEventLoop::ExcludeUserInputEvents, 1);
return;
}
if (!g_host_display)
if (!GetMTGS().IsOpen())
return;
pxAssertRel(!VMManager::HasValidVM(), "VM is not valid at FSUI shutdown time");
@@ -511,7 +512,7 @@ void EmuThread::setFullscreen(bool fullscreen)
GetMTGS().WaitGS();
// If we're using exclusive fullscreen, the refresh rate may have changed.
UpdateVSyncRate();
UpdateVSyncRate(true);
}
void EmuThread::setSurfaceless(bool surfaceless)
@@ -590,7 +591,7 @@ void EmuThread::checkForSettingChanges(const Pcsx2Config& old_config)
{
QMetaObject::invokeMethod(g_main_window, &MainWindow::checkForSettingChanges, Qt::QueuedConnection);
if (g_host_display)
if (GetMTGS().IsOpen())
{
const bool render_to_main = shouldRenderToMain();
if (!m_is_fullscreen && m_is_rendering_to_main != render_to_main)
@@ -787,7 +788,7 @@ void EmuThread::connectDisplaySignals(DisplayWidget* widget)
void EmuThread::onDisplayWindowResized(int width, int height, float scale)
{
if (!g_host_display)
if (!GetMTGS().IsOpen())
return;
GetMTGS().ResizeDisplayWindow(width, height, scale);
@@ -892,115 +893,41 @@ void EmuThread::endCapture()
GetMTGS().RunOnGSThread(&GSEndCapture);
}
void EmuThread::updateDisplay()
std::optional<WindowInfo> EmuThread::acquireRenderWindow()
{
pxAssertRel(!isOnEmuThread(), "Not on emu thread");
// finished with the display for now
g_host_display->DoneCurrent();
// but we should get it back after this call
onUpdateDisplayRequested(m_is_fullscreen, !m_is_fullscreen && m_is_rendering_to_main, m_is_surfaceless);
if (!g_host_display->MakeCurrent())
{
pxFailRel("Failed to recreate context after updating");
return;
}
}
bool EmuThread::acquireHostDisplay(RenderAPI api, bool clear_state_on_fail)
{
pxAssertRel(!g_host_display, "Host display does not exist on create");
m_is_rendering_to_main = shouldRenderToMain();
m_is_surfaceless = false;
g_host_display = HostDisplay::CreateForAPI(api);
if (!g_host_display)
return false;
DisplayWidget* widget = emit onCreateDisplayRequested(m_is_fullscreen, m_is_rendering_to_main);
if (!widget)
{
g_host_display.reset();
return false;
}
connectDisplaySignals(widget);
if (!g_host_display->MakeCurrent())
{
Console.Error("Failed to make render context current");
releaseHostDisplay(clear_state_on_fail);
return false;
}
if (!g_host_display->SetupDevice() || !ImGuiManager::Initialize())
{
Console.Error("Failed to initialize device/imgui");
releaseHostDisplay(clear_state_on_fail);
return false;
}
Console.WriteLn(Color_StrongGreen, "%s Graphics Driver Info:", HostDisplay::RenderAPIToString(g_host_display->GetRenderAPI()));
Console.Indent().WriteLn(g_host_display->GetDriverInfo());
if (m_run_fullscreen_ui && !ImGuiManager::InitializeFullscreenUI())
{
Console.Error("Failed to initialize fullscreen UI");
releaseHostDisplay(clear_state_on_fail);
m_run_fullscreen_ui = false;
return false;
}
return true;
return emit onCreateDisplayRequested(m_is_fullscreen, m_is_rendering_to_main);
}
void EmuThread::releaseHostDisplay(bool clear_state)
std::optional<WindowInfo> EmuThread::updateRenderWindow(bool recreate_window)
{
ImGuiManager::Shutdown(clear_state);
return emit onUpdateDisplayRequested(recreate_window, m_is_fullscreen, !m_is_fullscreen && m_is_rendering_to_main, m_is_surfaceless);
}
g_host_display.reset();
void EmuThread::releaseRenderWindow()
{
emit onDestroyDisplayRequested();
}
bool Host::AcquireHostDisplay(RenderAPI api, bool clear_state_on_fail)
std::optional<WindowInfo> Host::AcquireRenderWindow()
{
return g_emu_thread->acquireHostDisplay(api, clear_state_on_fail);
return g_emu_thread->acquireRenderWindow();
}
void Host::ReleaseHostDisplay(bool clear_state)
std::optional<WindowInfo> Host::UpdateRenderWindow(bool recreate_window)
{
g_emu_thread->releaseHostDisplay(clear_state);
return g_emu_thread->updateRenderWindow(recreate_window);
}
HostDisplay::PresentResult Host::BeginPresentFrame(bool frame_skip)
void Host::ReleaseRenderWindow()
{
const HostDisplay::PresentResult result = g_host_display->BeginPresent(frame_skip);
if (result != HostDisplay::PresentResult::OK)
{
// if we're skipping a frame, we need to reset imgui's state, since
// we won't be calling EndPresentFrame().
ImGuiManager::SkipFrame();
}
return result;
return g_emu_thread->releaseRenderWindow();
}
void Host::EndPresentFrame()
void Host::BeginPresentFrame()
{
if (GSDumpReplayer::IsReplayingDump())
GSDumpReplayer::RenderUI();
FullscreenUI::Render();
ImGuiManager::RenderOSD();
g_host_display->EndPresent();
ImGuiManager::NewFrame();
}
void Host::ResizeHostDisplay(u32 new_window_width, u32 new_window_height, float new_window_scale)
{
g_host_display->ResizeWindow(new_window_width, new_window_height, new_window_scale);
ImGuiManager::WindowResized();
}
void Host::RequestResizeHostDisplay(s32 width, s32 height)
@@ -1008,12 +935,6 @@ void Host::RequestResizeHostDisplay(s32 width, s32 height)
g_emu_thread->onResizeDisplayRequested(width, height);
}
void Host::UpdateHostDisplay()
{
g_emu_thread->updateDisplay();
ImGuiManager::WindowResized();
}
void Host::OnVMStarting()
{
CommonHost::OnVMStarting();
+5 -6
View File
@@ -21,7 +21,6 @@
#include <optional>
#include "pcsx2/Host.h"
#include "pcsx2/HostDisplay.h"
#include "pcsx2/HostSettings.h"
#include "pcsx2/Frontend/InputManager.h"
#include "pcsx2/VMManager.h"
@@ -70,10 +69,10 @@ public:
bool shouldRenderToMain() const;
/// Called back from the GS thread when the display state changes (e.g. fullscreen, render to main).
bool acquireHostDisplay(RenderAPI api, bool clear_state_on_fail);
std::optional<WindowInfo> acquireRenderWindow();
std::optional<WindowInfo> updateRenderWindow(bool recreate_window);
void connectDisplaySignals(DisplayWidget* widget);
void releaseHostDisplay(bool clear_state);
void updateDisplay();
void releaseRenderWindow();
void startBackgroundControllerPollTimer();
void stopBackgroundControllerPollTimer();
@@ -118,8 +117,8 @@ public Q_SLOTS:
Q_SIGNALS:
bool messageConfirmed(const QString& title, const QString& message);
DisplayWidget* onCreateDisplayRequested(bool fullscreen, bool render_to_main);
DisplayWidget* onUpdateDisplayRequested(bool fullscreen, bool render_to_main, bool surfaceless);
std::optional<WindowInfo> onCreateDisplayRequested(bool fullscreen, bool render_to_main);
std::optional<WindowInfo> onUpdateDisplayRequested(bool recreate_window, bool fullscreen, bool render_to_main, bool surfaceless);
void onResizeDisplayRequested(qint32 width, qint32 height);
void onDestroyDisplayRequested();
void onRelativeMouseModeRequested(bool enabled);
+6 -46
View File
@@ -26,18 +26,6 @@
#include "pcsx2/GS/GSCapture.h"
#include "pcsx2/GS/GSUtil.h"
#ifdef ENABLE_VULKAN
#include "Frontend/VulkanHostDisplay.h"
#endif
#ifdef _WIN32
#include "Frontend/D3D11HostDisplay.h"
#include "Frontend/D3D12HostDisplay.h"
#endif
#ifdef __APPLE__
#include "GS/Renderers/Metal/GSMetalCPPAccessible.h"
#endif
struct RendererInfo
{
const char* name;
@@ -965,37 +953,9 @@ void GraphicsSettingsWidget::updateRendererDependentOptions()
m_ui.disableFramebufferFetch->setDisabled(is_sw_dx);
// populate adapters
HostDisplay::AdapterAndModeList modes;
switch (type)
{
#ifdef _WIN32
case GSRendererType::DX11:
modes = D3D11HostDisplay::StaticGetAdapterAndModeList();
break;
case GSRendererType::DX12:
modes = D3D12HostDisplay::StaticGetAdapterAndModeList();
break;
#endif
#ifdef ENABLE_VULKAN
case GSRendererType::VK:
modes = VulkanHostDisplay::StaticGetAdapterAndModeList(nullptr);
break;
#endif
#ifdef __APPLE__
case GSRendererType::Metal:
modes = GetMetalAdapterAndModeList();
break;
#endif
case GSRendererType::OGL:
case GSRendererType::SW:
case GSRendererType::Null:
case GSRendererType::Auto:
default:
break;
}
std::vector<std::string> adapters;
std::vector<std::string> fullscreen_modes;
GSGetAdaptersAndFullscreenModes(type, &adapters, &fullscreen_modes);
// fill+select adapters
{
@@ -1003,7 +963,7 @@ void GraphicsSettingsWidget::updateRendererDependentOptions()
std::string current_adapter = Host::GetBaseStringSettingValue("EmuCore/GS", "Adapter", "");
m_ui.adapter->clear();
m_ui.adapter->setEnabled(!modes.adapter_names.empty());
m_ui.adapter->setEnabled(!adapters.empty());
m_ui.adapter->addItem(tr("(Default)"));
m_ui.adapter->setCurrentIndex(0);
@@ -1019,7 +979,7 @@ void GraphicsSettingsWidget::updateRendererDependentOptions()
}
}
for (const std::string& adapter : modes.adapter_names)
for (const std::string& adapter : adapters)
{
m_ui.adapter->addItem(QString::fromStdString(adapter));
if (current_adapter == adapter)
@@ -1047,7 +1007,7 @@ void GraphicsSettingsWidget::updateRendererDependentOptions()
}
}
for (const std::string& fs_mode : modes.fullscreen_modes)
for (const std::string& fs_mode : fullscreen_modes)
{
m_ui.fullscreenModes->addItem(QString::fromStdString(fs_mode));
if (current_mode == fs_mode)
-8
View File
@@ -907,14 +907,6 @@ void cdvdReset()
cdvdCtrlTrayClose();
}
struct Freeze_v10Compat
{
u8 Action;
u32 SeekToSector;
u32 ReadTime;
bool Spinning;
};
void SaveStateBase::cdvdFreeze()
{
FreezeTag("cdvd");
+1 -44
View File
@@ -98,7 +98,6 @@ set(pcsx2Sources
Gif_Unit.cpp
GS.cpp
Host.cpp
HostDisplay.cpp
Hw.cpp
HwRead.cpp
HwWrite.cpp
@@ -180,7 +179,6 @@ set(pcsx2Headers
GS.h
Hardware.h
Host.h
HostDisplay.h
Hw.h
IopBios.h
IopCounters.h
@@ -785,42 +783,7 @@ set(pcsx2FrontendHeaders
Frontend/ImGuiOverlays.h
)
if(USE_OPENGL)
list(APPEND pcsx2FrontendSources
Frontend/OpenGLHostDisplay.cpp
Frontend/imgui_impl_opengl3.cpp
)
list(APPEND pcsx2FrontendHeaders
Frontend/OpenGLHostDisplay.h
Frontend/imgui_impl_opengl3.h
)
endif()
if(USE_VULKAN)
list(APPEND pcsx2FrontendSources
Frontend/VulkanHostDisplay.cpp
Frontend/imgui_impl_vulkan.cpp
)
list(APPEND pcsx2FrontendHeaders
Frontend/imgui_impl_vulkan.h
Frontend/VulkanHostDisplay.h
)
endif()
if(WIN32)
list(APPEND pcsx2FrontendSources
Frontend/D3D11HostDisplay.cpp
Frontend/D3D12HostDisplay.cpp
Frontend/imgui_impl_dx11.cpp
Frontend/imgui_impl_dx12.cpp
)
list(APPEND pcsx2FrontendHeaders
Frontend/D3D11HostDisplay.h
Frontend/D3D12HostDisplay.h
Frontend/imgui_impl_dx11.h
Frontend/imgui_impl_dx12.h
)
elseif(APPLE)
if(APPLE)
list(APPEND pcsx2GSSources
GS/Renderers/Metal/GSDeviceMTL.mm
GS/Renderers/Metal/GSMTLDeviceInfo.mm
@@ -834,12 +797,6 @@ elseif(APPLE)
GS/Renderers/Metal/GSMTLShaderCommon.h
GS/Renderers/Metal/GSTextureMTL.h
)
list(APPEND pcsx2FrontendSources
Frontend/MetalHostDisplay.mm
)
list(APPEND pcsx2FrontendHeaders
Frontend/MetalHostDisplay.h
)
endif()
list(APPEND pcsx2FrontendSources
+67 -66
View File
@@ -25,13 +25,13 @@
#include "IopCounters.h"
#include "GS.h"
#include "GS/GS.h"
#include "VUmicro.h"
#include "PerformanceMetrics.h"
#include "Patch.h"
#include "ps2/HwInternal.h"
#include "Sio.h"
#include "HostDisplay.h"
#include "SPU2/spu2.h"
#include "PAD/Host/PAD.h"
#include "Recording/InputRecording.h"
@@ -226,10 +226,11 @@ static void vSyncInfoCalc(vSyncTimingInfo* info, double framesPerSecond, u32 sca
// Dynasty Warriors 3 Xtreme Legends - fake save corruption when loading save
// Jak II - random speedups
// Shadow of Rome - FMV audio issues
const bool ntsc_hblank = gsVideoMode != GS_VideoMode::PAL && gsVideoMode == GS_VideoMode::DVD_PAL;
const u64 HalfFrame = Frame / 2;
const u64 Blank = Scanline * ((gsVideoMode == GS_VideoMode::NTSC ? 22 : 25) + static_cast<int>(gsIsInterlaced));
const u64 Blank = Scanline * ((ntsc_hblank ? 22 : 25) + static_cast<int>(!IsProgressiveVideoMode()));
const u64 Render = HalfFrame - Blank;
const u64 GSBlank = Scanline * (gsVideoMode == GS_VideoMode::NTSC ? 3.5 : 3); // GS VBlank/CSR Swap happens roughly 3.5(NTSC) and 3(PAL) Scanlines after VBlank Start
const u64 GSBlank = Scanline * (ntsc_hblank ? 3.5 : 3); // GS VBlank/CSR Swap happens roughly 3.5(NTSC) and 3(PAL) Scanlines after VBlank Start
// Important! The hRender/hBlank timers should be 50/50 for best results.
// (this appears to be what the real EE's timing crystal does anyway)
@@ -321,6 +322,7 @@ double GetVerticalFrequency()
// https://web.archive.org/web/20201102100937/http://forums.nesdev.com/viewtopic.php?t=7909
// https://web.archive.org/web/20120629231826fw_/http://ntsc-tv.com/index.html
// https://web.archive.org/web/20200831051302/https://www.hdretrovision.com/240p/
switch (gsVideoMode)
{
case GS_VideoMode::Uninitialized: // SetGsCrt hasn't executed yet, give some temporary values.
@@ -355,7 +357,7 @@ static double AdjustToHostRefreshRate(double vertical_frequency, double frame_li
}
float host_refresh_rate;
if (!g_host_display->GetHostRefreshRate(&host_refresh_rate))
if (!GSGetHostRefreshRate(&host_refresh_rate))
{
Console.Warning("Cannot sync to host refresh since the query failed.");
SPU2::SetDeviceSampleRateMultiplier(1.0);
@@ -378,7 +380,7 @@ static double AdjustToHostRefreshRate(double vertical_frequency, double frame_li
return frame_limit;
}
u32 UpdateVSyncRate()
void UpdateVSyncRate(bool force)
{
// Notice: (and I probably repeat this elsewhere, but it's worth repeating)
// The PS2's vsync timer is an *independent* crystal that is fixed to either 59.94 (NTSC)
@@ -389,62 +391,63 @@ u32 UpdateVSyncRate()
const double vertical_frequency = GetVerticalFrequency();
const double frames_per_second = vertical_frequency / 2.0;
const double frame_limit = AdjustToHostRefreshRate(vertical_frequency, frames_per_second * EmuConfig.GS.LimitScalar);
const double tick_rate = GetTickFrequency() / 2.0;
const s64 ticks = static_cast<s64>(tick_rate / std::max(frame_limit, 1.0));
u32 total_scanlines = 0;
bool custom = false;
switch (gsVideoMode)
if (vSyncInfo.Framerate != frames_per_second || vSyncInfo.VideoMode != gsVideoMode || force)
{
case GS_VideoMode::Uninitialized: // SYSCALL instruction hasn't executed yet, give some temporary values.
if(gsIsInterlaced)
total_scanlines = SCANLINES_TOTAL_NTSC_I;
else
total_scanlines = SCANLINES_TOTAL_NTSC_NI;
break;
case GS_VideoMode::PAL:
case GS_VideoMode::DVD_PAL:
custom = (EmuConfig.GS.FrameratePAL != Pcsx2Config::GSOptions::DEFAULT_FRAME_RATE_PAL);
if (gsIsInterlaced)
total_scanlines = SCANLINES_TOTAL_PAL_I;
else
total_scanlines = SCANLINES_TOTAL_PAL_NI;
break;
case GS_VideoMode::NTSC:
case GS_VideoMode::DVD_NTSC:
custom = (EmuConfig.GS.FramerateNTSC != Pcsx2Config::GSOptions::DEFAULT_FRAME_RATE_NTSC);
if (gsIsInterlaced)
total_scanlines = SCANLINES_TOTAL_NTSC_I;
else
total_scanlines = SCANLINES_TOTAL_NTSC_NI;
break;
case GS_VideoMode::SDTV_480P:
case GS_VideoMode::SDTV_576P:
case GS_VideoMode::HDTV_720P:
case GS_VideoMode::VESA:
total_scanlines = SCANLINES_TOTAL_NTSC_I;
break;
case GS_VideoMode::HDTV_1080P:
case GS_VideoMode::HDTV_1080I:
total_scanlines = SCANLINES_TOTAL_1080;
break;
case GS_VideoMode::Unknown:
default:
if (gsIsInterlaced)
total_scanlines = SCANLINES_TOTAL_NTSC_I;
else
total_scanlines = SCANLINES_TOTAL_NTSC_NI;
Console.Error("PCSX2-Counters: Unknown video mode detected");
pxAssertDev(false , "Unknown video mode detected via SetGsCrt");
}
const double frame_limit = AdjustToHostRefreshRate(vertical_frequency, frames_per_second * EmuConfig.GS.LimitScalar);
const bool video_mode_initialized = gsVideoMode != GS_VideoMode::Uninitialized;
const double tick_rate = GetTickFrequency() / 2.0;
const s64 ticks = static_cast<s64>(tick_rate / std::max(frame_limit, 1.0));
u32 total_scanlines = 0;
bool custom = false;
switch (gsVideoMode)
{
case GS_VideoMode::Uninitialized: // SYSCALL instruction hasn't executed yet, give some temporary values.
if (gsIsInterlaced)
total_scanlines = SCANLINES_TOTAL_NTSC_I;
else
total_scanlines = SCANLINES_TOTAL_NTSC_NI;
break;
case GS_VideoMode::PAL:
case GS_VideoMode::DVD_PAL:
custom = (EmuConfig.GS.FrameratePAL != Pcsx2Config::GSOptions::DEFAULT_FRAME_RATE_PAL);
if (gsIsInterlaced)
total_scanlines = SCANLINES_TOTAL_PAL_I;
else
total_scanlines = SCANLINES_TOTAL_PAL_NI;
break;
case GS_VideoMode::NTSC:
case GS_VideoMode::DVD_NTSC:
custom = (EmuConfig.GS.FramerateNTSC != Pcsx2Config::GSOptions::DEFAULT_FRAME_RATE_NTSC);
if (gsIsInterlaced)
total_scanlines = SCANLINES_TOTAL_NTSC_I;
else
total_scanlines = SCANLINES_TOTAL_NTSC_NI;
break;
case GS_VideoMode::SDTV_480P:
case GS_VideoMode::SDTV_576P:
case GS_VideoMode::HDTV_720P:
case GS_VideoMode::VESA:
total_scanlines = SCANLINES_TOTAL_NTSC_I;
break;
case GS_VideoMode::HDTV_1080P:
case GS_VideoMode::HDTV_1080I:
total_scanlines = SCANLINES_TOTAL_1080;
break;
case GS_VideoMode::Unknown:
default:
if (gsIsInterlaced)
total_scanlines = SCANLINES_TOTAL_NTSC_I;
else
total_scanlines = SCANLINES_TOTAL_NTSC_NI;
Console.Error("PCSX2-Counters: Unknown video mode detected");
pxAssertDev(false, "Unknown video mode detected via SetGsCrt");
}
const bool video_mode_initialized = gsVideoMode != GS_VideoMode::Uninitialized;
if (vSyncInfo.Framerate != frames_per_second || vSyncInfo.VideoMode != gsVideoMode)
{
// NBA Jam 2004 PAL will fail to display 3D on the menu if this value isn't correct on reset.
if (video_mode_initialized && vSyncInfo.VideoMode != gsVideoMode)
CSRreg.FIELD = 1;
@@ -465,16 +468,14 @@ u32 UpdateVSyncRate()
vsyncCounter.sCycle = cpuRegs.cycle;
vsyncCounter.Mode = MODE_VRENDER;
cpuRcntSet();
PerformanceMetrics::SetVerticalFrequency(vertical_frequency);
if (m_iTicks != ticks)
m_iTicks = ticks;
m_iStart = GetCPUTicks();
}
PerformanceMetrics::SetVerticalFrequency(vertical_frequency);
if (m_iTicks != ticks)
m_iTicks = ticks;
m_iStart = GetCPUTicks();
return static_cast<u32>(m_iTicks);
}
void frameLimitReset()
+1 -1
View File
@@ -145,6 +145,6 @@ extern u32 rcntRcount(int index);
template< uint page > extern bool rcntWrite32( u32 mem, mem32_t& value );
template< uint page > extern u16 rcntRead32( u32 mem ); // returns u16 by design! (see implementation for details)
extern u32 UpdateVSyncRate();
extern void UpdateVSyncRate(bool force);
extern void frameLimitReset();
-1
View File
@@ -29,7 +29,6 @@
#include "GS.h"
#include "GS/Renderers/HW/GSTextureReplacements.h"
#include "Host.h"
#include "HostDisplay.h"
#include "HostSettings.h"
#include "IconsFontAwesome5.h"
#include "MemoryCardFile.h"
-1
View File
@@ -23,7 +23,6 @@
#include "Frontend/InputManager.h"
#include "GS.h"
#include "Host.h"
#include "HostDisplay.h"
#include "IconsFontAwesome5.h"
#include "Recording/InputRecording.h"
#include "SPU2/spu2.h"
-740
View File
@@ -1,740 +0,0 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2021 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "PrecompiledHeader.h"
#include "Frontend/D3D11HostDisplay.h"
#include "GS/Renderers/DX11/D3D.h"
#include "imgui.h"
#include "imgui_impl_dx11.h"
#include <array>
#include <dxgi1_5.h>
#include "common/Assertions.h"
#include "common/Console.h"
#include "common/StringUtil.h"
#include "common/RedtapeWindows.h"
#include "Config.h"
#pragma comment(lib, "d3d11.lib")
#pragma comment(lib, "dxgi.lib")
class D3D11HostDisplayTexture : public HostDisplayTexture
{
public:
D3D11HostDisplayTexture(wil::com_ptr_nothrow<ID3D11Texture2D> texture, wil::com_ptr_nothrow<ID3D11ShaderResourceView> srv, u32 width,
u32 height, bool dynamic)
: m_texture(std::move(texture))
, m_srv(std::move(srv))
, m_width(width)
, m_height(height)
, m_dynamic(dynamic)
{
}
~D3D11HostDisplayTexture() override = default;
void* GetHandle() const override { return m_srv.get(); }
u32 GetWidth() const override { return m_width; }
u32 GetHeight() const override { return m_height; }
__fi ID3D11Texture2D* GetD3DTexture() const { return m_texture.get(); }
__fi ID3D11ShaderResourceView* GetD3DSRV() const { return m_srv.get(); }
__fi ID3D11ShaderResourceView* const* GetD3DSRVArray() const { return m_srv.addressof(); }
__fi bool IsDynamic() const { return m_dynamic; }
private:
wil::com_ptr_nothrow<ID3D11Texture2D> m_texture;
wil::com_ptr_nothrow<ID3D11ShaderResourceView> m_srv;
u32 m_width;
u32 m_height;
bool m_dynamic;
};
D3D11HostDisplay::D3D11HostDisplay() = default;
D3D11HostDisplay::~D3D11HostDisplay()
{
D3D11HostDisplay::DestroySurface();
m_context.reset();
m_device.reset();
}
RenderAPI D3D11HostDisplay::GetRenderAPI() const
{
return RenderAPI::D3D11;
}
void* D3D11HostDisplay::GetDevice() const
{
return m_device.get();
}
void* D3D11HostDisplay::GetContext() const
{
return m_context.get();
}
void* D3D11HostDisplay::GetSurface() const
{
return m_swap_chain.get();
}
bool D3D11HostDisplay::HasDevice() const
{
return static_cast<bool>(m_device);
}
bool D3D11HostDisplay::HasSurface() const
{
return static_cast<bool>(m_swap_chain);
}
std::unique_ptr<HostDisplayTexture> D3D11HostDisplay::CreateTexture(
u32 width, u32 height, const void* data, u32 data_stride, bool dynamic /* = false */)
{
const CD3D11_TEXTURE2D_DESC desc(DXGI_FORMAT_R8G8B8A8_UNORM, width, height, 1u, 1u, D3D11_BIND_SHADER_RESOURCE,
dynamic ? D3D11_USAGE_DYNAMIC : D3D11_USAGE_DEFAULT, dynamic ? D3D11_CPU_ACCESS_WRITE : 0, 1, 0, 0);
const D3D11_SUBRESOURCE_DATA srd{data, data_stride, data_stride * height};
ComPtr<ID3D11Texture2D> texture;
HRESULT hr = m_device->CreateTexture2D(&desc, data ? &srd : nullptr, texture.addressof());
if (FAILED(hr))
return {};
const CD3D11_SHADER_RESOURCE_VIEW_DESC srv_desc(D3D11_SRV_DIMENSION_TEXTURE2D, DXGI_FORMAT_R8G8B8A8_UNORM, 0, 1, 0, 1);
ComPtr<ID3D11ShaderResourceView> srv;
hr = m_device->CreateShaderResourceView(texture.get(), &srv_desc, srv.addressof());
if (FAILED(hr))
return {};
return std::make_unique<D3D11HostDisplayTexture>(std::move(texture), std::move(srv), width, height, dynamic);
}
void D3D11HostDisplay::UpdateTexture(
HostDisplayTexture* texture, u32 x, u32 y, u32 width, u32 height, const void* texture_data, u32 texture_data_stride)
{
D3D11HostDisplayTexture* d3d11_texture = static_cast<D3D11HostDisplayTexture*>(texture);
if (!d3d11_texture->IsDynamic())
{
const CD3D11_BOX dst_box(x, y, 0, x + width, y + height, 1);
m_context->UpdateSubresource(
d3d11_texture->GetD3DTexture(), 0, &dst_box, texture_data, texture_data_stride, texture_data_stride * height);
}
else
{
D3D11_MAPPED_SUBRESOURCE sr;
HRESULT hr = m_context->Map(d3d11_texture->GetD3DTexture(), 0, D3D11_MAP_WRITE_DISCARD, 0, &sr);
pxAssertMsg(SUCCEEDED(hr), "Failed to map dynamic host display texture");
char* dst_ptr = static_cast<char*>(sr.pData) + (y * sr.RowPitch) + (x * sizeof(u32));
const char* src_ptr = static_cast<const char*>(texture_data);
if (sr.RowPitch == texture_data_stride)
{
std::memcpy(dst_ptr, src_ptr, texture_data_stride * height);
}
else
{
for (u32 row = 0; row < height; row++)
{
std::memcpy(dst_ptr, src_ptr, width * sizeof(u32));
src_ptr += texture_data_stride;
dst_ptr += sr.RowPitch;
}
}
m_context->Unmap(d3d11_texture->GetD3DTexture(), 0);
}
}
bool D3D11HostDisplay::GetHostRefreshRate(float* refresh_rate)
{
if (m_swap_chain && IsFullscreen())
{
DXGI_SWAP_CHAIN_DESC desc;
if (SUCCEEDED(m_swap_chain->GetDesc(&desc)) && desc.BufferDesc.RefreshRate.Numerator > 0 &&
desc.BufferDesc.RefreshRate.Denominator > 0)
{
DevCon.WriteLn("using fs rr: %u %u", desc.BufferDesc.RefreshRate.Numerator, desc.BufferDesc.RefreshRate.Denominator);
*refresh_rate =
static_cast<float>(desc.BufferDesc.RefreshRate.Numerator) / static_cast<float>(desc.BufferDesc.RefreshRate.Denominator);
return true;
}
}
return HostDisplay::GetHostRefreshRate(refresh_rate);
}
void D3D11HostDisplay::SetVSync(VsyncMode mode)
{
m_vsync_mode = mode;
}
bool D3D11HostDisplay::CreateDevice(const WindowInfo& wi, VsyncMode vsync)
{
UINT create_flags = 0;
if (EmuConfig.GS.UseDebugDevice)
create_flags |= D3D11_CREATE_DEVICE_DEBUG;
m_dxgi_factory = D3D::CreateFactory(EmuConfig.GS.UseDebugDevice);
if (!m_dxgi_factory)
return false;
ComPtr<IDXGIAdapter1> dxgi_adapter = D3D::GetAdapterByName(m_dxgi_factory.get(), EmuConfig.GS.Adapter);
static constexpr std::array<D3D_FEATURE_LEVEL, 3> requested_feature_levels = {
{D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_1, D3D_FEATURE_LEVEL_10_0}};
HRESULT hr = D3D11CreateDevice(dxgi_adapter.get(), dxgi_adapter ? D3D_DRIVER_TYPE_UNKNOWN : D3D_DRIVER_TYPE_HARDWARE, nullptr,
create_flags, requested_feature_levels.data(), static_cast<UINT>(requested_feature_levels.size()), D3D11_SDK_VERSION,
m_device.put(), nullptr, m_context.put());
// we re-grab these later, see below
dxgi_adapter.reset();
if (FAILED(hr))
{
Console.Error("Failed to create D3D device: 0x%08X", hr);
return false;
}
if (EmuConfig.GS.UseDebugDevice && IsDebuggerPresent())
{
ComPtr<ID3D11InfoQueue> info;
if (m_device.try_query_to(&info))
{
info->SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_ERROR, TRUE);
info->SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_WARNING, TRUE);
}
}
ComPtr<IDXGIDevice> dxgi_device;
if (m_device.try_query_to(&dxgi_device) && SUCCEEDED(dxgi_device->GetParent(IID_PPV_ARGS(dxgi_adapter.put()))))
Console.WriteLn(fmt::format("D3D Adapter: {}", D3D::GetAdapterName(dxgi_adapter.get())));
else
Console.Error("Failed to obtain D3D adapter name.");
BOOL allow_tearing_supported = false;
hr = m_dxgi_factory->CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING, &allow_tearing_supported, sizeof(allow_tearing_supported));
m_allow_tearing_supported = (SUCCEEDED(hr) && allow_tearing_supported == TRUE);
m_window_info = wi;
m_vsync_mode = vsync;
if (m_window_info.type != WindowInfo::Type::Surfaceless && !CreateSwapChain(nullptr))
return false;
return true;
}
bool D3D11HostDisplay::SetupDevice()
{
return true;
}
bool D3D11HostDisplay::MakeCurrent()
{
return true;
}
bool D3D11HostDisplay::DoneCurrent()
{
return true;
}
bool D3D11HostDisplay::CreateSwapChain(const DXGI_MODE_DESC* fullscreen_mode)
{
if (m_window_info.type != WindowInfo::Type::Win32)
return false;
m_using_flip_model_swap_chain = !EmuConfig.GS.UseBlitSwapChain || fullscreen_mode;
const HWND window_hwnd = reinterpret_cast<HWND>(m_window_info.window_handle);
RECT client_rc{};
GetClientRect(window_hwnd, &client_rc);
const u32 width = static_cast<u32>(client_rc.right - client_rc.left);
const u32 height = static_cast<u32>(client_rc.bottom - client_rc.top);
DXGI_SWAP_CHAIN_DESC1 swap_chain_desc = {};
swap_chain_desc.Width = width;
swap_chain_desc.Height = height;
swap_chain_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
swap_chain_desc.SampleDesc.Count = 1;
swap_chain_desc.BufferCount = 3;
swap_chain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swap_chain_desc.SwapEffect = m_using_flip_model_swap_chain ? DXGI_SWAP_EFFECT_FLIP_DISCARD : DXGI_SWAP_EFFECT_DISCARD;
m_using_allow_tearing = (m_allow_tearing_supported && m_using_flip_model_swap_chain && !fullscreen_mode);
if (m_using_allow_tearing)
swap_chain_desc.Flags |= DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING;
DXGI_SWAP_CHAIN_FULLSCREEN_DESC fs_desc = {};
if (fullscreen_mode)
{
swap_chain_desc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
swap_chain_desc.Width = fullscreen_mode->Width;
swap_chain_desc.Height = fullscreen_mode->Height;
fs_desc.RefreshRate = fullscreen_mode->RefreshRate;
fs_desc.ScanlineOrdering = fullscreen_mode->ScanlineOrdering;
fs_desc.Scaling = fullscreen_mode->Scaling;
fs_desc.Windowed = FALSE;
}
Console.WriteLn("Creating a %dx%d %s %s swap chain", swap_chain_desc.Width, swap_chain_desc.Height,
m_using_flip_model_swap_chain ? "flip-discard" : "discard", fullscreen_mode ? "full-screen" : "windowed");
HRESULT hr = m_dxgi_factory->CreateSwapChainForHwnd(
m_device.get(), window_hwnd, &swap_chain_desc, fullscreen_mode ? &fs_desc : nullptr, nullptr, m_swap_chain.put());
if (FAILED(hr) && m_using_flip_model_swap_chain)
{
Console.Warning("Failed to create a flip-discard swap chain, trying discard.");
swap_chain_desc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
swap_chain_desc.Flags = 0;
m_using_flip_model_swap_chain = false;
m_using_allow_tearing = false;
hr = m_dxgi_factory->CreateSwapChainForHwnd(
m_device.get(), window_hwnd, &swap_chain_desc, fullscreen_mode ? &fs_desc : nullptr, nullptr, m_swap_chain.put());
if (FAILED(hr))
{
Console.Error("CreateSwapChainForHwnd failed: 0x%08X", hr);
return false;
}
}
hr = m_dxgi_factory->MakeWindowAssociation(window_hwnd, DXGI_MWA_NO_WINDOW_CHANGES);
if (FAILED(hr))
Console.Warning("MakeWindowAssociation() to disable ALT+ENTER failed");
return CreateSwapChainRTV();
}
bool D3D11HostDisplay::CreateSwapChainRTV()
{
ComPtr<ID3D11Texture2D> backbuffer;
HRESULT hr = m_swap_chain->GetBuffer(0, IID_PPV_ARGS(backbuffer.put()));
if (FAILED(hr))
{
Console.Error("GetBuffer for RTV failed: 0x%08X", hr);
return false;
}
D3D11_TEXTURE2D_DESC backbuffer_desc;
backbuffer->GetDesc(&backbuffer_desc);
CD3D11_RENDER_TARGET_VIEW_DESC rtv_desc(D3D11_RTV_DIMENSION_TEXTURE2D, backbuffer_desc.Format, 0, 0, backbuffer_desc.ArraySize);
hr = m_device->CreateRenderTargetView(backbuffer.get(), &rtv_desc, m_swap_chain_rtv.put());
if (FAILED(hr))
{
Console.Error("CreateRenderTargetView for swap chain failed: 0x%08X", hr);
return false;
}
m_window_info.surface_width = backbuffer_desc.Width;
m_window_info.surface_height = backbuffer_desc.Height;
DevCon.WriteLn("Swap chain buffer size: %ux%u", m_window_info.surface_width, m_window_info.surface_height);
if (m_window_info.type == WindowInfo::Type::Win32)
{
BOOL fullscreen = FALSE;
DXGI_SWAP_CHAIN_DESC desc;
if (SUCCEEDED(m_swap_chain->GetFullscreenState(&fullscreen, nullptr)) && fullscreen && SUCCEEDED(m_swap_chain->GetDesc(&desc)))
{
m_window_info.surface_refresh_rate =
static_cast<float>(desc.BufferDesc.RefreshRate.Numerator) / static_cast<float>(desc.BufferDesc.RefreshRate.Denominator);
}
else
{
m_window_info.surface_refresh_rate = 0.0f;
}
}
return true;
}
bool D3D11HostDisplay::ChangeWindow(const WindowInfo& new_wi)
{
DestroySurface();
m_window_info = new_wi;
return CreateSwapChain(nullptr);
}
void D3D11HostDisplay::DestroySurface()
{
if (IsFullscreen())
SetFullscreen(false, 0, 0, 0.0f);
m_swap_chain_rtv.reset();
m_swap_chain.reset();
}
std::string D3D11HostDisplay::GetDriverInfo() const
{
std::string ret = "Unknown Feature Level";
static constexpr std::array<std::tuple<D3D_FEATURE_LEVEL, const char*>, 4> feature_level_names = {{
{D3D_FEATURE_LEVEL_10_0, "D3D_FEATURE_LEVEL_10_0"},
{D3D_FEATURE_LEVEL_10_0, "D3D_FEATURE_LEVEL_10_1"},
{D3D_FEATURE_LEVEL_11_0, "D3D_FEATURE_LEVEL_11_0"},
{D3D_FEATURE_LEVEL_11_1, "D3D_FEATURE_LEVEL_11_1"},
}};
const D3D_FEATURE_LEVEL fl = m_device->GetFeatureLevel();
for (size_t i = 0; i < std::size(feature_level_names); i++)
{
if (fl == std::get<0>(feature_level_names[i]))
{
ret = std::get<1>(feature_level_names[i]);
break;
}
}
ret += "\n";
ComPtr<IDXGIDevice> dxgi_dev;
if (m_device.try_query_to(&dxgi_dev))
{
ComPtr<IDXGIAdapter> dxgi_adapter;
if (SUCCEEDED(dxgi_dev->GetAdapter(dxgi_adapter.put())))
{
DXGI_ADAPTER_DESC desc;
if (SUCCEEDED(dxgi_adapter->GetDesc(&desc)))
{
ret += StringUtil::StdStringFromFormat("VID: 0x%04X PID: 0x%04X\n", desc.VendorId, desc.DeviceId);
ret += StringUtil::WideStringToUTF8String(desc.Description);
ret += "\n";
const std::string driver_version(D3D::GetDriverVersionFromLUID(desc.AdapterLuid));
if (!driver_version.empty())
{
ret += "Driver Version: ";
ret += driver_version;
}
}
}
}
return ret;
}
void D3D11HostDisplay::ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale)
{
if (!m_swap_chain)
return;
m_window_info.surface_scale = new_window_scale;
if (m_window_info.surface_width == new_window_width && m_window_info.surface_height == new_window_height)
return;
m_swap_chain_rtv.reset();
HRESULT hr = m_swap_chain->ResizeBuffers(0, 0, 0, DXGI_FORMAT_UNKNOWN, m_using_allow_tearing ? DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING : 0);
if (FAILED(hr))
Console.Error("ResizeBuffers() failed: 0x%08X", hr);
if (!CreateSwapChainRTV())
pxFailRel("Failed to recreate swap chain RTV after resize");
}
bool D3D11HostDisplay::SupportsFullscreen() const
{
return true;
}
bool D3D11HostDisplay::IsFullscreen()
{
BOOL is_fullscreen = FALSE;
return (m_swap_chain && SUCCEEDED(m_swap_chain->GetFullscreenState(&is_fullscreen, nullptr)) && is_fullscreen);
}
bool D3D11HostDisplay::SetFullscreen(bool fullscreen, u32 width, u32 height, float refresh_rate)
{
if (!m_swap_chain)
return false;
BOOL is_fullscreen = FALSE;
HRESULT hr = m_swap_chain->GetFullscreenState(&is_fullscreen, nullptr);
if (!fullscreen)
{
// leaving fullscreen
if (is_fullscreen)
return SUCCEEDED(m_swap_chain->SetFullscreenState(FALSE, nullptr));
else
return true;
}
IDXGIOutput* output;
if (FAILED(hr = m_swap_chain->GetContainingOutput(&output)))
return false;
DXGI_SWAP_CHAIN_DESC current_desc;
hr = m_swap_chain->GetDesc(&current_desc);
if (FAILED(hr))
return false;
DXGI_MODE_DESC new_mode = current_desc.BufferDesc;
new_mode.Width = width;
new_mode.Height = height;
new_mode.RefreshRate.Numerator = static_cast<UINT>(std::floor(refresh_rate * 1000.0f));
new_mode.RefreshRate.Denominator = 1000u;
DXGI_MODE_DESC closest_mode;
if (FAILED(hr = output->FindClosestMatchingMode(&new_mode, &closest_mode, nullptr)) ||
new_mode.Format != current_desc.BufferDesc.Format)
{
Console.Error("Failed to find closest matching mode, hr=%08X", hr);
return false;
}
if (new_mode.Width == current_desc.BufferDesc.Width && new_mode.Height == current_desc.BufferDesc.Height &&
new_mode.RefreshRate.Numerator == current_desc.BufferDesc.RefreshRate.Numerator &&
new_mode.RefreshRate.Denominator == current_desc.BufferDesc.RefreshRate.Denominator)
{
DevCon.WriteLn("Fullscreen mode already set");
return true;
}
m_swap_chain_rtv.reset();
m_swap_chain.reset();
if (!CreateSwapChain(&closest_mode))
{
Console.Error("Failed to create a fullscreen swap chain");
if (!CreateSwapChain(nullptr))
pxFailRel("Failed to recreate windowed swap chain");
return false;
}
return true;
}
bool D3D11HostDisplay::CreateImGuiContext()
{
return ImGui_ImplDX11_Init(m_device.get(), m_context.get());
}
void D3D11HostDisplay::DestroyImGuiContext()
{
ImGui_ImplDX11_Shutdown();
}
bool D3D11HostDisplay::UpdateImGuiFontTexture()
{
ImGui_ImplDX11_CreateFontsTexture();
return true;
}
HostDisplay::PresentResult D3D11HostDisplay::BeginPresent(bool frame_skip)
{
if (frame_skip || !m_swap_chain)
return PresentResult::FrameSkipped;
// When using vsync, the time here seems to include the time for the buffer to become available.
// This blows our our GPU usage number considerably, so read the timestamp before the final blit
// in this configuration. It does reduce accuracy a little, but better than seeing 100% all of
// the time, when it's more like a couple of percent.
if (m_vsync_mode != VsyncMode::Off && m_gpu_timing_enabled)
PopTimestampQuery();
static constexpr std::array<float, 4> clear_color = {};
m_context->ClearRenderTargetView(m_swap_chain_rtv.get(), clear_color.data());
m_context->OMSetRenderTargets(1, m_swap_chain_rtv.addressof(), nullptr);
const CD3D11_VIEWPORT vp(0.0f, 0.0f, static_cast<float>(m_window_info.surface_width), static_cast<float>(m_window_info.surface_height));
const CD3D11_RECT scissor(0, 0, m_window_info.surface_width, m_window_info.surface_height);
m_context->RSSetViewports(1, &vp);
m_context->RSSetScissorRects(1, &scissor);
return PresentResult::OK;
}
void D3D11HostDisplay::EndPresent()
{
ImGui::Render();
ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
// See note in BeginPresent() for why it's conditional on vsync-off.
const bool vsync_on = m_vsync_mode != VsyncMode::Off;
if (!vsync_on && m_gpu_timing_enabled)
PopTimestampQuery();
if (!vsync_on && m_using_allow_tearing)
m_swap_chain->Present(0, DXGI_PRESENT_ALLOW_TEARING);
else
m_swap_chain->Present(static_cast<UINT>(vsync_on), 0);
if (m_gpu_timing_enabled)
KickTimestampQuery();
}
bool D3D11HostDisplay::CreateTimestampQueries()
{
for (u32 i = 0; i < NUM_TIMESTAMP_QUERIES; i++)
{
for (u32 j = 0; j < 3; j++)
{
const CD3D11_QUERY_DESC qdesc((j == 0) ? D3D11_QUERY_TIMESTAMP_DISJOINT : D3D11_QUERY_TIMESTAMP);
const HRESULT hr = m_device->CreateQuery(&qdesc, m_timestamp_queries[i][j].put());
if (FAILED(hr))
{
m_timestamp_queries = {};
return false;
}
}
}
KickTimestampQuery();
return true;
}
void D3D11HostDisplay::DestroyTimestampQueries()
{
if (!m_timestamp_queries[0][0])
return;
if (m_timestamp_query_started)
m_context->End(m_timestamp_queries[m_write_timestamp_query][1].get());
m_timestamp_queries = {};
m_read_timestamp_query = 0;
m_write_timestamp_query = 0;
m_waiting_timestamp_queries = 0;
m_timestamp_query_started = 0;
}
void D3D11HostDisplay::PopTimestampQuery()
{
while (m_waiting_timestamp_queries > 0)
{
D3D11_QUERY_DATA_TIMESTAMP_DISJOINT disjoint;
const HRESULT disjoint_hr = m_context->GetData(
m_timestamp_queries[m_read_timestamp_query][0].get(), &disjoint, sizeof(disjoint), D3D11_ASYNC_GETDATA_DONOTFLUSH);
if (disjoint_hr != S_OK)
break;
if (disjoint.Disjoint)
{
DevCon.WriteLn("GPU timing disjoint, resetting.");
m_read_timestamp_query = 0;
m_write_timestamp_query = 0;
m_waiting_timestamp_queries = 0;
m_timestamp_query_started = 0;
}
else
{
u64 start = 0, end = 0;
const HRESULT start_hr = m_context->GetData(
m_timestamp_queries[m_read_timestamp_query][1].get(), &start, sizeof(start), D3D11_ASYNC_GETDATA_DONOTFLUSH);
const HRESULT end_hr =
m_context->GetData(m_timestamp_queries[m_read_timestamp_query][2].get(), &end, sizeof(end), D3D11_ASYNC_GETDATA_DONOTFLUSH);
if (start_hr == S_OK && end_hr == S_OK)
{
m_accumulated_gpu_time +=
static_cast<float>(static_cast<double>(end - start) / (static_cast<double>(disjoint.Frequency) / 1000.0));
m_read_timestamp_query = (m_read_timestamp_query + 1) % NUM_TIMESTAMP_QUERIES;
m_waiting_timestamp_queries--;
}
}
}
if (m_timestamp_query_started)
{
m_context->End(m_timestamp_queries[m_write_timestamp_query][2].get());
m_context->End(m_timestamp_queries[m_write_timestamp_query][0].get());
m_write_timestamp_query = (m_write_timestamp_query + 1) % NUM_TIMESTAMP_QUERIES;
m_timestamp_query_started = false;
m_waiting_timestamp_queries++;
}
}
void D3D11HostDisplay::KickTimestampQuery()
{
if (m_timestamp_query_started || !m_timestamp_queries[0][0] || m_waiting_timestamp_queries == NUM_TIMESTAMP_QUERIES)
return;
m_context->Begin(m_timestamp_queries[m_write_timestamp_query][0].get());
m_context->End(m_timestamp_queries[m_write_timestamp_query][1].get());
m_timestamp_query_started = true;
}
bool D3D11HostDisplay::SetGPUTimingEnabled(bool enabled)
{
if (m_gpu_timing_enabled == enabled)
return true;
m_gpu_timing_enabled = enabled;
if (m_gpu_timing_enabled)
{
return CreateTimestampQueries();
}
else
{
DestroyTimestampQueries();
return true;
}
}
float D3D11HostDisplay::GetAndResetAccumulatedGPUTime()
{
const float value = m_accumulated_gpu_time;
m_accumulated_gpu_time = 0.0f;
return value;
}
HostDisplay::AdapterAndModeList D3D11HostDisplay::StaticGetAdapterAndModeList()
{
auto factory = D3D::CreateFactory(false);
if (!factory)
return {};
return GetAdapterAndModeList(factory.get());
}
HostDisplay::AdapterAndModeList D3D11HostDisplay::GetAdapterAndModeList(IDXGIFactory5* dxgi_factory)
{
AdapterAndModeList adapter_info;
adapter_info.adapter_names = D3D::GetAdapterNames(dxgi_factory);
auto adapter = D3D::GetChosenOrFirstAdapter(dxgi_factory, EmuConfig.GS.Adapter);
if (adapter)
{
ComPtr<IDXGIOutput> output;
if (SUCCEEDED(adapter->EnumOutputs(0, &output)))
{
UINT num_modes = 0;
if (SUCCEEDED(output->GetDisplayModeList(DXGI_FORMAT_R8G8B8A8_UNORM, 0, &num_modes, nullptr)))
{
std::vector<DXGI_MODE_DESC> modes(num_modes);
if (SUCCEEDED(output->GetDisplayModeList(DXGI_FORMAT_R8G8B8A8_UNORM, 0, &num_modes, modes.data())))
{
for (const DXGI_MODE_DESC& mode : modes)
{
adapter_info.fullscreen_modes.push_back(GetFullscreenModeString(mode.Width, mode.Height,
static_cast<float>(mode.RefreshRate.Numerator) / static_cast<float>(mode.RefreshRate.Denominator)));
}
}
}
}
}
return adapter_info;
}
HostDisplay::AdapterAndModeList D3D11HostDisplay::GetAdapterAndModeList()
{
return GetAdapterAndModeList(m_dxgi_factory.get());
}
-113
View File
@@ -1,113 +0,0 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2021 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "HostDisplay.h"
#include "common/RedtapeWindows.h"
#include "common/RedtapeWilCom.h"
#include "common/WindowInfo.h"
#include <array>
#include <d3d11.h>
#include <dxgi1_5.h>
#include <memory>
#include <string>
#include <string_view>
#include <vector>
class D3D11HostDisplay final : public HostDisplay
{
public:
template <typename T>
using ComPtr = wil::com_ptr_nothrow<T>;
D3D11HostDisplay();
~D3D11HostDisplay();
RenderAPI GetRenderAPI() const override;
void* GetDevice() const override;
void* GetContext() const override;
void* GetSurface() const override;
bool HasDevice() const override;
bool HasSurface() const override;
bool CreateDevice(const WindowInfo& wi, VsyncMode vsync) override;
bool SetupDevice() override;
bool MakeCurrent() override;
bool DoneCurrent() override;
bool ChangeWindow(const WindowInfo& new_wi) override;
void ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale) override;
bool SupportsFullscreen() const override;
bool IsFullscreen() override;
bool SetFullscreen(bool fullscreen, u32 width, u32 height, float refresh_rate) override;
AdapterAndModeList GetAdapterAndModeList() override;
void DestroySurface() override;
std::string GetDriverInfo() const override;
std::unique_ptr<HostDisplayTexture> CreateTexture(u32 width, u32 height, const void* data, u32 data_stride, bool dynamic = false) override;
void UpdateTexture(HostDisplayTexture* texture, u32 x, u32 y, u32 width, u32 height, const void* texture_data, u32 texture_data_stride) override;
bool GetHostRefreshRate(float* refresh_rate) override;
void SetVSync(VsyncMode mode) override;
PresentResult BeginPresent(bool frame_skip) override;
void EndPresent() override;
bool SetGPUTimingEnabled(bool enabled) override;
float GetAndResetAccumulatedGPUTime() override;
static AdapterAndModeList StaticGetAdapterAndModeList();
protected:
static constexpr u32 DISPLAY_CONSTANT_BUFFER_SIZE = 16;
static constexpr u8 NUM_TIMESTAMP_QUERIES = 5;
static AdapterAndModeList GetAdapterAndModeList(IDXGIFactory5* dxgi_factory);
bool CreateImGuiContext() override;
void DestroyImGuiContext() override;
bool UpdateImGuiFontTexture() override;
bool CreateSwapChain(const DXGI_MODE_DESC* fullscreen_mode);
bool CreateSwapChainRTV();
bool CreateTimestampQueries();
void DestroyTimestampQueries();
void PopTimestampQuery();
void KickTimestampQuery();
ComPtr<ID3D11Device> m_device;
ComPtr<ID3D11DeviceContext> m_context;
ComPtr<IDXGIFactory5> m_dxgi_factory;
ComPtr<IDXGISwapChain1> m_swap_chain;
ComPtr<ID3D11RenderTargetView> m_swap_chain_rtv;
bool m_allow_tearing_supported = false;
bool m_using_flip_model_swap_chain = true;
bool m_using_allow_tearing = false;
std::array<std::array<ComPtr<ID3D11Query>, 3>, NUM_TIMESTAMP_QUERIES> m_timestamp_queries = {};
u8 m_read_timestamp_query = 0;
u8 m_write_timestamp_query = 0;
u8 m_waiting_timestamp_queries = 0;
bool m_timestamp_query_started = false;
float m_accumulated_gpu_time = 0.0f;
bool m_gpu_timing_enabled = false;
};
-573
View File
@@ -1,573 +0,0 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2022 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "PrecompiledHeader.h"
#include "Frontend/D3D12HostDisplay.h"
#include "GS/Renderers/DX11/D3D.h"
#include "common/Assertions.h"
#include "common/Console.h"
#include "common/D3D12/Context.h"
#include "common/D3D12/Util.h"
#include "common/StringUtil.h"
#include "imgui.h"
#include "imgui_impl_dx12.h"
#include <array>
#include <dxgi1_5.h>
class D3D12HostDisplayTexture : public HostDisplayTexture
{
public:
explicit D3D12HostDisplayTexture(D3D12::Texture texture)
: m_texture(std::move(texture))
{
}
~D3D12HostDisplayTexture() override = default;
void* GetHandle() const override { return const_cast<D3D12::Texture*>(&m_texture); }
u32 GetWidth() const override { return m_texture.GetWidth(); }
u32 GetHeight() const override { return m_texture.GetHeight(); }
const D3D12::Texture& GetTexture() const { return m_texture; }
D3D12::Texture& GetTexture() { return m_texture; }
private:
D3D12::Texture m_texture;
};
D3D12HostDisplay::D3D12HostDisplay() = default;
D3D12HostDisplay::~D3D12HostDisplay()
{
if (g_d3d12_context)
{
g_d3d12_context->WaitForGPUIdle();
D3D12HostDisplay::DestroySurface();
g_d3d12_context->Destroy();
}
}
RenderAPI D3D12HostDisplay::GetRenderAPI() const
{
return RenderAPI::D3D12;
}
void* D3D12HostDisplay::GetDevice() const
{
return g_d3d12_context->GetDevice();
}
void* D3D12HostDisplay::GetContext() const
{
return g_d3d12_context.get();
}
void* D3D12HostDisplay::GetSurface() const
{
return m_swap_chain.get();
}
bool D3D12HostDisplay::HasDevice() const
{
return static_cast<bool>(g_d3d12_context);
}
bool D3D12HostDisplay::HasSurface() const
{
return static_cast<bool>(m_swap_chain);
}
std::unique_ptr<HostDisplayTexture> D3D12HostDisplay::CreateTexture(
u32 width, u32 height, const void* data, u32 data_stride, bool dynamic /* = false */)
{
D3D12::Texture tex;
if (!tex.Create(width, height, 1, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN,
D3D12_RESOURCE_FLAG_NONE))
{
return {};
}
if (data && !tex.LoadData(g_d3d12_context->GetInitCommandList(), 0, 0, 0, width, height, data, data_stride))
return {};
return std::make_unique<D3D12HostDisplayTexture>(std::move(tex));
}
void D3D12HostDisplay::UpdateTexture(
HostDisplayTexture* texture, u32 x, u32 y, u32 width, u32 height, const void* texture_data, u32 texture_data_stride)
{
static_cast<D3D12HostDisplayTexture*>(texture)->GetTexture().LoadData(
g_d3d12_context->GetCommandList(), 0, x, y, width, height, texture_data, texture_data_stride);
}
bool D3D12HostDisplay::GetHostRefreshRate(float* refresh_rate)
{
if (m_swap_chain && IsFullscreen())
{
DXGI_SWAP_CHAIN_DESC desc;
if (SUCCEEDED(m_swap_chain->GetDesc(&desc)) && desc.BufferDesc.RefreshRate.Numerator > 0 &&
desc.BufferDesc.RefreshRate.Denominator > 0)
{
DevCon.WriteLn("using fs rr: %u %u", desc.BufferDesc.RefreshRate.Numerator, desc.BufferDesc.RefreshRate.Denominator);
*refresh_rate =
static_cast<float>(desc.BufferDesc.RefreshRate.Numerator) / static_cast<float>(desc.BufferDesc.RefreshRate.Denominator);
return true;
}
}
return HostDisplay::GetHostRefreshRate(refresh_rate);
}
void D3D12HostDisplay::SetVSync(VsyncMode mode)
{
m_vsync_mode = mode;
}
bool D3D12HostDisplay::CreateDevice(const WindowInfo& wi, VsyncMode vsync)
{
m_dxgi_factory = D3D::CreateFactory(EmuConfig.GS.UseDebugDevice);
if (!m_dxgi_factory)
return false;
ComPtr<IDXGIAdapter1> dxgi_adapter = D3D::GetAdapterByName(m_dxgi_factory.get(), EmuConfig.GS.Adapter);
if (!D3D12::Context::Create(m_dxgi_factory.get(), dxgi_adapter.get(), EmuConfig.GS.UseDebugDevice))
return false;
BOOL allow_tearing_supported = false;
HRESULT hr =
m_dxgi_factory->CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING, &allow_tearing_supported, sizeof(allow_tearing_supported));
m_allow_tearing_supported = (SUCCEEDED(hr) && allow_tearing_supported == TRUE);
m_window_info = wi;
m_vsync_mode = vsync;
if (m_window_info.type != WindowInfo::Type::Surfaceless && !CreateSwapChain(nullptr))
return false;
return true;
}
bool D3D12HostDisplay::SetupDevice()
{
return true;
}
bool D3D12HostDisplay::MakeCurrent()
{
return true;
}
bool D3D12HostDisplay::DoneCurrent()
{
return true;
}
bool D3D12HostDisplay::CreateSwapChain(const DXGI_MODE_DESC* fullscreen_mode)
{
if (m_window_info.type != WindowInfo::Type::Win32)
return false;
const HWND window_hwnd = reinterpret_cast<HWND>(m_window_info.window_handle);
RECT client_rc{};
GetClientRect(window_hwnd, &client_rc);
const u32 width = static_cast<u32>(client_rc.right - client_rc.left);
const u32 height = static_cast<u32>(client_rc.bottom - client_rc.top);
DXGI_SWAP_CHAIN_DESC1 swap_chain_desc = {};
swap_chain_desc.Width = width;
swap_chain_desc.Height = height;
swap_chain_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
swap_chain_desc.SampleDesc.Count = 1;
swap_chain_desc.BufferCount = 3;
swap_chain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swap_chain_desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
m_using_allow_tearing = (m_allow_tearing_supported && !fullscreen_mode);
if (m_using_allow_tearing)
swap_chain_desc.Flags |= DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING;
DXGI_SWAP_CHAIN_FULLSCREEN_DESC fs_desc = {};
if (fullscreen_mode)
{
swap_chain_desc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
swap_chain_desc.Width = fullscreen_mode->Width;
swap_chain_desc.Height = fullscreen_mode->Height;
fs_desc.RefreshRate = fullscreen_mode->RefreshRate;
fs_desc.ScanlineOrdering = fullscreen_mode->ScanlineOrdering;
fs_desc.Scaling = fullscreen_mode->Scaling;
fs_desc.Windowed = FALSE;
}
DevCon.WriteLn(
"Creating a %dx%d %s swap chain", swap_chain_desc.Width, swap_chain_desc.Height, fullscreen_mode ? "full-screen" : "windowed");
HRESULT hr = m_dxgi_factory->CreateSwapChainForHwnd(g_d3d12_context->GetCommandQueue(), window_hwnd, &swap_chain_desc,
fullscreen_mode ? &fs_desc : nullptr, nullptr, m_swap_chain.put());
if (FAILED(hr))
{
Console.Error("CreateSwapChainForHwnd failed: 0x%08X", hr);
return false;
}
hr = m_dxgi_factory->MakeWindowAssociation(window_hwnd, DXGI_MWA_NO_WINDOW_CHANGES);
if (FAILED(hr))
Console.Warning("MakeWindowAssociation() to disable ALT+ENTER failed");
return CreateSwapChainRTV();
}
bool D3D12HostDisplay::CreateSwapChainRTV()
{
DXGI_SWAP_CHAIN_DESC swap_chain_desc;
HRESULT hr = m_swap_chain->GetDesc(&swap_chain_desc);
if (FAILED(hr))
return false;
for (u32 i = 0; i < swap_chain_desc.BufferCount; i++)
{
ComPtr<ID3D12Resource> backbuffer;
hr = m_swap_chain->GetBuffer(i, IID_PPV_ARGS(backbuffer.put()));
if (FAILED(hr))
{
Console.Error("GetBuffer for RTV failed: 0x%08X", hr);
return false;
}
D3D12::Texture tex;
if (!tex.Adopt(std::move(backbuffer), DXGI_FORMAT_UNKNOWN, swap_chain_desc.BufferDesc.Format, DXGI_FORMAT_UNKNOWN,
D3D12_RESOURCE_STATE_PRESENT))
{
return false;
}
m_swap_chain_buffers.push_back(std::move(tex));
}
m_window_info.surface_width = swap_chain_desc.BufferDesc.Width;
m_window_info.surface_height = swap_chain_desc.BufferDesc.Height;
DevCon.WriteLn("Swap chain buffer size: %ux%u", m_window_info.surface_width, m_window_info.surface_height);
if (m_window_info.type == WindowInfo::Type::Win32)
{
BOOL fullscreen = FALSE;
DXGI_SWAP_CHAIN_DESC desc;
if (SUCCEEDED(m_swap_chain->GetFullscreenState(&fullscreen, nullptr)) && fullscreen && SUCCEEDED(m_swap_chain->GetDesc(&desc)))
{
m_window_info.surface_refresh_rate =
static_cast<float>(desc.BufferDesc.RefreshRate.Numerator) / static_cast<float>(desc.BufferDesc.RefreshRate.Denominator);
}
else
{
m_window_info.surface_refresh_rate = 0.0f;
}
}
m_current_swap_chain_buffer = 0;
return true;
}
void D3D12HostDisplay::DestroySwapChainRTVs()
{
for (D3D12::Texture& buffer : m_swap_chain_buffers)
buffer.Destroy(false);
m_swap_chain_buffers.clear();
m_current_swap_chain_buffer = 0;
}
bool D3D12HostDisplay::ChangeWindow(const WindowInfo& new_wi)
{
DestroySurface();
m_window_info = new_wi;
return CreateSwapChain(nullptr);
}
void D3D12HostDisplay::DestroySurface()
{
// For some reason if we don't execute the command list here, the swap chain is in use.. not sure where.
g_d3d12_context->ExecuteCommandList(D3D12::Context::WaitType::Sleep);
if (IsFullscreen())
SetFullscreen(false, 0, 0, 0.0f);
DestroySwapChainRTVs();
m_swap_chain.reset();
}
std::string D3D12HostDisplay::GetDriverInfo() const
{
std::string ret = "Unknown Feature Level";
static constexpr std::array<std::tuple<D3D_FEATURE_LEVEL, const char*>, 4> feature_level_names = {{
{D3D_FEATURE_LEVEL_10_0, "D3D_FEATURE_LEVEL_10_0"},
{D3D_FEATURE_LEVEL_10_0, "D3D_FEATURE_LEVEL_10_1"},
{D3D_FEATURE_LEVEL_11_0, "D3D_FEATURE_LEVEL_11_0"},
{D3D_FEATURE_LEVEL_11_1, "D3D_FEATURE_LEVEL_11_1"},
}};
const D3D_FEATURE_LEVEL fl = g_d3d12_context->GetFeatureLevel();
for (size_t i = 0; i < std::size(feature_level_names); i++)
{
if (fl == std::get<0>(feature_level_names[i]))
{
ret = std::get<1>(feature_level_names[i]);
break;
}
}
ret += "\n";
IDXGIAdapter* adapter = g_d3d12_context->GetAdapter();
DXGI_ADAPTER_DESC desc;
if (adapter && SUCCEEDED(adapter->GetDesc(&desc)))
{
ret += StringUtil::StdStringFromFormat("VID: 0x%04X PID: 0x%04X\n", desc.VendorId, desc.DeviceId);
ret += StringUtil::WideStringToUTF8String(desc.Description);
ret += "\n";
const std::string driver_version(D3D::GetDriverVersionFromLUID(desc.AdapterLuid));
if (!driver_version.empty())
{
ret += "Driver Version: ";
ret += driver_version;
}
}
return ret;
}
void D3D12HostDisplay::ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale)
{
if (!m_swap_chain)
return;
m_window_info.surface_scale = new_window_scale;
if (m_window_info.surface_width == new_window_width && m_window_info.surface_height == new_window_height)
return;
// For some reason if we don't execute the command list here, the swap chain is in use.. not sure where.
g_d3d12_context->ExecuteCommandList(D3D12::Context::WaitType::Sleep);
DestroySwapChainRTVs();
HRESULT hr = m_swap_chain->ResizeBuffers(0, 0, 0, DXGI_FORMAT_UNKNOWN, m_using_allow_tearing ? DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING : 0);
if (FAILED(hr))
Console.Error("ResizeBuffers() failed: 0x%08X", hr);
if (!CreateSwapChainRTV())
pxFailRel("Failed to recreate swap chain RTV after resize");
}
bool D3D12HostDisplay::SupportsFullscreen() const
{
return true;
}
bool D3D12HostDisplay::IsFullscreen()
{
BOOL is_fullscreen = FALSE;
return (m_swap_chain && SUCCEEDED(m_swap_chain->GetFullscreenState(&is_fullscreen, nullptr)) && is_fullscreen);
}
bool D3D12HostDisplay::SetFullscreen(bool fullscreen, u32 width, u32 height, float refresh_rate)
{
if (!m_swap_chain)
return false;
BOOL is_fullscreen = FALSE;
HRESULT hr = m_swap_chain->GetFullscreenState(&is_fullscreen, nullptr);
if (!fullscreen)
{
// leaving fullscreen
if (is_fullscreen)
return SUCCEEDED(m_swap_chain->SetFullscreenState(FALSE, nullptr));
else
return true;
}
IDXGIOutput* output;
if (FAILED(hr = m_swap_chain->GetContainingOutput(&output)))
return false;
DXGI_SWAP_CHAIN_DESC current_desc;
hr = m_swap_chain->GetDesc(&current_desc);
if (FAILED(hr))
return false;
DXGI_MODE_DESC new_mode = current_desc.BufferDesc;
new_mode.Width = width;
new_mode.Height = height;
new_mode.RefreshRate.Numerator = static_cast<UINT>(std::floor(refresh_rate * 1000.0f));
new_mode.RefreshRate.Denominator = 1000u;
DXGI_MODE_DESC closest_mode;
if (FAILED(hr = output->FindClosestMatchingMode(&new_mode, &closest_mode, nullptr)) ||
new_mode.Format != current_desc.BufferDesc.Format)
{
Console.Error("Failed to find closest matching mode, hr=%08X", hr);
return false;
}
if (new_mode.Width == current_desc.BufferDesc.Width && new_mode.Height == current_desc.BufferDesc.Width &&
new_mode.RefreshRate.Numerator == current_desc.BufferDesc.RefreshRate.Numerator &&
new_mode.RefreshRate.Denominator == current_desc.BufferDesc.RefreshRate.Denominator)
{
DevCon.WriteLn("Fullscreen mode already set");
return true;
}
g_d3d12_context->ExecuteCommandList(D3D12::Context::WaitType::Sleep);
DestroySwapChainRTVs();
m_swap_chain.reset();
if (!CreateSwapChain(&closest_mode))
{
Console.Error("Failed to create a fullscreen swap chain");
if (!CreateSwapChain(nullptr))
pxFailRel("Failed to recreate windowed swap chain");
return false;
}
return true;
}
HostDisplay::AdapterAndModeList D3D12HostDisplay::GetAdapterAndModeList()
{
return GetAdapterAndModeList(m_dxgi_factory.get());
}
bool D3D12HostDisplay::CreateImGuiContext()
{
ImGui::GetIO().DisplaySize.x = static_cast<float>(m_window_info.surface_width);
ImGui::GetIO().DisplaySize.y = static_cast<float>(m_window_info.surface_height);
if (!ImGui_ImplDX12_Init(DXGI_FORMAT_R8G8B8A8_UNORM))
return false;
return true;
}
void D3D12HostDisplay::DestroyImGuiContext()
{
g_d3d12_context->WaitForGPUIdle();
ImGui_ImplDX12_Shutdown();
}
bool D3D12HostDisplay::UpdateImGuiFontTexture()
{
return ImGui_ImplDX12_CreateFontsTexture();
}
HostDisplay::PresentResult D3D12HostDisplay::BeginPresent(bool frame_skip)
{
if (m_device_lost)
return HostDisplay::PresentResult::DeviceLost;
if (frame_skip || !m_swap_chain)
return PresentResult::FrameSkipped;
static constexpr std::array<float, 4> clear_color = {};
D3D12::Texture& swap_chain_buf = m_swap_chain_buffers[m_current_swap_chain_buffer];
ID3D12GraphicsCommandList* cmdlist = g_d3d12_context->GetCommandList();
swap_chain_buf.TransitionToState(cmdlist, D3D12_RESOURCE_STATE_RENDER_TARGET);
cmdlist->ClearRenderTargetView(swap_chain_buf.GetWriteDescriptor(), clear_color.data(), 0, nullptr);
cmdlist->OMSetRenderTargets(1, &swap_chain_buf.GetWriteDescriptor().cpu_handle, FALSE, nullptr);
const D3D12_VIEWPORT vp{
0.0f, 0.0f, static_cast<float>(m_window_info.surface_width), static_cast<float>(m_window_info.surface_height), 0.0f, 1.0f};
const D3D12_RECT scissor{0, 0, static_cast<LONG>(m_window_info.surface_width), static_cast<LONG>(m_window_info.surface_height)};
cmdlist->RSSetViewports(1, &vp);
cmdlist->RSSetScissorRects(1, &scissor);
return PresentResult::OK;
}
void D3D12HostDisplay::EndPresent()
{
ImGui::Render();
ImGui_ImplDX12_RenderDrawData(ImGui::GetDrawData());
D3D12::Texture& swap_chain_buf = m_swap_chain_buffers[m_current_swap_chain_buffer];
m_current_swap_chain_buffer = ((m_current_swap_chain_buffer + 1) % static_cast<u32>(m_swap_chain_buffers.size()));
swap_chain_buf.TransitionToState(g_d3d12_context->GetCommandList(), D3D12_RESOURCE_STATE_PRESENT);
if (!g_d3d12_context->ExecuteCommandList(D3D12::Context::WaitType::None))
{
m_device_lost = true;
return;
}
const bool vsync = static_cast<UINT>(m_vsync_mode != VsyncMode::Off);
if (!vsync && m_using_allow_tearing)
m_swap_chain->Present(0, DXGI_PRESENT_ALLOW_TEARING);
else
m_swap_chain->Present(static_cast<UINT>(vsync), 0);
}
bool D3D12HostDisplay::SetGPUTimingEnabled(bool enabled)
{
g_d3d12_context->SetEnableGPUTiming(enabled);
return true;
}
float D3D12HostDisplay::GetAndResetAccumulatedGPUTime()
{
return g_d3d12_context->GetAndResetAccumulatedGPUTime();
}
HostDisplay::AdapterAndModeList D3D12HostDisplay::StaticGetAdapterAndModeList()
{
auto factory = D3D::CreateFactory(false);
if (!factory)
return {};
return GetAdapterAndModeList(factory.get());
}
HostDisplay::AdapterAndModeList D3D12HostDisplay::GetAdapterAndModeList(IDXGIFactory5* dxgi_factory)
{
AdapterAndModeList adapter_info;
adapter_info.adapter_names = D3D::GetAdapterNames(dxgi_factory);
auto adapter = D3D::GetChosenOrFirstAdapter(dxgi_factory, EmuConfig.GS.Adapter);
if (adapter)
{
ComPtr<IDXGIOutput> output;
if (SUCCEEDED(adapter->EnumOutputs(0, &output)))
{
UINT num_modes = 0;
if (SUCCEEDED(output->GetDisplayModeList(DXGI_FORMAT_R8G8B8A8_UNORM, 0, &num_modes, nullptr)))
{
std::vector<DXGI_MODE_DESC> modes(num_modes);
if (SUCCEEDED(output->GetDisplayModeList(DXGI_FORMAT_R8G8B8A8_UNORM, 0, &num_modes, modes.data())))
{
for (const DXGI_MODE_DESC& mode : modes)
{
adapter_info.fullscreen_modes.push_back(GetFullscreenModeString(mode.Width, mode.Height,
static_cast<float>(mode.RefreshRate.Numerator) / static_cast<float>(mode.RefreshRate.Denominator)));
}
}
}
}
}
return adapter_info;
}
-101
View File
@@ -1,101 +0,0 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2022 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "common/Pcsx2Defs.h"
#include "common/RedtapeWindows.h"
#include "common/D3D12/DescriptorHeapManager.h"
#include "common/D3D12/Texture.h"
#include "common/WindowInfo.h"
#include "HostDisplay.h"
#include <d3d12.h>
#include <dxgi1_5.h>
#include <memory>
#include <string>
#include <string_view>
#include <vector>
#include <wil/com.h>
class D3D12HostDisplay : public HostDisplay
{
public:
template <typename T>
using ComPtr = wil::com_ptr_nothrow<T>;
D3D12HostDisplay();
~D3D12HostDisplay();
RenderAPI GetRenderAPI() const override;
void* GetDevice() const override;
void* GetContext() const override;
void* GetSurface() const override;
bool HasDevice() const override;
bool HasSurface() const override;
bool CreateDevice(const WindowInfo& wi, VsyncMode vsync) override;
bool SetupDevice() override;
bool MakeCurrent() override;
bool DoneCurrent() override;
bool ChangeWindow(const WindowInfo& new_wi) override;
void ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale) override;
bool SupportsFullscreen() const override;
bool IsFullscreen() override;
bool SetFullscreen(bool fullscreen, u32 width, u32 height, float refresh_rate) override;
AdapterAndModeList GetAdapterAndModeList() override;
void DestroySurface() override;
std::string GetDriverInfo() const override;
std::unique_ptr<HostDisplayTexture> CreateTexture(u32 width, u32 height, const void* data, u32 data_stride, bool dynamic = false) override;
void UpdateTexture(HostDisplayTexture* texture, u32 x, u32 y, u32 width, u32 height, const void* texture_data, u32 texture_data_stride) override;
bool GetHostRefreshRate(float* refresh_rate) override;
void SetVSync(VsyncMode mode) override;
PresentResult BeginPresent(bool frame_skip) override;
void EndPresent() override;
bool SetGPUTimingEnabled(bool enabled) override;
float GetAndResetAccumulatedGPUTime() override;
static AdapterAndModeList StaticGetAdapterAndModeList();
protected:
static AdapterAndModeList GetAdapterAndModeList(IDXGIFactory5* dxgi_factory);
bool CreateImGuiContext() override;
void DestroyImGuiContext() override;
bool UpdateImGuiFontTexture() override;
bool CreateSwapChain(const DXGI_MODE_DESC* fullscreen_mode);
bool CreateSwapChainRTV();
void DestroySwapChainRTVs();
ComPtr<IDXGIFactory5> m_dxgi_factory;
ComPtr<IDXGISwapChain1> m_swap_chain;
std::vector<D3D12::Texture> m_swap_chain_buffers;
u32 m_current_swap_chain_buffer = 0;
bool m_allow_tearing_supported = false;
bool m_using_allow_tearing = false;
bool m_device_lost = false;
};
+67 -58
View File
@@ -37,8 +37,9 @@
#include "CDVD/CDVDcommon.h"
#include "CDVD/CDVDdiscReader.h"
#include "GS.h"
#include "GS/Renderers/Common/GSDevice.h"
#include "GS/Renderers/Common/GSTexture.h"
#include "Host.h"
#include "HostDisplay.h"
#include "HostSettings.h"
#include "INISettingsInterface.h"
#include "MemoryCardFile.h"
@@ -196,6 +197,7 @@ namespace FullscreenUI
//////////////////////////////////////////////////////////////////////////
// Utility
//////////////////////////////////////////////////////////////////////////
static void ReleaseTexture(std::unique_ptr<GSTexture>& tex);
static std::string TimeToPrintableString(time_t t);
static void StartAsyncOp(std::function<void(::ProgressCallback*)> callback, std::string name);
static void AsyncOpThreadEntryPoint(std::function<void(::ProgressCallback*)> callback, FullscreenUI::ProgressCallback* progress);
@@ -244,12 +246,12 @@ namespace FullscreenUI
static bool LoadResources();
static void DestroyResources();
static std::shared_ptr<HostDisplayTexture> s_app_icon_texture;
static std::array<std::shared_ptr<HostDisplayTexture>, static_cast<u32>(GameDatabaseSchema::Compatibility::Perfect)>
static std::shared_ptr<GSTexture> s_app_icon_texture;
static std::array<std::shared_ptr<GSTexture>, static_cast<u32>(GameDatabaseSchema::Compatibility::Perfect)>
s_game_compatibility_textures;
static std::shared_ptr<HostDisplayTexture> s_fallback_disc_texture;
static std::shared_ptr<HostDisplayTexture> s_fallback_exe_texture;
static std::vector<std::unique_ptr<HostDisplayTexture>> s_cleanup_textures;
static std::shared_ptr<GSTexture> s_fallback_disc_texture;
static std::shared_ptr<GSTexture> s_fallback_exe_texture;
static std::vector<std::unique_ptr<GSTexture>> s_cleanup_textures;
//////////////////////////////////////////////////////////////////////////
// Landing
@@ -397,7 +399,7 @@ namespace FullscreenUI
std::string title;
std::string summary;
std::string path;
std::unique_ptr<HostDisplayTexture> preview_texture;
std::unique_ptr<GSTexture> preview_texture;
time_t timestamp;
s32 slot;
};
@@ -433,9 +435,9 @@ namespace FullscreenUI
static void DrawGameListSettingsPage(const ImVec2& heading_size);
static void SwitchToGameList();
static void PopulateGameListEntryList();
static HostDisplayTexture* GetTextureForGameListEntryType(GameList::EntryType type);
static HostDisplayTexture* GetGameListCover(const GameList::Entry* entry);
static HostDisplayTexture* GetCoverForCurrentGame();
static GSTexture* GetTextureForGameListEntryType(GameList::EntryType type);
static GSTexture* GetGameListCover(const GameList::Entry* entry);
static GSTexture* GetCoverForCurrentGame();
// Lazily populated cover images.
static std::unordered_map<std::string, std::string> s_cover_image_map;
@@ -465,6 +467,12 @@ namespace FullscreenUI
// Utility
//////////////////////////////////////////////////////////////////////////
void FullscreenUI::ReleaseTexture(std::unique_ptr<GSTexture>& tex)
{
if (tex)
g_gs_device->Recycle(tex.release());
}
std::string FullscreenUI::TimeToPrintableString(time_t t)
{
struct tm lt = {};
@@ -765,8 +773,8 @@ void FullscreenUI::Render()
if (!s_initialized)
return;
for (std::unique_ptr<HostDisplayTexture>& tex : s_cleanup_textures)
tex.reset();
for (std::unique_ptr<GSTexture>& tex : s_cleanup_textures)
g_gs_device->Recycle(tex.release());
s_cleanup_textures.clear();
ImGuiFullscreen::UploadAsyncTextures();
@@ -883,7 +891,7 @@ void FullscreenUI::DestroyResources()
for (auto& tex : s_game_compatibility_textures)
tex.reset();
for (auto& tex : s_cleanup_textures)
tex.reset();
g_gs_device->Recycle(tex.release());
}
//////////////////////////////////////////////////////////////////////////
@@ -1089,7 +1097,7 @@ void FullscreenUI::DrawLandingWindow()
const float image_size = LayoutScale(380.f);
ImGui::SetCursorPos(
ImVec2((ImGui::GetWindowWidth() * 0.5f) - (image_size * 0.5f), (ImGui::GetWindowHeight() * 0.5f) - (image_size * 0.5f)));
ImGui::Image(s_app_icon_texture->GetHandle(), ImVec2(image_size, image_size));
ImGui::Image(s_app_icon_texture->GetNativeHandle(), ImVec2(image_size, image_size));
}
EndFullscreenColumnWindow();
@@ -2360,10 +2368,7 @@ void FullscreenUI::SwitchToGameSettings(const GameList::Entry* entry)
void FullscreenUI::PopulateGraphicsAdapterList()
{
HostDisplay::AdapterAndModeList ml(g_host_display->GetAdapterAndModeList());
s_graphics_adapter_list_cache = std::move(ml.adapter_names);
s_fullscreen_mode_list_cache = std::move(ml.fullscreen_modes);
s_fullscreen_mode_list_cache.insert(s_fullscreen_mode_list_cache.begin(), "Borderless Fullscreen");
GSGetAdaptersAndFullscreenModes(GSConfig.Renderer, &s_graphics_adapter_list_cache, &s_fullscreen_mode_list_cache);
}
void FullscreenUI::PopulateGameListDirectoryCache(SettingsInterface* si)
@@ -3179,7 +3184,8 @@ void FullscreenUI::DrawGraphicsSettingsPage()
static constexpr const char* s_round_sprite_options[] = {"Off (Default)", "Half", "Full"};
DrawIntListSetting(bsi, "CRC Fix Level", "Applies manual fixes to difficult-to-emulate effects in the hardware renderers.",
"EmuCore/GS", "crc_hack_level", static_cast<int>(CRCHackLevel::Automatic), s_crc_fix_options, std::size(s_crc_fix_options), -1);
"EmuCore/GS", "crc_hack_level", static_cast<int>(CRCHackLevel::Automatic), s_crc_fix_options, std::size(s_crc_fix_options),
-1);
DrawIntListSetting(bsi, "Half-Bottom Override", "Control the half-screen fix detection on texture shuffling.", "EmuCore/GS",
"UserHacks_Half_Bottom_Override", -1, s_generic_options, std::size(s_generic_options), -1);
DrawIntListSetting(bsi, "CPU Sprite Render Size", "Uses software renderer to draw texture decompression-like sprites.",
@@ -3310,7 +3316,8 @@ void FullscreenUI::DrawGraphicsSettingsPage()
"EmuCore/GS", "SkipDuplicateFrames", false);
DrawToggleSetting(bsi, "Disable Threaded Presentation",
"Presents frames on a worker thread, instead of on the GS thread. Can improve frame times on some systems, at the cost of "
"potentially worse frame pacing.", "EmuCore/GS", "DisableThreadedPresentation", false);
"potentially worse frame pacing.",
"EmuCore/GS", "DisableThreadedPresentation", false);
if (hw_fixes_visible)
{
DrawIntListSetting(bsi, "Hardware Download Mode", "Changes synchronization behavior for GS downloads.", "EmuCore/GS",
@@ -3743,9 +3750,8 @@ void FullscreenUI::DrawControllerSettingsPage()
DrawToggleSetting(bsi, ICON_FA_WIFI " SDL DualShock 4 / DualSense Enhanced Mode",
"Provides vibration and LED control support over Bluetooth.", "InputSources", "SDLControllerEnhancedMode", false,
bsi->GetBoolValue("InputSources", "SDL", true), false);
DrawToggleSetting(bsi, ICON_FA_COG " SDL Raw Input",
"Allow SDL to use raw access to input devices.", "InputSources", "SDLRawInput", false,
bsi->GetBoolValue("InputSources", "SDL", true), false);
DrawToggleSetting(bsi, ICON_FA_COG " SDL Raw Input", "Allow SDL to use raw access to input devices.", "InputSources", "SDLRawInput",
false, bsi->GetBoolValue("InputSources", "SDL", true), false);
#endif
#ifdef _WIN32
DrawToggleSetting(bsi, ICON_FA_COG " Enable XInput Input Source",
@@ -4317,13 +4323,13 @@ void FullscreenUI::DrawPauseMenu(MainWindowType type)
#endif
HostDisplayTexture* const cover = GetCoverForCurrentGame();
GSTexture* const cover = GetCoverForCurrentGame();
const ImVec2 image_min(
display_size.x - LayoutScale(10.0f + image_width) - rp_height, display_size.y - LayoutScale(10.0f + image_height) - rp_height);
const ImVec2 image_max(image_min.x + LayoutScale(image_width) + rp_height, image_min.y + LayoutScale(image_height) + rp_height);
const ImRect image_rect(CenterImage(
ImRect(image_min, image_max), ImVec2(static_cast<float>(cover->GetWidth()), static_cast<float>(cover->GetHeight()))));
dl->AddImage(cover->GetHandle(), image_rect.Min, image_rect.Max);
dl->AddImage(cover->GetNativeHandle(), image_rect.Min, image_rect.Max);
}
// current time / play time
@@ -4549,10 +4555,14 @@ bool FullscreenUI::InitializeSaveStateListEntry(
std::vector<u32> screenshot_pixels;
if (SaveState_ReadScreenshot(li->path, &screenshot_width, &screenshot_height, &screenshot_pixels))
{
li->preview_texture = g_host_display->CreateTexture(
screenshot_width, screenshot_height, screenshot_pixels.data(), sizeof(u32) * screenshot_width, false);
if (!li->preview_texture)
li->preview_texture =
std::unique_ptr<GSTexture>(g_gs_device->CreateTexture(screenshot_width, screenshot_height, 1, GSTexture::Format::Color));
if (!li->preview_texture || !li->preview_texture->Update(GSVector4i(0, 0, screenshot_width, screenshot_height),
screenshot_pixels.data(), sizeof(u32) * screenshot_width))
{
Console.Error("Failed to upload save state image to GPU");
ReleaseTexture(li->preview_texture);
}
}
return true;
@@ -4849,12 +4859,11 @@ void FullscreenUI::DrawSaveStateSelector(bool is_loading)
bb.Min += style.FramePadding;
bb.Max -= style.FramePadding;
const HostDisplayTexture* const screenshot =
entry.preview_texture ? entry.preview_texture.get() : GetPlaceholderTexture().get();
const GSTexture* const screenshot = entry.preview_texture ? entry.preview_texture.get() : GetPlaceholderTexture().get();
const ImRect image_rect(CenterImage(ImRect(bb.Min, bb.Min + image_size),
ImVec2(static_cast<float>(screenshot->GetWidth()), static_cast<float>(screenshot->GetHeight()))));
ImGui::GetWindowDrawList()->AddImage(screenshot->GetHandle(), image_rect.Min, image_rect.Max, ImVec2(0.0f, 0.0f),
ImGui::GetWindowDrawList()->AddImage(screenshot->GetNativeHandle(), image_rect.Min, image_rect.Max, ImVec2(0.0f, 0.0f),
ImVec2(1.0f, 1.0f), IM_COL32(255, 255, 255, 255));
const ImVec2 title_pos(bb.Min.x, bb.Min.y + image_height + title_spacing);
@@ -4956,14 +4965,14 @@ void FullscreenUI::DrawResumeStateSelector()
ImGui::TextWrapped("A resume save state created at %s was found.\n\nDo you want to load this save and continue?",
TimeToPrintableString(entry.timestamp).c_str());
const HostDisplayTexture* image = entry.preview_texture ? entry.preview_texture.get() : GetPlaceholderTexture().get();
const GSTexture* image = entry.preview_texture ? entry.preview_texture.get() : GetPlaceholderTexture().get();
const float image_height = LayoutScale(250.0f);
const float image_width = image_height * (static_cast<float>(image->GetWidth()) / static_cast<float>(image->GetHeight()));
const ImVec2 pos(ImGui::GetCursorScreenPos() +
ImVec2((ImGui::GetCurrentWindow()->WorkRect.GetWidth() - image_width) * 0.5f, LayoutScale(20.0f)));
const ImRect image_bb(pos, pos + ImVec2(image_width, image_height));
ImGui::GetWindowDrawList()->AddImage(
static_cast<ImTextureID>(entry.preview_texture ? entry.preview_texture->GetHandle() : GetPlaceholderTexture()->GetHandle()),
ImGui::GetWindowDrawList()->AddImage(static_cast<ImTextureID>(entry.preview_texture ? entry.preview_texture->GetNativeHandle() :
GetPlaceholderTexture()->GetNativeHandle()),
image_bb.Min, image_bb.Max);
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + image_height + LayoutScale(40.0f));
@@ -5226,7 +5235,7 @@ void FullscreenUI::DrawGameList(const ImVec2& heading_size)
if (!visible)
continue;
HostDisplayTexture* cover_texture = GetGameListCover(entry);
GSTexture* cover_texture = GetGameListCover(entry);
summary.clear();
if (entry->serial.empty())
@@ -5240,7 +5249,7 @@ void FullscreenUI::DrawGameList(const ImVec2& heading_size)
const ImRect image_rect(CenterImage(ImRect(bb.Min, bb.Min + image_size),
ImVec2(static_cast<float>(cover_texture->GetWidth()), static_cast<float>(cover_texture->GetHeight()))));
ImGui::GetWindowDrawList()->AddImage(cover_texture->GetHandle(), image_rect.Min, image_rect.Max, ImVec2(0.0f, 0.0f),
ImGui::GetWindowDrawList()->AddImage(cover_texture->GetNativeHandle(), image_rect.Min, image_rect.Max, ImVec2(0.0f, 0.0f),
ImVec2(1.0f, 1.0f), IM_COL32(255, 255, 255, 255));
const float midpoint = bb.Min.y + g_large_font->FontSize + LayoutScale(4.0f);
@@ -5280,7 +5289,7 @@ void FullscreenUI::DrawGameList(const ImVec2& heading_size)
if (BeginFullscreenColumnWindow(-530.0f, 0.0f, "game_list_info", UIPrimaryDarkColor))
{
const HostDisplayTexture* cover_texture =
const GSTexture* cover_texture =
selected_entry ? GetGameListCover(selected_entry) : GetTextureForGameListEntryType(GameList::EntryType::Count);
if (cover_texture)
{
@@ -5288,8 +5297,8 @@ void FullscreenUI::DrawGameList(const ImVec2& heading_size)
ImVec2(static_cast<float>(cover_texture->GetWidth()), static_cast<float>(cover_texture->GetHeight()))));
ImGui::SetCursorPos(LayoutScale(ImVec2(128.0f, 20.0f)) + image_rect.Min);
ImGui::Image(selected_entry ? GetGameListCover(selected_entry)->GetHandle() :
GetTextureForGameListEntryType(GameList::EntryType::Count)->GetHandle(),
ImGui::Image(selected_entry ? GetGameListCover(selected_entry)->GetNativeHandle() :
GetTextureForGameListEntryType(GameList::EntryType::Count)->GetNativeHandle(),
image_rect.GetSize());
}
@@ -5337,7 +5346,7 @@ void FullscreenUI::DrawGameList(const ImVec2& heading_size)
std::string flag_texture(fmt::format("icons/flags/{}.png", GameList::RegionToString(selected_entry->region)));
ImGui::TextUnformatted("Region: ");
ImGui::SameLine();
ImGui::Image(GetCachedTextureAsync(flag_texture.c_str())->GetHandle(), LayoutScale(23.0f, 16.0f));
ImGui::Image(GetCachedTextureAsync(flag_texture.c_str())->GetNativeHandle(), LayoutScale(23.0f, 16.0f));
ImGui::SameLine();
ImGui::Text(" (%s)", GameList::RegionToString(selected_entry->region));
}
@@ -5347,7 +5356,7 @@ void FullscreenUI::DrawGameList(const ImVec2& heading_size)
ImGui::SameLine();
if (selected_entry->compatibility_rating != GameDatabaseSchema::Compatibility::Unknown)
{
ImGui::Image(s_game_compatibility_textures[static_cast<u32>(selected_entry->compatibility_rating) - 1]->GetHandle(),
ImGui::Image(s_game_compatibility_textures[static_cast<u32>(selected_entry->compatibility_rating) - 1]->GetNativeHandle(),
LayoutScale(64.0f, 16.0f));
ImGui::SameLine();
}
@@ -5451,11 +5460,11 @@ void FullscreenUI::DrawGameGrid(const ImVec2& heading_size)
bb.Min += style.FramePadding;
bb.Max -= style.FramePadding;
const HostDisplayTexture* const cover_texture = GetGameListCover(entry);
const GSTexture* const cover_texture = GetGameListCover(entry);
const ImRect image_rect(CenterImage(ImRect(bb.Min, bb.Min + image_size),
ImVec2(static_cast<float>(cover_texture->GetWidth()), static_cast<float>(cover_texture->GetHeight()))));
ImGui::GetWindowDrawList()->AddImage(cover_texture->GetHandle(), image_rect.Min, image_rect.Max, ImVec2(0.0f, 0.0f),
ImGui::GetWindowDrawList()->AddImage(cover_texture->GetNativeHandle(), image_rect.Min, image_rect.Max, ImVec2(0.0f, 0.0f),
ImVec2(1.0f, 1.0f), IM_COL32(255, 255, 255, 255));
const ImRect title_bb(ImVec2(bb.Min.x, bb.Min.y + image_height + title_spacing), bb.Max);
@@ -5777,7 +5786,7 @@ void FullscreenUI::SwitchToGameList()
QueueResetFocus();
}
HostDisplayTexture* FullscreenUI::GetGameListCover(const GameList::Entry* entry)
GSTexture* FullscreenUI::GetGameListCover(const GameList::Entry* entry)
{
// lookup and grab cover image
auto cover_it = s_cover_image_map.find(entry->path);
@@ -5787,11 +5796,11 @@ HostDisplayTexture* FullscreenUI::GetGameListCover(const GameList::Entry* entry)
cover_it = s_cover_image_map.emplace(entry->path, std::move(cover_path)).first;
}
HostDisplayTexture* tex = (!cover_it->second.empty()) ? GetCachedTextureAsync(cover_it->second.c_str()) : nullptr;
GSTexture* tex = (!cover_it->second.empty()) ? GetCachedTextureAsync(cover_it->second.c_str()) : nullptr;
return tex ? tex : GetTextureForGameListEntryType(entry->type);
}
HostDisplayTexture* FullscreenUI::GetTextureForGameListEntryType(GameList::EntryType type)
GSTexture* FullscreenUI::GetTextureForGameListEntryType(GameList::EntryType type)
{
switch (type)
{
@@ -5805,7 +5814,7 @@ HostDisplayTexture* FullscreenUI::GetTextureForGameListEntryType(GameList::Entry
}
}
HostDisplayTexture* FullscreenUI::GetCoverForCurrentGame()
GSTexture* FullscreenUI::GetCoverForCurrentGame()
{
auto lock = GameList::GetLock();
@@ -6064,11 +6073,11 @@ void FullscreenUI::DrawAchievement(const Achievements::Achievement& cheevo)
const std::string& badge_path = Achievements::GetAchievementBadgePath(cheevo);
if (!badge_path.empty())
{
HostDisplayTexture* badge = GetCachedTextureAsync(badge_path.c_str());
GSTexture* badge = GetCachedTextureAsync(badge_path.c_str());
if (badge)
{
ImGui::GetWindowDrawList()->AddImage(
badge->GetHandle(), bb.Min, bb.Min + image_size, ImVec2(0.0f, 0.0f), ImVec2(1.0f, 1.0f), IM_COL32(255, 255, 255, 255));
ImGui::GetWindowDrawList()->AddImage(badge->GetNativeHandle(), bb.Min, bb.Min + image_size, ImVec2(0.0f, 0.0f),
ImVec2(1.0f, 1.0f), IM_COL32(255, 255, 255, 255));
}
}
@@ -6183,11 +6192,11 @@ void FullscreenUI::DrawAchievementsWindow()
const std::string& icon_path = Achievements::GetGameIcon();
if (!icon_path.empty())
{
HostDisplayTexture* badge = GetCachedTexture(icon_path.c_str());
GSTexture* badge = GetCachedTexture(icon_path.c_str());
if (badge)
{
ImGui::GetWindowDrawList()->AddImage(
badge->GetHandle(), icon_min, icon_max, ImVec2(0.0f, 0.0f), ImVec2(1.0f, 1.0f), IM_COL32(255, 255, 255, 255));
badge->GetNativeHandle(), icon_min, icon_max, ImVec2(0.0f, 0.0f), ImVec2(1.0f, 1.0f), IM_COL32(255, 255, 255, 255));
}
}
@@ -6318,12 +6327,12 @@ void FullscreenUI::DrawPrimedAchievementsIcons()
if (badge_path.empty())
return true;
HostDisplayTexture* badge = GetCachedTextureAsync(badge_path.c_str());
GSTexture* badge = GetCachedTextureAsync(badge_path.c_str());
if (!badge)
return true;
ImDrawList* dl = ImGui::GetBackgroundDrawList();
dl->AddImage(badge->GetHandle(), position, position + image_size);
dl->AddImage(badge->GetNativeHandle(), position, position + image_size);
position.x -= x_advance;
return true;
});
@@ -6367,12 +6376,12 @@ void FullscreenUI::DrawPrimedAchievementsList()
if (badge_path.empty())
return true;
HostDisplayTexture* badge = GetCachedTextureAsync(badge_path.c_str());
GSTexture* badge = GetCachedTextureAsync(badge_path.c_str());
if (!badge)
return true;
ImDrawList* dl = ImGui::GetBackgroundDrawList();
dl->AddImage(badge->GetHandle(), position, position + image_size);
dl->AddImage(badge->GetNativeHandle(), position, position + image_size);
const char* achievement_title = achievement.title.c_str();
const char* achievement_tile_end = achievement_title + achievement.title.length();
@@ -6580,11 +6589,11 @@ void FullscreenUI::DrawLeaderboardsWindow()
const std::string& icon_path = Achievements::GetGameIcon();
if (!icon_path.empty())
{
HostDisplayTexture* badge = GetCachedTexture(icon_path.c_str());
GSTexture* badge = GetCachedTexture(icon_path.c_str());
if (badge)
{
ImGui::GetWindowDrawList()->AddImage(
badge->GetHandle(), icon_min, icon_max, ImVec2(0.0f, 0.0f), ImVec2(1.0f, 1.0f), IM_COL32(255, 255, 255, 255));
badge->GetNativeHandle(), icon_min, icon_max, ImVec2(0.0f, 0.0f), ImVec2(1.0f, 1.0f), IM_COL32(255, 255, 255, 255));
}
}
-2
View File
@@ -19,8 +19,6 @@
#include <string>
#include <memory>
class HostDisplayTexture;
struct Pcsx2Config;
namespace FullscreenUI
+27 -19
View File
@@ -29,7 +29,9 @@
#include "common/Threading.h"
#include "common/Timer.h"
#include "fmt/core.h"
#include "HostDisplay.h"
#include "Host.h"
#include "GS/Renderers/Common/GSDevice.h"
#include "GS/Renderers/Common/GSTexture.h"
#include "imgui_internal.h"
#include "imgui_stdlib.h"
#include <array>
@@ -43,7 +45,7 @@ namespace ImGuiFullscreen
using MessageDialogCallbackVariant = std::variant<InfoMessageDialogCallback, ConfirmMessageDialogCallback>;
static std::optional<Common::RGBA8Image> LoadTextureImage(const char* path);
static std::shared_ptr<HostDisplayTexture> UploadTexture(const char* path, const Common::RGBA8Image& image);
static std::shared_ptr<GSTexture> UploadTexture(const char* path, const Common::RGBA8Image& image);
static void TextureLoaderThread();
static void DrawFileSelector();
@@ -89,8 +91,8 @@ namespace ImGuiFullscreen
static u32 s_close_button_state = 0;
static bool s_focus_reset_queued = false;
static LRUCache<std::string, std::shared_ptr<HostDisplayTexture>> s_texture_cache(128, true);
static std::shared_ptr<HostDisplayTexture> s_placeholder_texture;
static LRUCache<std::string, std::shared_ptr<GSTexture>> s_texture_cache(128, true);
static std::shared_ptr<GSTexture> s_placeholder_texture;
static std::atomic_bool s_texture_load_thread_quit{false};
static std::mutex s_texture_load_mutex;
static std::condition_variable s_texture_load_cv;
@@ -247,7 +249,7 @@ void ImGuiFullscreen::Shutdown(bool clear_state)
}
}
const std::shared_ptr<HostDisplayTexture>& ImGuiFullscreen::GetPlaceholderTexture()
const std::shared_ptr<GSTexture>& ImGuiFullscreen::GetPlaceholderTexture()
{
return s_placeholder_texture;
}
@@ -278,26 +280,32 @@ std::optional<Common::RGBA8Image> ImGuiFullscreen::LoadTextureImage(const char*
return image;
}
std::shared_ptr<HostDisplayTexture> ImGuiFullscreen::UploadTexture(const char* path, const Common::RGBA8Image& image)
std::shared_ptr<GSTexture> ImGuiFullscreen::UploadTexture(const char* path, const Common::RGBA8Image& image)
{
std::unique_ptr<HostDisplayTexture> texture =
g_host_display->CreateTexture(image.GetWidth(), image.GetHeight(), image.GetPixels(), image.GetByteStride());
GSTexture* texture = g_gs_device->CreateTexture(image.GetWidth(), image.GetHeight(), 1, GSTexture::Format::Color);
if (!texture)
{
Console.Error("failed to create %ux%u texture for resource", image.GetWidth(), image.GetHeight());
return {};
}
if (!texture->Update(GSVector4i(0, 0, image.GetWidth(), image.GetHeight()), image.GetPixels(), image.GetByteStride()))
{
Console.Error("Failed to upload %ux%u texture for resource", image.GetWidth(), image.GetHeight());
g_gs_device->Recycle(texture);
return {};
}
DevCon.WriteLn("Uploaded texture resource '%s' (%ux%u)", path, image.GetWidth(), image.GetHeight());
return std::shared_ptr<HostDisplayTexture>(std::move(texture));
return std::shared_ptr<GSTexture>(texture, [](GSTexture* tex) { g_gs_device->Recycle(tex); });
}
std::shared_ptr<HostDisplayTexture> ImGuiFullscreen::LoadTexture(const char* path)
std::shared_ptr<GSTexture> ImGuiFullscreen::LoadTexture(const char* path)
{
std::optional<Common::RGBA8Image> image(LoadTextureImage(path));
if (image.has_value())
{
std::shared_ptr<HostDisplayTexture> ret(UploadTexture(path, image.value()));
std::shared_ptr<GSTexture> ret(UploadTexture(path, image.value()));
if (ret)
return ret;
}
@@ -305,21 +313,21 @@ std::shared_ptr<HostDisplayTexture> ImGuiFullscreen::LoadTexture(const char* pat
return s_placeholder_texture;
}
HostDisplayTexture* ImGuiFullscreen::GetCachedTexture(const char* name)
GSTexture* ImGuiFullscreen::GetCachedTexture(const char* name)
{
std::shared_ptr<HostDisplayTexture>* tex_ptr = s_texture_cache.Lookup(name);
std::shared_ptr<GSTexture>* tex_ptr = s_texture_cache.Lookup(name);
if (!tex_ptr)
{
std::shared_ptr<HostDisplayTexture> tex(LoadTexture(name));
std::shared_ptr<GSTexture> tex(LoadTexture(name));
tex_ptr = s_texture_cache.Insert(name, std::move(tex));
}
return tex_ptr->get();
}
HostDisplayTexture* ImGuiFullscreen::GetCachedTextureAsync(const char* name)
GSTexture* ImGuiFullscreen::GetCachedTextureAsync(const char* name)
{
std::shared_ptr<HostDisplayTexture>* tex_ptr = s_texture_cache.Lookup(name);
std::shared_ptr<GSTexture>* tex_ptr = s_texture_cache.Lookup(name);
if (!tex_ptr)
{
// insert the placeholder
@@ -348,7 +356,7 @@ void ImGuiFullscreen::UploadAsyncTextures()
s_texture_upload_queue.pop_front();
lock.unlock();
std::shared_ptr<HostDisplayTexture> tex = UploadTexture(it.first.c_str(), it.second);
std::shared_ptr<GSTexture> tex = UploadTexture(it.first.c_str(), it.second);
if (tex)
s_texture_cache.Insert(std::move(it.first), std::move(tex));
@@ -2378,9 +2386,9 @@ void ImGuiFullscreen::DrawNotifications(ImVec2& position, float spacing)
const ImVec2 badge_max(badge_min.x + badge_size, badge_min.y + badge_size);
if (!notif.badge_path.empty())
{
HostDisplayTexture* tex = GetCachedTexture(notif.badge_path.c_str());
GSTexture* tex = GetCachedTexture(notif.badge_path.c_str());
if (tex)
dl->AddImage(static_cast<ImTextureID>(tex->GetHandle()), badge_min, badge_max);
dl->AddImage(tex->GetNativeHandle(), badge_min, badge_max);
}
const ImVec2 title_min(badge_max.x + horizontal_spacing, box_min.y + vertical_padding);
+5 -5
View File
@@ -25,7 +25,7 @@
#include <string_view>
#include <vector>
class HostDisplayTexture;
class GSTexture;
namespace ImGuiFullscreen
{
@@ -116,10 +116,10 @@ namespace ImGuiFullscreen
void Shutdown(bool clear_state);
/// Texture cache.
const std::shared_ptr<HostDisplayTexture>& GetPlaceholderTexture();
std::shared_ptr<HostDisplayTexture> LoadTexture(const char* path);
HostDisplayTexture* GetCachedTexture(const char* name);
HostDisplayTexture* GetCachedTextureAsync(const char* name);
const std::shared_ptr<GSTexture>& GetPlaceholderTexture();
std::shared_ptr<GSTexture> LoadTexture(const char* path);
GSTexture* GetCachedTexture(const char* name);
GSTexture* GetCachedTextureAsync(const char* name);
bool InvalidateCachedTexture(const std::string& path);
void UploadAsyncTextures();
+15 -25
View File
@@ -37,8 +37,8 @@
#include "Frontend/InputManager.h"
#include "GS.h"
#include "GS/GS.h"
#include "GS/Renderers/Common/GSDevice.h"
#include "Host.h"
#include "HostDisplay.h"
#include "IconsFontAwesome5.h"
#include "PerformanceMetrics.h"
#include "Recording/InputRecording.h"
@@ -90,40 +90,32 @@ bool ImGuiManager::Initialize()
return false;
}
s_global_scale = std::max(0.5f, g_host_display->GetWindowScale() * (EmuConfig.GS.OsdScale / 100.0f));
s_global_scale = std::max(0.5f, g_gs_device->GetWindowScale() * (GSConfig.OsdScale / 100.0f));
ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO();
io.IniFilename = nullptr;
io.BackendFlags |= ImGuiBackendFlags_HasGamepad;
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset | ImGuiBackendFlags_HasGamepad;
io.BackendUsingLegacyKeyArrays = 0;
io.BackendUsingLegacyNavInputArray = 0;
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard | ImGuiConfigFlags_NavEnableGamepad;
io.DisplayFramebufferScale = ImVec2(1, 1); // We already scale things ourselves, this would double-apply scaling
io.DisplaySize.x = static_cast<float>(g_host_display->GetWindowWidth());
io.DisplaySize.y = static_cast<float>(g_host_display->GetWindowHeight());
io.DisplaySize.x = static_cast<float>(g_gs_device->GetWindowWidth());
io.DisplaySize.y = static_cast<float>(g_gs_device->GetWindowHeight());
SetKeyMap();
SetStyle();
const bool add_fullscreen_fonts = s_fullscreen_ui_was_initialized;
pxAssertRel(!FullscreenUI::IsInitialized(), "Fullscreen UI is not initialized on ImGui init");
if (add_fullscreen_fonts)
ImGuiFullscreen::UpdateLayoutScale();
if (!g_host_display->CreateImGuiContext())
if (!AddImGuiFonts(add_fullscreen_fonts) || !g_gs_device->UpdateImGuiFontTexture())
{
pxFailRel("Failed to create ImGui device context");
g_host_display->DestroyImGuiContext();
ImGui::DestroyContext();
UnloadFontData();
return false;
}
if (!AddImGuiFonts(add_fullscreen_fonts) || !g_host_display->UpdateImGuiFontTexture())
{
pxFailRel("Failed to create ImGui font text");
g_host_display->DestroyImGuiContext();
Host::ReportErrorAsync("ImGuiManager", "Failed to create ImGui font text");
ImGui::DestroyContext();
UnloadFontData();
return false;
@@ -143,7 +135,7 @@ bool ImGuiManager::Initialize()
bool ImGuiManager::InitializeFullscreenUI()
{
s_fullscreen_ui_was_initialized = FullscreenUI::Initialize();
s_fullscreen_ui_was_initialized = !ImGui::GetCurrentContext() || FullscreenUI::Initialize();
return s_fullscreen_ui_was_initialized;
}
@@ -154,8 +146,6 @@ void ImGuiManager::Shutdown(bool clear_state)
if (clear_state)
s_fullscreen_ui_was_initialized = false;
if (g_host_display)
g_host_display->DestroyImGuiContext();
if (ImGui::GetCurrentContext())
ImGui::DestroyContext();
@@ -170,8 +160,8 @@ void ImGuiManager::Shutdown(bool clear_state)
void ImGuiManager::WindowResized()
{
const u32 new_width = g_host_display ? g_host_display->GetWindowWidth() : 0;
const u32 new_height = g_host_display ? g_host_display->GetWindowHeight() : 0;
const u32 new_width = g_gs_device ? g_gs_device->GetWindowWidth() : 0;
const u32 new_height = g_gs_device ? g_gs_device->GetWindowHeight() : 0;
ImGui::GetIO().DisplaySize = ImVec2(static_cast<float>(new_width), static_cast<float>(new_height));
@@ -184,7 +174,7 @@ void ImGuiManager::WindowResized()
void ImGuiManager::UpdateScale()
{
const float window_scale = g_host_display ? g_host_display->GetWindowScale() : 1.0f;
const float window_scale = g_gs_device ? g_gs_device->GetWindowScale() : 1.0f;
const float scale = std::max(window_scale * (EmuConfig.GS.OsdScale / 100.0f), 0.5f);
if (scale == s_global_scale && (!HasFullscreenFonts() || !ImGuiFullscreen::UpdateLayoutScale()))
@@ -199,7 +189,7 @@ void ImGuiManager::UpdateScale()
if (!AddImGuiFonts(HasFullscreenFonts()))
pxFailRel("Failed to create ImGui font text");
if (!g_host_display->UpdateImGuiFontTexture())
if (!g_gs_device->UpdateImGuiFontTexture())
pxFailRel("Failed to recreate font texture after scale+resize");
NewFrame();
@@ -483,7 +473,7 @@ bool ImGuiManager::AddFullscreenFontsIfMissing()
AddImGuiFonts(false);
}
g_host_display->UpdateImGuiFontTexture();
g_gs_device->UpdateImGuiFontTexture();
NewFrame();
return HasFullscreenFonts();
-1
View File
@@ -43,7 +43,6 @@
#include "GS/GSCapture.h"
#include "GS/GSVector.h"
#include "Host.h"
#include "HostDisplay.h"
#include "IconsFontAwesome5.h"
#include "PerformanceMetrics.h"
#include "PAD/Host/PAD.h"
-99
View File
@@ -1,99 +0,0 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2022 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "HostDisplay.h"
#ifndef __OBJC__
#error "This header is for use with Objective-C++ only.
#endif
#ifdef __APPLE__
#include "GS/Renderers/Metal/GSMTLDeviceInfo.h"
#include <mutex>
#include <AppKit/AppKit.h>
#include <Metal/Metal.h>
#include <QuartzCore/QuartzCore.h>
class MetalHostDisplay final : public HostDisplay
{
enum class UsePresentDrawable : u8
{
Never = 0,
Always = 1,
IfVsync = 2,
};
MRCOwned<NSView*> m_view;
MRCOwned<CAMetalLayer*> m_layer;
GSMTLDevice m_dev;
MRCOwned<id<MTLCommandQueue>> m_queue;
MRCOwned<id<MTLTexture>> m_font_tex;
MRCOwned<id<CAMetalDrawable>> m_current_drawable;
MRCOwned<MTLRenderPassDescriptor*> m_pass_desc;
u32 m_capture_start_frame;
UsePresentDrawable m_use_present_drawable;
bool m_gpu_timing_enabled = false;
double m_accumulated_gpu_time = 0;
double m_last_gpu_time_end = 0;
std::mutex m_mtx;
void AttachSurfaceOnMainThread();
void DetachSurfaceOnMainThread();
public:
MetalHostDisplay();
~MetalHostDisplay();
RenderAPI GetRenderAPI() const override;
void* GetDevice() const override;
void* GetContext() const override;
void* GetSurface() const override;
bool HasDevice() const override;
bool HasSurface() const override;
bool CreateDevice(const WindowInfo& wi, VsyncMode vsync) override;
bool SetupDevice() override;
bool MakeCurrent() override;
bool DoneCurrent() override;
void DestroySurface() override;
bool ChangeWindow(const WindowInfo& wi) override;
bool SupportsFullscreen() const override;
bool IsFullscreen() override;
bool SetFullscreen(bool fullscreen, u32 width, u32 height, float refresh_rate) override;
AdapterAndModeList GetAdapterAndModeList() override;
std::string GetDriverInfo() const override;
void ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale) override;
std::unique_ptr<HostDisplayTexture> CreateTexture(u32 width, u32 height, const void* data, u32 data_stride, bool dynamic = false) override;
void UpdateTexture(id<MTLTexture> texture, u32 x, u32 y, u32 width, u32 height, const void* data, u32 data_stride);
void UpdateTexture(HostDisplayTexture* texture, u32 x, u32 y, u32 width, u32 height, const void* data, u32 data_stride) override;
PresentResult BeginPresent(bool frame_skip) override;
void EndPresent() override;
void SetVSync(VsyncMode mode) override;
bool CreateImGuiContext() override;
void DestroyImGuiContext() override;
bool UpdateImGuiFontTexture() override;
bool GetHostRefreshRate(float* refresh_rate) override;
bool SetGPUTimingEnabled(bool enabled) override;
float GetAndResetAccumulatedGPUTime() override;
void AccumulateCommandBufferTime(id<MTLCommandBuffer> buffer);
};
#endif
-467
View File
@@ -1,467 +0,0 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2022 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "PrecompiledHeader.h"
#include "MetalHostDisplay.h"
#include "GS/Renderers/Metal/GSMetalCPPAccessible.h"
#include "GS/Renderers/Metal/GSDeviceMTL.h"
#include <imgui.h>
#ifdef __APPLE__
class MetalHostDisplayTexture final : public HostDisplayTexture
{
MRCOwned<id<MTLTexture>> m_tex;
u32 m_width, m_height;
public:
MetalHostDisplayTexture(MRCOwned<id<MTLTexture>> tex, u32 width, u32 height)
: m_tex(std::move(tex))
, m_width(width)
, m_height(height)
{
}
void* GetHandle() const override { return (__bridge void*)m_tex; };
u32 GetWidth() const override { return m_width; }
u32 GetHeight() const override { return m_height; }
};
HostDisplay* MakeMetalHostDisplay()
{
return new MetalHostDisplay();
}
MetalHostDisplay::MetalHostDisplay()
{
}
MetalHostDisplay::~MetalHostDisplay()
{
MetalHostDisplay::DestroySurface();
m_queue = nullptr;
m_dev.Reset();
}
HostDisplay::AdapterAndModeList GetMetalAdapterAndModeList()
{ @autoreleasepool {
HostDisplay::AdapterAndModeList list;
auto devs = MRCTransfer(MTLCopyAllDevices());
for (id<MTLDevice> dev in devs.Get())
list.adapter_names.push_back([[dev name] UTF8String]);
return list;
}}
template <typename Fn>
static void OnMainThread(Fn&& fn)
{
if ([NSThread isMainThread])
fn();
else
dispatch_sync(dispatch_get_main_queue(), fn);
}
RenderAPI MetalHostDisplay::GetRenderAPI() const
{
return RenderAPI::Metal;
}
void* MetalHostDisplay::GetDevice() const { return const_cast<void*>(static_cast<const void*>(&m_dev)); }
void* MetalHostDisplay::GetContext() const { return (__bridge void*)m_queue; }
void* MetalHostDisplay::GetSurface() const { return (__bridge void*)m_layer; }
bool MetalHostDisplay::HasDevice() const { return m_dev.IsOk(); }
bool MetalHostDisplay::HasSurface() const { return static_cast<bool>(m_layer);}
void MetalHostDisplay::AttachSurfaceOnMainThread()
{
ASSERT([NSThread isMainThread]);
m_layer = MRCRetain([CAMetalLayer layer]);
[m_layer setDrawableSize:CGSizeMake(m_window_info.surface_width, m_window_info.surface_height)];
[m_layer setDevice:m_dev.dev];
m_view = MRCRetain((__bridge NSView*)m_window_info.window_handle);
[m_view setWantsLayer:YES];
[m_view setLayer:m_layer];
}
void MetalHostDisplay::DetachSurfaceOnMainThread()
{
ASSERT([NSThread isMainThread]);
[m_view setLayer:nullptr];
[m_view setWantsLayer:NO];
m_view = nullptr;
m_layer = nullptr;
}
bool MetalHostDisplay::CreateDevice(const WindowInfo& wi, VsyncMode vsync)
{ @autoreleasepool {
m_window_info = wi;
pxAssertRel(!m_dev.dev, "Device already created!");
NSString* ns_adapter_name = [NSString stringWithUTF8String:EmuConfig.GS.Adapter.c_str()];
auto devs = MRCTransfer(MTLCopyAllDevices());
for (id<MTLDevice> dev in devs.Get())
{
if ([[dev name] isEqualToString:ns_adapter_name])
m_dev = GSMTLDevice(MRCRetain(dev));
}
if (!m_dev.dev)
{
if (!EmuConfig.GS.Adapter.empty())
Console.Warning("Metal: Couldn't find adapter %s, using default", EmuConfig.GS.Adapter.c_str());
m_dev = GSMTLDevice(MRCTransfer(MTLCreateSystemDefaultDevice()));
if (!m_dev.dev)
Host::ReportErrorAsync("No Metal Devices Available", "No Metal-supporting GPUs were found. PCSX2 requires a Metal GPU (available on all macs from 2012 onwards).");
}
m_queue = MRCTransfer([m_dev.dev newCommandQueue]);
m_pass_desc = MRCTransfer([MTLRenderPassDescriptor new]);
[m_pass_desc colorAttachments][0].loadAction = MTLLoadActionClear;
[m_pass_desc colorAttachments][0].clearColor = MTLClearColorMake(0, 0, 0, 0);
[m_pass_desc colorAttachments][0].storeAction = MTLStoreActionStore;
if (char* env = getenv("MTL_USE_PRESENT_DRAWABLE"))
m_use_present_drawable = static_cast<UsePresentDrawable>(atoi(env));
else if (@available(macOS 13.0, *))
m_use_present_drawable = UsePresentDrawable::Always;
else // Before Ventura, presentDrawable acts like vsync is on when windowed
m_use_present_drawable = UsePresentDrawable::IfVsync;
m_capture_start_frame = 0;
if (char* env = getenv("MTL_CAPTURE"))
{
m_capture_start_frame = atoi(env);
}
if (m_capture_start_frame)
{
Console.WriteLn("Metal will capture frame %u", m_capture_start_frame);
}
if (m_dev.IsOk() && m_queue)
{
OnMainThread([this]
{
AttachSurfaceOnMainThread();
});
SetVSync(vsync);
return true;
}
else
return false;
}}
bool MetalHostDisplay::SetupDevice()
{
return true;
}
bool MetalHostDisplay::MakeCurrent() { return true; }
bool MetalHostDisplay::DoneCurrent() { return true; }
void MetalHostDisplay::DestroySurface()
{
if (!m_layer)
return;
OnMainThread([this]{ DetachSurfaceOnMainThread(); });
m_layer = nullptr;
}
bool MetalHostDisplay::ChangeWindow(const WindowInfo& wi)
{
OnMainThread([this, &wi]
{
DetachSurfaceOnMainThread();
m_window_info = wi;
AttachSurfaceOnMainThread();
});
return true;
}
bool MetalHostDisplay::SupportsFullscreen() const { return false; }
bool MetalHostDisplay::IsFullscreen() { return false; }
bool MetalHostDisplay::SetFullscreen(bool fullscreen, u32 width, u32 height, float refresh_rate) { return false; }
HostDisplay::AdapterAndModeList MetalHostDisplay::GetAdapterAndModeList()
{
return GetMetalAdapterAndModeList();
}
std::string MetalHostDisplay::GetDriverInfo() const
{ @autoreleasepool {
std::string desc([[m_dev.dev description] UTF8String]);
desc += "\n Texture Swizzle: " + std::string(m_dev.features.texture_swizzle ? "Supported" : "Unsupported");
desc += "\n Unified Memory: " + std::string(m_dev.features.unified_memory ? "Supported" : "Unsupported");
desc += "\n Framebuffer Fetch: " + std::string(m_dev.features.framebuffer_fetch ? "Supported" : "Unsupported");
desc += "\n Primitive ID: " + std::string(m_dev.features.primid ? "Supported" : "Unsupported");
desc += "\n Shader Version: " + std::string(to_string(m_dev.features.shader_version));
desc += "\n Max Texture Size: " + std::to_string(m_dev.features.max_texsize);
return desc;
}}
void MetalHostDisplay::ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale)
{
m_window_info.surface_scale = new_window_scale;
if (m_window_info.surface_width == static_cast<u32>(new_window_width) && m_window_info.surface_height == static_cast<u32>(new_window_height))
return;
m_window_info.surface_width = new_window_width;
m_window_info.surface_height = new_window_height;
@autoreleasepool
{
[m_layer setDrawableSize:CGSizeMake(new_window_width, new_window_height)];
}
}
std::unique_ptr<HostDisplayTexture> MetalHostDisplay::CreateTexture(u32 width, u32 height, const void* data, u32 data_stride, bool dynamic)
{ @autoreleasepool {
MTLTextureDescriptor* desc = [MTLTextureDescriptor
texture2DDescriptorWithPixelFormat:MTLPixelFormatRGBA8Unorm
width:width
height:height
mipmapped:false];
[desc setUsage:MTLTextureUsageShaderRead];
[desc setStorageMode:MTLStorageModePrivate];
MRCOwned<id<MTLTexture>> tex = MRCTransfer([m_dev.dev newTextureWithDescriptor:desc]);
if (!tex)
return nullptr; // Something broke yay
[tex setLabel:@"MetalHostDisplay Texture"];
if (data)
UpdateTexture(tex, 0, 0, width, height, data, data_stride);
return std::make_unique<MetalHostDisplayTexture>(std::move(tex), width, height);
}}
void MetalHostDisplay::UpdateTexture(id<MTLTexture> texture, u32 x, u32 y, u32 width, u32 height, const void* data, u32 data_stride)
{
id<MTLCommandBuffer> cmdbuf = [m_queue commandBuffer];
id<MTLBlitCommandEncoder> enc = [cmdbuf blitCommandEncoder];
size_t bytes = data_stride * height;
MRCOwned<id<MTLBuffer>> buf = MRCTransfer([m_dev.dev newBufferWithLength:bytes options:MTLResourceStorageModeShared | MTLResourceCPUCacheModeWriteCombined]);
memcpy([buf contents], data, bytes);
[enc copyFromBuffer:buf
sourceOffset:0
sourceBytesPerRow:data_stride
sourceBytesPerImage:bytes
sourceSize:MTLSizeMake(width, height, 1)
toTexture:texture
destinationSlice:0
destinationLevel:0
destinationOrigin:MTLOriginMake(0, 0, 0)];
[enc endEncoding];
[cmdbuf commit];
}
void MetalHostDisplay::UpdateTexture(HostDisplayTexture* texture, u32 x, u32 y, u32 width, u32 height, const void* data, u32 data_stride)
{ @autoreleasepool {
UpdateTexture((__bridge id<MTLTexture>)texture->GetHandle(), x, y, width, height, data, data_stride);
}}
static bool s_capture_next = false;
HostDisplay::PresentResult MetalHostDisplay::BeginPresent(bool frame_skip)
{ @autoreleasepool {
GSDeviceMTL* dev = static_cast<GSDeviceMTL*>(g_gs_device.get());
if (dev && m_capture_start_frame && dev->FrameNo() == m_capture_start_frame)
s_capture_next = true;
if (frame_skip || m_window_info.type == WindowInfo::Type::Surfaceless || !g_gs_device)
{
ImGui::EndFrame();
return PresentResult::FrameSkipped;
}
id<MTLCommandBuffer> buf = dev->GetRenderCmdBuf();
m_current_drawable = MRCRetain([m_layer nextDrawable]);
dev->EndRenderPass();
if (!m_current_drawable)
{
[buf pushDebugGroup:@"Present Skipped"];
[buf popDebugGroup];
dev->FlushEncoders();
ImGui::EndFrame();
return PresentResult::FrameSkipped;
}
[m_pass_desc colorAttachments][0].texture = [m_current_drawable texture];
id<MTLRenderCommandEncoder> enc = [buf renderCommandEncoderWithDescriptor:m_pass_desc];
[enc setLabel:@"Present"];
dev->m_current_render.encoder = MRCRetain(enc);
return PresentResult::OK;
}}
void MetalHostDisplay::EndPresent()
{ @autoreleasepool {
GSDeviceMTL* dev = static_cast<GSDeviceMTL*>(g_gs_device.get());
pxAssertDev(dev && dev->m_current_render.encoder && dev->m_current_render_cmdbuf, "BeginPresent cmdbuf was destroyed");
ImGui::Render();
dev->RenderImGui(ImGui::GetDrawData());
dev->EndRenderPass();
if (m_current_drawable)
{
const bool use_present_drawable = m_use_present_drawable == UsePresentDrawable::Always ||
(m_use_present_drawable == UsePresentDrawable::IfVsync && m_vsync_mode != VsyncMode::Off);
if (use_present_drawable)
[dev->m_current_render_cmdbuf presentDrawable:m_current_drawable];
else
[dev->m_current_render_cmdbuf addScheduledHandler:[drawable = std::move(m_current_drawable)](id<MTLCommandBuffer>){
[drawable present];
}];
}
dev->FlushEncoders();
dev->FrameCompleted();
m_current_drawable = nullptr;
if (m_capture_start_frame)
{
if (@available(macOS 10.15, iOS 13, *))
{
static NSString* const path = @"/tmp/PCSX2MTLCapture.gputrace";
static u32 frames;
if (frames)
{
--frames;
if (!frames)
{
[[MTLCaptureManager sharedCaptureManager] stopCapture];
Console.WriteLn("Metal Trace Capture to /tmp/PCSX2MTLCapture.gputrace finished");
[[NSWorkspace sharedWorkspace] selectFile:path
inFileViewerRootedAtPath:@"/tmp/"];
}
}
else if (s_capture_next)
{
s_capture_next = false;
MTLCaptureManager* mgr = [MTLCaptureManager sharedCaptureManager];
if ([mgr supportsDestination:MTLCaptureDestinationGPUTraceDocument])
{
MTLCaptureDescriptor* desc = [[MTLCaptureDescriptor new] autorelease];
[desc setCaptureObject:m_dev.dev];
if ([[NSFileManager defaultManager] fileExistsAtPath:path])
[[NSFileManager defaultManager] removeItemAtPath:path error:nil];
[desc setOutputURL:[NSURL fileURLWithPath:path]];
[desc setDestination:MTLCaptureDestinationGPUTraceDocument];
NSError* err = nullptr;
[mgr startCaptureWithDescriptor:desc error:&err];
if (err)
{
Console.Error("Metal Trace Capture failed: %s", [[err localizedDescription] UTF8String]);
}
else
{
Console.WriteLn("Metal Trace Capture to /tmp/PCSX2MTLCapture.gputrace started");
frames = 2;
}
}
else
{
Console.Error("Metal Trace Capture Failed: MTLCaptureManager doesn't support GPU trace documents! (Did you forget to run with METAL_CAPTURE_ENABLED=1?)");
}
}
}
}
}}
void MetalHostDisplay::SetVSync(VsyncMode mode)
{
[m_layer setDisplaySyncEnabled:mode != VsyncMode::Off];
m_vsync_mode = mode;
}
bool MetalHostDisplay::CreateImGuiContext()
{
ImGuiIO& io = ImGui::GetIO();
io.BackendRendererName = "pcsx2_imgui_metal";
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
return true;
}
void MetalHostDisplay::DestroyImGuiContext()
{
ImGui::GetIO().Fonts->SetTexID(nullptr);
}
bool MetalHostDisplay::UpdateImGuiFontTexture()
{ @autoreleasepool {
u8* data;
int width, height;
ImFontAtlas* fonts = ImGui::GetIO().Fonts;
fonts->GetTexDataAsAlpha8(&data, &width, &height);
MTLTextureDescriptor* desc = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatA8Unorm width:width height:height mipmapped:false];
[desc setUsage:MTLTextureUsageShaderRead];
[desc setStorageMode:MTLStorageModePrivate];
if (@available(macOS 10.15, *))
if (m_dev.features.texture_swizzle)
[desc setSwizzle:MTLTextureSwizzleChannelsMake(MTLTextureSwizzleOne, MTLTextureSwizzleOne, MTLTextureSwizzleOne, MTLTextureSwizzleAlpha)];
m_font_tex = MRCTransfer([m_dev.dev newTextureWithDescriptor:desc]);
[m_font_tex setLabel:@"ImGui Font"];
UpdateTexture(m_font_tex, 0, 0, width, height, data, width);
fonts->SetTexID((__bridge void*)m_font_tex);
return static_cast<bool>(m_font_tex);
}}
bool MetalHostDisplay::GetHostRefreshRate(float* refresh_rate)
{
OnMainThread([this, refresh_rate]
{
u32 did = [[[[[m_view window] screen] deviceDescription] valueForKey:@"NSScreenNumber"] unsignedIntValue];
if (CGDisplayModeRef mode = CGDisplayCopyDisplayMode(did))
{
*refresh_rate = CGDisplayModeGetRefreshRate(mode);
CGDisplayModeRelease(mode);
}
else
{
*refresh_rate = 0;
}
});
return *refresh_rate != 0;
}
bool MetalHostDisplay::SetGPUTimingEnabled(bool enabled)
{
if (enabled == m_gpu_timing_enabled)
return true;
if (@available(macOS 10.15, iOS 10.3, *))
{
std::lock_guard<std::mutex> l(m_mtx);
m_gpu_timing_enabled = enabled;
m_accumulated_gpu_time = 0;
m_last_gpu_time_end = 0;
return true;
}
return false;
}
float MetalHostDisplay::GetAndResetAccumulatedGPUTime()
{
std::lock_guard<std::mutex> l(m_mtx);
float time = m_accumulated_gpu_time * 1000;
m_accumulated_gpu_time = 0;
return time;
}
void MetalHostDisplay::AccumulateCommandBufferTime(id<MTLCommandBuffer> buffer)
{
std::lock_guard<std::mutex> l(m_mtx);
if (!m_gpu_timing_enabled)
return;
// We do the check before enabling m_gpu_timing_enabled
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunguarded-availability"
// It's unlikely, but command buffers can overlap or run out of order
// This doesn't handle every case (fully out of order), but it should at least handle overlapping
double begin = std::max(m_last_gpu_time_end, [buffer GPUStartTime]);
double end = [buffer GPUEndTime];
if (end > begin)
{
m_accumulated_gpu_time += end - begin;
m_last_gpu_time_end = end;
}
#pragma clang diagnostic pop
}
#endif // __APPLE__
-487
View File
@@ -1,487 +0,0 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2021 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "PrecompiledHeader.h"
#include "OpenGLHostDisplay.h"
#include "common/Assertions.h"
#include "common/Console.h"
#include "common/ScopedGuard.h"
#include "common/StringUtil.h"
#include "common/GL/Program.h"
#include "imgui.h"
#include "imgui_impl_opengl3.h"
#include <array>
#include <tuple>
class OpenGLHostDisplayTexture : public HostDisplayTexture
{
public:
OpenGLHostDisplayTexture(GLuint texture, u32 width, u32 height)
: m_texture(texture)
, m_width(width)
, m_height(height)
{
}
~OpenGLHostDisplayTexture() override = default;
void* GetHandle() const override { return reinterpret_cast<void*>(static_cast<uintptr_t>(m_texture)); }
u32 GetWidth() const override { return m_width; }
u32 GetHeight() const override { return m_height; }
GLuint GetGLID() const { return m_texture; }
private:
GLuint m_texture;
u32 m_width;
u32 m_height;
};
OpenGLHostDisplay::OpenGLHostDisplay() = default;
OpenGLHostDisplay::~OpenGLHostDisplay()
{
if (m_gl_context)
{
m_gl_context->DoneCurrent();
m_gl_context.reset();
}
}
RenderAPI OpenGLHostDisplay::GetRenderAPI() const
{
return m_gl_context->IsGLES() ? RenderAPI::OpenGLES : RenderAPI::OpenGL;
}
void* OpenGLHostDisplay::GetDevice() const
{
return nullptr;
}
void* OpenGLHostDisplay::GetContext() const
{
return m_gl_context.get();
}
void* OpenGLHostDisplay::GetSurface() const
{
return nullptr;
}
std::unique_ptr<HostDisplayTexture> OpenGLHostDisplay::CreateTexture(u32 width, u32 height, const void* data, u32 data_stride, bool dynamic /* = false */)
{
// clear error
glGetError();
// don't worry, I'm planning on removing this eventually - we'll use GSTexture instead.
glActiveTexture(GL_TEXTURE7);
GLuint id;
glGenTextures(1, &id);
glBindTexture(GL_TEXTURE_2D, id);
if (GLAD_GL_ARB_texture_storage || GLAD_GL_ES_VERSION_3_0)
{
glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, width, height);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, data);
}
else
{
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
}
glActiveTexture(GL_TEXTURE0);
GLenum error = glGetError();
if (error != GL_NO_ERROR)
{
Console.Error("Failed to create texture: 0x%X", error);
glDeleteTextures(1, &id);
return nullptr;
}
return std::make_unique<OpenGLHostDisplayTexture>(id, width, height);
}
void OpenGLHostDisplay::UpdateTexture(HostDisplayTexture* texture, u32 x, u32 y, u32 width, u32 height, const void* texture_data, u32 texture_data_stride)
{
glActiveTexture(GL_TEXTURE7);
glPixelStorei(GL_UNPACK_ROW_LENGTH, texture_data_stride / sizeof(u32));
glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, GL_RGBA8, GL_UNSIGNED_BYTE, texture_data);
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
glActiveTexture(GL_TEXTURE0);
}
void OpenGLHostDisplay::SetVSync(VsyncMode mode)
{
if (m_vsync_mode == mode || m_gl_context->GetWindowInfo().type == WindowInfo::Type::Surfaceless)
return;
// Window framebuffer has to be bound to call SetSwapInterval.
GLint current_fbo = 0;
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &current_fbo);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
if (mode != VsyncMode::Adaptive || !m_gl_context->SetSwapInterval(-1))
m_gl_context->SetSwapInterval(static_cast<s32>(mode != VsyncMode::Off));
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, current_fbo);
m_vsync_mode = mode;
}
const char* OpenGLHostDisplay::GetGLSLVersionString() const
{
if (GetRenderAPI() == RenderAPI::OpenGLES)
{
if (GLAD_GL_ES_VERSION_3_0)
return "#version 300 es";
else
return "#version 100";
}
else
{
if (GLAD_GL_VERSION_3_3)
return "#version 330";
else
return "#version 130";
}
}
std::string OpenGLHostDisplay::GetGLSLVersionHeader() const
{
std::string header = GetGLSLVersionString();
header += "\n\n";
if (GetRenderAPI() == RenderAPI::OpenGLES)
{
header += "precision highp float;\n";
header += "precision highp int;\n\n";
}
return header;
}
bool OpenGLHostDisplay::HasDevice() const
{
return static_cast<bool>(m_gl_context);
}
bool OpenGLHostDisplay::HasSurface() const
{
return m_window_info.type != WindowInfo::Type::Surfaceless;
}
bool OpenGLHostDisplay::CreateDevice(const WindowInfo& wi, VsyncMode vsync)
{
m_gl_context = GL::Context::Create(wi);
if (!m_gl_context)
{
Console.Error("Failed to create any GL context");
m_gl_context.reset();
return false;
}
m_window_info = m_gl_context->GetWindowInfo();
m_vsync_mode = vsync;
return true;
}
bool OpenGLHostDisplay::SetupDevice()
{
// We do use 8-bit formats, and higher alignment for 32-bit formats won't hurt anything.
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
SetSwapInterval();
GL::Program::ResetLastProgram();
return true;
}
void OpenGLHostDisplay::SetSwapInterval()
{
const int interval = ((m_vsync_mode == VsyncMode::Adaptive) ? -1 : ((m_vsync_mode == VsyncMode::On) ? 1 : 0));
m_gl_context->SetSwapInterval(interval);
}
bool OpenGLHostDisplay::MakeCurrent()
{
if (!m_gl_context->MakeCurrent())
{
Console.Error("Failed to make GL context current");
return false;
}
SetSwapInterval();
return true;
}
bool OpenGLHostDisplay::DoneCurrent()
{
return m_gl_context->DoneCurrent();
}
bool OpenGLHostDisplay::ChangeWindow(const WindowInfo& new_wi)
{
pxAssert(m_gl_context);
if (!m_gl_context->ChangeSurface(new_wi))
{
Console.Error("Failed to change surface");
return false;
}
m_window_info = m_gl_context->GetWindowInfo();
if (new_wi.type != WindowInfo::Type::Surfaceless)
{
// reset vsync rate, since it (usually) gets lost
if (m_vsync_mode != VsyncMode::Adaptive || !m_gl_context->SetSwapInterval(-1))
m_gl_context->SetSwapInterval(static_cast<s32>(m_vsync_mode != VsyncMode::Off));
}
return true;
}
void OpenGLHostDisplay::ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale)
{
if (!m_gl_context)
return;
m_window_info.surface_scale = new_window_scale;
if (m_window_info.surface_width == static_cast<u32>(new_window_width) &&
m_window_info.surface_height == static_cast<u32>(new_window_height))
{
return;
}
m_gl_context->ResizeSurface(static_cast<u32>(new_window_width), static_cast<u32>(new_window_height));
m_window_info = m_gl_context->GetWindowInfo();
}
bool OpenGLHostDisplay::SupportsFullscreen() const
{
return false;
}
bool OpenGLHostDisplay::IsFullscreen()
{
return false;
}
bool OpenGLHostDisplay::SetFullscreen(bool fullscreen, u32 width, u32 height, float refresh_rate)
{
return false;
}
HostDisplay::AdapterAndModeList OpenGLHostDisplay::GetAdapterAndModeList()
{
AdapterAndModeList aml;
if (m_gl_context)
{
for (const GL::Context::FullscreenModeInfo& fmi : m_gl_context->EnumerateFullscreenModes())
aml.fullscreen_modes.push_back(GetFullscreenModeString(fmi.width, fmi.height, fmi.refresh_rate));
}
return aml;
}
void OpenGLHostDisplay::DestroySurface()
{
if (!m_gl_context)
return;
m_window_info = {};
if (!m_gl_context->ChangeSurface(m_window_info))
Console.Error("Failed to switch to surfaceless");
}
std::string OpenGLHostDisplay::GetDriverInfo() const
{
const char* gl_vendor = reinterpret_cast<const char*>(glGetString(GL_VENDOR));
const char* gl_renderer = reinterpret_cast<const char*>(glGetString(GL_RENDERER));
const char* gl_version = reinterpret_cast<const char*>(glGetString(GL_VERSION));
return StringUtil::StdStringFromFormat(
"%s Context:\n%s\n%s %s", m_gl_context->IsGLES() ? "OpenGL ES" : "OpenGL", gl_version, gl_vendor, gl_renderer);
}
bool OpenGLHostDisplay::CreateImGuiContext()
{
return ImGui_ImplOpenGL3_Init(GetGLSLVersionString());
}
void OpenGLHostDisplay::DestroyImGuiContext()
{
ImGui_ImplOpenGL3_Shutdown();
}
bool OpenGLHostDisplay::UpdateImGuiFontTexture()
{
return ImGui_ImplOpenGL3_CreateFontsTexture();
}
HostDisplay::PresentResult OpenGLHostDisplay::BeginPresent(bool frame_skip)
{
if (frame_skip || m_window_info.type == WindowInfo::Type::Surfaceless)
return PresentResult::FrameSkipped;
glDisable(GL_SCISSOR_TEST);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glViewport(0, 0, m_window_info.surface_width, m_window_info.surface_height);
return PresentResult::OK;
}
void OpenGLHostDisplay::EndPresent()
{
glDisable(GL_SCISSOR_TEST);
glDisable(GL_STENCIL_TEST);
ImGui::Render();
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
GL::Program::ResetLastProgram();
if (m_gpu_timing_enabled)
PopTimestampQuery();
m_gl_context->SwapBuffers();
if (m_gpu_timing_enabled)
KickTimestampQuery();
}
void OpenGLHostDisplay::CreateTimestampQueries()
{
const bool gles = m_gl_context->IsGLES();
const auto GenQueries = gles ? glGenQueriesEXT : glGenQueries;
GenQueries(static_cast<u32>(m_timestamp_queries.size()), m_timestamp_queries.data());
KickTimestampQuery();
}
void OpenGLHostDisplay::DestroyTimestampQueries()
{
if (m_timestamp_queries[0] == 0)
return;
const bool gles = m_gl_context->IsGLES();
const auto DeleteQueries = gles ? glDeleteQueriesEXT : glDeleteQueries;
if (m_timestamp_query_started)
{
const auto EndQuery = gles ? glEndQueryEXT : glEndQuery;
EndQuery(GL_TIME_ELAPSED);
}
DeleteQueries(static_cast<u32>(m_timestamp_queries.size()), m_timestamp_queries.data());
m_timestamp_queries.fill(0);
m_read_timestamp_query = 0;
m_write_timestamp_query = 0;
m_waiting_timestamp_queries = 0;
m_timestamp_query_started = false;
}
void OpenGLHostDisplay::PopTimestampQuery()
{
const bool gles = m_gl_context->IsGLES();
if (gles)
{
GLint disjoint = 0;
glGetIntegerv(GL_GPU_DISJOINT_EXT, &disjoint);
if (disjoint)
{
DevCon.WriteLn("GPU timing disjoint, resetting.");
if (m_timestamp_query_started)
glEndQueryEXT(GL_TIME_ELAPSED);
m_read_timestamp_query = 0;
m_write_timestamp_query = 0;
m_waiting_timestamp_queries = 0;
m_timestamp_query_started = false;
}
}
while (m_waiting_timestamp_queries > 0)
{
const auto GetQueryObjectiv = gles ? glGetQueryObjectivEXT : glGetQueryObjectiv;
const auto GetQueryObjectui64v = gles ? glGetQueryObjectui64vEXT : glGetQueryObjectui64v;
GLint available = 0;
GetQueryObjectiv(m_timestamp_queries[m_read_timestamp_query], GL_QUERY_RESULT_AVAILABLE, &available);
if (!available)
break;
u64 result = 0;
GetQueryObjectui64v(m_timestamp_queries[m_read_timestamp_query], GL_QUERY_RESULT, &result);
m_accumulated_gpu_time += static_cast<float>(static_cast<double>(result) / 1000000.0);
m_read_timestamp_query = (m_read_timestamp_query + 1) % NUM_TIMESTAMP_QUERIES;
m_waiting_timestamp_queries--;
}
if (m_timestamp_query_started)
{
const auto EndQuery = gles ? glEndQueryEXT : glEndQuery;
EndQuery(GL_TIME_ELAPSED);
m_write_timestamp_query = (m_write_timestamp_query + 1) % NUM_TIMESTAMP_QUERIES;
m_timestamp_query_started = false;
m_waiting_timestamp_queries++;
}
}
void OpenGLHostDisplay::KickTimestampQuery()
{
if (m_timestamp_query_started || m_waiting_timestamp_queries == NUM_TIMESTAMP_QUERIES)
return;
const bool gles = m_gl_context->IsGLES();
const auto BeginQuery = gles ? glBeginQueryEXT : glBeginQuery;
BeginQuery(GL_TIME_ELAPSED, m_timestamp_queries[m_write_timestamp_query]);
m_timestamp_query_started = true;
}
bool OpenGLHostDisplay::SetGPUTimingEnabled(bool enabled)
{
if (m_gpu_timing_enabled == enabled)
return true;
if (enabled && m_gl_context->IsGLES() && !GLAD_GL_EXT_disjoint_timer_query)
return false;
m_gpu_timing_enabled = enabled;
if (m_gpu_timing_enabled)
CreateTimestampQueries();
else
DestroyTimestampQueries();
return true;
}
float OpenGLHostDisplay::GetAndResetAccumulatedGPUTime()
{
const float value = m_accumulated_gpu_time;
m_accumulated_gpu_time = 0.0f;
return value;
}
-94
View File
@@ -1,94 +0,0 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2021 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <glad.h>
#include "HostDisplay.h"
#include "common/GL/Context.h"
#include "common/WindowInfo.h"
#include <array>
#include <bitset>
#include <memory>
class OpenGLHostDisplay final : public HostDisplay
{
public:
OpenGLHostDisplay();
~OpenGLHostDisplay();
RenderAPI GetRenderAPI() const override;
void* GetDevice() const override;
void* GetContext() const override;
void* GetSurface() const override;
bool HasDevice() const override;
bool HasSurface() const override;
bool CreateDevice(const WindowInfo& wi, VsyncMode vsync) override;
bool SetupDevice() override;
bool MakeCurrent() override;
bool DoneCurrent() override;
bool ChangeWindow(const WindowInfo& new_wi) override;
void ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale) override;
bool SupportsFullscreen() const override;
bool IsFullscreen() override;
bool SetFullscreen(bool fullscreen, u32 width, u32 height, float refresh_rate) override;
AdapterAndModeList GetAdapterAndModeList() override;
void DestroySurface() override;
std::string GetDriverInfo() const override;
std::unique_ptr<HostDisplayTexture> CreateTexture(u32 width, u32 height, const void* data, u32 data_stride, bool dynamic) override;
void UpdateTexture(HostDisplayTexture* texture, u32 x, u32 y, u32 width, u32 height, const void* texture_data, u32 texture_data_stride) override;
void SetVSync(VsyncMode mode) override;
PresentResult BeginPresent(bool frame_skip) override;
void EndPresent() override;
bool SetGPUTimingEnabled(bool enabled) override;
float GetAndResetAccumulatedGPUTime() override;
protected:
static constexpr u8 NUM_TIMESTAMP_QUERIES = 5;
const char* GetGLSLVersionString() const;
std::string GetGLSLVersionHeader() const;
bool CreateImGuiContext() override;
void DestroyImGuiContext() override;
bool UpdateImGuiFontTexture() override;
void SetSwapInterval();
void CreateTimestampQueries();
void DestroyTimestampQueries();
void PopTimestampQuery();
void KickTimestampQuery();
std::unique_ptr<GL::Context> m_gl_context;
std::array<GLuint, NUM_TIMESTAMP_QUERIES> m_timestamp_queries = {};
u8 m_read_timestamp_query = 0;
u8 m_write_timestamp_query = 0;
u8 m_waiting_timestamp_queries = 0;
bool m_timestamp_query_started = false;
float m_accumulated_gpu_time = 0.0f;
bool m_gpu_timing_enabled = false;
};
-470
View File
@@ -1,470 +0,0 @@
#include "PrecompiledHeader.h"
#include "VulkanHostDisplay.h"
#include "ShaderCacheVersion.h"
#include "common/Align.h"
#include "common/Assertions.h"
#include "common/Console.h"
#include "common/ScopedGuard.h"
#include "common/Vulkan/Builders.h"
#include "common/Vulkan/Context.h"
#include "common/Vulkan/ShaderCache.h"
#include "common/Vulkan/StreamBuffer.h"
#include "common/Vulkan/SwapChain.h"
#include "common/Vulkan/Util.h"
#include "imgui.h"
#include "imgui_impl_vulkan.h"
#include <array>
class VulkanHostDisplayTexture : public HostDisplayTexture
{
public:
explicit VulkanHostDisplayTexture(Vulkan::Texture texture)
: m_texture(std::move(texture))
{
}
~VulkanHostDisplayTexture() override = default;
void* GetHandle() const override { return const_cast<Vulkan::Texture*>(&m_texture); }
u32 GetWidth() const override { return m_texture.GetWidth(); }
u32 GetHeight() const override { return m_texture.GetHeight(); }
const Vulkan::Texture& GetTexture() const { return m_texture; }
Vulkan::Texture& GetTexture() { return m_texture; }
private:
Vulkan::Texture m_texture;
};
static VkPresentModeKHR GetPreferredPresentModeForVsyncMode(VsyncMode mode)
{
if (mode == VsyncMode::On)
return VK_PRESENT_MODE_FIFO_KHR;
else if (mode == VsyncMode::Adaptive)
return VK_PRESENT_MODE_FIFO_RELAXED_KHR;
else
return VK_PRESENT_MODE_IMMEDIATE_KHR;
}
VulkanHostDisplay::VulkanHostDisplay() = default;
VulkanHostDisplay::~VulkanHostDisplay()
{
if (g_vulkan_context)
{
g_vulkan_context->WaitForGPUIdle();
m_swap_chain.reset();
Vulkan::ShaderCache::Destroy();
Vulkan::Context::Destroy();
}
}
RenderAPI VulkanHostDisplay::GetRenderAPI() const
{
return RenderAPI::Vulkan;
}
void* VulkanHostDisplay::GetDevice() const
{
return nullptr;
}
void* VulkanHostDisplay::GetContext() const
{
return nullptr;
}
void* VulkanHostDisplay::GetSurface() const
{
return m_swap_chain.get();
}
bool VulkanHostDisplay::ChangeWindow(const WindowInfo& new_wi)
{
g_vulkan_context->WaitForGPUIdle();
if (new_wi.type == WindowInfo::Type::Surfaceless)
{
g_vulkan_context->ExecuteCommandBuffer(Vulkan::Context::WaitType::Sleep);
m_swap_chain.reset();
m_window_info = new_wi;
return true;
}
// recreate surface in existing swap chain if it already exists
if (m_swap_chain)
{
if (m_swap_chain->RecreateSurface(new_wi))
{
m_window_info = m_swap_chain->GetWindowInfo();
return true;
}
m_swap_chain.reset();
}
WindowInfo wi_copy(new_wi);
VkSurfaceKHR surface =
Vulkan::SwapChain::CreateVulkanSurface(g_vulkan_context->GetVulkanInstance(), g_vulkan_context->GetPhysicalDevice(), &wi_copy);
if (surface == VK_NULL_HANDLE)
{
Console.Error("Failed to create new surface for swap chain");
return false;
}
m_swap_chain = Vulkan::SwapChain::Create(wi_copy, surface, GetPreferredPresentModeForVsyncMode(m_vsync_mode));
if (!m_swap_chain)
{
Console.Error("Failed to create swap chain");
Vulkan::SwapChain::DestroyVulkanSurface(g_vulkan_context->GetVulkanInstance(), &wi_copy, surface);
return false;
}
m_window_info = m_swap_chain->GetWindowInfo();
return true;
}
void VulkanHostDisplay::ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale)
{
if (m_swap_chain->GetWidth() == static_cast<u32>(new_window_width) && m_swap_chain->GetHeight() == static_cast<u32>(new_window_height))
{
// skip unnecessary resizes
m_window_info.surface_scale = new_window_scale;
return;
}
g_vulkan_context->WaitForGPUIdle();
if (!m_swap_chain->ResizeSwapChain(new_window_width, new_window_height, new_window_scale))
{
// AcquireNextImage() will fail, and we'll recreate the surface.
Console.Error("Failed to resize swap chain. Next present will fail.");
return;
}
m_window_info = m_swap_chain->GetWindowInfo();
}
bool VulkanHostDisplay::SupportsFullscreen() const
{
return false;
}
bool VulkanHostDisplay::IsFullscreen()
{
return false;
}
bool VulkanHostDisplay::SetFullscreen(bool fullscreen, u32 width, u32 height, float refresh_rate)
{
return false;
}
HostDisplay::AdapterAndModeList VulkanHostDisplay::GetAdapterAndModeList()
{
return StaticGetAdapterAndModeList(m_window_info.type != WindowInfo::Type::Surfaceless ? &m_window_info : nullptr);
}
void VulkanHostDisplay::DestroySurface()
{
g_vulkan_context->WaitForGPUIdle();
m_swap_chain.reset();
}
std::string VulkanHostDisplay::GetDriverInfo() const
{
std::string ret;
const u32 api_version = g_vulkan_context->GetDeviceProperties().apiVersion;
const u32 driver_version = g_vulkan_context->GetDeviceProperties().driverVersion;
if (g_vulkan_context->GetOptionalExtensions().vk_khr_driver_properties)
{
const VkPhysicalDeviceDriverProperties& props = g_vulkan_context->GetDeviceDriverProperties();
ret = StringUtil::StdStringFromFormat("Driver %u.%u.%u\nVulkan %u.%u.%u\nConformance Version %u.%u.%u.%u\n%s\n%s\n%s",
VK_VERSION_MAJOR(driver_version), VK_VERSION_MINOR(driver_version), VK_VERSION_PATCH(driver_version),
VK_API_VERSION_MAJOR(api_version), VK_API_VERSION_MINOR(api_version), VK_API_VERSION_PATCH(api_version),
props.conformanceVersion.major, props.conformanceVersion.minor, props.conformanceVersion.subminor,
props.conformanceVersion.patch, props.driverInfo, props.driverName, g_vulkan_context->GetDeviceProperties().deviceName);
}
else
{
ret = StringUtil::StdStringFromFormat("Driver %u.%u.%u\nVulkan %u.%u.%u\n%s", VK_VERSION_MAJOR(driver_version),
VK_VERSION_MINOR(driver_version), VK_VERSION_PATCH(driver_version), VK_API_VERSION_MAJOR(api_version),
VK_API_VERSION_MINOR(api_version), VK_API_VERSION_PATCH(api_version), g_vulkan_context->GetDeviceProperties().deviceName);
}
return ret;
}
static bool UploadBufferToTexture(
Vulkan::Texture* texture, VkCommandBuffer cmdbuf, u32 width, u32 height, const void* data, u32 data_stride)
{
const u32 texel_size = Vulkan::Util::GetTexelSize(texture->GetFormat());
const u32 row_size = texel_size * width;
const u32 upload_stride = Common::AlignUpPow2(row_size, g_vulkan_context->GetBufferCopyRowPitchAlignment());
const u32 upload_size = upload_stride * height;
pxAssert(row_size <= data_stride);
Vulkan::StreamBuffer& buf = g_vulkan_context->GetTextureUploadBuffer();
if (!buf.ReserveMemory(upload_size, g_vulkan_context->GetBufferCopyOffsetAlignment()))
{
Console.WriteLn("Executing command buffer for UploadBufferToTexture()");
g_vulkan_context->ExecuteCommandBuffer(Vulkan::Context::WaitType::None);
if (!buf.ReserveMemory(upload_size, g_vulkan_context->GetBufferCopyOffsetAlignment()))
{
Console.WriteLn("Failed to allocate %u bytes in stream buffer for UploadBufferToTexture()", upload_size);
return false;
}
cmdbuf = g_vulkan_context->GetCurrentInitCommandBuffer();
}
const u32 buf_offset = buf.GetCurrentOffset();
StringUtil::StrideMemCpy(buf.GetCurrentHostPointer(), upload_stride, data, data_stride, row_size, height);
buf.CommitMemory(upload_size);
texture->UpdateFromBuffer(cmdbuf, 0, 0, 0, 0, width, height, height, upload_stride / texel_size, buf.GetBuffer(), buf_offset);
return true;
}
std::unique_ptr<HostDisplayTexture> VulkanHostDisplay::CreateTexture(
u32 width, u32 height, const void* data, u32 data_stride, bool dynamic /* = false */)
{
static constexpr VkFormat vk_format = VK_FORMAT_R8G8B8A8_UNORM;
static constexpr VkImageUsageFlags usage =
VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
Vulkan::Texture texture;
if (!texture.Create(width, height, 1, 1, vk_format, VK_SAMPLE_COUNT_1_BIT, VK_IMAGE_VIEW_TYPE_2D, VK_IMAGE_TILING_OPTIMAL, usage))
return {};
texture.TransitionToLayout(g_vulkan_context->GetCurrentInitCommandBuffer(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
if (data)
{
if (!UploadBufferToTexture(&texture, g_vulkan_context->GetCurrentInitCommandBuffer(), width, height, data, data_stride))
return {};
}
else
{
// clear it instead so we don't read uninitialized data (and keep the validation layer happy!)
static constexpr VkClearColorValue ccv = {};
static constexpr VkImageSubresourceRange isr = {VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u};
vkCmdClearColorImage(g_vulkan_context->GetCurrentInitCommandBuffer(), texture.GetImage(), texture.GetLayout(), &ccv, 1u, &isr);
}
texture.TransitionToLayout(g_vulkan_context->GetCurrentInitCommandBuffer(), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
return std::make_unique<VulkanHostDisplayTexture>(std::move(texture));
}
void VulkanHostDisplay::UpdateTexture(HostDisplayTexture* texture, u32 x, u32 y, u32 width, u32 height, const void* data, u32 data_stride)
{
UploadBufferToTexture(&static_cast<VulkanHostDisplayTexture*>(texture)->GetTexture(), g_vulkan_context->GetCurrentCommandBuffer(),
width, height, data, data_stride);
}
void VulkanHostDisplay::SetVSync(VsyncMode mode)
{
if (!m_swap_chain || m_vsync_mode == mode)
return;
// This swap chain should not be used by the current buffer, thus safe to destroy.
g_vulkan_context->WaitForGPUIdle();
m_swap_chain->SetVSync(GetPreferredPresentModeForVsyncMode(mode));
m_vsync_mode = mode;
}
bool VulkanHostDisplay::CreateDevice(const WindowInfo& wi, VsyncMode vsync)
{
WindowInfo local_wi(wi);
const bool debug_device = EmuConfig.GS.UseDebugDevice;
if (!Vulkan::Context::Create(EmuConfig.GS.Adapter, &local_wi, &m_swap_chain, GetPreferredPresentModeForVsyncMode(vsync),
!EmuConfig.GS.DisableThreadedPresentation, debug_device, debug_device))
{
Console.Error("Failed to create Vulkan context");
m_window_info = {};
return false;
}
// NOTE: This is assigned afterwards, because some platforms can modify the window info (e.g. Metal).
m_window_info = m_swap_chain ? m_swap_chain->GetWindowInfo() : local_wi;
m_vsync_mode = vsync;
return true;
}
bool VulkanHostDisplay::SetupDevice()
{
Vulkan::ShaderCache::Create(EmuConfig.GS.DisableShaderCache ? std::string_view() : std::string_view(EmuFolders::Cache),
SHADER_CACHE_VERSION, EmuConfig.GS.UseDebugDevice);
return true;
}
bool VulkanHostDisplay::HasDevice() const
{
return static_cast<bool>(g_vulkan_context);
}
bool VulkanHostDisplay::HasSurface() const
{
return static_cast<bool>(m_swap_chain);
}
bool VulkanHostDisplay::CreateImGuiContext()
{
const VkRenderPass render_pass =
m_swap_chain ? m_swap_chain->GetClearRenderPass() : g_vulkan_context->GetRenderPass(VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_UNDEFINED);
if (render_pass == VK_NULL_HANDLE)
return false;
return ImGui_ImplVulkan_Init(render_pass);
}
void VulkanHostDisplay::DestroyImGuiContext()
{
g_vulkan_context->WaitForGPUIdle();
ImGui_ImplVulkan_Shutdown();
}
bool VulkanHostDisplay::UpdateImGuiFontTexture()
{
return ImGui_ImplVulkan_CreateFontsTexture();
}
bool VulkanHostDisplay::MakeCurrent()
{
return true;
}
bool VulkanHostDisplay::DoneCurrent()
{
return true;
}
HostDisplay::PresentResult VulkanHostDisplay::BeginPresent(bool frame_skip)
{
if (frame_skip || !m_swap_chain)
return PresentResult::FrameSkipped;
// Previous frame needs to be presented before we can acquire the swap chain.
g_vulkan_context->WaitForPresentComplete();
// Check if the device was lost.
if (g_vulkan_context->CheckLastSubmitFail())
return PresentResult::DeviceLost;
VkResult res = m_swap_chain->AcquireNextImage();
if (res != VK_SUCCESS)
{
m_swap_chain->ReleaseCurrentImage();
if (res == VK_SUBOPTIMAL_KHR || res == VK_ERROR_OUT_OF_DATE_KHR)
{
ResizeWindow(0, 0, m_window_info.surface_scale);
res = m_swap_chain->AcquireNextImage();
}
else if (res == VK_ERROR_SURFACE_LOST_KHR)
{
Console.Warning("Surface lost, attempting to recreate");
if (!m_swap_chain->RecreateSurface(m_window_info))
{
Console.Error("Failed to recreate surface after loss");
g_vulkan_context->ExecuteCommandBuffer(Vulkan::Context::WaitType::None);
return PresentResult::FrameSkipped;
}
res = m_swap_chain->AcquireNextImage();
}
// This can happen when multiple resize events happen in quick succession.
// In this case, just wait until the next frame to try again.
if (res != VK_SUCCESS && res != VK_SUBOPTIMAL_KHR)
{
// Still submit the command buffer, otherwise we'll end up with several frames waiting.
LOG_VULKAN_ERROR(res, "vkAcquireNextImageKHR() failed: ");
g_vulkan_context->ExecuteCommandBuffer(Vulkan::Context::WaitType::None);
return PresentResult::FrameSkipped;
}
}
VkCommandBuffer cmdbuffer = g_vulkan_context->GetCurrentCommandBuffer();
// Swap chain images start in undefined
Vulkan::Texture& swap_chain_texture = m_swap_chain->GetCurrentTexture();
swap_chain_texture.OverrideImageLayout(VK_IMAGE_LAYOUT_UNDEFINED);
swap_chain_texture.TransitionToLayout(cmdbuffer, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
const VkClearValue clear_value = {{{0.0f, 0.0f, 0.0f, 1.0f}}};
const VkRenderPassBeginInfo rp = {VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, nullptr, m_swap_chain->GetClearRenderPass(),
m_swap_chain->GetCurrentFramebuffer(), {{0, 0}, {swap_chain_texture.GetWidth(), swap_chain_texture.GetHeight()}}, 1u, &clear_value};
vkCmdBeginRenderPass(g_vulkan_context->GetCurrentCommandBuffer(), &rp, VK_SUBPASS_CONTENTS_INLINE);
const VkViewport vp{
0.0f, 0.0f, static_cast<float>(swap_chain_texture.GetWidth()), static_cast<float>(swap_chain_texture.GetHeight()), 0.0f, 1.0f};
const VkRect2D scissor{{0, 0}, {static_cast<u32>(swap_chain_texture.GetWidth()), static_cast<u32>(swap_chain_texture.GetHeight())}};
vkCmdSetViewport(g_vulkan_context->GetCurrentCommandBuffer(), 0, 1, &vp);
vkCmdSetScissor(g_vulkan_context->GetCurrentCommandBuffer(), 0, 1, &scissor);
return PresentResult::OK;
}
void VulkanHostDisplay::EndPresent()
{
ImGui::Render();
ImGui_ImplVulkan_RenderDrawData(ImGui::GetDrawData());
VkCommandBuffer cmdbuffer = g_vulkan_context->GetCurrentCommandBuffer();
vkCmdEndRenderPass(g_vulkan_context->GetCurrentCommandBuffer());
m_swap_chain->GetCurrentTexture().TransitionToLayout(cmdbuffer, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
g_vulkan_context->SubmitCommandBuffer(m_swap_chain.get(), !m_swap_chain->IsPresentModeSynchronizing());
g_vulkan_context->MoveToNextCommandBuffer();
}
bool VulkanHostDisplay::SetGPUTimingEnabled(bool enabled)
{
return g_vulkan_context->SetEnableGPUTiming(enabled);
}
float VulkanHostDisplay::GetAndResetAccumulatedGPUTime()
{
return g_vulkan_context->GetAndResetAccumulatedGPUTime();
}
HostDisplay::AdapterAndModeList VulkanHostDisplay::StaticGetAdapterAndModeList(const WindowInfo* wi)
{
AdapterAndModeList ret;
std::vector<Vulkan::SwapChain::FullscreenModeInfo> fsmodes;
if (g_vulkan_context)
{
ret.adapter_names = Vulkan::Context::EnumerateGPUNames(g_vulkan_context->GetVulkanInstance());
if (wi)
{
fsmodes = Vulkan::SwapChain::GetSurfaceFullscreenModes(
g_vulkan_context->GetVulkanInstance(), g_vulkan_context->GetPhysicalDevice(), *wi);
}
}
else if (Vulkan::LoadVulkanLibrary())
{
ScopedGuard lib_guard([]() { Vulkan::UnloadVulkanLibrary(); });
VkInstance instance = Vulkan::Context::CreateVulkanInstance(nullptr, false, false);
if (instance != VK_NULL_HANDLE)
{
ScopedGuard instance_guard([&instance]() { vkDestroyInstance(instance, nullptr); });
if (Vulkan::LoadVulkanInstanceFunctions(instance))
ret.adapter_names = Vulkan::Context::EnumerateGPUNames(instance);
}
}
if (!fsmodes.empty())
{
ret.fullscreen_modes.reserve(fsmodes.size());
for (const Vulkan::SwapChain::FullscreenModeInfo& fmi : fsmodes)
{
ret.fullscreen_modes.push_back(GetFullscreenModeString(fmi.width, fmi.height, fmi.refresh_rate));
}
}
return ret;
}
-64
View File
@@ -1,64 +0,0 @@
#pragma once
#include "common/Vulkan/Loader.h"
#include "common/Vulkan/StreamBuffer.h"
#include "common/Vulkan/SwapChain.h"
#include "common/WindowInfo.h"
#include "pcsx2/HostDisplay.h"
#include <memory>
#include <string_view>
namespace Vulkan
{
class StreamBuffer;
class SwapChain;
} // namespace Vulkan
class VulkanHostDisplay final : public HostDisplay
{
public:
VulkanHostDisplay();
~VulkanHostDisplay();
RenderAPI GetRenderAPI() const override;
void* GetDevice() const override;
void* GetContext() const override;
void* GetSurface() const override;
bool HasDevice() const override;
bool HasSurface() const override;
bool CreateDevice(const WindowInfo& wi, VsyncMode vsync) override;
bool SetupDevice() override;
bool MakeCurrent() override;
bool DoneCurrent() override;
bool ChangeWindow(const WindowInfo& new_wi) override;
void ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale) override;
bool SupportsFullscreen() const override;
bool IsFullscreen() override;
bool SetFullscreen(bool fullscreen, u32 width, u32 height, float refresh_rate) override;
AdapterAndModeList GetAdapterAndModeList() override;
void DestroySurface() override;
std::string GetDriverInfo() const override;
std::unique_ptr<HostDisplayTexture> CreateTexture(u32 width, u32 height, const void* data, u32 data_stride, bool dynamic = false) override;
void UpdateTexture(HostDisplayTexture* texture, u32 x, u32 y, u32 width, u32 height, const void* texture_data, u32 texture_data_stride) override;
void SetVSync(VsyncMode mode) override;
PresentResult BeginPresent(bool frame_skip) override;
void EndPresent() override;
bool SetGPUTimingEnabled(bool enabled) override;
float GetAndResetAccumulatedGPUTime() override;
static AdapterAndModeList StaticGetAdapterAndModeList(const WindowInfo* wi);
protected:
bool CreateImGuiContext() override;
void DestroyImGuiContext() override;
bool UpdateImGuiFontTexture() override;
std::unique_ptr<Vulkan::SwapChain> m_swap_chain;
};
-531
View File
@@ -1,531 +0,0 @@
// dear imgui: Renderer Backend for DirectX11
// This needs to be used along with a Platform Backend (e.g. Win32)
// Implemented features:
// [X] Renderer: User texture binding. Use 'ID3D11ShaderResourceView*' as ImTextureID. Read the FAQ about ImTextureID!
// [X] Renderer: Support for large meshes (64k+ vertices) with 16-bit indices.
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
// Read online: https://github.com/ocornut/imgui/tree/master/docs
// CHANGELOG
// (minor and older changes stripped away, please see git history for details)
// 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX).
// 2021-05-19: DirectX11: Replaced direct access to ImDrawCmd::TextureId with a call to ImDrawCmd::GetTexID(). (will become a requirement)
// 2021-02-18: DirectX11: Change blending equation to preserve alpha in output buffer.
// 2019-08-01: DirectX11: Fixed code querying the Geometry Shader state (would generally error with Debug layer enabled).
// 2019-07-21: DirectX11: Backup, clear and restore Geometry Shader is any is bound when calling ImGui_ImplDX10_RenderDrawData. Clearing Hull/Domain/Compute shaders without backup/restore.
// 2019-05-29: DirectX11: Added support for large mesh (64K+ vertices), enable ImGuiBackendFlags_RendererHasVtxOffset flag.
// 2019-04-30: DirectX11: Added support for special ImDrawCallback_ResetRenderState callback to reset render state.
// 2018-12-03: Misc: Added #pragma comment statement to automatically link with d3dcompiler.lib when using D3DCompile().
// 2018-11-30: Misc: Setting up io.BackendRendererName so it can be displayed in the About Window.
// 2018-08-01: DirectX11: Querying for IDXGIFactory instead of IDXGIFactory1 to increase compatibility.
// 2018-07-13: DirectX11: Fixed unreleased resources in Init and Shutdown functions.
// 2018-06-08: Misc: Extracted imgui_impl_dx11.cpp/.h away from the old combined DX11+Win32 example.
// 2018-06-08: DirectX11: Use draw_data->DisplayPos and draw_data->DisplaySize to setup projection matrix and clipping rectangle.
// 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplDX11_RenderDrawData() in the .h file so you can call it yourself.
// 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves.
// 2016-05-07: DirectX11: Disabling depth-write.
#include "PrecompiledHeader.h"
#include "imgui.h"
#include "imgui_impl_dx11.h"
// DirectX
#include <stdio.h>
#include <d3d11.h>
#include <d3dcompiler.h>
#ifdef _MSC_VER
#pragma comment(lib, "d3dcompiler") // Automatically link with d3dcompiler.lib as we are using D3DCompile() below.
#endif
// DirectX11 data
struct ImGui_ImplDX11_Data
{
ID3D11Device* pd3dDevice;
ID3D11DeviceContext* pd3dDeviceContext;
IDXGIFactory* pFactory;
ID3D11Buffer* pVB;
ID3D11Buffer* pIB;
ID3D11VertexShader* pVertexShader;
ID3D11InputLayout* pInputLayout;
ID3D11Buffer* pVertexConstantBuffer;
ID3D11PixelShader* pPixelShader;
ID3D11SamplerState* pFontSampler;
ID3D11ShaderResourceView* pFontTextureView;
ID3D11RasterizerState* pRasterizerState;
ID3D11BlendState* pBlendState;
ID3D11DepthStencilState* pDepthStencilState;
int VertexBufferSize;
int IndexBufferSize;
ImGui_ImplDX11_Data() { memset((void*)this, 0, sizeof(*this)); VertexBufferSize = 5000; IndexBufferSize = 10000; }
};
struct VERTEX_CONSTANT_BUFFER
{
float mvp[4][4];
};
// Backend data stored in io.BackendRendererUserData to allow support for multiple Dear ImGui contexts
// It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts.
static ImGui_ImplDX11_Data* ImGui_ImplDX11_GetBackendData()
{
return ImGui::GetCurrentContext() ? (ImGui_ImplDX11_Data*)ImGui::GetIO().BackendRendererUserData : NULL;
}
// Functions
static void ImGui_ImplDX11_SetupRenderState(ImDrawData* draw_data, ID3D11DeviceContext* ctx)
{
ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData();
// Setup viewport
D3D11_VIEWPORT vp;
memset(&vp, 0, sizeof(D3D11_VIEWPORT));
vp.Width = draw_data->DisplaySize.x;
vp.Height = draw_data->DisplaySize.y;
vp.MinDepth = 0.0f;
vp.MaxDepth = 1.0f;
vp.TopLeftX = vp.TopLeftY = 0;
ctx->RSSetViewports(1, &vp);
// Setup shader and vertex buffers
unsigned int stride = sizeof(ImDrawVert);
unsigned int offset = 0;
ctx->IASetInputLayout(bd->pInputLayout);
ctx->IASetVertexBuffers(0, 1, &bd->pVB, &stride, &offset);
ctx->IASetIndexBuffer(bd->pIB, sizeof(ImDrawIdx) == 2 ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R32_UINT, 0);
ctx->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
ctx->VSSetShader(bd->pVertexShader, NULL, 0);
ctx->VSSetConstantBuffers(0, 1, &bd->pVertexConstantBuffer);
ctx->PSSetShader(bd->pPixelShader, NULL, 0);
ctx->PSSetSamplers(0, 1, &bd->pFontSampler);
ctx->GSSetShader(NULL, NULL, 0);
ctx->HSSetShader(NULL, NULL, 0); // In theory we should backup and restore this as well.. very infrequently used..
ctx->DSSetShader(NULL, NULL, 0); // In theory we should backup and restore this as well.. very infrequently used..
ctx->CSSetShader(NULL, NULL, 0); // In theory we should backup and restore this as well.. very infrequently used..
// Setup blend state
const float blend_factor[4] = { 0.f, 0.f, 0.f, 0.f };
ctx->OMSetBlendState(bd->pBlendState, blend_factor, 0xffffffff);
ctx->OMSetDepthStencilState(bd->pDepthStencilState, 0);
ctx->RSSetState(bd->pRasterizerState);
}
// Render function
void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data)
{
// Avoid rendering when minimized
if (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f)
return;
ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData();
ID3D11DeviceContext* ctx = bd->pd3dDeviceContext;
// Create and grow vertex/index buffers if needed
if (!bd->pVB || bd->VertexBufferSize < draw_data->TotalVtxCount)
{
if (bd->pVB) { bd->pVB->Release(); bd->pVB = NULL; }
bd->VertexBufferSize = draw_data->TotalVtxCount + 5000;
D3D11_BUFFER_DESC desc;
memset(&desc, 0, sizeof(D3D11_BUFFER_DESC));
desc.Usage = D3D11_USAGE_DYNAMIC;
desc.ByteWidth = bd->VertexBufferSize * sizeof(ImDrawVert);
desc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
desc.MiscFlags = 0;
if (bd->pd3dDevice->CreateBuffer(&desc, NULL, &bd->pVB) < 0)
return;
}
if (!bd->pIB || bd->IndexBufferSize < draw_data->TotalIdxCount)
{
if (bd->pIB) { bd->pIB->Release(); bd->pIB = NULL; }
bd->IndexBufferSize = draw_data->TotalIdxCount + 10000;
D3D11_BUFFER_DESC desc;
memset(&desc, 0, sizeof(D3D11_BUFFER_DESC));
desc.Usage = D3D11_USAGE_DYNAMIC;
desc.ByteWidth = bd->IndexBufferSize * sizeof(ImDrawIdx);
desc.BindFlags = D3D11_BIND_INDEX_BUFFER;
desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
if (bd->pd3dDevice->CreateBuffer(&desc, NULL, &bd->pIB) < 0)
return;
}
// Upload vertex/index data into a single contiguous GPU buffer
D3D11_MAPPED_SUBRESOURCE vtx_resource, idx_resource;
if (ctx->Map(bd->pVB, 0, D3D11_MAP_WRITE_DISCARD, 0, &vtx_resource) != S_OK)
return;
if (ctx->Map(bd->pIB, 0, D3D11_MAP_WRITE_DISCARD, 0, &idx_resource) != S_OK)
return;
ImDrawVert* vtx_dst = (ImDrawVert*)vtx_resource.pData;
ImDrawIdx* idx_dst = (ImDrawIdx*)idx_resource.pData;
for (int n = 0; n < draw_data->CmdListsCount; n++)
{
const ImDrawList* cmd_list = draw_data->CmdLists[n];
memcpy(vtx_dst, cmd_list->VtxBuffer.Data, cmd_list->VtxBuffer.Size * sizeof(ImDrawVert));
memcpy(idx_dst, cmd_list->IdxBuffer.Data, cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx));
vtx_dst += cmd_list->VtxBuffer.Size;
idx_dst += cmd_list->IdxBuffer.Size;
}
ctx->Unmap(bd->pVB, 0);
ctx->Unmap(bd->pIB, 0);
// Setup orthographic projection matrix into our constant buffer
// Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps.
{
D3D11_MAPPED_SUBRESOURCE mapped_resource;
if (ctx->Map(bd->pVertexConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped_resource) != S_OK)
return;
VERTEX_CONSTANT_BUFFER* constant_buffer = (VERTEX_CONSTANT_BUFFER*)mapped_resource.pData;
float L = draw_data->DisplayPos.x;
float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x;
float T = draw_data->DisplayPos.y;
float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y;
float mvp[4][4] =
{
{ 2.0f/(R-L), 0.0f, 0.0f, 0.0f },
{ 0.0f, 2.0f/(T-B), 0.0f, 0.0f },
{ 0.0f, 0.0f, 0.5f, 0.0f },
{ (R+L)/(L-R), (T+B)/(B-T), 0.5f, 1.0f },
};
memcpy(&constant_buffer->mvp, mvp, sizeof(mvp));
ctx->Unmap(bd->pVertexConstantBuffer, 0);
}
// Setup desired DX state
ImGui_ImplDX11_SetupRenderState(draw_data, ctx);
// Render command lists
// (Because we merged all buffers into a single one, we maintain our own offset into them)
int global_idx_offset = 0;
int global_vtx_offset = 0;
ImVec2 clip_off = draw_data->DisplayPos;
for (int n = 0; n < draw_data->CmdListsCount; n++)
{
const ImDrawList* cmd_list = draw_data->CmdLists[n];
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
{
const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
if (pcmd->UserCallback != NULL)
{
// User callback, registered via ImDrawList::AddCallback()
// (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.)
if (pcmd->UserCallback == ImDrawCallback_ResetRenderState)
ImGui_ImplDX11_SetupRenderState(draw_data, ctx);
else
pcmd->UserCallback(cmd_list, pcmd);
}
else
{
// Project scissor/clipping rectangles into framebuffer space
ImVec2 clip_min(pcmd->ClipRect.x - clip_off.x, pcmd->ClipRect.y - clip_off.y);
ImVec2 clip_max(pcmd->ClipRect.z - clip_off.x, pcmd->ClipRect.w - clip_off.y);
if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y)
continue;
// Apply scissor/clipping rectangle
const D3D11_RECT r = { (LONG)clip_min.x, (LONG)clip_min.y, (LONG)clip_max.x, (LONG)clip_max.y };
ctx->RSSetScissorRects(1, &r);
// Bind texture, Draw
ID3D11ShaderResourceView* texture_srv = (ID3D11ShaderResourceView*)pcmd->GetTexID();
ctx->PSSetShaderResources(0, 1, &texture_srv);
ctx->DrawIndexed(pcmd->ElemCount, pcmd->IdxOffset + global_idx_offset, pcmd->VtxOffset + global_vtx_offset);
}
}
global_idx_offset += cmd_list->IdxBuffer.Size;
global_vtx_offset += cmd_list->VtxBuffer.Size;
}
}
void ImGui_ImplDX11_CreateFontsTexture()
{
// Build texture atlas
ImGuiIO& io = ImGui::GetIO();
ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData();
unsigned char* pixels;
int width, height;
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
if (bd->pFontTextureView)
bd->pFontTextureView->Release();
// Upload texture to graphics system
{
D3D11_TEXTURE2D_DESC desc;
ZeroMemory(&desc, sizeof(desc));
desc.Width = width;
desc.Height = height;
desc.MipLevels = 1;
desc.ArraySize = 1;
desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
desc.SampleDesc.Count = 1;
desc.Usage = D3D11_USAGE_DEFAULT;
desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
desc.CPUAccessFlags = 0;
ID3D11Texture2D* pTexture = NULL;
D3D11_SUBRESOURCE_DATA subResource;
subResource.pSysMem = pixels;
subResource.SysMemPitch = desc.Width * 4;
subResource.SysMemSlicePitch = 0;
bd->pd3dDevice->CreateTexture2D(&desc, &subResource, &pTexture);
IM_ASSERT(pTexture != NULL);
// Create texture view
D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
ZeroMemory(&srvDesc, sizeof(srvDesc));
srvDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
srvDesc.Texture2D.MipLevels = desc.MipLevels;
srvDesc.Texture2D.MostDetailedMip = 0;
bd->pd3dDevice->CreateShaderResourceView(pTexture, &srvDesc, &bd->pFontTextureView);
pTexture->Release();
}
// Store our identifier
io.Fonts->SetTexID((ImTextureID)bd->pFontTextureView);
// Create texture sampler
// (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling)
if (!bd->pFontSampler)
{
D3D11_SAMPLER_DESC desc;
ZeroMemory(&desc, sizeof(desc));
desc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
desc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;
desc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;
desc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
desc.MipLODBias = 0.f;
desc.ComparisonFunc = D3D11_COMPARISON_ALWAYS;
desc.MinLOD = 0.f;
desc.MaxLOD = 0.f;
bd->pd3dDevice->CreateSamplerState(&desc, &bd->pFontSampler);
}
}
bool ImGui_ImplDX11_CreateDeviceObjects()
{
ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData();
if (!bd->pd3dDevice)
return false;
if (bd->pFontSampler)
ImGui_ImplDX11_InvalidateDeviceObjects();
// By using D3DCompile() from <d3dcompiler.h> / d3dcompiler.lib, we introduce a dependency to a given version of d3dcompiler_XX.dll (see D3DCOMPILER_DLL_A)
// If you would like to use this DX11 sample code but remove this dependency you can:
// 1) compile once, save the compiled shader blobs into a file or source code and pass them to CreateVertexShader()/CreatePixelShader() [preferred solution]
// 2) use code to detect any version of the DLL and grab a pointer to D3DCompile from the DLL.
// See https://github.com/ocornut/imgui/pull/638 for sources and details.
// Create the vertex shader
{
static const char* vertexShader =
"cbuffer vertexBuffer : register(b0) \
{\
float4x4 ProjectionMatrix; \
};\
struct VS_INPUT\
{\
float2 pos : POSITION;\
float4 col : COLOR0;\
float2 uv : TEXCOORD0;\
};\
\
struct PS_INPUT\
{\
float4 pos : SV_POSITION;\
float4 col : COLOR0;\
float2 uv : TEXCOORD0;\
};\
\
PS_INPUT main(VS_INPUT input)\
{\
PS_INPUT output;\
output.pos = mul( ProjectionMatrix, float4(input.pos.xy, 0.f, 1.f));\
output.col = input.col;\
output.uv = input.uv;\
return output;\
}";
ID3DBlob* vertexShaderBlob;
if (FAILED(D3DCompile(vertexShader, strlen(vertexShader), NULL, NULL, NULL, "main", "vs_4_0", 0, 0, &vertexShaderBlob, NULL)))
return false; // NB: Pass ID3DBlob* pErrorBlob to D3DCompile() to get error showing in (const char*)pErrorBlob->GetBufferPointer(). Make sure to Release() the blob!
if (bd->pd3dDevice->CreateVertexShader(vertexShaderBlob->GetBufferPointer(), vertexShaderBlob->GetBufferSize(), NULL, &bd->pVertexShader) != S_OK)
{
vertexShaderBlob->Release();
return false;
}
// Create the input layout
D3D11_INPUT_ELEMENT_DESC local_layout[] =
{
{ "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (UINT)IM_OFFSETOF(ImDrawVert, pos), D3D11_INPUT_PER_VERTEX_DATA, 0 },
{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (UINT)IM_OFFSETOF(ImDrawVert, uv), D3D11_INPUT_PER_VERTEX_DATA, 0 },
{ "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, (UINT)IM_OFFSETOF(ImDrawVert, col), D3D11_INPUT_PER_VERTEX_DATA, 0 },
};
if (bd->pd3dDevice->CreateInputLayout(local_layout, 3, vertexShaderBlob->GetBufferPointer(), vertexShaderBlob->GetBufferSize(), &bd->pInputLayout) != S_OK)
{
vertexShaderBlob->Release();
return false;
}
vertexShaderBlob->Release();
// Create the constant buffer
{
D3D11_BUFFER_DESC desc;
desc.ByteWidth = sizeof(VERTEX_CONSTANT_BUFFER);
desc.Usage = D3D11_USAGE_DYNAMIC;
desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
desc.MiscFlags = 0;
bd->pd3dDevice->CreateBuffer(&desc, NULL, &bd->pVertexConstantBuffer);
}
}
// Create the pixel shader
{
static const char* pixelShader =
"struct PS_INPUT\
{\
float4 pos : SV_POSITION;\
float4 col : COLOR0;\
float2 uv : TEXCOORD0;\
};\
sampler sampler0;\
Texture2D texture0;\
\
float4 main(PS_INPUT input) : SV_Target\
{\
float4 out_col = input.col * texture0.Sample(sampler0, input.uv); \
return out_col; \
}";
ID3DBlob* pixelShaderBlob;
if (FAILED(D3DCompile(pixelShader, strlen(pixelShader), NULL, NULL, NULL, "main", "ps_4_0", 0, 0, &pixelShaderBlob, NULL)))
return false; // NB: Pass ID3DBlob* pErrorBlob to D3DCompile() to get error showing in (const char*)pErrorBlob->GetBufferPointer(). Make sure to Release() the blob!
if (bd->pd3dDevice->CreatePixelShader(pixelShaderBlob->GetBufferPointer(), pixelShaderBlob->GetBufferSize(), NULL, &bd->pPixelShader) != S_OK)
{
pixelShaderBlob->Release();
return false;
}
pixelShaderBlob->Release();
}
// Create the blending setup
{
D3D11_BLEND_DESC desc;
ZeroMemory(&desc, sizeof(desc));
desc.AlphaToCoverageEnable = false;
desc.RenderTarget[0].BlendEnable = true;
desc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
desc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA;
desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
desc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;
bd->pd3dDevice->CreateBlendState(&desc, &bd->pBlendState);
}
// Create the rasterizer state
{
D3D11_RASTERIZER_DESC desc;
ZeroMemory(&desc, sizeof(desc));
desc.FillMode = D3D11_FILL_SOLID;
desc.CullMode = D3D11_CULL_NONE;
desc.ScissorEnable = true;
desc.DepthClipEnable = true;
bd->pd3dDevice->CreateRasterizerState(&desc, &bd->pRasterizerState);
}
// Create depth-stencil State
{
D3D11_DEPTH_STENCIL_DESC desc;
ZeroMemory(&desc, sizeof(desc));
desc.DepthEnable = false;
desc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL;
desc.DepthFunc = D3D11_COMPARISON_ALWAYS;
desc.StencilEnable = false;
desc.FrontFace.StencilFailOp = desc.FrontFace.StencilDepthFailOp = desc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;
desc.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS;
desc.BackFace = desc.FrontFace;
bd->pd3dDevice->CreateDepthStencilState(&desc, &bd->pDepthStencilState);
}
ImGui_ImplDX11_CreateFontsTexture();
return true;
}
void ImGui_ImplDX11_InvalidateDeviceObjects()
{
ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData();
if (!bd->pd3dDevice)
return;
if (bd->pFontSampler) { bd->pFontSampler->Release(); bd->pFontSampler = NULL; }
if (bd->pFontTextureView) { bd->pFontTextureView->Release(); bd->pFontTextureView = NULL; ImGui::GetIO().Fonts->SetTexID(NULL); } // We copied data->pFontTextureView to io.Fonts->TexID so let's clear that as well.
if (bd->pIB) { bd->pIB->Release(); bd->pIB = NULL; }
if (bd->pVB) { bd->pVB->Release(); bd->pVB = NULL; }
if (bd->pBlendState) { bd->pBlendState->Release(); bd->pBlendState = NULL; }
if (bd->pDepthStencilState) { bd->pDepthStencilState->Release(); bd->pDepthStencilState = NULL; }
if (bd->pRasterizerState) { bd->pRasterizerState->Release(); bd->pRasterizerState = NULL; }
if (bd->pPixelShader) { bd->pPixelShader->Release(); bd->pPixelShader = NULL; }
if (bd->pVertexConstantBuffer) { bd->pVertexConstantBuffer->Release(); bd->pVertexConstantBuffer = NULL; }
if (bd->pInputLayout) { bd->pInputLayout->Release(); bd->pInputLayout = NULL; }
if (bd->pVertexShader) { bd->pVertexShader->Release(); bd->pVertexShader = NULL; }
}
bool ImGui_ImplDX11_Init(ID3D11Device* device, ID3D11DeviceContext* device_context)
{
ImGuiIO& io = ImGui::GetIO();
IM_ASSERT(io.BackendRendererUserData == NULL && "Already initialized a renderer backend!");
// Setup backend capabilities flags
ImGui_ImplDX11_Data* bd = IM_NEW(ImGui_ImplDX11_Data)();
io.BackendRendererUserData = (void*)bd;
io.BackendRendererName = "imgui_impl_dx11";
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
// Get factory from device
IDXGIDevice* pDXGIDevice = NULL;
IDXGIAdapter* pDXGIAdapter = NULL;
IDXGIFactory* pFactory = NULL;
if (device->QueryInterface(IID_PPV_ARGS(&pDXGIDevice)) == S_OK)
if (pDXGIDevice->GetParent(IID_PPV_ARGS(&pDXGIAdapter)) == S_OK)
if (pDXGIAdapter->GetParent(IID_PPV_ARGS(&pFactory)) == S_OK)
{
bd->pd3dDevice = device;
bd->pd3dDeviceContext = device_context;
bd->pFactory = pFactory;
}
if (pDXGIDevice) pDXGIDevice->Release();
if (pDXGIAdapter) pDXGIAdapter->Release();
bd->pd3dDevice->AddRef();
bd->pd3dDeviceContext->AddRef();
return ImGui_ImplDX11_CreateDeviceObjects();
}
void ImGui_ImplDX11_Shutdown()
{
ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData();
IM_ASSERT(bd != NULL && "No renderer backend to shutdown, or already shutdown?");
if (bd == NULL)
return;
ImGui_ImplDX11_InvalidateDeviceObjects();
if (bd->pFactory) { bd->pFactory->Release(); }
if (bd->pd3dDevice) { bd->pd3dDevice->Release(); }
if (bd->pd3dDeviceContext) { bd->pd3dDeviceContext->Release(); }
ImGuiIO& io = ImGui::GetIO();
io.BackendRendererName = NULL;
io.BackendRendererUserData = NULL;
IM_DELETE(bd);
}
-17
View File
@@ -1,17 +0,0 @@
// dear imgui: Renderer Backend for DirectX11
// This needs to be used along with a Platform Backend (e.g. Win32)
#pragma once
#include "imgui.h" // IMGUI_IMPL_API
struct ID3D11Device;
struct ID3D11DeviceContext;
bool ImGui_ImplDX11_Init(ID3D11Device* device, ID3D11DeviceContext* device_context);
void ImGui_ImplDX11_Shutdown();
void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data);
// Use if you want to reset your rendering device without losing Dear ImGui state.
void ImGui_ImplDX11_InvalidateDeviceObjects();
bool ImGui_ImplDX11_CreateDeviceObjects();
void ImGui_ImplDX11_CreateFontsTexture();
-536
View File
@@ -1,536 +0,0 @@
// dear imgui: Renderer Backend for DirectX12
// This needs to be used along with a Platform Backend (e.g. Win32)
// Implemented features:
// [X] Renderer: User texture binding. Use 'D3D12_GPU_DESCRIPTOR_HANDLE' as ImTextureID. Read the FAQ about ImTextureID!
// [X] Renderer: Support for large meshes (64k+ vertices) with 16-bit indices.
// Important: to compile on 32-bit systems, this backend requires code to be compiled with '#define ImTextureID ImU64'.
// This is because we need ImTextureID to carry a 64-bit value and by default ImTextureID is defined as void*.
// To build this on 32-bit systems:
// - [Solution 1] IDE/msbuild: in "Properties/C++/Preprocessor Definitions" add 'ImTextureID=ImU64' (this is what we do in the 'example_win32_direct12/example_win32_direct12.vcxproj' project file)
// - [Solution 2] IDE/msbuild: in "Properties/C++/Preprocessor Definitions" add 'IMGUI_USER_CONFIG="my_imgui_config.h"' and inside 'my_imgui_config.h' add '#define ImTextureID ImU64' and as many other options as you like.
// - [Solution 3] IDE/msbuild: edit imconfig.h and add '#define ImTextureID ImU64' (prefer solution 2 to create your own config file!)
// - [Solution 4] command-line: add '/D ImTextureID=ImU64' to your cl.exe command-line (this is what we do in the example_win32_direct12/build_win32.bat file)
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
// Read online: https://github.com/ocornut/imgui/tree/master/docs
// CHANGELOG
// (minor and older changes stripped away, please see git history for details)
// 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX).
// 2021-05-19: DirectX12: Replaced direct access to ImDrawCmd::TextureId with a call to ImDrawCmd::GetTexID(). (will become a requirement)
// 2021-02-18: DirectX12: Change blending equation to preserve alpha in output buffer.
// 2021-01-11: DirectX12: Improve Windows 7 compatibility (for D3D12On7) by loading d3d12.dll dynamically.
// 2020-09-16: DirectX12: Avoid rendering calls with zero-sized scissor rectangle since it generates a validation layer warning.
// 2020-09-08: DirectX12: Clarified support for building on 32-bit systems by redefining ImTextureID.
// 2019-10-18: DirectX12: *BREAKING CHANGE* Added extra ID3D12DescriptorHeap parameter to ImGui_ImplDX12_Init() function.
// 2019-05-29: DirectX12: Added support for large mesh (64K+ vertices), enable ImGuiBackendFlags_RendererHasVtxOffset flag.
// 2019-04-30: DirectX12: Added support for special ImDrawCallback_ResetRenderState callback to reset render state.
// 2019-03-29: Misc: Various minor tidying up.
// 2018-12-03: Misc: Added #pragma comment statement to automatically link with d3dcompiler.lib when using D3DCompile().
// 2018-11-30: Misc: Setting up io.BackendRendererName so it can be displayed in the About Window.
// 2018-06-12: DirectX12: Moved the ID3D12GraphicsCommandList* parameter from NewFrame() to RenderDrawData().
// 2018-06-08: Misc: Extracted imgui_impl_dx12.cpp/.h away from the old combined DX12+Win32 example.
// 2018-06-08: DirectX12: Use draw_data->DisplayPos and draw_data->DisplaySize to setup projection matrix and clipping rectangle (to ease support for future multi-viewport).
// 2018-02-22: Merged into master with all Win32 code synchronized to other examples.
#include "PrecompiledHeader.h"
#include "common/Assertions.h"
#include "common/D3D12/Context.h"
#include "common/D3D12/Texture.h"
#include "common/D3D12/StreamBuffer.h"
#include "common/RedtapeWindows.h"
#include "imgui.h"
#include "imgui_impl_dx12.h"
// DirectX
#include <d3d12.h>
#include <dxgi1_4.h>
#include <d3dcompiler.h>
#ifdef _MSC_VER
#pragma comment(lib, "d3dcompiler") // Automatically link with d3dcompiler.lib as we are using D3DCompile() below.
#endif
// If we're doing more than this... wtf?
static constexpr u32 VERTEX_BUFFER_SIZE = 8 * 1024 * 1024;
static constexpr u32 INDEX_BUFFER_SIZE = 4 * 1024 * 1024;
struct ImGui_ImplDX12_Data
{
D3D12::StreamBuffer VertexStreamBuffer;
D3D12::StreamBuffer IndexStreamBuffer;
D3D12::Texture FontTexture;
ID3D12RootSignature* pRootSignature = nullptr;
ID3D12PipelineState* pPipelineState = nullptr;
DXGI_FORMAT RTVFormat = DXGI_FORMAT_UNKNOWN;
};
struct VERTEX_CONSTANT_BUFFER
{
float mvp[4][4];
};
// Backend data stored in io.BackendRendererUserData to allow support for multiple Dear ImGui contexts
// It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts.
static ImGui_ImplDX12_Data* ImGui_ImplDX12_GetBackendData()
{
return ImGui::GetCurrentContext() ? (ImGui_ImplDX12_Data*)ImGui::GetIO().BackendRendererUserData : NULL;
}
// Functions
static void ImGui_ImplDX12_SetupRenderState(ImDrawData* draw_data, ID3D12GraphicsCommandList* ctx)
{
ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
// Setup orthographic projection matrix into our constant buffer
// Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right).
VERTEX_CONSTANT_BUFFER vertex_constant_buffer;
{
float L = draw_data->DisplayPos.x;
float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x;
float T = draw_data->DisplayPos.y;
float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y;
float mvp[4][4] =
{
{ 2.0f/(R-L), 0.0f, 0.0f, 0.0f },
{ 0.0f, 2.0f/(T-B), 0.0f, 0.0f },
{ 0.0f, 0.0f, 0.5f, 0.0f },
{ (R+L)/(L-R), (T+B)/(B-T), 0.5f, 1.0f },
};
memcpy(&vertex_constant_buffer.mvp, mvp, sizeof(mvp));
}
// Setup viewport
D3D12_VIEWPORT vp;
memset(&vp, 0, sizeof(D3D12_VIEWPORT));
vp.Width = draw_data->DisplaySize.x;
vp.Height = draw_data->DisplaySize.y;
vp.MinDepth = 0.0f;
vp.MaxDepth = 1.0f;
vp.TopLeftX = vp.TopLeftY = 0.0f;
ctx->RSSetViewports(1, &vp);
// Bind shader and vertex buffers
unsigned int stride = sizeof(ImDrawVert);
D3D12_VERTEX_BUFFER_VIEW vbv;
memset(&vbv, 0, sizeof(D3D12_VERTEX_BUFFER_VIEW));
vbv.BufferLocation = bd->VertexStreamBuffer.GetCurrentGPUPointer();
vbv.SizeInBytes = bd->VertexStreamBuffer.GetCurrentSpace();
vbv.StrideInBytes = stride;
ctx->IASetVertexBuffers(0, 1, &vbv);
D3D12_INDEX_BUFFER_VIEW ibv;
memset(&ibv, 0, sizeof(D3D12_INDEX_BUFFER_VIEW));
ibv.BufferLocation = bd->IndexStreamBuffer.GetCurrentGPUPointer();
ibv.SizeInBytes = bd->IndexStreamBuffer.GetCurrentSpace();
ibv.Format = sizeof(ImDrawIdx) == 2 ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R32_UINT;
ctx->IASetIndexBuffer(&ibv);
ctx->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
ctx->SetPipelineState(bd->pPipelineState);
ctx->SetGraphicsRootSignature(bd->pRootSignature);
ctx->SetGraphicsRoot32BitConstants(0, 16, &vertex_constant_buffer, 0);
// Setup blend factor
const float blend_factor[4] = { 0.f, 0.f, 0.f, 0.f };
ctx->OMSetBlendFactor(blend_factor);
}
template<typename T>
static inline void SafeRelease(T*& res)
{
if (res)
res->Release();
res = NULL;
}
// Render function
void ImGui_ImplDX12_RenderDrawData(ImDrawData* draw_data)
{
// Avoid rendering when minimized
if (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f)
return;
// FIXME: I'm assuming that this only gets called once per frame!
// If not, we can't just re-allocate the IB or VB, we'll have to do a proper allocator.
ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
const u32 needed_vb = draw_data->TotalVtxCount * sizeof(ImDrawVert);
const u32 needed_ib = draw_data->TotalIdxCount * sizeof(ImDrawIdx);
if (!bd->VertexStreamBuffer.ReserveMemory(needed_vb, sizeof(ImDrawVert)) ||
!bd->IndexStreamBuffer.ReserveMemory(needed_ib, sizeof(ImDrawIdx)))
{
g_d3d12_context->ExecuteCommandList(D3D12::Context::WaitType::None);
if (!bd->VertexStreamBuffer.ReserveMemory(needed_vb, sizeof(ImDrawVert)) ||
!bd->IndexStreamBuffer.ReserveMemory(needed_ib, sizeof(ImDrawIdx)))
{
pxFailRel("Failed to allocate space for imgui vertices/indices");
}
}
// Upload vertex/index data into a single contiguous GPU buffer
ImDrawVert* vtx_dst = (ImDrawVert*)bd->VertexStreamBuffer.GetCurrentHostPointer();
ImDrawIdx* idx_dst = (ImDrawIdx*)bd->IndexStreamBuffer.GetCurrentHostPointer();
for (int n = 0; n < draw_data->CmdListsCount; n++)
{
const ImDrawList* cmd_list = draw_data->CmdLists[n];
memcpy(vtx_dst, cmd_list->VtxBuffer.Data, cmd_list->VtxBuffer.Size * sizeof(ImDrawVert));
memcpy(idx_dst, cmd_list->IdxBuffer.Data, cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx));
vtx_dst += cmd_list->VtxBuffer.Size;
idx_dst += cmd_list->IdxBuffer.Size;
}
// Setup desired DX state (must happen before commit, because it uses the offsets)
ID3D12GraphicsCommandList* ctx = g_d3d12_context->GetCommandList();
ImGui_ImplDX12_SetupRenderState(draw_data, ctx);
bd->VertexStreamBuffer.CommitMemory(needed_vb);
bd->IndexStreamBuffer.CommitMemory(needed_ib);
// Render command lists
// (Because we merged all buffers into a single one, we maintain our own offset into them)
int global_vtx_offset = 0;
int global_idx_offset = 0;
ImVec2 clip_off = draw_data->DisplayPos;
const D3D12::Texture* last_texture = nullptr;
for (int n = 0; n < draw_data->CmdListsCount; n++)
{
const ImDrawList* cmd_list = draw_data->CmdLists[n];
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
{
const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
if (pcmd->UserCallback != NULL)
{
// User callback, registered via ImDrawList::AddCallback()
// (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.)
if (pcmd->UserCallback == ImDrawCallback_ResetRenderState)
ImGui_ImplDX12_SetupRenderState(draw_data, ctx);
else
pcmd->UserCallback(cmd_list, pcmd);
}
else
{
// Project scissor/clipping rectangles into framebuffer space
ImVec2 clip_min(pcmd->ClipRect.x - clip_off.x, pcmd->ClipRect.y - clip_off.y);
ImVec2 clip_max(pcmd->ClipRect.z - clip_off.x, pcmd->ClipRect.w - clip_off.y);
if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y)
continue;
// Apply Scissor/clipping rectangle, Bind texture, Draw
const D3D12_RECT r = { (LONG)clip_min.x, (LONG)clip_min.y, (LONG)clip_max.x, (LONG)clip_max.y };
const D3D12::Texture* tex = (D3D12::Texture*)pcmd->GetTexID();
if (tex && last_texture != tex)
{
D3D12::DescriptorHandle handle;
if (!g_d3d12_context->GetDescriptorAllocator().Allocate(1, &handle))
{
// ugh.
g_d3d12_context->ExecuteCommandList(D3D12::Context::WaitType::None);
ctx = g_d3d12_context->GetCommandList();
ImGui_ImplDX12_SetupRenderState(draw_data, ctx);
if (!g_d3d12_context->GetDescriptorAllocator().Allocate(1, &handle))
pxFailRel("Failed to allocate descriptor after cmdlist kick");
}
g_d3d12_context->GetDevice()->CopyDescriptorsSimple(1, handle, tex->GetSRVDescriptor(), D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
ctx->SetGraphicsRootDescriptorTable(1, handle);
last_texture = tex;
}
ctx->RSSetScissorRects(1, &r);
ctx->DrawIndexedInstanced(pcmd->ElemCount, 1, pcmd->IdxOffset + global_idx_offset, pcmd->VtxOffset + global_vtx_offset, 0);
}
}
global_idx_offset += cmd_list->IdxBuffer.Size;
global_vtx_offset += cmd_list->VtxBuffer.Size;
}
}
bool ImGui_ImplDX12_CreateFontsTexture()
{
// Build texture atlas
ImGuiIO& io = ImGui::GetIO();
ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
unsigned char* pixels;
int width, height;
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
// Upload texture to graphics system
if (bd->FontTexture.GetWidth() != width || bd->FontTexture.GetHeight() != height)
{
if (!bd->FontTexture.Create(width, height, 1, DXGI_FORMAT_R8G8B8A8_UNORM,
DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN,
D3D12_RESOURCE_FLAG_NONE))
{
return false;
}
}
if (!bd->FontTexture.LoadData(g_d3d12_context->GetInitCommandList(), 0, 0, 0, width, height, pixels, width * sizeof(u32)))
return false;
io.Fonts->SetTexID((ImTextureID)&bd->FontTexture);
return true;
}
bool ImGui_ImplDX12_CreateDeviceObjects()
{
ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
if (bd->pPipelineState)
ImGui_ImplDX12_DestroyDeviceObjects();
// Create the root signature
{
D3D12_DESCRIPTOR_RANGE descRange = {};
descRange.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
descRange.NumDescriptors = 1;
descRange.BaseShaderRegister = 0;
descRange.RegisterSpace = 0;
descRange.OffsetInDescriptorsFromTableStart = 0;
D3D12_ROOT_PARAMETER param[2] = {};
param[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS;
param[0].Constants.ShaderRegister = 0;
param[0].Constants.RegisterSpace = 0;
param[0].Constants.Num32BitValues = 16;
param[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_VERTEX;
param[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
param[1].DescriptorTable.NumDescriptorRanges = 1;
param[1].DescriptorTable.pDescriptorRanges = &descRange;
param[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
// Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling.
D3D12_STATIC_SAMPLER_DESC staticSampler = {};
staticSampler.Filter = D3D12_FILTER_MIN_MAG_MIP_LINEAR;
staticSampler.AddressU = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
staticSampler.AddressV = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
staticSampler.AddressW = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
staticSampler.MipLODBias = 0.f;
staticSampler.MaxAnisotropy = 0;
staticSampler.ComparisonFunc = D3D12_COMPARISON_FUNC_ALWAYS;
staticSampler.BorderColor = D3D12_STATIC_BORDER_COLOR_TRANSPARENT_BLACK;
staticSampler.MinLOD = 0.f;
staticSampler.MaxLOD = 0.f;
staticSampler.ShaderRegister = 0;
staticSampler.RegisterSpace = 0;
staticSampler.ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
D3D12_ROOT_SIGNATURE_DESC desc = {};
desc.NumParameters = _countof(param);
desc.pParameters = param;
desc.NumStaticSamplers = 1;
desc.pStaticSamplers = &staticSampler;
desc.Flags =
D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT |
D3D12_ROOT_SIGNATURE_FLAG_DENY_HULL_SHADER_ROOT_ACCESS |
D3D12_ROOT_SIGNATURE_FLAG_DENY_DOMAIN_SHADER_ROOT_ACCESS |
D3D12_ROOT_SIGNATURE_FLAG_DENY_GEOMETRY_SHADER_ROOT_ACCESS;
auto blob = g_d3d12_context->SerializeRootSignature(&desc);
if (!blob)
return false;
g_d3d12_context->GetDevice()->CreateRootSignature(0, blob->GetBufferPointer(), blob->GetBufferSize(), IID_PPV_ARGS(&bd->pRootSignature));
}
// By using D3DCompile() from <d3dcompiler.h> / d3dcompiler.lib, we introduce a dependency to a given version of d3dcompiler_XX.dll (see D3DCOMPILER_DLL_A)
// If you would like to use this DX12 sample code but remove this dependency you can:
// 1) compile once, save the compiled shader blobs into a file or source code and pass them to CreateVertexShader()/CreatePixelShader() [preferred solution]
// 2) use code to detect any version of the DLL and grab a pointer to D3DCompile from the DLL.
// See https://github.com/ocornut/imgui/pull/638 for sources and details.
D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc;
memset(&psoDesc, 0, sizeof(D3D12_GRAPHICS_PIPELINE_STATE_DESC));
psoDesc.NodeMask = 1;
psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
psoDesc.pRootSignature = bd->pRootSignature;
psoDesc.SampleMask = UINT_MAX;
psoDesc.NumRenderTargets = 1;
psoDesc.RTVFormats[0] = bd->RTVFormat;
psoDesc.SampleDesc.Count = 1;
psoDesc.Flags = D3D12_PIPELINE_STATE_FLAG_NONE;
ID3DBlob* vertexShaderBlob;
ID3DBlob* pixelShaderBlob;
// Create the vertex shader
{
static const char* vertexShader =
"cbuffer vertexBuffer : register(b0) \
{\
float4x4 ProjectionMatrix; \
};\
struct VS_INPUT\
{\
float2 pos : POSITION;\
float4 col : COLOR0;\
float2 uv : TEXCOORD0;\
};\
\
struct PS_INPUT\
{\
float4 pos : SV_POSITION;\
float4 col : COLOR0;\
float2 uv : TEXCOORD0;\
};\
\
PS_INPUT main(VS_INPUT input)\
{\
PS_INPUT output;\
output.pos = mul( ProjectionMatrix, float4(input.pos.xy, 0.f, 1.f));\
output.col = input.col;\
output.uv = input.uv;\
return output;\
}";
if (FAILED(D3DCompile(vertexShader, strlen(vertexShader), NULL, NULL, NULL, "main", "vs_5_0", 0, 0, &vertexShaderBlob, NULL)))
return false; // NB: Pass ID3D10Blob* pErrorBlob to D3DCompile() to get error showing in (const char*)pErrorBlob->GetBufferPointer(). Make sure to Release() the blob!
psoDesc.VS = { vertexShaderBlob->GetBufferPointer(), vertexShaderBlob->GetBufferSize() };
// Create the input layout
static D3D12_INPUT_ELEMENT_DESC local_layout[] =
{
{ "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (UINT)IM_OFFSETOF(ImDrawVert, pos), D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (UINT)IM_OFFSETOF(ImDrawVert, uv), D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
{ "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, (UINT)IM_OFFSETOF(ImDrawVert, col), D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
};
psoDesc.InputLayout = { local_layout, 3 };
}
// Create the pixel shader
{
static const char* pixelShader =
"struct PS_INPUT\
{\
float4 pos : SV_POSITION;\
float4 col : COLOR0;\
float2 uv : TEXCOORD0;\
};\
SamplerState sampler0 : register(s0);\
Texture2D texture0 : register(t0);\
\
float4 main(PS_INPUT input) : SV_Target\
{\
float4 out_col = input.col * texture0.Sample(sampler0, input.uv); \
return out_col; \
}";
if (FAILED(D3DCompile(pixelShader, strlen(pixelShader), NULL, NULL, NULL, "main", "ps_5_0", 0, 0, &pixelShaderBlob, NULL)))
{
vertexShaderBlob->Release();
return false; // NB: Pass ID3D10Blob* pErrorBlob to D3DCompile() to get error showing in (const char*)pErrorBlob->GetBufferPointer(). Make sure to Release() the blob!
}
psoDesc.PS = { pixelShaderBlob->GetBufferPointer(), pixelShaderBlob->GetBufferSize() };
}
// Create the blending setup
{
D3D12_BLEND_DESC& desc = psoDesc.BlendState;
desc.AlphaToCoverageEnable = false;
desc.RenderTarget[0].BlendEnable = true;
desc.RenderTarget[0].SrcBlend = D3D12_BLEND_SRC_ALPHA;
desc.RenderTarget[0].DestBlend = D3D12_BLEND_INV_SRC_ALPHA;
desc.RenderTarget[0].BlendOp = D3D12_BLEND_OP_ADD;
desc.RenderTarget[0].SrcBlendAlpha = D3D12_BLEND_ONE;
desc.RenderTarget[0].DestBlendAlpha = D3D12_BLEND_INV_SRC_ALPHA;
desc.RenderTarget[0].BlendOpAlpha = D3D12_BLEND_OP_ADD;
desc.RenderTarget[0].RenderTargetWriteMask = D3D12_COLOR_WRITE_ENABLE_ALL;
}
// Create the rasterizer state
{
D3D12_RASTERIZER_DESC& desc = psoDesc.RasterizerState;
desc.FillMode = D3D12_FILL_MODE_SOLID;
desc.CullMode = D3D12_CULL_MODE_NONE;
desc.FrontCounterClockwise = FALSE;
desc.DepthBias = D3D12_DEFAULT_DEPTH_BIAS;
desc.DepthBiasClamp = D3D12_DEFAULT_DEPTH_BIAS_CLAMP;
desc.SlopeScaledDepthBias = D3D12_DEFAULT_SLOPE_SCALED_DEPTH_BIAS;
desc.DepthClipEnable = true;
desc.MultisampleEnable = FALSE;
desc.AntialiasedLineEnable = FALSE;
desc.ForcedSampleCount = 0;
desc.ConservativeRaster = D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF;
}
// Create depth-stencil State
{
D3D12_DEPTH_STENCIL_DESC& desc = psoDesc.DepthStencilState;
desc.DepthEnable = false;
desc.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL;
desc.DepthFunc = D3D12_COMPARISON_FUNC_ALWAYS;
desc.StencilEnable = false;
desc.FrontFace.StencilFailOp = desc.FrontFace.StencilDepthFailOp = desc.FrontFace.StencilPassOp = D3D12_STENCIL_OP_KEEP;
desc.FrontFace.StencilFunc = D3D12_COMPARISON_FUNC_ALWAYS;
desc.BackFace = desc.FrontFace;
}
HRESULT result_pipeline_state = g_d3d12_context->GetDevice()->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(&bd->pPipelineState));
vertexShaderBlob->Release();
pixelShaderBlob->Release();
if (result_pipeline_state != S_OK)
return false;
return true;
}
void ImGui_ImplDX12_DestroyDeviceObjects()
{
ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
if (!bd)
return;
ImGuiIO& io = ImGui::GetIO();
SafeRelease(bd->pRootSignature);
SafeRelease(bd->pPipelineState);
bd->FontTexture.Destroy(false);
bd->VertexStreamBuffer.Destroy(false);
bd->IndexStreamBuffer.Destroy(false);
io.Fonts->SetTexID(NULL); // We copied bd->pFontTextureView to io.Fonts->TexID so let's clear that as well.
}
bool ImGui_ImplDX12_Init(DXGI_FORMAT rtv_format)
{
ImGuiIO& io = ImGui::GetIO();
IM_ASSERT(io.BackendRendererUserData == NULL && "Already initialized a renderer backend!");
// Setup backend capabilities flags
ImGui_ImplDX12_Data* bd = IM_NEW(ImGui_ImplDX12_Data)();
io.BackendRendererUserData = (void*)bd;
io.BackendRendererName = "imgui_impl_dx12";
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
bd->RTVFormat = rtv_format;
if (!bd->VertexStreamBuffer.Create(VERTEX_BUFFER_SIZE) || !bd->IndexStreamBuffer.Create(INDEX_BUFFER_SIZE))
return false;
return ImGui_ImplDX12_CreateDeviceObjects();
}
void ImGui_ImplDX12_Shutdown()
{
ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
IM_ASSERT(bd != NULL && "No renderer backend to shutdown, or already shutdown?");
ImGuiIO& io = ImGui::GetIO();
ImGui_ImplDX12_DestroyDeviceObjects();
io.BackendRendererName = NULL;
io.BackendRendererUserData = NULL;
IM_DELETE(bd);
}
void ImGui_ImplDX12_NewFrame()
{
ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
IM_ASSERT(bd != NULL && "Did you call ImGui_ImplDX12_Init()?");
if (!bd->pPipelineState)
ImGui_ImplDX12_CreateDeviceObjects();
}
-14
View File
@@ -1,14 +0,0 @@
// dear imgui: Renderer Backend for DirectX12
// This needs to be used along with a Platform Backend (e.g. Win32)
#pragma once
#include "imgui.h" // IMGUI_IMPL_API
bool ImGui_ImplDX12_Init(DXGI_FORMAT rtv_format);
void ImGui_ImplDX12_Shutdown();
void ImGui_ImplDX12_RenderDrawData(ImDrawData* draw_data);
// Use if you want to reset your rendering device without losing Dear ImGui state.
void ImGui_ImplDX12_DestroyDeviceObjects();
bool ImGui_ImplDX12_CreateDeviceObjects();
bool ImGui_ImplDX12_CreateFontsTexture();
-577
View File
@@ -1,577 +0,0 @@
// dear imgui: Renderer Backend for modern OpenGL with shaders / programmatic pipeline
// - Desktop GL: 2.x 3.x 4.x
// - Embedded GL: ES 2.0 (WebGL 1.0), ES 3.0 (WebGL 2.0)
// This needs to be used along with a Platform Backend (e.g. GLFW, SDL, Win32, custom..)
// Implemented features:
// [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID!
// [x] Renderer: Desktop GL only: Support for large meshes (64k+ vertices) with 16-bit indices.
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
// Read online: https://github.com/ocornut/imgui/tree/master/docs
// CHANGELOG
// (minor and older changes stripped away, please see git history for details)
// 2022-05-13: OpenGL: Fix state corruption on OpenGL ES 2.0 due to not preserving GL_ELEMENT_ARRAY_BUFFER_BINDING and vertex attribute states.
// 2021-12-15: OpenGL: Using buffer orphaning + glBufferSubData(), seems to fix leaks with multi-viewports with some Intel HD drivers.
// 2021-08-23: OpenGL: Fixed ES 3.0 shader ("#version 300 es") use normal precision floats to avoid wobbly rendering at HD resolutions.
// 2021-08-19: OpenGL: Embed and use our own minimal GL loader (imgui_impl_opengl3_loader.h), removing requirement and support for third-party loader.
// 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX).
// 2021-06-25: OpenGL: Use OES_vertex_array extension on Emscripten + backup/restore current state.
// 2021-06-21: OpenGL: Destroy individual vertex/fragment shader objects right after they are linked into the main shader.
// 2021-05-24: OpenGL: Access GL_CLIP_ORIGIN when "GL_ARB_clip_control" extension is detected, inside of just OpenGL 4.5 version.
// 2021-05-19: OpenGL: Replaced direct access to ImDrawCmd::TextureId with a call to ImDrawCmd::GetTexID(). (will become a requirement)
// 2021-04-06: OpenGL: Don't try to read GL_CLIP_ORIGIN unless we're OpenGL 4.5 or greater.
// 2021-02-18: OpenGL: Change blending equation to preserve alpha in output buffer.
// 2021-01-03: OpenGL: Backup, setup and restore GL_STENCIL_TEST state.
// 2020-10-23: OpenGL: Backup, setup and restore GL_PRIMITIVE_RESTART state.
// 2020-10-15: OpenGL: Use glGetString(GL_VERSION) instead of glGetIntegerv(GL_MAJOR_VERSION, ...) when the later returns zero (e.g. Desktop GL 2.x)
// 2020-09-17: OpenGL: Fix to avoid compiling/calling glBindSampler() on ES or pre 3.3 context which have the defines set by a loader.
// 2020-07-10: OpenGL: Added support for glad2 OpenGL loader.
// 2020-05-08: OpenGL: Made default GLSL version 150 (instead of 130) on OSX.
// 2020-04-21: OpenGL: Fixed handling of glClipControl(GL_UPPER_LEFT) by inverting projection matrix.
// 2020-04-12: OpenGL: Fixed context version check mistakenly testing for 4.0+ instead of 3.2+ to enable ImGuiBackendFlags_RendererHasVtxOffset.
// 2020-03-24: OpenGL: Added support for glbinding 2.x OpenGL loader.
// 2020-01-07: OpenGL: Added support for glbinding 3.x OpenGL loader.
// 2019-10-25: OpenGL: Using a combination of GL define and runtime GL version to decide whether to use glDrawElementsBaseVertex(). Fix building with pre-3.2 GL loaders.
// 2019-09-22: OpenGL: Detect default GL loader using __has_include compiler facility.
// 2019-09-16: OpenGL: Tweak initialization code to allow application calling ImGui_ImplOpenGL3_CreateFontsTexture() before the first NewFrame() call.
// 2019-05-29: OpenGL: Desktop GL only: Added support for large mesh (64K+ vertices), enable ImGuiBackendFlags_RendererHasVtxOffset flag.
// 2019-04-30: OpenGL: Added support for special ImDrawCallback_ResetRenderState callback to reset render state.
// 2019-03-29: OpenGL: Not calling glBindBuffer more than necessary in the render loop.
// 2019-03-15: OpenGL: Added a GL call + comments in ImGui_ImplOpenGL3_Init() to detect uninitialized GL function loaders early.
// 2019-03-03: OpenGL: Fix support for ES 2.0 (WebGL 1.0).
// 2019-02-20: OpenGL: Fix for OSX not supporting OpenGL 4.5, we don't try to read GL_CLIP_ORIGIN even if defined by the headers/loader.
// 2019-02-11: OpenGL: Projecting clipping rectangles correctly using draw_data->FramebufferScale to allow multi-viewports for retina display.
// 2019-02-01: OpenGL: Using GLSL 410 shaders for any version over 410 (e.g. 430, 450).
// 2018-11-30: Misc: Setting up io.BackendRendererName so it can be displayed in the About Window.
// 2018-11-13: OpenGL: Support for GL 4.5's glClipControl(GL_UPPER_LEFT) / GL_CLIP_ORIGIN.
// 2018-08-29: OpenGL: Added support for more OpenGL loaders: glew and glad, with comments indicative that any loader can be used.
// 2018-08-09: OpenGL: Default to OpenGL ES 3 on iOS and Android. GLSL version default to "#version 300 ES".
// 2018-07-30: OpenGL: Support for GLSL 300 ES and 410 core. Fixes for Emscripten compilation.
// 2018-07-10: OpenGL: Support for more GLSL versions (based on the GLSL version string). Added error output when shaders fail to compile/link.
// 2018-06-08: Misc: Extracted imgui_impl_opengl3.cpp/.h away from the old combined GLFW/SDL+OpenGL3 examples.
// 2018-06-08: OpenGL: Use draw_data->DisplayPos and draw_data->DisplaySize to setup projection matrix and clipping rectangle.
// 2018-05-25: OpenGL: Removed unnecessary backup/restore of GL_ELEMENT_ARRAY_BUFFER_BINDING since this is part of the VAO state.
// 2018-05-14: OpenGL: Making the call to glBindSampler() optional so 3.2 context won't fail if the function is a NULL pointer.
// 2018-03-06: OpenGL: Added const char* glsl_version parameter to ImGui_ImplOpenGL3_Init() so user can override the GLSL version e.g. "#version 150".
// 2018-02-23: OpenGL: Create the VAO in the render function so the setup can more easily be used with multiple shared GL context.
// 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplSdlGL3_RenderDrawData() in the .h file so you can call it yourself.
// 2018-01-07: OpenGL: Changed GLSL shader version from 330 to 150.
// 2017-09-01: OpenGL: Save and restore current bound sampler. Save and restore current polygon mode.
// 2017-05-01: OpenGL: Fixed save and restore of current blend func state.
// 2017-05-01: OpenGL: Fixed save and restore of current GL_ACTIVE_TEXTURE.
// 2016-09-05: OpenGL: Fixed save and restore of current scissor rectangle.
// 2016-07-29: OpenGL: Explicitly setting GL_UNPACK_ROW_LENGTH to reduce issues because SDL changes it. (#752)
//----------------------------------------
// OpenGL GLSL GLSL
// version version string
//----------------------------------------
// 2.0 110 "#version 110"
// 2.1 120 "#version 120"
// 3.0 130 "#version 130"
// 3.1 140 "#version 140"
// 3.2 150 "#version 150"
// 3.3 330 "#version 330 core"
// 4.0 400 "#version 400 core"
// 4.1 410 "#version 410 core"
// 4.2 420 "#version 410 core"
// 4.3 430 "#version 430 core"
// ES 2.0 100 "#version 100" = WebGL 1.0
// ES 3.0 300 "#version 300 es" = WebGL 2.0
//----------------------------------------
#include "PrecompiledHeader.h"
#include "imgui.h"
#include "imgui_impl_opengl3.h"
#include <stdio.h>
#if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier
#include <stddef.h> // intptr_t
#else
#include <stdint.h> // intptr_t
#endif
// Clang warnings with -Weverything
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast
#pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness
#if __has_warning("-Wzero-as-null-pointer-constant")
#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
#endif
#endif
// GL includes
#include <glad.h>
// OpenGL Data
struct ImGui_ImplOpenGL3_Data
{
GLuint GlVersion; // Extracted at runtime using GL_MAJOR_VERSION, GL_MINOR_VERSION queries (e.g. 320 for GL 3.2)
char GlslVersionString[32]; // Specified by user or detected based on compile time GL settings.
GLuint FontTexture;
GLuint ShaderHandle;
GLint AttribLocationTex; // Uniforms location
GLint AttribLocationProjMtx;
GLuint AttribLocationVtxPos; // Vertex attributes location
GLuint AttribLocationVtxUV;
GLuint AttribLocationVtxColor;
unsigned int VboHandle, ElementsHandle, VaoHandle, SamplerHandle;
GLsizeiptr VertexBufferSize;
GLsizeiptr IndexBufferSize;
ImGui_ImplOpenGL3_Data() { memset((void*)this, 0, sizeof(*this)); }
};
// Backend data stored in io.BackendRendererUserData to allow support for multiple Dear ImGui contexts
// It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts.
static ImGui_ImplOpenGL3_Data* ImGui_ImplOpenGL3_GetBackendData()
{
return ImGui::GetCurrentContext() ? (ImGui_ImplOpenGL3_Data*)ImGui::GetIO().BackendRendererUserData : NULL;
}
// Functions
bool ImGui_ImplOpenGL3_Init(const char* glsl_version)
{
ImGuiIO& io = ImGui::GetIO();
IM_ASSERT(io.BackendRendererUserData == NULL && "Already initialized a renderer backend!");
// Setup backend capabilities flags
ImGui_ImplOpenGL3_Data* bd = IM_NEW(ImGui_ImplOpenGL3_Data)();
io.BackendRendererUserData = (void*)bd;
io.BackendRendererName = "imgui_impl_opengl3";
// Query for GL version (e.g. 320 for GL 3.2)
GLint major = 0;
GLint minor = 0;
glGetIntegerv(GL_MAJOR_VERSION, &major);
glGetIntegerv(GL_MINOR_VERSION, &minor);
if (major == 0 && minor == 0)
{
// Query GL_VERSION in desktop GL 2.x, the string will start with "<major>.<minor>"
const char* gl_version = (const char*)glGetString(GL_VERSION);
sscanf(gl_version, "%d.%d", &major, &minor);
}
bd->GlVersion = (GLuint)(major * 100 + minor * 10);
if (bd->GlVersion >= 320)
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
// Store GLSL version string so we can refer to it later in case we recreate shaders.
// Note: GLSL version is NOT the same as GL version. Leave this to NULL if unsure.
if (glsl_version == NULL)
glsl_version = "#version 130";
IM_ASSERT((int)strlen(glsl_version) + 2 < IM_ARRAYSIZE(bd->GlslVersionString));
strcpy(bd->GlslVersionString, glsl_version);
strcat(bd->GlslVersionString, "\n");
return ImGui_ImplOpenGL3_CreateDeviceObjects();
}
void ImGui_ImplOpenGL3_Shutdown()
{
ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();
IM_ASSERT(bd != NULL && "No renderer backend to shutdown, or already shutdown?");
ImGuiIO& io = ImGui::GetIO();
ImGui_ImplOpenGL3_DestroyDeviceObjects();
io.BackendRendererName = NULL;
io.BackendRendererUserData = NULL;
IM_DELETE(bd);
}
static void ImGui_ImplOpenGL3_SetupRenderState(ImDrawData* draw_data, int fb_width, int fb_height)
{
ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();
// Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled, polygon fill
glEnable(GL_BLEND);
glBlendEquation(GL_FUNC_ADD);
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_CULL_FACE);
glDisable(GL_DEPTH_TEST);
glDisable(GL_STENCIL_TEST);
glEnable(GL_SCISSOR_TEST);
if (glPolygonMode)
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
// Setup viewport, orthographic projection matrix
// Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps.
glViewport(0, 0, (GLsizei)fb_width, (GLsizei)fb_height);
float L = draw_data->DisplayPos.x;
float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x;
float T = draw_data->DisplayPos.y;
float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y;
const float ortho_projection[4][4] =
{
{ 2.0f/(R-L), 0.0f, 0.0f, 0.0f },
{ 0.0f, 2.0f/(T-B), 0.0f, 0.0f },
{ 0.0f, 0.0f, -1.0f, 0.0f },
{ (R+L)/(L-R), (T+B)/(B-T), 0.0f, 1.0f },
};
glUseProgram(bd->ShaderHandle);
glUniform1i(bd->AttribLocationTex, 0);
glUniformMatrix4fv(bd->AttribLocationProjMtx, 1, GL_FALSE, &ortho_projection[0][0]);
glBindSampler(0, bd->SamplerHandle);
// Bind vertex/index buffers and setup attributes for ImDrawVert
glBindVertexArray(bd->VaoHandle);
glBindBuffer(GL_ARRAY_BUFFER, bd->VboHandle);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bd->ElementsHandle);
glEnableVertexAttribArray(bd->AttribLocationVtxPos);
glEnableVertexAttribArray(bd->AttribLocationVtxUV);
glEnableVertexAttribArray(bd->AttribLocationVtxColor);
glVertexAttribPointer(bd->AttribLocationVtxPos, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, pos));
glVertexAttribPointer(bd->AttribLocationVtxUV, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, uv));
glVertexAttribPointer(bd->AttribLocationVtxColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, col));
}
// OpenGL3 Render function.
// Note that this implementation is little overcomplicated because we are saving/setting up/restoring every OpenGL state explicitly.
// This is in order to be able to run within an OpenGL engine that doesn't do so.
void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data)
{
// Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates)
int fb_width = (int)(draw_data->DisplaySize.x * draw_data->FramebufferScale.x);
int fb_height = (int)(draw_data->DisplaySize.y * draw_data->FramebufferScale.y);
if (fb_width <= 0 || fb_height <= 0)
return;
ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();
// Setup desired GL state
ImGui_ImplOpenGL3_SetupRenderState(draw_data, fb_width, fb_height);
// Will project scissor/clipping rectangles into framebuffer space
ImVec2 clip_off = draw_data->DisplayPos; // (0,0) unless using multi-viewports
ImVec2 clip_scale = draw_data->FramebufferScale; // (1,1) unless using retina display which are often (2,2)
// Render command lists
for (int n = 0; n < draw_data->CmdListsCount; n++)
{
const ImDrawList* cmd_list = draw_data->CmdLists[n];
// Upload vertex/index buffers
GLsizeiptr vtx_buffer_size = (GLsizeiptr)cmd_list->VtxBuffer.Size * (int)sizeof(ImDrawVert);
GLsizeiptr idx_buffer_size = (GLsizeiptr)cmd_list->IdxBuffer.Size * (int)sizeof(ImDrawIdx);
if (bd->VertexBufferSize < vtx_buffer_size)
{
bd->VertexBufferSize = vtx_buffer_size;
glBufferData(GL_ARRAY_BUFFER, bd->VertexBufferSize, NULL, GL_STREAM_DRAW);
}
if (bd->IndexBufferSize < idx_buffer_size)
{
bd->IndexBufferSize = idx_buffer_size;
glBufferData(GL_ELEMENT_ARRAY_BUFFER, bd->IndexBufferSize, NULL, GL_STREAM_DRAW);
}
glBufferSubData(GL_ARRAY_BUFFER, 0, vtx_buffer_size, (const GLvoid*)cmd_list->VtxBuffer.Data);
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, idx_buffer_size, (const GLvoid*)cmd_list->IdxBuffer.Data);
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
{
const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
if (pcmd->UserCallback != NULL)
{
// User callback, registered via ImDrawList::AddCallback()
// (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.)
if (pcmd->UserCallback == ImDrawCallback_ResetRenderState)
ImGui_ImplOpenGL3_SetupRenderState(draw_data, fb_width, fb_height);
else
pcmd->UserCallback(cmd_list, pcmd);
}
else
{
// Project scissor/clipping rectangles into framebuffer space
ImVec2 clip_min((pcmd->ClipRect.x - clip_off.x) * clip_scale.x, (pcmd->ClipRect.y - clip_off.y) * clip_scale.y);
ImVec2 clip_max((pcmd->ClipRect.z - clip_off.x) * clip_scale.x, (pcmd->ClipRect.w - clip_off.y) * clip_scale.y);
if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y)
continue;
// Apply scissor/clipping rectangle (Y is inverted in OpenGL)
glScissor((int)clip_min.x, (int)((float)fb_height - clip_max.y), (int)(clip_max.x - clip_min.x), (int)(clip_max.y - clip_min.y));
// Bind texture, Draw
glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->GetTexID());
glDrawElementsBaseVertex(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, (void*)(intptr_t)(pcmd->IdxOffset * sizeof(ImDrawIdx)), (GLint)pcmd->VtxOffset);
}
}
}
glBindVertexArray(0);
}
bool ImGui_ImplOpenGL3_CreateFontsTexture()
{
ImGuiIO& io = ImGui::GetIO();
ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();
// Build texture atlas
unsigned char* pixels;
int width, height;
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); // Load as RGBA 32-bit (75% of the memory is wasted, but default font is so small) because it is more likely to be compatible with user's existing shaders. If your ImTextureId represent a higher-level concept than just a GL texture id, consider calling GetTexDataAsAlpha8() instead to save on GPU memory.
// Upload texture to graphics system
// (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling)
if (bd->FontTexture == 0)
glGenTextures(1, &bd->FontTexture);
glBindTexture(GL_TEXTURE_2D, bd->FontTexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
// Store our identifier
io.Fonts->SetTexID((ImTextureID)(intptr_t)bd->FontTexture);
return true;
}
void ImGui_ImplOpenGL3_DestroyFontsTexture()
{
ImGuiIO& io = ImGui::GetIO();
ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();
if (bd->FontTexture)
{
glDeleteTextures(1, &bd->FontTexture);
io.Fonts->SetTexID(0);
bd->FontTexture = 0;
}
}
// If you get an error please report on github. You may try different GL context version or GLSL version. See GL<>GLSL version table at the top of this file.
static bool CheckShader(GLuint handle, const char* desc)
{
ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();
GLint status = 0, log_length = 0;
glGetShaderiv(handle, GL_COMPILE_STATUS, &status);
glGetShaderiv(handle, GL_INFO_LOG_LENGTH, &log_length);
if ((GLboolean)status == GL_FALSE)
fprintf(stderr, "ERROR: ImGui_ImplOpenGL3_CreateDeviceObjects: failed to compile %s! With GLSL: %s\n", desc, bd->GlslVersionString);
if (log_length > 1)
{
ImVector<char> buf;
buf.resize((int)(log_length + 1));
glGetShaderInfoLog(handle, log_length, NULL, (GLchar*)buf.begin());
fprintf(stderr, "%s\n", buf.begin());
}
return (GLboolean)status == GL_TRUE;
}
// If you get an error please report on GitHub. You may try different GL context version or GLSL version.
static bool CheckProgram(GLuint handle, const char* desc)
{
ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();
GLint status = 0, log_length = 0;
glGetProgramiv(handle, GL_LINK_STATUS, &status);
glGetProgramiv(handle, GL_INFO_LOG_LENGTH, &log_length);
if ((GLboolean)status == GL_FALSE)
fprintf(stderr, "ERROR: ImGui_ImplOpenGL3_CreateDeviceObjects: failed to link %s! With GLSL %s\n", desc, bd->GlslVersionString);
if (log_length > 1)
{
ImVector<char> buf;
buf.resize((int)(log_length + 1));
glGetProgramInfoLog(handle, log_length, NULL, (GLchar*)buf.begin());
fprintf(stderr, "%s\n", buf.begin());
}
return (GLboolean)status == GL_TRUE;
}
bool ImGui_ImplOpenGL3_CreateDeviceObjects()
{
ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();
// Parse GLSL version string
int glsl_version = 130;
sscanf(bd->GlslVersionString, "#version %d", &glsl_version);
const GLchar* vertex_shader_glsl_120 =
"uniform mat4 ProjMtx;\n"
"attribute vec2 Position;\n"
"attribute vec2 UV;\n"
"attribute vec4 Color;\n"
"varying vec2 Frag_UV;\n"
"varying vec4 Frag_Color;\n"
"void main()\n"
"{\n"
" Frag_UV = UV;\n"
" Frag_Color = Color;\n"
" gl_Position = ProjMtx * vec4(Position.xy,0,1);\n"
"}\n";
const GLchar* vertex_shader_glsl_130 =
"uniform mat4 ProjMtx;\n"
"in vec2 Position;\n"
"in vec2 UV;\n"
"in vec4 Color;\n"
"out vec2 Frag_UV;\n"
"out vec4 Frag_Color;\n"
"void main()\n"
"{\n"
" Frag_UV = UV;\n"
" Frag_Color = Color;\n"
" gl_Position = ProjMtx * vec4(Position.xy,0,1);\n"
"}\n";
const GLchar* vertex_shader_glsl_300_es =
"precision highp float;\n"
"layout (location = 0) in vec2 Position;\n"
"layout (location = 1) in vec2 UV;\n"
"layout (location = 2) in vec4 Color;\n"
"uniform mat4 ProjMtx;\n"
"out vec2 Frag_UV;\n"
"out vec4 Frag_Color;\n"
"void main()\n"
"{\n"
" Frag_UV = UV;\n"
" Frag_Color = Color;\n"
" gl_Position = ProjMtx * vec4(Position.xy,0,1);\n"
"}\n";
const GLchar* vertex_shader_glsl_410_core =
"layout (location = 0) in vec2 Position;\n"
"layout (location = 1) in vec2 UV;\n"
"layout (location = 2) in vec4 Color;\n"
"uniform mat4 ProjMtx;\n"
"out vec2 Frag_UV;\n"
"out vec4 Frag_Color;\n"
"void main()\n"
"{\n"
" Frag_UV = UV;\n"
" Frag_Color = Color;\n"
" gl_Position = ProjMtx * vec4(Position.xy,0,1);\n"
"}\n";
const GLchar* fragment_shader_glsl_120 =
"#ifdef GL_ES\n"
" precision mediump float;\n"
"#endif\n"
"uniform sampler2D Texture;\n"
"varying vec2 Frag_UV;\n"
"varying vec4 Frag_Color;\n"
"void main()\n"
"{\n"
" gl_FragColor = Frag_Color * texture2D(Texture, Frag_UV.st);\n"
"}\n";
const GLchar* fragment_shader_glsl_130 =
"uniform sampler2D Texture;\n"
"in vec2 Frag_UV;\n"
"in vec4 Frag_Color;\n"
"out vec4 Out_Color;\n"
"void main()\n"
"{\n"
" Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n"
"}\n";
const GLchar* fragment_shader_glsl_300_es =
"precision mediump float;\n"
"uniform sampler2D Texture;\n"
"in vec2 Frag_UV;\n"
"in vec4 Frag_Color;\n"
"layout (location = 0) out vec4 Out_Color;\n"
"void main()\n"
"{\n"
" Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n"
"}\n";
const GLchar* fragment_shader_glsl_410_core =
"in vec2 Frag_UV;\n"
"in vec4 Frag_Color;\n"
"uniform sampler2D Texture;\n"
"layout (location = 0) out vec4 Out_Color;\n"
"void main()\n"
"{\n"
" Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n"
"}\n";
// Select shaders matching our GLSL versions
const GLchar* vertex_shader = NULL;
const GLchar* fragment_shader = NULL;
if (glsl_version < 130)
{
vertex_shader = vertex_shader_glsl_120;
fragment_shader = fragment_shader_glsl_120;
}
else if (glsl_version >= 410)
{
vertex_shader = vertex_shader_glsl_410_core;
fragment_shader = fragment_shader_glsl_410_core;
}
else if (glsl_version == 300)
{
vertex_shader = vertex_shader_glsl_300_es;
fragment_shader = fragment_shader_glsl_300_es;
}
else
{
vertex_shader = vertex_shader_glsl_130;
fragment_shader = fragment_shader_glsl_130;
}
// Create shaders
const GLchar* vertex_shader_with_version[2] = { bd->GlslVersionString, vertex_shader };
GLuint vert_handle = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vert_handle, 2, vertex_shader_with_version, NULL);
glCompileShader(vert_handle);
CheckShader(vert_handle, "vertex shader");
const GLchar* fragment_shader_with_version[2] = { bd->GlslVersionString, fragment_shader };
GLuint frag_handle = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(frag_handle, 2, fragment_shader_with_version, NULL);
glCompileShader(frag_handle);
CheckShader(frag_handle, "fragment shader");
// Link
bd->ShaderHandle = glCreateProgram();
glAttachShader(bd->ShaderHandle, vert_handle);
glAttachShader(bd->ShaderHandle, frag_handle);
glLinkProgram(bd->ShaderHandle);
CheckProgram(bd->ShaderHandle, "shader program");
glDetachShader(bd->ShaderHandle, vert_handle);
glDetachShader(bd->ShaderHandle, frag_handle);
glDeleteShader(vert_handle);
glDeleteShader(frag_handle);
bd->AttribLocationTex = glGetUniformLocation(bd->ShaderHandle, "Texture");
bd->AttribLocationProjMtx = glGetUniformLocation(bd->ShaderHandle, "ProjMtx");
bd->AttribLocationVtxPos = (GLuint)glGetAttribLocation(bd->ShaderHandle, "Position");
bd->AttribLocationVtxUV = (GLuint)glGetAttribLocation(bd->ShaderHandle, "UV");
bd->AttribLocationVtxColor = (GLuint)glGetAttribLocation(bd->ShaderHandle, "Color");
// Create buffers
glGenBuffers(1, &bd->VboHandle);
glGenBuffers(1, &bd->ElementsHandle);
glGenVertexArrays(1, &bd->VaoHandle);
// Create sampler
if (bd->SamplerHandle == 0)
{
glGenSamplers(1, &bd->SamplerHandle);
glSamplerParameteri(bd->SamplerHandle, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glSamplerParameteri(bd->SamplerHandle, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glSamplerParameteri(bd->SamplerHandle, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glSamplerParameteri(bd->SamplerHandle, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
}
return true;
}
void ImGui_ImplOpenGL3_DestroyDeviceObjects()
{
ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();
if (bd->VaoHandle) { glDeleteVertexArrays(1, &bd->VaoHandle); bd->VaoHandle = 0; }
if (bd->VboHandle) { glDeleteBuffers(1, &bd->VboHandle); bd->VboHandle = 0; }
if (bd->ElementsHandle) { glDeleteBuffers(1, &bd->ElementsHandle); bd->ElementsHandle = 0; }
if (bd->ShaderHandle) { glDeleteProgram(bd->ShaderHandle); bd->ShaderHandle = 0; }
if (bd->SamplerHandle) { glDeleteSamplers(1, &bd->SamplerHandle); bd->SamplerHandle = 0; }
ImGui_ImplOpenGL3_DestroyFontsTexture();
}
#if defined(__clang__)
#pragma clang diagnostic pop
#endif
-16
View File
@@ -1,16 +0,0 @@
// dear imgui: Renderer Backend for modern OpenGL with shaders / programmatic pipeline
// This needs to be used along with a Platform Backend (e.g. GLFW, SDL, Win32, custom..)
#pragma once
#include "imgui.h" // IMGUI_IMPL_API
// Backend API
bool ImGui_ImplOpenGL3_Init(const char* glsl_version = NULL);
void ImGui_ImplOpenGL3_Shutdown();
void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data);
// (Optional) Called by Init/NewFrame/Shutdown
bool ImGui_ImplOpenGL3_CreateFontsTexture();
void ImGui_ImplOpenGL3_DestroyFontsTexture();
bool ImGui_ImplOpenGL3_CreateDeviceObjects();
void ImGui_ImplOpenGL3_DestroyDeviceObjects();
-706
View File
@@ -1,706 +0,0 @@
// dear imgui: Renderer Backend for Vulkan
// This needs to be used along with a Platform Backend (e.g. GLFW, SDL, Win32, custom..)
// Implemented features:
// [X] Renderer: Support for large meshes (64k+ vertices) with 16-bit indices.
// [!] Renderer: User texture binding. Use 'VkDescriptorSet' as ImTextureID. Read the FAQ about ImTextureID! See https://github.com/ocornut/imgui/pull/914 for discussions.
// Important: on 32-bit systems, user texture binding is only supported if your imconfig file has '#define ImTextureID ImU64'.
// This is because we need ImTextureID to carry a 64-bit value and by default ImTextureID is defined as void*.
// To build this on 32-bit systems and support texture changes:
// - [Solution 1] IDE/msbuild: in "Properties/C++/Preprocessor Definitions" add 'ImTextureID=ImU64' (this is what we do in our .vcxproj files)
// - [Solution 2] IDE/msbuild: in "Properties/C++/Preprocessor Definitions" add 'IMGUI_USER_CONFIG="my_imgui_config.h"' and inside 'my_imgui_config.h' add '#define ImTextureID ImU64' and as many other options as you like.
// - [Solution 3] IDE/msbuild: edit imconfig.h and add '#define ImTextureID ImU64' (prefer solution 2 to create your own config file!)
// - [Solution 4] command-line: add '/D ImTextureID=ImU64' to your cl.exe command-line (this is what we do in our batch files)
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
// Read online: https://github.com/ocornut/imgui/tree/master/docs
// The aim of imgui_impl_vulkan.h/.cpp is to be usable in your engine without any modification.
// IF YOU FEEL YOU NEED TO MAKE ANY CHANGE TO THIS CODE, please share them and your feedback at https://github.com/ocornut/imgui/
// Important note to the reader who wish to integrate imgui_impl_vulkan.cpp/.h in their own engine/app.
// - Common ImGui_ImplVulkan_XXX functions and structures are used to interface with imgui_impl_vulkan.cpp/.h.
// You will use those if you want to use this rendering backend in your engine/app.
// - Helper ImGui_ImplVulkanH_XXX functions and structures are only used by this example (main.cpp) and by
// the backend itself (imgui_impl_vulkan.cpp), but should PROBABLY NOT be used by your own engine/app code.
// Read comments in imgui_impl_vulkan.h.
// CHANGELOG
// (minor and older changes stripped away, please see git history for details)
// 2021-10-15: Vulkan: Call vkCmdSetScissor() at the end of render a full-viewport to reduce likehood of issues with people using VK_DYNAMIC_STATE_SCISSOR in their app without calling vkCmdSetScissor() explicitly every frame.
// 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX).
// 2021-03-22: Vulkan: Fix mapped memory validation error when buffer sizes are not multiple of VkPhysicalDeviceLimits::nonCoherentAtomSize.
// 2021-02-18: Vulkan: Change blending equation to preserve alpha in output buffer.
// 2021-01-27: Vulkan: Added support for custom function load and IMGUI_IMPL_VULKAN_NO_PROTOTYPES by using ImGui_ImplVulkan_LoadFunctions().
// 2020-11-11: Vulkan: Added support for specifying which subpass to reference during VkPipeline creation.
// 2020-09-07: Vulkan: Added VkPipeline parameter to ImGui_ImplVulkan_RenderDrawData (default to one passed to ImGui_ImplVulkan_Init).
// 2020-05-04: Vulkan: Fixed crash if initial frame has no vertices.
// 2020-04-26: Vulkan: Fixed edge case where render callbacks wouldn't be called if the ImDrawData didn't have vertices.
// 2019-08-01: Vulkan: Added support for specifying multisample count. Set ImGui_ImplVulkan_InitInfo::MSAASamples to one of the VkSampleCountFlagBits values to use, default is non-multisampled as before.
// 2019-05-29: Vulkan: Added support for large mesh (64K+ vertices), enable ImGuiBackendFlags_RendererHasVtxOffset flag.
// 2019-04-30: Vulkan: Added support for special ImDrawCallback_ResetRenderState callback to reset render state.
// 2019-04-04: *BREAKING CHANGE*: Vulkan: Added ImageCount/MinImageCount fields in ImGui_ImplVulkan_InitInfo, required for initialization (was previously a hard #define IMGUI_VK_QUEUED_FRAMES 2). Added ImGui_ImplVulkan_SetMinImageCount().
// 2019-04-04: Vulkan: Added VkInstance argument to ImGui_ImplVulkanH_CreateWindow() optional helper.
// 2019-04-04: Vulkan: Avoid passing negative coordinates to vkCmdSetScissor, which debug validation layers do not like.
// 2019-04-01: Vulkan: Support for 32-bit index buffer (#define ImDrawIdx unsigned int).
// 2019-02-16: Vulkan: Viewport and clipping rectangles correctly using draw_data->FramebufferScale to allow retina display.
// 2018-11-30: Misc: Setting up io.BackendRendererName so it can be displayed in the About Window.
// 2018-08-25: Vulkan: Fixed mishandled VkSurfaceCapabilitiesKHR::maxImageCount=0 case.
// 2018-06-22: Inverted the parameters to ImGui_ImplVulkan_RenderDrawData() to be consistent with other backends.
// 2018-06-08: Misc: Extracted imgui_impl_vulkan.cpp/.h away from the old combined GLFW+Vulkan example.
// 2018-06-08: Vulkan: Use draw_data->DisplayPos and draw_data->DisplaySize to setup projection matrix and clipping rectangle.
// 2018-03-03: Vulkan: Various refactor, created a couple of ImGui_ImplVulkanH_XXX helper that the example can use and that viewport support will use.
// 2018-03-01: Vulkan: Renamed ImGui_ImplVulkan_Init_Info to ImGui_ImplVulkan_InitInfo and fields to match more closely Vulkan terminology.
// 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback, ImGui_ImplVulkan_Render() calls ImGui_ImplVulkan_RenderDrawData() itself.
// 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves.
// 2017-05-15: Vulkan: Fix scissor offset being negative. Fix new Vulkan validation warnings. Set required depth member for buffer image copy.
// 2016-11-13: Vulkan: Fix validation layer warnings and errors and redeclare gl_PerVertex.
// 2016-10-18: Vulkan: Add location decorators & change to use structs as in/out in glsl, update embedded spv (produced with glslangValidator -x). Null the released resources.
// 2016-08-27: Vulkan: Fix Vulkan example for use when a depth buffer is active.
#include "imgui_impl_vulkan.h"
#include "common/Vulkan/Builders.h"
#include "common/Vulkan/Context.h"
#include "common/Vulkan/Texture.h"
#include "common/Vulkan/StreamBuffer.h"
#include "common/Vulkan/Util.h"
#include <cstdio>
#include <cstring>
// Visual Studio warnings
#ifdef _MSC_VER
#pragma warning (disable: 4127) // condition expression is constant
#endif
// If we're doing more than this... wtf?
static constexpr u32 VERTEX_BUFFER_SIZE = 8 * 1024 * 1024;
static constexpr u32 INDEX_BUFFER_SIZE = 4 * 1024 * 1024;
// Vulkan data
struct ImGui_ImplVulkan_Data
{
VkRenderPass RenderPass = VK_NULL_HANDLE;
VkPipelineCreateFlags PipelineCreateFlags = 0;
VkDescriptorSetLayout DescriptorSetLayout = VK_NULL_HANDLE;
VkPipelineLayout PipelineLayout = VK_NULL_HANDLE;
VkPipeline Pipeline = VK_NULL_HANDLE;
VkShaderModule ShaderModuleVert = VK_NULL_HANDLE;
VkShaderModule ShaderModuleFrag = VK_NULL_HANDLE;
VkSampler FontSampler = VK_NULL_HANDLE;
Vulkan::StreamBuffer VertexStreamBuffer;
Vulkan::StreamBuffer IndexStreamBuffer;
Vulkan::Texture FontTexture;
};
// Forward Declarations
static bool ImGui_ImplVulkan_CreateDeviceObjects();
static void ImGui_ImplVulkan_DestroyDeviceObjects();
//-----------------------------------------------------------------------------
// SHADERS
//-----------------------------------------------------------------------------
// glsl_shader.vert, compiled with:
// # glslangValidator -V -x -o glsl_shader.vert.u32 glsl_shader.vert
/*
#version 450 core
layout(location = 0) in vec2 aPos;
layout(location = 1) in vec2 aUV;
layout(location = 2) in vec4 aColor;
layout(push_constant) uniform uPushConstant { vec2 uScale; vec2 uTranslate; } pc;
out gl_PerVertex { vec4 gl_Position; };
layout(location = 0) out struct { vec4 Color; vec2 UV; } Out;
void main()
{
Out.Color = aColor;
Out.UV = aUV;
gl_Position = vec4(aPos * pc.uScale + pc.uTranslate, 0, 1);
}
*/
static uint32_t __glsl_shader_vert_spv[] =
{
0x07230203,0x00010000,0x00080001,0x0000002e,0x00000000,0x00020011,0x00000001,0x0006000b,
0x00000001,0x4c534c47,0x6474732e,0x3035342e,0x00000000,0x0003000e,0x00000000,0x00000001,
0x000a000f,0x00000000,0x00000004,0x6e69616d,0x00000000,0x0000000b,0x0000000f,0x00000015,
0x0000001b,0x0000001c,0x00030003,0x00000002,0x000001c2,0x00040005,0x00000004,0x6e69616d,
0x00000000,0x00030005,0x00000009,0x00000000,0x00050006,0x00000009,0x00000000,0x6f6c6f43,
0x00000072,0x00040006,0x00000009,0x00000001,0x00005655,0x00030005,0x0000000b,0x0074754f,
0x00040005,0x0000000f,0x6c6f4361,0x0000726f,0x00030005,0x00000015,0x00565561,0x00060005,
0x00000019,0x505f6c67,0x65567265,0x78657472,0x00000000,0x00060006,0x00000019,0x00000000,
0x505f6c67,0x7469736f,0x006e6f69,0x00030005,0x0000001b,0x00000000,0x00040005,0x0000001c,
0x736f5061,0x00000000,0x00060005,0x0000001e,0x73755075,0x6e6f4368,0x6e617473,0x00000074,
0x00050006,0x0000001e,0x00000000,0x61635375,0x0000656c,0x00060006,0x0000001e,0x00000001,
0x61725475,0x616c736e,0x00006574,0x00030005,0x00000020,0x00006370,0x00040047,0x0000000b,
0x0000001e,0x00000000,0x00040047,0x0000000f,0x0000001e,0x00000002,0x00040047,0x00000015,
0x0000001e,0x00000001,0x00050048,0x00000019,0x00000000,0x0000000b,0x00000000,0x00030047,
0x00000019,0x00000002,0x00040047,0x0000001c,0x0000001e,0x00000000,0x00050048,0x0000001e,
0x00000000,0x00000023,0x00000000,0x00050048,0x0000001e,0x00000001,0x00000023,0x00000008,
0x00030047,0x0000001e,0x00000002,0x00020013,0x00000002,0x00030021,0x00000003,0x00000002,
0x00030016,0x00000006,0x00000020,0x00040017,0x00000007,0x00000006,0x00000004,0x00040017,
0x00000008,0x00000006,0x00000002,0x0004001e,0x00000009,0x00000007,0x00000008,0x00040020,
0x0000000a,0x00000003,0x00000009,0x0004003b,0x0000000a,0x0000000b,0x00000003,0x00040015,
0x0000000c,0x00000020,0x00000001,0x0004002b,0x0000000c,0x0000000d,0x00000000,0x00040020,
0x0000000e,0x00000001,0x00000007,0x0004003b,0x0000000e,0x0000000f,0x00000001,0x00040020,
0x00000011,0x00000003,0x00000007,0x0004002b,0x0000000c,0x00000013,0x00000001,0x00040020,
0x00000014,0x00000001,0x00000008,0x0004003b,0x00000014,0x00000015,0x00000001,0x00040020,
0x00000017,0x00000003,0x00000008,0x0003001e,0x00000019,0x00000007,0x00040020,0x0000001a,
0x00000003,0x00000019,0x0004003b,0x0000001a,0x0000001b,0x00000003,0x0004003b,0x00000014,
0x0000001c,0x00000001,0x0004001e,0x0000001e,0x00000008,0x00000008,0x00040020,0x0000001f,
0x00000009,0x0000001e,0x0004003b,0x0000001f,0x00000020,0x00000009,0x00040020,0x00000021,
0x00000009,0x00000008,0x0004002b,0x00000006,0x00000028,0x00000000,0x0004002b,0x00000006,
0x00000029,0x3f800000,0x00050036,0x00000002,0x00000004,0x00000000,0x00000003,0x000200f8,
0x00000005,0x0004003d,0x00000007,0x00000010,0x0000000f,0x00050041,0x00000011,0x00000012,
0x0000000b,0x0000000d,0x0003003e,0x00000012,0x00000010,0x0004003d,0x00000008,0x00000016,
0x00000015,0x00050041,0x00000017,0x00000018,0x0000000b,0x00000013,0x0003003e,0x00000018,
0x00000016,0x0004003d,0x00000008,0x0000001d,0x0000001c,0x00050041,0x00000021,0x00000022,
0x00000020,0x0000000d,0x0004003d,0x00000008,0x00000023,0x00000022,0x00050085,0x00000008,
0x00000024,0x0000001d,0x00000023,0x00050041,0x00000021,0x00000025,0x00000020,0x00000013,
0x0004003d,0x00000008,0x00000026,0x00000025,0x00050081,0x00000008,0x00000027,0x00000024,
0x00000026,0x00050051,0x00000006,0x0000002a,0x00000027,0x00000000,0x00050051,0x00000006,
0x0000002b,0x00000027,0x00000001,0x00070050,0x00000007,0x0000002c,0x0000002a,0x0000002b,
0x00000028,0x00000029,0x00050041,0x00000011,0x0000002d,0x0000001b,0x0000000d,0x0003003e,
0x0000002d,0x0000002c,0x000100fd,0x00010038
};
// glsl_shader.frag, compiled with:
// # glslangValidator -V -x -o glsl_shader.frag.u32 glsl_shader.frag
/*
#version 450 core
layout(location = 0) out vec4 fColor;
layout(set=0, binding=0) uniform sampler2D sTexture;
layout(location = 0) in struct { vec4 Color; vec2 UV; } In;
void main()
{
fColor = In.Color * texture(sTexture, In.UV.st);
}
*/
static uint32_t __glsl_shader_frag_spv[] =
{
0x07230203,0x00010000,0x00080001,0x0000001e,0x00000000,0x00020011,0x00000001,0x0006000b,
0x00000001,0x4c534c47,0x6474732e,0x3035342e,0x00000000,0x0003000e,0x00000000,0x00000001,
0x0007000f,0x00000004,0x00000004,0x6e69616d,0x00000000,0x00000009,0x0000000d,0x00030010,
0x00000004,0x00000007,0x00030003,0x00000002,0x000001c2,0x00040005,0x00000004,0x6e69616d,
0x00000000,0x00040005,0x00000009,0x6c6f4366,0x0000726f,0x00030005,0x0000000b,0x00000000,
0x00050006,0x0000000b,0x00000000,0x6f6c6f43,0x00000072,0x00040006,0x0000000b,0x00000001,
0x00005655,0x00030005,0x0000000d,0x00006e49,0x00050005,0x00000016,0x78655473,0x65727574,
0x00000000,0x00040047,0x00000009,0x0000001e,0x00000000,0x00040047,0x0000000d,0x0000001e,
0x00000000,0x00040047,0x00000016,0x00000022,0x00000000,0x00040047,0x00000016,0x00000021,
0x00000000,0x00020013,0x00000002,0x00030021,0x00000003,0x00000002,0x00030016,0x00000006,
0x00000020,0x00040017,0x00000007,0x00000006,0x00000004,0x00040020,0x00000008,0x00000003,
0x00000007,0x0004003b,0x00000008,0x00000009,0x00000003,0x00040017,0x0000000a,0x00000006,
0x00000002,0x0004001e,0x0000000b,0x00000007,0x0000000a,0x00040020,0x0000000c,0x00000001,
0x0000000b,0x0004003b,0x0000000c,0x0000000d,0x00000001,0x00040015,0x0000000e,0x00000020,
0x00000001,0x0004002b,0x0000000e,0x0000000f,0x00000000,0x00040020,0x00000010,0x00000001,
0x00000007,0x00090019,0x00000013,0x00000006,0x00000001,0x00000000,0x00000000,0x00000000,
0x00000001,0x00000000,0x0003001b,0x00000014,0x00000013,0x00040020,0x00000015,0x00000000,
0x00000014,0x0004003b,0x00000015,0x00000016,0x00000000,0x0004002b,0x0000000e,0x00000018,
0x00000001,0x00040020,0x00000019,0x00000001,0x0000000a,0x00050036,0x00000002,0x00000004,
0x00000000,0x00000003,0x000200f8,0x00000005,0x00050041,0x00000010,0x00000011,0x0000000d,
0x0000000f,0x0004003d,0x00000007,0x00000012,0x00000011,0x0004003d,0x00000014,0x00000017,
0x00000016,0x00050041,0x00000019,0x0000001a,0x0000000d,0x00000018,0x0004003d,0x0000000a,
0x0000001b,0x0000001a,0x00050057,0x00000007,0x0000001c,0x00000017,0x0000001b,0x00050085,
0x00000007,0x0000001d,0x00000012,0x0000001c,0x0003003e,0x00000009,0x0000001d,0x000100fd,
0x00010038
};
//-----------------------------------------------------------------------------
// FUNCTIONS
//-----------------------------------------------------------------------------
// Backend data stored in io.BackendRendererUserData to allow support for multiple Dear ImGui contexts
// It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts.
// FIXME: multi-context support is not tested and probably dysfunctional in this backend.
static ImGui_ImplVulkan_Data* ImGui_ImplVulkan_GetBackendData()
{
return ImGui::GetCurrentContext() ? (ImGui_ImplVulkan_Data*)ImGui::GetIO().BackendRendererUserData : NULL;
}
static void ImGui_ImplVulkan_SetupRenderState(ImDrawData* draw_data, VkPipeline pipeline, VkCommandBuffer command_buffer, int fb_width, int fb_height)
{
ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData();
// Bind pipeline:
{
vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
}
// Bind Vertex And Index Buffer:
if (draw_data->TotalVtxCount > 0)
{
VkBuffer vertex_buffers[1] = { bd->VertexStreamBuffer.GetBuffer() };
VkDeviceSize vertex_offset[1] = { bd->VertexStreamBuffer.GetCurrentOffset() };
vkCmdBindVertexBuffers(command_buffer, 0, 1, vertex_buffers, vertex_offset);
vkCmdBindIndexBuffer(command_buffer, bd->IndexStreamBuffer.GetBuffer(), bd->IndexStreamBuffer.GetCurrentOffset(), sizeof(ImDrawIdx) == 2 ? VK_INDEX_TYPE_UINT16 : VK_INDEX_TYPE_UINT32);
}
// Setup viewport:
{
VkViewport viewport;
viewport.x = 0;
viewport.y = 0;
viewport.width = (float)fb_width;
viewport.height = (float)fb_height;
viewport.minDepth = 0.0f;
viewport.maxDepth = 1.0f;
vkCmdSetViewport(command_buffer, 0, 1, &viewport);
}
// Setup scale and translation:
// Our visible imgui space lies from draw_data->DisplayPps (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps.
{
float scale[2];
scale[0] = 2.0f / draw_data->DisplaySize.x;
scale[1] = 2.0f / draw_data->DisplaySize.y;
float translate[2];
translate[0] = -1.0f - draw_data->DisplayPos.x * scale[0];
translate[1] = -1.0f - draw_data->DisplayPos.y * scale[1];
vkCmdPushConstants(command_buffer, bd->PipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, sizeof(float) * 0, sizeof(float) * 2, scale);
vkCmdPushConstants(command_buffer, bd->PipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, sizeof(float) * 2, sizeof(float) * 2, translate);
}
}
// Render function
void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data)
{
// Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates)
int fb_width = (int)(draw_data->DisplaySize.x * draw_data->FramebufferScale.x);
int fb_height = (int)(draw_data->DisplaySize.y * draw_data->FramebufferScale.y);
if (fb_width <= 0 || fb_height <= 0)
return;
ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData();
if (draw_data->TotalVtxCount > 0)
{
// Create or resize the vertex/index buffers
const size_t vertex_size = draw_data->TotalVtxCount * sizeof(ImDrawVert);
const size_t index_size = draw_data->TotalIdxCount * sizeof(ImDrawIdx);
if (!bd->VertexStreamBuffer.ReserveMemory(vertex_size, sizeof(ImDrawVert)) ||
!bd->IndexStreamBuffer.ReserveMemory(index_size, sizeof(ImDrawIdx)))
{
// this is annoying, because we can't restart the render pass...
return;
}
// Upload vertex/index data into a single contiguous GPU buffer
ImDrawVert* vtx_dst = (ImDrawVert*)bd->VertexStreamBuffer.GetCurrentHostPointer();
ImDrawIdx* idx_dst = (ImDrawIdx*)bd->IndexStreamBuffer.GetCurrentHostPointer();
for (int n = 0; n < draw_data->CmdListsCount; n++)
{
const ImDrawList* cmd_list = draw_data->CmdLists[n];
memcpy(vtx_dst, cmd_list->VtxBuffer.Data, cmd_list->VtxBuffer.Size * sizeof(ImDrawVert));
memcpy(idx_dst, cmd_list->IdxBuffer.Data, cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx));
vtx_dst += cmd_list->VtxBuffer.Size;
idx_dst += cmd_list->IdxBuffer.Size;
}
// Setup desired Vulkan state (must come before buffer commit)
ImGui_ImplVulkan_SetupRenderState(draw_data, bd->Pipeline, g_vulkan_context->GetCurrentCommandBuffer(), fb_width, fb_height);
bd->VertexStreamBuffer.CommitMemory(vertex_size);
bd->IndexStreamBuffer.CommitMemory(index_size);
}
// Will project scissor/clipping rectangles into framebuffer space
ImVec2 clip_off = draw_data->DisplayPos; // (0,0) unless using multi-viewports
ImVec2 clip_scale = draw_data->FramebufferScale; // (1,1) unless using retina display which are often (2,2)
// Render command lists
// (Because we merged all buffers into a single one, we maintain our own offset into them)
int global_vtx_offset = 0;
int global_idx_offset = 0;
const Vulkan::Texture* last_texture = nullptr;
VkCommandBuffer command_buffer = g_vulkan_context->GetCurrentCommandBuffer();
for (int n = 0; n < draw_data->CmdListsCount; n++)
{
const ImDrawList* cmd_list = draw_data->CmdLists[n];
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
{
const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
if (pcmd->UserCallback != NULL)
{
// User callback, registered via ImDrawList::AddCallback()
// (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.)
if (pcmd->UserCallback == ImDrawCallback_ResetRenderState)
ImGui_ImplVulkan_SetupRenderState(draw_data, bd->Pipeline, command_buffer, fb_width, fb_height);
else
pcmd->UserCallback(cmd_list, pcmd);
}
else
{
// Project scissor/clipping rectangles into framebuffer space
ImVec2 clip_min((pcmd->ClipRect.x - clip_off.x) * clip_scale.x, (pcmd->ClipRect.y - clip_off.y) * clip_scale.y);
ImVec2 clip_max((pcmd->ClipRect.z - clip_off.x) * clip_scale.x, (pcmd->ClipRect.w - clip_off.y) * clip_scale.y);
// Clamp to viewport as vkCmdSetScissor() won't accept values that are off bounds
if (clip_min.x < 0.0f) { clip_min.x = 0.0f; }
if (clip_min.y < 0.0f) { clip_min.y = 0.0f; }
if (clip_max.x > fb_width) { clip_max.x = (float)fb_width; }
if (clip_max.y > fb_height) { clip_max.y = (float)fb_height; }
if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y)
continue;
// Apply scissor/clipping rectangle
VkRect2D scissor;
scissor.offset.x = (int32_t)(clip_min.x);
scissor.offset.y = (int32_t)(clip_min.y);
scissor.extent.width = (uint32_t)(clip_max.x - clip_min.x);
scissor.extent.height = (uint32_t)(clip_max.y - clip_min.y);
vkCmdSetScissor(command_buffer, 0, 1, &scissor);
// Bind DescriptorSet with font or user texture
const Vulkan::Texture* tex = (const Vulkan::Texture*)pcmd->TextureId;
if (tex && last_texture != tex)
{
// if we can't get a descriptor set, we'll we're in trouble, since we can't restart the render pass from here.
VkDescriptorSet ds = g_vulkan_context->AllocateDescriptorSet(bd->DescriptorSetLayout);
if (ds == VK_NULL_HANDLE)
{
continue;
}
Vulkan::DescriptorSetUpdateBuilder dsb;
dsb.AddCombinedImageSamplerDescriptorWrite(ds, 0, tex->GetView(), bd->FontSampler);
dsb.Update(g_vulkan_context->GetDevice());
vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, bd->PipelineLayout, 0, 1, &ds, 0, nullptr);
last_texture = tex;
}
// Draw
vkCmdDrawIndexed(command_buffer, pcmd->ElemCount, 1, pcmd->IdxOffset + global_idx_offset, pcmd->VtxOffset + global_vtx_offset, 0);
}
}
global_idx_offset += cmd_list->IdxBuffer.Size;
global_vtx_offset += cmd_list->VtxBuffer.Size;
}
// Note: at this point both vkCmdSetViewport() and vkCmdSetScissor() have been called.
// Our last values will leak into user/application rendering IF:
// - Your app uses a pipeline with VK_DYNAMIC_STATE_VIEWPORT or VK_DYNAMIC_STATE_SCISSOR dynamic state
// - And you forgot to call vkCmdSetViewport() and vkCmdSetScissor() yourself to explicitely set that state.
// If you use VK_DYNAMIC_STATE_VIEWPORT or VK_DYNAMIC_STATE_SCISSOR you are responsible for setting the values before rendering.
// In theory we should aim to backup/restore those values but I am not sure this is possible.
// We perform a call to vkCmdSetScissor() to set back a full viewport which is likely to fix things for 99% users but technically this is not perfect. (See github #4644)
VkRect2D scissor = { { 0, 0 }, { (uint32_t)fb_width, (uint32_t)fb_height } };
vkCmdSetScissor(command_buffer, 0, 1, &scissor);
}
bool ImGui_ImplVulkan_CreateFontsTexture()
{
ImGuiIO& io = ImGui::GetIO();
ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData();
unsigned char* pixels;
int width, height;
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
if (bd->FontTexture.GetWidth() != static_cast<u32>(width) || bd->FontTexture.GetHeight() != static_cast<u32>(height))
{
if (!bd->FontTexture.Create(width, height, 1, 1, VK_FORMAT_R8G8B8A8_UNORM,
VK_SAMPLE_COUNT_1_BIT, VK_IMAGE_VIEW_TYPE_2D, VK_IMAGE_TILING_OPTIMAL,
VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT))
{
return false;
}
}
const size_t upload_size = width * height * 4 * sizeof(unsigned char);
const VkBufferCreateInfo bci = {VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, nullptr, 0,
static_cast<VkDeviceSize>(upload_size), VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_SHARING_MODE_EXCLUSIVE, 0, nullptr};
VmaAllocationCreateInfo aci = {};
aci.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
aci.usage = VMA_MEMORY_USAGE_CPU_TO_GPU;
VmaAllocationInfo ai;
VkBuffer buffer;
VmaAllocation allocation;
VkResult res = vmaCreateBuffer(g_vulkan_context->GetAllocator(), &bci, &aci, &buffer, &allocation, &ai);
if (res != VK_SUCCESS)
return false;
std::memcpy(ai.pMappedData, pixels, upload_size);
vmaFlushAllocation(g_vulkan_context->GetAllocator(), allocation, 0, upload_size);
bd->FontTexture.TransitionToLayout(g_vulkan_context->GetCurrentInitCommandBuffer(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
bd->FontTexture.UpdateFromBuffer(g_vulkan_context->GetCurrentInitCommandBuffer(), 0, 0, 0, 0, width, height, height, width, buffer, 0);
bd->FontTexture.TransitionToLayout(g_vulkan_context->GetCurrentInitCommandBuffer(), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
// Immediately queue it for freeing after the command buffer finishes, since it's only needed for the copy.
g_vulkan_context->DeferBufferDestruction(buffer, allocation);
// Store our identifier
io.Fonts->SetTexID((ImTextureID)&bd->FontTexture);
return true;
}
static bool ImGui_ImplVulkan_CreateShaderModules(VkDevice device)
{
// Create the shader modules
ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData();
if (bd->ShaderModuleVert == VK_NULL_HANDLE)
{
VkShaderModuleCreateInfo vert_info = {};
vert_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
vert_info.codeSize = sizeof(__glsl_shader_vert_spv);
vert_info.pCode = (uint32_t*)__glsl_shader_vert_spv;
VkResult err = vkCreateShaderModule(device, &vert_info, nullptr, &bd->ShaderModuleVert);
if (err != VK_SUCCESS)
return false;
}
if (bd->ShaderModuleFrag == VK_NULL_HANDLE)
{
VkShaderModuleCreateInfo frag_info = {};
frag_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
frag_info.codeSize = sizeof(__glsl_shader_frag_spv);
frag_info.pCode = (uint32_t*)__glsl_shader_frag_spv;
VkResult err = vkCreateShaderModule(device, &frag_info, nullptr, &bd->ShaderModuleFrag);
if (err != VK_SUCCESS)
return false;
}
return true;
}
static bool ImGui_ImplVulkan_CreateFontSampler(VkDevice device)
{
ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData();
if (bd->FontSampler)
return true;
// Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling.
VkSamplerCreateInfo info = {};
info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
info.magFilter = VK_FILTER_LINEAR;
info.minFilter = VK_FILTER_LINEAR;
info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
info.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
info.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
info.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
info.minLod = -1000;
info.maxLod = 1000;
info.maxAnisotropy = 1.0f;
VkResult err = vkCreateSampler(device, &info, nullptr, &bd->FontSampler);
return (err == VK_SUCCESS);
}
static bool ImGui_ImplVulkan_CreateDescriptorSetLayout(VkDevice device)
{
ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData();
if (bd->DescriptorSetLayout)
return true;
if (!ImGui_ImplVulkan_CreateFontSampler(device))
return false;
VkSampler sampler[1] = { bd->FontSampler };
VkDescriptorSetLayoutBinding binding[1] = {};
binding[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
binding[0].descriptorCount = 1;
binding[0].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
binding[0].pImmutableSamplers = sampler;
VkDescriptorSetLayoutCreateInfo info = {};
info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
info.bindingCount = 1;
info.pBindings = binding;
VkResult err = vkCreateDescriptorSetLayout(device, &info, nullptr, &bd->DescriptorSetLayout);
return (err == VK_SUCCESS);
}
static bool ImGui_ImplVulkan_CreatePipelineLayout(VkDevice device)
{
ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData();
if (bd->PipelineLayout)
return true;
// Constants: we are using 'vec2 offset' and 'vec2 scale' instead of a full 3d projection matrix
ImGui_ImplVulkan_CreateDescriptorSetLayout(device);
VkPushConstantRange push_constants[1] = {};
push_constants[0].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
push_constants[0].offset = sizeof(float) * 0;
push_constants[0].size = sizeof(float) * 4;
VkDescriptorSetLayout set_layout[1] = { bd->DescriptorSetLayout };
VkPipelineLayoutCreateInfo layout_info = {};
layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
layout_info.setLayoutCount = 1;
layout_info.pSetLayouts = set_layout;
layout_info.pushConstantRangeCount = 1;
layout_info.pPushConstantRanges = push_constants;
VkResult err = vkCreatePipelineLayout(device, &layout_info, nullptr, &bd->PipelineLayout);
return (err == VK_SUCCESS);
}
static bool ImGui_ImplVulkan_CreatePipeline(VkDevice device, VkPipelineCache pipelineCache, VkRenderPass renderPass, VkPipeline* pipeline)
{
ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData();
if (!ImGui_ImplVulkan_CreateShaderModules(device))
return false;
VkPipelineShaderStageCreateInfo stage[2] = {};
stage[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
stage[0].stage = VK_SHADER_STAGE_VERTEX_BIT;
stage[0].module = bd->ShaderModuleVert;
stage[0].pName = "main";
stage[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
stage[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;
stage[1].module = bd->ShaderModuleFrag;
stage[1].pName = "main";
VkVertexInputBindingDescription binding_desc[1] = {};
binding_desc[0].stride = sizeof(ImDrawVert);
binding_desc[0].inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
VkVertexInputAttributeDescription attribute_desc[3] = {};
attribute_desc[0].location = 0;
attribute_desc[0].binding = binding_desc[0].binding;
attribute_desc[0].format = VK_FORMAT_R32G32_SFLOAT;
attribute_desc[0].offset = IM_OFFSETOF(ImDrawVert, pos);
attribute_desc[1].location = 1;
attribute_desc[1].binding = binding_desc[0].binding;
attribute_desc[1].format = VK_FORMAT_R32G32_SFLOAT;
attribute_desc[1].offset = IM_OFFSETOF(ImDrawVert, uv);
attribute_desc[2].location = 2;
attribute_desc[2].binding = binding_desc[0].binding;
attribute_desc[2].format = VK_FORMAT_R8G8B8A8_UNORM;
attribute_desc[2].offset = IM_OFFSETOF(ImDrawVert, col);
VkPipelineVertexInputStateCreateInfo vertex_info = {};
vertex_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
vertex_info.vertexBindingDescriptionCount = 1;
vertex_info.pVertexBindingDescriptions = binding_desc;
vertex_info.vertexAttributeDescriptionCount = 3;
vertex_info.pVertexAttributeDescriptions = attribute_desc;
VkPipelineInputAssemblyStateCreateInfo ia_info = {};
ia_info.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
ia_info.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
VkPipelineViewportStateCreateInfo viewport_info = {};
viewport_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
viewport_info.viewportCount = 1;
viewport_info.scissorCount = 1;
VkPipelineRasterizationStateCreateInfo raster_info = {};
raster_info.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
raster_info.polygonMode = VK_POLYGON_MODE_FILL;
raster_info.cullMode = VK_CULL_MODE_NONE;
raster_info.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
raster_info.lineWidth = 1.0f;
VkPipelineMultisampleStateCreateInfo ms_info = {};
ms_info.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
ms_info.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
VkPipelineColorBlendAttachmentState color_attachment[1] = {};
color_attachment[0].blendEnable = VK_TRUE;
color_attachment[0].srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
color_attachment[0].dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
color_attachment[0].colorBlendOp = VK_BLEND_OP_ADD;
color_attachment[0].srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
color_attachment[0].dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
color_attachment[0].alphaBlendOp = VK_BLEND_OP_ADD;
color_attachment[0].colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
VkPipelineDepthStencilStateCreateInfo depth_info = {};
depth_info.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
VkPipelineColorBlendStateCreateInfo blend_info = {};
blend_info.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
blend_info.attachmentCount = 1;
blend_info.pAttachments = color_attachment;
VkDynamicState dynamic_states[2] = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR };
VkPipelineDynamicStateCreateInfo dynamic_state = {};
dynamic_state.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
dynamic_state.dynamicStateCount = (uint32_t)IM_ARRAYSIZE(dynamic_states);
dynamic_state.pDynamicStates = dynamic_states;
if (!ImGui_ImplVulkan_CreatePipelineLayout(device))
return false;
VkGraphicsPipelineCreateInfo info = {};
info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
info.flags = bd->PipelineCreateFlags;
info.stageCount = 2;
info.pStages = stage;
info.pVertexInputState = &vertex_info;
info.pInputAssemblyState = &ia_info;
info.pViewportState = &viewport_info;
info.pRasterizationState = &raster_info;
info.pMultisampleState = &ms_info;
info.pDepthStencilState = &depth_info;
info.pColorBlendState = &blend_info;
info.pDynamicState = &dynamic_state;
info.layout = bd->PipelineLayout;
info.renderPass = renderPass;
info.subpass = 0;
VkResult err = vkCreateGraphicsPipelines(device, pipelineCache, 1, &info, nullptr, pipeline);
return (err == VK_SUCCESS);
}
bool ImGui_ImplVulkan_CreateDeviceObjects()
{
ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData();
if (!bd->VertexStreamBuffer.Create(VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VERTEX_BUFFER_SIZE) ||
!bd->IndexStreamBuffer.Create(VK_BUFFER_USAGE_INDEX_BUFFER_BIT, INDEX_BUFFER_SIZE))
{
return false;
}
if (!ImGui_ImplVulkan_CreatePipeline(g_vulkan_context->GetDevice(), VK_NULL_HANDLE, bd->RenderPass, &bd->Pipeline))
return false;
return true;
}
void ImGui_ImplVulkan_DestroyDeviceObjects()
{
ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData();
bd->VertexStreamBuffer.Destroy(false);
bd->IndexStreamBuffer.Destroy(false);
bd->FontTexture.Destroy(false);
if (bd->ShaderModuleVert) { vkDestroyShaderModule(g_vulkan_context->GetDevice(), bd->ShaderModuleVert, nullptr); bd->ShaderModuleVert = VK_NULL_HANDLE; }
if (bd->ShaderModuleFrag) { vkDestroyShaderModule(g_vulkan_context->GetDevice(), bd->ShaderModuleFrag, nullptr); bd->ShaderModuleFrag = VK_NULL_HANDLE; }
if (bd->FontSampler) { vkDestroySampler(g_vulkan_context->GetDevice(), bd->FontSampler, nullptr); bd->FontSampler = VK_NULL_HANDLE; }
if (bd->DescriptorSetLayout) { vkDestroyDescriptorSetLayout(g_vulkan_context->GetDevice(), bd->DescriptorSetLayout, nullptr); bd->DescriptorSetLayout = VK_NULL_HANDLE; }
if (bd->PipelineLayout) { vkDestroyPipelineLayout(g_vulkan_context->GetDevice(), bd->PipelineLayout, nullptr); bd->PipelineLayout = VK_NULL_HANDLE; }
if (bd->Pipeline) { vkDestroyPipeline(g_vulkan_context->GetDevice(), bd->Pipeline, nullptr); bd->Pipeline = VK_NULL_HANDLE; }
}
bool ImGui_ImplVulkan_Init(VkRenderPass render_pass)
{
ImGuiIO& io = ImGui::GetIO();
IM_ASSERT(io.BackendRendererUserData == NULL && "Already initialized a renderer backend!");
// Setup backend capabilities flags
ImGui_ImplVulkan_Data* bd = IM_NEW(ImGui_ImplVulkan_Data)();
io.BackendRendererUserData = (void*)bd;
io.BackendRendererName = "imgui_impl_vulkan";
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
IM_ASSERT(render_pass != VK_NULL_HANDLE);
bd->RenderPass = render_pass;
return ImGui_ImplVulkan_CreateDeviceObjects();
}
void ImGui_ImplVulkan_Shutdown()
{
ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData();
IM_ASSERT(bd != NULL && "No renderer backend to shutdown, or already shutdown?");
ImGuiIO& io = ImGui::GetIO();
ImGui_ImplVulkan_DestroyDeviceObjects();
io.BackendRendererName = NULL;
io.BackendRendererUserData = NULL;
IM_DELETE(bd);
}
-13
View File
@@ -1,13 +0,0 @@
// dear imgui: Renderer Backend for Vulkan
// This needs to be used along with a Platform Backend (e.g. GLFW, SDL, Win32, custom..)
#pragma once
#include "imgui.h" // IMGUI_IMPL_API
#include "common/Vulkan/Loader.h"
// Called by user code
bool ImGui_ImplVulkan_Init(VkRenderPass render_pass);
void ImGui_ImplVulkan_Shutdown();
void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data);
bool ImGui_ImplVulkan_CreateFontsTexture();
void ImGui_ImplVulkan_DestroyFontUploadObjects();
+4 -4
View File
@@ -31,7 +31,7 @@ static bool s_GSRegistersWritten = false;
void gsSetVideoMode(GS_VideoMode mode)
{
gsVideoMode = mode;
UpdateVSyncRate();
UpdateVSyncRate(false);
}
// Make sure framelimiter options are in sync with GS capabilities.
@@ -40,7 +40,7 @@ void gsReset()
GetMTGS().ResetGS(true);
gsVideoMode = GS_VideoMode::Uninitialized;
memzero(g_RealGSMem);
UpdateVSyncRate();
UpdateVSyncRate(true);
}
void gsUpdateFrequency(Pcsx2Config& config)
@@ -71,7 +71,7 @@ void gsUpdateFrequency(Pcsx2Config& config)
}
GetMTGS().UpdateVSyncMode();
UpdateVSyncRate();
UpdateVSyncRate(true);
}
static __fi void gsCSRwrite( const tGS_CSR& csr )
@@ -227,7 +227,7 @@ void gsWrite64_page_00( u32 mem, u64 value )
if (mem == GS_SMODE1 || mem == GS_SMODE2)
{
if (value != *(u64*)PS2GS_BASE(mem))
UpdateVSyncRate();
UpdateVSyncRate(false);
}
gsWrite64_generic( mem, value );
+252 -155
View File
@@ -32,8 +32,8 @@
#include "common/StringUtil.h"
#include "pcsx2/Config.h"
#include "pcsx2/Counters.h"
#include "pcsx2/Frontend/ImGuiManager.h"
#include "pcsx2/Host.h"
#include "pcsx2/HostDisplay.h"
#include "pcsx2/HostSettings.h"
#include "pcsx2/Frontend/FullscreenUI.h"
#include "pcsx2/Frontend/InputManager.h"
@@ -73,7 +73,6 @@ static HRESULT s_hr = E_FAIL;
Pcsx2Config::GSOptions GSConfig;
static RenderAPI s_render_api;
static u64 s_next_manual_present_time;
int GSinit()
@@ -106,27 +105,6 @@ void GSshutdown()
GSJoinSnapshotThreads();
}
void GSclose()
{
GSTextureReplacements::Shutdown();
if (g_gs_renderer)
{
g_gs_renderer->Destroy();
g_gs_renderer.reset();
}
if (g_gs_device)
{
g_gs_device->Destroy();
g_gs_device.reset();
}
if (g_host_display)
g_host_display->SetGPUTimingEnabled(false);
Host::ReleaseHostDisplay(true);
}
static RenderAPI GetAPIForRenderer(GSRendererType renderer)
{
switch (renderer)
@@ -155,11 +133,54 @@ static RenderAPI GetAPIForRenderer(GSRendererType renderer)
}
}
static bool DoGSOpen(GSRendererType renderer, u8* basemem)
static void UpdateExclusiveFullscreen(bool force_off)
{
s_render_api = g_host_display->GetRenderAPI();
if (!g_gs_device->SupportsExclusiveFullscreen())
return;
switch (g_host_display->GetRenderAPI())
std::string fullscreen_mode = Host::GetBaseStringSettingValue("EmuCore/GS", "FullscreenMode", "");
u32 width, height;
float refresh_rate;
const bool wants_fullscreen = !force_off && Host::IsFullscreen() && !fullscreen_mode.empty() &&
GSDevice::ParseFullscreenMode(fullscreen_mode, &width, &height, &refresh_rate);
const bool is_fullscreen = g_gs_device->IsExclusiveFullscreen();
if (wants_fullscreen == is_fullscreen)
return;
if (wants_fullscreen)
{
Console.WriteLn("Trying to acquire exclusive fullscreen...");
if (g_gs_device->SetExclusiveFullscreen(true, width, height, refresh_rate))
{
Host::AddKeyedOSDMessage(
"UpdateExclusiveFullscreen", "Acquired exclusive fullscreen.", Host::OSD_INFO_DURATION);
}
else
{
Host::AddKeyedOSDMessage(
"UpdateExclusiveFullscreen", "Failed to acquire exclusive fullscreen.", Host::OSD_WARNING_DURATION);
}
}
else
{
Console.WriteLn("Leaving exclusive fullscreen...");
g_gs_device->SetExclusiveFullscreen(false, 0, 0, 0.0f);
Host::AddKeyedOSDMessage("UpdateExclusiveFullscreen", "Lost exclusive fullscreen.", Host::OSD_INFO_DURATION);
}
}
static bool OpenGSDevice(GSRendererType renderer, bool clear_state_on_fail, bool reopening, bool recreate_window)
{
std::optional<WindowInfo> wi = reopening ? Host::UpdateRenderWindow(recreate_window) : Host::AcquireRenderWindow();
if (!wi.has_value())
{
Console.Error("Failed to acquire render window.");
return false;
}
const RenderAPI new_api = GetAPIForRenderer(renderer);
switch (new_api)
{
#ifdef _WIN32
case RenderAPI::D3D11:
@@ -176,7 +197,6 @@ static bool DoGSOpen(GSRendererType renderer, u8* basemem)
#endif
#ifdef ENABLE_OPENGL
case RenderAPI::OpenGL:
case RenderAPI::OpenGLES:
g_gs_device = std::make_unique<GSDeviceOGL>();
break;
#endif
@@ -188,49 +208,93 @@ static bool DoGSOpen(GSRendererType renderer, u8* basemem)
#endif
default:
Console.Error("Unknown render API %u", static_cast<unsigned>(g_host_display->GetRenderAPI()));
Console.Error("Unsupported render API %s", GSDevice::RenderAPIToString(new_api));
return false;
}
if (!g_gs_device->Create())
const VsyncMode vsync_mode = Host::GetEffectiveVSyncMode();
bool okay = g_gs_device->Create(wi.value(), vsync_mode);
if (okay)
{
g_gs_device->Destroy();
g_gs_device.reset();
return false;
}
if (!g_gs_renderer)
{
if (renderer == GSRendererType::Null)
{
g_gs_renderer = std::make_unique<GSRendererNull>();
}
else if (renderer != GSRendererType::SW)
{
g_gs_renderer = std::make_unique<GSRendererHW>();
}
else
{
g_gs_renderer = std::unique_ptr<GSRenderer>(MULTI_ISA_SELECT(makeGSRendererSW)(GSConfig.SWExtraThreads));
}
g_gs_renderer->SetRegsMem(basemem);
g_gs_renderer->ResetPCRTC();
okay = ImGuiManager::Initialize();
if (!okay)
Console.Error("Failed to initialize ImGuiManager");
}
else
{
Console.Warning("(DoGSOpen) Using existing renderer.");
Console.Error("Failed to create GS device");
}
GSConfig.OsdShowGPU = EmuConfig.GS.OsdShowGPU && g_host_display->SetGPUTimingEnabled(true);
if (!okay)
{
ImGuiManager::Shutdown(clear_state_on_fail);
g_gs_device->Destroy();
g_gs_device.reset();
Host::ReleaseRenderWindow();
return false;
}
GSConfig.OsdShowGPU = EmuConfig.GS.OsdShowGPU && g_gs_device->SetGPUTimingEnabled(true);
Console.WriteLn(Color_StrongGreen, "%s Graphics Driver Info:", GSDevice::RenderAPIToString(new_api));
Console.Indent().WriteLn(g_gs_device->GetDriverInfo());
// Switch to exclusive fullscreen if enabled.
UpdateExclusiveFullscreen(false);
return true;
}
static void CloseGSDevice(bool clear_state, bool reopening)
{
if (!g_gs_device)
return;
UpdateExclusiveFullscreen(true);
ImGuiManager::Shutdown(clear_state);
g_gs_device->Destroy();
g_gs_device.reset();
if (!reopening)
Host::ReleaseRenderWindow();
}
static bool OpenGSRenderer(GSRendererType renderer, u8* basemem)
{
if (renderer == GSRendererType::Null)
{
g_gs_renderer = std::make_unique<GSRendererNull>();
}
else if (renderer != GSRendererType::SW)
{
g_gs_renderer = std::make_unique<GSRendererHW>();
}
else
{
g_gs_renderer = std::unique_ptr<GSRenderer>(MULTI_ISA_SELECT(makeGSRendererSW)(GSConfig.SWExtraThreads));
}
g_gs_renderer->SetRegsMem(basemem);
g_gs_renderer->ResetPCRTC();
g_perfmon.Reset();
return true;
}
bool GSreopen(bool recreate_display, bool recreate_renderer, const Pcsx2Config::GSOptions& old_config)
static void CloseGSRenderer()
{
Console.WriteLn("Reopening GS with %s display", recreate_display ? "new" : "existing");
GSTextureReplacements::Shutdown();
if (g_gs_renderer)
{
g_gs_renderer->Destroy();
g_gs_renderer.reset();
}
}
bool GSreopen(bool recreate_device, bool recreate_renderer, const Pcsx2Config::GSOptions& old_config)
{
Console.WriteLn("Reopening GS with %s device and %s renderer", recreate_device ? "new" : "existing",
recreate_renderer ? "new" : "existing");
if (recreate_renderer)
g_gs_renderer->Flush(GSState::GSFlushReason::GSREOPEN);
@@ -238,6 +302,9 @@ bool GSreopen(bool recreate_display, bool recreate_renderer, const Pcsx2Config::
if (GSConfig.UserHacks_ReadTCOnClose)
g_gs_renderer->ReadbackTextureCache();
u8* basemem = g_gs_renderer->GetRegsMem();
const u32 gamecrc = g_gs_renderer->GetGameCRC();
freezeData fd = {};
std::unique_ptr<u8[]> fd_data;
if (recreate_renderer)
@@ -255,6 +322,8 @@ bool GSreopen(bool recreate_display, bool recreate_renderer, const Pcsx2Config::
Console.Error("(GSreopen) Failed to freeze GS");
return false;
}
CloseGSRenderer();
}
else
{
@@ -263,66 +332,35 @@ bool GSreopen(bool recreate_display, bool recreate_renderer, const Pcsx2Config::
g_gs_renderer->PurgePool();
}
if (recreate_display)
if (recreate_device)
{
g_gs_device->ResetAPIState();
if (Host::BeginPresentFrame(true) == HostDisplay::PresentResult::OK)
Host::EndPresentFrame();
}
// We need a new render window when changing APIs.
const bool recreate_window = (g_gs_device->GetRenderAPI() != GetAPIForRenderer(GSConfig.Renderer));
CloseGSDevice(false, true);
u8* basemem = g_gs_renderer->GetRegsMem();
const u32 gamecrc = g_gs_renderer->GetGameCRC();
if (recreate_renderer)
{
GSTextureReplacements::Shutdown();
g_gs_renderer->Destroy();
g_gs_renderer.reset();
}
g_gs_device->Destroy();
g_gs_device.reset();
if (recreate_display)
{
Host::ReleaseHostDisplay(false);
if (!Host::AcquireHostDisplay(GetAPIForRenderer(GSConfig.Renderer), false))
if (!OpenGSDevice(GSConfig.Renderer, false, true, recreate_window) ||
(recreate_renderer && !OpenGSRenderer(GSConfig.Renderer, basemem)))
{
Console.Error("(GSreopen) Failed to reacquire host display");
Host::AddKeyedOSDMessage(
"GSReopenFailed", "Failed to reopen, restoring old configuration.", Host::OSD_CRITICAL_ERROR_DURATION);
// try to get the old one back
if (!Host::AcquireHostDisplay(GetAPIForRenderer(old_config.Renderer), false))
{
pxFailRel("Failed to recreate old config host display");
return false;
}
CloseGSDevice(false, true);
Host::AddKeyedOSDMessage("GSReopenFailed", fmt::format("Failed to open {} display, switching back to {}.",
HostDisplay::RenderAPIToString(GetAPIForRenderer(GSConfig.Renderer)),
HostDisplay::RenderAPIToString(GetAPIForRenderer(old_config.Renderer)), Host::OSD_CRITICAL_ERROR_DURATION));
GSConfig = old_config;
}
}
if (!DoGSOpen(GSConfig.Renderer, basemem))
{
Console.Error("(GSreopen) Failed to recreate GS");
// try the old config
if (recreate_display && GSConfig.Renderer != old_config.Renderer)
{
Host::ReleaseHostDisplay(false);
if (!Host::AcquireHostDisplay(GetAPIForRenderer(old_config.Renderer), false))
if (!OpenGSDevice(GSConfig.Renderer, false, true, recreate_window) ||
(recreate_renderer && !OpenGSRenderer(GSConfig.Renderer, basemem)))
{
pxFailRel("Failed to recreate old config host display (part 2)");
pxFailRel("Failed to reopen GS on old config");
Host::ReleaseRenderWindow();
return false;
}
}
Host::AddKeyedOSDMessage("GSReopenFailed","Failed to reopen, restoring old configuration.", Host::OSD_CRITICAL_ERROR_DURATION);
GSConfig = old_config;
if (!DoGSOpen(GSConfig.Renderer, basemem))
}
else if (recreate_renderer)
{
if (!OpenGSRenderer(GSConfig.Renderer, basemem))
{
pxFailRel("Failed to reopen GS on old config");
Console.Error("(GSreopen) Failed to create new renderer");
return false;
}
}
@@ -349,21 +387,32 @@ bool GSopen(const Pcsx2Config::GSOptions& config, GSRendererType renderer, u8* b
GSConfig = config;
GSConfig.Renderer = renderer;
if (!Host::AcquireHostDisplay(GetAPIForRenderer(renderer), true))
bool res = OpenGSDevice(renderer, true, false, false);
if (res)
{
Console.Error("Failed to acquire host display");
return false;
res = OpenGSRenderer(renderer, basemem);
if (!res)
CloseGSDevice(true, false);
}
if (!DoGSOpen(renderer, basemem))
if (!res)
{
Host::ReleaseHostDisplay(true);
Host::ReportErrorAsync(
"Error", fmt::format("Failed to create render device. This may be due to your GPU not supporting the "
"chosen renderer ({}), or because your graphics drivers need to be updated.",
Pcsx2Config::GSOptions::GetRendererName(EmuConfig.GS.Renderer)));
return false;
}
return true;
}
void GSclose()
{
CloseGSRenderer();
CloseGSDevice(true, false);
}
void GSreset(bool hardware_reset)
{
try
@@ -539,7 +588,7 @@ void GSPresentCurrentFrame()
void GSThrottlePresentation()
{
if (g_host_display->GetVsyncMode() != VsyncMode::Off)
if (g_gs_device->GetVsyncMode() != VsyncMode::Off)
{
// Let vsync take care of throttling.
return;
@@ -547,7 +596,7 @@ void GSThrottlePresentation()
// Manually throttle presentation when vsync isn't enabled, so we don't try to render the
// fullscreen UI at thousands of FPS and make the gpu go brrrrrrrr.
const float surface_refresh_rate = g_host_display->GetWindowInfo().surface_refresh_rate;
const float surface_refresh_rate = g_gs_device->GetWindowInfo().surface_refresh_rate;
const float throttle_rate = (surface_refresh_rate > 0.0f) ? surface_refresh_rate : 60.0f;
const u64 sleep_period = static_cast<u64>(static_cast<double>(GetTickFrequency()) / static_cast<double>(throttle_rate));
@@ -564,11 +613,96 @@ void GSThrottlePresentation()
Threading::SleepUntil(s_next_manual_present_time);
}
void GSsetGameCRC(u32 crc)
void GSSetGameCRC(u32 crc)
{
g_gs_renderer->SetGameCRC(crc);
}
void GSResizeDisplayWindow(int width, int height, float scale)
{
g_gs_device->ResizeWindow(width, height, scale);
ImGuiManager::WindowResized();
}
void GSUpdateDisplayWindow()
{
UpdateExclusiveFullscreen(true);
g_gs_device->DestroySurface();
const std::optional<WindowInfo> wi = Host::UpdateRenderWindow(false);
if (!wi.has_value())
{
pxFailRel("Failed to get window info after update.");
return;
}
if (!g_gs_device->ChangeWindow(wi.value()))
{
pxFailRel("Failed to change window after update.");
return;
}
UpdateExclusiveFullscreen(false);
ImGuiManager::WindowResized();
}
void GSSetVSyncMode(VsyncMode mode)
{
g_gs_device->SetVSync(mode);
}
bool GSGetHostRefreshRate(float* refresh_rate)
{
if (!g_gs_device)
return false;
return g_gs_device->GetHostRefreshRate(refresh_rate);
}
void GSGetAdaptersAndFullscreenModes(
GSRendererType renderer, std::vector<std::string>* adapters, std::vector<std::string>* fullscreen_modes)
{
switch (renderer)
{
#ifdef _WIN32
case GSRendererType::DX11:
case GSRendererType::DX12:
{
auto factory = D3D::CreateFactory(false);
if (factory)
{
if (adapters)
*adapters = D3D::GetAdapterNames(factory.get());
if (fullscreen_modes)
*fullscreen_modes = D3D::GetFullscreenModes(factory.get(), EmuConfig.GS.Adapter);
}
}
break;
#endif
#ifdef ENABLE_VULKAN
case GSRendererType::VK:
{
GSDeviceVK::GetAdaptersAndFullscreenModes(adapters, fullscreen_modes);
}
break;
#endif
#ifdef __APPLE__
case GSRendererType::Metal:
{
if (adapters)
*adapters = GetMetalAdapterList();
}
break;
#endif
default:
break;
}
}
GSVideoMode GSgetDisplayMode()
{
GSRenderer* gs = g_gs_renderer.get();
@@ -594,7 +728,7 @@ void GSgetInternalResolution(int* width, int* height)
void GSgetStats(std::string& info)
{
GSPerfMon& pm = g_perfmon;
const char* api_name = HostDisplay::RenderAPIToString(s_render_api);
const char* api_name = GSDevice::RenderAPIToString(g_gs_device->GetRenderAPI());
if (GSConfig.Renderer == GSRendererType::SW)
{
const double fps = GetVerticalFrequency();
@@ -661,7 +795,7 @@ void GSgetTitleStats(std::string& info)
static constexpr const char* deinterlace_modes[] = {
"Automatic", "None", "Weave tff", "Weave bff", "Bob tff", "Bob bff", "Blend tff", "Blend bff", "Adaptive tff", "Adaptive bff"};
const char* api_name = HostDisplay::RenderAPIToString(s_render_api);
const char* api_name = GSDevice::RenderAPIToString(g_gs_device->GetRenderAPI());
const char* hw_sw_name = (GSConfig.Renderer == GSRendererType::Null) ? " Null" : (GSConfig.UseHardwareRenderer() ? " HW" : " SW");
const char* deinterlace_mode = deinterlace_modes[static_cast<int>(GSConfig.InterlaceMode)];
@@ -681,28 +815,12 @@ void GSUpdateConfig(const Pcsx2Config::GSOptions& new_config)
// Handle OSD scale changes by pushing a window resize through.
if (new_config.OsdScale != old_config.OsdScale)
{
g_gs_device->ResetAPIState();
Host::ResizeHostDisplay(g_host_display->GetWindowWidth(), g_host_display->GetWindowHeight(), g_host_display->GetWindowScale());
g_gs_device->RestoreAPIState();
}
ImGuiManager::WindowResized();
// Options which need a full teardown/recreate.
if (!GSConfig.RestartOptionsAreEqual(old_config))
{
RenderAPI existing_api = g_host_display->GetRenderAPI();
if (existing_api == RenderAPI::OpenGLES)
existing_api = RenderAPI::OpenGL;
const bool do_full_restart = (
existing_api != GetAPIForRenderer(GSConfig.Renderer) ||
GSConfig.Adapter != old_config.Adapter ||
GSConfig.UseDebugDevice != old_config.UseDebugDevice ||
GSConfig.UseBlitSwapChain != old_config.UseBlitSwapChain ||
GSConfig.DisableShaderCache != old_config.DisableShaderCache ||
GSConfig.DisableThreadedPresentation != old_config.DisableThreadedPresentation
);
if (!GSreopen(do_full_restart, true, old_config))
if (!GSreopen(true, true, old_config))
pxFailRel("Failed to do full GS reopen");
return;
}
@@ -771,7 +889,7 @@ void GSUpdateConfig(const Pcsx2Config::GSOptions& new_config)
if (GSConfig.OsdShowGPU != old_config.OsdShowGPU)
{
if (!g_host_display->SetGPUTimingEnabled(GSConfig.OsdShowGPU))
if (!g_gs_device->SetGPUTimingEnabled(GSConfig.OsdShowGPU))
GSConfig.OsdShowGPU = false;
}
}
@@ -784,34 +902,13 @@ void GSSwitchRenderer(GSRendererType new_renderer)
if (!g_gs_renderer || GSConfig.Renderer == new_renderer)
return;
RenderAPI existing_api = g_host_display->GetRenderAPI();
if (existing_api == RenderAPI::OpenGLES)
existing_api = RenderAPI::OpenGL;
const bool is_software_switch = (new_renderer == GSRendererType::SW || GSConfig.Renderer == GSRendererType::SW);
const bool recreate_display = (!is_software_switch && existing_api != GetAPIForRenderer(new_renderer));
const Pcsx2Config::GSOptions old_config(GSConfig);
GSConfig.Renderer = new_renderer;
if (!GSreopen(recreate_display, true, old_config))
if (!GSreopen(!is_software_switch, true, old_config))
pxFailRel("Failed to reopen GS for renderer switch.");
}
void GSResetAPIState()
{
if (!g_gs_device)
return;
g_gs_device->ResetAPIState();
}
void GSRestoreAPIState()
{
if (!g_gs_device)
return;
g_gs_device->RestoreAPIState();
}
bool GSSaveSnapshotToMemory(u32 window_width, u32 window_height, bool apply_aspect, bool crop_borders,
u32* width, u32* height, std::vector<u32>* pixels)
{
+53 -4
View File
@@ -20,8 +20,20 @@
#include "pcsx2/Config.h"
#include <map>
#include <optional>
#include <string>
#include <string_view>
#include <vector>
enum class RenderAPI
{
None,
D3D11,
Metal,
D3D12,
Vulkan,
OpenGL
};
// ST_WRITE is defined in libc, avoid this
enum stateType
@@ -42,6 +54,13 @@ enum class GSVideoMode : u8
HDTV_1080I
};
enum class GSDisplayAlignment
{
Center,
LeftOrTop,
RightOrBottom
};
extern Pcsx2Config::GSOptions GSConfig;
class HostDisplay;
@@ -53,7 +72,7 @@ s16 GSLookupBeforeDrawFunctionId(const std::string_view& name);
int GSinit();
void GSshutdown();
bool GSopen(const Pcsx2Config::GSOptions& config, GSRendererType renderer, u8* basemem);
bool GSreopen(bool recreate_display, bool recreate_renderer, const Pcsx2Config::GSOptions& old_config);
bool GSreopen(bool recreate_device, bool recreate_renderer, const Pcsx2Config::GSOptions& old_config);
void GSreset(bool hardware_reset);
void GSclose();
void GSgifSoftReset(u32 mask);
@@ -74,8 +93,15 @@ bool GSBeginCapture(std::string filename);
void GSEndCapture();
void GSPresentCurrentFrame();
void GSThrottlePresentation();
void GSsetGameCRC(u32 crc);
void GSSetGameCRC(u32 crc);
void GSSetDisplayAlignment(GSDisplayAlignment alignment);
void GSResizeDisplayWindow(int width, int height, float scale);
void GSUpdateDisplayWindow();
void GSSetVSyncMode(VsyncMode mode);
bool GSGetHostRefreshRate(float* refresh_rate);
void GSGetAdaptersAndFullscreenModes(
GSRendererType renderer, std::vector<std::string>* adapters, std::vector<std::string>* fullscreen_modes);
GSVideoMode GSgetDisplayMode();
void GSgetInternalResolution(int* width, int* height);
void GSgetStats(std::string& info);
@@ -88,8 +114,6 @@ void GSTranslateWindowToDisplayCoordinates(float window_x, float window_y, float
void GSUpdateConfig(const Pcsx2Config::GSOptions& new_config);
void GSSwitchRenderer(GSRendererType new_renderer);
void GSResetAPIState();
void GSRestoreAPIState();
bool GSSaveSnapshotToMemory(u32 window_width, u32 window_height, bool apply_aspect, bool crop_borders,
u32* width, u32* height, std::vector<u32>* pixels);
void GSJoinSnapshotThreads();
@@ -100,3 +124,28 @@ struct GSError
struct GSRecoverableError : GSError
{
};
namespace Host
{
/// Called when the GS is creating a render device.
std::optional<WindowInfo> AcquireRenderWindow();
/// Called on the MTGS thread when a request to update the display is received.
/// This could be a fullscreen transition, for example.
std::optional<WindowInfo> UpdateRenderWindow(bool recreate_window);
/// Called before drawing the OSD and other display elements.
void BeginPresentFrame();
/// Called when the GS is finished with a render window.
void ReleaseRenderWindow();
/// Returns true if the hosting application is currently fullscreen.
bool IsFullscreen();
/// Alters fullscreen state of hosting application.
void SetFullscreen(bool enabled);
/// Returns the desired vsync mode, depending on the runtime environment.
VsyncMode GetEffectiveVSyncMode();
}
+3 -3
View File
@@ -460,7 +460,7 @@ public:
typedef u32 (GSLocalMemory::*readPixelAddr)(u32 addr) const;
typedef u32 (GSLocalMemory::*readTexelAddr)(u32 addr, const GIFRegTEXA& TEXA) const;
typedef void (*writeImage)(GSLocalMemory& mem, int& tx, int& ty, const u8* src, int len, GIFRegBITBLTBUF& BITBLTBUF, GIFRegTRXPOS& TRXPOS, GIFRegTRXREG& TRXREG);
typedef void (*readImage)(const GSLocalMemory& mem, int& tx, int& ty, int& offset, u8* dst, int len, GIFRegBITBLTBUF& BITBLTBUF, GIFRegTRXPOS& TRXPOS, GIFRegTRXREG& TRXREG);
typedef void (*readImage)(const GSLocalMemory& mem, int& tx, int& ty, u8* dst, int len, GIFRegBITBLTBUF& BITBLTBUF, GIFRegTRXPOS& TRXPOS, GIFRegTRXREG& TRXREG);
typedef void (*readTexture)(GSLocalMemory& mem, const GSOffset& off, const GSVector4i& r, u8* dst, int dstpitch, const GIFRegTEXA& TEXA);
typedef void (*readTextureBlock)(const GSLocalMemory& mem, u32 bp, u8* dst, int dstpitch, const GIFRegTEXA& TEXA);
@@ -1124,9 +1124,9 @@ public:
return ReadTexel16(PixelAddress16SZ(x, y, TEX0.TBP0, TEX0.TBW), TEXA);
}
__forceinline void ReadImageX(int& tx, int& ty, int& offset, u8* dst, int len, GIFRegBITBLTBUF& BITBLTBUF, GIFRegTRXPOS& TRXPOS, GIFRegTRXREG& TRXREG) const
__forceinline void ReadImageX(int& tx, int& ty, u8* dst, int len, GIFRegBITBLTBUF& BITBLTBUF, GIFRegTRXPOS& TRXPOS, GIFRegTRXREG& TRXREG) const
{
m_readImageX(*this, tx, ty, offset, dst, len, BITBLTBUF, TRXPOS, TRXREG);
m_readImageX(*this, tx, ty, dst, len, BITBLTBUF, TRXPOS, TRXREG);
}
void ReadTexture(const GSOffset& off, const GSVector4i& r, u8* dst, int dstpitch, const GIFRegTEXA& TEXA);
+9 -17
View File
@@ -87,7 +87,7 @@ class CURRENT_ISA::GSLocalMemoryFunctions
static void ReadTexture(GSLocalMemory& mem, const GSOffset& off, const GSVector4i& r, u8* dst, int dstpitch, const GIFRegTEXA& TEXA);
public:
static void ReadImageX(const GSLocalMemory& mem, int& tx, int& ty, int& offset, u8* dst, int len, GIFRegBITBLTBUF& BITBLTBUF, GIFRegTRXPOS& TRXPOS, GIFRegTRXREG& TRXREG);
static void ReadImageX(const GSLocalMemory& mem, int& tx, int& ty, u8* dst, int len, GIFRegBITBLTBUF& BITBLTBUF, GIFRegTRXPOS& TRXPOS, GIFRegTRXREG& TRXREG);
static void PopulateFunctions(GSLocalMemory& mem);
};
@@ -931,7 +931,7 @@ void GSLocalMemoryFunctions::WriteImageX(GSLocalMemory& mem, int& tx, int& ty, c
//
void GSLocalMemoryFunctions::ReadImageX(const GSLocalMemory& mem, int& tx, int& ty, int& offset, u8* dst, int len, GIFRegBITBLTBUF& BITBLTBUF, GIFRegTRXPOS& TRXPOS, GIFRegTRXREG& TRXREG)
void GSLocalMemoryFunctions::ReadImageX(const GSLocalMemory& mem, int& tx, int& ty, u8* dst, int len, GIFRegBITBLTBUF& BITBLTBUF, GIFRegTRXPOS& TRXPOS, GIFRegTRXREG& TRXREG)
{
if (len <= 0)
return;
@@ -1005,23 +1005,15 @@ void GSLocalMemoryFunctions::ReadImageX(const GSLocalMemory& mem, int& tx, int&
case PSMCT24:
case PSMZ24:
{
const int length = len / 3;
const int aligned_length = (len + 2) / 3;
if (length != aligned_length)
readWriteHelper(mem.vm32(), tx, ty, len / 3, 1, sx, w, off.assertSizesMatch(GSLocalMemory::swizzle32), [&](auto& pa, int x)
{
offset = 3 - (len - (length * 3));
}
readWriteHelper(mem.vm32(), tx, ty, aligned_length, 1, sx, w, off.assertSizesMatch(GSLocalMemory::swizzle32), [&](auto& pa, int x)
{
u32 c = *pa.value(x);
pb[0] = (u8)(c);
pb[1] = (u8)(c >> 8);
pb[2] = (u8)(c >> 16);
pb += 3;
});
u32 c = *pa.value(x);
pb[0] = (u8)(c);
pb[1] = (u8)(c >> 8);
pb[2] = (u8)(c >> 16);
pb += 3;
});
break;
}
case PSMCT16:
case PSMCT16S:
case PSMZ16:
+126 -89
View File
@@ -106,6 +106,7 @@ GSState::GSState()
memset(&m_vertex, 0, sizeof(m_vertex));
memset(&m_index, 0, sizeof(m_index));
memset(m_mem.m_vm8, 0, m_mem.m_vmsize);
m_v.RGBAQ.Q = 1.0f;
GrowVertexBuffer();
@@ -1403,10 +1404,10 @@ void GSState::GIFRegHandlerTRXDIR(const GIFReg* RESTRICT r)
switch (m_env.TRXDIR.XDIR)
{
case 0: // host -> local
m_tr.Init(m_env.TRXPOS.DSAX, m_env.TRXPOS.DSAY, m_env.BITBLTBUF);
m_tr.Init(m_env.TRXPOS.DSAX, m_env.TRXPOS.DSAY, m_env.BITBLTBUF, true);
break;
case 1: // local -> host
m_tr.Init(m_env.TRXPOS.SSAX, m_env.TRXPOS.SSAY, m_env.BITBLTBUF);
m_tr.Init(m_env.TRXPOS.SSAX, m_env.TRXPOS.SSAY, m_env.BITBLTBUF, false);
break;
case 2: // local -> local
Move();
@@ -1463,6 +1464,9 @@ void GSState::Flush(GSFlushReason reason)
void GSState::FlushWrite()
{
if (!m_tr.write)
return;
const int len = m_tr.end - m_tr.start;
if (len <= 0)
@@ -1474,6 +1478,7 @@ void GSState::FlushWrite()
r.top = m_env.TRXPOS.DSAY;
r.right = r.left + m_env.TRXREG.RRW;
r.bottom = r.top + m_env.TRXREG.RRH;
InvalidateVideoMem(m_env.BITBLTBUF, r, true);
const GSLocalMemory::writeImage wi = GSLocalMemory::m_psm[m_env.BITBLTBUF.DPSM].wi;
@@ -1690,7 +1695,6 @@ void GSState::Write(const u8* mem, int len)
//
// The no-solution: instead to handle garbage (aka RT) at the end of the
// depth buffer. Let's reduce the size of the transfer
if (m_game.title == CRC::SMTNocturne) // TODO: hack
{
if (blit.DBP == 0 && blit.DPSM == PSMZ32 && w == 512 && h > 224)
@@ -1703,77 +1707,78 @@ void GSState::Write(const u8* mem, int len)
if (!m_tr.Update(w, h, psm.trbpp, len))
return;
GIFRegTEX0& prev_tex0 = m_prev_env.CTXT[m_prev_env.PRIM.CTXT].TEX0;
const u32 write_start_bp = m_mem.m_psm[blit.DPSM].info.bn(m_env.TRXPOS.DSAX, m_env.TRXPOS.DSAY, blit.DBP, blit.DBW); // (m_mem.*psm.pa)(static_cast<int>(m_env.TRXPOS.DSAX), static_cast<int>(m_env.TRXPOS.DSAY), blit.DBP, blit.DBW) >> 6;
const u32 write_end_bp = m_mem.m_psm[blit.DPSM].info.bn(m_env.TRXPOS.DSAX + w - 1, m_env.TRXPOS.DSAY + h - 1, blit.DBP, blit.DBW); // (m_mem.*psm.pa)(w + static_cast<int>(m_env.TRXPOS.DSAX) - 1, h + static_cast<int>(m_env.TRXPOS.DSAY) - 1, blit.DBP, blit.DBW) >> 6;
const u32 tex_end_bp = m_mem.m_psm[prev_tex0.PSM].info.bn((1 << prev_tex0.TW) - 1, (1 << prev_tex0.TH) - 1, prev_tex0.TBP0, prev_tex0.TBW); // (m_mem.*psm.pa)((1 << prev_tex0.TW) - 1, (1 << prev_tex0.TH) - 1, prev_tex0.TBP0, prev_tex0.TBW) >> 6;
// Only flush on a NEW transfer if a pending one is using the same address or overlap.
// Check Fast & Furious (Hardare mode) and Assault Suits Valken (either renderer) and Tomb Raider - Angel of Darkness menu (TBP != DBP but overlaps).
if (m_tr.end == 0 && m_index.tail > 0 && m_prev_env.PRIM.TME && write_end_bp > prev_tex0.TBP0 && write_start_bp <= tex_end_bp)
if (m_tr.end == 0)
{
Flush(GSFlushReason::UPLOADDIRTYTEX);
}
// Invalid the CLUT if it crosses paths.
m_mem.m_clut.InvalidateRange(write_start_bp, write_end_bp);
const GIFRegTEX0& prev_tex0 = m_prev_env.CTXT[m_prev_env.PRIM.CTXT].TEX0;
const u32 write_start_bp = m_mem.m_psm[blit.DPSM].info.bn(m_env.TRXPOS.DSAX, m_env.TRXPOS.DSAY, blit.DBP, blit.DBW);
const u32 write_end_bp = m_mem.m_psm[blit.DPSM].info.bn(m_env.TRXPOS.DSAX + w - 1, m_env.TRXPOS.DSAY + h - 1, blit.DBP, blit.DBW);
const u32 tex_end_bp = m_mem.m_psm[prev_tex0.PSM].info.bn((1 << prev_tex0.TW) - 1, (1 << prev_tex0.TH) - 1, prev_tex0.TBP0, prev_tex0.TBW);
// Only flush on a NEW transfer if a pending one is using the same address or overlap.
// Check Fast & Furious (Hardare mode) and Assault Suits Valken (either renderer) and Tomb Raider - Angel of Darkness menu (TBP != DBP but overlaps).
if (m_index.tail > 0 && m_prev_env.PRIM.TME && write_end_bp > prev_tex0.TBP0 && write_start_bp <= tex_end_bp)
{
Flush(GSFlushReason::UPLOADDIRTYTEX);
}
// Invalid the CLUT if it crosses paths.
m_mem.m_clut.InvalidateRange(write_start_bp, write_end_bp);
GSVector4i r;
GSVector4i r;
r.left = m_env.TRXPOS.DSAX;
r.top = m_env.TRXPOS.DSAY;
r.right = r.left + m_env.TRXREG.RRW;
r.bottom = r.top + m_env.TRXREG.RRH;
r.left = m_env.TRXPOS.DSAX;
r.top = m_env.TRXPOS.DSAY;
r.right = r.left + m_env.TRXREG.RRW;
r.bottom = r.top + m_env.TRXREG.RRH;
// Store the transfer for preloading new RT's.
if ((m_draw_transfers.size() > 0 && blit.DBP == m_draw_transfers.back().blit.DBP))
{
// Same BP, let's update the rect.
GSUploadQueue transfer = m_draw_transfers.back();
m_draw_transfers.pop_back();
transfer.rect = transfer.rect.runion(r);
m_draw_transfers.push_back(transfer);
}
else
{
GSUploadQueue new_transfer = { blit, r, s_n };
m_draw_transfers.push_back(new_transfer);
// Store the transfer for preloading new RT's.
if ((m_draw_transfers.size() > 0 && blit.DBP == m_draw_transfers.back().blit.DBP))
{
// Same BP, let's update the rect.
GSUploadQueue transfer = m_draw_transfers.back();
m_draw_transfers.pop_back();
transfer.rect = transfer.rect.runion(r);
m_draw_transfers.push_back(transfer);
}
else
{
GSUploadQueue new_transfer = { blit, r, s_n };
m_draw_transfers.push_back(new_transfer);
}
GL_CACHE("Write! %u ... => 0x%x W:%d F:%s (DIR %d%d), dPos(%d %d) size(%d %d)", s_transfer_n,
blit.DBP, blit.DBW, psm_str(blit.DPSM),
m_env.TRXPOS.DIRX, m_env.TRXPOS.DIRY,
m_env.TRXPOS.DSAX, m_env.TRXPOS.DSAY, w, h);
if (len >= m_tr.total)
{
// received all data in one piece, no need to buffer it
InvalidateVideoMem(blit, r, true);
psm.wi(m_mem, m_tr.x, m_tr.y, mem, m_tr.total, blit, m_env.TRXPOS, m_env.TRXREG);
m_tr.start = m_tr.end = m_tr.total;
g_perfmon.Put(GSPerfMon::Swizzle, len);
s_transfer_n++;
return;
}
}
GL_CACHE("Write! %u ... => 0x%x W:%d F:%s (DIR %d%d), dPos(%d %d) size(%d %d)", s_transfer_n,
blit.DBP, blit.DBW, psm_str(blit.DPSM),
m_env.TRXPOS.DIRX, m_env.TRXPOS.DIRY,
m_env.TRXPOS.DSAX, m_env.TRXPOS.DSAY, w, h);
memcpy(&m_tr.buff[m_tr.end], mem, len);
if (m_tr.end == 0 && len >= m_tr.total)
{
// received all data in one piece, no need to buffer it
InvalidateVideoMem(blit, r, true);
m_tr.end += len;
psm.wi(m_mem, m_tr.x, m_tr.y, mem, m_tr.total, blit, m_env.TRXPOS, m_env.TRXREG);
m_tr.start = m_tr.end = m_tr.total;
g_perfmon.Put(GSPerfMon::Swizzle, len);
s_transfer_n++;
}
else
{
memcpy(&m_tr.buff[m_tr.end], mem, len);
m_tr.end += len;
if (m_tr.end >= m_tr.total)
FlushWrite();
}
if (m_tr.end >= m_tr.total)
FlushWrite();
}
void GSState::InitReadFIFO(u8* mem, int len)
{
if (len <= 0)
// No size or already a transfer in progress.
if (len <= 0 || m_tr.total != 0)
return;
const int sx = m_env.TRXPOS.SSAX;
const int sy = m_env.TRXPOS.SSAY;
const int w = m_env.TRXREG.RRW;
const int h = m_env.TRXREG.RRH;
@@ -1782,31 +1787,15 @@ void GSState::InitReadFIFO(u8* mem, int len)
if (!m_tr.Update(w, h, bpp, len))
return;
if (m_tr.x == sx && m_tr.y == sy)
InvalidateLocalMem(m_env.BITBLTBUF, GSVector4i(sx, sy, sx + w, sy + h));
}
// NOTE: called from outside MTGS
void GSState::Read(u8* mem, int len)
{
if (len <= 0)
return;
const int sx = m_env.TRXPOS.SSAX;
const int sy = m_env.TRXPOS.SSAY;
const int w = m_env.TRXREG.RRW;
const int h = m_env.TRXREG.RRH;
const GSVector4i r(sx, sy, sx + w, sy + h);
const u16 bpp = GSLocalMemory::m_psm[m_env.BITBLTBUF.SPSM].trbpp;
if (m_tr.x == sx && m_tr.y == sy)
InvalidateLocalMem(m_env.BITBLTBUF, r);
if (!m_tr.Update(w, h, bpp, len))
return;
mem += m_tr.offset;
len -= m_tr.offset;
m_mem.ReadImageX(m_tr.x, m_tr.y, m_tr.offset, mem, len, m_env.BITBLTBUF, m_env.TRXPOS, m_env.TRXREG);
// Read the image all in one go.
m_mem.ReadImageX(m_tr.x, m_tr.y, m_tr.buff, m_tr.total, m_env.BITBLTBUF, m_env.TRXPOS, m_env.TRXREG);
if (GSConfig.DumpGSData && GSConfig.SaveRT && s_n >= GSConfig.SaveN)
{
@@ -1819,6 +1808,36 @@ void GSState::Read(u8* mem, int len)
}
}
// NOTE: called from outside MTGS
void GSState::Read(u8* mem, int len)
{
if (len <= 0 || m_tr.total == 0)
return;
const int w = m_env.TRXREG.RRW;
const int h = m_env.TRXREG.RRH;
const u16 bpp = GSLocalMemory::m_psm[m_env.BITBLTBUF.SPSM].trbpp;
if (!m_tr.Update(w, h, bpp, len))
return;
// If it wraps memory, we need to break it up so we don't read out of bounds.
if ((m_tr.end + len) > m_mem.m_vmsize)
{
const int first_transfer = m_mem.m_vmsize - m_tr.end;
const int second_transfer = len - first_transfer;
memcpy(mem, &m_tr.buff[m_tr.end], first_transfer);
m_tr.end = 0;
memcpy(&mem[first_transfer], &m_tr.buff, second_transfer);
m_tr.end = second_transfer;
}
else
{
memcpy(mem, &m_tr.buff[m_tr.end], len);
m_tr.end += len;
}
}
void GSState::Move()
{
// ffxii uses this to move the top/bottom of the scrolling menus offscreen and then blends them back over the text to create a shading effect
@@ -2091,15 +2110,34 @@ void GSState::ReadLocalMemoryUnsync(u8* mem, int qwc, GIFRegBITBLTBUF BITBLTBUF,
const u16 bpp = GSLocalMemory::m_psm[BITBLTBUF.SPSM].trbpp;
GSTransferBuffer tb;
tb.Init(TRXPOS.SSAX, TRXPOS.SSAY, BITBLTBUF);
if(m_tr.end >= m_tr.total || m_tr.write == true)
tb.Init(TRXPOS.SSAX, TRXPOS.SSAY, BITBLTBUF, false);
int len = qwc * 16;
if (!tb.Update(w, h, bpp, len))
return;
mem += tb.offset;
len -= tb.offset;
m_mem.ReadImageX(tb.x, tb.y, tb.offset, mem, len, BITBLTBUF, TRXPOS, TRXREG);
if (m_tr.start == 0)
{
m_mem.ReadImageX(tb.x, tb.y, m_tr.buff, m_tr.total, BITBLTBUF, TRXPOS, TRXREG);
m_tr.start += m_tr.total;
}
if ((m_tr.end + len) > m_mem.m_vmsize)
{
const int masked_end = m_tr.end & 0x3FFFFF; // 4mb.
const int first_transfer = m_mem.m_vmsize - masked_end;
const int second_transfer = len - first_transfer;
memcpy(mem, &m_tr.buff[masked_end], first_transfer);
memcpy(&mem[first_transfer], &m_tr.buff, second_transfer);
m_tr.end += len;
}
else
{
memcpy(mem, &m_tr.buff[m_tr.end], len);
m_tr.end += len;
}
}
void GSState::PurgePool()
@@ -3857,13 +3895,15 @@ GSState::GSTransferBuffer::~GSTransferBuffer()
_aligned_free(buff);
}
void GSState::GSTransferBuffer::Init(int tx, int ty, const GIFRegBITBLTBUF& blit)
void GSState::GSTransferBuffer::Init(int tx, int ty, const GIFRegBITBLTBUF& blit, bool is_write)
{
x = tx;
y = ty;
total = 0;
offset = 0;
start = 0;
end = 0;
m_blit = blit;
write = is_write;
}
bool GSState::GSTransferBuffer::Update(int tw, int th, int bpp, int& len)
@@ -3872,10 +3912,7 @@ bool GSState::GSTransferBuffer::Update(int tw, int th, int bpp, int& len)
int packet_size = (tex_size + 15) & ~0xF; // Round up to the nearest quadword
if (total == 0)
{
start = end = 0;
total = std::min<int>(tex_size, 1024 * 1024 * 4);
}
const int remaining = total - end;
+2 -2
View File
@@ -123,14 +123,14 @@ private:
{
int x = 0, y = 0;
int start = 0, end = 0, total = 0;
int offset = 0;
u8* buff = nullptr;
GIFRegBITBLTBUF m_blit = {};
bool write = false;
GSTransferBuffer();
virtual ~GSTransferBuffer();
void Init(int tx, int ty, const GIFRegBITBLTBUF& blit);
void Init(int tx, int ty, const GIFRegBITBLTBUF& blit, bool write);
bool Update(int tw, int th, int bpp, int& len);
} m_tr;
+15 -5
View File
@@ -14,18 +14,22 @@
*/
#include "PrecompiledHeader.h"
#include "GS.h"
#include "GSExtra.h"
#include "GSUtil.h"
#include "GS/GS.h"
#include "GS/GSExtra.h"
#include "GS/GSUtil.h"
#include "MultiISA.h"
#include "common/StringUtil.h"
#ifdef ENABLE_VULKAN
#include "GS/Renderers/Vulkan/GSDeviceVK.h"
#endif
#ifdef _WIN32
#include "common/RedtapeWindows.h"
#include <d3dcommon.h>
#include <dxgi.h>
#include <VersionHelpers.h>
#include "Renderers/DX11/D3D.h"
#include "GS/Renderers/DX11/D3D.h"
#include <wil/com.h>
#endif
@@ -206,7 +210,13 @@ GSRendererType GSUtil::GetPreferredRenderer()
// Use D3D device info to select renderer.
return D3D::GetPreferredRenderer();
#else
// Linux: Prefer GL/Vulkan, whatever is available.
// Linux: Prefer Vulkan if the driver isn't buggy.
#if defined(ENABLE_VULKAN)
if (GSDeviceVK::IsSuitableDefaultRenderer())
return GSRendererType::VK;
#endif
// Otherwise, whatever is available.
#if defined(ENABLE_OPENGL)
return GSRendererType::OGL;
#elif defined(ENABLE_VULKAN)
+126 -13
View File
@@ -20,6 +20,8 @@
#include "Host.h"
#include "common/StringUtil.h"
#include "imgui.h"
#include <algorithm>
const char* shaderName(ShaderConvert value)
@@ -91,23 +93,137 @@ GSDevice::~GSDevice()
pxAssert(m_pool.empty() && !m_merge && !m_weavebob && !m_blend && !m_mad && !m_target_tmp && !m_cas);
}
bool GSDevice::Create()
const char* GSDevice::RenderAPIToString(RenderAPI api)
{
switch (api)
{
// clang-format off
#define CASE(x) case RenderAPI::x: return #x
CASE(None);
CASE(D3D11);
CASE(D3D12);
CASE(Metal);
CASE(Vulkan);
CASE(OpenGL);
#undef CASE
// clang-format on
default:
return "Unknown";
}
}
bool GSDevice::ParseFullscreenMode(const std::string_view& mode, u32* width, u32* height, float* refresh_rate)
{
if (!mode.empty())
{
std::string_view::size_type sep1 = mode.find('x');
if (sep1 != std::string_view::npos)
{
std::optional<u32> owidth = StringUtil::FromChars<u32>(mode.substr(0, sep1));
sep1++;
while (sep1 < mode.length() && std::isspace(mode[sep1]))
sep1++;
if (owidth.has_value() && sep1 < mode.length())
{
std::string_view::size_type sep2 = mode.find('@', sep1);
if (sep2 != std::string_view::npos)
{
std::optional<u32> oheight = StringUtil::FromChars<u32>(mode.substr(sep1, sep2 - sep1));
sep2++;
while (sep2 < mode.length() && std::isspace(mode[sep2]))
sep2++;
if (oheight.has_value() && sep2 < mode.length())
{
std::optional<float> orefresh_rate = StringUtil::FromChars<float>(mode.substr(sep2));
if (orefresh_rate.has_value())
{
*width = owidth.value();
*height = oheight.value();
*refresh_rate = orefresh_rate.value();
return true;
}
}
}
}
}
}
*width = 0;
*height = 0;
*refresh_rate = 0;
return false;
}
std::string GSDevice::GetFullscreenModeString(u32 width, u32 height, float refresh_rate)
{
return StringUtil::StdStringFromFormat("%u x %u @ %f hz", width, height, refresh_rate);
}
bool GSDevice::Create(const WindowInfo& wi, VsyncMode vsync)
{
m_window_info = wi;
m_vsync_mode = vsync;
return true;
}
void GSDevice::Destroy()
{
if (m_imgui_font)
{
Recycle(m_imgui_font);
m_imgui_font = nullptr;
}
ClearCurrent();
PurgePool();
}
void GSDevice::ResetAPIState()
bool GSDevice::GetHostRefreshRate(float* refresh_rate)
{
if (m_window_info.surface_refresh_rate > 0.0f)
{
*refresh_rate = m_window_info.surface_refresh_rate;
return true;
}
return WindowInfo::QueryRefreshRateForWindow(m_window_info, refresh_rate);
}
void GSDevice::RestoreAPIState()
bool GSDevice::UpdateImGuiFontTexture()
{
ImGuiIO& io = ImGui::GetIO();
unsigned char* pixels;
int width, height;
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
const GSVector4i r(0, 0, width, height);
const int pitch = sizeof(u32) * width;
if (m_imgui_font && m_imgui_font->GetWidth() == width && m_imgui_font->GetHeight() == height &&
m_imgui_font->Update(r, pixels, pitch))
{
io.Fonts->SetTexID(m_imgui_font->GetNativeHandle());
return true;
}
GSTexture* new_font = CreateTexture(width, height, 1, GSTexture::Format::Color);
if (!new_font || !new_font->Update(r, pixels, pitch))
{
io.Fonts->SetTexID(m_imgui_font ? m_imgui_font->GetNativeHandle() : nullptr);
return false;
}
// Don't bother recycling, it's unlikely we're going to reuse the same size as imgui for rendering.
delete m_imgui_font;
m_imgui_font = new_font;
ImGui::GetIO().Fonts->SetTexID(new_font->GetNativeHandle());
return true;
}
GSTexture* GSDevice::FetchSurface(GSTexture::Type type, int width, int height, int levels, GSTexture::Format format, bool clear, bool prefer_reuse)
@@ -182,11 +298,6 @@ GSTexture* GSDevice::FetchSurface(GSTexture::Type type, int width, int height, i
return t;
}
std::unique_ptr<GSDownloadTexture> GSDevice::CreateDownloadTexture(u32 width, u32 height, GSTexture::Format format)
{
return {};
}
void GSDevice::Recycle(GSTexture* t)
{
if (!t)
@@ -208,6 +319,12 @@ void GSDevice::Recycle(GSTexture* t)
}
}
bool GSDevice::UsesLowerLeftOrigin() const
{
const RenderAPI api = GetRenderAPI();
return (api == RenderAPI::OpenGL);
}
void GSDevice::AgePool()
{
m_frame++;
@@ -229,10 +346,6 @@ void GSDevice::PurgePool()
m_pool_memory_usage = 0;
}
void GSDevice::ClearSamplerCache()
{
}
GSTexture* GSDevice::CreateRenderTarget(int w, int h, GSTexture::Format format, bool clear)
{
return FetchSurface(GSTexture::Type::RenderTarget, w, h, 1, format, clear, true);
@@ -458,7 +571,7 @@ bool GSDevice::ResizeTexture(GSTexture** t, GSTexture::Type type, int w, int h,
// TODO: We probably want to make this optional if we're overwriting it...
const GSVector4 sRect(0, 0, 1, 1);
const GSVector4 dRect(0, 0, t2->GetWidth(), t2->GetHeight());
StretchRect(m_current, sRect, new_t, dRect, ShaderConvert::COPY, true);
StretchRect(t2, sRect, new_t, dRect, ShaderConvert::COPY, true);
Recycle(t2);
}
+115 -47
View File
@@ -17,15 +17,14 @@
#include "common/HashCombine.h"
#include "common/WindowInfo.h"
#include "GSFastList.h"
#include "GSTexture.h"
#include "GSVertex.h"
#include "GS/GS.h"
#include "GS/Renderers/Common/GSFastList.h"
#include "GS/Renderers/Common/GSTexture.h"
#include "GS/Renderers/Common/GSVertex.h"
#include "GS/GSAlignedClass.h"
#include "GS/GSExtra.h"
#include <array>
class HostDisplay;
enum class ShaderConvert
{
COPY = 0,
@@ -694,6 +693,22 @@ struct alignas(16) GSHWDrawConfig
class GSDevice : public GSAlignedClass<32>
{
public:
enum class PresentResult
{
OK,
FrameSkipped,
DeviceLost
};
enum class DebugMessageCategory
{
Cache,
Reg,
Debug,
Message,
Performance
};
// clang-format off
struct FeatureSupport
{
@@ -747,6 +762,7 @@ public:
private:
FastList<GSTexture*> m_pool;
u64 m_pool_memory_usage = 0;
static const std::array<HWBlend, 3*3*3*3> m_blendMap;
static const std::array<u8, 16> m_replaceDualSrcBlendMap;
@@ -756,6 +772,11 @@ protected:
static constexpr u32 MAX_POOLED_TEXTURES = 300;
static constexpr u32 NUM_CAS_CONSTANTS = 12; // 8 plus src offset x/y, 16 byte alignment
WindowInfo m_window_info;
VsyncMode m_vsync_mode = VsyncMode::Off;
GSTexture* m_imgui_font = nullptr;
GSTexture* m_merge = nullptr;
GSTexture* m_weavebob = nullptr;
GSTexture* m_blend = nullptr;
@@ -781,8 +802,8 @@ protected:
virtual void DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex, GSVector4* dRect, const GSRegPMODE& PMODE, const GSRegEXTBUF& EXTBUF, const GSVector4& c, const bool linear) = 0;
virtual void DoInterlace(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, ShaderInterlace shader, bool linear, const InterlaceConstantBuffer& cb) = 0;
virtual void DoFXAA(GSTexture* sTex, GSTexture* dTex) {}
virtual void DoShadeBoost(GSTexture* sTex, GSTexture* dTex, const float params[4]) {}
virtual void DoFXAA(GSTexture* sTex, GSTexture* dTex) = 0;
virtual void DoShadeBoost(GSTexture* sTex, GSTexture* dTex, const float params[4]) = 0;
/// Resolves CAS shader includes for the specified source.
static bool GetCASShaderSource(std::string* source);
@@ -794,58 +815,111 @@ public:
GSDevice();
virtual ~GSDevice();
/// Returns a string representing the specified API.
static const char* RenderAPIToString(RenderAPI api);
/// Parses a fullscreen mode into its components (width * height @ refresh hz)
static bool ParseFullscreenMode(const std::string_view& mode, u32* width, u32* height, float* refresh_rate);
/// Converts a fullscreen mode to a string.
static std::string GetFullscreenModeString(u32 width, u32 height, float refresh_rate);
__fi unsigned int GetFrameNumber() const { return m_frame; }
__fi u64 GetPoolMemoryUsage() const { return m_pool_memory_usage; }
__fi FeatureSupport Features() const { return m_features; }
__fi const WindowInfo& GetWindowInfo() const { return m_window_info; }
__fi s32 GetWindowWidth() const { return static_cast<s32>(m_window_info.surface_width); }
__fi s32 GetWindowHeight() const { return static_cast<s32>(m_window_info.surface_height); }
__fi GSVector2i GetWindowSize() const { return GSVector2i(static_cast<s32>(m_window_info.surface_width), static_cast<s32>(m_window_info.surface_height)); }
__fi float GetWindowScale() const { return m_window_info.surface_scale; }
__fi VsyncMode GetVsyncMode() const { return m_vsync_mode; }
__fi GSTexture* GetCurrent() const { return m_current; }
void Recycle(GSTexture* t);
enum
{
Windowed,
Fullscreen,
DontCare
};
/// Returns true if it's an OpenGL-based renderer.
bool UsesLowerLeftOrigin() const;
enum class DebugMessageCategory
{
Cache,
Reg,
Debug,
Message,
Performance
};
/// Recreates the font, call when the window scaling changes.
bool UpdateImGuiFontTexture();
virtual bool Create();
virtual bool Create(const WindowInfo& wi, VsyncMode vsync);
virtual void Destroy();
virtual void ResetAPIState();
virtual void RestoreAPIState();
/// Returns the graphics API used by this device.
virtual RenderAPI GetRenderAPI() const = 0;
virtual void ClearRenderTarget(GSTexture* t, const GSVector4& c) {}
virtual void ClearRenderTarget(GSTexture* t, u32 c) {}
virtual void InvalidateRenderTarget(GSTexture* t) {}
virtual void ClearDepth(GSTexture* t) {}
virtual void ClearStencil(GSTexture* t, u8 c) {}
/// Returns true if we have a window we're rendering into.
virtual bool HasSurface() const = 0;
virtual void PushDebugGroup(const char* fmt, ...) {}
virtual void PopDebugGroup() {}
virtual void InsertDebugMessage(DebugMessageCategory category, const char* fmt, ...) {}
/// Destroys the surface we're currently drawing to.
virtual void DestroySurface() = 0;
/// Switches to a new window/surface.
virtual bool ChangeWindow(const WindowInfo& wi) = 0;
/// Call when the window size changes externally to recreate any resources.
virtual void ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale) = 0;
/// Returns true if exclusive fullscreen is supported.
virtual bool SupportsExclusiveFullscreen() const = 0;
/// Returns true if exclusive fullscreen is active.
virtual bool IsExclusiveFullscreen() = 0;
/// Attempts to switch to the specified mode in exclusive fullscreen.
virtual bool SetExclusiveFullscreen(bool fullscreen, u32 width, u32 height, float refresh_rate) = 0;
/// Returns false if the window was completely occluded. If frame_skip is set, the frame won't be
/// displayed, but the GPU command queue will still be flushed.
virtual PresentResult BeginPresent(bool frame_skip) = 0;
/// Presents the frame to the display.
virtual void EndPresent() = 0;
/// Changes vsync mode for this display.
virtual void SetVSync(VsyncMode mode) = 0;
/// Returns the effective refresh rate of this display.
virtual bool GetHostRefreshRate(float* refresh_rate);
/// Returns a string of information about the graphics driver being used.
virtual std::string GetDriverInfo() const = 0;
/// Enables/disables GPU frame timing.
virtual bool SetGPUTimingEnabled(bool enabled) = 0;
/// Returns the amount of GPU time utilized since the last time this method was called.
virtual float GetAndResetAccumulatedGPUTime() = 0;
virtual void ClearRenderTarget(GSTexture* t, const GSVector4& c) = 0;
virtual void ClearRenderTarget(GSTexture* t, u32 c) = 0;
virtual void InvalidateRenderTarget(GSTexture* t) = 0;
virtual void ClearDepth(GSTexture* t) = 0;
virtual void ClearStencil(GSTexture* t, u8 c) = 0;
virtual void PushDebugGroup(const char* fmt, ...) = 0;
virtual void PopDebugGroup() = 0;
virtual void InsertDebugMessage(DebugMessageCategory category, const char* fmt, ...) = 0;
GSTexture* CreateRenderTarget(int w, int h, GSTexture::Format format, bool clear = true);
GSTexture* CreateDepthStencil(int w, int h, GSTexture::Format format, bool clear = true);
GSTexture* CreateTexture(int w, int h, int mipmap_levels, GSTexture::Format format, bool prefer_reuse = false);
GSTexture::Format GetDefaultTextureFormat(GSTexture::Type type);
virtual std::unique_ptr<GSDownloadTexture> CreateDownloadTexture(u32 width, u32 height, GSTexture::Format format);
virtual std::unique_ptr<GSDownloadTexture> CreateDownloadTexture(u32 width, u32 height, GSTexture::Format format) = 0;
virtual void CopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r, u32 destX, u32 destY) {}
virtual void StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, ShaderConvert shader = ShaderConvert::COPY, bool linear = true) {}
virtual void StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, bool red, bool green, bool blue, bool alpha) {}
virtual void CopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r, u32 destX, u32 destY) = 0;
virtual void StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, ShaderConvert shader = ShaderConvert::COPY, bool linear = true) = 0;
virtual void StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, bool red, bool green, bool blue, bool alpha) = 0;
void StretchRect(GSTexture* sTex, GSTexture* dTex, const GSVector4& dRect, ShaderConvert shader = ShaderConvert::COPY, bool linear = true);
/// Performs a screen blit for display. If dTex is null, it assumes you are writing to the system framebuffer/swap chain.
virtual void PresentRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, PresentShader shader, float shaderTime, bool linear) {}
virtual void PresentRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, PresentShader shader, float shaderTime, bool linear) = 0;
/// Same as doing StretchRect for each item, except tries to batch together rectangles in as few draws as possible.
/// The provided list should be sorted by texture, the implementations only check if it's the same as the last.
@@ -855,18 +929,14 @@ public:
static void SortMultiStretchRects(MultiStretchRect* rects, u32 num_rects);
/// Updates a GPU CLUT texture from a source texture.
virtual void UpdateCLUTTexture(GSTexture* sTex, float sScale, u32 offsetX, u32 offsetY, GSTexture* dTex, u32 dOffset, u32 dSize) {}
virtual void UpdateCLUTTexture(GSTexture* sTex, float sScale, u32 offsetX, u32 offsetY, GSTexture* dTex, u32 dOffset, u32 dSize) = 0;
/// Converts a colour format to an indexed format texture.
virtual void ConvertToIndexedTexture(GSTexture* sTex, float sScale, u32 offsetX, u32 offsetY, u32 SBW, u32 SPSM, GSTexture* dTex, u32 DBW, u32 DPSM) {}
virtual void ConvertToIndexedTexture(GSTexture* sTex, float sScale, u32 offsetX, u32 offsetY, u32 SBW, u32 SPSM, GSTexture* dTex, u32 DBW, u32 DPSM) = 0;
/// Converts a colour format to an indexed format texture.
virtual void ConvertToIndexedTexture(GSTexture* sTex, u32 offsetX, u32 offsetY, u32 SBW, u32 SPSM, GSTexture* dTex, u32 DBW, u32 DPSM) {}
virtual void RenderHW(GSHWDrawConfig& config) = 0;
virtual void RenderHW(GSHWDrawConfig& config) {}
__fi FeatureSupport Features() const { return m_features; }
__fi GSTexture* GetCurrent() const { return m_current; }
virtual void ClearSamplerCache() = 0;
void ClearCurrent();
void Merge(GSTexture* sTex[3], GSVector4* sRect, GSVector4* dRect, const GSVector2i& fs, const GSRegPMODE& PMODE, const GSRegEXTBUF& EXTBUF, const GSVector4& c);
@@ -887,8 +957,6 @@ public:
void AgePool();
void PurgePool();
virtual void ClearSamplerCache();
__fi static constexpr bool IsDualSourceBlendFactor(u8 factor)
{
return (factor == SRC1_ALPHA || factor == INV_SRC1_ALPHA || factor == SRC1_COLOR
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
<Type Name="FastList&lt;*&gt;">
<DisplayString>{{ size={m_free_indexes_stack_top} }}</DisplayString>
<Expand>
<Item Name="[size]" ExcludeView="simple">m_free_indexes_stack_top</Item>
<Item Name="[capacity]" ExcludeView="simple">m_capacity - 1</Item>
<CustomListItems MaxItemsPerView="5000" ExcludeView="simple">
<Variable Name="index" InitialValue="m_buffer[0].next_index" />
<Size>m_free_indexes_stack_top</Size>
<Loop>
<Item>m_buffer[index].data,na</Item>
<Exec>index = m_buffer[index].next_index</Exec>
</Loop>
</CustomListItems>
</Expand>
</Type>
</AutoVisualizer>
+65 -35
View File
@@ -14,22 +14,27 @@
*/
#include "PrecompiledHeader.h"
#include "GSRenderer.h"
#include "Frontend/FullscreenUI.h"
#include "Frontend/ImGuiManager.h"
#include "GS/Renderers/Common/GSRenderer.h"
#include "GS/GSCapture.h"
#include "GS/GSGL.h"
#include "GSDumpReplayer.h"
#include "Host.h"
#include "HostDisplay.h"
#include "PerformanceMetrics.h"
#include "pcsx2/Config.h"
#include "IconsFontAwesome5.h"
#include "VMManager.h"
#include "common/FileSystem.h"
#include "common/Image.h"
#include "common/Path.h"
#include "common/StringUtil.h"
#include "common/Timer.h"
#include "fmt/core.h"
#include <algorithm>
#include <array>
#include <deque>
@@ -53,6 +58,9 @@ static GSVector4 s_last_draw_rect;
// Last time we reset the renderer due to a GPU crash, if any.
static Common::Timer::Value s_last_gpu_reset_time;
// Screen alignment
static GSDisplayAlignment s_display_alignment = GSDisplayAlignment::Center;
GSRenderer::GSRenderer()
: m_shader_time_start(Common::Timer::GetCurrentValue())
{
@@ -230,9 +238,9 @@ bool GSRenderer::Merge(int field)
g_gs_device->FXAA();
// Sharpens biinear at lower resolutions, almost nearest but with more uniform pixels.
if (GSConfig.LinearPresent == GSPostBilinearMode::BilinearSharp && (g_host_display->GetWindowWidth() > fs.x || g_host_display->GetWindowHeight() > fs.y))
if (GSConfig.LinearPresent == GSPostBilinearMode::BilinearSharp && (g_gs_device->GetWindowWidth() > fs.x || g_gs_device->GetWindowHeight() > fs.y))
{
g_gs_device->Resize(g_host_display->GetWindowWidth(), g_host_display->GetWindowHeight());
g_gs_device->Resize(g_gs_device->GetWindowWidth(), g_gs_device->GetWindowHeight());
}
if (m_scanmask_used)
@@ -271,7 +279,7 @@ static float GetCurrentAspectRatioFloat(bool is_progressive)
return ars[static_cast<u32>(GSConfig.AspectRatio) + (3u * (is_progressive && GSConfig.AspectRatio == AspectRatioType::RAuto4_3_3_2))];
}
static GSVector4 CalculateDrawDstRect(s32 window_width, s32 window_height, const GSVector4i& src_rect, const GSVector2i& src_size, HostDisplay::Alignment alignment, bool flip_y, bool is_progressive)
static GSVector4 CalculateDrawDstRect(s32 window_width, s32 window_height, const GSVector4i& src_rect, const GSVector2i& src_size, GSDisplayAlignment alignment, bool flip_y, bool is_progressive)
{
const float f_width = static_cast<float>(window_width);
const float f_height = static_cast<float>(window_height);
@@ -318,7 +326,7 @@ static GSVector4 CalculateDrawDstRect(s32 window_width, s32 window_height, const
const GSVector2i fs = GSVector2i(static_cast<int>(static_cast<float>(resolution.x) * g_gs_renderer->GetUpscaleMultiplier()),
static_cast<int>(static_cast<float>(resolution.y) * g_gs_renderer->GetUpscaleMultiplier()));
if (g_host_display->GetWindowWidth() > fs.x || g_host_display->GetWindowHeight() > fs.y)
if (g_gs_device->GetWindowWidth() > fs.x || g_gs_device->GetWindowHeight() > fs.y)
{
t_width *= static_cast<float>(fs.x) / src_rect.width();
t_height *= static_cast<float>(fs.y) / src_rect.height();
@@ -348,13 +356,13 @@ static GSVector4 CalculateDrawDstRect(s32 window_width, s32 window_height, const
{
switch (alignment)
{
case HostDisplay::Alignment::Center:
case GSDisplayAlignment::Center:
target_x = (f_width - target_width) * 0.5f;
break;
case HostDisplay::Alignment::RightOrBottom:
case GSDisplayAlignment::RightOrBottom:
target_x = (f_width - target_width);
break;
case HostDisplay::Alignment::LeftOrTop:
case GSDisplayAlignment::LeftOrTop:
default:
target_x = 0.0f;
break;
@@ -368,13 +376,13 @@ static GSVector4 CalculateDrawDstRect(s32 window_width, s32 window_height, const
{
switch (alignment)
{
case HostDisplay::Alignment::Center:
case GSDisplayAlignment::Center:
target_y = (f_height - target_height) * 0.5f;
break;
case HostDisplay::Alignment::RightOrBottom:
case GSDisplayAlignment::RightOrBottom:
target_y = (f_height - target_height);
break;
case HostDisplay::Alignment::LeftOrTop:
case GSDisplayAlignment::LeftOrTop:
default:
target_y = 0.0f;
break;
@@ -466,11 +474,21 @@ void GSJoinSnapshotThreads()
bool GSRenderer::BeginPresentFrame(bool frame_skip)
{
const HostDisplay::PresentResult result = Host::BeginPresentFrame(frame_skip);
if (result == HostDisplay::PresentResult::OK)
return true;
else if (result == HostDisplay::PresentResult::FrameSkipped)
Host::BeginPresentFrame();
const GSDevice::PresentResult res = g_gs_device->BeginPresent(frame_skip);
if (res == GSDevice::PresentResult::FrameSkipped)
{
// If we're skipping a frame, we need to reset imgui's state, since
// we won't be calling EndPresentFrame().
ImGuiManager::SkipFrame();
return false;
}
else if (res == GSDevice::PresentResult::OK)
{
// All good!
return true;
}
// If we're constantly crashing on something in particular, we don't want to end up in an
// endless reset loop.. that'd probably end up leaking memory and/or crashing us for other
@@ -498,6 +516,17 @@ bool GSRenderer::BeginPresentFrame(bool frame_skip)
return false;
}
void GSRenderer::EndPresentFrame()
{
if (GSDumpReplayer::IsReplayingDump())
GSDumpReplayer::RenderUI();
FullscreenUI::Render();
ImGuiManager::RenderOSD();
g_gs_device->EndPresent();
ImGuiManager::NewFrame();
}
void GSRenderer::VSync(u32 field, bool registers_written)
{
Flush(GSFlushReason::VSYNC);
@@ -542,10 +571,9 @@ void GSRenderer::VSync(u32 field, bool registers_written)
if (skip_frame)
{
g_gs_device->ResetAPIState();
if (BeginPresentFrame(true))
Host::EndPresentFrame();
g_gs_device->RestoreAPIState();
EndPresentFrame();
PerformanceMetrics::Update(registers_written, fb_sprite_frame, true);
return;
}
@@ -564,8 +592,8 @@ void GSRenderer::VSync(u32 field, bool registers_written)
{
src_rect = CalculateDrawSrcRect(current);
src_uv = GSVector4(src_rect) / GSVector4(current->GetSize()).xyxy();
draw_rect = CalculateDrawDstRect(g_host_display->GetWindowWidth(), g_host_display->GetWindowHeight(),
src_rect, current->GetSize(), g_host_display->GetDisplayAlignment(), g_host_display->UsesLowerLeftOrigin(),
draw_rect = CalculateDrawDstRect(g_gs_device->GetWindowWidth(), g_gs_device->GetWindowHeight(),
src_rect, current->GetSize(), s_display_alignment, g_gs_device->UsesLowerLeftOrigin(),
GetVideoMode() == GSVideoMode::SDTV_480P || (GSConfig.PCRTCOverscan && GSConfig.PCRTCOffsets));
s_last_draw_rect = draw_rect;
@@ -576,8 +604,8 @@ void GSRenderer::VSync(u32 field, bool registers_written)
{
// sharpen only if the IR is higher than the display resolution
const bool sharpen_only = (GSConfig.CASMode == GSCASMode::SharpenOnly ||
(current->GetWidth() > g_host_display->GetWindowWidth() &&
current->GetHeight() > g_host_display->GetWindowHeight()));
(current->GetWidth() > g_gs_device->GetWindowWidth() &&
current->GetHeight() > g_gs_device->GetWindowHeight()));
g_gs_device->CAS(current, src_rect, src_uv, draw_rect, sharpen_only);
}
else if (!cas_log_once)
@@ -589,7 +617,6 @@ void GSRenderer::VSync(u32 field, bool registers_written)
}
}
g_gs_device->ResetAPIState();
if (BeginPresentFrame(false))
{
if (current && !blank_frame)
@@ -601,12 +628,12 @@ void GSRenderer::VSync(u32 field, bool registers_written)
s_tv_shader_indices[GSConfig.TVShader], shader_time, GSConfig.LinearPresent != GSPostBilinearMode::Off);
}
Host::EndPresentFrame();
EndPresentFrame();
if (GSConfig.OsdShowGPU)
PerformanceMetrics::OnGPUPresent(g_host_display->GetAndResetAccumulatedGPUTime());
PerformanceMetrics::OnGPUPresent(g_gs_device->GetAndResetAccumulatedGPUTime());
}
g_gs_device->RestoreAPIState();
PerformanceMetrics::Update(registers_written, fb_sprite_frame, false);
// snapshot
@@ -665,8 +692,8 @@ void GSRenderer::VSync(u32 field, bool registers_written)
const bool aspect_correct = (GSConfig.ScreenshotSize != GSScreenshotSize::InternalResolutionUncorrected);
if (g_gs_device->GetCurrent() && SaveSnapshotToMemory(
internal_resolution ? 0 : g_host_display->GetWindowWidth(),
internal_resolution ? 0 : g_host_display->GetWindowHeight(),
internal_resolution ? 0 : g_gs_device->GetWindowWidth(),
internal_resolution ? 0 : g_gs_device->GetWindowHeight(),
aspect_correct, true,
&screenshot_width, &screenshot_height, &screenshot_pixels))
{
@@ -805,7 +832,6 @@ void GSRenderer::StopGSDump()
void GSRenderer::PresentCurrentFrame()
{
g_gs_device->ResetAPIState();
if (BeginPresentFrame(false))
{
GSTexture* current = g_gs_device->GetCurrent();
@@ -813,8 +839,8 @@ void GSRenderer::PresentCurrentFrame()
{
const GSVector4i src_rect(CalculateDrawSrcRect(current));
const GSVector4 src_uv(GSVector4(src_rect) / GSVector4(current->GetSize()).xyxy());
const GSVector4 draw_rect(CalculateDrawDstRect(g_host_display->GetWindowWidth(), g_host_display->GetWindowHeight(),
src_rect, current->GetSize(), g_host_display->GetDisplayAlignment(), g_host_display->UsesLowerLeftOrigin(),
const GSVector4 draw_rect(CalculateDrawDstRect(g_gs_device->GetWindowWidth(), g_gs_device->GetWindowHeight(),
src_rect, current->GetSize(), s_display_alignment, g_gs_device->UsesLowerLeftOrigin(),
GetVideoMode() == GSVideoMode::SDTV_480P || (GSConfig.PCRTCOverscan && GSConfig.PCRTCOffsets)));
s_last_draw_rect = draw_rect;
@@ -825,9 +851,8 @@ void GSRenderer::PresentCurrentFrame()
s_tv_shader_indices[GSConfig.TVShader], shader_time, GSConfig.LinearPresent != GSPostBilinearMode::Off);
}
Host::EndPresentFrame();
EndPresentFrame();
}
g_gs_device->RestoreAPIState();
}
void GSTranslateWindowToDisplayCoordinates(float window_x, float window_y, float* display_x, float* display_y)
@@ -847,6 +872,11 @@ void GSTranslateWindowToDisplayCoordinates(float window_x, float window_y, float
*display_y = rel_y / draw_height;
}
void GSSetDisplayAlignment(GSDisplayAlignment alignment)
{
s_display_alignment = alignment;
}
bool GSRenderer::BeginCapture(std::string filename)
{
const GSVector2i capture_resolution(GSConfig.VideoCaptureAutoResolution ?
@@ -910,7 +940,7 @@ bool GSRenderer::SaveSnapshotToMemory(u32 window_width, u32 window_height, bool
else
{
draw_rect = CalculateDrawDstRect(window_width, window_height, src_rect, current->GetSize(),
HostDisplay::Alignment::LeftOrTop, false, is_progressive);
GSDisplayAlignment::LeftOrTop, false, is_progressive);
}
const u32 draw_width = static_cast<u32>(draw_rect.z - draw_rect.x);
const u32 draw_height = static_cast<u32>(draw_rect.w - draw_rect.y);
+1
View File
@@ -24,6 +24,7 @@ class GSRenderer : public GSState
private:
bool Merge(int field);
bool BeginPresentFrame(bool frame_skip);
void EndPresentFrame();
u64 m_shader_time_start = 0;
+40
View File
@@ -14,6 +14,7 @@
*/
#include "PrecompiledHeader.h"
#include "GS/Renderers/Common/GSDevice.h"
#include "GS/Renderers/DX11/D3D.h"
#include "GS/GSExtra.h"
@@ -80,6 +81,45 @@ std::vector<std::string> D3D::GetAdapterNames(IDXGIFactory5* factory)
return adapter_names;
}
std::vector<std::string> D3D::GetFullscreenModes(IDXGIFactory5* factory, const std::string_view& adapter_name)
{
std::vector<std::string> modes;
HRESULT hr;
wil::com_ptr_nothrow<IDXGIAdapter1> adapter = GetChosenOrFirstAdapter(factory, adapter_name);
if (!adapter)
return modes;
wil::com_ptr_nothrow<IDXGIOutput> output;
if (FAILED(hr = adapter->EnumOutputs(0, &output)))
{
Console.Error("EnumOutputs() failed: %08X", hr);
return modes;
}
UINT num_modes = 0;
if (FAILED(hr = output->GetDisplayModeList(DXGI_FORMAT_R8G8B8A8_UNORM, 0, &num_modes, nullptr)))
{
Console.Error("GetDisplayModeList() failed: %08X", hr);
return modes;
}
std::vector<DXGI_MODE_DESC> dmodes(num_modes);
if (FAILED(hr = output->GetDisplayModeList(DXGI_FORMAT_R8G8B8A8_UNORM, 0, &num_modes, dmodes.data())))
{
Console.Error("GetDisplayModeList() (2) failed: %08X", hr);
return modes;
}
for (const DXGI_MODE_DESC& mode : dmodes)
{
modes.push_back(GSDevice::GetFullscreenModeString(mode.Width, mode.Height,
static_cast<float>(mode.RefreshRate.Numerator) / static_cast<float>(mode.RefreshRate.Denominator)));
}
return modes;
}
wil::com_ptr_nothrow<IDXGIAdapter1> D3D::GetAdapterByName(IDXGIFactory5* factory, const std::string_view& name)
{
if (name.empty())
+3
View File
@@ -33,6 +33,9 @@ namespace D3D
// returns a list of all adapter names
std::vector<std::string> GetAdapterNames(IDXGIFactory5* factory);
// returns a list of fullscreen modes for the specified adapter
std::vector<std::string> GetFullscreenModes(IDXGIFactory5* factory, const std::string_view& adapter_name);
// get an adapter based on name
wil::com_ptr_nothrow<IDXGIAdapter1> GetAdapterByName(IDXGIFactory5* factory, const std::string_view& name);
File diff suppressed because it is too large Load Diff
+81 -27
View File
@@ -21,7 +21,7 @@
#include "common/D3D11/ShaderCache.h"
#include <unordered_map>
#include <wil/com.h>
#include <dxgi1_3.h>
#include <dxgi1_5.h>
#include <d3d11_1.h>
struct GSVertexShader11
@@ -116,32 +116,47 @@ private:
MAX_SAMPLERS = 1,
VERTEX_BUFFER_SIZE = 32 * 1024 * 1024,
INDEX_BUFFER_SIZE = 16 * 1024 * 1024,
NUM_TIMESTAMP_QUERIES = 5,
};
int m_d3d_texsize;
void SetFeatures();
GSTexture* CreateSurface(GSTexture::Type type, int width, int height, int levels, GSTexture::Format format) final;
bool CreateSwapChain(const DXGI_MODE_DESC* fullscreen_mode);
bool CreateSwapChainRTV();
std::unique_ptr<GSDownloadTexture> CreateDownloadTexture(u32 width, u32 height, GSTexture::Format format) final;
bool CreateTimestampQueries();
void DestroyTimestampQueries();
void PopTimestampQuery();
void KickTimestampQuery();
void DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex, GSVector4* dRect, const GSRegPMODE& PMODE, const GSRegEXTBUF& EXTBUF, const GSVector4& c, const bool linear) final;
void DoInterlace(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, ShaderInterlace shader, bool linear, const InterlaceConstantBuffer& cb) final;
void DoFXAA(GSTexture* sTex, GSTexture* dTex) final;
void DoShadeBoost(GSTexture* sTex, GSTexture* dTex, const float params[4]) final;
void DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex, GSVector4* dRect, const GSRegPMODE& PMODE, const GSRegEXTBUF& EXTBUF, const GSVector4& c, const bool linear) override;
void DoInterlace(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, ShaderInterlace shader, bool linear, const InterlaceConstantBuffer& cb) override;
void DoFXAA(GSTexture* sTex, GSTexture* dTex) override;
void DoShadeBoost(GSTexture* sTex, GSTexture* dTex, const float params[4]) override;
bool CreateCASShaders();
bool DoCAS(GSTexture* sTex, GSTexture* dTex, bool sharpen_only, const std::array<u32, NUM_CAS_CONSTANTS>& constants) final;
bool DoCAS(GSTexture* sTex, GSTexture* dTex, bool sharpen_only, const std::array<u32, NUM_CAS_CONSTANTS>& constants) override;
wil::com_ptr_nothrow<ID3D11Device> m_dev;
wil::com_ptr_nothrow<ID3D11DeviceContext> m_ctx;
bool CreateImGuiResources();
void RenderImGui();
wil::com_ptr_nothrow<IDXGIFactory5> m_dxgi_factory;
wil::com_ptr_nothrow<ID3D11Device1> m_dev;
wil::com_ptr_nothrow<ID3D11DeviceContext1> m_ctx;
wil::com_ptr_nothrow<ID3DUserDefinedAnnotation> m_annotation;
wil::com_ptr_nothrow<IDXGISwapChain1> m_swapchain;
wil::com_ptr_nothrow<IDXGISwapChain1> m_swap_chain;
wil::com_ptr_nothrow<ID3D11RenderTargetView> m_swap_chain_rtv;
wil::com_ptr_nothrow<ID3D11Buffer> m_vb;
wil::com_ptr_nothrow<ID3D11Buffer> m_ib;
u32 m_vb_pos = 0; // bytes
u32 m_ib_pos = 0; // indices/sizeof(u32)
int m_d3d_texsize = 0;
bool m_allow_tearing_supported = false;
bool m_using_flip_model_swap_chain = true;
bool m_using_allow_tearing = false;
struct
{
@@ -166,6 +181,13 @@ private:
ID3D11DepthStencilView* dsv;
} m_state;
std::array<std::array<wil::com_ptr_nothrow<ID3D11Query>, 3>, NUM_TIMESTAMP_QUERIES> m_timestamp_queries = {};
float m_accumulated_gpu_time = 0.0f;
u8 m_read_timestamp_query = 0;
u8 m_write_timestamp_query = 0;
u8 m_waiting_timestamp_queries = 0;
bool m_timestamp_query_started = false;
bool m_gpu_timing_enabled = false;
struct
{
@@ -222,6 +244,15 @@ private:
wil::com_ptr_nothrow<ID3D11ComputeShader> cs_sharpen;
} m_cas;
struct
{
wil::com_ptr_nothrow<ID3D11InputLayout> il;
wil::com_ptr_nothrow<ID3D11VertexShader> vs;
wil::com_ptr_nothrow<ID3D11PixelShader> ps;
wil::com_ptr_nothrow<ID3D11BlendState> bs;
wil::com_ptr_nothrow<ID3D11Buffer> vs_cb;
} m_imgui;
// Shaders...
std::unordered_map<u32, GSVertexShader11> m_vs;
@@ -237,8 +268,6 @@ private:
GSHWDrawConfig::VSConstantBuffer m_vs_cb_cache;
GSHWDrawConfig::PSConstantBuffer m_ps_cb_cache;
std::unique_ptr<GSTexture11> m_download_tex;
D3D11::ShaderCache m_shader_cache;
std::string m_tfx_source;
@@ -247,13 +276,32 @@ public:
~GSDevice11() override;
__fi static GSDevice11* GetInstance() { return static_cast<GSDevice11*>(g_gs_device.get()); }
__fi ID3D11Device* GetD3DDevice() const { return m_dev.get(); }
__fi ID3D11DeviceContext* GetD3DContext() const { return m_ctx.get(); }
__fi ID3D11Device1* GetD3DDevice() const { return m_dev.get(); }
__fi ID3D11DeviceContext1* GetD3DContext() const { return m_ctx.get(); }
bool Create() override;
bool Create(const WindowInfo& wi, VsyncMode vsync) override;
void Destroy() override;
void ResetAPIState() override;
void RestoreAPIState() override;
RenderAPI GetRenderAPI() const override;
bool ChangeWindow(const WindowInfo& new_wi) override;
void ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale) override;
bool SupportsExclusiveFullscreen() const override;
bool IsExclusiveFullscreen() override;
bool SetExclusiveFullscreen(bool fullscreen, u32 width, u32 height, float refresh_rate) override;
bool HasSurface() const override;
void DestroySurface() override;
std::string GetDriverInfo() const override;
bool GetHostRefreshRate(float* refresh_rate) override;
void SetVSync(VsyncMode mode) override;
PresentResult BeginPresent(bool frame_skip) override;
void EndPresent() override;
bool SetGPUTimingEnabled(bool enabled) override;
float GetAndResetAccumulatedGPUTime() override;
void DrawPrimitive();
void DrawIndexedPrimitive();
@@ -261,6 +309,7 @@ public:
void ClearRenderTarget(GSTexture* t, const GSVector4& c) override;
void ClearRenderTarget(GSTexture* t, u32 c) override;
void InvalidateRenderTarget(GSTexture* t) override;
void ClearDepth(GSTexture* t) override;
void ClearStencil(GSTexture* t, u8 c) override;
@@ -268,6 +317,9 @@ public:
void PopDebugGroup() override;
void InsertDebugMessage(DebugMessageCategory category, const char* fmt, ...) override;
GSTexture* CreateSurface(GSTexture::Type type, int width, int height, int levels, GSTexture::Format format) override;
std::unique_ptr<GSDownloadTexture> CreateDownloadTexture(u32 width, u32 height, GSTexture::Format format) override;
void CloneTexture(GSTexture* src, GSTexture** dest, const GSVector4i& rect);
void CopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r, u32 destX, u32 destY) override;
@@ -296,7 +348,7 @@ public:
void IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY topology);
void VSSetShader(ID3D11VertexShader* vs, ID3D11Buffer* vs_cb);
void GSSetShader(ID3D11GeometryShader* gs, ID3D11Buffer* gs_cb = NULL);
void GSSetShader(ID3D11GeometryShader* gs, ID3D11Buffer* gs_cb = nullptr);
void PSSetShaderResources(GSTexture* sr0, GSTexture* sr1);
void PSSetShaderResource(int i, GSTexture* sr);
@@ -306,7 +358,9 @@ public:
void OMSetDepthStencilState(ID3D11DepthStencilState* dss, u8 sref);
void OMSetBlendState(ID3D11BlendState* bs, float bf);
void OMSetRenderTargets(GSTexture* rt, GSTexture* ds, const GSVector4i* scissor = NULL);
void OMSetRenderTargets(GSTexture* rt, GSTexture* ds, const GSVector4i* scissor = nullptr);
void SetViewport(const GSVector2i& viewport);
void SetScissor(const GSVector4i& scissor);
bool CreateTextureFX();
void SetupVS(VSSelector sel, const GSHWDrawConfig::VSConstantBuffer* cb);
@@ -314,11 +368,11 @@ public:
void SetupPS(const PSSelector& sel, const GSHWDrawConfig::PSConstantBuffer* cb, PSSamplerSelector ssel);
void SetupOM(OMDepthStencilSelector dssel, OMBlendSelector bsel, u8 afix);
void RenderHW(GSHWDrawConfig& config) final;
void RenderHW(GSHWDrawConfig& config) override;
void ClearSamplerCache() final;
void ClearSamplerCache() override;
ID3D11Device* operator->() { return m_dev.get(); }
operator ID3D11Device*() { return m_dev.get(); }
operator ID3D11DeviceContext*() { return m_ctx.get(); }
ID3D11Device1* operator->() { return m_dev.get(); }
operator ID3D11Device1*() { return m_dev.get(); }
operator ID3D11DeviceContext1*() { return m_ctx.get(); }
};
+599 -32
View File
@@ -14,6 +14,16 @@
*/
#include "PrecompiledHeader.h"
#include "GS/GS.h"
#include "GS/GSGL.h"
#include "GS/GSPerfMon.h"
#include "GS/GSUtil.h"
#include "GS/Renderers/DX11/D3D.h"
#include "GS/Renderers/DX12/GSDevice12.h"
#include "Host.h"
#include "ShaderCacheVersion.h"
#include "common/D3D12/Builders.h"
#include "common/D3D12/Context.h"
#include "common/D3D12/ShaderCache.h"
@@ -21,15 +31,10 @@
#include "common/Align.h"
#include "common/ScopedGuard.h"
#include "common/StringUtil.h"
#include "D3D12MemAlloc.h"
#include "GS.h"
#include "GSDevice12.h"
#include "GS/GSGL.h"
#include "GS/GSPerfMon.h"
#include "GS/GSUtil.h"
#include "Host.h"
#include "HostDisplay.h"
#include "ShaderCacheVersion.h"
#include "imgui.h"
#include <sstream>
#include <limits>
@@ -94,16 +99,52 @@ D3D_SHADER_MACRO* GSDevice12::ShaderMacro::GetPtr(void)
return (D3D_SHADER_MACRO*)mout.data();
}
GSDevice12::GSDevice12()
GSDevice12::GSDevice12() = default;
GSDevice12::~GSDevice12()
{
std::memset(&m_pipeline_selector, 0, sizeof(m_pipeline_selector));
pxAssert(!g_d3d12_context);
}
GSDevice12::~GSDevice12() {}
bool GSDevice12::Create()
RenderAPI GSDevice12::GetRenderAPI() const
{
if (!GSDevice::Create() || !CheckFeatures())
return RenderAPI::D3D12;
}
bool GSDevice12::HasSurface() const
{
return static_cast<bool>(m_swap_chain);
}
bool GSDevice12::Create(const WindowInfo& wi, VsyncMode vsync)
{
if (!GSDevice::Create(wi, vsync))
return false;
m_dxgi_factory = D3D::CreateFactory(EmuConfig.GS.UseDebugDevice);
if (!m_dxgi_factory)
return false;
ComPtr<IDXGIAdapter1> dxgi_adapter = D3D::GetAdapterByName(m_dxgi_factory.get(), EmuConfig.GS.Adapter);
if (!D3D12::Context::Create(m_dxgi_factory.get(), dxgi_adapter.get(), EmuConfig.GS.UseDebugDevice))
{
Console.Error("Failed to create D3D12 context");
return false;
}
BOOL allow_tearing_supported = false;
HRESULT hr = m_dxgi_factory->CheckFeatureSupport(
DXGI_FEATURE_PRESENT_ALLOW_TEARING, &allow_tearing_supported, sizeof(allow_tearing_supported));
m_allow_tearing_supported = (SUCCEEDED(hr) && allow_tearing_supported == TRUE);
if (!CheckFeatures())
{
Console.Error("Your GPU does not support the required D3D12 features.");
return false;
}
if (m_window_info.type != WindowInfo::Type::Surfaceless && !CreateSwapChain(nullptr))
return false;
{
@@ -157,6 +198,9 @@ bool GSDevice12::Create()
}
CompileCASPipelines();
if (!CompileImGuiPipeline())
return false;
InitializeState();
InitializeSamplers();
return true;
@@ -164,25 +208,381 @@ bool GSDevice12::Create()
void GSDevice12::Destroy()
{
if (!g_d3d12_context)
GSDevice::Destroy();
if (g_d3d12_context)
{
EndRenderPass();
ExecuteCommandList(true);
DestroyResources();
g_d3d12_context->WaitForGPUIdle();
GSDevice12::DestroySurface();
g_d3d12_context->Destroy();
}
}
bool GSDevice12::GetHostRefreshRate(float* refresh_rate)
{
if (m_swap_chain && IsExclusiveFullscreen())
{
DXGI_SWAP_CHAIN_DESC desc;
if (SUCCEEDED(m_swap_chain->GetDesc(&desc)) && desc.BufferDesc.RefreshRate.Numerator > 0 &&
desc.BufferDesc.RefreshRate.Denominator > 0)
{
DevCon.WriteLn(
"using fs rr: %u %u", desc.BufferDesc.RefreshRate.Numerator, desc.BufferDesc.RefreshRate.Denominator);
*refresh_rate = static_cast<float>(desc.BufferDesc.RefreshRate.Numerator) /
static_cast<float>(desc.BufferDesc.RefreshRate.Denominator);
return true;
}
}
return GSDevice::GetHostRefreshRate(refresh_rate);
}
void GSDevice12::SetVSync(VsyncMode mode)
{
m_vsync_mode = mode;
}
bool GSDevice12::CreateSwapChain(const DXGI_MODE_DESC* fullscreen_mode)
{
if (m_window_info.type != WindowInfo::Type::Win32)
return false;
const HWND window_hwnd = reinterpret_cast<HWND>(m_window_info.window_handle);
RECT client_rc{};
GetClientRect(window_hwnd, &client_rc);
const u32 width = static_cast<u32>(client_rc.right - client_rc.left);
const u32 height = static_cast<u32>(client_rc.bottom - client_rc.top);
DXGI_SWAP_CHAIN_DESC1 swap_chain_desc = {};
swap_chain_desc.Width = width;
swap_chain_desc.Height = height;
swap_chain_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
swap_chain_desc.SampleDesc.Count = 1;
swap_chain_desc.BufferCount = 3;
swap_chain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swap_chain_desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
m_using_allow_tearing = (m_allow_tearing_supported && !fullscreen_mode);
if (m_using_allow_tearing)
swap_chain_desc.Flags |= DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING;
DXGI_SWAP_CHAIN_FULLSCREEN_DESC fs_desc = {};
if (fullscreen_mode)
{
swap_chain_desc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
swap_chain_desc.Width = fullscreen_mode->Width;
swap_chain_desc.Height = fullscreen_mode->Height;
fs_desc.RefreshRate = fullscreen_mode->RefreshRate;
fs_desc.ScanlineOrdering = fullscreen_mode->ScanlineOrdering;
fs_desc.Scaling = fullscreen_mode->Scaling;
fs_desc.Windowed = FALSE;
}
DevCon.WriteLn("Creating a %dx%d %s swap chain", swap_chain_desc.Width, swap_chain_desc.Height,
fullscreen_mode ? "full-screen" : "windowed");
HRESULT hr = m_dxgi_factory->CreateSwapChainForHwnd(g_d3d12_context->GetCommandQueue(), window_hwnd,
&swap_chain_desc, fullscreen_mode ? &fs_desc : nullptr, nullptr, m_swap_chain.put());
if (FAILED(hr))
{
Console.Error("CreateSwapChainForHwnd failed: 0x%08X", hr);
return false;
}
hr = m_dxgi_factory->MakeWindowAssociation(window_hwnd, DXGI_MWA_NO_WINDOW_CHANGES);
if (FAILED(hr))
Console.Warning("MakeWindowAssociation() to disable ALT+ENTER failed");
return CreateSwapChainRTV();
}
bool GSDevice12::CreateSwapChainRTV()
{
DXGI_SWAP_CHAIN_DESC swap_chain_desc;
HRESULT hr = m_swap_chain->GetDesc(&swap_chain_desc);
if (FAILED(hr))
return false;
for (u32 i = 0; i < swap_chain_desc.BufferCount; i++)
{
ComPtr<ID3D12Resource> backbuffer;
hr = m_swap_chain->GetBuffer(i, IID_PPV_ARGS(backbuffer.put()));
if (FAILED(hr))
{
Console.Error("GetBuffer for RTV failed: 0x%08X", hr);
return false;
}
D3D12::Texture tex;
if (!tex.Adopt(std::move(backbuffer), DXGI_FORMAT_UNKNOWN, swap_chain_desc.BufferDesc.Format,
DXGI_FORMAT_UNKNOWN, D3D12_RESOURCE_STATE_PRESENT))
{
return false;
}
m_swap_chain_buffers.push_back(std::move(tex));
}
m_window_info.surface_width = swap_chain_desc.BufferDesc.Width;
m_window_info.surface_height = swap_chain_desc.BufferDesc.Height;
DevCon.WriteLn("Swap chain buffer size: %ux%u", m_window_info.surface_width, m_window_info.surface_height);
if (m_window_info.type == WindowInfo::Type::Win32)
{
BOOL fullscreen = FALSE;
DXGI_SWAP_CHAIN_DESC desc;
if (SUCCEEDED(m_swap_chain->GetFullscreenState(&fullscreen, nullptr)) && fullscreen &&
SUCCEEDED(m_swap_chain->GetDesc(&desc)))
{
m_window_info.surface_refresh_rate = static_cast<float>(desc.BufferDesc.RefreshRate.Numerator) /
static_cast<float>(desc.BufferDesc.RefreshRate.Denominator);
}
else
{
m_window_info.surface_refresh_rate = 0.0f;
}
}
m_current_swap_chain_buffer = 0;
return true;
}
void GSDevice12::DestroySwapChainRTVs()
{
for (D3D12::Texture& buffer : m_swap_chain_buffers)
buffer.Destroy(false);
m_swap_chain_buffers.clear();
m_current_swap_chain_buffer = 0;
}
bool GSDevice12::ChangeWindow(const WindowInfo& new_wi)
{
DestroySurface();
m_window_info = new_wi;
if (new_wi.type == WindowInfo::Type::Surfaceless)
return true;
return CreateSwapChain(nullptr);
}
void GSDevice12::DestroySurface()
{
ExecuteCommandList(true);
if (IsExclusiveFullscreen())
SetExclusiveFullscreen(false, 0, 0, 0.0f);
DestroySwapChainRTVs();
m_swap_chain.reset();
}
std::string GSDevice12::GetDriverInfo() const
{
std::string ret = "Unknown Feature Level";
static constexpr std::array<std::tuple<D3D_FEATURE_LEVEL, const char*>, 4> feature_level_names = {{
{D3D_FEATURE_LEVEL_10_0, "D3D_FEATURE_LEVEL_10_0"},
{D3D_FEATURE_LEVEL_10_0, "D3D_FEATURE_LEVEL_10_1"},
{D3D_FEATURE_LEVEL_11_0, "D3D_FEATURE_LEVEL_11_0"},
{D3D_FEATURE_LEVEL_11_1, "D3D_FEATURE_LEVEL_11_1"},
}};
const D3D_FEATURE_LEVEL fl = g_d3d12_context->GetFeatureLevel();
for (size_t i = 0; i < std::size(feature_level_names); i++)
{
if (fl == std::get<0>(feature_level_names[i]))
{
ret = std::get<1>(feature_level_names[i]);
break;
}
}
ret += "\n";
IDXGIAdapter* adapter = g_d3d12_context->GetAdapter();
DXGI_ADAPTER_DESC desc;
if (adapter && SUCCEEDED(adapter->GetDesc(&desc)))
{
ret += StringUtil::StdStringFromFormat("VID: 0x%04X PID: 0x%04X\n", desc.VendorId, desc.DeviceId);
ret += StringUtil::WideStringToUTF8String(desc.Description);
ret += "\n";
const std::string driver_version(D3D::GetDriverVersionFromLUID(desc.AdapterLuid));
if (!driver_version.empty())
{
ret += "Driver Version: ";
ret += driver_version;
}
}
return ret;
}
void GSDevice12::ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale)
{
if (!m_swap_chain)
return;
m_window_info.surface_scale = new_window_scale;
if (m_window_info.surface_width == new_window_width && m_window_info.surface_height == new_window_height)
return;
EndRenderPass();
ExecuteCommandList(true);
DestroyResources();
GSDevice::Destroy();
DestroySwapChainRTVs();
HRESULT hr = m_swap_chain->ResizeBuffers(
0, 0, 0, DXGI_FORMAT_UNKNOWN, m_using_allow_tearing ? DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING : 0);
if (FAILED(hr))
Console.Error("ResizeBuffers() failed: 0x%08X", hr);
if (!CreateSwapChainRTV())
pxFailRel("Failed to recreate swap chain RTV after resize");
}
void GSDevice12::ResetAPIState()
bool GSDevice12::SupportsExclusiveFullscreen() const
{
return true;
}
bool GSDevice12::IsExclusiveFullscreen()
{
BOOL is_fullscreen = FALSE;
return (m_swap_chain && SUCCEEDED(m_swap_chain->GetFullscreenState(&is_fullscreen, nullptr)) && is_fullscreen);
}
bool GSDevice12::SetExclusiveFullscreen(bool fullscreen, u32 width, u32 height, float refresh_rate)
{
if (!m_swap_chain)
return false;
BOOL is_fullscreen = FALSE;
HRESULT hr = m_swap_chain->GetFullscreenState(&is_fullscreen, nullptr);
if (!fullscreen)
{
// leaving fullscreen
if (is_fullscreen)
return SUCCEEDED(m_swap_chain->SetFullscreenState(FALSE, nullptr));
else
return true;
}
IDXGIOutput* output;
if (FAILED(hr = m_swap_chain->GetContainingOutput(&output)))
return false;
DXGI_SWAP_CHAIN_DESC current_desc;
hr = m_swap_chain->GetDesc(&current_desc);
if (FAILED(hr))
return false;
DXGI_MODE_DESC new_mode = current_desc.BufferDesc;
new_mode.Width = width;
new_mode.Height = height;
new_mode.RefreshRate.Numerator = static_cast<UINT>(std::floor(refresh_rate * 1000.0f));
new_mode.RefreshRate.Denominator = 1000u;
DXGI_MODE_DESC closest_mode;
if (FAILED(hr = output->FindClosestMatchingMode(&new_mode, &closest_mode, nullptr)) ||
new_mode.Format != current_desc.BufferDesc.Format)
{
Console.Error("Failed to find closest matching mode, hr=%08X", hr);
return false;
}
if (new_mode.Width == current_desc.BufferDesc.Width && new_mode.Height == current_desc.BufferDesc.Width &&
new_mode.RefreshRate.Numerator == current_desc.BufferDesc.RefreshRate.Numerator &&
new_mode.RefreshRate.Denominator == current_desc.BufferDesc.RefreshRate.Denominator)
{
DevCon.WriteLn("Fullscreen mode already set");
return true;
}
g_d3d12_context->ExecuteCommandList(D3D12::Context::WaitType::Sleep);
DestroySwapChainRTVs();
m_swap_chain.reset();
if (!CreateSwapChain(&closest_mode))
{
Console.Error("Failed to create a fullscreen swap chain");
if (!CreateSwapChain(nullptr))
pxFailRel("Failed to recreate windowed swap chain");
return false;
}
return true;
}
GSDevice::PresentResult GSDevice12::BeginPresent(bool frame_skip)
{
EndRenderPass();
if (m_device_lost)
return PresentResult::DeviceLost;
if (frame_skip || !m_swap_chain)
return PresentResult::FrameSkipped;
static constexpr std::array<float, 4> clear_color = {};
D3D12::Texture& swap_chain_buf = m_swap_chain_buffers[m_current_swap_chain_buffer];
ID3D12GraphicsCommandList* cmdlist = g_d3d12_context->GetCommandList();
swap_chain_buf.TransitionToState(cmdlist, D3D12_RESOURCE_STATE_RENDER_TARGET);
cmdlist->ClearRenderTargetView(swap_chain_buf.GetWriteDescriptor(), clear_color.data(), 0, nullptr);
cmdlist->OMSetRenderTargets(1, &swap_chain_buf.GetWriteDescriptor().cpu_handle, FALSE, nullptr);
const D3D12_VIEWPORT vp{0.0f, 0.0f, static_cast<float>(m_window_info.surface_width),
static_cast<float>(m_window_info.surface_height), 0.0f, 1.0f};
const D3D12_RECT scissor{
0, 0, static_cast<LONG>(m_window_info.surface_width), static_cast<LONG>(m_window_info.surface_height)};
cmdlist->RSSetViewports(1, &vp);
cmdlist->RSSetScissorRects(1, &scissor);
return PresentResult::OK;
}
void GSDevice12::RestoreAPIState()
void GSDevice12::EndPresent()
{
RenderImGui();
D3D12::Texture& swap_chain_buf = m_swap_chain_buffers[m_current_swap_chain_buffer];
m_current_swap_chain_buffer = ((m_current_swap_chain_buffer + 1) % static_cast<u32>(m_swap_chain_buffers.size()));
swap_chain_buf.TransitionToState(g_d3d12_context->GetCommandList(), D3D12_RESOURCE_STATE_PRESENT);
if (!g_d3d12_context->ExecuteCommandList(D3D12::Context::WaitType::None))
{
m_device_lost = true;
InvalidateCachedState();
return;
}
const bool vsync = static_cast<UINT>(m_vsync_mode != VsyncMode::Off);
if (!vsync && m_using_allow_tearing)
m_swap_chain->Present(0, DXGI_PRESENT_ALLOW_TEARING);
else
m_swap_chain->Present(static_cast<UINT>(vsync), 0);
InvalidateCachedState();
}
bool GSDevice12::SetGPUTimingEnabled(bool enabled)
{
g_d3d12_context->SetEnableGPUTiming(enabled);
return true;
}
float GSDevice12::GetAndResetAccumulatedGPUTime()
{
return g_d3d12_context->GetAndResetAccumulatedGPUTime();
}
void GSDevice12::PushDebugGroup(const char* fmt, ...)
{
}
@@ -434,7 +834,7 @@ void GSDevice12::StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture*
int(dRect.right - dRect.left), int(dRect.bottom - dRect.top));
DoStretchRect(static_cast<GSTexture12*>(sTex), sRect, static_cast<GSTexture12*>(dTex), dRect,
dTex ? m_convert[static_cast<int>(shader)].get() : m_present[static_cast<int>(shader)].get(), linear);
dTex ? m_convert[static_cast<int>(shader)].get() : m_present[static_cast<int>(shader)].get(), linear, true);
}
void GSDevice12::StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, bool red,
@@ -443,8 +843,9 @@ void GSDevice12::StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture*
GL_PUSH("ColorCopy Red:%d Green:%d Blue:%d Alpha:%d", red, green, blue, alpha);
const u32 index = (red ? 1 : 0) | (green ? 2 : 0) | (blue ? 4 : 0) | (alpha ? 8 : 0);
DoStretchRect(
static_cast<GSTexture12*>(sTex), sRect, static_cast<GSTexture12*>(dTex), dRect, m_color_copy[index].get(), false);
const bool allow_discard = (index == 0xf);
DoStretchRect(static_cast<GSTexture12*>(sTex), sRect, static_cast<GSTexture12*>(dTex), dRect,
m_color_copy[index].get(), false, allow_discard);
}
void GSDevice12::PresentRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect,
@@ -452,13 +853,13 @@ void GSDevice12::PresentRect(GSTexture* sTex, const GSVector4& sRect, GSTexture*
{
DisplayConstantBuffer cb;
cb.SetSource(sRect, sTex->GetSize());
cb.SetTarget(dRect, dTex ? dTex->GetSize() : GSVector2i(g_host_display->GetWindowWidth(), g_host_display->GetWindowHeight()));
cb.SetTarget(dRect, dTex ? dTex->GetSize() : GSVector2i(GetWindowWidth(), GetWindowHeight()));
cb.SetTime(shaderTime);
SetUtilityRootSignature();
SetUtilityPushConstants(&cb, sizeof(cb));
DoStretchRect(static_cast<GSTexture12*>(sTex), sRect, static_cast<GSTexture12*>(dTex), dRect,
m_present[static_cast<int>(shader)].get(), linear);
m_present[static_cast<int>(shader)].get(), linear, true);
}
void GSDevice12::UpdateCLUTTexture(GSTexture* sTex, float sScale, u32 offsetX, u32 offsetY, GSTexture* dTex, u32 dOffset, u32 dSize)
@@ -478,7 +879,7 @@ void GSDevice12::UpdateCLUTTexture(GSTexture* sTex, float sScale, u32 offsetX, u
const GSVector4 dRect(0, 0, dSize, 1);
const ShaderConvert shader = (dSize == 16) ? ShaderConvert::CLUT_4 : ShaderConvert::CLUT_8;
DoStretchRect(static_cast<GSTexture12*>(sTex), GSVector4::zero(), static_cast<GSTexture12*>(dTex), dRect,
m_convert[static_cast<int>(shader)].get(), false);
m_convert[static_cast<int>(shader)].get(), false, true);
}
void GSDevice12::ConvertToIndexedTexture(GSTexture* sTex, float sScale, u32 offsetX, u32 offsetY, u32 SBW, u32 SPSM, GSTexture* dTex, u32 DBW, u32 DPSM)
@@ -498,7 +899,7 @@ void GSDevice12::ConvertToIndexedTexture(GSTexture* sTex, float sScale, u32 offs
const GSVector4 dRect(0, 0, dTex->GetWidth(), dTex->GetHeight());
const ShaderConvert shader = ShaderConvert::RGBA_TO_8I;
DoStretchRect(static_cast<GSTexture12*>(sTex), GSVector4::zero(), static_cast<GSTexture12*>(dTex), dRect,
m_convert[static_cast<int>(shader)].get(), false);
m_convert[static_cast<int>(shader)].get(), false, true);
}
void GSDevice12::DrawMultiStretchRects(
@@ -644,7 +1045,8 @@ void GSDevice12::BeginRenderPassForStretchRect(
}
}
void GSDevice12::DoStretchRect(GSTexture12* sTex, const GSVector4& sRect, GSTexture12* dTex, const GSVector4& dRect, const ID3D12PipelineState* pipeline, bool linear)
void GSDevice12::DoStretchRect(GSTexture12* sTex, const GSVector4& sRect, GSTexture12* dTex, const GSVector4& dRect,
const ID3D12PipelineState* pipeline, bool linear, bool allow_discard)
{
if (sTex->GetResourceState() != D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE)
{
@@ -659,8 +1061,7 @@ void GSDevice12::DoStretchRect(GSTexture12* sTex, const GSVector4& sRect, GSText
const bool is_present = (!dTex);
const bool depth = (dTex && dTex->GetType() == GSTexture::Type::DepthStencil);
const GSVector2i size(
is_present ? GSVector2i(g_host_display->GetWindowWidth(), g_host_display->GetWindowHeight()) : dTex->GetSize());
const GSVector2i size(is_present ? GSVector2i(GetWindowWidth(), GetWindowHeight()) : dTex->GetSize());
const GSVector4i dtex_rc(0, 0, size.x, size.y);
const GSVector4i dst_rc(GSVector4i(dRect).rintersect(dtex_rc));
@@ -677,7 +1078,7 @@ void GSDevice12::DoStretchRect(GSTexture12* sTex, const GSVector4& sRect, GSText
const bool drawing_to_current_rt = (is_present || InRenderPass());
if (!drawing_to_current_rt)
BeginRenderPassForStretchRect(dTex, dtex_rc, dst_rc);
BeginRenderPassForStretchRect(dTex, dtex_rc, dst_rc, allow_discard);
DrawStretchRect(sRect, dRect, size);
}
@@ -916,6 +1317,163 @@ bool GSDevice12::CompileCASPipelines()
return true;
}
bool GSDevice12::CompileImGuiPipeline()
{
const std::optional<std::string> hlsl = Host::ReadResourceFileToString("shaders/dx11/imgui.fx");
if (!hlsl.has_value())
{
Console.Error("Failed to read imgui.fx");
return false;
}
const ComPtr<ID3DBlob> vs = m_shader_cache.GetVertexShader(hlsl.value(), nullptr, "vs_main");
const ComPtr<ID3DBlob> ps = m_shader_cache.GetPixelShader(hlsl.value(), nullptr, "ps_main");
if (!vs || !ps)
{
Console.Error("Failed to compile ImGui shaders");
return false;
}
D3D12::GraphicsPipelineBuilder gpb;
gpb.SetRootSignature(m_utility_root_signature.get());
gpb.AddVertexAttribute("POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, offsetof(ImDrawVert, pos));
gpb.AddVertexAttribute("TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, offsetof(ImDrawVert, uv));
gpb.AddVertexAttribute("COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, offsetof(ImDrawVert, col));
gpb.SetPrimitiveTopologyType(D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE);
gpb.SetVertexShader(vs.get());
gpb.SetPixelShader(ps.get());
gpb.SetNoCullRasterizationState();
gpb.SetNoDepthTestState();
gpb.SetBlendState(0, true, D3D12_BLEND_SRC_ALPHA, D3D12_BLEND_INV_SRC_ALPHA, D3D12_BLEND_OP_ADD, D3D12_BLEND_ONE,
D3D12_BLEND_ZERO, D3D12_BLEND_OP_ADD);
gpb.SetRenderTarget(0, DXGI_FORMAT_R8G8B8A8_UNORM);
m_imgui_pipeline = gpb.Create(g_d3d12_context->GetDevice(), m_shader_cache, false);
if (!m_imgui_pipeline)
{
Console.Error("Failed to compile ImGui pipeline");
return false;
}
D3D12::SetObjectName(m_imgui_pipeline.get(), "ImGui pipeline");
return true;
}
void GSDevice12::RenderImGui()
{
ImGui::Render();
const ImDrawData* draw_data = ImGui::GetDrawData();
if (draw_data->CmdListsCount == 0)
return;
const float L = 0.0f;
const float R = static_cast<float>(m_window_info.surface_width);
const float T = 0.0f;
const float B = static_cast<float>(m_window_info.surface_height);
// clang-format off
const float ortho_projection[4][4] =
{
{ 2.0f/(R-L), 0.0f, 0.0f, 0.0f },
{ 0.0f, 2.0f/(T-B), 0.0f, 0.0f },
{ 0.0f, 0.0f, 0.5f, 0.0f },
{ (R+L)/(L-R), (T+B)/(B-T), 0.5f, 1.0f },
};
// clang-format on
SetUtilityRootSignature();
SetUtilityPushConstants(ortho_projection, sizeof(ortho_projection));
SetPipeline(m_imgui_pipeline.get());
SetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
if (m_utility_sampler_cpu != m_linear_sampler_cpu)
{
m_utility_sampler_cpu = m_linear_sampler_cpu;
m_dirty_flags |= DIRTY_FLAG_SAMPLERS_DESCRIPTOR_TABLE;
// just skip if we run out.. we can't resume the present render pass :/
if (!g_d3d12_context->GetSamplerAllocator().LookupSingle(&m_utility_sampler_gpu, m_linear_sampler_cpu))
{
Console.Warning("Skipping ImGui draw because of no descriptors");
return;
}
}
// this is for presenting, we don't want to screw with the viewport/scissor set by display
m_dirty_flags &= ~(DIRTY_FLAG_RENDER_TARGET | DIRTY_FLAG_VIEWPORT | DIRTY_FLAG_SCISSOR);
for (int n = 0; n < draw_data->CmdListsCount; n++)
{
const ImDrawList* cmd_list = draw_data->CmdLists[n];
u32 vertex_offset;
{
const u32 size = sizeof(ImDrawVert) * static_cast<u32>(cmd_list->VtxBuffer.Size);
if (!m_vertex_stream_buffer.ReserveMemory(size, sizeof(ImDrawVert)))
{
Console.Warning("Skipping ImGui draw because of no vertex buffer space");
return;
}
vertex_offset = m_vertex_stream_buffer.GetCurrentOffset() / sizeof(ImDrawVert);
std::memcpy(m_vertex_stream_buffer.GetCurrentHostPointer(), cmd_list->VtxBuffer.Data, size);
m_vertex_stream_buffer.CommitMemory(size);
}
u32 index_offset;
{
const u32 size = sizeof(ImDrawIdx) * static_cast<u32>(cmd_list->IdxBuffer.Size);
if (!m_index_stream_buffer.ReserveMemory(size, sizeof(ImDrawIdx)))
{
Console.Warning("Skipping ImGui draw because of no vertex buffer space");
return;
}
index_offset = m_index_stream_buffer.GetCurrentOffset() / sizeof(ImDrawIdx);
std::memcpy(m_index_stream_buffer.GetCurrentHostPointer(), cmd_list->IdxBuffer.Data, size);
m_index_stream_buffer.CommitMemory(size);
}
SetVertexBuffer(m_vertex_stream_buffer.GetGPUPointer(), m_vertex_stream_buffer.GetSize(), sizeof(ImDrawVert));
SetIndexBuffer(m_index_stream_buffer.GetGPUPointer(), m_index_stream_buffer.GetSize(), DXGI_FORMAT_R16_UINT);
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
{
const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
pxAssert(!pcmd->UserCallback);
const GSVector4 clip = GSVector4::load<false>(&pcmd->ClipRect);
if ((clip.zwzw() <= clip.xyxy()).mask() != 0)
continue;
SetScissor(GSVector4i(clip));
// Since we don't have the GSTexture...
D3D12::Texture* tex = static_cast<D3D12::Texture*>(pcmd->GetTexID());
D3D12::DescriptorHandle handle = tex ? tex->GetSRVDescriptor() : m_null_texture.GetSRVDescriptor();
if (m_utility_texture_cpu != handle)
{
m_utility_texture_cpu = handle;
m_dirty_flags |= DIRTY_FLAG_TEXTURES_DESCRIPTOR_TABLE;
if (!GetTextureGroupDescriptors(&m_utility_texture_gpu, &handle, 1))
{
Console.Warning("Skipping ImGui draw because of no descriptors");
return;
}
}
if (ApplyUtilityState())
{
g_d3d12_context->GetCommandList()->DrawIndexedInstanced(
pcmd->ElemCount, 1, index_offset + pcmd->IdxOffset, vertex_offset + pcmd->VtxOffset, 0);
}
}
g_perfmon.Put(GSPerfMon::DrawCalls, cmd_list->CmdBuffer.Size);
}
}
bool GSDevice12::DoCAS(GSTexture* sTex, GSTexture* dTex, bool sharpen_only, const std::array<u32, NUM_CAS_CONSTANTS>& constants)
{
EndRenderPass();
@@ -1180,7 +1738,7 @@ bool GSDevice12::CreateRootSignatures()
// Convert Pipeline Layout
//////////////////////////////////////////////////////////////////////////
rsb.SetInputAssemblerFlag();
rsb.Add32BitConstants(0, CONVERT_PUSH_CONSTANTS_SIZE / sizeof(u32), static_cast<D3D12_SHADER_VISIBILITY>(D3D12_SHADER_VISIBILITY_VERTEX | D3D12_SHADER_VISIBILITY_PIXEL));
rsb.Add32BitConstants(0, CONVERT_PUSH_CONSTANTS_SIZE / sizeof(u32), D3D12_SHADER_VISIBILITY_ALL);
rsb.AddDescriptorTable(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 0, NUM_UTILITY_SAMPLERS, D3D12_SHADER_VISIBILITY_PIXEL);
rsb.AddDescriptorTable(D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER, 0, NUM_UTILITY_SAMPLERS, D3D12_SHADER_VISIBILITY_PIXEL);
if (!(m_utility_root_signature = rsb.Create()))
@@ -1526,6 +2084,12 @@ void GSDevice12::DestroyResources()
{
g_d3d12_context->ExecuteCommandList(D3D12::Context::WaitType::Sleep);
m_convert_vs.reset();
m_cas_sharpen_pipeline.reset();
m_cas_upscale_pipeline.reset();
m_cas_root_signature.reset();
for (auto& it : m_tfx_pipelines)
g_d3d12_context->DeferObjectDestruction(it.second.get());
m_tfx_pipelines.clear();
@@ -1542,6 +2106,7 @@ void GSDevice12::DestroyResources()
m_date_image_setup_pipelines = {};
m_fxaa_pipeline.reset();
m_shadeboost_pipeline.reset();
m_imgui_pipeline.reset();
m_linear_sampler_cpu.Clear();
m_point_sampler_cpu.Clear();
@@ -1561,6 +2126,8 @@ void GSDevice12::DestroyResources()
m_tfx_root_signature.reset();
m_null_texture.Destroy(false);
m_shader_cache.Close();
}
const ID3DBlob* GSDevice12::GetTFXVertexShader(GSHWDrawConfig::VSSelector sel)
+39 -4
View File
@@ -138,6 +138,15 @@ public:
};
private:
ComPtr<IDXGIFactory5> m_dxgi_factory;
ComPtr<IDXGISwapChain1> m_swap_chain;
std::vector<D3D12::Texture> m_swap_chain_buffers;
u32 m_current_swap_chain_buffer = 0;
bool m_allow_tearing_supported = false;
bool m_using_allow_tearing = false;
bool m_device_lost = false;
ComPtr<ID3D12RootSignature> m_tfx_root_signature;
ComPtr<ID3D12RootSignature> m_utility_root_signature;
@@ -161,6 +170,7 @@ private:
std::array<std::array<ComPtr<ID3D12PipelineState>, 2>, 2> m_date_image_setup_pipelines{}; // [depth][datm]
ComPtr<ID3D12PipelineState> m_fxaa_pipeline;
ComPtr<ID3D12PipelineState> m_shadeboost_pipeline;
ComPtr<ID3D12PipelineState> m_imgui_pipeline;
std::unordered_map<u32, ComPtr<ID3DBlob>> m_tfx_vertex_shaders;
std::unordered_map<u32, ComPtr<ID3DBlob>> m_tfx_geometry_shaders;
@@ -180,6 +190,10 @@ private:
void LookupNativeFormat(GSTexture::Format format, DXGI_FORMAT* d3d_format, DXGI_FORMAT* srv_format, DXGI_FORMAT* rtv_format, DXGI_FORMAT* dsv_format) const;
bool CreateSwapChain(const DXGI_MODE_DESC* fullscreen_mode);
bool CreateSwapChainRTV();
void DestroySwapChainRTVs();
GSTexture* CreateSurface(GSTexture::Type type, int width, int height, int levels, GSTexture::Format format) override;
void DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex, GSVector4* dRect, const GSRegPMODE& PMODE,
@@ -215,6 +229,9 @@ private:
bool CompilePostProcessingPipelines();
bool CompileCASPipelines();
bool CompileImGuiPipeline();
void RenderImGui();
void DestroyResources();
public:
@@ -223,11 +240,29 @@ public:
__fi static GSDevice12* GetInstance() { return static_cast<GSDevice12*>(g_gs_device.get()); }
bool Create() override;
RenderAPI GetRenderAPI() const override;
bool HasSurface() const override;
bool Create(const WindowInfo& wi, VsyncMode vsync) override;
void Destroy() override;
void ResetAPIState() override;
void RestoreAPIState() override;
bool ChangeWindow(const WindowInfo& new_wi) override;
void ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale) override;
bool SupportsExclusiveFullscreen() const override;
bool IsExclusiveFullscreen() override;
bool SetExclusiveFullscreen(bool fullscreen, u32 width, u32 height, float refresh_rate) override;
void DestroySurface() override;
std::string GetDriverInfo() const override;
bool GetHostRefreshRate(float* refresh_rate) override;
void SetVSync(VsyncMode mode) override;
PresentResult BeginPresent(bool frame_skip) override;
void EndPresent() override;
bool SetGPUTimingEnabled(bool enabled) override;
float GetAndResetAccumulatedGPUTime() override;
void PushDebugGroup(const char* fmt, ...) override;
void PopDebugGroup() override;
@@ -262,7 +297,7 @@ public:
void BeginRenderPassForStretchRect(
GSTexture12* dTex, const GSVector4i& dtex_rc, const GSVector4i& dst_rc, bool allow_discard = true);
void DoStretchRect(GSTexture12* sTex, const GSVector4& sRect, GSTexture12* dTex, const GSVector4& dRect,
const ID3D12PipelineState* pipeline, bool linear);
const ID3D12PipelineState* pipeline, bool linear, bool allow_discard);
void DrawStretchRect(const GSVector4& sRect, const GSVector4& dRect, const GSVector2i& ds);
void SetupDATE(GSTexture* rt, GSTexture* ds, bool datm, const GSVector4i& bbox);
+114 -12
View File
@@ -95,6 +95,10 @@ void GSRendererHW::Reset(bool hardware_reset)
// Force targets to preload for 2 frames (for 30fps games).
static constexpr u8 TARGET_PRELOAD_FRAMES = 2;
// Read back on CSR Reset, conditional downloading on render swap etc handled elsewhere.
if (!hardware_reset)
g_texture_cache->ReadbackAll();
g_texture_cache->RemoveAll();
m_force_preload = TARGET_PRELOAD_FRAMES;
@@ -971,7 +975,7 @@ void GSRendererHW::InvalidateVideoMem(const GIFRegBITBLTBUF& BITBLTBUF, const GS
if (loop_w)
{
rect.x = 0;
rect.z = r.w - 2048;
rect.z = r.z - 2048;
}
g_texture_cache->InvalidateVideoMem(m_mem.GetOffset(BITBLTBUF.DBP, BITBLTBUF.DBW, BITBLTBUF.DPSM), rect, eewrite);
}
@@ -2235,8 +2239,10 @@ void GSRendererHW::Draw()
if (!GSConfig.UserHacks_DisableSafeFeatures)
{
if (IsConstantDirectWriteMemClear(false) && IsBlendedOrOpaque())
if (IsConstantDirectWriteMemClear(true) && IsBlendedOrOpaque())
{
OI_DoubleHalfClear(rt, ds);
}
}
// A couple of hack to avoid upscaling issue. So far it seems to impacts mostly sprite
@@ -2305,7 +2311,8 @@ void GSRendererHW::Draw()
if (old_ds)
g_texture_cache->InvalidateVideoMemType(GSTextureCache::DepthStencil, old_ds->m_TEX0.TBP0);
//
// Make sure they aren't the same source (double half clear), we don't want to invalidate the texture.
const bool can_invalidate = (m_cached_ctx.FRAME.Block() != m_cached_ctx.ZBUF.Block()) || !rt || !ds;
if ((fm & fm_mask) != fm_mask && rt)
{
@@ -2317,7 +2324,8 @@ void GSRendererHW::Draw()
g_texture_cache->InvalidateVideoMem(context->offset.fb, m_r, false, false);
g_texture_cache->InvalidateVideoMemType(GSTextureCache::DepthStencil, m_cached_ctx.FRAME.Block());
if (can_invalidate)
g_texture_cache->InvalidateVideoMemType(GSTextureCache::DepthStencil, m_cached_ctx.FRAME.Block());
}
if (zm != 0xffffffff && ds)
@@ -2330,7 +2338,8 @@ void GSRendererHW::Draw()
g_texture_cache->InvalidateVideoMem(context->offset.zb, m_r, false, false);
g_texture_cache->InvalidateVideoMemType(GSTextureCache::RenderTarget, m_cached_ctx.ZBUF.Block());
if (can_invalidate)
g_texture_cache->InvalidateVideoMemType(GSTextureCache::RenderTarget, m_cached_ctx.ZBUF.Block());
}
//
@@ -4925,6 +4934,12 @@ void GSRendererHW::OI_DoubleHalfClear(GSTextureCache::Target*& rt, GSTextureCach
{
// Note gs mem clear must be tested before calling this function
// If the mask is on any of the colour or partial alpha, keep the whole channel
const bool keep_a = (m_cached_ctx.FRAME.FBMSK >> 24) & 0xFF;
const bool keep_b = (m_cached_ctx.FRAME.FBMSK >> 16) & 0xFF;
const bool keep_g = (m_cached_ctx.FRAME.FBMSK >> 8) & 0xFF;
const bool keep_r = m_cached_ctx.FRAME.FBMSK & 0xFF;
// Limit further to unmask Z write
if (!m_cached_ctx.ZBUF.ZMSK && rt && ds)
{
@@ -4971,16 +4986,64 @@ void GSRendererHW::OI_DoubleHalfClear(GSTextureCache::Target*& rt, GSTextureCach
GL_INS("OI_DoubleHalfClear:%s: base %x half %x. w_pages %d h_pages %d fbw %d. Color %x",
clear_depth ? "depth" : "target", base << 5, half << 5, w_pages, h_pages, m_cached_ctx.FRAME.FBW, color);
if (clear_depth)
// If some of the channels are masked, we need to keep them.
if (m_cached_ctx.FRAME.FBMSK != 0)
{
// Only pure clear are supported for depth
ASSERT(color == 0);
g_gs_device->ClearDepth(ds->m_texture);
GSTexture* tex = nullptr;
GSTextureCache::Target* target = clear_depth ? ds : rt;
const GSVector2 size = GSVector2(static_cast<float>(target->GetUnscaledWidth()) * target->m_scale, static_cast<float>(target->GetUnscaledHeight()) * target->m_scale);
try
{
tex = target->m_texture->IsDepthStencil() ?
g_gs_device->CreateDepthStencil(size.x, size.y, target->m_texture->GetFormat(), true) :
g_gs_device->CreateRenderTarget(size.x, size.y, target->m_texture->GetFormat(), true);
}
catch (const std::bad_alloc&)
{
}
if (!tex)
{
Console.Error("(ResizeTexture) Failed to allocate %dx%d texture", size.x, size.y);
return;
}
if (clear_depth)
{
// Only pure clear are supported for depth
ASSERT(color == 0);
g_gs_device->ClearDepth(tex);
}
else
{
g_gs_device->ClearRenderTarget(tex, color);
}
const GSVector4 drect = GSVector4(target->GetUnscaledRect()) * GSVector4(target->m_scale);
// Copy channels being masked.
g_gs_device->StretchRect(target->m_texture, GSVector4(0.0f,0.0f,1.0f,1.0f), tex, drect, keep_r, keep_g, keep_b, keep_a);
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
delete target->m_texture;
target->m_texture = tex;
target->UpdateValidity(target->GetUnscaledRect());
}
else
{
g_gs_device->ClearRenderTarget(rt->m_texture, color);
if (clear_depth)
{
// Only pure clear are supported for depth
ASSERT(color == 0);
g_gs_device->ClearDepth(ds->m_texture);
ds->UpdateValidity(ds->GetUnscaledRect());
}
else
{
g_gs_device->ClearRenderTarget(rt->m_texture, color);
rt->UpdateValidity(rt->GetUnscaledRect());
}
}
}
}
// Striped double clear done by Powerdrome and Snoopy Vs Red Baron, it will clear in 32 pixel stripes half done by the Z and half done by the FRAME
@@ -4995,7 +5058,44 @@ void GSRendererHW::OI_DoubleHalfClear(GSTextureCache::Target*& rt, GSTextureCach
// If both buffers are side by side we can expect a fast clear in on-going
const u32 color = v[1].RGBAQ.U32[0];
g_gs_device->ClearRenderTarget(rt->m_texture, color);
// If some of the channels are masked, we need to keep them.
if (m_cached_ctx.FRAME.FBMSK != 0)
{
GSTexture* tex = nullptr;
GSTextureCache::Target* target = rt;
const GSVector2 size = GSVector2(static_cast<float>(target->GetUnscaledWidth()) * target->m_scale, static_cast<float>(target->GetUnscaledHeight()) * target->m_scale);
try
{
tex = g_gs_device->CreateRenderTarget(size.x, size.y, target->m_texture->GetFormat(), true);
}
catch (const std::bad_alloc&)
{
}
if (!tex)
{
Console.Error("(ResizeTexture) Failed to allocate %dx%d texture", size.x, size.y);
return;
}
g_gs_device->ClearRenderTarget(tex, color);
const GSVector4 drect = GSVector4(target->GetUnscaledRect()) * GSVector4(target->m_scale);
// Copy channels being masked.
g_gs_device->StretchRect(target->m_texture, GSVector4(0, 0, 1, 1), tex, drect, keep_r, keep_g, keep_b, keep_a);
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
delete target->m_texture;
target->m_texture = tex;
target->UpdateValidity(target->GetUnscaledRect());
}
else
{
g_gs_device->ClearRenderTarget(rt->m_texture, color);
rt->UpdateValidity(rt->GetUnscaledRect());
}
}
}
@@ -5120,10 +5220,13 @@ bool GSRendererHW::OI_BlitFMV(GSTextureCache::Target* _rt, GSTextureCache::Sourc
const GSVector4i r_full(0, 0, tw, th);
g_gs_device->CopyRect(tex->m_texture, rt, r_full, 0, 0);
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
g_gs_device->StretchRect(tex->m_texture, sRect, rt, dRect);
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
g_gs_device->CopyRect(rt, tex->m_texture, r_full, 0, 0);
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
g_gs_device->Recycle(rt);
}
@@ -5150,7 +5253,6 @@ bool GSRendererHW::IsConstantDirectWriteMemClear(bool include_zero)
const bool direct_draw = (m_vt.m_primclass == GS_SPRITE_CLASS) || (m_index.tail == 6 && m_vt.m_primclass == GS_TRIANGLE_CLASS);
// Constant Direct Write without texture/test/blending (aka a GS mem clear)
if (direct_draw && !PRIM->TME // Direct write
&& (m_cached_ctx.FRAME.FBMSK == 0 || (include_zero && m_vt.m_max.c.eq(GSVector4i::zero()))) // no color mask
&& !(m_draw_env->SCANMSK.MSK & 2)
&& !m_cached_ctx.TEST.ATE // no alpha test
&& (!m_cached_ctx.TEST.ZTE || m_cached_ctx.TEST.ZTST == ZTST_ALWAYS) // no depth test
+86 -32
View File
@@ -224,7 +224,7 @@ GSVector4i GSTextureCache::TranslateAlignedRectByPage(Target* t, u32 sbp, u32 sp
{
const u32 totalpages = rect_pages.width() * rect_pages.height();
const bool full_rows = in_rect.width() == (src_pgw * src_page_size.x);
const bool full_rows = in_rect.width() == static_cast<int>(src_pgw * src_page_size.x);
const bool single_row = in_rect.x == 0 && in_rect.y == 0 && totalpages <= dst_pgw;
const bool uneven_pages = (horizontal_offset || (totalpages % dst_pgw) != 0) && !single_row;
@@ -375,7 +375,7 @@ void GSTextureCache::DirtyRectByPage(u32 sbp, u32 spsm, u32 sbw, Target* t, GSVe
else if (static_cast<int>(src_pgw) == rect_pages.width())
{
const u32 totalpages = rect_pages.width() * rect_pages.height();
const bool full_rows = in_rect.width() == (src_pgw * src_page_size.x);
const bool full_rows = in_rect.width() == static_cast<int>(src_pgw * src_page_size.x);
const bool single_row = in_rect.x == 0 && in_rect.y == 0 && totalpages <= dst_pgw;
const bool uneven_pages = (horizontal_offset || (totalpages % dst_pgw) != 0) && !single_row;
@@ -711,7 +711,12 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, con
// Solution: consider the RT as 32 bits if the alpha was used in the past
const u32 t_psm = (t->m_dirty_alpha) ? t->m_TEX0.PSM & ~0x1 : t->m_TEX0.PSM;
bool rect_clean = GSUtil::HasSameSwizzleBits(psm, t_psm);
if (rect_clean && bp >= t->m_TEX0.TBP0 && bp < t->m_end_block && bw == t->m_TEX0.TBW && bp <= t->m_end_block && !t->m_dirty.empty())
const u32 end_block = (t->m_TEX0.TBP0 > t->m_end_block) ? (t->m_end_block + MAX_BP + 1) : t->m_end_block;
const GSLocalMemory::psm_t& psm_src = GSLocalMemory::m_psm[psm];
const GSLocalMemory::psm_t& psm_dst = GSLocalMemory::m_psm[t->m_TEX0.PSM];
const bool width_match = (std::max(64U, bw * 64U) / psm_src.pgs.x) == (std::max(64U, t->m_TEX0.TBW * 64U) / psm_dst.pgs.x);
if (rect_clean && bp >= t->m_TEX0.TBP0 && bp < end_block && width_match && !t->m_dirty.empty())
{
GSVector4i new_rect = r;
bool partial = false;
@@ -730,12 +735,6 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, con
if (swizzle_match)
{
new_rect = TranslateAlignedRectByPage(t, bp, psm, bw, r);
if (new_rect.eq(GSVector4i::zero()))
{
rect_clean = false;
break;
}
}
else
{
@@ -755,6 +754,8 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, con
}
new_rect = TranslateAlignedRectByPage(t, bp & ~((1 << 5) - 1), psm, bw, new_rect);
}
rect_clean = !new_rect.eq(GSVector4i::zero());
}
else
{
@@ -776,14 +777,17 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, con
}
}
for (auto& dirty : t->m_dirty)
if (rect_clean)
{
GSVector4i dirty_rect = dirty.GetDirtyRect(t->m_TEX0);
if (!dirty_rect.rintersect(new_rect).rempty())
for (auto& dirty : t->m_dirty)
{
rect_clean = false;
partial |= !new_rect.rintersect(dirty_rect).eq(new_rect);
break;
GSVector4i dirty_rect = dirty.GetDirtyRect(t->m_TEX0);
if (!dirty_rect.rintersect(new_rect).rempty())
{
rect_clean = false;
partial |= !new_rect.rintersect(dirty_rect).eq(new_rect);
break;
}
}
}
@@ -819,7 +823,7 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, con
rect_clean = t->m_dirty.empty();
const bool t_clean = ((t->m_dirty.GetDirtyChannels() & GSUtil::GetChannelMask(psm)) == 0) || rect_clean;
const bool t_wraps = t->m_end_block > GSTextureCache::MAX_BP;
const bool t_wraps = end_block > GSTextureCache::MAX_BP;
// Match if we haven't already got a tex in rt
if (t_clean && GSUtil::HasSharedBits(bp, psm, t->m_TEX0.TBP0, t_psm))
{
@@ -1207,6 +1211,7 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
GSTexture* tex = type == RenderTarget ? g_gs_device->CreateRenderTarget(new_scaled_size.x, new_scaled_size.y, GSTexture::Format::Color, clear) :
g_gs_device->CreateDepthStencil(new_scaled_size.x, new_scaled_size.y, GSTexture::Format::DepthStencil, clear);
g_gs_device->StretchRect(dst->m_texture, sRect, tex, dRect, (type == RenderTarget) ? ShaderConvert::COPY : ShaderConvert::DEPTH_COPY, false);
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
m_target_memory_usage = (m_target_memory_usage - dst->m_texture->GetMemUsage()) + tex->GetMemUsage();
g_gs_device->Recycle(dst->m_texture);
dst->m_texture = tex;
@@ -1243,11 +1248,11 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
if (dst_match)
{
dst_match->Update(true);
calcRescale(dst_match);
dst = CreateTarget(TEX0, new_size.x, new_size.y, scale, type, clear);
dst->m_32_bits_fmt = dst_match->m_32_bits_fmt;
dst->OffsetHack_modxy = dst_match->OffsetHack_modxy;
ShaderConvert shader;
// m_32_bits_fmt gets set on a shuffle or if the format isn't 16bit.
// In this case it needs to make sure it isn't part of a shuffle, where it needs to be interpreted as 32bits.
@@ -1262,7 +1267,22 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
GL_CACHE("TC: Lookup Target(Color) %dx%d, hit Depth (0x%x, TBW %d, %s was %s)", new_size.x, new_size.y, bp, TEX0.TBW, psm_str(TEX0.PSM), psm_str(dst_match->m_TEX0.PSM));
shader = (fmt_16_bits) ? ShaderConvert::FLOAT16_TO_RGB5A1 : ShaderConvert::FLOAT32_TO_RGBA8;
}
g_gs_device->StretchRect(dst_match->m_texture, sRect, dst->m_texture, dRect, shader, false);
// The old target's going to get invalidated (at least until we handle concurrent frame+depth at the same BP),
// so just move the dirty rects across.
dst->m_dirty = std::move(dst_match->m_dirty);
dst_match->m_dirty = {};
// Don't bother copying the old target in if the whole thing is dirty.
if (dst->m_dirty.empty() || (~dst->m_dirty.GetDirtyChannels() & GSUtil::GetChannelMask(TEX0.PSM)) != 0 ||
!dst->m_dirty.GetDirtyRect(0, TEX0, dst->GetUnscaledRect()).eq(dst->GetUnscaledRect()))
{
g_gs_device->StretchRect(dst_match->m_texture, sRect, dst->m_texture, dRect, shader, false);
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
}
// Now pull in any dirty areas in the new format.
dst->Update(true);
}
}
@@ -1454,6 +1474,7 @@ void GSTextureCache::ScaleTargetForDisplay(Target* t, const GIFRegTEX0& dispfb,
// Fill the new texture with the old data, and discard the old texture.
g_gs_device->StretchRect(old_texture, new_texture, GSVector4(old_texture->GetSize()).zwxy(), ShaderConvert::COPY, false);
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
m_target_memory_usage = (m_target_memory_usage - old_texture->GetMemUsage()) + new_texture->GetMemUsage();
g_gs_device->Recycle(old_texture);
t->m_texture = new_texture;
@@ -1928,11 +1949,11 @@ void GSTextureCache::InvalidateVideoMem(const GSOffset& off, const GSVector4i& r
const GSVector2i page_size = GSLocalMemory::m_psm[psm].pgs;
const bool can_translate = CanTranslate(bp, bw, psm, r, t->m_TEX0.TBP0, t->m_TEX0.PSM, t->m_TEX0.TBW);
const u32 end_block = (t->m_TEX0.TBP0 > t->m_end_block) ? (t->m_end_block + MAX_BP + 1) : t->m_end_block;
if (GSLocalMemory::m_psm[t->m_TEX0.PSM].bpp >= 16 && GSLocalMemory::m_psm[psm].bpp <= 8)
{
// could be overwriting a double buffer, so if it's the second half of it, just reduce the size down to half.
if (((t->m_end_block - t->m_TEX0.TBP0) >> 1) < (bp - t->m_TEX0.TBP0))
if (((end_block - t->m_TEX0.TBP0) >> 1) < (bp - t->m_TEX0.TBP0))
{
GSVector4i new_valid = t->m_valid;
new_valid.w = new_valid.w / 2;
@@ -1941,17 +1962,31 @@ void GSTextureCache::InvalidateVideoMem(const GSOffset& off, const GSVector4i& r
else
{
i = list.erase(j);
GL_CACHE("TC: Tex in RT Remove Target(%s) (0x%x) TPSM %x PSM %x bp 0x%x", to_string(type),
GL_CACHE("TC: Tex in RT Remove Target(%s) (0x%x) TPSM %x PSM %x bp 0x%x x %d y %d z %d w %d", to_string(type),
t->m_TEX0.TBP0,
t->m_TEX0.PSM,
psm,
bp);
bp,
r.x,
r.y,
r.z,
r.w);
delete t;
continue;
}
}
else if (can_translate)
{
GL_CACHE("TC: Tex in RT Dirty Target by page(%s) (0x%x->0x%x) TPSM %x PSM %x bp 0x%x r x %d y %d z %d w %d", to_string(type),
t->m_TEX0.TBP0,
t->m_end_block,
t->m_TEX0.PSM,
psm,
bp,
r.x,
r.y,
r.z,
r.w);
const bool swizzle_match = GSLocalMemory::m_psm[psm].depth == GSLocalMemory::m_psm[t->m_TEX0.PSM].depth;
if (swizzle_match)
{
@@ -1977,6 +2012,16 @@ void GSTextureCache::InvalidateVideoMem(const GSOffset& off, const GSVector4i& r
}
else
{
GL_CACHE("TC: Tex in RT Dirty Target by offset(%s) (0x%x->0x%x) TPSM %x PSM %x bp 0x%x r x %d y %d z %d w %d", to_string(type),
t->m_TEX0.TBP0,
t->m_end_block,
t->m_TEX0.PSM,
psm,
bp,
r.x,
r.y,
r.z,
r.w);
SurfaceOffsetKey sok;
sok.elems[0].bp = bp;
sok.elems[0].bw = bw;
@@ -3010,6 +3055,11 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con
int destX = 0;
int destY = 0;
// Create a cleared RT if we somehow end up with an empty source rect (because the RT isn't large enough).
const bool source_rect_empty = sRect.rempty();
const bool use_texture = (shader == ShaderConvert::COPY && !source_rect_empty);
GSVector4i region_rect = GSVector4i(0, 0, tw, th);
if (half_right)
{
// You typically hit this code in snow engine game. Dstsize is the size of of Dx/GL RT
@@ -3019,8 +3069,10 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con
if (half_width < dst->m_unscaled_size.x)
{
const int copy_width = std::min(half_width, dst->m_unscaled_size.x - half_width);
sRect.x = static_cast<int>(static_cast<float>(half_width) * dst->m_scale);
sRect.z = std::min(static_cast<int>(static_cast<float>(half_width + copy_width) * dst->m_scale), dst->m_texture->GetWidth());
region_rect = GSVector4i(half_width, 0, half_width + copy_width, th);
GL_CACHE("TC: Half right fix: %d,%d => %d,%d", region_rect.x, region_rect.y, region_rect.z, region_rect.w);
sRect = sRect.blend32<5>(GSVector4i(GSVector4(region_rect.rintersect(dst->GetUnscaledRect())) * GSVector4(dst->m_scale)));
new_size.x = sRect.width();
src->m_unscaled_size.x = copy_width;
}
@@ -3030,15 +3082,11 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con
}
}
// Create a cleared RT if we somehow end up with an empty source rect (because the RT isn't large enough).
const bool source_rect_empty = sRect.rempty();
const bool use_texture = (shader == ShaderConvert::COPY && !source_rect_empty);
const GSVector2i dst_texture_size = dst->m_texture->GetSize();
// Assuming everything matches up, instead of copying the target, we can just sample it directly.
// It's the same as doing the copy first, except we save GPU time.
// TODO: We still need to copy if the TBW is mismatched. Except when TBW <= 1 (Jak 2).
if (!half_right && // not the size change from above
const GSVector2i dst_texture_size = dst->m_texture->GetSize();
if ((!half_right || region_rect.z >= dst->m_unscaled_size.x) && // not a smaller subsample
use_texture && // not reinterpreting the RT
!force_target_copy)
{
@@ -3056,9 +3104,9 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con
dst_texture_size.y, new_size.x, new_size.y);
if (new_size.x != dst_texture_size.x)
src->m_region.SetX(0, tw);
src->m_region.SetX(region_rect.x, region_rect.z);
if (new_size.y != dst_texture_size.y)
src->m_region.SetY(0, th);
src->m_region.SetY(region_rect.y, region_rect.w);
}
// kill source immediately if it's the RT/DS, because that'll get invalidated immediately
@@ -3082,6 +3130,7 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con
if (use_texture)
{
g_gs_device->CopyRect(sTex, dTex, sRect, destX, destY);
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
}
else if (!source_rect_empty)
{
@@ -3097,6 +3146,8 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con
g_gs_device->StretchRect(
sTex, sRectF, dTex, GSVector4(destX, destY, new_size.x, new_size.y), shader, false);
}
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
}
}
@@ -3766,6 +3817,7 @@ void GSTextureCache::Read(Target* t, const GSVector4i& r)
if (tmp)
{
g_gs_device->StretchRect(t->m_texture, src, tmp, GSVector4(drc), ps_shader, false);
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
dltex->get()->CopyFromTexture(drc, tmp, drc, 0, true);
g_gs_device->Recycle(tmp);
}
@@ -4434,6 +4486,8 @@ bool GSTextureCache::Target::ResizeTexture(int new_unscaled_width, int new_unsca
// Fast memcpy()-like path for color targets.
g_gs_device->CopyRect(m_texture, tex, rc, 0, 0);
}
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
}
else if (m_texture->GetState() == GSTexture::State::Cleared)
{
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
<Type Name="GSTextureCache::Target">
<DisplayString Condition="m_type == 0">{{ RT @ BP={m_TEX0.TBP0,X}-{m_end_block,X} BW={m_TEX0.TBW} PSM={m_TEX0.PSM,X} {m_unscaled_size.x}x{m_unscaled_size.y} {m_valid.z},{m_valid.w} }}</DisplayString>
<DisplayString Condition="m_type == 1">{{ Depth @ BP={m_TEX0.TBP0,X}-{m_end_block,X} BW={m_TEX0.TBW} PSM={m_TEX0.PSM,X} {m_unscaled_size.x}x{m_unscaled_size.y} {m_valid.z},{m_valid.w} }}</DisplayString>
</Type>
</AutoVisualizer>
+49 -2
View File
@@ -143,6 +143,12 @@ class GSDeviceMTL final : public GSDevice
public:
using DepthStencilSelector = GSHWDrawConfig::DepthStencilSelector;
using SamplerSelector = GSHWDrawConfig::SamplerSelector;
enum class UsePresentDrawable : u8
{
Never = 0,
Always = 1,
IfVsync = 2,
};
enum class LoadAction
{
DontCare,
@@ -219,7 +225,18 @@ public:
MRCOwned<id<MTLFence>> m_draw_sync_fence;
MRCOwned<MTLFunctionConstantValues*> m_fn_constants;
MRCOwned<MTLVertexDescriptor*> m_hw_vertex;
std::unique_ptr<GSTextureMTL> m_font;
// Previously in MetalHostDisplay.
MRCOwned<NSView*> m_view;
MRCOwned<CAMetalLayer*> m_layer;
MRCOwned<id<CAMetalDrawable>> m_current_drawable;
MRCOwned<MTLRenderPassDescriptor*> m_pass_desc;
u32 m_capture_start_frame;
UsePresentDrawable m_use_present_drawable;
bool m_gpu_timing_enabled = false;
double m_accumulated_gpu_time = 0;
double m_last_gpu_time_end = 0;
std::mutex m_mtx;
// Draw IDs are used to make sure we're not clobbering things
u64 m_current_draw = 1;
@@ -361,15 +378,45 @@ public:
MRCOwned<id<MTLFunction>> LoadShader(NSString* name);
MRCOwned<id<MTLRenderPipelineState>> MakePipeline(MTLRenderPipelineDescriptor* desc, id<MTLFunction> vertex, id<MTLFunction> fragment, NSString* name);
MRCOwned<id<MTLComputePipelineState>> MakeComputePipeline(id<MTLFunction> compute, NSString* name);
bool Create() override;
bool Create(const WindowInfo& wi, VsyncMode vsync) override;
void Destroy() override;
void AttachSurfaceOnMainThread();
void DetachSurfaceOnMainThread();
RenderAPI GetRenderAPI() const override;
bool HasSurface() const override;
void DestroySurface() override;
bool ChangeWindow(const WindowInfo& wi) override;
bool SupportsExclusiveFullscreen() const override;
bool IsExclusiveFullscreen() override;
bool SetExclusiveFullscreen(bool fullscreen, u32 width, u32 height, float refresh_rate) override;
std::string GetDriverInfo() const override;
void ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale) override;
void UpdateTexture(id<MTLTexture> texture, u32 x, u32 y, u32 width, u32 height, const void* data, u32 data_stride);
PresentResult BeginPresent(bool frame_skip) override;
void EndPresent() override;
void SetVSync(VsyncMode mode) override;
bool GetHostRefreshRate(float* refresh_rate) override;
bool SetGPUTimingEnabled(bool enabled) override;
float GetAndResetAccumulatedGPUTime() override;
void AccumulateCommandBufferTime(id<MTLCommandBuffer> buffer);
void ClearRenderTarget(GSTexture* t, const GSVector4& c) override;
void ClearRenderTarget(GSTexture* t, u32 c) override;
void ClearDepth(GSTexture* t) override;
void ClearStencil(GSTexture* t, u8 c) override;
void InvalidateRenderTarget(GSTexture* t) override;
std::unique_ptr<GSDownloadTexture> CreateDownloadTexture(u32 width, u32 height, GSTexture::Format format) override;
void ClearSamplerCache() override;
void CopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r, u32 destX, u32 destY) override;
void DoStretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, id<MTLRenderPipelineState> pipeline, bool linear, LoadAction load_action, const void* frag_uniform, size_t frag_uniform_len);
void DrawStretchRect(const GSVector4& sRect, const GSVector4& dRect, const GSVector2i& ds);
+368 -34
View File
@@ -14,14 +14,14 @@
*/
#include "PrecompiledHeader.h"
#include "GSMetalCPPAccessible.h"
#include "GSDeviceMTL.h"
#include "Frontend/MetalHostDisplay.h"
#include "GSTextureMTL.h"
#include "Host.h"
#include "GS/Renderers/Metal/GSMetalCPPAccessible.h"
#include "GS/Renderers/Metal/GSDeviceMTL.h"
#include "GS/Renderers/Metal/GSTextureMTL.h"
#include "GS/GSPerfMon.h"
#include "HostDisplay.h"
#include <imgui.h>
#include "imgui.h"
#ifdef __APPLE__
#include "GSMTLSharedHeader.h"
@@ -36,6 +36,15 @@ GSDevice* MakeGSDeviceMTL()
return new GSDeviceMTL();
}
std::vector<std::string> GetMetalAdapterList()
{ @autoreleasepool {
std::vector<std::string> list;
auto devs = MRCTransfer(MTLCopyAllDevices());
for (id<MTLDevice> dev in devs.Get())
list.push_back([[dev name] UTF8String]);
return list;
}}
bool GSDeviceMTL::UsageTracker::PrepareForAllocation(u64 last_draw, size_t amt)
{
auto removeme = std::find_if(m_usage.begin(), m_usage.end(), [last_draw](UsageEntry usage){ return usage.drawno > last_draw; });
@@ -82,11 +91,8 @@ GSDeviceMTL::GSDeviceMTL()
}
GSDeviceMTL::~GSDeviceMTL()
{ @autoreleasepool {
FlushEncoders();
std::lock_guard<std::mutex> guard(m_backref->first);
m_backref->second = nullptr;
}}
{
}
GSDeviceMTL::Map GSDeviceMTL::Allocate(UploadBuffer& buffer, size_t amt)
{
@@ -241,7 +247,7 @@ void GSDeviceMTL::DrawCommandBufferFinished(u64 draw, id<MTLCommandBuffer> buffe
// We can do the update non-atomically because we only ever update under the lock
u64 newval = std::max(draw, m_last_finished_draw.load(std::memory_order_relaxed));
m_last_finished_draw.store(newval, std::memory_order_release);
static_cast<MetalHostDisplay*>(g_host_display.get())->AccumulateCommandBufferTime(buffer);
AccumulateCommandBufferTime(buffer);
}
void GSDeviceMTL::FlushEncoders()
@@ -706,19 +712,100 @@ static void setFnConstantI(MTLFunctionConstantValues* fc, unsigned int value, GS
[fc setConstantValue:&value type:MTLDataTypeUInt atIndex:constant];
}
bool GSDeviceMTL::Create()
template <typename Fn>
static void OnMainThread(Fn&& fn)
{
if ([NSThread isMainThread])
fn();
else
dispatch_sync(dispatch_get_main_queue(), fn);
}
RenderAPI GSDeviceMTL::GetRenderAPI() const
{
return RenderAPI::Metal;
}
bool GSDeviceMTL::HasSurface() const { return static_cast<bool>(m_layer);}
void GSDeviceMTL::AttachSurfaceOnMainThread()
{
ASSERT([NSThread isMainThread]);
m_layer = MRCRetain([CAMetalLayer layer]);
[m_layer setDrawableSize:CGSizeMake(m_window_info.surface_width, m_window_info.surface_height)];
[m_layer setDevice:m_dev.dev];
m_view = MRCRetain((__bridge NSView*)m_window_info.window_handle);
[m_view setWantsLayer:YES];
[m_view setLayer:m_layer];
}
void GSDeviceMTL::DetachSurfaceOnMainThread()
{
ASSERT([NSThread isMainThread]);
[m_view setLayer:nullptr];
[m_view setWantsLayer:NO];
m_view = nullptr;
m_layer = nullptr;
}
bool GSDeviceMTL::Create(const WindowInfo& wi, VsyncMode vsync)
{ @autoreleasepool {
if (!GSDevice::Create())
if (!GSDevice::Create(wi, vsync))
return false;
if (g_host_display->GetRenderAPI() != RenderAPI::Metal)
return false;
NSString* ns_adapter_name = [NSString stringWithUTF8String:EmuConfig.GS.Adapter.c_str()];
auto devs = MRCTransfer(MTLCopyAllDevices());
for (id<MTLDevice> dev in devs.Get())
{
if ([[dev name] isEqualToString:ns_adapter_name])
m_dev = GSMTLDevice(MRCRetain(dev));
}
if (!m_dev.dev)
{
if (!EmuConfig.GS.Adapter.empty())
Console.Warning("Metal: Couldn't find adapter %s, using default", EmuConfig.GS.Adapter.c_str());
m_dev = GSMTLDevice(MRCTransfer(MTLCreateSystemDefaultDevice()));
if (!m_dev.dev)
Host::ReportErrorAsync("No Metal Devices Available", "No Metal-supporting GPUs were found. PCSX2 requires a Metal GPU (available on all macs from 2012 onwards).");
}
m_queue = MRCTransfer([m_dev.dev newCommandQueue]);
if (!g_host_display->HasDevice() || !g_host_display->HasSurface())
m_pass_desc = MRCTransfer([MTLRenderPassDescriptor new]);
[m_pass_desc colorAttachments][0].loadAction = MTLLoadActionClear;
[m_pass_desc colorAttachments][0].clearColor = MTLClearColorMake(0, 0, 0, 0);
[m_pass_desc colorAttachments][0].storeAction = MTLStoreActionStore;
if (char* env = getenv("MTL_USE_PRESENT_DRAWABLE"))
m_use_present_drawable = static_cast<UsePresentDrawable>(atoi(env));
else if (@available(macOS 13.0, *))
m_use_present_drawable = UsePresentDrawable::Always;
else // Before Ventura, presentDrawable acts like vsync is on when windowed
m_use_present_drawable = UsePresentDrawable::IfVsync;
m_capture_start_frame = 0;
if (char* env = getenv("MTL_CAPTURE"))
{
m_capture_start_frame = atoi(env);
}
if (m_capture_start_frame)
{
Console.WriteLn("Metal will capture frame %u", m_capture_start_frame);
}
if (m_dev.IsOk() && m_queue)
{
OnMainThread([this]
{
AttachSurfaceOnMainThread();
});
SetVSync(vsync);
}
else
{
return false;
m_dev = *static_cast<const GSMTLDevice*>(g_host_display->GetDevice());
m_queue = MRCRetain((__bridge id<MTLCommandQueue>)g_host_display->GetContext());
MTLPixelFormat layer_px_fmt = [(__bridge CAMetalLayer*)g_host_display->GetSurface() pixelFormat];
}
MTLPixelFormat layer_px_fmt = [m_layer pixelFormat];
m_features.broken_point_sampler = [[m_dev.dev name] containsString:@"AMD"];
m_features.geometry_shader = false;
@@ -1042,8 +1129,6 @@ bool GSDeviceMTL::Create()
pdesc.vertexDescriptor.layouts[0].stride = sizeof(ImDrawVert);
pdesc.colorAttachments[0].pixelFormat = layer_px_fmt;
m_imgui_pipeline = MakePipeline(pdesc, LoadShader(@"vs_imgui"), LoadShader(@"ps_imgui"), @"imgui");
if (!m_dev.features.texture_swizzle)
m_imgui_pipeline_a8 = MakePipeline(pdesc, LoadShader(@"vs_imgui"), LoadShader(@"ps_imgui_a8"), @"imgui_a8");
[initCommands commit];
}
@@ -1054,6 +1139,255 @@ bool GSDeviceMTL::Create()
return true;
}}
void GSDeviceMTL::Destroy()
{ @autoreleasepool {
FlushEncoders();
std::lock_guard<std::mutex> guard(m_backref->first);
m_backref->second = nullptr;
GSDevice::Destroy();
GSDeviceMTL::DestroySurface();
m_queue = nullptr;
m_dev.Reset();
}}
void GSDeviceMTL::DestroySurface()
{
if (!m_layer)
return;
OnMainThread([this]{ DetachSurfaceOnMainThread(); });
m_layer = nullptr;
}
bool GSDeviceMTL::ChangeWindow(const WindowInfo& wi)
{
OnMainThread([this, &wi]
{
DetachSurfaceOnMainThread();
m_window_info = wi;
AttachSurfaceOnMainThread();
});
return true;
}
bool GSDeviceMTL::SupportsExclusiveFullscreen() const { return false; }
bool GSDeviceMTL::IsExclusiveFullscreen() { return false; }
bool GSDeviceMTL::SetExclusiveFullscreen(bool fullscreen, u32 width, u32 height, float refresh_rate) { return false; }
std::string GSDeviceMTL::GetDriverInfo() const
{ @autoreleasepool {
std::string desc([[m_dev.dev description] UTF8String]);
desc += "\n Texture Swizzle: " + std::string(m_dev.features.texture_swizzle ? "Supported" : "Unsupported");
desc += "\n Unified Memory: " + std::string(m_dev.features.unified_memory ? "Supported" : "Unsupported");
desc += "\n Framebuffer Fetch: " + std::string(m_dev.features.framebuffer_fetch ? "Supported" : "Unsupported");
desc += "\n Primitive ID: " + std::string(m_dev.features.primid ? "Supported" : "Unsupported");
desc += "\n Shader Version: " + std::string(to_string(m_dev.features.shader_version));
desc += "\n Max Texture Size: " + std::to_string(m_dev.features.max_texsize);
return desc;
}}
void GSDeviceMTL::ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale)
{
m_window_info.surface_scale = new_window_scale;
if (m_window_info.surface_width == static_cast<u32>(new_window_width) && m_window_info.surface_height == static_cast<u32>(new_window_height))
return;
m_window_info.surface_width = new_window_width;
m_window_info.surface_height = new_window_height;
@autoreleasepool
{
[m_layer setDrawableSize:CGSizeMake(new_window_width, new_window_height)];
}
}
void GSDeviceMTL::UpdateTexture(id<MTLTexture> texture, u32 x, u32 y, u32 width, u32 height, const void* data, u32 data_stride)
{
id<MTLCommandBuffer> cmdbuf = [m_queue commandBuffer];
id<MTLBlitCommandEncoder> enc = [cmdbuf blitCommandEncoder];
size_t bytes = data_stride * height;
MRCOwned<id<MTLBuffer>> buf = MRCTransfer([m_dev.dev newBufferWithLength:bytes options:MTLResourceStorageModeShared | MTLResourceCPUCacheModeWriteCombined]);
memcpy([buf contents], data, bytes);
[enc copyFromBuffer:buf
sourceOffset:0
sourceBytesPerRow:data_stride
sourceBytesPerImage:bytes
sourceSize:MTLSizeMake(width, height, 1)
toTexture:texture
destinationSlice:0
destinationLevel:0
destinationOrigin:MTLOriginMake(0, 0, 0)];
[enc endEncoding];
[cmdbuf commit];
}
static bool s_capture_next = false;
GSDevice::PresentResult GSDeviceMTL::BeginPresent(bool frame_skip)
{ @autoreleasepool {
GSDeviceMTL* dev = static_cast<GSDeviceMTL*>(g_gs_device.get());
if (dev && m_capture_start_frame && dev->FrameNo() == m_capture_start_frame)
s_capture_next = true;
if (frame_skip || m_window_info.type == WindowInfo::Type::Surfaceless || !g_gs_device)
{
ImGui::EndFrame();
return PresentResult::FrameSkipped;
}
id<MTLCommandBuffer> buf = dev->GetRenderCmdBuf();
m_current_drawable = MRCRetain([m_layer nextDrawable]);
dev->EndRenderPass();
if (!m_current_drawable)
{
[buf pushDebugGroup:@"Present Skipped"];
[buf popDebugGroup];
dev->FlushEncoders();
ImGui::EndFrame();
return PresentResult::FrameSkipped;
}
[m_pass_desc colorAttachments][0].texture = [m_current_drawable texture];
id<MTLRenderCommandEncoder> enc = [buf renderCommandEncoderWithDescriptor:m_pass_desc];
[enc setLabel:@"Present"];
dev->m_current_render.encoder = MRCRetain(enc);
return PresentResult::OK;
}}
void GSDeviceMTL::EndPresent()
{ @autoreleasepool {
GSDeviceMTL* dev = static_cast<GSDeviceMTL*>(g_gs_device.get());
pxAssertDev(dev && dev->m_current_render.encoder && dev->m_current_render_cmdbuf, "BeginPresent cmdbuf was destroyed");
ImGui::Render();
dev->RenderImGui(ImGui::GetDrawData());
dev->EndRenderPass();
if (m_current_drawable)
{
const bool use_present_drawable = m_use_present_drawable == UsePresentDrawable::Always ||
(m_use_present_drawable == UsePresentDrawable::IfVsync && m_vsync_mode != VsyncMode::Off);
if (use_present_drawable)
[dev->m_current_render_cmdbuf presentDrawable:m_current_drawable];
else
[dev->m_current_render_cmdbuf addScheduledHandler:[drawable = std::move(m_current_drawable)](id<MTLCommandBuffer>){
[drawable present];
}];
}
dev->FlushEncoders();
dev->FrameCompleted();
m_current_drawable = nullptr;
if (m_capture_start_frame)
{
if (@available(macOS 10.15, iOS 13, *))
{
static NSString* const path = @"/tmp/PCSX2MTLCapture.gputrace";
static u32 frames;
if (frames)
{
--frames;
if (!frames)
{
[[MTLCaptureManager sharedCaptureManager] stopCapture];
Console.WriteLn("Metal Trace Capture to /tmp/PCSX2MTLCapture.gputrace finished");
[[NSWorkspace sharedWorkspace] selectFile:path
inFileViewerRootedAtPath:@"/tmp/"];
}
}
else if (s_capture_next)
{
s_capture_next = false;
MTLCaptureManager* mgr = [MTLCaptureManager sharedCaptureManager];
if ([mgr supportsDestination:MTLCaptureDestinationGPUTraceDocument])
{
MTLCaptureDescriptor* desc = [[MTLCaptureDescriptor new] autorelease];
[desc setCaptureObject:m_dev.dev];
if ([[NSFileManager defaultManager] fileExistsAtPath:path])
[[NSFileManager defaultManager] removeItemAtPath:path error:nil];
[desc setOutputURL:[NSURL fileURLWithPath:path]];
[desc setDestination:MTLCaptureDestinationGPUTraceDocument];
NSError* err = nullptr;
[mgr startCaptureWithDescriptor:desc error:&err];
if (err)
{
Console.Error("Metal Trace Capture failed: %s", [[err localizedDescription] UTF8String]);
}
else
{
Console.WriteLn("Metal Trace Capture to /tmp/PCSX2MTLCapture.gputrace started");
frames = 2;
}
}
else
{
Console.Error("Metal Trace Capture Failed: MTLCaptureManager doesn't support GPU trace documents! (Did you forget to run with METAL_CAPTURE_ENABLED=1?)");
}
}
}
}
}}
void GSDeviceMTL::SetVSync(VsyncMode mode)
{
[m_layer setDisplaySyncEnabled:mode != VsyncMode::Off];
m_vsync_mode = mode;
}
bool GSDeviceMTL::GetHostRefreshRate(float* refresh_rate)
{
OnMainThread([this, refresh_rate]
{
u32 did = [[[[[m_view window] screen] deviceDescription] valueForKey:@"NSScreenNumber"] unsignedIntValue];
if (CGDisplayModeRef mode = CGDisplayCopyDisplayMode(did))
{
*refresh_rate = CGDisplayModeGetRefreshRate(mode);
CGDisplayModeRelease(mode);
}
else
{
*refresh_rate = 0;
}
});
return *refresh_rate != 0;
}
bool GSDeviceMTL::SetGPUTimingEnabled(bool enabled)
{
if (enabled == m_gpu_timing_enabled)
return true;
if (@available(macOS 10.15, iOS 10.3, *))
{
std::lock_guard<std::mutex> l(m_mtx);
m_gpu_timing_enabled = enabled;
m_accumulated_gpu_time = 0;
m_last_gpu_time_end = 0;
return true;
}
return false;
}
float GSDeviceMTL::GetAndResetAccumulatedGPUTime()
{
std::lock_guard<std::mutex> l(m_mtx);
float time = m_accumulated_gpu_time * 1000;
m_accumulated_gpu_time = 0;
return time;
}
void GSDeviceMTL::AccumulateCommandBufferTime(id<MTLCommandBuffer> buffer)
{
std::lock_guard<std::mutex> l(m_mtx);
if (!m_gpu_timing_enabled)
return;
// We do the check before enabling m_gpu_timing_enabled
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunguarded-availability"
// It's unlikely, but command buffers can overlap or run out of order
// This doesn't handle every case (fully out of order), but it should at least handle overlapping
double begin = std::max(m_last_gpu_time_end, [buffer GPUStartTime]);
double end = [buffer GPUEndTime];
if (end > begin)
{
m_accumulated_gpu_time += end - begin;
m_last_gpu_time_end = end;
}
#pragma clang diagnostic pop
}
void GSDeviceMTL::ClearRenderTarget(GSTexture* t, const GSVector4& c)
{
if (!t) return;
@@ -1078,11 +1412,21 @@ void GSDeviceMTL::ClearStencil(GSTexture* t, uint8 c)
static_cast<GSTextureMTL*>(t)->RequestStencilClear(c);
}
void GSDeviceMTL::InvalidateRenderTarget(GSTexture* t)
{
// TODO: Implement me
}
std::unique_ptr<GSDownloadTexture> GSDeviceMTL::CreateDownloadTexture(u32 width, u32 height, GSTexture::Format format)
{
return GSDownloadTextureMTL::Create(this, width, height, format);
}
void GSDeviceMTL::ClearSamplerCache()
{
// TODO: Implement me
}
void GSDeviceMTL::CopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r, u32 destX, u32 destY)
{ @autoreleasepool {
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
@@ -1231,7 +1575,7 @@ static_assert(offsetof(DisplayConstantBuffer, TimeAndPad.x) == offsetof(G
void GSDeviceMTL::PresentRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, PresentShader shader, float shaderTime, bool linear)
{ @autoreleasepool {
GSVector2i ds = dTex ? dTex->GetSize() : GSVector2i(g_host_display->GetWindowWidth(), g_host_display->GetWindowHeight());
GSVector2i ds = dTex ? dTex->GetSize() : GetWindowSize();
DisplayConstantBuffer cb;
cb.SetSource(sRect, sTex->GetSize());
cb.SetTarget(dRect, ds);
@@ -1965,12 +2309,11 @@ void GSDeviceMTL::RenderImGui(ImDrawData* data)
[enc setVertexBuffer:map.gpu_buffer offset:map.gpu_offset atIndex:GSMTLBufferIndexVertices];
[enc setVertexBytes:&transform length:sizeof(transform) atIndex:GSMTLBufferIndexUniforms];
simd::uint4 last_scissor = simd::make_uint4(0, 0, g_host_display->GetWindowWidth(), g_host_display->GetWindowHeight());
simd::uint4 last_scissor = simd::make_uint4(0, 0, GetWindowWidth(), GetWindowHeight());
simd::float2 fb_size = simd_float(last_scissor.zw);
simd::float2 clip_off = ToSimd(data->DisplayPos); // (0,0) unless using multi-viewports
simd::float2 clip_scale = ToSimd(data->FramebufferScale); // (1,1) unless using retina display which are often (2,2)
ImTextureID last_tex = nullptr;
bool last_tex_a8 = false;
for (int i = 0; i < data->CmdListsCount; i++)
{
@@ -2003,15 +2346,6 @@ void GSDeviceMTL::RenderImGui(ImDrawData* data)
{
last_tex = tex;
[enc setFragmentTexture:(__bridge id<MTLTexture>)tex atIndex:0];
if (!m_dev.features.texture_swizzle)
{
bool a8 = [(__bridge id<MTLTexture>)tex pixelFormat] == MTLPixelFormatA8Unorm;
if (last_tex_a8 != a8)
{
[enc setRenderPipelineState:a8 ? m_imgui_pipeline_a8 : m_imgui_pipeline];
last_tex_a8 = a8;
}
}
}
[enc setVertexBufferOffset:map.gpu_offset + vtx_off + cmd.VtxOffset * sizeof(ImDrawVert) atIndex:0];
@@ -14,15 +14,16 @@
*/
#pragma once
#include <string>
#include <vector>
// Header with all metal stuff available for use with C++ (rather than Objective-C++)
#ifdef __APPLE__
#include "HostDisplay.h"
class GSDevice;
GSDevice* MakeGSDeviceMTL();
HostDisplay* MakeMetalHostDisplay();
HostDisplay::AdapterAndModeList GetMetalAdapterAndModeList();
std::vector<std::string> GetMetalAdapterList();
#endif
-6
View File
@@ -362,12 +362,6 @@ fragment half4 ps_imgui(ImGuiShaderData data [[stage_in]], texture2d<half> textu
return data.c * texture.sample(s, data.t);
}
fragment half4 ps_imgui_a8(ImGuiShaderData data [[stage_in]], texture2d<half> texture [[texture(GSMTLTextureIndexNonHW)]])
{
constexpr sampler s(coord::normalized, filter::linear, address::clamp_to_edge);
return data.c * half4(1, 1, 1, texture.sample(s, data.t).a);
}
fragment float4 ps_shadeboost(float4 p [[position]], DirectReadTextureIn<float> tex, constant float3& cb [[buffer(GSMTLBufferIndexUniforms)]])
{
const float brt = cb.x;
+562 -179
View File
@@ -14,16 +14,20 @@
*/
#include "PrecompiledHeader.h"
#include "common/StringUtil.h"
#include "GS/GSState.h"
#include "GSDeviceOGL.h"
#include "GLState.h"
#include "GS/GSGL.h"
#include "GS/GSUtil.h"
#include "GS/Renderers/OpenGL/GSDeviceOGL.h"
#include "GS/Renderers/OpenGL/GLState.h"
#include "Host.h"
#include "HostDisplay.h"
#include "ShaderCacheVersion.h"
#include "common/StringUtil.h"
#include "imgui.h"
#include "IconsFontAwesome5.h"
#include <cinttypes>
#include <fstream>
#include <sstream>
@@ -45,35 +49,7 @@ GSDeviceOGL::GSDeviceOGL() = default;
GSDeviceOGL::~GSDeviceOGL()
{
s_texture_upload_buffer.reset();
if (m_vertex_array_object)
glDeleteVertexArrays(1, &m_vertex_array_object);
m_vertex_stream_buffer.reset();
m_index_stream_buffer.reset();
// Clean m_convert
delete m_convert.dss;
delete m_convert.dss_write;
// Clean m_date
delete m_date.dss;
// Clean various opengl allocation
glDeleteFramebuffers(1, &m_fbo);
glDeleteFramebuffers(1, &m_fbo_read);
glDeleteFramebuffers(1, &m_fbo_write);
// Delete HW FX
m_vertex_uniform_stream_buffer.reset();
m_fragment_uniform_stream_buffer.reset();
glDeleteSamplers(1, &m_palette_ss);
m_programs.clear();
glDeleteSamplers(std::size(m_ps_ss), m_ps_ss);
for (GSDepthStencilOGL* ds : m_om_dss)
delete ds;
pxAssert(!m_gl_context);
}
GSTexture* GSDeviceOGL::CreateSurface(GSTexture::Type type, int width, int height, int levels, GSTexture::Format format)
@@ -82,21 +58,60 @@ GSTexture* GSDeviceOGL::CreateSurface(GSTexture::Type type, int width, int heigh
return new GSTextureOGL(type, width, height, levels, format);
}
bool GSDeviceOGL::Create()
RenderAPI GSDeviceOGL::GetRenderAPI() const
{
if (!GSDevice::Create())
return RenderAPI::OpenGL;
}
bool GSDeviceOGL::HasSurface() const
{
return m_window_info.type != WindowInfo::Type::Surfaceless;
}
void GSDeviceOGL::SetVSync(VsyncMode mode)
{
if (m_vsync_mode == mode || m_gl_context->GetWindowInfo().type == WindowInfo::Type::Surfaceless)
return;
// Window framebuffer has to be bound to call SetSwapInterval.
GLint current_fbo = 0;
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &current_fbo);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
if (mode != VsyncMode::Adaptive || !m_gl_context->SetSwapInterval(-1))
m_gl_context->SetSwapInterval(static_cast<s32>(mode != VsyncMode::Off));
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, current_fbo);
m_vsync_mode = mode;
}
bool GSDeviceOGL::Create(const WindowInfo& wi, VsyncMode vsync)
{
if (!GSDevice::Create(wi, vsync))
return false;
const RenderAPI render_api = g_host_display->GetRenderAPI();
if (render_api != RenderAPI::OpenGL && render_api != RenderAPI::OpenGLES)
m_gl_context = GL::Context::Create(wi);
if (!m_gl_context)
{
Console.Error("Failed to create any GL context");
m_gl_context.reset();
return false;
}
if (!m_gl_context->MakeCurrent())
{
Console.Error("Failed to make GL context current");
return false;
}
// Check openGL requirement as soon as possible so we can switch to another
// renderer/device
GLLoader::is_gles = (render_api == RenderAPI::OpenGLES);
GLLoader::is_gles = m_gl_context->IsGLES();
if (!GLLoader::check_gl_requirements())
return false;
SetSwapInterval();
if (!GSConfig.DisableShaderCache)
{
if (!m_shader_cache.Open(false, EmuFolders::Cache, SHADER_CACHE_VERSION))
@@ -155,7 +170,7 @@ bool GSDeviceOGL::Create()
{
if (!GLLoader::is_gles)
{
glDebugMessageCallback((GLDEBUGPROC)DebugOutputToFile, NULL);
glDebugMessageCallback(DebugMessageCallback, NULL);
glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, NULL, true);
// Useless info message on Nvidia driver
@@ -164,7 +179,7 @@ bool GSDeviceOGL::Create()
}
else if (GLAD_GL_KHR_debug)
{
glDebugMessageCallbackKHR((GLDEBUGPROC)DebugOutputToFile, NULL);
glDebugMessageCallbackKHR(DebugMessageCallback, NULL);
glDebugMessageControlKHR(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, NULL, true);
}
@@ -183,14 +198,15 @@ bool GSDeviceOGL::Create()
GL_PUSH("GSDeviceOGL::Various");
glGenFramebuffers(1, &m_fbo);
// Always write to the first buffer
OMSetFBO(m_fbo);
const GLenum target[1] = {GL_COLOR_ATTACHMENT0};
glDrawBuffers(1, target);
OMSetFBO(0);
glGenFramebuffers(1, &m_fbo_read);
glGenFramebuffers(1, &m_fbo_write);
OMSetFBO(m_fbo);
// Always write to the first buffer
static constexpr GLenum target[1] = {GL_COLOR_ATTACHMENT0};
glDrawBuffers(1, target);
// Always read from the first buffer
glBindFramebuffer(GL_READ_FRAMEBUFFER, m_fbo_read);
glReadBuffer(GL_COLOR_ATTACHMENT0);
@@ -239,10 +255,6 @@ bool GSDeviceOGL::Create()
glVertexAttribPointer(7, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(GSVertex), (const GLvoid*)(28));
}
// must be done after va is created
GLState::Clear();
RestoreAPIState();
// ****************************************************************
// Pre Generate the different sampler object
// ****************************************************************
@@ -395,24 +407,10 @@ bool GSDeviceOGL::Create()
}
// ****************************************************************
// Shade boost
// Post processing
// ****************************************************************
{
GL_PUSH("GSDeviceOGL::Shadeboost");
const auto shader = Host::ReadResourceFileToString("shaders/opengl/shadeboost.glsl");
if (!shader.has_value())
{
Host::ReportErrorAsync("GS", "Failed to read shaders/opengl/shadeboost.glsl.");
return false;
}
const std::string ps(GetShaderSource("ps_main", GL_FRAGMENT_SHADER, *shader));
if (!m_shader_cache.GetProgram(&m_shadeboost.ps, m_convert.vs, {}, ps))
return false;
m_shadeboost.ps.RegisterUniform("params");
m_shadeboost.ps.SetName("Shadeboost pipe");
}
if (!CompileShadeBoostProgram() || !CompileFXAAProgram())
return false;
// Image load store and GLSL 420pack is core in GL4.2, no need to check.
m_features.cas_sharpening = ((GLAD_GL_VERSION_4_2 && GLAD_GL_ARB_compute_shader) || GLAD_GL_ES_VERSION_3_2) && CreateCASPrograms();
@@ -491,6 +489,9 @@ bool GSDeviceOGL::Create()
}
}
if (!CreateImGuiProgram())
return false;
// Basic to ensure structures are correctly packed
static_assert(sizeof(VSSelector) == 1, "Wrong VSSelector size");
static_assert(sizeof(PSSelector) == 12, "Wrong PSSelector size");
@@ -501,6 +502,20 @@ bool GSDeviceOGL::Create()
return true;
}
void GSDeviceOGL::Destroy()
{
GSDevice::Destroy();
if (m_gl_context)
{
DestroyTimestampQueries();
DestroyResources();
m_gl_context->DoneCurrent();
m_gl_context.reset();
}
}
bool GSDeviceOGL::CreateTextureFX()
{
GL_PUSH("GSDeviceOGL::CreateTextureFX");
@@ -529,78 +544,293 @@ bool GSDeviceOGL::CreateTextureFX()
m_om_dss[key] = CreateDepthStencil(OMDepthStencilSelector(key));
}
GL::Program::ResetLastProgram();
return true;
}
void GSDeviceOGL::ResetAPIState()
void GSDeviceOGL::SetSwapInterval()
{
if (GLState::point_size)
glDisable(GL_PROGRAM_POINT_SIZE);
if (GLState::line_width != 1.0f)
glLineWidth(1.0f);
// clear out DSB
glBlendFuncSeparate(GL_ONE, GL_ZERO, GL_ONE, GL_ZERO);
glDisable(GL_BLEND);
glActiveTexture(GL_TEXTURE0);
const int interval = ((m_vsync_mode == VsyncMode::Adaptive) ? -1 : ((m_vsync_mode == VsyncMode::On) ? 1 : 0));
m_gl_context->SetSwapInterval(interval);
}
void GSDeviceOGL::RestoreAPIState()
void GSDeviceOGL::DestroyResources()
{
glBindVertexArray(m_vertex_array_object);
m_shader_cache.Close();
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, GLState::fbo);
if (m_palette_ss != 0)
glDeleteSamplers(1, &m_palette_ss);
glViewportIndexedf(0, 0, 0, static_cast<float>(GLState::viewport.x), static_cast<float>(GLState::viewport.y));
glScissorIndexed(0, GLState::scissor.x, GLState::scissor.y, GLState::scissor.width(), GLState::scissor.height());
m_programs.clear();
glBlendEquationSeparate(GLState::eq_RGB, GL_FUNC_ADD);
glBlendFuncSeparate(GLState::f_sRGB, GLState::f_dRGB, GL_ONE, GL_ZERO);
for (GSDepthStencilOGL* ds : m_om_dss)
delete ds;
const float bf = static_cast<float>(GLState::bf) / 128.0f;
glBlendColor(bf, bf, bf, bf);
if (m_ps_ss[0] != 0)
glDeleteSamplers(std::size(m_ps_ss), m_ps_ss);
if (GLState::blend)
m_imgui.ps.Destroy();
if (m_imgui.vao != 0)
glDeleteVertexArrays(1, &m_imgui.vao);
m_cas.upscale_ps.Destroy();
m_cas.sharpen_ps.Destroy();
m_shadeboost.ps.Destroy();
for (GL::Program& prog : m_date.primid_ps)
prog.Destroy();
delete m_date.dss;
m_fxaa.ps.Destroy();
for (GL::Program& prog : m_present)
prog.Destroy();
for (GL::Program& prog : m_convert.ps)
prog.Destroy();
delete m_convert.dss;
delete m_convert.dss_write;
for (GL::Program& prog : m_interlace.ps)
prog.Destroy();
for (GL::Program& prog : m_merge_obj.ps)
prog.Destroy();
m_fragment_uniform_stream_buffer.reset();
m_vertex_uniform_stream_buffer.reset();
glBindVertexArray(0);
if (m_vertex_array_object != 0)
glDeleteVertexArrays(1, &m_vertex_array_object);
m_index_stream_buffer.reset();
m_vertex_stream_buffer.reset();
s_texture_upload_buffer.reset();
if (m_fbo != 0)
glDeleteFramebuffers(1, &m_fbo);
if (m_fbo_read != 0)
glDeleteFramebuffers(1, &m_fbo_read);
if (m_fbo_write != 0)
glDeleteFramebuffers(1, &m_fbo_write);
}
bool GSDeviceOGL::ChangeWindow(const WindowInfo& new_wi)
{
pxAssert(m_gl_context);
if (!m_gl_context->ChangeSurface(new_wi))
{
glEnable(GL_BLEND);
Console.Error("Failed to change surface");
return false;
}
m_window_info = m_gl_context->GetWindowInfo();
if (new_wi.type != WindowInfo::Type::Surfaceless)
{
// reset vsync rate, since it (usually) gets lost
if (m_vsync_mode != VsyncMode::Adaptive || !m_gl_context->SetSwapInterval(-1))
m_gl_context->SetSwapInterval(static_cast<s32>(m_vsync_mode != VsyncMode::Off));
}
return true;
}
void GSDeviceOGL::ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale)
{
m_window_info.surface_scale = new_window_scale;
if (m_window_info.surface_width == static_cast<u32>(new_window_width) &&
m_window_info.surface_height == static_cast<u32>(new_window_height))
{
return;
}
m_gl_context->ResizeSurface(static_cast<u32>(new_window_width), static_cast<u32>(new_window_height));
m_window_info = m_gl_context->GetWindowInfo();
}
bool GSDeviceOGL::SupportsExclusiveFullscreen() const
{
return false;
}
bool GSDeviceOGL::IsExclusiveFullscreen()
{
return false;
}
bool GSDeviceOGL::SetExclusiveFullscreen(bool fullscreen, u32 width, u32 height, float refresh_rate)
{
return false;
}
void GSDeviceOGL::DestroySurface()
{
m_window_info = {};
if (!m_gl_context->ChangeSurface(m_window_info))
Console.Error("Failed to switch to surfaceless");
}
std::string GSDeviceOGL::GetDriverInfo() const
{
const char* gl_vendor = reinterpret_cast<const char*>(glGetString(GL_VENDOR));
const char* gl_renderer = reinterpret_cast<const char*>(glGetString(GL_RENDERER));
const char* gl_version = reinterpret_cast<const char*>(glGetString(GL_VERSION));
return StringUtil::StdStringFromFormat(
"%s Context:\n%s\n%s %s", m_gl_context->IsGLES() ? "OpenGL ES" : "OpenGL", gl_version, gl_vendor, gl_renderer);
}
GSDevice::PresentResult GSDeviceOGL::BeginPresent(bool frame_skip)
{
if (frame_skip || m_window_info.type == WindowInfo::Type::Surfaceless)
return PresentResult::FrameSkipped;
OMSetFBO(0);
OMSetColorMaskState();
glDisable(GL_SCISSOR_TEST);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glEnable(GL_SCISSOR_TEST);
const GSVector2i size = GetWindowSize();
SetViewport(size);
SetScissor(GSVector4i::loadh(size));
return PresentResult::OK;
}
void GSDeviceOGL::EndPresent()
{
RenderImGui();
if (m_gpu_timing_enabled)
PopTimestampQuery();
m_gl_context->SwapBuffers();
if (m_gpu_timing_enabled)
KickTimestampQuery();
}
void GSDeviceOGL::CreateTimestampQueries()
{
const bool gles = m_gl_context->IsGLES();
const auto GenQueries = gles ? glGenQueriesEXT : glGenQueries;
GenQueries(static_cast<u32>(m_timestamp_queries.size()), m_timestamp_queries.data());
KickTimestampQuery();
}
void GSDeviceOGL::DestroyTimestampQueries()
{
if (m_timestamp_queries[0] == 0)
return;
const bool gles = m_gl_context->IsGLES();
const auto DeleteQueries = gles ? glDeleteQueriesEXT : glDeleteQueries;
if (m_timestamp_query_started)
{
const auto EndQuery = gles ? glEndQueryEXT : glEndQuery;
EndQuery(GL_TIME_ELAPSED);
}
DeleteQueries(static_cast<u32>(m_timestamp_queries.size()), m_timestamp_queries.data());
m_timestamp_queries.fill(0);
m_read_timestamp_query = 0;
m_write_timestamp_query = 0;
m_waiting_timestamp_queries = 0;
m_timestamp_query_started = false;
}
void GSDeviceOGL::PopTimestampQuery()
{
const bool gles = m_gl_context->IsGLES();
if (gles)
{
GLint disjoint = 0;
glGetIntegerv(GL_GPU_DISJOINT_EXT, &disjoint);
if (disjoint)
{
DevCon.WriteLn("GPU timing disjoint, resetting.");
if (m_timestamp_query_started)
glEndQueryEXT(GL_TIME_ELAPSED);
m_read_timestamp_query = 0;
m_write_timestamp_query = 0;
m_waiting_timestamp_queries = 0;
m_timestamp_query_started = false;
}
}
while (m_waiting_timestamp_queries > 0)
{
const auto GetQueryObjectiv = gles ? glGetQueryObjectivEXT : glGetQueryObjectiv;
const auto GetQueryObjectui64v = gles ? glGetQueryObjectui64vEXT : glGetQueryObjectui64v;
GLint available = 0;
GetQueryObjectiv(m_timestamp_queries[m_read_timestamp_query], GL_QUERY_RESULT_AVAILABLE, &available);
if (!available)
break;
u64 result = 0;
GetQueryObjectui64v(m_timestamp_queries[m_read_timestamp_query], GL_QUERY_RESULT, &result);
m_accumulated_gpu_time += static_cast<float>(static_cast<double>(result) / 1000000.0);
m_read_timestamp_query = (m_read_timestamp_query + 1) % NUM_TIMESTAMP_QUERIES;
m_waiting_timestamp_queries--;
}
if (m_timestamp_query_started)
{
const auto EndQuery = gles ? glEndQueryEXT : glEndQuery;
EndQuery(GL_TIME_ELAPSED);
m_write_timestamp_query = (m_write_timestamp_query + 1) % NUM_TIMESTAMP_QUERIES;
m_timestamp_query_started = false;
m_waiting_timestamp_queries++;
}
}
void GSDeviceOGL::KickTimestampQuery()
{
if (m_timestamp_query_started || m_waiting_timestamp_queries == NUM_TIMESTAMP_QUERIES)
return;
const bool gles = m_gl_context->IsGLES();
const auto BeginQuery = gles ? glBeginQueryEXT : glBeginQuery;
BeginQuery(GL_TIME_ELAPSED, m_timestamp_queries[m_write_timestamp_query]);
m_timestamp_query_started = true;
}
bool GSDeviceOGL::SetGPUTimingEnabled(bool enabled)
{
if (m_gpu_timing_enabled == enabled)
return true;
if (enabled && m_gl_context->IsGLES() && !GLAD_GL_EXT_disjoint_timer_query)
return false;
m_gpu_timing_enabled = enabled;
if (m_gpu_timing_enabled)
CreateTimestampQueries();
else
{
glDisable(GL_BLEND);
}
DestroyTimestampQueries();
const OMColorMaskSelector msel{ GLState::wrgba };
glColorMask(msel.wr, msel.wg, msel.wb, msel.wa);
return true;
}
GLState::depth ? glEnable(GL_DEPTH_TEST) : glDisable(GL_DEPTH_TEST);
glDepthFunc(GLState::depth_func);
glDepthMask(GLState::depth_mask);
if (GLState::stencil)
{
glEnable(GL_STENCIL_TEST);
}
else
{
glDisable(GL_STENCIL_TEST);
}
glStencilFunc(GLState::stencil_func, 1, 1);
glStencilOp(GL_KEEP, GL_KEEP, GLState::stencil_pass);
glBindSampler(0, GLState::ps_ss);
for (GLuint i = 0; i < sizeof(GLState::tex_unit) / sizeof(GLState::tex_unit[0]); i++)
glBindTextureUnit(i, GLState::tex_unit[i]);
if (GLState::point_size)
glEnable(GL_PROGRAM_POINT_SIZE);
if (GLState::line_width != 1.0f)
glLineWidth(GLState::line_width);
// Force UBOs to be reuploaded, we don't know what else was bound there.
std::memset(&m_vs_cb_cache, 0xFF, sizeof(m_vs_cb_cache));
std::memset(&m_ps_cb_cache, 0xFF, sizeof(m_ps_cb_cache));
float GSDeviceOGL::GetAndResetAccumulatedGPUTime()
{
const float value = m_accumulated_gpu_time;
m_accumulated_gpu_time = 0.0f;
return value;
}
void GSDeviceOGL::DrawPrimitive()
@@ -898,8 +1128,6 @@ std::string GSDeviceOGL::GenGlslHeader(const std::string_view& entry, GLenum typ
// Need GL version 420
header += "#extension GL_ARB_shading_language_420pack: require\n";
// Need GL version 410
header += "#extension GL_ARB_separate_shader_objects: require\n";
if (m_features.framebuffer_fetch && GLAD_GL_EXT_shader_framebuffer_fetch)
header += "#extension GL_EXT_shader_framebuffer_fetch : require\n";
@@ -948,7 +1176,7 @@ std::string GSDeviceOGL::GetVSSource(VSSelector sel)
{
DevCon.WriteLn("Compiling new vertex shader with selector 0x%" PRIX64, sel.key);
std::string macro = fmt::format("#define VS_INT_FST {}\n", static_cast<u32>(sel.int_fst))
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));
@@ -961,8 +1189,8 @@ std::string GSDeviceOGL::GetGSSource(GSSelector sel)
{
DevCon.WriteLn("Compiling new geometry shader with selector 0x%" PRIX64, sel.key);
std::string macro = fmt::format("#define GS_POINT {}\n", static_cast<u32>(sel.point))
+ fmt::format("#define GS_LINE {}\n", static_cast<u32>(sel.line))
std::string macro = fmt::format("#define GS_PRIM {}\n", static_cast<u32>(sel.topology))
+ fmt::format("#define GS_EXPAND {}\n", static_cast<u32>(sel.expand))
+ fmt::format("#define GS_IIP {}\n", static_cast<u32>(sel.iip));
std::string src = GenGlslHeader("gs_main", GL_GEOMETRY_SHADER, macro);
@@ -1176,7 +1404,7 @@ void GSDeviceOGL::PresentRect(GSTexture* sTex, const GSVector4& sRect, GSTexture
{
ASSERT(sTex);
const GSVector2i ds(dTex ? dTex->GetSize() : GSVector2i(g_host_display->GetWindowWidth(), g_host_display->GetWindowHeight()));
const GSVector2i ds(dTex ? dTex->GetSize() : GSVector2i(GetWindowWidth(), GetWindowHeight()));
DisplayConstantBuffer cb;
cb.SetSource(sRect, sTex->GetSize());
cb.SetTarget(dRect, ds);
@@ -1441,24 +1669,39 @@ void GSDeviceOGL::DoInterlace(GSTexture* sTex, const GSVector4& sRect, GSTexture
StretchRect(sTex, sRect, dTex, dRect, m_interlace.ps[static_cast<int>(shader)], linear);
}
bool GSDeviceOGL::CompileFXAAProgram()
{
// Needs ARB_gpu_shader5 for gather.
if (!GLLoader::is_gles && !GLLoader::found_GL_ARB_gpu_shader5)
{
Console.Warning("FXAA is not supported with the current GPU");
return true;
}
const std::string_view fxaa_macro = "#define FXAA_GLSL_130 1\n";
std::optional<std::string> shader = Host::ReadResourceFileToString("shaders/common/fxaa.fx");
if (!shader.has_value())
{
Console.Error("Failed to read fxaa.fs");
return false;
}
const std::string ps(GetShaderSource("ps_main", GL_FRAGMENT_SHADER, shader->c_str(), fxaa_macro));
std::optional<GL::Program> prog = m_shader_cache.GetProgram(m_convert.vs, {}, ps);
if (!prog.has_value())
{
Console.Error("Failed to compile FXAA fragment shader");
return false;
}
m_fxaa.ps = std::move(prog.value());
return true;
}
void GSDeviceOGL::DoFXAA(GSTexture* sTex, GSTexture* dTex)
{
// Lazy compile
if (!m_fxaa.ps.IsValid())
{
// Needs ARB_gpu_shader5 for gather.
if (!GLLoader::is_gles && !GLLoader::found_GL_ARB_gpu_shader5)
return;
const std::string_view fxaa_macro = "#define FXAA_GLSL_130 1\n";
std::optional<std::string> shader = Host::ReadResourceFileToString("shaders/common/fxaa.fx");
if (!shader.has_value())
return;
const std::string ps(GetShaderSource("ps_main", GL_FRAGMENT_SHADER, shader->c_str(), fxaa_macro));
if (!m_fxaa.ps.Compile(m_convert.vs, {}, ps) || !m_fxaa.ps.Link())
return;
}
return;
GL_PUSH("DoFxaa");
@@ -1472,6 +1715,23 @@ void GSDeviceOGL::DoFXAA(GSTexture* sTex, GSTexture* dTex)
StretchRect(sTex, sRect, dTex, dRect, m_fxaa.ps, true);
}
bool GSDeviceOGL::CompileShadeBoostProgram()
{
const auto shader = Host::ReadResourceFileToString("shaders/opengl/shadeboost.glsl");
if (!shader.has_value())
{
Host::ReportErrorAsync("GS", "Failed to read shaders/opengl/shadeboost.glsl.");
return false;
}
const std::string ps(GetShaderSource("ps_main", GL_FRAGMENT_SHADER, *shader));
if (!m_shader_cache.GetProgram(&m_shadeboost.ps, m_convert.vs, {}, ps))
return false;
m_shadeboost.ps.RegisterUniform("params");
m_shadeboost.ps.SetName("Shadeboost pipe");
return true;
}
void GSDeviceOGL::DoShadeBoost(GSTexture* sTex, GSTexture* dTex, const float params[4])
{
GL_PUSH("DoShadeBoost");
@@ -1651,6 +1911,140 @@ bool GSDeviceOGL::DoCAS(GSTexture* sTex, GSTexture* dTex, bool sharpen_only, con
return true;
}
bool GSDeviceOGL::CreateImGuiProgram()
{
std::optional<std::string> glsl = Host::ReadResourceFileToString("shaders/opengl/imgui.glsl");
if (!glsl.has_value())
{
Console.Error("Failed to read imgui.glsl");
return false;
}
std::optional<GL::Program> prog = m_shader_cache.GetProgram(
GetShaderSource("vs_main", GL_VERTEX_SHADER, glsl.value()), {},
GetShaderSource("ps_main", GL_FRAGMENT_SHADER, glsl.value()));
if (!prog.has_value())
{
Console.Error("Failed to compile imgui shaders");
return false;
}
prog->SetName("ImGui Render");
prog->RegisterUniform("ProjMtx");
m_imgui.ps = std::move(prog.value());
// Need a different VAO because the layout doesn't match GS
glGenVertexArrays(1, &m_imgui.vao);
glBindVertexArray(m_imgui.vao);
m_vertex_stream_buffer->Bind();
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glEnableVertexAttribArray(2);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, pos));
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, uv));
glVertexAttribPointer(2, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, col));
glBindVertexArray(m_vertex_array_object);
return true;
}
void GSDeviceOGL::RenderImGui()
{
ImGui::Render();
const ImDrawData* draw_data = ImGui::GetDrawData();
if (draw_data->CmdListsCount == 0)
return;
constexpr float L = 0.0f;
const float R = static_cast<float>(m_window_info.surface_width);
constexpr float T = 0.0f;
const float B = static_cast<float>(m_window_info.surface_height);
// clang-format off
const float ortho_projection[4][4] =
{
{ 2.0f/(R-L), 0.0f, 0.0f, 0.0f },
{ 0.0f, 2.0f/(T-B), 0.0f, 0.0f },
{ 0.0f, 0.0f, -1.0f, 0.0f },
{ (R+L)/(L-R), (T+B)/(B-T), 0.0f, 1.0f },
};
// clang-format on
m_imgui.ps.Bind();
m_imgui.ps.UniformMatrix4fv(0, &ortho_projection[0][0]);
glBindVertexArray(m_imgui.vao);
OMSetBlendState(true, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_FUNC_ADD);
OMSetDepthStencilState(m_convert.dss);
PSSetSamplerState(m_convert.ln);
// Need to flip the scissor due to lower-left on the window framebuffer
GSVector4i last_scissor = GSVector4i::xffffffff();
// Render command lists
for (int n = 0; n < draw_data->CmdListsCount; n++)
{
const ImDrawList* cmd_list = draw_data->CmdLists[n];
// Different vertex format.
u32 vertex_start;
{
const u32 size = static_cast<u32>(cmd_list->VtxBuffer.Size) * sizeof(ImDrawVert);
auto res = m_vertex_stream_buffer->Map(sizeof(ImDrawVert), size);
std::memcpy(res.pointer, cmd_list->VtxBuffer.Data, size);
vertex_start = res.index_aligned;
m_vertex_stream_buffer->Unmap(size);
}
// Bit awkward, because this is using 16-bit indices, not 32-bit.
u32 index_start;
{
static_assert(sizeof(ImDrawIdx) == sizeof(u16));
const u32 size = static_cast<u32>(cmd_list->IdxBuffer.Size) * sizeof(ImDrawIdx);
auto res = m_index_stream_buffer->Map(sizeof(u16), size);
index_start = res.index_aligned;
std::memcpy(res.pointer, cmd_list->IdxBuffer.Data, size);
m_index_stream_buffer->Unmap(size);
m_index_stream_buffer->Bind();
}
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
{
const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
pxAssert(!pcmd->UserCallback);
const GSVector4 clip = GSVector4::load<false>(&pcmd->ClipRect);
if ((clip.zwzw() <= clip.xyxy()).mask() != 0)
continue;
// Apply scissor/clipping rectangle (Y is inverted in OpenGL)
const GSVector4i iclip = GSVector4i(clip);
if (!last_scissor.eq(iclip))
{
glScissor(iclip.x, m_window_info.surface_height - iclip.w, iclip.width(), iclip.height());
last_scissor = iclip;
}
// Since we don't have the GSTexture...
const GLuint texture_id = static_cast<GLuint>(reinterpret_cast<uintptr_t>(pcmd->GetTexID()));
if (GLState::tex_unit[0] != texture_id)
{
GLState::tex_unit[0] = texture_id;
glBindTextureUnit(0, texture_id);
}
glDrawElementsBaseVertex(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, GL_UNSIGNED_SHORT,
(void*)(intptr_t)((pcmd->IdxOffset + index_start) * sizeof(ImDrawIdx)), pcmd->VtxOffset + vertex_start);
}
g_perfmon.Put(GSPerfMon::DrawCalls, cmd_list->CmdBuffer.Size);
}
glBindVertexArray(m_vertex_array_object);
glScissor(GLState::scissor.x, GLState::scissor.y, GLState::scissor.width(), GLState::scissor.height());
}
void GSDeviceOGL::OMAttachRt(GSTextureOGL* rt)
{
if (rt)
@@ -1780,21 +2174,29 @@ void GSDeviceOGL::OMSetRenderTargets(GSTexture* rt, GSTexture* ds, const GSVecto
else
OMAttachDs();
const GSVector2i size = rt ? rt->GetSize() : ds ? ds->GetSize() : GLState::viewport;
if (GLState::viewport != size)
if (rt || ds)
{
GLState::viewport = size;
// FIXME ViewportIndexedf or ViewportIndexedfv (GL4.1)
glViewportIndexedf(0, 0, 0, GLfloat(size.x), GLfloat(size.y));
const GSVector2i size = rt ? rt->GetSize() : ds->GetSize();
SetViewport(size);
SetScissor(scissor ? *scissor : GSVector4i::loadh(size));
}
}
const GSVector4i r = scissor ? *scissor : GSVector4i(size).zwxy();
if (!GLState::scissor.eq(r))
void GSDeviceOGL::SetViewport(const GSVector2i& viewport)
{
if (GLState::viewport != viewport)
{
GLState::scissor = r;
// FIXME ScissorIndexedv (GL4.1)
glScissorIndexed(0, r.x, r.y, r.width(), r.height());
GLState::viewport = viewport;
glViewport(0, 0, viewport.x, viewport.y);
}
}
void GSDeviceOGL::SetScissor(const GSVector4i& scissor)
{
if (!GLState::scissor.eq(scissor))
{
GLState::scissor = scissor;
glScissor(scissor.x, scissor.y, scissor.width(), scissor.height());
}
}
@@ -1841,16 +2243,6 @@ void GSDeviceOGL::SetupOM(OMDepthStencilSelector dssel)
OMSetDepthStencilState(m_om_dss[dssel.key]);
}
static GSDeviceOGL::VSSelector convertSel(const GSHWDrawConfig::VSSelector sel, const GSHWDrawConfig::Topology topology)
{
// Mali requires gl_PointSize written when rasterizing points. The spec seems to suggest this is okay.
GSDeviceOGL::VSSelector out;
out.int_fst = !sel.fst;
out.iip = sel.iip;
out.point_size = sel.point_size || (GLLoader::is_gles && topology == GSHWDrawConfig::Topology::Point);
return out;
}
// clang-format off
static constexpr std::array<GLenum, 16> s_gl_blend_factors = { {
GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR, GL_DST_COLOR, GL_ONE_MINUS_DST_COLOR,
@@ -1964,22 +2356,13 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
}
ProgramSelector psel;
psel.vs = convertSel(config.vs, config.topology);
psel.vs = config.vs;
psel.ps.key_hi = config.ps.key_hi;
psel.ps.key_lo = config.ps.key_lo;
psel.gs.key = 0;
psel.pad = 0;
if (config.gs.expand)
{
psel.gs.iip = config.gs.iip;
switch (config.gs.topology)
{
case GSHWDrawConfig::GSTopology::Point: psel.gs.point = 1; break;
case GSHWDrawConfig::GSTopology::Line: psel.gs.line = 1; break;
case GSHWDrawConfig::GSTopology::Sprite: psel.gs.sprite = 1; break;
case GSHWDrawConfig::GSTopology::Triangle: ASSERT(0); break;
}
}
psel.gs.key = config.gs.key;
SetupPipeline(psel);
@@ -2174,7 +2557,7 @@ void GSDeviceOGL::SendHWDraw(const GSHWDrawConfig& config, bool needs_barrier)
}
// Note: used as a callback of DebugMessageCallback. Don't change the signature
void GSDeviceOGL::DebugOutputToFile(GLenum gl_source, GLenum gl_type, GLuint id, GLenum gl_severity, GLsizei gl_length, const GLchar* gl_message, const void* userParam)
void GSDeviceOGL::DebugMessageCallback(GLenum gl_source, GLenum gl_type, GLuint id, GLenum gl_severity, GLsizei gl_length, const GLchar* gl_message, const void* userParam)
{
std::string message(gl_message, gl_length >= 0 ? gl_length : strlen(gl_message));
std::string type, severity, source;
@@ -2230,7 +2613,7 @@ GL::StreamBuffer* GSDeviceOGL::GetTextureUploadBuffer()
void GSDeviceOGL::PushDebugGroup(const char* fmt, ...)
{
#ifdef ENABLE_OGL_DEBUG
if (!glPushDebugGroup)
if (!glPushDebugGroup || !GSConfig.UseDebugDevice)
return;
std::va_list ap;
@@ -2245,7 +2628,7 @@ void GSDeviceOGL::PushDebugGroup(const char* fmt, ...)
void GSDeviceOGL::PopDebugGroup()
{
#ifdef ENABLE_OGL_DEBUG
if (!glPopDebugGroup)
if (!glPopDebugGroup || !GSConfig.UseDebugDevice)
return;
glPopDebugGroup();
@@ -2255,7 +2638,7 @@ void GSDeviceOGL::PopDebugGroup()
void GSDeviceOGL::InsertDebugMessage(DebugMessageCategory category, const char* fmt, ...)
{
#ifdef ENABLE_OGL_DEBUG
if (!glDebugMessageInsert)
if (!glDebugMessageInsert || !GSConfig.UseDebugDevice)
return;
GLenum type, id, severity;
+87 -86
View File
@@ -120,60 +120,8 @@ public:
class GSDeviceOGL final : public GSDevice
{
public:
struct VSSelector
{
union
{
struct
{
u8 int_fst : 1;
u8 iip : 1;
u8 point_size : 1;
u8 _free : 5;
};
u8 key;
};
VSSelector()
: key(0)
{
}
VSSelector(u8 k)
: key(k)
{
}
};
struct GSSelector
{
union
{
struct
{
u8 sprite : 1;
u8 point : 1;
u8 line : 1;
u8 iip : 1;
u8 _free : 4;
};
u8 key;
};
operator u32() const { return key; }
GSSelector()
: key(0)
{
}
GSSelector(u8 k)
: key(k)
{
}
};
using VSSelector = GSHWDrawConfig::VSSelector;
using GSSelector = GSHWDrawConfig::GSSelector;
using PSSelector = GSHWDrawConfig::PSSelector;
using PSSamplerSelector = GSHWDrawConfig::SamplerSelector;
using OMDepthStencilSelector = GSHWDrawConfig::DepthStencilSelector;
@@ -202,8 +150,9 @@ public:
};
private:
std::string m_shader_tfx_vgs;
std::string m_shader_tfx_fs;
static constexpr u8 NUM_TIMESTAMP_QUERIES = 5;
std::unique_ptr<GL::Context> m_gl_context;
GLuint m_fbo = 0; // frame buffer container
GLuint m_fbo_read = 0; // frame buffer container only for reading
@@ -262,6 +211,12 @@ private:
GL::Program sharpen_ps;
} m_cas;
struct
{
GL::Program ps;
GLuint vao = 0;
} m_imgui;
GLuint m_ps_ss[1 << 8];
GSDepthStencilOGL* m_om_dss[1 << 5] = {};
std::unordered_map<ProgramSelector, GL::Program, ProgramSelectorHash> m_programs;
@@ -269,21 +224,47 @@ private:
GLuint m_palette_ss = 0;
std::array<GLuint, NUM_TIMESTAMP_QUERIES> m_timestamp_queries = {};
float m_accumulated_gpu_time = 0.0f;
u8 m_read_timestamp_query = 0;
u8 m_write_timestamp_query = 0;
u8 m_waiting_timestamp_queries = 0;
bool m_timestamp_query_started = false;
bool m_gpu_timing_enabled = false;
GSHWDrawConfig::VSConstantBuffer m_vs_cb_cache;
GSHWDrawConfig::PSConstantBuffer m_ps_cb_cache;
GSTexture* CreateSurface(GSTexture::Type type, int width, int height, int levels, GSTexture::Format format) final;
std::string m_shader_tfx_vgs;
std::string m_shader_tfx_fs;
void DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex, GSVector4* dRect, const GSRegPMODE& PMODE, const GSRegEXTBUF& EXTBUF, const GSVector4& c, const bool linear) final;
void DoInterlace(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, ShaderInterlace shader, bool linear, const InterlaceConstantBuffer& cb) final;
void DoFXAA(GSTexture* sTex, GSTexture* dTex) final;
void DoShadeBoost(GSTexture* sTex, GSTexture* dTex, const float params[4]) final;
void SetSwapInterval();
void DestroyResources();
void CreateTimestampQueries();
void DestroyTimestampQueries();
void PopTimestampQuery();
void KickTimestampQuery();
GSTexture* CreateSurface(GSTexture::Type type, int width, int height, int levels, GSTexture::Format format) override;
void DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex, GSVector4* dRect, const GSRegPMODE& PMODE, const GSRegEXTBUF& EXTBUF, const GSVector4& c, const bool linear) override;
void DoInterlace(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, ShaderInterlace shader, bool linear, const InterlaceConstantBuffer& cb) override;
bool CompileFXAAProgram();
void DoFXAA(GSTexture* sTex, GSTexture* dTex) override;
bool CompileShadeBoostProgram();
void DoShadeBoost(GSTexture* sTex, GSTexture* dTex, const float params[4]) override;
bool CreateCASPrograms();
bool DoCAS(GSTexture* sTex, GSTexture* dTex, bool sharpen_only, const std::array<u32, NUM_CAS_CONSTANTS>& constants) final;
bool DoCAS(GSTexture* sTex, GSTexture* dTex, bool sharpen_only, const std::array<u32, NUM_CAS_CONSTANTS>& constants) override;
void OMAttachRt(GSTextureOGL* rt = NULL);
void OMAttachDs(GSTextureOGL* ds = NULL);
bool CreateImGuiProgram();
void RenderImGui();
void OMAttachRt(GSTextureOGL* rt = nullptr);
void OMAttachDs(GSTextureOGL* ds = nullptr);
void OMSetFBO(GLuint fbo);
void DrawStretchRect(const GSVector4& sRect, const GSVector4& dRect, const GSVector2i& ds);
@@ -295,53 +276,70 @@ public:
__fi static GSDeviceOGL* GetInstance() { return static_cast<GSDeviceOGL*>(g_gs_device.get()); }
// Used by OpenGL, so the same calling convention is required.
static void APIENTRY DebugOutputToFile(GLenum gl_source, GLenum gl_type, GLuint id, GLenum gl_severity, GLsizei gl_length, const GLchar* gl_message, const void* userParam);
static void APIENTRY DebugMessageCallback(GLenum gl_source, GLenum gl_type, GLuint id, GLenum gl_severity, GLsizei gl_length, const GLchar* gl_message, const void* userParam);
static GL::StreamBuffer* GetTextureUploadBuffer();
__fi u32 GetFBORead() const { return m_fbo_read; }
__fi u32 GetFBOWrite() const { return m_fbo_write; }
bool Create() override;
RenderAPI GetRenderAPI() const override;
bool HasSurface() const override;
void ResetAPIState() override;
void RestoreAPIState() override;
bool Create(const WindowInfo& wi, VsyncMode vsync) override;
void Destroy() override;
bool ChangeWindow(const WindowInfo& new_wi) override;
void ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale) override;
bool SupportsExclusiveFullscreen() const override;
bool IsExclusiveFullscreen() override;
bool SetExclusiveFullscreen(bool fullscreen, u32 width, u32 height, float refresh_rate) override;
void DestroySurface() override;
std::string GetDriverInfo() const override;
void SetVSync(VsyncMode mode) override;
PresentResult BeginPresent(bool frame_skip) override;
void EndPresent() override;
bool SetGPUTimingEnabled(bool enabled) override;
float GetAndResetAccumulatedGPUTime() override;
void DrawPrimitive();
void DrawIndexedPrimitive();
void DrawIndexedPrimitive(int offset, int count);
void ClearRenderTarget(GSTexture* t, const GSVector4& c) final;
void ClearRenderTarget(GSTexture* t, u32 c) final;
void InvalidateRenderTarget(GSTexture* t) final;
void ClearDepth(GSTexture* t) final;
void ClearStencil(GSTexture* t, u8 c) final;
void ClearRenderTarget(GSTexture* t, const GSVector4& c) override;
void ClearRenderTarget(GSTexture* t, u32 c) override;
void InvalidateRenderTarget(GSTexture* t) override;
void ClearDepth(GSTexture* t) override;
void ClearStencil(GSTexture* t, u8 c) override;
std::unique_ptr<GSDownloadTexture> CreateDownloadTexture(u32 width, u32 height, GSTexture::Format format) override;
GSTexture* InitPrimDateTexture(GSTexture* rt, const GSVector4i& area, bool datm);
void CopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r, u32 destX, u32 destY) final;
void CopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r, u32 destX, u32 destY) override;
void PushDebugGroup(const char* fmt, ...) final;
void PopDebugGroup() final;
void InsertDebugMessage(DebugMessageCategory category, const char* fmt, ...) final;
void PushDebugGroup(const char* fmt, ...) override;
void PopDebugGroup() override;
void InsertDebugMessage(DebugMessageCategory category, const char* fmt, ...) override;
// BlitRect *does* mess with GL state, be sure to re-bind.
void BlitRect(GSTexture* sTex, const GSVector4i& r, const GSVector2i& dsize, bool at_origin, bool linear);
void StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, ShaderConvert shader = ShaderConvert::COPY, bool linear = true) final;
void StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, ShaderConvert shader = ShaderConvert::COPY, bool linear = true) override;
void StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, const GL::Program& ps, bool linear = true);
void StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, bool red, bool green, bool blue, bool alpha) final;
void StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, bool red, bool green, bool blue, bool alpha) override;
void StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, const GL::Program& ps, bool alpha_blend, OMColorMaskSelector cms, bool linear = true);
void PresentRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, PresentShader shader, float shaderTime, bool linear) final;
void UpdateCLUTTexture(GSTexture* sTex, float sScale, u32 offsetX, u32 offsetY, GSTexture* dTex, u32 dOffset, u32 dSize) final;
void ConvertToIndexedTexture(GSTexture* sTex, float sScale, u32 offsetX, u32 offsetY, u32 SBW, u32 SPSM, GSTexture* dTex, u32 DBW, u32 DPSM) final;
void PresentRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, PresentShader shader, float shaderTime, bool linear) override;
void UpdateCLUTTexture(GSTexture* sTex, float sScale, u32 offsetX, u32 offsetY, GSTexture* dTex, u32 dOffset, u32 dSize) override;
void ConvertToIndexedTexture(GSTexture* sTex, float sScale, u32 offsetX, u32 offsetY, u32 SBW, u32 SPSM, GSTexture* dTex, u32 DBW, u32 DPSM) override;
void DrawMultiStretchRects(const MultiStretchRect* rects, u32 num_rects, GSTexture* dTex, ShaderConvert shader) final;
void DrawMultiStretchRects(const MultiStretchRect* rects, u32 num_rects, GSTexture* dTex, ShaderConvert shader) override;
void DoMultiStretchRects(const MultiStretchRect* rects, u32 num_rects, const GSVector2& ds);
void RenderHW(GSHWDrawConfig& config) final;
void RenderHW(GSHWDrawConfig& config) override;
void SendHWDraw(const GSHWDrawConfig& config, bool needs_barrier);
void SetupDATE(GSTexture* rt, GSTexture* ds, const GSVertexPT1* vertices, bool datm);
@@ -353,14 +351,17 @@ public:
void PSSetShaderResource(int i, GSTexture* sr);
void PSSetShaderResources(GSTexture* sr0, GSTexture* sr1);
void PSSetSamplerState(GLuint ss);
void ClearSamplerCache() final;
void ClearSamplerCache() override;
void OMSetDepthStencilState(GSDepthStencilOGL* dss);
void OMSetBlendState(bool enable = false, GLenum src_factor = GL_ONE, GLenum dst_factor = GL_ZERO, GLenum op = GL_FUNC_ADD, bool is_constant = false, u8 constant = 0);
void OMSetRenderTargets(GSTexture* rt, GSTexture* ds, const GSVector4i* scissor = NULL);
void OMSetRenderTargets(GSTexture* rt, GSTexture* ds, const GSVector4i* scissor = nullptr);
void OMSetColorMaskState(OMColorMaskSelector sel = OMColorMaskSelector());
void OMUnbindTexture(GSTextureOGL* tex);
void SetViewport(const GSVector2i& viewport);
void SetScissor(const GSVector4i& scissor);
bool CreateTextureFX();
std::string GetShaderSource(const std::string_view& entry, GLenum type, const std::string_view& glsl_h_code,
const std::string_view& macro_sel = std::string_view());
+534 -45
View File
@@ -14,6 +14,15 @@
*/
#include "PrecompiledHeader.h"
#include "GS/GS.h"
#include "GS/Renderers/Vulkan/GSDeviceVK.h"
#include "GS/GSGL.h"
#include "GS/GSPerfMon.h"
#include "GS/GSUtil.h"
#include "Host.h"
#include "ShaderCacheVersion.h"
#include "common/Vulkan/Builders.h"
#include "common/Vulkan/Context.h"
#include "common/Vulkan/ShaderCache.h"
@@ -22,13 +31,9 @@
#include "common/Align.h"
#include "common/Path.h"
#include "common/ScopedGuard.h"
#include "GS.h"
#include "GSDeviceVK.h"
#include "GS/GSGL.h"
#include "GS/GSPerfMon.h"
#include "GS/GSUtil.h"
#include "Host.h"
#include "HostDisplay.h"
#include "imgui.h"
#include <sstream>
#include <limits>
@@ -55,6 +60,16 @@ static VkAttachmentLoadOp GetLoadOpForTexture(GSTextureVK* tex)
// clang-format on
}
static VkPresentModeKHR GetPreferredPresentModeForVsyncMode(VsyncMode mode)
{
if (mode == VsyncMode::On)
return VK_PRESENT_MODE_FIFO_KHR;
else if (mode == VsyncMode::Adaptive)
return VK_PRESENT_MODE_FIFO_RELAXED_KHR;
else
return VK_PRESENT_MODE_IMMEDIATE_KHR;
}
GSDeviceVK::GSDeviceVK()
{
#ifdef ENABLE_OGL_DEBUG
@@ -64,12 +79,123 @@ GSDeviceVK::GSDeviceVK()
std::memset(&m_pipeline_selector, 0, sizeof(m_pipeline_selector));
}
GSDeviceVK::~GSDeviceVK() {}
bool GSDeviceVK::Create()
GSDeviceVK::~GSDeviceVK()
{
if (!GSDevice::Create() || !CheckFeatures())
pxAssert(!g_vulkan_context);
}
void GSDeviceVK::GetAdaptersAndFullscreenModes(
std::vector<std::string>* adapters, std::vector<std::string>* fullscreen_modes)
{
std::vector<Vulkan::SwapChain::FullscreenModeInfo> fsmodes;
if (g_vulkan_context)
{
if (adapters)
*adapters = Vulkan::Context::EnumerateGPUNames(g_vulkan_context->GetVulkanInstance());
if (fullscreen_modes)
{
fsmodes = Vulkan::SwapChain::GetSurfaceFullscreenModes(
g_vulkan_context->GetVulkanInstance(), g_vulkan_context->GetPhysicalDevice(), WindowInfo());
}
}
else
{
if (Vulkan::LoadVulkanLibrary())
{
ScopedGuard lib_guard([]() { Vulkan::UnloadVulkanLibrary(); });
const VkInstance instance = Vulkan::Context::CreateVulkanInstance(nullptr, false, false);
if (instance != VK_NULL_HANDLE)
{
if (Vulkan::LoadVulkanInstanceFunctions(instance))
*adapters = Vulkan::Context::EnumerateGPUNames(instance);
vkDestroyInstance(instance, nullptr);
}
}
}
if (!fsmodes.empty())
{
fullscreen_modes->clear();
fullscreen_modes->reserve(fsmodes.size());
for (const Vulkan::SwapChain::FullscreenModeInfo& fmi : fsmodes)
{
fullscreen_modes->push_back(GetFullscreenModeString(fmi.width, fmi.height, fmi.refresh_rate));
}
}
}
bool GSDeviceVK::IsSuitableDefaultRenderer()
{
std::vector<std::string> adapters;
GetAdaptersAndFullscreenModes(&adapters, nullptr);
if (adapters.empty())
{
// No adapters, not gonna be able to use VK.
return false;
}
// Check the first GPU, should be enough.
const std::string& name = adapters.front();
Console.WriteLn(fmt::format("Using Vulkan GPU '{}' for automatic renderer check.", name));
// Any software rendering (LLVMpipe, SwiftShader).
if (StringUtil::StartsWithNoCase(name, "llvmpipe") ||
StringUtil::StartsWithNoCase(name, "SwiftShader"))
{
Console.WriteLn(Color_StrongOrange, "Not using Vulkan for software renderer.");
return false;
}
// For Intel, OpenGL usually ends up faster on Linux, because of fbfetch.
// Plus, the Ivy Bridge and Haswell drivers are incomplete.
if (StringUtil::StartsWithNoCase(name, "Intel"))
{
Console.WriteLn(Color_StrongOrange, "Not using Vulkan for Intel GPU.");
return false;
}
Console.WriteLn(Color_StrongGreen, "Allowing Vulkan as default renderer.");
return true;
}
RenderAPI GSDeviceVK::GetRenderAPI() const
{
return RenderAPI::Vulkan;
}
bool GSDeviceVK::HasSurface() const
{
return static_cast<bool>(m_swap_chain);
}
bool GSDeviceVK::Create(const WindowInfo& wi, VsyncMode vsync)
{
if (!GSDevice::Create(wi, vsync))
return false;
WindowInfo local_wi(wi);
const bool debug_device = GSConfig.UseDebugDevice;
if (!Vulkan::Context::Create(GSConfig.Adapter, &local_wi, &m_swap_chain, GetPreferredPresentModeForVsyncMode(vsync),
!GSConfig.DisableThreadedPresentation, debug_device, debug_device))
{
Console.Error("Failed to create Vulkan context");
return false;
}
Vulkan::ShaderCache::Create(GSConfig.DisableShaderCache ? std::string_view() : std::string_view(EmuFolders::Cache),
SHADER_CACHE_VERSION, GSConfig.UseDebugDevice);
// NOTE: This is assigned afterwards, because some platforms can modify the window info (e.g. Metal).
m_window_info = m_swap_chain ? m_swap_chain->GetWindowInfo() : local_wi;
if (!CheckFeatures())
{
Console.Error("Your GPU does not support the required Vulkan features.");
return false;
}
{
std::optional<std::string> shader = Host::ReadResourceFileToString("shaders/vulkan/tfx.glsl");
@@ -119,31 +245,253 @@ bool GSDeviceVK::Create()
CompileCASPipelines();
if (!CompileImGuiPipeline())
return false;
InitializeState();
return true;
}
void GSDeviceVK::Destroy()
{
if (!g_vulkan_context)
GSDevice::Destroy();
if (g_vulkan_context)
{
EndRenderPass();
ExecuteCommandBuffer(true);
DestroyResources();
g_vulkan_context->WaitForGPUIdle();
m_swap_chain.reset();
Vulkan::ShaderCache::Destroy();
Vulkan::Context::Destroy();
}
}
bool GSDeviceVK::ChangeWindow(const WindowInfo& new_wi)
{
if (new_wi.type == WindowInfo::Type::Surfaceless)
{
ExecuteCommandBuffer(true);
m_swap_chain.reset();
m_window_info = new_wi;
return true;
}
// make sure previous frames are presented
g_vulkan_context->WaitForGPUIdle();
// recreate surface in existing swap chain if it already exists
if (m_swap_chain)
{
if (m_swap_chain->RecreateSurface(new_wi))
{
m_window_info = m_swap_chain->GetWindowInfo();
return true;
}
m_swap_chain.reset();
}
WindowInfo wi_copy(new_wi);
VkSurfaceKHR surface = Vulkan::SwapChain::CreateVulkanSurface(
g_vulkan_context->GetVulkanInstance(), g_vulkan_context->GetPhysicalDevice(), &wi_copy);
if (surface == VK_NULL_HANDLE)
{
Console.Error("Failed to create new surface for swap chain");
return false;
}
m_swap_chain = Vulkan::SwapChain::Create(wi_copy, surface, GetPreferredPresentModeForVsyncMode(m_vsync_mode));
if (!m_swap_chain)
{
Console.Error("Failed to create swap chain");
Vulkan::SwapChain::DestroyVulkanSurface(g_vulkan_context->GetVulkanInstance(), &wi_copy, surface);
return false;
}
m_window_info = m_swap_chain->GetWindowInfo();
return true;
}
void GSDeviceVK::ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale)
{
if (m_swap_chain->GetWidth() == static_cast<u32>(new_window_width) &&
m_swap_chain->GetHeight() == static_cast<u32>(new_window_height))
{
// skip unnecessary resizes
m_window_info.surface_scale = new_window_scale;
return;
}
// make sure previous frames are presented
g_vulkan_context->WaitForGPUIdle();
if (!m_swap_chain->ResizeSwapChain(new_window_width, new_window_height, new_window_scale))
{
// AcquireNextImage() will fail, and we'll recreate the surface.
Console.Error("Failed to resize swap chain. Next present will fail.");
return;
}
m_window_info = m_swap_chain->GetWindowInfo();
}
bool GSDeviceVK::SupportsExclusiveFullscreen() const
{
return false;
}
bool GSDeviceVK::IsExclusiveFullscreen()
{
return false;
}
bool GSDeviceVK::SetExclusiveFullscreen(bool fullscreen, u32 width, u32 height, float refresh_rate)
{
return false;
}
void GSDeviceVK::DestroySurface()
{
g_vulkan_context->WaitForGPUIdle();
m_swap_chain.reset();
}
std::string GSDeviceVK::GetDriverInfo() const
{
std::string ret;
const u32 api_version = g_vulkan_context->GetDeviceProperties().apiVersion;
const u32 driver_version = g_vulkan_context->GetDeviceProperties().driverVersion;
if (g_vulkan_context->GetOptionalExtensions().vk_khr_driver_properties)
{
const VkPhysicalDeviceDriverProperties& props = g_vulkan_context->GetDeviceDriverProperties();
ret = StringUtil::StdStringFromFormat(
"Driver %u.%u.%u\nVulkan %u.%u.%u\nConformance Version %u.%u.%u.%u\n%s\n%s\n%s",
VK_VERSION_MAJOR(driver_version), VK_VERSION_MINOR(driver_version), VK_VERSION_PATCH(driver_version),
VK_API_VERSION_MAJOR(api_version), VK_API_VERSION_MINOR(api_version), VK_API_VERSION_PATCH(api_version),
props.conformanceVersion.major, props.conformanceVersion.minor, props.conformanceVersion.subminor,
props.conformanceVersion.patch, props.driverInfo, props.driverName,
g_vulkan_context->GetDeviceProperties().deviceName);
}
else
{
ret = StringUtil::StdStringFromFormat("Driver %u.%u.%u\nVulkan %u.%u.%u\n%s", VK_VERSION_MAJOR(driver_version),
VK_VERSION_MINOR(driver_version), VK_VERSION_PATCH(driver_version), VK_API_VERSION_MAJOR(api_version),
VK_API_VERSION_MINOR(api_version), VK_API_VERSION_PATCH(api_version),
g_vulkan_context->GetDeviceProperties().deviceName);
}
return ret;
}
void GSDeviceVK::SetVSync(VsyncMode mode)
{
if (!m_swap_chain || m_vsync_mode == mode)
return;
EndRenderPass();
ExecuteCommandBuffer(true);
DestroyResources();
GSDevice::Destroy();
// This swap chain should not be used by the current buffer, thus safe to destroy.
g_vulkan_context->WaitForGPUIdle();
m_swap_chain->SetVSync(GetPreferredPresentModeForVsyncMode(mode));
m_vsync_mode = mode;
}
void GSDeviceVK::ResetAPIState()
GSDevice::PresentResult GSDeviceVK::BeginPresent(bool frame_skip)
{
EndRenderPass();
if (frame_skip || !m_swap_chain)
return PresentResult::FrameSkipped;
// Previous frame needs to be presented before we can acquire the swap chain.
g_vulkan_context->WaitForPresentComplete();
// Check if the device was lost.
if (g_vulkan_context->CheckLastSubmitFail())
return PresentResult::DeviceLost;
VkResult res = m_swap_chain->AcquireNextImage();
if (res != VK_SUCCESS)
{
m_swap_chain->ReleaseCurrentImage();
if (res == VK_SUBOPTIMAL_KHR || res == VK_ERROR_OUT_OF_DATE_KHR)
{
ResizeWindow(0, 0, m_window_info.surface_scale);
res = m_swap_chain->AcquireNextImage();
}
else if (res == VK_ERROR_SURFACE_LOST_KHR)
{
Console.Warning("Surface lost, attempting to recreate");
if (!m_swap_chain->RecreateSurface(m_window_info))
{
Console.Error("Failed to recreate surface after loss");
g_vulkan_context->ExecuteCommandBuffer(Vulkan::Context::WaitType::None);
return PresentResult::FrameSkipped;
}
res = m_swap_chain->AcquireNextImage();
}
// This can happen when multiple resize events happen in quick succession.
// In this case, just wait until the next frame to try again.
if (res != VK_SUCCESS && res != VK_SUBOPTIMAL_KHR)
{
// Still submit the command buffer, otherwise we'll end up with several frames waiting.
LOG_VULKAN_ERROR(res, "vkAcquireNextImageKHR() failed: ");
g_vulkan_context->ExecuteCommandBuffer(Vulkan::Context::WaitType::None);
return PresentResult::FrameSkipped;
}
}
VkCommandBuffer cmdbuffer = g_vulkan_context->GetCurrentCommandBuffer();
// Swap chain images start in undefined
Vulkan::Texture& swap_chain_texture = m_swap_chain->GetCurrentTexture();
swap_chain_texture.OverrideImageLayout(VK_IMAGE_LAYOUT_UNDEFINED);
swap_chain_texture.TransitionToLayout(cmdbuffer, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
const VkClearValue clear_value = {{{0.0f, 0.0f, 0.0f, 1.0f}}};
const VkRenderPassBeginInfo rp = {VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, nullptr,
m_swap_chain->GetClearRenderPass(), m_swap_chain->GetCurrentFramebuffer(),
{{0, 0}, {swap_chain_texture.GetWidth(), swap_chain_texture.GetHeight()}}, 1u, &clear_value};
vkCmdBeginRenderPass(g_vulkan_context->GetCurrentCommandBuffer(), &rp, VK_SUBPASS_CONTENTS_INLINE);
const VkViewport vp{0.0f, 0.0f, static_cast<float>(swap_chain_texture.GetWidth()),
static_cast<float>(swap_chain_texture.GetHeight()), 0.0f, 1.0f};
const VkRect2D scissor{
{0, 0}, {static_cast<u32>(swap_chain_texture.GetWidth()), static_cast<u32>(swap_chain_texture.GetHeight())}};
vkCmdSetViewport(g_vulkan_context->GetCurrentCommandBuffer(), 0, 1, &vp);
vkCmdSetScissor(g_vulkan_context->GetCurrentCommandBuffer(), 0, 1, &scissor);
return PresentResult::OK;
}
void GSDeviceVK::RestoreAPIState()
void GSDeviceVK::EndPresent()
{
RenderImGui();
VkCommandBuffer cmdbuffer = g_vulkan_context->GetCurrentCommandBuffer();
vkCmdEndRenderPass(g_vulkan_context->GetCurrentCommandBuffer());
m_swap_chain->GetCurrentTexture().TransitionToLayout(cmdbuffer, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
g_vulkan_context->SubmitCommandBuffer(m_swap_chain.get(), !m_swap_chain->IsPresentModeSynchronizing());
g_vulkan_context->MoveToNextCommandBuffer();
InvalidateCachedState();
}
bool GSDeviceVK::SetGPUTimingEnabled(bool enabled)
{
return g_vulkan_context->SetEnableGPUTiming(enabled);
}
float GSDeviceVK::GetAndResetAccumulatedGPUTime()
{
return g_vulkan_context->GetAndResetAccumulatedGPUTime();
}
#ifdef ENABLE_OGL_DEBUG
static std::array<float, 3> Palette(float phase, const std::array<float, 3>& a, const std::array<float, 3>& b,
const std::array<float, 3>& c, const std::array<float, 3>& d)
@@ -532,7 +880,7 @@ void GSDeviceVK::StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture*
int(dRect.right - dRect.left), int(dRect.bottom - dRect.top));
DoStretchRect(static_cast<GSTextureVK*>(sTex), sRect, static_cast<GSTextureVK*>(dTex), dRect,
dTex ? m_convert[static_cast<int>(shader)] : m_present[static_cast<int>(shader)], linear);
dTex ? m_convert[static_cast<int>(shader)] : m_present[static_cast<int>(shader)], linear, true);
}
void GSDeviceVK::StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, bool red,
@@ -541,8 +889,9 @@ void GSDeviceVK::StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture*
GL_PUSH("ColorCopy Red:%d Green:%d Blue:%d Alpha:%d", red, green, blue, alpha);
const u32 index = (red ? 1 : 0) | (green ? 2 : 0) | (blue ? 4 : 0) | (alpha ? 8 : 0);
DoStretchRect(
static_cast<GSTextureVK*>(sTex), sRect, static_cast<GSTextureVK*>(dTex), dRect, m_color_copy[index], false);
const bool allow_discard = (index == 0xf);
DoStretchRect(static_cast<GSTextureVK*>(sTex), sRect, static_cast<GSTextureVK*>(dTex), dRect, m_color_copy[index],
false, allow_discard);
}
void GSDeviceVK::PresentRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect,
@@ -550,12 +899,12 @@ void GSDeviceVK::PresentRect(GSTexture* sTex, const GSVector4& sRect, GSTexture*
{
DisplayConstantBuffer cb;
cb.SetSource(sRect, sTex->GetSize());
cb.SetTarget(dRect, dTex ? dTex->GetSize() : GSVector2i(g_host_display->GetWindowWidth(), g_host_display->GetWindowHeight()));
cb.SetTarget(dRect, dTex ? dTex->GetSize() : GSVector2i(GetWindowWidth(), GetWindowHeight()));
cb.SetTime(shaderTime);
SetUtilityPushConstants(&cb, sizeof(cb));
DoStretchRect(static_cast<GSTextureVK*>(sTex), sRect, static_cast<GSTextureVK*>(dTex), dRect,
m_present[static_cast<int>(shader)], linear);
m_present[static_cast<int>(shader)], linear, true);
}
void GSDeviceVK::DrawMultiStretchRects(
@@ -712,7 +1061,7 @@ void GSDeviceVK::BeginRenderPassForStretchRect(
}
void GSDeviceVK::DoStretchRect(GSTextureVK* sTex, const GSVector4& sRect, GSTextureVK* dTex, const GSVector4& dRect,
VkPipeline pipeline, bool linear)
VkPipeline pipeline, bool linear, bool allow_discard)
{
if (sTex->GetLayout() != VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
{
@@ -727,7 +1076,7 @@ void GSDeviceVK::DoStretchRect(GSTextureVK* sTex, const GSVector4& sRect, GSText
const bool is_present = (!dTex);
const bool depth = (dTex && dTex->GetType() == GSTexture::Type::DepthStencil);
const GSVector2i size(
is_present ? GSVector2i(g_host_display->GetWindowWidth(), g_host_display->GetWindowHeight()) : dTex->GetSize());
is_present ? GSVector2i(GetWindowWidth(), GetWindowHeight()) : dTex->GetSize());
const GSVector4i dtex_rc(0, 0, size.x, size.y);
const GSVector4i dst_rc(GSVector4i(dRect).rintersect(dtex_rc));
@@ -745,7 +1094,7 @@ void GSDeviceVK::DoStretchRect(GSTextureVK* sTex, const GSVector4& sRect, GSText
}
if (!is_present && !InRenderPass())
BeginRenderPassForStretchRect(dTex, dtex_rc, dst_rc);
BeginRenderPassForStretchRect(dTex, dtex_rc, dst_rc, allow_discard);
DrawStretchRect(sRect, dRect, size);
}
@@ -813,7 +1162,7 @@ void GSDeviceVK::UpdateCLUTTexture(GSTexture* sTex, float sScale, u32 offsetX, u
const GSVector4 dRect(0, 0, dSize, 1);
const ShaderConvert shader = (dSize == 16) ? ShaderConvert::CLUT_4 : ShaderConvert::CLUT_8;
DoStretchRect(static_cast<GSTextureVK*>(sTex), GSVector4::zero(), static_cast<GSTextureVK*>(dTex), dRect,
m_convert[static_cast<int>(shader)], false);
m_convert[static_cast<int>(shader)], false, true);
}
void GSDeviceVK::ConvertToIndexedTexture(GSTexture* sTex, float sScale, u32 offsetX, u32 offsetY, u32 SBW, u32 SPSM, GSTexture* dTex, u32 DBW, u32 DPSM)
@@ -833,7 +1182,7 @@ void GSDeviceVK::ConvertToIndexedTexture(GSTexture* sTex, float sScale, u32 offs
const ShaderConvert shader = ShaderConvert::RGBA_TO_8I;
const GSVector4 dRect(0, 0, dTex->GetWidth(), dTex->GetHeight());
DoStretchRect(static_cast<GSTextureVK*>(sTex), GSVector4::zero(), static_cast<GSTextureVK*>(dTex), dRect,
m_convert[static_cast<int>(shader)], false);
m_convert[static_cast<int>(shader)], false, true);
}
void GSDeviceVK::DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex, GSVector4* dRect,
@@ -1006,7 +1355,6 @@ void GSDeviceVK::IASetVertexBuffer(const void* vertex, size_t stride, size_t cou
m_vertex.start = m_vertex_stream_buffer.GetCurrentOffset() / stride;
m_vertex.count = count;
SetVertexBuffer(m_vertex_stream_buffer.GetBuffer(), 0);
GSVector4i::storent(m_vertex_stream_buffer.GetCurrentHostPointer(), vertex, count * stride);
m_vertex_stream_buffer.CommitMemory(size);
@@ -1024,7 +1372,6 @@ void GSDeviceVK::IASetIndexBuffer(const void* index, size_t count)
m_index.start = m_index_stream_buffer.GetCurrentOffset() / sizeof(u32);
m_index.count = count;
SetIndexBuffer(m_index_stream_buffer.GetBuffer(), 0, VK_INDEX_TYPE_UINT32);
std::memcpy(m_index_stream_buffer.GetCurrentHostPointer(), index, size);
m_index_stream_buffer.CommitMemory(size);
@@ -1241,6 +1588,9 @@ bool GSDeviceVK::CreateBuffers()
return false;
}
SetVertexBuffer(m_vertex_stream_buffer.GetBuffer(), 0);
SetIndexBuffer(m_index_stream_buffer.GetBuffer(), 0, VK_INDEX_TYPE_UINT32);
return true;
}
@@ -1383,16 +1733,6 @@ bool GSDeviceVK::CreateRenderPasses()
bool GSDeviceVK::CompileConvertPipelines()
{
// we may not have a swap chain if running in headless mode.
Vulkan::SwapChain* swapchain = static_cast<Vulkan::SwapChain*>(g_host_display->GetSurface());
if (swapchain)
{
m_swap_chain_render_pass =
g_vulkan_context->GetRenderPass(swapchain->GetSurfaceFormat().format, VK_FORMAT_UNDEFINED);
if (!m_swap_chain_render_pass)
return false;
}
std::optional<std::string> shader = Host::ReadResourceFileToString("shaders/vulkan/convert.glsl");
if (!shader)
{
@@ -1582,11 +1922,10 @@ bool GSDeviceVK::CompileConvertPipelines()
bool GSDeviceVK::CompilePresentPipelines()
{
// we may not have a swap chain if running in headless mode.
Vulkan::SwapChain* swapchain = static_cast<Vulkan::SwapChain*>(g_host_display->GetSurface());
m_swap_chain_render_pass = swapchain ?
swapchain->GetClearRenderPass() :
g_vulkan_context->GetRenderPass(VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_UNDEFINED);
if (!m_swap_chain_render_pass)
m_swap_chain_render_pass = m_swap_chain ?
m_swap_chain->GetClearRenderPass() :
g_vulkan_context->GetRenderPass(VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_UNDEFINED);
if (m_swap_chain_render_pass == VK_NULL_HANDLE)
return false;
std::optional<std::string> shader = Host::ReadResourceFileToString("shaders/vulkan/present.glsl");
@@ -1879,6 +2218,155 @@ bool GSDeviceVK::CompileCASPipelines()
return true;
}
bool GSDeviceVK::CompileImGuiPipeline()
{
const std::optional<std::string> glsl = Host::ReadResourceFileToString("shaders/vulkan/imgui.glsl");
if (!glsl.has_value())
{
Console.Error("Failed to read imgui.glsl");
return false;
}
VkShaderModule vs = GetUtilityVertexShader(glsl.value(), "vs_main");
if (vs == VK_NULL_HANDLE)
{
Console.Error("Failed to compile ImGui vertex shader");
return false;
}
ScopedGuard vs_guard([&vs]() { Vulkan::Util::SafeDestroyShaderModule(vs); });
VkShaderModule ps = GetUtilityFragmentShader(glsl.value(), "ps_main");
if (ps == VK_NULL_HANDLE)
{
Console.Error("Failed to compile ImGui pixel shader");
return false;
}
ScopedGuard ps_guard([&ps]() { Vulkan::Util::SafeDestroyShaderModule(ps); });
Vulkan::GraphicsPipelineBuilder gpb;
gpb.SetPipelineLayout(m_utility_pipeline_layout);
gpb.SetRenderPass(m_swap_chain_render_pass, 0);
gpb.AddVertexBuffer(0, sizeof(ImDrawVert), VK_VERTEX_INPUT_RATE_VERTEX);
gpb.AddVertexAttribute(0, 0, VK_FORMAT_R32G32_SFLOAT, offsetof(ImDrawVert, pos));
gpb.AddVertexAttribute(1, 0, VK_FORMAT_R32G32_SFLOAT, offsetof(ImDrawVert, uv));
gpb.AddVertexAttribute(2, 0, VK_FORMAT_R8G8B8A8_UNORM, offsetof(ImDrawVert, col));
gpb.SetPrimitiveTopology(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST);
gpb.SetVertexShader(vs);
gpb.SetFragmentShader(ps);
gpb.SetNoCullRasterizationState();
gpb.SetNoDepthTestState();
gpb.SetBlendAttachment(0, true, VK_BLEND_FACTOR_SRC_ALPHA, VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA, VK_BLEND_OP_ADD,
VK_BLEND_FACTOR_ONE, VK_BLEND_FACTOR_ZERO, VK_BLEND_OP_ADD);
gpb.SetDynamicViewportAndScissorState();
gpb.AddDynamicState(VK_DYNAMIC_STATE_BLEND_CONSTANTS);
m_imgui_pipeline = gpb.Create(g_vulkan_context->GetDevice(), g_vulkan_shader_cache->GetPipelineCache(), false);
if (!m_imgui_pipeline)
{
Console.Error("Failed to compile ImGui pipeline");
return false;
}
Vulkan::Util::SetObjectName(g_vulkan_context->GetDevice(), m_imgui_pipeline, "ImGui pipeline");
return true;
}
void GSDeviceVK::RenderImGui()
{
ImGui::Render();
const ImDrawData* draw_data = ImGui::GetDrawData();
if (draw_data->CmdListsCount == 0)
return;
const float uniforms[2][2] = {{
2.0f / static_cast<float>(m_window_info.surface_width),
2.0f / static_cast<float>(m_window_info.surface_height),
},
{
-1.0f,
-1.0f,
}};
SetUtilityPushConstants(uniforms, sizeof(uniforms));
SetPipeline(m_imgui_pipeline);
if (m_utility_sampler != m_linear_sampler)
{
m_utility_sampler = m_linear_sampler;
m_dirty_flags |= DIRTY_FLAG_UTILITY_TEXTURE;
}
// imgui uses 16-bit indices
SetIndexBuffer(m_index_stream_buffer.GetBuffer(), 0, VK_INDEX_TYPE_UINT16);
// this is for presenting, we don't want to screw with the viewport/scissor set by display
m_dirty_flags &= ~(DIRTY_FLAG_VIEWPORT | DIRTY_FLAG_SCISSOR);
for (int n = 0; n < draw_data->CmdListsCount; n++)
{
const ImDrawList* cmd_list = draw_data->CmdLists[n];
u32 vertex_offset;
{
const u32 size = sizeof(ImDrawVert) * static_cast<u32>(cmd_list->VtxBuffer.Size);
if (!m_vertex_stream_buffer.ReserveMemory(size, sizeof(ImDrawVert)))
{
Console.Warning("Skipping ImGui draw because of no vertex buffer space");
return;
}
vertex_offset = m_vertex_stream_buffer.GetCurrentOffset() / sizeof(ImDrawVert);
std::memcpy(m_vertex_stream_buffer.GetCurrentHostPointer(), cmd_list->VtxBuffer.Data, size);
m_vertex_stream_buffer.CommitMemory(size);
}
u32 index_offset;
{
const u32 size = sizeof(ImDrawIdx) * static_cast<u32>(cmd_list->IdxBuffer.Size);
if (!m_index_stream_buffer.ReserveMemory(size, sizeof(ImDrawIdx)))
{
Console.Warning("Skipping ImGui draw because of no vertex buffer space");
return;
}
index_offset = m_index_stream_buffer.GetCurrentOffset() / sizeof(ImDrawIdx);
std::memcpy(m_index_stream_buffer.GetCurrentHostPointer(), cmd_list->IdxBuffer.Data, size);
m_index_stream_buffer.CommitMemory(size);
}
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
{
const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
pxAssert(!pcmd->UserCallback);
const GSVector4 clip = GSVector4::load<false>(&pcmd->ClipRect);
if ((clip.zwzw() <= clip.xyxy()).mask() != 0)
continue;
SetScissor(GSVector4i(clip));
// Since we don't have the GSTexture...
Vulkan::Texture* tex = static_cast<Vulkan::Texture*>(pcmd->GetTexID());
if (m_utility_texture != tex)
{
m_utility_texture = tex;
m_dirty_flags |= DIRTY_FLAG_UTILITY_TEXTURE;
}
if (ApplyUtilityState())
{
vkCmdDrawIndexed(g_vulkan_context->GetCurrentCommandBuffer(), pcmd->ElemCount, 1,
index_offset + pcmd->IdxOffset, vertex_offset + pcmd->VtxOffset, 0);
}
}
g_perfmon.Put(GSPerfMon::DrawCalls, cmd_list->CmdBuffer.Size);
}
// normal draws use 32-bit indices
SetIndexBuffer(m_index_stream_buffer.GetBuffer(), 0, VK_INDEX_TYPE_UINT32);
}
bool GSDeviceVK::DoCAS(GSTexture* sTex, GSTexture* dTex, bool sharpen_only, const std::array<u32, NUM_CAS_CONSTANTS>& constants)
{
EndRenderPass();
@@ -1993,6 +2481,7 @@ void GSDeviceVK::DestroyResources()
Vulkan::Util::SafeDestroyPipeline(it);
Vulkan::Util::SafeDestroyPipelineLayout(m_cas_pipeline_layout);
Vulkan::Util::SafeDestroyDescriptorSetLayout(m_cas_ds_layout);
Vulkan::Util::SafeDestroyPipeline(m_imgui_pipeline);
for (auto& it : m_samplers)
Vulkan::Util::SafeDestroySampler(it.second);
+36 -4
View File
@@ -24,6 +24,11 @@
#include <array>
#include <unordered_map>
namespace Vulkan
{
class SwapChain;
}
class GSDeviceVK final : public GSDevice
{
public:
@@ -104,6 +109,8 @@ public:
};
private:
std::unique_ptr<Vulkan::SwapChain> m_swap_chain;
VkDescriptorSetLayout m_utility_ds_layout = VK_NULL_HANDLE;
VkPipelineLayout m_utility_pipeline_layout = VK_NULL_HANDLE;
@@ -153,6 +160,7 @@ private:
VkDescriptorSetLayout m_cas_ds_layout = VK_NULL_HANDLE;
VkPipelineLayout m_cas_pipeline_layout = VK_NULL_HANDLE;
std::array<VkPipeline, NUM_CAS_PIPELINES> m_cas_pipelines = {};
VkPipeline m_imgui_pipeline = VK_NULL_HANDLE;
GSHWDrawConfig::VSConstantBuffer m_vs_cb_cache;
GSHWDrawConfig::PSConstantBuffer m_ps_cb_cache;
@@ -196,6 +204,9 @@ private:
bool CompilePostProcessingPipelines();
bool CompileCASPipelines();
bool CompileImGuiPipeline();
void RenderImGui();
void DestroyResources();
public:
@@ -204,6 +215,11 @@ public:
__fi static GSDeviceVK* GetInstance() { return static_cast<GSDeviceVK*>(g_gs_device.get()); }
static void GetAdaptersAndFullscreenModes(std::vector<std::string>* adapters, std::vector<std::string>* fullscreen_modes);
/// Returns true if Vulkan is suitable as a default for the devices in the system.
static bool IsSuitableDefaultRenderer();
__fi VkRenderPass GetTFXRenderPass(bool rt, bool ds, bool hdr, DATE_RENDER_PASS date, bool fbl, bool dsp,
VkAttachmentLoadOp rt_op, VkAttachmentLoadOp ds_op) const
{
@@ -212,11 +228,27 @@ public:
__fi VkSampler GetPointSampler() const { return m_point_sampler; }
__fi VkSampler GetLinearSampler() const { return m_linear_sampler; }
bool Create() override;
RenderAPI GetRenderAPI() const override;
bool HasSurface() const override;
bool Create(const WindowInfo& wi, VsyncMode vsync) override;
void Destroy() override;
void ResetAPIState() override;
void RestoreAPIState() override;
bool ChangeWindow(const WindowInfo& new_wi) override;
void ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale) override;
bool SupportsExclusiveFullscreen() const override;
bool IsExclusiveFullscreen() override;
bool SetExclusiveFullscreen(bool fullscreen, u32 width, u32 height, float refresh_rate) override;
void DestroySurface() override;
std::string GetDriverInfo() const override;
void SetVSync(VsyncMode mode) override;
PresentResult BeginPresent(bool frame_skip) override;
void EndPresent() override;
bool SetGPUTimingEnabled(bool enabled) override;
float GetAndResetAccumulatedGPUTime() override;
void PushDebugGroup(const char* fmt, ...) override;
void PopDebugGroup() override;
@@ -248,7 +280,7 @@ public:
void BeginRenderPassForStretchRect(
GSTextureVK* dTex, const GSVector4i& dtex_rc, const GSVector4i& dst_rc, bool allow_discard = true);
void DoStretchRect(GSTextureVK* sTex, const GSVector4& sRect, GSTextureVK* dTex, const GSVector4& dRect,
VkPipeline pipeline, bool linear);
VkPipeline pipeline, bool linear, bool allow_discard);
void DrawStretchRect(const GSVector4& sRect, const GSVector4& dRect, const GSVector2i& ds);
void BlitRect(GSTexture* sTex, const GSVector4i& sRect, u32 sLevel, GSTexture* dTex, const GSVector4i& dRect,
+2 -6
View File
@@ -24,6 +24,8 @@
#include <optional>
#include <vector>
enum class VsyncMode;
namespace Host
{
/// Typical durations for OSD messages.
@@ -87,10 +89,4 @@ namespace Host
/// Requests shut down of the current virtual machine.
void RequestVMShutdown(bool allow_confirm, bool allow_save_state, bool default_save_state);
/// Returns true if the hosting application is currently fullscreen.
bool IsFullscreen();
/// Alters fullscreen state of hosting application.
void SetFullscreen(bool enabled);
} // namespace Host
-190
View File
@@ -1,190 +0,0 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2021 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "PrecompiledHeader.h"
#include "HostDisplay.h"
#include "VMManager.h"
#include "common/Assertions.h"
#include "common/Console.h"
#include "common/StringUtil.h"
#include <cerrno>
#include <cmath>
#include <cstring>
#include <chrono>
#include <thread>
#include <vector>
std::unique_ptr<HostDisplay> g_host_display;
HostDisplayTexture::~HostDisplayTexture() = default;
HostDisplay::~HostDisplay() = default;
const char* HostDisplay::RenderAPIToString(RenderAPI api)
{
switch (api)
{
#define CASE(x) case RenderAPI::x: return #x
CASE(None);
CASE(D3D11);
CASE(D3D12);
CASE(Metal);
CASE(Vulkan);
CASE(OpenGL);
CASE(OpenGLES);
#undef CASE
default:
return "Unknown";
}
}
bool HostDisplay::UsesLowerLeftOrigin() const
{
const RenderAPI api = GetRenderAPI();
return (api == RenderAPI::OpenGL || api == RenderAPI::OpenGLES);
}
bool HostDisplay::GetHostRefreshRate(float* refresh_rate)
{
if (m_window_info.surface_refresh_rate > 0.0f)
{
*refresh_rate = m_window_info.surface_refresh_rate;
return true;
}
return WindowInfo::QueryRefreshRateForWindow(m_window_info, refresh_rate);
}
bool HostDisplay::SetGPUTimingEnabled(bool enabled)
{
return false;
}
float HostDisplay::GetAndResetAccumulatedGPUTime()
{
return 0.0f;
}
bool HostDisplay::ParseFullscreenMode(const std::string_view& mode, u32* width, u32* height, float* refresh_rate)
{
if (!mode.empty())
{
std::string_view::size_type sep1 = mode.find('x');
if (sep1 != std::string_view::npos)
{
std::optional<u32> owidth = StringUtil::FromChars<u32>(mode.substr(0, sep1));
sep1++;
while (sep1 < mode.length() && std::isspace(mode[sep1]))
sep1++;
if (owidth.has_value() && sep1 < mode.length())
{
std::string_view::size_type sep2 = mode.find('@', sep1);
if (sep2 != std::string_view::npos)
{
std::optional<u32> oheight = StringUtil::FromChars<u32>(mode.substr(sep1, sep2 - sep1));
sep2++;
while (sep2 < mode.length() && std::isspace(mode[sep2]))
sep2++;
if (oheight.has_value() && sep2 < mode.length())
{
std::optional<float> orefresh_rate = StringUtil::FromChars<float>(mode.substr(sep2));
if (orefresh_rate.has_value())
{
*width = owidth.value();
*height = oheight.value();
*refresh_rate = orefresh_rate.value();
return true;
}
}
}
}
}
}
*width = 0;
*height = 0;
*refresh_rate = 0;
return false;
}
std::string HostDisplay::GetFullscreenModeString(u32 width, u32 height, float refresh_rate)
{
return StringUtil::StdStringFromFormat("%u x %u @ %f hz", width, height, refresh_rate);
}
VsyncMode Host::GetEffectiveVSyncMode()
{
const bool has_vm = VMManager::GetState() != VMState::Shutdown;
// Force vsync off when not running at 100% speed.
if (has_vm && EmuConfig.GS.LimitScalar != 1.0f)
return VsyncMode::Off;
// Otherwise use the config setting.
return EmuConfig.GS.VsyncEnable;
}
#ifdef ENABLE_OPENGL
#include "Frontend/OpenGLHostDisplay.h"
#endif
#ifdef ENABLE_VULKAN
#include "Frontend/VulkanHostDisplay.h"
#endif
#ifdef _WIN32
#include "Frontend/D3D11HostDisplay.h"
#include "Frontend/D3D12HostDisplay.h"
#endif
#include "GS/Renderers/Metal/GSMetalCPPAccessible.h"
std::unique_ptr<HostDisplay> HostDisplay::CreateForAPI(RenderAPI api)
{
switch (api)
{
#ifdef _WIN32
case RenderAPI::D3D11:
return std::make_unique<D3D11HostDisplay>();
case RenderAPI::D3D12:
return std::make_unique<D3D12HostDisplay>();
#endif
#ifdef __APPLE__
case RenderAPI::Metal:
return std::unique_ptr<HostDisplay>(MakeMetalHostDisplay());
#endif
#ifdef ENABLE_OPENGL
case RenderAPI::OpenGL:
case RenderAPI::OpenGLES:
return std::make_unique<OpenGLHostDisplay>();
#endif
#ifdef ENABLE_VULKAN
case RenderAPI::Vulkan:
return std::make_unique<VulkanHostDisplay>();
#endif
default:
Console.Error("Unknown render API %u", static_cast<unsigned>(api));
return {};
}
}
-206
View File
@@ -1,206 +0,0 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2021 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "common/Pcsx2Defs.h"
#include "common/WindowInfo.h"
#include <memory>
#include <string>
#include <string_view>
#include <tuple>
#include <vector>
#include "Host.h"
#include "Config.h"
enum class RenderAPI
{
None,
D3D11,
Metal,
D3D12,
Vulkan,
OpenGL,
OpenGLES
};
/// An abstracted RGBA8 texture.
class HostDisplayTexture
{
public:
virtual ~HostDisplayTexture();
virtual void* GetHandle() const = 0;
virtual u32 GetWidth() const = 0;
virtual u32 GetHeight() const = 0;
};
/// Interface to the frontend's renderer.
class HostDisplay
{
public:
enum class Alignment
{
LeftOrTop,
Center,
RightOrBottom
};
enum class PresentResult
{
OK,
FrameSkipped,
DeviceLost
};
struct AdapterAndModeList
{
std::vector<std::string> adapter_names;
std::vector<std::string> fullscreen_modes;
};
virtual ~HostDisplay();
/// Returns a string representing the specified API.
static const char* RenderAPIToString(RenderAPI api);
/// Creates a display for the specified API.
static std::unique_ptr<HostDisplay> CreateForAPI(RenderAPI api);
/// Parses a fullscreen mode into its components (width * height @ refresh hz)
static bool ParseFullscreenMode(const std::string_view& mode, u32* width, u32* height, float* refresh_rate);
/// Converts a fullscreen mode to a string.
static std::string GetFullscreenModeString(u32 width, u32 height, float refresh_rate);
__fi const WindowInfo& GetWindowInfo() const { return m_window_info; }
__fi s32 GetWindowWidth() const { return static_cast<s32>(m_window_info.surface_width); }
__fi s32 GetWindowHeight() const { return static_cast<s32>(m_window_info.surface_height); }
__fi float GetWindowScale() const { return m_window_info.surface_scale; }
__fi VsyncMode GetVsyncMode() const { return m_vsync_mode; }
/// Changes the alignment for this display (screen positioning).
__fi Alignment GetDisplayAlignment() const { return m_display_alignment; }
__fi void SetDisplayAlignment(Alignment alignment) { m_display_alignment = alignment; }
virtual RenderAPI GetRenderAPI() const = 0;
virtual void* GetDevice() const = 0;
virtual void* GetContext() const = 0;
virtual void* GetSurface() const = 0;
virtual bool HasDevice() const = 0;
virtual bool HasSurface() const = 0;
/// Creates the rendering/GPU device. This should be called on the thread which owns the window.
virtual bool CreateDevice(const WindowInfo& wi, VsyncMode vsync) = 0;
/// Fully initializes the rendering device. This should be called on the GS thread.
virtual bool SetupDevice() = 0;
/// Sets the device for the current thread. Only needed for OpenGL.
virtual bool MakeCurrent() = 0;
/// Clears the device for the current thread. Only needed for OpenGL.
virtual bool DoneCurrent() = 0;
/// Destroys the surface we're currently drawing to.
virtual void DestroySurface() = 0;
/// Switches to a new window/surface.
virtual bool ChangeWindow(const WindowInfo& wi) = 0;
/// Call when the window size changes externally to recreate any resources.
virtual void ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale) = 0;
/// Returns true if exclusive fullscreen is supported.
virtual bool SupportsFullscreen() const = 0;
/// Returns true if exclusive fullscreen is active.
virtual bool IsFullscreen() = 0;
/// Attempts to switch to the specified mode in exclusive fullscreen.
virtual bool SetFullscreen(bool fullscreen, u32 width, u32 height, float refresh_rate) = 0;
virtual AdapterAndModeList GetAdapterAndModeList() = 0;
virtual std::string GetDriverInfo() const = 0;
/// Creates an abstracted RGBA8 texture. If dynamic, the texture can be updated with UpdateTexture() below.
virtual std::unique_ptr<HostDisplayTexture> CreateTexture(u32 width, u32 height, const void* data, u32 data_stride, bool dynamic = false) = 0;
virtual void UpdateTexture(HostDisplayTexture* texture, u32 x, u32 y, u32 width, u32 height, const void* data, u32 data_stride) = 0;
/// Returns false if the window was completely occluded. If frame_skip is set, the frame won't be
/// displayed, but the GPU command queue will still be flushed.
virtual PresentResult BeginPresent(bool frame_skip) = 0;
/// Presents the frame to the display, and renders OSD elements.
virtual void EndPresent() = 0;
/// Changes vsync mode for this display.
virtual void SetVSync(VsyncMode mode) = 0;
/// ImGui context management, usually called by derived classes.
virtual bool CreateImGuiContext() = 0;
virtual void DestroyImGuiContext() = 0;
virtual bool UpdateImGuiFontTexture() = 0;
/// Returns the effective refresh rate of this display.
virtual bool GetHostRefreshRate(float* refresh_rate);
/// Enables/disables GPU frame timing.
virtual bool SetGPUTimingEnabled(bool enabled);
/// Returns the amount of GPU time utilized since the last time this method was called.
virtual float GetAndResetAccumulatedGPUTime();
/// Returns true if it's an OpenGL-based renderer.
bool UsesLowerLeftOrigin() const;
protected:
WindowInfo m_window_info;
Alignment m_display_alignment = Alignment::Center;
VsyncMode m_vsync_mode = VsyncMode::Off;
};
/// Returns a pointer to the current host display abstraction. Assumes AcquireHostDisplay() has been caled.
extern std::unique_ptr<HostDisplay> g_host_display;
namespace Host
{
/// Creates the host display. This may create a new window. The API used depends on the current configuration.
bool AcquireHostDisplay(RenderAPI api, bool clear_state_on_fail);
/// Destroys the host display. This may close the display window.
void ReleaseHostDisplay(bool clear_state);
/// Returns the desired vsync mode, depending on the runtime environment.
VsyncMode GetEffectiveVSyncMode();
/// Returns false if the window was completely occluded. If frame_skip is set, the frame won't be
/// displayed, but the GPU command queue will still be flushed.
HostDisplay::PresentResult BeginPresentFrame(bool frame_skip);
/// Presents the frame to the display, and renders OSD elements.
void EndPresentFrame();
/// Called on the MTGS thread when a resize request is received.
void ResizeHostDisplay(u32 new_window_width, u32 new_window_height, float new_window_scale);
/// Called on the MTGS thread when a request to update the display is received.
/// This could be a fullscreen transition, for example.
void UpdateHostDisplay();
} // namespace Host
+2 -1
View File
@@ -305,9 +305,10 @@ std::vector<std::pair<std::string, std::string>> INISettingsInterface::GetKeyVal
using KVEntry = std::pair<const char*, Entry>;
std::vector<KVEntry> entries;
std::vector<std::pair<std::string, std::string>> output;
std::list<Entry> keys, values;
std::list<Entry> keys;
if (m_ini.GetAllKeys(section, keys))
{
std::list<Entry> values;
for (Entry& key : keys)
{
if (!m_ini.GetAllValues(section, key.pItem, values)) // [[unlikely]]
+6 -11
View File
@@ -27,7 +27,6 @@
#include "Elfheader.h"
#include "Host.h"
#include "HostDisplay.h"
#include "IconsFontAwesome5.h"
#include "VMManager.h"
@@ -276,7 +275,7 @@ bool SysMtgsThread::TryOpenGS()
if (!GSopen(EmuConfig.GS, EmuConfig.GS.Renderer, RingBuffer.Regs))
return false;
GSsetGameCRC(ElfCRC);
GSSetGameCRC(ElfCRC);
return true;
}
@@ -511,7 +510,7 @@ void SysMtgsThread::MainLoop()
break;
case GS_RINGTYPE_CRC:
GSsetGameCRC(tag.data[0]);
GSSetGameCRC(tag.data[0]);
break;
case GS_RINGTYPE_INIT_AND_READ_FIFO:
@@ -915,7 +914,7 @@ void SysMtgsThread::ApplySettings()
RunOnGSThread([opts = EmuConfig.GS]() {
GSUpdateConfig(opts);
g_host_display->SetVSync(Host::GetEffectiveVSyncMode());
GSSetVSyncMode(Host::GetEffectiveVSyncMode());
});
// We need to synchronize the thread when changing any settings when the download mode
@@ -929,9 +928,7 @@ void SysMtgsThread::ResizeDisplayWindow(int width, int height, float scale)
{
pxAssertRel(IsOpen(), "MTGS is running");
RunOnGSThread([width, height, scale]() {
GSResetAPIState();
Host::ResizeHostDisplay(width, height, scale);
GSRestoreAPIState();
GSResizeDisplayWindow(width, height, scale);
// If we're paused, re-present the current frame at the new window size.
if (VMManager::GetState() == VMState::Paused)
@@ -943,9 +940,7 @@ void SysMtgsThread::UpdateDisplayWindow()
{
pxAssertRel(IsOpen(), "MTGS is running");
RunOnGSThread([]() {
GSResetAPIState();
Host::UpdateHostDisplay();
GSRestoreAPIState();
GSUpdateDisplayWindow();
// If we're paused, re-present the current frame at the new window size.
if (VMManager::GetState() == VMState::Paused)
@@ -959,7 +954,7 @@ void SysMtgsThread::SetVSyncMode(VsyncMode mode)
RunOnGSThread([mode]() {
Console.WriteLn("Vsync is %s", mode == VsyncMode::Off ? "OFF" : (mode == VsyncMode::Adaptive ? "ADAPTIVE" : "ON"));
g_host_display->SetVSync(mode);
GSSetVSyncMode(mode);
});
}
+1 -1
View File
@@ -72,7 +72,7 @@ void PADshutdown()
{
}
s32 PADopen(const WindowInfo& wi)
s32 PADopen()
{
g_key_status.Init();
return 0;
+1 -1
View File
@@ -30,7 +30,7 @@ enum class GenericInputBinding : u8;
s32 PADinit();
void PADshutdown();
s32 PADopen(const WindowInfo& wi);
s32 PADopen();
void PADclose();
s32 PADsetSlot(u8 port, u8 slot);
s32 PADfreeze(FreezeAction mode, freezeData* data);
-1
View File
@@ -22,7 +22,6 @@
#include "common/StringUtil.h"
#include "Config.h"
#include "GS.h"
#include "HostDisplay.h"
#include "CDVD/CDVDcommon.h"
#include "MemoryCardFile.h"
#include "USB/USB.h"
+1 -1
View File
@@ -91,7 +91,7 @@ static void PostLoadPrep()
CBreakPoints::SetSkipFirst(BREAKPOINT_EE, 0);
CBreakPoints::SetSkipFirst(BREAKPOINT_IOP, 0);
UpdateVSyncRate();
UpdateVSyncRate(true);
}
// --------------------------------------------------------------------------------------
-1
View File
@@ -20,7 +20,6 @@
#include "USB/qemu-usb/USBinternal.h"
#include "USB/USB.h"
#include "GS/GS.h"
#include "HostDisplay.h"
#include "StateWrapper.h"
#include "VMManager.h"
+17 -5
View File
@@ -39,7 +39,7 @@
#include "GameDatabase.h"
#include "GS.h"
#include "GSDumpReplayer.h"
#include "HostDisplay.h"
#include "Host.h"
#include "HostSettings.h"
#include "INISettingsInterface.h"
#include "IopBios.h"
@@ -993,7 +993,7 @@ bool VMManager::Initialize(VMBootParameters boot_params)
ScopedGuard close_spu2(&SPU2::Close);
Console.WriteLn("Opening PAD...");
if (PADinit() != 0 || PADopen(g_host_display->GetWindowInfo()) != 0)
if (PADinit() != 0 || PADopen() != 0)
{
Host::ReportErrorAsync("Startup Error", "Failed to initialize PAD.");
return false;
@@ -1196,7 +1196,7 @@ void VMManager::Reset()
SysClearExecutionCache();
memBindConditionalHandlers();
UpdateVSyncRate();
UpdateVSyncRate(true);
frameLimitReset();
cpuReset();
@@ -1591,6 +1591,18 @@ void VMManager::SetPaused(bool paused)
SetState(paused ? VMState::Paused : VMState::Running);
}
VsyncMode Host::GetEffectiveVSyncMode()
{
const bool has_vm = VMManager::GetState() != VMState::Shutdown;
// Force vsync off when not running at 100% speed.
if (has_vm && EmuConfig.GS.LimitScalar != 1.0f)
return VsyncMode::Off;
// Otherwise use the config setting.
return EmuConfig.GS.VsyncEnable;
}
const std::string& VMManager::Internal::GetElfOverride()
{
return s_elf_override;
@@ -1701,7 +1713,7 @@ void VMManager::CheckForGSConfigChanges(const Pcsx2Config& old_config)
EmuConfig.LimiterMode = GetInitialLimiterMode();
gsUpdateFrequency(EmuConfig);
UpdateVSyncRate();
UpdateVSyncRate(true);
frameLimitReset();
GetMTGS().ApplySettings();
}
@@ -1713,7 +1725,7 @@ void VMManager::CheckForFramerateConfigChanges(const Pcsx2Config& old_config)
Console.WriteLn("Updating frame rate configuration");
gsUpdateFrequency(EmuConfig);
UpdateVSyncRate();
UpdateVSyncRate(true);
frameLimitReset();
}
+4 -18
View File
@@ -194,27 +194,19 @@
<ClCompile Include="DEV9\Win32\tap-win32.cpp" />
<ClCompile Include="Frontend\CommonHost.cpp" />
<ClCompile Include="Frontend\CommonHotkeys.cpp" />
<ClCompile Include="Frontend\D3D11HostDisplay.cpp" />
<ClCompile Include="Frontend\D3D12HostDisplay.cpp" />
<ClCompile Include="Frontend\DInputSource.cpp" />
<ClCompile Include="Frontend\FullscreenUI.cpp" />
<ClCompile Include="Frontend\GameList.cpp" />
<ClCompile Include="Frontend\ImGuiFullscreen.cpp" />
<ClCompile Include="Frontend\ImGuiManager.cpp" />
<ClCompile Include="Frontend\ImGuiOverlays.cpp" />
<ClCompile Include="Frontend\imgui_impl_dx11.cpp" />
<ClCompile Include="Frontend\imgui_impl_dx12.cpp" />
<ClCompile Include="Frontend\imgui_impl_opengl3.cpp" />
<ClCompile Include="Frontend\imgui_impl_vulkan.cpp" />
<ClCompile Include="INISettingsInterface.cpp" />
<ClCompile Include="Frontend\InputManager.cpp" />
<ClCompile Include="Frontend\InputSource.cpp" />
<ClCompile Include="Frontend\LayeredSettingsInterface.cpp" />
<ClCompile Include="Frontend\LogSink.cpp" />
<ClCompile Include="Frontend\OpenGLHostDisplay.cpp" />
<ClCompile Include="Frontend\Achievements.cpp" />
<ClCompile Include="Frontend\SDLInputSource.cpp" />
<ClCompile Include="Frontend\VulkanHostDisplay.cpp" />
<ClCompile Include="Frontend\XInputSource.cpp" />
<ClCompile Include="GameDatabase.cpp" />
<ClCompile Include="Gif_Logger.cpp" />
@@ -228,7 +220,6 @@
<ClCompile Include="GS\Renderers\Vulkan\GSDeviceVK.cpp" />
<ClCompile Include="GS\Renderers\Vulkan\GSTextureVK.cpp" />
<ClCompile Include="Host.cpp" />
<ClCompile Include="HostDisplay.cpp" />
<ClCompile Include="Frontend\HostSettings.cpp" />
<ClCompile Include="IopGte.cpp" />
<ClCompile Include="MemoryCardProtocol.cpp" />
@@ -543,28 +534,20 @@
<ClInclude Include="DEV9\Win32\pcap_io_win32_funcs.h" />
<ClInclude Include="DEV9\Win32\tap.h" />
<ClInclude Include="Frontend\CommonHost.h" />
<ClInclude Include="Frontend\D3D11HostDisplay.h" />
<ClInclude Include="Frontend\D3D12HostDisplay.h" />
<ClInclude Include="Frontend\DInputSource.h" />
<ClInclude Include="Frontend\FullscreenUI.h" />
<ClInclude Include="Frontend\GameList.h" />
<ClInclude Include="Frontend\ImGuiFullscreen.h" />
<ClInclude Include="Frontend\ImGuiManager.h" />
<ClInclude Include="Frontend\ImGuiOverlays.h" />
<ClInclude Include="Frontend\imgui_impl_dx11.h" />
<ClInclude Include="Frontend\imgui_impl_dx12.h" />
<ClInclude Include="Frontend\imgui_impl_opengl3.h" />
<ClInclude Include="Frontend\imgui_impl_vulkan.h" />
<ClInclude Include="GS\Renderers\HW\GSHwHack.h" />
<ClInclude Include="INISettingsInterface.h" />
<ClInclude Include="Frontend\InputManager.h" />
<ClInclude Include="Frontend\InputSource.h" />
<ClInclude Include="Frontend\LayeredSettingsInterface.h" />
<ClInclude Include="Frontend\LogSink.h" />
<ClInclude Include="Frontend\OpenGLHostDisplay.h" />
<ClInclude Include="Frontend\Achievements.h" />
<ClInclude Include="Frontend\SDLInputSource.h" />
<ClInclude Include="Frontend\VulkanHostDisplay.h" />
<ClInclude Include="Frontend\XInputSource.h" />
<ClInclude Include="GameDatabase.h" />
<ClInclude Include="Gif_Unit.h" />
@@ -576,7 +559,6 @@
<ClInclude Include="GS\Renderers\Vulkan\GSDeviceVK.h" />
<ClInclude Include="GS\Renderers\Vulkan\GSTextureVK.h" />
<ClInclude Include="Host.h" />
<ClInclude Include="HostDisplay.h" />
<ClInclude Include="HostSettings.h" />
<ClInclude Include="IopGte.h" />
<ClInclude Include="IPU\mpeg2_vlc.h" />
@@ -872,6 +854,10 @@
<Project>{c0293b32-5acf-40f0-aa6c-e6da6f3bf33a}</Project>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Natvis Include="GS\Renderers\Common\GSFastList.natvis" />
<Natvis Include="GS\Renderers\HW\GSTextureCache.natvis" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets" />
</Project>
+8 -54
View File
@@ -1157,24 +1157,12 @@
<ClCompile Include="SPU2\SndOut_Cubeb.cpp">
<Filter>System\Ps2\SPU2</Filter>
</ClCompile>
<ClCompile Include="HostDisplay.cpp">
<Filter>Host</Filter>
</ClCompile>
<ClCompile Include="Frontend\OpenGLHostDisplay.cpp">
<Filter>Host</Filter>
</ClCompile>
<ClCompile Include="Frontend\D3D11HostDisplay.cpp">
<Filter>Host</Filter>
</ClCompile>
<ClCompile Include="Frontend\ImGuiManager.cpp">
<Filter>Host</Filter>
</ClCompile>
<ClCompile Include="Host.cpp">
<Filter>Host</Filter>
</ClCompile>
<ClCompile Include="Frontend\VulkanHostDisplay.cpp">
<Filter>Host</Filter>
</ClCompile>
<ClCompile Include="GS\Renderers\Vulkan\GSTextureVK.cpp">
<Filter>System\Ps2\GS\Renderers\Vulkan</Filter>
</ClCompile>
@@ -1235,21 +1223,6 @@
<ClCompile Include="GS\Renderers\DX12\GSDevice12.cpp">
<Filter>System\Ps2\GS\Renderers\Direct3D12</Filter>
</ClCompile>
<ClCompile Include="Frontend\D3D12HostDisplay.cpp">
<Filter>Host</Filter>
</ClCompile>
<ClCompile Include="Frontend\imgui_impl_dx11.cpp">
<Filter>Host</Filter>
</ClCompile>
<ClCompile Include="Frontend\imgui_impl_dx12.cpp">
<Filter>Host</Filter>
</ClCompile>
<ClCompile Include="Frontend\imgui_impl_opengl3.cpp">
<Filter>Host</Filter>
</ClCompile>
<ClCompile Include="Frontend\imgui_impl_vulkan.cpp">
<Filter>Host</Filter>
</ClCompile>
<ClCompile Include="Recording\InputRecording.cpp">
<Filter>Tools\Input Recording</Filter>
</ClCompile>
@@ -2074,21 +2047,9 @@
<ClInclude Include="Host.h">
<Filter>Host</Filter>
</ClInclude>
<ClInclude Include="HostDisplay.h">
<Filter>Host</Filter>
</ClInclude>
<ClInclude Include="Frontend\OpenGLHostDisplay.h">
<Filter>Host</Filter>
</ClInclude>
<ClInclude Include="Frontend\D3D11HostDisplay.h">
<Filter>Host</Filter>
</ClInclude>
<ClInclude Include="Frontend\ImGuiManager.h">
<Filter>Host</Filter>
</ClInclude>
<ClInclude Include="Frontend\VulkanHostDisplay.h">
<Filter>Host</Filter>
</ClInclude>
<ClInclude Include="GS\Renderers\Vulkan\GSTextureVK.h">
<Filter>System\Ps2\GS\Renderers\Vulkan</Filter>
</ClInclude>
@@ -2147,21 +2108,6 @@
<ClInclude Include="GS\Renderers\DX12\GSDevice12.h">
<Filter>System\Ps2\GS\Renderers\Direct3D12</Filter>
</ClInclude>
<ClInclude Include="Frontend\D3D12HostDisplay.h">
<Filter>Host</Filter>
</ClInclude>
<ClInclude Include="Frontend\imgui_impl_dx11.h">
<Filter>Host</Filter>
</ClInclude>
<ClInclude Include="Frontend\imgui_impl_dx12.h">
<Filter>Host</Filter>
</ClInclude>
<ClInclude Include="Frontend\imgui_impl_opengl3.h">
<Filter>Host</Filter>
</ClInclude>
<ClInclude Include="Frontend\imgui_impl_vulkan.h">
<Filter>Host</Filter>
</ClInclude>
<ClInclude Include="Recording\InputRecording.h">
<Filter>Tools\Input Recording</Filter>
</ClInclude>
@@ -2342,4 +2288,12 @@
<Filter>System\Ps2\Debug\rdebug</Filter>
</CustomBuildStep>
</ItemGroup>
<ItemGroup>
<Natvis Include="GS\Renderers\Common\GSFastList.natvis">
<Filter>System\Ps2\GS</Filter>
</Natvis>
<Natvis Include="GS\Renderers\HW\GSTextureCache.natvis">
<Filter>System\Ps2\GS\Renderers\Hardware</Filter>
</Natvis>
</ItemGroup>
</Project>
+1 -2
View File
@@ -1912,11 +1912,10 @@ void recompileNextInstruction(bool delayslot, bool swapped_delay_slot)
else if (COP2IsQOP(cpuRegs.code))
{
std::string disasm;
Console.Warning("Possible incorrect Q value used in COP2. If the game is broken, please report to http://github.com/pcsx2/pcsx2.");
for (u32 i = s_pCurBlockEx->startpc; i < s_nEndBlock; i += 4)
{
disasm = "";
std::string disasm = "";
disR5900Fasm(disasm, memRead32(i), i, false);
Console.Warning("%x %s%08X %s", i, i == pc - 4 ? "*" : i == p ? "=" :
" ",
+13 -21
View File
@@ -17,8 +17,8 @@
#include "pcsx2/Frontend/ImGuiManager.h"
#include "pcsx2/Frontend/InputManager.h"
#include "pcsx2/GS.h"
#include "pcsx2/GS/GS.h"
#include "pcsx2/Host.h"
#include "pcsx2/HostDisplay.h"
#include "pcsx2/HostSettings.h"
#include "pcsx2/VMManager.h"
@@ -49,7 +49,7 @@ void Host::SetDefaultUISettings(SettingsInterface& si)
std::optional<std::vector<u8>> Host::ReadResourceFile(const char* filename)
{
return std::nullopt;
return std::nullopt;
}
std::optional<std::string> Host::ReadResourceFileToString(const char* filename)
@@ -90,7 +90,7 @@ void Host::EndTextInput()
std::optional<WindowInfo> Host::GetTopLevelWindowInfo()
{
return std::nullopt;
return std::nullopt;
}
void Host::OnInputDeviceConnected(const std::string_view& identifier, const std::string_view& device_name)
@@ -105,29 +105,21 @@ void Host::SetRelativeMouseMode(bool enabled)
{
}
bool Host::AcquireHostDisplay(RenderAPI api, bool clear_state_on_fail)
std::optional<WindowInfo> Host::AcquireRenderWindow()
{
return false;
return std::nullopt;
}
void Host::ReleaseHostDisplay(bool clear_state)
std::optional<WindowInfo> Host::UpdateRenderWindow(bool recreate_window)
{
return std::nullopt;
}
void Host::ReleaseRenderWindow()
{
}
HostDisplay::PresentResult Host::BeginPresentFrame(bool frame_skip)
{
return HostDisplay::PresentResult::FrameSkipped;
}
void Host::EndPresentFrame()
{
}
void Host::ResizeHostDisplay(u32 new_window_width, u32 new_window_height, float new_window_scale)
{
}
void Host::UpdateHostDisplay()
void Host::BeginPresentFrame()
{
}
@@ -227,7 +219,7 @@ std::optional<std::string> InputManager::ConvertHostKeyboardCodeToString(u32 cod
SysMtgsThread& GetMTGS()
{
throw std::exception();
throw std::exception();
}
//////////////////////////////////////////////////////////////////////////