Fixed indexing in gles_canvas_fragment shaders and fixed native keys buffer

This commit is contained in:
martinfouilleul 2023-02-01 16:23:51 +01:00
parent 8e87837fcc
commit e0300e9e3c
23 changed files with 6835 additions and 6454 deletions

View File

@ -1,6 +1,8 @@
if not exist bin mkdir bin if not exist bin mkdir bin
call python scripts\embed_text.py src\gles_canvas_shaders\gles_canvas_fragment.glsl src\gles_canvas_shaders\gles_canvas_vertex.glsl --output src\gles_canvas_shaders.h
set INCLUDES=/I src /I src/util /I src/platform /I ext /I ext/angle_headers set INCLUDES=/I src /I src/util /I src/platform /I ext /I ext/angle_headers
cl /we4013 /Zi /Zc:preprocessor /std:c11 %INCLUDES% /c /Fo:bin/milepost.obj src/milepost.c cl /we4013 /Zi /Zc:preprocessor /DMG_IMPLEMENTS_BACKEND_GLES /std:c11 %INCLUDES% /c /Fo:bin/milepost.obj src/milepost.c
lib bin/milepost.obj /OUT:bin/milepost.lib lib bin/milepost.obj /OUT:bin/milepost.lib

View File

@ -0,0 +1,3 @@
set INCLUDES=/I ..\..\src /I ..\..\src\util /I ..\..\src\platform /I ../../ext /I ../../ext/angle_headers
cl /we4013 /Zi /Zc:preprocessor /DMG_IMPLEMENTS_BACKEND_GLES /std:c11 %INCLUDES% main.c /link /LIBPATH:../../bin milepost.lib /LIBPATH:../../bin libEGL.dll.lib libGLESv2.dll.lib user32.lib opengl32.lib gdi32.lib /out:../../bin/example_canvas.exe

View File

@ -1,144 +1,153 @@
/************************************************************//** /************************************************************//**
* *
* @file: main.cpp * @file: main.cpp
* @author: Martin Fouilleul * @author: Martin Fouilleul
* @date: 30/07/2022 * @date: 30/07/2022
* @revision: * @revision:
* *
*****************************************************************/ *****************************************************************/
#include<stdlib.h> #include<stdlib.h>
#include<string.h> #include<string.h>
#define _USE_MATH_DEFINES //NOTE: necessary for MSVC #define _USE_MATH_DEFINES //NOTE: necessary for MSVC
#include<math.h> #include<math.h>
#include"milepost.h" #include"milepost.h"
#include"metal_surface.h"
#define LOG_SUBSYSTEM "Main"
#define LOG_SUBSYSTEM "Main"
int main()
int main() {
{ LogLevel(LOG_LEVEL_DEBUG);
LogLevel(LOG_LEVEL_DEBUG);
mp_init();
mp_init();
mp_rect rect = {.x = 100, .y = 100, .w = 800, .h = 600};
mp_rect rect = {.x = 100, .y = 100, .w = 800, .h = 600}; mp_window window = mp_window_create(rect, "test", 0);
mp_window window = mp_window_create(rect, "test", 0);
//NOTE: create surface
//NOTE: create surface #if defined(OS_MACOS)
mg_surface surface = mg_metal_surface_create_for_window(window); mg_surface surface = mg_metal_surface_create_for_window(window);
#elif defined(OS_WIN64)
//TODO: create canvas mg_surface surface = mg_gles_surface_create_for_window(window);
mg_canvas canvas = mg_canvas_create(surface); #else
#error "unsupported OS"
// start app #endif
mp_window_bring_to_front(window);
mp_window_focus(window); //TODO: create canvas
mg_canvas canvas = mg_canvas_create(surface);
while(!mp_should_quit())
{ // start app
mp_pump_events(0); mp_window_bring_to_front(window);
mp_event event = {0}; mp_window_focus(window);
while(mp_next_event(&event))
{ while(!mp_should_quit())
switch(event.type) {
{ mp_pump_events(0);
case MP_EVENT_WINDOW_CLOSE: mp_event event = {0};
{ while(mp_next_event(&event))
mp_request_quit(); {
} break; switch(event.type)
{
case MP_EVENT_WINDOW_RESIZE: case MP_EVENT_WINDOW_CLOSE:
{ {
printf("resized, rect = {%f, %f, %f, %f}\n", mp_request_quit();
event.frame.rect.x, } break;
event.frame.rect.y,
event.frame.rect.w, case MP_EVENT_WINDOW_RESIZE:
event.frame.rect.h); {
} break; printf("resized, rect = {%f, %f, %f, %f}\n",
event.frame.rect.x,
case MP_EVENT_WINDOW_MOVE: event.frame.rect.y,
{ event.frame.rect.w,
printf("moved, rect = {%f, %f, %f, %f}\n", event.frame.rect.h);
event.frame.rect.x, } break;
event.frame.rect.y,
event.frame.rect.w, case MP_EVENT_WINDOW_MOVE:
event.frame.rect.h); {
} break; printf("moved, rect = {%f, %f, %f, %f}\n",
event.frame.rect.x,
case MP_EVENT_MOUSE_MOVE: event.frame.rect.y,
{ event.frame.rect.w,
printf("mouse moved, pos = {%f, %f}, delta = {%f, %f}\n", event.frame.rect.h);
event.move.x, } break;
event.move.y,
event.move.deltaX, case MP_EVENT_MOUSE_MOVE:
event.move.deltaY); {
} break; printf("mouse moved, pos = {%f, %f}, delta = {%f, %f}\n",
event.move.x,
case MP_EVENT_MOUSE_WHEEL: event.move.y,
{ event.move.deltaX,
printf("mouse wheel, delta = {%f, %f}\n", event.move.deltaY);
event.move.deltaX, } break;
event.move.deltaY);
} break; case MP_EVENT_MOUSE_WHEEL:
{
case MP_EVENT_MOUSE_ENTER: printf("mouse wheel, delta = {%f, %f}\n",
{ event.move.deltaX,
printf("mouse enter\n"); event.move.deltaY);
} break; } break;
case MP_EVENT_MOUSE_LEAVE: case MP_EVENT_MOUSE_ENTER:
{ {
printf("mouse leave\n"); printf("mouse enter\n");
} break; } break;
case MP_EVENT_MOUSE_BUTTON: case MP_EVENT_MOUSE_LEAVE:
{ {
printf("mouse button %i: %i\n", printf("mouse leave\n");
event.key.code, } break;
event.key.action == MP_KEY_PRESS ? 1 : 0);
} break; case MP_EVENT_MOUSE_BUTTON:
{
case MP_EVENT_KEYBOARD_KEY: printf("mouse button %i: %i\n",
{ event.key.code,
printf("key %i: %s\n", event.key.action == MP_KEY_PRESS ? 1 : 0);
event.key.code, } break;
event.key.action == MP_KEY_PRESS ? "press" : (event.key.action == MP_KEY_RELEASE ? "release" : "repeat"));
} break; case MP_EVENT_KEYBOARD_KEY:
{
case MP_EVENT_KEYBOARD_CHAR: printf("key %i: %s\n",
{ event.key.code,
printf("entered char %s\n", event.character.sequence); event.key.action == MP_KEY_PRESS ? "press" : (event.key.action == MP_KEY_RELEASE ? "release" : "repeat"));
} break; } break;
default: case MP_EVENT_KEYBOARD_CHAR:
break; {
} printf("entered char %s\n", event.character.sequence);
} } break;
mg_surface_prepare(surface); default:
mg_set_color_rgba(1, 0, 1, 1); break;
mg_clear(); }
}
mg_set_color_rgba(1, 1, 0, 1);
mg_circle_fill(400, 300, 200); mg_surface_prepare(surface);
// background
mg_set_color_rgba(0, 0, 0, 1); mg_set_color_rgba(1, 0, 1, 1);
mg_set_width(20); mg_clear();
mg_move_to(300, 200); // head
mg_cubic_to(350, 150, 450, 150, 500, 200); mg_set_color_rgba(1, 1, 0, 1);
mg_stroke(); mg_circle_fill(400, 300, 200);
mg_ellipse_fill(330, 350, 30, 50); // smile
mg_ellipse_fill(470, 350, 30, 50); mg_set_color_rgba(1, 0, 0, 1);
mg_flush(); mg_set_width(20);
mg_surface_present(surface); mg_move_to(300, 200);
} mg_cubic_to(350, 150, 450, 150, 500, 200);
mg_stroke();
mp_terminate();
// eyes
return(0); mg_ellipse_fill(330, 350, 30, 50);
} mg_ellipse_fill(470, 350, 30, 50);
mg_flush();
mg_surface_present(surface);
}
mp_terminate();
return(0);
}

View File

@ -1,2 +1,2 @@
set INCLUDES=/I ..\..\src /I ..\..\src\util /I ..\..\src\platform /I ../../ext /I ../../ext/angle_headers 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.lib /LIBPATH:./ libEGL.dll.lib libGLESv2.dll.lib user32.lib opengl32.lib gdi32.lib /out:test.exe cl /we4013 /Zi /Zc:preprocessor /std:c11 %INCLUDES% main.c /link /LIBPATH:../../bin milepost.lib /LIBPATH:../../bin libEGL.dll.lib libGLESv2.dll.lib user32.lib opengl32.lib gdi32.lib /out:../../bin/example_gles_triangle.exe

View File

@ -0,0 +1,3 @@
set INCLUDES=/I ..\..\src /I ..\..\src\util /I ..\..\src\platform /I ../../ext /I ../../ext/angle_headers
cl /we4013 /Zi /Zc:preprocessor /DMG_IMPLEMENTS_BACKEND_GLES /std:c11 %INCLUDES% main.c /link /LIBPATH:../../bin milepost.lib /LIBPATH:../../bin libEGL.dll.lib libGLESv2.dll.lib user32.lib opengl32.lib gdi32.lib /out:../../bin/example_canvas.exe

View File

@ -0,0 +1,11 @@
#!/bin/bash
BINDIR=../../bin
RESDIR=../../resources
SRCDIR=../../src
INCLUDES="-I$SRCDIR -I$SRCDIR/util -I$SRCDIR/platform -I$SRCDIR/app"
LIBS="-L$BINDIR -lmilepost -framework Carbon -framework Cocoa -framework Metal -framework QuartzCore"
FLAGS="-mmacos-version-min=10.15.4 -DDEBUG -DLOG_COMPILE_DEBUG -Wl,-dead_strip"
clang -g $FLAGS $LIBS $INCLUDES -o $BINDIR/example_canvas main.c

136
examples/win_canvas/main.c Normal file
View File

@ -0,0 +1,136 @@
/************************************************************//**
*
* @file: main.cpp
* @author: Martin Fouilleul
* @date: 30/07/2022
* @revision:
*
*****************************************************************/
#include<stdlib.h>
#include<string.h>
#define _USE_MATH_DEFINES //NOTE: necessary for MSVC
#include<math.h>
#include"milepost.h"
#define LOG_SUBSYSTEM "Main"
int main()
{
LogLevel(LOG_LEVEL_DEBUG);
mp_init();
mp_rect rect = {.x = 100, .y = 100, .w = 800, .h = 600};
mp_window window = mp_window_create(rect, "test", 0);
//NOTE: create surface
#if defined(OS_MACOS)
mg_surface surface = mg_metal_surface_create_for_window(window);
#elif defined(OS_WIN64)
mg_surface surface = mg_gles_surface_create_for_window(window);
#else
#error "unsupported OS"
#endif
//TODO: create canvas
mg_canvas canvas = mg_canvas_create(surface);
// start app
mp_window_bring_to_front(window);
mp_window_focus(window);
f32 dx = 17.000029, dy = 0;
while(!mp_should_quit())
{
mp_pump_events(0);
mp_event event = {0};
while(mp_next_event(&event))
{
switch(event.type)
{
case MP_EVENT_WINDOW_CLOSE:
{
mp_request_quit();
} break;
case MP_EVENT_WINDOW_RESIZE:
{
printf("resized, rect = {%f, %f, %f, %f}\n",
event.frame.rect.x,
event.frame.rect.y,
event.frame.rect.w,
event.frame.rect.h);
} break;
case MP_EVENT_KEYBOARD_KEY:
{
printf("key %i: %s\n",
event.key.code,
event.key.action == MP_KEY_PRESS ? "press" : (event.key.action == MP_KEY_RELEASE ? "release" : "repeat"));
if(event.key.action == MP_KEY_PRESS || event.key.action == MP_KEY_REPEAT)
{
if(event.key.code == MP_KEY_LEFT)
{
printf("left\n");
dx-=0.1;
}
else if(event.key.code == MP_KEY_RIGHT)
{
printf("right\n");
dx+=0.1;
}
else if(event.key.code == MP_KEY_UP)
{
printf("up\n");
dy+=0.1;
}
else if(event.key.code == MP_KEY_DOWN)
{
printf("down\n");
dy-=0.1;
}
}
} break;
default:
break;
}
}
mg_surface_prepare(surface);
printf("dx = %f, dy = %f\n", dx, dy);
// background
mg_set_color_rgba(1, 0, 1, 1);
mg_clear();
/*
// head
mg_set_color_rgba(1, 1, 0, 1);
mg_circle_fill(dx+400, dy+300, 200);
// smile
mg_set_color_rgba(0, 0, 0, 1);
mg_set_width(20);
mg_move_to(dx+300, dy+200);
mg_cubic_to(dx+350, dy+150, dx+450, dy+150, dx+500, dy+200);
mg_stroke();
// eyes
mg_ellipse_fill(dx+330, dy+350, 30, 50);
mg_ellipse_fill(dx+470, dy+350, 30, 50);
*/
mg_rectangle_fill((int)(dx + 200), 200, (int)(dy+300), (int)(dy+300));
mg_flush();
mg_surface_present(surface);
}
mp_terminate();
return(0);
}

View File

