Memcpy 因访问冲突而失败 - Vulkan 复制到顶点缓冲区
Memcpy Fails with access violation - Vulkan copying to vertex buffer
我正在尝试将一些数据传递给 Vulkan 中的 GPU。 AFAIK,执行此操作的唯一方法是使用 memcpy。这适用于较小的向量,但对于较大的模型,当我尝试这样做时会遇到访问冲突异常。同样的方法在同一台机器上的另一个实现中工作。我应该寻找什么?谢谢
这是代码
void VulkanAllocator::createVertexBuffer(std::vector<HE2_Vertex>* vertices, VkBuffer& vertexBuffer, VkDeviceMemory& vertexBufferMemory)
{
VkDeviceSize bufferSize = sizeof(HE2_Vertex) * vertices->size();
VkBuffer stagingBuffer;
VkDeviceMemory stagingBufferMemory;
createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, stagingBuffer, stagingBufferMemory);
VkMemoryRequirements memReqs;
vkGetBufferMemoryRequirements(vdi->device, stagingBuffer, &memReqs);
void* data;
if (vkMapMemory(vdi->device, stagingBufferMemory, 0, (size_t)memReqs.size, 0, &data) != VK_SUCCESS)
{
throw std::exception("Bad Memory map!");
}
memcpy(data, vertices->data(), (size_t)memReqs.size); //Crash after this line
vkUnmapMemory(vdi->device, stagingBufferMemory);
createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, vertexBuffer, vertexBufferMemory);
copyBuffer(stagingBuffer, vertexBuffer, bufferSize);
vkDestroyBuffer(vdi->device, stagingBuffer, nullptr);
vkFreeMemory(vdi->device, stagingBufferMemory, nullptr);
}
CreateBuffer 看起来像这样
void VulkanAllocator::createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer& buffer, VkDeviceMemory& bufferMemory)
{
VkBufferCreateInfo bufferInfo = {};
bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
bufferInfo.size = size;
bufferInfo.usage = usage;
bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
if (vkCreateBuffer(vdi->device, &bufferInfo, nullptr, &buffer) != VK_SUCCESS)
{
throw std::runtime_error("Unable to create vertex buffer");
}
VkMemoryRequirements memRequirements;
vkGetBufferMemoryRequirements(vdi->device, buffer, &memRequirements);
VkMemoryAllocateInfo allocInfo = {};
allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
allocInfo.allocationSize = memRequirements.size;
allocInfo.memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
if (vkAllocateMemory(vdi->device, &allocInfo, nullptr, &bufferMemory) != VK_SUCCESS)
{
throw std::runtime_error("failed to allocate vertex buffer memory");
}
vkBindBufferMemory(vdi->device, buffer, bufferMemory, 0);
}
矢量是这样创建的(如果您已经完成 https://vulkan-tutorial.com/,您可能会认出我的重构)。 HE2_Model 只包含一个 HE2_Vertex
的向量和一个 uint32_t
的向量。我不知道是否相关,但我还没有为 HE2_Vertex
.
编写复制构造函数
HE2_Model* 模型 = 新 HE2_Model();
//Using tiny_obj_loader to pull in from a file
tinyobj::attrib_t attrib;
std::vector<tinyobj::shape_t> shapes;
std::vector<tinyobj::material_t> materials;
std::string warn, err;
if (!tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, filename.c_str()))
{
throw std::runtime_error("Couldn't load model!");
}
std::unordered_map<HE2_Vertex, uint32_t> uniqueVertices = { };
for (const auto& shape : shapes)
{
for (const auto& index : shape.mesh.indices)
{
HE2_Vertex vertex = {};
vertex.pos = {
attrib.vertices[3 * index.vertex_index + 0],
attrib.vertices[3 * index.vertex_index + 1],
attrib.vertices[3 * index.vertex_index + 2] };
vertex.texCoord = {
attrib.texcoords[2 * index.texcoord_index + 0],
1.0f - attrib.texcoords[2 * index.texcoord_index + 1] };
if (uniqueVertices.count(vertex) == 0)
{
uniqueVertices[vertex] = static_cast<uint32_t>(model->vertices.size());
model->vertices.push_back(vertex);
}
model->indices.push_back(uniqueVertices[vertex]);
}
}
HE2_Instance::renderBackend->onAddModel(model);
return model;
这是崩溃的行:
memcpy(data, vertices->data(), (size_t)memReqs.size); //Crash after this line
您在评论中说过 "Memreqs size is slightly larger than the bufferSize",即 memReqs.size
大于 bufferSize
,后者是向量的完整大小 (sizeof(HE2_Vertex) * vertices->size();
),因此您的 memcpy将从 vertices->data() 读取比允许的更多的数据,这是未定义的行为,有时会崩溃。你应该改用这个:
memcpy(data, vertices->data(), (size_t)bufferSize);
(也可能添加类似 assert(bufferSize <= memReqs.size);
的内容)
令人气愤的是,这是通过重建修复的。我在一个 visual studio 解决方案中有多个项目,我只需要学会不依赖项目依赖性,对于内存错误和异常,我总是需要做的第一件事就是重建每个项目。给任何有奇怪的访问冲突和行为等的人的重要提示 - 重建重建重建!!!
我正在尝试将一些数据传递给 Vulkan 中的 GPU。 AFAIK,执行此操作的唯一方法是使用 memcpy。这适用于较小的向量,但对于较大的模型,当我尝试这样做时会遇到访问冲突异常。同样的方法在同一台机器上的另一个实现中工作。我应该寻找什么?谢谢
这是代码
void VulkanAllocator::createVertexBuffer(std::vector<HE2_Vertex>* vertices, VkBuffer& vertexBuffer, VkDeviceMemory& vertexBufferMemory)
{
VkDeviceSize bufferSize = sizeof(HE2_Vertex) * vertices->size();
VkBuffer stagingBuffer;
VkDeviceMemory stagingBufferMemory;
createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, stagingBuffer, stagingBufferMemory);
VkMemoryRequirements memReqs;
vkGetBufferMemoryRequirements(vdi->device, stagingBuffer, &memReqs);
void* data;
if (vkMapMemory(vdi->device, stagingBufferMemory, 0, (size_t)memReqs.size, 0, &data) != VK_SUCCESS)
{
throw std::exception("Bad Memory map!");
}
memcpy(data, vertices->data(), (size_t)memReqs.size); //Crash after this line
vkUnmapMemory(vdi->device, stagingBufferMemory);
createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, vertexBuffer, vertexBufferMemory);
copyBuffer(stagingBuffer, vertexBuffer, bufferSize);
vkDestroyBuffer(vdi->device, stagingBuffer, nullptr);
vkFreeMemory(vdi->device, stagingBufferMemory, nullptr);
}
CreateBuffer 看起来像这样
void VulkanAllocator::createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer& buffer, VkDeviceMemory& bufferMemory)
{
VkBufferCreateInfo bufferInfo = {};
bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
bufferInfo.size = size;
bufferInfo.usage = usage;
bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
if (vkCreateBuffer(vdi->device, &bufferInfo, nullptr, &buffer) != VK_SUCCESS)
{
throw std::runtime_error("Unable to create vertex buffer");
}
VkMemoryRequirements memRequirements;
vkGetBufferMemoryRequirements(vdi->device, buffer, &memRequirements);
VkMemoryAllocateInfo allocInfo = {};
allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
allocInfo.allocationSize = memRequirements.size;
allocInfo.memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
if (vkAllocateMemory(vdi->device, &allocInfo, nullptr, &bufferMemory) != VK_SUCCESS)
{
throw std::runtime_error("failed to allocate vertex buffer memory");
}
vkBindBufferMemory(vdi->device, buffer, bufferMemory, 0);
}
矢量是这样创建的(如果您已经完成 https://vulkan-tutorial.com/,您可能会认出我的重构)。 HE2_Model 只包含一个 HE2_Vertex
的向量和一个 uint32_t
的向量。我不知道是否相关,但我还没有为 HE2_Vertex
.
HE2_Model* 模型 = 新 HE2_Model();
//Using tiny_obj_loader to pull in from a file
tinyobj::attrib_t attrib;
std::vector<tinyobj::shape_t> shapes;
std::vector<tinyobj::material_t> materials;
std::string warn, err;
if (!tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, filename.c_str()))
{
throw std::runtime_error("Couldn't load model!");
}
std::unordered_map<HE2_Vertex, uint32_t> uniqueVertices = { };
for (const auto& shape : shapes)
{
for (const auto& index : shape.mesh.indices)
{
HE2_Vertex vertex = {};
vertex.pos = {
attrib.vertices[3 * index.vertex_index + 0],
attrib.vertices[3 * index.vertex_index + 1],
attrib.vertices[3 * index.vertex_index + 2] };
vertex.texCoord = {
attrib.texcoords[2 * index.texcoord_index + 0],
1.0f - attrib.texcoords[2 * index.texcoord_index + 1] };
if (uniqueVertices.count(vertex) == 0)
{
uniqueVertices[vertex] = static_cast<uint32_t>(model->vertices.size());
model->vertices.push_back(vertex);
}
model->indices.push_back(uniqueVertices[vertex]);
}
}
HE2_Instance::renderBackend->onAddModel(model);
return model;
这是崩溃的行:
memcpy(data, vertices->data(), (size_t)memReqs.size); //Crash after this line
您在评论中说过 "Memreqs size is slightly larger than the bufferSize",即 memReqs.size
大于 bufferSize
,后者是向量的完整大小 (sizeof(HE2_Vertex) * vertices->size();
),因此您的 memcpy将从 vertices->data() 读取比允许的更多的数据,这是未定义的行为,有时会崩溃。你应该改用这个:
memcpy(data, vertices->data(), (size_t)bufferSize);
(也可能添加类似 assert(bufferSize <= memReqs.size);
的内容)
令人气愤的是,这是通过重建修复的。我在一个 visual studio 解决方案中有多个项目,我只需要学会不依赖项目依赖性,对于内存错误和异常,我总是需要做的第一件事就是重建每个项目。给任何有奇怪的访问冲突和行为等的人的重要提示 - 重建重建重建!!!