载入中...
搜索中...
未找到
GraphicsShaderReload.cpp
浏览该文件的文档.
2
3#include <algorithm>
4#include <exception>
5#include <utility>
6
7#include "common/Diagnostic.h"
8#include "common/Exception.h"
9#include "graphics/Shader.h"
10#include "graphics/shaders/mesh3d_hair_vert_spv.inc"
11#include "graphics/shaders/mesh3d_vert_spv.inc"
12#include "graphics/shaders/textured_vert_spv.inc"
15
16namespace eve::graphics::vulkan {
17namespace {
18
19void destroyCandidate(vkb::Device &device, GpuShader &candidate) {
20 if (candidate.swapchainPipeline) device->destroyPipeline(candidate.swapchainPipeline);
21 if (candidate.swapchainOpaquePipeline) device->destroyPipeline(candidate.swapchainOpaquePipeline);
22 if (candidate.offscreenPipeline) device->destroyPipeline(candidate.offscreenPipeline);
23 if (candidate.offscreenOpaquePipeline) device->destroyPipeline(candidate.offscreenOpaquePipeline);
24 if (candidate.hdrOffscreenPipeline) device->destroyPipeline(candidate.hdrOffscreenPipeline);
25 if (candidate.hdrOffscreenOpaquePipeline) device->destroyPipeline(candidate.hdrOffscreenOpaquePipeline);
26 if (candidate.mesh3dPipeline) device->destroyPipeline(candidate.mesh3dPipeline);
27 if (candidate.mesh3dOffscreenPipeline) device->destroyPipeline(candidate.mesh3dOffscreenPipeline);
28 if (candidate.mesh3dHdrOffscreenPipeline) device->destroyPipeline(candidate.mesh3dHdrOffscreenPipeline);
29 if (candidate.mesh3dXrayPipeline) device->destroyPipeline(candidate.mesh3dXrayPipeline);
30}
31
32} // namespace
33
35 const std::vector<uint32_t> &vertSpv,
36 const std::vector<uint32_t> &fragSpv) {
37 auto shaderIt = std::find_if(ownedShaders.begin(), ownedShaders.end(),
38 [&](const std::unique_ptr<Shader> &owned) {
39 return owned.get() == &shader;
40 });
41 if (shaderIt == ownedShaders.end() || !shader.gpuHandle)
42 return Result<void>::failure(Diagnostic::error(DiagnosticCode::StaleHandle, "shader is not a live resource owned by this Graphics instance", "shader", {},
43 "graphics.vulkan.shader_reload"));
44 if (fragSpv.empty())
45 return Result<void>::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "fragment SPIR-V must not be empty", "fragSpv", {},
46 "graphics.vulkan.shader_reload"));
47 if (fragSpv.front() != 0x07230203 ||
48 (!vertSpv.empty() && vertSpv.front() != 0x07230203))
49 return Result<void>::failure(Diagnostic::error(DiagnosticCode::ParseError, "SPIR-V magic mismatch", "source", {},
50 "graphics.vulkan.shader_reload"));
51
52 auto *current = static_cast<GpuShader *>(shader.gpuHandle);
53 auto resourceValidation = validateMeshResourceShaderReload(*current, vertSpv, fragSpv);
54 if (!resourceValidation) return resourceValidation;
55 auto gpuIt = std::find_if(ownedGpuShaders.begin(), ownedGpuShaders.end(),
56 [&](const std::unique_ptr<GpuShader> &owned) {
57 return owned.get() == current;
58 });
59 if (gpuIt == ownedGpuShaders.end())
60 return Result<void>::failure(Diagnostic::error(DiagnosticCode::StaleHandle, "shader GPU resource is no longer owned by this Graphics instance", "shader.gpuHandle", {},
61 "graphics.vulkan.shader_reload"));
62
63 std::vector<uint32_t> vert = vertSpv;
64 if (vert.empty()) {
65 if (!current->isMesh3D)
66 vert.assign(textured_vert_spv, textured_vert_spv + textured_vert_spv_count);
67 else if (current->isHair3D)
68 vert.assign(mesh3d_hair_vert_spv,
69 mesh3d_hair_vert_spv + mesh3d_hair_vert_spv_count);
70 else
71 vert.assign(mesh3d_vert_spv, mesh3d_vert_spv + mesh3d_vert_spv_count);
72 }
73
74 GpuShader candidate;
75 candidate.isMesh3D = current->isMesh3D;
76 candidate.isHair3D = current->isHair3D;
77 candidate.pipelineLayout = current->pipelineLayout;
78 candidate.owner = &shader;
79 try {
80 if (!candidate.isMesh3D) {
81 // Every render pass the shader owns a pipeline for must be replaced: the
82 // renderer binds the opaque variant for opaque batches, so rebuilding only
83 // the blended pipelines would keep drawing the previous fragment stage there.
84 candidate.swapchainPipeline =
85 createTexturedStylePipeline(vert, fragSpv, renderpass, candidate.pipelineLayout);
86 candidate.swapchainOpaquePipeline = createTexturedStylePipeline(
87 vert, fragSpv, renderpass, candidate.pipelineLayout, BlendMode::Opaque);
88 if (offscreenRenderPass) {
89 candidate.offscreenPipeline = createTexturedStylePipeline(
90 vert, fragSpv, offscreenRenderPass, candidate.pipelineLayout);
91 candidate.offscreenOpaquePipeline = createTexturedStylePipeline(
92 vert, fragSpv, offscreenRenderPass, candidate.pipelineLayout, BlendMode::Opaque);
93 }
94 if (hdrOffscreenRenderPass) {
95 candidate.hdrOffscreenPipeline = createTexturedStylePipeline(
96 vert, fragSpv, hdrOffscreenRenderPass, candidate.pipelineLayout);
97 candidate.hdrOffscreenOpaquePipeline = createTexturedStylePipeline(
98 vert, fragSpv, hdrOffscreenRenderPass, candidate.pipelineLayout, BlendMode::Opaque);
99 }
100 } else if (candidate.isHair3D) {
101 candidate.mesh3dPipeline = createMesh3DHairPipeline(
102 vert, fragSpv, candidate.pipelineLayout, activeScenePass(), activeSceneSamples());
103 } else {
104 candidate.mesh3dPipeline = createMesh3DStylePipeline(
105 vert, fragSpv, candidate.pipelineLayout, activeScenePass(), activeSceneSamples(), shader.meshBlend,
106 shader.meshDepthWrite, shader.meshDoubleSided, shader.meshRasterState());
107 candidate.mesh3dOffscreenPipeline = createMesh3DStylePipeline(
108 vert, fragSpv, candidate.pipelineLayout, offscreen3DRenderPass, vk::SampleCountFlagBits::e1,
109 shader.meshBlend, shader.meshDepthWrite, shader.meshDoubleSided, shader.meshRasterState());
110 candidate.mesh3dHdrOffscreenPipeline = createMesh3DStylePipeline(
111 vert, fragSpv, candidate.pipelineLayout, hdrOffscreen3DRenderPass, vk::SampleCountFlagBits::e1,
112 shader.meshBlend, shader.meshDepthWrite, shader.meshDoubleSided, shader.meshRasterState());
113 candidate.mesh3dXrayPipeline = createMesh3DXrayPipeline(
114 vert, fragSpv, candidate.pipelineLayout, activeScenePass(), activeSceneSamples());
115 }
116 } catch (const std::exception &error) {
117 destroyCandidate(device, candidate);
118 return Result<void>::failure(Diagnostic::error(DiagnosticCode::Failed, std::string("failed to prepare replacement pipeline: ") + error.what(), "pipeline", {},
119 "graphics.vulkan.shader_reload"));
120 } catch (...) {
121 destroyCandidate(device, candidate);
122 return Result<void>::failure(Diagnostic::error(DiagnosticCode::Failed, "failed to prepare replacement pipeline", "pipeline", {},
123 "graphics.vulkan.shader_reload"));
124 }
125
127 if (current->swapchainPipeline) device->destroyPipeline(current->swapchainPipeline);
128 if (current->swapchainOpaquePipeline) device->destroyPipeline(current->swapchainOpaquePipeline);
129 if (current->offscreenPipeline) device->destroyPipeline(current->offscreenPipeline);
130 if (current->offscreenOpaquePipeline) device->destroyPipeline(current->offscreenOpaquePipeline);
131 if (current->hdrOffscreenPipeline) device->destroyPipeline(current->hdrOffscreenPipeline);
132 if (current->hdrOffscreenOpaquePipeline) device->destroyPipeline(current->hdrOffscreenOpaquePipeline);
133 if (current->mesh3dPipeline) device->destroyPipeline(current->mesh3dPipeline);
134 if (current->mesh3dXrayPipeline) device->destroyPipeline(current->mesh3dXrayPipeline);
135 if (current->isMesh3D && !current->isHair3D) {
136 if (current->mesh3dOffscreenPipeline) device->destroyPipeline(current->mesh3dOffscreenPipeline);
137 if (current->mesh3dHdrOffscreenPipeline) device->destroyPipeline(current->mesh3dHdrOffscreenPipeline);
138 current->mesh3dOffscreenPipeline = candidate.mesh3dOffscreenPipeline;
139 current->mesh3dHdrOffscreenPipeline = candidate.mesh3dHdrOffscreenPipeline;
140 }
141 current->swapchainPipeline = candidate.swapchainPipeline;
142 current->swapchainOpaquePipeline = candidate.swapchainOpaquePipeline;
143 current->offscreenPipeline = candidate.offscreenPipeline;
144 current->offscreenOpaquePipeline = candidate.offscreenOpaquePipeline;
145 current->hdrOffscreenPipeline = candidate.hdrOffscreenPipeline;
146 current->hdrOffscreenOpaquePipeline = candidate.hdrOffscreenOpaquePipeline;
147 current->mesh3dPipeline = candidate.mesh3dPipeline;
148 current->mesh3dXrayPipeline = candidate.mesh3dXrayPipeline;
149 shader.setSpirv(std::move(vert), fragSpv);
150 return Result<void>::success();
151}
152
154 const std::string &) {
155 return Result<void>::failure(Diagnostic::error(DiagnosticCode::Unsupported, "WGSL shader replacement is unavailable on the Vulkan backend", "source", {},
156 "graphics.vulkan.shader_reload"));
157}
158
160 const std::string &fragGlsl) {
161 if (fragGlsl.empty())
162 return Result<void>::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "fragment GLSL must not be empty", "fragGlsl", {},
163 "graphics.vulkan.shader_reload"));
164 auto shaderIt = std::find_if(ownedShaders.begin(), ownedShaders.end(),
165 [&](const std::unique_ptr<Shader> &owned) {
166 return owned.get() == &shader;
167 });
168 if (shaderIt == ownedShaders.end() || !shader.gpuHandle)
169 return Result<void>::failure(Diagnostic::error(DiagnosticCode::StaleHandle, "shader is not a live resource owned by this Graphics instance", "shader", {},
170 "graphics.vulkan.shader_reload"));
171 auto *current = static_cast<GpuShader *>(shader.gpuHandle);
172 if (current->isHair3D)
173 return Result<void>::failure(Diagnostic::error(DiagnosticCode::Unsupported, "runtime GLSL replacement does not yet provide the hair vertex "
174 "contract; publish SPIR-V stages instead", "shader.kind", {},
175 "graphics.vulkan.shader_reload"));
176
177 Shader *candidate = nullptr;
178 try {
179 candidate = shader.getKind() == Shader::Kind::eMesh3D
180 ? newMeshShader(vertGlsl, fragGlsl)
181 : newShader(vertGlsl, fragGlsl);
182 } catch (const Exception &error) {
184 "graphics.vulkan.shader_reload"));
185 } catch (const std::exception &error) {
187 "graphics.vulkan.shader_reload"));
188 } catch (...) {
189 return Result<void>::failure(Diagnostic::error(DiagnosticCode::Failed, "GLSL compilation failed", "compiler", {},
190 "graphics.vulkan.shader_reload"));
191 }
192
193 auto replaced = replaceShaderFromSpv(shader, candidate->vertexSpirv(),
194 candidate->fragmentSpirv());
195 const bool released = releaseShader(candidate);
196 if (released) delete candidate;
197 if (!released && replaced.ok())
198 return Result<void>::failure(Diagnostic::error(DiagnosticCode::InvariantViolation, "temporary compiled shader could not be released", "candidate", {},
199 "graphics.vulkan.shader_reload"));
200 return replaced;
201}
202
203} // namespace eve::graphics::vulkan
ActiveSource owned
Stable, structured diagnostics shared by engine modules.
vkb::Device & device
vk::ShaderModule vert
std::string error
Definition Package.cpp:60
Shader * shader
double current
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
EVENGINE_API_FOUNDATION public API.
Definition Exception.h:13
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
Custom GPU program.
Definition Shader.h:39
Kind getKind() const
Definition Shader.h:63
const std::vector< uint32_t > & fragmentSpirv() const
Definition Shader.h:137
const std::vector< uint32_t > & vertexSpirv() const
Definition Shader.h:136
Shader * newShader(const std::string &vertGlsl, const std::string &fragGlsl) override
Creates a shader. @ownership Caller deletes unless documented otherwise.
void waitForSharedGpuResources()
Block until all in-flight GPU work (all frames) has completed.
Definition Graphics.cpp:885
Result< void > replaceShaderFromWgsl(Shader &shader, const std::string &vertWgsl, const std::string &fragWgsl) override
Replace shader from wgsl.
Shader * newMeshShader(const std::string &vertGlsl, const std::string &fragGlsl) override
Creates a mesh shader. @ownership Caller deletes unless documented otherwise.
Result< void > replaceShaderFromSpv(Shader &shader, const std::vector< uint32_t > &vertSpv, const std::vector< uint32_t > &fragSpv) override
Replace shader from spv.
Result< void > replaceShaderFromGlsl(Shader &shader, const std::string &vertGlsl, const std::string &fragGlsl) override
Replace shader from glsl.
bool releaseShader(Shader *shader) override
Release shader.
Result< void > validateMeshResourceShaderReload(const GpuShader &shader, std::span< const uint32_t > vertex, std::span< const uint32_t > fragment)
Validate mesh resource shader reload.
GpuShader public API.
Definition Graphics.h:322
vk::Pipeline hdrOffscreenOpaquePipeline
Definition Graphics.h:328
vk::Pipeline mesh3dHdrOffscreenPipeline
Definition Graphics.h:332
vk::PipelineLayout pipelineLayout
Definition Graphics.h:333