diff --git a/.gitignore b/.gitignore index 5df72cc..5524392 100644 --- a/.gitignore +++ b/.gitignore @@ -17,11 +17,15 @@ build Debug/* +scripts/__pycache__ +src/gles_api.json src/bindgen_core_api.c src/bindgen_gles_api.c sdk/io_stubs.c sdk/orca_surface.c +sdk/gl31.h *bind_gen.c +gles_gen.log .vscode/launch.json .vscode/settings.json diff --git a/build.bat b/build.bat deleted file mode 100644 index ccd88f3..0000000 --- a/build.bat +++ /dev/null @@ -1,62 +0,0 @@ -@echo off - -set target=%1% -if "%~1%" == "" set target=orca - -if not exist bin mkdir bin -if not exist bin\obj mkdir bin\obj - -if %target% == wasm3 ( - echo building wasm3 - - set wasm3_includes=/I .\ext\wasm3\source - set wasm3_sources=/I .\ext\wasm3\source\*.c - - for %%f in ( .\ext\wasm3\source\*.c ) do ( - cl /nologo /Zi /Zc:preprocessor /O2 /c /Fo:bin\obj\%%~nf.obj %wasm3_includes% %%f - ) - lib /nologo /out:bin\wasm3.lib bin\obj\*.obj -) - -if %target% == milepost ( - echo building milepost - cd milepost - build.bat - cd .. -) - -if %target% == orca ( - echo building orca - - ::copy libraries - copy milepost\bin\milepost.dll bin - copy milepost\bin\milepost.dll.lib bin - - ::generate wasm3 api bindings - python3 scripts\bindgen.py core src\core_api.json^ - --wasm3-bindings src\core_api_bind_gen.c - - python3 scripts\bindgen.py gles src\gles_api.json^ - --wasm3-bindings src\gles_api_bind_gen.c - - python3 scripts\bindgen.py canvas src\canvas_api.json^ - --guest-stubs sdk\orca_surface.c^ - --guest-include graphics.h^ - --wasm3-bindings src\canvas_api_bind_gen.c - - python3 scripts\bindgen.py clock src\clock_api.json^ - --guest-stubs sdk\orca_clock.c^ - --guest-include platform_clock.h^ - --wasm3-bindings src\clock_api_bind_gen.c - - python3 scripts\bindgen.py io^ - src\io_api.json^ - --guest-stubs sdk\io_stubs.c^ - --wasm3-bindings src\io_api_bind_gen.c - - ::compile orca - set INCLUDES=/I src /I sdk /I ext\wasm3\source /I milepost\src /I milepost\ext - set LIBS=/LIBPATH:bin milepost.dll.lib wasm3.lib - - cl /Zi /Zc:preprocessor /std:c11 /experimental:c11atomics %INCLUDES% src\main.c /link %LIBS% /out:bin\orca.exe -) diff --git a/build.sh b/build.sh deleted file mode 100755 index 6c84383..0000000 --- a/build.sh +++ /dev/null @@ -1,97 +0,0 @@ -#!/bin/bash - -set -exo pipefail - -target="$1" - -if [ -z $target ] ; then - target='orca' -fi -target=$(echo $target | tr '[:upper:]' '[:lower:]') - -if [ ! \( -e bin \) ] ; then - mkdir ./bin -fi - -if [ ! \( -e resources \) ] ; then - mkdir ./resources -fi - - -if [ $target = milepost ] ; then - echo "building milepost" - pushd milepost > /dev/null - ./build.sh lib "$2" - popd > /dev/null - -elif [ $target = wasm3 ] ; then - - echo "building wasm3" - mkdir ./bin/obj - for file in ./ext/wasm3/source/*.c ; do - name=$(basename $file) - name=${name/.c/.o} - clang -c -g -O2 -foptimize-sibling-calls -Wno-extern-initializer -Dd_m3VerboseErrorMessages -o ./bin/obj/$name -I./ext/wasm3/source $file - done - ar -rcs ./bin/libwasm3.a ./bin/obj/*.o - rm -rf ./bin/obj - -elif [ $target = orca ] ; then - echo "building orca" - - if [ ! \( -e milepost/bin/libmilepost.dylib \) -o ! \( -e milepost/bin/mtl_renderer.metallib \) ] ; then - ./build.sh milepost - fi - - if [ ! \( -e ./bin/libwasm3.a \) ] ; then - ./build.sh wasm3 - fi - - # copy libraries - cp milepost/bin/mtl_renderer.metallib bin/ - cp milepost/bin/libmilepost.dylib bin/ - cp milepost/bin/libGLESv2.dylib bin/ - cp milepost/bin/libEGL.dylib bin/ - - INCLUDES="-Isrc -Isdk -Imilepost/src -Imilepost/src/util -Imilepost/src/platform -Iext/wasm3/source -Imilepost/ext/" - LIBS="-Lbin -lmilepost -lwasm3" - FLAGS="-g -DLOG_COMPILE_DEBUG -mmacos-version-min=10.15.4 -maes" - - # generate wasm3 api bindings - - python3 ./scripts/bindgen.py core \ - src/core_api.json \ - --wasm3-bindings ./src/core_api_bind_gen.c - - python3 ./scripts/bindgen.py gles \ - src/gles_api.json \ - --wasm3-bindings ./src/gles_api_bind_gen.c - - python3 ./scripts/bindgen.py canvas \ - src/canvas_api.json \ - --guest-stubs sdk/orca_surface.c \ - --guest-include graphics.h \ - --wasm3-bindings ./src/canvas_api_bind_gen.c - - python3 ./scripts/bindgen.py clock \ - src/clock_api.json \ - --guest-stubs sdk/orca_clock.c \ - --guest-include platform_clock.h \ - --wasm3-bindings ./src/clock_api_bind_gen.c - - python3 ./scripts/bindgen.py io \ - src/io_api.json \ - --guest-stubs sdk/io_stubs.c \ - --wasm3-bindings ./src/io_api_bind_gen.c - - # compile orca - clang $FLAGS $INCLUDES $LIBS -o bin/orca src/main.c - - # fix libs imports - install_name_tool -change "./bin/libmilepost.dylib" "@rpath/libmilepost.dylib" bin/orca - install_name_tool -add_rpath "@executable_path/" bin/orca - - -else - echo "unknown build target $target" -fi diff --git a/milepost b/milepost index c103c00..d01dc83 160000 --- a/milepost +++ b/milepost @@ -1 +1 @@ -Subproject commit c103c001f7c8e780602193f22360201810802438 +Subproject commit d01dc832fbbc7b1e720476178a2c49a737717e0f diff --git a/samples/fluid/.gitignore b/samples/fluid/.gitignore new file mode 100644 index 0000000..8840e08 --- /dev/null +++ b/samples/fluid/.gitignore @@ -0,0 +1,3 @@ +Fluid +profile.dtrace +profile.spall diff --git a/samples/fluid/build.bat b/samples/fluid/build.bat new file mode 100644 index 0000000..c01c970 --- /dev/null +++ b/samples/fluid/build.bat @@ -0,0 +1,33 @@ +@echo off + +:: compile wasm module +set wasmFlags=--target=wasm32^ + --no-standard-libraries ^ + -fno-builtin ^ + -Wl,--no-entry ^ + -Wl,--export-dynamic ^ + -g ^ + -O2 ^ + -mbulk-memory ^ + -D__ORCA__ ^ + -isystem ..\..\cstdlib\include -I ..\..\sdk -I..\..\milepost\ext -I ..\..\milepost -I ..\..\milepost\src + +set shaders=src/shaders/advect.glsl^ + src/shaders/blit_div_fragment.glsl^ + src/shaders/blit_div_vertex.glsl^ + src/shaders/blit_fragment.glsl^ + src/shaders/blit_residue_fragment.glsl^ + src/shaders/blit_vertex.glsl^ + src/shaders/common_vertex.glsl^ + src/shaders/divergence.glsl^ + src/shaders/jacobi_step.glsl^ + src/shaders/multigrid_correct.glsl^ + src/shaders/multigrid_restrict_residual.glsl^ + src/shaders/splat.glsl^ + src/shaders/subtract_pressure.glsl + +call python3 ../../milepost/scripts/embed_text.py --prefix=glsl_ --output src/glsl_shaders.h %shaders% + +clang %wasmFlags% -o .\module.wasm ..\..\sdk\orca.c ..\..\cstdlib\src\*.c src\main.c + +python3 ..\..\scripts\mkapp.py --orca-dir ..\.. --icon icon.png --name Fluid module.wasm diff --git a/samples/fluid/build.sh b/samples/fluid/build.sh new file mode 100755 index 0000000..c2439c3 --- /dev/null +++ b/samples/fluid/build.sh @@ -0,0 +1,35 @@ +#!/bin/bash + +set -euo pipefail + +if [[ -x /usr/local/opt/llvm/bin/clang ]]; then + CLANG=/usr/local/opt/llvm/bin/clang +elif [[ -x /opt/homebrew/opt/llvm/bin/clang ]]; then + CLANG=/opt/homebrew/opt/llvm/bin/clang +else + echo "Could not find Homebrew clang; this script will probably not work." + CLANG=clang +fi + +STDLIB_DIR=../../cstdlib +ORCA_SDK_DIR=../../sdk +MILEPOST_DIR=../../milepost + +python3 ../../milepost/scripts/embed_text.py --prefix=glsl_ --output src/glsl_shaders.h src/shaders/*.glsl + +wasmFlags="--target=wasm32 \ + --no-standard-libraries \ + -fno-builtin \ + -Wl,--no-entry \ + -Wl,--export-dynamic \ + -g \ + -O2 \ + -mbulk-memory \ + -D__ORCA__ \ + -I $STDLIB_DIR/include \ + -I $ORCA_SDK_DIR \ + -I $MILEPOST_DIR/ext -I $MILEPOST_DIR -I $MILEPOST_DIR/src" + +$CLANG $wasmFlags -o ./module.wasm ../../sdk/orca.c ../../cstdlib/src/*.c src/main.c + +orca bundle --orca-dir ../.. --icon icon.png --name Fluid module.wasm diff --git a/samples/fluid/icon.png b/samples/fluid/icon.png new file mode 100644 index 0000000..ebfa188 Binary files /dev/null and b/samples/fluid/icon.png differ diff --git a/samples/fluid/src/glsl_shaders.h b/samples/fluid/src/glsl_shaders.h new file mode 100644 index 0000000..a15bb11 --- /dev/null +++ b/samples/fluid/src/glsl_shaders.h @@ -0,0 +1,551 @@ +/********************************************************************* +* +* file: glsl_shaders.h +* note: string literals auto-generated by embed_text.py +* date: 08/082023 +* +**********************************************************************/ +#ifndef __GLSL_SHADERS_H__ +#define __GLSL_SHADERS_H__ + + +//NOTE: string imported from src/shaders/advect.glsl +const char* glsl_advect = +"#version 300 es\n" +"\n" +"precision highp float;\n" +"precision highp sampler2D;\n" +"\n" +"in vec2 texCoord;\n" +"out vec4 fragColor;\n" +"\n" +"uniform sampler2D src;\n" +"uniform sampler2D velocity;\n" +"uniform float delta;\n" +"uniform float dissipation;\n" +"\n" +"vec2 u(ivec2 coord)\n" +"{\n" +" return(texelFetch(velocity, coord, 0).xy);\n" +"}\n" +"\n" +"vec4 q(ivec2 coord)\n" +"{\n" +" if( coord.x < 0\n" +" || coord.x >= textureSize(src, 0).x\n" +" || coord.y < 0\n" +" || coord.y >= textureSize(src, 0).y)\n" +" {\n" +" return(vec4(0.));\n" +" }\n" +" return(texelFetch(src, coord, 0));\n" +"}\n" +"\n" +"vec4 bilerpSrc(vec2 pos)\n" +"{\n" +" vec2 offset = fract(pos);\n" +"\n" +" ivec2 bl = ivec2(floor(pos));\n" +"\n" +" ivec2 br = bl + ivec2(1, 0);\n" +" ivec2 tl = bl + ivec2(0, 1);\n" +" ivec2 tr = bl + ivec2(1, 1);\n" +"\n" +" vec4 lerpTop = (1.-offset.x)*q(tl) + offset.x*q(tr);\n" +" vec4 lerpBottom = (1.-offset.x)*q(bl) + offset.x*q(br);\n" +" vec4 result = (1.-offset.y)*lerpBottom + offset.y*lerpTop;\n" +"\n" +" return(result);\n" +"}\n" +"\n" +"void main()\n" +"{\n" +" float texWidth = float(textureSize(velocity, 0).x);\n" +"\n" +" ivec2 pixelCoord = ivec2(floor(gl_FragCoord.xy));\n" +"\n" +" vec2 samplePos = vec2(pixelCoord) - texWidth * delta * u(pixelCoord);\n" +" fragColor = bilerpSrc(samplePos) / (1. + dissipation*delta);\n" +"}\n"; + +//NOTE: string imported from src/shaders/blit_div_fragment.glsl +const char* glsl_blit_div_fragment = +"#version 300 es\n" +"\n" +"precision highp float;\n" +"precision highp sampler2D;\n" +"\n" +"in vec2 texCoord;\n" +"out vec4 fragColor;\n" +"\n" +"uniform sampler2D tex;\n" +"\n" +"vec3 color_map(float v)\n" +"{\n" +" float logv = log(abs(v))/log(10.0);\n" +" float f = floor(logv + 7.0);\n" +" float i = floor(4.0*(logv + 7.0 - f));\n" +"\n" +" if(f < 0.0) return vec3(0.0);\n" +" if(f < 1.0) return mix(vec3(1.0, 0.0, 0.0), vec3(1.0), i/4.0);\n" +" if(f < 2.0) return mix(vec3(0.0, 1.0, 0.0), vec3(1.0), i/4.0);\n" +" if(f < 3.0) return mix(vec3(0.0, 0.0, 1.0), vec3(1.0), i/4.0);\n" +" if(f < 4.0) return mix(vec3(1.0, 1.0, 0.0), vec3(1.0), i/4.0);\n" +" if(f < 5.0) return mix(vec3(1.0, 0.0, 1.0), vec3(1.0), i/4.0);\n" +" if(f < 6.0) return mix(vec3(0.0, 1.0, 1.0), vec3(1.0), i/4.0);\n" +" if(f < 7.0) return mix(vec3(1.0, 0.5, 0.0), vec3(1.0), i/4.0);\n" +" if(f < 8.0) return mix(vec3(1.0, 1.0, 1.0), vec3(1.0), i/4.0);\n" +" return vec3(1.0);\n" +"}\n" +"\n" +"void main()\n" +"{\n" +" ivec2 pixelCoord = ivec2(floor(texCoord.xy * vec2(textureSize(tex, 0).xy)));\n" +" float f = texelFetch(tex, pixelCoord, 0).x;\n" +" fragColor = vec4(color_map(f), 1.0);\n" +"}\n"; + +//NOTE: string imported from src/shaders/blit_div_vertex.glsl +const char* glsl_blit_div_vertex = +"#version 300 es\n" +"\n" +"precision highp float;\n" +"\n" +"in vec2 pos;\n" +"out vec2 texCoord;\n" +"\n" +"uniform mat4 mvp;\n" +"\n" +"void main()\n" +"{\n" +" texCoord = 0.5*(pos + vec2(1,1));\n" +" gl_Position = mvp * vec4(pos, 0, 1);\n" +"}\n"; + +//NOTE: string imported from src/shaders/blit_fragment.glsl +const char* glsl_blit_fragment = +"#version 300 es\n" +"\n" +"precision highp float;\n" +"precision highp sampler2D;\n" +"\n" +"in vec2 texCoord;\n" +"out vec4 fragColor;\n" +"\n" +"uniform sampler2D tex;\n" +"\n" +"void main()\n" +"{\n" +" fragColor = texture(tex, texCoord);\n" +" fragColor.a = 1.0;\n" +"}\n"; + +//NOTE: string imported from src/shaders/blit_residue_fragment.glsl +const char* glsl_blit_residue_fragment = +"#version 300 es\n" +"\n" +"precision highp float;\n" +"precision highp sampler2D;\n" +"\n" +"in vec2 texCoord;\n" +"out vec4 fragColor;\n" +"\n" +"uniform sampler2D xTex;\n" +"uniform sampler2D bTex;\n" +"\n" +"float x(ivec2 coord)\n" +"{\n" +" if( coord.x <= 0\n" +" || coord.x >= textureSize(xTex, 0).x\n" +" || coord.y <= 0\n" +" || coord.y >= textureSize(xTex, 0).y)\n" +" {\n" +" return(0.);\n" +" }\n" +" return(texelFetch(xTex, coord, 0).x);\n" +"}\n" +"\n" +"float b(ivec2 coord)\n" +"{\n" +" if( coord.x <= 0\n" +" || coord.x >= textureSize(bTex, 0).x\n" +" || coord.y <= 0\n" +" || coord.y >= textureSize(bTex, 0).y)\n" +" {\n" +" return(0.);\n" +" }\n" +" return(texelFetch(bTex, coord, 0).x);\n" +"}\n" +"\n" +"vec3 color_map(float v)\n" +"{\n" +" float logv = log(abs(v))/log(10.0);\n" +" float f = floor(logv + 7.0);\n" +" float i = floor(4.0*(logv + 7.0 - f));\n" +"\n" +" if(f < 0.0) return vec3(0.0);\n" +" if(f < 1.0) return mix(vec3(1.0, 0.0, 0.0), vec3(1.0), i/4.0);\n" +" if(f < 2.0) return mix(vec3(0.0, 1.0, 0.0), vec3(1.0), i/4.0);\n" +" if(f < 3.0) return mix(vec3(0.0, 0.0, 1.0), vec3(1.0), i/4.0);\n" +" if(f < 4.0) return mix(vec3(1.0, 1.0, 0.0), vec3(1.0), i/4.0);\n" +" if(f < 5.0) return mix(vec3(1.0, 0.0, 1.0), vec3(1.0), i/4.0);\n" +" if(f < 6.0) return mix(vec3(0.0, 1.0, 1.0), vec3(1.0), i/4.0);\n" +" if(f < 7.0) return mix(vec3(1.0, 0.5, 0.0), vec3(1.0), i/4.0);\n" +" if(f < 8.0) return mix(vec3(1.0, 1.0, 1.0), vec3(1.0), i/4.0);\n" +" return vec3(1.0);\n" +"}\n" +"\n" +"void main()\n" +"{\n" +" ivec2 pixelCoord = ivec2(floor(texCoord.xy * vec2(textureSize(xTex, 0).xy)));\n" +"\n" +" float tl = x(pixelCoord + ivec2(-1, 1));\n" +" float tr = x(pixelCoord + ivec2(1, 1));\n" +" float bl = x(pixelCoord + ivec2(-1, -1));\n" +" float br = x(pixelCoord + ivec2(1, -1));\n" +"\n" +" float residue = b(pixelCoord) - (-tl - tr - bl - br + 4.*x(pixelCoord));\n" +" fragColor = vec4(color_map(residue), 1);\n" +"}\n"; + +//NOTE: string imported from src/shaders/blit_vertex.glsl +const char* glsl_blit_vertex = +"#version 300 es\n" +"\n" +"precision highp float;\n" +"\n" +"in vec2 pos;\n" +"out vec2 texCoord;\n" +"\n" +"uniform mat4 mvp;\n" +"uniform ivec2 gridSize;\n" +"\n" +"void main()\n" +"{\n" +" float margin = 32.;\n" +" float ratio = 1. - 2.*margin/float(gridSize.x);\n" +"\n" +" texCoord = margin/float(gridSize.x) + ratio*(0.5*(pos + vec2(1,1)));\n" +" gl_Position = mvp * vec4(pos, 0, 1);\n" +"}\n"; + +//NOTE: string imported from src/shaders/common_vertex.glsl +const char* glsl_common_vertex = +"#version 300 es\n" +"\n" +"precision highp float;\n" +"\n" +"in vec2 pos;\n" +"out vec2 texCoord;\n" +"\n" +"void main()\n" +"{\n" +" texCoord = 0.5*(pos + vec2(1,1));\n" +" gl_Position = vec4(pos, 0, 1);\n" +"}\n"; + +//NOTE: string imported from src/shaders/divergence.glsl +const char* glsl_divergence = +"#version 300 es\n" +"\n" +"precision highp float;\n" +"precision highp sampler2D;\n" +"\n" +"in vec2 texCoord;\n" +"out vec4 fragColor;\n" +"\n" +"uniform sampler2D src;\n" +"\n" +"vec2 u(ivec2 coord)\n" +"{\n" +" return(texelFetch(src, coord, 0).xy);\n" +"}\n" +"\n" +"void main()\n" +"{\n" +" ivec2 pixelCoord = ivec2(floor(gl_FragCoord.xy));\n" +"\n" +" if( pixelCoord.x <= 0\n" +" || pixelCoord.x >= textureSize(src, 0).x\n" +" || pixelCoord.y <= 0\n" +" || pixelCoord.y >= textureSize(src, 0).y)\n" +" {\n" +" fragColor = vec4(0, 0, 0, 1);\n" +" }\n" +" else\n" +" {\n" +" vec2 tl = u(pixelCoord + ivec2(-1, 0));\n" +" vec2 tr = u(pixelCoord);\n" +" vec2 bl = u(pixelCoord + ivec2(-1, -1));\n" +" vec2 br = u(pixelCoord + ivec2(0, -1));\n" +"\n" +" float r = (tr.x + br.x)/2.;\n" +" float l = (tl.x + bl.x)/2.;\n" +" float t = (tl.y + tr.y)/2.;\n" +" float b = (bl.y + br.y)/2.;\n" +"\n" +" fragColor = vec4(-2.*(r - l + t - b), 0, 0, 1);\n" +" }\n" +"}\n"; + +//NOTE: string imported from src/shaders/jacobi_step.glsl +const char* glsl_jacobi_step = +"#version 300 es\n" +"\n" +"precision highp float;\n" +"precision highp sampler2D;\n" +"\n" +"in vec2 texCoord;\n" +"out vec4 fragColor;\n" +"\n" +"uniform sampler2D xTex;\n" +"uniform sampler2D bTex;\n" +"\n" +"float x(ivec2 coord)\n" +"{\n" +" if( coord.x <= 0\n" +" || coord.x >= textureSize(xTex, 0).x\n" +" || coord.y <= 0\n" +" || coord.y >= textureSize(xTex, 0).y)\n" +" {\n" +" return(0.);\n" +" }\n" +" return(texelFetch(xTex, coord, 0).x);\n" +"}\n" +"\n" +"float b(ivec2 coord)\n" +"{\n" +" if( coord.x <= 0\n" +" || coord.x >= textureSize(bTex, 0).x\n" +" || coord.y <= 0\n" +" || coord.y >= textureSize(bTex, 0).y)\n" +" {\n" +" return(0.);\n" +" }\n" +" return(texelFetch(bTex, coord, 0).x);\n" +"}\n" +"\n" +"void main()\n" +"{\n" +" ivec2 pixelCoord = ivec2(floor(gl_FragCoord.xy));\n" +"\n" +" if( pixelCoord.x <= 0\n" +" || pixelCoord.y <= 0)\n" +" {\n" +" fragColor = vec4(0, 0, 0, 1);\n" +" }\n" +" else\n" +" {\n" +" float tl = x(pixelCoord + ivec2(-1, 1));\n" +" float tr = x(pixelCoord + ivec2(1, 1));\n" +" float bl = x(pixelCoord + ivec2(-1, -1));\n" +" float br = x(pixelCoord + ivec2(1, -1));\n" +"\n" +" float jacobi = (tl + tr + bl + br + b(pixelCoord))/4.;\n" +" fragColor = vec4(jacobi, 0, 0, 1);\n" +" }\n" +"}\n"; + +//NOTE: string imported from src/shaders/multigrid_correct.glsl +const char* glsl_multigrid_correct = +"#version 300 es\n" +"\n" +"precision highp float;\n" +"precision highp sampler2D;\n" +"\n" +"in vec2 texCoord;\n" +"out vec4 fragColor;\n" +"\n" +"uniform sampler2D src;\n" +"uniform sampler2D error;\n" +"uniform float invGridSize;\n" +"\n" +"float e(ivec2 coord)\n" +"{\n" +" if( coord.x <= 0\n" +" || coord.x >= textureSize(error, 0).x\n" +" || coord.y <= 0\n" +" || coord.y >= textureSize(error, 0).y)\n" +" {\n" +" return(0.);\n" +" }\n" +" return(texelFetch(error, coord, 0).x);\n" +"}\n" +"\n" +"float p(ivec2 coord)\n" +"{\n" +" if( coord.x <= 0\n" +" || coord.x >= textureSize(src, 0).x\n" +" || coord.y <= 0\n" +" || coord.y >= textureSize(src, 0).y)\n" +" {\n" +" return(0.);\n" +" }\n" +" return(texelFetch(src, coord, 0).x);\n" +"}\n" +"\n" +"void main()\n" +"{\n" +" ivec2 pixelCoord = ivec2(floor(gl_FragCoord.xy));\n" +" vec2 coarseCoord = vec2(pixelCoord)/2.;\n" +" vec2 offset = fract(coarseCoord);\n" +"\n" +" ivec2 bl = ivec2(floor(coarseCoord));\n" +" ivec2 br = bl + ivec2(1, 0);\n" +" ivec2 tl = bl + ivec2(0, 1);\n" +" ivec2 tr = bl + ivec2(1, 1);\n" +"\n" +" float topLerp = (1.-offset.x)*e(tl)+ offset.x*e(tr);\n" +" float bottomLerp = (1.-offset.x)*e(bl) + offset.x*e(br);\n" +" float bilerpError = (1.-offset.y)*bottomLerp + offset.y*topLerp;\n" +"\n" +" fragColor = vec4(p(pixelCoord) + bilerpError, 0, 0, 1);\n" +"}\n"; + +//NOTE: string imported from src/shaders/multigrid_restrict_residual.glsl +const char* glsl_multigrid_restrict_residual = +"#version 300 es\n" +"\n" +"precision highp float;\n" +"precision highp sampler2D;\n" +"\n" +"in vec2 texCoord;\n" +"out vec4 fragColor;\n" +"\n" +"uniform sampler2D xTex;\n" +"uniform sampler2D bTex;\n" +"\n" +"float x(ivec2 coord)\n" +"{\n" +" if( coord.x <= 0\n" +" || coord.x >= textureSize(xTex, 0).x\n" +" || coord.y <= 0\n" +" || coord.y >= textureSize(xTex, 0).y)\n" +" {\n" +" return(0.);\n" +" }\n" +" return(texelFetch(xTex, coord, 0).x);\n" +"}\n" +"\n" +"float b(ivec2 coord)\n" +"{\n" +" if( coord.x <= 0\n" +" || coord.x >= textureSize(bTex, 0).x\n" +" || coord.y <= 0\n" +" || coord.y >= textureSize(bTex, 0).y)\n" +" {\n" +" return(0.);\n" +" }\n" +" return(texelFetch(bTex, coord, 0).x);\n" +"}\n" +"\n" +"float residual(ivec2 coord)\n" +"{\n" +" ivec2 vr = coord + ivec2(1, 0);\n" +" ivec2 vl = coord - ivec2(1, 0);\n" +" ivec2 vt = coord + ivec2(0, 1);\n" +" ivec2 vb = coord - ivec2(0, 1);\n" +"\n" +" return((x(vl) + x(vr) + x(vt) + x(vb) + b(coord) - 4.*x(coord))*4.);\n" +"}\n" +"\n" +"void main()\n" +"{\n" +" ivec2 pixelCoord = ivec2(floor(gl_FragCoord.xy));\n" +"\n" +" float restricted = residual(2*pixelCoord + ivec2(-1, -1))\n" +" + residual(2*pixelCoord + ivec2(1, -1))\n" +" + residual(2*pixelCoord + ivec2(1, 1))\n" +" + residual(2*pixelCoord + ivec2(-1, 1))\n" +" + 2.*residual(2*pixelCoord + ivec2(-1, 0))\n" +" + 2.*residual(2*pixelCoord + ivec2(1, 0))\n" +" + 2.*residual(2*pixelCoord + ivec2(0, -1))\n" +" + 2.*residual(2*pixelCoord + ivec2(0, 1))\n" +" + 4.*residual(2*pixelCoord);\n" +" restricted /= 16.;\n" +" fragColor = vec4(restricted, 0, 0, 1);\n" +"}\n"; + +//NOTE: string imported from src/shaders/splat.glsl +const char* glsl_splat = +"#version 300 es\n" +"\n" +"precision highp float;\n" +"precision highp sampler2D;\n" +"\n" +"in vec2 texCoord;\n" +"out vec4 fragColor;\n" +"\n" +"uniform sampler2D src;\n" +"uniform vec2 splatPos;\n" +"uniform vec3 splatColor;\n" +"uniform float radius;\n" +"uniform float additive;\n" +"uniform float blending;\n" +"\n" +"uniform float randomize;\n" +"\n" +"void main()\n" +"{\n" +" float d2 = dot(texCoord - splatPos, texCoord - splatPos);\n" +" float intensity = exp(-10.*d2/radius);\n" +" vec2 force = splatColor.xy;\n" +"\n" +" vec3 u = texture(src, texCoord).xyz;\n" +" vec3 uAdd = u + intensity*splatColor.xyz;\n" +" vec3 uBlend = u*(1.-intensity) + intensity * splatColor;\n" +"\n" +" fragColor = vec4(uAdd*additive + uBlend*blending, 1);\n" +"}\n"; + +//NOTE: string imported from src/shaders/subtract_pressure.glsl +const char* glsl_subtract_pressure = +"#version 300 es\n" +"\n" +"precision highp float;\n" +"precision highp sampler2D;\n" +"\n" +"in vec2 texCoord;\n" +"out vec4 fragColor;\n" +"\n" +"uniform sampler2D src;\n" +"uniform sampler2D pressure;\n" +"uniform float invGridSize;\n" +"\n" +"vec2 u(ivec2 coord)\n" +"{\n" +" return(texelFetch(src, coord, 0).xy);\n" +"}\n" +"\n" +"float p(ivec2 coord)\n" +"{\n" +" if( coord.x <= 0\n" +" || coord.x >= textureSize(pressure, 0).x\n" +" || coord.y <= 0\n" +" || coord.y >= textureSize(pressure, 0).y)\n" +" {\n" +" return(0.);\n" +" }\n" +" return(texelFetch(pressure, coord, 0).x);\n" +"}\n" +"\n" +"void main()\n" +"{\n" +" ivec2 pixelCoord = ivec2(floor(gl_FragCoord.xy));\n" +"\n" +" float tl = p(pixelCoord + ivec2(0, 1));\n" +" float tr = p(pixelCoord + ivec2(1, 1));\n" +" float bl = p(pixelCoord);\n" +" float br = p(pixelCoord + ivec2(1, 0));\n" +"\n" +" float r = (tr + br)/2.;\n" +" float l = (tl + bl)/2.;\n" +" float t = (tl + tr)/2.;\n" +" float b = (bl + br)/2.;\n" +"\n" +" vec2 gradP = vec2(r - l, t - b);\n" +"\n" +" fragColor = vec4(u(pixelCoord) - gradP, 0, 1);\n" +"}\n"; + +#endif // __GLSL_SHADERS_H__ diff --git a/samples/fluid/src/main.c b/samples/fluid/src/main.c new file mode 100644 index 0000000..fe17f4e --- /dev/null +++ b/samples/fluid/src/main.c @@ -0,0 +1,928 @@ +/************************************************************//** +* +* @file: main.cpp +* @author: Martin Fouilleul +* @date: 27/02/2022 +* @revision: +* +*****************************************************************/ + +#include"orca.h" +#include"glsl_shaders.h" + +//---------------------------------------------------------------- +//NOTE(martin): GL vertex struct and identifiers +//---------------------------------------------------------------- +typedef struct Vertex { float x, y; } Vertex; + +typedef struct advect_program +{ + GLuint prog; + + GLint pos; + GLint src; + GLint velocity; + GLint delta; + GLint dissipation; + +} advect_program; + +typedef struct div_program +{ + GLuint prog; + GLint pos; + GLint src; + +} div_program; + +typedef struct jacobi_program +{ + GLuint prog; + GLint pos; + GLint xTex; + GLint bTex; + +} jacobi_program; + +typedef struct blit_residue_program +{ + GLuint prog; + + GLint pos; + GLint mvp; + GLint xTex; + GLint bTex; +} blit_residue_program; + +typedef struct multigrid_restrict_residual_program +{ + GLuint prog; + GLint pos; + GLint xTex; + GLint bTex; + +} multigrid_restrict_residual_program; + +typedef struct multigrid_correct_program +{ + GLuint prog; + GLint pos; + GLint src; + GLint error; + GLint invGridSize; + +} multigrid_correct_program; + +typedef struct subtract_program +{ + GLuint prog; + + GLint pos; + GLint src; + GLint pressure; + GLint invGridSize; + +} subtract_program; + +typedef struct blit_program +{ + GLuint prog; + + GLint pos; + GLint mvp; + GLint gridSize; + GLint tex; +} blit_program; + +typedef struct splat_program +{ + GLuint prog; + + GLint pos; + GLint src; + GLint splatPos; + GLint splatColor; + GLint radius; + GLint additive; + GLint blending; + GLint randomize; + +} splat_program; + +typedef struct frame_buffer +{ + GLuint textures[2]; + GLuint fbos[2]; +} frame_buffer; + +advect_program advectProgram; +div_program divProgram; +jacobi_program jacobiProgram; +multigrid_restrict_residual_program multigridRestrictResidualProgram; +multigrid_correct_program multigridCorrectProgram; + +subtract_program subtractProgram; +splat_program splatProgram; +blit_program blitProgram; +blit_program blitDivProgram; +blit_residue_program blitResidueProgram; + +frame_buffer colorBuffer; +frame_buffer velocityBuffer; + +const int MULTIGRID_COUNT = 4; +frame_buffer pressureBuffer[4]; +frame_buffer divBuffer[4]; + +GLuint vertexBuffer; + +//---------------------------------------------------------------- +//NOTE(martin): initialization +//---------------------------------------------------------------- + +GLuint compile_shader(const char* vs, const char* fs) +{ + GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(vertexShader, 1, &vs, 0); + glCompileShader(vertexShader); + + GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(fragmentShader, 1, &fs, 0); + glCompileShader(fragmentShader); + + GLuint prog = glCreateProgram(); + glAttachShader(prog, vertexShader); + glAttachShader(prog, fragmentShader); + glLinkProgram(prog); + + + //TODO errors + int status = 0; + glGetProgramiv(prog, GL_LINK_STATUS, &status); + if(status != GL_TRUE) + { + log_error("program failed to link: "); + int logSize = 0; + glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &logSize); + + mem_arena_scope scratch = mem_scratch_begin(); + char* log = mem_arena_alloc(scratch.arena, logSize); + + glGetProgramInfoLog(prog, logSize, 0, log); + log_error("%s\n", log); + + mem_scratch_end(scratch); + } + + int err = glGetError(); + if(err) + { + log_error("gl error %i\n", err); + } + + return(prog); +} + +void init_advect(advect_program* program) +{ + log_info("compiling advect..."); + program->prog = compile_shader(glsl_common_vertex, glsl_advect); + program->pos = glGetAttribLocation(program->prog, "pos"); + program->src = glGetUniformLocation(program->prog, "src"); + program->velocity = glGetUniformLocation(program->prog, "velocity"); + program->delta = glGetUniformLocation(program->prog, "delta"); + program->dissipation = glGetUniformLocation(program->prog, "dissipation"); +} + +void init_div(div_program* program) +{ + log_info("compiling div..."); + program->prog = compile_shader(glsl_common_vertex, glsl_divergence); + program->pos = glGetAttribLocation(program->prog, "pos"); + program->src = glGetUniformLocation(program->prog, "src"); +} + +void init_jacobi(jacobi_program* program) +{ + log_info("compiling jacobi..."); + program->prog = compile_shader(glsl_common_vertex, glsl_jacobi_step); + program->pos = glGetAttribLocation(program->prog, "pos"); + program->xTex = glGetUniformLocation(program->prog, "xTex"); + program->bTex = glGetUniformLocation(program->prog, "bTex"); +} + +void init_multigrid_restrict_residual(multigrid_restrict_residual_program* program) +{ + log_info("compiling multigrid restrict residual..."); + program->prog = compile_shader(glsl_common_vertex, glsl_multigrid_restrict_residual); + program->pos = glGetAttribLocation(program->prog, "pos"); + program->xTex = glGetUniformLocation(program->prog, "xTex"); + program->bTex = glGetUniformLocation(program->prog, "bTex"); +} + +void init_multigrid_correct(multigrid_correct_program* program) +{ + log_info("compiling multigrid correct..."); + program->prog = compile_shader(glsl_common_vertex, glsl_multigrid_correct); + program->pos = glGetAttribLocation(program->prog, "pos"); + program->src = glGetUniformLocation(program->prog, "src"); + program->error = glGetUniformLocation(program->prog, "error"); + program->invGridSize = glGetUniformLocation(program->prog, "invGridSize"); +} + +void init_subtract(subtract_program* program) +{ + log_info("compiling subtract..."); + program->prog = compile_shader(glsl_common_vertex, glsl_subtract_pressure); + program->pos = glGetAttribLocation(program->prog, "pos"); + program->src = glGetUniformLocation(program->prog, "src"); + program->pressure = glGetUniformLocation(program->prog, "pressure"); + program->invGridSize = glGetUniformLocation(program->prog, "invGridSize"); +} + +void init_splat(splat_program* program) +{ + log_info("compiling splat..."); + program->prog = compile_shader(glsl_common_vertex, glsl_splat); + program->pos = glGetAttribLocation(program->prog, "pos"); + program->src = glGetUniformLocation(program->prog, "src"); + program->splatPos = glGetUniformLocation(program->prog, "splatPos"); + program->splatColor = glGetUniformLocation(program->prog, "splatColor"); + program->radius = glGetUniformLocation(program->prog, "radius"); + program->additive = glGetUniformLocation(program->prog, "additive"); + program->blending = glGetUniformLocation(program->prog, "blending"); + program->randomize = glGetUniformLocation(program->prog, "randomize"); +} + +void init_blit(blit_program* program) +{ + log_info("compiling blit..."); + program->prog = compile_shader(glsl_blit_vertex, glsl_blit_fragment); + program->pos = glGetAttribLocation(program->prog, "pos"); + program->mvp = glGetUniformLocation(program->prog, "mvp"); + program->tex = glGetUniformLocation(program->prog, "tex"); + program->gridSize = glGetUniformLocation(program->prog, "gridSize"); +} + +void init_blit_div(blit_program* program) +{ + log_info("compiling blit div..."); + program->prog = compile_shader(glsl_blit_div_vertex, glsl_blit_div_fragment); + program->pos = glGetAttribLocation(program->prog, "pos"); + program->mvp = glGetUniformLocation(program->prog, "mvp"); + program->tex = glGetUniformLocation(program->prog, "tex"); +} + +void init_blit_residue(blit_residue_program* program) +{ + log_info("compiling blit residue..."); + program->prog = compile_shader(glsl_blit_div_vertex, glsl_blit_residue_fragment); + program->pos = glGetAttribLocation(program->prog, "pos"); + program->mvp = glGetUniformLocation(program->prog, "mvp"); + program->xTex = glGetUniformLocation(program->prog, "xTex"); + program->bTex = glGetUniformLocation(program->prog, "bTex"); +} + + +GLuint create_texture(int width, int height, GLenum internalFormat, GLenum format, GLenum type, char* initData) +{ + GLuint texture; + glGenTextures(1, &texture); + glBindTexture(GL_TEXTURE_2D, texture); + glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, 0, format, type, initData); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + return(texture); +} + +GLuint create_fbo(GLuint texture) +{ + GLuint fbo; + glGenFramebuffers(1, &fbo); + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + glBindTexture(GL_TEXTURE_2D, texture); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0); + return(fbo); +} + +void init_frame_buffer(frame_buffer* framebuffer, + int width, + int height, + GLenum internalFormat, + GLenum format, + GLenum type, + char* initData) +{ + for(int i=0; i<2; i++) + { + framebuffer->textures[i] = create_texture(width, height, internalFormat, format, type, initData); + framebuffer->fbos[i] = create_fbo(framebuffer->textures[i]); + } + + GLenum err = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if(err != GL_FRAMEBUFFER_COMPLETE) + { + log_info("Frame buffer incomplete, %i", err); + } +} + +void frame_buffer_swap(frame_buffer* buffer) +{ + GLuint tmp = buffer->fbos[0]; + buffer->fbos[0] = buffer->fbos[1]; + buffer->fbos[1] = tmp; + + tmp = buffer->textures[0]; + buffer->textures[0] = buffer->textures[1]; + buffer->textures[1] = tmp; +} + +//---------------------------------------------------------------- +//NOTE(martin): entry point +//---------------------------------------------------------------- + +#define texWidth (256) +#define texHeight (256) + +float colorInitData[texWidth][texHeight][4] = {0}; +float velocityInitData[texWidth][texHeight][4] = {0}; + +const float EPSILON = 1., + INV_GRID_SIZE = 1./(float)texWidth, + DELTA = 1./120.; + +const GLenum TEX_INTERNAL_FORMAT = GL_RGBA32F; +const GLenum TEX_FORMAT = GL_RGBA; +const GLenum TEX_TYPE = GL_FLOAT; + +#define square(x) ((x)*(x)) + +/* +void reset_texture(GLuint texture, float width, float height, char* initData) +{ + glBindTexture(GL_TEXTURE_2D, texture); + glTexImage2D(GL_TEXTURE_2D, 0, TEX_INTERNAL_FORMAT, width, height, 0, TEX_FORMAT, TEX_TYPE, initData); +} + +static bool resetCmd = false; + +void reset() +{ +// resetCmd = true; + log_info("reset"); + + reset_texture(colorBuffer.textures[0], texWidth, texHeight, (char*)colorInitData); + reset_texture(colorBuffer.textures[1], texWidth, texHeight, (char*)colorInitData); + reset_texture(velocityBuffer.textures[0], texWidth, texHeight, (char*)velocityInitData); + reset_texture(velocityBuffer.textures[1], texWidth, texHeight, (char*)velocityInitData); + + int gridFactor = 1; + for(int i=0; ifbos[1]); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, x->textures[0]); + glUniform1i(jacobiProgram.xTex, 0); + + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, b->textures[0]); + glUniform1i(jacobiProgram.bTex, 1); + + glDrawArrays(GL_TRIANGLES, 0, 6); + + frame_buffer_swap(x); + } +} + +void multigrid_coarsen_residual(frame_buffer* output, frame_buffer* x, frame_buffer* b, float invFineGridSize) +{ + //NOTE: compute residual and downsample to coarser grid, put result in coarser buffer + glUseProgram(multigridRestrictResidualProgram.prog); + glBindFramebuffer(GL_FRAMEBUFFER, output->fbos[1]); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, x->textures[0]); + glUniform1i(multigridRestrictResidualProgram.xTex, 0); + + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, b->textures[0]); + glUniform1i(multigridRestrictResidualProgram.bTex, 1); + + glDrawArrays(GL_TRIANGLES, 0, 6); + + frame_buffer_swap(output); +} + +void multigrid_prolongate_and_correct(frame_buffer* x, frame_buffer* error, float invFineGridSize) +{ + //NOTE: correct finer pressure + glUseProgram(multigridCorrectProgram.prog); + glBindFramebuffer(GL_FRAMEBUFFER, x->fbos[1]); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, x->textures[0]); + glUniform1i(multigridCorrectProgram.src, 0); + + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, error->textures[0]); + glUniform1i(multigridCorrectProgram.error, 1); + + glUniform1f(multigridCorrectProgram.invGridSize, invFineGridSize); + + glDrawArrays(GL_TRIANGLES, 0, 6); + + frame_buffer_swap(x); +} + +void multigrid_clear(frame_buffer* error) +{ + glBindFramebuffer(GL_FRAMEBUFFER, error->fbos[0]); + glClear(GL_COLOR_BUFFER_BIT); +} + +void input_splat(float t) +{ + //NOTE: apply force and dye + if(mouseInput.down && (mouseInput.deltaX || mouseInput.deltaY)) + { + // account for margin + float margin = 32; + + float offset = margin/texWidth; + float ratio = 1 - 2*margin/texWidth; + + float splatPosX = (mouseInput.x/frameWidth)*ratio + offset; + float splatPosY = (1 - mouseInput.y/frameHeight)*ratio + offset; + + float splatVelX = (10000.*DELTA*mouseInput.deltaX/frameWidth)*ratio; + float splatVelY = (-10000.*DELTA*mouseInput.deltaY/frameWidth)*ratio; + + float intensity = 100*sqrtf(square(ratio*mouseInput.deltaX/frameWidth) + square(ratio*mouseInput.deltaY/frameHeight)); + + float r = intensity * (sinf(2*M_PI*0.1*t) + 1); + float g = 0.5*intensity * (cosf(2*M_PI*0.1/M_E*t + 654) + 1); + float b = intensity * (sinf(2*M_PI*0.1/M_SQRT2*t + 937) + 1); + + float radius = 0.005; + + apply_splat(splatPosX, splatPosY, radius, splatVelX, splatVelY, r, g, b, false); + + mouseInput.deltaX = 0; + mouseInput.deltaY = 0; + } +} + +float testDiv[texWidth/2][texWidth/2][4]; + +mg_surface surface; + +ORCA_EXPORT void OnInit() +{ + log_info("Hello, world (from C)"); + + surface = mg_surface_gles(); + mg_surface_prepare(surface); + +// init_color_checker(); +// init_velocity_vortex(); + + // init programs + init_advect(&advectProgram); + init_div(&divProgram); + init_jacobi(&jacobiProgram); + init_multigrid_restrict_residual(&multigridRestrictResidualProgram); + init_multigrid_correct(&multigridCorrectProgram); + init_blit_residue(&blitResidueProgram); + + init_subtract(&subtractProgram); + init_splat(&splatProgram); + init_blit(&blitProgram); + init_blit_div(&blitDivProgram); + + // init frame buffers + log_info("create color buffer"); + init_frame_buffer(&colorBuffer, texWidth, texHeight, TEX_INTERNAL_FORMAT, TEX_FORMAT, TEX_TYPE, (char*)colorInitData); + log_info("create velocity buffer"); + init_frame_buffer(&velocityBuffer, texWidth, texHeight, TEX_INTERNAL_FORMAT, TEX_FORMAT, TEX_TYPE, (char*)velocityInitData); + + int gridFactor = 1; + for(int i=0; i= 0.5) + { + splat = false; + splatDir++; + splatDir = splatDir % 3; + } + float dirX = 0; + float dirY = 0; + if(splatDir == 0) + { + dirX = 0; + dirY = 0.3; + } + if(splatDir == 1) + { + dirX = 0.3; + dirY = 0; + } + if(splatDir == 2) + { + dirX = 0.2121; + dirY = 0.2121; + } + apply_splat(0.5, 0.5, dirX, dirY, 1.5, 1., 0.1, false); + } + resetCmd = false; + + if(frameCount>20) + { + return; + } + frameCount++; +*/ + + input_splat(t); + + + //NOTE: compute divergence of advected velocity + glUseProgram(divProgram.prog); + glBindFramebuffer(GL_FRAMEBUFFER, divBuffer[0].fbos[1]); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, velocityBuffer.textures[0]); + glUniform1i(divProgram.src, 0); + + glDrawArrays(GL_TRIANGLES, 0, 6); + + frame_buffer_swap(&divBuffer[0]); + + //NOTE: compute pressure + glBindFramebuffer(GL_FRAMEBUFFER, pressureBuffer[0].fbos[1]); + glClear(GL_COLOR_BUFFER_BIT); + + #if 0 + multigrid_clear(&pressureBuffer[0]); + jacobi_solve(&pressureBuffer[0], &divBuffer[0], INV_GRID_SIZE, texWidth*texHeight); + #else + multigrid_clear(&pressureBuffer[0]); + + for(int i=0; i<1; i++) + { + jacobi_solve(&pressureBuffer[0], &divBuffer[0], INV_GRID_SIZE, 2); + multigrid_coarsen_residual(&divBuffer[1], &pressureBuffer[0], &divBuffer[0], INV_GRID_SIZE); + + multigrid_clear(&pressureBuffer[1]); + jacobi_solve(&pressureBuffer[1], &divBuffer[1], 2*INV_GRID_SIZE, 2); + multigrid_coarsen_residual(&divBuffer[2], &pressureBuffer[1], &divBuffer[1], 2*INV_GRID_SIZE); + + multigrid_clear(&pressureBuffer[2]); + jacobi_solve(&pressureBuffer[2], &divBuffer[2], 4*INV_GRID_SIZE, 30); + + multigrid_prolongate_and_correct(&pressureBuffer[1], &pressureBuffer[2], 2*INV_GRID_SIZE); + jacobi_solve(&pressureBuffer[1], &divBuffer[1], 2*INV_GRID_SIZE, 8); + + multigrid_prolongate_and_correct(&pressureBuffer[0], &pressureBuffer[1], INV_GRID_SIZE); + jacobi_solve(&pressureBuffer[0], &divBuffer[0], INV_GRID_SIZE, 4); + } + #endif + + + + + //NOTE: subtract pressure gradient to advected velocity + glUseProgram(subtractProgram.prog); + glBindFramebuffer(GL_FRAMEBUFFER, velocityBuffer.fbos[1]); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, velocityBuffer.textures[0]); + glUniform1i(subtractProgram.src, 0); + + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, pressureBuffer[0].textures[0]); + glUniform1i(subtractProgram.pressure, 1); + + glUniform1f(subtractProgram.invGridSize, INV_GRID_SIZE); + + glDrawArrays(GL_TRIANGLES, 0, 6); + + frame_buffer_swap(&velocityBuffer); + + //NOTE: Advect color through corrected velocity field + glUseProgram(advectProgram.prog); + glBindFramebuffer(GL_FRAMEBUFFER, colorBuffer.fbos[1]); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, colorBuffer.textures[0]); + glUniform1i(advectProgram.src, 0); + + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, velocityBuffer.textures[0]); + glUniform1i(advectProgram.velocity, 1); + + glUniform1f(advectProgram.delta, DELTA); + + glUniform1f(advectProgram.dissipation, 0.001); + + glDrawArrays(GL_TRIANGLES, 0, 6); + + frame_buffer_swap(&colorBuffer); + + //NOTE: Blit color texture to screen + + //NOTE: blit residue to screen + glViewport(0, 0, frameWidth, frameHeight); + + float displayMatrix[16] = { + 1/aspectRatio, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 }; + +/* + glUseProgram(blitResidueProgram.prog); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, pressureBuffer[0].textures[0]); + glUniform1i(blitResidueProgram.xTex, 0); + + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, divBuffer[0].textures[0]); + glUniform1i(blitResidueProgram.bTex, 1); + + glUniformMatrix4fv(blitResidueProgram.mvp, 1, GL_FALSE, displayMatrix); + + glDrawArrays(GL_TRIANGLES, 0, 6); +//*/ +//* + glUseProgram(blitProgram.prog); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, colorBuffer.textures[0]); + glUniform1i(blitProgram.tex, 0); + + glUniform2i(blitProgram.gridSize, texWidth, texHeight); + + glUniformMatrix4fv(blitProgram.mvp, 1, GL_FALSE, displayMatrix); + + glDrawArrays(GL_TRIANGLES, 0, 6); +/*/ + + //NOTE: recompute divergence of (corrected) velocity + glUseProgram(divProgram.prog); + glBindFramebuffer(GL_FRAMEBUFFER, divBuffer[0].fbos[1]); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, velocityBuffer.textures[0]); + glUniform1i(divProgram.src, 0); + + glDrawArrays(GL_TRIANGLES, 0, 6); + + frame_buffer_swap(&divBuffer[0]); + + //NOTE: Blit divergence to screen + glViewport(0, 0, canvas_width(), canvas_height()); + glUseProgram(blitDivProgram.prog); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, divBuffer[0].textures[0]); + glUniform1i(blitDivProgram.tex, 0); + + glUniformMatrix4fv(blitDivProgram.mvp, 1, GL_FALSE, displayMatrix); + + glDrawArrays(GL_TRIANGLES, 0, 6); + +//*/ + + mg_surface_present(surface); +} diff --git a/samples/fluid/src/shaders/advect.glsl b/samples/fluid/src/shaders/advect.glsl new file mode 100644 index 0000000..28cbd2c --- /dev/null +++ b/samples/fluid/src/shaders/advect.glsl @@ -0,0 +1,56 @@ +#version 300 es + +precision highp float; +precision highp sampler2D; + +in vec2 texCoord; +out vec4 fragColor; + +uniform sampler2D src; +uniform sampler2D velocity; +uniform float delta; +uniform float dissipation; + +vec2 u(ivec2 coord) +{ + return(texelFetch(velocity, coord, 0).xy); +} + +vec4 q(ivec2 coord) +{ + if( coord.x < 0 + || coord.x >= textureSize(src, 0).x + || coord.y < 0 + || coord.y >= textureSize(src, 0).y) + { + return(vec4(0.)); + } + return(texelFetch(src, coord, 0)); +} + +vec4 bilerpSrc(vec2 pos) +{ + vec2 offset = fract(pos); + + ivec2 bl = ivec2(floor(pos)); + + ivec2 br = bl + ivec2(1, 0); + ivec2 tl = bl + ivec2(0, 1); + ivec2 tr = bl + ivec2(1, 1); + + vec4 lerpTop = (1.-offset.x)*q(tl) + offset.x*q(tr); + vec4 lerpBottom = (1.-offset.x)*q(bl) + offset.x*q(br); + vec4 result = (1.-offset.y)*lerpBottom + offset.y*lerpTop; + + return(result); +} + +void main() +{ + float texWidth = float(textureSize(velocity, 0).x); + + ivec2 pixelCoord = ivec2(floor(gl_FragCoord.xy)); + + vec2 samplePos = vec2(pixelCoord) - texWidth * delta * u(pixelCoord); + fragColor = bilerpSrc(samplePos) / (1. + dissipation*delta); +} diff --git a/samples/fluid/src/shaders/blit_div_fragment.glsl b/samples/fluid/src/shaders/blit_div_fragment.glsl new file mode 100644 index 0000000..2beaac8 --- /dev/null +++ b/samples/fluid/src/shaders/blit_div_fragment.glsl @@ -0,0 +1,34 @@ +#version 300 es + +precision highp float; +precision highp sampler2D; + +in vec2 texCoord; +out vec4 fragColor; + +uniform sampler2D tex; + +vec3 color_map(float v) +{ + float logv = log(abs(v))/log(10.0); + float f = floor(logv + 7.0); + float i = floor(4.0*(logv + 7.0 - f)); + + if(f < 0.0) return vec3(0.0); + if(f < 1.0) return mix(vec3(1.0, 0.0, 0.0), vec3(1.0), i/4.0); + if(f < 2.0) return mix(vec3(0.0, 1.0, 0.0), vec3(1.0), i/4.0); + if(f < 3.0) return mix(vec3(0.0, 0.0, 1.0), vec3(1.0), i/4.0); + if(f < 4.0) return mix(vec3(1.0, 1.0, 0.0), vec3(1.0), i/4.0); + if(f < 5.0) return mix(vec3(1.0, 0.0, 1.0), vec3(1.0), i/4.0); + if(f < 6.0) return mix(vec3(0.0, 1.0, 1.0), vec3(1.0), i/4.0); + if(f < 7.0) return mix(vec3(1.0, 0.5, 0.0), vec3(1.0), i/4.0); + if(f < 8.0) return mix(vec3(1.0, 1.0, 1.0), vec3(1.0), i/4.0); + return vec3(1.0); +} + +void main() +{ + ivec2 pixelCoord = ivec2(floor(texCoord.xy * vec2(textureSize(tex, 0).xy))); + float f = texelFetch(tex, pixelCoord, 0).x; + fragColor = vec4(color_map(f), 1.0); +} diff --git a/samples/fluid/src/shaders/blit_div_vertex.glsl b/samples/fluid/src/shaders/blit_div_vertex.glsl new file mode 100644 index 0000000..f69dc76 --- /dev/null +++ b/samples/fluid/src/shaders/blit_div_vertex.glsl @@ -0,0 +1,14 @@ +#version 300 es + +precision highp float; + +in vec2 pos; +out vec2 texCoord; + +uniform mat4 mvp; + +void main() +{ + texCoord = 0.5*(pos + vec2(1,1)); + gl_Position = mvp * vec4(pos, 0, 1); +} diff --git a/samples/fluid/src/shaders/blit_fragment.glsl b/samples/fluid/src/shaders/blit_fragment.glsl new file mode 100644 index 0000000..4e905a3 --- /dev/null +++ b/samples/fluid/src/shaders/blit_fragment.glsl @@ -0,0 +1,15 @@ +#version 300 es + +precision highp float; +precision highp sampler2D; + +in vec2 texCoord; +out vec4 fragColor; + +uniform sampler2D tex; + +void main() +{ + fragColor = texture(tex, texCoord); + fragColor.a = 1.0; +} diff --git a/samples/fluid/src/shaders/blit_residue_fragment.glsl b/samples/fluid/src/shaders/blit_residue_fragment.glsl new file mode 100644 index 0000000..f630f41 --- /dev/null +++ b/samples/fluid/src/shaders/blit_residue_fragment.glsl @@ -0,0 +1,65 @@ +#version 300 es + +precision highp float; +precision highp sampler2D; + +in vec2 texCoord; +out vec4 fragColor; + +uniform sampler2D xTex; +uniform sampler2D bTex; + +float x(ivec2 coord) +{ + if( coord.x <= 0 + || coord.x >= textureSize(xTex, 0).x + || coord.y <= 0 + || coord.y >= textureSize(xTex, 0).y) + { + return(0.); + } + return(texelFetch(xTex, coord, 0).x); +} + +float b(ivec2 coord) +{ + if( coord.x <= 0 + || coord.x >= textureSize(bTex, 0).x + || coord.y <= 0 + || coord.y >= textureSize(bTex, 0).y) + { + return(0.); + } + return(texelFetch(bTex, coord, 0).x); +} + +vec3 color_map(float v) +{ + float logv = log(abs(v))/log(10.0); + float f = floor(logv + 7.0); + float i = floor(4.0*(logv + 7.0 - f)); + + if(f < 0.0) return vec3(0.0); + if(f < 1.0) return mix(vec3(1.0, 0.0, 0.0), vec3(1.0), i/4.0); + if(f < 2.0) return mix(vec3(0.0, 1.0, 0.0), vec3(1.0), i/4.0); + if(f < 3.0) return mix(vec3(0.0, 0.0, 1.0), vec3(1.0), i/4.0); + if(f < 4.0) return mix(vec3(1.0, 1.0, 0.0), vec3(1.0), i/4.0); + if(f < 5.0) return mix(vec3(1.0, 0.0, 1.0), vec3(1.0), i/4.0); + if(f < 6.0) return mix(vec3(0.0, 1.0, 1.0), vec3(1.0), i/4.0); + if(f < 7.0) return mix(vec3(1.0, 0.5, 0.0), vec3(1.0), i/4.0); + if(f < 8.0) return mix(vec3(1.0, 1.0, 1.0), vec3(1.0), i/4.0); + return vec3(1.0); +} + +void main() +{ + ivec2 pixelCoord = ivec2(floor(texCoord.xy * vec2(textureSize(xTex, 0).xy))); + + float tl = x(pixelCoord + ivec2(-1, 1)); + float tr = x(pixelCoord + ivec2(1, 1)); + float bl = x(pixelCoord + ivec2(-1, -1)); + float br = x(pixelCoord + ivec2(1, -1)); + + float residue = b(pixelCoord) - (-tl - tr - bl - br + 4.*x(pixelCoord)); + fragColor = vec4(color_map(residue), 1); +} diff --git a/samples/fluid/src/shaders/blit_vertex.glsl b/samples/fluid/src/shaders/blit_vertex.glsl new file mode 100644 index 0000000..d397a76 --- /dev/null +++ b/samples/fluid/src/shaders/blit_vertex.glsl @@ -0,0 +1,18 @@ +#version 300 es + +precision highp float; + +in vec2 pos; +out vec2 texCoord; + +uniform mat4 mvp; +uniform ivec2 gridSize; + +void main() +{ + float margin = 32.; + float ratio = 1. - 2.*margin/float(gridSize.x); + + texCoord = margin/float(gridSize.x) + ratio*(0.5*(pos + vec2(1,1))); + gl_Position = mvp * vec4(pos, 0, 1); +} diff --git a/samples/fluid/src/shaders/common_vertex.glsl b/samples/fluid/src/shaders/common_vertex.glsl new file mode 100644 index 0000000..1dd2519 --- /dev/null +++ b/samples/fluid/src/shaders/common_vertex.glsl @@ -0,0 +1,12 @@ +#version 300 es + +precision highp float; + +in vec2 pos; +out vec2 texCoord; + +void main() +{ + texCoord = 0.5*(pos + vec2(1,1)); + gl_Position = vec4(pos, 0, 1); +} diff --git a/samples/fluid/src/shaders/divergence.glsl b/samples/fluid/src/shaders/divergence.glsl new file mode 100644 index 0000000..8b23781 --- /dev/null +++ b/samples/fluid/src/shaders/divergence.glsl @@ -0,0 +1,41 @@ +#version 300 es + +precision highp float; +precision highp sampler2D; + +in vec2 texCoord; +out vec4 fragColor; + +uniform sampler2D src; + +vec2 u(ivec2 coord) +{ + return(texelFetch(src, coord, 0).xy); +} + +void main() +{ + ivec2 pixelCoord = ivec2(floor(gl_FragCoord.xy)); + + if( pixelCoord.x <= 0 + || pixelCoord.x >= textureSize(src, 0).x + || pixelCoord.y <= 0 + || pixelCoord.y >= textureSize(src, 0).y) + { + fragColor = vec4(0, 0, 0, 1); + } + else + { + vec2 tl = u(pixelCoord + ivec2(-1, 0)); + vec2 tr = u(pixelCoord); + vec2 bl = u(pixelCoord + ivec2(-1, -1)); + vec2 br = u(pixelCoord + ivec2(0, -1)); + + float r = (tr.x + br.x)/2.; + float l = (tl.x + bl.x)/2.; + float t = (tl.y + tr.y)/2.; + float b = (bl.y + br.y)/2.; + + fragColor = vec4(-2.*(r - l + t - b), 0, 0, 1); + } +} diff --git a/samples/fluid/src/shaders/jacobi_step.glsl b/samples/fluid/src/shaders/jacobi_step.glsl new file mode 100644 index 0000000..35418b9 --- /dev/null +++ b/samples/fluid/src/shaders/jacobi_step.glsl @@ -0,0 +1,55 @@ +#version 300 es + +precision highp float; +precision highp sampler2D; + +in vec2 texCoord; +out vec4 fragColor; + +uniform sampler2D xTex; +uniform sampler2D bTex; + +float x(ivec2 coord) +{ + if( coord.x <= 0 + || coord.x >= textureSize(xTex, 0).x + || coord.y <= 0 + || coord.y >= textureSize(xTex, 0).y) + { + return(0.); + } + return(texelFetch(xTex, coord, 0).x); +} + +float b(ivec2 coord) +{ + if( coord.x <= 0 + || coord.x >= textureSize(bTex, 0).x + || coord.y <= 0 + || coord.y >= textureSize(bTex, 0).y) + { + return(0.); + } + return(texelFetch(bTex, coord, 0).x); +} + +void main() +{ + ivec2 pixelCoord = ivec2(floor(gl_FragCoord.xy)); + + if( pixelCoord.x <= 0 + || pixelCoord.y <= 0) + { + fragColor = vec4(0, 0, 0, 1); + } + else + { + float tl = x(pixelCoord + ivec2(-1, 1)); + float tr = x(pixelCoord + ivec2(1, 1)); + float bl = x(pixelCoord + ivec2(-1, -1)); + float br = x(pixelCoord + ivec2(1, -1)); + + float jacobi = (tl + tr + bl + br + b(pixelCoord))/4.; + fragColor = vec4(jacobi, 0, 0, 1); + } +} diff --git a/samples/fluid/src/shaders/multigrid_correct.glsl b/samples/fluid/src/shaders/multigrid_correct.glsl new file mode 100644 index 0000000..1d59ec7 --- /dev/null +++ b/samples/fluid/src/shaders/multigrid_correct.glsl @@ -0,0 +1,53 @@ +#version 300 es + +precision highp float; +precision highp sampler2D; + +in vec2 texCoord; +out vec4 fragColor; + +uniform sampler2D src; +uniform sampler2D error; +uniform float invGridSize; + +float e(ivec2 coord) +{ + if( coord.x <= 0 + || coord.x >= textureSize(error, 0).x + || coord.y <= 0 + || coord.y >= textureSize(error, 0).y) + { + return(0.); + } + return(texelFetch(error, coord, 0).x); +} + +float p(ivec2 coord) +{ + if( coord.x <= 0 + || coord.x >= textureSize(src, 0).x + || coord.y <= 0 + || coord.y >= textureSize(src, 0).y) + { + return(0.); + } + return(texelFetch(src, coord, 0).x); +} + +void main() +{ + ivec2 pixelCoord = ivec2(floor(gl_FragCoord.xy)); + vec2 coarseCoord = vec2(pixelCoord)/2.; + vec2 offset = fract(coarseCoord); + + ivec2 bl = ivec2(floor(coarseCoord)); + ivec2 br = bl + ivec2(1, 0); + ivec2 tl = bl + ivec2(0, 1); + ivec2 tr = bl + ivec2(1, 1); + + float topLerp = (1.-offset.x)*e(tl)+ offset.x*e(tr); + float bottomLerp = (1.-offset.x)*e(bl) + offset.x*e(br); + float bilerpError = (1.-offset.y)*bottomLerp + offset.y*topLerp; + + fragColor = vec4(p(pixelCoord) + bilerpError, 0, 0, 1); +} diff --git a/samples/fluid/src/shaders/multigrid_restrict_residual.glsl b/samples/fluid/src/shaders/multigrid_restrict_residual.glsl new file mode 100644 index 0000000..bea8b9c --- /dev/null +++ b/samples/fluid/src/shaders/multigrid_restrict_residual.glsl @@ -0,0 +1,61 @@ +#version 300 es + +precision highp float; +precision highp sampler2D; + +in vec2 texCoord; +out vec4 fragColor; + +uniform sampler2D xTex; +uniform sampler2D bTex; + +float x(ivec2 coord) +{ + if( coord.x <= 0 + || coord.x >= textureSize(xTex, 0).x + || coord.y <= 0 + || coord.y >= textureSize(xTex, 0).y) + { + return(0.); + } + return(texelFetch(xTex, coord, 0).x); +} + +float b(ivec2 coord) +{ + if( coord.x <= 0 + || coord.x >= textureSize(bTex, 0).x + || coord.y <= 0 + || coord.y >= textureSize(bTex, 0).y) + { + return(0.); + } + return(texelFetch(bTex, coord, 0).x); +} + +float residual(ivec2 coord) +{ + ivec2 vr = coord + ivec2(1, 0); + ivec2 vl = coord - ivec2(1, 0); + ivec2 vt = coord + ivec2(0, 1); + ivec2 vb = coord - ivec2(0, 1); + + return((x(vl) + x(vr) + x(vt) + x(vb) + b(coord) - 4.*x(coord))*4.); +} + +void main() +{ + ivec2 pixelCoord = ivec2(floor(gl_FragCoord.xy)); + + float restricted = residual(2*pixelCoord + ivec2(-1, -1)) + + residual(2*pixelCoord + ivec2(1, -1)) + + residual(2*pixelCoord + ivec2(1, 1)) + + residual(2*pixelCoord + ivec2(-1, 1)) + + 2.*residual(2*pixelCoord + ivec2(-1, 0)) + + 2.*residual(2*pixelCoord + ivec2(1, 0)) + + 2.*residual(2*pixelCoord + ivec2(0, -1)) + + 2.*residual(2*pixelCoord + ivec2(0, 1)) + + 4.*residual(2*pixelCoord); + restricted /= 16.; + fragColor = vec4(restricted, 0, 0, 1); +} diff --git a/samples/fluid/src/shaders/splat.glsl b/samples/fluid/src/shaders/splat.glsl new file mode 100644 index 0000000..34a1347 --- /dev/null +++ b/samples/fluid/src/shaders/splat.glsl @@ -0,0 +1,29 @@ +#version 300 es + +precision highp float; +precision highp sampler2D; + +in vec2 texCoord; +out vec4 fragColor; + +uniform sampler2D src; +uniform vec2 splatPos; +uniform vec3 splatColor; +uniform float radius; +uniform float additive; +uniform float blending; + +uniform float randomize; + +void main() +{ + float d2 = dot(texCoord - splatPos, texCoord - splatPos); + float intensity = exp(-10.*d2/radius); + vec2 force = splatColor.xy; + + vec3 u = texture(src, texCoord).xyz; + vec3 uAdd = u + intensity*splatColor.xyz; + vec3 uBlend = u*(1.-intensity) + intensity * splatColor; + + fragColor = vec4(uAdd*additive + uBlend*blending, 1); +} diff --git a/samples/fluid/src/shaders/subtract_pressure.glsl b/samples/fluid/src/shaders/subtract_pressure.glsl new file mode 100644 index 0000000..7905df9 --- /dev/null +++ b/samples/fluid/src/shaders/subtract_pressure.glsl @@ -0,0 +1,47 @@ +#version 300 es + +precision highp float; +precision highp sampler2D; + +in vec2 texCoord; +out vec4 fragColor; + +uniform sampler2D src; +uniform sampler2D pressure; +uniform float invGridSize; + +vec2 u(ivec2 coord) +{ + return(texelFetch(src, coord, 0).xy); +} + +float p(ivec2 coord) +{ + if( coord.x <= 0 + || coord.x >= textureSize(pressure, 0).x + || coord.y <= 0 + || coord.y >= textureSize(pressure, 0).y) + { + return(0.); + } + return(texelFetch(pressure, coord, 0).x); +} + +void main() +{ + ivec2 pixelCoord = ivec2(floor(gl_FragCoord.xy)); + + float tl = p(pixelCoord + ivec2(0, 1)); + float tr = p(pixelCoord + ivec2(1, 1)); + float bl = p(pixelCoord); + float br = p(pixelCoord + ivec2(1, 0)); + + float r = (tr + br)/2.; + float l = (tl + bl)/2.; + float t = (tl + tr)/2.; + float b = (bl + br)/2.; + + vec2 gradP = vec2(r - l, t - b); + + fragColor = vec4(u(pixelCoord) - gradP, 0, 1); +} diff --git a/samples/glesTriangle/.gitignore b/samples/glesTriangle/.gitignore new file mode 100644 index 0000000..4c0e970 --- /dev/null +++ b/samples/glesTriangle/.gitignore @@ -0,0 +1,3 @@ +Triangle +profile.dtrace +profile.spall diff --git a/samples/glesTriangle/build.bat b/samples/glesTriangle/build.bat new file mode 100644 index 0000000..add38e3 --- /dev/null +++ b/samples/glesTriangle/build.bat @@ -0,0 +1,17 @@ +@echo off + +:: compile wasm module +set wasmFlags=--target=wasm32^ + --no-standard-libraries ^ + -fno-builtin ^ + -Wl,--no-entry ^ + -Wl,--export-dynamic ^ + -g ^ + -O2 ^ + -mbulk-memory ^ + -D__ORCA__ ^ + -isystem ..\..\cstdlib\include -I ..\..\sdk -I..\..\milepost\ext -I ..\..\milepost -I ..\..\milepost\src + +clang %wasmFlags% -o .\module.wasm ..\..\sdk\orca.c ..\..\cstdlib\src\*.c src\main.c + +python3 ..\..\scripts\mkapp.py --orca-dir ..\.. --name Triangle module.wasm diff --git a/samples/glesTriangle/build.sh b/samples/glesTriangle/build.sh new file mode 100755 index 0000000..32f59df --- /dev/null +++ b/samples/glesTriangle/build.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +set -euo pipefail + +if [[ -x /usr/local/opt/llvm/bin/clang ]]; then + CLANG=/usr/local/opt/llvm/bin/clang +elif [[ -x /opt/homebrew/opt/llvm/bin/clang ]]; then + CLANG=/opt/homebrew/opt/llvm/bin/clang +else + echo "Could not find Homebrew clang; this script will probably not work." + CLANG=clang +fi + +STDLIB_DIR=../../cstdlib +ORCA_SDK_DIR=../../sdk +MILEPOST_DIR=../../milepost + +wasmFlags="--target=wasm32 \ + --no-standard-libraries \ + -fno-builtin \ + -Wl,--no-entry \ + -Wl,--export-dynamic \ + -g \ + -O2 \ + -mbulk-memory \ + -D__ORCA__ \ + -I $STDLIB_DIR/include \ + -I $ORCA_SDK_DIR \ + -I $MILEPOST_DIR/ext -I $MILEPOST_DIR -I $MILEPOST_DIR/src" + +$CLANG $wasmFlags -o ./module.wasm ../../sdk/orca.c ../../cstdlib/src/*.c src/main.c + +orca bundle --orca-dir ../.. --name Triangle module.wasm diff --git a/samples/glesTriangle/src/main.c b/samples/glesTriangle/src/main.c new file mode 100644 index 0000000..c0b0dd6 --- /dev/null +++ b/samples/glesTriangle/src/main.c @@ -0,0 +1,114 @@ +#include +#include +#include + +#include + +vec2 frameSize = {100, 100}; + +mg_surface surface; + +unsigned int program; + +const char* vshaderSource = + "attribute vec4 vPosition;\n" + "uniform mat4 transform;\n" + "void main()\n" + "{\n" + " gl_Position = transform*vPosition;\n" + "}\n"; + +const char* fshaderSource = + "precision mediump float;\n" + "void main()\n" + "{\n" + " gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n" + "}\n"; + +void compile_shader(GLuint shader, const char* source) +{ + glShaderSource(shader, 1, &source, 0); + glCompileShader(shader); + + int err = glGetError(); + if(err) + { + log_info("gl error"); + } +} + +char* ORCA_IMPORT(orca_mem_grow)(u64 size); + +ORCA_EXPORT void OnInit(void) +{ + surface = mg_surface_gles(); + mg_surface_prepare(surface); + + const char* extensions = (const char*)glGetString(GL_EXTENSIONS); + log_info("GLES extensions: %s\n", extensions); + + int extensionCount = 0; + glGetIntegerv(GL_NUM_EXTENSIONS, &extensionCount); + for(int i=0; i= (char*)_mem) && (((char*)'+ argName +' - (char*)_mem) < m3_GetMemorySize(runtime)), "parameter \''+argName+'\' is out of bounds");\n' + s += '\t\tORCA_ASSERT((char*)' + argName + ' + ' + + proc = argLen.get('proc') + if proc != None: + s += proc + '(runtime, ' + lenProcArgs = argLen['args'] + for i, lenProcArg in enumerate(lenProcArgs): + s += lenProcArg + if i < len(lenProcArgs)-1: + s += ', ' + s += ')' + else: + components = argLen.get('components') + countArg = argLen.get('count') + + if components != None: + s += str(components) + if countArg != None: + s += '*' + if countArg != None: + s += countArg + + if typeCName.endswith('**') or (typeCName.startswith('void') == False and typeCName.startswith('const void') == False): + s += '*sizeof('+typeCName[:-1]+')' + + s += ' <= ((char*)_mem + m3_GetMemorySize(runtime)), "parameter \''+argName+'\' overflows wasm memory");\n' + s += '\t}\n' + + s += '\t' + + if retTag == 'i': + s += '*((i32*)&_sp[0]) = (i32)' + elif retTag == 'I': + s += '*((i64*)&_sp[0]) = (i64)' + elif retTag == 'f': + s += '*((f32*)&_sp[0]) = (f32)' + elif retTag == 'F': + s += '*((f64*)&_sp[0]) = (f64)' + elif retTag == 'S': + retTypeName = decl['ret']['name'] + retTypeCName = decl['ret'].get('cname', retTypeName) + s += '*(' + retTypeCName + '*)((char*)_mem + *(i32*)&_sp[0]) = ' + elif retTag == 'p': + print("Warning: " + name + ": pointer return type not supported yet") + + s += cname + '(' + + for i, arg in enumerate(decl['args']): + s += arg['name'] + if i+1 < len(decl['args']): s += ', ' @@ -158,8 +214,9 @@ def bindgen(apiName, spec, **kwargs): print(s, file=host_bindings) # link function - s = 'int bindgen_link_' + apiName + '_api(IM3Module module)\n{\n\t' - s += 'M3Result res;\n' + s = 'int bindgen_link_' + apiName + '_api(IM3Module module)\n{\n' + s += ' M3Result res;\n' + s += ' int ret = 0;\n' for decl in data: name = decl['name'] @@ -185,11 +242,14 @@ def bindgen(apiName, spec, **kwargs): m3Sig += ')' - s += '\tres = m3_LinkRawFunction(module, "*", "' + name + '", "' + m3Sig + '", ' + cname + '_stub);\n' - s += '\tif(res != m3Err_none && res != m3Err_functionLookupFailed) { log_error("error: %s\\n", res); return(-1); }\n\n' + s += ' res = m3_LinkRawFunction(module, "*", "' + name + '", "' + m3Sig + '", ' + cname + '_stub);\n' + s += ' if(res != m3Err_none && res != m3Err_functionLookupFailed)\n' + s += ' {\n' + s += ' log_error("Couldn\'t link function ' + name + ' (%s)\\n", res);\n' + s += ' ret = -1;\n' + s += ' }\n\n' - - s += '\treturn(0);\n}\n' + s += '\treturn(ret);\n}\n' print(s, file=host_bindings) @@ -213,7 +273,7 @@ if __name__ == "__main__": wasm3_bindings_path = args.wasm3_bindings if wasm3_bindings_path == None: wasm3_bindings_path = 'bindgen_' + apiName + '_wasm3_bindings.c' - + bindgen(apiName, spec, guest_stubs=guest_stubs_path, guest_include=args.guest_include, diff --git a/scripts/bundle.py b/scripts/bundle.py index 1cd4791..7fe9e9a 100644 --- a/scripts/bundle.py +++ b/scripts/bundle.py @@ -171,6 +171,8 @@ def macos_make_app(args): icon.icns NSHighResolutionCapable True + MetalCaptureEnabled + """ diff --git a/scripts/dev.py b/scripts/dev.py index 571b9d0..7f258ab 100644 --- a/scripts/dev.py +++ b/scripts/dev.py @@ -10,6 +10,7 @@ from zipfile import ZipFile from . import checksum from .bindgen import bindgen +from .gles_gen import gles_gen from .log import * from .utils import pushd, removeall @@ -374,6 +375,12 @@ def gen_all_bindings(): bindgen("core", "src/core_api.json", wasm3_bindings="src/core_api_bind_gen.c", ) + + gles_gen("milepost/ext/gl.xml", + "src/gles_api.json", + "sdk/gl31.h" + ) + bindgen("gles", "src/gles_api.json", wasm3_bindings="src/gles_api_bind_gen.c", ) diff --git a/scripts/gles_gen.py b/scripts/gles_gen.py new file mode 100644 index 0000000..cb3377e --- /dev/null +++ b/scripts/gles_gen.py @@ -0,0 +1,315 @@ +from .reg_modified import * +import xml.etree.ElementTree as et +from argparse import ArgumentParser + +# remove APIs that can't be sandboxed +removeProc = [ + "glMapBuffer", + "glMapBufferRange", + "glUnmapBuffer", + "glFlushMappedBufferRange", + "glGetBufferPointerv" +] + +def gen_gles_header(spec, filename): + # Generates the GLES header, wrapping gl functions + # prototypes in ORCA_IMPORT() macro + + gles2through31Pat = '2\.[0-9]|3\.[01]' + allVersions = '.*' + + genOpts = CGeneratorOptions( + filename=filename, + apiname='gles2', + profile='common', + versions=gles2through31Pat, + emitversions=allVersions, + protectProto=False, + procMacro='ORCA_IMPORT', + removeProc = removeProc) + + reg = Registry() + tree = et.parse(spec) + reg.loadElementTree(tree) + + logFile = open('./gles_gen.log', 'w') + gen = COutputGenerator(diagFile=logFile) + reg.setGenerator(gen) + reg.apiGen(genOpts) + + logFile.close() + + +def get_bindgen_tag_for_type(typeName): + typeToTags = { + "void": "v", + + "GLenum": "i", + "GLbitfield": "i", + + "GLboolean": "i", + "GLbyte": "i", + "GLubyte": "i", + "GLchar": "i", + + "GLshort": "i", + "GLushort": "i", + "GLhalf": "i", + "GLhalfARB": "i", + + "GLuint": "i", + "GLint": "i", + "GLclampx": "i", + "GLsizei": "i", + "GLfixed": "i", + + "GLintptr": "i", + "GLsizeiptr": "i", + + "GLuint64": "I", + "GLint64": "I", + + "GLfloat": "f", + "GLclampf": "f", + + "GLdouble": "F", + "GLclampd": "F", + + #NOTE: we treat sync objects as opaque 64bit values + #TODO we should _also_ make sure that Wasm code treat them as 64bit values + "GLsync": "I" + } + + if typeName[len(typeName)-1] == '*': + return "p" + else: + tag = typeToTags.get(typeName) + return tag + + +def gen_compsize_len_entry(name, argName, compsizeArgs): + + entry = '\t\t\t"len": {\n' + entry += '\t\t\t\t"proc": "orca_'+ name +'_'+argName+'_length",\n' + entry += '\t\t\t\t"args": [' + + for i, compsizeArg in enumerate(compsizeArgs): + entry += '"' + compsizeArg + '"' + if i < len(compsizeArgs)-1: + entry += ', ' + entry += ']\n' + entry += '\t\t\t}' + return entry + +def gen_argcount_len_entry(name, argName, tokens): + + entry = '\t\t\t"len": {' + if len(tokens) == 2: + if tokens[1].isnumeric() == False: + print("Warning: function " + name + ": couldn't parse parameter '" + argName + "' lenght attribute") + entry += '"count": "' + tokens[0] + '", "components": '+ tokens[1] + elif len(tokens) == 1: + if tokens[0].isnumeric(): + entry += '"components":'+ tokens[0] + else: + entry += '"count": "'+ tokens[0] + '"' + else: + print("Warning: function " + name + ": couldn't parse parameter '" + argName + "' lenght attribute") + + entry += '}' + return entry + +def gen_gles_bindgen_json(spec, filename): + + # Gather gles 3.1 required functions + tree = et.parse(spec) + api = [] + + for feature in tree.iterfind('feature[@api="gles2"]'): + if float(feature.get('number')) > 3.1: + break + + for require in feature.iter('require'): + if require.get('profile') == 'compatibility': + continue + for command in require.iter('command'): + if command.get('name') not in removeProc: + api.append(command.get('name')) + + for remove in feature.iter('remove'): + for command in remove.iter('command'): + api.remove(command.get('name')) + + # put all GL commands in a dict + commands = dict() + commandsSpec = tree.find('./commands') + for command in commandsSpec.iter('command'): + name = command.find('proto/name') + commands[name.text] = command + + # TODO: Generate json descriptions for commands in api + + manualBind = [ + "glShaderSource", + "glGetVertexAttribPointerv", + "glVertexAttribPointer", + "glVertexAttribIPointer", + "glGetString", + "glGetStringi", + "glGetUniformIndices" + ] + + json = '[\n' + for name in api: + if name in manualBind: + continue + + command = commands.get(name) + if command == None: + print("Couldn't find definition for required command '" + name + "'") + exit(-1) + + proto = command.find("proto") + ptype = proto.find("ptype") + + retType = '' + if proto.text != None: + retType += proto.text + + if ptype != None: + if ptype.text != None: + retType += ptype.text + if ptype.tail != None: + retType += ptype.tail + + retType = retType.strip() + + retTag = get_bindgen_tag_for_type(retType) + if retTag == None: + print("Couldn't find tag for GL type '" + retType + "'") + exit(-1) + + entry = '{\n\t"name": "' + name + '",\n' + entry += '\t"cname": "' + name + '",\n' + entry += '\t"ret": { "name": "' + retType + '", "tag": "' + retTag + '"},\n' + + entry += '\t"args": [ ' + + # iterate through params + for param in command.iter('param'): + + argNode = param.find('name') + argName = argNode.text + + typeNode = param.find('ptype') + + typeName = '' + + if param.text != None: + typeName += param.text + + if typeNode != None: + if typeNode.text != None: + typeName += typeNode.text + if typeNode.tail != None: + typeName += typeNode.tail + + typeName = typeName.strip() + + if typeName.endswith('**'): + print("Warning: function " + name + ": parameter " + argName + " has 2 (or more) levels of indirection") + + typeTag = get_bindgen_tag_for_type(typeName) + + if typeTag == None: + print("Couldn't find tag for GL type '" + typeName + "' in function '"+ name +"'") + exit(-1) + + entry += '\n' + entry += '\t\t{\n\t\t\t"name": "'+ argName +'",\n' + entry += '\t\t\t"type": {"name": "'+ typeName +'", "tag": "'+ typeTag +'"}' + + lenString = param.get('len') + + nullStringProcWithNoLen = [ + "glBindAttribLocation", + "glGetAttribLocation", + "glGetUniformLocation" + ] + + drawIndirectProc = [ + "glDrawArraysIndirect", + "glDrawElementsIndirect" + ] + + if typeTag == "p": + if lenString == None: + if name in drawIndirectProc: + entry += ',\n' + entry += gen_compsize_len_entry(name, argName, ['indirect']) + elif name in nullStringProcWithNoLen: + entry += ',\n' + entry += gen_compsize_len_entry(name, argName, ['name']) + else: + print("Warning: function " + name + ": parameter " + argName + " has no len attribute") + + elif lenString != None: + entry += ',\n' + + tokens = lenString.split('*') + + if lenString.startswith("COMPSIZE"): + tmp = lenString + if tmp.startswith("COMPSIZE("): + tmp = tmp[len("COMPSIZE("):] + if tmp.endswith(")"): + tmp = tmp[:-1] + + compsizeArgs = list(filter(None, tmp.split(","))) + + if len(compsizeArgs) == 0: + # special case glGetUniformBlockIndex which isn't specified correctly in gl.xml + if name == 'glGetUniformBlockIndex': + compsizeArgs = ['uniformBlockName'] + + entry += gen_compsize_len_entry(name, argName, compsizeArgs) + + else: + entry += gen_argcount_len_entry(name, argName, tokens) + + entry += '\n\t\t},' + + entry = entry[:-1] + entry += '\n\t]\n}' + + json += entry + json += ',\n' + + json = json[:-2] + json += '\n]' + + # write json to jsonFile + f = open(filename, 'w') + f.write(json) + f.close() + +def gles_gen(spec, json, header): + gen_gles_header(spec, header) + gen_gles_bindgen_json(spec, json) + +#---------------------------------------- +# driver +#---------------------------------------- + +if __name__ == "__main__": + parser = ArgumentParser() + parser.add_argument("-s", "--spec") + parser.add_argument("--header") + parser.add_argument("-j", "--json") + + args = parser.parse_args() + + glesHeader = args.header + jsonFile = args.json + + gles_gen(args.spec, jsonFile, glesHeader) diff --git a/scripts/reg_modified.py b/scripts/reg_modified.py new file mode 100644 index 0000000..5f4e769 --- /dev/null +++ b/scripts/reg_modified.py @@ -0,0 +1,1175 @@ +#!/usr/bin/python3 -i +# +# Copyright 2013-2020 The Khronos Group Inc. +# SPDX-License-Identifier: Apache-2.0 + +import io,os,re,string,sys +from xml import etree + +def write(*args, **kwargs): + file = kwargs.pop('file', sys.stdout) + end = kwargs.pop('end', '\n') + file.write(' '.join([str(arg) for arg in args])) + file.write(end) + +# noneStr - returns string argument, or "" if argument is None. +# Used in converting lxml Elements into text. +# str - string to convert +def noneStr(str): + if (str): + return str + else: + return "" + +# matchAPIProfile - returns whether an API and profile +# being generated matches an element's profile +# api - string naming the API to match +# profile - string naming the profile to match +# elem - Element which (may) have 'api' and 'profile' +# attributes to match to. +# If a tag is not present in the Element, the corresponding API +# or profile always matches. +# Otherwise, the tag must exactly match the API or profile. +# Thus, if 'profile' = core: +# with no attribute will match +# will match +# will not match +# Possible match conditions: +# Requested Element +# Profile Profile +# --------- -------- +# None None Always matches +# 'string' None Always matches +# None 'string' Does not match. Can't generate multiple APIs +# or profiles, so if an API/profile constraint +# is present, it must be asked for explicitly. +# 'string' 'string' Strings must match +# +# ** In the future, we will allow regexes for the attributes, +# not just strings, so that api="^(gl|gles2)" will match. Even +# this isn't really quite enough, we might prefer something +# like "gl(core)|gles1(common-lite)". +def matchAPIProfile(api, profile, elem): + """Match a requested API & profile name to a api & profile attributes of an Element""" + match = True + # Match 'api', if present + if ('api' in elem.attrib): + if (api == None): + raise UserWarning("No API requested, but 'api' attribute is present with value '" + + elem.get('api') + "'") + elif (api != elem.get('api')): + # Requested API doesn't match attribute + return False + if ('profile' in elem.attrib): + if (profile == None): + raise UserWarning("No profile requested, but 'profile' attribute is present with value '" + + elem.get('profile') + "'") + elif (profile != elem.get('profile')): + # Requested profile doesn't match attribute + return False + return True + +# BaseInfo - base class for information about a registry feature +# (type/group/enum/command/API/extension). +# required - should this feature be defined during header generation +# (has it been removed by a profile or version)? +# declared - has this feature been defined already? +# elem - lxml.etree Element for this feature +# resetState() - reset required/declared to initial values. Used +# prior to generating a new API interface. +class BaseInfo: + """Represents the state of a registry feature, used during API generation""" + def __init__(self, elem): + self.required = False + self.declared = False + self.elem = elem + def resetState(self): + self.required = False + self.declared = False + +# TypeInfo - registry information about a type. No additional state +# beyond BaseInfo is required. +class TypeInfo(BaseInfo): + """Represents the state of a registry type""" + def __init__(self, elem): + BaseInfo.__init__(self, elem) + +# GroupInfo - registry information about a group of related enums. +# enums - dictionary of enum names which are in the group +class GroupInfo(BaseInfo): + """Represents the state of a registry enumerant group""" + def __init__(self, elem): + BaseInfo.__init__(self, elem) + self.enums = {} + +# EnumInfo - registry information about an enum +# type - numeric type of the value of the tag +# ( '' for GLint, 'u' for GLuint, 'ull' for GLuint64 ) +class EnumInfo(BaseInfo): + """Represents the state of a registry enum""" + def __init__(self, elem): + BaseInfo.__init__(self, elem) + self.type = elem.get('type') + if (self.type == None): + self.type = '' + +# CmdInfo - registry information about a command +# glxtype - type of GLX protocol { None, 'render', 'single', 'vendor' } +# glxopcode - GLX protocol opcode { None, number } +# glxequiv - equivalent command at GLX dispatch level { None, string } +# vecequiv - equivalent vector form of a command taking multiple scalar args +# { None, string } +class CmdInfo(BaseInfo): + """Represents the state of a registry command""" + def __init__(self, elem): + BaseInfo.__init__(self, elem) + self.glxtype = None + self.glxopcode = None + self.glxequiv = None + self.vecequiv = None + +# FeatureInfo - registry information about an API +# or +# name - feature name string (e.g. 'GL_ARB_multitexture') +# number - feature version number (e.g. 1.2). +# features are unversioned and assigned version number 0. +# category - category, e.g. VERSION or ARB/KHR/OES/ETC/vendor +# emit - has this feature been defined already? +class FeatureInfo(BaseInfo): + """Represents the state of an API feature (version/extension)""" + def __init__(self, elem): + BaseInfo.__init__(self, elem) + self.name = elem.get('name') + # Determine element category (vendor). Only works + # for elements. + if (elem.tag == 'feature'): + self.category = 'VERSION' + self.number = elem.get('number') + else: + self.category = self.name.split('_', 2)[1] + self.number = "0" + self.emit = False + +# Primary sort key for regSortFeatures. +# Sorts by category of the feature name string: +# Core API features (those defined with a tag) +# ARB/KHR/OES (Khronos extensions) +# other (EXT/vendor extensions) +def regSortCategoryKey(feature): + if (feature.elem.tag == 'feature'): + return 0 + elif (feature.category == 'ARB' or + feature.category == 'KHR' or + feature.category == 'OES'): + return 1 + else: + return 2 + +# Secondary sort key for regSortFeatures. +# Sorts by extension name. +def regSortNameKey(feature): + return feature.name + +# Tertiary sort key for regSortFeatures. +# Sorts by feature version number. +# elements all have version number "0" +def regSortNumberKey(feature): + return feature.number + +# regSortFeatures - default sort procedure for features. +# Sorts by primary key of feature category, +# then by feature name within the category, +# then by version number +def regSortFeatures(featureList): + featureList.sort(key = regSortNumberKey) + featureList.sort(key = regSortNameKey) + featureList.sort(key = regSortCategoryKey) + +# GeneratorOptions - base class for options used during header production +# These options are target language independent, and used by +# Registry.apiGen() and by base OutputGenerator objects. +# +# Members +# filename - name of file to generate, or None to write to stdout. +# apiname - string matching 'apiname' attribute, e.g. 'gl'. +# profile - string specifying API profile , e.g. 'core', or None. +# versions - regex matching API versions to process interfaces for. +# Normally '.*' or '[0-9]\.[0-9]' to match all defined versions. +# emitversions - regex matching API versions to actually emit +# interfaces for (though all requested versions are considered +# when deciding which interfaces to generate). For GL 4.3 glext.h, +# this might be '1\.[2-5]|[2-4]\.[0-9]'. +# defaultExtensions - If not None, a string which must in its +# entirety match the pattern in the "supported" attribute of +# the . Defaults to None. Usually the same as apiname. +# addExtensions - regex matching names of additional extensions +# to include. Defaults to None. +# removeExtensions - regex matching names of extensions to +# remove (after defaultExtensions and addExtensions). Defaults +# to None. +# sortProcedure - takes a list of FeatureInfo objects and sorts +# them in place to a preferred order in the generated output. +# Default is core API versions, ARB/KHR/OES extensions, all +# other extensions, alphabetically within each group. +# The regex patterns can be None or empty, in which case they match +# nothing. +class GeneratorOptions: + """Represents options during header production from an API registry""" + def __init__(self, + filename = None, + apiname = None, + profile = None, + versions = '.*', + emitversions = '.*', + defaultExtensions = None, + addExtensions = None, + removeExtensions = None, + sortProcedure = regSortFeatures): + self.filename = filename + self.apiname = apiname + self.profile = profile + self.versions = self.emptyRegex(versions) + self.emitversions = self.emptyRegex(emitversions) + self.defaultExtensions = defaultExtensions + self.addExtensions = self.emptyRegex(addExtensions) + self.removeExtensions = self.emptyRegex(removeExtensions) + self.sortProcedure = sortProcedure + # + # Substitute a regular expression which matches no version + # or extension names for None or the empty string. + def emptyRegex(self,pat): + if (pat == None or pat == ''): + return '_nomatch_^' + else: + return pat + +# CGeneratorOptions - subclass of GeneratorOptions. +# +# Adds options used by COutputGenerator objects during C language header +# generation. +# +# Additional members +# prefixText - list of strings to prefix generated header with +# (usually a copyright statement + calling convention macros). +# protectFile - True if multiple inclusion protection should be +# generated (based on the filename) around the entire header. +# protectFeature - True if #ifndef..#endif protection should be +# generated around a feature interface in the header file. +# genFuncPointers - True if function pointer typedefs should be +# generated +# protectProto - Controls cpp protection around prototypes: +# False - no protection +# 'nonzero' - protectProtoStr must be defined to a nonzero value +# True - protectProtoStr must be defined +# protectProtoStr - #ifdef symbol to use around prototype +# declarations, if protected +# apicall - string to use for the function declaration prefix, +# such as APICALL on Windows. +# apientry - string to use for the calling convention macro, +# in typedefs, such as APIENTRY. +# apientryp - string to use for the calling convention macro +# in function pointer typedefs, such as APIENTRYP. +class CGeneratorOptions(GeneratorOptions): + """Represents options during C header production from an API registry""" + def __init__(self, + filename = None, + apiname = None, + profile = None, + versions = '.*', + emitversions = '.*', + defaultExtensions = None, + addExtensions = None, + removeExtensions = None, + sortProcedure = regSortFeatures, + prefixText = "", + genFuncPointers = True, + protectFile = True, + protectFeature = True, + protectProto = True, + protectProtoStr = True, + apicall = '', + apientry = '', + apientryp = '', + procMacro = '', + removeProc = []): + GeneratorOptions.__init__(self, filename, apiname, profile, + versions, emitversions, defaultExtensions, + addExtensions, removeExtensions, sortProcedure) + self.prefixText = prefixText + self.genFuncPointers = genFuncPointers + self.protectFile = protectFile + self.protectFeature = protectFeature + self.protectProto = protectProto + self.protectProtoStr = protectProtoStr + self.apicall = apicall + self.apientry = apientry + self.apientryp = apientryp + self.procMacro = procMacro + self.removeProc = removeProc + +# OutputGenerator - base class for generating API interfaces. +# Manages basic logic, logging, and output file control +# Derived classes actually generate formatted output. +# +# ---- methods ---- +# OutputGenerator(errFile, warnFile, diagFile) +# errFile, warnFile, diagFile - file handles to write errors, +# warnings, diagnostics to. May be None to not write. +# logMsg(level, *args) - log messages of different categories +# level - 'error', 'warn', or 'diag'. 'error' will also +# raise a UserWarning exception +# *args - print()-style arguments +# beginFile(genOpts) - start a new interface file +# genOpts - GeneratorOptions controlling what's generated and how +# endFile() - finish an interface file, closing it when done +# beginFeature(interface, emit) - write interface for a feature +# and tag generated features as having been done. +# interface - element for the / to generate +# emit - actually write to the header only when True +# endFeature() - finish an interface. +# genType(typeinfo,name) - generate interface for a type +# typeinfo - TypeInfo for a type +# genEnum(enuminfo, name) - generate interface for an enum +# enuminfo - EnumInfo for an enum +# name - enum name +# genCmd(cmdinfo) - generate interface for a command +# cmdinfo - CmdInfo for a command +class OutputGenerator: + """Generate specified API interfaces in a specific style, such as a C header""" + def __init__(self, + errFile = sys.stderr, + warnFile = sys.stderr, + diagFile = sys.stdout): + self.outFile = None + self.errFile = errFile + self.warnFile = warnFile + self.diagFile = diagFile + # Internal state + self.featureName = None + self.genOpts = None + # + # logMsg - write a message of different categories to different + # destinations. + # level - + # 'diag' (diagnostic, voluminous) + # 'warn' (warning) + # 'error' (fatal error - raises exception after logging) + # *args - print()-style arguments to direct to corresponding log + def logMsg(self, level, *args): + """Log a message at the given level. Can be ignored or log to a file""" + if (level == 'error'): + strfile = io.StringIO() + write('ERROR:', *args, file=strfile) + if (self.errFile != None): + write(strfile.getvalue(), file=self.errFile) + raise UserWarning(strfile.getvalue()) + elif (level == 'warn'): + if (self.warnFile != None): + write('WARNING:', *args, file=self.warnFile) + elif (level == 'diag'): + if (self.diagFile != None): + write('DIAG:', *args, file=self.diagFile) + else: + raise UserWarning( + '*** FATAL ERROR in Generator.logMsg: unknown level:' + level) + # + def beginFile(self, genOpts): + self.genOpts = genOpts + # + # Open specified output file. Not done in constructor since a + # Generator can be used without writing to a file. + if (self.genOpts.filename != None): + self.outFile = open(self.genOpts.filename, 'w') + else: + self.outFile = sys.stdout + def endFile(self): + self.errFile and self.errFile.flush() + self.warnFile and self.warnFile.flush() + self.diagFile and self.diagFile.flush() + self.outFile.flush() + if (self.outFile != sys.stdout and self.outFile != sys.stderr): + self.outFile.close() + self.genOpts = None + # + def beginFeature(self, interface, emit): + self.emit = emit + self.featureName = interface.get('name') + # If there's an additional 'protect' attribute in the feature, save it + self.featureExtraProtect = interface.get('protect') + def endFeature(self): + # Derived classes responsible for emitting feature + self.featureName = None + self.featureExtraProtect = None + # + # Type generation + def genType(self, typeinfo, name): + if (self.featureName == None): + raise UserWarning('Attempt to generate type', name, + 'when not in feature') + # + # Enumerant generation + def genEnum(self, enuminfo, name): + if (self.featureName == None): + raise UserWarning('Attempt to generate enum', name, + 'when not in feature') + # + # Command generation + def genCmd(self, cmd, name): + if (self.featureName == None): + raise UserWarning('Attempt to generate command', name, + 'when not in feature') + +# COutputGenerator - subclass of OutputGenerator. +# Generates C-language API interfaces. +# +# ---- methods ---- +# COutputGenerator(errFile, warnFile, diagFile) - args as for +# OutputGenerator. Defines additional internal state. +# makeCDecls(cmd) - return C prototype and function pointer typedef for a +# Element, as a list of two strings +# cmd - Element for the +# newline() - print a newline to the output file (utility function) +# ---- methods overriding base class ---- +# beginFile(genOpts) +# endFile() +# beginFeature(interface, emit) +# endFeature() +# genType(typeinfo,name) - generate interface for a type +# genEnum(enuminfo, name) +# genCmd(cmdinfo) +class COutputGenerator(OutputGenerator): + """Generate specified API interfaces in a specific style, such as a C header""" + def __init__(self, + errFile = sys.stderr, + warnFile = sys.stderr, + diagFile = sys.stdout): + OutputGenerator.__init__(self, errFile, warnFile, diagFile) + # Internal state - accumulators for different inner block text + self.typeBody = '' + self.enumBody = '' + self.cmdBody = '' + # + # makeCDecls - return C prototype and function pointer typedef for a + # command, as a two-element list of strings. + # cmd - Element containing a tag + def makeCDecls(self, cmd): + """Generate C function pointer typedef for Element""" + proto = cmd.find('proto') + params = cmd.findall('param') + # Begin accumulating prototype and typedef strings + pdecl = self.genOpts.apicall + + tdecl = 'typedef ' + # + # Insert the function return type/name. + # For prototypes, add APIENTRY macro before the name + # For typedefs, add (APIENTRYP ) around the name and + # use the PFNGLCMDNAMEPROC nameng convention. + # Done by walking the tree for element by element. + # lxml.etree has elem.text followed by (elem[i], elem[i].tail) + # for each child element and any following text + # Leading text + pdecl += noneStr(proto.text) + tdecl += noneStr(proto.text) + # For each child element, if it's a wrap in appropriate + # declaration. Otherwise append its contents and tail contents. + for elem in proto: + text = noneStr(elem.text) + tail = noneStr(elem.tail) + if (elem.tag == 'name'): + pdecl += self.genOpts.apientry + + if self.genOpts.procMacro != '': + pdecl += self.genOpts.procMacro + '(' + + pdecl += text + tail + + if self.genOpts.procMacro != '': + pdecl += ')' + + tdecl += '(' + self.genOpts.apientryp + 'PFN' + text.upper() + 'PROC' + tail + ')' + else: + pdecl += text + tail + tdecl += text + tail + # Now add the parameter declaration list, which is identical + # for prototypes and typedefs. Concatenate all the text from + # a node without the tags. No tree walking required + # since all tags are ignored. + n = len(params) + paramdecl = ' (' + if n > 0: + for i in range(0,n): + paramdecl += ''.join([t for t in params[i].itertext()]) + if (i < n - 1): + paramdecl += ', ' + else: + paramdecl += 'void' + paramdecl += ');\n'; + return [ pdecl + paramdecl, tdecl + paramdecl ] + # + def newline(self): + write('', file=self.outFile) + # + def beginFile(self, genOpts): + OutputGenerator.beginFile(self, genOpts) + # C-specific + # + # Multiple inclusion protection & C++ wrappers. + if (genOpts.protectFile and self.genOpts.filename): + headerSym = '__' + self.genOpts.apiname + '_' + re.sub('\.h', '_h_', os.path.basename(self.genOpts.filename)) + write('#ifndef', headerSym, file=self.outFile) + write('#define', headerSym, '1', file=self.outFile) + self.newline() + write('#ifdef __cplusplus', file=self.outFile) + write('extern "C" {', file=self.outFile) + write('#endif', file=self.outFile) + self.newline() + # + # User-supplied prefix text, if any (list of strings) + if (genOpts.prefixText): + for s in genOpts.prefixText: + write(s, file=self.outFile) + # + # Some boilerplate describing what was generated - this + # will probably be removed later since the extensions + # pattern may be very long. + write('/* Generated C header for:', file=self.outFile) + write(' * API:', genOpts.apiname, file=self.outFile) + if (genOpts.profile): + write(' * Profile:', genOpts.profile, file=self.outFile) + write(' * Versions considered:', genOpts.versions, file=self.outFile) + write(' * Versions emitted:', genOpts.emitversions, file=self.outFile) + write(' * Default extensions included:', genOpts.defaultExtensions, file=self.outFile) + write(' * Additional extensions included:', genOpts.addExtensions, file=self.outFile) + write(' * Extensions removed:', genOpts.removeExtensions, file=self.outFile) + write(' */', file=self.outFile) + def endFile(self): + # C-specific + # Finish C++ wrapper and multiple inclusion protection + self.newline() + write('#ifdef __cplusplus', file=self.outFile) + write('}', file=self.outFile) + write('#endif', file=self.outFile) + if (self.genOpts.protectFile and self.genOpts.filename): + self.newline() + write('#endif', file=self.outFile) + # Finish processing in superclass + OutputGenerator.endFile(self) + def beginFeature(self, interface, emit): + # Start processing in superclass + OutputGenerator.beginFeature(self, interface, emit) + # C-specific + # Accumulate types, enums, function pointer typedefs, end function + # prototypes separately for this feature. They're only printed in + # endFeature(). + self.typeBody = '' + self.enumBody = '' + self.cmdPointerBody = '' + self.cmdBody = '' + def endFeature(self): + # C-specific + # Actually write the interface to the output file. + if (self.emit): + self.newline() + if (self.genOpts.protectFeature): + write('#ifndef', self.featureName, file=self.outFile) + write('#define', self.featureName, '1', file=self.outFile) + if (self.typeBody != ''): + write(self.typeBody, end='', file=self.outFile) + # + # Don't add additional protection for derived type declarations, + # which may be needed by other features later on. + if (self.featureExtraProtect != None): + write('#ifdef', self.featureExtraProtect, file=self.outFile) + if (self.enumBody != ''): + write(self.enumBody, end='', file=self.outFile) + if (self.genOpts.genFuncPointers and self.cmdPointerBody != ''): + write(self.cmdPointerBody, end='', file=self.outFile) + if (self.cmdBody != ''): + if (self.genOpts.protectProto == True): + prefix = '#ifdef ' + self.genOpts.protectProtoStr + '\n' + suffix = '#endif\n' + elif (self.genOpts.protectProto == 'nonzero'): + prefix = '#if ' + self.genOpts.protectProtoStr + '\n' + suffix = '#endif\n' + elif (self.genOpts.protectProto == False): + prefix = '' + suffix = '' + else: + self.gen.logMsg('warn', + '*** Unrecognized value for protectProto:', + self.genOpts.protectProto, + 'not generating prototype wrappers') + prefix = '' + suffix = '' + + write(prefix + self.cmdBody + suffix, end='', file=self.outFile) + if (self.featureExtraProtect != None): + write('#endif /*', self.featureExtraProtect, '*/', file=self.outFile) + if (self.genOpts.protectFeature): + write('#endif /*', self.featureName, '*/', file=self.outFile) + # Finish processing in superclass + OutputGenerator.endFeature(self) + # + # Type generation + def genType(self, typeinfo, name): + OutputGenerator.genType(self, typeinfo, name) + # + # Replace tags with an APIENTRY-style string + # (from self.genOpts). Copy other text through unchanged. + # If the resulting text is an empty string, don't emit it. + typeElem = typeinfo.elem + s = noneStr(typeElem.text) + for elem in typeElem: + if (elem.tag == 'apientry'): + s += self.genOpts.apientry + noneStr(elem.tail) + else: + s += noneStr(elem.text) + noneStr(elem.tail) + if (len(s) > 0): + self.typeBody += s + '\n' + # + # Enumerant generation + def genEnum(self, enuminfo, name): + OutputGenerator.genEnum(self, enuminfo, name) + # + # EnumInfo.type is a C value suffix (e.g. u, ull) + self.enumBody += '#define ' + name.ljust(33) + ' ' + enuminfo.elem.get('value') + # + # Handle non-integer 'type' fields by using it as the C value suffix + t = enuminfo.elem.get('type') + if (t != '' and t != 'i'): + self.enumBody += enuminfo.type + self.enumBody += '\n' + # + # Command generation + def genCmd(self, cmdinfo, name): + if name in self.genOpts.removeProc: + return + OutputGenerator.genCmd(self, cmdinfo, name) + # + decls = self.makeCDecls(cmdinfo.elem) + self.cmdBody += decls[0] + if (self.genOpts.genFuncPointers): + self.cmdPointerBody += decls[1] + +# Registry - object representing an API registry, loaded from an XML file +# Members +# tree - ElementTree containing the root +# typedict - dictionary of TypeInfo objects keyed by type name +# groupdict - dictionary of GroupInfo objects keyed by group name +# enumdict - dictionary of EnumInfo objects keyed by enum name +# cmddict - dictionary of CmdInfo objects keyed by command name +# apidict - dictionary of Elements keyed by API name +# extensions - list of Elements +# extdict - dictionary of Elements keyed by extension name +# gen - OutputGenerator object used to write headers / messages +# genOpts - GeneratorOptions object used to control which +# fetures to write and how to format them +# emitFeatures - True to actually emit features for a version / extension, +# or False to just treat them as emitted +# Public methods +# loadElementTree(etree) - load registry from specified ElementTree +# loadFile(filename) - load registry from XML file +# setGenerator(gen) - OutputGenerator to use +# parseTree() - parse the registry once loaded & create dictionaries +# dumpReg(maxlen, filehandle) - diagnostic to dump the dictionaries +# to specified file handle (default stdout). Truncates type / +# enum / command elements to maxlen characters (default 80) +# generator(g) - specify the output generator object +# apiGen(apiname, genOpts) - generate API headers for the API type +# and profile specified in genOpts, but only for the versions and +# extensions specified there. +# apiReset() - call between calls to apiGen() to reset internal state +# validateGroups() - call to verify that each or +# with a 'group' attribute matches an actual existing group. +# Private methods +# addElementInfo(elem,info,infoName,dictionary) - add feature info to dict +# lookupElementInfo(fname,dictionary) - lookup feature info in dict +class Registry: + """Represents an API registry loaded from XML""" + def __init__(self): + self.tree = None + self.typedict = {} + self.groupdict = {} + self.enumdict = {} + self.cmddict = {} + self.apidict = {} + self.extensions = [] + self.extdict = {} + # A default output generator, so commands prior to apiGen can report + # errors via the generator object. + self.gen = OutputGenerator() + self.genOpts = None + self.emitFeatures = False + def loadElementTree(self, tree): + """Load ElementTree into a Registry object and parse it""" + self.tree = tree + self.parseTree() + def loadFile(self, file): + """Load an API registry XML file into a Registry object and parse it""" + self.tree = etree.parse(file) + self.parseTree() + def setGenerator(self, gen): + """Specify output generator object. None restores the default generator""" + self.gen = gen + # addElementInfo - add information about an element to the + # corresponding dictionary + # elem - ///// Element + # info - corresponding {Type|Group|Enum|Cmd|Feature}Info object + # infoName - 'type' / 'group' / 'enum' / 'command' / 'feature' / 'extension' + # dictionary - self.{type|group|enum|cmd|api|ext}dict + # If the Element has an 'api' attribute, the dictionary key is the + # tuple (name,api). If not, the key is the name. 'name' is an + # attribute of the Element + def addElementInfo(self, elem, info, infoName, dictionary): + if ('api' in elem.attrib): + key = (elem.get('name'),elem.get('api')) + else: + key = elem.get('name') + if key in dictionary: + self.gen.logMsg('warn', '*** Attempt to redefine', + infoName, 'with key:', key) + else: + dictionary[key] = info + # + # lookupElementInfo - find a {Type|Enum|Cmd}Info object by name. + # If an object qualified by API name exists, use that. + # fname - name of type / enum / command + # dictionary - self.{type|enum|cmd}dict + def lookupElementInfo(self, fname, dictionary): + key = (fname, self.genOpts.apiname) + if (key in dictionary): + # self.gen.logMsg('diag', 'Found API-specific element for feature', fname) + return dictionary[key] + elif (fname in dictionary): + # self.gen.logMsg('diag', 'Found generic element for feature', fname) + return dictionary[fname] + else: + return None + def parseTree(self): + """Parse the registry Element, once created""" + # This must be the Element for the root + self.reg = self.tree.getroot() + # + # Create dictionary of registry types from toplevel tags + # and add 'name' attribute to each tag (where missing) + # based on its element. + # + # There's usually one block; more are OK + # Required attributes: 'name' or nested tag contents + self.typedict = {} + for type in self.reg.findall('types/type'): + # If the doesn't already have a 'name' attribute, set + # it from contents of its tag. + if (type.get('name') == None): + type.attrib['name'] = type.find('name').text + self.addElementInfo(type, TypeInfo(type), 'type', self.typedict) + # + # Create dictionary of registry groups from toplevel tags. + # + # There's usually one block; more are OK. + # Required attributes: 'name' + self.groupdict = {} + for group in self.reg.findall('groups/group'): + self.addElementInfo(group, GroupInfo(group), 'group', self.groupdict) + # + # Create dictionary of registry enums from toplevel tags + # + # There are usually many tags in different namespaces, but + # these are functional namespaces of the values, while the actual + # enum names all share the dictionary. + # Required attributes: 'name', 'value' + self.enumdict = {} + for enum in self.reg.findall('enums/enum'): + self.addElementInfo(enum, EnumInfo(enum), 'enum', self.enumdict) + # + # Create dictionary of registry commands from tags + # and add 'name' attribute to each tag (where missing) + # based on its element. + # + # There's usually only one block; more are OK. + # Required attributes: 'name' or tag contents + self.cmddict = {} + for cmd in self.reg.findall('commands/command'): + # If the doesn't already have a 'name' attribute, set + # it from contents of its tag. + if (cmd.get('name') == None): + cmd.attrib['name'] = cmd.find('proto/name').text + ci = CmdInfo(cmd) + self.addElementInfo(cmd, ci, 'command', self.cmddict) + # + # Create dictionaries of API and extension interfaces + # from toplevel and tags. + # + self.apidict = {} + for feature in self.reg.findall('feature'): + ai = FeatureInfo(feature) + self.addElementInfo(feature, ai, 'feature', self.apidict) + self.extensions = self.reg.findall('extensions/extension') + self.extdict = {} + for feature in self.extensions: + ei = FeatureInfo(feature) + self.addElementInfo(feature, ei, 'extension', self.extdict) + def dumpReg(self, maxlen = 40, filehandle = sys.stdout): + """Dump all the dictionaries constructed from the Registry object""" + write('***************************************', file=filehandle) + write(' ** Dumping Registry contents **', file=filehandle) + write('***************************************', file=filehandle) + write('// Types', file=filehandle) + for name in self.typedict: + tobj = self.typedict[name] + write(' Type', name, '->', etree.tostring(tobj.elem)[0:maxlen], file=filehandle) + write('// Groups', file=filehandle) + for name in self.groupdict: + gobj = self.groupdict[name] + write(' Group', name, '->', etree.tostring(gobj.elem)[0:maxlen], file=filehandle) + write('// Enums', file=filehandle) + for name in self.enumdict: + eobj = self.enumdict[name] + write(' Enum', name, '->', etree.tostring(eobj.elem)[0:maxlen], file=filehandle) + write('// Commands', file=filehandle) + for name in self.cmddict: + cobj = self.cmddict[name] + write(' Command', name, '->', etree.tostring(cobj.elem)[0:maxlen], file=filehandle) + write('// APIs', file=filehandle) + for key in self.apidict: + write(' API Version ', key, '->', + etree.tostring(self.apidict[key].elem)[0:maxlen], file=filehandle) + write('// Extensions', file=filehandle) + for key in self.extdict: + write(' Extension', key, '->', + etree.tostring(self.extdict[key].elem)[0:maxlen], file=filehandle) + # write('***************************************', file=filehandle) + # write(' ** Dumping XML ElementTree **', file=filehandle) + # write('***************************************', file=filehandle) + # write(etree.tostring(self.tree.getroot(),pretty_print=True), file=filehandle) + # + # typename - name of type + # required - boolean (to tag features as required or not) + def markTypeRequired(self, typename, required): + """Require (along with its dependencies) or remove (but not its dependencies) a type""" + self.gen.logMsg('diag', '*** tagging type:', typename, '-> required =', required) + # Get TypeInfo object for tag corresponding to typename + type = self.lookupElementInfo(typename, self.typedict) + if (type != None): + # Tag required type dependencies as required. + # This DOES NOT un-tag dependencies in a tag. + # See comments in markRequired() below for the reason. + if (required and ('requires' in type.elem.attrib)): + depType = type.elem.get('requires') + self.gen.logMsg('diag', '*** Generating dependent type', + depType, 'for type', typename) + self.markTypeRequired(depType, required) + type.required = required + else: + self.gen.logMsg('warn', '*** type:', typename , 'IS NOT DEFINED') + # + # features - Element for or tag + # required - boolean (to tag features as required or not) + def markRequired(self, features, required): + """Require or remove features specified in the Element""" + self.gen.logMsg('diag', '*** markRequired (features = , required =', required, ')') + # Loop over types, enums, and commands in the tag + # @@ It would be possible to respect 'api' and 'profile' attributes + # in individual features, but that's not done yet. + for typeElem in features.findall('type'): + self.markTypeRequired(typeElem.get('name'), required) + for enumElem in features.findall('enum'): + name = enumElem.get('name') + self.gen.logMsg('diag', '*** tagging enum:', name, '-> required =', required) + enum = self.lookupElementInfo(name, self.enumdict) + if (enum != None): + enum.required = required + else: + self.gen.logMsg('warn', '*** enum:', name , 'IS NOT DEFINED') + for cmdElem in features.findall('command'): + name = cmdElem.get('name') + self.gen.logMsg('diag', '*** tagging command:', name, '-> required =', required) + cmd = self.lookupElementInfo(name, self.cmddict) + if (cmd != None): + cmd.required = required + # Tag all parameter types of this command as required. + # This DOES NOT remove types of commands in a + # tag, because many other commands may use the same type. + # We could be more clever and reference count types, + # instead of using a boolean. + if (required): + # Look for in entire tree, + # not just immediate children + for ptype in cmd.elem.findall('.//ptype'): + self.gen.logMsg('diag', '*** markRequired: command implicitly requires dependent type', ptype.text) + self.markTypeRequired(ptype.text, required) + else: + self.gen.logMsg('warn', '*** command:', name, 'IS NOT DEFINED') + # + # interface - Element for or , containing + # and tags + # api - string specifying API name being generated + # profile - string specifying API profile being generated + def requireAndRemoveFeatures(self, interface, api, profile): + """Process and tags for a or """ + # marks things that are required by this version/profile + for feature in interface.findall('require'): + if (matchAPIProfile(api, profile, feature)): + self.markRequired(feature,True) + # marks things that are removed by this version/profile + for feature in interface.findall('remove'): + if (matchAPIProfile(api, profile, feature)): + self.markRequired(feature,False) + # + # generateFeature - generate a single type / enum / command, + # and all its dependencies as needed. + # fname - name of feature (// + # ftype - type of feature, 'type' | 'enum' | 'command' + # dictionary - of *Info objects - self.{type|enum|cmd}dict + # genProc - bound function pointer for self.gen.gen{Type|Enum|Cmd} + def generateFeature(self, fname, ftype, dictionary, genProc): + f = self.lookupElementInfo(fname, dictionary) + if (f == None): + # No such feature. This is an error, but reported earlier + self.gen.logMsg('diag', '*** No entry found for feature', fname, + 'returning!') + return + # + # If feature isn't required, or has already been declared, return + if (not f.required): + self.gen.logMsg('diag', '*** Skipping', ftype, fname, '(not required)') + return + if (f.declared): + self.gen.logMsg('diag', '*** Skipping', ftype, fname, '(already declared)') + return + # + # Pull in dependent type declaration(s) of the feature. + # For types, there may be one in the 'required' attribute of the element + # For commands, there may be many in tags within the element + # For enums, no dependencies are allowed (though perhasps if you + # have a uint64 enum, it should require GLuint64) + if (ftype == 'type'): + if ('requires' in f.elem.attrib): + depname = f.elem.get('requires') + self.gen.logMsg('diag', '*** Generating required dependent type', + depname) + self.generateFeature(depname, 'type', self.typedict, + self.gen.genType) + elif (ftype == 'command'): + for ptype in f.elem.findall('.//ptype'): + depname = ptype.text + self.gen.logMsg('diag', '*** Generating required parameter type', + depname) + self.generateFeature(depname, 'type', self.typedict, + self.gen.genType) + # + # Actually generate the type only if emitting declarations + if self.emitFeatures: + self.gen.logMsg('diag', '*** Emitting', ftype, 'decl for', fname) + genProc(f, fname) + else: + self.gen.logMsg('diag', '*** Skipping', ftype, fname, + '(not emitting this feature)') + # Always mark feature declared, as though actually emitted + f.declared = True + # + # generateRequiredInterface - generate all interfaces required + # by an API version or extension + # interface - Element for or + def generateRequiredInterface(self, interface): + """Generate required C interface for specified API version/extension""" + # + # Loop over all features inside all tags. + # tags are ignored (handled in pass 1). + for features in interface.findall('require'): + for t in features.findall('type'): + self.generateFeature(t.get('name'), 'type', self.typedict, + self.gen.genType) + for e in features.findall('enum'): + self.generateFeature(e.get('name'), 'enum', self.enumdict, + self.gen.genEnum) + for c in features.findall('command'): + self.generateFeature(c.get('name'), 'command', self.cmddict, + self.gen.genCmd) + # + # apiGen(genOpts) - generate interface for specified versions + # genOpts - GeneratorOptions object with parameters used + # by the Generator object. + def apiGen(self, genOpts): + """Generate interfaces for the specified API type and range of versions""" + # + self.gen.logMsg('diag', '*******************************************') + self.gen.logMsg('diag', ' Registry.apiGen file:', genOpts.filename, + 'api:', genOpts.apiname, + 'profile:', genOpts.profile) + self.gen.logMsg('diag', '*******************************************') + # + self.genOpts = genOpts + # Reset required/declared flags for all features + self.apiReset() + # + # Compile regexps used to select versions & extensions + regVersions = re.compile(self.genOpts.versions) + regEmitVersions = re.compile(self.genOpts.emitversions) + regAddExtensions = re.compile(self.genOpts.addExtensions) + regRemoveExtensions = re.compile(self.genOpts.removeExtensions) + # + # Get all matching API versions & add to list of FeatureInfo + features = [] + apiMatch = False + for key in self.apidict: + fi = self.apidict[key] + api = fi.elem.get('api') + if (api == self.genOpts.apiname): + apiMatch = True + if (regVersions.match(fi.number)): + # Matches API & version #s being generated. Mark for + # emission and add to the features[] list . + # @@ Could use 'declared' instead of 'emit'? + fi.emit = (regEmitVersions.match(fi.number) != None) + features.append(fi) + if (not fi.emit): + self.gen.logMsg('diag', '*** NOT tagging feature api =', api, + 'name =', fi.name, 'number =', fi.number, + 'for emission (does not match emitversions pattern)') + else: + self.gen.logMsg('diag', '*** NOT including feature api =', api, + 'name =', fi.name, 'number =', fi.number, + '(does not match requested versions)') + else: + self.gen.logMsg('diag', '*** NOT including feature api =', api, + 'name =', fi.name, + '(does not match requested API)') + if (not apiMatch): + self.gen.logMsg('warn', '*** No matching API versions found!') + # + # Get all matching extensions & add to the list. + # Start with extensions tagged with 'api' pattern matching the API + # being generated. Add extensions matching the pattern specified in + # regExtensions, then remove extensions matching the pattern + # specified in regRemoveExtensions + for key in self.extdict: + ei = self.extdict[key] + extName = ei.name + include = False + # + # Include extension if defaultExtensions is not None and if the + # 'supported' attribute matches defaultExtensions. The regexp in + # 'supported' must exactly match defaultExtensions, so bracket + # it with ^(pat)$. + pat = '^(' + ei.elem.get('supported') + ')$' + if (self.genOpts.defaultExtensions and + re.match(pat, self.genOpts.defaultExtensions)): + self.gen.logMsg('diag', '*** Including extension', + extName, "(defaultExtensions matches the 'supported' attribute)") + include = True + # + # Include additional extensions if the extension name matches + # the regexp specified in the generator options. This allows + # forcing extensions into an interface even if they're not + # tagged appropriately in the registry. + if (regAddExtensions.match(extName) != None): + self.gen.logMsg('diag', '*** Including extension', + extName, '(matches explicitly requested extensions to add)') + include = True + # Remove extensions if the name matches the regexp specified + # in generator options. This allows forcing removal of + # extensions from an interface even if they're tagged that + # way in the registry. + if (regRemoveExtensions.match(extName) != None): + self.gen.logMsg('diag', '*** Removing extension', + extName, '(matches explicitly requested extensions to remove)') + include = False + # + # If the extension is to be included, add it to the + # extension features list. + if (include): + ei.emit = True + features.append(ei) + else: + self.gen.logMsg('diag', '*** NOT including extension', + extName, '(does not match api attribute or explicitly requested extensions)') + # + # Sort the extension features list, if a sort procedure is defined + if (self.genOpts.sortProcedure): + self.genOpts.sortProcedure(features) + # + # Pass 1: loop over requested API versions and extensions tagging + # types/commands/features as required (in an block) or no + # longer required (in an block). It is possible to remove + # a feature in one version and restore it later by requiring it in + # a later version. + # If a profile other than 'None' is being generated, it must + # match the profile attribute (if any) of the and + # tags. + self.gen.logMsg('diag', '*** PASS 1: TAG FEATURES ********************************************') + for f in features: + self.gen.logMsg('diag', '*** PASS 1: Tagging required and removed features for', + f.name) + self.requireAndRemoveFeatures(f.elem, self.genOpts.apiname, self.genOpts.profile) + # + # Pass 2: loop over specified API versions and extensions printing + # declarations for required things which haven't already been + # generated. + self.gen.logMsg('diag', '*** PASS 2: GENERATE INTERFACES FOR FEATURES ************************') + self.gen.beginFile(self.genOpts) + for f in features: + self.gen.logMsg('diag', '*** PASS 2: Generating interface for', + f.name) + emit = self.emitFeatures = f.emit + if (not emit): + self.gen.logMsg('diag', '*** PASS 2: NOT declaring feature', + f.elem.get('name'), 'because it is not tagged for emission') + # Generate the interface (or just tag its elements as having been + # emitted, if they haven't been). + self.gen.beginFeature(f.elem, emit) + self.generateRequiredInterface(f.elem) + self.gen.endFeature() + self.gen.endFile() + # + # apiReset - use between apiGen() calls to reset internal state + # + def apiReset(self): + """Reset type/enum/command dictionaries before generating another API""" + for type in self.typedict: + self.typedict[type].resetState() + for enum in self.enumdict: + self.enumdict[enum].resetState() + for cmd in self.cmddict: + self.cmddict[cmd].resetState() + for cmd in self.apidict: + self.apidict[cmd].resetState() + # + # validateGroups - check that group= attributes match actual groups + # + def validateGroups(self): + """Validate group= attributes on and tags""" + # Keep track of group names not in tags + badGroup = {} + self.gen.logMsg('diag', '*** VALIDATING GROUP ATTRIBUTES ***') + for cmd in self.reg.findall('commands/command'): + proto = cmd.find('proto') + funcname = cmd.find('proto/name').text + if ('group' in proto.attrib.keys()): + group = proto.get('group') + # self.gen.logMsg('diag', '*** Command ', funcname, ' has return group ', group) + if (group not in self.groupdict.keys()): + # self.gen.logMsg('diag', '*** Command ', funcname, ' has UNKNOWN return group ', group) + if (group not in badGroup.keys()): + badGroup[group] = 1 + else: + badGroup[group] = badGroup[group] + 1 + for param in cmd.findall('param'): + pname = param.find('name') + if (pname != None): + pname = pname.text + else: + pname = type.get('name') + if ('group' in param.attrib.keys()): + group = param.get('group') + if (group not in self.groupdict.keys()): + # self.gen.logMsg('diag', '*** Command ', funcname, ' param ', pname, ' has UNKNOWN group ', group) + if (group not in badGroup.keys()): + badGroup[group] = 1 + else: + badGroup[group] = badGroup[group] + 1 + if (len(badGroup.keys()) > 0): + self.gen.logMsg('diag', '*** SUMMARY OF UNRECOGNIZED GROUPS ***') + for key in sorted(badGroup.keys()): + self.gen.logMsg('diag', ' ', key, ' occurred ', badGroup[key], ' times') diff --git a/sdk/orca.h b/sdk/orca.h index a093c2d..2c96e5b 100644 --- a/sdk/orca.h +++ b/sdk/orca.h @@ -19,10 +19,22 @@ #include"platform/platform_clock.h" #include"platform/platform_io.h" +#include"math.h" + +#include"graphics.h" +#include"gl31.h" + #if COMPILER_CLANG #define ORCA_EXPORT __attribute__((visibility("default"))) #else #error "Orca apps can only be compiled with clang for now" #endif + +mg_surface mg_surface_canvas(); +mg_surface mg_surface_gles(); + + + + #endif //__ORCA_H_ diff --git a/src/canvas_api.json b/src/canvas_api.json index 4bb301c..5c329df 100644 --- a/src/canvas_api.json +++ b/src/canvas_api.json @@ -36,12 +36,6 @@ {"name": "pixels", "type": {"name": "u8*", "tag": "p"}}] }, -{ - "name": "mg_surface_main", - "cname": "orca_surface_main", - "ret": {"name": "mg_surface", "tag": "S"}, - "args": [] -}, { "name": "mg_surface_prepare", "cname": "mg_surface_prepare", @@ -75,4 +69,17 @@ "type": {"name": "u32", "tag": "i"}}, {"name": "elements", "type": {"name": "mg_path_elt*", "tag": "p"}}] -}] +}, +{ + "name": "mg_surface_canvas", + "cname": "orca_surface_canvas", + "ret": {"name": "mg_surface", "tag": "S"}, + "args": [] +}, +{ + "name": "mg_surface_gles", + "cname": "orca_surface_gles", + "ret": {"name": "mg_surface", "tag": "S"}, + "args": [] +} +] diff --git a/src/core_api.json b/src/core_api.json index 061dfac..fe67f27 100644 --- a/src/core_api.json +++ b/src/core_api.json @@ -1,60 +1,4 @@ [ -{ - "name": "cosf", - "cname": "cosf", - "ret": {"name": "float", "tag": "f"}, - "args": [ {"name": "x", - "type": {"name": "float", "tag": "f"}}] -}, -{ - "name": "sinf", - "cname": "sinf", - "ret": {"name": "float", "tag": "f"}, - "args": [ {"name": "x", - "type": {"name": "float", "tag": "f"}}] -}, -{ - "name": "floorf", - "cname": "floorf", - "ret": {"name": "float", "tag": "f"}, - "args": [ {"name": "x", - "type": {"name": "float", "tag": "f"}}] -}, -{ - "name": "sqrtf", - "cname": "sqrtf", - "ret": {"name": "float", "tag": "f"}, - "args": [ {"name": "x", - "type": {"name": "float", "tag": "f"}}] -}, -{ - "name": "cos", - "cname": "cos", - "ret": {"name": "double", "tag": "F"}, - "args": [ {"name": "x", - "type": {"name": "double", "tag": "F"}}] -}, -{ - "name": "sin", - "cname": "sin", - "ret": {"name": "double", "tag": "F"}, - "args": [ {"name": "x", - "type": {"name": "double", "tag": "F"}}] -}, -{ - "name": "sqrt", - "cname": "sqrt", - "ret": {"name": "double", "tag": "F"}, - "args": [ {"name": "x", - "type": {"name": "double", "tag": "F"}}] -}, -{ - "name": "fabs", - "cname": "fabs", - "ret": {"name": "double", "tag": "F"}, - "args": [ {"name": "x", - "type": {"name": "double", "tag": "F"}}] -}, { "name": "orca_log", "cname": "orca_log", diff --git a/src/gles_api.json b/src/gles_api.json deleted file mode 100644 index fcd6982..0000000 --- a/src/gles_api.json +++ /dev/null @@ -1,383 +0,0 @@ -[ -{ - "name": "glCreateProgram", - "cname": "glCreateProgram", - "ret": {"name": "int", "tag": "i"}, - "args": [] -}, -{ - "name": "glCreateShader", - "cname": "glCreateShader", - "ret": {"name": "int", "tag": "i"}, - "args": [ {"name": "shaderType", - "type": {"name": "GLenum", "tag": "i"}}] -}, -{ - "name": "glCompileShader", - "cname": "glCompileShader", - "ret": {"name": "void", "tag": "v"}, - "args": [ {"name": "shader", - "type": {"name": "GLuint", "tag": "i"}}] -}, -{ - "name": "glAttachShader", - "cname": "glAttachShader", - "ret": {"name": "void", "tag": "v"}, - "args": [ {"name": "program", - "type": {"name": "GLuint", "tag": "i"}}, - {"name": "shader", - "type": {"name": "GLuint", "tag": "i"}} - ] -}, -{ - "name": "glLinkProgram", - "cname": "glLinkProgram", - "ret": {"name": "void", "tag": "v"}, - "args": [ {"name": "program", - "type": {"name": "GLuint", "tag": "i"}}] -}, -{ - "name": "glUseProgram", - "cname": "glUseProgram", - "ret": {"name": "void", "tag": "v"}, - "args": [ {"name": "program", - "type": {"name": "GLuint", "tag": "i"}}] -}, -{ - "name": "glGetError", - "cname": "glGetError", - "ret": {"name": "GLenum", "tag": "i"}, - "args": [] -}, -{ - "name": "glClearColor", - "cname": "glClearColor", - "ret": {"name": "void", "tag": "v"}, - "args": [ {"name": "red", - "type": {"name": "GLfloat", "tag": "f"}}, - {"name": "green", - "type": {"name": "GLfloat", "tag": "f"}}, - {"name": "blue", - "type": {"name": "GLfloat", "tag": "f"}}, - {"name": "alpha", - "type": {"name": "GLfloat", "tag": "f"}} - ] -}, -{ - "name": "glClear", - "cname": "glClear", - "ret": {"name": "void", "tag": "v"}, - "args": [ {"name": "mask", - "type": {"name": "GLbitfield", "tag": "i"}}] -}, -{ - "name": "glViewport", - "cname": "glViewport", - "ret": {"name": "void", "tag": "v"}, - "args": [ {"name": "x", - "type": {"name": "GLint", "tag": "i"}}, - {"name": "y", - "type": {"name": "GLint", "tag": "i"}}, - {"name": "width", - "type": {"name": "GLsizei", "tag": "i"}}, - {"name": "height", - "type": {"name": "GLsizei", "tag": "i"}} - ] -}, -{ - "name": "glGetShaderiv", - "cname": "glGetShaderiv", - "ret": {"name": "void", "tag": "v"}, - "args": [ {"name": "shader", - "type": {"name": "GLuint", "tag": "i"}}, - {"name": "pname", - "type": {"name": "GLenum", "tag": "i"}}, - {"name": "params", - "type": {"name": "GLint*", "tag": "p"}} - ] -}, -{ - "name": "glGetShaderInfoLog", - "cname": "glGetShaderInfoLog", - "ret": {"name": "void", "tag": "v"}, - "args": [ {"name": "shader", - "type": {"name": "GLuint", "tag": "i"}}, - {"name": "maxLength", - "type": {"name": "GLsizei", "tag": "i"}}, - {"name": "length", - "type": {"name": "GLsizei", "tag": "p"}}, - {"name": "infoLog", - "type": {"name": "GLchar", "tag": "p"}} - ] -}, -{ - "name": "glBindFramebuffer", - "cname": "glBindFramebuffer", - "ret": {"name": "void", "tag": "v"}, - "args": [ {"name": "target", - "type": {"name": "GLenum", "tag": "i"}}, - {"name": "framebuffer", - "type": {"name": "GLuint", "tag": "i"}} - ] -}, -{ - "name": "glBindTexture", - "cname": "glBindTexture", - "ret": {"name": "void", "tag": "v"}, - "args": [ {"name": "target", - "type": {"name": "GLenum", "tag": "i"}}, - {"name": "texture", - "type": {"name": "GLuint", "tag": "i"}} - ] -}, -{ - "name": "glActiveTexture", - "cname": "glActiveTexture", - "ret": {"name": "void", "tag": "v"}, - "args": [ {"name": "texture", - "type": {"name": "GLuint", "tag": "i"}}] -}, -{ - "name": "glGenBuffers", - "cname": "glGenBuffers", - "ret": {"name": "void", "tag": "v"}, - "args": [ {"name": "n", - "type": {"name": "GLenum", "tag": "i"}}, - {"name": "buffers", - "type": {"name": "GLuint*", "tag": "p"}} - ] -}, -{ - "name": "glGenTextures", - "cname": "glGenTextures", - "ret": {"name": "void", "tag": "v"}, - "args": [ {"name": "n", - "type": {"name": "GLenum", "tag": "i"}}, - {"name": "textures", - "type": {"name": "GLuint*", "tag": "p"}} - ] -}, -{ - "name": "glGenFramebuffers", - "cname": "glGenFramebuffers", - "ret": {"name": "void", "tag": "v"}, - "args": [ {"name": "n", - "type": {"name": "GLenum", "tag": "i"}}, - {"name": "framebuffers", - "type": {"name": "GLuint*", "tag": "p"}} - ] -}, -{ - "name": "glFramebufferTexture2D", - "cname": "glFramebufferTexture2D", - "ret": {"name": "void", "tag": "v"}, - "args": [ {"name": "target", - "type": {"name": "GLenum", "tag": "i"}}, - {"name": "attachment", - "type": {"name": "GLenum", "tag": "i"}}, - {"name": "textarget", - "type": {"name": "GLenum", "tag": "i"}}, - {"name": "texture", - "type": {"name": "GLuint", "tag": "i"}}, - {"name": "level", - "type": {"name": "GLint", "tag": "i"}} - ] -}, -{ - "name": "glCheckFramebufferStatus", - "cname": "glCheckFramebufferStatus", - "ret": {"name": "GLenum", "tag": "i"}, - "args": [ {"name": "target", - "type": {"name": "GLenum", "tag": "i"}}] -}, -{ - "name": "glTexImage2D", - "cname": "glTexImage2D", - "ret": {"name": "void", "tag": "v"}, - "args": [ {"name": "target", - "type": {"name": "GLenum", "tag": "i"}}, - {"name": "level", - "type": {"name": "GLint", "tag": "i"}}, - {"name": "internalformat", - "type": {"name": "GLint", "tag": "i"}}, - {"name": "width", - "type": {"name": "GLsizei", "tag": "i"}}, - {"name": "height", - "type": {"name": "GLsizei", "tag": "i"}}, - {"name": "border", - "type": {"name": "GLint", "tag": "i"}}, - {"name": "format", - "type": {"name": "GLenum", "tag": "i"}}, - {"name": "type", - "type": {"name": "GLenum", "tag": "i"}}, - {"name": "data", - "type": {"name": "void*", "tag": "p"}} - ] -}, -{ - "name": "glTexParameteri", - "cname": "glTexParameteri", - "ret": {"name": "void", "tag": "v"}, - "args": [ {"name": "target", - "type": {"name": "GLenum", "tag": "i"}}, - {"name": "pname", - "type": {"name": "GLenum", "tag": "i"}}, - {"name": "param", - "type": {"name": "GLint", "tag": "i"}} - ] -}, -{ - "name": "glBindBuffer", - "cname": "glBindBuffer", - "ret": {"name": "void", "tag": "v"}, - "args": [ {"name": "target", - "type": {"name": "GLenum", "tag": "i"}}, - {"name": "buffer", - "type": {"name": "GLuint", "tag": "i"}} - ] -}, -{ - "name": "glBufferData", - "cname": "glBufferData", - "ret": {"name": "void", "tag": "v"}, - "args": [ {"name": "target", - "type": {"name": "GLenum", "tag": "i"}}, - {"name": "size", - "type": {"name": "GLsizeiptr", "tag": "i"}}, - {"name": "data", - "type": {"name": "void*", "tag": "p"}}, - {"name": "usage", - "type": {"name": "GLenum", "tag": "i"}} - ] -}, -{ - "name": "glUniform1i", - "cname": "glUniform1i", - "ret": {"name": "void", "tag": "v"}, - "args": [ {"name": "location", - "type": {"name": "GLint", "tag": "i"}}, - {"name": "v0", - "type": {"name": "GLint", "tag": "i"}} - ] -}, -{ - "name": "glUniform2i", - "cname": "glUniform2i", - "ret": {"name": "void", "tag": "v"}, - "args": [ {"name": "location", - "type": {"name": "GLint", "tag": "i"}}, - {"name": "v0", - "type": {"name": "GLint", "tag": "i"}}, - {"name": "v1", - "type": {"name": "GLint", "tag": "i"}} - ] -}, -{ - "name": "glUniform1f", - "cname": "glUniform1f", - "ret": {"name": "void", "tag": "v"}, - "args": [ {"name": "location", - "type": {"name": "GLint", "tag": "i"}}, - {"name": "v0", - "type": {"name": "GLfloat", "tag": "f"}} - ] -}, -{ - "name": "glUniform2f", - "cname": "glUniform2f", - "ret": {"name": "void", "tag": "v"}, - "args": [ {"name": "location", - "type": {"name": "GLint", "tag": "i"}}, - {"name": "v0", - "type": {"name": "GLfloat", "tag": "f"}}, - {"name": "v1", - "type": {"name": "GLfloat", "tag": "f"}} - ] -}, -{ - "name": "glUniform3f", - "cname": "glUniform3f", - "ret": {"name": "void", "tag": "v"}, - "args": [ {"name": "location", - "type": {"name": "GLint", "tag": "i"}}, - {"name": "v0", - "type": {"name": "GLfloat", "tag": "f"}}, - {"name": "v1", - "type": {"name": "GLfloat", "tag": "f"}}, - {"name": "v2", - "type": {"name": "GLfloat", "tag": "f"}} - ] -}, -{ - "name": "glUniformMatrix4fv", - "cname": "glUniformMatrix4fv", - "ret": {"name": "void", "tag": "v"}, - "args": [ {"name": "location", - "type": {"name": "GLint", "tag": "i"}}, - {"name": "count", - "type": {"name": "GLsizei", "tag": "i"}}, - {"name": "transpose", - "type": {"name": "GLboolean", "tag": "i"}}, - {"name": "value", - "type": {"name": "GLfloat*", "tag": "p"}} - ] -}, -{ - "name": "glGetAttribLocation", - "cname": "glGetAttribLocation", - "ret": {"name": "GLint", "tag": "i"}, - "args": [ {"name": "program", - "type": {"name": "GLuint", "tag": "i"}}, - {"name": "name", - "type": {"name": "GLchar*", "tag": "p"}} - ] -}, -{ - "name": "glGetUniformLocation", - "cname": "glGetUniformLocation", - "ret": {"name": "GLint", "tag": "i"}, - "args": [ {"name": "program", - "type": {"name": "GLuint", "tag": "i"}}, - {"name": "name", - "type": {"name": "GLchar*", "tag": "p"}} - ] -}, - -{ - "name": "glVertexAttribPointer", - "cname": "glVertexAttribPointer", - "ret": {"name": "void", "tag": "v"}, - "args": [ {"name": "index", - "type": {"name": "GLuint", "tag": "i"}}, - {"name": "size", - "type": {"name": "GLint", "tag": "i"}}, - {"name": "type", - "type": {"name": "GLenum", "tag": "i"}}, - {"name": "normalized", - "type": {"name": "GLboolean", "tag": "i"}}, - {"name": "stride", - "type": {"name": "GLsizei", "tag": "i"}}, - {"name": "pointer", - "type": {"name": "void*", "tag": "p"}} - ] -}, -{ - "name": "glEnableVertexAttribArray", - "cname": "glEnableVertexAttribArray", - "ret": {"name": "void", "tag": "v"}, - "args": [ {"name": "index", - "type": {"name": "GLuint", "tag": "i"}}] -}, -{ - "name": "glDrawArrays", - "cname": "glDrawArrays", - "ret": {"name": "void", "tag": "v"}, - "args": [ {"name": "mode", - "type": {"name": "GLenum", "tag": "i"}}, - {"name": "first", - "type": {"name": "GLint", "tag": "i"}}, - {"name": "count", - "type": {"name": "GLsizei", "tag": "i"}} - ] -} -] diff --git a/src/gles_api_bind_manual.c b/src/gles_api_bind_manual.c new file mode 100644 index 0000000..a9bdfea --- /dev/null +++ b/src/gles_api_bind_manual.c @@ -0,0 +1,1132 @@ + +//------------------------------------------------------------------------ +// Manual pointer size checking functions +//------------------------------------------------------------------------ + +u64 orca_gles_check_cstring(IM3Runtime runtime, const char* ptr) +{ + uint32_t memorySize = 0; + char* memory = (char*)m3_GetMemory(runtime, &memorySize, 0); + + //NOTE: Here we are guaranteed that ptr is in [ memory ; memory + memorySize [ + // hence (memory + memorySize) - ptr is representable by size_t and <= memorySize + size_t maxLen = (memory + memorySize) - ptr; + + u64 len = strnlen(ptr, maxLen); + + if(len == maxLen) + { + //NOTE: string overflows wasm memory, return a length that will trigger the bounds check + len = maxLen + 1; + } + return(len+1); //include null-terminator +} + +u64 orca_gl_type_size(GLenum type) +{ + u64 size = 8; + switch(type) + { + case GL_UNSIGNED_BYTE: + case GL_BYTE: + size = sizeof(GLbyte); + break; + + case GL_UNSIGNED_SHORT: + case GL_SHORT: + case GL_HALF_FLOAT: + size = sizeof(GLshort); + break; + + case GL_UNSIGNED_INT: + case GL_INT: + case GL_FIXED: + case GL_INT_2_10_10_10_REV: + case GL_UNSIGNED_INT_2_10_10_10_REV: + size = sizeof(GLint); + break; + + case GL_FLOAT: + size = sizeof(GLfloat); + break; + + case GL_DOUBLE: + size = sizeof(GLdouble); + break; + + default: + ORCA_ASSERT(0, "unknown GLenum type %i", type); + } + + return(size); +} + +u64 orca_gl_format_count(GLenum format) +{ + u64 count = 4; + switch(format) + { + case GL_RED: + case GL_RED_INTEGER: + case GL_DEPTH_COMPONENT: + case GL_STENCIL_INDEX: + case GL_LUMINANCE: + case GL_ALPHA: + count = 1; + break; + + case GL_RG: + case GL_RG_INTEGER: + case GL_DEPTH_STENCIL: + case GL_LUMINANCE_ALPHA: + count = 2; + break; + + case GL_RGB: + case GL_RGB_INTEGER: + count = 3; + break; + + case GL_RGBA: + case GL_RGBA_INTEGER: + count = 4; + break; + + default: + ORCA_ASSERT(0, "unknow GLenum format %i", format); + } + + return(count); +} + +typedef struct orca_gles_impl_limits +{ + bool init; + int maxDrawBuffers; + int numCompressedTextureFormats; + int numProgramBinaryFormats; + int numShaderBinaryFormats; + //... +} orca_gles_impl_limits; + +orca_gles_impl_limits __orcaGLESImplLimits = {0}; + +u64 orca_glGet_data_length(GLenum pname) +{ + if(!__orcaGLESImplLimits.init) + { + glGetIntegerv(GL_MAX_DRAW_BUFFERS, &__orcaGLESImplLimits.maxDrawBuffers); + glGetIntegerv(GL_NUM_COMPRESSED_TEXTURE_FORMATS, &__orcaGLESImplLimits.numCompressedTextureFormats); + glGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS, &__orcaGLESImplLimits.numProgramBinaryFormats); + glGetIntegerv(GL_NUM_SHADER_BINARY_FORMATS, &__orcaGLESImplLimits.numShaderBinaryFormats); + } + u64 count = 8; + + if(pname >= GL_DRAW_BUFFER0 && pname < GL_DRAW_BUFFER0 + __orcaGLESImplLimits.maxDrawBuffers) + { + count = 1; + } + else + { + switch(pname) + { + case GL_ACTIVE_TEXTURE: + case GL_ALPHA_BITS: + case GL_ARRAY_BUFFER_BINDING: + case GL_BLEND: + case GL_BLEND_DST_ALPHA: + case GL_BLEND_DST_RGB: + case GL_BLEND_EQUATION_ALPHA: + case GL_BLEND_EQUATION_RGB: + case GL_BLEND_SRC_ALPHA: + case GL_BLEND_SRC_RGB: + case GL_BLUE_BITS: + case GL_CONTEXT_FLAGS: + case GL_CONTEXT_ROBUST_ACCESS: + case GL_COPY_READ_BUFFER_BINDING: + case GL_COPY_WRITE_BUFFER_BINDING: + case GL_CULL_FACE: + case GL_CULL_FACE_MODE: + case GL_CURRENT_PROGRAM: + case GL_DEBUG_GROUP_STACK_DEPTH: + case GL_DEBUG_LOGGED_MESSAGES: + case GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH: + case GL_DEPTH_BITS: + case GL_DEPTH_CLEAR_VALUE: + case GL_DEPTH_FUNC: + case GL_DEPTH_TEST: + case GL_DEPTH_WRITEMASK: + case GL_DISPATCH_INDIRECT_BUFFER_BINDING: + case GL_DITHER: + case GL_DRAW_FRAMEBUFFER_BINDING: + case GL_ELEMENT_ARRAY_BUFFER_BINDING: + case GL_FRAGMENT_INTERPOLATION_OFFSET_BITS: + case GL_FRAGMENT_SHADER_DERIVATIVE_HINT: + case GL_FRONT_FACE: + case GL_GENERATE_MIPMAP_HINT: + case GL_GREEN_BITS: + case GL_IMAGE_BINDING_LAYERED: + case GL_IMPLEMENTATION_COLOR_READ_FORMAT: + case GL_IMPLEMENTATION_COLOR_READ_TYPE: + case GL_LAYER_PROVOKING_VERTEX: + case GL_LINE_WIDTH: + case GL_MAJOR_VERSION: + case GL_MAX_3D_TEXTURE_SIZE: + case GL_MAX_ARRAY_TEXTURE_LAYERS: + case GL_MAX_ATOMIC_COUNTER_BUFFER_BINDINGS: + case GL_MAX_COLOR_ATTACHMENTS: + + case GL_MAX_COMBINED_ATOMIC_COUNTERS: + case GL_MAX_COMBINED_ATOMIC_COUNTER_BUFFERS: + case GL_MAX_COMBINED_COMPUTE_UNIFORM_COMPONENTS: + case GL_MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS: + case GL_MAX_COMBINED_GEOMETRY_UNIFORM_COMPONENTS: + case GL_MAX_COMBINED_SHADER_STORAGE_BLOCKS: + case GL_MAX_COMBINED_TESS_CONTROL_UNIFORM_COMPONENTS: + case GL_MAX_COMBINED_TESS_EVALUATION_UNIFORM_COMPONENTS: + case GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS: + case GL_MAX_COMBINED_UNIFORM_BLOCKS: + case GL_MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS: + case GL_MAX_COMPUTE_ATOMIC_COUNTERS: + case GL_MAX_COMPUTE_ATOMIC_COUNTER_BUFFERS: + case GL_MAX_COMPUTE_IMAGE_UNIFORMS: + case GL_MAX_COMPUTE_SHADER_STORAGE_BLOCKS: + case GL_MAX_COMPUTE_TEXTURE_IMAGE_UNITS: + case GL_MAX_COMPUTE_UNIFORM_BLOCKS: + case GL_MAX_COMPUTE_UNIFORM_COMPONENTS: + case GL_MAX_COMPUTE_WORK_GROUP_INVOCATIONS: + case GL_MAX_COMPUTE_WORK_GROUP_COUNT: + case GL_MAX_COMPUTE_WORK_GROUP_SIZE: + case GL_MAX_CUBE_MAP_TEXTURE_SIZE: + case GL_MAX_DEBUG_GROUP_STACK_DEPTH: + case GL_MAX_DEBUG_LOGGED_MESSAGES: + case GL_MAX_DEBUG_MESSAGE_LENGTH: + case GL_MAX_DEPTH_TEXTURE_SAMPLES: + case GL_MAX_DRAW_BUFFERS: + case GL_MAX_ELEMENT_INDEX: + case GL_MAX_ELEMENTS_INDICES: + case GL_MAX_ELEMENTS_VERTICES: + case GL_MAX_FRAGMENT_ATOMIC_COUNTERS: + case GL_MAX_FRAGMENT_ATOMIC_COUNTER_BUFFERS: + case GL_MAX_FRAGMENT_IMAGE_UNIFORMS: + case GL_MAX_FRAGMENT_INPUT_COMPONENTS: + case GL_MAX_FRAGMENT_INTERPOLATION_OFFSET: + case GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS: + case GL_MAX_FRAGMENT_UNIFORM_BLOCKS: + case GL_MAX_FRAGMENT_UNIFORM_COMPONENTS: + case GL_MAX_FRAGMENT_UNIFORM_VECTORS: + case GL_MAX_FRAMEBUFFER_HEIGHT: + case GL_MAX_FRAMEBUFFER_LAYERS: + case GL_MAX_FRAMEBUFFER_SAMPLES: + case GL_MAX_FRAMEBUFFER_WIDTH: + case GL_MAX_GEOMETRY_ATOMIC_COUNTER_BUFFERS: + case GL_MAX_GEOMETRY_ATOMIC_COUNTERS: + case GL_MAX_GEOMETRY_IMAGE_UNIFORMS: + case GL_MAX_GEOMETRY_INPUT_COMPONENTS: + case GL_MAX_GEOMETRY_OUTPUT_COMPONENTS: + case GL_MAX_GEOMETRY_OUTPUT_VERTICES: + case GL_MAX_GEOMETRY_SHADER_STORAGE_BLOCKS: + case GL_MAX_GEOMETRY_SHADER_INVOCATIONS: + case GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS: + case GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS: + case GL_MAX_GEOMETRY_UNIFORM_BLOCKS: + case GL_MAX_GEOMETRY_UNIFORM_COMPONENTS: + case GL_MAX_INTEGER_SAMPLES: + case GL_MAX_LABEL_LENGTH: + case GL_MAX_PROGRAM_TEXEL_OFFSET: + case GL_MAX_RENDERBUFFER_SIZE: + case GL_MAX_SAMPLE_MASK_WORDS: + case GL_MAX_SAMPLES: + case GL_MAX_SERVER_WAIT_TIMEOUT: + case GL_MAX_SHADER_STORAGE_BLOCK_SIZE: + case GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS: + case GL_MAX_TESS_CONTROL_ATOMIC_COUNTER_BUFFERS: + case GL_MAX_TESS_CONTROL_ATOMIC_COUNTERS: + case GL_MAX_TESS_CONTROL_IMAGE_UNIFORMS: + case GL_MAX_TESS_CONTROL_INPUT_COMPONENTS: + case GL_MAX_TESS_CONTROL_OUTPUT_COMPONENTS: + case GL_MAX_TESS_CONTROL_SHADER_STORAGE_BLOCKS: + case GL_MAX_TESS_CONTROL_TEXTURE_IMAGE_UNITS: + case GL_MAX_TESS_CONTROL_TOTAL_OUTPUT_COMPONENTS: + case GL_MAX_TESS_CONTROL_UNIFORM_BLOCKS: + case GL_MAX_TESS_CONTROL_UNIFORM_COMPONENTS: + case GL_MAX_TESS_EVALUATION_ATOMIC_COUNTER_BUFFERS: + case GL_MAX_TESS_EVALUATION_ATOMIC_COUNTERS: + case GL_MAX_TESS_EVALUATION_IMAGE_UNIFORMS: + case GL_MAX_TESS_EVALUATION_INPUT_COMPONENTS: + case GL_MAX_TESS_EVALUATION_OUTPUT_COMPONENTS: + case GL_MAX_TESS_EVALUATION_SHADER_STORAGE_BLOCKS: + case GL_MAX_TESS_EVALUATION_TEXTURE_IMAGE_UNITS: + case GL_MAX_TESS_EVALUATION_UNIFORM_BLOCKS: + case GL_MAX_TESS_EVALUATION_UNIFORM_COMPONENTS: + case GL_MAX_TESS_GEN_LEVEL: + case GL_MAX_TESS_PATCH_COMPONENTS: + case GL_MAX_TEXTURE_BUFFER_SIZE: + case GL_MAX_TEXTURE_IMAGE_UNITS: + case GL_MAX_TEXTURE_LOD_BIAS: + case GL_MAX_TEXTURE_SIZE: + case GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS: + case GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS: + case GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS: + case GL_MAX_UNIFORM_BLOCK_SIZE: + case GL_MAX_UNIFORM_BUFFER_BINDINGS: + case GL_MAX_UNIFORM_LOCATIONS: + case GL_MAX_VARYING_COMPONENTS: + case GL_MAX_VARYING_VECTORS: + case GL_MAX_VERTEX_ATOMIC_COUNTERS: + case GL_MAX_VERTEX_ATOMIC_COUNTER_BUFFERS: + case GL_MAX_VERTEX_ATTRIB_BINDINGS: + case GL_MAX_VERTEX_ATTRIB_RELATIVE_OFFSET: + case GL_MAX_VERTEX_ATTRIBS: + case GL_MAX_VERTEX_IMAGE_UNIFORMS: + case GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS: + case GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS: + case GL_MAX_VERTEX_OUTPUT_COMPONENTS: + case GL_MAX_VERTEX_UNIFORM_BLOCKS: + case GL_MAX_VERTEX_UNIFORM_COMPONENTS: + case GL_MAX_VERTEX_UNIFORM_VECTORS: + case GL_MIN_FRAGMENT_INTERPOLATION_OFFSET: + case GL_MIN_PROGRAM_TEXEL_OFFSET: + case GL_MIN_SAMPLE_SHADING_VALUE: + case GL_MINOR_VERSION: + case GL_NUM_COMPRESSED_TEXTURE_FORMATS: + case GL_NUM_EXTENSIONS: + case GL_NUM_PROGRAM_BINARY_FORMATS: + case GL_NUM_SHADER_BINARY_FORMATS: + case GL_PACK_ALIGNMENT: + case GL_PACK_ROW_LENGTH: + case GL_PACK_SKIP_PIXELS: + case GL_PACK_SKIP_ROWS: + case GL_PATCH_VERTICES: + case GL_PIXEL_PACK_BUFFER_BINDING: + case GL_PIXEL_UNPACK_BUFFER_BINDING: + case GL_POLYGON_OFFSET_FACTOR: + case GL_POLYGON_OFFSET_FILL: + case GL_POLYGON_OFFSET_UNITS: + case GL_PRIMITIVE_RESTART_FIXED_INDEX: + case GL_PROGRAM_PIPELINE_BINDING: + case GL_RASTERIZER_DISCARD: + case GL_READ_BUFFER: + case GL_READ_FRAMEBUFFER_BINDING: + case GL_RED_BITS: + case GL_RENDERBUFFER_BINDING: + case GL_RESET_NOTIFICATION_STRATEGY: + case GL_SAMPLE_ALPHA_TO_COVERAGE: + case GL_SAMPLE_BUFFERS: + case GL_SAMPLE_COVERAGE: + case GL_SAMPLE_COVERAGE_INVERT: + case GL_SAMPLE_COVERAGE_VALUE: + case GL_SAMPLE_MASK_VALUE: + case GL_SAMPLE_SHADING: + case GL_SAMPLER_BINDING: + case GL_SAMPLES: + case GL_SCISSOR_TEST: + case GL_SHADER_COMPILER: + case GL_SHADER_STORAGE_BUFFER_BINDING: + case GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT: + case GL_SHADER_STORAGE_BUFFER_SIZE: + case GL_SHADER_STORAGE_BUFFER_START: + case GL_STENCIL_BACK_FAIL: + case GL_STENCIL_BACK_FUNC: + case GL_STENCIL_BACK_PASS_DEPTH_FAIL: + case GL_STENCIL_BACK_PASS_DEPTH_PASS: + case GL_STENCIL_BACK_REF: + case GL_STENCIL_BACK_VALUE_MASK: + case GL_STENCIL_BACK_WRITEMASK: + case GL_STENCIL_BITS: + case GL_STENCIL_CLEAR_VALUE: + case GL_STENCIL_FAIL: + case GL_STENCIL_FUNC: + case GL_STENCIL_PASS_DEPTH_FAIL: + case GL_STENCIL_PASS_DEPTH_PASS: + case GL_STENCIL_REF: + case GL_STENCIL_TEST: + case GL_STENCIL_VALUE_MASK: + case GL_STENCIL_WRITEMASK: + case GL_SUBPIXEL_BITS: + case GL_TEXTURE_BINDING_2D: + case GL_TEXTURE_BINDING_2D_ARRAY: + case GL_TEXTURE_BINDING_3D: + case GL_TEXTURE_BINDING_BUFFER: + case GL_TEXTURE_BINDING_CUBE_MAP: + case GL_TEXTURE_BINDING_2D_MULTISAMPLE: + case GL_TEXTURE_BINDING_2D_MULTISAMPLE_ARRAY: + case GL_TEXTURE_BINDING_CUBE_MAP_ARRAY: + case GL_TEXTURE_BUFFER_BINDING: + case GL_TEXTURE_BUFFER_OFFSET_ALIGNMENT: + case GL_TRANSFORM_FEEDBACK_BINDING: + case GL_TRANSFORM_FEEDBACK_ACTIVE: + case GL_TRANSFORM_FEEDBACK_BUFFER_BINDING: + case GL_TRANSFORM_FEEDBACK_PAUSED: + case GL_TRANSFORM_FEEDBACK_BUFFER_SIZE: + case GL_TRANSFORM_FEEDBACK_BUFFER_START: + case GL_UNIFORM_BUFFER_BINDING: + case GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT: + case GL_UNIFORM_BUFFER_SIZE: + case GL_UNIFORM_BUFFER_START: + case GL_UNPACK_ALIGNMENT: + case GL_UNPACK_IMAGE_HEIGHT: + case GL_UNPACK_ROW_LENGTH: + case GL_UNPACK_SKIP_IMAGES: + case GL_UNPACK_SKIP_PIXELS: + case GL_UNPACK_SKIP_ROWS: + case GL_VERTEX_ARRAY_BINDING: + case GL_VERTEX_BINDING_DIVISOR: + case GL_VERTEX_BINDING_OFFSET: + case GL_VERTEX_BINDING_STRIDE: + case GL_TEXTURE_2D: + case GL_TEXTURE_3D: + count = 1; + break; + + case GL_ALIASED_LINE_WIDTH_RANGE: + case GL_ALIASED_POINT_SIZE_RANGE: + case GL_DEPTH_RANGE: + case GL_MAX_VIEWPORT_DIMS: + case GL_MULTISAMPLE_LINE_WIDTH_RANGE: + count = 2; + break; + + case GL_BLEND_COLOR: + case GL_COLOR_CLEAR_VALUE: + case GL_COLOR_WRITEMASK: + case GL_SCISSOR_BOX: + case GL_VIEWPORT: + count = 4; + break; + + case GL_PRIMITIVE_BOUNDING_BOX: + count = 8; + break; + + case GL_COMPRESSED_TEXTURE_FORMATS: + count = __orcaGLESImplLimits.numCompressedTextureFormats; + break; + + case GL_PROGRAM_BINARY_FORMATS: + count = __orcaGLESImplLimits.numProgramBinaryFormats; + break; + + case GL_SHADER_BINARY_FORMATS: + count = __orcaGLESImplLimits.numShaderBinaryFormats; + break; + + default: + ORCA_ASSERT(0, "unknown GLenum pname %i", pname); + break; + } + } + return(count); +} + +u64 orca_glDrawElements_indices_length(IM3Runtime runtime, GLsizei count, GLenum type) +{ + return(orca_gl_type_size(type)*count); +} + +u64 orca_glGetBooleanv_data_length(IM3Runtime runtime, GLenum pname) +{ + return(orca_glGet_data_length(pname)); +} + +u64 orca_glGetBufferParameteriv_params_length(IM3Runtime runtime, GLenum pname) +{ + //NOTE: all pnames return a single value in 3.1 + return(1); +} +u64 orca_glGetFloatv_data_length(IM3Runtime runtime, GLenum pname) +{ + return(orca_glGet_data_length(pname)); +} +u64 orca_glGetFramebufferAttachmentParameteriv_params_length(IM3Runtime runtime, GLenum pname) +{ + //NOTE: all pnames return a single value in 3.1 + return(1); +} +u64 orca_glGetIntegerv_data_length(IM3Runtime runtime, GLenum pname) +{ + return(orca_glGet_data_length(pname)); +} +u64 orca_glGetProgramiv_params_length(IM3Runtime runtime, GLenum pname) +{ + //NOTE: all pnames return a single value in 3.1 + return(1); +} +u64 orca_glGetRenderbufferParameteriv_params_length(IM3Runtime runtime, GLenum pname) +{ + //NOTE: all pnames return a single value in 3.1 + return(1); +} +u64 orca_glGetShaderiv_params_length(IM3Runtime runtime, GLenum pname) +{ + //NOTE: all pnames return a single value in 3.1 + return(1); +} + +u64 orca_glTexParameter_params_length_generic(GLenum pname) +{ + u64 count = 4; + if(pname == GL_TEXTURE_BORDER_COLOR) + { + count = 4; + } + else + { + count = 1; + } + return(count); +} + +u64 orca_glGetTexParameterfv_params_length(IM3Runtime runtime, GLenum pname) +{ + return(orca_glTexParameter_params_length_generic(pname)); +} + +u64 orca_glGetTexParameteriv_params_length(IM3Runtime runtime, GLenum pname) +{ + return(orca_glTexParameter_params_length_generic(pname)); +} + +u64 orca_glReadPixels_pixels_length(IM3Runtime runtime, GLenum format, GLenum type, GLsizei width, GLsizei height) +{ + u64 count = width*height*orca_gl_type_size(type)*orca_gl_format_count(format); + return(count); +} +u64 orca_glTexImage2D_pixels_length(IM3Runtime runtime, GLenum format, GLenum type, GLsizei width, GLsizei height) +{ + u64 count = width*height*orca_gl_type_size(type)*orca_gl_format_count(format); + return(count); +} + +u64 orca_glTexParameterfv_params_length(IM3Runtime runtime, GLenum pname) +{ + return(orca_glTexParameter_params_length_generic(pname)); +} +u64 orca_glTexParameteriv_params_length(IM3Runtime runtime, GLenum pname) +{ + return(orca_glTexParameter_params_length_generic(pname)); +} +u64 orca_glTexSubImage2D_pixels_length(IM3Runtime runtime, GLenum format, GLenum type, GLsizei width, GLsizei height) +{ + u64 count = width*height*orca_gl_type_size(type)*orca_gl_format_count(format); + return(count); +} + +u64 orca_glDrawRangeElements_indices_length(IM3Runtime runtime, GLsizei count, GLenum type) +{ + return(count*orca_gl_type_size(type)); +} +u64 orca_glTexImage3D_pixels_length(IM3Runtime runtime, GLenum format, GLenum type, GLsizei width, GLsizei height, GLsizei depth) +{ + u64 count = width*height*depth*orca_gl_type_size(type)*orca_gl_format_count(format); + return(count); +} +u64 orca_glTexSubImage3D_pixels_length(IM3Runtime runtime, GLenum format, GLenum type, GLsizei width, GLsizei height, GLsizei depth) +{ + u64 count = width*height*depth*orca_gl_type_size(type)*orca_gl_format_count(format); + return(count); +} +u64 orca_glGetQueryiv_params_length(IM3Runtime runtime, GLenum pname) +{ + //NOTE: all pnames return a single value in 3.1 + return(1); +} +u64 orca_glGetQueryObjectuiv_params_length(IM3Runtime runtime, GLenum pname) +{ + //NOTE: all pnames return a single value in 3.1 + return(1); +} +u64 orca_glGetIntegeri_v_data_length(IM3Runtime runtime, GLenum target) +{ + return(orca_glGet_data_length(target)); +} +u64 orca_glVertexAttribIPointer_pointer_length(IM3Runtime runtime, GLint size, GLenum type, GLsizei stride) +{ + //WARN: pointer param of glVertexAttribPointer is actually treated as an offset, + // so, we don't need to check if this points to valid memory ?? + return(0); +} + +u64 orca_glClearBuffer_value_length_generic(GLenum buffer) +{ + u64 count = 4; + switch(buffer) + { + case GL_COLOR: + count = 4; + break; + + case GL_DEPTH: + case GL_STENCIL: + count = 1; + break; + + default: + ORCA_ASSERT(0, "invalid buffer enum for glClearBuffer()"); + } + return(count); +} + +u64 orca_glClearBufferiv_value_length(IM3Runtime runtime, GLenum buffer) +{ + return(orca_glClearBuffer_value_length_generic(buffer)); +} +u64 orca_glClearBufferuiv_value_length(IM3Runtime runtime, GLenum buffer) +{ + return(orca_glClearBuffer_value_length_generic(buffer)); +} +u64 orca_glClearBufferfv_value_length(IM3Runtime runtime, GLenum buffer) +{ + return(orca_glClearBuffer_value_length_generic(buffer)); +} + +u64 orca_glGetUniformIndices_uniformIndices_length(IM3Runtime runtime, GLsizei uniformCount) +{ + return(uniformCount); +} +u64 orca_glGetActiveUniformsiv_params_length(IM3Runtime runtime, GLsizei uniformCount, GLenum pname) +{ + return(uniformCount); +} + +u64 orca_glGetActiveUniformBlockiv_params_length(IM3Runtime runtime, GLuint program, GLuint uniformBlockIndex, GLenum pname) +{ + u64 count; + if(pname == GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES) + { + GLint param; + glGetActiveUniformBlockiv(program, uniformBlockIndex, GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, ¶m); + count = param; + } + else + { + count = 1; + } + return(count); +} +u64 orca_glDrawElementsInstanced_indices_length(IM3Runtime runtime, GLsizei count, GLenum type) +{ + return(count*orca_gl_type_size(type)); +} +u64 orca_glGetInteger64v_data_length(IM3Runtime runtime, GLenum pname) +{ + return(orca_glGet_data_length(pname)); +} +u64 orca_glGetInteger64i_v_data_length(IM3Runtime runtime, GLenum target) +{ + return(orca_glGet_data_length(target)); +} +u64 orca_glGetBufferParameteri64v_params_length(IM3Runtime runtime, GLenum pname) +{ + //NOTE: all pnames return a single value in 3.1 + return(1); +} + +u64 orca_glSamplerParameter_param_length_generic(GLenum pname) +{ + //NOTE: same as texture parameter pnames + return(orca_glTexParameter_params_length_generic(pname)); +} + +u64 orca_glSamplerParameteriv_param_length(IM3Runtime runtime, GLenum pname) +{ + return(orca_glSamplerParameter_param_length_generic(pname)); +} +u64 orca_glSamplerParameterfv_param_length(IM3Runtime runtime, GLenum pname) +{ + return(orca_glSamplerParameter_param_length_generic(pname)); +} +u64 orca_glGetSamplerParameteriv_params_length(IM3Runtime runtime, GLenum pname) +{ + return(orca_glSamplerParameter_param_length_generic(pname)); +} +u64 orca_glGetSamplerParameterfv_params_length(IM3Runtime runtime, GLenum pname) +{ + return(orca_glSamplerParameter_param_length_generic(pname)); +} +u64 orca_glGetFramebufferParameteriv_params_length(IM3Runtime runtime, GLenum pname) +{ + //NOTE: all pnames return a single value in 3.1 + return(1); +} +u64 orca_glGetProgramInterfaceiv_params_length(IM3Runtime runtime, GLenum pname) +{ + //NOTE: all pnames return a single value in 3.1 + return(1); +} + +u64 orca_glGetProgramPipelineiv_params_length(IM3Runtime runtime, GLenum pname) +{ + //NOTE: all pnames return a single value in 3.1 + return(1); +} +u64 orca_glGetBooleani_v_data_length(IM3Runtime runtime, GLenum target) +{ + return(orca_glSamplerParameter_param_length_generic(target)); +} +u64 orca_glGetMultisamplefv_val_length(IM3Runtime runtime, GLenum pname) +{ + return(2); +} +u64 orca_glGetTexLevelParameteriv_params_length(IM3Runtime runtime, GLenum pname) +{ + //NOTE: all pnames return a single value in 3.1 + return(1); +} +u64 orca_glGetTexLevelParameterfv_params_length(IM3Runtime runtime, GLenum pname) +{ + //NOTE: all pnames return a single value in 3.1 + return(1); +} + +//------------------------------------------------------------------------ +// Uniforms size checking +//------------------------------------------------------------------------ + +u64 orca_glGetUniform_params_length_generic(GLuint program, GLint location) +{ + //NOTE: This is super stupid but we can't get the size (or index) of a uniform directly from its location, + // so we have to iterate through all uniforms... + GLint maxUniformName = 0; + glGetProgramiv(program, GL_ACTIVE_UNIFORM_MAX_LENGTH, &maxUniformName); + + int uniformCount = 0; + glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &uniformCount); + + mem_arena_scope scratch = mem_scratch_begin(); + char* name = mem_arena_alloc(scratch.arena, maxUniformName+1); + + u64 count = 0; + bool found = false; + + for(int i=0; i= (char*)_mem) && (((char*)pointer - (char*)_mem) < m3_GetMemorySize(runtime)), + "parameter 'pointer' is out of bounds"); + ORCA_ASSERT((char*)pointer + sizeof(i32) <= ((char*)_mem + m3_GetMemorySize(runtime)), + "parameter 'pointer' overflows wasm memory"); + } + void* rawPointer = 0; + glGetVertexAttribPointerv(index, pname, &rawPointer); + + //NOTE: pointer is actually a _byte offset_ into a GPU buffer. So we do _not_ convert it to a wasm pointer, + // but we need to truncate it to u32 size... + //WARN: can OpenGL return a byte offset > UINT_MAX ? + *pointer = (i32)(intptr_t)rawPointer; + return(0); +} + +const void* glVertexAttribPointer_stub(IM3Runtime runtime, IM3ImportContext _ctx, uint64_t* _sp, void* _mem) +{ + GLuint index = *(u32*)&_sp[0]; + GLint size = *(i32*)&_sp[1]; + GLenum type = *(i32*)&_sp[2]; + GLboolean normalized = (GLboolean)*(i32*)&_sp[3]; + GLsizei stride = *(i32*)&_sp[4]; + + //NOTE: pointer is interpreted as an offset if there's a non-null buffer bound to GL_ARRAY_BUFFER, + // or as a pointer otherwise. Since there's no way of checking the length of client vertex arrays, + // we just disable those. + + GLint boundBuffer = 0; + glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &boundBuffer); + + if(boundBuffer != 0) + { + //NOTE: don't do bounds checking since pointer is really an offset in a GPU buffer + const void* pointer = (void*)(intptr_t)*(u32*)&_sp[5]; + + glVertexAttribPointer(index, size, type, normalized, stride, pointer); + } + else + { + //NOTE: we crash here before letting ANGLE crash because vertex attrib pointer is not set + ORCA_ASSERT("Calling glVertexAttribPointer with a GL_ARRAY_BUFFER binding of 0 is unsafe and disabled in Orca."); + } + return(0); +} + +const void* glVertexAttribIPointer_stub(IM3Runtime runtime, IM3ImportContext _ctx, uint64_t* _sp, void* _mem) +{ + GLuint index = *(u32*)&_sp[0]; + GLint size = *(i32*)&_sp[1]; + GLenum type = *(i32*)&_sp[2]; + GLsizei stride = *(i32*)&_sp[3]; + + //NOTE: pointer is interpreted as an offset if there's a non-null buffer bound to GL_ARRAY_BUFFER, + // or as a pointer otherwise. Since there's no way of checking the length of client vertex arrays, + // we just disable those. + + GLint boundBuffer = 0; + glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &boundBuffer); + + if(boundBuffer != 0) + { + //NOTE: don't do bounds checking since pointer is really an offset in a GPU buffer + const void* pointer = (void*)(intptr_t)*(u32*)&_sp[4]; + + glVertexAttribIPointer(index, size, type, stride, pointer); + } + else + { + ORCA_ASSERT(0, "Calling glVertexAttribIPointer with a GL_ARRAY_BUFFER binding of 0 is unsafe and disabled in Orca."); + } + return(0); +} + +const void* glGetUniformIndices_stub(IM3Runtime runtime, IM3ImportContext _ctx, uint64_t* _sp, void* _mem) +{ + GLuint program = (GLuint)*(i32*)&_sp[0]; + GLsizei uniformCount = (GLsizei)*(i32*)&_sp[1]; + u32* uniformNames = (u32*)((char*)_mem + *(u32*)&_sp[2]); + GLuint * uniformIndices = (GLuint *)((char*)_mem + *(u32*)&_sp[3]); + + u64 memorySize = m3_GetMemorySize(runtime); + //NOTE: check size of uniformNames + { + ORCA_ASSERT(((char*)uniformNames >= (char*)_mem) && (((char*)uniformNames - (char*)_mem) < memorySize), + "parameter 'uniformNames' is out of bounds"); + ORCA_ASSERT((char*)uniformNames + uniformCount * sizeof(u32) <= ((char*)_mem + memorySize), + "parameter 'uniformNames' overflows wasm memory"); + } + //NOTE: check each individual uniformNames + mem_arena_scope scratch = mem_scratch_begin(); + + char** uniformNamesRaw = mem_arena_alloc_array(scratch.arena, char*, uniformCount); + for(int i=0; i= (char*)_mem && (raw - (char*)_mem) < memorySize, "uniformName[%i] is out of bounds", i); + + u64 len = orca_gles_check_cstring(runtime, raw); + + ORCA_ASSERT(raw + len <= ((char*)_mem + memorySize), "uniformName[%i] overflows wasm memory", i); + + uniformNamesRaw[i] = raw; + } + + //NOTE: check size of uniformIndices + { + ORCA_ASSERT(((char*)uniformIndices >= (char*)_mem) && (((char*)uniformIndices - (char*)_mem) < memorySize), + "parameter 'uniformIndices' is out of bounds"); + ORCA_ASSERT((char*)uniformIndices + uniformCount * sizeof(GLuint) <= ((char*)_mem + memorySize), + "parameter 'uniformIndices' overflows wasm memory"); + } + + glGetUniformIndices(program, uniformCount, (const GLchar* const*)uniformNamesRaw, uniformIndices); + + mem_scratch_end(scratch); + return(0); +} + +typedef struct orca_gl_getstring_entry +{ + u32 offset; + u32 len; +} orca_gl_getstring_entry; + +GLenum ORCA_GL_GETSTRING_NAMES[] = { + GL_EXTENSIONS, + GL_VENDOR, + GL_RENDERER, + GL_VERSION, + GL_SHADING_LANGUAGE_VERSION +}; + +enum { + ORCA_GL_GETSTRING_ENTRY_COUNT = sizeof(ORCA_GL_GETSTRING_NAMES)/sizeof(GLenum) +}; + +typedef struct orca_gl_getstring_info +{ + bool init; + + orca_gl_getstring_entry entries[ORCA_GL_GETSTRING_ENTRY_COUNT]; + + u32 indexedEntryCount; + orca_gl_getstring_entry* indexedEntries; + +} orca_gl_getstring_info; + +orca_gl_getstring_info __orcaGLGetStringInfo = {0}; + +void orca_gl_getstring_init(orca_gl_getstring_info* info, char* memory) +{ + u32 totalSize = 0; + const char* strings[ORCA_GL_GETSTRING_ENTRY_COUNT] = {0}; + + for(int i=0; ientries[i].len = strlen(strings[i]) + 1; + totalSize += info->entries[i].len; + } + } + + glGetIntegerv(GL_NUM_EXTENSIONS, (GLint*)&info->indexedEntryCount); + mem_arena_scope scratch = mem_scratch_begin(); + const char** extensions = mem_arena_alloc(scratch.arena, info->indexedEntryCount); + + //NOTE: we will hold this until program terminates + info->indexedEntries = malloc_array(orca_gl_getstring_entry, info->indexedEntryCount); + + for(int i=0; iindexedEntryCount; i++) + { + extensions[i] = (const char*)glGetStringi(GL_EXTENSIONS, i); + if(extensions[i]) + { + info->indexedEntries[i].len = strlen(extensions[i])+1; + totalSize += info->indexedEntries[i].len; + } + } + + u32 wasmIndex = orca_mem_grow(totalSize); + + for(int i=0; ientries[i].offset = wasmIndex; + memcpy(memory + wasmIndex, strings[i], info->entries[i].len); + + wasmIndex += info->entries[i].len; + } + } + + for(int i=0; iindexedEntryCount; i++) + { + if(extensions[i]) + { + info->indexedEntries[i].offset = wasmIndex; + memcpy(memory + wasmIndex, extensions[i], info->indexedEntries[i].len); + wasmIndex += info->indexedEntries[i].len; + } + } + + mem_scratch_end(scratch); + + info->init = true; +} + +const void* glGetString_stub(IM3Runtime runtime, IM3ImportContext _ctx, uint64_t* _sp, void* _mem) +{ + if(!__orcaGLGetStringInfo.init) + { + uint32_t memorySize = 0; + char* memory = (char*)m3_GetMemory(runtime, &memorySize, 0); + orca_gl_getstring_init(&__orcaGLGetStringInfo, memory); + } + + GLenum name = (GLenum)*(i32*)&_sp[1]; + *(u32*)&_sp[0] = 0; + + for(int i=0; isurface = mg_surface_create_for_window(data->window, data->api); + + //NOTE: this will be called on main thread, so we need to deselect the surface here, + // and reselect it on the orca thread + mg_surface_deselect(); + + return(0); +} + +mg_surface orca_surface_canvas(void) +{ + orca_surface_create_data data = { + .surface = mg_surface_nil(), + .window = __orcaApp.window, + .api = MG_CANVAS + }; + + mp_dispatch_on_main_thread_sync(__orcaApp.window, orca_surface_callback, (void*)&data); + mg_surface_prepare(data.surface); + return(data.surface); +} + +mg_surface orca_surface_gles(void) +{ + orca_surface_create_data data = { + .surface = mg_surface_nil(), + .window = __orcaApp.window, + .api = MG_GLES + }; + + mp_dispatch_on_main_thread_sync(__orcaApp.window, orca_surface_callback, (void*)&data); + mg_surface_prepare(data.surface); + return(data.surface); } void orca_surface_render_commands(mg_surface surface, @@ -302,8 +380,25 @@ void orca_runtime_init(orca_runtime* runtime) #include"clock_api_bind_gen.c" #include"io_api_bind_gen.c" +#include"gles_api_bind_manual.c" #include"gles_api_bind_gen.c" -#include"manual_gles_api.c" + + +void orca_wasm3_abort(IM3Runtime runtime, M3Result res, const char* file, const char* function, int line, const char* msg) +{ + M3ErrorInfo errInfo = {0}; + m3_GetErrorInfo(runtime, &errInfo); + if(errInfo.message && res == errInfo.result) + { + orca_abort_fmt(file, function, line, "%s: %s (%s)", msg, res, errInfo.message); + } + else + { + orca_abort_fmt(file, function, line, "%s: %s", msg, res); + } +} + +#define ORCA_WASM3_ABORT(runtime, err, msg) orca_wasm3_abort(runtime, err, __FILE__, __FUNCTION__, __LINE__, msg) i32 orca_runloop(void* user) { @@ -318,16 +413,7 @@ i32 orca_runloop(void* user) FILE* file = fopen(modulePath.ptr, "rb"); if(!file) { - log_error("Couldn't load wasm module at %s\n", modulePath.ptr); - - const char* options[] = {"OK"}; - mp_alert_popup("Error", - "The application couldn't load: web assembly module not found", - 1, - options); - - mp_request_quit(); - return(-1); + ORCA_ABORT("The application couldn't load: web assembly module not found"); } fseek(file, 0, SEEK_END); @@ -343,41 +429,44 @@ i32 orca_runloop(void* user) app->runtime.m3Env = m3_NewEnvironment(); app->runtime.m3Runtime = m3_NewRuntime(app->runtime.m3Env, stackSize, NULL); - m3_RuntimeSetMemoryCallbacks(app->runtime.m3Runtime, wasm_memory_resize_callback, wasm_memory_free_callback, &app->runtime.wasmMemory); //NOTE: host memory will be freed when runtime is freed. + m3_RuntimeSetMemoryCallbacks(app->runtime.m3Runtime, wasm_memory_resize_callback, wasm_memory_free_callback, &app->runtime.wasmMemory); - //TODO check errors - m3_ParseModule(app->runtime.m3Env, &app->runtime.m3Module, (u8*)app->runtime.wasmBytecode.ptr, app->runtime.wasmBytecode.len); - m3_LoadModule(app->runtime.m3Runtime, app->runtime.m3Module); + M3Result res = m3_ParseModule(app->runtime.m3Env, &app->runtime.m3Module, (u8*)app->runtime.wasmBytecode.ptr, app->runtime.wasmBytecode.len); + if(res) + { + ORCA_WASM3_ABORT(app->runtime.m3Runtime, res, "The application couldn't parse its web assembly module"); + } + + res = m3_LoadModule(app->runtime.m3Runtime, app->runtime.m3Module); + if(res) + { + ORCA_WASM3_ABORT(app->runtime.m3Runtime, res, "The application couldn't load its web assembly module into the runtime"); + } m3_SetModuleName(app->runtime.m3Module, bundleNameCString); mem_arena_clear(mem_scratch()); //NOTE: bind orca APIs - bindgen_link_core_api(app->runtime.m3Module); - bindgen_link_canvas_api(app->runtime.m3Module); - bindgen_link_clock_api(app->runtime.m3Module); - bindgen_link_io_api(app->runtime.m3Module); - bindgen_link_gles_api(app->runtime.m3Module); - manual_link_gles_api(app->runtime.m3Module); + { + int err = 0; + err |= bindgen_link_core_api(app->runtime.m3Module); + err |= bindgen_link_canvas_api(app->runtime.m3Module); + err |= bindgen_link_clock_api(app->runtime.m3Module); + err |= bindgen_link_io_api(app->runtime.m3Module); + err |= bindgen_link_gles_api(app->runtime.m3Module); + err |= manual_link_gles_api(app->runtime.m3Module); + if(err) + { + ORCA_ABORT("The application couldn't link one or more functions to its web assembly module (see console log for more information)"); + } + } //NOTE: compile - M3Result res = m3_CompileModule(app->runtime.m3Module); + res = m3_CompileModule(app->runtime.m3Module); if(res) { - M3ErrorInfo errInfo = {0}; - m3_GetErrorInfo(app->runtime.m3Runtime, &errInfo); - - log_error("wasm error: %s\n", errInfo.message); - - const char* options[] = {"OK"}; - mp_alert_popup("Error", - "The application couldn't load: can't compile web assembly module", - 1, - options); - - mp_request_quit(); - return(-1); + ORCA_WASM3_ABORT(app->runtime.m3Runtime, res, "The application couldn't compile its web assembly module"); } //NOTE: Find and type check event handlers. @@ -451,28 +540,15 @@ i32 orca_runloop(void* user) app->rootDir = cmp.handle; } - //NOTE: prepare GL surface - mg_surface_prepare(app->surface); - IM3Function* exports = app->runtime.exports; //NOTE: call init handler if(exports[G_EXPORT_ON_INIT]) { - M3Result err = m3_Call(exports[G_EXPORT_ON_INIT], 0, 0); - if(err != NULL) + M3Result res = m3_Call(exports[G_EXPORT_ON_INIT], 0, 0); + if(res) { - log_error("runtime error: %s\n", err); - - str8 msg = str8_pushf(mem_scratch(), "Runtime error: %s\n", err); - const char* options[] = {"OK"}; - mp_alert_popup("Error", - msg.ptr, - 1, - options); - - mp_request_quit(); - return(-1); + ORCA_WASM3_ABORT(app->runtime.m3Runtime, res, "Runtime error"); } } @@ -482,7 +558,11 @@ i32 orca_runloop(void* user) u32 width = (u32)content.w; u32 height = (u32)content.h; const void* args[2] = {&width, &height}; - m3_Call(exports[G_EXPORT_FRAME_RESIZE], 2, args); + M3Result res = m3_Call(exports[G_EXPORT_FRAME_RESIZE], 2, args); + if(res) + { + ORCA_WASM3_ABORT(app->runtime.m3Runtime, res, "Runtime error"); + } } ui_set_context(&app->debugOverlay.ui); @@ -505,7 +585,11 @@ i32 orca_runloop(void* user) memcpy(eventPtr, event, sizeof(*event)); const void* args[1] = {&app->runtime.rawEventOffset}; - m3_Call(exports[G_EXPORT_RAW_EVENT], 1, args); + M3Result res = m3_Call(exports[G_EXPORT_RAW_EVENT], 1, args); + if(res) + { + ORCA_WASM3_ABORT(app->runtime.m3Runtime, res, "Runtime error"); + } #else log_error("OnRawEvent() is not supported on big endian platforms"); #endif @@ -527,7 +611,11 @@ i32 orca_runloop(void* user) u32 width = (u32)event->move.content.w; u32 height = (u32)event->move.content.h; const void* args[2] = {&width, &height}; - m3_Call(exports[G_EXPORT_FRAME_RESIZE], 2, args); + M3Result res = m3_Call(exports[G_EXPORT_FRAME_RESIZE], 2, args); + if(res) + { + ORCA_WASM3_ABORT(app->runtime.m3Runtime, res, "Runtime error"); + } } } break; @@ -539,7 +627,11 @@ i32 orca_runloop(void* user) { int key = event->key.code; const void* args[1] = {&key}; - m3_Call(exports[G_EXPORT_MOUSE_DOWN], 1, args); + M3Result res = m3_Call(exports[G_EXPORT_MOUSE_DOWN], 1, args); + if(res) + { + ORCA_WASM3_ABORT(app->runtime.m3Runtime, res, "Runtime error"); + } } } else @@ -548,7 +640,11 @@ i32 orca_runloop(void* user) { int key = event->key.code; const void* args[1] = {&key}; - m3_Call(exports[G_EXPORT_MOUSE_UP], 1, args); + M3Result res = m3_Call(exports[G_EXPORT_MOUSE_UP], 1, args); + if(res) + { + ORCA_WASM3_ABORT(app->runtime.m3Runtime, res, "Runtime error"); + } } } } break; @@ -558,7 +654,11 @@ i32 orca_runloop(void* user) if(exports[G_EXPORT_MOUSE_MOVE]) { const void* args[4] = {&event->mouse.x, &event->mouse.y, &event->mouse.deltaX, &event->mouse.deltaY}; - m3_Call(exports[G_EXPORT_MOUSE_MOVE], 4, args); + M3Result res = m3_Call(exports[G_EXPORT_MOUSE_MOVE], 4, args); + if(res) + { + ORCA_WASM3_ABORT(app->runtime.m3Runtime, res, "Runtime error"); + } } } break; @@ -576,7 +676,11 @@ i32 orca_runloop(void* user) if(exports[G_EXPORT_KEY_DOWN]) { const void* args[1] = {&event->key.code}; - m3_Call(exports[G_EXPORT_KEY_DOWN], 1, args); + M3Result res = m3_Call(exports[G_EXPORT_KEY_DOWN], 1, args); + if(res) + { + ORCA_WASM3_ABORT(app->runtime.m3Runtime, res, "Runtime error"); + } } } else if(event->key.action == MP_KEY_RELEASE) @@ -584,7 +688,11 @@ i32 orca_runloop(void* user) if(exports[G_EXPORT_KEY_UP]) { const void* args[1] = {&event->key.code}; - m3_Call(exports[G_EXPORT_KEY_UP], 1, args); + M3Result res = m3_Call(exports[G_EXPORT_KEY_UP], 1, args); + if(res) + { + ORCA_WASM3_ABORT(app->runtime.m3Runtime, res, "Runtime error"); + } } } } break; @@ -732,8 +840,11 @@ i32 orca_runloop(void* user) if(exports[G_EXPORT_FRAME_REFRESH]) { - mg_surface_prepare(app->surface); - m3_Call(exports[G_EXPORT_FRAME_REFRESH], 0, 0); + M3Result res = m3_Call(exports[G_EXPORT_FRAME_REFRESH], 0, 0); + if(res) + { + ORCA_WASM3_ABORT(app->runtime.m3Runtime, res, "Runtime error"); + } } if(app->debugOverlay.show) @@ -761,10 +872,6 @@ int main(int argc, char** argv) mp_rect windowRect = {.x = 100, .y = 100, .w = 810, .h = 610}; app->window = mp_window_create(windowRect, "orca", 0); - app->surface = mg_surface_create_for_window(app->window, MG_CANVAS); - app->canvas = mg_canvas_create(); - mg_surface_swap_interval(app->surface, 1); - app->debugOverlay.show = false; app->debugOverlay.surface = mg_surface_create_for_window(app->window, MG_CANVAS); app->debugOverlay.canvas = mg_canvas_create(); @@ -784,11 +891,6 @@ int main(int argc, char** argv) for(int i=0; i<3; i++) { - mg_surface_prepare(app->surface); - mg_canvas_set_current(app->canvas); - mg_render(app->surface, app->canvas); - mg_surface_present(app->surface); - mg_surface_prepare(app->debugOverlay.surface); mg_canvas_set_current(app->debugOverlay.canvas); mg_render(app->debugOverlay.surface, app->debugOverlay.canvas); @@ -812,9 +914,6 @@ int main(int argc, char** argv) mp_thread_join(runloopThread, NULL); - mg_canvas_destroy(app->canvas); - mg_surface_destroy(app->surface); - mg_canvas_destroy(app->debugOverlay.canvas); mg_surface_destroy(app->debugOverlay.surface); diff --git a/src/manual_gles_api.c b/src/manual_gles_api.c deleted file mode 100644 index e7a52ce..0000000 --- a/src/manual_gles_api.c +++ /dev/null @@ -1,29 +0,0 @@ - -const void* glShaderSource_stub(IM3Runtime runtime, IM3ImportContext _ctx, uint64_t * _sp, void * _mem) -{ - i32 shader = *(i32*)&_sp[0]; - i32 count = *(i32*)&_sp[1]; - i32 stringArrayOffset = *(i32*)&_sp[2]; - i32 lengthArrayOffset = *(i32*)&_sp[3]; - - int* stringOffsetArray = (int*)((char*)_mem + stringArrayOffset); - const char** stringArray = (const char**)mem_arena_alloc_array(mem_scratch(), char*, count); - for(int i=0; i