540 lines
20 KiB
Zig
540 lines
20 KiB
Zig
const std = @import("std");
|
|
const vk = @import("vk");
|
|
const c = @import("c.zig");
|
|
const shaders = @import("shaders");
|
|
const Allocator = std.mem.Allocator;
|
|
|
|
const gfx = @import("gfx.zig");
|
|
|
|
const app_name = "vulkan-zig triangle example";
|
|
|
|
const Vertex = extern struct {
|
|
const binding_description = vk.VertexInputBindingDescription{
|
|
.binding = 0,
|
|
.stride = @sizeOf(Vertex),
|
|
.input_rate = .vertex,
|
|
};
|
|
|
|
const attribute_description = [_]vk.VertexInputAttributeDescription{
|
|
.{
|
|
.binding = 0,
|
|
.location = 0,
|
|
.format = .r32g32b32a32_sfloat,
|
|
.offset = @offsetOf(Vertex, "pos"),
|
|
},
|
|
.{
|
|
.binding = 0,
|
|
.location = 1,
|
|
.format = .r32g32b32_sfloat,
|
|
.offset = @offsetOf(Vertex, "color"),
|
|
},
|
|
};
|
|
|
|
pos: [4]f32,
|
|
color: [3]f32,
|
|
};
|
|
|
|
const Index = u16;
|
|
|
|
const vertices = [_]Vertex{
|
|
// Vulkan depth range is 0, 1 instead of OpenGL -1, 1
|
|
.{ .pos = .{ -0.5, -0.5, -0.5, 1.0 }, .color = .{ 1, 0, 0 } },
|
|
.{ .pos = .{ -0.5, 0.5, -0.5, 1.0 }, .color = .{ 0, 1, 0 } },
|
|
.{ .pos = .{ 0.5, -0.5, -0.5, 1.0 }, .color = .{ 0, 0, 1 } },
|
|
.{ .pos = .{ 0.5, 0.5, -0.5, 1.0 }, .color = .{ 1, 1, 0 } },
|
|
.{ .pos = .{ -0.5, -0.5, 0.5, 1.0 }, .color = .{ 1, 0, 0 } },
|
|
.{ .pos = .{ -0.5, 0.5, 0.5, 1.0 }, .color = .{ 0, 1, 0 } },
|
|
.{ .pos = .{ 0.5, -0.5, 0.5, 1.0 }, .color = .{ 0, 0, 1 } },
|
|
.{ .pos = .{ 0.5, 0.5, 0.5, 1.0 }, .color = .{ 1, 1, 0 } },
|
|
};
|
|
|
|
const indices = [_]Index{ 4, 5, 6, 6, 5, 7 };
|
|
|
|
pub fn main() !void {
|
|
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
|
defer _ = gpa.deinit();
|
|
const ally = gpa.allocator();
|
|
|
|
if (c.glfwInit() != c.GLFW_TRUE) return error.GlfwInitFailed;
|
|
defer c.glfwTerminate();
|
|
|
|
if (c.glfwVulkanSupported() != c.GLFW_TRUE) {
|
|
std.log.err("GLFW could not find libvulkan", .{});
|
|
return error.NoVulkan;
|
|
}
|
|
|
|
var extent = vk.Extent2D{ .width = 800, .height = 600 };
|
|
|
|
const window = try gfx.create_window(extent, app_name);
|
|
defer c.glfwDestroyWindow(window);
|
|
|
|
const vkb = try gfx.BaseDispatch.load(c.glfwGetInstanceProcAddress);
|
|
|
|
const instance, const vki, const messenger = try gfx.create_instance(vkb, app_name);
|
|
defer vki.destroyInstance(instance, null);
|
|
defer if (gfx.use_debug_messenger)
|
|
vki.destroyDebugUtilsMessengerEXT(instance, messenger, null);
|
|
|
|
const surface = try gfx.create_surface(instance, window);
|
|
defer vki.destroySurfaceKHR(instance, surface, null);
|
|
|
|
const pdev: vk.PhysicalDevice, const dev: vk.Device, const vkd: gfx.DeviceDispatch, const family: u32 =
|
|
try gfx.create_device(ally, instance, surface, vki);
|
|
defer vkd.destroyDevice(dev, null);
|
|
|
|
const queue = vkd.getDeviceQueue(dev, family, 0);
|
|
|
|
const pool = try vkd.createCommandPool(dev, &.{
|
|
.queue_family_index = family,
|
|
}, null);
|
|
defer vkd.destroyCommandPool(dev, pool, null);
|
|
|
|
const preferred_format: vk.SurfaceFormatKHR = .{
|
|
.format = .b8g8r8a8_srgb,
|
|
.color_space = .srgb_nonlinear_khr,
|
|
};
|
|
const format = try gfx.find_surface_format(pdev, vki, surface, preferred_format);
|
|
extent = try gfx.find_swap_extent(pdev, vki, surface, window);
|
|
|
|
const present_mode = try gfx.find_present_mode(pdev, vki, surface, .mailbox_khr);
|
|
|
|
const swap_image_count = try gfx.find_swap_image_count(pdev, vki, surface);
|
|
|
|
var swapchain: vk.SwapchainKHR = .null_handle;
|
|
defer vkd.destroySwapchainKHR(dev, swapchain, null);
|
|
|
|
const ChainImage = struct {
|
|
image: vk.Image = .null_handle,
|
|
view: vk.ImageView = .null_handle,
|
|
cmdbuf: vk.CommandBuffer = .null_handle,
|
|
fence: vk.Fence = .null_handle,
|
|
image_available: vk.Semaphore = .null_handle,
|
|
render_finished: vk.Semaphore = .null_handle,
|
|
};
|
|
|
|
var chain = std.MultiArrayList(ChainImage){};
|
|
defer chain.deinit(ally);
|
|
defer vkd.freeCommandBuffers(dev, pool, @intCast(chain.len), chain.items(.cmdbuf).ptr);
|
|
defer for (chain.items(.view)) |view| vkd.destroyImageView(dev, view, null);
|
|
defer for (chain.items(.fence)) |fence| vkd.destroyFence(dev, fence, null);
|
|
defer for (chain.items(.image_available)) |sem| vkd.destroySemaphore(dev, sem, null);
|
|
defer for (chain.items(.render_finished)) |sem| vkd.destroySemaphore(dev, sem, null);
|
|
|
|
swapchain = try vkd.createSwapchainKHR(dev, &.{
|
|
.surface = surface,
|
|
.min_image_count = swap_image_count,
|
|
.image_format = format.format,
|
|
.image_color_space = format.color_space,
|
|
.image_extent = extent,
|
|
.image_array_layers = 1,
|
|
.image_usage = .{ .color_attachment_bit = true },
|
|
.image_sharing_mode = .exclusive,
|
|
.pre_transform = .{ .identity_bit_khr = true },
|
|
.composite_alpha = .{ .opaque_bit_khr = true },
|
|
.present_mode = present_mode,
|
|
.clipped = vk.TRUE,
|
|
.old_swapchain = swapchain,
|
|
}, null);
|
|
|
|
var image_count: u32 = undefined;
|
|
_ = try vkd.getSwapchainImagesKHR(dev, swapchain, &image_count, null);
|
|
try chain.resize(ally, image_count);
|
|
_ = try vkd.getSwapchainImagesKHR(dev, swapchain, &image_count, chain.items(.image).ptr);
|
|
|
|
for (chain.items(.image), chain.items(.view)) |image, *view| {
|
|
view.* = try vkd.createImageView(dev, &.{
|
|
.image = image,
|
|
.view_type = .@"2d",
|
|
.format = format.format,
|
|
.components = .{ .r = .identity, .g = .identity, .b = .identity, .a = .identity },
|
|
.subresource_range = .{
|
|
.aspect_mask = .{ .color_bit = true },
|
|
.base_mip_level = 0,
|
|
.level_count = 1,
|
|
.base_array_layer = 0,
|
|
.layer_count = 1,
|
|
},
|
|
}, null);
|
|
}
|
|
|
|
for (chain.items(.fence)) |*fence| {
|
|
fence.* = try vkd.createFence(dev, &.{ .flags = .{ .signaled_bit = true } }, null);
|
|
}
|
|
|
|
for (chain.items(.image_available)) |*sem| {
|
|
sem.* = try vkd.createSemaphore(dev, &.{}, null);
|
|
}
|
|
|
|
for (chain.items(.render_finished)) |*sem| {
|
|
sem.* = try vkd.createSemaphore(dev, &.{}, null);
|
|
}
|
|
|
|
try vkd.allocateCommandBuffers(dev, &.{
|
|
.command_buffer_count = @intCast(chain.len),
|
|
.command_pool = pool,
|
|
.level = .primary,
|
|
}, chain.items(.cmdbuf).ptr);
|
|
|
|
const pipeline_layout = try vkd.createPipelineLayout(dev, &.{
|
|
.flags = .{},
|
|
.set_layout_count = 0,
|
|
.p_set_layouts = undefined,
|
|
.push_constant_range_count = 0,
|
|
.p_push_constant_ranges = undefined,
|
|
}, null);
|
|
defer vkd.destroyPipelineLayout(dev, pipeline_layout, null);
|
|
|
|
const pipeline = try createPipeline(dev, pipeline_layout, format, vkd);
|
|
defer vkd.destroyPipeline(dev, pipeline, null);
|
|
|
|
const vertex_buffer = try vkd.createBuffer(dev, &.{
|
|
.size = @sizeOf(@TypeOf(vertices)),
|
|
.usage = .{ .transfer_dst_bit = true, .vertex_buffer_bit = true },
|
|
.sharing_mode = .exclusive,
|
|
}, null);
|
|
defer vkd.destroyBuffer(dev, vertex_buffer, null);
|
|
const vertex_mem_reqs = vkd.getBufferMemoryRequirements(dev, vertex_buffer);
|
|
const vertex_memory = try gfx.allocate(pdev, vki, dev, vkd, vertex_mem_reqs, .{ .device_local_bit = true });
|
|
defer vkd.freeMemory(dev, vertex_memory, null);
|
|
try vkd.bindBufferMemory(dev, vertex_buffer, vertex_memory, 0);
|
|
|
|
try gfx.uploadData(Vertex, pdev, vki, dev, vkd, queue, pool, vertex_buffer, &vertices);
|
|
|
|
const index_buffer = try vkd.createBuffer(dev, &.{
|
|
.size = @sizeOf(@TypeOf(indices)),
|
|
.usage = .{ .transfer_dst_bit = true, .index_buffer_bit = true },
|
|
.sharing_mode = .exclusive,
|
|
}, null);
|
|
defer vkd.destroyBuffer(dev, index_buffer, null);
|
|
const index_mem_reqs = vkd.getBufferMemoryRequirements(dev, index_buffer);
|
|
const index_memory = try gfx.allocate(pdev, vki, dev, vkd, index_mem_reqs, .{ .device_local_bit = true });
|
|
defer vkd.freeMemory(dev, index_memory, null);
|
|
try vkd.bindBufferMemory(dev, index_buffer, index_memory, 0);
|
|
|
|
try gfx.uploadData(Index, pdev, vki, dev, vkd, queue, pool, index_buffer, &indices);
|
|
|
|
for (chain.items(.image), chain.items(.view), chain.items(.cmdbuf)) |image, view, cmdbuf| {
|
|
try record_cmdbuf(cmdbuf, vkd, image, view, extent, pipeline, vertex_buffer, index_buffer);
|
|
}
|
|
|
|
var index: u32 = 0;
|
|
|
|
while (c.glfwWindowShouldClose(window) == c.GLFW_FALSE) {
|
|
var w: c_int = undefined;
|
|
var h: c_int = undefined;
|
|
c.glfwGetFramebufferSize(window, &w, &h);
|
|
|
|
// Don't present or resize swapchain while the window is minimized
|
|
if (w == 0 or h == 0) {
|
|
c.glfwPollEvents();
|
|
continue;
|
|
}
|
|
|
|
const frame: ChainImage = chain.get(index);
|
|
// const next_frame: ChainImage = chain.get((index + 1) % chain.len);
|
|
|
|
_ = try vkd.waitForFences(dev, 1, @ptrCast(&frame.fence), vk.TRUE, std.math.maxInt(u64));
|
|
try vkd.resetFences(dev, 1, @ptrCast(&frame.fence));
|
|
|
|
// var index: u32 = undefined;
|
|
// try vkd.acquireNextImageKHR(dev, swapchain, std.math.maxInt(u64), frame., fence);
|
|
const result = try vkd.acquireNextImageKHR(dev, swapchain, std.math.maxInt(u64), frame.image_available, .null_handle);
|
|
|
|
// std.log.debug("frame {d}", .{result.image_index});
|
|
// const frame = chain.get(result.image_index);
|
|
|
|
try vkd.queueSubmit(queue, 1, @ptrCast(&vk.SubmitInfo{
|
|
.wait_semaphore_count = 1,
|
|
.p_wait_semaphores = @ptrCast(&frame.image_available),
|
|
.p_wait_dst_stage_mask = @ptrCast(&vk.PipelineStageFlags{ .color_attachment_output_bit = true }),
|
|
.command_buffer_count = 1,
|
|
.p_command_buffers = @ptrCast(&frame.cmdbuf),
|
|
.signal_semaphore_count = 1,
|
|
.p_signal_semaphores = @ptrCast(&frame.render_finished),
|
|
}), frame.fence);
|
|
|
|
_ = try vkd.queuePresentKHR(queue, &.{
|
|
.wait_semaphore_count = 1,
|
|
.p_wait_semaphores = @ptrCast(&frame.render_finished),
|
|
.swapchain_count = 1,
|
|
.p_swapchains = @ptrCast(&swapchain),
|
|
.p_image_indices = @ptrCast(&result.image_index),
|
|
.p_results = null,
|
|
});
|
|
|
|
// 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 or extent.width != @as(u32, @intCast(w)) or extent.height != @as(u32, @intCast(h))) {
|
|
// extent.width = @intCast(w);
|
|
// extent.height = @intCast(h);
|
|
// try swapchain.recreate(extent, format);
|
|
//
|
|
// destroyCommandBuffers(&gc, pool, ally, cmdbufs);
|
|
//
|
|
// cmdbufs = try createCommandBuffers(
|
|
// &gc,
|
|
// pool,
|
|
// ally,
|
|
// vertex_buffer,
|
|
// index_buffer,
|
|
// pipeline,
|
|
// swapchain,
|
|
// );
|
|
// }
|
|
|
|
c.glfwPollEvents();
|
|
|
|
index = @intCast((index + 1) % chain.len);
|
|
}
|
|
|
|
// try swapchain.waitForAllFences();
|
|
try vkd.deviceWaitIdle(dev);
|
|
}
|
|
|
|
fn record_cmdbuf(
|
|
cmdbuf: vk.CommandBuffer,
|
|
vkd: gfx.DeviceDispatch,
|
|
image: vk.Image,
|
|
view: vk.ImageView,
|
|
extent: vk.Extent2D,
|
|
pipeline: vk.Pipeline,
|
|
vertex_buffer: vk.Buffer,
|
|
index_buffer: vk.Buffer,
|
|
) !void {
|
|
const clear = vk.ClearValue{
|
|
.color = .{ .float_32 = .{ 0, 0, 0, 1 } },
|
|
};
|
|
|
|
const viewport = vk.Viewport{
|
|
.x = 0,
|
|
.y = 0,
|
|
.width = @floatFromInt(extent.width),
|
|
.height = @floatFromInt(extent.height),
|
|
.min_depth = 0,
|
|
.max_depth = 1,
|
|
};
|
|
|
|
const scissor = vk.Rect2D{
|
|
.offset = .{ .x = 0, .y = 0 },
|
|
.extent = extent,
|
|
};
|
|
|
|
try vkd.beginCommandBuffer(cmdbuf, &.{});
|
|
|
|
vkd.cmdPipelineBarrier(
|
|
cmdbuf,
|
|
.{ .top_of_pipe_bit = true },
|
|
.{ .color_attachment_output_bit = true },
|
|
.{},
|
|
0,
|
|
null,
|
|
0,
|
|
null,
|
|
1,
|
|
@ptrCast(&vk.ImageMemoryBarrier{
|
|
.src_access_mask = .{},
|
|
.dst_access_mask = .{ .color_attachment_write_bit = true },
|
|
.old_layout = .undefined,
|
|
.new_layout = .color_attachment_optimal,
|
|
.src_queue_family_index = 0,
|
|
.dst_queue_family_index = 0,
|
|
.image = image,
|
|
.subresource_range = .{
|
|
.aspect_mask = .{ .color_bit = true },
|
|
.base_mip_level = 0,
|
|
.level_count = 1,
|
|
.base_array_layer = 0,
|
|
.layer_count = 1,
|
|
},
|
|
}),
|
|
);
|
|
|
|
vkd.cmdSetViewport(cmdbuf, 0, 1, @ptrCast(&viewport));
|
|
vkd.cmdSetScissor(cmdbuf, 0, 1, @ptrCast(&scissor));
|
|
|
|
const color_attachments = [_]vk.RenderingAttachmentInfoKHR{
|
|
.{
|
|
.image_view = view,
|
|
.image_layout = .color_attachment_optimal,
|
|
.resolve_mode = .{},
|
|
.resolve_image_view = .null_handle,
|
|
.resolve_image_layout = .undefined,
|
|
.load_op = .clear,
|
|
.store_op = .store,
|
|
.clear_value = clear,
|
|
},
|
|
};
|
|
|
|
const render_info = vk.RenderingInfoKHR{
|
|
.render_area = scissor, // since we always do full-frame changes
|
|
.layer_count = 1,
|
|
.view_mask = 0,
|
|
.color_attachment_count = color_attachments.len,
|
|
.p_color_attachments = &color_attachments,
|
|
};
|
|
|
|
vkd.cmdBeginRenderingKHR(cmdbuf, &render_info);
|
|
|
|
vkd.cmdBindPipeline(cmdbuf, .graphics, pipeline);
|
|
const offset = [_]vk.DeviceSize{0};
|
|
vkd.cmdBindVertexBuffers(cmdbuf, 0, 1, @ptrCast(&vertex_buffer), &offset);
|
|
vkd.cmdBindIndexBuffer(cmdbuf, index_buffer, 0, .uint16);
|
|
vkd.cmdDrawIndexed(cmdbuf, indices.len, 1, 0, 0, 0);
|
|
|
|
vkd.cmdEndRenderingKHR(cmdbuf);
|
|
|
|
vkd.cmdPipelineBarrier(
|
|
cmdbuf,
|
|
.{ .color_attachment_output_bit = true },
|
|
.{ .bottom_of_pipe_bit = true },
|
|
.{},
|
|
0,
|
|
null,
|
|
0,
|
|
null,
|
|
1,
|
|
@ptrCast(&vk.ImageMemoryBarrier{
|
|
.src_access_mask = .{ .color_attachment_write_bit = true },
|
|
.dst_access_mask = .{},
|
|
.old_layout = .color_attachment_optimal,
|
|
.new_layout = .present_src_khr,
|
|
.src_queue_family_index = 0,
|
|
.dst_queue_family_index = 0,
|
|
.image = image,
|
|
.subresource_range = .{
|
|
.aspect_mask = .{ .color_bit = true },
|
|
.base_mip_level = 0,
|
|
.level_count = 1,
|
|
.base_array_layer = 0,
|
|
.layer_count = 1,
|
|
},
|
|
}),
|
|
);
|
|
|
|
try vkd.endCommandBuffer(cmdbuf);
|
|
}
|
|
|
|
fn createPipeline(dev: vk.Device, layout: vk.PipelineLayout, format: vk.SurfaceFormatKHR, vkd: gfx.DeviceDispatch) !vk.Pipeline {
|
|
const vert = try vkd.createShaderModule(dev, &.{
|
|
.code_size = shaders.triangle_vert.len,
|
|
.p_code = @as([*]const u32, @ptrCast(&shaders.triangle_vert)),
|
|
}, null);
|
|
defer vkd.destroyShaderModule(dev, vert, null);
|
|
|
|
const frag = try vkd.createShaderModule(dev, &.{
|
|
.code_size = shaders.triangle_frag.len,
|
|
.p_code = @as([*]const u32, @ptrCast(&shaders.triangle_frag)),
|
|
}, null);
|
|
defer vkd.destroyShaderModule(dev, frag, null);
|
|
|
|
const pssci = [_]vk.PipelineShaderStageCreateInfo{
|
|
.{
|
|
.stage = .{ .vertex_bit = true },
|
|
.module = vert,
|
|
.p_name = "main",
|
|
},
|
|
.{
|
|
.stage = .{ .fragment_bit = true },
|
|
.module = frag,
|
|
.p_name = "main",
|
|
},
|
|
};
|
|
|
|
const color_blend_attachment_states = [_]vk.PipelineColorBlendAttachmentState{
|
|
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 dynamic_states = [_]vk.DynamicState{
|
|
.viewport,
|
|
.scissor,
|
|
};
|
|
|
|
const create_infos = [_]vk.GraphicsPipelineCreateInfo{
|
|
.{
|
|
.flags = .{},
|
|
.stage_count = @intCast(pssci.len),
|
|
.p_stages = &pssci,
|
|
.p_vertex_input_state = &vk.PipelineVertexInputStateCreateInfo{
|
|
.vertex_binding_description_count = 1,
|
|
.p_vertex_binding_descriptions = @ptrCast(&Vertex.binding_description),
|
|
.vertex_attribute_description_count = Vertex.attribute_description.len,
|
|
.p_vertex_attribute_descriptions = &Vertex.attribute_description,
|
|
},
|
|
.p_input_assembly_state = &vk.PipelineInputAssemblyStateCreateInfo{
|
|
.topology = .triangle_list,
|
|
.primitive_restart_enable = vk.FALSE,
|
|
},
|
|
.p_tessellation_state = null,
|
|
.p_viewport_state = &vk.PipelineViewportStateCreateInfo{
|
|
.viewport_count = 1,
|
|
.p_viewports = undefined, // set in createCommandBuffers with cmdSetViewport
|
|
.scissor_count = 1,
|
|
.p_scissors = undefined, // set in createCommandBuffers with cmdSetScissor
|
|
},
|
|
.p_rasterization_state = &vk.PipelineRasterizationStateCreateInfo{
|
|
.depth_clamp_enable = vk.FALSE,
|
|
.rasterizer_discard_enable = vk.FALSE,
|
|
.polygon_mode = .fill,
|
|
.cull_mode = .{ .back_bit = true },
|
|
.front_face = .counter_clockwise,
|
|
.depth_bias_enable = vk.FALSE,
|
|
.depth_bias_constant_factor = 0,
|
|
.depth_bias_clamp = 0,
|
|
.depth_bias_slope_factor = 0,
|
|
.line_width = 1,
|
|
},
|
|
.p_multisample_state = &vk.PipelineMultisampleStateCreateInfo{
|
|
.rasterization_samples = .{ .@"1_bit" = true },
|
|
.sample_shading_enable = vk.FALSE,
|
|
.min_sample_shading = 1,
|
|
.alpha_to_coverage_enable = vk.FALSE,
|
|
.alpha_to_one_enable = vk.FALSE,
|
|
},
|
|
.p_depth_stencil_state = null,
|
|
.p_color_blend_state = &vk.PipelineColorBlendStateCreateInfo{
|
|
.logic_op_enable = vk.FALSE,
|
|
.logic_op = .copy,
|
|
.attachment_count = @intCast(color_blend_attachment_states.len),
|
|
.p_attachments = &color_blend_attachment_states,
|
|
.blend_constants = [_]f32{ 0, 0, 0, 0 },
|
|
},
|
|
.p_dynamic_state = &vk.PipelineDynamicStateCreateInfo{
|
|
.flags = .{},
|
|
.dynamic_state_count = @intCast(dynamic_states.len),
|
|
.p_dynamic_states = &dynamic_states,
|
|
},
|
|
.layout = layout,
|
|
.render_pass = .null_handle,
|
|
.subpass = 0,
|
|
.base_pipeline_handle = .null_handle,
|
|
.base_pipeline_index = -1,
|
|
.p_next = &vk.PipelineRenderingCreateInfoKHR{
|
|
.color_attachment_count = 1,
|
|
.p_color_attachment_formats = @ptrCast(&format),
|
|
.depth_attachment_format = .undefined,
|
|
.stencil_attachment_format = .undefined,
|
|
.view_mask = 0,
|
|
},
|
|
},
|
|
};
|
|
|
|
var pipelines: [create_infos.len]vk.Pipeline = undefined;
|
|
_ = try vkd.createGraphicsPipelines(dev, .null_handle, @intCast(create_infos.len), &create_infos, null, &pipelines);
|
|
std.debug.assert(pipelines.len == 1);
|
|
return pipelines[0];
|
|
}
|