Refactor custom shaders to abstract binding differences. Closes #2458

* Newly written shaders and any updated shaders can now use pre-defined macros
  to abstract away binding differences between APIs, so custom shaders will be
  more portable in particular shaders written in HLSL for D3D or GLSL on OpenGL
  won't break on vulkan because they refer to incorrect binds.
This commit is contained in:
baldurk
2022-01-31 19:14:08 +00:00
parent 0d7dcabdac
commit 3ac4bd0ebd
37 changed files with 1382 additions and 613 deletions
+460
View File
@@ -51,3 +51,463 @@
#endif
#include "glsl_ubos.h"
struct RD_CustomShader_UBO_Type
{
uvec4 TexDim;
uint SelectedMip;
uint TextureType;
uint SelectedSliceFace;
int SelectedSample;
uvec4 YUVDownsampleRate;
uvec4 YUVAChannels;
vec2 SelectedRange;
};
#if defined(VULKAN)
#define HLSL_CUSTOM_PREFIX \
R"EOPREFIX(
#define RD_FLOAT_1D_ARRAY_BINDING t6
#define RD_FLOAT_1D_BINDING t6 // all textures treated as arrays, add macro aliases
#define RD_FLOAT_2D_ARRAY_BINDING t7
#define RD_FLOAT_2D_BINDING t7
#define RD_FLOAT_3D_BINDING t8
#define RD_FLOAT_DEPTH_BINDING t7
#define RD_FLOAT_DEPTH_ARRAY_BINDING t7
#define RD_FLOAT_STENCIL_BINDING t17
#define RD_FLOAT_STENCIL_ARRAY_BINDING t17
#define RD_FLOAT_DEPTHMS_BINDING t9
#define RD_FLOAT_DEPTHMS_ARRAY_BINDING t9
#define RD_FLOAT_STENCILMS_BINDING t19
#define RD_FLOAT_STENCILMS_ARRAY_BINDING t19
#define RD_FLOAT_2DMS_ARRAY_BINDING t9
#define RD_FLOAT_2DMS_BINDING t9
#define RD_FLOAT_YUV_ARRAY_BINDING t10
#define RD_FLOAT_YUV_BINDING t10
#define RD_INT_1D_ARRAY_BINDING t11
#define RD_INT_1D_BINDING t11
#define RD_INT_2D_ARRAY_BINDING t12
#define RD_INT_2D_BINDING t12
#define RD_INT_3D_BINDING t13
#define RD_INT_2DMS_ARRAY_BINDING t14
#define RD_INT_2DMS_BINDING t14
#define RD_UINT_1D_ARRAY_BINDING t16
#define RD_UINT_1D_BINDING t16
#define RD_UINT_2D_ARRAY_BINDING t17
#define RD_UINT_2D_BINDING t17
#define RD_UINT_3D_BINDING t18
#define RD_UINT_2DMS_ARRAY_BINDING t19
#define RD_UINT_2DMS_BINDING t19
#define RD_POINT_SAMPLER_BINDING s50
#define RD_LINEAR_SAMPLER_BINDING s51
#define RD_CONSTANT_BUFFER_BINDING b0
cbuffer RD_CBuffer_Type : register(RD_CONSTANT_BUFFER_BINDING)
{
struct RD_CBuffer_Struct
{
uint4 TexDim;
uint SelectedMip;
uint TextureType;
uint SelectedSliceFace;
int SelectedSample;
uint4 YUVDownsampleRate;
uint4 YUVAChannels;
float2 SelectedRange;
} RD_CBuffer_Data;
};
#define RD_TextureType_1D 1
#define RD_TextureType_2D 2
#define RD_TextureType_3D 3
#define RD_TextureType_Depth 999
#define RD_TextureType_DepthStencil 999
#define RD_TextureType_DepthMS 999
#define RD_TextureType_DepthStencilMS 999
#define RD_TextureType_2DMS 9
// for compatibility
#define RD_TextureType_1D_Array 1
#define RD_TextureType_2D_Array 2
#define RD_TextureType_Cube 999
#define RD_TextureType_Cube_Array 999
// possible values (these are only return values from this function, NOT texture binding points):
// RD_TextureType_1D
// RD_TextureType_2D
// RD_TextureType_3D
// RD_TextureType_Depth (D3D only)
// RD_TextureType_DepthStencil (D3D only)
// RD_TextureType_DepthMS (D3D only)
// RD_TextureType_DepthStencilMS (D3D only)
// RD_TextureType_2DMS
uint RD_TextureType()
{
return RD_CBuffer_Data.TextureType;
}
// selected sample, or -numSamples for resolve
int RD_SelectedSample()
{
return RD_CBuffer_Data.SelectedSample;
}
uint RD_SelectedSliceFace()
{
return RD_CBuffer_Data.SelectedSliceFace;
}
uint RD_SelectedMip()
{
return RD_CBuffer_Data.SelectedMip;
}
// xyz = width, height, depth (or array size). w = # mips
uint4 RD_TexDim()
{
return RD_CBuffer_Data.TexDim;
}
// x = horizontal downsample rate (1 full rate, 2 half rate)
// y = vertical downsample rate
// z = number of planes in input texture
// w = number of bits per component (8, 10, 16)
uint4 RD_YUVDownsampleRate()
{
return RD_CBuffer_Data.YUVDownsampleRate;
}
// x = where Y channel comes from
// y = where U channel comes from
// z = where V channel comes from
// w = where A channel comes from
// each index will be [0,1,2,3] for xyzw in first plane,
// [4,5,6,7] for xyzw in second plane texture, etc.
// it will be 0xff = 255 if the channel does not exist.
uint4 RD_YUVAChannels()
{
return RD_CBuffer_Data.YUVAChannels;
}
// a pair with minimum and maximum selected range values
float2 RD_SelectedRange()
{
return RD_CBuffer_Data.SelectedRange;
}
)EOPREFIX"
#define GLSL_CUSTOM_PREFIX \
R"EOPREFIX(
#define RD_FLOAT_1D_ARRAY_BINDING 6
#define RD_FLOAT_1D_BINDING 6 // all textures treated as arrays, add macro aliases
#define RD_FLOAT_2D_ARRAY_BINDING 7
#define RD_FLOAT_2D_BINDING 7
// cubemaps can read from the 2D binding
#define RD_FLOAT_CUBE_BINDING 7
#define RD_FLOAT_CUBE_ARRAY_BINDING 7
// these have no equivalent. Define them to something valid so shaders still compile,
// but they will break if used
#define RD_FLOAT_BUFFER_BINDING 3
#define RD_FLOAT_RECT_BINDING 4
#define RD_FLOAT_3D_BINDING 8
#define RD_FLOAT_2DMS_ARRAY_BINDING 9
#define RD_FLOAT_2DMS_BINDING 9
#define RD_FLOAT_YUV_BINDING 10
#define RD_FLOAT_YUV_ARRAY_SIZE 2
#define RD_INT_1D_ARRAY_BINDING 11
#define RD_INT_1D_BINDING 11
#define RD_INT_2D_ARRAY_BINDING 12
#define RD_INT_2D_BINDING 12
#define RD_INT_3D_BINDING 13
#define RD_INT_2DMS_ARRAY_BINDING 14
#define RD_INT_2DMS_BINDING 14
#define RD_UINT_1D_ARRAY_BINDING 16
#define RD_UINT_1D_BINDING 16
#define RD_UINT_2D_ARRAY_BINDING 17
#define RD_UINT_2D_BINDING 17
#define RD_UINT_3D_BINDING 18
#define RD_UINT_2DMS_ARRAY_BINDING 19
#define RD_UINT_2DMS_BINDING 19
#define RD_POINT_SAMPLER_BINDING 50
#define RD_LINEAR_SAMPLER_BINDING 51
#define RD_CONSTANT_BUFFER_BINDING 0
layout(binding = RD_CONSTANT_BUFFER_BINDING) uniform RD_CBuffer_Type
{
uvec4 TexDim;
uint SelectedMip;
uint TextureType;
uint SelectedSliceFace;
int SelectedSample;
uvec4 YUVDownsampleRate;
uvec4 YUVAChannels;
vec2 SelectedRange;
} RD_CBuffer_Data;
#define RD_TextureType_1D 1
#define RD_TextureType_2D 2
#define RD_TextureType_3D 3
#define RD_TextureType_2DMS 4
// for compatibility
#define RD_TextureType_1D_Array 1
#define RD_TextureType_2D_Array 2
#define RD_TextureType_2DMS_Array 4
#define RD_TextureType_Cube 999
#define RD_TextureType_Cube_Array 999
#define RD_TextureType_Rect 999
#define RD_TextureType_Buffer 999
#define RD_TextureType_Depth 999
#define RD_TextureType_DepthStencil 999
#define RD_TextureType_DepthMS 999
#define RD_TextureType_DepthStencilMS 999
// possible values (these are only return values from this function, NOT texture binding points):
// RD_TextureType_1D
// RD_TextureType_2D
// RD_TextureType_3D
// RD_TextureType_Cube (OpenGL only)
// RD_TextureType_1D_Array (OpenGL only)
// RD_TextureType_2D_Array (OpenGL only)
// RD_TextureType_Cube_Array (OpenGL only)
// RD_TextureType_Rect (OpenGL only)
// RD_TextureType_Buffer (OpenGL only)
// RD_TextureType_2DMS
// RD_TextureType_2DMS_Array (OpenGL only)
uint RD_TextureType()
{
return RD_CBuffer_Data.TextureType;
}
// selected sample, or -numSamples for resolve
int RD_SelectedSample()
{
return RD_CBuffer_Data.SelectedSample;
}
uint RD_SelectedSliceFace()
{
return RD_CBuffer_Data.SelectedSliceFace;
}
uint RD_SelectedMip()
{
return RD_CBuffer_Data.SelectedMip;
}
// xyz = width, height, depth (or array size). w = # mips
uvec4 RD_TexDim()
{
return RD_CBuffer_Data.TexDim;
}
// x = horizontal downsample rate (1 full rate, 2 half rate)
// y = vertical downsample rate
// z = number of planes in input texture
// w = number of bits per component (8, 10, 16)
uvec4 RD_YUVDownsampleRate()
{
return RD_CBuffer_Data.YUVDownsampleRate;
}
// x = where Y channel comes from
// y = where U channel comes from
// z = where V channel comes from
// w = where A channel comes from
// each index will be [0,1,2,3] for xyzw in first plane,
// [4,5,6,7] for xyzw in second plane texture, etc.
// it will be 0xff = 255 if the channel does not exist.
uvec4 RD_YUVAChannels()
{
return RD_CBuffer_Data.YUVAChannels;
}
// a pair with minimum and maximum selected range values
vec2 RD_SelectedRange()
{
return RD_CBuffer_Data.SelectedRange;
}
)EOPREFIX"
#elif defined(OPENGL)
#define GLSL_CUSTOM_PREFIX \
R"EOPREFIX(
#define RD_FLOAT_1D_BINDING 1
#define RD_FLOAT_2D_BINDING 2
#define RD_FLOAT_3D_BINDING 3
#define RD_FLOAT_CUBE_BINDING 4
#define RD_FLOAT_1D_ARRAY_BINDING 5
#define RD_FLOAT_2D_ARRAY_BINDING 6
#define RD_FLOAT_CUBE_ARRAY_BINDING 7
#define RD_FLOAT_RECT_BINDING 8
#define RD_FLOAT_BUFFER_BINDING 9
#define RD_FLOAT_2DMS_BINDING 10
#define RD_FLOAT_2DMS_ARRAY_BINDING 11
#define RD_INT_1D_BINDING 1
#define RD_INT_2D_BINDING 2
#define RD_INT_3D_BINDING 3
#define RD_INT_CUBE_BINDING 4
#define RD_INT_1D_ARRAY_BINDING 5
#define RD_INT_2D_ARRAY_BINDING 6
#define RD_INT_CUBE_ARRAY_BINDING 7
#define RD_INT_RECT_BINDING 8
#define RD_INT_BUFFER_BINDING 9
#define RD_INT_2DMS_BINDING 10
#define RD_INT_2DMS_ARRAY_BINDING 11
#define RD_UINT_1D_BINDING 1
#define RD_UINT_2D_BINDING 2
#define RD_UINT_3D_BINDING 3
#define RD_UINT_CUBE_BINDING 4
#define RD_UINT_1D_ARRAY_BINDING 5
#define RD_UINT_2D_ARRAY_BINDING 6
#define RD_UINT_CUBE_ARRAY_BINDING 7
#define RD_UINT_RECT_BINDING 8
#define RD_UINT_BUFFER_BINDING 9
#define RD_UINT_2DMS_BINDING 10
#define RD_UINT_2DMS_ARRAY_BINDING 11
#define RD_CONSTANT_BUFFER_BINDING 0
layout(binding = RD_CONSTANT_BUFFER_BINDING) uniform RD_CBuffer_Type
{
uvec4 TexDim;
uint SelectedMip;
uint TextureType;
uint SelectedSliceFace;
int SelectedSample;
uvec4 YUVDownsampleRate;
uvec4 YUVAChannels;
vec2 SelectedRange;
} RD_CBuffer_Data;
#define RD_TextureType_1D 1
#define RD_TextureType_2D 2
#define RD_TextureType_3D 3
#define RD_TextureType_Cube 4
#define RD_TextureType_1D_Array 5
#define RD_TextureType_2D_Array 6
#define RD_TextureType_Cube_Array 7
#define RD_TextureType_Rect 8
#define RD_TextureType_Buffer 9
#define RD_TextureType_2DMS 10
#define RD_TextureType_2DMS_Array 11
// for compatibility
#define RD_TextureType_Depth 999
#define RD_TextureType_DepthStencil 999
#define RD_TextureType_DepthMS 999
#define RD_TextureType_DepthStencilMS 999
// possible values (these are only return values from this function, NOT texture binding points):
// RD_TextureType_1D
// RD_TextureType_2D
// RD_TextureType_3D
// RD_TextureType_Cube (OpenGL only)
// RD_TextureType_1D_Array (OpenGL only)
// RD_TextureType_2D_Array (OpenGL only)
// RD_TextureType_Cube_Array (OpenGL only)
// RD_TextureType_Rect (OpenGL only)
// RD_TextureType_Buffer (OpenGL only)
// RD_TextureType_2DMS
// RD_TextureType_2DMS_Array (OpenGL only)
uint RD_TextureType()
{
return RD_CBuffer_Data.TextureType;
}
// selected sample, or -numSamples for resolve
int RD_SelectedSample()
{
return RD_CBuffer_Data.SelectedSample;
}
uint RD_SelectedSliceFace()
{
return RD_CBuffer_Data.SelectedSliceFace;
}
uint RD_SelectedMip()
{
return RD_CBuffer_Data.SelectedMip;
}
// xyz = width, height, depth (or array size). w = # mips
uvec4 RD_TexDim()
{
return RD_CBuffer_Data.TexDim;
}
// x = horizontal downsample rate (1 full rate, 2 half rate)
// y = vertical downsample rate
// z = number of planes in input texture
// w = number of bits per component (8, 10, 16)
uvec4 RD_YUVDownsampleRate()
{
return RD_CBuffer_Data.YUVDownsampleRate;
}
// x = where Y channel comes from
// y = where U channel comes from
// z = where V channel comes from
// w = where A channel comes from
// each index will be [0,1,2,3] for xyzw in first plane,
// [4,5,6,7] for xyzw in second plane texture, etc.
// it will be 0xff = 255 if the channel does not exist.
uvec4 RD_YUVAChannels()
{
return RD_CBuffer_Data.YUVAChannels;
}
// a pair with minimum and maximum selected range values
vec2 RD_SelectedRange()
{
return RD_CBuffer_Data.SelectedRange;
}
)EOPREFIX"
#else
#error "OPENGL and VULKAN both not defined"
#endif
+85
View File
@@ -171,6 +171,91 @@ rdcstr GenerateGLSLShader(const rdcstr &shader, ShaderType type, int version, co
return ret;
}
static bool isspacetab(char c)
{
return c == '\t' || c == ' ';
}
static bool isnewline(char c)
{
return c == '\r' || c == '\n';
}
rdcstr InsertSnippetAfterVersion(ShaderType type, const char *source, int len, const char *snippet)
{
// we require these enums to be compatible elsewhere
glslang::TShader sh(EShLangFragment);
glslang::EShClient client =
type == ShaderType::Vulkan ? glslang::EShClientVulkan : glslang::EShClientNone;
glslang::EShTargetClientVersion targetversion =
type == ShaderType::Vulkan ? glslang::EShTargetVulkan_1_0 : glslang::EShTargetOpenGL_450;
int inputVersion = client != glslang::EShClientNone ? 100 : 0;
sh.setStringsWithLengths(&source, &len, 1);
sh.setEnvInput(glslang::EShSourceGlsl, EShLangFragment, client, inputVersion);
sh.setEnvClient(client, targetversion);
sh.setEnvTarget(glslang::EShTargetNone, glslang::EShTargetSpv_1_0);
glslang::TShader::ForbidIncluder incl;
bool success;
EShMessages flags = EShMsgOnlyPreprocessor;
if(type == ShaderType::Vulkan)
flags = EShMessages(flags | EShMsgSpvRules | EShMsgVulkanRules);
else if(type == ShaderType::GLSPIRV)
flags = EShMessages(flags | EShMsgSpvRules);
rdcstr src;
std::string outstr;
{
success =
sh.preprocess(GetDefaultResources(), 100, ENoProfile, false, false, flags, &outstr, incl);
src.assign(outstr.c_str(), outstr.size());
}
// find if this source contains a #version, accounting for whitespace. It must be the first thing
// (except for whitespace and comments, and comments have been removed)
int32_t it = src.find("#");
len = src.count();
if(it >= 0)
{
// advance past the #
++it;
// skip whitespace
while(it < len && isspacetab(src[it]))
++it;
if(it + 7 < len && !strncmp(&src[it], "version", 7))
{
it = src.find_first_of("\r\n", it);
while(it < len && isnewline(src[it]))
it++;
// it points after the #version statement
}
else
{
it = -1;
}
}
// no #version statement found - insert our own
if(it < 0)
{
rdcstr version = "#version 430 core\n\n";
src.insert(0, version);
it = version.count();
}
src.insert(it, snippet);
return src;
}
#if ENABLED(ENABLE_UNIT_TESTS)
#include "catch/catch.hpp"
+2
View File
@@ -36,6 +36,8 @@ enum class ShaderType
rdcstr GenerateGLSLShader(const rdcstr &shader, ShaderType type, int version,
const rdcstr &defines = "");
rdcstr InsertSnippetAfterVersion(ShaderType type, const char *source, int len, const char *snippet);
// for unit tests
struct ShaderReflection;
struct ShaderBindpointMapping;
+19
View File
@@ -247,3 +247,22 @@ cbuffer HistogramCBufferData REG(b0)
#define SINT_TEX 0
#endif
#if defined(__cplusplus)
struct RD_CustomShader_CBuffer_Type
{
uint4 TexDim;
uint SelectedMip;
uint TextureType;
uint SelectedSliceFace;
int SelectedSample;
uint4 YUVDownsampleRate;
uint4 YUVAChannels;
float2 SelectedRange;
};
// move to an #include since fxc barfs on it
#include "hlsl_custom_prefix.h"
#endif
+153
View File
@@ -0,0 +1,153 @@
#define HLSL_CUSTOM_PREFIX \
R"EOPREFIX(
#define RD_FLOAT_1D_ARRAY_BINDING t1
#define RD_FLOAT_1D_BINDING t1 // all textures treated as arrays, add macro aliases
#define RD_FLOAT_2D_ARRAY_BINDING t2
#define RD_FLOAT_2D_BINDING t2
#define RD_FLOAT_3D_BINDING t3
#define RD_FLOAT_DEPTH_BINDING t4
#define RD_FLOAT_DEPTH_ARRAY_BINDING t4
#define RD_FLOAT_STENCIL_BINDING t5
#define RD_FLOAT_STENCIL_ARRAY_BINDING t5
#define RD_FLOAT_DEPTHMS_BINDING t6
#define RD_FLOAT_DEPTHMS_ARRAY_BINDING t6
#define RD_FLOAT_STENCILMS_BINDING t7
#define RD_FLOAT_STENCILMS_ARRAY_BINDING t7
#define RD_FLOAT_2DMS_ARRAY_BINDING t9
#define RD_FLOAT_2DMS_BINDING t9
#define RD_FLOAT_YUV_ARRAY_BINDING t10
#define RD_FLOAT_YUV_BINDING t10
#define RD_INT_1D_ARRAY_BINDING t11
#define RD_INT_1D_BINDING t11
#define RD_INT_2D_ARRAY_BINDING t12
#define RD_INT_2D_BINDING t12
#define RD_INT_3D_BINDING t13
#define RD_INT_2DMS_ARRAY_BINDING t19
#define RD_INT_2DMS_BINDING t19
#define RD_UINT_1D_ARRAY_BINDING t21
#define RD_UINT_1D_BINDING t21
#define RD_UINT_2D_ARRAY_BINDING t22
#define RD_UINT_2D_BINDING t22
#define RD_UINT_3D_BINDING t23
#define RD_UINT_2DMS_ARRAY_BINDING t29
#define RD_UINT_2DMS_BINDING t29
#define RD_POINT_SAMPLER_BINDING s0
#define RD_LINEAR_SAMPLER_BINDING s1
#define RD_CONSTANT_BUFFER_BINDING b0
cbuffer RD_CBuffer_Type : register(RD_CONSTANT_BUFFER_BINDING)
{
struct RD_CBuffer_Struct
{
uint4 TexDim;
uint SelectedMip;
uint TextureType;
uint SelectedSliceFace;
int SelectedSample;
uint4 YUVDownsampleRate;
uint4 YUVAChannels;
float2 SelectedRange;
} RD_CBuffer_Data;
};
#define RD_TextureType_1D 1
#define RD_TextureType_2D 2
#define RD_TextureType_3D 3
#define RD_TextureType_Depth 4
#define RD_TextureType_DepthStencil 5
#define RD_TextureType_DepthMS 6
#define RD_TextureType_DepthStencilMS 7
#define RD_TextureType_2DMS 9
// for compatibility
#define RD_TextureType_Cube 999
#define RD_TextureType_Cube_Array 999
#define RD_TextureType_1D_Array 999
#define RD_TextureType_2D_Array 999
#define RD_TextureType_Rect 999
#define RD_TextureType_Buffer 999
#define RD_TextureType_2DMS_Array 999
// possible values (these are only return values from this function, NOT texture binding points):
// RD_TextureType_1D
// RD_TextureType_2D
// RD_TextureType_3D
// RD_TextureType_Depth (D3D only)
// RD_TextureType_DepthStencil (D3D only)
// RD_TextureType_DepthMS (D3D only)
// RD_TextureType_DepthStencilMS (D3D only)
// RD_TextureType_2DMS
uint RD_TextureType()
{
return RD_CBuffer_Data.TextureType;
}
// selected sample, or -numSamples for resolve
int RD_SelectedSample()
{
return RD_CBuffer_Data.SelectedSample;
}
uint RD_SelectedSliceFace()
{
return RD_CBuffer_Data.SelectedSliceFace;
}
uint RD_SelectedMip()
{
return RD_CBuffer_Data.SelectedMip;
}
// xyz = width, height, depth (or array size). w = # mips
uint4 RD_TexDim()
{
return RD_CBuffer_Data.TexDim;
}
// x = horizontal downsample rate (1 full rate, 2 half rate)
// y = vertical downsample rate
// z = number of planes in input texture
// w = number of bits per component (8, 10, 16)
uint4 RD_YUVDownsampleRate()
{
return RD_CBuffer_Data.YUVDownsampleRate;
}
// x = where Y channel comes from
// y = where U channel comes from
// z = where V channel comes from
// w = where A channel comes from
// each index will be [0,1,2,3] for xyzw in first plane,
// [4,5,6,7] for xyzw in second plane texture, etc.
// it will be 0xff = 255 if the channel does not exist.
uint4 RD_YUVAChannels()
{
return RD_CBuffer_Data.YUVAChannels;
}
// a pair with minimum and maximum selected range values
float2 RD_SelectedRange()
{
return RD_CBuffer_Data.SelectedRange;
}
)EOPREFIX"