Graphics context for example

This commit is contained in:
Robin Voetter
2020-07-02 04:01:59 +02:00
parent 260a7966ee
commit 1ebed09413
5 changed files with 385 additions and 11 deletions

View File

@@ -27,9 +27,11 @@ pub fn build(b: *Builder) void {
exe.setTarget(target); exe.setTarget(target);
exe.setBuildMode(mode); exe.setBuildMode(mode);
exe.install(); exe.install();
exe.linkSystemLibrary("c");
exe.linkSystemLibrary("glfw");
const vk_path = generateVk(b); const vk_path = generateVk(b);
const fmt_step = b.addFmt(&[_][]const u8{vk_path}); const fmt_step = b.addFmt(&[_][]const u8{vk_path});
exe.step.dependOn(&fmt_step.step); exe.step.dependOn(&fmt_step.step);
exe.addPackagePath("vk", vk_path); exe.addPackagePath("vulkan", vk_path);
} }

13
examples/c.zig Normal file
View File

@@ -0,0 +1,13 @@
pub usingnamespace @cImport({
@cDefine("GLFW_INCLUDE_NONE", {});
@cInclude("GLFW/glfw3.h");
});
const vk = @import("vulkan");
// usually the GLFW vulkan functions are exported if Vulkan is included,
// but since thats not the case here, they are manually imported.
pub extern "c" fn glfwGetInstanceProcAddress(instance: vk.Instance, procname: [*:0]const u8) vk.PfnVoidFunction;
pub extern "c" fn glfwGetPhysicalDevicePresentationSupport(instance: vk.Instance, pdev: vk.PhysicalDevice, queuefamily: u32) c_int;
pub extern "c" fn glfwCreateWindowSurface(instance: vk.Instance, window: *GLFWwindow, allocation_callbacks: ?*const vk.AllocationCallbacks, surface: *vk.SurfaceKHR) vk.Result;

View File

