GS: Add Vulkan renderer

This commit is contained in:
Connor McLaughlin 2021-10-21 18:45:27 +10:00 committed by refractionpcsx2
parent c4ab6280c6
commit 1a8a5a5e8e
28 changed files with 5610 additions and 52 deletions

View File

@ -0,0 +1,412 @@
#ifndef PS_SCALE_FACTOR
#define PS_SCALE_FACTOR 1
#endif
#ifdef VERTEX_SHADER
layout(location = 0) in vec4 a_pos;
layout(location = 1) in vec2 a_tex;
layout(location = 2) in vec4 a_color;
layout(location = 0) out vec2 v_tex;
layout(location = 1) out vec4 v_color;
void main()
{
gl_Position = vec4(a_pos.x, -a_pos.y, a_pos.z, a_pos.w);
v_tex = a_tex;
v_color = a_color;
}
#endif
#ifdef FRAGMENT_SHADER
layout(location = 0) in vec2 v_tex;
layout(location = 1) in vec4 v_color;
#if defined(ps_convert_rgba8_16bits) || defined(ps_convert_float32_32bits)
layout(location = 0) out uint o_col0;
#else
layout(location = 0) out vec4 o_col0;
#endif
layout(set = 0, binding = 0) uniform sampler2D samp0;
vec4 sample_c(vec2 uv)
{
return texture(samp0, uv);
}
vec4 ps_crt(uint i)
{
vec4 mask[4] = vec4[4]
(
vec4(1, 0, 0, 0),
vec4(0, 1, 0, 0),
vec4(0, 0, 1, 0),
vec4(1, 1, 1, 0)
);
return sample_c(v_tex) * clamp((mask[i] + 0.5f), 0.0f, 1.0f);
}
vec4 ps_scanlines(uint i)
{
vec4 mask[2] =
{
vec4(1, 1, 1, 0),
vec4(0, 0, 0, 0)
};
return sample_c(v_tex) * clamp((mask[i] + 0.5f), 0.0f, 1.0f);
}
#ifdef ps_copy
void ps_copy()
{
o_col0 = sample_c(v_tex);
}
#endif
#ifdef ps_filter_transparency
void ps_filter_transparency()
{
vec4 c = sample_c(v_tex);
c.a = dot(c.rgb, vec3(0.299, 0.587, 0.114));
o_col0 = c;
}
#endif
#ifdef ps_convert_rgba8_16bits
void ps_convert_rgba8_16bits()
{
vec4 c = sample_c(v_tex);
c.a *= 256.0f / 127; // hm, 0.5 won't give us 1.0 if we just multiply with 2
uvec4 i = uvec4(c * vec4(0x001f, 0x03e0, 0x7c00, 0x8000));
o_col0 = (i.x & 0x001fu) | (i.y & 0x03e0u) | (i.z & 0x7c00u) | (i.w & 0x8000u);
}
#endif
#ifdef ps_datm1
void ps_datm1()
{
o_col0 = vec4(0, 0, 0, 0);
if(sample_c(v_tex).a < (127.5f / 255.0f)) // >= 0x80 pass
discard;
}
#endif
#ifdef ps_datm0
void ps_datm0()
{
o_col0 = vec4(0, 0, 0, 0);
if((127.5f / 255.0f) < sample_c(v_tex).a) // < 0x80 pass (== 0x80 should not pass)
discard;
}
#endif
#ifdef ps_mod256
void ps_mod256()
{
vec4 c = roundEven(sample_c(v_tex) * 255);
// We use 2 fmod to avoid negative value.
vec4 fmod1 = mod(c, 256) + 256;
vec4 fmod2 = mod(fmod1, 256);
o_col0 = fmod2 / 255.0f;
}
#endif
#ifdef ps_filter_scanlines
void ps_filter_scanlines() // scanlines
{
uvec4 p = uvec4(gl_FragCoord);
o_col0 = ps_scanlines(p.y % 2);
}
#endif
#ifdef ps_filter_diagonal
void ps_filter_diagonal() // diagonal
{
uvec4 p = uvec4(gl_FragCoord);
o_col0 = ps_crt((p.x + (p.y % 3)) % 3);
}
#endif
#ifdef ps_filter_triangular
void ps_filter_triangular() // triangular
{
uvec4 p = uvec4(gl_FragCoord);
// output.c = ps_crt(input, ((p.x + (p.y & 1) * 3) >> 1) % 3);
o_col0 = ps_crt(((p.x + ((p.y >> 1) & 1) * 3) >> 1) % 3);
}
#endif
#ifdef ps_filter_complex
void ps_filter_complex() // triangular
{
const float PI = 3.14159265359f;
vec2 texdim = vec2(textureSize(samp0, 0));
if (dFdy(v_tex.y) * texdim.y > 0.5)
o_col0 = sample_c(v_tex);
else
o_col0 = (0.9 - 0.4 * cos(2 * PI * v_tex.y * texdim.y)) * sample_c(vec2(v_tex.x, (floor(v_tex.y * texdim.y) + 0.5) / texdim.y));
}
#endif
#ifdef ps_convert_float32_32bits
void ps_convert_float32_32bits()
{
// Convert a vec32 depth texture into a 32 bits UINT texture
o_col0 = uint(exp2(32.0f) * sample_c(v_tex).r);
}
#endif
#ifdef ps_convert_float32_rgba8
void ps_convert_float32_rgba8()
{
// Convert a vec32 depth texture into a RGBA color texture
const vec4 bitSh = vec4(exp2(24.0f), exp2(16.0f), exp2(8.0f), exp2(0.0f));
const vec4 bitMsk = vec4(0.0, 1.0 / 256.0, 1.0 / 256.0, 1.0 / 256.0);
vec4 res = fract(vec4(sample_c(v_tex).rrrr) * bitSh);
o_col0 = (res - res.xxyz * bitMsk) * 256.0f / 255.0f;
}
#endif
#ifdef ps_convert_float16_rgb5a1
void ps_convert_float16_rgb5a1()
{
// Convert a vec32 (only 16 lsb) depth into a RGB5A1 color texture
const vec4 bitSh = vec4(exp2(32.0f), exp2(27.0f), exp2(22.0f), exp2(17.0f));
const uvec4 bitMsk = uvec4(0x1F, 0x1F, 0x1F, 0x1);
uvec4 color = uvec4(vec4(sample_c(v_tex).rrrr) * bitSh) & bitMsk;
o_col0 = vec4(color) / vec4(32.0f, 32.0f, 32.0f, 1.0f);
}
#endif
#ifdef ps_convert_rgba8_float32
void ps_convert_rgba8_float32()
{
// Convert a RRGBA texture into a float depth texture
// FIXME: I'm afraid of the accuracy
const vec4 bitSh = vec4(exp2(-32.0f), exp2(-24.0f), exp2(-16.0f), exp2(-8.0f)) * vec4(255.0);
gl_FragDepth = dot(sample_c(v_tex), bitSh);
}
#endif
#ifdef ps_convert_rgba8_float24
void ps_convert_rgba8_float24()
{
// Same as above but without the alpha channel (24 bits Z)
// Convert a RRGBA texture into a float depth texture
const vec3 bitSh = vec3(exp2(-32.0f), exp2(-24.0f), exp2(-16.0f)) * vec3(255.0);
gl_FragDepth = dot(sample_c(v_tex).rgb, bitSh);
}
#endif
#ifdef ps_convert_rgba8_float16
void ps_convert_rgba8_float16()
{
// Same as above but without the A/B channels (16 bits Z)
// Convert a RRGBA texture into a float depth texture
// FIXME: I'm afraid of the accuracy
const vec2 bitSh = vec2(exp2(-32.0f), exp2(-24.0f)) * vec2(255.0);
gl_FragDepth = dot(sample_c(v_tex).rg, bitSh);
}
#endif
#ifdef ps_convert_rgb5a1_float16
void ps_convert_rgb5a1_float16()
{
// Convert a RGB5A1 (saved as RGBA8) color to a 16 bit Z
// FIXME: I'm afraid of the accuracy
const vec4 bitSh = vec4(exp2(-32.0f), exp2(-27.0f), exp2(-22.0f), exp2(-17.0f));
// Trunc color to drop useless lsb
vec4 color = trunc(sample_c(v_tex) * vec4(255.0f) / vec4(8.0f, 8.0f, 8.0f, 128.0f));
gl_FragDepth = dot(vec4(color), bitSh);
}
#endif
#ifdef ps_convert_rgba_8i
void ps_convert_rgba_8i()
{
// Potential speed optimization. There is a high probability that
// game only want to extract a single channel (blue). It will allow
// to remove most of the conditional operation and yield a +2/3 fps
// boost on MGS3
//
// Hypothesis wrong in Prince of Persia ... Seriously WTF !
//#define ONLY_BLUE;
// Convert a RGBA texture into a 8 bits packed texture
// Input column: 8x2 RGBA pixels
// 0: 8 RGBA
// 1: 8 RGBA
// Output column: 16x4 Index pixels
// 0: 8 R | 8 B
// 1: 8 R | 8 B
// 2: 8 G | 8 A
// 3: 8 G | 8 A
float c;
uvec2 sel = uvec2(gl_FragCoord.xy) % uvec2(16u, 16u);
ivec2 tb = ((ivec2(gl_FragCoord.xy) & ~ivec2(15, 3)) >> 1);
int ty = tb.y | (int(gl_FragCoord.y) & 1);
int txN = tb.x | (int(gl_FragCoord.x) & 7);
int txH = tb.x | ((int(gl_FragCoord.x) + 4) & 7);
txN *= PS_SCALE_FACTOR;
txH *= PS_SCALE_FACTOR;
ty *= PS_SCALE_FACTOR;
// TODO investigate texture gather
vec4 cN = texelFetch(samp0, ivec2(txN, ty), 0);
vec4 cH = texelFetch(samp0, ivec2(txH, ty), 0);
if ((sel.y & 4u) == 0u)
{
#ifdef ONLY_BLUE
c = cN.b;
#else
// Column 0 and 2
if ((sel.y & 3u) < 2u)
{
// First 2 lines of the col
if (sel.x < 8u)
c = cN.r;
else
c = cN.b;
}
else
{
if (sel.x < 8u)
c = cH.g;
else
c = cH.a;
}
#endif
}
else
{
#ifdef ONLY_BLUE
c = cH.b;
#else
// Column 1 and 3
if ((sel.y & 3u) < 2u)
{
// First 2 lines of the col
if (sel.x < 8u)
c = cH.r;
else
c = cH.b;
}
else
{
if (sel.x < 8u)
c = cN.g;
else
c = cN.a;
}
#endif
}
o_col0 = vec4(c); // Divide by something here?
}
#endif
#ifdef ps_yuv
layout(push_constant) uniform cb10
{
int EMODA;
int EMODC;
};
void ps_yuv()
{
vec4 i = sample_c(v_tex);
vec4 o;
mat3 rgb2yuv;
rgb2yuv[0] = vec3(0.587, -0.311, -0.419);
rgb2yuv[1] = vec3(0.114, 0.500, -0.081);
rgb2yuv[2] = vec3(0.299, -0.169, 0.500);
vec3 yuv = rgb2yuv * i.gbr;
float Y = float(0xDB)/255.0f * yuv.x + float(0x10)/255.0f;
float Cr = float(0xE0)/255.0f * yuv.y + float(0x80)/255.0f;
float Cb = float(0xE0)/255.0f * yuv.z + float(0x80)/255.0f;
switch(EMODA) {
case 0:
o.a = i.a;
break;
case 1:
o.a = Y;
break;
case 2:
o.a = Y/2.0f;
break;
case 3:
o.a = 0.0f;
break;
}
switch(EMODC) {
case 0:
o.rgb = i.rgb;
break;
case 1:
o.rgb = vec3(Y);
break;
case 2:
o.rgb = vec3(Y, Cb, Cr);
break;
case 3:
o.rgb = vec3(i.a);
break;
}
o_col0 = o;
}
#endif
#if defined(ps_stencil_image_init_0) || defined(ps_stencil_image_init_1)
void main()
{
o_col0 = vec4(0x7FFFFFFF);
#ifdef ps_stencil_image_init_0
if((127.5f / 255.0f) < sample_c(v_tex).a) // < 0x80 pass (== 0x80 should not pass)
o_col0 = vec4(-1);
#endif
#ifdef ps_stencil_image_init_1
if(sample_c(v_tex).a < (127.5f / 255.0f)) // >= 0x80 pass
o_col0 = vec4(-1);
#endif
}
#endif
#endif

View File

@ -0,0 +1,65 @@
#ifdef VERTEX_SHADER
layout(location = 0) in vec4 a_pos;
layout(location = 1) in vec2 a_tex;
layout(location = 0) out vec2 v_tex;
void main()
{
gl_Position = vec4(a_pos.x, -a_pos.y, a_pos.z, a_pos.w);
v_tex = a_tex;
}
#endif
#ifdef FRAGMENT_SHADER
layout(location = 0) in vec2 v_tex;
layout(location = 0) out vec4 o_col0;
layout(push_constant) uniform cb0
{
vec2 ZrH;
float hH;
};
layout(set = 0, binding = 0) uniform sampler2D samp0;
#ifdef ps_main0
void ps_main0()
{
o_col0 = texture(samp0, v_tex);
if (fract(v_tex.y * hH) - 0.5 < 0.0)
discard;
}
#endif
#ifdef ps_main1
void ps_main1()
{
o_col0 = texture(samp0, v_tex);
if (0.5 - fract(v_tex.y * hH) < 0.0)
discard;
}
#endif
#ifdef ps_main2
void ps_main2()
{
vec4 c0 = texture(samp0, v_tex - ZrH);
vec4 c1 = texture(samp0, v_tex);
vec4 c2 = texture(samp0, v_tex + ZrH);
o_col0 = (c0 + c1 * 2.0f + c2) / 4.0f;
}
#endif
#ifdef ps_main3
void ps_main3()
{
o_col0 = texture(samp0, v_tex);
}
#endif
#endif

View File

@ -0,0 +1,43 @@
#ifdef VERTEX_SHADER
layout(location = 0) in vec4 a_pos;
layout(location = 1) in vec2 a_tex;
layout(location = 0) out vec2 v_tex;
void main()
{
gl_Position = vec4(a_pos.x, -a_pos.y, a_pos.z, a_pos.w);
v_tex = a_tex;
}
#endif
#ifdef FRAGMENT_SHADER
layout(location = 0) in vec2 v_tex;
layout(location = 0) out vec4 o_col0;
layout(push_constant) uniform cb10
{
vec4 BGColor;
};
layout(set = 0, binding = 0) uniform sampler2D samp0;
void ps_main0()
{
vec4 c = texture(samp0, v_tex);
// Note: clamping will be done by fixed unit
c.a *= 2.0f;
o_col0 = c;
}
void ps_main1()
{
vec4 c = texture(samp0, v_tex);
c.a = BGColor.a;
o_col0 = c;
}
#endif

File diff suppressed because it is too large Load Diff

View File

