[mtl canvas] render to an intermediate texture and acquire the drawable and blit to it only at the end of all compute passes

This commit is contained in:
Martin Fouilleul 2023-03-16 10:25:56 +01:00
parent f14f397c99
commit 1d36088302
2 changed files with 89 additions and 45 deletions

View File

@ -60,10 +60,10 @@ static const char* TEST_STRING =
"sit amet, malesuada enim. Mauris ultricies nibh orci."; "sit amet, malesuada enim. Mauris ultricies nibh orci.";
mg_font create_font() mg_font create_font(const char* path)
{ {
//NOTE(martin): create font //NOTE(martin): create font
str8 fontPath = mp_app_get_resource_path(mem_scratch(), "../resources/OpenSansLatinSubset.ttf"); str8 fontPath = mp_app_get_resource_path(mem_scratch(), path);
char* fontPathCString = str8_to_cstring(mem_scratch(), fontPath); char* fontPathCString = str8_to_cstring(mem_scratch(), fontPath);
FILE* fontFile = fopen(fontPathCString, "r"); FILE* fontFile = fopen(fontPathCString, "r");
@ -106,15 +106,26 @@ int main()
//NOTE: create surface, canvas and font //NOTE: create surface, canvas and font
mg_surface surface = mg_surface_create_for_window(window, MG_BACKEND_DEFAULT); mg_surface surface = mg_surface_create_for_window(window, MG_BACKEND_DEFAULT);
mg_surface_swap_interval(surface, 0); mg_surface_swap_interval(surface, 1);
mg_canvas canvas = mg_canvas_create(surface); mg_canvas canvas = mg_canvas_create(surface);
mg_font font = create_font(); const int fontCount = 3;
mg_font_extents extents = mg_font_get_extents(font); int fontIndex = 0;
f32 fontScale = mg_font_get_scale_for_em_pixels(font, 14); mg_font fonts[fontCount] = {create_font("../resources/OpenSansLatinSubset.ttf"),
create_font("../resources/CMUSerif-Roman.ttf"),
create_font("../resources/courier.ttf")};
f32 lineHeight = fontScale*(extents.ascent + extents.descent + extents.leading); mg_font_extents extents[fontCount];
f32 fontScales[fontCount];
f32 lineHeights[fontCount];
for(int i=0; i<fontCount; i++)
{
extents[i] = mg_font_get_extents(fonts[i]);
fontScales[i] = mg_font_get_scale_for_em_pixels(fonts[i], 14);
lineHeights[i] = fontScales[i]*(extents[i].ascent + extents[i].descent + extents[i].leading);
}
int codePointCount = utf8_codepoint_count_for_string(STR8((char*)TEST_STRING)); int codePointCount = utf8_codepoint_count_for_string(STR8((char*)TEST_STRING));
u32* codePoints = malloc_array(utf32, codePointCount); u32* codePoints = malloc_array(utf32, codePointCount);
@ -140,7 +151,7 @@ int main()
f32 zoom = 1; f32 zoom = 1;
f32 startX = 10; f32 startX = 10;
f32 startY = 10 + lineHeight; f32 startY = 10 + lineHeights[fontIndex];
while(!mp_should_quit()) while(!mp_should_quit())
{ {
@ -188,6 +199,14 @@ int main()
startY = mousePos.y/zoom - trackY; startY = mousePos.y/zoom - trackY;
} break; } break;
case MP_EVENT_KEYBOARD_KEY:
{
if(event.key.code == MP_KEY_SPACE && event.key.action == MP_KEY_PRESS)
{
fontIndex = (fontIndex+1)%fontCount;
}
} break;
default: default:
break; break;
} }
@ -219,7 +238,7 @@ int main()
mg_set_color_rgba(1, 1, 1, 1); mg_set_color_rgba(1, 1, 1, 1);
mg_clear(); mg_clear();
mg_set_font(font); mg_set_font(fonts[fontIndex]);
mg_set_font_size(14); mg_set_font_size(14);
mg_set_color_rgba(0, 0, 0, 1); mg_set_color_rgba(0, 0, 0, 1);
@ -239,12 +258,12 @@ int main()
} }
u32 glyphs[512]; u32 glyphs[512];
mg_font_get_glyph_indices(font, (str32){subIndex, codePoints+startIndex}, (str32){512, glyphs}); mg_font_get_glyph_indices(fonts[fontIndex], (str32){subIndex, codePoints+startIndex}, (str32){512, glyphs});
mg_glyph_outlines((str32){subIndex, glyphs}); mg_glyph_outlines((str32){subIndex, glyphs});
mg_fill(); mg_fill();
textY += lineHeight; textY += lineHeights[fontIndex];
mg_move_to(textX, textY); mg_move_to(textX, textY);
startIndex++; startIndex++;
@ -254,9 +273,9 @@ int main()
mg_matrix_pop(); mg_matrix_pop();
mg_set_color_rgba(0, 0, 1, 1); mg_set_color_rgba(0, 0, 1, 1);
mg_set_font(font); mg_set_font(fonts[fontIndex]);
mg_set_font_size(14); mg_set_font_size(14);
mg_move_to(10, contentRect.h - 10 - lineHeight); mg_move_to(10, contentRect.h - 10 - lineHeights[fontIndex]);
str8 text = str8_pushf(mem_scratch(), str8 text = str8_pushf(mem_scratch(),
"Test program: %i glyphs, frame time = %fs, fps = %f", "Test program: %i glyphs, frame time = %fs, fps = %f",
@ -290,7 +309,10 @@ int main()
} }
mg_font_destroy(font); for(int i=0; i<fontCount; i++)
{
mg_font_destroy(fonts[i]);
}
mg_canvas_destroy(canvas); mg_canvas_destroy(canvas);
mg_surface_destroy(surface); mg_surface_destroy(surface);
mp_window_destroy(window); mp_window_destroy(window);

