host image copyless host image copy

This commit is contained in:
PancakeTAS 2025-06-30 01:58:36 +02:00
parent bdeae9b8d0
commit 10ac493312
No known key found for this signature in database
5 changed files with 112 additions and 56 deletions

View file

@ -33,6 +33,21 @@ namespace Vulkan::Core {
construct(device, reinterpret_cast<const void*>(&data), usage);
}
///
/// Create the buffer.
///
/// @param device Vulkan device
/// @param data Initial data for the buffer
/// @param size Size of the buffer in bytes
/// @param usage Usage flags for the buffer
///
/// @throws ls::vulkan_error if object creation fails.
///
Buffer(const Device& device, const void* data, size_t size, VkBufferUsageFlags usage)
: size(size) {
construct(device, data, usage);
}
/// Get the Vulkan handle.
[[nodiscard]] auto handle() const { return *this->buffer; }
/// Get the size of the buffer.

View file

@ -1,6 +1,7 @@
#ifndef UPLOAD_HPP
#define UPLOAD_HPP
#include "core/commandpool.hpp"
#include "core/image.hpp"
#include "device.hpp"
@ -8,8 +9,19 @@
namespace Upload {
void upload(const Vulkan::Device& device,
Vulkan::Core::Image& image, const std::string& path);
///
/// Upload a DDS file to a Vulkan image.
///
/// @param device The Vulkan device
/// @param commandPool The command pool
/// @param image The Vulkan image to upload to
/// @param path The path to the DDS file.
///
/// @throws std::system_error If the file cannot be opened or read.
/// @throws ls:vulkan_error If the Vulkan image cannot be created or updated.
///
void upload(const Vulkan::Device& device, const Vulkan::Core::CommandPool& commandPool,
Vulkan::Core::Image& image, const std::string& path);
}

View file

