繁体   English   中英

Vulkan RT BLAS 构建有时会失败。 没有验证层消息

[英]Vulkan RT BLAS build sometimes fail. No Validation Layer message

我在使用光线追踪在 Vulkan 中创建 BLAS 时遇到问题。 基本上,并非总是如此,但通常当我通过计算队列中的 commandBuffer 发送命令“vkCmdBuildAccelerationStructuresKHR”时,VkDevice 变为 VK_ERROR_DEVICE_LOST。 vkQueueSubmit 返回 VK_SUCCESS,但是当我尝试等待发送的命令完成时,vkDeviceWaitIdle 返回 VK_ERROR_DEVICE_LOST。 所有使用的缓冲区均已正确分配,并且可以获得设备上的地址。 我还使用 VMA(Vulkan 内存管理)库来管理分配。 缓冲区是使用属性 VK_SHARING_MODE_EXCLUSIVE 创建的,但仅在计算队列的 commandBuffer 中使用。 真正的问题是验证层没有给出任何错误消息。

创建顶点缓冲区的代码:

VkBufferCreateInfo vkVertexBufferCreateInfo{};
    vkVertexBufferCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
    vkVertexBufferCreateInfo.size = vertexSize;
    vkVertexBufferCreateInfo.usage = VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR
        | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT
        | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
    vkVertexBufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
    vkVertexBufferCreateInfo.pQueueFamilyIndices = VK_NULL_HANDLE;
    vkVertexBufferCreateInfo.queueFamilyIndexCount = 0;

    VmaAllocationCreateInfo vmaVertexBufferAllocationCreateInfo{};
    vmaVertexBufferAllocationCreateInfo.flags = VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT;
    vmaVertexBufferAllocationCreateInfo.requiredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;

创建索引缓冲区的代码:

    VkBufferCreateInfo vkIndexBufferCreateInfo{};
    vkIndexBufferCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
    vkIndexBufferCreateInfo.size = faceSize;
    vkIndexBufferCreateInfo.usage = VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR
        | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT
        | VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
    vkIndexBufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
    vkIndexBufferCreateInfo.pQueueFamilyIndices = VK_NULL_HANDLE;
    vkIndexBufferCreateInfo.queueFamilyIndexCount = 0;

    
    VmaAllocationCreateInfo vmaIndexBufferAllocationCreateInfo = {};
    vmaIndexBufferAllocationCreateInfo.flags = VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT;
    vmaIndexBufferAllocationCreateInfo.requiredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;

