blob: 176513df6b7a45bb88415b9b6a76d544db5f3d0a [file]
/*
* Copyright 2025 Rive
*/
#pragma once
#include "rive/renderer/gpu.hpp"
#include <stdint.h>
#include <algorithm>
#include <cassert>
namespace rive::gpu
{
// This class breaks large ranges up into bite-size chunks that can be
// accommodated by a single draw call (maxPerDrawCommand). This is needed for
// depthStencil draws, which use repeating index patterns instead of instancing,
// and for some Mali and PowerVR devices that crash when issuing draw commands
// with a large instance count.
class RangeChunker
{
public:
struct Chunk
{
uint32_t count;
uint32_t first;
};
struct Iterator
{
public:
uint32_t currentChunkCount() const
{
return std::min(end - current, maxPerDrawCommand);
}
Chunk operator*() const { return {currentChunkCount(), current}; }
Iterator& operator++()
{
current += currentChunkCount();
return *this;
}
bool operator==(const Iterator& other) const
{
assert(end == other.end);
assert(maxPerDrawCommand == other.maxPerDrawCommand);
return current == other.current;
}
bool operator!=(const Iterator& other) const
{
return !(*this == other);
}
const uint32_t end;
const uint32_t maxPerDrawCommand;
uint32_t current;
};
RangeChunker(uint32_t count, uint32_t first, uint32_t maxPerDrawCommand) :
m_first(first),
m_end(first + count),
m_maxPerDrawCommand(maxPerDrawCommand)
{}
Iterator begin() const { return {m_end, m_maxPerDrawCommand, m_first}; }
Iterator end() const { return {m_end, m_maxPerDrawCommand, m_end}; }
private:
const uint32_t m_first;
const uint32_t m_end;
const uint32_t m_maxPerDrawCommand;
};
// depthStencil draws use repeating index patterns instead of instancing. Index
// patterns are mapped to real patches via the "baseVertex" parameter. Once the
// GPU adds in the baseVertex, the shader is able to derives all of its
// attributes from gl_VertexID.
//
// Since the index buffer only has a finite number of repetitions built in,
// large draws may also need to be broken up into chunks.
//
// This class walks the draws needed by a depthStencil batch and provides the
// direct arguments for the raw indexed-draw commands.
class DSIndexRangeChunker
{
public:
struct Draw
{
uint32_t indexCount;
int32_t baseVertex;
};
struct Iterator
{
public:
Draw operator*() const
{
auto [patchCount, firstPatch] = *chunk;
return {patchCount * indexCountPerPatch,
(static_cast<int32_t>(firstPatch) << patchStrideLog2) |
vertexFlags};
}
Iterator& operator++()
{
++chunk;
return *this;
}
bool operator==(const Iterator& other) const
{
assert(indexCountPerPatch == other.indexCountPerPatch);
assert(patchStrideLog2 == other.patchStrideLog2);
assert(vertexFlags == other.vertexFlags);
return chunk == other.chunk;
}
bool operator!=(const Iterator& other) const
{
return !(*this == other);
}
const uint32_t indexCountPerPatch;
const uint32_t patchStrideLog2;
const int32_t vertexFlags;
RangeChunker::Iterator chunk;
};
DSIndexRangeChunker(DrawType drawType,
uint32_t patchCount,
uint32_t firstPatch,
int32_t vertexFlags = 0) :
DSIndexRangeChunker(drawTypeSubmitsOuterCubicPatches(drawType),
patchCount,
firstPatch,
vertexFlags)
{}
Iterator begin() const
{
return {m_indexCountPerPatch,
m_patchStrideLog2,
m_vertexFlags,
m_chunker.begin()};
}
Iterator end() const
{
return {m_indexCountPerPatch,
m_patchStrideLog2,
m_vertexFlags,
m_chunker.end()};
}
private:
DSIndexRangeChunker(bool outerCubic,
uint32_t patchCount,
uint32_t firstPatch,
int32_t vertexFlags) :
m_chunker(patchCount, firstPatch, dsFillPatchMaxReps(outerCubic)),
m_indexCountPerPatch(dsFillPatchIndexCount(outerCubic)),
m_patchStrideLog2(outerCubic ? DSOuterCubicFillPatchStrideLog2
: DSMidpointFanFillPatchStrideLog2),
m_vertexFlags(outerCubic ? vertexFlags | DSFillVertexFlagOuterCubic
: vertexFlags)
{
// Make sure the vertex indices won't stomp on the flags.
assert((firstPatch + patchCount) << m_patchStrideLog2 <=
1 << DSFillVertexFlagsShift);
}
const RangeChunker m_chunker;
const uint32_t m_indexCountPerPatch;
const uint32_t m_patchStrideLog2;
const int32_t m_vertexFlags;
};
} // namespace rive::gpu