From bbaf7a0e7b8f9d8b3e0a7eb10f00ce955055fa29 Mon Sep 17 00:00:00 2001 From: Riyyi Date: Sat, 29 Aug 2026 23:57:06 +0200 Subject: [PATCH] Add wgpu download script --- .DS_Store | Bin 6148 -> 0 bytes .gitignore | 8 ++ README.md | 8 ++ build.sh | 12 ++- ols.json | 8 +- scripts/wgpu-init.sh | 77 ++++++++++++++++ src/main.odin | 213 ++++++++++++++++++++++++++++++++++++++++++- src/os_glfw.odin | 60 ++++++++++++ vendor/.DS_Store | Bin 6148 -> 0 bytes vendor/gram | 2 +- 10 files changed, 379 insertions(+), 9 deletions(-) delete mode 100644 .DS_Store create mode 100644 .gitignore create mode 100755 scripts/wgpu-init.sh create mode 100644 src/os_glfw.odin delete mode 100644 vendor/.DS_Store diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index e29c8ce56b745f9deedc1c4df883d9ddd57fc0f0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeH~O-{o=427R*1(n!z$#Sm14MtUaf?fbMVSyA8lmP4OdhI^{v?>ZntXLph^1Rez z&nVv_t^vsAqkRO{09JHW9CewS?yJx2CL#-}*coql$JmZdcj#x?-v_kz4kH>2c)?YD z-)X^uTLT(DvhR2cZt;m{yrM;q;k==_X-OanB!MK51d_ln2xvE@t?pfOO#(?E30w&1 z_o2{LYv|-^pAIfI0#KI>oAKFZ32M;*wT4cvjL^hOsa`5EVu+VBULvoCPOe@KiQz-y zW{C+!>~@}CEFDr^b4>zC;41;0y=iOh|2z5*^S|b#C4nUHPYK9!yWej3a?x97uc^JZ u(4XjMW3Huhu~tmAR?HvTiXX1>iav9{hEA?l&bXCR^N)aXNlOBMLEr?dV;_b9 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d61eaf3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +# Files + +.DS_Store + +# Directories + +build +vendor/wgpu diff --git a/README.md b/README.md index 3363b28..2143afd 100644 --- a/README.md +++ b/README.md @@ -27,3 +27,11 @@ Make sure to enable git hooks: ```sh git config core.hooksPath scripts ``` + +vendor +- wgpu +- glfw +- lua +- miniaudio +- microui (temp) +- box3d? diff --git a/build.sh b/build.sh index 31ca658..261ea04 100755 --- a/build.sh +++ b/build.sh @@ -4,14 +4,22 @@ set -e PROJECT="sindri" VERSION="dev-$(date -u '+%Y-%m-%d')-$(git rev-parse --short HEAD)" +WGPU_VERSION="v29.0.1.1" + +# ------------------------------------------ + +# Setup compiled wgpu binary +./scripts/wgpu-init.sh "$WGPU_VERSION" + +# ------------------------------------------ mkdir -p build if [ "$1" = "debug" ]; then shift - odin build src/ -show-timings -collection:src=src -out:build/$PROJECT -microarch:native -use-separate-modules -define:VERSION="$VERSION-debug" -debug "$@" + odin build src/ -show-timings -collection:sindri=src -collection:gram=vendor/gram/src -out:build/$PROJECT -microarch:native -use-separate-modules -define:VERSION="$VERSION-debug" -debug "$@" exit 0 fi -odin build src/ -show-timings -collection:src=src -out:build/$PROJECT -microarch:native -o:speed -define:VERSION="$VERSION" "$@" +odin build src/ -show-timings -collection:sindri=src -collection:gram=vendor/gram/src -out:build/$PROJECT -microarch:native -o:speed -define:VERSION="$VERSION" "$@" diff --git a/ols.json b/ols.json index 3b36b1e..d99275a 100644 --- a/ols.json +++ b/ols.json @@ -1,8 +1,6 @@ { "collections": [ - { - "name": "src", - "path": "src" - } + { "name": "sindri", "path": "src" }, + { "name": "gram", "path": "vendor/gram/src" } ] -} \ No newline at end of file +} diff --git a/scripts/wgpu-init.sh b/scripts/wgpu-init.sh new file mode 100755 index 0000000..cb2d33e --- /dev/null +++ b/scripts/wgpu-init.sh @@ -0,0 +1,77 @@ +#!/bin/sh + +WGPU_VERSION="$1" + +if [ "$1" = "" ]; then + echo "error: WGPU_VERSION was not set" + exit 1 +fi + +SCRIPT_DIR=$(CDPATH='' cd -- "$(dirname -- "$0")" && pwd) +VENDOR_DIR="$SCRIPT_DIR/../vendor" + +# ------------------------------------------ +# Copy vendor/wgpu + +# Check if wgpu was copied already +if [ -d "$VENDOR_DIR/wgpu" ]; then + exit 0 +fi + +# Copy wgpu +wgpu_path="$(odin root)/vendor/wgpu" +cp -r "$wgpu_path" "$VENDOR_DIR" +chmod +w "$VENDOR_DIR/wgpu/lib" + +# ------------------------------------------ +# Download + +ASSET_BASE="https://github.com/gfx-rs/wgpu-native/releases/download/${WGPU_VERSION}" + +# Platform detection +case "$(uname -s)" in + Darwin) + os="macos" + ;; + Linux) + os="linux" + ;; + MINGW* | MSYS* | CYGWIN*) + os="windows" + extra="-msvc" + ;; + *) + echo "wgpu setup: unsupported OS '$(uname -s)'" >&2 + exit 1 + ;; +esac + +# Arch detection +case "$(uname -m)" in + arm64 | aarch64) + arch="aarch64" + ;; + x86_64 | amd64) + arch="x86_64" + ;; + *) + echo "wgpu setup: unsupported arch '$(uname -m)'" >&2 + exit 1 + ;; +esac + +# Download filename +name="wgpu-${os}-${arch}${extra:-}-release" + +# Compiler filename: lib/wgpu-macos-aarch64-release/lib/libwgpu_native.a +target="$VENDOR_DIR/wgpu/lib/$name" + +# Download +if [ ! -d "$target" ]; then + url="${ASSET_BASE}/${name}.zip" + echo "wgpu setup: downloading $url" + tmp=$(mktemp -d) + trap 'rm -rf "$tmp"' EXIT + curl -fL --retry 3 -o "$tmp/wgpu.zip" "$url" + unzip -o -q "$tmp/wgpu.zip" -d "$VENDOR_DIR/wgpu/lib/$name" +fi diff --git a/src/main.odin b/src/main.odin index db97920..848a2a7 100644 --- a/src/main.odin +++ b/src/main.odin @@ -1,11 +1,222 @@ -package main +package sindri +import "base:runtime" import "core:fmt" +import "vendor:wgpu" + +import "gram:chunks" VERSION :: #config(VERSION, "dev") // ----------------------------------------- +state: struct { + ctx: runtime.Context, + os: OS, + instance: wgpu.Instance, + surface: wgpu.Surface, + adapter: wgpu.Adapter, + device: wgpu.Device, + config: wgpu.SurfaceConfiguration, + queue: wgpu.Queue, + module: wgpu.ShaderModule, + pipeline_layout: wgpu.PipelineLayout, + pipeline: wgpu.RenderPipeline, +} + +// ----------------------------------------- + main :: proc() { fmt.println("hello world!") + + state.ctx = context + + os_init() + + state.instance = wgpu.CreateInstance(nil) + if state.instance == nil { + panic("WebGPU is not supported") + } + state.surface = os_get_surface(state.instance) + + wgpu.InstanceRequestAdapter( + state.instance, + &{compatibleSurface = state.surface}, + {callback = on_adapter}, + ) + + on_adapter :: proc "c" ( + status: wgpu.RequestAdapterStatus, + adapter: wgpu.Adapter, + message: string, + userdata1: rawptr, + userdata2: rawptr, + ) { + context = state.ctx + if status != .Success || adapter == nil { + fmt.panicf("request adapter failure: [%v] %s", status, message) + } + state.adapter = adapter + wgpu.AdapterRequestDevice(adapter, nil, {callback = on_device}) + } + + on_device :: proc "c" ( + status: wgpu.RequestDeviceStatus, + device: wgpu.Device, + message: string, + userdata1: rawptr, + userdata2: rawptr, + ) { + context = state.ctx + if status != .Success || device == nil { + fmt.panicf("request device failure: [%v] %s", status, message) + } + state.device = device + + width, height := os_get_framebuffer_size() + + state.config = wgpu.SurfaceConfiguration { + device = state.device, + usage = {.RenderAttachment}, + format = .BGRA8Unorm, + width = width, + height = height, + presentMode = .Fifo, + alphaMode = .Opaque, + } + wgpu.SurfaceConfigure(state.surface, &state.config) + + state.queue = wgpu.DeviceGetQueue(state.device) + + shader :: ` + @vertex + fn vs_main(@builtin(vertex_index) in_vertex_index: u32) -> @builtin(position) vec4 { + let x = f32(i32(in_vertex_index) - 1); + let y = f32(i32(in_vertex_index & 1u) * 2 - 1); + return vec4(x, y, 0.0, 1.0); + } + + @fragment + fn fs_main() -> @location(0) vec4 { + return vec4(1.0, 0.0, 0.0, 1.0); + }` + + state.module = wgpu.DeviceCreateShaderModule( + state.device, + &{ + nextInChain = &wgpu.ShaderSourceWGSL { + sType = .ShaderSourceWGSL, + code = shader, + }, + }, + ) + + state.pipeline_layout = wgpu.DeviceCreatePipelineLayout( + state.device, + &{}, + ) + state.pipeline = wgpu.DeviceCreateRenderPipeline( + state.device, + &{ + layout = state.pipeline_layout, + vertex = {module = state.module, entryPoint = "vs_main"}, + fragment = &{ + module = state.module, + entryPoint = "fs_main", + targetCount = 1, + targets = &wgpu.ColorTargetState { + format = .BGRA8Unorm, + writeMask = wgpu.ColorWriteMaskFlags_All, + }, + }, + primitive = {topology = .TriangleList}, + multisample = {count = 1, mask = 0xFFFFFFFF}, + }, + ) + + os_run() + } +} + +resize :: proc "c" () { + context = state.ctx + + state.config.width, state.config.height = os_get_framebuffer_size() + wgpu.SurfaceConfigure(state.surface, &state.config) +} + +frame :: proc "c" (dt: f32) { + context = state.ctx + + surface_texture := wgpu.SurfaceGetCurrentTexture(state.surface) + switch surface_texture.status { + case .SuccessOptimal, .SuccessSuboptimal: + // All good, could handle suboptimal here. + case .Timeout, .Outdated, .Lost: + // Skip this frame, and re-configure surface. + if surface_texture.texture != nil { + wgpu.TextureRelease(surface_texture.texture) + } + resize() + return + case .Occluded: + // Window is occluded (e.g. minimized), skip this frame. + return + case .Error: + // Fatal error + fmt.panicf( + "[triangle] get_current_texture status=%v", + surface_texture.status, + ) + } + defer wgpu.TextureRelease(surface_texture.texture) + + frame := wgpu.TextureCreateView(surface_texture.texture, nil) + defer wgpu.TextureViewRelease(frame) + + command_encoder := wgpu.DeviceCreateCommandEncoder(state.device, nil) + defer wgpu.CommandEncoderRelease(command_encoder) + + render_pass_encoder := wgpu.CommandEncoderBeginRenderPass( + command_encoder, + &{ + colorAttachmentCount = 1, + colorAttachments = &wgpu.RenderPassColorAttachment { + view = frame, + loadOp = .Clear, + storeOp = .Store, + depthSlice = wgpu.DEPTH_SLICE_UNDEFINED, + clearValue = {0, 1, 0, 1}, + }, + }, + ) + + wgpu.RenderPassEncoderSetPipeline(render_pass_encoder, state.pipeline) + wgpu.RenderPassEncoderDraw( + render_pass_encoder, + vertexCount = 3, + instanceCount = 1, + firstVertex = 0, + firstInstance = 0, + ) + + wgpu.RenderPassEncoderEnd(render_pass_encoder) + wgpu.RenderPassEncoderRelease(render_pass_encoder) + + command_buffer := wgpu.CommandEncoderFinish(command_encoder, nil) + defer wgpu.CommandBufferRelease(command_buffer) + + wgpu.QueueSubmit(state.queue, {command_buffer}) + wgpu.SurfacePresent(state.surface) +} + +finish :: proc() { + wgpu.RenderPipelineRelease(state.pipeline) + wgpu.PipelineLayoutRelease(state.pipeline_layout) + wgpu.ShaderModuleRelease(state.module) + wgpu.QueueRelease(state.queue) + wgpu.DeviceRelease(state.device) + wgpu.AdapterRelease(state.adapter) + wgpu.SurfaceRelease(state.surface) + wgpu.InstanceRelease(state.instance) } diff --git a/src/os_glfw.odin b/src/os_glfw.odin new file mode 100644 index 0000000..4c959f6 --- /dev/null +++ b/src/os_glfw.odin @@ -0,0 +1,60 @@ +package sindri + +import "core:time" + +import "vendor:glfw" +import "vendor:wgpu" +import "vendor:wgpu/glfwglue" + +OS :: struct { + window: glfw.WindowHandle, +} + +os_init :: proc() { + if !glfw.Init() { + panic("[glfw] init failure") + } + + glfw.WindowHint(glfw.CLIENT_API, glfw.NO_API) + state.os.window = glfw.CreateWindow( + 960, + 540, + "WGPU Native Triangle", + nil, + nil, + ) + + glfw.SetFramebufferSizeCallback(state.os.window, size_callback) +} + +os_run :: proc() { + dt: f32 + + for !glfw.WindowShouldClose(state.os.window) { + start := time.tick_now() + + glfw.PollEvents() + frame(dt) + + dt = f32(time.duration_seconds(time.tick_since(start))) + } + + finish() + + glfw.DestroyWindow(state.os.window) + glfw.Terminate() +} + +os_get_framebuffer_size :: proc() -> (width, height: u32) { + iw, ih := glfw.GetFramebufferSize(state.os.window) + return u32(iw), u32(ih) +} + +os_get_surface :: proc(instance: wgpu.Instance) -> wgpu.Surface { + return glfwglue.GetSurface(instance, state.os.window) +} + +@(private = "file") +size_callback :: proc "c" (window: glfw.WindowHandle, width, height: i32) { + resize() +} diff --git a/vendor/.DS_Store b/vendor/.DS_Store deleted file mode 100644 index 5cf16d142dba5dfd57cc0bf63315aeb2cacf7fd7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeH~F>b>!3`IXv4*{}x?5L#&$PI)bIYBOvwg$SS2#_GVj-F47OWn?aQG5dA6Dbq6 z|6rK_Y%%#8VfFI@4C@9Fk={$ccLbDoBkOER*roceB=k-%oeck9}T+Z<0CxD3`#jo@*?iXK>HQ72@q3K5;WKfU- HPgURna?}#6 diff --git a/vendor/gram b/vendor/gram index 0219d56..1d492cb 160000 --- a/vendor/gram +++ b/vendor/gram @@ -1 +1 @@ -Subproject commit 0219d56dc20946653588ec5b91d371805c023336 +Subproject commit 1d492cb0d7c45a5790c243da46ec19de23711094