为几何信息创建结构的代码:

 // Query the 64-bit vertex/index buffer device address value through which buffer memory 
    // can be accessed in a shader
    std::optional<VkDeviceAddress> vertexBufferAddress = geometry.getVertexBuffer().getBufferDeviceAddress();
    if (vertexBufferAddress.has_value() == false)
    {
        OV_LOG_ERROR("Fail to retrive the device address of the vertex buffer. Probably geometry not uploaded.");
        return false;
    }
    std::optional<VkDeviceAddress> faceBufferAddress = geometry.getFaceBuffer().getBufferDeviceAddress();
    if (faceBufferAddress.has_value() == false)
    {
        OV_LOG_ERROR("Fail to retrive the device address of the face buffer. Probably geometry not uploaded.");
        return false;
    }

    VkDeviceOrHostAddressConstKHR vertexDeviceOrHostAddressConst = {};
    vertexDeviceOrHostAddressConst.deviceAddress = vertexBufferAddress.value();

    VkDeviceOrHostAddressConstKHR faceDeviceOrHostAddressConst = {};
    faceDeviceOrHostAddressConst.deviceAddress = faceBufferAddress.value();

    // Structure specifying a triangle geometry in a bottom-level acceleration structure
    VkAccelerationStructureGeometryTrianglesDataKHR accelerationStructureGeometryTrianglesData = {};
    accelerationStructureGeometryTrianglesData.sType =
        VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_TRIANGLES_DATA_KHR;
    accelerationStructureGeometryTrianglesData.pNext = NULL;
    // Vertex glm::vec3
    accelerationStructureGeometryTrianglesData.vertexFormat = VK_FORMAT_R32G32B32_SFLOAT;
    accelerationStructureGeometryTrianglesData.vertexData = vertexDeviceOrHostAddressConst;
    // sizeof(float) * 3 => vertex
    // sizeof(uint32_t) * 3 => normal / uv / tangent
    accelerationStructureGeometryTrianglesData.vertexStride = sizeof(Ov::Geometry::VertexData);
    // # vertices = vertex buffer size bytes / vertex stride
    accelerationStructureGeometryTrianglesData.maxVertex = geometry.getNrOfVertices();
    accelerationStructureGeometryTrianglesData.indexType = VK_INDEX_TYPE_UINT32;
    accelerationStructureGeometryTrianglesData.indexData = faceDeviceOrHostAddressConst;
    // transformData is a device or host address to memory containing an optional reference to
    // a VkTransformMatrixKHR structure
    accelerationStructureGeometryTrianglesData.transformData = transformData;

    // Union specifying acceleration structure geometry data
    VkAccelerationStructureGeometryDataKHR accelerationStructureGeometryData = {};
    accelerationStructureGeometryData.triangles = accelerationStructureGeometryTrianglesData;

    // Structure specifying geometries to be built into an acceleration structure
    VkAccelerationStructureGeometryKHR& accelerationStructureGeometry = reserved->geometriesAS.emplace_back();
    accelerationStructureGeometry.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_KHR;
    accelerationStructureGeometry.pNext = NULL;
    accelerationStructureGeometry.geometryType = VK_GEOMETRY_TYPE_TRIANGLES_KHR;
    accelerationStructureGeometry.geometry = accelerationStructureGeometryData;
    accelerationStructureGeometry.flags = geometryFlags;

    // Structure specifying build offsets and counts for acceleration structure builds
    VkAccelerationStructureBuildRangeInfoKHR& accelerationStructureBuildRangeInfoKHR = reserved->geometriesBuildRangeAS.emplace_back();
    // primitiveCount defines the number of primitives for a corresponding acceleration structure geometry.
    accelerationStructureBuildRangeInfoKHR.primitiveCount = geometry.getNrOfFaces();
    accelerationStructureBuildRangeInfoKHR.primitiveOffset = 0;
    accelerationStructureBuildRangeInfoKHR.firstVertex = 0;
    accelerationStructureBuildRangeInfoKHR.transformOffset = 0;

这是构建 BLAS 的代码:

    // Structure specifying the geometry data used to build an acceleration structure.
    reserved->accelerationStructureBuildGeometryInfo.sType =
        VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_GEOMETRY_INFO_KHR;
    reserved->accelerationStructureBuildGeometryInfo.pNext = NULL;
    reserved->accelerationStructureBuildGeometryInfo.type = type;
    reserved->accelerationStructureBuildGeometryInfo.flags = flags;
    // VK_BUILD_ACCELERATION_STRUCTURE_MODE_BUILD_KHR => specifies that the destination acceleration
    //      structure will be built using the specified geometries.
    reserved->accelerationStructureBuildGeometryInfo.mode = VK_BUILD_ACCELERATION_STRUCTURE_MODE_BUILD_KHR;
    reserved->accelerationStructureBuildGeometryInfo.srcAccelerationStructure = VK_NULL_HANDLE;
    reserved->accelerationStructureBuildGeometryInfo.dstAccelerationStructure = VK_NULL_HANDLE;
    reserved->accelerationStructureBuildGeometryInfo.geometryCount = nrOfgeometriesStructuresAS;
    // The index of each element of the pGeometries or ppGeometries members of VkAccelerationStructureBuildGeometryInfoKHR
    // is used as the geometry index during ray traversal.The geometry index is available in ray shaders via the
    // RayGeometryIndexKHR built - in, and is used to determine hitand intersection shaders executed 
    // during traversal.The geometry index is available to ray queries via the OpRayQueryGetIntersectionGeometryIndexKHR instruction.
    reserved->accelerationStructureBuildGeometryInfo.pGeometries = geometriesStructuresAS.data();
    reserved->accelerationStructureBuildGeometryInfo.ppGeometries = NULL;
    reserved->accelerationStructureBuildGeometryInfo.scratchData = {};

    // Structure specifying build sizes for an acceleration structure
    VkAccelerationStructureBuildSizesInfoKHR accelerationStructureBuildSizesInfo = {};
    accelerationStructureBuildSizesInfo.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_SIZES_INFO_KHR;
    accelerationStructureBuildSizesInfo.pNext = NULL;
    accelerationStructureBuildSizesInfo.accelerationStructureSize = 0;
    accelerationStructureBuildSizesInfo.updateScratchSize = 0;
    accelerationStructureBuildSizesInfo.buildScratchSize = 0;

    // Retrieve the required size for an acceleration structure
    // VK_ACCELERATION_STRUCTURE_BUILD_TYPE_DEVICE_KHR => requests the memory requirement for operations
    // performed by the device.
    PFN_vkGetAccelerationStructureBuildSizesKHR pvkGetAccelerationStructureBuildSizesKHR =
        (PFN_vkGetAccelerationStructureBuildSizesKHR)vkGetDeviceProcAddr(logicalDevice.get().getVkDevice(), "vkGetAccelerationStructureBuildSizesKHR");

    pvkGetAccelerationStructureBuildSizesKHR(logicalDevice.get().getVkDevice(),
        VK_ACCELERATION_STRUCTURE_BUILD_TYPE_HOST_KHR,
        &reserved->accelerationStructureBuildGeometryInfo,
        &reserved->accelerationStructureBuildGeometryInfo.geometryCount,
        &accelerationStructureBuildSizesInfo);


    ////////////////////
    // Scratch buffer //
    ////////////////////

