294 lines
8.1 KiB
Zig
294 lines
8.1 KiB
Zig
const vk = @import("vulkan");
|
|
|
|
const context = @import("context.zig");
|
|
const device = @import("device.zig");
|
|
const buffer = @import("buffer.zig");
|
|
const commands = @import("commands.zig");
|
|
const assets = @import("../assets.zig");
|
|
|
|
pub const TextureContext = struct {
|
|
image: vk.Image,
|
|
memory: vk.DeviceMemory,
|
|
image_view: vk.ImageView,
|
|
sampler: vk.Sampler,
|
|
|
|
pub fn destroy(self: *const TextureContext, ldc: *const device.LogicalDeviceContext) void {
|
|
ldc.vkd.destroySampler(ldc.device, self.sampler, null);
|
|
ldc.vkd.destroyImageView(ldc.device, self.image_view, null);
|
|
ldc.vkd.destroyImage(ldc.device, self.image, null);
|
|
ldc.vkd.freeMemory(ldc.device, self.memory, null);
|
|
}
|
|
};
|
|
|
|
pub fn createImage(
|
|
vc: context.VulkanContext,
|
|
ldc: device.LogicalDeviceContext,
|
|
width: u32,
|
|
height: u32,
|
|
format: vk.Format,
|
|
tiling: vk.ImageTiling,
|
|
usage: vk.ImageUsageFlags,
|
|
properties: vk.MemoryPropertyFlags,
|
|
) !TextureContext {
|
|
const image_create_info = vk.ImageCreateInfo{
|
|
.image_type = .@"2d",
|
|
.extent = .{
|
|
.width = width,
|
|
.height = height,
|
|
.depth = 1,
|
|
},
|
|
.mip_levels = 1,
|
|
.array_layers = 1,
|
|
.format = format,
|
|
.tiling = tiling,
|
|
.initial_layout = .undefined,
|
|
.usage = usage,
|
|
.samples = .{ .@"1_bit" = true },
|
|
.sharing_mode = .exclusive,
|
|
};
|
|
|
|
const image = try ldc.vkd.createImage(ldc.device, &image_create_info, null);
|
|
errdefer ldc.vkd.destroyImage(ldc.device, image, null);
|
|
|
|
const memory_requirements = ldc.vkd.getImageMemoryRequirements(ldc.device, image);
|
|
const memory_type_index = try buffer.findMemoryType(
|
|
vc,
|
|
ldc,
|
|
memory_requirements.memory_type_bits,
|
|
properties,
|
|
);
|
|
|
|
const alloc_info = vk.MemoryAllocateInfo{
|
|
.allocation_size = memory_requirements.size,
|
|
.memory_type_index = memory_type_index,
|
|
};
|
|
|
|
const memory = try ldc.vkd.allocateMemory(ldc.device, &alloc_info, null);
|
|
errdefer ldc.vkd.freeMemory(ldc.device, memory, null);
|
|
|
|
try ldc.vkd.bindImageMemory(ldc.device, image, memory, 0);
|
|
|
|
const image_view = try createImageView(ldc, image, format);
|
|
errdefer ldc.vkd.destroyImageView(ldc.device, image_view, null);
|
|
|
|
const sampler = try createTextureSampler(ldc);
|
|
errdefer ldc.vkd.destroySampler(ldc.device, sampler, null);
|
|
|
|
return .{
|
|
.image = image,
|
|
.memory = memory,
|
|
.image_view = image_view,
|
|
.sampler = sampler,
|
|
};
|
|
}
|
|
|
|
fn createImageView(
|
|
ldc: device.LogicalDeviceContext,
|
|
image: vk.Image,
|
|
format: vk.Format,
|
|
) !vk.ImageView {
|
|
const view_create_info = vk.ImageViewCreateInfo{
|
|
.image = image,
|
|
.view_type = .@"2d",
|
|
.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,
|
|
},
|
|
};
|
|
|
|
return try ldc.vkd.createImageView(
|
|
ldc.device,
|
|
&view_create_info,
|
|
null,
|
|
);
|
|
}
|
|
|
|
fn createTextureSampler(ldc: device.LogicalDeviceContext) !vk.Sampler {
|
|
const sampler_create_info = vk.SamplerCreateInfo{
|
|
.mag_filter = .linear,
|
|
.min_filter = .linear,
|
|
.mipmap_mode = .linear,
|
|
.address_mode_u = .clamp_to_edge,
|
|
.address_mode_v = .clamp_to_edge,
|
|
.address_mode_w = .clamp_to_edge,
|
|
.mip_lod_bias = 0.0,
|
|
.anisotropy_enable = .false,
|
|
.max_anisotropy = 1.0,
|
|
.compare_enable = .false,
|
|
.compare_op = .always,
|
|
.min_lod = 0.0,
|
|
.max_lod = 0.0,
|
|
.border_color = .int_opaque_black,
|
|
.unnormalized_coordinates = .false,
|
|
};
|
|
|
|
return try ldc.vkd.createSampler(
|
|
ldc.device,
|
|
&sampler_create_info,
|
|
null,
|
|
);
|
|
}
|
|
|
|
pub fn transitionImageLayout(
|
|
ldc: device.LogicalDeviceContext,
|
|
command_context: commands.CommandContext,
|
|
image: vk.Image,
|
|
old_layout: vk.ImageLayout,
|
|
new_layout: vk.ImageLayout,
|
|
) !void {
|
|
const command_buffer = try commands.beginSingleTimeCommands(ldc, command_context);
|
|
|
|
var barrier = vk.ImageMemoryBarrier{
|
|
.src_access_mask = .{},
|
|
.dst_access_mask = .{},
|
|
.old_layout = old_layout,
|
|
.new_layout = new_layout,
|
|
.src_queue_family_index = vk.QUEUE_FAMILY_IGNORED,
|
|
.dst_queue_family_index = vk.QUEUE_FAMILY_IGNORED,
|
|
.image = image,
|
|
.subresource_range = .{
|
|
.aspect_mask = .{ .color_bit = true },
|
|
.base_mip_level = 0,
|
|
.level_count = 1,
|
|
.base_array_layer = 0,
|
|
.layer_count = 1,
|
|
},
|
|
};
|
|
|
|
var source_stage: vk.PipelineStageFlags = undefined;
|
|
var destination_stage: vk.PipelineStageFlags = undefined;
|
|
|
|
if (old_layout == .undefined and new_layout == .transfer_dst_optimal) {
|
|
barrier.src_access_mask = .{};
|
|
barrier.dst_access_mask = .{ .transfer_write_bit = true };
|
|
|
|
source_stage = .{ .top_of_pipe_bit = true };
|
|
destination_stage = .{ .transfer_bit = true };
|
|
} else if (old_layout == .transfer_dst_optimal and new_layout == .shader_read_only_optimal) {
|
|
barrier.src_access_mask = .{ .transfer_write_bit = true };
|
|
barrier.dst_access_mask = .{ .shader_read_bit = true };
|
|
|
|
source_stage = .{ .transfer_bit = true };
|
|
destination_stage = .{ .fragment_shader_bit = true };
|
|
} else {
|
|
return error.UnsupportedLayoutTransition;
|
|
}
|
|
|
|
const image_barriers = [_]vk.ImageMemoryBarrier{barrier};
|
|
|
|
ldc.vkd.cmdPipelineBarrier(
|
|
command_buffer,
|
|
source_stage,
|
|
destination_stage,
|
|
.{},
|
|
null,
|
|
null,
|
|
&image_barriers,
|
|
);
|
|
|
|
try commands.endSingleTimeCommands(ldc, command_context, command_buffer);
|
|
}
|
|
|
|
pub fn copyBufferToImage(
|
|
ldc: device.LogicalDeviceContext,
|
|
command_context: commands.CommandContext,
|
|
source_buffer: vk.Buffer,
|
|
destination_image: vk.Image,
|
|
width: u32,
|
|
height: u32,
|
|
) !void {
|
|
const command_buffer = try commands.beginSingleTimeCommands(ldc, command_context);
|
|
|
|
const region = vk.BufferImageCopy{
|
|
.buffer_offset = 0,
|
|
.buffer_row_length = 0,
|
|
.buffer_image_height = 0,
|
|
.image_subresource = .{
|
|
.aspect_mask = .{ .color_bit = true },
|
|
.mip_level = 0,
|
|
.base_array_layer = 0,
|
|
.layer_count = 1,
|
|
},
|
|
.image_offset = .{ .x = 0, .y = 0, .z = 0 },
|
|
.image_extent = .{
|
|
.width = width,
|
|
.height = height,
|
|
.depth = 1,
|
|
},
|
|
};
|
|
|
|
const regions = [_]vk.BufferImageCopy{region};
|
|
|
|
ldc.vkd.cmdCopyBufferToImage(
|
|
command_buffer,
|
|
source_buffer,
|
|
destination_image,
|
|
.transfer_dst_optimal,
|
|
®ions,
|
|
);
|
|
|
|
try commands.endSingleTimeCommands(ldc, command_context, command_buffer);
|
|
}
|
|
|
|
pub fn initTextureFromRgba(
|
|
vc: context.VulkanContext,
|
|
ldc: device.LogicalDeviceContext,
|
|
command_context: commands.CommandContext,
|
|
asset: []const u8,
|
|
width: u32,
|
|
height: u32,
|
|
format: vk.Format,
|
|
tiling: vk.ImageTiling,
|
|
usage: vk.ImageUsageFlags,
|
|
properties: vk.MemoryPropertyFlags,
|
|
) !TextureContext {
|
|
const staging = try buffer.initStagingBuffer(vc, ldc, asset);
|
|
defer staging.destroy(&ldc);
|
|
|
|
const texture = try createImage(
|
|
vc,
|
|
ldc,
|
|
width,
|
|
height,
|
|
format,
|
|
tiling,
|
|
usage,
|
|
properties,
|
|
);
|
|
try transitionImageLayout(
|
|
ldc,
|
|
command_context,
|
|
texture.image,
|
|
.undefined,
|
|
.transfer_dst_optimal,
|
|
);
|
|
try copyBufferToImage(
|
|
ldc,
|
|
command_context,
|
|
staging.buffer,
|
|
texture.image,
|
|
width,
|
|
height,
|
|
);
|
|
try transitionImageLayout(
|
|
ldc,
|
|
command_context,
|
|
texture.image,
|
|
.transfer_dst_optimal,
|
|
.shader_read_only_optimal,
|
|
);
|
|
return texture;
|
|
}
|