diff --git a/assets/cubyz/shaders/graphics/Text.frag b/assets/cubyz/shaders/graphics/Text.frag index 74ffc2415a..27702027ff 100644 --- a/assets/cubyz/shaders/graphics/Text.frag +++ b/assets/cubyz/shaders/graphics/Text.frag @@ -7,6 +7,7 @@ layout(location = 1) flat in vec4 color; layout(binding = 0) uniform sampler2D textureSampler; +#ifdef OPEN_GL // in pixels layout(location = 0) uniform vec4 textureRect; layout(location = 1) uniform vec2 scene; @@ -15,6 +16,18 @@ layout(location = 3) uniform float ratio; layout(location = 4) uniform int fontEffects; layout(location = 6) uniform vec2 fontSize; layout(location = 7) uniform vec4 textureBounds; +#else +layout(push_constant, std430) uniform _ { + vec4 textureRect; + vec2 offset; + int fontEffects; + vec4 textureBounds; + vec2 scene; + float ratio; + uint inColor; + vec2 fontSize; +}; +#endif vec2 convert2Proportional(vec2 original, vec2 full){ return vec2(original.x/full.x, original.y/full.y); diff --git a/assets/cubyz/shaders/graphics/Text.vert b/assets/cubyz/shaders/graphics/Text.vert index 5f59862ddb..630e77c92e 100644 --- a/assets/cubyz/shaders/graphics/Text.vert +++ b/assets/cubyz/shaders/graphics/Text.vert @@ -5,6 +5,7 @@ layout(location = 0) in vec2 face_pos; layout(location = 0) out vec2 frag_face_pos; layout(location = 1) flat out vec4 color; +#ifdef OPEN_GL // in pixels layout(location = 0) uniform vec4 textureRect; layout(location = 1) uniform vec2 scene; @@ -12,6 +13,18 @@ layout(location = 2) uniform vec2 offset; layout(location = 3) uniform float ratio; layout(location = 4) uniform int fontEffects; layout(location = 5) uniform uint inColor; +#else +layout(push_constant, std430) uniform _ { + vec4 textureRect; + vec2 offset; + int fontEffects; + vec4 textureBounds; + vec2 scene; + float ratio; + uint inColor; + vec2 fontSize; +}; +#endif vec2 convert2Proportional(vec2 original, vec2 full) { return vec2(original.x/full.x, original.y/full.y); diff --git a/src/graphics.zig b/src/graphics.zig index 79350a9ef5..aa21f3103f 100644 --- a/src/graphics.zig +++ b/src/graphics.zig @@ -1083,15 +1083,34 @@ pub const TextBuffer = struct { // MARK: TextBuffer defer draw.restoreScale(oldScale); var x: f32 = 0; var y: f32 = 0; - TextRendering.pipeline.bind(draw.getScissor()); var viewport: [4]c_int = undefined; c.glGetIntegerv(c.GL_VIEWPORT, &viewport); - c.glUniform2f(TextRendering.uniforms.scene, @floatFromInt(viewport[2]), @floatFromInt(viewport[3])); - c.glUniform1f(TextRendering.uniforms.ratio, draw.scale); - c.glUniform1ui(TextRendering.uniforms.inColor, @bitCast(draw.getColor())); - c.glActiveTexture(c.GL_TEXTURE0); - c.glBindTexture(c.GL_TEXTURE_2D, TextRendering.glyphTexture[0]); - draw.rectVao.bind(); + + if (main.settings.launchConfig.vulkanTestingMode) { + vulkan.currentFrame.guiCommands.bindPipeline(TextRendering.pipeline, draw.getScissor()); + vulkan.currentFrame.guiCommands.pushConstants(TextRendering.pipeline, &TextRendering.CompleteUniforms{ + .glyphData = undefined, + .scene = .{@floatFromInt(viewport[2]), @floatFromInt(viewport[3])}, + .ratio = draw.scale, + .inColor = @bitCast(draw.getColor()), + .fontSize = .{@floatFromInt(TextRendering.textureWidth), @floatFromInt(TextRendering.textureHeight)}, + }); + vulkan.currentFrame.guiCommands.bindDescriptors(TextRendering.pipeline, .graphics, 0, &.{ + .{.image = .{.binding = 0, .image = TextRendering.glyphTexture[0].vulkanImage.?}}, + }); + + vulkan.currentFrame.guiCommands.bindVertexArray(draw.rectVao); + } else { + TextRendering.pipeline.bind(draw.getScissor()); + + c.glUniform2f(TextRendering.uniforms.scene, @floatFromInt(viewport[2]), @floatFromInt(viewport[3])); + c.glUniform1f(TextRendering.uniforms.ratio, draw.scale); + c.glUniform1ui(TextRendering.uniforms.inColor, @bitCast(draw.getColor())); + c.glActiveTexture(c.GL_TEXTURE0); + c.glBindTexture(c.GL_TEXTURE_2D, TextRendering.glyphTexture[0].textureID); + + draw.rectVao.bind(); + } const lineWraps: []f32 = main.stackAllocator.alloc(f32, self.lineBreaks.items.len - 1); defer main.stackAllocator.free(lineWraps); var i: usize = 0; @@ -1150,15 +1169,33 @@ pub const TextBuffer = struct { // MARK: TextBuffer defer draw.restoreScale(oldScale); var x: f32 = 0; var y: f32 = 0; - TextRendering.pipeline.bind(draw.getScissor()); var viewport: [4]c_int = undefined; c.glGetIntegerv(c.GL_VIEWPORT, &viewport); - c.glUniform2f(TextRendering.uniforms.scene, @floatFromInt(viewport[2]), @floatFromInt(viewport[3])); - c.glUniform1f(TextRendering.uniforms.ratio, draw.scale); - c.glUniform1ui(TextRendering.uniforms.inColor, @bitCast(draw.getColor())); - c.glActiveTexture(c.GL_TEXTURE0); - c.glBindTexture(c.GL_TEXTURE_2D, TextRendering.glyphTexture[0]); - draw.rectVao.bind(); + + if (main.settings.launchConfig.vulkanTestingMode) { + vulkan.currentFrame.guiCommands.bindPipeline(TextRendering.pipeline, draw.getScissor()); + vulkan.currentFrame.guiCommands.pushConstants(TextRendering.pipeline, &TextRendering.CompleteUniforms{ + .glyphData = undefined, + .scene = .{@floatFromInt(viewport[2]), @floatFromInt(viewport[3])}, + .ratio = draw.scale, + .inColor = @bitCast(draw.getColor()), + .fontSize = .{@floatFromInt(TextRendering.textureWidth), @floatFromInt(TextRendering.textureHeight)}, + }); + vulkan.currentFrame.guiCommands.bindDescriptors(TextRendering.pipeline, .graphics, 0, &.{ + .{.image = .{.binding = 0, .image = TextRendering.glyphTexture[0].vulkanImage.?}}, + }); + + vulkan.currentFrame.guiCommands.bindVertexArray(draw.rectVao); + } else { + TextRendering.pipeline.bind(draw.getScissor()); + + c.glUniform2f(TextRendering.uniforms.scene, @floatFromInt(viewport[2]), @floatFromInt(viewport[3])); + c.glUniform1f(TextRendering.uniforms.ratio, draw.scale); + c.glUniform1ui(TextRendering.uniforms.inColor, @bitCast(draw.getColor())); + c.glActiveTexture(c.GL_TEXTURE0); + c.glBindTexture(c.GL_TEXTURE_2D, TextRendering.glyphTexture[0].textureID); + draw.rectVao.bind(); + } const lineWraps: []f32 = main.stackAllocator.alloc(f32, self.lineBreaks.items.len - 1); defer main.stackAllocator.free(lineWraps); var i: usize = 0; @@ -1220,13 +1257,28 @@ const TextRendering = struct { // MARK: TextRendering fontSize: c_int, } = undefined; + const CompleteUniforms = extern struct { + glyphData: GlypUniforms, + scene: [2]f32 align(8), + ratio: f32, + inColor: c_uint, + fontSize: [2]f32 align(8), + }; + + const GlypUniforms = extern struct { + textureRect: [4]f32 align(16), + offset: [2]f32 align(8), + fontEffects: c_int, + textureBounds: [4]f32 align(16), + }; + var freetypeLib: c.FT_Library = undefined; var freetypeFace: c.FT_Face = undefined; var harfbuzzFace: ?*c.hb_face_t = undefined; var harfbuzzFont: ?*c.hb_font_t = undefined; var glyphMapping: main.ListManaged(u31) = undefined; var glyphData: main.ListManaged(Glyph) = undefined; - var glyphTexture: [2]c_uint = undefined; + var glyphTexture: [2]Texture = undefined; var textureWidth: i32 = 1024; const textureHeight: i32 = 16; var textureOffset: i32 = 0; @@ -1247,14 +1299,14 @@ const TextRendering = struct { // MARK: TextRendering &uniforms, draw.SimpleVertex2D, .{ + .bindings = &.{.sampler(0, .{.fragment = true})}, .rasterState = .{.cullMode = .none}, .depthStencilState = .{.depthTest = false, .depthWrite = false}, - .blendState = .{.attachments = &.{.alphaBlending}, .formats = &.{.{.custom = c.VK_FORMAT_R8_UNORM}}}, + .blendState = .{.attachments = &.{.alphaBlending}, .formats = &.{.swapChain}}, + .inputAssemblyState = .{.topology = .triangleStrip}, + .pushConstantSize = @sizeOf(CompleteUniforms), }, ); - pipeline.bind(null); - errdefer pipeline.deinit(); - c.glUniform2f(uniforms.fontSize, @floatFromInt(textureWidth), @floatFromInt(textureHeight)); try ftError(c.FT_Init_FreeType(&freetypeLib)); try ftError(c.FT_New_Face(freetypeLib, "assets/cubyz/fonts/unscii-16-full.ttf", 0, &freetypeFace)); try ftError(c.FT_Set_Pixel_Sizes(freetypeFace, 0, textureHeight)); @@ -1265,18 +1317,37 @@ const TextRendering = struct { // MARK: TextRendering glyphMapping = .init(main.globalAllocator); glyphData = .init(main.globalAllocator); glyphData.append(undefined); // 0 is a reserved value. - c.glGenTextures(2, &glyphTexture); - c.glBindTexture(c.GL_TEXTURE_2D, glyphTexture[0]); - c.glTexImage2D(c.GL_TEXTURE_2D, 0, c.GL_R8, textureWidth, textureHeight, 0, c.GL_RED, c.GL_UNSIGNED_BYTE, null); - c.glTexParameteri(c.GL_TEXTURE_2D, c.GL_TEXTURE_MIN_FILTER, c.GL_NEAREST); - c.glTexParameteri(c.GL_TEXTURE_2D, c.GL_TEXTURE_MAG_FILTER, c.GL_NEAREST); - c.glTexParameteri(c.GL_TEXTURE_2D, c.GL_TEXTURE_WRAP_S, c.GL_REPEAT); - c.glTexParameteri(c.GL_TEXTURE_2D, c.GL_TEXTURE_WRAP_T, c.GL_REPEAT); - c.glBindTexture(c.GL_TEXTURE_2D, glyphTexture[1]); - c.glTexParameteri(c.GL_TEXTURE_2D, c.GL_TEXTURE_MIN_FILTER, c.GL_NEAREST); - c.glTexParameteri(c.GL_TEXTURE_2D, c.GL_TEXTURE_MAG_FILTER, c.GL_NEAREST); - c.glTexParameteri(c.GL_TEXTURE_2D, c.GL_TEXTURE_WRAP_S, c.GL_REPEAT); - c.glTexParameteri(c.GL_TEXTURE_2D, c.GL_TEXTURE_WRAP_T, c.GL_REPEAT); + glyphTexture = .{ + .init(), + .init(), + }; + + if (main.settings.launchConfig.vulkanTestingMode) { + vulkan.currentFrame.guiCommands.bindPipeline(TextRendering.pipeline, null); + errdefer pipeline.deinit(); + + glyphTexture[0].vulkanImage = vulkan.Image.init(.{textureWidth, textureHeight, 1}, .{ + .format = c.VK_FORMAT_R8_UNORM, + .usage = c.VK_IMAGE_USAGE_TRANSFER_DST_BIT | c.VK_IMAGE_USAGE_SAMPLED_BIT | c.VK_IMAGE_USAGE_TRANSFER_SRC_BIT, + }); + } else { + pipeline.bind(null); + errdefer pipeline.deinit(); + c.glUniform2f(uniforms.fontSize, @floatFromInt(textureWidth), @floatFromInt(textureHeight)); + glyphTexture[0].bind(); + + c.glTexImage2D(c.GL_TEXTURE_2D, 0, c.GL_R8, textureWidth, textureHeight, 0, c.GL_RED, c.GL_UNSIGNED_BYTE, null); + c.glTexParameteri(c.GL_TEXTURE_2D, c.GL_TEXTURE_MIN_FILTER, c.GL_NEAREST); + c.glTexParameteri(c.GL_TEXTURE_2D, c.GL_TEXTURE_MAG_FILTER, c.GL_NEAREST); + c.glTexParameteri(c.GL_TEXTURE_2D, c.GL_TEXTURE_WRAP_S, c.GL_REPEAT); + c.glTexParameteri(c.GL_TEXTURE_2D, c.GL_TEXTURE_WRAP_T, c.GL_REPEAT); + glyphTexture[1].bind(); + + c.glTexParameteri(c.GL_TEXTURE_2D, c.GL_TEXTURE_MIN_FILTER, c.GL_NEAREST); + c.glTexParameteri(c.GL_TEXTURE_2D, c.GL_TEXTURE_MAG_FILTER, c.GL_NEAREST); + c.glTexParameteri(c.GL_TEXTURE_2D, c.GL_TEXTURE_WRAP_S, c.GL_REPEAT); + c.glTexParameteri(c.GL_TEXTURE_2D, c.GL_TEXTURE_WRAP_T, c.GL_REPEAT); + } } fn deinit() void { @@ -1284,33 +1355,51 @@ const TextRendering = struct { // MARK: TextRendering ftError(c.FT_Done_FreeType(freetypeLib)) catch {}; glyphMapping.deinit(); glyphData.deinit(); - c.glDeleteTextures(2, &glyphTexture); + glyphTexture[0].deinit(); + glyphTexture[1].deinit(); c.hb_font_destroy(harfbuzzFont); } fn resizeTexture(newWidth: i32) void { textureWidth = newWidth; - const swap = glyphTexture[1]; - glyphTexture[1] = glyphTexture[0]; - glyphTexture[0] = swap; - c.glActiveTexture(c.GL_TEXTURE0); - c.glBindTexture(c.GL_TEXTURE_2D, glyphTexture[0]); - c.glTexImage2D(c.GL_TEXTURE_2D, 0, c.GL_R8, newWidth, textureHeight, 0, c.GL_RED, c.GL_UNSIGNED_BYTE, null); - c.glCopyImageSubData(glyphTexture[1], c.GL_TEXTURE_2D, 0, 0, 0, 0, glyphTexture[0], c.GL_TEXTURE_2D, 0, 0, 0, 0, textureOffset, textureHeight, 1); - pipeline.bind(draw.getScissor()); - c.glUniform2f(uniforms.fontSize, @floatFromInt(textureWidth), @floatFromInt(textureHeight)); + if (main.settings.launchConfig.vulkanTestingMode) { + const old = glyphTexture[0]; + glyphTexture[0].vulkanImage = vulkan.Image.init(.{textureWidth, textureHeight, 1}, .{ + .format = c.VK_FORMAT_R8_UNORM, + .usage = c.VK_IMAGE_USAGE_TRANSFER_DST_BIT | c.VK_IMAGE_USAGE_SAMPLED_BIT | c.VK_IMAGE_USAGE_TRANSFER_SRC_BIT, + }); + glyphTexture[0].vulkanImage.?.uploadImage(old.vulkanImage.?); + old.vulkanImage.?.deferredDeinit(); + } else { + const swap = glyphTexture[1]; + glyphTexture[1] = glyphTexture[0]; + glyphTexture[0] = swap; + c.glActiveTexture(c.GL_TEXTURE0); + c.glBindTexture(c.GL_TEXTURE_2D, glyphTexture[0].textureID); + c.glTexImage2D(c.GL_TEXTURE_2D, 0, c.GL_R8, newWidth, textureHeight, 0, c.GL_RED, c.GL_UNSIGNED_BYTE, null); + c.glCopyImageSubData(glyphTexture[1].textureID, c.GL_TEXTURE_2D, 0, 0, 0, 0, glyphTexture[0].textureID, c.GL_TEXTURE_2D, 0, 0, 0, 0, textureOffset, textureHeight, 1); + + pipeline.bind(draw.getScissor()); + c.glUniform2f(uniforms.fontSize, @floatFromInt(textureWidth), @floatFromInt(textureHeight)); + } } fn uploadData(bitmap: c.FT_Bitmap) void { - const width: i32 = @bitCast(bitmap.width); - const height: i32 = @bitCast(bitmap.rows); const buffer = bitmap.buffer orelse return; - if (textureOffset + width > textureWidth) { + const pitch: u32 = @intCast(bitmap.pitch); + if (textureOffset + @as(i32, @intCast(bitmap.width)) > textureWidth) { resizeTexture(textureWidth*2); } - c.glPixelStorei(c.GL_UNPACK_ALIGNMENT, 1); - c.glTexSubImage2D(c.GL_TEXTURE_2D, 0, textureOffset, 0, width, height, c.GL_RED, c.GL_UNSIGNED_BYTE, buffer); - textureOffset += width; + if (main.settings.launchConfig.vulkanTestingMode) { + glyphTexture[0].vulkanImage.?.uploadData(buffer[0 .. pitch*bitmap.rows], .{ + .imageOffset = .{.x = textureOffset}, + .imageExtent = .{.width = bitmap.width, .height = bitmap.rows, .depth = 1}, + }); + } else { + c.glPixelStorei(c.GL_UNPACK_ALIGNMENT, 1); + c.glTexSubImage2D(c.GL_TEXTURE_2D, 0, textureOffset, 0, @intCast(bitmap.width), @intCast(bitmap.rows), c.GL_RED, c.GL_UNSIGNED_BYTE, buffer); + } + textureOffset += @intCast(bitmap.width); } fn getGlyph(index: u32) !Glyph { @@ -1344,16 +1433,28 @@ const TextRendering = struct { // MARK: TextRendering y += draw.translation[1]; x = @floor(x); y = @ceil(y); - c.glUniform1i(uniforms.fontEffects, fontEffects); - c.glUniform4f(uniforms.textureBounds, @floatFromInt(glyph.textureX), 0, @floatFromInt(glyph.size[0]), @floatFromInt(glyph.size[1])); + + var glypUniforms: GlypUniforms = .{ + .textureRect = .{@floatFromInt(glyph.textureX), 0, @floatFromInt(glyph.size[0]), @floatFromInt(glyph.size[1])}, + .offset = .{@as(f32, @floatFromInt(glyph.bearing[0]))*draw.scale + x, @as(f32, @floatFromInt(glyph.bearing[1]))*draw.scale + y}, + .fontEffects = fontEffects, + .textureBounds = .{@floatFromInt(glyph.textureX), 0, @floatFromInt(glyph.size[0]), @floatFromInt(glyph.size[1])}, + }; if (fontEffects & 0x1000000 != 0) { // bold - c.glUniform2f(uniforms.offset, @as(f32, @floatFromInt(glyph.bearing[0]))*draw.scale + x - 1, @as(f32, @floatFromInt(glyph.bearing[1]))*draw.scale + y - 1); - c.glUniform4f(uniforms.textureRect, @floatFromInt(glyph.textureX - 1), -1, @floatFromInt(glyph.size[0] + 2), @floatFromInt(glyph.size[1] + 2)); + glypUniforms.textureRect = .{@floatFromInt(glyph.textureX - 1), -1, @floatFromInt(glyph.size[0] + 2), @floatFromInt(glyph.size[1] + 2)}; + glypUniforms.offset = .{@as(f32, @floatFromInt(glyph.bearing[0]))*draw.scale + x - 1, @as(f32, @floatFromInt(glyph.bearing[1]))*draw.scale + y - 1}; + } + + if (main.settings.launchConfig.vulkanTestingMode) { + vulkan.currentFrame.guiCommands.pushConstants(TextRendering.pipeline, &glypUniforms); + vulkan.currentFrame.guiCommands.draw(4, 0); } else { - c.glUniform2f(uniforms.offset, @as(f32, @floatFromInt(glyph.bearing[0]))*draw.scale + x, @as(f32, @floatFromInt(glyph.bearing[1]))*draw.scale + y); - c.glUniform4f(uniforms.textureRect, @floatFromInt(glyph.textureX), 0, @floatFromInt(glyph.size[0]), @floatFromInt(glyph.size[1])); + c.glUniform1i(uniforms.fontEffects, glypUniforms.fontEffects); + c.glUniform4f(uniforms.textureBounds, glypUniforms.textureBounds[0], glypUniforms.textureBounds[1], glypUniforms.textureBounds[2], glypUniforms.textureBounds[3]); + c.glUniform2f(uniforms.offset, glypUniforms.offset[0], glypUniforms.offset[1]); + c.glUniform4f(uniforms.textureRect, glypUniforms.textureRect[0], glypUniforms.textureRect[1], glypUniforms.textureRect[2], glypUniforms.textureRect[3]); + c.glDrawArrays(c.GL_TRIANGLE_STRIP, 0, 4); } - c.glDrawArrays(c.GL_TRIANGLE_STRIP, 0, 4); } fn renderText(text: []const u8, x: f32, y: f32, fontSize: f32, initialFontEffect: TextBuffer.FontEffect) void { @@ -2043,7 +2144,7 @@ pub const Texture = struct { // MARK: Texture self.vulkanImage = vulkan.Image.init(.{image.width, image.height, 1}, .{ .usage = c.VK_IMAGE_USAGE_TRANSFER_DST_BIT | c.VK_IMAGE_USAGE_SAMPLED_BIT, }); - self.vulkanImage.?.uploadData(0, std.mem.sliceAsBytes(image.imageData)); + self.vulkanImage.?.uploadData(std.mem.sliceAsBytes(image.imageData), .{.imageExtent = .{.width = image.width, .height = image.height, .depth = 1}}); } } diff --git a/src/graphics/CommandBuffer.zig b/src/graphics/CommandBuffer.zig index 1ad82e6e5c..83135d674d 100644 --- a/src/graphics/CommandBuffer.zig +++ b/src/graphics/CommandBuffer.zig @@ -295,3 +295,16 @@ pub fn copyBufferToImage(self: CommandBuffer, dest: vulkan.Image, destLayout: c. }; c.vkCmdCopyBufferToImage2(self.handle, &info); } + +pub fn copyImageToImage(self: CommandBuffer, dest: vulkan.Image, destLayout: c.VkImageLayout, source: vulkan.Image, sourceLayout: c.VkImageLayout, regions: []const c.VkImageCopy2) void { + const info: c.VkCopyImageInfo2 = .{ + .sType = c.VK_STRUCTURE_TYPE_COPY_IMAGE_INFO_2, + .srcImage = source.handle, + .srcImageLayout = sourceLayout, + .dstImage = dest.handle, + .dstImageLayout = destLayout, + .regionCount = @intCast(regions.len), + .pRegions = regions.ptr, + }; + c.vkCmdCopyImage2(self.handle, &info); +} diff --git a/src/graphics/vulkan.zig b/src/graphics/vulkan.zig index 3136be557c..c39d7de260 100644 --- a/src/graphics/vulkan.zig +++ b/src/graphics/vulkan.zig @@ -963,7 +963,12 @@ pub const Image = struct { // MARK: Image gpu_garbage_collection.deferredFree(.{.image = self}); } - pub fn uploadData(self: Image, offset: usize, data: []const u8) void { + const UploadDataConfig = struct { + imageOffset: c.struct_VkOffset3D = .{}, + imageExtent: c.struct_VkExtent3D = .{}, + }; + + pub fn uploadData(self: Image, data: []const u8, config: UploadDataConfig) void { if (data.len == 0) return; const stagingBuffer: Buffer = .init(data.len, .{.usage = c.VK_BUFFER_USAGE_TRANSFER_SRC_BIT, .hostAccessible = true}); defer stagingBuffer.deferredDeinit(); @@ -987,15 +992,15 @@ pub const Image = struct { // MARK: Image currentFrame.uploadCommands.copyBufferToImage(self, c.VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, stagingBuffer, &.{ .{ .sType = c.VK_STRUCTURE_TYPE_BUFFER_IMAGE_COPY_2, - .bufferOffset = offset, + .bufferOffset = 0, .imageSubresource = .{ .aspectMask = c.VK_IMAGE_ASPECT_COLOR_BIT, .mipLevel = 0, .baseArrayLayer = 0, .layerCount = 1, }, - .imageOffset = .{.x = 0, .y = 0, .z = 0}, - .imageExtent = .{.width = @intCast(self.size[0]), .height = @intCast(self.size[1]), .depth = @intCast(self.size[2])}, + .imageOffset = config.imageOffset, + .imageExtent = config.imageExtent, }, }); currentFrame.uploadCommands.pipelineBarrier(.{.imageMemoryBarriers = &.{ @@ -1012,6 +1017,77 @@ pub const Image = struct { // MARK: Image }, }}); } + + pub fn uploadImage(dest: Image, source: Image) void { + currentFrame.uploadCommands.pipelineBarrier(.{.imageMemoryBarriers = &.{ + .{ + .sType = c.VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, + .srcStageMask = c.VK_PIPELINE_STAGE_2_NONE, + .srcAccessMask = c.VK_ACCESS_2_NONE, + .dstStageMask = c.VK_PIPELINE_STAGE_2_TRANSFER_BIT, + .dstAccessMask = c.VK_ACCESS_2_TRANSFER_WRITE_BIT, + .oldLayout = c.VK_IMAGE_LAYOUT_UNDEFINED, + .newLayout = c.VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + .image = dest.handle, + .subresourceRange = .{.aspectMask = c.VK_IMAGE_ASPECT_COLOR_BIT, .levelCount = dest.mipLevels, .layerCount = 1}, + }, + .{ + .sType = c.VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, + .srcStageMask = c.VK_PIPELINE_STAGE_2_NONE, + .srcAccessMask = c.VK_ACCESS_2_NONE, + .dstStageMask = c.VK_PIPELINE_STAGE_2_TRANSFER_BIT, + .dstAccessMask = c.VK_ACCESS_2_TRANSFER_READ_BIT, + .oldLayout = c.VK_IMAGE_LAYOUT_UNDEFINED, + .newLayout = c.VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + .image = source.handle, + .subresourceRange = .{.aspectMask = c.VK_IMAGE_ASPECT_COLOR_BIT, .levelCount = source.mipLevels, .layerCount = 1}, + }, + }}); + currentFrame.uploadCommands.copyImageToImage(dest, c.VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, source, c.VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, &.{ + .{ + .sType = c.VK_STRUCTURE_TYPE_IMAGE_COPY_2, + .srcSubresource = .{ + .aspectMask = c.VK_IMAGE_ASPECT_COLOR_BIT, + .mipLevel = 0, + .baseArrayLayer = 0, + .layerCount = 1, + }, + .srcOffset = .{}, + .dstSubresource = .{ + .aspectMask = c.VK_IMAGE_ASPECT_COLOR_BIT, + .mipLevel = 0, + .baseArrayLayer = 0, + .layerCount = 1, + }, + .dstOffset = .{}, + .extent = .{.width = @intCast(source.size[0]), .height = @intCast(source.size[1]), .depth = @intCast(source.size[2])}, + }, + }); + currentFrame.uploadCommands.pipelineBarrier(.{.imageMemoryBarriers = &.{ + .{ + .sType = c.VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, + .srcStageMask = c.VK_PIPELINE_STAGE_2_TRANSFER_BIT, + .srcAccessMask = c.VK_ACCESS_2_TRANSFER_WRITE_BIT, + .dstStageMask = c.VK_PIPELINE_STAGE_2_ALL_GRAPHICS_BIT, + .dstAccessMask = c.VK_ACCESS_SHADER_READ_BIT, + .oldLayout = c.VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + .newLayout = c.VK_IMAGE_LAYOUT_READ_ONLY_OPTIMAL, + .image = dest.handle, + .subresourceRange = .{.aspectMask = c.VK_IMAGE_ASPECT_COLOR_BIT, .levelCount = dest.mipLevels, .layerCount = 1}, + }, + .{ + .sType = c.VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, + .srcStageMask = c.VK_PIPELINE_STAGE_2_TRANSFER_BIT, + .srcAccessMask = c.VK_ACCESS_2_TRANSFER_READ_BIT, + .dstStageMask = c.VK_PIPELINE_STAGE_2_ALL_GRAPHICS_BIT, + .dstAccessMask = c.VK_ACCESS_SHADER_READ_BIT, + .oldLayout = c.VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + .newLayout = c.VK_IMAGE_LAYOUT_READ_ONLY_OPTIMAL, + .image = source.handle, + .subresourceRange = .{.aspectMask = c.VK_IMAGE_ASPECT_COLOR_BIT, .levelCount = source.mipLevels, .layerCount = 1}, + }, + }}); + } }; pub const gpu_allocator = struct {