Browse Source

vulkan: improve render pass performance
Before this patch some common patterns would lead to very suboptimal
render passes.
Consider the following code:
```lua
local canvas = love.graphics.newCanvas(...)

function love.draw()
love.graphics.setCanvas(canvas)
love.graphics.clear(...)
love.graphics.draw(...)
love.graphics.setCanvas()

love.graphics.draw(canvas)
end
```
This would lead to the following rendering:
1) render pass on main window with loadOp=load, followed by an immediate
call to vkCmdClearAttachments
2) render pass on canvas with loadOp=load, followed by an immediate call
to vkCmdClearAttachments
3) render pass on main window with loadOp=load

This patch changes the behaviour to the more performant
(and equivalent) version:
1) render pass on canvas with loadOp=clear
2) render pass on main window with loadOp=clear

This is especially helpful on mobile devices, where creating render
passes is an expensive operation.

niki 2 years ago
parent
commit
094d6e39d1
2 changed files with 232 additions and 127 deletions
  1. 203 121
      src/modules/graphics/vulkan/Graphics.cpp
  2. 29 6
      src/modules/graphics/vulkan/Graphics.h

+ 203 - 121
src/modules/graphics/vulkan/Graphics.cpp

@@ -110,50 +110,85 @@ void Graphics::clear(OptionalColorD color, OptionalInt stencil, OptionalDouble d
 
 	flushBatchedDraws();
 
-	if (!renderPassState.active)
-		startRenderPass();
+	if (renderPassState.active)
+	{
+		VkClearAttachment attachment{};
 
-	VkClearAttachment attachment{};
+		if (color.hasValue)
+		{
+			Colorf cf((float)color.value.r, (float)color.value.g, (float)color.value.b, (float)color.value.a);
+			gammaCorrectColor(cf);
 
-	if (color.hasValue)
-	{
-		Colorf cf((float)color.value.r, (float)color.value.g, (float)color.value.b, (float)color.value.a);
-		gammaCorrectColor(cf);
+			attachment.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+			attachment.clearValue.color.float32[0] = static_cast<float>(cf.r);
+			attachment.clearValue.color.float32[1] = static_cast<float>(cf.g);
+			attachment.clearValue.color.float32[2] = static_cast<float>(cf.b);
+			attachment.clearValue.color.float32[3] = static_cast<float>(cf.a);
+		}
 
-		attachment.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
-		attachment.clearValue.color.float32[0] = static_cast<float>(cf.r);
-		attachment.clearValue.color.float32[1] = static_cast<float>(cf.g);
-		attachment.clearValue.color.float32[2] = static_cast<float>(cf.b);
-		attachment.clearValue.color.float32[3] = static_cast<float>(cf.a);
-	}
+		VkClearAttachment depthStencilAttachment{};
+
+		if (stencil.hasValue)
+		{
+			depthStencilAttachment.aspectMask = VK_IMAGE_ASPECT_STENCIL_BIT;
+			depthStencilAttachment.clearValue.depthStencil.stencil = static_cast<uint32_t>(stencil.value);
+		}
+		if (depth.hasValue)
+		{
+			depthStencilAttachment.aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT;
+			depthStencilAttachment.clearValue.depthStencil.depth = static_cast<float>(depth.value);
+		}
 
-	VkClearAttachment depthStencilAttachment{};
+		std::array<VkClearAttachment, 2> attachments = {
+			attachment,
+			depthStencilAttachment
+		};
 
-	if (stencil.hasValue)
-	{
-		depthStencilAttachment.aspectMask = VK_IMAGE_ASPECT_STENCIL_BIT;
-		depthStencilAttachment.clearValue.depthStencil.stencil = static_cast<uint32_t>(stencil.value);
+		VkClearRect rect{};
+		rect.layerCount = 1;
+		rect.rect.extent.width = static_cast<uint32_t>(renderPassState.width);
+		rect.rect.extent.height = static_cast<uint32_t>(renderPassState.height);
+
+		vkCmdClearAttachments(
+			commandBuffers[currentFrame], 
+			static_cast<uint32_t>(attachments.size()), attachments.data(),
+			1, &rect);
 	}
-	if (depth.hasValue)
+	else
 	{
-		depthStencilAttachment.aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT;
-		depthStencilAttachment.clearValue.depthStencil.depth = static_cast<float>(depth.value);
-	}
+		renderPassState.useConfigurations = true;
 
-	std::array<VkClearAttachment, 2> attachments = {
-		attachment,
-		depthStencilAttachment
-	};
+		if (color.hasValue)
+		{
+			renderPassState.renderPassConfiguration.colorAttachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
+			renderPassState.clearColors[0].color.float32[0] = static_cast<float>(color.value.r);
+			renderPassState.clearColors[0].color.float32[1] = static_cast<float>(color.value.g);
+			renderPassState.clearColors[0].color.float32[2] = static_cast<float>(color.value.b);
+			renderPassState.clearColors[0].color.float32[3] = static_cast<float>(color.value.a);
+		}
+
+		if (depth.hasValue)
+		{
+			renderPassState.renderPassConfiguration.staticData.depthStencilAttachment.depthLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
+			renderPassState.clearColors[1].depthStencil.depth = static_cast<float>(depth.value);
+		}
 
-	VkClearRect rect{};
-	rect.layerCount = 1;
-	rect.rect.extent.width = static_cast<uint32_t>(renderPassState.width);
-	rect.rect.extent.height = static_cast<uint32_t>(renderPassState.height);
+		if (stencil.hasValue)
+		{
+			renderPassState.renderPassConfiguration.staticData.depthStencilAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
+			renderPassState.clearColors[1].depthStencil.stencil = static_cast<uint32_t>(stencil.value);
+		}
 
-	vkCmdClearAttachments(
-		commandBuffers[currentFrame], 
-		static_cast<uint32_t>(attachments.size()), attachments.data(),
-		1, &rect);
+		if (renderPassState.isWindow)
+		{
+			renderPassState.windowClearRequested = true;
+			renderPassState.mainWindowClearColorValue = color;
+			renderPassState.mainWindowClearDepthValue = depth;
+			renderPassState.mainWindowClearStencilValue = stencil;
+		}
+		else
+			startRenderPass();
+	}
 }
 
 void Graphics::clear(const std::vector<OptionalColorD> &colors, OptionalInt stencil, OptionalDouble depth)
