2017-07-20 01:10:02 -06:00
|
|
|
// Copyright 2017 Dolphin Emulator Project
|
2021-07-04 19:22:19 -06:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
2017-07-20 01:10:02 -06:00
|
|
|
|
|
|
|
#include "VideoCommon/ShaderGenCommon.h"
|
2019-11-22 15:10:41 -07:00
|
|
|
|
|
|
|
#include <fmt/format.h>
|
|
|
|
|
2022-07-22 23:47:04 -06:00
|
|
|
#include "Common/Assert.h"
|
2017-07-20 01:10:02 -06:00
|
|
|
#include "Common/FileUtil.h"
|
|
|
|
#include "Core/ConfigManager.h"
|
2019-12-22 11:40:40 -07:00
|
|
|
#include "VideoCommon/VideoCommon.h"
|
|
|
|
#include "VideoCommon/VideoConfig.h"
|
2017-07-20 01:10:02 -06:00
|
|
|
|
|
|
|
ShaderHostConfig ShaderHostConfig::GetCurrent()
|
|
|
|
{
|
|
|
|
ShaderHostConfig bits = {};
|
|
|
|
bits.msaa = g_ActiveConfig.iMultisamples > 1;
|
|
|
|
bits.ssaa = g_ActiveConfig.iMultisamples > 1 && g_ActiveConfig.bSSAA &&
|
|
|
|
g_ActiveConfig.backend_info.bSupportsSSAA;
|
2017-11-10 20:55:00 -07:00
|
|
|
bits.stereo = g_ActiveConfig.stereo_mode != StereoMode::Off;
|
2017-07-20 01:10:02 -06:00
|
|
|
bits.wireframe = g_ActiveConfig.bWireFrame;
|
|
|
|
bits.per_pixel_lighting = g_ActiveConfig.bEnablePixelLighting;
|
|
|
|
bits.vertex_rounding = g_ActiveConfig.UseVertexRounding();
|
|
|
|
bits.fast_depth_calc = g_ActiveConfig.bFastDepthCalc;
|
|
|
|
bits.bounding_box = g_ActiveConfig.bBBoxEnable;
|
|
|
|
bits.backend_dual_source_blend = g_ActiveConfig.backend_info.bSupportsDualSourceBlend;
|
|
|
|
bits.backend_geometry_shaders = g_ActiveConfig.backend_info.bSupportsGeometryShaders;
|
|
|
|
bits.backend_early_z = g_ActiveConfig.backend_info.bSupportsEarlyZ;
|
|
|
|
bits.backend_bbox = g_ActiveConfig.backend_info.bSupportsBBox;
|
|
|
|
bits.backend_gs_instancing = g_ActiveConfig.backend_info.bSupportsGSInstancing;
|
|
|
|
bits.backend_clip_control = g_ActiveConfig.backend_info.bSupportsClipControl;
|
|
|
|
bits.backend_ssaa = g_ActiveConfig.backend_info.bSupportsSSAA;
|
|
|
|
bits.backend_atomics = g_ActiveConfig.backend_info.bSupportsFragmentStoresAndAtomics;
|
|
|
|
bits.backend_depth_clamp = g_ActiveConfig.backend_info.bSupportsDepthClamp;
|
|
|
|
bits.backend_reversed_depth_range = g_ActiveConfig.backend_info.bSupportsReversedDepthRange;
|
2017-07-19 23:25:24 -06:00
|
|
|
bits.backend_bitfield = g_ActiveConfig.backend_info.bSupportsBitfield;
|
|
|
|
bits.backend_dynamic_sampler_indexing =
|
|
|
|
g_ActiveConfig.backend_info.bSupportsDynamicSamplerIndexing;
|
2017-10-25 23:44:39 -06:00
|
|
|
bits.backend_shader_framebuffer_fetch = g_ActiveConfig.backend_info.bSupportsFramebufferFetch;
|
2018-05-25 08:04:18 -06:00
|
|
|
bits.backend_logic_op = g_ActiveConfig.backend_info.bSupportsLogicOp;
|
2019-02-14 18:59:50 -07:00
|
|
|
bits.backend_palette_conversion = g_ActiveConfig.backend_info.bSupportsPaletteConversion;
|
2021-07-28 22:18:18 -06:00
|
|
|
bits.enable_validation_layer = g_ActiveConfig.bEnableValidationLayer;
|
2021-07-29 18:43:35 -06:00
|
|
|
bits.manual_texture_sampling = !g_ActiveConfig.bFastTextureSampling;
|
2021-08-01 18:31:40 -06:00
|
|
|
bits.manual_texture_sampling_custom_texture_sizes =
|
2022-12-27 14:45:13 -07:00
|
|
|
g_ActiveConfig.ManualTextureSamplingWithCustomTextureSizes();
|
2021-08-06 21:28:26 -06:00
|
|
|
bits.backend_sampler_lod_bias = g_ActiveConfig.backend_info.bSupportsLodBiasInSampler;
|
2022-06-18 00:09:35 -06:00
|
|
|
bits.backend_dynamic_vertex_loader = g_ActiveConfig.backend_info.bSupportsDynamicVertexLoader;
|
2022-07-22 23:47:04 -06:00
|
|
|
bits.backend_vs_point_line_expand = g_ActiveConfig.UseVSForLinePointExpand();
|
2017-07-20 01:10:02 -06:00
|
|
|
return bits;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string GetDiskShaderCacheFileName(APIType api_type, const char* type, bool include_gameid,
|
2018-02-24 08:15:35 -07:00
|
|
|
bool include_host_config, bool include_api)
|
2017-07-20 01:10:02 -06:00
|
|
|
{
|
|
|
|
if (!File::Exists(File::GetUserPath(D_SHADERCACHE_IDX)))
|
|
|
|
File::CreateDir(File::GetUserPath(D_SHADERCACHE_IDX));
|
|
|
|
|
|
|
|
std::string filename = File::GetUserPath(D_SHADERCACHE_IDX);
|
2018-02-24 08:15:35 -07:00
|
|
|
if (include_api)
|
2017-07-20 01:10:02 -06:00
|
|
|
{
|
2018-02-24 08:15:35 -07:00
|
|
|
switch (api_type)
|
|
|
|
{
|
|
|
|
case APIType::D3D:
|
|
|
|
filename += "D3D";
|
|
|
|
break;
|
2022-06-01 03:58:13 -06:00
|
|
|
case APIType::Metal:
|
|
|
|
filename += "Metal";
|
|
|
|
break;
|
2018-02-24 08:15:35 -07:00
|
|
|
case APIType::OpenGL:
|
|
|
|
filename += "OpenGL";
|
|
|
|
break;
|
|
|
|
case APIType::Vulkan:
|
|
|
|
filename += "Vulkan";
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
filename += '-';
|
2017-07-20 01:10:02 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
filename += type;
|
|
|
|
|
|
|
|
if (include_gameid)
|
|
|
|
{
|
|
|
|
filename += '-';
|
|
|
|
filename += SConfig::GetInstance().GetGameID();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (include_host_config)
|
|
|
|
{
|
2017-10-25 23:44:39 -06:00
|
|
|
// We're using 21 bits, so 6 hex characters.
|
2017-07-20 01:10:02 -06:00
|
|
|
ShaderHostConfig host_config = ShaderHostConfig::GetCurrent();
|
2019-11-22 15:10:41 -07:00
|
|
|
filename += fmt::format("-{:06X}", host_config.bits);
|
2017-07-20 01:10:02 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
filename += ".cache";
|
|
|
|
return filename;
|
|
|
|
}
|
2019-12-22 11:40:40 -07:00
|
|
|
|
2021-09-07 18:27:18 -06:00
|
|
|
void WriteIsNanHeader(ShaderCode& out, APIType api_type)
|
|
|
|
{
|
2022-05-03 23:41:34 -06:00
|
|
|
out.Write("#define dolphin_isnan(f) isnan(f)\n");
|
2021-09-07 18:27:18 -06:00
|
|
|
}
|
|
|
|
|
2021-07-26 12:20:04 -06:00
|
|
|
void WriteBitfieldExtractHeader(ShaderCode& out, APIType api_type,
|
|
|
|
const ShaderHostConfig& host_config)
|
|
|
|
{
|
|
|
|
// ==============================================
|
|
|
|
// BitfieldExtract for APIs which don't have it
|
|
|
|
// ==============================================
|
|
|
|
if (!host_config.backend_bitfield)
|
|
|
|
{
|
|
|
|
out.Write("uint bitfieldExtract(uint val, int off, int size) {{\n"
|
|
|
|
" // This built-in function is only supported in OpenGL 4.0+ and ES 3.1+\n"
|
|
|
|
" // Microsoft's HLSL compiler automatically optimises this to a bitfield extract "
|
|
|
|
"instruction.\n"
|
|
|
|
" uint mask = uint((1 << size) - 1);\n"
|
|
|
|
" return uint(val >> off) & mask;\n"
|
|
|
|
"}}\n\n");
|
2021-08-26 14:58:45 -06:00
|
|
|
out.Write("int bitfieldExtract(int val, int off, int size) {{\n"
|
|
|
|
" // This built-in function is only supported in OpenGL 4.0+ and ES 3.1+\n"
|
|
|
|
" // Microsoft's HLSL compiler automatically optimises this to a bitfield extract "
|
|
|
|
"instruction.\n"
|
|
|
|
" return ((val << (32 - size - off)) >> (32 - size));\n"
|
|
|
|
"}}\n\n");
|
2021-07-26 12:20:04 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-22 11:40:40 -07:00
|
|
|
static void DefineOutputMember(ShaderCode& object, APIType api_type, std::string_view qualifier,
|
|
|
|
std::string_view type, std::string_view name, int var_index,
|
2022-05-03 23:41:34 -06:00
|
|
|
ShaderStage stage, std::string_view semantic = {},
|
|
|
|
int semantic_index = -1)
|
2019-12-22 11:40:40 -07:00
|
|
|
{
|
2020-11-09 00:23:32 -07:00
|
|
|
object.Write("\t{} {} {}", qualifier, type, name);
|
2019-12-22 11:40:40 -07:00
|
|
|
|
|
|
|
if (var_index != -1)
|
2020-11-09 00:23:32 -07:00
|
|
|
object.Write("{}", var_index);
|
2019-12-22 11:40:40 -07:00
|
|
|
|
2022-05-03 23:41:34 -06:00
|
|
|
if (api_type == APIType::D3D && !semantic.empty() && stage == ShaderStage::Geometry)
|
2019-12-22 11:40:40 -07:00
|
|
|
{
|
|
|
|
if (semantic_index != -1)
|
2020-11-09 00:23:32 -07:00
|
|
|
object.Write(" : {}{}", semantic, semantic_index);
|
2019-12-22 11:40:40 -07:00
|
|
|
else
|
2020-11-09 00:23:32 -07:00
|
|
|
object.Write(" : {}", semantic);
|
2019-12-22 11:40:40 -07:00
|
|
|
}
|
|
|
|
|
2020-11-09 00:23:32 -07:00
|
|
|
object.Write(";\n");
|
2019-12-22 11:40:40 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void GenerateVSOutputMembers(ShaderCode& object, APIType api_type, u32 texgens,
|
2022-05-03 23:41:34 -06:00
|
|
|
const ShaderHostConfig& host_config, std::string_view qualifier,
|
|
|
|
ShaderStage stage)
|
2019-12-22 11:40:40 -07:00
|
|
|
{
|
2022-05-03 23:41:34 -06:00
|
|
|
// SPIRV-Cross names all semantics as "TEXCOORD"
|
|
|
|
// Unfortunately Geometry shaders (which also uses this function)
|
|
|
|
// aren't supported. The output semantic name needs to match
|
|
|
|
// up with the input semantic name for both the next stage (pixel shader)
|
|
|
|
// and the previous stage (vertex shader), so
|
|
|
|
// we need to handle geometry in a special way...
|
|
|
|
if (api_type == APIType::D3D && stage == ShaderStage::Geometry)
|
|
|
|
{
|
|
|
|
DefineOutputMember(object, api_type, qualifier, "float4", "pos", -1, stage, "TEXCOORD", 0);
|
|
|
|
DefineOutputMember(object, api_type, qualifier, "float4", "colors_", 0, stage, "TEXCOORD", 1);
|
|
|
|
DefineOutputMember(object, api_type, qualifier, "float4", "colors_", 1, stage, "TEXCOORD", 2);
|
2019-12-22 11:40:40 -07:00
|
|
|
|
2022-05-03 23:41:34 -06:00
|
|
|
const unsigned int index_base = 3;
|
|
|
|
unsigned int index_offset = 0;
|
|
|
|
if (host_config.backend_geometry_shaders)
|
|
|
|
{
|
|
|
|
DefineOutputMember(object, api_type, qualifier, "float", "clipDist", 0, stage, "TEXCOORD",
|
|
|
|
index_base + index_offset);
|
|
|
|
DefineOutputMember(object, api_type, qualifier, "float", "clipDist", 1, stage, "TEXCOORD",
|
|
|
|
index_base + index_offset + 1);
|
|
|
|
index_offset += 2;
|
|
|
|
}
|
2019-12-22 11:40:40 -07:00
|
|
|
|
2022-05-03 23:41:34 -06:00
|
|
|
for (unsigned int i = 0; i < texgens; ++i)
|
|
|
|
{
|
|
|
|
DefineOutputMember(object, api_type, qualifier, "float3", "tex", i, stage, "TEXCOORD",
|
|
|
|
index_base + index_offset + i);
|
|
|
|
}
|
|
|
|
index_offset += texgens;
|
2019-12-22 11:40:40 -07:00
|
|
|
|
2022-05-03 23:41:34 -06:00
|
|
|
if (!host_config.fast_depth_calc)
|
|
|
|
{
|
|
|
|
DefineOutputMember(object, api_type, qualifier, "float4", "clipPos", -1, stage, "TEXCOORD",
|
|
|
|
index_base + index_offset);
|
|
|
|
index_offset++;
|
|
|
|
}
|
2019-12-22 11:40:40 -07:00
|
|
|
|
2022-05-03 23:41:34 -06:00
|
|
|
if (host_config.per_pixel_lighting)
|
|
|
|
{
|
|
|
|
DefineOutputMember(object, api_type, qualifier, "float3", "Normal", -1, stage, "TEXCOORD",
|
|
|
|
index_base + index_offset);
|
|
|
|
DefineOutputMember(object, api_type, qualifier, "float3", "WorldPos", -1, stage, "TEXCOORD",
|
|
|
|
index_base + index_offset + 1);
|
|
|
|
index_offset += 2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
2019-12-22 11:40:40 -07:00
|
|
|
{
|
2022-05-03 23:41:34 -06:00
|
|
|
DefineOutputMember(object, api_type, qualifier, "float4", "pos", -1, stage, "SV_Position");
|
|
|
|
DefineOutputMember(object, api_type, qualifier, "float4", "colors_", 0, stage, "COLOR", 0);
|
|
|
|
DefineOutputMember(object, api_type, qualifier, "float4", "colors_", 1, stage, "COLOR", 1);
|
|
|
|
|
|
|
|
if (host_config.backend_geometry_shaders)
|
|
|
|
{
|
|
|
|
DefineOutputMember(object, api_type, qualifier, "float", "clipDist", 0, stage,
|
|
|
|
"SV_ClipDistance", 0);
|
|
|
|
DefineOutputMember(object, api_type, qualifier, "float", "clipDist", 1, stage,
|
|
|
|
"SV_ClipDistance", 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (unsigned int i = 0; i < texgens; ++i)
|
|
|
|
DefineOutputMember(object, api_type, qualifier, "float3", "tex", i, stage, "TEXCOORD", i);
|
|
|
|
|
|
|
|
if (!host_config.fast_depth_calc)
|
|
|
|
DefineOutputMember(object, api_type, qualifier, "float4", "clipPos", -1, stage, "TEXCOORD",
|
|
|
|
texgens);
|
|
|
|
|
|
|
|
if (host_config.per_pixel_lighting)
|
|
|
|
{
|
|
|
|
DefineOutputMember(object, api_type, qualifier, "float3", "Normal", -1, stage, "TEXCOORD",
|
|
|
|
texgens + 1);
|
|
|
|
DefineOutputMember(object, api_type, qualifier, "float3", "WorldPos", -1, stage, "TEXCOORD",
|
|
|
|
texgens + 2);
|
|
|
|
}
|
2019-12-22 11:40:40 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void AssignVSOutputMembers(ShaderCode& object, std::string_view a, std::string_view b, u32 texgens,
|
|
|
|
const ShaderHostConfig& host_config)
|
|
|
|
{
|
2020-11-09 00:23:32 -07:00
|
|
|
object.Write("\t{}.pos = {}.pos;\n", a, b);
|
|
|
|
object.Write("\t{}.colors_0 = {}.colors_0;\n", a, b);
|
|
|
|
object.Write("\t{}.colors_1 = {}.colors_1;\n", a, b);
|
2019-12-22 11:40:40 -07:00
|
|
|
|
|
|
|
for (unsigned int i = 0; i < texgens; ++i)
|
2020-11-09 00:23:32 -07:00
|
|
|
object.Write("\t{}.tex{} = {}.tex{};\n", a, i, b, i);
|
2019-12-22 11:40:40 -07:00
|
|
|
|
|
|
|
if (!host_config.fast_depth_calc)
|
2020-11-09 00:23:32 -07:00
|
|
|
object.Write("\t{}.clipPos = {}.clipPos;\n", a, b);
|
2019-12-22 11:40:40 -07:00
|
|
|
|
|
|
|
if (host_config.per_pixel_lighting)
|
|
|
|
{
|
2020-11-09 00:23:32 -07:00
|
|
|
object.Write("\t{}.Normal = {}.Normal;\n", a, b);
|
|
|
|
object.Write("\t{}.WorldPos = {}.WorldPos;\n", a, b);
|
2019-12-22 11:40:40 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (host_config.backend_geometry_shaders)
|
|
|
|
{
|
2020-11-09 00:23:32 -07:00
|
|
|
object.Write("\t{}.clipDist0 = {}.clipDist0;\n", a, b);
|
|
|
|
object.Write("\t{}.clipDist1 = {}.clipDist1;\n", a, b);
|
2019-12-22 11:40:40 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-04 21:54:30 -06:00
|
|
|
void GenerateLineOffset(ShaderCode& object, std::string_view indent0, std::string_view indent1,
|
|
|
|
std::string_view pos_a, std::string_view pos_b, std::string_view sign)
|
|
|
|
{
|
|
|
|
// GameCube/Wii's line drawing algorithm is a little quirky. It does not
|
|
|
|
// use the correct line caps. Instead, the line caps are vertical or
|
|
|
|
// horizontal depending the slope of the line.
|
|
|
|
object.Write("{indent0}float2 offset;\n"
|
|
|
|
"{indent0}float2 to = abs({pos_a}.xy / {pos_a}.w - {pos_b}.xy / {pos_b}.w);\n"
|
|
|
|
// FIXME: What does real hardware do when line is at a 45-degree angle?
|
|
|
|
// FIXME: Lines aren't drawn at the correct width. See Twilight Princess map.
|
|
|
|
"{indent0}if (" I_LINEPTPARAMS ".y * to.y > " I_LINEPTPARAMS ".x * to.x) {{\n"
|
|
|
|
// Line is more tall. Extend geometry left and right.
|
|
|
|
// Lerp LineWidth/2 from [0..VpWidth] to [-1..1]
|
|
|
|
"{indent1}offset = float2({sign}" I_LINEPTPARAMS ".z / " I_LINEPTPARAMS ".x, 0);\n"
|
|
|
|
"{indent0}}} else {{\n"
|
|
|
|
// Line is more wide. Extend geometry up and down.
|
|
|
|
// Lerp LineWidth/2 from [0..VpHeight] to [1..-1]
|
|
|
|
"{indent1}offset = float2(0, {sign}-" I_LINEPTPARAMS ".z / " I_LINEPTPARAMS ".y);\n"
|
|
|
|
"{indent0}}}\n",
|
|
|
|
fmt::arg("indent0", indent0), fmt::arg("indent1", indent1), //
|
|
|
|
fmt::arg("pos_a", pos_a), fmt::arg("pos_b", pos_b), fmt::arg("sign", sign));
|
|
|
|
}
|
|
|
|
|
|
|
|
void GenerateVSLineExpansion(ShaderCode& object, std::string_view indent, u32 texgens)
|
|
|
|
{
|
|
|
|
std::string indent1 = std::string(indent) + " ";
|
|
|
|
object.Write("{0}other_pos = float4(dot(" I_PROJECTION "[0], other_pos), dot(" I_PROJECTION
|
|
|
|
"[1], other_pos), dot(" I_PROJECTION "[2], other_pos), dot(" I_PROJECTION
|
|
|
|
"[3], other_pos));\n"
|
|
|
|
"\n"
|
|
|
|
"{0}float expand_sign = is_right ? 1.0f : -1.0f;\n",
|
|
|
|
indent);
|
|
|
|
GenerateLineOffset(object, indent, indent1, "o.pos", "other_pos", "expand_sign * ");
|
|
|
|
object.Write("\n"
|
|
|
|
"{}o.pos.xy += offset * o.pos.w;\n",
|
|
|
|
indent);
|
|
|
|
if (texgens > 0)
|
|
|
|
{
|
|
|
|
object.Write("{}if ((" I_TEXOFFSET "[2] != 0) && is_right) {{\n", indent);
|
|
|
|
object.Write("{} float texOffset = 1.0 / float(" I_TEXOFFSET "[2]);\n", indent);
|
|
|
|
for (u32 i = 0; i < texgens; i++)
|
|
|
|
{
|
|
|
|
object.Write("{} if (((" I_TEXOFFSET "[0] >> {}) & 0x1) != 0)\n", indent, i);
|
|
|
|
object.Write("{} o.tex{}.x += texOffset;\n", indent, i);
|
|
|
|
}
|
|
|
|
object.Write("{}}}\n", indent);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void GenerateVSPointExpansion(ShaderCode& object, std::string_view indent, u32 texgens)
|
|
|
|
{
|
|
|
|
object.Write(
|
|
|
|
"{0}float2 expand_sign = float2(is_right ? 1.0f : -1.0f, is_bottom ? -1.0f : 1.0f);\n"
|
|
|
|
"{0}float2 offset = expand_sign * " I_LINEPTPARAMS ".ww / " I_LINEPTPARAMS ".xy;\n"
|
|
|
|
"{0}o.pos.xy += offset * o.pos.w;\n",
|
|
|
|
indent);
|
|
|
|
if (texgens > 0)
|
|
|
|
{
|
|
|
|
object.Write("{0}if (" I_TEXOFFSET "[3] != 0) {{\n"
|
|
|
|
"{0} float texOffsetMagnitude = 1.0f / float(" I_TEXOFFSET "[3]);\n"
|
|
|
|
"{0} float2 texOffset = float2(is_right ? texOffsetMagnitude : 0.0f, "
|
|
|
|
"is_bottom ? texOffsetMagnitude : 0.0f);",
|
|
|
|
indent);
|
|
|
|
for (u32 i = 0; i < texgens; i++)
|
|
|
|
{
|
|
|
|
object.Write("{} if (((" I_TEXOFFSET "[1] >> {}) & 0x1) != 0)\n", indent, i);
|
|
|
|
object.Write("{} o.tex{}.xy += texOffset;\n", indent, i);
|
|
|
|
}
|
|
|
|
object.Write("{}}}\n", indent);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-22 11:40:40 -07:00
|
|
|
const char* GetInterpolationQualifier(bool msaa, bool ssaa, bool in_glsl_interface_block, bool in)
|
|
|
|
{
|
|
|
|
if (!msaa)
|
|
|
|
return "";
|
|
|
|
|
|
|
|
// Without GL_ARB_shading_language_420pack support, the interpolation qualifier must be
|
|
|
|
// "centroid in" and not "centroid", even within an interface block.
|
|
|
|
if (in_glsl_interface_block && !g_ActiveConfig.backend_info.bSupportsBindingLayout)
|
|
|
|
{
|
|
|
|
if (!ssaa)
|
|
|
|
return in ? "centroid in" : "centroid out";
|
|
|
|
else
|
|
|
|
return in ? "sample in" : "sample out";
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (!ssaa)
|
|
|
|
return "centroid";
|
|
|
|
else
|
|
|
|
return "sample";
|
|
|
|
}
|
|
|
|
}
|