Hardcore Vulkan migration began

This commit is contained in:
sergeypdev 2024-09-21 21:09:17 +04:00
parent d708144dca
commit 52cdbef876
10 changed files with 794 additions and 131 deletions

16
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,16 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Debug",
"type": "gdb",
"request": "launch",
"target": "./zig-out/learnopengl",
"cwd": "${workspaceRoot}",
"valuesFormatting": "parseText"
}
]
}

View File

@ -3,6 +3,20 @@ const Build = std.Build;
const Step = Build.Step; const Step = Build.Step;
const GenerateAssetManifest = @import("tools/GenerateAssetManifest.zig"); const GenerateAssetManifest = @import("tools/GenerateAssetManifest.zig");
fn buildVulkanWrapper(b: *Build, target: Build.ResolvedTarget, optimize: std.builtin.OptimizeMode) *Build.Module {
const registry = b.dependency("vulkan_headers", .{}).path("registry/vk.xml");
const vk_gen = b.dependency("vulkan_zig", .{}).artifact("vulkan-zig-generator");
const vk_generate_cmd = b.addRunArtifact(vk_gen);
vk_generate_cmd.addFileArg(registry);
const vk_zig_path = vk_generate_cmd.addOutputFileArg("vk.zig");
return b.addModule("vk", .{
.root_source_file = vk_zig_path,
.optimize = optimize,
.target = target,
});
}
// Although this function looks imperative, note that its job is to // Although this function looks imperative, note that its job is to
// declaratively construct a build graph that will be executed by an external // declaratively construct a build graph that will be executed by an external
// runner. // runner.
@ -23,6 +37,8 @@ pub fn build(b: *Build) void {
"Prioritize performance, safety, or binary size for build time tools", "Prioritize performance, safety, or binary size for build time tools",
) orelse .Debug; ) orelse .Debug;
const vk = buildVulkanWrapper(b, target, optimize);
const tracy = b.dependency("zig-tracy", .{ const tracy = b.dependency("zig-tracy", .{
.target = target, .target = target,
.optimize = optimize, .optimize = optimize,
@ -67,6 +83,7 @@ pub fn build(b: *Build) void {
l.root_module.addImport("assets", assets_mod); l.root_module.addImport("assets", assets_mod);
l.root_module.addImport("asset_manifest", asset_manifest_mod); l.root_module.addImport("asset_manifest", asset_manifest_mod);
l.root_module.addImport("tracy", tracy.module("tracy")); l.root_module.addImport("tracy", tracy.module("tracy"));
l.root_module.addImport("vk", vk);
l.linkLibrary(tracy.artifact("tracy")); l.linkLibrary(tracy.artifact("tracy"));
} }
@ -153,7 +170,6 @@ pub fn build(b: *Build) void {
const asset_extensions = [_][]const u8{ const asset_extensions = [_][]const u8{
"obj", "obj",
"glsl",
"prog", "prog",
"png", "png",
"dds", "dds",
@ -226,8 +242,14 @@ fn buildAssetCompiler(b: *Build, optimize: std.builtin.OptimizeMode, assets_mod:
//.formats = @as([]const u8, "3DS,3MF,AC,AMF,ASE,Assbin,Assjson,Assxml,B3D,Blender,BVH,C4D,COB,Collada,CSM,DXF,FBX,glTF,glTF2,HMP,IFC,Irr,LWO,LWS,M3D,MD2,MD3,MD5,MDC,MDL,MMD,MS3D,NDO,NFF,Obj,OFF,Ogre,OpenGEX,Ply,Q3BSP,Q3D,Raw,SIB,SMD,Step,STEPParser,STL,Terragen,Unreal,X,X3D,XGL"), //.formats = @as([]const u8, "3DS,3MF,AC,AMF,ASE,Assbin,Assjson,Assxml,B3D,Blender,BVH,C4D,COB,Collada,CSM,DXF,FBX,glTF,glTF2,HMP,IFC,Irr,LWO,LWS,M3D,MD2,MD3,MD5,MDC,MDL,MMD,MS3D,NDO,NFF,Obj,OFF,Ogre,OpenGEX,Ply,Q3BSP,Q3D,Raw,SIB,SMD,Step,STEPParser,STL,Terragen,Unreal,X,X3D,XGL"),
.formats = @as([]const u8, "Obj,FBX,glTF,glTF2,Blend"), .formats = @as([]const u8, "Obj,FBX,glTF,glTF2,Blend"),
}); });
const mach_dxcompiler_dep = b.dependency("mach_dxcompiler", .{
.target = b.graph.host,
.optimize = optimize,
.from_source = true,
.spirv = true,
.skip_tests = true,
});
const zalgebra_dep = b.dependency("zalgebra", .{}); const zalgebra_dep = b.dependency("zalgebra", .{});
const assimp_lib = assimp_dep.artifact("assimp"); const assimp_lib = assimp_dep.artifact("assimp");
const assetc = b.addExecutable(.{ const assetc = b.addExecutable(.{
@ -247,8 +269,11 @@ fn buildAssetCompiler(b: *Build, optimize: std.builtin.OptimizeMode, assets_mod:
assetc.linkSystemLibrary("ispc_texcomp"); assetc.linkSystemLibrary("ispc_texcomp");
const zalgebra_mod = zalgebra_dep.module("zalgebra"); const zalgebra_mod = zalgebra_dep.module("zalgebra");
const mach_dxcompiler_mod = mach_dxcompiler_dep.module("mach-dxcompiler");
const formats_mod = b.addModule("formats", .{ .root_source_file = b.path("src/formats.zig") }); const formats_mod = b.addModule("formats", .{ .root_source_file = b.path("src/formats.zig") });
formats_mod.addImport("zalgebra", zalgebra_mod); formats_mod.addImport("zalgebra", zalgebra_mod);
formats_mod.addImport("mach-dxcompiler", mach_dxcompiler_mod);
formats_mod.addImport("assets", assets_mod); formats_mod.addImport("assets", assets_mod);
assetc.root_module.addImport("formats", formats_mod); assetc.root_module.addImport("formats", formats_mod);
assetc.root_module.addImport("zalgebra", zalgebra_mod); assetc.root_module.addImport("zalgebra", zalgebra_mod);

View File

@ -31,6 +31,18 @@
.url = "https://github.com/sergeypdev/zig-tracy/tarball/2b818574810a66deacc424298c1a7679ca6e4375", .url = "https://github.com/sergeypdev/zig-tracy/tarball/2b818574810a66deacc424298c1a7679ca6e4375",
.hash = "1220638bc94d67225a620e1abd71d85b299c8b764490fd51233ed73d76ee44cc5835", .hash = "1220638bc94d67225a620e1abd71d85b299c8b764490fd51233ed73d76ee44cc5835",
}, },
.vulkan_headers = .{
.url = "https://github.com/KhronosGroup/Vulkan-Headers/archive/v1.3.283.tar.gz",
.hash = "1220a7e73d72a0d56bc2a65f9d8999a7c019e42260a0744c408d1cded111bc205e10",
},
.vulkan_zig = .{
.url = "https://github.com/Snektron/vulkan-zig/tarball/66b7b773bb61e2102025f2d5ff0ae8c5f53e19cc",
.hash = "12208958f173b8b81bfac797955f0416ab38b21d1f69d4ebf6c7ca460a828a41cd45",
},
.mach_dxcompiler = .{
.url = "https://github.com/sinnwrig/mach-dxcompiler/tarball/c3dfe92f3f04d4a3262dbc1a71f0016b9af92eb4",
.hash = "12202f48e7cf06b1f2ecfd84f16effbd5bb9d644ea17e8a6144b4301a4dea198cf9c",
},
}, },
.paths = .{ .paths = .{
// This makes *all* files, recursively, included in this package. It is generally // This makes *all* files, recursively, included in this package. It is generally

View File

@ -1596,18 +1596,18 @@ const VertexBufferHeap = struct {
var index_buddy = try BuddyAllocator.init(allocator, 4096, 13); var index_buddy = try BuddyAllocator.init(allocator, 4096, 13);
errdefer index_buddy.deinit(); errdefer index_buddy.deinit();
const vertex_buf_size = vertex_buddy.getSize(); // const vertex_buf_size = vertex_buddy.getSize();
const index_buf_size = index_buddy.getSize(); // const index_buf_size = index_buddy.getSize();
var bufs = [_]gl.GLuint{ 0, 0, 0, 0, 0 }; const bufs = [_]gl.GLuint{ 0, 0, 0, 0, 0 };
gl.createBuffers(bufs.len, &bufs); // gl.createBuffers(bufs.len, &bufs);
errdefer gl.deleteBuffers(bufs.len, &bufs); // errdefer gl.deleteBuffers(bufs.len, &bufs);
for (bufs) |buf| { // for (bufs) |buf| {
if (buf == 0) { // if (buf == 0) {
return error.BufferAllocationFailed; // return error.BufferAllocationFailed;
} // }
} // }
const vertices = Buffer.init(bufs[0], @sizeOf(formats.Vector3)); const vertices = Buffer.init(bufs[0], @sizeOf(formats.Vector3));
const normals = Buffer.init(bufs[1], @sizeOf(formats.Vector3)); const normals = Buffer.init(bufs[1], @sizeOf(formats.Vector3));
@ -1615,36 +1615,36 @@ const VertexBufferHeap = struct {
const uvs = Buffer.init(bufs[3], @sizeOf(formats.Vector2)); const uvs = Buffer.init(bufs[3], @sizeOf(formats.Vector2));
const indices = Buffer.init(bufs[4], @sizeOf(formats.Index)); const indices = Buffer.init(bufs[4], @sizeOf(formats.Index));
gl.namedBufferStorage( // gl.namedBufferStorage(
vertices.buffer, // vertices.buffer,
@intCast(vertex_buf_size * @sizeOf(formats.Vector3)), // @intCast(vertex_buf_size * @sizeOf(formats.Vector3)),
null, // null,
gl.DYNAMIC_STORAGE_BIT, // gl.DYNAMIC_STORAGE_BIT,
); // );
gl.namedBufferStorage( // gl.namedBufferStorage(
normals.buffer, // normals.buffer,
@intCast(vertex_buf_size * @sizeOf(formats.Vector3)), // @intCast(vertex_buf_size * @sizeOf(formats.Vector3)),
null, // null,
gl.DYNAMIC_STORAGE_BIT, // gl.DYNAMIC_STORAGE_BIT,
); // );
gl.namedBufferStorage( // gl.namedBufferStorage(
tangents.buffer, // tangents.buffer,
@intCast(vertex_buf_size * @sizeOf(formats.Vector3)), // @intCast(vertex_buf_size * @sizeOf(formats.Vector3)),
null, // null,
gl.DYNAMIC_STORAGE_BIT, // gl.DYNAMIC_STORAGE_BIT,
); // );
gl.namedBufferStorage( // gl.namedBufferStorage(
uvs.buffer, // uvs.buffer,
@intCast(vertex_buf_size * @sizeOf(formats.Vector2)), // @intCast(vertex_buf_size * @sizeOf(formats.Vector2)),
null, // null,
gl.DYNAMIC_STORAGE_BIT, // gl.DYNAMIC_STORAGE_BIT,
); // );
gl.namedBufferStorage( // gl.namedBufferStorage(
indices.buffer, // indices.buffer,
@intCast(index_buf_size * @sizeOf(formats.Index)), // @intCast(index_buf_size * @sizeOf(formats.Index)),
null, // null,
gl.DYNAMIC_STORAGE_BIT, // gl.DYNAMIC_STORAGE_BIT,
); // );
return .{ return .{
.vertex_buddy = vertex_buddy, .vertex_buddy = vertex_buddy,

534
src/GraphicsContext.zig Normal file
View File

@ -0,0 +1,534 @@
const std = @import("std");
const vk = @import("vk");
const c = @import("sdl.zig");
pub const GraphicsContext = @This();
const apis: []const vk.ApiInfo = &.{
vk.features.version_1_0,
vk.features.version_1_1,
vk.features.version_1_2,
vk.features.version_1_3,
vk.extensions.khr_surface,
vk.extensions.khr_swapchain,
};
const BaseDispatch = vk.BaseWrapper(apis);
const InstanceDispatch = vk.InstanceWrapper(apis);
const Instance = vk.InstanceProxy(apis);
const Device = vk.DeviceProxy(apis);
const DeviceDispatch = Device.Wrapper;
const CommandBuffer = vk.CommandBufferProxy(apis);
const device_extensions = [_][:0]const u8{
vk.extensions.khr_swapchain.name,
};
const vk_layers = [_][:0]const u8{"VK_LAYER_KHRONOS_validation"};
const MAX_FRAME_LAG = 3;
allocator: std.mem.Allocator = undefined,
window: *c.SDL_Window = undefined,
vkb: BaseDispatch = undefined,
vki: InstanceDispatch = undefined,
vkd: DeviceDispatch = undefined,
device_info: SelectedPhysicalDevice = undefined,
instance: Instance = undefined,
device: Device = undefined,
queues: DeviceQueues = undefined,
surface: vk.SurfaceKHR = .null_handle,
swapchain: vk.SwapchainKHR = .null_handle,
swapchain_extent: vk.Extent2D = .{ .width = 0, .height = 0 },
swapchain_images: []vk.Image = &.{},
// NOTE: TEST
frame: u32 = 0,
frame_syncs: [MAX_FRAME_LAG]Sync = [1]Sync{.{}} ** MAX_FRAME_LAG,
command_pool: vk.CommandPool = .null_handle,
const Sync = struct {
acquire_swapchain_image: vk.Semaphore = .null_handle,
draw_sema: vk.Semaphore = .null_handle,
draw_fence: vk.Fence = .null_handle,
pub fn waitForDrawAndReset(self: *Sync, gc: *GraphicsContext) !void {
_ = try gc.device.waitForFences(1, &.{self.draw_fence}, vk.TRUE, std.math.maxInt(u64));
try gc.device.resetFences(1, &.{self.draw_fence});
}
};
pub fn init(self: *GraphicsContext, allocator: std.mem.Allocator, window: *c.SDL_Window) !void {
self.allocator = allocator;
self.window = window;
var scratch: [4096]u8 = undefined;
var fba = std.heap.FixedBufferAllocator.init(&scratch);
const vkGetInstanceProcAddr: vk.PfnGetInstanceProcAddr = @ptrCast(c.SDL_Vulkan_GetVkGetInstanceProcAddr());
var sdl_instance_ext_count: c_uint = 0;
if (c.SDL_Vulkan_GetInstanceExtensions(window, &sdl_instance_ext_count, null) == c.SDL_FALSE) {
std.debug.print("SDL_Vulkan_GetInstanceExtensions: get count {s}\n", .{c.SDL_GetError()});
return error.GetSDLExtensions;
}
const sdl_instance_ext_names = try fba.allocator().alloc([*:0]const u8, sdl_instance_ext_count);
if (c.SDL_Vulkan_GetInstanceExtensions(window, &sdl_instance_ext_count, @ptrCast(sdl_instance_ext_names.ptr)) == c.SDL_FALSE) {
std.debug.print("SDL_Vulkan_GetInstanceExtensions: get names {s}\n", .{c.SDL_GetError()});
return error.GetSDLExtensions;
}
std.debug.print("SDL Extensions: {s}\n", .{sdl_instance_ext_names});
self.vkb = try BaseDispatch.load(vkGetInstanceProcAddr);
const instance_handle = try self.vkb.createInstance(&vk.InstanceCreateInfo{
.p_application_info = &vk.ApplicationInfo{
.api_version = vk.API_VERSION_1_3,
.application_version = 0,
.engine_version = 0,
},
.pp_enabled_layer_names = @ptrCast((&vk_layers).ptr),
.enabled_layer_count = @intCast(vk_layers.len),
.enabled_extension_count = @intCast(sdl_instance_ext_names.len),
.pp_enabled_extension_names = sdl_instance_ext_names.ptr,
}, null);
self.vki = try InstanceDispatch.load(instance_handle, vkGetInstanceProcAddr);
errdefer self.vki.destroyInstance(instance_handle, null);
self.instance = Instance.init(instance_handle, &self.vki);
var sdl_vksurface: c.VkSurfaceKHR = null;
if (c.SDL_Vulkan_CreateSurface(window, @as(*c.VkInstance, @ptrCast(&self.instance.handle)).*, &sdl_vksurface) == c.SDL_FALSE) {
std.debug.print("SDL_Vulkan_CreateSurface: {s}\n", .{c.SDL_GetError()});
return error.SDLVulkanCreateSurface;
}
std.debug.assert(sdl_vksurface != null);
self.surface = @as(*vk.SurfaceKHR, @ptrCast(&sdl_vksurface)).*;
const physical_devices = try self.instance.enumeratePhysicalDevicesAlloc(fba.allocator());
self.device_info = try selectPhysicalDevice(self.instance, self.surface, physical_devices);
const queue_config = try selectQueues(self.instance, self.device_info.physical_device);
const device_create_config = vk.DeviceCreateInfo{
.p_next = @ptrCast(&vk.PhysicalDeviceSynchronization2Features{ .synchronization_2 = vk.TRUE }),
.p_queue_create_infos = &queue_config.queue_create_info,
.queue_create_info_count = queue_config.queue_count,
.p_enabled_features = &self.device_info.features,
.pp_enabled_layer_names = @ptrCast((&vk_layers).ptr),
.enabled_layer_count = @intCast(vk_layers.len),
.pp_enabled_extension_names = @ptrCast((&device_extensions).ptr),
.enabled_extension_count = @intCast(device_extensions.len),
};
const device_handle = try self.instance.createDevice(self.device_info.physical_device, &device_create_config, null);
self.vkd = try DeviceDispatch.load(device_handle, self.instance.wrapper.dispatch.vkGetDeviceProcAddr);
errdefer self.vkd.destroyDevice(device_handle, null);
self.device = Device.init(device_handle, &self.vkd);
try self.maybeResizeSwapchain();
errdefer self.device.destroySwapchainKHR(self.swapchain, null);
// TODO: handle the case when different queue instance map to the same queue
const graphics_queue = QueueInstance{
.device = self.device,
.family = queue_config.graphics.family,
.handle = self.device.getDeviceQueue(queue_config.graphics.family, queue_config.graphics.index),
};
const compute_queue = QueueInstance{
.device = self.device,
.family = queue_config.compute.family,
.handle = self.device.getDeviceQueue(queue_config.graphics.family, queue_config.compute.index),
};
const host_to_device_queue = QueueInstance{
.device = self.device,
.family = queue_config.host_to_device.family,
.handle = self.device.getDeviceQueue(queue_config.graphics.family, queue_config.host_to_device.index),
};
const device_to_host_queue = QueueInstance{
.device = self.device,
.family = queue_config.device_to_host.family,
.handle = self.device.getDeviceQueue(queue_config.graphics.family, queue_config.device_to_host.index),
};
self.queues = DeviceQueues{
.graphics = graphics_queue,
.compute = compute_queue,
.host_to_device = host_to_device_queue,
.device_to_host = device_to_host_queue,
};
for (0..MAX_FRAME_LAG) |i| {
self.frame_syncs[i].acquire_swapchain_image = try self.device.createSemaphore(&.{}, null);
self.frame_syncs[i].draw_sema = try self.device.createSemaphore(&.{}, null);
self.frame_syncs[i].draw_fence = try self.device.createFence(&.{ .flags = .{ .signaled_bit = true } }, null);
}
self.command_pool = try self.queues.graphics.createCommandPool(.{});
}
pub fn draw(self: *GraphicsContext) !void {
var cmd_bufs = [_]vk.CommandBuffer{.null_handle};
try self.device.allocateCommandBuffers(&.{
.command_pool = self.command_pool,
.level = .primary,
.command_buffer_count = cmd_bufs.len,
}, &cmd_bufs);
const test_cmd_buf = CommandBuffer.init(cmd_bufs[0], &self.vkd);
const sync = &self.frame_syncs[self.frame];
try sync.waitForDrawAndReset(self);
// Move this out into a separate func
const swapchain_image_index: u32 = blk: {
var found = false;
var swapchain_img: u32 = 0;
try self.maybeResizeSwapchain();
while (!found) {
const acquire_result = try self.device.acquireNextImageKHR(self.swapchain, std.math.maxInt(u64), sync.acquire_swapchain_image, .null_handle);
switch (acquire_result.result) {
.success, .suboptimal_khr => {
swapchain_img = acquire_result.image_index;
found = true;
},
.error_out_of_date_khr => {
// TODO: resize swapchain
std.debug.print("Out of date swapchain\n", .{});
try self.maybeResizeSwapchain();
},
.error_surface_lost_khr => {
// TODO: recreate surface
return error.SurfaceLost;
},
.not_ready => return error.SwapchainImageNotReady,
.timeout => return error.SwapchainImageTimeout,
else => {
std.debug.print("Unexpected value: {}\n", .{acquire_result.result});
@panic("Unexpected");
},
}
}
break :blk swapchain_img;
};
const current_image = self.swapchain_images[swapchain_image_index];
try test_cmd_buf.beginCommandBuffer(&.{});
{
{
const img_barrier = vk.ImageMemoryBarrier2{
.image = current_image,
.old_layout = .undefined,
.new_layout = .transfer_dst_optimal,
.src_access_mask = .{},
.dst_access_mask = .{},
.src_queue_family_index = self.queues.graphics.family,
.dst_queue_family_index = self.queues.graphics.family,
.subresource_range = .{
.aspect_mask = .{ .color_bit = true },
.base_array_layer = 0,
.base_mip_level = 0,
.layer_count = 1,
.level_count = 1,
},
};
test_cmd_buf.pipelineBarrier2(&.{
.p_image_memory_barriers = &.{img_barrier},
.image_memory_barrier_count = 1,
});
}
test_cmd_buf.clearColorImage(current_image, .transfer_dst_optimal, &.{ .float_32 = .{ 0.8, 0.7, 0.6, 1.0 } }, 1, &.{.{
.aspect_mask = .{ .color_bit = true },
.base_array_layer = 0,
.base_mip_level = 0,
.layer_count = 1,
.level_count = 1,
}});
{
const img_barrier = vk.ImageMemoryBarrier2{
.image = current_image,
.old_layout = .transfer_dst_optimal,
.new_layout = .present_src_khr,
.src_access_mask = .{},
.dst_access_mask = .{},
.src_queue_family_index = self.queues.graphics.family,
.dst_queue_family_index = self.queues.graphics.family,
.subresource_range = .{
.aspect_mask = .{ .color_bit = true },
.base_array_layer = 0,
.base_mip_level = 0,
.layer_count = 1,
.level_count = 1,
},
};
test_cmd_buf.pipelineBarrier2(&.{
.p_image_memory_barriers = &.{img_barrier},
.image_memory_barrier_count = 1,
});
}
}
try test_cmd_buf.endCommandBuffer();
try self.queues.graphics.submit(
&SubmitInfo{
.wait_semaphores = &.{sync.acquire_swapchain_image},
.wait_dst_stage_mask = &.{.{ .transfer_bit = true }},
.command_buffers = &.{test_cmd_buf.handle},
.signal_semaphores = &.{sync.draw_sema},
},
sync.draw_fence,
);
_ = try self.device.queuePresentKHR(self.queues.graphics.handle, &.{
.swapchain_count = 1,
.wait_semaphore_count = 1,
.p_wait_semaphores = &.{sync.draw_sema},
.p_swapchains = &.{self.swapchain},
.p_image_indices = &.{swapchain_image_index},
});
self.frame = (self.frame + 1) % MAX_FRAME_LAG;
}
fn maybeResizeSwapchain(self: *GraphicsContext) !void {
var width: c_int = 0;
var height: c_int = 0;
c.SDL_Vulkan_GetDrawableSize(self.window, &width, &height);
const new_extent = vk.Extent2D{ .width = @intCast(width), .height = @intCast(height) };
if (self.swapchain_extent.width == new_extent.width and self.swapchain_extent.height == new_extent.height) {
return;
}
if (self.swapchain_images.len > 0) {
self.allocator.free(self.swapchain_images);
self.swapchain_images = &.{};
}
self.swapchain_extent = new_extent;
const surface_caps = self.device_info.surface_capabilities;
self.swapchain = try self.device.createSwapchainKHR(&.{
.surface = self.surface,
.min_image_count = std.math.clamp(3, surface_caps.min_image_count, if (surface_caps.max_image_count == 0) std.math.maxInt(u32) else surface_caps.max_image_count),
.image_format = .r8g8b8a8_unorm, // tonemapping handles srgb
.image_color_space = .srgb_nonlinear_khr,
.image_extent = self.swapchain_extent,
.image_array_layers = 1,
.image_usage = .{
.color_attachment_bit = true,
.transfer_dst_bit = true,
},
.image_sharing_mode = .exclusive,
.present_mode = .fifo_khr, // required to be supported
.pre_transform = surface_caps.current_transform,
.composite_alpha = .{ .opaque_bit_khr = true },
.clipped = vk.TRUE,
.old_swapchain = self.swapchain,
}, null);
self.swapchain_images = try self.device.getSwapchainImagesAllocKHR(self.swapchain, self.allocator);
}
pub const DeviceQueues = struct {
graphics: QueueInstance,
compute: QueueInstance,
host_to_device: QueueInstance,
device_to_host: QueueInstance,
};
pub const SubmitInfo = struct {
wait_semaphores: []const vk.Semaphore = &.{},
wait_dst_stage_mask: []const vk.PipelineStageFlags = &.{},
command_buffers: []const vk.CommandBuffer = &.{},
signal_semaphores: []const vk.Semaphore = &.{},
};
pub const QueueInstance = struct {
const Self = @This();
mu: std.Thread.Mutex = .{},
device: Device,
handle: vk.Queue,
family: u32,
pub fn createCommandPool(self: *Self, flags: vk.CommandPoolCreateFlags) !vk.CommandPool {
return self.device.createCommandPool(&.{
.flags = flags,
.queue_family_index = self.family,
}, null);
}
pub fn submit(self: *Self, info: *const SubmitInfo, fence: vk.Fence) Device.QueueSubmitError!void {
std.debug.assert(info.wait_semaphores.len == info.wait_dst_stage_mask.len);
var vk_submit_info = vk.SubmitInfo{};
if (info.wait_semaphores.len > 0) {
vk_submit_info.p_wait_semaphores = info.wait_semaphores.ptr;
vk_submit_info.p_wait_dst_stage_mask = info.wait_dst_stage_mask.ptr;
vk_submit_info.wait_semaphore_count = @intCast(info.wait_semaphores.len);
}
if (info.command_buffers.len > 0) {
vk_submit_info.p_command_buffers = info.command_buffers.ptr;
vk_submit_info.command_buffer_count = @intCast(info.command_buffers.len);
}
if (info.signal_semaphores.len > 0) {
vk_submit_info.p_signal_semaphores = info.signal_semaphores.ptr;
vk_submit_info.signal_semaphore_count = @intCast(info.signal_semaphores.len);
}
try self.submitVK(&.{vk_submit_info}, fence);
}
pub fn submitVK(self: *Self, infos: []const vk.SubmitInfo, fence: vk.Fence) Device.QueueSubmitError!void {
self.mu.lock();
defer self.mu.unlock();
try self.device.queueSubmit(self.handle, @intCast(infos.len), infos.ptr, fence);
}
};
const SelectedPhysicalDevice = struct {
physical_device: vk.PhysicalDevice,
properties: vk.PhysicalDeviceProperties,
features: vk.PhysicalDeviceFeatures,
surface_capabilities: vk.SurfaceCapabilitiesKHR,
};
fn selectPhysicalDevice(vki: Instance, surface: vk.SurfaceKHR, devices: []vk.PhysicalDevice) !SelectedPhysicalDevice {
// TODO: select suitable physical device, allow overriding using some user config
for (devices) |device| {
const props = vki.getPhysicalDeviceProperties(device);
const features = vki.getPhysicalDeviceFeatures(device);
const surface_caps = try vki.getPhysicalDeviceSurfaceCapabilitiesKHR(device, surface);
return SelectedPhysicalDevice{
.physical_device = device,
.properties = props,
.features = features,
.surface_capabilities = surface_caps,
};
}
return error.NoDeviceFound;
}
const DeviceQueueConfig = struct {
const Config = struct {
family: u32,
index: u32,
};
queue_create_info: [4]vk.DeviceQueueCreateInfo = undefined,
queue_count: u32 = 0,
graphics: Config,
compute: Config,
host_to_device: Config,
device_to_host: Config,
};
// Hardcode queue priorities, no idea why I would need to use them
const queue_priorities = [_]f32{ 1.0, 1.0, 1.0, 1.0, 1.0 };
fn selectQueues(instance: Instance, device: vk.PhysicalDevice) !DeviceQueueConfig {
var scratch: [1024]u8 = undefined;
var fba = std.heap.FixedBufferAllocator.init(&scratch);
const queue_family_props = try instance.getPhysicalDeviceQueueFamilyPropertiesAlloc(device, fba.allocator());
if (queue_family_props.len == 0) {
return error.NoQueues;
}
var queue_create_info: [4]vk.DeviceQueueCreateInfo = undefined;
var queue_count: u32 = 0;
var graphics: ?DeviceQueueConfig.Config = null;
var compute: ?DeviceQueueConfig.Config = null;
var host_to_device: ?DeviceQueueConfig.Config = null;
var device_to_host: ?DeviceQueueConfig.Config = null;
// We're on Intel most likely, just a single queue for everything :(
if (queue_family_props.len == 1) {
if (!queue_family_props[0].queue_flags.contains(.{ .graphics_bit = true, .compute_bit = true, .transfer_bit = true })) {
return error.InvalidQueue;
}
graphics = .{ .family = 0, .index = 0 };
compute = graphics;
device_to_host = graphics;
host_to_device = graphics;
queue_create_info[0] = .{
.queue_family_index = 0,
.queue_count = 1,
.p_queue_priorities = &queue_priorities,
};
queue_count = 1;
} else {
for (queue_family_props, 0..) |props, family_idx| {
// Jackpot, generous Jensen provided us with an all powerfull queue family, use it for everything
if (props.queue_flags.contains(.{ .graphics_bit = true, .compute_bit = true, .transfer_bit = true }) and props.queue_count >= 4) {
graphics = .{
.family = @intCast(family_idx),
.index = 0,
};
compute = .{
.family = @intCast(family_idx),
.index = 1,
};
host_to_device = .{
.family = @intCast(family_idx),
.index = 2,
};
device_to_host = .{
.family = @intCast(family_idx),
.index = 3,
};
queue_create_info[0] = .{
.queue_family_index = 0,
.queue_count = 4,
.p_queue_priorities = &queue_priorities,
};
queue_count = 1;
break;
}
// TODO: make queue create info for AMD
// Probably AMD, one graphics+compute queue, 2 separate compute queues, one pure transfer queue
if (props.queue_flags.graphics_bit) {
graphics = .{
.family = @intCast(family_idx),
.index = 0,
};
}
if (props.queue_flags.compute_bit and (compute == null or !props.queue_flags.graphics_bit)) {
compute = .{
.family = @intCast(family_idx),
.index = 0,
};
}
if (props.queue_flags.transfer_bit and (host_to_device == null or !props.queue_flags.graphics_bit or !props.queue_flags.compute_bit)) {
device_to_host = .{
.family = @intCast(family_idx),
.index = 0,
};
host_to_device = .{
.family = @intCast(family_idx),
.index = 0,
};
}
}
}
if (graphics == null or compute == null or device_to_host == null or host_to_device == null) {
return error.MissingQueueFeatures;
}
return .{
.queue_create_info = queue_create_info,
.queue_count = queue_count,
.graphics = graphics.?,
.compute = compute.?,
.host_to_device = host_to_device.?,
.device_to_host = device_to_host.?,
};
}

View File

@ -9,6 +9,9 @@ const math = @import("math.zig");
const formats = @import("formats.zig"); const formats = @import("formats.zig");
const AABB = AssetManager.AABB; // TODO: move AABB out of formats pls const AABB = AssetManager.AABB; // TODO: move AABB out of formats pls
const tracy = @import("tracy"); const tracy = @import("tracy");
const GraphicsContext = @import("GraphicsContext.zig");
const gc = GraphicsContext.init();
const za = @import("zalgebra"); const za = @import("zalgebra");
const Vec2 = za.Vec2; const Vec2 = za.Vec2;

View File

@ -3,7 +3,7 @@ const globals = @import("globals.zig");
const InitMemory = globals.InitMemory; const InitMemory = globals.InitMemory;
const GameMemory = globals.GameMemory; const GameMemory = globals.GameMemory;
const c = @import("sdl.zig"); const c = @import("sdl.zig");
const gl = @import("gl.zig"); // const gl = @import("gl.zig");
const AssetManager = @import("AssetManager.zig"); const AssetManager = @import("AssetManager.zig");
const Render = @import("Render.zig"); const Render = @import("Render.zig");
const formats = @import("formats.zig"); const formats = @import("formats.zig");
@ -16,6 +16,7 @@ const Quat = za.Quat;
const a = @import("asset_manifest"); const a = @import("asset_manifest");
const windows = std.os.windows; const windows = std.os.windows;
const tracy = @import("tracy"); const tracy = @import("tracy");
const GraphicsContext = @import("GraphicsContext.zig");
pub extern "dwmapi" fn DwmEnableMMCSS(fEnableMMCSS: windows.BOOL) callconv(windows.WINAPI) windows.HRESULT; pub extern "dwmapi" fn DwmEnableMMCSS(fEnableMMCSS: windows.BOOL) callconv(windows.WINAPI) windows.HRESULT;
pub extern "dwmapi" fn DwmFlush() callconv(windows.WINAPI) void; pub extern "dwmapi" fn DwmFlush() callconv(windows.WINAPI) void;
@ -42,19 +43,19 @@ fn game_init_window_err(global_allocator: std.mem.Allocator) !void {
// _ = DwmEnableMMCSS(1); // _ = DwmEnableMMCSS(1);
try sdl_try(c.SDL_Init(c.SDL_INIT_EVERYTHING)); try sdl_try(c.SDL_Init(c.SDL_INIT_EVERYTHING));
try sdl_try(c.SDL_GL_SetAttribute(c.SDL_GL_DOUBLEBUFFER, 1)); // try sdl_try(c.SDL_GL_SetAttribute(c.SDL_GL_DOUBLEBUFFER, 1));
try sdl_try(c.SDL_GL_SetAttribute(c.SDL_GL_CONTEXT_MAJOR_VERSION, 4)); // try sdl_try(c.SDL_GL_SetAttribute(c.SDL_GL_CONTEXT_MAJOR_VERSION, 4));
try sdl_try(c.SDL_GL_SetAttribute(c.SDL_GL_CONTEXT_MINOR_VERSION, 5)); // try sdl_try(c.SDL_GL_SetAttribute(c.SDL_GL_CONTEXT_MINOR_VERSION, 5));
try sdl_try(c.SDL_GL_SetAttribute(c.SDL_GL_CONTEXT_PROFILE_MASK, c.SDL_GL_CONTEXT_PROFILE_CORE)); // try sdl_try(c.SDL_GL_SetAttribute(c.SDL_GL_CONTEXT_PROFILE_MASK, c.SDL_GL_CONTEXT_PROFILE_CORE));
try sdl_try(c.SDL_GL_SetAttribute(c.SDL_GL_FRAMEBUFFER_SRGB_CAPABLE, 0)); // try sdl_try(c.SDL_GL_SetAttribute(c.SDL_GL_FRAMEBUFFER_SRGB_CAPABLE, 0));
const maybe_window = c.SDL_CreateWindow( const maybe_window = c.SDL_CreateWindow(
"Learn OpenGL with Zig!", "Vulkan Engine",
c.SDL_WINDOWPOS_UNDEFINED, c.SDL_WINDOWPOS_UNDEFINED,
c.SDL_WINDOWPOS_UNDEFINED, c.SDL_WINDOWPOS_UNDEFINED,
globals.DEFAULT_WIDTH, globals.DEFAULT_WIDTH,
globals.DEFAULT_HEIGHT, globals.DEFAULT_HEIGHT,
c.SDL_WINDOW_SHOWN | c.SDL_WINDOW_OPENGL | c.SDL_WINDOW_ALLOW_HIGHDPI | c.SDL_WINDOW_RESIZABLE, c.SDL_WINDOW_SHOWN | c.SDL_WINDOW_ALLOW_HIGHDPI | c.SDL_WINDOW_RESIZABLE | c.SDL_WINDOW_VULKAN,
); );
if (maybe_window == null) { if (maybe_window == null) {
std.log.err("SDL Error: {s}", .{c.SDL_GetError()}); std.log.err("SDL Error: {s}", .{c.SDL_GetError()});
@ -62,26 +63,28 @@ fn game_init_window_err(global_allocator: std.mem.Allocator) !void {
} }
const window = maybe_window.?; const window = maybe_window.?;
const context = c.SDL_GL_CreateContext(window); // const context = c.SDL_GL_CreateContext(window);
try sdl_try(c.SDL_GL_SetSwapInterval(0)); // try sdl_try(c.SDL_GL_SetSwapInterval(0));
globals.g_init = try global_allocator.create(InitMemory); globals.g_init = try global_allocator.create(InitMemory);
globals.g_init_exists = true; globals.g_init_exists = true;
globals.g_init.* = .{ globals.g_init.* = .{
.global_allocator = global_allocator, .global_allocator = global_allocator,
.window = window, .window = window,
.context = context, .context = null,
.width = globals.DEFAULT_WIDTH, .width = globals.DEFAULT_WIDTH,
.height = globals.DEFAULT_HEIGHT, .height = globals.DEFAULT_HEIGHT,
}; };
try globals.g_init.gc.init(global_allocator, window);
const version = &globals.g_init.syswm_info.version; const version = &globals.g_init.syswm_info.version;
version.major = c.SDL_MAJOR_VERSION; version.major = c.SDL_MAJOR_VERSION;
version.minor = c.SDL_MINOR_VERSION; version.minor = c.SDL_MINOR_VERSION;
version.patch = c.SDL_PATCHLEVEL; version.patch = c.SDL_PATCHLEVEL;
c.SDL_GL_GetDrawableSize(window, &globals.g_init.width, &globals.g_init.height); // c.SDL_GL_GetDrawableSize(window, &globals.g_init.width, &globals.g_init.height);
if (c.SDL_GetWindowWMInfo(window, &globals.g_init.syswm_info) == 0) { if (c.SDL_GetWindowWMInfo(window, &globals.g_init.syswm_info) == 0) {
const err = c.SDL_GetError(); const err = c.SDL_GetError();
@ -98,70 +101,70 @@ export fn game_init_window(global_allocator: *std.mem.Allocator) void {
}; };
} }
fn loadGL() void { // fn loadGL() void {
const getProcAddress = struct { // const getProcAddress = struct {
fn getProcAddress(ctx: @TypeOf(null), proc: [:0]const u8) ?gl.FunctionPointer { // fn getProcAddress(ctx: @TypeOf(null), proc: [:0]const u8) ?gl.FunctionPointer {
_ = ctx; // _ = ctx;
return @ptrCast(c.SDL_GL_GetProcAddress(proc)); // return @ptrCast(c.SDL_GL_GetProcAddress(proc));
} // }
}.getProcAddress; // }.getProcAddress;
gl.load(null, getProcAddress) catch |err| { // gl.load(null, getProcAddress) catch |err| {
std.log.debug("Failed to load gl funcs {}\n", .{err}); // std.log.debug("Failed to load gl funcs {}\n", .{err});
@panic("gl.load"); // @panic("gl.load");
}; // };
gl.GL_ARB_bindless_texture.load(null, getProcAddress) catch |err| { // gl.GL_ARB_bindless_texture.load(null, getProcAddress) catch |err| {
std.log.debug("Failed to load gl funcs GL_ARB_bindless_texture {}\n", .{err}); // std.log.debug("Failed to load gl funcs GL_ARB_bindless_texture {}\n", .{err});
@panic("gl.load"); // @panic("gl.load");
}; // };
gl.debugMessageCallback(glDebugCallback, null); // gl.debugMessageCallback(glDebugCallback, null);
// gl.enable(gl.DEBUG_OUTPUT); // // gl.enable(gl.DEBUG_OUTPUT);
// gl.enable(gl.DEBUG_OUTPUT_SYNCHRONOUS); // // gl.enable(gl.DEBUG_OUTPUT_SYNCHRONOUS);
} // }
//
fn glDebugCallback(source: gl.GLenum, _type: gl.GLenum, id: gl.GLuint, severity: gl.GLenum, length: gl.GLsizei, message: [*:0]const u8, userParam: ?*anyopaque) callconv(.C) void { // fn glDebugCallback(source: gl.GLenum, _type: gl.GLenum, id: gl.GLuint, severity: gl.GLenum, length: gl.GLsizei, message: [*:0]const u8, userParam: ?*anyopaque) callconv(.C) void {
_ = userParam; // autofix // _ = userParam; // autofix
const source_str = switch (source) { // const source_str = switch (source) {
gl.DEBUG_SOURCE_API => "API", // gl.DEBUG_SOURCE_API => "API",
gl.DEBUG_SOURCE_WINDOW_SYSTEM => "WindowSystem", // gl.DEBUG_SOURCE_WINDOW_SYSTEM => "WindowSystem",
gl.DEBUG_SOURCE_APPLICATION => "App", // gl.DEBUG_SOURCE_APPLICATION => "App",
gl.DEBUG_SOURCE_SHADER_COMPILER => "ShaderCompiler", // gl.DEBUG_SOURCE_SHADER_COMPILER => "ShaderCompiler",
gl.DEBUG_SOURCE_THIRD_PARTY => "ThirdParty", // gl.DEBUG_SOURCE_THIRD_PARTY => "ThirdParty",
gl.DEBUG_SOURCE_OTHER => "Other", // gl.DEBUG_SOURCE_OTHER => "Other",
else => unreachable, // else => unreachable,
}; // };
const type_str = switch (_type) { // const type_str = switch (_type) {
gl.DEBUG_TYPE_ERROR => "Error", // gl.DEBUG_TYPE_ERROR => "Error",
gl.DEBUG_TYPE_DEPRECATED_BEHAVIOR => "Deprecated Behaviour", // gl.DEBUG_TYPE_DEPRECATED_BEHAVIOR => "Deprecated Behaviour",
gl.DEBUG_TYPE_UNDEFINED_BEHAVIOR => "Undefined Behaviour", // gl.DEBUG_TYPE_UNDEFINED_BEHAVIOR => "Undefined Behaviour",
gl.DEBUG_TYPE_PORTABILITY => "Portability", // gl.DEBUG_TYPE_PORTABILITY => "Portability",
gl.DEBUG_TYPE_PERFORMANCE => "Performance", // gl.DEBUG_TYPE_PERFORMANCE => "Performance",
gl.DEBUG_TYPE_MARKER => "Marker", // gl.DEBUG_TYPE_MARKER => "Marker",
gl.DEBUG_TYPE_PUSH_GROUP => "Push Group", // gl.DEBUG_TYPE_PUSH_GROUP => "Push Group",
gl.DEBUG_TYPE_POP_GROUP => "Pop Group", // gl.DEBUG_TYPE_POP_GROUP => "Pop Group",
gl.DEBUG_TYPE_OTHER => "Other", // gl.DEBUG_TYPE_OTHER => "Other",
else => unreachable, // else => unreachable,
}; // };
switch (severity) { // switch (severity) {
gl.DEBUG_SEVERITY_HIGH => { // gl.DEBUG_SEVERITY_HIGH => {
std.log.scoped(.OpenGL).err("{s}:{}:{s}: {s}", .{ source_str, id, type_str, message[0..@intCast(length)] }); // std.log.scoped(.OpenGL).err("{s}:{}:{s}: {s}", .{ source_str, id, type_str, message[0..@intCast(length)] });
}, // },
gl.DEBUG_SEVERITY_MEDIUM => { // gl.DEBUG_SEVERITY_MEDIUM => {
std.log.scoped(.OpenGL).warn("{s}:{}:{s}: {s}", .{ source_str, id, type_str, message[0..@intCast(length)] }); // std.log.scoped(.OpenGL).warn("{s}:{}:{s}: {s}", .{ source_str, id, type_str, message[0..@intCast(length)] });
}, // },
gl.DEBUG_SEVERITY_LOW => { // gl.DEBUG_SEVERITY_LOW => {
std.log.scoped(.OpenGL).debug("{s}:{}:{s}: {s}", .{ source_str, id, type_str, message[0..@intCast(length)] }); // std.log.scoped(.OpenGL).debug("{s}:{}:{s}: {s}", .{ source_str, id, type_str, message[0..@intCast(length)] });
}, // },
gl.DEBUG_SEVERITY_NOTIFICATION => { // gl.DEBUG_SEVERITY_NOTIFICATION => {
std.log.scoped(.OpenGL).info("{s}:{}:{s}: {s}", .{ source_str, id, type_str, message[0..@intCast(length)] }); // std.log.scoped(.OpenGL).info("{s}:{}:{s}: {s}", .{ source_str, id, type_str, message[0..@intCast(length)] });
}, // },
else => unreachable, // else => unreachable,
} // }
} // }
const mesh_program = a.ShaderPrograms.mesh; const mesh_program = a.ShaderPrograms.mesh;
export fn game_init(global_allocator: *std.mem.Allocator) void { export fn game_init(global_allocator: *std.mem.Allocator) void {
loadGL(); // loadGL();
tracy.startupProfiler(); tracy.startupProfiler();
std.log.debug("game_init\n", .{}); std.log.debug("game_init\n", .{});
@ -171,7 +174,7 @@ export fn game_init(global_allocator: *std.mem.Allocator) void {
.global_allocator = global_allocator.*, .global_allocator = global_allocator.*,
.frame_fba = std.heap.FixedBufferAllocator.init(frame_arena_buffer), .frame_fba = std.heap.FixedBufferAllocator.init(frame_arena_buffer),
.assetman = AssetManager.init(global_allocator.*, globals.g_mem.frame_fba.allocator()), .assetman = AssetManager.init(global_allocator.*, globals.g_mem.frame_fba.allocator()),
.render = Render.init(global_allocator.*, globals.g_mem.frame_fba.allocator(), &globals.g_mem.assetman), // .render = Render.init(global_allocator.*, globals.g_mem.frame_fba.allocator(), &globals.g_mem.assetman),
.world = .{ .frame_arena = globals.g_mem.frame_fba.allocator() }, .world = .{ .frame_arena = globals.g_mem.frame_fba.allocator() },
}; };
globals.g_mem.render.camera = &globals.g_mem.free_cam.camera; globals.g_mem.render.camera = &globals.g_mem.free_cam.camera;
@ -181,13 +184,13 @@ export fn game_init(global_allocator: *std.mem.Allocator) void {
globals.g_mem.performance_frequency = c.SDL_GetPerformanceFrequency(); globals.g_mem.performance_frequency = c.SDL_GetPerformanceFrequency();
globals.g_mem.last_frame_time = c.SDL_GetPerformanceCounter(); globals.g_mem.last_frame_time = c.SDL_GetPerformanceCounter();
var majorVer: gl.GLint = 0; // var majorVer: gl.GLint = 0;
var minorVer: gl.GLint = 0; // var minorVer: gl.GLint = 0;
gl.getIntegerv(gl.MAJOR_VERSION, &majorVer); // gl.getIntegerv(gl.MAJOR_VERSION, &majorVer);
gl.getIntegerv(gl.MINOR_VERSION, &minorVer); // gl.getIntegerv(gl.MINOR_VERSION, &minorVer);
std.log.debug("OpenGL Version: {}.{}", .{ majorVer, minorVer }); // std.log.debug("OpenGL Version: {}.{}", .{ majorVer, minorVer });
gl.viewport(0, 0, globals.g_init.width, globals.g_init.height); // gl.viewport(0, 0, globals.g_init.width, globals.g_init.height);
_ = globals.g_mem.world.addEntity(.{ _ = globals.g_mem.world.addEntity(.{
.flags = .{ .dir_light = true, .rotate = true }, .flags = .{ .dir_light = true, .rotate = true },
@ -402,7 +405,7 @@ export fn game_update() bool {
c.SDL_GL_GetDrawableSize(ginit.window, &ginit.width, &ginit.height); c.SDL_GL_GetDrawableSize(ginit.window, &ginit.width, &ginit.height);
std.log.debug("w: {}, h: {}\n", .{ ginit.width, ginit.height }); std.log.debug("w: {}, h: {}\n", .{ ginit.width, ginit.height });
gl.viewport(0, 0, ginit.width, ginit.height); // gl.viewport(0, 0, ginit.width, ginit.height);
}, },
else => {}, else => {},
} }
@ -458,8 +461,10 @@ export fn game_update() bool {
} }
} }
ginit.gc.draw() catch @panic("draw error");
// Render // Render
{ if (false) {
const zone = tracy.initZone(@src(), .{ .name = "game.render()" }); const zone = tracy.initZone(@src(), .{ .name = "game.render()" });
defer zone.deinit(); defer zone.deinit();
gmem.render.begin(); gmem.render.begin();
@ -516,11 +521,11 @@ export fn game_update() bool {
} }
} }
{ // {
const zone = tracy.initZone(@src(), .{ .name = "SDL_GL_SwapWindow" }); // const zone = tracy.initZone(@src(), .{ .name = "SDL_GL_SwapWindow" });
defer zone.deinit(); // defer zone.deinit();
c.SDL_GL_SwapWindow(ginit.window); // c.SDL_GL_SwapWindow(ginit.window);
} // }
tracy.frameMark(); tracy.frameMark();
//c.SDL_Delay(1); //c.SDL_Delay(1);
@ -535,7 +540,7 @@ export fn game_shutdown() void {
gmem.assetman.deinit(); gmem.assetman.deinit();
gmem.global_allocator.free(gmem.frame_fba.buffer); gmem.global_allocator.free(gmem.frame_fba.buffer);
gmem.global_allocator.destroy(gmem); gmem.global_allocator.destroy(gmem);
gl.disable(gl.DEBUG_OUTPUT); // gl.disable(gl.DEBUG_OUTPUT);
tracy.shutdownProfiler(); tracy.shutdownProfiler();
} }
@ -553,7 +558,7 @@ export fn game_hot_reload(init_memory: ?*anyopaque, gmemory: ?*anyopaque) void {
if (init_memory) |init_mem| { if (init_memory) |init_mem| {
globals.g_init = @alignCast(@ptrCast(init_mem)); globals.g_init = @alignCast(@ptrCast(init_mem));
globals.g_init_exists = true; globals.g_init_exists = true;
loadGL(); // loadGL();
} }
if (gmemory) |gmem| { if (gmemory) |gmem| {
globals.g_mem = @alignCast(@ptrCast(gmem)); globals.g_mem = @alignCast(@ptrCast(gmem));

View File

@ -3,6 +3,7 @@ const c = @import("sdl.zig");
const Render = @import("Render.zig"); const Render = @import("Render.zig");
const AssetManager = @import("AssetManager.zig"); const AssetManager = @import("AssetManager.zig");
const World = @import("entity.zig").World; const World = @import("entity.zig").World;
const GraphicsContext = @import("GraphicsContext.zig");
const za = @import("zalgebra"); const za = @import("zalgebra");
const Vec2 = za.Vec2; const Vec2 = za.Vec2;
@ -28,12 +29,13 @@ pub const InitMemory = struct {
fullscreen: bool = false, fullscreen: bool = false,
vsync: bool = false, vsync: bool = false,
syswm_info: c.SDL_SysWMinfo = .{}, syswm_info: c.SDL_SysWMinfo = .{},
gc: GraphicsContext = .{},
}; };
pub const GameMemory = struct { pub const GameMemory = struct {
global_allocator: std.mem.Allocator, global_allocator: std.mem.Allocator,
frame_fba: std.heap.FixedBufferAllocator, frame_fba: std.heap.FixedBufferAllocator,
assetman: AssetManager, assetman: AssetManager,
render: Render, render: Render = undefined,
performance_frequency: u64 = 0, performance_frequency: u64 = 0,
last_frame_time: u64 = 0, last_frame_time: u64 = 0,
delta_time: f32 = 0.0000001, delta_time: f32 = 0.0000001,

65
src/mem/Arena.zig Normal file
View File

@ -0,0 +1,65 @@
const std = @import("std");
const Arena = @This();
memory: []align(std.mem.page_size) u8,
ptr: usize = 0,
commited_pages: usize = 0,
pub fn init(reserve_mem: usize) !Arena {
const slice = try std.posix.mmap(null, reserve_mem, std.posix.PROT.NONE, .{ .TYPE = .PRIVATE, .ANONYMOUS = true }, -1, 0);
return Arena{
.memory = slice,
};
}
pub fn allocator(self: *Arena) std.mem.Allocator {
return std.mem.Allocator{
.ptr = @ptrCast(self),
.vtable = &vtable,
};
}
const vtable = std.mem.Allocator.VTable{
.alloc = rawAlloc,
};
/// This function is not intended to be called except from within the
/// implementation of an Allocator
pub inline fn rawAlloc(self: *Arena, len: usize, ptr_align: u8, ret_addr: usize) ?[*]u8 {
_ = ret_addr; // autofix
const result = std.mem.alignForwardLog2(usize, self.ptr, ptr_align);
const new_ptr = result + len;
if (new_ptr >= self.memory.len) {
return null;
}
if (new_ptr >= self.getLastPageEnd()) {
self.reservePagesUpTo(new_ptr) catch {
return null;
};
}
self.ptr = new_ptr;
return &self.memory.ptr[result];
}
inline fn getLastPageEnd(self: *const Arena) usize {
return std.mem.page_size * (self.commited_pages + 1);
}
fn reservePagesUpTo(self: *Arena, addr: usize) !void {
while (addr >= self.getLastPageEnd()) {
switch (std.os.errno(std.os.linux.mprotect(self.getLastPageEnd(), std.mem.page_size, std.os.linux.PROT.WRITE | std.os.linux.PROT.READ))) {
.SUCCESS => {},
.ENOMEM => return error.OutOfMemory,
.EINVAL => unreachable,
else => unreachable,
}
self.commited_pages += 1;
}
}

View File

@ -1,6 +1,7 @@
pub usingnamespace @cImport({ pub usingnamespace @cImport({
@cInclude("SDL2/SDL.h"); @cInclude("SDL2/SDL.h");
@cInclude("SDL2/SDL_syswm.h"); @cInclude("SDL2/SDL_syswm.h");
@cInclude("SDL2/SDL_vulkan.h");
}); });
// When using system sdl2 library this might not be defined // When using system sdl2 library this might not be defined