[mtl canvas, wip] implemented backprop
This commit is contained in:
parent
c4415aaeea
commit
a6c53649bd
|
@ -59,18 +59,26 @@ typedef struct mg_mtl_path_queue
|
||||||
using namespace metal;
|
using namespace metal;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
typedef enum { MG_MTL_OP_SEGMENT } mg_mtl_tile_op_kind;
|
typedef enum { MG_MTL_OP_START,
|
||||||
|
MG_MTL_OP_SEGMENT } mg_mtl_tile_op_kind;
|
||||||
|
|
||||||
typedef struct mg_mtl_tile_op
|
typedef struct mg_mtl_tile_op
|
||||||
{
|
{
|
||||||
mg_mtl_tile_op_kind kind;
|
mg_mtl_tile_op_kind kind;
|
||||||
int index;
|
int index;
|
||||||
int next;
|
int next;
|
||||||
|
union
|
||||||
|
{
|
||||||
|
bool crossRight;
|
||||||
|
int windingOffset;
|
||||||
|
};
|
||||||
} mg_mtl_tile_op;
|
} mg_mtl_tile_op;
|
||||||
|
|
||||||
typedef struct mg_mtl_tile_queue
|
typedef struct mg_mtl_tile_queue
|
||||||
{
|
{
|
||||||
|
atomic_int windingOffset;
|
||||||
atomic_int first;
|
atomic_int first;
|
||||||
|
int last;
|
||||||
|
|
||||||
} mg_mtl_tile_queue;
|
} mg_mtl_tile_queue;
|
||||||
|
|
||||||
|
|
|
@ -27,13 +27,18 @@ typedef struct mg_mtl_canvas_backend
|
||||||
|
|
||||||
id<MTLComputePipelineState> pathPipeline;
|
id<MTLComputePipelineState> pathPipeline;
|
||||||
id<MTLComputePipelineState> segmentPipeline;
|
id<MTLComputePipelineState> segmentPipeline;
|
||||||
|
id<MTLComputePipelineState> backpropPipeline;
|
||||||
id<MTLComputePipelineState> rasterPipeline;
|
id<MTLComputePipelineState> rasterPipeline;
|
||||||
id<MTLRenderPipelineState> blitPipeline;
|
id<MTLRenderPipelineState> blitPipeline;
|
||||||
|
|
||||||
id<MTLTexture> outTexture;
|
id<MTLTexture> outTexture;
|
||||||
|
|
||||||
id<MTLBuffer> pathBuffer;
|
int bufferIndex;
|
||||||
id<MTLBuffer> elementBuffer;
|
dispatch_semaphore_t bufferSemaphore;
|
||||||
|
|
||||||
|
id<MTLBuffer> pathBuffer[MG_MTL_INPUT_BUFFERS_COUNT];
|
||||||
|
id<MTLBuffer> elementBuffer[MG_MTL_INPUT_BUFFERS_COUNT];
|
||||||
|
|
||||||
id<MTLBuffer> segmentCountBuffer;
|
id<MTLBuffer> segmentCountBuffer;
|
||||||
id<MTLBuffer> segmentBuffer;
|
id<MTLBuffer> segmentBuffer;
|
||||||
id<MTLBuffer> pathQueueBuffer;
|
id<MTLBuffer> pathQueueBuffer;
|
||||||
|
@ -63,8 +68,11 @@ void mg_mtl_canvas_render(mg_canvas_backend* interface,
|
||||||
|
|
||||||
//TODO: update rolling buffers
|
//TODO: update rolling buffers
|
||||||
|
|
||||||
mg_mtl_path_elt* elementBufferData = (mg_mtl_path_elt*)[backend->elementBuffer contents];
|
dispatch_semaphore_wait(backend->bufferSemaphore, DISPATCH_TIME_FOREVER);
|
||||||
mg_mtl_path* pathBufferData = (mg_mtl_path*)[backend->pathBuffer contents];
|
backend->bufferIndex = (backend->bufferIndex + 1) % MG_MTL_INPUT_BUFFERS_COUNT;
|
||||||
|
|
||||||
|
mg_mtl_path_elt* elementBufferData = (mg_mtl_path_elt*)[backend->elementBuffer[backend->bufferIndex] contents];
|
||||||
|
mg_mtl_path* pathBufferData = (mg_mtl_path*)[backend->pathBuffer[backend->bufferIndex] contents];
|
||||||
|
|
||||||
//NOTE: fill renderer input buffers
|
//NOTE: fill renderer input buffers
|
||||||
int pathCount = 0;
|
int pathCount = 0;
|
||||||
|
@ -153,14 +161,14 @@ void mg_mtl_canvas_render(mg_canvas_backend* interface,
|
||||||
[pathEncoder setComputePipelineState: backend->pathPipeline];
|
[pathEncoder setComputePipelineState: backend->pathPipeline];
|
||||||
|
|
||||||
[pathEncoder setBytes:&pathCount length:sizeof(int) atIndex:0];
|
[pathEncoder setBytes:&pathCount length:sizeof(int) atIndex:0];
|
||||||
[pathEncoder setBuffer:backend->pathBuffer offset:0 atIndex:1];
|
[pathEncoder setBuffer:backend->pathBuffer[backend->bufferIndex] offset:0 atIndex:1];
|
||||||
[pathEncoder setBuffer:backend->pathQueueBuffer offset:0 atIndex:2];
|
[pathEncoder setBuffer:backend->pathQueueBuffer offset:0 atIndex:2];
|
||||||
[pathEncoder setBuffer:backend->tileQueueBuffer offset:0 atIndex:3];
|
[pathEncoder setBuffer:backend->tileQueueBuffer offset:0 atIndex:3];
|
||||||
[pathEncoder setBuffer:backend->tileQueueCountBuffer offset:0 atIndex:4];
|
[pathEncoder setBuffer:backend->tileQueueCountBuffer offset:0 atIndex:4];
|
||||||
[pathEncoder setBytes:&tileSize length:sizeof(int) atIndex:5];
|
[pathEncoder setBytes:&tileSize length:sizeof(int) atIndex:5];
|
||||||
|
|
||||||
MTLSize pathGridSize = MTLSizeMake(pathCount, 1, 1);
|
MTLSize pathGridSize = MTLSizeMake(pathCount, 1, 1);
|
||||||
MTLSize pathGroupSize = MTLSizeMake(64, 1, 1);
|
MTLSize pathGroupSize = MTLSizeMake([backend->pathPipeline maxTotalThreadsPerThreadgroup], 1, 1);
|
||||||
|
|
||||||
[pathEncoder dispatchThreads: pathGridSize threadsPerThreadgroup: pathGroupSize];
|
[pathEncoder dispatchThreads: pathGridSize threadsPerThreadgroup: pathGroupSize];
|
||||||
[pathEncoder endEncoding];
|
[pathEncoder endEncoding];
|
||||||
|
@ -171,7 +179,7 @@ void mg_mtl_canvas_render(mg_canvas_backend* interface,
|
||||||
[segmentEncoder setComputePipelineState: backend->segmentPipeline];
|
[segmentEncoder setComputePipelineState: backend->segmentPipeline];
|
||||||
|
|
||||||
[segmentEncoder setBytes:&eltCount length:sizeof(int) atIndex:0];
|
[segmentEncoder setBytes:&eltCount length:sizeof(int) atIndex:0];
|
||||||
[segmentEncoder setBuffer:backend->elementBuffer offset:0 atIndex:1];
|
[segmentEncoder setBuffer:backend->elementBuffer[backend->bufferIndex] offset:0 atIndex:1];
|
||||||
[segmentEncoder setBuffer:backend->segmentCountBuffer offset:0 atIndex:2];
|
[segmentEncoder setBuffer:backend->segmentCountBuffer offset:0 atIndex:2];
|
||||||
[segmentEncoder setBuffer:backend->segmentBuffer offset:0 atIndex:3];
|
[segmentEncoder setBuffer:backend->segmentBuffer offset:0 atIndex:3];
|
||||||
[segmentEncoder setBuffer:backend->pathQueueBuffer offset:0 atIndex:4];
|
[segmentEncoder setBuffer:backend->pathQueueBuffer offset:0 atIndex:4];
|
||||||
|
@ -181,18 +189,32 @@ void mg_mtl_canvas_render(mg_canvas_backend* interface,
|
||||||
[segmentEncoder setBytes:&tileSize length:sizeof(int) atIndex:8];
|
[segmentEncoder setBytes:&tileSize length:sizeof(int) atIndex:8];
|
||||||
|
|
||||||
MTLSize segmentGridSize = MTLSizeMake(mtlEltCount, 1, 1);
|
MTLSize segmentGridSize = MTLSizeMake(mtlEltCount, 1, 1);
|
||||||
MTLSize segmentGroupSize = MTLSizeMake(64, 1, 1);
|
MTLSize segmentGroupSize = MTLSizeMake([backend->segmentPipeline maxTotalThreadsPerThreadgroup], 1, 1);
|
||||||
|
|
||||||
[segmentEncoder dispatchThreads: segmentGridSize threadsPerThreadgroup: segmentGroupSize];
|
[segmentEncoder dispatchThreads: segmentGridSize threadsPerThreadgroup: segmentGroupSize];
|
||||||
[segmentEncoder endEncoding];
|
[segmentEncoder endEncoding];
|
||||||
|
|
||||||
|
//NOTE: backprop pass
|
||||||
|
id<MTLComputeCommandEncoder> backpropEncoder = [surface->commandBuffer computeCommandEncoder];
|
||||||
|
backpropEncoder.label = @"backprop pass";
|
||||||
|
[backpropEncoder setComputePipelineState: backend->backpropPipeline];
|
||||||
|
|
||||||
|
[backpropEncoder setBuffer:backend->pathQueueBuffer offset:0 atIndex:0];
|
||||||
|
[backpropEncoder setBuffer:backend->tileQueueBuffer offset:0 atIndex:1];
|
||||||
|
|
||||||
|
MTLSize backpropGroupSize = MTLSizeMake([backend->backpropPipeline maxTotalThreadsPerThreadgroup], 1, 1);
|
||||||
|
MTLSize backpropGridSize = MTLSizeMake(pathCount*backpropGroupSize.width, 1, 1);
|
||||||
|
|
||||||
|
[backpropEncoder dispatchThreads: backpropGridSize threadsPerThreadgroup: backpropGroupSize];
|
||||||
|
[backpropEncoder endEncoding];
|
||||||
|
|
||||||
//NOTE: raster pass
|
//NOTE: raster pass
|
||||||
id<MTLComputeCommandEncoder> rasterEncoder = [surface->commandBuffer computeCommandEncoder];
|
id<MTLComputeCommandEncoder> rasterEncoder = [surface->commandBuffer computeCommandEncoder];
|
||||||
rasterEncoder.label = @"raster pass";
|
rasterEncoder.label = @"raster pass";
|
||||||
[rasterEncoder setComputePipelineState: backend->rasterPipeline];
|
[rasterEncoder setComputePipelineState: backend->rasterPipeline];
|
||||||
|
|
||||||
[rasterEncoder setBytes:&pathCount length:sizeof(int) atIndex:0];
|
[rasterEncoder setBytes:&pathCount length:sizeof(int) atIndex:0];
|
||||||
[rasterEncoder setBuffer:backend->pathBuffer offset:0 atIndex:1];
|
[rasterEncoder setBuffer:backend->pathBuffer[backend->bufferIndex] offset:0 atIndex:1];
|
||||||
[rasterEncoder setBuffer:backend->segmentCountBuffer offset:0 atIndex:2];
|
[rasterEncoder setBuffer:backend->segmentCountBuffer offset:0 atIndex:2];
|
||||||
[rasterEncoder setBuffer:backend->segmentBuffer offset:0 atIndex:3];
|
[rasterEncoder setBuffer:backend->segmentBuffer offset:0 atIndex:3];
|
||||||
[rasterEncoder setBuffer:backend->pathQueueBuffer offset:0 atIndex:4];
|
[rasterEncoder setBuffer:backend->pathQueueBuffer offset:0 atIndex:4];
|
||||||
|
@ -230,6 +252,12 @@ void mg_mtl_canvas_render(mg_canvas_backend* interface,
|
||||||
vertexCount: 3 ];
|
vertexCount: 3 ];
|
||||||
[renderEncoder endEncoding];
|
[renderEncoder endEncoding];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//NOTE: finalize
|
||||||
|
[surface->commandBuffer addCompletedHandler:^(id<MTLCommandBuffer> commandBuffer)
|
||||||
|
{
|
||||||
|
dispatch_semaphore_signal(backend->bufferSemaphore);
|
||||||
|
}];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -241,11 +269,15 @@ void mg_mtl_canvas_destroy(mg_canvas_backend* interface)
|
||||||
{
|
{
|
||||||
[backend->pathPipeline release];
|
[backend->pathPipeline release];
|
||||||
[backend->segmentPipeline release];
|
[backend->segmentPipeline release];
|
||||||
|
[backend->backpropPipeline release];
|
||||||
[backend->rasterPipeline release];
|
[backend->rasterPipeline release];
|
||||||
[backend->blitPipeline release];
|
[backend->blitPipeline release];
|
||||||
|
|
||||||
[backend->pathBuffer release];
|
for(int i=0; i<MG_MTL_INPUT_BUFFERS_COUNT; i++)
|
||||||
[backend->elementBuffer release];
|
{
|
||||||
|
[backend->pathBuffer[i] release];
|
||||||
|
[backend->elementBuffer[i] release];
|
||||||
|
}
|
||||||
[backend->segmentCountBuffer release];
|
[backend->segmentCountBuffer release];
|
||||||
[backend->segmentBuffer release];
|
[backend->segmentBuffer release];
|
||||||
[backend->tileQueueBuffer release];
|
[backend->tileQueueBuffer release];
|
||||||
|
@ -296,6 +328,7 @@ mg_canvas_backend* mg_mtl_canvas_create(mg_surface surface)
|
||||||
}
|
}
|
||||||
id<MTLFunction> pathFunction = [library newFunctionWithName:@"mtl_path_setup"];
|
id<MTLFunction> pathFunction = [library newFunctionWithName:@"mtl_path_setup"];
|
||||||
id<MTLFunction> segmentFunction = [library newFunctionWithName:@"mtl_segment_setup"];
|
id<MTLFunction> segmentFunction = [library newFunctionWithName:@"mtl_segment_setup"];
|
||||||
|
id<MTLFunction> backpropFunction = [library newFunctionWithName:@"mtl_backprop"];
|
||||||
id<MTLFunction> rasterFunction = [library newFunctionWithName:@"mtl_raster"];
|
id<MTLFunction> rasterFunction = [library newFunctionWithName:@"mtl_raster"];
|
||||||
id<MTLFunction> vertexFunction = [library newFunctionWithName:@"mtl_vertex_shader"];
|
id<MTLFunction> vertexFunction = [library newFunctionWithName:@"mtl_vertex_shader"];
|
||||||
id<MTLFunction> fragmentFunction = [library newFunctionWithName:@"mtl_fragment_shader"];
|
id<MTLFunction> fragmentFunction = [library newFunctionWithName:@"mtl_fragment_shader"];
|
||||||
|
@ -309,6 +342,9 @@ mg_canvas_backend* mg_mtl_canvas_create(mg_surface surface)
|
||||||
backend->segmentPipeline = [metalSurface->device newComputePipelineStateWithFunction: segmentFunction
|
backend->segmentPipeline = [metalSurface->device newComputePipelineStateWithFunction: segmentFunction
|
||||||
error:&error];
|
error:&error];
|
||||||
|
|
||||||
|
backend->backpropPipeline = [metalSurface->device newComputePipelineStateWithFunction: backpropFunction
|
||||||
|
error:&error];
|
||||||
|
|
||||||
backend->rasterPipeline = [metalSurface->device newComputePipelineStateWithFunction: rasterFunction
|
backend->rasterPipeline = [metalSurface->device newComputePipelineStateWithFunction: rasterFunction
|
||||||
error:&error];
|
error:&error];
|
||||||
|
|
||||||
|
@ -343,14 +379,21 @@ mg_canvas_backend* mg_mtl_canvas_create(mg_surface surface)
|
||||||
backend->outTexture = [metalSurface->device newTextureWithDescriptor:texDesc];
|
backend->outTexture = [metalSurface->device newTextureWithDescriptor:texDesc];
|
||||||
|
|
||||||
//NOTE: create buffers
|
//NOTE: create buffers
|
||||||
|
|
||||||
|
backend->bufferSemaphore = dispatch_semaphore_create(MG_MTL_INPUT_BUFFERS_COUNT);
|
||||||
|
backend->bufferIndex = 0;
|
||||||
|
|
||||||
MTLResourceOptions bufferOptions = MTLResourceCPUCacheModeWriteCombined
|
MTLResourceOptions bufferOptions = MTLResourceCPUCacheModeWriteCombined
|
||||||
| MTLResourceStorageModeShared;
|
| MTLResourceStorageModeShared;
|
||||||
|
|
||||||
backend->pathBuffer = [metalSurface->device newBufferWithLength: MG_MTL_PATH_BUFFER_SIZE
|
for(int i=0; i<MG_MTL_INPUT_BUFFERS_COUNT; i++)
|
||||||
options: bufferOptions];
|
{
|
||||||
|
backend->pathBuffer[i] = [metalSurface->device newBufferWithLength: MG_MTL_PATH_BUFFER_SIZE
|
||||||
|
options: bufferOptions];
|
||||||
|
|
||||||
backend->elementBuffer = [metalSurface->device newBufferWithLength: MG_MTL_ELEMENT_BUFFER_SIZE
|
backend->elementBuffer[i] = [metalSurface->device newBufferWithLength: MG_MTL_ELEMENT_BUFFER_SIZE
|
||||||
options: bufferOptions];
|
options: bufferOptions];
|
||||||
|
}
|
||||||
|
|
||||||
bufferOptions = MTLResourceStorageModePrivate;
|
bufferOptions = MTLResourceStorageModePrivate;
|
||||||
backend->segmentBuffer = [metalSurface->device newBufferWithLength: MG_MTL_SEGMENT_BUFFER_SIZE
|
backend->segmentBuffer = [metalSurface->device newBufferWithLength: MG_MTL_SEGMENT_BUFFER_SIZE
|
||||||
|
|
|
@ -33,6 +33,8 @@ kernel void mtl_path_setup(constant int* pathCount [[buffer(0)]],
|
||||||
for(int i=0; i<tileCount; i++)
|
for(int i=0; i<tileCount; i++)
|
||||||
{
|
{
|
||||||
atomic_store_explicit(&tileQueues[i].first, -1, memory_order_relaxed);
|
atomic_store_explicit(&tileQueues[i].first, -1, memory_order_relaxed);
|
||||||
|
tileQueues[i].last = -1;
|
||||||
|
atomic_store_explicit(&tileQueues[i].windingOffset, 0, memory_order_relaxed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,12 +69,12 @@ bool mtl_is_left_of_segment(float2 p, const device mg_mtl_segment* seg)
|
||||||
float dy = p.y - seg->box.y;
|
float dy = p.y - seg->box.y;
|
||||||
|
|
||||||
if( (seg->config == MG_MTL_BR && dy > alpha*dx)
|
if( (seg->config == MG_MTL_BR && dy > alpha*dx)
|
||||||
||(seg->config == MG_MTL_TR && dy < ofs - alpha*dx))
|
||(seg->config == MG_MTL_TR && dy < ofs - alpha*dx))
|
||||||
{
|
{
|
||||||
isLeft = true;
|
isLeft = true;
|
||||||
}
|
}
|
||||||
else if( !(seg->config == MG_MTL_TL && dy < alpha*dx)
|
else if( !(seg->config == MG_MTL_TL && dy < alpha*dx)
|
||||||
&& !(seg->config == MG_MTL_BL && dy > ofs - 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
|
//Need implicit test, but for lines, we only have config BR or TR, so the test is always negative for now
|
||||||
}
|
}
|
||||||
|
@ -95,7 +97,7 @@ kernel void mtl_segment_setup(constant int* elementCount [[buffer(0)]],
|
||||||
float2 p0 = elt->p[0];
|
float2 p0 = elt->p[0];
|
||||||
float2 p3 = elt->p[3];
|
float2 p3 = elt->p[3];
|
||||||
|
|
||||||
if(elt->kind == MG_MTL_LINE && p0.y != p3.y)
|
if(elt->kind == MG_MTL_LINE)
|
||||||
{
|
{
|
||||||
int segIndex = atomic_fetch_add_explicit(segmentCount, 1, memory_order_relaxed);
|
int segIndex = atomic_fetch_add_explicit(segmentCount, 1, memory_order_relaxed);
|
||||||
device mg_mtl_segment* seg = &segmentBuffer[segIndex];
|
device mg_mtl_segment* seg = &segmentBuffer[segIndex];
|
||||||
|
@ -107,13 +109,15 @@ kernel void mtl_segment_setup(constant int* elementCount [[buffer(0)]],
|
||||||
max(p0.y, p3.y)};
|
max(p0.y, p3.y)};
|
||||||
|
|
||||||
if( (p3.x > p0.x && p3.y < p0.y)
|
if( (p3.x > p0.x && p3.y < p0.y)
|
||||||
||(p3.x <= p0.x && p3.y > p0.y))
|
||(p3.x <= p0.x && p3.y > p0.y))
|
||||||
{
|
{
|
||||||
seg->config = MG_MTL_TR;
|
seg->config = MG_MTL_TR;
|
||||||
}
|
}
|
||||||
else if( (p3.x > p0.x && p3.y > p0.y)
|
else if( (p3.x > p0.x && p3.y >= p0.y)
|
||||||
||(p3.x <= p0.x && p3.y < p0.y))
|
||(p3.x <= p0.x && p3.y <= p0.y))
|
||||||
{
|
{
|
||||||
|
//NOTE: it is important to include horizontal segments here, so that the mtl_is_left_of_segment() test
|
||||||
|
// becomes x > seg->box.x, in order to correctly detect right-crossing horizontal segments
|
||||||
seg->config = MG_MTL_BR;
|
seg->config = MG_MTL_BR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -139,15 +143,20 @@ kernel void mtl_segment_setup(constant int* elementCount [[buffer(0)]],
|
||||||
float(y + pathQueue->area.y + 1)} * float(tileSize[0]);
|
float(y + pathQueue->area.y + 1)} * float(tileSize[0]);
|
||||||
|
|
||||||
//NOTE: select two corners of tile box to test against the curve
|
//NOTE: select two corners of tile box to test against the curve
|
||||||
float2 testPoint[2] = {{tileBox.x, tileBox.y},
|
float2 testPoint0;
|
||||||
{tileBox.z, tileBox.w}};
|
float2 testPoint1;
|
||||||
if(seg->config == MG_MTL_BR || seg->config == MG_MTL_TL)
|
if(seg->config == MG_MTL_BL || seg->config == MG_MTL_TR)
|
||||||
{
|
{
|
||||||
testPoint[0] = (float2){tileBox.x, tileBox.w};
|
testPoint0 = (float2){tileBox.x, tileBox.y},
|
||||||
testPoint[1] = (float2){tileBox.z, tileBox.y};
|
testPoint1 = (float2){tileBox.z, tileBox.w};
|
||||||
}
|
}
|
||||||
bool test0 = mtl_is_left_of_segment(testPoint[0], seg);
|
else
|
||||||
bool test1 = mtl_is_left_of_segment(testPoint[1], seg);
|
{
|
||||||
|
testPoint0 = (float2){tileBox.z, tileBox.y};
|
||||||
|
testPoint1 = (float2){tileBox.x, tileBox.w};
|
||||||
|
}
|
||||||
|
bool test0 = mtl_is_left_of_segment(testPoint0, seg);
|
||||||
|
bool test1 = mtl_is_left_of_segment(testPoint1, seg);
|
||||||
|
|
||||||
//NOTE: the curve overlaps the tile only if test points are on opposite sides of segment
|
//NOTE: the curve overlaps the tile only if test points are on opposite sides of segment
|
||||||
if(test0 != test1)
|
if(test0 != test1)
|
||||||
|
@ -159,13 +168,160 @@ kernel void mtl_segment_setup(constant int* elementCount [[buffer(0)]],
|
||||||
op->index = segIndex;
|
op->index = segIndex;
|
||||||
|
|
||||||
int tileIndex = y*pathQueue->area.z + x;
|
int tileIndex = y*pathQueue->area.z + x;
|
||||||
op->next = atomic_exchange_explicit(&tileQueues[tileIndex].first, tileOpIndex, memory_order_relaxed);
|
device mg_mtl_tile_queue* tile = &tileQueues[tileIndex];
|
||||||
|
op->next = atomic_exchange_explicit(&tile->first, tileOpIndex, memory_order_relaxed);
|
||||||
|
if(op->next == -1)
|
||||||
|
{
|
||||||
|
tile->last = tileOpIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
//NOTE: if the segment crosses the tile's bottom boundary, update the tile's winding offset
|
||||||
|
// testPoint0 is always a bottom point. We select the other one and check if they are on
|
||||||
|
// opposite sides of the curve.
|
||||||
|
// We also need to check that the endpoints of the curve are on opposite sides of the bottom
|
||||||
|
// boundary.
|
||||||
|
float2 testPoint3;
|
||||||
|
if(seg->config == MG_MTL_BL || seg->config == MG_MTL_TR)
|
||||||
|
{
|
||||||
|
testPoint3 = (float2){tileBox.z, tileBox.y};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
testPoint3 = (float2){tileBox.x, tileBox.y};
|
||||||
|
}
|
||||||
|
bool test3 = mtl_is_left_of_segment(testPoint3, seg);
|
||||||
|
|
||||||
|
if( test0 != test3
|
||||||
|
&& seg->box.y <= testPoint0.y
|
||||||
|
&& seg->box.w > testPoint0.y)
|
||||||
|
{
|
||||||
|
atomic_fetch_add_explicit(&tile->windingOffset, seg->windingIncrement, memory_order_relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
|
//NOTE: if the segment crosses the right boundary, mark it. We reuse one of the previous tests
|
||||||
|
float2 top = {tileBox.z, tileBox.w};
|
||||||
|
bool testTop = mtl_is_left_of_segment(top, seg);
|
||||||
|
bool testBottom = (seg->config == MG_MTL_BL || seg->config == MG_MTL_TR)? test3 : test0;
|
||||||
|
|
||||||
|
if(testTop != testBottom
|
||||||
|
&& seg->box.x <= top.x
|
||||||
|
&& seg->box.z > top.x)
|
||||||
|
{
|
||||||
|
op->crossRight = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
op->crossRight = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
kernel void mtl_backprop(const device mg_mtl_path_queue* pathQueueBuffer [[buffer(0)]],
|
||||||
|
device mg_mtl_tile_queue* tileQueueBuffer [[buffer(1)]],
|
||||||
|
uint pathIndex [[threadgroup_position_in_grid]],
|
||||||
|
uint localID [[thread_position_in_threadgroup]])
|
||||||
|
{
|
||||||
|
threadgroup atomic_int nextRowIndex;
|
||||||
|
if(localID == 0)
|
||||||
|
{
|
||||||
|
atomic_store_explicit(&nextRowIndex, 0, memory_order_relaxed);
|
||||||
|
}
|
||||||
|
threadgroup_barrier(mem_flags::mem_threadgroup);
|
||||||
|
|
||||||
|
int rowIndex = 0;
|
||||||
|
const device mg_mtl_path_queue* pathQueue = &pathQueueBuffer[pathIndex];
|
||||||
|
device mg_mtl_tile_queue* tiles = &tileQueueBuffer[pathQueue->tileQueues];
|
||||||
|
int rowSize = pathQueue->area.z;
|
||||||
|
int rowCount = pathQueue->area.w;
|
||||||
|
|
||||||
|
rowIndex = atomic_fetch_add_explicit(&nextRowIndex, 1, memory_order_relaxed);
|
||||||
|
while(rowIndex < rowCount)
|
||||||
|
{
|
||||||
|
device mg_mtl_tile_queue* row = &tiles[rowIndex * rowSize];
|
||||||
|
int sum = 0;
|
||||||
|
for(int x = rowSize-1; x >= 0; x--)
|
||||||
|
{
|
||||||
|
device mg_mtl_tile_queue* tile = &row[x];
|
||||||
|
int offset = *(device int*)&tile->windingOffset;
|
||||||
|
*(device int*)(&tile->windingOffset) = sum;
|
||||||
|
sum += offset;
|
||||||
|
}
|
||||||
|
rowIndex = atomic_fetch_add_explicit(&nextRowIndex, 1, memory_order_relaxed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
kernel void mtl_merge(constant int* pathCount [[buffer(0)]],
|
||||||
|
const device mg_mtl_path* pathBuffer [[buffer(1)]],
|
||||||
|
const device mg_mtl_path_queue* pathQueueBuffer [[buffer(2)]],
|
||||||
|
const device mg_mtl_tile_queue* tileQueueBuffer [[buffer(3)]],
|
||||||
|
device mg_mtl_tile_op* tileOpBuffer [[buffer(4)]],
|
||||||
|
device atomic_int* tileOpCount [[buffer(5)]],
|
||||||
|
device int* screenTilesBuffer [[buffer(6)]],
|
||||||
|
uint2 threadCoord [[thread_position_in_grid]],
|
||||||
|
uint2 gridSize [[threads_per_grid]])
|
||||||
|
{
|
||||||
|
int2 tileCoord = int2(threadCoord);
|
||||||
|
int tileIndex = tileCoord.y * gridSize.x + tileCoord.x;
|
||||||
|
device int* nextLink = &screenTilesBuffer[tileIndex];
|
||||||
|
|
||||||
|
for(int pathIndex = 0; pathIndex < pathCount[0]; pathIndex++)
|
||||||
|
{
|
||||||
|
const device mg_mtl_path_queue* pathQueue = &pathQueueBuffer[pathIndex];
|
||||||
|
int2 pathTileCoord = tileCoord - pathQueue->area.xy;
|
||||||
|
|
||||||
|
if( pathTileCoord.x >= 0
|
||||||
|
&& pathTileCoord.x < pathQueue->area.z
|
||||||
|
&& pathTileCoord.y >= 0
|
||||||
|
&& pathTileCoord.y < pathQueue->area.w)
|
||||||
|
{
|
||||||
|
int pathTileIndex = pathTileCoord.y * pathQueue->area.z + pathTileCoord.x;
|
||||||
|
const device mg_mtl_tile_queue* tileQueue = &tileQueueBuffer[pathQueue->tileQueues + pathTileIndex];
|
||||||
|
|
||||||
|
int windingOffset = atomic_load_explicit(&tileQueue->windingOffset, memory_order_relaxed);
|
||||||
|
int opIndex = atomic_load_explicit(&tileQueue->first, memory_order_relaxed);
|
||||||
|
|
||||||
|
if((opIndex != -1) || (windingOffset & 1))
|
||||||
|
{
|
||||||
|
//NOTE: add path start op (with winding offset)
|
||||||
|
int startOpIndex = atomic_fetch_add_explicit(tileOpCount, 1, memory_order_relaxed);
|
||||||
|
device mg_mtl_tile_op* startOp = &tileOpBuffer[startOpIndex];
|
||||||
|
startOp->kind = MG_MTL_OP_START;
|
||||||
|
startOp->index = pathIndex;
|
||||||
|
startOp->windingOffset = windingOffset;
|
||||||
|
|
||||||
|
if(opIndex == -1)
|
||||||
|
{
|
||||||
|
//NOTE: the tile is fully covered by path fill. Insert start op,
|
||||||
|
// and if the fill color is opaque, trim tile list.
|
||||||
|
if(pathBuffer[pathIndex].color.a == 1)
|
||||||
|
{
|
||||||
|
screenTilesBuffer[tileIndex] = startOpIndex;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
*nextLink = startOpIndex;
|
||||||
|
}
|
||||||
|
nextLink = &startOp->next;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//NOTE: add start op
|
||||||
|
*nextLink = startOpIndex;
|
||||||
|
nextLink = &startOp->next;
|
||||||
|
|
||||||
|
//NOTE: chain path ops to end of tile list
|
||||||
|
device mg_mtl_tile_op* lastOp = &tileOpBuffer[opIndex];
|
||||||
|
*nextLink = opIndex;
|
||||||
|
nextLink = &lastOp->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
kernel void mtl_raster(constant int* pathCount [[buffer(0)]],
|
kernel void mtl_raster(constant int* pathCount [[buffer(0)]],
|
||||||
const device mg_mtl_path* pathBuffer [[buffer(1)]],
|
const device mg_mtl_path* pathBuffer [[buffer(1)]],
|
||||||
constant int* segCount [[buffer(2)]],
|
constant int* segCount [[buffer(2)]],
|
||||||
|
@ -182,8 +338,6 @@ kernel void mtl_raster(constant int* pathCount [[buffer(0)]],
|
||||||
int2 tileCoord = pixelCoord / tileSize[0];
|
int2 tileCoord = pixelCoord / tileSize[0];
|
||||||
|
|
||||||
float4 color = float4(0, 0, 0, 0);
|
float4 color = float4(0, 0, 0, 0);
|
||||||
int currentPath = 0;
|
|
||||||
int winding = 0;
|
|
||||||
|
|
||||||
if( (pixelCoord.x % tileSize[0] == 0)
|
if( (pixelCoord.x % tileSize[0] == 0)
|
||||||
||(pixelCoord.y % tileSize[0] == 0))
|
||(pixelCoord.y % tileSize[0] == 0))
|
||||||
|
@ -205,31 +359,17 @@ kernel void mtl_raster(constant int* pathCount [[buffer(0)]],
|
||||||
int pathTileIndex = pathTileCoord.y * pathQueue->area.z + pathTileCoord.x;
|
int pathTileIndex = pathTileCoord.y * pathQueue->area.z + pathTileCoord.x;
|
||||||
const device mg_mtl_tile_queue* tileQueue = &tileQueueBuffer[pathQueue->tileQueues + pathTileIndex];
|
const device mg_mtl_tile_queue* tileQueue = &tileQueueBuffer[pathQueue->tileQueues + pathTileIndex];
|
||||||
|
|
||||||
|
int winding = atomic_load_explicit(&tileQueue->windingOffset, memory_order_relaxed);
|
||||||
|
|
||||||
int opIndex = atomic_load_explicit(&tileQueue->first, memory_order_relaxed);
|
int opIndex = atomic_load_explicit(&tileQueue->first, memory_order_relaxed);
|
||||||
while(opIndex != -1)
|
while(opIndex != -1)
|
||||||
{
|
{
|
||||||
//outTexture.write(float4(0, 0, 1, 1), uint2(pixelCoord));
|
|
||||||
//return;
|
|
||||||
|
|
||||||
const device mg_mtl_tile_op* op = &tileOpBuffer[opIndex];
|
const device mg_mtl_tile_op* op = &tileOpBuffer[opIndex];
|
||||||
|
|
||||||
if(op->kind == MG_MTL_OP_SEGMENT)
|
if(op->kind == MG_MTL_OP_SEGMENT)
|
||||||
{
|
{
|
||||||
const device mg_mtl_segment* seg = &segmentBuffer[op->index];
|
const device mg_mtl_segment* seg = &segmentBuffer[op->index];
|
||||||
|
|
||||||
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.y >= seg->box.y && pixelCoord.y < seg->box.w)
|
||||||
{
|
{
|
||||||
if(pixelCoord.x < seg->box.x)
|
if(pixelCoord.x < seg->box.x)
|
||||||
|
@ -258,17 +398,31 @@ kernel void mtl_raster(constant int* pathCount [[buffer(0)]],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(op->crossRight)
|
||||||
|
{
|
||||||
|
if( (seg->config == MG_MTL_BR || seg->config == MG_MTL_TL)
|
||||||
|
&&(pixelCoord.y >= seg->box.w))
|
||||||
|
{
|
||||||
|
winding += seg->windingIncrement;
|
||||||
|
}
|
||||||
|
else if( (seg->config == MG_MTL_BL || seg->config == MG_MTL_TR)
|
||||||
|
&&(pixelCoord.y >= seg->box.y))
|
||||||
|
{
|
||||||
|
winding -= seg->windingIncrement;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
opIndex = op->next;
|
opIndex = op->next;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(winding & 1)
|
if(winding & 1)
|
||||||
{
|
{
|
||||||
float4 pathColor = pathBuffer[currentPath].color;
|
float4 pathColor = pathBuffer[pathIndex].color;
|
||||||
pathColor.rgb *= pathColor.a;
|
pathColor.rgb *= pathColor.a;
|
||||||
color = color*(1-pathColor.a) + pathColor;
|
color = color*(1-pathColor.a) + pathColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
outTexture.write(color, uint2(pixelCoord));
|
outTexture.write(color, uint2(pixelCoord));
|
||||||
|
|
Loading…
Reference in New Issue