diff --git a/.gitignore b/.gitignore index d8c8979..8c34726 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .zig-cache zig-out +*.spv diff --git a/assets/shaders/main_common.glsl b/assets/shaders/main_common.glsl index 3717059..e08df3a 100644 --- a/assets/shaders/main_common.glsl +++ b/assets/shaders/main_common.glsl @@ -40,13 +40,13 @@ layout(set = 0, binding = 2, scalar) readonly buffer DirectionalLights { DirectionalLight lights[]; } _DirectionalLights; -layout(set = 0, binding = 3) uniform sampler _Sampler; -layout(set = 0, binding = 4) uniform texture2D _Textures[]; - -layout(set = 0, binding = 5, scalar) readonly buffer Materials { +layout(set = 0, binding = 3, scalar) readonly buffer Materials { Material _Materials[]; }; +layout(set = 0, binding = 4) uniform sampler _Sampler; +layout(set = 0, binding = 5) uniform texture2D _Textures[]; + // --- SET 1 --- PER OBJECT ---------------------------------------------------- layout(set = 1, binding = 0, scalar) uniform ObjectUniforms { diff --git a/build.zig.zon b/build.zig.zon index 0f344ae..fb6dd56 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -20,6 +20,7 @@ }, .paths = .{ + "assets", "build.zig", "build.zig.zon", "src", diff --git a/compile_shaders.sh b/compile_shaders.sh new file mode 100644 index 0000000..0360f13 --- /dev/null +++ b/compile_shaders.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +glslc --target-env=vulkan1.2 assets/shaders/equirect_to_cube.comp -o src/shaders/equirect_to_cube_comp.spv +glslc --target-env=vulkan1.2 assets/shaders/main.frag -o src/shaders/main_frag.spv +glslc --target-env=vulkan1.2 assets/shaders/main.vert -o src/shaders/main_vert.spv diff --git a/src/Game.zig b/src/Game.zig index 728abc0..238f20a 100644 --- a/src/Game.zig +++ b/src/Game.zig @@ -2,6 +2,7 @@ const Game = @This(); const std = @import("std"); const glfw = @import("zglfw"); +const vk = @import("vulkan"); const math = @import("math.zig"); @@ -9,14 +10,32 @@ const Atoms = @import("assets/Atoms.zig"); const Textures = @import("assets/Textures.zig"); const Materials = @import("assets/Materials.zig"); const Swapchain = @import("engine/Swapchain.zig"); +const VertexBuffer = @import("engine/VertexBuffer.zig"); +const IndexBuffer = @import("engine/IndexBuffer.zig"); const Iterator3 = math.Iterator3; const Matrix4x4 = math.Matrix4x4; const Quaternion = math.Quaternion; const Vector2 = math.Vector2; const Vector3 = math.Vector3; +const Vertex = extern struct { + positionOS: [3]f32, + texCoord: [2]u16, + normalOS: [3]i8, + tangentOS: [4]i8, +}; + +const main_vert_spv align(4) = @embedFile("shaders/main_vert.spv").*; +const main_frag_spv align(4) = @embedFile("shaders/main_frag.spv").*; + allocator: std.mem.Allocator, swapchain: *Swapchain, +global_descriptor_set_layout: vk.DescriptorSetLayout, +per_object_descriptor_set_layout: vk.DescriptorSetLayout, +pipeline_layout: vk.PipelineLayout, +pipeline: vk.Pipeline, +vertex_buffer: VertexBuffer, +index_buffer: IndexBuffer, atoms: Atoms, materials: Materials, @@ -52,6 +71,271 @@ pub fn init(allocator: std.mem.Allocator, swapchain: *Swapchain) !Game { errdefer textures.deinit(swapchain.engine); _ = try materials.getOrLoadId(swapchain.engine, &textures, &atoms, try atoms.getOrPutAtom("Bricks.json")); + _ = try materials.getOrLoadId(swapchain.engine, &textures, &atoms, try atoms.getOrPutAtom("Dirt.json")); + _ = try materials.getOrLoadId(swapchain.engine, &textures, &atoms, try atoms.getOrPutAtom("Gold.json")); + _ = try materials.getOrLoadId(swapchain.engine, &textures, &atoms, try atoms.getOrPutAtom("Stone.json")); + + const device = swapchain.engine.device; + const interface = &swapchain.engine.vk_allocator.interface; + + const global_descriptor_set_layout_bindings = [_]vk.DescriptorSetLayoutBinding{ + .{ + .binding = 0, + .descriptor_type = .uniform_buffer, + .descriptor_count = 1, + .stage_flags = .{ .vertex_bit = true, .fragment_bit = true }, + }, + .{ + .binding = 1, + .descriptor_type = .storage_buffer, + .descriptor_count = 1, + .stage_flags = .{ .vertex_bit = true, .fragment_bit = true }, + }, + .{ + .binding = 2, + .descriptor_type = .storage_buffer, + .descriptor_count = 1, + .stage_flags = .{ .vertex_bit = true, .fragment_bit = true }, + }, + .{ + .binding = 3, + .descriptor_type = .storage_buffer, + .descriptor_count = 1, + .stage_flags = .{ .vertex_bit = true, .fragment_bit = true }, + }, + .{ + .binding = 4, + .descriptor_type = .sampler, + .descriptor_count = 1, + .stage_flags = .{ .vertex_bit = true, .fragment_bit = true }, + }, + .{ + .binding = 5, + .descriptor_type = .sampled_image, + .descriptor_count = 1024, + .stage_flags = .{ .vertex_bit = true, .fragment_bit = true }, + }, + }; + + const global_descriptor_set_layout_bindings_flags = [_]vk.DescriptorBindingFlags{ + .{}, + .{}, + .{}, + .{}, + .{}, + .{ .variable_descriptor_count_bit = true }, + }; + + std.debug.assert(global_descriptor_set_layout_bindings.len == global_descriptor_set_layout_bindings_flags.len); + + const global_descriptor_set_layout = try device.createDescriptorSetLayout(&.{ + .p_next = &vk.DescriptorSetLayoutBindingFlagsCreateInfo{ + .binding_count = global_descriptor_set_layout_bindings_flags.len, + .p_binding_flags = &global_descriptor_set_layout_bindings_flags, + }, + .binding_count = global_descriptor_set_layout_bindings.len, + .p_bindings = &global_descriptor_set_layout_bindings, + }, interface); + errdefer device.destroyDescriptorSetLayout(global_descriptor_set_layout, interface); + + const per_object_descriptor_set_layout_bindings = [_]vk.DescriptorSetLayoutBinding{ + .{ + .binding = 0, + .descriptor_type = .uniform_buffer_dynamic, + .descriptor_count = 1, + .stage_flags = .{ .vertex_bit = true, .fragment_bit = true }, + }, + }; + + const per_object_descriptor_set_layout = try device.createDescriptorSetLayout(&.{ + .binding_count = per_object_descriptor_set_layout_bindings.len, + .p_bindings = &per_object_descriptor_set_layout_bindings, + }, interface); + errdefer device.destroyDescriptorSetLayout(per_object_descriptor_set_layout, interface); + + const descriptor_set_layouts = [_]vk.DescriptorSetLayout{ + global_descriptor_set_layout, + per_object_descriptor_set_layout, + }; + + const pipeline_layout = try device.createPipelineLayout(&.{ + .set_layout_count = descriptor_set_layouts.len, + .p_set_layouts = &descriptor_set_layouts, + }, interface); + errdefer device.destroyPipelineLayout(pipeline_layout, interface); + + const vertex_shader = try device.createShaderModule(&.{ + .code_size = main_vert_spv.len, + .p_code = @ptrCast(&main_vert_spv), + }, interface); + defer device.destroyShaderModule(vertex_shader, interface); + + const fragment_shader = try device.createShaderModule(&.{ + .code_size = main_frag_spv.len, + .p_code = @ptrCast(&main_frag_spv), + }, interface); + defer device.destroyShaderModule(fragment_shader, interface); + + var vertex_buffer: VertexBuffer = try .init(swapchain.engine, Vertex, 4); + errdefer vertex_buffer.deinit(swapchain.engine); + try vertex_buffer.write(Vertex, swapchain.engine, &.{ + .{ + .positionOS = .{ -0.5, -0.5, 0 }, + .texCoord = .{ 0, 65535 }, + .normalOS = .{ 127, 0, 0 }, + .tangentOS = .{ 127, 0, 0, -127 }, + }, + .{ + .positionOS = .{ 0.5, -0.5, 0 }, + .texCoord = .{ 65535, 65535 }, + .normalOS = .{ 127, 0, 0 }, + .tangentOS = .{ 127, 0, 0, -127 }, + }, + .{ + .positionOS = .{ -0.5, 0.5, 0 }, + .texCoord = .{ 0, 0 }, + .normalOS = .{ 127, 0, 0 }, + .tangentOS = .{ 127, 0, 0, -127 }, + }, + .{ + .positionOS = .{ 0.5, 0.5, 0 }, + .texCoord = .{ 65535, 0 }, + .normalOS = .{ 127, 0, 0 }, + .tangentOS = .{ 127, 0, 0, -127 }, + }, + }); + + var index_buffer: IndexBuffer = try .init(swapchain.engine, 6); + errdefer index_buffer.deinit(swapchain.engine); + try index_buffer.write(swapchain.engine, &.{ 0, 1, 2, 2, 1, 3 }); + + const pipeline_shader_stages = [_]vk.PipelineShaderStageCreateInfo{ + .{ + .stage = .{ .vertex_bit = true }, + .module = vertex_shader, + .p_name = "main", + }, + .{ + .stage = .{ .fragment_bit = true }, + .module = fragment_shader, + .p_name = "main", + }, + }; + + const vertex_input_binding_descriptions = [_]vk.VertexInputBindingDescription{ + .{ + .binding = 0, + .stride = @sizeOf(Vertex), + .input_rate = .vertex, + }, + }; + + const vertex_input_attribute_descriptions = [_]vk.VertexInputAttributeDescription{ + .{ + .location = 0, + .binding = 0, + .format = .r32g32b32_sfloat, + .offset = @offsetOf(Vertex, "positionOS"), + }, + .{ + .location = 1, + .binding = 0, + .format = .r16g16_unorm, + .offset = @offsetOf(Vertex, "texCoord"), + }, + .{ + .location = 2, + .binding = 0, + .format = .r8g8b8_snorm, + .offset = @offsetOf(Vertex, "normalOS"), + }, + .{ + .location = 3, + .binding = 0, + .format = .r8g8b8a8_snorm, + .offset = @offsetOf(Vertex, "tangentOS"), + }, + }; + + const pipeline_color_blend_attachment_states = [_]vk.PipelineColorBlendAttachmentState{ + .{ + .blend_enable = .false, + .src_color_blend_factor = .one, + .dst_color_blend_factor = .zero, + .color_blend_op = .add, + .src_alpha_blend_factor = .one, + .dst_alpha_blend_factor = .zero, + .alpha_blend_op = .add, + .color_write_mask = .{ + .r_bit = true, + .g_bit = true, + .b_bit = true, + .a_bit = true, + }, + }, + }; + + const dynamic_states = [_]vk.DynamicState{ .viewport, .scissor }; + + var pipeline: vk.Pipeline = undefined; + _ = try device.createGraphicsPipelines(.null_handle, 1, &.{ + .{ + .stage_count = pipeline_shader_stages.len, + .p_stages = &pipeline_shader_stages, + .p_vertex_input_state = &.{ + .vertex_binding_description_count = vertex_input_binding_descriptions.len, + .p_vertex_binding_descriptions = &vertex_input_binding_descriptions, + .vertex_attribute_description_count = vertex_input_attribute_descriptions.len, + .p_vertex_attribute_descriptions = &vertex_input_attribute_descriptions, + }, + .p_input_assembly_state = &.{ + .topology = .triangle_list, + .primitive_restart_enable = .false, + }, + .p_viewport_state = &.{ + .viewport_count = 1, + .p_viewports = undefined, + .scissor_count = 1, + .p_scissors = undefined, + }, + .p_rasterization_state = &.{ + .depth_clamp_enable = .false, + .rasterizer_discard_enable = .false, + .polygon_mode = .fill, + .cull_mode = .{ .back_bit = true }, + .front_face = .counter_clockwise, + .depth_bias_enable = .false, + .depth_bias_constant_factor = 0, + .depth_bias_clamp = 0, + .depth_bias_slope_factor = 0, + .line_width = 1, + }, + .p_multisample_state = &.{ + .rasterization_samples = .{ .@"1_bit" = true }, + .sample_shading_enable = .false, + .min_sample_shading = 1, + .alpha_to_coverage_enable = .false, + .alpha_to_one_enable = .false, + }, + .p_depth_stencil_state = null, + .p_color_blend_state = &.{ + .logic_op_enable = .false, + .logic_op = .copy, + .attachment_count = pipeline_color_blend_attachment_states.len, + .p_attachments = &pipeline_color_blend_attachment_states, + .blend_constants = .{ 0, 0, 0, 0 }, + }, + .p_dynamic_state = &.{ + .dynamic_state_count = dynamic_states.len, + .p_dynamic_states = &dynamic_states, + }, + .layout = pipeline_layout, + .render_pass = swapchain.render_pass, + .subpass = 0, + .base_pipeline_index = -1, + }, + }, interface, @ptrCast(&pipeline)); + errdefer device.destroyPipeline(pipeline, interface); return .{ .allocator = allocator, @@ -59,10 +343,27 @@ pub fn init(allocator: std.mem.Allocator, swapchain: *Swapchain) !Game { .atoms = atoms, .materials = materials, .textures = textures, + .global_descriptor_set_layout = global_descriptor_set_layout, + .per_object_descriptor_set_layout = per_object_descriptor_set_layout, + .pipeline_layout = pipeline_layout, + .pipeline = pipeline, + .vertex_buffer = vertex_buffer, + .index_buffer = index_buffer, }; } pub fn deinit(self: *Game) void { + const device = self.swapchain.engine.device; + const interface = &self.swapchain.engine.vk_allocator.interface; + + self.vertex_buffer.deinit(self.swapchain.engine); + self.index_buffer.deinit(self.swapchain.engine); + + device.destroyPipeline(self.pipeline, interface); + device.destroyPipelineLayout(self.pipeline_layout, interface); + device.destroyDescriptorSetLayout(self.per_object_descriptor_set_layout, interface); + device.destroyDescriptorSetLayout(self.global_descriptor_set_layout, interface); + self.textures.deinit(self.swapchain.engine); self.materials.deinit(self.swapchain.engine); self.atoms.deinit(); @@ -76,6 +377,10 @@ pub fn update(self: *Game, dt: f32) void { ).rotate(self.camera_yaw).mulScalar(player_speed * dt); self.camera_position = Vector3.add(self.camera_position, camera_d.asVector3(0)); + + self.render() catch |err| { + std.log.err("Failed to render: {s}", .{@errorName(err)}); + }; } pub fn onKeyDown(self: *Game, key_code: glfw.Key, mods: glfw.Mods) void { @@ -127,3 +432,62 @@ pub fn onMouseMove(self: *Game, dx: f32, dy: f32) void { self.camera_pitch = std.math.clamp(self.camera_pitch, -0.5 * std.math.pi, 0.5 * std.math.pi); self.camera_yaw = @mod(self.camera_yaw, 2 * std.math.pi); } + +fn render(self: *Game) !void { + const engine = self.swapchain.engine; + + const fence = try engine.device.createFence(&.{}, &engine.vk_allocator.interface); + defer engine.device.destroyFence(fence, &engine.vk_allocator.interface); + + const command_buffer = try engine.allocateGraphicsCommandBuffer(); + defer engine.freeGraphicsCommandBuffer(command_buffer); + try command_buffer.beginCommandBuffer(&.{ .flags = .{ .one_time_submit_bit = true } }); + + const viewports = [_]vk.Viewport{ + .{ + .x = 0, + .y = 0, + .width = 0, + .height = 0, + .min_depth = 0, + .max_depth = 1, + }, + }; + command_buffer.setViewport(0, viewports.len, &viewports); + + const scissors = [_]vk.Rect2D{ + .{ + .offset = .{ .x = 0, .y = 0 }, + .extent = .{ .width = 0, .height = 0 }, + }, + }; + command_buffer.setScissor(0, scissors.len, &scissors); + + { + const clear_values = [_]vk.ClearValue{ + .{ .color = .{ .float_32 = .{ 0, 0, 0, 1 } } }, + }; + + command_buffer.beginRenderPass(&.{ + .render_pass = self.swapchain.render_pass, + .framebuffer = self.swapchain.swapchain_images[0].framebuffer, + .render_area = .{ + .offset = .{ .x = 0, .y = 0 }, + .extent = .{ .width = 0, .height = 0 }, + }, + .clear_value_count = clear_values.len, + .p_clear_values = &clear_values, + }, .@"inline"); + defer command_buffer.endRenderPass(); + + command_buffer.bindPipeline(.graphics, self.pipeline); + + command_buffer.bindVertexBuffers(0, 1, @ptrCast(&self.vertex_buffer.buffer), &.{0}); + command_buffer.bindIndexBuffer(self.index_buffer.buffer, 0, .uint16); + command_buffer.drawIndexed(@intCast(self.index_buffer.index_count), 1, 0, 0, 0); + } + + try command_buffer.endCommandBuffer(); + try engine.submitGraphicsCommandBuffer(command_buffer, fence); + _ = try engine.device.waitForFences(1, @ptrCast(&fence), .true, std.math.maxInt(u64)); +} diff --git a/src/assets/Materials.zig b/src/assets/Materials.zig index 821b6ae..89508b4 100644 --- a/src/assets/Materials.zig +++ b/src/assets/Materials.zig @@ -88,6 +88,7 @@ fn loadMaterial(self: *Materials, engine: *Engine, textures: *Textures, atoms: * }; const filename = atoms.getString(key).?; + std.log.debug("Loading material \"{s}\"...", .{filename}); const cwd = std.fs.cwd(); diff --git a/src/assets/Textures.zig b/src/assets/Textures.zig index 126e41e..a7185da 100644 --- a/src/assets/Textures.zig +++ b/src/assets/Textures.zig @@ -58,7 +58,7 @@ pub fn init(allocator: std.mem.Allocator, engine: *Engine) !Textures { try empty_base_color_texture.write([4]u8, engine, &.{.{ 255, 255, 255, 255 }}); try empty_emissive_texture.write([4]f16, engine, &.{.{ 1.0, 1.0, 1.0, 1.0 }}); - try empty_normal_texture.write([4]u8, engine, &.{.{ 128, 128, 255, 255 }}); + try empty_normal_texture.write([4]i8, engine, &.{.{ 0, 0, 127, 127 }}); try empty_occlusuion_roughness_metallic_texture.write([4]u8, engine, &.{.{ 255, 255, 255, 255 }}); return .{ @@ -112,6 +112,8 @@ pub fn getOrLoadId(self: *Textures, engine: *Engine, atoms: *Atoms, key: Key) !I fn loadTexture(self: *Textures, engine: *Engine, atoms: *Atoms, key: Key) !Texture { const filename = atoms.getString(key.atom).?; + std.log.debug("Loading texture \"{s}\" as {s}...", .{ filename, @tagName(key.usage) }); + const cwd = std.fs.cwd(); var dir = try cwd.openDir("assets/textures", .{}); diff --git a/src/engine/Engine.zig b/src/engine/Engine.zig index c0b700f..3cfa9af 100644 --- a/src/engine/Engine.zig +++ b/src/engine/Engine.zig @@ -281,7 +281,15 @@ pub fn init(allocator: std.mem.Allocator, maybe_window: ?*glfw.Window) !Engine { try enabled_extensions.appendBounded(vk.extensions.khr_swapchain.name); } + var enabled_features_vulkan12: vk.PhysicalDeviceVulkan12Features = .{ + .descriptor_binding_partially_bound = .true, + .descriptor_binding_variable_descriptor_count = .true, + .runtime_descriptor_array = .true, + .scalar_block_layout = .true, + }; + break :blk try instance.createDevice(physical_device, &.{ + .p_next = &enabled_features_vulkan12, .queue_create_info_count = @intCast(queue_create_info.items.len), .p_queue_create_infos = queue_create_info.items.ptr, .enabled_extension_count = @intCast(enabled_extensions.items.len), diff --git a/src/engine/IndexBuffer.zig b/src/engine/IndexBuffer.zig index 32040c1..0be9607 100644 --- a/src/engine/IndexBuffer.zig +++ b/src/engine/IndexBuffer.zig @@ -25,7 +25,7 @@ pub fn init(engine: *Engine, index_count: usize) !IndexBuffer { .queue_family_index_count = qsm.queue_family_index_count, .p_queue_family_indices = qsm.p_queue_family_indices, }, &engine.vk_allocator.interface); - errdefer engine.device.destroyBuffer(buffer); + errdefer engine.device.destroyBuffer(buffer, &engine.vk_allocator.interface); const memory_requirements = engine.device.getBufferMemoryRequirements(buffer); const memory = try engine.allocate(memory_requirements, .{ .device_local_bit = true }); @@ -42,7 +42,7 @@ pub fn init(engine: *Engine, index_count: usize) !IndexBuffer { pub fn deinit(self: *IndexBuffer, engine: *Engine) void { engine.device.freeMemory(self.memory, &engine.vk_allocator.interface); - engine.device.destroyBuffer(self.buffer); + engine.device.destroyBuffer(self.buffer, &engine.vk_allocator.interface); self.* = undefined; } @@ -50,12 +50,12 @@ pub fn deinit(self: *IndexBuffer, engine: *Engine) void { pub fn write(self: IndexBuffer, engine: *Engine, indices: []const u16) !void { std.debug.assert(indices.len == self.index_count); - const fence = try engine.device.createFence(.{}, &engine.vk_allocator.interface); + const fence = try engine.device.createFence(&.{}, &engine.vk_allocator.interface); defer engine.device.destroyFence(fence, &engine.vk_allocator.interface); const size = std.mem.sliceAsBytes(indices).len; - const staging_buffer: StagingBuffer = .init(engine, std.mem.sliceAsBytes(indices), engine.graphics_queue.allocation.family); + var staging_buffer: StagingBuffer = try .init(engine, std.mem.sliceAsBytes(indices), engine.graphics_queue.allocation.family); defer staging_buffer.deinit(engine); const command_buffer = try engine.allocateTransferCommandBuffer(); diff --git a/src/engine/VertexBuffer.zig b/src/engine/VertexBuffer.zig index 80d2862..a1b1128 100644 --- a/src/engine/VertexBuffer.zig +++ b/src/engine/VertexBuffer.zig @@ -27,7 +27,7 @@ pub fn init(engine: *Engine, comptime VertexType: type, vertex_count: usize) !Ve .queue_family_index_count = qsm.queue_family_index_count, .p_queue_family_indices = qsm.p_queue_family_indices, }, &engine.vk_allocator.interface); - errdefer engine.device.destroyBuffer(buffer); + errdefer engine.device.destroyBuffer(buffer, &engine.vk_allocator.interface); const memory_requirements = engine.device.getBufferMemoryRequirements(buffer); const memory = try engine.allocate(memory_requirements, .{ .device_local_bit = true }); @@ -45,7 +45,7 @@ pub fn init(engine: *Engine, comptime VertexType: type, vertex_count: usize) !Ve pub fn deinit(self: *VertexBuffer, engine: *Engine) void { engine.device.freeMemory(self.memory, &engine.vk_allocator.interface); - engine.device.destroyBuffer(self.buffer); + engine.device.destroyBuffer(self.buffer, &engine.vk_allocator.interface); self.* = undefined; } @@ -54,12 +54,12 @@ pub fn write(self: VertexBuffer, comptime VertexType: type, engine: *Engine, ver std.debug.assert(vertices.len == self.vertex_count); std.debug.assert(@sizeOf(VertexType) == self.vertex_size); - const fence = try engine.device.createFence(.{}, &engine.vk_allocator.interface); + const fence = try engine.device.createFence(&.{}, &engine.vk_allocator.interface); defer engine.device.destroyFence(fence, &engine.vk_allocator.interface); const size = std.mem.sliceAsBytes(vertices).len; - const staging_buffer: StagingBuffer = .init(engine, std.mem.sliceAsBytes(vertices), engine.graphics_queue.allocation.family); + var staging_buffer: StagingBuffer = try .init(engine, std.mem.sliceAsBytes(vertices), engine.graphics_queue.allocation.family); defer staging_buffer.deinit(engine); const command_buffer = try engine.allocateTransferCommandBuffer(); diff --git a/src/samplers.zig b/src/samplers.zig deleted file mode 100644 index 43a4d5f..0000000 --- a/src/samplers.zig +++ /dev/null @@ -1,49 +0,0 @@ -const std = @import("std"); - -const sokol = @import("sokol"); - -const sg = sokol.gfx; - -const main = @import("main.zig"); - -pub var sampler_cache: std.AutoHashMapUnmanaged(Descriptor, sg.Sampler) = .{}; - -pub const Descriptor = struct { - wrap_u: sg.Wrap = .REPEAT, - wrap_v: sg.Wrap = .REPEAT, - wrap_w: sg.Wrap = .REPEAT, - min_filter: sg.Filter = .LINEAR, - mag_filter: sg.Filter = .LINEAR, - mipmap_filter: sg.Filter = .LINEAR, -}; - -pub fn getOrCreateSampler(descriptor: Descriptor) !sg.Sampler { - const entry = try sampler_cache.getOrPut(main.allocator, descriptor); - - if (entry.found_existing) { - return entry.value_ptr.*; - } else { - const sampler = sg.makeSampler(.{ - .wrap_u = descriptor.wrap_u, - .wrap_v = descriptor.wrap_v, - .wrap_w = descriptor.wrap_w, - .min_filter = descriptor.min_filter, - .mag_filter = descriptor.mag_filter, - .mipmap_filter = descriptor.mipmap_filter, - }); - - entry.key_ptr.* = descriptor; - entry.value_ptr.* = sampler; - - return sampler; - } -} - -pub fn deinit() void { - var sampler_cache_it = sampler_cache.valueIterator(); - while (sampler_cache_it.next()) |sampler| { - sg.destroySampler(sampler.*); - } - - sampler_cache.deinit(main.allocator); -}