[canvas] simple image API
This commit is contained in:
parent
d1b71110bb
commit
fd4c4e7be3
|
@ -12,4 +12,5 @@ bin/*
|
|||
*.dll
|
||||
*.sln
|
||||
|
||||
src/gles_canvas_shaders.h
|
||||
Debug/*
|
||||
src/glsl_shaders.h
|
|
@ -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
|
|
@ -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
|
|
@ -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);
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 209 KiB |
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
//-----------------------------------------------------------
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue