diff --git a/examples/smooth_resize/build.sh b/examples/smooth_resize/build.sh new file mode 100755 index 0000000..f587549 --- /dev/null +++ b/examples/smooth_resize/build.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +BINDIR=../../bin +RESDIR=../../resources +SRCDIR=../../src + +INCLUDES="-I$SRCDIR -I$SRCDIR/util -I$SRCDIR/platform -I$SRCDIR/app" +LIBS="-L$BINDIR -lmilepost" +FLAGS="-mmacos-version-min=10.15.4 -DDEBUG -DLOG_COMPILE_DEBUG" + +clang -g $FLAGS $LIBS $INCLUDES -o $BINDIR/example_smooth_resize main.c diff --git a/examples/smooth_resize/main.c b/examples/smooth_resize/main.c new file mode 100644 index 0000000..3d1296a --- /dev/null +++ b/examples/smooth_resize/main.c @@ -0,0 +1,278 @@ +/************************************************************//** +* +* @file: main.cpp +* @author: Martin Fouilleul +* @date: 30/07/2022 +* @revision: +* +*****************************************************************/ +#include +#include +#include + +#define _USE_MATH_DEFINES //NOTE: necessary for MSVC +#include + +#include"milepost.h" + +#include + +#define LOG_SUBSYSTEM "Main" + + +mg_font create_font() +{ + //NOTE(martin): create font + str8 fontPath = mp_app_get_resource_path(mem_scratch(), "../resources/OpenSansLatinSubset.ttf"); + char* fontPathCString = str8_to_cstring(mem_scratch(), fontPath); + + FILE* fontFile = fopen(fontPathCString, "r"); + if(!fontFile) + { + LOG_ERROR("Could not load font file '%s': %s\n", fontPathCString, strerror(errno)); + return(mg_font_nil()); + } + unsigned char* fontData = 0; + fseek(fontFile, 0, SEEK_END); + u32 fontDataSize = ftell(fontFile); + rewind(fontFile); + fontData = (unsigned char*)malloc(fontDataSize); + fread(fontData, 1, fontDataSize, fontFile); + fclose(fontFile); + + unicode_range ranges[5] = {UNICODE_RANGE_BASIC_LATIN, + UNICODE_RANGE_C1_CONTROLS_AND_LATIN_1_SUPPLEMENT, + UNICODE_RANGE_LATIN_EXTENDED_A, + UNICODE_RANGE_LATIN_EXTENDED_B, + UNICODE_RANGE_SPECIALS}; + + mg_font font = mg_font_create_from_memory(fontDataSize, fontData, 5, ranges); + free(fontData); + + return(font); +} + +typedef struct app_data +{ + mp_window window; + mg_surface surface; + mg_canvas canvas; + mg_font font; + + f32 x; + f32 y; + f32 dx; + f32 dy; + f32 speed; + f32 frameTime; + +} app_data; + +void process_event(app_data* app, mp_event event) +{ + switch(event.type) + { + case MP_EVENT_WINDOW_CLOSE: + { + mp_request_quit(); + } break; + + case MP_EVENT_WINDOW_RESIZE: + { + mp_rect frame = {0, 0, event.frame.rect.w, event.frame.rect.h}; + mg_surface_set_frame(app->surface, frame); + } break; + + case MP_EVENT_KEYBOARD_KEY: + { + if(event.key.action == MP_KEY_PRESS || event.key.action == MP_KEY_REPEAT) + { + f32 factor = (event.key.mods & MP_KEYMOD_SHIFT) ? 10 : 1; + + if(event.key.code == MP_KEY_LEFT) + { + app->x-=0.3*factor; + } + else if(event.key.code == MP_KEY_RIGHT) + { + app->x+=0.3*factor; + } + else if(event.key.code == MP_KEY_UP) + { + app->y-=0.3*factor; + } + else if(event.key.code == MP_KEY_DOWN) + { + app->y+=0.3*factor; + } + } + } break; + + default: + break; + } +} + +void update_and_render(app_data* app) +{ + mp_rect contentRect = mp_window_get_content_rect(app->window); + + if(app->x-200 < 0) + { + app->x = 200; + app->dx = app->speed; + } + if(app->x+200 > contentRect.w) + { + app->x = contentRect.w - 200; + app->dx = -app->speed; + } + if(app->y-200 < 0) + { + app->y = 200; + app->dy = app->speed; + } + if(app->y+200 > contentRect.h) + { + app->y = contentRect.h - 200; + app->dy = -app->speed; + } + app->x += app->dx; + app->y += app->dy; + + f64 startTime = mp_get_time(MP_CLOCK_MONOTONIC); + + mg_surface_prepare(app->surface); + + // background + mg_set_color_rgba(0, 1, 1, 1); + mg_clear(); + + // head + mg_set_color_rgba(1, 1, 0, 1); + + mg_circle_fill(app->x, app->y, 200); + + // smile + f32 frown = app->frameTime > 0.033 ? -100 : 0; + + mg_set_color_rgba(0, 0, 0, 1); + mg_set_width(20); + mg_move_to(app->x-100, app->y+100); + mg_cubic_to(app->x-50, app->y+150+frown, app->x+50, app->y+150+frown, app->x+100, app->y+100); + mg_stroke(); + + // eyes + mg_ellipse_fill(app->x-70, app->y-50, 30, 50); + mg_ellipse_fill(app->x+70, app->y-50, 30, 50); + + // text + mg_set_color_rgba(0, 0, 1, 1); + mg_set_font(app->font); + mg_set_font_size(12); + mg_move_to(50, 600-50); + + str8 text = str8_pushf(mem_scratch(), + "Milepost vector graphics test program (frame time = %fs, fps = %f)...", + app->frameTime, + 1./app->frameTime); + mg_text_outlines(text); + mg_fill(); + + printf("Milepost vector graphics test program (frame time = %fs, fps = %f)...\n", + app->frameTime, + 1./app->frameTime); + + mg_flush(); + mg_surface_present(app->surface); + + mem_arena_clear(mem_scratch()); + app->frameTime = mp_get_time(MP_CLOCK_MONOTONIC) - startTime; +} + +void* render(void* user) +{ + app_data* app = (app_data*)user; + + mg_canvas_prepare(app->canvas); + + while(!mp_should_quit()) + { + mp_event event = {0}; + while(mp_next_event(&event)) + { + process_event(app, event); + } + update_and_render(app); + } + + return(0); +} + +int main() +{ + LogLevel(LOG_LEVEL_WARNING); + + mp_init(); + mp_clock_init(); //TODO put that in mp_init()? + + mp_rect windowRect = {.x = 100, .y = 100, .w = 810, .h = 610}; + mp_window window = mp_window_create(windowRect, "test", 0); + + //NOTE: create surface + mg_surface surface = mg_surface_create_for_window(window, MG_BACKEND_DEFAULT); + mg_surface_swap_interval(surface, 0); + + //TODO: create canvas + mg_canvas canvas = mg_canvas_create(surface); + + if(mg_canvas_is_nil(canvas)) + { + printf("Error: couldn't create canvas\n"); + return(-1); + } + + mg_font font = create_font(); + + // start app + mp_window_bring_to_front(window); + mp_window_focus(window); + + //TODO: start thread + app_data app = {.window = window, + .surface = surface, + .canvas = canvas, + .font = font, + .x = 400, + .y = 300, + .dx = 0, + .dy = 0}; + + pthread_t renderThread; + pthread_create(&renderThread, 0, render, &app); + + while(!mp_should_quit()) + { + mp_pump_events(0); + /* + mp_event event = {0}; + while(mp_next_event(&event)) + { + process_event(&app, event); + } + update_and_render(&app); + //*/ + } + + void* res; + pthread_join(renderThread, &res); + + mg_font_destroy(font); + mg_canvas_destroy(canvas); + mg_surface_destroy(surface); + mp_window_destroy(window); + + mp_terminate(); + + return(0); +} diff --git a/examples/tiger/main.c b/examples/tiger/main.c index 56a90ff..3c769dd 100644 --- a/examples/tiger/main.c +++ b/examples/tiger/main.c @@ -107,6 +107,12 @@ int main() mp_request_quit(); } break; + case MP_EVENT_WINDOW_RESIZE: + { + mp_rect frame = {0, 0, event.frame.rect.w, event.frame.rect.h}; + mg_surface_set_frame(surface, frame); + } break; + case MP_EVENT_MOUSE_BUTTON: { if(event.key.code == MP_MOUSE_LEFT) diff --git a/src/graphics.c b/src/graphics.c index 7362f9f..3f924aa 100644 --- a/src/graphics.c +++ b/src/graphics.c @@ -3657,37 +3657,18 @@ void mg_rounded_rectangle_stroke(f32 x, f32 y, f32 w, f32 h, f32 r) } } -void mg_circle_fill(f32 x, f32 y, f32 r) -{ - mg_canvas_data* canvas = __mgCurrentCanvas; - if(canvas) - { - mg_primitive primitive = {.cmd = MG_CMD_ELLIPSE_FILL, - .rect = (mp_rect){x-r, y-r, 2*r, 2*r}}; - mg_push_command(canvas, primitive); - } -} - -void mg_circle_stroke(f32 x, f32 y, f32 r) -{ - mg_canvas_data* canvas = __mgCurrentCanvas; - if(canvas) - { - mg_primitive primitive = {.cmd = MG_CMD_ELLIPSE_STROKE, - .rect = (mp_rect){x-r, y-r, 2*r, 2*r}}; - mg_push_command(canvas, primitive); - } -} - void mg_ellipse_fill(f32 x, f32 y, f32 rx, f32 ry) { - mg_canvas_data* canvas = __mgCurrentCanvas; - if(canvas) - { - mg_primitive primitive = {.cmd = MG_CMD_ELLIPSE_FILL, - .rect = (mp_rect){x-rx, y-ry, 2*rx, 2*ry}}; - mg_push_command(canvas, primitive); - } + f32 cx = rx*4*(sqrt(2)-1)/3; + f32 cy = ry*4*(sqrt(2)-1)/3; + + mg_move_to(x-rx, y); + mg_cubic_to(x-rx, y+cy, x-cx, y+ry, x, y+ry); + mg_cubic_to(x+cx, y+ry, x+rx, y+cy, x+rx, y); + mg_cubic_to(x+rx, y-cy, x+cx, y-ry, x, y-ry); + mg_cubic_to(x-cx, y-ry, x-rx, y-cy, x-rx, y); + + mg_fill(); } void mg_ellipse_stroke(f32 x, f32 y, f32 rx, f32 ry) @@ -3701,6 +3682,16 @@ void mg_ellipse_stroke(f32 x, f32 y, f32 rx, f32 ry) } } +void mg_circle_fill(f32 x, f32 y, f32 r) +{ + mg_ellipse_fill(x, y, r, r); +} + +void mg_circle_stroke(f32 x, f32 y, f32 r) +{ + mg_ellipse_stroke(x, y, r, r); +} + //TODO: change to arc_to? void mg_arc(f32 x, f32 y, f32 r, f32 arcAngle, f32 startAngle) { diff --git a/src/mtl_renderer.m b/src/mtl_renderer.m index 87bec63..183405b 100644 --- a/src/mtl_renderer.m +++ b/src/mtl_renderer.m @@ -56,6 +56,7 @@ typedef struct mg_mtl_canvas_backend id screenTilesBuffer; int msaaCount; + vec2 frameSize; } mg_mtl_canvas_backend; @@ -768,6 +769,45 @@ void mg_mtl_render_batch(mg_mtl_canvas_backend* backend, } } +void mg_mtl_canvas_resize(mg_mtl_canvas_backend* backend, vec2 size) +{ + mg_mtl_surface* surface = (mg_mtl_surface*)mg_surface_data_from_handle(backend->surface); + if(surface) + { + @autoreleasepool + { + if(backend->screenTilesBuffer) + { + [backend->screenTilesBuffer release]; + backend->screenTilesBuffer = nil; + } + int tileSize = MG_MTL_TILE_SIZE; + int nTilesX = (int)(size.x + tileSize - 1)/tileSize; + int nTilesY = (int)(size.y + tileSize - 1)/tileSize; + MTLResourceOptions bufferOptions = MTLResourceStorageModePrivate; + backend->screenTilesBuffer = [surface->device newBufferWithLength: nTilesX*nTilesY*sizeof(int) + options: bufferOptions]; + + if(backend->outTexture) + { + [backend->outTexture release]; + backend->outTexture = nil; + } + MTLTextureDescriptor* texDesc = [[MTLTextureDescriptor alloc] init]; + texDesc.textureType = MTLTextureType2D; + texDesc.storageMode = MTLStorageModePrivate; + texDesc.usage = MTLTextureUsageShaderRead | MTLTextureUsageShaderWrite; + texDesc.pixelFormat = MTLPixelFormatRGBA8Unorm; + texDesc.width = size.x; + texDesc.height = size.y; + + backend->outTexture = [surface->device newTextureWithDescriptor:texDesc]; + + backend->frameSize = size; + } + } +} + void mg_mtl_canvas_render(mg_canvas_backend* interface, mg_color clearColor, u32 primitiveCount, @@ -792,9 +832,6 @@ void mg_mtl_canvas_render(mg_canvas_backend* interface, mg_mtl_surface* surface = (mg_mtl_surface*)mg_surface_data_from_handle(backend->surface); ASSERT(surface && surface->interface.backend == MG_BACKEND_METAL); - mg_mtl_surface_acquire_command_buffer(surface); - mg_mtl_surface_acquire_drawable(surface); - mp_rect frame = mg_surface_get_frame(backend->surface); f32 scale = surface->mtlLayer.contentsScale; vec2 viewportSize = {frame.w * scale, frame.h * scale}; @@ -802,6 +839,14 @@ void mg_mtl_canvas_render(mg_canvas_backend* interface, int nTilesX = (int)(frame.w * scale + tileSize - 1)/tileSize; int nTilesY = (int)(frame.h * scale + tileSize - 1)/tileSize; + if(viewportSize.x != backend->frameSize.x || viewportSize.y != backend->frameSize.y) + { + mg_mtl_canvas_resize(backend, viewportSize); + } + + mg_mtl_surface_acquire_command_buffer(surface); + mg_mtl_surface_acquire_drawable(surface); + @autoreleasepool { //NOTE: clear log counter @@ -1157,13 +1202,15 @@ mg_canvas_backend* mg_mtl_canvas_create(mg_surface surface) mp_rect frame = mg_surface_get_frame(surface); f32 scale = metalSurface->mtlLayer.contentsScale; + backend->frameSize = (vec2){frame.w*scale, frame.h*scale}; + MTLTextureDescriptor* texDesc = [[MTLTextureDescriptor alloc] init]; texDesc.textureType = MTLTextureType2D; texDesc.storageMode = MTLStorageModePrivate; texDesc.usage = MTLTextureUsageShaderRead | MTLTextureUsageShaderWrite; texDesc.pixelFormat = MTLPixelFormatRGBA8Unorm; - texDesc.width = frame.w * scale; - texDesc.height = frame.h * scale; + texDesc.width = backend->frameSize.x; + texDesc.height = backend->frameSize.y; backend->outTexture = [metalSurface->device newTextureWithDescriptor:texDesc]; @@ -1213,7 +1260,6 @@ mg_canvas_backend* mg_mtl_canvas_create(mg_surface surface) backend->screenTilesBuffer = [metalSurface->device newBufferWithLength: nTilesX*nTilesY*sizeof(int) options: bufferOptions]; - bufferOptions = MTLResourceStorageModeShared; for(int i=0; imtlLayer setFrame: cgFrame]; + [CATransaction commit]; +// }); + CGSize drawableSize = (CGSize){.width = frame.w * scale.x, .height = frame.h * scale.y}; surface->mtlLayer.drawableSize = drawableSize; } @@ -185,8 +195,8 @@ mg_surface_data* mg_mtl_surface_create_for_window(mp_window window) surface->mtlLayer.pixelFormat = MTLPixelFormatBGRA8Unorm; //NOTE(martin): handling resizing - surface->mtlLayer.autoresizingMask = kCALayerHeightSizable | kCALayerWidthSizable; - surface->mtlLayer.needsDisplayOnBoundsChange = YES; +// surface->mtlLayer.autoresizingMask = kCALayerHeightSizable | kCALayerWidthSizable; +// surface->mtlLayer.needsDisplayOnBoundsChange = YES; //----------------------------------------------------------- //NOTE(martin): create a command queue diff --git a/src/osx_app.m b/src/osx_app.m index 68bd9c5..232d42e 100644 --- a/src/osx_app.m +++ b/src/osx_app.m @@ -1405,6 +1405,7 @@ mp_window mp_window_create(mp_rect contentRect, const char* title, mp_window_sty } MPNativeView* view = [[MPNativeView alloc] initWithMPWindow:window]; + [view setCanDrawConcurrently: YES]; [window->osx.nsWindow setContentView:view]; [window->osx.nsWindow makeFirstResponder:view]; @@ -1709,7 +1710,15 @@ mp_rect mg_osx_surface_get_frame(mg_surface_data* surface) void mg_osx_surface_set_frame(mg_surface_data* surface, mp_rect frame) {@autoreleasepool{ CGRect cgFrame = {{frame.x, frame.y}, {frame.w, frame.h}}; - [surface->layer.caLayer setFrame: cgFrame]; + +// dispatch_async(dispatch_get_main_queue(), +// ^{ + [CATransaction begin]; + [CATransaction setDisableActions:YES]; + [surface->layer.caLayer setFrame: cgFrame]; + [CATransaction commit]; +// }); + }} bool mg_osx_surface_get_hidden(mg_surface_data* surface) @@ -1746,6 +1755,7 @@ void mg_surface_init_for_window(mg_surface_data* surface, mp_window_data* window NSRect frame = [[window->osx.nsWindow contentView] frame]; CGSize size = frame.size; surface->layer.caLayer.frame = (CGRect){{0, 0}, size}; + surface->layer.caLayer.contentsScale = window->osx.nsView.layer.contentsScale; [window->osx.nsView.layer addSublayer: surface->layer.caLayer]; }}