diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d9000ab..567ed09 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -49,6 +49,52 @@ jobs: - name: (Zig) Build Shaders run: zig build shaders + - name: (Zig) Running Test + if: runner.os != 'Windows' + run: zig build test -DzigCC + - name: (Zig) Build Native + run: zig build -Dimgui --summary all + # - name: (Zig + emsdk) Build Wasm + # run: zig build -Dimgui -DzigCC --summary all -Dtarget=wasm32-emscripten-none -Doptimize=ReleaseSmall + + opend: + strategy: + fail-fast: false + matrix: + runs-on: [ubuntu-latest, macos-latest] + runs-on: ${{ matrix.runs-on }} + steps: + - uses: actions/checkout@v4 + - uses: mlugg/setup-zig@v1 + with: + version: master + - name: Install opend + run: | + if [ "$RUNNER_OS" == "Linux" ]; then + if [ "$RUNNER_ARCH" == "X64" ]; then + curl -sLO https://github.com/opendlang/opend/releases/download/CI/opend-latest-linux-x86_64.tar.xz + tar -xf opend-latest-linux-x86_64.tar.xz + echo "$PWD/opend-latest-linux-x86_64/bin" >> $GITHUB_PATH + $PWD/opend-latest-linux-x86_64/bin/opend install xpack-emscripten + fi + elif [ "$RUNNER_OS" == "macOS" ]; then + if [ "$RUNNER_ARCH" == "ARM64" ]; then + curl -sLO https://github.com/opendlang/opend/releases/download/CI/opend-latest-osx-arm64.tar.xz + tar -xf opend-latest-osx-arm64.tar.xz + echo "$PWD/opend-latest-osx-arm64/bin" >> $GITHUB_PATH + $PWD/opend-latest-osx-arm64/bin/opend install xpack-emscripten + fi + fi + + - name: prepare-linux + if: runner.os == 'Linux' + run: | + sudo apt-get update + sudo apt-get install libglu1-mesa-dev mesa-common-dev xorg-dev libasound-dev + + - name: (Zig) Build Shaders + run: zig build shaders + - name: (Zig) Running Test if: runner.os != 'Windows' run: zig build test -DzigCC diff --git a/build.zig b/build.zig index d6d144a..74a1673 100644 --- a/build.zig +++ b/build.zig @@ -274,7 +274,8 @@ pub fn build(b: *Build) !void { .betterC = if (std.mem.eql(u8, example, "user-data")) false else enable_betterC, .dflags = &.{ "-w", - "-preview=all", + "-preview=rvaluerefparam", + "-preview=dip1000", }, // fixme: https://github.com/kassane/sokol-d/issues/1 - betterC works on darwin .zig_cc = if (target.result.isDarwin() and !enable_betterC) false else enable_zigcc, @@ -456,6 +457,7 @@ pub fn ldcBuildStep(b: *Build, options: DCompileStep) !*std.Build.Step.InstallDi \\ \\ extern (C): \\ + \\ version(D_BetterC): \\ version (Emscripten) \\ { \\ union fpos_t @@ -653,8 +655,9 @@ pub fn ldcBuildStep(b: *Build, options: DCompileStep) !*std.Build.Step.InstallDi .emsdk = options.emsdk.?, .use_webgpu = backend == .wgpu, .use_webgl2 = backend != .wgpu, - .use_emmalloc = true, + .use_emmalloc = options.betterC, .use_filesystem = false, + .use_drt = !options.betterC and options.target.result.isWasm(), .use_ubsan = options.artifact.?.root_module.sanitize_c orelse false, .release_use_lto = options.artifact.?.want_lto orelse false, .shell_file_path = b.path("src/sokol/web/shell.html"), @@ -816,6 +819,7 @@ pub const EmLinkOptions = struct { use_emmalloc: bool = false, use_filesystem: bool = true, use_ubsan: bool = false, + use_drt: bool = false, shell_file_path: ?Build.LazyPath, extra_args: []const []const u8 = &.{}, }; @@ -866,6 +870,11 @@ pub fn emLinkStep(b: *Build, options: EmLinkOptions) !*Build.Step.InstallDir { emcc.addArg(arg); } + if (options.use_drt) { + const xpack = b.dependency("xpack", .{}).path("lib").getPath(b); + emcc.addFileArg(path(b, b.fmt("{s}/libdruntime-ldc.a", .{xpack}))); + emcc.addFileArg(path(b, b.fmt("{s}/libphobos2-ldc.a", .{xpack}))); + } // add the main lib, and then scan for library dependencies and add those too emcc.addArtifactArg(options.lib_main); for (options.lib_main.getCompileDependencies(false)) |item| { diff --git a/build.zig.zon b/build.zig.zon index 617402b..55838ab 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -23,5 +23,9 @@ .url = "git+https://github.com/floooh/sokol-tools-bin#d80b1d8f20fef813092ba37f26723d3880839651", .hash = "1220f2ae16952843dd25b6bc86b888a3fd8c3f25d34adddfd8d1d2b9f801429e80e4", }, + .xpack = .{ + .url = "https://github.com/opendlang/opend/releases/download/CI/opend-latest-xpack-emscripten.tar.xz", + .hash = "1220132c994d2f9754174c5fe934413f3fc79603d1a180429bb231aaed6110429cc6", + }, }, } diff --git a/src/examples/cube.d b/src/examples/cube.d index afea8c9..6ba2243 100644 --- a/src/examples/cube.d +++ b/src/examples/cube.d @@ -17,142 +17,160 @@ import shd = examples.shaders.cube; extern (C): @safe: -struct State +interface ICubeRenderer { - float rx = 0; - float ry = 0; - - sg.Pipeline pip; - sg.Bindings bind; - sg.PassAction passAction = { - colors: [ - { - load_action: sg.LoadAction.Clear, - clear_value: {r: 0.25, g: 0.5, b: 0.75, a: 1.0} - } - ] - }; + void init(); + void frame(); + void cleanup(); +} - Mat4 view() +class CubeRenderer : ICubeRenderer +{ + private struct State + { + float rx = 0; + float ry = 0; + sg.Pipeline pip; + sg.Bindings bind; + sg.PassAction passAction = { + colors: [ + { + load_action: sg.LoadAction.Clear, + clear_value: {r: 0.25, g: 0.5, b: 0.75, a: 1.0} + } + ] + }; + } + + private State state; + + private Mat4 view() { return Mat4.lookAt(Vec3(0.0, 1.5, 6.0), Vec3.zero(), Vec3.up()); } -} -static State state; + private Mat4 computeMvp(float rx, float ry) + { + immutable proj = Mat4.perspective(60.0, app.widthf() / app.heightf(), 0.01, 10.0); + immutable rxm = Mat4.rotate(rx, Vec3(1.0, 0.0, 0.0)); + immutable rym = Mat4.rotate(ry, Vec3(0.0, 1.0, 0.0)); + immutable model = Mat4.mul(rxm, rym); + immutable view_proj = Mat4.mul(proj, view()); + return Mat4.mul(view_proj, model); + } -void init() -{ - sg.Desc gfxd = {environment: sglue.environment, - logger: {func: &log.func}}; - sg.setup(gfxd); - - float[168] vertices = [ - -1.0, -1.0, -1.0, 1.0, 0.0, 0.0, 1.0, - 1.0, -1.0, -1.0, 1.0, 0.0, 0.0, 1.0, - 1.0, 1.0, -1.0, 1.0, 0.0, 0.0, 1.0, - -1.0, 1.0, -1.0, 1.0, 0.0, 0.0, 1.0, - - -1.0, -1.0, 1.0, 0.0, 1.0, 0.0, 1.0, - 1.0, -1.0, 1.0, 0.0, 1.0, 0.0, 1.0, - 1.0, 1.0, 1.0, 0.0, 1.0, 0.0, 1.0, - -1.0, 1.0, 1.0, 0.0, 1.0, 0.0, 1.0, - - -1.0, -1.0, -1.0, 0.0, 0.0, 1.0, 1.0, - -1.0, 1.0, -1.0, 0.0, 0.0, 1.0, 1.0, - -1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 1.0, - -1.0, -1.0, 1.0, 0.0, 0.0, 1.0, 1.0, - - 1.0, -1.0, -1.0, 1.0, 0.5, 0.0, 1.0, - 1.0, 1.0, -1.0, 1.0, 0.5, 0.0, 1.0, - 1.0, 1.0, 1.0, 1.0, 0.5, 0.0, 1.0, - 1.0, -1.0, 1.0, 1.0, 0.5, 0.0, 1.0, - - -1.0, -1.0, -1.0, 0.0, 0.5, 1.0, 1.0, - -1.0, -1.0, 1.0, 0.0, 0.5, 1.0, 1.0, - 1.0, -1.0, 1.0, 0.0, 0.5, 1.0, 1.0, - 1.0, -1.0, -1.0, 0.0, 0.5, 1.0, 1.0, - - -1.0, 1.0, -1.0, 1.0, 0.0, 0.5, 1.0, - -1.0, 1.0, 1.0, 1.0, 0.0, 0.5, 1.0, - 1.0, 1.0, 1.0, 1.0, 0.0, 0.5, 1.0, - 1.0, 1.0, -1.0, 1.0, 0.0, 0.5, 1.0 - ]; - sg.BufferDesc vbufd = {data: {ptr: vertices.ptr, size: vertices.sizeof},}; - state.bind.vertex_buffers[0] = sg.makeBuffer(vbufd); - - ushort[36] indices = [ - 0, 1, 2, 0, 2, 3, - 6, 5, 4, 7, 6, 4, - 8, 9, 10, 8, 10, 11, - 14, 13, 12, 15, 14, 12, - 16, 17, 18, 16, 18, 19, - 22, 21, 20, 23, 22, 20, - ]; - sg.BufferDesc ibufd = { - type: sg.BufferType.Indexbuffer, - data: {ptr: indices.ptr, size: indices.sizeof}, - }; - state.bind.index_buffer = sg.makeBuffer(ibufd); - - sg.PipelineDesc pld = { - layout: { - attrs: [ - shd.ATTR_CUBE_POSITION: {format: sg.VertexFormat.Float3}, - shd.ATTR_CUBE_COLOR0: {format: sg.VertexFormat.Float4}, - ], - }, - shader: sg.makeShader(shd.cubeShaderDesc(sg.queryBackend())), - index_type: sg.IndexType.Uint16, - cull_mode: sg.CullMode.Back, - depth: {write_enabled: true, - compare: sg.CompareFunc.Less_equal}, - }; - state.pip = sg.makePipeline(pld); -} + void init() + { + sg.Desc gfxd = { + environment: sglue.environment, + logger: {func: &log.func} + }; + sg.setup(gfxd); + + float[168] vertices = [ + -1.0, -1.0, -1.0, 1.0, 0.0, 0.0, 1.0, + 1.0, -1.0, -1.0, 1.0, 0.0, 0.0, 1.0, + 1.0, 1.0, -1.0, 1.0, 0.0, 0.0, 1.0, + -1.0, 1.0, -1.0, 1.0, 0.0, 0.0, 1.0, + + -1.0, -1.0, 1.0, 0.0, 1.0, 0.0, 1.0, + 1.0, -1.0, 1.0, 0.0, 1.0, 0.0, 1.0, + 1.0, 1.0, 1.0, 0.0, 1.0, 0.0, 1.0, + -1.0, 1.0, 1.0, 0.0, 1.0, 0.0, 1.0, + + -1.0, -1.0, -1.0, 0.0, 0.0, 1.0, 1.0, + -1.0, 1.0, -1.0, 0.0, 0.0, 1.0, 1.0, + -1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 1.0, + -1.0, -1.0, 1.0, 0.0, 0.0, 1.0, 1.0, + + 1.0, -1.0, -1.0, 1.0, 0.5, 0.0, 1.0, + 1.0, 1.0, -1.0, 1.0, 0.5, 0.0, 1.0, + 1.0, 1.0, 1.0, 1.0, 0.5, 0.0, 1.0, + 1.0, -1.0, 1.0, 1.0, 0.5, 0.0, 1.0, + + -1.0, -1.0, -1.0, 0.0, 0.5, 1.0, 1.0, + -1.0, -1.0, 1.0, 0.0, 0.5, 1.0, 1.0, + 1.0, -1.0, 1.0, 0.0, 0.5, 1.0, 1.0, + 1.0, -1.0, -1.0, 0.0, 0.5, 1.0, 1.0, + + -1.0, 1.0, -1.0, 1.0, 0.0, 0.5, 1.0, + -1.0, 1.0, 1.0, 1.0, 0.0, 0.5, 1.0, + 1.0, 1.0, 1.0, 1.0, 0.0, 0.5, 1.0, + 1.0, 1.0, -1.0, 1.0, 0.0, 0.5, 1.0 + ]; + sg.BufferDesc vbufd = { + data: {ptr: vertices.ptr, size: vertices.sizeof}, + }; + state.bind.vertex_buffers[0] = sg.makeBuffer(vbufd); + + ushort[36] indices = [ + 0, 1, 2, 0, 2, 3, + 6, 5, 4, 7, 6, 4, + 8, 9, 10, 8, 10, 11, + 14, 13, 12, 15, 14, 12, + 16, 17, 18, 16, 18, 19, + 22, 21, 20, 23, 22, 20, + ]; + sg.BufferDesc ibufd = { + type: sg.BufferType.Indexbuffer, + data: {ptr: indices.ptr, size: indices.sizeof}, + }; + state.bind.index_buffer = sg.makeBuffer(ibufd); + + sg.PipelineDesc pld = { + layout: { + attrs: [ + shd.ATTR_CUBE_POSITION: {format: sg.VertexFormat.Float3}, + shd.ATTR_CUBE_COLOR0: {format: sg.VertexFormat.Float4}, + ], + }, + shader: sg.makeShader(shd.cubeShaderDesc(sg.queryBackend())), + index_type: sg.IndexType.Uint16, + cull_mode: sg.CullMode.Back, + depth: {write_enabled: true, + compare: sg.CompareFunc.Less_equal}, + }; + state.pip = sg.makePipeline(pld); + } -void frame() -{ - immutable float t = cast(float)(app.frameDuration() * 60.0); - - state.rx += 1.0 * t; - state.ry += 2.0 * t; - - shd.VsParams vsParams = {mvp: computeMvp(state.rx, state.ry)}; - - sg.Pass pass = {action: state.passAction, swapchain: sglue.swapchain()}; - sg.beginPass(pass); - sg.applyPipeline(state.pip); - sg.applyBindings(state.bind); - sg.Range r = {ptr: &vsParams, size: vsParams.sizeof}; - sg.applyUniforms(shd.UB_VS_PARAMS, r); - sg.draw(0, 36, 1); - sg.endPass(); - sg.commit(); -} + void frame() @trusted + { + immutable float t = cast(float)(app.frameDuration() * 60.0); + + state.rx += 1.0 * t; + state.ry += 2.0 * t; + + shd.VsParams vsParams = {mvp: computeMvp(state.rx, state.ry)}; + + sg.Pass pass = {action: state.passAction, swapchain: sglue.swapchain()}; + sg.beginPass(pass); + sg.applyPipeline(state.pip); + sg.applyBindings(state.bind); + sg.Range r = {ptr: &vsParams, size: vsParams.sizeof}; + sg.applyUniforms(shd.UB_VS_PARAMS, r); + sg.draw(0, 36, 1); + sg.endPass(); + sg.commit(); + } -void cleanup() -{ - sg.shutdown(); + void cleanup() + { + sg.shutdown(); + } } -Mat4 computeMvp(float rx, float ry) -{ - immutable proj = Mat4.perspective(60.0, app.widthf() / app.heightf(), 0.01, 10.0); - immutable rxm = Mat4.rotate(rx, Vec3(1.0, 0.0, 0.0)); - immutable rym = Mat4.rotate(ry, Vec3(0.0, 1.0, 0.0)); - immutable model = Mat4.mul(rxm, rym); - immutable view_proj = Mat4.mul(proj, state.view()); - return Mat4.mul(view_proj, model); -} +private __gshared ICubeRenderer renderer; -void main() +void main() @system { + renderer = new CubeRenderer(); + app.Desc runner = { window_title: "cube.d", - init_cb: &init, - frame_cb: &frame, - cleanup_cb: &cleanup, + + init_cb: &initWrapper, + frame_cb: &frameWrapper, + cleanup_cb: &cleanupWrapper, width: 800, height: 600, sample_count: 4, @@ -161,3 +179,18 @@ void main() }; app.run(runner); } + +extern (C) private void initWrapper() @system +{ + renderer.init(); +} + +extern (C) private void frameWrapper() @system +{ + renderer.frame(); +} + +extern (C) private void cleanupWrapper() @system +{ + renderer.cleanup(); +} diff --git a/src/examples/mrt.d b/src/examples/mrt.d index 685c504..a6e018f 100644 --- a/src/examples/mrt.d +++ b/src/examples/mrt.d @@ -229,7 +229,7 @@ void init() state.dbg.bind.samplers[shd.SMP_SMP] = smp; } -void frame() +void frame() @trusted { immutable(float) dt = (app.frameDuration() * 60.0); state.rx += 1.0 * dt; diff --git a/src/examples/user_data.d b/src/examples/user_data.d index 5c90023..75580e7 100644 --- a/src/examples/user_data.d +++ b/src/examples/user_data.d @@ -29,9 +29,9 @@ void frame_userdata(scope void* userdata) @trusted auto state = cast(ExampleUserData*) userdata; state.data++; - version (WebAssembly) + version (D_BetterC) { - // TODO support + } else { @@ -43,18 +43,9 @@ void frame_userdata(scope void* userdata) @trusted { state.map.clear(); } - } - debug - { import std.stdio : writeln; - try - { - writeln(*state); - } - catch (Exception) - { - } + writeln(*state); } sg.Pass pass = {swapchain: sglue.swapchain};