Loading...
Searching...
No Matches
GeometryToBufferStrategies.hpp
1#pragma once
2#include <Crousti/GpuUtils.hpp>
3#include <score/tools/std/String.hpp>
4#include <Gfx/Graph/Node.hpp>
5#include <Gfx/Graph/NodeRenderer.hpp>
6
7#include <ossia/detail/pod_vector.hpp>
8
9#include <boost/container/vector.hpp>
10
11#include <avnd/introspection/input.hpp>
12#include <avnd/introspection/output.hpp>
13#include <halp/controls.hpp>
14#include <halp/geometry.hpp>
15#include <halp/meta.hpp>
16#include <halp/texture.hpp>
17
18#include <optional>
19#include <variant>
20namespace Threedim
21{
22[[nodiscard]] constexpr int32_t attributeFormatSize(halp::attribute_format fmt) noexcept
23{
24 using namespace halp;
25 switch(fmt)
26 {
27 case attribute_format::float4:
28 return 4 * sizeof(float);
29 case attribute_format::float3:
30 return 3 * sizeof(float);
31 case attribute_format::float2:
32 return 2 * sizeof(float);
33 case attribute_format::float1:
34 return sizeof(float);
35
36 case attribute_format::uint4:
37 return 4 * sizeof(uint32_t);
38 case attribute_format::uint3:
39 return 3 * sizeof(uint32_t);
40 case attribute_format::uint2:
41 return 2 * sizeof(uint32_t);
42 case attribute_format::uint1:
43 return sizeof(uint32_t);
44
45 case attribute_format::sint4:
46 return 4 * sizeof(int32_t);
47 case attribute_format::sint3:
48 return 3 * sizeof(int32_t);
49 case attribute_format::sint2:
50 return 2 * sizeof(int32_t);
51 case attribute_format::sint1:
52 return sizeof(int32_t);
53
54 case attribute_format::unormbyte4:
55 return 4 * sizeof(uint8_t);
56 case attribute_format::unormbyte2:
57 return 2 * sizeof(uint8_t);
58 case attribute_format::unormbyte1:
59 return sizeof(uint8_t);
60
61 case attribute_format::half4:
62 return 4 * sizeof(uint16_t);
63 case attribute_format::half3:
64 return 3 * sizeof(uint16_t);
65 case attribute_format::half2:
66 return 2 * sizeof(uint16_t);
67 case attribute_format::half1:
68 return sizeof(uint16_t);
69
70 case attribute_format::ushort4:
71 return 4 * sizeof(uint16_t);
72 case attribute_format::ushort3:
73 return 3 * sizeof(uint16_t);
74 case attribute_format::ushort2:
75 return 2 * sizeof(uint16_t);
76 case attribute_format::ushort1:
77 return sizeof(uint16_t);
78
79 case attribute_format::sshort4:
80 return 4 * sizeof(int16_t);
81 case attribute_format::sshort3:
82 return 3 * sizeof(int16_t);
83 case attribute_format::sshort2:
84 return 2 * sizeof(int16_t);
85 case attribute_format::sshort1:
86 return sizeof(int16_t);
87
88 default:
89 return 0;
90 }
91 return 0;
92}
93
94[[nodiscard]] constexpr int32_t
95attributeFormatComponents(halp::attribute_format fmt) noexcept
96{
97 using namespace halp;
98 switch(fmt)
99 {
100 case attribute_format::float4:
101 case attribute_format::uint4:
102 case attribute_format::sint4:
103 case attribute_format::unormbyte4:
104 case attribute_format::half4:
105 case attribute_format::ushort4:
106 case attribute_format::sshort4:
107 return 4;
108 case attribute_format::float3:
109 case attribute_format::uint3:
110 case attribute_format::sint3:
111 case attribute_format::half3:
112 case attribute_format::ushort3:
113 case attribute_format::sshort3:
114 return 3;
115 case attribute_format::float2:
116 case attribute_format::uint2:
117 case attribute_format::sint2:
118 case attribute_format::unormbyte2:
119 case attribute_format::half2:
120 case attribute_format::ushort2:
121 case attribute_format::sshort2:
122 return 2;
123 case attribute_format::float1:
124 case attribute_format::uint1:
125 case attribute_format::sint1:
126 case attribute_format::unormbyte1:
127 case attribute_format::half1:
128 case attribute_format::ushort1:
129 case attribute_format::sshort1:
130 return 1;
131
132 default:
133 return 0;
134 }
135 return 0;
136}
137
138[[nodiscard]] constexpr bool isFloatFormat(halp::attribute_format fmt) noexcept
139{
140 const int f = static_cast<int>(fmt);
141 return (f >= int(halp::attribute_format::float4)
142 && f <= int(halp::attribute_format::float1))
143 || (f >= int(halp::attribute_format::half4)
144 && f <= int(halp::attribute_format::half1));
145}
146
147//=============================================================================
148// Attribute Lookup Result
149//=============================================================================
150
152{
153 const halp::geometry_attribute* attribute{};
154 const halp::geometry_binding* binding{};
155 const halp::geometry_input* input{};
156 const halp::geometry_gpu_buffer* buffer{};
157 int32_t attribute_size{};
158 int32_t binding_index{};
159
160 [[nodiscard]] bool valid() const noexcept { return attribute != nullptr; }
161
162 [[nodiscard]] bool canDirectReference() const noexcept
163 {
164 if(!valid())
165 return false;
166 return attribute->byte_offset == 0 && attribute_size == binding->stride;
167 }
168};
169
170[[nodiscard]] inline std::optional<attribute_lookup> findAttribute(
171 const halp::dynamic_gpu_geometry& mesh, halp::attribute_location location) noexcept
172{
173 for(const auto& attr : mesh.attributes)
174 {
175 if(attr.location == location)
176 {
177 const auto binding_idx = attr.binding;
178 if(binding_idx < 0 || binding_idx >= static_cast<int>(mesh.bindings.size()))
179 {
180 qDebug() << "GeometryExtraction: Invalid binding index" << binding_idx;
181 return std::nullopt;
182 }
183
184 if(binding_idx >= static_cast<int>(mesh.input.size()))
185 {
186 qDebug() << "GeometryExtraction: Missing input for binding" << binding_idx;
187 return std::nullopt;
188 }
189
190 const auto& inp = mesh.input[binding_idx];
191 if(inp.buffer < 0 || inp.buffer >= static_cast<int>(mesh.buffers.size()))
192 {
193 qDebug() << "GeometryExtraction: Invalid buffer index" << inp.buffer;
194 return std::nullopt;
195 }
196
197 return attribute_lookup{
198 .attribute = &attr,
199 .binding = &mesh.bindings[binding_idx],
200 .input = &inp,
201 .buffer = &mesh.buffers[inp.buffer],
202 .attribute_size = attributeFormatSize(attr.format),
203 .binding_index = binding_idx};
204 }
205 }
206 return std::nullopt;
207}
208
209[[nodiscard]] inline std::optional<attribute_lookup>
210findAttribute(const halp::dynamic_gpu_geometry& mesh, int index) noexcept
211{
212 if(index < 0 || index >= mesh.attributes.size())
213 return {};
214
215 const auto& attr = mesh.attributes[index];
216 const auto binding_idx = attr.binding;
217 if(binding_idx < 0 || binding_idx >= static_cast<int>(mesh.bindings.size()))
218 {
219 qDebug() << "GeometryExtraction: Invalid binding index" << binding_idx;
220 return std::nullopt;
221 }
222
223 if(binding_idx >= static_cast<int>(mesh.input.size()))
224 {
225 qDebug() << "GeometryExtraction: Missing input for binding" << binding_idx;
226 return std::nullopt;
227 }
228
229 const auto& inp = mesh.input[binding_idx];
230 if(inp.buffer < 0 || inp.buffer >= static_cast<int>(mesh.buffers.size()))
231 {
232 qDebug() << "GeometryExtraction: Invalid buffer index" << inp.buffer;
233 return std::nullopt;
234 }
235
236 return attribute_lookup{
237 .attribute = &attr,
238 .binding = &mesh.bindings[binding_idx],
239 .input = &inp,
240 .buffer = &mesh.buffers[inp.buffer],
241 .attribute_size = attributeFormatSize(attr.format),
242 .binding_index = binding_idx};
243 return std::nullopt;
244}
246{
247 QRhiBuffer* buffer{};
248 int64_t offset{};
249 int64_t size{};
250
251 [[nodiscard]] bool valid() const noexcept { return buffer != nullptr && size > 0; }
252};
253
255{
256public:
257 bool init(
258 const score::gfx::RenderState& renderState, QRhi& rhi,
259 const halp::dynamic_gpu_geometry& mesh, int buffer, int byte_offset, int byte_size)
260 {
261 m_buffer = static_cast<QRhiBuffer*>(mesh.buffers[buffer].handle);
262 m_offset = byte_offset;
263 m_size = byte_size;
264 assert(m_buffer->size() >= byte_size + byte_offset);
265
266 if(!m_buffer)
267 {
268 qDebug() << "DirectBufferReferenceStrategy: Null buffer handle";
269 return false;
270 }
271 return true;
272 }
273
274 void
275 update(QRhi&, const halp::dynamic_gpu_geometry&, const attribute_lookup& lookup, bool)
276 {
277 }
278
279 void release() noexcept
280 {
281 m_buffer = nullptr;
282 m_offset = 0;
283 m_size = 0;
284 }
285
286 void runCompute(QRhi&, QRhiCommandBuffer&, QRhiResourceUpdateBatch*&) { }
287
288 [[nodiscard]] gpu_buffer_view output() const noexcept
289 {
290 return {
291 .buffer = m_buffer,
292 .offset = m_offset,
293 .size = m_size,
294 };
295 }
296
297 [[nodiscard]] static constexpr bool needsCompute() noexcept { return false; }
298
299private:
300 QRhiBuffer* m_buffer{};
301 int64_t m_offset{};
302 int64_t m_size{};
303};
304
306{
307public:
308 bool init(
309 const score::gfx::RenderState& renderState, QRhi& rhi,
310 const halp::dynamic_gpu_geometry& mesh, const attribute_lookup& lookup,
311 bool /*padToVec4*/)
312 {
313 m_buffer = static_cast<QRhiBuffer*>(lookup.buffer->handle);
314 m_offset = lookup.input->byte_offset;
315 m_size = static_cast<int64_t>(lookup.attribute_size) * mesh.vertices;
316
317 if(!m_buffer)
318 {
319 qDebug() << "DirectReferenceStrategy: Null buffer handle";
320 return false;
321 }
322 return true;
323 }
324
325 void update(
326 QRhi& /*rhi*/, const halp::dynamic_gpu_geometry& mesh,
327 const attribute_lookup& lookup, bool /*padToVec4*/)
328 {
329 m_buffer = static_cast<QRhiBuffer*>(lookup.buffer->handle);
330 m_offset = lookup.input->byte_offset;
331 m_size = static_cast<int64_t>(lookup.attribute_size) * mesh.vertices;
332 }
333
334 void release() noexcept
335 {
336 // We don't own the buffer
337 m_buffer = nullptr;
338 m_offset = 0;
339 m_size = 0;
340 }
341
342 void runCompute(QRhi&, QRhiCommandBuffer&, QRhiResourceUpdateBatch*&) { }
343
344 [[nodiscard]] gpu_buffer_view output() const noexcept
345 {
346 return {
347 .buffer = m_buffer,
348 .offset = m_offset,
349 .size = m_size,
350 };
351 }
352
353 [[nodiscard]] static constexpr bool needsCompute() noexcept { return false; }
354
355private:
356 QRhiBuffer* m_buffer{};
357 int64_t m_offset{};
358 int64_t m_size{};
359};
360
362{
363public:
364 bool init(
365 const score::gfx::RenderState& renderState, QRhi& rhi,
366 const halp::dynamic_gpu_geometry& mesh, const attribute_lookup& lookup,
367 bool padToVec4)
368 {
369 m_vertexCount = mesh.vertices;
370 m_srcStride = lookup.binding->stride;
371 m_srcOffset = lookup.attribute->byte_offset
372 + static_cast<int32_t>(lookup.input->byte_offset);
373 m_elementCount = attributeFormatComponents(lookup.attribute->format);
374 m_padToVec4
375 = padToVec4 && m_elementCount < 4 && isFloatFormat(lookup.attribute->format);
376
377 const int32_t outputComponents = m_padToVec4 ? 4 : m_elementCount;
378 m_outputComponents = outputComponents;
379 m_outputSize
380 = static_cast<int64_t>(m_vertexCount) * outputComponents * sizeof(float);
381
382 if(m_outputSize == 0)
383 {
384 qDebug() << "ComputeExtractionStrategy: Zero output size";
385 return false;
386 }
387
388 // Create output buffer
389 m_outputBuffer = rhi.newBuffer(
390 QRhiBuffer::Static, QRhiBuffer::StorageBuffer | QRhiBuffer::VertexBuffer,
391 static_cast<quint32>(m_outputSize));
392 m_outputBuffer->setName("ComputeExtractionStrategy::m_outputBuffer");
393
394 if(!m_outputBuffer || !m_outputBuffer->create())
395 {
396 qDebug() << "ComputeExtractionStrategy: Failed to create output buffer";
397 return false;
398 }
399
400 m_srcBuffer = static_cast<QRhiBuffer*>(lookup.buffer->handle);
401 if(!m_srcBuffer)
402 {
403 qDebug() << "ComputeExtractionStrategy: Null source buffer";
404 return false;
405 }
406
407 return createPipeline(renderState, rhi);
408 }
409
410 void update(
411 QRhi& rhi, const halp::dynamic_gpu_geometry& mesh, const attribute_lookup& lookup,
412 bool padToVec4)
413 {
414 m_srcBuffer = static_cast<QRhiBuffer*>(lookup.buffer->handle);
415 m_srcStride = lookup.binding->stride;
416 m_srcOffset = lookup.attribute->byte_offset
417 + static_cast<int32_t>(lookup.input->byte_offset);
418
419 const bool newPadToVec4
420 = padToVec4 && m_elementCount < 4 && isFloatFormat(lookup.attribute->format);
421 const int32_t newOutputComponents = newPadToVec4 ? 4 : m_elementCount;
422 const int64_t newSize
423 = static_cast<int64_t>(mesh.vertices) * newOutputComponents * sizeof(float);
424
425 // Check if buffer needs resize
426 if(newSize != m_outputSize || mesh.vertices != m_vertexCount
427 || newPadToVec4 != m_padToVec4)
428 {
429 m_vertexCount = mesh.vertices;
430 m_padToVec4 = newPadToVec4;
431 m_outputComponents = newOutputComponents;
432 m_outputSize = newSize;
433
434 if(m_outputSize > 0)
435 {
436 m_outputBuffer->setSize(static_cast<quint32>(m_outputSize));
437 m_outputBuffer->create();
438 }
439 }
440
441 // Rebind if source buffer changed
442 if(m_srb)
443 {
444 m_srb->setBindings({
445 QRhiShaderResourceBinding::bufferLoad(
446 0, QRhiShaderResourceBinding::ComputeStage, m_srcBuffer),
447 QRhiShaderResourceBinding::bufferStore(
448 1, QRhiShaderResourceBinding::ComputeStage, m_outputBuffer),
449 });
450 m_srb->create();
451 }
452
453 m_dirty = true;
454 }
455
456 void release() noexcept
457 {
458 delete m_uniformBuffer;
459 m_uniformBuffer = nullptr;
460
461 delete m_pipeline;
462 m_pipeline = nullptr;
463
464 delete m_srb;
465 m_srb = nullptr;
466
467 delete m_outputBuffer;
468 m_outputBuffer = nullptr;
469
470 m_srcBuffer = nullptr;
471 }
472
473 void runCompute(QRhi& rhi, QRhiCommandBuffer& cb, QRhiResourceUpdateBatch*& res)
474 {
475 if(!m_dirty || m_vertexCount == 0 || !m_pipeline)
476 return;
477
478 struct alignas(16) Params
479 {
480 uint32_t vertexCount;
481 uint32_t srcStrideBytes;
482 uint32_t srcOffsetBytes;
483 uint32_t elementCount;
484 uint32_t padToVec4;
485 uint32_t _pad[3]; // Padding to maintain alignment
486 } params{
487 static_cast<uint32_t>(m_vertexCount),
488 static_cast<uint32_t>(m_srcStride),
489 static_cast<uint32_t>(m_srcOffset),
490 static_cast<uint32_t>(m_elementCount),
491 m_padToVec4 ? 1u : 0u,
492 {0, 0, 0}};
493
494 res->updateDynamicBuffer(m_uniformBuffer, 0, sizeof(params), &params);
495
496 cb.beginComputePass(res);
497 cb.setComputePipeline(m_pipeline);
498 cb.setShaderResources(m_srb);
499
500 const int workgroups = (m_vertexCount + 255) / 256;
501 cb.dispatch(workgroups, 1, 1);
502
503 cb.endComputePass();
504
505 // Get new resource batch after compute pass
506 res = rhi.nextResourceUpdateBatch();
507
508 m_dirty = false;
509 }
510
511 [[nodiscard]] gpu_buffer_view output() const noexcept
512 {
513 return {
514 .buffer = m_outputBuffer,
515 .offset = 0,
516 .size = m_outputSize,
517 };
518 }
519
520 [[nodiscard]] static constexpr bool needsCompute() noexcept { return true; }
521
522private:
523 bool createPipeline(const score::gfx::RenderState& renderState, QRhi& rhi)
524 {
525 static const QString shaderCode = QStringLiteral(R"(#version 450
526
527layout(local_size_x = 256) in;
528
529layout(std140, binding = 0) uniform Params {
530 uint vertexCount;
531 uint srcStrideBytes;
532 uint srcOffsetBytes;
533 uint elementCount;
534 uint padToVec4;
535};
536
537layout(std430, binding = 1) readonly buffer SrcBuffer {
538 uint src_data[];
539};
540
541layout(std430, binding = 2) writeonly buffer DstBuffer {
542 uint dst_data[];
543};
544
545void main()
546{
547 uint idx = gl_GlobalInvocationID.x;
548 if (idx >= vertexCount)
549 return;
550
551 uint srcBase = (idx * srcStrideBytes + srcOffsetBytes) / 4;
552 uint dstComponents = padToVec4 != 0 ? 4 : elementCount;
553 uint dstBase = idx * dstComponents;
554
555 for (uint i = 0; i < elementCount; ++i)
556 dst_data[dstBase + i] = src_data[srcBase + i];
557
558 if (padToVec4 != 0)
559 {
560 for (uint i = elementCount; i < 4; ++i)
561 dst_data[dstBase + i] = (i == 3) ? 0x3f800000u : 0u;
562 }
563}
564)");
565
566 QShader shader = score::gfx::makeCompute(renderState, shaderCode);
567 if(!shader.isValid())
568 {
569 qDebug() << "ComputeExtractionStrategy: Shader compilation failed";
570 return false;
571 }
572
573 // Create uniform buffer (aligned to 256 bytes for compatibility)
574 m_uniformBuffer = rhi.newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 256);
575 m_uniformBuffer->setName("ComputeExtractionStrategy::m_uniformBuffer");
576
577 if(!m_uniformBuffer || !m_uniformBuffer->create())
578 {
579 qDebug() << "ComputeExtractionStrategy: UBO creation failed";
580 return false;
581 }
582
583 m_srb = rhi.newShaderResourceBindings();
584 m_srb->setBindings({
585 QRhiShaderResourceBinding::uniformBuffer(
586 0, QRhiShaderResourceBinding::ComputeStage, m_uniformBuffer),
587 QRhiShaderResourceBinding::bufferLoad(
588 1, QRhiShaderResourceBinding::ComputeStage, m_srcBuffer),
589 QRhiShaderResourceBinding::bufferStore(
590 2, QRhiShaderResourceBinding::ComputeStage, m_outputBuffer),
591 });
592
593 if(!m_srb->create())
594 {
595 qDebug() << "ComputeExtractionStrategy: SRB creation failed";
596 return false;
597 }
598
599 m_pipeline = rhi.newComputePipeline();
600 m_pipeline->setShaderResourceBindings(m_srb);
601 m_pipeline->setShaderStage({QRhiShaderStage::Compute, shader});
602
603 if(!m_pipeline->create())
604 {
605 qDebug() << "ComputeExtractionStrategy: Pipeline creation failed";
606 return false;
607 }
608
609 m_dirty = true;
610 return true;
611 }
612
613 QRhiBuffer* m_srcBuffer{};
614 QRhiBuffer* m_uniformBuffer{};
615 QRhiBuffer* m_outputBuffer{};
616 QRhiShaderResourceBindings* m_srb{};
617 QRhiComputePipeline* m_pipeline{};
618
619 int32_t m_vertexCount{};
620 int32_t m_srcStride{};
621 int32_t m_srcOffset{};
622 int32_t m_elementCount{};
623 int32_t m_outputComponents{};
624 int64_t m_outputSize{};
625
626 bool m_padToVec4{false};
627 bool m_dirty{true};
628};
629
631{
632public:
633 bool init(
634 const score::gfx::RenderState& renderState, QRhi& rhi,
635 const halp::dynamic_gpu_geometry& mesh, const attribute_lookup& lookup,
636 bool padToVec4)
637 {
638 if(mesh.index.buffer < 0
639 || mesh.index.buffer >= static_cast<int>(mesh.buffers.size()))
640 {
641 qDebug() << "IndexedExtractionStrategy: Invalid index buffer";
642 return false;
643 }
644
645 // For indexed geometry, we output one vertex per index
646 m_indexCount = mesh.vertices;
647 m_srcStride = lookup.binding->stride;
648 m_srcOffset = lookup.attribute->byte_offset
649 + static_cast<int32_t>(lookup.input->byte_offset);
650 m_elementCount = attributeFormatComponents(lookup.attribute->format);
651 m_padToVec4
652 = padToVec4 && m_elementCount < 4 && isFloatFormat(lookup.attribute->format);
653 m_indexFormat32 = (mesh.index.format == halp::index_format::uint32);
654 m_indexOffset = static_cast<int32_t>(mesh.index.byte_offset);
655
656 const int32_t outputComponents = m_padToVec4 ? 4 : m_elementCount;
657 m_outputComponents = outputComponents;
658 m_outputSize = static_cast<int64_t>(m_indexCount) * outputComponents * sizeof(float);
659
660 if(m_outputSize == 0)
661 {
662 qDebug() << "IndexedExtractionStrategy: Zero output size";
663 return false;
664 }
665
666 m_outputBuffer = rhi.newBuffer(
667 QRhiBuffer::Static, QRhiBuffer::StorageBuffer | QRhiBuffer::VertexBuffer,
668 static_cast<quint32>(m_outputSize));
669 m_outputBuffer->setName("IndexedExtractionStrategy::m_outputBuffer");
670
671 if(!m_outputBuffer || !m_outputBuffer->create())
672 {
673 qDebug() << "IndexedExtractionStrategy: Failed to create output buffer";
674 return false;
675 }
676
677 m_srcBuffer = static_cast<QRhiBuffer*>(lookup.buffer->handle);
678 m_indexBuffer = static_cast<QRhiBuffer*>(mesh.buffers[mesh.index.buffer].handle);
679
680 if(!m_srcBuffer || !m_indexBuffer)
681 {
682 qDebug() << "IndexedExtractionStrategy: Null source or index buffer";
683 return false;
684 }
685
686 return createPipeline(renderState, rhi);
687 }
688
689 void update(
690 QRhi& rhi, const halp::dynamic_gpu_geometry& mesh, const attribute_lookup& lookup,
691 bool padToVec4)
692 {
693 m_srcBuffer = static_cast<QRhiBuffer*>(lookup.buffer->handle);
694 m_srcStride = lookup.binding->stride;
695 m_srcOffset = lookup.attribute->byte_offset
696 + static_cast<int32_t>(lookup.input->byte_offset);
697
698 if(mesh.index.buffer >= 0
699 && mesh.index.buffer < static_cast<int>(mesh.buffers.size()))
700 {
701 m_indexBuffer = static_cast<QRhiBuffer*>(mesh.buffers[mesh.index.buffer].handle);
702 m_indexOffset = static_cast<int32_t>(mesh.index.byte_offset);
703 m_indexFormat32 = (mesh.index.format == halp::index_format::uint32);
704 }
705
706 const bool newPadToVec4
707 = padToVec4 && m_elementCount < 4 && isFloatFormat(lookup.attribute->format);
708 const int32_t newOutputComponents = newPadToVec4 ? 4 : m_elementCount;
709 const int64_t newSize
710 = static_cast<int64_t>(mesh.vertices) * newOutputComponents * sizeof(float);
711
712 if(newSize != m_outputSize || mesh.vertices != m_indexCount
713 || newPadToVec4 != m_padToVec4)
714 {
715 m_indexCount = mesh.vertices;
716 m_padToVec4 = newPadToVec4;
717 m_outputComponents = newOutputComponents;
718 m_outputSize = newSize;
719
720 if(m_outputSize > 0)
721 {
722 m_outputBuffer->setSize(static_cast<quint32>(m_outputSize));
723 m_outputBuffer->create();
724 }
725 }
726
727 if(m_srb)
728 {
729 m_srb->setBindings({
730 QRhiShaderResourceBinding::bufferLoad(
731 0, QRhiShaderResourceBinding::ComputeStage, m_srcBuffer),
732 QRhiShaderResourceBinding::bufferLoad(
733 1, QRhiShaderResourceBinding::ComputeStage, m_indexBuffer),
734 QRhiShaderResourceBinding::bufferStore(
735 2, QRhiShaderResourceBinding::ComputeStage, m_outputBuffer),
736 });
737 m_srb->create();
738 }
739
740 m_dirty = true;
741 }
742
743 void release() noexcept
744 {
745 delete m_pipeline;
746 m_pipeline = nullptr;
747
748 delete m_srb;
749 m_srb = nullptr;
750
751 delete m_uniformBuffer;
752 m_uniformBuffer = nullptr;
753
754 delete m_outputBuffer;
755 m_outputBuffer = nullptr;
756
757 m_srcBuffer = nullptr;
758 m_indexBuffer = nullptr;
759 }
760
761 void runCompute(QRhi& rhi, QRhiCommandBuffer& cb, QRhiResourceUpdateBatch*& res)
762 {
763 if(!m_dirty || m_indexCount == 0 || !m_pipeline)
764 return;
765
766 struct alignas(16) Params
767 {
768 uint32_t indexCount;
769 uint32_t srcStrideBytes;
770 uint32_t srcOffsetBytes;
771 uint32_t elementCount;
772 uint32_t padToVec4;
773 uint32_t indexOffsetBytes;
774 uint32_t index32Bit;
775 uint32_t _pad;
776 } params{
777 static_cast<uint32_t>(m_indexCount),
778 static_cast<uint32_t>(m_srcStride),
779 static_cast<uint32_t>(m_srcOffset),
780 static_cast<uint32_t>(m_elementCount),
781 m_padToVec4 ? 1u : 0u,
782 static_cast<uint32_t>(m_indexOffset),
783 m_indexFormat32 ? 1u : 0u,
784 0};
785
786 res->updateDynamicBuffer(m_uniformBuffer, 0, sizeof(params), &params);
787
788 cb.beginComputePass(res);
789 cb.setComputePipeline(m_pipeline);
790 cb.setShaderResources(m_srb);
791
792 const int workgroups = (m_indexCount + 255) / 256;
793 cb.dispatch(workgroups, 1, 1);
794
795 cb.endComputePass();
796
797 res = rhi.nextResourceUpdateBatch();
798
799 m_dirty = false;
800 }
801
802 [[nodiscard]] gpu_buffer_view output() const noexcept
803 {
804 return {
805 .buffer = m_outputBuffer,
806 .offset = 0,
807 .size = m_outputSize,
808 };
809 }
810
811 [[nodiscard]] static constexpr bool needsCompute() noexcept { return true; }
812
813private:
814 bool createPipeline(const score::gfx::RenderState& renderState, QRhi& rhi)
815 {
816 static const QString shaderCode = QStringLiteral(R"(#version 450
817
818layout(local_size_x = 256) in;
819
820layout(std140, binding = 0) uniform Params {
821 uint indexCount;
822 uint srcStrideBytes;
823 uint srcOffsetBytes;
824 uint elementCount;
825 uint padToVec4;
826 uint indexOffsetBytes;
827 uint index32Bit;
828};
829
830layout(std430, binding = 1) readonly buffer SrcBuffer {
831 uint src_data[];
832};
833
834layout(std430, binding = 2) readonly buffer IndexBuffer {
835 uint index_data[];
836};
837
838layout(std430, binding = 3) writeonly buffer DstBuffer {
839 uint dst_data[];
840};
841
842uint readIndex(uint i)
843{
844 if (index32Bit != 0)
845 {
846 uint wordIndex = (indexOffsetBytes / 4) + i;
847 return index_data[wordIndex];
848 }
849 else
850 {
851 uint bytePos = indexOffsetBytes + i * 2;
852 uint wordIndex = bytePos / 4;
853 uint word = index_data[wordIndex];
854 uint shift = (bytePos % 4) * 8;
855 return (word >> shift) & 0xFFFFu;
856 }
857}
858
859void main()
860{
861 uint outputIdx = gl_GlobalInvocationID.x;
862 if (outputIdx >= indexCount)
863 return;
864
865 uint vertexIdx = readIndex(outputIdx);
866 uint srcBase = (vertexIdx * srcStrideBytes + srcOffsetBytes) / 4;
867 uint dstComponents = padToVec4 != 0 ? 4 : elementCount;
868 uint dstBase = outputIdx * dstComponents;
869
870 for (uint i = 0; i < elementCount; ++i)
871 dst_data[dstBase + i] = src_data[srcBase + i];
872
873 if (padToVec4 != 0)
874 {
875 for (uint i = elementCount; i < 4; ++i)
876 dst_data[dstBase + i] = (i == 3) ? 0x3f800000u : 0u;
877 }
878}
879)");
880
881 QShader shader = score::gfx::makeCompute(renderState, shaderCode);
882 if(!shader.isValid())
883 {
884 qDebug() << "IndexedExtractionStrategy: Shader compilation failed";
885 return false;
886 }
887
888 m_uniformBuffer = rhi.newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 256);
889 m_uniformBuffer->setName("IndexedExtractionStrategy::m_uniformBuffer");
890
891 if(!m_uniformBuffer || !m_uniformBuffer->create())
892 {
893 qDebug() << "IndexedExtractionStrategy: UBO creation failed";
894 return false;
895 }
896
897 m_srb = rhi.newShaderResourceBindings();
898 m_srb->setBindings({
899 QRhiShaderResourceBinding::uniformBuffer(
900 0, QRhiShaderResourceBinding::ComputeStage, m_uniformBuffer),
901 QRhiShaderResourceBinding::bufferLoad(
902 1, QRhiShaderResourceBinding::ComputeStage, m_srcBuffer),
903 QRhiShaderResourceBinding::bufferLoad(
904 2, QRhiShaderResourceBinding::ComputeStage, m_indexBuffer),
905 QRhiShaderResourceBinding::bufferStore(
906 3, QRhiShaderResourceBinding::ComputeStage, m_outputBuffer),
907 });
908
909 if(!m_srb->create())
910 {
911 qDebug() << "IndexedExtractionStrategy: SRB creation failed";
912 return false;
913 }
914
915 m_pipeline = rhi.newComputePipeline();
916 m_pipeline->setShaderResourceBindings(m_srb);
917 m_pipeline->setShaderStage({QRhiShaderStage::Compute, shader});
918
919 if(!m_pipeline->create())
920 {
921 qDebug() << "IndexedExtractionStrategy: Pipeline creation failed";
922 return false;
923 }
924
925 m_dirty = true;
926 return true;
927 }
928
929 QRhiBuffer* m_srcBuffer{};
930 QRhiBuffer* m_uniformBuffer{};
931 QRhiBuffer* m_indexBuffer{};
932 QRhiBuffer* m_outputBuffer{};
933 QRhiShaderResourceBindings* m_srb{};
934 QRhiComputePipeline* m_pipeline{};
935
936 int32_t m_indexCount{};
937 int32_t m_srcStride{};
938 int32_t m_srcOffset{};
939 int32_t m_indexOffset{};
940 int32_t m_elementCount{};
941 int32_t m_outputComponents{};
942 int64_t m_outputSize{};
943
944 bool m_padToVec4{false};
945 bool m_indexFormat32{true};
946 bool m_dirty{true};
947};
948
949using ExtractionStrategyVariant = std::variant<
952
953}
Definition GeometryToBufferStrategies.hpp:362
Definition GeometryToBufferStrategies.hpp:255
Definition GeometryToBufferStrategies.hpp:306
Definition GeometryToBufferStrategies.hpp:631
QShader makeCompute(const RenderState &v, QString compute)
Compile a compute shader.
Definition score-plugin-gfx/Gfx/Graph/Utils.cpp:421
Definition GeometryToBufferStrategies.hpp:152
Definition GeometryToBufferStrategies.hpp:246
Definition TinyObj.hpp:19
Global state associated to a rendering context.
Definition RenderState.hpp:35