#pragma region ScratchBuffer 

    ///////////////////////////
    // Create scratch buffer //
    ///////////////////////////

    // Create info buffer
    VkBufferCreateInfo vkScratchBufferCreateInfo{};
    vkScratchBufferCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
    // The second field of the struct is size, which specifies the size of the buffer in bytes
    vkScratchBufferCreateInfo.size = accelerationStructureBuildSizesInfo.buildScratchSize;
    // The third field is usage, which indicates for which purposes the data in the buffer 
    // is going to be used. It is possible to specify multiple purposes using a bitwise or.
    // VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR => specifies that the buffer is suitable for
    //      use as a read-only input to an acceleration structure build.
    // VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT => specifies that the buffer can be used to retrieve a buffer device address
    //      via vkGetBufferDeviceAddress and use that address to access the buffer’s memory from a shader.
    vkScratchBufferCreateInfo.usage = VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR
        | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
    // buffers can also be owned by a specific queue family or be shared between multiple 
    // at the same time. 
    // VK_SHARING_MODE_CONCURRENT specifies that concurrent access to any range or image subresource of the object
    // from multiple queue families is supported.
    vkScratchBufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
    // From which queue family the buffer will be accessed.
    vkScratchBufferCreateInfo.pQueueFamilyIndices = NULL;
    vkScratchBufferCreateInfo.queueFamilyIndexCount = 0;

    // Create allocation info
    VmaAllocationCreateInfo vmaScratchBufferAllocationCreateInfo = {};
    // VMA_ALLOCATION_CREATE_MAPPED_BIT => Set this flag to use a memory that will be persistently 
    // mappedand retrieve pointer to it. It is valid to use this flag for allocation made from memory
    // type that is not HOST_VISIBLE. This flag is then ignored and memory is not mapped. This is useful
    // if you need an allocation that is efficient to use on GPU (DEVICE_LOCAL) and still want to map it
    // directly if possible on platforms that support it (e.g. Intel GPU).
    vmaScratchBufferAllocationCreateInfo.flags = VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT;
    // Flags that must be set in a Memory Type chosen for an allocation. 
    vmaScratchBufferAllocationCreateInfo.requiredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;

    if (!reserved->scratchBuffer.create(vkScratchBufferCreateInfo, vmaScratchBufferAllocationCreateInfo))
    {
        OV_LOG_ERROR("Fail creation scrath buffer for BLAS.");
        this->free();
        return false;
    }


    ////////////////////////
    // Set scratch buffer //
    ////////////////////////

    std::optional<VkDeviceAddress> deviceAddress = reserved->scratchBuffer.getBufferDeviceAddress();
    if (deviceAddress.has_value() == false)
    {
        OV_LOG_ERROR("Fail to retrieve the scratch buffer device address.");
        this->free();
        return false;
    }

    VkDeviceOrHostAddressKHR scratchDeviceOrHostAddress = {};
    scratchDeviceOrHostAddress.deviceAddress = deviceAddress.value();

    // ScratchData is the device or host address to memory that will be used as scratch memory for the build.
    reserved->accelerationStructureBuildGeometryInfo.scratchData = scratchDeviceOrHostAddress;

