Skip to content

Commit 144299d

Browse files
author
devsh
committed
finalize my indexing abstraction logic
1 parent fd87ce7 commit 144299d

File tree

3 files changed

+157
-110
lines changed

3 files changed

+157
-110
lines changed

include/nbl/asset/ICPUPolygonGeometry.h

Lines changed: 18 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -137,39 +137,32 @@ class NBL_API2 ICPUPolygonGeometry final : public IAsset, public IPolygonGeometr
137137
return nullptr;
138138
}
139139

140-
//
141-
template<typename Out, typename Index=uint32_t> requires (hlsl::concepts::UnsignedIntegralScalar<Out> && hlsl::concepts::UnsignedIntegralScalar<Index>)
142-
inline bool getPrimitiveIndices(Out* out, const Index primitiveID) const
140+
// We don't care about primitive restart, you need to check for it yourself.
141+
// Unlike OpenGL and other APIs we don't adjust the Primitive ID because that breaks parallel processing.
142+
// So a triangle strip `{ 0 1 2 3 RESTART 2 3 4 5 }` means 7 primitives, of which 3 are invalid (contain the restart index)
143+
template<typename Out> requires hlsl::concepts::UnsignedIntegralScalar<Out>
144+
inline bool getPrimitiveIndices(Out* out, const uint32_t beginPrimitive, const uint32_t endPrimitive) const
143145
{
144146
if (!m_indexing)
145147
return false;
146-
if (m_indexView)
147-
{
148-
}
149-
else
150-
{
151-
}
152148
IIndexingCallback::SContext ctx = {
153-
.indexSizeLog2 = m_indexView
149+
.indexBuffer = m_indexView.getPointer(),
150+
.indexSize = getTexelOrBlockBytesize(m_indexView.composed.format),
151+
.beginPrimitive = beginPrimitive,
152+
.endPrimitive = endPrimitive,
153+
.out = out
154154
};
155-
for (auto i=0; i<m_verticesForFirst; i++)
156-
{
157-
assert(false);
158-
indexID = 0xdeadbeefBADC0FFEull; // TODO: need a callback for index mapping
159-
if (m_indexView)
160-
{
161-
hlsl::vector<Uint,1> tmp;
162-
if (!m_indexView.decodeElement<decltype(tmp),Index>(indexID,tmp));
163-
return false;
164-
*out = tmp[0];
165-
}
166-
else
167-
*out = indexID;
168-
out++;
169-
}
155+
m_indexing->operator()(ctx);
170156
return true;
171157
}
172158

159+
//
160+
template<typename Out>
161+
inline bool getPrimitiveIndices(Out* out, const uint32_t primitiveID) const
162+
{
163+
return getPrimitiveIndices(out,primtiveID,primtiveID+1);
164+
}
165+
173166
protected:
174167
//
175168
inline void visitDependents(std::function<bool(const IAsset*)> visit) const //override

include/nbl/asset/IPolygonGeometry.h

Lines changed: 60 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include "nbl/asset/IGeometry.h"
99
#include "nbl/asset/IAccelerationStructure.h"
1010

11+
#include <span>
1112

1213
namespace nbl::asset
1314
{
@@ -26,87 +27,91 @@ class NBL_API2 IPolygonGeometryBase : public virtual core::IReferenceCounted
2627
assert(retval>0);
2728
return retval;
2829
}
29-
//
30-
inline uint8_t reuseCount() const
30+
// at which we consume indices for each new polygon
31+
inline uint8_t rate() const
3132
{
32-
const auto retval = reuseCount_impl();
33-
assert(retval<degree());
33+
const auto retval = rate_impl();
34+
assert(retval>0 && retval<=degree());
3435
return retval;
3536
}
3637

