implementing proper pipeline cache
This commit is contained in:
+72
-28
@@ -8,6 +8,7 @@ const VkError = @import("error_set.zig").VkError;
|
||||
const Device = @import("Device.zig");
|
||||
const PipelineCache = @import("PipelineCache.zig");
|
||||
const PipelineLayout = @import("PipelineLayout.zig");
|
||||
const RenderPass = @import("RenderPass.zig");
|
||||
|
||||
const Self = @This();
|
||||
pub const ObjectType: vk.ObjectType = .pipeline;
|
||||
@@ -108,6 +109,13 @@ pub fn initGraphics(device: *Device, allocator: std.mem.Allocator, cache: ?*Pipe
|
||||
var color_attachments: ?[]vk.PipelineColorBlendAttachmentState = null;
|
||||
errdefer if (color_attachments) |value| allocator.free(value);
|
||||
|
||||
const dynamic_state = try parseDynamicState(info.p_dynamic_state);
|
||||
const rasterizer_discard_enable = if (info.p_rasterization_state) |state|
|
||||
state.rasterizer_discard_enable == .true
|
||||
else
|
||||
false;
|
||||
const has_color_attachments, const has_depth_stencil_attachment = try renderPassAttachmentState(info);
|
||||
|
||||
return .{
|
||||
.owner = device,
|
||||
.vtable = undefined,
|
||||
@@ -144,8 +152,12 @@ pub fn initGraphics(device: *Device, allocator: std.mem.Allocator, cache: ?*Pipe
|
||||
},
|
||||
.viewport_state = .{
|
||||
.viewports = blk: {
|
||||
if (rasterizer_discard_enable or dynamic_state.viewport) {
|
||||
break :blk null;
|
||||
}
|
||||
if (info.p_viewport_state) |viewport_state| {
|
||||
if (viewport_state.p_viewports) |p_viewports| {
|
||||
if (viewport_state.viewport_count != 0) {
|
||||
const p_viewports = viewport_state.p_viewports orelse return VkError.ValidationFailed;
|
||||
const copy = allocator.dupe(vk.Viewport, p_viewports[0..viewport_state.viewport_count]) catch return VkError.OutOfHostMemory;
|
||||
viewports = copy;
|
||||
break :blk viewports;
|
||||
@@ -154,8 +166,12 @@ pub fn initGraphics(device: *Device, allocator: std.mem.Allocator, cache: ?*Pipe
|
||||
break :blk null;
|
||||
},
|
||||
.scissor = blk: {
|
||||
if (rasterizer_discard_enable or dynamic_state.scissor) {
|
||||
break :blk null;
|
||||
}
|
||||
if (info.p_viewport_state) |viewport_state| {
|
||||
if (viewport_state.p_scissors) |p_scissors| {
|
||||
if (viewport_state.scissor_count != 0) {
|
||||
const p_scissors = viewport_state.p_scissors orelse return VkError.ValidationFailed;
|
||||
const copy = allocator.dupe(vk.Rect2D, p_scissors[0..viewport_state.scissor_count]) catch return VkError.OutOfHostMemory;
|
||||
scissors = copy;
|
||||
break :blk scissors;
|
||||
@@ -171,9 +187,17 @@ pub fn initGraphics(device: *Device, allocator: std.mem.Allocator, cache: ?*Pipe
|
||||
.line_width = if (info.p_rasterization_state) |state| state.line_width else return VkError.ValidationFailed,
|
||||
},
|
||||
.color_blend = blk: {
|
||||
if (rasterizer_discard_enable or !has_color_attachments) {
|
||||
break :blk .{
|
||||
.attachments = null,
|
||||
.constants = .{ 0.0, 0.0, 0.0, 0.0 },
|
||||
};
|
||||
}
|
||||
|
||||
if (info.p_color_blend_state) |state| {
|
||||
break :blk .{
|
||||
.attachments = if (state.p_attachments) |attachments| blk_attachments: {
|
||||
.attachments = if (state.attachment_count != 0) blk_attachments: {
|
||||
const attachments = state.p_attachments orelse return VkError.ValidationFailed;
|
||||
color_attachments = allocator.dupe(vk.PipelineColorBlendAttachmentState, attachments[0..state.attachment_count]) catch return VkError.OutOfHostMemory;
|
||||
break :blk_attachments color_attachments;
|
||||
} else null,
|
||||
@@ -186,36 +210,56 @@ pub fn initGraphics(device: *Device, allocator: std.mem.Allocator, cache: ?*Pipe
|
||||
.constants = .{ 0.0, 0.0, 0.0, 0.0 },
|
||||
};
|
||||
},
|
||||
.depth_stencil = if (info.p_depth_stencil_state) |state| state.* else null,
|
||||
.dynamic_state = blk: {
|
||||
var state: DynamicState = .{};
|
||||
|
||||
if (info.p_dynamic_state) |dynamic_state| {
|
||||
if (dynamic_state.p_dynamic_states) |states| {
|
||||
for (states[0..dynamic_state.dynamic_state_count]) |info_state| {
|
||||
switch (info_state) {
|
||||
.viewport => state.viewport = true,
|
||||
.scissor => state.scissor = true,
|
||||
.line_width => state.line_width = true,
|
||||
.depth_bias => state.depth_bias = true,
|
||||
.blend_constants => state.blend_constants = true,
|
||||
.depth_bounds => state.depth_bounds = true,
|
||||
.stencil_compare_mask => state.stencil_compare_mask = true,
|
||||
.stencil_write_mask => state.stencil_write_mask = true,
|
||||
.stencil_reference => state.stencil_reference = true,
|
||||
else => return VkError.Unknown,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break :blk state;
|
||||
},
|
||||
.depth_stencil = if (rasterizer_discard_enable or !has_depth_stencil_attachment)
|
||||
null
|
||||
else if (info.p_depth_stencil_state) |state|
|
||||
state.*
|
||||
else
|
||||
null,
|
||||
.dynamic_state = dynamic_state,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
fn parseDynamicState(info: ?*const vk.PipelineDynamicStateCreateInfo) VkError!DynamicState {
|
||||
var state: DynamicState = .{};
|
||||
const dynamic_state = info orelse return state;
|
||||
if (dynamic_state.dynamic_state_count == 0) {
|
||||
return state;
|
||||
}
|
||||
|
||||
const states = dynamic_state.p_dynamic_states orelse return VkError.ValidationFailed;
|
||||
for (states[0..dynamic_state.dynamic_state_count]) |info_state| {
|
||||
switch (info_state) {
|
||||
.viewport => state.viewport = true,
|
||||
.scissor => state.scissor = true,
|
||||
.line_width => state.line_width = true,
|
||||
.depth_bias => state.depth_bias = true,
|
||||
.blend_constants => state.blend_constants = true,
|
||||
.depth_bounds => state.depth_bounds = true,
|
||||
.stencil_compare_mask => state.stencil_compare_mask = true,
|
||||
.stencil_write_mask => state.stencil_write_mask = true,
|
||||
.stencil_reference => state.stencil_reference = true,
|
||||
else => return VkError.Unknown,
|
||||
}
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
fn renderPassAttachmentState(info: *const vk.GraphicsPipelineCreateInfo) VkError!struct { bool, bool } {
|
||||
if (info.render_pass == .null_handle) {
|
||||
return .{ true, true };
|
||||
}
|
||||
|
||||
const render_pass = try NonDispatchable(RenderPass).fromHandleObject(info.render_pass);
|
||||
return .{
|
||||
try render_pass.subpassHasColorAttachments(info.subpass),
|
||||
try render_pass.subpassHasDepthStencilAttachment(info.subpass),
|
||||
};
|
||||
}
|
||||
|
||||
pub inline fn destroy(self: *Self, allocator: std.mem.Allocator) void {
|
||||
switch (self.mode) {
|
||||
.compute => {},
|
||||
|
||||
@@ -12,7 +12,7 @@ const Self = @This();
|
||||
pub const ObjectType: vk.ObjectType = .pipeline_cache;
|
||||
|
||||
owner: *Device,
|
||||
data_available: bool,
|
||||
data: []u8,
|
||||
|
||||
vtable: *const VTable,
|
||||
|
||||
@@ -24,55 +24,105 @@ pub const Header = vk.PipelineCacheHeaderVersionOne;
|
||||
|
||||
pub fn init(device: *Device, allocator: std.mem.Allocator, info: *const vk.PipelineCacheCreateInfo) VkError!Self {
|
||||
_ = allocator;
|
||||
const has_initial_data = info.initial_data_size != 0 or info.p_initial_data != null;
|
||||
if (info.initial_data_size != 0 and info.p_initial_data == null) {
|
||||
return VkError.ValidationFailed;
|
||||
}
|
||||
|
||||
const initial_data = if (info.p_initial_data) |ptr|
|
||||
@as([*]const u8, @ptrCast(ptr))[0..info.initial_data_size]
|
||||
else
|
||||
&.{};
|
||||
|
||||
const data_allocator = device.host_allocator.allocator();
|
||||
const data = if (isCompatibleInitialData(device, initial_data))
|
||||
data_allocator.dupe(u8, initial_data) catch return VkError.OutOfHostMemory
|
||||
else
|
||||
createEmptyData(device, data_allocator) catch return VkError.OutOfHostMemory;
|
||||
|
||||
return .{
|
||||
.owner = device,
|
||||
.data_available = !has_initial_data or info.initial_data_size >= dataSize(),
|
||||
.data = data,
|
||||
.vtable = undefined,
|
||||
};
|
||||
}
|
||||
|
||||
pub inline fn destroy(self: *Self, allocator: std.mem.Allocator) void {
|
||||
pub fn destroy(self: *Self, allocator: std.mem.Allocator) void {
|
||||
self.owner.host_allocator.allocator().free(self.data);
|
||||
self.vtable.destroy(self, allocator);
|
||||
}
|
||||
|
||||
pub fn merge(self: *Self) VkError!void {
|
||||
_ = self;
|
||||
pub fn merge(self: *Self, source: *const Self) VkError!void {
|
||||
if (source.data.len <= @sizeOf(Header)) {
|
||||
return;
|
||||
}
|
||||
if (!isCompatibleInitialData(self.owner, source.data)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const old_len = self.data.len;
|
||||
const source_payload = source.data[@sizeOf(Header)..];
|
||||
self.data = self.owner.host_allocator.allocator().realloc(self.data, old_len + source_payload.len) catch return VkError.OutOfHostMemory;
|
||||
@memcpy(self.data[old_len..], source_payload);
|
||||
}
|
||||
|
||||
pub fn appendPayload(self: *Self, payload: []const u8) VkError!void {
|
||||
if (payload.len == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const old_len = self.data.len;
|
||||
self.data = self.owner.host_allocator.allocator().realloc(self.data, old_len + payload.len) catch return VkError.OutOfHostMemory;
|
||||
@memcpy(self.data[old_len..], payload);
|
||||
}
|
||||
|
||||
pub fn getData(self: *Self, data: ?[]u8) vk.Result {
|
||||
if (!self.data_available) {
|
||||
return .success;
|
||||
}
|
||||
|
||||
const cache_header = self.header();
|
||||
const bytes = std.mem.asBytes(&cache_header);
|
||||
|
||||
if (data) |dst| {
|
||||
if (dst.len < bytes.len) {
|
||||
const written = @min(dst.len, self.data.len);
|
||||
@memcpy(dst[0..written], self.data[0..written]);
|
||||
if (written < self.data.len) {
|
||||
return .incomplete;
|
||||
}
|
||||
|
||||
@memcpy(dst[0..bytes.len], bytes);
|
||||
}
|
||||
|
||||
return .success;
|
||||
}
|
||||
|
||||
pub inline fn dataSize() usize {
|
||||
return @sizeOf(Header);
|
||||
}
|
||||
|
||||
pub inline fn availableDataSize(self: *const Self) usize {
|
||||
return if (self.data_available) dataSize() else 0;
|
||||
return self.data.len;
|
||||
}
|
||||
|
||||
fn header(self: *const Self) Header {
|
||||
fn makeHeader(device: *const Device) Header {
|
||||
return .{
|
||||
.header_size = @sizeOf(Header),
|
||||
.header_version = .one,
|
||||
.vendor_id = @intCast(root.VULKAN_VENDOR_ID),
|
||||
.device_id = self.owner.physical_device.props.device_id,
|
||||
.pipeline_cache_uuid = self.owner.physical_device.props.pipeline_cache_uuid,
|
||||
.device_id = device.physical_device.props.device_id,
|
||||
.pipeline_cache_uuid = device.physical_device.props.pipeline_cache_uuid,
|
||||
};
|
||||
}
|
||||
|
||||
fn createEmptyData(device: *const Device, allocator: std.mem.Allocator) VkError![]u8 {
|
||||
const cache_header = makeHeader(device);
|
||||
return allocator.dupe(u8, std.mem.asBytes(&cache_header)) catch VkError.OutOfHostMemory;
|
||||
}
|
||||
|
||||
fn isCompatibleInitialData(device: *const Device, initial_data: []const u8) bool {
|
||||
if (initial_data.len == 0) {
|
||||
return false;
|
||||
}
|
||||
if (initial_data.len < @sizeOf(Header)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const cache_header = readHeader(initial_data);
|
||||
const expected = makeHeader(device);
|
||||
return cache_header.header_size == @sizeOf(Header) and
|
||||
cache_header.header_version == .one and
|
||||
cache_header.vendor_id == expected.vendor_id and
|
||||
cache_header.device_id == expected.device_id and
|
||||
std.mem.eql(u8, &cache_header.pipeline_cache_uuid, &expected.pipeline_cache_uuid);
|
||||
}
|
||||
|
||||
inline fn readHeader(bytes: []const u8) Header {
|
||||
return std.mem.bytesToValue(Header, bytes);
|
||||
}
|
||||
|
||||
@@ -98,3 +98,25 @@ pub fn destroy(self: *Self, allocator: std.mem.Allocator) void {
|
||||
pub inline fn getRenderAreaGranularity(self: *Self) vk.Extent2D {
|
||||
return self.vtable.getRenderAreaGranularity(self);
|
||||
}
|
||||
|
||||
pub fn subpassHasColorAttachments(self: *const Self, subpass_index: u32) VkError!bool {
|
||||
if (subpass_index >= self.subpasses.len) {
|
||||
return VkError.ValidationFailed;
|
||||
}
|
||||
|
||||
const color_attachments = self.subpasses[subpass_index].color_attachments orelse return false;
|
||||
for (color_attachments) |attachment| {
|
||||
if (attachment.attachment != vk.ATTACHMENT_UNUSED) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
pub fn subpassHasDepthStencilAttachment(self: *const Self, subpass_index: u32) VkError!bool {
|
||||
if (subpass_index >= self.subpasses.len) {
|
||||
return VkError.ValidationFailed;
|
||||
}
|
||||
|
||||
return self.subpasses[subpass_index].depth_stencil_attachments != null;
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
//! Here lies the documentation of the common internal API that backends need to implement
|
||||
//! Here lies the documentation of the common internal API that Ape backends need to implement
|
||||
|
||||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
|
||||
@@ -1480,12 +1480,10 @@ pub export fn apeGetImageSparseMemoryRequirements(p_device: vk.Device, p_image:
|
||||
defer entryPointEndLogTrace();
|
||||
|
||||
Dispatchable(Device).checkHandleValidity(p_device) catch |err| return errorLogger(err);
|
||||
NonDispatchable(Image).checkHandleValidity(p_image) catch |err| return errorLogger(err);
|
||||
|
||||
const image = NonDispatchable(Image).fromHandleObject(p_image) catch |err| return errorLogger(err);
|
||||
lib.unsupported("sparse images are not supported", .{});
|
||||
|
||||
notImplementedWarning();
|
||||
|
||||
_ = image;
|
||||
_ = requirements;
|
||||
}
|
||||
|
||||
@@ -1511,7 +1509,7 @@ pub export fn apeGetPipelineCacheData(p_device: vk.Device, p_cache: vk.PipelineC
|
||||
const bytes = @as([*]u8, @ptrCast(ptr))[0..size.*];
|
||||
break :blk cache.getData(bytes);
|
||||
} else cache.getData(null);
|
||||
size.* = if (result == .incomplete) 0 else available;
|
||||
size.* = if (result == .incomplete) @min(size.*, available) else available;
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1578,10 +1576,10 @@ pub export fn apeMergePipelineCaches(p_device: vk.Device, p_dst: vk.PipelineCach
|
||||
const dst = NonDispatchable(PipelineCache).fromHandleObject(p_dst) catch |err| return toVkResult(err);
|
||||
|
||||
for (0..count) |i| {
|
||||
_ = NonDispatchable(PipelineCache).fromHandleObject(p_srcs[i]) catch |err| return toVkResult(err);
|
||||
const src = NonDispatchable(PipelineCache).fromHandleObject(p_srcs[i]) catch |err| return toVkResult(err);
|
||||
dst.merge(src) catch |err| return toVkResult(err);
|
||||
}
|
||||
|
||||
dst.merge() catch |err| return toVkResult(err);
|
||||
return .success;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user