载入中...
搜索中...
未找到
ShaderResourceValidation.cpp
浏览该文件的文档.
2#include <algorithm>
3#include <exception>
4#include <string>
5#if defined(EVENGINE_SPIRV_ASSET_VALIDATION)
6#include <spirv-tools/libspirv.hpp>
7#include <spirv_cross/spirv_cross.hpp>
8#endif
9
10namespace eve::graphics::detail {
11namespace {
12#if defined(EVENGINE_SPIRV_ASSET_VALIDATION)
13bool floats(const spirv_cross::SPIRType& t, uint32_t size) {
14 return t.basetype == spirv_cross::SPIRType::Float && t.width == 32 && t.vecsize == size && t.columns == 1 &&
15 t.array.empty();
16}
17Result<void> stage(std::span<const uint32_t> words, bool vertex, const ShaderResourceInputs& inputs) {
18 spvtools::SpirvTools validator(SPV_ENV_VULKAN_1_2);
19 if (!validator.Validate(words.data(), words.size()))
20 return Result<void>::failure(Diagnostic::error(DiagnosticCode::ParseError, "Invalid Vulkan 1.2 SPIR-V", "shader.resources"));
21 spirv_cross::Compiler compiler(words.data(), words.size());
22 const auto entries = compiler.get_entry_points_and_stages();
23 if (entries.size() != 1 || entries[0].name != "main" ||
24 entries[0].execution_model != (vertex ? spv::ExecutionModelVertex : spv::ExecutionModelFragment))
25 return Result<void>::failure(Diagnostic::error(DiagnosticCode::Unsupported, "Expected one main entry point with the requested stage", "shader.resources"));
26 for (auto cap : compiler.get_declared_capabilities())
27 if (cap != spv::CapabilityShader && cap != spv::CapabilityMatrix)
28 return Result<void>::failure(Diagnostic::error(DiagnosticCode::Unsupported, "Resource programs require only Shader/Matrix capabilities", "shader.resources"));
29 if (!compiler.get_declared_extensions().empty() || !compiler.get_specialization_constants().empty())
30 return Result<void>::failure(Diagnostic::error(DiagnosticCode::Unsupported, "Extensions and specialization constants are unsupported", "shader.resources"));
31 const auto r = compiler.get_shader_resources();
32 if (!r.storage_images.empty() || !r.subpass_inputs.empty() || !r.separate_images.empty() ||
33 !r.separate_samplers.empty() || !r.atomic_counters.empty())
34 return Result<void>::failure(Diagnostic::error(DiagnosticCode::Unsupported, "Resource programs require combined sampled images and uniform buffers", "shader.resources"));
35 if (r.storage_buffers.size() > 1) return Result<void>::failure(Diagnostic::error(DiagnosticCode::Unsupported, "Only the readonly instance matrix buffer is supported", "shader.resources"));
36 for (const auto& buffer : r.storage_buffers) {
37 const auto& t = compiler.get_type(buffer.base_type_id);
38 if (!vertex || inputs.instanceMatrices.empty() ||
39 compiler.get_decoration(buffer.id, spv::DecorationDescriptorSet) != 1 ||
40 compiler.get_decoration(buffer.id, spv::DecorationBinding) != 33 ||
41 !compiler.get_type(buffer.type_id).array.empty() || t.member_types.size() != 1 ||
42 compiler.type_struct_member_offset(t, 0) != 0)
43 return Result<void>::failure(Diagnostic::error(DiagnosticCode::Unsupported, "Instance storage must be vertex-only set 1 binding 33", "shader.resources"));
44 const auto& m = compiler.get_type(t.member_types[0]);
45 if ((!compiler.has_decoration(buffer.id, spv::DecorationNonWritable) &&
46 !compiler.has_member_decoration(t.self, 0, spv::DecorationNonWritable)) ||
47 m.basetype != spirv_cross::SPIRType::Float || m.width != 32 || m.vecsize != 4 || m.columns != 4 ||
48 m.array.size() != 1 || m.array[0] != 0 || compiler.type_struct_member_array_stride(t, 0) != 64 ||
49 compiler.type_struct_member_matrix_stride(t, 0) != 16 ||
50 compiler.has_member_decoration(t.self, 0, spv::DecorationRowMajor))
51 return Result<void>::failure(Diagnostic::error(DiagnosticCode::Unsupported, "Instance storage must contain readonly column-major mat4 records", "shader.resources"));
52 }
53 for (const auto& image : r.sampled_images) {
54 const auto& t = compiler.get_type(image.type_id);
55 const auto set = compiler.get_decoration(image.id, spv::DecorationDescriptorSet);
56 const auto binding = compiler.get_decoration(image.id, spv::DecorationBinding);
57 if (!t.array.empty() || t.image.ms || t.image.depth ||
58 compiler.get_type(t.image.type).basetype != spirv_cross::SPIRType::Float)
59 return Result<void>::failure(Diagnostic::error(DiagnosticCode::Unsupported, "Expected a non-shadow, non-MS float combined image", "shader.resources"));
61 if (set == 0 && binding == 1 && !vertex) {
62 // The existing per-material albedo binding.
63 } else if (set == 1) {
64 auto found = std::find_if(inputs.images.begin(), inputs.images.end(),
65 [&](const auto& item) { return item.binding == binding; });
66 if (found == inputs.images.end()) return Result<void>::failure(Diagnostic::error(DiagnosticCode::Unsupported, "Shader image binding has no supplied resource", "shader.resources"));
67 expected = found->dimension;
68 } else
69 return Result<void>::failure(Diagnostic::error(DiagnosticCode::Unsupported, "Image binding is outside the resource program ABI", "shader.resources"));
70 const auto expectedDimension = expected == ShaderImageDimension::Cube ? spv::DimCube
71 : expected == ShaderImageDimension::Image3D ? spv::Dim3D
72 : spv::Dim2D;
73 if (t.image.dim != expectedDimension || bool(t.image.arrayed) != (expected == ShaderImageDimension::Array2D))
74 return Result<void>::failure(Diagnostic::error(DiagnosticCode::Unsupported, "Shader image view dimension does not match the supplied resource", "shader.resources"));
75 }
76 for (const auto& buffer : r.uniform_buffers) {
77 const auto set = compiler.get_decoration(buffer.id, spv::DecorationDescriptorSet);
78 const auto binding = compiler.get_decoration(buffer.id, spv::DecorationBinding);
79 const auto& t = compiler.get_type(buffer.base_type_id);
80 if (!compiler.get_type(buffer.type_id).array.empty()) return Result<void>::failure(Diagnostic::error(DiagnosticCode::Unsupported, "Buffer descriptor arrays unsupported", "shader.resources"));
81 if (set == 1 && binding == 32) {
82 if (inputs.constants.empty() || compiler.get_declared_struct_size(t) > inputs.constants.size())
83 return Result<void>::failure(Diagnostic::error(DiagnosticCode::Unsupported, "Uniform buffer is larger than its supplied bytes", "shader.resources"));
84 } else if (set == 0 && binding == 0) {
85 if (t.member_types.empty() || t.member_types.size() > 6) return Result<void>::failure(Diagnostic::error(DiagnosticCode::Unsupported, "Expected the supported Frame prefix", "shader.resources"));
86 for (unsigned i = 0; i < t.member_types.size(); ++i) {
87 const auto& m = compiler.get_type(t.member_types[i]);
88 if (compiler.type_struct_member_offset(t, i) != (i < 2 ? i * 64 : 128 + (i - 2) * 16))
89 return Result<void>::failure(Diagnostic::error(DiagnosticCode::Unsupported, "Frame member offset mismatch", "shader.resources"));
90 if (i < 2) {
91 if (m.basetype != spirv_cross::SPIRType::Float || m.width != 32 || m.columns != 4 ||
92 m.vecsize != 4 || !m.array.empty() || compiler.type_struct_member_matrix_stride(t, i) != 16 ||
93 compiler.has_member_decoration(t.self, i, spv::DecorationRowMajor))
94 return Result<void>::failure(Diagnostic::error(DiagnosticCode::Unsupported, "Frame matrices must be column-major mat4", "shader.resources"));
95 } else if (!floats(m, 4))
96 return Result<void>::failure(Diagnostic::error(DiagnosticCode::Unsupported, "Frame trailing members must be vec4", "shader.resources"));
97 }
98 } else
99 return Result<void>::failure(Diagnostic::error(DiagnosticCode::Unsupported, "Uniform binding is outside the resource program ABI", "shader.resources"));
100 }
101 if (r.push_constant_buffers.size() > 1) return Result<void>::failure(Diagnostic::error(DiagnosticCode::Unsupported, "Multiple push constant blocks unsupported", "shader.resources"));
102 for (const auto& buffer : r.push_constant_buffers) {
103 const auto& t = compiler.get_type(buffer.base_type_id);
104 if (t.member_types.size() != 1 || compiler.type_struct_member_offset(t, 0) != 0)
105 return Result<void>::failure(Diagnostic::error(DiagnosticCode::Unsupported, "Push constants must contain float data[32]", "shader.resources"));
106 const auto& m = compiler.get_type(t.member_types[0]);
107 if (m.basetype != spirv_cross::SPIRType::Float || m.width != 32 || m.vecsize != 1 || m.columns != 1 ||
108 m.array.size() != 1 || !m.array_size_literal[0] || m.array[0] != 32 ||
109 compiler.type_struct_member_array_stride(t, 0) != 4)
110 return Result<void>::failure(Diagnostic::error(DiagnosticCode::Unsupported, "Push constants must be 128 tightly packed float bytes", "shader.resources"));
111 }
112 if (vertex) {
113 for (const auto& input : r.stage_inputs) {
114 auto location = compiler.get_decoration(input.id, spv::DecorationLocation);
115 const uint32_t sizes[] = {3, 3, 2, 0, 0, 4};
116 if (location > 5 || sizes[location] == 0 || !floats(compiler.get_type(input.type_id), sizes[location]))
117 return Result<void>::failure(Diagnostic::error(DiagnosticCode::Unsupported, "Resource vertex inputs require position/normal/UV or optional vec4 tangent at location 5", "shader.resources"));
118 }
119 } else if (r.stage_outputs.size() != 1 ||
120 compiler.get_decoration(r.stage_outputs[0].id, spv::DecorationLocation) != 0 ||
121 !floats(compiler.get_type(r.stage_outputs[0].type_id), 4))
122 return Result<void>::failure(Diagnostic::error(DiagnosticCode::Unsupported, "Fragment output must be one vec4 at location 0", "shader.resources"));
123 return Result<void>::success();
124}
125#endif
126} // namespace
127
128Result<void> validateResourceShaderStages(std::span<const uint32_t> vertex, std::span<const uint32_t> fragment,
130#if defined(EVENGINE_SPIRV_ASSET_VALIDATION)
131 try {
132 if (!vertex.empty()) {
133 auto result = stage(vertex, true, inputs);
134 if (!result) return result;
135 }
136 return stage(fragment, false, inputs);
137 } catch (const std::exception& error) {
138 return Result<void>::failure(Diagnostic::error(DiagnosticCode::ParseError, error.what(), "shader.resources"));
139 }
140#else
141 (void)vertex;
142 (void)fragment;
143 (void)inputs;
144 return Result<void>::failure(Diagnostic::error(DiagnosticCode::Unsupported, "SPIR-V validation provider is unavailable", "shader.resources"));
145#endif
146}
147} // namespace eve::graphics::detail
std::unordered_map< std::string, QuestRuntime > entries
EvpackChunkInput input
Definition Evpack.cpp:170
vk::UniqueImage image
int inputs
Definition GridGraph.cpp:23
double r
onnx_detail::CompilerQueue compiler
Definition OnnxGpgpu.cpp:45
std::unique_ptr< gpgpu::GpuBuffer > buffer
Definition OnnxGpgpu.cpp:26
std::string error
Definition Package.cpp:60
eve::action::ActionVfxBinding binding
float t
bool found
float size
Definition TreeMesh.cpp:156
float m[16]
static Diagnostic error(DiagnosticCode code, std::string message, std::string path={}, DiagnosticDetails details={}, std::string source={})
Construct an error diagnostic with the standard error severity.
Definition Diagnostic.h:125
Move-only operation result carrying either a value or Status.
Definition Result.h:155
static Result success(T value)
Construct a successful result owning value.
Definition Result.h:164
static Result failure(Status status)
Construct a failed result from a structured status.
Definition Result.h:175
eve::Diagnostic Diagnostic
eve::Result< T > Result
Result< void > validateResourceShaderStages(std::span< const uint32_t > vertex, std::span< const uint32_t > fragment, const ShaderResourceInputs &inputs)
Validate resource shader stages.
ShaderImageDimension
Sampled image view dimension; arrays retain all authored layers.
std::vector< float > floats(const RuntimeTensor &v)
Floats.
DiagnosticCode
Stable machine-readable diagnostic codes.
Definition Diagnostic.h:47
Inputs for descriptor set 1 of a resource mesh program.