Compare commits

...

3 commits

Author SHA1 Message Date
61a18e6f0b Clean up model 2024-09-01 19:24:00 +02:00
bd725421b6 Experiment with subpasses 2024-09-01 19:20:41 +02:00
27689b8cda WIP: Subpasses 2024-09-01 17:36:22 +02:00
14 changed files with 2198 additions and 238 deletions

View file

@ -1,24 +0,0 @@
newmtl white
Ka 0 0 0
Kd 1 1 1
Ks 0 0 0
newmtl red
Ka 0 0 0
Kd 1 0 0
Ks 0 0 0
newmtl green
Ka 0 0 0
Kd 0 1 0
Ks 0 0 0
newmtl blue
Ka 0 0 0
Kd 0 0 1
Ks 0 0 0
newmtl light
Ka 20 20 20
Kd 1 1 1
Ks 0 0 0

View file

@ -1,145 +0,0 @@
# cornell_box.obj and cornell_box.mtl are grabbed from Intel's embree project.
# original cornell box data
# comment
# empty line including some space
mtllib cornell_box.mtl
o floor
usemtl white
v 552.8 0.0 0.0
v 0.0 0.0 0.0
v 0.0 0.0 559.2
v 549.6 0.0 559.2
v 130.0 0.0 65.0
v 82.0 0.0 225.0
v 240.0 0.0 272.0
v 290.0 0.0 114.0
v 423.0 0.0 247.0
v 265.0 0.0 296.0
v 314.0 0.0 456.0
v 472.0 0.0 406.0
f 1 2 3 4
f 8 7 6 5
f 12 11 10 9
o light
usemtl light
v 343.0 548.0 227.0
v 343.0 548.0 332.0
v 213.0 548.0 332.0
v 213.0 548.0 227.0
f -4 -3 -2 -1
o ceiling
usemtl white
v 556.0 548.8 0.0
v 556.0 548.8 559.2
v 0.0 548.8 559.2
v 0.0 548.8 0.0
f -4 -3 -2 -1
o back_wall
usemtl white
v 549.6 0.0 559.2
v 0.0 0.0 559.2
v 0.0 548.8 559.2
v 556.0 548.8 559.2
f -4 -3 -2 -1
o front_wall
usemtl blue
v 549.6 0.0 0
v 0.0 0.0 0
v 0.0 548.8 0
v 556.0 548.8 0
#f -1 -2 -3 -4
o green_wall
usemtl green
v 0.0 0.0 559.2
v 0.0 0.0 0.0
v 0.0 548.8 0.0
v 0.0 548.8 559.2
f -4 -3 -2 -1
o red_wall
usemtl red
v 552.8 0.0 0.0
v 549.6 0.0 559.2
v 556.0 548.8 559.2
v 556.0 548.8 0.0
f -4 -3 -2 -1
o short_block
usemtl white
v 130.0 165.0 65.0
v 82.0 165.0 225.0
v 240.0 165.0 272.0
v 290.0 165.0 114.0
f -4 -3 -2 -1
v 290.0 0.0 114.0
v 290.0 165.0 114.0
v 240.0 165.0 272.0
v 240.0 0.0 272.0
f -4 -3 -2 -1
v 130.0 0.0 65.0
v 130.0 165.0 65.0
v 290.0 165.0 114.0
v 290.0 0.0 114.0
f -4 -3 -2 -1
v 82.0 0.0 225.0
v 82.0 165.0 225.0
v 130.0 165.0 65.0
v 130.0 0.0 65.0
f -4 -3 -2 -1
v 240.0 0.0 272.0
v 240.0 165.0 272.0
v 82.0 165.0 225.0
v 82.0 0.0 225.0
f -4 -3 -2 -1
o tall_block
usemtl white
v 423.0 330.0 247.0
v 265.0 330.0 296.0
v 314.0 330.0 456.0
v 472.0 330.0 406.0
f -4 -3 -2 -1
usemtl white
v 423.0 0.0 247.0
v 423.0 330.0 247.0
v 472.0 330.0 406.0
v 472.0 0.0 406.0
f -4 -3 -2 -1
v 472.0 0.0 406.0
v 472.0 330.0 406.0
v 314.0 330.0 456.0
v 314.0 0.0 456.0
f -4 -3 -2 -1
v 314.0 0.0 456.0
v 314.0 330.0 456.0
v 265.0 330.0 296.0
v 265.0 0.0 296.0
f -4 -3 -2 -1
v 265.0 0.0 296.0
v 265.0 330.0 296.0
v 423.0 330.0 247.0
v 423.0 0.0 247.0
f -4 -3 -2 -1

View file

