[canvas] simple image API

This commit is contained in:
Martin Fouilleul 2023-02-24 12:01:00 +01:00
parent d1b71110bb
commit fd4c4e7be3
10 changed files with 190 additions and 46 deletions

3
.gitignore vendored
View File

@ -12,4 +12,5 @@ bin/*
*.dll
*.sln
src/gles_canvas_shaders.h
Debug/*
src/glsl_shaders.h

4
examples/image/build.bat Normal file
View File

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

11
examples/image/build.sh Executable file
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"
FLAGS="-mmacos-version-min=10.15.4 -DDEBUG -DLOG_COMPILE_DEBUG"
clang -g $FLAGS $LIBS $INCLUDES -o $BINDIR/example_image main.c

94
examples/image/main.c Normal file
View File

@ -0,0 +1,94 @@
/************************************************************//**
*
* @file: main.cpp
* @author: Martin Fouilleul
* @date: 30/07/2022
* @revision:
*
*****************************************************************/
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#define _USE_MATH_DEFINES //NOTE: necessary for MSVC
#include<math.h>
#include"milepost.h"
#define LOG_SUBSYSTEM "Main"
int main()
{
LogLevel(LOG_LEVEL_WARNING);
mp_init();
mp_clock_init(); //TODO put that in mp_init()?
mp_rect windowRect = {.x = 100, .y = 100, .w = 810, .h = 610};
mp_window window = mp_window_create(windowRect, "test", 0);
mp_rect contentRect = mp_window_get_content_rect(window);
//NOTE: create surface
mg_surface surface = mg_surface_create_for_window(window, MG_BACKEND_DEFAULT);
mg_surface_swap_interval(surface, 0);
//NOTE: create canvas
mg_canvas canvas = mg_canvas_create(surface);
if(mg_canvas_is_nil(canvas))
{
printf("Error: couldn't create canvas\n");
return(-1);
}
//NOTE: create image
str8 imagePath = mp_app_get_resource_path(mem_scratch(), "../resources/triceratops.png");
mg_image image = mg_image_create_from_file(imagePath, true);
vec2 imageSize = mg_image_size(image);
// start app
mp_window_bring_to_front(window);
mp_window_focus(window);
while(!mp_should_quit())
{
mp_pump_events(0);
mp_event event = {0};
while(mp_next_event(&event))
{
switch(event.type)
{
case MP_EVENT_WINDOW_CLOSE:
{
mp_request_quit();
} break;
default:
break;
}
}
mg_surface_prepare(surface);
// background
mg_set_color_rgba(0, 1, 1, 1);
mg_clear();
mg_set_color_rgba(1, 0, 1, 1);
mg_image_draw(image, (mp_rect){100, 100, imageSize.x/8, imageSize.y/8});
mg_flush();
mg_surface_present(surface);
mem_arena_clear(mem_scratch());
}
mg_image_destroy(image);
mg_canvas_destroy(canvas);
mg_surface_destroy(surface);
mp_window_destroy(window);
mp_terminate();
return(0);
}

BIN
resources/triceratops.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 209 KiB

View File