@ -38,41 +38,19 @@ mg_gles_surface* mg_gles_canvas_get_surface(mg_gles_canvas_backend* canvas)
return(res); return(res);
} }
void mg_gles_canvas_draw_buffers(mg_canvas_backend* interface, u32 vertexCount, u32 indexCount, mg_color clearColor) //NOTE: debugger
typedef struct debug_vertex
{ {
mg_gles_canvas_backend* backend = (mg_gles_canvas_backend*)interface; vec2 pos;
mg_gles_surface* surface = mg_gles_canvas_get_surface(backend); u8 align0[8];
if(!surface) vec4 cubic;
{ vec2 uv;
return; u8 align1[8];
} vec4 color;
vec4 clip;
//WARN: dummy test code int zIndex;
u8 align2[12];
indexCount = 3; } debug_vertex;
*(vec2*)(interface->vertexLayout.posBuffer) = (vec2){400, 300};
*(vec2*)(interface->vertexLayout.posBuffer + interface->vertexLayout.posStride) = (vec2){450, 300};
*(vec2*)(interface->vertexLayout.posBuffer + 2*interface->vertexLayout.posStride) = (vec2){400, 350};
for(int i=0; i<3; i++)
{
*(vec4*)(interface->vertexLayout.cubicBuffer + i*interface->vertexLayout.cubicStride) = (vec4){1, 1, 1, 1};
*(vec2*)(interface->vertexLayout.uvBuffer + i*interface->vertexLayout.uvStride) = (vec2){0, 0};
*(vec4*)(interface->vertexLayout.colorBuffer + i*interface->vertexLayout.colorStride) = (vec4){1, 0, 0, 1};
*(vec4*)(interface->vertexLayout.clipBuffer + i*interface->vertexLayout.clipStride) = (vec4){-FLT_MAX/2, -FLT_MAX/2, FLT_MAX, FLT_MAX};
*(u32*)(interface->vertexLayout.zIndexBuffer + i*interface->vertexLayout.zIndexStride) = 1;
*(u32*)(interface->vertexLayout.indexBuffer + i*interface->vertexLayout.indexStride) = i;
}
// end dummy test code
glUseProgram(backend->program);
glBindBuffer(GL_ARRAY_BUFFER, backend->dummyVertexBuffer);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, backend->vertexBuffer);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, backend->indexBuffer);
glUniform1i(0, indexCount);
glDrawArrays(GL_TRIANGLES, 0, 6);
}
#define LayoutNext(prevName, prevType, nextType) \ #define LayoutNext(prevName, prevType, nextType) \
AlignUpOnPow2(_cat3_(LAYOUT_, prevName, _OFFSET)+_cat3_(LAYOUT_, prevType, _SIZE), _cat3_(LAYOUT_, nextType, _ALIGN)) AlignUpOnPow2(_cat3_(LAYOUT_, prevName, _OFFSET)+_cat3_(LAYOUT_, prevType, _SIZE), _cat3_(LAYOUT_, nextType, _ALIGN))
@ -104,17 +82,9 @@ enum {
void mg_gles_canvas_update_vertex_layout(mg_gles_canvas_backend* backend) void mg_gles_canvas_update_vertex_layout(mg_gles_canvas_backend* backend)
{ {
if(backend->vertexMapping)
{
glUnmapBuffer(backend->vertexBuffer);
}
glBindBuffer(GL_SHADER_STORAGE_BUFFER, backend->vertexBuffer); glBindBuffer(GL_SHADER_STORAGE_BUFFER, backend->vertexBuffer);
backend->vertexMapping = glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, MG_GLES_CANVAS_VERTEX_BUFFER_SIZE, GL_MAP_WRITE_BIT); backend->vertexMapping = glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, MG_GLES_CANVAS_VERTEX_BUFFER_SIZE, GL_MAP_WRITE_BIT);
if(backend->indexMapping)
{
free(backend->indexMapping);
}
glBindBuffer(GL_SHADER_STORAGE_BUFFER, backend->indexBuffer); glBindBuffer(GL_SHADER_STORAGE_BUFFER, backend->indexBuffer);
backend->indexMapping = glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, MG_GLES_CANVAS_INDEX_BUFFER_SIZE, GL_MAP_WRITE_BIT); backend->indexMapping = glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, MG_GLES_CANVAS_INDEX_BUFFER_SIZE, GL_MAP_WRITE_BIT);
@ -137,6 +107,35 @@ void mg_gles_canvas_update_vertex_layout(mg_gles_canvas_backend* backend)
.indexStride = LAYOUT_INT_SIZE}; .indexStride = LAYOUT_INT_SIZE};
} }
void mg_gles_canvas_draw_buffers(mg_canvas_backend* interface, u32 vertexCount, u32 indexCount, mg_color clearColor)
{
mg_gles_canvas_backend* backend = (mg_gles_canvas_backend*)interface;
mg_gles_surface* surface = mg_gles_canvas_get_surface(backend);
if(!surface)
{
return;
}
/*NOTE: if we want debug_vertex while debugging, the following ensures the struct def doesn't get stripped away
debug_vertex vertex;
printf("foo %p\n", &vertex);
//*/
glBindBuffer(GL_SHADER_STORAGE_BUFFER, backend->vertexBuffer);
glUnmapBuffer(GL_SHADER_STORAGE_BUFFER);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, backend->indexBuffer);
glUnmapBuffer(GL_SHADER_STORAGE_BUFFER);
glUseProgram(backend->program);
glBindBuffer(GL_ARRAY_BUFFER, backend->dummyVertexBuffer);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, backend->vertexBuffer);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, backend->indexBuffer);
glUniform1i(0, indexCount);
glDrawArrays(GL_TRIANGLES, 0, 6);
mg_gles_canvas_update_vertex_layout(backend);
}
void mg_gles_canvas_destroy(mg_canvas_backend* interface) void mg_gles_canvas_destroy(mg_canvas_backend* interface)
{ {
mg_gles_canvas_backend* backend = (mg_gles_canvas_backend*)interface; mg_gles_canvas_backend* backend = (mg_gles_canvas_backend*)interface;

View File

@ -2,7 +2,7 @@
* *
* file: gles_canvas_shaders.h * file: gles_canvas_shaders.h
* note: string literals auto-generated by embed_text.py * note: string literals auto-generated by embed_text.py
* date: 31/012023 * date: 01/022023
* *
**********************************************************************/ **********************************************************************/
#ifndef __GLES_CANVAS_SHADERS_H__ #ifndef __GLES_CANVAS_SHADERS_H__
@ -30,15 +30,112 @@ const char* gles_canvas_fragment =
"} vertexBuffer ;\n" "} vertexBuffer ;\n"
"\n" "\n"
"layout(binding = 1) buffer indexBufferSSBO {\n" "layout(binding = 1) buffer indexBufferSSBO {\n"
" vec2 elements[];\n" " uint elements[];\n"
"} indexBuffer ;\n" "} indexBuffer ;\n"
"\n" "\n"
"layout(location = 0) uniform int indexCount;\n" "layout(location = 0) uniform int indexCount;\n"
"layout(location = 0) out vec4 fragColor;\n" "layout(location = 0) out vec4 fragColor;\n"
"\n" "\n"
"bool is_top_left(vec2 a, vec2 b)\n"
"{\n"
" return( (a.y == b.y && b.x < a.x)\n"
" ||(b.y < a.y));\n"
"}\n"
"\n"
"float orient2d(vec2 a, vec2 b, vec2 c)\n"
"{\n"
" //////////////////////////////////////////////////////////////////////////////////////////\n"
" //TODO(martin): FIX this. This is a **horrible** quick hack to fix the precision issues\n"
" // arising when a, b, and c are close. But it degrades when a, c, and c\n"
" // are big. The proper solution is to change the expression to avoid\n"
" // precision loss but I'm too busy/lazy to do it now.\n"
" //////////////////////////////////////////////////////////////////////////////////////////\n"
" a *= 10.;\n"
" b *= 10.;\n"
" c *= 10.;\n"
" return((b.x-a.x)*(c.y-a.y) - (b.y-a.y)*(c.x-a.x));\n"
"}\n"
"\n"
"void main()\n" "void main()\n"
"{\n" "{\n"
" fragColor = vec4(0.0, 1.0, 0.0, 1.0);\n" " vec4 pixelColor = vec4(0.0, 1.0, 0.0, 1.0);\n"
" vec4 currentColor = vec4(0., 0., 0., 1.0);\n"
"\n"
" vec2 samplePoint = gl_FragCoord.xy;\n"
"\n"
" int currentZIndex = -1;\n"
" int flipCount = 0;\n"
"\n"
"\n"
" for(int i=0; i<indexCount; i+=3)\n"
" {\n"
" uint i0 = indexBuffer.elements[i];\n"
" uint i1 = indexBuffer.elements[i+1];\n"
" uint i2 = indexBuffer.elements[i+2];\n"
"\n"
" vec2 p0 = vertexBuffer.elements[i0].pos;\n"
" vec2 p1 = vertexBuffer.elements[i1].pos;\n"
" vec2 p2 = vertexBuffer.elements[i2].pos;\n"
"\n"
" int zIndex = vertexBuffer.elements[i0].zIndex;\n"
" vec4 color = vertexBuffer.elements[i0].color;\n"
"\n"
" //NOTE(martin): reorder triangle counter-clockwise and compute bias for each edge\n"
" float cw = (p1 - p0).x*(p2 - p0).y - (p1 - p0).y*(p2 - p0).x;\n"
" if(cw < 0.)\n"
" {\n"
" uint tmpIndex = i1;\n"
" i1 = i2;\n"
" i2 = tmpIndex;\n"
"\n"
" vec2 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"
" float w0 = orient2d(p1, p2, samplePoint);\n"
" float w1 = orient2d(p2, p0, samplePoint);\n"
" float w2 = orient2d(p0, p1, samplePoint);\n"
"\n"
" if((int(w0)+bias0) >= 0 && (int(w1)+bias1) >= 0 && (int(w2)+bias2) >= 0)\n"
" {\n"
" //TODO check cubic\n"
" vec4 cubic = (cubic0*w0 + cubic1*w1 + cubic2*w2)/(w0+w1+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(zIndex == currentZIndex)\n"
" {\n"
" flipCount++;\n"
" }\n"
" else\n"
" {\n"
" if((flipCount & 0x01) != 0)\n"
" {\n"
" pixelColor = currentColor;\n"
" }\n"
" currentColor = pixelColor*(1.-color.a) + color.a*color;\n"
" currentZIndex = zIndex;\n"
" flipCount = 1;\n"
" }\n"
" }\n"
" }\n"
" }\n"
" if((flipCount & 0x01) != 0)\n"
" {\n"
" pixelColor = currentColor;\n"
" }\n"
"\n"
" fragColor = pixelColor;\n"
"}\n"; "}\n";
//NOTE: string imported from src\gles_canvas_shaders\gles_canvas_vertex.glsl //NOTE: string imported from src\gles_canvas_shaders\gles_canvas_vertex.glsl

View File

@ -17,13 +17,110 @@ layout(binding = 0) buffer vertexBufferSSBO {
} vertexBuffer ; } vertexBuffer ;
layout(binding = 1) buffer indexBufferSSBO { layout(binding = 1) buffer indexBufferSSBO {
vec2 elements[]; uint elements[];
} indexBuffer ; } indexBuffer ;
layout(location = 0) uniform int indexCount; layout(location = 0) uniform int indexCount;
layout(location = 0) out vec4 fragColor; layout(location = 0) out vec4 fragColor;
bool is_top_left(vec2 a, vec2 b)
{
return( (a.y == b.y && b.x < a.x)
||(b.y < a.y));
}
float orient2d(vec2 a, vec2 b, vec2 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));
}
void main() void main()
{ {
fragColor = vec4(0.0, 1.0, 0.0, 1.0); vec4 pixelColor = vec4(0.0, 1.0, 0.0, 1.0);
vec4 currentColor = vec4(0., 0., 0., 1.0);
vec2 samplePoint = gl_FragCoord.xy;
int currentZIndex = -1;
int flipCount = 0;
for(int i=0; i<indexCount; i+=3)
{
uint i0 = indexBuffer.elements[i];
uint i1 = indexBuffer.elements[i+1];
uint i2 = indexBuffer.elements[i+2];
vec2 p0 = vertexBuffer.elements[i0].pos;
vec2 p1 = vertexBuffer.elements[i1].pos;
vec2 p2 = vertexBuffer.elements[i2].pos;
int zIndex = vertexBuffer.elements[i0].zIndex;
vec4 color = vertexBuffer.elements[i0].color;
//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;
vec2 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;
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)
{
//TODO check cubic
vec4 cubic = (cubic0*w0 + cubic1*w1 + cubic2*w2)/(w0+w1+w2);
float eps = 0.0001;
if(cubic.w*(cubic.x*cubic.x*cubic.x - cubic.y*cubic.z) <= eps)
{
if(zIndex == currentZIndex)
{
flipCount++;
}
else
{
if((flipCount & 0x01) != 0)
{
pixelColor = currentColor;
}
currentColor = pixelColor*(1.-color.a) + color.a*color;
currentZIndex = zIndex;
flipCount = 1;
}
}
}
}
if((flipCount & 0x01) != 0)
{
pixelColor = currentColor;
}
fragColor = pixelColor;
} }

File diff suppressed because it is too large Load Diff

View File

@ -1,290 +1,290 @@
/************************************************************//** /************************************************************//**
* *
* @file: graphics_internal.h * @file: graphics_internal.h
* @author: Martin Fouilleul * @author: Martin Fouilleul
* @date: 23/01/2023 * @date: 23/01/2023
* @revision: * @revision:
* *
*****************************************************************/ *****************************************************************/
#ifndef __GRAPHICS_INTERNAL_H_ #ifndef __GRAPHICS_INTERNAL_H_
#define __GRAPHICS_INTERNAL_H_ #define __GRAPHICS_INTERNAL_H_
#include"graphics.h" #include"graphics.h"
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
typedef enum { MG_BACKEND_DUMMY, typedef enum { MG_BACKEND_DUMMY,
MG_BACKEND_METAL, MG_BACKEND_METAL,
MG_BACKEND_GL, MG_BACKEND_GL,
MG_BACKEND_GLES, MG_BACKEND_GLES,
//... //...
} mg_backend_id; } mg_backend_id;
typedef struct mg_surface_data mg_surface_data; typedef struct mg_surface_data mg_surface_data;
typedef void (*mg_surface_destroy_proc)(mg_surface_data* surface); typedef void (*mg_surface_destroy_proc)(mg_surface_data* surface);
typedef void (*mg_surface_prepare_proc)(mg_surface_data* surface); typedef void (*mg_surface_prepare_proc)(mg_surface_data* surface);
typedef void (*mg_surface_present_proc)(mg_surface_data* surface); typedef void (*mg_surface_present_proc)(mg_surface_data* surface);
typedef mp_rect (*mg_surface_get_frame_proc)(mg_surface_data* surface); typedef mp_rect (*mg_surface_get_frame_proc)(mg_surface_data* surface);
typedef void (*mg_surface_set_frame_proc)(mg_surface_data* surface, mp_rect frame); typedef void (*mg_surface_set_frame_proc)(mg_surface_data* surface, mp_rect frame);
typedef bool (*mg_surface_get_hidden_proc)(mg_surface_data* surface); typedef bool (*mg_surface_get_hidden_proc)(mg_surface_data* surface);
typedef void (*mg_surface_set_hidden_proc)(mg_surface_data* surface, bool hidden); typedef void (*mg_surface_set_hidden_proc)(mg_surface_data* surface, bool hidden);
typedef struct mg_surface_data typedef struct mg_surface_data
{ {
mg_backend_id backend; mg_backend_id backend;
mg_surface_destroy_proc destroy; mg_surface_destroy_proc destroy;
mg_surface_prepare_proc prepare; mg_surface_prepare_proc prepare;
mg_surface_present_proc present; mg_surface_present_proc present;
mg_surface_get_frame_proc getFrame; mg_surface_get_frame_proc getFrame;
mg_surface_set_frame_proc setFrame; mg_surface_set_frame_proc setFrame;
mg_surface_get_hidden_proc getHidden; mg_surface_get_hidden_proc getHidden;
mg_surface_set_hidden_proc setHidden; mg_surface_set_hidden_proc setHidden;
} mg_surface_data; } mg_surface_data;
mg_surface mg_surface_alloc_handle(mg_surface_data* surface); mg_surface mg_surface_alloc_handle(mg_surface_data* surface);
mg_surface_data* mg_surface_data_from_handle(mg_surface handle); mg_surface_data* mg_surface_data_from_handle(mg_surface handle);
//--------------------------------------------------------------- //---------------------------------------------------------------
// graphics structs // graphics structs
//--------------------------------------------------------------- //---------------------------------------------------------------
typedef enum { MG_PATH_MOVE, typedef enum { MG_PATH_MOVE,
MG_PATH_LINE, MG_PATH_LINE,
MG_PATH_QUADRATIC, MG_PATH_QUADRATIC,
MG_PATH_CUBIC } mg_path_elt_type; MG_PATH_CUBIC } mg_path_elt_type;
typedef struct mg_path_elt typedef struct mg_path_elt
{ {
mg_path_elt_type type; mg_path_elt_type type;
vec2 p[3]; vec2 p[3];
} mg_path_elt; } mg_path_elt;
typedef struct mg_path_descriptor typedef struct mg_path_descriptor
{ {
u32 startIndex; u32 startIndex;
u32 count; u32 count;
vec2 startPoint; vec2 startPoint;
} mg_path_descriptor; } mg_path_descriptor;
typedef struct mg_attributes typedef struct mg_attributes
{ {
f32 width; f32 width;
f32 tolerance; f32 tolerance;
mg_color color; mg_color color;
mg_joint_type joint; mg_joint_type joint;
f32 maxJointExcursion; f32 maxJointExcursion;
mg_cap_type cap; mg_cap_type cap;
mg_font font; mg_font font;
f32 fontSize; f32 fontSize;
mg_image image; mg_image image;
mp_rect clip; mp_rect clip;
} mg_attributes; } mg_attributes;
typedef struct mg_rounded_rect typedef struct mg_rounded_rect
{ {
f32 x; f32 x;
f32 y; f32 y;
f32 w; f32 w;
f32 h; f32 h;
f32 r; f32 r;
} mg_rounded_rect; } mg_rounded_rect;
typedef enum { MG_CMD_CLEAR = 0, typedef enum { MG_CMD_CLEAR = 0,
MG_CMD_FILL, MG_CMD_FILL,
MG_CMD_STROKE, MG_CMD_STROKE,
MG_CMD_RECT_FILL, MG_CMD_RECT_FILL,
MG_CMD_RECT_STROKE, MG_CMD_RECT_STROKE,
MG_CMD_ROUND_RECT_FILL, MG_CMD_ROUND_RECT_FILL,
MG_CMD_ROUND_RECT_STROKE, MG_CMD_ROUND_RECT_STROKE,
MG_CMD_ELLIPSE_FILL, MG_CMD_ELLIPSE_FILL,
MG_CMD_ELLIPSE_STROKE, MG_CMD_ELLIPSE_STROKE,
MG_CMD_JUMP, MG_CMD_JUMP,
MG_CMD_MATRIX_PUSH, MG_CMD_MATRIX_PUSH,
MG_CMD_MATRIX_POP, MG_CMD_MATRIX_POP,
MG_CMD_CLIP_PUSH, MG_CMD_CLIP_PUSH,
MG_CMD_CLIP_POP, MG_CMD_CLIP_POP,
MG_CMD_IMAGE_DRAW, MG_CMD_IMAGE_DRAW,
MG_CMD_ROUNDED_IMAGE_DRAW, MG_CMD_ROUNDED_IMAGE_DRAW,
} mg_primitive_cmd; } mg_primitive_cmd;
typedef struct mg_primitive typedef struct mg_primitive
{ {
mg_primitive_cmd cmd; mg_primitive_cmd cmd;
mg_attributes attributes; mg_attributes attributes;
union union
{ {
mg_path_descriptor path; mg_path_descriptor path;
mp_rect rect; mp_rect rect;
mg_rounded_rect roundedRect; mg_rounded_rect roundedRect;
utf32 codePoint; utf32 codePoint;
u32 jump; u32 jump;
mg_mat2x3 matrix; mg_mat2x3 matrix;
}; };
} mg_primitive; } mg_primitive;
typedef struct mg_glyph_map_entry typedef struct mg_glyph_map_entry
{ {
unicode_range range; unicode_range range;
u32 firstGlyphIndex; u32 firstGlyphIndex;
} mg_glyph_map_entry; } mg_glyph_map_entry;
typedef struct mg_glyph_data typedef struct mg_glyph_data
{ {
bool exists; bool exists;
utf32 codePoint; utf32 codePoint;
mg_path_descriptor pathDescriptor; mg_path_descriptor pathDescriptor;
mg_text_extents extents; mg_text_extents extents;
//... //...
} mg_glyph_data; } mg_glyph_data;
enum enum
{ {
MG_STREAM_MAX_COUNT = 128, MG_STREAM_MAX_COUNT = 128,
MG_IMAGE_MAX_COUNT = 128 MG_IMAGE_MAX_COUNT = 128
}; };
typedef struct mg_image_data typedef struct mg_image_data
{ {
list_elt listElt; list_elt listElt;
u32 generation; u32 generation;
mp_rect rect; mp_rect rect;
} mg_image_data; } mg_image_data;
enum enum
{ {
MG_MATRIX_STACK_MAX_DEPTH = 64, MG_MATRIX_STACK_MAX_DEPTH = 64,
MG_CLIP_STACK_MAX_DEPTH = 64, MG_CLIP_STACK_MAX_DEPTH = 64,
MG_MAX_PATH_ELEMENT_COUNT = 2<<20, MG_MAX_PATH_ELEMENT_COUNT = 2<<20,
MG_MAX_PRIMITIVE_COUNT = 8<<10 MG_MAX_PRIMITIVE_COUNT = 8<<10
}; };
typedef struct mg_font_data typedef struct mg_font_data
{ {
list_elt freeListElt; list_elt freeListElt;
u32 generation; u32 generation;
u32 rangeCount; u32 rangeCount;
u32 glyphCount; u32 glyphCount;
u32 outlineCount; u32 outlineCount;
mg_glyph_map_entry* glyphMap; mg_glyph_map_entry* glyphMap;
mg_glyph_data* glyphs; mg_glyph_data* glyphs;
mg_path_elt* outlines; mg_path_elt* outlines;
f32 unitsPerEm; f32 unitsPerEm;
mg_font_extents extents; mg_font_extents extents;
} mg_font_data; } mg_font_data;
typedef struct mg_vertex_layout typedef struct mg_vertex_layout
{ {
u32 maxVertexCount; u32 maxVertexCount;
u32 maxIndexCount; u32 maxIndexCount;
void* posBuffer; char* posBuffer;
u32 posStride; u32 posStride;
void* cubicBuffer; char* cubicBuffer;
u32 cubicStride; u32 cubicStride;
void* uvBuffer; char* uvBuffer;
u32 uvStride; u32 uvStride;
void* colorBuffer; char* colorBuffer;
u32 colorStride; u32 colorStride;
void* zIndexBuffer; char* zIndexBuffer;
u32 zIndexStride; u32 zIndexStride;
void* clipsBuffer; char* clipBuffer;
u32 clipsStride; u32 clipStride;
void* indexBuffer; char* indexBuffer;
u32 indexStride; u32 indexStride;
} mg_vertex_layout; } mg_vertex_layout;
typedef struct mg_canvas_backend mg_canvas_backend; typedef struct mg_canvas_backend mg_canvas_backend;
typedef void (*mg_canvas_backend_destroy_proc)(mg_canvas_backend* backend); typedef void (*mg_canvas_backend_destroy_proc)(mg_canvas_backend* backend);
typedef void (*mg_canvas_backend_draw_buffers_proc)(mg_canvas_backend* backend, u32 vertexCount, u32 indexCount, mg_color clearColor); typedef void (*mg_canvas_backend_draw_buffers_proc)(mg_canvas_backend* backend, u32 vertexCount, u32 indexCount, mg_color clearColor);
typedef void (*mg_canvas_backend_atlas_upload_proc)(mg_canvas_backend* backend, mp_rect rect, u8* bytes); typedef void (*mg_canvas_backend_atlas_upload_proc)(mg_canvas_backend* backend, mp_rect rect, u8* bytes);
typedef struct mg_canvas_backend typedef struct mg_canvas_backend
{ {
mg_vertex_layout vertexLayout; mg_vertex_layout vertexLayout;
mg_canvas_backend_destroy_proc destroy; mg_canvas_backend_destroy_proc destroy;
mg_canvas_backend_draw_buffers_proc drawBuffers; mg_canvas_backend_draw_buffers_proc drawBuffers;
mg_canvas_backend_atlas_upload_proc atlasUpload; mg_canvas_backend_atlas_upload_proc atlasUpload;
} mg_canvas_backend; } mg_canvas_backend;
typedef struct mg_canvas_data typedef struct mg_canvas_data
{ {
list_elt freeListElt; list_elt freeListElt;
u32 generation; u32 generation;
u64 frameCounter; u64 frameCounter;
mg_mat2x3 transform; mg_mat2x3 transform;
mp_rect clip; mp_rect clip;
mg_attributes attributes; mg_attributes attributes;
bool textFlip; bool textFlip;
mg_path_elt pathElements[MG_MAX_PATH_ELEMENT_COUNT]; mg_path_elt pathElements[MG_MAX_PATH_ELEMENT_COUNT];
mg_path_descriptor path; mg_path_descriptor path;
vec2 subPathStartPoint; vec2 subPathStartPoint;
vec2 subPathLastPoint; vec2 subPathLastPoint;
mg_mat2x3 matrixStack[MG_MATRIX_STACK_MAX_DEPTH]; mg_mat2x3 matrixStack[MG_MATRIX_STACK_MAX_DEPTH];
u32 matrixStackSize; u32 matrixStackSize;
mp_rect clipStack[MG_CLIP_STACK_MAX_DEPTH]; mp_rect clipStack[MG_CLIP_STACK_MAX_DEPTH];
u32 clipStackSize; u32 clipStackSize;
u32 nextZIndex; u32 nextZIndex;
u32 primitiveCount; u32 primitiveCount;
mg_primitive primitives[MG_MAX_PRIMITIVE_COUNT]; mg_primitive primitives[MG_MAX_PRIMITIVE_COUNT];
u32 vertexCount; u32 vertexCount;
u32 indexCount; u32 indexCount;
mg_image_data images[MG_IMAGE_MAX_COUNT]; mg_image_data images[MG_IMAGE_MAX_COUNT];
u32 imageNextIndex; u32 imageNextIndex;
list_info imageFreeList; list_info imageFreeList;
vec2 atlasPos; vec2 atlasPos;
u32 atlasLineHeight; u32 atlasLineHeight;
mg_image blankImage; mg_image blankImage;
mg_canvas_backend* backend; mg_canvas_backend* backend;
} mg_canvas_data; } mg_canvas_data;
enum enum
{ {
MG_ATLAS_SIZE = 8192, MG_ATLAS_SIZE = 8192,
}; };
#ifdef __cplusplus #ifdef __cplusplus
} // extern "C" } // extern "C"
#endif #endif
#endif //__GRAPHICS_INTERNAL_H_ #endif //__GRAPHICS_INTERNAL_H_

