Beginning and ending command buffer recording without clearing the command buffer

Hi!
I’ve a problem with code :

void RenderTarget::draw(const Vertex* vertices, unsigned int vertexCount, sf::PrimitiveType type,
                      RenderStates states) {
             vertexBuffer.clear();
             for (unsigned int i = 0; i < vertexCount; i++) {
                vertexBuffer.append(vertices[i]);
             }
             if (type == sf::Quads) {
                for (unsigned int i = 0; i < vertexBuffer.getSize(); i+=4) {
                    vertexBuffer.addIndex(i);
                    vertexBuffer.addIndex(i+1);
                    vertexBuffer.addIndex(i+2);
                    vertexBuffer.addIndex(i);
                    vertexBuffer.addIndex(i+2);
                    vertexBuffer.addIndex(i+3);
                }
                type = sf::Triangles;
             }
             vertexBuffer.update();
             UniformBufferObject ubo;
             ubo.proj = m_view.getProjMatrix().getMatrix().transpose();
             ubo.proj.m22 *= -1;
             ubo.view = m_view.getViewMatrix().getMatrix().transpose();
             ubo.model = states.transform.getMatrix().transpose();
             updateUniformBuffer(getCurrentFrame(), ubo);
             createDescriptorSetLayout(states.texture);
             createGraphicPipeline(type, states);
             createDescriptorPool(states.texture);
             createDescriptorSets(states.texture);
             recordCommandBuffers();
             vertexBuffer.clearIndexes();

        }
void RenderTarget::recordCommandBuffers() {
            for (size_t i = 0; i < commandBuffers.size(); i++) {
                VkCommandBufferBeginInfo beginInfo{};
                beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;

                if (vkBeginCommandBuffer(commandBuffers[i], &beginInfo) != VK_SUCCESS) {
                    throw core::Erreur(0, "failed to begin recording command buffer!", 1);
                }
                VkRenderPassBeginInfo renderPassInfo{};
                renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
                renderPassInfo.renderPass = getRenderPass();
                renderPassInfo.framebuffer = getSwapchainFrameBuffers()[i];
                renderPassInfo.renderArea.offset = {0, 0};
                renderPassInfo.renderArea.extent = getSwapchainExtents();

                VkClearValue clrColor = {clearColor.r / 255.f,clearColor.g / 255.f, clearColor.b / 255.f, clearColor.a / 255.f};
                renderPassInfo.clearValueCount = 1;
                renderPassInfo.pClearValues = &clrColor;

                vkCmdBeginRenderPass(commandBuffers[i], &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
                vkCmdBindPipeline(commandBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipeline);
                VkBuffer vertexBuffers[] = {vertexBuffer.getVertexBuffer()};
                VkDeviceSize offsets[] = {0};
                vkCmdBindVertexBuffers(commandBuffers[i], 0, 1, vertexBuffers, offsets);
                vkCmdBindDescriptorSets(commandBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSets[getCurrentFrame()], 0, nullptr);

                if(vertexBuffer.getIndicesSize() > 0) {
                    vkCmdBindIndexBuffer(commandBuffers[i], vertexBuffer.getIndexBuffer(), 0, VK_INDEX_TYPE_UINT16);
                }
                if(vertexBuffer.getIndicesSize() > 0) {
                    vkCmdDrawIndexed(commandBuffers[i], static_cast<uint32_t>(vertexBuffer.getIndicesSize()), 1, 0, 0, 0);
                } else {
                    vkCmdDraw(commandBuffers[i], static_cast<uint32_t>(vertexBuffer.getSize()), 1, 0, 0);
                }

                vkCmdEndRenderPass(commandBuffers[i]);

                if (vkEndCommandBuffer(commandBuffers[i]) != VK_SUCCESS) {
                    throw core::Erreur(0, "failed to record command buffer!", 1);
                }

            }

        }

The problem is that when I want to draw multiple sprites here :

rt.clear();
        rt.draw(sprite);
        rt.draw(sprite2);
        rt.display();

Only the second sprite is draw it means that only the second command buffer recording is performed. I’m forced to submit the command buffer each time I draw to make it works. (the display function submit the command buffer)

rt.clear();
        rt.draw(sprite);
        rt.display();
        rt.draw(sprite2);
        rt.display();

But…this is not what I want! I want to draw everything and then submit the command buffer at last.

How can I dot it ?

Thanks.

Erf this is not possible…

https://stackoverflow.com/questions/76525807/is-it-possible-to-begin-then-end-a-vkcommandbuffer-multiple-times-before-submitt

… why would you end recording on a command buffer before you’re finished recording to it? The point of stopping recording is to say that you’re done. So don’t stop until you’re done.

I tried to do as you said :

I start recording when clearing the window :

void RenderTarget::clear(const sf::Color& color) {
             clearColor = color;
             VkClearColorValue clearValue{
                 .uint32 = {clearColor.r, clearColor.g, clearColor.b, clearColor.a}
             };
             VkImageSubresourceRange imageRange = {
                 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
                 .baseMipLevel = 0,
                 .levelCount = 1,
                 .baseArrayLayer = 0,
                 .layerCount = 1
             };

             for (unsigned int i = 0; i < commandBuffers.size(); i++) {
                vkResetCommandBuffer(commandBuffers[i], VK_COMMAND_BUFFER_RESET_RELEASE_RESOURCES_BIT);
                VkCommandBufferBeginInfo beginInfo{};
                beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
                if (vkBeginCommandBuffer(commandBuffers[i], &beginInfo) != VK_SUCCESS) {

                    throw core::Erreur(0, "failed to begin recording command buffer!", 1);
                }
                VkImageMemoryBarrier presentToClearBarrier {
                    .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
                    .pNext = nullptr,
                    .srcAccessMask = VK_ACCESS_MEMORY_READ_BIT,
                    .dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
                    .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
                    .newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
                    .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
                    .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
                    .image = getSwapchainImages()[i],
                    .subresourceRange = imageRange
                };
                VkImageMemoryBarrier clearToPresentBarrier {
                    .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
                    .pNext = nullptr,
                    .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
                    .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT,
                    .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
                    .newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
                    .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
                    .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
                    .image = getSwapchainImages()[i],
                    .subresourceRange = imageRange
                };
                vkCmdPipelineBarrier(commandBuffers[i], VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &presentToClearBarrier);
                vkCmdClearColorImage(commandBuffers[i], getSwapchainImages()[i], VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &clearValue, 1, &imageRange);
                vkCmdPipelineBarrier(commandBuffers[i], VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, 1, &clearToPresentBarrier);
                /*if (vkEndCommandBuffer(commandBuffers[i]) != VK_SUCCESS) {
                    throw core::Erreur(0, "failed to record command buffer!", 1);
                }*/
             }
        }

Then I perform all my draw calls :

void RenderTarget::draw(const Vertex* vertices, unsigned int vertexCount, sf::PrimitiveType type,
                      RenderStates states) {
             vertexBuffer.clear();
             for (unsigned int i = 0; i < vertexCount; i++) {
                vertexBuffer.append(vertices[i]);
             }
             if (type == sf::Quads) {
                for (unsigned int i = 0; i < vertexBuffer.getSize(); i+=4) {
                    vertexBuffer.addIndex(i);
                    vertexBuffer.addIndex(i+1);
                    vertexBuffer.addIndex(i+2);
                    vertexBuffer.addIndex(i);
                    vertexBuffer.addIndex(i+2);
                    vertexBuffer.addIndex(i+3);
                }
                type = sf::Triangles;
             }
             vertexBuffer.update();
             UniformBufferObject ubo;
             ubo.proj = m_view.getProjMatrix().getMatrix().transpose();
             ubo.proj.m22 *= -1;
             ubo.view = m_view.getViewMatrix().getMatrix().transpose();
             ubo.model = states.transform.getMatrix().transpose();
             updateUniformBuffer(getCurrentFrame(), ubo);
             createDescriptorSetLayout(states.texture);
             createGraphicPipeline(type, states);
             createDescriptorPool(states.texture);
             createDescriptorSets(states.texture);
             recordCommandBuffers();
             vertexBuffer.clearIndexes();

        }

And when it’s done I finish recording :

void RenderWindow::drawVulkanFrame() {
            if (getCommandBuffers().size() > 0) {
                for (unsigned int i = 0; i < getCommandBuffers().size(); i++) {
                    if (vkEndCommandBuffer(getCommandBuffers()[i]) != VK_SUCCESS) {
                        throw core::Erreur(0, "failed to record command buffer!", 1);
                    }
                }
                vkWaitForFences(vkDevice.getDevice(), 1, &inFlightFences[currentFrame], VK_TRUE, UINT64_MAX);
                uint32_t imageIndex;
                vkAcquireNextImageKHR(vkDevice.getDevice(), swapChain, UINT64_MAX, imageAvailableSemaphores[currentFrame], VK_NULL_HANDLE, &imageIndex);
                // Vérifier si une frame précédente est en train d'utiliser cette image (il y a une fence à attendre)
                if (imagesInFlight[imageIndex] != VK_NULL_HANDLE) {
                    vkWaitForFences(vkDevice.getDevice(), 1, &imagesInFlight[imageIndex], VK_TRUE, UINT64_MAX);
                }
                 // Marque l'image comme étant à nouveau utilisée par cette frame
                imagesInFlight[imageIndex] = inFlightFences[currentFrame];

                VkSubmitInfo submitInfo{};
                submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;

                VkSemaphore waitSemaphores[] = {imageAvailableSemaphores[currentFrame]};
                VkPipelineStageFlags waitStages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT};
                submitInfo.waitSemaphoreCount = 1;
                submitInfo.pWaitSemaphores = waitSemaphores;
                submitInfo.pWaitDstStageMask = waitStages;
                submitInfo.commandBufferCount = 1;
                submitInfo.pCommandBuffers = &getCommandBuffers()[imageIndex];
                VkSemaphore signalSemaphores[] = {renderFinishedSemaphores[currentFrame]};
                submitInfo.signalSemaphoreCount = 1;
                submitInfo.pSignalSemaphores = signalSemaphores;
                vkResetFences(vkDevice.getDevice(), 1, &inFlightFences[currentFrame]);
                /*VkResult result = vkQueueSubmit(vkDevice.getGraphicsQueue(), 1, &submitInfo, inFlightFences[currentFrame]);
                std::cout<<"render window result : "<<result<<std::endl;*/
                if (vkQueueSubmit(vkDevice.getGraphicsQueue(), 1, &submitInfo, inFlightFences[currentFrame]) != VK_SUCCESS) {
                    throw core::Erreur(0, "échec de l'envoi d'un command buffer!", 1);
                }
                VkPresentInfoKHR presentInfo{};
                presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;

                presentInfo.waitSemaphoreCount = 1;
                presentInfo.pWaitSemaphores = signalSemaphores;
                VkSwapchainKHR swapChains[] = {swapChain};
                presentInfo.swapchainCount = 1;
                presentInfo.pSwapchains = swapChains;
                presentInfo.pImageIndices = &imageIndex;
                presentInfo.pResults = nullptr; // Optionnel
                vkQueuePresentKHR(vkDevice.getPresentQueue(), &presentInfo);
                currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT;
                vkDeviceWaitIdle(vkDevice.getDevice());
            }
        }

