mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-07-23 06:09:50 -06:00
Merge pull request #10781 from tellowkrinkle/UberVertexLoader
VideoCommon: Add dynamic vertex loader for ubershaders to reduce pipeline count
This commit is contained in:
@ -20,6 +20,8 @@ class NativeVertexFormat;
|
||||
// - Per-stage UBO (VS/GS/PS, VS constants accessible from PS)
|
||||
// - 8 combined image samplers (accessible from PS)
|
||||
// - 1 SSBO, accessible from PS if bounding box is enabled
|
||||
// - GX Uber
|
||||
// - Same as GX, plus one VS SSBO for vertices if dynamic vertex loading is enabled
|
||||
// - Utility
|
||||
// - Single UBO, accessible from all stages [set=0, binding=1]
|
||||
// - 8 combined image samplers (accessible from PS) [set=1, binding=0-7]
|
||||
@ -32,6 +34,7 @@ class NativeVertexFormat;
|
||||
enum class AbstractPipelineUsage
|
||||
{
|
||||
GX,
|
||||
GXUber,
|
||||
Utility
|
||||
};
|
||||
|
||||
|
@ -93,6 +93,13 @@ struct VertexShaderConstants
|
||||
|
||||
float4 cached_tangent;
|
||||
float4 cached_binormal;
|
||||
// For UberShader vertex loader
|
||||
u32 vertex_stride;
|
||||
std::array<u32, 3> vertex_offset_normals;
|
||||
u32 vertex_offset_position;
|
||||
u32 vertex_offset_posmtx;
|
||||
std::array<u32, 2> vertex_offset_colors;
|
||||
std::array<u32, 8> vertex_offset_texcoords;
|
||||
};
|
||||
|
||||
struct GeometryShaderConstants
|
||||
|
@ -58,9 +58,9 @@ struct PortableVertexDeclaration
|
||||
int stride;
|
||||
|
||||
AttributeFormat position;
|
||||
AttributeFormat normals[3];
|
||||
AttributeFormat colors[2];
|
||||
AttributeFormat texcoords[8];
|
||||
std::array<AttributeFormat, 3> normals;
|
||||
std::array<AttributeFormat, 2> colors;
|
||||
std::array<AttributeFormat, 8> texcoords;
|
||||
AttributeFormat posmtx;
|
||||
|
||||
inline bool operator<(const PortableVertexDeclaration& b) const
|
||||
|
@ -588,10 +588,10 @@ AbstractPipelineConfig ShaderCache::GetGXPipelineConfig(
|
||||
const NativeVertexFormat* vertex_format, const AbstractShader* vertex_shader,
|
||||
const AbstractShader* geometry_shader, const AbstractShader* pixel_shader,
|
||||
const RasterizationState& rasterization_state, const DepthState& depth_state,
|
||||
const BlendingState& blending_state)
|
||||
const BlendingState& blending_state, AbstractPipelineUsage usage)
|
||||
{
|
||||
AbstractPipelineConfig config = {};
|
||||
config.usage = AbstractPipelineUsage::GX;
|
||||
config.usage = usage;
|
||||
config.vertex_format = vertex_format;
|
||||
config.vertex_shader = vertex_shader;
|
||||
config.geometry_shader = geometry_shader;
|
||||
@ -735,7 +735,7 @@ ShaderCache::GetGXPipelineConfig(const GXPipelineUid& config_in)
|
||||
}
|
||||
|
||||
return GetGXPipelineConfig(config.vertex_format, vs, gs, ps, config.rasterization_state,
|
||||
config.depth_state, config.blending_state);
|
||||
config.depth_state, config.blending_state, AbstractPipelineUsage::GX);
|
||||
}
|
||||
|
||||
/// Edits the UID based on driver bugs and other special configurations
|
||||
@ -743,6 +743,8 @@ static GXUberPipelineUid ApplyDriverBugs(const GXUberPipelineUid& in)
|
||||
{
|
||||
GXUberPipelineUid out;
|
||||
memcpy(&out, &in, sizeof(out)); // Copy padding
|
||||
if (g_ActiveConfig.backend_info.bSupportsDynamicVertexLoader)
|
||||
out.vertex_format = nullptr;
|
||||
if (g_ActiveConfig.backend_info.bSupportsFramebufferFetch)
|
||||
{
|
||||
// Always blend in shader
|
||||
@ -798,7 +800,8 @@ ShaderCache::GetGXPipelineConfig(const GXUberPipelineUid& config_in)
|
||||
}
|
||||
|
||||
return GetGXPipelineConfig(config.vertex_format, vs, gs, ps, config.rasterization_state,
|
||||
config.depth_state, config.blending_state);
|
||||
config.depth_state, config.blending_state,
|
||||
AbstractPipelineUsage::GXUber);
|
||||
}
|
||||
|
||||
const AbstractPipeline* ShaderCache::InsertGXPipeline(const GXPipelineUid& config,
|
||||
@ -1233,32 +1236,32 @@ void ShaderCache::QueueUberShaderPipelines()
|
||||
dummy_vertex_decl.stride = sizeof(float) * 4;
|
||||
NativeVertexFormat* dummy_vertex_format =
|
||||
VertexLoaderManager::GetUberVertexFormat(dummy_vertex_decl);
|
||||
auto QueueDummyPipeline = [&](const UberShader::VertexShaderUid& vs_uid,
|
||||
const GeometryShaderUid& gs_uid,
|
||||
const UberShader::PixelShaderUid& ps_uid) {
|
||||
GXUberPipelineUid config;
|
||||
config.vertex_format = dummy_vertex_format;
|
||||
config.vs_uid = vs_uid;
|
||||
config.gs_uid = gs_uid;
|
||||
config.ps_uid = ps_uid;
|
||||
config.rasterization_state = RenderState::GetCullBackFaceRasterizationState(
|
||||
static_cast<PrimitiveType>(gs_uid.GetUidData()->primitive_type));
|
||||
config.depth_state = RenderState::GetNoDepthTestingDepthState();
|
||||
config.blending_state = RenderState::GetNoBlendingBlendState();
|
||||
if (ps_uid.GetUidData()->uint_output)
|
||||
{
|
||||
// uint_output is only ever enabled when logic ops are enabled.
|
||||
config.blending_state.logicopenable = true;
|
||||
config.blending_state.logicmode = LogicOp::And;
|
||||
}
|
||||
auto QueueDummyPipeline =
|
||||
[&](const UberShader::VertexShaderUid& vs_uid, const GeometryShaderUid& gs_uid,
|
||||
const UberShader::PixelShaderUid& ps_uid, const BlendingState& blend) {
|
||||
GXUberPipelineUid config;
|
||||
config.vertex_format = dummy_vertex_format;
|
||||
config.vs_uid = vs_uid;
|
||||
config.gs_uid = gs_uid;
|
||||
config.ps_uid = ps_uid;
|
||||
config.rasterization_state = RenderState::GetCullBackFaceRasterizationState(
|
||||
static_cast<PrimitiveType>(gs_uid.GetUidData()->primitive_type));
|
||||
config.depth_state = RenderState::GetNoDepthTestingDepthState();
|
||||
config.blending_state = blend;
|
||||
if (ps_uid.GetUidData()->uint_output)
|
||||
{
|
||||
// uint_output is only ever enabled when logic ops are enabled.
|
||||
config.blending_state.logicopenable = true;
|
||||
config.blending_state.logicmode = LogicOp::And;
|
||||
}
|
||||
|
||||
auto iter = m_gx_uber_pipeline_cache.find(config);
|
||||
if (iter != m_gx_uber_pipeline_cache.end())
|
||||
return;
|
||||
auto iter = m_gx_uber_pipeline_cache.find(config);
|
||||
if (iter != m_gx_uber_pipeline_cache.end())
|
||||
return;
|
||||
|
||||
auto& entry = m_gx_uber_pipeline_cache[config];
|
||||
entry.second = false;
|
||||
};
|
||||
auto& entry = m_gx_uber_pipeline_cache[config];
|
||||
entry.second = false;
|
||||
};
|
||||
|
||||
// Populate the pipeline configs with empty entries, these will be compiled afterwards.
|
||||
UberShader::EnumerateVertexShaderUids([&](const UberShader::VertexShaderUid& vuid) {
|
||||
@ -1275,7 +1278,45 @@ void ShaderCache::QueueUberShaderPipelines()
|
||||
{
|
||||
return;
|
||||
}
|
||||
QueueDummyPipeline(vuid, guid, cleared_puid);
|
||||
BlendingState blend = RenderState::GetNoBlendingBlendState();
|
||||
QueueDummyPipeline(vuid, guid, cleared_puid, blend);
|
||||
if (g_ActiveConfig.backend_info.bSupportsDynamicVertexLoader)
|
||||
{
|
||||
// Not all GPUs need all the pipeline state compiled into shaders, so they tend to key
|
||||
// compiled shaders based on some subset of the pipeline state.
|
||||
// Some test results:
|
||||
// (GPUs tested: AMD Radeon Pro 5600M, Nvidia GT 750M, Intel UHD 630,
|
||||
// Intel Iris Pro 5200, Apple M1)
|
||||
// MacOS Metal:
|
||||
// - AMD, Nvidia, Intel GPUs: Shaders are keyed on vertex layout and whether or not
|
||||
// dual source blend is enabled. That's it.
|
||||
// - Apple GPUs: Shaders are keyed on vertex layout and all blending settings. We use
|
||||
// framebuffer fetch here, so the only blending settings used by ubershaders are the
|
||||
// alphaupdate and colorupdate ones. Also keyed on primitive type, but Metal supports
|
||||
// setting it to "unknown" and we do for ubershaders (but MoltenVK won't).
|
||||
// Windows Vulkan:
|
||||
// - AMD, Nvidia: Definitely keyed on dual source blend, but the others seem more random
|
||||
// Changing a setting on one shader will require a recompile, but changing the same
|
||||
// setting on another won't. Compiling a copy with alphaupdate off, colorupdate off,
|
||||
// and one with DSB on seems to get pretty good coverage though.
|
||||
// Windows D3D12:
|
||||
// - AMD: Keyed on dual source blend and vertex layout
|
||||
// - Nvidia Kepler: No recompiles for changes to vertex layout or blend
|
||||
blend.alphaupdate = false;
|
||||
QueueDummyPipeline(vuid, guid, cleared_puid, blend);
|
||||
blend.alphaupdate = true;
|
||||
blend.colorupdate = false;
|
||||
QueueDummyPipeline(vuid, guid, cleared_puid, blend);
|
||||
blend.colorupdate = true;
|
||||
if (!cleared_puid.GetUidData()->no_dual_src && !cleared_puid.GetUidData()->uint_output)
|
||||
{
|
||||
blend.blendenable = true;
|
||||
blend.usedualsrc = true;
|
||||
blend.srcfactor = SrcBlendFactor::SrcAlpha;
|
||||
blend.dstfactor = DstBlendFactor::InvSrcAlpha;
|
||||
QueueDummyPipeline(vuid, guid, cleared_puid, blend);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -151,7 +151,7 @@ private:
|
||||
GetGXPipelineConfig(const NativeVertexFormat* vertex_format, const AbstractShader* vertex_shader,
|
||||
const AbstractShader* geometry_shader, const AbstractShader* pixel_shader,
|
||||
const RasterizationState& rasterization_state, const DepthState& depth_state,
|
||||
const BlendingState& blending_state);
|
||||
const BlendingState& blending_state, AbstractPipelineUsage usage);
|
||||
std::optional<AbstractPipelineConfig> GetGXPipelineConfig(const GXPipelineUid& uid);
|
||||
std::optional<AbstractPipelineConfig> GetGXPipelineConfig(const GXUberPipelineUid& uid);
|
||||
const AbstractPipeline* InsertGXPipeline(const GXPipelineUid& config,
|
||||
|
@ -43,6 +43,7 @@ ShaderHostConfig ShaderHostConfig::GetCurrent()
|
||||
bits.manual_texture_sampling_custom_texture_sizes =
|
||||
g_ActiveConfig.ManualTextureSamplingWithHiResTextures();
|
||||
bits.backend_sampler_lod_bias = g_ActiveConfig.backend_info.bSupportsLodBiasInSampler;
|
||||
bits.backend_dynamic_vertex_loader = g_ActiveConfig.backend_info.bSupportsDynamicVertexLoader;
|
||||
return bits;
|
||||
}
|
||||
|
||||
|
@ -177,6 +177,7 @@ union ShaderHostConfig
|
||||
BitField<24, 1, bool, u32> manual_texture_sampling;
|
||||
BitField<25, 1, bool, u32> manual_texture_sampling_custom_texture_sizes;
|
||||
BitField<26, 1, bool, u32> backend_sampler_lod_bias;
|
||||
BitField<27, 1, bool, u32> backend_dynamic_vertex_loader;
|
||||
|
||||
static ShaderHostConfig GetCurrent();
|
||||
};
|
||||
@ -302,6 +303,15 @@ static const char s_shader_uniforms[] = "\tuint components;\n"
|
||||
"\tuint4 xfmem_pack1[8];\n"
|
||||
"\tfloat4 " I_CACHED_TANGENT ";\n"
|
||||
"\tfloat4 " I_CACHED_BINORMAL ";\n"
|
||||
"\tuint vertex_stride;\n"
|
||||
"\tuint vertex_offset_rawnormal;\n"
|
||||
"\tuint vertex_offset_rawtangent;\n"
|
||||
"\tuint vertex_offset_rawbinormal;\n"
|
||||
"\tuint vertex_offset_rawpos;\n"
|
||||
"\tuint vertex_offset_posmtx;\n"
|
||||
"\tuint vertex_offset_rawcolor0;\n"
|
||||
"\tuint vertex_offset_rawcolor1;\n"
|
||||
"\tuint4 vertex_offset_rawtex[2];\n" // std140 is pain
|
||||
"\t#define xfmem_texMtxInfo(i) (xfmem_pack1[(i)].x)\n"
|
||||
"\t#define xfmem_postMtxInfo(i) (xfmem_pack1[(i)].y)\n"
|
||||
"\t#define xfmem_color(i) (xfmem_pack1[(i)].z)\n"
|
||||
|
@ -22,7 +22,11 @@ VertexShaderUid GetVertexShaderUid()
|
||||
return out;
|
||||
}
|
||||
|
||||
static void GenVertexShaderTexGens(APIType api_type, u32 num_texgen, ShaderCode& out);
|
||||
static void GenVertexShaderTexGens(APIType api_type, const ShaderHostConfig& host_config,
|
||||
u32 num_texgen, ShaderCode& out);
|
||||
static void LoadVertexAttribute(ShaderCode& code, const ShaderHostConfig& host_config, u32 indent,
|
||||
std::string_view name, std::string_view shader_type,
|
||||
std::string_view stored_type, std::string_view offset_name = {});
|
||||
|
||||
ShaderCode GenVertexShader(APIType api_type, const ShaderHostConfig& host_config,
|
||||
const vertex_ubershader_uid_data* uid_data)
|
||||
@ -50,15 +54,99 @@ ShaderCode GenVertexShader(APIType api_type, const ShaderHostConfig& host_config
|
||||
WriteBitfieldExtractHeader(out, api_type, host_config);
|
||||
WriteLightingFunction(out);
|
||||
|
||||
out.Write("ATTRIBUTE_LOCATION({}) in float4 rawpos;\n", SHADER_POSITION_ATTRIB);
|
||||
out.Write("ATTRIBUTE_LOCATION({}) in uint4 posmtx;\n", SHADER_POSMTX_ATTRIB);
|
||||
out.Write("ATTRIBUTE_LOCATION({}) in float3 rawnormal;\n", SHADER_NORMAL_ATTRIB);
|
||||
out.Write("ATTRIBUTE_LOCATION({}) in float3 rawtangent;\n", SHADER_TANGENT_ATTRIB);
|
||||
out.Write("ATTRIBUTE_LOCATION({}) in float3 rawbinormal;\n", SHADER_BINORMAL_ATTRIB);
|
||||
out.Write("ATTRIBUTE_LOCATION({}) in float4 rawcolor0;\n", SHADER_COLOR0_ATTRIB);
|
||||
out.Write("ATTRIBUTE_LOCATION({}) in float4 rawcolor1;\n", SHADER_COLOR1_ATTRIB);
|
||||
for (int i = 0; i < 8; ++i)
|
||||
out.Write("ATTRIBUTE_LOCATION({}) in float3 rawtex{};\n", SHADER_TEXTURE0_ATTRIB + i, i);
|
||||
if (host_config.backend_dynamic_vertex_loader)
|
||||
{
|
||||
out.Write(R"(
|
||||
SSBO_BINDING(1) readonly restrict buffer Vertices {{
|
||||
uint vertex_buffer[];
|
||||
}};
|
||||
)");
|
||||
if (api_type == APIType::D3D)
|
||||
{
|
||||
// Write a function to get an offset into vertex_buffer corresponding to this vertex.
|
||||
// This must be done differently for D3D compared to OpenGL/Vulkan/Metal, as on OpenGL, etc.,
|
||||
// gl_VertexID starts counting at the base vertex specified in glDrawElementsBaseVertex,
|
||||
// while on D3D, SV_VertexID (which spirv-cross translates gl_VertexID into) starts counting
|
||||
// at 0 regardless of the BaseVertexLocation value passed to DrawIndexed. In both cases,
|
||||
// offset 0 of vertex_buffer corresponds to index 0 with basevertex set to 0, so we have to
|
||||
// manually apply the basevertex offset for D3D
|
||||
// D3D12 uses a root constant for this uniform, since it changes with every draw.
|
||||
// D3D11 doesn't currently support dynamic vertex loader, and we'll have to figure something
|
||||
// out for it if we want to support it in the future.
|
||||
out.Write("UBO_BINDING(std140, 3) uniform DX_Constants {{\n"
|
||||
" uint base_vertex;\n"
|
||||
"}};\n\n"
|
||||
"uint GetVertexBaseOffset() {{\n"
|
||||
" return (gl_VertexID + base_vertex) * vertex_stride;\n"
|
||||
"}}\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
out.Write("uint GetVertexBaseOffset() {{\n"
|
||||
" return gl_VertexID * vertex_stride;\n"
|
||||
"}}\n");
|
||||
}
|
||||
|
||||
out.Write(R"(
|
||||
uint4 load_input_uint4_ubyte4(uint vtx_offset, uint attr_offset) {{
|
||||
uint value = vertex_buffer[vtx_offset + attr_offset];
|
||||
return uint4(value & 0xff, (value >> 8) & 0xff, (value >> 16) & 0xff, value >> 24);
|
||||
}}
|
||||
|
||||
float4 load_input_float4_ubyte4(uint vtx_offset, uint attr_offset) {{
|
||||
return float4(load_input_uint4_ubyte4(vtx_offset, attr_offset)) / 255.0f;
|
||||
}}
|
||||
|
||||
float3 load_input_float3_float3(uint vtx_offset, uint attr_offset) {{
|
||||
uint offset = vtx_offset + attr_offset;
|
||||
return float3(uintBitsToFloat(vertex_buffer[offset + 0]),
|
||||
uintBitsToFloat(vertex_buffer[offset + 1]),
|
||||
uintBitsToFloat(vertex_buffer[offset + 2]));
|
||||
}}
|
||||
|
||||
float4 load_input_float4_rawpos(uint vtx_offset, uint attr_offset) {{
|
||||
uint components = attr_offset >> 16;
|
||||
uint offset = vtx_offset + (attr_offset & 0xffff);
|
||||
if (components < 3)
|
||||
return float4(uintBitsToFloat(vertex_buffer[offset + 0]),
|
||||
uintBitsToFloat(vertex_buffer[offset + 1]),
|
||||
0.0f, 1.0f);
|
||||
else
|
||||
return float4(uintBitsToFloat(vertex_buffer[offset + 0]),
|
||||
uintBitsToFloat(vertex_buffer[offset + 1]),
|
||||
uintBitsToFloat(vertex_buffer[offset + 2]),
|
||||
1.0f);
|
||||
}}
|
||||
|
||||
float3 load_input_float3_rawtex(uint vtx_offset, uint attr_offset) {{
|
||||
uint components = attr_offset >> 16;
|
||||
uint offset = vtx_offset + (attr_offset & 0xffff);
|
||||
if (components < 2)
|
||||
return float3(uintBitsToFloat(vertex_buffer[offset + 0]), 0.0f, 0.0f);
|
||||
else if (components < 3)
|
||||
return float3(uintBitsToFloat(vertex_buffer[offset + 0]),
|
||||
uintBitsToFloat(vertex_buffer[offset + 1]),
|
||||
0.0f);
|
||||
else
|
||||
return float3(uintBitsToFloat(vertex_buffer[offset + 0]),
|
||||
uintBitsToFloat(vertex_buffer[offset + 1]),
|
||||
uintBitsToFloat(vertex_buffer[offset + 2]));
|
||||
}}
|
||||
|
||||
)");
|
||||
}
|
||||
else
|
||||
{
|
||||
out.Write("ATTRIBUTE_LOCATION({}) in float4 rawpos;\n", SHADER_POSITION_ATTRIB);
|
||||
out.Write("ATTRIBUTE_LOCATION({}) in uint4 posmtx;\n", SHADER_POSMTX_ATTRIB);
|
||||
out.Write("ATTRIBUTE_LOCATION({}) in float3 rawnormal;\n", SHADER_NORMAL_ATTRIB);
|
||||
out.Write("ATTRIBUTE_LOCATION({}) in float3 rawtangent;\n", SHADER_TANGENT_ATTRIB);
|
||||
out.Write("ATTRIBUTE_LOCATION({}) in float3 rawbinormal;\n", SHADER_BINORMAL_ATTRIB);
|
||||
out.Write("ATTRIBUTE_LOCATION({}) in float4 rawcolor0;\n", SHADER_COLOR0_ATTRIB);
|
||||
out.Write("ATTRIBUTE_LOCATION({}) in float4 rawcolor1;\n", SHADER_COLOR1_ATTRIB);
|
||||
for (int i = 0; i < 8; ++i)
|
||||
out.Write("ATTRIBUTE_LOCATION({}) in float3 rawtex{};\n", SHADER_TEXTURE0_ATTRIB + i, i);
|
||||
}
|
||||
|
||||
if (host_config.backend_geometry_shaders)
|
||||
{
|
||||
@ -99,7 +187,12 @@ ShaderCode GenVertexShader(APIType api_type, const ShaderHostConfig& host_config
|
||||
|
||||
out.Write("VS_OUTPUT o;\n"
|
||||
"\n");
|
||||
|
||||
if (host_config.backend_dynamic_vertex_loader)
|
||||
{
|
||||
out.Write("uint vertex_base_offset = GetVertexBaseOffset();\n");
|
||||
}
|
||||
// rawpos is always needed
|
||||
LoadVertexAttribute(out, host_config, 0, "rawpos", "float4", "rawpos");
|
||||
// Transforms
|
||||
out.Write("// Position matrix\n"
|
||||
"float4 P0;\n"
|
||||
@ -113,6 +206,7 @@ ShaderCode GenVertexShader(APIType api_type, const ShaderHostConfig& host_config
|
||||
"\n"
|
||||
"if ((components & {}u) != 0u) {{ // VB_HAS_POSMTXIDX\n",
|
||||
VB_HAS_POSMTXIDX);
|
||||
LoadVertexAttribute(out, host_config, 2, "posmtx", "uint4", "ubyte4");
|
||||
out.Write(" // Vertex format has a per-vertex matrix\n"
|
||||
" int posidx = int(posmtx.r);\n"
|
||||
" P0 = " I_TRANSFORMMATRICES "[posidx];\n"
|
||||
@ -144,27 +238,40 @@ ShaderCode GenVertexShader(APIType api_type, const ShaderHostConfig& host_config
|
||||
"// by lighting calculations and needs to be unit length), the same transform matrix\n"
|
||||
"// can do double duty, scaling for emboss mapping, and not scaling for lighting.\n"
|
||||
"float3 _normal = float3(0.0, 0.0, 0.0);\n"
|
||||
"if ((components & {}u) != 0u) // VB_HAS_NORMAL\n",
|
||||
"if ((components & {}u) != 0u) // VB_HAS_NORMAL\n"
|
||||
"{{\n",
|
||||
VB_HAS_NORMAL);
|
||||
LoadVertexAttribute(out, host_config, 2, "rawnormal", "float3", "float3");
|
||||
out.Write(" _normal = normalize(float3(dot(N0, rawnormal), dot(N1, rawnormal), dot(N2, "
|
||||
"rawnormal)));\n"
|
||||
"}}\n"
|
||||
"\n"
|
||||
"float3 _tangent = float3(0.0, 0.0, 0.0);\n"
|
||||
"if ((components & {}u) != 0u) // VB_HAS_TANGENT\n",
|
||||
"if ((components & {}u) != 0u) // VB_HAS_TANGENT\n"
|
||||
"{{\n",
|
||||
VB_HAS_TANGENT);
|
||||
LoadVertexAttribute(out, host_config, 2, "rawtangent", "float3", "float3");
|
||||
out.Write(" _tangent = float3(dot(N0, rawtangent), dot(N1, rawtangent), dot(N2, rawtangent));\n"
|
||||
"}}\n"
|
||||
"else\n"
|
||||
"{{\n"
|
||||
" _tangent = float3(dot(N0, " I_CACHED_TANGENT ".xyz), dot(N1, " I_CACHED_TANGENT
|
||||
".xyz), dot(N2, " I_CACHED_TANGENT ".xyz));\n"
|
||||
"}}\n"
|
||||
"\n"
|
||||
"float3 _binormal = float3(0.0, 0.0, 0.0);\n"
|
||||
"if ((components & {}u) != 0u) // VB_HAS_BINORMAL\n",
|
||||
"if ((components & {}u) != 0u) // VB_HAS_BINORMAL\n"
|
||||
"{{\n",
|
||||
VB_HAS_BINORMAL);
|
||||
LoadVertexAttribute(out, host_config, 2, "rawbinormal", "float3", "float3");
|
||||
out.Write(" _binormal = float3(dot(N0, rawbinormal), dot(N1, rawbinormal), dot(N2, "
|
||||
"rawbinormal));\n"
|
||||
"}}\n"
|
||||
"else\n"
|
||||
"{{\n"
|
||||
" _binormal = float3(dot(N0, " I_CACHED_BINORMAL ".xyz), dot(N1, " I_CACHED_BINORMAL
|
||||
".xyz), dot(N2, " I_CACHED_BINORMAL ".xyz));\n"
|
||||
"}}\n"
|
||||
"\n");
|
||||
|
||||
// Hardware Lighting
|
||||
@ -178,34 +285,40 @@ ShaderCode GenVertexShader(APIType api_type, const ShaderHostConfig& host_config
|
||||
"bool use_color_1 = ((components & {0}u) == {0}u); // VB_HAS_COL0 | VB_HAS_COL1\n",
|
||||
VB_HAS_COL0 | VB_HAS_COL1);
|
||||
|
||||
out.Write("for (uint color = 0u; color < {}u; color++) {{\n", NUM_XF_COLOR_CHANNELS);
|
||||
out.Write(" if ((color == 0u || use_color_1) && (components & ({}u << color)) != 0u) {{\n",
|
||||
VB_HAS_COL0);
|
||||
out.Write(" // Use color0 for channel 0, and color1 for channel 1 if both colors 0 and 1 are "
|
||||
"present.\n"
|
||||
" if (color == 0u)\n"
|
||||
" vertex_color_0 = rawcolor0;\n"
|
||||
" else\n"
|
||||
" vertex_color_1 = rawcolor1;\n"
|
||||
" }} else if (color == 0u && (components & {}u) != 0u) {{\n",
|
||||
VB_HAS_COL1);
|
||||
out.Write(" // Use color1 for channel 0 if color0 is not present.\n"
|
||||
" vertex_color_0 = rawcolor1;\n"
|
||||
" }} else {{\n"
|
||||
" if (color == 0u)\n"
|
||||
" vertex_color_0 = missing_color_value;\n"
|
||||
" else\n"
|
||||
" vertex_color_1 = missing_color_value;\n"
|
||||
" }}\n"
|
||||
out.Write("if ((components & {0}u) == {0}u) // VB_HAS_COL0 | VB_HAS_COL1\n"
|
||||
"{{\n",
|
||||
VB_HAS_COL0 | VB_HAS_COL1);
|
||||
LoadVertexAttribute(out, host_config, 2, "rawcolor0", "float4", "ubyte4");
|
||||
LoadVertexAttribute(out, host_config, 2, "rawcolor1", "float4", "ubyte4");
|
||||
out.Write(" vertex_color_0 = rawcolor0;\n"
|
||||
" vertex_color_1 = rawcolor1;\n"
|
||||
"}}\n"
|
||||
"\n");
|
||||
"else if ((components & {}u) != 0u) // VB_HAS_COL0\n"
|
||||
"{{\n",
|
||||
VB_HAS_COL0);
|
||||
LoadVertexAttribute(out, host_config, 2, "rawcolor0", "float4", "ubyte4");
|
||||
out.Write(" vertex_color_0 = rawcolor0;\n"
|
||||
" vertex_color_1 = rawcolor0;\n"
|
||||
"}}\n"
|
||||
"else if ((components & {}u) != 0u) // VB_HAS_COL1\n"
|
||||
"{{\n",
|
||||
VB_HAS_COL1);
|
||||
LoadVertexAttribute(out, host_config, 2, "rawcolor1", "float4", "ubyte4");
|
||||
out.Write(" vertex_color_0 = rawcolor1;\n"
|
||||
" vertex_color_1 = rawcolor1;\n"
|
||||
"}}\n"
|
||||
"else\n"
|
||||
"{{\n"
|
||||
" vertex_color_0 = missing_color_value;\n"
|
||||
" vertex_color_1 = missing_color_value;\n"
|
||||
"}}\n");
|
||||
|
||||
WriteVertexLighting(out, api_type, "pos.xyz", "_normal", "vertex_color_0", "vertex_color_1",
|
||||
"o.colors_0", "o.colors_1");
|
||||
|
||||
// Texture Coordinates
|
||||
if (num_texgen > 0)
|
||||
GenVertexShaderTexGens(api_type, num_texgen, out);
|
||||
GenVertexShaderTexGens(api_type, host_config, num_texgen, out);
|
||||
|
||||
if (per_pixel_lighting)
|
||||
{
|
||||
@ -352,7 +465,8 @@ ShaderCode GenVertexShader(APIType api_type, const ShaderHostConfig& host_config
|
||||
return out;
|
||||
}
|
||||
|
||||
static void GenVertexShaderTexGens(APIType api_type, u32 num_texgen, ShaderCode& out)
|
||||
static void GenVertexShaderTexGens(APIType api_type, const ShaderHostConfig& host_config,
|
||||
u32 num_texgen, ShaderCode& out)
|
||||
{
|
||||
// The HLSL compiler complains that the output texture coordinates are uninitialized when trying
|
||||
// to dynamically index them.
|
||||
@ -377,27 +491,40 @@ static void GenVertexShaderTexGens(APIType api_type, u32 num_texgen, ShaderCode&
|
||||
out.Write(" coord.xyz = rawpos.xyz;\n");
|
||||
out.Write(" break;\n\n");
|
||||
out.Write(" case {:s}:\n", SourceRow::Normal);
|
||||
out.Write(" coord.xyz = ((components & {}u /* VB_HAS_NORMAL */) != 0u) ? rawnormal.xyz : "
|
||||
"coord.xyz;",
|
||||
out.Write(" if ((components & {}u) != 0u) // VB_HAS_NORMAL\n"
|
||||
" {{\n",
|
||||
VB_HAS_NORMAL);
|
||||
out.Write(" break;\n\n");
|
||||
LoadVertexAttribute(out, host_config, 6, "rawnormal", "float3", "float3");
|
||||
out.Write(" coord.xyz = rawnormal.xyz;\n"
|
||||
" }}\n"
|
||||
" break;\n\n");
|
||||
out.Write(" case {:s}:\n", SourceRow::BinormalT);
|
||||
out.Write(" coord.xyz = ((components & {}u /* VB_HAS_TANGENT */) != 0u) ? rawtangent.xyz : "
|
||||
"coord.xyz;",
|
||||
out.Write(" if ((components & {}u) != 0u) // VB_HAS_TANGENT\n"
|
||||
" {{\n",
|
||||
VB_HAS_TANGENT);
|
||||
out.Write(" break;\n\n");
|
||||
LoadVertexAttribute(out, host_config, 6, "rawtangent", "float3", "float3");
|
||||
out.Write(" coord.xyz = rawtangent.xyz;\n"
|
||||
" }}\n"
|
||||
" break;\n\n");
|
||||
out.Write(" case {:s}:\n", SourceRow::BinormalB);
|
||||
out.Write(" coord.xyz = ((components & {}u /* VB_HAS_BINORMAL */) != 0u) ? rawbinormal.xyz : "
|
||||
"coord.xyz;",
|
||||
out.Write(" if ((components & {}u) != 0u) // VB_HAS_BINORMAL\n"
|
||||
" {{\n",
|
||||
VB_HAS_BINORMAL);
|
||||
out.Write(" break;\n\n");
|
||||
LoadVertexAttribute(out, host_config, 6, "rawbinormal", "float3", "float3");
|
||||
out.Write(" coord.xyz = rawbinormal.xyz;\n"
|
||||
" }}\n"
|
||||
" break;\n\n");
|
||||
for (u32 i = 0; i < 8; i++)
|
||||
{
|
||||
out.Write(" case {:s}:\n", static_cast<SourceRow>(static_cast<u32>(SourceRow::Tex0) + i));
|
||||
out.Write(
|
||||
" coord = ((components & {}u /* VB_HAS_UV{} */) != 0u) ? float4(rawtex{}.x, rawtex{}.y, "
|
||||
"1.0, 1.0) : coord;\n",
|
||||
VB_HAS_UV0 << i, i, i, i);
|
||||
out.Write(" if ((components & {}u) != 0u) // VB_HAS_UV{}\n"
|
||||
" {{\n",
|
||||
VB_HAS_UV0 << i, i);
|
||||
LoadVertexAttribute(out, host_config, 6, fmt::format("rawtex{}", i), "float3", "rawtex",
|
||||
fmt::format("rawtex[{}][{}]", i / 4, i % 4));
|
||||
out.Write(" coord = float4(rawtex{}.x, rawtex{}.y, 1.0f, 1.0f);\n"
|
||||
" }}\n",
|
||||
i, i);
|
||||
out.Write(" break;\n\n");
|
||||
}
|
||||
out.Write(" }}\n"
|
||||
@ -447,14 +574,24 @@ static void GenVertexShaderTexGens(APIType api_type, u32 num_texgen, ShaderCode&
|
||||
" {{\n");
|
||||
out.Write(" if ((components & ({}u /* VB_HAS_TEXMTXIDX0 */ << texgen)) != 0u) {{\n",
|
||||
VB_HAS_TEXMTXIDX0);
|
||||
out.Write(" // This is messy, due to dynamic indexing of the input texture coordinates.\n"
|
||||
" // Hopefully the compiler will unroll this whole loop anyway and the switch.\n"
|
||||
" int tmp = 0;\n"
|
||||
" switch (texgen) {{\n");
|
||||
for (u32 i = 0; i < num_texgen; i++)
|
||||
out.Write(" case {}u: tmp = int(rawtex{}.z); break;\n", i, i);
|
||||
out.Write(" }}\n"
|
||||
"\n");
|
||||
if (host_config.backend_dynamic_vertex_loader)
|
||||
{
|
||||
out.Write(" int tmp = int(load_input_float3_rawtex(vertex_base_offset, "
|
||||
"vertex_offset_rawtex[texgen / 4][texgen % 4]).z);\n"
|
||||
"\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
out.Write(
|
||||
" // This is messy, due to dynamic indexing of the input texture coordinates.\n"
|
||||
" // Hopefully the compiler will unroll this whole loop anyway and the switch.\n"
|
||||
" int tmp = 0;\n"
|
||||
" switch (texgen) {{\n");
|
||||
for (u32 i = 0; i < num_texgen; i++)
|
||||
out.Write(" case {}u: tmp = int(rawtex{}.z); break;\n", i, i);
|
||||
out.Write(" }}\n"
|
||||
"\n");
|
||||
}
|
||||
out.Write(" if ({} == {:s}) {{\n", BitfieldExtract<&TexMtxInfo::projection>("texMtxInfo"),
|
||||
TexSize::STQ);
|
||||
out.Write(" output_tex.xyz = float3(dot(coord, " I_TRANSFORMMATRICES "[tmp]),\n"
|
||||
@ -514,6 +651,19 @@ static void GenVertexShaderTexGens(APIType api_type, u32 num_texgen, ShaderCode&
|
||||
"}}\n");
|
||||
}
|
||||
|
||||
static void LoadVertexAttribute(ShaderCode& code, const ShaderHostConfig& host_config, u32 indent,
|
||||
std::string_view name, std::string_view shader_type,
|
||||
std::string_view stored_type, std::string_view offset_name)
|
||||
{
|
||||
if (host_config.backend_dynamic_vertex_loader)
|
||||
{
|
||||
code.Write("{:{}}{} {} = load_input_{}_{}(vertex_base_offset, vertex_offset_{});\n", "", indent,
|
||||
shader_type, name, shader_type, stored_type,
|
||||
offset_name.empty() ? name : offset_name);
|
||||
}
|
||||
// else inputs are always available
|
||||
}
|
||||
|
||||
void EnumerateVertexShaderUids(const std::function<void(const VertexShaderUid&)>& callback)
|
||||
{
|
||||
VertexShaderUid uid;
|
||||
|
@ -353,7 +353,8 @@ int RunVertices(int vtx_attr_group, OpcodeDecoder::Primitive primitive, int coun
|
||||
}
|
||||
s_current_vtx_fmt = loader->m_native_vertex_format;
|
||||
g_current_components = loader->m_native_components;
|
||||
VertexShaderManager::SetVertexFormat(loader->m_native_components);
|
||||
VertexShaderManager::SetVertexFormat(loader->m_native_components,
|
||||
loader->m_native_vertex_format->GetVertexDeclaration());
|
||||
|
||||
// if cull mode is CULL_ALL, tell VertexManager to skip triangles and quads.
|
||||
// They still need to go through vertex loading, because we need to calculate a zfreeze refrence
|
||||
|
@ -606,13 +606,42 @@ void VertexShaderManager::SetMaterialColorChanged(int index)
|
||||
nMaterialsChanged[index] = true;
|
||||
}
|
||||
|
||||
void VertexShaderManager::SetVertexFormat(u32 components)
|
||||
static void UpdateValue(bool* dirty, u32* old_value, u32 new_value)
|
||||
{
|
||||
if (components != constants.components)
|
||||
{
|
||||
constants.components = components;
|
||||
dirty = true;
|
||||
}
|
||||
if (*old_value == new_value)
|
||||
return;
|
||||
*old_value = new_value;
|
||||
*dirty = true;
|
||||
}
|
||||
|
||||
static void UpdateOffset(bool* dirty, bool include_components, u32* old_value,
|
||||
const AttributeFormat& attribute)
|
||||
{
|
||||
if (!attribute.enable)
|
||||
return;
|
||||
u32 new_value = attribute.offset / 4; // GPU uses uint offsets
|
||||
if (include_components)
|
||||
new_value |= attribute.components << 16;
|
||||
UpdateValue(dirty, old_value, new_value);
|
||||
}
|
||||
|
||||
template <size_t N>
|
||||
static void UpdateOffsets(bool* dirty, bool include_components, std::array<u32, N>* old_value,
|
||||
const std::array<AttributeFormat, N>& attribute)
|
||||
{
|
||||
for (size_t i = 0; i < N; i++)
|
||||
UpdateOffset(dirty, include_components, &(*old_value)[i], attribute[i]);
|
||||
}
|
||||
|
||||
void VertexShaderManager::SetVertexFormat(u32 components, const PortableVertexDeclaration& format)
|
||||
{
|
||||
UpdateValue(&dirty, &constants.components, components);
|
||||
UpdateValue(&dirty, &constants.vertex_stride, format.stride / 4);
|
||||
UpdateOffset(&dirty, true, &constants.vertex_offset_position, format.position);
|
||||
UpdateOffset(&dirty, false, &constants.vertex_offset_posmtx, format.posmtx);
|
||||
UpdateOffsets(&dirty, true, &constants.vertex_offset_texcoords, format.texcoords);
|
||||
UpdateOffsets(&dirty, false, &constants.vertex_offset_colors, format.colors);
|
||||
UpdateOffsets(&dirty, false, &constants.vertex_offset_normals, format.normals);
|
||||
}
|
||||
|
||||
void VertexShaderManager::SetTexMatrixInfoChanged(int index)
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "VideoCommon/ConstantManager.h"
|
||||
|
||||
class PointerWrap;
|
||||
struct PortableVertexDeclaration;
|
||||
|
||||
// The non-API dependent parts.
|
||||
class VertexShaderManager
|
||||
@ -29,7 +30,7 @@ public:
|
||||
static void SetProjectionChanged();
|
||||
static void SetMaterialColorChanged(int index);
|
||||
|
||||
static void SetVertexFormat(u32 components);
|
||||
static void SetVertexFormat(u32 components, const PortableVertexDeclaration& format);
|
||||
static void SetTexMatrixInfoChanged(int index);
|
||||
static void SetLightingConfigChanged();
|
||||
|
||||
|
@ -232,6 +232,7 @@ struct VideoConfig final
|
||||
bool bSupportsLodBiasInSampler = false;
|
||||
bool bSupportsSettingObjectNames = false;
|
||||
bool bSupportsPartialMultisampleResolve = false;
|
||||
bool bSupportsDynamicVertexLoader = false;
|
||||
} backend_info;
|
||||
|
||||
// Utility
|
||||
|
Reference in New Issue
Block a user