@ -94,6 +94,7 @@ typedef struct mg_canvas_data
u32 primitiveCount;
mg_primitive primitives[MG_MAX_PRIMITIVE_COUNT];
u32 shapeFirstVertexIndex;
u32 vertexCount;
u32 indexCount;
@ -586,22 +587,41 @@ void mg_reset_shape_index(mg_canvas_data* canvas)
u32 mg_next_shape_textured(mg_canvas_data* canvas, vec2 uv, mg_color color)
{
//TODO: push color, uv, clip
mg_vertex_layout* layout = &canvas->backend->vertexLayout;
if(canvas->nextShapeIndex)
{
//NOTE: compute shape's bounding box for the _previous_ shape
vec2 boxMin = {FLT_MAX, FLT_MAX};
vec2 boxMax = {-FLT_MAX, -FLT_MAX};
for(int posIndex = canvas->shapeFirstVertexIndex;
posIndex < canvas->vertexCount;
posIndex++)
{
vec2 pos = *(vec2*)(layout->posBuffer + posIndex*layout->posStride);
boxMin.x = minimum(boxMin.x, pos.x);
boxMin.y = minimum(boxMin.y, pos.y);
boxMax.x = maximum(boxMax.x, pos.x);
boxMax.y = maximum(boxMax.y, pos.y);
}
int prevIndex = canvas->nextShapeIndex-1;
*(mp_rect*)(((char*)layout->boxBuffer) + prevIndex*layout->boxStride) = (mp_rect){boxMin.x, boxMin.y, boxMax.x, boxMax.y};
}
int index = canvas->nextShapeIndex;
canvas->nextShapeIndex++;
canvas->shapeFirstVertexIndex = canvas->vertexCount;
mp_rect clip = {canvas->clip.x,
canvas->clip.y,
canvas->clip.x + canvas->clip.w - 1,
canvas->clip.y + canvas->clip.h - 1};
mg_vertex_layout* layout = &canvas->backend->vertexLayout;
*(vec2*)(((char*)layout->uvBuffer) + index*layout->uvStride) = uv;
*(mg_color*)(((char*)layout->colorBuffer) + index*layout->colorStride) = color;
*(mp_rect*)(((char*)layout->clipBuffer) + index*layout->clipStride) = clip;
return(index);
}
@ -2162,7 +2182,7 @@ void mg_render_image(mg_canvas_data* canvas, mg_image image, mp_rect srcRegion,
u32 baseIndex = mg_vertices_base_index(canvas);
i32* indices = mg_reserve_indices(canvas, 6);
mg_next_shape(canvas, attributes->color);
mg_next_shape(canvas, (mg_color){1, 1, 1, 1});
vec2 points[4] = {{dstRegion.x, dstRegion.y},
{dstRegion.x + dstRegion.w, dstRegion.y},
@ -2885,15 +2905,19 @@ void mg_do_clip_push(mg_canvas_data* canvas, mp_rect clip)
mg_clip_stack_push(canvas, r);
}
void mg_flush_batch(mg_canvas_data* canvas)
void mg_flush_batch(mg_canvas_data* canvas, mg_image_data* image)
{
if(canvas->backend && canvas->backend->drawBatch)
//NOTE: finalize last shape
int shapeCount = mg_next_shape(canvas, (mg_color){1, 1, 1, 1});
if(canvas->backend && canvas->backend->drawBatch && canvas->indexCount)
{
canvas->backend->drawBatch(canvas->backend, canvas->nextShapeIndex, canvas->vertexCount, canvas->indexCount);
canvas->backend->drawBatch(canvas->backend, image, shapeCount, canvas->vertexCount, canvas->indexCount);
}
mg_reset_shape_index(canvas);
canvas->vertexCount = 0;
canvas->indexCount = 0;
canvas->shapeFirstVertexIndex = 0;
}
void mg_flush_commands(int primitiveCount, mg_primitive* primitives, mg_path_elt* pathElements)
@ -2927,13 +2951,12 @@ void mg_flush_commands(int primitiveCount, mg_primitive* primitives, mg_path_elt
mg_primitive* primitive = &(primitives[nextIndex]);
nextIndex++;
/*
if(i && primitive->attributes.image.h != currentImage.h)
{
mg_flush_batch(canvas);
mg_image_data* imageData = mg_image_data_from_handle(canvas, currentImage);
mg_flush_batch(canvas, imageData);
currentImage = primitive->attributes.image;
}
*/
switch(primitive->cmd)
{
@ -3042,7 +3065,8 @@ void mg_flush_commands(int primitiveCount, mg_primitive* primitives, mg_path_elt
}
exit_command_loop: ;
mg_flush_batch(canvas);
mg_image_data* imageData = mg_image_data_from_handle(canvas, currentImage);
mg_flush_batch(canvas, imageData);
canvas->backend->end(canvas->backend);

View File

@ -212,6 +212,9 @@ typedef struct mg_vertex_layout
char* shapeIndexBuffer;
u32 shapeIndexStride;
char* boxBuffer;
u32 boxStride;
char* clipBuffer;
u32 clipStride;
@ -226,7 +229,7 @@ typedef void (*mg_canvas_backend_destroy_proc)(mg_canvas_backend* backend);
typedef void (*mg_canvas_backend_begin_proc)(mg_canvas_backend* backend);
typedef void (*mg_canvas_backend_end_proc)(mg_canvas_backend* backend);
typedef void (*mg_canvas_backend_clear_proc)(mg_canvas_backend* backend, mg_color clearColor);
typedef void (*mg_canvas_backend_draw_batch_proc)(mg_canvas_backend* backend, u32 vertexCount, u32 shapeCount, u32 indexCount);
typedef void (*mg_canvas_backend_draw_batch_proc)(mg_canvas_backend* backend, mg_image_data* imageData, u32 vertexCount, u32 shapeCount, u32 indexCount);
typedef void (*mg_canvas_backend_atlas_upload_proc)(mg_canvas_backend* backend, mp_rect rect, u8* bytes);

View File

@ -38,7 +38,6 @@ typedef struct mg_mtl_canvas_backend
// textures and buffers
id<MTLTexture> outTexture;
id<MTLTexture> atlasTexture;
id<MTLBuffer> shapeBuffer;
id<MTLBuffer> vertexBuffer;
id<MTLBuffer> indexBuffer;
@ -49,6 +48,13 @@ typedef struct mg_mtl_canvas_backend
} mg_mtl_canvas_backend;
typedef struct mg_mtl_image_data
{
mg_image_data interface;
id<MTLTexture> texture;
} mg_mtl_image_data;
mg_mtl_surface* mg_mtl_canvas_get_surface(mg_mtl_canvas_backend* canvas)
{
mg_mtl_surface* res = 0;
@ -73,7 +79,7 @@ void mg_mtl_canvas_clear(mg_canvas_backend* interface, mg_color clearColor)
backend->clearColor = clearColor;
}
void mg_mtl_canvas_draw_batch(mg_canvas_backend* interface, u32 shapeCount, u32 vertexCount, u32 indexCount)
void mg_mtl_canvas_draw_batch(mg_canvas_backend* interface, mg_image_data* image, u32 shapeCount, u32 vertexCount, u32 indexCount)
{
mg_mtl_canvas_backend* backend = (mg_mtl_canvas_backend*)interface;
mg_mtl_surface* surface = mg_mtl_canvas_get_surface(backend);
@ -161,7 +167,14 @@ void mg_mtl_canvas_draw_batch(mg_canvas_backend* interface, u32 shapeCount, u32
id<MTLComputeCommandEncoder> encoder = [surface->commandBuffer computeCommandEncoder];
[encoder setComputePipelineState:backend->computePipeline];
[encoder setTexture: backend->outTexture atIndex: 0];
[encoder setTexture: backend->atlasTexture atIndex: 1];
int useTexture = 0;
if(image)
{
mg_mtl_image_data* mtlImage = (mg_mtl_image_data*)image;
[encoder setTexture: mtlImage->texture atIndex: 1];
useTexture = 1;
}
[encoder setBuffer: backend->vertexBuffer offset:0 atIndex: 0];
[encoder setBuffer: backend->shapeBuffer offset:0 atIndex: 1];
[encoder setBuffer: backend->tileCounters offset:0 atIndex: 2];
@ -169,6 +182,8 @@ void mg_mtl_canvas_draw_batch(mg_canvas_backend* interface, u32 shapeCount, u32
[encoder setBuffer: backend->triangleArray offset:0 atIndex: 4];
[encoder setBuffer: backend->boxArray offset:0 atIndex: 5];
[encoder setBytes: &clearColorVec4 length: sizeof(vector_float4) atIndex: 6];
[encoder setBytes: &useTexture length:sizeof(int) atIndex:7];
[encoder setBytes: &scale length: sizeof(float) atIndex: 8];
//TODO: check that we don't exceed maxTotalThreadsPerThreadgroup
DEBUG_ASSERT(RENDERER_TILE_SIZE*RENDERER_TILE_SIZE <= backend->computePipeline.maxTotalThreadsPerThreadgroup);
@ -259,6 +274,9 @@ void mg_mtl_canvas_update_vertex_layout(mg_mtl_canvas_backend* backend)
.uvBuffer = shapeBase + offsetof(mg_shape, uv),
.uvStride = sizeof(mg_shape),
.boxBuffer = shapeBase + offsetof(mg_shape, box),
.boxStride = sizeof(mg_shape),
.indexBuffer = indexBase,
.indexStride = sizeof(int)};
}
@ -270,7 +288,6 @@ void mg_mtl_canvas_destroy(mg_canvas_backend* interface)
@autoreleasepool
{
[backend->outTexture release];
[backend->atlasTexture release];
[backend->vertexBuffer release];
[backend->indexBuffer release];
[backend->tilesArray release];
@ -280,12 +297,6 @@ void mg_mtl_canvas_destroy(mg_canvas_backend* interface)
}
}
typedef struct mg_mtl_image_data
{
mg_image_data interface;
id<MTLTexture> texture;
} mg_mtl_image_data;
mg_image_data* mg_mtl_canvas_image_create(mg_canvas_backend* interface, vec2 size)
{
mg_mtl_image_data* image = 0;
@ -303,7 +314,7 @@ mg_image_data* mg_mtl_canvas_image_create(mg_canvas_backend* interface, vec2 siz
texDesc.textureType = MTLTextureType2D;
texDesc.storageMode = MTLStorageModeManaged;
texDesc.usage = MTLTextureUsageShaderRead;
texDesc.pixelFormat = MTLPixelFormatBGRA8Unorm;
texDesc.pixelFormat = MTLPixelFormatRGBA8Unorm;
texDesc.width = size.x;
texDesc.height = size.y;
@ -382,25 +393,13 @@ mg_canvas_backend* mg_mtl_canvas_create(mg_surface surface)
texDesc.textureType = MTLTextureType2D;
texDesc.storageMode = MTLStorageModePrivate;
texDesc.usage = MTLTextureUsageShaderRead | MTLTextureUsageShaderWrite;
texDesc.pixelFormat = MTLPixelFormatBGRA8Unorm;// MTLPixelFormatBGRA8Unorm_sRGB;
texDesc.pixelFormat = MTLPixelFormatRGBA8Unorm;
texDesc.width = drawableSize.width;
texDesc.height = drawableSize.height;
backend->outTexture = [metalSurface->device newTextureWithDescriptor:texDesc];
//TODO(martin): retain ?
//-----------------------------------------------------------
//NOTE(martin): create our atlas texture
//-----------------------------------------------------------
texDesc.textureType = MTLTextureType2D;
texDesc.storageMode = MTLStorageModeManaged;
texDesc.usage = MTLTextureUsageShaderRead;
texDesc.pixelFormat = MTLPixelFormatRGBA8Unorm; //MTLPixelFormatBGRA8Unorm;
texDesc.width = MG_ATLAS_SIZE;
texDesc.height = MG_ATLAS_SIZE;
backend->atlasTexture = [metalSurface->device newTextureWithDescriptor:texDesc];
//-----------------------------------------------------------
//NOTE(martin): create buffers for vertex and index
//-----------------------------------------------------------

View File

@ -29,7 +29,8 @@ typedef struct mg_shape
{
vector_float4 color;
vector_float4 clip;
vector_float2 uv; // texture coordinates
vector_float4 box;
vector_float2 uv; // texture coordinates?
} mg_shape;

View File

@ -181,6 +181,8 @@ kernel void RenderKernel(texture2d<float, access::write> outTexture [[texture(0)
const device mg_triangle_data* triangleArray [[buffer(4)]],
const device float4* boxArray [[buffer(5)]],
constant vector_float4* clearColor [[buffer(6)]],
constant int* useTexture [[buffer(7)]],
constant float* contentsScaling [[buffer(8)]],
uint2 gid [[thread_position_in_grid]],
uint2 tgid [[threadgroup_position_in_grid]],
uint2 threadsPerThreadgroup [[threads_per_threadgroup]],
@ -266,13 +268,15 @@ kernel void RenderKernel(texture2d<float, access::write> outTexture [[texture(0)
float4 color = shapeBuffer[shapeIndex].color;
/////////////////////////////////////////////////////////////////////////
//TODO: dummy uv while we figure out image handling.
float2 uv0 = shapeBuffer[shapeIndex].uv;
float2 uv1 = uv0;
float2 uv2 = uv0;
float4 shapeBox = shapeBuffer[shapeIndex].box * contentsScaling[0];
float2 uv0 = (p0 - shapeBox.xy)/(shapeBox.zw - shapeBox.xy);
float2 uv1 = (p1 - shapeBox.xy)/(shapeBox.zw - shapeBox.xy);
float2 uv2 = (p2 - shapeBox.xy)/(shapeBox.zw - shapeBox.xy);
/////////////////////////////////////////////////////////////////////////
for(int i=0; i<6; i++)
{
float2 samplePoint = (float2)gid + sampleOffsets[i];
@ -292,9 +296,12 @@ kernel void RenderKernel(texture2d<float, access::write> outTexture [[texture(0)
float4 cubic = (cubic0*w0 + cubic1*w1 + cubic2*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);
float4 texColor = texAtlas.sample(smp, uv);
float4 texColor = float4(1, 1, 1, 1);
if(*useTexture)
{
constexpr sampler smp(mip_filter::nearest, mag_filter::linear, min_filter::linear);
texColor = texAtlas.sample(smp, uv);
}
//TODO(martin): this is a quick and dirty fix for solid polygons where we use
// cubic = (1, 1, 1, 1) on all vertices, which can cause small errors to
// flip the sign.
@ -317,7 +324,7 @@ kernel void RenderKernel(texture2d<float, access::write> outTexture [[texture(0)
pixelColors[i] = nextColors[i];
}
float4 nextCol = color;//*texColor;
float4 nextCol = color*texColor;
nextColors[i] = pixelColors[i]*(1-nextCol.a) +nextCol.a*nextCol;
zIndices[i] = shapeIndex;