But that doesn’t works, only one sprite is drawn on the window :

while (window.isOpen()) {
        rt.clear(sf::Color::Red);
        rt.draw(sprite);
        rt.display();
        rt2.clear();
        rt2.draw(sprite2);
        rt2.display();
        window.clear();
        window.draw(rtSprite);
        window.draw(rt2Sprite);
        window.display();
    }

Maybe it’s because I recreate the graphic pipeline in the second draw so when submiting the first graphic pipeline is gone.

But when I do this :

rt.clear(sf::Color::Red);
        rt.draw(sprite);
        rt.draw(sprite2);
        rt.display();
        rt2.clear();
        rt2.draw(sprite2);
        rt2.display();
        window.clear();
        window.draw(rtSprite);
        //window.draw(rt2Sprite);
        window.display();

The two sprites are drawn…, they are just drawn at the same place because it apply the second uniform buffer updates for both sprites…
So why it draw both sprite for render textures but not on the renderwindow with the same code ?
And how to apply different uniform buffer values for both sprites ?

Ok I found vulkan is very not practice to use.

At each beginning of command buffer recording, the buffer is cleared, I tried to make several submits, so I tried to do this :

-Acquiring en image.
-Submitting graphics work.
-Presenting.

But that doesn’t work if I don’t put all this code in the same function like this :

