I am building a 3d renderer in C++ using Vulkan. I am using the C++ implementation of Vulkan. I've been trying to implement shadow mapping to my renderer. I've set up a separate render pass and command buffer to handle the shadow mapping and I have another render pass and command buffer to render the scene. I am no longer getting any validation errors when running the renderer. I am now trying to validate that the shadow mapping is working as intended by rendering the outputs to a texture within the scene. The transitions between the depth stencil and the output image are being handled using subpass dependencies in the shadow render pass. While testing the shadow mapping output, the resulting texture is now entirely red. Do you know what might be causing this problem?

Here is some of the code:

Shadow Render Pass:

vk::AttachmentDescription depthAttachment{};
    depthAttachment.format = FindDepthFormat(*GetPhysicalDevice()); // High precision format
    depthAttachment.samples = vk::SampleCountFlagBits::e1;
    depthAttachment.loadOp = vk::AttachmentLoadOp::eClear;
    depthAttachment.storeOp = vk::AttachmentStoreOp::eStore;
    depthAttachment.stencilLoadOp = vk::AttachmentLoadOp::eDontCare;
    depthAttachment.stencilStoreOp = vk::AttachmentStoreOp::eDontCare;
    depthAttachment.initialLayout = vk::ImageLayout::eUndefined;
    depthAttachment.finalLayout = vk::ImageLayout::eDepthStencilAttachmentOptimal;

    vk::AttachmentReference depthAttachmentRef{};
    depthAttachmentRef.attachment = 0;
    depthAttachmentRef.layout = vk::ImageLayout::eDepthStencilAttachmentOptimal;

    vk::SubpassDescription subpass{};
    subpass.pipelineBindPoint = vk::PipelineBindPoint::eGraphics;
    subpass.colorAttachmentCount = 0;
    subpass.pDepthStencilAttachment = &depthAttachmentRef;

    // Use subpass dependencies for layout transitions
    std::array<vk::SubpassDependency, 2> dependencies;

    dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL;
    dependencies[0].dstSubpass = 0;
    dependencies[0].srcStageMask = vk::PipelineStageFlagBits::eFragmentShader;// VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
    dependencies[0].dstStageMask = vk::PipelineStageFlagBits::eEarlyFragmentTests;// VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;
    dependencies[0].srcAccessMask = vk::AccessFlagBits::eShaderRead; //VK_ACCESS_SHADER_READ_BIT;
    dependencies[0].dstAccessMask = vk::AccessFlagBits::eDepthStencilAttachmentWrite; //VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
    dependencies[0].dependencyFlags = vk::DependencyFlagBits::eByRegion; //VK_DEPENDENCY_BY_REGION_BIT;

    dependencies[1].srcSubpass = 0;
    dependencies[1].dstSubpass = VK_SUBPASS_EXTERNAL;
    dependencies[1].srcStageMask = vk::PipelineStageFlagBits::eLateFragmentTests;// VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
    dependencies[1].dstStageMask = vk::PipelineStageFlagBits::eFragmentShader; //VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
    dependencies[1].srcAccessMask = vk::AccessFlagBits::eDepthStencilAttachmentWrite; //VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
    dependencies[1].dstAccessMask = vk::AccessFlagBits::eShaderRead;// VK_ACCESS_SHADER_READ_BIT;
    dependencies[1].dependencyFlags = vk::DependencyFlagBits::eByRegion;// VK_DEPENDENCY_BY_REGION_BIT;


    vk::RenderPassCreateInfo renderPassInfo{};
    renderPassInfo.attachmentCount = 1;
    renderPassInfo.pAttachments = &depthAttachment;
    renderPassInfo.subpassCount = 1;
    renderPassInfo.pSubpasses = &subpass;
    renderPassInfo.dependencyCount = static_cast<uint32_t>(dependencies.size());
    renderPassInfo.pDependencies = dependencies.data();

    if (device.createRenderPass(&renderPassInfo, nullptr, &shadowRenderPass) != vk::Result::eSuccess)
    {
        throw std::runtime_error("Failed to create render pass.");
    }


The Begin Shadow Frame:

