vkDeviceIdle() returning DEVICE_LOST, but only sometimes

I have a bare bones renderer that currently renders a hardcoded rotating cube. It’s basically vulkan-tutorial dot com up to texture mapping chapter. I use vulkan.hpp instead of the C bindings.

I wanted to try out the new Khronos ray tracing extension, so to begin with, I created a separate class with functions that create various components required to make it works, a blas for starters.

For now I call the function and store the result in a local variable of a renderer init function - as soon as init is done, the blas goes out of scope and is destroyed, normal rendering continues, it’s great for testing.

However, the function has proven to be inconsistent. I provided the function with a vertex and index buffers and tried to create the structure. Once the cmd buffer is submitted to a compute queue, a call to vkWaitDeviceIdle() is made to make sure everything is done and complete before moving forward. The problem is that vkWaitDeviceIdle() returns VK_ERROR_DEVICE_LOST, but only 80% of the time. If it were consistent, I’d assume I was doing something wrong when creating the blas, but now I’m not sure. If the error doesn’t occur, the program continues and I get my rotating cube on the screen.

The error happens with and without validation layers, and they aren’t particularly useful, api dump reports the return value of vkWaitDeviceIdle() as VK_ERROR_DEVICE_LOST, but that’s about it.

The forum is accusing me of there being a link in my code (there wasn’t any), so, ironically, if you want to see my code, you’re gonna have to follow this camouflaged link

Here’s the code that creates the blas:

vk::UniqueAccelerationStructureKHR RayTracing::createBottomAccelerationStructure(Mesh& mesh) {
	vk::AccelerationStructureCreateGeometryTypeInfoKHR geometryTypeInfo;
	geometryTypeInfo.geometryType = vk::GeometryTypeKHR::eTriangles;
	geometryTypeInfo.maxPrimitiveCount = mesh.getIndexCount() / 3;
	geometryTypeInfo.indexType = vk::IndexType::eUint16;
	geometryTypeInfo.maxVertexCount = mesh.getVertextCount();
	geometryTypeInfo.vertexFormat = vk::Format::eR32G32B32Sfloat;
	geometryTypeInfo.allowsTransforms = VK_TRUE;

	vk::AccelerationStructureCreateInfoKHR createInfo;
	createInfo.type = vk::AccelerationStructureTypeKHR::eBottomLevel;
	createInfo.flags = vk::BuildAccelerationStructureFlagBitsKHR::ePreferFastTrace;
	createInfo.maxGeometryCount = 1;
	createInfo.pGeometryInfos = &geometryTypeInfo;

	vk::UniqueAccelerationStructureKHR accelerationStructure =
		m_context->m_logicalDevice->createAccelerationStructureKHRUnique(createInfo);

	float rowMajorAffineTransformation[3][4] =
	{{1.0f, 0.0f, 0.0f, 0.0f},
	{0.0f, 1.0f, 0.0f, 0.0f},
	{0.0f, 0.0f, 1.0f, 0.0f}};

	vk::TransformMatrixKHR transformation;
	memcpy(transformation.matrix, rowMajorAffineTransformation, 3 * 4 * sizeof(float));

	vk::DeviceOrHostAddressConstKHR transformAddress;
	transformAddress.hostAddress = &transformation;

	vk::AccelerationStructureGeometryTrianglesDataKHR triangleData;
	triangleData.vertexFormat = vk::Format::eR32G32B32Sfloat;
	triangleData.vertexData = getBufferDeviceAddress<vk::DeviceOrHostAddressConstKHR>(mesh.getVertexBuffer());
    //vertex buffer holds six floats per vertex, three coordinates followed by an RBG value
	triangleData.vertexStride = vk::DeviceSize(sizeof(float) * 6);
	triangleData.indexType = vk::IndexType::eUint16;
	triangleData.indexData = getBufferDeviceAddress<vk::DeviceOrHostAddressConstKHR>(mesh.getIndexBuffer());
	triangleData.transformData = transformAddress;

	vk::AccelerationStructureGeometryDataKHR geometryData;
	geometryData.triangles = triangleData;

	vk::AccelerationStructureGeometryKHR geometry;
	geometry.geometryType = vk::GeometryTypeKHR::eTriangles;
	geometry.geometry = geometryData;
	geometry.flags = vk::GeometryFlagBitsKHR::eOpaque;

	Buffer scratchBuffer = createScratchBuffer(*accelerationStructure);

	const vk::AccelerationStructureGeometryKHR* const pGeometry = &geometry;

	vk::AccelerationStructureBuildGeometryInfoKHR geometryInfo;
	geometryInfo.type = vk::AccelerationStructureTypeKHR::eBottomLevel;
	geometryInfo.flags = vk::BuildAccelerationStructureFlagBitsKHR::ePreferFastTrace;
	geometryInfo.update = VK_FALSE;
	geometryInfo.srcAccelerationStructure = nullptr;
	geometryInfo.dstAccelerationStructure = *accelerationStructure;
	geometryInfo.geometryArrayOfPointers = VK_FALSE;
	geometryInfo.geometryCount = 1;
	geometryInfo.ppGeometries = &pGeometry;
	geometryInfo.scratchData = getBufferDeviceAddress<vk::DeviceOrHostAddressKHR>(scratchBuffer.get());

	vk::AccelerationStructureBuildOffsetInfoKHR offsetInfo;
	offsetInfo.primitiveCount = mesh.getVertextCount() / 3;
	offsetInfo.primitiveOffset = 0;
	offsetInfo.firstVertex = 0;
	offsetInfo.transformOffset = 0;

	vk::AccelerationStructureBuildOffsetInfoKHR* pOffsetInfo = &offsetInfo;

	vk::UniqueCommandBuffer computeCmdBuffer = CmdBufferAllocator::get()->createBufferUnique(*CmdBufferAllocator::get()->m_computeCmdPool);
	vk::CommandBufferBeginInfo beginInfo;
	beginInfo.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit;
	computeCmdBuffer->begin(beginInfo);
	computeCmdBuffer->buildAccelerationStructureKHR(1, &geometryInfo, &pOffsetInfo);
	computeCmdBuffer->end();

	CmdBufferAllocator::get()->submitBuffer(*computeCmdBuffer, m_context->m_computeQueue, true);

	return std::move(accelerationStructure);
}

Buffer is just a wrapper that holds a vk::UniqueBuffer and info about vk::DeviceMemory it was suballocated from (and a few other things, like size and offset). This class does work well in other places in my code.

CmdBufferAllocator creates one pool for each of the queue types at the start of the program. One can request CmdBuffers from it and submit them using it.

Out of used functions, two might be interesting:

Buffer RayTracing::createScratchBuffer(vk::AccelerationStructureKHR& accelerationStructure) {
	vk::AccelerationStructureMemoryRequirementsInfoKHR memoryInfo;
	memoryInfo.type = vk::AccelerationStructureMemoryRequirementsTypeKHR::eBuildScratch;
	memoryInfo.buildType = vk::AccelerationStructureBuildTypeKHR::eDevice;
	memoryInfo.accelerationStructure = accelerationStructure;

	vk::MemoryRequirements memoryRequirements =
		m_context->m_logicalDevice->getAccelerationStructureMemoryRequirementsKHR(memoryInfo).memoryRequirements;

	return Buffer(
		*m_context->m_logicalDevice,
		memoryRequirements.size,
		vk::BufferUsageFlagBits::eRayTracingKHR
		| vk::BufferUsageFlagBits::eShaderDeviceAddress,
		vk::MemoryPropertyFlagBits::eDeviceLocal,
		vk::MemoryAllocateFlagBits::eDeviceAddress,
		memoryRequirements);
}

and

void CmdBufferAllocator::submitBuffer(vk::CommandBuffer& cmdBuffer, vk::Queue& queue, bool wait)     {
    vk::SubmitInfo submitInfo;
    submitInfo.commandBufferCount = 1;
    submitInfo.pCommandBuffers = &cmdBuffer;

    queue.submit(submitInfo, nullptr);

    if (wait) {
        m_context.lock()->m_logicalDevice->waitIdle();
    }
}

I can provide more info, be it more code, system specifications or why I did something I did.
Any idea why the code isn’t working only 80% of the time?

This topic was automatically closed 183 days after the last reply. New replies are no longer allowed.