View File

@ -1,442 +1,442 @@
/************************************************************//** /************************************************************//**
* *
* @file: metal_canvas.m * @file: metal_canvas.m
* @author: Martin Fouilleul * @author: Martin Fouilleul
* @date: 12/07/2020 * @date: 12/07/2020
* @revision: 24/01/2023 * @revision: 24/01/2023
* *
*****************************************************************/ *****************************************************************/
#import<Metal/Metal.h> #import<Metal/Metal.h>
#import<QuartzCore/CAMetalLayer.h> #import<QuartzCore/CAMetalLayer.h>
#include<simd/simd.h> #include<simd/simd.h>
#include"graphics_internal.h" #include"graphics_internal.h"
#include"macro_helpers.h" #include"macro_helpers.h"
#include"osx_app.h" #include"osx_app.h"
#include"metal_shader.h" #include"metal_shader.h"
#define LOG_SUBSYSTEM "Graphics" #define LOG_SUBSYSTEM "Graphics"
static const int MG_METAL_CANVAS_DEFAULT_BUFFER_LENGTH = 4<<20; static const int MG_METAL_CANVAS_DEFAULT_BUFFER_LENGTH = 4<<20;
typedef struct mg_metal_canvas_backend typedef struct mg_metal_canvas_backend
{ {
mg_canvas_backend interface; mg_canvas_backend interface;
mg_surface surface; mg_surface surface;
// permanent metal resources // permanent metal resources
id<MTLComputePipelineState> tilingPipeline; id<MTLComputePipelineState> tilingPipeline;
id<MTLComputePipelineState> sortingPipeline; id<MTLComputePipelineState> sortingPipeline;
id<MTLComputePipelineState> boxingPipeline; id<MTLComputePipelineState> boxingPipeline;
id<MTLComputePipelineState> computePipeline; id<MTLComputePipelineState> computePipeline;
id<MTLRenderPipelineState> renderPipeline; id<MTLRenderPipelineState> renderPipeline;
mp_rect viewPort; mp_rect viewPort;
// textures and buffers // textures and buffers
id<MTLTexture> outTexture; id<MTLTexture> outTexture;
id<MTLTexture> atlasTexture; id<MTLTexture> atlasTexture;
id<MTLBuffer> vertexBuffer; id<MTLBuffer> vertexBuffer;
id<MTLBuffer> indexBuffer; id<MTLBuffer> indexBuffer;
id<MTLBuffer> tileCounters; id<MTLBuffer> tileCounters;
id<MTLBuffer> tilesArray; id<MTLBuffer> tilesArray;
id<MTLBuffer> triangleArray; id<MTLBuffer> triangleArray;
id<MTLBuffer> boxArray; id<MTLBuffer> boxArray;
} mg_metal_canvas_backend; } mg_metal_canvas_backend;
mg_metal_surface* mg_metal_canvas_get_surface(mg_metal_canvas_backend* canvas) mg_metal_surface* mg_metal_canvas_get_surface(mg_metal_canvas_backend* canvas)
{ {
mg_metal_surface* res = 0; mg_metal_surface* res = 0;
mg_surface_data* data = mg_surface_data_from_handle(canvas->surface); mg_surface_data* data = mg_surface_data_from_handle(canvas->surface);
if(data && data->backend == MG_BACKEND_METAL) if(data && data->backend == MG_BACKEND_METAL)
{ {
res = (mg_metal_surface*)data; res = (mg_metal_surface*)data;
} }
return(res); return(res);
} }
void mg_metal_canvas_draw_buffers(mg_canvas_backend* interface, u32 vertexCount, u32 indexCount, mg_color clearColor) void mg_metal_canvas_draw_buffers(mg_canvas_backend* interface, u32 vertexCount, u32 indexCount, mg_color clearColor)
{ {
mg_metal_canvas_backend* backend = (mg_metal_canvas_backend*)interface; mg_metal_canvas_backend* backend = (mg_metal_canvas_backend*)interface;
mg_metal_surface* surface = mg_metal_canvas_get_surface(backend); mg_metal_surface* surface = mg_metal_canvas_get_surface(backend);
if(!surface) if(!surface)
{ {
return; return;
} }
@autoreleasepool @autoreleasepool
{ {
if(surface->commandBuffer == nil || surface->commandBuffer == nil) if(surface->commandBuffer == nil || surface->commandBuffer == nil)
{ {
mg_metal_surface_acquire_drawable_and_command_buffer(surface); mg_metal_surface_acquire_drawable_and_command_buffer(surface);
} }
ASSERT(indexCount * sizeof(i32) < [backend->indexBuffer length]); ASSERT(indexCount * sizeof(i32) < [backend->indexBuffer length]);
f32 scale = surface->metalLayer.contentsScale; f32 scale = surface->metalLayer.contentsScale;
vector_uint2 viewportSize = {backend->viewPort.w * scale, backend->viewPort.h * scale}; vector_uint2 viewportSize = {backend->viewPort.w * scale, backend->viewPort.h * scale};
//----------------------------------------------------------- //-----------------------------------------------------------
//NOTE(martin): encode the clear counter //NOTE(martin): encode the clear counter
//----------------------------------------------------------- //-----------------------------------------------------------
id<MTLBlitCommandEncoder> blitEncoder = [surface->commandBuffer blitCommandEncoder]; id<MTLBlitCommandEncoder> blitEncoder = [surface->commandBuffer blitCommandEncoder];
[blitEncoder fillBuffer: backend->tileCounters range: NSMakeRange(0, RENDERER_MAX_TILES*sizeof(uint)) value: 0]; [blitEncoder fillBuffer: backend->tileCounters range: NSMakeRange(0, RENDERER_MAX_TILES*sizeof(uint)) value: 0];
[blitEncoder endEncoding]; [blitEncoder endEncoding];
//----------------------------------------------------------- //-----------------------------------------------------------
//NOTE(martin): encode the boxing pass //NOTE(martin): encode the boxing pass
//----------------------------------------------------------- //-----------------------------------------------------------
id<MTLComputeCommandEncoder> boxEncoder = [surface->commandBuffer computeCommandEncoder]; id<MTLComputeCommandEncoder> boxEncoder = [surface->commandBuffer computeCommandEncoder];
[boxEncoder setComputePipelineState: backend->boxingPipeline]; [boxEncoder setComputePipelineState: backend->boxingPipeline];
[boxEncoder setBuffer: backend->vertexBuffer offset:0 atIndex: 0]; [boxEncoder setBuffer: backend->vertexBuffer offset:0 atIndex: 0];
[boxEncoder setBuffer: backend->indexBuffer offset:0 atIndex: 1]; [boxEncoder setBuffer: backend->indexBuffer offset:0 atIndex: 1];
[boxEncoder setBuffer: backend->triangleArray offset:0 atIndex: 2]; [boxEncoder setBuffer: backend->triangleArray offset:0 atIndex: 2];
[boxEncoder setBuffer: backend->boxArray offset:0 atIndex: 3]; [boxEncoder setBuffer: backend->boxArray offset:0 atIndex: 3];
[boxEncoder setBytes: &scale length: sizeof(float) atIndex: 4]; [boxEncoder setBytes: &scale length: sizeof(float) atIndex: 4];
MTLSize boxGroupSize = MTLSizeMake(backend->boxingPipeline.maxTotalThreadsPerThreadgroup, 1, 1); MTLSize boxGroupSize = MTLSizeMake(backend->boxingPipeline.maxTotalThreadsPerThreadgroup, 1, 1);
MTLSize boxGridSize = MTLSizeMake(indexCount/3, 1, 1); MTLSize boxGridSize = MTLSizeMake(indexCount/3, 1, 1);
[boxEncoder dispatchThreads: boxGridSize threadsPerThreadgroup: boxGroupSize]; [boxEncoder dispatchThreads: boxGridSize threadsPerThreadgroup: boxGroupSize];
[boxEncoder endEncoding]; [boxEncoder endEncoding];
//----------------------------------------------------------- //-----------------------------------------------------------
//NOTE(martin): encode the tiling pass //NOTE(martin): encode the tiling pass
//----------------------------------------------------------- //-----------------------------------------------------------
id<MTLComputeCommandEncoder> tileEncoder = [surface->commandBuffer computeCommandEncoder]; id<MTLComputeCommandEncoder> tileEncoder = [surface->commandBuffer computeCommandEncoder];
[tileEncoder setComputePipelineState: backend->tilingPipeline]; [tileEncoder setComputePipelineState: backend->tilingPipeline];
[tileEncoder setBuffer: backend->boxArray offset:0 atIndex: 0]; [tileEncoder setBuffer: backend->boxArray offset:0 atIndex: 0];
[tileEncoder setBuffer: backend->tileCounters offset:0 atIndex: 1]; [tileEncoder setBuffer: backend->tileCounters offset:0 atIndex: 1];
[tileEncoder setBuffer: backend->tilesArray offset:0 atIndex: 2]; [tileEncoder setBuffer: backend->tilesArray offset:0 atIndex: 2];
[tileEncoder setBytes: &viewportSize length: sizeof(vector_uint2) atIndex: 3]; [tileEncoder setBytes: &viewportSize length: sizeof(vector_uint2) atIndex: 3];
[tileEncoder dispatchThreads: boxGridSize threadsPerThreadgroup: boxGroupSize]; [tileEncoder dispatchThreads: boxGridSize threadsPerThreadgroup: boxGroupSize];
[tileEncoder endEncoding]; [tileEncoder endEncoding];
//----------------------------------------------------------- //-----------------------------------------------------------
//NOTE(martin): encode the sorting pass //NOTE(martin): encode the sorting pass
//----------------------------------------------------------- //-----------------------------------------------------------
id<MTLComputeCommandEncoder> sortEncoder = [surface->commandBuffer computeCommandEncoder]; id<MTLComputeCommandEncoder> sortEncoder = [surface->commandBuffer computeCommandEncoder];
[sortEncoder setComputePipelineState: backend->sortingPipeline]; [sortEncoder setComputePipelineState: backend->sortingPipeline];
[sortEncoder setBuffer: backend->tileCounters offset:0 atIndex: 0]; [sortEncoder setBuffer: backend->tileCounters offset:0 atIndex: 0];
[sortEncoder setBuffer: backend->triangleArray offset:0 atIndex: 1]; [sortEncoder setBuffer: backend->triangleArray offset:0 atIndex: 1];
[sortEncoder setBuffer: backend->tilesArray offset:0 atIndex: 2]; [sortEncoder setBuffer: backend->tilesArray offset:0 atIndex: 2];
[sortEncoder setBytes: &viewportSize length: sizeof(vector_uint2) atIndex: 3]; [sortEncoder setBytes: &viewportSize length: sizeof(vector_uint2) atIndex: 3];
u32 nTilesX = (viewportSize.x + RENDERER_TILE_SIZE - 1)/RENDERER_TILE_SIZE; u32 nTilesX = (viewportSize.x + RENDERER_TILE_SIZE - 1)/RENDERER_TILE_SIZE;
u32 nTilesY = (viewportSize.y + RENDERER_TILE_SIZE - 1)/RENDERER_TILE_SIZE; u32 nTilesY = (viewportSize.y + RENDERER_TILE_SIZE - 1)/RENDERER_TILE_SIZE;
MTLSize sortGroupSize = MTLSizeMake(backend->boxingPipeline.maxTotalThreadsPerThreadgroup, 1, 1); MTLSize sortGroupSize = MTLSizeMake(backend->boxingPipeline.maxTotalThreadsPerThreadgroup, 1, 1);
MTLSize sortGridSize = MTLSizeMake(nTilesX*nTilesY, 1, 1); MTLSize sortGridSize = MTLSizeMake(nTilesX*nTilesY, 1, 1);
[sortEncoder dispatchThreads: sortGridSize threadsPerThreadgroup: sortGroupSize]; [sortEncoder dispatchThreads: sortGridSize threadsPerThreadgroup: sortGroupSize];
[sortEncoder endEncoding]; [sortEncoder endEncoding];
//----------------------------------------------------------- //-----------------------------------------------------------
//NOTE(martin): create compute encoder and encode commands //NOTE(martin): create compute encoder and encode commands
//----------------------------------------------------------- //-----------------------------------------------------------
vector_float4 clearColorVec4 = {clearColor.r, clearColor.g, clearColor.b, clearColor.a}; vector_float4 clearColorVec4 = {clearColor.r, clearColor.g, clearColor.b, clearColor.a};
id<MTLComputeCommandEncoder> encoder = [surface->commandBuffer computeCommandEncoder]; id<MTLComputeCommandEncoder> encoder = [surface->commandBuffer computeCommandEncoder];
[encoder setComputePipelineState:backend->computePipeline]; [encoder setComputePipelineState:backend->computePipeline];
[encoder setTexture: backend->outTexture atIndex: 0]; [encoder setTexture: backend->outTexture atIndex: 0];
[encoder setTexture: backend->atlasTexture atIndex: 1]; [encoder setTexture: backend->atlasTexture atIndex: 1];
[encoder setBuffer: backend->vertexBuffer offset:0 atIndex: 0]; [encoder setBuffer: backend->vertexBuffer offset:0 atIndex: 0];
[encoder setBuffer: backend->tileCounters offset:0 atIndex: 1]; [encoder setBuffer: backend->tileCounters offset:0 atIndex: 1];
[encoder setBuffer: backend->tilesArray offset:0 atIndex: 2]; [encoder setBuffer: backend->tilesArray offset:0 atIndex: 2];
[encoder setBuffer: backend->triangleArray offset:0 atIndex: 3]; [encoder setBuffer: backend->triangleArray offset:0 atIndex: 3];
[encoder setBuffer: backend->boxArray offset:0 atIndex: 4]; [encoder setBuffer: backend->boxArray offset:0 atIndex: 4];
[encoder setBytes: &clearColorVec4 length: sizeof(vector_float4) atIndex: 5]; [encoder setBytes: &clearColorVec4 length: sizeof(vector_float4) atIndex: 5];
//TODO: check that we don't exceed maxTotalThreadsPerThreadgroup //TODO: check that we don't exceed maxTotalThreadsPerThreadgroup
DEBUG_ASSERT(RENDERER_TILE_SIZE*RENDERER_TILE_SIZE <= backend->computePipeline.maxTotalThreadsPerThreadgroup); DEBUG_ASSERT(RENDERER_TILE_SIZE*RENDERER_TILE_SIZE <= backend->computePipeline.maxTotalThreadsPerThreadgroup);
MTLSize threadGridSize = MTLSizeMake(viewportSize.x, viewportSize.y, 1); MTLSize threadGridSize = MTLSizeMake(viewportSize.x, viewportSize.y, 1);
MTLSize threadGroupSize = MTLSizeMake(RENDERER_TILE_SIZE, RENDERER_TILE_SIZE, 1); MTLSize threadGroupSize = MTLSizeMake(RENDERER_TILE_SIZE, RENDERER_TILE_SIZE, 1);
[encoder dispatchThreads: threadGridSize threadsPerThreadgroup:threadGroupSize]; [encoder dispatchThreads: threadGridSize threadsPerThreadgroup:threadGroupSize];
[encoder endEncoding]; [encoder endEncoding];
//----------------------------------------------------------- //-----------------------------------------------------------
//NOTE(martin): acquire drawable, create render encoder to blit texture to framebuffer //NOTE(martin): acquire drawable, create render encoder to blit texture to framebuffer
//----------------------------------------------------------- //-----------------------------------------------------------
MTLViewport viewport = {backend->viewPort.x * scale, MTLViewport viewport = {backend->viewPort.x * scale,
backend->viewPort.y * scale, backend->viewPort.y * scale,
backend->viewPort.w * scale, backend->viewPort.w * scale,
backend->viewPort.h * scale, backend->viewPort.h * scale,
0, 0,
1}; 1};
MTLRenderPassDescriptor* renderPassDescriptor = [MTLRenderPassDescriptor renderPassDescriptor]; MTLRenderPassDescriptor* renderPassDescriptor = [MTLRenderPassDescriptor renderPassDescriptor];
renderPassDescriptor.colorAttachments[0].texture = surface->drawable.texture; renderPassDescriptor.colorAttachments[0].texture = surface->drawable.texture;
renderPassDescriptor.colorAttachments[0].storeAction = MTLStoreActionStore; renderPassDescriptor.colorAttachments[0].storeAction = MTLStoreActionStore;
id<MTLRenderCommandEncoder> renderEncoder = [surface->commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor]; id<MTLRenderCommandEncoder> renderEncoder = [surface->commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor];
[renderEncoder setViewport: viewport]; [renderEncoder setViewport: viewport];
[renderEncoder setRenderPipelineState: backend->renderPipeline]; [renderEncoder setRenderPipelineState: backend->renderPipeline];
[renderEncoder setFragmentTexture: backend->outTexture atIndex: 0]; [renderEncoder setFragmentTexture: backend->outTexture atIndex: 0];
[renderEncoder drawPrimitives: MTLPrimitiveTypeTriangle [renderEncoder drawPrimitives: MTLPrimitiveTypeTriangle
vertexStart: 0 vertexStart: 0
vertexCount: 3 ]; vertexCount: 3 ];
[renderEncoder endEncoding]; [renderEncoder endEncoding];
} }
} }
/* /*
void mg_metal_canvas_viewport(mg_canvas_backend* interface, mp_rect viewPort) void mg_metal_canvas_viewport(mg_canvas_backend* interface, mp_rect viewPort)
{ {
mg_metal_canvas_backend* backend = (mg_metal_canvas_backend*)interface; mg_metal_canvas_backend* backend = (mg_metal_canvas_backend*)interface;
mg_metal_surface* surface = mg_metal_canvas_get_surface(backend); mg_metal_surface* surface = mg_metal_canvas_get_surface(backend);
if(!surface) if(!surface)
{ {
return; return;
} }
backend->viewPort = viewPort; backend->viewPort = viewPort;
@autoreleasepool @autoreleasepool
{ {
f32 scale = surface->metalLayer.contentsScale; f32 scale = surface->metalLayer.contentsScale;
CGSize drawableSize = (CGSize){.width = viewPort.w * scale, .height = viewPort.h * scale}; CGSize drawableSize = (CGSize){.width = viewPort.w * scale, .height = viewPort.h * scale};
[backend->outTexture release]; [backend->outTexture release];
MTLTextureDescriptor* texDesc = [[MTLTextureDescriptor alloc] init]; MTLTextureDescriptor* texDesc = [[MTLTextureDescriptor alloc] init];
texDesc.textureType = MTLTextureType2D; texDesc.textureType = MTLTextureType2D;
texDesc.storageMode = MTLStorageModePrivate; texDesc.storageMode = MTLStorageModePrivate;
texDesc.usage = MTLTextureUsageShaderRead | MTLTextureUsageShaderWrite; texDesc.usage = MTLTextureUsageShaderRead | MTLTextureUsageShaderWrite;
texDesc.pixelFormat = MTLPixelFormatBGRA8Unorm;// MTLPixelFormatBGRA8Unorm_sRGB; texDesc.pixelFormat = MTLPixelFormatBGRA8Unorm;// MTLPixelFormatBGRA8Unorm_sRGB;
texDesc.width = drawableSize.width; texDesc.width = drawableSize.width;
texDesc.height = drawableSize.height; texDesc.height = drawableSize.height;
backend->outTexture = [surface->device newTextureWithDescriptor:texDesc]; backend->outTexture = [surface->device newTextureWithDescriptor:texDesc];
} }
} }
*/ */
void mg_metal_canvas_update_vertex_layout(mg_metal_canvas_backend* backend) void mg_metal_canvas_update_vertex_layout(mg_metal_canvas_backend* backend)
{ {
char* vertexBase = (char*)[backend->vertexBuffer contents]; char* vertexBase = (char*)[backend->vertexBuffer contents];
backend->interface.vertexLayout = (mg_vertex_layout){ backend->interface.vertexLayout = (mg_vertex_layout){
.maxVertexCount = MG_METAL_CANVAS_DEFAULT_BUFFER_LENGTH, .maxVertexCount = MG_METAL_CANVAS_DEFAULT_BUFFER_LENGTH,
.maxIndexCount = MG_METAL_CANVAS_DEFAULT_BUFFER_LENGTH, .maxIndexCount = MG_METAL_CANVAS_DEFAULT_BUFFER_LENGTH,
.posBuffer = vertexBase + offsetof(mg_vertex, pos), .posBuffer = vertexBase + offsetof(mg_vertex, pos),
.posStride = sizeof(mg_vertex), .posStride = sizeof(mg_vertex),
.cubicBuffer = vertexBase + offsetof(mg_vertex, cubic), .cubicBuffer = vertexBase + offsetof(mg_vertex, cubic),
.cubicStride = sizeof(mg_vertex), .cubicStride = sizeof(mg_vertex),
.uvBuffer = vertexBase + offsetof(mg_vertex, uv), .uvBuffer = vertexBase + offsetof(mg_vertex, uv),
.uvStride = sizeof(mg_vertex), .uvStride = sizeof(mg_vertex),
.colorBuffer = vertexBase + offsetof(mg_vertex, color), .colorBuffer = vertexBase + offsetof(mg_vertex, color),
.colorStride = sizeof(mg_vertex), .colorStride = sizeof(mg_vertex),
.zIndexBuffer = vertexBase + offsetof(mg_vertex, zIndex), .zIndexBuffer = vertexBase + offsetof(mg_vertex, zIndex),
.zIndexStride = sizeof(mg_vertex), .zIndexStride = sizeof(mg_vertex),
.clipsBuffer = vertexBase + offsetof(mg_vertex, clip), .clipBuffer = vertexBase + offsetof(mg_vertex, clip),
.clipsStride = sizeof(mg_vertex), .clipStride = sizeof(mg_vertex),
.indexBuffer = [backend->indexBuffer contents], .indexBuffer = [backend->indexBuffer contents],
.indexStride = sizeof(int)}; .indexStride = sizeof(int)};
} }
void mg_metal_canvas_destroy(mg_canvas_backend* interface) void mg_metal_canvas_destroy(mg_canvas_backend* interface)
{ {
mg_metal_canvas_backend* backend = (mg_metal_canvas_backend*)interface; mg_metal_canvas_backend* backend = (mg_metal_canvas_backend*)interface;
@autoreleasepool @autoreleasepool
{ {
[backend->outTexture release]; [backend->outTexture release];
[backend->atlasTexture release]; [backend->atlasTexture release];
[backend->vertexBuffer release]; [backend->vertexBuffer release];
[backend->indexBuffer release]; [backend->indexBuffer release];
[backend->tilesArray release]; [backend->tilesArray release];
[backend->triangleArray release]; [backend->triangleArray release];
[backend->boxArray release]; [backend->boxArray release];
[backend->computePipeline release]; [backend->computePipeline release];
} }
} }
void mg_metal_canvas_atlas_upload(mg_canvas_backend* interface, mp_rect rect, u8* bytes) void mg_metal_canvas_atlas_upload(mg_canvas_backend* interface, mp_rect rect, u8* bytes)
{@autoreleasepool{ {@autoreleasepool{
mg_metal_canvas_backend* backend = (mg_metal_canvas_backend*)interface; mg_metal_canvas_backend* backend = (mg_metal_canvas_backend*)interface;
MTLRegion region = MTLRegionMake2D(rect.x, rect.y, rect.w, rect.h); MTLRegion region = MTLRegionMake2D(rect.x, rect.y, rect.w, rect.h);
[backend->atlasTexture replaceRegion:region [backend->atlasTexture replaceRegion:region
mipmapLevel:0 mipmapLevel:0
withBytes:(void*)bytes withBytes:(void*)bytes
bytesPerRow: 4 * rect.w]; bytesPerRow: 4 * rect.w];
}} }}
mg_canvas_backend* mg_metal_canvas_create(mg_surface surface) mg_canvas_backend* mg_metal_canvas_create(mg_surface surface)
{ {
mg_metal_canvas_backend* backend = 0; mg_metal_canvas_backend* backend = 0;
mg_surface_data* surfaceData = mg_surface_data_from_handle(surface); mg_surface_data* surfaceData = mg_surface_data_from_handle(surface);
if(surfaceData && surfaceData->backend == MG_BACKEND_METAL) if(surfaceData && surfaceData->backend == MG_BACKEND_METAL)
{ {
mg_metal_surface* metalSurface = (mg_metal_surface*)surfaceData; mg_metal_surface* metalSurface = (mg_metal_surface*)surfaceData;
backend = malloc_type(mg_metal_canvas_backend); backend = malloc_type(mg_metal_canvas_backend);
backend->surface = surface; backend->surface = surface;
//NOTE(martin): setup interface functions //NOTE(martin): setup interface functions
backend->interface.destroy = mg_metal_canvas_destroy; backend->interface.destroy = mg_metal_canvas_destroy;
backend->interface.drawBuffers = mg_metal_canvas_draw_buffers; backend->interface.drawBuffers = mg_metal_canvas_draw_buffers;
backend->interface.atlasUpload = mg_metal_canvas_atlas_upload; backend->interface.atlasUpload = mg_metal_canvas_atlas_upload;
mp_rect frame = mg_surface_get_frame(surface); mp_rect frame = mg_surface_get_frame(surface);
backend->viewPort = (mp_rect){0, 0, frame.w, frame.h}; backend->viewPort = (mp_rect){0, 0, frame.w, frame.h};
@autoreleasepool @autoreleasepool
{ {
f32 scale = metalSurface->metalLayer.contentsScale; f32 scale = metalSurface->metalLayer.contentsScale;
CGSize drawableSize = (CGSize){.width = backend->viewPort.w * scale, .height = backend->viewPort.h * scale}; CGSize drawableSize = (CGSize){.width = backend->viewPort.w * scale, .height = backend->viewPort.h * scale};
//----------------------------------------------------------- //-----------------------------------------------------------
//NOTE(martin): create our output texture //NOTE(martin): create our output texture
//----------------------------------------------------------- //-----------------------------------------------------------
MTLTextureDescriptor* texDesc = [[MTLTextureDescriptor alloc] init]; MTLTextureDescriptor* texDesc = [[MTLTextureDescriptor alloc] init];
texDesc.textureType = MTLTextureType2D; texDesc.textureType = MTLTextureType2D;
texDesc.storageMode = MTLStorageModePrivate; texDesc.storageMode = MTLStorageModePrivate;
texDesc.usage = MTLTextureUsageShaderRead | MTLTextureUsageShaderWrite; texDesc.usage = MTLTextureUsageShaderRead | MTLTextureUsageShaderWrite;
texDesc.pixelFormat = MTLPixelFormatBGRA8Unorm;// MTLPixelFormatBGRA8Unorm_sRGB; texDesc.pixelFormat = MTLPixelFormatBGRA8Unorm;// MTLPixelFormatBGRA8Unorm_sRGB;
texDesc.width = drawableSize.width; texDesc.width = drawableSize.width;
texDesc.height = drawableSize.height; texDesc.height = drawableSize.height;
backend->outTexture = [metalSurface->device newTextureWithDescriptor:texDesc]; backend->outTexture = [metalSurface->device newTextureWithDescriptor:texDesc];
//TODO(martin): retain ? //TODO(martin): retain ?
//----------------------------------------------------------- //-----------------------------------------------------------
//NOTE(martin): create our atlas texture //NOTE(martin): create our atlas texture
//----------------------------------------------------------- //-----------------------------------------------------------
texDesc.textureType = MTLTextureType2D; texDesc.textureType = MTLTextureType2D;
texDesc.storageMode = MTLStorageModeManaged; texDesc.storageMode = MTLStorageModeManaged;
texDesc.usage = MTLTextureUsageShaderRead; texDesc.usage = MTLTextureUsageShaderRead;
texDesc.pixelFormat = MTLPixelFormatRGBA8Unorm; //MTLPixelFormatBGRA8Unorm; texDesc.pixelFormat = MTLPixelFormatRGBA8Unorm; //MTLPixelFormatBGRA8Unorm;
texDesc.width = MG_ATLAS_SIZE; texDesc.width = MG_ATLAS_SIZE;
texDesc.height = MG_ATLAS_SIZE; texDesc.height = MG_ATLAS_SIZE;
backend->atlasTexture = [metalSurface->device newTextureWithDescriptor:texDesc]; backend->atlasTexture = [metalSurface->device newTextureWithDescriptor:texDesc];
//----------------------------------------------------------- //-----------------------------------------------------------
//NOTE(martin): create buffers for vertex and index //NOTE(martin): create buffers for vertex and index
//----------------------------------------------------------- //-----------------------------------------------------------
MTLResourceOptions bufferOptions = MTLResourceCPUCacheModeWriteCombined MTLResourceOptions bufferOptions = MTLResourceCPUCacheModeWriteCombined
| MTLResourceStorageModeShared; | MTLResourceStorageModeShared;
backend->indexBuffer = [metalSurface->device newBufferWithLength: MG_METAL_CANVAS_DEFAULT_BUFFER_LENGTH*sizeof(int) backend->indexBuffer = [metalSurface->device newBufferWithLength: MG_METAL_CANVAS_DEFAULT_BUFFER_LENGTH*sizeof(int)
options: bufferOptions]; options: bufferOptions];
backend->vertexBuffer = [metalSurface->device newBufferWithLength: MG_METAL_CANVAS_DEFAULT_BUFFER_LENGTH*sizeof(mg_vertex) backend->vertexBuffer = [metalSurface->device newBufferWithLength: MG_METAL_CANVAS_DEFAULT_BUFFER_LENGTH*sizeof(mg_vertex)
options: bufferOptions]; options: bufferOptions];
backend->tilesArray = [metalSurface->device newBufferWithLength: RENDERER_TILE_BUFFER_SIZE*sizeof(int)*RENDERER_MAX_TILES backend->tilesArray = [metalSurface->device newBufferWithLength: RENDERER_TILE_BUFFER_SIZE*sizeof(int)*RENDERER_MAX_TILES
options: MTLResourceStorageModePrivate]; options: MTLResourceStorageModePrivate];
backend->triangleArray = [metalSurface->device newBufferWithLength: MG_METAL_CANVAS_DEFAULT_BUFFER_LENGTH*sizeof(mg_triangle_data) backend->triangleArray = [metalSurface->device newBufferWithLength: MG_METAL_CANVAS_DEFAULT_BUFFER_LENGTH*sizeof(mg_triangle_data)
options: MTLResourceStorageModePrivate]; options: MTLResourceStorageModePrivate];
backend->boxArray = [metalSurface->device newBufferWithLength: MG_METAL_CANVAS_DEFAULT_BUFFER_LENGTH*sizeof(vector_float4) backend->boxArray = [metalSurface->device newBufferWithLength: MG_METAL_CANVAS_DEFAULT_BUFFER_LENGTH*sizeof(vector_float4)
options: MTLResourceStorageModePrivate]; options: MTLResourceStorageModePrivate];
//TODO(martin): retain ? //TODO(martin): retain ?
//----------------------------------------------------------- //-----------------------------------------------------------
//NOTE(martin): create and initialize tile counters //NOTE(martin): create and initialize tile counters
//----------------------------------------------------------- //-----------------------------------------------------------
backend->tileCounters = [metalSurface->device newBufferWithLength: RENDERER_MAX_TILES*sizeof(uint) backend->tileCounters = [metalSurface->device newBufferWithLength: RENDERER_MAX_TILES*sizeof(uint)
options: MTLResourceStorageModePrivate]; options: MTLResourceStorageModePrivate];
id<MTLCommandBuffer> commandBuffer = [metalSurface->commandQueue commandBuffer]; id<MTLCommandBuffer> commandBuffer = [metalSurface->commandQueue commandBuffer];
id<MTLBlitCommandEncoder> blitEncoder = [commandBuffer blitCommandEncoder]; id<MTLBlitCommandEncoder> blitEncoder = [commandBuffer blitCommandEncoder];
[blitEncoder fillBuffer: backend->tileCounters range: NSMakeRange(0, RENDERER_MAX_TILES*sizeof(uint)) value: 0]; [blitEncoder fillBuffer: backend->tileCounters range: NSMakeRange(0, RENDERER_MAX_TILES*sizeof(uint)) value: 0];
[blitEncoder endEncoding]; [blitEncoder endEncoding];
[commandBuffer commit]; [commandBuffer commit];
//----------------------------------------------------------- //-----------------------------------------------------------
//NOTE(martin): load the library //NOTE(martin): load the library
//----------------------------------------------------------- //-----------------------------------------------------------
//TODO(martin): filepath magic to find metallib path when not in the working directory //TODO(martin): filepath magic to find metallib path when not in the working directory
str8 shaderPath = mp_app_get_resource_path(mem_scratch(), "../resources/metal_shader.metallib"); str8 shaderPath = mp_app_get_resource_path(mem_scratch(), "../resources/metal_shader.metallib");
NSString* metalFileName = [[NSString alloc] initWithBytes: shaderPath.ptr length:shaderPath.len encoding: NSUTF8StringEncoding]; NSString* metalFileName = [[NSString alloc] initWithBytes: shaderPath.ptr length:shaderPath.len encoding: NSUTF8StringEncoding];
NSError* err = 0; NSError* err = 0;
id<MTLLibrary> library = [metalSurface->device newLibraryWithFile: metalFileName error:&err]; id<MTLLibrary> library = [metalSurface->device newLibraryWithFile: metalFileName error:&err];
if(err != nil) if(err != nil)
{ {
const char* errStr = [[err localizedDescription] UTF8String]; const char* errStr = [[err localizedDescription] UTF8String];
LOG_ERROR("error : %s\n", errStr); LOG_ERROR("error : %s\n", errStr);
return(0); return(0);
} }
id<MTLFunction> tilingFunction = [library newFunctionWithName:@"TileKernel"]; id<MTLFunction> tilingFunction = [library newFunctionWithName:@"TileKernel"];
id<MTLFunction> sortingFunction = [library newFunctionWithName:@"SortKernel"]; id<MTLFunction> sortingFunction = [library newFunctionWithName:@"SortKernel"];
id<MTLFunction> boxingFunction = [library newFunctionWithName:@"BoundingBoxKernel"]; id<MTLFunction> boxingFunction = [library newFunctionWithName:@"BoundingBoxKernel"];
id<MTLFunction> computeFunction = [library newFunctionWithName:@"RenderKernel"]; id<MTLFunction> computeFunction = [library newFunctionWithName:@"RenderKernel"];
id<MTLFunction> vertexFunction = [library newFunctionWithName:@"VertexShader"]; id<MTLFunction> vertexFunction = [library newFunctionWithName:@"VertexShader"];
id<MTLFunction> fragmentFunction = [library newFunctionWithName:@"FragmentShader"]; id<MTLFunction> fragmentFunction = [library newFunctionWithName:@"FragmentShader"];
//----------------------------------------------------------- //-----------------------------------------------------------
//NOTE(martin): setup our data layout and pipeline state //NOTE(martin): setup our data layout and pipeline state
//----------------------------------------------------------- //-----------------------------------------------------------
NSError* error = NULL; NSError* error = NULL;
backend->computePipeline = [metalSurface->device newComputePipelineStateWithFunction: computeFunction backend->computePipeline = [metalSurface->device newComputePipelineStateWithFunction: computeFunction
error:&error]; error:&error];
ASSERT(backend->computePipeline); ASSERT(backend->computePipeline);
MTLComputePipelineDescriptor* tilingPipelineDesc = [[MTLComputePipelineDescriptor alloc] init]; MTLComputePipelineDescriptor* tilingPipelineDesc = [[MTLComputePipelineDescriptor alloc] init];
tilingPipelineDesc.computeFunction = tilingFunction; tilingPipelineDesc.computeFunction = tilingFunction;
// tilingPipelineDesc.threadGroupSizeIsMultipleOfThreadExecutionWidth = true; // tilingPipelineDesc.threadGroupSizeIsMultipleOfThreadExecutionWidth = true;
backend->tilingPipeline = [metalSurface->device newComputePipelineStateWithDescriptor: tilingPipelineDesc backend->tilingPipeline = [metalSurface->device newComputePipelineStateWithDescriptor: tilingPipelineDesc
options: MTLPipelineOptionNone options: MTLPipelineOptionNone
reflection: nil reflection: nil
error: &error]; error: &error];
MTLComputePipelineDescriptor* sortingPipelineDesc = [[MTLComputePipelineDescriptor alloc] init]; MTLComputePipelineDescriptor* sortingPipelineDesc = [[MTLComputePipelineDescriptor alloc] init];
sortingPipelineDesc.computeFunction = sortingFunction; sortingPipelineDesc.computeFunction = sortingFunction;
// sortingPipelineDesc.threadGroupSizeIsMultipleOfThreadExecutionWidth = true; // sortingPipelineDesc.threadGroupSizeIsMultipleOfThreadExecutionWidth = true;
backend->sortingPipeline = [metalSurface->device newComputePipelineStateWithDescriptor: sortingPipelineDesc backend->sortingPipeline = [metalSurface->device newComputePipelineStateWithDescriptor: sortingPipelineDesc
options: MTLPipelineOptionNone options: MTLPipelineOptionNone
reflection: nil reflection: nil
error: &error]; error: &error];
MTLComputePipelineDescriptor* boxingPipelineDesc = [[MTLComputePipelineDescriptor alloc] init]; MTLComputePipelineDescriptor* boxingPipelineDesc = [[MTLComputePipelineDescriptor alloc] init];
boxingPipelineDesc.computeFunction = boxingFunction; boxingPipelineDesc.computeFunction = boxingFunction;
// boxingPipelineDesc.threadGroupSizeIsMultipleOfThreadExecutionWidth = true; // boxingPipelineDesc.threadGroupSizeIsMultipleOfThreadExecutionWidth = true;
backend->boxingPipeline = [metalSurface->device newComputePipelineStateWithDescriptor: boxingPipelineDesc backend->boxingPipeline = [metalSurface->device newComputePipelineStateWithDescriptor: boxingPipelineDesc
options: MTLPipelineOptionNone options: MTLPipelineOptionNone
reflection: nil reflection: nil
error: &error]; error: &error];
//----------------------------------------------------------- //-----------------------------------------------------------
//NOTE(martin): setup our render pipeline state //NOTE(martin): setup our render pipeline state
//----------------------------------------------------------- //-----------------------------------------------------------
// create and initialize the pipeline state descriptor // create and initialize the pipeline state descriptor
MTLRenderPipelineDescriptor *pipelineStateDescriptor = [[MTLRenderPipelineDescriptor alloc] init]; MTLRenderPipelineDescriptor *pipelineStateDescriptor = [[MTLRenderPipelineDescriptor alloc] init];
pipelineStateDescriptor.label = @"My simple pipeline"; pipelineStateDescriptor.label = @"My simple pipeline";
pipelineStateDescriptor.vertexFunction = vertexFunction; pipelineStateDescriptor.vertexFunction = vertexFunction;
pipelineStateDescriptor.fragmentFunction = fragmentFunction; pipelineStateDescriptor.fragmentFunction = fragmentFunction;
pipelineStateDescriptor.colorAttachments[0].pixelFormat = metalSurface->metalLayer.pixelFormat; pipelineStateDescriptor.colorAttachments[0].pixelFormat = metalSurface->metalLayer.pixelFormat;
// create render pipeline // create render pipeline
backend->renderPipeline = [metalSurface->device newRenderPipelineStateWithDescriptor: pipelineStateDescriptor error:&err]; backend->renderPipeline = [metalSurface->device newRenderPipelineStateWithDescriptor: pipelineStateDescriptor error:&err];
if(err != nil) if(err != nil)
{ {
const char* errStr = [[err localizedDescription] UTF8String]; const char* errStr = [[err localizedDescription] UTF8String];
const char* descStr = [[err localizedFailureReason] UTF8String]; const char* descStr = [[err localizedFailureReason] UTF8String];
const char* recovStr = [[err localizedRecoverySuggestion] UTF8String]; const char* recovStr = [[err localizedRecoverySuggestion] UTF8String];
LOG_ERROR("(%li) %s. %s. %s\n", [err code], errStr, descStr, recovStr); LOG_ERROR("(%li) %s. %s. %s\n", [err code], errStr, descStr, recovStr);
return(0); return(0);
} }
} }
mg_metal_canvas_update_vertex_layout(backend); mg_metal_canvas_update_vertex_layout(backend);
} }
return((mg_canvas_backend*)backend); return((mg_canvas_backend*)backend);
} }
#undef LOG_SUBSYSTEM #undef LOG_SUBSYSTEM

