载入中...
搜索中...
未找到
ShaderAssetValidation.cpp
浏览该文件的文档.
2#include "asset/ShaderAsset.h"
3
4#ifdef EVENGINE_SPIRV_ASSET_VALIDATION
5#include <exception>
6#include <map>
7#include <spirv-tools/libspirv.hpp>
8#include <spirv_cross/spirv_cross.hpp>
9#endif
10
11namespace eve::asset_graphics {
12namespace {
13#ifdef EVENGINE_SPIRV_ASSET_VALIDATION
14bool floatType(const spirv_cross::SPIRType& type, unsigned components) {
15 return type.basetype == spirv_cross::SPIRType::Float && type.width == 32 && type.vecsize == components &&
16 type.columns == 1 && type.array.empty();
17}
18Result<void> stageLayout(spirv_cross::Compiler& compiler, bool vertex, bool mesh) {
19 for (const auto capability : compiler.get_declared_capabilities())
20 if (capability != spv::CapabilityShader && capability != spv::CapabilityMatrix)
21 return Result<void>::failure(Diagnostic::error(DiagnosticCode::Unsupported, "Shader requires device features outside the baseline contract", {}, {}, "asset.shader.gpu-layout"));
22 if (!compiler.get_declared_extensions().empty())
23 return Result<void>::failure(Diagnostic::error(DiagnosticCode::Unsupported, "Shader extensions are outside the baseline contract", {}, {}, "asset.shader.gpu-layout"));
24 const auto resources = compiler.get_shader_resources();
25 if (!resources.storage_buffers.empty() || !resources.storage_images.empty() || !resources.subpass_inputs.empty() ||
26 !resources.separate_images.empty() || !resources.separate_samplers.empty() ||
27 !resources.atomic_counters.empty() || !resources.acceleration_structures.empty() ||
28 !compiler.get_specialization_constants().empty())
29 return Result<void>::failure(Diagnostic::error(DiagnosticCode::Unsupported, "Shader uses unsupported resources or specialization constants", {}, {}, "asset.shader.gpu-layout"));
30 if (resources.sampled_images.size() > 1 || (vertex && !resources.sampled_images.empty()))
31 return Result<void>::failure(Diagnostic::error(DiagnosticCode::Unsupported, "Only one fragment albedo sampler is supported", {}, {}, "asset.shader.gpu-layout"));
32 for (const auto& sampler : resources.sampled_images) {
33 const auto& type = compiler.get_type(sampler.type_id);
34 if (compiler.get_decoration(sampler.id, spv::DecorationDescriptorSet) != 0 ||
35 compiler.get_decoration(sampler.id, spv::DecorationBinding) != (mesh ? 1u : 0u) || !type.array.empty() ||
36 type.image.dim != spv::Dim2D || type.image.arrayed || type.image.ms || type.image.depth ||
37 compiler.get_type(type.image.type).basetype != spirv_cross::SPIRType::Float)
38 return Result<void>::failure(Diagnostic::error(DiagnosticCode::Unsupported, "Albedo must use the engine's single float sampler2D slot", {}, {}, "asset.shader.gpu-layout"));
39 }
40 if (resources.uniform_buffers.size() > 1 || (!mesh && !resources.uniform_buffers.empty()))
41 return Result<void>::failure(Diagnostic::error(DiagnosticCode::Unsupported, "Shader uses unsupported uniform buffers", {}, {}, "asset.shader.gpu-layout"));
42 for (const auto& buffer : resources.uniform_buffers) {
43 const auto& type = compiler.get_type(buffer.base_type_id);
44 if (compiler.get_decoration(buffer.id, spv::DecorationDescriptorSet) != 0 ||
45 compiler.get_decoration(buffer.id, spv::DecorationBinding) != 0 ||
46 !compiler.get_type(buffer.type_id).array.empty() || type.member_types.empty() ||
47 type.member_types.size() > 6)
48 return Result<void>::failure(Diagnostic::error(DiagnosticCode::Unsupported, "Mesh Frame buffer does not match the supported prefix", {}, {}, "asset.shader.gpu-layout"));
49 for (unsigned i = 0; i < type.member_types.size(); ++i) {
50 const auto& member = compiler.get_type(type.member_types[i]);
51 const auto offset = i < 2 ? i * 64 : 128 + (i - 2) * 16;
52 if (compiler.type_struct_member_offset(type, i) != offset)
53 return Result<void>::failure(Diagnostic::error(DiagnosticCode::Unsupported, "Mesh Frame member offset mismatch", {}, {}, "asset.shader.gpu-layout"));
54 if (i < 2) {
55 if (member.basetype != spirv_cross::SPIRType::Float || member.width != 32 || member.columns != 4 ||
56 member.vecsize != 4 || !member.array.empty() ||
57 compiler.type_struct_member_matrix_stride(type, i) != 16 ||
58 compiler.has_member_decoration(type.self, i, spv::DecorationRowMajor))
59 return Result<void>::failure(Diagnostic::error(DiagnosticCode::Unsupported, "Mesh Frame matrices require column-major mat4", {}, {}, "asset.shader.gpu-layout"));
60 } else if (!floatType(member, 4))
61 return Result<void>::failure(Diagnostic::error(DiagnosticCode::Unsupported, "Mesh Frame requires vec4 fields", {}, {}, "asset.shader.gpu-layout"));
62 }
63 }
64 if (resources.push_constant_buffers.size() > 1)
65 return Result<void>::failure(Diagnostic::error(DiagnosticCode::Unsupported, "Multiple push-constant blocks are unsupported", {}, {}, "asset.shader.gpu-layout"));
66 for (const auto& buffer : resources.push_constant_buffers) {
67 const auto& type = compiler.get_type(buffer.base_type_id);
68 if (type.member_types.size() != 1 || compiler.type_struct_member_offset(type, 0) != 0)
69 return Result<void>::failure(Diagnostic::error(DiagnosticCode::Unsupported, "Push constants require one float data[32] member", {}, {}, "asset.shader.gpu-layout"));
70 const auto& member = compiler.get_type(type.member_types[0]);
71 if (member.basetype != spirv_cross::SPIRType::Float || member.width != 32 || member.vecsize != 1 ||
72 member.columns != 1 || member.array.size() != 1 || !member.array_size_literal[0] || member.array[0] != 32 ||
73 compiler.type_struct_member_array_stride(type, 0) != 4)
74 return Result<void>::failure(Diagnostic::error(DiagnosticCode::Unsupported, "Push constants require tightly packed float data[32]", {}, {}, "asset.shader.gpu-layout"));
75 }
76 if (vertex) {
77 for (const auto& input : resources.stage_inputs) {
78 const auto location = compiler.get_decoration(input.id, spv::DecorationLocation);
79 const unsigned sizes[] = {mesh ? 3u : 2u, mesh ? 3u : 4u, 2u};
80 if (location > 2 || compiler.get_decoration(input.id, spv::DecorationComponent) != 0 ||
81 !floatType(compiler.get_type(input.type_id), sizes[location]))
82 return Result<void>::failure(Diagnostic::error(DiagnosticCode::Unsupported, "Vertex attributes do not match the engine layout", {}, {}, "asset.shader.gpu-layout"));
83 }
84 } else {
85 if (resources.stage_outputs.size() != 1 ||
86 compiler.get_decoration(resources.stage_outputs[0].id, spv::DecorationLocation) != 0 ||
87 compiler.get_decoration(resources.stage_outputs[0].id, spv::DecorationIndex) != 0 ||
88 compiler.get_decoration(resources.stage_outputs[0].id, spv::DecorationComponent) != 0 ||
89 !floatType(compiler.get_type(resources.stage_outputs[0].type_id), 4))
90 return Result<void>::failure(Diagnostic::error(DiagnosticCode::Unsupported, "Fragment output must be location zero vec4", {}, {}, "asset.shader.gpu-layout"));
91 }
92 return Result<void>::success();
93}
94#endif
95} // namespace
97#ifndef EVENGINE_SPIRV_ASSET_VALIDATION
98 (void)shader;
99 return Result<void>::failure(Diagnostic::error(DiagnosticCode::Unsupported, "Shader asset upload requires SPIRV-Tools and SPIRV-Cross", {}, {}, "asset.shader.gpu-layout"));
100#else
101 try {
102 spvtools::SpirvTools validator(SPV_ENV_VULKAN_1_2);
103 std::string diagnostics;
104 validator.SetMessageConsumer([&](spv_message_level_t, const char*, const spv_position_t&, const char* text) {
105 if (diagnostics.size() < 4096) diagnostics += text;
106 });
107 if (!validator.Validate(shader.vertex) || !validator.Validate(shader.fragment))
108 return Result<void>::failure(Diagnostic::error(DiagnosticCode::ParseError, "SPIR-V validation failed: " + diagnostics, {}, {}, "asset.shader.gpu-layout"));
109 spirv_cross::Compiler vertex(shader.vertex), fragment(shader.fragment);
110 vertex.set_entry_point("main", spv::ExecutionModelVertex);
111 fragment.set_entry_point("main", spv::ExecutionModelFragment);
112 const bool mesh = shader.interface == asset::ShaderAssetInterface::Mesh3D;
113 auto v = stageLayout(vertex, true, mesh);
114 if (!v) return v;
115 auto f = stageLayout(fragment, false, mesh);
116 if (!f) return f;
117 const auto outputs = vertex.get_shader_resources().stage_outputs;
118 for (const auto& input : fragment.get_shader_resources().stage_inputs) {
119 const auto location = fragment.get_decoration(input.id, spv::DecorationLocation);
120 bool matched = false;
121 const auto& type = fragment.get_type(input.type_id);
122 if (!floatType(type, type.vecsize) || fragment.get_decoration(input.id, spv::DecorationComponent) != 0)
123 return Result<void>::failure(Diagnostic::error(DiagnosticCode::Unsupported, "Fragment varyings must be float vectors", {}, {}, "asset.shader.gpu-layout"));
124 for (const auto& output : outputs)
125 if (vertex.get_decoration(output.id, spv::DecorationLocation) == location &&
126 vertex.get_decoration(output.id, spv::DecorationComponent) == 0 &&
127 floatType(vertex.get_type(output.type_id), type.vecsize))
128 matched = true;
129 if (!matched) return Result<void>::failure(Diagnostic::error(DiagnosticCode::Unsupported, "Vertex/fragment varying mismatch", {}, {}, "asset.shader.gpu-layout"));
130 }
131 return Result<void>::success();
132 } catch (const std::exception& error) {
133 return Result<void>::failure(Diagnostic::error(DiagnosticCode::ParseError, error.what(), {}, {}, "asset.shader.gpu-layout"));
134 }
135#endif
136}
137} // namespace eve::asset_graphics
std::string output
EvpackChunkInput input
Definition Evpack.cpp:170
vk::UniqueSampler sampler
vk::UniqueImage image
float u
Definition Grass.cpp:233
float v
std::string text
size_t offset
std::vector< std::weak_ptr< DeviceBytes > > resources
Definition OnnxGpgpu.cpp:55
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
float f
Mesh * mesh
Shader * shader
float(ui::Theme::* member)[4]
std::uint32_t depth
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
Result< void > validateShaderAssetGpu(const asset::ShaderAsset &shader)
Validate complete SPIR-V semantics and the supported engine pipeline interface before upload.
eve::Diagnostic Diagnostic
eve::Result< T > Result
enum EVENGINE_API_FOUNDATION Float
OT_FLOAT.
Definition Runtime.h:118
DiagnosticCode
Stable machine-readable diagnostic codes.
Definition Diagnostic.h:47
Owning CPU program snapshot; decoding does not prove GPU/descriptor compatibility.
Definition ShaderAsset.h:26