void RenderWindow::drawVulkanFrame() {
            
                vkWaitForFences(vkDevice.getDevice(), 1, &inFlightFences[currentFrame], VK_TRUE, UINT64_MAX);
                uint32_t imageIndex;
                vkAcquireNextImageKHR(vkDevice.getDevice(), swapChain, UINT64_MAX, imageAvailableSemaphores[currentFrame], VK_NULL_HANDLE, &imageIndex);
                // Vérifier si une frame précédente est en train d'utiliser cette image (il y a une fence à attendre)
                if (imagesInFlight[imageIndex] != VK_NULL_HANDLE) {
                    vkWaitForFences(vkDevice.getDevice(), 1, &imagesInFlight[imageIndex], VK_TRUE, UINT64_MAX);
                }
                 // Marque l'image comme étant à nouveau utilisée par cette frame
                imagesInFlight[imageIndex] = inFlightFences[currentFrame];

                VkSubmitInfo submitInfo{};
                submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;

                VkSemaphore waitSemaphores[] = {imageAvailableSemaphores[currentFrame]};
                VkPipelineStageFlags waitStages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT};
                submitInfo.waitSemaphoreCount = 1;
                submitInfo.pWaitSemaphores = waitSemaphores;
                submitInfo.pWaitDstStageMask = waitStages;
                submitInfo.commandBufferCount = 1;
                submitInfo.pCommandBuffers = &getCommandBuffers()[imageIndex];
                VkSemaphore signalSemaphores[] = {renderFinishedSemaphores[currentFrame]};
                submitInfo.signalSemaphoreCount = 1;
                submitInfo.pSignalSemaphores = signalSemaphores;
                vkResetFences(vkDevice.getDevice(), 1, &inFlightFences[currentFrame]);
                /*VkResult result = vkQueueSubmit(vkDevice.getGraphicsQueue(), 1, &submitInfo, inFlightFences[currentFrame]);
                std::cout<<"render window result : "<<result<<std::endl;*/
                if (vkQueueSubmit(vkDevice.getGraphicsQueue(), 1, &submitInfo, inFlightFences[currentFrame]) != VK_SUCCESS) {
                    throw core::Erreur(0, "échec de l'envoi d'un command buffer!", 1);
                }
                VkPresentInfoKHR presentInfo{};
                presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;

                presentInfo.waitSemaphoreCount = 1;
                presentInfo.pWaitSemaphores = signalSemaphores;
                VkSwapchainKHR swapChains[] = {swapChain};
                presentInfo.swapchainCount = 1;
                presentInfo.pSwapchains = swapChains;
                presentInfo.pImageIndices = &imageIndex;
                presentInfo.pResults = nullptr; // Optionnel
                vkQueuePresentKHR(vkDevice.getPresentQueue(), &presentInfo);
                currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT;
                vkDeviceWaitIdle(vkDevice.getDevice());
            }
        }

