vulkan代碼按照這個(gè)教程的基礎(chǔ)上進(jìn)行拓展
https://vulkan-tutorial.com/
參考了知乎大佬的筆記
和github大佬的代碼
需要將vulkan與游戲引擎向結(jié)合,最基礎(chǔ)的就是要把每一個(gè)game object畫(huà)出來(lái)
基礎(chǔ)教程中只畫(huà)了一個(gè)圖形,而vulkan的draw又是用command buffer預(yù)先錄制,所以不能采用OpenGL那種更新一次uniform再draw一次的方法,效率也不高。
Vulkan可以用dynamic uniform來(lái)做這件事情。
1.申請(qǐng)uniform buffer
定義Dynamic Uniform Buffer Object
struct DynamicUBO {
glm::mat4 *model = nullptr;
};
新增一組空指針來(lái)保存地址, 并申明dynamic ubo變量,以及2個(gè)對(duì)齊變量,等等
std::vector<void*> dynamicUniformData;
size_t dynamicAlignment;
size_t normalUBOAlignment;
DynamicUBO uboDynamic;
vulkan 內(nèi)存管理,最好申請(qǐng)一塊buffer,用offset的形式來(lái)使用,但是Vertex, Index buffer和uniform buffer的usage和property flag不盡相同,而且按照上文vulkan的教程,uniform buffer要有多塊與swapchain數(shù)量對(duì)應(yīng),所以這里為了方便就另外申請(qǐng)了

dynamic和普通的uniform buffer還是可以放一起的,用minUboAlignment計(jì)算出最小間隔,從而得出offset的值。申請(qǐng)完Buffer后馬上將指針map上去。
這里的VK_MEMORY_PROPERTY_HOST_COHERENT_BIT加不加在我電腦上看不出區(qū)別,可能和顯卡型號(hào)有關(guān),下面的flush也是,因?yàn)榫驮谖译娔X上搞搞就先不糾結(jié)這些了。
//dynamic
VkPhysicalDeviceProperties properties;
vkGetPhysicalDeviceProperties(physicalDevice, &properties);
size_t minUboAlignment = properties.limits.minUniformBufferOffsetAlignment;
dynamicAlignment = sizeof(glm::mat4);
if (minUboAlignment > 0) {
dynamicAlignment = (dynamicAlignment + minUboAlignment - 1) & ~(minUboAlignment - 1);
}
VkDeviceSize bufferSize = LONG_SIZE * dynamicAlignment;
uboDynamic.model = (glm::mat4*)alignedAlloc(bufferSize, dynamicAlignment);
dynamicUniformData.resize(swapChainImages.size());
//normal
normalUBOAlignment = sizeof(UniformBufferObject);
if (minUboAlignment > 0) {
normalUBOAlignment = (normalUBOAlignment + minUboAlignment - 1) & ~(minUboAlignment - 1);
}
bufferSize += normalUBOAlignment;
uniformBuffers.resize(swapChainImages.size());
uniformBuffersMemory.resize(swapChainImages.size());
for (size_t i = 0; i < swapChainImages.size(); ++i)
{
createBuffer(bufferSize, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, uniformBuffers[i], uniformBuffersMemory[i]);
vkMapMemory(device, uniformBuffersMemory[i], 0, LONG_SIZE * dynamicAlignment, 0, &dynamicUniformData[i]);
}
shader里把model矩陣單獨(dú)拿出來(lái)作為binding 1
layout(binding = 1) uniform UboInstance{
mat4 model;
}uboInstance;
2.下面是descriptor 相關(guān)的一套操作。
Vulkan Shader Resource Binding

