如何在不更改数据的情况下将 3D VkImage 传递给计算着色器?
How do I pass a 3D VkImage to a compute shader without the data changing?
我正在尝试将体素的随机 3D 图像传递给计算着色器,但是当我 运行 着色器时,整个着色器的结果如下:
如您所见,除了第一个和第二个体素的一半之外,这看起来一点也不像随机生成的体素。老实说,我完全不知道数据发生了什么。我知道我的计算着色器输出到交换链图像不是问题,因为我检查了其他计算着色器(比如噪声屏幕等)是否可以工作,它们确实可以工作。我已经将错误的位置缩小到将数据从我的 std::vector<std::vector<std::vector<glm::vec4>>>
复制到 voxelImage
或 它可能在 voxelImage
到计算着色器。
我还检查了体素的生成不仅仅是生成屏幕上看到的内容。 vec4s 的向量确实是随机的,我保证错误在我缩小到的两个地方中的任何一个。
我只会 post 创建 voxelImage
和 voxelImageView
的代码,以及可能出现错误的描述符。 (post剩下的代码会不必要地过多,而且会太长)
体素图像创建:
void createVoxelImage() {
VkDeviceSize imageSize = voxelDataInit.size();
VkBuffer stagingBuffer;
VkDeviceMemory stagingBufferMemory;
VmaAllocation stagingAllocation;
createBuffer(imageSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VMA_MEMORY_USAGE_GPU_ONLY, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, stagingBuffer, stagingAllocation, stagingBufferMemory);
void* data;
vmaMapMemory(allocator, stagingAllocation, &data);
memcpy(data, &voxelDataInit, imageSize);
vmaUnmapMemory(allocator, stagingAllocation);
VkDeviceMemory temp;
createImage(voxWidth, voxHeight, voxDepth, VK_IMAGE_TYPE_3D, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_STORAGE_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, voxelImage, voxelAllocation, VMA_MEMORY_USAGE_GPU_ONLY, temp, 5);
//vkFreeMemory(device, temp, NULL);
//VkDeviceMemory temp;
//createImage(voxWidth, voxHeight, voxDepth, VK_IMAGE_TYPE_3D, VK_FORMAT_B8G8R8_UNORM, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_STORAGE_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, voxelImage, voxelAllocation, VMA_MEMORY_USAGE_CPU_TO_GPU, temp, 5);
transitionImageLayout(voxelImage, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_ACCESS_TRANSFER_READ_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
copyBufferToImage(stagingBuffer, voxelImage, static_cast<uint32_t>(voxWidth), static_cast<uint32_t>(voxHeight), static_cast<uint32_t>(voxDepth));
transitionImageLayout(voxelImage, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT);
vmaDestroyBuffer(allocator, stagingBuffer, stagingAllocation);
}
体素图像视图创建:
void createVoxelImageView() {
VkImageViewCreateInfo viewInfo{};
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
viewInfo.image = voxelImage;
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_3D;
viewInfo.format = VK_FORMAT_R8G8B8A8_SRGB;
//viewInfo.flags = VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT;
viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
viewInfo.subresourceRange.baseMipLevel = 0;
viewInfo.subresourceRange.levelCount = 1;
viewInfo.subresourceRange.baseArrayLayer = 0;
viewInfo.subresourceRange.layerCount = 1;
if (vkCreateImageView(device, &viewInfo, nullptr, &voxelImageView) != VK_SUCCESS) {
throw std::runtime_error("Failed to create image view! (voxel)");
}
}
数据输入着色器
layout(binding = 4, rgba8) uniform image3D voxels;
Def of createImage
:
void createImage(uint32_t width, uint32_t height, uint32_t depth, VkImageType imgType, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkImageLayout layout, VkMemoryPropertyFlags properties, VkImage& image, VmaAllocation& allocation, VmaMemoryUsage memUsage, VkDeviceMemory& imageMemory, int callNum) {
VkImageCreateInfo imageInfo{};
imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
imageInfo.imageType = imgType;
imageInfo.extent.width = width;
imageInfo.extent.height = height;
imageInfo.extent.depth = depth;
imageInfo.mipLevels = 1;
imageInfo.arrayLayers = 1;
imageInfo.format = format;
imageInfo.tiling = tiling;
imageInfo.initialLayout = layout;
imageInfo.usage = usage;
imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
imageInfo.flags = VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT;
VmaAllocationCreateInfo vmaAllocInfo = {};
vmaAllocInfo.usage = memUsage;
vmaAllocInfo.requiredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
if (vmaCreateImage(allocator, &imageInfo, &vmaAllocInfo, &image, &allocation, nullptr) != VK_SUCCESS) {
throw std::runtime_error(std::to_string(callNum));
throw std::runtime_error("failed to create image!");
}
}
Def of transitionImageLayout
:
void transitionImageLayout(VkImage image, VkFormat format, VkImageLayout oldLayout, VkImageLayout newLayout, VkAccessFlagBits srcAccess, VkAccessFlagBits dstAccess, VkPipelineStageFlagBits srcStage, VkPipelineStageFlagBits dstStage) {
VkCommandBuffer commandBuffer = beginSingleTimeCommands();
VkImageMemoryBarrier barrier{};
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
barrier.oldLayout = oldLayout;
barrier.newLayout = newLayout;
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.image = image;
barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
barrier.subresourceRange.baseMipLevel = 0;
barrier.subresourceRange.levelCount = 1;
barrier.subresourceRange.baseArrayLayer = 0;
barrier.subresourceRange.layerCount = 1;
VkPipelineStageFlags sourceStage;
VkPipelineStageFlags destinationStage;
if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) {
barrier.srcAccessMask = 0;
barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
}
else {
if (srcStage == VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT) {
barrier.srcAccessMask = 0;
}
else {
barrier.srcAccessMask = srcAccess;
}
barrier.dstAccessMask = dstAccess;
sourceStage = srcStage;
destinationStage = dstStage;
//throw std::runtime_error("Unsupported layout transition.");
}
vkCmdPipelineBarrier(commandBuffer, sourceStage, destinationStage, 0, 0, nullptr, 0, nullptr, 1, &barrier);
endSingleTimeCommands(commandBuffer);
}
Def of copyBufferToImage
:
void copyBufferToImage(VkBuffer buffer, VkImage image, uint32_t width, uint32_t height, uint32_t depth){
VkCommandBuffer commandBuffer = beginSingleTimeCommands();
VkBufferImageCopy region{};
region.bufferOffset = 0;
region.bufferRowLength = 0;
region.bufferImageHeight = 0;
region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region.imageSubresource.mipLevel = 0;
region.imageSubresource.baseArrayLayer = 0;
region.imageSubresource.layerCount = 1;
region.imageOffset = { 0, 0, 0 };
region.imageExtent = {
width,
height,
depth
};
vkCmdCopyBufferToImage(commandBuffer, buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion);
endSingleTimeCommands(commandBuffer);
}
如果 voxelDataInit
确实是 std::vector<std::vector<std::vector<glm::vec4>>>
类型的变量,那么 memcpy(data, &voxelDataInit, imageSize);
永远不会起作用。 &voxelDataInit
是指向 vector
的指针。并且指向 vector<T>
s 的指针总是相同的大小(忽略分配器本身):3 个指针的大小。
记住:vector<T>
是指向 T
数组的 指针 。或者更确切地说,它是指向该数组的 3 个指针。但无论如何,vector<T>
本身并不是 的 T
数组;它只是 拥有 一个。因此,复制 vector
的字节不会复制数组本身。
另外,vector
的size
只是元素的个数,并不是数组bytes的个数。
复制这种数据结构的最好方法是停止使用这种数据结构。如果你想要一个 3D 数组,那么你想要的是一个大小为长度 宽度 高度的 一维 数组。您可以通过使用长度、宽度、高度将 3D 坐标转换为 1D 坐标来索引数组的任何特定 X、Y、Z 分量。
我正在尝试将体素的随机 3D 图像传递给计算着色器,但是当我 运行 着色器时,整个着色器的结果如下:
如您所见,除了第一个和第二个体素的一半之外,这看起来一点也不像随机生成的体素。老实说,我完全不知道数据发生了什么。我知道我的计算着色器输出到交换链图像不是问题,因为我检查了其他计算着色器(比如噪声屏幕等)是否可以工作,它们确实可以工作。我已经将错误的位置缩小到将数据从我的 std::vector<std::vector<std::vector<glm::vec4>>>
复制到 voxelImage
或 它可能在 voxelImage
到计算着色器。
我还检查了体素的生成不仅仅是生成屏幕上看到的内容。 vec4s 的向量确实是随机的,我保证错误在我缩小到的两个地方中的任何一个。
我只会 post 创建 voxelImage
和 voxelImageView
的代码,以及可能出现错误的描述符。 (post剩下的代码会不必要地过多,而且会太长)
体素图像创建:
void createVoxelImage() {
VkDeviceSize imageSize = voxelDataInit.size();
VkBuffer stagingBuffer;
VkDeviceMemory stagingBufferMemory;
VmaAllocation stagingAllocation;
createBuffer(imageSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VMA_MEMORY_USAGE_GPU_ONLY, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, stagingBuffer, stagingAllocation, stagingBufferMemory);
void* data;
vmaMapMemory(allocator, stagingAllocation, &data);
memcpy(data, &voxelDataInit, imageSize);
vmaUnmapMemory(allocator, stagingAllocation);
VkDeviceMemory temp;
createImage(voxWidth, voxHeight, voxDepth, VK_IMAGE_TYPE_3D, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_STORAGE_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, voxelImage, voxelAllocation, VMA_MEMORY_USAGE_GPU_ONLY, temp, 5);
//vkFreeMemory(device, temp, NULL);
//VkDeviceMemory temp;
//createImage(voxWidth, voxHeight, voxDepth, VK_IMAGE_TYPE_3D, VK_FORMAT_B8G8R8_UNORM, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_STORAGE_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, voxelImage, voxelAllocation, VMA_MEMORY_USAGE_CPU_TO_GPU, temp, 5);
transitionImageLayout(voxelImage, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_ACCESS_TRANSFER_READ_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
copyBufferToImage(stagingBuffer, voxelImage, static_cast<uint32_t>(voxWidth), static_cast<uint32_t>(voxHeight), static_cast<uint32_t>(voxDepth));
transitionImageLayout(voxelImage, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT);
vmaDestroyBuffer(allocator, stagingBuffer, stagingAllocation);
}
体素图像视图创建:
void createVoxelImageView() {
VkImageViewCreateInfo viewInfo{};
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
viewInfo.image = voxelImage;
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_3D;
viewInfo.format = VK_FORMAT_R8G8B8A8_SRGB;
//viewInfo.flags = VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT;
viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
viewInfo.subresourceRange.baseMipLevel = 0;
viewInfo.subresourceRange.levelCount = 1;
viewInfo.subresourceRange.baseArrayLayer = 0;
viewInfo.subresourceRange.layerCount = 1;
if (vkCreateImageView(device, &viewInfo, nullptr, &voxelImageView) != VK_SUCCESS) {
throw std::runtime_error("Failed to create image view! (voxel)");
}
}
数据输入着色器
layout(binding = 4, rgba8) uniform image3D voxels;
Def of createImage
:
void createImage(uint32_t width, uint32_t height, uint32_t depth, VkImageType imgType, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkImageLayout layout, VkMemoryPropertyFlags properties, VkImage& image, VmaAllocation& allocation, VmaMemoryUsage memUsage, VkDeviceMemory& imageMemory, int callNum) {
VkImageCreateInfo imageInfo{};
imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
imageInfo.imageType = imgType;
imageInfo.extent.width = width;
imageInfo.extent.height = height;
imageInfo.extent.depth = depth;
imageInfo.mipLevels = 1;
imageInfo.arrayLayers = 1;
imageInfo.format = format;
imageInfo.tiling = tiling;
imageInfo.initialLayout = layout;
imageInfo.usage = usage;
imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
imageInfo.flags = VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT;
VmaAllocationCreateInfo vmaAllocInfo = {};
vmaAllocInfo.usage = memUsage;
vmaAllocInfo.requiredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
if (vmaCreateImage(allocator, &imageInfo, &vmaAllocInfo, &image, &allocation, nullptr) != VK_SUCCESS) {
throw std::runtime_error(std::to_string(callNum));
throw std::runtime_error("failed to create image!");
}
}
Def of transitionImageLayout
:
void transitionImageLayout(VkImage image, VkFormat format, VkImageLayout oldLayout, VkImageLayout newLayout, VkAccessFlagBits srcAccess, VkAccessFlagBits dstAccess, VkPipelineStageFlagBits srcStage, VkPipelineStageFlagBits dstStage) {
VkCommandBuffer commandBuffer = beginSingleTimeCommands();
VkImageMemoryBarrier barrier{};
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
barrier.oldLayout = oldLayout;
barrier.newLayout = newLayout;
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.image = image;
barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
barrier.subresourceRange.baseMipLevel = 0;
barrier.subresourceRange.levelCount = 1;
barrier.subresourceRange.baseArrayLayer = 0;
barrier.subresourceRange.layerCount = 1;
VkPipelineStageFlags sourceStage;
VkPipelineStageFlags destinationStage;
if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) {
barrier.srcAccessMask = 0;
barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
}
else {
if (srcStage == VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT) {
barrier.srcAccessMask = 0;
}
else {
barrier.srcAccessMask = srcAccess;
}
barrier.dstAccessMask = dstAccess;
sourceStage = srcStage;
destinationStage = dstStage;
//throw std::runtime_error("Unsupported layout transition.");
}
vkCmdPipelineBarrier(commandBuffer, sourceStage, destinationStage, 0, 0, nullptr, 0, nullptr, 1, &barrier);
endSingleTimeCommands(commandBuffer);
}
Def of copyBufferToImage
:
void copyBufferToImage(VkBuffer buffer, VkImage image, uint32_t width, uint32_t height, uint32_t depth){
VkCommandBuffer commandBuffer = beginSingleTimeCommands();
VkBufferImageCopy region{};
region.bufferOffset = 0;
region.bufferRowLength = 0;
region.bufferImageHeight = 0;
region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region.imageSubresource.mipLevel = 0;
region.imageSubresource.baseArrayLayer = 0;
region.imageSubresource.layerCount = 1;
region.imageOffset = { 0, 0, 0 };
region.imageExtent = {
width,
height,
depth
};
vkCmdCopyBufferToImage(commandBuffer, buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion);
endSingleTimeCommands(commandBuffer);
}
如果 voxelDataInit
确实是 std::vector<std::vector<std::vector<glm::vec4>>>
类型的变量,那么 memcpy(data, &voxelDataInit, imageSize);
永远不会起作用。 &voxelDataInit
是指向 vector
的指针。并且指向 vector<T>
s 的指针总是相同的大小(忽略分配器本身):3 个指针的大小。
记住:vector<T>
是指向 T
数组的 指针 。或者更确切地说,它是指向该数组的 3 个指针。但无论如何,vector<T>
本身并不是 的 T
数组;它只是 拥有 一个。因此,复制 vector
的字节不会复制数组本身。
另外,vector
的size
只是元素的个数,并不是数组bytes的个数。
复制这种数据结构的最好方法是停止使用这种数据结构。如果你想要一个 3D 数组,那么你想要的是一个大小为长度 宽度 高度的 一维 数组。您可以通过使用长度、宽度、高度将 3D 坐标转换为 1D 坐标来索引数组的任何特定 X、Y、Z 分量。