Compare commits
4
Commits
f1028acb10
..
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2b8488a1e2 | ||
|
|
7740632541 | ||
|
|
5ef1dafed2 | ||
|
|
86ee8083b5 |
@@ -6,6 +6,7 @@ PROJECT="sindri"
|
|||||||
VERSION="dev-$(date -u '+%Y-%m-%d')-$(git rev-parse --short HEAD)"
|
VERSION="dev-$(date -u '+%Y-%m-%d')-$(git rev-parse --short HEAD)"
|
||||||
|
|
||||||
OPTION="${1:-}"
|
OPTION="${1:-}"
|
||||||
|
shift
|
||||||
|
|
||||||
# ------------------------------------------
|
# ------------------------------------------
|
||||||
|
|
||||||
@@ -42,8 +43,6 @@ if pgrep -x $PROJECT >/dev/null; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$OPTION" = "debug" ]; then
|
if [ "$OPTION" = "debug" ]; then
|
||||||
shift
|
|
||||||
|
|
||||||
odin build src/ -show-timings \
|
odin build src/ -show-timings \
|
||||||
-collection:sindri=src \
|
-collection:sindri=src \
|
||||||
-collection:gram=vendor/gram/src \
|
-collection:gram=vendor/gram/src \
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ Settings :: struct {
|
|||||||
height: u16,
|
height: u16,
|
||||||
title: string,
|
title: string,
|
||||||
mode: WindowMode,
|
mode: WindowMode,
|
||||||
refresh: u16,
|
refresh: u16, // 0 = unlimited
|
||||||
vsync: bool,
|
vsync: bool,
|
||||||
// TODO:
|
// TODO:
|
||||||
// windowed resizable y/n
|
// windowed resizable y/n
|
||||||
|
|||||||
@@ -11,5 +11,9 @@ key_state: proc(key: Key) -> Action
|
|||||||
// implementation in platform package, to prevent cyclic dependency
|
// implementation in platform package, to prevent cyclic dependency
|
||||||
mouse_button_state: proc(button: Mouse_Button) -> Action
|
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
|
// Private functions
|
||||||
|
|||||||
+1
-1
@@ -26,7 +26,7 @@ main :: proc() {
|
|||||||
defer platform.os_destroy()
|
defer platform.os_destroy()
|
||||||
|
|
||||||
// Initialize GPU resources
|
// Initialize GPU resources
|
||||||
platform.instance_init()
|
platform.instance_init(settings)
|
||||||
defer platform.instance_destroy()
|
defer platform.instance_destroy()
|
||||||
|
|
||||||
api.init_once()
|
api.init_once()
|
||||||
|
|||||||
@@ -52,11 +52,7 @@ os_init :: proc(settings: core.Settings) {
|
|||||||
// Register input functions
|
// Register input functions
|
||||||
input.key_state = key_state
|
input.key_state = key_state
|
||||||
input.mouse_button_state = mouse_button_state
|
input.mouse_button_state = mouse_button_state
|
||||||
|
input.mouse_position = mouse_position
|
||||||
// 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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
os_destroy :: proc() {
|
os_destroy :: proc() {
|
||||||
@@ -106,8 +102,7 @@ os_set_monitor :: proc(settings: core.Settings) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
refresh := settings.vsync ? mode.refresh_rate : i32(settings.refresh)
|
refresh := settings.refresh == 0 ? glfw.DONT_CARE : i32(settings.refresh)
|
||||||
if refresh == 0 do refresh = glfw.DONT_CARE
|
|
||||||
|
|
||||||
glfw.SetWindowMonitor(
|
glfw.SetWindowMonitor(
|
||||||
state.os.window,
|
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)))
|
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")
|
@(private = "file")
|
||||||
input_action :: proc(action: i32) -> input.Action {
|
input_action :: proc(action: i32) -> input.Action {
|
||||||
if action == glfw.RELEASE do return input.Action.Release
|
if action == glfw.RELEASE do return input.Action.Release
|
||||||
|
|||||||
+165
-92
@@ -2,15 +2,20 @@ package platform
|
|||||||
|
|
||||||
import "base:runtime"
|
import "base:runtime"
|
||||||
import "core:fmt"
|
import "core:fmt"
|
||||||
|
import "core:slice"
|
||||||
|
|
||||||
import "wgpu:wgpu"
|
import "wgpu:wgpu"
|
||||||
|
|
||||||
|
import "sindri:core"
|
||||||
|
|
||||||
// -----------------------------------------
|
// -----------------------------------------
|
||||||
|
// Variables
|
||||||
|
|
||||||
// Instance -> Surface -> Adapter -> Device -> Queue
|
// Instance -> Surface -> Adapter -> Device -> Queue
|
||||||
state: struct {
|
state: struct {
|
||||||
ctx: runtime.Context,
|
ctx: runtime.Context,
|
||||||
os: OS,
|
os: OS,
|
||||||
|
// ----------------------------------------
|
||||||
instance: wgpu.Instance, // entry point: creates Adapter, Device and Surface
|
instance: wgpu.Instance, // entry point: creates Adapter, Device and Surface
|
||||||
surface: wgpu.Surface, // handle to a presentable surface (e.g. a window)
|
surface: wgpu.Surface, // handle to a presentable surface (e.g. a window)
|
||||||
adapter: wgpu.Adapter, // handle to physical GPU
|
adapter: wgpu.Adapter, // handle to physical GPU
|
||||||
@@ -20,115 +25,42 @@ state: struct {
|
|||||||
module: wgpu.ShaderModule,
|
module: wgpu.ShaderModule,
|
||||||
pipeline_layout: wgpu.PipelineLayout,
|
pipeline_layout: wgpu.PipelineLayout,
|
||||||
pipeline: wgpu.RenderPipeline,
|
pipeline: wgpu.RenderPipeline,
|
||||||
|
// ----------------------------------------
|
||||||
|
capabilities: wgpu.SurfaceCapabilities,
|
||||||
|
vsync: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
// -----------------------------------------
|
// -----------------------------------------
|
||||||
|
// Constructor/destructor
|
||||||
|
|
||||||
instance_init :: proc() {
|
instance_init :: proc(settings: core.Settings) {
|
||||||
state.ctx = context
|
state.ctx = context
|
||||||
|
|
||||||
|
wgpu.SetLogCallback(log_callback, nil)
|
||||||
|
wgpu.SetLogLevel(.Warn)
|
||||||
|
|
||||||
|
// Instance
|
||||||
state.instance = wgpu.CreateInstance(nil)
|
state.instance = wgpu.CreateInstance(nil)
|
||||||
if state.instance == nil {
|
if state.instance == nil {
|
||||||
panic("WebGPU is not supported")
|
panic("[wgpu] WebGPU is not supported")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Surface
|
||||||
state.surface = os_get_surface(state.instance)
|
state.surface = os_get_surface(state.instance)
|
||||||
|
|
||||||
|
// Adapter
|
||||||
wgpu.InstanceRequestAdapter(
|
wgpu.InstanceRequestAdapter(
|
||||||
state.instance,
|
state.instance,
|
||||||
&{compatibleSurface = state.surface},
|
&{compatibleSurface = state.surface},
|
||||||
{callback = on_adapter},
|
{callback = on_adapter},
|
||||||
)
|
)
|
||||||
|
|
||||||
on_adapter :: proc "c" (
|
// Present modes
|
||||||
status: wgpu.RequestAdapterStatus,
|
get_present_modes()
|
||||||
adapter: wgpu.Adapter,
|
state.vsync = settings.vsync
|
||||||
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" (
|
// Device
|
||||||
status: wgpu.RequestDeviceStatus,
|
wgpu.AdapterRequestDevice(state.adapter, nil, {callback = on_device})
|
||||||
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},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
instance_destroy :: proc() {
|
instance_destroy :: proc() {
|
||||||
@@ -137,12 +69,23 @@ instance_destroy :: proc() {
|
|||||||
wgpu.ShaderModuleRelease(state.module)
|
wgpu.ShaderModuleRelease(state.module)
|
||||||
wgpu.QueueRelease(state.queue)
|
wgpu.QueueRelease(state.queue)
|
||||||
wgpu.DeviceRelease(state.device)
|
wgpu.DeviceRelease(state.device)
|
||||||
|
wgpu.SurfaceCapabilitiesFreeMembers(state.capabilities)
|
||||||
wgpu.AdapterRelease(state.adapter)
|
wgpu.AdapterRelease(state.adapter)
|
||||||
wgpu.SurfaceRelease(state.surface)
|
wgpu.SurfaceRelease(state.surface)
|
||||||
wgpu.InstanceRelease(state.instance)
|
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" () {
|
resize :: proc "c" () {
|
||||||
context = state.ctx
|
context = state.ctx
|
||||||
@@ -173,7 +116,7 @@ frame :: proc "c" (dt: f32) {
|
|||||||
case .Error:
|
case .Error:
|
||||||
// Fatal error
|
// Fatal error
|
||||||
fmt.panicf(
|
fmt.panicf(
|
||||||
"[triangle] get_current_texture status=%v",
|
"[wgpu] triangle get_current_texture status=%v",
|
||||||
surface_texture.status,
|
surface_texture.status,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -217,3 +160,133 @@ frame :: proc "c" (dt: f32) {
|
|||||||
wgpu.QueueSubmit(state.queue, {command_buffer})
|
wgpu.QueueSubmit(state.queue, {command_buffer})
|
||||||
wgpu.SurfacePresent(state.surface)
|
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
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user