|
| 1 | +#include <etna/PerFrameTransferHelper.hpp> |
| 2 | +#include <etna/Etna.hpp> |
| 3 | + |
| 4 | +#include <vulkan/vulkan_format_traits.hpp> |
| 5 | + |
| 6 | +namespace etna |
| 7 | +{ |
| 8 | + |
| 9 | +PerFrameTransferHelper::PerFrameTransferHelper(CreateInfo info) |
| 10 | + : stagingSize{info.stagingSize} |
| 11 | + , stagingBuffer{etna::get_context().createBuffer(Buffer::CreateInfo{ |
| 12 | + .size = stagingSize, |
| 13 | + .bufferUsage = vk::BufferUsageFlagBits::eTransferDst | vk::BufferUsageFlagBits::eTransferSrc, |
| 14 | + .memoryUsage = VMA_MEMORY_USAGE_AUTO, |
| 15 | + .allocationCreate = |
| 16 | + VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT, |
| 17 | + .name = "PerFrameTransferHelper::stagingBuffer", |
| 18 | + })} |
| 19 | +{ |
| 20 | + stagingBuffer.map(); |
| 21 | +} |
| 22 | + |
| 23 | +void PerFrameTransferHelper::update(vk::CommandBuffer cmd_buf) |
| 24 | +{ |
| 25 | + UpdateContext ctx{.cmdBuf = cmd_buf}; |
| 26 | + for (size_t requestId = 0; auto &request : requestQueue) |
| 27 | + { |
| 28 | + std::visit([&, this](auto &r) { |
| 29 | + bool fulfilled; |
| 30 | + if constexpr (std::is_same_v<std::remove_cvref_t<decltype(r)>, ImageUploadRequest>) |
| 31 | + fulfilled = serveImageUploadRequest(ctx, r); |
| 32 | + else |
| 33 | + fulfilled = serveBufferUploadRequest(ctx, r); |
| 34 | + if (fulfilled) |
| 35 | + ctx.fulfilledRequestIds.push_back(requestId); |
| 36 | + }, request); |
| 37 | + ++requestId; |
| 38 | + } |
| 39 | + for (auto it = ctx.fulfilledRequestIds.rbegin(); it != ctx.fulfilledRequestIds.rend(); ++it) |
| 40 | + requestQueue.erase(requestQueue.begin() + (*it)); |
| 41 | +} |
| 42 | + |
| 43 | +void PerFrameTransferHelper::requestUploadImage( |
| 44 | + Image&& dst, |
| 45 | + std::span<std::byte const> src, |
| 46 | + uint32_t mip_level, |
| 47 | + uint32_t layer, |
| 48 | + std::function<void(Image&&)> &&return_dst_cb) |
| 49 | +{ |
| 50 | + // @TODO: this duplicated code with BlockingTransferHelper! |
| 51 | + auto [w, h, d] = dst.getExtent(); |
| 52 | + |
| 53 | + size_t const bytesPerPixel = vk::blockSize(dst.getFormat()); |
| 54 | + |
| 55 | + ETNA_ASSERTF(d == 1, "3D image uploads are not implemented yet!"); |
| 56 | + |
| 57 | + ETNA_ASSERTF( |
| 58 | + w * h * bytesPerPixel == src.size(), |
| 59 | + "Image size mismatch between CPU and GPU! Expected {} bytes, but got {}!", |
| 60 | + w * h * bytesPerPixel, |
| 61 | + src.size()); |
| 62 | + |
| 63 | + size_t const bytesPerLine = w * bytesPerPixel; |
| 64 | + size_t const maxLinesPerUpload = stagingSize / bytesPerLine; |
| 65 | + ETNA_ASSERTF( |
| 66 | + maxLinesPerUpload > 0, |
| 67 | + "Unable to fit a single line into the staging buffer! Buffer size is {} bytes, but a single " |
| 68 | + "line is {} bytes!", |
| 69 | + stagingSize, |
| 70 | + w * bytesPerPixel); |
| 71 | + |
| 72 | + requestQueue.emplace_back( |
| 73 | + ImageUploadRequest{ |
| 74 | + std::move(dst), |
| 75 | + src, |
| 76 | + mip_level, |
| 77 | + layer, |
| 78 | + bytesPerPixel, |
| 79 | + bytesPerLine, |
| 80 | + 0, |
| 81 | + std::move(return_dst_cb)}); |
| 82 | +} |
| 83 | + |
| 84 | +bool PerFrameTransferHelper::serveImageUploadRequest(UpdateContext &ctx, ImageUploadRequest &r) |
| 85 | +{ |
| 86 | + auto [w, h, _] = r.dst.getExtent(); |
| 87 | + |
| 88 | + // @TODO: pull out |
| 89 | + auto const alignUp = [](size_t id, size_t to) { return ((id - 1) / to + 1) * to; }; |
| 90 | + vk::DeviceSize offset = alignUp(ctx.stagingUsed, r.bytesPerPixel); |
| 91 | + vk::DeviceSize space = stagingSize - offset; |
| 92 | + |
| 93 | + if (space < r.bytesPerLine) |
| 94 | + return false; |
| 95 | + |
| 96 | + size_t const linesLeft = h - r.uploadedLines; |
| 97 | + size_t const linesThisUpload = std::min(space / r.bytesPerLine, linesLeft); |
| 98 | + size_t const bytesThisUpload = linesThisUpload * r.bytesPerLine; |
| 99 | + |
| 100 | + memcpy( |
| 101 | + stagingBuffer.data() + ctx.stagingUsed, |
| 102 | + r.src.data() + r.uploadedLines * r.bytesPerLine, |
| 103 | + bytesThisUpload); |
| 104 | + |
| 105 | + if (r.uploadedLines == 0) |
| 106 | + { |
| 107 | + etna::set_state( |
| 108 | + ctx.cmdBuf, |
| 109 | + r.dst.get(), |
| 110 | + vk::PipelineStageFlagBits2::eTransfer, |
| 111 | + vk::AccessFlagBits2::eTransferWrite, |
| 112 | + vk::ImageLayout::eTransferDstOptimal, |
| 113 | + r.dst.getAspectMaskByFormat()); |
| 114 | + etna::flush_barriers(ctx.cmdBuf); |
| 115 | + } |
| 116 | + |
| 117 | + vk::BufferImageCopy2 copy{ |
| 118 | + .bufferOffset = offset, |
| 119 | + .bufferRowLength = 0, |
| 120 | + .bufferImageHeight = 0, |
| 121 | + .imageSubresource = |
| 122 | + vk::ImageSubresourceLayers{ |
| 123 | + .aspectMask = r.dst.getAspectMaskByFormat(), |
| 124 | + .mipLevel = r.mipLevel, |
| 125 | + .baseArrayLayer = r.layer, |
| 126 | + .layerCount = 1, |
| 127 | + }, |
| 128 | + .imageOffset = vk::Offset3D{0, static_cast<int32_t>(r.uploadedLines), 0}, |
| 129 | + .imageExtent = vk::Extent3D{w, static_cast<uint32_t>(linesThisUpload), 1}, |
| 130 | + }; |
| 131 | + vk::CopyBufferToImageInfo2 info{ |
| 132 | + .srcBuffer = stagingBuffer.get(), |
| 133 | + .dstImage = r.dst.get(), |
| 134 | + .dstImageLayout = vk::ImageLayout::eTransferDstOptimal, |
| 135 | + .regionCount = 1, |
| 136 | + .pRegions = ©, |
| 137 | + }; |
| 138 | + ctx.cmdBuf.copyBufferToImage2(info); |
| 139 | + |
| 140 | + ctx.stagingUsed = offset + bytesThisUpload; |
| 141 | + r.uploadedLines += linesThisUpload; |
| 142 | + |
| 143 | + if (r.uploadedLines < h) |
| 144 | + return false; |
| 145 | + |
| 146 | + // Done |
| 147 | + etna::set_state( |
| 148 | + ctx.cmdBuf, |
| 149 | + r.dst.get(), |
| 150 | + {}, |
| 151 | + {}, |
| 152 | + vk::ImageLayout::eShaderReadOnlyOptimal, |
| 153 | + r.dst.getAspectMaskByFormat()); |
| 154 | + etna::flush_barriers(ctx.cmdBuf); |
| 155 | + |
| 156 | + r.returnDstCb(std::move(r.dst)); |
| 157 | + return true; |
| 158 | +} |
| 159 | + |
| 160 | +bool PerFrameTransferHelper::serveBufferUploadRequest(UpdateContext &ctx, BufferUploadRequest &r) |
| 161 | +{ |
| 162 | + (void)ctx, (void)r; |
| 163 | + ETNA_ASSERTF(0, "Not implemented (PerFrameTransferHelper::serveBufferUploadRequest)"); |
| 164 | + return false; |
| 165 | +} |
| 166 | + |
| 167 | +} // namespace etna |
0 commit comments