@@ -0,0 +1,281 @@
const std = @import("std");
const vk = @import("vulkan");
const c = @import("c.zig");
const Allocator = std.mem.Allocator;
const required_device_extensions = [_][]const u8{
vk.extension_info.khr_swapchain.name
};
const BaseDispatch = struct {
vkCreateInstance: vk.PfnCreateInstance,
usingnamespace vk.BaseWrapper(@This());
};
const InstanceDispatch = struct {
vkDestroyInstance: vk.PfnDestroyInstance,
vkCreateDevice: vk.PfnCreateDevice,
vkDestroySurfaceKHR: vk.PfnDestroySurfaceKHR,
vkEnumeratePhysicalDevices: vk.PfnEnumeratePhysicalDevices,
vkGetPhysicalDeviceProperties: vk.PfnGetPhysicalDeviceProperties,
vkEnumerateDeviceExtensionProperties: vk.PfnEnumerateDeviceExtensionProperties,
vkGetPhysicalDeviceSurfaceFormatsKHR: vk.PfnGetPhysicalDeviceSurfaceFormatsKHR,
vkGetPhysicalDeviceSurfacePresentModesKHR: vk.PfnGetPhysicalDeviceSurfacePresentModesKHR,
vkGetPhysicalDeviceQueueFamilyProperties: vk.PfnGetPhysicalDeviceQueueFamilyProperties,
vkGetPhysicalDeviceSurfaceSupportKHR: vk.PfnGetPhysicalDeviceSurfaceSupportKHR,
vkGetDeviceProcAddr: vk.PfnGetDeviceProcAddr,
usingnamespace vk.InstanceWrapper(@This());
};
const DeviceDispatch = struct {
vkDestroyDevice: vk.PfnDestroyDevice,
vkGetDeviceQueue: vk.PfnGetDeviceQueue,
usingnamespace vk.DeviceWrapper(@This());
};
pub const GraphicsContext = struct {
vkb: BaseDispatch,
vki: InstanceDispatch,
vkd: DeviceDispatch,
instance: vk.Instance,
surface: vk.SurfaceKHR,
pdev: vk.PhysicalDevice,
props: vk.PhysicalDeviceProperties,
dev: vk.Device,
graphics_queue: Queue,
present_queue: Queue,
pub fn init(allocator: *Allocator, app_info: *const vk.ApplicationInfo, window: *c.GLFWwindow) !GraphicsContext {
var self: GraphicsContext = undefined;
self.vkb = try BaseDispatch.load(c.glfwGetInstanceProcAddress);
var glfw_exts_count: u32 = 0;
const glfw_exts = c.glfwGetRequiredInstanceExtensions(&glfw_exts_count);
self.instance = try self.vkb.createInstance(.{
.flags = .{},
.p_application_info = app_info,
.enabled_layer_count = 0,
.pp_enabled_layer_names = undefined,
.enabled_extension_count = glfw_exts_count,
.pp_enabled_extension_names = @ptrCast([*]const [*:0]const u8, glfw_exts),
}, null);
self.vki = try InstanceDispatch.load(self.instance, c.glfwGetInstanceProcAddress);
errdefer self.vki.destroyInstance(self.instance, null);
self.surface = try createSurface(self.vki, self.instance, window);
errdefer self.vki.destroySurfaceKHR(self.instance, self.surface, null);
const candidate = try pickPhysicalDevice(self.vki, self.instance, allocator, self.surface);
self.pdev = candidate.pdev;
self.props = candidate.props;
self.dev = try initializeCandidate(self.vki, candidate);
self.vkd = try DeviceDispatch.load(self.dev, self.vki.vkGetDeviceProcAddr);
errdefer self.vkd.destroyDevice(self.dev, null);
self.graphics_queue = Queue.init(self.vkd, self.dev, candidate.queues.graphics_family);
self.present_queue = Queue.init(self.vkd, self.dev, candidate.queues.graphics_family);
return self;
}
pub fn deinit(self: GraphicsContext) void {
self.vkd.destroyDevice(self.dev, null);
self.vki.destroySurfaceKHR(self.instance, self.surface, null);
self.vki.destroyInstance(self.instance, null);
}
};
pub const Queue = struct {
handle: vk.Queue,
family: u32,
fn init(vkd: DeviceDispatch, dev: vk.Device, family: u32) Queue {
return .{
.handle = vkd.getDeviceQueue(dev, family, 0),
.family = family,
};
}
};
fn createSurface(vki: InstanceDispatch, instance: vk.Instance, window: *c.GLFWwindow) !vk.SurfaceKHR {
var surface: vk.SurfaceKHR = undefined;
if (c.glfwCreateWindowSurface(instance, window, null, &surface) != .success) {
return error.SurfaceInitFailed;
}
return surface;
}
fn initializeCandidate(vki: InstanceDispatch, candidate: DeviceCandidate) !vk.Device {
const priority = [_]f32{1};
const qci = [_]vk.DeviceQueueCreateInfo{
.{
.flags = .{},
.queue_family_index = candidate.queues.graphics_family,
.queue_count = 1,
.p_queue_priorities = &priority,
},
.{
.flags = .{},
.queue_family_index = candidate.queues.present_family,
.queue_count = 1,
.p_queue_priorities = &priority,
}
};
const queue_count: u32 = if (candidate.queues.graphics_family == candidate.queues.present_family)
1
else
2;
return try vki.createDevice(candidate.pdev, .{
.flags = .{},
.queue_create_info_count = queue_count,
.p_queue_create_infos = &qci,
.enabled_layer_count = 0,
.pp_enabled_layer_names = undefined,
.enabled_extension_count = required_device_extensions.len,
.pp_enabled_extension_names = @ptrCast([*]const [*:0]const u8, &required_device_extensions),
.p_enabled_features = null,
}, null);
}
const DeviceCandidate = struct {
pdev: vk.PhysicalDevice,
props: vk.PhysicalDeviceProperties,
queues: QueueAllocation,
};
const QueueAllocation = struct {
graphics_family: u32,
present_family: u32,
};
fn pickPhysicalDevice(
vki: InstanceDispatch,
instance: vk.Instance,
allocator: *Allocator,
surface: vk.SurfaceKHR,
) !DeviceCandidate {
var device_count: u32 = undefined;
_ = try vki.enumeratePhysicalDevices(instance, &device_count, null);
const pdevs = try allocator.alloc(vk.PhysicalDevice, device_count);
defer allocator.free(pdevs);
_ = try vki.enumeratePhysicalDevices(instance, &device_count, pdevs.ptr);
for (pdevs) |pdev| {
if (try checkSuitable(vki, pdev, allocator, surface)) |candidate| {
return candidate;
}
}
return error.NoSuitableDevice;
}
fn checkSuitable(
vki: InstanceDispatch,
pdev: vk.PhysicalDevice,
allocator: *Allocator,
surface: vk.SurfaceKHR,
) !?DeviceCandidate {
const props = vki.getPhysicalDeviceProperties(pdev);
if (!try checkExtensionSupport(vki, pdev, allocator)) {
return null;
}
if (!try checkSurfaceSupport(vki, pdev, surface)) {
return null;
}
if (try allocateQueues(vki, pdev, allocator, surface)) |allocation| {
return DeviceCandidate{
.pdev = pdev,
.props = props,
.queues = allocation
};
}
return null;
}
fn allocateQueues(
vki: InstanceDispatch,
pdev: vk.PhysicalDevice,
allocator: *Allocator,
surface: vk.SurfaceKHR
) !?QueueAllocation {
var family_count: u32 = undefined;
vki.getPhysicalDeviceQueueFamilyProperties(pdev, &family_count, null);
const families = try allocator.alloc(vk.QueueFamilyProperties, family_count);
defer allocator.free(families);
vki.getPhysicalDeviceQueueFamilyProperties(pdev, &family_count, families.ptr);
var graphics_family: ?u32 = null;
var present_family: ?u32 = null;
for (families) |properties, i| {
const family = @intCast(u32, i);
if (graphics_family == null and properties.queue_flags.contains(.{.graphics_bit = true})) {
graphics_family = family;
}
if (present_family == null and (try vki.getPhysicalDeviceSurfaceSupportKHR(pdev, family, surface)) == vk.TRUE) {
present_family = family;
}
}
if (graphics_family != null and present_family != null) {
return QueueAllocation{
.graphics_family = graphics_family.?,
.present_family = present_family.?
};
}
return null;
}
fn checkSurfaceSupport(vki: InstanceDispatch, pdev: vk.PhysicalDevice, surface: vk.SurfaceKHR) !bool {
var format_count: u32 = undefined;
_ = try vki.getPhysicalDeviceSurfaceFormatsKHR(pdev, surface, &format_count, null);
var present_mode_count: u32 = undefined;
_ = try vki.getPhysicalDeviceSurfacePresentModesKHR(pdev, surface, &present_mode_count, null);
return format_count > 0 and present_mode_count > 0;
}
fn checkExtensionSupport(
vki: InstanceDispatch,
pdev: vk.PhysicalDevice,
allocator: *Allocator,
) !bool {
var count: u32 = undefined;
_ = try vki.enumerateDeviceExtensionProperties(pdev, null, &count, null);
const propsv = try allocator.alloc(vk.ExtensionProperties, count);
defer allocator.free(propsv);
_ = try vki.enumerateDeviceExtensionProperties(pdev, null, &count, propsv.ptr);
for (required_device_extensions) |ext| {
for (propsv) |props| {
const len = std.mem.indexOfScalar(u8, &props.extension_name, 0).?;
const prop_ext_name = props.extension_name[0 .. len];
if (std.mem.eql(u8, ext, prop_ext_name)) {
break;
}
} else {
return false;
}
}
return true;
}

