Powervessel/window/window.odin

335 lines
10 KiB
Odin
Raw Permalink Normal View History

package window
import "base:runtime"
import "core:strings"
import "core:log"
import "core:time"
import "vendor:glfw"
import "vendor:wgpu"
import "vendor:wgpu/glfwglue"
Config :: struct {
title: string,
width, height: int,
resizable: bool,
clear_color: wgpu.Color,
}
g_window: glfw.WindowHandle
g_wgpu_surface: wgpu.Surface
g_wgpu_queue: wgpu.Queue
g_wgpu_device: wgpu.Device
g_clear_color: wgpu.Color
// CURRENT LIMITATION: Only one window can be created per application
init :: proc(win_config: Config) -> bool {
using win_config
title_cstr, err := strings.clone_to_cstring(title, context.temp_allocator)
if err != nil {
log.errorf("Failed to translate title string to cstring")
return false
}
if glfw.Init() == false {
log.errorf("Failed to init GLFW")
return false
}
glfw.WindowHint(glfw.CLIENT_API, glfw.NO_API)
if !resizable {
glfw.WindowHint(glfw.RESIZABLE, glfw.FALSE)
}
g_window = glfw.CreateWindow(i32(width), i32(height), title_cstr, nil, nil)
if g_window == nil {
log.errorf("Failed to create GLFW window")
return false
}
inst_desc := wgpu.InstanceDescriptor{
nextInChain = nil
}
when ODIN_ARCH == .wasm32 {
instance := wgpu.CreateInstance(nil)
} else {
instance := wgpu.CreateInstance(&inst_desc)
}
if instance == nil {
log.errorf("Failed to initialize WebGPU instance")
return false
}
log.debugf("WebGPU Instance: {0}", instance)
g_wgpu_surface = glfwglue.GetSurface(instance, g_window)
log.debugf("Requesting WebGPU Adapter...")
adapter_opts: wgpu.RequestAdapterOptions
adapter_opts.nextInChain = nil
adapter_opts.compatibleSurface = g_wgpu_surface
adapter := request_adapter_sync(instance, &adapter_opts)
log.debugf("Got WebGPU Adapter: {0}", adapter)
when ODIN_DEBUG {
inspect_adapter(adapter)
}
log.debugf("Requesting WebGPU device...")
device_desc := wgpu.DeviceDescriptor{
nextInChain = nil,
label = "Powervessel Engine WebGPU Device",
requiredFeatureCount = 0,
requiredLimits = nil,
defaultQueue = {
nextInChain = nil,
label = "Powervessel Engine Default Queue"
},
deviceLostCallback = proc "cdecl" (reason: wgpu.DeviceLostReason, msg: cstring, raw_user_data: rawptr) {
context = runtime.default_context()
log.debugf("Device lost: reason {0}", reason)
if msg != "" do log.debugf(" ({0})", msg)
},
}
g_wgpu_device = request_device_sync(adapter, &device_desc)
log.debugf("Got WebGPU device: {0}", g_wgpu_device)
on_device_error :: proc "cdecl" (type: wgpu.ErrorType, msg: cstring, raw_user_data: rawptr) {
context = runtime.default_context()
log.debugf("Uncaptured device error: type {0}", type)
if msg != "" do log.debugf(" ({0})", msg)
}
wgpu.DeviceSetUncapturedErrorCallback(g_wgpu_device, on_device_error, nil)
when ODIN_DEBUG {
inspect_device(g_wgpu_device)
}
g_wgpu_queue = wgpu.DeviceGetQueue(g_wgpu_device)
wgpu.QueueOnSubmittedWorkDone(g_wgpu_queue,
proc "cdecl" (status: wgpu.QueueWorkDoneStatus, raw_user_data: rawptr) {
context = runtime.default_context()
log.debugf("Queued work finished with status: {0}", status)
})
surface_format := wgpu.SurfaceGetPreferredFormat(g_wgpu_surface, adapter)
surface_config := wgpu.SurfaceConfiguration{
nextInChain = nil,
width = u32(width),
height = u32(height),
format = surface_format,
viewFormatCount = 0,
viewFormats = nil,
usage = {.RenderAttachment},
device = g_wgpu_device,
presentMode = .Fifo,
alphaMode = .Auto,
}
wgpu.SurfaceConfigure(g_wgpu_surface, &surface_config)
wgpu.AdapterRelease(adapter)
g_clear_color = clear_color
return true
}
update :: proc() -> bool {
if glfw.WindowShouldClose(g_window) do return false
glfw.PollEvents()
rtv := get_render_target_view()
if rtv == nil {
log.fatalf("Failed to get render target view")
return false
}
encoder_desc := wgpu.CommandEncoderDescriptor{
nextInChain = nil,
label = "Powervessel Engine Command Encoder"
}
encoder := wgpu.DeviceCreateCommandEncoder(g_wgpu_device, &encoder_desc)
color_attachment := wgpu.RenderPassColorAttachment{
view = rtv,
resolveTarget = nil,
loadOp = .Clear,
storeOp = .Store,
clearValue = g_clear_color
}
render_pass_desc := wgpu.RenderPassDescriptor{
nextInChain = nil,
colorAttachmentCount = 1,
colorAttachments = &color_attachment,
depthStencilAttachment = nil,
timestampWrites = nil,
}
render_pass := wgpu.CommandEncoderBeginRenderPass(encoder, &render_pass_desc)
wgpu.RenderPassEncoderEnd(render_pass)
wgpu.RenderPassEncoderRelease(render_pass)
buffer_desc := wgpu.CommandBufferDescriptor{
nextInChain = nil,
label = "Powervessel Engine Command Buffer"
}
buffer := wgpu.CommandEncoderFinish(encoder, &buffer_desc)
wgpu.CommandEncoderRelease(encoder)
wgpu.QueueSubmit(g_wgpu_queue, {buffer})
wgpu.CommandBufferRelease(buffer)
wgpu.TextureViewRelease(rtv)
when ODIN_ARCH != .wasm32 {
wgpu.SurfacePresent(g_wgpu_surface)
}
return true
}
shutdown :: proc() {
glfw.DestroyWindow(g_window)
glfw.Terminate()
}
@(private)
request_adapter_sync :: proc(instance: wgpu.Instance, opts: ^wgpu.RequestAdapterOptions) -> wgpu.Adapter {
User_Data :: struct {
adapter: wgpu.Adapter,
request_ended: bool,
odin_ctx: runtime.Context
}
user_data: User_Data
user_data.odin_ctx = context
on_adapter_request_ended :: proc "cdecl" (status: wgpu.RequestAdapterStatus, adapter: wgpu.Adapter, msg: cstring, raw_user_data: rawptr) {
user_data := cast(^User_Data)(raw_user_data)
context = user_data.odin_ctx
if status == .Success {
user_data.adapter = adapter
} else {
log.errorf("Failed to get WebGPU adapter: {0}", msg)
}
user_data.request_ended = true
}
wgpu.InstanceRequestAdapter(instance, opts, on_adapter_request_ended, rawptr(&user_data))
for !user_data.request_ended {
time.sleep(100 * time.Millisecond)
}
assert(user_data.request_ended)
return user_data.adapter
}
@(private)
request_device_sync :: proc(adapter: wgpu.Adapter, desc: ^wgpu.DeviceDescriptor) -> wgpu.Device {
User_Data :: struct {
device: wgpu.Device,
request_ended: bool,
odin_ctx: runtime.Context
}
user_data: User_Data
user_data.odin_ctx = context
on_device_request_end :: proc "cdecl" (status: wgpu.RequestDeviceStatus, device: wgpu.Device, msg: cstring, raw_user_data: rawptr) {
user_data := cast(^User_Data)(raw_user_data)
context = user_data.odin_ctx
if status == .Success {
user_data.device = device
} else {
log.errorf("Failed to get WebGPU device: {0}", msg)
}
user_data.request_ended = true
}
wgpu.AdapterRequestDevice(adapter, desc, on_device_request_end, rawptr(&user_data))
for !user_data.request_ended {
time.sleep(100 * time.Millisecond)
}
assert(user_data.request_ended)
return user_data.device
}
@(private)
inspect_adapter :: proc(adapter: wgpu.Adapter) {
when ODIN_ARCH != .wasm32 {
limits, limits_ok := wgpu.AdapterGetLimits(adapter)
if limits_ok {
log.debugf("Adapter limits:")
log.debugf(" - maxTextureDimension1D: {0}", limits.limits.maxTextureDimension1D)
log.debugf(" - maxTextureDimension2D: {0}", limits.limits.maxTextureDimension2D)
log.debugf(" - maxTextureDimension3D: {0}", limits.limits.maxTextureDimension3D)
log.debugf(" - maxTextureArrayLayers: {0}", limits.limits.maxTextureArrayLayers)
}
}
features := wgpu.AdapterEnumerateFeatures(adapter, context.temp_allocator)
log.debugf("WebGPU Adapter Features:")
for f in features {
log.debugf(" - {0}", f)
}
properties := wgpu.AdapterGetProperties(adapter)
log.debugf("WebGPU Adapter Properties:")
log.debugf(" - vendorID: {0}", properties.vendorID)
log.debugf(" - vendorName: {0}", properties.vendorName)
log.debugf(" - architecture: {0}", properties.architecture)
log.debugf(" - deviceID: {0}", properties.deviceID)
log.debugf(" - name: {0}", properties.name)
log.debugf(" - driverDescription: {0}", properties.driverDescription)
log.debugf(" - adapterType: {0}", properties.adapterType)
log.debugf(" - backendType: {0}", properties.backendType)
}
@(private)
inspect_device :: proc(device: wgpu.Device) {
features := wgpu.DeviceEnumerateFeatures(device, context.temp_allocator)
log.debugf("WebGPU Device Features:")
for f in features {
log.debugf(" - {0}", f)
}
limits, limits_ok := wgpu.DeviceGetLimits(device)
if limits_ok {
log.debugf("Device limits:")
log.debugf(" - maxTextureDimension1D: {0}", limits.limits.maxTextureDimension1D)
log.debugf(" - maxTextureDimension2D: {0}", limits.limits.maxTextureDimension2D)
log.debugf(" - maxTextureDimension3D: {0}", limits.limits.maxTextureDimension3D)
log.debugf(" - maxTextureArrayLayers: {0}", limits.limits.maxTextureArrayLayers)
}
}
@(private)
get_render_target_view :: proc() -> wgpu.TextureView {
surface_tex := wgpu.SurfaceGetCurrentTexture(g_wgpu_surface)
if surface_tex.status != .Success {
log.errorf("Failed to get surface texture: {0}", surface_tex.status)
return nil
}
view_desc := wgpu.TextureViewDescriptor{
nextInChain = nil,
label = "Powervessel Engine Surface Texture View",
format = wgpu.TextureGetFormat(surface_tex.texture),
dimension = ._2D,
baseMipLevel = 0,
mipLevelCount = 1,
baseArrayLayer = 0,
arrayLayerCount = 1,
aspect = .All,
}
return wgpu.TextureCreateView(surface_tex.texture, &view_desc)
}