564 lines
18 KiB
Zig
564 lines
18 KiB
Zig
const std = @import("std");
|
|
const glfw = @import("zglfw");
|
|
const vk = @import("vulkan");
|
|
|
|
const square_vert_spv = @embedFile("square_vertex_shader");
|
|
const square_frag_spv = @embedFile("square_fragment_shader");
|
|
|
|
pub fn main() !void {
|
|
std.debug.print("zig-chess bootstrap\n", .{});
|
|
std.debug.print("vertex shader bytes: {}\n", .{square_vert_spv.len});
|
|
std.debug.print("fragment shader bytes: {}\n", .{square_frag_spv.len});
|
|
|
|
try glfw.init();
|
|
defer glfw.terminate();
|
|
|
|
glfw.windowHint(.client_api, .no_api);
|
|
const window = try glfw.Window.create(
|
|
800,
|
|
600,
|
|
"zig-chess",
|
|
null,
|
|
null,
|
|
);
|
|
defer window.destroy();
|
|
|
|
std.debug.print("GLFW platform: {any}\n", .{glfw.getPlatform()});
|
|
std.debug.print("Vulkan supported by GLFW: {}\n", .{glfw.isVulkanSupported()});
|
|
|
|
const size = window.getSize();
|
|
const fb_size = window.getFramebufferSize();
|
|
|
|
std.debug.print("window size: {}x{}\n", .{ size[0], size[1] });
|
|
std.debug.print("framebuffer size: {}x{}\n", .{ fb_size[0], fb_size[1] });
|
|
std.debug.print("window visible attr: {}\n", .{window.getAttribute(.visible)});
|
|
|
|
window.show();
|
|
window.requestAttention();
|
|
|
|
const base = vk.BaseWrapper.load(glfw.getInstanceProcAddress);
|
|
const required_extensions = try glfw.getRequiredInstanceExtensions();
|
|
|
|
const app_info = vk.ApplicationInfo{
|
|
.p_application_name = "zig-chess",
|
|
.application_version = 1,
|
|
.p_engine_name = "zig-chess",
|
|
.engine_version = 1,
|
|
.api_version = @bitCast(vk.makeApiVersion(0, 1, 2, 0)),
|
|
};
|
|
|
|
const instance_create_info = vk.InstanceCreateInfo{
|
|
.p_application_info = &app_info,
|
|
.enabled_extension_count = @intCast(required_extensions.len),
|
|
.pp_enabled_extension_names = required_extensions.ptr,
|
|
};
|
|
|
|
const instance = try base.createInstance(&instance_create_info, null);
|
|
|
|
std.debug.print("required instance extensions:\n", .{});
|
|
for (required_extensions) |extension| {
|
|
std.debug.print(" {s}\n", .{extension});
|
|
}
|
|
|
|
std.debug.print("Created Vulkan Instance\n", .{});
|
|
|
|
const vki = vk.InstanceWrapper.load(instance, base.dispatch.vkGetInstanceProcAddr.?);
|
|
defer vki.destroyInstance(instance, null);
|
|
|
|
var surface: vk.SurfaceKHR = undefined;
|
|
try glfw.createWindowSurface(instance, window, null, &surface);
|
|
defer vki.destroySurfaceKHR(instance, surface, null);
|
|
std.debug.print("Created Vulkan surface\n", .{});
|
|
|
|
const physical_devices = try vki.enumeratePhysicalDevicesAlloc(instance, std.heap.page_allocator);
|
|
defer std.heap.page_allocator.free(physical_devices);
|
|
|
|
std.debug.print("physical devices: {}\n", .{physical_devices.len});
|
|
|
|
for (physical_devices, 0..) |physical_device, i| {
|
|
const props = vki.getPhysicalDeviceProperties(physical_device);
|
|
std.debug.print("device {}: {s}\n", .{ i, std.mem.sliceTo(&props.device_name, 0) });
|
|
const queue_families = try vki.getPhysicalDeviceQueueFamilyPropertiesAlloc(
|
|
physical_device,
|
|
std.heap.page_allocator,
|
|
);
|
|
defer std.heap.page_allocator.free(queue_families);
|
|
|
|
for (queue_families, 0..) |queue_family, queue_index| {
|
|
const supports_graphics = queue_family.queue_flags.graphics_bit;
|
|
const supports_compute = queue_family.queue_flags.compute_bit;
|
|
const supports_transfer = queue_family.queue_flags.transfer_bit;
|
|
|
|
const supports_present = try vki.getPhysicalDeviceSurfaceSupportKHR(
|
|
physical_device,
|
|
@intCast(queue_index),
|
|
surface,
|
|
);
|
|
|
|
std.debug.print(
|
|
" queue {}: count={}, graphics={}, compute={}, transfer={}, present={}\n",
|
|
.{
|
|
queue_index,
|
|
queue_family.queue_count,
|
|
supports_graphics,
|
|
supports_compute,
|
|
supports_transfer,
|
|
supports_present,
|
|
},
|
|
);
|
|
}
|
|
}
|
|
|
|
const selected_physical_device = physical_devices[1];
|
|
const graphics_queue_family_index: u32 = 0;
|
|
|
|
const selected_props = vki.getPhysicalDeviceProperties(selected_physical_device);
|
|
std.debug.print(
|
|
"selected device: {s}, queue family {}\n",
|
|
.{
|
|
std.mem.sliceTo(&selected_props.device_name, 0),
|
|
graphics_queue_family_index,
|
|
},
|
|
);
|
|
|
|
const queue_priority: f32 = 1.0;
|
|
|
|
const queue_create_info = vk.DeviceQueueCreateInfo{
|
|
.queue_family_index = graphics_queue_family_index,
|
|
.queue_count = 1,
|
|
.p_queue_priorities = @ptrCast(&queue_priority),
|
|
};
|
|
|
|
const device_extensions = [_][*:0]const u8{
|
|
"VK_KHR_swapchain",
|
|
};
|
|
|
|
const device_create_info = vk.DeviceCreateInfo{
|
|
.queue_create_info_count = 1,
|
|
.p_queue_create_infos = @ptrCast(&queue_create_info),
|
|
.enabled_extension_count = device_extensions.len,
|
|
.pp_enabled_extension_names = &device_extensions,
|
|
};
|
|
|
|
const device = try vki.createDevice(selected_physical_device, &device_create_info, null);
|
|
std.debug.print("created logical device\n", .{});
|
|
|
|
const vkd = vk.DeviceWrapper.load(device, vki.dispatch.vkGetDeviceProcAddr.?);
|
|
defer vkd.destroyDevice(device, null);
|
|
|
|
const graphics_queue = vkd.getDeviceQueue(device, graphics_queue_family_index, 0);
|
|
|
|
std.debug.print("retrieved graphics queue\n", .{});
|
|
|
|
const surface_caps = try vki.getPhysicalDeviceSurfaceCapabilitiesKHR(
|
|
selected_physical_device,
|
|
surface,
|
|
);
|
|
|
|
std.debug.print(
|
|
"surface current extent: {}x{}\n",
|
|
.{
|
|
surface_caps.current_extent.width,
|
|
surface_caps.current_extent.height,
|
|
},
|
|
);
|
|
|
|
std.debug.print(
|
|
"surface min/max image count: {}/{}\n",
|
|
.{
|
|
surface_caps.min_image_count,
|
|
surface_caps.max_image_count,
|
|
},
|
|
);
|
|
|
|
const surface_formats = try vki.getPhysicalDeviceSurfaceFormatsAllocKHR(
|
|
selected_physical_device,
|
|
surface,
|
|
std.heap.page_allocator,
|
|
);
|
|
defer std.heap.page_allocator.free(surface_formats);
|
|
|
|
std.debug.print("surface formats: {}\n", .{surface_formats.len});
|
|
for (surface_formats, 0..) |format, i| {
|
|
std.debug.print(
|
|
" format {}: format={any}, color_space={any}\n",
|
|
.{ i, format.format, format.color_space },
|
|
);
|
|
}
|
|
|
|
const present_modes = try vki.getPhysicalDeviceSurfacePresentModesAllocKHR(
|
|
selected_physical_device,
|
|
surface,
|
|
std.heap.page_allocator,
|
|
);
|
|
defer std.heap.page_allocator.free(present_modes);
|
|
|
|
std.debug.print("present modes: {}\n", .{present_modes.len});
|
|
for (present_modes, 0..) |mode, i| {
|
|
std.debug.print(" present mode {}: {any}\n", .{ i, mode });
|
|
}
|
|
|
|
var chosen_surface_format = surface_formats[0];
|
|
for (surface_formats) |format| {
|
|
if (format.format == .b8g8r8a8_srgb and
|
|
format.color_space == .srgb_nonlinear_khr)
|
|
{
|
|
chosen_surface_format = format;
|
|
break;
|
|
}
|
|
}
|
|
|
|
var chosen_present_mode: vk.PresentModeKHR = .fifo_khr;
|
|
for (present_modes) |mode| {
|
|
if (mode == .fifo_khr) {
|
|
chosen_present_mode = mode;
|
|
break;
|
|
}
|
|
}
|
|
|
|
const framebuffer_size = window.getFramebufferSize();
|
|
|
|
const chosen_extent = if (surface_caps.current_extent.width != std.math.maxInt(u32))
|
|
surface_caps.current_extent
|
|
else
|
|
vk.Extent2D{
|
|
.width = @intCast(framebuffer_size[0]),
|
|
.height = @intCast(framebuffer_size[1]),
|
|
};
|
|
|
|
var chosen_image_count = surface_caps.min_image_count + 1;
|
|
if (surface_caps.max_image_count != 0 and chosen_image_count > surface_caps.max_image_count) {
|
|
chosen_image_count = surface_caps.max_image_count;
|
|
}
|
|
|
|
std.debug.print(
|
|
"chosen swapchain format={any}, color_space={any}\n",
|
|
.{ chosen_surface_format.format, chosen_surface_format.color_space },
|
|
);
|
|
std.debug.print("chosen present mode={any}\n", .{chosen_present_mode});
|
|
std.debug.print(
|
|
"chosen extent={}x{}\n",
|
|
.{ chosen_extent.width, chosen_extent.height },
|
|
);
|
|
std.debug.print("chosen image count={}\n", .{chosen_image_count});
|
|
|
|
const swapchain_create_info = vk.SwapchainCreateInfoKHR{
|
|
.surface = surface,
|
|
.min_image_count = chosen_image_count,
|
|
.image_format = chosen_surface_format.format,
|
|
.image_color_space = chosen_surface_format.color_space,
|
|
.image_extent = chosen_extent,
|
|
.image_array_layers = 1,
|
|
.image_usage = .{
|
|
.color_attachment_bit = true,
|
|
},
|
|
.image_sharing_mode = .exclusive,
|
|
.pre_transform = surface_caps.current_transform,
|
|
.composite_alpha = .{
|
|
.opaque_bit_khr = true,
|
|
},
|
|
.present_mode = chosen_present_mode,
|
|
.clipped = .true,
|
|
};
|
|
|
|
const swapchain = try vkd.createSwapchainKHR(device, &swapchain_create_info, null);
|
|
defer vkd.destroySwapchainKHR(device, swapchain, null);
|
|
|
|
std.debug.print("created swapchain\n", .{});
|
|
|
|
const swapchain_images = try vkd.getSwapchainImagesAllocKHR(
|
|
device,
|
|
swapchain,
|
|
std.heap.page_allocator,
|
|
);
|
|
defer std.heap.page_allocator.free(swapchain_images);
|
|
|
|
std.debug.print("swapchain images: {}\n", .{swapchain_images.len});
|
|
|
|
const swapchain_image_views = try std.heap.page_allocator.alloc(
|
|
vk.ImageView,
|
|
swapchain_images.len,
|
|
);
|
|
defer std.heap.page_allocator.free(swapchain_image_views);
|
|
|
|
for (swapchain_images, 0..) |image, i| {
|
|
const image_view_create_info = vk.ImageViewCreateInfo{
|
|
.image = image,
|
|
.view_type = .@"2d",
|
|
.format = chosen_surface_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,
|
|
},
|
|
};
|
|
|
|
swapchain_image_views[i] = try vkd.createImageView(
|
|
device,
|
|
&image_view_create_info,
|
|
null,
|
|
);
|
|
}
|
|
|
|
defer {
|
|
for (swapchain_image_views) |image_view| {
|
|
vkd.destroyImageView(device, image_view, null);
|
|
}
|
|
}
|
|
|
|
std.debug.print("created swapchain image views: {}\n", .{swapchain_image_views.len});
|
|
|
|
const color_attachment = vk.AttachmentDescription{
|
|
.format = chosen_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{
|
|
.pipeline_bind_point = .graphics,
|
|
.color_attachment_count = 1,
|
|
.p_color_attachments = @ptrCast(&color_attachment_ref),
|
|
};
|
|
|
|
const subpass_dependency = vk.SubpassDependency{
|
|
.src_subpass = vk.SUBPASS_EXTERNAL,
|
|
.dst_subpass = 0,
|
|
.src_stage_mask = .{
|
|
.color_attachment_output_bit = true,
|
|
},
|
|
.src_access_mask = .{},
|
|
.dst_stage_mask = .{
|
|
.color_attachment_output_bit = true,
|
|
},
|
|
.dst_access_mask = .{
|
|
.color_attachment_write_bit = true,
|
|
},
|
|
};
|
|
|
|
const render_pass_create_info = vk.RenderPassCreateInfo{
|
|
.attachment_count = 1,
|
|
.p_attachments = @ptrCast(&color_attachment),
|
|
.subpass_count = 1,
|
|
.p_subpasses = @ptrCast(&subpass),
|
|
.dependency_count = 1,
|
|
.p_dependencies = @ptrCast(&subpass_dependency),
|
|
};
|
|
|
|
const render_pass = try vkd.createRenderPass(device, &render_pass_create_info, null);
|
|
defer vkd.destroyRenderPass(device, render_pass, null);
|
|
|
|
std.debug.print("created render pass\n", .{});
|
|
|
|
const framebuffers = try std.heap.page_allocator.alloc(
|
|
vk.Framebuffer,
|
|
swapchain_image_views.len,
|
|
);
|
|
defer std.heap.page_allocator.free(framebuffers);
|
|
|
|
for (swapchain_image_views, 0..) |image_view, i| {
|
|
const attachments = [_]vk.ImageView{image_view};
|
|
|
|
const framebuffer_create_info = vk.FramebufferCreateInfo{
|
|
.render_pass = render_pass,
|
|
.attachment_count = attachments.len,
|
|
.p_attachments = &attachments,
|
|
.width = chosen_extent.width,
|
|
.height = chosen_extent.height,
|
|
.layers = 1,
|
|
};
|
|
|
|
framebuffers[i] = try vkd.createFramebuffer(
|
|
device,
|
|
&framebuffer_create_info,
|
|
null,
|
|
);
|
|
}
|
|
|
|
defer {
|
|
for (framebuffers) |framebuffer| {
|
|
vkd.destroyFramebuffer(device, framebuffer, null);
|
|
}
|
|
}
|
|
|
|
std.debug.print("created framebuffers: {}\n", .{framebuffers.len});
|
|
|
|
const command_pool_create_info = vk.CommandPoolCreateInfo{
|
|
.flags = .{
|
|
.reset_command_buffer_bit = true,
|
|
},
|
|
.queue_family_index = graphics_queue_family_index,
|
|
};
|
|
|
|
const command_pool = try vkd.createCommandPool(
|
|
device,
|
|
&command_pool_create_info,
|
|
null,
|
|
);
|
|
defer vkd.destroyCommandPool(device, command_pool, null);
|
|
|
|
std.debug.print("created command pool\n", .{});
|
|
|
|
const command_buffers = try std.heap.page_allocator.alloc(
|
|
vk.CommandBuffer,
|
|
framebuffers.len,
|
|
);
|
|
defer std.heap.page_allocator.free(command_buffers);
|
|
|
|
const command_buffer_allocate_info = vk.CommandBufferAllocateInfo{
|
|
.command_pool = command_pool,
|
|
.level = .primary,
|
|
.command_buffer_count = @intCast(command_buffers.len),
|
|
};
|
|
|
|
try vkd.allocateCommandBuffers(
|
|
device,
|
|
&command_buffer_allocate_info,
|
|
command_buffers.ptr,
|
|
);
|
|
|
|
std.debug.print("allocated command buffers: {}\n", .{command_buffers.len});
|
|
|
|
for (command_buffers, 0..) |command_buffer, i| {
|
|
const begin_info = vk.CommandBufferBeginInfo{};
|
|
|
|
try vkd.beginCommandBuffer(command_buffer, &begin_info);
|
|
|
|
const clear_color = vk.ClearValue{
|
|
.color = .{
|
|
.float_32 = .{ 0.02, 0.02, 0.08, 1.0 },
|
|
},
|
|
};
|
|
|
|
const render_pass_begin_info = vk.RenderPassBeginInfo{
|
|
.render_pass = render_pass,
|
|
.framebuffer = framebuffers[i],
|
|
.render_area = .{
|
|
.offset = .{ .x = 0, .y = 0 },
|
|
.extent = chosen_extent,
|
|
},
|
|
.clear_value_count = 1,
|
|
.p_clear_values = @ptrCast(&clear_color),
|
|
};
|
|
|
|
vkd.cmdBeginRenderPass(
|
|
command_buffer,
|
|
&render_pass_begin_info,
|
|
.@"inline",
|
|
);
|
|
|
|
vkd.cmdEndRenderPass(command_buffer);
|
|
|
|
try vkd.endCommandBuffer(command_buffer);
|
|
}
|
|
|
|
std.debug.print("recorded command buffers\n", .{});
|
|
|
|
const semaphore_create_info = vk.SemaphoreCreateInfo{};
|
|
|
|
const image_available_semaphore = try vkd.createSemaphore(
|
|
device,
|
|
&semaphore_create_info,
|
|
null,
|
|
);
|
|
defer vkd.destroySemaphore(device, image_available_semaphore, null);
|
|
|
|
const render_finished_semaphore = try vkd.createSemaphore(
|
|
device,
|
|
&semaphore_create_info,
|
|
null,
|
|
);
|
|
defer vkd.destroySemaphore(device, render_finished_semaphore, null);
|
|
|
|
const fence_create_info = vk.FenceCreateInfo{
|
|
.flags = .{
|
|
.signaled_bit = true,
|
|
},
|
|
};
|
|
|
|
const in_flight_fence = try vkd.createFence(
|
|
device,
|
|
&fence_create_info,
|
|
null,
|
|
);
|
|
defer vkd.destroyFence(device, in_flight_fence, null);
|
|
|
|
std.debug.print("created synchronization objects\n", .{});
|
|
|
|
const wait_fences = [_]vk.Fence{in_flight_fence};
|
|
_ = try vkd.waitForFences(device, &wait_fences, .true, std.math.maxInt(u64));
|
|
try vkd.resetFences(device, &wait_fences);
|
|
|
|
const acquire_result = try vkd.acquireNextImageKHR(
|
|
device,
|
|
swapchain,
|
|
std.math.maxInt(u64),
|
|
image_available_semaphore,
|
|
.null_handle,
|
|
);
|
|
|
|
const image_index = acquire_result.image_index;
|
|
std.debug.print("acquired swapchain image: {}\n", .{image_index});
|
|
|
|
const wait_semaphores = [_]vk.Semaphore{image_available_semaphore};
|
|
const wait_stages = [_]vk.PipelineStageFlags{
|
|
.{
|
|
.color_attachment_output_bit = true,
|
|
},
|
|
};
|
|
const signal_semaphores = [_]vk.Semaphore{render_finished_semaphore};
|
|
const submit_command_buffers = [_]vk.CommandBuffer{
|
|
command_buffers[image_index],
|
|
};
|
|
|
|
const submit_info = vk.SubmitInfo{
|
|
.wait_semaphore_count = wait_semaphores.len,
|
|
.p_wait_semaphores = &wait_semaphores,
|
|
.p_wait_dst_stage_mask = &wait_stages,
|
|
.command_buffer_count = submit_command_buffers.len,
|
|
.p_command_buffers = &submit_command_buffers,
|
|
.signal_semaphore_count = signal_semaphores.len,
|
|
.p_signal_semaphores = &signal_semaphores,
|
|
};
|
|
|
|
try vkd.queueSubmit(graphics_queue, &[_]vk.SubmitInfo{submit_info}, in_flight_fence);
|
|
|
|
const present_swapchains = [_]vk.SwapchainKHR{swapchain};
|
|
const present_image_indices = [_]u32{image_index};
|
|
|
|
const present_info = vk.PresentInfoKHR{
|
|
.wait_semaphore_count = signal_semaphores.len,
|
|
.p_wait_semaphores = &signal_semaphores,
|
|
.swapchain_count = present_swapchains.len,
|
|
.p_swapchains = &present_swapchains,
|
|
.p_image_indices = &present_image_indices,
|
|
};
|
|
|
|
_ = try vkd.queuePresentKHR(graphics_queue, &present_info);
|
|
|
|
std.debug.print("presented one frame\n", .{});
|
|
|
|
while (!window.shouldClose()) {
|
|
glfw.pollEvents();
|
|
}
|
|
}
|