View File

@ -1,349 +1,349 @@
#include<metal_stdlib> #include<metal_stdlib>
#include<simd/simd.h> #include<simd/simd.h>
#include"metal_shader.h" #include"metal_shader.h"
using namespace metal; using namespace metal;
struct vs_out struct vs_out
{ {
float4 pos [[position]]; float4 pos [[position]];
float2 uv; float2 uv;
}; };
vertex vs_out VertexShader(ushort vid [[vertex_id]]) vertex vs_out VertexShader(ushort vid [[vertex_id]])
{ {
vs_out out; vs_out out;
out.uv = float2((vid << 1) & 2, vid & 2); out.uv = float2((vid << 1) & 2, vid & 2);
out.pos = float4(out.uv * float2(2, 2) + float2(-1, -1), 0, 1); out.pos = float4(out.uv * float2(2, 2) + float2(-1, -1), 0, 1);
return(out); return(out);
} }
fragment float4 FragmentShader(vs_out i [[stage_in]], texture2d<float> tex [[texture(0)]]) 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); constexpr sampler smp(mip_filter::nearest, mag_filter::linear, min_filter::linear);
return(float4(tex.sample(smp, i.uv).rgb, 1)); return(float4(tex.sample(smp, i.uv).rgb, 1));
} }
bool is_top_left(float2 a, float2 b) bool is_top_left(float2 a, float2 b)
{ {
return( (a.y == b.y && b.x < a.x) return( (a.y == b.y && b.x < a.x)
||(b.y < a.y)); ||(b.y < a.y));
} }
kernel void BoundingBoxKernel(constant mg_vertex* vertexBuffer [[buffer(0)]], kernel void BoundingBoxKernel(constant mg_vertex* vertexBuffer [[buffer(0)]],
constant uint* indexBuffer [[buffer(1)]], constant uint* indexBuffer [[buffer(1)]],
device mg_triangle_data* triangleArray [[buffer(2)]], device mg_triangle_data* triangleArray [[buffer(2)]],
device float4* boxArray [[buffer(3)]], device float4* boxArray [[buffer(3)]],
constant float* contentsScaling [[buffer(4)]], constant float* contentsScaling [[buffer(4)]],
uint gid [[thread_position_in_grid]]) uint gid [[thread_position_in_grid]])
{ {
uint triangleIndex = gid; uint triangleIndex = gid;
uint vertexIndex = triangleIndex*3; uint vertexIndex = triangleIndex*3;
uint i0 = indexBuffer[vertexIndex]; uint i0 = indexBuffer[vertexIndex];
uint i1 = indexBuffer[vertexIndex+1]; uint i1 = indexBuffer[vertexIndex+1];
uint i2 = indexBuffer[vertexIndex+2]; uint i2 = indexBuffer[vertexIndex+2];
float2 p0 = vertexBuffer[i0].pos * contentsScaling[0]; float2 p0 = vertexBuffer[i0].pos * contentsScaling[0];
float2 p1 = vertexBuffer[i1].pos * contentsScaling[0]; float2 p1 = vertexBuffer[i1].pos * contentsScaling[0];
float2 p2 = vertexBuffer[i2].pos * contentsScaling[0]; float2 p2 = vertexBuffer[i2].pos * contentsScaling[0];
//NOTE(martin): compute triangle bounding box //NOTE(martin): compute triangle bounding box
float2 boxMin = min(min(p0, p1), p2); float2 boxMin = min(min(p0, p1), p2);
float2 boxMax = max(max(p0, p1), p2); float2 boxMax = max(max(p0, p1), p2);
//NOTE(martin): clip bounding box against clip rect //NOTE(martin): clip bounding box against clip rect
vector_float4 clip = contentsScaling[0]*vertexBuffer[indexBuffer[vertexIndex]].clip; vector_float4 clip = contentsScaling[0]*vertexBuffer[indexBuffer[vertexIndex]].clip;
float2 clipMin(clip.x, clip.y); float2 clipMin(clip.x, clip.y);
float2 clipMax(clip.x + clip.z-1, clip.y + clip.w-1); float2 clipMax(clip.x + clip.z-1, clip.y + clip.w-1);
//NOTE(martin): intersect with current clip //NOTE(martin): intersect with current clip
boxMin = max(boxMin, clipMin); boxMin = max(boxMin, clipMin);
boxMax = min(boxMax, clipMax); boxMax = min(boxMax, clipMax);
//NOTE(martin): reorder triangle counter-clockwise and compute bias for each edge //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; float cw = (p1 - p0).x*(p2 - p0).y - (p1 - p0).y*(p2 - p0).x;
if(cw < 0) if(cw < 0)
{ {
uint tmpIndex = i1; uint tmpIndex = i1;
i1 = i2; i1 = i2;
i2 = tmpIndex; i2 = tmpIndex;
float2 tmpPoint = p1; float2 tmpPoint = p1;
p1 = p2; p1 = p2;
p2 = tmpPoint; p2 = tmpPoint;
} }
int bias0 = is_top_left(p1, p2) ? 0 : -1; int bias0 = is_top_left(p1, p2) ? 0 : -1;
int bias1 = is_top_left(p2, p0) ? 0 : -1; int bias1 = is_top_left(p2, p0) ? 0 : -1;
int bias2 = is_top_left(p0, p1) ? 0 : -1; int bias2 = is_top_left(p0, p1) ? 0 : -1;
//NOTE(martin): fill triangle data //NOTE(martin): fill triangle data
boxArray[triangleIndex] = float4(boxMin.x, boxMin.y, boxMax.x, boxMax.y); boxArray[triangleIndex] = float4(boxMin.x, boxMin.y, boxMax.x, boxMax.y);
triangleArray[triangleIndex].zIndex = vertexBuffer[i0].zIndex; triangleArray[triangleIndex].zIndex = vertexBuffer[i0].zIndex;
triangleArray[triangleIndex].i0 = i0; triangleArray[triangleIndex].i0 = i0;
triangleArray[triangleIndex].i1 = i1; triangleArray[triangleIndex].i1 = i1;
triangleArray[triangleIndex].i2 = i2; triangleArray[triangleIndex].i2 = i2;
triangleArray[triangleIndex].p0 = p0; triangleArray[triangleIndex].p0 = p0;
triangleArray[triangleIndex].p1 = p1; triangleArray[triangleIndex].p1 = p1;
triangleArray[triangleIndex].p2 = p2; triangleArray[triangleIndex].p2 = p2;
triangleArray[triangleIndex].bias0 = bias0; triangleArray[triangleIndex].bias0 = bias0;
triangleArray[triangleIndex].bias1 = bias1; triangleArray[triangleIndex].bias1 = bias1;
triangleArray[triangleIndex].bias2 = bias2; triangleArray[triangleIndex].bias2 = bias2;
} }
kernel void TileKernel(const device float4* boxArray [[buffer(0)]], kernel void TileKernel(const device float4* boxArray [[buffer(0)]],
device volatile atomic_uint* tileCounters [[buffer(1)]], device volatile atomic_uint* tileCounters [[buffer(1)]],
device uint* tilesArray [[buffer(2)]], device uint* tilesArray [[buffer(2)]],
constant vector_uint2* viewport [[buffer(3)]], constant vector_uint2* viewport [[buffer(3)]],
uint gid [[thread_position_in_grid]]) uint gid [[thread_position_in_grid]])
{ {
uint2 tilesMatrixDim = (*viewport - 1) / RENDERER_TILE_SIZE + 1; uint2 tilesMatrixDim = (*viewport - 1) / RENDERER_TILE_SIZE + 1;
uint nTilesX = tilesMatrixDim.x; uint nTilesX = tilesMatrixDim.x;
uint nTilesY = tilesMatrixDim.y; uint nTilesY = tilesMatrixDim.y;
uint triangleIndex = gid; uint triangleIndex = gid;
uint4 box = uint4(floor(boxArray[triangleIndex]))/RENDERER_TILE_SIZE; uint4 box = uint4(floor(boxArray[triangleIndex]))/RENDERER_TILE_SIZE;
uint xMin = max((uint)0, box.x); uint xMin = max((uint)0, box.x);
uint yMin = max((uint)0, box.y); uint yMin = max((uint)0, box.y);
uint xMax = min(box.z, nTilesX-1); uint xMax = min(box.z, nTilesX-1);
uint yMax = min(box.w, nTilesY-1); uint yMax = min(box.w, nTilesY-1);
for(uint y = yMin; y <= yMax; y++) for(uint y = yMin; y <= yMax; y++)
{ {
for(uint x = xMin ; x <= xMax; x++) for(uint x = xMin ; x <= xMax; x++)
{ {
uint tileIndex = y*nTilesX + x; uint tileIndex = y*nTilesX + x;
device uint* tileBuffer = tilesArray + tileIndex*RENDERER_TILE_BUFFER_SIZE; device uint* tileBuffer = tilesArray + tileIndex*RENDERER_TILE_BUFFER_SIZE;
uint counter = atomic_fetch_add_explicit(&(tileCounters[tileIndex]), 1, memory_order_relaxed); uint counter = atomic_fetch_add_explicit(&(tileCounters[tileIndex]), 1, memory_order_relaxed);
tileBuffer[counter] = triangleIndex; tileBuffer[counter] = triangleIndex;
} }
} }
} }
kernel void SortKernel(const device uint* tileCounters [[buffer(0)]], kernel void SortKernel(const device uint* tileCounters [[buffer(0)]],
const device mg_triangle_data* triangleArray [[buffer(1)]], const device mg_triangle_data* triangleArray [[buffer(1)]],
device uint* tilesArray [[buffer(2)]], device uint* tilesArray [[buffer(2)]],
constant vector_uint2* viewport [[buffer(3)]], constant vector_uint2* viewport [[buffer(3)]],
uint gid [[thread_position_in_grid]]) uint gid [[thread_position_in_grid]])
{ {
uint tileIndex = gid; uint tileIndex = gid;
device uint* tileBuffer = tilesArray + tileIndex*RENDERER_TILE_BUFFER_SIZE; device uint* tileBuffer = tilesArray + tileIndex*RENDERER_TILE_BUFFER_SIZE;
uint tileBufferSize = tileCounters[tileIndex]; uint tileBufferSize = tileCounters[tileIndex];
for(int eltIndex=0; eltIndex < (int)tileBufferSize; eltIndex++) for(int eltIndex=0; eltIndex < (int)tileBufferSize; eltIndex++)
{ {
uint elt = tileBuffer[eltIndex]; uint elt = tileBuffer[eltIndex];
uint eltZIndex = triangleArray[elt].zIndex; uint eltZIndex = triangleArray[elt].zIndex;
int backIndex = eltIndex-1; int backIndex = eltIndex-1;
for(; backIndex >= 0; backIndex--) for(; backIndex >= 0; backIndex--)
{ {
uint backElt = tileBuffer[backIndex]; uint backElt = tileBuffer[backIndex];
uint backEltZIndex = triangleArray[backElt].zIndex; uint backEltZIndex = triangleArray[backElt].zIndex;
if(eltZIndex >= backEltZIndex) if(eltZIndex >= backEltZIndex)
{ {
break; break;
} }
else else
{ {
tileBuffer[backIndex+1] = backElt; tileBuffer[backIndex+1] = backElt;
} }
} }
tileBuffer[backIndex+1] = elt; tileBuffer[backIndex+1] = elt;
} }
} }
float orient2d(float2 a, float2 b, float2 c) float orient2d(float2 a, float2 b, float2 c)
{ {
////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////
//TODO(martin): FIX this. This is a **horrible** quick hack to fix the precision issues //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 // 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 // are big. The proper solution is to change the expression to avoid
// precision loss but I'm too busy/lazy to do it now. // precision loss but I'm too busy/lazy to do it now.
////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////
a *= 10; a *= 10;
b *= 10; b *= 10;
c *= 10; c *= 10;
return((b.x-a.x)*(c.y-a.y) - (b.y-a.y)*(c.x-a.x)); 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)]], kernel void RenderKernel(texture2d<float, access::write> outTexture [[texture(0)]],
texture2d<float> texAtlas [[texture(1)]], texture2d<float> texAtlas [[texture(1)]],
const device mg_vertex* vertexBuffer [[buffer(0)]], const device mg_vertex* vertexBuffer [[buffer(0)]],
device uint* tileCounters [[buffer(1)]], device uint* tileCounters [[buffer(1)]],
const device uint* tilesArray [[buffer(2)]], const device uint* tilesArray [[buffer(2)]],
const device mg_triangle_data* triangleArray [[buffer(3)]], const device mg_triangle_data* triangleArray [[buffer(3)]],
const device float4* boxArray [[buffer(4)]], const device float4* boxArray [[buffer(4)]],
constant vector_float4* clearColor [[buffer(5)]], constant vector_float4* clearColor [[buffer(5)]],
uint2 gid [[thread_position_in_grid]], uint2 gid [[thread_position_in_grid]],
uint2 tgid [[threadgroup_position_in_grid]], uint2 tgid [[threadgroup_position_in_grid]],
uint2 threadsPerThreadgroup [[threads_per_threadgroup]], uint2 threadsPerThreadgroup [[threads_per_threadgroup]],
uint2 gridSize [[threads_per_grid]]) uint2 gridSize [[threads_per_grid]])
{ {
//TODO: guard against thread group size not equal to tile size? //TODO: guard against thread group size not equal to tile size?
const uint2 tilesMatrixDim = (gridSize - 1) / RENDERER_TILE_SIZE + 1; const uint2 tilesMatrixDim = (gridSize - 1) / RENDERER_TILE_SIZE + 1;
// const uint2 tilePos = tgid * threadsPerThreadgroup / RENDERER_TILE_SIZE; // const uint2 tilePos = tgid * threadsPerThreadgroup / RENDERER_TILE_SIZE;
const uint2 tilePos = gid/RENDERER_TILE_SIZE; const uint2 tilePos = gid/RENDERER_TILE_SIZE;
const uint tileIndex = tilePos.y * tilesMatrixDim.x + tilePos.x; const uint tileIndex = tilePos.y * tilesMatrixDim.x + tilePos.x;
const device uint* tileBuffer = tilesArray + tileIndex * RENDERER_TILE_BUFFER_SIZE; const device uint* tileBuffer = tilesArray + tileIndex * RENDERER_TILE_BUFFER_SIZE;
const uint tileBufferSize = tileCounters[tileIndex]; const uint tileBufferSize = tileCounters[tileIndex];
//#define RENDERER_DEBUG_TILES //#define RENDERER_DEBUG_TILES
#ifdef RENDERER_DEBUG_TILES #ifdef RENDERER_DEBUG_TILES
//NOTE(martin): color code debug values and show the tile grid //NOTE(martin): color code debug values and show the tile grid
uint nTileX = tilesMatrixDim.x; uint nTileX = tilesMatrixDim.x;
uint nTileY = tilesMatrixDim.y; uint nTileY = tilesMatrixDim.y;
if(tilePos.x == 2 && tilePos.y == 12) if(tilePos.x == 2 && tilePos.y == 12)
{ {
outTexture.write(float4(1, 0.5, 1, 1), gid); outTexture.write(float4(1, 0.5, 1, 1), gid);
return; return;
} }
if(nTileY != 13 || nTileX != 13) if(nTileY != 13 || nTileX != 13)
{ {
outTexture.write(float4(1, 1, 0, 1), gid); outTexture.write(float4(1, 1, 0, 1), gid);
return; return;
} }
if(tilePos.x > nTileX || tilePos.y > nTileY) if(tilePos.x > nTileX || tilePos.y > nTileY)
{ {
outTexture.write(float4(0, 1, 1, 1), gid); outTexture.write(float4(0, 1, 1, 1), gid);
return; return;
} }
if((gid.x % RENDERER_TILE_SIZE == 0) || (gid.y % RENDERER_TILE_SIZE == 0)) if((gid.x % RENDERER_TILE_SIZE == 0) || (gid.y % RENDERER_TILE_SIZE == 0))
{ {
outTexture.write(float4(0, 0, 0, 1), gid); outTexture.write(float4(0, 0, 0, 1), gid);
return; return;
} }
if(tileBufferSize <= 0) if(tileBufferSize <= 0)
{ {
outTexture.write(float4(0, 1, 0, 1), gid); outTexture.write(float4(0, 1, 0, 1), gid);
return; return;
} }
else else
{ {
outTexture.write(float4(1, 0, 0, 1), gid); outTexture.write(float4(1, 0, 0, 1), gid);
return; return;
} }
#endif #endif
const float2 sampleOffsets[6] = { float2(5./12, 5./12), const float2 sampleOffsets[6] = { float2(5./12, 5./12),
float2(-3./12, 3./12), float2(-3./12, 3./12),
float2(1./12, 1./12), float2(1./12, 1./12),
float2(3./12, -1./12), float2(3./12, -1./12),
float2(-5./12, -3./12), float2(-5./12, -3./12),
float2(-1./12, -5./12)}; float2(-1./12, -5./12)};
int zIndices[6]; int zIndices[6];
uint flipCounts[6]; uint flipCounts[6];
float4 pixelColors[6]; float4 pixelColors[6];
float4 nextColors[6]; float4 nextColors[6];
for(int i=0; i<6; i++) for(int i=0; i<6; i++)
{ {
zIndices[i] = -1; zIndices[i] = -1;
flipCounts[i] = 0; flipCounts[i] = 0;
pixelColors[i] = *clearColor; pixelColors[i] = *clearColor;
nextColors[i] = *clearColor; nextColors[i] = *clearColor;
} }
for(uint tileBufferIndex=0; tileBufferIndex < tileBufferSize; tileBufferIndex++) for(uint tileBufferIndex=0; tileBufferIndex < tileBufferSize; tileBufferIndex++)
{ {
float4 box = boxArray[tileBuffer[tileBufferIndex]]; float4 box = boxArray[tileBuffer[tileBufferIndex]];
const device mg_triangle_data* triangle = &triangleArray[tileBuffer[tileBufferIndex]]; const device mg_triangle_data* triangle = &triangleArray[tileBuffer[tileBufferIndex]];
float2 p0 = triangle->p0; float2 p0 = triangle->p0;
float2 p1 = triangle->p1; float2 p1 = triangle->p1;
float2 p2 = triangle->p2; float2 p2 = triangle->p2;
int bias0 = triangle->bias0; int bias0 = triangle->bias0;
int bias1 = triangle->bias1; int bias1 = triangle->bias1;
int bias2 = triangle->bias2; int bias2 = triangle->bias2;
const device mg_vertex* v0 = &(vertexBuffer[triangle->i0]); const device mg_vertex* v0 = &(vertexBuffer[triangle->i0]);
const device mg_vertex* v1 = &(vertexBuffer[triangle->i1]); const device mg_vertex* v1 = &(vertexBuffer[triangle->i1]);
const device mg_vertex* v2 = &(vertexBuffer[triangle->i2]); const device mg_vertex* v2 = &(vertexBuffer[triangle->i2]);
float4 cubic0 = v0->cubic; float4 cubic0 = v0->cubic;
float4 cubic1 = v1->cubic; float4 cubic1 = v1->cubic;
float4 cubic2 = v2->cubic; float4 cubic2 = v2->cubic;
float2 uv0 = v0->uv; float2 uv0 = v0->uv;
float2 uv1 = v1->uv; float2 uv1 = v1->uv;
float2 uv2 = v2->uv; float2 uv2 = v2->uv;
int zIndex = v0->zIndex; int zIndex = v0->zIndex;
float4 color = v0->color; float4 color = v0->color;
for(int i=0; i<6; i++) for(int i=0; i<6; i++)
{ {
float2 samplePoint = (float2)gid + sampleOffsets[i]; float2 samplePoint = (float2)gid + sampleOffsets[i];
//NOTE(martin): cull if pixel is outside box //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) if(samplePoint.x < box.x || samplePoint.x > box.z || samplePoint.y < box.y || samplePoint.y > box.w)
{ {
continue; continue;
} }
float w0 = orient2d(p1, p2, samplePoint); float w0 = orient2d(p1, p2, samplePoint);
float w1 = orient2d(p2, p0, samplePoint); float w1 = orient2d(p2, p0, samplePoint);
float w2 = orient2d(p0, p1, samplePoint); float w2 = orient2d(p0, p1, samplePoint);
if(((int)w0+bias0) >= 0 && ((int)w1+bias1) >= 0 && ((int)w2+bias2) >= 0) if(((int)w0+bias0) >= 0 && ((int)w1+bias1) >= 0 && ((int)w2+bias2) >= 0)
{ {
float4 cubic = (cubic0*w0 + cubic1*w1 + cubic2*w2)/(w0+w1+w2); float4 cubic = (cubic0*w0 + cubic1*w1 + cubic2*w2)/(w0+w1+w2);
float2 uv = (uv0*w0 + uv1*w1 + uv2*w2)/(w0+w1+w2); float2 uv = (uv0*w0 + uv1*w1 + uv2*w2)/(w0+w1+w2);
constexpr sampler smp(mip_filter::nearest, mag_filter::linear, min_filter::linear); constexpr sampler smp(mip_filter::nearest, mag_filter::linear, min_filter::linear);
float4 texColor = texAtlas.sample(smp, uv); float4 texColor = texAtlas.sample(smp, uv);
//TODO(martin): this is a quick and dirty fix for solid polygons where we use //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 // cubic = (1, 1, 1, 1) on all vertices, which can cause small errors to
// flip the sign. // flip the sign.
// We should really use another value that always lead to <= 0, but we must // We should really use another value that always lead to <= 0, but we must
// make sure we never share these vertices with bezier shapes. // make sure we never share these vertices with bezier shapes.
// Alternatively, an ugly (but maybe less than this one) solution would be // 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 // to check if uvs are equal on all vertices of the triangle and always render
// those triangles. // those triangles.
float eps = 0.0001; float eps = 0.0001;
if(cubic.w*(cubic.x*cubic.x*cubic.x - cubic.y*cubic.z) <= eps) if(cubic.w*(cubic.x*cubic.x*cubic.x - cubic.y*cubic.z) <= eps)
{ {
if(zIndex == zIndices[i]) if(zIndex == zIndices[i])
{ {
flipCounts[i]++; flipCounts[i]++;
} }
else else
{ {
if(flipCounts[i] & 0x01) if(flipCounts[i] & 0x01)
{ {
pixelColors[i] = nextColors[i]; pixelColors[i] = nextColors[i];
} }
float4 nextCol = color*texColor; float4 nextCol = color*texColor;
nextColors[i] = pixelColors[i]*(1-nextCol.a) +nextCol.a*nextCol; nextColors[i] = pixelColors[i]*(1-nextCol.a) +nextCol.a*nextCol;
zIndices[i] = zIndex; zIndices[i] = zIndex;
flipCounts[i] = 1; flipCounts[i] = 1;
} }
} }
} }
} }
} }
float4 out = float4(0, 0, 0, 0); float4 out = float4(0, 0, 0, 0);
for(int i=0; i<6; i++) for(int i=0; i<6; i++)
{ {
if(flipCounts[i] & 0x01) if(flipCounts[i] & 0x01)
{ {
pixelColors[i] = nextColors[i]; pixelColors[i] = nextColors[i];
} }
out += pixelColors[i]; out += pixelColors[i];
} }
out = float4(out.xyz/6, 1); out = float4(out.xyz/6, 1);
outTexture.write(out, gid); outTexture.write(out, gid);
} }

