I’ve gotten one of the basic Vulkan examples working and it’s performing very well. Highest marks.
I’ve been doing various forms of graphics programming for a couple of decades - but never worked in kernel pipeline code. The terminology your team takes for granted is virtually Sanskrit at times. It’s taken me several weeks to understand that the ‘swap chain’ is the list of off screen buffers my code draws into.
I’m completely blocked on how to do something like this - in pseudo code
SceneNodeList {
SceneNode0 { transform, mesh, vertex color, normals}
SceneNode1 { transform, lines, vertex color }
SceneNode2 { transform, obj mesh, texture image, mipmaps...}
SceneNode3 { 2d overlay }
}
for (SceneNode: ScenNodeList )
drawSceneNode(SceneNode);
In theory, such a list would draw a mix of 3D content, and then overwrite parts of it with a 2D UI frame. Inelegant, but fairly error proof. No clipping regions etc.
Each scene node could use a different shader, texture etc. Including no texture. They definitely will have their own transforms which are are concatenated with the view and root model transforms. That could result in hundreds or thousands of uniform blocks. Do they all go into one flat command stream?
For that matter, please define ‘command.’ I’m assuming it is equivalent to ‘draw call’ or ‘draw instruction’ and the the command list is like a script of openGL calls.
I’m blocked right now, because the highest priority case is the no texture CAD image. I can get all of the game images to work great, but if I don’t have a texture…
vkUpdateDescriptorSets
throws an exception. Apparently setting descriptorCount= 0
, isn’t legal.
If can omit some of the textures and a two pass draw so the textured models go first and the order is preserved works, but it’s an ugly solution.
My cad image looks a bit weird when I have to have an obj format potted plant in the scene to make the code work.
The code is large and scattered, here are the most pertinent snippets.
class Model : public SceneNode{...};
void Model::addCommands(VkCommandBuffer cmdBuff, bool hasImage, VkPipelineLayout pipelineLayout, VkDescriptorSet& descSet) {
if (hasImage) {
if (_textureImage) {
VkBuffer vertexBuffers[] = { getVertexBuffer() };
VkDeviceSize offsets[] = { 0 };
vkCmdBindVertexBuffers(cmdBuff, 0, 1, vertexBuffers, offsets);
vkCmdBindIndexBuffer(cmdBuff, getIndexBuffer(), 0, VK_INDEX_TYPE_UINT32);
vkCmdBindDescriptorSets(cmdBuff, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descSet, 0, nullptr);
vkCmdDrawIndexed(cmdBuff, numIndices(), 1, 0, 0, 0);
}
} else {
if (!_textureImage) {
VkBuffer vertexBuffers[] = { getVertexBuffer() };
VkDeviceSize offsets[] = { 0 };
vkCmdBindVertexBuffers(cmdBuff, 0, 1, vertexBuffers, offsets);
vkCmdBindIndexBuffer(cmdBuff, getIndexBuffer(), 0, VK_INDEX_TYPE_UINT32);
vkCmdBindDescriptorSets(cmdBuff, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descSet, 0, nullptr);
vkCmdDrawIndexed(cmdBuff, numIndices(), 1, 0, 0, 0);
}
}
}
void Model::buildImageInfoList(std::vector<VkDescriptorImageInfo>& imageInfoList) {
const auto& texture = getTexture();
if (texture) {
VkDescriptorImageInfo info;
info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
info.imageView = *texture;
info.sampler = *texture;
imageInfoList.push_back(info);
}
}
void VulkanApp::createDescriptorSets() {
std::vector<VkDescriptorSetLayout> layouts(_swapChain._images.size(), descriptorSetLayout);
VkDescriptorSetAllocateInfo allocInfo = {};
allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
allocInfo.descriptorPool = descriptorPool;
allocInfo.descriptorSetCount = static_cast<uint32_t>(_swapChain._images.size());
allocInfo.pSetLayouts = layouts.data();
descriptorSets.resize(_swapChain._images.size());
if (vkAllocateDescriptorSets(deviceContext.device_, &allocInfo, descriptorSets.data()) != VK_SUCCESS) {
throw std::runtime_error("failed to allocate descriptor sets!");
}
if (imageInfoList.empty())
descriptorWrites.resize(1);
else
descriptorWrites.resize(2);
descriptorWrites[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
descriptorWrites[0].dstSet = descriptorSets[i];
descriptorWrites[0].dstBinding = 0;
descriptorWrites[0].dstArrayElement = 0;
descriptorWrites[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
descriptorWrites[0].descriptorCount = 1;
descriptorWrites[0].pBufferInfo = &bufferInfo;
if (!imageInfoList.empty()) {
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_COMBINED_IMAGE_SAMPLER;
descriptorWrites[1].descriptorCount = static_cast<uint32_t>(imageInfoList.size());
descriptorWrites[1].pImageInfo = imageInfoList.data();
}
vkUpdateDescriptorSets(deviceContext.device_, static_cast<uint32_t>(descriptorWrites.size()), descriptorWrites.data(), 0, nullptr);
It’s pretty clear that I probably need multiple pipelines and possibly more. The way the examples are structured, I can’t tell which portions are device 1-1, surface 1-1, sceneNode 1-1 etc.