- Conflating mp_views and surfaces
- Implicit canvas context in canvas API - GLES window surface on mac
This commit is contained in:
parent
9bf62d2218
commit
bd7e1a15f1
|
@ -0,0 +1,11 @@
|
|||
#!/bin/bash
|
||||
|
||||
BINDIR=../../bin
|
||||
RESDIR=../../resources
|
||||
SRCDIR=../../src
|
||||
|
||||
INCLUDES="-I$SRCDIR -I$SRCDIR/util -I$SRCDIR/platform -I$SRCDIR/app"
|
||||
LIBS="-L$BINDIR -lmilepost -framework Carbon -framework Cocoa -framework Metal -framework QuartzCore"
|
||||
FLAGS="-mmacos-version-min=10.15.4 -DDEBUG -DLOG_COMPILE_DEBUG -Wl,-dead_strip"
|
||||
|
||||
clang -g $FLAGS $LIBS $INCLUDES -o $BINDIR/example_canvas main.c
|
|
@ -0,0 +1,144 @@
|
|||
/************************************************************//**
|
||||
*
|
||||
* @file: main.cpp
|
||||
* @author: Martin Fouilleul
|
||||
* @date: 30/07/2022
|
||||
* @revision:
|
||||
*
|
||||
*****************************************************************/
|
||||
#include<stdlib.h>
|
||||
#include<string.h>
|
||||
|
||||
#define _USE_MATH_DEFINES //NOTE: necessary for MSVC
|
||||
#include<math.h>
|
||||
|
||||
#include"milepost.h"
|
||||
#include"metal_surface.h"
|
||||
|
||||
#define LOG_SUBSYSTEM "Main"
|
||||
|
||||
int main()
|
||||
{
|
||||
LogLevel(LOG_LEVEL_DEBUG);
|
||||
|
||||
mp_init();
|
||||
|
||||
mp_rect rect = {.x = 100, .y = 100, .w = 800, .h = 600};
|
||||
mp_window window = mp_window_create(rect, "test", 0);
|
||||
|
||||
//NOTE: create surface
|
||||
mg_surface surface = mg_metal_surface_create_for_window(window);
|
||||
|
||||
//TODO: create canvas
|
||||
mg_canvas canvas = mg_canvas_create(surface);
|
||||
|
||||
// start app
|
||||
mp_window_bring_to_front(window);
|
||||
mp_window_focus(window);
|
||||
|
||||
while(!mp_should_quit())
|
||||
{
|
||||
mp_pump_events(0);
|
||||
mp_event event = {0};
|
||||
while(mp_next_event(&event))
|
||||
{
|
||||
switch(event.type)
|
||||
{
|
||||
case MP_EVENT_WINDOW_CLOSE:
|
||||
{
|
||||
mp_request_quit();
|
||||
} break;
|
||||
|
||||
case MP_EVENT_WINDOW_RESIZE:
|
||||
{
|
||||
printf("resized, rect = {%f, %f, %f, %f}\n",
|
||||
event.frame.rect.x,
|
||||
event.frame.rect.y,
|
||||
event.frame.rect.w,
|
||||
event.frame.rect.h);
|
||||
} break;
|
||||
|
||||
case MP_EVENT_WINDOW_MOVE:
|
||||
{
|
||||
printf("moved, rect = {%f, %f, %f, %f}\n",
|
||||
event.frame.rect.x,
|
||||
event.frame.rect.y,
|
||||
event.frame.rect.w,
|
||||
event.frame.rect.h);
|
||||
} break;
|
||||
|
||||
case MP_EVENT_MOUSE_MOVE:
|
||||
{
|
||||
printf("mouse moved, pos = {%f, %f}, delta = {%f, %f}\n",
|
||||
event.move.x,
|
||||
event.move.y,
|
||||
event.move.deltaX,
|
||||
event.move.deltaY);
|
||||
} break;
|
||||
|
||||
case MP_EVENT_MOUSE_WHEEL:
|
||||
{
|
||||
printf("mouse wheel, delta = {%f, %f}\n",
|
||||
event.move.deltaX,
|
||||
event.move.deltaY);
|
||||
} break;
|
||||
|
||||
case MP_EVENT_MOUSE_ENTER:
|
||||
{
|
||||
printf("mouse enter\n");
|
||||
} break;
|
||||
|
||||
case MP_EVENT_MOUSE_LEAVE:
|
||||
{
|
||||
printf("mouse leave\n");
|
||||
} break;
|
||||
|
||||
case MP_EVENT_MOUSE_BUTTON:
|
||||
{
|
||||
printf("mouse button %i: %i\n",
|
||||
event.key.code,
|
||||
event.key.action == MP_KEY_PRESS ? 1 : 0);
|
||||
} break;
|
||||
|
||||
case MP_EVENT_KEYBOARD_KEY:
|
||||
{
|
||||
printf("key %i: %s\n",
|
||||
event.key.code,
|
||||
event.key.action == MP_KEY_PRESS ? "press" : (event.key.action == MP_KEY_RELEASE ? "release" : "repeat"));
|
||||
} break;
|
||||
|
||||
case MP_EVENT_KEYBOARD_CHAR:
|
||||
{
|
||||
printf("entered char %s\n", event.character.sequence);
|
||||
} break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mg_surface_prepare(surface);
|
||||
mg_set_color_rgba(1, 0, 1, 1);
|
||||
mg_clear();
|
||||
|
||||
mg_set_color_rgba(1, 1, 0, 1);
|
||||
mg_circle_fill(400, 300, 200);
|
||||
|
||||
mg_set_color_rgba(0, 0, 0, 1);
|
||||
mg_set_width(20);
|
||||
|
||||
mg_move_to(300, 200);
|
||||
mg_cubic_to(350, 150, 450, 150, 500, 200);
|
||||
mg_stroke();
|
||||
|
||||
mg_ellipse_fill(330, 350, 30, 50);
|
||||
mg_ellipse_fill(470, 350, 30, 50);
|
||||
|
||||
mg_flush();
|
||||
mg_surface_present(surface);
|
||||
}
|
||||
|
||||
mp_terminate();
|
||||
|
||||
return(0);
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
#!/bin/bash
|
||||
|
||||
BINDIR=../../bin
|
||||
RESDIR=../../resources
|
||||
SRCDIR=../../src
|
||||
|
||||
INCLUDES="-I$SRCDIR -I$SRCDIR/util -I$SRCDIR/platform -I$SRCDIR/app"
|
||||
LIBS="-L$BINDIR -lmilepost -framework Carbon -framework Cocoa -framework Metal -framework QuartzCore"
|
||||
FLAGS="-mmacos-version-min=10.15.4 -DDEBUG -DLOG_COMPILE_DEBUG"
|
||||
|
||||
clang -g $FLAGS $LIBS $INCLUDES -o test main.c
|
|
@ -0,0 +1,2 @@
|
|||
set INCLUDES=/I ..\..\src /I ..\..\src\util /I ..\..\src\platform /I ../../ext
|
||||
cl /we4013 /Zi /Zc:preprocessor /std:c11 %INCLUDES% main.c /link /LIBPATH:../../bin milepost.lib user32.lib opengl32.lib gdi32.lib /out:test.exe
|
|
@ -0,0 +1,15 @@
|
|||
#!/bin/bash
|
||||
|
||||
BINDIR=../../bin
|
||||
RESDIR=../../resources
|
||||
SRCDIR=../../src
|
||||
|
||||
INCLUDES="-I$SRCDIR -I$SRCDIR/util -I$SRCDIR/platform -I$SRCDIR/app -I$SRCDIR/../ext/angle_headers"
|
||||
LIBS="-L$BINDIR -lmilepost -framework Carbon -framework Cocoa -framework Metal -framework QuartzCore -L$RESDIR -lGLESv2 -lEGL"
|
||||
FLAGS="-mmacos-version-min=10.15.4 -DDEBUG -DLOG_COMPILE_DEBUG"
|
||||
|
||||
clang -g $FLAGS $LIBS $INCLUDES -o $BINDIR/example_gles_triangle main.c
|
||||
|
||||
# change dynamic libraries install name
|
||||
install_name_tool -change "./libEGL.dylib" "@loader_path/../resources/libEGL.dylib" $BINDIR/example_gles_triangle
|
||||
install_name_tool -change "./libGLESv2.dylib" "@loader_path/../resources/libGLESv2.dylib" $BINDIR/example_gles_triangle
|
|
@ -0,0 +1,232 @@
|
|||
/************************************************************//**
|
||||
*
|
||||
* @file: main.cpp
|
||||
* @author: Martin Fouilleul
|
||||
* @date: 30/07/2022
|
||||
* @revision:
|
||||
*
|
||||
*****************************************************************/
|
||||
#include<stdlib.h>
|
||||
#include<string.h>
|
||||
|
||||
#define _USE_MATH_DEFINES //NOTE: necessary for MSVC
|
||||
#include<math.h>
|
||||
|
||||
#include<GLES3/gl32.h>
|
||||
|
||||
#include"milepost.h"
|
||||
|
||||
#define LOG_SUBSYSTEM "Main"
|
||||
|
||||
|
||||
mg_surface mg_gles_surface_create_for_window(mp_window window);
|
||||
|
||||
unsigned int program;
|
||||
|
||||
const char* vshaderSource =
|
||||
//"#version 320 es\n"
|
||||
"attribute vec4 vPosition;\n"
|
||||
"uniform mat4 transform;\n"
|
||||
"void main()\n"
|
||||
"{\n"
|
||||
" gl_Position = transform*vPosition;\n"
|
||||
"}\n";
|
||||
|
||||
const char* fshaderSource =
|
||||
//"#version 320 es\n"
|
||||
"precision mediump float;\n"
|
||||
"void main()\n"
|
||||
"{\n"
|
||||
" gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n"
|
||||
"}\n";
|
||||
|
||||
void compile_shader(GLuint shader, const char* source)
|
||||
{
|
||||
glShaderSource(shader, 1, &source, 0);
|
||||
glCompileShader(shader);
|
||||
|
||||
int err = glGetError();
|
||||
if(err)
|
||||
{
|
||||
printf("gl error: %i\n", err);
|
||||
}
|
||||
|
||||
int status = 0;
|
||||
glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
|
||||
if(!status)
|
||||
{
|
||||
char buffer[256];
|
||||
int size = 0;
|
||||
glGetShaderInfoLog(shader, 256, &size, buffer);
|
||||
printf("shader error: %.*s\n", size, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
LogLevel(LOG_LEVEL_DEBUG);
|
||||
|
||||
mp_init();
|
||||
|
||||
mp_rect rect = {.x = 100, .y = 100, .w = 800, .h = 600};
|
||||
mp_window window = mp_window_create(rect, "test", 0);
|
||||
|
||||
//NOTE: create surface
|
||||
mg_surface surface = mg_gles_surface_create_for_window(window);
|
||||
|
||||
//NOTE: init shader and gl state
|
||||
GLuint vao;
|
||||
glGenVertexArrays(1, &vao);
|
||||
glBindVertexArray(vao);
|
||||
|
||||
GLuint vertexBuffer;
|
||||
glGenBuffers(1, &vertexBuffer);
|
||||
|
||||
GLfloat vertices[] = {
|
||||
-0.866/2, -0.5/2, 0, 0.866/2, -0.5/2, 0, 0, 0.5, 0};
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
|
||||
|
||||
|
||||
unsigned int vshader = glCreateShader(GL_VERTEX_SHADER);
|
||||
unsigned int fshader = glCreateShader(GL_FRAGMENT_SHADER);
|
||||
program = glCreateProgram();
|
||||
|
||||
compile_shader(vshader, vshaderSource);
|
||||
compile_shader(fshader, fshaderSource);
|
||||
|
||||
glAttachShader(program, vshader);
|
||||
glAttachShader(program, fshader);
|
||||
glLinkProgram(program);
|
||||
|
||||
int status = 0;
|
||||
glGetProgramiv(program, GL_LINK_STATUS, &status);
|
||||
if(!status)
|
||||
{
|
||||
char buffer[256];
|
||||
int size = 0;
|
||||
glGetProgramInfoLog(program, 256, &size, buffer);
|
||||
printf("link error: %.*s\n", size, buffer);
|
||||
}
|
||||
|
||||
glUseProgram(program);
|
||||
|
||||
mp_window_bring_to_front(window);
|
||||
// mp_window_focus(window);
|
||||
|
||||
while(!mp_should_quit())
|
||||
{
|
||||
mp_pump_events(0);
|
||||
mp_event event = {0};
|
||||
while(mp_next_event(&event))
|
||||
{
|
||||
switch(event.type)
|
||||
{
|
||||
case MP_EVENT_WINDOW_CLOSE:
|
||||
{
|
||||
mp_request_quit();
|
||||
} break;
|
||||
|
||||
case MP_EVENT_WINDOW_RESIZE:
|
||||
{
|
||||
printf("resized, rect = {%f, %f, %f, %f}\n",
|
||||
event.frame.rect.x,
|
||||
event.frame.rect.y,
|
||||
event.frame.rect.w,
|
||||
event.frame.rect.h);
|
||||
} break;
|
||||
|
||||
case MP_EVENT_WINDOW_MOVE:
|
||||
{
|
||||
printf("moved, rect = {%f, %f, %f, %f}\n",
|
||||
event.frame.rect.x,
|
||||
event.frame.rect.y,
|
||||
event.frame.rect.w,
|
||||
event.frame.rect.h);
|
||||
} break;
|
||||
|
||||
case MP_EVENT_MOUSE_MOVE:
|
||||
{
|
||||
printf("mouse moved, pos = {%f, %f}, delta = {%f, %f}\n",
|
||||
event.move.x,
|
||||
event.move.y,
|
||||
event.move.deltaX,
|
||||
event.move.deltaY);
|
||||
} break;
|
||||
|
||||
case MP_EVENT_MOUSE_WHEEL:
|
||||
{
|
||||
printf("mouse wheel, delta = {%f, %f}\n",
|
||||
event.move.deltaX,
|
||||
event.move.deltaY);
|
||||
} break;
|
||||
|
||||
case MP_EVENT_MOUSE_ENTER:
|
||||
{
|
||||
printf("mouse enter\n");
|
||||
} break;
|
||||
|
||||
case MP_EVENT_MOUSE_LEAVE:
|
||||
{
|
||||
printf("mouse leave\n");
|
||||
} break;
|
||||
|
||||
case MP_EVENT_MOUSE_BUTTON:
|
||||
{
|
||||
printf("mouse button %i: %i\n",
|
||||
event.key.code,
|
||||
event.key.action == MP_KEY_PRESS ? 1 : 0);
|
||||
} break;
|
||||
|
||||
case MP_EVENT_KEYBOARD_KEY:
|
||||
{
|
||||
printf("key %i: %s\n",
|
||||
event.key.code,
|
||||
event.key.action == MP_KEY_PRESS ? "press" : (event.key.action == MP_KEY_RELEASE ? "release" : "repeat"));
|
||||
} break;
|
||||
|
||||
case MP_EVENT_KEYBOARD_CHAR:
|
||||
{
|
||||
printf("entered char %s\n", event.character.sequence);
|
||||
} break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mg_surface_prepare(surface);
|
||||
|
||||
glClearColor(0.3, 0.3, 1, 1);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
static float alpha = 0;
|
||||
//f32 aspect = frameSize.x/frameSize.y;
|
||||
f32 aspect = 800/(f32)600;
|
||||
|
||||
glViewport(0, 0, 800, 600);
|
||||
|
||||
GLfloat matrix[] = {cosf(alpha)/aspect, sinf(alpha), 0, 0,
|
||||
-sinf(alpha)/aspect, cosf(alpha), 0, 0,
|
||||
0, 0, 1, 0,
|
||||
0, 0, 0, 1};
|
||||
|
||||
alpha += 2*M_PI/120;
|
||||
|
||||
glUniformMatrix4fv(0, 1, false, matrix);
|
||||
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
|
||||
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
|
||||
glEnableVertexAttribArray(0);
|
||||
|
||||
glDrawArrays(GL_TRIANGLES, 0, 3);
|
||||
|
||||
mg_surface_present(surface);
|
||||
}
|
||||
|
||||
mp_terminate();
|
||||
|
||||
return(0);
|
||||
}
|
|
@ -36,8 +36,7 @@ int main()
|
|||
mp_window window = mp_window_create(rect, "test", 0);
|
||||
|
||||
//NOTE: create surface
|
||||
mg_init();
|
||||
mg_surface surface = mg_surface_create_for_window(window, MG_BACKEND_METAL);
|
||||
mg_surface surface = mg_metal_surface_create_for_window(window);
|
||||
|
||||
//NOTE(martin): load the library
|
||||
id<MTLDevice> device = MTLCreateSystemDefaultDevice();
|
||||
|
|
3046
src/graphics.c
3046
src/graphics.c
File diff suppressed because it is too large
Load Diff
225
src/graphics.h
225
src/graphics.h
|
@ -2,72 +2,33 @@
|
|||
*
|
||||
* @file: graphics.h
|
||||
* @author: Martin Fouilleul
|
||||
* @date: 01/08/2022
|
||||
* @date: 23/01/2023
|
||||
* @revision:
|
||||
*
|
||||
*****************************************************************/
|
||||
#ifndef __GRAPHICS_H_
|
||||
#define __GRAPHICS_H_
|
||||
|
||||
#include"mp_app.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------------------
|
||||
//NOTE(martin): graphics surface
|
||||
//------------------------------------------------------------------------------------------
|
||||
|
||||
typedef struct mg_surface { u64 h; } mg_surface;
|
||||
|
||||
typedef enum { MG_BACKEND_DUMMY,
|
||||
MG_BACKEND_METAL,
|
||||
MG_BACKEND_GL,
|
||||
MG_BACKEND_GLES,
|
||||
//...
|
||||
} mg_backend_id;
|
||||
|
||||
void mg_init();
|
||||
|
||||
mg_surface mg_surface_nil();
|
||||
bool mg_surface_is_nil(mg_surface surface);
|
||||
mg_surface mg_surface_create_for_window(mp_window window, mg_backend_id backend);
|
||||
mg_surface mg_surface_create_offscreen(mg_backend_id backend, u32 width, u32 height);
|
||||
|
||||
void mg_surface_destroy(mg_surface surface);
|
||||
void* mg_surface_get_os_resource(mg_surface surface);
|
||||
|
||||
void mg_surface_prepare(mg_surface surface);
|
||||
void mg_surface_present(mg_surface surface);
|
||||
|
||||
void mg_surface_resize(mg_surface surface, int width, int height);
|
||||
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);
|
||||
|
||||
vec2 mg_surface_size(mg_surface surface);
|
||||
mp_rect mg_surface_frame(mg_surface surface);
|
||||
|
||||
//------------------------------------------------------------------------------------------
|
||||
//NOTE(martin): graphics surface sharing
|
||||
//NOTE(martin): graphics canvas structs
|
||||
//------------------------------------------------------------------------------------------
|
||||
typedef void* mg_surface_server_id;
|
||||
|
||||
typedef struct mg_surface_server { u64 h; } mg_surface_server;
|
||||
typedef struct mg_surface_client { u64 h; } mg_surface_client;
|
||||
|
||||
mg_surface_server mg_surface_server_create(mg_surface surface);
|
||||
mg_surface_server mg_surface_server_create_native(void* p);
|
||||
void mg_surface_server_destroy(mg_surface_server server);
|
||||
mg_surface_server_id mg_surface_server_get_id(mg_surface_server server);
|
||||
|
||||
mg_surface_client mg_surface_client_create(mg_surface_server_id id);
|
||||
void mg_surface_client_destroy(mg_surface_client client);
|
||||
|
||||
//------------------------------------------------------------------------------------------
|
||||
//NOTE(martin): canvas drawing 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;
|
||||
|
@ -77,10 +38,6 @@ typedef struct mg_mat2x3
|
|||
f32 m[6];
|
||||
} mg_mat2x3;
|
||||
|
||||
typedef enum { MG_COORDS_2D_DISPLAY_CENTER,
|
||||
MG_COORDS_2D_DISPLAY_TOP_LEFT,
|
||||
MG_COORDS_2D_DISPLAY_BOTTOM_LEFT } mg_coordinate_system;
|
||||
|
||||
typedef struct mg_color
|
||||
{
|
||||
union
|
||||
|
@ -126,19 +83,86 @@ typedef struct mg_text_extents
|
|||
} mg_text_extents;
|
||||
|
||||
//------------------------------------------------------------------------------------------
|
||||
//NOTE(martin): canvas lifetime and command streams
|
||||
//NOTE(martin): graphics canvas
|
||||
//------------------------------------------------------------------------------------------
|
||||
mg_canvas mg_canvas_create(mg_surface surface, mp_rect viewPort);
|
||||
void mg_canvas_destroy(mg_canvas context);
|
||||
void mg_canvas_viewport(mg_canvas, mp_rect viewPort);
|
||||
void mg_canvas_flush(mg_canvas context);
|
||||
|
||||
mg_stream mg_stream_create(mg_canvas context);
|
||||
mg_stream mg_stream_swap(mg_canvas context, mg_stream stream);
|
||||
void mg_stream_append(mg_canvas context, mg_stream stream);
|
||||
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): fonts management
|
||||
//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);
|
||||
|
@ -165,78 +189,6 @@ int mg_font_get_glyph_extents(mg_font font, str32 glyphIndices, mg_text_extents*
|
|||
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): matrix settings
|
||||
//------------------------------------------------------------------------------------------
|
||||
void mg_matrix_push(mg_canvas context, mg_mat2x3 matrix);
|
||||
void mg_matrix_pop(mg_canvas context);
|
||||
|
||||
//------------------------------------------------------------------------------------------
|
||||
//NOTE(martin): clipping
|
||||
//------------------------------------------------------------------------------------------
|
||||
void mg_clip_push(mg_canvas context, f32 x, f32 y, f32 w, f32 h);
|
||||
void mg_clip_pop(mg_canvas context);
|
||||
|
||||
//------------------------------------------------------------------------------------------
|
||||
//NOTE(martin): graphics attributes setting/getting
|
||||
//------------------------------------------------------------------------------------------
|
||||
void mg_set_clear_color(mg_canvas context, mg_color color);
|
||||
void mg_set_clear_color_rgba(mg_canvas context, f32 r, f32 g, f32 b, f32 a);
|
||||
void mg_set_color(mg_canvas context, mg_color color);
|
||||
void mg_set_color_rgba(mg_canvas context, f32 r, f32 g, f32 b, f32 a);
|
||||
void mg_set_width(mg_canvas context, f32 width);
|
||||
void mg_set_tolerance(mg_canvas context, f32 tolerance);
|
||||
void mg_set_joint(mg_canvas context, mg_joint_type joint);
|
||||
void mg_set_max_joint_excursion(mg_canvas context, f32 maxJointExcursion);
|
||||
void mg_set_cap(mg_canvas context, mg_cap_type cap);
|
||||
void mg_set_font(mg_canvas context, mg_font font);
|
||||
void mg_set_font_size(mg_canvas context, f32 size);
|
||||
void mg_set_text_flip(mg_canvas context, bool flip);
|
||||
|
||||
mg_color mg_get_clear_color(mg_canvas context);
|
||||
mg_color mg_get_color(mg_canvas context);
|
||||
f32 mg_get_width(mg_canvas context);
|
||||
f32 mg_get_tolerance(mg_canvas context);
|
||||
mg_joint_type mg_get_joint(mg_canvas context);
|
||||
f32 mg_get_max_joint_excursion(mg_canvas context);
|
||||
mg_cap_type mg_get_cap(mg_canvas context);
|
||||
mg_font mg_get_font(mg_canvas context);
|
||||
f32 mg_get_font_size(mg_canvas context);
|
||||
bool mg_get_text_flip(mg_canvas context);
|
||||
//------------------------------------------------------------------------------------------
|
||||
//NOTE(martin): path construction
|
||||
//------------------------------------------------------------------------------------------
|
||||
vec2 mg_get_position(mg_canvas context);
|
||||
void mg_move_to(mg_canvas context, f32 x, f32 y);
|
||||
void mg_line_to(mg_canvas context, f32 x, f32 y);
|
||||
void mg_quadratic_to(mg_canvas context, f32 x1, f32 y1, f32 x2, f32 y2);
|
||||
void mg_cubic_to(mg_canvas context, f32 x1, f32 y1, f32 x2, f32 y2, f32 x3, f32 y3);
|
||||
void mg_close_path(mg_canvas context);
|
||||
|
||||
mp_rect mg_glyph_outlines(mg_canvas context, str32 glyphIndices);
|
||||
void mg_codepoints_outlines(mg_canvas context, str32 string);
|
||||
void mg_text_outlines(mg_canvas context, str8 string);
|
||||
|
||||
//------------------------------------------------------------------------------------------
|
||||
//NOTE(martin): clear/fill/stroke
|
||||
//------------------------------------------------------------------------------------------
|
||||
void mg_clear(mg_canvas context);
|
||||
void mg_fill(mg_canvas context);
|
||||
void mg_stroke(mg_canvas context);
|
||||
|
||||
//------------------------------------------------------------------------------------------
|
||||
//NOTE(martin): 'fast' shapes primitives
|
||||
//------------------------------------------------------------------------------------------
|
||||
void mg_rectangle_fill(mg_canvas context, f32 x, f32 y, f32 w, f32 h);
|
||||
void mg_rectangle_stroke(mg_canvas context, f32 x, f32 y, f32 w, f32 h);
|
||||
void mg_rounded_rectangle_fill(mg_canvas context, f32 x, f32 y, f32 w, f32 h, f32 r);
|
||||
void mg_rounded_rectangle_stroke(mg_canvas context, f32 x, f32 y, f32 w, f32 h, f32 r);
|
||||
void mg_ellipse_fill(mg_canvas context, f32 x, f32 y, f32 rx, f32 ry);
|
||||
void mg_ellipse_stroke(mg_canvas context, f32 x, f32 y, f32 rx, f32 ry);
|
||||
void mg_circle_fill(mg_canvas context, f32 x, f32 y, f32 r);
|
||||
void mg_circle_stroke(mg_canvas context, f32 x, f32 y, f32 r);
|
||||
void mg_arc(mg_canvas handle, f32 x, f32 y, f32 r, f32 arcAngle, f32 startAngle);
|
||||
|
||||
//------------------------------------------------------------------------------------------
|
||||
//NOTE(martin): images
|
||||
//------------------------------------------------------------------------------------------
|
||||
|
@ -245,17 +197,14 @@ 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(mg_canvas canvas, u32 width, u32 height, u8* pixels);
|
||||
mg_image mg_image_create_from_data(mg_canvas canvas, str8 data, bool flip);
|
||||
mg_image mg_image_create_from_file(mg_canvas canvas, str8 path, bool flip);
|
||||
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_canvas canvas, mg_image image);
|
||||
void mg_image_drestroy(mg_image image);
|
||||
|
||||
vec2 mg_image_size(mg_canvas canvas, mg_image image);
|
||||
void mg_image_draw(mg_canvas canvas, mg_image image, mp_rect rect);
|
||||
void mg_rounded_image_draw(mg_canvas handle, mg_image image, mp_rect rect, f32 roundness);
|
||||
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);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
#endif //__GRAPHICS_H_
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
* @file: graphics_internal.h
|
||||
* @author: Martin Fouilleul
|
||||
* @date: 01/08/2022
|
||||
* @date: 23/01/2023
|
||||
* @revision:
|
||||
*
|
||||
*****************************************************************/
|
||||
|
@ -15,73 +15,183 @@
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
// Surfaces
|
||||
//---------------------------------------------------------------------------------------------
|
||||
typedef struct mg_surface_info mg_surface_info;
|
||||
typedef enum { MG_BACKEND_DUMMY,
|
||||
MG_BACKEND_METAL,
|
||||
MG_BACKEND_GL,
|
||||
MG_BACKEND_GLES,
|
||||
//...
|
||||
} mg_backend_id;
|
||||
|
||||
typedef void (*mg_surface_prepare_proc)(mg_surface_info* surface);
|
||||
typedef void (*mg_surface_present_proc)(mg_surface_info* surface);
|
||||
typedef void (*mg_surface_resize_proc)(mg_surface_info* surface, u32 width, u32 height);
|
||||
typedef void (*mg_surface_set_hidden_proc)(mg_surface_info* surface, bool hidden);
|
||||
typedef vec2 (*mg_surface_get_size_proc)(mg_surface_info* surface);
|
||||
typedef void (*mg_surface_destroy_proc)(mg_surface_info* surface);
|
||||
typedef void* (*mg_surface_get_os_resource_proc)(mg_surface_info* surface);
|
||||
typedef struct mg_surface_data mg_surface_data;
|
||||
|
||||
typedef struct mg_surface_info
|
||||
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 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);
|
||||
typedef void (*mg_surface_set_hidden_proc)(mg_surface_data* surface, bool hidden);
|
||||
|
||||
typedef struct mg_surface_data
|
||||
{
|
||||
mg_backend_id backend;
|
||||
|
||||
mg_surface_destroy_proc destroy;
|
||||
mg_surface_prepare_proc prepare;
|
||||
mg_surface_present_proc present;
|
||||
mg_surface_resize_proc resize;
|
||||
mg_surface_get_frame_proc getFrame;
|
||||
mg_surface_set_frame_proc setFrame;
|
||||
mg_surface_get_hidden_proc getHidden;
|
||||
mg_surface_set_hidden_proc setHidden;
|
||||
mg_surface_get_size_proc getSize;
|
||||
mg_surface_get_os_resource_proc getOSResource;
|
||||
|
||||
} mg_surface_info;
|
||||
} mg_surface_data;
|
||||
|
||||
mg_surface mg_surface_alloc_handle(mg_surface_info* surface);
|
||||
mg_surface_info* mg_surface_ptr_from_handle(mg_surface handle);
|
||||
//---------------------------------------------------------------------------------------------
|
||||
// Surface servers
|
||||
//---------------------------------------------------------------------------------------------
|
||||
typedef struct mg_surface_server_info mg_surface_server_info;
|
||||
mg_surface mg_surface_alloc_handle(mg_surface_data* surface);
|
||||
mg_surface_data* mg_surface_data_from_handle(mg_surface handle);
|
||||
|
||||
typedef void (*mg_surface_server_destroy_proc)(mg_surface_server_info* server);
|
||||
typedef mg_surface_server_id (*mg_surface_server_get_id_proc)(mg_surface_server_info* server);
|
||||
//---------------------------------------------------------------
|
||||
// graphics structs
|
||||
//---------------------------------------------------------------
|
||||
typedef enum { MG_PATH_MOVE,
|
||||
MG_PATH_LINE,
|
||||
MG_PATH_QUADRATIC,
|
||||
MG_PATH_CUBIC } mg_path_elt_type;
|
||||
|
||||
typedef struct mg_surface_server_info
|
||||
typedef struct mg_path_elt
|
||||
{
|
||||
mg_surface_server_destroy_proc destroy;
|
||||
mg_surface_server_get_id_proc getID;
|
||||
mg_path_elt_type type;
|
||||
vec2 p[3];
|
||||
|
||||
} mg_surface_server_info;
|
||||
} mg_path_elt;
|
||||
|
||||
mg_surface_server mg_surface_server_alloc_handle(mg_surface_server_info* server);
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
// Surface clients
|
||||
//---------------------------------------------------------------------------------------------
|
||||
typedef struct mg_surface_client_info mg_surface_client_info;
|
||||
|
||||
typedef void (*mg_surface_client_destroy_proc)(mg_surface_client_info* client);
|
||||
typedef void (*mg_surface_client_attach_proc)(mg_surface_client_info* client);
|
||||
typedef void (*mg_surface_client_detach_proc)(mg_surface_client_info* client);
|
||||
|
||||
typedef struct mg_surface_client_info
|
||||
typedef struct mg_path_descriptor
|
||||
{
|
||||
mg_surface_client_destroy_proc destroy;
|
||||
mg_surface_client_attach_proc attach;
|
||||
mg_surface_client_detach_proc detach;
|
||||
u32 startIndex;
|
||||
u32 count;
|
||||
vec2 startPoint;
|
||||
|
||||
} mg_surface_client_info;
|
||||
} mg_path_descriptor;
|
||||
|
||||
mg_surface_client mg_surface_client_alloc_handle(mg_surface_client_info* client);
|
||||
//---------------------------------------------------------------------------------------------
|
||||
// vertex layout
|
||||
//---------------------------------------------------------------------------------------------
|
||||
typedef struct mgc_vertex_layout
|
||||
typedef struct mg_attributes
|
||||
{
|
||||
f32 width;
|
||||
f32 tolerance;
|
||||
mg_color color;
|
||||
mg_joint_type joint;
|
||||
f32 maxJointExcursion;
|
||||
mg_cap_type cap;
|
||||
|
||||
mg_font font;
|
||||
f32 fontSize;
|
||||
|
||||
mg_image image;
|
||||
|
||||
mp_rect clip;
|
||||
|
||||
} mg_attributes;
|
||||
|
||||
typedef struct mg_rounded_rect
|
||||
{
|
||||
f32 x;
|
||||
f32 y;
|
||||
f32 w;
|
||||
f32 h;
|
||||
f32 r;
|
||||
} mg_rounded_rect;
|
||||
|
||||
typedef enum { MG_CMD_CLEAR = 0,
|
||||
MG_CMD_FILL,
|
||||
MG_CMD_STROKE,
|
||||
MG_CMD_RECT_FILL,
|
||||
MG_CMD_RECT_STROKE,
|
||||
MG_CMD_ROUND_RECT_FILL,
|
||||
MG_CMD_ROUND_RECT_STROKE,
|
||||
MG_CMD_ELLIPSE_FILL,
|
||||
MG_CMD_ELLIPSE_STROKE,
|
||||
MG_CMD_JUMP,
|
||||
MG_CMD_MATRIX_PUSH,
|
||||
MG_CMD_MATRIX_POP,
|
||||
MG_CMD_CLIP_PUSH,
|
||||
MG_CMD_CLIP_POP,
|
||||
MG_CMD_IMAGE_DRAW,
|
||||
MG_CMD_ROUNDED_IMAGE_DRAW,
|
||||
} mg_primitive_cmd;
|
||||
|
||||
typedef struct mg_primitive
|
||||
{
|
||||
mg_primitive_cmd cmd;
|
||||
mg_attributes attributes;
|
||||
|
||||
union
|
||||
{
|
||||
mg_path_descriptor path;
|
||||
mp_rect rect;
|
||||
mg_rounded_rect roundedRect;
|
||||
utf32 codePoint;
|
||||
u32 jump;
|
||||
mg_mat2x3 matrix;
|
||||
};
|
||||
|
||||
} mg_primitive;
|
||||
|
||||
typedef struct mg_glyph_map_entry
|
||||
{
|
||||
unicode_range range;
|
||||
u32 firstGlyphIndex;
|
||||
|
||||
} mg_glyph_map_entry;
|
||||
|
||||
typedef struct mg_glyph_data
|
||||
{
|
||||
bool exists;
|
||||
utf32 codePoint;
|
||||
mg_path_descriptor pathDescriptor;
|
||||
mg_text_extents extents;
|
||||
//...
|
||||
|
||||
} mg_glyph_data;
|
||||
|
||||
enum
|
||||
{
|
||||
MG_STREAM_MAX_COUNT = 128,
|
||||
MG_IMAGE_MAX_COUNT = 128
|
||||
};
|
||||
|
||||
typedef struct mg_image_data
|
||||
{
|
||||
list_elt listElt;
|
||||
u32 generation;
|
||||
|
||||
mp_rect rect;
|
||||
|
||||
} mg_image_data;
|
||||
|
||||
enum
|
||||
{
|
||||
MG_MATRIX_STACK_MAX_DEPTH = 64,
|
||||
MG_CLIP_STACK_MAX_DEPTH = 64,
|
||||
MG_MAX_PATH_ELEMENT_COUNT = 2<<20,
|
||||
MG_MAX_PRIMITIVE_COUNT = 8<<10
|
||||
};
|
||||
|
||||
typedef struct mg_font_data
|
||||
{
|
||||
list_elt freeListElt;
|
||||
u32 generation;
|
||||
|
||||
u32 rangeCount;
|
||||
u32 glyphCount;
|
||||
u32 outlineCount;
|
||||
mg_glyph_map_entry* glyphMap;
|
||||
mg_glyph_data* glyphs;
|
||||
mg_path_elt* outlines;
|
||||
|
||||
f32 unitsPerEm;
|
||||
mg_font_extents extents;
|
||||
|
||||
} mg_font_data;
|
||||
|
||||
typedef struct mg_vertex_layout
|
||||
{
|
||||
u32 maxVertexCount;
|
||||
u32 maxIndexCount;
|
||||
|
@ -107,26 +217,71 @@ typedef struct mgc_vertex_layout
|
|||
void* indexBuffer;
|
||||
u32 indexStride;
|
||||
|
||||
} mgc_vertex_layout;
|
||||
} mg_vertex_layout;
|
||||
|
||||
typedef struct mg_canvas_painter mg_canvas_painter;
|
||||
typedef void (*mgc_painter_destroy_proc)(mg_canvas_painter* painter);
|
||||
typedef void (*mgc_painter_draw_buffers_proc)(mg_canvas_painter* painter, u32 vertexCount, u32 indexCount, mg_color clearColor);
|
||||
typedef void (*mgc_painter_set_viewport_proc)(mg_canvas_painter* painter, mp_rect viewPort);
|
||||
typedef void (*mgc_painter_atlas_upload_proc)(mg_canvas_painter* painter, mp_rect rect, u8* bytes);
|
||||
typedef struct mg_canvas_backend mg_canvas_backend;
|
||||
|
||||
typedef struct mg_canvas_painter
|
||||
typedef void (*mg_canvas_backend_destroy_proc)(mg_canvas_backend* backend);
|
||||
typedef void (*mg_canvas_backend_draw_buffers_proc)(mg_canvas_backend* backend, u32 vertexCount, u32 indexCount, mg_color clearColor);
|
||||
typedef void (*mg_canvas_backend_atlas_upload_proc)(mg_canvas_backend* backend, mp_rect rect, u8* bytes);
|
||||
|
||||
typedef struct mg_canvas_backend
|
||||
{
|
||||
mg_surface_info* surface;
|
||||
mgc_vertex_layout vertexLayout;
|
||||
mgc_painter_destroy_proc destroy;
|
||||
mgc_painter_draw_buffers_proc drawBuffers;
|
||||
mgc_painter_set_viewport_proc setViewPort;
|
||||
mgc_painter_atlas_upload_proc atlasUpload;
|
||||
mg_vertex_layout vertexLayout;
|
||||
|
||||
} mg_canvas_painter;
|
||||
mg_canvas_backend_destroy_proc destroy;
|
||||
mg_canvas_backend_draw_buffers_proc drawBuffers;
|
||||
mg_canvas_backend_atlas_upload_proc atlasUpload;
|
||||
|
||||
} mg_canvas_backend;
|
||||
|
||||
typedef struct mg_canvas_data
|
||||
{
|
||||
list_elt freeListElt;
|
||||
u32 generation;
|
||||
|
||||
u64 frameCounter;
|
||||
|
||||
mg_mat2x3 transform;
|
||||
mp_rect clip;
|
||||
mg_attributes attributes;
|
||||
bool textFlip;
|
||||
|
||||
mg_path_elt pathElements[MG_MAX_PATH_ELEMENT_COUNT];
|
||||
mg_path_descriptor path;
|
||||
vec2 subPathStartPoint;
|
||||
vec2 subPathLastPoint;
|
||||
|
||||
mg_mat2x3 matrixStack[MG_MATRIX_STACK_MAX_DEPTH];
|
||||
u32 matrixStackSize;
|
||||
|
||||
mp_rect clipStack[MG_CLIP_STACK_MAX_DEPTH];
|
||||
u32 clipStackSize;
|
||||
|
||||
u32 nextZIndex;
|
||||
u32 primitiveCount;
|
||||
mg_primitive primitives[MG_MAX_PRIMITIVE_COUNT];
|
||||
|
||||
u32 vertexCount;
|
||||
u32 indexCount;
|
||||
|
||||
mg_image_data images[MG_IMAGE_MAX_COUNT];
|
||||
u32 imageNextIndex;
|
||||
list_info imageFreeList;
|
||||
|
||||
vec2 atlasPos;
|
||||
u32 atlasLineHeight;
|
||||
mg_image blankImage;
|
||||
|
||||
mg_canvas_backend* backend;
|
||||
|
||||
} mg_canvas_data;
|
||||
|
||||
enum
|
||||
{
|
||||
MG_ATLAS_SIZE = 8192,
|
||||
};
|
||||
|
||||
#define MG_ATLAS_SIZE 8192
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
|
|
|
@ -0,0 +1,442 @@
|
|||
/************************************************************//**
|
||||
*
|
||||
* @file: metal_canvas.m
|
||||
* @author: Martin Fouilleul
|
||||
* @date: 12/07/2020
|
||||
* @revision: 24/01/2023
|
||||
*
|
||||
*****************************************************************/
|
||||
#import<Metal/Metal.h>
|
||||
#import<QuartzCore/CAMetalLayer.h>
|
||||
#include<simd/simd.h>
|
||||
|
||||
#include"graphics_internal.h"
|
||||
#include"macro_helpers.h"
|
||||
#include"osx_app.h"
|
||||
|
||||
#include"metal_shader.h"
|
||||
|
||||
#define LOG_SUBSYSTEM "Graphics"
|
||||
|
||||
static const int MG_METAL_CANVAS_DEFAULT_BUFFER_LENGTH = 4<<20;
|
||||
|
||||
typedef struct mg_metal_canvas_backend
|
||||
{
|
||||
mg_canvas_backend interface;
|
||||
mg_surface surface;
|
||||
|
||||
// permanent metal resources
|
||||
id<MTLComputePipelineState> tilingPipeline;
|
||||
id<MTLComputePipelineState> sortingPipeline;
|
||||
id<MTLComputePipelineState> boxingPipeline;
|
||||
id<MTLComputePipelineState> computePipeline;
|
||||
id<MTLRenderPipelineState> renderPipeline;
|
||||
|
||||
mp_rect viewPort;
|
||||
|
||||
// textures and buffers
|
||||
id<MTLTexture> outTexture;
|
||||
id<MTLTexture> atlasTexture;
|
||||
id<MTLBuffer> vertexBuffer;
|
||||
id<MTLBuffer> indexBuffer;
|
||||
id<MTLBuffer> tileCounters;
|
||||
id<MTLBuffer> tilesArray;
|
||||
id<MTLBuffer> triangleArray;
|
||||
id<MTLBuffer> boxArray;
|
||||
|
||||
} mg_metal_canvas_backend;
|
||||
|
||||
mg_metal_surface* mg_metal_canvas_get_surface(mg_metal_canvas_backend* canvas)
|
||||
{
|
||||
mg_metal_surface* res = 0;
|
||||
mg_surface_data* data = mg_surface_data_from_handle(canvas->surface);
|
||||
if(data && data->backend == MG_BACKEND_METAL)
|
||||
{
|
||||
res = (mg_metal_surface*)data;
|
||||
}
|
||||
return(res);
|
||||
}
|
||||
|
||||
void mg_metal_canvas_draw_buffers(mg_canvas_backend* interface, u32 vertexCount, u32 indexCount, mg_color clearColor)
|
||||
{
|
||||
mg_metal_canvas_backend* backend = (mg_metal_canvas_backend*)interface;
|
||||
mg_metal_surface* surface = mg_metal_canvas_get_surface(backend);
|
||||
if(!surface)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@autoreleasepool
|
||||
{
|
||||
if(surface->commandBuffer == nil || surface->commandBuffer == nil)
|
||||
{
|
||||
mg_metal_surface_acquire_drawable_and_command_buffer(surface);
|
||||
}
|
||||
|
||||
ASSERT(indexCount * sizeof(i32) < [backend->indexBuffer length]);
|
||||
|
||||
f32 scale = surface->metalLayer.contentsScale;
|
||||
vector_uint2 viewportSize = {backend->viewPort.w * scale, backend->viewPort.h * scale};
|
||||
|
||||
//-----------------------------------------------------------
|
||||
//NOTE(martin): encode the clear counter
|
||||
//-----------------------------------------------------------
|
||||
id<MTLBlitCommandEncoder> blitEncoder = [surface->commandBuffer blitCommandEncoder];
|
||||
[blitEncoder fillBuffer: backend->tileCounters range: NSMakeRange(0, RENDERER_MAX_TILES*sizeof(uint)) value: 0];
|
||||
[blitEncoder endEncoding];
|
||||
|
||||
//-----------------------------------------------------------
|
||||
//NOTE(martin): encode the boxing pass
|
||||
//-----------------------------------------------------------
|
||||
id<MTLComputeCommandEncoder> boxEncoder = [surface->commandBuffer computeCommandEncoder];
|
||||
[boxEncoder setComputePipelineState: backend->boxingPipeline];
|
||||
[boxEncoder setBuffer: backend->vertexBuffer offset:0 atIndex: 0];
|
||||
[boxEncoder setBuffer: backend->indexBuffer offset:0 atIndex: 1];
|
||||
[boxEncoder setBuffer: backend->triangleArray offset:0 atIndex: 2];
|
||||
[boxEncoder setBuffer: backend->boxArray offset:0 atIndex: 3];
|
||||
[boxEncoder setBytes: &scale length: sizeof(float) atIndex: 4];
|
||||
|
||||
MTLSize boxGroupSize = MTLSizeMake(backend->boxingPipeline.maxTotalThreadsPerThreadgroup, 1, 1);
|
||||
MTLSize boxGridSize = MTLSizeMake(indexCount/3, 1, 1);
|
||||
|
||||
[boxEncoder dispatchThreads: boxGridSize threadsPerThreadgroup: boxGroupSize];
|
||||
[boxEncoder endEncoding];
|
||||
|
||||
//-----------------------------------------------------------
|
||||
//NOTE(martin): encode the tiling pass
|
||||
//-----------------------------------------------------------
|
||||
|
||||
id<MTLComputeCommandEncoder> tileEncoder = [surface->commandBuffer computeCommandEncoder];
|
||||
[tileEncoder setComputePipelineState: backend->tilingPipeline];
|
||||
[tileEncoder setBuffer: backend->boxArray offset:0 atIndex: 0];
|
||||
[tileEncoder setBuffer: backend->tileCounters offset:0 atIndex: 1];
|
||||
[tileEncoder setBuffer: backend->tilesArray offset:0 atIndex: 2];
|
||||
[tileEncoder setBytes: &viewportSize length: sizeof(vector_uint2) atIndex: 3];
|
||||
|
||||
[tileEncoder dispatchThreads: boxGridSize threadsPerThreadgroup: boxGroupSize];
|
||||
[tileEncoder endEncoding];
|
||||
|
||||
//-----------------------------------------------------------
|
||||
//NOTE(martin): encode the sorting pass
|
||||
//-----------------------------------------------------------
|
||||
|
||||
id<MTLComputeCommandEncoder> sortEncoder = [surface->commandBuffer computeCommandEncoder];
|
||||
[sortEncoder setComputePipelineState: backend->sortingPipeline];
|
||||
[sortEncoder setBuffer: backend->tileCounters offset:0 atIndex: 0];
|
||||
[sortEncoder setBuffer: backend->triangleArray offset:0 atIndex: 1];
|
||||
[sortEncoder setBuffer: backend->tilesArray offset:0 atIndex: 2];
|
||||
[sortEncoder setBytes: &viewportSize length: sizeof(vector_uint2) atIndex: 3];
|
||||
|
||||
u32 nTilesX = (viewportSize.x + RENDERER_TILE_SIZE - 1)/RENDERER_TILE_SIZE;
|
||||
u32 nTilesY = (viewportSize.y + RENDERER_TILE_SIZE - 1)/RENDERER_TILE_SIZE;
|
||||
|
||||
MTLSize sortGroupSize = MTLSizeMake(backend->boxingPipeline.maxTotalThreadsPerThreadgroup, 1, 1);
|
||||
MTLSize sortGridSize = MTLSizeMake(nTilesX*nTilesY, 1, 1);
|
||||
|
||||
[sortEncoder dispatchThreads: sortGridSize threadsPerThreadgroup: sortGroupSize];
|
||||
[sortEncoder endEncoding];
|
||||
|
||||
//-----------------------------------------------------------
|
||||
//NOTE(martin): create compute encoder and encode commands
|
||||
//-----------------------------------------------------------
|
||||
vector_float4 clearColorVec4 = {clearColor.r, clearColor.g, clearColor.b, clearColor.a};
|
||||
|
||||
id<MTLComputeCommandEncoder> encoder = [surface->commandBuffer computeCommandEncoder];
|
||||
[encoder setComputePipelineState:backend->computePipeline];
|
||||
[encoder setTexture: backend->outTexture atIndex: 0];
|
||||
[encoder setTexture: backend->atlasTexture atIndex: 1];
|
||||
[encoder setBuffer: backend->vertexBuffer offset:0 atIndex: 0];
|
||||
[encoder setBuffer: backend->tileCounters offset:0 atIndex: 1];
|
||||
[encoder setBuffer: backend->tilesArray offset:0 atIndex: 2];
|
||||
[encoder setBuffer: backend->triangleArray offset:0 atIndex: 3];
|
||||
[encoder setBuffer: backend->boxArray offset:0 atIndex: 4];
|
||||
[encoder setBytes: &clearColorVec4 length: sizeof(vector_float4) atIndex: 5];
|
||||
|
||||
//TODO: check that we don't exceed maxTotalThreadsPerThreadgroup
|
||||
DEBUG_ASSERT(RENDERER_TILE_SIZE*RENDERER_TILE_SIZE <= backend->computePipeline.maxTotalThreadsPerThreadgroup);
|
||||
MTLSize threadGridSize = MTLSizeMake(viewportSize.x, viewportSize.y, 1);
|
||||
MTLSize threadGroupSize = MTLSizeMake(RENDERER_TILE_SIZE, RENDERER_TILE_SIZE, 1);
|
||||
|
||||
[encoder dispatchThreads: threadGridSize threadsPerThreadgroup:threadGroupSize];
|
||||
[encoder endEncoding];
|
||||
|
||||
//-----------------------------------------------------------
|
||||
//NOTE(martin): acquire drawable, create render encoder to blit texture to framebuffer
|
||||
//-----------------------------------------------------------
|
||||
|
||||
MTLViewport viewport = {backend->viewPort.x * scale,
|
||||
backend->viewPort.y * scale,
|
||||
backend->viewPort.w * scale,
|
||||
backend->viewPort.h * scale,
|
||||
0,
|
||||
1};
|
||||
|
||||
MTLRenderPassDescriptor* renderPassDescriptor = [MTLRenderPassDescriptor renderPassDescriptor];
|
||||
renderPassDescriptor.colorAttachments[0].texture = surface->drawable.texture;
|
||||
renderPassDescriptor.colorAttachments[0].storeAction = MTLStoreActionStore;
|
||||
|
||||
id<MTLRenderCommandEncoder> renderEncoder = [surface->commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor];
|
||||
[renderEncoder setViewport: viewport];
|
||||
[renderEncoder setRenderPipelineState: backend->renderPipeline];
|
||||
[renderEncoder setFragmentTexture: backend->outTexture atIndex: 0];
|
||||
[renderEncoder drawPrimitives: MTLPrimitiveTypeTriangle
|
||||
vertexStart: 0
|
||||
vertexCount: 3 ];
|
||||
[renderEncoder endEncoding];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
void mg_metal_canvas_viewport(mg_canvas_backend* interface, mp_rect viewPort)
|
||||
{
|
||||
mg_metal_canvas_backend* backend = (mg_metal_canvas_backend*)interface;
|
||||
mg_metal_surface* surface = mg_metal_canvas_get_surface(backend);
|
||||
if(!surface)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
backend->viewPort = viewPort;
|
||||
|
||||
@autoreleasepool
|
||||
{
|
||||
f32 scale = surface->metalLayer.contentsScale;
|
||||
CGSize drawableSize = (CGSize){.width = viewPort.w * scale, .height = viewPort.h * scale};
|
||||
|
||||
[backend->outTexture release];
|
||||
|
||||
MTLTextureDescriptor* texDesc = [[MTLTextureDescriptor alloc] init];
|
||||
texDesc.textureType = MTLTextureType2D;
|
||||
texDesc.storageMode = MTLStorageModePrivate;
|
||||
texDesc.usage = MTLTextureUsageShaderRead | MTLTextureUsageShaderWrite;
|
||||
texDesc.pixelFormat = MTLPixelFormatBGRA8Unorm;// MTLPixelFormatBGRA8Unorm_sRGB;
|
||||
texDesc.width = drawableSize.width;
|
||||
texDesc.height = drawableSize.height;
|
||||
|
||||
backend->outTexture = [surface->device newTextureWithDescriptor:texDesc];
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
void mg_metal_canvas_update_vertex_layout(mg_metal_canvas_backend* backend)
|
||||
{
|
||||
char* vertexBase = (char*)[backend->vertexBuffer contents];
|
||||
|
||||
backend->interface.vertexLayout = (mg_vertex_layout){
|
||||
.maxVertexCount = MG_METAL_CANVAS_DEFAULT_BUFFER_LENGTH,
|
||||
.maxIndexCount = MG_METAL_CANVAS_DEFAULT_BUFFER_LENGTH,
|
||||
.posBuffer = vertexBase + offsetof(mg_vertex, pos),
|
||||
.posStride = sizeof(mg_vertex),
|
||||
.cubicBuffer = vertexBase + offsetof(mg_vertex, cubic),
|
||||
.cubicStride = sizeof(mg_vertex),
|
||||
.uvBuffer = vertexBase + offsetof(mg_vertex, uv),
|
||||
.uvStride = sizeof(mg_vertex),
|
||||
.colorBuffer = vertexBase + offsetof(mg_vertex, color),
|
||||
.colorStride = sizeof(mg_vertex),
|
||||
.zIndexBuffer = vertexBase + offsetof(mg_vertex, zIndex),
|
||||
.zIndexStride = sizeof(mg_vertex),
|
||||
.clipsBuffer = vertexBase + offsetof(mg_vertex, clip),
|
||||
.clipsStride = sizeof(mg_vertex),
|
||||
.indexBuffer = [backend->indexBuffer contents],
|
||||
.indexStride = sizeof(int)};
|
||||
}
|
||||
|
||||
void mg_metal_canvas_destroy(mg_canvas_backend* interface)
|
||||
{
|
||||
mg_metal_canvas_backend* backend = (mg_metal_canvas_backend*)interface;
|
||||
|
||||
@autoreleasepool
|
||||
{
|
||||
[backend->outTexture release];
|
||||
[backend->atlasTexture release];
|
||||
[backend->vertexBuffer release];
|
||||
[backend->indexBuffer release];
|
||||
[backend->tilesArray release];
|
||||
[backend->triangleArray release];
|
||||
[backend->boxArray release];
|
||||
[backend->computePipeline release];
|
||||
}
|
||||
}
|
||||
|
||||
void mg_metal_canvas_atlas_upload(mg_canvas_backend* interface, mp_rect rect, u8* bytes)
|
||||
{@autoreleasepool{
|
||||
mg_metal_canvas_backend* backend = (mg_metal_canvas_backend*)interface;
|
||||
|
||||
MTLRegion region = MTLRegionMake2D(rect.x, rect.y, rect.w, rect.h);
|
||||
[backend->atlasTexture replaceRegion:region
|
||||
mipmapLevel:0
|
||||
withBytes:(void*)bytes
|
||||
bytesPerRow: 4 * rect.w];
|
||||
}}
|
||||
|
||||
mg_canvas_backend* mg_metal_canvas_create(mg_surface surface)
|
||||
{
|
||||
mg_metal_canvas_backend* backend = 0;
|
||||
|
||||
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;
|
||||
|
||||
backend = malloc_type(mg_metal_canvas_backend);
|
||||
backend->surface = surface;
|
||||
|
||||
//NOTE(martin): setup interface functions
|
||||
backend->interface.destroy = mg_metal_canvas_destroy;
|
||||
backend->interface.drawBuffers = mg_metal_canvas_draw_buffers;
|
||||
backend->interface.atlasUpload = mg_metal_canvas_atlas_upload;
|
||||
|
||||
mp_rect frame = mg_surface_get_frame(surface);
|
||||
backend->viewPort = (mp_rect){0, 0, frame.w, frame.h};
|
||||
|
||||
@autoreleasepool
|
||||
{
|
||||
f32 scale = metalSurface->metalLayer.contentsScale;
|
||||
CGSize drawableSize = (CGSize){.width = backend->viewPort.w * scale, .height = backend->viewPort.h * scale};
|
||||
|
||||
//-----------------------------------------------------------
|
||||
//NOTE(martin): create our output texture
|
||||
//-----------------------------------------------------------
|
||||
MTLTextureDescriptor* texDesc = [[MTLTextureDescriptor alloc] init];
|
||||
texDesc.textureType = MTLTextureType2D;
|
||||
texDesc.storageMode = MTLStorageModePrivate;
|
||||
texDesc.usage = MTLTextureUsageShaderRead | MTLTextureUsageShaderWrite;
|
||||
texDesc.pixelFormat = MTLPixelFormatBGRA8Unorm;// MTLPixelFormatBGRA8Unorm_sRGB;
|
||||
texDesc.width = drawableSize.width;
|
||||
texDesc.height = drawableSize.height;
|
||||
|
||||
backend->outTexture = [metalSurface->device newTextureWithDescriptor:texDesc];
|
||||
//TODO(martin): retain ?
|
||||
|
||||
//-----------------------------------------------------------
|
||||
//NOTE(martin): create our atlas texture
|
||||
//-----------------------------------------------------------
|
||||
texDesc.textureType = MTLTextureType2D;
|
||||
texDesc.storageMode = MTLStorageModeManaged;
|
||||
texDesc.usage = MTLTextureUsageShaderRead;
|
||||
texDesc.pixelFormat = MTLPixelFormatRGBA8Unorm; //MTLPixelFormatBGRA8Unorm;
|
||||
texDesc.width = MG_ATLAS_SIZE;
|
||||
texDesc.height = MG_ATLAS_SIZE;
|
||||
|
||||
backend->atlasTexture = [metalSurface->device newTextureWithDescriptor:texDesc];
|
||||
|
||||
//-----------------------------------------------------------
|
||||
//NOTE(martin): create buffers for vertex and index
|
||||
//-----------------------------------------------------------
|
||||
|
||||
MTLResourceOptions bufferOptions = MTLResourceCPUCacheModeWriteCombined
|
||||
| MTLResourceStorageModeShared;
|
||||
|
||||
backend->indexBuffer = [metalSurface->device newBufferWithLength: MG_METAL_CANVAS_DEFAULT_BUFFER_LENGTH*sizeof(int)
|
||||
options: bufferOptions];
|
||||
|
||||
backend->vertexBuffer = [metalSurface->device newBufferWithLength: MG_METAL_CANVAS_DEFAULT_BUFFER_LENGTH*sizeof(mg_vertex)
|
||||
options: bufferOptions];
|
||||
|
||||
backend->tilesArray = [metalSurface->device newBufferWithLength: RENDERER_TILE_BUFFER_SIZE*sizeof(int)*RENDERER_MAX_TILES
|
||||
options: MTLResourceStorageModePrivate];
|
||||
|
||||
backend->triangleArray = [metalSurface->device newBufferWithLength: MG_METAL_CANVAS_DEFAULT_BUFFER_LENGTH*sizeof(mg_triangle_data)
|
||||
options: MTLResourceStorageModePrivate];
|
||||
|
||||
backend->boxArray = [metalSurface->device newBufferWithLength: MG_METAL_CANVAS_DEFAULT_BUFFER_LENGTH*sizeof(vector_float4)
|
||||
options: MTLResourceStorageModePrivate];
|
||||
|
||||
//TODO(martin): retain ?
|
||||
//-----------------------------------------------------------
|
||||
//NOTE(martin): create and initialize tile counters
|
||||
//-----------------------------------------------------------
|
||||
backend->tileCounters = [metalSurface->device newBufferWithLength: RENDERER_MAX_TILES*sizeof(uint)
|
||||
options: MTLResourceStorageModePrivate];
|
||||
id<MTLCommandBuffer> commandBuffer = [metalSurface->commandQueue commandBuffer];
|
||||
id<MTLBlitCommandEncoder> blitEncoder = [commandBuffer blitCommandEncoder];
|
||||
[blitEncoder fillBuffer: backend->tileCounters range: NSMakeRange(0, RENDERER_MAX_TILES*sizeof(uint)) value: 0];
|
||||
[blitEncoder endEncoding];
|
||||
[commandBuffer commit];
|
||||
|
||||
//-----------------------------------------------------------
|
||||
//NOTE(martin): load the library
|
||||
//-----------------------------------------------------------
|
||||
|
||||
//TODO(martin): filepath magic to find metallib path when not in the working directory
|
||||
str8 shaderPath = mp_app_get_resource_path(mem_scratch(), "../resources/metal_shader.metallib");
|
||||
NSString* metalFileName = [[NSString alloc] initWithBytes: shaderPath.ptr length:shaderPath.len encoding: NSUTF8StringEncoding];
|
||||
NSError* err = 0;
|
||||
id<MTLLibrary> library = [metalSurface->device newLibraryWithFile: metalFileName error:&err];
|
||||
if(err != nil)
|
||||
{
|
||||
const char* errStr = [[err localizedDescription] UTF8String];
|
||||
LOG_ERROR("error : %s\n", errStr);
|
||||
return(0);
|
||||
}
|
||||
id<MTLFunction> tilingFunction = [library newFunctionWithName:@"TileKernel"];
|
||||
id<MTLFunction> sortingFunction = [library newFunctionWithName:@"SortKernel"];
|
||||
id<MTLFunction> boxingFunction = [library newFunctionWithName:@"BoundingBoxKernel"];
|
||||
id<MTLFunction> computeFunction = [library newFunctionWithName:@"RenderKernel"];
|
||||
id<MTLFunction> vertexFunction = [library newFunctionWithName:@"VertexShader"];
|
||||
id<MTLFunction> fragmentFunction = [library newFunctionWithName:@"FragmentShader"];
|
||||
|
||||
//-----------------------------------------------------------
|
||||
//NOTE(martin): setup our data layout and pipeline state
|
||||
//-----------------------------------------------------------
|
||||
NSError* error = NULL;
|
||||
backend->computePipeline = [metalSurface->device newComputePipelineStateWithFunction: computeFunction
|
||||
error:&error];
|
||||
ASSERT(backend->computePipeline);
|
||||
|
||||
MTLComputePipelineDescriptor* tilingPipelineDesc = [[MTLComputePipelineDescriptor alloc] init];
|
||||
tilingPipelineDesc.computeFunction = tilingFunction;
|
||||
// tilingPipelineDesc.threadGroupSizeIsMultipleOfThreadExecutionWidth = true;
|
||||
|
||||
backend->tilingPipeline = [metalSurface->device newComputePipelineStateWithDescriptor: tilingPipelineDesc
|
||||
options: MTLPipelineOptionNone
|
||||
reflection: nil
|
||||
error: &error];
|
||||
|
||||
MTLComputePipelineDescriptor* sortingPipelineDesc = [[MTLComputePipelineDescriptor alloc] init];
|
||||
sortingPipelineDesc.computeFunction = sortingFunction;
|
||||
// sortingPipelineDesc.threadGroupSizeIsMultipleOfThreadExecutionWidth = true;
|
||||
|
||||
backend->sortingPipeline = [metalSurface->device newComputePipelineStateWithDescriptor: sortingPipelineDesc
|
||||
options: MTLPipelineOptionNone
|
||||
reflection: nil
|
||||
error: &error];
|
||||
|
||||
MTLComputePipelineDescriptor* boxingPipelineDesc = [[MTLComputePipelineDescriptor alloc] init];
|
||||
boxingPipelineDesc.computeFunction = boxingFunction;
|
||||
// boxingPipelineDesc.threadGroupSizeIsMultipleOfThreadExecutionWidth = true;
|
||||
|
||||
backend->boxingPipeline = [metalSurface->device newComputePipelineStateWithDescriptor: boxingPipelineDesc
|
||||
options: MTLPipelineOptionNone
|
||||
reflection: nil
|
||||
error: &error];
|
||||
//-----------------------------------------------------------
|
||||
//NOTE(martin): setup our render pipeline state
|
||||
//-----------------------------------------------------------
|
||||
// create and initialize the pipeline state descriptor
|
||||
MTLRenderPipelineDescriptor *pipelineStateDescriptor = [[MTLRenderPipelineDescriptor alloc] init];
|
||||
pipelineStateDescriptor.label = @"My simple pipeline";
|
||||
pipelineStateDescriptor.vertexFunction = vertexFunction;
|
||||
pipelineStateDescriptor.fragmentFunction = fragmentFunction;
|
||||
pipelineStateDescriptor.colorAttachments[0].pixelFormat = metalSurface->metalLayer.pixelFormat;
|
||||
|
||||
// create render pipeline
|
||||
backend->renderPipeline = [metalSurface->device newRenderPipelineStateWithDescriptor: pipelineStateDescriptor error:&err];
|
||||
if(err != nil)
|
||||
{
|
||||
const char* errStr = [[err localizedDescription] UTF8String];
|
||||
const char* descStr = [[err localizedFailureReason] UTF8String];
|
||||
const char* recovStr = [[err localizedRecoverySuggestion] UTF8String];
|
||||
LOG_ERROR("(%li) %s. %s. %s\n", [err code], errStr, descStr, recovStr);
|
||||
return(0);
|
||||
}
|
||||
}
|
||||
|
||||
mg_metal_canvas_update_vertex_layout(backend);
|
||||
}
|
||||
|
||||
return((mg_canvas_backend*)backend);
|
||||
}
|
||||
|
||||
|
||||
#undef LOG_SUBSYSTEM
|
|
@ -1,417 +0,0 @@
|
|||
/************************************************************//**
|
||||
*
|
||||
* @file: graphics.m
|
||||
* @author: Martin Fouilleul
|
||||
* @date: 12/07/2020
|
||||
* @revision:
|
||||
*
|
||||
*****************************************************************/
|
||||
#import<Metal/Metal.h>
|
||||
#import<QuartzCore/CAMetalLayer.h>
|
||||
#include<simd/simd.h>
|
||||
|
||||
#include"graphics_internal.h"
|
||||
#include"macro_helpers.h"
|
||||
#include"osx_app.h"
|
||||
|
||||
#include"metal_shader.h"
|
||||
|
||||
#define LOG_SUBSYSTEM "Graphics"
|
||||
|
||||
static const int MG_METAL_PAINTER_DEFAULT_BUFFER_LENGTH = 4<<20;
|
||||
|
||||
typedef struct mg_metal_painter
|
||||
{
|
||||
mg_canvas_painter interface;
|
||||
|
||||
mg_metal_surface* surface;
|
||||
|
||||
// permanent metal resources
|
||||
id<MTLComputePipelineState> tilingPipeline;
|
||||
id<MTLComputePipelineState> sortingPipeline;
|
||||
id<MTLComputePipelineState> boxingPipeline;
|
||||
id<MTLComputePipelineState> computePipeline;
|
||||
id<MTLRenderPipelineState> renderPipeline;
|
||||
|
||||
mp_rect viewPort;
|
||||
|
||||
// textures and buffers
|
||||
id<MTLTexture> outTexture;
|
||||
id<MTLTexture> atlasTexture;
|
||||
id<MTLBuffer> vertexBuffer;
|
||||
id<MTLBuffer> indexBuffer;
|
||||
id<MTLBuffer> tileCounters;
|
||||
id<MTLBuffer> tilesArray;
|
||||
id<MTLBuffer> triangleArray;
|
||||
id<MTLBuffer> boxArray;
|
||||
|
||||
} mg_metal_painter;
|
||||
|
||||
void mg_metal_painter_draw_buffers(mg_canvas_painter* interface, u32 vertexCount, u32 indexCount, mg_color clearColor)
|
||||
{
|
||||
mg_metal_painter* painter = (mg_metal_painter*)interface;
|
||||
mg_metal_surface* surface = painter->surface;
|
||||
|
||||
@autoreleasepool
|
||||
{
|
||||
if(surface->commandBuffer == nil || surface->commandBuffer == nil)
|
||||
{
|
||||
mg_metal_surface_acquire_drawable_and_command_buffer(surface);
|
||||
}
|
||||
|
||||
ASSERT(indexCount * sizeof(i32) < [painter->indexBuffer length]);
|
||||
|
||||
f32 scale = surface->metalLayer.contentsScale;
|
||||
vector_uint2 viewportSize = {painter->viewPort.w * scale, painter->viewPort.h * scale};
|
||||
|
||||
//-----------------------------------------------------------
|
||||
//NOTE(martin): encode the clear counter
|
||||
//-----------------------------------------------------------
|
||||
id<MTLBlitCommandEncoder> blitEncoder = [surface->commandBuffer blitCommandEncoder];
|
||||
[blitEncoder fillBuffer: painter->tileCounters range: NSMakeRange(0, RENDERER_MAX_TILES*sizeof(uint)) value: 0];
|
||||
[blitEncoder endEncoding];
|
||||
|
||||
//-----------------------------------------------------------
|
||||
//NOTE(martin): encode the boxing pass
|
||||
//-----------------------------------------------------------
|
||||
id<MTLComputeCommandEncoder> boxEncoder = [surface->commandBuffer computeCommandEncoder];
|
||||
[boxEncoder setComputePipelineState: painter->boxingPipeline];
|
||||
[boxEncoder setBuffer: painter->vertexBuffer offset:0 atIndex: 0];
|
||||
[boxEncoder setBuffer: painter->indexBuffer offset:0 atIndex: 1];
|
||||
[boxEncoder setBuffer: painter->triangleArray offset:0 atIndex: 2];
|
||||
[boxEncoder setBuffer: painter->boxArray offset:0 atIndex: 3];
|
||||
[boxEncoder setBytes: &scale length: sizeof(float) atIndex: 4];
|
||||
|
||||
MTLSize boxGroupSize = MTLSizeMake(painter->boxingPipeline.maxTotalThreadsPerThreadgroup, 1, 1);
|
||||
MTLSize boxGridSize = MTLSizeMake(indexCount/3, 1, 1);
|
||||
|
||||
[boxEncoder dispatchThreads: boxGridSize threadsPerThreadgroup: boxGroupSize];
|
||||
[boxEncoder endEncoding];
|
||||
|
||||
//-----------------------------------------------------------
|
||||
//NOTE(martin): encode the tiling pass
|
||||
//-----------------------------------------------------------
|
||||
|
||||
id<MTLComputeCommandEncoder> tileEncoder = [surface->commandBuffer computeCommandEncoder];
|
||||
[tileEncoder setComputePipelineState: painter->tilingPipeline];
|
||||
[tileEncoder setBuffer: painter->boxArray offset:0 atIndex: 0];
|
||||
[tileEncoder setBuffer: painter->tileCounters offset:0 atIndex: 1];
|
||||
[tileEncoder setBuffer: painter->tilesArray offset:0 atIndex: 2];
|
||||
[tileEncoder setBytes: &viewportSize length: sizeof(vector_uint2) atIndex: 3];
|
||||
|
||||
[tileEncoder dispatchThreads: boxGridSize threadsPerThreadgroup: boxGroupSize];
|
||||
[tileEncoder endEncoding];
|
||||
|
||||
//-----------------------------------------------------------
|
||||
//NOTE(martin): encode the sorting pass
|
||||
//-----------------------------------------------------------
|
||||
|
||||
id<MTLComputeCommandEncoder> sortEncoder = [surface->commandBuffer computeCommandEncoder];
|
||||
[sortEncoder setComputePipelineState: painter->sortingPipeline];
|
||||
[sortEncoder setBuffer: painter->tileCounters offset:0 atIndex: 0];
|
||||
[sortEncoder setBuffer: painter->triangleArray offset:0 atIndex: 1];
|
||||
[sortEncoder setBuffer: painter->tilesArray offset:0 atIndex: 2];
|
||||
[sortEncoder setBytes: &viewportSize length: sizeof(vector_uint2) atIndex: 3];
|
||||
|
||||
u32 nTilesX = (viewportSize.x + RENDERER_TILE_SIZE - 1)/RENDERER_TILE_SIZE;
|
||||
u32 nTilesY = (viewportSize.y + RENDERER_TILE_SIZE - 1)/RENDERER_TILE_SIZE;
|
||||
|
||||
MTLSize sortGroupSize = MTLSizeMake(painter->boxingPipeline.maxTotalThreadsPerThreadgroup, 1, 1);
|
||||
MTLSize sortGridSize = MTLSizeMake(nTilesX*nTilesY, 1, 1);
|
||||
|
||||
[sortEncoder dispatchThreads: sortGridSize threadsPerThreadgroup: sortGroupSize];
|
||||
[sortEncoder endEncoding];
|
||||
|
||||
//-----------------------------------------------------------
|
||||
//NOTE(martin): create compute encoder and encode commands
|
||||
//-----------------------------------------------------------
|
||||
vector_float4 clearColorVec4 = {clearColor.r, clearColor.g, clearColor.b, clearColor.a};
|
||||
|
||||
id<MTLComputeCommandEncoder> encoder = [surface->commandBuffer computeCommandEncoder];
|
||||
[encoder setComputePipelineState:painter->computePipeline];
|
||||
[encoder setTexture: painter->outTexture atIndex: 0];
|
||||
[encoder setTexture: painter->atlasTexture atIndex: 1];
|
||||
[encoder setBuffer: painter->vertexBuffer offset:0 atIndex: 0];
|
||||
[encoder setBuffer: painter->tileCounters offset:0 atIndex: 1];
|
||||
[encoder setBuffer: painter->tilesArray offset:0 atIndex: 2];
|
||||
[encoder setBuffer: painter->triangleArray offset:0 atIndex: 3];
|
||||
[encoder setBuffer: painter->boxArray offset:0 atIndex: 4];
|
||||
[encoder setBytes: &clearColorVec4 length: sizeof(vector_float4) atIndex: 5];
|
||||
|
||||
//TODO: check that we don't exceed maxTotalThreadsPerThreadgroup
|
||||
DEBUG_ASSERT(RENDERER_TILE_SIZE*RENDERER_TILE_SIZE <= painter->computePipeline.maxTotalThreadsPerThreadgroup);
|
||||
MTLSize threadGridSize = MTLSizeMake(viewportSize.x, viewportSize.y, 1);
|
||||
MTLSize threadGroupSize = MTLSizeMake(RENDERER_TILE_SIZE, RENDERER_TILE_SIZE, 1);
|
||||
|
||||
[encoder dispatchThreads: threadGridSize threadsPerThreadgroup:threadGroupSize];
|
||||
[encoder endEncoding];
|
||||
|
||||
//-----------------------------------------------------------
|
||||
//NOTE(martin): acquire drawable, create render encoder to blit texture to framebuffer
|
||||
//-----------------------------------------------------------
|
||||
|
||||
MTLViewport viewport = {painter->viewPort.x * scale,
|
||||
painter->viewPort.y * scale,
|
||||
painter->viewPort.w * scale,
|
||||
painter->viewPort.h * scale,
|
||||
0,
|
||||
1};
|
||||
|
||||
MTLRenderPassDescriptor* renderPassDescriptor = [MTLRenderPassDescriptor renderPassDescriptor];
|
||||
renderPassDescriptor.colorAttachments[0].texture = surface->drawable.texture;
|
||||
renderPassDescriptor.colorAttachments[0].storeAction = MTLStoreActionStore;
|
||||
|
||||
id<MTLRenderCommandEncoder> renderEncoder = [surface->commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor];
|
||||
[renderEncoder setViewport: viewport];
|
||||
[renderEncoder setRenderPipelineState: painter->renderPipeline];
|
||||
[renderEncoder setFragmentTexture: painter->outTexture atIndex: 0];
|
||||
[renderEncoder drawPrimitives: MTLPrimitiveTypeTriangle
|
||||
vertexStart: 0
|
||||
vertexCount: 3 ];
|
||||
[renderEncoder endEncoding];
|
||||
}
|
||||
}
|
||||
|
||||
void mg_metal_painter_viewport(mg_canvas_painter* interface, mp_rect viewPort)
|
||||
{
|
||||
mg_metal_painter* painter = (mg_metal_painter*)interface;
|
||||
mg_metal_surface* surface = painter->surface;
|
||||
|
||||
painter->viewPort = viewPort;
|
||||
|
||||
@autoreleasepool
|
||||
{
|
||||
f32 scale = surface->metalLayer.contentsScale;
|
||||
CGSize drawableSize = (CGSize){.width = viewPort.w * scale, .height = viewPort.h * scale};
|
||||
|
||||
[painter->outTexture release];
|
||||
|
||||
MTLTextureDescriptor* texDesc = [[MTLTextureDescriptor alloc] init];
|
||||
texDesc.textureType = MTLTextureType2D;
|
||||
texDesc.storageMode = MTLStorageModePrivate;
|
||||
texDesc.usage = MTLTextureUsageShaderRead | MTLTextureUsageShaderWrite;
|
||||
texDesc.pixelFormat = MTLPixelFormatBGRA8Unorm;// MTLPixelFormatBGRA8Unorm_sRGB;
|
||||
texDesc.width = drawableSize.width;
|
||||
texDesc.height = drawableSize.height;
|
||||
|
||||
painter->outTexture = [surface->device newTextureWithDescriptor:texDesc];
|
||||
}
|
||||
}
|
||||
|
||||
void mg_metal_painter_update_vertex_layout(mg_metal_painter* painter)
|
||||
{
|
||||
mg_metal_surface* surface = painter->surface;
|
||||
|
||||
char* vertexBase = (char*)[painter->vertexBuffer contents];
|
||||
|
||||
painter->interface.vertexLayout = (mgc_vertex_layout){
|
||||
.maxVertexCount = MG_METAL_PAINTER_DEFAULT_BUFFER_LENGTH,
|
||||
.maxIndexCount = MG_METAL_PAINTER_DEFAULT_BUFFER_LENGTH,
|
||||
.posBuffer = vertexBase + offsetof(mg_vertex, pos),
|
||||
.posStride = sizeof(mg_vertex),
|
||||
.cubicBuffer = vertexBase + offsetof(mg_vertex, cubic),
|
||||
.cubicStride = sizeof(mg_vertex),
|
||||
.uvBuffer = vertexBase + offsetof(mg_vertex, uv),
|
||||
.uvStride = sizeof(mg_vertex),
|
||||
.colorBuffer = vertexBase + offsetof(mg_vertex, color),
|
||||
.colorStride = sizeof(mg_vertex),
|
||||
.zIndexBuffer = vertexBase + offsetof(mg_vertex, zIndex),
|
||||
.zIndexStride = sizeof(mg_vertex),
|
||||
.clipsBuffer = vertexBase + offsetof(mg_vertex, clip),
|
||||
.clipsStride = sizeof(mg_vertex),
|
||||
.indexBuffer = [painter->indexBuffer contents],
|
||||
.indexStride = sizeof(int)};
|
||||
}
|
||||
|
||||
|
||||
void mg_metal_painter_destroy(mg_canvas_painter* interface)
|
||||
{
|
||||
mg_metal_painter* painter = (mg_metal_painter*)interface;
|
||||
|
||||
@autoreleasepool
|
||||
{
|
||||
[painter->outTexture release];
|
||||
[painter->atlasTexture release];
|
||||
[painter->vertexBuffer release];
|
||||
[painter->indexBuffer release];
|
||||
[painter->tilesArray release];
|
||||
[painter->triangleArray release];
|
||||
[painter->boxArray release];
|
||||
[painter->computePipeline release];
|
||||
}
|
||||
}
|
||||
|
||||
void mg_metal_painter_atlas_upload(mg_canvas_painter* interface, mp_rect rect, u8* bytes)
|
||||
{@autoreleasepool{
|
||||
mg_metal_painter* painter = (mg_metal_painter*)interface;
|
||||
|
||||
MTLRegion region = MTLRegionMake2D(rect.x, rect.y, rect.w, rect.h);
|
||||
[painter->atlasTexture replaceRegion:region
|
||||
mipmapLevel:0
|
||||
withBytes:(void*)bytes
|
||||
bytesPerRow: 4 * rect.w];
|
||||
}}
|
||||
|
||||
mg_canvas_painter* mg_metal_painter_create_for_surface(mg_metal_surface* surface, mp_rect viewPort)
|
||||
{
|
||||
mg_metal_painter* painter = malloc_type(mg_metal_painter);
|
||||
painter->surface = surface;
|
||||
|
||||
//NOTE(martin): setup interface functions
|
||||
painter->interface.drawBuffers = mg_metal_painter_draw_buffers;
|
||||
painter->interface.setViewPort = mg_metal_painter_viewport;
|
||||
painter->interface.destroy = mg_metal_painter_destroy;
|
||||
painter->interface.atlasUpload = mg_metal_painter_atlas_upload;
|
||||
|
||||
painter->viewPort = viewPort;
|
||||
|
||||
@autoreleasepool
|
||||
{
|
||||
f32 scale = surface->metalLayer.contentsScale;
|
||||
CGSize drawableSize = (CGSize){.width = viewPort.w * scale, .height = viewPort.h * scale};
|
||||
|
||||
//-----------------------------------------------------------
|
||||
//NOTE(martin): create our output texture
|
||||
//-----------------------------------------------------------
|
||||
MTLTextureDescriptor* texDesc = [[MTLTextureDescriptor alloc] init];
|
||||
texDesc.textureType = MTLTextureType2D;
|
||||
texDesc.storageMode = MTLStorageModePrivate;
|
||||
texDesc.usage = MTLTextureUsageShaderRead | MTLTextureUsageShaderWrite;
|
||||
texDesc.pixelFormat = MTLPixelFormatBGRA8Unorm;// MTLPixelFormatBGRA8Unorm_sRGB;
|
||||
texDesc.width = drawableSize.width;
|
||||
texDesc.height = drawableSize.height;
|
||||
|
||||
painter->outTexture = [surface->device newTextureWithDescriptor:texDesc];
|
||||
//TODO(martin): retain ?
|
||||
|
||||
//-----------------------------------------------------------
|
||||
//NOTE(martin): create our atlas texture
|
||||
//-----------------------------------------------------------
|
||||
texDesc.textureType = MTLTextureType2D;
|
||||
texDesc.storageMode = MTLStorageModeManaged;
|
||||
texDesc.usage = MTLTextureUsageShaderRead;
|
||||
texDesc.pixelFormat = MTLPixelFormatRGBA8Unorm; //MTLPixelFormatBGRA8Unorm;
|
||||
texDesc.width = MG_ATLAS_SIZE;
|
||||
texDesc.height = MG_ATLAS_SIZE;
|
||||
|
||||
painter->atlasTexture = [surface->device newTextureWithDescriptor:texDesc];
|
||||
|
||||
//-----------------------------------------------------------
|
||||
//NOTE(martin): create buffers for vertex and index
|
||||
//-----------------------------------------------------------
|
||||
|
||||
MTLResourceOptions bufferOptions = MTLResourceCPUCacheModeWriteCombined
|
||||
| MTLResourceStorageModeShared;
|
||||
|
||||
painter->indexBuffer = [surface->device newBufferWithLength: MG_METAL_PAINTER_DEFAULT_BUFFER_LENGTH*sizeof(int)
|
||||
options: bufferOptions];
|
||||
|
||||
painter->vertexBuffer = [surface->device newBufferWithLength: MG_METAL_PAINTER_DEFAULT_BUFFER_LENGTH*sizeof(mg_vertex)
|
||||
options: bufferOptions];
|
||||
|
||||
painter->tilesArray = [surface->device newBufferWithLength: RENDERER_TILE_BUFFER_SIZE*sizeof(int)*RENDERER_MAX_TILES
|
||||
options: MTLResourceStorageModePrivate];
|
||||
|
||||
painter->triangleArray = [surface->device newBufferWithLength: MG_METAL_PAINTER_DEFAULT_BUFFER_LENGTH*sizeof(mg_triangle_data)
|
||||
options: MTLResourceStorageModePrivate];
|
||||
|
||||
painter->boxArray = [surface->device newBufferWithLength: MG_METAL_PAINTER_DEFAULT_BUFFER_LENGTH*sizeof(vector_float4)
|
||||
options: MTLResourceStorageModePrivate];
|
||||
|
||||
//TODO(martin): retain ?
|
||||
//-----------------------------------------------------------
|
||||
//NOTE(martin): create and initialize tile counters
|
||||
//-----------------------------------------------------------
|
||||
painter->tileCounters = [surface->device newBufferWithLength: RENDERER_MAX_TILES*sizeof(uint)
|
||||
options: MTLResourceStorageModePrivate];
|
||||
id<MTLCommandBuffer> commandBuffer = [surface->commandQueue commandBuffer];
|
||||
id<MTLBlitCommandEncoder> blitEncoder = [commandBuffer blitCommandEncoder];
|
||||
[blitEncoder fillBuffer: painter->tileCounters range: NSMakeRange(0, RENDERER_MAX_TILES*sizeof(uint)) value: 0];
|
||||
[blitEncoder endEncoding];
|
||||
[commandBuffer commit];
|
||||
|
||||
//-----------------------------------------------------------
|
||||
//NOTE(martin): load the library
|
||||
//-----------------------------------------------------------
|
||||
|
||||
//TODO(martin): filepath magic to find metallib path when not in the working directory
|
||||
str8 shaderPath = mp_app_get_resource_path(mem_scratch(), "../resources/metal_shader.metallib");
|
||||
NSString* metalFileName = [[NSString alloc] initWithBytes: shaderPath.ptr length:shaderPath.len encoding: NSUTF8StringEncoding];
|
||||
NSError* err = 0;
|
||||
id<MTLLibrary> library = [surface->device newLibraryWithFile: metalFileName error:&err];
|
||||
if(err != nil)
|
||||
{
|
||||
const char* errStr = [[err localizedDescription] UTF8String];
|
||||
LOG_ERROR("error : %s\n", errStr);
|
||||
return(0);
|
||||
}
|
||||
id<MTLFunction> tilingFunction = [library newFunctionWithName:@"TileKernel"];
|
||||
id<MTLFunction> sortingFunction = [library newFunctionWithName:@"SortKernel"];
|
||||
id<MTLFunction> boxingFunction = [library newFunctionWithName:@"BoundingBoxKernel"];
|
||||
id<MTLFunction> computeFunction = [library newFunctionWithName:@"RenderKernel"];
|
||||
id<MTLFunction> vertexFunction = [library newFunctionWithName:@"VertexShader"];
|
||||
id<MTLFunction> fragmentFunction = [library newFunctionWithName:@"FragmentShader"];
|
||||
|
||||
//-----------------------------------------------------------
|
||||
//NOTE(martin): setup our data layout and pipeline state
|
||||
//-----------------------------------------------------------
|
||||
NSError* error = NULL;
|
||||
painter->computePipeline = [surface->device newComputePipelineStateWithFunction: computeFunction
|
||||
error:&error];
|
||||
ASSERT(painter->computePipeline);
|
||||
|
||||
MTLComputePipelineDescriptor* tilingPipelineDesc = [[MTLComputePipelineDescriptor alloc] init];
|
||||
tilingPipelineDesc.computeFunction = tilingFunction;
|
||||
// tilingPipelineDesc.threadGroupSizeIsMultipleOfThreadExecutionWidth = true;
|
||||
|
||||
painter->tilingPipeline = [surface->device newComputePipelineStateWithDescriptor: tilingPipelineDesc
|
||||
options: MTLPipelineOptionNone
|
||||
reflection: nil
|
||||
error: &error];
|
||||
|
||||
MTLComputePipelineDescriptor* sortingPipelineDesc = [[MTLComputePipelineDescriptor alloc] init];
|
||||
sortingPipelineDesc.computeFunction = sortingFunction;
|
||||
// sortingPipelineDesc.threadGroupSizeIsMultipleOfThreadExecutionWidth = true;
|
||||
|
||||
painter->sortingPipeline = [surface->device newComputePipelineStateWithDescriptor: sortingPipelineDesc
|
||||
options: MTLPipelineOptionNone
|
||||
reflection: nil
|
||||
error: &error];
|
||||
|
||||
MTLComputePipelineDescriptor* boxingPipelineDesc = [[MTLComputePipelineDescriptor alloc] init];
|
||||
boxingPipelineDesc.computeFunction = boxingFunction;
|
||||
// boxingPipelineDesc.threadGroupSizeIsMultipleOfThreadExecutionWidth = true;
|
||||
|
||||
painter->boxingPipeline = [surface->device newComputePipelineStateWithDescriptor: boxingPipelineDesc
|
||||
options: MTLPipelineOptionNone
|
||||
reflection: nil
|
||||
error: &error];
|
||||
//-----------------------------------------------------------
|
||||
//NOTE(martin): setup our render pipeline state
|
||||
//-----------------------------------------------------------
|
||||
// create and initialize the pipeline state descriptor
|
||||
MTLRenderPipelineDescriptor *pipelineStateDescriptor = [[MTLRenderPipelineDescriptor alloc] init];
|
||||
pipelineStateDescriptor.label = @"My simple pipeline";
|
||||
pipelineStateDescriptor.vertexFunction = vertexFunction;
|
||||
pipelineStateDescriptor.fragmentFunction = fragmentFunction;
|
||||
pipelineStateDescriptor.colorAttachments[0].pixelFormat = surface->metalLayer.pixelFormat;
|
||||
|
||||
// create render pipeline
|
||||
painter->renderPipeline = [surface->device newRenderPipelineStateWithDescriptor: pipelineStateDescriptor error:&err];
|
||||
if(err != nil)
|
||||
{
|
||||
const char* errStr = [[err localizedDescription] UTF8String];
|
||||
const char* descStr = [[err localizedFailureReason] UTF8String];
|
||||
const char* recovStr = [[err localizedRecoverySuggestion] UTF8String];
|
||||
LOG_ERROR("(%li) %s. %s. %s\n", [err code], errStr, descStr, recovStr);
|
||||
return(0);
|
||||
}
|
||||
}
|
||||
|
||||
mg_metal_painter_update_vertex_layout(painter);
|
||||
|
||||
return((mg_canvas_painter*)painter);
|
||||
}
|
||||
|
||||
|
||||
#undef LOG_SUBSYSTEM
|
|
@ -14,9 +14,9 @@
|
|||
#endif
|
||||
|
||||
mg_surface mg_metal_surface_create_for_window(mp_window window);
|
||||
|
||||
void* mg_metal_surface_render_encoder(mg_surface surface);
|
||||
void* mg_metal_surface_compute_encoder(mg_surface surface);
|
||||
|
||||
void* mg_metal_surface_layer(mg_surface surface);
|
||||
void* mg_metal_surface_drawable(mg_surface surface);
|
||||
void* mg_metal_surface_command_buffer(mg_surface surface);
|
||||
|
|
|
@ -21,7 +21,7 @@ static const u32 MP_METAL_MAX_DRAWABLES_IN_FLIGHT = 3;
|
|||
|
||||
typedef struct mg_metal_surface
|
||||
{
|
||||
mg_surface_info interface;
|
||||
mg_surface_data interface;
|
||||
|
||||
// permanent metal resources
|
||||
NSView* view;
|
||||
|
@ -37,6 +37,18 @@ typedef struct mg_metal_surface
|
|||
|
||||
} 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
|
||||
|
@ -80,7 +92,7 @@ void mg_metal_surface_acquire_drawable_and_command_buffer(mg_metal_surface* surf
|
|||
[surface->drawable retain];
|
||||
}}
|
||||
|
||||
void mg_metal_surface_prepare(mg_surface_info* interface)
|
||||
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);
|
||||
|
@ -97,7 +109,7 @@ void mg_metal_surface_prepare(mg_surface_info* interface)
|
|||
}
|
||||
}
|
||||
|
||||
void mg_metal_surface_present(mg_surface_info* interface)
|
||||
void mg_metal_surface_present(mg_surface_data* interface)
|
||||
{
|
||||
mg_metal_surface* surface = (mg_metal_surface*)interface;
|
||||
@autoreleasepool
|
||||
|
@ -115,34 +127,32 @@ void mg_metal_surface_present(mg_surface_info* interface)
|
|||
}
|
||||
}
|
||||
|
||||
void mg_metal_surface_destroy(mg_surface_info* interface)
|
||||
void mg_metal_surface_set_frame(mg_surface_data* interface, mp_rect frame)
|
||||
{
|
||||
mg_metal_surface* surface = (mg_metal_surface*)interface;
|
||||
|
||||
@autoreleasepool
|
||||
{
|
||||
[surface->commandQueue release];
|
||||
[surface->metalLayer release];
|
||||
[surface->device release];
|
||||
}
|
||||
}
|
||||
|
||||
void mg_metal_surface_resize(mg_surface_info* interface, u32 width, u32 height)
|
||||
{
|
||||
mg_metal_surface* surface = (mg_metal_surface*)interface;
|
||||
|
||||
@autoreleasepool
|
||||
{
|
||||
//TODO(martin): actually detect scaling
|
||||
CGRect frame = {{0, 0}, {width, height}};
|
||||
[surface->view setFrame: frame];
|
||||
CGRect cgFrame = {{frame.x, frame.y}, {frame.w, frame.h}};
|
||||
[surface->view setFrame: cgFrame];
|
||||
f32 scale = surface->metalLayer.contentsScale;
|
||||
CGSize drawableSize = (CGSize){.width = width * scale, .height = height * scale};
|
||||
CGSize drawableSize = (CGSize){.width = frame.w * scale, .height = frame.h * scale};
|
||||
surface->metalLayer.drawableSize = drawableSize;
|
||||
}
|
||||
}
|
||||
|
||||
void mg_metal_surface_set_hidden(mg_surface_info* interface, bool hidden)
|
||||
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
|
||||
|
@ -154,24 +164,16 @@ void mg_metal_surface_set_hidden(mg_surface_info* interface, bool hidden)
|
|||
}
|
||||
}
|
||||
|
||||
vec2 mg_metal_surface_get_size(mg_surface_info* interface)
|
||||
bool mg_metal_surface_get_hidden(mg_surface_data* interface)
|
||||
{
|
||||
mg_metal_surface* surface = (mg_metal_surface*)interface;
|
||||
|
||||
@autoreleasepool
|
||||
{
|
||||
//TODO(martin): actually detect scaling
|
||||
CGRect frame = surface->view.frame;
|
||||
return((vec2){frame.size.width, frame.size.height});
|
||||
return([surface->metalLayer isHidden]);
|
||||
}
|
||||
}
|
||||
|
||||
void* mg_metal_surface_get_os_resource(mg_surface_info* interface)
|
||||
{
|
||||
mg_metal_surface* surface = (mg_metal_surface*)interface;
|
||||
return((void*)surface->metalLayer);
|
||||
}
|
||||
|
||||
//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)
|
||||
|
@ -190,10 +192,10 @@ mg_surface mg_metal_surface_create_for_window(mp_window window)
|
|||
surface->interface.destroy = mg_metal_surface_destroy;
|
||||
surface->interface.prepare = mg_metal_surface_prepare;
|
||||
surface->interface.present = mg_metal_surface_present;
|
||||
surface->interface.resize = mg_metal_surface_resize;
|
||||
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;
|
||||
surface->interface.getSize = mg_metal_surface_get_size;
|
||||
surface->interface.getOSResource = mg_metal_surface_get_os_resource;
|
||||
|
||||
@autoreleasepool
|
||||
{
|
||||
|
@ -246,14 +248,14 @@ mg_surface mg_metal_surface_create_for_window(mp_window window)
|
|||
surface->commandBuffer = nil;
|
||||
}
|
||||
|
||||
mg_surface handle = mg_surface_alloc_handle((mg_surface_info*)surface);
|
||||
mg_surface handle = mg_surface_alloc_handle((mg_surface_data*)surface);
|
||||
return(handle);
|
||||
}
|
||||
}
|
||||
|
||||
void* mg_metal_surface_layer(mg_surface surface)
|
||||
{
|
||||
mg_surface_info* surfaceData = mg_surface_ptr_from_handle(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;
|
||||
|
@ -267,7 +269,7 @@ void* mg_metal_surface_layer(mg_surface surface)
|
|||
|
||||
void* mg_metal_surface_drawable(mg_surface surface)
|
||||
{
|
||||
mg_surface_info* surfaceData = mg_surface_ptr_from_handle(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;
|
||||
|
@ -281,7 +283,7 @@ void* mg_metal_surface_drawable(mg_surface surface)
|
|||
|
||||
void* mg_metal_surface_command_buffer(mg_surface surface)
|
||||
{
|
||||
mg_surface_info* surfaceData = mg_surface_ptr_from_handle(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;
|
||||
|
|
|
@ -46,6 +46,6 @@
|
|||
// graphics/ui layer
|
||||
//----------------------------------------------------------------
|
||||
#include"graphics.h"
|
||||
#include"ui.h"
|
||||
//#include"ui.h"
|
||||
|
||||
#endif //__MILEPOST_H_
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
#include"osx_app.m"
|
||||
#include"metal_surface.m"
|
||||
#include"metal_painter.m"
|
||||
#include"metal_canvas.m"
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
|
|
|
@ -19,8 +19,9 @@
|
|||
|
||||
typedef struct mg_gles_surface
|
||||
{
|
||||
mg_surface_info interface;
|
||||
mg_surface_data interface;
|
||||
|
||||
NSView* view;
|
||||
CALayer* layer;
|
||||
EGLDisplay eglDisplay;
|
||||
EGLConfig eglConfig;
|
||||
|
|
|
@ -19,32 +19,59 @@
|
|||
#include"osx_gles_surface.h"
|
||||
|
||||
|
||||
void mg_gles_surface_prepare(mg_surface_info* interface)
|
||||
void mg_gles_surface_destroy(mg_surface_data* interface)
|
||||
{
|
||||
//////////////////////////////////////////////////
|
||||
//TODO
|
||||
//////////////////////////////////////////////////
|
||||
}
|
||||
|
||||
void mg_gles_surface_prepare(mg_surface_data* interface)
|
||||
{
|
||||
mg_gles_surface* surface = (mg_gles_surface*)interface;
|
||||
eglMakeCurrent(surface->eglDisplay, surface->eglSurface, surface->eglSurface, surface->eglContext);
|
||||
}
|
||||
|
||||
void mg_gles_surface_present(mg_surface_info* interface)
|
||||
void mg_gles_surface_present(mg_surface_data* interface)
|
||||
{
|
||||
|
||||
//TODO: eglSwapBuffers seem to never block in macOS (ie eglSwapInterval doesn't seem to have any effect)
|
||||
// We need to use a CVDisplayLink to time this if we want surface present to block
|
||||
|
||||
mg_gles_surface* surface = (mg_gles_surface*)interface;
|
||||
eglSwapBuffers(surface->eglDisplay, surface->eglSurface);
|
||||
}
|
||||
|
||||
void mg_gles_surface_resize(mg_surface_info* interface, u32 width, u32 height)
|
||||
void mg_gles_surface_set_frame(mg_surface_data* interface, mp_rect frame)
|
||||
{
|
||||
mg_gles_surface* surface = (mg_gles_surface*)interface;
|
||||
f32 scale = surface->layer.contentsScale;
|
||||
CGRect bounds = (CGRect){{0, 0}, {.width = width*scale, .height = height*scale}};
|
||||
CGRect bounds = (CGRect){{frame.x * scale, frame.y * scale}, {.width = frame.w*scale, .height = frame.h*scale}};
|
||||
[surface->layer setBounds: bounds];
|
||||
}
|
||||
|
||||
void mg_gles_surface_set_hidden(mg_surface_info* interface, bool hidden)
|
||||
mp_rect mg_gles_surface_get_frame(mg_surface_data* interface)
|
||||
{
|
||||
//NOTE: doesn't make sense for an offscreen surface?
|
||||
mg_gles_surface* surface = (mg_gles_surface*)interface;
|
||||
f32 scale = surface->layer.contentsScale;
|
||||
CGRect bounds = [surface->layer bounds];
|
||||
mp_rect rect = {bounds.origin.x / scale, bounds.origin.y / scale, bounds.size.width / scale, bounds.size.height / scale};
|
||||
return(rect);
|
||||
}
|
||||
|
||||
vec2 mg_gles_surface_get_size(mg_surface_info* interface)
|
||||
|
||||
void mg_gles_surface_set_hidden(mg_surface_data* interface, bool hidden)
|
||||
{
|
||||
//TODO: doesn't make sense for an offscreen surface?
|
||||
}
|
||||
|
||||
bool mg_gles_surface_get_hidden(mg_surface_data* interface)
|
||||
{
|
||||
//TODO: doesn't make sense for an offscreen surface?
|
||||
return(false);
|
||||
}
|
||||
|
||||
vec2 mg_gles_surface_get_size(mg_surface_data* interface)
|
||||
{
|
||||
mg_gles_surface* surface = (mg_gles_surface*)interface;
|
||||
CGRect bounds = [surface->layer bounds];
|
||||
|
@ -53,39 +80,40 @@ vec2 mg_gles_surface_get_size(mg_surface_info* interface)
|
|||
return(res);
|
||||
}
|
||||
|
||||
void mg_gles_surface_destroy(mg_surface_info* interface)
|
||||
{
|
||||
//TODO
|
||||
}
|
||||
|
||||
void* mg_gles_surface_get_os_resource(mg_surface_info* interface)
|
||||
{
|
||||
mg_gles_surface* surface = (mg_gles_surface*)interface;
|
||||
return((void*)surface->layer);
|
||||
}
|
||||
|
||||
mg_surface mg_gles_surface_create_offscreen(u32 width, u32 height)
|
||||
mg_surface mg_gles_surface_create_with_view(u32 width, u32 height, NSView* view)
|
||||
{
|
||||
mg_gles_surface* surface = malloc_type(mg_gles_surface);
|
||||
memset(surface, 0, sizeof(mg_gles_surface));
|
||||
|
||||
surface->interface.backend = MG_BACKEND_GLES;
|
||||
surface->interface.destroy = mg_gles_surface_destroy;
|
||||
surface->interface.prepare = mg_gles_surface_prepare;
|
||||
surface->interface.present = mg_gles_surface_present;
|
||||
surface->interface.resize = mg_gles_surface_resize;
|
||||
surface->interface.getFrame = mg_gles_surface_get_frame;
|
||||
surface->interface.setFrame = mg_gles_surface_set_frame;
|
||||
surface->interface.getHidden = mg_gles_surface_get_hidden;
|
||||
surface->interface.setHidden = mg_gles_surface_set_hidden;
|
||||
surface->interface.getSize = mg_gles_surface_get_size;
|
||||
surface->interface.getOSResource = mg_gles_surface_get_os_resource;
|
||||
|
||||
surface->view = view;
|
||||
|
||||
@autoreleasepool
|
||||
{
|
||||
surface->layer = [[CALayer alloc] init];
|
||||
[surface->layer retain];
|
||||
[surface->layer setBounds:CGRectMake(0, 0, width, height)];
|
||||
|
||||
if(surface->view)
|
||||
{
|
||||
[surface->view setWantsLayer: YES];
|
||||
surface->view.layer = surface->layer;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
EGLAttrib displayAttribs[] = {
|
||||
EGL_PLATFORM_ANGLE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE,
|
||||
//NOTE: we need to explicitly set EGL_PLATFORM_ANGLE_TYPE_ANGLE to EGL_PLATFORM_ANGLE_TYPE_METAL_ANGLE, because
|
||||
// EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE defaults to using CGL, and eglSetSwapInterval is broken for this backend
|
||||
EGL_PLATFORM_ANGLE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_TYPE_METAL_ANGLE,
|
||||
EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_DEVICE_TYPE_HARDWARE_ANGLE,
|
||||
EGL_NONE};
|
||||
|
||||
|
@ -113,7 +141,7 @@ mg_surface mg_gles_surface_create_offscreen(u32 width, u32 height)
|
|||
eglBindAPI(EGL_OPENGL_ES_API);
|
||||
EGLint contextAttributes[] = {
|
||||
EGL_CONTEXT_MAJOR_VERSION_KHR, 3,
|
||||
EGL_CONTEXT_MINOR_VERSION_KHR, 0,
|
||||
EGL_CONTEXT_MINOR_VERSION_KHR, 0, //NOTE: Angle can't create a GLES 3.1 context on macOS
|
||||
EGL_CONTEXT_BIND_GENERATES_RESOURCE_CHROMIUM, EGL_TRUE,
|
||||
EGL_CONTEXT_CLIENT_ARRAYS_ENABLED_ANGLE, EGL_TRUE,
|
||||
EGL_CONTEXT_OPENGL_BACKWARDS_COMPATIBLE_ANGLE, EGL_FALSE,
|
||||
|
@ -122,6 +150,35 @@ mg_surface mg_gles_surface_create_offscreen(u32 width, u32 height)
|
|||
surface->eglContext = eglCreateContext(surface->eglDisplay, surface->eglConfig, EGL_NO_CONTEXT, contextAttributes);
|
||||
eglMakeCurrent(surface->eglDisplay, surface->eglSurface, surface->eglSurface, surface->eglContext);
|
||||
|
||||
mg_surface handle = mg_surface_alloc_handle((mg_surface_info*)surface);
|
||||
eglSwapInterval(surface->eglDisplay, 1);
|
||||
|
||||
mg_surface handle = mg_surface_alloc_handle((mg_surface_data*)surface);
|
||||
return(handle);
|
||||
}
|
||||
|
||||
mg_surface mg_gles_surface_create_offscreen(u32 width, u32 height)
|
||||
{
|
||||
return(mg_gles_surface_create_with_view(width, height, 0));
|
||||
}
|
||||
|
||||
mg_surface mg_gles_surface_create_for_window(mp_window window)
|
||||
{
|
||||
mg_surface res = mg_surface_nil();
|
||||
mp_window_data* windowData = mp_window_ptr_from_handle(window);
|
||||
if(windowData)
|
||||
{
|
||||
@autoreleasepool
|
||||
{
|
||||
NSRect frame = [[windowData->osx.nsWindow contentView] frame];
|
||||
NSView* view = [[NSView alloc] initWithFrame: frame];
|
||||
|
||||
res = mg_gles_surface_create_with_view(frame.size.width, frame.size.height, view);
|
||||
|
||||
if(!mg_surface_is_nil(res))
|
||||
{
|
||||
[[windowData->osx.nsWindow contentView] addSubview: view];
|
||||
}
|
||||
}
|
||||
}
|
||||
return(res);
|
||||
}
|
||||
|
|
|
@ -207,6 +207,8 @@ mg_surface mg_gl_surface_create_for_window(mp_window window)
|
|||
surface->interface.prepare = mg_gl_surface_prepare;
|
||||
surface->interface.present = mg_gl_surface_present;
|
||||
|
||||
//TODO: get/set frame/hidden
|
||||
|
||||
surface->hDC = hDC;
|
||||
surface->glContext = glContext;
|
||||
|
||||
|
|
75
todo.txt
75
todo.txt
|
@ -1,4 +1,25 @@
|
|||
|
||||
[.] Check changes in macos version
|
||||
[x] Restructure macos version to use mp_app_internal.h/mp_app.c
|
||||
[x] test new run loop structure on macos
|
||||
[x] Fix resize crash when there's no surface
|
||||
[>] separate data for key and mouse event?
|
||||
[>] Simplify event structs
|
||||
[ ] use isARepeat in macos keyDown event and simplify update key state
|
||||
|
||||
[x] use surfaces to define restricted drawing locations
|
||||
[x] Implement with NSView subviews on osx
|
||||
[/] Maybe switch to just using CALayers?
|
||||
|
||||
[ ] Cleanup graphics backend compile-time/runtime selection
|
||||
[ ] Cleanup graphics resource handles
|
||||
|
||||
[>>] OpenGL surface on OSX
|
||||
[>>] Port vector graphics to OpenGL on OSX
|
||||
[>] Check OpenGL vector graphics on win32
|
||||
[ ] Implement surfaces with child windows on win32
|
||||
[/] Maybe implement compositing directly in d3d and opengl compat extension...
|
||||
|
||||
Windows port
|
||||
------------
|
||||
[.] Finish events handling
|
||||
|
@ -19,57 +40,6 @@ Windows port
|
|||
(mp_app.c can 'see' platform specific stuff, so ObjectiveC defs pose a problem, but we can define id as void*
|
||||
when not in ObjC...)
|
||||
|
||||
[.] Check changes in macos version
|
||||
[x] Restructure macos version to use mp_app_internal.h/mp_app.c
|
||||
[x] test new run loop structure on macos
|
||||
[x] Fix resize crash when there's no surface
|
||||
[>] separate data for key and mouse event?
|
||||
[>] Simplify event structs
|
||||
[ ] use isARepeat in macos keyDown event and simplify update key state
|
||||
|
||||
[>] Clarify how we want to do view handling across platforms
|
||||
[.] use surfaces to define restricted drawing locations
|
||||
[x] Implement with NSView subviews on osx
|
||||
[/] Maybe switch to just using CALayers?
|
||||
[ ] Clarify metal-specific surface API
|
||||
> right now mg_surface doesn't really give access to directly drawing in metal
|
||||
we only need render encoder from the surface? -> if we just want to issue draw calls.
|
||||
We'll likely need the command buffer if we want other passes? or also have get_compute_encoder().
|
||||
We only need the command buffer/command queue if we want to manually create encoders, eg with different clear color
|
||||
|
||||
-> get pixel format / command buffer / texture
|
||||
(don't get drawable as it is specific to the kind of target (window or image))
|
||||
-> as helper, get simple encoders in the command buffer
|
||||
|
||||
maybe even _just_ get a CAMetalDrawable and pixel format, and let user code create commandQueue, buffers, encoders...
|
||||
-> but we need to present the drawable to a specific commandbuffer
|
||||
-> so, maybe the view should also maintain a command buffer
|
||||
-> pixel format, drawable, command buffer, + helpers for encoders(passes)
|
||||
|
||||
--> but then, mg_surface doesn't really abstract anything (usage code needs to know which kind of surface it is to be able to draw inside it)
|
||||
(well, it abstracts the creation of the context and the presentation...)
|
||||
|
||||
maybe we should actually abstract over that, a la sokol_gfx... And then the 2D vector graphics uses this abstract API?
|
||||
-> but this is a bigger endeavour
|
||||
|
||||
Maybe keep surface simple, write an opengl version, then try to port painter to opengl version,
|
||||
and then we will have more info to decide if we want to abstract over the different APIs.
|
||||
|
||||
Do surfaces for different graphics API even need to be abstracted? don't we always know which surface we're using?
|
||||
|
||||
|
||||
|
||||
->> how much do we abstract graphics API vs how much we give access to native calls...
|
||||
|
||||
ie we could abstract shaders, buffers, textures, pipeline, encoders, etc... but also provide access to native data structures
|
||||
|
||||
|
||||
|
||||
|
||||
[ ] Cleanup graphics backend compile-time/runtime selection
|
||||
[ ] Implement surfaces with child windows on win32
|
||||
[/] Maybe implement compositing directly in d3d and opengl compat extension...
|
||||
|
||||
[.] Implement input polling
|
||||
[ ] Simplify input polling API names
|
||||
[/] Try to simplify input state and polling once we have UI usage code
|
||||
|
@ -88,9 +58,6 @@ Windows port
|
|||
|
||||
[ ] Remove unused APIs
|
||||
|
||||
[ ] OpenGL backend on OSX
|
||||
[ ] Port vector graphics to OpenGL
|
||||
[ ] Check OpenGL vector graphics on windows
|
||||
|
||||
|
||||
Misc
|
||||
|
|
Loading…
Reference in New Issue