View File

@ -54,6 +54,7 @@
#include"win32_app.c" #include"win32_app.c"
// #include"win32_gl_surface.c" // #include"win32_gl_surface.c"
#include"win32_gles_surface.c" #include"win32_gles_surface.c"
#include"gles_canvas.c"
#elif defined(OS_MACOS) #elif defined(OS_MACOS)
//NOTE: macos application layer is defined in milepost.m //NOTE: macos application layer is defined in milepost.m
#else #else

View File

@ -40,8 +40,17 @@
#if defined(OS_WIN64) || defined(OS_WIN32) #if defined(OS_WIN64) || defined(OS_WIN32)
#define WIN32_GL_LOADER_API #define WIN32_GL_LOADER_API
// #include"win32_gl_loader.h" // #include"win32_gl_loader.h"
#if MG_IMPLEMENTS_BACKEND_GLES
#include"win32_gles_surface.h"
#endif
#endif #endif
#if MG_IMPLEMENTS_BACKEND_METAL
#include"metal_surface.h"
#endif
//---------------------------------------------------------------- //----------------------------------------------------------------
// graphics/ui layer // graphics/ui layer
//---------------------------------------------------------------- //----------------------------------------------------------------

View File

@ -1,349 +1,349 @@
/************************************************************//** /************************************************************//**
* *
* @file: mp_app_internal.c * @file: mp_app_internal.c
* @author: Martin Fouilleul * @author: Martin Fouilleul
* @date: 23/12/2022 * @date: 23/12/2022
* @revision: * @revision:
* *
*****************************************************************/ *****************************************************************/
#include"mp_app_internal.h" #include"mp_app_internal.h"
#define LOG_SUBSYSTEM "Application" #define LOG_SUBSYSTEM "Application"
mp_app __mpApp = {0}; mp_app __mpApp = {0};
//--------------------------------------------------------------- //---------------------------------------------------------------
// Window handles // Window handles
//--------------------------------------------------------------- //---------------------------------------------------------------
void mp_init_window_handles() void mp_init_window_handles()
{ {
ListInit(&__mpApp.windowFreeList); ListInit(&__mpApp.windowFreeList);
for(int i=0; i<MP_APP_MAX_WINDOWS; i++) for(int i=0; i<MP_APP_MAX_WINDOWS; i++)
{ {
__mpApp.windowPool[i].generation = 1; __mpApp.windowPool[i].generation = 1;
ListAppend(&__mpApp.windowFreeList, &__mpApp.windowPool[i].freeListElt); ListAppend(&__mpApp.windowFreeList, &__mpApp.windowPool[i].freeListElt);
} }
} }
bool mp_window_handle_is_null(mp_window window) bool mp_window_handle_is_null(mp_window window)
{ {
return(window.h == 0); return(window.h == 0);
} }
mp_window mp_window_null_handle() mp_window mp_window_null_handle()
{ {
return((mp_window){.h = 0}); return((mp_window){.h = 0});
} }
mp_window_data* mp_window_alloc() mp_window_data* mp_window_alloc()
{ {
return(ListPopEntry(&__mpApp.windowFreeList, mp_window_data, freeListElt)); return(ListPopEntry(&__mpApp.windowFreeList, mp_window_data, freeListElt));
} }
mp_window_data* mp_window_ptr_from_handle(mp_window handle) mp_window_data* mp_window_ptr_from_handle(mp_window handle)
{ {
u32 index = handle.h>>32; u32 index = handle.h>>32;
u32 generation = handle.h & 0xffffffff; u32 generation = handle.h & 0xffffffff;
if(index >= MP_APP_MAX_WINDOWS) if(index >= MP_APP_MAX_WINDOWS)
{ {
return(0); return(0);
} }
mp_window_data* window = &__mpApp.windowPool[index]; mp_window_data* window = &__mpApp.windowPool[index];
if(window->generation != generation) if(window->generation != generation)
{ {
return(0); return(0);
} }
else else
{ {
return(window); return(window);
} }
} }
mp_window mp_window_handle_from_ptr(mp_window_data* window) mp_window mp_window_handle_from_ptr(mp_window_data* window)
{ {
DEBUG_ASSERT( (window - __mpApp.windowPool) >= 0 DEBUG_ASSERT( (window - __mpApp.windowPool) >= 0
&& (window - __mpApp.windowPool) < MP_APP_MAX_WINDOWS); && (window - __mpApp.windowPool) < MP_APP_MAX_WINDOWS);
u64 h = ((u64)(window - __mpApp.windowPool))<<32 u64 h = ((u64)(window - __mpApp.windowPool))<<32
| ((u64)window->generation); | ((u64)window->generation);
return((mp_window){h}); return((mp_window){h});
} }
void mp_window_recycle_ptr(mp_window_data* window) void mp_window_recycle_ptr(mp_window_data* window)
{ {
window->generation++; window->generation++;
ListPush(&__mpApp.windowFreeList, &window->freeListElt); ListPush(&__mpApp.windowFreeList, &window->freeListElt);
} }
//--------------------------------------------------------------- //---------------------------------------------------------------
// Init // Init
//--------------------------------------------------------------- //---------------------------------------------------------------
static void mp_init_common() static void mp_init_common()
{ {
mp_init_window_handles(); mp_init_window_handles();
ringbuffer_init(&__mpApp.eventQueue, 16); ringbuffer_init(&__mpApp.eventQueue, 16);
} }
static void mp_terminate_common() static void mp_terminate_common()
{ {
ringbuffer_cleanup(&__mpApp.eventQueue); ringbuffer_cleanup(&__mpApp.eventQueue);
} }
//--------------------------------------------------------------- //---------------------------------------------------------------
// Event handling // Event handling
//--------------------------------------------------------------- //---------------------------------------------------------------
void mp_queue_event(mp_event* event) void mp_queue_event(mp_event* event)
{ {
if(ringbuffer_write_available(&__mpApp.eventQueue) < sizeof(mp_event)) if(ringbuffer_write_available(&__mpApp.eventQueue) < sizeof(mp_event))
{ {
LOG_ERROR("event queue full\n"); LOG_ERROR("event queue full\n");
} }
else else
{ {
u32 written = ringbuffer_write(&__mpApp.eventQueue, sizeof(mp_event), (u8*)event); u32 written = ringbuffer_write(&__mpApp.eventQueue, sizeof(mp_event), (u8*)event);
DEBUG_ASSERT(written == sizeof(mp_event)); DEBUG_ASSERT(written == sizeof(mp_event));
} }
} }
bool mp_next_event(mp_event* event) bool mp_next_event(mp_event* event)
{ {
//NOTE pop and return event from queue //NOTE pop and return event from queue
if(ringbuffer_read_available(&__mpApp.eventQueue) >= sizeof(mp_event)) if(ringbuffer_read_available(&__mpApp.eventQueue) >= sizeof(mp_event))
{ {
u64 read = ringbuffer_read(&__mpApp.eventQueue, sizeof(mp_event), (u8*)event); u64 read = ringbuffer_read(&__mpApp.eventQueue, sizeof(mp_event), (u8*)event);
DEBUG_ASSERT(read == sizeof(mp_event)); DEBUG_ASSERT(read == sizeof(mp_event));
return(true); return(true);
} }
else else
{ {
return(false); return(false);
} }
} }
//--------------------------------------------------------------- //---------------------------------------------------------------
// Input state updating // Input state updating
//--------------------------------------------------------------- //---------------------------------------------------------------
static void mp_update_key_state(mp_key_state* key, bool down) static void mp_update_key_state(mp_key_state* key, bool down)
{ {
u64 frameCounter = __mpApp.inputState.frameCounter; u64 frameCounter = __mpApp.inputState.frameCounter;
if(key->lastUpdate != frameCounter) if(key->lastUpdate != frameCounter)
{ {
key->transitionCounter = 0; key->transitionCounter = 0;
key->clicked = false; key->clicked = false;
key->doubleClicked = false; key->doubleClicked = false;
key->lastUpdate = frameCounter; key->lastUpdate = frameCounter;
} }
if(key->down == down) if(key->down == down)
{ {
key->transitionCounter++; key->transitionCounter++;
} }
key->down = down; key->down = down;
} }
static void mp_update_mouse_move(f32 x, f32 y, f32 deltaX, f32 deltaY) static void mp_update_mouse_move(f32 x, f32 y, f32 deltaX, f32 deltaY)
{ {
u64 frameCounter = __mpApp.inputState.frameCounter; u64 frameCounter = __mpApp.inputState.frameCounter;
mp_mouse_state* mouse = &__mpApp.inputState.mouse; mp_mouse_state* mouse = &__mpApp.inputState.mouse;
if(mouse->lastUpdate != frameCounter) if(mouse->lastUpdate != frameCounter)
{ {
mouse->delta = (vec2){0, 0}; mouse->delta = (vec2){0, 0};
mouse->wheel = (vec2){0, 0}; mouse->wheel = (vec2){0, 0};
mouse->lastUpdate = frameCounter; mouse->lastUpdate = frameCounter;
} }
mouse->pos = (vec2){x, y}; mouse->pos = (vec2){x, y};
mouse->delta.x += deltaX; mouse->delta.x += deltaX;
mouse->delta.y += deltaY; mouse->delta.y += deltaY;
} }
static void mp_update_mouse_wheel(f32 deltaX, f32 deltaY) static void mp_update_mouse_wheel(f32 deltaX, f32 deltaY)
{ {
u64 frameCounter = __mpApp.inputState.frameCounter; u64 frameCounter = __mpApp.inputState.frameCounter;
mp_mouse_state* mouse = &__mpApp.inputState.mouse; mp_mouse_state* mouse = &__mpApp.inputState.mouse;
if(mouse->lastUpdate != frameCounter) if(mouse->lastUpdate != frameCounter)
{ {
mouse->delta = (vec2){0, 0}; mouse->delta = (vec2){0, 0};
mouse->wheel = (vec2){0, 0}; mouse->wheel = (vec2){0, 0};
mouse->lastUpdate = frameCounter; mouse->lastUpdate = frameCounter;
} }
mouse->wheel.x += deltaX; mouse->wheel.x += deltaX;
mouse->wheel.y += deltaY; mouse->wheel.y += deltaY;
} }
static void mp_update_text(utf32 codepoint) static void mp_update_text(utf32 codepoint)
{ {
u64 frameCounter = __mpApp.inputState.frameCounter; u64 frameCounter = __mpApp.inputState.frameCounter;
mp_text_state* text = &__mpApp.inputState.text; mp_text_state* text = &__mpApp.inputState.text;
if(text->lastUpdate != frameCounter) if(text->lastUpdate != frameCounter)
{ {
text->codePoints.len = 0; text->codePoints.len = 0;
text->lastUpdate = frameCounter; text->lastUpdate = frameCounter;
} }
text->codePoints.ptr = text->backing; text->codePoints.ptr = text->backing;
if(text->codePoints.len < MP_INPUT_TEXT_BACKING_SIZE) if(text->codePoints.len < MP_INPUT_TEXT_BACKING_SIZE)
{ {
text->codePoints.ptr[text->codePoints.len] = codepoint; text->codePoints.ptr[text->codePoints.len] = codepoint;
text->codePoints.len++; text->codePoints.len++;
} }
else else
{ {
LOG_WARNING("too many input codepoints per frame, dropping input"); LOG_WARNING("too many input codepoints per frame, dropping input");
} }
} }
//-------------------------------------------------------------------- //--------------------------------------------------------------------
// Input state polling // Input state polling
//-------------------------------------------------------------------- //--------------------------------------------------------------------
mp_key_state mp_input_get_key_state(mp_key_code key) mp_key_state mp_input_get_key_state(mp_key_code key)
{ {
if(key <= MP_KEY_COUNT) if(key <= MP_KEY_COUNT)
{ {
return(__mpApp.inputState.keyboard.keys[key]); return(__mpApp.inputState.keyboard.keys[key]);
} }
else else
{ {
return((mp_key_state){0}); return((mp_key_state){0});
} }
} }
mp_key_state mp_input_get_mouse_button_state(mp_mouse_button button) mp_key_state mp_input_get_mouse_button_state(mp_mouse_button button)
{ {
if(button <= MP_MOUSE_BUTTON_COUNT) if(button <= MP_MOUSE_BUTTON_COUNT)
{ {
return(__mpApp.inputState.mouse.buttons[button]); return(__mpApp.inputState.mouse.buttons[button]);
} }
else else
{ {
return((mp_key_state){0}); return((mp_key_state){0});
} }
} }
bool mp_input_check_key_transition(mp_key_state* key, bool pressed) bool mp_input_check_key_transition(mp_key_state* key, bool pressed)
{ {
bool res = ( (key->lastUpdate == __mpApp.inputState.frameCounter) bool res = ( (key->lastUpdate == __mpApp.inputState.frameCounter)
&& key->transitionCounter && key->transitionCounter
&&(key->down == pressed || key->transitionCounter > 1)); &&(key->down == pressed || key->transitionCounter > 1));
return(res); return(res);
} }
bool mp_input_key_down(mp_key_code key) bool mp_input_key_down(mp_key_code key)
{ {
mp_key_state state = mp_input_get_key_state(key); mp_key_state state = mp_input_get_key_state(key);
return(state.down); return(state.down);
} }
bool mp_input_key_pressed(mp_key_code key) bool mp_input_key_pressed(mp_key_code key)
{ {
mp_key_state state = mp_input_get_key_state(key); mp_key_state state = mp_input_get_key_state(key);
bool res = mp_input_check_key_transition(&state, true); bool res = mp_input_check_key_transition(&state, true);
return(res); return(res);
} }
bool mp_input_key_released(mp_key_code key) bool mp_input_key_released(mp_key_code key)
{ {
mp_key_state state = mp_input_get_key_state(key); mp_key_state state = mp_input_get_key_state(key);
bool res = mp_input_check_key_transition(&state, false); bool res = mp_input_check_key_transition(&state, false);
return(res); return(res);
} }
bool mp_input_mouse_down(mp_mouse_button button) bool mp_input_mouse_down(mp_mouse_button button)
{ {
mp_key_state state = mp_input_get_mouse_button_state(button); mp_key_state state = mp_input_get_mouse_button_state(button);
return(state.down); return(state.down);
} }
bool mp_input_mouse_pressed(mp_mouse_button button) bool mp_input_mouse_pressed(mp_mouse_button button)
{ {
mp_key_state state = mp_input_get_mouse_button_state(button); mp_key_state state = mp_input_get_mouse_button_state(button);
bool res = mp_input_check_key_transition(&state, true); bool res = mp_input_check_key_transition(&state, true);
return(res); return(res);
} }
bool mp_input_mouse_released(mp_mouse_button button) bool mp_input_mouse_released(mp_mouse_button button)
{ {
mp_key_state state = mp_input_get_mouse_button_state(button); mp_key_state state = mp_input_get_mouse_button_state(button);
bool res = mp_input_check_key_transition(&state, false); bool res = mp_input_check_key_transition(&state, false);
return(res); return(res);
} }
bool mp_input_mouse_clicked(mp_mouse_button button) bool mp_input_mouse_clicked(mp_mouse_button button)
{ {
mp_key_state state = mp_input_get_mouse_button_state(button); mp_key_state state = mp_input_get_mouse_button_state(button);
return(state.clicked); return(state.clicked);
} }
bool mp_input_mouse_double_clicked(mp_mouse_button button) bool mp_input_mouse_double_clicked(mp_mouse_button button)
{ {
mp_key_state state = mp_input_get_mouse_button_state(button); mp_key_state state = mp_input_get_mouse_button_state(button);
if(state.lastUpdate == __mpApp.inputState.frameCounter) if(state.lastUpdate == __mpApp.inputState.frameCounter)
{ {
return(state.doubleClicked); return(state.doubleClicked);
} }
else else
{ {
return(false); return(false);
} }
} }
mp_key_mods mp_input_key_mods() mp_key_mods mp_input_key_mods()
{ {
return(__mpApp.inputState.keyboard.mods); return(__mpApp.inputState.keyboard.mods);
} }
vec2 mp_input_mouse_position() vec2 mp_input_mouse_position()
{ {
return(__mpApp.inputState.mouse.pos); return(__mpApp.inputState.mouse.pos);
} }
vec2 mp_input_mouse_delta() vec2 mp_input_mouse_delta()
{ {
if(__mpApp.inputState.mouse.lastUpdate == __mpApp.inputState.frameCounter) if(__mpApp.inputState.mouse.lastUpdate == __mpApp.inputState.frameCounter)
{ {
return(__mpApp.inputState.mouse.delta); return(__mpApp.inputState.mouse.delta);
} }
else else
{ {
return((vec2){0, 0}); return((vec2){0, 0});
} }
} }
vec2 mp_input_mouse_wheel() vec2 mp_input_mouse_wheel()
{ {
if(__mpApp.inputState.mouse.lastUpdate == __mpApp.inputState.frameCounter) if(__mpApp.inputState.mouse.lastUpdate == __mpApp.inputState.frameCounter)
{ {
return(__mpApp.inputState.mouse.wheel); return(__mpApp.inputState.mouse.wheel);
} }
else else
{ {
return((vec2){0, 0}); return((vec2){0, 0});
} }
} }
str32 mp_input_text_utf32(mem_arena* arena) str32 mp_input_text_utf32(mem_arena* arena)
{ {
str32 res = {0}; str32 res = {0};
if(__mpApp.inputState.text.lastUpdate == __mpApp.inputState.frameCounter) if(__mpApp.inputState.text.lastUpdate == __mpApp.inputState.frameCounter)
{ {
res = str32_push_copy(arena, __mpApp.inputState.text.codePoints); res = str32_push_copy(arena, __mpApp.inputState.text.codePoints);
} }
return(res); return(res);
} }
str8 mp_input_text_utf8(mem_arena* arena) str8 mp_input_text_utf8(mem_arena* arena)
{ {
str8 res = {0}; str8 res = {0};
if(__mpApp.inputState.text.lastUpdate == __mpApp.inputState.frameCounter) if(__mpApp.inputState.text.lastUpdate == __mpApp.inputState.frameCounter)
{ {
res = utf8_push_from_codepoints(arena, __mpApp.inputState.text.codePoints); res = utf8_push_from_codepoints(arena, __mpApp.inputState.text.codePoints);
} }
return(res); return(res);
} }
#undef LOG_SUBSYSTEM #undef LOG_SUBSYSTEM

