diff --git a/.gitignore b/.gitignore index e0d1eef..8eac138 100644 --- a/.gitignore +++ b/.gitignore @@ -12,4 +12,5 @@ bin/* *.dll *.sln -src/gles_canvas_shaders.h \ No newline at end of file +Debug/* +src/glsl_shaders.h \ No newline at end of file diff --git a/examples/image/build.bat b/examples/image/build.bat new file mode 100644 index 0000000..004a30f --- /dev/null +++ b/examples/image/build.bat @@ -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 diff --git a/examples/image/build.sh b/examples/image/build.sh new file mode 100755 index 0000000..1e71bf2 --- /dev/null +++ b/examples/image/build.sh @@ -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 diff --git a/examples/image/main.c b/examples/image/main.c new file mode 100644 index 0000000..f2ac3c2 --- /dev/null +++ b/examples/image/main.c @@ -0,0 +1,94 @@ +/************************************************************//** +* +* @file: main.cpp +* @author: Martin Fouilleul +* @date: 30/07/2022 +* @revision: +* +*****************************************************************/ +#include +#include +#include + +#define _USE_MATH_DEFINES //NOTE: necessary for MSVC +#include + +#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); +} diff --git a/resources/triceratops.png b/resources/triceratops.png new file mode 100644 index 0000000..5d8ec68 Binary files /dev/null and b/resources/triceratops.png differ diff --git a/src/graphics.c b/src/graphics.c index 3186578..6782eda 100644 --- a/src/graphics.c +++ b/src/graphics.c @@ -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); diff --git a/src/graphics_internal.h b/src/graphics_internal.h index 8128513..cddcbdf 100644 --- a/src/graphics_internal.h +++ b/src/graphics_internal.h @@ -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); diff --git a/src/mtl_canvas.m b/src/mtl_canvas.m index 5db91c2..0868af1 100644 --- a/src/mtl_canvas.m +++ b/src/mtl_canvas.m @@ -38,7 +38,6 @@ typedef struct mg_mtl_canvas_backend // textures and buffers id outTexture; - id atlasTexture; id shapeBuffer; id vertexBuffer; id 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 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 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 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 //----------------------------------------------------------- diff --git a/src/mtl_shader.h b/src/mtl_shader.h index 767fff5..0e3d86d 100644 --- a/src/mtl_shader.h +++ b/src/mtl_shader.h @@ -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; diff --git a/src/mtl_shader.metal b/src/mtl_shader.metal index 7ea2c7d..8f43c0d 100644 --- a/src/mtl_shader.metal +++ b/src/mtl_shader.metal @@ -181,6 +181,8 @@ kernel void RenderKernel(texture2d 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 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 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 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;