@ -32,7 +32,6 @@ enum : u32
MAX_COMBINED_IMAGE_SAMPLER_DESCRIPTORS_PER_FRAME = 2 * MAX_DRAW_CALLS_PER_FRAME, MAX_COMBINED_IMAGE_SAMPLER_DESCRIPTORS_PER_FRAME = 2 * MAX_DRAW_CALLS_PER_FRAME,
MAX_SAMPLED_IMAGE_DESCRIPTORS_PER_FRAME = MAX_SAMPLED_IMAGE_DESCRIPTORS_PER_FRAME =
MAX_DRAW_CALLS_PER_FRAME, // assume at least half our draws aren't going to be shuffle/blending MAX_DRAW_CALLS_PER_FRAME, // assume at least half our draws aren't going to be shuffle/blending
MAX_STORAGE_IMAGE_DESCRIPTORS_PER_FRAME = MAX_DRAW_CALLS_PER_FRAME,
MAX_INPUT_ATTACHMENT_IMAGE_DESCRIPTORS_PER_FRAME = MAX_DRAW_CALLS_PER_FRAME, MAX_INPUT_ATTACHMENT_IMAGE_DESCRIPTORS_PER_FRAME = MAX_DRAW_CALLS_PER_FRAME,
MAX_DESCRIPTOR_SETS_PER_FRAME = MAX_DRAW_CALLS_PER_FRAME * 2 MAX_DESCRIPTOR_SETS_PER_FRAME = MAX_DRAW_CALLS_PER_FRAME * 2
}; };
@ -406,7 +405,9 @@ namespace Vulkan
if (g_vulkan_context->m_debug_messenger_callback != VK_NULL_HANDLE) if (g_vulkan_context->m_debug_messenger_callback != VK_NULL_HANDLE)
g_vulkan_context->DisableDebugUtils(); g_vulkan_context->DisableDebugUtils();
vkDestroyInstance(g_vulkan_context->m_instance, nullptr); if (g_vulkan_context->m_instance != VK_NULL_HANDLE)
vkDestroyInstance(g_vulkan_context->m_instance, nullptr);
Vulkan::UnloadVulkanLibrary(); Vulkan::UnloadVulkanLibrary();
g_vulkan_context.reset(); g_vulkan_context.reset();
@ -701,8 +702,7 @@ namespace Vulkan
VkDescriptorPoolSize pool_sizes[] = { VkDescriptorPoolSize pool_sizes[] = {
{VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, MAX_COMBINED_IMAGE_SAMPLER_DESCRIPTORS_PER_FRAME}, {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, MAX_COMBINED_IMAGE_SAMPLER_DESCRIPTORS_PER_FRAME},
{VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, MAX_SAMPLED_IMAGE_DESCRIPTORS_PER_FRAME}, {VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, MAX_SAMPLED_IMAGE_DESCRIPTORS_PER_FRAME},
{VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, MAX_STORAGE_IMAGE_DESCRIPTORS_PER_FRAME}, {VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, MAX_INPUT_ATTACHMENT_IMAGE_DESCRIPTORS_PER_FRAME},
{VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, MAX_STORAGE_IMAGE_DESCRIPTORS_PER_FRAME},
}; };
VkDescriptorPoolCreateInfo pool_create_info = {VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, nullptr, 0, VkDescriptorPoolCreateInfo pool_create_info = {VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, nullptr, 0,

View File

@ -709,6 +709,17 @@ set(pcsx2GSHeaders
GS/Window/GSwxDialog.h GS/Window/GSwxDialog.h
) )
if(USE_VULKAN)
list(APPEND pcsx2GSSources
GS/Renderers/Vulkan/GSDeviceVK.cpp
GS/Renderers/Vulkan/GSTextureVK.cpp
)
list(APPEND pcsx2GSHeaders
GS/Renderers/Vulkan/GSDeviceVK.h
GS/Renderers/Vulkan/GSTextureVK.h
)
endif()
if(WIN32) if(WIN32)
list(APPEND pcsx2SPU2Sources list(APPEND pcsx2SPU2Sources
SPU2/Windows/CfgHelpers.cpp SPU2/Windows/CfgHelpers.cpp

View File

@ -108,6 +108,7 @@ enum class GSRendererType : s8
Null = 11, Null = 11,
OGL = 12, OGL = 12,
SW = 13, SW = 13,
VK = 14,
}; };
enum class GSInterlaceMode : u8 enum class GSInterlaceMode : u8

View File

@ -41,6 +41,10 @@
#include "pcsx2/HostSettings.h" #include "pcsx2/HostSettings.h"
#endif #endif
#ifdef ENABLE_VULKAN
#include "Renderers/Vulkan/GSDeviceVK.h"
#endif
#ifdef _WIN32 #ifdef _WIN32
#include "Renderers/DX11/GSDevice11.h" #include "Renderers/DX11/GSDevice11.h"
@ -154,6 +158,9 @@ static HostDisplay::RenderAPI GetAPIForRenderer(GSRendererType renderer)
#endif #endif
return HostDisplay::RenderAPI::OpenGL; return HostDisplay::RenderAPI::OpenGL;
case GSRendererType::VK:
return HostDisplay::RenderAPI::Vulkan;
#ifdef _WIN32 #ifdef _WIN32
case GSRendererType::DX11: case GSRendererType::DX11:
case GSRendererType::SW: case GSRendererType::SW:
@ -183,6 +190,12 @@ static bool DoGSOpen(GSRendererType renderer, u8* basemem)
g_gs_device = std::make_unique<GSDeviceOGL>(); g_gs_device = std::make_unique<GSDeviceOGL>();
break; break;
#ifdef ENABLE_VULKAN
case HostDisplay::RenderAPI::Vulkan:
g_gs_device = std::make_unique<GSDeviceVK>();
break;
#endif
default: default:
Console.Error("Unknown render API %u", static_cast<unsigned>(display->GetRenderAPI())); Console.Error("Unknown render API %u", static_cast<unsigned>(display->GetRenderAPI()));
return false; return false;
@ -1123,6 +1136,9 @@ void GSApp::Init()
m_gs_renderers.push_back(GSSetting(static_cast<u32>(GSRendererType::DX11), "Direct3D 11", "")); m_gs_renderers.push_back(GSSetting(static_cast<u32>(GSRendererType::DX11), "Direct3D 11", ""));
#endif #endif
m_gs_renderers.push_back(GSSetting(static_cast<u32>(GSRendererType::OGL), "OpenGL", "")); m_gs_renderers.push_back(GSSetting(static_cast<u32>(GSRendererType::OGL), "OpenGL", ""));
#ifdef ENABLE_VULKAN
m_gs_renderers.push_back(GSSetting(static_cast<u32>(GSRendererType::VK), "Vulkan", ""));
#endif
m_gs_renderers.push_back(GSSetting(static_cast<u32>(GSRendererType::SW), "Software", "")); m_gs_renderers.push_back(GSSetting(static_cast<u32>(GSRendererType::SW), "Software", ""));
// The null renderer goes last, it has use for benchmarking purposes in a release build // The null renderer goes last, it has use for benchmarking purposes in a release build
@ -1216,9 +1232,12 @@ void GSApp::Init()
// clang-format off // clang-format off
// Avoid to clutter the ini file with useless options // Avoid to clutter the ini file with useless options
#if defined(ENABLE_VULKAN) || defined(_WIN32)
m_default_configuration["Adapter"] = "";
#endif
#ifdef _WIN32 #ifdef _WIN32
// Per OS option. // Per OS option.
m_default_configuration["Adapter"] = "";
m_default_configuration["CaptureFileName"] = ""; m_default_configuration["CaptureFileName"] = "";
m_default_configuration["CaptureVideoCodecDisplayName"] = ""; m_default_configuration["CaptureVideoCodecDisplayName"] = "";
m_default_configuration["dx_break_on_severity"] = "0"; m_default_configuration["dx_break_on_severity"] = "0";

View File

@ -51,13 +51,13 @@ const char* shaderName(ShaderConvert value)
std::unique_ptr<GSDevice> g_gs_device; std::unique_ptr<GSDevice> g_gs_device;
GSDevice::GSDevice() GSDevice::GSDevice()
: m_rbswapped(false) : m_merge(NULL)
, m_merge(NULL)
, m_weavebob(NULL) , m_weavebob(NULL)
, m_blend(NULL) , m_blend(NULL)
, m_target_tmp(NULL) , m_target_tmp(NULL)
, m_current(NULL) , m_current(NULL)
, m_frame(0) , m_frame(0)
, m_rbswapped(false)
{ {
memset(&m_vertex, 0, sizeof(m_vertex)); memset(&m_vertex, 0, sizeof(m_vertex));
memset(&m_index, 0, sizeof(m_index)); memset(&m_index, 0, sizeof(m_index));
@ -104,11 +104,13 @@ void GSDevice::RestoreAPIState()
{ {
} }
GSTexture* GSDevice::FetchSurface(GSTexture::Type type, int w, int h, GSTexture::Format format, bool clear) GSTexture* GSDevice::FetchSurface(GSTexture::Type type, int w, int h, GSTexture::Format format, bool clear, bool prefer_reuse)
{ {
const GSVector2i size(w, h); const GSVector2i size(w, h);
const bool prefer_new_texture = (m_features.prefer_new_textures && type == GSTexture::Type::Texture && !prefer_reuse);
GSTexture* t = nullptr; GSTexture* t = nullptr;
auto fallback = m_pool.end();
for (auto i = m_pool.begin(); i != m_pool.end(); ++i) for (auto i = m_pool.begin(); i != m_pool.end(); ++i)
{ {
@ -118,21 +120,37 @@ GSTexture* GSDevice::FetchSurface(GSTexture::Type type, int w, int h, GSTexture:
if (t->GetType() == type && t->GetFormat() == format && t->GetSize() == size) if (t->GetType() == type && t->GetFormat() == format && t->GetSize() == size)
{ {
m_pool.erase(i); if (!prefer_new_texture)
break; {
m_pool.erase(i);
break;
}
else if (fallback == m_pool.end())
{
fallback = i;
}
} }
t = nullptr; t = nullptr;
} }
if (!t) if (!t)
t = CreateSurface(type, w, h, format); {
if (m_pool.size() >= MAX_POOLED_TEXTURES && fallback != m_pool.end())
if (!t) {
throw std::bad_alloc(); t = *fallback;
m_pool.erase(fallback);
}
else
{
t = CreateSurface(type, w, h, format);
if (!t)
throw std::bad_alloc();
}
}
t->Commit(); // Clear won't be done if the texture isn't committed. t->Commit(); // Clear won't be done if the texture isn't committed.
switch (type) switch (type)
{ {
case GSTexture::Type::RenderTarget: case GSTexture::Type::RenderTarget:
@ -195,7 +213,7 @@ void GSDevice::Recycle(GSTexture* t)
//printf("%d\n",m_pool.size()); //printf("%d\n",m_pool.size());
while (m_pool.size() > 300) while (m_pool.size() > MAX_POOLED_TEXTURES)
{ {
delete m_pool.back(); delete m_pool.back();
@ -225,32 +243,32 @@ void GSDevice::PurgePool()
GSTexture* GSDevice::CreateSparseRenderTarget(int w, int h, GSTexture::Format format, bool clear) GSTexture* GSDevice::CreateSparseRenderTarget(int w, int h, GSTexture::Format format, bool clear)
{ {
return FetchSurface(HasColorSparse() ? GSTexture::Type::SparseRenderTarget : GSTexture::Type::RenderTarget, w, h, format, clear); return FetchSurface(HasColorSparse() ? GSTexture::Type::SparseRenderTarget : GSTexture::Type::RenderTarget, w, h, format, clear, true);
} }
GSTexture* GSDevice::CreateSparseDepthStencil(int w, int h, GSTexture::Format format, bool clear) GSTexture* GSDevice::CreateSparseDepthStencil(int w, int h, GSTexture::Format format, bool clear)
{ {
return FetchSurface(HasDepthSparse() ? GSTexture::Type::SparseDepthStencil : GSTexture::Type::DepthStencil, w, h, format, clear); return FetchSurface(HasDepthSparse() ? GSTexture::Type::SparseDepthStencil : GSTexture::Type::DepthStencil, w, h, format, clear, true);
} }
GSTexture* GSDevice::CreateRenderTarget(int w, int h, GSTexture::Format format, bool clear) GSTexture* GSDevice::CreateRenderTarget(int w, int h, GSTexture::Format format, bool clear)
{ {
return FetchSurface(GSTexture::Type::RenderTarget, w, h, format, clear); return FetchSurface(GSTexture::Type::RenderTarget, w, h, format, clear, true);
} }
GSTexture* GSDevice::CreateDepthStencil(int w, int h, GSTexture::Format format, bool clear) GSTexture* GSDevice::CreateDepthStencil(int w, int h, GSTexture::Format format, bool clear)
{ {
return FetchSurface(GSTexture::Type::DepthStencil, w, h, format, clear); return FetchSurface(GSTexture::Type::DepthStencil, w, h, format, clear, true);
} }
GSTexture* GSDevice::CreateTexture(int w, int h, GSTexture::Format format) GSTexture* GSDevice::CreateTexture(int w, int h, GSTexture::Format format, bool prefer_reuse)
{ {
return FetchSurface(GSTexture::Type::Texture, w, h, format, false); return FetchSurface(GSTexture::Type::Texture, w, h, format, false, prefer_reuse);
} }
GSTexture* GSDevice::CreateOffscreen(int w, int h, GSTexture::Format format) GSTexture* GSDevice::CreateOffscreen(int w, int h, GSTexture::Format format)
{ {
return FetchSurface(GSTexture::Type::Offscreen, w, h, format, false); return FetchSurface(GSTexture::Type::Offscreen, w, h, format, false, true);
} }
GSTexture::Format GSDevice::GetDefaultTextureFormat(GSTexture::Type type) GSTexture::Format GSDevice::GetDefaultTextureFormat(GSTexture::Type type)
@ -398,7 +416,7 @@ void GSDevice::ShadeBoost()
} }
} }
bool GSDevice::ResizeTexture(GSTexture** t, GSTexture::Type type, int w, int h, bool clear) bool GSDevice::ResizeTexture(GSTexture** t, GSTexture::Type type, int w, int h, bool clear, bool prefer_reuse)
{ {
if (t == NULL) if (t == NULL)
{ {
@ -413,7 +431,7 @@ bool GSDevice::ResizeTexture(GSTexture** t, GSTexture::Type type, int w, int h,
GSTexture::Format fmt = t2 ? t2->GetFormat() : GetDefaultTextureFormat(type); GSTexture::Format fmt = t2 ? t2->GetFormat() : GetDefaultTextureFormat(type);
delete t2; delete t2;
t2 = FetchSurface(type, w, h, fmt, clear); t2 = FetchSurface(type, w, h, fmt, clear, prefer_reuse);
*t = t2; *t = t2;
} }
@ -421,9 +439,9 @@ bool GSDevice::ResizeTexture(GSTexture** t, GSTexture::Type type, int w, int h,
return t2 != NULL; return t2 != NULL;
} }
bool GSDevice::ResizeTexture(GSTexture** t, int w, int h, bool clear) bool GSDevice::ResizeTexture(GSTexture** t, int w, int h, bool prefer_reuse)
{ {
return ResizeTexture(t, GSTexture::Type::Texture, w, h, clear); return ResizeTexture(t, GSTexture::Type::Texture, w, h, false, prefer_reuse);
} }
bool GSDevice::ResizeTarget(GSTexture** t, int w, int h) bool GSDevice::ResizeTarget(GSTexture** t, int w, int h)

View File

@ -197,7 +197,7 @@ struct alignas(16) GSHWDrawConfig
// Flat/goround shading // Flat/goround shading
u32 iip : 1; u32 iip : 1;
// Pixel test // Pixel test
u32 date : 3; u32 date : 4;
u32 atst : 3; u32 atst : 3;
// Color sampling // Color sampling
u32 fst : 1; // Investigate to do it on the VS u32 fst : 1; // Investigate to do it on the VS
@ -248,12 +248,17 @@ struct alignas(16) GSHWDrawConfig
// Scan mask // Scan mask
u32 scanmsk : 2; u32 scanmsk : 2;
u32 _free2 : 3; u32 _free2 : 2;
}; };
u64 key; u64 key;
}; };
PSSelector(): key(0) {} PSSelector(): key(0) {}
__fi bool IsFeedbackLoop() const
{
return tex_is_fb || fbmask || date > 0 || blend_a == 1 || blend_b == 1 || blend_c == 1 || blend_d == 1;
}
}; };
struct SamplerSelector struct SamplerSelector
{ {
@ -501,6 +506,7 @@ public:
bool provoking_vertex_last: 1; ///< Supports using the last vertex in a primitive as the value for flat shading. bool provoking_vertex_last: 1; ///< Supports using the last vertex in a primitive as the value for flat shading.
bool point_expand : 1; ///< Supports point expansion in hardware without using geometry shaders. bool point_expand : 1; ///< Supports point expansion in hardware without using geometry shaders.
bool line_expand : 1; ///< Supports line expansion in hardware without using geometry shaders. bool line_expand : 1; ///< Supports line expansion in hardware without using geometry shaders.
bool prefer_new_textures : 1; ///< Allocate textures up to the pool size before reusing them, to avoid render pass restarts.
FeatureSupport() FeatureSupport()
{ {
memset(this, 0, sizeof(*this)); memset(this, 0, sizeof(*this));
@ -527,7 +533,8 @@ protected:
static const int m_NO_BLEND = 0; static const int m_NO_BLEND = 0;
static const int m_MERGE_BLEND = m_blendMap.size() - 1; static const int m_MERGE_BLEND = m_blendMap.size() - 1;
bool m_rbswapped; static constexpr u32 MAX_POOLED_TEXTURES = 300;
HostDisplay* m_display; HostDisplay* m_display;
GSTexture* m_merge; GSTexture* m_merge;
GSTexture* m_weavebob; GSTexture* m_weavebob;
@ -543,11 +550,11 @@ protected:
size_t start, count, limit; size_t start, count, limit;
} m_index; } m_index;
unsigned int m_frame; // for ageing the pool unsigned int m_frame; // for ageing the pool
bool m_linear_present; bool m_rbswapped;
FeatureSupport m_features; FeatureSupport m_features;
virtual GSTexture* CreateSurface(GSTexture::Type type, int w, int h, GSTexture::Format format) = 0; virtual GSTexture* CreateSurface(GSTexture::Type type, int w, int h, GSTexture::Format format) = 0;
virtual GSTexture* FetchSurface(GSTexture::Type type, int w, int h, GSTexture::Format format, bool clear); virtual GSTexture* FetchSurface(GSTexture::Type type, int w, int h, GSTexture::Format format, bool clear, bool prefer_reuse);
virtual void DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex, GSVector4* dRect, const GSRegPMODE& PMODE, const GSRegEXTBUF& EXTBUF, const GSVector4& c) = 0; virtual void DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex, GSVector4* dRect, const GSRegPMODE& PMODE, const GSRegEXTBUF& EXTBUF, const GSVector4& c) = 0;
virtual void DoInterlace(GSTexture* sTex, GSTexture* dTex, int shader, bool linear, float yoffset) = 0; virtual void DoInterlace(GSTexture* sTex, GSTexture* dTex, int shader, bool linear, float yoffset) = 0;
@ -561,6 +568,7 @@ public:
virtual ~GSDevice(); virtual ~GSDevice();
__fi HostDisplay* GetDisplay() const { return m_display; } __fi HostDisplay* GetDisplay() const { return m_display; }
__fi unsigned int GetFrameNumber() const { return m_frame; }
void Recycle(GSTexture* t); void Recycle(GSTexture* t);
@ -606,7 +614,7 @@ public:
GSTexture* CreateSparseDepthStencil(int w, int h, GSTexture::Format format, bool clear = true); GSTexture* CreateSparseDepthStencil(int w, int h, GSTexture::Format format, bool clear = true);
GSTexture* CreateRenderTarget(int w, int h, GSTexture::Format format, bool clear = true); 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* CreateDepthStencil(int w, int h, GSTexture::Format format, bool clear = true);
GSTexture* CreateTexture(int w, int h, GSTexture::Format format); GSTexture* CreateTexture(int w, int h, GSTexture::Format format, bool prefer_reuse = false);
GSTexture* CreateOffscreen(int w, int h, GSTexture::Format format); GSTexture* CreateOffscreen(int w, int h, GSTexture::Format format);
GSTexture::Format GetDefaultTextureFormat(GSTexture::Type type); GSTexture::Format GetDefaultTextureFormat(GSTexture::Type type);
@ -638,8 +646,8 @@ public:
void ShadeBoost(); void ShadeBoost();
void ExternalFX(); void ExternalFX();
bool ResizeTexture(GSTexture** t, GSTexture::Type type, int w, int h, bool clear = true); bool ResizeTexture(GSTexture** t, GSTexture::Type type, int w, int h, bool clear = true, bool prefer_reuse = false);
bool ResizeTexture(GSTexture** t, int w, int h, bool clear = true); bool ResizeTexture(GSTexture** t, int w, int h, bool prefer_reuse = false);
bool ResizeTarget(GSTexture** t, int w, int h); bool ResizeTarget(GSTexture** t, int w, int h);
bool ResizeTarget(GSTexture** t); bool ResizeTarget(GSTexture** t);

View File

@ -27,6 +27,7 @@ GSTexture::GSTexture()
, m_mipmap_levels(0) , m_mipmap_levels(0)
, m_type(Type::Invalid) , m_type(Type::Invalid)
, m_format(Format::Invalid) , m_format(Format::Invalid)
, m_state(State::Dirty)
, m_sparse(false) , m_sparse(false)
, m_needs_mipmaps_generated(true) , m_needs_mipmaps_generated(true)
, last_frame_used(0) , last_frame_used(0)

View File

@ -49,6 +49,13 @@ public:
Int32, ///< Int32 texture for date emulation Int32, ///< Int32 texture for date emulation
}; };
enum class State : u8
{
Dirty,
Cleared,
Invalidated
};
protected: protected:
GSVector2 m_scale; GSVector2 m_scale;
GSVector2i m_size; GSVector2i m_size;
@ -57,6 +64,7 @@ protected:
int m_mipmap_levels; int m_mipmap_levels;
Type m_type; Type m_type;
Format m_format; Format m_format;
State m_state;
bool m_sparse; bool m_sparse;
bool m_needs_mipmaps_generated; bool m_needs_mipmaps_generated;
@ -91,6 +99,23 @@ public:
Type GetType() const { return m_type; } Type GetType() const { return m_type; }
Format GetFormat() const { return m_format; } Format GetFormat() const { return m_format; }
bool IsRenderTargetOrDepthStencil() const
{
return (m_type >= Type::RenderTarget && m_type <= Type::DepthStencil) ||
(m_type >= Type::SparseRenderTarget && m_type <= Type::SparseDepthStencil);
}
bool IsRenderTarget() const
{
return (m_type == Type::RenderTarget || m_type == Type::SparseRenderTarget);
}
bool IsDepthStencil() const
{
return (m_type == Type::DepthStencil || m_type == Type::SparseDepthStencil);
}
State GetState() const { return m_state; }
void SetState(State state) { m_state = state; }
void GenerateMipmapsIfNeeded(); void GenerateMipmapsIfNeeded();
void ClearMipmapGenerationFlag() { m_needs_mipmaps_generated = false; } void ClearMipmapGenerationFlag() { m_needs_mipmaps_generated = false; }