@@ -163,51 +198,81 @@ void Graphics::clear(const std::vector<OptionalColorD> &colors, OptionalInt sten
 
 	flushBatchedDraws();
 
-	if (!renderPassState.active)
-		startRenderPass();
-
-	std::vector<VkClearAttachment> attachments;
-	for (const auto &color : colors)
+	if (renderPassState.active)
 	{
-		VkClearAttachment attachment{};
-		if (color.hasValue)
+		std::vector<VkClearAttachment> attachments;
+		for (const auto &color : colors)
 		{
-			Colorf cf((float)color.value.r, (float)color.value.g, (float)color.value.b, (float)color.value.a);
-			gammaCorrectColor(cf);
+			VkClearAttachment attachment{};
+			if (color.hasValue)
+			{
+				Colorf cf((float)color.value.r, (float)color.value.g, (float)color.value.b, (float)color.value.a);
+				gammaCorrectColor(cf);
+
+				attachment.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+				attachment.clearValue.color.float32[0] = static_cast<float>(cf.r);
+				attachment.clearValue.color.float32[1] = static_cast<float>(cf.g);
+				attachment.clearValue.color.float32[2] = static_cast<float>(cf.b);
+				attachment.clearValue.color.float32[3] = static_cast<float>(cf.a);
+			}
+			attachments.push_back(attachment);
+		}
 
-			attachment.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
-			attachment.clearValue.color.float32[0] = static_cast<float>(cf.r);
-			attachment.clearValue.color.float32[1] = static_cast<float>(cf.g);
-			attachment.clearValue.color.float32[2] = static_cast<float>(cf.b);
-			attachment.clearValue.color.float32[3] = static_cast<float>(cf.a);
+		VkClearAttachment depthStencilAttachment{};
+
+		if (stencil.hasValue)
+		{
+			depthStencilAttachment.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT;
+			depthStencilAttachment.clearValue.depthStencil.stencil = static_cast<uint32_t>(stencil.value);
 		}
-		attachments.push_back(attachment);
-	}
+		if (depth.hasValue)
+		{
+			depthStencilAttachment.aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT;
+			depthStencilAttachment.clearValue.depthStencil.depth = static_cast<float>(depth.value);
+		}
+
+		attachments.push_back(depthStencilAttachment);
 
-	VkClearAttachment depthStencilAttachment{};
+		VkClearRect rect{};
+		rect.layerCount = 1;
+		rect.rect.extent.width = static_cast<uint32_t>(renderPassState.width);
+		rect.rect.extent.height = static_cast<uint32_t>(renderPassState.height);
 
-	if (stencil.hasValue)
-	{
-		depthStencilAttachment.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT;
-		depthStencilAttachment.clearValue.depthStencil.stencil = static_cast<uint32_t>(stencil.value);
+		vkCmdClearAttachments(
+			commandBuffers[currentFrame],
+			static_cast<uint32_t>(attachments.size()), attachments.data(),
+			1, &rect);
 	}
-	if (depth.hasValue)
+	else
 	{
-		depthStencilAttachment.aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT;
-		depthStencilAttachment.clearValue.depthStencil.depth = static_cast<float>(depth.value);
-	}
+		renderPassState.useConfigurations = true;
 
-	attachments.push_back(depthStencilAttachment);
+		for (size_t i = 0; i < colors.size(); i++)
+		{
+			if (colors[i].hasValue)
+			{
+				renderPassState.renderPassConfiguration.colorAttachments[i].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
+				renderPassState.clearColors[i].color.float32[0] = colors[i].value.r;
+				renderPassState.clearColors[i].color.float32[1] = colors[i].value.g;
+				renderPassState.clearColors[i].color.float32[2] = colors[i].value.b;
+				renderPassState.clearColors[i].color.float32[3] = colors[i].value.a;
+			}
+		}
 
-	VkClearRect rect{};
-	rect.layerCount = 1;
-	rect.rect.extent.width = static_cast<uint32_t>(renderPassState.width);
-	rect.rect.extent.height = static_cast<uint32_t>(renderPassState.height);
+		if (depth.hasValue)
+		{
+			renderPassState.renderPassConfiguration.staticData.depthStencilAttachment.depthLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
+			renderPassState.clearColors[colors.size()].depthStencil.depth = depth.value;
+		}
+
+		if (stencil.hasValue)
+		{
+			renderPassState.renderPassConfiguration.staticData.depthStencilAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
+			renderPassState.clearColors[colors.size()].depthStencil.stencil = stencil.value;
+		}
 
-	vkCmdClearAttachments(
-		commandBuffers[currentFrame],
-		static_cast<uint32_t>(attachments.size()), attachments.data(),
-		1, &rect);
+		startRenderPass();
+	}
 }
 
 void Graphics::discard(const std::vector<bool> &colorbuffers, bool depthstencil)
