Compare commits

..
3 Commits
Author SHA1 Message Date
Riyyi be516d39a3 Add Windows build script 2026-09-15 18:41:04 +02:00
Riyyi e7cc935035 lol 2026-09-14 23:00:24 +02:00
Riyyi 416b42c27e . 2026-09-14 19:25:33 +02:00
7 changed files with 28 additions and 172 deletions
+3 -8
View File
@@ -7,11 +7,6 @@ $VERSION = "dev-$(Get-Date -Format 'yyyy-MM-dd')-$COMMIT"
$OPTION = $args[0]
if ($args) { $args = $args[1..($args.Length - 1)] } else { $args = @() }
# ------------------------------------------
# Setup compiled wgpu binary
& (Join-Path $PSScriptRoot "scripts\wgpu-init.ps1")
# ------------------------------------------
# Game compile
@@ -21,7 +16,7 @@ New-Item -ItemType Directory -Force -Path build | Out-Null
odin build game/ -show-timings `
-collection:sindri=src `
-build-mode:dynamic `
-out:build/game_tmp.dll -microarch:native "-define:VERSION=$VERSION-debug" -debug @args
-out:build/game_tmp -microarch:native "-define:VERSION=$VERSION-debug" -debug @args
# Need to use a temp file on Windows because it first writes an empty file,
# which the engine will load before it is actually fully written.
@@ -46,7 +41,7 @@ if ($OPTION -eq "debug") {
-collection:sindri=src `
-collection:gram=vendor/gram/src `
-collection:wgpu=vendor `
-out:build/$PROJECT.exe -microarch:native -use-separate-modules "-define:VERSION=$VERSION-debug" -debug @args
-out:build/$PROJECT -microarch:native -use-separate-modules "-define:VERSION=$VERSION-debug" -debug @args
exit 0
}
@@ -54,4 +49,4 @@ odin build src/ -show-timings `
-collection:sindri=src `
-collection:gram=vendor/gram/src `
-collection:wgpu=vendor `
-out:build/$PROJECT.exe -microarch:native -o:speed "-define:VERSION=$VERSION" @args
-out:build/$PROJECT -microarch:native -o:speed "-define:VERSION=$VERSION" @args
+6 -48
View File
@@ -9,56 +9,14 @@ tested and validated findings, for continued reference by me.
When a program loads in a dynamic library, what happens with package globals?
Findings (macOS):
- Package globals are shared between modules, but only indirectly
- This is due to the dynamic linker merging symbols into a (single) flat namespace
- Practically this means the lib's unset package globals get shadowed by the hosts'
- The package globals are shared between the host and the lib
- After hot reloading the lib, the memory of the packge is not refreshed,
it keeps pointing to the package global of the host
Findings (Windows):
- Package globals are not shared between modules, they have their own copy
The clanker states this is due to "flat-namespace symbol interposition".
Findings (Linux):
- Package globals are not shared between modules, they have their own copy
Linux "ELF has symbol preemption: when resolving references, ld.so searches
globals scope in order — executable first, then loaded shared objects".
Conclusion: avoid package globals, or pass pointers to the dynamic library so
it can match its package globals with the host module.
## Dependency Patterns
1. Strict layering, only call downwards. If two modules need each other, one of
the modules is in a wrong layer. Move it, or extract the shared part into a
lower layer.
2. Dependency inversion: if required, define an interface in the lower layer,
upper layer registers itself.
3. Dependency Injection: Prevent dependencies via data, over includes. Pass what
a function needs as arguments instead of reaching another module's state.
4. Events instead of callbacks both ways If A and B would call each other,
have A push events into a queue and B consume them. Neither module includes
the other; they only share the event type (which lives in a lower layer).
5. Merge or split as a last resort, if two modules genuinely can't be untangled.
Sindri Engine:
```
+---------------------+ Game:
| Game | - Game
+---------------------+
|
v
+---------------------+ +---------------------+ Core:
| Core |<----+----| main | - Input
+---------------------+ | +---------------------+
| |
v |
+---------------------+ | Platform:
| Platform |<----+ - GLFW
+---------------------+ | - wgpu
| |
v |
+---------------------+ | Base:
| Base |<----+ - Hot Reload
+---------------------+ - Settings
- Event
- Keycodes
```
Windows PE has no interposition at all, every module (exe and each DLL) has its
own symbol table.
+10 -1
View File
@@ -5,6 +5,7 @@ import "core:time"
import "core:fmt"
import "sindri:core"
import "sindri:test"
// -----------------------------------------
@@ -54,6 +55,8 @@ settings :: proc() -> core.Settings {
@(export)
init_once :: proc() {
fmt.println("init once")
test.test_proc = proc() {}
}
@(export)
@@ -62,6 +65,8 @@ init :: proc() {
g = new(Game_Memory)
memory_set(g)
test.test_proc = proc() { asd := 2 }
}
@(export)
@@ -71,6 +76,10 @@ update :: proc(dt: f32) {
if input.key_state(.Key_Escape) == .Press {
g.should_close = true
}
fmt.println("GAME:", test.test)
fmt.printf("pointer: %p | %p\n", &test.test, &test.test_proc)
test.test += 1
}
@(export)
@@ -84,7 +93,7 @@ start := time.tick_now()
should_close :: proc() -> bool {
elapsed := time.tick_since(start)
seconds := time.duration_seconds(elapsed)
if seconds > 4 do return true
// if seconds > 4 do return true
return g.should_close
}
-100
View File
@@ -1,100 +0,0 @@
$ErrorActionPreference = "Stop"
$VENDOR_DIR = Join-Path $PSScriptRoot "..\vendor" | Resolve-Path
# ------------------------------------------
# Platform detection (target dir name, only used for the up-to-date check)
# Windows PowerShell 5.1 doesn't define $IsWindows/$IsMacOS/$IsLinux
if (-not (Test-Path variable:IsWindows)) { $IsWindows = $env:OS -eq "Windows_NT" }
if (-not (Test-Path variable:IsMacOS)) { $IsMacOS = $false }
if (-not (Test-Path variable:IsLinux)) { $IsLinux = $false }
if ($IsWindows) {
$os = "windows"
$extra = "-msvc"
} elseif ($IsMacOS) {
$os = "macos"
$extra = ""
} elseif ($IsLinux) {
$os = "linux"
$extra = ""
} else {
Write-Error "wgpu setup: unsupported OS"
exit 1
}
$procArch = [System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture
$arch = if ($procArch -eq [System.Runtime.InteropServices.Architecture]::Arm64) { "aarch64" } else { "x86_64" }
$odinRoot = (odin root).Trim()
$name = "wgpu-$os-$arch$extra-release"
$target = Join-Path $VENDOR_DIR "wgpu\lib\$name"
# ------------------------------------------
# WGPU version detection
if ($args.Count -gt 0 -and $args[0]) {
$WGPU_VERSION = $args[0]
} else {
$odinTypes = Join-Path $odinRoot "vendor\wgpu\wgpu_native_types.odin"
$typesContent = Get-Content $odinTypes -Raw
if ($typesContent -match 'BINDINGS_VERSION_STRING :: "([^"]*)"') {
$WGPU_VERSION = "v" + $Matches[1]
}
}
if (-not $WGPU_VERSION -or $WGPU_VERSION -eq "v") {
Write-Error "wgpu setup: could not detect the wgpu-native version"
exit 1
}
# Detect the version of the vendored wgpu bindings
$currentTypes = Join-Path $VENDOR_DIR "wgpu\wgpu_native_types.odin"
$current = $null
if (Test-Path $currentTypes) {
$cur = Get-Content $currentTypes -Raw
if ($cur -match 'BINDINGS_VERSION_STRING :: "([^"]*)"') {
$current = "v" + $Matches[1]
}
}
# Check if the vendored wgpu is already up to date
if ($current -eq $WGPU_VERSION -and (Test-Path $target)) {
exit 0
}
# ------------------------------------------
# Copy vendor/wgpu
# Remove any stale copy
if (Test-Path (Join-Path $VENDOR_DIR "wgpu")) {
Remove-Item -Recurse -Force (Join-Path $VENDOR_DIR "wgpu")
}
# Copy wgpu
$wgpuPath = Join-Path $odinRoot "vendor\wgpu"
Copy-Item -Recurse $wgpuPath $VENDOR_DIR
# Point imports at the copied vendor wgpu package
Get-ChildItem -Path (Join-Path $VENDOR_DIR "wgpu") -Recurse -Filter *.odin | ForEach-Object {
(Get-Content $_.FullName -Raw) -replace '"vendor:wgpu"', '"wgpu:wgpu"' | Set-Content $_.FullName -NoNewline
}
# ------------------------------------------
# Download
$assetBase = "https://github.com/gfx-rs/wgpu-native/releases/download/$WGPU_VERSION"
if (-not (Test-Path $target)) {
$url = "$assetBase/$name.zip"
Write-Output "wgpu setup: downloading $url"
$tmp = Join-Path ([System.IO.Path]::GetTempPath()) ([System.Guid]::NewGuid().ToString())
New-Item -ItemType Directory -Path $tmp | Out-Null
try {
Invoke-WebRequest -MaximumRedirection 5 -Uri $url -OutFile (Join-Path $tmp "wgpu.zip")
Expand-Archive -Force -Path (Join-Path $tmp "wgpu.zip") -DestinationPath $target
} finally {
Remove-Item -Recurse -Force $tmp
}
}
-15
View File
@@ -1,6 +1,5 @@
package hot_reload
import "core:c"
import "core:dynlib"
import "core:fmt"
import "core:os"
@@ -133,20 +132,6 @@ load_game_lib :: proc(api: ^Game_API) -> bool {
"error: failed initializing symbols: {0}",
dynlib.last_error(),
)
} else {
// Odin's shared libraries don't register a constructor, so the lib's
// runtime init (@init procs like os.stdout setup) never runs on load.
// Call it explicitly.
// Windows doesn't need this: its dynamic build exports DllMain, which
// the OS calls on load and which runs _startup_runtime itself.
when ODIN_OS != .Windows {
Entry :: #type proc "c" () -> c.int
entry_ptr, entry_ok := dynlib.symbol_address(
api.lib,
"_odin_entry_point",
)
if entry_ok do (transmute(Entry)entry_ptr)()
}
}
return ok
+4
View File
@@ -2,6 +2,7 @@ package sindri
import "core:fmt"
import "core:time"
import "sindri:test"
import "sindri:hot_reload"
import "sindri:platform"
@@ -41,6 +42,9 @@ main :: proc() {
platform.os_poll_events()
api.update(dt)
fmt.println("MAIN:", test.test)
fmt.printf("pointer: %p | %p\n", &test.test, &test.test_proc)
platform.frame(dt)
dt = f32(time.duration_seconds(time.tick_since(start)))
+5
View File
@@ -0,0 +1,5 @@
package test
test: int = 0
test_proc: proc()