From ee0ffbe09d7eb1a224673f541b89df0d1a144f82 Mon Sep 17 00:00:00 2001 From: Kbz-8 Date: Tue, 14 Apr 2026 02:46:25 +0200 Subject: [PATCH] adding base blit --- src/soft/SoftCommandBuffer.zig | 40 +++++++++++++++++++++++++++++----- src/soft/SoftDevice.zig | 3 --- src/soft/SoftImage.zig | 28 +++++++++--------------- src/soft/device/Blitter.zig | 12 +++++----- src/soft/device/Device.zig | 4 ++++ src/vulkan/CommandBuffer.zig | 5 +++++ src/vulkan/lib_vulkan.zig | 11 +--------- 7 files changed, 62 insertions(+), 41 deletions(-) diff --git a/src/soft/SoftCommandBuffer.zig b/src/soft/SoftCommandBuffer.zig index 9addb5e..da5405e 100644 --- a/src/soft/SoftCommandBuffer.zig +++ b/src/soft/SoftCommandBuffer.zig @@ -41,6 +41,7 @@ pub fn create(device: *base.Device, allocator: std.mem.Allocator, info: *const v .begin = begin, .bindDescriptorSets = bindDescriptorSets, .bindPipeline = bindPipeline, + .blitImage = blitImage, .clearColorImage = clearColorImage, .copyBuffer = copyBuffer, .copyBufferToImage = copyBufferToImage, @@ -162,7 +163,37 @@ pub fn bindPipeline(interface: *Interface, bind_point: vk.PipelineBindPoint, pip self.commands.append(allocator, Command.from(cmd)) catch return VkError.OutOfHostMemory; } -pub fn clearColorImage(interface: *Interface, image: *base.Image, layout: vk.ImageLayout, color: *const vk.ClearColorValue, range: vk.ImageSubresourceRange) VkError!void { +pub fn blitImage(interface: *Interface, src: *base.Image, _: vk.ImageLayout, dst: *base.Image, _: vk.ImageLayout, regions: []const vk.ImageBlit, filter: vk.Filter) VkError!void { + const self: *Self = @alignCast(@fieldParentPtr("interface", interface)); + const allocator = self.command_allocator.allocator(); + + const CommandImpl = struct { + const Impl = @This(); + + src: *const SoftImage, + dst: *SoftImage, + regions: []const vk.ImageBlit, + filter: vk.Filter, + + pub fn execute(impl: *const Impl, device: *ExecutionDevice) VkError!void { + for (impl.regions[0..]) |region| { + try device.blitter.blitRegion(impl.src, impl.dst, region); + } + } + }; + + const cmd = allocator.create(CommandImpl) catch return VkError.OutOfHostMemory; + errdefer allocator.destroy(cmd); + cmd.* = .{ + .src = @alignCast(@fieldParentPtr("interface", src)), + .dst = @alignCast(@fieldParentPtr("interface", dst)), + .regions = allocator.dupe(vk.ImageBlit, regions) catch return VkError.OutOfHostMemory, // Will be freed on cmdbuf reset or destroy + .filter = filter, + }; + self.commands.append(allocator, Command.from(cmd)) catch return VkError.OutOfHostMemory; +} + +pub fn clearColorImage(interface: *Interface, image: *base.Image, _: vk.ImageLayout, color: *const vk.ClearColorValue, range: vk.ImageSubresourceRange) VkError!void { const self: *Self = @alignCast(@fieldParentPtr("interface", interface)); const allocator = self.command_allocator.allocator(); @@ -170,12 +201,12 @@ pub fn clearColorImage(interface: *Interface, image: *base.Image, layout: vk.Ima const Impl = @This(); image: *SoftImage, - layout: vk.ImageLayout, clear_color: vk.ClearColorValue, range: vk.ImageSubresourceRange, - pub fn execute(impl: *const Impl, _: *ExecutionDevice) VkError!void { - try impl.image.clearRange(impl.clear_color, impl.range); + pub fn execute(impl: *const Impl, device: *ExecutionDevice) VkError!void { + const clear_format = try impl.image.getClearFormat(); + try device.blitter.clear(.{ .color = impl.clear_color }, clear_format, impl.image, impl.image.interface.format, impl.range, null); } }; @@ -183,7 +214,6 @@ pub fn clearColorImage(interface: *Interface, image: *base.Image, layout: vk.Ima errdefer allocator.destroy(cmd); cmd.* = .{ .image = @alignCast(@fieldParentPtr("interface", image)), - .layout = layout, .clear_color = color.*, .range = range, }; diff --git a/src/soft/SoftDevice.zig b/src/soft/SoftDevice.zig index f2ad973..4701e31 100644 --- a/src/soft/SoftDevice.zig +++ b/src/soft/SoftDevice.zig @@ -5,7 +5,6 @@ const builtin = @import("builtin"); const config = @import("config"); const SoftQueue = @import("SoftQueue.zig"); -const Blitter = @import("device/Blitter.zig"); pub const SoftBinarySemaphore = @import("SoftBinarySemaphore.zig"); pub const SoftBuffer = @import("SoftBuffer.zig"); @@ -44,7 +43,6 @@ const DeviceAllocator = struct { interface: Interface, device_allocator: if (config.debug_allocator) std.heap.DebugAllocator(.{}) else DeviceAllocator, workers: std.Thread.Pool, -blitter: Blitter, pub fn create(physical_device: *base.PhysicalDevice, allocator: std.mem.Allocator, info: *const vk.DeviceCreateInfo) VkError!*Self { const self = allocator.create(Self) catch return VkError.OutOfHostMemory; @@ -85,7 +83,6 @@ pub fn create(physical_device: *base.PhysicalDevice, allocator: std.mem.Allocato .interface = interface, .device_allocator = if (config.debug_allocator) .init else .{}, .workers = undefined, - .blitter = .init, }; self.workers.init(.{ .allocator = self.device_allocator.allocator() }) catch |err| return switch (err) { diff --git a/src/soft/SoftImage.zig b/src/soft/SoftImage.zig index 9f2775f..e69703d 100644 --- a/src/soft/SoftImage.zig +++ b/src/soft/SoftImage.zig @@ -42,21 +42,13 @@ pub fn getMemoryRequirements(_: *Interface, requirements: *vk.MemoryRequirements requirements.alignment = lib.MEMORY_REQUIREMENTS_IMAGE_ALIGNMENT; } -inline fn clear(self: *Self, pixel: vk.ClearValue, format: vk.Format, view_format: vk.Format, range: vk.ImageSubresourceRange, area: ?vk.Rect2D) VkError!void { - const soft_device: *SoftDevice = @alignCast(@fieldParentPtr("interface", self.interface.owner)); - try soft_device.blitter.clear(pixel, format, self, view_format, range, area); -} - -pub fn clearRange(self: *Self, color: vk.ClearColorValue, range: vk.ImageSubresourceRange) VkError!void { - std.debug.assert(range.aspect_mask == vk.ImageAspectFlags{ .color_bit = true }); - - const clear_format: vk.Format = if (base.vku.vkuFormatIsSINT(@intCast(@intFromEnum(self.interface.format)))) +pub fn getClearFormat(self: *Self) VkError!vk.Format { + return if (base.vku.vkuFormatIsSINT(@intCast(@intFromEnum(self.interface.format)))) .r32g32b32a32_sint else if (base.vku.vkuFormatIsUINT(@intCast(@intFromEnum(self.interface.format)))) .r32g32b32a32_uint else .r32g32b32a32_sfloat; - try self.clear(.{ .color = color }, clear_format, self.interface.format, range, null); } /// Based on SwiftShader vk::Image::copyTo @@ -311,13 +303,13 @@ pub fn copy( } } -fn getTexelMemoryOffsetInSubresource(self: *const Self, offset: vk.Offset3D, subresource: vk.ImageSubresource) usize { +pub fn getTexelMemoryOffsetInSubresource(self: *const Self, offset: vk.Offset3D, subresource: vk.ImageSubresource) usize { return @as(usize, @intCast(offset.z)) * self.getSliceMemSizeForMipLevel(subresource.aspect_mask, subresource.mip_level) + @as(usize, @intCast(offset.y)) * self.getRowPitchMemSizeForMipLevel(subresource.aspect_mask, subresource.mip_level) + @as(usize, @intCast(offset.x)) * base.format.texelSize(base.format.fromAspect(self.interface.format, subresource.aspect_mask)); } -fn getTexelMemoryOffset(self: *const Self, offset: vk.Offset3D, subresource: vk.ImageSubresource) VkError!usize { +pub fn getTexelMemoryOffset(self: *const Self, offset: vk.Offset3D, subresource: vk.ImageSubresource) VkError!usize { return self.getTexelMemoryOffsetInSubresource(offset, subresource) + try self.getSubresourceOffset(subresource.aspect_mask, subresource.mip_level, subresource.array_layer); } @@ -372,7 +364,7 @@ fn getTotalSizeForAspect(interface: *const Interface, aspect_mask: vk.ImageAspec return size * self.interface.array_layers; } -fn getLayerSize(self: *const Self, aspect_mask: vk.ImageAspectFlags) usize { +pub fn getLayerSize(self: *const Self, aspect_mask: vk.ImageAspectFlags) usize { var size: usize = 0; for (0..self.interface.mip_levels) |mip_level| { size += self.getMultiSampledLevelSize(aspect_mask, @intCast(mip_level)); @@ -380,15 +372,15 @@ fn getLayerSize(self: *const Self, aspect_mask: vk.ImageAspectFlags) usize { return size; } -inline fn getMultiSampledLevelSize(self: *const Self, aspect_mask: vk.ImageAspectFlags, mip_level: u32) usize { +pub inline fn getMultiSampledLevelSize(self: *const Self, aspect_mask: vk.ImageAspectFlags, mip_level: u32) usize { return self.getMipLevelSize(aspect_mask, mip_level) * self.interface.samples.toInt(); } -inline fn getMipLevelSize(self: *const Self, aspect_mask: vk.ImageAspectFlags, mip_level: u32) usize { +pub inline fn getMipLevelSize(self: *const Self, aspect_mask: vk.ImageAspectFlags, mip_level: u32) usize { return self.getSliceMemSizeForMipLevel(aspect_mask, mip_level) * self.getMipLevelExtent(mip_level).depth; } -fn getMipLevelExtent(self: *const Self, mip_level: u32) vk.Extent3D { +pub fn getMipLevelExtent(self: *const Self, mip_level: u32) vk.Extent3D { var extent: vk.Extent3D = .{ .width = self.interface.extent.width >> @intCast(mip_level), .height = self.interface.extent.height >> @intCast(mip_level), @@ -402,13 +394,13 @@ fn getMipLevelExtent(self: *const Self, mip_level: u32) vk.Extent3D { return extent; } -fn getSliceMemSizeForMipLevel(self: *const Self, aspect_mask: vk.ImageAspectFlags, mip_level: u32) usize { +pub fn getSliceMemSizeForMipLevel(self: *const Self, aspect_mask: vk.ImageAspectFlags, mip_level: u32) usize { const mip_extent = self.getMipLevelExtent(mip_level); const format = self.interface.formatFromAspect(aspect_mask); return base.format.sliceMemSize(format, mip_extent.width, mip_extent.height); } -fn getRowPitchMemSizeForMipLevel(self: *const Self, aspect_mask: vk.ImageAspectFlags, mip_level: u32) usize { +pub fn getRowPitchMemSizeForMipLevel(self: *const Self, aspect_mask: vk.ImageAspectFlags, mip_level: u32) usize { const mip_extent = self.getMipLevelExtent(mip_level); const format = self.interface.formatFromAspect(aspect_mask); return base.format.pitchMemSize(format, mip_extent.width); diff --git a/src/soft/device/Blitter.zig b/src/soft/device/Blitter.zig index eede8d7..a0ebbdb 100644 --- a/src/soft/device/Blitter.zig +++ b/src/soft/device/Blitter.zig @@ -11,11 +11,7 @@ pub const SoftImageView = @import("../SoftImageView.zig"); const Self = @This(); -blit_mutex: std.Thread.Mutex, - -pub const init: Self = .{ - .blit_mutex = .{}, -}; +pub const init: Self = .{}; pub fn clear(self: *Self, pixel: vk.ClearValue, format: vk.Format, dest: *SoftImage, view_format: vk.Format, range: vk.ImageSubresourceRange, area: ?vk.Rect2D) VkError!void { const dst_format = base.format.fromAspect(view_format, range.aspect_mask); @@ -104,3 +100,9 @@ fn fastClear(self: *Self, clear_value: vk.ClearValue, clear_format: vk.Format, d } return false; } + +pub fn blitRegion(_: *Self, src: *const SoftImage, dst: *SoftImage, region: vk.ImageBlit) VkError!void { + _ = src; + _ = dst; + _ = region; +} diff --git a/src/soft/device/Device.zig b/src/soft/device/Device.zig index 4fbb073..6de90d4 100644 --- a/src/soft/device/Device.zig +++ b/src/soft/device/Device.zig @@ -6,6 +6,7 @@ const SoftDescriptorSet = @import("../SoftDescriptorSet.zig"); const SoftDevice = @import("../SoftDevice.zig"); const SoftPipeline = @import("../SoftPipeline.zig"); +const Blitter = @import("Blitter.zig"); const ComputeRoutines = @import("ComputeRoutines.zig"); const PipelineState = @import("PipelineState.zig"); @@ -13,6 +14,8 @@ const VkError = base.VkError; const Self = @This(); +blitter: Blitter, + compute_routines: ComputeRoutines, /// .graphics = 0 @@ -20,6 +23,7 @@ compute_routines: ComputeRoutines, pipeline_states: [2]PipelineState, pub const init: Self = .{ + .blitter = .init, .compute_routines = undefined, .pipeline_states = undefined, }; diff --git a/src/vulkan/CommandBuffer.zig b/src/vulkan/CommandBuffer.zig index 6277800..ff0dd3b 100644 --- a/src/vulkan/CommandBuffer.zig +++ b/src/vulkan/CommandBuffer.zig @@ -40,6 +40,7 @@ pub const DispatchTable = struct { begin: *const fn (*Self, *const vk.CommandBufferBeginInfo) VkError!void, bindDescriptorSets: *const fn (*Self, vk.PipelineBindPoint, u32, [lib.VULKAN_MAX_DESCRIPTOR_SETS]?*DescriptorSet, []const u32) VkError!void, bindPipeline: *const fn (*Self, vk.PipelineBindPoint, *Pipeline) VkError!void, + blitImage: *const fn (*Self, *Image, vk.ImageLayout, *Image, vk.ImageLayout, []const vk.ImageBlit, vk.Filter) VkError!void, clearColorImage: *const fn (*Self, *Image, vk.ImageLayout, *const vk.ClearColorValue, vk.ImageSubresourceRange) VkError!void, copyBuffer: *const fn (*Self, *Buffer, *Buffer, []const vk.BufferCopy) VkError!void, copyBufferToImage: *const fn (*Self, *Buffer, *Image, vk.ImageLayout, []const vk.BufferImageCopy) VkError!void, @@ -148,6 +149,10 @@ pub inline fn bindPipeline(self: *Self, bind_point: vk.PipelineBindPoint, pipeli try self.dispatch_table.bindPipeline(self, bind_point, pipeline); } +pub inline fn blitImage(self: *Self, src: *Image, src_layout: vk.ImageLayout, dst: *Image, dst_layout: vk.ImageLayout, regions: []const vk.ImageBlit, filter: vk.Filter) VkError!void { + try self.dispatch_table.blitImage(self, src, src_layout, dst, dst_layout, regions, filter); +} + pub inline fn clearColorImage(self: *Self, image: *Image, layout: vk.ImageLayout, color: *const vk.ClearColorValue, ranges: []const vk.ImageSubresourceRange) VkError!void { for (ranges) |range| { try self.dispatch_table.clearColorImage(self, image, layout, color, range); diff --git a/src/vulkan/lib_vulkan.zig b/src/vulkan/lib_vulkan.zig index 204dd09..019c50f 100644 --- a/src/vulkan/lib_vulkan.zig +++ b/src/vulkan/lib_vulkan.zig @@ -1735,16 +1735,7 @@ pub export fn strollCmdBlitImage( const src = NonDispatchable(Image).fromHandleObject(p_src_image) catch |err| return errorLogger(err); const dst = NonDispatchable(Image).fromHandleObject(p_dst_image) catch |err| return errorLogger(err); - notImplementedWarning(); - - _ = cmd; - _ = src; - _ = src_layout; - _ = dst; - _ = dst_layout; - _ = count; - _ = regions; - _ = filter; + cmd.blitImage(src, src_layout, dst, dst_layout, regions[0..count], filter) catch |err| return errorLogger(err); } pub export fn strollCmdClearAttachments(p_cmd: vk.CommandBuffer, attachment_count: u32, attachments: [*]const vk.ClearAttachment, rect_count: u32, rects: [*]const vk.ClearRect) callconv(vk.vulkan_call_conv) void {