载入中...
搜索中...
未找到
PbrVariantSource.cpp
浏览该文件的文档.
3
4#include <cstddef>
5#include <cstdint>
6#include <string>
7#include <array>
8
9#include "graphics/shaders/pbr_surface.frag_wgsl.inc"
10#include "graphics/shaders/pbr_surface.vert_wgsl.inc"
11
12namespace eve::graphics::webgpu {
13namespace {
14std::string_view sourceView(const std::uint32_t* words, std::size_t count) {
15 std::string_view source(reinterpret_cast<const char*>(words), count * sizeof(std::uint32_t));
16 while (!source.empty() && source.back() == '\0') source.remove_suffix(1);
17 return source;
18}
19
20void eraseDeclaration(std::string& source, std::string_view variable) {
21 const std::string marker = " var " + std::string(variable) + " :";
22 auto position = source.find(marker);
23 if (position == std::string::npos) return;
24 auto begin = source.rfind('\n', position);
25 begin = begin == std::string::npos ? 0 : begin + 1;
26 auto end = source.find('\n', position);
27 end = end == std::string::npos ? source.size() : end + 1;
28 source.erase(begin, end - begin);
29}
30
31void eraseKeepalive(std::string& source, std::string_view variable) {
32 const std::string marker = "_ = " + std::string(variable) + ";";
33 std::size_t position = 0;
34 while ((position = source.find(marker, position)) != std::string::npos) {
35 auto begin = source.rfind('\n', position);
36 begin = begin == std::string::npos ? 0 : begin + 1;
37 auto end = source.find('\n', position);
38 end = end == std::string::npos ? source.size() : end + 1;
39 source.erase(begin, end - begin);
40 }
41}
42
43void replaceAll(std::string& source, std::string_view from, std::string_view to) {
44 std::size_t position = 0;
45 while ((position = source.find(from, position)) != std::string::npos) {
46 source.replace(position, from.size(), to);
47 position += to.size();
48 }
49}
50
51std::string samplerName(std::uint32_t resource) {
52 constexpr auto canonicalCount = std::uint32_t(PbrTextureSlot::Count);
53 if (resource < canonicalCount) return "map" + std::to_string(resource) + "_sampler";
54 return "detailMap" + std::to_string(resource - canonicalCount) + "_sampler";
55}
56
57void coalesceSampler(std::string& source, std::string_view resource, std::uint32_t resourceIndex,
58 std::uint32_t representative) {
59 if (representative == PbrVariantPlan::NoSampler || representative == resourceIndex) return;
60 const std::string sampler = std::string(resource) + "_sampler";
61 eraseDeclaration(source, sampler);
62 eraseKeepalive(source, sampler);
63 replaceAll(source, sampler, samplerName(representative));
64}
65
66void replaceResourceCalls(std::string& source, std::string_view function, std::string_view resource,
67 std::string_view replacement) {
68 const std::string prefix = std::string(function) + "(" + std::string(resource);
69 std::size_t begin = 0;
70 while ((begin = source.find(prefix, begin)) != std::string::npos) {
71 const auto delimiter = begin + prefix.size();
72 if (delimiter >= source.size() || (source[delimiter] != ',' && source[delimiter] != ')')) {
73 begin = delimiter;
74 continue;
75 }
76 std::size_t depth = 0;
77 std::size_t end = begin;
78 for (; end < source.size(); ++end) {
79 if (source[end] == '(') ++depth;
80 if (source[end] == ')' && --depth == 0) {
81 ++end;
82 break;
83 }
84 }
85 source.replace(begin, end - begin, replacement);
86 begin += replacement.size();
87 }
88}
89
90void removeSampledResource(std::string& source, std::string_view base,
91 std::string_view replacement = "vec4<f32>(1.0f)") {
92 const std::string image = std::string(base) + "_image";
93 const std::string sampler = std::string(base) + "_sampler";
94 replaceResourceCalls(source, "textureSampleLevel", image, replacement);
95 replaceResourceCalls(source, "textureSampleCompare", image, "1.0f");
96 replaceResourceCalls(source, "textureSample", image, replacement);
97 eraseDeclaration(source, image);
98 eraseDeclaration(source, sampler);
99 eraseKeepalive(source, image);
100 eraseKeepalive(source, sampler);
101}
102} // namespace
103
105 return {sourceView(pbr_surface_vert_wgsl, pbr_surface_vert_wgsl_count),
106 sourceView(pbr_surface_frag_wgsl, pbr_surface_frag_wgsl_count)};
107}
108
110 const auto base = pbrVariantBaseSources();
111 PbrSpecializedSources result{std::string(base.vertex), std::string(base.fragment)};
112 static constexpr std::array<std::string_view, 11> maps{
113 "map0", "map1", "map2", "map3", "map4", "map5", "map6", "map7", "map8", "map9", "map10"};
114 static constexpr std::array<std::string_view, 3> detailMaps{"detailMap0", "detailMap1", "detailMap2"};
115 for (std::uint32_t index = 0; index < maps.size(); ++index)
116 if ((plan.canonicalTextureMask & (1u << index)) == 0u)
117 removeSampledResource(result.fragment, maps[index]);
118 for (std::uint32_t index = 0; index < detailMaps.size(); ++index)
119 if ((plan.detailTextureMask & (1u << index)) == 0u)
120 removeSampledResource(result.fragment, detailMaps[index]);
121 for (std::uint32_t index = 0; index < maps.size(); ++index)
122 if ((plan.canonicalTextureMask & (1u << index)) != 0u)
123 coalesceSampler(result.fragment, maps[index], index,
125 for (std::uint32_t index = 0; index < detailMaps.size(); ++index)
126 if ((plan.detailTextureMask & (1u << index)) != 0u)
127 coalesceSampler(result.fragment, detailMaps[index],
128 std::uint32_t(PbrTextureSlot::Count) + index,
130 if ((plan.fragmentFlags & PbrExtras) == 0u) removeSampledResource(result.fragment, "extrasMap");
131 if ((plan.fragmentFlags & PbrColors) == 0u) removeSampledResource(result.fragment, "colorsMap");
132 if ((plan.fragmentFlags & PbrFadeNoise) == 0u)
133 removeSampledResource(result.fragment, "vegetationFadeNoise",
134 "vec4<f32>(0.0f, 0.0f, 0.0f, 1.0f)");
135 if ((plan.fragmentFlags & PbrEnvironment) == 0u) {
136 replaceResourceCalls(result.fragment, "textureNumLevels", "environmentMap_image", "1u");
137 removeSampledResource(result.fragment, "environmentMap");
138 }
139 if ((plan.fragmentFlags & PbrShadows) == 0u) {
140 replaceResourceCalls(result.fragment, "textureDimensions", "shadowMap_image", "vec2<u32>(1u)");
141 replaceResourceCalls(result.fragment, "textureNumLayers", "shadowMap_image", "1u");
142 removeSampledResource(result.fragment, "shadowMap", "1.0f");
143 }
144 if ((plan.vertexFlags & PbrVertexField) == 0u) removeSampledResource(result.vertex, "vertexField");
145 if ((plan.vertexFlags & PbrMotionField) == 0u) removeSampledResource(result.vertex, "motionField");
146 if ((plan.vertexFlags & PbrMotionNoise) == 0u) removeSampledResource(result.vertex, "motionNoise");
147 return result;
148}
149
153
155 if (!device || !instance)
157 Diagnostic::error(DiagnosticCode::PreconditionViolation, "WebGPU device is not initialized"));
158 device.PushErrorScope(wgpu::ErrorFilter::Validation);
159 auto makeModule = [&](std::string_view source) {
160 wgpu::ShaderSourceWGSL wgsl{};
161 wgsl.code = source.data();
162 wgpu::ShaderModuleDescriptor descriptor{};
163 descriptor.nextInChain = &wgsl;
164 return device.CreateShaderModule(&descriptor);
165 };
166 auto vertex = makeModule(sources.vertex);
167 auto fragment = makeModule(sources.fragment);
168
169 bool done = false;
170 wgpu::ErrorType error = wgpu::ErrorType::Unknown;
171 std::string message;
172 device.PopErrorScope(
173 wgpu::CallbackMode::AllowProcessEvents,
174 [&](wgpu::PopErrorScopeStatus status, wgpu::ErrorType type, wgpu::StringView text) {
175 error = status == wgpu::PopErrorScopeStatus::Success ? type : wgpu::ErrorType::Unknown;
176 if (text.data) message.assign(text.data, text.length);
177 done = true;
178 });
179 while (!done) wgpuInstanceProcessEvents(instance.Get());
180 if (!vertex || !fragment || error != wgpu::ErrorType::NoError)
183 message.empty() ? "WebGPU rejected generated PBR WGSL" : message));
184 return Result<void>::success();
185}
186
188 auto modules = debugValidatePbrVariantSources(sources);
189 if (!modules) return modules;
190
191 device.PushErrorScope(wgpu::ErrorFilter::Validation);
192 auto makeModule = [&](std::string_view source) {
193 wgpu::ShaderSourceWGSL wgsl{};
194 wgsl.code = source.data();
195 wgpu::ShaderModuleDescriptor moduleDescriptor{};
196 moduleDescriptor.nextInChain = &wgsl;
197 return device.CreateShaderModule(&moduleDescriptor);
198 };
199 auto vertex = makeModule(sources.vertex);
200 auto fragment = makeModule(sources.fragment);
201
202 std::array<wgpu::VertexAttribute, 16> attributes{};
203 auto attribute = [&](std::size_t index, wgpu::VertexFormat format, std::uint64_t offset,
204 std::uint32_t location) {
205 attributes[index].format = format;
206 attributes[index].offset = offset;
207 attributes[index].shaderLocation = location;
208 };
209 attribute(0, wgpu::VertexFormat::Float32x3, 0, 0);
210 attribute(1, wgpu::VertexFormat::Float32x3, 12, 1);
211 attribute(2, wgpu::VertexFormat::Float32x2, 24, 2);
212 attribute(3, wgpu::VertexFormat::Uint16x4, 32, 3);
213 attribute(4, wgpu::VertexFormat::Float32x4, 40, 4);
214 for (std::uint32_t index = 0; index < 11; ++index)
215 attribute(index + 5, wgpu::VertexFormat::Float32x2, std::uint64_t(index) * 8u, index + 5);
216 std::array<wgpu::VertexBufferLayout, 2> buffers{};
217 buffers[0].arrayStride = 56;
218 buffers[0].attributeCount = 5;
219 buffers[0].attributes = attributes.data();
220 buffers[1].arrayStride = 88;
221 buffers[1].attributeCount = 11;
222 buffers[1].attributes = attributes.data() + 5;
223
224 wgpu::ColorTargetState target{};
225 target.format = wgpu::TextureFormat::RGBA8Unorm;
226 wgpu::FragmentState fragmentState{};
227 fragmentState.module = fragment;
228 fragmentState.entryPoint = "main";
229 fragmentState.targetCount = 1;
230 fragmentState.targets = &target;
231 wgpu::RenderPipelineDescriptor descriptor{};
232 descriptor.vertex.module = vertex;
233 descriptor.vertex.entryPoint = "main";
234 descriptor.vertex.bufferCount = buffers.size();
235 descriptor.vertex.buffers = buffers.data();
236 descriptor.fragment = &fragmentState;
237 descriptor.primitive.topology = wgpu::PrimitiveTopology::TriangleList;
238 descriptor.multisample.count = 1;
239 auto pipeline = device.CreateRenderPipeline(&descriptor);
240
241 bool done = false;
242 wgpu::ErrorType error = wgpu::ErrorType::Unknown;
243 std::string message;
244 device.PopErrorScope(
245 wgpu::CallbackMode::AllowProcessEvents,
246 [&](wgpu::PopErrorScopeStatus status, wgpu::ErrorType type, wgpu::StringView text) {
247 error = status == wgpu::PopErrorScopeStatus::Success ? type : wgpu::ErrorType::Unknown;
248 if (text.data) message.assign(text.data, text.length);
249 done = true;
250 });
251 while (!done) wgpuInstanceProcessEvents(instance.Get());
252 if (!pipeline || error != wgpu::ErrorType::NoError)
254 DiagnosticCode::Failed, message.empty() ? "WebGPU rejected generated PBR pipeline" : message));
255 return Result<void>::success();
256}
257
258} // namespace eve::graphics::webgpu
LogicalId target
std::string descriptor
std::string from
std::string message
vk::UniqueSampler sampler
vk::UniqueImage image
wgpu::PopErrorScopeStatus status
float u
Definition Grass.cpp:233
HexCoordinates to
Cell the unit walks towards on this segment.
Definition HexUnits.cpp:64
std::string text
size_t offset
std::array< float, 3 > position
std::string error
Definition Package.cpp:60
float begin
MigrationFunction function
std::uint32_t count
uint32_t index
const UnitySourceAsset & source
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 > debugValidatePbrVariantBaseSources()
Compile the generated full PBR WGSL stages under a validation scope.
Result< void > debugValidatePbrVariantSources(const PbrVariantSources &sources)
Compile caller-provided PBR WGSL stages under a validation scope.
Result< void > debugValidatePbrVariantPipeline(const PbrVariantSources &sources)
Compile and link a specialized PBR pair through a real Dawn render pipeline.
PbrVariantSources pbrVariantBaseSources()
Return immutable full PBR WGSL generated from the canonical GLSL implementation.
PbrSpecializedSources specializePbrVariantSources(const PbrVariantPlan &plan)
Specialize the generated WGSL for the resource reachability encoded by a checked plan.
Owning WGSL stages after unreachable texture operations and declarations are removed.
Stable resource-layout key and checked stage counts for a PBR snapshot.
static constexpr std::uint32_t NoSampler
std::array< std::uint32_t, 11 > canonicalSamplerRepresentatives
std::array< std::uint32_t, 3 > detailSamplerRepresentatives
Reproducibly generated full PBR WGSL sources before resource specialization.