@@ -215,40 +280,19 @@ void Graphics::discard(const std::vector<bool> &colorbuffers, bool depthstencil)
 	if (renderPassState.active)
 		endRenderPass();
 
-	if (renderPassState.useConfigurations)
-	{
-		auto & renderPassConfiguration = renderPassState.renderPassConfiguration;
+	renderPassState.useConfigurations = true;
+	auto & renderPassConfiguration = renderPassState.renderPassConfiguration;
 
-		for (size_t i = 0; i < colorbuffers.size(); i++)
-			renderPassConfiguration.colorAttachments[i].discard = colorbuffers[i];
-		renderPassConfiguration.staticData.depthAttachment.discard = depthstencil;
-	}
-	else
+	for (size_t i = 0; i < colorbuffers.size(); i++)
 	{
-		RenderPassConfiguration renderPassConfiguration{};
-		renderPassConfiguration.colorAttachments.push_back({ swapChainImageFormat, colorbuffers[0], msaaSamples });
-		renderPassConfiguration.staticData.depthAttachment = { findDepthFormat(), depthstencil, msaaSamples };
-		if (msaaSamples & VK_SAMPLE_COUNT_1_BIT)
-			renderPassConfiguration.staticData.resolve = false;
-		else
-			renderPassConfiguration.staticData.resolve = true;
-
-		FramebufferConfiguration framebufferConfiguration{};
-		framebufferConfiguration.staticData.depthView = depthImageView;
-		if (msaaSamples & VK_SAMPLE_COUNT_1_BIT)
-		{
-			framebufferConfiguration.colorViews.push_back(swapChainImageViews.at(imageIndex));
-			framebufferConfiguration.staticData.resolveView = VK_NULL_HANDLE;
-		}
-		else
-		{
-			framebufferConfiguration.colorViews.push_back(colorImageView);
-			framebufferConfiguration.staticData.resolveView = swapChainImageViews.at(imageIndex);
-		}
+		if (colorbuffers[i])
+			renderPassConfiguration.colorAttachments[i].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
+	}
 
