Allow setting surface swap interval in opengl surface

This commit is contained in:
martinfouilleul 2023-02-08 11:49:00 +01:00
parent c982b524c0
commit e6e674ee04
8 changed files with 609 additions and 535 deletions

View File

@ -58,8 +58,10 @@ int main()
mp_init(); mp_init();
mp_clock_init(); //TODO put that in mp_init()? mp_clock_init(); //TODO put that in mp_init()?
mp_rect rect = {.x = 100, .y = 100, .w = 810, .h = 610}; mp_rect windowRect = {.x = 100, .y = 100, .w = 810, .h = 610};
mp_window window = mp_window_create(rect, "test", 0); mp_window window = mp_window_create(windowRect, "test", 0);
mp_rect contentRect = mp_window_get_content_rect(window);
//NOTE: create surface //NOTE: create surface
#if defined(OS_MACOS) #if defined(OS_MACOS)
@ -70,6 +72,8 @@ int main()
#error "unsupported OS" #error "unsupported OS"
#endif #endif
mg_surface_swap_interval(surface, 0);
//TODO: create canvas //TODO: create canvas
mg_canvas canvas = mg_canvas_create(surface); mg_canvas canvas = mg_canvas_create(surface);
mg_font font = create_font(); mg_font font = create_font();
@ -79,7 +83,8 @@ int main()
mp_window_focus(window); mp_window_focus(window);
f32 x = 400, y = 300; f32 x = 400, y = 300;
f32 dx = 5, dy = 5; f32 speed = 0;
f32 dx = speed, dy = speed;
f64 frameTime = 0; f64 frameTime = 0;
while(!mp_should_quit()) while(!mp_should_quit())
@ -110,24 +115,36 @@ int main()
{ {
if(event.key.action == MP_KEY_PRESS || event.key.action == MP_KEY_REPEAT) if(event.key.action == MP_KEY_PRESS || event.key.action == MP_KEY_REPEAT)
{ {
/* //*
if(event.key.code == MP_KEY_LEFT) if(event.key.code == MP_KEY_LEFT)
{ {
dx-=5.1; if(x - 200 > 0)
{
x-=1;
}
} }
else if(event.key.code == MP_KEY_RIGHT) else if(event.key.code == MP_KEY_RIGHT)
{ {
dx+=5.1; if(x + 200 < contentRect.w)
{
x+=1;
}
} }
else if(event.key.code == MP_KEY_UP) else if(event.key.code == MP_KEY_UP)
{ {
dy+=5.1; if(y + 200 < contentRect.h)
{
y+=1;
}
} }
else if(event.key.code == MP_KEY_DOWN) else if(event.key.code == MP_KEY_DOWN)
{ {
dy-=5.1; if(y - 200 > 0)
{
y-=1;
}
} }
*/ //*/
} }
} break; } break;
@ -138,19 +155,19 @@ int main()
if(x-200 < 0) if(x-200 < 0)
{ {
dx = 5; dx = speed;
} }
if(x+200 > 800) if(x+200 > contentRect.w)
{ {
dx = -5; dx = -speed;
} }
if(y-200 < 0) if(y-200 < 0)
{ {
dy = 5; dy = speed;
} }
if(y+200 > 550) if(y+200 > contentRect.h)
{ {
dy = -5; dy = -speed;
} }
x += dx; x += dx;
y += dy; y += dy;

View File

