c++ - 如何在不更改数据的情况下将 3D VkImage 传递给计算着色器?

标签 c++ glsl shader vulkan

我正在尝试将体素的随机 3D 图像传递给计算着色器,但是当我运行着色器时,整个着色器的结果如下: Compute Shader Output

如您所见,除了第一个和第二个体素的一半之外,这看起来一点也不像随机生成的体素。老实说,我完全不知道数据发生了什么。我知道我的计算着色器输出到交换链图像不是问题,因为我检查了其他计算着色器(比如噪声屏幕等)是否可以工作,它们确实可以工作。我已将错误的位置缩小到从我的 std::vector<std::vector<std::vector<glm::vec4>>> 复制数据。到 voxelImage 或者它可能在voxelImage的传递中到计算着色器。

我还检查了体素的生成不仅仅是生成屏幕上看到的内容。 vec4s 的 vector 确实是随机的,我保证错误出现在我缩小到的两个地方。

我只会发布 voxelImage 的代码和 voxelImageView创建,以及可能发生错误的描述符。 (发布其余代码会不必要地过多,而且会太长)

体素图像创建:

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);

    }

体素 ImageView 创建:

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;

定义 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!");
        }
    }

定义 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);
    }

定义 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, &region);

    endSingleTimeCommands(commandBuffer);
}

最佳答案

如果voxelDataInit truly 是 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 的字节不会复制数组本身。

另外,sizevector只是元素的数量,而不是数组中字节的数量。

复制这样的数据结构的最好方法就是停止使用这样的数据结构。如果您想要一个 3D 数组,那么您需要的是一个大小为长度宽度高度的一维数组。您可以通过使用长度、宽度、高度将 3D 坐标转换为 1D 坐标来索引数组的任何特定 X、Y、Z 组件。

关于c++ - 如何在不更改数据的情况下将 3D VkImage 传递给计算着色器?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68464085/

相关文章:

iphone - 将变量传递给 OpenGL GLSL 着色器

c++ - glsl 1.2 + 绘图元素

c++ - 直通几何着色器

c++ - 为什么当我更改函数返回类型时,C++ 代码的输出会发生变化?

c++ - 对应该在 C++ 中保存结构的缓冲区使用右对齐

c++ - Visual Studio 中的混合语言

C++如何使用动态库(.so文件)

opengl - 具有深度立方体贴图的全向阴影贴图

javascript - Openlayers 访问低级 WebGL 着色器

opencv - 从Leap运动摄像机接收到的原始图像不失真