forked from mirror/vulkan-zig
Rename example to triangle
This commit is contained in:
457
examples/triangle.zig
Normal file
457
examples/triangle.zig
Normal file
@@ -0,0 +1,457 @@
|
||||
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 triangle example";
|
||||
|
||||
const Vertex = struct {
|
||||
const binding_description = vk.VertexInputBindingDescription{
|
||||
.binding = 0,
|
||||
.stride = @sizeOf(Vertex),
|
||||
.input_rate = .vertex,
|
||||
};
|
||||
|
||||
const attribute_description = [_]vk.VertexInputAttributeDescription{
|
||||
.{
|
||||
.binding = 0,
|
||||
.location = 0,
|
||||
.format = .r32g32_sfloat,
|
||||
.offset = @byteOffsetOf(Vertex, "pos"),
|
||||
},
|
||||
.{
|
||||
.binding = 0,
|
||||
.location = 1,
|
||||
.format = .r32g32b32_sfloat,
|
||||
.offset = @byteOffsetOf(Vertex, "color"),
|
||||
},
|
||||
};
|
||||
|
||||
pos: [2]f32,
|
||||
color: [3]f32,
|
||||
};
|
||||
|
||||
const vertices = [_]Vertex{
|
||||
.{.pos = .{0, -0.5}, .color = .{1, 0, 0}},
|
||||
.{.pos = .{0.5, 0.5}, .color = .{0, 1, 0}},
|
||||
.{.pos = .{-0.5, 0.5}, .color = .{0, 0, 1}},
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
var pipeline = try createPipeline(&gc, extent, pipeline_layout, render_pass);
|
||||
defer gc.vkd.destroyPipeline(gc.dev, pipeline, null);
|
||||
|
||||
var framebuffers = try createFramebuffers(&gc, allocator, render_pass, swapchain);
|
||||
defer destroyFramebuffers(&gc, allocator, framebuffers);
|
||||
|
||||
const buffer = try gc.vkd.createBuffer(gc.dev, .{
|
||||
.flags = .{},
|
||||
.size = @sizeOf(@TypeOf(vertices)),
|
||||
.usage = .{.vertex_buffer_bit = true},
|
||||
.sharing_mode = .exclusive,
|
||||
.queue_family_index_count = 0,
|
||||
.p_queue_family_indices = undefined,
|
||||
}, null);
|
||||
defer gc.vkd.destroyBuffer(gc.dev, buffer, null);
|
||||
const mem_reqs = gc.vkd.getBufferMemoryRequirements(gc.dev, buffer);
|
||||
const memory = try gc.allocate(mem_reqs, .{.host_visible_bit = true, .host_coherent_bit = true});
|
||||
defer gc.vkd.freeMemory(gc.dev, memory, null);
|
||||
try gc.vkd.bindBufferMemory(gc.dev, buffer, memory, 0);
|
||||
|
||||
{
|
||||
const data = try gc.vkd.mapMemory(gc.dev, memory, 0, vk.WHOLE_SIZE, .{});
|
||||
defer gc.vkd.unmapMemory(gc.dev, memory);
|
||||
|
||||
const gpu_vertices = @ptrCast([*]Vertex, @alignCast(@alignOf(Vertex), data));
|
||||
for (vertices) |vertex, i| {
|
||||
gpu_vertices[i] = vertex;
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
buffer,
|
||||
swapchain.extent,
|
||||
render_pass,
|
||||
pipeline,
|
||||
framebuffers
|
||||
);
|
||||
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);
|
||||
|
||||
destroyFramebuffers(&gc, allocator, framebuffers);
|
||||
framebuffers = try createFramebuffers(&gc, allocator, render_pass, swapchain);
|
||||
|
||||
destroyCommandBuffers(&gc, pool, allocator, cmdbufs);
|
||||
cmdbufs = try createCommandBuffers(
|
||||
&gc,
|
||||
pool,
|
||||
allocator,
|
||||
buffer,
|
||||
swapchain.extent,
|
||||
render_pass,
|
||||
pipeline,
|
||||
framebuffers
|
||||
);
|
||||
}
|
||||
|
||||
c.glfwSwapBuffers(window);
|
||||
c.glfwPollEvents();
|
||||
|
||||
try gc.vkd.queueWaitIdle(gc.graphics_queue.handle);
|
||||
}
|
||||
}
|
||||
|
||||
fn createCommandBuffers(
|
||||
gc: *const GraphicsContext,
|
||||
pool: vk.CommandPool,
|
||||
allocator: *Allocator,
|
||||
buffer: vk.Buffer,
|
||||
extent: vk.Extent2D,
|
||||
render_pass: vk.RenderPass,
|
||||
pipeline: vk.Pipeline,
|
||||
framebuffers: []vk.Framebuffer,
|
||||
) ![]vk.CommandBuffer {
|
||||
const cmdbufs = try allocator.alloc(vk.CommandBuffer, framebuffers.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 clear = vk.ClearValue{
|
||||
.color = .{.float_32 = .{0, 0, 0, 1}},
|
||||
};
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
for (cmdbufs) |cmdbuf, i| {
|
||||
try gc.vkd.beginCommandBuffer(cmdbuf, .{
|
||||
.flags = .{},
|
||||
.p_inheritance_info = null,
|
||||
});
|
||||
|
||||
gc.vkd.cmdSetViewport(cmdbuf, 0, 1, @ptrCast([*]const vk.Viewport, &viewport));
|
||||
gc.vkd.cmdSetScissor(cmdbuf, 0, 1, @ptrCast([*]const vk.Rect2D, &scissor));
|
||||
|
||||
gc.vkd.cmdBeginRenderPass(cmdbuf, .{
|
||||
.render_pass = render_pass,
|
||||
.framebuffer = framebuffers[i],
|
||||
.render_area = .{
|
||||
.offset = .{.x = 0, .y = 0},
|
||||
.extent = extent,
|
||||
},
|
||||
.clear_value_count = 1,
|
||||
.p_clear_values = @ptrCast([*]const vk.ClearValue, &clear),
|
||||
}, .@"inline");
|
||||
|
||||
gc.vkd.cmdBindPipeline(cmdbuf, .graphics, pipeline);
|
||||
const offset = [_]vk.DeviceSize{0};
|
||||
gc.vkd.cmdBindVertexBuffers(cmdbuf, 0, 1, @ptrCast([*]const vk.Buffer, &buffer), &offset);
|
||||
gc.vkd.cmdDraw(cmdbuf, vertices.len, 1, 0, 0);
|
||||
|
||||
gc.vkd.cmdEndRenderPass(cmdbuf);
|
||||
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 createFramebuffers(
|
||||
gc: *const GraphicsContext,
|
||||
allocator: *Allocator,
|
||||
render_pass: vk.RenderPass,
|
||||
swapchain: Swapchain
|
||||
) ![]vk.Framebuffer {
|
||||
const framebuffers = try allocator.alloc(vk.Framebuffer, swapchain.swap_images.len);
|
||||
errdefer allocator.free(framebuffers);
|
||||
|
||||
var i: usize = 0;
|
||||
errdefer for (framebuffers[0 .. i]) |fb| gc.vkd.destroyFramebuffer(gc.dev, fb, null);
|
||||
|
||||
for (framebuffers) |*fb| {
|
||||
fb.* = try gc.vkd.createFramebuffer(gc.dev, .{
|
||||
.flags = .{},
|
||||
.render_pass = render_pass,
|
||||
.attachment_count = 1,
|
||||
.p_attachments = @ptrCast([*]const vk.ImageView, &swapchain.swap_images[i].view),
|
||||
.width = swapchain.extent.width,
|
||||
.height = swapchain.extent.height,
|
||||
.layers = 1,
|
||||
}, null);
|
||||
i += 1;
|
||||
}
|
||||
|
||||
return framebuffers;
|
||||
}
|
||||
|
||||
fn destroyFramebuffers(gc: *const GraphicsContext, allocator: *Allocator, framebuffers: []const vk.Framebuffer) void {
|
||||
for (framebuffers) |fb| gc.vkd.destroyFramebuffer(gc.dev, fb, null);
|
||||
allocator.free(framebuffers);
|
||||
}
|
||||
|
||||
fn createRenderPass(gc: *const GraphicsContext, swapchain: 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 = 1,
|
||||
.p_vertex_binding_descriptions = @ptrCast([*]const vk.VertexInputBindingDescription, &Vertex.binding_description),
|
||||
.vertex_attribute_description_count = Vertex.attribute_description.len,
|
||||
.p_vertex_attribute_descriptions = &Vertex.attribute_description,
|
||||
};
|
||||
|
||||
const piasci = vk.PipelineInputAssemblyStateCreateInfo{
|
||||
.flags = .{},
|
||||
.topology = .triangle_list,
|
||||
.primitive_restart_enable = vk.FALSE,
|
||||
};
|
||||
|
||||
const pvsci = vk.PipelineViewportStateCreateInfo{
|
||||
.flags = .{},
|
||||
.viewport_count = 1,
|
||||
.p_viewports = undefined, // set in createCommandBuffers with cmdSetViewport
|
||||
.scissor_count = 1,
|
||||
.p_scissors = undefined, // set in createCommandBuffers with cmdSetScissor
|
||||
};
|
||||
|
||||
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, .scissor};
|
||||
const pdsci = vk.PipelineDynamicStateCreateInfo{
|
||||
.flags = .{},
|
||||
.dynamic_state_count = dynstate.len,
|
||||
.p_dynamic_states = &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;
|
||||
}
|
||||
Reference in New Issue
Block a user