end of line renormalization
This commit is contained in:
parent
4359bdaa3c
commit
592f4cdecd
|
@ -1,16 +1,16 @@
|
|||
.DS_Store
|
||||
*.dSYM
|
||||
bin/*
|
||||
*.metallib
|
||||
|
||||
*.pdb
|
||||
*.exe
|
||||
*.ilk
|
||||
*.vs
|
||||
*.obj
|
||||
*.lib
|
||||
*.dll
|
||||
*.sln
|
||||
|
||||
Debug/*
|
||||
.DS_Store
|
||||
*.dSYM
|
||||
bin/*
|
||||
*.metallib
|
||||
|
||||
*.pdb
|
||||
*.exe
|
||||
*.ilk
|
||||
*.vs
|
||||
*.obj
|
||||
*.lib
|
||||
*.dll
|
||||
*.sln
|
||||
|
||||
Debug/*
|
||||
src/glsl_shaders.h
|
22
build.bat
22
build.bat
|
@ -1,11 +1,11 @@
|
|||
|
||||
if not exist bin mkdir bin
|
||||
|
||||
set glsl_shaders=src\glsl_shaders\common.glsl src\glsl_shaders\blit_vertex.glsl src\glsl_shaders\blit_fragment.glsl src\glsl_shaders\clear_counters.glsl src\glsl_shaders\tile.glsl src\glsl_shaders\sort.glsl src\glsl_shaders\draw.glsl
|
||||
|
||||
call python3 scripts\embed_text.py %glsl_shaders% --prefix=glsl_ --output src\glsl_shaders.h
|
||||
|
||||
set INCLUDES=/I src /I src/util /I src/platform /I ext /I ext/angle_headers
|
||||
set LIBS=user32.lib opengl32.lib gdi32.lib shcore.lib delayimp.lib dwmapi.lib /LIBPATH:./bin libEGL.dll.lib libGLESv2.dll.lib /DELAYLOAD:libEGL.dll /DELAYLOAD:libGLESv2.dll
|
||||
|
||||
cl /we4013 /Zi /Zc:preprocessor /DMP_BUILD_DLL /std:c11 %INCLUDES% src/milepost.c /Fo:bin/milepost.o /LD /link %LIBS% /OUT:bin/milepost.dll /IMPLIB:bin/milepost.dll.lib
|
||||
|
||||
if not exist bin mkdir bin
|
||||
|
||||
set glsl_shaders=src\glsl_shaders\common.glsl src\glsl_shaders\blit_vertex.glsl src\glsl_shaders\blit_fragment.glsl src\glsl_shaders\clear_counters.glsl src\glsl_shaders\tile.glsl src\glsl_shaders\sort.glsl src\glsl_shaders\draw.glsl
|
||||
|
||||
call python3 scripts\embed_text.py %glsl_shaders% --prefix=glsl_ --output src\glsl_shaders.h
|
||||
|
||||
set INCLUDES=/I src /I src/util /I src/platform /I ext /I ext/angle_headers
|
||||
set LIBS=user32.lib opengl32.lib gdi32.lib shcore.lib delayimp.lib dwmapi.lib /LIBPATH:./bin libEGL.dll.lib libGLESv2.dll.lib /DELAYLOAD:libEGL.dll /DELAYLOAD:libGLESv2.dll
|
||||
|
||||
cl /we4013 /Zi /Zc:preprocessor /DMP_BUILD_DLL /std:c11 %INCLUDES% src/milepost.c /Fo:bin/milepost.o /LD /link %LIBS% /OUT:bin/milepost.dll /IMPLIB:bin/milepost.dll.lib
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
|
||||
set INCLUDES=/I ..\..\src /I ..\..\src\util /I ..\..\src\platform /I ../../ext /I ../../ext/angle_headers
|
||||
|
||||
cl /we4013 /Zi /Zc:preprocessor /std:c11 %INCLUDES% main.c /link /LIBPATH:../../bin milepost.dll.lib /out:../../bin/example_canvas.exe
|
||||
|
||||
set INCLUDES=/I ..\..\src /I ..\..\src\util /I ..\..\src\platform /I ../../ext /I ../../ext/angle_headers
|
||||
|
||||
cl /we4013 /Zi /Zc:preprocessor /std:c11 %INCLUDES% main.c /link /LIBPATH:../../bin milepost.dll.lib /out:../../bin/example_canvas.exe
|
||||
|
|
|
@ -1,223 +1,223 @@
|
|||
/************************************************************//**
|
||||
*
|
||||
* @file: main.cpp
|
||||
* @author: Martin Fouilleul
|
||||
* @date: 30/07/2022
|
||||
* @revision:
|
||||
*
|
||||
*****************************************************************/
|
||||
#include<stdlib.h>
|
||||
#include<string.h>
|
||||
#include<errno.h>
|
||||
|
||||
#define _USE_MATH_DEFINES //NOTE: necessary for MSVC
|
||||
#include<math.h>
|
||||
|
||||
#include"milepost.h"
|
||||
|
||||
#define LOG_SUBSYSTEM "Main"
|
||||
|
||||
|
||||
mg_font create_font()
|
||||
{
|
||||
//NOTE(martin): create font
|
||||
str8 fontPath = mp_app_get_resource_path(mem_scratch(), "../resources/OpenSansLatinSubset.ttf");
|
||||
char* fontPathCString = str8_to_cstring(mem_scratch(), fontPath);
|
||||
|
||||
FILE* fontFile = fopen(fontPathCString, "r");
|
||||
if(!fontFile)
|
||||
{
|
||||
LOG_ERROR("Could not load font file '%s': %s\n", fontPathCString, strerror(errno));
|
||||
return(mg_font_nil());
|
||||
}
|
||||
unsigned char* fontData = 0;
|
||||
fseek(fontFile, 0, SEEK_END);
|
||||
u32 fontDataSize = ftell(fontFile);
|
||||
rewind(fontFile);
|
||||
fontData = (unsigned char*)malloc(fontDataSize);
|
||||
fread(fontData, 1, fontDataSize, fontFile);
|
||||
fclose(fontFile);
|
||||
|
||||
unicode_range ranges[5] = {UNICODE_RANGE_BASIC_LATIN,
|
||||
UNICODE_RANGE_C1_CONTROLS_AND_LATIN_1_SUPPLEMENT,
|
||||
UNICODE_RANGE_LATIN_EXTENDED_A,
|
||||
UNICODE_RANGE_LATIN_EXTENDED_B,
|
||||
UNICODE_RANGE_SPECIALS};
|
||||
|
||||
mg_font font = mg_font_create_from_memory(fontDataSize, fontData, 5, ranges);
|
||||
free(fontData);
|
||||
|
||||
return(font);
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
LogLevel(LOG_LEVEL_WARNING);
|
||||
|
||||
mp_init();
|
||||
mp_clock_init(); //TODO put that in mp_init()?
|
||||
|
||||
mp_rect windowRect = {.x = 100, .y = 100, .w = 810, .h = 610};
|
||||
mp_window window = mp_window_create(windowRect, "test", 0);
|
||||
|
||||
mp_rect contentRect = mp_window_get_content_rect(window);
|
||||
|
||||
//NOTE: create surface
|
||||
mg_surface surface = mg_surface_create_for_window(window, MG_BACKEND_DEFAULT);
|
||||
mg_surface_swap_interval(surface, 0);
|
||||
|
||||
//TODO: create canvas
|
||||
mg_canvas canvas = mg_canvas_create(surface);
|
||||
|
||||
if(mg_canvas_is_nil(canvas))
|
||||
{
|
||||
printf("Error: couldn't create canvas\n");
|
||||
return(-1);
|
||||
}
|
||||
|
||||
mg_font font = create_font();
|
||||
|
||||
// start app
|
||||
mp_window_bring_to_front(window);
|
||||
mp_window_focus(window);
|
||||
|
||||
f32 x = 400, y = 300;
|
||||
f32 speed = 0;
|
||||
f32 dx = speed, dy = speed;
|
||||
f64 frameTime = 0;
|
||||
|
||||
while(!mp_should_quit())
|
||||
{
|
||||
f64 startTime = mp_get_time(MP_CLOCK_MONOTONIC);
|
||||
|
||||
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_KEYBOARD_KEY:
|
||||
{
|
||||
if(event.key.action == MP_KEY_PRESS || event.key.action == MP_KEY_REPEAT)
|
||||
{
|
||||
if(event.key.code == MP_KEY_LEFT)
|
||||
{
|
||||
if(x - 200 > 0)
|
||||
{
|
||||
x-=5;
|
||||
}
|
||||
}
|
||||
else if(event.key.code == MP_KEY_RIGHT)
|
||||
{
|
||||
if(x + 200 < contentRect.w)
|
||||
{
|
||||
x+=5;
|
||||
}
|
||||
}
|
||||
else if(event.key.code == MP_KEY_UP)
|
||||
{
|
||||
if(y + 200 < contentRect.h)
|
||||
{
|
||||
y+=5;
|
||||
}
|
||||
}
|
||||
else if(event.key.code == MP_KEY_DOWN)
|
||||
{
|
||||
if(y - 200 > 0)
|
||||
{
|
||||
y-=5;
|
||||
}
|
||||
}
|
||||
//*/
|
||||
}
|
||||
} break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(x-200 < 0)
|
||||
{
|
||||
x = 200;
|
||||
dx = speed;
|
||||
}
|
||||
if(x+200 > contentRect.w)
|
||||
{
|
||||
x = contentRect.w - 200;
|
||||
dx = -speed;
|
||||
}
|
||||
if(y-200 < 0)
|
||||
{
|
||||
y = 200;
|
||||
dy = speed;
|
||||
}
|
||||
if(y+200 > contentRect.h)
|
||||
{
|
||||
y = contentRect.h - 200;
|
||||
dy = -speed;
|
||||
}
|
||||
x += dx;
|
||||
y += dy;
|
||||
|
||||
mg_surface_prepare(surface);
|
||||
|
||||
// background
|
||||
mg_set_color_rgba(0, 1, 1, 1);
|
||||
mg_clear();
|
||||
|
||||
// head
|
||||
mg_set_color_rgba(1, 1, 0, 1);
|
||||
mg_circle_fill(x, y, 200);
|
||||
|
||||
// smile
|
||||
f32 frown = frameTime > 0.033 ? 100 : 0;
|
||||
|
||||
mg_set_color_rgba(0, 0, 0, 1);
|
||||
mg_set_width(20);
|
||||
mg_move_to(x-100, y-100);
|
||||
mg_cubic_to(x-50, y-150+frown, x+50, y-150+frown, x+100, y-100);
|
||||
mg_stroke();
|
||||
|
||||
// eyes
|
||||
mg_ellipse_fill(x-70, y+50, 30, 50);
|
||||
mg_ellipse_fill(x+70, y+50, 30, 50);
|
||||
|
||||
// text
|
||||
mg_set_color_rgba(0, 0, 1, 1);
|
||||
mg_set_font(font);
|
||||
mg_set_font_size(12);
|
||||
mg_move_to(50, 50);
|
||||
|
||||
str8 text = str8_pushf(mem_scratch(),
|
||||
"Milepost vector graphics test program (frame time = %fs, fps = %f)...",
|
||||
frameTime,
|
||||
1./frameTime);
|
||||
mg_text_outlines(text);
|
||||
mg_fill();
|
||||
|
||||
printf("Milepost vector graphics test program (frame time = %fs, fps = %f)...\n",
|
||||
frameTime,
|
||||
1./frameTime);
|
||||
|
||||
mg_flush();
|
||||
mg_surface_present(surface);
|
||||
|
||||
mem_arena_clear(mem_scratch());
|
||||
frameTime = mp_get_time(MP_CLOCK_MONOTONIC) - startTime;
|
||||
}
|
||||
|
||||
mg_font_destroy(font);
|
||||
mg_canvas_destroy(canvas);
|
||||
mg_surface_destroy(surface);
|
||||
mp_window_destroy(window);
|
||||
|
||||
mp_terminate();
|
||||
|
||||
return(0);
|
||||
}
|
||||
/************************************************************//**
|
||||
*
|
||||
* @file: main.cpp
|
||||
* @author: Martin Fouilleul
|
||||
* @date: 30/07/2022
|
||||
* @revision:
|
||||
*
|
||||
*****************************************************************/
|
||||
#include<stdlib.h>
|
||||
#include<string.h>
|
||||
#include<errno.h>
|
||||
|
||||
#define _USE_MATH_DEFINES //NOTE: necessary for MSVC
|
||||
#include<math.h>
|
||||
|
||||
#include"milepost.h"
|
||||
|
||||
#define LOG_SUBSYSTEM "Main"
|
||||
|
||||
|
||||
mg_font create_font()
|
||||
{
|
||||
//NOTE(martin): create font
|
||||
str8 fontPath = mp_app_get_resource_path(mem_scratch(), "../resources/OpenSansLatinSubset.ttf");
|
||||
char* fontPathCString = str8_to_cstring(mem_scratch(), fontPath);
|
||||
|
||||
FILE* fontFile = fopen(fontPathCString, "r");
|
||||
if(!fontFile)
|
||||
{
|
||||
LOG_ERROR("Could not load font file '%s': %s\n", fontPathCString, strerror(errno));
|
||||
return(mg_font_nil());
|
||||
}
|
||||
unsigned char* fontData = 0;
|
||||
fseek(fontFile, 0, SEEK_END);
|
||||
u32 fontDataSize = ftell(fontFile);
|
||||
rewind(fontFile);
|
||||
fontData = (unsigned char*)malloc(fontDataSize);
|
||||
fread(fontData, 1, fontDataSize, fontFile);
|
||||
fclose(fontFile);
|
||||
|
||||
unicode_range ranges[5] = {UNICODE_RANGE_BASIC_LATIN,
|
||||
UNICODE_RANGE_C1_CONTROLS_AND_LATIN_1_SUPPLEMENT,
|
||||
UNICODE_RANGE_LATIN_EXTENDED_A,
|
||||
UNICODE_RANGE_LATIN_EXTENDED_B,
|
||||
UNICODE_RANGE_SPECIALS};
|
||||
|
||||
mg_font font = mg_font_create_from_memory(fontDataSize, fontData, 5, ranges);
|
||||
free(fontData);
|
||||
|
||||
return(font);
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
LogLevel(LOG_LEVEL_WARNING);
|
||||
|
||||
mp_init();
|
||||
mp_clock_init(); //TODO put that in mp_init()?
|
||||
|
||||
mp_rect windowRect = {.x = 100, .y = 100, .w = 810, .h = 610};
|
||||
mp_window window = mp_window_create(windowRect, "test", 0);
|
||||
|
||||
mp_rect contentRect = mp_window_get_content_rect(window);
|
||||
|
||||
//NOTE: create surface
|
||||
mg_surface surface = mg_surface_create_for_window(window, MG_BACKEND_DEFAULT);
|
||||
mg_surface_swap_interval(surface, 0);
|
||||
|
||||
//TODO: create canvas
|
||||
mg_canvas canvas = mg_canvas_create(surface);
|
||||
|
||||
if(mg_canvas_is_nil(canvas))
|
||||
{
|
||||
printf("Error: couldn't create canvas\n");
|
||||
return(-1);
|
||||
}
|
||||
|
||||
mg_font font = create_font();
|
||||
|
||||
// start app
|
||||
mp_window_bring_to_front(window);
|
||||
mp_window_focus(window);
|
||||
|
||||
f32 x = 400, y = 300;
|
||||
f32 speed = 0;
|
||||
f32 dx = speed, dy = speed;
|
||||
f64 frameTime = 0;
|
||||
|
||||
while(!mp_should_quit())
|
||||
{
|
||||
f64 startTime = mp_get_time(MP_CLOCK_MONOTONIC);
|
||||
|
||||
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_KEYBOARD_KEY:
|
||||
{
|
||||
if(event.key.action == MP_KEY_PRESS || event.key.action == MP_KEY_REPEAT)
|
||||
{
|
||||
if(event.key.code == MP_KEY_LEFT)
|
||||
{
|
||||
if(x - 200 > 0)
|
||||
{
|
||||
x-=5;
|
||||
}
|
||||
}
|
||||
else if(event.key.code == MP_KEY_RIGHT)
|
||||
{
|
||||
if(x + 200 < contentRect.w)
|
||||
{
|
||||
x+=5;
|
||||
}
|
||||
}
|
||||
else if(event.key.code == MP_KEY_UP)
|
||||
{
|
||||
if(y + 200 < contentRect.h)
|
||||
{
|
||||
y+=5;
|
||||
}
|
||||
}
|
||||
else if(event.key.code == MP_KEY_DOWN)
|
||||
{
|
||||
if(y - 200 > 0)
|
||||
{
|
||||
y-=5;
|
||||
}
|
||||
}
|
||||
//*/
|
||||
}
|
||||
} break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(x-200 < 0)
|
||||
{
|
||||
x = 200;
|
||||
dx = speed;
|
||||
}
|
||||
if(x+200 > contentRect.w)
|
||||
{
|
||||
x = contentRect.w - 200;
|
||||
dx = -speed;
|
||||
}
|
||||
if(y-200 < 0)
|
||||
{
|
||||
y = 200;
|
||||
dy = speed;
|
||||
}
|
||||
if(y+200 > contentRect.h)
|
||||
{
|
||||
y = contentRect.h - 200;
|
||||
dy = -speed;
|
||||
}
|
||||
x += dx;
|
||||
y += dy;
|
||||
|
||||
mg_surface_prepare(surface);
|
||||
|
||||
// background
|
||||
mg_set_color_rgba(0, 1, 1, 1);
|
||||
mg_clear();
|
||||
|
||||
// head
|
||||
mg_set_color_rgba(1, 1, 0, 1);
|
||||
mg_circle_fill(x, y, 200);
|
||||
|
||||
// smile
|
||||
f32 frown = frameTime > 0.033 ? 100 : 0;
|
||||
|
||||
mg_set_color_rgba(0, 0, 0, 1);
|
||||
mg_set_width(20);
|
||||
mg_move_to(x-100, y-100);
|
||||
mg_cubic_to(x-50, y-150+frown, x+50, y-150+frown, x+100, y-100);
|
||||
mg_stroke();
|
||||
|
||||
// eyes
|
||||
mg_ellipse_fill(x-70, y+50, 30, 50);
|
||||
mg_ellipse_fill(x+70, y+50, 30, 50);
|
||||
|
||||
// text
|
||||
mg_set_color_rgba(0, 0, 1, 1);
|
||||
mg_set_font(font);
|
||||
mg_set_font_size(12);
|
||||
mg_move_to(50, 50);
|
||||
|
||||
str8 text = str8_pushf(mem_scratch(),
|
||||
"Milepost vector graphics test program (frame time = %fs, fps = %f)...",
|
||||
frameTime,
|
||||
1./frameTime);
|
||||
mg_text_outlines(text);
|
||||
mg_fill();
|
||||
|
||||
printf("Milepost vector graphics test program (frame time = %fs, fps = %f)...\n",
|
||||
frameTime,
|
||||
1./frameTime);
|
||||
|
||||
mg_flush();
|
||||
mg_surface_present(surface);
|
||||
|
||||
mem_arena_clear(mem_scratch());
|
||||
frameTime = mp_get_time(MP_CLOCK_MONOTONIC) - startTime;
|
||||
}
|
||||
|
||||
mg_font_destroy(font);
|
||||
mg_canvas_destroy(canvas);
|
||||
mg_surface_destroy(surface);
|
||||
mp_window_destroy(window);
|
||||
|
||||
mp_terminate();
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
|
||||
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.dll.lib /out:../../bin/perf_text.exe
|
||||
|
||||
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.dll.lib /out:../../bin/perf_text.exe
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
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.dll.lib /out:../../bin/example_gl_triangle.exe
|
||||
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.dll.lib /out:../../bin/example_gl_triangle.exe
|
||||
|
|
|
@ -1,165 +1,165 @@
|
|||
/************************************************************//**
|
||||
*
|
||||
* @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>
|
||||
|
||||
#define MG_INCLUDE_GL_API
|
||||
#include"milepost.h"
|
||||
|
||||
#define LOG_SUBSYSTEM "Main"
|
||||
|
||||
unsigned int program;
|
||||
|
||||
const char* vshaderSource =
|
||||
"#version 430\n"
|
||||
"attribute vec4 vPosition;\n"
|
||||
"uniform mat4 transform;\n"
|
||||
"void main()\n"
|
||||
"{\n"
|
||||
" gl_Position = transform*vPosition;\n"
|
||||
"}\n";
|
||||
|
||||
const char* fshaderSource =
|
||||
"#version 430\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_surface_create_for_window(window, MG_BACKEND_GL);
|
||||
|
||||
//NOTE: init shader and gl state
|
||||
mg_surface_prepare(surface);
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
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);
|
||||
}
|
||||
/************************************************************//**
|
||||
*
|
||||
* @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>
|
||||
|
||||
#define MG_INCLUDE_GL_API
|
||||
#include"milepost.h"
|
||||
|
||||
#define LOG_SUBSYSTEM "Main"
|
||||
|
||||
unsigned int program;
|
||||
|
||||
const char* vshaderSource =
|
||||
"#version 430\n"
|
||||
"attribute vec4 vPosition;\n"
|
||||
"uniform mat4 transform;\n"
|
||||
"void main()\n"
|
||||
"{\n"
|
||||
" gl_Position = transform*vPosition;\n"
|
||||
"}\n";
|
||||
|
||||
const char* fshaderSource =
|
||||
"#version 430\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_surface_create_for_window(window, MG_BACKEND_GL);
|
||||
|
||||
//NOTE: init shader and gl state
|
||||
mg_surface_prepare(surface);
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
set INCLUDES=/I ..\..\src /I ..\..\src\util /I ..\..\src\platform /I ../../ext /I ../../ext/angle_headers
|
||||
|
||||
cl /we4013 /Zi /Zc:preprocessor /std:c11 %INCLUDES% main.c /link /LIBPATH:../../bin milepost.dll.lib /out:../../bin/example_gles_triangle.exe
|
||||
set INCLUDES=/I ..\..\src /I ..\..\src\util /I ..\..\src\platform /I ../../ext /I ../../ext/angle_headers
|
||||
|
||||
cl /we4013 /Zi /Zc:preprocessor /std:c11 %INCLUDES% main.c /link /LIBPATH:../../bin milepost.dll.lib /out:../../bin/example_gles_triangle.exe
|
||||
|
|
|
@ -1,164 +1,164 @@
|
|||
/************************************************************//**
|
||||
*
|
||||
* @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>
|
||||
|
||||
#define MG_INCLUDE_GL_API 1
|
||||
#include"milepost.h"
|
||||
|
||||
#define LOG_SUBSYSTEM "Main"
|
||||
|
||||
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_surface_create_for_window(window, MG_BACKEND_GLES);
|
||||
mg_surface_prepare(surface);
|
||||
|
||||
//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;
|
||||
|
||||
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;
|
||||
|
||||
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);
|
||||
}
|
||||
/************************************************************//**
|
||||
*
|
||||
* @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>
|
||||
|
||||
#define MG_INCLUDE_GL_API 1
|
||||
#include"milepost.h"
|
||||
|
||||
#define LOG_SUBSYSTEM "Main"
|
||||
|
||||
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_surface_create_for_window(window, MG_BACKEND_GLES);
|
||||
mg_surface_prepare(surface);
|
||||
|
||||
//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;
|
||||
|
||||
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;
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -1,44 +1,44 @@
|
|||
angle install on windows
|
||||
|
||||
* need Python3 (can install through win app store)
|
||||
* need Windows SDK
|
||||
* clone depot_tools git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git
|
||||
or download and unzip bundle at https://storage.googleapis.com/chrome-infra/depot_tools.zip
|
||||
* set depot_tools in path env variable through control panel>System and security>system>advanced system settings
|
||||
* run gclient in a cmd shell
|
||||
|
||||
* set DEPOT_TOOLS_WIN_TOOLCHAIN=0
|
||||
* mkdir angle
|
||||
* cd angle
|
||||
* fetch angle
|
||||
* wait a million years
|
||||
|
||||
* fails when running python3 third_party/depot_tools/download_from_google_storage.py ...
|
||||
-> open DEPS and change third_party/depot_tools with ../depot/tools
|
||||
|
||||
* run gclient sync to complete previous step
|
||||
|
||||
* gn gen out/Debug
|
||||
* gn args out/Debug and edit arguments:
|
||||
angle_enable_vulkan = false
|
||||
angle_build_tests = false
|
||||
is_component_build = false
|
||||
|
||||
#to get debugging kinda working with renderdoc:
|
||||
angle_enable_trace = true
|
||||
angle_enable_annotator_run_time_checks = true
|
||||
|
||||
|
||||
* autoninja -C out/Debug
|
||||
* wait a while
|
||||
|
||||
* link with libEGL.dll.lib and libGLESv2.dll.lib
|
||||
* put libEGL.dll and libGLESv2.dll in same directory as executable
|
||||
|
||||
Debugging
|
||||
---------
|
||||
in renderdoc, set env variables
|
||||
RENDERDOC_HOOK_EGL 0 (if you want to trace underlying native API)
|
||||
RENDERDOC_HOOK_EGL 1 (if you want to trace EGL calls. You also need to put libEGL in the renderdoc folder so it's found when capturing stuff. Unfortunately though, that seems to provoke crashes...)
|
||||
|
||||
ANGLE_ENABLE_DEBUG_MARKERS 1 (to turn on debug markers)
|
||||
angle install on windows
|
||||
|
||||
* need Python3 (can install through win app store)
|
||||
* need Windows SDK
|
||||
* clone depot_tools git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git
|
||||
or download and unzip bundle at https://storage.googleapis.com/chrome-infra/depot_tools.zip
|
||||
* set depot_tools in path env variable through control panel>System and security>system>advanced system settings
|
||||
* run gclient in a cmd shell
|
||||
|
||||
* set DEPOT_TOOLS_WIN_TOOLCHAIN=0
|
||||
* mkdir angle
|
||||
* cd angle
|
||||
* fetch angle
|
||||
* wait a million years
|
||||
|
||||
* fails when running python3 third_party/depot_tools/download_from_google_storage.py ...
|
||||
-> open DEPS and change third_party/depot_tools with ../depot/tools
|
||||
|
||||
* run gclient sync to complete previous step
|
||||
|
||||
* gn gen out/Debug
|
||||
* gn args out/Debug and edit arguments:
|
||||
angle_enable_vulkan = false
|
||||
angle_build_tests = false
|
||||
is_component_build = false
|
||||
|
||||
#to get debugging kinda working with renderdoc:
|
||||
angle_enable_trace = true
|
||||
angle_enable_annotator_run_time_checks = true
|
||||
|
||||
|
||||
* autoninja -C out/Debug
|
||||
* wait a while
|
||||
|
||||
* link with libEGL.dll.lib and libGLESv2.dll.lib
|
||||
* put libEGL.dll and libGLESv2.dll in same directory as executable
|
||||
|
||||
Debugging
|
||||
---------
|
||||
in renderdoc, set env variables
|
||||
RENDERDOC_HOOK_EGL 0 (if you want to trace underlying native API)
|
||||
RENDERDOC_HOOK_EGL 1 (if you want to trace EGL calls. You also need to put libEGL in the renderdoc folder so it's found when capturing stuff. Unfortunately though, that seems to provoke crashes...)
|
||||
|
||||
ANGLE_ENABLE_DEBUG_MARKERS 1 (to turn on debug markers)
|
||||
|
|
14880
ext/stb_image.h
14880
ext/stb_image.h
File diff suppressed because it is too large
Load Diff
10022
ext/stb_truetype.h
10022
ext/stb_truetype.h
File diff suppressed because it is too large
Load Diff
|
@ -1,44 +1,44 @@
|
|||
import os
|
||||
from datetime import datetime
|
||||
from argparse import ArgumentParser
|
||||
|
||||
parser = ArgumentParser()
|
||||
parser.add_argument("inputFiles", nargs="+")
|
||||
parser.add_argument("-o", "--output")
|
||||
parser.add_argument("-p", "--prefix")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
output = open(args.output, "w")
|
||||
output.write("/*********************************************************************\n")
|
||||
output.write("*\n")
|
||||
output.write("*\tfile: %s\n" % os.path.basename(args.output))
|
||||
output.write("*\tnote: string literals auto-generated by embed_text.py\n")
|
||||
output.write("*\tdate: %s\n" % datetime.now().strftime("%d/%m%Y"))
|
||||
output.write("*\n")
|
||||
output.write("**********************************************************************/\n")
|
||||
|
||||
outSymbol = (os.path.splitext(os.path.basename(args.output))[0]).upper()
|
||||
|
||||
output.write("#ifndef __%s_H__\n" % outSymbol)
|
||||
output.write("#define __%s_H__\n" % outSymbol)
|
||||
output.write("\n\n")
|
||||
|
||||
for fileName in args.inputFiles:
|
||||
f = open(fileName, "r")
|
||||
lines = f.read().splitlines()
|
||||
|
||||
output.write("//NOTE: string imported from %s\n" % fileName)
|
||||
|
||||
stringName = os.path.splitext(os.path.basename(fileName))[0]
|
||||
output.write(f"const char* {args.prefix}{stringName} = ")
|
||||
|
||||
for line in lines:
|
||||
output.write("\n\"%s\\n\"" % line)
|
||||
|
||||
output.write(";\n\n")
|
||||
f.close()
|
||||
|
||||
output.write("#endif // __%s_H__\n" % outSymbol)
|
||||
|
||||
output.close()
|
||||
import os
|
||||
from datetime import datetime
|
||||
from argparse import ArgumentParser
|
||||
|
||||
parser = ArgumentParser()
|
||||
parser.add_argument("inputFiles", nargs="+")
|
||||
parser.add_argument("-o", "--output")
|
||||
parser.add_argument("-p", "--prefix")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
output = open(args.output, "w")
|
||||
output.write("/*********************************************************************\n")
|
||||
output.write("*\n")
|
||||
output.write("*\tfile: %s\n" % os.path.basename(args.output))
|
||||
output.write("*\tnote: string literals auto-generated by embed_text.py\n")
|
||||
output.write("*\tdate: %s\n" % datetime.now().strftime("%d/%m%Y"))
|
||||
output.write("*\n")
|
||||
output.write("**********************************************************************/\n")
|
||||
|
||||
outSymbol = (os.path.splitext(os.path.basename(args.output))[0]).upper()
|
||||
|
||||
output.write("#ifndef __%s_H__\n" % outSymbol)
|
||||
output.write("#define __%s_H__\n" % outSymbol)
|
||||
output.write("\n\n")
|
||||
|
||||
for fileName in args.inputFiles:
|
||||
f = open(fileName, "r")
|
||||
lines = f.read().splitlines()
|
||||
|
||||
output.write("//NOTE: string imported from %s\n" % fileName)
|
||||
|
||||
stringName = os.path.splitext(os.path.basename(fileName))[0]
|
||||
output.write(f"const char* {args.prefix}{stringName} = ")
|
||||
|
||||
for line in lines:
|
||||
output.write("\n\"%s\\n\"" % line)
|
||||
|
||||
output.write(";\n\n")
|
||||
f.close()
|
||||
|
||||
output.write("#endif // __%s_H__\n" % outSymbol)
|
||||
|
||||
output.close()
|
||||
|
|
|
@ -1,175 +1,175 @@
|
|||
/************************************************************//**
|
||||
*
|
||||
* @file: egl_surface.cpp
|
||||
* @author: Martin Fouilleul
|
||||
* @date: 17/02/2023
|
||||
* @revision:
|
||||
*
|
||||
*****************************************************************/
|
||||
|
||||
#define EGL_EGLEXT_PROTOTYPES
|
||||
#include<EGL/egl.h>
|
||||
#include<EGL/eglext.h>
|
||||
#include"mp_app_internal.h"
|
||||
#include"graphics_internal.h"
|
||||
#include"gl_loader.h"
|
||||
|
||||
#if OS_MACOS
|
||||
//NOTE: EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE on osx defaults to CGL backend, which doesn't handle SwapInterval correctly
|
||||
#define MG_EGL_PLATFORM_ANGLE_TYPE EGL_PLATFORM_ANGLE_TYPE_METAL_ANGLE
|
||||
|
||||
//NOTE: hardcode GLES versions for now
|
||||
//TODO: use version hints, once we have all api versions correctly categorized by glapi.py
|
||||
#define MG_GLES_VERSION_MAJOR 3
|
||||
#define MG_GLES_VERSION_MINOR 0
|
||||
#define mg_gl_load_gles mg_gl_load_gles30
|
||||
#else
|
||||
#define MG_EGL_PLATFORM_ANGLE_TYPE EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE
|
||||
#define MG_GLES_VERSION_MAJOR 3
|
||||
#define MG_GLES_VERSION_MINOR 1
|
||||
#define mg_gl_load_gles mg_gl_load_gles31
|
||||
#endif
|
||||
|
||||
|
||||
typedef struct mg_egl_surface
|
||||
{
|
||||
mg_surface_data interface;
|
||||
|
||||
EGLDisplay eglDisplay;
|
||||
EGLConfig eglConfig;
|
||||
EGLContext eglContext;
|
||||
EGLSurface eglSurface;
|
||||
|
||||
mg_gl_api api;
|
||||
|
||||
} mg_egl_surface;
|
||||
|
||||
void mg_egl_surface_destroy(mg_surface_data* interface)
|
||||
{
|
||||
mg_egl_surface* surface = (mg_egl_surface*)interface;
|
||||
|
||||
if(&surface->api == mg_gl_get_api())
|
||||
{
|
||||
mg_gl_select_api(0);
|
||||
}
|
||||
if(eglGetCurrentContext() == surface->eglContext)
|
||||
{
|
||||
eglMakeCurrent(surface->eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
||||
}
|
||||
eglDestroyContext(surface->eglDisplay, surface->eglContext);
|
||||
eglDestroySurface(surface->eglDisplay, surface->eglSurface);
|
||||
|
||||
mg_surface_cleanup((mg_surface_data*)surface);
|
||||
free(surface);
|
||||
}
|
||||
|
||||
void mg_egl_surface_prepare(mg_surface_data* interface)
|
||||
{
|
||||
mg_egl_surface* surface = (mg_egl_surface*)interface;
|
||||
eglMakeCurrent(surface->eglDisplay, surface->eglSurface, surface->eglSurface, surface->eglContext);
|
||||
mg_gl_select_api(&surface->api);
|
||||
}
|
||||
|
||||
void mg_egl_surface_present(mg_surface_data* interface)
|
||||
{
|
||||
mg_egl_surface* surface = (mg_egl_surface*)interface;
|
||||
eglSwapBuffers(surface->eglDisplay, surface->eglSurface);
|
||||
}
|
||||
|
||||
void mg_egl_surface_swap_interval(mg_surface_data* interface, int swap)
|
||||
{
|
||||
mg_egl_surface* surface = (mg_egl_surface*)interface;
|
||||
eglSwapInterval(surface->eglDisplay, swap);
|
||||
}
|
||||
|
||||
void mg_egl_surface_init(mg_egl_surface* surface)
|
||||
{
|
||||
void* nativeLayer = surface->interface.nativeLayer((mg_surface_data*)surface);
|
||||
|
||||
surface->interface.backend = MG_BACKEND_GLES;
|
||||
|
||||
surface->interface.destroy = mg_egl_surface_destroy;
|
||||
surface->interface.prepare = mg_egl_surface_prepare;
|
||||
surface->interface.present = mg_egl_surface_present;
|
||||
surface->interface.swapInterval = mg_egl_surface_swap_interval;
|
||||
|
||||
EGLAttrib displayAttribs[] = {
|
||||
EGL_PLATFORM_ANGLE_TYPE_ANGLE, MG_EGL_PLATFORM_ANGLE_TYPE,
|
||||
EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_DEVICE_TYPE_HARDWARE_ANGLE,
|
||||
EGL_NONE};
|
||||
|
||||
surface->eglDisplay = eglGetPlatformDisplay(EGL_PLATFORM_ANGLE_ANGLE, (void*)EGL_DEFAULT_DISPLAY, displayAttribs);
|
||||
eglInitialize(surface->eglDisplay, NULL, NULL);
|
||||
|
||||
EGLint const configAttributes[] = {
|
||||
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
|
||||
EGL_RED_SIZE, 8,
|
||||
EGL_GREEN_SIZE, 8,
|
||||
EGL_BLUE_SIZE, 8,
|
||||
EGL_ALPHA_SIZE, 8,
|
||||
EGL_DEPTH_SIZE, 24,
|
||||
EGL_STENCIL_SIZE, 8,
|
||||
EGL_SAMPLE_BUFFERS, 0,
|
||||
EGL_SAMPLES, EGL_DONT_CARE,
|
||||
EGL_COLOR_COMPONENT_TYPE_EXT, EGL_COLOR_COMPONENT_TYPE_FIXED_EXT,
|
||||
EGL_NONE };
|
||||
|
||||
int numConfigs = 0;
|
||||
eglChooseConfig(surface->eglDisplay, configAttributes, &surface->eglConfig, 1, &numConfigs);
|
||||
|
||||
EGLint const surfaceAttributes[] = {EGL_NONE};
|
||||
surface->eglSurface = eglCreateWindowSurface(surface->eglDisplay,
|
||||
surface->eglConfig,
|
||||
nativeLayer,
|
||||
surfaceAttributes);
|
||||
|
||||
eglBindAPI(EGL_OPENGL_ES_API);
|
||||
EGLint contextAttributes[] = {
|
||||
EGL_CONTEXT_MAJOR_VERSION_KHR, MG_GLES_VERSION_MAJOR,
|
||||
EGL_CONTEXT_MINOR_VERSION_KHR, MG_GLES_VERSION_MINOR,
|
||||
EGL_CONTEXT_BIND_GENERATES_RESOURCE_CHROMIUM, EGL_TRUE,
|
||||
EGL_CONTEXT_CLIENT_ARRAYS_ENABLED_ANGLE, EGL_TRUE,
|
||||
EGL_CONTEXT_OPENGL_BACKWARDS_COMPATIBLE_ANGLE, EGL_FALSE,
|
||||
EGL_NONE};
|
||||
|
||||
surface->eglContext = eglCreateContext(surface->eglDisplay, surface->eglConfig, EGL_NO_CONTEXT, contextAttributes);
|
||||
eglMakeCurrent(surface->eglDisplay, surface->eglSurface, surface->eglSurface, surface->eglContext);
|
||||
|
||||
mg_gl_load_gles(&surface->api, (mg_gl_load_proc)eglGetProcAddress);
|
||||
|
||||
eglSwapInterval(surface->eglDisplay, 1);
|
||||
}
|
||||
|
||||
mg_surface_data* mg_egl_surface_create_remote(u32 width, u32 height)
|
||||
{
|
||||
mg_egl_surface* surface = 0;
|
||||
|
||||
surface = malloc_type(mg_egl_surface);
|
||||
if(surface)
|
||||
{
|
||||
memset(surface, 0, sizeof(mg_egl_surface));
|
||||
|
||||
mg_surface_init_remote((mg_surface_data*)surface, width, height);
|
||||
mg_egl_surface_init(surface);
|
||||
}
|
||||
|
||||
return((mg_surface_data*)surface);
|
||||
}
|
||||
|
||||
mg_surface_data* mg_egl_surface_create_for_window(mp_window window)
|
||||
{
|
||||
mg_egl_surface* surface = 0;
|
||||
mp_window_data* windowData = mp_window_ptr_from_handle(window);
|
||||
if(windowData)
|
||||
{
|
||||
surface = malloc_type(mg_egl_surface);
|
||||
if(surface)
|
||||
{
|
||||
memset(surface, 0, sizeof(mg_egl_surface));
|
||||
|
||||
mg_surface_init_for_window((mg_surface_data*)surface, windowData);
|
||||
mg_egl_surface_init(surface);
|
||||
}
|
||||
}
|
||||
return((mg_surface_data*)surface);
|
||||
}
|
||||
/************************************************************//**
|
||||
*
|
||||
* @file: egl_surface.cpp
|
||||
* @author: Martin Fouilleul
|
||||
* @date: 17/02/2023
|
||||
* @revision:
|
||||
*
|
||||
*****************************************************************/
|
||||
|
||||
#define EGL_EGLEXT_PROTOTYPES
|
||||
#include<EGL/egl.h>
|
||||
#include<EGL/eglext.h>
|
||||
#include"mp_app_internal.h"
|
||||
#include"graphics_internal.h"
|
||||
#include"gl_loader.h"
|
||||
|
||||
#if OS_MACOS
|
||||
//NOTE: EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE on osx defaults to CGL backend, which doesn't handle SwapInterval correctly
|
||||
#define MG_EGL_PLATFORM_ANGLE_TYPE EGL_PLATFORM_ANGLE_TYPE_METAL_ANGLE
|
||||
|
||||
//NOTE: hardcode GLES versions for now
|
||||
//TODO: use version hints, once we have all api versions correctly categorized by glapi.py
|
||||
#define MG_GLES_VERSION_MAJOR 3
|
||||
#define MG_GLES_VERSION_MINOR 0
|
||||
#define mg_gl_load_gles mg_gl_load_gles30
|
||||
#else
|
||||
#define MG_EGL_PLATFORM_ANGLE_TYPE EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE
|
||||
#define MG_GLES_VERSION_MAJOR 3
|
||||
#define MG_GLES_VERSION_MINOR 1
|
||||
#define mg_gl_load_gles mg_gl_load_gles31
|
||||
#endif
|
||||
|
||||
|
||||
typedef struct mg_egl_surface
|
||||
{
|
||||
mg_surface_data interface;
|
||||
|
||||
EGLDisplay eglDisplay;
|
||||
EGLConfig eglConfig;
|
||||
EGLContext eglContext;
|
||||
EGLSurface eglSurface;
|
||||
|
||||
mg_gl_api api;
|
||||
|
||||
} mg_egl_surface;
|
||||
|
||||
void mg_egl_surface_destroy(mg_surface_data* interface)
|
||||
{
|
||||
mg_egl_surface* surface = (mg_egl_surface*)interface;
|
||||
|
||||
if(&surface->api == mg_gl_get_api())
|
||||
{
|
||||
mg_gl_select_api(0);
|
||||
}
|
||||
if(eglGetCurrentContext() == surface->eglContext)
|
||||
{
|
||||
eglMakeCurrent(surface->eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
||||
}
|
||||
eglDestroyContext(surface->eglDisplay, surface->eglContext);
|
||||
eglDestroySurface(surface->eglDisplay, surface->eglSurface);
|
||||
|
||||
mg_surface_cleanup((mg_surface_data*)surface);
|
||||
free(surface);
|
||||
}
|
||||
|
||||
void mg_egl_surface_prepare(mg_surface_data* interface)
|
||||
{
|
||||
mg_egl_surface* surface = (mg_egl_surface*)interface;
|
||||
eglMakeCurrent(surface->eglDisplay, surface->eglSurface, surface->eglSurface, surface->eglContext);
|
||||
mg_gl_select_api(&surface->api);
|
||||
}
|
||||
|
||||
void mg_egl_surface_present(mg_surface_data* interface)
|
||||
{
|
||||
mg_egl_surface* surface = (mg_egl_surface*)interface;
|
||||
eglSwapBuffers(surface->eglDisplay, surface->eglSurface);
|
||||
}
|
||||
|
||||
void mg_egl_surface_swap_interval(mg_surface_data* interface, int swap)
|
||||
{
|
||||
mg_egl_surface* surface = (mg_egl_surface*)interface;
|
||||
eglSwapInterval(surface->eglDisplay, swap);
|
||||
}
|
||||
|
||||
void mg_egl_surface_init(mg_egl_surface* surface)
|
||||
{
|
||||
void* nativeLayer = surface->interface.nativeLayer((mg_surface_data*)surface);
|
||||
|
||||
surface->interface.backend = MG_BACKEND_GLES;
|
||||
|
||||
surface->interface.destroy = mg_egl_surface_destroy;
|
||||
surface->interface.prepare = mg_egl_surface_prepare;
|
||||
surface->interface.present = mg_egl_surface_present;
|
||||
surface->interface.swapInterval = mg_egl_surface_swap_interval;
|
||||
|
||||
EGLAttrib displayAttribs[] = {
|
||||
EGL_PLATFORM_ANGLE_TYPE_ANGLE, MG_EGL_PLATFORM_ANGLE_TYPE,
|
||||
EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_DEVICE_TYPE_HARDWARE_ANGLE,
|
||||
EGL_NONE};
|
||||
|
||||
surface->eglDisplay = eglGetPlatformDisplay(EGL_PLATFORM_ANGLE_ANGLE, (void*)EGL_DEFAULT_DISPLAY, displayAttribs);
|
||||
eglInitialize(surface->eglDisplay, NULL, NULL);
|
||||
|
||||
EGLint const configAttributes[] = {
|
||||
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
|
||||
EGL_RED_SIZE, 8,
|
||||
EGL_GREEN_SIZE, 8,
|
||||
EGL_BLUE_SIZE, 8,
|
||||
EGL_ALPHA_SIZE, 8,
|
||||
EGL_DEPTH_SIZE, 24,
|
||||
EGL_STENCIL_SIZE, 8,
|
||||
EGL_SAMPLE_BUFFERS, 0,
|
||||
EGL_SAMPLES, EGL_DONT_CARE,
|
||||
EGL_COLOR_COMPONENT_TYPE_EXT, EGL_COLOR_COMPONENT_TYPE_FIXED_EXT,
|
||||
EGL_NONE };
|
||||
|
||||
int numConfigs = 0;
|
||||
eglChooseConfig(surface->eglDisplay, configAttributes, &surface->eglConfig, 1, &numConfigs);
|
||||
|
||||
EGLint const surfaceAttributes[] = {EGL_NONE};
|
||||
surface->eglSurface = eglCreateWindowSurface(surface->eglDisplay,
|
||||
surface->eglConfig,
|
||||
nativeLayer,
|
||||
surfaceAttributes);
|
||||
|
||||
eglBindAPI(EGL_OPENGL_ES_API);
|
||||
EGLint contextAttributes[] = {
|
||||
EGL_CONTEXT_MAJOR_VERSION_KHR, MG_GLES_VERSION_MAJOR,
|
||||
EGL_CONTEXT_MINOR_VERSION_KHR, MG_GLES_VERSION_MINOR,
|
||||
EGL_CONTEXT_BIND_GENERATES_RESOURCE_CHROMIUM, EGL_TRUE,
|
||||
EGL_CONTEXT_CLIENT_ARRAYS_ENABLED_ANGLE, EGL_TRUE,
|
||||
EGL_CONTEXT_OPENGL_BACKWARDS_COMPATIBLE_ANGLE, EGL_FALSE,
|
||||
EGL_NONE};
|
||||
|
||||
surface->eglContext = eglCreateContext(surface->eglDisplay, surface->eglConfig, EGL_NO_CONTEXT, contextAttributes);
|
||||
eglMakeCurrent(surface->eglDisplay, surface->eglSurface, surface->eglSurface, surface->eglContext);
|
||||
|
||||
mg_gl_load_gles(&surface->api, (mg_gl_load_proc)eglGetProcAddress);
|
||||
|
||||
eglSwapInterval(surface->eglDisplay, 1);
|
||||
}
|
||||
|
||||
mg_surface_data* mg_egl_surface_create_remote(u32 width, u32 height)
|
||||
{
|
||||
mg_egl_surface* surface = 0;
|
||||
|
||||
surface = malloc_type(mg_egl_surface);
|
||||
if(surface)
|
||||
{
|
||||
memset(surface, 0, sizeof(mg_egl_surface));
|
||||
|
||||
mg_surface_init_remote((mg_surface_data*)surface, width, height);
|
||||
mg_egl_surface_init(surface);
|
||||
}
|
||||
|
||||
return((mg_surface_data*)surface);
|
||||
}
|
||||
|
||||
mg_surface_data* mg_egl_surface_create_for_window(mp_window window)
|
||||
{
|
||||
mg_egl_surface* surface = 0;
|
||||
mp_window_data* windowData = mp_window_ptr_from_handle(window);
|
||||
if(windowData)
|
||||
{
|
||||
surface = malloc_type(mg_egl_surface);
|
||||
if(surface)
|
||||
{
|
||||
memset(surface, 0, sizeof(mg_egl_surface));
|
||||
|
||||
mg_surface_init_for_window((mg_surface_data*)surface, windowData);
|
||||
mg_egl_surface_init(surface);
|
||||
}
|
||||
}
|
||||
return((mg_surface_data*)surface);
|
||||
}
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
/************************************************************//**
|
||||
*
|
||||
* @file: egl_surface.h
|
||||
* @author: Martin Fouilleul
|
||||
* @date: 28/01/2023
|
||||
* @revision:
|
||||
*
|
||||
*****************************************************************/
|
||||
#ifndef __EGL_SURFACE_H_
|
||||
#define __EGL_SURFACE_H_
|
||||
|
||||
#include"graphics_internal.h"
|
||||
#include"mp_app.h"
|
||||
|
||||
mg_surface_data* mg_egl_surface_create_for_window(mp_window window);
|
||||
mg_surface_data* mg_egl_surface_create_remote(u32 width, u32 height);
|
||||
|
||||
#endif // __EGL_SURFACE_H_
|
||||
/************************************************************//**
|
||||
*
|
||||
* @file: egl_surface.h
|
||||
* @author: Martin Fouilleul
|
||||
* @date: 28/01/2023
|
||||
* @revision:
|
||||
*
|
||||
*****************************************************************/
|
||||
#ifndef __EGL_SURFACE_H_
|
||||
#define __EGL_SURFACE_H_
|
||||
|
||||
#include"graphics_internal.h"
|
||||
#include"mp_app.h"
|
||||
|
||||
mg_surface_data* mg_egl_surface_create_for_window(mp_window window);
|
||||
mg_surface_data* mg_egl_surface_create_remote(u32 width, u32 height);
|
||||
|
||||
#endif // __EGL_SURFACE_H_
|
||||
|
|
2208
src/gl_api.h
2208
src/gl_api.h
File diff suppressed because it is too large
Load Diff
1006
src/gl_canvas.c
1006
src/gl_canvas.c
File diff suppressed because it is too large
Load Diff
3446
src/gl_loader.c
3446
src/gl_loader.c
File diff suppressed because it is too large
Load Diff
|
@ -1,22 +1,22 @@
|
|||
/********************************************************
|
||||
*
|
||||
* @file: gl_loader.h
|
||||
* @note: auto-generated by glapi.py from gl.xml
|
||||
* @date: 22/022023
|
||||
*
|
||||
*********************************************************/
|
||||
#ifndef __GL_LOADER_H__
|
||||
#define __GL_LOADER_H__
|
||||
|
||||
#include"gl_api.h"
|
||||
|
||||
typedef void*(*mg_gl_load_proc)(const char* name);
|
||||
|
||||
void mg_gl_load_gl41(mg_gl_api* api, mg_gl_load_proc loadProc);
|
||||
void mg_gl_load_gl43(mg_gl_api* api, mg_gl_load_proc loadProc);
|
||||
void mg_gl_load_gles30(mg_gl_api* api, mg_gl_load_proc loadProc);
|
||||
void mg_gl_load_gles31(mg_gl_api* api, mg_gl_load_proc loadProc);
|
||||
|
||||
void mg_gl_select_api(mg_gl_api* api);
|
||||
|
||||
#endif // __GL_LOADER_H__
|
||||
/********************************************************
|
||||
*
|
||||
* @file: gl_loader.h
|
||||
* @note: auto-generated by glapi.py from gl.xml
|
||||
* @date: 22/022023
|
||||
*
|
||||
*********************************************************/
|
||||
#ifndef __GL_LOADER_H__
|
||||
#define __GL_LOADER_H__
|
||||
|
||||
#include"gl_api.h"
|
||||
|
||||
typedef void*(*mg_gl_load_proc)(const char* name);
|
||||
|
||||
void mg_gl_load_gl41(mg_gl_api* api, mg_gl_load_proc loadProc);
|
||||
void mg_gl_load_gl43(mg_gl_api* api, mg_gl_load_proc loadProc);
|
||||
void mg_gl_load_gles30(mg_gl_api* api, mg_gl_load_proc loadProc);
|
||||
void mg_gl_load_gles31(mg_gl_api* api, mg_gl_load_proc loadProc);
|
||||
|
||||
void mg_gl_select_api(mg_gl_api* api);
|
||||
|
||||
#endif // __GL_LOADER_H__
|
||||
|
|
|
@ -1,467 +1,467 @@
|
|||
/*********************************************************************
|
||||
*
|
||||
* file: glsl_shaders.h
|
||||
* note: string literals auto-generated by embed_text.py
|
||||
* date: 03/032023
|
||||
*
|
||||
**********************************************************************/
|
||||
#ifndef __GLSL_SHADERS_H__
|
||||
#define __GLSL_SHADERS_H__
|
||||
|
||||
|
||||
//NOTE: string imported from src\glsl_shaders\common.glsl
|
||||
const char* glsl_common =
|
||||
"\n"
|
||||
"layout(std430) buffer;\n"
|
||||
"\n"
|
||||
"struct vertex {\n"
|
||||
" vec4 cubic;\n"
|
||||
" vec2 pos;\n"
|
||||
" int shapeIndex;\n"
|
||||
"};\n"
|
||||
"\n"
|
||||
"struct shape {\n"
|
||||
" vec4 color;\n"
|
||||
" vec4 clip;\n"
|
||||
" float uvTransform[6];\n"
|
||||
"};\n";
|
||||
|
||||
//NOTE: string imported from src\glsl_shaders\blit_vertex.glsl
|
||||
const char* glsl_blit_vertex =
|
||||
"\n"
|
||||
"precision mediump float;\n"
|
||||
"\n"
|
||||
"out vec2 uv;\n"
|
||||
"\n"
|
||||
"void main()\n"
|
||||
"{\n"
|
||||
" float x = float(((uint(gl_VertexID) + 2u) / 3u)%2u);\n"
|
||||
" float y = float(((uint(gl_VertexID) + 1u) / 3u)%2u);\n"
|
||||
"\n"
|
||||
" gl_Position = vec4(-1.0f + x*2.0f, -1.0f+y*2.0f, 0.0f, 1.0f);\n"
|
||||
" uv = vec2(x, y);\n"
|
||||
"}\n";
|
||||
|
||||
//NOTE: string imported from src\glsl_shaders\blit_fragment.glsl
|
||||
const char* glsl_blit_fragment =
|
||||
"\n"
|
||||
"precision mediump float;\n"
|
||||
"\n"
|
||||
"in vec2 uv;\n"
|
||||
"out vec4 fragColor;\n"
|
||||
"\n"
|
||||
"layout(location=0) uniform sampler2D tex;\n"
|
||||
"\n"
|
||||
"void main()\n"
|
||||
"{\n"
|
||||
" fragColor = texture(tex, uv);\n"
|
||||
"}\n";
|
||||
|
||||
//NOTE: string imported from src\glsl_shaders\clear_counters.glsl
|
||||
const char* glsl_clear_counters =
|
||||
"\n"
|
||||
"layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n"
|
||||
"\n"
|
||||
"precision mediump float;\n"
|
||||
"layout(std430) buffer;\n"
|
||||
"\n"
|
||||
"layout(binding = 0) coherent restrict writeonly buffer tileCounterBufferSSBO {\n"
|
||||
" uint elements[];\n"
|
||||
"} tileCounterBuffer ;\n"
|
||||
"\n"
|
||||
"void main()\n"
|
||||
"{\n"
|
||||
" uint tileIndex = gl_WorkGroupID.x;\n"
|
||||
" tileCounterBuffer.elements[tileIndex] = 0u;\n"
|
||||
"}\n";
|
||||
|
||||
//NOTE: string imported from src\glsl_shaders\tile.glsl
|
||||
const char* glsl_tile =
|
||||
"\n"
|
||||
"layout(local_size_x = 512, local_size_y = 1, local_size_z = 1) in;\n"
|
||||
"\n"
|
||||
"precision mediump float;\n"
|
||||
"\n"
|
||||
"layout(binding = 0) restrict readonly buffer vertexBufferSSBO {\n"
|
||||
" vertex elements[];\n"
|
||||
"} vertexBuffer ;\n"
|
||||
"\n"
|
||||
"layout(binding = 1) restrict readonly buffer shapeBufferSSBO {\n"
|
||||
" shape elements[];\n"
|
||||
"} shapeBuffer ;\n"
|
||||
"\n"
|
||||
"layout(binding = 2) restrict readonly buffer indexBufferSSBO {\n"
|
||||
" uint elements[];\n"
|
||||
"} indexBuffer ;\n"
|
||||
"\n"
|
||||
"layout(binding = 3) coherent restrict buffer tileCounterBufferSSBO {\n"
|
||||
" uint elements[];\n"
|
||||
"} tileCounterBuffer ;\n"
|
||||
"\n"
|
||||
"layout(binding = 4) coherent restrict writeonly buffer tileArrayBufferSSBO {\n"
|
||||
" uint elements[];\n"
|
||||
"} tileArrayBuffer ;\n"
|
||||
"\n"
|
||||
"layout(location = 0) uniform uint indexCount;\n"
|
||||
"layout(location = 1) uniform uvec2 tileCount;\n"
|
||||
"layout(location = 2) uniform uint tileSize;\n"
|
||||
"layout(location = 3) uniform uint tileArraySize;\n"
|
||||
"layout(location = 4) uniform vec2 scaling;\n"
|
||||
"\n"
|
||||
"void main()\n"
|
||||
"{\n"
|
||||
" uint triangleIndex = (gl_WorkGroupID.x*gl_WorkGroupSize.x + gl_LocalInvocationIndex) * 3u;\n"
|
||||
" if(triangleIndex >= indexCount)\n"
|
||||
" {\n"
|
||||
" return;\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" uint i0 = indexBuffer.elements[triangleIndex];\n"
|
||||
" uint i1 = indexBuffer.elements[triangleIndex+1u];\n"
|
||||
" uint i2 = indexBuffer.elements[triangleIndex+2u];\n"
|
||||
"\n"
|
||||
" vec2 p0 = vertexBuffer.elements[i0].pos * scaling;\n"
|
||||
" vec2 p1 = vertexBuffer.elements[i1].pos * scaling;\n"
|
||||
" vec2 p2 = vertexBuffer.elements[i2].pos * scaling;\n"
|
||||
"\n"
|
||||
" int shapeIndex = vertexBuffer.elements[i0].shapeIndex;\n"
|
||||
" vec4 clip = shapeBuffer.elements[shapeIndex].clip * vec4(scaling, scaling);\n"
|
||||
"\n"
|
||||
" vec4 fbox = vec4(max(min(min(p0.x, p1.x), p2.x), clip.x),\n"
|
||||
" max(min(min(p0.y, p1.y), p2.y), clip.y),\n"
|
||||
" min(max(max(p0.x, p1.x), p2.x), clip.z),\n"
|
||||
" min(max(max(p0.y, p1.y), p2.y), clip.w));\n"
|
||||
"\n"
|
||||
" ivec4 box = ivec4(floor(fbox))/int(tileSize);\n"
|
||||
"\n"
|
||||
" //NOTE(martin): it's importat to do the computation with signed int, so that we can have negative xMax/yMax\n"
|
||||
" // otherwise all triangles on the left or below the x/y axis are attributed to tiles on row/column 0.\n"
|
||||
" int xMin = max(0, box.x);\n"
|
||||
" int yMin = max(0, box.y);\n"
|
||||
" int xMax = min(box.z, int(tileCount.x) - 1);\n"
|
||||
" int yMax = min(box.w, int(tileCount.y) - 1);\n"
|
||||
"\n"
|
||||
" for(int y = yMin; y <= yMax; y++)\n"
|
||||
" {\n"
|
||||
" for(int x = xMin ; x <= xMax; x++)\n"
|
||||
" {\n"
|
||||
" uint tileIndex = uint(y)*tileCount.x + uint(x);\n"
|
||||
" uint tileCounter = atomicAdd(tileCounterBuffer.elements[tileIndex], 1u);\n"
|
||||
" if(tileCounter < tileArraySize)\n"
|
||||
" {\n"
|
||||
" tileArrayBuffer.elements[tileArraySize*tileIndex + tileCounter] = triangleIndex;\n"
|
||||
" }\n"
|
||||
" }\n"
|
||||
" }\n"
|
||||
"}\n";
|
||||
|
||||
//NOTE: string imported from src\glsl_shaders\sort.glsl
|
||||
const char* glsl_sort =
|
||||
"\n"
|
||||
"layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n"
|
||||
"\n"
|
||||
"precision mediump float;\n"
|
||||
"\n"
|
||||
"layout(binding = 0) restrict readonly buffer vertexBufferSSBO {\n"
|
||||
" vertex elements[];\n"
|
||||
"} vertexBuffer ;\n"
|
||||
"\n"
|
||||
"layout(binding = 1) restrict readonly buffer shapeBufferSSBO {\n"
|
||||
" shape elements[];\n"
|
||||
"} shapeBuffer ;\n"
|
||||
"\n"
|
||||
"layout(binding = 2) restrict readonly buffer indexBufferSSBO {\n"
|
||||
" uint elements[];\n"
|
||||
"} indexBuffer ;\n"
|
||||
"\n"
|
||||
"layout(binding = 3) coherent readonly restrict buffer tileCounterBufferSSBO {\n"
|
||||
" uint elements[];\n"
|
||||
"} tileCounterBuffer ;\n"
|
||||
"\n"
|
||||
"layout(binding = 4) coherent restrict buffer tileArrayBufferSSBO {\n"
|
||||
" uint elements[];\n"
|
||||
"} tileArrayBuffer ;\n"
|
||||
"\n"
|
||||
"layout(location = 0) uniform uint indexCount;\n"
|
||||
"layout(location = 1) uniform uvec2 tileCount;\n"
|
||||
"layout(location = 2) uniform uint tileSize;\n"
|
||||
"layout(location = 3) uniform uint tileArraySize;\n"
|
||||
"\n"
|
||||
"int get_shape_index(uint tileArrayOffset, uint tileArrayIndex)\n"
|
||||
"{\n"
|
||||
" uint triangleIndex = tileArrayBuffer.elements[tileArrayOffset + tileArrayIndex];\n"
|
||||
" uint i0 = indexBuffer.elements[triangleIndex];\n"
|
||||
" int shapeIndex = vertexBuffer.elements[i0].shapeIndex;\n"
|
||||
" return(shapeIndex);\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"void main()\n"
|
||||
"{\n"
|
||||
" uint tileIndex = gl_WorkGroupID.x;\n"
|
||||
" uint tileArrayOffset = tileArraySize * tileIndex;\n"
|
||||
" uint tileArrayCount = min(tileCounterBuffer.elements[tileIndex], tileArraySize);\n"
|
||||
"\n"
|
||||
" for(uint tileArrayIndex=1u; tileArrayIndex < tileArrayCount; tileArrayIndex++)\n"
|
||||
" {\n"
|
||||
" for(uint sortIndex = tileArrayIndex; sortIndex > 0u; sortIndex--)\n"
|
||||
" {\n"
|
||||
" int shapeIndex = get_shape_index(tileArrayOffset, sortIndex);\n"
|
||||
" int prevShapeIndex = get_shape_index(tileArrayOffset, sortIndex-1u);\n"
|
||||
"\n"
|
||||
" if(shapeIndex >= prevShapeIndex)\n"
|
||||
" {\n"
|
||||
" break;\n"
|
||||
" }\n"
|
||||
" uint tmp = tileArrayBuffer.elements[tileArrayOffset + sortIndex];\n"
|
||||
" tileArrayBuffer.elements[tileArrayOffset + sortIndex] = tileArrayBuffer.elements[tileArrayOffset + sortIndex - 1u];\n"
|
||||
" tileArrayBuffer.elements[tileArrayOffset + sortIndex - 1u] = tmp;\n"
|
||||
" }\n"
|
||||
" }\n"
|
||||
"}\n";
|
||||
|
||||
//NOTE: string imported from src\glsl_shaders\draw.glsl
|
||||
const char* glsl_draw =
|
||||
"\n"
|
||||
"#extension GL_ARB_gpu_shader_int64 : require\n"
|
||||
"layout(local_size_x = 16, local_size_y = 16, local_size_z = 1) in;\n"
|
||||
"\n"
|
||||
"precision mediump float;\n"
|
||||
"//precision mediump image2D;\n"
|
||||
"\n"
|
||||
"layout(binding = 0) restrict readonly buffer vertexBufferSSBO {\n"
|
||||
" vertex elements[];\n"
|
||||
"} vertexBuffer ;\n"
|
||||
"\n"
|
||||
"layout(binding = 1) restrict readonly buffer shapeBufferSSBO {\n"
|
||||
" shape elements[];\n"
|
||||
"} shapeBuffer ;\n"
|
||||
"\n"
|
||||
"layout(binding = 2) restrict readonly buffer indexBufferSSBO {\n"
|
||||
" uint elements[];\n"
|
||||
"} indexBuffer ;\n"
|
||||
"\n"
|
||||
"layout(binding = 3) restrict readonly buffer tileCounterBufferSSBO {\n"
|
||||
" uint elements[];\n"
|
||||
"} tileCounterBuffer ;\n"
|
||||
"\n"
|
||||
"layout(binding = 4) restrict readonly buffer tileArrayBufferSSBO {\n"
|
||||
" uint elements[];\n"
|
||||
"} tileArrayBuffer ;\n"
|
||||
"\n"
|
||||
"layout(location = 0) uniform uint indexCount;\n"
|
||||
"layout(location = 1) uniform uvec2 tileCount;\n"
|
||||
"layout(location = 2) uniform uint tileSize;\n"
|
||||
"layout(location = 3) uniform uint tileArraySize;\n"
|
||||
"layout(location = 4) uniform vec2 scaling;\n"
|
||||
"layout(location = 5) uniform uint useTexture;\n"
|
||||
"\n"
|
||||
"layout(rgba8, binding = 0) uniform restrict writeonly image2D outTexture;\n"
|
||||
"\n"
|
||||
"layout(binding = 1) uniform sampler2D srcTexture;\n"
|
||||
"\n"
|
||||
"\n"
|
||||
"bool is_top_left(ivec2 a, ivec2 b)\n"
|
||||
"{\n"
|
||||
" return( (a.y == b.y && b.x < a.x)\n"
|
||||
" ||(b.y < a.y));\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"//////////////////////////////////////////////////////////////////////////////\n"
|
||||
"//TODO: we should do these computations on 64bits, because otherwise\n"
|
||||
"// we might overflow for values > 2048.\n"
|
||||
"// Unfortunately this is costly.\n"
|
||||
"// Another way is to precompute triangle edges (b - a) in full precision\n"
|
||||
"// once to avoid doing it all the time...\n"
|
||||
"//////////////////////////////////////////////////////////////////////////////\n"
|
||||
"int orient2d(ivec2 a, ivec2 b, ivec2 p)\n"
|
||||
"{\n"
|
||||
" return((b.x-a.x)*(p.y-a.y) - (b.y-a.y)*(p.x-a.x));\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"int is_clockwise(ivec2 p0, ivec2 p1, ivec2 p2)\n"
|
||||
"{\n"
|
||||
" return((p1 - p0).x*(p2 - p0).y - (p1 - p0).y*(p2 - p0).x);\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"void main()\n"
|
||||
"{\n"
|
||||
" ivec2 pixelCoord = ivec2(gl_WorkGroupID.xy*uvec2(16, 16) + gl_LocalInvocationID.xy);\n"
|
||||
" uvec2 tileCoord = uvec2(pixelCoord) / tileSize;\n"
|
||||
" uint tileIndex = tileCoord.y * tileCount.x + tileCoord.x;\n"
|
||||
" uint tileCounter = min(tileCounterBuffer.elements[tileIndex], tileArraySize);\n"
|
||||
"\n"
|
||||
" const float subPixelFactor = 16.;\n"
|
||||
" ivec2 centerPoint = ivec2((vec2(pixelCoord) + vec2(0.5, 0.5)) * subPixelFactor);\n"
|
||||
"\n"
|
||||
"//*\n"
|
||||
" const int sampleCount = 8;\n"
|
||||
" ivec2 samplePoints[sampleCount] = ivec2[sampleCount](centerPoint + ivec2(1, 3),\n"
|
||||
" centerPoint + ivec2(-1, -3),\n"
|
||||
" centerPoint + ivec2(5, -1),\n"
|
||||
" centerPoint + ivec2(-3, 5),\n"
|
||||
" centerPoint + ivec2(-5, -5),\n"
|
||||
" centerPoint + ivec2(-7, 1),\n"
|
||||
" centerPoint + ivec2(3, -7),\n"
|
||||
" centerPoint + ivec2(7, 7));\n"
|
||||
"/*/\n"
|
||||
" const int sampleCount = 4;\n"
|
||||
" ivec2 samplePoints[sampleCount] = ivec2[sampleCount](centerPoint + ivec2(-2, 6),\n"
|
||||
" centerPoint + ivec2(6, 2),\n"
|
||||
" centerPoint + ivec2(-6, -2),\n"
|
||||
" centerPoint + ivec2(2, -6));\n"
|
||||
"//*/\n"
|
||||
" //DEBUG\n"
|
||||
"/*\n"
|
||||
" {\n"
|
||||
" vec4 fragColor = vec4(0);\n"
|
||||
"\n"
|
||||
" if( pixelCoord.x % 16 == 0\n"
|
||||
" ||pixelCoord.y % 16 == 0)\n"
|
||||
" {\n"
|
||||
" fragColor = vec4(0, 0, 0, 1);\n"
|
||||
" }\n"
|
||||
" else if(tileCounterBuffer.elements[tileIndex] == 0xffffu)\n"
|
||||
" {\n"
|
||||
" fragColor = vec4(1, 0, 1, 1);\n"
|
||||
" }\n"
|
||||
" else if(tileCounter != 0u)\n"
|
||||
" {\n"
|
||||
" fragColor = vec4(0, 1, 0, 1);\n"
|
||||
" }\n"
|
||||
" else\n"
|
||||
" {\n"
|
||||
" fragColor = vec4(1, 0, 0, 1);\n"
|
||||
" }\n"
|
||||
" imageStore(outTexture, pixelCoord, fragColor);\n"
|
||||
" return;\n"
|
||||
" }\n"
|
||||
"//*/\n"
|
||||
" //----\n"
|
||||
"\n"
|
||||
" vec4 sampleColor[sampleCount];\n"
|
||||
" vec4 currentColor[sampleCount];\n"
|
||||
" int currentShapeIndex[sampleCount];\n"
|
||||
" int flipCount[sampleCount];\n"
|
||||
"\n"
|
||||
" for(int i=0; i<sampleCount; i++)\n"
|
||||
" {\n"
|
||||
" currentShapeIndex[i] = -1;\n"
|
||||
" flipCount[i] = 0;\n"
|
||||
" sampleColor[i] = vec4(0, 0, 0, 0);\n"
|
||||
" currentColor[i] = vec4(0, 0, 0, 0);\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" for(uint tileArrayIndex=0u; tileArrayIndex < tileCounter; tileArrayIndex++)\n"
|
||||
" {\n"
|
||||
" uint triangleIndex = tileArrayBuffer.elements[tileArraySize * tileIndex + tileArrayIndex];\n"
|
||||
"\n"
|
||||
" uint i0 = indexBuffer.elements[triangleIndex];\n"
|
||||
" uint i1 = indexBuffer.elements[triangleIndex+1u];\n"
|
||||
" uint i2 = indexBuffer.elements[triangleIndex+2u];\n"
|
||||
"\n"
|
||||
" ivec2 p0 = ivec2((vertexBuffer.elements[i0].pos * scaling) * subPixelFactor);\n"
|
||||
" ivec2 p1 = ivec2((vertexBuffer.elements[i1].pos * scaling) * subPixelFactor);\n"
|
||||
" ivec2 p2 = ivec2((vertexBuffer.elements[i2].pos * scaling) * subPixelFactor);\n"
|
||||
"\n"
|
||||
" int shapeIndex = vertexBuffer.elements[i0].shapeIndex;\n"
|
||||
" vec4 color = shapeBuffer.elements[shapeIndex].color;\n"
|
||||
" color.rgb *= color.a;\n"
|
||||
"\n"
|
||||
" ivec4 clip = ivec4(round((shapeBuffer.elements[shapeIndex].clip * vec4(scaling, scaling) + vec4(0.5, 0.5, 0.5, 0.5)) * subPixelFactor));\n"
|
||||
"\n"
|
||||
" mat3 uvTransform = mat3(shapeBuffer.elements[shapeIndex].uvTransform[0],\n"
|
||||
" shapeBuffer.elements[shapeIndex].uvTransform[3],\n"
|
||||
" 0.,\n"
|
||||
" shapeBuffer.elements[shapeIndex].uvTransform[1],\n"
|
||||
" shapeBuffer.elements[shapeIndex].uvTransform[4],\n"
|
||||
" 0.,\n"
|
||||
" shapeBuffer.elements[shapeIndex].uvTransform[2],\n"
|
||||
" shapeBuffer.elements[shapeIndex].uvTransform[5],\n"
|
||||
" 1.);\n"
|
||||
"\n"
|
||||
" //NOTE(martin): reorder triangle counter-clockwise and compute bias for each edge\n"
|
||||
" int cw = is_clockwise(p0, p1, p2);\n"
|
||||
" if(cw < 0)\n"
|
||||
" {\n"
|
||||
" uint tmpIndex = i1;\n"
|
||||
" i1 = i2;\n"
|
||||
" i2 = tmpIndex;\n"
|
||||
"\n"
|
||||
" ivec2 tmpPoint = p1;\n"
|
||||
" p1 = p2;\n"
|
||||
" p2 = tmpPoint;\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" vec4 cubic0 = vertexBuffer.elements[i0].cubic;\n"
|
||||
" vec4 cubic1 = vertexBuffer.elements[i1].cubic;\n"
|
||||
" vec4 cubic2 = vertexBuffer.elements[i2].cubic;\n"
|
||||
"\n"
|
||||
" int bias0 = is_top_left(p1, p2) ? 0 : -1;\n"
|
||||
" int bias1 = is_top_left(p2, p0) ? 0 : -1;\n"
|
||||
" int bias2 = is_top_left(p0, p1) ? 0 : -1;\n"
|
||||
"\n"
|
||||
" for(int sampleIndex = 0; sampleIndex < sampleCount; sampleIndex++)\n"
|
||||
" {\n"
|
||||
" ivec2 samplePoint = samplePoints[sampleIndex];\n"
|
||||
"\n"
|
||||
" if( samplePoint.x < clip.x\n"
|
||||
" || samplePoint.x > clip.z\n"
|
||||
" || samplePoint.y < clip.y\n"
|
||||
" || samplePoint.y > clip.w)\n"
|
||||
" {\n"
|
||||
" continue;\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" int w0 = orient2d(p1, p2, samplePoint);\n"
|
||||
" int w1 = orient2d(p2, p0, samplePoint);\n"
|
||||
" int w2 = orient2d(p0, p1, samplePoint);\n"
|
||||
"\n"
|
||||
" if((w0+bias0) >= 0 && (w1+bias1) >= 0 && (w2+bias2) >= 0)\n"
|
||||
" {\n"
|
||||
" vec4 cubic = (cubic0*float(w0) + cubic1*float(w1) + cubic2*float(w2))/(float(w0)+float(w1)+float(w2));\n"
|
||||
"\n"
|
||||
" float eps = 0.0001;\n"
|
||||
" if(cubic.w*(cubic.x*cubic.x*cubic.x - cubic.y*cubic.z) <= eps)\n"
|
||||
" {\n"
|
||||
" if(shapeIndex == currentShapeIndex[sampleIndex])\n"
|
||||
" {\n"
|
||||
" flipCount[sampleIndex]++;\n"
|
||||
" }\n"
|
||||
" else\n"
|
||||
" {\n"
|
||||
" if((flipCount[sampleIndex] & 0x01) != 0)\n"
|
||||
" {\n"
|
||||
" sampleColor[sampleIndex] = currentColor[sampleIndex];\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" vec4 nextColor = color;\n"
|
||||
" if(useTexture)\n"
|
||||
" {\n"
|
||||
" vec3 sampleFP = vec3(vec2(samplePoint).xy/(subPixelFactor*2.), 1);\n"
|
||||
" vec2 uv = (uvTransform * sampleFP).xy;\n"
|
||||
" vec4 texColor = texture(srcTexture, uv);\n"
|
||||
" texColor.rgb *= texColor.a;\n"
|
||||
" nextColor *= texColor;\n"
|
||||
" }\n"
|
||||
" currentColor[sampleIndex] = sampleColor[sampleIndex]*(1.-nextColor.a) + nextColor;\n"
|
||||
" currentShapeIndex[sampleIndex] = shapeIndex;\n"
|
||||
" flipCount[sampleIndex] = 1;\n"
|
||||
" }\n"
|
||||
" }\n"
|
||||
" }\n"
|
||||
" }\n"
|
||||
" }\n"
|
||||
" vec4 pixelColor = vec4(0);\n"
|
||||
" for(int sampleIndex = 0; sampleIndex < sampleCount; sampleIndex++)\n"
|
||||
" {\n"
|
||||
" if((flipCount[sampleIndex] & 0x01) != 0)\n"
|
||||
" {\n"
|
||||
" sampleColor[sampleIndex] = currentColor[sampleIndex];\n"
|
||||
" }\n"
|
||||
" pixelColor += sampleColor[sampleIndex];\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" imageStore(outTexture, pixelCoord, pixelColor/float(sampleCount));\n"
|
||||
"}\n";
|
||||
|
||||
#endif // __GLSL_SHADERS_H__
|
||||
/*********************************************************************
|
||||
*
|
||||
* file: glsl_shaders.h
|
||||
* note: string literals auto-generated by embed_text.py
|
||||
* date: 03/032023
|
||||
*
|
||||
**********************************************************************/
|
||||
#ifndef __GLSL_SHADERS_H__
|
||||
#define __GLSL_SHADERS_H__
|
||||
|
||||
|
||||
//NOTE: string imported from src\glsl_shaders\common.glsl
|
||||
const char* glsl_common =
|
||||
"\n"
|
||||
"layout(std430) buffer;\n"
|
||||
"\n"
|
||||
"struct vertex {\n"
|
||||
" vec4 cubic;\n"
|
||||
" vec2 pos;\n"
|
||||
" int shapeIndex;\n"
|
||||
"};\n"
|
||||
"\n"
|
||||
"struct shape {\n"
|
||||
" vec4 color;\n"
|
||||
" vec4 clip;\n"
|
||||
" float uvTransform[6];\n"
|
||||
"};\n";
|
||||
|
||||
//NOTE: string imported from src\glsl_shaders\blit_vertex.glsl
|
||||
const char* glsl_blit_vertex =
|
||||
"\n"
|
||||
"precision mediump float;\n"
|
||||
"\n"
|
||||
"out vec2 uv;\n"
|
||||
"\n"
|
||||
"void main()\n"
|
||||
"{\n"
|
||||
" float x = float(((uint(gl_VertexID) + 2u) / 3u)%2u);\n"
|
||||
" float y = float(((uint(gl_VertexID) + 1u) / 3u)%2u);\n"
|
||||
"\n"
|
||||
" gl_Position = vec4(-1.0f + x*2.0f, -1.0f+y*2.0f, 0.0f, 1.0f);\n"
|
||||
" uv = vec2(x, y);\n"
|
||||
"}\n";
|
||||
|
||||
//NOTE: string imported from src\glsl_shaders\blit_fragment.glsl
|
||||
const char* glsl_blit_fragment =
|
||||
"\n"
|
||||
"precision mediump float;\n"
|
||||
"\n"
|
||||
"in vec2 uv;\n"
|
||||
"out vec4 fragColor;\n"
|
||||
"\n"
|
||||
"layout(location=0) uniform sampler2D tex;\n"
|
||||
"\n"
|
||||
"void main()\n"
|
||||
"{\n"
|
||||
" fragColor = texture(tex, uv);\n"
|
||||
"}\n";
|
||||
|
||||
//NOTE: string imported from src\glsl_shaders\clear_counters.glsl
|
||||
const char* glsl_clear_counters =
|
||||
"\n"
|
||||
"layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n"
|
||||
"\n"
|
||||
"precision mediump float;\n"
|
||||
"layout(std430) buffer;\n"
|
||||
"\n"
|
||||
"layout(binding = 0) coherent restrict writeonly buffer tileCounterBufferSSBO {\n"
|
||||
" uint elements[];\n"
|
||||
"} tileCounterBuffer ;\n"
|
||||
"\n"
|
||||
"void main()\n"
|
||||
"{\n"
|
||||
" uint tileIndex = gl_WorkGroupID.x;\n"
|
||||
" tileCounterBuffer.elements[tileIndex] = 0u;\n"
|
||||
"}\n";
|
||||
|
||||
//NOTE: string imported from src\glsl_shaders\tile.glsl
|
||||
const char* glsl_tile =
|
||||
"\n"
|
||||
"layout(local_size_x = 512, local_size_y = 1, local_size_z = 1) in;\n"
|
||||
"\n"
|
||||
"precision mediump float;\n"
|
||||
"\n"
|
||||
"layout(binding = 0) restrict readonly buffer vertexBufferSSBO {\n"
|
||||
" vertex elements[];\n"
|
||||
"} vertexBuffer ;\n"
|
||||
"\n"
|
||||
"layout(binding = 1) restrict readonly buffer shapeBufferSSBO {\n"
|
||||
" shape elements[];\n"
|
||||
"} shapeBuffer ;\n"
|
||||
"\n"
|
||||
"layout(binding = 2) restrict readonly buffer indexBufferSSBO {\n"
|
||||
" uint elements[];\n"
|
||||
"} indexBuffer ;\n"
|
||||
"\n"
|
||||
"layout(binding = 3) coherent restrict buffer tileCounterBufferSSBO {\n"
|
||||
" uint elements[];\n"
|
||||
"} tileCounterBuffer ;\n"
|
||||
"\n"
|
||||
"layout(binding = 4) coherent restrict writeonly buffer tileArrayBufferSSBO {\n"
|
||||
" uint elements[];\n"
|
||||
"} tileArrayBuffer ;\n"
|
||||
"\n"
|
||||
"layout(location = 0) uniform uint indexCount;\n"
|
||||
"layout(location = 1) uniform uvec2 tileCount;\n"
|
||||
"layout(location = 2) uniform uint tileSize;\n"
|
||||
"layout(location = 3) uniform uint tileArraySize;\n"
|
||||
"layout(location = 4) uniform vec2 scaling;\n"
|
||||
"\n"
|
||||
"void main()\n"
|
||||
"{\n"
|
||||
" uint triangleIndex = (gl_WorkGroupID.x*gl_WorkGroupSize.x + gl_LocalInvocationIndex) * 3u;\n"
|
||||
" if(triangleIndex >= indexCount)\n"
|
||||
" {\n"
|
||||
" return;\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" uint i0 = indexBuffer.elements[triangleIndex];\n"
|
||||
" uint i1 = indexBuffer.elements[triangleIndex+1u];\n"
|
||||
" uint i2 = indexBuffer.elements[triangleIndex+2u];\n"
|
||||
"\n"
|
||||
" vec2 p0 = vertexBuffer.elements[i0].pos * scaling;\n"
|
||||
" vec2 p1 = vertexBuffer.elements[i1].pos * scaling;\n"
|
||||
" vec2 p2 = vertexBuffer.elements[i2].pos * scaling;\n"
|
||||
"\n"
|
||||
" int shapeIndex = vertexBuffer.elements[i0].shapeIndex;\n"
|
||||
" vec4 clip = shapeBuffer.elements[shapeIndex].clip * vec4(scaling, scaling);\n"
|
||||
"\n"
|
||||
" vec4 fbox = vec4(max(min(min(p0.x, p1.x), p2.x), clip.x),\n"
|
||||
" max(min(min(p0.y, p1.y), p2.y), clip.y),\n"
|
||||
" min(max(max(p0.x, p1.x), p2.x), clip.z),\n"
|
||||
" min(max(max(p0.y, p1.y), p2.y), clip.w));\n"
|
||||
"\n"
|
||||
" ivec4 box = ivec4(floor(fbox))/int(tileSize);\n"
|
||||
"\n"
|
||||
" //NOTE(martin): it's importat to do the computation with signed int, so that we can have negative xMax/yMax\n"
|
||||
" // otherwise all triangles on the left or below the x/y axis are attributed to tiles on row/column 0.\n"
|
||||
" int xMin = max(0, box.x);\n"
|
||||
" int yMin = max(0, box.y);\n"
|
||||
" int xMax = min(box.z, int(tileCount.x) - 1);\n"
|
||||
" int yMax = min(box.w, int(tileCount.y) - 1);\n"
|
||||
"\n"
|
||||
" for(int y = yMin; y <= yMax; y++)\n"
|
||||
" {\n"
|
||||
" for(int x = xMin ; x <= xMax; x++)\n"
|
||||
" {\n"
|
||||
" uint tileIndex = uint(y)*tileCount.x + uint(x);\n"
|
||||
" uint tileCounter = atomicAdd(tileCounterBuffer.elements[tileIndex], 1u);\n"
|
||||
" if(tileCounter < tileArraySize)\n"
|
||||
" {\n"
|
||||
" tileArrayBuffer.elements[tileArraySize*tileIndex + tileCounter] = triangleIndex;\n"
|
||||
" }\n"
|
||||
" }\n"
|
||||
" }\n"
|
||||
"}\n";
|
||||
|
||||
//NOTE: string imported from src\glsl_shaders\sort.glsl
|
||||
const char* glsl_sort =
|
||||
"\n"
|
||||
"layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n"
|
||||
"\n"
|
||||
"precision mediump float;\n"
|
||||
"\n"
|
||||
"layout(binding = 0) restrict readonly buffer vertexBufferSSBO {\n"
|
||||
" vertex elements[];\n"
|
||||
"} vertexBuffer ;\n"
|
||||
"\n"
|
||||
"layout(binding = 1) restrict readonly buffer shapeBufferSSBO {\n"
|
||||
" shape elements[];\n"
|
||||
"} shapeBuffer ;\n"
|
||||
"\n"
|
||||
"layout(binding = 2) restrict readonly buffer indexBufferSSBO {\n"
|
||||
" uint elements[];\n"
|
||||
"} indexBuffer ;\n"
|
||||
"\n"
|
||||
"layout(binding = 3) coherent readonly restrict buffer tileCounterBufferSSBO {\n"
|
||||
" uint elements[];\n"
|
||||
"} tileCounterBuffer ;\n"
|
||||
"\n"
|
||||
"layout(binding = 4) coherent restrict buffer tileArrayBufferSSBO {\n"
|
||||
" uint elements[];\n"
|
||||
"} tileArrayBuffer ;\n"
|
||||
"\n"
|
||||
"layout(location = 0) uniform uint indexCount;\n"
|
||||
"layout(location = 1) uniform uvec2 tileCount;\n"
|
||||
"layout(location = 2) uniform uint tileSize;\n"
|
||||
"layout(location = 3) uniform uint tileArraySize;\n"
|
||||
"\n"
|
||||
"int get_shape_index(uint tileArrayOffset, uint tileArrayIndex)\n"
|
||||
"{\n"
|
||||
" uint triangleIndex = tileArrayBuffer.elements[tileArrayOffset + tileArrayIndex];\n"
|
||||
" uint i0 = indexBuffer.elements[triangleIndex];\n"
|
||||
" int shapeIndex = vertexBuffer.elements[i0].shapeIndex;\n"
|
||||
" return(shapeIndex);\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"void main()\n"
|
||||
"{\n"
|
||||
" uint tileIndex = gl_WorkGroupID.x;\n"
|
||||
" uint tileArrayOffset = tileArraySize * tileIndex;\n"
|
||||
" uint tileArrayCount = min(tileCounterBuffer.elements[tileIndex], tileArraySize);\n"
|
||||
"\n"
|
||||
" for(uint tileArrayIndex=1u; tileArrayIndex < tileArrayCount; tileArrayIndex++)\n"
|
||||
" {\n"
|
||||
" for(uint sortIndex = tileArrayIndex; sortIndex > 0u; sortIndex--)\n"
|
||||
" {\n"
|
||||
" int shapeIndex = get_shape_index(tileArrayOffset, sortIndex);\n"
|
||||
" int prevShapeIndex = get_shape_index(tileArrayOffset, sortIndex-1u);\n"
|
||||
"\n"
|
||||
" if(shapeIndex >= prevShapeIndex)\n"
|
||||
" {\n"
|
||||
" break;\n"
|
||||
" }\n"
|
||||
" uint tmp = tileArrayBuffer.elements[tileArrayOffset + sortIndex];\n"
|
||||
" tileArrayBuffer.elements[tileArrayOffset + sortIndex] = tileArrayBuffer.elements[tileArrayOffset + sortIndex - 1u];\n"
|
||||
" tileArrayBuffer.elements[tileArrayOffset + sortIndex - 1u] = tmp;\n"
|
||||
" }\n"
|
||||
" }\n"
|
||||
"}\n";
|
||||
|
||||
//NOTE: string imported from src\glsl_shaders\draw.glsl
|
||||
const char* glsl_draw =
|
||||
"\n"
|
||||
"#extension GL_ARB_gpu_shader_int64 : require\n"
|
||||
"layout(local_size_x = 16, local_size_y = 16, local_size_z = 1) in;\n"
|
||||
"\n"
|
||||
"precision mediump float;\n"
|
||||
"//precision mediump image2D;\n"
|
||||
"\n"
|
||||
"layout(binding = 0) restrict readonly buffer vertexBufferSSBO {\n"
|
||||
" vertex elements[];\n"
|
||||
"} vertexBuffer ;\n"
|
||||
"\n"
|
||||
"layout(binding = 1) restrict readonly buffer shapeBufferSSBO {\n"
|
||||
" shape elements[];\n"
|
||||
"} shapeBuffer ;\n"
|
||||
"\n"
|
||||
"layout(binding = 2) restrict readonly buffer indexBufferSSBO {\n"
|
||||
" uint elements[];\n"
|
||||
"} indexBuffer ;\n"
|
||||
"\n"
|
||||
"layout(binding = 3) restrict readonly buffer tileCounterBufferSSBO {\n"
|
||||
" uint elements[];\n"
|
||||
"} tileCounterBuffer ;\n"
|
||||
"\n"
|
||||
"layout(binding = 4) restrict readonly buffer tileArrayBufferSSBO {\n"
|
||||
" uint elements[];\n"
|
||||
"} tileArrayBuffer ;\n"
|
||||
"\n"
|
||||
"layout(location = 0) uniform uint indexCount;\n"
|
||||
"layout(location = 1) uniform uvec2 tileCount;\n"
|
||||
"layout(location = 2) uniform uint tileSize;\n"
|
||||
"layout(location = 3) uniform uint tileArraySize;\n"
|
||||
"layout(location = 4) uniform vec2 scaling;\n"
|
||||
"layout(location = 5) uniform uint useTexture;\n"
|
||||
"\n"
|
||||
"layout(rgba8, binding = 0) uniform restrict writeonly image2D outTexture;\n"
|
||||
"\n"
|
||||
"layout(binding = 1) uniform sampler2D srcTexture;\n"
|
||||
"\n"
|
||||
"\n"
|
||||
"bool is_top_left(ivec2 a, ivec2 b)\n"
|
||||
"{\n"
|
||||
" return( (a.y == b.y && b.x < a.x)\n"
|
||||
" ||(b.y < a.y));\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"//////////////////////////////////////////////////////////////////////////////\n"
|
||||
"//TODO: we should do these computations on 64bits, because otherwise\n"
|
||||
"// we might overflow for values > 2048.\n"
|
||||
"// Unfortunately this is costly.\n"
|
||||
"// Another way is to precompute triangle edges (b - a) in full precision\n"
|
||||
"// once to avoid doing it all the time...\n"
|
||||
"//////////////////////////////////////////////////////////////////////////////\n"
|
||||
"int orient2d(ivec2 a, ivec2 b, ivec2 p)\n"
|
||||
"{\n"
|
||||
" return((b.x-a.x)*(p.y-a.y) - (b.y-a.y)*(p.x-a.x));\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"int is_clockwise(ivec2 p0, ivec2 p1, ivec2 p2)\n"
|
||||
"{\n"
|
||||
" return((p1 - p0).x*(p2 - p0).y - (p1 - p0).y*(p2 - p0).x);\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"void main()\n"
|
||||
"{\n"
|
||||
" ivec2 pixelCoord = ivec2(gl_WorkGroupID.xy*uvec2(16, 16) + gl_LocalInvocationID.xy);\n"
|
||||
" uvec2 tileCoord = uvec2(pixelCoord) / tileSize;\n"
|
||||
" uint tileIndex = tileCoord.y * tileCount.x + tileCoord.x;\n"
|
||||
" uint tileCounter = min(tileCounterBuffer.elements[tileIndex], tileArraySize);\n"
|
||||
"\n"
|
||||
" const float subPixelFactor = 16.;\n"
|
||||
" ivec2 centerPoint = ivec2((vec2(pixelCoord) + vec2(0.5, 0.5)) * subPixelFactor);\n"
|
||||
"\n"
|
||||
"//*\n"
|
||||
" const int sampleCount = 8;\n"
|
||||
" ivec2 samplePoints[sampleCount] = ivec2[sampleCount](centerPoint + ivec2(1, 3),\n"
|
||||
" centerPoint + ivec2(-1, -3),\n"
|
||||
" centerPoint + ivec2(5, -1),\n"
|
||||
" centerPoint + ivec2(-3, 5),\n"
|
||||
" centerPoint + ivec2(-5, -5),\n"
|
||||
" centerPoint + ivec2(-7, 1),\n"
|
||||
" centerPoint + ivec2(3, -7),\n"
|
||||
" centerPoint + ivec2(7, 7));\n"
|
||||
"/*/\n"
|
||||
" const int sampleCount = 4;\n"
|
||||
" ivec2 samplePoints[sampleCount] = ivec2[sampleCount](centerPoint + ivec2(-2, 6),\n"
|
||||
" centerPoint + ivec2(6, 2),\n"
|
||||
" centerPoint + ivec2(-6, -2),\n"
|
||||
" centerPoint + ivec2(2, -6));\n"
|
||||
"//*/\n"
|
||||
" //DEBUG\n"
|
||||
"/*\n"
|
||||
" {\n"
|
||||
" vec4 fragColor = vec4(0);\n"
|
||||
"\n"
|
||||
" if( pixelCoord.x % 16 == 0\n"
|
||||
" ||pixelCoord.y % 16 == 0)\n"
|
||||
" {\n"
|
||||
" fragColor = vec4(0, 0, 0, 1);\n"
|
||||
" }\n"
|
||||
" else if(tileCounterBuffer.elements[tileIndex] == 0xffffu)\n"
|
||||
" {\n"
|
||||
" fragColor = vec4(1, 0, 1, 1);\n"
|
||||
" }\n"
|
||||
" else if(tileCounter != 0u)\n"
|
||||
" {\n"
|
||||
" fragColor = vec4(0, 1, 0, 1);\n"
|
||||
" }\n"
|
||||
" else\n"
|
||||
" {\n"
|
||||
" fragColor = vec4(1, 0, 0, 1);\n"
|
||||
" }\n"
|
||||
" imageStore(outTexture, pixelCoord, fragColor);\n"
|
||||
" return;\n"
|
||||
" }\n"
|
||||
"//*/\n"
|
||||
" //----\n"
|
||||
"\n"
|
||||
" vec4 sampleColor[sampleCount];\n"
|
||||
" vec4 currentColor[sampleCount];\n"
|
||||
" int currentShapeIndex[sampleCount];\n"
|
||||
" int flipCount[sampleCount];\n"
|
||||
"\n"
|
||||
" for(int i=0; i<sampleCount; i++)\n"
|
||||
" {\n"
|
||||
" currentShapeIndex[i] = -1;\n"
|
||||
" flipCount[i] = 0;\n"
|
||||
" sampleColor[i] = vec4(0, 0, 0, 0);\n"
|
||||
" currentColor[i] = vec4(0, 0, 0, 0);\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" for(uint tileArrayIndex=0u; tileArrayIndex < tileCounter; tileArrayIndex++)\n"
|
||||
" {\n"
|
||||
" uint triangleIndex = tileArrayBuffer.elements[tileArraySize * tileIndex + tileArrayIndex];\n"
|
||||
"\n"
|
||||
" uint i0 = indexBuffer.elements[triangleIndex];\n"
|
||||
" uint i1 = indexBuffer.elements[triangleIndex+1u];\n"
|
||||
" uint i2 = indexBuffer.elements[triangleIndex+2u];\n"
|
||||
"\n"
|
||||
" ivec2 p0 = ivec2((vertexBuffer.elements[i0].pos * scaling) * subPixelFactor);\n"
|
||||
" ivec2 p1 = ivec2((vertexBuffer.elements[i1].pos * scaling) * subPixelFactor);\n"
|
||||
" ivec2 p2 = ivec2((vertexBuffer.elements[i2].pos * scaling) * subPixelFactor);\n"
|
||||
"\n"
|
||||
" int shapeIndex = vertexBuffer.elements[i0].shapeIndex;\n"
|
||||
" vec4 color = shapeBuffer.elements[shapeIndex].color;\n"
|
||||
" color.rgb *= color.a;\n"
|
||||
"\n"
|
||||
" ivec4 clip = ivec4(round((shapeBuffer.elements[shapeIndex].clip * vec4(scaling, scaling) + vec4(0.5, 0.5, 0.5, 0.5)) * subPixelFactor));\n"
|
||||
"\n"
|
||||
" mat3 uvTransform = mat3(shapeBuffer.elements[shapeIndex].uvTransform[0],\n"
|
||||
" shapeBuffer.elements[shapeIndex].uvTransform[3],\n"
|
||||
" 0.,\n"
|
||||
" shapeBuffer.elements[shapeIndex].uvTransform[1],\n"
|
||||
" shapeBuffer.elements[shapeIndex].uvTransform[4],\n"
|
||||
" 0.,\n"
|
||||
" shapeBuffer.elements[shapeIndex].uvTransform[2],\n"
|
||||
" shapeBuffer.elements[shapeIndex].uvTransform[5],\n"
|
||||
" 1.);\n"
|
||||
"\n"
|
||||
" //NOTE(martin): reorder triangle counter-clockwise and compute bias for each edge\n"
|
||||
" int cw = is_clockwise(p0, p1, p2);\n"
|
||||
" if(cw < 0)\n"
|
||||
" {\n"
|
||||
" uint tmpIndex = i1;\n"
|
||||
" i1 = i2;\n"
|
||||
" i2 = tmpIndex;\n"
|
||||
"\n"
|
||||
" ivec2 tmpPoint = p1;\n"
|
||||
" p1 = p2;\n"
|
||||
" p2 = tmpPoint;\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" vec4 cubic0 = vertexBuffer.elements[i0].cubic;\n"
|
||||
" vec4 cubic1 = vertexBuffer.elements[i1].cubic;\n"
|
||||
" vec4 cubic2 = vertexBuffer.elements[i2].cubic;\n"
|
||||
"\n"
|
||||
" int bias0 = is_top_left(p1, p2) ? 0 : -1;\n"
|
||||
" int bias1 = is_top_left(p2, p0) ? 0 : -1;\n"
|
||||
" int bias2 = is_top_left(p0, p1) ? 0 : -1;\n"
|
||||
"\n"
|
||||
" for(int sampleIndex = 0; sampleIndex < sampleCount; sampleIndex++)\n"
|
||||
" {\n"
|
||||
" ivec2 samplePoint = samplePoints[sampleIndex];\n"
|
||||
"\n"
|
||||
" if( samplePoint.x < clip.x\n"
|
||||
" || samplePoint.x > clip.z\n"
|
||||
" || samplePoint.y < clip.y\n"
|
||||
" || samplePoint.y > clip.w)\n"
|
||||
" {\n"
|
||||
" continue;\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" int w0 = orient2d(p1, p2, samplePoint);\n"
|
||||
" int w1 = orient2d(p2, p0, samplePoint);\n"
|
||||
" int w2 = orient2d(p0, p1, samplePoint);\n"
|
||||
"\n"
|
||||
" if((w0+bias0) >= 0 && (w1+bias1) >= 0 && (w2+bias2) >= 0)\n"
|
||||
" {\n"
|
||||
" vec4 cubic = (cubic0*float(w0) + cubic1*float(w1) + cubic2*float(w2))/(float(w0)+float(w1)+float(w2));\n"
|
||||
"\n"
|
||||
" float eps = 0.0001;\n"
|
||||
" if(cubic.w*(cubic.x*cubic.x*cubic.x - cubic.y*cubic.z) <= eps)\n"
|
||||
" {\n"
|
||||
" if(shapeIndex == currentShapeIndex[sampleIndex])\n"
|
||||
" {\n"
|
||||
" flipCount[sampleIndex]++;\n"
|
||||
" }\n"
|
||||
" else\n"
|
||||
" {\n"
|
||||
" if((flipCount[sampleIndex] & 0x01) != 0)\n"
|
||||
" {\n"
|
||||
" sampleColor[sampleIndex] = currentColor[sampleIndex];\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" vec4 nextColor = color;\n"
|
||||
" if(useTexture)\n"
|
||||
" {\n"
|
||||
" vec3 sampleFP = vec3(vec2(samplePoint).xy/(subPixelFactor*2.), 1);\n"
|
||||
" vec2 uv = (uvTransform * sampleFP).xy;\n"
|
||||
" vec4 texColor = texture(srcTexture, uv);\n"
|
||||
" texColor.rgb *= texColor.a;\n"
|
||||
" nextColor *= texColor;\n"
|
||||
" }\n"
|
||||
" currentColor[sampleIndex] = sampleColor[sampleIndex]*(1.-nextColor.a) + nextColor;\n"
|
||||
" currentShapeIndex[sampleIndex] = shapeIndex;\n"
|
||||
" flipCount[sampleIndex] = 1;\n"
|
||||
" }\n"
|
||||
" }\n"
|
||||
" }\n"
|
||||
" }\n"
|
||||
" }\n"
|
||||
" vec4 pixelColor = vec4(0);\n"
|
||||
" for(int sampleIndex = 0; sampleIndex < sampleCount; sampleIndex++)\n"
|
||||
" {\n"
|
||||
" if((flipCount[sampleIndex] & 0x01) != 0)\n"
|
||||
" {\n"
|
||||
" sampleColor[sampleIndex] = currentColor[sampleIndex];\n"
|
||||
" }\n"
|
||||
" pixelColor += sampleColor[sampleIndex];\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" imageStore(outTexture, pixelCoord, pixelColor/float(sampleCount));\n"
|
||||
"}\n";
|
||||
|
||||
#endif // __GLSL_SHADERS_H__
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
|
||||
precision mediump float;
|
||||
|
||||
in vec2 uv;
|
||||
out vec4 fragColor;
|
||||
|
||||
layout(location=0) uniform sampler2D tex;
|
||||
|
||||
void main()
|
||||
{
|
||||
fragColor = texture(tex, uv);
|
||||
}
|
||||
|
||||
precision mediump float;
|
||||
|
||||
in vec2 uv;
|
||||
out vec4 fragColor;
|
||||
|
||||
layout(location=0) uniform sampler2D tex;
|
||||
|
||||
void main()
|
||||
{
|
||||
fragColor = texture(tex, uv);
|
||||
}
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
|
||||
precision mediump float;
|
||||
|
||||
out vec2 uv;
|
||||
|
||||
void main()
|
||||
{
|
||||
float x = float(((uint(gl_VertexID) + 2u) / 3u)%2u);
|
||||
float y = float(((uint(gl_VertexID) + 1u) / 3u)%2u);
|
||||
|
||||
gl_Position = vec4(-1.0f + x*2.0f, -1.0f+y*2.0f, 0.0f, 1.0f);
|
||||
uv = vec2(x, y);
|
||||
}
|
||||
|
||||
precision mediump float;
|
||||
|
||||
out vec2 uv;
|
||||
|
||||
void main()
|
||||
{
|
||||
float x = float(((uint(gl_VertexID) + 2u) / 3u)%2u);
|
||||
float y = float(((uint(gl_VertexID) + 1u) / 3u)%2u);
|
||||
|
||||
gl_Position = vec4(-1.0f + x*2.0f, -1.0f+y*2.0f, 0.0f, 1.0f);
|
||||
uv = vec2(x, y);
|
||||
}
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
|
||||
layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
|
||||
|
||||
precision mediump float;
|
||||
layout(std430) buffer;
|
||||
|
||||
layout(binding = 0) coherent restrict writeonly buffer tileCounterBufferSSBO {
|
||||
uint elements[];
|
||||
} tileCounterBuffer ;
|
||||
|
||||
void main()
|
||||
{
|
||||
uint tileIndex = gl_WorkGroupID.x;
|
||||
tileCounterBuffer.elements[tileIndex] = 0u;
|
||||
}
|
||||
|
||||
layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
|
||||
|
||||
precision mediump float;
|
||||
layout(std430) buffer;
|
||||
|
||||
layout(binding = 0) coherent restrict writeonly buffer tileCounterBufferSSBO {
|
||||
uint elements[];
|
||||
} tileCounterBuffer ;
|
||||
|
||||
void main()
|
||||
{
|
||||
uint tileIndex = gl_WorkGroupID.x;
|
||||
tileCounterBuffer.elements[tileIndex] = 0u;
|
||||
}
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
|
||||
layout(std430) buffer;
|
||||
|
||||
struct vertex {
|
||||
vec4 cubic;
|
||||
vec2 pos;
|
||||
int shapeIndex;
|
||||
};
|
||||
|
||||
struct shape {
|
||||
vec4 color;
|
||||
vec4 clip;
|
||||
float uvTransform[6];
|
||||
};
|
||||
|
||||
layout(std430) buffer;
|
||||
|
||||
struct vertex {
|
||||
vec4 cubic;
|
||||
vec2 pos;
|
||||
int shapeIndex;
|
||||
};
|
||||
|
||||
struct shape {
|
||||
vec4 color;
|
||||
vec4 clip;
|
||||
float uvTransform[6];
|
||||
};
|
||||
|
|
|
@ -1,242 +1,242 @@
|
|||
|
||||
#extension GL_ARB_gpu_shader_int64 : require
|
||||
layout(local_size_x = 16, local_size_y = 16, local_size_z = 1) in;
|
||||
|
||||
precision mediump float;
|
||||
//precision mediump image2D;
|
||||
|
||||
layout(binding = 0) restrict readonly buffer vertexBufferSSBO {
|
||||
vertex elements[];
|
||||
} vertexBuffer ;
|
||||
|
||||
layout(binding = 1) restrict readonly buffer shapeBufferSSBO {
|
||||
shape elements[];
|
||||
} shapeBuffer ;
|
||||
|
||||
layout(binding = 2) restrict readonly buffer indexBufferSSBO {
|
||||
uint elements[];
|
||||
} indexBuffer ;
|
||||
|
||||
layout(binding = 3) restrict readonly buffer tileCounterBufferSSBO {
|
||||
uint elements[];
|
||||
} tileCounterBuffer ;
|
||||
|
||||
layout(binding = 4) restrict readonly buffer tileArrayBufferSSBO {
|
||||
uint elements[];
|
||||
} tileArrayBuffer ;
|
||||
|
||||
layout(location = 0) uniform uint indexCount;
|
||||
layout(location = 1) uniform uvec2 tileCount;
|
||||
layout(location = 2) uniform uint tileSize;
|
||||
layout(location = 3) uniform uint tileArraySize;
|
||||
layout(location = 4) uniform vec2 scaling;
|
||||
layout(location = 5) uniform uint useTexture;
|
||||
|
||||
layout(rgba8, binding = 0) uniform restrict writeonly image2D outTexture;
|
||||
|
||||
layout(binding = 1) uniform sampler2D srcTexture;
|
||||
|
||||
|
||||
bool is_top_left(ivec2 a, ivec2 b)
|
||||
{
|
||||
return( (a.y == b.y && b.x < a.x)
|
||||
||(b.y < a.y));
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//TODO: we should do these computations on 64bits, because otherwise
|
||||
// we might overflow for values > 2048.
|
||||
// Unfortunately this is costly.
|
||||
// Another way is to precompute triangle edges (b - a) in full precision
|
||||
// once to avoid doing it all the time...
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
int orient2d(ivec2 a, ivec2 b, ivec2 p)
|
||||
{
|
||||
return((b.x-a.x)*(p.y-a.y) - (b.y-a.y)*(p.x-a.x));
|
||||
}
|
||||
|
||||
int is_clockwise(ivec2 p0, ivec2 p1, ivec2 p2)
|
||||
{
|
||||
return((p1 - p0).x*(p2 - p0).y - (p1 - p0).y*(p2 - p0).x);
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
ivec2 pixelCoord = ivec2(gl_WorkGroupID.xy*uvec2(16, 16) + gl_LocalInvocationID.xy);
|
||||
uvec2 tileCoord = uvec2(pixelCoord) / tileSize;
|
||||
uint tileIndex = tileCoord.y * tileCount.x + tileCoord.x;
|
||||
uint tileCounter = min(tileCounterBuffer.elements[tileIndex], tileArraySize);
|
||||
|
||||
const float subPixelFactor = 16.;
|
||||
ivec2 centerPoint = ivec2((vec2(pixelCoord) + vec2(0.5, 0.5)) * subPixelFactor);
|
||||
|
||||
//*
|
||||
const int sampleCount = 8;
|
||||
ivec2 samplePoints[sampleCount] = ivec2[sampleCount](centerPoint + ivec2(1, 3),
|
||||
centerPoint + ivec2(-1, -3),
|
||||
centerPoint + ivec2(5, -1),
|
||||
centerPoint + ivec2(-3, 5),
|
||||
centerPoint + ivec2(-5, -5),
|
||||
centerPoint + ivec2(-7, 1),
|
||||
centerPoint + ivec2(3, -7),
|
||||
centerPoint + ivec2(7, 7));
|
||||
/*/
|
||||
const int sampleCount = 4;
|
||||
ivec2 samplePoints[sampleCount] = ivec2[sampleCount](centerPoint + ivec2(-2, 6),
|
||||
centerPoint + ivec2(6, 2),
|
||||
centerPoint + ivec2(-6, -2),
|
||||
centerPoint + ivec2(2, -6));
|
||||
//*/
|
||||
//DEBUG
|
||||
/*
|
||||
{
|
||||
vec4 fragColor = vec4(0);
|
||||
|
||||
if( pixelCoord.x % 16 == 0
|
||||
||pixelCoord.y % 16 == 0)
|
||||
{
|
||||
fragColor = vec4(0, 0, 0, 1);
|
||||
}
|
||||
else if(tileCounterBuffer.elements[tileIndex] == 0xffffu)
|
||||
{
|
||||
fragColor = vec4(1, 0, 1, 1);
|
||||
}
|
||||
else if(tileCounter != 0u)
|
||||
{
|
||||
fragColor = vec4(0, 1, 0, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
fragColor = vec4(1, 0, 0, 1);
|
||||
}
|
||||
imageStore(outTexture, pixelCoord, fragColor);
|
||||
return;
|
||||
}
|
||||
//*/
|
||||
//----
|
||||
|
||||
vec4 sampleColor[sampleCount];
|
||||
vec4 currentColor[sampleCount];
|
||||
int currentShapeIndex[sampleCount];
|
||||
int flipCount[sampleCount];
|
||||
|
||||
for(int i=0; i<sampleCount; i++)
|
||||
{
|
||||
currentShapeIndex[i] = -1;
|
||||
flipCount[i] = 0;
|
||||
sampleColor[i] = vec4(0, 0, 0, 0);
|
||||
currentColor[i] = vec4(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
for(uint tileArrayIndex=0u; tileArrayIndex < tileCounter; tileArrayIndex++)
|
||||
{
|
||||
uint triangleIndex = tileArrayBuffer.elements[tileArraySize * tileIndex + tileArrayIndex];
|
||||
|
||||
uint i0 = indexBuffer.elements[triangleIndex];
|
||||
uint i1 = indexBuffer.elements[triangleIndex+1u];
|
||||
uint i2 = indexBuffer.elements[triangleIndex+2u];
|
||||
|
||||
ivec2 p0 = ivec2((vertexBuffer.elements[i0].pos * scaling) * subPixelFactor);
|
||||
ivec2 p1 = ivec2((vertexBuffer.elements[i1].pos * scaling) * subPixelFactor);
|
||||
ivec2 p2 = ivec2((vertexBuffer.elements[i2].pos * scaling) * subPixelFactor);
|
||||
|
||||
int shapeIndex = vertexBuffer.elements[i0].shapeIndex;
|
||||
vec4 color = shapeBuffer.elements[shapeIndex].color;
|
||||
color.rgb *= color.a;
|
||||
|
||||
ivec4 clip = ivec4(round((shapeBuffer.elements[shapeIndex].clip * vec4(scaling, scaling) + vec4(0.5, 0.5, 0.5, 0.5)) * subPixelFactor));
|
||||
|
||||
mat3 uvTransform = mat3(shapeBuffer.elements[shapeIndex].uvTransform[0],
|
||||
shapeBuffer.elements[shapeIndex].uvTransform[3],
|
||||
0.,
|
||||
shapeBuffer.elements[shapeIndex].uvTransform[1],
|
||||
shapeBuffer.elements[shapeIndex].uvTransform[4],
|
||||
0.,
|
||||
shapeBuffer.elements[shapeIndex].uvTransform[2],
|
||||
shapeBuffer.elements[shapeIndex].uvTransform[5],
|
||||
1.);
|
||||
|
||||
//NOTE(martin): reorder triangle counter-clockwise and compute bias for each edge
|
||||
int cw = is_clockwise(p0, p1, p2);
|
||||
if(cw < 0)
|
||||
{
|
||||
uint tmpIndex = i1;
|
||||
i1 = i2;
|
||||
i2 = tmpIndex;
|
||||
|
||||
ivec2 tmpPoint = p1;
|
||||
p1 = p2;
|
||||
p2 = tmpPoint;
|
||||
}
|
||||
|
||||
vec4 cubic0 = vertexBuffer.elements[i0].cubic;
|
||||
vec4 cubic1 = vertexBuffer.elements[i1].cubic;
|
||||
vec4 cubic2 = vertexBuffer.elements[i2].cubic;
|
||||
|
||||
int bias0 = is_top_left(p1, p2) ? 0 : -1;
|
||||
int bias1 = is_top_left(p2, p0) ? 0 : -1;
|
||||
int bias2 = is_top_left(p0, p1) ? 0 : -1;
|
||||
|
||||
for(int sampleIndex = 0; sampleIndex < sampleCount; sampleIndex++)
|
||||
{
|
||||
ivec2 samplePoint = samplePoints[sampleIndex];
|
||||
|
||||
if( samplePoint.x < clip.x
|
||||
|| samplePoint.x > clip.z
|
||||
|| samplePoint.y < clip.y
|
||||
|| samplePoint.y > clip.w)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int w0 = orient2d(p1, p2, samplePoint);
|
||||
int w1 = orient2d(p2, p0, samplePoint);
|
||||
int w2 = orient2d(p0, p1, samplePoint);
|
||||
|
||||
if((w0+bias0) >= 0 && (w1+bias1) >= 0 && (w2+bias2) >= 0)
|
||||
{
|
||||
vec4 cubic = (cubic0*float(w0) + cubic1*float(w1) + cubic2*float(w2))/(float(w0)+float(w1)+float(w2));
|
||||
|
||||
float eps = 0.0001;
|
||||
if(cubic.w*(cubic.x*cubic.x*cubic.x - cubic.y*cubic.z) <= eps)
|
||||
{
|
||||
if(shapeIndex == currentShapeIndex[sampleIndex])
|
||||
{
|
||||
flipCount[sampleIndex]++;
|
||||
}
|
||||
else
|
||||
{
|
||||
if((flipCount[sampleIndex] & 0x01) != 0)
|
||||
{
|
||||
sampleColor[sampleIndex] = currentColor[sampleIndex];
|
||||
}
|
||||
|
||||
vec4 nextColor = color;
|
||||
if(useTexture)
|
||||
{
|
||||
vec3 sampleFP = vec3(vec2(samplePoint).xy/(subPixelFactor*2.), 1);
|
||||
vec2 uv = (uvTransform * sampleFP).xy;
|
||||
vec4 texColor = texture(srcTexture, uv);
|
||||
texColor.rgb *= texColor.a;
|
||||
nextColor *= texColor;
|
||||
}
|
||||
currentColor[sampleIndex] = sampleColor[sampleIndex]*(1.-nextColor.a) + nextColor;
|
||||
currentShapeIndex[sampleIndex] = shapeIndex;
|
||||
flipCount[sampleIndex] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
vec4 pixelColor = vec4(0);
|
||||
for(int sampleIndex = 0; sampleIndex < sampleCount; sampleIndex++)
|
||||
{
|
||||
if((flipCount[sampleIndex] & 0x01) != 0)
|
||||
{
|
||||
sampleColor[sampleIndex] = currentColor[sampleIndex];
|
||||
}
|
||||
pixelColor += sampleColor[sampleIndex];
|
||||
}
|
||||
|
||||
imageStore(outTexture, pixelCoord, pixelColor/float(sampleCount));
|
||||
}
|
||||
|
||||
#extension GL_ARB_gpu_shader_int64 : require
|
||||
layout(local_size_x = 16, local_size_y = 16, local_size_z = 1) in;
|
||||
|
||||
precision mediump float;
|
||||
//precision mediump image2D;
|
||||
|
||||
layout(binding = 0) restrict readonly buffer vertexBufferSSBO {
|
||||
vertex elements[];
|
||||
} vertexBuffer ;
|
||||
|
||||
layout(binding = 1) restrict readonly buffer shapeBufferSSBO {
|
||||
shape elements[];
|
||||
} shapeBuffer ;
|
||||
|
||||
layout(binding = 2) restrict readonly buffer indexBufferSSBO {
|
||||
uint elements[];
|
||||
} indexBuffer ;
|
||||
|
||||
layout(binding = 3) restrict readonly buffer tileCounterBufferSSBO {
|
||||
uint elements[];
|
||||
} tileCounterBuffer ;
|
||||
|
||||
layout(binding = 4) restrict readonly buffer tileArrayBufferSSBO {
|
||||
uint elements[];
|
||||
} tileArrayBuffer ;
|
||||
|
||||
layout(location = 0) uniform uint indexCount;
|
||||
layout(location = 1) uniform uvec2 tileCount;
|
||||
layout(location = 2) uniform uint tileSize;
|
||||
layout(location = 3) uniform uint tileArraySize;
|
||||
layout(location = 4) uniform vec2 scaling;
|
||||
layout(location = 5) uniform uint useTexture;
|
||||
|
||||
layout(rgba8, binding = 0) uniform restrict writeonly image2D outTexture;
|
||||
|
||||
layout(binding = 1) uniform sampler2D srcTexture;
|
||||
|
||||
|
||||
bool is_top_left(ivec2 a, ivec2 b)
|
||||
{
|
||||
return( (a.y == b.y && b.x < a.x)
|
||||
||(b.y < a.y));
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//TODO: we should do these computations on 64bits, because otherwise
|
||||
// we might overflow for values > 2048.
|
||||
// Unfortunately this is costly.
|
||||
// Another way is to precompute triangle edges (b - a) in full precision
|
||||
// once to avoid doing it all the time...
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
int orient2d(ivec2 a, ivec2 b, ivec2 p)
|
||||
{
|
||||
return((b.x-a.x)*(p.y-a.y) - (b.y-a.y)*(p.x-a.x));
|
||||
}
|
||||
|
||||
int is_clockwise(ivec2 p0, ivec2 p1, ivec2 p2)
|
||||
{
|
||||
return((p1 - p0).x*(p2 - p0).y - (p1 - p0).y*(p2 - p0).x);
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
ivec2 pixelCoord = ivec2(gl_WorkGroupID.xy*uvec2(16, 16) + gl_LocalInvocationID.xy);
|
||||
uvec2 tileCoord = uvec2(pixelCoord) / tileSize;
|
||||
uint tileIndex = tileCoord.y * tileCount.x + tileCoord.x;
|
||||
uint tileCounter = min(tileCounterBuffer.elements[tileIndex], tileArraySize);
|
||||
|
||||
const float subPixelFactor = 16.;
|
||||
ivec2 centerPoint = ivec2((vec2(pixelCoord) + vec2(0.5, 0.5)) * subPixelFactor);
|
||||
|
||||
//*
|
||||
const int sampleCount = 8;
|
||||
ivec2 samplePoints[sampleCount] = ivec2[sampleCount](centerPoint + ivec2(1, 3),
|
||||
centerPoint + ivec2(-1, -3),
|
||||
centerPoint + ivec2(5, -1),
|
||||
centerPoint + ivec2(-3, 5),
|
||||
centerPoint + ivec2(-5, -5),
|
||||
centerPoint + ivec2(-7, 1),
|
||||
centerPoint + ivec2(3, -7),
|
||||
centerPoint + ivec2(7, 7));
|
||||
/*/
|
||||
const int sampleCount = 4;
|
||||
ivec2 samplePoints[sampleCount] = ivec2[sampleCount](centerPoint + ivec2(-2, 6),
|
||||
centerPoint + ivec2(6, 2),
|
||||
centerPoint + ivec2(-6, -2),
|
||||
centerPoint + ivec2(2, -6));
|
||||
//*/
|
||||
//DEBUG
|
||||
/*
|
||||
{
|
||||
vec4 fragColor = vec4(0);
|
||||
|
||||
if( pixelCoord.x % 16 == 0
|
||||
||pixelCoord.y % 16 == 0)
|
||||
{
|
||||
fragColor = vec4(0, 0, 0, 1);
|
||||
}
|
||||
else if(tileCounterBuffer.elements[tileIndex] == 0xffffu)
|
||||
{
|
||||
fragColor = vec4(1, 0, 1, 1);
|
||||
}
|
||||
else if(tileCounter != 0u)
|
||||
{
|
||||
fragColor = vec4(0, 1, 0, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
fragColor = vec4(1, 0, 0, 1);
|
||||
}
|
||||
imageStore(outTexture, pixelCoord, fragColor);
|
||||
return;
|
||||
}
|
||||
//*/
|
||||
//----
|
||||
|
||||
vec4 sampleColor[sampleCount];
|
||||
vec4 currentColor[sampleCount];
|
||||
int currentShapeIndex[sampleCount];
|
||||
int flipCount[sampleCount];
|
||||
|
||||
for(int i=0; i<sampleCount; i++)
|
||||
{
|
||||
currentShapeIndex[i] = -1;
|
||||
flipCount[i] = 0;
|
||||
sampleColor[i] = vec4(0, 0, 0, 0);
|
||||
currentColor[i] = vec4(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
for(uint tileArrayIndex=0u; tileArrayIndex < tileCounter; tileArrayIndex++)
|
||||
{
|
||||
uint triangleIndex = tileArrayBuffer.elements[tileArraySize * tileIndex + tileArrayIndex];
|
||||
|
||||
uint i0 = indexBuffer.elements[triangleIndex];
|
||||
uint i1 = indexBuffer.elements[triangleIndex+1u];
|
||||
uint i2 = indexBuffer.elements[triangleIndex+2u];
|
||||
|
||||
ivec2 p0 = ivec2((vertexBuffer.elements[i0].pos * scaling) * subPixelFactor);
|
||||
ivec2 p1 = ivec2((vertexBuffer.elements[i1].pos * scaling) * subPixelFactor);
|
||||
ivec2 p2 = ivec2((vertexBuffer.elements[i2].pos * scaling) * subPixelFactor);
|
||||
|
||||
int shapeIndex = vertexBuffer.elements[i0].shapeIndex;
|
||||
vec4 color = shapeBuffer.elements[shapeIndex].color;
|
||||
color.rgb *= color.a;
|
||||
|
||||
ivec4 clip = ivec4(round((shapeBuffer.elements[shapeIndex].clip * vec4(scaling, scaling) + vec4(0.5, 0.5, 0.5, 0.5)) * subPixelFactor));
|
||||
|
||||
mat3 uvTransform = mat3(shapeBuffer.elements[shapeIndex].uvTransform[0],
|
||||
shapeBuffer.elements[shapeIndex].uvTransform[3],
|
||||
0.,
|
||||
shapeBuffer.elements[shapeIndex].uvTransform[1],
|
||||
shapeBuffer.elements[shapeIndex].uvTransform[4],
|
||||
0.,
|
||||
shapeBuffer.elements[shapeIndex].uvTransform[2],
|
||||
shapeBuffer.elements[shapeIndex].uvTransform[5],
|
||||
1.);
|
||||
|
||||
//NOTE(martin): reorder triangle counter-clockwise and compute bias for each edge
|
||||
int cw = is_clockwise(p0, p1, p2);
|
||||
if(cw < 0)
|
||||
{
|
||||
uint tmpIndex = i1;
|
||||
i1 = i2;
|
||||
i2 = tmpIndex;
|
||||
|
||||
ivec2 tmpPoint = p1;
|
||||
p1 = p2;
|
||||
p2 = tmpPoint;
|
||||
}
|
||||
|
||||
vec4 cubic0 = vertexBuffer.elements[i0].cubic;
|
||||
vec4 cubic1 = vertexBuffer.elements[i1].cubic;
|
||||
vec4 cubic2 = vertexBuffer.elements[i2].cubic;
|
||||
|
||||
int bias0 = is_top_left(p1, p2) ? 0 : -1;
|
||||
int bias1 = is_top_left(p2, p0) ? 0 : -1;
|
||||
int bias2 = is_top_left(p0, p1) ? 0 : -1;
|
||||
|
||||
for(int sampleIndex = 0; sampleIndex < sampleCount; sampleIndex++)
|
||||
{
|
||||
ivec2 samplePoint = samplePoints[sampleIndex];
|
||||
|
||||
if( samplePoint.x < clip.x
|
||||
|| samplePoint.x > clip.z
|
||||
|| samplePoint.y < clip.y
|
||||
|| samplePoint.y > clip.w)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int w0 = orient2d(p1, p2, samplePoint);
|
||||
int w1 = orient2d(p2, p0, samplePoint);
|
||||
int w2 = orient2d(p0, p1, samplePoint);
|
||||
|
||||
if((w0+bias0) >= 0 && (w1+bias1) >= 0 && (w2+bias2) >= 0)
|
||||
{
|
||||
vec4 cubic = (cubic0*float(w0) + cubic1*float(w1) + cubic2*float(w2))/(float(w0)+float(w1)+float(w2));
|
||||
|
||||
float eps = 0.0001;
|
||||
if(cubic.w*(cubic.x*cubic.x*cubic.x - cubic.y*cubic.z) <= eps)
|
||||
{
|
||||
if(shapeIndex == currentShapeIndex[sampleIndex])
|
||||
{
|
||||
flipCount[sampleIndex]++;
|
||||
}
|
||||
else
|
||||
{
|
||||
if((flipCount[sampleIndex] & 0x01) != 0)
|
||||
{
|
||||
sampleColor[sampleIndex] = currentColor[sampleIndex];
|
||||
}
|
||||
|
||||
vec4 nextColor = color;
|
||||
if(useTexture)
|
||||
{
|
||||
vec3 sampleFP = vec3(vec2(samplePoint).xy/(subPixelFactor*2.), 1);
|
||||
vec2 uv = (uvTransform * sampleFP).xy;
|
||||
vec4 texColor = texture(srcTexture, uv);
|
||||
texColor.rgb *= texColor.a;
|
||||
nextColor *= texColor;
|
||||
}
|
||||
currentColor[sampleIndex] = sampleColor[sampleIndex]*(1.-nextColor.a) + nextColor;
|
||||
currentShapeIndex[sampleIndex] = shapeIndex;
|
||||
flipCount[sampleIndex] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
vec4 pixelColor = vec4(0);
|
||||
for(int sampleIndex = 0; sampleIndex < sampleCount; sampleIndex++)
|
||||
{
|
||||
if((flipCount[sampleIndex] & 0x01) != 0)
|
||||
{
|
||||
sampleColor[sampleIndex] = currentColor[sampleIndex];
|
||||
}
|
||||
pixelColor += sampleColor[sampleIndex];
|
||||
}
|
||||
|
||||
imageStore(outTexture, pixelCoord, pixelColor/float(sampleCount));
|
||||
}
|
||||
|
|
|
@ -1,61 +1,61 @@
|
|||
|
||||
layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
|
||||
|
||||
precision mediump float;
|
||||
|
||||
layout(binding = 0) restrict readonly buffer vertexBufferSSBO {
|
||||
vertex elements[];
|
||||
} vertexBuffer ;
|
||||
|
||||
layout(binding = 1) restrict readonly buffer shapeBufferSSBO {
|
||||
shape elements[];
|
||||
} shapeBuffer ;
|
||||
|
||||
layout(binding = 2) restrict readonly buffer indexBufferSSBO {
|
||||
uint elements[];
|
||||
} indexBuffer ;
|
||||
|
||||
layout(binding = 3) coherent readonly restrict buffer tileCounterBufferSSBO {
|
||||
uint elements[];
|
||||
} tileCounterBuffer ;
|
||||
|
||||
layout(binding = 4) coherent restrict buffer tileArrayBufferSSBO {
|
||||
uint elements[];
|
||||
} tileArrayBuffer ;
|
||||
|
||||
layout(location = 0) uniform uint indexCount;
|
||||
layout(location = 1) uniform uvec2 tileCount;
|
||||
layout(location = 2) uniform uint tileSize;
|
||||
layout(location = 3) uniform uint tileArraySize;
|
||||
|
||||
int get_shape_index(uint tileArrayOffset, uint tileArrayIndex)
|
||||
{
|
||||
uint triangleIndex = tileArrayBuffer.elements[tileArrayOffset + tileArrayIndex];
|
||||
uint i0 = indexBuffer.elements[triangleIndex];
|
||||
int shapeIndex = vertexBuffer.elements[i0].shapeIndex;
|
||||
return(shapeIndex);
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
uint tileIndex = gl_WorkGroupID.x;
|
||||
uint tileArrayOffset = tileArraySize * tileIndex;
|
||||
uint tileArrayCount = min(tileCounterBuffer.elements[tileIndex], tileArraySize);
|
||||
|
||||
for(uint tileArrayIndex=1u; tileArrayIndex < tileArrayCount; tileArrayIndex++)
|
||||
{
|
||||
for(uint sortIndex = tileArrayIndex; sortIndex > 0u; sortIndex--)
|
||||
{
|
||||
int shapeIndex = get_shape_index(tileArrayOffset, sortIndex);
|
||||
int prevShapeIndex = get_shape_index(tileArrayOffset, sortIndex-1u);
|
||||
|
||||
if(shapeIndex >= prevShapeIndex)
|
||||
{
|
||||
break;
|
||||
}
|
||||
uint tmp = tileArrayBuffer.elements[tileArrayOffset + sortIndex];
|
||||
tileArrayBuffer.elements[tileArrayOffset + sortIndex] = tileArrayBuffer.elements[tileArrayOffset + sortIndex - 1u];
|
||||
tileArrayBuffer.elements[tileArrayOffset + sortIndex - 1u] = tmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
|
||||
|
||||
precision mediump float;
|
||||
|
||||
layout(binding = 0) restrict readonly buffer vertexBufferSSBO {
|
||||
vertex elements[];
|
||||
} vertexBuffer ;
|
||||
|
||||
layout(binding = 1) restrict readonly buffer shapeBufferSSBO {
|
||||
shape elements[];
|
||||
} shapeBuffer ;
|
||||
|
||||
layout(binding = 2) restrict readonly buffer indexBufferSSBO {
|
||||
uint elements[];
|
||||
} indexBuffer ;
|
||||
|
||||
layout(binding = 3) coherent readonly restrict buffer tileCounterBufferSSBO {
|
||||
uint elements[];
|
||||
} tileCounterBuffer ;
|
||||
|
||||
layout(binding = 4) coherent restrict buffer tileArrayBufferSSBO {
|
||||
uint elements[];
|
||||
} tileArrayBuffer ;
|
||||
|
||||
layout(location = 0) uniform uint indexCount;
|
||||
layout(location = 1) uniform uvec2 tileCount;
|
||||
layout(location = 2) uniform uint tileSize;
|
||||
layout(location = 3) uniform uint tileArraySize;
|
||||
|
||||
int get_shape_index(uint tileArrayOffset, uint tileArrayIndex)
|
||||
{
|
||||
uint triangleIndex = tileArrayBuffer.elements[tileArrayOffset + tileArrayIndex];
|
||||
uint i0 = indexBuffer.elements[triangleIndex];
|
||||
int shapeIndex = vertexBuffer.elements[i0].shapeIndex;
|
||||
return(shapeIndex);
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
uint tileIndex = gl_WorkGroupID.x;
|
||||
uint tileArrayOffset = tileArraySize * tileIndex;
|
||||
uint tileArrayCount = min(tileCounterBuffer.elements[tileIndex], tileArraySize);
|
||||
|
||||
for(uint tileArrayIndex=1u; tileArrayIndex < tileArrayCount; tileArrayIndex++)
|
||||
{
|
||||
for(uint sortIndex = tileArrayIndex; sortIndex > 0u; sortIndex--)
|
||||
{
|
||||
int shapeIndex = get_shape_index(tileArrayOffset, sortIndex);
|
||||
int prevShapeIndex = get_shape_index(tileArrayOffset, sortIndex-1u);
|
||||
|
||||
if(shapeIndex >= prevShapeIndex)
|
||||
{
|
||||
break;
|
||||
}
|
||||
uint tmp = tileArrayBuffer.elements[tileArrayOffset + sortIndex];
|
||||
tileArrayBuffer.elements[tileArrayOffset + sortIndex] = tileArrayBuffer.elements[tileArrayOffset + sortIndex - 1u];
|
||||
tileArrayBuffer.elements[tileArrayOffset + sortIndex - 1u] = tmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,77 +1,77 @@
|
|||
|
||||
layout(local_size_x = 512, local_size_y = 1, local_size_z = 1) in;
|
||||
|
||||
precision mediump float;
|
||||
|
||||
layout(binding = 0) restrict readonly buffer vertexBufferSSBO {
|
||||
vertex elements[];
|
||||
} vertexBuffer ;
|
||||
|
||||
layout(binding = 1) restrict readonly buffer shapeBufferSSBO {
|
||||
shape elements[];
|
||||
} shapeBuffer ;
|
||||
|
||||
layout(binding = 2) restrict readonly buffer indexBufferSSBO {
|
||||
uint elements[];
|
||||
} indexBuffer ;
|
||||
|
||||
layout(binding = 3) coherent restrict buffer tileCounterBufferSSBO {
|
||||
uint elements[];
|
||||
} tileCounterBuffer ;
|
||||
|
||||
layout(binding = 4) coherent restrict writeonly buffer tileArrayBufferSSBO {
|
||||
uint elements[];
|
||||
} tileArrayBuffer ;
|
||||
|
||||
layout(location = 0) uniform uint indexCount;
|
||||
layout(location = 1) uniform uvec2 tileCount;
|
||||
layout(location = 2) uniform uint tileSize;
|
||||
layout(location = 3) uniform uint tileArraySize;
|
||||
layout(location = 4) uniform vec2 scaling;
|
||||
|
||||
void main()
|
||||
{
|
||||
uint triangleIndex = (gl_WorkGroupID.x*gl_WorkGroupSize.x + gl_LocalInvocationIndex) * 3u;
|
||||
if(triangleIndex >= indexCount)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
uint i0 = indexBuffer.elements[triangleIndex];
|
||||
uint i1 = indexBuffer.elements[triangleIndex+1u];
|
||||
uint i2 = indexBuffer.elements[triangleIndex+2u];
|
||||
|
||||
vec2 p0 = vertexBuffer.elements[i0].pos * scaling;
|
||||
vec2 p1 = vertexBuffer.elements[i1].pos * scaling;
|
||||
vec2 p2 = vertexBuffer.elements[i2].pos * scaling;
|
||||
|
||||
int shapeIndex = vertexBuffer.elements[i0].shapeIndex;
|
||||
vec4 clip = shapeBuffer.elements[shapeIndex].clip * vec4(scaling, scaling);
|
||||
|
||||
vec4 fbox = vec4(max(min(min(p0.x, p1.x), p2.x), clip.x),
|
||||
max(min(min(p0.y, p1.y), p2.y), clip.y),
|
||||
min(max(max(p0.x, p1.x), p2.x), clip.z),
|
||||
min(max(max(p0.y, p1.y), p2.y), clip.w));
|
||||
|
||||
ivec4 box = ivec4(floor(fbox))/int(tileSize);
|
||||
|
||||
//NOTE(martin): it's importat to do the computation with signed int, so that we can have negative xMax/yMax
|
||||
// otherwise all triangles on the left or below the x/y axis are attributed to tiles on row/column 0.
|
||||
int xMin = max(0, box.x);
|
||||
int yMin = max(0, box.y);
|
||||
int xMax = min(box.z, int(tileCount.x) - 1);
|
||||
int yMax = min(box.w, int(tileCount.y) - 1);
|
||||
|
||||
for(int y = yMin; y <= yMax; y++)
|
||||
{
|
||||
for(int x = xMin ; x <= xMax; x++)
|
||||
{
|
||||
uint tileIndex = uint(y)*tileCount.x + uint(x);
|
||||
uint tileCounter = atomicAdd(tileCounterBuffer.elements[tileIndex], 1u);
|
||||
if(tileCounter < tileArraySize)
|
||||
{
|
||||
tileArrayBuffer.elements[tileArraySize*tileIndex + tileCounter] = triangleIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
layout(local_size_x = 512, local_size_y = 1, local_size_z = 1) in;
|
||||
|
||||
precision mediump float;
|
||||
|
||||
layout(binding = 0) restrict readonly buffer vertexBufferSSBO {
|
||||
vertex elements[];
|
||||
} vertexBuffer ;
|
||||
|
||||
layout(binding = 1) restrict readonly buffer shapeBufferSSBO {
|
||||
shape elements[];
|
||||
} shapeBuffer ;
|
||||
|
||||
layout(binding = 2) restrict readonly buffer indexBufferSSBO {
|
||||
uint elements[];
|
||||
} indexBuffer ;
|
||||
|
||||
layout(binding = 3) coherent restrict buffer tileCounterBufferSSBO {
|
||||
uint elements[];
|
||||
} tileCounterBuffer ;
|
||||
|
||||
layout(binding = 4) coherent restrict writeonly buffer tileArrayBufferSSBO {
|
||||
uint elements[];
|
||||
} tileArrayBuffer ;
|
||||
|
||||
layout(location = 0) uniform uint indexCount;
|
||||
layout(location = 1) uniform uvec2 tileCount;
|
||||
layout(location = 2) uniform uint tileSize;
|
||||
layout(location = 3) uniform uint tileArraySize;
|
||||
layout(location = 4) uniform vec2 scaling;
|
||||
|
||||
void main()
|
||||
{
|
||||
uint triangleIndex = (gl_WorkGroupID.x*gl_WorkGroupSize.x + gl_LocalInvocationIndex) * 3u;
|
||||
if(triangleIndex >= indexCount)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
uint i0 = indexBuffer.elements[triangleIndex];
|
||||
uint i1 = indexBuffer.elements[triangleIndex+1u];
|
||||
uint i2 = indexBuffer.elements[triangleIndex+2u];
|
||||
|
||||
vec2 p0 = vertexBuffer.elements[i0].pos * scaling;
|
||||
vec2 p1 = vertexBuffer.elements[i1].pos * scaling;
|
||||
vec2 p2 = vertexBuffer.elements[i2].pos * scaling;
|
||||
|
||||
int shapeIndex = vertexBuffer.elements[i0].shapeIndex;
|
||||
vec4 clip = shapeBuffer.elements[shapeIndex].clip * vec4(scaling, scaling);
|
||||
|
||||
vec4 fbox = vec4(max(min(min(p0.x, p1.x), p2.x), clip.x),
|
||||
max(min(min(p0.y, p1.y), p2.y), clip.y),
|
||||
min(max(max(p0.x, p1.x), p2.x), clip.z),
|
||||
min(max(max(p0.y, p1.y), p2.y), clip.w));
|
||||
|
||||
ivec4 box = ivec4(floor(fbox))/int(tileSize);
|
||||
|
||||
//NOTE(martin): it's importat to do the computation with signed int, so that we can have negative xMax/yMax
|
||||
// otherwise all triangles on the left or below the x/y axis are attributed to tiles on row/column 0.
|
||||
int xMin = max(0, box.x);
|
||||
int yMin = max(0, box.y);
|
||||
int xMax = min(box.z, int(tileCount.x) - 1);
|
||||
int yMax = min(box.w, int(tileCount.y) - 1);
|
||||
|
||||
for(int y = yMin; y <= yMax; y++)
|
||||
{
|
||||
for(int x = xMin ; x <= xMax; x++)
|
||||
{
|
||||
uint tileIndex = uint(y)*tileCount.x + uint(x);
|
||||
uint tileCounter = atomicAdd(tileCounterBuffer.elements[tileIndex], 1u);
|
||||
if(tileCounter < tileArraySize)
|
||||
{
|
||||
tileArrayBuffer.elements[tileArraySize*tileIndex + tileCounter] = triangleIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
7720
src/graphics.c
7720
src/graphics.c
File diff suppressed because it is too large
Load Diff
646
src/graphics.h
646
src/graphics.h
|
@ -1,323 +1,323 @@
|
|||
/************************************************************//**
|
||||
*
|
||||
* @file: graphics.h
|
||||
* @author: Martin Fouilleul
|
||||
* @date: 23/01/2023
|
||||
* @revision:
|
||||
*
|
||||
*****************************************************************/
|
||||
#ifndef __GRAPHICS_H_
|
||||
#define __GRAPHICS_H_
|
||||
|
||||
#include"typedefs.h"
|
||||
#include"platform.h"
|
||||
#include"mp_app.h"
|
||||
|
||||
//------------------------------------------------------------------------------------------
|
||||
//NOTE(martin): backends selection
|
||||
//------------------------------------------------------------------------------------------
|
||||
|
||||
typedef enum {
|
||||
MG_BACKEND_NONE,
|
||||
MG_BACKEND_METAL,
|
||||
MG_BACKEND_GL,
|
||||
MG_BACKEND_GLES,
|
||||
MG_BACKEND_HOST } mg_backend_id;
|
||||
|
||||
//NOTE: these macros are used to select which backend to include when building milepost
|
||||
// they can be overridden by passing them to the compiler command line
|
||||
#if defined(OS_MACOS)
|
||||
#ifndef MG_COMPILE_BACKEND_METAL
|
||||
#define MG_COMPILE_BACKEND_METAL 1
|
||||
#endif
|
||||
|
||||
#ifndef MG_COMPILE_BACKEND_GLES
|
||||
#define MG_COMPILE_BACKEND_GLES 1
|
||||
#endif
|
||||
|
||||
#define MG_COMPILE_BACKEND_GL 0
|
||||
|
||||
#if MG_COMPILE_BACKEND_METAL
|
||||
#define MG_BACKEND_DEFAULT MG_BACKEND_METAL
|
||||
#elif MG_COMPILE_BACKEND_GL
|
||||
#define MG_BACKEND_DEFAULT MG_BACKEND_GL
|
||||
#else
|
||||
#define MG_BACKEND_DEFAULT MG_BACKEND_NONE
|
||||
#endif
|
||||
|
||||
#elif defined(OS_WIN64)
|
||||
#ifndef MG_COMPILE_BACKEND_GL
|
||||
#define MG_COMPILE_BACKEND_GL 1
|
||||
#endif
|
||||
|
||||
#ifndef MG_COMPILE_BACKEND_GLES
|
||||
#define MG_COMPILE_BACKEND_GLES 1
|
||||
#endif
|
||||
|
||||
#if MG_COMPILE_BACKEND_GL
|
||||
#define MG_BACKEND_DEFAULT MG_BACKEND_GL
|
||||
#else
|
||||
#define MG_BACKEND_DEFAULT MG_BACKEND_NONE
|
||||
#endif
|
||||
|
||||
#elif defined(OS_LINUX)
|
||||
#ifndef MG_COMPILE_BACKEND_GL
|
||||
#define MG_COMPILE_BACKEND_GL 1
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
//NOTE: these macros are used to select backend-specific APIs to include when using milepost
|
||||
#ifdef MG_EXPOSE_SURFACE_METAL
|
||||
#include"mtl_surface.h"
|
||||
#endif
|
||||
|
||||
#ifdef MG_EXPOSE_SURFACE_WGL
|
||||
#include"wgl_surface.h"
|
||||
#endif
|
||||
|
||||
//TODO: expose nsgl surface when supported, expose egl surface, etc...
|
||||
|
||||
//TODO: add MG_INCLUDE_OPENGL/GLES/etc, once we know how we make different gl versions co-exist
|
||||
|
||||
MP_API bool mg_is_surface_backend_available(mg_backend_id id);
|
||||
MP_API bool mg_is_canvas_backend_available(mg_backend_id id);
|
||||
|
||||
//------------------------------------------------------------------------------------------
|
||||
//NOTE(martin): graphics surface
|
||||
//------------------------------------------------------------------------------------------
|
||||
typedef struct mg_surface { u64 h; } mg_surface;
|
||||
|
||||
MP_API mg_surface mg_surface_nil();
|
||||
MP_API bool mg_surface_is_nil(mg_surface surface);
|
||||
|
||||
MP_API mg_surface mg_surface_create_for_window(mp_window window, mg_backend_id backend);
|
||||
MP_API void mg_surface_destroy(mg_surface surface);
|
||||
MP_API void mg_surface_prepare(mg_surface surface);
|
||||
MP_API void mg_surface_present(mg_surface surface);
|
||||
MP_API void mg_surface_swap_interval(mg_surface surface, int swap);
|
||||
MP_API vec2 mg_surface_contents_scaling(mg_surface surface);
|
||||
MP_API mp_rect mg_surface_get_frame(mg_surface surface);
|
||||
MP_API void mg_surface_set_frame(mg_surface surface, mp_rect frame);
|
||||
MP_API bool mg_surface_get_hidden(mg_surface surface);
|
||||
MP_API void mg_surface_set_hidden(mg_surface surface, bool hidden);
|
||||
|
||||
//NOTE(martin): surface sharing
|
||||
typedef u64 mg_surface_id;
|
||||
|
||||
MP_API mg_surface mg_surface_create_remote(u32 width, u32 height, mg_backend_id backend);
|
||||
MP_API mg_surface mg_surface_create_host(mp_window window);
|
||||
MP_API mg_surface_id mg_surface_remote_id(mg_surface surface);
|
||||
MP_API void mg_surface_host_connect(mg_surface surface, mg_surface_id remoteId);
|
||||
|
||||
//------------------------------------------------------------------------------------------
|
||||
//NOTE(martin): graphics canvas structs
|
||||
//------------------------------------------------------------------------------------------
|
||||
typedef struct mg_canvas { u64 h; } mg_canvas;
|
||||
typedef struct mg_font { u64 h; } mg_font;
|
||||
typedef struct mg_image { u64 h; } mg_image;
|
||||
|
||||
typedef struct mg_mat2x3
|
||||
{
|
||||
f32 m[6];
|
||||
} mg_mat2x3;
|
||||
|
||||
typedef struct mg_color
|
||||
{
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
f32 r;
|
||||
f32 g;
|
||||
f32 b;
|
||||
f32 a;
|
||||
};
|
||||
f32 c[4];
|
||||
};
|
||||
} mg_color;
|
||||
|
||||
typedef enum {MG_JOINT_MITER = 0,
|
||||
MG_JOINT_BEVEL,
|
||||
MG_JOINT_NONE } mg_joint_type;
|
||||
|
||||
typedef enum {MG_CAP_NONE = 0,
|
||||
MG_CAP_SQUARE } mg_cap_type;
|
||||
|
||||
typedef struct mg_font_extents
|
||||
{
|
||||
f32 ascent; // the extent above the baseline (by convention a positive value extends above the baseline)
|
||||
f32 descent; // the extent below the baseline (by convention, positive value extends below the baseline)
|
||||
f32 leading; // spacing between one row's descent and the next row's ascent
|
||||
f32 xHeight; // height of the lower case letter 'x'
|
||||
f32 capHeight; // height of the upper case letter 'M'
|
||||
f32 width; // maximum width of the font
|
||||
|
||||
} mg_font_extents;
|
||||
|
||||
typedef struct mg_text_extents
|
||||
{
|
||||
f32 xBearing;
|
||||
f32 yBearing;
|
||||
f32 width;
|
||||
f32 height;
|
||||
f32 xAdvance;
|
||||
f32 yAdvance;
|
||||
|
||||
} mg_text_extents;
|
||||
|
||||
//------------------------------------------------------------------------------------------
|
||||
//NOTE(martin): graphics canvas
|
||||
//------------------------------------------------------------------------------------------
|
||||
MP_API mg_canvas mg_canvas_nil();
|
||||
MP_API bool mg_canvas_is_nil(mg_canvas canvas);
|
||||
|
||||
MP_API mg_canvas mg_canvas_create(mg_surface surface);
|
||||
MP_API void mg_canvas_destroy(mg_canvas canvas);
|
||||
MP_API mg_canvas mg_canvas_set_current(mg_canvas canvas);
|
||||
|
||||
MP_API void mg_flush();
|
||||
|
||||
//------------------------------------------------------------------------------------------
|
||||
//NOTE(martin): fonts
|
||||
//------------------------------------------------------------------------------------------
|
||||
MP_API mg_font mg_font_nil();
|
||||
MP_API mg_font mg_font_create_from_memory(u32 size, byte* buffer, u32 rangeCount, unicode_range* ranges);
|
||||
MP_API void mg_font_destroy(mg_font font);
|
||||
|
||||
//NOTE(martin): the following int valued functions return -1 if font is invalid or codepoint is not present in font//
|
||||
//TODO(martin): add enum error codes
|
||||
|
||||
MP_API mg_font_extents mg_font_get_extents(mg_font font);
|
||||
MP_API mg_font_extents mg_font_get_scaled_extents(mg_font font, f32 emSize);
|
||||
MP_API f32 mg_font_get_scale_for_em_pixels(mg_font font, f32 emSize);
|
||||
|
||||
//NOTE(martin): if you need to process more than one codepoint, first convert your codepoints to glyph indices, then use the
|
||||
// glyph index versions of the functions, which can take an array of glyph indices.
|
||||
|
||||
MP_API str32 mg_font_get_glyph_indices(mg_font font, str32 codePoints, str32 backing);
|
||||
MP_API str32 mg_font_push_glyph_indices(mg_font font, mem_arena* arena, str32 codePoints);
|
||||
MP_API u32 mg_font_get_glyph_index(mg_font font, utf32 codePoint);
|
||||
|
||||
MP_API int mg_font_get_codepoint_extents(mg_font font, utf32 codePoint, mg_text_extents* outExtents);
|
||||
|
||||
MP_API int mg_font_get_glyph_extents(mg_font font, str32 glyphIndices, mg_text_extents* outExtents);
|
||||
|
||||
MP_API mp_rect mg_text_bounding_box_utf32(mg_font font, f32 fontSize, str32 text);
|
||||
MP_API mp_rect mg_text_bounding_box(mg_font font, f32 fontSize, str8 text);
|
||||
|
||||
//------------------------------------------------------------------------------------------
|
||||
//NOTE(martin): images
|
||||
//------------------------------------------------------------------------------------------
|
||||
MP_API mg_image mg_image_nil();
|
||||
MP_API bool mg_image_is_nil(mg_image a);
|
||||
|
||||
MP_API mg_image mg_image_create(u32 width, u32 height);
|
||||
MP_API mg_image mg_image_create_from_rgba8(u32 width, u32 height, u8* pixels);
|
||||
MP_API mg_image mg_image_create_from_data(str8 data, bool flip);
|
||||
MP_API mg_image mg_image_create_from_file(str8 path, bool flip);
|
||||
|
||||
MP_API void mg_image_destroy(mg_image image);
|
||||
|
||||
MP_API void mg_image_upload_region_rgba8(mg_image image, mp_rect region, u8* pixels);
|
||||
MP_API vec2 mg_image_size(mg_image image);
|
||||
|
||||
//------------------------------------------------------------------------------------------
|
||||
//NOTE(martin): atlasing
|
||||
//------------------------------------------------------------------------------------------
|
||||
|
||||
//NOTE: rectangle allocator
|
||||
typedef struct mg_rect_atlas mg_rect_atlas;
|
||||
|
||||
mg_rect_atlas* mg_rect_atlas_create(mem_arena* arena, i32 width, i32 height);
|
||||
mp_rect mg_rect_atlas_alloc(mg_rect_atlas* atlas, i32 width, i32 height);
|
||||
void mg_rect_atlas_recycle(mg_rect_atlas* atlas, mp_rect rect);
|
||||
|
||||
//NOTE: image atlas helpers
|
||||
typedef struct mg_image_region
|
||||
{
|
||||
mg_image image;
|
||||
mp_rect rect;
|
||||
} mg_image_region;
|
||||
|
||||
mg_image_region mg_image_atlas_alloc_from_rgba8(mg_rect_atlas* atlas, mg_image backingImage, u32 width, u32 height, u8* pixels);
|
||||
mg_image_region mg_image_atlas_alloc_from_data(mg_rect_atlas* atlas, mg_image backingImage, str8 data, bool flip);
|
||||
mg_image_region mg_image_atlas_alloc_from_file(mg_rect_atlas* atlas, mg_image backingImage, str8 path, bool flip);
|
||||
void mg_image_atlas_recycle(mg_rect_atlas* atlas, mg_image_region imageRgn);
|
||||
|
||||
//------------------------------------------------------------------------------------------
|
||||
//NOTE(martin): transform, viewport and clipping
|
||||
//------------------------------------------------------------------------------------------
|
||||
MP_API void mg_viewport(mp_rect viewPort);
|
||||
|
||||
MP_API void mg_matrix_push(mg_mat2x3 matrix);
|
||||
MP_API void mg_matrix_pop();
|
||||
|
||||
MP_API void mg_clip_push(f32 x, f32 y, f32 w, f32 h);
|
||||
MP_API void mg_clip_pop();
|
||||
|
||||
//------------------------------------------------------------------------------------------
|
||||
//NOTE(martin): graphics attributes setting/getting
|
||||
//------------------------------------------------------------------------------------------
|
||||
MP_API void mg_set_color(mg_color color);
|
||||
MP_API void mg_set_color_rgba(f32 r, f32 g, f32 b, f32 a);
|
||||
MP_API void mg_set_width(f32 width);
|
||||
MP_API void mg_set_tolerance(f32 tolerance);
|
||||
MP_API void mg_set_joint(mg_joint_type joint);
|
||||
MP_API void mg_set_max_joint_excursion(f32 maxJointExcursion);
|
||||
MP_API void mg_set_cap(mg_cap_type cap);
|
||||
MP_API void mg_set_font(mg_font font);
|
||||
MP_API void mg_set_font_size(f32 size);
|
||||
MP_API void mg_set_text_flip(bool flip);
|
||||
MP_API void mg_set_image(mg_image image);
|
||||
MP_API void mg_set_image_source_region(mp_rect region);
|
||||
|
||||
MP_API mg_color mg_get_color();
|
||||
MP_API f32 mg_get_width();
|
||||
MP_API f32 mg_get_tolerance();
|
||||
MP_API mg_joint_type mg_get_joint();
|
||||
MP_API f32 mg_get_max_joint_excursion();
|
||||
MP_API mg_cap_type mg_get_cap();
|
||||
MP_API mg_font mg_get_font();
|
||||
MP_API f32 mg_get_font_size();
|
||||
MP_API bool mg_get_text_flip();
|
||||
|
||||
//------------------------------------------------------------------------------------------
|
||||
//NOTE(martin): path construction
|
||||
//------------------------------------------------------------------------------------------
|
||||
MP_API vec2 mg_get_position();
|
||||
MP_API void mg_move_to(f32 x, f32 y);
|
||||
MP_API void mg_line_to(f32 x, f32 y);
|
||||
MP_API void mg_quadratic_to(f32 x1, f32 y1, f32 x2, f32 y2);
|
||||
MP_API void mg_cubic_to(f32 x1, f32 y1, f32 x2, f32 y2, f32 x3, f32 y3);
|
||||
MP_API void mg_close_path();
|
||||
|
||||
MP_API mp_rect mg_glyph_outlines(str32 glyphIndices);
|
||||
MP_API void mg_codepoints_outlines(str32 string);
|
||||
MP_API void mg_text_outlines(str8 string);
|
||||
|
||||
//------------------------------------------------------------------------------------------
|
||||
//NOTE(martin): clear/fill/stroke
|
||||
//------------------------------------------------------------------------------------------
|
||||
MP_API void mg_clear();
|
||||
MP_API void mg_fill();
|
||||
MP_API void mg_stroke();
|
||||
|
||||
//------------------------------------------------------------------------------------------
|
||||
//NOTE(martin): 'fast' shapes primitives
|
||||
//------------------------------------------------------------------------------------------
|
||||
MP_API void mg_rectangle_fill(f32 x, f32 y, f32 w, f32 h);
|
||||
MP_API void mg_rectangle_stroke(f32 x, f32 y, f32 w, f32 h);
|
||||
MP_API void mg_rounded_rectangle_fill(f32 x, f32 y, f32 w, f32 h, f32 r);
|
||||
MP_API void mg_rounded_rectangle_stroke(f32 x, f32 y, f32 w, f32 h, f32 r);
|
||||
MP_API void mg_ellipse_fill(f32 x, f32 y, f32 rx, f32 ry);
|
||||
MP_API void mg_ellipse_stroke(f32 x, f32 y, f32 rx, f32 ry);
|
||||
MP_API void mg_circle_fill(f32 x, f32 y, f32 r);
|
||||
MP_API void mg_circle_stroke(f32 x, f32 y, f32 r);
|
||||
MP_API void mg_arc(f32 x, f32 y, f32 r, f32 arcAngle, f32 startAngle);
|
||||
|
||||
//NOTE: image helpers
|
||||
MP_API void mg_image_draw(mg_image image, mp_rect rect);
|
||||
MP_API void mg_image_draw_region(mg_image image, mp_rect srcRegion, mp_rect dstRegion);
|
||||
|
||||
#endif //__GRAPHICS_H_
|
||||
/************************************************************//**
|
||||
*
|
||||
* @file: graphics.h
|
||||
* @author: Martin Fouilleul
|
||||
* @date: 23/01/2023
|
||||
* @revision:
|
||||
*
|
||||
*****************************************************************/
|
||||
#ifndef __GRAPHICS_H_
|
||||
#define __GRAPHICS_H_
|
||||
|
||||
#include"typedefs.h"
|
||||
#include"platform.h"
|
||||
#include"mp_app.h"
|
||||
|
||||
//------------------------------------------------------------------------------------------
|
||||
//NOTE(martin): backends selection
|
||||
//------------------------------------------------------------------------------------------
|
||||
|
||||
typedef enum {
|
||||
MG_BACKEND_NONE,
|
||||
MG_BACKEND_METAL,
|
||||
MG_BACKEND_GL,
|
||||
MG_BACKEND_GLES,
|
||||
MG_BACKEND_HOST } mg_backend_id;
|
||||
|
||||
//NOTE: these macros are used to select which backend to include when building milepost
|
||||
// they can be overridden by passing them to the compiler command line
|
||||
#if defined(OS_MACOS)
|
||||
#ifndef MG_COMPILE_BACKEND_METAL
|
||||
#define MG_COMPILE_BACKEND_METAL 1
|
||||
#endif
|
||||
|
||||
#ifndef MG_COMPILE_BACKEND_GLES
|
||||
#define MG_COMPILE_BACKEND_GLES 1
|
||||
#endif
|
||||
|
||||
#define MG_COMPILE_BACKEND_GL 0
|
||||
|
||||
#if MG_COMPILE_BACKEND_METAL
|
||||
#define MG_BACKEND_DEFAULT MG_BACKEND_METAL
|
||||
#elif MG_COMPILE_BACKEND_GL
|
||||
#define MG_BACKEND_DEFAULT MG_BACKEND_GL
|
||||
#else
|
||||
#define MG_BACKEND_DEFAULT MG_BACKEND_NONE
|
||||
#endif
|
||||
|
||||
#elif defined(OS_WIN64)
|
||||
#ifndef MG_COMPILE_BACKEND_GL
|
||||
#define MG_COMPILE_BACKEND_GL 1
|
||||
#endif
|
||||
|
||||
#ifndef MG_COMPILE_BACKEND_GLES
|
||||
#define MG_COMPILE_BACKEND_GLES 1
|
||||
#endif
|
||||
|
||||
#if MG_COMPILE_BACKEND_GL
|
||||
#define MG_BACKEND_DEFAULT MG_BACKEND_GL
|
||||
#else
|
||||
#define MG_BACKEND_DEFAULT MG_BACKEND_NONE
|
||||
#endif
|
||||
|
||||
#elif defined(OS_LINUX)
|
||||
#ifndef MG_COMPILE_BACKEND_GL
|
||||
#define MG_COMPILE_BACKEND_GL 1
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
//NOTE: these macros are used to select backend-specific APIs to include when using milepost
|
||||
#ifdef MG_EXPOSE_SURFACE_METAL
|
||||
#include"mtl_surface.h"
|
||||
#endif
|
||||
|
||||
#ifdef MG_EXPOSE_SURFACE_WGL
|
||||
#include"wgl_surface.h"
|
||||
#endif
|
||||
|
||||
//TODO: expose nsgl surface when supported, expose egl surface, etc...
|
||||
|
||||
//TODO: add MG_INCLUDE_OPENGL/GLES/etc, once we know how we make different gl versions co-exist
|
||||
|
||||
MP_API bool mg_is_surface_backend_available(mg_backend_id id);
|
||||
MP_API bool mg_is_canvas_backend_available(mg_backend_id id);
|
||||
|
||||
//------------------------------------------------------------------------------------------
|
||||
//NOTE(martin): graphics surface
|
||||
//------------------------------------------------------------------------------------------
|
||||
typedef struct mg_surface { u64 h; } mg_surface;
|
||||
|
||||
MP_API mg_surface mg_surface_nil();
|
||||
MP_API bool mg_surface_is_nil(mg_surface surface);
|
||||
|
||||
MP_API mg_surface mg_surface_create_for_window(mp_window window, mg_backend_id backend);
|
||||
MP_API void mg_surface_destroy(mg_surface surface);
|
||||
MP_API void mg_surface_prepare(mg_surface surface);
|
||||
MP_API void mg_surface_present(mg_surface surface);
|
||||
MP_API void mg_surface_swap_interval(mg_surface surface, int swap);
|
||||
MP_API vec2 mg_surface_contents_scaling(mg_surface surface);
|
||||
MP_API mp_rect mg_surface_get_frame(mg_surface surface);
|
||||
MP_API void mg_surface_set_frame(mg_surface surface, mp_rect frame);
|
||||
MP_API bool mg_surface_get_hidden(mg_surface surface);
|
||||
MP_API void mg_surface_set_hidden(mg_surface surface, bool hidden);
|
||||
|
||||
//NOTE(martin): surface sharing
|
||||
typedef u64 mg_surface_id;
|
||||
|
||||
MP_API mg_surface mg_surface_create_remote(u32 width, u32 height, mg_backend_id backend);
|
||||
MP_API mg_surface mg_surface_create_host(mp_window window);
|
||||
MP_API mg_surface_id mg_surface_remote_id(mg_surface surface);
|
||||
MP_API void mg_surface_host_connect(mg_surface surface, mg_surface_id remoteId);
|
||||
|
||||
//------------------------------------------------------------------------------------------
|
||||
//NOTE(martin): graphics canvas structs
|
||||
//------------------------------------------------------------------------------------------
|
||||
typedef struct mg_canvas { u64 h; } mg_canvas;
|
||||
typedef struct mg_font { u64 h; } mg_font;
|
||||
typedef struct mg_image { u64 h; } mg_image;
|
||||
|
||||
typedef struct mg_mat2x3
|
||||
{
|
||||
f32 m[6];
|
||||
} mg_mat2x3;
|
||||
|
||||
typedef struct mg_color
|
||||
{
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
f32 r;
|
||||
f32 g;
|
||||
f32 b;
|
||||
f32 a;
|
||||
};
|
||||
f32 c[4];
|
||||
};
|
||||
} mg_color;
|
||||
|
||||
typedef enum {MG_JOINT_MITER = 0,
|
||||
MG_JOINT_BEVEL,
|
||||
MG_JOINT_NONE } mg_joint_type;
|
||||
|
||||
typedef enum {MG_CAP_NONE = 0,
|
||||
MG_CAP_SQUARE } mg_cap_type;
|
||||
|
||||
typedef struct mg_font_extents
|
||||
{
|
||||
f32 ascent; // the extent above the baseline (by convention a positive value extends above the baseline)
|
||||
f32 descent; // the extent below the baseline (by convention, positive value extends below the baseline)
|
||||
f32 leading; // spacing between one row's descent and the next row's ascent
|
||||
f32 xHeight; // height of the lower case letter 'x'
|
||||
f32 capHeight; // height of the upper case letter 'M'
|
||||
f32 width; // maximum width of the font
|
||||
|
||||
} mg_font_extents;
|
||||
|
||||
typedef struct mg_text_extents
|
||||
{
|
||||
f32 xBearing;
|
||||
f32 yBearing;
|
||||
f32 width;
|
||||
f32 height;
|
||||
f32 xAdvance;
|
||||
f32 yAdvance;
|
||||
|
||||
} mg_text_extents;
|
||||
|
||||
//------------------------------------------------------------------------------------------
|
||||
//NOTE(martin): graphics canvas
|
||||
//------------------------------------------------------------------------------------------
|
||||
MP_API mg_canvas mg_canvas_nil();
|
||||
MP_API bool mg_canvas_is_nil(mg_canvas canvas);
|
||||
|
||||
MP_API mg_canvas mg_canvas_create(mg_surface surface);
|
||||
MP_API void mg_canvas_destroy(mg_canvas canvas);
|
||||
MP_API mg_canvas mg_canvas_set_current(mg_canvas canvas);
|
||||
|
||||
MP_API void mg_flush();
|
||||
|
||||
//------------------------------------------------------------------------------------------
|
||||
//NOTE(martin): fonts
|
||||
//------------------------------------------------------------------------------------------
|
||||
MP_API mg_font mg_font_nil();
|
||||
MP_API mg_font mg_font_create_from_memory(u32 size, byte* buffer, u32 rangeCount, unicode_range* ranges);
|
||||
MP_API void mg_font_destroy(mg_font font);
|
||||
|
||||
//NOTE(martin): the following int valued functions return -1 if font is invalid or codepoint is not present in font//
|
||||
//TODO(martin): add enum error codes
|
||||
|
||||
MP_API mg_font_extents mg_font_get_extents(mg_font font);
|
||||
MP_API mg_font_extents mg_font_get_scaled_extents(mg_font font, f32 emSize);
|
||||
MP_API f32 mg_font_get_scale_for_em_pixels(mg_font font, f32 emSize);
|
||||
|
||||
//NOTE(martin): if you need to process more than one codepoint, first convert your codepoints to glyph indices, then use the
|
||||
// glyph index versions of the functions, which can take an array of glyph indices.
|
||||
|
||||
MP_API str32 mg_font_get_glyph_indices(mg_font font, str32 codePoints, str32 backing);
|
||||
MP_API str32 mg_font_push_glyph_indices(mg_font font, mem_arena* arena, str32 codePoints);
|
||||
MP_API u32 mg_font_get_glyph_index(mg_font font, utf32 codePoint);
|
||||
|
||||
MP_API int mg_font_get_codepoint_extents(mg_font font, utf32 codePoint, mg_text_extents* outExtents);
|
||||
|
||||
MP_API int mg_font_get_glyph_extents(mg_font font, str32 glyphIndices, mg_text_extents* outExtents);
|
||||
|
||||
MP_API mp_rect mg_text_bounding_box_utf32(mg_font font, f32 fontSize, str32 text);
|
||||
MP_API mp_rect mg_text_bounding_box(mg_font font, f32 fontSize, str8 text);
|
||||
|
||||
//------------------------------------------------------------------------------------------
|
||||
//NOTE(martin): images
|
||||
//------------------------------------------------------------------------------------------
|
||||
MP_API mg_image mg_image_nil();
|
||||
MP_API bool mg_image_is_nil(mg_image a);
|
||||
|
||||
MP_API mg_image mg_image_create(u32 width, u32 height);
|
||||
MP_API mg_image mg_image_create_from_rgba8(u32 width, u32 height, u8* pixels);
|
||||
MP_API mg_image mg_image_create_from_data(str8 data, bool flip);
|
||||
MP_API mg_image mg_image_create_from_file(str8 path, bool flip);
|
||||
|
||||
MP_API void mg_image_destroy(mg_image image);
|
||||
|
||||
MP_API void mg_image_upload_region_rgba8(mg_image image, mp_rect region, u8* pixels);
|
||||
MP_API vec2 mg_image_size(mg_image image);
|
||||
|
||||
//------------------------------------------------------------------------------------------
|
||||
//NOTE(martin): atlasing
|
||||
//------------------------------------------------------------------------------------------
|
||||
|
||||
//NOTE: rectangle allocator
|
||||
typedef struct mg_rect_atlas mg_rect_atlas;
|
||||
|
||||
mg_rect_atlas* mg_rect_atlas_create(mem_arena* arena, i32 width, i32 height);
|
||||
mp_rect mg_rect_atlas_alloc(mg_rect_atlas* atlas, i32 width, i32 height);
|
||||
void mg_rect_atlas_recycle(mg_rect_atlas* atlas, mp_rect rect);
|
||||
|
||||
//NOTE: image atlas helpers
|
||||
typedef struct mg_image_region
|
||||
{
|
||||
mg_image image;
|
||||
mp_rect rect;
|
||||
} mg_image_region;
|
||||
|
||||
mg_image_region mg_image_atlas_alloc_from_rgba8(mg_rect_atlas* atlas, mg_image backingImage, u32 width, u32 height, u8* pixels);
|
||||
mg_image_region mg_image_atlas_alloc_from_data(mg_rect_atlas* atlas, mg_image backingImage, str8 data, bool flip);
|
||||
mg_image_region mg_image_atlas_alloc_from_file(mg_rect_atlas* atlas, mg_image backingImage, str8 path, bool flip);
|
||||
void mg_image_atlas_recycle(mg_rect_atlas* atlas, mg_image_region imageRgn);
|
||||
|
||||
//------------------------------------------------------------------------------------------
|
||||
//NOTE(martin): transform, viewport and clipping
|
||||
//------------------------------------------------------------------------------------------
|
||||
MP_API void mg_viewport(mp_rect viewPort);
|
||||
|
||||
MP_API void mg_matrix_push(mg_mat2x3 matrix);
|
||||
MP_API void mg_matrix_pop();
|
||||
|
||||
MP_API void mg_clip_push(f32 x, f32 y, f32 w, f32 h);
|
||||
MP_API void mg_clip_pop();
|
||||
|
||||
//------------------------------------------------------------------------------------------
|
||||
//NOTE(martin): graphics attributes setting/getting
|
||||
//------------------------------------------------------------------------------------------
|
||||
MP_API void mg_set_color(mg_color color);
|
||||
MP_API void mg_set_color_rgba(f32 r, f32 g, f32 b, f32 a);
|
||||
MP_API void mg_set_width(f32 width);
|
||||
MP_API void mg_set_tolerance(f32 tolerance);
|
||||
MP_API void mg_set_joint(mg_joint_type joint);
|
||||
MP_API void mg_set_max_joint_excursion(f32 maxJointExcursion);
|
||||
MP_API void mg_set_cap(mg_cap_type cap);
|
||||
MP_API void mg_set_font(mg_font font);
|
||||
MP_API void mg_set_font_size(f32 size);
|
||||
MP_API void mg_set_text_flip(bool flip);
|
||||
MP_API void mg_set_image(mg_image image);
|
||||
MP_API void mg_set_image_source_region(mp_rect region);
|
||||
|
||||
MP_API mg_color mg_get_color();
|
||||
MP_API f32 mg_get_width();
|
||||
MP_API f32 mg_get_tolerance();
|
||||
MP_API mg_joint_type mg_get_joint();
|
||||
MP_API f32 mg_get_max_joint_excursion();
|
||||
MP_API mg_cap_type mg_get_cap();
|
||||
MP_API mg_font mg_get_font();
|
||||
MP_API f32 mg_get_font_size();
|
||||
MP_API bool mg_get_text_flip();
|
||||
|
||||
//------------------------------------------------------------------------------------------
|
||||
//NOTE(martin): path construction
|
||||
//------------------------------------------------------------------------------------------
|
||||
MP_API vec2 mg_get_position();
|
||||
MP_API void mg_move_to(f32 x, f32 y);
|
||||
MP_API void mg_line_to(f32 x, f32 y);
|
||||
MP_API void mg_quadratic_to(f32 x1, f32 y1, f32 x2, f32 y2);
|
||||
MP_API void mg_cubic_to(f32 x1, f32 y1, f32 x2, f32 y2, f32 x3, f32 y3);
|
||||
MP_API void mg_close_path();
|
||||
|
||||
MP_API mp_rect mg_glyph_outlines(str32 glyphIndices);
|
||||
MP_API void mg_codepoints_outlines(str32 string);
|
||||
MP_API void mg_text_outlines(str8 string);
|
||||
|
||||
//------------------------------------------------------------------------------------------
|
||||
//NOTE(martin): clear/fill/stroke
|
||||
//------------------------------------------------------------------------------------------
|
||||
MP_API void mg_clear();
|
||||
MP_API void mg_fill();
|
||||
MP_API void mg_stroke();
|
||||
|
||||
//------------------------------------------------------------------------------------------
|
||||
//NOTE(martin): 'fast' shapes primitives
|
||||
//------------------------------------------------------------------------------------------
|
||||
MP_API void mg_rectangle_fill(f32 x, f32 y, f32 w, f32 h);
|
||||
MP_API void mg_rectangle_stroke(f32 x, f32 y, f32 w, f32 h);
|
||||
MP_API void mg_rounded_rectangle_fill(f32 x, f32 y, f32 w, f32 h, f32 r);
|
||||
MP_API void mg_rounded_rectangle_stroke(f32 x, f32 y, f32 w, f32 h, f32 r);
|
||||
MP_API void mg_ellipse_fill(f32 x, f32 y, f32 rx, f32 ry);
|
||||
MP_API void mg_ellipse_stroke(f32 x, f32 y, f32 rx, f32 ry);
|
||||
MP_API void mg_circle_fill(f32 x, f32 y, f32 r);
|
||||
MP_API void mg_circle_stroke(f32 x, f32 y, f32 r);
|
||||
MP_API void mg_arc(f32 x, f32 y, f32 r, f32 arcAngle, f32 startAngle);
|
||||
|
||||
//NOTE: image helpers
|
||||
MP_API void mg_image_draw(mg_image image, mp_rect rect);
|
||||
MP_API void mg_image_draw_region(mg_image image, mp_rect srcRegion, mp_rect dstRegion);
|
||||
|
||||
#endif //__GRAPHICS_H_
|
||||
|
|
|
@ -1,143 +1,143 @@
|
|||
/************************************************************//**
|
||||
*
|
||||
* @file: graphics_internal.h
|
||||
* @author: Martin Fouilleul
|
||||
* @date: 23/01/2023
|
||||
* @revision:
|
||||
*
|
||||
*****************************************************************/
|
||||
#ifndef __GRAPHICS_INTERNAL_H_
|
||||
#define __GRAPHICS_INTERNAL_H_
|
||||
|
||||
#include"graphics.h"
|
||||
#include"mp_app_internal.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// surface interface
|
||||
//---------------------------------------------------------------
|
||||
typedef struct mg_surface_data mg_surface_data;
|
||||
|
||||
typedef void (*mg_surface_destroy_proc)(mg_surface_data* surface);
|
||||
typedef void (*mg_surface_prepare_proc)(mg_surface_data* surface);
|
||||
typedef void (*mg_surface_present_proc)(mg_surface_data* surface);
|
||||
typedef void (*mg_surface_swap_interval_proc)(mg_surface_data* surface, int swap);
|
||||
typedef vec2 (*mg_surface_contents_scaling_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 void* (*mg_surface_native_layer_proc)(mg_surface_data* surface);
|
||||
typedef mg_surface_id (*mg_surface_remote_id_proc)(mg_surface_data* surface);
|
||||
typedef void (*mg_surface_host_connect_proc)(mg_surface_data* surface, mg_surface_id remoteId);
|
||||
|
||||
typedef struct mg_surface_data
|
||||
{
|
||||
mg_backend_id backend;
|
||||
mp_layer layer;
|
||||
|
||||
mg_surface_destroy_proc destroy;
|
||||
mg_surface_prepare_proc prepare;
|
||||
mg_surface_present_proc present;
|
||||
mg_surface_swap_interval_proc swapInterval;
|
||||
mg_surface_contents_scaling_proc contentsScaling;
|
||||
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_native_layer_proc nativeLayer;
|
||||
mg_surface_remote_id_proc remoteID;
|
||||
mg_surface_host_connect_proc hostConnect;
|
||||
} mg_surface_data;
|
||||
|
||||
mg_surface mg_surface_alloc_handle(mg_surface_data* surface);
|
||||
mg_surface_data* mg_surface_data_from_handle(mg_surface handle);
|
||||
|
||||
void mg_surface_init_for_window(mg_surface_data* surface, mp_window_data* window);
|
||||
void mg_surface_init_remote(mg_surface_data* surface, u32 width, u32 height);
|
||||
void mg_surface_init_host(mg_surface_data* surface, mp_window_data* window);
|
||||
void mg_surface_cleanup(mg_surface_data* surface);
|
||||
void* mg_surface_native_layer(mg_surface surface);
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// canvas backend interface
|
||||
//---------------------------------------------------------------
|
||||
typedef struct mg_image_data
|
||||
{
|
||||
list_elt listElt;
|
||||
u32 generation;
|
||||
vec2 size;
|
||||
|
||||
} mg_image_data;
|
||||
|
||||
typedef struct mg_vertex_layout
|
||||
{
|
||||
u32 maxVertexCount;
|
||||
u32 maxIndexCount;
|
||||
|
||||
char* posBuffer;
|
||||
u32 posStride;
|
||||
|
||||
char* cubicBuffer;
|
||||
u32 cubicStride;
|
||||
|
||||
char* uvTransformBuffer;
|
||||
u32 uvTransformStride;
|
||||
|
||||
char* colorBuffer;
|
||||
u32 colorStride;
|
||||
|
||||
char* shapeIndexBuffer;
|
||||
u32 shapeIndexStride;
|
||||
|
||||
char* clipBuffer;
|
||||
u32 clipStride;
|
||||
|
||||
char* indexBuffer;
|
||||
u32 indexStride;
|
||||
|
||||
} mg_vertex_layout;
|
||||
|
||||
typedef struct mg_canvas_backend mg_canvas_backend;
|
||||
|
||||
typedef void (*mg_canvas_backend_destroy_proc)(mg_canvas_backend* backend);
|
||||
typedef void (*mg_canvas_backend_begin_proc)(mg_canvas_backend* backend);
|
||||
typedef void (*mg_canvas_backend_end_proc)(mg_canvas_backend* backend);
|
||||
typedef void (*mg_canvas_backend_clear_proc)(mg_canvas_backend* backend, mg_color clearColor);
|
||||
typedef void (*mg_canvas_backend_draw_batch_proc)(mg_canvas_backend* backend,
|
||||
mg_image_data* imageData,
|
||||
u32 vertexCount,
|
||||
u32 shapeCount,
|
||||
u32 indexCount);
|
||||
|
||||
|
||||
typedef mg_image_data* (*mg_canvas_backend_image_create_proc)(mg_canvas_backend* backend, vec2 size);
|
||||
typedef void (*mg_canvas_backend_image_destroy_proc)(mg_canvas_backend* backend, mg_image_data* image);
|
||||
typedef void (*mg_canvas_backend_image_upload_region_proc)(mg_canvas_backend* backend,
|
||||
mg_image_data* image,
|
||||
mp_rect region,
|
||||
u8* pixels);
|
||||
|
||||
typedef struct mg_canvas_backend
|
||||
{
|
||||
mg_vertex_layout vertexLayout;
|
||||
|
||||
mg_canvas_backend_destroy_proc destroy;
|
||||
mg_canvas_backend_begin_proc begin;
|
||||
mg_canvas_backend_end_proc end;
|
||||
mg_canvas_backend_clear_proc clear;
|
||||
mg_canvas_backend_draw_batch_proc drawBatch;
|
||||
|
||||
mg_canvas_backend_image_create_proc imageCreate;
|
||||
mg_canvas_backend_image_destroy_proc imageDestroy;
|
||||
mg_canvas_backend_image_upload_region_proc imageUploadRegion;
|
||||
} mg_canvas_backend;
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif //__GRAPHICS_INTERNAL_H_
|
||||
/************************************************************//**
|
||||
*
|
||||
* @file: graphics_internal.h
|
||||
* @author: Martin Fouilleul
|
||||
* @date: 23/01/2023
|
||||
* @revision:
|
||||
*
|
||||
*****************************************************************/
|
||||
#ifndef __GRAPHICS_INTERNAL_H_
|
||||
#define __GRAPHICS_INTERNAL_H_
|
||||
|
||||
#include"graphics.h"
|
||||
#include"mp_app_internal.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// surface interface
|
||||
//---------------------------------------------------------------
|
||||
typedef struct mg_surface_data mg_surface_data;
|
||||
|
||||
typedef void (*mg_surface_destroy_proc)(mg_surface_data* surface);
|
||||
typedef void (*mg_surface_prepare_proc)(mg_surface_data* surface);
|
||||
typedef void (*mg_surface_present_proc)(mg_surface_data* surface);
|
||||
typedef void (*mg_surface_swap_interval_proc)(mg_surface_data* surface, int swap);
|
||||
typedef vec2 (*mg_surface_contents_scaling_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 void* (*mg_surface_native_layer_proc)(mg_surface_data* surface);
|
||||
typedef mg_surface_id (*mg_surface_remote_id_proc)(mg_surface_data* surface);
|
||||
typedef void (*mg_surface_host_connect_proc)(mg_surface_data* surface, mg_surface_id remoteId);
|
||||
|
||||
typedef struct mg_surface_data
|
||||
{
|
||||
mg_backend_id backend;
|
||||
mp_layer layer;
|
||||
|
||||
mg_surface_destroy_proc destroy;
|
||||
mg_surface_prepare_proc prepare;
|
||||
mg_surface_present_proc present;
|
||||
mg_surface_swap_interval_proc swapInterval;
|
||||
mg_surface_contents_scaling_proc contentsScaling;
|
||||
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_native_layer_proc nativeLayer;
|
||||
mg_surface_remote_id_proc remoteID;
|
||||
mg_surface_host_connect_proc hostConnect;
|
||||
} mg_surface_data;
|
||||
|
||||
mg_surface mg_surface_alloc_handle(mg_surface_data* surface);
|
||||
mg_surface_data* mg_surface_data_from_handle(mg_surface handle);
|
||||
|
||||
void mg_surface_init_for_window(mg_surface_data* surface, mp_window_data* window);
|
||||
void mg_surface_init_remote(mg_surface_data* surface, u32 width, u32 height);
|
||||
void mg_surface_init_host(mg_surface_data* surface, mp_window_data* window);
|
||||
void mg_surface_cleanup(mg_surface_data* surface);
|
||||
void* mg_surface_native_layer(mg_surface surface);
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// canvas backend interface
|
||||
//---------------------------------------------------------------
|
||||
typedef struct mg_image_data
|
||||
{
|
||||
list_elt listElt;
|
||||
u32 generation;
|
||||
vec2 size;
|
||||
|
||||
} mg_image_data;
|
||||
|
||||
typedef struct mg_vertex_layout
|
||||
{
|
||||
u32 maxVertexCount;
|
||||
u32 maxIndexCount;
|
||||
|
||||
char* posBuffer;
|
||||
u32 posStride;
|
||||
|
||||
char* cubicBuffer;
|
||||
u32 cubicStride;
|
||||
|
||||
char* uvTransformBuffer;
|
||||
u32 uvTransformStride;
|
||||
|
||||
char* colorBuffer;
|
||||
u32 colorStride;
|
||||
|
||||
char* shapeIndexBuffer;
|
||||
u32 shapeIndexStride;
|
||||
|
||||
char* clipBuffer;
|
||||
u32 clipStride;
|
||||
|
||||
char* indexBuffer;
|
||||
u32 indexStride;
|
||||
|
||||
} mg_vertex_layout;
|
||||
|
||||
typedef struct mg_canvas_backend mg_canvas_backend;
|
||||
|
||||
typedef void (*mg_canvas_backend_destroy_proc)(mg_canvas_backend* backend);
|
||||
typedef void (*mg_canvas_backend_begin_proc)(mg_canvas_backend* backend);
|
||||
typedef void (*mg_canvas_backend_end_proc)(mg_canvas_backend* backend);
|
||||
typedef void (*mg_canvas_backend_clear_proc)(mg_canvas_backend* backend, mg_color clearColor);
|
||||
typedef void (*mg_canvas_backend_draw_batch_proc)(mg_canvas_backend* backend,
|
||||
mg_image_data* imageData,
|
||||
u32 vertexCount,
|
||||
u32 shapeCount,
|
||||
u32 indexCount);
|
||||
|
||||
|
||||
typedef mg_image_data* (*mg_canvas_backend_image_create_proc)(mg_canvas_backend* backend, vec2 size);
|
||||
typedef void (*mg_canvas_backend_image_destroy_proc)(mg_canvas_backend* backend, mg_image_data* image);
|
||||
typedef void (*mg_canvas_backend_image_upload_region_proc)(mg_canvas_backend* backend,
|
||||
mg_image_data* image,
|
||||
mp_rect region,
|
||||
u8* pixels);
|
||||
|
||||
typedef struct mg_canvas_backend
|
||||
{
|
||||
mg_vertex_layout vertexLayout;
|
||||
|
||||
mg_canvas_backend_destroy_proc destroy;
|
||||
mg_canvas_backend_begin_proc begin;
|
||||
mg_canvas_backend_end_proc end;
|
||||
mg_canvas_backend_clear_proc clear;
|
||||
mg_canvas_backend_draw_batch_proc drawBatch;
|
||||
|
||||
mg_canvas_backend_image_create_proc imageCreate;
|
||||
mg_canvas_backend_image_destroy_proc imageDestroy;
|
||||
mg_canvas_backend_image_upload_region_proc imageUploadRegion;
|
||||
} mg_canvas_backend;
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif //__GRAPHICS_INTERNAL_H_
|
||||
|
|
154
src/milepost.c
154
src/milepost.c
|
@ -1,77 +1,77 @@
|
|||
/************************************************************//**
|
||||
*
|
||||
* @file: milepost.c
|
||||
* @author: Martin Fouilleul
|
||||
* @date: 13/02/2021
|
||||
* @revision:
|
||||
*
|
||||
*****************************************************************/
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// utilities implementations
|
||||
//---------------------------------------------------------------
|
||||
#include"util/debug_log.c"
|
||||
#include"util/memory.c"
|
||||
#include"util/strings.c"
|
||||
#include"util/utf8.c"
|
||||
#include"util/hash.c"
|
||||
#include"util/ringbuffer.c"
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// platform implementations
|
||||
//---------------------------------------------------------------
|
||||
#include"platform.h"
|
||||
|
||||
#if defined(OS_WIN64)
|
||||
#include"platform/win32_base_allocator.c"
|
||||
#include"platform/win32_clock.c"
|
||||
//TODO
|
||||
#elif defined(OS_MACOS)
|
||||
#include"platform/unix_base_allocator.c"
|
||||
#include"platform/osx_clock.c"
|
||||
/*
|
||||
#include"platform/unix_rng.c"
|
||||
#include"platform/posix_thread.c"
|
||||
#include"platform/posix_socket.c"
|
||||
*/
|
||||
|
||||
#elif defined(OS_LINUX)
|
||||
#include"platform/unix_base_allocator.c"
|
||||
#include"platform/linux_clock.c"
|
||||
/*
|
||||
#include"platform/unix_rng.c"
|
||||
#include"platform/posix_thread.c"
|
||||
#include"platform/posix_socket.c"
|
||||
*/
|
||||
#else
|
||||
#error "Unsupported platform"
|
||||
#endif
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// app/graphics layer
|
||||
//---------------------------------------------------------------
|
||||
|
||||
#if defined(OS_WIN64)
|
||||
#include"win32_app.c"
|
||||
#include"graphics.c"
|
||||
|
||||
#if MG_COMPILE_BACKEND_GL || MG_COMPILE_BACKEND_GLES
|
||||
#include"gl_loader.c"
|
||||
#endif
|
||||
|
||||
#if MG_COMPILE_BACKEND_GL
|
||||
#include"wgl_surface.c"
|
||||
#include"gl_canvas.c"
|
||||
#endif
|
||||
|
||||
#if MG_COMPILE_BACKEND_GLES
|
||||
#include"egl_surface.c"
|
||||
#endif
|
||||
|
||||
#elif defined(OS_MACOS)
|
||||
//NOTE: macos application layer and graphics backends are defined in milepost.m
|
||||
#else
|
||||
#error "Unsupported platform"
|
||||
#endif
|
||||
|
||||
#include"ui.c"
|
||||
/************************************************************//**
|
||||
*
|
||||
* @file: milepost.c
|
||||
* @author: Martin Fouilleul
|
||||
* @date: 13/02/2021
|
||||
* @revision:
|
||||
*
|
||||
*****************************************************************/
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// utilities implementations
|
||||
//---------------------------------------------------------------
|
||||
#include"util/debug_log.c"
|
||||
#include"util/memory.c"
|
||||
#include"util/strings.c"
|
||||
#include"util/utf8.c"
|
||||
#include"util/hash.c"
|
||||
#include"util/ringbuffer.c"
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// platform implementations
|
||||
//---------------------------------------------------------------
|
||||
#include"platform.h"
|
||||
|
||||
#if defined(OS_WIN64)
|
||||
#include"platform/win32_base_allocator.c"
|
||||
#include"platform/win32_clock.c"
|
||||
//TODO
|
||||
#elif defined(OS_MACOS)
|
||||
#include"platform/unix_base_allocator.c"
|
||||
#include"platform/osx_clock.c"
|
||||
/*
|
||||
#include"platform/unix_rng.c"
|
||||
#include"platform/posix_thread.c"
|
||||
#include"platform/posix_socket.c"
|
||||
*/
|
||||
|
||||
#elif defined(OS_LINUX)
|
||||
#include"platform/unix_base_allocator.c"
|
||||
#include"platform/linux_clock.c"
|
||||
/*
|
||||
#include"platform/unix_rng.c"
|
||||
#include"platform/posix_thread.c"
|
||||
#include"platform/posix_socket.c"
|
||||
*/
|
||||
#else
|
||||
#error "Unsupported platform"
|
||||
#endif
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// app/graphics layer
|
||||
//---------------------------------------------------------------
|
||||
|
||||
#if defined(OS_WIN64)
|
||||
#include"win32_app.c"
|
||||
#include"graphics.c"
|
||||
|
||||
#if MG_COMPILE_BACKEND_GL || MG_COMPILE_BACKEND_GLES
|
||||
#include"gl_loader.c"
|
||||
#endif
|
||||
|
||||
#if MG_COMPILE_BACKEND_GL
|
||||
#include"wgl_surface.c"
|
||||
#include"gl_canvas.c"
|
||||
#endif
|
||||
|
||||
#if MG_COMPILE_BACKEND_GLES
|
||||
#include"egl_surface.c"
|
||||
#endif
|
||||
|
||||
#elif defined(OS_MACOS)
|
||||
//NOTE: macos application layer and graphics backends are defined in milepost.m
|
||||
#else
|
||||
#error "Unsupported platform"
|
||||
#endif
|
||||
|
||||
#include"ui.c"
|
||||
|
|
|
@ -1,48 +1,48 @@
|
|||
/************************************************************//**
|
||||
*
|
||||
* @file: milepost.h
|
||||
* @author: Martin Fouilleul
|
||||
* @date: 13/02/2021
|
||||
* @revision:
|
||||
*
|
||||
*****************************************************************/
|
||||
#ifndef __MILEPOST_H_
|
||||
#define __MILEPOST_H_
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// utility layer
|
||||
//----------------------------------------------------------------
|
||||
#include"platform.h"
|
||||
#include"typedefs.h"
|
||||
#include"macro_helpers.h"
|
||||
#include"debug_log.h"
|
||||
#include"lists.h"
|
||||
#include"memory.h"
|
||||
#include"strings.h"
|
||||
#include"utf8.h"
|
||||
#include"hash.h"
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// platform layer
|
||||
//----------------------------------------------------------------
|
||||
#include"platform_clock.h"
|
||||
/*
|
||||
#include"platform_rng.h"
|
||||
#include"platform_socket.h"
|
||||
#include"platform_thread.h"
|
||||
*/
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// application/graphics layer
|
||||
//----------------------------------------------------------------
|
||||
#include"mp_app.h"
|
||||
#include"graphics.h"
|
||||
#include"ui.h"
|
||||
|
||||
#ifdef MG_INCLUDE_GL_API
|
||||
#include"gl_api.h"
|
||||
#endif
|
||||
|
||||
//#include"ui.h"
|
||||
|
||||
#endif //__MILEPOST_H_
|
||||
/************************************************************//**
|
||||
*
|
||||
* @file: milepost.h
|
||||
* @author: Martin Fouilleul
|
||||
* @date: 13/02/2021
|
||||
* @revision:
|
||||
*
|
||||
*****************************************************************/
|
||||
#ifndef __MILEPOST_H_
|
||||
#define __MILEPOST_H_
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// utility layer
|
||||
//----------------------------------------------------------------
|
||||
#include"platform.h"
|
||||
#include"typedefs.h"
|
||||
#include"macro_helpers.h"
|
||||
#include"debug_log.h"
|
||||
#include"lists.h"
|
||||
#include"memory.h"
|
||||
#include"strings.h"
|
||||
#include"utf8.h"
|
||||
#include"hash.h"
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// platform layer
|
||||
//----------------------------------------------------------------
|
||||
#include"platform_clock.h"
|
||||
/*
|
||||
#include"platform_rng.h"
|
||||
#include"platform_socket.h"
|
||||
#include"platform_thread.h"
|
||||
*/
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// application/graphics layer
|
||||
//----------------------------------------------------------------
|
||||
#include"mp_app.h"
|
||||
#include"graphics.h"
|
||||
#include"ui.h"
|
||||
|
||||
#ifdef MG_INCLUDE_GL_API
|
||||
#include"gl_api.h"
|
||||
#endif
|
||||
|
||||
//#include"ui.h"
|
||||
|
||||
#endif //__MILEPOST_H_
|
||||
|
|
|
@ -1,29 +1,29 @@
|
|||
/************************************************************//**
|
||||
*
|
||||
* @file: milepost.m
|
||||
* @author: Martin Fouilleul
|
||||
* @date: 13/02/2021
|
||||
* @revision:
|
||||
*
|
||||
*****************************************************************/
|
||||
|
||||
#include"osx_app.m"
|
||||
#include"graphics.c"
|
||||
|
||||
#if MG_COMPILE_BACKEND_METAL
|
||||
#include"mtl_surface.m"
|
||||
#include"mtl_canvas.m"
|
||||
#endif
|
||||
|
||||
#if MG_COMPILE_BACKEND_GLES
|
||||
#include"gl_loader.c"
|
||||
#include"egl_surface.c"
|
||||
#endif
|
||||
|
||||
/*
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
#include"osx_gles_surface.m"
|
||||
#pragma clang diagnostic pop
|
||||
*/
|
||||
//#include"osx_surface_client.m"
|
||||
/************************************************************//**
|
||||
*
|
||||
* @file: milepost.m
|
||||
* @author: Martin Fouilleul
|
||||
* @date: 13/02/2021
|
||||
* @revision:
|
||||
*
|
||||
*****************************************************************/
|
||||
|
||||
#include"osx_app.m"
|
||||
#include"graphics.c"
|
||||
|
||||
#if MG_COMPILE_BACKEND_METAL
|
||||
#include"mtl_surface.m"
|
||||
#include"mtl_canvas.m"
|
||||
#endif
|
||||
|
||||
#if MG_COMPILE_BACKEND_GLES
|
||||
#include"gl_loader.c"
|
||||
#include"egl_surface.c"
|
||||
#endif
|
||||
|
||||
/*
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
#include"osx_gles_surface.m"
|
||||
#pragma clang diagnostic pop
|
||||
*/
|
||||
//#include"osx_surface_client.m"
|
||||
|
|
814
src/mp_app.c
814
src/mp_app.c
|
@ -1,407 +1,407 @@
|
|||
/************************************************************//**
|
||||
*
|
||||
* @file: mp_app_internal.c
|
||||
* @author: Martin Fouilleul
|
||||
* @date: 23/12/2022
|
||||
* @revision:
|
||||
*
|
||||
*****************************************************************/
|
||||
|
||||
#include"mp_app_internal.h"
|
||||
|
||||
#define LOG_SUBSYSTEM "Application"
|
||||
|
||||
mp_app __mpApp = {0};
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// Window handles
|
||||
//---------------------------------------------------------------
|
||||
|
||||
void mp_init_window_handles()
|
||||
{
|
||||
ListInit(&__mpApp.windowFreeList);
|
||||
for(int i=0; i<MP_APP_MAX_WINDOWS; i++)
|
||||
{
|
||||
__mpApp.windowPool[i].generation = 1;
|
||||
ListAppend(&__mpApp.windowFreeList, &__mpApp.windowPool[i].freeListElt);
|
||||
}
|
||||
}
|
||||
|
||||
bool mp_window_handle_is_null(mp_window window)
|
||||
{
|
||||
return(window.h == 0);
|
||||
}
|
||||
|
||||
mp_window mp_window_null_handle()
|
||||
{
|
||||
return((mp_window){.h = 0});
|
||||
}
|
||||
|
||||
mp_window_data* mp_window_alloc()
|
||||
{
|
||||
return(ListPopEntry(&__mpApp.windowFreeList, mp_window_data, freeListElt));
|
||||
}
|
||||
|
||||
mp_window_data* mp_window_ptr_from_handle(mp_window handle)
|
||||
{
|
||||
u32 index = handle.h>>32;
|
||||
u32 generation = handle.h & 0xffffffff;
|
||||
if(index >= MP_APP_MAX_WINDOWS)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
mp_window_data* window = &__mpApp.windowPool[index];
|
||||
if(window->generation != generation)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
return(window);
|
||||
}
|
||||
}
|
||||
|
||||
mp_window mp_window_handle_from_ptr(mp_window_data* window)
|
||||
{
|
||||
DEBUG_ASSERT( (window - __mpApp.windowPool) >= 0
|
||||
&& (window - __mpApp.windowPool) < MP_APP_MAX_WINDOWS);
|
||||
|
||||
u64 h = ((u64)(window - __mpApp.windowPool))<<32
|
||||
| ((u64)window->generation);
|
||||
|
||||
return((mp_window){h});
|
||||
}
|
||||
|
||||
void mp_window_recycle_ptr(mp_window_data* window)
|
||||
{
|
||||
window->generation++;
|
||||
ListPush(&__mpApp.windowFreeList, &window->freeListElt);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// Init
|
||||
//---------------------------------------------------------------
|
||||
|
||||
static void mp_init_common()
|
||||
{
|
||||
mp_init_window_handles();
|
||||
ringbuffer_init(&__mpApp.eventQueue, 16);
|
||||
}
|
||||
|
||||
static void mp_terminate_common()
|
||||
{
|
||||
ringbuffer_cleanup(&__mpApp.eventQueue);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// Event handling
|
||||
//---------------------------------------------------------------
|
||||
|
||||
void mp_queue_event(mp_event* event)
|
||||
{
|
||||
if(ringbuffer_write_available(&__mpApp.eventQueue) < sizeof(mp_event))
|
||||
{
|
||||
LOG_ERROR("event queue full\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
u32 written = ringbuffer_write(&__mpApp.eventQueue, sizeof(mp_event), (u8*)event);
|
||||
DEBUG_ASSERT(written == sizeof(mp_event));
|
||||
}
|
||||
}
|
||||
|
||||
bool mp_next_event(mp_event* event)
|
||||
{
|
||||
//NOTE pop and return event from queue
|
||||
if(ringbuffer_read_available(&__mpApp.eventQueue) >= sizeof(mp_event))
|
||||
{
|
||||
u64 read = ringbuffer_read(&__mpApp.eventQueue, sizeof(mp_event), (u8*)event);
|
||||
DEBUG_ASSERT(read == sizeof(mp_event));
|
||||
return(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
return(false);
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// Input state updating
|
||||
//---------------------------------------------------------------
|
||||
|
||||
static void mp_update_key_state(mp_key_state* key, mp_key_action action)
|
||||
{
|
||||
u64 frameCounter = __mpApp.inputState.frameCounter;
|
||||
if(key->lastUpdate != frameCounter)
|
||||
{
|
||||
key->transitionCount = 0;
|
||||
key->repeatCount = 0;
|
||||
key->sysClicked = false;
|
||||
key->sysDoubleClicked = false;
|
||||
key->lastUpdate = frameCounter;
|
||||
}
|
||||
|
||||
switch(action)
|
||||
{
|
||||
case MP_KEY_PRESS:
|
||||
{
|
||||
if(!key->down)
|
||||
{
|
||||
key->transitionCount++;
|
||||
}
|
||||
key->down = true;
|
||||
} break;
|
||||
|
||||
case MP_KEY_REPEAT:
|
||||
{
|
||||
key->repeatCount++;
|
||||
key->down = true;
|
||||
} break;
|
||||
|
||||
case MP_KEY_RELEASE:
|
||||
{
|
||||
if(key->down)
|
||||
{
|
||||
key->transitionCount++;
|
||||
}
|
||||
key->down = false;
|
||||
} break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void mp_update_mouse_move(f32 x, f32 y, f32 deltaX, f32 deltaY)
|
||||
{
|
||||
u64 frameCounter = __mpApp.inputState.frameCounter;
|
||||
mp_mouse_state* mouse = &__mpApp.inputState.mouse;
|
||||
if(mouse->lastUpdate != frameCounter)
|
||||
{
|
||||
mouse->delta = (vec2){0, 0};
|
||||
mouse->wheel = (vec2){0, 0};
|
||||
mouse->lastUpdate = frameCounter;
|
||||
}
|
||||
mouse->pos = (vec2){x, y};
|
||||
mouse->delta.x += deltaX;
|
||||
mouse->delta.y += deltaY;
|
||||
}
|
||||
|
||||
static void mp_update_mouse_wheel(f32 deltaX, f32 deltaY)
|
||||
{
|
||||
u64 frameCounter = __mpApp.inputState.frameCounter;
|
||||
mp_mouse_state* mouse = &__mpApp.inputState.mouse;
|
||||
if(mouse->lastUpdate != frameCounter)
|
||||
{
|
||||
mouse->delta = (vec2){0, 0};
|
||||
mouse->wheel = (vec2){0, 0};
|
||||
mouse->lastUpdate = frameCounter;
|
||||
}
|
||||
mouse->wheel.x += deltaX;
|
||||
mouse->wheel.y += deltaY;
|
||||
}
|
||||
|
||||
static void mp_update_text(utf32 codepoint)
|
||||
{
|
||||
u64 frameCounter = __mpApp.inputState.frameCounter;
|
||||
mp_text_state* text = &__mpApp.inputState.text;
|
||||
|
||||
if(text->lastUpdate != frameCounter)
|
||||
{
|
||||
text->codePoints.len = 0;
|
||||
text->lastUpdate = frameCounter;
|
||||
}
|
||||
|
||||
text->codePoints.ptr = text->backing;
|
||||
if(text->codePoints.len < MP_INPUT_TEXT_BACKING_SIZE)
|
||||
{
|
||||
text->codePoints.ptr[text->codePoints.len] = codepoint;
|
||||
text->codePoints.len++;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_WARNING("too many input codepoints per frame, dropping input");
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Input state polling
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
mp_key_state mp_key_get_state(mp_key_code key)
|
||||
{
|
||||
mp_key_state state = {0};
|
||||
if(key <= MP_KEY_COUNT)
|
||||
{
|
||||
state = __mpApp.inputState.keyboard.keys[key];
|
||||
}
|
||||
return(state);
|
||||
}
|
||||
|
||||
mp_key_state mp_mouse_button_get_state(mp_mouse_button button)
|
||||
{
|
||||
mp_key_state state = {0};
|
||||
if(button <= MP_MOUSE_BUTTON_COUNT)
|
||||
{
|
||||
state = __mpApp.inputState.mouse.buttons[button];
|
||||
}
|
||||
return(state);
|
||||
}
|
||||
|
||||
int mp_key_state_press_count(mp_key_state* key)
|
||||
{
|
||||
int count = 0;
|
||||
if(key->lastUpdate == __mpApp.inputState.frameCounter)
|
||||
{
|
||||
count = key->transitionCount / 2;
|
||||
if(key->down)
|
||||
{
|
||||
//NOTE: add one if state is down transition count is odd
|
||||
count += (key->transitionCount & 0x01);
|
||||
}
|
||||
}
|
||||
return(count);
|
||||
}
|
||||
|
||||
int mp_key_state_release_count(mp_key_state* key)
|
||||
{
|
||||
int count = 0;
|
||||
if(key->lastUpdate == __mpApp.inputState.frameCounter)
|
||||
{
|
||||
count = key->transitionCount / 2;
|
||||
if(!key->down)
|
||||
{
|
||||
//NOTE: add one if state is up and transition count is odd
|
||||
count += (key->transitionCount & 0x01);
|
||||
}
|
||||
}
|
||||
return(count);
|
||||
}
|
||||
|
||||
int mp_key_state_repeat_count(mp_key_state* key)
|
||||
{
|
||||
int count = 0;
|
||||
if(key->lastUpdate == __mpApp.inputState.frameCounter)
|
||||
{
|
||||
count = key->repeatCount;
|
||||
}
|
||||
return(count);
|
||||
}
|
||||
|
||||
bool mp_key_down(mp_key_code key)
|
||||
{
|
||||
mp_key_state state = mp_key_get_state(key);
|
||||
return(state.down);
|
||||
}
|
||||
|
||||
int mp_key_pressed(mp_key_code key)
|
||||
{
|
||||
mp_key_state state = mp_key_get_state(key);
|
||||
int res = mp_key_state_press_count(&state);
|
||||
return(res);
|
||||
}
|
||||
|
||||
int mp_key_released(mp_key_code key)
|
||||
{
|
||||
mp_key_state state = mp_key_get_state(key);
|
||||
int res = mp_key_state_release_count(&state);
|
||||
return(res);
|
||||
}
|
||||
|
||||
int mp_key_repeated(mp_key_code key)
|
||||
{
|
||||
mp_key_state state = mp_key_get_state(key);
|
||||
int res = mp_key_state_repeat_count(&state);
|
||||
return(res);
|
||||
}
|
||||
|
||||
bool mp_mouse_down(mp_mouse_button button)
|
||||
{
|
||||
mp_key_state state = mp_mouse_button_get_state(button);
|
||||
return(state.down);
|
||||
}
|
||||
|
||||
int mp_mouse_pressed(mp_mouse_button button)
|
||||
{
|
||||
mp_key_state state = mp_mouse_button_get_state(button);
|
||||
int res = mp_key_state_press_count(&state);
|
||||
return(res);
|
||||
}
|
||||
|
||||
int mp_mouse_released(mp_mouse_button button)
|
||||
{
|
||||
mp_key_state state = mp_mouse_button_get_state(button);
|
||||
int res = mp_key_state_release_count(&state);
|
||||
return(res);
|
||||
}
|
||||
|
||||
bool mp_mouse_clicked(mp_mouse_button button)
|
||||
{
|
||||
mp_key_state state = mp_mouse_button_get_state(button);
|
||||
bool clicked = state.sysClicked && (state.lastUpdate == __mpApp.inputState.frameCounter);
|
||||
return(clicked);
|
||||
}
|
||||
|
||||
bool mp_mouse_double_clicked(mp_mouse_button button)
|
||||
{
|
||||
mp_key_state state = mp_mouse_button_get_state(button);
|
||||
bool doubleClicked = state.sysClicked && (state.lastUpdate == __mpApp.inputState.frameCounter);
|
||||
return(doubleClicked);
|
||||
}
|
||||
|
||||
mp_keymod_flags mp_key_mods()
|
||||
{
|
||||
return(__mpApp.inputState.keyboard.mods);
|
||||
}
|
||||
|
||||
vec2 mp_mouse_position()
|
||||
{
|
||||
return(__mpApp.inputState.mouse.pos);
|
||||
}
|
||||
|
||||
vec2 mp_mouse_delta()
|
||||
{
|
||||
if(__mpApp.inputState.mouse.lastUpdate == __mpApp.inputState.frameCounter)
|
||||
{
|
||||
return(__mpApp.inputState.mouse.delta);
|
||||
}
|
||||
else
|
||||
{
|
||||
return((vec2){0, 0});
|
||||
}
|
||||
}
|
||||
|
||||
vec2 mp_mouse_wheel()
|
||||
{
|
||||
if(__mpApp.inputState.mouse.lastUpdate == __mpApp.inputState.frameCounter)
|
||||
{
|
||||
return(__mpApp.inputState.mouse.wheel);
|
||||
}
|
||||
else
|
||||
{
|
||||
return((vec2){0, 0});
|
||||
}
|
||||
}
|
||||
|
||||
str32 mp_input_text_utf32(mem_arena* arena)
|
||||
{
|
||||
str32 res = {0};
|
||||
if(__mpApp.inputState.text.lastUpdate == __mpApp.inputState.frameCounter)
|
||||
{
|
||||
res = str32_push_copy(arena, __mpApp.inputState.text.codePoints);
|
||||
}
|
||||
return(res);
|
||||
}
|
||||
|
||||
str8 mp_input_text_utf8(mem_arena* arena)
|
||||
{
|
||||
str8 res = {0};
|
||||
if(__mpApp.inputState.text.lastUpdate == __mpApp.inputState.frameCounter)
|
||||
{
|
||||
res = utf8_push_from_codepoints(arena, __mpApp.inputState.text.codePoints);
|
||||
}
|
||||
return(res);
|
||||
}
|
||||
|
||||
|
||||
#undef LOG_SUBSYSTEM
|
||||
/************************************************************//**
|
||||
*
|
||||
* @file: mp_app_internal.c
|
||||
* @author: Martin Fouilleul
|
||||
* @date: 23/12/2022
|
||||
* @revision:
|
||||
*
|
||||
*****************************************************************/
|
||||
|
||||
#include"mp_app_internal.h"
|
||||
|
||||
#define LOG_SUBSYSTEM "Application"
|
||||
|
||||
mp_app __mpApp = {0};
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// Window handles
|
||||
//---------------------------------------------------------------
|
||||
|
||||
void mp_init_window_handles()
|
||||
{
|
||||
ListInit(&__mpApp.windowFreeList);
|
||||
for(int i=0; i<MP_APP_MAX_WINDOWS; i++)
|
||||
{
|
||||
__mpApp.windowPool[i].generation = 1;
|
||||
ListAppend(&__mpApp.windowFreeList, &__mpApp.windowPool[i].freeListElt);
|
||||
}
|
||||
}
|
||||
|
||||
bool mp_window_handle_is_null(mp_window window)
|
||||
{
|
||||
return(window.h == 0);
|
||||
}
|
||||
|
||||
mp_window mp_window_null_handle()
|
||||
{
|
||||
return((mp_window){.h = 0});
|
||||
}
|
||||
|
||||
mp_window_data* mp_window_alloc()
|
||||
{
|
||||
return(ListPopEntry(&__mpApp.windowFreeList, mp_window_data, freeListElt));
|
||||
}
|
||||
|
||||
mp_window_data* mp_window_ptr_from_handle(mp_window handle)
|
||||
{
|
||||
u32 index = handle.h>>32;
|
||||
u32 generation = handle.h & 0xffffffff;
|
||||
if(index >= MP_APP_MAX_WINDOWS)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
mp_window_data* window = &__mpApp.windowPool[index];
|
||||
if(window->generation != generation)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
return(window);
|
||||
}
|
||||
}
|
||||
|
||||
mp_window mp_window_handle_from_ptr(mp_window_data* window)
|
||||
{
|
||||
DEBUG_ASSERT( (window - __mpApp.windowPool) >= 0
|
||||
&& (window - __mpApp.windowPool) < MP_APP_MAX_WINDOWS);
|
||||
|
||||
u64 h = ((u64)(window - __mpApp.windowPool))<<32
|
||||
| ((u64)window->generation);
|
||||
|
||||
return((mp_window){h});
|
||||
}
|
||||
|
||||
void mp_window_recycle_ptr(mp_window_data* window)
|
||||
{
|
||||
window->generation++;
|
||||
ListPush(&__mpApp.windowFreeList, &window->freeListElt);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// Init
|
||||
//---------------------------------------------------------------
|
||||
|
||||
static void mp_init_common()
|
||||
{
|
||||
mp_init_window_handles();
|
||||
ringbuffer_init(&__mpApp.eventQueue, 16);
|
||||
}
|
||||
|
||||
static void mp_terminate_common()
|
||||
{
|
||||
ringbuffer_cleanup(&__mpApp.eventQueue);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// Event handling
|
||||
//---------------------------------------------------------------
|
||||
|
||||
void mp_queue_event(mp_event* event)
|
||||
{
|
||||
if(ringbuffer_write_available(&__mpApp.eventQueue) < sizeof(mp_event))
|
||||
{
|
||||
LOG_ERROR("event queue full\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
u32 written = ringbuffer_write(&__mpApp.eventQueue, sizeof(mp_event), (u8*)event);
|
||||
DEBUG_ASSERT(written == sizeof(mp_event));
|
||||
}
|
||||
}
|
||||
|
||||
bool mp_next_event(mp_event* event)
|
||||
{
|
||||
//NOTE pop and return event from queue
|
||||
if(ringbuffer_read_available(&__mpApp.eventQueue) >= sizeof(mp_event))
|
||||
{
|
||||
u64 read = ringbuffer_read(&__mpApp.eventQueue, sizeof(mp_event), (u8*)event);
|
||||
DEBUG_ASSERT(read == sizeof(mp_event));
|
||||
return(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
return(false);
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// Input state updating
|
||||
//---------------------------------------------------------------
|
||||
|
||||
static void mp_update_key_state(mp_key_state* key, mp_key_action action)
|
||||
{
|
||||
u64 frameCounter = __mpApp.inputState.frameCounter;
|
||||
if(key->lastUpdate != frameCounter)
|
||||
{
|
||||
key->transitionCount = 0;
|
||||
key->repeatCount = 0;
|
||||
key->sysClicked = false;
|
||||
key->sysDoubleClicked = false;
|
||||
key->lastUpdate = frameCounter;
|
||||
}
|
||||
|
||||
switch(action)
|
||||
{
|
||||
case MP_KEY_PRESS:
|
||||
{
|
||||
if(!key->down)
|
||||
{
|
||||
key->transitionCount++;
|
||||
}
|
||||
key->down = true;
|
||||
} break;
|
||||
|
||||
case MP_KEY_REPEAT:
|
||||
{
|
||||
key->repeatCount++;
|
||||
key->down = true;
|
||||
} break;
|
||||
|
||||
case MP_KEY_RELEASE:
|
||||
{
|
||||
if(key->down)
|
||||
{
|
||||
key->transitionCount++;
|
||||
}
|
||||
key->down = false;
|
||||
} break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void mp_update_mouse_move(f32 x, f32 y, f32 deltaX, f32 deltaY)
|
||||
{
|
||||
u64 frameCounter = __mpApp.inputState.frameCounter;
|
||||
mp_mouse_state* mouse = &__mpApp.inputState.mouse;
|
||||
if(mouse->lastUpdate != frameCounter)
|
||||
{
|
||||
mouse->delta = (vec2){0, 0};
|
||||
mouse->wheel = (vec2){0, 0};
|
||||
mouse->lastUpdate = frameCounter;
|
||||
}
|
||||
mouse->pos = (vec2){x, y};
|
||||
mouse->delta.x += deltaX;
|
||||
mouse->delta.y += deltaY;
|
||||
}
|
||||
|
||||
static void mp_update_mouse_wheel(f32 deltaX, f32 deltaY)
|
||||
{
|
||||
u64 frameCounter = __mpApp.inputState.frameCounter;
|
||||
mp_mouse_state* mouse = &__mpApp.inputState.mouse;
|
||||
if(mouse->lastUpdate != frameCounter)
|
||||
{
|
||||
mouse->delta = (vec2){0, 0};
|
||||
mouse->wheel = (vec2){0, 0};
|
||||
mouse->lastUpdate = frameCounter;
|
||||
}
|
||||
mouse->wheel.x += deltaX;
|
||||
mouse->wheel.y += deltaY;
|
||||
}
|
||||
|
||||
static void mp_update_text(utf32 codepoint)
|
||||
{
|
||||
u64 frameCounter = __mpApp.inputState.frameCounter;
|
||||
mp_text_state* text = &__mpApp.inputState.text;
|
||||
|
||||
if(text->lastUpdate != frameCounter)
|
||||
{
|
||||
text->codePoints.len = 0;
|
||||
text->lastUpdate = frameCounter;
|
||||
}
|
||||
|
||||
text->codePoints.ptr = text->backing;
|
||||
if(text->codePoints.len < MP_INPUT_TEXT_BACKING_SIZE)
|
||||
{
|
||||
text->codePoints.ptr[text->codePoints.len] = codepoint;
|
||||
text->codePoints.len++;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_WARNING("too many input codepoints per frame, dropping input");
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Input state polling
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
mp_key_state mp_key_get_state(mp_key_code key)
|
||||
{
|
||||
mp_key_state state = {0};
|
||||
if(key <= MP_KEY_COUNT)
|
||||
{
|
||||
state = __mpApp.inputState.keyboard.keys[key];
|
||||
}
|
||||
return(state);
|
||||
}
|
||||
|
||||
mp_key_state mp_mouse_button_get_state(mp_mouse_button button)
|
||||
{
|
||||
mp_key_state state = {0};
|
||||
if(button <= MP_MOUSE_BUTTON_COUNT)
|
||||
{
|
||||
state = __mpApp.inputState.mouse.buttons[button];
|
||||
}
|
||||
return(state);
|
||||
}
|
||||
|
||||
int mp_key_state_press_count(mp_key_state* key)
|
||||
{
|
||||
int count = 0;
|
||||
if(key->lastUpdate == __mpApp.inputState.frameCounter)
|
||||
{
|
||||
count = key->transitionCount / 2;
|
||||
if(key->down)
|
||||
{
|
||||
//NOTE: add one if state is down transition count is odd
|
||||
count += (key->transitionCount & 0x01);
|
||||
}
|
||||
}
|
||||
return(count);
|
||||
}
|
||||
|
||||
int mp_key_state_release_count(mp_key_state* key)
|
||||
{
|
||||
int count = 0;
|
||||
if(key->lastUpdate == __mpApp.inputState.frameCounter)
|
||||
{
|
||||
count = key->transitionCount / 2;
|
||||
if(!key->down)
|
||||
{
|
||||
//NOTE: add one if state is up and transition count is odd
|
||||
count += (key->transitionCount & 0x01);
|
||||
}
|
||||
}
|
||||
return(count);
|
||||
}
|
||||
|
||||
int mp_key_state_repeat_count(mp_key_state* key)
|
||||
{
|
||||
int count = 0;
|
||||
if(key->lastUpdate == __mpApp.inputState.frameCounter)
|
||||
{
|
||||
count = key->repeatCount;
|
||||
}
|
||||
return(count);
|
||||
}
|
||||
|
||||
bool mp_key_down(mp_key_code key)
|
||||
{
|
||||
mp_key_state state = mp_key_get_state(key);
|
||||
return(state.down);
|
||||
}
|
||||
|
||||
int mp_key_pressed(mp_key_code key)
|
||||
{
|
||||
mp_key_state state = mp_key_get_state(key);
|
||||
int res = mp_key_state_press_count(&state);
|
||||
return(res);
|
||||
}
|
||||
|
||||
int mp_key_released(mp_key_code key)
|
||||
{
|
||||
mp_key_state state = mp_key_get_state(key);
|
||||
int res = mp_key_state_release_count(&state);
|
||||
return(res);
|
||||
}
|
||||
|
||||
int mp_key_repeated(mp_key_code key)
|
||||
{
|
||||
mp_key_state state = mp_key_get_state(key);
|
||||
int res = mp_key_state_repeat_count(&state);
|
||||
return(res);
|
||||
}
|
||||
|
||||
bool mp_mouse_down(mp_mouse_button button)
|
||||
{
|
||||
mp_key_state state = mp_mouse_button_get_state(button);
|
||||
return(state.down);
|
||||
}
|
||||
|
||||
int mp_mouse_pressed(mp_mouse_button button)
|
||||
{
|
||||
mp_key_state state = mp_mouse_button_get_state(button);
|
||||
int res = mp_key_state_press_count(&state);
|
||||
return(res);
|
||||
}
|
||||
|
||||
int mp_mouse_released(mp_mouse_button button)
|
||||
{
|
||||
mp_key_state state = mp_mouse_button_get_state(button);
|
||||
int res = mp_key_state_release_count(&state);
|
||||
return(res);
|
||||
}
|
||||
|
||||
bool mp_mouse_clicked(mp_mouse_button button)
|
||||
{
|
||||
mp_key_state state = mp_mouse_button_get_state(button);
|
||||
bool clicked = state.sysClicked && (state.lastUpdate == __mpApp.inputState.frameCounter);
|
||||
return(clicked);
|
||||
}
|
||||
|
||||
bool mp_mouse_double_clicked(mp_mouse_button button)
|
||||
{
|
||||
mp_key_state state = mp_mouse_button_get_state(button);
|
||||
bool doubleClicked = state.sysClicked && (state.lastUpdate == __mpApp.inputState.frameCounter);
|
||||
return(doubleClicked);
|
||||
}
|
||||
|
||||
mp_keymod_flags mp_key_mods()
|
||||
{
|
||||
return(__mpApp.inputState.keyboard.mods);
|
||||
}
|
||||
|
||||
vec2 mp_mouse_position()
|
||||
{
|
||||
return(__mpApp.inputState.mouse.pos);
|
||||
}
|
||||
|
||||
vec2 mp_mouse_delta()
|
||||
{
|
||||
if(__mpApp.inputState.mouse.lastUpdate == __mpApp.inputState.frameCounter)
|
||||
{
|
||||
return(__mpApp.inputState.mouse.delta);
|
||||
}
|
||||
else
|
||||
{
|
||||
return((vec2){0, 0});
|
||||
}
|
||||
}
|
||||
|
||||
vec2 mp_mouse_wheel()
|
||||
{
|
||||
if(__mpApp.inputState.mouse.lastUpdate == __mpApp.inputState.frameCounter)
|
||||
{
|
||||
return(__mpApp.inputState.mouse.wheel);
|
||||
}
|
||||
else
|
||||
{
|
||||
return((vec2){0, 0});
|
||||
}
|
||||
}
|
||||
|
||||
str32 mp_input_text_utf32(mem_arena* arena)
|
||||
{
|
||||
str32 res = {0};
|
||||
if(__mpApp.inputState.text.lastUpdate == __mpApp.inputState.frameCounter)
|
||||
{
|
||||
res = str32_push_copy(arena, __mpApp.inputState.text.codePoints);
|
||||
}
|
||||
return(res);
|
||||
}
|
||||
|
||||
str8 mp_input_text_utf8(mem_arena* arena)
|
||||
{
|
||||
str8 res = {0};
|
||||
if(__mpApp.inputState.text.lastUpdate == __mpApp.inputState.frameCounter)
|
||||
{
|
||||
res = utf8_push_from_codepoints(arena, __mpApp.inputState.text.codePoints);
|
||||
}
|
||||
return(res);
|
||||
}
|
||||
|
||||
|
||||
#undef LOG_SUBSYSTEM
|
||||
|
|
796
src/mp_app.h
796
src/mp_app.h
|
@ -1,398 +1,398 @@
|
|||
/************************************************************//**
|
||||
*
|
||||
* @file: platform_app.h
|
||||
* @author: Martin Fouilleul
|
||||
* @date: 16/05/2020
|
||||
* @revision:
|
||||
*
|
||||
*****************************************************************/
|
||||
#ifndef __PLATFORM_APP_H_
|
||||
#define __PLATFORM_APP_H_
|
||||
|
||||
#include"typedefs.h"
|
||||
#include"utf8.h"
|
||||
#include"lists.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Typedefs, enums and constants
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
typedef struct mp_window { u64 h; } mp_window;
|
||||
|
||||
typedef enum { MP_MOUSE_CURSOR_ARROW,
|
||||
MP_MOUSE_CURSOR_RESIZE_0,
|
||||
MP_MOUSE_CURSOR_RESIZE_90,
|
||||
MP_MOUSE_CURSOR_RESIZE_45,
|
||||
MP_MOUSE_CURSOR_RESIZE_135,
|
||||
MP_MOUSE_CURSOR_TEXT } mp_mouse_cursor;
|
||||
|
||||
typedef i32 mp_window_style;
|
||||
static const mp_window_style MP_WINDOW_STYLE_NO_TITLE = 0x01<<0,
|
||||
MP_WINDOW_STYLE_FIXED_SIZE = 0x01<<1,
|
||||
MP_WINDOW_STYLE_NO_CLOSE = 0x01<<2,
|
||||
MP_WINDOW_STYLE_NO_MINIFY = 0x01<<3,
|
||||
MP_WINDOW_STYLE_NO_FOCUS = 0x01<<4,
|
||||
MP_WINDOW_STYLE_FLOAT = 0x01<<5,
|
||||
MP_WINDOW_STYLE_POPUPMENU = 0x01<<6,
|
||||
MP_WINDOW_STYLE_NO_BUTTONS = 0x01<<7;
|
||||
|
||||
typedef enum { MP_EVENT_NONE,
|
||||
MP_EVENT_KEYBOARD_MODS, //TODO: remove, keep only key?
|
||||
MP_EVENT_KEYBOARD_KEY,
|
||||
MP_EVENT_KEYBOARD_CHAR,
|
||||
MP_EVENT_MOUSE_BUTTON,
|
||||
MP_EVENT_MOUSE_MOVE,
|
||||
MP_EVENT_MOUSE_WHEEL,
|
||||
MP_EVENT_MOUSE_ENTER,
|
||||
MP_EVENT_MOUSE_LEAVE,
|
||||
MP_EVENT_WINDOW_RESIZE,
|
||||
MP_EVENT_WINDOW_MOVE,
|
||||
MP_EVENT_WINDOW_FOCUS,
|
||||
MP_EVENT_WINDOW_UNFOCUS,
|
||||
MP_EVENT_WINDOW_HIDE, // rename to minimize?
|
||||
MP_EVENT_WINDOW_SHOW, // rename to restore?
|
||||
MP_EVENT_WINDOW_CLOSE,
|
||||
MP_EVENT_PATHDROP,
|
||||
MP_EVENT_FRAME,
|
||||
MP_EVENT_QUIT } mp_event_type;
|
||||
|
||||
typedef enum { MP_KEY_NO_ACTION,
|
||||
MP_KEY_PRESS,
|
||||
MP_KEY_RELEASE,
|
||||
MP_KEY_REPEAT } mp_key_action;
|
||||
|
||||
typedef enum { MP_KEY_UNKNOWN = 0,
|
||||
MP_KEY_SPACE = 32,
|
||||
MP_KEY_APOSTROPHE = 39, /* ' */
|
||||
MP_KEY_COMMA = 44, /* , */
|
||||
MP_KEY_MINUS = 45, // -
|
||||
MP_KEY_PERIOD = 46, // .
|
||||
MP_KEY_SLASH = 47, // /
|
||||
MP_KEY_0 = 48,
|
||||
MP_KEY_1 = 49,
|
||||
MP_KEY_2 = 50,
|
||||
MP_KEY_3 = 51,
|
||||
MP_KEY_4 = 52,
|
||||
MP_KEY_5 = 53,
|
||||
MP_KEY_6 = 54,
|
||||
MP_KEY_7 = 55,
|
||||
MP_KEY_8 = 56,
|
||||
MP_KEY_9 = 57,
|
||||
MP_KEY_SEMICOLON = 59, // ;
|
||||
MP_KEY_EQUAL = 61, // =
|
||||
MP_KEY_A = 65,
|
||||
MP_KEY_B = 66,
|
||||
MP_KEY_C = 67,
|
||||
MP_KEY_D = 68,
|
||||
MP_KEY_E = 69,
|
||||
MP_KEY_F = 70,
|
||||
MP_KEY_G = 71,
|
||||
MP_KEY_H = 72,
|
||||
MP_KEY_I = 73,
|
||||
MP_KEY_J = 74,
|
||||
MP_KEY_K = 75,
|
||||
MP_KEY_L = 76,
|
||||
MP_KEY_M = 77,
|
||||
MP_KEY_N = 78,
|
||||
MP_KEY_O = 79,
|
||||
MP_KEY_P = 80,
|
||||
MP_KEY_Q = 81,
|
||||
MP_KEY_R = 82,
|
||||
MP_KEY_S = 83,
|
||||
MP_KEY_T = 84,
|
||||
MP_KEY_U = 85,
|
||||
MP_KEY_V = 86,
|
||||
MP_KEY_W = 87,
|
||||
MP_KEY_X = 88,
|
||||
MP_KEY_Y = 89,
|
||||
MP_KEY_Z = 90,
|
||||
MP_KEY_LEFT_BRACKET = 91, // [
|
||||
MP_KEY_BACKSLASH = 92, // \ */
|
||||
MP_KEY_RIGHT_BRACKET = 93, // ]
|
||||
MP_KEY_GRAVE_ACCENT = 96, // `
|
||||
MP_KEY_WORLD_1 = 161, // non-US #1
|
||||
MP_KEY_WORLD_2 = 162, // non-US #2
|
||||
MP_KEY_ESCAPE = 256,
|
||||
MP_KEY_ENTER = 257,
|
||||
MP_KEY_TAB = 258,
|
||||
MP_KEY_BACKSPACE = 259,
|
||||
MP_KEY_INSERT = 260,
|
||||
MP_KEY_DELETE = 261,
|
||||
MP_KEY_RIGHT = 262,
|
||||
MP_KEY_LEFT = 263,
|
||||
MP_KEY_DOWN = 264,
|
||||
MP_KEY_UP = 265,
|
||||
MP_KEY_PAGE_UP = 266,
|
||||
MP_KEY_PAGE_DOWN = 267,
|
||||
MP_KEY_HOME = 268,
|
||||
MP_KEY_END = 269,
|
||||
MP_KEY_CAPS_LOCK = 280,
|
||||
MP_KEY_SCROLL_LOCK = 281,
|
||||
MP_KEY_NUM_LOCK = 282,
|
||||
MP_KEY_PRINT_SCREEN = 283,
|
||||
MP_KEY_PAUSE = 284,
|
||||
MP_KEY_F1 = 290,
|
||||
MP_KEY_F2 = 291,
|
||||
MP_KEY_F3 = 292,
|
||||
MP_KEY_F4 = 293,
|
||||
MP_KEY_F5 = 294,
|
||||
MP_KEY_F6 = 295,
|
||||
MP_KEY_F7 = 296,
|
||||
MP_KEY_F8 = 297,
|
||||
MP_KEY_F9 = 298,
|
||||
MP_KEY_F10 = 299,
|
||||
MP_KEY_F11 = 300,
|
||||
MP_KEY_F12 = 301,
|
||||
MP_KEY_F13 = 302,
|
||||
MP_KEY_F14 = 303,
|
||||
MP_KEY_F15 = 304,
|
||||
MP_KEY_F16 = 305,
|
||||
MP_KEY_F17 = 306,
|
||||
MP_KEY_F18 = 307,
|
||||
MP_KEY_F19 = 308,
|
||||
MP_KEY_F20 = 309,
|
||||
MP_KEY_F21 = 310,
|
||||
MP_KEY_F22 = 311,
|
||||
MP_KEY_F23 = 312,
|
||||
MP_KEY_F24 = 313,
|
||||
MP_KEY_F25 = 314,
|
||||
MP_KEY_KP_0 = 320,
|
||||
MP_KEY_KP_1 = 321,
|
||||
MP_KEY_KP_2 = 322,
|
||||
MP_KEY_KP_3 = 323,
|
||||
MP_KEY_KP_4 = 324,
|
||||
MP_KEY_KP_5 = 325,
|
||||
MP_KEY_KP_6 = 326,
|
||||
MP_KEY_KP_7 = 327,
|
||||
MP_KEY_KP_8 = 328,
|
||||
MP_KEY_KP_9 = 329,
|
||||
MP_KEY_KP_DECIMAL = 330,
|
||||
MP_KEY_KP_DIVIDE = 331,
|
||||
MP_KEY_KP_MULTIPLY = 332,
|
||||
MP_KEY_KP_SUBTRACT = 333,
|
||||
MP_KEY_KP_ADD = 334,
|
||||
MP_KEY_KP_ENTER = 335,
|
||||
MP_KEY_KP_EQUAL = 336,
|
||||
MP_KEY_LEFT_SHIFT = 340,
|
||||
MP_KEY_LEFT_CONTROL = 341,
|
||||
MP_KEY_LEFT_ALT = 342,
|
||||
MP_KEY_LEFT_SUPER = 343,
|
||||
MP_KEY_RIGHT_SHIFT = 344,
|
||||
MP_KEY_RIGHT_CONTROL = 345,
|
||||
MP_KEY_RIGHT_ALT = 346,
|
||||
MP_KEY_RIGHT_SUPER = 347,
|
||||
MP_KEY_MENU = 348,
|
||||
MP_KEY_COUNT } mp_key_code;
|
||||
|
||||
typedef enum {
|
||||
MP_KEYMOD_NONE = 0x00,
|
||||
MP_KEYMOD_ALT = 0x01,
|
||||
MP_KEYMOD_SHIFT = 0x02,
|
||||
MP_KEYMOD_CTRL = 0x04,
|
||||
MP_KEYMOD_CMD = 0x08 } mp_keymod_flags;
|
||||
|
||||
typedef enum {
|
||||
MP_MOUSE_LEFT = 0x00,
|
||||
MP_MOUSE_RIGHT = 0x01,
|
||||
MP_MOUSE_MIDDLE = 0x02,
|
||||
MP_MOUSE_EXT1 = 0x03,
|
||||
MP_MOUSE_EXT2 = 0x04,
|
||||
MP_MOUSE_BUTTON_COUNT } mp_mouse_button;
|
||||
|
||||
typedef struct mp_key_event // keyboard and mouse buttons input
|
||||
{
|
||||
mp_key_action action;
|
||||
i32 code;
|
||||
mp_keymod_flags mods;
|
||||
char label[8];
|
||||
u8 labelLen;
|
||||
int clickCount;
|
||||
} mp_key_event;
|
||||
|
||||
typedef struct mp_char_event // character input
|
||||
{
|
||||
utf32 codepoint;
|
||||
char sequence[8];
|
||||
u8 seqLen;
|
||||
} mp_char_event;
|
||||
|
||||
typedef struct mp_move_event // mouse move/scroll
|
||||
{
|
||||
f32 x;
|
||||
f32 y;
|
||||
f32 deltaX;
|
||||
f32 deltaY;
|
||||
mp_keymod_flags mods;
|
||||
} mp_move_event;
|
||||
|
||||
typedef struct mp_frame_event // window resize / move
|
||||
{
|
||||
mp_rect rect;
|
||||
} mp_frame_event;
|
||||
|
||||
typedef struct mp_event
|
||||
{
|
||||
//TODO clipboard and path drop
|
||||
|
||||
mp_window window;
|
||||
mp_event_type type;
|
||||
|
||||
union
|
||||
{
|
||||
mp_key_event key;
|
||||
mp_char_event character;
|
||||
mp_move_event move;
|
||||
mp_frame_event frame;
|
||||
str8 path;
|
||||
};
|
||||
|
||||
//TODO(martin): chain externally ?
|
||||
list_elt list;
|
||||
} mp_event;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// app management
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
MP_API void mp_init(void);
|
||||
MP_API void mp_terminate(void);
|
||||
|
||||
MP_API bool mp_should_quit(void);
|
||||
MP_API void mp_cancel_quit(void);
|
||||
MP_API void mp_request_quit(void);
|
||||
|
||||
MP_API void mp_set_cursor(mp_mouse_cursor cursor);
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Main loop and events handling
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
MP_API void mp_pump_events(f64 timeout);
|
||||
MP_API bool mp_next_event(mp_event* event);
|
||||
|
||||
typedef void(*mp_live_resize_callback)(mp_event event, void* data);
|
||||
MP_API void mp_set_live_resize_callback(mp_live_resize_callback callback, void* data);
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// window management
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
MP_API bool mp_window_handle_is_null(mp_window window);
|
||||
MP_API mp_window mp_window_null_handle(void);
|
||||
|
||||
MP_API mp_window mp_window_create(mp_rect contentRect, const char* title, mp_window_style style);
|
||||
MP_API void mp_window_destroy(mp_window window);
|
||||
MP_API void* mp_window_native_pointer(mp_window window);
|
||||
|
||||
MP_API bool mp_window_should_close(mp_window window);
|
||||
MP_API void mp_window_request_close(mp_window window);
|
||||
MP_API void mp_window_cancel_close(mp_window window);
|
||||
|
||||
MP_API bool mp_window_is_hidden(mp_window window);
|
||||
MP_API void mp_window_hide(mp_window window);
|
||||
MP_API void mp_window_show(mp_window window);
|
||||
|
||||
MP_API bool mp_window_is_minimized(mp_window window);
|
||||
MP_API bool mp_window_is_maximized(mp_window window);
|
||||
MP_API void mp_window_minimize(mp_window window);
|
||||
MP_API void mp_window_maximize(mp_window window);
|
||||
MP_API void mp_window_restore(mp_window window);
|
||||
|
||||
MP_API bool mp_window_has_focus(mp_window window);
|
||||
MP_API void mp_window_focus(mp_window window);
|
||||
MP_API void mp_window_unfocus(mp_window window);
|
||||
|
||||
MP_API void mp_window_send_to_back(mp_window window);
|
||||
MP_API void mp_window_bring_to_front(mp_window window);
|
||||
|
||||
MP_API mp_rect mp_window_get_content_rect(mp_window window);
|
||||
MP_API mp_rect mp_window_get_frame_rect(mp_window window);
|
||||
MP_API void mp_window_set_content_rect(mp_window window, mp_rect contentRect);
|
||||
MP_API void mp_window_set_frame_rect(mp_window window, mp_rect frameRect);
|
||||
|
||||
MP_API void mp_window_center(mp_window window);
|
||||
|
||||
MP_API mp_rect mp_window_content_rect_for_frame_rect(mp_rect frameRect, mp_window_style style);
|
||||
MP_API mp_rect mp_window_frame_rect_for_content_rect(mp_rect contentRect, mp_window_style style);
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Input state polling
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
MP_API bool mp_key_down(mp_key_code key);
|
||||
MP_API int mp_key_pressed(mp_key_code key);
|
||||
MP_API int mp_key_released(mp_key_code key);
|
||||
MP_API int mp_key_repeated(mp_key_code key);
|
||||
|
||||
MP_API bool mp_mouse_down(mp_mouse_button button);
|
||||
MP_API int mp_mouse_pressed(mp_mouse_button button);
|
||||
MP_API int mp_mouse_released(mp_mouse_button button);
|
||||
MP_API bool mp_mouse_clicked(mp_mouse_button button);
|
||||
MP_API bool mp_mouse_double_clicked(mp_mouse_button button);
|
||||
|
||||
MP_API vec2 mp_mouse_position(void);
|
||||
MP_API vec2 mp_mouse_delta(void);
|
||||
MP_API vec2 mp_mouse_wheel(void);
|
||||
|
||||
MP_API str32 mp_input_text_utf32(mem_arena* arena);
|
||||
MP_API str8 mp_input_text_utf8(mem_arena* arena);
|
||||
|
||||
MP_API mp_keymod_flags mp_key_mods();
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Clipboard
|
||||
//--------------------------------------------------------------------
|
||||
MP_API void mp_clipboard_clear(void);
|
||||
|
||||
MP_API void mp_clipboard_set_string(str8 string);
|
||||
MP_API str8 mp_clipboard_get_string(mem_arena* arena);
|
||||
MP_API str8 mp_clipboard_copy_string(str8 backing);
|
||||
|
||||
MP_API bool mp_clipboard_has_tag(const char* tag);
|
||||
MP_API void mp_clipboard_set_data_for_tag(const char* tag, str8 data);
|
||||
MP_API str8 mp_clipboard_get_data_for_tag(mem_arena* arena, const char* tag);
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// native open/save/alert windows
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
MP_API str8 mp_open_dialog(mem_arena* arena,
|
||||
const char* title,
|
||||
const char* defaultPath,
|
||||
int filterCount,
|
||||
const char** filters,
|
||||
bool directory);
|
||||
|
||||
MP_API str8 mp_save_dialog(mem_arena* arena,
|
||||
const char* title,
|
||||
const char* defaultPath,
|
||||
int filterCount,
|
||||
const char** filters);
|
||||
|
||||
MP_API int mp_alert_popup(const char* title,
|
||||
const char* message,
|
||||
u32 count,
|
||||
const char** options);
|
||||
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// file system stuff... //TODO: move elsewhere
|
||||
//--------------------------------------------------------------------
|
||||
MP_API int mp_file_move(str8 from, str8 to);
|
||||
MP_API int mp_file_remove(str8 path);
|
||||
|
||||
MP_API int mp_directory_create(str8 path);
|
||||
|
||||
MP_API str8 mp_app_get_resource_path(mem_arena* arena, const char* name);
|
||||
MP_API str8 mp_app_get_executable_path(mem_arena* arena);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif //__PLATFORM_APP_H_
|
||||
/************************************************************//**
|
||||
*
|
||||
* @file: platform_app.h
|
||||
* @author: Martin Fouilleul
|
||||
* @date: 16/05/2020
|
||||
* @revision:
|
||||
*
|
||||
*****************************************************************/
|
||||
#ifndef __PLATFORM_APP_H_
|
||||
#define __PLATFORM_APP_H_
|
||||
|
||||
#include"typedefs.h"
|
||||
#include"utf8.h"
|
||||
#include"lists.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Typedefs, enums and constants
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
typedef struct mp_window { u64 h; } mp_window;
|
||||
|
||||
typedef enum { MP_MOUSE_CURSOR_ARROW,
|
||||
MP_MOUSE_CURSOR_RESIZE_0,
|
||||
MP_MOUSE_CURSOR_RESIZE_90,
|
||||
MP_MOUSE_CURSOR_RESIZE_45,
|
||||
MP_MOUSE_CURSOR_RESIZE_135,
|
||||
MP_MOUSE_CURSOR_TEXT } mp_mouse_cursor;
|
||||
|
||||
typedef i32 mp_window_style;
|
||||
static const mp_window_style MP_WINDOW_STYLE_NO_TITLE = 0x01<<0,
|
||||
MP_WINDOW_STYLE_FIXED_SIZE = 0x01<<1,
|
||||
MP_WINDOW_STYLE_NO_CLOSE = 0x01<<2,
|
||||
MP_WINDOW_STYLE_NO_MINIFY = 0x01<<3,
|
||||
MP_WINDOW_STYLE_NO_FOCUS = 0x01<<4,
|
||||
MP_WINDOW_STYLE_FLOAT = 0x01<<5,
|
||||
MP_WINDOW_STYLE_POPUPMENU = 0x01<<6,
|
||||
MP_WINDOW_STYLE_NO_BUTTONS = 0x01<<7;
|
||||
|
||||
typedef enum { MP_EVENT_NONE,
|
||||
MP_EVENT_KEYBOARD_MODS, //TODO: remove, keep only key?
|
||||
MP_EVENT_KEYBOARD_KEY,
|
||||
MP_EVENT_KEYBOARD_CHAR,
|
||||
MP_EVENT_MOUSE_BUTTON,
|
||||
MP_EVENT_MOUSE_MOVE,
|
||||
MP_EVENT_MOUSE_WHEEL,
|
||||
MP_EVENT_MOUSE_ENTER,
|
||||
MP_EVENT_MOUSE_LEAVE,
|
||||
MP_EVENT_WINDOW_RESIZE,
|
||||
MP_EVENT_WINDOW_MOVE,
|
||||
MP_EVENT_WINDOW_FOCUS,
|
||||
MP_EVENT_WINDOW_UNFOCUS,
|
||||
MP_EVENT_WINDOW_HIDE, // rename to minimize?
|
||||
MP_EVENT_WINDOW_SHOW, // rename to restore?
|
||||
MP_EVENT_WINDOW_CLOSE,
|
||||
MP_EVENT_PATHDROP,
|
||||
MP_EVENT_FRAME,
|
||||
MP_EVENT_QUIT } mp_event_type;
|
||||
|
||||
typedef enum { MP_KEY_NO_ACTION,
|
||||
MP_KEY_PRESS,
|
||||
MP_KEY_RELEASE,
|
||||
MP_KEY_REPEAT } mp_key_action;
|
||||
|
||||
typedef enum { MP_KEY_UNKNOWN = 0,
|
||||
MP_KEY_SPACE = 32,
|
||||
MP_KEY_APOSTROPHE = 39, /* ' */
|
||||
MP_KEY_COMMA = 44, /* , */
|
||||
MP_KEY_MINUS = 45, // -
|
||||
MP_KEY_PERIOD = 46, // .
|
||||
MP_KEY_SLASH = 47, // /
|
||||
MP_KEY_0 = 48,
|
||||
MP_KEY_1 = 49,
|
||||
MP_KEY_2 = 50,
|
||||
MP_KEY_3 = 51,
|
||||
MP_KEY_4 = 52,
|
||||
MP_KEY_5 = 53,
|
||||
MP_KEY_6 = 54,
|
||||
MP_KEY_7 = 55,
|
||||
MP_KEY_8 = 56,
|
||||
MP_KEY_9 = 57,
|
||||
MP_KEY_SEMICOLON = 59, // ;
|
||||
MP_KEY_EQUAL = 61, // =
|
||||
MP_KEY_A = 65,
|
||||
MP_KEY_B = 66,
|
||||
MP_KEY_C = 67,
|
||||
MP_KEY_D = 68,
|
||||
MP_KEY_E = 69,
|
||||
MP_KEY_F = 70,
|
||||
MP_KEY_G = 71,
|
||||
MP_KEY_H = 72,
|
||||
MP_KEY_I = 73,
|
||||
MP_KEY_J = 74,
|
||||
MP_KEY_K = 75,
|
||||
MP_KEY_L = 76,
|
||||
MP_KEY_M = 77,
|
||||
MP_KEY_N = 78,
|
||||
MP_KEY_O = 79,
|
||||
MP_KEY_P = 80,
|
||||
MP_KEY_Q = 81,
|
||||
MP_KEY_R = 82,
|
||||
MP_KEY_S = 83,
|
||||
MP_KEY_T = 84,
|
||||
MP_KEY_U = 85,
|
||||
MP_KEY_V = 86,
|
||||
MP_KEY_W = 87,
|
||||
MP_KEY_X = 88,
|
||||
MP_KEY_Y = 89,
|
||||
MP_KEY_Z = 90,
|
||||
MP_KEY_LEFT_BRACKET = 91, // [
|
||||
MP_KEY_BACKSLASH = 92, // \ */
|
||||
MP_KEY_RIGHT_BRACKET = 93, // ]
|
||||
MP_KEY_GRAVE_ACCENT = 96, // `
|
||||
MP_KEY_WORLD_1 = 161, // non-US #1
|
||||
MP_KEY_WORLD_2 = 162, // non-US #2
|
||||
MP_KEY_ESCAPE = 256,
|
||||
MP_KEY_ENTER = 257,
|
||||
MP_KEY_TAB = 258,
|
||||
MP_KEY_BACKSPACE = 259,
|
||||
MP_KEY_INSERT = 260,
|
||||
MP_KEY_DELETE = 261,
|
||||
MP_KEY_RIGHT = 262,
|
||||
MP_KEY_LEFT = 263,
|
||||
MP_KEY_DOWN = 264,
|
||||
MP_KEY_UP = 265,
|
||||
MP_KEY_PAGE_UP = 266,
|
||||
MP_KEY_PAGE_DOWN = 267,
|
||||
MP_KEY_HOME = 268,
|
||||
MP_KEY_END = 269,
|
||||
MP_KEY_CAPS_LOCK = 280,
|
||||
MP_KEY_SCROLL_LOCK = 281,
|
||||
MP_KEY_NUM_LOCK = 282,
|
||||
MP_KEY_PRINT_SCREEN = 283,
|
||||
MP_KEY_PAUSE = 284,
|
||||
MP_KEY_F1 = 290,
|
||||
MP_KEY_F2 = 291,
|
||||
MP_KEY_F3 = 292,
|
||||
MP_KEY_F4 = 293,
|
||||
MP_KEY_F5 = 294,
|
||||
MP_KEY_F6 = 295,
|
||||
MP_KEY_F7 = 296,
|
||||
MP_KEY_F8 = 297,
|
||||
MP_KEY_F9 = 298,
|
||||
MP_KEY_F10 = 299,
|
||||
MP_KEY_F11 = 300,
|
||||
MP_KEY_F12 = 301,
|
||||
MP_KEY_F13 = 302,
|
||||
MP_KEY_F14 = 303,
|
||||
MP_KEY_F15 = 304,
|
||||
MP_KEY_F16 = 305,
|
||||
MP_KEY_F17 = 306,
|
||||
MP_KEY_F18 = 307,
|
||||
MP_KEY_F19 = 308,
|
||||
MP_KEY_F20 = 309,
|
||||
MP_KEY_F21 = 310,
|
||||
MP_KEY_F22 = 311,
|
||||
MP_KEY_F23 = 312,
|
||||
MP_KEY_F24 = 313,
|
||||
MP_KEY_F25 = 314,
|
||||
MP_KEY_KP_0 = 320,
|
||||
MP_KEY_KP_1 = 321,
|
||||
MP_KEY_KP_2 = 322,
|
||||
MP_KEY_KP_3 = 323,
|
||||
MP_KEY_KP_4 = 324,
|
||||
MP_KEY_KP_5 = 325,
|
||||
MP_KEY_KP_6 = 326,
|
||||
MP_KEY_KP_7 = 327,
|
||||
MP_KEY_KP_8 = 328,
|
||||
MP_KEY_KP_9 = 329,
|
||||
MP_KEY_KP_DECIMAL = 330,
|
||||
MP_KEY_KP_DIVIDE = 331,
|
||||
MP_KEY_KP_MULTIPLY = 332,
|
||||
MP_KEY_KP_SUBTRACT = 333,
|
||||
MP_KEY_KP_ADD = 334,
|
||||
MP_KEY_KP_ENTER = 335,
|
||||
MP_KEY_KP_EQUAL = 336,
|
||||
MP_KEY_LEFT_SHIFT = 340,
|
||||
MP_KEY_LEFT_CONTROL = 341,
|
||||
MP_KEY_LEFT_ALT = 342,
|
||||
MP_KEY_LEFT_SUPER = 343,
|
||||
MP_KEY_RIGHT_SHIFT = 344,
|
||||
MP_KEY_RIGHT_CONTROL = 345,
|
||||
MP_KEY_RIGHT_ALT = 346,
|
||||
MP_KEY_RIGHT_SUPER = 347,
|
||||
MP_KEY_MENU = 348,
|
||||
MP_KEY_COUNT } mp_key_code;
|
||||
|
||||
typedef enum {
|
||||
MP_KEYMOD_NONE = 0x00,
|
||||
MP_KEYMOD_ALT = 0x01,
|
||||
MP_KEYMOD_SHIFT = 0x02,
|
||||
MP_KEYMOD_CTRL = 0x04,
|
||||
MP_KEYMOD_CMD = 0x08 } mp_keymod_flags;
|
||||
|
||||
typedef enum {
|
||||
MP_MOUSE_LEFT = 0x00,
|
||||
MP_MOUSE_RIGHT = 0x01,
|
||||
MP_MOUSE_MIDDLE = 0x02,
|
||||
MP_MOUSE_EXT1 = 0x03,
|
||||
MP_MOUSE_EXT2 = 0x04,
|
||||
MP_MOUSE_BUTTON_COUNT } mp_mouse_button;
|
||||
|
||||
typedef struct mp_key_event // keyboard and mouse buttons input
|
||||
{
|
||||
mp_key_action action;
|
||||
i32 code;
|
||||
mp_keymod_flags mods;
|
||||
char label[8];
|
||||
u8 labelLen;
|
||||
int clickCount;
|
||||
} mp_key_event;
|
||||
|
||||
typedef struct mp_char_event // character input
|
||||
{
|
||||
utf32 codepoint;
|
||||
char sequence[8];
|
||||
u8 seqLen;
|
||||
} mp_char_event;
|
||||
|
||||
typedef struct mp_move_event // mouse move/scroll
|
||||
{
|
||||
f32 x;
|
||||
f32 y;
|
||||
f32 deltaX;
|
||||
f32 deltaY;
|
||||
mp_keymod_flags mods;
|
||||
} mp_move_event;
|
||||
|
||||
typedef struct mp_frame_event // window resize / move
|
||||
{
|
||||
mp_rect rect;
|
||||
} mp_frame_event;
|
||||
|
||||
typedef struct mp_event
|
||||
{
|
||||
//TODO clipboard and path drop
|
||||
|
||||
mp_window window;
|
||||
mp_event_type type;
|
||||
|
||||
union
|
||||
{
|
||||
mp_key_event key;
|
||||
mp_char_event character;
|
||||
mp_move_event move;
|
||||
mp_frame_event frame;
|
||||
str8 path;
|
||||
};
|
||||
|
||||
//TODO(martin): chain externally ?
|
||||
list_elt list;
|
||||
} mp_event;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// app management
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
MP_API void mp_init(void);
|
||||
MP_API void mp_terminate(void);
|
||||
|
||||
MP_API bool mp_should_quit(void);
|
||||
MP_API void mp_cancel_quit(void);
|
||||
MP_API void mp_request_quit(void);
|
||||
|
||||
MP_API void mp_set_cursor(mp_mouse_cursor cursor);
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Main loop and events handling
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
MP_API void mp_pump_events(f64 timeout);
|
||||
MP_API bool mp_next_event(mp_event* event);
|
||||
|
||||
typedef void(*mp_live_resize_callback)(mp_event event, void* data);
|
||||
MP_API void mp_set_live_resize_callback(mp_live_resize_callback callback, void* data);
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// window management
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
MP_API bool mp_window_handle_is_null(mp_window window);
|
||||
MP_API mp_window mp_window_null_handle(void);
|
||||
|
||||
MP_API mp_window mp_window_create(mp_rect contentRect, const char* title, mp_window_style style);
|
||||
MP_API void mp_window_destroy(mp_window window);
|
||||
MP_API void* mp_window_native_pointer(mp_window window);
|
||||
|
||||
MP_API bool mp_window_should_close(mp_window window);
|
||||
MP_API void mp_window_request_close(mp_window window);
|
||||
MP_API void mp_window_cancel_close(mp_window window);
|
||||
|
||||
MP_API bool mp_window_is_hidden(mp_window window);
|
||||
MP_API void mp_window_hide(mp_window window);
|
||||
MP_API void mp_window_show(mp_window window);
|
||||
|
||||
MP_API bool mp_window_is_minimized(mp_window window);
|
||||
MP_API bool mp_window_is_maximized(mp_window window);
|
||||
MP_API void mp_window_minimize(mp_window window);
|
||||
MP_API void mp_window_maximize(mp_window window);
|
||||
MP_API void mp_window_restore(mp_window window);
|
||||
|
||||
MP_API bool mp_window_has_focus(mp_window window);
|
||||
MP_API void mp_window_focus(mp_window window);
|
||||
MP_API void mp_window_unfocus(mp_window window);
|
||||
|
||||
MP_API void mp_window_send_to_back(mp_window window);
|
||||
MP_API void mp_window_bring_to_front(mp_window window);
|
||||
|
||||
MP_API mp_rect mp_window_get_content_rect(mp_window window);
|
||||
MP_API mp_rect mp_window_get_frame_rect(mp_window window);
|
||||
MP_API void mp_window_set_content_rect(mp_window window, mp_rect contentRect);
|
||||
MP_API void mp_window_set_frame_rect(mp_window window, mp_rect frameRect);
|
||||
|
||||
MP_API void mp_window_center(mp_window window);
|
||||
|
||||
MP_API mp_rect mp_window_content_rect_for_frame_rect(mp_rect frameRect, mp_window_style style);
|
||||
MP_API mp_rect mp_window_frame_rect_for_content_rect(mp_rect contentRect, mp_window_style style);
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Input state polling
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
MP_API bool mp_key_down(mp_key_code key);
|
||||
MP_API int mp_key_pressed(mp_key_code key);
|
||||
MP_API int mp_key_released(mp_key_code key);
|
||||
MP_API int mp_key_repeated(mp_key_code key);
|
||||
|
||||
MP_API bool mp_mouse_down(mp_mouse_button button);
|
||||
MP_API int mp_mouse_pressed(mp_mouse_button button);
|
||||
MP_API int mp_mouse_released(mp_mouse_button button);
|
||||
MP_API bool mp_mouse_clicked(mp_mouse_button button);
|
||||
MP_API bool mp_mouse_double_clicked(mp_mouse_button button);
|
||||
|
||||
MP_API vec2 mp_mouse_position(void);
|
||||
MP_API vec2 mp_mouse_delta(void);
|
||||
MP_API vec2 mp_mouse_wheel(void);
|
||||
|
||||
MP_API str32 mp_input_text_utf32(mem_arena* arena);
|
||||
MP_API str8 mp_input_text_utf8(mem_arena* arena);
|
||||
|
||||
MP_API mp_keymod_flags mp_key_mods();
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Clipboard
|
||||
//--------------------------------------------------------------------
|
||||
MP_API void mp_clipboard_clear(void);
|
||||
|
||||
MP_API void mp_clipboard_set_string(str8 string);
|
||||
MP_API str8 mp_clipboard_get_string(mem_arena* arena);
|
||||
MP_API str8 mp_clipboard_copy_string(str8 backing);
|
||||
|
||||
MP_API bool mp_clipboard_has_tag(const char* tag);
|
||||
MP_API void mp_clipboard_set_data_for_tag(const char* tag, str8 data);
|
||||
MP_API str8 mp_clipboard_get_data_for_tag(mem_arena* arena, const char* tag);
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// native open/save/alert windows
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
MP_API str8 mp_open_dialog(mem_arena* arena,
|
||||
const char* title,
|
||||
const char* defaultPath,
|
||||
int filterCount,
|
||||
const char** filters,
|
||||
bool directory);
|
||||
|
||||
MP_API str8 mp_save_dialog(mem_arena* arena,
|
||||
const char* title,
|
||||
const char* defaultPath,
|
||||
int filterCount,
|
||||
const char** filters);
|
||||
|
||||
MP_API int mp_alert_popup(const char* title,
|
||||
const char* message,
|
||||
u32 count,
|
||||
const char** options);
|
||||
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// file system stuff... //TODO: move elsewhere
|
||||
//--------------------------------------------------------------------
|
||||
MP_API int mp_file_move(str8 from, str8 to);
|
||||
MP_API int mp_file_remove(str8 path);
|
||||
|
||||
MP_API int mp_directory_create(str8 path);
|
||||
|
||||
MP_API str8 mp_app_get_resource_path(mem_arena* arena, const char* name);
|
||||
MP_API str8 mp_app_get_executable_path(mem_arena* arena);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif //__PLATFORM_APP_H_
|
||||
|
|
|
@ -1,154 +1,154 @@
|
|||
/************************************************************//**
|
||||
*
|
||||
* @file: mp_app_internal.h
|
||||
* @author: Martin Fouilleul
|
||||
* @date: 23/12/2022
|
||||
* @revision:
|
||||
*
|
||||
*****************************************************************/
|
||||
#ifndef __MP_APP_INTERNAL_H_
|
||||
#define __MP_APP_INTERNAL_H_
|
||||
|
||||
#include"mp_app.h"
|
||||
|
||||
#include"platform.h"
|
||||
#include"ringbuffer.h"
|
||||
|
||||
#if defined(OS_WIN64) || defined(OS_WIN32)
|
||||
#include"win32_app.h"
|
||||
#elif defined(OS_MACOS)
|
||||
#include"osx_app.h"
|
||||
#else
|
||||
#error "platform not supported yet"
|
||||
#endif
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// Input State
|
||||
//---------------------------------------------------------------
|
||||
|
||||
typedef struct mp_key_utf8
|
||||
{
|
||||
u8 labelLen;
|
||||
char label[8];
|
||||
} mp_key_utf8;
|
||||
|
||||
typedef struct mp_key_state
|
||||
{
|
||||
u64 lastUpdate;
|
||||
u32 transitionCount;
|
||||
u32 repeatCount;
|
||||
bool down;
|
||||
bool sysClicked;
|
||||
bool sysDoubleClicked;
|
||||
|
||||
} mp_key_state;
|
||||
|
||||
typedef struct mp_keyboard_state
|
||||
{
|
||||
mp_key_state keys[MP_KEY_COUNT];
|
||||
mp_keymod_flags mods;
|
||||
} mp_keyboard_state;
|
||||
|
||||
typedef struct mp_mouse_state
|
||||
{
|
||||
u64 lastUpdate;
|
||||
bool posValid;
|
||||
vec2 pos;
|
||||
vec2 delta;
|
||||
vec2 wheel;
|
||||
|
||||
union
|
||||
{
|
||||
mp_key_state buttons[MP_MOUSE_BUTTON_COUNT];
|
||||
struct
|
||||
{
|
||||
mp_key_state left;
|
||||
mp_key_state right;
|
||||
mp_key_state middle;
|
||||
mp_key_state ext1;
|
||||
mp_key_state ext2;
|
||||
};
|
||||
};
|
||||
} mp_mouse_state;
|
||||
|
||||
enum { MP_INPUT_TEXT_BACKING_SIZE = 64 };
|
||||
|
||||
typedef struct mp_text_state
|
||||
{
|
||||
u64 lastUpdate;
|
||||
utf32 backing[MP_INPUT_TEXT_BACKING_SIZE];
|
||||
str32 codePoints;
|
||||
} mp_text_state;
|
||||
|
||||
typedef struct mp_input_state
|
||||
{
|
||||
u64 frameCounter;
|
||||
mp_keyboard_state keyboard;
|
||||
mp_mouse_state mouse;
|
||||
mp_text_state text;
|
||||
} mp_input_state;
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// Window structure
|
||||
//---------------------------------------------------------------
|
||||
|
||||
typedef struct mp_frame_stats
|
||||
{
|
||||
f64 start;
|
||||
f64 workTime;
|
||||
f64 remainingTime;
|
||||
f64 targetFramePeriod;
|
||||
} mp_frame_stats;
|
||||
|
||||
typedef struct mp_window_data
|
||||
{
|
||||
list_elt freeListElt;
|
||||
u32 generation;
|
||||
|
||||
mp_rect contentRect;
|
||||
mp_rect frameRect;
|
||||
mp_window_style style;
|
||||
|
||||
bool shouldClose; //TODO could be in status flags
|
||||
bool hidden;
|
||||
bool minimized;
|
||||
|
||||
MP_PLATFORM_WINDOW_DATA
|
||||
} mp_window_data;
|
||||
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// Global App State
|
||||
//---------------------------------------------------------------
|
||||
|
||||
enum { MP_APP_MAX_WINDOWS = 128 };
|
||||
|
||||
typedef struct mp_app
|
||||
{
|
||||
bool init;
|
||||
bool shouldQuit;
|
||||
bool minimized;
|
||||
|
||||
str8 pendingPathDrop;
|
||||
mem_arena eventArena;
|
||||
|
||||
ringbuffer eventQueue;
|
||||
|
||||
mp_frame_stats frameStats;
|
||||
|
||||
mp_window_data windowPool[MP_APP_MAX_WINDOWS];
|
||||
list_info windowFreeList;
|
||||
|
||||
mp_live_resize_callback liveResizeCallback;
|
||||
void* liveResizeData;
|
||||
|
||||
mp_input_state inputState;
|
||||
|
||||
mp_key_utf8 keyLabels[512];
|
||||
int keyCodes[512];
|
||||
int nativeKeys[MP_KEY_COUNT];
|
||||
|
||||
MP_PLATFORM_APP_DATA
|
||||
} mp_app;
|
||||
|
||||
#endif // __MP_APP_INTERNAL_H_
|
||||
/************************************************************//**
|
||||
*
|
||||
* @file: mp_app_internal.h
|
||||
* @author: Martin Fouilleul
|
||||
* @date: 23/12/2022
|
||||
* @revision:
|
||||
*
|
||||
*****************************************************************/
|
||||
#ifndef __MP_APP_INTERNAL_H_
|
||||
#define __MP_APP_INTERNAL_H_
|
||||
|
||||
#include"mp_app.h"
|
||||
|
||||
#include"platform.h"
|
||||
#include"ringbuffer.h"
|
||||
|
||||
#if defined(OS_WIN64) || defined(OS_WIN32)
|
||||
#include"win32_app.h"
|
||||
#elif defined(OS_MACOS)
|
||||
#include"osx_app.h"
|
||||
#else
|
||||
#error "platform not supported yet"
|
||||
#endif
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// Input State
|
||||
//---------------------------------------------------------------
|
||||
|
||||
typedef struct mp_key_utf8
|
||||
{
|
||||
u8 labelLen;
|
||||
char label[8];
|
||||
} mp_key_utf8;
|
||||
|
||||
typedef struct mp_key_state
|
||||
{
|
||||
u64 lastUpdate;
|
||||
u32 transitionCount;
|
||||
u32 repeatCount;
|
||||
bool down;
|
||||
bool sysClicked;
|
||||
bool sysDoubleClicked;
|
||||
|
||||
} mp_key_state;
|
||||
|
||||
typedef struct mp_keyboard_state
|
||||
{
|
||||
mp_key_state keys[MP_KEY_COUNT];
|
||||
mp_keymod_flags mods;
|
||||
} mp_keyboard_state;
|
||||
|
||||
typedef struct mp_mouse_state
|
||||
{
|
||||
u64 lastUpdate;
|
||||
bool posValid;
|
||||
vec2 pos;
|
||||
vec2 delta;
|
||||
vec2 wheel;
|
||||
|
||||
union
|
||||
{
|
||||
mp_key_state buttons[MP_MOUSE_BUTTON_COUNT];
|
||||
struct
|
||||
{
|
||||
mp_key_state left;
|
||||
mp_key_state right;
|
||||
mp_key_state middle;
|
||||
mp_key_state ext1;
|
||||
mp_key_state ext2;
|
||||
};
|
||||
};
|
||||
} mp_mouse_state;
|
||||
|
||||
enum { MP_INPUT_TEXT_BACKING_SIZE = 64 };
|
||||
|
||||
typedef struct mp_text_state
|
||||
{
|
||||
u64 lastUpdate;
|
||||
utf32 backing[MP_INPUT_TEXT_BACKING_SIZE];
|
||||
str32 codePoints;
|
||||
} mp_text_state;
|
||||
|
||||
typedef struct mp_input_state
|
||||
{
|
||||
u64 frameCounter;
|
||||
mp_keyboard_state keyboard;
|
||||
mp_mouse_state mouse;
|
||||
mp_text_state text;
|
||||
} mp_input_state;
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// Window structure
|
||||
//---------------------------------------------------------------
|
||||
|
||||
typedef struct mp_frame_stats
|
||||
{
|
||||
f64 start;
|
||||
f64 workTime;
|
||||
f64 remainingTime;
|
||||
f64 targetFramePeriod;
|
||||
} mp_frame_stats;
|
||||
|
||||
typedef struct mp_window_data
|
||||
{
|
||||
list_elt freeListElt;
|
||||
u32 generation;
|
||||
|
||||
mp_rect contentRect;
|
||||
mp_rect frameRect;
|
||||
mp_window_style style;
|
||||
|
||||
bool shouldClose; //TODO could be in status flags
|
||||
bool hidden;
|
||||
bool minimized;
|
||||
|
||||
MP_PLATFORM_WINDOW_DATA
|
||||
} mp_window_data;
|
||||
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// Global App State
|
||||
//---------------------------------------------------------------
|
||||
|
||||
enum { MP_APP_MAX_WINDOWS = 128 };
|
||||
|
||||
typedef struct mp_app
|
||||
{
|
||||
bool init;
|
||||
bool shouldQuit;
|
||||
bool minimized;
|
||||
|
||||
str8 pendingPathDrop;
|
||||
mem_arena eventArena;
|
||||
|
||||
ringbuffer eventQueue;
|
||||
|
||||
mp_frame_stats frameStats;
|
||||
|
||||
mp_window_data windowPool[MP_APP_MAX_WINDOWS];
|
||||
list_info windowFreeList;
|
||||
|
||||
mp_live_resize_callback liveResizeCallback;
|
||||
void* liveResizeData;
|
||||
|
||||
mp_input_state inputState;
|
||||
|
||||
mp_key_utf8 keyLabels[512];
|
||||
int keyCodes[512];
|
||||
int nativeKeys[MP_KEY_COUNT];
|
||||
|
||||
MP_PLATFORM_APP_DATA
|
||||
} mp_app;
|
||||
|
||||
#endif // __MP_APP_INTERNAL_H_
|
||||
|
|
1128
src/mtl_canvas.m
1128
src/mtl_canvas.m
File diff suppressed because it is too large
Load Diff
|
@ -1,347 +1,347 @@
|
|||
|
||||
#include<metal_stdlib>
|
||||
#include<simd/simd.h>
|
||||
|
||||
#include"mtl_shader.h"
|
||||
|
||||
using namespace metal;
|
||||
|
||||
struct vs_out
|
||||
{
|
||||
float4 pos [[position]];
|
||||
float2 uv;
|
||||
};
|
||||
|
||||
vertex vs_out VertexShader(ushort vid [[vertex_id]])
|
||||
{
|
||||
vs_out out;
|
||||
out.uv = float2((vid << 1) & 2, vid & 2);
|
||||
out.pos = float4(out.uv * float2(2, 2) + float2(-1, -1), 0, 1);
|
||||
return(out);
|
||||
}
|
||||
|
||||
fragment float4 FragmentShader(vs_out i [[stage_in]], texture2d<float> tex [[texture(0)]])
|
||||
{
|
||||
constexpr sampler smp(mip_filter::nearest, mag_filter::linear, min_filter::linear);
|
||||
return(tex.sample(smp, i.uv));
|
||||
}
|
||||
|
||||
|
||||
bool is_top_left(float2 a, float2 b)
|
||||
{
|
||||
return( (a.y == b.y && b.x < a.x)
|
||||
||(b.y < a.y));
|
||||
}
|
||||
|
||||
kernel void BoundingBoxKernel(constant mg_vertex* vertexBuffer [[buffer(0)]],
|
||||
constant uint* indexBuffer [[buffer(1)]],
|
||||
constant mg_shape* shapeBuffer [[buffer(2)]],
|
||||
device mg_triangle_data* triangleArray [[buffer(3)]],
|
||||
device float4* boxArray [[buffer(4)]],
|
||||
constant float* contentsScaling [[buffer(5)]],
|
||||
uint gid [[thread_position_in_grid]])
|
||||
{
|
||||
uint triangleIndex = gid;
|
||||
uint vertexIndex = triangleIndex*3;
|
||||
|
||||
uint i0 = indexBuffer[vertexIndex];
|
||||
uint i1 = indexBuffer[vertexIndex+1];
|
||||
uint i2 = indexBuffer[vertexIndex+2];
|
||||
|
||||
float2 p0 = vertexBuffer[i0].pos * contentsScaling[0];
|
||||
float2 p1 = vertexBuffer[i1].pos * contentsScaling[0];
|
||||
float2 p2 = vertexBuffer[i2].pos * contentsScaling[0];
|
||||
|
||||
//NOTE(martin): compute triangle bounding box
|
||||
float2 boxMin = min(min(p0, p1), p2);
|
||||
float2 boxMax = max(max(p0, p1), p2);
|
||||
|
||||
//NOTE(martin): clip bounding box against clip rect
|
||||
int shapeIndex = vertexBuffer[i0].shapeIndex;
|
||||
|
||||
vector_float4 clip = contentsScaling[0]*shapeBuffer[shapeIndex].clip;
|
||||
|
||||
//NOTE(martin): intersect with current clip
|
||||
boxMin = max(boxMin, clip.xy);
|
||||
boxMax = min(boxMax, clip.zw);
|
||||
|
||||
//NOTE(martin): reorder triangle counter-clockwise and compute bias for each edge
|
||||
float cw = (p1 - p0).x*(p2 - p0).y - (p1 - p0).y*(p2 - p0).x;
|
||||
if(cw < 0)
|
||||
{
|
||||
uint tmpIndex = i1;
|
||||
i1 = i2;
|
||||
i2 = tmpIndex;
|
||||
|
||||
float2 tmpPoint = p1;
|
||||
p1 = p2;
|
||||
p2 = tmpPoint;
|
||||
}
|
||||
int bias0 = is_top_left(p1, p2) ? 0 : -1;
|
||||
int bias1 = is_top_left(p2, p0) ? 0 : -1;
|
||||
int bias2 = is_top_left(p0, p1) ? 0 : -1;
|
||||
|
||||
//NOTE(martin): fill triangle data
|
||||
boxArray[triangleIndex] = float4(boxMin.x, boxMin.y, boxMax.x, boxMax.y);
|
||||
|
||||
triangleArray[triangleIndex].shapeIndex = shapeIndex;
|
||||
triangleArray[triangleIndex].i0 = i0;
|
||||
triangleArray[triangleIndex].i1 = i1;
|
||||
triangleArray[triangleIndex].i2 = i2;
|
||||
triangleArray[triangleIndex].p0 = p0;
|
||||
triangleArray[triangleIndex].p1 = p1;
|
||||
triangleArray[triangleIndex].p2 = p2;
|
||||
triangleArray[triangleIndex].bias0 = bias0;
|
||||
triangleArray[triangleIndex].bias1 = bias1;
|
||||
triangleArray[triangleIndex].bias2 = bias2;
|
||||
}
|
||||
|
||||
kernel void TileKernel(const device float4* boxArray [[buffer(0)]],
|
||||
device volatile atomic_uint* tileCounters [[buffer(1)]],
|
||||
device uint* tilesArray [[buffer(2)]],
|
||||
constant vector_uint2* viewport [[buffer(3)]],
|
||||
uint gid [[thread_position_in_grid]])
|
||||
{
|
||||
uint2 tilesMatrixDim = (*viewport - 1) / RENDERER_TILE_SIZE + 1;
|
||||
uint nTilesX = tilesMatrixDim.x;
|
||||
uint nTilesY = tilesMatrixDim.y;
|
||||
|
||||
uint triangleIndex = gid;
|
||||
uint4 box = uint4(floor(boxArray[triangleIndex]))/RENDERER_TILE_SIZE;
|
||||
uint xMin = max((uint)0, box.x);
|
||||
uint yMin = max((uint)0, box.y);
|
||||
uint xMax = min(box.z, nTilesX-1);
|
||||
uint yMax = min(box.w, nTilesY-1);
|
||||
|
||||
for(uint y = yMin; y <= yMax; y++)
|
||||
{
|
||||
for(uint x = xMin ; x <= xMax; x++)
|
||||
{
|
||||
uint tileIndex = y*nTilesX + x;
|
||||
device uint* tileBuffer = tilesArray + tileIndex*RENDERER_TILE_BUFFER_SIZE;
|
||||
uint counter = atomic_fetch_add_explicit(&(tileCounters[tileIndex]), 1, memory_order_relaxed);
|
||||
tileBuffer[counter] = triangleIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
kernel void SortKernel(const device uint* tileCounters [[buffer(0)]],
|
||||
const device mg_triangle_data* triangleArray [[buffer(1)]],
|
||||
device uint* tilesArray [[buffer(2)]],
|
||||
constant vector_uint2* viewport [[buffer(3)]],
|
||||
uint gid [[thread_position_in_grid]])
|
||||
{
|
||||
uint tileIndex = gid;
|
||||
device uint* tileBuffer = tilesArray + tileIndex*RENDERER_TILE_BUFFER_SIZE;
|
||||
uint tileBufferSize = tileCounters[tileIndex];
|
||||
|
||||
for(int eltIndex=0; eltIndex < (int)tileBufferSize; eltIndex++)
|
||||
{
|
||||
uint elt = tileBuffer[eltIndex];
|
||||
uint eltZIndex = triangleArray[elt].shapeIndex;
|
||||
|
||||
int backIndex = eltIndex-1;
|
||||
for(; backIndex >= 0; backIndex--)
|
||||
{
|
||||
uint backElt = tileBuffer[backIndex];
|
||||
uint backEltZIndex = triangleArray[backElt].shapeIndex;
|
||||
if(eltZIndex >= backEltZIndex)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
tileBuffer[backIndex+1] = backElt;
|
||||
}
|
||||
}
|
||||
tileBuffer[backIndex+1] = elt;
|
||||
}
|
||||
}
|
||||
|
||||
float orient2d(float2 a, float2 b, float2 c)
|
||||
{
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
//TODO(martin): FIX this. This is a **horrible** quick hack to fix the precision issues
|
||||
// arising when a, b, and c are close. But it degrades when a, c, and c
|
||||
// are big. The proper solution is to change the expression to avoid
|
||||
// precision loss but I'm too busy/lazy to do it now.
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
a *= 10;
|
||||
b *= 10;
|
||||
c *= 10;
|
||||
return((b.x-a.x)*(c.y-a.y) - (b.y-a.y)*(c.x-a.x));
|
||||
}
|
||||
|
||||
kernel void RenderKernel(texture2d<float, access::write> outTexture [[texture(0)]],
|
||||
texture2d<float> texAtlas [[texture(1)]],
|
||||
const device mg_vertex* vertexBuffer [[buffer(0)]],
|
||||
const device mg_shape* shapeBuffer [[buffer(1)]],
|
||||
device uint* tileCounters [[buffer(2)]],
|
||||
const device uint* tilesArray [[buffer(3)]],
|
||||
const device mg_triangle_data* triangleArray [[buffer(4)]],
|
||||
const device float4* boxArray [[buffer(5)]],
|
||||
constant vector_float4* clearColor [[buffer(6)]],
|
||||
constant int* useTexture [[buffer(7)]],
|
||||
constant float* contentsScaling [[buffer(8)]],
|
||||
uint2 gid [[thread_position_in_grid]],
|
||||
uint2 tgid [[threadgroup_position_in_grid]],
|
||||
uint2 threadsPerThreadgroup [[threads_per_threadgroup]],
|
||||
uint2 gridSize [[threads_per_grid]])
|
||||
{
|
||||
//TODO: guard against thread group size not equal to tile size?
|
||||
const uint2 tilesMatrixDim = (gridSize - 1) / RENDERER_TILE_SIZE + 1;
|
||||
// const uint2 tilePos = tgid * threadsPerThreadgroup / RENDERER_TILE_SIZE;
|
||||
const uint2 tilePos = gid/RENDERER_TILE_SIZE;
|
||||
const uint tileIndex = tilePos.y * tilesMatrixDim.x + tilePos.x;
|
||||
const device uint* tileBuffer = tilesArray + tileIndex * RENDERER_TILE_BUFFER_SIZE;
|
||||
|
||||
const uint tileBufferSize = tileCounters[tileIndex];
|
||||
|
||||
#ifdef RENDERER_DEBUG_TILES
|
||||
//NOTE(martin): color code debug values and show the tile grid
|
||||
uint nTileX = tilesMatrixDim.x;
|
||||
uint nTileY = tilesMatrixDim.y;
|
||||
|
||||
if(tilePos.x > nTileX || tilePos.y > nTileY)
|
||||
{
|
||||
outTexture.write(float4(0, 1, 1, 1), gid);
|
||||
return;
|
||||
}
|
||||
|
||||
if((gid.x % RENDERER_TILE_SIZE == 0) || (gid.y % RENDERER_TILE_SIZE == 0))
|
||||
{
|
||||
outTexture.write(float4(0, 0, 0, 1), gid);
|
||||
return;
|
||||
}
|
||||
if(tileBufferSize <= 0)
|
||||
{
|
||||
outTexture.write(float4(0, 1, 0, 1), gid);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
outTexture.write(float4(1, 0, 0, 1), gid);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
const float2 sampleOffsets[6] = { float2(5./12, 5./12),
|
||||
float2(-3./12, 3./12),
|
||||
float2(1./12, 1./12),
|
||||
float2(3./12, -1./12),
|
||||
float2(-5./12, -3./12),
|
||||
float2(-1./12, -5./12)};
|
||||
|
||||
int zIndices[6];
|
||||
uint flipCounts[6];
|
||||
float4 pixelColors[6];
|
||||
float4 nextColors[6];
|
||||
for(int i=0; i<6; i++)
|
||||
{
|
||||
zIndices[i] = -1;
|
||||
flipCounts[i] = 0;
|
||||
pixelColors[i] = float4(0, 0, 0, 0);
|
||||
nextColors[i] = float4(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
for(uint tileBufferIndex=0; tileBufferIndex < tileBufferSize; tileBufferIndex++)
|
||||
{
|
||||
float4 box = boxArray[tileBuffer[tileBufferIndex]];
|
||||
const device mg_triangle_data* triangle = &triangleArray[tileBuffer[tileBufferIndex]];
|
||||
|
||||
float2 p0 = triangle->p0;
|
||||
float2 p1 = triangle->p1;
|
||||
float2 p2 = triangle->p2;
|
||||
|
||||
int bias0 = triangle->bias0;
|
||||
int bias1 = triangle->bias1;
|
||||
int bias2 = triangle->bias2;
|
||||
|
||||
const device mg_vertex* v0 = &(vertexBuffer[triangle->i0]);
|
||||
const device mg_vertex* v1 = &(vertexBuffer[triangle->i1]);
|
||||
const device mg_vertex* v2 = &(vertexBuffer[triangle->i2]);
|
||||
|
||||
float4 cubic0 = v0->cubic;
|
||||
float4 cubic1 = v1->cubic;
|
||||
float4 cubic2 = v2->cubic;
|
||||
|
||||
int shapeIndex = v0->shapeIndex;
|
||||
float4 color = shapeBuffer[shapeIndex].color;
|
||||
color.rgb *= color.a;
|
||||
|
||||
const device float* uvTransform2x3 = shapeBuffer[shapeIndex].uvTransform;
|
||||
matrix_float3x3 uvTransform = {{uvTransform2x3[0], uvTransform2x3[3], 0},
|
||||
{uvTransform2x3[1], uvTransform2x3[4], 0},
|
||||
{uvTransform2x3[2], uvTransform2x3[5], 1}};
|
||||
|
||||
for(int i=0; i<6; i++)
|
||||
{
|
||||
float2 samplePoint = (float2)gid + sampleOffsets[i];
|
||||
|
||||
//NOTE(martin): cull if pixel is outside box
|
||||
if(samplePoint.x < box.x || samplePoint.x > box.z || samplePoint.y < box.y || samplePoint.y > box.w)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
float w0 = orient2d(p1, p2, samplePoint);
|
||||
float w1 = orient2d(p2, p0, samplePoint);
|
||||
float w2 = orient2d(p0, p1, samplePoint);
|
||||
|
||||
if(((int)w0+bias0) >= 0 && ((int)w1+bias1) >= 0 && ((int)w2+bias2) >= 0)
|
||||
{
|
||||
float4 cubic = (cubic0*w0 + cubic1*w1 + cubic2*w2)/(w0+w1+w2);
|
||||
|
||||
//float2 uv = (uv0*w0 + uv1*w1 + uv2*w2)/(w0+w1+w2);
|
||||
float2 uv = (uvTransform*(float3(samplePoint.xy/contentsScaling[0], 1))).xy;
|
||||
|
||||
float4 texColor = float4(1, 1, 1, 1);
|
||||
if(*useTexture)
|
||||
{
|
||||
constexpr sampler smp(mip_filter::nearest, mag_filter::linear, min_filter::linear);
|
||||
texColor = texAtlas.sample(smp, uv);
|
||||
texColor.rgb *= texColor.a;
|
||||
}
|
||||
//TODO(martin): this is a quick and dirty fix for solid polygons where we use
|
||||
// cubic = (1, 1, 1, 1) on all vertices, which can cause small errors to
|
||||
// flip the sign.
|
||||
// We should really use another value that always lead to <= 0, but we must
|
||||
// make sure we never share these vertices with bezier shapes.
|
||||
// Alternatively, an ugly (but maybe less than this one) solution would be
|
||||
// to check if uvs are equal on all vertices of the triangle and always render
|
||||
// those triangles.
|
||||
float eps = 0.0001;
|
||||
if(cubic.w*(cubic.x*cubic.x*cubic.x - cubic.y*cubic.z) <= eps)
|
||||
{
|
||||
if(shapeIndex == zIndices[i])
|
||||
{
|
||||
flipCounts[i]++;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(flipCounts[i] & 0x01)
|
||||
{
|
||||
pixelColors[i] = nextColors[i];
|
||||
}
|
||||
|
||||
float4 nextCol = color*texColor;
|
||||
nextColors[i] = pixelColors[i]*(1-nextCol.a) +nextCol.a*nextCol;
|
||||
|
||||
zIndices[i] = shapeIndex;
|
||||
flipCounts[i] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
float4 out = float4(0, 0, 0, 0);
|
||||
for(int i=0; i<6; i++)
|
||||
{
|
||||
if(flipCounts[i] & 0x01)
|
||||
{
|
||||
pixelColors[i] = nextColors[i];
|
||||
}
|
||||
out += pixelColors[i];
|
||||
}
|
||||
out = out/6.;
|
||||
outTexture.write(out, gid);
|
||||
}
|
||||
|
||||
#include<metal_stdlib>
|
||||
#include<simd/simd.h>
|
||||
|
||||
#include"mtl_shader.h"
|
||||
|
||||
using namespace metal;
|
||||
|
||||
struct vs_out
|
||||
{
|
||||
float4 pos [[position]];
|
||||
float2 uv;
|
||||
};
|
||||
|
||||
vertex vs_out VertexShader(ushort vid [[vertex_id]])
|
||||
{
|
||||
vs_out out;
|
||||
out.uv = float2((vid << 1) & 2, vid & 2);
|
||||
out.pos = float4(out.uv * float2(2, 2) + float2(-1, -1), 0, 1);
|
||||
return(out);
|
||||
}
|
||||
|
||||
fragment float4 FragmentShader(vs_out i [[stage_in]], texture2d<float> tex [[texture(0)]])
|
||||
{
|
||||
constexpr sampler smp(mip_filter::nearest, mag_filter::linear, min_filter::linear);
|
||||
return(tex.sample(smp, i.uv));
|
||||
}
|
||||
|
||||
|
||||
bool is_top_left(float2 a, float2 b)
|
||||
{
|
||||
return( (a.y == b.y && b.x < a.x)
|
||||
||(b.y < a.y));
|
||||
}
|
||||
|
||||
kernel void BoundingBoxKernel(constant mg_vertex* vertexBuffer [[buffer(0)]],
|
||||
constant uint* indexBuffer [[buffer(1)]],
|
||||
constant mg_shape* shapeBuffer [[buffer(2)]],
|
||||
device mg_triangle_data* triangleArray [[buffer(3)]],
|
||||
device float4* boxArray [[buffer(4)]],
|
||||
constant float* contentsScaling [[buffer(5)]],
|
||||
uint gid [[thread_position_in_grid]])
|
||||
{
|
||||
uint triangleIndex = gid;
|
||||
uint vertexIndex = triangleIndex*3;
|
||||
|
||||
uint i0 = indexBuffer[vertexIndex];
|
||||
uint i1 = indexBuffer[vertexIndex+1];
|
||||
uint i2 = indexBuffer[vertexIndex+2];
|
||||
|
||||
float2 p0 = vertexBuffer[i0].pos * contentsScaling[0];
|
||||
float2 p1 = vertexBuffer[i1].pos * contentsScaling[0];
|
||||
float2 p2 = vertexBuffer[i2].pos * contentsScaling[0];
|
||||
|
||||
//NOTE(martin): compute triangle bounding box
|
||||
float2 boxMin = min(min(p0, p1), p2);
|
||||
float2 boxMax = max(max(p0, p1), p2);
|
||||
|
||||
//NOTE(martin): clip bounding box against clip rect
|
||||
int shapeIndex = vertexBuffer[i0].shapeIndex;
|
||||
|
||||
vector_float4 clip = contentsScaling[0]*shapeBuffer[shapeIndex].clip;
|
||||
|
||||
//NOTE(martin): intersect with current clip
|
||||
boxMin = max(boxMin, clip.xy);
|
||||
boxMax = min(boxMax, clip.zw);
|
||||
|
||||
//NOTE(martin): reorder triangle counter-clockwise and compute bias for each edge
|
||||
float cw = (p1 - p0).x*(p2 - p0).y - (p1 - p0).y*(p2 - p0).x;
|
||||
if(cw < 0)
|
||||
{
|
||||
uint tmpIndex = i1;
|
||||
i1 = i2;
|
||||
i2 = tmpIndex;
|
||||
|
||||
float2 tmpPoint = p1;
|
||||
p1 = p2;
|
||||
p2 = tmpPoint;
|
||||
}
|
||||
int bias0 = is_top_left(p1, p2) ? 0 : -1;
|
||||
int bias1 = is_top_left(p2, p0) ? 0 : -1;
|
||||
int bias2 = is_top_left(p0, p1) ? 0 : -1;
|
||||
|
||||
//NOTE(martin): fill triangle data
|
||||
boxArray[triangleIndex] = float4(boxMin.x, boxMin.y, boxMax.x, boxMax.y);
|
||||
|
||||
triangleArray[triangleIndex].shapeIndex = shapeIndex;
|
||||
triangleArray[triangleIndex].i0 = i0;
|
||||
triangleArray[triangleIndex].i1 = i1;
|
||||
triangleArray[triangleIndex].i2 = i2;
|
||||
triangleArray[triangleIndex].p0 = p0;
|
||||
triangleArray[triangleIndex].p1 = p1;
|
||||
triangleArray[triangleIndex].p2 = p2;
|
||||
triangleArray[triangleIndex].bias0 = bias0;
|
||||
triangleArray[triangleIndex].bias1 = bias1;
|
||||
triangleArray[triangleIndex].bias2 = bias2;
|
||||
}
|
||||
|
||||
kernel void TileKernel(const device float4* boxArray [[buffer(0)]],
|
||||
device volatile atomic_uint* tileCounters [[buffer(1)]],
|
||||
device uint* tilesArray [[buffer(2)]],
|
||||
constant vector_uint2* viewport [[buffer(3)]],
|
||||
uint gid [[thread_position_in_grid]])
|
||||
{
|
||||
uint2 tilesMatrixDim = (*viewport - 1) / RENDERER_TILE_SIZE + 1;
|
||||
uint nTilesX = tilesMatrixDim.x;
|
||||
uint nTilesY = tilesMatrixDim.y;
|
||||
|
||||
uint triangleIndex = gid;
|
||||
uint4 box = uint4(floor(boxArray[triangleIndex]))/RENDERER_TILE_SIZE;
|
||||
uint xMin = max((uint)0, box.x);
|
||||
uint yMin = max((uint)0, box.y);
|
||||
uint xMax = min(box.z, nTilesX-1);
|
||||
uint yMax = min(box.w, nTilesY-1);
|
||||
|
||||
for(uint y = yMin; y <= yMax; y++)
|
||||
{
|
||||
for(uint x = xMin ; x <= xMax; x++)
|
||||
{
|
||||
uint tileIndex = y*nTilesX + x;
|
||||
device uint* tileBuffer = tilesArray + tileIndex*RENDERER_TILE_BUFFER_SIZE;
|
||||
uint counter = atomic_fetch_add_explicit(&(tileCounters[tileIndex]), 1, memory_order_relaxed);
|
||||
tileBuffer[counter] = triangleIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
kernel void SortKernel(const device uint* tileCounters [[buffer(0)]],
|
||||
const device mg_triangle_data* triangleArray [[buffer(1)]],
|
||||
device uint* tilesArray [[buffer(2)]],
|
||||
constant vector_uint2* viewport [[buffer(3)]],
|
||||
uint gid [[thread_position_in_grid]])
|
||||
{
|
||||
uint tileIndex = gid;
|
||||
device uint* tileBuffer = tilesArray + tileIndex*RENDERER_TILE_BUFFER_SIZE;
|
||||
uint tileBufferSize = tileCounters[tileIndex];
|
||||
|
||||
for(int eltIndex=0; eltIndex < (int)tileBufferSize; eltIndex++)
|
||||
{
|
||||
uint elt = tileBuffer[eltIndex];
|
||||
uint eltZIndex = triangleArray[elt].shapeIndex;
|
||||
|
||||
int backIndex = eltIndex-1;
|
||||
for(; backIndex >= 0; backIndex--)
|
||||
{
|
||||
uint backElt = tileBuffer[backIndex];
|
||||
uint backEltZIndex = triangleArray[backElt].shapeIndex;
|
||||
if(eltZIndex >= backEltZIndex)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
tileBuffer[backIndex+1] = backElt;
|
||||
}
|
||||
}
|
||||
tileBuffer[backIndex+1] = elt;
|
||||
}
|
||||
}
|
||||
|
||||
float orient2d(float2 a, float2 b, float2 c)
|
||||
{
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
//TODO(martin): FIX this. This is a **horrible** quick hack to fix the precision issues
|
||||
// arising when a, b, and c are close. But it degrades when a, c, and c
|
||||
// are big. The proper solution is to change the expression to avoid
|
||||
// precision loss but I'm too busy/lazy to do it now.
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
a *= 10;
|
||||
b *= 10;
|
||||
c *= 10;
|
||||
return((b.x-a.x)*(c.y-a.y) - (b.y-a.y)*(c.x-a.x));
|
||||
}
|
||||
|
||||
kernel void RenderKernel(texture2d<float, access::write> outTexture [[texture(0)]],
|
||||
texture2d<float> texAtlas [[texture(1)]],
|
||||
const device mg_vertex* vertexBuffer [[buffer(0)]],
|
||||
const device mg_shape* shapeBuffer [[buffer(1)]],
|
||||
device uint* tileCounters [[buffer(2)]],
|
||||
const device uint* tilesArray [[buffer(3)]],
|
||||
const device mg_triangle_data* triangleArray [[buffer(4)]],
|
||||
const device float4* boxArray [[buffer(5)]],
|
||||
constant vector_float4* clearColor [[buffer(6)]],
|
||||
constant int* useTexture [[buffer(7)]],
|
||||
constant float* contentsScaling [[buffer(8)]],
|
||||
uint2 gid [[thread_position_in_grid]],
|
||||
uint2 tgid [[threadgroup_position_in_grid]],
|
||||
uint2 threadsPerThreadgroup [[threads_per_threadgroup]],
|
||||
uint2 gridSize [[threads_per_grid]])
|
||||
{
|
||||
//TODO: guard against thread group size not equal to tile size?
|
||||
const uint2 tilesMatrixDim = (gridSize - 1) / RENDERER_TILE_SIZE + 1;
|
||||
// const uint2 tilePos = tgid * threadsPerThreadgroup / RENDERER_TILE_SIZE;
|
||||
const uint2 tilePos = gid/RENDERER_TILE_SIZE;
|
||||
const uint tileIndex = tilePos.y * tilesMatrixDim.x + tilePos.x;
|
||||
const device uint* tileBuffer = tilesArray + tileIndex * RENDERER_TILE_BUFFER_SIZE;
|
||||
|
||||
const uint tileBufferSize = tileCounters[tileIndex];
|
||||
|
||||
#ifdef RENDERER_DEBUG_TILES
|
||||
//NOTE(martin): color code debug values and show the tile grid
|
||||
uint nTileX = tilesMatrixDim.x;
|
||||
uint nTileY = tilesMatrixDim.y;
|
||||
|
||||
if(tilePos.x > nTileX || tilePos.y > nTileY)
|
||||
{
|
||||
outTexture.write(float4(0, 1, 1, 1), gid);
|
||||
return;
|
||||
}
|
||||
|
||||
if((gid.x % RENDERER_TILE_SIZE == 0) || (gid.y % RENDERER_TILE_SIZE == 0))
|
||||
{
|
||||
outTexture.write(float4(0, 0, 0, 1), gid);
|
||||
return;
|
||||
}
|
||||
if(tileBufferSize <= 0)
|
||||
{
|
||||
outTexture.write(float4(0, 1, 0, 1), gid);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
outTexture.write(float4(1, 0, 0, 1), gid);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
const float2 sampleOffsets[6] = { float2(5./12, 5./12),
|
||||
float2(-3./12, 3./12),
|
||||
float2(1./12, 1./12),
|
||||
float2(3./12, -1./12),
|
||||
float2(-5./12, -3./12),
|
||||
float2(-1./12, -5./12)};
|
||||
|
||||
int zIndices[6];
|
||||
uint flipCounts[6];
|
||||
float4 pixelColors[6];
|
||||
float4 nextColors[6];
|
||||
for(int i=0; i<6; i++)
|
||||
{
|
||||
zIndices[i] = -1;
|
||||
flipCounts[i] = 0;
|
||||
pixelColors[i] = float4(0, 0, 0, 0);
|
||||
nextColors[i] = float4(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
for(uint tileBufferIndex=0; tileBufferIndex < tileBufferSize; tileBufferIndex++)
|
||||
{
|
||||
float4 box = boxArray[tileBuffer[tileBufferIndex]];
|
||||
const device mg_triangle_data* triangle = &triangleArray[tileBuffer[tileBufferIndex]];
|
||||
|
||||
float2 p0 = triangle->p0;
|
||||
float2 p1 = triangle->p1;
|
||||
float2 p2 = triangle->p2;
|
||||
|
||||
int bias0 = triangle->bias0;
|
||||
int bias1 = triangle->bias1;
|
||||
int bias2 = triangle->bias2;
|
||||
|
||||
const device mg_vertex* v0 = &(vertexBuffer[triangle->i0]);
|
||||
const device mg_vertex* v1 = &(vertexBuffer[triangle->i1]);
|
||||
const device mg_vertex* v2 = &(vertexBuffer[triangle->i2]);
|
||||
|
||||
float4 cubic0 = v0->cubic;
|
||||
float4 cubic1 = v1->cubic;
|
||||
float4 cubic2 = v2->cubic;
|
||||
|
||||
int shapeIndex = v0->shapeIndex;
|
||||
float4 color = shapeBuffer[shapeIndex].color;
|
||||
color.rgb *= color.a;
|
||||
|
||||
const device float* uvTransform2x3 = shapeBuffer[shapeIndex].uvTransform;
|
||||
matrix_float3x3 uvTransform = {{uvTransform2x3[0], uvTransform2x3[3], 0},
|
||||
{uvTransform2x3[1], uvTransform2x3[4], 0},
|
||||
{uvTransform2x3[2], uvTransform2x3[5], 1}};
|
||||
|
||||
for(int i=0; i<6; i++)
|
||||
{
|
||||
float2 samplePoint = (float2)gid + sampleOffsets[i];
|
||||
|
||||
//NOTE(martin): cull if pixel is outside box
|
||||
if(samplePoint.x < box.x || samplePoint.x > box.z || samplePoint.y < box.y || samplePoint.y > box.w)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
float w0 = orient2d(p1, p2, samplePoint);
|
||||
float w1 = orient2d(p2, p0, samplePoint);
|
||||
float w2 = orient2d(p0, p1, samplePoint);
|
||||
|
||||
if(((int)w0+bias0) >= 0 && ((int)w1+bias1) >= 0 && ((int)w2+bias2) >= 0)
|
||||
{
|
||||
float4 cubic = (cubic0*w0 + cubic1*w1 + cubic2*w2)/(w0+w1+w2);
|
||||
|
||||
//float2 uv = (uv0*w0 + uv1*w1 + uv2*w2)/(w0+w1+w2);
|
||||
float2 uv = (uvTransform*(float3(samplePoint.xy/contentsScaling[0], 1))).xy;
|
||||
|
||||
float4 texColor = float4(1, 1, 1, 1);
|
||||
if(*useTexture)
|
||||
{
|
||||
constexpr sampler smp(mip_filter::nearest, mag_filter::linear, min_filter::linear);
|
||||
texColor = texAtlas.sample(smp, uv);
|
||||
texColor.rgb *= texColor.a;
|
||||
}
|
||||
//TODO(martin): this is a quick and dirty fix for solid polygons where we use
|
||||
// cubic = (1, 1, 1, 1) on all vertices, which can cause small errors to
|
||||
// flip the sign.
|
||||
// We should really use another value that always lead to <= 0, but we must
|
||||
// make sure we never share these vertices with bezier shapes.
|
||||
// Alternatively, an ugly (but maybe less than this one) solution would be
|
||||
// to check if uvs are equal on all vertices of the triangle and always render
|
||||
// those triangles.
|
||||
float eps = 0.0001;
|
||||
if(cubic.w*(cubic.x*cubic.x*cubic.x - cubic.y*cubic.z) <= eps)
|
||||
{
|
||||
if(shapeIndex == zIndices[i])
|
||||
{
|
||||
flipCounts[i]++;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(flipCounts[i] & 0x01)
|
||||
{
|
||||
pixelColors[i] = nextColors[i];
|
||||
}
|
||||
|
||||
float4 nextCol = color*texColor;
|
||||
nextColors[i] = pixelColors[i]*(1-nextCol.a) +nextCol.a*nextCol;
|
||||
|
||||
zIndices[i] = shapeIndex;
|
||||
flipCounts[i] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
float4 out = float4(0, 0, 0, 0);
|
||||
for(int i=0; i<6; i++)
|
||||
{
|
||||
if(flipCounts[i] & 0x01)
|
||||
{
|
||||
pixelColors[i] = nextColors[i];
|
||||
}
|
||||
out += pixelColors[i];
|
||||
}
|
||||
out = out/6.;
|
||||
outTexture.write(out, gid);
|
||||
}
|
||||
|
|
|
@ -1,254 +1,254 @@
|
|||
/************************************************************//**
|
||||
*
|
||||
* @file: mtl_surface.m
|
||||
* @author: Martin Fouilleul
|
||||
* @date: 12/07/2023
|
||||
* @revision:
|
||||
*
|
||||
*****************************************************************/
|
||||
#import<Metal/Metal.h>
|
||||
#import <QuartzCore/QuartzCore.h>
|
||||
#import<QuartzCore/CAMetalLayer.h>
|
||||
#include<simd/simd.h>
|
||||
|
||||
#include"graphics_internal.h"
|
||||
#include"macro_helpers.h"
|
||||
#include"osx_app.h"
|
||||
|
||||
#define LOG_SUBSYSTEM "Graphics"
|
||||
|
||||
static const u32 MP_MTL_MAX_DRAWABLES_IN_FLIGHT = 3;
|
||||
|
||||
typedef struct mg_mtl_surface
|
||||
{
|
||||
mg_surface_data interface;
|
||||
|
||||
// permanent mtl resources
|
||||
id<MTLDevice> device;
|
||||
CAMetalLayer* mtlLayer;
|
||||
id<MTLCommandQueue> commandQueue;
|
||||
|
||||
// transient metal resources
|
||||
id<CAMetalDrawable> drawable;
|
||||
id<MTLCommandBuffer> commandBuffer;
|
||||
|
||||
dispatch_semaphore_t drawableSemaphore;
|
||||
|
||||
} mg_mtl_surface;
|
||||
|
||||
void mg_mtl_surface_destroy(mg_surface_data* interface)
|
||||
{
|
||||
mg_mtl_surface* surface = (mg_mtl_surface*)interface;
|
||||
|
||||
@autoreleasepool
|
||||
{
|
||||
[surface->commandQueue release];
|
||||
[surface->mtlLayer removeFromSuperlayer];
|
||||
[surface->mtlLayer release];
|
||||
[surface->device release];
|
||||
}
|
||||
//NOTE: we don't use mp_layer_cleanup here, because the CAMetalLayer is taken care off by the surface itself
|
||||
}
|
||||
|
||||
void mg_mtl_surface_acquire_drawable_and_command_buffer(mg_mtl_surface* surface)
|
||||
{@autoreleasepool{
|
||||
/*WARN(martin): this is super important
|
||||
When the app is put in the background, it seems that if there are buffers in flight, the drawables to
|
||||
can be leaked. This causes the gpu to allocate more and more drawables, until the app crashes.
|
||||
(note: the drawable objects themselves are released once the app comes back to the forefront, but the
|
||||
memory allocated in the GPU is never freed...)
|
||||
|
||||
In background the gpu seems to create drawable if none is available instead of actually
|
||||
blocking on nextDrawable. These drawable never get freed.
|
||||
This is not a problem if our shader is fast enough, since a previous drawable becomes
|
||||
available before we finish the frame. But we want to protect against it anyway
|
||||
|
||||
The normal blocking mechanism of nextDrawable seems useless, so we implement our own scheme by
|
||||
counting the number of drawables available with a semaphore that gets decremented here and
|
||||
incremented in the presentedHandler of the drawable.
|
||||
Thus we ensure that we don't consume more drawables than we are able to draw.
|
||||
|
||||
//TODO: we _also_ should stop trying to render if we detect that the app is in the background
|
||||
or occluded, but we can't count only on this because there is a potential race between the
|
||||
notification of background mode and the rendering.
|
||||
|
||||
//TODO: We should set a reasonable timeout and skip the frame and log an error in case we are stalled
|
||||
for too long.
|
||||
*/
|
||||
dispatch_semaphore_wait(surface->drawableSemaphore, DISPATCH_TIME_FOREVER);
|
||||
|
||||
surface->drawable = [surface->mtlLayer nextDrawable];
|
||||
ASSERT(surface->drawable != nil);
|
||||
|
||||
//TODO: make this a weak reference if we use ARC
|
||||
dispatch_semaphore_t semaphore = surface->drawableSemaphore;
|
||||
[surface->drawable addPresentedHandler:^(id<MTLDrawable> drawable){
|
||||
dispatch_semaphore_signal(semaphore);
|
||||
}];
|
||||
|
||||
//NOTE(martin): create a command buffer
|
||||
surface->commandBuffer = [surface->commandQueue commandBuffer];
|
||||
|
||||
[surface->commandBuffer retain];
|
||||
[surface->drawable retain];
|
||||
}}
|
||||
|
||||
void mg_mtl_surface_prepare(mg_surface_data* interface)
|
||||
{
|
||||
mg_mtl_surface* surface = (mg_mtl_surface*)interface;
|
||||
mg_mtl_surface_acquire_drawable_and_command_buffer(surface);
|
||||
}
|
||||
|
||||
void mg_mtl_surface_present(mg_surface_data* interface)
|
||||
{
|
||||
mg_mtl_surface* surface = (mg_mtl_surface*)interface;
|
||||
@autoreleasepool
|
||||
{
|
||||
//NOTE(martin): present drawable and commit command buffer
|
||||
[surface->commandBuffer presentDrawable: surface->drawable];
|
||||
[surface->commandBuffer commit];
|
||||
[surface->commandBuffer waitUntilCompleted];
|
||||
|
||||
//NOTE(martin): acquire next frame resources
|
||||
[surface->commandBuffer release];
|
||||
surface->commandBuffer = nil;
|
||||
[surface->drawable release];
|
||||
surface->drawable = nil;
|
||||
}
|
||||
}
|
||||
|
||||
void mg_mtl_surface_swap_interval(mg_surface_data* interface, int swap)
|
||||
{
|
||||
mg_mtl_surface* surface = (mg_mtl_surface*)interface;
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
//TODO
|
||||
////////////////////////////////////////////////////////////////
|
||||
}
|
||||
|
||||
void mg_mtl_surface_set_frame(mg_surface_data* interface, mp_rect frame)
|
||||
{
|
||||
mg_mtl_surface* surface = (mg_mtl_surface*)interface;
|
||||
mg_osx_surface_set_frame(interface, frame);
|
||||
vec2 scale = mg_osx_surface_contents_scaling(interface);
|
||||
|
||||
CGSize drawableSize = (CGSize){.width = frame.w * scale.x, .height = frame.h * scale.y};
|
||||
surface->mtlLayer.drawableSize = drawableSize;
|
||||
}
|
||||
|
||||
|
||||
//TODO fix that according to real scaling, depending on the monitor settings
|
||||
static const f32 MG_MTL_SURFACE_CONTENTS_SCALING = 2;
|
||||
|
||||
mg_surface_data* mg_mtl_surface_create_for_window(mp_window window)
|
||||
{
|
||||
mg_mtl_surface* surface = 0;
|
||||
mp_window_data* windowData = mp_window_ptr_from_handle(window);
|
||||
if(windowData)
|
||||
{
|
||||
surface = (mg_mtl_surface*)malloc(sizeof(mg_mtl_surface));
|
||||
|
||||
mg_surface_init_for_window((mg_surface_data*)surface, windowData);
|
||||
|
||||
//NOTE(martin): setup interface functions
|
||||
surface->interface.backend = MG_BACKEND_METAL;
|
||||
surface->interface.destroy = mg_mtl_surface_destroy;
|
||||
surface->interface.prepare = mg_mtl_surface_prepare;
|
||||
surface->interface.present = mg_mtl_surface_present;
|
||||
surface->interface.swapInterval = mg_mtl_surface_swap_interval;
|
||||
|
||||
surface->interface.setFrame = mg_mtl_surface_set_frame;
|
||||
|
||||
@autoreleasepool
|
||||
{
|
||||
surface->drawableSemaphore = dispatch_semaphore_create(MP_MTL_MAX_DRAWABLES_IN_FLIGHT);
|
||||
|
||||
//-----------------------------------------------------------
|
||||
//NOTE(martin): create a mtl device and a mtl layer and
|
||||
//-----------------------------------------------------------
|
||||
|
||||
surface->device = MTLCreateSystemDefaultDevice();
|
||||
[surface->device retain];
|
||||
surface->mtlLayer = [CAMetalLayer layer];
|
||||
[surface->mtlLayer retain];
|
||||
|
||||
surface->mtlLayer.device = surface->device;
|
||||
[surface->mtlLayer setOpaque:NO];
|
||||
|
||||
[surface->interface.layer.caLayer addSublayer: (CALayer*)surface->mtlLayer];
|
||||
|
||||
//-----------------------------------------------------------
|
||||
//NOTE(martin): set the size and scaling
|
||||
//-----------------------------------------------------------
|
||||
NSRect frame = [[windowData->osx.nsWindow contentView] frame];
|
||||
CGSize size = frame.size;
|
||||
surface->mtlLayer.frame = (CGRect){{0, 0}, size};
|
||||
size.width *= MG_MTL_SURFACE_CONTENTS_SCALING;
|
||||
size.height *= MG_MTL_SURFACE_CONTENTS_SCALING;
|
||||
surface->mtlLayer.drawableSize = size;
|
||||
surface->mtlLayer.contentsScale = MG_MTL_SURFACE_CONTENTS_SCALING;
|
||||
|
||||
surface->mtlLayer.pixelFormat = MTLPixelFormatBGRA8Unorm;
|
||||
|
||||
//NOTE(martin): handling resizing
|
||||
surface->mtlLayer.autoresizingMask = kCALayerHeightSizable | kCALayerWidthSizable;
|
||||
surface->mtlLayer.needsDisplayOnBoundsChange = YES;
|
||||
|
||||
//-----------------------------------------------------------
|
||||
//NOTE(martin): create a command queue
|
||||
//-----------------------------------------------------------
|
||||
surface->commandQueue = [surface->device newCommandQueue];
|
||||
[surface->commandQueue retain];
|
||||
|
||||
//NOTE(martin): command buffer and drawable are set on demand and at the end of each present() call
|
||||
surface->drawable = nil;
|
||||
surface->commandBuffer = nil;
|
||||
}
|
||||
}
|
||||
return((mg_surface_data*)surface);
|
||||
}
|
||||
|
||||
void* mg_mtl_surface_layer(mg_surface surface)
|
||||
{
|
||||
mg_surface_data* surfaceData = mg_surface_data_from_handle(surface);
|
||||
if(surfaceData && surfaceData->backend == MG_BACKEND_METAL)
|
||||
{
|
||||
mg_mtl_surface* mtlSurface = (mg_mtl_surface*)surfaceData;
|
||||
return(mtlSurface->mtlLayer);
|
||||
}
|
||||
else
|
||||
{
|
||||
return(nil);
|
||||
}
|
||||
}
|
||||
|
||||
void* mg_mtl_surface_drawable(mg_surface surface)
|
||||
{
|
||||
mg_surface_data* surfaceData = mg_surface_data_from_handle(surface);
|
||||
if(surfaceData && surfaceData->backend == MG_BACKEND_METAL)
|
||||
{
|
||||
mg_mtl_surface* mtlSurface = (mg_mtl_surface*)surfaceData;
|
||||
return(mtlSurface->drawable);
|
||||
}
|
||||
else
|
||||
{
|
||||
return(nil);
|
||||
}
|
||||
}
|
||||
|
||||
void* mg_mtl_surface_command_buffer(mg_surface surface)
|
||||
{
|
||||
mg_surface_data* surfaceData = mg_surface_data_from_handle(surface);
|
||||
if(surfaceData && surfaceData->backend == MG_BACKEND_METAL)
|
||||
{
|
||||
mg_mtl_surface* mtlSurface = (mg_mtl_surface*)surfaceData;
|
||||
return(mtlSurface->commandBuffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
return(nil);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#undef LOG_SUBSYSTEM
|
||||
/************************************************************//**
|
||||
*
|
||||
* @file: mtl_surface.m
|
||||
* @author: Martin Fouilleul
|
||||
* @date: 12/07/2023
|
||||
* @revision:
|
||||
*
|
||||
*****************************************************************/
|
||||
#import<Metal/Metal.h>
|
||||
#import <QuartzCore/QuartzCore.h>
|
||||
#import<QuartzCore/CAMetalLayer.h>
|
||||
#include<simd/simd.h>
|
||||
|
||||
#include"graphics_internal.h"
|
||||
#include"macro_helpers.h"
|
||||
#include"osx_app.h"
|
||||
|
||||
#define LOG_SUBSYSTEM "Graphics"
|
||||
|
||||
static const u32 MP_MTL_MAX_DRAWABLES_IN_FLIGHT = 3;
|
||||
|
||||
typedef struct mg_mtl_surface
|
||||
{
|
||||
mg_surface_data interface;
|
||||
|
||||
// permanent mtl resources
|
||||
id<MTLDevice> device;
|
||||
CAMetalLayer* mtlLayer;
|
||||
id<MTLCommandQueue> commandQueue;
|
||||
|
||||
// transient metal resources
|
||||
id<CAMetalDrawable> drawable;
|
||||
id<MTLCommandBuffer> commandBuffer;
|
||||
|
||||
dispatch_semaphore_t drawableSemaphore;
|
||||
|
||||
} mg_mtl_surface;
|
||||
|
||||
void mg_mtl_surface_destroy(mg_surface_data* interface)
|
||||
{
|
||||
mg_mtl_surface* surface = (mg_mtl_surface*)interface;
|
||||
|
||||
@autoreleasepool
|
||||
{
|
||||
[surface->commandQueue release];
|
||||
[surface->mtlLayer removeFromSuperlayer];
|
||||
[surface->mtlLayer release];
|
||||
[surface->device release];
|
||||
}
|
||||
//NOTE: we don't use mp_layer_cleanup here, because the CAMetalLayer is taken care off by the surface itself
|
||||
}
|
||||
|
||||
void mg_mtl_surface_acquire_drawable_and_command_buffer(mg_mtl_surface* surface)
|
||||
{@autoreleasepool{
|
||||
/*WARN(martin): this is super important
|
||||
When the app is put in the background, it seems that if there are buffers in flight, the drawables to
|
||||
can be leaked. This causes the gpu to allocate more and more drawables, until the app crashes.
|
||||
(note: the drawable objects themselves are released once the app comes back to the forefront, but the
|
||||
memory allocated in the GPU is never freed...)
|
||||
|
||||
In background the gpu seems to create drawable if none is available instead of actually
|
||||
blocking on nextDrawable. These drawable never get freed.
|
||||
This is not a problem if our shader is fast enough, since a previous drawable becomes
|
||||
available before we finish the frame. But we want to protect against it anyway
|
||||
|
||||
The normal blocking mechanism of nextDrawable seems useless, so we implement our own scheme by
|
||||
counting the number of drawables available with a semaphore that gets decremented here and
|
||||
incremented in the presentedHandler of the drawable.
|
||||
Thus we ensure that we don't consume more drawables than we are able to draw.
|
||||
|
||||
//TODO: we _also_ should stop trying to render if we detect that the app is in the background
|
||||
or occluded, but we can't count only on this because there is a potential race between the
|
||||
notification of background mode and the rendering.
|
||||
|
||||
//TODO: We should set a reasonable timeout and skip the frame and log an error in case we are stalled
|
||||
for too long.
|
||||
*/
|
||||
dispatch_semaphore_wait(surface->drawableSemaphore, DISPATCH_TIME_FOREVER);
|
||||
|
||||
surface->drawable = [surface->mtlLayer nextDrawable];
|
||||
ASSERT(surface->drawable != nil);
|
||||
|
||||
//TODO: make this a weak reference if we use ARC
|
||||
dispatch_semaphore_t semaphore = surface->drawableSemaphore;
|
||||
[surface->drawable addPresentedHandler:^(id<MTLDrawable> drawable){
|
||||
dispatch_semaphore_signal(semaphore);
|
||||
}];
|
||||
|
||||
//NOTE(martin): create a command buffer
|
||||
surface->commandBuffer = [surface->commandQueue commandBuffer];
|
||||
|
||||
[surface->commandBuffer retain];
|
||||
[surface->drawable retain];
|
||||
}}
|
||||
|
||||
void mg_mtl_surface_prepare(mg_surface_data* interface)
|
||||
{
|
||||
mg_mtl_surface* surface = (mg_mtl_surface*)interface;
|
||||
mg_mtl_surface_acquire_drawable_and_command_buffer(surface);
|
||||
}
|
||||
|
||||
void mg_mtl_surface_present(mg_surface_data* interface)
|
||||
{
|
||||
mg_mtl_surface* surface = (mg_mtl_surface*)interface;
|
||||
@autoreleasepool
|
||||
{
|
||||
//NOTE(martin): present drawable and commit command buffer
|
||||
[surface->commandBuffer presentDrawable: surface->drawable];
|
||||
[surface->commandBuffer commit];
|
||||
[surface->commandBuffer waitUntilCompleted];
|
||||
|
||||
//NOTE(martin): acquire next frame resources
|
||||
[surface->commandBuffer release];
|
||||
surface->commandBuffer = nil;
|
||||
[surface->drawable release];
|
||||
surface->drawable = nil;
|
||||
}
|
||||
}
|
||||
|
||||
void mg_mtl_surface_swap_interval(mg_surface_data* interface, int swap)
|
||||
{
|
||||
mg_mtl_surface* surface = (mg_mtl_surface*)interface;
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
//TODO
|
||||
////////////////////////////////////////////////////////////////
|
||||
}
|
||||
|
||||
void mg_mtl_surface_set_frame(mg_surface_data* interface, mp_rect frame)
|
||||
{
|
||||
mg_mtl_surface* surface = (mg_mtl_surface*)interface;
|
||||
mg_osx_surface_set_frame(interface, frame);
|
||||
vec2 scale = mg_osx_surface_contents_scaling(interface);
|
||||
|
||||
CGSize drawableSize = (CGSize){.width = frame.w * scale.x, .height = frame.h * scale.y};
|
||||
surface->mtlLayer.drawableSize = drawableSize;
|
||||
}
|
||||
|
||||
|
||||
//TODO fix that according to real scaling, depending on the monitor settings
|
||||
static const f32 MG_MTL_SURFACE_CONTENTS_SCALING = 2;
|
||||
|
||||
mg_surface_data* mg_mtl_surface_create_for_window(mp_window window)
|
||||
{
|
||||
mg_mtl_surface* surface = 0;
|
||||
mp_window_data* windowData = mp_window_ptr_from_handle(window);
|
||||
if(windowData)
|
||||
{
|
||||
surface = (mg_mtl_surface*)malloc(sizeof(mg_mtl_surface));
|
||||
|
||||
mg_surface_init_for_window((mg_surface_data*)surface, windowData);
|
||||
|
||||
//NOTE(martin): setup interface functions
|
||||
surface->interface.backend = MG_BACKEND_METAL;
|
||||
surface->interface.destroy = mg_mtl_surface_destroy;
|
||||
surface->interface.prepare = mg_mtl_surface_prepare;
|
||||
surface->interface.present = mg_mtl_surface_present;
|
||||
surface->interface.swapInterval = mg_mtl_surface_swap_interval;
|
||||
|
||||
surface->interface.setFrame = mg_mtl_surface_set_frame;
|
||||
|
||||
@autoreleasepool
|
||||
{
|
||||
surface->drawableSemaphore = dispatch_semaphore_create(MP_MTL_MAX_DRAWABLES_IN_FLIGHT);
|
||||
|
||||
//-----------------------------------------------------------
|
||||
//NOTE(martin): create a mtl device and a mtl layer and
|
||||
//-----------------------------------------------------------
|
||||
|
||||
surface->device = MTLCreateSystemDefaultDevice();
|
||||
[surface->device retain];
|
||||
surface->mtlLayer = [CAMetalLayer layer];
|
||||
[surface->mtlLayer retain];
|
||||
|
||||
surface->mtlLayer.device = surface->device;
|
||||
[surface->mtlLayer setOpaque:NO];
|
||||
|
||||
[surface->interface.layer.caLayer addSublayer: (CALayer*)surface->mtlLayer];
|
||||
|
||||
//-----------------------------------------------------------
|
||||
//NOTE(martin): set the size and scaling
|
||||
//-----------------------------------------------------------
|
||||
NSRect frame = [[windowData->osx.nsWindow contentView] frame];
|
||||
CGSize size = frame.size;
|
||||
surface->mtlLayer.frame = (CGRect){{0, 0}, size};
|
||||
size.width *= MG_MTL_SURFACE_CONTENTS_SCALING;
|
||||
size.height *= MG_MTL_SURFACE_CONTENTS_SCALING;
|
||||
surface->mtlLayer.drawableSize = size;
|
||||
surface->mtlLayer.contentsScale = MG_MTL_SURFACE_CONTENTS_SCALING;
|
||||
|
||||
surface->mtlLayer.pixelFormat = MTLPixelFormatBGRA8Unorm;
|
||||
|
||||
//NOTE(martin): handling resizing
|
||||
surface->mtlLayer.autoresizingMask = kCALayerHeightSizable | kCALayerWidthSizable;
|
||||
surface->mtlLayer.needsDisplayOnBoundsChange = YES;
|
||||
|
||||
//-----------------------------------------------------------
|
||||
//NOTE(martin): create a command queue
|
||||
//-----------------------------------------------------------
|
||||
surface->commandQueue = [surface->device newCommandQueue];
|
||||
[surface->commandQueue retain];
|
||||
|
||||
//NOTE(martin): command buffer and drawable are set on demand and at the end of each present() call
|
||||
surface->drawable = nil;
|
||||
surface->commandBuffer = nil;
|
||||
}
|
||||
}
|
||||
return((mg_surface_data*)surface);
|
||||
}
|
||||
|
||||
void* mg_mtl_surface_layer(mg_surface surface)
|
||||
{
|
||||
mg_surface_data* surfaceData = mg_surface_data_from_handle(surface);
|
||||
if(surfaceData && surfaceData->backend == MG_BACKEND_METAL)
|
||||
{
|
||||
mg_mtl_surface* mtlSurface = (mg_mtl_surface*)surfaceData;
|
||||
return(mtlSurface->mtlLayer);
|
||||
}
|
||||
else
|
||||
{
|
||||
return(nil);
|
||||
}
|
||||
}
|
||||
|
||||
void* mg_mtl_surface_drawable(mg_surface surface)
|
||||
{
|
||||
mg_surface_data* surfaceData = mg_surface_data_from_handle(surface);
|
||||
if(surfaceData && surfaceData->backend == MG_BACKEND_METAL)
|
||||
{
|
||||
mg_mtl_surface* mtlSurface = (mg_mtl_surface*)surfaceData;
|
||||
return(mtlSurface->drawable);
|
||||
}
|
||||
else
|
||||
{
|
||||
return(nil);
|
||||
}
|
||||
}
|
||||
|
||||
void* mg_mtl_surface_command_buffer(mg_surface surface)
|
||||
{
|
||||
mg_surface_data* surfaceData = mg_surface_data_from_handle(surface);
|
||||
if(surfaceData && surfaceData->backend == MG_BACKEND_METAL)
|
||||
{
|
||||
mg_mtl_surface* mtlSurface = (mg_mtl_surface*)surfaceData;
|
||||
return(mtlSurface->commandBuffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
return(nil);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#undef LOG_SUBSYSTEM
|
||||
|
|
|
@ -1,93 +1,93 @@
|
|||
//*****************************************************************
|
||||
//
|
||||
// $file: platform.h $
|
||||
// $author: Martin Fouilleul $
|
||||
// $date: 22/12/2022 $
|
||||
// $revision: $
|
||||
// $note: (C) 2022 by Martin Fouilleul - all rights reserved $
|
||||
//
|
||||
//*****************************************************************
|
||||
#ifndef __PLATFORM_H_
|
||||
#define __PLATFORM_H_
|
||||
|
||||
//-----------------------------------------------------------------
|
||||
// Compiler identification
|
||||
//-----------------------------------------------------------------
|
||||
#if defined(__clang__)
|
||||
#define COMPILER_CLANG 1
|
||||
#if defined(__apple_build_version__)
|
||||
#define COMPILER_CLANG_APPLE 1
|
||||
#elif defined(_MSC_VER)
|
||||
#define COMPILER_CLANG_CL 1
|
||||
#endif
|
||||
|
||||
#elif defined(_MSC_VER)
|
||||
#define COMPILER_CL 1
|
||||
#elif defined(__GNUC__)
|
||||
#define COMPILER_GCC 1
|
||||
#else
|
||||
#error "Can't identify compiler"
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------
|
||||
// OS identification
|
||||
//-----------------------------------------------------------------
|
||||
#if defined(_WIN64)
|
||||
#define OS_WIN64 1
|
||||
#elif defined(_WIN32)
|
||||
#error "Unsupported OS (32bit only version of Windows)"
|
||||
#elif defined(__APPLE__) && defined(__MACH__)
|
||||
#define OS_MACOS 1
|
||||
#elif defined(__gnu_linux__)
|
||||
#define OS_LINUX 1
|
||||
#else
|
||||
#error "Can't identify OS"
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------
|
||||
// Architecture identification
|
||||
//-----------------------------------------------------------------
|
||||
#if defined(COMPILER_CL)
|
||||
#if defined(_M_AMD64)
|
||||
#define ARCH_X64 1
|
||||
#elif defined(_M_I86)
|
||||
#define ARCH_X86 1
|
||||
#elif defined(_M_ARM64)
|
||||
#define ARCH_ARM64 1
|
||||
#elif defined(_M_ARM)
|
||||
#define ARCH_ARM32 1
|
||||
#else
|
||||
#error "Can't identify architecture"
|
||||
#endif
|
||||
#else
|
||||
#if defined(__x86_64__)
|
||||
#define ARCH_X64 1
|
||||
#elif defined(__i386__)
|
||||
#define ARCH_X86 1
|
||||
#elif defined(__arm__)
|
||||
#define ARCH_ARM32 1
|
||||
#elif defined(__aarch64__)
|
||||
#define ARCH_ARM64 1
|
||||
#else
|
||||
#error "Can't identify architecture"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------
|
||||
// platform helper macros
|
||||
//-----------------------------------------------------------------
|
||||
#if defined(COMPILER_CL)
|
||||
#if defined(MP_BUILD_DLL)
|
||||
#define MP_API __declspec(dllexport)
|
||||
#else
|
||||
#define MP_API __declspec(dllimport)
|
||||
#endif
|
||||
|
||||
#define mp_thread_local __declspec(thread)
|
||||
|
||||
#elif defined(COMPILER_GCC) || defined(COMPILER_CLANG)
|
||||
#define MP_API
|
||||
#define mp_thread_local __thread
|
||||
#endif
|
||||
|
||||
#endif // __PLATFORM_H_
|
||||
//*****************************************************************
|
||||
//
|
||||
// $file: platform.h $
|
||||
// $author: Martin Fouilleul $
|
||||
// $date: 22/12/2022 $
|
||||
// $revision: $
|
||||
// $note: (C) 2022 by Martin Fouilleul - all rights reserved $
|
||||
//
|
||||
//*****************************************************************
|
||||
#ifndef __PLATFORM_H_
|
||||
#define __PLATFORM_H_
|
||||
|
||||
//-----------------------------------------------------------------
|
||||
// Compiler identification
|
||||
//-----------------------------------------------------------------
|
||||
#if defined(__clang__)
|
||||
#define COMPILER_CLANG 1
|
||||
#if defined(__apple_build_version__)
|
||||
#define COMPILER_CLANG_APPLE 1
|
||||
#elif defined(_MSC_VER)
|
||||
#define COMPILER_CLANG_CL 1
|
||||
#endif
|
||||
|
||||
#elif defined(_MSC_VER)
|
||||
#define COMPILER_CL 1
|
||||
#elif defined(__GNUC__)
|
||||
#define COMPILER_GCC 1
|
||||
#else
|
||||
#error "Can't identify compiler"
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------
|
||||
// OS identification
|
||||
//-----------------------------------------------------------------
|
||||
#if defined(_WIN64)
|
||||
#define OS_WIN64 1
|
||||
#elif defined(_WIN32)
|
||||
#error "Unsupported OS (32bit only version of Windows)"
|
||||
#elif defined(__APPLE__) && defined(__MACH__)
|
||||
#define OS_MACOS 1
|
||||
#elif defined(__gnu_linux__)
|
||||
#define OS_LINUX 1
|
||||
#else
|
||||
#error "Can't identify OS"
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------
|
||||
// Architecture identification
|
||||
//-----------------------------------------------------------------
|
||||
#if defined(COMPILER_CL)
|
||||
#if defined(_M_AMD64)
|
||||
#define ARCH_X64 1
|
||||
#elif defined(_M_I86)
|
||||
#define ARCH_X86 1
|
||||
#elif defined(_M_ARM64)
|
||||
#define ARCH_ARM64 1
|
||||
#elif defined(_M_ARM)
|
||||
#define ARCH_ARM32 1
|
||||
#else
|
||||
#error "Can't identify architecture"
|
||||
#endif
|
||||
#else
|
||||
#if defined(__x86_64__)
|
||||
#define ARCH_X64 1
|
||||
#elif defined(__i386__)
|
||||
#define ARCH_X86 1
|
||||
#elif defined(__arm__)
|
||||
#define ARCH_ARM32 1
|
||||
#elif defined(__aarch64__)
|
||||
#define ARCH_ARM64 1
|
||||
#else
|
||||
#error "Can't identify architecture"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------
|
||||
// platform helper macros
|
||||
//-----------------------------------------------------------------
|
||||
#if defined(COMPILER_CL)
|
||||
#if defined(MP_BUILD_DLL)
|
||||
#define MP_API __declspec(dllexport)
|
||||
#else
|
||||
#define MP_API __declspec(dllimport)
|
||||
#endif
|
||||
|
||||
#define mp_thread_local __declspec(thread)
|
||||
|
||||
#elif defined(COMPILER_GCC) || defined(COMPILER_CLANG)
|
||||
#define MP_API
|
||||
#define mp_thread_local __thread
|
||||
#endif
|
||||
|
||||
#endif // __PLATFORM_H_
|
||||
|
|
|
@ -1,34 +1,34 @@
|
|||
/************************************************************//**
|
||||
*
|
||||
* @file: platform_clock.h
|
||||
* @author: Martin Fouilleul
|
||||
* @date: 07/03/2019
|
||||
* @revision:
|
||||
*
|
||||
*****************************************************************/
|
||||
#ifndef __PLATFORM_CLOCK_H_
|
||||
#define __PLATFORM_CLOCK_H_
|
||||
|
||||
#include"typedefs.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif // __cplusplus
|
||||
|
||||
typedef enum {
|
||||
MP_CLOCK_MONOTONIC, // clock that increment monotonically
|
||||
MP_CLOCK_UPTIME, // clock that increment monotonically during uptime
|
||||
MP_CLOCK_DATE // clock that is driven by the platform time
|
||||
} mp_clock_kind;
|
||||
|
||||
MP_API void mp_clock_init(); // initialize the clock subsystem
|
||||
MP_API u64 mp_get_timestamp(mp_clock_kind clock);
|
||||
MP_API f64 mp_get_time(mp_clock_kind clock);
|
||||
MP_API void mp_sleep_nanoseconds(u64 nanoseconds); // sleep for a given number of nanoseconds
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif // __cplusplus
|
||||
|
||||
|
||||
#endif //__PLATFORM_CLOCK_H_
|
||||
/************************************************************//**
|
||||
*
|
||||
* @file: platform_clock.h
|
||||
* @author: Martin Fouilleul
|
||||
* @date: 07/03/2019
|
||||
* @revision:
|
||||
*
|
||||
*****************************************************************/
|
||||
#ifndef __PLATFORM_CLOCK_H_
|
||||
#define __PLATFORM_CLOCK_H_
|
||||
|
||||
#include"typedefs.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif // __cplusplus
|
||||
|
||||
typedef enum {
|
||||
MP_CLOCK_MONOTONIC, // clock that increment monotonically
|
||||
MP_CLOCK_UPTIME, // clock that increment monotonically during uptime
|
||||
MP_CLOCK_DATE // clock that is driven by the platform time
|
||||
} mp_clock_kind;
|
||||
|
||||
MP_API void mp_clock_init(); // initialize the clock subsystem
|
||||
MP_API u64 mp_get_timestamp(mp_clock_kind clock);
|
||||
MP_API f64 mp_get_time(mp_clock_kind clock);
|
||||
MP_API void mp_sleep_nanoseconds(u64 nanoseconds); // sleep for a given number of nanoseconds
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif // __cplusplus
|
||||
|
||||
|
||||
#endif //__PLATFORM_CLOCK_H_
|
||||
|
|
|
@ -1,38 +1,38 @@
|
|||
/************************************************************//**
|
||||
*
|
||||
* @file: win32_clock.c
|
||||
* @author: Martin Fouilleul
|
||||
* @date: 03/02/2023
|
||||
* @revision:
|
||||
*
|
||||
*****************************************************************/
|
||||
#include<profileapi.h>
|
||||
|
||||
#include"typedefs.h"
|
||||
#include"platform_clock.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
static u64 __performanceCounterFreq = 0;
|
||||
|
||||
void mp_clock_init()
|
||||
{
|
||||
LARGE_INTEGER freq;
|
||||
QueryPerformanceFrequency(&freq);
|
||||
__performanceCounterFreq = freq.QuadPart;
|
||||
}
|
||||
|
||||
f64 mp_get_time(mp_clock_kind clock)
|
||||
{
|
||||
LARGE_INTEGER counter;
|
||||
QueryPerformanceCounter(&counter);
|
||||
|
||||
f64 time = __performanceCounterFreq ? (counter.QuadPart / (f64)__performanceCounterFreq) : 0;
|
||||
return(time);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
/************************************************************//**
|
||||
*
|
||||
* @file: win32_clock.c
|
||||
* @author: Martin Fouilleul
|
||||
* @date: 03/02/2023
|
||||
* @revision:
|
||||
*
|
||||
*****************************************************************/
|
||||
#include<profileapi.h>
|
||||
|
||||
#include"typedefs.h"
|
||||
#include"platform_clock.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
static u64 __performanceCounterFreq = 0;
|
||||
|
||||
void mp_clock_init()
|
||||
{
|
||||
LARGE_INTEGER freq;
|
||||
QueryPerformanceFrequency(&freq);
|
||||
__performanceCounterFreq = freq.QuadPart;
|
||||
}
|
||||
|
||||
f64 mp_get_time(mp_clock_kind clock)
|
||||
{
|
||||
LARGE_INTEGER counter;
|
||||
QueryPerformanceCounter(&counter);
|
||||
|
||||
f64 time = __performanceCounterFreq ? (counter.QuadPart / (f64)__performanceCounterFreq) : 0;
|
||||
return(time);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
|
|
@ -1,98 +1,98 @@
|
|||
/************************************************************//**
|
||||
*
|
||||
* @file: debug_log.h
|
||||
* @author: Martin Fouilleul
|
||||
* @date: 05/04/2019
|
||||
* @revision:
|
||||
*
|
||||
*****************************************************************/
|
||||
#ifndef __DEBUG_LOG_H_
|
||||
#define __DEBUG_LOG_H_
|
||||
|
||||
#include<stdio.h>
|
||||
#include"platform.h"
|
||||
#include"typedefs.h"
|
||||
#include"macro_helpers.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
//NOTE(martin): the default logging level can be adjusted by defining LOG_DEFAULT_LEVEL. As the name suggest, it is the default, but it
|
||||
// can be adjusted at runtime with LogLevel()
|
||||
#ifndef LOG_DEFAULT_LEVEL
|
||||
#define LOG_DEFAULT_LEVEL LOG_LEVEL_WARNING
|
||||
#endif
|
||||
|
||||
//NOTE(martin): the default output can be adjusted by defining LOG_DEFAULT_OUTPUT. It can be adjusted at runtime with LogOutput()
|
||||
#ifndef LOG_DEFAULT_OUTPUT
|
||||
#define LOG_DEFAULT_OUTPUT stdout
|
||||
#endif
|
||||
|
||||
//NOTE(martin): LOG_SUBSYSTEM can be defined in each compilation unit to associate it with a subsystem, like this:
|
||||
// #define LOG_SUBSYSTEM "name"
|
||||
|
||||
typedef enum { LOG_LEVEL_ERROR,
|
||||
LOG_LEVEL_WARNING,
|
||||
LOG_LEVEL_MESSAGE,
|
||||
LOG_LEVEL_DEBUG,
|
||||
LOG_LEVEL_COUNT } log_level;
|
||||
|
||||
MP_API void LogGeneric(log_level level,
|
||||
const char* subsystem,
|
||||
const char* functionName,
|
||||
const char* fileName,
|
||||
u32 line,
|
||||
const char* msg,
|
||||
...);
|
||||
|
||||
MP_API void LogOutput(FILE* output);
|
||||
MP_API void LogLevel(log_level level);
|
||||
MP_API void LogFilter(const char* subsystem, log_level level);
|
||||
|
||||
#define LOG_GENERIC(level, func, file, line, msg, ...) LogGeneric(level, LOG_SUBSYSTEM, func, file, line, msg, ##__VA_ARGS__ )
|
||||
|
||||
#define LOG_ERROR(msg, ...) LOG_GENERIC(LOG_LEVEL_ERROR, __FUNCTION__, __FILE__, __LINE__, msg, ##__VA_ARGS__ )
|
||||
|
||||
//NOTE(martin): warnings, messages, and debug info can be enabled in debug mode by defining LOG_COMPILE_XXX, XXX being the max desired log level
|
||||
// error logging is always compiled
|
||||
#if defined(LOG_COMPILE_WARNING) || defined(LOG_COMPILE_MESSAGE) || defined(LOG_COMPILE_DEBUG)
|
||||
#define LOG_WARNING(msg, ...) LOG_GENERIC(LOG_LEVEL_WARNING, __FUNCTION__, __FILE__, __LINE__, msg, ##__VA_ARGS__ )
|
||||
|
||||
#if defined(LOG_COMPILE_MESSAGE) || defined(LOG_COMPILE_DEBUG)
|
||||
#define LOG_MESSAGE(msg, ...) LOG_GENERIC(LOG_LEVEL_MESSAGE, __FUNCTION__, __FILE__, __LINE__, msg, ##__VA_ARGS__ )
|
||||
|
||||
#if defined(LOG_COMPILE_DEBUG)
|
||||
#define LOG_DEBUG(msg, ...) LOG_GENERIC(LOG_LEVEL_DEBUG, __FUNCTION__, __FILE__, __LINE__, msg, ##__VA_ARGS__ )
|
||||
#else
|
||||
#define LOG_DEBUG(msg, ...)
|
||||
#endif
|
||||
#else
|
||||
#define LOG_MESSAGE(msg, ...)
|
||||
#define LOG_DEBUG(msg, ...)
|
||||
#endif
|
||||
#else
|
||||
#define LOG_WARNING(msg, ...)
|
||||
#define LOG_MESSAGE(msg, ...)
|
||||
#define LOG_DEBUG(msg, ...)
|
||||
#endif
|
||||
|
||||
#ifndef NO_ASSERT
|
||||
#include<assert.h>
|
||||
#define _ASSERT_(x, msg) assert(x && msg)
|
||||
#define ASSERT(x, ...) _ASSERT_(x, #__VA_ARGS__)
|
||||
|
||||
#ifdef DEBUG
|
||||
#define DEBUG_ASSERT(x, ...) ASSERT(x, ##__VA_ARGS__ )
|
||||
#else
|
||||
#define DEBUG_ASSERT(x, ...)
|
||||
#endif
|
||||
#else
|
||||
#define ASSERT(x, ...)
|
||||
#define DEBUG_ASSERT(x, ...)
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif //__DEBUG_LOG_H_
|
||||
/************************************************************//**
|
||||
*
|
||||
* @file: debug_log.h
|
||||
* @author: Martin Fouilleul
|
||||
* @date: 05/04/2019
|
||||
* @revision:
|
||||
*
|
||||
*****************************************************************/
|
||||
#ifndef __DEBUG_LOG_H_
|
||||
#define __DEBUG_LOG_H_
|
||||
|
||||
#include<stdio.h>
|
||||
#include"platform.h"
|
||||
#include"typedefs.h"
|
||||
#include"macro_helpers.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
//NOTE(martin): the default logging level can be adjusted by defining LOG_DEFAULT_LEVEL. As the name suggest, it is the default, but it
|
||||
// can be adjusted at runtime with LogLevel()
|
||||
#ifndef LOG_DEFAULT_LEVEL
|
||||
#define LOG_DEFAULT_LEVEL LOG_LEVEL_WARNING
|
||||
#endif
|
||||
|
||||
//NOTE(martin): the default output can be adjusted by defining LOG_DEFAULT_OUTPUT. It can be adjusted at runtime with LogOutput()
|
||||
#ifndef LOG_DEFAULT_OUTPUT
|
||||
#define LOG_DEFAULT_OUTPUT stdout
|
||||
#endif
|
||||
|
||||
//NOTE(martin): LOG_SUBSYSTEM can be defined in each compilation unit to associate it with a subsystem, like this:
|
||||
// #define LOG_SUBSYSTEM "name"
|
||||
|
||||
typedef enum { LOG_LEVEL_ERROR,
|
||||
LOG_LEVEL_WARNING,
|
||||
LOG_LEVEL_MESSAGE,
|
||||
LOG_LEVEL_DEBUG,
|
||||
LOG_LEVEL_COUNT } log_level;
|
||||
|
||||
MP_API void LogGeneric(log_level level,
|
||||
const char* subsystem,
|
||||
const char* functionName,
|
||||
const char* fileName,
|
||||
u32 line,
|
||||
const char* msg,
|
||||
...);
|
||||
|
||||
MP_API void LogOutput(FILE* output);
|
||||
MP_API void LogLevel(log_level level);
|
||||
MP_API void LogFilter(const char* subsystem, log_level level);
|
||||
|
||||
#define LOG_GENERIC(level, func, file, line, msg, ...) LogGeneric(level, LOG_SUBSYSTEM, func, file, line, msg, ##__VA_ARGS__ )
|
||||
|
||||
#define LOG_ERROR(msg, ...) LOG_GENERIC(LOG_LEVEL_ERROR, __FUNCTION__, __FILE__, __LINE__, msg, ##__VA_ARGS__ )
|
||||
|
||||
//NOTE(martin): warnings, messages, and debug info can be enabled in debug mode by defining LOG_COMPILE_XXX, XXX being the max desired log level
|
||||
// error logging is always compiled
|
||||
#if defined(LOG_COMPILE_WARNING) || defined(LOG_COMPILE_MESSAGE) || defined(LOG_COMPILE_DEBUG)
|
||||
#define LOG_WARNING(msg, ...) LOG_GENERIC(LOG_LEVEL_WARNING, __FUNCTION__, __FILE__, __LINE__, msg, ##__VA_ARGS__ )
|
||||
|
||||
#if defined(LOG_COMPILE_MESSAGE) || defined(LOG_COMPILE_DEBUG)
|
||||
#define LOG_MESSAGE(msg, ...) LOG_GENERIC(LOG_LEVEL_MESSAGE, __FUNCTION__, __FILE__, __LINE__, msg, ##__VA_ARGS__ )
|
||||
|
||||
#if defined(LOG_COMPILE_DEBUG)
|
||||
#define LOG_DEBUG(msg, ...) LOG_GENERIC(LOG_LEVEL_DEBUG, __FUNCTION__, __FILE__, __LINE__, msg, ##__VA_ARGS__ )
|
||||
#else
|
||||
#define LOG_DEBUG(msg, ...)
|
||||
#endif
|
||||
#else
|
||||
#define LOG_MESSAGE(msg, ...)
|
||||
#define LOG_DEBUG(msg, ...)
|
||||
#endif
|
||||
#else
|
||||
#define LOG_WARNING(msg, ...)
|
||||
#define LOG_MESSAGE(msg, ...)
|
||||
#define LOG_DEBUG(msg, ...)
|
||||
#endif
|
||||
|
||||
#ifndef NO_ASSERT
|
||||
#include<assert.h>
|
||||
#define _ASSERT_(x, msg) assert(x && msg)
|
||||
#define ASSERT(x, ...) _ASSERT_(x, #__VA_ARGS__)
|
||||
|
||||
#ifdef DEBUG
|
||||
#define DEBUG_ASSERT(x, ...) ASSERT(x, ##__VA_ARGS__ )
|
||||
#else
|
||||
#define DEBUG_ASSERT(x, ...)
|
||||
#endif
|
||||
#else
|
||||
#define ASSERT(x, ...)
|
||||
#define DEBUG_ASSERT(x, ...)
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif //__DEBUG_LOG_H_
|
||||
|
|
|
@ -1,29 +1,29 @@
|
|||
/************************************************************//**
|
||||
*
|
||||
* @file: hash.h
|
||||
* @author: Martin Fouilleul
|
||||
* @date: 08/08/2022
|
||||
* @revision:
|
||||
*
|
||||
*****************************************************************/
|
||||
#ifndef __HASH_H_
|
||||
#define __HASH_H_
|
||||
|
||||
#include"typedefs.h"
|
||||
#include"strings.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
MP_API u64 mp_hash_aes_u64(u64 x);
|
||||
MP_API u64 mp_hash_aes_u64_x2(u64 x, u64 y);
|
||||
MP_API u64 mp_hash_aes_string(str8 string);
|
||||
MP_API u64 mp_hash_aes_string_seed(str8 string, u64 seed);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
|
||||
#endif //__HASH_H_
|
||||
/************************************************************//**
|
||||
*
|
||||
* @file: hash.h
|
||||
* @author: Martin Fouilleul
|
||||
* @date: 08/08/2022
|
||||
* @revision:
|
||||
*
|
||||
*****************************************************************/
|
||||
#ifndef __HASH_H_
|
||||
#define __HASH_H_
|
||||
|
||||
#include"typedefs.h"
|
||||
#include"strings.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
MP_API u64 mp_hash_aes_u64(u64 x);
|
||||
MP_API u64 mp_hash_aes_u64_x2(u64 x, u64 y);
|
||||
MP_API u64 mp_hash_aes_string(str8 string);
|
||||
MP_API u64 mp_hash_aes_string_seed(str8 string, u64 seed);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
|
||||
#endif //__HASH_H_
|
||||
|
|
|
@ -1,106 +1,106 @@
|
|||
/************************************************************//**
|
||||
*
|
||||
* @file: memory.h
|
||||
* @author: Martin Fouilleul
|
||||
* @date: 24/10/2019
|
||||
* @revision:
|
||||
*
|
||||
*****************************************************************/
|
||||
#ifndef __MEMORY_H_
|
||||
#define __MEMORY_H_
|
||||
|
||||
#include"typedefs.h"
|
||||
#include"lists.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
//NOTE(martin): base allocator
|
||||
//--------------------------------------------------------------------------------
|
||||
typedef void*(*mem_reserve_function)(void* context, u64 size);
|
||||
typedef void(*mem_modify_function)(void* context, void* ptr, u64 size);
|
||||
|
||||
typedef struct mem_base_allocator
|
||||
{
|
||||
mem_reserve_function reserve;
|
||||
mem_modify_function commit;
|
||||
mem_modify_function decommit;
|
||||
mem_modify_function release;
|
||||
void* context;
|
||||
|
||||
} mem_base_allocator;
|
||||
|
||||
MP_API mem_base_allocator* mem_base_allocator_default();
|
||||
|
||||
#define mem_base_reserve(base, size) base->reserve(base->context, size)
|
||||
#define mem_base_commit(base, ptr, size) base->commit(base->context, ptr, size)
|
||||
#define mem_base_decommit(base, ptr, size) base->decommit(base->context, ptr, size)
|
||||
#define mem_base_release(base, ptr, size) base->release(base->context, ptr, size)
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
//NOTE(martin): memory arena
|
||||
//--------------------------------------------------------------------------------
|
||||
typedef struct mem_arena
|
||||
{
|
||||
mem_base_allocator* base;
|
||||
char* ptr;
|
||||
u64 offset;
|
||||
u64 committed;
|
||||
u64 cap;
|
||||
} mem_arena;
|
||||
|
||||
typedef struct mem_arena_options
|
||||
{
|
||||
mem_base_allocator* base;
|
||||
u64 reserve;
|
||||
} mem_arena_options;
|
||||
|
||||
MP_API void mem_arena_init(mem_arena* arena);
|
||||
MP_API void mem_arena_init_with_options(mem_arena* arena, mem_arena_options* options);
|
||||
MP_API void mem_arena_release(mem_arena* arena);
|
||||
|
||||
MP_API void* mem_arena_alloc(mem_arena* arena, u64 size);
|
||||
MP_API void mem_arena_clear(mem_arena* arena);
|
||||
|
||||
#define mem_arena_alloc_type(arena, type) ((type*)mem_arena_alloc(arena, sizeof(type)))
|
||||
#define mem_arena_alloc_array(arena, type, count) ((type*)mem_arena_alloc(arena, sizeof(type)*(count)))
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
//NOTE(martin): memory pool
|
||||
//--------------------------------------------------------------------------------
|
||||
typedef struct mem_pool
|
||||
{
|
||||
mem_arena arena;
|
||||
list_info freeList;
|
||||
u64 blockSize;
|
||||
} mem_pool;
|
||||
|
||||
typedef struct mem_pool_options
|
||||
{
|
||||
mem_base_allocator* base;
|
||||
u64 reserve;
|
||||
} mem_pool_options;
|
||||
|
||||
MP_API void mem_pool_init(mem_pool* pool, u64 blockSize);
|
||||
MP_API void mem_pool_init_with_options(mem_pool* pool, u64 blockSize, mem_pool_options* options);
|
||||
MP_API void mem_pool_release(mem_pool* pool);
|
||||
|
||||
MP_API void* mem_pool_alloc(mem_pool* pool);
|
||||
MP_API void mem_pool_recycle(mem_pool* pool, void* ptr);
|
||||
MP_API void mem_pool_clear(mem_pool* pool);
|
||||
|
||||
#define mem_pool_alloc_type(arena, type) ((type*)mem_pool_alloc(arena))
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
//NOTE(martin): per-thread implicit scratch arena
|
||||
//--------------------------------------------------------------------------------
|
||||
MP_API void mem_scratch_clear();
|
||||
MP_API mem_arena* mem_scratch();
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif //__MEMORY_H_
|
||||
/************************************************************//**
|
||||
*
|
||||
* @file: memory.h
|
||||
* @author: Martin Fouilleul
|
||||
* @date: 24/10/2019
|
||||
* @revision:
|
||||
*
|
||||
*****************************************************************/
|
||||
#ifndef __MEMORY_H_
|
||||
#define __MEMORY_H_
|
||||
|
||||
#include"typedefs.h"
|
||||
#include"lists.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
//NOTE(martin): base allocator
|
||||
//--------------------------------------------------------------------------------
|
||||
typedef void*(*mem_reserve_function)(void* context, u64 size);
|
||||
typedef void(*mem_modify_function)(void* context, void* ptr, u64 size);
|
||||
|
||||
typedef struct mem_base_allocator
|
||||
{
|
||||
mem_reserve_function reserve;
|
||||
mem_modify_function commit;
|
||||
mem_modify_function decommit;
|
||||
mem_modify_function release;
|
||||
void* context;
|
||||
|
||||
} mem_base_allocator;
|
||||
|
||||
MP_API mem_base_allocator* mem_base_allocator_default();
|
||||
|
||||
#define mem_base_reserve(base, size) base->reserve(base->context, size)
|
||||
#define mem_base_commit(base, ptr, size) base->commit(base->context, ptr, size)
|
||||
#define mem_base_decommit(base, ptr, size) base->decommit(base->context, ptr, size)
|
||||
#define mem_base_release(base, ptr, size) base->release(base->context, ptr, size)
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
//NOTE(martin): memory arena
|
||||
//--------------------------------------------------------------------------------
|
||||
typedef struct mem_arena
|
||||
{
|
||||
mem_base_allocator* base;
|
||||
char* ptr;
|
||||
u64 offset;
|
||||
u64 committed;
|
||||
u64 cap;
|
||||
} mem_arena;
|
||||
|
||||
typedef struct mem_arena_options
|
||||
{
|
||||
mem_base_allocator* base;
|
||||
u64 reserve;
|
||||
} mem_arena_options;
|
||||
|
||||
MP_API void mem_arena_init(mem_arena* arena);
|
||||
MP_API void mem_arena_init_with_options(mem_arena* arena, mem_arena_options* options);
|
||||
MP_API void mem_arena_release(mem_arena* arena);
|
||||
|
||||
MP_API void* mem_arena_alloc(mem_arena* arena, u64 size);
|
||||
MP_API void mem_arena_clear(mem_arena* arena);
|
||||
|
||||
#define mem_arena_alloc_type(arena, type) ((type*)mem_arena_alloc(arena, sizeof(type)))
|
||||
#define mem_arena_alloc_array(arena, type, count) ((type*)mem_arena_alloc(arena, sizeof(type)*(count)))
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
//NOTE(martin): memory pool
|
||||
//--------------------------------------------------------------------------------
|
||||
typedef struct mem_pool
|
||||
{
|
||||
mem_arena arena;
|
||||
list_info freeList;
|
||||
u64 blockSize;
|
||||
} mem_pool;
|
||||
|
||||
typedef struct mem_pool_options
|
||||
{
|
||||
mem_base_allocator* base;
|
||||
u64 reserve;
|
||||
} mem_pool_options;
|
||||
|
||||
MP_API void mem_pool_init(mem_pool* pool, u64 blockSize);
|
||||
MP_API void mem_pool_init_with_options(mem_pool* pool, u64 blockSize, mem_pool_options* options);
|
||||
MP_API void mem_pool_release(mem_pool* pool);
|
||||
|
||||
MP_API void* mem_pool_alloc(mem_pool* pool);
|
||||
MP_API void mem_pool_recycle(mem_pool* pool, void* ptr);
|
||||
MP_API void mem_pool_clear(mem_pool* pool);
|
||||
|
||||
#define mem_pool_alloc_type(arena, type) ((type*)mem_pool_alloc(arena))
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
//NOTE(martin): per-thread implicit scratch arena
|
||||
//--------------------------------------------------------------------------------
|
||||
MP_API void mem_scratch_clear();
|
||||
MP_API mem_arena* mem_scratch();
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif //__MEMORY_H_
|
||||
|
|
|
@ -1,113 +1,113 @@
|
|||
/************************************************************//**
|
||||
*
|
||||
* @file: strings.h
|
||||
* @author: Martin Fouilleul
|
||||
* @date: 29/05/2021
|
||||
* @revision:
|
||||
*
|
||||
*****************************************************************/
|
||||
#ifndef __STRINGS_H_
|
||||
#define __STRINGS_H_
|
||||
|
||||
#include"typedefs.h"
|
||||
#include"lists.h"
|
||||
#include"memory.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//----------------------------------------------------------------------------------
|
||||
// string slices as values
|
||||
//----------------------------------------------------------------------------------
|
||||
typedef struct str8
|
||||
{
|
||||
u64 len;
|
||||
char* ptr;
|
||||
} str8;
|
||||
|
||||
#define str8_lit(s) ((str8){.len = sizeof(s)-1, .ptr = (char*)(s)})
|
||||
#define str8_unbox(s) (int)((s).len), ((s).ptr)
|
||||
|
||||
MP_API str8 str8_from_buffer(u64 len, char* buffer);
|
||||
MP_API str8 str8_from_cstring(char* str);
|
||||
MP_API str8 str8_slice(str8 s, u64 start, u64 end);
|
||||
|
||||
MP_API str8 str8_push_buffer(mem_arena* arena, u64 len, char* buffer);
|
||||
MP_API str8 str8_push_cstring(mem_arena* arena, const char* str);
|
||||
MP_API str8 str8_push_copy(mem_arena* arena, str8 s);
|
||||
MP_API str8 str8_push_slice(mem_arena* arena, str8 s, u64 start, u64 end);
|
||||
|
||||
MP_API str8 str8_pushfv(mem_arena* arena, const char* format, va_list args);
|
||||
MP_API str8 str8_pushf(mem_arena* arena, const char* format, ...);
|
||||
|
||||
MP_API int str8_cmp(str8 s1, str8 s2);
|
||||
|
||||
MP_API char* str8_to_cstring(mem_arena* arena, str8 string);
|
||||
//----------------------------------------------------------------------------------
|
||||
// string lists
|
||||
//----------------------------------------------------------------------------------
|
||||
typedef struct str8_elt
|
||||
{
|
||||
list_elt listElt;
|
||||
str8 string;
|
||||
} str8_elt;
|
||||
|
||||
typedef struct str8_list
|
||||
{
|
||||
list_info list;
|
||||
u64 eltCount;
|
||||
u64 len;
|
||||
} str8_list;
|
||||
|
||||
MP_API void str8_list_push(mem_arena* arena, str8_list* list, str8 str);
|
||||
MP_API void str8_list_pushf(mem_arena* arena, str8_list* list, const char* format, ...);
|
||||
|
||||
MP_API str8 str8_list_join(mem_arena* arena, str8_list list);
|
||||
MP_API str8_list str8_split(mem_arena* arena, str8 str, str8_list separators);
|
||||
|
||||
//----------------------------------------------------------------------------------
|
||||
// u32 strings
|
||||
//----------------------------------------------------------------------------------
|
||||
typedef struct str32
|
||||
{
|
||||
u64 len;
|
||||
u32* ptr;
|
||||
} str32;
|
||||
|
||||
MP_API str32 str32_from_buffer(u64 len, u32* buffer);
|
||||
MP_API str32 str32_slice(str32 s, u64 start, u64 end);
|
||||
|
||||
MP_API str32 str32_push_buffer(mem_arena* arena, u64 len, u32* buffer);
|
||||
MP_API str32 str32_push_copy(mem_arena* arena, str32 s);
|
||||
MP_API str32 str32_push_slice(mem_arena* arena, str32 s, u64 start, u64 end);
|
||||
|
||||
typedef struct str32_elt
|
||||
{
|
||||
list_elt listElt;
|
||||
str32 string;
|
||||
} str32_elt;
|
||||
|
||||
typedef struct str32_list
|
||||
{
|
||||
list_info list;
|
||||
u64 eltCount;
|
||||
u64 len;
|
||||
} str32_list;
|
||||
|
||||
MP_API void str32_list_push(mem_arena* arena, str32_list* list, str32 str);
|
||||
MP_API str32 str32_list_join(mem_arena* arena, str32_list list);
|
||||
MP_API str32_list str32_split(mem_arena* arena, str32 str, str32_list separators);
|
||||
|
||||
//----------------------------------------------------------------------------------
|
||||
// Paths helpers
|
||||
//----------------------------------------------------------------------------------
|
||||
MP_API str8 mp_path_directory(str8 fullPath);
|
||||
MP_API str8 mp_path_base_name(str8 fullPath);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
|
||||
#endif //__STRINGS_H_
|
||||
/************************************************************//**
|
||||
*
|
||||
* @file: strings.h
|
||||
* @author: Martin Fouilleul
|
||||
* @date: 29/05/2021
|
||||
* @revision:
|
||||
*
|
||||
*****************************************************************/
|
||||
#ifndef __STRINGS_H_
|
||||
#define __STRINGS_H_
|
||||
|
||||
#include"typedefs.h"
|
||||
#include"lists.h"
|
||||
#include"memory.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//----------------------------------------------------------------------------------
|
||||
// string slices as values
|
||||
//----------------------------------------------------------------------------------
|
||||
typedef struct str8
|
||||
{
|
||||
u64 len;
|
||||
char* ptr;
|
||||
} str8;
|
||||
|
||||
#define str8_lit(s) ((str8){.len = sizeof(s)-1, .ptr = (char*)(s)})
|
||||
#define str8_unbox(s) (int)((s).len), ((s).ptr)
|
||||
|
||||
MP_API str8 str8_from_buffer(u64 len, char* buffer);
|
||||
MP_API str8 str8_from_cstring(char* str);
|
||||
MP_API str8 str8_slice(str8 s, u64 start, u64 end);
|
||||
|
||||
MP_API str8 str8_push_buffer(mem_arena* arena, u64 len, char* buffer);
|
||||
MP_API str8 str8_push_cstring(mem_arena* arena, const char* str);
|
||||
MP_API str8 str8_push_copy(mem_arena* arena, str8 s);
|
||||
MP_API str8 str8_push_slice(mem_arena* arena, str8 s, u64 start, u64 end);
|
||||
|
||||
MP_API str8 str8_pushfv(mem_arena* arena, const char* format, va_list args);
|
||||
MP_API str8 str8_pushf(mem_arena* arena, const char* format, ...);
|
||||
|
||||
MP_API int str8_cmp(str8 s1, str8 s2);
|
||||
|
||||
MP_API char* str8_to_cstring(mem_arena* arena, str8 string);
|
||||
//----------------------------------------------------------------------------------
|
||||
// string lists
|
||||
//----------------------------------------------------------------------------------
|
||||
typedef struct str8_elt
|
||||
{
|
||||
list_elt listElt;
|
||||
str8 string;
|
||||
} str8_elt;
|
||||
|
||||
typedef struct str8_list
|
||||
{
|
||||
list_info list;
|
||||
u64 eltCount;
|
||||
u64 len;
|
||||
} str8_list;
|
||||
|
||||
MP_API void str8_list_push(mem_arena* arena, str8_list* list, str8 str);
|
||||
MP_API void str8_list_pushf(mem_arena* arena, str8_list* list, const char* format, ...);
|
||||
|
||||
MP_API str8 str8_list_join(mem_arena* arena, str8_list list);
|
||||
MP_API str8_list str8_split(mem_arena* arena, str8 str, str8_list separators);
|
||||
|
||||
//----------------------------------------------------------------------------------
|
||||
// u32 strings
|
||||
//----------------------------------------------------------------------------------
|
||||
typedef struct str32
|
||||
{
|
||||
u64 len;
|
||||
u32* ptr;
|
||||
} str32;
|
||||
|
||||
MP_API str32 str32_from_buffer(u64 len, u32* buffer);
|
||||
MP_API str32 str32_slice(str32 s, u64 start, u64 end);
|
||||
|
||||
MP_API str32 str32_push_buffer(mem_arena* arena, u64 len, u32* buffer);
|
||||
MP_API str32 str32_push_copy(mem_arena* arena, str32 s);
|
||||
MP_API str32 str32_push_slice(mem_arena* arena, str32 s, u64 start, u64 end);
|
||||
|
||||
typedef struct str32_elt
|
||||
{
|
||||
list_elt listElt;
|
||||
str32 string;
|
||||
} str32_elt;
|
||||
|
||||
typedef struct str32_list
|
||||
{
|
||||
list_info list;
|
||||
u64 eltCount;
|
||||
u64 len;
|
||||
} str32_list;
|
||||
|
||||
MP_API void str32_list_push(mem_arena* arena, str32_list* list, str32 str);
|
||||
MP_API str32 str32_list_join(mem_arena* arena, str32_list list);
|
||||
MP_API str32_list str32_split(mem_arena* arena, str32 str, str32_list separators);
|
||||
|
||||
//----------------------------------------------------------------------------------
|
||||
// Paths helpers
|
||||
//----------------------------------------------------------------------------------
|
||||
MP_API str8 mp_path_directory(str8 fullPath);
|
||||
MP_API str8 mp_path_base_name(str8 fullPath);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
|
||||
#endif //__STRINGS_H_
|
||||
|
|
568
src/util/utf8.c
568
src/util/utf8.c
|
@ -1,284 +1,284 @@
|
|||
//*****************************************************************
|
||||
//
|
||||
// $file: utf8.c $
|
||||
// $author: Martin Fouilleul $
|
||||
// $date: 05/11/2016 $
|
||||
// $revision: $
|
||||
// $note: (C) 2016 by Martin Fouilleul - all rights reserved $
|
||||
//
|
||||
//*****************************************************************
|
||||
#include"utf8.h"
|
||||
#include"platform.h"
|
||||
#include<string.h>
|
||||
|
||||
//-----------------------------------------------------------------
|
||||
// utf-8 gore
|
||||
//-----------------------------------------------------------------
|
||||
const u32 offsetsFromUTF8[6] = {
|
||||
0x00000000UL, 0x00003080UL, 0x000E2080UL,
|
||||
0x03C82080UL, 0xFA082080UL, 0x82082080UL };
|
||||
|
||||
const char trailingBytesForUTF8[256] = {
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5
|
||||
};
|
||||
|
||||
#define utf8_is_start_byte(c) (((c)&0xc0)!=0x80)
|
||||
|
||||
//-----------------------------------------------------------------
|
||||
//NOTE: getting sizes / offsets / indices
|
||||
//-----------------------------------------------------------------
|
||||
|
||||
u32 utf8_size_from_leading_char(char leadingChar)
|
||||
{
|
||||
return(trailingBytesForUTF8[(unsigned int)(unsigned char)leadingChar] + 1);
|
||||
}
|
||||
|
||||
u32 utf8_codepoint_size(utf32 codePoint)
|
||||
{
|
||||
if(codePoint < 0x80)
|
||||
{
|
||||
return(1);
|
||||
}
|
||||
if(codePoint < 0x800)
|
||||
{
|
||||
return(2);
|
||||
}
|
||||
if(codePoint < 0x10000)
|
||||
{
|
||||
return(3);
|
||||
}
|
||||
if(codePoint < 0x110000)
|
||||
{
|
||||
return(4);
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
u64 utf8_codepoint_count_for_string(str8 string)
|
||||
{
|
||||
u64 byteOffset = 0;
|
||||
u64 codePointIndex = 0;
|
||||
for(;
|
||||
(byteOffset < string.len) && (string.ptr[byteOffset] != 0);
|
||||
codePointIndex++)
|
||||
{
|
||||
utf8_dec decode = utf8_decode_at(string, byteOffset);
|
||||
byteOffset += decode.size;
|
||||
}
|
||||
return(codePointIndex);
|
||||
}
|
||||
|
||||
u64 utf8_byte_count_for_codepoints(str32 codePoints)
|
||||
{
|
||||
//NOTE(martin): return the exact number of bytes taken by the encoded
|
||||
// version of codePoints. (ie do not attempt to provision
|
||||
// for a zero terminator).
|
||||
u64 byteCount = 0;
|
||||
for(u64 i=0; i<codePoints.len; i++)
|
||||
{
|
||||
byteCount += utf8_codepoint_size(codePoints.ptr[i]);
|
||||
}
|
||||
return(byteCount);
|
||||
}
|
||||
|
||||
u64 utf8_next_offset(str8 string, u64 byteOffset)
|
||||
{
|
||||
u64 res = 0;
|
||||
if(byteOffset >= string.len)
|
||||
{
|
||||
res = string.len;
|
||||
}
|
||||
else
|
||||
{
|
||||
u64 nextOffset = byteOffset + utf8_size_from_leading_char(string.ptr[byteOffset]);
|
||||
res = minimum(nextOffset, string.len);
|
||||
}
|
||||
return(res);
|
||||
}
|
||||
|
||||
u64 utf8_prev_offset(str8 string, u64 byteOffset)
|
||||
{
|
||||
u64 res = 0;
|
||||
if(byteOffset > string.len)
|
||||
{
|
||||
res = string.len;
|
||||
}
|
||||
else if(byteOffset)
|
||||
{
|
||||
byteOffset--;
|
||||
while(byteOffset > 0 && !utf8_is_start_byte(string.ptr[byteOffset]))
|
||||
{
|
||||
byteOffset--;
|
||||
}
|
||||
res = byteOffset;
|
||||
}
|
||||
return(res);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------
|
||||
//NOTE: encoding / decoding
|
||||
//-----------------------------------------------------------------
|
||||
|
||||
utf8_dec utf8_decode_at(str8 string, u64 offset)
|
||||
{
|
||||
//NOTE(martin): get the first codepoint in str, and advance index to the
|
||||
// next utf8 character
|
||||
//TODO(martin): check for utf-16 surrogate pairs
|
||||
utf32 cp = 0;
|
||||
u64 sz = 0;
|
||||
|
||||
if(offset >= string.len || !string.ptr[offset])
|
||||
{
|
||||
cp = 0;
|
||||
sz = 1;
|
||||
}
|
||||
else if( !utf8_is_start_byte(string.ptr[offset]))
|
||||
{
|
||||
//NOTE(martin): unexpected continuation or invalid character.
|
||||
cp = 0xfffd;
|
||||
sz = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
int expectedSize = utf8_size_from_leading_char(string.ptr[offset]);
|
||||
do
|
||||
{
|
||||
/*NOTE(martin):
|
||||
we shift 6 bits and add the next byte at each round.
|
||||
at the end we have our utf8 codepoint, added to the shifted versions
|
||||
of the utf8 leading bits for each encoded byte. These values are
|
||||
precomputed in offsetsFromUTF8.
|
||||
*/
|
||||
unsigned char b = string.ptr[offset];
|
||||
cp <<= 6;
|
||||
cp += b;
|
||||
offset += 1;
|
||||
sz++;
|
||||
|
||||
if(b == 0xc0 || b == 0xc1 || b >= 0xc5)
|
||||
{
|
||||
//NOTE(martin): invalid byte encountered
|
||||
break;
|
||||
}
|
||||
|
||||
} while( offset < string.len
|
||||
&& string.ptr[offset]
|
||||
&& !utf8_is_start_byte(string.ptr[offset])
|
||||
&& sz < expectedSize);
|
||||
|
||||
if(sz != expectedSize)
|
||||
{
|
||||
//NOTE(martin): if we encountered an error, we return the replacement codepoint U+FFFD
|
||||
cp = 0xfffd;
|
||||
}
|
||||
else
|
||||
{
|
||||
cp -= offsetsFromUTF8[sz-1];
|
||||
|
||||
//NOTE(martin): check for invalid codepoints
|
||||
if(cp > 0x10ffff || (cp >= 0xd800 && cp <= 0xdfff))
|
||||
{
|
||||
cp = 0xfffd;
|
||||
}
|
||||
}
|
||||
}
|
||||
utf8_dec res = {.codepoint = cp, .size = sz};
|
||||
return(res);
|
||||
}
|
||||
|
||||
utf8_dec utf8_decode(str8 string)
|
||||
{
|
||||
return(utf8_decode_at(string, 0));
|
||||
}
|
||||
|
||||
str8 utf8_encode(char* dest, utf32 codePoint)
|
||||
{
|
||||
u64 sz = 0;
|
||||
if (codePoint < 0x80)
|
||||
{
|
||||
dest[0] = (char)codePoint;
|
||||
sz = 1;
|
||||
}
|
||||
else if (codePoint < 0x800)
|
||||
{
|
||||
dest[0] = (codePoint>>6) | 0xC0;
|
||||
dest[1] = (codePoint & 0x3F) | 0x80;
|
||||
sz = 2;
|
||||
}
|
||||
else if (codePoint < 0x10000)
|
||||
{
|
||||
dest[0] = (codePoint>>12) | 0xE0;
|
||||
dest[1] = ((codePoint>>6) & 0x3F) | 0x80;
|
||||
dest[2] = (codePoint & 0x3F) | 0x80;
|
||||
sz = 3;
|
||||
}
|
||||
else if (codePoint < 0x110000)
|
||||
{
|
||||
dest[0] = (codePoint>>18) | 0xF0;
|
||||
dest[1] = ((codePoint>>12) & 0x3F) | 0x80;
|
||||
dest[2] = ((codePoint>>6) & 0x3F) | 0x80;
|
||||
dest[3] = (codePoint & 0x3F) | 0x80;
|
||||
sz = 4;
|
||||
}
|
||||
str8 res = {.len = sz, .ptr = dest};
|
||||
return(res);
|
||||
}
|
||||
|
||||
str32 utf8_to_codepoints(u64 maxCount, utf32* backing, str8 string)
|
||||
{
|
||||
u64 codePointIndex = 0;
|
||||
u64 byteOffset = 0;
|
||||
for(; codePointIndex < maxCount && byteOffset < string.len; codePointIndex++)
|
||||
{
|
||||
utf8_dec decode = utf8_decode_at(string, byteOffset);
|
||||
backing[codePointIndex] = decode.codepoint;
|
||||
byteOffset += decode.size;
|
||||
}
|
||||
str32 res = {.len = codePointIndex, .ptr = backing};
|
||||
return(res);
|
||||
}
|
||||
|
||||
str8 utf8_from_codepoints(u64 maxBytes, char* backing, str32 codePoints)
|
||||
{
|
||||
u64 byteOffset = 0;
|
||||
for(u64 codePointIndex = 0; (codePointIndex < codePoints.len); codePointIndex++)
|
||||
{
|
||||
utf32 codePoint = codePoints.ptr[codePointIndex];
|
||||
u32 byteCount = utf8_codepoint_size(codePoint);
|
||||
if(byteOffset + byteCount > maxBytes)
|
||||
{
|
||||
break;
|
||||
}
|
||||
utf8_encode(backing+byteOffset, codePoint);
|
||||
byteOffset += byteCount;
|
||||
}
|
||||
str8 res = {.len = byteOffset, .ptr = backing};
|
||||
return(res);
|
||||
}
|
||||
|
||||
str32 utf8_push_to_codepoints(mem_arena* arena, str8 string)
|
||||
{
|
||||
u64 count = utf8_codepoint_count_for_string(string);
|
||||
utf32* backing = mem_arena_alloc_array(arena, utf32, count);
|
||||
str32 res = utf8_to_codepoints(count, backing, string);
|
||||
return(res);
|
||||
}
|
||||
|
||||
str8 utf8_push_from_codepoints(mem_arena* arena, str32 codePoints)
|
||||
{
|
||||
u64 count = utf8_byte_count_for_codepoints(codePoints);
|
||||
char* backing = mem_arena_alloc_array(arena, char, count);
|
||||
str8 res = utf8_from_codepoints(count, backing, codePoints);
|
||||
return(res);
|
||||
}
|
||||
|
||||
#define UNICODE_RANGE(start, cnt, name) MP_API const unicode_range _cat2_(UNICODE_RANGE_, name) = { .firstCodePoint = start, .count = cnt };
|
||||
UNICODE_RANGES
|
||||
#undef UNICODE_RANGE
|
||||
//*****************************************************************
|
||||
//
|
||||
// $file: utf8.c $
|
||||
// $author: Martin Fouilleul $
|
||||
// $date: 05/11/2016 $
|
||||
// $revision: $
|
||||
// $note: (C) 2016 by Martin Fouilleul - all rights reserved $
|
||||
//
|
||||
//*****************************************************************
|
||||
#include"utf8.h"
|
||||
#include"platform.h"
|
||||
#include<string.h>
|
||||
|
||||
//-----------------------------------------------------------------
|
||||
// utf-8 gore
|
||||
//-----------------------------------------------------------------
|
||||
const u32 offsetsFromUTF8[6] = {
|
||||
0x00000000UL, 0x00003080UL, 0x000E2080UL,
|
||||
0x03C82080UL, 0xFA082080UL, 0x82082080UL };
|
||||
|
||||
const char trailingBytesForUTF8[256] = {
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5
|
||||
};
|
||||
|
||||
#define utf8_is_start_byte(c) (((c)&0xc0)!=0x80)
|
||||
|
||||
//-----------------------------------------------------------------
|
||||
//NOTE: getting sizes / offsets / indices
|
||||
//-----------------------------------------------------------------
|
||||
|
||||
u32 utf8_size_from_leading_char(char leadingChar)
|
||||
{
|
||||
return(trailingBytesForUTF8[(unsigned int)(unsigned char)leadingChar] + 1);
|
||||
}
|
||||
|
||||
u32 utf8_codepoint_size(utf32 codePoint)
|
||||
{
|
||||
if(codePoint < 0x80)
|
||||
{
|
||||
return(1);
|
||||
}
|
||||
if(codePoint < 0x800)
|
||||
{
|
||||
return(2);
|
||||
}
|
||||
if(codePoint < 0x10000)
|
||||
{
|
||||
return(3);
|
||||
}
|
||||
if(codePoint < 0x110000)
|
||||
{
|
||||
return(4);
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
u64 utf8_codepoint_count_for_string(str8 string)
|
||||
{
|
||||
u64 byteOffset = 0;
|
||||
u64 codePointIndex = 0;
|
||||
for(;
|
||||
(byteOffset < string.len) && (string.ptr[byteOffset] != 0);
|
||||
codePointIndex++)
|
||||
{
|
||||
utf8_dec decode = utf8_decode_at(string, byteOffset);
|
||||
byteOffset += decode.size;
|
||||
}
|
||||
return(codePointIndex);
|
||||
}
|
||||
|
||||
u64 utf8_byte_count_for_codepoints(str32 codePoints)
|
||||
{
|
||||
//NOTE(martin): return the exact number of bytes taken by the encoded
|
||||
// version of codePoints. (ie do not attempt to provision
|
||||
// for a zero terminator).
|
||||
u64 byteCount = 0;
|
||||
for(u64 i=0; i<codePoints.len; i++)
|
||||
{
|
||||
byteCount += utf8_codepoint_size(codePoints.ptr[i]);
|
||||
}
|
||||
return(byteCount);
|
||||
}
|
||||
|
||||
u64 utf8_next_offset(str8 string, u64 byteOffset)
|
||||
{
|
||||
u64 res = 0;
|
||||
if(byteOffset >= string.len)
|
||||
{
|
||||
res = string.len;
|
||||
}
|
||||
else
|
||||
{
|
||||
u64 nextOffset = byteOffset + utf8_size_from_leading_char(string.ptr[byteOffset]);
|
||||
res = minimum(nextOffset, string.len);
|
||||
}
|
||||
return(res);
|
||||
}
|
||||
|
||||
u64 utf8_prev_offset(str8 string, u64 byteOffset)
|
||||
{
|
||||
u64 res = 0;
|
||||
if(byteOffset > string.len)
|
||||
{
|
||||
res = string.len;
|
||||
}
|
||||
else if(byteOffset)
|
||||
{
|
||||
byteOffset--;
|
||||
while(byteOffset > 0 && !utf8_is_start_byte(string.ptr[byteOffset]))
|
||||
{
|
||||
byteOffset--;
|
||||
}
|
||||
res = byteOffset;
|
||||
}
|
||||
return(res);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------
|
||||
//NOTE: encoding / decoding
|
||||
//-----------------------------------------------------------------
|
||||
|
||||
utf8_dec utf8_decode_at(str8 string, u64 offset)
|
||||
{
|
||||
//NOTE(martin): get the first codepoint in str, and advance index to the
|
||||
// next utf8 character
|
||||
//TODO(martin): check for utf-16 surrogate pairs
|
||||
utf32 cp = 0;
|
||||
u64 sz = 0;
|
||||
|
||||
if(offset >= string.len || !string.ptr[offset])
|
||||
{
|
||||
cp = 0;
|
||||
sz = 1;
|
||||
}
|
||||
else if( !utf8_is_start_byte(string.ptr[offset]))
|
||||
{
|
||||
//NOTE(martin): unexpected continuation or invalid character.
|
||||
cp = 0xfffd;
|
||||
sz = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
int expectedSize = utf8_size_from_leading_char(string.ptr[offset]);
|
||||
do
|
||||
{
|
||||
/*NOTE(martin):
|
||||
we shift 6 bits and add the next byte at each round.
|
||||
at the end we have our utf8 codepoint, added to the shifted versions
|
||||
of the utf8 leading bits for each encoded byte. These values are
|
||||
precomputed in offsetsFromUTF8.
|
||||
*/
|
||||
unsigned char b = string.ptr[offset];
|
||||
cp <<= 6;
|
||||
cp += b;
|
||||
offset += 1;
|
||||
sz++;
|
||||
|
||||
if(b == 0xc0 || b == 0xc1 || b >= 0xc5)
|
||||
{
|
||||
//NOTE(martin): invalid byte encountered
|
||||
break;
|
||||
}
|
||||
|
||||
} while( offset < string.len
|
||||
&& string.ptr[offset]
|
||||
&& !utf8_is_start_byte(string.ptr[offset])
|
||||
&& sz < expectedSize);
|
||||
|
||||
if(sz != expectedSize)
|
||||
{
|
||||
//NOTE(martin): if we encountered an error, we return the replacement codepoint U+FFFD
|
||||
cp = 0xfffd;
|
||||
}
|
||||
else
|
||||
{
|
||||
cp -= offsetsFromUTF8[sz-1];
|
||||
|
||||
//NOTE(martin): check for invalid codepoints
|
||||
if(cp > 0x10ffff || (cp >= 0xd800 && cp <= 0xdfff))
|
||||
{
|
||||
cp = 0xfffd;
|
||||
}
|
||||
}
|
||||
}
|
||||
utf8_dec res = {.codepoint = cp, .size = sz};
|
||||
return(res);
|
||||
}
|
||||
|
||||
utf8_dec utf8_decode(str8 string)
|
||||
{
|
||||
return(utf8_decode_at(string, 0));
|
||||
}
|
||||
|
||||
str8 utf8_encode(char* dest, utf32 codePoint)
|
||||
{
|
||||
u64 sz = 0;
|
||||
if (codePoint < 0x80)
|
||||
{
|
||||
dest[0] = (char)codePoint;
|
||||
sz = 1;
|
||||
}
|
||||
else if (codePoint < 0x800)
|
||||
{
|
||||
dest[0] = (codePoint>>6) | 0xC0;
|
||||
dest[1] = (codePoint & 0x3F) | 0x80;
|
||||
sz = 2;
|
||||
}
|
||||
else if (codePoint < 0x10000)
|
||||
{
|
||||
dest[0] = (codePoint>>12) | 0xE0;
|
||||
dest[1] = ((codePoint>>6) & 0x3F) | 0x80;
|
||||
dest[2] = (codePoint & 0x3F) | 0x80;
|
||||
sz = 3;
|
||||
}
|
||||
else if (codePoint < 0x110000)
|
||||
{
|
||||
dest[0] = (codePoint>>18) | 0xF0;
|
||||
dest[1] = ((codePoint>>12) & 0x3F) | 0x80;
|
||||
dest[2] = ((codePoint>>6) & 0x3F) | 0x80;
|
||||
dest[3] = (codePoint & 0x3F) | 0x80;
|
||||
sz = 4;
|
||||
}
|
||||
str8 res = {.len = sz, .ptr = dest};
|
||||
return(res);
|
||||
}
|
||||
|
||||
str32 utf8_to_codepoints(u64 maxCount, utf32* backing, str8 string)
|
||||
{
|
||||
u64 codePointIndex = 0;
|
||||
u64 byteOffset = 0;
|
||||
for(; codePointIndex < maxCount && byteOffset < string.len; codePointIndex++)
|
||||
{
|
||||
utf8_dec decode = utf8_decode_at(string, byteOffset);
|
||||
backing[codePointIndex] = decode.codepoint;
|
||||
byteOffset += decode.size;
|
||||
}
|
||||
str32 res = {.len = codePointIndex, .ptr = backing};
|
||||
return(res);
|
||||
}
|
||||
|
||||
str8 utf8_from_codepoints(u64 maxBytes, char* backing, str32 codePoints)
|
||||
{
|
||||
u64 byteOffset = 0;
|
||||
for(u64 codePointIndex = 0; (codePointIndex < codePoints.len); codePointIndex++)
|
||||
{
|
||||
utf32 codePoint = codePoints.ptr[codePointIndex];
|
||||
u32 byteCount = utf8_codepoint_size(codePoint);
|
||||
if(byteOffset + byteCount > maxBytes)
|
||||
{
|
||||
break;
|
||||
}
|
||||
utf8_encode(backing+byteOffset, codePoint);
|
||||
byteOffset += byteCount;
|
||||
}
|
||||
str8 res = {.len = byteOffset, .ptr = backing};
|
||||
return(res);
|
||||
}
|
||||
|
||||
str32 utf8_push_to_codepoints(mem_arena* arena, str8 string)
|
||||
{
|
||||
u64 count = utf8_codepoint_count_for_string(string);
|
||||
utf32* backing = mem_arena_alloc_array(arena, utf32, count);
|
||||
str32 res = utf8_to_codepoints(count, backing, string);
|
||||
return(res);
|
||||
}
|
||||
|
||||
str8 utf8_push_from_codepoints(mem_arena* arena, str32 codePoints)
|
||||
{
|
||||
u64 count = utf8_byte_count_for_codepoints(codePoints);
|
||||
char* backing = mem_arena_alloc_array(arena, char, count);
|
||||
str8 res = utf8_from_codepoints(count, backing, codePoints);
|
||||
return(res);
|
||||
}
|
||||
|
||||
#define UNICODE_RANGE(start, cnt, name) MP_API const unicode_range _cat2_(UNICODE_RANGE_, name) = { .firstCodePoint = start, .count = cnt };
|
||||
UNICODE_RANGES
|
||||
#undef UNICODE_RANGE
|
||||
|
|
402
src/util/utf8.h
402
src/util/utf8.h
|
@ -1,201 +1,201 @@
|
|||
//*****************************************************************
|
||||
//
|
||||
// $file: utf8.h $
|
||||
// $author: Martin Fouilleul $
|
||||
// $date: 05/11/2016 $
|
||||
// $revision: $
|
||||
// $note: (C) 2016 by Martin Fouilleul - all rights reserved $
|
||||
//
|
||||
//*****************************************************************
|
||||
#ifndef __UTF8_H_
|
||||
#define __UTF8_H_
|
||||
|
||||
#include"typedefs.h"
|
||||
#include"macro_helpers.h"
|
||||
#include"strings.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef u32 utf32;
|
||||
|
||||
//-----------------------------------------------------------------
|
||||
//NOTE: getting sizes / offsets / indices
|
||||
//-----------------------------------------------------------------
|
||||
MP_API u32 utf8_size_from_leading_char(char leadingChar);
|
||||
MP_API u32 utf8_codepoint_size(utf32 codePoint);
|
||||
|
||||
MP_API u64 utf8_codepoint_count_for_string(str8 string);
|
||||
MP_API u64 utf8_byte_count_for_codepoints(str32 codePoints);
|
||||
|
||||
MP_API u64 utf8_next_offset(str8 string, u64 byteOffset);
|
||||
MP_API u64 utf8_prev_offset(str8 string, u64 byteOffset);
|
||||
|
||||
//-----------------------------------------------------------------
|
||||
//NOTE: encoding / decoding
|
||||
//-----------------------------------------------------------------
|
||||
typedef struct utf8_dec
|
||||
{
|
||||
utf32 codepoint; //NOTE: decoded codepoint
|
||||
u32 size; //NOTE: size of corresponding utf8 sequence
|
||||
} utf8_dec;
|
||||
|
||||
MP_API utf8_dec utf8_decode(str8 string); //NOTE: decode a single utf8 sequence at start of string
|
||||
MP_API utf8_dec utf8_decode_at(str8 string, u64 offset); //NOTE: decode a single utf8 sequence starting at byte offset
|
||||
MP_API str8 utf8_encode(char* dst, utf32 codePoint); //NOTE: encode codepoint into backing buffer dst
|
||||
|
||||
MP_API str32 utf8_to_codepoints(u64 maxCount, utf32* backing, str8 string);
|
||||
MP_API str8 utf8_from_codepoints(u64 maxBytes, char* backing, str32 codePoints);
|
||||
|
||||
MP_API str32 utf8_push_to_codepoints(mem_arena* arena, str8 string);
|
||||
MP_API str8 utf8_push_from_codepoints(mem_arena* arena, str32 codePoints);
|
||||
|
||||
//-----------------------------------------------------------------
|
||||
// utf8 range struct and X-macros for defining utf8 ranges
|
||||
//-----------------------------------------------------------------
|
||||
|
||||
typedef struct unicode_range
|
||||
{
|
||||
utf32 firstCodePoint;
|
||||
u32 count;
|
||||
} unicode_range;
|
||||
|
||||
//NOTE(martin): range declared here are defined in utf8.cpp
|
||||
// they can be used by prefixing them with UTF8_RANGE_, as in 'UTF8_RANGE_BASIC_LATIN'
|
||||
#define UNICODE_RANGES \
|
||||
UNICODE_RANGE(0x0000, 127, BASIC_LATIN) \
|
||||
UNICODE_RANGE(0x0080, 127, C1_CONTROLS_AND_LATIN_1_SUPPLEMENT) \
|
||||
UNICODE_RANGE(0x0100, 127, LATIN_EXTENDED_A) \
|
||||
UNICODE_RANGE(0x0180, 207, LATIN_EXTENDED_B) \
|
||||
UNICODE_RANGE(0x0250, 95, IPA_EXTENSIONS) \
|
||||
UNICODE_RANGE(0x02b0, 79, SPACING_MODIFIER_LETTERS) \
|
||||
UNICODE_RANGE(0x0300, 111, COMBINING_DIACRITICAL_MARKS) \
|
||||
UNICODE_RANGE(0x0370, 143, GREEK_COPTIC) \
|
||||
UNICODE_RANGE(0x0400, 255, CYRILLIC) \
|
||||
UNICODE_RANGE(0x0500, 47, CYRILLIC_SUPPLEMENT) \
|
||||
UNICODE_RANGE(0x0530, 95, ARMENIAN) \
|
||||
UNICODE_RANGE(0x0590, 111, HEBREW) \
|
||||
UNICODE_RANGE(0x0600, 255, ARABIC) \
|
||||
UNICODE_RANGE(0x0700, 79, SYRIAC) \
|
||||
UNICODE_RANGE(0x0780, 63, THAANA) \
|
||||
UNICODE_RANGE(0x0900, 127, DEVANAGARI) \
|
||||
UNICODE_RANGE(0x0980, 127, BENGALI_ASSAMESE) \
|
||||
UNICODE_RANGE(0x0a00, 127, GURMUKHI) \
|
||||
UNICODE_RANGE(0x0a80, 127, GUJARATI) \
|
||||
UNICODE_RANGE(0x0b00, 127, ORIYA) \
|
||||
UNICODE_RANGE(0x0b80, 127, TAMIL) \
|
||||
UNICODE_RANGE(0x0c00, 127, TELUGU) \
|
||||
UNICODE_RANGE(0x0c80, 127, KANNADA) \
|
||||
UNICODE_RANGE(0x0d00, 255, MALAYALAM) \
|
||||
UNICODE_RANGE(0x0d80, 127, SINHALA) \
|
||||
UNICODE_RANGE(0x0e00, 127, THAI) \
|
||||
UNICODE_RANGE(0x0e80, 127, LAO) \
|
||||
UNICODE_RANGE(0x0f00, 255, TIBETAN) \
|
||||
UNICODE_RANGE(0x1000, 159, MYANMAR) \
|
||||
UNICODE_RANGE(0x10a0, 95, GEORGIAN) \
|
||||
UNICODE_RANGE(0x1100, 255, HANGUL_JAMO) \
|
||||
UNICODE_RANGE(0x1200, 383, ETHIOPIC) \
|
||||
UNICODE_RANGE(0x13a0, 95, CHEROKEE) \
|
||||
UNICODE_RANGE(0x1400, 639, UNIFIED_CANADIAN_ABORIGINAL_SYLLABICS) \
|
||||
UNICODE_RANGE(0x1680, 31, OGHAM) \
|
||||
UNICODE_RANGE(0x16a0, 95, RUNIC) \
|
||||
UNICODE_RANGE(0x1700, 31, TAGALOG) \
|
||||
UNICODE_RANGE(0x1720, 31, HANUNOO) \
|
||||
UNICODE_RANGE(0x1740, 31, BUHID) \
|
||||
UNICODE_RANGE(0x1760, 31, TAGBANWA) \
|
||||
UNICODE_RANGE(0x1780, 127, KHMER) \
|
||||
UNICODE_RANGE(0x1800, 175, MONGOLIAN) \
|
||||
UNICODE_RANGE(0x1900, 79, LIMBU) \
|
||||
UNICODE_RANGE(0x1950, 47, TAI_LE) \
|
||||
UNICODE_RANGE(0x19e0, 31, KHMER_SYMBOLS) \
|
||||
UNICODE_RANGE(0x1d00, 127, PHONETIC_EXTENSIONS) \
|
||||
UNICODE_RANGE(0x1e00, 255, LATIN_EXTENDED_ADDITIONAL) \
|
||||
UNICODE_RANGE(0x1f00, 255, GREEK_EXTENDED) \
|
||||
UNICODE_RANGE(0x2000, 111, GENERAL_PUNCTUATION) \
|
||||
UNICODE_RANGE(0x2070, 47, SUPERSCRIPTS_AND_SUBSCRIPTS) \
|
||||
UNICODE_RANGE(0x20a0, 47, CURRENCY_SYMBOLS) \
|
||||
UNICODE_RANGE(0x20d0, 47, COMBINING_DIACRITICAL_MARKS_FOR_SYMBOLS) \
|
||||
UNICODE_RANGE(0x2100, 79, LETTERLIKE_SYMBOLS) \
|
||||
UNICODE_RANGE(0x2150, 63, NUMBER_FORMS) \
|
||||
UNICODE_RANGE(0x2190, 111, ARROWS) \
|
||||
UNICODE_RANGE(0x2200, 255, MATHEMATICAL_OPERATORS) \
|
||||
UNICODE_RANGE(0x2300, 255, MISCELLANEOUS_TECHNICAL) \
|
||||
UNICODE_RANGE(0x2400, 63, CONTROL_PICTURES) \
|
||||
UNICODE_RANGE(0x2440, 31, OPTICAL_CHARACTER_RECOGNITION) \
|
||||
UNICODE_RANGE(0x2460, 159, ENCLOSED_ALPHANUMERICS) \
|
||||
UNICODE_RANGE(0x2500, 127, BOX_DRAWING) \
|
||||
UNICODE_RANGE(0x2580, 31, BLOCK_ELEMENTS) \
|
||||
UNICODE_RANGE(0x25a0, 95, GEOMETRIC_SHAPES) \
|
||||
UNICODE_RANGE(0x2600, 255, MISCELLANEOUS_SYMBOLS) \
|
||||
UNICODE_RANGE(0x2700, 191, DINGBATS) \
|
||||
UNICODE_RANGE(0x27c0, 47, MISCELLANEOUS_MATHEMATICAL_SYMBOLS_A) \
|
||||
UNICODE_RANGE(0x27f0, 15, SUPPLEMENTAL_ARROWS_A) \
|
||||
UNICODE_RANGE(0x2800, 255, BRAILLE_PATTERNS) \
|
||||
UNICODE_RANGE(0x2900, 127, SUPPLEMENTAL_ARROWS_B) \
|
||||
UNICODE_RANGE(0x2980, 127, MISCELLANEOUS_MATHEMATICAL_SYMBOLS_B) \
|
||||
UNICODE_RANGE(0x2a00, 255, SUPPLEMENTAL_MATHEMATICAL_OPERATORS) \
|
||||
UNICODE_RANGE(0x2b00, 255, MISCELLANEOUS_SYMBOLS_AND_ARROWS) \
|
||||
UNICODE_RANGE(0x2e80, 127, CJK_RADICALS_SUPPLEMENT) \
|
||||
UNICODE_RANGE(0x2f00, 223, KANGXI_RADICALS) \
|
||||
UNICODE_RANGE(0x2ff0, 15, IDEOGRAPHIC_DESCRIPTION_CHARACTERS) \
|
||||
UNICODE_RANGE(0x3000, 63, CJK_SYMBOLS_AND_PUNCTUATION) \
|
||||
UNICODE_RANGE(0x3040, 95, HIRAGANA) \
|
||||
UNICODE_RANGE(0x30a0, 95, KATAKANA) \
|
||||
UNICODE_RANGE(0x3100, 47, BOPOMOFO) \
|
||||
UNICODE_RANGE(0x3130, 95, HANGUL_COMPATIBILITY_JAMO) \
|
||||
UNICODE_RANGE(0x3190, 15, KANBUN_KUNTEN) \
|
||||
UNICODE_RANGE(0x31a0, 31, BOPOMOFO_EXTENDED) \
|
||||
UNICODE_RANGE(0x31f0, 15, KATAKANA_PHONETIC_EXTENSIONS) \
|
||||
UNICODE_RANGE(0x3200, 255, ENCLOSED_CJK_LETTERS_AND_MONTHS) \
|
||||
UNICODE_RANGE(0x3300, 255, CJK_COMPATIBILITY) \
|
||||
UNICODE_RANGE(0x3400, 6591, CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A) \
|
||||
UNICODE_RANGE(0x4dc0, 63, YIJING_HEXAGRAM_SYMBOLS) \
|
||||
UNICODE_RANGE(0x4e00, 20911, CJK_UNIFIED_IDEOGRAPHS) \
|
||||
UNICODE_RANGE(0xa000, 1167, YI_SYLLABLES) \
|
||||
UNICODE_RANGE(0xa490, 63, YI_RADICALS) \
|
||||
UNICODE_RANGE(0xac00, 11183, HANGUL_SYLLABLES) \
|
||||
UNICODE_RANGE(0xd800, 1023, HIGH_SURROGATE_AREA) \
|
||||
UNICODE_RANGE(0xdc00, 1023, LOW_SURROGATE_AREA) \
|
||||
UNICODE_RANGE(0xe000, 6399, PRIVATE_USE_AREA) \
|
||||
UNICODE_RANGE(0xf900, 511, CJK_COMPATIBILITY_IDEOGRAPHS) \
|
||||
UNICODE_RANGE(0xfb00, 79, ALPHABETIC_PRESENTATION_FORMS) \
|
||||
UNICODE_RANGE(0xfb50, 687, ARABIC_PRESENTATION_FORMS_A) \
|
||||
UNICODE_RANGE(0xfe00, 15, VARIATION_SELECTORS) \
|
||||
UNICODE_RANGE(0xfe20, 15, COMBINING_HALF_MARKS) \
|
||||
UNICODE_RANGE(0xfe30, 31, CJK_COMPATIBILITY_FORMS) \
|
||||
UNICODE_RANGE(0xfe50, 31, SMALL_FORM_VARIANTS) \
|
||||
UNICODE_RANGE(0xfe70, 143, ARABIC_PRESENTATION_FORMS_B) \
|
||||
UNICODE_RANGE(0xff00, 239, HALFWIDTH_AND_FULLWIDTH_FORMS) \
|
||||
UNICODE_RANGE(0xfff0, 15, SPECIALS) \
|
||||
UNICODE_RANGE(0x10000, 127, LINEAR_B_SYLLABARY) \
|
||||
UNICODE_RANGE(0x10080, 127, LINEAR_B_IDEOGRAMS) \
|
||||
UNICODE_RANGE(0x10100, 63, AEGEAN_NUMBERS) \
|
||||
UNICODE_RANGE(0x10300, 47, OLD_ITALIC) \
|
||||
UNICODE_RANGE(0x10330, 31, GOTHIC) \
|
||||
UNICODE_RANGE(0x10380, 31, UGARITIC) \
|
||||
UNICODE_RANGE(0x10400, 79, DESERET) \
|
||||
UNICODE_RANGE(0x10450, 47, SHAVIAN) \
|
||||
UNICODE_RANGE(0x10480, 47, OSMANYA) \
|
||||
UNICODE_RANGE(0x10800, 63, CYPRIOT_SYLLABARY) \
|
||||
UNICODE_RANGE(0x1d000, 255, BYZANTINE_MUSICAL_SYMBOLS) \
|
||||
UNICODE_RANGE(0x1d100, 255, MUSICAL_SYMBOLS) \
|
||||
UNICODE_RANGE(0x1d300, 95, TAI_XUAN_JING_SYMBOLS) \
|
||||
UNICODE_RANGE(0x1d400, 1023, MATHEMATICAL_ALPHANUMERIC_SYMBOLS) \
|
||||
UNICODE_RANGE(0x20000, 42719, CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B) \
|
||||
UNICODE_RANGE(0x2f800, 543, CJK_COMPATIBILITY_IDEOGRAPHS_SUPPLEMENT) \
|
||||
UNICODE_RANGE(0xe0000, 127, TAGS) \
|
||||
UNICODE_RANGE(0xe0100, 239, VARIATION_SELECTORS_SUPPLEMENT) \
|
||||
UNICODE_RANGE(0xf0000, 65533, SUPPLEMENTARY_PRIVATE_USE_AREA_A) \
|
||||
UNICODE_RANGE(0x100000, 65533, SUPPLEMENTARY_PRIVATE_USE_AREA_B)
|
||||
|
||||
#define UNICODE_RANGE(start, count, name) \
|
||||
MP_API extern const unicode_range _cat2_(UNICODE_RANGE_, name);
|
||||
UNICODE_RANGES
|
||||
#undef UNICODE_RANGE
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif //__UTF8_H_
|
||||
//*****************************************************************
|
||||
//
|
||||
// $file: utf8.h $
|
||||
// $author: Martin Fouilleul $
|
||||
// $date: 05/11/2016 $
|
||||
// $revision: $
|
||||
// $note: (C) 2016 by Martin Fouilleul - all rights reserved $
|
||||
//
|
||||
//*****************************************************************
|
||||
#ifndef __UTF8_H_
|
||||
#define __UTF8_H_
|
||||
|
||||
#include"typedefs.h"
|
||||
#include"macro_helpers.h"
|
||||
#include"strings.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef u32 utf32;
|
||||
|
||||
//-----------------------------------------------------------------
|
||||
//NOTE: getting sizes / offsets / indices
|
||||
//-----------------------------------------------------------------
|
||||
MP_API u32 utf8_size_from_leading_char(char leadingChar);
|
||||
MP_API u32 utf8_codepoint_size(utf32 codePoint);
|
||||
|
||||
MP_API u64 utf8_codepoint_count_for_string(str8 string);
|
||||
MP_API u64 utf8_byte_count_for_codepoints(str32 codePoints);
|
||||
|
||||
MP_API u64 utf8_next_offset(str8 string, u64 byteOffset);
|
||||
MP_API u64 utf8_prev_offset(str8 string, u64 byteOffset);
|
||||
|
||||
//-----------------------------------------------------------------
|
||||
//NOTE: encoding / decoding
|
||||
//-----------------------------------------------------------------
|
||||
typedef struct utf8_dec
|
||||
{
|
||||
utf32 codepoint; //NOTE: decoded codepoint
|
||||
u32 size; //NOTE: size of corresponding utf8 sequence
|
||||
} utf8_dec;
|
||||
|
||||
MP_API utf8_dec utf8_decode(str8 string); //NOTE: decode a single utf8 sequence at start of string
|
||||
MP_API utf8_dec utf8_decode_at(str8 string, u64 offset); //NOTE: decode a single utf8 sequence starting at byte offset
|
||||
MP_API str8 utf8_encode(char* dst, utf32 codePoint); //NOTE: encode codepoint into backing buffer dst
|
||||
|
||||
MP_API str32 utf8_to_codepoints(u64 maxCount, utf32* backing, str8 string);
|
||||
MP_API str8 utf8_from_codepoints(u64 maxBytes, char* backing, str32 codePoints);
|
||||
|
||||
MP_API str32 utf8_push_to_codepoints(mem_arena* arena, str8 string);
|
||||
MP_API str8 utf8_push_from_codepoints(mem_arena* arena, str32 codePoints);
|
||||
|
||||
//-----------------------------------------------------------------
|
||||
// utf8 range struct and X-macros for defining utf8 ranges
|
||||
//-----------------------------------------------------------------
|
||||
|
||||
typedef struct unicode_range
|
||||
{
|
||||
utf32 firstCodePoint;
|
||||
u32 count;
|
||||
} unicode_range;
|
||||
|
||||
//NOTE(martin): range declared here are defined in utf8.cpp
|
||||
// they can be used by prefixing them with UTF8_RANGE_, as in 'UTF8_RANGE_BASIC_LATIN'
|
||||
#define UNICODE_RANGES \
|
||||
UNICODE_RANGE(0x0000, 127, BASIC_LATIN) \
|
||||
UNICODE_RANGE(0x0080, 127, C1_CONTROLS_AND_LATIN_1_SUPPLEMENT) \
|
||||
UNICODE_RANGE(0x0100, 127, LATIN_EXTENDED_A) \
|
||||
UNICODE_RANGE(0x0180, 207, LATIN_EXTENDED_B) \
|
||||
UNICODE_RANGE(0x0250, 95, IPA_EXTENSIONS) \
|
||||
UNICODE_RANGE(0x02b0, 79, SPACING_MODIFIER_LETTERS) \
|
||||
UNICODE_RANGE(0x0300, 111, COMBINING_DIACRITICAL_MARKS) \
|
||||
UNICODE_RANGE(0x0370, 143, GREEK_COPTIC) \
|
||||
UNICODE_RANGE(0x0400, 255, CYRILLIC) \
|
||||
UNICODE_RANGE(0x0500, 47, CYRILLIC_SUPPLEMENT) \
|
||||
UNICODE_RANGE(0x0530, 95, ARMENIAN) \
|
||||
UNICODE_RANGE(0x0590, 111, HEBREW) \
|
||||
UNICODE_RANGE(0x0600, 255, ARABIC) \
|
||||
UNICODE_RANGE(0x0700, 79, SYRIAC) \
|
||||
UNICODE_RANGE(0x0780, 63, THAANA) \
|
||||
UNICODE_RANGE(0x0900, 127, DEVANAGARI) \
|
||||
UNICODE_RANGE(0x0980, 127, BENGALI_ASSAMESE) \
|
||||
UNICODE_RANGE(0x0a00, 127, GURMUKHI) \
|
||||
UNICODE_RANGE(0x0a80, 127, GUJARATI) \
|
||||
UNICODE_RANGE(0x0b00, 127, ORIYA) \
|
||||
UNICODE_RANGE(0x0b80, 127, TAMIL) \
|
||||
UNICODE_RANGE(0x0c00, 127, TELUGU) \
|
||||
UNICODE_RANGE(0x0c80, 127, KANNADA) \
|
||||
UNICODE_RANGE(0x0d00, 255, MALAYALAM) \
|
||||
UNICODE_RANGE(0x0d80, 127, SINHALA) \
|
||||
UNICODE_RANGE(0x0e00, 127, THAI) \
|
||||
UNICODE_RANGE(0x0e80, 127, LAO) \
|
||||
UNICODE_RANGE(0x0f00, 255, TIBETAN) \
|
||||
UNICODE_RANGE(0x1000, 159, MYANMAR) \
|
||||
UNICODE_RANGE(0x10a0, 95, GEORGIAN) \
|
||||
UNICODE_RANGE(0x1100, 255, HANGUL_JAMO) \
|
||||
UNICODE_RANGE(0x1200, 383, ETHIOPIC) \
|
||||
UNICODE_RANGE(0x13a0, 95, CHEROKEE) \
|
||||
UNICODE_RANGE(0x1400, 639, UNIFIED_CANADIAN_ABORIGINAL_SYLLABICS) \
|
||||
UNICODE_RANGE(0x1680, 31, OGHAM) \
|
||||
UNICODE_RANGE(0x16a0, 95, RUNIC) \
|
||||
UNICODE_RANGE(0x1700, 31, TAGALOG) \
|
||||
UNICODE_RANGE(0x1720, 31, HANUNOO) \
|
||||
UNICODE_RANGE(0x1740, 31, BUHID) \
|
||||
UNICODE_RANGE(0x1760, 31, TAGBANWA) \
|
||||
UNICODE_RANGE(0x1780, 127, KHMER) \
|
||||
UNICODE_RANGE(0x1800, 175, MONGOLIAN) \
|
||||
UNICODE_RANGE(0x1900, 79, LIMBU) \
|
||||
UNICODE_RANGE(0x1950, 47, TAI_LE) \
|
||||
UNICODE_RANGE(0x19e0, 31, KHMER_SYMBOLS) \
|
||||
UNICODE_RANGE(0x1d00, 127, PHONETIC_EXTENSIONS) \
|
||||
UNICODE_RANGE(0x1e00, 255, LATIN_EXTENDED_ADDITIONAL) \
|
||||
UNICODE_RANGE(0x1f00, 255, GREEK_EXTENDED) \
|
||||
UNICODE_RANGE(0x2000, 111, GENERAL_PUNCTUATION) \
|
||||
UNICODE_RANGE(0x2070, 47, SUPERSCRIPTS_AND_SUBSCRIPTS) \
|
||||
UNICODE_RANGE(0x20a0, 47, CURRENCY_SYMBOLS) \
|
||||
UNICODE_RANGE(0x20d0, 47, COMBINING_DIACRITICAL_MARKS_FOR_SYMBOLS) \
|
||||
UNICODE_RANGE(0x2100, 79, LETTERLIKE_SYMBOLS) \
|
||||
UNICODE_RANGE(0x2150, 63, NUMBER_FORMS) \
|
||||
UNICODE_RANGE(0x2190, 111, ARROWS) \
|
||||
UNICODE_RANGE(0x2200, 255, MATHEMATICAL_OPERATORS) \
|
||||
UNICODE_RANGE(0x2300, 255, MISCELLANEOUS_TECHNICAL) \
|
||||
UNICODE_RANGE(0x2400, 63, CONTROL_PICTURES) \
|
||||
UNICODE_RANGE(0x2440, 31, OPTICAL_CHARACTER_RECOGNITION) \
|
||||
UNICODE_RANGE(0x2460, 159, ENCLOSED_ALPHANUMERICS) \
|
||||
UNICODE_RANGE(0x2500, 127, BOX_DRAWING) \
|
||||
UNICODE_RANGE(0x2580, 31, BLOCK_ELEMENTS) \
|
||||
UNICODE_RANGE(0x25a0, 95, GEOMETRIC_SHAPES) \
|
||||
UNICODE_RANGE(0x2600, 255, MISCELLANEOUS_SYMBOLS) \
|
||||
UNICODE_RANGE(0x2700, 191, DINGBATS) \
|
||||
UNICODE_RANGE(0x27c0, 47, MISCELLANEOUS_MATHEMATICAL_SYMBOLS_A) \
|
||||
UNICODE_RANGE(0x27f0, 15, SUPPLEMENTAL_ARROWS_A) \
|
||||
UNICODE_RANGE(0x2800, 255, BRAILLE_PATTERNS) \
|
||||
UNICODE_RANGE(0x2900, 127, SUPPLEMENTAL_ARROWS_B) \
|
||||
UNICODE_RANGE(0x2980, 127, MISCELLANEOUS_MATHEMATICAL_SYMBOLS_B) \
|
||||
UNICODE_RANGE(0x2a00, 255, SUPPLEMENTAL_MATHEMATICAL_OPERATORS) \
|
||||
UNICODE_RANGE(0x2b00, 255, MISCELLANEOUS_SYMBOLS_AND_ARROWS) \
|
||||
UNICODE_RANGE(0x2e80, 127, CJK_RADICALS_SUPPLEMENT) \
|
||||
UNICODE_RANGE(0x2f00, 223, KANGXI_RADICALS) \
|
||||
UNICODE_RANGE(0x2ff0, 15, IDEOGRAPHIC_DESCRIPTION_CHARACTERS) \
|
||||
UNICODE_RANGE(0x3000, 63, CJK_SYMBOLS_AND_PUNCTUATION) \
|
||||
UNICODE_RANGE(0x3040, 95, HIRAGANA) \
|
||||
UNICODE_RANGE(0x30a0, 95, KATAKANA) \
|
||||
UNICODE_RANGE(0x3100, 47, BOPOMOFO) \
|
||||
UNICODE_RANGE(0x3130, 95, HANGUL_COMPATIBILITY_JAMO) \
|
||||
UNICODE_RANGE(0x3190, 15, KANBUN_KUNTEN) \
|
||||
UNICODE_RANGE(0x31a0, 31, BOPOMOFO_EXTENDED) \
|
||||
UNICODE_RANGE(0x31f0, 15, KATAKANA_PHONETIC_EXTENSIONS) \
|
||||
UNICODE_RANGE(0x3200, 255, ENCLOSED_CJK_LETTERS_AND_MONTHS) \
|
||||
UNICODE_RANGE(0x3300, 255, CJK_COMPATIBILITY) \
|
||||
UNICODE_RANGE(0x3400, 6591, CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A) \
|
||||
UNICODE_RANGE(0x4dc0, 63, YIJING_HEXAGRAM_SYMBOLS) \
|
||||
UNICODE_RANGE(0x4e00, 20911, CJK_UNIFIED_IDEOGRAPHS) \
|
||||
UNICODE_RANGE(0xa000, 1167, YI_SYLLABLES) \
|
||||
UNICODE_RANGE(0xa490, 63, YI_RADICALS) \
|
||||
UNICODE_RANGE(0xac00, 11183, HANGUL_SYLLABLES) \
|
||||
UNICODE_RANGE(0xd800, 1023, HIGH_SURROGATE_AREA) \
|
||||
UNICODE_RANGE(0xdc00, 1023, LOW_SURROGATE_AREA) \
|
||||
UNICODE_RANGE(0xe000, 6399, PRIVATE_USE_AREA) \
|
||||
UNICODE_RANGE(0xf900, 511, CJK_COMPATIBILITY_IDEOGRAPHS) \
|
||||
UNICODE_RANGE(0xfb00, 79, ALPHABETIC_PRESENTATION_FORMS) \
|
||||
UNICODE_RANGE(0xfb50, 687, ARABIC_PRESENTATION_FORMS_A) \
|
||||
UNICODE_RANGE(0xfe00, 15, VARIATION_SELECTORS) \
|
||||
UNICODE_RANGE(0xfe20, 15, COMBINING_HALF_MARKS) \
|
||||
UNICODE_RANGE(0xfe30, 31, CJK_COMPATIBILITY_FORMS) \
|
||||
UNICODE_RANGE(0xfe50, 31, SMALL_FORM_VARIANTS) \
|
||||
UNICODE_RANGE(0xfe70, 143, ARABIC_PRESENTATION_FORMS_B) \
|
||||
UNICODE_RANGE(0xff00, 239, HALFWIDTH_AND_FULLWIDTH_FORMS) \
|
||||
UNICODE_RANGE(0xfff0, 15, SPECIALS) \
|
||||
UNICODE_RANGE(0x10000, 127, LINEAR_B_SYLLABARY) \
|
||||
UNICODE_RANGE(0x10080, 127, LINEAR_B_IDEOGRAMS) \
|
||||
UNICODE_RANGE(0x10100, 63, AEGEAN_NUMBERS) \
|
||||
UNICODE_RANGE(0x10300, 47, OLD_ITALIC) \
|
||||
UNICODE_RANGE(0x10330, 31, GOTHIC) \
|
||||
UNICODE_RANGE(0x10380, 31, UGARITIC) \
|
||||
UNICODE_RANGE(0x10400, 79, DESERET) \
|
||||
UNICODE_RANGE(0x10450, 47, SHAVIAN) \
|
||||
UNICODE_RANGE(0x10480, 47, OSMANYA) \
|
||||
UNICODE_RANGE(0x10800, 63, CYPRIOT_SYLLABARY) \
|
||||
UNICODE_RANGE(0x1d000, 255, BYZANTINE_MUSICAL_SYMBOLS) \
|
||||
UNICODE_RANGE(0x1d100, 255, MUSICAL_SYMBOLS) \
|
||||
UNICODE_RANGE(0x1d300, 95, TAI_XUAN_JING_SYMBOLS) \
|
||||
UNICODE_RANGE(0x1d400, 1023, MATHEMATICAL_ALPHANUMERIC_SYMBOLS) \
|
||||
UNICODE_RANGE(0x20000, 42719, CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B) \
|
||||
UNICODE_RANGE(0x2f800, 543, CJK_COMPATIBILITY_IDEOGRAPHS_SUPPLEMENT) \
|
||||
UNICODE_RANGE(0xe0000, 127, TAGS) \
|
||||
UNICODE_RANGE(0xe0100, 239, VARIATION_SELECTORS_SUPPLEMENT) \
|
||||
UNICODE_RANGE(0xf0000, 65533, SUPPLEMENTARY_PRIVATE_USE_AREA_A) \
|
||||
UNICODE_RANGE(0x100000, 65533, SUPPLEMENTARY_PRIVATE_USE_AREA_B)
|
||||
|
||||
#define UNICODE_RANGE(start, count, name) \
|
||||
MP_API extern const unicode_range _cat2_(UNICODE_RANGE_, name);
|
||||
UNICODE_RANGES
|
||||
#undef UNICODE_RANGE
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif //__UTF8_H_
|
||||
|
|
|
@ -1,264 +1,264 @@
|
|||
/************************************************************//**
|
||||
*
|
||||
* @file: wgl_surface.c
|
||||
* @author: Martin Fouilleul
|
||||
* @date: 01/08/2022
|
||||
* @revision:
|
||||
*
|
||||
*****************************************************************/
|
||||
#include"win32_app.h"
|
||||
#include"graphics_internal.h"
|
||||
#include"gl_loader.h"
|
||||
|
||||
#include<GL/wglext.h>
|
||||
#include"macro_helpers.h"
|
||||
|
||||
#define WGL_PROC_LIST \
|
||||
WGL_PROC(WGLCHOOSEPIXELFORMATARB, wglChoosePixelFormatARB) \
|
||||
WGL_PROC(WGLCREATECONTEXTATTRIBSARB, wglCreateContextAttribsARB) \
|
||||
WGL_PROC(WGLMAKECONTEXTCURRENTARB, wglMakeContextCurrentARB) \
|
||||
WGL_PROC(WGLSWAPINTERVALEXT, wglSwapIntervalEXT) \
|
||||
|
||||
//NOTE: wgl function pointers declarations
|
||||
|
||||
#define WGL_PROC(type, name) _cat3_(PFN, type, PROC) name = 0;
|
||||
WGL_PROC_LIST
|
||||
#undef WGL_PROC
|
||||
|
||||
//NOTE: wgl loader
|
||||
|
||||
typedef struct wgl_dummy_context
|
||||
{
|
||||
bool init;
|
||||
HWND hWnd;
|
||||
HDC hDC;
|
||||
HGLRC glContext;
|
||||
|
||||
} wgl_dummy_context;
|
||||
|
||||
static wgl_dummy_context __mgWGLDummyContext = {0};
|
||||
|
||||
static void wgl_init()
|
||||
{
|
||||
if(!__mgWGLDummyContext.init)
|
||||
{
|
||||
//NOTE: create a dummy window
|
||||
WNDCLASS windowClass = {.style = CS_OWNDC,
|
||||
.lpfnWndProc = DefWindowProc,
|
||||
.hInstance = GetModuleHandleW(NULL),
|
||||
.lpszClassName = "wgl_helper_window_class",
|
||||
.hCursor = LoadCursor(0, IDC_ARROW)};
|
||||
|
||||
if(!RegisterClass(&windowClass))
|
||||
{
|
||||
//TODO: error
|
||||
goto quit;
|
||||
}
|
||||
|
||||
__mgWGLDummyContext.hWnd = CreateWindow("wgl_helper_window_class",
|
||||
"dummy",
|
||||
WS_OVERLAPPEDWINDOW,
|
||||
0, 0, 100, 100,
|
||||
0, 0, windowClass.hInstance, 0);
|
||||
|
||||
if(!__mgWGLDummyContext.hWnd)
|
||||
{
|
||||
//TODO: error
|
||||
goto quit;
|
||||
}
|
||||
__mgWGLDummyContext.hDC = GetDC(__mgWGLDummyContext.hWnd);
|
||||
|
||||
PIXELFORMATDESCRIPTOR pixelFormatDesc =
|
||||
{
|
||||
sizeof(PIXELFORMATDESCRIPTOR),
|
||||
1,
|
||||
PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, // Flags
|
||||
PFD_TYPE_RGBA, // The kind of framebuffer. RGBA or palette.
|
||||
32, // Colordepth of the framebuffer.
|
||||
0, 0, 0, 0, 0, 0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0, 0, 0, 0,
|
||||
24, // Number of bits for the depthbuffer
|
||||
8, // Number of bits for the stencilbuffer
|
||||
0, // Number of Aux buffers in the framebuffer.
|
||||
PFD_MAIN_PLANE,
|
||||
0,
|
||||
0, 0, 0
|
||||
};
|
||||
|
||||
int pixelFormat = ChoosePixelFormat(__mgWGLDummyContext.hDC, &pixelFormatDesc);
|
||||
SetPixelFormat(__mgWGLDummyContext.hDC, pixelFormat, &pixelFormatDesc);
|
||||
|
||||
__mgWGLDummyContext.glContext = wglCreateContext(__mgWGLDummyContext.hDC);
|
||||
wglMakeCurrent(__mgWGLDummyContext.hDC, __mgWGLDummyContext.glContext);
|
||||
|
||||
//NOTE(martin): now load WGL extension functions
|
||||
#define WGL_PROC(type, name) name = (_cat3_(PFN, type, PROC))wglGetProcAddress( #name );
|
||||
WGL_PROC_LIST
|
||||
#undef WGL_PROC
|
||||
|
||||
__mgWGLDummyContext.init = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
wglMakeCurrent(__mgWGLDummyContext.hDC, __mgWGLDummyContext.glContext);
|
||||
}
|
||||
quit:;
|
||||
}
|
||||
|
||||
#undef WGL_PROC_LIST
|
||||
|
||||
|
||||
typedef struct mg_wgl_surface
|
||||
{
|
||||
mg_surface_data interface;
|
||||
|
||||
HDC hDC;
|
||||
HGLRC glContext;
|
||||
|
||||
//NOTE: this may be a bit wasteful to have one api struct per surface, but win32 docs says that loading procs
|
||||
// from different contexts might select different implementations (eg. depending on context version/pixel format)
|
||||
mg_gl_api api;
|
||||
} mg_wgl_surface;
|
||||
|
||||
void mg_wgl_surface_destroy(mg_surface_data* interface)
|
||||
{
|
||||
mg_wgl_surface* surface = (mg_wgl_surface*)interface;
|
||||
|
||||
if(surface->glContext == wglGetCurrentContext())
|
||||
{
|
||||
wglMakeCurrent(NULL, NULL);
|
||||
}
|
||||
wglDeleteContext(surface->glContext);
|
||||
|
||||
mg_surface_cleanup(interface);
|
||||
|
||||
free(surface);
|
||||
}
|
||||
|
||||
void mg_wgl_surface_prepare(mg_surface_data* interface)
|
||||
{
|
||||
mg_wgl_surface* surface = (mg_wgl_surface*)interface;
|
||||
|
||||
wglMakeCurrent(surface->hDC, surface->glContext);
|
||||
mg_gl_select_api(&surface->api);
|
||||
}
|
||||
|
||||
void mg_wgl_surface_present(mg_surface_data* interface)
|
||||
{
|
||||
mg_wgl_surface* surface = (mg_wgl_surface*)interface;
|
||||
SwapBuffers(surface->hDC);
|
||||
}
|
||||
|
||||
void mg_wgl_surface_swap_interval(mg_surface_data* interface, int swap)
|
||||
{
|
||||
mg_wgl_surface* surface = (mg_wgl_surface*)interface;
|
||||
wglSwapIntervalEXT(swap);
|
||||
}
|
||||
|
||||
void* mg_wgl_get_proc(const char* name)
|
||||
{
|
||||
void* p = wglGetProcAddress(name);
|
||||
if( p == 0
|
||||
|| p == (void*)0x01
|
||||
|| p == (void*)0x02
|
||||
|| p == (void*)0x03
|
||||
|| p == (void*)(-1))
|
||||
{
|
||||
//TODO: should we avoid re-loading every time?
|
||||
HMODULE module = LoadLibrary("opengl32.dll");
|
||||
p = (void*)GetProcAddress(module, name);
|
||||
}
|
||||
return(p);
|
||||
}
|
||||
|
||||
mg_surface_data* mg_wgl_surface_create_for_window(mp_window window)
|
||||
{
|
||||
mg_surface* surface = 0;
|
||||
|
||||
mp_window_data* windowData = mp_window_ptr_from_handle(window);
|
||||
if(windowData)
|
||||
{
|
||||
wgl_init();
|
||||
|
||||
//NOTE: fill surface data and load api
|
||||
mg_wgl_surface* surface = malloc_type(mg_wgl_surface);
|
||||
if(surface)
|
||||
{
|
||||
mg_surface_init_for_window((mg_surface_data*)surface, windowData);
|
||||
|
||||
surface->interface.backend = MG_BACKEND_GL;
|
||||
surface->interface.destroy = mg_wgl_surface_destroy;
|
||||
surface->interface.prepare = mg_wgl_surface_prepare;
|
||||
surface->interface.present = mg_wgl_surface_present;
|
||||
surface->interface.swapInterval = mg_wgl_surface_swap_interval;
|
||||
|
||||
surface->hDC = GetDC(surface->interface.layer.hWnd);
|
||||
|
||||
//NOTE(martin): create the pixel format and gl context
|
||||
PIXELFORMATDESCRIPTOR pixelFormatDesc =
|
||||
{
|
||||
sizeof(PIXELFORMATDESCRIPTOR),
|
||||
1,
|
||||
PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, // Flags
|
||||
PFD_TYPE_RGBA, // The kind of framebuffer. RGBA or palette.
|
||||
32, // Colordepth of the framebuffer.
|
||||
0, 0, 0, 0, 0, 0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0, 0, 0, 0,
|
||||
24, // Number of bits for the depthbuffer
|
||||
8, // Number of bits for the stencilbuffer
|
||||
0, // Number of Aux buffers in the framebuffer.
|
||||
PFD_MAIN_PLANE,
|
||||
0,
|
||||
0, 0, 0
|
||||
};
|
||||
|
||||
int pixelFormatAttrs[] = {
|
||||
WGL_DRAW_TO_WINDOW_ARB, GL_TRUE,
|
||||
WGL_SUPPORT_OPENGL_ARB, GL_TRUE,
|
||||
WGL_DOUBLE_BUFFER_ARB, GL_TRUE,
|
||||
WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB,
|
||||
WGL_COLOR_BITS_ARB, 32,
|
||||
WGL_DEPTH_BITS_ARB, 24,
|
||||
WGL_STENCIL_BITS_ARB, 8,
|
||||
0};
|
||||
|
||||
u32 numFormats = 0;
|
||||
int pixelFormat = 0;
|
||||
|
||||
wglChoosePixelFormatARB(surface->hDC, pixelFormatAttrs, 0, 1, &pixelFormat, &numFormats);
|
||||
|
||||
if(!pixelFormat)
|
||||
{
|
||||
//TODO: error
|
||||
}
|
||||
SetPixelFormat(surface->hDC, pixelFormat, &pixelFormatDesc);
|
||||
|
||||
int contextAttrs[] = {
|
||||
WGL_CONTEXT_MAJOR_VERSION_ARB, 4,
|
||||
WGL_CONTEXT_MINOR_VERSION_ARB, 3,
|
||||
WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
|
||||
0};
|
||||
|
||||
surface->glContext = wglCreateContextAttribsARB(surface->hDC, __mgWGLDummyContext.glContext, contextAttrs);
|
||||
|
||||
if(!surface->glContext)
|
||||
{
|
||||
//TODO error
|
||||
int error = GetLastError();
|
||||
printf("error: %i\n", error);
|
||||
}
|
||||
|
||||
//NOTE: make gl context current and load api
|
||||
wglMakeCurrent(surface->hDC, surface->glContext);
|
||||
wglSwapIntervalEXT(1);
|
||||
mg_gl_load_gl43(&surface->api, mg_wgl_get_proc);
|
||||
}
|
||||
}
|
||||
return((mg_surface_data*)surface);
|
||||
}
|
||||
/************************************************************//**
|
||||
*
|
||||
* @file: wgl_surface.c
|
||||
* @author: Martin Fouilleul
|
||||
* @date: 01/08/2022
|
||||
* @revision:
|
||||
*
|
||||
*****************************************************************/
|
||||
#include"win32_app.h"
|
||||
#include"graphics_internal.h"
|
||||
#include"gl_loader.h"
|
||||
|
||||
#include<GL/wglext.h>
|
||||
#include"macro_helpers.h"
|
||||
|
||||
#define WGL_PROC_LIST \
|
||||
WGL_PROC(WGLCHOOSEPIXELFORMATARB, wglChoosePixelFormatARB) \
|
||||
WGL_PROC(WGLCREATECONTEXTATTRIBSARB, wglCreateContextAttribsARB) \
|
||||
WGL_PROC(WGLMAKECONTEXTCURRENTARB, wglMakeContextCurrentARB) \
|
||||
WGL_PROC(WGLSWAPINTERVALEXT, wglSwapIntervalEXT) \
|
||||
|
||||
//NOTE: wgl function pointers declarations
|
||||
|
||||
#define WGL_PROC(type, name) _cat3_(PFN, type, PROC) name = 0;
|
||||
WGL_PROC_LIST
|
||||
#undef WGL_PROC
|
||||
|
||||
//NOTE: wgl loader
|
||||
|
||||
typedef struct wgl_dummy_context
|
||||
{
|
||||
bool init;
|
||||
HWND hWnd;
|
||||
HDC hDC;
|
||||
HGLRC glContext;
|
||||
|
||||
} wgl_dummy_context;
|
||||
|
||||
static wgl_dummy_context __mgWGLDummyContext = {0};
|
||||
|
||||
static void wgl_init()
|
||||
{
|
||||
if(!__mgWGLDummyContext.init)
|
||||
{
|
||||
//NOTE: create a dummy window
|
||||
WNDCLASS windowClass = {.style = CS_OWNDC,
|
||||
.lpfnWndProc = DefWindowProc,
|
||||
.hInstance = GetModuleHandleW(NULL),
|
||||
.lpszClassName = "wgl_helper_window_class",
|
||||
.hCursor = LoadCursor(0, IDC_ARROW)};
|
||||
|
||||
if(!RegisterClass(&windowClass))
|
||||
{
|
||||
//TODO: error
|
||||
goto quit;
|
||||
}
|
||||
|
||||
__mgWGLDummyContext.hWnd = CreateWindow("wgl_helper_window_class",
|
||||
"dummy",
|
||||
WS_OVERLAPPEDWINDOW,
|
||||
0, 0, 100, 100,
|
||||
0, 0, windowClass.hInstance, 0);
|
||||
|
||||
if(!__mgWGLDummyContext.hWnd)
|
||||
{
|
||||
//TODO: error
|
||||
goto quit;
|
||||
}
|
||||
__mgWGLDummyContext.hDC = GetDC(__mgWGLDummyContext.hWnd);
|
||||
|
||||
PIXELFORMATDESCRIPTOR pixelFormatDesc =
|
||||
{
|
||||
sizeof(PIXELFORMATDESCRIPTOR),
|
||||
1,
|
||||
PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, // Flags
|
||||
PFD_TYPE_RGBA, // The kind of framebuffer. RGBA or palette.
|
||||
32, // Colordepth of the framebuffer.
|
||||
0, 0, 0, 0, 0, 0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0, 0, 0, 0,
|
||||
24, // Number of bits for the depthbuffer
|
||||
8, // Number of bits for the stencilbuffer
|
||||
0, // Number of Aux buffers in the framebuffer.
|
||||
PFD_MAIN_PLANE,
|
||||
0,
|
||||
0, 0, 0
|
||||
};
|
||||
|
||||
int pixelFormat = ChoosePixelFormat(__mgWGLDummyContext.hDC, &pixelFormatDesc);
|
||||
SetPixelFormat(__mgWGLDummyContext.hDC, pixelFormat, &pixelFormatDesc);
|
||||
|
||||
__mgWGLDummyContext.glContext = wglCreateContext(__mgWGLDummyContext.hDC);
|
||||
wglMakeCurrent(__mgWGLDummyContext.hDC, __mgWGLDummyContext.glContext);
|
||||
|
||||
//NOTE(martin): now load WGL extension functions
|
||||
#define WGL_PROC(type, name) name = (_cat3_(PFN, type, PROC))wglGetProcAddress( #name );
|
||||
WGL_PROC_LIST
|
||||
#undef WGL_PROC
|
||||
|
||||
__mgWGLDummyContext.init = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
wglMakeCurrent(__mgWGLDummyContext.hDC, __mgWGLDummyContext.glContext);
|
||||
}
|
||||
quit:;
|
||||
}
|
||||
|
||||
#undef WGL_PROC_LIST
|
||||
|
||||
|
||||
typedef struct mg_wgl_surface
|
||||
{
|
||||
mg_surface_data interface;
|
||||
|
||||
HDC hDC;
|
||||
HGLRC glContext;
|
||||
|
||||
//NOTE: this may be a bit wasteful to have one api struct per surface, but win32 docs says that loading procs
|
||||
// from different contexts might select different implementations (eg. depending on context version/pixel format)
|
||||
mg_gl_api api;
|
||||
} mg_wgl_surface;
|
||||
|
||||
void mg_wgl_surface_destroy(mg_surface_data* interface)
|
||||
{
|
||||
mg_wgl_surface* surface = (mg_wgl_surface*)interface;
|
||||
|
||||
if(surface->glContext == wglGetCurrentContext())
|
||||
{
|
||||
wglMakeCurrent(NULL, NULL);
|
||||
}
|
||||
wglDeleteContext(surface->glContext);
|
||||
|
||||
mg_surface_cleanup(interface);
|
||||
|
||||
free(surface);
|
||||
}
|
||||
|
||||
void mg_wgl_surface_prepare(mg_surface_data* interface)
|
||||
{
|
||||
mg_wgl_surface* surface = (mg_wgl_surface*)interface;
|
||||
|
||||
wglMakeCurrent(surface->hDC, surface->glContext);
|
||||
mg_gl_select_api(&surface->api);
|
||||
}
|
||||
|
||||
void mg_wgl_surface_present(mg_surface_data* interface)
|
||||
{
|
||||
mg_wgl_surface* surface = (mg_wgl_surface*)interface;
|
||||
SwapBuffers(surface->hDC);
|
||||
}
|
||||
|
||||
void mg_wgl_surface_swap_interval(mg_surface_data* interface, int swap)
|
||||
{
|
||||
mg_wgl_surface* surface = (mg_wgl_surface*)interface;
|
||||
wglSwapIntervalEXT(swap);
|
||||
}
|
||||
|
||||
void* mg_wgl_get_proc(const char* name)
|
||||
{
|
||||
void* p = wglGetProcAddress(name);
|
||||
if( p == 0
|
||||
|| p == (void*)0x01
|
||||
|| p == (void*)0x02
|
||||
|| p == (void*)0x03
|
||||
|| p == (void*)(-1))
|
||||
{
|
||||
//TODO: should we avoid re-loading every time?
|
||||
HMODULE module = LoadLibrary("opengl32.dll");
|
||||
p = (void*)GetProcAddress(module, name);
|
||||
}
|
||||
return(p);
|
||||
}
|
||||
|
||||
mg_surface_data* mg_wgl_surface_create_for_window(mp_window window)
|
||||
{
|
||||
mg_surface* surface = 0;
|
||||
|
||||
mp_window_data* windowData = mp_window_ptr_from_handle(window);
|
||||
if(windowData)
|
||||
{
|
||||
wgl_init();
|
||||
|
||||
//NOTE: fill surface data and load api
|
||||
mg_wgl_surface* surface = malloc_type(mg_wgl_surface);
|
||||
if(surface)
|
||||
{
|
||||
mg_surface_init_for_window((mg_surface_data*)surface, windowData);
|
||||
|
||||
surface->interface.backend = MG_BACKEND_GL;
|
||||
surface->interface.destroy = mg_wgl_surface_destroy;
|
||||
surface->interface.prepare = mg_wgl_surface_prepare;
|
||||
surface->interface.present = mg_wgl_surface_present;
|
||||
surface->interface.swapInterval = mg_wgl_surface_swap_interval;
|
||||
|
||||
surface->hDC = GetDC(surface->interface.layer.hWnd);
|
||||
|
||||
//NOTE(martin): create the pixel format and gl context
|
||||
PIXELFORMATDESCRIPTOR pixelFormatDesc =
|
||||
{
|
||||
sizeof(PIXELFORMATDESCRIPTOR),
|
||||
1,
|
||||
PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, // Flags
|
||||
PFD_TYPE_RGBA, // The kind of framebuffer. RGBA or palette.
|
||||
32, // Colordepth of the framebuffer.
|
||||
0, 0, 0, 0, 0, 0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0, 0, 0, 0,
|
||||
24, // Number of bits for the depthbuffer
|
||||
8, // Number of bits for the stencilbuffer
|
||||
0, // Number of Aux buffers in the framebuffer.
|
||||
PFD_MAIN_PLANE,
|
||||
0,
|
||||
0, 0, 0
|
||||
};
|
||||
|
||||
int pixelFormatAttrs[] = {
|
||||
WGL_DRAW_TO_WINDOW_ARB, GL_TRUE,
|
||||
WGL_SUPPORT_OPENGL_ARB, GL_TRUE,
|
||||
WGL_DOUBLE_BUFFER_ARB, GL_TRUE,
|
||||
WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB,
|
||||
WGL_COLOR_BITS_ARB, 32,
|
||||
WGL_DEPTH_BITS_ARB, 24,
|
||||
WGL_STENCIL_BITS_ARB, 8,
|
||||
0};
|
||||
|
||||
u32 numFormats = 0;
|
||||
int pixelFormat = 0;
|
||||
|
||||
wglChoosePixelFormatARB(surface->hDC, pixelFormatAttrs, 0, 1, &pixelFormat, &numFormats);
|
||||
|
||||
if(!pixelFormat)
|
||||
{
|
||||
//TODO: error
|
||||
}
|
||||
SetPixelFormat(surface->hDC, pixelFormat, &pixelFormatDesc);
|
||||
|
||||
int contextAttrs[] = {
|
||||
WGL_CONTEXT_MAJOR_VERSION_ARB, 4,
|
||||
WGL_CONTEXT_MINOR_VERSION_ARB, 3,
|
||||
WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
|
||||
0};
|
||||
|
||||
surface->glContext = wglCreateContextAttribsARB(surface->hDC, __mgWGLDummyContext.glContext, contextAttrs);
|
||||
|
||||
if(!surface->glContext)
|
||||
{
|
||||
//TODO error
|
||||
int error = GetLastError();
|
||||
printf("error: %i\n", error);
|
||||
}
|
||||
|
||||
//NOTE: make gl context current and load api
|
||||
wglMakeCurrent(surface->hDC, surface->glContext);
|
||||
wglSwapIntervalEXT(1);
|
||||
mg_gl_load_gl43(&surface->api, mg_wgl_get_proc);
|
||||
}
|
||||
}
|
||||
return((mg_surface_data*)surface);
|
||||
}
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
/************************************************************//**
|
||||
*
|
||||
* @file: wgl_surface.c
|
||||
* @author: Martin Fouilleul
|
||||
* @date: 28/01/2023
|
||||
* @revision:
|
||||
*
|
||||
*****************************************************************/
|
||||
#ifndef __WGL_SURFACE_H_
|
||||
#define __WGL_SURFACE_H_
|
||||
|
||||
#include"graphics_internal.h"
|
||||
|
||||
mg_surface_data* mg_wgl_surface_create_for_window(mp_window window);
|
||||
|
||||
#endif // __WIN32_GL_SURFACE_H_
|
||||
/************************************************************//**
|
||||
*
|
||||
* @file: wgl_surface.c
|
||||
* @author: Martin Fouilleul
|
||||
* @date: 28/01/2023
|
||||
* @revision:
|
||||
*
|
||||
*****************************************************************/
|
||||
#ifndef __WGL_SURFACE_H_
|
||||
#define __WGL_SURFACE_H_
|
||||
|
||||
#include"graphics_internal.h"
|
||||
|
||||
mg_surface_data* mg_wgl_surface_create_for_window(mp_window window);
|
||||
|
||||
#endif // __WIN32_GL_SURFACE_H_
|
||||
|
|
2024
src/win32_app.c
2024
src/win32_app.c
File diff suppressed because it is too large
Load Diff
|
@ -1,43 +1,43 @@
|
|||
//*****************************************************************
|
||||
//
|
||||
// $file: win32_app.h $
|
||||
// $author: Martin Fouilleul $
|
||||
// $date: 20/12/2022 $
|
||||
// $revision: $
|
||||
// $note: (C) 2022 by Martin Fouilleul - all rights reserved $
|
||||
//
|
||||
//*****************************************************************
|
||||
#ifndef __WIN32_APP_H_
|
||||
#define __WIN32_APP_H_
|
||||
|
||||
#include"mp_app.h"
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#define UNICODE
|
||||
#include<windows.h>
|
||||
|
||||
|
||||
typedef struct win32_window_data
|
||||
{
|
||||
HWND hWnd;
|
||||
} win32_window_data;
|
||||
|
||||
typedef struct mp_layer
|
||||
{
|
||||
HWND hWnd;
|
||||
} mp_layer;
|
||||
|
||||
#define MP_PLATFORM_WINDOW_DATA win32_window_data win32;
|
||||
|
||||
typedef struct win32_app_data
|
||||
{
|
||||
u32 savedConsoleCodePage;
|
||||
|
||||
int mouseCaptureMask;
|
||||
bool mouseTracked;
|
||||
|
||||
} win32_app_data;
|
||||
|
||||
#define MP_PLATFORM_APP_DATA win32_app_data win32;
|
||||
|
||||
#endif __WIN32_APP_H_
|
||||
//*****************************************************************
|
||||
//
|
||||
// $file: win32_app.h $
|
||||
// $author: Martin Fouilleul $
|
||||
// $date: 20/12/2022 $
|
||||
// $revision: $
|
||||
// $note: (C) 2022 by Martin Fouilleul - all rights reserved $
|
||||
//
|
||||
//*****************************************************************
|
||||
#ifndef __WIN32_APP_H_
|
||||
#define __WIN32_APP_H_
|
||||
|
||||
#include"mp_app.h"
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#define UNICODE
|
||||
#include<windows.h>
|
||||
|
||||
|
||||
typedef struct win32_window_data
|
||||
{
|
||||
HWND hWnd;
|
||||
} win32_window_data;
|
||||
|
||||
typedef struct mp_layer
|
||||
{
|
||||
HWND hWnd;
|
||||
} mp_layer;
|
||||
|
||||
#define MP_PLATFORM_WINDOW_DATA win32_window_data win32;
|
||||
|
||||
typedef struct win32_app_data
|
||||
{
|
||||
u32 savedConsoleCodePage;
|
||||
|
||||
int mouseCaptureMask;
|
||||
bool mouseTracked;
|
||||
|
||||
} win32_app_data;
|
||||
|
||||
#define MP_PLATFORM_APP_DATA win32_app_data win32;
|
||||
|
||||
#endif __WIN32_APP_H_
|
||||
|
|
Loading…
Reference in New Issue