37-
struct SContext
38+
template<typename OutT> requires (sizeof(OutT)<8 && hlsl::concepts::UnsignedIntegralScalar<OutT>)
39+
struct SContext final
3840
{
39-
template<typename Index> requires std::is_integral_v<Index>
40-
inline Index indexSize() const {return Index(1)<<indexSizeLog2;}
41-
42-
template<typename Index> requires std::is_integral_v<Index>
43-
inline void setOutput(const Index value) const
41+
// `indexOfIndex` is somewhat of a baseIndex
42+
inline void streamOut(const uint32_t indexOfIndex, const std::span<const int32_t> permutation)
4443
{
45-
switch (indexSizeLog2)
44+
auto& typedOut = reinterpret_cast<OutT*&>(out);
45+
if (indexBuffer)
46+
switch (indexSize)
4647
{
47-
case 0:
48-
*reinterpret_cast<uint8_t*>(out) = value;
49-
break;
5048
case 1:
51-
*reinterpret_cast<uint16_t*>(out) = value;
49+
for (const auto relIx : permutation)
50+
*(typedOut++) = reinterpret_cast<const uint8_t*>(indexBuffer)[indexOfIndex+relIx];
5251
break;
5352
case 2:
54-
*reinterpret_cast<uint32_t*>(out) = value;
53+
for (const auto relIx : permutation)
54+
*(typedOut++) = reinterpret_cast<const uint16_t*>(indexBuffer)[indexOfIndex+relIx];
5555
break;
56-
case 3:
57-
*reinterpret_cast<uint64_t*>(out) = value;
56+
case 4:
57+
for (const auto relIx : permutation)
58+
*(typedOut++) = reinterpret_cast<const uint32_t*>(indexBuffer)[indexOfIndex+relIx];
5859
break;
5960
default:
6061
assert(false);
6162
break;
6263
}
64+
else
65+
for (const auto relIx : permutation)
66+
*(typedOut++) = indexOfIndex+relIx;
6367
}
6468

65-
const uint8_t* const indexBuffer;
66-
// no point making them smaller cause of padding
67-
const uint32_t inSizeLog2;
68-
const uint32_t outSizeLog2;
69-
uint8_t* out;
70-
uint32_t newIndexID;
71-
// if `reuseCount()==0` then should be ignored
72-
uint32_t restartValue = ~0ull;
69+
// always the base pointer, doesn't get advanced
70+
const void* const indexBuffer;
71+
const uint64_t indexSize : 3;
72+
const uint64_t beginPrimitive : 30;
73+
const uint64_t endPrimitive : 31;
74+
void* out;
7375
};
74-
inline void operator()(SContext& ctx) const
75-
{
76-
const auto deg = degree();
77-
if (ctx.newIndexID<deg || primitiveRestart)
78-
{
79-
// straight up copy
80-
}
81-
operator_impl(ctx);
82-
ctx.newIndexID += reuseCount();
83-
ctx.out += deg<<ctx.outSizeLog2;
84-
}
76+
// could have been a static if not virtual
77+
virtual void operator()(SContext<uint8_t>& ctx) const = 0;
78+
virtual void operator()(SContext<uint16_t>& ctx) const = 0;
79+
virtual void operator()(SContext<uint32_t>& ctx) const = 0;
8580

8681
// default is unknown
8782
virtual inline E_PRIMITIVE_TOPOLOGY knownTopology() const {return static_cast<E_PRIMITIVE_TOPOLOGY>(~0);}
8883

8984
protected:
9085
virtual uint8_t degree_impl() const = 0;
91-
virtual uint8_t reuseCount_impl() const = 0;
92-
// needs to deal with being handed `!ctx.indexBuffer`
93-
virtual void operator_impl(const SContext& ctx) const = 0;
86+
virtual uint8_t rate_impl() const = 0;
9487
};
9588
//
9689
static IIndexingCallback* PointList();
9790
static IIndexingCallback* LineList();
9891
static IIndexingCallback* TriangleList();
9992
static IIndexingCallback* QuadList();
100-
//
93+
// TODO: Adjacency, Patch, etc.
10194
static IIndexingCallback* TriangleStrip();
10295
static IIndexingCallback* TriangleFan();
10396

104-
//
97+
// This should be a pointer to a stateless singleton (think of it more like a dynamic enum/template than anything else)
10598
inline const IIndexingCallback* getIndexingCallback() const {return m_indexing;}
10699

107100
protected:
108101
virtual ~IPolygonGeometryBase() = default;
109102

103+
// indexing callback cannot be cleared
104+
bool setIndexingCallback(IIndexingCallback* indexing)
105+
{
106+
if (!indexing)
107+
return false;
108+
const auto deg = m_indexing->degree();
109+
if (deg==0 || m_indexing->rate()==0 || m_indexing->rate()>deg)
110+
return false;
111+
m_indexing = indexing;
112+
return true;
113+
}
114+
110115
//
111116
const IIndexingCallback* m_indexing = nullptr;
112117
};
@@ -131,9 +136,6 @@ class NBL_API2 IPolygonGeometry : public IIndexableGeometry<BufferType>, public
131136
return false;
132137
if (!m_indexing)
133138
return false;
134-
// things that make no sense
135-
if (m_verticesPerSupplementary==0 || m_verticesPerSupplementary>m_verticesForFirst)
136-
return false;
137139
// there needs to be at least one vertex to reference (it also needs to be formatted)
138140
const auto& positionBase = base_t::m_positionView.composed;
139141
const auto vertexCount = positionBase.getElementCount();
@@ -170,7 +172,7 @@ class NBL_API2 IPolygonGeometry : public IIndexableGeometry<BufferType>, public
170172
const auto verticesForFirst = m_indexing->degree();
171173
if (vertexReferenceCount<verticesForFirst)
172174
return 0;
173-
return (vertexReferenceCount-verticesForFirst)/(verticesForFirst-m_indexing->reuseCount());
175+
return (vertexReferenceCount-verticesForFirst)/m_indexing->rate()+1;
174176
}
175177

