perf(shader): restore prepared vertex inputs + harden buffer specialization
This commit is contained in:
parent
7f56e04157
commit
7431e5a6e1
|
|
@ -20,37 +20,39 @@ struct VertexFormat {
|
|||
|
||||
inline VertexFormat DecodeVertexFormat(const ShaderRecompiler::VertexAttribute& attribute) {
|
||||
Require(attribute.components >= 1 && attribute.components <= 4, "invalid vertex attribute component count");
|
||||
const auto components = attribute.formatComponents == 0u ? attribute.components : attribute.formatComponents;
|
||||
Require(components >= 1u && components <= 4u, "invalid vertex format component count");
|
||||
const auto format = (attribute.resource.fields[3] >> 12u) & 0x7fu;
|
||||
switch (format) {
|
||||
case 1: { const std::array formats{VK_FORMAT_R8_UNORM}; const auto count = std::min(attribute.components, 1u); return {formats[count - 1], count * 1u, 1u, "f32"}; }
|
||||
case 2: { const std::array formats{VK_FORMAT_R8_SNORM}; const auto count = std::min(attribute.components, 1u); return {formats[count - 1], count * 1u, 1u, "f32"}; }
|
||||
case 3: { const std::array formats{VK_FORMAT_R8_USCALED}; const auto count = std::min(attribute.components, 1u); return {formats[count - 1], count * 1u, 1u, "f32"}; }
|
||||
case 4: { const std::array formats{VK_FORMAT_R8_SSCALED}; const auto count = std::min(attribute.components, 1u); return {formats[count - 1], count * 1u, 1u, "f32"}; }
|
||||
case 5: { const std::array formats{VK_FORMAT_R8_UINT}; const auto count = std::min(attribute.components, 1u); return {formats[count - 1], count * 1u, 1u, "u32"}; }
|
||||
case 6: { const std::array formats{VK_FORMAT_R8_SINT}; const auto count = std::min(attribute.components, 1u); return {formats[count - 1], count * 1u, 1u, "i32"}; }
|
||||
case 7: { const std::array formats{VK_FORMAT_R16_UNORM}; const auto count = std::min(attribute.components, 1u); return {formats[count - 1], count * 2u, 2u, "f32"}; }
|
||||
case 8: { const std::array formats{VK_FORMAT_R16_SNORM}; const auto count = std::min(attribute.components, 1u); return {formats[count - 1], count * 2u, 2u, "f32"}; }
|
||||
case 9: { const std::array formats{VK_FORMAT_R16_USCALED}; const auto count = std::min(attribute.components, 1u); return {formats[count - 1], count * 2u, 2u, "f32"}; }
|
||||
case 10: { const std::array formats{VK_FORMAT_R16_SSCALED}; const auto count = std::min(attribute.components, 1u); return {formats[count - 1], count * 2u, 2u, "f32"}; }
|
||||
case 11: { const std::array formats{VK_FORMAT_R16_UINT}; const auto count = std::min(attribute.components, 1u); return {formats[count - 1], count * 2u, 2u, "u32"}; }
|
||||
case 12: { const std::array formats{VK_FORMAT_R16_SINT}; const auto count = std::min(attribute.components, 1u); return {formats[count - 1], count * 2u, 2u, "i32"}; }
|
||||
case 13: { const std::array formats{VK_FORMAT_R16_SFLOAT}; const auto count = std::min(attribute.components, 1u); return {formats[count - 1], count * 2u, 2u, "f32"}; }
|
||||
case 14: { const std::array formats{VK_FORMAT_R8_UNORM, VK_FORMAT_R8G8_UNORM}; const auto count = std::min(attribute.components, 2u); return {formats[count - 1], count * 1u, 1u, "f32"}; }
|
||||
case 15: { const std::array formats{VK_FORMAT_R8_SNORM, VK_FORMAT_R8G8_SNORM}; const auto count = std::min(attribute.components, 2u); return {formats[count - 1], count * 1u, 1u, "f32"}; }
|
||||
case 16: { const std::array formats{VK_FORMAT_R8_USCALED, VK_FORMAT_R8G8_USCALED}; const auto count = std::min(attribute.components, 2u); return {formats[count - 1], count * 1u, 1u, "f32"}; }
|
||||
case 17: { const std::array formats{VK_FORMAT_R8_SSCALED, VK_FORMAT_R8G8_SSCALED}; const auto count = std::min(attribute.components, 2u); return {formats[count - 1], count * 1u, 1u, "f32"}; }
|
||||
case 18: { const std::array formats{VK_FORMAT_R8_UINT, VK_FORMAT_R8G8_UINT}; const auto count = std::min(attribute.components, 2u); return {formats[count - 1], count * 1u, 1u, "u32"}; }
|
||||
case 19: { const std::array formats{VK_FORMAT_R8_SINT, VK_FORMAT_R8G8_SINT}; const auto count = std::min(attribute.components, 2u); return {formats[count - 1], count * 1u, 1u, "i32"}; }
|
||||
case 20: { const std::array formats{VK_FORMAT_R32_UINT}; const auto count = std::min(attribute.components, 1u); return {formats[count - 1], count * 4u, 4u, "u32"}; }
|
||||
case 21: { const std::array formats{VK_FORMAT_R32_SINT}; const auto count = std::min(attribute.components, 1u); return {formats[count - 1], count * 4u, 4u, "i32"}; }
|
||||
case 22: { const std::array formats{VK_FORMAT_R32_SFLOAT}; const auto count = std::min(attribute.components, 1u); return {formats[count - 1], count * 4u, 4u, "f32"}; }
|
||||
case 23: { const std::array formats{VK_FORMAT_R16_UNORM, VK_FORMAT_R16G16_UNORM}; const auto count = std::min(attribute.components, 2u); return {formats[count - 1], count * 2u, 2u, "f32"}; }
|
||||
case 24: { const std::array formats{VK_FORMAT_R16_SNORM, VK_FORMAT_R16G16_SNORM}; const auto count = std::min(attribute.components, 2u); return {formats[count - 1], count * 2u, 2u, "f32"}; }
|
||||
case 25: { const std::array formats{VK_FORMAT_R16_USCALED, VK_FORMAT_R16G16_USCALED}; const auto count = std::min(attribute.components, 2u); return {formats[count - 1], count * 2u, 2u, "f32"}; }
|
||||
case 26: { const std::array formats{VK_FORMAT_R16_SSCALED, VK_FORMAT_R16G16_SSCALED}; const auto count = std::min(attribute.components, 2u); return {formats[count - 1], count * 2u, 2u, "f32"}; }
|
||||
case 27: { const std::array formats{VK_FORMAT_R16_UINT, VK_FORMAT_R16G16_UINT}; const auto count = std::min(attribute.components, 2u); return {formats[count - 1], count * 2u, 2u, "u32"}; }
|
||||
case 28: { const std::array formats{VK_FORMAT_R16_SINT, VK_FORMAT_R16G16_SINT}; const auto count = std::min(attribute.components, 2u); return {formats[count - 1], count * 2u, 2u, "i32"}; }
|
||||
case 29: { const std::array formats{VK_FORMAT_R16_SFLOAT, VK_FORMAT_R16G16_SFLOAT}; const auto count = std::min(attribute.components, 2u); return {formats[count - 1], count * 2u, 2u, "f32"}; }
|
||||
case 1: { const std::array formats{VK_FORMAT_R8_UNORM}; const auto count = std::min(components, 1u); return {formats[count - 1], count * 1u, 1u, "f32"}; }
|
||||
case 2: { const std::array formats{VK_FORMAT_R8_SNORM}; const auto count = std::min(components, 1u); return {formats[count - 1], count * 1u, 1u, "f32"}; }
|
||||
case 3: { const std::array formats{VK_FORMAT_R8_USCALED}; const auto count = std::min(components, 1u); return {formats[count - 1], count * 1u, 1u, "f32"}; }
|
||||
case 4: { const std::array formats{VK_FORMAT_R8_SSCALED}; const auto count = std::min(components, 1u); return {formats[count - 1], count * 1u, 1u, "f32"}; }
|
||||
case 5: { const std::array formats{VK_FORMAT_R8_UINT}; const auto count = std::min(components, 1u); return {formats[count - 1], count * 1u, 1u, "u32"}; }
|
||||
case 6: { const std::array formats{VK_FORMAT_R8_SINT}; const auto count = std::min(components, 1u); return {formats[count - 1], count * 1u, 1u, "i32"}; }
|
||||
case 7: { const std::array formats{VK_FORMAT_R16_UNORM}; const auto count = std::min(components, 1u); return {formats[count - 1], count * 2u, 2u, "f32"}; }
|
||||
case 8: { const std::array formats{VK_FORMAT_R16_SNORM}; const auto count = std::min(components, 1u); return {formats[count - 1], count * 2u, 2u, "f32"}; }
|
||||
case 9: { const std::array formats{VK_FORMAT_R16_USCALED}; const auto count = std::min(components, 1u); return {formats[count - 1], count * 2u, 2u, "f32"}; }
|
||||
case 10: { const std::array formats{VK_FORMAT_R16_SSCALED}; const auto count = std::min(components, 1u); return {formats[count - 1], count * 2u, 2u, "f32"}; }
|
||||
case 11: { const std::array formats{VK_FORMAT_R16_UINT}; const auto count = std::min(components, 1u); return {formats[count - 1], count * 2u, 2u, "u32"}; }
|
||||
case 12: { const std::array formats{VK_FORMAT_R16_SINT}; const auto count = std::min(components, 1u); return {formats[count - 1], count * 2u, 2u, "i32"}; }
|
||||
case 13: { const std::array formats{VK_FORMAT_R16_SFLOAT}; const auto count = std::min(components, 1u); return {formats[count - 1], count * 2u, 2u, "f32"}; }
|
||||
case 14: { const std::array formats{VK_FORMAT_R8_UNORM, VK_FORMAT_R8G8_UNORM}; const auto count = std::min(components, 2u); return {formats[count - 1], count * 1u, 1u, "f32"}; }
|
||||
case 15: { const std::array formats{VK_FORMAT_R8_SNORM, VK_FORMAT_R8G8_SNORM}; const auto count = std::min(components, 2u); return {formats[count - 1], count * 1u, 1u, "f32"}; }
|
||||
case 16: { const std::array formats{VK_FORMAT_R8_USCALED, VK_FORMAT_R8G8_USCALED}; const auto count = std::min(components, 2u); return {formats[count - 1], count * 1u, 1u, "f32"}; }
|
||||
case 17: { const std::array formats{VK_FORMAT_R8_SSCALED, VK_FORMAT_R8G8_SSCALED}; const auto count = std::min(components, 2u); return {formats[count - 1], count * 1u, 1u, "f32"}; }
|
||||
case 18: { const std::array formats{VK_FORMAT_R8_UINT, VK_FORMAT_R8G8_UINT}; const auto count = std::min(components, 2u); return {formats[count - 1], count * 1u, 1u, "u32"}; }
|
||||
case 19: { const std::array formats{VK_FORMAT_R8_SINT, VK_FORMAT_R8G8_SINT}; const auto count = std::min(components, 2u); return {formats[count - 1], count * 1u, 1u, "i32"}; }
|
||||
case 20: { const std::array formats{VK_FORMAT_R32_UINT}; const auto count = std::min(components, 1u); return {formats[count - 1], count * 4u, 4u, "u32"}; }
|
||||
case 21: { const std::array formats{VK_FORMAT_R32_SINT}; const auto count = std::min(components, 1u); return {formats[count - 1], count * 4u, 4u, "i32"}; }
|
||||
case 22: { const std::array formats{VK_FORMAT_R32_SFLOAT}; const auto count = std::min(components, 1u); return {formats[count - 1], count * 4u, 4u, "f32"}; }
|
||||
case 23: { const std::array formats{VK_FORMAT_R16_UNORM, VK_FORMAT_R16G16_UNORM}; const auto count = std::min(components, 2u); return {formats[count - 1], count * 2u, 2u, "f32"}; }
|
||||
case 24: { const std::array formats{VK_FORMAT_R16_SNORM, VK_FORMAT_R16G16_SNORM}; const auto count = std::min(components, 2u); return {formats[count - 1], count * 2u, 2u, "f32"}; }
|
||||
case 25: { const std::array formats{VK_FORMAT_R16_USCALED, VK_FORMAT_R16G16_USCALED}; const auto count = std::min(components, 2u); return {formats[count - 1], count * 2u, 2u, "f32"}; }
|
||||
case 26: { const std::array formats{VK_FORMAT_R16_SSCALED, VK_FORMAT_R16G16_SSCALED}; const auto count = std::min(components, 2u); return {formats[count - 1], count * 2u, 2u, "f32"}; }
|
||||
case 27: { const std::array formats{VK_FORMAT_R16_UINT, VK_FORMAT_R16G16_UINT}; const auto count = std::min(components, 2u); return {formats[count - 1], count * 2u, 2u, "u32"}; }
|
||||
case 28: { const std::array formats{VK_FORMAT_R16_SINT, VK_FORMAT_R16G16_SINT}; const auto count = std::min(components, 2u); return {formats[count - 1], count * 2u, 2u, "i32"}; }
|
||||
case 29: { const std::array formats{VK_FORMAT_R16_SFLOAT, VK_FORMAT_R16G16_SFLOAT}; const auto count = std::min(components, 2u); return {formats[count - 1], count * 2u, 2u, "f32"}; }
|
||||
case 36: return {VK_FORMAT_B10G11R11_UFLOAT_PACK32, 4u, 4u, "f32"};
|
||||
case 50: return {VK_FORMAT_A2B10G10R10_UNORM_PACK32, 4u, 4u, "f32"};
|
||||
case 51: return {VK_FORMAT_A2B10G10R10_SNORM_PACK32, 4u, 4u, "f32"};
|
||||
|
|
@ -58,28 +60,28 @@ inline VertexFormat DecodeVertexFormat(const ShaderRecompiler::VertexAttribute&
|
|||
case 53: return {VK_FORMAT_A2B10G10R10_SSCALED_PACK32, 4u, 4u, "f32"};
|
||||
case 54: return {VK_FORMAT_A2B10G10R10_UINT_PACK32, 4u, 4u, "u32"};
|
||||
case 55: return {VK_FORMAT_A2B10G10R10_SINT_PACK32, 4u, 4u, "i32"};
|
||||
case 56: { const std::array formats{VK_FORMAT_R8_UNORM, VK_FORMAT_R8G8_UNORM, VK_FORMAT_R8G8B8_UNORM, VK_FORMAT_R8G8B8A8_UNORM}; const auto count = std::min(attribute.components, 4u); return {formats[count - 1], count * 1u, 1u, "f32"}; }
|
||||
case 57: { const std::array formats{VK_FORMAT_R8_SNORM, VK_FORMAT_R8G8_SNORM, VK_FORMAT_R8G8B8_SNORM, VK_FORMAT_R8G8B8A8_SNORM}; const auto count = std::min(attribute.components, 4u); return {formats[count - 1], count * 1u, 1u, "f32"}; }
|
||||
case 58: { const std::array formats{VK_FORMAT_R8_USCALED, VK_FORMAT_R8G8_USCALED, VK_FORMAT_R8G8B8_USCALED, VK_FORMAT_R8G8B8A8_USCALED}; const auto count = std::min(attribute.components, 4u); return {formats[count - 1], count * 1u, 1u, "f32"}; }
|
||||
case 59: { const std::array formats{VK_FORMAT_R8_SSCALED, VK_FORMAT_R8G8_SSCALED, VK_FORMAT_R8G8B8_SSCALED, VK_FORMAT_R8G8B8A8_SSCALED}; const auto count = std::min(attribute.components, 4u); return {formats[count - 1], count * 1u, 1u, "f32"}; }
|
||||
case 60: { const std::array formats{VK_FORMAT_R8_UINT, VK_FORMAT_R8G8_UINT, VK_FORMAT_R8G8B8_UINT, VK_FORMAT_R8G8B8A8_UINT}; const auto count = std::min(attribute.components, 4u); return {formats[count - 1], count * 1u, 1u, "u32"}; }
|
||||
case 61: { const std::array formats{VK_FORMAT_R8_SINT, VK_FORMAT_R8G8_SINT, VK_FORMAT_R8G8B8_SINT, VK_FORMAT_R8G8B8A8_SINT}; const auto count = std::min(attribute.components, 4u); return {formats[count - 1], count * 1u, 1u, "i32"}; }
|
||||
case 62: { const std::array formats{VK_FORMAT_R32_UINT, VK_FORMAT_R32G32_UINT}; const auto count = std::min(attribute.components, 2u); return {formats[count - 1], count * 4u, 4u, "u32"}; }
|
||||
case 63: { const std::array formats{VK_FORMAT_R32_SINT, VK_FORMAT_R32G32_SINT}; const auto count = std::min(attribute.components, 2u); return {formats[count - 1], count * 4u, 4u, "i32"}; }
|
||||
case 64: { const std::array formats{VK_FORMAT_R32_SFLOAT, VK_FORMAT_R32G32_SFLOAT}; const auto count = std::min(attribute.components, 2u); return {formats[count - 1], count * 4u, 4u, "f32"}; }
|
||||
case 65: { const std::array formats{VK_FORMAT_R16_UNORM, VK_FORMAT_R16G16_UNORM, VK_FORMAT_R16G16B16_UNORM, VK_FORMAT_R16G16B16A16_UNORM}; const auto count = std::min(attribute.components, 4u); return {formats[count - 1], count * 2u, 2u, "f32"}; }
|
||||
case 66: { const std::array formats{VK_FORMAT_R16_SNORM, VK_FORMAT_R16G16_SNORM, VK_FORMAT_R16G16B16_SNORM, VK_FORMAT_R16G16B16A16_SNORM}; const auto count = std::min(attribute.components, 4u); return {formats[count - 1], count * 2u, 2u, "f32"}; }
|
||||
case 67: { const std::array formats{VK_FORMAT_R16_USCALED, VK_FORMAT_R16G16_USCALED, VK_FORMAT_R16G16B16_USCALED, VK_FORMAT_R16G16B16A16_USCALED}; const auto count = std::min(attribute.components, 4u); return {formats[count - 1], count * 2u, 2u, "f32"}; }
|
||||
case 68: { const std::array formats{VK_FORMAT_R16_SSCALED, VK_FORMAT_R16G16_SSCALED, VK_FORMAT_R16G16B16_SSCALED, VK_FORMAT_R16G16B16A16_SSCALED}; const auto count = std::min(attribute.components, 4u); return {formats[count - 1], count * 2u, 2u, "f32"}; }
|
||||
case 69: { const std::array formats{VK_FORMAT_R16_UINT, VK_FORMAT_R16G16_UINT, VK_FORMAT_R16G16B16_UINT, VK_FORMAT_R16G16B16A16_UINT}; const auto count = std::min(attribute.components, 4u); return {formats[count - 1], count * 2u, 2u, "u32"}; }
|
||||
case 70: { const std::array formats{VK_FORMAT_R16_SINT, VK_FORMAT_R16G16_SINT, VK_FORMAT_R16G16B16_SINT, VK_FORMAT_R16G16B16A16_SINT}; const auto count = std::min(attribute.components, 4u); return {formats[count - 1], count * 2u, 2u, "i32"}; }
|
||||
case 71: { const std::array formats{VK_FORMAT_R16_SFLOAT, VK_FORMAT_R16G16_SFLOAT, VK_FORMAT_R16G16B16_SFLOAT, VK_FORMAT_R16G16B16A16_SFLOAT}; const auto count = std::min(attribute.components, 4u); return {formats[count - 1], count * 2u, 2u, "f32"}; }
|
||||
case 72: { const std::array formats{VK_FORMAT_R32_UINT, VK_FORMAT_R32G32_UINT, VK_FORMAT_R32G32B32_UINT}; const auto count = std::min(attribute.components, 3u); return {formats[count - 1], count * 4u, 4u, "u32"}; }
|
||||
case 73: { const std::array formats{VK_FORMAT_R32_SINT, VK_FORMAT_R32G32_SINT, VK_FORMAT_R32G32B32_SINT}; const auto count = std::min(attribute.components, 3u); return {formats[count - 1], count * 4u, 4u, "i32"}; }
|
||||
case 74: { const std::array formats{VK_FORMAT_R32_SFLOAT, VK_FORMAT_R32G32_SFLOAT, VK_FORMAT_R32G32B32_SFLOAT}; const auto count = std::min(attribute.components, 3u); return {formats[count - 1], count * 4u, 4u, "f32"}; }
|
||||
case 75: { const std::array formats{VK_FORMAT_R32_UINT, VK_FORMAT_R32G32_UINT, VK_FORMAT_R32G32B32_UINT, VK_FORMAT_R32G32B32A32_UINT}; const auto count = std::min(attribute.components, 4u); return {formats[count - 1], count * 4u, 4u, "u32"}; }
|
||||
case 76: { const std::array formats{VK_FORMAT_R32_SINT, VK_FORMAT_R32G32_SINT, VK_FORMAT_R32G32B32_SINT, VK_FORMAT_R32G32B32A32_SINT}; const auto count = std::min(attribute.components, 4u); return {formats[count - 1], count * 4u, 4u, "i32"}; }
|
||||
case 77: { const std::array formats{VK_FORMAT_R32_SFLOAT, VK_FORMAT_R32G32_SFLOAT, VK_FORMAT_R32G32B32_SFLOAT, VK_FORMAT_R32G32B32A32_SFLOAT}; const auto count = std::min(attribute.components, 4u); return {formats[count - 1], count * 4u, 4u, "f32"}; }
|
||||
case 56: { const std::array formats{VK_FORMAT_R8_UNORM, VK_FORMAT_R8G8_UNORM, VK_FORMAT_R8G8B8_UNORM, VK_FORMAT_R8G8B8A8_UNORM}; const auto count = std::min(components, 4u); return {formats[count - 1], count * 1u, 1u, "f32"}; }
|
||||
case 57: { const std::array formats{VK_FORMAT_R8_SNORM, VK_FORMAT_R8G8_SNORM, VK_FORMAT_R8G8B8_SNORM, VK_FORMAT_R8G8B8A8_SNORM}; const auto count = std::min(components, 4u); return {formats[count - 1], count * 1u, 1u, "f32"}; }
|
||||
case 58: { const std::array formats{VK_FORMAT_R8_USCALED, VK_FORMAT_R8G8_USCALED, VK_FORMAT_R8G8B8_USCALED, VK_FORMAT_R8G8B8A8_USCALED}; const auto count = std::min(components, 4u); return {formats[count - 1], count * 1u, 1u, "f32"}; }
|
||||
case 59: { const std::array formats{VK_FORMAT_R8_SSCALED, VK_FORMAT_R8G8_SSCALED, VK_FORMAT_R8G8B8_SSCALED, VK_FORMAT_R8G8B8A8_SSCALED}; const auto count = std::min(components, 4u); return {formats[count - 1], count * 1u, 1u, "f32"}; }
|
||||
case 60: { const std::array formats{VK_FORMAT_R8_UINT, VK_FORMAT_R8G8_UINT, VK_FORMAT_R8G8B8_UINT, VK_FORMAT_R8G8B8A8_UINT}; const auto count = std::min(components, 4u); return {formats[count - 1], count * 1u, 1u, "u32"}; }
|
||||
case 61: { const std::array formats{VK_FORMAT_R8_SINT, VK_FORMAT_R8G8_SINT, VK_FORMAT_R8G8B8_SINT, VK_FORMAT_R8G8B8A8_SINT}; const auto count = std::min(components, 4u); return {formats[count - 1], count * 1u, 1u, "i32"}; }
|
||||
case 62: { const std::array formats{VK_FORMAT_R32_UINT, VK_FORMAT_R32G32_UINT}; const auto count = std::min(components, 2u); return {formats[count - 1], count * 4u, 4u, "u32"}; }
|
||||
case 63: { const std::array formats{VK_FORMAT_R32_SINT, VK_FORMAT_R32G32_SINT}; const auto count = std::min(components, 2u); return {formats[count - 1], count * 4u, 4u, "i32"}; }
|
||||
case 64: { const std::array formats{VK_FORMAT_R32_SFLOAT, VK_FORMAT_R32G32_SFLOAT}; const auto count = std::min(components, 2u); return {formats[count - 1], count * 4u, 4u, "f32"}; }
|
||||
case 65: { const std::array formats{VK_FORMAT_R16_UNORM, VK_FORMAT_R16G16_UNORM, VK_FORMAT_R16G16B16_UNORM, VK_FORMAT_R16G16B16A16_UNORM}; const auto count = std::min(components, 4u); return {formats[count - 1], count * 2u, 2u, "f32"}; }
|
||||
case 66: { const std::array formats{VK_FORMAT_R16_SNORM, VK_FORMAT_R16G16_SNORM, VK_FORMAT_R16G16B16_SNORM, VK_FORMAT_R16G16B16A16_SNORM}; const auto count = std::min(components, 4u); return {formats[count - 1], count * 2u, 2u, "f32"}; }
|
||||
case 67: { const std::array formats{VK_FORMAT_R16_USCALED, VK_FORMAT_R16G16_USCALED, VK_FORMAT_R16G16B16_USCALED, VK_FORMAT_R16G16B16A16_USCALED}; const auto count = std::min(components, 4u); return {formats[count - 1], count * 2u, 2u, "f32"}; }
|
||||
case 68: { const std::array formats{VK_FORMAT_R16_SSCALED, VK_FORMAT_R16G16_SSCALED, VK_FORMAT_R16G16B16_SSCALED, VK_FORMAT_R16G16B16A16_SSCALED}; const auto count = std::min(components, 4u); return {formats[count - 1], count * 2u, 2u, "f32"}; }
|
||||
case 69: { const std::array formats{VK_FORMAT_R16_UINT, VK_FORMAT_R16G16_UINT, VK_FORMAT_R16G16B16_UINT, VK_FORMAT_R16G16B16A16_UINT}; const auto count = std::min(components, 4u); return {formats[count - 1], count * 2u, 2u, "u32"}; }
|
||||
case 70: { const std::array formats{VK_FORMAT_R16_SINT, VK_FORMAT_R16G16_SINT, VK_FORMAT_R16G16B16_SINT, VK_FORMAT_R16G16B16A16_SINT}; const auto count = std::min(components, 4u); return {formats[count - 1], count * 2u, 2u, "i32"}; }
|
||||
case 71: { const std::array formats{VK_FORMAT_R16_SFLOAT, VK_FORMAT_R16G16_SFLOAT, VK_FORMAT_R16G16B16_SFLOAT, VK_FORMAT_R16G16B16A16_SFLOAT}; const auto count = std::min(components, 4u); return {formats[count - 1], count * 2u, 2u, "f32"}; }
|
||||
case 72: { const std::array formats{VK_FORMAT_R32_UINT, VK_FORMAT_R32G32_UINT, VK_FORMAT_R32G32B32_UINT}; const auto count = std::min(components, 3u); return {formats[count - 1], count * 4u, 4u, "u32"}; }
|
||||
case 73: { const std::array formats{VK_FORMAT_R32_SINT, VK_FORMAT_R32G32_SINT, VK_FORMAT_R32G32B32_SINT}; const auto count = std::min(components, 3u); return {formats[count - 1], count * 4u, 4u, "i32"}; }
|
||||
case 74: { const std::array formats{VK_FORMAT_R32_SFLOAT, VK_FORMAT_R32G32_SFLOAT, VK_FORMAT_R32G32B32_SFLOAT}; const auto count = std::min(components, 3u); return {formats[count - 1], count * 4u, 4u, "f32"}; }
|
||||
case 75: { const std::array formats{VK_FORMAT_R32_UINT, VK_FORMAT_R32G32_UINT, VK_FORMAT_R32G32B32_UINT, VK_FORMAT_R32G32B32A32_UINT}; const auto count = std::min(components, 4u); return {formats[count - 1], count * 4u, 4u, "u32"}; }
|
||||
case 76: { const std::array formats{VK_FORMAT_R32_SINT, VK_FORMAT_R32G32_SINT, VK_FORMAT_R32G32B32_SINT, VK_FORMAT_R32G32B32A32_SINT}; const auto count = std::min(components, 4u); return {formats[count - 1], count * 4u, 4u, "i32"}; }
|
||||
case 77: { const std::array formats{VK_FORMAT_R32_SFLOAT, VK_FORMAT_R32G32_SFLOAT, VK_FORMAT_R32G32B32_SFLOAT, VK_FORMAT_R32G32B32A32_SFLOAT}; const auto count = std::min(components, 4u); return {formats[count - 1], count * 4u, 4u, "f32"}; }
|
||||
default: throw std::runtime_error("AGC graphics: unsupported vertex format " + std::to_string(format));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -479,6 +479,7 @@ bool ValidationKey(const Context& context, std::span<const CompiledShader> shade
|
|||
add(attribute.location);
|
||||
add(attribute.components);
|
||||
add(attribute.fetchIndex);
|
||||
add(attribute.formatComponents);
|
||||
// The data format bits of the V# decide the attribute's signature.
|
||||
add((attribute.resource.fields[3] >> 12u) & 0x7fu);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -198,7 +198,13 @@ ShaderRecompiler::ShaderVertexStageInfo DecodeVertexStageInfo(std::span<const st
|
|||
for (std::uint32_t i = 0; i < shader.num_input_semantics; ++i) {
|
||||
const auto& semantic = semantics[i];
|
||||
if (semantic.static_vb_index == 1 || semantic.static_attribute == 1) throw std::runtime_error("AGC graphics: statically bound vertex attributes are not implemented");
|
||||
if (staticAbi) continue;
|
||||
if (staticAbi) {
|
||||
auto& destination = info.resourcesDst.at(info.resourcesNum++);
|
||||
destination.registerStart = static_cast<std::int32_t>(semantic.hardware_mapping);
|
||||
destination.registersNum = static_cast<std::int32_t>(semantic.size_in_elements);
|
||||
destination.attrId = static_cast<std::int32_t>(semantic.semantic);
|
||||
continue;
|
||||
}
|
||||
std::array<std::byte, 4> attribWordBytes{};
|
||||
const auto attribWordAddress = attribTableAddr + static_cast<std::uint64_t>(semantic.semantic) * 4u;
|
||||
AgcDriver::GuestMemory::Read(attribWordAddress, attribWordBytes, 4);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#include "ShaderDiskCache.hpp"
|
||||
#include "CacheKey.hpp"
|
||||
#include "VertexInputSpecialization.hpp"
|
||||
#include "Optimization/ResourceMaterializer.hpp"
|
||||
#include "ShaderCacheDirectory.hpp"
|
||||
#include <algorithm>
|
||||
|
|
@ -12,6 +13,7 @@
|
|||
#include <functional>
|
||||
#include <future>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <random>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
|
@ -54,6 +56,7 @@ void requireSameArtifact(const CompiledShaderArtifact& left, const CompiledShade
|
|||
require(left.memoryOffsetDword == right.memoryOffsetDword, prefix + "memory offset differs");
|
||||
require(left.hostSubgroupSize == right.hostSubgroupSize, prefix + "host subgroup size differs");
|
||||
require(left.vertexInputs == right.vertexInputs, prefix + "vertex inputs differ");
|
||||
require(left.vertexInputPatches == right.vertexInputPatches, prefix + "vertex type patches differ");
|
||||
require(left.vertexOffsetSgpr == right.vertexOffsetSgpr && left.instanceOffsetSgpr == right.instanceOffsetSgpr, prefix + "offset SGPRs differ");
|
||||
require(left.vertexOffsetShared == right.vertexOffsetShared && left.instanceOffsetShared == right.instanceOffsetShared && left.vertexOffsetConflict == right.vertexOffsetConflict && left.instanceOffsetConflict == right.instanceOffsetConflict, prefix + "offset flags differ");
|
||||
require(left.parameterExports == right.parameterExports, prefix + "parameter exports differ");
|
||||
|
|
@ -70,11 +73,12 @@ void requireSameResult(const RecompileResult& left, const RecompileResult& right
|
|||
const std::string prefix = std::string(what) + ": ";
|
||||
require(sameBindings(left.bindings, right.bindings), prefix + "bindings differ");
|
||||
require(left.pushConstants == right.pushConstants, prefix + "push constants differ");
|
||||
require(left.specialization == right.specialization, prefix + "specialization constants differ");
|
||||
require(left.vertexAttributes.size() == right.vertexAttributes.size(), prefix + "vertex attribute count differs");
|
||||
for (std::size_t i = 0; i < left.vertexAttributes.size(); ++i) {
|
||||
const auto& a = left.vertexAttributes[i];
|
||||
const auto& b = right.vertexAttributes[i];
|
||||
require(a.location == b.location && a.components == b.components && a.resource.fields == b.resource.fields && a.fetchIndex == b.fetchIndex, prefix + "vertex attribute differs");
|
||||
require(a.location == b.location && a.components == b.components && a.resource.fields == b.resource.fields && a.fetchIndex == b.fetchIndex && a.formatComponents == b.formatComponents, prefix + "vertex attribute differs");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -127,7 +131,11 @@ RecompileResult sampleResult() {
|
|||
result.memoryOffsetDword = 7;
|
||||
result.hostSubgroupSize = 32;
|
||||
result.vertexInputs = {{1, 4, 2}, {5, 2, 0}};
|
||||
result.vertexInputPatches = {{1, 20, {7, 8, 9}}, {5, 40, {10, 11, 12}}};
|
||||
result.specialization = {{512, 4}, {516, 0x3f800000u}, {517, 0}};
|
||||
result.vertexAttributes = {{1, 4, {{0x1000u, 0x20000u, 0x30u, 0x4u}}, 2}, {5, 2, {{9u, 8u, 7u, 6u}}, 0}};
|
||||
result.vertexInputs[0].outputMask = 5u;
|
||||
result.vertexAttributes[0].formatComponents = 3u;
|
||||
result.vertexOffsetSgpr = 12;
|
||||
result.instanceOffsetSgpr = -1;
|
||||
result.vertexOffsetShared = true;
|
||||
|
|
@ -162,6 +170,8 @@ CompiledVariant sampleVariant() {
|
|||
buffer.read = true;
|
||||
buffer.atomic = true;
|
||||
buffer.scalar = true;
|
||||
buffer.descriptorFormatted = true;
|
||||
buffer.formattedReadMask = 5u;
|
||||
info.info.buffers = {buffer, BufferResource{}};
|
||||
ImageResource image{};
|
||||
image.source = 5;
|
||||
|
|
@ -409,8 +419,12 @@ void verifyKeySensitivity() {
|
|||
input.fetchEmbedded = true;
|
||||
const auto embeddedKey = vertex.Key();
|
||||
input.resources[0].fields = {};
|
||||
input.resourcesNum = 0u;
|
||||
input.resourcesDst[0].fetchIndex = 1u;
|
||||
require(vertex.Key() == embeddedKey, "runtime vertex fetch descriptors changed the artifact key");
|
||||
input.resources[0].fields[3] = (20u << 12u) | 0xfacu;
|
||||
require(vertex.Key() == embeddedKey, "embedded numeric class changed the prepared template key");
|
||||
input.resourcesNum = 0u;
|
||||
require(vertex.Key() != embeddedKey, "embedded semantic interface did not change the artifact key");
|
||||
SampleRequest fragment;
|
||||
fragment.request.shader.stage = ShaderStage::Fragment;
|
||||
fragment.request.context.compute.reset();
|
||||
|
|
@ -634,6 +648,73 @@ void verifyInvocationIsolation() {
|
|||
require(rejected, "an unsupported buffer descriptor type was accepted");
|
||||
}
|
||||
|
||||
void verifyVertexTypeSpecialization() {
|
||||
CompiledShaderArtifact artifact;
|
||||
artifact.vertexInputs = {{0, 4, 0, 1}, {1, 4, 0, 1}};
|
||||
artifact.spirv = std::vector<std::uint32_t>{
|
||||
spv::MagicNumber, 0x00010000u, 0, 28, 0,
|
||||
(2u << 16u) | spv::OpCapability, spv::CapabilityShader,
|
||||
(3u << 16u) | spv::OpMemoryModel, spv::AddressingModelLogical, spv::MemoryModelGLSL450,
|
||||
(7u << 16u) | spv::OpEntryPoint, spv::ExecutionModelVertex, 20, 0x6e69616du, 0, 10, 11,
|
||||
(4u << 16u) | spv::OpDecorate, 10, spv::DecorationLocation, 0,
|
||||
(4u << 16u) | spv::OpDecorate, 11, spv::DecorationLocation, 1,
|
||||
(2u << 16u) | spv::OpTypeVoid, 1,
|
||||
(3u << 16u) | spv::OpTypeFloat, 2, 32,
|
||||
(4u << 16u) | spv::OpTypeInt, 3, 32, 0,
|
||||
(4u << 16u) | spv::OpTypeVector, 4, 2, 4,
|
||||
(4u << 16u) | spv::OpTypePointer, 5, spv::StorageClassInput, 4,
|
||||
(4u << 16u) | spv::OpTypePointer, 6, spv::StorageClassInput, 2,
|
||||
(3u << 16u) | spv::OpTypeFunction, 7, 1,
|
||||
(4u << 16u) | spv::OpConstant, 3, 8, 0,
|
||||
(4u << 16u) | spv::OpVariable, 5, 10, spv::StorageClassInput,
|
||||
(4u << 16u) | spv::OpVariable, 5, 11, spv::StorageClassInput,
|
||||
(5u << 16u) | spv::OpFunction, 1, 20, spv::FunctionControlMaskNone, 7,
|
||||
(2u << 16u) | spv::OpLabel, 21,
|
||||
(5u << 16u) | spv::OpAccessChain, 6, 22, 10, 8,
|
||||
(4u << 16u) | spv::OpLoad, 2, 23, 22,
|
||||
(4u << 16u) | spv::OpBitcast, 3, 24, 23,
|
||||
(5u << 16u) | spv::OpAccessChain, 6, 25, 11, 8,
|
||||
(4u << 16u) | spv::OpLoad, 2, 26, 25,
|
||||
(4u << 16u) | spv::OpBitcast, 3, 27, 26,
|
||||
(1u << 16u) | spv::OpReturn,
|
||||
(1u << 16u) | spv::OpFunctionEnd
|
||||
};
|
||||
PrepareVertexInputSpecialization(artifact);
|
||||
const auto original = artifact.spirv.Words();
|
||||
for (std::uint32_t first = 0; first < 3u; ++first) {
|
||||
for (std::uint32_t second = 0; second < 3u; ++second) {
|
||||
const std::array classes{first, second};
|
||||
const auto specialized = SpecializeVertexInputTypes(artifact, classes);
|
||||
const auto& words = specialized.Words();
|
||||
std::map<std::uint32_t, std::uint32_t> scalarKinds;
|
||||
std::uint32_t checked = 0;
|
||||
for (std::size_t cursor = 5; cursor < words.size(); cursor += words[cursor] >> 16u) {
|
||||
const auto op = static_cast<spv::Op>(words[cursor] & 0xffffu);
|
||||
if (op == spv::OpTypeFloat) scalarKinds.emplace(words[cursor + 1u], 0u);
|
||||
if (op == spv::OpTypeInt) scalarKinds.emplace(words[cursor + 1u], words[cursor + 3u] != 0u ? 1u : 2u);
|
||||
if (op == spv::OpLoad) {
|
||||
const auto attribute = words[cursor + 2u] == 23u ? 0u : 1u;
|
||||
require(scalarKinds.at(words[cursor + 1u]) == classes[attribute], "vertex input loaded the wrong numeric type");
|
||||
++checked;
|
||||
}
|
||||
if (op == spv::OpBitcast || op == spv::OpCopyObject) {
|
||||
const auto attribute = words[cursor + 2u] == 24u ? 0u : 1u;
|
||||
require(op == (classes[attribute] == 2u ? spv::OpCopyObject : spv::OpBitcast), "vertex input did not preserve raw component bits");
|
||||
++checked;
|
||||
}
|
||||
}
|
||||
require(checked == 4u, "vertex specialization lost attribute loads");
|
||||
require(artifact.spirv.Words() == original, "vertex specialization mutated the shared template");
|
||||
}
|
||||
}
|
||||
auto invalid = artifact;
|
||||
invalid.vertexInputPatches.front().word = static_cast<std::uint32_t>(original.size());
|
||||
bool rejected = false;
|
||||
try { static_cast<void>(SpecializeVertexInputTypes(invalid, std::array{0u, 0u})); }
|
||||
catch (const std::runtime_error&) { rejected = true; }
|
||||
require(rejected, "out-of-range vertex type patch was accepted");
|
||||
}
|
||||
|
||||
void verifyDefaultDirectory(const char* self) {
|
||||
setEnvironment("ANYPS5_SHADER_CACHE_DIR", "");
|
||||
setEnvironment("ANYPS5_NO_SHADER_CACHE", "1");
|
||||
|
|
@ -665,6 +746,7 @@ int main(int argc, char** argv) {
|
|||
verifyEmissionFailureMemo();
|
||||
verifyFailureMemoSwitch(argv[0]);
|
||||
verifyInvocationIsolation();
|
||||
verifyVertexTypeSpecialization();
|
||||
std::error_code error;
|
||||
std::filesystem::remove_all(directory, error);
|
||||
std::cout << "shader disk cache tests passed\n";
|
||||
|
|
|
|||
|
|
@ -182,11 +182,12 @@ private:
|
|||
append(key, value.fetchBufferReg);
|
||||
append(key, value.fetchEmbedded);
|
||||
if (value.resourcesNum > value.resources.size()) throw std::runtime_error("Shader cache: invalid vertex resource count");
|
||||
if (value.fetchEmbedded) return;
|
||||
append(key, value.resourcesNum);
|
||||
for (std::uint32_t i = 0; i < value.resourcesNum; ++i) {
|
||||
append(key, VertexInputNumericClass(static_cast<IrBufferFormat>((value.resources[i].fields[3] >> 12u) & 0x7fu)));
|
||||
append(key, value.resourcesDst[i]);
|
||||
if (!value.fetchEmbedded) append(key, VertexInputNumericClass(static_cast<IrBufferFormat>((value.resources[i].fields[3] >> 12u) & 0x7fu)));
|
||||
auto destination = value.resourcesDst[i];
|
||||
if (value.fetchEmbedded) destination.fetchIndex = 0;
|
||||
append(key, destination);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@ struct BufferResource {
|
|||
bool written = false;
|
||||
bool atomic = false;
|
||||
bool formatted = false;
|
||||
bool descriptorFormatted = false;
|
||||
std::uint32_t formattedReadMask = 0;
|
||||
bool scalar = false;
|
||||
|
||||
bool operator==(const BufferResource& other) const = default;
|
||||
|
|
|
|||
|
|
@ -217,12 +217,17 @@ void DescriptorBindingBuilder::Populate(BindingAllocationResult& allocation, con
|
|||
for (std::uint32_t index = 0; index < info.buffers.size(); ++index) {
|
||||
const auto& descriptor = snapshot.buffers.at(index);
|
||||
if (descriptor.dwordCount != 4u) fail("buffer specialization requires four descriptor words");
|
||||
const auto& buffer = info.buffers[index];
|
||||
const auto format = (descriptor.dwords[3] >> 12u) & 0x7fu;
|
||||
const bool present = descriptor.dwords[0] != 0u || (descriptor.dwords[1] & 0xffffu) != 0u;
|
||||
if (buffer.descriptorFormatted && (format > static_cast<std::uint32_t>(IrBufferFormat::Format32_32_32_32Float) || (present && format == 0u))) fail("buffer specialization has an unsupported format");
|
||||
const auto first = PipelineSpecialization::BufferBase + index * PipelineSpecialization::BufferWords;
|
||||
allocation.specialization.push_back({first, descriptor.dwords[1] & 0xffff0000u});
|
||||
allocation.specialization.push_back({first + 1u, descriptor.dwords[3]});
|
||||
allocation.specialization.push_back({first + 2u, descriptor.dwords[0] != 0u || (descriptor.dwords[1] & 0xffffu) != 0u ? 1u : 0u});
|
||||
if (info.buffers[index].formatted) {
|
||||
allocation.specialization.push_back({first + 2u, present ? 1u : 0u});
|
||||
if (buffer.formattedReadMask != 0u) {
|
||||
for (std::uint32_t component = 0; component < 4u; ++component) {
|
||||
if ((buffer.formattedReadMask & (1u << component)) == 0u) continue;
|
||||
const auto selector = (descriptor.dwords[3] >> (component * 3u)) & 7u;
|
||||
if (selector == 2u || selector == 3u) fail("buffer specialization has a reserved component selector");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -806,6 +806,7 @@ private:
|
|||
if (resource == std::numeric_limits<std::uint32_t>::max()) fail("buffer resource limit exceeded");
|
||||
AddMemoryPatch(memoryIndex, resource, 0u, false);
|
||||
memory.gpuDescriptor = false;
|
||||
m_info.usesDma = m_info.usesDma || access == BufferAccess::Atomic;
|
||||
return true;
|
||||
}
|
||||
memory.gpuDescriptor = true;
|
||||
|
|
@ -849,6 +850,8 @@ private:
|
|||
resource.written = resource.written || write;
|
||||
resource.atomic = resource.atomic || atomic;
|
||||
resource.formatted = resource.formatted || memory.formatted;
|
||||
resource.descriptorFormatted = resource.descriptorFormatted || (memory.formatted && !memory.typed);
|
||||
if (memory.formatted && !memory.typed && !write) resource.formattedReadMask |= (1u << std::min(memory.dataDwords, 4u)) - 1u;
|
||||
resource.scalar = resource.scalar || op == IrOpcode::ReadConstBuffer || memory.kind == ResourceKind::ScalarBuffer;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -189,7 +189,7 @@ void CollectVertexInputs(const IrProgram& program, const ShaderVertexInputInfo*
|
|||
if (inst->Opcode() == IrOpcode::GetAttribute) {
|
||||
const auto attribute = inst->Argument(0)->Resolve()->ImmediateU32();
|
||||
const auto channel = inst->Argument(1)->Resolve()->ImmediateU32();
|
||||
usedComponents[attribute] = std::max(usedComponents[attribute], channel + 1u);
|
||||
usedComponents[attribute] = std::max(usedComponents[attribute], inst->Flags<std::uint32_t>() == 1u ? 4u : channel + 1u);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ inline constexpr std::uint32_t BufferWords = 3u;
|
|||
inline constexpr std::uint32_t ImageBase = 128u;
|
||||
inline constexpr std::uint32_t ImageWords = 5u;
|
||||
inline constexpr std::uint32_t VertexBase = 512u;
|
||||
inline constexpr std::uint32_t VertexWords = 6u;
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@
|
|||
#include <cstdio>
|
||||
#include "CacheKey.hpp"
|
||||
#include "CompiledVariant.hpp"
|
||||
#include "VertexInputSpecialization.hpp"
|
||||
#include "RdnaDecoder/RdnaDescriptorFormat.hpp"
|
||||
#include "ShaderDiskCache.hpp"
|
||||
#include <list>
|
||||
#include <map>
|
||||
|
|
@ -118,6 +120,11 @@ IrProgram PrepareResourceProgram(const RecompileRequest& request) {
|
|||
|
||||
constexpr InstructionTranslator translator;
|
||||
|
||||
EmbeddedFetchPlan embeddedFetch;
|
||||
if ((stageKind == ShaderStageKind::Vertex || stageKind == ShaderStageKind::Local) && inputInfo.vertex != nullptr && inputInfo.vertex->fetchEmbedded) {
|
||||
embeddedFetch = EmbeddedVertexFetchAnalyzer{}.Analyze(decoded, inputInfo.vertex->fetchAttribReg, inputInfo.vertex->fetchBufferReg, request.context.userDataBaseRegister, static_cast<std::uint32_t>(request.context.userData.size()), request.context.waveSize);
|
||||
translateOptions.embeddedFetch = &embeddedFetch;
|
||||
}
|
||||
auto program = translator.Translate(decoded, cfg, translateOptions);
|
||||
// Debug aid: APS5_DUMP_IR=<hex code address> (or "all") prints the program after each front-end pass.
|
||||
const auto dumpIr = [&](const char* pass) {
|
||||
|
|
@ -299,7 +306,7 @@ std::uint64_t nextVariantId() {
|
|||
return variants.fetch_add(1, std::memory_order_relaxed) + 1;
|
||||
}
|
||||
|
||||
CompiledVariant compileVariant(const RecompileRequest& request, IrProgram program, std::exception_ptr* emissionFailure = nullptr) {
|
||||
CompiledVariant compileVariant(const RecompileRequest& request, IrProgram program, std::exception_ptr* emissionFailure = nullptr) try {
|
||||
const auto inputInfo = RequestInputInfo(request);
|
||||
constexpr DeadCodeEliminator deadCodeEliminator;
|
||||
constexpr ResourceMaterializer resourceMaterializer;
|
||||
|
|
@ -327,17 +334,7 @@ CompiledVariant compileVariant(const RecompileRequest& request, IrProgram progra
|
|||
constexpr SpirvEmitter spirvEmitter;
|
||||
CompiledShaderArtifact result;
|
||||
result.variantId = nextVariantId();
|
||||
try {
|
||||
result.spirv = spirvEmitter.Emit(program, inputInfo, bindings, targetOptions);
|
||||
#if ANYPS5_ENABLE_SPIRV_TOOLS
|
||||
result.spirv = ValidateAndOptimizeSpirv(result.spirv, request.target.vulkanVersion, request.target.spirvVersion, request.target.nonConstantImageOffsets);
|
||||
#endif
|
||||
} catch (const std::bad_alloc&) {
|
||||
throw;
|
||||
} catch (...) {
|
||||
if (emissionFailure != nullptr) *emissionFailure = std::current_exception();
|
||||
throw;
|
||||
}
|
||||
result.spirv = spirvEmitter.Emit(program, inputInfo, bindings, targetOptions);
|
||||
|
||||
result.bdaAbiVersion = program.Info().usesDma ? request.target.bdaAbiVersion : 0u;
|
||||
result.memoryOffsetDword = bindings.layout.memoryOffsetDword;
|
||||
|
|
@ -358,10 +355,36 @@ CompiledVariant compileVariant(const RecompileRequest& request, IrProgram progra
|
|||
if (input.kind != StageInputKind::Parameter) continue;
|
||||
if (input.location >= static_cast<std::uint32_t>(inputInfo.vertex->resourcesNum)) throw std::runtime_error("vertex attribute location exceeds resource count");
|
||||
result.vertexInputs.push_back({input.location, input.componentCount, inputInfo.vertex->resourcesDst[input.location].fetchIndex});
|
||||
if (inputInfo.vertex->fetchEmbedded) {
|
||||
for (const auto& block : program.Blocks()) {
|
||||
for (const auto* instruction : block->Instructions()) {
|
||||
if (instruction->Opcode() == IrOpcode::GetAttribute && instruction->Argument(0)->Resolve()->ImmediateU32() == input.location) result.vertexInputs.back().outputMask |= 1u << instruction->Argument(1)->Resolve()->ImmediateU32();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const bool embedded = !result.vertexInputs.empty() && inputInfo.vertex != nullptr && inputInfo.vertex->fetchEmbedded;
|
||||
if (embedded) PrepareVertexInputSpecialization(result);
|
||||
#if ANYPS5_ENABLE_SPIRV_TOOLS
|
||||
result.spirv = ValidateAndOptimizeSpirv(result.spirv, request.target.vulkanVersion, request.target.spirvVersion, request.target.nonConstantImageOffsets, !embedded);
|
||||
if (embedded) {
|
||||
std::array<std::uint32_t, ShaderVertexStageInfo::MaxResources> classes{};
|
||||
for (std::uint32_t kind = 1u; kind < 3u; ++kind) {
|
||||
classes.fill(kind);
|
||||
const auto specialized = SpecializeVertexInputTypes(result, classes);
|
||||
static_cast<void>(ValidateAndOptimizeSpirv(specialized, request.target.vulkanVersion, request.target.spirvVersion, request.target.nonConstantImageOffsets, false));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return {request.layout, std::move(program).TakeCompiledInfo(), std::move(static_cast<CompiledBindingLayout&>(bindings)), std::move(result)};
|
||||
} catch (const std::bad_alloc&) {
|
||||
throw;
|
||||
} catch (...) {
|
||||
if (emissionFailure != nullptr) *emissionFailure = std::current_exception();
|
||||
throw;
|
||||
}
|
||||
|
||||
std::uint64_t specializationId(std::uint64_t artifact, std::span<const PipelineSpecializationConstant> constants) {
|
||||
|
|
@ -377,6 +400,16 @@ std::uint64_t specializationId(std::uint64_t artifact, std::span<const PipelineS
|
|||
return entry->second;
|
||||
}
|
||||
|
||||
SharedSpirv specializeVertexInputs(const CompiledShaderArtifact& artifact, std::span<const std::uint32_t> classes) {
|
||||
if (artifact.vertexInputPatches.empty()) return artifact.spirv;
|
||||
static std::mutex mutex;
|
||||
static std::map<std::pair<std::uint64_t, std::vector<std::uint32_t>>, SharedSpirv> modules;
|
||||
std::pair key{artifact.variantId, std::vector<std::uint32_t>(classes.begin(), classes.end())};
|
||||
std::lock_guard lock(mutex);
|
||||
if (const auto found = modules.find(key); found != modules.end()) return found->second;
|
||||
return modules.emplace(std::move(key), SpecializeVertexInputTypes(artifact, classes)).first->second;
|
||||
}
|
||||
|
||||
RecompileResult materializeResult(const CompiledVariant& variant, const RecompileRequest& request, const ResourceSnapshot& snapshot) {
|
||||
RecompileResult result;
|
||||
static_cast<CompiledShaderArtifact&>(result) = variant.artifact;
|
||||
|
|
@ -387,13 +420,35 @@ RecompileResult materializeResult(const CompiledVariant& variant, const Recompil
|
|||
DescriptorBindingBuilder{}.Populate(bindings, variant.info.info, variant.info.stage, variant.info.userDataBase, snapshot, partialThreads(request), request.context.pixel ? std::span<const std::uint8_t>(request.context.pixel->targetExportMapping) : std::span<const std::uint8_t>{});
|
||||
result.bindings = std::move(bindings.bindings);
|
||||
result.specialization = std::move(bindings.specialization);
|
||||
result.specializationId = specializationId(result.variantId, result.specialization);
|
||||
result.pushConstants = std::move(bindings.pushConstants);
|
||||
result.vertexAttributes.reserve(result.vertexInputs.size());
|
||||
std::array<std::uint32_t, ShaderVertexStageInfo::MaxResources> vertexClasses{};
|
||||
for (const auto& input : result.vertexInputs) {
|
||||
if (!request.context.vertex || input.location >= request.context.vertex->resourcesNum || input.location >= request.context.vertex->resources.size()) throw std::runtime_error("Shader cache: invalid vertex attribute metadata");
|
||||
result.vertexAttributes.push_back({input.location, input.components, request.context.vertex->resources[input.location], input.fetchIndex});
|
||||
const auto& vertex = *request.context.vertex;
|
||||
const auto& resource = vertex.resources[input.location];
|
||||
const auto fetchIndex = vertex.fetchEmbedded ? vertex.resourcesDst[input.location].fetchIndex : input.fetchIndex;
|
||||
result.vertexAttributes.push_back({input.location, input.components, resource, fetchIndex});
|
||||
if (!result.vertexInputPatches.empty()) {
|
||||
const auto numeric = VertexInputNumericClass(static_cast<IrBufferFormat>((resource.fields[3] >> 12u) & 0x7fu));
|
||||
if (numeric == IrTextureNumericClass::Unsupported) throw std::runtime_error("unsupported prepared vertex format");
|
||||
const auto kind = numeric == IrTextureNumericClass::Float ? 0u : numeric == IrTextureNumericClass::Sint ? 1u : 2u;
|
||||
vertexClasses.at(input.location) = kind;
|
||||
const auto first = PipelineSpecialization::VertexBase + input.location * PipelineSpecialization::VertexWords;
|
||||
auto& formatComponents = result.vertexAttributes.back().formatComponents;
|
||||
formatComponents = 1u;
|
||||
for (std::uint32_t component = 0; component < 4u; ++component) {
|
||||
const auto selector = (input.outputMask & (1u << component)) != 0u ? (resource.fields[3] >> (component * 3u)) & 7u : 0u;
|
||||
if (selector == 2u || selector == 3u) throw std::runtime_error("reserved prepared vertex component selector");
|
||||
if (selector >= 4u) formatComponents = std::max(formatComponents, selector - 3u);
|
||||
result.specialization.push_back({first + component, selector});
|
||||
}
|
||||
result.specialization.push_back({first + 4u, kind == 0u ? 0x3f800000u : 1u});
|
||||
result.specialization.push_back({first + 5u, kind});
|
||||
}
|
||||
}
|
||||
result.spirv = specializeVertexInputs(variant.artifact, vertexClasses);
|
||||
result.specializationId = specializationId(result.variantId, result.specialization);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
@ -541,12 +596,13 @@ std::uint64_t snapshotHash(const RecompileRequest& request, const ResourceSnapsh
|
|||
if (request.context.pixel) {
|
||||
for (const auto mapping : request.context.pixel->targetExportMapping) mix(mapping);
|
||||
}
|
||||
if (request.context.vertex && !request.context.vertex->fetchEmbedded) {
|
||||
if (request.context.vertex) {
|
||||
const auto& vertex = *request.context.vertex;
|
||||
const auto count = std::min<std::uint32_t>(vertex.resourcesNum, ShaderVertexStageInfo::MaxResources);
|
||||
mix(count);
|
||||
for (std::uint32_t i = 0; i < count; ++i) {
|
||||
for (const auto field : vertex.resources[i].fields) mix(field);
|
||||
mix(vertex.resourcesDst[i].fetchIndex);
|
||||
}
|
||||
} else {
|
||||
mix(1ull << 32u);
|
||||
|
|
|
|||
|
|
@ -346,6 +346,7 @@ struct VertexAttribute {
|
|||
std::uint32_t components;
|
||||
ShaderVertexBufferResource resource;
|
||||
std::uint32_t fetchIndex;
|
||||
std::uint32_t formatComponents = 0;
|
||||
};
|
||||
|
||||
struct FragmentParameter {
|
||||
|
|
@ -405,11 +406,21 @@ struct VertexInput {
|
|||
std::uint32_t location;
|
||||
std::uint32_t components;
|
||||
std::uint32_t fetchIndex;
|
||||
std::uint32_t outputMask = 0;
|
||||
|
||||
bool operator==(const VertexInput& other) const = default;
|
||||
};
|
||||
|
||||
struct VertexInputPatch {
|
||||
std::uint32_t location;
|
||||
std::uint32_t word;
|
||||
std::array<std::uint32_t, 3> values;
|
||||
|
||||
bool operator==(const VertexInputPatch& other) const = default;
|
||||
};
|
||||
|
||||
struct CompiledShaderArtifact {
|
||||
std::vector<VertexInputPatch> vertexInputPatches;
|
||||
SharedSpirv spirv;
|
||||
std::uint32_t memoryOffsetDword = 0;
|
||||
std::uint32_t bdaAbiVersion = 0;
|
||||
|
|
|
|||
|
|
@ -287,10 +287,16 @@ void encodeArtifact(Writer& writer, const CompiledShaderArtifact& result) {
|
|||
writer.Values(std::span<const std::uint32_t>(result.spirv.Words()));
|
||||
writer.Value(result.bdaAbiVersion);
|
||||
writer.Value(result.memoryOffsetDword);
|
||||
writer.List(result.vertexInputPatches, [](Writer& out, const VertexInputPatch& patch) {
|
||||
out.Value(patch.location);
|
||||
out.Value(patch.word);
|
||||
for (const auto value : patch.values) out.Value(value);
|
||||
});
|
||||
writer.List(result.vertexInputs, [](Writer& out, const VertexInput& attribute) {
|
||||
out.Value(attribute.location);
|
||||
out.Value(attribute.components);
|
||||
out.Value(attribute.fetchIndex);
|
||||
out.Value(attribute.outputMask);
|
||||
});
|
||||
writer.Value(result.vertexOffsetSgpr);
|
||||
writer.Value(result.instanceOffsetSgpr);
|
||||
|
|
@ -316,10 +322,16 @@ void decodeArtifact(Reader& reader, CompiledShaderArtifact& result) {
|
|||
result.spirv = std::move(words);
|
||||
reader.Value(result.bdaAbiVersion);
|
||||
reader.Value(result.memoryOffsetDword);
|
||||
reader.List(result.vertexInputs, 12, [](Reader& in, VertexInput& attribute) {
|
||||
reader.List(result.vertexInputPatches, 20, [](Reader& in, VertexInputPatch& patch) {
|
||||
in.Value(patch.location);
|
||||
in.Value(patch.word);
|
||||
for (auto& value : patch.values) in.Value(value);
|
||||
});
|
||||
reader.List(result.vertexInputs, 16, [](Reader& in, VertexInput& attribute) {
|
||||
in.Value(attribute.location);
|
||||
in.Value(attribute.components);
|
||||
in.Value(attribute.fetchIndex);
|
||||
in.Value(attribute.outputMask);
|
||||
});
|
||||
reader.Value(result.vertexOffsetSgpr);
|
||||
reader.Value(result.instanceOffsetSgpr);
|
||||
|
|
@ -352,6 +364,7 @@ void encodeInvocation(Writer& writer, const ShaderInvocation& invocation) {
|
|||
out.Value(attribute.components);
|
||||
for (const auto field : attribute.resource.fields) out.Value(field);
|
||||
out.Value(attribute.fetchIndex);
|
||||
out.Value(attribute.formatComponents);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -366,11 +379,12 @@ void decodeInvocation(Reader& reader, ShaderInvocation& invocation) {
|
|||
reader.Values(pushConstants);
|
||||
invocation.pushConstants.resize(pushConstants.size());
|
||||
if (!pushConstants.empty()) std::memcpy(invocation.pushConstants.data(), pushConstants.data(), pushConstants.size());
|
||||
reader.List(invocation.vertexAttributes, 28, [](Reader& in, VertexAttribute& attribute) {
|
||||
reader.List(invocation.vertexAttributes, 32, [](Reader& in, VertexAttribute& attribute) {
|
||||
in.Value(attribute.location);
|
||||
in.Value(attribute.components);
|
||||
for (auto& field : attribute.resource.fields) in.Value(field);
|
||||
in.Value(attribute.fetchIndex);
|
||||
in.Value(attribute.formatComponents);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -418,6 +432,8 @@ void encodeInfo(Writer& writer, const CompiledShaderInfo& compiled) {
|
|||
out.Value(buffer.written);
|
||||
out.Value(buffer.atomic);
|
||||
out.Value(buffer.formatted);
|
||||
out.Value(buffer.descriptorFormatted);
|
||||
out.Value(buffer.formattedReadMask);
|
||||
out.Value(buffer.scalar);
|
||||
});
|
||||
writer.List(info.images, [](Writer& out, const ImageResource& image) {
|
||||
|
|
@ -502,7 +518,7 @@ void decodeInfo(Reader& reader, CompiledShaderInfo& compiled) {
|
|||
auto& info = compiled.info;
|
||||
reader.Value(info.scratchDwords);
|
||||
reader.Value(info.sharedMemoryBytes);
|
||||
reader.List(info.buffers, 21, [](Reader& in, BufferResource& buffer) {
|
||||
reader.List(info.buffers, 26, [](Reader& in, BufferResource& buffer) {
|
||||
in.Value(buffer.source);
|
||||
in.Value(buffer.firstUsePc);
|
||||
in.Value(buffer.maxByteExtent);
|
||||
|
|
@ -511,6 +527,8 @@ void decodeInfo(Reader& reader, CompiledShaderInfo& compiled) {
|
|||
in.Value(buffer.written);
|
||||
in.Value(buffer.atomic);
|
||||
in.Value(buffer.formatted);
|
||||
in.Value(buffer.descriptorFormatted);
|
||||
in.Value(buffer.formattedReadMask);
|
||||
in.Value(buffer.scalar);
|
||||
});
|
||||
reader.List(info.images, 65, [](Reader& in, ImageResource& image) {
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
|
||||
namespace ShaderRecompiler::ShaderDiskCache {
|
||||
|
||||
inline constexpr std::uint32_t FormatVersion = 14;
|
||||
inline constexpr std::uint32_t FormatVersion = 15;
|
||||
|
||||
enum class LoadStatus {
|
||||
Loaded,
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
namespace ShaderRecompiler {
|
||||
|
||||
[[nodiscard]] std::vector<std::uint32_t> ValidateAndOptimizeSpirv(std::span<const std::uint32_t> spirv, std::uint32_t vulkanVersion, std::uint32_t spirvVersion, bool allowOffsetTextureOperand = false);
|
||||
[[nodiscard]] std::vector<std::uint32_t> ValidateAndOptimizeSpirv(std::span<const std::uint32_t> spirv, std::uint32_t vulkanVersion, std::uint32_t spirvVersion, bool allowOffsetTextureOperand = false, bool optimize = true);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
#include "SpirvBackend/SpirvBda.hpp"
|
||||
#include <spirv/unified1/GLSL.std.450.h>
|
||||
#include <spirv/unified1/spirv.hpp>
|
||||
#include "PipelineSpecialization.hpp"
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <limits>
|
||||
|
|
@ -1065,6 +1066,18 @@ std::uint32_t EmitGetBuiltin(SpirvValueEmitContext& ctx, const IrValue* kind, co
|
|||
}
|
||||
|
||||
std::uint32_t EmitGetAttribute(SpirvValueEmitContext& ctx, const IrValue& inst) {
|
||||
if (inst.Flags<std::uint32_t>() == 1u) {
|
||||
auto& state = ctx.state;
|
||||
const auto attribute = inst.Argument(0)->ImmediateU32();
|
||||
const auto component = inst.Argument(1)->ImmediateU32();
|
||||
if (state.inputInfo.vertex == nullptr || !state.inputInfo.vertex->fetchEmbedded || component >= 4u) ctx.Fail(inst, "invalid prepared vertex attribute");
|
||||
const auto first = PipelineSpecialization::VertexBase + attribute * PipelineSpecialization::VertexWords;
|
||||
const auto selector = state.module.SpecializationConstant(TypeU32(state), first + component, component + 4u);
|
||||
const auto one = state.module.SpecializationConstant(TypeU32(state), first + 4u, 0x3f800000u);
|
||||
auto value = Select(state, TypeU32(state), Binary(state, spv::OpIEqual, TypeBool(state), selector, ConstantU32(state, 1u)), one, ConstantU32(state, 0u));
|
||||
for (std::uint32_t channel = 0; channel < 4u; ++channel) value = Select(state, TypeU32(state), Binary(state, spv::OpIEqual, TypeBool(state), selector, ConstantU32(state, channel + 4u)), EmitAttributeValue(state, attribute, channel), value);
|
||||
return value;
|
||||
}
|
||||
return EmitAttributeValue(ctx.state, inst.Argument(0)->ImmediateU32(), inst.Argument(1)->ImmediateU32());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ bool HasLimitedUseTypes(std::span<const std::uint32_t> spirv) {
|
|||
|
||||
}
|
||||
|
||||
std::vector<std::uint32_t> ValidateAndOptimizeSpirv(std::span<const std::uint32_t> spirv, std::uint32_t vulkanVersion, std::uint32_t spirvVersion, bool allowOffsetTextureOperand) {
|
||||
std::vector<std::uint32_t> ValidateAndOptimizeSpirv(std::span<const std::uint32_t> spirv, std::uint32_t vulkanVersion, std::uint32_t spirvVersion, bool allowOffsetTextureOperand, bool optimize) {
|
||||
spv_target_env environment;
|
||||
const auto apiVersion = vulkanVersion & ~0xfffu;
|
||||
std::uint32_t maxSpirvVersion = 0;
|
||||
|
|
@ -89,6 +89,7 @@ std::vector<std::uint32_t> ValidateAndOptimizeSpirv(std::span<const std::uint32_
|
|||
if (!tools.Validate(spirv.data(), spirv.size(), validatorOptions)) {
|
||||
throw std::runtime_error("SPIR-V validation before optimization failed:\n" + diagnostics);
|
||||
}
|
||||
if (!optimize) return std::vector<std::uint32_t>(spirv.begin(), spirv.end());
|
||||
spvtools::Optimizer optimizer(environment);
|
||||
optimizer.SetMessageConsumer(consumer);
|
||||
static const char* mode = std::getenv("APS5_SPIRV_OPT");
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
#include "SpirvBackend/SpirvBufferFormat.hpp"
|
||||
#include "SpirvBackend/SpirvMemory/SpirvSubgroup.hpp"
|
||||
#include "PipelineSpecialization.hpp"
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <vector>
|
||||
|
||||
|
|
@ -57,9 +58,9 @@ RuntimeDescriptor descriptor(SpirvValueEmitContext& context, const IrValue& inst
|
|||
const auto word3 = memory.gpuDescriptor ? context.Arg(*handle, 3u) : state.module.SpecializationConstant(TypeU32(state), first + 1u, 0u);
|
||||
const auto u32 = TypeU32(state);
|
||||
auto base = ConstantU32(state, 0u);
|
||||
if (memory.gpuDescriptor) {
|
||||
if (memory.gpuDescriptor || BufferAccessOf(instruction.Opcode()) == BufferAccess::Atomic) {
|
||||
const auto u64 = TypeScalarU64(state);
|
||||
const auto high = Binary(state, spv::OpShiftLeftLogical, u64, Unary(state, spv::OpUConvert, u64, field(state, word1, 0u, 16u)), BdaConstant(state, 32u));
|
||||
const auto high = Binary(state, spv::OpShiftLeftLogical, u64, Unary(state, spv::OpUConvert, u64, field(state, context.Arg(*handle, 1u), 0u, 16u)), BdaConstant(state, 32u));
|
||||
base = Binary(state, spv::OpBitwiseOr, u64, Unary(state, spv::OpUConvert, u64, context.Arg(*handle, 0u)), high);
|
||||
}
|
||||
auto invalid = nonzero(state, field(state, word3, 30u, 2u));
|
||||
|
|
@ -383,11 +384,9 @@ void EmitRuntimeBufferStore(SpirvValueEmitContext& context, const IrValue& instr
|
|||
|
||||
std::uint32_t EmitRuntimeScalarBufferLoad(SpirvValueEmitContext& context, const IrValue& instruction) {
|
||||
auto& state = context.state;
|
||||
if (std::ranges::find(state.supportedCapabilities, spv::CapabilityInt64) == state.supportedCapabilities.end()) context.Fail(instruction, "scalar buffer bounds require shaderInt64");
|
||||
state.module.EmitCapability(spv::CapabilityInt64);
|
||||
const auto resource = descriptor(context, instruction);
|
||||
if (!context.Memory(instruction).gpuDescriptor) {
|
||||
const auto byte = EmitAndConstant(state, EmitAddU32(state, context.Arg(instruction, 1u), ConstantU32(state, context.Memory(instruction).offset)), ~3u);
|
||||
return EmitValueOrZeroIfCondition(state, resource.valid, [&] { return readBuffer(context, instruction, byte, 32u); });
|
||||
}
|
||||
const auto u64 = TypeScalarU64(state);
|
||||
const auto size = Select(state, u64, equal(state, resource.stride, ConstantU32(state, 0u)), Unary(state, spv::OpUConvert, u64, resource.records), Binary(state, spv::OpIMul, u64, Unary(state, spv::OpUConvert, u64, resource.stride), Unary(state, spv::OpUConvert, u64, resource.records)));
|
||||
const auto address = EmitAddU32(state, context.Arg(instruction, 1u), ConstantU32(state, context.Memory(instruction).offset));
|
||||
|
|
@ -395,6 +394,7 @@ std::uint32_t EmitRuntimeScalarBufferLoad(SpirvValueEmitContext& context, const
|
|||
const auto byte = EmitAndConstant(state, address, ~3u);
|
||||
const auto offset = Unary(state, spv::OpUConvert, u64, byte);
|
||||
const auto inBounds = both(state, noCarry, both(state, resource.valid, Binary(state, spv::OpULessThanEqual, TypeBool(state), Binary(state, spv::OpIAdd, u64, offset, BdaConstant(state, 4u)), size)));
|
||||
if (!context.Memory(instruction).gpuDescriptor) return EmitValueOrZeroIfCondition(state, inBounds, [&] { return readBuffer(context, instruction, byte, 32u); });
|
||||
return EmitValueOrZeroIfCondition(state, inBounds, [&] { return readBuffer(context, instruction, AddBdaAddress(context, instruction, resource.base, offset, false), 32u); });
|
||||
}
|
||||
|
||||
|
|
@ -412,8 +412,11 @@ std::uint32_t EmitRuntimeBufferAtomic(SpirvValueEmitContext& context, const IrVa
|
|||
if (!context.Memory(instruction).gpuDescriptor) {
|
||||
const auto storage = directAccess(context, instruction, bits == 64u);
|
||||
const auto byte = EmitAddU32(state, access.guest, storage.byteOffset);
|
||||
const auto unaligned = nonzero(state, EmitAndConstant(state, byte, bits / 8u - 1u));
|
||||
const auto guest = Binary(state, spv::OpIAdd, TypeScalarU64(state), resource.base, Unary(state, spv::OpUConvert, TypeScalarU64(state), access.guest));
|
||||
faultIf(context, instruction, unaligned, guest, bits / 8u, BdaAbi::FaultReason::Unaligned);
|
||||
const auto index = Binary(state, spv::OpShiftRightLogical, TypeU32(state), byte, ConstantU32(state, bits == 64u ? 3u : 2u));
|
||||
return EmitValueOrDefaultIfCondition(state, EmitMemoryElementInBounds(state, storage, index), type, zero, [&] {
|
||||
return EmitValueOrDefaultIfCondition(state, both(state, Unary(state, spv::OpLogicalNot, TypeBool(state), unaligned), EmitMemoryElementInBounds(state, storage, index)), type, zero, [&] {
|
||||
const auto pointer = EmitStorageBufferElementPointer(state, storage, index, bits == 64u ? TypeStorageBufferU64ElementPointer(state) : TypeStorageBufferElementPointer(state));
|
||||
return operation(pointer);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
#include "IntermediateRepresentation/IrBuilder.hpp"
|
||||
#include "IntermediateRepresentation/IrProgram.hpp"
|
||||
#include "Optimization/ShaderStageInputInfo.hpp"
|
||||
#include "Translation/EmbeddedVertexFetch.hpp"
|
||||
#include <cstdint>
|
||||
|
||||
namespace ShaderRecompiler {
|
||||
|
|
@ -31,6 +32,7 @@ struct TranslateOptions {
|
|||
std::uint64_t shaderHash = 0;
|
||||
bool fragmentShaderBarycentricEnabled = false;
|
||||
ShaderStageInputInfo inputInfo;
|
||||
const EmbeddedFetchPlan* embeddedFetch = nullptr;
|
||||
};
|
||||
|
||||
class InstructionTranslator {
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ class TranslationContext {
|
|||
public:
|
||||
TranslationContext(IrProgram& program, IrBlock& block, std::uint32_t vectorLimit);
|
||||
|
||||
void TranslateEmbeddedFetch(const RdnaInstruction& instruction, std::uint32_t attribute, std::uint32_t components);
|
||||
void TranslateInstruction(const RdnaInstruction& instruction);
|
||||
void SetPixelInput(const ShaderPixelInputInfo* info, bool barycentricEnabled) { pixelInput = info; fragmentShaderBarycentricEnabled = barycentricEnabled; }
|
||||
void AddBranchCondition(const BasicBlock& source, BlockInfo& info);
|
||||
|
|
|
|||
|
|
@ -127,6 +127,15 @@ bool TranslationContext::emitInterpolation(const RdnaInstruction& inst) {
|
|||
}
|
||||
}
|
||||
|
||||
void TranslationContext::TranslateEmbeddedFetch(const RdnaInstruction& instruction, std::uint32_t attribute, std::uint32_t components) {
|
||||
if (!instruction.formatted || instruction.typed || components == 0u || components > 4u) throw std::runtime_error("invalid prepared vertex fetch");
|
||||
for (std::uint32_t component = 0; component < components; ++component) {
|
||||
auto& value = ir.Emit(IrOpcode::GetAttribute, IrType::U32, {&ir.Constant(attribute), &ir.Constant(component)});
|
||||
value.SetFlags<std::uint32_t>(1u);
|
||||
writeOperand(offsetOperand(instruction.destination, component), &value);
|
||||
}
|
||||
}
|
||||
|
||||
void TranslateAttributeInstruction(TranslationContext& context, const RdnaInstruction& instruction) {
|
||||
throw std::runtime_error("TranslateAttributeInstruction not implemented");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ IrShaderStage toIrShaderStage(ShaderStageKind stage) {
|
|||
}
|
||||
|
||||
void validateTranslateOptions(const TranslateOptions& options) {
|
||||
if (options.embeddedFetch != nullptr && options.stage != ShaderStageKind::Vertex && options.stage != ShaderStageKind::Local) throw std::runtime_error("embedded vertex fetch requires a vertex or local shader");
|
||||
if (options.userDataBaseRegister >= NumScalarRegs || options.userDataCount > NumScalarRegs - options.userDataBaseRegister) {
|
||||
throw std::runtime_error("shader user data exceeds the scalar register bank");
|
||||
}
|
||||
|
|
@ -351,6 +352,14 @@ IrProgram InstructionTranslator::Translate(const RdnaProgram& decoded, const Con
|
|||
program.Resources().userDataBase = options.userDataBaseRegister;
|
||||
program.Resources().userDataCount = options.userDataCount;
|
||||
program.Info().scratchDwords = options.scratchDwords;
|
||||
if (options.embeddedFetch != nullptr) {
|
||||
program.Info().vertexOffsetSgpr = options.embeddedFetch->vertexOffsetSgpr;
|
||||
program.Info().instanceOffsetSgpr = options.embeddedFetch->instanceOffsetSgpr;
|
||||
program.Info().vertexOffsetShared = options.embeddedFetch->vertexOffsetShared;
|
||||
program.Info().instanceOffsetShared = options.embeddedFetch->instanceOffsetShared;
|
||||
program.Info().vertexOffsetConflict = options.embeddedFetch->vertexOffsetConflict;
|
||||
program.Info().instanceOffsetConflict = options.embeddedFetch->instanceOffsetConflict;
|
||||
}
|
||||
program.Metadata().cfgFailureKind = cfg.failureKind;
|
||||
program.Metadata().failureReason = cfg.unsupportedReason;
|
||||
|
||||
|
|
@ -409,6 +418,18 @@ IrProgram InstructionTranslator::Translate(const RdnaProgram& decoded, const Con
|
|||
context.TranslateCodeTableLoad(instruction, *table);
|
||||
continue;
|
||||
}
|
||||
if (options.embeddedFetch != nullptr) {
|
||||
const auto load = std::ranges::find(options.embeddedFetch->loads, instruction.programCounter, &EmbeddedFetchLoad::programCounter);
|
||||
if (load != options.embeddedFetch->loads.end()) {
|
||||
if (options.inputInfo.vertex == nullptr || instruction.destination.kind != RdnaOperandKind::VectorRegister || instruction.dataDwordCount != load->componentCount) throw std::runtime_error("prepared vertex fetch does not match its instruction");
|
||||
const auto& vertex = *options.inputInfo.vertex;
|
||||
std::uint32_t attribute = 0;
|
||||
while (attribute < static_cast<std::uint32_t>(vertex.resourcesNum) && vertex.resourcesDst[attribute].attrId != load->attributeId) ++attribute;
|
||||
if (attribute == static_cast<std::uint32_t>(vertex.resourcesNum)) throw std::runtime_error("prepared vertex fetch has no matching semantic");
|
||||
context.TranslateEmbeddedFetch(instruction, attribute, load->componentCount);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
context.TranslateInstruction(instruction);
|
||||
}
|
||||
context.AddBranchCondition(cfgBlock, blockInfos[typedIndex]);
|
||||
|
|
|
|||
|
|
@ -161,13 +161,14 @@ ShaderStageInputInfo BuildShaderStageInputInfo(ShaderStageKind stage, const Gues
|
|||
vertexStorage.fetchExternal = false;
|
||||
vertexStorage.fetchAttribReg = static_cast<int>(vertex.fetchAttribReg);
|
||||
vertexStorage.fetchBufferReg = static_cast<int>(vertex.fetchBufferReg);
|
||||
vertexStorage.resourcesNum = vertex.fetchEmbedded ? 0 : static_cast<int>(vertex.resourcesNum);
|
||||
vertexStorage.resourcesNum = static_cast<int>(vertex.resourcesNum);
|
||||
for (std::uint32_t i = 0; i < static_cast<std::uint32_t>(vertexStorage.resourcesNum); ++i) {
|
||||
vertexStorage.resources[i].fields = vertex.resources[i].fields;
|
||||
if (vertex.fetchEmbedded) vertexStorage.resources[i].fields = {0u, 0u, 0u, (static_cast<std::uint32_t>(IrBufferFormat::Format32_32_32_32Float) << 12u) | ShaderImageIdentitySwizzle};
|
||||
vertexStorage.resourcesDst[i].registerStart = vertex.resourcesDst[i].registerStart;
|
||||
vertexStorage.resourcesDst[i].registersNum = vertex.resourcesDst[i].registersNum;
|
||||
vertexStorage.resourcesDst[i].attrId = vertex.resourcesDst[i].attrId;
|
||||
vertexStorage.resourcesDst[i].fetchIndex = vertex.resourcesDst[i].fetchIndex;
|
||||
vertexStorage.resourcesDst[i].fetchIndex = vertex.fetchEmbedded ? 0u : vertex.resourcesDst[i].fetchIndex;
|
||||
}
|
||||
_detectVertexBuffers(vertexStorage);
|
||||
if (stage == ShaderStageKind::Local || stage == ShaderStageKind::TessellationControl || stage == ShaderStageKind::TessellationEvaluation) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,131 @@
|
|||
#ifndef CORE_SHADER_RECOMPILER_VERTEXINPUTSPECIALIZATION_HPP
|
||||
#define CORE_SHADER_RECOMPILER_VERTEXINPUTSPECIALIZATION_HPP
|
||||
|
||||
#include "Recompiler.hpp"
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <limits>
|
||||
#include <map>
|
||||
#include <span>
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <spirv/unified1/spirv.hpp>
|
||||
|
||||
namespace ShaderRecompiler {
|
||||
|
||||
inline SharedSpirv SpecializeVertexInputTypes(const CompiledShaderArtifact& artifact, std::span<const std::uint32_t> classes) {
|
||||
if (artifact.vertexInputPatches.empty()) return artifact.spirv;
|
||||
auto words = artifact.spirv.Words();
|
||||
for (const auto& patch : artifact.vertexInputPatches) {
|
||||
if (patch.location >= classes.size() || classes[patch.location] >= patch.values.size() || patch.word >= words.size()) throw std::runtime_error("invalid prepared vertex type patch");
|
||||
words[patch.word] = patch.values[classes[patch.location]];
|
||||
}
|
||||
return SharedSpirv(std::move(words));
|
||||
}
|
||||
|
||||
inline void PrepareVertexInputSpecialization(CompiledShaderArtifact& artifact) {
|
||||
const auto& source = artifact.spirv.Words();
|
||||
if (source.size() < 5u || source[0] != spv::MagicNumber) throw std::runtime_error("invalid vertex specialization module");
|
||||
std::map<std::uint32_t, std::uint32_t> locations;
|
||||
std::map<std::vector<std::uint32_t>, std::uint32_t> types;
|
||||
std::vector<std::vector<std::uint32_t>> declarations;
|
||||
std::vector<std::vector<std::uint32_t>> variables;
|
||||
std::map<std::uint32_t, std::uint32_t> pointers;
|
||||
std::map<std::uint32_t, std::uint32_t> loads;
|
||||
std::map<std::uint32_t, std::array<std::uint32_t, 3>> variableTypes;
|
||||
std::map<std::uint32_t, std::uint32_t> components;
|
||||
for (const auto& input : artifact.vertexInputs) {
|
||||
if (input.location >= ShaderVertexStageInfo::MaxResources || input.components == 0u || input.components > 4u || !components.emplace(input.location, input.components).second) throw std::runtime_error("invalid vertex specialization input");
|
||||
}
|
||||
auto bound = source[3];
|
||||
const auto declare = [&](std::vector<std::uint32_t> key) {
|
||||
const auto found = types.find(key);
|
||||
if (found != types.end()) return found->second;
|
||||
if (bound == std::numeric_limits<std::uint32_t>::max()) throw std::runtime_error("vertex specialization module ID overflow");
|
||||
const auto id = bound++;
|
||||
std::vector<std::uint32_t> words{(static_cast<std::uint32_t>(key.size() + 1u) << 16u) | key.front(), id};
|
||||
words.insert(words.end(), key.begin() + 1, key.end());
|
||||
types.emplace(std::move(key), id);
|
||||
declarations.push_back(std::move(words));
|
||||
return id;
|
||||
};
|
||||
for (std::size_t cursor = 5; cursor < source.size();) {
|
||||
const auto count = source[cursor] >> 16u;
|
||||
const auto op = static_cast<spv::Op>(source[cursor] & 0xffffu);
|
||||
if (count == 0u || count > source.size() - cursor) throw std::runtime_error("truncated vertex specialization instruction");
|
||||
if (op == spv::OpDecorate && count == 4u && source[cursor + 2u] == spv::DecorationLocation) locations.emplace(source[cursor + 1u], source[cursor + 3u]);
|
||||
if (op == spv::OpTypeInt || op == spv::OpTypeFloat || op == spv::OpTypeVector || op == spv::OpTypePointer) {
|
||||
std::vector<std::uint32_t> key{static_cast<std::uint32_t>(op)};
|
||||
key.insert(key.end(), source.begin() + cursor + 2u, source.begin() + cursor + count);
|
||||
types.emplace(std::move(key), source[cursor + 1u]);
|
||||
}
|
||||
cursor += count;
|
||||
}
|
||||
const std::array scalarTypes{declare({spv::OpTypeFloat, 32u}), declare({spv::OpTypeInt, 32u, 1u}), declare({spv::OpTypeInt, 32u, 0u})};
|
||||
std::array<std::uint32_t, 3> scalarPointers{};
|
||||
for (std::size_t kind = 0; kind < 3u; ++kind) scalarPointers[kind] = declare({spv::OpTypePointer, spv::StorageClassInput, scalarTypes[kind]});
|
||||
for (std::size_t cursor = 5; cursor < source.size();) {
|
||||
const auto count = source[cursor] >> 16u;
|
||||
const auto op = static_cast<spv::Op>(source[cursor] & 0xffffu);
|
||||
if (op == spv::OpVariable && count == 4u && source[cursor + 3u] == spv::StorageClassInput) {
|
||||
const auto location = locations.find(source[cursor + 2u]);
|
||||
if (location != locations.end() && components.contains(location->second)) {
|
||||
const auto id = source[cursor + 2u];
|
||||
pointers.emplace(id, location->second);
|
||||
auto& choices = variableTypes[id];
|
||||
for (std::size_t kind = 0; kind < 3u; ++kind) {
|
||||
const auto count = components.at(location->second);
|
||||
const auto type = count == 1u ? scalarTypes[kind] : declare({spv::OpTypeVector, scalarTypes[kind], count});
|
||||
choices[kind] = declare({spv::OpTypePointer, spv::StorageClassInput, type});
|
||||
}
|
||||
variables.emplace_back(source.begin() + cursor, source.begin() + cursor + count);
|
||||
}
|
||||
}
|
||||
cursor += count;
|
||||
}
|
||||
if (variables.size() != artifact.vertexInputs.size()) throw std::runtime_error("vertex specialization inputs disagree with metadata");
|
||||
std::vector<std::uint32_t> result(source.begin(), source.begin() + 5);
|
||||
result[3] = bound;
|
||||
bool inserted = false;
|
||||
const auto patch = [&](std::uint32_t location, std::size_t word, const std::array<std::uint32_t, 3>& values) {
|
||||
if (word > std::numeric_limits<std::uint32_t>::max()) throw std::runtime_error("vertex specialization offset overflow");
|
||||
artifact.vertexInputPatches.push_back({location, static_cast<std::uint32_t>(word), values});
|
||||
};
|
||||
artifact.vertexInputPatches.clear();
|
||||
for (std::size_t cursor = 5; cursor < source.size();) {
|
||||
const auto count = source[cursor] >> 16u;
|
||||
const auto op = static_cast<spv::Op>(source[cursor] & 0xffffu);
|
||||
if (op == spv::OpFunction && !inserted) {
|
||||
for (const auto& declaration : declarations) result.insert(result.end(), declaration.begin(), declaration.end());
|
||||
for (const auto& variable : variables) {
|
||||
patch(pointers.at(variable[2]), result.size() + 1u, variableTypes.at(variable[2]));
|
||||
result.insert(result.end(), variable.begin(), variable.end());
|
||||
}
|
||||
inserted = true;
|
||||
}
|
||||
if (op == spv::OpVariable && variableTypes.contains(source[cursor + 2u])) {
|
||||
cursor += count;
|
||||
continue;
|
||||
}
|
||||
if (op == spv::OpAccessChain && count == 5u && pointers.contains(source[cursor + 3u])) {
|
||||
const auto location = pointers.at(source[cursor + 3u]);
|
||||
pointers.emplace(source[cursor + 2u], location);
|
||||
patch(location, result.size() + 1u, scalarPointers);
|
||||
} else if (op == spv::OpLoad && count == 4u && pointers.contains(source[cursor + 3u])) {
|
||||
const auto location = pointers.at(source[cursor + 3u]);
|
||||
loads.emplace(source[cursor + 2u], location);
|
||||
patch(location, result.size() + 1u, scalarTypes);
|
||||
} else if (op == spv::OpBitcast && count == 4u && loads.contains(source[cursor + 3u])) {
|
||||
patch(loads.at(source[cursor + 3u]), result.size(), {(4u << 16u) | spv::OpBitcast, (4u << 16u) | spv::OpBitcast, (4u << 16u) | spv::OpCopyObject});
|
||||
}
|
||||
result.insert(result.end(), source.begin() + cursor, source.begin() + cursor + count);
|
||||
cursor += count;
|
||||
}
|
||||
if (!inserted) throw std::runtime_error("vertex specialization module has no function");
|
||||
artifact.spirv = std::move(result);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
Loading…
Reference in New Issue