当使用带有 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, ©Region);
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
这使得这些成员未初始化:pNext
、flags
、queueFamilyIndexCount
和 pQueueFamilyIndices
。值得注意的是,如果 pNext
恰好是非 NULL
,驱动程序将尝试取消引用它(并因为指针指向任何地方而崩溃)。
如果某些事情只发生在调试配置文件中,这是您应该考虑的第一种错误。在发布配置文件中,99% 的时间未初始化变量将为零。编译器确实会在此处帮助您,在调试配置文件中它会用(非零)垃圾填充未初始化的内存,因此它会为您崩溃以发现隐藏的错误。
关于c++ - 对右值使用移动赋值运算符时的异常,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52605940/