176178
// For when the geometric normal of the patch isn't enough and you want interpolated custom normals
@@ -205,8 +207,8 @@ class NBL_API2 IPolygonGeometry : public IIndexableGeometry<BufferType>, public
205207
inline BLASTriangles exportForBLAS() const
206208
{
207209
BLASTriangles retval = {};
208-
// must be a triangle list
209-
if (m_verticesForFirst==3 && m_verticesPerSupplementary==3)
210+
// must be a triangle list, but don't want to compare pointers
211+
if (m_indexing && m_indexing->knownTopology()==EPT_TRIANGLE_LIST)// && m_indexing->degree() == TriangleList()->degree() && m_indexing->rate() == TriangleList->rate())
210212
{
211213
auto indexType = EIT_UNKNOWN;
212214
// disallowed index format
@@ -239,15 +241,20 @@ class NBL_API2 IPolygonGeometry : public IIndexableGeometry<BufferType>, public
239241
protected:
240242
virtual ~IPolygonGeometry() = default;
241243

242-
//
244+
// 64bit indices are just too much to deal with in all the other code
245+
// Also if you have more than 2G vertex references in a single geometry there's something wrong with your architecture
246+
// Note that this still allows 6GB vertex attribute streams (assuming at least 3 bytes for a postion)
243247
inline bool setIndexView(SDataView&& view)
244248
{
245-
if (!view || view.isFormattedScalarInteger())
249+
if (view)
246250
{
247-
m_indexView = std::move(view);
248-
return true;
251+
if (!view.isFormattedScalarInteger() || view.format == EF_R64_UINT || view.format == EF_R64_SINT)
252+
return false;
253+
if (view.getElementCount()>(1u<<31))
254+
return false;
249255
}
250-
return false;
256+
m_indexView = std::move(view);
257+
return true;
251258
}
252259

253260
//

src/nbl/asset/ICPUPolygonGeometry.cpp

Lines changed: 79 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,43 @@
11
#include "nbl/asset/ICPUPolygonGeometry.h"
22

3+
#include <ranges>
4+
35
using namespace nbl;
46
using namespace asset;
57

68

79
template<uint8_t Order> requires (Order>0)
810
class CListIndexingCB final : public IPolygonGeometryBase::IIndexingCallback
911
{
10-
inline uint8_t degree_impl() const override {return Order;}
11-
inline uint8_t reuseCount_impl() const override {return 0;}
12-
inline void operator_impl(const SContext& ctx) const override
13-
{
14-
if (ctx.indexBuffer)
15-
memcpy(ctx.out,ctx.indexBuffer+(ctx.newIndexID<<ctx.indexSizeLog2),Order<<ctx.indexSizeLog2);
16-
else
17-
for (auto i=0u; i<Order; i++)
12+
template<typename OutT>
13+
static void operator_impl(SContext<OutT>& ctx)
14+
{
15+
auto indexOfIndex = ctx.beginPrimitive*3;
16+
for (const auto end=ctx.endPrimitive*3; indexOfIndex!=end; indexOfIndex+=3)
17+
ctx.streamOut(indexOfIndex,std::ranges::iota_view{0,Order});
18+
}
19+
20+
public:
21+
uint8_t degree_impl() const override {return Order;}
22+
uint8_t rate_impl() const override {return Order;}
23+
void operator()(SContext<uint8_t>& ctx) const override {operator_impl(ctx);}
24+
void operator()(SContext<uint16_t>& ctx) const override {operator_impl(ctx);}
25+
void operator()(SContext<uint32_t>& ctx) const override {operator_impl(ctx);}
26+
27+
E_PRIMITIVE_TOPOLOGY knownTopology() const override
1828
{
19-
ctx.setOutput(ctx.newIndexID+i);
20-
ctx.out += ctx.indexSize();
29+
switch (Order)
30+
{
31+
case 1:
32+
return EPT_POINT_LIST;
33+
case 2:
34+
return EPT_LINE_LIST;
35+
case 3:
36+
return EPT_TRIANGLE_LIST;
37+
default:
38+
return EPT_PATCH_LIST;
39+
}
2140
}
22-
}
2341
};
2442
auto IPolygonGeometryBase::PointList() -> IIndexingCallback*
2543
{
@@ -44,13 +62,30 @@ auto IPolygonGeometryBase::QuadList() -> IIndexingCallback*
4462

4563
class CTriangleStripIndexingCB final : public IIndexingCallback
4664
{
47-
inline uint8_t degree_impl() const override {return 3;}
48-
inline uint8_t reuseCount_impl() const override {return 2;}
49-
inline void operator_impl(const SContext& ctx) const override
50-
{
51-
// two immediately previous and current
52-
memcpy(out,indexBuffer+(newIndexID-2)*indexSize,indexSize*3);
53-
}
65+
template<typename OutT>
66+
static void operator_impl(SContext<OutT>& ctx)
67+
{
68+
auto indexOfIndex;
69+
if (ctx.beginPrimitive==0)
70+
{
71+
ctx.streamOut(0,std::ranges::iota_view{0,3});
72+
indexOfIndex = 3;
73+
}
74+
else
75+
indexOfIndex = ctx.beginPrimitive+2;
76+
const int32_t perm[] = {-1,-2,0};
77+
for (const auto end=ctx.endPrimitive+2; indexOfIndex!=end; indexOfIndex++)
78+
ctx.streamOut(indexOfIndex,perm);
79+
}
80+
81+
public:
82+
inline uint8_t degree_impl() const override { return 3; }
83+
inline uint8_t rate_impl() const override { return 1; }
84+
void operator()(SContext<uint8_t>& ctx) const override { operator_impl(ctx); }
85+
void operator()(SContext<uint16_t>& ctx) const override { operator_impl(ctx); }
86+
void operator()(SContext<uint32_t>& ctx) const override { operator_impl(ctx); }
87+
88+
E_PRIMITIVE_TOPOLOGY knownTopology() const override {return EPT_TRIANGLE_STRIP;}
5489
};
5590
auto IPolygonGeometryBase::TriangleStrip() -> IIndexingCallback*
5691
{
@@ -60,22 +95,34 @@ auto IPolygonGeometryBase::TriangleStrip() -> IIndexingCallback*
6095

6196
class CTriangleFanIndexingCB final : public IIndexingCallback
6297
{
63-
inline uint8_t degree_impl() const override {return 3;}
64-
inline uint8_t reuseCount_impl() const override {return 2;}
65-
inline void operator_impl(const SContext& ctx) const override
66-
{
67-
// first index is always 0
68-
memset(ctx.out,0,ctx.indexSize());
69-
ctx.out += ctx.indexSize();
70-
// immediately previous and current
71-
if (ctx.indexBuffer)
72-
memcpy(ctx.out,ctx.indexBuffer+((ctx.newIndexID-1)<<ctx.indexSizeLog2),2u<<ctx.indexSizeLog2);
73-
else
98+
template<typename OutT>
99+
static void operator_impl(SContext<OutT>& ctx)
74100
{
75-
ctx.setOutput(ctx.newIndexID-1);
76-
ctx.setOutput(ctx.newIndexID);
101+
auto indexOfIndex;
102+
if (ctx.beginPrimitive==0)
103+
{
104+
ctx.streamOut(0,std::ranges::iota_view{0,3});
105+
indexOfIndex = 3;
106+
}
107+
else
108+
indexOfIndex = ctx.beginPrimitive+2;
109+
int32_t perm[] = {0xdeadbeefu,-1,0};
110+
for (const auto end=ctx.endPrimitive+2; indexOfIndex!=end; indexOfIndex++)
111+
{
112+
// first index is always global 0
113+
perm[0] = -indexOfIndex;
114+
ctx.streamOut(indexOfIndex,perm);
115+
}
77116
}
78-
}
117+
118+
public:
119+
inline uint8_t degree_impl() const override {return 3;}
120+
inline uint8_t rate_impl() const override {return 1;}
121+
void operator()(SContext<uint8_t>& ctx) const override { operator_impl(ctx); }
122+
void operator()(SContext<uint16_t>& ctx) const override { operator_impl(ctx); }
123+
void operator()(SContext<uint32_t>& ctx) const override { operator_impl(ctx); }
124+
125+
E_PRIMITIVE_TOPOLOGY knownTopology() const override {return EPT_TRIANGLE_STRIP;}
79126
};
80127
auto IPolygonGeometryBase::TriangleFan() -> IIndexingCallback*
81128
{

0 commit comments

Comments
 (0)