Compare commits
7
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
be516d39a3 | ||
|
|
e7cc935035 | ||
|
|
416b42c27e | ||
|
|
2b8488a1e2 | ||
|
|
7740632541 | ||
|
|
5ef1dafed2 | ||
|
|
86ee8083b5 |
@@ -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
|
||||
@@ -6,6 +6,7 @@ PROJECT="sindri"
|
||||
VERSION="dev-$(date -u '+%Y-%m-%d')-$(git rev-parse --short HEAD)"
|
||||
|
||||
OPTION="${1:-}"
|
||||
shift
|
||||
|
||||
# ------------------------------------------
|
||||
|
||||
@@ -42,8 +43,6 @@ if pgrep -x $PROJECT >/dev/null; then
|
||||
fi
|
||||
|
||||
if [ "$OPTION" = "debug" ]; then
|
||||
shift
|
||||
|
||||
odin build src/ -show-timings \
|
||||
-collection:sindri=src \
|
||||
-collection:gram=vendor/gram/src \
|
||||
|
||||
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ Settings :: struct {
|
||||
height: u16,
|
||||
title: string,
|
||||
mode: WindowMode,
|
||||
refresh: u16,
|
||||
refresh: u16, // 0 = unlimited
|
||||
vsync: bool,
|
||||
// TODO:
|
||||
// windowed resizable y/n
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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
|
||||
|
||||
+5
-1
@@ -2,6 +2,7 @@ package sindri
|
||||
|
||||
import "core:fmt"
|
||||
import "core:time"
|
||||
import "sindri:test"
|
||||
|
||||
import "sindri:hot_reload"
|
||||
import "sindri:platform"
|
||||
@@ -26,7 +27,7 @@ main :: proc() {
|
||||
defer platform.os_destroy()
|
||||
|
||||
// Initialize GPU resources
|
||||
platform.instance_init()
|
||||
platform.instance_init(settings)
|
||||
defer platform.instance_destroy()
|
||||
|
||||
api.init_once()
|
||||
@@ -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)))
|
||||
|
||||
@@ -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() {
|
||||
@@ -106,8 +102,7 @@ os_set_monitor :: proc(settings: core.Settings) {
|
||||
)
|
||||
}
|
||||
|
||||
refresh := settings.vsync ? mode.refresh_rate : i32(settings.refresh)
|
||||
if refresh == 0 do refresh = glfw.DONT_CARE
|
||||
refresh := settings.refresh == 0 ? glfw.DONT_CARE : i32(settings.refresh)
|
||||
|
||||
glfw.SetWindowMonitor(
|
||||
state.os.window,
|
||||
@@ -158,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
|
||||
|
||||
+165
-92
@@ -2,15 +2,20 @@ package platform
|
||||
|
||||
import "base:runtime"
|
||||
import "core:fmt"
|
||||
import "core:slice"
|
||||
|
||||
import "wgpu:wgpu"
|
||||
|
||||
import "sindri:core"
|
||||
|
||||
// -----------------------------------------
|
||||
// Variables
|
||||
|
||||
// Instance -> Surface -> Adapter -> Device -> Queue
|
||||
state: struct {
|
||||
ctx: runtime.Context,
|
||||
os: OS,
|
||||
// ----------------------------------------
|
||||
instance: wgpu.Instance, // entry point: creates Adapter, Device and Surface
|
||||
surface: wgpu.Surface, // handle to a presentable surface (e.g. a window)
|
||||
adapter: wgpu.Adapter, // handle to physical GPU
|
||||
@@ -20,115 +25,42 @@ state: struct {
|
||||
module: wgpu.ShaderModule,
|
||||
pipeline_layout: wgpu.PipelineLayout,
|
||||
pipeline: wgpu.RenderPipeline,
|
||||
// ----------------------------------------
|
||||
capabilities: wgpu.SurfaceCapabilities,
|
||||
vsync: bool,
|
||||
}
|
||||
|
||||
// -----------------------------------------
|
||||
// Constructor/destructor
|
||||
|
||||
instance_init :: proc() {
|
||||
instance_init :: proc(settings: core.Settings) {
|
||||
state.ctx = context
|
||||
|
||||
wgpu.SetLogCallback(log_callback, nil)
|
||||
wgpu.SetLogLevel(.Warn)
|
||||
|
||||
// Instance
|
||||
state.instance = wgpu.CreateInstance(nil)
|
||||
if state.instance == nil {
|
||||
panic("WebGPU is not supported")
|
||||
panic("[wgpu] WebGPU is not supported")
|
||||
}
|
||||
|
||||
// Surface
|
||||
state.surface = os_get_surface(state.instance)
|
||||
|
||||
// Adapter
|
||||
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})
|
||||
}
|
||||
// Present modes
|
||||
get_present_modes()
|
||||
state.vsync = settings.vsync
|
||||
|
||||
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
|
||||
|
||||
state.queue = wgpu.DeviceGetQueue(state.device)
|
||||
|
||||
width, height := os_get_framebuffer_size()
|
||||
|
||||
state.config = wgpu.SurfaceConfiguration {
|
||||
device = state.device,
|
||||
usage = {.RenderAttachment},
|
||||
format = .BGRA8Unorm,
|
||||
width = width,
|
||||
height = height,
|
||||
// https://docs.rs/wgpu/latest/wgpu/enum.PresentMode.html
|
||||
presentMode = .Fifo, // .Fifo is essentially VSync
|
||||
alphaMode = .Opaque,
|
||||
}
|
||||
wgpu.SurfaceConfigure(state.surface, &state.config)
|
||||
|
||||
shader :: `
|
||||
@vertex
|
||||
fn vs_main(@builtin(vertex_index) in_vertex_index: u32) -> @builtin(position) vec4<f32> {
|
||||
let x = f32(i32(in_vertex_index) - 1);
|
||||
let y = f32(i32(in_vertex_index & 1u) * 2 - 1);
|
||||
return vec4<f32>(x, y, 0.0, 1.0);
|
||||
}
|
||||
|
||||
@fragment
|
||||
fn fs_main() -> @location(0) vec4<f32> {
|
||||
return vec4<f32>(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},
|
||||
},
|
||||
)
|
||||
}
|
||||
// Device
|
||||
wgpu.AdapterRequestDevice(state.adapter, nil, {callback = on_device})
|
||||
}
|
||||
|
||||
instance_destroy :: proc() {
|
||||
@@ -137,12 +69,23 @@ 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)
|
||||
}
|
||||
|
||||
// -----------------------------------------
|
||||
// Public functions
|
||||
|
||||
log_callback :: proc "c" (
|
||||
level: wgpu.LogLevel,
|
||||
message: string,
|
||||
userdata: rawptr,
|
||||
) {
|
||||
context = state.ctx
|
||||
fmt.eprintfln("[wgpu:%v] %v", level, message)
|
||||
}
|
||||
|
||||
resize :: proc "c" () {
|
||||
context = state.ctx
|
||||
@@ -173,7 +116,7 @@ frame :: proc "c" (dt: f32) {
|
||||
case .Error:
|
||||
// Fatal error
|
||||
fmt.panicf(
|
||||
"[triangle] get_current_texture status=%v",
|
||||
"[wgpu] triangle get_current_texture status=%v",
|
||||
surface_texture.status,
|
||||
)
|
||||
}
|
||||
@@ -217,3 +160,133 @@ frame :: proc "c" (dt: f32) {
|
||||
wgpu.QueueSubmit(state.queue, {command_buffer})
|
||||
wgpu.SurfacePresent(state.surface)
|
||||
}
|
||||
|
||||
// -----------------------------------------
|
||||
// Private functions
|
||||
|
||||
@(private = "file")
|
||||
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("[wgpu] request adapter failure: [%v] %s", status, message)
|
||||
}
|
||||
state.adapter = adapter
|
||||
}
|
||||
|
||||
@(private = "file")
|
||||
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("[wgpu] request device failure: [%v] %s", status, message)
|
||||
}
|
||||
state.device = device
|
||||
|
||||
state.queue = wgpu.DeviceGetQueue(state.device)
|
||||
|
||||
width, height := os_get_framebuffer_size()
|
||||
|
||||
state.config = wgpu.SurfaceConfiguration {
|
||||
device = state.device,
|
||||
usage = {.RenderAttachment},
|
||||
format = .BGRA8Unorm,
|
||||
width = width,
|
||||
height = height,
|
||||
presentMode = pick_present_mode(state.vsync),
|
||||
alphaMode = .Opaque,
|
||||
}
|
||||
wgpu.SurfaceConfigure(state.surface, &state.config)
|
||||
|
||||
shader :: `
|
||||
@vertex
|
||||
fn vs_main(@builtin(vertex_index) in_vertex_index: u32) -> @builtin(position) vec4<f32> {
|
||||
let x = f32(i32(in_vertex_index) - 1);
|
||||
let y = f32(i32(in_vertex_index & 1u) * 2 - 1);
|
||||
return vec4<f32>(x, y, 0.0, 1.0);
|
||||
}
|
||||
|
||||
@fragment
|
||||
fn fs_main() -> @location(0) vec4<f32> {
|
||||
return vec4<f32>(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},
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@(private = "file")
|
||||
get_present_modes :: proc() {
|
||||
capabilities, status := wgpu.SurfaceGetCapabilities(
|
||||
state.surface,
|
||||
state.adapter,
|
||||
)
|
||||
if status != .Success {
|
||||
fmt.panicf("[wgpu] surface capabilities failure: [%v]", status)
|
||||
}
|
||||
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 {
|
||||
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(present_modes, wgpu.PresentMode.FifoRelaxed) {
|
||||
return .FifoRelaxed // Adaptive Vsync
|
||||
}
|
||||
} else {
|
||||
if slice.contains(present_modes, wgpu.PresentMode.Immediate) {
|
||||
return .Immediate // Vsync Off
|
||||
}
|
||||
if slice.contains(present_modes, wgpu.PresentMode.Mailbox) {
|
||||
return .Mailbox // Fast Vsync
|
||||
}
|
||||
}
|
||||
|
||||
return .Fifo // Vsync On
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
package test
|
||||
|
||||
test: int = 0
|
||||
|
||||
test_proc: proc()
|
||||
Reference in New Issue
Block a user