- Clear final surface to clear color, and enable blending layers onto it
- Fixed Batching bug where we were writing to vertex/shape buffers while the previous batch is using them
- Fixed bug where uvTransform uses transform of next shape instead of that of shape being finalized
This commit is contained in:
Martin Fouilleul 2023-02-27 11:56:04 +01:00
parent e66932e624
commit 9ed630fc55
4 changed files with 109 additions and 54 deletions

View File

@ -227,12 +227,13 @@ typedef struct mg_canvas_data
//NOTE: these are used at render time
mp_rect clip;
mg_mat2x3 transform;
mg_mat2x3 prevTransform; //TODO: [cleanup] use only one transform
mg_image currentImage;
mp_rect currentSrcRegion;
vec4 shapeExtents;
u32 nextShapeIndex;
u32 shapeFirstVertexIndex;
u32 vertexCount;
u32 indexCount;
@ -784,7 +785,8 @@ void mg_finalize_shape(mg_canvas_data* canvas)
0, srcRegion.h/destRegion.h, 0};
mg_mat2x3 userToDestRegion = {1, 0, -destRegion.x,
0, 1, -destRegion.y};
mg_mat2x3 screenToUser = mg_mat2x3_inv(canvas->transform);
mg_mat2x3 screenToUser = mg_mat2x3_inv(canvas->prevTransform);
mg_mat2x3 uvTransform = srcRegionToTexture;
uvTransform = mg_mat2x3_mul_m(uvTransform, destRegionToSrcRegion);
@ -794,7 +796,9 @@ void mg_finalize_shape(mg_canvas_data* canvas)
int index = canvas->nextShapeIndex-1;
mg_vertex_layout* layout = &canvas->backend->vertexLayout;
*(mg_mat2x3*)(layout->uvTransformBuffer + index*layout->uvTransformStride) = uvTransform;
}
canvas->prevTransform = canvas->transform;
}
u32 mg_next_shape(mg_canvas_data* canvas, mg_attributes* attributes)
@ -802,11 +806,11 @@ u32 mg_next_shape(mg_canvas_data* canvas, mg_attributes* attributes)
mg_finalize_shape(canvas);
canvas->currentSrcRegion = attributes->srcRegion;
canvas->shapeExtents = (vec4){FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX};
mg_vertex_layout* layout = &canvas->backend->vertexLayout;
int index = canvas->nextShapeIndex;
canvas->nextShapeIndex++;
canvas->shapeFirstVertexIndex = canvas->vertexCount;
mp_rect clip = {canvas->clip.x,
canvas->clip.y,
@ -2854,7 +2858,7 @@ void mg_do_clip_push(mg_canvas_data* canvas, mp_rect clip)
mg_clip_stack_push(canvas, r);
}
void mg_flush_batch(mg_canvas_data* canvas, mg_image_data* image)
void mg_draw_batch(mg_canvas_data* canvas, mg_image_data* image)
{
mg_finalize_shape(canvas);
@ -2863,9 +2867,9 @@ void mg_flush_batch(mg_canvas_data* canvas, mg_image_data* image)
canvas->backend->drawBatch(canvas->backend, image, canvas->nextShapeIndex, canvas->vertexCount, canvas->indexCount);
}
mg_reset_shape_index(canvas);
canvas->vertexCount = 0;
canvas->indexCount = 0;
canvas->shapeFirstVertexIndex = 0;
}
void mg_flush_commands(int primitiveCount, mg_primitive* primitives, mg_path_elt* pathElements)
@ -2898,14 +2902,13 @@ void mg_flush_commands(int primitiveCount, mg_primitive* primitives, mg_path_elt
mg_primitive* primitive = &(primitives[nextIndex]);
nextIndex++;
canvas->transform = primitive->transform;
if(i && primitive->attributes.image.h != canvas->currentImage.h)
{
mg_image_data* imageData = mg_image_data_from_handle(canvas, canvas->currentImage);
mg_flush_batch(canvas, imageData);
mg_draw_batch(canvas, imageData);
canvas->currentImage = primitive->attributes.image;
}
canvas->transform = primitive->transform;
switch(primitive->cmd)
{
@ -2994,7 +2997,7 @@ void mg_flush_commands(int primitiveCount, mg_primitive* primitives, mg_path_elt
exit_command_loop: ;
mg_image_data* imageData = mg_image_data_from_handle(canvas, canvas->currentImage);
mg_flush_batch(canvas, imageData);
mg_draw_batch(canvas, imageData);
canvas->backend->end(canvas->backend);

View File

@ -46,6 +46,10 @@ int main()
mg_image image = mg_image_create_from_file(imagePath, true);
vec2 imageSize = mg_image_size(image);
str8 imagePath2 = mp_app_get_resource_path(mem_scratch(), "../resources/Top512.png");
mg_image image2 = mg_image_create_from_file(imagePath2, true);
vec2 imageSize2 = mg_image_size(image2);
// start app
mp_window_bring_to_front(window);
mp_window_focus(window);
@ -82,7 +86,7 @@ int main()
mg_set_image(image);
mg_set_image_source_region((mp_rect){500, 500, 2000, 1400});
// mg_rectangle_fill(100, 100, imageSize.x/8, imageSize.y/8);
//mg_rectangle_fill(100, 100, imageSize.x/8, imageSize.y/8);
mg_move_to(0, 0);
mg_line_to(200, 0);
@ -96,6 +100,8 @@ int main()
mg_matrix_pop();
mg_image_draw(image2, (mp_rect){200, 200, 300, 300});
mg_flush();
mg_surface_present(surface);

View File

@ -26,6 +26,9 @@ typedef struct mg_mtl_canvas_backend
mg_surface surface;
mg_color clearColor;
u32 vertexBufferOffset;
u32 indexBufferOffset;
u32 shapeBufferOffset;
// permanent metal resources
id<MTLComputePipelineState> tilingPipeline;
@ -66,8 +69,65 @@ mg_mtl_surface* mg_mtl_canvas_get_surface(mg_mtl_canvas_backend* canvas)
return(res);
}
void mg_mtl_canvas_update_vertex_layout(mg_mtl_canvas_backend* backend)
{
char* vertexBase = (char*)[backend->vertexBuffer contents] + backend->vertexBufferOffset;
char* shapeBase = (char*)[backend->shapeBuffer contents] + backend->shapeBufferOffset;
char* indexBase = (char*)[backend->indexBuffer contents] + backend->indexBufferOffset;
backend->interface.vertexLayout = (mg_vertex_layout){
.maxVertexCount = MG_MTL_CANVAS_DEFAULT_BUFFER_LENGTH,
.maxIndexCount = MG_MTL_CANVAS_DEFAULT_BUFFER_LENGTH,
.cubicBuffer = vertexBase + offsetof(mg_vertex, cubic),
.cubicStride = sizeof(mg_vertex),
.posBuffer = vertexBase + offsetof(mg_vertex, pos),
.posStride = sizeof(mg_vertex),
.shapeIndexBuffer = vertexBase + offsetof(mg_vertex, shapeIndex),
.shapeIndexStride = sizeof(mg_vertex),
.colorBuffer = shapeBase + offsetof(mg_shape, color),
.colorStride = sizeof(mg_shape),
.clipBuffer = shapeBase + offsetof(mg_shape, clip),
.clipStride = sizeof(mg_shape),
.uvTransformBuffer = shapeBase + offsetof(mg_shape, uvTransform),
.uvTransformStride = sizeof(mg_shape),
.indexBuffer = indexBase,
.indexStride = sizeof(int)};
}
void mg_mtl_canvas_begin(mg_canvas_backend* interface)
{}
{
mg_mtl_canvas_backend* backend = (mg_mtl_canvas_backend*)interface;
mg_mtl_surface* surface = mg_mtl_canvas_get_surface(backend);
if(!surface)
{
return;
}
@autoreleasepool
{
if(surface->commandBuffer == nil || surface->commandBuffer == nil)
{
mg_mtl_surface_acquire_drawable_and_command_buffer(surface);
}
backend->vertexBufferOffset = 0;
backend->indexBufferOffset = 0;
backend->shapeBufferOffset = 0;
mg_mtl_canvas_update_vertex_layout(backend);
MTLClearColor clearColor = MTLClearColorMake(backend->clearColor.r, backend->clearColor.g, backend->clearColor.b, backend->clearColor.a);
MTLRenderPassDescriptor* renderPassDescriptor = [MTLRenderPassDescriptor renderPassDescriptor];
renderPassDescriptor.colorAttachments[0].texture = surface->drawable.texture;
renderPassDescriptor.colorAttachments[0].loadAction = MTLLoadActionClear;
renderPassDescriptor.colorAttachments[0].clearColor = clearColor;
renderPassDescriptor.colorAttachments[0].storeAction = MTLStoreActionStore;
id<MTLRenderCommandEncoder> renderEncoder = [surface->commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor];
[renderEncoder endEncoding];
}
}
void mg_mtl_canvas_end(mg_canvas_backend* interface)
{}
@ -90,11 +150,6 @@ void mg_mtl_canvas_draw_batch(mg_canvas_backend* interface, mg_image_data* image
@autoreleasepool
{
if(surface->commandBuffer == nil || surface->commandBuffer == nil)
{
mg_mtl_surface_acquire_drawable_and_command_buffer(surface);
}
ASSERT(indexCount * sizeof(i32) < [backend->indexBuffer length]);
f32 scale = surface->mtlLayer.contentsScale;
@ -112,9 +167,11 @@ void mg_mtl_canvas_draw_batch(mg_canvas_backend* interface, mg_image_data* image
//-----------------------------------------------------------
id<MTLComputeCommandEncoder> boxEncoder = [surface->commandBuffer computeCommandEncoder];
[boxEncoder setComputePipelineState: backend->boxingPipeline];
[boxEncoder setBuffer: backend->vertexBuffer offset:0 atIndex: 0];
[boxEncoder setBuffer: backend->indexBuffer offset:0 atIndex: 1];
[boxEncoder setBuffer: backend->shapeBuffer offset:0 atIndex: 2];
[boxEncoder setBuffer: backend->vertexBuffer offset:backend->vertexBufferOffset atIndex: 0];
[boxEncoder setBuffer: backend->indexBuffer offset:backend->indexBufferOffset atIndex: 1];
[boxEncoder setBuffer: backend->shapeBuffer offset:backend->shapeBufferOffset atIndex: 2];
[boxEncoder setBuffer: backend->triangleArray offset:0 atIndex: 3];
[boxEncoder setBuffer: backend->boxArray offset:0 atIndex: 4];
[boxEncoder setBytes: &scale length: sizeof(float) atIndex: 5];
@ -162,6 +219,7 @@ void mg_mtl_canvas_draw_batch(mg_canvas_backend* interface, mg_image_data* image
//-----------------------------------------------------------
//NOTE(martin): encode drawing pass
//-----------------------------------------------------------
//TODO: remove that
vector_float4 clearColorVec4 = {backend->clearColor.r, backend->clearColor.g, backend->clearColor.b, backend->clearColor.a};
id<MTLComputeCommandEncoder> encoder = [surface->commandBuffer computeCommandEncoder];
@ -175,8 +233,9 @@ void mg_mtl_canvas_draw_batch(mg_canvas_backend* interface, mg_image_data* image
useTexture = 1;
}
[encoder setBuffer: backend->vertexBuffer offset:0 atIndex: 0];
[encoder setBuffer: backend->shapeBuffer offset:0 atIndex: 1];
[boxEncoder setBuffer: backend->vertexBuffer offset:backend->vertexBufferOffset atIndex: 0];
[boxEncoder setBuffer: backend->shapeBuffer offset:backend->shapeBufferOffset atIndex: 1];
[encoder setBuffer: backend->tileCounters offset:0 atIndex: 2];
[encoder setBuffer: backend->tilesArray offset:0 atIndex: 3];
[encoder setBuffer: backend->triangleArray offset:0 atIndex: 4];
@ -194,7 +253,7 @@ void mg_mtl_canvas_draw_batch(mg_canvas_backend* interface, mg_image_data* image
[encoder endEncoding];
//-----------------------------------------------------------
//NOTE(martin): acquire drawable, create render encoder to blit texture to framebuffer
//NOTE(martin): blit texture to framebuffer
//-----------------------------------------------------------
MTLViewport viewport = {backend->viewPort.x * scale,
@ -206,6 +265,7 @@ void mg_mtl_canvas_draw_batch(mg_canvas_backend* interface, mg_image_data* image
MTLRenderPassDescriptor* renderPassDescriptor = [MTLRenderPassDescriptor renderPassDescriptor];
renderPassDescriptor.colorAttachments[0].texture = surface->drawable.texture;
renderPassDescriptor.colorAttachments[0].loadAction = MTLLoadActionLoad;
renderPassDescriptor.colorAttachments[0].storeAction = MTLStoreActionStore;
id<MTLRenderCommandEncoder> renderEncoder = [surface->commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor];
@ -217,6 +277,12 @@ void mg_mtl_canvas_draw_batch(mg_canvas_backend* interface, mg_image_data* image
vertexCount: 3 ];
[renderEncoder endEncoding];
}
backend->vertexBufferOffset += vertexCount * sizeof(mg_vertex);
backend->indexBufferOffset += indexCount * sizeof(int);
backend->shapeBufferOffset += shapeCount * sizeof(mg_shape);
mg_mtl_canvas_update_vertex_layout(backend);
}
/*
@ -251,33 +317,6 @@ void mg_mtl_canvas_viewport(mg_canvas_backend* interface, mp_rect viewPort)
}
*/
void mg_mtl_canvas_update_vertex_layout(mg_mtl_canvas_backend* backend)
{
char* vertexBase = (char*)[backend->vertexBuffer contents];
char* shapeBase = (char*)[backend->shapeBuffer contents];
char* indexBase = (char*)[backend->indexBuffer contents];
backend->interface.vertexLayout = (mg_vertex_layout){
.maxVertexCount = MG_MTL_CANVAS_DEFAULT_BUFFER_LENGTH,
.maxIndexCount = MG_MTL_CANVAS_DEFAULT_BUFFER_LENGTH,
.cubicBuffer = vertexBase + offsetof(mg_vertex, cubic),
.cubicStride = sizeof(mg_vertex),
.posBuffer = vertexBase + offsetof(mg_vertex, pos),
.posStride = sizeof(mg_vertex),
.shapeIndexBuffer = vertexBase + offsetof(mg_vertex, shapeIndex),
.shapeIndexStride = sizeof(mg_vertex),
.colorBuffer = shapeBase + offsetof(mg_shape, color),
.colorStride = sizeof(mg_shape),
.clipBuffer = shapeBase + offsetof(mg_shape, clip),
.clipStride = sizeof(mg_shape),
.uvTransformBuffer = shapeBase + offsetof(mg_shape, uvTransform),
.uvTransformStride = sizeof(mg_shape),
.indexBuffer = indexBase,
.indexStride = sizeof(int)};
}
void mg_mtl_canvas_destroy(mg_canvas_backend* interface)
{
mg_mtl_canvas_backend* backend = (mg_mtl_canvas_backend*)interface;
@ -432,7 +471,6 @@ mg_canvas_backend* mg_mtl_canvas_create(mg_surface surface)
id<MTLBlitCommandEncoder> blitEncoder = [commandBuffer blitCommandEncoder];
[blitEncoder fillBuffer: backend->tileCounters range: NSMakeRange(0, RENDERER_MAX_TILES*sizeof(uint)) value: 0];
[blitEncoder endEncoding];
[commandBuffer commit];
//-----------------------------------------------------------
//NOTE(martin): load the library
@ -499,6 +537,14 @@ mg_canvas_backend* mg_mtl_canvas_create(mg_surface surface)
pipelineStateDescriptor.vertexFunction = vertexFunction;
pipelineStateDescriptor.fragmentFunction = fragmentFunction;
pipelineStateDescriptor.colorAttachments[0].pixelFormat = metalSurface->mtlLayer.pixelFormat;
pipelineStateDescriptor.colorAttachments[0].blendingEnabled = YES;
pipelineStateDescriptor.colorAttachments[0].rgbBlendOperation = MTLBlendOperationAdd;
pipelineStateDescriptor.colorAttachments[0].sourceRGBBlendFactor = MTLBlendFactorSourceAlpha;
pipelineStateDescriptor.colorAttachments[0].destinationRGBBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
pipelineStateDescriptor.colorAttachments[0].alphaBlendOperation = MTLBlendOperationAdd;
pipelineStateDescriptor.colorAttachments[0].sourceAlphaBlendFactor = MTLBlendFactorZero;
pipelineStateDescriptor.colorAttachments[0].destinationAlphaBlendFactor = MTLBlendFactorOne;
// create render pipeline
backend->renderPipeline = [metalSurface->device newRenderPipelineStateWithDescriptor: pipelineStateDescriptor error:&err];

View File

@ -23,7 +23,7 @@ vertex vs_out VertexShader(ushort vid [[vertex_id]])
fragment float4 FragmentShader(vs_out i [[stage_in]], texture2d<float> tex [[texture(0)]])
{
constexpr sampler smp(mip_filter::nearest, mag_filter::linear, min_filter::linear);
return(float4(tex.sample(smp, i.uv).rgb, 1));
return(tex.sample(smp, i.uv));
}
@ -239,8 +239,8 @@ kernel void RenderKernel(texture2d<float, access::write> outTexture [[texture(0)
{
zIndices[i] = -1;
flipCounts[i] = 0;
pixelColors[i] = *clearColor;
nextColors[i] = *clearColor;
pixelColors[i] = float4(0, 0, 0, 0);
nextColors[i] = float4(0, 0, 0, 0);
}
for(uint tileBufferIndex=0; tileBufferIndex < tileBufferSize; tileBufferIndex++)
@ -340,6 +340,6 @@ kernel void RenderKernel(texture2d<float, access::write> outTexture [[texture(0)
}
out += pixelColors[i];
}
out = float4(out.xyz/6, 1);
out = out/6.;
outTexture.write(out, gid);
}