@ -321,6 +321,16 @@ void mg_surface_present(mg_surface surface)
} }
} }
void mg_surface_swap_interval(mg_surface surface, int swap)
{
DEBUG_ASSERT(__mgData.init);
mg_surface_data* surfaceData = mg_surface_data_from_handle(surface);
if(surfaceData)
{
surfaceData->swapInterval(surfaceData, swap);
}
}
void mg_surface_set_frame(mg_surface surface, mp_rect frame) void mg_surface_set_frame(mg_surface surface, mp_rect frame)
{ {
DEBUG_ASSERT(__mgData.init); DEBUG_ASSERT(__mgData.init);

View File

@ -1,210 +1,211 @@
/************************************************************//** /************************************************************//**
* *
* @file: graphics.h * @file: graphics.h
* @author: Martin Fouilleul * @author: Martin Fouilleul
* @date: 23/01/2023 * @date: 23/01/2023
* @revision: * @revision:
* *
*****************************************************************/ *****************************************************************/
#ifndef __GRAPHICS_H_ #ifndef __GRAPHICS_H_
#define __GRAPHICS_H_ #define __GRAPHICS_H_
//------------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------------
//NOTE(martin): graphics surface //NOTE(martin): graphics surface
//------------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------------
typedef struct mg_surface { u64 h; } mg_surface; typedef struct mg_surface { u64 h; } mg_surface;
mg_surface mg_surface_nil(); mg_surface mg_surface_nil();
bool mg_surface_is_nil(mg_surface surface); bool mg_surface_is_nil(mg_surface surface);
void mg_surface_destroy(mg_surface surface); void mg_surface_destroy(mg_surface surface);
void mg_surface_prepare(mg_surface surface); void mg_surface_prepare(mg_surface surface);
void mg_surface_present(mg_surface surface); void mg_surface_present(mg_surface surface);
mp_rect mg_surface_get_frame(mg_surface surface); void mg_surface_swap_interval(mg_surface surface, int swap);
void mg_surface_set_frame(mg_surface surface, mp_rect frame); mp_rect mg_surface_get_frame(mg_surface surface);
bool mg_surface_get_hidden(mg_surface surface); void mg_surface_set_frame(mg_surface surface, mp_rect frame);
void mg_surface_set_hidden(mg_surface surface, bool hidden); bool mg_surface_get_hidden(mg_surface surface);
void mg_surface_set_hidden(mg_surface surface, bool hidden);
//------------------------------------------------------------------------------------------
//NOTE(martin): graphics canvas structs //------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------ //NOTE(martin): graphics canvas structs
typedef struct mg_canvas { u64 h; } mg_canvas; //------------------------------------------------------------------------------------------
typedef struct mg_stream { u64 h; } mg_stream; typedef struct mg_canvas { u64 h; } mg_canvas;
typedef struct mg_font { u64 h; } mg_font; typedef struct mg_stream { u64 h; } mg_stream;
typedef struct mg_font { u64 h; } mg_font;
typedef struct mg_mat2x3
{ typedef struct mg_mat2x3
f32 m[6]; {
} mg_mat2x3; f32 m[6];
} mg_mat2x3;
typedef struct mg_color
{ typedef struct mg_color
union {
{ union
struct {
{ struct
f32 r; {
f32 g; f32 r;
f32 b; f32 g;
f32 a; f32 b;
}; f32 a;
f32 c[4]; };
}; f32 c[4];
} mg_color; };
} mg_color;
typedef enum {MG_JOINT_MITER = 0,
MG_JOINT_BEVEL, typedef enum {MG_JOINT_MITER = 0,
MG_JOINT_NONE } mg_joint_type; MG_JOINT_BEVEL,
MG_JOINT_NONE } mg_joint_type;
typedef enum {MG_CAP_NONE = 0,
MG_CAP_SQUARE } mg_cap_type; typedef enum {MG_CAP_NONE = 0,
MG_CAP_SQUARE } mg_cap_type;
typedef struct mg_font_extents
{ typedef struct mg_font_extents
f32 ascent; // the extent above the baseline (by convention a positive value extends above the baseline) {
f32 descent; // the extent below the baseline (by convention, positive value extends below the baseline) f32 ascent; // the extent above the baseline (by convention a positive value extends above the baseline)
f32 leading; // spacing between one row's descent and the next row's ascent f32 descent; // the extent below the baseline (by convention, positive value extends below the baseline)
f32 xHeight; // height of the lower case letter 'x' f32 leading; // spacing between one row's descent and the next row's ascent
f32 capHeight; // height of the upper case letter 'M' f32 xHeight; // height of the lower case letter 'x'
f32 width; // maximum width of the font f32 capHeight; // height of the upper case letter 'M'
f32 width; // maximum width of the font
} mg_font_extents;
} mg_font_extents;
typedef struct mg_text_extents
{ typedef struct mg_text_extents
f32 xBearing; {
f32 yBearing; f32 xBearing;
f32 width; f32 yBearing;
f32 height; f32 width;
f32 xAdvance; f32 height;
f32 yAdvance; f32 xAdvance;
f32 yAdvance;
} mg_text_extents;
} mg_text_extents;
//------------------------------------------------------------------------------------------
//NOTE(martin): graphics canvas //------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------ //NOTE(martin): graphics canvas
//------------------------------------------------------------------------------------------
mg_canvas mg_canvas_create(mg_surface surface);
void mg_canvas_destroy(mg_canvas canvas); mg_canvas mg_canvas_create(mg_surface surface);
mg_canvas mg_canvas_set_current(mg_canvas canvas); void mg_canvas_destroy(mg_canvas canvas);
mg_canvas mg_canvas_set_current(mg_canvas canvas);
void mg_flush();
void mg_flush();
//------------------------------------------------------------------------------------------
//NOTE(martin): transform, viewport and clipping //------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------ //NOTE(martin): transform, viewport and clipping
void mg_viewport(mp_rect viewPort); //------------------------------------------------------------------------------------------
void mg_viewport(mp_rect viewPort);
void mg_matrix_push(mg_mat2x3 matrix);
void mg_matrix_pop(); void mg_matrix_push(mg_mat2x3 matrix);
void mg_matrix_pop();
void mg_clip_push(f32 x, f32 y, f32 w, f32 h);
void mg_clip_pop(); void mg_clip_push(f32 x, f32 y, f32 w, f32 h);
void mg_clip_pop();
//------------------------------------------------------------------------------------------
//NOTE(martin): graphics attributes setting/getting //------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------ //NOTE(martin): graphics attributes setting/getting
void mg_set_color(mg_color color); //------------------------------------------------------------------------------------------
void mg_set_color_rgba(f32 r, f32 g, f32 b, f32 a); void mg_set_color(mg_color color);
void mg_set_width(f32 width); void mg_set_color_rgba(f32 r, f32 g, f32 b, f32 a);
void mg_set_tolerance(f32 tolerance); void mg_set_width(f32 width);
void mg_set_joint(mg_joint_type joint); void mg_set_tolerance(f32 tolerance);
void mg_set_max_joint_excursion(f32 maxJointExcursion); void mg_set_joint(mg_joint_type joint);
void mg_set_cap(mg_cap_type cap); void mg_set_max_joint_excursion(f32 maxJointExcursion);
void mg_set_font(mg_font font); void mg_set_cap(mg_cap_type cap);
void mg_set_font_size(f32 size); void mg_set_font(mg_font font);
void mg_set_text_flip(bool flip); void mg_set_font_size(f32 size);
void mg_set_text_flip(bool flip);
mg_color mg_get_color();
f32 mg_get_width(); mg_color mg_get_color();
f32 mg_get_tolerance(); f32 mg_get_width();
mg_joint_type mg_get_joint(); f32 mg_get_tolerance();
f32 mg_get_max_joint_excursion(); mg_joint_type mg_get_joint();
mg_cap_type mg_get_cap(); f32 mg_get_max_joint_excursion();
mg_font mg_get_font(); mg_cap_type mg_get_cap();
f32 mg_get_font_size(); mg_font mg_get_font();
bool mg_get_text_flip(); f32 mg_get_font_size();
bool mg_get_text_flip();
//------------------------------------------------------------------------------------------
//NOTE(martin): path construction //------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------ //NOTE(martin): path construction
vec2 mg_get_position(); //------------------------------------------------------------------------------------------
void mg_move_to(f32 x, f32 y); vec2 mg_get_position();
void mg_line_to(f32 x, f32 y); void mg_move_to(f32 x, f32 y);
void mg_quadratic_to(f32 x1, f32 y1, f32 x2, f32 y2); void mg_line_to(f32 x, f32 y);
void mg_cubic_to(f32 x1, f32 y1, f32 x2, f32 y2, f32 x3, f32 y3); void mg_quadratic_to(f32 x1, f32 y1, f32 x2, f32 y2);
void mg_close_path(); void mg_cubic_to(f32 x1, f32 y1, f32 x2, f32 y2, f32 x3, f32 y3);
void mg_close_path();
mp_rect mg_glyph_outlines(str32 glyphIndices);
void mg_codepoints_outlines(str32 string); mp_rect mg_glyph_outlines(str32 glyphIndices);
void mg_text_outlines(str8 string); void mg_codepoints_outlines(str32 string);
void mg_text_outlines(str8 string);
//------------------------------------------------------------------------------------------
//NOTE(martin): clear/fill/stroke //------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------ //NOTE(martin): clear/fill/stroke
void mg_clear(); //------------------------------------------------------------------------------------------
void mg_fill(); void mg_clear();
void mg_stroke(); void mg_fill();
void mg_stroke();
//------------------------------------------------------------------------------------------
//NOTE(martin): 'fast' shapes primitives //------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------ //NOTE(martin): 'fast' shapes primitives
void mg_rectangle_fill(f32 x, f32 y, f32 w, f32 h); //------------------------------------------------------------------------------------------
void mg_rectangle_stroke(f32 x, f32 y, f32 w, f32 h); void mg_rectangle_fill(f32 x, f32 y, f32 w, f32 h);
void mg_rounded_rectangle_fill(f32 x, f32 y, f32 w, f32 h, f32 r); void mg_rectangle_stroke(f32 x, f32 y, f32 w, f32 h);
void mg_rounded_rectangle_stroke(f32 x, f32 y, f32 w, f32 h, f32 r); void mg_rounded_rectangle_fill(f32 x, f32 y, f32 w, f32 h, f32 r);
void mg_ellipse_fill(f32 x, f32 y, f32 rx, f32 ry); void mg_rounded_rectangle_stroke(f32 x, f32 y, f32 w, f32 h, f32 r);
void mg_ellipse_stroke(f32 x, f32 y, f32 rx, f32 ry); void mg_ellipse_fill(f32 x, f32 y, f32 rx, f32 ry);
void mg_circle_fill(f32 x, f32 y, f32 r); void mg_ellipse_stroke(f32 x, f32 y, f32 rx, f32 ry);
void mg_circle_stroke(f32 x, f32 y, f32 r); void mg_circle_fill(f32 x, f32 y, f32 r);
void mg_arc(f32 x, f32 y, f32 r, f32 arcAngle, f32 startAngle); void mg_circle_stroke(f32 x, f32 y, f32 r);
void mg_arc(f32 x, f32 y, f32 r, f32 arcAngle, f32 startAngle);
//------------------------------------------------------------------------------------------
//NOTE(martin): fonts //------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------ //NOTE(martin): fonts
mg_font mg_font_nil(); //------------------------------------------------------------------------------------------
mg_font mg_font_create_from_memory(u32 size, byte* buffer, u32 rangeCount, unicode_range* ranges); mg_font mg_font_nil();
void mg_font_destroy(mg_font font); mg_font mg_font_create_from_memory(u32 size, byte* buffer, u32 rangeCount, unicode_range* ranges);
void mg_font_destroy(mg_font font);
//NOTE(martin): the following int valued functions return -1 if font is invalid or codepoint is not present in font//
//TODO(martin): add enum error codes //NOTE(martin): the following int valued functions return -1 if font is invalid or codepoint is not present in font//
//TODO(martin): add enum error codes
mg_font_extents mg_font_get_extents(mg_font font);
mg_font_extents mg_font_get_scaled_extents(mg_font font, f32 emSize); mg_font_extents mg_font_get_extents(mg_font font);
f32 mg_font_get_scale_for_em_pixels(mg_font font, f32 emSize); mg_font_extents mg_font_get_scaled_extents(mg_font font, f32 emSize);
f32 mg_font_get_scale_for_em_pixels(mg_font font, f32 emSize);
//NOTE(martin): if you need to process more than one codepoint, first convert your codepoints to glyph indices, then use the
// glyph index versions of the functions, which can take an array of glyph indices. //NOTE(martin): if you need to process more than one codepoint, first convert your codepoints to glyph indices, then use the
// glyph index versions of the functions, which can take an array of glyph indices.
str32 mg_font_get_glyph_indices(mg_font font, str32 codePoints, str32 backing);
str32 mg_font_push_glyph_indices(mg_font font, mem_arena* arena, str32 codePoints); str32 mg_font_get_glyph_indices(mg_font font, str32 codePoints, str32 backing);
u32 mg_font_get_glyph_index(mg_font font, utf32 codePoint); str32 mg_font_push_glyph_indices(mg_font font, mem_arena* arena, str32 codePoints);
u32 mg_font_get_glyph_index(mg_font font, utf32 codePoint);
int mg_font_get_codepoint_extents(mg_font font, utf32 codePoint, mg_text_extents* outExtents);
int mg_font_get_codepoint_extents(mg_font font, utf32 codePoint, mg_text_extents* outExtents);
int mg_font_get_glyph_extents(mg_font font, str32 glyphIndices, mg_text_extents* outExtents);
int mg_font_get_glyph_extents(mg_font font, str32 glyphIndices, mg_text_extents* outExtents);
mp_rect mg_text_bounding_box_utf32(mg_font font, f32 fontSize, str32 text);
mp_rect mg_text_bounding_box(mg_font font, f32 fontSize, str8 text); mp_rect mg_text_bounding_box_utf32(mg_font font, f32 fontSize, str32 text);
mp_rect mg_text_bounding_box(mg_font font, f32 fontSize, str8 text);
//------------------------------------------------------------------------------------------
//NOTE(martin): images //------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------ //NOTE(martin): images
typedef struct mg_image { u64 h; } mg_image; //------------------------------------------------------------------------------------------
typedef struct mg_image { u64 h; } mg_image;
mg_image mg_image_nil();
bool mg_image_equal(mg_image a, mg_image b); mg_image mg_image_nil();
bool mg_image_equal(mg_image a, mg_image b);
mg_image mg_image_create_from_rgba8(u32 width, u32 height, u8* pixels);
mg_image mg_image_create_from_data(str8 data, bool flip); mg_image mg_image_create_from_rgba8(u32 width, u32 height, u8* pixels);
mg_image mg_image_create_from_file(str8 path, bool flip); mg_image mg_image_create_from_data(str8 data, bool flip);
mg_image mg_image_create_from_file(str8 path, bool flip);
void mg_image_drestroy(mg_image image);
void mg_image_drestroy(mg_image image);
vec2 mg_image_size(mg_image image);
void mg_image_draw(mg_image image, mp_rect rect); vec2 mg_image_size(mg_image image);
void mg_rounded_image_draw(mg_image image, mp_rect rect, f32 roundness); void mg_image_draw(mg_image image, mp_rect rect);
void mg_rounded_image_draw(mg_image image, mp_rect rect, f32 roundness);
#endif //__GRAPHICS_H_
#endif //__GRAPHICS_H_

View File

@ -27,6 +27,7 @@ typedef struct mg_surface_data mg_surface_data;
typedef void (*mg_surface_destroy_proc)(mg_surface_data* surface); typedef void (*mg_surface_destroy_proc)(mg_surface_data* surface);
typedef void (*mg_surface_prepare_proc)(mg_surface_data* surface); typedef void (*mg_surface_prepare_proc)(mg_surface_data* surface);
typedef void (*mg_surface_present_proc)(mg_surface_data* surface); typedef void (*mg_surface_present_proc)(mg_surface_data* surface);
typedef void (*mg_surface_swap_interval_proc)(mg_surface_data* surface, int swap);
typedef mp_rect (*mg_surface_get_frame_proc)(mg_surface_data* surface); typedef mp_rect (*mg_surface_get_frame_proc)(mg_surface_data* surface);
typedef void (*mg_surface_set_frame_proc)(mg_surface_data* surface, mp_rect frame); typedef void (*mg_surface_set_frame_proc)(mg_surface_data* surface, mp_rect frame);
typedef bool (*mg_surface_get_hidden_proc)(mg_surface_data* surface); typedef bool (*mg_surface_get_hidden_proc)(mg_surface_data* surface);
@ -39,6 +40,7 @@ typedef struct mg_surface_data
mg_surface_destroy_proc destroy; mg_surface_destroy_proc destroy;
mg_surface_prepare_proc prepare; mg_surface_prepare_proc prepare;
mg_surface_present_proc present; mg_surface_present_proc present;
mg_surface_swap_interval_proc swapInterval;
mg_surface_get_frame_proc getFrame; mg_surface_get_frame_proc getFrame;
mg_surface_set_frame_proc setFrame; mg_surface_set_frame_proc setFrame;
mg_surface_get_hidden_proc getHidden; mg_surface_get_hidden_proc getHidden;

View File

@ -1,299 +1,309 @@
/************************************************************//** /************************************************************//**
* *
* @file: graphics.m * @file: graphics.m
* @author: Martin Fouilleul * @author: Martin Fouilleul
* @date: 12/07/2020 * @date: 12/07/2020
* @revision: * @revision:
* *
*****************************************************************/ *****************************************************************/
#import<Metal/Metal.h> #import<Metal/Metal.h>
#import <QuartzCore/QuartzCore.h> #import <QuartzCore/QuartzCore.h>
#import<QuartzCore/CAMetalLayer.h> #import<QuartzCore/CAMetalLayer.h>
#include<simd/simd.h> #include<simd/simd.h>
#include"graphics_internal.h" #include"graphics_internal.h"
#include"macro_helpers.h" #include"macro_helpers.h"
#include"osx_app.h" #include"osx_app.h"
#define LOG_SUBSYSTEM "Graphics" #define LOG_SUBSYSTEM "Graphics"
static const u32 MP_METAL_MAX_DRAWABLES_IN_FLIGHT = 3; static const u32 MP_METAL_MAX_DRAWABLES_IN_FLIGHT = 3;
typedef struct mg_metal_surface typedef struct mg_metal_surface
{ {
mg_surface_data interface; mg_surface_data interface;
// permanent metal resources // permanent metal resources
NSView* view; NSView* view;
id<MTLDevice> device; id<MTLDevice> device;
CAMetalLayer* metalLayer; CAMetalLayer* metalLayer;
id<MTLCommandQueue> commandQueue; id<MTLCommandQueue> commandQueue;
// transient metal resources // transient metal resources
id<CAMetalDrawable> drawable; id<CAMetalDrawable> drawable;
id<MTLCommandBuffer> commandBuffer; id<MTLCommandBuffer> commandBuffer;
dispatch_semaphore_t drawableSemaphore; dispatch_semaphore_t drawableSemaphore;
} mg_metal_surface; } mg_metal_surface;
void mg_metal_surface_destroy(mg_surface_data* interface) void mg_metal_surface_destroy(mg_surface_data* interface)
{ {
mg_metal_surface* surface = (mg_metal_surface*)interface; mg_metal_surface* surface = (mg_metal_surface*)interface;
@autoreleasepool @autoreleasepool
{ {
[surface->commandQueue release]; [surface->commandQueue release];
[surface->metalLayer release]; [surface->metalLayer release];
[surface->device release]; [surface->device release];
} }
} }
void mg_metal_surface_acquire_drawable_and_command_buffer(mg_metal_surface* surface) void mg_metal_surface_acquire_drawable_and_command_buffer(mg_metal_surface* surface)
{@autoreleasepool{ {@autoreleasepool{
/*WARN(martin): this is super important /*WARN(martin): this is super important
When the app is put in the background, it seems that if there are buffers in flight, the drawables to When the app is put in the background, it seems that if there are buffers in flight, the drawables to
can be leaked. This causes the gpu to allocate more and more drawables, until the app crashes. can be leaked. This causes the gpu to allocate more and more drawables, until the app crashes.
(note: the drawable objects themselves are released once the app comes back to the forefront, but the (note: the drawable objects themselves are released once the app comes back to the forefront, but the
memory allocated in the GPU is never freed...) memory allocated in the GPU is never freed...)
In background the gpu seems to create drawable if none is available instead of actually In background the gpu seems to create drawable if none is available instead of actually
blocking on nextDrawable. These drawable never get freed. blocking on nextDrawable. These drawable never get freed.
This is not a problem if our shader is fast enough, since a previous drawable becomes This is not a problem if our shader is fast enough, since a previous drawable becomes
available before we finish the frame. But we want to protect against it anyway available before we finish the frame. But we want to protect against it anyway
The normal blocking mechanism of nextDrawable seems useless, so we implement our own scheme by The normal blocking mechanism of nextDrawable seems useless, so we implement our own scheme by
counting the number of drawables available with a semaphore that gets decremented here and counting the number of drawables available with a semaphore that gets decremented here and
incremented in the presentedHandler of the drawable. incremented in the presentedHandler of the drawable.
Thus we ensure that we don't consume more drawables than we are able to draw. Thus we ensure that we don't consume more drawables than we are able to draw.
//TODO: we _also_ should stop trying to render if we detect that the app is in the background //TODO: we _also_ should stop trying to render if we detect that the app is in the background
or occluded, but we can't count only on this because there is a potential race between the or occluded, but we can't count only on this because there is a potential race between the
notification of background mode and the rendering. notification of background mode and the rendering.
//TODO: We should set a reasonable timeout and skip the frame and log an error in case we are stalled //TODO: We should set a reasonable timeout and skip the frame and log an error in case we are stalled
for too long. for too long.
*/ */
dispatch_semaphore_wait(surface->drawableSemaphore, DISPATCH_TIME_FOREVER); dispatch_semaphore_wait(surface->drawableSemaphore, DISPATCH_TIME_FOREVER);
surface->drawable = [surface->metalLayer nextDrawable]; surface->drawable = [surface->metalLayer nextDrawable];
ASSERT(surface->drawable != nil); ASSERT(surface->drawable != nil);
//TODO: make this a weak reference if we use ARC //TODO: make this a weak reference if we use ARC
dispatch_semaphore_t semaphore = surface->drawableSemaphore; dispatch_semaphore_t semaphore = surface->drawableSemaphore;
[surface->drawable addPresentedHandler:^(id<MTLDrawable> drawable){ [surface->drawable addPresentedHandler:^(id<MTLDrawable> drawable){
dispatch_semaphore_signal(semaphore); dispatch_semaphore_signal(semaphore);
}]; }];
//NOTE(martin): create a command buffer //NOTE(martin): create a command buffer
surface->commandBuffer = [surface->commandQueue commandBuffer]; surface->commandBuffer = [surface->commandQueue commandBuffer];
[surface->commandBuffer retain]; [surface->commandBuffer retain];
[surface->drawable retain]; [surface->drawable retain];
}} }}
void mg_metal_surface_prepare(mg_surface_data* interface) void mg_metal_surface_prepare(mg_surface_data* interface)
{ {
mg_metal_surface* surface = (mg_metal_surface*)interface; mg_metal_surface* surface = (mg_metal_surface*)interface;
mg_metal_surface_acquire_drawable_and_command_buffer(surface); mg_metal_surface_acquire_drawable_and_command_buffer(surface);
@autoreleasepool @autoreleasepool
{ {
MTLRenderPassDescriptor* renderPassDescriptor = [MTLRenderPassDescriptor renderPassDescriptor]; MTLRenderPassDescriptor* renderPassDescriptor = [MTLRenderPassDescriptor renderPassDescriptor];
renderPassDescriptor.colorAttachments[0].texture = surface->drawable.texture; renderPassDescriptor.colorAttachments[0].texture = surface->drawable.texture;
renderPassDescriptor.colorAttachments[0].loadAction = MTLLoadActionClear; renderPassDescriptor.colorAttachments[0].loadAction = MTLLoadActionClear;
renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColorMake(0.0,0.0,0.0,0.0); renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColorMake(0.0,0.0,0.0,0.0);
id<MTLRenderCommandEncoder> renderEncoder = [surface->commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor]; id<MTLRenderCommandEncoder> renderEncoder = [surface->commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor];
[renderEncoder endEncoding]; [renderEncoder endEncoding];
} }
} }
void mg_metal_surface_present(mg_surface_data* interface) void mg_metal_surface_present(mg_surface_data* interface)
{ {
mg_metal_surface* surface = (mg_metal_surface*)interface; mg_metal_surface* surface = (mg_metal_surface*)interface;
@autoreleasepool @autoreleasepool
{ {
//NOTE(martin): present drawable and commit command buffer //NOTE(martin): present drawable and commit command buffer
[surface->commandBuffer presentDrawable: surface->drawable]; [surface->commandBuffer presentDrawable: surface->drawable];
[surface->commandBuffer commit]; [surface->commandBuffer commit];
[surface->commandBuffer waitUntilCompleted]; [surface->commandBuffer waitUntilCompleted];
//NOTE(martin): acquire next frame resources //NOTE(martin): acquire next frame resources
[surface->commandBuffer release]; [surface->commandBuffer release];
surface->commandBuffer = nil; surface->commandBuffer = nil;
[surface->drawable release]; [surface->drawable release];
surface->drawable = nil; surface->drawable = nil;
} }
} }
void mg_metal_surface_set_frame(mg_surface_data* interface, mp_rect frame) void mg_metal_surface_swap_interval(mg_surface_data* interface, int swap)
{ {
mg_metal_surface* surface = (mg_metal_surface*)interface; mg_metal_surface* surface = (mg_metal_surface*)interface;
@autoreleasepool ////////////////////////////////////////////////////////////////
{ //TODO
CGRect cgFrame = {{frame.x, frame.y}, {frame.w, frame.h}}; ////////////////////////////////////////////////////////////////
[surface->view setFrame: cgFrame]; }
f32 scale = surface->metalLayer.contentsScale;
CGSize drawableSize = (CGSize){.width = frame.w * scale, .height = frame.h * scale}; void mg_metal_surface_set_frame(mg_surface_data* interface, mp_rect frame)
surface->metalLayer.drawableSize = drawableSize; {
} mg_metal_surface* surface = (mg_metal_surface*)interface;
}
@autoreleasepool
mp_rect mg_metal_surface_get_frame(mg_surface_data* interface) {
{ CGRect cgFrame = {{frame.x, frame.y}, {frame.w, frame.h}};
mg_metal_surface* surface = (mg_metal_surface*)interface; [surface->view setFrame: cgFrame];
f32 scale = surface->metalLayer.contentsScale;
@autoreleasepool CGSize drawableSize = (CGSize){.width = frame.w * scale, .height = frame.h * scale};
{ surface->metalLayer.drawableSize = drawableSize;
CGRect frame = surface->view.frame; }
return((mp_rect){frame.origin.x, frame.origin.y, frame.size.width, frame.size.height}); }
}
} mp_rect mg_metal_surface_get_frame(mg_surface_data* interface)
{
void mg_metal_surface_set_hidden(mg_surface_data* interface, bool hidden) mg_metal_surface* surface = (mg_metal_surface*)interface;
{
mg_metal_surface* surface = (mg_metal_surface*)interface; @autoreleasepool
@autoreleasepool {
{ CGRect frame = surface->view.frame;
[CATransaction begin]; return((mp_rect){frame.origin.x, frame.origin.y, frame.size.width, frame.size.height});
[CATransaction setDisableActions:YES]; }
[surface->metalLayer setHidden:hidden]; }
[CATransaction commit];
} void mg_metal_surface_set_hidden(mg_surface_data* interface, bool hidden)
} {
mg_metal_surface* surface = (mg_metal_surface*)interface;
bool mg_metal_surface_get_hidden(mg_surface_data* interface) @autoreleasepool
{ {
mg_metal_surface* surface = (mg_metal_surface*)interface; [CATransaction begin];
@autoreleasepool [CATransaction setDisableActions:YES];
{ [surface->metalLayer setHidden:hidden];
return([surface->metalLayer isHidden]); [CATransaction commit];
} }
} }
//TODO fix that according to real scaling, depending on the monitor settings bool mg_metal_surface_get_hidden(mg_surface_data* interface)
static const f32 MG_METAL_SURFACE_CONTENTS_SCALING = 2; {
mg_metal_surface* surface = (mg_metal_surface*)interface;
mg_surface mg_metal_surface_create_for_window(mp_window window) @autoreleasepool
{ {
mp_window_data* windowData = mp_window_ptr_from_handle(window); return([surface->metalLayer isHidden]);
if(!windowData) }
{ }
return(mg_surface_nil());
} //TODO fix that according to real scaling, depending on the monitor settings
else static const f32 MG_METAL_SURFACE_CONTENTS_SCALING = 2;
{
mg_metal_surface* surface = (mg_metal_surface*)malloc(sizeof(mg_metal_surface)); mg_surface mg_metal_surface_create_for_window(mp_window window)
{
//NOTE(martin): setup interface functions mp_window_data* windowData = mp_window_ptr_from_handle(window);
surface->interface.backend = MG_BACKEND_METAL; if(!windowData)
surface->interface.destroy = mg_metal_surface_destroy; {
surface->interface.prepare = mg_metal_surface_prepare; return(mg_surface_nil());
surface->interface.present = mg_metal_surface_present; }
surface->interface.getFrame = mg_metal_surface_get_frame; else
surface->interface.setFrame = mg_metal_surface_set_frame; {
surface->interface.getHidden = mg_metal_surface_get_hidden; mg_metal_surface* surface = (mg_metal_surface*)malloc(sizeof(mg_metal_surface));
surface->interface.setHidden = mg_metal_surface_set_hidden;
//NOTE(martin): setup interface functions
@autoreleasepool surface->interface.backend = MG_BACKEND_METAL;
{ surface->interface.destroy = mg_metal_surface_destroy;
NSRect frame = [[windowData->osx.nsWindow contentView] frame]; surface->interface.prepare = mg_metal_surface_prepare;
surface->view = [[NSView alloc] initWithFrame: frame]; surface->interface.present = mg_metal_surface_present;
[surface->view setWantsLayer:YES]; surface->interface.swapInterval = mg_metal_surface_swap_interval;
surface->interface.getFrame = mg_metal_surface_get_frame;
[[windowData->osx.nsWindow contentView] addSubview: surface->view]; surface->interface.setFrame = mg_metal_surface_set_frame;
surface->interface.getHidden = mg_metal_surface_get_hidden;
surface->drawableSemaphore = dispatch_semaphore_create(MP_METAL_MAX_DRAWABLES_IN_FLIGHT); surface->interface.setHidden = mg_metal_surface_set_hidden;
//----------------------------------------------------------- @autoreleasepool
//NOTE(martin): create a metal device and a metal layer and {
//----------------------------------------------------------- NSRect frame = [[windowData->osx.nsWindow contentView] frame];
surface->device = MTLCreateSystemDefaultDevice(); surface->view = [[NSView alloc] initWithFrame: frame];
[surface->device retain]; [surface->view setWantsLayer:YES];
surface->metalLayer = [CAMetalLayer layer];
[surface->metalLayer retain]; [[windowData->osx.nsWindow contentView] addSubview: surface->view];
[surface->metalLayer setOpaque:NO];
surface->drawableSemaphore = dispatch_semaphore_create(MP_METAL_MAX_DRAWABLES_IN_FLIGHT);
surface->metalLayer.device = surface->device;
//-----------------------------------------------------------
//----------------------------------------------------------- //NOTE(martin): create a metal device and a metal layer and
//NOTE(martin): set the size and scaling //-----------------------------------------------------------
//----------------------------------------------------------- surface->device = MTLCreateSystemDefaultDevice();
CGSize size = surface->view.bounds.size; [surface->device retain];
size.width *= MG_METAL_SURFACE_CONTENTS_SCALING; surface->metalLayer = [CAMetalLayer layer];
size.height *= MG_METAL_SURFACE_CONTENTS_SCALING; [surface->metalLayer retain];
surface->metalLayer.drawableSize = size; [surface->metalLayer setOpaque:NO];
surface->metalLayer.contentsScale = MG_METAL_SURFACE_CONTENTS_SCALING;
surface->metalLayer.device = surface->device;
surface->metalLayer.pixelFormat = MTLPixelFormatBGRA8Unorm;
//-----------------------------------------------------------
surface->view.wantsLayer = YES; //NOTE(martin): set the size and scaling
surface->view.layer = surface->metalLayer; //-----------------------------------------------------------
CGSize size = surface->view.bounds.size;
//NOTE(martin): handling resizing size.width *= MG_METAL_SURFACE_CONTENTS_SCALING;
surface->view.layerContentsRedrawPolicy = NSViewLayerContentsRedrawDuringViewResize; size.height *= MG_METAL_SURFACE_CONTENTS_SCALING;
surface->metalLayer.autoresizingMask = kCALayerHeightSizable | kCALayerWidthSizable; surface->metalLayer.drawableSize = size;
surface->metalLayer.needsDisplayOnBoundsChange = YES; surface->metalLayer.contentsScale = MG_METAL_SURFACE_CONTENTS_SCALING;
//----------------------------------------------------------- surface->metalLayer.pixelFormat = MTLPixelFormatBGRA8Unorm;
//NOTE(martin): create a command queue
//----------------------------------------------------------- surface->view.wantsLayer = YES;
surface->commandQueue = [surface->device newCommandQueue]; surface->view.layer = surface->metalLayer;
[surface->commandQueue retain];
//NOTE(martin): handling resizing
//NOTE(martin): command buffer and drawable are set on demand and at the end of each present() call surface->view.layerContentsRedrawPolicy = NSViewLayerContentsRedrawDuringViewResize;
surface->drawable = nil; surface->metalLayer.autoresizingMask = kCALayerHeightSizable | kCALayerWidthSizable;
surface->commandBuffer = nil; surface->metalLayer.needsDisplayOnBoundsChange = YES;
}
//-----------------------------------------------------------
mg_surface handle = mg_surface_alloc_handle((mg_surface_data*)surface); //NOTE(martin): create a command queue
return(handle); //-----------------------------------------------------------
} surface->commandQueue = [surface->device newCommandQueue];
} [surface->commandQueue retain];
void* mg_metal_surface_layer(mg_surface surface) //NOTE(martin): command buffer and drawable are set on demand and at the end of each present() call
{ surface->drawable = nil;
mg_surface_data* surfaceData = mg_surface_data_from_handle(surface); surface->commandBuffer = nil;
if(surfaceData && surfaceData->backend == MG_BACKEND_METAL) }
{
mg_metal_surface* metalSurface = (mg_metal_surface*)surfaceData; mg_surface handle = mg_surface_alloc_handle((mg_surface_data*)surface);
return(metalSurface->metalLayer); return(handle);
} }
else }
{
return(nil); void* mg_metal_surface_layer(mg_surface surface)
} {
} mg_surface_data* surfaceData = mg_surface_data_from_handle(surface);
if(surfaceData && surfaceData->backend == MG_BACKEND_METAL)
void* mg_metal_surface_drawable(mg_surface surface) {
{ mg_metal_surface* metalSurface = (mg_metal_surface*)surfaceData;
mg_surface_data* surfaceData = mg_surface_data_from_handle(surface); return(metalSurface->metalLayer);
if(surfaceData && surfaceData->backend == MG_BACKEND_METAL) }
{ else
mg_metal_surface* metalSurface = (mg_metal_surface*)surfaceData; {
return(metalSurface->drawable); return(nil);
} }
else }
{
return(nil); void* mg_metal_surface_drawable(mg_surface surface)
} {
} mg_surface_data* surfaceData = mg_surface_data_from_handle(surface);
if(surfaceData && surfaceData->backend == MG_BACKEND_METAL)
void* mg_metal_surface_command_buffer(mg_surface surface) {
{ mg_metal_surface* metalSurface = (mg_metal_surface*)surfaceData;
mg_surface_data* surfaceData = mg_surface_data_from_handle(surface); return(metalSurface->drawable);
if(surfaceData && surfaceData->backend == MG_BACKEND_METAL) }
{ else
mg_metal_surface* metalSurface = (mg_metal_surface*)surfaceData; {
return(metalSurface->commandBuffer); return(nil);
} }
else }
{
return(nil); void* mg_metal_surface_command_buffer(mg_surface surface)
} {
} mg_surface_data* surfaceData = mg_surface_data_from_handle(surface);
if(surfaceData && surfaceData->backend == MG_BACKEND_METAL)
{
#undef LOG_SUBSYSTEM mg_metal_surface* metalSurface = (mg_metal_surface*)surfaceData;
return(metalSurface->commandBuffer);
}
else
{
return(nil);
}
}
#undef LOG_SUBSYSTEM