View File

@ -43,6 +43,7 @@ GSDevice11::GSDevice11()
m_features.provoking_vertex_last = false; m_features.provoking_vertex_last = false;
m_features.point_expand = false; m_features.point_expand = false;
m_features.line_expand = false; m_features.line_expand = false;
m_features.prefer_new_textures = false;
} }
bool GSDevice11::SetFeatureLevel(D3D_FEATURE_LEVEL level, bool compat_mode) bool GSDevice11::SetFeatureLevel(D3D_FEATURE_LEVEL level, bool compat_mode)

View File

@ -185,6 +185,12 @@ GSRendererHW::~GSRendererHW()
delete m_tc; delete m_tc;
} }
void GSRendererHW::Destroy()
{
m_tc->RemoveAll();
GSRenderer::Destroy();
}
void GSRendererHW::SetGameCRC(u32 crc, int options) void GSRendererHW::SetGameCRC(u32 crc, int options)
{ {
GSRenderer::SetGameCRC(crc, options); GSRenderer::SetGameCRC(crc, options);

View File

@ -166,6 +166,8 @@ public:
GSRendererHW(); GSRendererHW();
virtual ~GSRendererHW() override; virtual ~GSRendererHW() override;
void Destroy() override;
void SetGameCRC(u32 crc, int options) override; void SetGameCRC(u32 crc, int options) override;
bool CanUpscale() override; bool CanUpscale() override;
int GetUpscaleMultiplier() override; int GetUpscaleMultiplier() override;

View File

@ -1297,7 +1297,7 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con
int h = (int)(scale.y * th); int h = (int)(scale.y * th);
GSTexture* sTex = dst->m_texture; GSTexture* sTex = dst->m_texture;
GSTexture* dTex = g_gs_device->CreateTexture(w, h, GSTexture::Format::Color); GSTexture* dTex = g_gs_device->CreateTexture(w, h, GSTexture::Format::Color, true);
GSVector4i area(x, y, x + w, y + h); GSVector4i area(x, y, x + w, y + h);
g_gs_device->CopyRect(sTex, dTex, area); g_gs_device->CopyRect(sTex, dTex, area);
@ -1329,7 +1329,7 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con
// So it could be tricky to put in the middle of the DrawPrims // So it could be tricky to put in the middle of the DrawPrims
// Texture is created to keep code compatibility // Texture is created to keep code compatibility
GSTexture* dTex = g_gs_device->CreateTexture(tw, th, GSTexture::Format::Color); GSTexture* dTex = g_gs_device->CreateTexture(tw, th, GSTexture::Format::Color, true);
// Keep a trace of origin of the texture // Keep a trace of origin of the texture
src->m_texture = dTex; src->m_texture = dTex;
@ -1503,7 +1503,7 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con
// 'src' is the new texture cache entry (hence the output) // 'src' is the new texture cache entry (hence the output)
GSTexture* sTex = dst->m_texture; GSTexture* sTex = dst->m_texture;
GSTexture* dTex = use_texture ? GSTexture* dTex = use_texture ?
g_gs_device->CreateTexture(w, h, GSTexture::Format::Color) : g_gs_device->CreateTexture(w, h, GSTexture::Format::Color, true) :
g_gs_device->CreateRenderTarget(w, h, GSTexture::Format::Color, !texture_completely_overwritten); g_gs_device->CreateRenderTarget(w, h, GSTexture::Format::Color, !texture_completely_overwritten);
src->m_texture = dTex; src->m_texture = dTex;

View File

@ -238,6 +238,7 @@ bool GSDeviceOGL::Create(HostDisplay* display)
m_features.image_load_store = GLLoader::found_GL_ARB_shader_image_load_store && GLLoader::found_GL_ARB_clear_texture; m_features.image_load_store = GLLoader::found_GL_ARB_shader_image_load_store && GLLoader::found_GL_ARB_clear_texture;
m_features.texture_barrier = true; m_features.texture_barrier = true;
m_features.provoking_vertex_last = true; m_features.provoking_vertex_last = true;
m_features.prefer_new_textures = false;
GLint point_range[2] = {}; GLint point_range[2] = {};
GLint line_range[2] = {}; GLint line_range[2] = {};

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,387 @@
/* 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 "GSTextureVK.h"
#include "GS/GSVector.h"
#include "GS/Renderers/Common/GSDevice.h"
#include "common/Vulkan/StreamBuffer.h"
#include "common/HashCombine.h"
#include "vk_mem_alloc.h"
#include <array>
#include <unordered_map>
class GSDeviceVK final : public GSDevice
{
public:
struct PipelineSelector
{
GSHWDrawConfig::VSSelector vs;
GSHWDrawConfig::GSSelector gs;
GSHWDrawConfig::PSSelector ps;
GSHWDrawConfig::DepthStencilSelector dss;
GSHWDrawConfig::BlendState bs;
GSHWDrawConfig::ColorMaskSelector cms;
union
{
struct
{
u32 topology : 2;
u32 rt : 1;
u32 ds : 1;
u32 line_width : 1;
u32 feedback_loop : 1;
};
u32 key;
};
__fi bool operator==(const PipelineSelector& p) const
{
return vs.key == p.vs.key && gs.key == p.gs.key && ps.key == p.ps.key && dss.key == p.dss.key &&
bs.key == p.bs.key && cms.key == p.cms.key && key == p.key;
}
__fi bool operator!=(const PipelineSelector& p) const
{
return vs.key != p.vs.key || gs.key != p.gs.key || ps.key != p.ps.key || dss.key != p.dss.key ||
bs.key != p.bs.key || cms.key != p.cms.key || key != p.key;
}
PipelineSelector()
: key(0)
{
}
};
struct PipelineSelectorHash
{
std::size_t operator()(const PipelineSelector& e) const noexcept
{
std::size_t hash = 0;
HashCombine(hash, e.vs.key, e.gs.key, e.ps.key, e.dss.key, e.cms.key, e.bs.key, e.key);
return hash;
}
};
enum : u32
{
NUM_TFX_DESCRIPTOR_SETS = 3,
NUM_TFX_DYNAMIC_OFFSETS = 2,
NUM_TFX_SAMPLERS = 2,
NUM_TFX_RT_TEXTURES = 3,
NUM_TFX_TEXTURES = NUM_TFX_SAMPLERS + NUM_TFX_RT_TEXTURES,
NUM_CONVERT_TEXTURES = 1,
NUM_CONVERT_SAMPLERS = 1,
CONVERT_PUSH_CONSTANTS_SIZE = 32,
VERTEX_BUFFER_SIZE = 32 * 1024 * 1024,
INDEX_BUFFER_SIZE = 16 * 1024 * 1024,
VERTEX_UNIFORM_BUFFER_SIZE = 8 * 1024 * 1024,
FRAGMENT_UNIFORM_BUFFER_SIZE = 8 * 1024 * 1024,
};
enum DATE_RENDER_PASS : u32
{
DATE_RENDER_PASS_NONE = 0,
DATE_RENDER_PASS_STENCIL = 1,
DATE_RENDER_PASS_STENCIL_ONE = 2,
};
private:
u32 m_upscale_multiplier = 1;
int m_mipmap = 0;
VkDescriptorSetLayout m_utility_ds_layout = VK_NULL_HANDLE;
VkPipelineLayout m_utility_pipeline_layout = VK_NULL_HANDLE;
VkDescriptorSetLayout m_tfx_ubo_ds_layout = VK_NULL_HANDLE;
VkDescriptorSetLayout m_tfx_sampler_ds_layout = VK_NULL_HANDLE;
VkDescriptorSetLayout m_tfx_rt_texture_ds_layout = VK_NULL_HANDLE;
VkPipelineLayout m_tfx_pipeline_layout = VK_NULL_HANDLE;
Vulkan::StreamBuffer m_vertex_stream_buffer;
Vulkan::StreamBuffer m_index_stream_buffer;
Vulkan::StreamBuffer m_vertex_uniform_stream_buffer;
Vulkan::StreamBuffer m_fragment_uniform_stream_buffer;
VmaAllocation m_readback_staging_allocation = VK_NULL_HANDLE;
VkBuffer m_readback_staging_buffer = VK_NULL_HANDLE;
void* m_readback_staging_buffer_map = nullptr;
u32 m_readback_staging_buffer_size = 0;
VkSampler m_point_sampler = VK_NULL_HANDLE;
VkSampler m_linear_sampler = VK_NULL_HANDLE;
std::unordered_map<u32, VkSampler> m_samplers;
std::array<VkPipeline, static_cast<int>(ShaderConvert::Count)> m_convert{};
std::array<VkPipeline, static_cast<int>(ShaderConvert::Count)> m_present{};
std::array<VkPipeline, 16> m_color_copy{};
std::array<VkPipeline, 2> m_merge{};
std::array<VkPipeline, 4> m_interlace{};
VkPipeline m_hdr_setup_pipelines[2][2] = {}; // [depth][feedback_loop]
VkPipeline m_hdr_finish_pipelines[2][2] = {}; // [depth][feedback_loop]
VkRenderPass m_date_image_setup_render_passes[2][2] = {}; // [depth][clear]
VkPipeline m_date_image_setup_pipelines[2][2] = {}; // [depth][datm]
std::unordered_map<u32, VkShaderModule> m_tfx_vertex_shaders;
std::unordered_map<u32, VkShaderModule> m_tfx_geometry_shaders;
std::unordered_map<u64, VkShaderModule> m_tfx_fragment_shaders;
std::unordered_map<PipelineSelector, VkPipeline, PipelineSelectorHash> m_tfx_pipelines;
VkRenderPass m_utility_color_render_pass_load = VK_NULL_HANDLE;
VkRenderPass m_utility_color_render_pass_clear = VK_NULL_HANDLE;
VkRenderPass m_utility_color_render_pass_discard = VK_NULL_HANDLE;
VkRenderPass m_utility_depth_render_pass_load = VK_NULL_HANDLE;
VkRenderPass m_utility_depth_render_pass_clear = VK_NULL_HANDLE;
VkRenderPass m_utility_depth_render_pass_discard = VK_NULL_HANDLE;
VkRenderPass m_date_setup_render_pass = VK_NULL_HANDLE;
VkRenderPass m_swap_chain_render_pass = VK_NULL_HANDLE;
VkRenderPass m_tfx_render_pass[2][2][2][3][2][3][3] = {}; // [rt][ds][hdr][date][fbl][rt_op][ds_op]
GSHWDrawConfig::VSConstantBuffer m_vs_cb_cache;
GSHWDrawConfig::PSConstantBuffer m_ps_cb_cache;
std::string m_tfx_source;
GSTexture* CreateSurface(GSTexture::Type type, int w, int h, GSTexture::Format format) override;
void DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex, GSVector4* dRect, const GSRegPMODE& PMODE,
const GSRegEXTBUF& EXTBUF, const GSVector4& c) final;
void DoInterlace(GSTexture* sTex, GSTexture* dTex, int shader, bool linear, float yoffset = 0) final;
u16 ConvertBlendEnum(u16 generic) final;
VkSampler GetSampler(GSHWDrawConfig::SamplerSelector ss);
VkShaderModule GetTFXVertexShader(GSHWDrawConfig::VSSelector sel);
VkShaderModule GetTFXGeometryShader(GSHWDrawConfig::GSSelector sel);
VkShaderModule GetTFXFragmentShader(GSHWDrawConfig::PSSelector sel);
VkPipeline CreateTFXPipeline(const PipelineSelector& p);
VkPipeline GetTFXPipeline(const PipelineSelector& p);
VkShaderModule GetUtilityVertexShader(const std::string& source, const char* replace_main);
VkShaderModule GetUtilityFragmentShader(const std::string& source, const char* replace_main);
bool CheckFeatures();
bool CreateNullTexture();
bool CreateBuffers();
bool CreatePipelineLayouts();
bool CreateRenderPasses();
bool CompileConvertPipelines();
bool CompileInterlacePipelines();
bool CompileMergePipelines();
bool CheckStagingBufferSize(u32 required_size);
void DestroyStagingBuffer();
void DestroyResources();
public:
GSDeviceVK();
~GSDeviceVK() override;
__fi static GSDeviceVK* GetInstance() { return static_cast<GSDeviceVK*>(g_gs_device.get()); }
__fi VkRenderPass GetTFXRenderPass(bool rt, bool ds, bool hdr, DATE_RENDER_PASS date, bool fbl,
VkAttachmentLoadOp rt_op, VkAttachmentLoadOp ds_op) const
{
return m_tfx_render_pass[rt][ds][hdr][date][fbl][rt_op][ds_op];
}
__fi VkSampler GetPointSampler() const { return m_point_sampler; }
__fi VkSampler GetLinearSampler() const { return m_linear_sampler; }
bool Create(HostDisplay* display) override;
void Destroy() override;
void ResetAPIState() override;
void RestoreAPIState() override;
void PushDebugGroup(const char* fmt, ...) override;
void PopDebugGroup() override;
void InsertDebugMessage(DebugMessageCategory category, const char* fmt, ...) override;
void DrawPrimitive();
void DrawIndexedPrimitive();
void DrawIndexedPrimitive(int offset, int count);
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;
bool DownloadTexture(GSTexture* src, const GSVector4i& rect, GSTexture::GSMap& out_map) override;
void DownloadTextureComplete() override;
GSTexture* DrawForReadback(GSTexture* src, const GSVector4& sRect, int w, int h, int format = 0, int ps_shader = 0);
bool ReadbackTexture(GSTexture* src, const GSVector4i& rect, u32 level, GSTexture::GSMap* dst);
void CopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r) override;
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, bool red,
bool green, bool blue, bool alpha) override;
void BeginRenderPassForStretchRect(GSTextureVK* dTex, const GSVector4i& dtex_rc, const GSVector4i& dst_rc);
void DoStretchRect(GSTextureVK* sTex, const GSVector4& sRect, GSTextureVK* dTex, const GSVector4& dRect,
VkPipeline pipeline, bool linear);
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,
u32 dLevel, bool linear);
void SetupDATE(GSTexture* rt, GSTexture* ds, bool datm, const GSVector4i& bbox);
GSTextureVK* SetupPrimitiveTrackingDATE(GSHWDrawConfig& config, PipelineSelector& pipe);
void IASetVertexBuffer(const void* vertex, size_t stride, size_t count);
bool IAMapVertexBuffer(void** vertex, size_t stride, size_t count);
void IAUnmapVertexBuffer();
void IASetIndexBuffer(const void* index, size_t count);
void PSSetShaderResource(int i, GSTexture* sr);
void PSSetSampler(u32 index, GSHWDrawConfig::SamplerSelector sel);
void OMSetRenderTargets(GSTexture* rt, GSTexture* ds, const GSVector4i& scissor, bool feedback_loop);
void SetVSConstantBuffer(const GSHWDrawConfig::VSConstantBuffer& cb);
void SetPSConstantBuffer(const GSHWDrawConfig::PSConstantBuffer& cb);
bool BindDrawPipeline(const PipelineSelector& p);
void RenderHW(GSHWDrawConfig& config) override;
void UpdateHWPipelineSelector(GSHWDrawConfig& config);
void SendHWDraw(const GSHWDrawConfig& config);
//////////////////////////////////////////////////////////////////////////
// Vulkan State
//////////////////////////////////////////////////////////////////////////
public:
__fi bool CurrentFramebufferHasFeedbackLoop() const { return m_current_framebuffer_has_feedback_loop; }
__fi VkFramebuffer GetCurrentFramebuffer() const { return m_current_framebuffer; }
/// Ends any render pass, executes the command buffer, and invalidates cached state.
void ExecuteCommandBuffer(bool wait_for_completion);
void ExecuteCommandBuffer(bool wait_for_completion, const char* reason, ...);
void ExecuteCommandBufferAndRestartRenderPass(const char* reason);
/// Set dirty flags on everything to force re-bind at next draw time.
void InvalidateCachedState();
/// Binds all dirty state to the command buffer.
bool ApplyUtilityState(bool already_execed = false);
bool ApplyTFXState(bool already_execed = false);
void SetVertexBuffer(VkBuffer buffer, VkDeviceSize offset);
void SetIndexBuffer(VkBuffer buffer, VkDeviceSize offset, VkIndexType type);
void SetBlendConstants(u8 color);
void SetUtilityTexture(GSTexture* tex, VkSampler sampler);
void SetUtilityPushConstants(const void* data, u32 size);
void UnbindTexture(GSTextureVK* tex);
// Ends a render pass if we're currently in one.
// When Bind() is next called, the pass will be restarted.
// Calling this function is allowed even if a pass has not begun.
bool InRenderPass();
void BeginRenderPass(VkRenderPass rp, const GSVector4i& rect);
void BeginClearRenderPass(VkRenderPass rp, const GSVector4i& rect, const VkClearValue* cv, u32 cv_count);
void BeginClearRenderPass(VkRenderPass rp, const GSVector4i& rect, const GSVector4& clear_color);
void BeginClearRenderPass(VkRenderPass rp, const GSVector4i& rect, float depth, u8 stencil);
bool CheckRenderPassArea(const GSVector4i& rect);
void EndRenderPass();
void SetViewport(const VkViewport& viewport);
void SetScissor(const GSVector4i& scissor);
void SetPipeline(VkPipeline pipeline);
private:
enum DIRTY_FLAG : u32
{
DIRTY_FLAG_TFX_SAMPLERS_DS = (1 << 0),
DIRTY_FLAG_TFX_RT_TEXTURE_DS = (1 << 1),
DIRTY_FLAG_TFX_DYNAMIC_OFFSETS = (1 << 2),
DIRTY_FLAG_UTILITY_TEXTURE = (1 << 3),
DIRTY_FLAG_BLEND_CONSTANTS = (1 << 4),
DIRTY_FLAG_VERTEX_BUFFER = (1 << 5),
DIRTY_FLAG_INDEX_BUFFER = (1 << 6),
DIRTY_FLAG_VIEWPORT = (1 << 7),
DIRTY_FLAG_SCISSOR = (1 << 8),
DIRTY_FLAG_PIPELINE = (1 << 9),
DIRTY_FLAG_VS_CONSTANT_BUFFER = (1 << 10),
DIRTY_FLAG_PS_CONSTANT_BUFFER = (1 << 11),
DIRTY_BASE_STATE = DIRTY_FLAG_VERTEX_BUFFER | DIRTY_FLAG_INDEX_BUFFER | DIRTY_FLAG_PIPELINE |
DIRTY_FLAG_VIEWPORT | DIRTY_FLAG_SCISSOR | DIRTY_FLAG_BLEND_CONSTANTS,
DIRTY_TFX_STATE = DIRTY_BASE_STATE | DIRTY_FLAG_TFX_SAMPLERS_DS | DIRTY_FLAG_TFX_RT_TEXTURE_DS,
DIRTY_UTILITY_STATE = DIRTY_BASE_STATE | DIRTY_FLAG_UTILITY_TEXTURE,
DIRTY_CONSTANT_BUFFER_STATE = DIRTY_FLAG_VS_CONSTANT_BUFFER | DIRTY_FLAG_PS_CONSTANT_BUFFER,
};
enum class PipelineLayout
{
Undefined,
TFX,
Utility
};
void InitializeState();
bool CreatePersistentDescriptorSets();
void ApplyBaseState(u32 flags, VkCommandBuffer cmdbuf);
// Which bindings/state has to be updated before the next draw.
u32 m_dirty_flags = 0;
bool m_current_framebuffer_has_feedback_loop = false;
// input assembly
VkBuffer m_vertex_buffer = VK_NULL_HANDLE;
VkDeviceSize m_vertex_buffer_offset = 0;
VkBuffer m_index_buffer = VK_NULL_HANDLE;
VkDeviceSize m_index_buffer_offset = 0;
VkIndexType m_index_type = VK_INDEX_TYPE_UINT16;
GSTextureVK* m_current_render_target = nullptr;
GSTextureVK* m_current_depth_target = nullptr;
VkFramebuffer m_current_framebuffer = VK_NULL_HANDLE;
VkRenderPass m_current_render_pass = VK_NULL_HANDLE;
GSVector4i m_current_render_pass_area = GSVector4i::zero();
VkViewport m_viewport = {0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f};
GSVector4i m_scissor = GSVector4i::zero();
u8 m_blend_constant_color = 0;
std::array<VkImageView, NUM_TFX_TEXTURES> m_tfx_textures{};
std::array<VkSampler, NUM_TFX_SAMPLERS> m_tfx_samplers{};
std::array<u32, NUM_TFX_SAMPLERS> m_tfx_sampler_sel{};
std::array<VkDescriptorSet, NUM_TFX_DESCRIPTOR_SETS> m_tfx_descriptor_sets{};
std::array<u32, NUM_TFX_DYNAMIC_OFFSETS> m_tfx_dynamic_offsets{};
VkImageView m_utility_texture = VK_NULL_HANDLE;
VkSampler m_utility_sampler = VK_NULL_HANDLE;
VkDescriptorSet m_utility_descriptor_set = VK_NULL_HANDLE;
PipelineLayout m_current_pipeline_layout = PipelineLayout::Undefined;
VkPipeline m_current_pipeline = VK_NULL_HANDLE;
Vulkan::Texture m_null_texture;
// current pipeline selector - we save this in the struct to avoid re-zeroing it every draw
PipelineSelector m_pipeline_selector = {};
};

View File

@ -0,0 +1,367 @@
/* 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 "GSDeviceVK.h"
#include "GSTextureVK.h"
#include "common/Assertions.h"
#include "common/Vulkan/Builders.h"
#include "common/Vulkan/Context.h"
#include "common/Vulkan/Util.h"
#include "GS/GSPerfMon.h"
#include "GS/GSGL.h"
GSTextureVK::GSTextureVK(Type type, Format format, Vulkan::Texture texture)
: m_texture(std::move(texture))
{
m_type = type;
m_format = format;
m_size.x = m_texture.GetWidth();
m_size.y = m_texture.GetHeight();
m_mipmap_levels = m_texture.GetLevels();
}
GSTextureVK::~GSTextureVK()
{
GSDeviceVK::GetInstance()->UnbindTexture(this);
if (m_type == Type::RenderTarget || m_type == Type::DepthStencil)
{
for (const auto& [other_tex, fb, feedback] : m_framebuffers)
{
if (other_tex)
{
for (auto other_it = other_tex->m_framebuffers.begin(); other_it != other_tex->m_framebuffers.end();
++other_it)
{
if (std::get<0>(*other_it) == this)
{
other_tex->m_framebuffers.erase(other_it);
break;
}
}
}
g_vulkan_context->DeferFramebufferDestruction(fb);
}
}
}
std::unique_ptr<GSTextureVK> GSTextureVK::Create(Type type, u32 width, u32 height, u32 levels, Format format)
{
switch (type)
{
case Type::Texture:
{
VkImageUsageFlags usage =
VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
const VkComponentMapping* swizzle = nullptr;
if (format == Format::UNorm8)
{
// for r8 textures, swizzle it across all 4 components. the shaders depend on it being in alpha.. why?
static constexpr VkComponentMapping r8_swizzle = {
VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_R};
swizzle = &r8_swizzle;
}
Vulkan::Texture texture;
if (!texture.Create(width, height, levels, 1, LookupNativeFormat(format), VK_SAMPLE_COUNT_1_BIT,
VK_IMAGE_VIEW_TYPE_2D, VK_IMAGE_TILING_OPTIMAL, usage, swizzle))
{
return {};
}
Vulkan::Util::SetObjectName(
g_vulkan_context->GetDevice(), texture.GetImage(), "%ux%u texture", width, height);
return std::make_unique<GSTextureVK>(type, format, std::move(texture));
}
case Type::RenderTarget:
{
pxAssert(levels == 1);
Vulkan::Texture texture;
if (!texture.Create(width, height, levels, 1, LookupNativeFormat(format), VK_SAMPLE_COUNT_1_BIT,
VK_IMAGE_VIEW_TYPE_2D, VK_IMAGE_TILING_OPTIMAL,
VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT |
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT |
VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT))
{
return {};
}
Vulkan::Util::SetObjectName(
g_vulkan_context->GetDevice(), texture.GetImage(), "%ux%u render target", width, height);
return std::make_unique<GSTextureVK>(type, format, std::move(texture));
}
case Type::DepthStencil:
{
pxAssert(levels == 1);
Vulkan::Texture texture;
if (!texture.Create(width, height, levels, 1, LookupNativeFormat(format), VK_SAMPLE_COUNT_1_BIT,
VK_IMAGE_VIEW_TYPE_2D, VK_IMAGE_TILING_OPTIMAL,
VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT |
VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT))
{
return {};
}
Vulkan::Util::SetObjectName(
g_vulkan_context->GetDevice(), texture.GetImage(), "%ux%u depth stencil", width, height);
return std::make_unique<GSTextureVK>(type, format, std::move(texture));
}
default:
return {};
}
}
VkFormat GSTextureVK::LookupNativeFormat(Format format)
{
static constexpr std::array<VkFormat, static_cast<int>(GSTexture::Format::Int32) + 1> s_format_mapping = {{
VK_FORMAT_UNDEFINED, // Invalid
VK_FORMAT_R8G8B8A8_UNORM, // Color
VK_FORMAT_R32G32B32A32_SFLOAT, // FloatColor
VK_FORMAT_D32_SFLOAT_S8_UINT, // DepthStencil
VK_FORMAT_R8_UNORM, // UNorm8
VK_FORMAT_R16_UINT, // UInt16
VK_FORMAT_R32_UINT, // UInt32
VK_FORMAT_R32_SFLOAT, // Int32
}};
return s_format_mapping[static_cast<int>(format)];
}
void* GSTextureVK::GetNativeHandle() const { return const_cast<Vulkan::Texture*>(&m_texture); }
VkCommandBuffer GSTextureVK::GetCommandBufferForUpdate()
{
const u32 frame = GSDeviceVK::GetInstance()->GetFrameNumber();
if (m_type != Type::Texture || frame == last_frame_used)
{
// Console.WriteLn("Texture update within frame, can't use do beforehand");
GSDeviceVK::GetInstance()->EndRenderPass();
return g_vulkan_context->GetCurrentCommandBuffer();
}
return g_vulkan_context->GetCurrentInitCommandBuffer();
}
bool GSTextureVK::Update(const GSVector4i& r, const void* data, int pitch, int layer)
{
if (m_type != Type::Texture || static_cast<u32>(layer) >= m_texture.GetLevels())
return false;
g_perfmon.Put(GSPerfMon::TextureUploads, 1);
const u32 width = r.width();
const u32 height = r.height();
const u32 row_length = static_cast<u32>(pitch) / Vulkan::Util::GetTexelSize(m_texture.GetFormat());
const u32 required_size = static_cast<u32>(pitch) * height;
Vulkan::StreamBuffer& buffer = g_vulkan_context->GetTextureUploadBuffer();
if (!buffer.ReserveMemory(required_size, g_vulkan_context->GetBufferImageGranularity()))
{
GSDeviceVK::GetInstance()->ExecuteCommandBuffer(
false, "While waiting for %u bytes in texture upload buffer", required_size);
if (!buffer.ReserveMemory(required_size, g_vulkan_context->GetBufferImageGranularity()))
pxFailRel("Failed to reserve texture upload memory");
}
const u32 buffer_offset = buffer.GetCurrentOffset();
std::memcpy(buffer.GetCurrentHostPointer(), data, required_size);
buffer.CommitMemory(required_size);
const VkCommandBuffer cmdbuf = GetCommandBufferForUpdate();
GL_PUSH("GSTextureVK::Update({%d,%d} %dx%d Lvl:%u", r.x, r.y, r.width(), r.height(), layer);
// first time the texture is used? don't leave it undefined
if (m_texture.GetLayout() == VK_IMAGE_LAYOUT_UNDEFINED)
m_texture.TransitionToLayout(cmdbuf, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
m_texture.UpdateFromBuffer(
cmdbuf, layer, 0, r.x, r.y, width, height, row_length, buffer.GetBuffer(), buffer_offset);
m_texture.TransitionToLayout(cmdbuf, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
m_needs_mipmaps_generated |= (layer == 0);
return true;
}
bool GSTextureVK::Map(GSMap& m, const GSVector4i* r, int layer)
{
if (m_type == Type::Texture && static_cast<u32>(layer) < m_texture.GetLevels())
{
// map for writing
m_map_area = r ? *r : GSVector4i(0, 0, m_texture.GetWidth(), m_texture.GetHeight());
m_map_level = layer;
m.pitch = m_map_area.width() * Vulkan::Util::GetTexelSize(m_texture.GetFormat());
const u32 required_size = m.pitch * m_map_area.height();
Vulkan::StreamBuffer& buffer = g_vulkan_context->GetTextureUploadBuffer();
if (!buffer.ReserveMemory(required_size, g_vulkan_context->GetBufferImageGranularity()))
{
GSDeviceVK::GetInstance()->ExecuteCommandBuffer(
false, "While waiting for %u bytes in texture upload buffer", required_size);
if (!buffer.ReserveMemory(required_size, g_vulkan_context->GetBufferImageGranularity()))
pxFailRel("Failed to reserve texture upload memory");
}
m.bits = static_cast<u8*>(buffer.GetCurrentHostPointer());
return true;
}
else
{
// not available
return false;
}
}
void GSTextureVK::Unmap()
{
if (m_type == Type::Texture)
{
pxAssert(m_map_level < m_texture.GetLevels());
g_perfmon.Put(GSPerfMon::TextureUploads, 1);
// TODO: non-tightly-packed formats
const u32 width = static_cast<u32>(m_map_area.width());
const u32 height = static_cast<u32>(m_map_area.height());
const u32 required_size = width * height * Vulkan::Util::GetTexelSize(m_texture.GetFormat());
Vulkan::StreamBuffer& buffer = g_vulkan_context->GetTextureUploadBuffer();
const u32 buffer_offset = buffer.GetCurrentOffset();
buffer.CommitMemory(required_size);
const VkCommandBuffer cmdbuf = GetCommandBufferForUpdate();
GL_PUSH("GSTextureVK::Update({%d,%d} %dx%d Lvl:%u", m_map_area.x, m_map_area.y, m_map_area.width(),
m_map_area.height(), m_map_level);
// first time the texture is used? don't leave it undefined
if (m_texture.GetLayout() == VK_IMAGE_LAYOUT_UNDEFINED)
m_texture.TransitionToLayout(cmdbuf, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
m_texture.UpdateFromBuffer(cmdbuf, m_map_level, 0, m_map_area.x, m_map_area.y, width, height, width,
buffer.GetBuffer(), buffer_offset);
m_texture.TransitionToLayout(cmdbuf, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
m_needs_mipmaps_generated |= (m_map_level == 0);
}
}
void GSTextureVK::GenerateMipmap()
{
const VkCommandBuffer cmdbuf = GetCommandBufferForUpdate();
for (int dst_level = 1; dst_level < m_mipmap_levels; dst_level++)
{
const int src_level = dst_level - 1;
const int src_width = std::max<int>(m_size.x >> src_level, 1);
const int src_height = std::max<int>(m_size.y >> src_level, 1);
const int dst_width = std::max<int>(m_size.x >> dst_level, 1);
const int dst_height = std::max<int>(m_size.y >> dst_level, 1);
m_texture.TransitionSubresourcesToLayout(
cmdbuf, src_level, 1, 0, 1, m_texture.GetLayout(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
m_texture.TransitionSubresourcesToLayout(
cmdbuf, dst_level, 1, 0, 1, m_texture.GetLayout(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
const VkImageBlit blit = {
{VK_IMAGE_ASPECT_COLOR_BIT, static_cast<u32>(src_level), 0u, 1u}, // srcSubresource
{{0, 0, 0}, {src_width, src_height, 1}}, // srcOffsets
{VK_IMAGE_ASPECT_COLOR_BIT, static_cast<u32>(dst_level), 0u, 1u}, // dstSubresource
{{0, 0, 0}, {dst_width, dst_height, 1}} // dstOffsets
};
vkCmdBlitImage(cmdbuf, m_texture.GetImage(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, m_texture.GetImage(),
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &blit, VK_FILTER_LINEAR);
m_texture.TransitionSubresourcesToLayout(
cmdbuf, src_level, 1, 0, 1, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, m_texture.GetLayout());
m_texture.TransitionSubresourcesToLayout(
cmdbuf, dst_level, 1, 0, 1, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, m_texture.GetLayout());
}
}
void GSTextureVK::TransitionToLayout(VkImageLayout layout)
{
m_texture.TransitionToLayout(g_vulkan_context->GetCurrentCommandBuffer(), layout);
}
void GSTextureVK::CommitClear()
{
if (m_state != GSTexture::State::Cleared)
return;
GSDeviceVK::GetInstance()->EndRenderPass();
TransitionToLayout(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
if (IsDepthStencil())
{
const VkClearDepthStencilValue cv = { m_clear_value.depth };
const VkImageSubresourceRange srr = { VK_IMAGE_ASPECT_DEPTH_BIT, 0u, 1u, 0u, 1u };
vkCmdClearDepthStencilImage(g_vulkan_context->GetCurrentCommandBuffer(), m_texture.GetImage(), m_texture.GetLayout(), &cv, 1, &srr);
}
else
{
alignas(16) VkClearColorValue cv;
GSVector4::store<true>(cv.float32, GetClearColor());
const VkImageSubresourceRange srr = {VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u};
vkCmdClearColorImage(g_vulkan_context->GetCurrentCommandBuffer(), m_texture.GetImage(), m_texture.GetLayout(), &cv, 1, &srr);
}
SetState(GSTexture::State::Dirty);
}
VkFramebuffer GSTextureVK::GetFramebuffer(bool feedback_loop) { return GetLinkedFramebuffer(nullptr, feedback_loop); }
VkFramebuffer GSTextureVK::GetLinkedFramebuffer(GSTextureVK* depth_texture, bool feedback_loop)
{
pxAssertRel(m_type != Type::Texture, "Texture is a render target");
for (const auto& [other_tex, fb, other_feedback_loop] : m_framebuffers)
{
if (other_tex == depth_texture && other_feedback_loop == feedback_loop)
return fb;
}
VkRenderPass rp = g_vulkan_context->GetRenderPass(
(m_type != GSTexture::Type::DepthStencil) ? GetNativeFormat() : VK_FORMAT_UNDEFINED,
(m_type != GSTexture::Type::DepthStencil) ?
(depth_texture ? depth_texture->GetNativeFormat() : VK_FORMAT_UNDEFINED) :
GetNativeFormat(),
VK_ATTACHMENT_LOAD_OP_LOAD, VK_ATTACHMENT_STORE_OP_STORE, VK_ATTACHMENT_LOAD_OP_LOAD,
VK_ATTACHMENT_STORE_OP_STORE, VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_DONT_CARE, feedback_loop);
if (!rp)
return VK_NULL_HANDLE;
Vulkan::FramebufferBuilder fbb;
fbb.AddAttachment(m_texture.GetView());
if (depth_texture)
fbb.AddAttachment(depth_texture->m_texture.GetView());
fbb.SetSize(m_texture.GetWidth(), m_texture.GetHeight(), m_texture.GetLayers());
fbb.SetRenderPass(rp);
VkFramebuffer fb = fbb.Create(g_vulkan_context->GetDevice());
if (!fb)
return VK_NULL_HANDLE;
m_framebuffers.emplace_back(depth_texture, fb, feedback_loop);
if (depth_texture)
depth_texture->m_framebuffers.emplace_back(this, fb, feedback_loop);
return fb;
}

View File

@ -0,0 +1,85 @@
/* 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 "GS.h"
#include "GS/Renderers/Common/GSTexture.h"
#include "common/Vulkan/Texture.h"
class GSTextureVK final : public GSTexture
{
public:
union alignas(16) ClearValue
{
float color[4];
float depth;
};
public:
GSTextureVK(Type type, Format format, Vulkan::Texture texture);
~GSTextureVK() override;
static std::unique_ptr<GSTextureVK> Create(Type type, u32 width, u32 height, u32 levels, Format format);
static VkFormat LookupNativeFormat(Format format);
__fi Vulkan::Texture& GetTexture() { return m_texture; }
__fi VkFormat GetNativeFormat() const { return m_texture.GetFormat(); }
__fi VkImage GetImage() const { return m_texture.GetImage(); }
__fi VkImageView GetView() const { return m_texture.GetView(); }
__fi VkImageLayout GetLayout() const { return m_texture.GetLayout(); }
__fi GSVector4 GetClearColor() const { return GSVector4::load<true>(m_clear_value.color); }
__fi float GetClearDepth() const { return m_clear_value.depth; }
void* GetNativeHandle() const override;
bool Update(const GSVector4i& r, const void* data, int pitch, int layer = 0) override;
bool Map(GSMap& m, const GSVector4i* r = NULL, int layer = 0) override;
void Unmap() override;
void GenerateMipmap() override;
void TransitionToLayout(VkImageLayout layout);
void CommitClear();
/// Framebuffers are lazily allocated.
VkFramebuffer GetFramebuffer(bool feedback_loop);
VkFramebuffer GetLinkedFramebuffer(GSTextureVK* depth_texture, bool feedback_loop);
__fi void SetClearColor(const GSVector4& color)
{
m_state = State::Cleared;
GSVector4::store<true>(m_clear_value.color, color);
}
__fi void SetClearDepth(float depth)
{
m_state = State::Cleared;
m_clear_value.depth = depth;
}
private:
VkCommandBuffer GetCommandBufferForUpdate();
Vulkan::Texture m_texture;
ClearValue m_clear_value = {};
GSVector4i m_map_area = GSVector4i::zero();
u32 m_map_level = UINT32_MAX;
// linked framebuffer is combined with depth texture
// list of color textures this depth texture is linked to or vice versa
std::vector<std::tuple<GSTextureVK*, VkFramebuffer, bool>> m_framebuffers;
};

View File

@ -23,6 +23,10 @@
#include "Frontend/D3D11HostDisplay.h" #include "Frontend/D3D11HostDisplay.h"
#endif #endif
#ifdef ENABLE_VULKAN
#include "Frontend/VulkanHostDisplay.h"
#endif
using namespace GSSettingsDialog; using namespace GSSettingsDialog;
namespace namespace
@ -288,7 +292,7 @@ RendererTab::RendererTab(wxWindow* parent)
m_ui.addComboBoxAndLabel(hw_choice_grid, "Anisotropic Filtering:", "MaxAnisotropy", &theApp.m_gs_max_anisotropy, IDC_AFCOMBO, aniso_prereq); m_ui.addComboBoxAndLabel(hw_choice_grid, "Anisotropic Filtering:", "MaxAnisotropy", &theApp.m_gs_max_anisotropy, IDC_AFCOMBO, aniso_prereq);
m_ui.addComboBoxAndLabel(hw_choice_grid, "Dithering (PgDn):", "dithering_ps2", &theApp.m_gs_dithering, IDC_DITHERING, hw_prereq); m_ui.addComboBoxAndLabel(hw_choice_grid, "Dithering (PgDn):", "dithering_ps2", &theApp.m_gs_dithering, IDC_DITHERING, hw_prereq);
m_ui.addComboBoxAndLabel(hw_choice_grid, "Mipmapping (Insert):", "mipmap_hw", &theApp.m_gs_hw_mipmapping, IDC_MIPMAP_HW, hw_prereq); m_ui.addComboBoxAndLabel(hw_choice_grid, "Mipmapping:", "mipmap_hw", &theApp.m_gs_hw_mipmapping, IDC_MIPMAP_HW, hw_prereq);
m_ui.addComboBoxAndLabel(hw_choice_grid, "CRC Hack Level:", "crc_hack_level", &theApp.m_gs_crc_level, IDC_CRC_LEVEL, hw_prereq); m_ui.addComboBoxAndLabel(hw_choice_grid, "CRC Hack Level:", "crc_hack_level", &theApp.m_gs_crc_level, IDC_CRC_LEVEL, hw_prereq);
m_blend_mode = m_ui.addComboBoxAndLabel(hw_choice_grid, "Blending Accuracy:", "accurate_blending_unit", &theApp.m_gs_acc_blend_level, IDC_ACCURATE_BLEND_UNIT, hw_prereq); m_blend_mode = m_ui.addComboBoxAndLabel(hw_choice_grid, "Blending Accuracy:", "accurate_blending_unit", &theApp.m_gs_acc_blend_level, IDC_ACCURATE_BLEND_UNIT, hw_prereq);
@ -349,7 +353,7 @@ HacksTab::HacksTab(wxWindow* parent)
auto hw_prereq = [this]{ return m_is_hardware; }; auto hw_prereq = [this]{ return m_is_hardware; };
auto* hacks_check_box = m_ui.addCheckBox(tab_box.inner, "Enable HW Hacks", "UserHacks", -1, hw_prereq); auto* hacks_check_box = m_ui.addCheckBox(tab_box.inner, "Enable HW Hacks", "UserHacks", -1, hw_prereq);
auto hacks_prereq = [this, hacks_check_box]{ return m_is_hardware && hacks_check_box->GetValue(); }; auto hacks_prereq = [this, hacks_check_box]{ return m_is_hardware && hacks_check_box->GetValue(); };
auto gl_hacks_prereq = [this, hacks_check_box]{ return m_is_ogl_hw && hacks_check_box->GetValue(); }; auto gl_or_vk_hacks_prereq = [this, hacks_check_box]{ return (m_is_ogl_hw || m_is_vk_hw) && hacks_check_box->GetValue(); };
auto upscale_hacks_prereq = [this, hacks_check_box]{ return !m_is_native_res && hacks_check_box->GetValue(); }; auto upscale_hacks_prereq = [this, hacks_check_box]{ return !m_is_native_res && hacks_check_box->GetValue(); };
PaddedBoxSizer<wxStaticBoxSizer> rend_hacks_box (wxVERTICAL, this, "Renderer Hacks"); PaddedBoxSizer<wxStaticBoxSizer> rend_hacks_box (wxVERTICAL, this, "Renderer Hacks");
@ -379,7 +383,7 @@ HacksTab::HacksTab(wxWindow* parent)
// Renderer Hacks: // Renderer Hacks:
m_ui.addComboBoxAndLabel(rend_hack_choice_grid, "Half Screen Fix:", "UserHacks_Half_Bottom_Override", &theApp.m_gs_generic_list, IDC_HALF_SCREEN_TS, hacks_prereq); m_ui.addComboBoxAndLabel(rend_hack_choice_grid, "Half Screen Fix:", "UserHacks_Half_Bottom_Override", &theApp.m_gs_generic_list, IDC_HALF_SCREEN_TS, hacks_prereq);
m_ui.addComboBoxAndLabel(rend_hack_choice_grid, "Trilinear Filtering:", "UserHacks_TriFilter", &theApp.m_gs_trifilter, IDC_TRI_FILTER, gl_hacks_prereq); m_ui.addComboBoxAndLabel(rend_hack_choice_grid, "Trilinear Filtering:", "UserHacks_TriFilter", &theApp.m_gs_trifilter, IDC_TRI_FILTER, gl_or_vk_hacks_prereq);
// Skipdraw Range // Skipdraw Range
add_label(this, rend_hack_choice_grid, "Skipdraw Range:", IDC_SKIPDRAWHACK); add_label(this, rend_hack_choice_grid, "Skipdraw Range:", IDC_SKIPDRAWHACK);
@ -470,10 +474,12 @@ PostTab::PostTab(wxWindow* parent)
PaddedBoxSizer<wxBoxSizer> tab_box(wxVERTICAL); PaddedBoxSizer<wxBoxSizer> tab_box(wxVERTICAL);
PaddedBoxSizer<wxStaticBoxSizer> shader_box(wxVERTICAL, this, "Custom Shader"); PaddedBoxSizer<wxStaticBoxSizer> shader_box(wxVERTICAL, this, "Custom Shader");
m_ui.addCheckBox(shader_box.inner, "Texture Filtering of Display", "linear_present", IDC_LINEAR_PRESENT); auto not_vk_prereq = [this] { return !m_is_vk_hw; };
m_ui.addCheckBox(shader_box.inner, "FXAA Shader (PgUp)", "fxaa", IDC_FXAA);
CheckboxPrereq shade_boost_check(m_ui.addCheckBox(shader_box.inner, "Enable Shade Boost", "ShadeBoost", IDC_SHADEBOOST)); m_ui.addCheckBox(shader_box.inner, "Texture Filtering of Display", "linear_present", IDC_LINEAR_PRESENT);
m_ui.addCheckBox(shader_box.inner, "FXAA Shader (PgUp)", "fxaa", IDC_FXAA, not_vk_prereq);
CheckboxPrereq shade_boost_check(m_ui.addCheckBox(shader_box.inner, "Enable Shade Boost", "ShadeBoost", IDC_SHADEBOOST, not_vk_prereq));
PaddedBoxSizer<wxStaticBoxSizer> shade_boost_box(wxVERTICAL, this, "Shade Boost"); PaddedBoxSizer<wxStaticBoxSizer> shade_boost_box(wxVERTICAL, this, "Shade Boost");
auto* shader_boost_grid = new wxFlexGridSizer(2, space, space); auto* shader_boost_grid = new wxFlexGridSizer(2, space, space);
@ -486,7 +492,7 @@ PostTab::PostTab(wxWindow* parent)
shade_boost_box->Add(shader_boost_grid, wxSizerFlags().Expand()); shade_boost_box->Add(shader_boost_grid, wxSizerFlags().Expand());
shader_box->Add(shade_boost_box.outer, wxSizerFlags().Expand()); shader_box->Add(shade_boost_box.outer, wxSizerFlags().Expand());
CheckboxPrereq ext_shader_check(m_ui.addCheckBox(shader_box.inner, "Enable External Shader", "shaderfx", IDC_SHADER_FX)); CheckboxPrereq ext_shader_check(m_ui.addCheckBox(shader_box.inner, "Enable External Shader", "shaderfx", IDC_SHADER_FX, not_vk_prereq));
PaddedBoxSizer<wxStaticBoxSizer> ext_shader_box(wxVERTICAL, this, "External Shader (Home)"); PaddedBoxSizer<wxStaticBoxSizer> ext_shader_box(wxVERTICAL, this, "External Shader (Home)");
auto* ext_shader_grid = new wxFlexGridSizer(2, space, space); auto* ext_shader_grid = new wxFlexGridSizer(2, space, space);
@ -687,6 +693,11 @@ void Dialog::RendererChange()
case GSRendererType::DX11: case GSRendererType::DX11:
list = D3D11HostDisplay::StaticGetAdapterAndModeList(); list = D3D11HostDisplay::StaticGetAdapterAndModeList();
break; break;
#endif
#ifdef ENABLE_VULKAN
case GSRendererType::VK:
list = VulkanHostDisplay::StaticGetAdapterAndModeList(nullptr);
break;
#endif #endif
default: default:
break; break;
@ -763,13 +774,15 @@ void Dialog::Update()
else else
{ {
// cross-tab dependencies yay // cross-tab dependencies yay
const bool is_hw = renderer == GSRendererType::OGL || renderer == GSRendererType::DX11; const bool is_hw = renderer == GSRendererType::OGL || renderer == GSRendererType::DX11 || renderer == GSRendererType::VK;
const bool is_upscale = m_renderer_panel->m_internal_resolution->GetSelection() != 0; const bool is_upscale = m_renderer_panel->m_internal_resolution->GetSelection() != 0;
m_hacks_panel->m_is_native_res = !is_hw || !is_upscale; m_hacks_panel->m_is_native_res = !is_hw || !is_upscale;
m_hacks_panel->m_is_hardware = is_hw; m_hacks_panel->m_is_hardware = is_hw;
m_hacks_panel->m_is_ogl_hw = renderer == GSRendererType::OGL; m_hacks_panel->m_is_ogl_hw = renderer == GSRendererType::OGL;
m_hacks_panel->m_is_vk_hw = renderer == GSRendererType::VK;
m_renderer_panel->m_is_hardware = is_hw; m_renderer_panel->m_is_hardware = is_hw;
m_renderer_panel->m_is_native_res = !is_hw || !is_upscale; m_renderer_panel->m_is_native_res = !is_hw || !is_upscale;
m_post_panel->m_is_vk_hw = renderer == GSRendererType::VK;
m_debug_panel->m_is_ogl_hw = renderer == GSRendererType::OGL; m_debug_panel->m_is_ogl_hw = renderer == GSRendererType::OGL;
m_ui.Update(); m_ui.Update();

View File

@ -127,6 +127,7 @@ namespace GSSettingsDialog
bool m_is_hardware = false; bool m_is_hardware = false;
bool m_is_native_res = false; bool m_is_native_res = false;
bool m_is_ogl_hw = false; bool m_is_ogl_hw = false;
bool m_is_vk_hw = false;
HacksTab(wxWindow* parent); HacksTab(wxWindow* parent);
void Load() { m_ui.Load(); } void Load() { m_ui.Load(); }
@ -161,6 +162,7 @@ namespace GSSettingsDialog
{ {
public: public:
GSUIElementHolder m_ui; GSUIElementHolder m_ui;
bool m_is_vk_hw = false;
PostTab(wxWindow* parent); PostTab(wxWindow* parent);
void Load() { m_ui.Load(); } void Load() { m_ui.Load(); }

View File

@ -119,12 +119,12 @@ std::unique_ptr<HostDisplay> HostDisplay::CreateDisplayForAPI(RenderAPI api)
switch (api) switch (api)
{ {
#ifdef _WIN32 #ifdef _WIN32
case HostDisplay::RenderAPI::D3D11: case RenderAPI::D3D11:
return std::make_unique<D3D11HostDisplay>(); return std::make_unique<D3D11HostDisplay>();
#endif #endif
case HostDisplay::RenderAPI::OpenGL: case RenderAPI::OpenGL:
case HostDisplay::RenderAPI::OpenGLES: case RenderAPI::OpenGLES:
return std::make_unique<OpenGLHostDisplay>(); return std::make_unique<OpenGLHostDisplay>();
#ifdef ENABLE_VULKAN #ifdef ENABLE_VULKAN

View File

@ -266,6 +266,7 @@ const char* Pcsx2Config::GSOptions::GetRendererName(GSRendererType type)
case GSRendererType::Auto: return "Auto"; case GSRendererType::Auto: return "Auto";
case GSRendererType::DX11: return "Direct3D 11"; case GSRendererType::DX11: return "Direct3D 11";
case GSRendererType::OGL: return "OpenGL"; case GSRendererType::OGL: return "OpenGL";
case GSRendererType::VK: return "Vulkan";
case GSRendererType::SW: return "Software"; case GSRendererType::SW: return "Software";
case GSRendererType::Null: return "Null"; case GSRendererType::Null: return "Null";
default: return ""; default: return "";
@ -580,7 +581,7 @@ void Pcsx2Config::GSOptions::MaskUserHacks()
bool Pcsx2Config::GSOptions::UseHardwareRenderer() const bool Pcsx2Config::GSOptions::UseHardwareRenderer() const
{ {
return (Renderer == GSRendererType::DX11 || Renderer == GSRendererType::OGL); return (Renderer == GSRendererType::DX11 || Renderer == GSRendererType::OGL || Renderer == GSRendererType::VK);
} }
VsyncMode Pcsx2Config::GetEffectiveVsyncMode() const VsyncMode Pcsx2Config::GetEffectiveVsyncMode() const

View File

@ -87,6 +87,9 @@ Dialogs::GSDumpDialog::GSDumpDialog(wxWindow* parent)
rdoverrides.Add("None"); rdoverrides.Add("None");
rdoverrides.Add(Pcsx2Config::GSOptions::GetRendererName(GSRendererType::SW)); rdoverrides.Add(Pcsx2Config::GSOptions::GetRendererName(GSRendererType::SW));
rdoverrides.Add(Pcsx2Config::GSOptions::GetRendererName(GSRendererType::OGL)); rdoverrides.Add(Pcsx2Config::GSOptions::GetRendererName(GSRendererType::OGL));
#ifdef ENABLE_VULKAN
rdoverrides.Add(Pcsx2Config::GSOptions::GetRendererName(GSRendererType::VK));
#endif
#if defined(_WIN32) #if defined(_WIN32)
rdoverrides.Add(Pcsx2Config::GSOptions::GetRendererName(GSRendererType::DX11)); rdoverrides.Add(Pcsx2Config::GSOptions::GetRendererName(GSRendererType::DX11));
#endif #endif
@ -719,10 +722,18 @@ void Dialogs::GSDumpDialog::GSThread::ExecuteTaskInThread()
case 2: case 2:
renderer = GSRendererType::OGL; renderer = GSRendererType::OGL;
break; break;
// D3D11 #ifdef ENABLE_VULKAN
// Vulkan
case 3: case 3:
renderer = GSRendererType::VK;
break;
#endif
#ifdef _WIN32
// D3D11
case 4: // WIN32 implies WITH_VULKAN so this is okay
renderer = GSRendererType::DX11; renderer = GSRendererType::DX11;
break; break;
#endif
default: default:
break; break;
} }

View File

@ -313,6 +313,8 @@
<ClCompile Include="Gif_Unit.cpp" /> <ClCompile Include="Gif_Unit.cpp" />
<ClCompile Include="GS\Renderers\DX11\D3D.cpp" /> <ClCompile Include="GS\Renderers\DX11\D3D.cpp" />
<ClCompile Include="GS\Window\GSwxDialog.cpp" /> <ClCompile Include="GS\Window\GSwxDialog.cpp" />
<ClCompile Include="GS\Renderers\Vulkan\GSDeviceVK.cpp" />
<ClCompile Include="GS\Renderers\Vulkan\GSTextureVK.cpp" />
<ClCompile Include="gui\AppHost.cpp" /> <ClCompile Include="gui\AppHost.cpp" />
<ClCompile Include="gui\AppUserMode.cpp" /> <ClCompile Include="gui\AppUserMode.cpp" />
<ClCompile Include="gui\CheckedStaticBox.cpp" /> <ClCompile Include="gui\CheckedStaticBox.cpp" />
@ -755,6 +757,8 @@
<ClInclude Include="Gif_Unit.h" /> <ClInclude Include="Gif_Unit.h" />
<ClInclude Include="GS\Renderers\DX11\D3D.h" /> <ClInclude Include="GS\Renderers\DX11\D3D.h" />
<ClInclude Include="GS\Window\GSwxDialog.h" /> <ClInclude Include="GS\Window\GSwxDialog.h" />
<ClInclude Include="GS\Renderers\Vulkan\GSDeviceVK.h" />
<ClInclude Include="GS\Renderers\Vulkan\GSTextureVK.h" />
<ClInclude Include="gui\AppHost.h" /> <ClInclude Include="gui\AppHost.h" />
<ClInclude Include="gui\CheckedStaticBox.h" /> <ClInclude Include="gui\CheckedStaticBox.h" />
<ClInclude Include="gui\i18n.h" /> <ClInclude Include="gui\i18n.h" />
@ -1179,4 +1183,4 @@
</ItemGroup> </ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets" /> <ImportGroup Label="ExtensionTargets" />
</Project> </Project>

View File

@ -280,6 +280,9 @@
<Filter Include="Host"> <Filter Include="Host">
<UniqueIdentifier>{65f21394-287a-471b-a0c1-d8f0d5d95a81}</UniqueIdentifier> <UniqueIdentifier>{65f21394-287a-471b-a0c1-d8f0d5d95a81}</UniqueIdentifier>
</Filter> </Filter>
<Filter Include="System\Ps2\GS\Renderers\Vulkan">
<UniqueIdentifier>{98829aa9-bb81-4564-bd6e-128719c4faa0}</UniqueIdentifier>
</Filter>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="Utilities\folderdesc.txt"> <None Include="Utilities\folderdesc.txt">
@ -1670,6 +1673,12 @@
<ClCompile Include="Frontend\imgui_impl_vulkan.cpp"> <ClCompile Include="Frontend\imgui_impl_vulkan.cpp">
<Filter>Host</Filter> <Filter>Host</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="GS\Renderers\Vulkan\GSTextureVK.cpp">
<Filter>System\Ps2\GS\Renderers\Vulkan</Filter>
</ClCompile>
<ClCompile Include="GS\Renderers\Vulkan\GSDeviceVK.cpp">
<Filter>System\Ps2\GS\Renderers\Vulkan</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="Patch.h"> <ClInclude Include="Patch.h">
@ -2788,6 +2797,13 @@
<ClInclude Include="Frontend\VulkanHostDisplay.h"> <ClInclude Include="Frontend\VulkanHostDisplay.h">
<Filter>Host</Filter> <Filter>Host</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="GS\Renderers\Vulkan\GSTextureVK.h">
<Filter>System\Ps2\GS\Renderers\Vulkan</Filter>
</ClInclude>
<ClInclude Include="GS\Renderers\Vulkan\GSDeviceVK.h">
<Filter>System\Ps2\GS\Renderers\Vulkan</Filter>
</ClInclude>
<ClInclude Include="GS.h" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ResourceCompile Include="windows\wxResources.rc"> <ResourceCompile Include="windows\wxResources.rc">