diff --git a/Common/GPU/Shader.h b/Common/GPU/Shader.h index 4020a7b253..4daa5717d1 100644 --- a/Common/GPU/Shader.h +++ b/Common/GPU/Shader.h @@ -94,6 +94,7 @@ struct UniformDef { struct SamplerDef { int binding; // Might only be used by some backends. const char *name; + bool array; // TODO: Might need unsigned samplers, 3d samplers, or other types in the future. }; diff --git a/Common/GPU/ShaderWriter.cpp b/Common/GPU/ShaderWriter.cpp index e00a468e35..238ce848ed 100644 --- a/Common/GPU/ShaderWriter.cpp +++ b/Common/GPU/ShaderWriter.cpp @@ -114,6 +114,9 @@ void ShaderWriter::Preamble(Slice extensions) { switch (lang_.shaderLanguage) { case GLSL_VULKAN: C("#version 450\n"); + if (flags_ & ShaderWriterFlags::FS_AUTO_STEREO) { + C("#extension GL_EXT_multiview : enable\n"); + } // IMPORTANT! Extensions must be the first thing after #version. for (size_t i = 0; i < extensions.size(); i++) { F("%s\n", extensions[i]); @@ -462,6 +465,15 @@ void ShaderWriter::DeclareSamplers(Slice samplers) { samplerDefs_ = samplers; } +const SamplerDef *ShaderWriter::GetSamplerDef(const char *name) const { + for (int i = 0; i < (int)samplers_.size(); i++) { + if (!strcmp(samplers_[i].name, name)) { + return &samplers_[i]; + } + } + return nullptr; +} + void ShaderWriter::DeclareTexture2D(const SamplerDef &def) { switch (lang_.shaderLanguage) { case HLSL_D3D11: @@ -472,7 +484,11 @@ void ShaderWriter::DeclareTexture2D(const SamplerDef &def) { break; case GLSL_VULKAN: // In the thin3d descriptor set layout, textures start at 1 in set 0. Hence the +1. - F("layout(set = 0, binding = %d) uniform sampler2D %s;\n", def.binding + texBindingBase_, def.name); + if ((flags_ & ShaderWriterFlags::FS_AUTO_STEREO) && def.array) { + F("layout(set = 0, binding = %d) uniform sampler2DArray %s;\n", def.binding + texBindingBase_, def.name); + } else { + F("layout(set = 0, binding = %d) uniform sampler2D %s;\n", def.binding + texBindingBase_, def.name); + } break; default: F("uniform sampler2D %s;\n", def.name); @@ -492,6 +508,7 @@ void ShaderWriter::DeclareSampler2D(const SamplerDef &def) { } ShaderWriter &ShaderWriter::SampleTexture2D(const char *sampName, const char *uv) { + const SamplerDef *samp = GetSamplerDef(sampName); switch (lang_.shaderLanguage) { case HLSL_D3D11: F("%s.Sample(%sSamp, %s)", sampName, sampName, uv); @@ -501,13 +518,20 @@ ShaderWriter &ShaderWriter::SampleTexture2D(const char *sampName, const char *uv break; default: // Note: we ignore the sampler. make sure you bound samplers to the textures correctly. - F("%s(%s, %s)", lang_.texture, sampName, uv); + if (samp && samp->array) { + const char *index = (flags_ & ShaderWriterFlags::FS_AUTO_STEREO) ? "float(gl_ViewIndex)" : "0.0"; + F("%s(%s, vec3(%s, %s))", lang_.texture, sampName, uv, index); + } else { + F("%s(%s, %s)", lang_.texture, sampName, uv); + } break; } return *this; } ShaderWriter &ShaderWriter::SampleTexture2DOffset(const char *sampName, const char *uv, int offX, int offY) { + const SamplerDef *samp = GetSamplerDef(sampName); + switch (lang_.shaderLanguage) { case HLSL_D3D11: F("%s.Sample(%sSamp, %s, int2(%d, %d))", sampName, sampName, uv, offX, offY); @@ -518,13 +542,20 @@ ShaderWriter &ShaderWriter::SampleTexture2DOffset(const char *sampName, const ch break; default: // Note: we ignore the sampler. make sure you bound samplers to the textures correctly. - F("%sOffset(%s, %s, ivec2(%d, %d))", lang_.texture, sampName, uv, offX, offY); + if (samp->array) { + const char *index = (flags_ & ShaderWriterFlags::FS_AUTO_STEREO) ? "float(gl_ViewIndex)" : "0.0"; + F("%sOffset(%s, vec3(%s, %s), ivec3(%d, %d))", lang_.texture, sampName, uv, index, offX, offY); + } else { + F("%sOffset(%s, %s, ivec2(%d, %d))", lang_.texture, sampName, uv, offX, offY); + } break; } return *this; } ShaderWriter &ShaderWriter::LoadTexture2D(const char *sampName, const char *uv, int level) { + const SamplerDef *samp = GetSamplerDef(sampName); + switch (lang_.shaderLanguage) { case HLSL_D3D11: F("%s.Load(ivec3(%s, %d))", sampName, uv, level); @@ -535,7 +566,11 @@ ShaderWriter &ShaderWriter::LoadTexture2D(const char *sampName, const char *uv, break; default: // Note: we ignore the sampler. make sure you bound samplers to the textures correctly. - F("texelFetch(%s, %s, %d)", sampName, uv, level); + if ((flags_ & ShaderWriterFlags::FS_AUTO_STEREO) && samp->array) { + F("texelFetch(%s, %s, %d)", sampName, uv, level); + } else { + F("texelFetch(%s, %s, %d)", sampName, uv, level); + } break; } return *this; diff --git a/Common/GPU/ShaderWriter.h b/Common/GPU/ShaderWriter.h index 610a7fe0ec..567e4c33a5 100644 --- a/Common/GPU/ShaderWriter.h +++ b/Common/GPU/ShaderWriter.h @@ -33,7 +33,7 @@ struct VaryingDef { enum class ShaderWriterFlags { NONE = 0, FS_WRITE_DEPTH = 1, - FS_AUTO_STEREO = 2, + FS_AUTO_STEREO = 2, // Automatically makes sampler 0 an array sampler, and samples it by gl_ViewIndex. Useful for stereo rendering. }; ENUM_CLASS_BITOPS(ShaderWriterFlags); @@ -119,9 +119,12 @@ private: void Preamble(Slice extensions); + const SamplerDef *GetSamplerDef(const char *name) const; + char *p_; const ShaderLanguageDesc &lang_; const ShaderStage stage_; + Slice samplers_; ShaderWriterFlags flags_ = ShaderWriterFlags::NONE; Slice samplerDefs_; int texBindingBase_ = 1; diff --git a/GPU/Common/FragmentShaderGenerator.cpp b/GPU/Common/FragmentShaderGenerator.cpp index e6ce4a0f94..64693097a4 100644 --- a/GPU/Common/FragmentShaderGenerator.cpp +++ b/GPU/Common/FragmentShaderGenerator.cpp @@ -96,6 +96,10 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu bool doTextureAlpha = id.Bit(FS_BIT_TEXALPHA); bool arrayTexture = id.Bit(FS_BIT_SAMPLE_ARRAY_TEXTURE); + if (texture3D && arrayTexture) { + *errorString = "Invalid combination of 3D texture and array texture, shouldn't happen"; + return false; + } bool flatBug = bugs.Has(Draw::Bugs::BROKEN_FLAT_IN_SHADER) && g_Config.bVendorBugChecksEnabled; @@ -156,6 +160,9 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu return false; } + // Currently only used by Vulkan. + std::vector samplers; + if (compat.shaderLanguage == ShaderLanguage::GLSL_VULKAN) { if (useDiscardStencilBugWorkaround && !gstate_c.Use(GPU_ROUND_FRAGMENT_DEPTH_TO_16BIT)) { WRITE(p, "layout (depth_unchanged) out float gl_FragDepth;\n"); @@ -163,7 +170,7 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu WRITE(p, "layout (std140, set = 0, binding = 3) uniform baseUBO {\n%s};\n", ub_baseStr); if (doTexture) { - WRITE(p, "layout (binding = 0) uniform %s%s tex;\n", texture3D ? "sampler3D" : "sampler2D", arrayTexture ? "array" : ""); + WRITE(p, "layout (binding = 0) uniform %s%s tex;\n", texture3D ? "sampler3D" : "sampler2D", arrayTexture ? "Array" : ""); } if (readFramebufferTex) { @@ -639,6 +646,15 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu } else { WRITE(p, " vec4 t = %s(tex, vec3(%s.xy, u_mipBias));\n", compat.texture3D, texcoord); } + } else if (arrayTexture) { + // Used for stereo rendering. + if (doTextureProjection) { + if (doTextureProjection) { + WRITE(p, " vec4 t = %sProj(tex, %s);\n", compat.texture, texcoord); + } else { + WRITE(p, " vec4 t = %s(tex, %s.xy);\n", compat.texture, texcoord); + } + } } else { if (doTextureProjection) { WRITE(p, " vec4 t = %sProj(tex, %s);\n", compat.texture, texcoord);