// 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;

    // Shadow Pass
    shadowCommandBuffers[0].begin(beginInfo);

    vk::RenderPassBeginInfo shadowPassInfo{};
    shadowPassInfo.renderPass = shadowRenderPass;
    shadowPassInfo.framebuffer = shadowFramebuffers[0];
    shadowPassInfo.renderArea.offset = vk::Offset2D{ 0, 0 };
    shadowPassInfo.renderArea.extent = vk::Extent2D(SHADOW_WIDTH, SHADOW_HEIGHT);

    vk::ClearValue shadowClearValue = { vk::ClearDepthStencilValue(1.0f, 0) };
    shadowPassInfo.clearValueCount = 1;
    shadowPassInfo.pClearValues = &shadowClearValue;

    shadowCommandBuffers[0].beginRenderPass(shadowPassInfo, vk::SubpassContents::eInline);
    // (Render scene from light perspective)

The End Shadow Frame:

    shadowCommandBuffers[0].endRenderPass();

    shadowCommandBuffers[0].end();

    vk::SubmitInfo shadowSubmitInfo{};
    shadowSubmitInfo.commandBufferCount = 1;
    shadowSubmitInfo.pCommandBuffers = &shadowCommandBuffers[0];

    vk::Semaphore waitSemaphores[] = { imageAvailableSemaphores[currentFrame] };
    vk::PipelineStageFlags waitStages[] = { vk::PipelineStageFlagBits::eColorAttachmentOutput };
    shadowSubmitInfo.waitSemaphoreCount = 1;
    shadowSubmitInfo.pWaitSemaphores = waitSemaphores;
    shadowSubmitInfo.pWaitDstStageMask = waitStages;

    vk::Semaphore signalSemaphores[] = { shadowMapCompletedSemaphore };
    shadowSubmitInfo.signalSemaphoreCount = 1;
    shadowSubmitInfo.pSignalSemaphores = signalSemaphores;

    graphicsQueue.submit(1, &shadowSubmitInfo, nullptr);

