本文共 3925 字,大约阅读时间需要 13 分钟。
相比于显卡内部读取数据,单纯从CPU访问内存数据的方式性能并不理想。最佳的做法是使用VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT标志位,这种方式通常用于专用图形卡,因为这样可以确保CPU无法访问这些内存,从而提高性能。
创建临时缓冲区的最佳实现方式是通过辅助函数来完成。以下是一个常用的方法,涉及创建缓冲区和顶点缓冲区的实现:
void 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(device, &bufferInfo, nullptr, &buffer) != VK_SUCCESS) { throw std::runtime_error("failed to create buffer!"); } VkMemoryRequirements memRequirements; vkGetBufferMemoryRequirements(device, buffer, &memRequirements); VkMemoryAllocateInfo allocInfo = {}; allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; allocInfo.allocationSize = memRequirements.size; allocInfo.memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, properties); if (vkAllocateMemory(device, &allocInfo, nullptr, &bufferMemory) != VK_SUCCESS) { throw std::runtime_error("failed to allocate buffer memory!"); } vkBindBufferMemory(device, buffer, bufferMemory, 0);}
这个函数需要传递缓冲区大小、内存属性和使用标志,以创建不同类型的缓冲区。最后两个参数用于存储输出的句柄。
以下是创建顶点缓冲区的实现:
void createVertexBuffer() { VkDeviceSize bufferSize = sizeof(vertices[0]) * vertices.size(); createBuffer(bufferSize, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, vertexBuffer, vertexBufferMemory);}
为了实现临时缓冲区的使用,我们可以修改顶点缓冲区的创建方法:
void createVertexBuffer() { VkDeviceSize bufferSize = sizeof(vertices[0]) * 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); void* data; vkMapMemory(device, stagingBufferMemory, 0, bufferSize, 0, &data); memcpy(data, vertices.data(), bufferSize); vkUnmapMemory(device, stagingBufferMemory); createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, vertexBuffer, vertexBufferMemory);}
在这个实现中,stagingBuffer
用于临时存储顶点数据,然后通过copyBuffer
函数将其复制到设备缓冲区中。
为了实现内存传输操作,我们需要使用命令缓冲区。以下是一个常用的方法:
void copyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size) { VkCommandBufferAllocateInfo allocInfo = {}; allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; allocInfo.commandPool = commandPool; allocInfo.commandBufferCount = 1; if (vkAllocateCommandBuffers(device, &allocInfo, &commandBuffer) != VK_SUCCESS) { throw std::runtime_error("failed to allocate command buffer!"); }}void copyBuffer() { VkCommandBufferBeginInfo beginInfo = {}; beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; if (vkBeginCommandBuffer(commandBuffer, &beginInfo) != VK_SUCCESS) { throw std::runtime_error("failed to begin command buffer!"); } VkBufferCopy copyRegion = {}; copyRegion.srcOffset = 0; copyRegion.dstOffset = 0; copyRegion.size = size; if (vkCmdCopyBuffer(commandBuffer, srcBuffer, dstBuffer, 1, copyRegion) != VK_SUCCESS) { throw std::runtime_error("failed to copy buffers!"); } if (vkEndCommandBuffer(commandBuffer) != VK_SUCCESS) { throw std::runtime_error("failed to end command buffer!"); }}
在copyBuffer
函数中,可以通过命令缓冲区执行内存传输操作。为了优化命令缓冲区的内存使用,可以为短期命令缓冲区分配内存,并使用VK_COMMAND_POOL_CREATE_TRANSIENT_BIT
标志位。
最后,确保在完成内存传输后清理临时缓冲区:
void clearStagingBuffer() { vkDestroyBuffer(device, stagingBuffer, nullptr); vkFreeMemory(device, stagingBufferMemory, nullptr);}
通过上述方法,可以实现对顶点数据的高效内存传输和缓冲区管理。
转载地址:http://hpnp.baihongyu.com/