#pragma only_renderers d3d11 vulkan metal glcore
#pragma kernel MarkBilinearOverlaps
#define UINT_MAX 4294967295u
#define NO_CHART UINT_MAX
uint _TileSize;
uint _TileX;
uint _TileY;
uint _InstanceIndex;
uint _EdgeCount;
StructuredBuffer<float4> _TriangleEdges; // Per thread. Edge of each triangle (startX, startY, endX, endY)
StructuredBuffer<uint> _ChartIndices; // Per thread chart index.
uint _TextureSize;
RWStructuredBuffer<uint> _PerPixelChart; // Per pixel chart index.
RWStructuredBuffer<uint> _OverlapPixels; // Per pixel, no overlap = 0, overlap = 1
RWStructuredBuffer<uint> _OverlapInstances; // Per instance, no overlap = 0, overlap = 1
[numthreads(128,1,1)]
void MarkBilinearOverlaps(uint3 id : SV_DispatchThreadID)
{
if (id.x >= _EdgeCount)
return;
// Tile the dispatch so we don't do too much work on each thread
uint2 tilePos = uint2(_TileX, _TileY) * _TileSize;
uint2 tileEnd = tilePos + _TileSize;
// Fetch chart and triangle edge for this thread.
uint chartIdx = _ChartIndices[id.x];
float4 edge = _TriangleEdges[id.x];
float2 startPos = edge.xy;
float2 endPos = edge.zw;
uint2 aabbStartPos = max(uint2(floor(min(startPos, endPos) - 0.5f)), 0);
uint2 aabbEndPos = min(uint2(ceil(max(startPos, endPos) + 0.5f)), _TextureSize-1);
aabbStartPos = clamp(aabbStartPos, tilePos, tileEnd);
aabbEndPos = clamp(aabbEndPos, tilePos, tileEnd);
uint2 extent = aabbEndPos - aabbStartPos;
// The edge is described by a line A*x + B*y + C = 0.
float lineA = endPos.y - startPos.y;
float lineB = startPos.x - endPos.x;
float lineC = startPos.y * endPos.x - startPos.x * endPos.y;
// For every pixel in the AABB...
[allow_uav_condition]
for (uint y = 0; y < extent.y; y++)
{
[allow_uav_condition]
for (uint x = 0; x < extent.x; x++)
{
uint2 pixelCoord = uint2(x, y) + aabbStartPos;
float2 bottomLeftCornerPos = pixelCoord - 0.5f;
// For each corner of the bilinear neighborhood...
bool4 cornerMask = false;
[unroll]
for (uint corner = 0; corner < 4; corner++)
{
uint offsetX = 2 * (corner & 1);
uint offsetY = corner & 2;
float2 cornerPos = bottomLeftCornerPos + float2(offsetX, offsetY);
// Check if the corner is below or above the edge.
cornerMask[corner] = lineA * cornerPos.x + lineB * cornerPos.y + lineC > 0;
}
// If some, but not all, corners of the bilinear neighborhood are above the edge,
// the bilinear neighborhood is occupied - mark it.
[branch]
if (any(cornerMask) && !all(cornerMask))
{
uint pixelIndex = pixelCoord.y * _TextureSize + pixelCoord.x;
// Try to write assuming there was no chart already present.
uint prevChart;
InterlockedCompareExchange(_PerPixelChart[pixelIndex], NO_CHART, chartIdx, prevChart);
// If there was already a chart present, and it wasn't this threads chart, we have an overlap, so mark it.
if (prevChart != NO_CHART && prevChart != chartIdx)
{
_OverlapPixels[pixelIndex] = 1;
_OverlapInstances[_InstanceIndex] = 1;
}
}
}
}
}