The Shadow Image creation:

        imageInfo.imageType = vk::ImageType::e2D;
        imageInfo.format = FindDepthFormat(*GetPhysicalDevice();
        imageInfo.extent = vk::Extent3D(SHADOW_WIDTH, SHADOW_HEIGHT, 1);
        imageInfo.mipLevels = 1;
        imageInfo.arrayLayers = 1;
        imageInfo.samples = vk::SampleCountFlagBits::e1;
        imageInfo.tiling = vk::ImageTiling::eOptimal;
        imageInfo.usage = vk::ImageUsageFlagBits::eDepthStencilAttachment | vk::ImageUsageFlagBits::eSampled;
        imageInfo.sharingMode = vk::SharingMode::eExclusive;

The Shadow Render:

VkDevice device = *renderer.GetDevice();

	//glm::mat4 modelMatrix;
	glm::vec3 position = glm::vec3(0.0f, 0.0f, 10.0f);
	glm::vec3 rotation = glm::vec3(0.0f, 0.0f, 0.0f);
	glm::vec3 scale = glm::vec3(0.2f);

	glm::vec3 position2 = light.position;// glm::vec3(0.0f, 5.0f, 0.0f);//
	glm::vec3 position3 = glm::vec3(0.0f, 0.0f, 0.0f);

	// Bind mesh vertex and index buffers
	VkBuffer vertexBuffers[] = { meshes[0].get()->GetVertexBuffer() };
	VkDeviceSize offsets[] = { 0 };
	vkCmdBindVertexBuffers(renderer.shadowCommandBuffers[0], 0, 1, vertexBuffers, offsets);
	vkCmdBindIndexBuffer(renderer.shadowCommandBuffers[0], meshes[0].get()->GetIndexBuffer(), 0, VK_INDEX_TYPE_UINT32);


	glm::mat4 modelMatrix[] = { glm::mat4(1.0f),glm::mat4(1.0f),glm::mat4(1.0f) };
	modelMatrix[0] = glm::translate(modelMatrix[0], position);
	modelMatrix[1] = glm::translate(modelMatrix[1], position2);
	modelMatrix[2] = glm::translate(modelMatrix[2], position3);

	modelMatrix[1] = glm::scale(modelMatrix[1], scale);

	BufferUtils::UpdateBuffer(device, modelMatrixBufferobj.memory, sizeof(glm::mat4) * modelcount, modelMatrix);

	float near_plane = 1.0f, far_plane = 7.5f;
	glm::mat4 lightProjection = glm::ortho(-10.0f, 10.0f, -10.0f, 10.0f, near_plane, far_plane);

	glm::mat4 lightView = glm::lookAt(light.position,
		glm::vec3(0.0f, 0.0f, 0.0f),
		glm::vec3(0.0f, 1.0f, 0.0f));

	glm::mat4 lightSpaceMatrix = lightProjection * lightView;

	BufferUtils::UpdateBuffer(device, lightSpaceBufferobj.memory, sizeof(glm::mat4), &lightSpaceMatrix);

	vkDeviceWaitIdle(device);

	// Geometry Pass

	VkDescriptorSet shadowSet = this->shadowdescriptorSet.get();
	vkCmdBindPipeline(renderer.shadowCommandBuffers[0], VK_PIPELINE_BIND_POINT_GRAPHICS, renderer.GetPipeline(3).get()->GetPipeline());
	vkCmdBindDescriptorSets(renderer.shadowCommandBuffers[0], VK_PIPELINE_BIND_POINT_GRAPHICS, shadowpipelineLayout, 0, 1, &shadowSet, 0, nullptr);

	uint32_t numIndices4 = static_cast<uint32_t>(meshes[0].get()->GetIndices().size());
	vkCmdDrawIndexed(renderer.shadowCommandBuffers[0], numIndices4, modelcount, 0, 0, 0);

The Scene Render:

VkDevice device = *renderer.GetDevice();

	//glm::mat4 modelMatrix;
	glm::vec3 position = glm::vec3(0.0f, 0.0f, 10.0f);
	glm::vec3 rotation = glm::vec3(0.0f, 0.0f, 0.0f);
	glm::vec3 scale = glm::vec3(0.2f);

	glm::vec3 position2 = light.position;// glm::vec3(0.0f, 5.0f, 0.0f);//
	glm::vec3 position3 = glm::vec3(0.0f, 0.0f, 0.0f);

	// Bind mesh vertex and index buffers
	VkBuffer vertexBuffers[] = { meshes[0].get()->GetVertexBuffer() };
	VkDeviceSize offsets[] = { 0 };
	vkCmdBindVertexBuffers(*renderer.GetCurrentCommandBuffer(), 0, 1, vertexBuffers, offsets);
	vkCmdBindIndexBuffer(*renderer.GetCurrentCommandBuffer(), meshes[0].get()->GetIndexBuffer(), 0, VK_INDEX_TYPE_UINT32);


	glm::mat4 modelMatrix[] = { glm::mat4(1.0f),glm::mat4(1.0f),glm::mat4(1.0f) };
	modelMatrix[0] = glm::translate(modelMatrix[0], position);
	modelMatrix[1] = glm::translate(modelMatrix[1], position2);
	modelMatrix[2] = glm::translate(modelMatrix[2], position3);

	modelMatrix[1] = glm::scale(modelMatrix[1], scale);

	//modelcount = 2;

	AmbientLight ambientUBO = {};

	ambientUBO.color = { 1.0, 1.0, 1.0 };
	ambientUBO.intensity = 0.05;

	DirectionalLight directionalUBO = {};

	//directionalUBO.position = { 1.0, 1.0, 1.0 };
	directionalUBO.color = { 1.0, 1.0, 1.0 };
	directionalUBO.intensity = 1.0;

	directionalUBO.position = light.position;

	SpecularData specularUBO = {};

	specularUBO.intensity = 0.5;
	specularUBO.shininess = 32;

	CameraData cameraUBO = {};

	cameraUBO.position = camera.GetPosition();

	BufferUtils::UpdateBuffer(device, modelMatrixBufferobj.memory, sizeof(glm::mat4) * modelcount, modelMatrix);
	BufferUtils::UpdateBuffer(device, viewMatrixBufferobj.memory, sizeof(glm::mat4), &camera.GetViewMatrix());
	BufferUtils::UpdateBuffer(device, projMatrixBufferobj.memory, sizeof(glm::mat4), &camera.GetProjectionMatrix());
	BufferUtils::UpdateBuffer(device, modelCountBufferobj.memory, sizeof(int), &modelcount);
	BufferUtils::UpdateBuffer(device, ambientLightBufferobj.memory, sizeof(AmbientLight), &ambientUBO);
	BufferUtils::UpdateBuffer(device, directionalLightBufferobj.memory, sizeof(DirectionalLight), &directionalUBO);
	BufferUtils::UpdateBuffer(device, specularDataBufferobj.memory, sizeof(SpecularData), &specularUBO);
	BufferUtils::UpdateBuffer(device, cameraDataBufferobj.memory, sizeof(SpecularData), &cameraUBO);


	vkDeviceWaitIdle(device);

	// Geometry Pass

	VkDescriptorSet geomSet = this->geomdescriptorSet.get();
	vkCmdBindPipeline(*renderer.GetCurrentCommandBuffer(), VK_PIPELINE_BIND_POINT_GRAPHICS, renderer.GetPipeline(0).get()->GetPipeline());
	vkCmdBindDescriptorSets(*renderer.GetCurrentCommandBuffer(), VK_PIPELINE_BIND_POINT_GRAPHICS, geompipelineLayout, 0, 1, &geomSet, 0, nullptr);

	uint32_t numIndices4 = static_cast<uint32_t>(meshes[0].get()->GetIndices().size());
	vkCmdDrawIndexed(*renderer.GetCurrentCommandBuffer(), numIndices4, modelcount, 0, 0, 0);


	VkBuffer vertexBuffers2[] = { meshes[1].get()->GetVertexBuffer() };
	VkDeviceSize offsets2[] = { 0 };
	vkCmdBindVertexBuffers(*renderer.GetCurrentCommandBuffer(), 0, 1, vertexBuffers2, offsets2);
	vkCmdBindIndexBuffer(*renderer.GetCurrentCommandBuffer(), meshes[1].get()->GetIndexBuffer(), 0, VK_INDEX_TYPE_UINT32);
	glm::vec3 position4 = glm::vec3(0.0f, 0.0f, 0.0f);

	glm::vec3 scale2 = glm::vec3(5.0f);
	glm::mat4 modelMatrix2[] = { glm::mat4(1.0f)};
	modelMatrix2[0] = glm::translate(modelMatrix2[0], position4);
	modelMatrix2[0] = glm::scale(modelMatrix2[0], scale2);

	BufferUtils::UpdateBuffer(device, modelMatrix2Bufferobj.memory, sizeof(glm::mat4) * 1, modelMatrix2);

	VkDescriptorSet geomSet2 = this->geomdescriptorSet2.get();

	vkCmdBindPipeline(*renderer.GetCurrentCommandBuffer(), VK_PIPELINE_BIND_POINT_GRAPHICS, renderer.GetPipeline(2).get()->GetPipeline());
	vkCmdBindDescriptorSets(*renderer.GetCurrentCommandBuffer(), VK_PIPELINE_BIND_POINT_GRAPHICS, geompipelineLayout, 0, 1, &geomSet2, 0, nullptr);
	vkCmdDrawIndexed(*renderer.GetCurrentCommandBuffer(), numIndices4, 1, 0, 0, 0);

	//vkCmdDrawIndexed(*renderer.GetCurrentCommandBuffer(), numIndices4, 1, 0, 0, 0);

	//// Transition to Lighting Pass
	 
	renderer.GetCurrentCommandBuffer()->nextSubpass(vk::SubpassContents::eInline);

	//// Lighting Pass

	VkDescriptorSet lightSet = this->lightdescriptorSet.get();
	vkCmdBindPipeline(*renderer.GetCurrentCommandBuffer(), VK_PIPELINE_BIND_POINT_GRAPHICS, renderer.GetPipeline(1).get()->GetPipeline());
	vkCmdBindDescriptorSets(*renderer.GetCurrentCommandBuffer(), VK_PIPELINE_BIND_POINT_GRAPHICS, lightpipelineLayout, 0, 1, &lightSet, 0, nullptr);

	// Draw fullscreen quad
	renderer.GetCurrentCommandBuffer()->draw(6, 1, 0, 0);