-		renderPassState.useConfigurations = true;
-		renderPassState.renderPassConfiguration = renderPassConfiguration;
-		renderPassState.framebufferConfiguration = framebufferConfiguration;
+	if (depthstencil)
+	{
+		renderPassConfiguration.staticData.depthStencilAttachment.depthLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
+		renderPassConfiguration.staticData.depthStencilAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
 	}
 
 	startRenderPass();
@@ -422,6 +466,9 @@ void Graphics::present(void *screenshotCallbackdata)
 	if (isRenderTargetActive())
 		throw love::Exception("present cannot be called while a render target is active.");
 
+	if (!renderPassState.active && renderPassState.windowClearRequested)
+		startRenderPass();
+
 	deprecations.draw(this);
 
 	submitGpuCommands(true, screenshotCallbackdata);
@@ -1938,8 +1985,8 @@ void Graphics::createScreenshotCallbackBuffers()
 void Graphics::createDefaultRenderPass()
 {
 	RenderPassConfiguration renderPassConfiguration{};
-	renderPassConfiguration.colorAttachments.push_back({ swapChainImageFormat, false, msaaSamples });
-	renderPassConfiguration.staticData.depthAttachment = { findDepthFormat(), false, msaaSamples };
+	renderPassConfiguration.colorAttachments.push_back({ swapChainImageFormat, VK_ATTACHMENT_LOAD_OP_LOAD, msaaSamples });
+	renderPassConfiguration.staticData.depthStencilAttachment = { findDepthFormat(), VK_ATTACHMENT_LOAD_OP_LOAD, VK_ATTACHMENT_LOAD_OP_LOAD, msaaSamples };
 	if (msaaSamples & VK_SAMPLE_COUNT_1_BIT)
 		renderPassConfiguration.staticData.resolve = false;
 	else
@@ -2050,10 +2097,7 @@ VkRenderPass Graphics::createRenderPass(RenderPassConfiguration &configuration)
 		VkAttachmentDescription colorDescription{};
 		colorDescription.format = colorAttachment.format;
 		colorDescription.samples = colorAttachment.msaaSamples;
-		if (colorAttachment.discard)
-			colorDescription.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
-		else
-			colorDescription.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
+		colorDescription.loadOp = colorAttachment.loadOp;
 		colorDescription.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
 		colorDescription.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
 		colorDescription.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
@@ -2066,24 +2110,18 @@ VkRenderPass Graphics::createRenderPass(RenderPassConfiguration &configuration)
 	subPass.pColorAttachments = colorAttachmentRefs.data();
 
 	VkAttachmentReference depthStencilAttachmentRef{};
-	if (configuration.staticData.depthAttachment.format != VK_FORMAT_UNDEFINED)
+	if (configuration.staticData.depthStencilAttachment.format != VK_FORMAT_UNDEFINED)
 	{
 		depthStencilAttachmentRef.attachment = attachment++;
 		depthStencilAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
 		subPass.pDepthStencilAttachment = &depthStencilAttachmentRef;
 
 		VkAttachmentDescription depthStencilAttachment{};
-		depthStencilAttachment.format = configuration.staticData.depthAttachment.format;
-		depthStencilAttachment.samples = configuration.staticData.depthAttachment.msaaSamples;
-		if (configuration.staticData.depthAttachment.discard)
-			depthStencilAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
-		else
-			depthStencilAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
+		depthStencilAttachment.format = configuration.staticData.depthStencilAttachment.format;
+		depthStencilAttachment.samples = configuration.staticData.depthStencilAttachment.msaaSamples;
+		depthStencilAttachment.loadOp = configuration.staticData.depthStencilAttachment.depthLoadOp;
 		depthStencilAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
-		if (configuration.staticData.depthAttachment.discard)
-			depthStencilAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
-		else
-			depthStencilAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
+		depthStencilAttachment.stencilLoadOp = configuration.staticData.depthStencilAttachment.stencilLoadOp;
 		depthStencilAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE;
 		depthStencilAttachment.initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
 		depthStencilAttachment.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
@@ -2285,13 +2323,18 @@ void Graphics::prepareDraw(const VertexAttributes &attributes, const BufferBindi
 
 void Graphics::setDefaultRenderPass()
 {
+	uint32_t numClearValues = 2;
+	renderPassState.clearColors.resize(numClearValues);
+
 	renderPassState.beginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
 	renderPassState.beginInfo.renderPass = defaultRenderPass;
 	renderPassState.beginInfo.framebuffer = defaultFramebuffers[imageIndex];
 	renderPassState.beginInfo.renderArea.offset = { 0, 0 };
 	renderPassState.beginInfo.renderArea.extent = swapChainExtent;
-	renderPassState.beginInfo.clearValueCount = 0;
+	renderPassState.beginInfo.clearValueCount = numClearValues;
+	renderPassState.beginInfo.pClearValues = renderPassState.clearColors.data();
 
+	renderPassState.isWindow = true;
 	renderPassState.useConfigurations = false;
 	renderPassState.pipeline = VK_NULL_HANDLE;
 	renderPassState.width = static_cast<float>(swapChainExtent.width);
@@ -2299,6 +2342,36 @@ void Graphics::setDefaultRenderPass()
 	renderPassState.msaa = msaaSamples;
 	renderPassState.numColorAttachments = 1;
 	renderPassState.transitionImages.clear();
+
+	RenderPassConfiguration renderPassConfiguration{};
+	renderPassConfiguration.colorAttachments.push_back({ swapChainImageFormat, VK_ATTACHMENT_LOAD_OP_LOAD, msaaSamples });
+	renderPassConfiguration.staticData.depthStencilAttachment = { findDepthFormat(), VK_ATTACHMENT_LOAD_OP_LOAD, VK_ATTACHMENT_LOAD_OP_LOAD, msaaSamples };
+	if (msaaSamples & VK_SAMPLE_COUNT_1_BIT)
+		renderPassConfiguration.staticData.resolve = false;
+	else
+		renderPassConfiguration.staticData.resolve = true;
+
+	FramebufferConfiguration framebufferConfiguration{};
+	framebufferConfiguration.staticData.depthView = depthImageView;
+	framebufferConfiguration.staticData.width = swapChainExtent.width;
+	framebufferConfiguration.staticData.height = swapChainExtent.height;
+
+	if (msaaSamples & VK_SAMPLE_COUNT_1_BIT)
+	{
+		framebufferConfiguration.colorViews.push_back(swapChainImageViews.at(imageIndex));
+		framebufferConfiguration.staticData.resolveView = VK_NULL_HANDLE;
+	}
+	else
+	{
+		framebufferConfiguration.colorViews.push_back(colorImageView);
+		framebufferConfiguration.staticData.resolveView = swapChainImageViews.at(imageIndex);
+	}
+
+	renderPassState.renderPassConfiguration = renderPassConfiguration;
+	renderPassState.framebufferConfiguration = framebufferConfiguration;
+
+	if (renderPassState.windowClearRequested)
+		clear(renderPassState.mainWindowClearColorValue, renderPassState.mainWindowClearStencilValue, renderPassState.mainWindowClearDepthValue);
 }
 
 void Graphics::setRenderPass(const RenderTargets &rts, int pixelw, int pixelh, bool hasSRGBtexture)
@@ -2310,12 +2383,13 @@ void Graphics::setRenderPass(const RenderTargets &rts, int pixelw, int pixelh, b
 	for (const auto &color : rts.colors)
 		renderPassConfiguration.colorAttachments.push_back({ 
 			Vulkan::getTextureFormat(color.texture->getPixelFormat(), isPixelFormatSRGB(color.texture->getPixelFormat())).internalFormat,
-			false, 
+			VK_ATTACHMENT_LOAD_OP_LOAD,
 			dynamic_cast<Texture*>(color.texture)->getMsaaSamples() });
 	if (rts.depthStencil.texture != nullptr)
-		renderPassConfiguration.staticData.depthAttachment = {
+		renderPassConfiguration.staticData.depthStencilAttachment = {
 			Vulkan::getTextureFormat(rts.depthStencil.texture->getPixelFormat(), false).internalFormat,
-			false,
+			VK_ATTACHMENT_LOAD_OP_LOAD,
+			VK_ATTACHMENT_LOAD_OP_LOAD,
 			dynamic_cast<Texture*>(rts.depthStencil.texture)->getMsaaSamples() };
 
 	FramebufferConfiguration configuration{};
@@ -2333,14 +2407,19 @@ void Graphics::setRenderPass(const RenderTargets &rts, int pixelw, int pixelh, b
 	configuration.staticData.width = static_cast<uint32_t>(pixelw);
 	configuration.staticData.height = static_cast<uint32_t>(pixelh);
 
+	uint32_t numClearValues = rts.colors.size() + 1;
+	renderPassState.clearColors.resize(numClearValues);
+
 	renderPassState.beginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
 	renderPassState.beginInfo.renderPass = VK_NULL_HANDLE;
 	renderPassState.beginInfo.framebuffer = VK_NULL_HANDLE;
 	renderPassState.beginInfo.renderArea.offset = {0, 0};
 	renderPassState.beginInfo.renderArea.extent.width = static_cast<uint32_t>(pixelw);
 	renderPassState.beginInfo.renderArea.extent.height = static_cast<uint32_t>(pixelh);
-	renderPassState.beginInfo.clearValueCount = 0;
+	renderPassState.beginInfo.clearValueCount = numClearValues;
+	renderPassState.beginInfo.pClearValues = renderPassState.clearColors.data();
 
+	renderPassState.isWindow = false;
 	renderPassState.useConfigurations = true;
 	renderPassState.renderPassConfiguration = renderPassConfiguration;
 	renderPassState.framebufferConfiguration = configuration;
@@ -2356,6 +2435,9 @@ void Graphics::startRenderPass()
 {
 	renderPassState.active = true;
 
+	if (renderPassState.isWindow && renderPassState.windowClearRequested)
+		renderPassState.windowClearRequested = false;
+
 	VkViewport viewport{};
 	viewport.x = 0.0f;
 	viewport.y = 0.0f;

+ 29 - 6
src/modules/graphics/vulkan/Graphics.h

@@ -46,27 +46,43 @@ namespace graphics
 namespace vulkan
 {
 
-struct RenderPassAttachment
+struct ColorAttachment
 {
 	VkFormat format = VK_FORMAT_UNDEFINED;
-	bool discard = true;
+	VkAttachmentLoadOp loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
 	VkSampleCountFlagBits msaaSamples = VK_SAMPLE_COUNT_1_BIT;
 
-	bool operator==(const RenderPassAttachment &attachment) const
+	bool operator==(const ColorAttachment&attachment) const
 	{
 		return format == attachment.format && 
-			discard == attachment.discard && 
+			loadOp == attachment.loadOp &&
+			msaaSamples == attachment.msaaSamples;
+	}
+};
+
+struct DepthStencilAttachment
+{
+	VkFormat format = VK_FORMAT_UNDEFINED;
+	VkAttachmentLoadOp depthLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
+	VkAttachmentLoadOp stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
+	VkSampleCountFlagBits msaaSamples = VK_SAMPLE_COUNT_1_BIT;
+
+	bool operator==(const DepthStencilAttachment &attachment) const
+	{
+		return format == attachment.format &&
+			depthLoadOp == attachment.depthLoadOp &&
+			stencilLoadOp == attachment.stencilLoadOp &&
 			msaaSamples == attachment.msaaSamples;
 	}
 };
 
 struct RenderPassConfiguration
 {
-	std::vector<RenderPassAttachment> colorAttachments;
+	std::vector<ColorAttachment> colorAttachments;
 
 	struct StaticRenderPassConfiguration
 	{
-		RenderPassAttachment depthAttachment;
+		DepthStencilAttachment depthStencilAttachment;
 		bool resolve = false;
 	} staticData;
 
@@ -216,6 +232,7 @@ struct RenderpassState
 {
 	bool active = false;
 	VkRenderPassBeginInfo beginInfo{};
+	bool isWindow = false;
 	bool useConfigurations = false;
 	RenderPassConfiguration renderPassConfiguration{};
 	FramebufferConfiguration framebufferConfiguration{};
@@ -225,6 +242,12 @@ struct RenderpassState
 	float width = 0.0f;
 	float height = 0.0f;
 	VkSampleCountFlagBits msaa = VK_SAMPLE_COUNT_1_BIT;
+	std::vector<VkClearValue> clearColors;
+
+	bool windowClearRequested = false;
+	OptionalColorD mainWindowClearColorValue;
+	OptionalDouble mainWindowClearDepthValue;
+	OptionalInt mainWindowClearStencilValue;
 };
 
 struct ScreenshotReadbackBuffer