View File

@ -1,152 +1,152 @@
/************************************************************//** /************************************************************//**
* *
* @file: mp_app_internal.h * @file: mp_app_internal.h
* @author: Martin Fouilleul * @author: Martin Fouilleul
* @date: 23/12/2022 * @date: 23/12/2022
* @revision: * @revision:
* *
*****************************************************************/ *****************************************************************/
#ifndef __MP_APP_INTERNAL_H_ #ifndef __MP_APP_INTERNAL_H_
#define __MP_APP_INTERNAL_H_ #define __MP_APP_INTERNAL_H_
#include"platform.h" #include"platform.h"
#include"ringbuffer.h" #include"ringbuffer.h"
#if defined(OS_WIN64) || defined(OS_WIN32) #if defined(OS_WIN64) || defined(OS_WIN32)
#include"win32_app.h" #include"win32_app.h"
#elif defined(OS_MACOS) #elif defined(OS_MACOS)
#include"osx_app.h" #include"osx_app.h"
#else #else
#error "platform not supported yet" #error "platform not supported yet"
#endif #endif
//--------------------------------------------------------------- //---------------------------------------------------------------
// Input State // Input State
//--------------------------------------------------------------- //---------------------------------------------------------------
typedef struct mp_key_utf8 typedef struct mp_key_utf8
{ {
u8 labelLen; u8 labelLen;
char label[8]; char label[8];
} mp_key_utf8; } mp_key_utf8;
typedef struct mp_key_state typedef struct mp_key_state
{ {
u64 lastUpdate; u64 lastUpdate;
u32 transitionCounter; u32 transitionCounter;
bool down; bool down;
bool clicked; bool clicked;
bool doubleClicked; bool doubleClicked;
} mp_key_state; } mp_key_state;
typedef struct mp_keyboard_state typedef struct mp_keyboard_state
{ {
mp_key_state keys[MP_KEY_COUNT]; mp_key_state keys[MP_KEY_COUNT];
mp_key_mods mods; mp_key_mods mods;
} mp_keyboard_state; } mp_keyboard_state;
typedef struct mp_mouse_state typedef struct mp_mouse_state
{ {
u64 lastUpdate; u64 lastUpdate;
bool posValid; bool posValid;
vec2 pos; vec2 pos;
vec2 delta; vec2 delta;
vec2 wheel; vec2 wheel;
union union
{ {
mp_key_state buttons[MP_MOUSE_BUTTON_COUNT]; mp_key_state buttons[MP_MOUSE_BUTTON_COUNT];
struct struct
{ {
mp_key_state left; mp_key_state left;
mp_key_state right; mp_key_state right;
mp_key_state middle; mp_key_state middle;
mp_key_state ext1; mp_key_state ext1;
mp_key_state ext2; mp_key_state ext2;
}; };
}; };
} mp_mouse_state; } mp_mouse_state;
enum { MP_INPUT_TEXT_BACKING_SIZE = 64 }; enum { MP_INPUT_TEXT_BACKING_SIZE = 64 };
typedef struct mp_text_state typedef struct mp_text_state
{ {
u64 lastUpdate; u64 lastUpdate;
utf32 backing[MP_INPUT_TEXT_BACKING_SIZE]; utf32 backing[MP_INPUT_TEXT_BACKING_SIZE];
str32 codePoints; str32 codePoints;
} mp_text_state; } mp_text_state;
typedef struct mp_input_state typedef struct mp_input_state
{ {
u64 frameCounter; u64 frameCounter;
mp_keyboard_state keyboard; mp_keyboard_state keyboard;
mp_mouse_state mouse; mp_mouse_state mouse;
mp_text_state text; mp_text_state text;
} mp_input_state; } mp_input_state;
//--------------------------------------------------------------- //---------------------------------------------------------------
// Window structure // Window structure
//--------------------------------------------------------------- //---------------------------------------------------------------
typedef struct mp_frame_stats typedef struct mp_frame_stats
{ {
f64 start; f64 start;
f64 workTime; f64 workTime;
f64 remainingTime; f64 remainingTime;
f64 targetFramePeriod; f64 targetFramePeriod;
} mp_frame_stats; } mp_frame_stats;
typedef struct mp_window_data typedef struct mp_window_data
{ {
list_elt freeListElt; list_elt freeListElt;
u32 generation; u32 generation;
mp_rect contentRect; mp_rect contentRect;
mp_rect frameRect; mp_rect frameRect;
mp_window_style style; mp_window_style style;
bool shouldClose; //TODO could be in status flags bool shouldClose; //TODO could be in status flags
bool hidden; bool hidden;
bool minimized; bool minimized;
MP_PLATFORM_WINDOW_DATA MP_PLATFORM_WINDOW_DATA
} mp_window_data; } mp_window_data;
//--------------------------------------------------------------- //---------------------------------------------------------------
// Global App State // Global App State
//--------------------------------------------------------------- //---------------------------------------------------------------
enum { MP_APP_MAX_WINDOWS = 128 }; enum { MP_APP_MAX_WINDOWS = 128 };
typedef struct mp_app typedef struct mp_app
{ {
bool init; bool init;
bool shouldQuit; bool shouldQuit;
bool minimized; bool minimized;
str8 pendingPathDrop; str8 pendingPathDrop;
mem_arena eventArena; mem_arena eventArena;
ringbuffer eventQueue; ringbuffer eventQueue;
mp_frame_stats frameStats; mp_frame_stats frameStats;
mp_window_data windowPool[MP_APP_MAX_WINDOWS]; mp_window_data windowPool[MP_APP_MAX_WINDOWS];
list_info windowFreeList; list_info windowFreeList;
mp_live_resize_callback liveResizeCallback; mp_live_resize_callback liveResizeCallback;
void* liveResizeData; void* liveResizeData;
mp_input_state inputState; mp_input_state inputState;
mp_key_utf8 keyLabels[256]; mp_key_utf8 keyLabels[512];
int keyCodes[256]; int keyCodes[512];
int nativeKeys[MP_KEY_COUNT]; int nativeKeys[MP_KEY_COUNT];
MP_PLATFORM_APP_DATA MP_PLATFORM_APP_DATA
} mp_app; } mp_app;
#endif // __MP_APP_INTERNAL_H_ #endif // __MP_APP_INTERNAL_H_

