248 lines
8.7 KiB
Zig
248 lines
8.7 KiB
Zig
allocator: std.mem.Allocator,
|
|
|
|
timer: std.time.Timer,
|
|
// fillPipeline: *sdl3.GpuGraphicsPipeline = undefined,
|
|
// linePipeline: *sdl3.GpuGraphicsPipeline = undefined,
|
|
// viewport: sdl3.GpuViewport = undefined,
|
|
scissor: gpu.Rect = .{ .x = 0, .y = 0, .w = 640, .h = 480 },
|
|
|
|
running: bool = true,
|
|
window: *sdl3.Window = undefined,
|
|
device: *gpu.GPUDevice = undefined,
|
|
shaderpath: []const u8 = undefined,
|
|
shadersuffix: []const u8 = undefined,
|
|
entrypoint: []const u8 = "main",
|
|
fragment: *gpu.GPUShader = undefined,
|
|
vertex: *gpu.GPUShader = undefined,
|
|
pipeline: *gpu.GPUGraphicsPipeline = undefined,
|
|
shaderformat: gpu.GPUShaderFormat = undefined,
|
|
|
|
colorBuffer: *gpu.GPUBuffer = undefined,
|
|
colorBufferTransfer: *gpu.GPUTransferBuffer = undefined,
|
|
totalTime: f64 = 0.0,
|
|
|
|
var stdout_buffer: [1024]u8 = undefined;
|
|
|
|
pub fn print(comptime fmt: []const u8, args: anytype) void {
|
|
var stdout = std.fs.File.stdout().writer(&stdout_buffer);
|
|
stdout.interface.print(">>> " ++ fmt ++ "\n", args) catch return;
|
|
}
|
|
|
|
pub fn create(allocator: std.mem.Allocator) !*@This() {
|
|
const self = try allocator.create(@This());
|
|
self.* = .{
|
|
.allocator = allocator,
|
|
.timer = try std.time.Timer.start(),
|
|
};
|
|
|
|
return self;
|
|
}
|
|
|
|
pub fn frameStamp(self: *@This()) f64 {
|
|
return @as(f64, @floatFromInt(self.timer.read())) / 1000 / 1000 / 1000;
|
|
}
|
|
|
|
pub fn startContext(self: *@This()) !void {
|
|
try sdl3.init(.{
|
|
.video = true,
|
|
.gamepad = true,
|
|
});
|
|
|
|
self.window = sdl3.c.SDL_CreateWindow("hello-world", 640, 480, 0).?;
|
|
self.device = gpu.createGPUDevice(.{
|
|
.shaderformatSpirv = true,
|
|
.shaderformatDxil = true,
|
|
.shaderformatMsl = true,
|
|
}, false, null);
|
|
|
|
if (!self.device.claimWindowForGPUDevice(self.window))
|
|
return error.UnableToClaimGpu;
|
|
|
|
const formats = self.device.getGPUShaderFormats();
|
|
|
|
print("SDL Context Loaded: {}\n", .{formats});
|
|
if (formats.shaderformatSpirv) {
|
|
self.shaderpath = "./content/_cooked/spv"; // shaderformatSpirv
|
|
self.shadersuffix = ".spv";
|
|
self.shaderformat = .{ .shaderformatSpirv = true };
|
|
} else if (formats.shaderformatMsl) {
|
|
self.shaderpath = "./content/_cooked/msl"; // shaderformatSpirv
|
|
self.shadersuffix = ".msl";
|
|
self.entrypoint = "main0";
|
|
self.shaderformat = .{ .shaderformatMsl = true };
|
|
} else if (formats.shaderformatDxil) {
|
|
self.shaderpath = "./content/_cooked/dxil"; // shaderformatSpirv
|
|
self.shadersuffix = ".dxil";
|
|
self.shaderformat = .{ .shaderformatDxil = true };
|
|
}
|
|
|
|
self.vertex = try self.loadShader("hello-triangle.vert", .shaderstageVertex, 0, 0, 1, 0);
|
|
self.fragment = try self.loadShader("hello-triangle.frag", .shaderstageFragment, 0, 0, 0, 0);
|
|
|
|
var pci = std.mem.zeroes(gpu.GPUGraphicsPipelineCreateInfo);
|
|
pci.fragment_shader = self.fragment;
|
|
pci.vertex_shader = self.vertex;
|
|
|
|
pci.target_info.num_color_targets = 1;
|
|
pci.target_info.color_target_descriptions = &[_]gpu.GPUColorTargetDescription{
|
|
.{
|
|
.format = self.device.getGPUSwapchainTextureFormat(self.window),
|
|
.blend_state = std.mem.zeroes(gpu.GPUColorTargetBlendState),
|
|
},
|
|
};
|
|
|
|
pci.rasterizer_state.fill_mode = .fillmodeFill;
|
|
self.pipeline = self.device.createGPUGraphicsPipeline(&pci);
|
|
|
|
self.colorBuffer = self.device.createGPUBuffer(&.{
|
|
.usage = .{ .bufferusageVertex = true, .bufferusageGraphicsStorageRead = true },
|
|
.size = 8192 * 2,
|
|
.props = 0,
|
|
});
|
|
|
|
self.colorBufferTransfer = self.device.createGPUTransferBuffer(&.{
|
|
.usage = .transferbufferusageUpload,
|
|
.size = 8192 * 2,
|
|
.props = 0,
|
|
});
|
|
}
|
|
|
|
pub fn loadFileAlloc(filename: []const u8, comptime alignment: ?std.mem.Alignment, allocator: std.mem.Allocator) ![]u8 {
|
|
var file = try std.fs.cwd().openFile(filename, .{});
|
|
defer file.close();
|
|
const filesize = (try file.stat()).size + 1; // add null byte
|
|
const buffer: []u8 = try allocator.alignedAlloc(u8, alignment, filesize);
|
|
errdefer allocator.free(buffer);
|
|
std.mem.copyForwards(u8, buffer, try file.readToEndAlloc(allocator, buffer.len - 1));
|
|
buffer[buffer.len - 1] = 0;
|
|
return buffer;
|
|
}
|
|
|
|
pub fn loadShader(
|
|
self: *@This(),
|
|
shaderName: []const u8,
|
|
stage: gpu.GPUShaderStage,
|
|
num_samplers: u32, // The number of samplers defined in the shader.
|
|
num_storage_textures: u32, // The number of storage textures defined in the shader.
|
|
num_storage_buffers: u32, // The number of storage buffers defined in the shader.
|
|
num_uniform_buffers: u32, // The number of uniform buffers defined in the shader.
|
|
) !*gpu.GPUShader {
|
|
const spath = try std.fmt.allocPrintSentinel(self.allocator, "{s}/{s}{s}", .{ self.shaderpath, shaderName, self.shadersuffix }, 0);
|
|
defer self.allocator.free(spath);
|
|
|
|
print("loading shader {s}\n", .{spath});
|
|
|
|
const fileContents = try loadFileAlloc(spath, .@"8", self.allocator);
|
|
defer self.allocator.free(fileContents);
|
|
const sci = gpu.GPUShaderCreateInfo{
|
|
.code = @ptrCast(fileContents.ptr),
|
|
.entrypoint = @ptrCast(self.entrypoint.ptr),
|
|
.format = self.shaderformat,
|
|
.code_size = fileContents.len - 1,
|
|
.stage = stage,
|
|
.num_samplers = num_samplers,
|
|
.num_storage_textures = num_storage_textures, // The number of storage textures defined in the shader.
|
|
.num_storage_buffers = num_storage_buffers, // The number of storage buffers defined in the shader.
|
|
.num_uniform_buffers = num_uniform_buffers, // The number of uniform buffers defined in the shader.
|
|
.props = 0,
|
|
};
|
|
|
|
return self.device.createGPUShader(&sci);
|
|
}
|
|
|
|
pub fn loop(self: *@This()) !void {
|
|
print("loop started\n", .{});
|
|
try self.startContext();
|
|
while (self.running) {
|
|
const dt = @as(f64, @floatFromInt(self.timer.lap())) / 1000 / 1000 / 1000;
|
|
self.update(dt);
|
|
}
|
|
}
|
|
|
|
fn update(self: *@This(), dt: f64) void {
|
|
while (sdl3.pollEvent()) |event| {
|
|
switch (event.type) {
|
|
sdl_event.quit => {
|
|
self.running = false;
|
|
},
|
|
sdl_event.key_down => {
|
|
const key = event.key.scancode;
|
|
if (key == @intFromEnum(sdl3.Scancode.escape)) {
|
|
self.running = false;
|
|
}
|
|
},
|
|
else => {},
|
|
}
|
|
}
|
|
|
|
self.draw(dt);
|
|
print("frametime {d:.3}ms (fps: {d:.1})\r", .{ dt * 1000, 1 / dt });
|
|
}
|
|
|
|
pub fn draw(self: *@This(), dt: f64) void {
|
|
self.totalTime += dt;
|
|
|
|
const cmd = self.device.acquireGPUCommandBuffer();
|
|
var swapchain_texture: *gpu.GPUTexture = undefined;
|
|
{
|
|
const buffer = self.device.mapGPUTransferBuffer(self.colorBufferTransfer, true);
|
|
|
|
var b: [*][4]f32 = @ptrCast(@alignCast(buffer));
|
|
|
|
b[0] = .{ @floatCast(std.math.sin(self.totalTime * 3 * 2 + 0.8) * 0.2 + 0.8), 0.4, 0.4, 1.0 };
|
|
b[1] = .{ 0.4, @floatCast(std.math.sin(self.totalTime * 2 * 2 + 0.3) * 0.2 + 0.8), 0.4, 1.0 };
|
|
b[2] = .{ 0.4, 0.4, @floatCast(std.math.sin(self.totalTime * 4 * 2) * 0.2 + 0.8), 1.0 };
|
|
|
|
self.device.unmapGPUTransferBuffer(self.colorBufferTransfer);
|
|
}
|
|
|
|
{
|
|
const copyPass = cmd.beginGPUCopyPass();
|
|
defer copyPass.endGPUCopyPass();
|
|
copyPass.uploadToGPUBuffer(&.{ .transfer_buffer = self.colorBufferTransfer, .offset = 0 }, &.{
|
|
.buffer = self.colorBuffer,
|
|
.offset = 0,
|
|
.size = 4 * 12,
|
|
}, true);
|
|
}
|
|
|
|
if (cmd.waitAndAcquireGPUSwapchainTexture(self.window, &swapchain_texture, null, null)) {
|
|
var targetInfo: gpu.GPUColorTargetInfo = std.mem.zeroes(gpu.GPUColorTargetInfo);
|
|
targetInfo.texture = swapchain_texture;
|
|
targetInfo.clear_color = .{ .r = 0.1, .g = 0.1, .b = 0.1, .a = 1.0 };
|
|
targetInfo.load_op = .loadopClear;
|
|
targetInfo.store_op = .storeopStore;
|
|
|
|
const renderpass = cmd.beginGPURenderPass(&targetInfo, 1, null);
|
|
renderpass.bindGPUGraphicsPipeline(self.pipeline);
|
|
renderpass.bindGPUVertexStorageBuffers(0, &self.colorBuffer, 1);
|
|
renderpass.setGPUScissor(&self.scissor);
|
|
renderpass.drawGPUPrimitives(3, 1, 0, 0);
|
|
renderpass.endGPURenderPass();
|
|
}
|
|
|
|
print("rendering time: {d:.3}ms ", .{self.frameStamp()});
|
|
|
|
_ = cmd.submitGPUCommandBuffer();
|
|
}
|
|
|
|
pub fn destroy(self: *@This()) void {
|
|
sdl3.c.SDL_DestroyWindow(self.window);
|
|
self.device.releaseWindowFromGPUDevice(self.window);
|
|
|
|
self.allocator.destroy(self);
|
|
}
|
|
|
|
pub fn main() !void {
|
|
print("hello world... n", .{});
|
|
const app = try create(std.heap.c_allocator);
|
|
try app.loop();
|
|
defer app.destroy();
|
|
}
|
|
|
|
const hello_triangle_vert = @import("hello-triangle.vert");
|
|
const sdl3 = @import("sdl3");
|
|
const gpu = sdl3.gpu;
|
|
const sdl_event = sdl3.events;
|
|
const std = @import("std");
|