在DescriptorSetLayout里新增一個(gè)dyanmic的binding
VkDescriptorSetLayoutBinding uboDynamicLayoutBinding = {};
uboDynamicLayoutBinding.binding = 1;
uboDynamicLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
uboDynamicLayoutBinding.descriptorCount = 1;
uboDynamicLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
uboDynamicLayoutBinding.pImmutableSamplers = nullptr;
3.修改DescriptorPool
poolSizes[1].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
poolSizes[1].descriptorCount = static_cast<uint32_t>(swapChainImages.size());
4.在DescriptorSet里新增一個(gè)VkWriteDescriptorSet
別忘了描述下在buffer里的位置
VkDescriptorBufferInfo bufferInfo = {};
bufferInfo.buffer = uniformBuffers[i];
bufferInfo.offset = 0;
bufferInfo.range = normalUBOAlignment;
VkDescriptorBufferInfo dynamicBufferInfo = {};
dynamicBufferInfo.buffer = uniformBuffers[i];
dynamicBufferInfo.offset = normalUBOAlignment;
dynamicBufferInfo.range = dynamicAlignment;
加一個(gè)write
descriptorWrites[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
descriptorWrites[1].dstSet = descriptorSets[i];
descriptorWrites[1].dstBinding = 1;
descriptorWrites[1].dstArrayElement = 0;
descriptorWrites[1].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
descriptorWrites[1].descriptorCount = 1;
descriptorWrites[1].pBufferInfo = &dynamicBufferInfo;
descriptorWrites[1].pTexelBufferView = nullptr;
5.畫(huà)之前更新數(shù)據(jù)
先更新視距矩陣的信息
void VulkanAPI::updateUniformBuffer(uint32_t currentImage)
{
UniformBufferObject ubo = {};
glm::vec4 view_dir = *cameraTransform * glm::vec4(0.0f, -1.0f, 0.0f, 0.0f);
glm::vec4 view_up = *cameraTransform * glm::vec4(0.0f, 0.0f, 1.0f, 0.0f);
glm::vec4 view_pos = *cameraTransform * glm::vec4(cameraOffset, 1.0f);
ubo.view = glm::lookAt(glm::vec3(view_pos), glm::vec3(view_pos + view_dir), glm::vec3(view_up));
ubo.proj = glm::perspective(glm::radians(45.0f), swapChainExtent.width / (float)swapChainExtent.height, 0.1f, 100.0f);
ubo.proj[1][1] *= -1;
memcpy(dynamicUniformData[currentImage], &ubo, sizeof(UniformBufferObject));
}
用dynamic uniform更新transform,注意偏移量指針需要強(qiáng)制轉(zhuǎn)換成size_t來(lái)對(duì)應(yīng)偏移單位
(此處可以優(yōu)化,transform里的matrix作為指針指向這個(gè)medelMat,增刪物體的時(shí)候做對(duì)應(yīng)的指針變換,即可節(jié)省下這個(gè)復(fù)制的過(guò)程)
void VulkanAPI::updateDynamicUniformBuffer(uint32_t currentImage)
{
uint32_t index = 0;
for (auto transID: ComponentManager::GetInstance()->activeComponents[TRANSFORM])
{
glm::mat4* modelMat = (glm::mat4*)(((uint64_t)uboDynamic.model + (index * dynamicAlignment)));
*modelMat = ComponentManager::GetInstance()->mTransforms[transID].transMatrix;
++index;
}
void* data = reinterpret_cast<size_t*>(dynamicUniformData[currentImage]) + normalUBOAlignment / sizeof(size_t);
memcpy(data, uboDynamic.model, ComponentManager::GetInstance()->activeComponents[TRANSFORM].size() * dynamicAlignment);
// Flush to make changes visible to the host
//VkMappedMemoryRange memoryRange = {};
//memoryRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
//memoryRange.memory = dynamicUniformBuffersMemory[currentImage];
//memoryRange.size = VK_WHOLE_SIZE;
//vkFlushMappedMemoryRanges(device, 1, &memoryRange);
}
flush不知道有啥用,據(jù)說(shuō)有些顯卡必須要flush才會(huì)可見(jiàn),我的機(jī)器都一樣,所以上面都加了VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,試了下都去掉也不影響正常的uniform...
- command buffer里面逐一畫(huà)出來(lái)
for (uint32_t j = 0; j < ComponentManager::GetInstance()->activeComponents[TRANSFORM].size(); ++j)
{
uint32_t dynamicOffset = j * static_cast<uint32_t>(dynamicAlignment);
vkCmdBindDescriptorSets(commandBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSets[i], 1, &dynamicOffset);
vkCmdDrawIndexed(commandBuffers[i], static_cast<uint32_t>(indices.size()), INSTANCE_COUNT, 0, 0, 0);
}
我申請(qǐng)buffer的時(shí)候是按照物體最大數(shù)量的大小來(lái)申請(qǐng)的,但是畫(huà)的時(shí)候只畫(huà)實(shí)際生效的物體數(shù)量,因?yàn)閿?shù)量是錄在command buffer里的,所以一旦增刪物體需要重新錄制。
void VulkanAPI::flushCommandBuffer()
{
vkFreeCommandBuffers(device, commandPool, static_cast<uint32_t>(commandBuffers.size()), commandBuffers.data());
createCommandBuffers();
}

(暫時(shí)hard code了一下運(yùn)動(dòng),之前還做了一下INSTANCE DRAW所以是幾個(gè)方塊重疊的)