View File

@@ -1,5 +1,51 @@
const std = @import("std"); const std = @import("std");
const vk = @import("vulkan");
const c = @import("c.zig");
const GraphicsContext = @import("graphics_context.zig").GraphicsContext;
pub fn main() anyerror!void { const BaseDispatch = struct {
std.debug.warn("All your codebase are belong to us.\n", .{}); vkCreateInstance: vk.PfnCreateInstance,
usingnamespace vk.BaseWrapper(@This());
};
const InstanceDispatch = struct {
vkDestroyInstance: vk.PfnDestroyInstance,
vkCreateDevice: vk.PfnCreateDevice,
vkDestroySurfaceKHR: vk.PfnDestroySurfaceKHR,
usingnamespace vk.InstanceWrapper(@This());
};
const DeviceDispatch = struct {
vkDestroyDevice: vk.PfnDestroyDevice,
usingnamespace vk.DeviceWrapper(@This());
};
const app_name = "vulkan-zig example";
const app_info = vk.ApplicationInfo{
.p_application_name = app_name,
.application_version = vk.makeVersion(0, 0, 0),
.p_engine_name = app_name,
.engine_version = vk.makeVersion(0, 0, 0),
.api_version = vk.API_VERSION_1_2,
};
pub fn main() !void {
if (c.glfwInit() != c.GLFW_TRUE) return error.GlfwInitFailed;
defer c.glfwTerminate();
const dim = vk.Extent2D{.width = 800, .height = 600};
c.glfwWindowHint(c.GLFW_CLIENT_API, c.GLFW_NO_API);
const window = c.glfwCreateWindow(
dim.width,
dim.height,
app_name,
null,
null
) orelse return error.WindowInitFailed;
defer c.glfwDestroyWindow(window);
const gc = try GraphicsContext.init(std.heap.page_allocator, &app_info, window);
defer gc.deinit();
} }

View File