View File

@ -516,6 +516,16 @@ mp_window mp_window_create(mp_rect rect, const char* title, mp_window_style styl
goto quit; goto quit;
} }
/*
//NOTE: get primary monitor dimensions
const POINT ptZero = { 0, 0 };
HMONITOR monitor = MonitorFromPoint(ptZero, MONITOR_DEFAULTTOPRIMARY);
MONITORINFO monitorInfo = {.cbSize = sizeof(MONITORINFO)};
GetMonitorInfo(monitor, &monitorInfo);
RECT adjustRect = {rect.x, monitorInfo.rcMonitor.bottom - rect.y - rect.h, rect.w, rect.h};
AdjustWindowRect(&adjustRect, WS_OVERLAPPEDWINDOW, false);
*/
HWND windowHandle = CreateWindow("ApplicationWindowClass", "Test Window", HWND windowHandle = CreateWindow("ApplicationWindowClass", "Test Window",
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
rect.w, rect.h, rect.w, rect.h,
@ -721,6 +731,21 @@ void mp_window_bring_to_front(mp_window window)
} }
} }
mp_rect mp_window_get_content_rect(mp_window window)
{
mp_rect rect = {0};
mp_window_data* windowData = mp_window_ptr_from_handle(window);
if(windowData)
{
RECT winRect;
if(GetClientRect(windowData->win32.hWnd, &winRect))
{
rect = (mp_rect){0, 0, winRect.right - winRect.left, winRect.bottom - winRect.top};
}
}
return(rect);
}
/////////////////////////////////////////// WIP /////////////////////////////////////////////// /////////////////////////////////////////// WIP ///////////////////////////////////////////////
//TODO: this is thrown here for a quick test. We should: //TODO: this is thrown here for a quick test. We should:
// - check for errors // - check for errors