View File

@ -1,32 +1,32 @@
/************************************************************//** /************************************************************//**
* *
* @file: osx_gles_surface.cpp * @file: osx_gles_surface.cpp
* @author: Martin Fouilleul * @author: Martin Fouilleul
* @date: 18/08/2022 * @date: 18/08/2022
* @revision: * @revision:
* *
*****************************************************************/ *****************************************************************/
//#include<Cocoa/Cocoa.h> //#include<Cocoa/Cocoa.h>
//#include <Foundation/Foundation.h> //#include <Foundation/Foundation.h>
#include <QuartzCore/QuartzCore.h> #include <QuartzCore/QuartzCore.h>
#include<GLES3/gl32.h> #include<GLES3/gl3.h>
#define EGL_EGLEXT_PROTOTYPES #define EGL_EGLEXT_PROTOTYPES
#include<EGL/egl.h> #include<EGL/egl.h>
#include<EGL/eglext.h> #include<EGL/eglext.h>
#include"graphics_internal.h" #include"graphics_internal.h"
typedef struct mg_gles_surface typedef struct mg_gles_surface
{ {
mg_surface_data interface; mg_surface_data interface;
NSView* view; NSView* view;
CALayer* layer; CALayer* layer;
EGLDisplay eglDisplay; EGLDisplay eglDisplay;
EGLConfig eglConfig; EGLConfig eglConfig;
EGLContext eglContext; EGLContext eglContext;
EGLSurface eglSurface; EGLSurface eglSurface;
EGLint numConfigs; EGLint numConfigs;
} mg_gles_surface; } mg_gles_surface;

