orca/src/mtl_renderer.metal

138 lines
4.0 KiB
Metal
Raw Normal View History

#include<metal_stdlib>
#include<simd/simd.h>
#include<metal_simdgroup>
#include"mtl_renderer.h"
using namespace metal;
2023-03-28 13:10:05 +00:00
kernel void mtl_segment(constant int* elementCount [[buffer(0)]],
const device mg_mtl_path_elt* elementBuffer [[buffer(1)]],
device atomic_int* segmentCount [[buffer(2)]],
device mg_mtl_segment* segmentBuffer [[buffer(3)]],
uint eltIndex [[thread_position_in_grid]])
{
const device mg_mtl_path_elt* elt = &elementBuffer[eltIndex];
float2 p0 = elt->p[0];
float2 p3 = elt->p[3];
if(elt->kind == MG_MTL_LINE && p0.y != p3.y)
{
int segIndex = atomic_fetch_add_explicit(segmentCount, 1, memory_order_relaxed);
device mg_mtl_segment* seg = &segmentBuffer[segIndex];
seg->pathIndex = elt->pathIndex;
seg->box = (vector_float4){min(p0.x, p3.x),
min(p0.y, p3.y),
max(p0.x, p3.x),
max(p0.y, p3.y)};
if( (p3.x > p0.x && p3.y < p0.y)
||(p3.x <= p0.x && p3.y > p0.y))
{
seg->config = MG_MTL_TR;
}
else if( (p3.x > p0.x && p3.y > p0.y)
||(p3.x <= p0.x && p3.y < p0.y))
{
seg->config = MG_MTL_BR;
}
seg->windingIncrement = (p3.y > p0.y)? 1 : -1;
}
}
kernel void mtl_raster(constant int* pathCount [[buffer(0)]],
const device mg_mtl_path* pathBuffer [[buffer(1)]],
constant int* segCount [[buffer(2)]],
const device mg_mtl_segment* segmentBuffer [[buffer(3)]],
texture2d<float, access::write> outTexture [[texture(0)]],
uint2 threadCoord [[thread_position_in_grid]])
{
int2 pixelCoord = int2(threadCoord);
float4 color = float4(0, 0, 0, 0);
int currentPath = 0;
int winding = 0;
for(int segIndex = 0; segIndex < segCount[0]; segIndex++)
{
const device mg_mtl_segment* seg = &segmentBuffer[segIndex];
if(seg->pathIndex != currentPath)
{
//depending on winding number, update color
if(winding & 1)
{
float4 pathColor = pathBuffer[currentPath].color;
pathColor.rgb *= pathColor.a;
color = color*(1-pathColor.a) + pathColor;
}
currentPath = seg->pathIndex;
winding = 0;
}
if(pixelCoord.y >= seg->box.y && pixelCoord.y < seg->box.w)
{
if(pixelCoord.x < seg->box.x)
{
winding += seg->windingIncrement;
}
else if(pixelCoord.x < seg->box.z)
{
/*TODO: if pixel is on opposite size of diagonal as curve on the right, increment
otherwise if not on same size of diagonal as curve, do implicit test
*/
float alpha = (seg->box.w - seg->box.y)/(seg->box.z - seg->box.x);
float ofs = seg->box.w - seg->box.y;
float dx = pixelCoord.x - seg->box.x;
float dy = pixelCoord.y - seg->box.y;
if( (seg->config == MG_MTL_BR && dy > alpha*dx)
||(seg->config == MG_MTL_TR && dy < ofs - alpha*dx))
{
winding += seg->windingIncrement;
}
else if( !(seg->config == MG_MTL_TL && dy < alpha*dx)
&& !(seg->config == MG_MTL_BL && dy > ofs - alpha*dx))
{
//Need implicit test, but for lines, we only have config BR or TR, so the test is always negative for now
}
}
}
}
if(winding & 1)
{
float4 pathColor = pathBuffer[currentPath].color;
pathColor.rgb *= pathColor.a;
color = color*(1-pathColor.a) + pathColor;
}
outTexture.write(color, uint2(pixelCoord));
}
//------------------------------------------------------------------------------------
// Blit shader
//------------------------------------------------------------------------------------
struct vs_out
{
float4 pos [[position]];
float2 uv;
};
vertex vs_out mtl_vertex_shader(ushort vid [[vertex_id]])
{
vs_out out;
out.uv = float2((vid << 1) & 2, vid & 2);
out.pos = float4(out.uv * float2(2, -2) + float2(-1, 1), 0, 1);
return(out);
}
fragment float4 mtl_fragment_shader(vs_out i [[stage_in]], texture2d<float> tex [[texture(0)]])
{
constexpr sampler smp(mip_filter::nearest, mag_filter::linear, min_filter::linear);
return(tex.sample(smp, i.uv));
}