diff --git a/Source/Core/VideoCommon/GeometryShaderGen.cpp b/Source/Core/VideoCommon/GeometryShaderGen.cpp index 99e546a363..90b9d01e16 100644 --- a/Source/Core/VideoCommon/GeometryShaderGen.cpp +++ b/Source/Core/VideoCommon/GeometryShaderGen.cpp @@ -55,7 +55,7 @@ ShaderCode GenerateGeometryShaderCode(APIType ApiType, const geometry_shader_uid if (uid_data->wireframe) vertex_out++; - if (ApiType == APIType::OpenGL) + if (ApiType == APIType::OpenGL || ApiType == APIType::Vulkan) { // Insert layout parameters if (g_ActiveConfig.backend_info.bSupportsGSInstancing) @@ -77,10 +77,11 @@ ShaderCode GenerateGeometryShaderCode(APIType ApiType, const geometry_shader_uid out.Write("%s", s_lighting_struct); // uniforms - if (ApiType == APIType::OpenGL) + if (ApiType == APIType::OpenGL || ApiType == APIType::Vulkan) out.Write("UBO_BINDING(std140, 3) uniform GSBlock {\n"); else out.Write("cbuffer GSBlock {\n"); + out.Write("\tfloat4 " I_STEREOPARAMS ";\n" "\tfloat4 " I_LINEPTPARAMS ";\n" "\tint4 " I_TEXOFFSET ";\n" @@ -91,7 +92,7 @@ ShaderCode GenerateGeometryShaderCode(APIType ApiType, const geometry_shader_uid ""); out.Write("};\n"); - if (ApiType == APIType::OpenGL) + if (ApiType == APIType::OpenGL || ApiType == APIType::Vulkan) { if (g_ActiveConfig.backend_info.bSupportsGSInstancing) out.Write("#define InstanceID gl_InvocationID\n"); @@ -145,7 +146,7 @@ ShaderCode GenerateGeometryShaderCode(APIType ApiType, const geometry_shader_uid if (uid_data->primitive_type == PRIMITIVE_LINES) { - if (ApiType == APIType::OpenGL) + if (ApiType == APIType::OpenGL || ApiType == APIType::Vulkan) { out.Write("\tVS_OUTPUT start, end;\n"); AssignVSOutputMembers(out, "start", "vs[0]", uid_data->numTexGens, uid_data->pixel_lighting); @@ -176,7 +177,7 @@ ShaderCode GenerateGeometryShaderCode(APIType ApiType, const geometry_shader_uid } else if (uid_data->primitive_type == PRIMITIVE_POINTS) { - if (ApiType == APIType::OpenGL) + if (ApiType == APIType::OpenGL || ApiType == APIType::Vulkan) { out.Write("\tVS_OUTPUT center;\n"); AssignVSOutputMembers(out, "center", "vs[0]", uid_data->numTexGens, uid_data->pixel_lighting); @@ -207,7 +208,7 @@ ShaderCode GenerateGeometryShaderCode(APIType ApiType, const geometry_shader_uid out.Write("\tfor (int i = 0; i < %d; ++i) {\n", vertex_in); - if (ApiType == APIType::OpenGL) + if (ApiType == APIType::OpenGL || ApiType == APIType::Vulkan) { out.Write("\tVS_OUTPUT f;\n"); AssignVSOutputMembers(out, "f", "vs[i]", uid_data->numTexGens, uid_data->pixel_lighting); @@ -230,7 +231,7 @@ ShaderCode GenerateGeometryShaderCode(APIType ApiType, const geometry_shader_uid { // Select the output layer out.Write("\tps.layer = eye;\n"); - if (ApiType == APIType::OpenGL) + if (ApiType == APIType::OpenGL || ApiType == APIType::Vulkan) out.Write("\tgl_Layer = eye;\n"); // For stereoscopy add a small horizontal offset in Normalized Device Coordinates proportional @@ -329,12 +330,19 @@ static void EmitVertex(ShaderCode& out, const geometry_shader_uid_data* uid_data } AssignVSOutputMembers(out, "ps", vertex, uid_data->numTexGens, uid_data->pixel_lighting); } + else if (ApiType == APIType::Vulkan) + { + // Vulkan NDC space has Y pointing down (right-handed NDC space). + out.Write("\tgl_Position = %s.pos;\n", vertex); + out.Write("\tgl_Position.y = -gl_Position.y;\n"); + AssignVSOutputMembers(out, "ps", vertex, uid_data->numTexGens, uid_data->pixel_lighting); + } else { out.Write("\tps.o = %s;\n", vertex); } - if (ApiType == APIType::OpenGL) + if (ApiType == APIType::OpenGL || ApiType == APIType::Vulkan) out.Write("\tEmitVertex();\n"); else out.Write("\toutput.Append(ps);\n"); @@ -345,7 +353,7 @@ static void EndPrimitive(ShaderCode& out, const geometry_shader_uid_data* uid_da if (uid_data->wireframe) EmitVertex(out, uid_data, "first", ApiType); - if (ApiType == APIType::OpenGL) + if (ApiType == APIType::OpenGL || ApiType == APIType::Vulkan) out.Write("\tEndPrimitive();\n"); else out.Write("\toutput.RestartStrip();\n"); diff --git a/Source/Core/VideoCommon/PixelShaderGen.cpp b/Source/Core/VideoCommon/PixelShaderGen.cpp index 65722fb7d4..4038d9329c 100644 --- a/Source/Core/VideoCommon/PixelShaderGen.cpp +++ b/Source/Core/VideoCommon/PixelShaderGen.cpp @@ -384,6 +384,17 @@ ShaderCode GeneratePixelShaderCode(DSTALPHA_MODE dstAlphaMode, APIType ApiType, { out.Write("SAMPLER_BINDING(0) uniform sampler2DArray samp[8];\n"); } + else if (ApiType == APIType::Vulkan) + { + out.Write("SAMPLER_BINDING(0) uniform sampler2DArray samp0;\n"); + out.Write("SAMPLER_BINDING(1) uniform sampler2DArray samp1;\n"); + out.Write("SAMPLER_BINDING(2) uniform sampler2DArray samp2;\n"); + out.Write("SAMPLER_BINDING(3) uniform sampler2DArray samp3;\n"); + out.Write("SAMPLER_BINDING(4) uniform sampler2DArray samp4;\n"); + out.Write("SAMPLER_BINDING(5) uniform sampler2DArray samp5;\n"); + out.Write("SAMPLER_BINDING(6) uniform sampler2DArray samp6;\n"); + out.Write("SAMPLER_BINDING(7) uniform sampler2DArray samp7;\n"); + } else // D3D { // Declare samplers @@ -393,7 +404,7 @@ ShaderCode GeneratePixelShaderCode(DSTALPHA_MODE dstAlphaMode, APIType ApiType, } out.Write("\n"); - if (ApiType == APIType::OpenGL) + if (ApiType == APIType::OpenGL || ApiType == APIType::Vulkan) out.Write("UBO_BINDING(std140, 1) uniform PSBlock {\n"); else out.Write("cbuffer PSBlock : register(b0) {\n"); @@ -416,7 +427,7 @@ ShaderCode GeneratePixelShaderCode(DSTALPHA_MODE dstAlphaMode, APIType ApiType, { out.Write("%s", s_lighting_struct); - if (ApiType == APIType::OpenGL) + if (ApiType == APIType::OpenGL || ApiType == APIType::Vulkan) out.Write("UBO_BINDING(std140, 2) uniform VSBlock {\n"); else out.Write("cbuffer VSBlock : register(b1) {\n"); @@ -427,7 +438,7 @@ ShaderCode GeneratePixelShaderCode(DSTALPHA_MODE dstAlphaMode, APIType ApiType, if (uid_data->bounding_box) { - if (ApiType == APIType::OpenGL) + if (ApiType == APIType::OpenGL || ApiType == APIType::Vulkan) { out.Write("SSBO_BINDING(0) buffer BBox {\n" "\tint4 bbox_data;\n" @@ -480,7 +491,7 @@ ShaderCode GeneratePixelShaderCode(DSTALPHA_MODE dstAlphaMode, APIType ApiType, // ARB_image_load_store extension yet. // D3D11 also has a way to force the driver to enable early-z, so we're fine here. - if (ApiType == APIType::OpenGL) + if (ApiType == APIType::OpenGL || ApiType == APIType::Vulkan) { // This is a #define which signals whatever early-z method the driver supports. out.Write("FORCE_EARLY_Z; \n"); @@ -502,7 +513,7 @@ ShaderCode GeneratePixelShaderCode(DSTALPHA_MODE dstAlphaMode, APIType ApiType, warn_once = false; } - if (ApiType == APIType::OpenGL) + if (ApiType == APIType::OpenGL || ApiType == APIType::Vulkan) { if (dstAlphaMode == DSTALPHA_DUAL_SOURCE_BLEND) { @@ -517,7 +528,8 @@ ShaderCode GeneratePixelShaderCode(DSTALPHA_MODE dstAlphaMode, APIType ApiType, if (uid_data->per_pixel_depth) out.Write("#define depth gl_FragDepth\n"); - if (g_ActiveConfig.backend_info.bSupportsGeometryShaders) + // We need to always use output blocks for Vulkan, but geometry shaders are also optional. + if (g_ActiveConfig.backend_info.bSupportsGeometryShaders || ApiType == APIType::Vulkan) { out.Write("VARYING_LOCATION(0) in VertexData {\n"); GenerateVSOutputMembers( @@ -555,7 +567,7 @@ ShaderCode GeneratePixelShaderCode(DSTALPHA_MODE dstAlphaMode, APIType ApiType, out.Write("void main()\n{\n"); - if (g_ActiveConfig.backend_info.bSupportsGeometryShaders) + if (g_ActiveConfig.backend_info.bSupportsGeometryShaders || ApiType == APIType::Vulkan) { for (unsigned int i = 0; i < uid_data->genMode_numtexgens; ++i) out.Write("\tfloat3 uv%d = tex%d;\n", i, i); @@ -726,7 +738,7 @@ ShaderCode GeneratePixelShaderCode(DSTALPHA_MODE dstAlphaMode, APIType ApiType, } else { - if (ApiType == APIType::D3D) + if (ApiType == APIType::D3D || ApiType == APIType::Vulkan) out.Write("\tint zCoord = int((1.0 - rawpos.z) * 16777216.0);\n"); else out.Write("\tint zCoord = int(rawpos.z * 16777216.0);\n"); @@ -740,7 +752,7 @@ ShaderCode GeneratePixelShaderCode(DSTALPHA_MODE dstAlphaMode, APIType ApiType, // Note: z-textures are not written to depth buffer if early depth test is used if (uid_data->per_pixel_depth && uid_data->early_ztest) { - if (ApiType == APIType::D3D) + if (ApiType == APIType::D3D || ApiType == APIType::Vulkan) out.Write("\tdepth = 1.0 - float(zCoord) / 16777216.0;\n"); else out.Write("\tdepth = float(zCoord) / 16777216.0;\n"); @@ -761,7 +773,7 @@ ShaderCode GeneratePixelShaderCode(DSTALPHA_MODE dstAlphaMode, APIType ApiType, if (uid_data->per_pixel_depth && uid_data->late_ztest) { - if (ApiType == APIType::D3D) + if (ApiType == APIType::D3D || ApiType == APIType::Vulkan) out.Write("\tdepth = 1.0 - float(zCoord) / 16777216.0;\n"); else out.Write("\tdepth = float(zCoord) / 16777216.0;\n"); @@ -791,7 +803,8 @@ ShaderCode GeneratePixelShaderCode(DSTALPHA_MODE dstAlphaMode, APIType ApiType, if (uid_data->bounding_box) { - const char* atomic_op = ApiType == APIType::OpenGL ? "atomic" : "Interlocked"; + const char* atomic_op = + (ApiType == APIType::OpenGL || ApiType == APIType::Vulkan) ? "atomic" : "Interlocked"; out.Write("\tif(bbox_data[0] > int(rawpos.x)) %sMin(bbox_data[0], int(rawpos.x));\n" "\tif(bbox_data[1] < int(rawpos.x)) %sMax(bbox_data[1], int(rawpos.x));\n" "\tif(bbox_data[2] > int(rawpos.y)) %sMin(bbox_data[2], int(rawpos.y));\n" @@ -1138,12 +1151,21 @@ static void SampleTexture(ShaderCode& out, const char* texcoords, const char* te out.SetConstantsUsed(C_TEXDIMS + texmap, C_TEXDIMS + texmap); if (ApiType == APIType::D3D) + { out.Write("iround(255.0 * Tex[%d].Sample(samp[%d], float3(%s.xy * " I_TEXDIMS "[%d].xy, %s))).%s;\n", texmap, texmap, texcoords, texmap, stereo ? "layer" : "0.0", texswap); + } + else if (ApiType == APIType::Vulkan) + { + out.Write("iround(255.0 * texture(samp%d, float3(%s.xy * " I_TEXDIMS "[%d].xy, %s))).%s;\n", + texmap, texcoords, texmap, stereo ? "layer" : "0.0", texswap); + } else + { out.Write("iround(255.0 * texture(samp[%d], float3(%s.xy * " I_TEXDIMS "[%d].xy, %s))).%s;\n", texmap, texcoords, texmap, stereo ? "layer" : "0.0", texswap); + } } static const char* tevAlphaFuncsTable[] = { @@ -1195,7 +1217,10 @@ static void WriteAlphaTest(ShaderCode& out, const pixel_shader_uid_data* uid_dat if (dstAlphaMode == DSTALPHA_DUAL_SOURCE_BLEND) out.Write("\t\tocol1 = float4(0.0, 0.0, 0.0, 0.0);\n"); if (per_pixel_depth) - out.Write("\t\tdepth = %s;\n", (ApiType == APIType::D3D) ? "0.0" : "1.0"); + { + out.Write("\t\tdepth = %s;\n", + (ApiType == APIType::D3D || ApiType == APIType::Vulkan) ? "0.0" : "1.0"); + } // ZCOMPLOC HACK: if (!uid_data->alpha_test_use_zcomploc_hack) diff --git a/Source/Core/VideoCommon/TextureConversionShader.cpp b/Source/Core/VideoCommon/TextureConversionShader.cpp index 16ea016675..bd4e1349df 100644 --- a/Source/Core/VideoCommon/TextureConversionShader.cpp +++ b/Source/Core/VideoCommon/TextureConversionShader.cpp @@ -81,7 +81,10 @@ static void WriteSwizzler(char*& p, u32 format, APIType ApiType) { // left, top, of source rectangle within source texture // width of the destination rectangle, scale_factor (1 or 2) - WRITE(p, "uniform int4 position;\n"); + if (ApiType == APIType::Vulkan) + WRITE(p, "layout(std140, push_constant) uniform PCBlock { int4 position; } PC;\n"); + else + WRITE(p, "uniform int4 position;\n"); int blkW = TexDecoder_GetBlockWidthInTexels(format); int blkH = TexDecoder_GetBlockHeightInTexels(format); @@ -98,6 +101,17 @@ static void WriteSwizzler(char*& p, u32 format, APIType ApiType) " int2 sampleUv;\n" " int2 uv1 = int2(gl_FragCoord.xy);\n"); } + else if (ApiType == APIType::Vulkan) + { + WRITE(p, "SAMPLER_BINDING(0) uniform sampler2DArray samp0;\n"); + WRITE(p, "FRAGMENT_OUTPUT_LOCATION(0) out vec4 ocol0;\n"); + + WRITE(p, "void main()\n"); + WRITE(p, "{\n" + " int2 sampleUv;\n" + " int2 uv1 = int2(gl_FragCoord.xy);\n" + " int4 position = PC.position;\n"); + } else // D3D { WRITE(p, "sampler samp0 : register(s0);\n"); @@ -146,7 +160,7 @@ static void WriteSwizzler(char*& p, u32 format, APIType ApiType) static void WriteSampleColor(char*& p, const char* colorComp, const char* dest, int xoffset, APIType ApiType, bool depth = false) { - if (ApiType == APIType::OpenGL) + if (ApiType == APIType::OpenGL || ApiType == APIType::Vulkan) { WRITE(p, " %s = texture(samp0, float3(uv0 + float2(%d, 0) * sample_offset, 0.0)).%s;\n", dest, xoffset, colorComp); @@ -155,7 +169,10 @@ static void WriteSampleColor(char*& p, const char* colorComp, const char* dest, { WRITE(p, " %s = Tex0.Sample(samp0, float3(uv0 + float2(%d, 0) * sample_offset, 0.0)).%s;\n", dest, xoffset, colorComp); + } + if (ApiType == APIType::D3D || ApiType == APIType::Vulkan) + { // Handle D3D depth inversion. if (depth) WRITE(p, " %s = 1.0 - %s;\n", dest, dest); diff --git a/Source/Core/VideoCommon/VertexShaderGen.cpp b/Source/Core/VideoCommon/VertexShaderGen.cpp index 4801bc4797..7725331193 100644 --- a/Source/Core/VideoCommon/VertexShaderGen.cpp +++ b/Source/Core/VideoCommon/VertexShaderGen.cpp @@ -85,10 +85,11 @@ ShaderCode GenerateVertexShaderCode(APIType api_type, const vertex_shader_uid_da out.Write("%s", s_lighting_struct); // uniforms - if (api_type == APIType::OpenGL) + if (api_type == APIType::OpenGL || api_type == APIType::Vulkan) out.Write("UBO_BINDING(std140, 2) uniform VSBlock {\n"); else out.Write("cbuffer VSBlock {\n"); + out.Write(s_shader_uniforms); out.Write("};\n"); @@ -96,7 +97,7 @@ ShaderCode GenerateVertexShaderCode(APIType api_type, const vertex_shader_uid_da GenerateVSOutputMembers(out, api_type, uid_data->numTexGens, uid_data->pixel_lighting, ""); out.Write("};\n"); - if (api_type == APIType::OpenGL) + if (api_type == APIType::OpenGL || api_type == APIType::Vulkan) { out.Write("ATTRIBUTE_LOCATION(%d) in float4 rawpos;\n", SHADER_POSITION_ATTRIB); if (uid_data->components & VB_HAS_POSMTXIDX) @@ -123,7 +124,8 @@ ShaderCode GenerateVertexShaderCode(APIType api_type, const vertex_shader_uid_da } } - if (g_ActiveConfig.backend_info.bSupportsGeometryShaders) + // We need to always use output blocks for Vulkan, but geometry shaders are also optional. + if (g_ActiveConfig.backend_info.bSupportsGeometryShaders || api_type == APIType::Vulkan) { out.Write("VARYING_LOCATION(0) out VertexData {\n"); GenerateVSOutputMembers( @@ -463,9 +465,9 @@ ShaderCode GenerateVertexShaderCode(APIType api_type, const vertex_shader_uid_da // get rasterized correctly. out.Write("o.pos.xy = o.pos.xy - o.pos.w * " I_PIXELCENTERCORRECTION ".xy;\n"); - if (api_type == APIType::OpenGL) + if (api_type == APIType::OpenGL || api_type == APIType::Vulkan) { - if (g_ActiveConfig.backend_info.bSupportsGeometryShaders) + if (g_ActiveConfig.backend_info.bSupportsGeometryShaders || api_type == APIType::Vulkan) { AssignVSOutputMembers(out, "vs", "o", uid_data->numTexGens, uid_data->pixel_lighting); } @@ -490,7 +492,12 @@ ShaderCode GenerateVertexShaderCode(APIType api_type, const vertex_shader_uid_da out.Write("gl_ClipDistance[0] = o.clipDist0;\n"); out.Write("gl_ClipDistance[1] = o.clipDist1;\n"); } - out.Write("gl_Position = o.pos;\n"); + + // Vulkan NDC space has Y pointing down (right-handed NDC space). + if (api_type == APIType::Vulkan) + out.Write("gl_Position = float4(o.pos.x, -o.pos.y, o.pos.z, o.pos.w);\n"); + else + out.Write("gl_Position = o.pos;\n"); } else // D3D { diff --git a/Source/Core/VideoCommon/VideoCommon.h b/Source/Core/VideoCommon/VideoCommon.h index de4f69c9a6..e25b2b66a5 100644 --- a/Source/Core/VideoCommon/VideoCommon.h +++ b/Source/Core/VideoCommon/VideoCommon.h @@ -73,6 +73,7 @@ enum class APIType { OpenGL, D3D, + Vulkan, Nothing };