c++ - 对右值使用移动赋值运算符时的异常

标签 c++ visual-studio-2017 vulkan

当使用带有 r 值的移动赋值运算符时,我遇到了一个非常奇怪的崩溃(仅在 Debug模式下):

在 Debug模式下清理和重新编译代码没有帮助。崩溃发生在调用 VulkanBuffer::operator=() 之前。

// ok
//VulkanBuffer myBuffer(logicalDevice, bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_SHARING_MODE_EXCLUSIVE);
//buffer_ = std::move(myBuffer);

// ok
//VulkanBuffer myBuffer = VulkanBuffer(logicalDevice, bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_SHARING_MODE_EXCLUSIVE);
//buffer_ = std::move(myBuffer);

// crash in debug mode, release mode works fine
//buffer_ = std::move( VulkanBuffer(logicalDevice, bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_SHARING_MODE_EXCLUSIVE) );

// crash in debug mode, release mode works fine
buffer_ = VulkanBuffer(logicalDevice, bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_SHARING_MODE_EXCLUSIVE);

我得到这个异常:

在 Vulkan.exe 中的 0x0000000059F95E35 (nvoglv64.dll) 抛出异常:0xC0000005:访问冲突读取位置 0xFFFFFFFFFFFFFFFF。

此异常发生在 VulkanBuffer 非默认构造函数中

任何人都可以阐明这一点吗?在我看来,代码应该是等价的。

实际代码如下:

VulkanBuffer 声明

#pragma once

#include "VulkanLogicalDevice.h"
#include "Vertex.h"

#define GLFW_INCLUDE_VULKAN
#include <GLFW/glfw3.h>

#include <memory>
#include <vector>

class VulkanCommandPool;

class VulkanBuffer
{
public:
    VulkanBuffer();
    VulkanBuffer(const std::shared_ptr<VulkanLogicalDevice>& logicalDevice, VkDeviceSize deviceSizeInbytes, VkBufferUsageFlags bufferUsageFlags, VkSharingMode sharingMode);
    ~VulkanBuffer();
    VulkanBuffer(const VulkanBuffer &rhs) = delete;
    VulkanBuffer & operator=(const VulkanBuffer &rhs) = delete;
    VulkanBuffer(VulkanBuffer &&rhs) = delete;
    VulkanBuffer & operator=(VulkanBuffer &&rhs);

    VkBuffer & handle() { return buffer_; }
    const VkBuffer & handle() const { return buffer_; }
    const VkBufferCreateInfo & getBufferInfo() const { return createInfo_; }
    void copyDataFrom(const VulkanBuffer & rhs, const VulkanCommandPool &commandPool, VkDeviceSize dataSizeInBytes);

    friend void swap(VulkanBuffer &lhs, VulkanBuffer &rhs);

private:
    std::shared_ptr<VulkanLogicalDevice> logicalDevice_;
    VkBufferCreateInfo createInfo_;
    VkBuffer buffer_;
};

VulkanBuffer的定义

#include "VulkanBuffer.h"

#include "VulkanCommandPool.h"
#include "Vertex.h"
#include <iostream>

void swap(VulkanBuffer &lhs, VulkanBuffer &rhs)
{
    std::swap(lhs.logicalDevice_, rhs.logicalDevice_);
    std::swap(lhs.buffer_, rhs.buffer_);
    std::swap(lhs.createInfo_, rhs.createInfo_);
}

VulkanBuffer::VulkanBuffer()
    : buffer_(VK_NULL_HANDLE)
{}

/// \param logicalDevice        Vulkan device
/// \param deviceSizeInbytes    number of bytes of the vertices to be stored in this vertex buffer
/// \param bufferUsageFlags     what will the buffer be used for, eg VK_BUFFER_USAGE_VERTEX_BUFFER_BIT
/// \param sharingMode          is the buffer used by more than one queue family, eg: VK_SHARING_MODE_EXCLUSIVE, VK_SHARING_MODE_CONCURRENT
VulkanBuffer::VulkanBuffer(const std::shared_ptr<VulkanLogicalDevice>& logicalDevice, VkDeviceSize deviceSizeInbytes, VkBufferUsageFlags bufferUsageFlags, VkSharingMode sharingMode)
    : logicalDevice_(logicalDevice), buffer_(VK_NULL_HANDLE)
{
    createInfo_.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
    createInfo_.size = deviceSizeInbytes;
    createInfo_.usage = bufferUsageFlags; // indicates this data is for a vertex buffer.
    createInfo_.sharingMode = sharingMode; // ownership by one queue family or multiple

    if (vkCreateBuffer(logicalDevice->handle(), &createInfo_, nullptr, &buffer_) != VK_SUCCESS) {
        throw std::runtime_error("failed to create buffer!");
    }
}

VulkanBuffer::~VulkanBuffer()
{
    if (buffer_ != VK_NULL_HANDLE)
        vkDestroyBuffer(logicalDevice_->handle(), buffer_, nullptr);
}

