OpenGothic
Open source reimplementation of Gothic I and II
Loading...
Searching...
No Matches
drawcommands.cpp
Go to the documentation of this file.
1#include "drawcommands.h"
2
3#include <Tempest/Log>
4
9#include "shaders.h"
10
11#include "gothic.h"
12
13using namespace Tempest;
14
15static bool needtoReallocate(const StorageBuffer& b, const size_t desiredSz) {
16 return b.byteSize()<desiredSz || b.byteSize()>=2*desiredSz;
17 }
18
19
23
27
31
35
37 return bucketId==uint32_t(-1);
38 }
39
41 auto& opt = Gothic::inst().options();
42 if(!opt.doMeshShading)
43 return false;
44 if(Material::isTesselated(alpha) && type==DrawCommands::Landscape && Resources::device().properties().tesselationShader)
45 return false;
46 return true;
47 }
48
49
51 : owner(owner), buckets(buckets), clusters(clusters), scene(scene), vsmSupported(Shaders::isVsmSupported()) {
52 for(uint8_t v=0; v<SceneGlobals::V_Count; ++v) {
53 views[v].viewport = SceneGlobals::VisCamera(v);
54 }
55
56 if(vsmSupported) {
57 Tempest::DispatchIndirectCommand cmd = {2000,1,1};
58 vsmIndirectCmd = Resources::device().ssbo(&cmd, sizeof(cmd));
59 }
60 // vsmSwrImage = Resources::device().image2d(TextureFormat::R16, 4096, 4096);
61 // vsmSwrImage = Resources::device().image2d(TextureFormat::R32U, 4096, 4096);
62 }
63
66
67bool DrawCommands::isViewEnabled(SceneGlobals::VisCamera viewport) const {
68 if(viewport==SceneGlobals::V_Vsm && !(vsmSupported && scene.vsmEnabled))
69 return false;
70 if(viewport==SceneGlobals::V_Shadow0 && scene.shadowMap[0]->size()==Size(1,1))
71 return false;
72 if(viewport==SceneGlobals::V_Shadow1 && scene.shadowMap[1]->size()==Size(1,1))
73 return false;
74 return true;
75 }
76
77void DrawCommands::setBindings(Tempest::Encoder<CommandBuffer>& cmd, const DrawCmd& cx, SceneGlobals::VisCamera v) {
78 static const DrawBuckets::Bucket nullBk;
79
80 const auto bId = cx.bucketId;
81 const auto& bx = cx.isBindless() ? nullBk : buckets.buckets()[bId];
82
83 cmd.setBinding(L_Scene, scene.uboGlobal[v]);
84 cmd.setBinding(L_Payload, views[v].visClusters);
85 cmd.setBinding(L_Instance, owner.instanceSsbo());
86 cmd.setBinding(L_Bucket, buckets.ssbo());
87
88 if(cx.isBindless()) {
89 cmd.setBinding(L_Ibo, buckets.ibo());
90 cmd.setBinding(L_Vbo, buckets.vbo());
91 }
92 else if(bx.staticMesh!=nullptr) {
93 cmd.setBinding(L_Ibo, bx.staticMesh->ibo8);
94 cmd.setBinding(L_Vbo, bx.staticMesh->vbo);
95 }
96 else {
97 cmd.setBinding(L_Ibo, bx.animMesh->ibo8);
98 cmd.setBinding(L_Vbo, bx.animMesh->vbo);
99 }
100
101 if(v==SceneGlobals::V_Main || cx.isTextureInShadowPass()) {
102 if(cx.isBindless()) {
103 cmd.setBinding(L_Diffuse, buckets.textures());
104 }
105 else if(bx.mat.hasFrameAnimation()) {
106 uint64_t timeShift = 0;
107 auto frame = size_t((timeShift+scene.tickCount)/bx.mat.texAniFPSInv);
108 auto* t = bx.mat.frames[frame%bx.mat.frames.size()];
109 cmd.setBinding(L_Diffuse, *t);
110 }
111 else {
112 cmd.setBinding(L_Diffuse, *bx.mat.tex);
113 }
114 auto smp = SceneGlobals::isShadowView(v) ? Sampler::trillinear() : Sampler::anisotrophy();
115 cmd.setBinding(L_Sampler, smp);
116 }
117
118 if(v==SceneGlobals::V_Main && cx.isShadowmapRequired()) {
119 cmd.setBinding(L_Shadow0, *scene.shadowMap[0], Resources::shadowSampler());
120 cmd.setBinding(L_Shadow1, *scene.shadowMap[1], Resources::shadowSampler());
121 }
122
123 if(cx.type==Morph && cx.isBindless()) {
124 cmd.setBinding(L_MorphId, buckets.morphId());
125 cmd.setBinding(L_Morph, buckets.morph());
126 }
127 else if(cx.type==Morph && bx.staticMesh!=nullptr) {
128 cmd.setBinding(L_MorphId, *bx.staticMesh->morph.index);
129 cmd.setBinding(L_Morph, *bx.staticMesh->morph.samples);
130 }
131
132 if(v==SceneGlobals::V_Main && cx.isSceneInfoRequired()) {
133 cmd.setBinding(L_SceneClr, *scene.sceneColor, Sampler::bilinear(ClampMode::MirroredRepeat));
134 cmd.setBinding(L_GDepth, *scene.sceneDepth, Sampler::nearest (ClampMode::MirroredRepeat));
135 }
136
137 if(v==SceneGlobals::V_Vsm) {
138 cmd.setBinding(L_CmdOffsets, views[v].indirectCmd);
139 cmd.setBinding(L_VsmPages, *scene.vsmPageList);
140 cmd.setBinding(L_Lights, *scene.lights);
141 }
142 }
143
144uint16_t DrawCommands::commandId(const Material& m, Type type, uint32_t bucketId) {
145 const bool bindlessSys = Gothic::inst().options().doBindless;
146 const bool bindless = bindlessSys && !m.hasFrameAnimation();
147
148 auto pMain = Shaders::inst().materialPipeline(m, type, Shaders::T_Main, bindless);
149 auto pShadow = Shaders::inst().materialPipeline(m, type, Shaders::T_Shadow, bindless);
150 auto pVsm = Shaders::inst().materialPipeline(m, type, Shaders::T_Vsm, bindless);
151 auto pHiZ = Shaders::inst().materialPipeline(m, type, Shaders::T_Depth, bindless);
152 if(pMain==nullptr && pShadow==nullptr && pHiZ==nullptr)
153 return uint16_t(-1);
154
155 for(size_t i=0; i<cmd.size(); ++i) {
156 if(cmd[i].pMain!=pMain || cmd[i].pShadow!=pShadow || cmd[i].pHiZ!=pHiZ)
157 continue;
158 if(!bindless && cmd[i].bucketId!=bucketId)
159 continue;
160 return uint16_t(i);
161 }
162
163 auto ret = uint16_t(cmd.size());
164
165 DrawCmd cx;
166 cx.pMain = pMain;
167 cx.pShadow = pShadow;
168 cx.pVsm = pVsm;
169 cx.pHiZ = pHiZ;
170 cx.bucketId = bindless ? 0xFFFFFFFF : bucketId;
171 cx.type = type;
172 cx.alpha = m.alpha;
173 cmd.push_back(std::move(cx));
174 cmdDurtyBit = true;
175 return ret;
176 }
177
178void DrawCommands::addClusters(uint16_t cmdId, uint32_t meshletCount) {
179 cmdDurtyBit = true;
180 cmd[cmdId].maxPayload += meshletCount;
181 }
182
184 for(auto& v:views) {
185 Resources::recycle(std::move(v.indirectCmd));
186 Resources::recycle(std::move(v.visClusters));
187 Resources::recycle(std::move(v.vsmClusters));
188 }
189 cmdDurtyBit = true;
190 }
191
192void DrawCommands::commit(Encoder<CommandBuffer>& enc) {
193 if(!cmdDurtyBit)
194 return;
195 cmdDurtyBit = false;
196
197 ord.resize(cmd.size());
198 for(size_t i=0; i<cmd.size(); ++i)
199 ord[i] = &cmd[i];
200 std::sort(ord.begin(), ord.end(), [](const DrawCmd* l, const DrawCmd* r){
201 return l->alpha < r->alpha;
202 });
203
204 size_t totalPayload = 0;
205 bool layChg = false;
206 for(auto& i:cmd) {
207 layChg |= (i.firstPayload != uint32_t(totalPayload));
208 i.firstPayload = uint32_t(totalPayload);
209 totalPayload += i.maxPayload;
210 }
211
212 totalPayload = (totalPayload + 0xFF) & ~size_t(0xFF);
213 const size_t visClustersSz = totalPayload*sizeof(uint32_t)*4;
214
215 auto& v = views[SceneGlobals::V_Main];
216 bool cmdChg = needtoReallocate(v.indirectCmd, sizeof(IndirectCmd)*cmd.size());
217 bool visChg = needtoReallocate(v.visClusters, visClustersSz);
218 this->maxPayload = totalPayload;
219
220 if(!layChg && !visChg) {
221 return;
222 }
223
224 std::vector<IndirectCmd> cx(cmd.size());
225 for(size_t i=0; i<cmd.size(); ++i) {
226 auto mesh = cmd[i].isMeshShader();
227
228 cx[i].vertexCount = PackedMesh::MaxInd;
229 cx[i].writeOffset = cmd[i].firstPayload;
230 cx[i].firstVertex = mesh ? 1 : 0;
231 cx[i].firstInstance = mesh ? 1 : 0;
232 }
233
234 auto& device = Resources::device();
235 for(auto& v:views) {
236 if(!isViewEnabled(v.viewport))
237 continue;
238
239 if(visChg) {
240 const size_t vsmMax = 1024*1024*4*4; // arbitrary: ~1k clusters per page
241 const size_t size = v.viewport==SceneGlobals::V_Vsm ? vsmMax : visClustersSz;
242 Resources::recycle(std::move(v.visClusters));
243 v.visClusters = device.ssbo(nullptr, size);
244 }
245 if(visChg && v.viewport==SceneGlobals::V_Vsm) {
246 Resources::recycle(std::move(v.vsmClusters));
247 v.vsmClusters = device.ssbo(nullptr, v.visClusters.byteSize());
248 }
249 if(cmdChg) {
250 Resources::recycle(std::move(v.indirectCmd));
251 v.indirectCmd = device.ssbo(cx.data(), sizeof(IndirectCmd)*cx.size());
252 }
253 else if(layChg) {
254 auto staging = device.ssbo(BufferHeap::Upload, cx.data(), sizeof(IndirectCmd)*cx.size());
255
256 enc.setBinding(0, v.indirectCmd);
257 enc.setBinding(1, staging);
258 enc.setPipeline(Shaders::inst().copyBuf);
259 enc.dispatchThreads(staging.byteSize()/sizeof(uint32_t));
260
261 Resources::recycle(std::move(staging));
262 }
263 }
264 }
265
266void DrawCommands::visibilityPass(Encoder<CommandBuffer>& cmd, int pass) {
267 static bool freeze = false;
268 if(freeze)
269 return;
270
271 cmd.setFramebuffer({});
272 if(pass==0) {
273 for(auto& v:views) {
274 if(this->cmd.empty())
275 continue;
276 if(!isViewEnabled(v.viewport))
277 continue;
278 const uint32_t isMeshShader = (Gothic::options().doMeshShading ? 1 : 0);
279 cmd.setBinding(T_Indirect, v.indirectCmd);
280 cmd.setPushData(&isMeshShader, sizeof(isMeshShader));
281 cmd.setPipeline(Shaders::inst().clusterInit);
282 cmd.dispatchThreads(this->cmd.size());
283 }
284 }
285
286 for(uint8_t v=0; v<SceneGlobals::V_Count; ++v) {
287 const auto viewport = SceneGlobals::VisCamera(v);
288 if(viewport==SceneGlobals::V_HiZ && pass!=0)
289 continue;
290 if(viewport!=SceneGlobals::V_HiZ && pass==0)
291 continue;
292 if(viewport==SceneGlobals::V_Vsm)
293 continue;
294 if(!isViewEnabled(viewport))
295 continue;
296
297 struct Push { uint32_t firstMeshlet; uint32_t meshletCount; float znear; } push = {};
298 push.firstMeshlet = 0;
299 push.meshletCount = uint32_t(clusters.size());
300 push.znear = scene.znear;
301
302 auto* pso = &Shaders::inst().visibilityPassSh;
303 if(viewport==SceneGlobals::V_Main)
305 else if(viewport==SceneGlobals::V_HiZ)
307
308 cmd.setBinding(T_Scene, scene.uboGlobal[viewport]);
309 cmd.setBinding(T_Payload, views[viewport].visClusters);
310 cmd.setBinding(T_Instance, owner.instanceSsbo());
311 cmd.setBinding(T_Bucket, buckets.ssbo());
312 cmd.setBinding(T_Indirect, views[viewport].indirectCmd);
313 cmd.setBinding(T_Clusters, clusters.ssbo());
314 cmd.setBinding(T_HiZ, *scene.hiZ);
315 cmd.setPushData(push);
316 cmd.setPipeline(*pso);
317 cmd.dispatchThreads(push.meshletCount);
318 }
319 }
320
321void DrawCommands::visibilityVsm(Encoder<CommandBuffer>& cmd) {
322 auto& shaders = Shaders::inst();
323
324 struct Push { uint32_t meshletCount; } push = {};
325 push.meshletCount = uint32_t(clusters.size());
326
327 auto& view = views[SceneGlobals::V_Vsm];
328 cmd.setBinding(T_Scene, scene.uboGlobal[SceneGlobals::V_Vsm]);
329 cmd.setBinding(T_Payload, view.vsmClusters); //unsorted clusters
330 cmd.setBinding(T_Instance, owner.instanceSsbo());
331 cmd.setBinding(T_Bucket, buckets.ssbo());
332 cmd.setBinding(T_Indirect, view.indirectCmd);
333 cmd.setBinding(T_Clusters, clusters.ssbo());
334 cmd.setBinding(T_Lights, *scene.lights);
335 cmd.setBinding(T_HiZ, *scene.vsmPageHiZ);
336 cmd.setBinding(T_VsmPages, *scene.vsmPageList);
337 cmd.setBinding(9, scene.vsmDbg);
338 cmd.setPushData(&push, sizeof(push));
339 cmd.setPipeline(shaders.vsmVisibilityPass);
340 cmd.dispatchThreads(push.meshletCount, size_t(scene.vsmPageTbl->d() + 1));
341
342 cmd.setBinding(1, view.vsmClusters);
343 cmd.setBinding(2, view.visClusters);
344 cmd.setBinding(3, view.indirectCmd);
345 cmd.setBinding(4, *scene.vsmPageList);
346 cmd.setBinding(5, vsmIndirectCmd);
347 cmd.setPipeline(shaders.vsmPackDraw0);
348 cmd.dispatch(1);
349
350 cmd.setBinding(1, view.vsmClusters);
351 cmd.setBinding(2, view.visClusters);
352 cmd.setBinding(3, view.indirectCmd);
353 cmd.setBinding(4, *scene.vsmPageList);
354 cmd.setPipeline(shaders.vsmPackDraw1);
355 cmd.dispatch(8096); // TODO: indirect
356 // cmd.dispatchIndirect(vsmIndirectCmd, 0);
357 }
358
359void DrawCommands::drawVsm(Tempest::Encoder<Tempest::CommandBuffer>& cmd) {
360 // return;
361 struct Push { uint32_t commandId; } push = {};
362
363 auto viewId = SceneGlobals::V_Vsm;
364 auto& view = views[viewId];
365
366 for(size_t i=0; i<ord.size(); ++i) {
367 auto& cx = *ord[i];
368 if(cx.alpha!=Material::Solid && cx.alpha!=Material::AlphaTest)
369 continue;
370 if(cx.pVsm==nullptr)
371 continue;
372
373 auto id = size_t(std::distance(this->cmd.data(), &cx));
374 push.commandId = uint32_t(id);
375
376 // cmd.setUniforms(*cx.pVsm, desc[viewId], &push, sizeof(push));
377 setBindings(cmd, cx, viewId);
378 cmd.setPushData(push);
379 cmd.setPipeline(*cx.pVsm);
380 if(cx.isMeshShader())
381 cmd.dispatchMeshIndirect(view.indirectCmd, sizeof(IndirectCmd)*id + sizeof(uint32_t)); else
382 cmd.drawIndirect(view.indirectCmd, sizeof(IndirectCmd)*id);
383 }
384
385 if(false) {
386 struct Push { uint32_t meshletCount; } push = {};
387 push.meshletCount = uint32_t(clusters.size());
388
389 cmd.setFramebuffer({});
390 cmd.setBinding(0, *scene.vsmPageData);
391 cmd.setBinding(1, scene.uboGlobal[SceneGlobals::V_Vsm]);
392 cmd.setBinding(2, *scene.vsmPageList);
393 cmd.setBinding(3, clusters.ssbo());
394 cmd.setBinding(4, owner.instanceSsbo());
395 cmd.setBinding(5, buckets.ibo());
396 cmd.setBinding(6, buckets.vbo());
397 cmd.setBinding(7, buckets.textures());
398 cmd.setBinding(8, Sampler::bilinear());
399 cmd.setPushData(&push, sizeof(push));
400 cmd.setPipeline(Shaders::inst().vsmRendering);
401 // const auto sz = Shaders::inst().vsmRendering.workGroupSize();
402 cmd.dispatch(1024u);
403 }
404 }
405
406void DrawCommands::drawHiZ(Tempest::Encoder<Tempest::CommandBuffer>& cmd) {
407 // return;
408 struct Push { uint32_t firstMeshlet; uint32_t meshletCount; } push = {};
409
410 auto viewId = SceneGlobals::V_HiZ;
411 auto& view = views[viewId];
412
413 for(size_t i=0; i<ord.size(); ++i) {
414 auto& cx = *ord[i];
415 if(cx.alpha!=Material::Solid && cx.alpha!=Material::AlphaTest)
416 continue;
417 if(cx.type!=Landscape && cx.type!=Static)
418 continue;
419 if(cx.pHiZ==nullptr)
420 continue;
421
422 auto id = size_t(std::distance(this->cmd.data(), &cx));
423 push.firstMeshlet = cx.firstPayload;
424 push.meshletCount = cx.maxPayload;
425
426 setBindings(cmd, cx, viewId);
427 cmd.setPushData(push);
428 cmd.setPipeline(*cx.pHiZ);
429 if(cx.isMeshShader())
430 cmd.dispatchMeshIndirect(view.indirectCmd, sizeof(IndirectCmd)*id + sizeof(uint32_t)); else
431 cmd.drawIndirect(view.indirectCmd, sizeof(IndirectCmd)*id);
432 }
433 }
434
435void DrawCommands::drawCommon(Tempest::Encoder<Tempest::CommandBuffer>& cmd, SceneGlobals::VisCamera viewId, Material::AlphaFunc func) {
436 struct Push { uint32_t firstMeshlet; uint32_t meshletCount; } push = {};
437
438 auto b = std::lower_bound(ord.begin(), ord.end(), func, [](const DrawCmd* l, Material::AlphaFunc f){
439 return l->alpha < f;
440 });
441 auto e = std::upper_bound(ord.begin(), ord.end(), func, [](Material::AlphaFunc f, const DrawCmd* r){
442 return f < r->alpha;
443 });
444
445 auto& view = views[viewId];
446 for(auto i=b; i!=e; ++i) {
447 auto& cx = **i;
448 if(cx.alpha!=func)
449 continue;
450
451 if(cx.maxPayload==0)
452 continue;
453
454 const RenderPipeline* pso = nullptr;
455 switch(viewId) {
459 pso = cx.pShadow;
460 break;
462 pso = cx.pMain;
463 break;
466 break;
467 }
468 if(pso==nullptr)
469 continue;
470
471 auto id = size_t(std::distance(this->cmd.data(), &cx));
472 push.firstMeshlet = cx.firstPayload;
473 push.meshletCount = cx.maxPayload;
474
475 setBindings(cmd, cx, viewId);
476 cmd.setPushData(push);
477 cmd.setPipeline(*pso);
478 if(cx.isMeshShader())
479 cmd.dispatchMeshIndirect(view.indirectCmd, sizeof(IndirectCmd)*id + sizeof(uint32_t)); else
480 cmd.drawIndirect(view.indirectCmd, sizeof(IndirectCmd)*id);
481 }
482 }
Tempest::DescriptorArray ibo
Definition drawbuckets.h:82
Tempest::DescriptorArray morphId
Definition drawbuckets.h:83
auto buckets() -> const std::vector< Bucket > &
auto ssbo() const -> const Tempest::StorageBuffer &
Definition drawbuckets.h:47
auto & textures() const
Definition drawbuckets.h:50
Tempest::DescriptorArray vbo
Definition drawbuckets.h:81
Tempest::DescriptorArray morph
Definition drawbuckets.h:84
auto ssbo() -> Tempest::StorageBuffer &
size_t size() const
void addClusters(uint16_t cmdId, uint32_t meshletCount)
void visibilityVsm(Tempest::Encoder< Tempest::CommandBuffer > &cmd)
DrawCommands(VisualObjects &owner, DrawBuckets &buckets, DrawClusters &clusters, const SceneGlobals &scene)
uint16_t commandId(const Material &m, Type type, uint32_t bucketId)
void drawVsm(Tempest::Encoder< Tempest::CommandBuffer > &cmd)
void drawHiZ(Tempest::Encoder< Tempest::CommandBuffer > &cmd)
void commit(Tempest::Encoder< Tempest::CommandBuffer > &cmd)
void drawCommon(Tempest::Encoder< Tempest::CommandBuffer > &cmd, SceneGlobals::VisCamera viewId, Material::AlphaFunc func)
void resetRendering()
void visibilityPass(Tempest::Encoder< Tempest::CommandBuffer > &cmd, int pass)
static auto options() -> const Options &
Definition gothic.cpp:496
static Gothic & inst()
Definition gothic.cpp:249
bool isShadowmapRequired() const
Definition material.h:47
bool isForwardShading() const
Definition material.h:45
bool isTextureInShadowPass() const
Definition material.h:48
bool isTesselated() const
Definition material.h:44
bool hasFrameAnimation() const
Definition material.h:42
@ AlphaTest
Definition material.h:19
AlphaFunc alpha
Definition material.h:30
bool isSceneInfoRequired() const
Definition material.h:46
static const Tempest::Sampler & shadowSampler()
static Tempest::Device & device()
Definition resources.h:83
static void recycle(Tempest::DescriptorArray &&arr)
const Tempest::StorageBuffer * vsmPageList
const Tempest::Texture2d * shadowMap[2]
Tempest::StorageImage vsmDbg
Tempest::StorageBuffer uboGlobal[V_Count]
const Tempest::Texture2d * sceneColor
const Tempest::Texture2d * hiZ
const Tempest::StorageImage * vsmPageTbl
const Tempest::Texture2d * vsmPageData
static bool isShadowView(VisCamera v)
uint64_t tickCount
const Tempest::StorageBuffer * lights
const Tempest::StorageImage * vsmPageHiZ
const Tempest::Texture2d * sceneDepth
static Shaders & inst()
Definition shaders.cpp:39
const Tempest::RenderPipeline * materialPipeline(const Material &desc, DrawCommands::Type t, PipelineType pt, bool bindless) const
Definition shaders.cpp:338
Tempest::ComputePipeline visibilityPassSh
Definition shaders.h:73
@ T_Main
Definition shaders.h:24
@ T_Shadow
Definition shaders.h:22
@ T_Vsm
Definition shaders.h:23
@ T_Depth
Definition shaders.h:21
Tempest::ComputePipeline visibilityPassHiZCr
Definition shaders.h:73
Tempest::ComputePipeline visibilityPassHiZ
Definition shaders.h:73
Tempest::ComputePipeline clusterInit
Definition shaders.h:72
auto instanceSsbo() const -> const Tempest::StorageBuffer &
static bool needtoReallocate(const StorageBuffer &b, const size_t desiredSz)
bool isSceneInfoRequired() const
const Tempest::RenderPipeline * pShadow
bool isTextureInShadowPass() const
const Tempest::RenderPipeline * pMain
const Tempest::RenderPipeline * pHiZ
bool isMeshShader() const
bool isShadowmapRequired() const
const Tempest::RenderPipeline * pVsm
Material::AlphaFunc alpha
bool isForwardShading() const