@@ -40,8 +40,8 @@ const preamble =
\\ pub fn subtract(lhs: FlagsType, rhs: FlagsType) FlagsType { \\ pub fn subtract(lhs: FlagsType, rhs: FlagsType) FlagsType {
\\ return fromInt(toInt(lhs) & toInt(rhs.complement())); \\ return fromInt(toInt(lhs) & toInt(rhs.complement()));
\\ } \\ }
\\ pub fn contains(lhs: FlagsType, rhs: FlagsType) FlagsType { \\ pub fn contains(lhs: FlagsType, rhs: FlagsType) bool {
\\ return toInt(merge(lhs, rhs)) == toInt(rhs); \\ return toInt(intersect(lhs, rhs)) == toInt(rhs);
\\ } \\ }
\\ }; \\ };
\\} \\}
@@ -409,9 +409,9 @@ fn Renderer(comptime WriterType: type) type {
try self.writeIdentifier(name[2..]); try self.writeIdentifier(name[2..]);
return; return;
} else if (mem.startsWith(u8, name, "PFN_vk")) { } else if (mem.startsWith(u8, name, "PFN_vk")) {
// Function pointer type, strip off the PFN_vk part. Note that this function // Function pointer type, strip off the PFN_vk part and replace it with Pfn. Note that
// is only called to render the typedeffed function pointers like vkVoidFunction // this function is only called to render the typedeffed function pointers like vkVoidFunction
try self.writeIdentifier(name[6..]); try self.writeIdentifierFmt("Pfn{}", .{name[6..]});
return; return;
} else if (mem.startsWith(u8, name, "VK_")) { } else if (mem.startsWith(u8, name, "VK_")) {
// Constants // Constants
@@ -706,7 +706,7 @@ fn Renderer(comptime WriterType: type) type {
} }
fn renderCommandPtrName(self: *Self, name: []const u8) !void { fn renderCommandPtrName(self: *Self, name: []const u8) !void {
try self.writeIdentifierFmt("{}Fn", .{util.trimVkNamespace(name)}); try self.writeIdentifierFmt("Pfn{}", .{util.trimVkNamespace(name)});
} }
fn renderCommandPtrs(self: *Self) !void { fn renderCommandPtrs(self: *Self) !void {
@@ -727,7 +727,7 @@ fn Renderer(comptime WriterType: type) type {
try self.writer.writeAll( try self.writer.writeAll(
\\pub const extension_info = struct { \\pub const extension_info = struct {
\\ const Info = struct { \\ const Info = struct {
\\ name: []const u8, \\ name: [:0]const u8,
\\ version: u32, \\ version: u32,
\\ }; \\ };
); );
@@ -755,6 +755,8 @@ fn Renderer(comptime WriterType: type) type {
, .{name} , .{name}
); );
try self.renderWrapperLoader(dispatch_type);
for (self.registry.decls) |decl| { for (self.registry.decls) |decl| {
if (decl.decl_type == .command) { if (decl.decl_type == .command) {
const command = decl.decl_type.command; const command = decl.decl_type.command;
@@ -767,6 +769,36 @@ fn Renderer(comptime WriterType: type) type {
try self.writer.writeAll("};}\n"); try self.writer.writeAll("};}\n");
} }
fn renderWrapperLoader(self: *Self, dispatch_type: CommandDispatchType) !void {
const params = switch (dispatch_type) {
.base => "loader: PfnGetInstanceProcAddr",
.instance => "instance: Instance, loader: PfnGetInstanceProcAddr",
.device => "device: Device, loader: PfnGetDeviceProcAddr",
};
const loader_first_param = switch (dispatch_type) {
.base => ".null_handle, ",
.instance => "instance, ",
.device => "device, ",
};
@setEvalBranchQuota(2000);
try self.writer.print(
\\pub fn load({}) !Self {{
\\ var self: Self = undefined;
\\ inline for (std.meta.fields(Self)) |field| {{
\\ const name = @ptrCast([*:0]const u8, field.name ++ "\x00");
\\ const cmd_ptr = loader({}name) orelse return error.InvalidCommand;
\\ @field(self, field.name) = @ptrCast(field.field_type, cmd_ptr);
\\ }}
\\ return self;
\\}}
\\
, .{params, loader_first_param}
);
}
fn derefName(name: []const u8) []const u8 { fn derefName(name: []const u8) []const u8 {
var it = util.SegmentIterator.init(name); var it = util.SegmentIterator.init(name);
return if (mem.eql(u8, it.next().?, "p")) return if (mem.eql(u8, it.next().?, "p"))
@@ -1012,7 +1044,7 @@ fn Renderer(comptime WriterType: type) type {
try self.renderResultAsErrorName(name); try self.renderResultAsErrorName(name);
try self.writer.writeAll(", "); try self.writer.writeAll(", ");
} }
try self.writer.writeAll("Unkown, }"); try self.writer.writeAll("Unknown, }");
} }
fn renderResultAsErrorName(self: *Self, name: []const u8) !void { fn renderResultAsErrorName(self: *Self, name: []const u8) !void {