From ac8b0ef339313b19e5cb02491114f6cb6f533a39 Mon Sep 17 00:00:00 2001 From: Martin Fouilleul Date: Sun, 22 Sep 2024 13:07:45 +0200 Subject: [PATCH] Fix uv to pixel coord calculation and clamp texture sampling to edges. --- sketches/check-bleeding/build.sh | 19 +++ sketches/check-bleeding/main.c | 168 ++++++++++++++++++++++++++ sketches/image/build.sh | 3 +- sketches/image/main.c | 32 ++--- src/graphics/wgsl_shaders/raster.wgsl | 104 +++++++++++++++- 5 files changed, 306 insertions(+), 20 deletions(-) create mode 100755 sketches/check-bleeding/build.sh create mode 100644 sketches/check-bleeding/main.c diff --git a/sketches/check-bleeding/build.sh b/sketches/check-bleeding/build.sh new file mode 100755 index 00000000..8a27cb1b --- /dev/null +++ b/sketches/check-bleeding/build.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +BINDIR=bin +LIBDIR=../../build/bin +RESDIR=../resources +SRCDIR=../../src + +INCLUDES="-I$SRCDIR -I$SRCDIR/util -I$SRCDIR/platform -I$SRCDIR/app" +LIBS="-L$LIBDIR -lorca" +FLAGS="-mmacos-version-min=10.15.4 -DOC_DEBUG -DLOG_COMPILE_DEBUG" + +mkdir -p $BINDIR +clang -g $FLAGS $LIBS $INCLUDES -o $BINDIR/test_bleeding main.c + +cp $LIBDIR/liborca.dylib $BINDIR/ +cp $LIBDIR/libwebgpu.dylib $BINDIR/ + + +install_name_tool -add_rpath "@executable_path" $BINDIR/test_bleeding diff --git a/sketches/check-bleeding/main.c b/sketches/check-bleeding/main.c new file mode 100644 index 00000000..b5e4cd5d --- /dev/null +++ b/sketches/check-bleeding/main.c @@ -0,0 +1,168 @@ +/************************************************************************* +* +* Orca +* Copyright 2023 Martin Fouilleul and the Orca project contributors +* See LICENSE.txt for licensing information +* +**************************************************************************/ +#include +#include +#include +#include + +#define _USE_MATH_DEFINES //NOTE: necessary for MSVC +#include + +#include "orca.h" +#include "graphics/wgpu_renderer_debug.h" + +int main() +{ + oc_init(); + + oc_rect windowRect = { .x = 100, .y = 100, .w = 800, .h = 700 }; + oc_window window = oc_window_create(windowRect, OC_STR8("test"), 0); + + oc_rect contentRect = oc_window_get_content_rect(window); + + oc_canvas_renderer renderer = oc_canvas_renderer_create(); + if(oc_canvas_renderer_is_nil(renderer)) + { + oc_log_error("Error: couldn't create renderer\n"); + return (-1); + } + + oc_wgpu_canvas_debug_display_options displayOptions = { + .showTileBorders = true, + }; + + oc_wgpu_canvas_debug_set_display_options(renderer, &displayOptions); + + oc_surface surface = oc_canvas_surface_create_for_window(renderer, window); + if(oc_surface_is_nil(surface)) + { + oc_log_error("Error: couldn't create surface\n"); + return (-1); + } + + oc_canvas_context context = oc_canvas_context_create(); + if(oc_canvas_context_is_nil(context)) + { + oc_log_error("Error: couldn't create canvas\n"); + return (-1); + } + + //NOTE: create image + oc_arena_scope scratch = oc_scratch_begin(); + + oc_str8 imagePath = oc_path_executable_relative(scratch.arena, OC_STR8("../../resources/square_small.png")); + oc_image image = oc_image_create_from_path(renderer, imagePath, false); + oc_vec2 imageSize = oc_image_size(image); + + oc_scratch_end(scratch); + // start app + oc_window_bring_to_front(window); + oc_window_focus(window); + + f32 x = 100; + f32 y = 100; + f32 width = 128; + f32 height = 128; + + while(!oc_should_quit()) + { + scratch = oc_scratch_begin(); + + oc_pump_events(0); + oc_event* event = 0; + while((event = oc_next_event(scratch.arena)) != 0) + { + switch(event->type) + { + case OC_EVENT_WINDOW_CLOSE: + { + oc_request_quit(); + } + break; + + case OC_EVENT_KEYBOARD_KEY: + { + if(event->key.action == OC_KEY_PRESS || event->key.action == OC_KEY_REPEAT) + { + if(event->key.keyCode == OC_KEY_LEFT) + { + if(event->key.mods & OC_KEYMOD_SHIFT) + { + x--; + } + else + { + width--; + } + } + else if(event->key.keyCode == OC_KEY_RIGHT) + { + if(event->key.mods & OC_KEYMOD_SHIFT) + { + x++; + } + else + { + width++; + } + } + else if(event->key.keyCode == OC_KEY_UP) + { + if(event->key.mods & OC_KEYMOD_SHIFT) + { + y--; + } + else + { + height--; + } + } + else if(event->key.keyCode == OC_KEY_DOWN) + { + if(event->key.mods & OC_KEYMOD_SHIFT) + { + y++; + } + else + { + height++; + } + } + } + } + break; + + default: + break; + } + } + + oc_set_color_rgba(0, 0, 0, 1); + oc_clear(); + + oc_set_color_rgba(0, 1, 0, 1); + oc_rectangle_fill(x, y, width, height); + + oc_set_color_rgba(1, 1, 1, 1); + oc_image_draw(image, (oc_rect){ x, y, width, height }); + + oc_canvas_render(renderer, context, surface); + oc_canvas_present(renderer, surface); + + oc_scratch_end(scratch); + } + + oc_image_destroy(image); + // oc_canvas_renderer_destroy(renderer); + oc_surface_destroy(surface); + oc_window_destroy(window); + + oc_terminate(); + + return (0); +} diff --git a/sketches/image/build.sh b/sketches/image/build.sh index 97909e76..ff70bdb0 100755 --- a/sketches/image/build.sh +++ b/sketches/image/build.sh @@ -13,6 +13,7 @@ mkdir -p $BINDIR clang -g $FLAGS $LIBS $INCLUDES -o $BINDIR/example_image main.c cp $LIBDIR/liborca.dylib $BINDIR/ -cp $LIBDIR/mtl_renderer.metallib $BINDIR/ +cp $LIBDIR/libwebgpu.dylib $BINDIR/ + install_name_tool -add_rpath "@executable_path" $BINDIR/example_image diff --git a/sketches/image/main.c b/sketches/image/main.c index 9ca9a7e3..2b3203a9 100644 --- a/sketches/image/main.c +++ b/sketches/image/main.c @@ -24,18 +24,22 @@ int main() oc_rect contentRect = oc_window_get_content_rect(window); - //NOTE: create surface - oc_surface surface = oc_surface_create_for_window(window, OC_CANVAS); + oc_canvas_renderer renderer = oc_canvas_renderer_create(); + if(oc_canvas_renderer_is_nil(renderer)) + { + oc_log_error("Error: couldn't create renderer\n"); + return (-1); + } + + oc_surface surface = oc_canvas_surface_create_for_window(renderer, window); if(oc_surface_is_nil(surface)) { - oc_log_error("couldn't create surface\n"); + oc_log_error("Error: couldn't create surface\n"); return (-1); } - oc_surface_swap_interval(surface, 0); - //NOTE: create canvas - oc_canvas canvas = oc_canvas_create(); - if(oc_canvas_is_nil(canvas)) + oc_canvas_context context = oc_canvas_context_create(); + if(oc_canvas_context_is_nil(context)) { oc_log_error("Error: couldn't create canvas\n"); return (-1); @@ -45,11 +49,11 @@ int main() oc_arena_scope scratch = oc_scratch_begin(); oc_str8 imagePath = oc_path_executable_relative(scratch.arena, OC_STR8("../../resources/triceratops.png")); - oc_image image = oc_image_create_from_file(surface, imagePath, false); + oc_image image = oc_image_create_from_path(renderer, imagePath, false); oc_vec2 imageSize = oc_image_size(image); oc_str8 imagePath2 = oc_path_executable_relative(scratch.arena, OC_STR8("../../resources/Top512.png")); - oc_image image2 = oc_image_create_from_file(surface, imagePath2, false); + oc_image image2 = oc_image_create_from_path(renderer, imagePath2, false); oc_vec2 imageSize2 = oc_image_size(image2); oc_scratch_end(scratch); @@ -78,8 +82,6 @@ int main() } } - oc_surface_select(surface); - oc_set_color_rgba(0, 1, 1, 1); oc_clear(); @@ -103,17 +105,17 @@ int main() oc_image_draw(image2, (oc_rect){300, 200, 300, 300}); */ - oc_image_draw(image, (oc_rect){ 100, 100, 300, 300 }); + oc_image_draw(image, (oc_rect){ 100, 100, 362, 256 }); oc_image_draw(image2, (oc_rect){ 300, 200, 300, 300 }); - oc_render(canvas); - oc_surface_present(surface); + oc_canvas_render(renderer, context, surface); + oc_canvas_present(renderer, surface); oc_scratch_end(scratch); } oc_image_destroy(image); - oc_canvas_destroy(canvas); + // oc_canvas_renderer_destroy(renderer); oc_surface_destroy(surface); oc_window_destroy(window); diff --git a/src/graphics/wgsl_shaders/raster.wgsl b/src/graphics/wgsl_shaders/raster.wgsl index 72b5483e..d61e6f30 100644 --- a/src/graphics/wgsl_shaders/raster.wgsl +++ b/src/graphics/wgsl_shaders/raster.wgsl @@ -37,7 +37,11 @@ fn sampleFromTexture(texture : texture_2d, uv : vec2f) -> vec4f } else { - let p : vec2f = uv * vec2f(textureDimensions(texture)); + let p : vec2f = clamp(uv * vec2f(textureDimensions(texture)) - vec2f(0.5, 0.5), + vec2f(0, 0), + vec2f(textureDimensions(texture)) - vec2f(1, 1)); + let r = fract(p); + let tl = vec2i(p); let bl = tl + vec2i(0, 1); let tr = tl + vec2i(1, 0); @@ -48,9 +52,9 @@ fn sampleFromTexture(texture : texture_2d, uv : vec2f) -> vec4f let trColor : vec4f = textureLoad(texture, tr, 0); let brColor : vec4f = textureLoad(texture, br, 0); - let lColor : vec4f = 0.5*(tlColor + blColor); - let rColor : vec4f = 0.5*(trColor + brColor); - color = 0.5*(lColor + rColor); + let lColor : vec4f = (1-r.y) * tlColor + r.y * blColor; + let rColor : vec4f = (1-r.y) * trColor + r.y * brColor; + color = (1-r.x) * lColor + r.x * rColor; } return(color); } @@ -154,6 +158,97 @@ fn get_next_color(pathIndex : u32, } return(nextColor); } +/* +fn get_next_color(pathIndex : u32, sampleCoord : vec2f) -> vec4f +{ + var nextColor : vec4f; + + if(pathBuffer[pathIndex].hasGradient == 0) + { + nextColor = pathBuffer[pathIndex].colors[0]; + } + else + { + var sampleColor = vec4f(0, 0, 0, 0); + + var bl = pathBuffer[pathIndex].colors[0]; + var br = pathBuffer[pathIndex].colors[1]; + var tr = pathBuffer[pathIndex].colors[2]; + var tl = pathBuffer[pathIndex].colors[3]; + + if(pathBuffer[pathIndex].blendSpace == 1) + { + bl = pow(bl, vec4f(0.454545, 0.454545, 0.454545, 1)); + br = pow(br, vec4f(0.454545, 0.454545, 0.454545, 1)); + tr = pow(tr, vec4f(0.454545, 0.454545, 0.454545, 1)); + tl = pow(tl, vec4f(0.454545, 0.454545, 0.454545, 1)); + } + + let sampleCoord3 = vec3f(sampleCoord, 1); + let uv : vec2f = (pathBuffer[pathIndex].uvTransform * sampleCoord3).xy; + + let bottomColor : vec4f = (1. - uv.x) * bl + uv.x * br; + let topColor : vec4f = (1. - uv.x) * tl + uv.x * tr; + sampleColor += uv.y * bottomColor + (1 - uv.y) * topColor; + + nextColor = sampleColor; + + if(pathBuffer[pathIndex].blendSpace == 1) + { + nextColor = pow(nextColor, vec4f(2.2, 2.2, 2.2, 1)); + } + } + + nextColor = vec4f(nextColor.rgb * nextColor.a, nextColor.a); + + let textureID : i32 = pathBuffer[pathIndex].textureID; + if( textureID >= 0 + && debugDisplayOptions.textureOff == 0) + { + var texColor = vec4f(0, 0, 0, 0); + + let sampleCoord3 = vec3f(sampleCoord, 1); + let uv : vec2f = (pathBuffer[pathIndex].uvTransform * sampleCoord3).xy; + + if(textureID == 0) + { + texColor += sampleFromTexture(srcTexture0, uv); + } + else if(textureID == 1) + { + texColor += sampleFromTexture(srcTexture1, uv); + } + else if(textureID == 2) + { + texColor += sampleFromTexture(srcTexture2, uv); + } + else if(textureID == 3) + { + texColor += sampleFromTexture(srcTexture3, uv); + } + else if(textureID == 4) + { + texColor += sampleFromTexture(srcTexture4, uv); + } + else if(textureID == 5) + { + texColor += sampleFromTexture(srcTexture5, uv); + } + else if(textureID == 6) + { + texColor += sampleFromTexture(srcTexture6, uv); + } + else if(textureID == 7) + { + texColor += sampleFromTexture(srcTexture7, uv); + } + + texColor = vec4f(texColor.rgb*texColor.a, texColor.a); + nextColor *= texColor; + } + return(nextColor); +} +*/ @compute @workgroup_size(16, 16) fn raster(@builtin(num_workgroups) workGroupCount : vec3u, @builtin(workgroup_id) workGroupID : vec3u, @@ -247,6 +342,7 @@ fn get_next_color(pathIndex : u32, else { let pathIndex : u32 = op.index; +// let nextColor : vec4f = get_next_color(pathIndex, centerCoord); let nextColor : vec4f = get_next_color(pathIndex, sourceSampleCoords); if(op.kind == OC_OP_FILL)