I’ve a bunch a errors :

It tells me a semaphore is signaled when acquiring the swapchain I don’t know why.
It tells me the image acquiring is exceeded the maximum swapchain images I don’t know why.
And it tells me a semaphore is never signaled I don’t know why.

So I tried to submit all draw command at once like this :

-I start recording.
-I record all draw calls.
-I end recording before the presentation.

void RenderWindow::clear(const sf::Color& color) {
             clearColor = color;
             VkClearColorValue clearValue{
                 .uint32 = {clearColor.r, clearColor.g, clearColor.b, clearColor.a}
             };
             VkImageSubresourceRange imageRange = {
                 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
                 .baseMipLevel = 0,
                 .levelCount = 1,
                 .baseArrayLayer = 0,
                 .layerCount = 1
             };

             for (unsigned int i = 0; i < getCommandBuffers().size(); i++) {
                VkCommandBufferBeginInfo beginInfo{};
                beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
                if (vkBeginCommandBuffer(getCommandBuffers()[i], &beginInfo) != VK_SUCCESS) {

                    throw core::Erreur(0, "failed to begin recording command buffer!", 1);
                }
                VkImageMemoryBarrier presentToClearBarrier {
                    .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
                    .pNext = nullptr,
                    .srcAccessMask = VK_ACCESS_MEMORY_READ_BIT,
                    .dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
                    .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
                    .newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
                    .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
                    .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
                    .image = getSwapchainImages()[i],
                    .subresourceRange = imageRange
                };
                VkImageMemoryBarrier clearToPresentBarrier {
                    .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
                    .pNext = nullptr,
                    .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
                    .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT,
                    .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
                    .newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
                    .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
                    .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
                    .image = getSwapchainImages()[i],
                    .subresourceRange = imageRange
                };
                vkCmdPipelineBarrier(getCommandBuffers()[i], VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &presentToClearBarrier);
                vkCmdClearColorImage(getCommandBuffers()[i], getSwapchainImages()[i], VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &clearValue, 1, &imageRange);
                vkCmdPipelineBarrier(getCommandBuffers()[i], VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, 1, &clearToPresentBarrier);
                /*if (vkEndCommandBuffer(commandBuffers[i]) != VK_SUCCESS) {
                    throw core::Erreur(0, "failed to record command buffer!", 1);
                }*/
             }
        }
void RenderTarget::draw(const Vertex* vertices, unsigned int vertexCount, sf::PrimitiveType type,
                      RenderStates states) {
             vertexBuffer.clear();
             for (unsigned int i = 0; i < vertexCount; i++) {
                vertexBuffer.append(vertices[i]);
             }
             if (type == sf::Quads) {
                for (unsigned int i = 0; i < vertexBuffer.getSize(); i+=4) {
                    vertexBuffer.addIndex(i);
                    vertexBuffer.addIndex(i+1);
                    vertexBuffer.addIndex(i+2);
                    vertexBuffer.addIndex(i);
                    vertexBuffer.addIndex(i+2);
                    vertexBuffer.addIndex(i+3);
                }
                type = sf::Triangles;
             }
             vertexBuffer.update();
             UniformBufferObject ubo;
             ubo.proj = m_view.getProjMatrix().getMatrix().transpose();
             ubo.proj.m22 *= -1;
             ubo.view = m_view.getViewMatrix().getMatrix().transpose();
             ubo.model = states.transform.getMatrix().transpose();
             updateUniformBuffer(getCurrentFrame(), ubo);
             createDescriptorSetLayout(states.texture);
             createGraphicPipeline(type, states);
             createDescriptorPool(states.texture);
             createDescriptorSets(states.texture);
             recordCommandBuffers();
             vertexBuffer.clearIndexes();

        }
