1

EDIT2: I found the error, the code that creates the buffer was overwriting one of the storage buffers with one of the uniform buffers that I create afterwards because of a copy paste error.

So I'm currently trying to adapt the Ray Tracing Weekend project (https://raytracing.github.io/) from a CPU program into a compute shader using Vulkan. I'm writing the compute shader using GLSL which is compiled to SPIRV.

I send the scene in the form of a struct containing arrays of structs to the GPU as a storage buffer which looks like this on the CPU (world_gpu being the storage buffer):

struct sphere_gpu
{
    point3 centre;
    float radius;
};

struct material_gpu
{
    vec3 albedo;
    float refraction_index;
    float fuzz;
    uint32_t material_type;
};

struct world_gpu
{
    sphere_gpu spheres[484];
    material_gpu materials[484];
    uint32_t size;
};

and this on the GPU:

// Struct definitions to mirror the CPU representation
struct sphere{
    vec4 centre;
    float radius;
};

struct material{
    vec4 albedo;
    float refraction_index;
    float fuzz;
    uint material_type;
};

// Input scene
layout(std430, binding = 0) buffer world{
    sphere[MAX_SPHERES] spheres;
    material[MAX_SPHERES] materials;
    uint size;
} wrld;

I've already fixed the alignment problem for vec3 on the CPU side by using alignas(16) for my vec3 type: class alignas (16) vec3, and changing the type on the GPU representation to be vec4s as shown above to match the alignment of the data I'm sending over

