218 lines
5.6 KiB
Odin
218 lines
5.6 KiB
Odin
package sindri
|
|
|
|
import "base:runtime"
|
|
import "core:fmt"
|
|
|
|
import "wgpu:wgpu"
|
|
|
|
// -----------------------------------------
|
|
|
|
// 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
|
|
device: wgpu.Device, // open connection to a GPU
|
|
queue: wgpu.Queue, // handle to command queue on a Device
|
|
config: wgpu.SurfaceConfiguration,
|
|
module: wgpu.ShaderModule,
|
|
pipeline_layout: wgpu.PipelineLayout,
|
|
pipeline: wgpu.RenderPipeline,
|
|
}
|
|
|
|
// -----------------------------------------
|
|
|
|
instance_init :: proc() {
|
|
state.ctx = context
|
|
|
|
state.instance = wgpu.CreateInstance(nil)
|
|
if state.instance == nil {
|
|
panic("WebGPU is not supported")
|
|
}
|
|
state.surface = os_get_surface(state.instance)
|
|
|
|
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})
|
|
}
|
|
|
|
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},
|
|
},
|
|
)
|
|
}
|
|
}
|
|
|
|
instance_destroy :: proc() {
|
|
wgpu.RenderPipelineRelease(state.pipeline)
|
|
wgpu.PipelineLayoutRelease(state.pipeline_layout)
|
|
wgpu.ShaderModuleRelease(state.module)
|
|
wgpu.QueueRelease(state.queue)
|
|
wgpu.DeviceRelease(state.device)
|
|
wgpu.AdapterRelease(state.adapter)
|
|
wgpu.SurfaceRelease(state.surface)
|
|
wgpu.InstanceRelease(state.instance)
|
|
}
|
|
|
|
// -----------------------------------------
|
|
|
|
resize :: proc "c" () {
|
|
context = state.ctx
|
|
|
|
state.config.width, state.config.height = os_get_framebuffer_size()
|
|
wgpu.SurfaceConfigure(state.surface, &state.config)
|
|
}
|
|
|
|
frame :: proc "c" (dt: f32) {
|
|
context = state.ctx
|
|
|
|
surface_texture := wgpu.SurfaceGetCurrentTexture(state.surface)
|
|
switch surface_texture.status {
|
|
case .SuccessOptimal, .SuccessSuboptimal:
|
|
// All good, could handle suboptimal here.
|
|
case .Timeout, .Outdated, .Lost:
|
|
// Skip this frame, and re-configure surface.
|
|
if surface_texture.texture != nil {
|
|
wgpu.TextureRelease(surface_texture.texture)
|
|
}
|
|
resize()
|
|
return
|
|
case .Occluded:
|
|
// Window is occluded (e.g. minimized), skip this frame.
|
|
return
|
|
case .Error:
|
|
// Fatal error
|
|
fmt.panicf(
|
|
"[triangle] get_current_texture status=%v",
|
|
surface_texture.status,
|
|
)
|
|
}
|
|
defer wgpu.TextureRelease(surface_texture.texture)
|
|
|
|
frame := wgpu.TextureCreateView(surface_texture.texture, nil)
|
|
defer wgpu.TextureViewRelease(frame)
|
|
|
|
command_encoder := wgpu.DeviceCreateCommandEncoder(state.device, nil)
|
|
defer wgpu.CommandEncoderRelease(command_encoder)
|
|
|
|
render_pass_encoder := wgpu.CommandEncoderBeginRenderPass(
|
|
command_encoder,
|
|
&{
|
|
colorAttachmentCount = 1,
|
|
colorAttachments = &wgpu.RenderPassColorAttachment {
|
|
view = frame,
|
|
loadOp = .Clear,
|
|
storeOp = .Store,
|
|
depthSlice = wgpu.DEPTH_SLICE_UNDEFINED,
|
|
clearValue = {0, 1, 0, 1},
|
|
},
|
|
},
|
|
)
|
|
|
|
wgpu.RenderPassEncoderSetPipeline(render_pass_encoder, state.pipeline)
|
|
wgpu.RenderPassEncoderDraw(
|
|
render_pass_encoder,
|
|
vertexCount = 3,
|
|
instanceCount = 1,
|
|
firstVertex = 0,
|
|
firstInstance = 0,
|
|
)
|
|
|
|
wgpu.RenderPassEncoderEnd(render_pass_encoder)
|
|
wgpu.RenderPassEncoderRelease(render_pass_encoder)
|
|
|
|
command_buffer := wgpu.CommandEncoderFinish(command_encoder, nil)
|
|
defer wgpu.CommandBufferRelease(command_buffer)
|
|
|
|
wgpu.QueueSubmit(state.queue, {command_buffer})
|
|
wgpu.SurfacePresent(state.surface)
|
|
}
|