void RenderTarget::recordCommandBuffers() {
            for (size_t i = 0; i < commandBuffers.size(); i++) {

                /*VkCommandBufferBeginInfo beginInfo{};
                beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;

                if (vkBeginCommandBuffer(commandBuffers[i], &beginInfo) != VK_SUCCESS) {
                    throw core::Erreur(0, "failed to begin recording command buffer!", 1);
                }*/
                VkRenderPassBeginInfo renderPassInfo{};
                renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
                renderPassInfo.renderPass = getRenderPass();
                renderPassInfo.framebuffer = getSwapchainFrameBuffers()[i];
                renderPassInfo.renderArea.offset = {0, 0};
                renderPassInfo.renderArea.extent = getSwapchainExtents();

                VkClearValue clrColor = {clearColor.r / 255.f,clearColor.g / 255.f, clearColor.b / 255.f, clearColor.a / 255.f};
                renderPassInfo.clearValueCount = 1;
                renderPassInfo.pClearValues = &clrColor;



                vkCmdBeginRenderPass(commandBuffers[i], &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
                vkCmdBindPipeline(commandBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipeline);
                VkBuffer vertexBuffers[] = {vertexBuffer.getVertexBuffer()};
                VkDeviceSize offsets[] = {0};
                vkCmdBindVertexBuffers(commandBuffers[i], 0, 1, vertexBuffers, offsets);
                vkCmdBindDescriptorSets(commandBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSets[getCurrentFrame()], 0, nullptr);

                if(vertexBuffer.getIndicesSize() > 0) {
                    vkCmdBindIndexBuffer(commandBuffers[i], vertexBuffer.getIndexBuffer(), 0, VK_INDEX_TYPE_UINT16);
                }
                if(vertexBuffer.getIndicesSize() > 0) {
                    vkCmdDrawIndexed(commandBuffers[i], static_cast<uint32_t>(vertexBuffer.getIndicesSize()), 1, 0, 0, 0);
                } else {
                    vkCmdDraw(commandBuffers[i], static_cast<uint32_t>(vertexBuffer.getSize()), 1, 0, 0);
                }

                vkCmdEndRenderPass(commandBuffers[i]);

                /*if (vkEndCommandBuffer(commandBuffers[i]) != VK_SUCCESS) {
                    throw core::Erreur(0, "failed to record command buffer!", 1);
                }*/

            }

        }
void RenderWindow::drawVulkanFrame() {
            if (getCommandBuffers().size() > 0) {
                for (unsigned int i = 0; i < getCommandBuffers().size(); i++) {
                    if (vkEndCommandBuffer(getCommandBuffers()[i]) != VK_SUCCESS) {
                        throw core::Erreur(0, "failed to record command buffer!", 1);
                    }
                }
                vkWaitForFences(vkDevice.getDevice(), 1, &inFlightFences[currentFrame], VK_TRUE, UINT64_MAX);
                uint32_t imageIndex;
                vkAcquireNextImageKHR(vkDevice.getDevice(), swapChain, UINT64_MAX, imageAvailableSemaphores[currentFrame], VK_NULL_HANDLE, &imageIndex);
                // Vérifier si une frame précédente est en train d'utiliser cette image (il y a une fence à attendre)
                if (imagesInFlight[imageIndex] != VK_NULL_HANDLE) {
                    vkWaitForFences(vkDevice.getDevice(), 1, &imagesInFlight[imageIndex], VK_TRUE, UINT64_MAX);
                }
                 // Marque l'image comme étant à nouveau utilisée par cette frame
                imagesInFlight[imageIndex] = inFlightFences[currentFrame];

                VkSubmitInfo submitInfo{};
                submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;

                VkSemaphore waitSemaphores[] = {imageAvailableSemaphores[currentFrame]};
                VkPipelineStageFlags waitStages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT};
                submitInfo.waitSemaphoreCount = 1;
                submitInfo.pWaitSemaphores = waitSemaphores;
                submitInfo.pWaitDstStageMask = waitStages;
                submitInfo.commandBufferCount = 1;
                submitInfo.pCommandBuffers = &getCommandBuffers()[imageIndex];
                VkSemaphore signalSemaphores[] = {renderFinishedSemaphores[currentFrame]};
                submitInfo.signalSemaphoreCount = 1;
                submitInfo.pSignalSemaphores = signalSemaphores;
                vkResetFences(vkDevice.getDevice(), 1, &inFlightFences[currentFrame]);
                /*VkResult result = vkQueueSubmit(vkDevice.getGraphicsQueue(), 1, &submitInfo, inFlightFences[currentFrame]);
                std::cout<<"render window result : "<<result<<std::endl;*/
                if (vkQueueSubmit(vkDevice.getGraphicsQueue(), 1, &submitInfo, inFlightFences[currentFrame]) != VK_SUCCESS) {
                    throw core::Erreur(0, "échec de l'envoi d'un command buffer!", 1);
                }
                VkPresentInfoKHR presentInfo{};
                presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;

                presentInfo.waitSemaphoreCount = 1;
                presentInfo.pWaitSemaphores = signalSemaphores;
                VkSwapchainKHR swapChains[] = {swapChain};
                presentInfo.swapchainCount = 1;
                presentInfo.pSwapchains = swapChains;
                presentInfo.pImageIndices = &imageIndex;
                presentInfo.pResults = nullptr; // Optionnel
                vkQueuePresentKHR(vkDevice.getPresentQueue(), &presentInfo);
                currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT;
                vkDeviceWaitIdle(vkDevice.getDevice());
            }
        }
        void RenderWindow::cleanupSwapchain() {
            for (size_t i = 0; i < swapChainFramebuffers.size(); i++) {
                vkDestroyFramebuffer(vkDevice.getDevice(), swapChainFramebuffers[i], nullptr);
            }
            for (size_t i = 0; i < swapChainImageViews.size(); i++) {
                vkDestroyImageView(vkDevice.getDevice(), swapChainImageViews[i], nullptr);
            }

            vkDestroySwapchainKHR(vkDevice.getDevice(), swapChain, nullptr);
            vkDestroyRenderPass(vkDevice.getDevice(), renderPass, nullptr);
        }