However, whilst testing this I only seem to be able to read 0s for the spheres when I inspect the data after the compute shader has finished running (I've hijacked my output pixel array in the shader which I write debug data to so that I can read it and debug certain things).

Is there anything obviously stupid that I'm doing here, aside from being a Vulkan noob in general?

EDIT: Here's my buffer uploading code. set_manual_buffer_data is where the data is actually copied to the buffer, create_manual_buffer is where the buffer and memory itself are created.

    template <typename T>
    void set_manual_buffer_data(vk::Device device, vk::Buffer& buffer, vk::DeviceMemory& buffer_memory, T* elements, uint32_t num_elements,uint32_t element_size)
    {
        uint32_t size = element_size * num_elements;
        // Get a pointer to the device memory
        void* buffer_ptr = device.mapMemory(buffer_memory, 0, element_size * num_elements);
        // Copy data to buffer
        memcpy(buffer_ptr, elements, element_size * num_elements);

        device.unmapMemory(buffer_memory);
    }

    // call with physical_device.getMemoryProperties() for second argument
    void create_manual_buffer(vk::Device device, vk::PhysicalDeviceMemoryProperties memory_properties, uint32_t queue_family_index, const uint32_t buffer_size, vk::BufferUsageFlagBits buffer_usage, vk::Buffer& buffer, vk::DeviceMemory& buffer_memory)
    {
        vk::BufferCreateInfo buffer_create_info{};
        buffer_create_info.flags = vk::BufferCreateFlags();
        buffer_create_info.size = buffer_size;
        buffer_create_info.usage = buffer_usage; // Play with this
        buffer_create_info.sharingMode = vk::SharingMode::eExclusive; //concurrent or exclusive
        buffer_create_info.pQueueFamilyIndices = &queue_family_index;
        buffer_create_info.queueFamilyIndexCount = 1;


        buffer = device.createBuffer(buffer_create_info);

        vk::MemoryRequirements memory_requirements = device.getBufferMemoryRequirements(buffer);

        uint32_t memory_type_index = static_cast<uint32_t>(~0);
        vk::DeviceSize memory_heap_size = static_cast<uint32_t>(~0);

        for (uint32_t current_memory_type_index = 0; current_memory_type_index < memory_properties.memoryTypeCount; ++current_memory_type_index)
        {
            // search for desired memory type from the device memory 
            vk::MemoryType MemoryType = memory_properties.memoryTypes[current_memory_type_index];
            if ((vk::MemoryPropertyFlagBits::eHostVisible & MemoryType.propertyFlags) &&
                (vk::MemoryPropertyFlagBits::eHostCoherent & MemoryType.propertyFlags))
            {
                memory_heap_size = memory_properties.memoryHeaps[MemoryType.heapIndex].size;
                memory_type_index = current_memory_type_index;
                break;
            }
        }

        // Create device memory
        vk::MemoryAllocateInfo buffer_allocate_info(memory_requirements.size, memory_type_index);
        buffer_memory = device.allocateMemory(buffer_allocate_info);

        device.bindBufferMemory(buffer, buffer_memory, 0);
    }

This code is then called here (I haven't got to the refactoring stage yet so please forgive the spaghetti):

    std::vector<vk::Buffer> uniform_buffers;
    std::vector<vk::DeviceMemory> uniform_buffers_memory;
    std::vector<vk::Buffer> storage_buffers;
    std::vector<vk::DeviceMemory> storage_buffers_memory;
    
    
    void run_compute(Vulkan_Wrapper &vulkan, Vulkan_Compute &compute, world_gpu *world, color* image, uint32_t image_size, image_info img_info, camera_gpu camera_gpu)
    {
        vulkan.init();
    
        uniform_buffers.resize(2);
        uniform_buffers_memory.resize(2);
        storage_buffers.resize(2);
        storage_buffers_memory.resize(2);
        
        vulkan.create_manual_buffer(vulkan.m_device, vulkan.m_physical_device.getMemoryProperties(),
            vulkan.m_queue_family_index, sizeof(world_gpu),
            vk::BufferUsageFlagBits::eStorageBuffer, storage_buffers[0],
            storage_buffers_memory[0]);
        vulkan.create_manual_buffer(vulkan.m_device, vulkan.m_physical_device.getMemoryProperties(),
            vulkan.m_queue_family_index, image_size * sizeof(color),
            vk::BufferUsageFlagBits::eStorageBuffer, storage_buffers[1],
            storage_buffers_memory[1]);
    
        vulkan.set_manual_buffer_data(vulkan.m_device, storage_buffers[0], storage_buffers_memory[0], world, 1, sizeof(world_gpu));
        vulkan.set_manual_buffer_data(vulkan.m_device, storage_buffers[1], storage_buffers_memory[1], image, image_size, sizeof(color));
    
        vulkan.create_manual_buffer(vulkan.m_device, vulkan.m_physical_device.getMemoryProperties(),
            vulkan.m_queue_family_index, sizeof(image_info),
            vk::BufferUsageFlagBits::eUniformBuffer, storage_buffers[0],
            uniform_buffers_memory[0]);
        vulkan.create_manual_buffer(vulkan.m_device, vulkan.m_physical_device.getMemoryProperties(),
            vulkan.m_queue_family_index, sizeof(camera_gpu),
            vk::BufferUsageFlagBits::eUniformBuffer, uniform_buffers[1],
            uniform_buffers_memory[1]);
    
        vulkan.set_manual_buffer_data(vulkan.m_device, uniform_buffers[0], uniform_buffers_memory[0], &img_info, 1, sizeof(img_info));
        vulkan.set_manual_buffer_data(vulkan.m_device, uniform_buffers[1], uniform_buffers_memory[1], &camera_gpu, 1, sizeof(camera_gpu));
// Run pipeline etc

I should note that it works perfectly fine when I check the values stored in the image storage buffer (storage_buffers_memory[1]), it's the other 3 that is giving me difficulties

ZTaylor97
  • 11
  • 2
  • 1
    Looks alright to me. If you're getting zeros for everything then I suspect your problem rather lies in the upload code instead of the buffer specification. Could you share that part? – IGarFieldI Oct 08 '22 at 09:11
  • @IGarFieldI I've made an edit showing the buffer uploading code, I can't seem to find the problem here that makes it any different from the image storage buffer which works fine – ZTaylor97 Oct 13 '22 at 00:46
  • @ZTaylor97: "*Struct definitions to mirror the CPU representation*" Wrong. – Nicol Bolas Oct 13 '22 at 01:16
  • @NicolBolas Your comment was extremely helpful, thankyou – ZTaylor97 Oct 30 '22 at 03:18

0 Answers0