View File

@ -39,10 +39,15 @@ void mg_gl_surface_prepare(mg_surface_data* interface)
void mg_gl_surface_present(mg_surface_data* interface) void mg_gl_surface_present(mg_surface_data* interface)
{ {
mg_gl_surface* surface = (mg_gl_surface*)interface; mg_gl_surface* surface = (mg_gl_surface*)interface;
SwapBuffers(surface->hDC); SwapBuffers(surface->hDC);
} }
void mg_gl_surface_swap_interval(mg_surface_data* interface, int swap)
{
mg_gl_surface* surface = (mg_gl_surface*)interface;
wglSwapIntervalEXT(swap);
}
mp_rect mg_gl_surface_get_frame(mg_surface_data* interface) mp_rect mg_gl_surface_get_frame(mg_surface_data* interface)
{ {
mg_gl_surface* surface = (mg_gl_surface*)interface; mg_gl_surface* surface = (mg_gl_surface*)interface;
@ -186,9 +191,7 @@ mg_surface mg_gl_surface_create_for_window(mp_window window)
//NOTE: make gl context current //NOTE: make gl context current
wglMakeCurrent(hDC, glContext); wglMakeCurrent(hDC, glContext);
wglSwapIntervalEXT(1);
//TODO: set this back to 1 after testing
wglSwapIntervalEXT(0);
//TODO save important info in surface_data and return a handle //TODO save important info in surface_data and return a handle
mg_gl_surface* surface = malloc_type(mg_gl_surface); mg_gl_surface* surface = malloc_type(mg_gl_surface);
@ -196,6 +199,7 @@ mg_surface mg_gl_surface_create_for_window(mp_window window)
surface->interface.destroy = mg_gl_surface_destroy; surface->interface.destroy = mg_gl_surface_destroy;
surface->interface.prepare = mg_gl_surface_prepare; surface->interface.prepare = mg_gl_surface_prepare;
surface->interface.present = mg_gl_surface_present; surface->interface.present = mg_gl_surface_present;
surface->interface.swapInterval = mg_gl_surface_swap_interval;
surface->interface.getFrame = mg_gl_surface_get_frame; surface->interface.getFrame = mg_gl_surface_get_frame;
//TODO: get/set frame/hidden //TODO: get/set frame/hidden

View File

@ -3,17 +3,22 @@ Canvas renderer perf
-------------------- --------------------
[.] Perf [.] Perf
[x] Split vertex data into per-vertex and per-shape data. Keep only pos, cubics, and shapeID in vertex data [x] Split vertex data into per-vertex and per-shape data. Keep only pos, cubics, and shapeID in vertex data
[>] make zIndex implicit when calling push_vertex
[>] rename zIndex with "shapeIndex" everywhere
[>] remove color args in functions that don't need it anymore
[?] use half-floats or short fixed-point for pos and uv, packing them in two ints [?] use half-floats or short fixed-point for pos and uv, packing them in two ints
[?] pre-compute triangle edges/bounding boxes? [?] pre-compute triangle edges/bounding boxes?
[ ] Use clip rects [>] Add surface scaling for high dpi surfaces
[x] Allow setting swap interval
[!] Allow swap interval of 0 on macos
[>] Use clip rects in tiling/drawing pass
[ ] Clean canvas code
[ ] make zIndex implicit when calling push_vertex
[ ] rename zIndex with "shapeIndex" everywhere
[ ] remove color args in functions that don't need it anymore
[ ] Clean shaders (folder/filenames, version string, debug flags, ...) [ ] Clean shaders (folder/filenames, version string, debug flags, ...)
[ ] Correctly handle surface size
[ ] Add surface scaling for high dpi surfaces
[ ] Allow setting swap interval
[ ] Fix resource loading path in examples (to load font relative to executable) [ ] Fix resource loading path in examples (to load font relative to executable)