mirror of https://github.com/PCSX2/pcsx2.git
GS: Add Vulkan renderer
This commit is contained in:
parent
c4ab6280c6
commit
1a8a5a5e8e
|
@ -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
|
|
@ -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
|
|
@ -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
|
@ -32,7 +32,6 @@ enum : u32
|
|||
MAX_COMBINED_IMAGE_SAMPLER_DESCRIPTORS_PER_FRAME = 2 * MAX_DRAW_CALLS_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_STORAGE_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
|
||||
};
|
||||
|
@ -406,7 +405,9 @@ namespace Vulkan
|
|||
if (g_vulkan_context->m_debug_messenger_callback != VK_NULL_HANDLE)
|
||||
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();
|
||||
|
||||
g_vulkan_context.reset();
|
||||
|
@ -701,8 +702,7 @@ namespace Vulkan
|
|||
VkDescriptorPoolSize pool_sizes[] = {
|
||||
{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_INPUT_ATTACHMENT, MAX_STORAGE_IMAGE_DESCRIPTORS_PER_FRAME},
|
||||
{VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, MAX_STORAGE_IMAGE_DESCRIPTORS_PER_FRAME},
|
||||
{VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, MAX_INPUT_ATTACHMENT_IMAGE_DESCRIPTORS_PER_FRAME},
|
||||
};
|
||||
|
||||
VkDescriptorPoolCreateInfo pool_create_info = {VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, nullptr, 0,
|
||||
|
|
|
@ -709,6 +709,17 @@ set(pcsx2GSHeaders
|
|||
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)
|
||||
list(APPEND pcsx2SPU2Sources
|
||||
SPU2/Windows/CfgHelpers.cpp
|
||||
|
|
|
@ -108,6 +108,7 @@ enum class GSRendererType : s8
|
|||
Null = 11,
|
||||
OGL = 12,
|
||||
SW = 13,
|
||||
VK = 14,
|
||||
};
|
||||
|
||||
enum class GSInterlaceMode : u8
|
||||
|
|
|
@ -41,6 +41,10 @@
|
|||
#include "pcsx2/HostSettings.h"
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_VULKAN
|
||||
#include "Renderers/Vulkan/GSDeviceVK.h"
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#include "Renderers/DX11/GSDevice11.h"
|
||||
|
@ -154,6 +158,9 @@ static HostDisplay::RenderAPI GetAPIForRenderer(GSRendererType renderer)
|
|||
#endif
|
||||
return HostDisplay::RenderAPI::OpenGL;
|
||||
|
||||
case GSRendererType::VK:
|
||||
return HostDisplay::RenderAPI::Vulkan;
|
||||
|
||||
#ifdef _WIN32
|
||||
case GSRendererType::DX11:
|
||||
case GSRendererType::SW:
|
||||
|
@ -183,6 +190,12 @@ static bool DoGSOpen(GSRendererType renderer, u8* basemem)
|
|||
g_gs_device = std::make_unique<GSDeviceOGL>();
|
||||
break;
|
||||
|
||||
#ifdef ENABLE_VULKAN
|
||||
case HostDisplay::RenderAPI::Vulkan:
|
||||
g_gs_device = std::make_unique<GSDeviceVK>();
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
Console.Error("Unknown render API %u", static_cast<unsigned>(display->GetRenderAPI()));
|
||||
return false;
|
||||
|
@ -1123,6 +1136,9 @@ void GSApp::Init()
|
|||
m_gs_renderers.push_back(GSSetting(static_cast<u32>(GSRendererType::DX11), "Direct3D 11", ""));
|
||||
#endif
|
||||
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", ""));
|
||||
|
||||
// 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
|
||||
// Avoid to clutter the ini file with useless options
|
||||
#if defined(ENABLE_VULKAN) || defined(_WIN32)
|
||||
m_default_configuration["Adapter"] = "";
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
// Per OS option.
|
||||
m_default_configuration["Adapter"] = "";
|
||||
m_default_configuration["CaptureFileName"] = "";
|
||||
m_default_configuration["CaptureVideoCodecDisplayName"] = "";
|
||||
m_default_configuration["dx_break_on_severity"] = "0";
|
||||
|
|
|
@ -51,13 +51,13 @@ const char* shaderName(ShaderConvert value)
|
|||
std::unique_ptr<GSDevice> g_gs_device;
|
||||
|
||||
GSDevice::GSDevice()
|
||||
: m_rbswapped(false)
|
||||
, m_merge(NULL)
|
||||
: m_merge(NULL)
|
||||
, m_weavebob(NULL)
|
||||
, m_blend(NULL)
|
||||
, m_target_tmp(NULL)
|
||||
, m_current(NULL)
|
||||
, m_frame(0)
|
||||
, m_rbswapped(false)
|
||||
{
|
||||
memset(&m_vertex, 0, sizeof(m_vertex));
|
||||
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 bool prefer_new_texture = (m_features.prefer_new_textures && type == GSTexture::Type::Texture && !prefer_reuse);
|
||||
|
||||
GSTexture* t = nullptr;
|
||||
auto fallback = m_pool.end();
|
||||
|
||||
for (auto i = m_pool.begin(); i != m_pool.end(); ++i)
|
||||
{
|
||||
|
@ -118,18 +120,34 @@ GSTexture* GSDevice::FetchSurface(GSTexture::Type type, int w, int h, GSTexture:
|
|||
|
||||
if (t->GetType() == type && t->GetFormat() == format && t->GetSize() == size)
|
||||
{
|
||||
m_pool.erase(i);
|
||||
break;
|
||||
if (!prefer_new_texture)
|
||||
{
|
||||
m_pool.erase(i);
|
||||
break;
|
||||
}
|
||||
else if (fallback == m_pool.end())
|
||||
{
|
||||
fallback = i;
|
||||
}
|
||||
}
|
||||
|
||||
t = nullptr;
|
||||
}
|
||||
|
||||
if (!t)
|
||||
t = CreateSurface(type, w, h, format);
|
||||
|
||||
if (!t)
|
||||
throw std::bad_alloc();
|
||||
{
|
||||
if (m_pool.size() >= MAX_POOLED_TEXTURES && fallback != m_pool.end())
|
||||
{
|
||||
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.
|
||||
|
||||
|
@ -195,7 +213,7 @@ void GSDevice::Recycle(GSTexture* t)
|
|||
|
||||
//printf("%d\n",m_pool.size());
|
||||
|
||||
while (m_pool.size() > 300)
|
||||
while (m_pool.size() > MAX_POOLED_TEXTURES)
|
||||
{
|
||||
delete m_pool.back();
|
||||
|
||||
|
@ -225,32 +243,32 @@ void GSDevice::PurgePool()
|
|||
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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)
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -413,7 +431,7 @@ bool GSDevice::ResizeTexture(GSTexture** t, GSTexture::Type type, int w, int h,
|
|||
GSTexture::Format fmt = t2 ? t2->GetFormat() : GetDefaultTextureFormat(type);
|
||||
delete t2;
|
||||
|
||||
t2 = FetchSurface(type, w, h, fmt, clear);
|
||||
t2 = FetchSurface(type, w, h, fmt, clear, prefer_reuse);
|
||||
|
||||
*t = t2;
|
||||
}
|
||||
|
@ -421,9 +439,9 @@ bool GSDevice::ResizeTexture(GSTexture** t, GSTexture::Type type, int w, int h,
|
|||
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)
|
||||
|
|
|
@ -197,7 +197,7 @@ struct alignas(16) GSHWDrawConfig
|
|||
// Flat/goround shading
|
||||
u32 iip : 1;
|
||||
// Pixel test
|
||||
u32 date : 3;
|
||||
u32 date : 4;
|
||||
u32 atst : 3;
|
||||
// Color sampling
|
||||
u32 fst : 1; // Investigate to do it on the VS
|
||||
|
@ -248,12 +248,17 @@ struct alignas(16) GSHWDrawConfig
|
|||
// Scan mask
|
||||
u32 scanmsk : 2;
|
||||
|
||||
u32 _free2 : 3;
|
||||
u32 _free2 : 2;
|
||||
};
|
||||
|
||||
u64 key;
|
||||
};
|
||||
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
|
||||
{
|
||||
|
@ -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 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 prefer_new_textures : 1; ///< Allocate textures up to the pool size before reusing them, to avoid render pass restarts.
|
||||
FeatureSupport()
|
||||
{
|
||||
memset(this, 0, sizeof(*this));
|
||||
|
@ -527,7 +533,8 @@ protected:
|
|||
static const int m_NO_BLEND = 0;
|
||||
static const int m_MERGE_BLEND = m_blendMap.size() - 1;
|
||||
|
||||
bool m_rbswapped;
|
||||
static constexpr u32 MAX_POOLED_TEXTURES = 300;
|
||||
|
||||
HostDisplay* m_display;
|
||||
GSTexture* m_merge;
|
||||
GSTexture* m_weavebob;
|
||||
|
@ -543,11 +550,11 @@ protected:
|
|||
size_t start, count, limit;
|
||||
} m_index;
|
||||
unsigned int m_frame; // for ageing the pool
|
||||
bool m_linear_present;
|
||||
bool m_rbswapped;
|
||||
FeatureSupport m_features;
|
||||
|
||||
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 DoInterlace(GSTexture* sTex, GSTexture* dTex, int shader, bool linear, float yoffset) = 0;
|
||||
|
@ -561,6 +568,7 @@ public:
|
|||
virtual ~GSDevice();
|
||||
|
||||
__fi HostDisplay* GetDisplay() const { return m_display; }
|
||||
__fi unsigned int GetFrameNumber() const { return m_frame; }
|
||||
|
||||
void Recycle(GSTexture* t);
|
||||
|
||||
|
@ -606,7 +614,7 @@ public:
|
|||
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* 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::Format GetDefaultTextureFormat(GSTexture::Type type);
|
||||
|
||||
|
@ -638,8 +646,8 @@ public:
|
|||
void ShadeBoost();
|
||||
void ExternalFX();
|
||||
|
||||
bool ResizeTexture(GSTexture** t, GSTexture::Type type, int w, int h, bool clear = true);
|
||||
bool ResizeTexture(GSTexture** t, 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 prefer_reuse = false);
|
||||
bool ResizeTarget(GSTexture** t, int w, int h);
|
||||
bool ResizeTarget(GSTexture** t);
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@ GSTexture::GSTexture()
|
|||
, m_mipmap_levels(0)
|
||||
, m_type(Type::Invalid)
|
||||
, m_format(Format::Invalid)
|
||||
, m_state(State::Dirty)
|
||||
, m_sparse(false)
|
||||
, m_needs_mipmaps_generated(true)
|
||||
, last_frame_used(0)
|
||||
|
|
|
@ -49,6 +49,13 @@ public:
|
|||
Int32, ///< Int32 texture for date emulation
|
||||
};
|
||||
|
||||
enum class State : u8
|
||||
{
|
||||
Dirty,
|
||||
Cleared,
|
||||
Invalidated
|
||||
};
|
||||
|
||||
protected:
|
||||
GSVector2 m_scale;
|
||||
GSVector2i m_size;
|
||||
|
@ -57,6 +64,7 @@ protected:
|
|||
int m_mipmap_levels;
|
||||
Type m_type;
|
||||
Format m_format;
|
||||
State m_state;
|
||||
bool m_sparse;
|
||||
bool m_needs_mipmaps_generated;
|
||||
|
||||
|
@ -91,6 +99,23 @@ public:
|
|||
Type GetType() const { return m_type; }
|
||||
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 ClearMipmapGenerationFlag() { m_needs_mipmaps_generated = false; }
|
||||
|
||||
|
|
|
@ -43,6 +43,7 @@ GSDevice11::GSDevice11()
|
|||
m_features.provoking_vertex_last = false;
|
||||
m_features.point_expand = false;
|
||||
m_features.line_expand = false;
|
||||
m_features.prefer_new_textures = false;
|
||||
}
|
||||
|
||||
bool GSDevice11::SetFeatureLevel(D3D_FEATURE_LEVEL level, bool compat_mode)
|
||||
|
|
|
@ -185,6 +185,12 @@ GSRendererHW::~GSRendererHW()
|
|||
delete m_tc;
|
||||
}
|
||||
|
||||
void GSRendererHW::Destroy()
|
||||
{
|
||||
m_tc->RemoveAll();
|
||||
GSRenderer::Destroy();
|
||||
}
|
||||
|
||||
void GSRendererHW::SetGameCRC(u32 crc, int options)
|
||||
{
|
||||
GSRenderer::SetGameCRC(crc, options);
|
||||
|
|
|
@ -166,6 +166,8 @@ public:
|
|||
GSRendererHW();
|
||||
virtual ~GSRendererHW() override;
|
||||
|
||||
void Destroy() override;
|
||||
|
||||
void SetGameCRC(u32 crc, int options) override;
|
||||
bool CanUpscale() override;
|
||||
int GetUpscaleMultiplier() override;
|
||||
|
|
|
@ -1297,7 +1297,7 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con
|
|||
int h = (int)(scale.y * th);
|
||||
|
||||
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);
|
||||
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
|
||||
|
||||
// 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
|
||||
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)
|
||||
GSTexture* sTex = dst->m_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);
|
||||
src->m_texture = dTex;
|
||||
|
||||
|
|
|
@ -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.texture_barrier = true;
|
||||
m_features.provoking_vertex_last = true;
|
||||
m_features.prefer_new_textures = false;
|
||||
|
||||
GLint point_range[2] = {};
|
||||
GLint line_range[2] = {};
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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 = {};
|
||||
};
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
};
|
|
@ -23,6 +23,10 @@
|
|||
#include "Frontend/D3D11HostDisplay.h"
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_VULKAN
|
||||
#include "Frontend/VulkanHostDisplay.h"
|
||||
#endif
|
||||
|
||||
using namespace GSSettingsDialog;
|
||||
|
||||
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, "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_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* 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 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(); };
|
||||
|
||||
PaddedBoxSizer<wxStaticBoxSizer> rend_hacks_box (wxVERTICAL, this, "Renderer Hacks");
|
||||
|
@ -379,7 +383,7 @@ HacksTab::HacksTab(wxWindow* parent)
|
|||
|
||||
// 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, "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
|
||||
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<wxStaticBoxSizer> shader_box(wxVERTICAL, this, "Custom Shader");
|
||||
|
||||
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);
|
||||
auto not_vk_prereq = [this] { return !m_is_vk_hw; };
|
||||
|
||||
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");
|
||||
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());
|
||||
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)");
|
||||
auto* ext_shader_grid = new wxFlexGridSizer(2, space, space);
|
||||
|
@ -687,6 +693,11 @@ void Dialog::RendererChange()
|
|||
case GSRendererType::DX11:
|
||||
list = D3D11HostDisplay::StaticGetAdapterAndModeList();
|
||||
break;
|
||||
#endif
|
||||
#ifdef ENABLE_VULKAN
|
||||
case GSRendererType::VK:
|
||||
list = VulkanHostDisplay::StaticGetAdapterAndModeList(nullptr);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
|
@ -763,13 +774,15 @@ void Dialog::Update()
|
|||
else
|
||||
{
|
||||
// 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;
|
||||
m_hacks_panel->m_is_native_res = !is_hw || !is_upscale;
|
||||
m_hacks_panel->m_is_hardware = is_hw;
|
||||
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_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_ui.Update();
|
||||
|
|
|
@ -127,6 +127,7 @@ namespace GSSettingsDialog
|
|||
bool m_is_hardware = false;
|
||||
bool m_is_native_res = false;
|
||||
bool m_is_ogl_hw = false;
|
||||
bool m_is_vk_hw = false;
|
||||
|
||||
HacksTab(wxWindow* parent);
|
||||
void Load() { m_ui.Load(); }
|
||||
|
@ -161,6 +162,7 @@ namespace GSSettingsDialog
|
|||
{
|
||||
public:
|
||||
GSUIElementHolder m_ui;
|
||||
bool m_is_vk_hw = false;
|
||||
|
||||
PostTab(wxWindow* parent);
|
||||
void Load() { m_ui.Load(); }
|
||||
|
|
|
@ -119,12 +119,12 @@ std::unique_ptr<HostDisplay> HostDisplay::CreateDisplayForAPI(RenderAPI api)
|
|||
switch (api)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
case HostDisplay::RenderAPI::D3D11:
|
||||
case RenderAPI::D3D11:
|
||||
return std::make_unique<D3D11HostDisplay>();
|
||||
#endif
|
||||
|
||||
case HostDisplay::RenderAPI::OpenGL:
|
||||
case HostDisplay::RenderAPI::OpenGLES:
|
||||
case RenderAPI::OpenGL:
|
||||
case RenderAPI::OpenGLES:
|
||||
return std::make_unique<OpenGLHostDisplay>();
|
||||
|
||||
#ifdef ENABLE_VULKAN
|
||||
|
|
|
@ -266,6 +266,7 @@ const char* Pcsx2Config::GSOptions::GetRendererName(GSRendererType type)
|
|||
case GSRendererType::Auto: return "Auto";
|
||||
case GSRendererType::DX11: return "Direct3D 11";
|
||||
case GSRendererType::OGL: return "OpenGL";
|
||||
case GSRendererType::VK: return "Vulkan";
|
||||
case GSRendererType::SW: return "Software";
|
||||
case GSRendererType::Null: return "Null";
|
||||
default: return "";
|
||||
|
@ -580,7 +581,7 @@ void Pcsx2Config::GSOptions::MaskUserHacks()
|
|||
|
||||
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
|
||||
|
|
|
@ -87,6 +87,9 @@ Dialogs::GSDumpDialog::GSDumpDialog(wxWindow* parent)
|
|||
rdoverrides.Add("None");
|
||||
rdoverrides.Add(Pcsx2Config::GSOptions::GetRendererName(GSRendererType::SW));
|
||||
rdoverrides.Add(Pcsx2Config::GSOptions::GetRendererName(GSRendererType::OGL));
|
||||
#ifdef ENABLE_VULKAN
|
||||
rdoverrides.Add(Pcsx2Config::GSOptions::GetRendererName(GSRendererType::VK));
|
||||
#endif
|
||||
#if defined(_WIN32)
|
||||
rdoverrides.Add(Pcsx2Config::GSOptions::GetRendererName(GSRendererType::DX11));
|
||||
#endif
|
||||
|
@ -719,10 +722,18 @@ void Dialogs::GSDumpDialog::GSThread::ExecuteTaskInThread()
|
|||
case 2:
|
||||
renderer = GSRendererType::OGL;
|
||||
break;
|
||||
// D3D11
|
||||
#ifdef ENABLE_VULKAN
|
||||
// Vulkan
|
||||
case 3:
|
||||
renderer = GSRendererType::VK;
|
||||
break;
|
||||
#endif
|
||||
#ifdef _WIN32
|
||||
// D3D11
|
||||
case 4: // WIN32 implies WITH_VULKAN so this is okay
|
||||
renderer = GSRendererType::DX11;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -313,6 +313,8 @@
|
|||
<ClCompile Include="Gif_Unit.cpp" />
|
||||
<ClCompile Include="GS\Renderers\DX11\D3D.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\AppUserMode.cpp" />
|
||||
<ClCompile Include="gui\CheckedStaticBox.cpp" />
|
||||
|
@ -755,6 +757,8 @@
|
|||
<ClInclude Include="Gif_Unit.h" />
|
||||
<ClInclude Include="GS\Renderers\DX11\D3D.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\CheckedStaticBox.h" />
|
||||
<ClInclude Include="gui\i18n.h" />
|
||||
|
|
|
@ -280,6 +280,9 @@
|
|||
<Filter Include="Host">
|
||||
<UniqueIdentifier>{65f21394-287a-471b-a0c1-d8f0d5d95a81}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="System\Ps2\GS\Renderers\Vulkan">
|
||||
<UniqueIdentifier>{98829aa9-bb81-4564-bd6e-128719c4faa0}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="Utilities\folderdesc.txt">
|
||||
|
@ -1670,6 +1673,12 @@
|
|||
<ClCompile Include="Frontend\imgui_impl_vulkan.cpp">
|
||||
<Filter>Host</Filter>
|
||||
</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>
|
||||
<ClInclude Include="Patch.h">
|
||||
|
@ -2788,6 +2797,13 @@
|
|||
<ClInclude Include="Frontend\VulkanHostDisplay.h">
|
||||
<Filter>Host</Filter>
|
||||
</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>
|
||||
<ResourceCompile Include="windows\wxResources.rc">
|
||||
|
|
Loading…
Reference in New Issue