View File

@ -31,6 +31,8 @@ typedef struct mg_mtl_canvas_backend
u32 indexBufferOffset; u32 indexBufferOffset;
u32 shapeBufferOffset; u32 shapeBufferOffset;
mg_color clearColor;
// permanent metal resources // permanent metal resources
id<MTLComputePipelineState> tilingPipeline; id<MTLComputePipelineState> tilingPipeline;
id<MTLComputePipelineState> sortingPipeline; id<MTLComputePipelineState> sortingPipeline;
@ -44,7 +46,7 @@ typedef struct mg_mtl_canvas_backend
dispatch_semaphore_t bufferSemaphore; dispatch_semaphore_t bufferSemaphore;
// textures and buffers // textures and buffers
id<MTLTexture> framebuffer; id<MTLTexture> backbuffer;
id<MTLTexture> outTexture; id<MTLTexture> outTexture;
id<MTLBuffer> shapeBuffer[MG_MTL_MAX_BUFFERS_IN_FLIGHT]; id<MTLBuffer> shapeBuffer[MG_MTL_MAX_BUFFERS_IN_FLIGHT];
@ -109,6 +111,7 @@ void mg_mtl_canvas_begin(mg_canvas_backend* interface, mg_color clearColor)
{ {
return; return;
} }
backend->clearColor = clearColor;
backend->vertexBufferOffset = 0; backend->vertexBufferOffset = 0;
backend->indexBufferOffset = 0; backend->indexBufferOffset = 0;
@ -121,30 +124,21 @@ void mg_mtl_canvas_begin(mg_canvas_backend* interface, mg_color clearColor)
@autoreleasepool @autoreleasepool
{ {
if(surface->commandBuffer == nil || surface->drawable == nil) MTLClearColor mtlClearColor = MTLClearColorMake(clearColor.r,
{ clearColor.g,
mg_mtl_surface_acquire_drawable_and_command_buffer(surface); clearColor.b,
} clearColor.a);
if(surface->drawable != nil)
{
backend->framebuffer = surface->drawable.texture;
MTLClearColor mtlClearColor = MTLClearColorMake(clearColor.r, clearColor.g, clearColor.b, clearColor.a);
MTLRenderPassDescriptor* renderPassDescriptor = [MTLRenderPassDescriptor renderPassDescriptor]; MTLRenderPassDescriptor* renderPassDescriptor = [MTLRenderPassDescriptor renderPassDescriptor];
renderPassDescriptor.colorAttachments[0].texture = surface->drawable.texture; renderPassDescriptor.colorAttachments[0].texture = backend->backbuffer;
renderPassDescriptor.colorAttachments[0].loadAction = MTLLoadActionClear; renderPassDescriptor.colorAttachments[0].loadAction = MTLLoadActionClear;
renderPassDescriptor.colorAttachments[0].clearColor = mtlClearColor; renderPassDescriptor.colorAttachments[0].clearColor = mtlClearColor;
renderPassDescriptor.colorAttachments[0].storeAction = MTLStoreActionStore; renderPassDescriptor.colorAttachments[0].storeAction = MTLStoreActionStore;
id<MTLRenderCommandEncoder> renderEncoder = [surface->commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor]; id<MTLRenderCommandEncoder> renderEncoder = [surface->commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor];
renderEncoder.label = @"clear pass";
[renderEncoder endEncoding]; [renderEncoder endEncoding];
} }
else
{
backend->framebuffer = nil;
}
}
} }
void mg_mtl_canvas_end(mg_canvas_backend* interface) void mg_mtl_canvas_end(mg_canvas_backend* interface)
@ -156,6 +150,32 @@ void mg_mtl_canvas_end(mg_canvas_backend* interface)
{ {
@autoreleasepool @autoreleasepool
{ {
mg_mtl_surface_acquire_drawable_and_command_buffer(surface);
if(surface->drawable != nil)
{
f32 scale = surface->mtlLayer.contentsScale;
MTLViewport viewport = {backend->viewPort.x * scale,
backend->viewPort.y * scale,
backend->viewPort.w * scale,
backend->viewPort.h * scale,
0,
1};
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];
renderEncoder.label = @"blit pass";
[renderEncoder setViewport: viewport];
[renderEncoder setRenderPipelineState: backend->renderPipeline];
[renderEncoder setFragmentTexture: backend->backbuffer atIndex: 0];
[renderEncoder drawPrimitives: MTLPrimitiveTypeTriangle
vertexStart: 0
vertexCount: 3 ];
[renderEncoder endEncoding];
[surface->commandBuffer addCompletedHandler:^(id<MTLCommandBuffer> commandBuffer) [surface->commandBuffer addCompletedHandler:^(id<MTLCommandBuffer> commandBuffer)
{ {
dispatch_semaphore_signal(backend->bufferSemaphore); dispatch_semaphore_signal(backend->bufferSemaphore);
@ -164,13 +184,14 @@ void mg_mtl_canvas_end(mg_canvas_backend* interface)
} }
} }
} }
}
void mg_mtl_canvas_draw_batch(mg_canvas_backend* interface, mg_image_data* image, u32 shapeCount, u32 vertexCount, u32 indexCount) void mg_mtl_canvas_draw_batch(mg_canvas_backend* interface, mg_image_data* image, u32 shapeCount, u32 vertexCount, u32 indexCount)
{ {
mg_mtl_canvas_backend* backend = (mg_mtl_canvas_backend*)interface; mg_mtl_canvas_backend* backend = (mg_mtl_canvas_backend*)interface;
mg_mtl_surface* surface = mg_mtl_canvas_get_surface(backend); mg_mtl_surface* surface = mg_mtl_canvas_get_surface(backend);
if(!surface || (backend->framebuffer == nil)) if(!surface || (backend->backbuffer == nil))
{ {
return; return;
} }
@ -264,7 +285,7 @@ void mg_mtl_canvas_draw_batch(mg_canvas_backend* interface, mg_image_data* image
[drawEncoder endEncoding]; [drawEncoder endEncoding];
//----------------------------------------------------------- //-----------------------------------------------------------
//NOTE(martin): blit texture to framebuffer //NOTE(martin): blit texture to backbuffer
//----------------------------------------------------------- //-----------------------------------------------------------
MTLViewport viewport = {backend->viewPort.x * scale, MTLViewport viewport = {backend->viewPort.x * scale,
@ -275,7 +296,7 @@ void mg_mtl_canvas_draw_batch(mg_canvas_backend* interface, mg_image_data* image
1}; 1};
MTLRenderPassDescriptor* renderPassDescriptor = [MTLRenderPassDescriptor renderPassDescriptor]; MTLRenderPassDescriptor* renderPassDescriptor = [MTLRenderPassDescriptor renderPassDescriptor];
renderPassDescriptor.colorAttachments[0].texture = backend->framebuffer; renderPassDescriptor.colorAttachments[0].texture = backend->backbuffer;
renderPassDescriptor.colorAttachments[0].loadAction = MTLLoadActionLoad; renderPassDescriptor.colorAttachments[0].loadAction = MTLLoadActionLoad;
renderPassDescriptor.colorAttachments[0].storeAction = MTLStoreActionStore; renderPassDescriptor.colorAttachments[0].storeAction = MTLStoreActionStore;
@ -453,6 +474,7 @@ mg_canvas_backend* mg_mtl_canvas_create(mg_surface surface)
texDesc.height = drawableSize.height; texDesc.height = drawableSize.height;
backend->outTexture = [metalSurface->device newTextureWithDescriptor:texDesc]; backend->outTexture = [metalSurface->device newTextureWithDescriptor:texDesc];
backend->backbuffer = [metalSurface->device newTextureWithDescriptor:texDesc];
//TODO(martin): retain ? //TODO(martin): retain ?
//----------------------------------------------------------- //-----------------------------------------------------------