I am building a 3d renderer in C++ using Vulkan. Note that this is the C++ implementation of Vulkan. I am trying to incorporate shadow mapping into my rendering pipeline. I have created an image, frame buffer, render pass and command buffer to handle the shadow mapping. Below is my current code to handle frame rendering for my main render pass. The rendering of specific pipelines happens between the BeginFrame and EndFrame functions. Can you modify the below code to incorporate the shadow mapping while ensuring that any synchronisation concerns are handled?

void Renderer::BeginFrame()
{
    // Wait for any previous work on this swapchain image to complete
    device.waitForFences(1, &inFlightFences[currentFrame], VK_TRUE, UINT64_MAX);
    device.resetFences(1, &inFlightFences[currentFrame]);

    // Acquire an image from the swapchain, then begin recording commands for the current frame.
    vk::Result acquireResult = device.acquireNextImageKHR(swapchain, UINT64_MAX, imageAvailableSemaphores[currentFrame], vk::Fence(), &imageIndex);

    if (acquireResult != vk::Result::eSuccess && acquireResult != vk::Result::eSuboptimalKHR) {
        throw std::runtime_error("Failed to acquire next swapchain image.");
    }

    vk::CommandBufferBeginInfo beginInfo{};
    beginInfo.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit;

    currentCmdBufferIndex = (currentCmdBufferIndex + 1) % 2;
    currentCommandBuffer = commandBuffers[currentFrame * 2 + currentCmdBufferIndex];

    currentCommandBuffer.begin(beginInfo);

    vk::RenderPassBeginInfo renderPassInfo{};
    renderPassInfo.renderPass = renderPass;
    renderPassInfo.framebuffer = framebuffers[imageIndex];
    renderPassInfo.renderArea.offset = vk::Offset2D{ 0, 0 };
    renderPassInfo.renderArea.extent = swapChainExtent;

    // Set the clear color to black
    vk::ClearValue clearColor = { vk::ClearColorValue(std::array<float, 4>{0.0f, 0.0f, 0.0f, 1.0f}) };
    vk::ClearValue clearDepth = { vk::ClearDepthStencilValue(1.0f, 0) }; // Specify the depth clear value here

    std::array<vk::ClearValue, 7> clearValues = { clearColor, clearColor, clearColor, clearColor, clearDepth, clearColor, clearColor}; // Add the clearDepth value to the array

    renderPassInfo.clearValueCount = static_cast<uint32_t>(clearValues.size());
    renderPassInfo.pClearValues = clearValues.data(); // Pass the clearValues array to pClearValues

    currentCommandBuffer.beginRenderPass(renderPassInfo, vk::SubpassContents::eInline);
}

void Renderer::EndFrame()
{
    currentCommandBuffer.endRenderPass();

    vk::SubmitInfo submitInfo{};

    vk::PipelineStageFlags waitStages[] = { vk::PipelineStageFlagBits::eColorAttachmentOutput };
    submitInfo.waitSemaphoreCount = 1;
    submitInfo.pWaitSemaphores = &imageAvailableSemaphores[currentFrame];
    submitInfo.pWaitDstStageMask = waitStages;

    submitInfo.commandBufferCount = 1;
    submitInfo.pCommandBuffers = &currentCommandBuffer;

    submitInfo.signalSemaphoreCount = 1;
    submitInfo.pSignalSemaphores = &renderFinishedSemaphores[currentFrame];

    currentCommandBuffer.end();
    graphicsQueue.submit(1, &submitInfo, inFlightFences[currentFrame]);

    vk::PresentInfoKHR presentInfo{};
    presentInfo.waitSemaphoreCount = 1;
    presentInfo.pWaitSemaphores = &renderFinishedSemaphores[currentFrame];

    vk::SwapchainKHR swapChains[] = { swapchain };
    presentInfo.swapchainCount = 1;
    presentInfo.pSwapchains = swapChains;
    presentInfo.pImageIndices = &imageIndex;

    vk::Result queuePresentResult = presentQueue.presentKHR(presentInfo);

    if (queuePresentResult == vk::Result::eErrorOutOfDateKHR || queuePresentResult == vk::Result::eSuboptimalKHR) {
        // Handle swapchain recreation if needed, e.g., due to resizing the window or other swapchain properties changes
    }
    else if (queuePresentResult != vk::Result::eSuccess) {
        throw std::runtime_error("Failed to present the swapchain image.");
    }

    currentFrame = (currentFrame + 1) % kMaxFramesInFlight;
}