View File

@ -1,184 +1,184 @@
/************************************************************//** /************************************************************//**
* *
* @file: osx_gles_surface.cpp * @file: osx_gles_surface.cpp
* @author: Martin Fouilleul * @author: Martin Fouilleul
* @date: 18/08/2022 * @date: 18/08/2022
* @revision: * @revision:
* *
*****************************************************************/ *****************************************************************/
//#include<Cocoa/Cocoa.h> //#include<Cocoa/Cocoa.h>
//#include <Foundation/Foundation.h> //#include <Foundation/Foundation.h>
#include <QuartzCore/QuartzCore.h> #include <QuartzCore/QuartzCore.h>
#include<GLES3/gl32.h> #include<GLES3/gl3.h>
#define EGL_EGLEXT_PROTOTYPES #define EGL_EGLEXT_PROTOTYPES
#include<EGL/egl.h> #include<EGL/egl.h>
#include<EGL/eglext.h> #include<EGL/eglext.h>
#include"graphics_internal.h" #include"graphics_internal.h"
#include"osx_gles_surface.h" #include"osx_gles_surface.h"
void mg_gles_surface_destroy(mg_surface_data* interface) void mg_gles_surface_destroy(mg_surface_data* interface)
{ {
////////////////////////////////////////////////// //////////////////////////////////////////////////
//TODO //TODO
////////////////////////////////////////////////// //////////////////////////////////////////////////
} }
void mg_gles_surface_prepare(mg_surface_data* interface) void mg_gles_surface_prepare(mg_surface_data* interface)
{ {
mg_gles_surface* surface = (mg_gles_surface*)interface; mg_gles_surface* surface = (mg_gles_surface*)interface;
eglMakeCurrent(surface->eglDisplay, surface->eglSurface, surface->eglSurface, surface->eglContext); eglMakeCurrent(surface->eglDisplay, surface->eglSurface, surface->eglSurface, surface->eglContext);
} }
void mg_gles_surface_present(mg_surface_data* interface) void mg_gles_surface_present(mg_surface_data* interface)
{ {
//TODO: eglSwapBuffers seem to never block in macOS (ie eglSwapInterval doesn't seem to have any effect) //TODO: eglSwapBuffers seem to never block in macOS (ie eglSwapInterval doesn't seem to have any effect)
// We need to use a CVDisplayLink to time this if we want surface present to block // We need to use a CVDisplayLink to time this if we want surface present to block
mg_gles_surface* surface = (mg_gles_surface*)interface; mg_gles_surface* surface = (mg_gles_surface*)interface;
eglSwapBuffers(surface->eglDisplay, surface->eglSurface); eglSwapBuffers(surface->eglDisplay, surface->eglSurface);
} }
void mg_gles_surface_set_frame(mg_surface_data* interface, mp_rect frame) void mg_gles_surface_set_frame(mg_surface_data* interface, mp_rect frame)
{ {
mg_gles_surface* surface = (mg_gles_surface*)interface; mg_gles_surface* surface = (mg_gles_surface*)interface;
f32 scale = surface->layer.contentsScale; f32 scale = surface->layer.contentsScale;
CGRect bounds = (CGRect){{frame.x * scale, frame.y * scale}, {.width = frame.w*scale, .height = frame.h*scale}}; CGRect bounds = (CGRect){{frame.x * scale, frame.y * scale}, {.width = frame.w*scale, .height = frame.h*scale}};
[surface->layer setBounds: bounds]; [surface->layer setBounds: bounds];
} }
mp_rect mg_gles_surface_get_frame(mg_surface_data* interface) mp_rect mg_gles_surface_get_frame(mg_surface_data* interface)
{ {
mg_gles_surface* surface = (mg_gles_surface*)interface; mg_gles_surface* surface = (mg_gles_surface*)interface;
f32 scale = surface->layer.contentsScale; f32 scale = surface->layer.contentsScale;
CGRect bounds = [surface->layer bounds]; CGRect bounds = [surface->layer bounds];
mp_rect rect = {bounds.origin.x / scale, bounds.origin.y / scale, bounds.size.width / scale, bounds.size.height / scale}; mp_rect rect = {bounds.origin.x / scale, bounds.origin.y / scale, bounds.size.width / scale, bounds.size.height / scale};
return(rect); return(rect);
} }
void mg_gles_surface_set_hidden(mg_surface_data* interface, bool hidden) void mg_gles_surface_set_hidden(mg_surface_data* interface, bool hidden)
{ {
//TODO: doesn't make sense for an offscreen surface? //TODO: doesn't make sense for an offscreen surface?
} }
bool mg_gles_surface_get_hidden(mg_surface_data* interface) bool mg_gles_surface_get_hidden(mg_surface_data* interface)
{ {
//TODO: doesn't make sense for an offscreen surface? //TODO: doesn't make sense for an offscreen surface?
return(false); return(false);
} }
vec2 mg_gles_surface_get_size(mg_surface_data* interface) vec2 mg_gles_surface_get_size(mg_surface_data* interface)
{ {
mg_gles_surface* surface = (mg_gles_surface*)interface; mg_gles_surface* surface = (mg_gles_surface*)interface;
CGRect bounds = [surface->layer bounds]; CGRect bounds = [surface->layer bounds];
f32 scale = surface->layer.contentsScale; f32 scale = surface->layer.contentsScale;
vec2 res = {bounds.size.width/scale, bounds.size.height/scale}; vec2 res = {bounds.size.width/scale, bounds.size.height/scale};
return(res); return(res);
} }
mg_surface mg_gles_surface_create_with_view(u32 width, u32 height, NSView* view) mg_surface mg_gles_surface_create_with_view(u32 width, u32 height, NSView* view)
{ {
mg_gles_surface* surface = malloc_type(mg_gles_surface); mg_gles_surface* surface = malloc_type(mg_gles_surface);
memset(surface, 0, sizeof(mg_gles_surface)); memset(surface, 0, sizeof(mg_gles_surface));
surface->interface.backend = MG_BACKEND_GLES; surface->interface.backend = MG_BACKEND_GLES;
surface->interface.destroy = mg_gles_surface_destroy; surface->interface.destroy = mg_gles_surface_destroy;
surface->interface.prepare = mg_gles_surface_prepare; surface->interface.prepare = mg_gles_surface_prepare;
surface->interface.present = mg_gles_surface_present; surface->interface.present = mg_gles_surface_present;
surface->interface.getFrame = mg_gles_surface_get_frame; surface->interface.getFrame = mg_gles_surface_get_frame;
surface->interface.setFrame = mg_gles_surface_set_frame; surface->interface.setFrame = mg_gles_surface_set_frame;
surface->interface.getHidden = mg_gles_surface_get_hidden; surface->interface.getHidden = mg_gles_surface_get_hidden;
surface->interface.setHidden = mg_gles_surface_set_hidden; surface->interface.setHidden = mg_gles_surface_set_hidden;
surface->view = view; surface->view = view;
@autoreleasepool @autoreleasepool
{ {
surface->layer = [[CALayer alloc] init]; surface->layer = [[CALayer alloc] init];
[surface->layer retain]; [surface->layer retain];
[surface->layer setBounds:CGRectMake(0, 0, width, height)]; [surface->layer setBounds:CGRectMake(0, 0, width, height)];
if(surface->view) if(surface->view)
{ {
[surface->view setWantsLayer: YES]; [surface->view setWantsLayer: YES];
surface->view.layer = surface->layer; surface->view.layer = surface->layer;
} }
} }
EGLAttrib displayAttribs[] = { EGLAttrib displayAttribs[] = {
//NOTE: we need to explicitly set EGL_PLATFORM_ANGLE_TYPE_ANGLE to EGL_PLATFORM_ANGLE_TYPE_METAL_ANGLE, because //NOTE: we need to explicitly set EGL_PLATFORM_ANGLE_TYPE_ANGLE to EGL_PLATFORM_ANGLE_TYPE_METAL_ANGLE, because
// EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE defaults to using CGL, and eglSetSwapInterval is broken for this backend // EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE defaults to using CGL, and eglSetSwapInterval is broken for this backend
EGL_PLATFORM_ANGLE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_TYPE_METAL_ANGLE, EGL_PLATFORM_ANGLE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_TYPE_METAL_ANGLE,
EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_DEVICE_TYPE_HARDWARE_ANGLE, EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_DEVICE_TYPE_HARDWARE_ANGLE,
EGL_NONE}; EGL_NONE};
surface->eglDisplay = eglGetPlatformDisplay(EGL_PLATFORM_ANGLE_ANGLE, (void*)EGL_DEFAULT_DISPLAY, displayAttribs); surface->eglDisplay = eglGetPlatformDisplay(EGL_PLATFORM_ANGLE_ANGLE, (void*)EGL_DEFAULT_DISPLAY, displayAttribs);
eglInitialize(surface->eglDisplay, NULL, NULL); eglInitialize(surface->eglDisplay, NULL, NULL);
EGLint const configAttributes[] = { EGLint const configAttributes[] = {
EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_RED_SIZE, 8, EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8, EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8, EGL_BLUE_SIZE, 8,
EGL_ALPHA_SIZE, 8, EGL_ALPHA_SIZE, 8,
EGL_DEPTH_SIZE, 24, EGL_DEPTH_SIZE, 24,
EGL_STENCIL_SIZE, 8, EGL_STENCIL_SIZE, 8,
EGL_SAMPLE_BUFFERS, 0, EGL_SAMPLE_BUFFERS, 0,
EGL_SAMPLES, EGL_DONT_CARE, EGL_SAMPLES, EGL_DONT_CARE,
EGL_COLOR_COMPONENT_TYPE_EXT, EGL_COLOR_COMPONENT_TYPE_FIXED_EXT, EGL_COLOR_COMPONENT_TYPE_EXT, EGL_COLOR_COMPONENT_TYPE_FIXED_EXT,
EGL_NONE }; EGL_NONE };
eglChooseConfig(surface->eglDisplay, configAttributes, &surface->eglConfig, 1, &surface->numConfigs); eglChooseConfig(surface->eglDisplay, configAttributes, &surface->eglConfig, 1, &surface->numConfigs);
EGLint const surfaceAttributes[] = {EGL_NONE}; EGLint const surfaceAttributes[] = {EGL_NONE};
surface->eglSurface = eglCreateWindowSurface(surface->eglDisplay, surface->eglConfig, surface->layer, surfaceAttributes); surface->eglSurface = eglCreateWindowSurface(surface->eglDisplay, surface->eglConfig, surface->layer, surfaceAttributes);
eglBindAPI(EGL_OPENGL_ES_API); eglBindAPI(EGL_OPENGL_ES_API);
EGLint contextAttributes[] = { EGLint contextAttributes[] = {
EGL_CONTEXT_MAJOR_VERSION_KHR, 3, EGL_CONTEXT_MAJOR_VERSION_KHR, 3,
EGL_CONTEXT_MINOR_VERSION_KHR, 0, //NOTE: Angle can't create a GLES 3.1 context on macOS EGL_CONTEXT_MINOR_VERSION_KHR, 0, //NOTE: Angle can't create a GLES 3.1 context on macOS
EGL_CONTEXT_BIND_GENERATES_RESOURCE_CHROMIUM, EGL_TRUE, EGL_CONTEXT_BIND_GENERATES_RESOURCE_CHROMIUM, EGL_TRUE,
EGL_CONTEXT_CLIENT_ARRAYS_ENABLED_ANGLE, EGL_TRUE, EGL_CONTEXT_CLIENT_ARRAYS_ENABLED_ANGLE, EGL_TRUE,
EGL_CONTEXT_OPENGL_BACKWARDS_COMPATIBLE_ANGLE, EGL_FALSE, EGL_CONTEXT_OPENGL_BACKWARDS_COMPATIBLE_ANGLE, EGL_FALSE,
EGL_NONE}; EGL_NONE};
surface->eglContext = eglCreateContext(surface->eglDisplay, surface->eglConfig, EGL_NO_CONTEXT, contextAttributes); surface->eglContext = eglCreateContext(surface->eglDisplay, surface->eglConfig, EGL_NO_CONTEXT, contextAttributes);
eglMakeCurrent(surface->eglDisplay, surface->eglSurface, surface->eglSurface, surface->eglContext); eglMakeCurrent(surface->eglDisplay, surface->eglSurface, surface->eglSurface, surface->eglContext);
eglSwapInterval(surface->eglDisplay, 1); eglSwapInterval(surface->eglDisplay, 1);
mg_surface handle = mg_surface_alloc_handle((mg_surface_data*)surface); mg_surface handle = mg_surface_alloc_handle((mg_surface_data*)surface);
return(handle); return(handle);
} }
mg_surface mg_gles_surface_create_offscreen(u32 width, u32 height) mg_surface mg_gles_surface_create_offscreen(u32 width, u32 height)
{ {
return(mg_gles_surface_create_with_view(width, height, 0)); return(mg_gles_surface_create_with_view(width, height, 0));
} }
mg_surface mg_gles_surface_create_for_window(mp_window window) mg_surface mg_gles_surface_create_for_window(mp_window window)
{ {
mg_surface res = mg_surface_nil(); mg_surface res = mg_surface_nil();
mp_window_data* windowData = mp_window_ptr_from_handle(window); mp_window_data* windowData = mp_window_ptr_from_handle(window);
if(windowData) if(windowData)
{ {
@autoreleasepool @autoreleasepool
{ {
NSRect frame = [[windowData->osx.nsWindow contentView] frame]; NSRect frame = [[windowData->osx.nsWindow contentView] frame];
NSView* view = [[NSView alloc] initWithFrame: frame]; NSView* view = [[NSView alloc] initWithFrame: frame];
res = mg_gles_surface_create_with_view(frame.size.width, frame.size.height, view); res = mg_gles_surface_create_with_view(frame.size.width, frame.size.height, view);
if(!mg_surface_is_nil(res)) if(!mg_surface_is_nil(res))
{ {
[[windowData->osx.nsWindow contentView] addSubview: view]; [[windowData->osx.nsWindow contentView] addSubview: view];
} }
} }
} }
return(res); return(res);
} }

File diff suppressed because it is too large Load Diff

View File

@ -6,7 +6,7 @@
* @revision: * @revision:
* *
*****************************************************************/ *****************************************************************/
#include<GLES3/gl32.h> #include<GLES3/gl31.h>
#define EGL_EGLEXT_PROTOTYPES #define EGL_EGLEXT_PROTOTYPES
#include<EGL/egl.h> #include<EGL/egl.h>
#include<EGL/eglext.h> #include<EGL/eglext.h>
@ -108,7 +108,7 @@ mg_surface mg_gles_surface_create_for_window(mp_window window)
eglBindAPI(EGL_OPENGL_ES_API); eglBindAPI(EGL_OPENGL_ES_API);
EGLint contextAttributes[] = { EGLint contextAttributes[] = {
EGL_CONTEXT_MAJOR_VERSION_KHR, 3, EGL_CONTEXT_MAJOR_VERSION_KHR, 3,
EGL_CONTEXT_MINOR_VERSION_KHR, 0, //NOTE: Angle can't create a GLES 3.1 context on macOS EGL_CONTEXT_MINOR_VERSION_KHR, 1, //NOTE: Angle can't create a GLES 3.1 context on macOS
EGL_CONTEXT_BIND_GENERATES_RESOURCE_CHROMIUM, EGL_TRUE, EGL_CONTEXT_BIND_GENERATES_RESOURCE_CHROMIUM, EGL_TRUE,
EGL_CONTEXT_CLIENT_ARRAYS_ENABLED_ANGLE, EGL_TRUE, EGL_CONTEXT_CLIENT_ARRAYS_ENABLED_ANGLE, EGL_TRUE,
EGL_CONTEXT_OPENGL_BACKWARDS_COMPATIBLE_ANGLE, EGL_FALSE, EGL_CONTEXT_OPENGL_BACKWARDS_COMPATIBLE_ANGLE, EGL_FALSE,

View File

@ -9,6 +9,9 @@
#ifndef __WIN32_GLES_SURFACE_H_ #ifndef __WIN32_GLES_SURFACE_H_
#define __WIN32_GLES_SURFACE_H_ #define __WIN32_GLES_SURFACE_H_
mg_surface mg_gles_surface_create_for_window(mg_window window); #include"graphics.h"
#include"mp_app.h"
mg_surface mg_gles_surface_create_for_window(mp_window window);
#endif // __WIN32_GLES_SURFACE_H_ #endif // __WIN32_GLES_SURFACE_H_