Backlog/engine/rend/src/sgpu/renderer.zig

755 lines
28 KiB
Zig

// sdl3_gpu is like 8 characterso
// so instead of refering to it by it's full name every time
//
// we will just call it sgpu.
pub const Renderer = struct {
allocator: std.mem.Allocator,
totalTime: f64 = 0,
device: *gpu.GPUDevice = undefined,
shaderType: []const u8 = undefined,
shaderSuffix: []const u8 = undefined,
shaderformat: gpu.GPUShaderFormat = undefined,
entrypoint: []const u8 = "main",
testPipeline: *gpu.GPUGraphicsPipeline = undefined,
meshPipe: *gpu.GPUGraphicsPipeline = undefined,
scissor: gpu.Rect = undefined, // .{ .x = 0, .y = 0, .w = 1600, .h = 900 },
colorBuffer: *gpu.GPUBuffer = undefined,
colorBufferTransfer: *gpu.GPUTransferBuffer = undefined,
window: *sdl3.Window = undefined,
activeCamera: ?*rend.CameraComponent = null,
ssboScene: *gpu.GPUBuffer = undefined,
ssboSceneUpload: *gpu.GPUTransferBuffer = undefined,
meshPool: *MeshPool = undefined,
uploads: std.ArrayListUnmanaged(struct { ptr: *anyopaque, func: *const fn (*anyopaque, *gpu.GPUCopyPass) void }) = .{},
uploadCleanup: std.ArrayListUnmanaged(struct { ptr: *anyopaque, func: *const fn (*anyopaque) void }) = .{},
postMesh: std.ArrayListUnmanaged(struct { ptr: *anyopaque, func: *const fn (*anyopaque, *gpu.GPUCommandBuffer, *gpu.GPURenderPass) void }) = .{},
postRenders: std.ArrayListUnmanaged(struct { ptr: *anyopaque, func: *const fn (*anyopaque, *gpu.GPUCommandBuffer) void }) = .{},
destroys: std.ArrayListUnmanaged(struct { ptr: *anyopaque, func: *const fn (*anyopaque) void }) = .{},
preDraws: std.ArrayListUnmanaged(struct { ptr: *anyopaque, func: *const fn (*anyopaque, *gpu.GPUCommandBuffer) void }) = .{},
depthTexture: *gpu.GPUTexture = undefined,
depthFormat: gpu.GPUTextureFormat = .textureformatD16Unorm,
textureList: *TextureList = undefined,
shadowMapProjection: core.Transform = core.zm.identity(),
blockySampler: *gpu.GPUSampler = undefined,
defaultTexture: *rend.Texture = undefined,
lightPosition: core.Vectorf = .{},
debugDrawSys: *DebugDrawSystem = undefined,
shadowOrthoNear: f32 = -150,
shadowOrthoFar: f32 = 150,
directionalLightYaw: f32 = 0.0,
directionalLightColor: core.colors.Color = .{ .r = 0.8, .g = 0.8, .b = 0.9 },
directionalLightDir: core.Vectorf = core.Vectorf.new(0.5, -0.5, 0.0).normalize(),
shadowDepthFormat: gpu.GPUTextureFormat = .textureformatD16Unorm,
shadowDepthTexture: *gpu.GPUTexture = undefined,
shadowDepthDebugOutput: *gpu.GPUTexture = undefined,
shadowCastingPipeline: *gpu.GPUGraphicsPipeline = undefined,
// transients DO NOT TOUCH
swapchainTexture: ?*gpu.GPUTexture = undefined,
pub var NeonObjectTable: core.EngineObjectVTable = core.EngineObjectVTable.from(@This());
pub const MaxObjectCount = 50000;
pub fn init(allocator: std.mem.Allocator) !*@This() {
const self = try allocator.create(@This());
self.* = .{
.allocator = allocator,
};
try core.defineComponent(rend.MeshComponent, allocator);
try core.defineComponent(rend.CameraComponent, allocator);
return self;
}
pub fn startRenderer(self: *@This()) !void {
self.device = gpu.createGPUDevice(.{
.shaderformatSpirv = true,
.shaderformatDxil = true,
.shaderformatMsl = true,
}, true, null);
self.window = platform.getInstance().window;
self.scissor = .{ .x = 0, .y = 0, .w = platform.getInstance().windowExtent.x, .h = platform.getInstance().windowExtent.y };
if (!self.device.claimWindowForGPUDevice(self.window))
return error.UnableToClaimGpu;
try self.discoverFormats();
core.engine_log("sgpu device created, using shader format: {s}", .{self.shaderSuffix});
core.engine_log("using renderer... scientist", .{});
try self.createMeshPipeline();
try self.createBuffers();
try self.createDepthTexture();
self.meshPool = try self.createRendererObject(MeshPool);
self.textureList = try self.createRendererEngineObject(TextureList);
try self.createSamplers();
try assets.load(
assets.MakeImportRefOptions(
"Texture",
"t_default",
.{ .path = "textures/texture_sample.png" },
),
);
try assets.load(
assets.MakeImportRefOptions(
"Mesh",
"m_default_cube",
.{ .path = "meshes/primitive_box.obj" },
),
);
var defaultTextureName = core.MakeName("t_default");
self.defaultTexture = getTexture(&defaultTextureName).?;
self.debugDrawSys = try self.createRendererEngineObject(DebugDrawSystem);
try self.createDirectionalShadowsPipeline();
}
pub fn createDirectionalShadowsPipeline(self: *@This()) !void {
try self.createShadowDepthTexture();
try self.createShadowCastingPipeline();
}
pub fn createSamplers(self: *@This()) !void {
core.engine_log("creating blocky sampler", .{});
self.blockySampler = self.device.createGPUSampler(&std.mem.zeroInit(gpu.GPUSamplerCreateInfo, .{
.min_filter = .filterNearest,
.mag_filter = .filterNearest,
.mipmap_mode = .samplermipmapmodeNearest,
.address_mode_u = .sampleraddressmodeRepeat,
.address_mode_v = .sampleraddressmodeRepeat,
.address_mode_w = .sampleraddressmodeRepeat,
}));
}
// registers a pre-existing render object, does not add to destroys
pub fn registerRendererObject(self: *@This(), T: type, object: *anyopaque) !void {
if (@hasDecl(T, "onUpload")) {
try self.uploads.append(self.allocator, .{ .ptr = object, .func = T.onUpload });
}
if (@hasDecl(T, "onUploadCleanup")) {
try self.uploadCleanup.append(self.allocator, .{ .ptr = object, .func = T.onUploadCleanup });
}
if (@hasDecl(T, "postRender")) {
try self.postRenders.append(self.allocator, .{ .ptr = object, .func = T.postRender });
}
if (@hasDecl(T, "postMesh")) {
try self.postMesh.append(self.allocator, .{ .ptr = object, .func = T.postMesh });
}
if (@hasDecl(T, "onPreDraw")) {
try self.preDraws.append(self.allocator, .{ .ptr = object, .func = T.onPreDraw });
}
}
pub fn createRendererEngineObject(self: *@This(), T: type) !*T {
const object = try core.createObject(T, .{});
try object.setup(self.device);
try self.registerRendererObject(T, object);
return object;
}
pub fn createRendererObject(self: *@This(), T: type) !*T {
const object = try T.create(self.device, self.allocator, .{});
try self.registerRendererObject(T, object);
try self.destroys.append(self.allocator, .{ .ptr = object, .func = T.destroy });
return object;
}
fn createShadowDepthTexture(self: *@This()) !void {
var gci = std.mem.zeroes(gpu.GPUTextureCreateInfo);
const shadowMapRes = core.configVar(u32, "renderer.shadowmap.resolution", 2048);
gci.type = .texturetype2d;
gci.format = self.shadowDepthFormat;
gci.width = shadowMapRes;
gci.height = shadowMapRes;
gci.layer_count_or_depth = 1;
gci.num_levels = 1;
gci.sample_count = .samplecount1;
gci.usage = .{ .textureusageDepthStencilTarget = true, .textureusageSampler = true };
self.shadowDepthTexture = self.device.createGPUTexture(&gci);
}
fn createDepthTexture(self: *@This()) !void {
var gci = std.mem.zeroes(gpu.GPUTextureCreateInfo);
gci.type = .texturetype2d;
gci.format = self.depthFormat;
gci.width = @intCast(self.scissor.w);
gci.height = @intCast(self.scissor.h);
gci.layer_count_or_depth = 1;
gci.num_levels = 1;
gci.usage = .{ .textureusageDepthStencilTarget = true };
self.depthTexture = self.device.createGPUTexture(&gci);
}
pub fn discoverFormats(self: *@This()) !void {
const formats = self.device.getGPUShaderFormats();
if (formats.shaderformatSpirv) {
self.shaderType = "spv"; // shaderformatSpirv
self.shaderSuffix = ".spv";
self.shaderformat = .{ .shaderformatSpirv = true };
} else if (formats.shaderformatMsl) {
self.shaderType = "msl"; // shaderformatSpirv
self.shaderSuffix = ".msl";
self.entrypoint = "main0";
self.shaderformat = .{ .shaderformatMsl = true };
} else if (formats.shaderformatDxil) {
self.shaderType = "dxil"; // shaderformatSpirv
self.shaderSuffix = ".dxil";
self.shaderformat = .{ .shaderformatDxil = true };
}
}
fn addAttribute(list: *std.ArrayList(gpu.GPUVertexAttribute), offset: *u32, size: u32, format: gpu.GPUVertexElementFormat) !void {
try list.append(.{ .location = @intCast(list.items.len), .offset = offset.*, .format = format, .buffer_slot = 0 });
offset.* = offset.* + size;
}
pub fn generateVertexAttributeList(self: *@This()) !std.ArrayList(gpu.GPUVertexAttribute) {
var list = std.ArrayList(gpu.GPUVertexAttribute).init(self.allocator);
var offset: u32 = 0;
{
try addAttribute(&list, &offset, @sizeOf(f32) * 3, .vertexelementformatFloat3);
try addAttribute(&list, &offset, @sizeOf(f32) * 3, .vertexelementformatFloat3);
try addAttribute(&list, &offset, @sizeOf(f32) * 4, .vertexelementformatFloat4);
try addAttribute(&list, &offset, @sizeOf(f32) * 2, .vertexelementformatFloat2);
try addAttribute(&list, &offset, @sizeOf(u32), .vertexelementformatUint);
}
return list;
}
pub fn createShadowCastingPipeline(self: *@This()) !void {
const vertex = try self.loadShader("meshes.vert", meshes_vert.LoadArgs);
const fragment = try self.loadShader("depthOnly.frag", depthOnly.LoadArgs);
var pci = std.mem.zeroes(gpu.GPUGraphicsPipelineCreateInfo);
pci.vertex_shader = vertex;
pci.fragment_shader = fragment;
var attributes = try self.generateVertexAttributeList();
defer attributes.deinit();
pci.vertex_input_state = .{
.num_vertex_buffers = 1,
.vertex_buffer_descriptions = &[_]gpu.GPUVertexBufferDescription{
.{ .slot = 0, .pitch = @sizeOf(rend.MeshVertex), .input_rate = .vertexinputrateVertex, .instance_step_rate = 0 },
},
.num_vertex_attributes = @intCast(attributes.items.len),
.vertex_attributes = @ptrCast(attributes.items.ptr),
};
pci.depth_stencil_state.compare_op = .compareopLess;
pci.depth_stencil_state.enable_depth_test = true;
pci.depth_stencil_state.enable_depth_write = true;
pci.depth_stencil_state.write_mask = 0xff;
pci.target_info.has_depth_stencil_target = true;
pci.target_info.depth_stencil_format = self.shadowDepthFormat;
pci.target_info.num_color_targets = 0;
// no color targets
pci.rasterizer_state.cull_mode = .cullmodeNone;
pci.rasterizer_state.fill_mode = .fillmodeFill;
pci.rasterizer_state.enable_depth_bias = true;
self.shadowCastingPipeline = self.device.createGPUGraphicsPipeline(&pci);
}
pub fn createMeshPipeline(self: *@This()) !void {
const vertex = try self.loadShader("meshes.vert", meshes_vert.LoadArgs);
const fragment = try self.loadShader("lit_mesh.frag", lit_mesh_frag.LoadArgs);
var pci = std.mem.zeroes(gpu.GPUGraphicsPipelineCreateInfo);
pci.vertex_shader = vertex;
pci.fragment_shader = fragment;
var attributes = try self.generateVertexAttributeList();
defer attributes.deinit();
pci.vertex_input_state = .{
.num_vertex_buffers = 1,
.vertex_buffer_descriptions = &[_]gpu.GPUVertexBufferDescription{
.{ .slot = 0, .pitch = @sizeOf(rend.MeshVertex), .input_rate = .vertexinputrateVertex, .instance_step_rate = 0 },
},
.num_vertex_attributes = @intCast(attributes.items.len),
.vertex_attributes = @ptrCast(attributes.items.ptr),
};
pci.depth_stencil_state.compare_op = .compareopLess;
pci.depth_stencil_state.enable_depth_test = true;
pci.depth_stencil_state.enable_depth_write = true;
pci.depth_stencil_state.write_mask = 0xff;
pci.target_info.has_depth_stencil_target = true;
pci.target_info.depth_stencil_format = self.depthFormat;
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.meshPipe = self.device.createGPUGraphicsPipeline(&pci);
}
pub fn createBuffers(self: *@This()) !void {
// ssbo buffer
// todo.. sparse uploads
self.ssboScene = self.device.createGPUBuffer(&.{
.usage = .{ .bufferusageGraphicsStorageRead = true, .bufferusageVertex = true },
.size = MaxObjectCount * @sizeOf(meshes_vert.Scene),
.props = 0,
});
self.ssboSceneUpload = self.device.createGPUTransferBuffer(&.{
.usage = .transferbufferusageUpload,
.size = MaxObjectCount * @sizeOf(meshes_vert.Scene),
.props = 0,
});
}
pub fn uploadSSBOs(self: *@This(), copyPass: *gpu.GPUCopyPass) !void {
const container = rend.MeshComponent.BaseContainer;
const uploadCount: usize = @min(container.dense.items.len, MaxObjectCount);
{
const uploadMapped: [*]meshes_vert.Scene = @ptrCast(@alignCast(self.device.mapGPUTransferBuffer(self.ssboSceneUpload, true)));
defer self.device.unmapGPUTransferBuffer(self.ssboSceneUpload);
// iterate over MeshComponents
for (0..uploadCount) |i| {
// const object = &container.dense.items[i].value;
const objectId = container.dense.items[i].sparseIndex;
var transform = core.zm.identity();
if (core.Scene.SceneObjectContainer.get(objectId, ._repr)) |repr| {
transform = repr.transform;
}
uploadMapped[i].Model = @bitCast(transform);
}
}
if (uploadCount == 0)
return;
copyPass.uploadToGPUBuffer(&.{ .transfer_buffer = self.ssboSceneUpload, .offset = 0 }, &.{
.buffer = self.ssboScene,
.offset = 0,
.size = @intCast(uploadCount * @sizeOf(meshes_vert.Scene)),
}, true);
}
pub fn loadShader(
self: *@This(),
shaderName: []const u8,
loadArgs: ShaderLoadArgs,
) !*gpu.GPUShader {
const contentPath = try std.fmt.allocPrint(self.allocator, "_shaders/{s}/{s}{s}", .{ self.shaderType, shaderName, self.shaderSuffix });
defer self.allocator.free(contentPath);
var stage: gpu.GPUShaderStage = undefined;
if (std.mem.endsWith(u8, shaderName, ".vert")) {
stage = .shaderstageVertex;
} else if (std.mem.endsWith(u8, shaderName, ".frag")) {
stage = .shaderstageFragment;
} else {
return error.NotImplemented;
}
core.engine_log("creating shader {s} => {s} {any} args: {any}", .{ shaderName, contentPath, stage, loadArgs });
const mapping = try core.fs().loadFile(contentPath);
defer core.fs().unmap(mapping);
const sci = gpu.GPUShaderCreateInfo{
.code = @ptrCast(mapping.bytes.ptr),
.entrypoint = @ptrCast(self.entrypoint.ptr),
.format = self.shaderformat,
.code_size = mapping.bytes.len - 1,
.stage = stage,
.num_samplers = loadArgs.num_samplers,
.num_storage_textures = loadArgs.num_storage_textures, // The number of storage textures defined in the shader.
.num_storage_buffers = loadArgs.num_storage_buffers, // The number of storage buffers defined in the shader.
.num_uniform_buffers = loadArgs.num_uniform_buffers, // The number of uniform buffers defined in the shader.
.props = 0,
};
const rv = self.device.createGPUShader(&sci);
return rv;
}
pub fn uploadUniforms(self: *@This(), cmd: *gpu.GPUCommandBuffer) !void {
{
var data = std.mem.zeroes([@sizeOf(meshes_vert.Uniforms) / 8 + 1]usize);
const ptr: *meshes_vert.Uniforms = @ptrCast(@alignCast(&data));
if (self.activeCamera) |camera| {
ptr.ViewProjection = @bitCast(camera.final);
}
ptr.ShadowMapProjection = @bitCast(self.shadowMapProjection);
// ptr.ViewProjection = @bitCast(self.shadowMapProjection);
ptr.time = @floatCast(self.totalTime);
cmd.pushGPUVertexUniformData(0, &data, @sizeOf(meshes_vert.Uniforms));
}
{
var data = std.mem.zeroes([@sizeOf(lit_mesh_frag.Uniforms) / 8 + 1]usize);
const ptr: *lit_mesh_frag.Uniforms = @ptrCast(@alignCast(&data));
ptr.time = @floatCast(self.totalTime);
var position: core.Vectorf = .{};
if (self.activeCamera) |cam| {
position = cam.finalPos;
}
const resolved = core.Rotation.eulerY(core.radians(self.directionalLightYaw)).rotateVector(self.directionalLightDir.fmul(-1));
ptr.screenSize = @bitCast(platform.context().extent);
ptr.viewPos = @bitCast(position.toZm());
ptr.lightPosition = @bitCast(self.lightPosition.toZm());
ptr.directionalLight = @bitCast(resolved.toZm());
ptr.directionalLightColor = @bitCast(self.directionalLightColor);
cmd.pushGPUFragmentUniformData(0, &data, @sizeOf(lit_mesh_frag.Uniforms));
}
}
pub fn frameUploads(self: *@This()) void {
const cmd = self.device.acquireGPUCommandBuffer();
// 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);
if (self.activeCamera) |camera| {
camera.resolve();
}
const copyPass = cmd.beginGPUCopyPass();
// copyPass.uploadToGPUBuffer(&.{ .transfer_buffer = self.colorBufferTransfer, .offset = 0 }, &.{
// .buffer = self.colorBuffer,
// .offset = 0,
// .size = 4 * 12,
// }, true);
try self.uploadSSBOs(copyPass);
for (self.uploads.items) |upload| {
upload.func(upload.ptr, copyPass);
}
copyPass.endGPUCopyPass();
if (!cmd.submitGPUCommandBuffer()) {
core.graphics_log("submit gpu command buffer SDL ERROR: {s}", .{sdl3.getError()});
}
for (self.uploadCleanup.items) |uploadCleanup| {
uploadCleanup.func(uploadCleanup.ptr);
}
}
pub fn tick(self: *@This(), dt: f64) void {
self.totalTime += dt;
const cmd = self.device.acquireGPUCommandBuffer();
self.frameUploads();
for (self.preDraws.items) |interface| {
interface.func(interface.ptr, cmd);
}
// mesh pre-iteration
const container = rend.MeshComponent.BaseContainer;
const uploadCount: usize = @min(container.dense.items.len, MaxObjectCount);
for (0..uploadCount) |i| {
const component = &container.dense.items[i].value;
if (component.mesh == null) {
component.updateMesh();
}
if (component.texture == null) {
component.updateTexture();
}
}
self.drawDirectionalShadowMap(cmd);
self.draw(cmd);
}
// orthographic view projection from the sun towards the center of the thing
pub fn shadowMapOrtho(self: *@This()) core.Transform {
const ortho = core.zm.orthographicLh(200, 200, self.shadowOrthoNear, self.shadowOrthoFar);
var rotation = core.zm.lookAtLh(
.{ 0, 0, 0, 1 },
self.directionalLightDir.toZm(),
.{ 0, 1, 0, 1 },
);
rotation = core.zm.mul(core.zm.rotationY(core.radians(self.directionalLightYaw)), rotation);
return core.zm.mul(rotation, ortho);
}
pub fn drawDirectionalShadowMap(self: *@This(), cmd: *gpu.GPUCommandBuffer) void {
{
var data = std.mem.zeroes([@sizeOf(meshes_vert.Uniforms) / 8 + 1]usize);
const ptr: *meshes_vert.Uniforms = @ptrCast(@alignCast(&data));
self.shadowMapProjection = self.shadowMapOrtho();
ptr.ViewProjection = @bitCast(self.shadowMapProjection);
cmd.pushGPUVertexUniformData(0, &data, @sizeOf(meshes_vert.Uniforms));
}
// can cache this
const depthTarget = std.mem.zeroInit(gpu.GPUDepthStencilTargetInfo, .{
.texture = self.shadowDepthTexture,
.load_op = .loadopClear,
.store_op = .storeopStore,
.stencil_load_op = .loadopDontCare,
.stencil_store_op = .storeopDontCare,
.clear_depth = 1.0,
.clear_stencil = 0,
});
const renderpass = cmd.beginGPURenderPass(null, 0, &depthTarget);
renderpass.bindGPUGraphicsPipeline(self.shadowCastingPipeline);
renderpass.bindGPUVertexStorageBuffers(0, &self.ssboScene, 1);
renderpass.bindGPUVertexBuffers(0, &.{ .buffer = self.meshPool.vertexBuffer, .offset = 0 }, 1);
renderpass.bindGPUIndexBuffer(&.{ .buffer = self.meshPool.indexBuffer, .offset = 0 }, .indexelementsize32bit);
const container = rend.MeshComponent.BaseContainer;
const uploadCount: usize = @min(container.dense.items.len, MaxObjectCount);
for (0..uploadCount) |i| {
const component = &container.dense.items[i].value;
if (component.mesh) |mesh| {
renderpass.drawGPUIndexedPrimitives(mesh.index.size, 1, mesh.index.start, @intCast(mesh.vertex.start), @intCast(i));
}
}
renderpass.endGPURenderPass();
}
pub fn draw(self: *@This(), cmd: *gpu.GPUCommandBuffer) void {
if (cmd.waitAndAcquireGPUSwapchainTexture(self.window, @ptrCast(&self.swapchainTexture), null, null)) {
if (self.swapchainTexture == null) {
_ = cmd.submitGPUCommandBuffer();
return;
}
try self.uploadUniforms(cmd);
// can cache this
const targetInfo: gpu.GPUColorTargetInfo = std.mem.zeroInit(gpu.GPUColorTargetInfo, .{
.texture = self.swapchainTexture.?,
.clear_color = .{ .r = 71.0 / 256.0, .g = 200.0 / 256.0, .b = 1.0, .a = 1.0 },
.load_op = .loadopClear,
.store_op = .storeopStore,
});
const depthTarget = std.mem.zeroInit(gpu.GPUDepthStencilTargetInfo, .{
.texture = self.depthTexture,
.load_op = .loadopClear,
.store_op = .storeopDontCare,
.stencil_load_op = .loadopDontCare,
.stencil_store_op = .storeopDontCare,
.clear_depth = 1.0,
.clear_stencil = 0,
});
const renderpass = cmd.beginGPURenderPass(&targetInfo, 1, &depthTarget);
//renderpass.bindGPUGraphicsPipeline(self.testPipeline);
renderpass.bindGPUGraphicsPipeline(self.meshPipe);
renderpass.bindGPUVertexStorageBuffers(0, &self.ssboScene, 1);
renderpass.bindGPUVertexBuffers(0, &.{ .buffer = self.meshPool.vertexBuffer, .offset = 0 }, 1);
renderpass.bindGPUIndexBuffer(&.{ .buffer = self.meshPool.indexBuffer, .offset = 0 }, .indexelementsize32bit);
renderpass.bindGPUFragmentSamplers(1, &.{ .texture = self.shadowDepthTexture, .sampler = self.blockySampler }, 1);
// todo move into materials system
renderpass.setGPUScissor(&self.scissor);
//rendering each mesh
{
const container = rend.MeshComponent.BaseContainer;
const uploadCount: usize = @min(container.dense.items.len, MaxObjectCount);
for (0..uploadCount) |i| {
const component = &container.dense.items[i].value;
var t = component.texture;
if (t == null) {
t = self.defaultTexture;
}
renderpass.bindGPUFragmentSamplers(0, &.{ .texture = t.?.texture, .sampler = self.blockySampler }, 1);
if (component.mesh) |mesh| {
renderpass.drawGPUIndexedPrimitives(mesh.index.size, 1, mesh.index.start, @intCast(mesh.vertex.start), @intCast(i));
}
}
}
for (self.postMesh.items) |interface| {
interface.func(interface.ptr, cmd, renderpass);
}
renderpass.endGPURenderPass();
for (self.postRenders.items) |interface| {
interface.func(interface.ptr, cmd);
}
// renderpass.drawGPUPrimitives(3, 1, 0, 0);
}
_ = cmd.submitGPUCommandBuffer();
}
pub fn deinit(self: *@This()) void {
// self.device.releaseGPUGraphicsPipeline(self.testPipeline);
for (self.destroys.items) |d| {
d.func(d.ptr);
}
self.postMesh.deinit(self.allocator);
self.uploadCleanup.deinit(self.allocator);
self.preDraws.deinit(self.allocator);
self.postRenders.deinit(self.allocator);
self.uploads.deinit(self.allocator);
self.destroys.deinit(self.allocator);
self.allocator.destroy(self);
}
};
pub var gRenderer: *Renderer = undefined;
pub var gAllocator: std.mem.Allocator = undefined;
const rend = @import("../rend.zig");
const std = @import("std");
const assets = @import("assets");
const core = @import("core");
const platform = @import("platform");
const sdl3 = @import("sdl3");
const gpu = sdl3.gpu;
const meshes_vert = @import("meshes.vert");
const lit_mesh_frag = @import("lit_mesh.frag");
const depthOnly = @import("depthOnly.frag");
const sample_vert = @import("sample.vert");
const MeshVertices = meshes_vert.Scene;
const MeshUniforms = meshes_vert.Uniforms;
const ShaderLoadArgs = sdl3.shaderTypes.ShaderLoadArgs;
pub const mesh_pool = @import("mesh-pool.zig");
pub const MeshPool = mesh_pool.MeshPool;
pub const TextureList = @import("TextureList.zig");
const DebugDrawSystem = @import("DebugDrawSystem.zig");
// debug api
pub fn reloadShaders() !void {
_ = core.shell.runCmd(gRenderer.allocator, &.{ "python", "../tools/scripts/cookShaders.py" }, ".") catch {
core.graphics_logs("shader cook script failed");
return;
};
try gRenderer.createMeshPipeline();
}
// ====== renderer API =======
pub fn createInstance() !void {
gRenderer = try core.createObject(Renderer, .{ .can_tick = true, .isCore = true });
gAllocator = gRenderer.allocator;
}
pub fn start() !void {
try gRenderer.startRenderer();
}
pub fn shutdown() void {}
pub fn context() *Renderer {
return gRenderer;
}
pub fn setActiveCamera(camera: ?*rend.CameraComponent) void {
gRenderer.activeCamera = camera;
}
pub fn createRendererObject(comptime T: type) !*T {
return try gRenderer.createRendererObject(T);
}
pub fn registerRendererObject(comptime T: type, object: *anyopaque) !void {
return try gRenderer.registerRendererObject(T, object);
}
pub const getMesh = mesh_pool.getMesh;
pub const getMeshByName = mesh_pool.getMeshByName;
pub const pushMeshUpdate = mesh_pool.pushMeshUpdate;
pub fn getTexture(name: *core.Name) ?*rend.Texture {
return gRenderer.textureList.map.get(name.handle());
}
pub const GPUTextureType = gpu.GPUTexture;