Compare commits

..
5 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
Riyyi 2b8488a1e2 Add mouse position input function 2026-09-13 00:43:18 +02:00
Riyyi 7740632541 Free wgpu capabilities memory 2026-09-13 00:23:56 +02:00
9 changed files with 140 additions and 18 deletions
+52
View File
@@ -0,0 +1,52 @@
$ErrorActionPreference = "Stop"
$PROJECT = "sindri"
$COMMIT = git rev-parse --short HEAD
$VERSION = "dev-$(Get-Date -Format 'yyyy-MM-dd')-$COMMIT"
$OPTION = $args[0]
if ($args) { $args = $args[1..($args.Length - 1)] } else { $args = @() }
# ------------------------------------------
# Game compile
New-Item -ItemType Directory -Force -Path build | Out-Null
# Game.dll
odin build game/ -show-timings `
-collection:sindri=src `
-build-mode:dynamic `
-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.
if (-not (Test-Path "build/game_tmp.dll")) {
Write-Error "error: build produced no game lib.dll"
exit 1
}
Move-Item -Force "build/game_tmp.dll" "build/game.dll"
# ------------------------------------------
# Engine compile
# If the executable is already running, then don't try to build and start it.
$proc = Get-Process -Name $PROJECT -ErrorAction SilentlyContinue
if ($proc) {
Write-Output "Hot reloading..."
exit 0
}
if ($OPTION -eq "debug") {
odin build src/ -show-timings `
-collection:sindri=src `
-collection:gram=vendor/gram/src `
-collection:wgpu=vendor `
-out:build/$PROJECT -microarch:native -use-separate-modules "-define:VERSION=$VERSION-debug" -debug @args
exit 0
}
odin build src/ -show-timings `
-collection:sindri=src `
-collection:gram=vendor/gram/src `
-collection:wgpu=vendor `
-out:build/$PROJECT -microarch:native -o:speed "-define:VERSION=$VERSION" @args
+22
View File
@@ -0,0 +1,22 @@
# Odin
This document contains specifics about the workings of the Odin programming
language, dynamic linking and state management design patterns. These are
tested and validated findings, for continued reference by me.
## Dynamic Library Memory
When a program loads in a dynamic library, what happens with package globals?
Findings (macOS):
- 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
The clanker states this is due to "flat-namespace symbol interposition".
Linux "ELF has symbol preemption: when resolving references, ld.so searches
globals scope in order — executable first, then loaded shared objects".
Windows PE has no interposition at all, every module (exe and each DLL) has its
own symbol table.
+14 -6
View File
@@ -5,11 +5,12 @@ import "core:time"
import "core:fmt"
import "sindri:core"
import "sindri:test"
// -----------------------------------------
Game_Memory :: struct {
should_close: bool
should_close: bool,
}
g: ^Game_Memory
@@ -54,6 +55,8 @@ settings :: proc() -> core.Settings {
@(export)
init_once :: proc() {
fmt.println("init once")
test.test_proc = proc() {}
}
@(export)
@@ -61,6 +64,9 @@ init :: proc() {
fmt.println("hello from .dll!")
g = new(Game_Memory)
memory_set(g)
test.test_proc = proc() { asd := 2 }
}
@(export)
@@ -70,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)
@@ -83,19 +93,17 @@ 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
}
@(export)
force_reload :: proc() -> bool {
// TODO: read keypress
return false
return input.key_state(.Key_F5) == .Press
}
@(export)
force_restart :: proc() -> bool {
// TODO: read keypress
return false
return input.key_state(.Key_F6) == .Press
}
+20
View File
@@ -5,9 +5,11 @@ import "core:fmt"
import "sindri:input"
// -----------------------------------------
// Types
// event category, bitfield (?)
// Event_Data :: union {
Event :: union {
Window_Close_Event,
Window_Resize_Event,
@@ -22,6 +24,11 @@ Event :: union {
Mouse_Scroll_Event,
}
// Event :: struct {
// data: Event_Data,
// handled: bool,
// }
Window_Close_Event :: struct {
handled: bool,
}
@@ -85,6 +92,13 @@ Mouse_Scroll_Event :: struct {
}
// -----------------------------------------
// Variables
// I want to have a list of Listeners per Event type, but this is state
// listeners: map[typeid][dynamic]Listener
// -----------------------------------------
// Public functions
// Dispatcher(Event) -> forwards to the right function
on_event :: proc(event: Event) {
@@ -116,3 +130,9 @@ on_event :: proc(event: Event) {
fmt.println("Joy dis:", e.id)
}
}
/*
Notes:
Have the events queued up into a frame buffer that gets drained at a specific time.
*/
+4
View File
@@ -11,5 +11,9 @@ key_state: proc(key: Key) -> Action
// implementation in platform package, to prevent cyclic dependency
mouse_button_state: proc(button: Mouse_Button) -> Action
// Returns the position of the mouse cursor
// implementation in platform package, to prevent cyclic dependency
mouse_position: proc() -> (x_pos: f32, y_pos: f32)
// -----------------------------------------
// Private functions
+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)))
+7 -5
View File
@@ -52,11 +52,7 @@ os_init :: proc(settings: core.Settings) {
// Register input functions
input.key_state = key_state
input.mouse_button_state = mouse_button_state
// TODO: Figure out proper vsync, found 3 spots so far
// - glfw.SwapInterval(0) this is only for OpenGL it seems?
// - glfw.SetWindowMonitor(refresh)
// - wgpu presentMode = .Fifo
input.mouse_position = mouse_position
}
os_destroy :: proc() {
@@ -157,6 +153,12 @@ mouse_button_state :: proc(button: input.Mouse_Button) -> input.Action {
return input_action(glfw.GetMouseButton(state.os.window, i32(button)))
}
@(private = "file")
mouse_position :: proc() -> (x_pos: f32, y_pos: f32) {
x_pos_f64, y_pos_64 := glfw.GetCursorPos(state.os.window)
return f32(x_pos_f64), f32(y_pos_64)
}
@(private = "file")
input_action :: proc(action: i32) -> input.Action {
if action == glfw.RELEASE do return input.Action.Release
+12 -7
View File
@@ -26,7 +26,7 @@ state: struct {
pipeline_layout: wgpu.PipelineLayout,
pipeline: wgpu.RenderPipeline,
// ----------------------------------------
present_modes: []wgpu.PresentMode,
capabilities: wgpu.SurfaceCapabilities,
vsync: bool,
}
@@ -69,6 +69,7 @@ instance_destroy :: proc() {
wgpu.ShaderModuleRelease(state.module)
wgpu.QueueRelease(state.queue)
wgpu.DeviceRelease(state.device)
wgpu.SurfaceCapabilitiesFreeMembers(state.capabilities)
wgpu.AdapterRelease(state.adapter)
wgpu.SurfaceRelease(state.surface)
wgpu.InstanceRelease(state.instance)
@@ -260,25 +261,29 @@ get_present_modes :: proc() {
if status != .Success {
fmt.panicf("[wgpu] surface capabilities failure: [%v]", status)
}
state.present_modes = capabilities.presentModes[:capabilities.presentModeCount]
fmt.println("[wgpu] supported present modes", state.present_modes)
state.capabilities = capabilities
present_modes := capabilities.presentModes[:capabilities.presentModeCount]
fmt.println("[wgpu] supported present modes", present_modes)
}
@(private = "file")
pick_present_mode :: proc(vsync: bool) -> wgpu.PresentMode {
if len(state.present_modes) == 0 do return .Fifo
present_modes := state.capabilities.presentModes[:state.capabilities.presentModeCount]
if len(present_modes) == 0 do return .Fifo
// Mimmick wgpu auto Vsync behavior
// https://docs.rs/wgpu/latest/wgpu/enum.PresentMode.html
if vsync {
if slice.contains(state.present_modes, wgpu.PresentMode.FifoRelaxed) {
if slice.contains(present_modes, wgpu.PresentMode.FifoRelaxed) {
return .FifoRelaxed // Adaptive Vsync
}
} else {
if slice.contains(state.present_modes, wgpu.PresentMode.Immediate) {
if slice.contains(present_modes, wgpu.PresentMode.Immediate) {
return .Immediate // Vsync Off
}
if slice.contains(state.present_modes, wgpu.PresentMode.Mailbox) {
if slice.contains(present_modes, wgpu.PresentMode.Mailbox) {
return .Mailbox // Fast Vsync
}
}
+5
View File
@@ -0,0 +1,5 @@
package test
test: int = 0
test_proc: proc()