@ -7,7 +7,7 @@
using namespace Vulkan;
const std::vector<const char*> requiredExtensions = {
"VK_EXT_host_image_copy"
"VK_KHR_external_memory_fd"
};
Device::Device(const Instance& instance) {
@ -53,13 +53,8 @@ Device::Device(const Instance& instance) {
// create logical device
const float queuePriority{1.0F}; // highest priority
VkPhysicalDeviceHostImageCopyFeaturesEXT hostImageCopyFeatures{
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_IMAGE_COPY_FEATURES_EXT,
.hostImageCopy = VK_TRUE
};
VkPhysicalDeviceVulkan13Features features13{
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES,
.pNext = &hostImageCopyFeatures,
.synchronization2 = VK_TRUE
};
const VkPhysicalDeviceVulkan12Features features12{

View file

@ -11,9 +11,28 @@
#include <iostream>
#include <renderdoc_app.h>
#include <dlfcn.h>
#include <unistd.h>
#include <vulkan/vulkan_core.h>
using namespace Vulkan;
int main() {
// attempt to load renderdoc
RENDERDOC_API_1_6_0* rdoc = nullptr;
if (void* mod_renderdoc = dlopen("/usr/lib/librenderdoc.so", RTLD_NOLOAD | RTLD_NOW)) {
std::cerr << "Found RenderDoc library, setting up frame capture." << '\n';
auto GetAPI = reinterpret_cast<pRENDERDOC_GetAPI>(dlsym(mod_renderdoc, "RENDERDOC_GetAPI"));
const int ret = GetAPI(eRENDERDOC_API_Version_1_6_0, reinterpret_cast<void**>(&rdoc));
if (ret == 0) {
std::cerr << "Unable to initialize RenderDoc API. Is your RenderDoc up to date?" << '\n';
rdoc = nullptr;
}
usleep(1000 * 100); // give renderdoc time to load
}
// initialize application
const Instance instance;
const Device device(instance);
@ -26,16 +45,18 @@ int main() {
// create initialization resources
Core::Image inputImage(
device, { 2560, 1411 }, VK_FORMAT_R8G8B8A8_UNORM,
VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT
| VK_IMAGE_USAGE_HOST_TRANSFER_BIT, // (remove in prod)
VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT,
VK_IMAGE_ASPECT_COLOR_BIT
);
Upload::upload(device, inputImage, "rsc/images/source.dds");
Upload::upload(device, commandPool, inputImage, "rsc/images/source.dds");
// create the shaderchains
Shaderchains::Downsample downsample(device, descriptorPool, inputImage);
// start the rendering pipeline
if (rdoc)
rdoc->StartFrameCapture(nullptr, nullptr);
Core::CommandBuffer commandBuffer(device, commandPool);
commandBuffer.begin();
@ -52,6 +73,11 @@ int main() {
return 1;
}
if (rdoc)
rdoc->EndFrameCapture(nullptr, nullptr);
usleep(1000 * 100); // give renderdoc time to capture
Globals::uninitializeGlobals();
std::cerr << "Application finished" << '\n';

View file

@ -1,4 +1,6 @@
#include "utils/upload.hpp"
#include "core/buffer.hpp"
#include "core/commandbuffer.hpp"
#include "utils/exceptions.hpp"
#include <fstream>
@ -6,34 +8,14 @@
#include <vulkan/vulkan_core.h>
using namespace Upload;
using namespace Vulkan;
void Upload::upload(const Vulkan::Device& device,
Vulkan::Core::Image& image, const std::string& path) {
auto vkTransitionImageLayoutEXT = reinterpret_cast<PFN_vkTransitionImageLayoutEXT>(
vkGetDeviceProcAddr(device.handle(), "vkTransitionImageLayoutEXT"));
auto vkCopyMemoryToImageEXT = reinterpret_cast<PFN_vkCopyMemoryToImageEXT>(
vkGetDeviceProcAddr(device.handle(), "vkCopyMemoryToImageEXT"));
const VkHostImageLayoutTransitionInfoEXT transitionInfo{
.sType = VK_STRUCTURE_TYPE_HOST_IMAGE_LAYOUT_TRANSITION_INFO_EXT,
.image = image.handle(),
.oldLayout = image.getLayout(),
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
.subresourceRange = {
.aspectMask = image.getAspectFlags(),
.levelCount = 1,
.layerCount = 1
}
};
image.setLayout(VK_IMAGE_LAYOUT_GENERAL);
auto res = vkTransitionImageLayoutEXT(device.handle(), 1, &transitionInfo);
if (res != VK_SUCCESS)
throw ls::vulkan_error(res, "Failed to transition image layout for upload");
// read shader bytecode
void Upload::upload(const Device& device, const Core::CommandPool& commandPool,
Core::Image& image, const std::string& path) {
// read iamge bytecode
std::ifstream file(path, std::ios::ate | std::ios::binary);
if (!file)
throw std::system_error(errno, std::generic_category(), "Failed to open shader file: " + path);
throw std::system_error(errno, std::generic_category(), "Failed to open image: " + path);
std::streamsize size = file.tellg();
size -= 124 - 4;
@ -41,34 +23,60 @@ void Upload::upload(const Vulkan::Device& device,
file.seekg(0, std::ios::beg);
if (!file.read(reinterpret_cast<char*>(code.data()), size))
throw std::system_error(errno, std::generic_category(), "Failed to read shader file: " + path);
throw std::system_error(errno, std::generic_category(), "Failed to read image: " + path);
file.close();
// copy data to image
// copy data to buffer
const Vulkan::Core::Buffer stagingBuffer(
device, code.data(), static_cast<uint32_t>(code.size()),
VK_BUFFER_USAGE_TRANSFER_SRC_BIT
);
// perform the upload
Core::CommandBuffer commandBuffer(device, commandPool);
commandBuffer.begin();
const VkImageMemoryBarrier barrier{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.srcAccessMask = VK_ACCESS_NONE,
.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
.oldLayout = image.getLayout(),
.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
.image = image.handle(),
.subresourceRange = {
.aspectMask = image.getAspectFlags(),
.levelCount = 1,
.layerCount = 1
}
};
image.setLayout(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
vkCmdPipelineBarrier(
commandBuffer.handle(),
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
0, 0, nullptr, 0, nullptr, 1, &barrier
);
auto extent = image.getExtent();
const VkMemoryToImageCopyEXT copyInfo{
.sType = VK_STRUCTURE_TYPE_MEMORY_TO_IMAGE_COPY_EXT,
.pHostPointer = code.data(),
const VkBufferImageCopy region{
.bufferImageHeight = 0,
.imageSubresource = {
.aspectMask = image.getAspectFlags(),
.layerCount = 1
},
.imageExtent = {
.width = extent.width,
.height = extent.height,
.depth = 1
},
.imageExtent = { extent.width, extent.height, 1 }
};
vkCmdCopyBufferToImage(
commandBuffer.handle(),
stagingBuffer.handle(), image.handle(),
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region
);
const VkCopyMemoryToImageInfoEXT operationInfo{
.sType = VK_STRUCTURE_TYPE_COPY_MEMORY_TO_IMAGE_INFO_EXT,
.dstImage = image.handle(),
.dstImageLayout = image.getLayout(),
.regionCount = 1,
.pRegions = &copyInfo,
};
res = vkCopyMemoryToImageEXT(device.handle(), &operationInfo);
if (res != VK_SUCCESS)
throw ls::vulkan_error(res, "Failed to copy memory to image");
commandBuffer.end();
Core::Fence fence(device);
commandBuffer.submit(device.getComputeQueue(), fence);
if (!fence.wait(device))
throw ls::vulkan_error(VK_TIMEOUT, "Upload operation timed out");
}