But it cause another problem, my buffers are updated at each draw calls, so, the values passed to the shaders are only the ones from the last buffer update.

And I don’t know how to fix this.

Thanks.

Ok I’ve just forgot to change op_clear to op_load for the render window when creating the render pass.

But I don’t know how to solve this problem with my buffers because as I submit all drawcalls in once, I can’t update the buffer, making the first draw call, updating the buffer again and making the second draw call, it doesn’t work.

I guess I need to use several buffers but I don’t know how to set up the descriptors for this.

I’ve lost track of what buffers you are referring to here, but I’m guessing you either mean a buffer that contains vertex data or uniform data?
For vertex data, you should populate that buffer once and only if any of the vertex data changes between frames update the contents.
For uniform data there are often things that change at different frequencies: once per frame (e.g. camera position/view projection matrix), per object (e.g. transformation matrix); possibly more. For things that change within a frame (e.g. per object) have a buffer where you append the values for each object and adjust the offset to the data within the buffer before each draw call.

If you don’t want to introduce waits (via vkDeviceWaitIdle or finer grained synchronization) until the GPU is done processing all work that references one of these buffers, before you can start overwriting the contents for the next frame, you may need multiple copies of these buffers.

Overall, from skimming some of the code you’ve posted, you are not doing things the way modern GPUs want you to - while that is unusual and not necessarily what the API designers had in mind it’s also fine as long as you are aware of it.
Very simplified modern GPUs want you to do some setup work up front to prepare all the data you are going to work on for a frame and then submit big chunks of work that operate on that data - that’s usually when their internal schedulers can achieve the highest utilization of the compute units (and thus best perf).

Ok, j’ai fait un buffer par drawable objet et ça règle le problème…