Исключение, когда оператор присваивания перемещения используется для r-значения - PullRequest
0 голосов
/ 02 октября 2018

Я получаю очень странный сбой (только в режиме отладки) при использовании оператора присваивания перемещения со значением r:

Очистка и повторная компиляция кода в режиме отладки не помогают.Сбой происходит до вызова 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);

Я получаю это исключение:

Исключение, выданное в 0x0000000059F95E35 (nvoglv64.dll) в Vulkan.exe: 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);

}

edit: добавлено это как часть предложения Джерико: Когда класс падает, из функций класса выводится следующее:

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()

Ответы [ 2 ]

0 голосов
/ 04 октября 2018

Вы, кажется, забыли полностью инициализировать 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% времени неинициализированная переменная будет равна нулю.Здесь вам поможет компилятор, и в профиле отладки он заполняет неинициализированную память (ненулевым) мусором, поэтому он вылетает, чтобы обнаружить скрытую ошибку, в противном случае.

0 голосов
/ 02 октября 2018

Это говорит о том, что у вас происходит какое-то двойное разрушение буфера.Во время разработки у вас должны быть включены слои проверки, потому что, если вы это сделаете, они должны уловить это до того, как произойдет сбой.

С точки зрения выяснения того, что именно идет не так, возможно, самый простой способ - это занести запись в лог вашего оператора назначения перемещения, ctor и dtor (который включает в себя дескриптор буфера, передаваемого в vkDestroyBuffer), такчто вы можете увидеть, какова фактическая последовательность вызовов.

...