#pragma endregion


    /////////////////
    // BLAS buffer //
    /////////////////

#pragma region BLASBuffer

    // Create BLASBuffer
    // Create info buffer
    VkBufferCreateInfo vkBLASBufferCreateInfo{};
    vkBLASBufferCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
    // The second field of the struct is size, which specifies the size of the buffer in bytes
    vkBLASBufferCreateInfo.size = accelerationStructureBuildSizesInfo.accelerationStructureSize;
    // The third field is usage, which indicates for which purposes the data in the buffer 
    // is going to be used. It is possible to specify multiple purposes using a bitwise or.
    // VK_BUFFER_USAGE_TRANSFER_SRC_BIT specifies that the buffer can be used as the source of a transfer command.
    vkBLASBufferCreateInfo.usage = VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR |
        VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT;
    // buffers can also be owned by a specific queue family or be shared between multiple 
    // at the same time. 
    // VK_SHARING_MODE_CONCURRENT specifies that concurrent access to any range or image subresource of the object
    // from multiple queue families is supported.
    vkBLASBufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
    // From which queue family the buffer will be accessed.
    vkBLASBufferCreateInfo.pQueueFamilyIndices = NULL;
    vkBLASBufferCreateInfo.queueFamilyIndexCount = 0;

    // Create allocation info
    VmaAllocationCreateInfo vmaBLASBufferAllocationCreateInfo = {};
    // VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT  => Allocation strategy that chooses smallest possible free range for the allocation
    // to minimize memory usage and fragmentation, possibly at the expense of allocation time.
    vmaBLASBufferAllocationCreateInfo.flags = VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT;
    //  Flags that must be set in a Memory Type chosen for an allocation. 
    vmaBLASBufferAllocationCreateInfo.requiredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;

    if (!reserved->accelerationStructureBuffer.create(vkBLASBufferCreateInfo, vmaBLASBufferAllocationCreateInfo))
    {
        OV_LOG_ERROR("Fail creation scrath buffer for BLAS.");
        this->free();
        return false;
    }

#pragma endregion


    //////////////
    // Build AS //
    //////////////

#pragma region BuildAS

    // Structure specifying the parameters of a newly created acceleration structure object
    VkAccelerationStructureCreateInfoKHR accelerationStructureCreateInfo = {};
    accelerationStructureCreateInfo.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_CREATE_INFO_KHR;
    accelerationStructureCreateInfo.pNext = NULL;
    accelerationStructureCreateInfo.createFlags = 0;
    accelerationStructureCreateInfo.buffer = reserved->accelerationStructureBuffer.getVkBuffer();
    accelerationStructureCreateInfo.offset = 0;
    accelerationStructureCreateInfo.size = accelerationStructureBuildSizesInfo.accelerationStructureSize;
    accelerationStructureCreateInfo.type = type;
    accelerationStructureCreateInfo.deviceAddress = 0;

    // Create a new acceleration structure object
    PFN_vkCreateAccelerationStructureKHR pvkCreateAccelerationStructureKHR =
        (PFN_vkCreateAccelerationStructureKHR)vkGetDeviceProcAddr(logicalDevice.get().getVkDevice(), "vkCreateAccelerationStructureKHR");

    if (pvkCreateAccelerationStructureKHR(logicalDevice.get().getVkDevice(), &accelerationStructureCreateInfo, NULL,
        &reserved->accelerationStructure) != VK_SUCCESS)
    {
        OV_LOG_ERROR("Fail to create AS, id: %d.", this->Ov::Object::getId());
        this->free();
        return false;
    }

    // dstAccelerationStructure is a pointer to the target acceleration structure for the build.
    reserved->accelerationStructureBuildGeometryInfo.dstAccelerationStructure = reserved->accelerationStructure;

经过一夜好眠,我找到了问题的答案。 基本上,错误是由于传递给函数pvkGetAccelerationStructureBuildSizesKHR()的参数不正确。 作为参数pMaxPrimitiveCounts我传递了几何图形的数量,但有必要传递每个几何图形的基元数量。

Vulkan 规格:
如果pBuildInfo->geometryCount不为 0, pMaxPrimitiveCounts必须是指向pBuildInfo->geometryCount uint32_t 值数组的有效指针

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM