go-vk icon indicating copy to clipboard operation
go-vk copied to clipboard

vkCreateBuffer sometimes returns a 0 handle.

Open gazed opened this issue 5 months ago • 0 comments

Sometimes creating a buffer with vkCreateBuffer returns a 0 handle, but no error. I would appreciate your thoughts and help as I've come up with a few workarounds, but the most likely explanation is that I've got a bug on my end. Platform: win11 Go: go1.23.1 windows/amd64 Vulkan API version=1.3.280 GPU NVIDIA GeForce RTX 3080 CPU 13th Gen Intel(R) Core(TM) i7-13700K 3.40 GHz

For example here is the error getting caught during asset upload (this particular case was a texture upload every few frames). It only fails a few times out of a hundred or so uploads.

time=2024-09-16T09:40:05.698-04:00 level=ERROR msg="UpdateTexture update" err="updateTexture create staging vk.CreateBuffer: 0 
time=2024-09-16T09:40:09.443-04:00 level=ERROR msg="UpdateTexture update" err="updateTexture create staging vk.CreateBuffer: 0

On engine shutdown, there are complaints about not cleaning up VkBuffers - matching the number of caught errors. which seems to imply that the buffers were really allocated, and the problem was that the handle was not properly returned.

time=2024-09-16T09:40:14.147-04:00 level=DEBUG msg="engine shutdown!"
VUID-vkDestroyDevice-device-05137(ERROR / SPEC): msgNum: 1215490720 - Validation Error: [ VUID-vkDestroyDevice-device-05137 ] Object 0: handle = 0xb849bd0000001efb, type = VK_OBJECT_TYPE_BUFFER; | MessageID = 0x4872eaa0 | vkCreateDevice():  OBJ ERROR : For VkDevice 0x29a407f74a0[], VkBuffer 0xb849bd0000001efb[] has not been destroyed. The Vulkan spec states: All child objects created on device must have been destroyed prior to destroying device (https://vulkan.lunarg.com/doc/view/1.3.268.0/windows/1.3-extensions/vkspec.html#VUID-vkDestroyDevice-device-05137)
    Objects: 1
        [0] 0xb849bd0000001efb, type: 9, name: NULL
VUID-vkDestroyDevice-device-05137(ERROR / SPEC): msgNum: 1215490720 - Validation Error: [ VUID-vkDestroyDevice-device-05137 ] Object 0: handle = 0x36ba2f000000203c, type = VK_OBJECT_TYPE_BUFFER; | MessageID = 0x4872eaa0 | vkCreateDevice():  OBJ ERROR : For VkDevice 0x29a407f74a0[], VkBuffer 0x36ba2f000000203c[] has not been destroyed. The Vulkan spec states: All child objects created on device must have been destroyed prior to destroying device (https://vulkan.lunarg.com/doc/view/1.3.268.0/windows/1.3-extensions/vkspec.html#VUID-vkDestroyDevice-device-05137)
    Objects: 1
        [0] 0x36ba2f000000203c, type: 9, name: NULL```

Background - I am currently using the go-vk bindings in small game engine project github.com/gazed/vu. I started to see this only recently when uploading assets to the GPU (on a recent separate game project using vu). I haven't seen the problem anywhere else and I haven't been able to reduce the problem down to a small code sample.

I tried a few things to categorize the problem... here's what I've got so far: from: github.com/gazed/vu/internal/render/vk/command.go where internal/render/vk is the generated go-vk bindings. The only changes are the work-around attempts below:

// CreateBuffer: See https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/vkCreateBuffer.html
func CreateBuffer(device Device, createInfo *BufferCreateInfo, allocator *AllocationCallbacks) (buffer Buffer, r error) {
    // Parameter is a singular input, requires translation - createInfo
    var pCreateInfo *_vkBufferCreateInfo
    if createInfo != nil {
        pCreateInfo = createInfo.Vulkanize()
    }

    // Parameter is a singular input, pass direct - allocator
    var pAllocator unsafe.Pointer
    if allocator != nil {
        pAllocator = unsafe.Pointer(allocator)
    }

    // Workaround 1.  a fmt.Printf("anything\n") here makes the problem to go away.
    // Not sure its timing related though because a busy wait loop with different wait times instead of the Printf did not work.
    // A Sleep causes it to fail every time :)

    // buffer is a binding-allocated single return value and will be populated by Vulkan
    ptr_pBuffer := &buffer

    // Workaround 2. Syscall also makes the problem go away
    // See: https://pkg.go.dev/unsafe#Pointer for possible reason.
    //
    // "The compiler handles a Pointer converted to a uintptr in the argument list of a call
    //  to a function implemented in assembly by arranging that the referenced allocated object,
    //  if any, is retained and not moved until the call completes, even though from the types
    //  alone it would appear that the object is no longer needed during the call.
    //  For the compiler to recognize this pattern, the conversion must appear in the argument list"

    if vkCreateBuffer.fnHandle == nil {
        // call once to handle any lazy initialization
        r = Result(execTrampoline(vkCreateBuffer, uintptr(device), uintptr(unsafe.Pointer(pCreateInfo)),
            uintptr(unsafe.Pointer(pAllocator)), uintptr(unsafe.Pointer(ptr_pBuffer))))
    } else {
        result, _, _ := syscall.Syscall6(uintptr(vkCreateBuffer.fnHandle), 4,
            uintptr(device),
            uintptr(unsafe.Pointer(pCreateInfo)),
            uintptr(unsafe.Pointer(pAllocator)),
            uintptr(unsafe.Pointer(ptr_pBuffer)),
            0,
            0)
        r = Result(result)
    }
    if r == Result(0) {
        r = SUCCESS
    }
    return buffer, r
}

Thoughts? Ideas? (Intermittent problems are the worst)

gazed avatar Sep 17 '24 11:09 gazed