Allow setting surface swap interval in opengl surface
This commit is contained in:
parent
c982b524c0
commit
e6e674ee04
|
@ -58,8 +58,10 @@ int main()
|
|||
mp_init();
|
||||
mp_clock_init(); //TODO put that in mp_init()?
|
||||
|
||||
mp_rect rect = {.x = 100, .y = 100, .w = 810, .h = 610};
|
||||
mp_window window = mp_window_create(rect, "test", 0);
|
||||
mp_rect windowRect = {.x = 100, .y = 100, .w = 810, .h = 610};
|
||||
mp_window window = mp_window_create(windowRect, "test", 0);
|
||||
|
||||
mp_rect contentRect = mp_window_get_content_rect(window);
|
||||
|
||||
//NOTE: create surface
|
||||
#if defined(OS_MACOS)
|
||||
|
@ -70,6 +72,8 @@ int main()
|
|||
#error "unsupported OS"
|
||||
#endif
|
||||
|
||||
mg_surface_swap_interval(surface, 0);
|
||||
|
||||
//TODO: create canvas
|
||||
mg_canvas canvas = mg_canvas_create(surface);
|
||||
mg_font font = create_font();
|
||||
|
@ -79,7 +83,8 @@ int main()
|
|||
mp_window_focus(window);
|
||||
|
||||
f32 x = 400, y = 300;
|
||||
f32 dx = 5, dy = 5;
|
||||
f32 speed = 0;
|
||||
f32 dx = speed, dy = speed;
|
||||
f64 frameTime = 0;
|
||||
|
||||
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.code == MP_KEY_LEFT)
|
||||
{
|
||||
dx-=5.1;
|
||||
if(x - 200 > 0)
|
||||
{
|
||||
x-=1;
|
||||
}
|
||||
}
|
||||
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)
|
||||
{
|
||||
dy+=5.1;
|
||||
if(y + 200 < contentRect.h)
|
||||
{
|
||||
y+=1;
|
||||
}
|
||||
}
|
||||
else if(event.key.code == MP_KEY_DOWN)
|
||||
{
|
||||
dy-=5.1;
|
||||
if(y - 200 > 0)
|
||||
{
|
||||
y-=1;
|
||||
}
|
||||
}
|
||||
*/
|
||||
//*/
|
||||
}
|
||||
} break;
|
||||
|
||||
|
@ -138,19 +155,19 @@ int main()
|
|||
|
||||
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)
|
||||
{
|
||||
dy = 5;
|
||||
dy = speed;
|
||||
}
|
||||
if(y+200 > 550)
|
||||
if(y+200 > contentRect.h)
|
||||
{
|
||||
dy = -5;
|
||||
dy = -speed;
|
||||
}
|
||||
x += dx;
|
||||
y += dy;
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
DEBUG_ASSERT(__mgData.init);
|
||||
|
|
421
src/graphics.h
421
src/graphics.h
|
@ -1,210 +1,211 @@
|
|||
/************************************************************//**
|
||||
*
|
||||
* @file: graphics.h
|
||||
* @author: Martin Fouilleul
|
||||
* @date: 23/01/2023
|
||||
* @revision:
|
||||
*
|
||||
*****************************************************************/
|
||||
#ifndef __GRAPHICS_H_
|
||||
#define __GRAPHICS_H_
|
||||
|
||||
//------------------------------------------------------------------------------------------
|
||||
//NOTE(martin): graphics surface
|
||||
//------------------------------------------------------------------------------------------
|
||||
typedef struct mg_surface { u64 h; } mg_surface;
|
||||
|
||||
mg_surface mg_surface_nil();
|
||||
bool mg_surface_is_nil(mg_surface surface);
|
||||
|
||||
void mg_surface_destroy(mg_surface surface);
|
||||
void mg_surface_prepare(mg_surface surface);
|
||||
void mg_surface_present(mg_surface surface);
|
||||
mp_rect mg_surface_get_frame(mg_surface surface);
|
||||
void mg_surface_set_frame(mg_surface surface, mp_rect frame);
|
||||
bool mg_surface_get_hidden(mg_surface surface);
|
||||
void mg_surface_set_hidden(mg_surface surface, bool hidden);
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------------------
|
||||
//NOTE(martin): graphics canvas structs
|
||||
//------------------------------------------------------------------------------------------
|
||||
typedef struct mg_canvas { u64 h; } mg_canvas;
|
||||
typedef struct mg_stream { u64 h; } mg_stream;
|
||||
typedef struct mg_font { u64 h; } mg_font;
|
||||
|
||||
typedef struct mg_mat2x3
|
||||
{
|
||||
f32 m[6];
|
||||
} mg_mat2x3;
|
||||
|
||||
typedef struct mg_color
|
||||
{
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
f32 r;
|
||||
f32 g;
|
||||
f32 b;
|
||||
f32 a;
|
||||
};
|
||||
f32 c[4];
|
||||
};
|
||||
} mg_color;
|
||||
|
||||
typedef enum {MG_JOINT_MITER = 0,
|
||||
MG_JOINT_BEVEL,
|
||||
MG_JOINT_NONE } mg_joint_type;
|
||||
|
||||
typedef enum {MG_CAP_NONE = 0,
|
||||
MG_CAP_SQUARE } mg_cap_type;
|
||||
|
||||
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 leading; // spacing between one row's descent and the next row's ascent
|
||||
f32 xHeight; // height of the lower case letter 'x'
|
||||
f32 capHeight; // height of the upper case letter 'M'
|
||||
f32 width; // maximum width of the font
|
||||
|
||||
} mg_font_extents;
|
||||
|
||||
typedef struct mg_text_extents
|
||||
{
|
||||
f32 xBearing;
|
||||
f32 yBearing;
|
||||
f32 width;
|
||||
f32 height;
|
||||
f32 xAdvance;
|
||||
f32 yAdvance;
|
||||
|
||||
} mg_text_extents;
|
||||
|
||||
//------------------------------------------------------------------------------------------
|
||||
//NOTE(martin): graphics canvas
|
||||
//------------------------------------------------------------------------------------------
|
||||
|
||||
mg_canvas mg_canvas_create(mg_surface surface);
|
||||
void mg_canvas_destroy(mg_canvas canvas);
|
||||
mg_canvas mg_canvas_set_current(mg_canvas canvas);
|
||||
|
||||
void mg_flush();
|
||||
|
||||
//------------------------------------------------------------------------------------------
|
||||
//NOTE(martin): transform, viewport and clipping
|
||||
//------------------------------------------------------------------------------------------
|
||||
void mg_viewport(mp_rect viewPort);
|
||||
|
||||
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();
|
||||
|
||||
//------------------------------------------------------------------------------------------
|
||||
//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_width(f32 width);
|
||||
void mg_set_tolerance(f32 tolerance);
|
||||
void mg_set_joint(mg_joint_type joint);
|
||||
void mg_set_max_joint_excursion(f32 maxJointExcursion);
|
||||
void mg_set_cap(mg_cap_type cap);
|
||||
void mg_set_font(mg_font font);
|
||||
void mg_set_font_size(f32 size);
|
||||
void mg_set_text_flip(bool flip);
|
||||
|
||||
mg_color mg_get_color();
|
||||
f32 mg_get_width();
|
||||
f32 mg_get_tolerance();
|
||||
mg_joint_type mg_get_joint();
|
||||
f32 mg_get_max_joint_excursion();
|
||||
mg_cap_type mg_get_cap();
|
||||
mg_font mg_get_font();
|
||||
f32 mg_get_font_size();
|
||||
bool mg_get_text_flip();
|
||||
|
||||
//------------------------------------------------------------------------------------------
|
||||
//NOTE(martin): path construction
|
||||
//------------------------------------------------------------------------------------------
|
||||
vec2 mg_get_position();
|
||||
void mg_move_to(f32 x, f32 y);
|
||||
void mg_line_to(f32 x, f32 y);
|
||||
void mg_quadratic_to(f32 x1, f32 y1, f32 x2, f32 y2);
|
||||
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);
|
||||
void mg_text_outlines(str8 string);
|
||||
|
||||
//------------------------------------------------------------------------------------------
|
||||
//NOTE(martin): clear/fill/stroke
|
||||
//------------------------------------------------------------------------------------------
|
||||
void mg_clear();
|
||||
void mg_fill();
|
||||
void mg_stroke();
|
||||
|
||||
//------------------------------------------------------------------------------------------
|
||||
//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_rounded_rectangle_fill(f32 x, f32 y, f32 w, f32 h, f32 r);
|
||||
void mg_rounded_rectangle_stroke(f32 x, f32 y, f32 w, f32 h, f32 r);
|
||||
void mg_ellipse_fill(f32 x, f32 y, f32 rx, f32 ry);
|
||||
void mg_ellipse_stroke(f32 x, f32 y, f32 rx, f32 ry);
|
||||
void mg_circle_fill(f32 x, f32 y, f32 r);
|
||||
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
|
||||
//------------------------------------------------------------------------------------------
|
||||
mg_font mg_font_nil();
|
||||
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
|
||||
|
||||
mg_font_extents mg_font_get_extents(mg_font font);
|
||||
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.
|
||||
|
||||
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);
|
||||
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_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);
|
||||
|
||||
//------------------------------------------------------------------------------------------
|
||||
//NOTE(martin): images
|
||||
//------------------------------------------------------------------------------------------
|
||||
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_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_file(str8 path, bool flip);
|
||||
|
||||
void mg_image_drestroy(mg_image image);
|
||||
|
||||
vec2 mg_image_size(mg_image image);
|
||||
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_
|
||||
/************************************************************//**
|
||||
*
|
||||
* @file: graphics.h
|
||||
* @author: Martin Fouilleul
|
||||
* @date: 23/01/2023
|
||||
* @revision:
|
||||
*
|
||||
*****************************************************************/
|
||||
#ifndef __GRAPHICS_H_
|
||||
#define __GRAPHICS_H_
|
||||
|
||||
//------------------------------------------------------------------------------------------
|
||||
//NOTE(martin): graphics surface
|
||||
//------------------------------------------------------------------------------------------
|
||||
typedef struct mg_surface { u64 h; } mg_surface;
|
||||
|
||||
mg_surface mg_surface_nil();
|
||||
bool mg_surface_is_nil(mg_surface surface);
|
||||
|
||||
void mg_surface_destroy(mg_surface surface);
|
||||
void mg_surface_prepare(mg_surface surface);
|
||||
void mg_surface_present(mg_surface surface);
|
||||
void mg_surface_swap_interval(mg_surface surface, int swap);
|
||||
mp_rect mg_surface_get_frame(mg_surface surface);
|
||||
void mg_surface_set_frame(mg_surface surface, mp_rect frame);
|
||||
bool mg_surface_get_hidden(mg_surface surface);
|
||||
void mg_surface_set_hidden(mg_surface surface, bool hidden);
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------------------
|
||||
//NOTE(martin): graphics canvas structs
|
||||
//------------------------------------------------------------------------------------------
|
||||
typedef struct mg_canvas { u64 h; } mg_canvas;
|
||||
typedef struct mg_stream { u64 h; } mg_stream;
|
||||
typedef struct mg_font { u64 h; } mg_font;
|
||||
|
||||
typedef struct mg_mat2x3
|
||||
{
|
||||
f32 m[6];
|
||||
} mg_mat2x3;
|
||||
|
||||
typedef struct mg_color
|
||||
{
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
f32 r;
|
||||
f32 g;
|
||||
f32 b;
|
||||
f32 a;
|
||||
};
|
||||
f32 c[4];
|
||||
};
|
||||
} mg_color;
|
||||
|
||||
typedef enum {MG_JOINT_MITER = 0,
|
||||
MG_JOINT_BEVEL,
|
||||
MG_JOINT_NONE } mg_joint_type;
|
||||
|
||||
typedef enum {MG_CAP_NONE = 0,
|
||||
MG_CAP_SQUARE } mg_cap_type;
|
||||
|
||||
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 leading; // spacing between one row's descent and the next row's ascent
|
||||
f32 xHeight; // height of the lower case letter 'x'
|
||||
f32 capHeight; // height of the upper case letter 'M'
|
||||
f32 width; // maximum width of the font
|
||||
|
||||
} mg_font_extents;
|
||||
|
||||
typedef struct mg_text_extents
|
||||
{
|
||||
f32 xBearing;
|
||||
f32 yBearing;
|
||||
f32 width;
|
||||
f32 height;
|
||||
f32 xAdvance;
|
||||
f32 yAdvance;
|
||||
|
||||
} mg_text_extents;
|
||||
|
||||
//------------------------------------------------------------------------------------------
|
||||
//NOTE(martin): graphics canvas
|
||||
//------------------------------------------------------------------------------------------
|
||||
|
||||
mg_canvas mg_canvas_create(mg_surface surface);
|
||||
void mg_canvas_destroy(mg_canvas canvas);
|
||||
mg_canvas mg_canvas_set_current(mg_canvas canvas);
|
||||
|
||||
void mg_flush();
|
||||
|
||||
//------------------------------------------------------------------------------------------
|
||||
//NOTE(martin): transform, viewport and clipping
|
||||
//------------------------------------------------------------------------------------------
|
||||
void mg_viewport(mp_rect viewPort);
|
||||
|
||||
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();
|
||||
|
||||
//------------------------------------------------------------------------------------------
|
||||
//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_width(f32 width);
|
||||
void mg_set_tolerance(f32 tolerance);
|
||||
void mg_set_joint(mg_joint_type joint);
|
||||
void mg_set_max_joint_excursion(f32 maxJointExcursion);
|
||||
void mg_set_cap(mg_cap_type cap);
|
||||
void mg_set_font(mg_font font);
|
||||
void mg_set_font_size(f32 size);
|
||||
void mg_set_text_flip(bool flip);
|
||||
|
||||
mg_color mg_get_color();
|
||||
f32 mg_get_width();
|
||||
f32 mg_get_tolerance();
|
||||
mg_joint_type mg_get_joint();
|
||||
f32 mg_get_max_joint_excursion();
|
||||
mg_cap_type mg_get_cap();
|
||||
mg_font mg_get_font();
|
||||
f32 mg_get_font_size();
|
||||
bool mg_get_text_flip();
|
||||
|
||||
//------------------------------------------------------------------------------------------
|
||||
//NOTE(martin): path construction
|
||||
//------------------------------------------------------------------------------------------
|
||||
vec2 mg_get_position();
|
||||
void mg_move_to(f32 x, f32 y);
|
||||
void mg_line_to(f32 x, f32 y);
|
||||
void mg_quadratic_to(f32 x1, f32 y1, f32 x2, f32 y2);
|
||||
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);
|
||||
void mg_text_outlines(str8 string);
|
||||
|
||||
//------------------------------------------------------------------------------------------
|
||||
//NOTE(martin): clear/fill/stroke
|
||||
//------------------------------------------------------------------------------------------
|
||||
void mg_clear();
|
||||
void mg_fill();
|
||||
void mg_stroke();
|
||||
|
||||
//------------------------------------------------------------------------------------------
|
||||
//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_rounded_rectangle_fill(f32 x, f32 y, f32 w, f32 h, f32 r);
|
||||
void mg_rounded_rectangle_stroke(f32 x, f32 y, f32 w, f32 h, f32 r);
|
||||
void mg_ellipse_fill(f32 x, f32 y, f32 rx, f32 ry);
|
||||
void mg_ellipse_stroke(f32 x, f32 y, f32 rx, f32 ry);
|
||||
void mg_circle_fill(f32 x, f32 y, f32 r);
|
||||
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
|
||||
//------------------------------------------------------------------------------------------
|
||||
mg_font mg_font_nil();
|
||||
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
|
||||
|
||||
mg_font_extents mg_font_get_extents(mg_font font);
|
||||
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.
|
||||
|
||||
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);
|
||||
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_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);
|
||||
|
||||
//------------------------------------------------------------------------------------------
|
||||
//NOTE(martin): images
|
||||
//------------------------------------------------------------------------------------------
|
||||
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_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_file(str8 path, bool flip);
|
||||
|
||||
void mg_image_drestroy(mg_image image);
|
||||
|
||||
vec2 mg_image_size(mg_image image);
|
||||
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_
|
||||
|
|
|
@ -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_prepare_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 void (*mg_surface_set_frame_proc)(mg_surface_data* surface, mp_rect frame);
|
||||
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_prepare_proc prepare;
|
||||
mg_surface_present_proc present;
|
||||
mg_surface_swap_interval_proc swapInterval;
|
||||
mg_surface_get_frame_proc getFrame;
|
||||
mg_surface_set_frame_proc setFrame;
|
||||
mg_surface_get_hidden_proc getHidden;
|
||||
|
|
|
@ -1,299 +1,309 @@
|
|||
/************************************************************//**
|
||||
*
|
||||
* @file: graphics.m
|
||||
* @author: Martin Fouilleul
|
||||
* @date: 12/07/2020
|
||||
* @revision:
|
||||
*
|
||||
*****************************************************************/
|
||||
#import<Metal/Metal.h>
|
||||
#import <QuartzCore/QuartzCore.h>
|
||||
#import<QuartzCore/CAMetalLayer.h>
|
||||
#include<simd/simd.h>
|
||||
|
||||
#include"graphics_internal.h"
|
||||
#include"macro_helpers.h"
|
||||
#include"osx_app.h"
|
||||
|
||||
#define LOG_SUBSYSTEM "Graphics"
|
||||
|
||||
static const u32 MP_METAL_MAX_DRAWABLES_IN_FLIGHT = 3;
|
||||
|
||||
typedef struct mg_metal_surface
|
||||
{
|
||||
mg_surface_data interface;
|
||||
|
||||
// permanent metal resources
|
||||
NSView* view;
|
||||
id<MTLDevice> device;
|
||||
CAMetalLayer* metalLayer;
|
||||
id<MTLCommandQueue> commandQueue;
|
||||
|
||||
// transient metal resources
|
||||
id<CAMetalDrawable> drawable;
|
||||
id<MTLCommandBuffer> commandBuffer;
|
||||
|
||||
dispatch_semaphore_t drawableSemaphore;
|
||||
|
||||
} mg_metal_surface;
|
||||
|
||||
void mg_metal_surface_destroy(mg_surface_data* interface)
|
||||
{
|
||||
mg_metal_surface* surface = (mg_metal_surface*)interface;
|
||||
|
||||
@autoreleasepool
|
||||
{
|
||||
[surface->commandQueue release];
|
||||
[surface->metalLayer release];
|
||||
[surface->device release];
|
||||
}
|
||||
}
|
||||
|
||||
void mg_metal_surface_acquire_drawable_and_command_buffer(mg_metal_surface* surface)
|
||||
{@autoreleasepool{
|
||||
/*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
|
||||
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
|
||||
memory allocated in the GPU is never freed...)
|
||||
|
||||
In background the gpu seems to create drawable if none is available instead of actually
|
||||
blocking on nextDrawable. These drawable never get freed.
|
||||
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
|
||||
|
||||
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
|
||||
incremented in the presentedHandler of the drawable.
|
||||
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
|
||||
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.
|
||||
|
||||
//TODO: We should set a reasonable timeout and skip the frame and log an error in case we are stalled
|
||||
for too long.
|
||||
*/
|
||||
dispatch_semaphore_wait(surface->drawableSemaphore, DISPATCH_TIME_FOREVER);
|
||||
|
||||
surface->drawable = [surface->metalLayer nextDrawable];
|
||||
ASSERT(surface->drawable != nil);
|
||||
|
||||
//TODO: make this a weak reference if we use ARC
|
||||
dispatch_semaphore_t semaphore = surface->drawableSemaphore;
|
||||
[surface->drawable addPresentedHandler:^(id<MTLDrawable> drawable){
|
||||
dispatch_semaphore_signal(semaphore);
|
||||
}];
|
||||
|
||||
//NOTE(martin): create a command buffer
|
||||
surface->commandBuffer = [surface->commandQueue commandBuffer];
|
||||
|
||||
[surface->commandBuffer retain];
|
||||
[surface->drawable retain];
|
||||
}}
|
||||
|
||||
void mg_metal_surface_prepare(mg_surface_data* interface)
|
||||
{
|
||||
mg_metal_surface* surface = (mg_metal_surface*)interface;
|
||||
mg_metal_surface_acquire_drawable_and_command_buffer(surface);
|
||||
|
||||
@autoreleasepool
|
||||
{
|
||||
MTLRenderPassDescriptor* renderPassDescriptor = [MTLRenderPassDescriptor renderPassDescriptor];
|
||||
renderPassDescriptor.colorAttachments[0].texture = surface->drawable.texture;
|
||||
renderPassDescriptor.colorAttachments[0].loadAction = MTLLoadActionClear;
|
||||
renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColorMake(0.0,0.0,0.0,0.0);
|
||||
|
||||
id<MTLRenderCommandEncoder> renderEncoder = [surface->commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor];
|
||||
[renderEncoder endEncoding];
|
||||
}
|
||||
}
|
||||
|
||||
void mg_metal_surface_present(mg_surface_data* interface)
|
||||
{
|
||||
mg_metal_surface* surface = (mg_metal_surface*)interface;
|
||||
@autoreleasepool
|
||||
{
|
||||
//NOTE(martin): present drawable and commit command buffer
|
||||
[surface->commandBuffer presentDrawable: surface->drawable];
|
||||
[surface->commandBuffer commit];
|
||||
[surface->commandBuffer waitUntilCompleted];
|
||||
|
||||
//NOTE(martin): acquire next frame resources
|
||||
[surface->commandBuffer release];
|
||||
surface->commandBuffer = nil;
|
||||
[surface->drawable release];
|
||||
surface->drawable = nil;
|
||||
}
|
||||
}
|
||||
|
||||
void mg_metal_surface_set_frame(mg_surface_data* interface, mp_rect frame)
|
||||
{
|
||||
mg_metal_surface* surface = (mg_metal_surface*)interface;
|
||||
|
||||
@autoreleasepool
|
||||
{
|
||||
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};
|
||||
surface->metalLayer.drawableSize = drawableSize;
|
||||
}
|
||||
}
|
||||
|
||||
mp_rect mg_metal_surface_get_frame(mg_surface_data* interface)
|
||||
{
|
||||
mg_metal_surface* surface = (mg_metal_surface*)interface;
|
||||
|
||||
@autoreleasepool
|
||||
{
|
||||
CGRect frame = surface->view.frame;
|
||||
return((mp_rect){frame.origin.x, frame.origin.y, frame.size.width, frame.size.height});
|
||||
}
|
||||
}
|
||||
|
||||
void mg_metal_surface_set_hidden(mg_surface_data* interface, bool hidden)
|
||||
{
|
||||
mg_metal_surface* surface = (mg_metal_surface*)interface;
|
||||
@autoreleasepool
|
||||
{
|
||||
[CATransaction begin];
|
||||
[CATransaction setDisableActions:YES];
|
||||
[surface->metalLayer setHidden:hidden];
|
||||
[CATransaction commit];
|
||||
}
|
||||
}
|
||||
|
||||
bool mg_metal_surface_get_hidden(mg_surface_data* interface)
|
||||
{
|
||||
mg_metal_surface* surface = (mg_metal_surface*)interface;
|
||||
@autoreleasepool
|
||||
{
|
||||
return([surface->metalLayer isHidden]);
|
||||
}
|
||||
}
|
||||
|
||||
//TODO fix that according to real scaling, depending on the monitor settings
|
||||
static const f32 MG_METAL_SURFACE_CONTENTS_SCALING = 2;
|
||||
|
||||
mg_surface mg_metal_surface_create_for_window(mp_window window)
|
||||
{
|
||||
mp_window_data* windowData = mp_window_ptr_from_handle(window);
|
||||
if(!windowData)
|
||||
{
|
||||
return(mg_surface_nil());
|
||||
}
|
||||
else
|
||||
{
|
||||
mg_metal_surface* surface = (mg_metal_surface*)malloc(sizeof(mg_metal_surface));
|
||||
|
||||
//NOTE(martin): setup interface functions
|
||||
surface->interface.backend = MG_BACKEND_METAL;
|
||||
surface->interface.destroy = mg_metal_surface_destroy;
|
||||
surface->interface.prepare = mg_metal_surface_prepare;
|
||||
surface->interface.present = mg_metal_surface_present;
|
||||
surface->interface.getFrame = mg_metal_surface_get_frame;
|
||||
surface->interface.setFrame = mg_metal_surface_set_frame;
|
||||
surface->interface.getHidden = mg_metal_surface_get_hidden;
|
||||
surface->interface.setHidden = mg_metal_surface_set_hidden;
|
||||
|
||||
@autoreleasepool
|
||||
{
|
||||
NSRect frame = [[windowData->osx.nsWindow contentView] frame];
|
||||
surface->view = [[NSView alloc] initWithFrame: frame];
|
||||
[surface->view setWantsLayer:YES];
|
||||
|
||||
[[windowData->osx.nsWindow contentView] addSubview: surface->view];
|
||||
|
||||
surface->drawableSemaphore = dispatch_semaphore_create(MP_METAL_MAX_DRAWABLES_IN_FLIGHT);
|
||||
|
||||
//-----------------------------------------------------------
|
||||
//NOTE(martin): create a metal device and a metal layer and
|
||||
//-----------------------------------------------------------
|
||||
surface->device = MTLCreateSystemDefaultDevice();
|
||||
[surface->device retain];
|
||||
surface->metalLayer = [CAMetalLayer layer];
|
||||
[surface->metalLayer retain];
|
||||
[surface->metalLayer setOpaque:NO];
|
||||
|
||||
surface->metalLayer.device = surface->device;
|
||||
|
||||
//-----------------------------------------------------------
|
||||
//NOTE(martin): set the size and scaling
|
||||
//-----------------------------------------------------------
|
||||
CGSize size = surface->view.bounds.size;
|
||||
size.width *= MG_METAL_SURFACE_CONTENTS_SCALING;
|
||||
size.height *= MG_METAL_SURFACE_CONTENTS_SCALING;
|
||||
surface->metalLayer.drawableSize = size;
|
||||
surface->metalLayer.contentsScale = MG_METAL_SURFACE_CONTENTS_SCALING;
|
||||
|
||||
surface->metalLayer.pixelFormat = MTLPixelFormatBGRA8Unorm;
|
||||
|
||||
surface->view.wantsLayer = YES;
|
||||
surface->view.layer = surface->metalLayer;
|
||||
|
||||
//NOTE(martin): handling resizing
|
||||
surface->view.layerContentsRedrawPolicy = NSViewLayerContentsRedrawDuringViewResize;
|
||||
surface->metalLayer.autoresizingMask = kCALayerHeightSizable | kCALayerWidthSizable;
|
||||
surface->metalLayer.needsDisplayOnBoundsChange = YES;
|
||||
|
||||
//-----------------------------------------------------------
|
||||
//NOTE(martin): create a command queue
|
||||
//-----------------------------------------------------------
|
||||
surface->commandQueue = [surface->device newCommandQueue];
|
||||
[surface->commandQueue retain];
|
||||
|
||||
//NOTE(martin): command buffer and drawable are set on demand and at the end of each present() call
|
||||
surface->drawable = nil;
|
||||
surface->commandBuffer = nil;
|
||||
}
|
||||
|
||||
mg_surface handle = mg_surface_alloc_handle((mg_surface_data*)surface);
|
||||
return(handle);
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
mg_metal_surface* metalSurface = (mg_metal_surface*)surfaceData;
|
||||
return(metalSurface->metalLayer);
|
||||
}
|
||||
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)
|
||||
{
|
||||
mg_metal_surface* metalSurface = (mg_metal_surface*)surfaceData;
|
||||
return(metalSurface->drawable);
|
||||
}
|
||||
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)
|
||||
{
|
||||
mg_metal_surface* metalSurface = (mg_metal_surface*)surfaceData;
|
||||
return(metalSurface->commandBuffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
return(nil);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#undef LOG_SUBSYSTEM
|
||||
/************************************************************//**
|
||||
*
|
||||
* @file: graphics.m
|
||||
* @author: Martin Fouilleul
|
||||
* @date: 12/07/2020
|
||||
* @revision:
|
||||
*
|
||||
*****************************************************************/
|
||||
#import<Metal/Metal.h>
|
||||
#import <QuartzCore/QuartzCore.h>
|
||||
#import<QuartzCore/CAMetalLayer.h>
|
||||
#include<simd/simd.h>
|
||||
|
||||
#include"graphics_internal.h"
|
||||
#include"macro_helpers.h"
|
||||
#include"osx_app.h"
|
||||
|
||||
#define LOG_SUBSYSTEM "Graphics"
|
||||
|
||||
static const u32 MP_METAL_MAX_DRAWABLES_IN_FLIGHT = 3;
|
||||
|
||||
typedef struct mg_metal_surface
|
||||
{
|
||||
mg_surface_data interface;
|
||||
|
||||
// permanent metal resources
|
||||
NSView* view;
|
||||
id<MTLDevice> device;
|
||||
CAMetalLayer* metalLayer;
|
||||
id<MTLCommandQueue> commandQueue;
|
||||
|
||||
// transient metal resources
|
||||
id<CAMetalDrawable> drawable;
|
||||
id<MTLCommandBuffer> commandBuffer;
|
||||
|
||||
dispatch_semaphore_t drawableSemaphore;
|
||||
|
||||
} mg_metal_surface;
|
||||
|
||||
void mg_metal_surface_destroy(mg_surface_data* interface)
|
||||
{
|
||||
mg_metal_surface* surface = (mg_metal_surface*)interface;
|
||||
|
||||
@autoreleasepool
|
||||
{
|
||||
[surface->commandQueue release];
|
||||
[surface->metalLayer release];
|
||||
[surface->device release];
|
||||
}
|
||||
}
|
||||
|
||||
void mg_metal_surface_acquire_drawable_and_command_buffer(mg_metal_surface* surface)
|
||||
{@autoreleasepool{
|
||||
/*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
|
||||
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
|
||||
memory allocated in the GPU is never freed...)
|
||||
|
||||
In background the gpu seems to create drawable if none is available instead of actually
|
||||
blocking on nextDrawable. These drawable never get freed.
|
||||
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
|
||||
|
||||
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
|
||||
incremented in the presentedHandler of the drawable.
|
||||
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
|
||||
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.
|
||||
|
||||
//TODO: We should set a reasonable timeout and skip the frame and log an error in case we are stalled
|
||||
for too long.
|
||||
*/
|
||||
dispatch_semaphore_wait(surface->drawableSemaphore, DISPATCH_TIME_FOREVER);
|
||||
|
||||
surface->drawable = [surface->metalLayer nextDrawable];
|
||||
ASSERT(surface->drawable != nil);
|
||||
|
||||
//TODO: make this a weak reference if we use ARC
|
||||
dispatch_semaphore_t semaphore = surface->drawableSemaphore;
|
||||
[surface->drawable addPresentedHandler:^(id<MTLDrawable> drawable){
|
||||
dispatch_semaphore_signal(semaphore);
|
||||
}];
|
||||
|
||||
//NOTE(martin): create a command buffer
|
||||
surface->commandBuffer = [surface->commandQueue commandBuffer];
|
||||
|
||||
[surface->commandBuffer retain];
|
||||
[surface->drawable retain];
|
||||
}}
|
||||
|
||||
void mg_metal_surface_prepare(mg_surface_data* interface)
|
||||
{
|
||||
mg_metal_surface* surface = (mg_metal_surface*)interface;
|
||||
mg_metal_surface_acquire_drawable_and_command_buffer(surface);
|
||||
|
||||
@autoreleasepool
|
||||
{
|
||||
MTLRenderPassDescriptor* renderPassDescriptor = [MTLRenderPassDescriptor renderPassDescriptor];
|
||||
renderPassDescriptor.colorAttachments[0].texture = surface->drawable.texture;
|
||||
renderPassDescriptor.colorAttachments[0].loadAction = MTLLoadActionClear;
|
||||
renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColorMake(0.0,0.0,0.0,0.0);
|
||||
|
||||
id<MTLRenderCommandEncoder> renderEncoder = [surface->commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor];
|
||||
[renderEncoder endEncoding];
|
||||
}
|
||||
}
|
||||
|
||||
void mg_metal_surface_present(mg_surface_data* interface)
|
||||
{
|
||||
mg_metal_surface* surface = (mg_metal_surface*)interface;
|
||||
@autoreleasepool
|
||||
{
|
||||
//NOTE(martin): present drawable and commit command buffer
|
||||
[surface->commandBuffer presentDrawable: surface->drawable];
|
||||
[surface->commandBuffer commit];
|
||||
[surface->commandBuffer waitUntilCompleted];
|
||||
|
||||
//NOTE(martin): acquire next frame resources
|
||||
[surface->commandBuffer release];
|
||||
surface->commandBuffer = nil;
|
||||
[surface->drawable release];
|
||||
surface->drawable = nil;
|
||||
}
|
||||
}
|
||||
|
||||
void mg_metal_surface_swap_interval(mg_surface_data* interface, int swap)
|
||||
{
|
||||
mg_metal_surface* surface = (mg_metal_surface*)interface;
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
//TODO
|
||||
////////////////////////////////////////////////////////////////
|
||||
}
|
||||
|
||||
void mg_metal_surface_set_frame(mg_surface_data* interface, mp_rect frame)
|
||||
{
|
||||
mg_metal_surface* surface = (mg_metal_surface*)interface;
|
||||
|
||||
@autoreleasepool
|
||||
{
|
||||
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};
|
||||
surface->metalLayer.drawableSize = drawableSize;
|
||||
}
|
||||
}
|
||||
|
||||
mp_rect mg_metal_surface_get_frame(mg_surface_data* interface)
|
||||
{
|
||||
mg_metal_surface* surface = (mg_metal_surface*)interface;
|
||||
|
||||
@autoreleasepool
|
||||
{
|
||||
CGRect frame = surface->view.frame;
|
||||
return((mp_rect){frame.origin.x, frame.origin.y, frame.size.width, frame.size.height});
|
||||
}
|
||||
}
|
||||
|
||||
void mg_metal_surface_set_hidden(mg_surface_data* interface, bool hidden)
|
||||
{
|
||||
mg_metal_surface* surface = (mg_metal_surface*)interface;
|
||||
@autoreleasepool
|
||||
{
|
||||
[CATransaction begin];
|
||||
[CATransaction setDisableActions:YES];
|
||||
[surface->metalLayer setHidden:hidden];
|
||||
[CATransaction commit];
|
||||
}
|
||||
}
|
||||
|
||||
bool mg_metal_surface_get_hidden(mg_surface_data* interface)
|
||||
{
|
||||
mg_metal_surface* surface = (mg_metal_surface*)interface;
|
||||
@autoreleasepool
|
||||
{
|
||||
return([surface->metalLayer isHidden]);
|
||||
}
|
||||
}
|
||||
|
||||
//TODO fix that according to real scaling, depending on the monitor settings
|
||||
static const f32 MG_METAL_SURFACE_CONTENTS_SCALING = 2;
|
||||
|
||||
mg_surface mg_metal_surface_create_for_window(mp_window window)
|
||||
{
|
||||
mp_window_data* windowData = mp_window_ptr_from_handle(window);
|
||||
if(!windowData)
|
||||
{
|
||||
return(mg_surface_nil());
|
||||
}
|
||||
else
|
||||
{
|
||||
mg_metal_surface* surface = (mg_metal_surface*)malloc(sizeof(mg_metal_surface));
|
||||
|
||||
//NOTE(martin): setup interface functions
|
||||
surface->interface.backend = MG_BACKEND_METAL;
|
||||
surface->interface.destroy = mg_metal_surface_destroy;
|
||||
surface->interface.prepare = mg_metal_surface_prepare;
|
||||
surface->interface.present = mg_metal_surface_present;
|
||||
surface->interface.swapInterval = mg_metal_surface_swap_interval;
|
||||
surface->interface.getFrame = mg_metal_surface_get_frame;
|
||||
surface->interface.setFrame = mg_metal_surface_set_frame;
|
||||
surface->interface.getHidden = mg_metal_surface_get_hidden;
|
||||
surface->interface.setHidden = mg_metal_surface_set_hidden;
|
||||
|
||||
@autoreleasepool
|
||||
{
|
||||
NSRect frame = [[windowData->osx.nsWindow contentView] frame];
|
||||
surface->view = [[NSView alloc] initWithFrame: frame];
|
||||
[surface->view setWantsLayer:YES];
|
||||
|
||||
[[windowData->osx.nsWindow contentView] addSubview: surface->view];
|
||||
|
||||
surface->drawableSemaphore = dispatch_semaphore_create(MP_METAL_MAX_DRAWABLES_IN_FLIGHT);
|
||||
|
||||
//-----------------------------------------------------------
|
||||
//NOTE(martin): create a metal device and a metal layer and
|
||||
//-----------------------------------------------------------
|
||||
surface->device = MTLCreateSystemDefaultDevice();
|
||||
[surface->device retain];
|
||||
surface->metalLayer = [CAMetalLayer layer];
|
||||
[surface->metalLayer retain];
|
||||
[surface->metalLayer setOpaque:NO];
|
||||
|
||||
surface->metalLayer.device = surface->device;
|
||||
|
||||
//-----------------------------------------------------------
|
||||
//NOTE(martin): set the size and scaling
|
||||
//-----------------------------------------------------------
|
||||
CGSize size = surface->view.bounds.size;
|
||||
size.width *= MG_METAL_SURFACE_CONTENTS_SCALING;
|
||||
size.height *= MG_METAL_SURFACE_CONTENTS_SCALING;
|
||||
surface->metalLayer.drawableSize = size;
|
||||
surface->metalLayer.contentsScale = MG_METAL_SURFACE_CONTENTS_SCALING;
|
||||
|
||||
surface->metalLayer.pixelFormat = MTLPixelFormatBGRA8Unorm;
|
||||
|
||||
surface->view.wantsLayer = YES;
|
||||
surface->view.layer = surface->metalLayer;
|
||||
|
||||
//NOTE(martin): handling resizing
|
||||
surface->view.layerContentsRedrawPolicy = NSViewLayerContentsRedrawDuringViewResize;
|
||||
surface->metalLayer.autoresizingMask = kCALayerHeightSizable | kCALayerWidthSizable;
|
||||
surface->metalLayer.needsDisplayOnBoundsChange = YES;
|
||||
|
||||
//-----------------------------------------------------------
|
||||
//NOTE(martin): create a command queue
|
||||
//-----------------------------------------------------------
|
||||
surface->commandQueue = [surface->device newCommandQueue];
|
||||
[surface->commandQueue retain];
|
||||
|
||||
//NOTE(martin): command buffer and drawable are set on demand and at the end of each present() call
|
||||
surface->drawable = nil;
|
||||
surface->commandBuffer = nil;
|
||||
}
|
||||
|
||||
mg_surface handle = mg_surface_alloc_handle((mg_surface_data*)surface);
|
||||
return(handle);
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
mg_metal_surface* metalSurface = (mg_metal_surface*)surfaceData;
|
||||
return(metalSurface->metalLayer);
|
||||
}
|
||||
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)
|
||||
{
|
||||
mg_metal_surface* metalSurface = (mg_metal_surface*)surfaceData;
|
||||
return(metalSurface->drawable);
|
||||
}
|
||||
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)
|
||||
{
|
||||
mg_metal_surface* metalSurface = (mg_metal_surface*)surfaceData;
|
||||
return(metalSurface->commandBuffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
return(nil);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#undef LOG_SUBSYSTEM
|
||||
|
|
|
@ -516,6 +516,16 @@ mp_window mp_window_create(mp_rect rect, const char* title, mp_window_style styl
|
|||
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",
|
||||
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
|
||||
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 ///////////////////////////////////////////////
|
||||
//TODO: this is thrown here for a quick test. We should:
|
||||
// - check for errors
|
||||
|
|
|
@ -39,10 +39,15 @@ void mg_gl_surface_prepare(mg_surface_data* interface)
|
|||
void mg_gl_surface_present(mg_surface_data* interface)
|
||||
{
|
||||
mg_gl_surface* surface = (mg_gl_surface*)interface;
|
||||
|
||||
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)
|
||||
{
|
||||
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
|
||||
wglMakeCurrent(hDC, glContext);
|
||||
|
||||
//TODO: set this back to 1 after testing
|
||||
wglSwapIntervalEXT(0);
|
||||
wglSwapIntervalEXT(1);
|
||||
|
||||
//TODO save important info in surface_data and return a handle
|
||||
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.prepare = mg_gl_surface_prepare;
|
||||
surface->interface.present = mg_gl_surface_present;
|
||||
surface->interface.swapInterval = mg_gl_surface_swap_interval;
|
||||
surface->interface.getFrame = mg_gl_surface_get_frame;
|
||||
|
||||
//TODO: get/set frame/hidden
|
||||
|
|
19
todo.txt
19
todo.txt
|
@ -3,17 +3,22 @@ Canvas renderer perf
|
|||
--------------------
|
||||
[.] Perf
|
||||
[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
|
||||
[?] 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, ...)
|
||||
[ ] 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)
|
||||
|
||||
|
|
Loading…
Reference in New Issue