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, int64_t byte_offset,
260 int64_t byte_size)
261 {
262 m_buffer = static_cast<QRhiBuffer*>(mesh.buffers[buffer].handle);
263 m_offset = byte_offset;
264 m_size = byte_size;
265
266 if(!m_buffer)
267 {
268 qDebug() << "DirectBufferReferenceStrategy: Null buffer handle";
269 return false;
270 }
271 assert(m_buffer->size() >= byte_size + byte_offset);
272 return true;
273 }
274
275 void
276 update(QRhi&, const halp::dynamic_gpu_geometry&, const attribute_lookup& lookup, bool)
277 {
278 }
279
280 void release() noexcept
281 {
282 m_buffer = nullptr;
283 m_offset = 0;
284 m_size = 0;
285 }
286
287 void runCompute(QRhi&, QRhiCommandBuffer&, QRhiResourceUpdateBatch*&) { }
288
289 [[nodiscard]] gpu_buffer_view output() const noexcept
290 {
291 return {
292 .buffer = m_buffer,
293 .offset = m_offset,
294 .size = m_size,
295 };
296 }
297
298 [[nodiscard]] static constexpr bool needsCompute() noexcept { return false; }
299
300private:
301 QRhiBuffer* m_buffer{};
302 int64_t m_offset{};
303 int64_t m_size{};
304};
305
307{
308public:
309 bool init(
310 const score::gfx::RenderState& renderState, QRhi& rhi,
311 const halp::dynamic_gpu_geometry& mesh, const attribute_lookup& lookup,
312 bool /*padToVec4*/)
313 {
314 m_buffer = static_cast<QRhiBuffer*>(lookup.buffer->handle);
315 m_offset = lookup.input->byte_offset;
316 m_size = static_cast<int64_t>(lookup.attribute_size) * mesh.vertices;
317
318 if(!m_buffer)
319 {
320 qDebug() << "DirectReferenceStrategy: Null buffer handle";
321 return false;
322 }
323 return true;
324 }
325
326 void update(
327 QRhi& /*rhi*/, const halp::dynamic_gpu_geometry& mesh,
328 const attribute_lookup& lookup, bool /*padToVec4*/)
329 {
330 m_buffer = static_cast<QRhiBuffer*>(lookup.buffer->handle);
331 m_offset = lookup.input->byte_offset;
332 m_size = static_cast<int64_t>(lookup.attribute_size) * mesh.vertices;
333 }
334
335 void release() noexcept
336 {
337 // We don't own the buffer
338 m_buffer = nullptr;
339 m_offset = 0;
340 m_size = 0;
341 }
342
343 void runCompute(QRhi&, QRhiCommandBuffer&, QRhiResourceUpdateBatch*&) { }
344
345 [[nodiscard]] gpu_buffer_view output() const noexcept
346 {
347 return {
348 .buffer = m_buffer,
349 .offset = m_offset,
350 .size = m_size,
351 };
352 }
353
354 [[nodiscard]] static constexpr bool needsCompute() noexcept { return false; }
355
356private:
357 QRhiBuffer* m_buffer{};
358 int64_t m_offset{};
359 int64_t m_size{};
360};
361
363{
364public:
365 bool init(
366 const score::gfx::RenderState& renderState, QRhi& rhi,
367 const halp::dynamic_gpu_geometry& mesh, const attribute_lookup& lookup,
368 bool padToVec4)
369 {
370 m_vertexCount = mesh.vertices;
371 m_srcStride = lookup.binding->stride;
372 m_srcOffset = lookup.attribute->byte_offset
373 + static_cast<int32_t>(lookup.input->byte_offset);
374 m_elementCount = attributeFormatComponents(lookup.attribute->format);
375 m_padToVec4
376 = padToVec4 && m_elementCount < 4 && isFloatFormat(lookup.attribute->format);
377
378 const int32_t outputComponents = m_padToVec4 ? 4 : m_elementCount;
379 m_outputComponents = outputComponents;
380 m_outputSize
381 = static_cast<int64_t>(m_vertexCount) * outputComponents * sizeof(float);
382
383 if(m_outputSize == 0)
384 {
385 qDebug() << "ComputeExtractionStrategy: Zero output size";
386 return false;
387 }
388
389 // Create output buffer
390 m_outputBuffer = rhi.newBuffer(
391 QRhiBuffer::Static, QRhiBuffer::StorageBuffer | QRhiBuffer::VertexBuffer,
392 static_cast<quint32>(m_outputSize));
393 m_outputBuffer->setName("ComputeExtractionStrategy::m_outputBuffer");
394
395 if(!m_outputBuffer || !m_outputBuffer->create())
396 {
397 qDebug() << "ComputeExtractionStrategy: Failed to create output buffer";
398 return false;
399 }
400
401 m_srcBuffer = static_cast<QRhiBuffer*>(lookup.buffer->handle);
402 if(!m_srcBuffer)
403 {
404 qDebug() << "ComputeExtractionStrategy: Null source buffer";
405 return false;
406 }
407
408 return createPipeline(renderState, rhi);
409 }
410
411 void update(
412 QRhi& rhi, const halp::dynamic_gpu_geometry& mesh, const attribute_lookup& lookup,
413 bool padToVec4)
414 {
415 m_srcBuffer = static_cast<QRhiBuffer*>(lookup.buffer->handle);
416 m_srcStride = lookup.binding->stride;
417 m_srcOffset = lookup.attribute->byte_offset
418 + static_cast<int32_t>(lookup.input->byte_offset);
419
420 const bool newPadToVec4
421 = padToVec4 && m_elementCount < 4 && isFloatFormat(lookup.attribute->format);
422 const int32_t newOutputComponents = newPadToVec4 ? 4 : m_elementCount;
423 const int64_t newSize
424 = static_cast<int64_t>(mesh.vertices) * newOutputComponents * sizeof(float);
425
426 // Check if buffer needs resize
427 if(newSize != m_outputSize || mesh.vertices != m_vertexCount
428 || newPadToVec4 != m_padToVec4)
429 {
430 m_vertexCount = mesh.vertices;
431 m_padToVec4 = newPadToVec4;
432 m_outputComponents = newOutputComponents;
433 m_outputSize = newSize;
434
435 if(m_outputSize > 0)
436 {
437 m_outputBuffer->setSize(static_cast<quint32>(m_outputSize));
438 m_outputBuffer->create();
439 }
440 }
441
442 // Rebind if source buffer changed
443 if(m_srb)
444 {
445 m_srb->setBindings({
446 QRhiShaderResourceBinding::bufferLoad(
447 0, QRhiShaderResourceBinding::ComputeStage, m_srcBuffer),
448 QRhiShaderResourceBinding::bufferStore(
449 1, QRhiShaderResourceBinding::ComputeStage, m_outputBuffer),
450 });
451 m_srb->create();
452 }
453
454 m_dirty = true;
455 }
456
457 void release() noexcept
458 {
459 delete m_uniformBuffer;
460 m_uniformBuffer = nullptr;
461
462 delete m_pipeline;
463 m_pipeline = nullptr;
464
465 delete m_srb;
466 m_srb = nullptr;
467
468 delete m_outputBuffer;
469 m_outputBuffer = nullptr;
470
471 m_srcBuffer = nullptr;
472 }
473
474 void runCompute(QRhi& rhi, QRhiCommandBuffer& cb, QRhiResourceUpdateBatch*& res)
475 {
476 if(!m_dirty || m_vertexCount == 0 || !m_pipeline)
477 return;
478
479 struct alignas(16) Params
480 {
481 uint32_t vertexCount;
482 uint32_t srcStrideBytes;
483 uint32_t srcOffsetBytes;
484 uint32_t elementCount;
485 uint32_t padToVec4;
486 uint32_t _pad[3]; // Padding to maintain alignment
487 } params{
488 static_cast<uint32_t>(m_vertexCount),
489 static_cast<uint32_t>(m_srcStride),
490 static_cast<uint32_t>(m_srcOffset),
491 static_cast<uint32_t>(m_elementCount),
492 m_padToVec4 ? 1u : 0u,
493 {0, 0, 0}};
494
495 res->updateDynamicBuffer(m_uniformBuffer, 0, sizeof(params), &params);
496
497 cb.beginComputePass(res);
498 cb.setComputePipeline(m_pipeline);
499 cb.setShaderResources(m_srb);
500
501 const int workgroups = (m_vertexCount + 255) / 256;
502 cb.dispatch(workgroups, 1, 1);
503
504 cb.endComputePass();
505
506 // Get new resource batch after compute pass
507 res = rhi.nextResourceUpdateBatch();
508
509 m_dirty = false;
510 }
511
512 [[nodiscard]] gpu_buffer_view output() const noexcept
513 {
514 return {
515 .buffer = m_outputBuffer,
516 .offset = 0,
517 .size = m_outputSize,
518 };
519 }
520
521 [[nodiscard]] static constexpr bool needsCompute() noexcept { return true; }
522
523private:
524 bool createPipeline(const score::gfx::RenderState& renderState, QRhi& rhi)
525 {
526 static const QString shaderCode = QStringLiteral(R"(#version 450
527
528layout(local_size_x = 256) in;
529
530layout(std140, binding = 0) uniform Params {
531 uint vertexCount;
532 uint srcStrideBytes;
533 uint srcOffsetBytes;
534 uint elementCount;
535 uint padToVec4;
536};
537
538layout(std430, binding = 1) readonly buffer SrcBuffer {
539 uint src_data[];
540};
541
542layout(std430, binding = 2) writeonly buffer DstBuffer {
543 uint dst_data[];
544};
545
546void main()
547{
548 uint idx = gl_GlobalInvocationID.x;
549 if (idx >= vertexCount)
550 return;
551
552 uint srcBase = (idx * srcStrideBytes + srcOffsetBytes) / 4;
553 uint dstComponents = padToVec4 != 0 ? 4 : elementCount;
554 uint dstBase = idx * dstComponents;
555
556 for (uint i = 0; i < elementCount; ++i)
557 dst_data[dstBase + i] = src_data[srcBase + i];
558
559 if (padToVec4 != 0)
560 {
561 for (uint i = elementCount; i < 4; ++i)
562 dst_data[dstBase + i] = (i == 3) ? 0x3f800000u : 0u;
563 }
564}
565)");
566
567 QShader shader = score::gfx::makeCompute(renderState, shaderCode);
568 if(!shader.isValid())
569 {
570 qDebug() << "ComputeExtractionStrategy: Shader compilation failed";
571 return false;
572 }
573
574 // Create uniform buffer (aligned to 256 bytes for compatibility)
575 m_uniformBuffer = rhi.newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 256);
576 m_uniformBuffer->setName("ComputeExtractionStrategy::m_uniformBuffer");
577
578 if(!m_uniformBuffer || !m_uniformBuffer->create())
579 {
580 qDebug() << "ComputeExtractionStrategy: UBO creation failed";
581 return false;
582 }
583
584 m_srb = rhi.newShaderResourceBindings();
585 m_srb->setBindings({
586 QRhiShaderResourceBinding::uniformBuffer(
587 0, QRhiShaderResourceBinding::ComputeStage, m_uniformBuffer),
588 QRhiShaderResourceBinding::bufferLoad(
589 1, QRhiShaderResourceBinding::ComputeStage, m_srcBuffer),
590 QRhiShaderResourceBinding::bufferStore(
591 2, QRhiShaderResourceBinding::ComputeStage, m_outputBuffer),
592 });
593
594 if(!m_srb->create())
595 {
596 qDebug() << "ComputeExtractionStrategy: SRB creation failed";
597 return false;
598 }
599
600 m_pipeline = rhi.newComputePipeline();
601 m_pipeline->setShaderResourceBindings(m_srb);
602 m_pipeline->setShaderStage({QRhiShaderStage::Compute, shader});
603
604 if(!m_pipeline->create())
605 {
606 qDebug() << "ComputeExtractionStrategy: Pipeline creation failed";
607 return false;
608 }
609
610 m_dirty = true;
611 return true;
612 }
613
614 QRhiBuffer* m_srcBuffer{};
615 QRhiBuffer* m_uniformBuffer{};
616 QRhiBuffer* m_outputBuffer{};
617 QRhiShaderResourceBindings* m_srb{};
618 QRhiComputePipeline* m_pipeline{};
619
620 int32_t m_vertexCount{};
621 int32_t m_srcStride{};
622 int32_t m_srcOffset{};
623 int32_t m_elementCount{};
624 int32_t m_outputComponents{};
625 int64_t m_outputSize{};
626
627 bool m_padToVec4{false};
628 bool m_dirty{true};
629};
630
632{
633public:
634 bool init(
635 const score::gfx::RenderState& renderState, QRhi& rhi,
636 const halp::dynamic_gpu_geometry& mesh, const attribute_lookup& lookup,
637 bool padToVec4)
638 {
639 if(mesh.index.buffer < 0
640 || mesh.index.buffer >= static_cast<int>(mesh.buffers.size()))
641 {
642 qDebug() << "IndexedExtractionStrategy: Invalid index buffer";
643 return false;
644 }
645
646 // For indexed geometry, we output one vertex per index
647 m_indexCount = mesh.vertices;
648 m_srcStride = lookup.binding->stride;
649 m_srcOffset = lookup.attribute->byte_offset
650 + static_cast<int32_t>(lookup.input->byte_offset);
651 m_elementCount = attributeFormatComponents(lookup.attribute->format);
652 m_padToVec4
653 = padToVec4 && m_elementCount < 4 && isFloatFormat(lookup.attribute->format);
654 m_indexFormat32 = (mesh.index.format == halp::index_format::uint32);
655 m_indexOffset = static_cast<int32_t>(mesh.index.byte_offset);
656
657 const int32_t outputComponents = m_padToVec4 ? 4 : m_elementCount;
658 m_outputComponents = outputComponents;
659 m_outputSize = static_cast<int64_t>(m_indexCount) * outputComponents * sizeof(float);
660
661 if(m_outputSize == 0)
662 {
663 qDebug() << "IndexedExtractionStrategy: Zero output size";
664 return false;
665 }
666
667 m_outputBuffer = rhi.newBuffer(
668 QRhiBuffer::Static, QRhiBuffer::StorageBuffer | QRhiBuffer::VertexBuffer,
669 static_cast<quint32>(m_outputSize));
670 m_outputBuffer->setName("IndexedExtractionStrategy::m_outputBuffer");
671
672 if(!m_outputBuffer || !m_outputBuffer->create())
673 {
674 qDebug() << "IndexedExtractionStrategy: Failed to create output buffer";
675 return false;
676 }
677
678 m_srcBuffer = static_cast<QRhiBuffer*>(lookup.buffer->handle);
679 m_indexBuffer = static_cast<QRhiBuffer*>(mesh.buffers[mesh.index.buffer].handle);
680
681 if(!m_srcBuffer || !m_indexBuffer)
682 {
683 qDebug() << "IndexedExtractionStrategy: Null source or index buffer";
684 return false;
685 }
686
687 return createPipeline(renderState, rhi);
688 }
689
690 void update(
691 QRhi& rhi, const halp::dynamic_gpu_geometry& mesh, const attribute_lookup& lookup,
692 bool padToVec4)
693 {
694 m_srcBuffer = static_cast<QRhiBuffer*>(lookup.buffer->handle);
695 m_srcStride = lookup.binding->stride;
696 m_srcOffset = lookup.attribute->byte_offset
697 + static_cast<int32_t>(lookup.input->byte_offset);
698
699 if(mesh.index.buffer >= 0
700 && mesh.index.buffer < static_cast<int>(mesh.buffers.size()))
701 {
702 m_indexBuffer = static_cast<QRhiBuffer*>(mesh.buffers[mesh.index.buffer].handle);
703 m_indexOffset = static_cast<int32_t>(mesh.index.byte_offset);
704 m_indexFormat32 = (mesh.index.format == halp::index_format::uint32);
705 }
706
707 const bool newPadToVec4
708 = padToVec4 && m_elementCount < 4 && isFloatFormat(lookup.attribute->format);
709 const int32_t newOutputComponents = newPadToVec4 ? 4 : m_elementCount;
710 const int64_t newSize
711 = static_cast<int64_t>(mesh.vertices) * newOutputComponents * sizeof(float);
712
713 if(newSize != m_outputSize || mesh.vertices != m_indexCount
714 || newPadToVec4 != m_padToVec4)
715 {
716 m_indexCount = mesh.vertices;
717 m_padToVec4 = newPadToVec4;
718 m_outputComponents = newOutputComponents;
719 m_outputSize = newSize;
720
721 if(m_outputSize > 0)
722 {
723 m_outputBuffer->setSize(static_cast<quint32>(m_outputSize));
724 m_outputBuffer->create();
725 }
726 }
727
728 if(m_srb)
729 {
730 m_srb->setBindings({
731 QRhiShaderResourceBinding::bufferLoad(
732 0, QRhiShaderResourceBinding::ComputeStage, m_srcBuffer),
733 QRhiShaderResourceBinding::bufferLoad(
734 1, QRhiShaderResourceBinding::ComputeStage, m_indexBuffer),
735 QRhiShaderResourceBinding::bufferStore(
736 2, QRhiShaderResourceBinding::ComputeStage, m_outputBuffer),
737 });
738 m_srb->create();
739 }
740
741 m_dirty = true;
742 }
743
744 void release() noexcept
745 {
746 delete m_pipeline;
747 m_pipeline = nullptr;
748
749 delete m_srb;
750 m_srb = nullptr;
751
752 delete m_uniformBuffer;
753 m_uniformBuffer = nullptr;
754
755 delete m_outputBuffer;
756 m_outputBuffer = nullptr;
757
758 m_srcBuffer = nullptr;
759 m_indexBuffer = nullptr;
760 }
761
762 void runCompute(QRhi& rhi, QRhiCommandBuffer& cb, QRhiResourceUpdateBatch*& res)
763 {
764 if(!m_dirty || m_indexCount == 0 || !m_pipeline)
765 return;
766
767 struct alignas(16) Params
768 {
769 uint32_t indexCount;
770 uint32_t srcStrideBytes;
771 uint32_t srcOffsetBytes;
772 uint32_t elementCount;
773 uint32_t padToVec4;
774 uint32_t indexOffsetBytes;
775 uint32_t index32Bit;
776 uint32_t _pad;
777 } params{
778 static_cast<uint32_t>(m_indexCount),
779 static_cast<uint32_t>(m_srcStride),
780 static_cast<uint32_t>(m_srcOffset),
781 static_cast<uint32_t>(m_elementCount),
782 m_padToVec4 ? 1u : 0u,
783 static_cast<uint32_t>(m_indexOffset),
784 m_indexFormat32 ? 1u : 0u,
785 0};
786
787 res->updateDynamicBuffer(m_uniformBuffer, 0, sizeof(params), &params);
788
789 cb.beginComputePass(res);
790 cb.setComputePipeline(m_pipeline);
791 cb.setShaderResources(m_srb);
792
793 const int workgroups = (m_indexCount + 255) / 256;
794 cb.dispatch(workgroups, 1, 1);
795
796 cb.endComputePass();
797
798 res = rhi.nextResourceUpdateBatch();
799
800 m_dirty = false;
801 }
802
803 [[nodiscard]] gpu_buffer_view output() const noexcept
804 {
805 return {
806 .buffer = m_outputBuffer,
807 .offset = 0,
808 .size = m_outputSize,
809 };
810 }
811
812 [[nodiscard]] static constexpr bool needsCompute() noexcept { return true; }
813
814private:
815 bool createPipeline(const score::gfx::RenderState& renderState, QRhi& rhi)
816 {
817 static const QString shaderCode = QStringLiteral(R"(#version 450
818
819layout(local_size_x = 256) in;
820
821layout(std140, binding = 0) uniform Params {
822 uint indexCount;
823 uint srcStrideBytes;
824 uint srcOffsetBytes;
825 uint elementCount;
826 uint padToVec4;
827 uint indexOffsetBytes;
828 uint index32Bit;
829};
830
831layout(std430, binding = 1) readonly buffer SrcBuffer {
832 uint src_data[];
833};
834
835layout(std430, binding = 2) readonly buffer IndexBuffer {
836 uint index_data[];
837};
838
839layout(std430, binding = 3) writeonly buffer DstBuffer {
840 uint dst_data[];
841};
842
843uint readIndex(uint i)
844{
845 if (index32Bit != 0)
846 {
847 uint wordIndex = (indexOffsetBytes / 4) + i;
848 return index_data[wordIndex];
849 }
850 else
851 {
852 uint bytePos = indexOffsetBytes + i * 2;
853 uint wordIndex = bytePos / 4;
854 uint word = index_data[wordIndex];
855 uint shift = (bytePos % 4) * 8;
856 return (word >> shift) & 0xFFFFu;
857 }
858}
859
860void main()
861{
862 uint outputIdx = gl_GlobalInvocationID.x;
863 if (outputIdx >= indexCount)
864 return;
865
866 uint vertexIdx = readIndex(outputIdx);
867 uint srcBase = (vertexIdx * srcStrideBytes + srcOffsetBytes) / 4;
868 uint dstComponents = padToVec4 != 0 ? 4 : elementCount;
869 uint dstBase = outputIdx * dstComponents;
870
871 for (uint i = 0; i < elementCount; ++i)
872 dst_data[dstBase + i] = src_data[srcBase + i];
873
874 if (padToVec4 != 0)
875 {
876 for (uint i = elementCount; i < 4; ++i)
877 dst_data[dstBase + i] = (i == 3) ? 0x3f800000u : 0u;
878 }
879}
880)");
881
882 QShader shader = score::gfx::makeCompute(renderState, shaderCode);
883 if(!shader.isValid())
884 {
885 qDebug() << "IndexedExtractionStrategy: Shader compilation failed";
886 return false;
887 }
888
889 m_uniformBuffer = rhi.newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 256);
890 m_uniformBuffer->setName("IndexedExtractionStrategy::m_uniformBuffer");
891
892 if(!m_uniformBuffer || !m_uniformBuffer->create())
893 {
894 qDebug() << "IndexedExtractionStrategy: UBO creation failed";
895 return false;
896 }
897
898 m_srb = rhi.newShaderResourceBindings();
899 m_srb->setBindings({
900 QRhiShaderResourceBinding::uniformBuffer(
901 0, QRhiShaderResourceBinding::ComputeStage, m_uniformBuffer),
902 QRhiShaderResourceBinding::bufferLoad(
903 1, QRhiShaderResourceBinding::ComputeStage, m_srcBuffer),
904 QRhiShaderResourceBinding::bufferLoad(
905 2, QRhiShaderResourceBinding::ComputeStage, m_indexBuffer),
906 QRhiShaderResourceBinding::bufferStore(
907 3, QRhiShaderResourceBinding::ComputeStage, m_outputBuffer),
908 });
909
910 if(!m_srb->create())
911 {
912 qDebug() << "IndexedExtractionStrategy: SRB creation failed";
913 return false;
914 }
915
916 m_pipeline = rhi.newComputePipeline();
917 m_pipeline->setShaderResourceBindings(m_srb);
918 m_pipeline->setShaderStage({QRhiShaderStage::Compute, shader});
919
920 if(!m_pipeline->create())
921 {
922 qDebug() << "IndexedExtractionStrategy: Pipeline creation failed";
923 return false;
924 }
925
926 m_dirty = true;
927 return true;
928 }
929
930 QRhiBuffer* m_srcBuffer{};
931 QRhiBuffer* m_uniformBuffer{};
932 QRhiBuffer* m_indexBuffer{};
933 QRhiBuffer* m_outputBuffer{};
934 QRhiShaderResourceBindings* m_srb{};
935 QRhiComputePipeline* m_pipeline{};
936
937 int32_t m_indexCount{};
938 int32_t m_srcStride{};
939 int32_t m_srcOffset{};
940 int32_t m_indexOffset{};
941 int32_t m_elementCount{};
942 int32_t m_outputComponents{};
943 int64_t m_outputSize{};
944
945 bool m_padToVec4{false};
946 bool m_indexFormat32{true};
947 bool m_dirty{true};
948};
949
950using ExtractionStrategyVariant = std::variant<
953
954}
Definition GeometryToBufferStrategies.hpp:363
Definition GeometryToBufferStrategies.hpp:255
Definition GeometryToBufferStrategies.hpp:307
Definition GeometryToBufferStrategies.hpp:632
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