forked from mirror/vulkan-zig
389 lines
12 KiB
Zig
389 lines
12 KiB
Zig
const std = @import("std");
|
|
const vk = @import("vulkan");
|
|
const c = @import("c.zig");
|
|
const resources = @import("resources");
|
|
const GraphicsContext = @import("graphics_context.zig").GraphicsContext;
|
|
const Swapchain = @import("swapchain.zig").Swapchain;
|
|
const Allocator = std.mem.Allocator;
|
|
|
|
const app_name = "vulkan-zig example";
|
|
|
|
pub fn main() !void {
|
|
if (c.glfwInit() != c.GLFW_TRUE) return error.GlfwInitFailed;
|
|
defer c.glfwTerminate();
|
|
|
|
var extent = vk.Extent2D{.width = 800, .height = 600};
|
|
|
|
c.glfwWindowHint(c.GLFW_CLIENT_API, c.GLFW_NO_API);
|
|
const window = c.glfwCreateWindow(
|
|
@intCast(c_int, extent.width),
|
|
@intCast(c_int, extent.height),
|
|
app_name,
|
|
null,
|
|
null
|
|
) orelse return error.WindowInitFailed;
|
|
defer c.glfwDestroyWindow(window);
|
|
|
|
const allocator = std.heap.page_allocator;
|
|
|
|
const gc = try GraphicsContext.init(allocator, app_name, window);
|
|
defer gc.deinit();
|
|
|
|
std.debug.print("Using device: {}\n", .{gc.deviceName()});
|
|
|
|
var swapchain = try Swapchain.init(&gc, allocator, extent);
|
|
defer swapchain.deinit();
|
|
|
|
const pipeline_layout = try gc.vkd.createPipelineLayout(gc.dev, .{
|
|
.flags = .{},
|
|
.set_layout_count = 0,
|
|
.p_set_layouts = undefined,
|
|
.push_constant_range_count = 0,
|
|
.p_push_constant_ranges = undefined,
|
|
}, null);
|
|
defer gc.vkd.destroyPipelineLayout(gc.dev, pipeline_layout, null);
|
|
|
|
const render_pass = try createRenderPass(&gc, &swapchain);
|
|
defer gc.vkd.destroyRenderPass(gc.dev, render_pass, null);
|
|
|
|
const pipeline = try createPipeline(&gc, extent, pipeline_layout, render_pass);
|
|
defer gc.vkd.destroyPipeline(gc.dev, pipeline, null);
|
|
|
|
const pool = try gc.vkd.createCommandPool(gc.dev, .{
|
|
.flags = .{},
|
|
.queue_family_index = gc.graphics_queue.family,
|
|
}, null);
|
|
defer gc.vkd.destroyCommandPool(gc.dev, pool, null);
|
|
|
|
var cmdbufs = try createCommandBuffers(&gc, pool, allocator, swapchain);
|
|
defer destroyCommandBuffers(&gc, pool, allocator, cmdbufs);
|
|
|
|
while (c.glfwWindowShouldClose(window) == c.GLFW_FALSE) {
|
|
const cmdbuf = cmdbufs[swapchain.image_index];
|
|
|
|
const state = swapchain.present(cmdbuf) catch |err| switch (err) {
|
|
error.OutOfDateKHR => Swapchain.PresentState.suboptimal,
|
|
else => |narrow| return narrow,
|
|
};
|
|
|
|
if (state == .suboptimal) {
|
|
var w: c_int = undefined;
|
|
var h: c_int = undefined;
|
|
c.glfwGetWindowSize(window, &w, &h);
|
|
extent.width = @intCast(u32, w);
|
|
extent.height = @intCast(u32, h);
|
|
try swapchain.recreate(extent);
|
|
|
|
destroyCommandBuffers(&gc, pool, allocator, cmdbufs);
|
|
cmdbufs = try createCommandBuffers(&gc, pool, allocator, swapchain);
|
|
}
|
|
|
|
c.glfwSwapBuffers(window);
|
|
c.glfwPollEvents();
|
|
|
|
try gc.vkd.queueWaitIdle(gc.graphics_queue.handle);
|
|
}
|
|
}
|
|
|
|
fn createCommandBuffers(
|
|
gc: *const GraphicsContext,
|
|
pool: vk.CommandPool,
|
|
allocator: *Allocator,
|
|
swapchain: Swapchain,
|
|
) ![]vk.CommandBuffer {
|
|
const cmdbufs = try allocator.alloc(vk.CommandBuffer, swapchain.swap_images.len);
|
|
errdefer allocator.free(cmdbufs);
|
|
|
|
try gc.vkd.allocateCommandBuffers(gc.dev, .{
|
|
.command_pool = pool,
|
|
.level = .primary,
|
|
.command_buffer_count = @truncate(u32, cmdbufs.len),
|
|
}, cmdbufs.ptr);
|
|
errdefer gc.vkd.freeCommandBuffers(gc.dev, pool, @truncate(u32, cmdbufs.len), cmdbufs.ptr);
|
|
|
|
const subresource_range = vk.ImageSubresourceRange{
|
|
.aspect_mask = .{.color_bit = true},
|
|
.base_mip_level = 0,
|
|
.level_count = 1,
|
|
.base_array_layer = 0,
|
|
.layer_count = 1,
|
|
};
|
|
|
|
const color = vk.ClearColorValue{.float_32 = .{1, 0, 1, 1}};
|
|
|
|
for (cmdbufs) |cmdbuf, i| {
|
|
const image = swapchain.swap_images[i].image;
|
|
|
|
try gc.vkd.beginCommandBuffer(cmdbuf, .{
|
|
.flags = .{},
|
|
.p_inheritance_info = null,
|
|
});
|
|
|
|
imageTransition(
|
|
gc,
|
|
cmdbuf,
|
|
image,
|
|
subresource_range,
|
|
.{.layout = .@"undefined", .stage = .{.top_of_pipe_bit = true}},
|
|
.{.layout = .general, .stage = .{.top_of_pipe_bit = true}},
|
|
);
|
|
|
|
gc.vkd.cmdClearColorImage(
|
|
cmdbuf,
|
|
image,
|
|
.general,
|
|
color,
|
|
1,
|
|
@ptrCast([*]const vk.ImageSubresourceRange, &subresource_range),
|
|
);
|
|
|
|
imageTransition(
|
|
gc,
|
|
cmdbuf,
|
|
image,
|
|
subresource_range,
|
|
.{.layout = .general, .stage = .{.top_of_pipe_bit = true}},
|
|
.{.layout = .present_src_khr, .stage = .{.bottom_of_pipe_bit = true}},
|
|
);
|
|
|
|
try gc.vkd.endCommandBuffer(cmdbuf);
|
|
}
|
|
|
|
return cmdbufs;
|
|
}
|
|
|
|
fn destroyCommandBuffers(gc: *const GraphicsContext, pool: vk.CommandPool, allocator: *Allocator, cmdbufs: []vk.CommandBuffer) void {
|
|
gc.vkd.freeCommandBuffers(gc.dev, pool, @truncate(u32, cmdbufs.len), cmdbufs.ptr);
|
|
allocator.free(cmdbufs);
|
|
}
|
|
|
|
fn createRenderPass(gc: *const GraphicsContext, swapchain: *const Swapchain) !vk.RenderPass {
|
|
const color_attachment = vk.AttachmentDescription{
|
|
.flags = .{},
|
|
.format = swapchain.surface_format.format,
|
|
.samples = .{.@"1_bit" = true},
|
|
.load_op = .clear,
|
|
.store_op = .store,
|
|
.stencil_load_op = .dont_care,
|
|
.stencil_store_op = .dont_care,
|
|
.initial_layout = .@"undefined",
|
|
.final_layout = .present_src_khr,
|
|
};
|
|
|
|
const color_attachment_ref = vk.AttachmentReference{
|
|
.attachment = 0,
|
|
.layout = .color_attachment_optimal,
|
|
};
|
|
|
|
const subpass = vk.SubpassDescription{
|
|
.flags = .{},
|
|
.pipeline_bind_point = .graphics,
|
|
.input_attachment_count = 0,
|
|
.p_input_attachments = undefined,
|
|
.color_attachment_count = 1,
|
|
.p_color_attachments = @ptrCast([*]const vk.AttachmentReference, &color_attachment_ref),
|
|
.p_resolve_attachments = null,
|
|
.p_depth_stencil_attachment = null,
|
|
.preserve_attachment_count = 0,
|
|
.p_preserve_attachments = undefined,
|
|
};
|
|
|
|
return try gc.vkd.createRenderPass(gc.dev, .{
|
|
.flags = .{},
|
|
.attachment_count = 1,
|
|
.p_attachments = @ptrCast([*]const vk.AttachmentDescription, &color_attachment),
|
|
.subpass_count = 1,
|
|
.p_subpasses = @ptrCast([*]const vk.SubpassDescription, &subpass),
|
|
.dependency_count = 0,
|
|
.p_dependencies = undefined,
|
|
}, null);
|
|
}
|
|
|
|
fn createPipeline(
|
|
gc: *const GraphicsContext,
|
|
extent: vk.Extent2D,
|
|
layout: vk.PipelineLayout,
|
|
render_pass: vk.RenderPass,
|
|
) !vk.Pipeline {
|
|
const vert = try gc.vkd.createShaderModule(gc.dev, .{
|
|
.flags = .{},
|
|
.code_size = resources.triangle_vert.len,
|
|
.p_code = @ptrCast([*]const u32, resources.triangle_vert),
|
|
}, null);
|
|
defer gc.vkd.destroyShaderModule(gc.dev, vert, null);
|
|
|
|
const frag = try gc.vkd.createShaderModule(gc.dev, .{
|
|
.flags = .{},
|
|
.code_size = resources.triangle_frag.len,
|
|
.p_code = @ptrCast([*]const u32, resources.triangle_frag),
|
|
}, null);
|
|
defer gc.vkd.destroyShaderModule(gc.dev, frag, null);
|
|
|
|
const pssci = [_]vk.PipelineShaderStageCreateInfo{
|
|
.{
|
|
.flags = .{},
|
|
.stage = .{.vertex_bit = true},
|
|
.module = vert,
|
|
.p_name = "main",
|
|
.p_specialization_info = null,
|
|
},
|
|
.{
|
|
.flags = .{},
|
|
.stage = .{.fragment_bit = true},
|
|
.module = frag,
|
|
.p_name = "main",
|
|
.p_specialization_info = null,
|
|
},
|
|
};
|
|
|
|
const pvisci = vk.PipelineVertexInputStateCreateInfo{
|
|
.flags = .{},
|
|
.vertex_binding_description_count = 0,
|
|
.p_vertex_binding_descriptions = undefined,
|
|
.vertex_attribute_description_count = 0,
|
|
.p_vertex_attribute_descriptions = undefined,
|
|
};
|
|
|
|
const piasci = vk.PipelineInputAssemblyStateCreateInfo{
|
|
.flags = .{},
|
|
.topology = .triangle_list,
|
|
.primitive_restart_enable = vk.FALSE,
|
|
};
|
|
|
|
const viewport = vk.Viewport{
|
|
.x = 0,
|
|
.y = 0,
|
|
.width = @intToFloat(f32, extent.width),
|
|
.height = @intToFloat(f32, extent.height),
|
|
.min_depth = 0,
|
|
.max_depth = 1,
|
|
};
|
|
|
|
const scissor = vk.Rect2D{
|
|
.offset = .{.x = 0, .y = 0},
|
|
.extent = extent,
|
|
};
|
|
|
|
const pvsci = vk.PipelineViewportStateCreateInfo{
|
|
.flags = .{},
|
|
.viewport_count = 1,
|
|
.p_viewports = @ptrCast([*]const vk.Viewport, &viewport),
|
|
.scissor_count = 1,
|
|
.p_scissors = @ptrCast([*]const vk.Rect2D, &scissor),
|
|
};
|
|
|
|
const prsci = vk.PipelineRasterizationStateCreateInfo{
|
|
.flags = .{},
|
|
.depth_clamp_enable = vk.FALSE,
|
|
.rasterizer_discard_enable = vk.FALSE,
|
|
.polygon_mode = .fill,
|
|
.cull_mode = .{.back_bit = true},
|
|
.front_face = .clockwise,
|
|
.depth_bias_enable = vk.FALSE,
|
|
.depth_bias_constant_factor = 0,
|
|
.depth_bias_clamp = 0,
|
|
.depth_bias_slope_factor = 0,
|
|
.line_width = 1,
|
|
};
|
|
|
|
const pmsci = vk.PipelineMultisampleStateCreateInfo{
|
|
.flags = .{},
|
|
.rasterization_samples = .{.@"1_bit" = true},
|
|
.sample_shading_enable = vk.FALSE,
|
|
.min_sample_shading = 1,
|
|
.p_sample_mask = null,
|
|
.alpha_to_coverage_enable = vk.FALSE,
|
|
.alpha_to_one_enable = vk.FALSE,
|
|
};
|
|
|
|
const pcbas = vk.PipelineColorBlendAttachmentState{
|
|
.blend_enable = vk.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 pcbsci = vk.PipelineColorBlendStateCreateInfo{
|
|
.flags = .{},
|
|
.logic_op_enable = vk.FALSE,
|
|
.logic_op = .copy,
|
|
.attachment_count = 1,
|
|
.p_attachments = @ptrCast([*]const vk.PipelineColorBlendAttachmentState, &pcbas),
|
|
.blend_constants = [_]f32{0, 0, 0, 0},
|
|
};
|
|
|
|
const dynstate = vk.DynamicState.viewport;
|
|
const pdsci = vk.PipelineDynamicStateCreateInfo{
|
|
.flags = .{},
|
|
.dynamic_state_count = 1,
|
|
.p_dynamic_states = @ptrCast([*]const vk.DynamicState, &dynstate),
|
|
};
|
|
|
|
const gpci = vk.GraphicsPipelineCreateInfo{
|
|
.flags = .{},
|
|
.stage_count = 2,
|
|
.p_stages = &pssci,
|
|
.p_vertex_input_state = &pvisci,
|
|
.p_input_assembly_state = &piasci,
|
|
.p_tessellation_state = null,
|
|
.p_viewport_state = &pvsci,
|
|
.p_rasterization_state = &prsci,
|
|
.p_multisample_state = &pmsci,
|
|
.p_depth_stencil_state = null,
|
|
.p_color_blend_state = &pcbsci,
|
|
.p_dynamic_state = &pdsci,
|
|
.layout = layout,
|
|
.render_pass = render_pass,
|
|
.subpass = 0,
|
|
.base_pipeline_handle = .null_handle,
|
|
.base_pipeline_index = -1,
|
|
};
|
|
|
|
var pipeline: vk.Pipeline = undefined;
|
|
_ = try gc.vkd.createGraphicsPipelines(
|
|
gc.dev,
|
|
.null_handle,
|
|
1, @ptrCast([*]const vk.GraphicsPipelineCreateInfo, &gpci),
|
|
null,
|
|
@ptrCast([*]vk.Pipeline, &pipeline),
|
|
);
|
|
return pipeline;
|
|
}
|
|
|
|
const ImageState = struct {
|
|
layout: vk.ImageLayout,
|
|
stage: vk.PipelineStageFlags,
|
|
access_mask: vk.AccessFlags = .{},
|
|
};
|
|
|
|
fn imageTransition(
|
|
gc: *const GraphicsContext,
|
|
cmdbuf: vk.CommandBuffer,
|
|
image: vk.Image,
|
|
subresource_range: vk.ImageSubresourceRange,
|
|
src: ImageState,
|
|
dst: ImageState
|
|
) void {
|
|
const barrier = vk.ImageMemoryBarrier{
|
|
.src_access_mask = src.access_mask,
|
|
.dst_access_mask = dst.access_mask,
|
|
.old_layout = src.layout,
|
|
.new_layout = dst.layout,
|
|
.src_queue_family_index = vk.QUEUE_FAMILY_IGNORED,
|
|
.dst_queue_family_index = vk.QUEUE_FAMILY_IGNORED,
|
|
.image = image,
|
|
.subresource_range = subresource_range,
|
|
};
|
|
|
|
gc.vkd.cmdPipelineBarrier(
|
|
cmdbuf, src.stage, dst.stage, .{},
|
|
0, undefined,
|
|
0, undefined,
|
|
1, @ptrCast([*]const vk.ImageMemoryBarrier, &barrier)
|
|
);
|
|
}
|