VulkanBuffer & VulkanBuffer::operator=(VulkanBuffer &&rhs)
{
    swap(*this, rhs);
    return *this;
}

void VulkanBuffer::copyDataFrom(const VulkanBuffer & rhs, const VulkanCommandPool &commandPool, VkDeviceSize dataSizeInBytes)
{
    if (buffer_ == VK_NULL_HANDLE || rhs.buffer_ == VK_NULL_HANDLE)
    {
        std::cout << "Illegal VulkanBuffer::copyDataFrom(), one or more buffers not initialized.\n";
        return;
    }

    VkCommandBufferAllocateInfo allocInfo = {};
    allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
    allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
    allocInfo.commandPool = commandPool.handle();
    allocInfo.commandBufferCount = 1;

    VkCommandBuffer commandBuffer;
    vkAllocateCommandBuffers(logicalDevice_->handle(), &allocInfo, &commandBuffer);

    VkCommandBufferBeginInfo beginInfo = {};
    beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
    beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;

    vkBeginCommandBuffer(commandBuffer, &beginInfo);

    VkBufferCopy copyRegion = {};
    copyRegion.size = dataSizeInBytes;
    vkCmdCopyBuffer(commandBuffer, rhs.handle(), buffer_, 1, &copyRegion);

    vkEndCommandBuffer(commandBuffer);

    VkSubmitInfo submitInfo = {};
    submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
    submitInfo.commandBufferCount = 1;
    submitInfo.pCommandBuffers = &commandBuffer;

    vkQueueSubmit(logicalDevice_->getGraphicsQueue(), 1, &submitInfo, VK_NULL_HANDLE);
    vkQueueWaitIdle(logicalDevice_->getGraphicsQueue());

    vkFreeCommandBuffers(logicalDevice_->handle(), commandPool.handle(), 1, &commandBuffer);

}

编辑:将此添加为 Jherico 建议的一部分: 当类崩溃时,类函数会打印以下内容:

handle: 0000000000000000 VulkanBuffer: default ctor()
handle: 0000000000000000 VulkanBuffer: non default ctor() start
handle: 000001B2A00C84E0 VulkanBuffer: non default ctor() end
handle: 0000000000000000 VulkanBuffer: non default ctor() start

不崩溃时,打印如下内容

handle: 0000000000000000 VulkanBuffer: default ctor()
handle: 0000000000000000 VulkanBuffer: non default ctor() start
handle: 000001989ADB56E0 VulkanBuffer: non default ctor() end
handle: 0000000000000000 VulkanBuffer: non default ctor() start
handle: 000001989ADB6310 VulkanBuffer: non default ctor() end
handle: 0000000000000000 VulkanBuffer: operator=()
handle lhs: 0000000000000000 handle rhs: 000001989ADB6310 VulkanBuffer: Swap() start
handle lhs: 000001989ADB6310 handle rhs: 0000000000000000 VulkanBuffer: Swap() end
handle: 000001989ADB6310 VulkanBuffer: copyDataFrom()
handle: 0000000000000000 VulkanBuffer: dtor() // instruction re-ordered here?
handle: 000001989ADB56E0 VulkanBuffer: dtor()

最佳答案

您似乎忘记了完全初始化 VkBufferCreateInfo(即 createInfo_)。

您似乎只在此处进行了部分初始化:

createInfo_.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
createInfo_.size = deviceSizeInbytes;
createInfo_.usage = bufferUsageFlags; // indicates this data is for a vertex buffer.
createInfo_.sharingMode = sharingMode; // ownership by one queue family or multiple

这使得这些成员未初始化:pNextflagsqueueFamilyIndexCountpQueueFamilyIndi​​ces。值得注意的是,如果 pNext 恰好是非 NULL,驱动程序尝试取消引用它(并因为指针指向任何地方而崩溃)。

如果某些事情只发生在调试配置文件中,这是您应该考虑的第一种错误。在发布配置文件中,99% 的时间未初始化变量将为零。编译器确实会在此处帮助您,在调试配置文件中它会用(非零)垃圾填充未初始化的内存,因此它会为您崩溃以发现隐藏的错误。

关于c++ - 对右值使用移动赋值运算符时的异常,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52605940/

相关文章:

c++ - Makefile 中的 "relink"意味着什么?

c++ - VS2013中构造函数调用自身时会发生什么?

c# - Visual Studio C# 异常错误消息

c++ - 如何强制执行包括模板成员在内的依赖静态对象的初始化顺序?

c++ - 使用 -fPIcflags重新编译

c++ - 线性搜索类对象数组

c++ - 两个具有相同代码的代码库,但一个正在生成重载解析编译错误,而另一个则没有

将所有纹理绑定(bind)到一个巨大的描述符集上

c++ - Vulkan - 我应该什么时候创建新管道?

c++ - 如何为我的 Vulkan 类创建这个通用数据结构字段?