package main import "core:fmt" import "core:os" import sdl "vendor:sdl3" import "rsmview" import "gndview" import "shared" read_file_or_fail :: proc(path: string) -> []u8 { file_bytes, err := os.read_entire_file(path, context.allocator) if err != nil { fmt.println("failed to read file:", path, "error:", err) return nil } return file_bytes } create_gpu_shader :: proc(device: ^sdl.GPUDevice, path: string, stage: sdl.GPUShaderStage, num_samplers: u32) -> ^sdl.GPUShader { bytes := read_file_or_fail(path) if len(bytes) == 0 { return nil } ci := sdl.GPUShaderCreateInfo{ code_size = uint(len(bytes)), code = raw_data(bytes), entrypoint = cstring("main"), format = sdl.GPUShaderFormat{.SPIRV}, stage = stage, num_samplers = num_samplers, num_storage_textures = 0, num_storage_buffers = 0, num_uniform_buffers = 1, props = 0, } shader := sdl.CreateGPUShader(device, ci) delete(bytes) if shader == nil { fmt.println("CreateGPUShader failed:", path, "error:", sdl.GetError()) } return shader } copy_data_to_gpu :: proc( device: ^sdl.GPUDevice, cmd: ^sdl.GPUCommandBuffer, vertices: []shared.Vertex, vert_buf: ^sdl.GPUBuffer, indices: []u16, ind_buf: ^sdl.GPUBuffer ) -> bool { return true } GPU_Vertex :: struct { position: [3]f32, uv: [2]f32, } main :: proc() { if !sdl.Init(sdl.INIT_VIDEO) { fmt.println("SDL init failed:", sdl.GetError()) return } defer sdl.Quit() window := sdl.CreateWindow("Odin + SDL3 GPU Cubes", 1280, 720, sdl.WINDOW_RESIZABLE) if window == nil { fmt.println("window create failed:", sdl.GetError()) return } defer sdl.DestroyWindow(window) device := sdl.CreateGPUDevice(sdl.GPUShaderFormat{.SPIRV}, false, cstring("vulkan")) if device == nil { fmt.println("CreateGPUDevice failed:", sdl.GetError()) return } defer sdl.DestroyGPUDevice(device) if !sdl.ClaimWindowForGPUDevice(device, window) { fmt.println("ClaimWindowForGPUDevice failed:", sdl.GetError()) return } defer sdl.ReleaseWindowFromGPUDevice(device, window) driver_name := sdl.GetGPUDeviceDriver(device) fmt.println("SDL GPU driver:", string(driver_name)) if string(driver_name) != "vulkan" { fmt.println("expected Vulkan driver, got:", string(driver_name)) return } vertex_shader := create_gpu_shader(device, "shaders/cubes.vert.spv", .VERTEX, 0) if vertex_shader == nil { return } defer sdl.ReleaseGPUShader(device, vertex_shader) fragment_shader := create_gpu_shader(device, "shaders/cubes.frag.spv", .FRAGMENT, 1) if fragment_shader == nil { return } defer sdl.ReleaseGPUShader(device, fragment_shader) color_format := sdl.GetGPUSwapchainTextureFormat(device, window) if color_format == .INVALID { fmt.println("invalid swapchain format:", sdl.GetError()) return } vb_descs := []sdl.GPUVertexBufferDescription{ {slot = 0, pitch = size_of(shared.Vertex), input_rate = .VERTEX, instance_step_rate = 0}, } vattrs := []sdl.GPUVertexAttribute{ {location = 0, buffer_slot = 0, format = .FLOAT3, offset = u32(offset_of(shared.Vertex, pos))}, {location = 1, buffer_slot = 0, format = .FLOAT2, offset = u32(offset_of(shared.Vertex, uv))}, } blend := sdl.GPUColorTargetBlendState{ src_color_blendfactor = .ONE, dst_color_blendfactor = .ZERO, color_blend_op = .ADD, src_alpha_blendfactor = .ONE, dst_alpha_blendfactor = .ZERO, alpha_blend_op = .ADD, color_write_mask = sdl.GPUColorComponentFlags{.R, .G, .B, .A}, enable_blend = false, enable_color_write_mask = true, } color_targets := [1]sdl.GPUColorTargetDescription{ {format = color_format, blend_state = blend}, } pipeline_ci := sdl.GPUGraphicsPipelineCreateInfo{ vertex_shader = vertex_shader, fragment_shader = fragment_shader, vertex_input_state = sdl.GPUVertexInputState{ vertex_buffer_descriptions = &vb_descs[0], num_vertex_buffers = 1, vertex_attributes = &vattrs[0], num_vertex_attributes = 2, }, primitive_type = .TRIANGLELIST, rasterizer_state = sdl.GPURasterizerState{ fill_mode = .FILL, cull_mode = .NONE, front_face = .COUNTER_CLOCKWISE, depth_bias_constant_factor = 0, depth_bias_clamp = 0, depth_bias_slope_factor = 0, enable_depth_bias = false, enable_depth_clip = true, }, multisample_state = sdl.GPUMultisampleState{ sample_count = ._1, sample_mask = 0, enable_mask = false, enable_alpha_to_coverage = false, }, depth_stencil_state = sdl.GPUDepthStencilState{ compare_op = .LESS, back_stencil_state = sdl.GPUStencilOpState{fail_op = .KEEP, pass_op = .KEEP, depth_fail_op = .KEEP, compare_op = .ALWAYS}, front_stencil_state = sdl.GPUStencilOpState{fail_op = .KEEP, pass_op = .KEEP, depth_fail_op = .KEEP, compare_op = .ALWAYS}, compare_mask = 0, write_mask = 0, enable_depth_test = true, enable_depth_write = true, enable_stencil_test = true, }, target_info = sdl.GPUGraphicsPipelineTargetInfo{ color_target_descriptions = &color_targets[0], num_color_targets = 1, depth_stencil_format = .D32_FLOAT, has_depth_stencil_target = true, }, props = 0, } pipeline := sdl.CreateGPUGraphicsPipeline(device, pipeline_ci) if pipeline == nil { fmt.println("CreateGPUGraphicsPipeline failed:", sdl.GetError()) return } defer sdl.ReleaseGPUGraphicsPipeline(device, pipeline) pipeline_create_info := sdl.GPUGraphicsPipelineCreateInfo{ primitive_type = .TRIANGLELIST, rasterizer_state = { fill_mode = .FILL, cull_mode = .NONE, } } gndview.runGame(window, device, pipeline) _ = sdl.WaitForGPUIdle(device) }