@ -0,0 +1,13 @@
# Blender 4.2.0 MTL File: 'tescoPiwo.blend'
# www.blender.org
newmtl Material.001
Ns 360.000000
Ka 0.500000 0.500000 0.500000
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.500000
d 1.000000
illum 3
map_Kd tescoPiwoTexture.png
map_Bump -bm 1.000000 /home/przmk/Downloads/Downloads/tescoPiwoNormal.png

1751
assets/models/tescoPiwo.obj Normal file

File diff suppressed because it is too large Load diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 179 KiB

View file

@ -1,6 +1,6 @@
const std = @import("std");
const sdl = @import("libs/sdl/build.zig");
const vkgen = @import("vulkan_zig");
const sdl = @import("sdl");
const vkgen = @import("vulkan");
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
@ -20,7 +20,7 @@ pub fn build(b: *std.Build) void {
// --- Dependencies ---
// Vulkan
const vkzig_dep = b.dependency("vulkan_zig", .{
const vkzig_dep = b.dependency("vulkan", .{
.registry = @as([]const u8, b.pathFromRoot("./vk.xml")),
});
const vkzig_bindings = vkzig_dep.module("vulkan-zig");
@ -28,15 +28,18 @@ pub fn build(b: *std.Build) void {
const shader_comp = vkgen.ShaderCompileStep.create(
b,
&[_][]const u8{ "glslc", "--target-env=vulkan1.3" },
.{ .real_path = "glslc" },
&[_][]const u8{"--target-env=vulkan1.3"},
"-o",
);
shader_comp.add("shader_frag", "src/shaders/shader.frag", .{});
shader_comp.add("shader_vert", "src/shaders/shader.vert", .{});
shader_comp.add("second_frag", "src/shaders/second.frag", .{});
shader_comp.add("second_vert", "src/shaders/second.vert", .{});
exe.root_module.addImport("shaders", shader_comp.getModule());
// SDL2
const sdl_sdk = sdl.init(b, null, null);
const sdl_sdk = sdl.init(b, .{});
sdl_sdk.link(exe, .dynamic, sdl.Library.SDL2);
exe.root_module.addImport("sdl2", sdl_sdk.getWrapperModuleVulkan(vkzig_bindings));

View file

@ -6,9 +6,10 @@
.dependencies = .{
.zmath = .{ .path = "libs/zmath" },
.zstbi = .{ .path = "libs/zstbi" },
.vulkan_zig = .{
.url = "https://github.com/Snektron/vulkan-zig/archive/9f6e6177b1fdb3ed22231d9216a24480e84cfa5e.tar.gz",
.hash = "1220f2961df224f7d35dee774b26194b8b937cc252fa8e4023407776c58521d53e38",
.sdl = .{ .path = "libs/sdl" },
.vulkan = .{
.url = "https://github.com/Snektron/vulkan-zig/archive/f7b21d034f527765f62935de1b62855033621989.tar.gz",
.hash = "12201e484e173e70634e664864763223427703e677f28c63ebec9332513c8ca5121c",
},
.obj = .{
.url = "https://github.com/chip2n/zig-obj/archive/58f524ed6834790b29ac1e97b2f9e6b7de7b5346.tar.gz",

@ -1 +1 @@
Subproject commit 9663dc70c19b13afcb4b9f596c928d7b2838e548
Subproject commit 172a84e7b5ce7d4891b8b970c68f4532f96aa7e9

View file

@ -329,7 +329,7 @@ pub inline fn boolx16(
// zig fmt: on
pub inline fn veclen(comptime T: type) comptime_int {
return @typeInfo(T).Vector.len;
return @typeInfo(T).vector.len;
}
pub inline fn splat(comptime T: type, value: f32) T {
@ -413,14 +413,14 @@ pub inline fn storeArr4(arr: *[4]f32, v: F32x4) void {
}
pub inline fn arr3Ptr(ptr: anytype) *const [3]f32 {
comptime assert(@typeInfo(@TypeOf(ptr)) == .Pointer);
comptime assert(@typeInfo(@TypeOf(ptr)) == .pointer);
const T = std.meta.Child(@TypeOf(ptr));
comptime assert(T == F32x4);
return @as(*const [3]f32, @ptrCast(ptr));
}
pub inline fn arrNPtr(ptr: anytype) [*]const f32 {
comptime assert(@typeInfo(@TypeOf(ptr)) == .Pointer);
comptime assert(@typeInfo(@TypeOf(ptr)) == .pointer);
const T = std.meta.Child(@TypeOf(ptr));
comptime assert(T == Mat or T == F32x4 or T == F32x8 or T == F32x16);
return @as([*]const f32, @ptrCast(ptr));

View file

@ -71,7 +71,7 @@ pub fn main() !void {
var angle: f32 = 0.0;
const move_speed: f32 = 1000;
const model_handle = try vulkan_renderer.createMeshModel("teapot.obj");
const model_handle = try vulkan_renderer.createMeshModel("tescoPiwo.obj");
mainLoop: while (true) {
while (sdl.pollEvent()) |ev| {

22
src/shaders/second.frag Normal file
View file

@ -0,0 +1,22 @@
#version 450
// Colour output from subpass 1
layout(input_attachment_index = 0, binding = 0) uniform subpassInput inputColour;
// Depth output from subpass 1
layout(input_attachment_index = 1, binding = 1) uniform subpassInput inputDepth;
layout(location = 0) out vec4 colour;
void main() {
int xHalf = 800 / 2;
if (gl_FragCoord.x > xHalf) {
float lowerBound = 0.98;
float upperBound = 1;
float depth = subpassLoad(inputDepth).r;
float depthColourScaled = 1.0f - ((depth - lowerBound) / (upperBound - lowerBound));
colour = vec4(subpassLoad(inputColour).rgb * depthColourScaled, 1.0f);
} else {
colour = subpassLoad(inputColour).rgba;
}
}

12
src/shaders/second.vert Normal file
View file

@ -0,0 +1,12 @@
#version 450
// Array for triangle that fills screen
vec2 positions[3] = vec2[](
vec2(3.0, -1.0),
vec2(-1.0, -1.0),
vec2(-1.0, 3.0)
);
void main() {
gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0);
}

View file

@ -82,19 +82,26 @@ pub const VulkanRenderer = struct {
swapchain_framebuffers: []vk.Framebuffer,
command_buffers: []CommandBuffer,
depth_buffer_image: vk.Image,
depth_buffer_image_memory: vk.DeviceMemory,
depth_buffer_image_view: vk.ImageView,
depth_buffer_image: []vk.Image,
depth_buffer_image_memory: []vk.DeviceMemory,
depth_buffer_image_view: []vk.ImageView,
colour_buffer_image: []vk.Image,
colour_buffer_image_memory: []vk.DeviceMemory,
colour_buffer_image_view: []vk.ImageView,
// Descriptors
descriptor_set_layout: vk.DescriptorSetLayout,
sampler_set_layout: vk.DescriptorSetLayout,
input_set_layout: vk.DescriptorSetLayout,
push_constant_range: vk.PushConstantRange,
descriptor_pool: vk.DescriptorPool,
sampler_descriptor_pool: vk.DescriptorPool,
input_descriptor_pool: vk.DescriptorPool,
descriptor_sets: []vk.DescriptorSet,
sampler_descriptor_sets: std.ArrayList(vk.DescriptorSet),
input_descriptor_sets: []vk.DescriptorSet,
vp_uniform_buffer: []vk.Buffer,
vp_uniform_buffer_memory: []vk.DeviceMemory,
@ -109,6 +116,10 @@ pub const VulkanRenderer = struct {
// Pipeline
graphics_pipeline: vk.Pipeline,
pipeline_layout: vk.PipelineLayout,
second_pipeline: vk.Pipeline,
second_pipeline_layout: vk.PipelineLayout,
render_pass: vk.RenderPass,
// Pools
@ -146,6 +157,7 @@ pub const VulkanRenderer = struct {
try self.getPhysicalDevice();
try self.createLogicalDevice();
try self.createSwapchain();
try self.createColourBufferImage();
try self.createDepthBufferImage();
try self.createRenderPass();
try self.createDescriptorSetLayout();
@ -154,12 +166,14 @@ pub const VulkanRenderer = struct {
try self.createFramebuffers();
try self.createCommandPool();
self.sampler_descriptor_sets = try std.ArrayList(vk.DescriptorSet).initCapacity(self.allocator, self.swapchain_images.len);
try self.createCommandBuffers();
try self.createTextureSampler();
try self.createUniformBuffers();
try self.createDescriptorPool();
try self.createDescriptorSets();
try self.createInputDescriptorSets();
try self.createSynchronisation();
self.image_files = std.ArrayList(img.Image).init(self.allocator);
@ -167,7 +181,6 @@ pub const VulkanRenderer = struct {
self.texture_image_memory = std.ArrayList(vk.DeviceMemory).init(self.allocator);
self.texture_image_views = std.ArrayList(vk.ImageView).init(self.allocator);
self.model_list = std.ArrayList(MeshModel).init(allocator);
self.sampler_descriptor_sets = std.ArrayList(vk.DescriptorSet).init(self.allocator);
const aspect: f32 = @as(f32, @floatFromInt(self.extent.width)) / @as(f32, @floatFromInt(self.extent.height));
self.ubo_view_projection.projection = zm.perspectiveFovRh(
@ -289,15 +302,34 @@ pub const VulkanRenderer = struct {
self.texture_image_memory.deinit();
self.texture_image_views.deinit();
self.device.destroyImageView(self.depth_buffer_image_view, null);
self.device.destroyImage(self.depth_buffer_image, null);
self.device.freeMemory(self.depth_buffer_image_memory, null);
for (0..self.depth_buffer_image.len) |i| {
self.device.destroyImageView(self.depth_buffer_image_view[i], null);
self.device.destroyImage(self.depth_buffer_image[i], null);
self.device.freeMemory(self.depth_buffer_image_memory[i], null);
}
self.allocator.free(self.depth_buffer_image);
self.allocator.free(self.depth_buffer_image_memory);
self.allocator.free(self.depth_buffer_image_view);
for (0..self.colour_buffer_image.len) |i| {
self.device.destroyImageView(self.colour_buffer_image_view[i], null);
self.device.destroyImage(self.colour_buffer_image[i], null);
self.device.freeMemory(self.colour_buffer_image_memory[i], null);
}
self.allocator.free(self.colour_buffer_image);
self.allocator.free(self.colour_buffer_image_memory);
self.allocator.free(self.colour_buffer_image_view);
self.device.destroyDescriptorPool(self.input_descriptor_pool, null);
self.device.destroyDescriptorPool(self.descriptor_pool, null);
self.device.destroyDescriptorSetLayout(self.descriptor_set_layout, null);
self.device.destroyDescriptorPool(self.sampler_descriptor_pool, null);
self.device.destroyDescriptorSetLayout(self.sampler_set_layout, null);
self.device.destroyDescriptorSetLayout(self.input_set_layout, null);
self.sampler_descriptor_sets.deinit();
self.allocator.free(self.input_descriptor_sets);
for (0..self.swapchain_images.len) |i| {
self.device.destroyBuffer(self.vp_uniform_buffer[i], null);
@ -322,6 +354,8 @@ pub const VulkanRenderer = struct {
self.allocator.free(self.swapchain_framebuffers);
self.device.destroyPipeline(self.second_pipeline, null);
self.device.destroyPipelineLayout(self.second_pipeline_layout, null);
self.device.destroyPipeline(self.graphics_pipeline, null);
self.device.destroyPipelineLayout(self.pipeline_layout, null);
self.device.destroyRenderPass(self.render_pass, null);
@ -523,8 +557,65 @@ pub const VulkanRenderer = struct {
fn createRenderPass(self: *Self) !void {
// -- Attachments --
// Colour attachment of the render pass
var subpasses: [2]vk.SubpassDescription = undefined;
// Subpass 1 attachments and references (input attachments)
// Colour attachment (input)
const colour_format = chooseSupportedFormat(
self.physical_device,
self.instance,
&[_]vk.Format{.r8g8b8a8_srgb},
.optimal,
.{ .color_attachment_bit = true },
);
const colour_attachment: vk.AttachmentDescription = .{
.format = colour_format.?,
.samples = .{ .@"1_bit" = true },
.load_op = .clear,
.store_op = .dont_care,
.stencil_load_op = .dont_care,
.stencil_store_op = .dont_care,
.initial_layout = .undefined,
.final_layout = .color_attachment_optimal,
};
// Depth attachment (input)
const depth_attachment: vk.AttachmentDescription = .{
.format = self.depth_format,
.samples = .{ .@"1_bit" = true },
.load_op = .clear,
.store_op = .dont_care,
.stencil_load_op = .dont_care,
.stencil_store_op = .dont_care,
.initial_layout = .undefined,
.final_layout = .depth_stencil_attachment_optimal,
};
// Colour attachment (input) reference
const colour_attachment_reference: vk.AttachmentReference = .{
.attachment = 1,
.layout = .color_attachment_optimal,
};
// Depth attachment (input) reference
const depth_attachment_reference: vk.AttachmentReference = .{
.attachment = 2,
.layout = .depth_stencil_attachment_optimal,
};
subpasses[0] = .{
.pipeline_bind_point = .graphics, // Pipeline type subpass is to be bound to
.color_attachment_count = 1,
.p_color_attachments = @ptrCast(&colour_attachment_reference),
.p_depth_stencil_attachment = &depth_attachment_reference,
};
// Subpass 2 attachments and references
// Colour attachment of the render pass
const swapchain_colour_attachment: vk.AttachmentDescription = .{
.format = self.swapchain_image_format, // Format to use for attachment
.samples = .{ .@"1_bit" = true }, // Number of samples to write for multisampling
.load_op = .clear, // Describes what to do with attachment before rendering
@ -537,42 +628,38 @@ pub const VulkanRenderer = struct {
.final_layout = vk.ImageLayout.present_src_khr, // Image data layout after render pass (to change to)
};
// Depth attachment of render pass
const depth_attachment: vk.AttachmentDescription = .{
.format = self.depth_format,
.samples = .{ .@"1_bit" = true },
.load_op = .clear,
.store_op = .dont_care,
.stencil_load_op = .dont_care,
.stencil_store_op = .dont_care,
.initial_layout = .undefined,
.final_layout = .depth_stencil_attachment_optimal,
};
// -- References --
// Attachment reference uses an attachment index that refers to index in the attachment list passed to render pass create info
const colour_attachment_reference: vk.AttachmentReference = .{
const swapchain_colour_attachment_reference: vk.AttachmentReference = .{
.attachment = 0,
.layout = vk.ImageLayout.color_attachment_optimal,
};
const depth_attachment_reference: vk.AttachmentReference = .{
.attachment = 1,
.layout = vk.ImageLayout.depth_stencil_attachment_optimal,
// References to attachments that subpass will take input from
const input_references = [_]vk.AttachmentReference{
.{
.attachment = 1, // Colour attachment
.layout = .shader_read_only_optimal,
},
.{
.attachment = 2, // Depth attachment
.layout = .shader_read_only_optimal,
},
};
// Information about a particular subpass the render pass is using
const subpass: vk.SubpassDescription = .{
.pipeline_bind_point = .graphics, // Pipeline type subpass is to be bound to
subpasses[1] = .{
.pipeline_bind_point = .graphics,
.color_attachment_count = 1,
.p_color_attachments = @ptrCast(&colour_attachment_reference),
.p_depth_stencil_attachment = &depth_attachment_reference,
.p_color_attachments = @ptrCast(&swapchain_colour_attachment_reference),
.input_attachment_count = @intCast(input_references.len),
.p_input_attachments = &input_references,
};
// -- Subpass dependencies
// Need to determine when layout transitions occur using subpass dependencies
const subpass_dependencies = [2]vk.SubpassDependency{
const subpass_dependencies = [_]vk.SubpassDependency{
// Conversion from VK_IMAGE_LAYOUT_UNDEFINED to VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
vk.SubpassDependency{
.{
// Transition must happen after...
.src_subpass = vk.SUBPASS_EXTERNAL, // Subpass index (VK_SUBPASS_EXTERNAL = outside of renderpass)
.src_stage_mask = .{ .bottom_of_pipe_bit = true }, // Pipeline stage
@ -582,8 +669,17 @@ pub const VulkanRenderer = struct {
.dst_stage_mask = .{ .color_attachment_output_bit = true },
.dst_access_mask = .{ .color_attachment_read_bit = true, .color_attachment_write_bit = true },
},
// Subpass 1 layout (colour/depth) to subpass 2 layout (shader read)
.{
.src_subpass = 0,
.src_stage_mask = .{ .color_attachment_output_bit = true },
.src_access_mask = .{ .color_attachment_write_bit = true },
.dst_subpass = 1,
.dst_stage_mask = .{ .fragment_shader_bit = true },
.dst_access_mask = .{ .shader_read_bit = true },
},
// Conversion from VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL to VK_IMAGE_LAYOUT_PRESENT_SRC_KHR
vk.SubpassDependency{
.{
// Transition must happen after...
.src_subpass = 0,
.src_stage_mask = .{ .color_attachment_output_bit = true },
@ -596,13 +692,13 @@ pub const VulkanRenderer = struct {
};
// Order matters
const render_pass_attachments = [_]vk.AttachmentDescription{ colour_attachment, depth_attachment };
const render_pass_attachments = [_]vk.AttachmentDescription{ swapchain_colour_attachment, colour_attachment, depth_attachment };
const render_pass_create_info: vk.RenderPassCreateInfo = .{
.attachment_count = @intCast(render_pass_attachments.len),
.p_attachments = &render_pass_attachments,
.subpass_count = 1,
.p_subpasses = @ptrCast(&subpass),
.subpass_count = @intCast(subpasses.len),
.p_subpasses = &subpasses,
.dependency_count = @intCast(subpass_dependencies.len),
.p_dependencies = &subpass_dependencies,
};
@ -651,6 +747,34 @@ pub const VulkanRenderer = struct {
};
self.sampler_set_layout = try self.device.createDescriptorSetLayout(&texture_layout_info, null);
// -- Create input attachment image descriptor set layout
// Colour input binding
const colour_input_layout_binding: vk.DescriptorSetLayoutBinding = .{
.binding = 0,
.descriptor_type = .input_attachment,
.descriptor_count = 1,
.stage_flags = .{ .fragment_bit = true },
};
// Depth input binding
const depth_input_layout_binding: vk.DescriptorSetLayoutBinding = .{
.binding = 1,
.descriptor_type = .input_attachment,
.descriptor_count = 1,
.stage_flags = .{ .fragment_bit = true },
};
// Array of input attachment bindings
const input_bindings = [_]vk.DescriptorSetLayoutBinding{ colour_input_layout_binding, depth_input_layout_binding };
// Create a descriptor set layout for input attachments
const input_layout_create_info: vk.DescriptorSetLayoutCreateInfo = .{
.binding_count = @intCast(input_bindings.len),
.p_bindings = &input_bindings,
};
self.input_set_layout = try self.device.createDescriptorSetLayout(&input_layout_create_info, null);
}
fn createPushConstantRange(self: *Self) !void {
@ -662,7 +786,45 @@ pub const VulkanRenderer = struct {
};
}
fn createColourBufferImage(self: *Self) !void {
self.colour_buffer_image = try self.allocator.alloc(vk.Image, self.swapchain_images.len);
self.colour_buffer_image_memory = try self.allocator.alloc(vk.DeviceMemory, self.swapchain_images.len);
self.colour_buffer_image_view = try self.allocator.alloc(vk.ImageView, self.swapchain_images.len);
// Get supported format for colour attachment
const colour_format = chooseSupportedFormat(
self.physical_device,
self.instance,
&[_]vk.Format{.r8g8b8a8_srgb},
.optimal,
.{ .color_attachment_bit = true },
) orelse return error.FormatNotSupported;
// Create colour buffers
for (0..self.colour_buffer_image.len) |i| {
self.colour_buffer_image[i] = try self.createImage(
self.extent.width,
self.extent.height,
colour_format,
.optimal,
.{ .color_attachment_bit = true, .input_attachment_bit = true },
.{ .device_local_bit = true },
&self.colour_buffer_image_memory[i],
);
self.colour_buffer_image_view[i] = try self.createImageView(
self.colour_buffer_image[i],
colour_format,
.{ .color_bit = true },
);
}
}
fn createDepthBufferImage(self: *Self) !void {
self.depth_buffer_image = try self.allocator.alloc(vk.Image, self.swapchain_images.len);
self.depth_buffer_image_memory = try self.allocator.alloc(vk.DeviceMemory, self.swapchain_images.len);
self.depth_buffer_image_view = try self.allocator.alloc(vk.ImageView, self.swapchain_images.len);
// Get supported depth buffer format
const formats = [_]vk.Format{ .d32_sfloat_s8_uint, .d32_sfloat, .d24_unorm_s8_uint };
self.depth_format = chooseSupportedFormat(
@ -673,19 +835,21 @@ pub const VulkanRenderer = struct {
.{ .depth_stencil_attachment_bit = true },
) orelse return error.UnsupportedDepthBufferFormat;
for (0..self.depth_buffer_image.len) |i| {
// Create depth buffer image
self.depth_buffer_image = try self.createImage(
self.depth_buffer_image[i] = try self.createImage(
self.extent.width,
self.extent.height,
self.depth_format,
.optimal,
.{ .depth_stencil_attachment_bit = true },
.{ .depth_stencil_attachment_bit = true, .input_attachment_bit = true },
.{ .device_local_bit = true },
&self.depth_buffer_image_memory,
&self.depth_buffer_image_memory[i],
);
// Create depth buffer image view
self.depth_buffer_image_view = try self.createImageView(self.depth_buffer_image, self.depth_format, .{ .depth_bit = true });
self.depth_buffer_image_view[i] = try self.createImageView(self.depth_buffer_image[i], self.depth_format, .{ .depth_bit = true });
}
}
fn createGraphicsPipeline(self: *Self) !void {
@ -705,14 +869,14 @@ pub const VulkanRenderer = struct {
// -- Shader stage creation information --
// Vertex stage creation information
const vertex_shader_create_info: vk.PipelineShaderStageCreateInfo = .{
var vertex_shader_create_info: vk.PipelineShaderStageCreateInfo = .{
.stage = .{ .vertex_bit = true },
.module = vert,
.p_name = "main",
};
// Fragment stage creation information
const fragment_shader_create_info: vk.PipelineShaderStageCreateInfo = .{
var fragment_shader_create_info: vk.PipelineShaderStageCreateInfo = .{
.stage = .{ .fragment_bit = true },
.module = frag,
.p_name = "main",
@ -758,7 +922,7 @@ pub const VulkanRenderer = struct {
};
// -- Vertex input --
const vertex_input_create_info: vk.PipelineVertexInputStateCreateInfo = .{
var vertex_input_create_info: vk.PipelineVertexInputStateCreateInfo = .{
.vertex_binding_description_count = 1,
.p_vertex_binding_descriptions = @ptrCast(&binding_description), // List of vertex binding descriptions (data spacing, stride info)
.vertex_attribute_description_count = @intCast(attribute_descriptions.len),
@ -864,7 +1028,7 @@ pub const VulkanRenderer = struct {
self.pipeline_layout = try self.device.createPipelineLayout(&pipeline_layout_create_info, null);
// -- Depth stencil testing --
const depth_stencil_create_info: vk.PipelineDepthStencilStateCreateInfo = .{
var depth_stencil_create_info: vk.PipelineDepthStencilStateCreateInfo = .{
.depth_test_enable = vk.TRUE, // Enable checking depth to determine fragment write
.depth_write_enable = vk.TRUE, // Enable writing to depth buffer to replace all values
.depth_compare_op = .less, // Comparison operation that allows an overwrite (is in front)
@ -877,7 +1041,7 @@ pub const VulkanRenderer = struct {
};
// -- Graphics pipeline creation --
const pipeline_create_info: vk.GraphicsPipelineCreateInfo = .{
var pipeline_create_info: vk.GraphicsPipelineCreateInfo = .{
.stage_count = @intCast(shader_create_infos.len), // Number of shader stages
.p_stages = &shader_create_infos, // List of shader stages
.p_vertex_input_state = &vertex_input_create_info,
@ -903,6 +1067,57 @@ pub const VulkanRenderer = struct {
null,
@ptrCast(&self.graphics_pipeline),
);
// -- Create second pass pipeline
// Second pass shaders
const second_vert_shader_module = try self.device.createShaderModule(&.{
.code_size = shaders.second_vert.len,
.p_code = @ptrCast(&shaders.second_vert),
}, null);
defer self.device.destroyShaderModule(second_vert_shader_module, null);
const second_frag_shader_module = try self.device.createShaderModule(&.{
.code_size = shaders.second_frag.len,
.p_code = @ptrCast(&shaders.second_frag),
}, null);
defer self.device.destroyShaderModule(second_frag_shader_module, null);
// Set new shaders
vertex_shader_create_info.module = second_vert_shader_module;
fragment_shader_create_info.module = second_frag_shader_module;
const second_shader_stages = [_]vk.PipelineShaderStageCreateInfo{ vertex_shader_create_info, fragment_shader_create_info };
// No vertex data for second pass
vertex_input_create_info.vertex_binding_description_count = 0;
vertex_input_create_info.p_vertex_binding_descriptions = null;
vertex_input_create_info.vertex_attribute_description_count = 0;
vertex_input_create_info.p_vertex_attribute_descriptions = null;
// Don't want to write to depth buffer
depth_stencil_create_info.depth_write_enable = vk.FALSE;
// Create new pipeline layout
const second_pipeline_layout_create_info: vk.PipelineLayoutCreateInfo = .{
.set_layout_count = 1,
.p_set_layouts = @ptrCast(&self.input_set_layout),
};
self.second_pipeline_layout = try self.device.createPipelineLayout(&second_pipeline_layout_create_info, null);
pipeline_create_info.stage_count = @intCast(second_shader_stages.len);
pipeline_create_info.p_stages = &second_shader_stages;
pipeline_create_info.layout = self.second_pipeline_layout;
pipeline_create_info.subpass = 1;
// Create second pipeline
_ = try self.device.createGraphicsPipelines(
.null_handle,
1,
@ptrCast(&pipeline_create_info),
null,
@ptrCast(&self.second_pipeline),
);
}
fn createFramebuffers(self: *Self) !void {
@ -911,7 +1126,11 @@ pub const VulkanRenderer = struct {
// Create a frammebuffer for each swapchain image
for (self.swapchain_images, 0..) |swapchain_image, i| {
// Order matters
const attachments = [_]vk.ImageView{ swapchain_image.image_view, self.depth_buffer_image_view };
const attachments = [_]vk.ImageView{
swapchain_image.image_view,
self.colour_buffer_image_view[i],
self.depth_buffer_image_view[i],
};
const framebuffer_create_info: vk.FramebufferCreateInfo = .{
.render_pass = self.render_pass, // Render pass layout the frambuffer will be used with
@ -1056,6 +1275,30 @@ pub const VulkanRenderer = struct {
};
self.sampler_descriptor_pool = try self.device.createDescriptorPool(&sampler_pool_create_info, null);
// -- Create input attachment descriptor pool
// Colour attachment pool size
const colour_input_pool_size: vk.DescriptorPoolSize = .{
.type = .input_attachment,
.descriptor_count = @intCast(self.colour_buffer_image_view.len),
};
// Depth attachment pool size
const depth_input_pool_size: vk.DescriptorPoolSize = .{
.type = .input_attachment,
.descriptor_count = @intCast(self.depth_buffer_image_view.len),
};
const input_pool_sizes = [_]vk.DescriptorPoolSize{ colour_input_pool_size, depth_input_pool_size };
// Create input attachment pool
const input_pool_create_info: vk.DescriptorPoolCreateInfo = .{
.max_sets = @intCast(self.swapchain_images.len),
.pool_size_count = @intCast(input_pool_sizes.len),
.p_pool_sizes = &input_pool_sizes,
};
self.input_descriptor_pool = try self.device.createDescriptorPool(&input_pool_create_info, null);
}
fn createDescriptorSets(self: *Self) !void {
@ -1108,6 +1351,74 @@ pub const VulkanRenderer = struct {
}
}
fn createInputDescriptorSets(self: *Self) !void {
self.input_descriptor_sets = try self.allocator.alloc(vk.DescriptorSet, self.swapchain_images.len);
// Fill array of layouts ready for set creation
var set_layouts = try self.allocator.alloc(vk.DescriptorSetLayout, self.swapchain_images.len);
defer self.allocator.free(set_layouts);
for (0..set_layouts.len) |i| {
set_layouts[i] = self.input_set_layout;
}
// Input attachment descriptor set allocation info
const set_alloc_info: vk.DescriptorSetAllocateInfo = .{
.descriptor_pool = self.input_descriptor_pool,
.descriptor_set_count = @intCast(self.swapchain_images.len),
.p_set_layouts = set_layouts.ptr,
};
// Allocate descriptor sets
try self.device.allocateDescriptorSets(&set_alloc_info, self.input_descriptor_sets.ptr);
// Update each descriptor set with input attachment
for (0..self.swapchain_images.len) |i| {
// Colour attachment descriptor
const colour_attachment_descriptor: vk.DescriptorImageInfo = .{
.image_layout = .shader_read_only_optimal,
.image_view = self.colour_buffer_image_view[i],
.sampler = .null_handle,
};
// Colour attachment descriptor write
const colour_write: vk.WriteDescriptorSet = .{
.dst_set = self.input_descriptor_sets[i],
.dst_binding = 0,
.dst_array_element = 0,
.descriptor_type = .input_attachment,
.descriptor_count = 1,
.p_image_info = @ptrCast(&colour_attachment_descriptor),
.p_buffer_info = undefined,
.p_texel_buffer_view = undefined,
};
// Depth attachment descriptor
const depth_attachment_descriptor: vk.DescriptorImageInfo = .{
.image_layout = .shader_read_only_optimal,
.image_view = self.depth_buffer_image_view[i],
.sampler = .null_handle,
};
// Depth attachment descriptor write
const depth_write: vk.WriteDescriptorSet = .{
.dst_set = self.input_descriptor_sets[i],
.dst_binding = 1,
.dst_array_element = 0,
.descriptor_type = .input_attachment,
.descriptor_count = 1,
.p_image_info = @ptrCast(&depth_attachment_descriptor),
.p_buffer_info = undefined,
.p_texel_buffer_view = undefined,
};
// List of input descriptor set writes
const set_writes = [_]vk.WriteDescriptorSet{ colour_write, depth_write };
// Update descriptor sets
self.device.updateDescriptorSets(@intCast(set_writes.len), &set_writes, 0, null);
}
}
fn updateUniformBuffers(self: *Self, image_index: u32) !void {
// Copy VP data
const data = try self.device.mapMemory(
@ -1130,7 +1441,8 @@ pub const VulkanRenderer = struct {
};
const clear_values = [_]vk.ClearValue{
.{ .color = .{ .float_32 = [4]f32{ 0.6, 0.65, 0.4, 1.0 } } },
.{ .color = .{ .float_32 = .{ 0.0, 0.0, 0.0, 0.0 } } },
.{ .color = .{ .float_32 = .{ 0.6, 0.65, 0.4, 1.0 } } },
.{ .depth_stencil = .{ .depth = 1.0, .stencil = 1 } },
};
@ -1204,6 +1516,21 @@ pub const VulkanRenderer = struct {
}
}
// Start second subpass
command_buffer.nextSubpass(.@"inline");
command_buffer.bindPipeline(.graphics, self.second_pipeline);
command_buffer.bindDescriptorSets(
.graphics,
self.second_pipeline_layout,
0,
1,
@ptrCast(&self.input_descriptor_sets[current_image]),
0,
null,
);
command_buffer.draw(3, 1, 0, 0);
// End render pass
command_buffer.endRenderPass();
}