Browse Source

vulkan: implement depth and stencil

niki 3 years ago
parent
commit
7cc6ed40be

+ 183 - 10
src/modules/graphics/vulkan/Graphics.cpp

@@ -83,7 +83,6 @@ love::graphics::Buffer* Graphics::newBuffer(const love::graphics::Buffer::Settin
 	return new Buffer(this, settings, format, data, size, arraylength);
 }
 
-// FIXME: clear stencil and depth missing.
 void Graphics::clear(OptionalColorD color, OptionalInt stencil, OptionalDouble depth) {
 	VkClearAttachment attachment{};
 
@@ -95,12 +94,31 @@ void Graphics::clear(OptionalColorD color, OptionalInt stencil, OptionalDouble d
 		attachment.clearValue.color.float32[3] = static_cast<float>(color.value.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);
+	}
+
+	std::array<VkClearAttachment, 2> attachments = {
+		attachment,
+		depthStencilAttachment
+	};
+
 	VkClearRect rect{};
 	rect.layerCount = 1;
 	rect.rect.extent.width = static_cast<uint32_t>(currentViewportWidth);
 	rect.rect.extent.height = static_cast<uint32_t>(currentViewportHeight);
 
-	vkCmdClearAttachments(commandBuffers[currentFrame], 1, &attachment, 1, &rect);
+	vkCmdClearAttachments(
+		commandBuffers[currentFrame], 
+		static_cast<uint32_t>(attachments.size()), attachments.data(),
+		1, &rect);
 }
 
 void Graphics::clear(const std::vector<OptionalColorD>& colors, OptionalInt stencil, OptionalDouble depth) {
@@ -214,6 +232,7 @@ bool Graphics::setMode(void* context, int width, int height, int pixelwidth, int
 	createSwapChain();
 	createImageViews();
 	createSyncObjects();
+	createDepthResources();
 	createCommandPool();
 	createCommandBuffers();
 	startRecordingGraphicsCommands();
@@ -422,6 +441,23 @@ void Graphics::setScissor() {
 	states.back().scissor = false;
 }
 
+void Graphics::setStencilMode(StencilAction action, CompareMode compare, int value, love::uint32 readmask, love::uint32 writemask) {
+	flushBatchedDraws();
+
+	states.back().stencil.action = action;
+	states.back().stencil.compare = compare;
+	states.back().stencil.value = value;
+	states.back().stencil.readMask = readmask;
+	states.back().stencil.writeMask = writemask;
+}
+
+void Graphics::setDepthMode(CompareMode compare, bool write) {
+	flushBatchedDraws();
+
+	states.back().depthTest = compare;
+	states.back().depthWrite = write;
+}
+
 void Graphics::setWireframe(bool enable) {
 	flushBatchedDraws();
 
@@ -1048,18 +1084,27 @@ VkPresentModeKHR Graphics::chooseSwapPresentMode(const std::vector<VkPresentMode
 		if (it != availablePresentModes.end()) {
 			return VK_PRESENT_MODE_FIFO_RELAXED_KHR;
 		}
+		else {
+			[[fallthrough]];
+		}
 	}
 	case 1: {
 		auto it = std::find(availablePresentModes.begin(), availablePresentModes.end(), VK_PRESENT_MODE_MAILBOX_KHR);
 		if (it != availablePresentModes.end()) {
 			return VK_PRESENT_MODE_MAILBOX_KHR;
 		}
+		else {
+			return VK_PRESENT_MODE_FIFO_KHR;
+		}
 	}
 	case 0: {
 		auto it = std::find(availablePresentModes.begin(), availablePresentModes.end(), VK_PRESENT_MODE_IMMEDIATE_KHR);
 		if (it != availablePresentModes.end()) {
 			return VK_PRESENT_MODE_IMMEDIATE_KHR;
 		}
+		else {
+			return VK_PRESENT_MODE_FIFO_KHR;
+		}
 	}
 	default:
 		return VK_PRESENT_MODE_FIFO_KHR;
@@ -1130,18 +1175,19 @@ void Graphics::createImageViews() {
 }
 
 VkFramebuffer Graphics::createFramebuffer(FramebufferConfiguration configuration) {
-	VkImageView attachments[] = {
-		configuration.imageView
+	std::array<VkImageView, 2> attachments = {
+		configuration.imageView,
+		depthImageView
 	};
 
 	VkFramebufferCreateInfo createInfo{};
 	createInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
 	createInfo.renderPass = configuration.renderPass;
-	createInfo.attachmentCount = 1;
-	createInfo.pAttachments = attachments;
+	createInfo.attachmentCount = static_cast<uint32_t>(attachments.size());
+	createInfo.pAttachments = attachments.data();
 	createInfo.width = configuration.width;
 	createInfo.height = configuration.height;
-	createInfo.layers = 1;	// is this correct?
+	createInfo.layers = 1;
 
 	VkFramebuffer frameBuffer;
 	if (vkCreateFramebuffer(device, &createInfo, nullptr, &frameBuffer) != VK_SUCCESS) {
@@ -1186,21 +1232,48 @@ VkRenderPass Graphics::createRenderPass(RenderPassConfiguration configuration) {
     colorDescription.initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
     colorDescription.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
 
+	VkAttachmentDescription depthStencilAttachment{};
+	depthStencilAttachment.format = findDepthFormat();
+	depthStencilAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
+	depthStencilAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
+	depthStencilAttachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
+	depthStencilAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
+	depthStencilAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
+	depthStencilAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+	depthStencilAttachment.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
+
     VkAttachmentReference colorAttachmentRef{};
     colorAttachmentRef.attachment = 0;
     colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
 
+	VkAttachmentReference depthStencilAttachmentRef{};
+	depthStencilAttachmentRef.attachment = 1;
+	depthStencilAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
+
     VkSubpassDescription subPass{};
     subPass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
     subPass.colorAttachmentCount = 1;
     subPass.pColorAttachments = &colorAttachmentRef;
+	subPass.pDepthStencilAttachment = &depthStencilAttachmentRef;
+
+	std::array<VkAttachmentDescription, 2> attachments = { colorDescription, depthStencilAttachment };
+
+	VkSubpassDependency dependency{};
+	dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
+	dependency.dstSubpass = 0;
+	dependency.srcStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;
+	dependency.srcAccessMask = 0;
+	dependency.dstStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;
+	dependency.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
 
     VkRenderPassCreateInfo createInfo{};
     createInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
-    createInfo.attachmentCount = 1;
-    createInfo.pAttachments = &colorDescription;
+    createInfo.attachmentCount = static_cast<uint32_t>(attachments.size());
+    createInfo.pAttachments = attachments.data();
     createInfo.subpassCount = 1;
     createInfo.pSubpasses = &subPass;
+	createInfo.dependencyCount = 1;
+	createInfo.pDependencies = &dependency;
 
     VkRenderPass renderPass;
     if (vkCreateRenderPass(device, &createInfo, nullptr, &renderPass) != VK_SUCCESS) {
@@ -1284,6 +1357,8 @@ void Graphics::createVulkanVertexFormat(
 }
 
 void Graphics::prepareDraw(const VertexAttributes& attributes, const BufferBindings& buffers, graphics::Texture* texture, PrimitiveType primitiveType, CullMode cullmode) {
+	states.back().stencil;
+
 	GraphicsPipelineConfiguration configuration;
     configuration.renderPass = currentRenderPass;
 	configuration.vertexAttributes = attributes;
@@ -1293,6 +1368,9 @@ void Graphics::prepareDraw(const VertexAttributes& attributes, const BufferBindi
 	configuration.blendState = states.back().blend;
 	configuration.colorChannelMask = states.back().colorMask;
 	configuration.winding = states.back().winding;
+	configuration.stencil = states.back().stencil;
+	configuration.depthState.compare = states.back().depthTest;
+	configuration.depthState.write = states.back().depthWrite;
 	configuration.cullmode = cullmode;
 	configuration.viewportWidth = currentViewportWidth;
 	configuration.viewportHeight = currentViewportHeight;
@@ -1509,6 +1587,32 @@ VkPipeline Graphics::createGraphicsPipeline(GraphicsPipelineConfiguration config
 	multisampling.alphaToCoverageEnable = VK_FALSE; // Optional
 	multisampling.alphaToOneEnable = VK_FALSE; // Optional
 
+	VkPipelineDepthStencilStateCreateInfo depthStencil{};
+	depthStencil.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
+	depthStencil.depthTestEnable = VK_TRUE;
+	depthStencil.depthWriteEnable = Vulkan::getBool(configuration.depthState.write);
+	depthStencil.depthCompareOp = Vulkan::getCompareOp(configuration.depthState.compare);
+	depthStencil.depthBoundsTestEnable = VK_FALSE;
+	depthStencil.minDepthBounds = 0.0f;
+	depthStencil.maxDepthBounds = 1.0f;
+	depthStencil.stencilTestEnable = VK_TRUE;
+
+	depthStencil.front.failOp = VK_STENCIL_OP_KEEP;
+	depthStencil.front.passOp = Vulkan::getStencilOp(configuration.stencil.action);
+	depthStencil.front.depthFailOp = VK_STENCIL_OP_KEEP;
+	depthStencil.front.compareOp = Vulkan::getCompareOp(configuration.stencil.compare);
+	depthStencil.front.compareMask = configuration.stencil.readMask;
+	depthStencil.front.writeMask = configuration.stencil.writeMask;
+	depthStencil.front.reference = configuration.stencil.value;
+
+	depthStencil.back.failOp = VK_STENCIL_OP_KEEP;
+	depthStencil.back.passOp = Vulkan::getStencilOp(configuration.stencil.action);
+	depthStencil.back.depthFailOp = VK_STENCIL_OP_KEEP;
+	depthStencil.back.compareOp = Vulkan::getCompareOp(configuration.stencil.compare);
+	depthStencil.back.compareMask = configuration.stencil.readMask;
+	depthStencil.back.writeMask = configuration.stencil.writeMask;
+	depthStencil.back.reference = static_cast<uint32_t>(configuration.stencil.value);
+
 	VkPipelineColorBlendAttachmentState colorBlendAttachment{};
 	colorBlendAttachment.colorWriteMask = Vulkan::getColorMask(configuration.colorChannelMask);
 	colorBlendAttachment.blendEnable = Vulkan::getBool(configuration.blendState.enable);
@@ -1539,7 +1643,7 @@ VkPipeline Graphics::createGraphicsPipeline(GraphicsPipelineConfiguration config
 	pipelineInfo.pViewportState = &viewportState;
 	pipelineInfo.pRasterizationState = &rasterizer;
 	pipelineInfo.pMultisampleState = &multisampling;
-	pipelineInfo.pDepthStencilState = nullptr;
+	pipelineInfo.pDepthStencilState = &depthStencil;
 	pipelineInfo.pColorBlendState = &colorBlending;
 	pipelineInfo.pDynamicState = nullptr;
 	pipelineInfo.layout = configuration.shader->getGraphicsPipelineLayout();
@@ -1570,6 +1674,72 @@ void Graphics::ensureGraphicsPipelineConfiguration(GraphicsPipelineConfiguration
 	}
 }
 
+VkFormat Graphics::findSupportedFormat(const std::vector<VkFormat>& candidates, VkImageTiling tiling, VkFormatFeatureFlags features) {
+	for (auto format : candidates) {
+		VkFormatProperties properties;
+		vkGetPhysicalDeviceFormatProperties(physicalDevice, format, &properties);
+		if (tiling == VK_IMAGE_TILING_LINEAR && (properties.linearTilingFeatures & features) == features) {
+			return format;
+		}
+		else if (tiling == VK_IMAGE_TILING_OPTIMAL && (properties.optimalTilingFeatures & features) == features) {
+			return format;
+		}
+	}
+
+	throw love::Exception("failed to find supported format");
+}
+
+
+VkFormat Graphics::findDepthFormat() {
+	return findSupportedFormat(
+		{ VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT },
+		VK_IMAGE_TILING_OPTIMAL,
+		VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT
+	);
+}
+
+void Graphics::createDepthResources() {
+	VkFormat depthFormat = findDepthFormat();
+
+	VkImageCreateInfo imageInfo{};
+	imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
+	imageInfo.imageType = VK_IMAGE_TYPE_2D;
+	imageInfo.format = depthFormat;
+	imageInfo.extent.width = swapChainExtent.width;
+	imageInfo.extent.height = swapChainExtent.height;
+	imageInfo.extent.depth = 1;
+	imageInfo.mipLevels = 1;
+	imageInfo.arrayLayers = 1;
+	imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
+	imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
+	imageInfo.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
+	imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
+	imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+
+	VmaAllocationCreateInfo allocationInfo{};
+	allocationInfo.usage = VMA_MEMORY_USAGE_AUTO;
+	allocationInfo.flags = VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
+
+	vmaCreateImage(vmaAllocator, &imageInfo, &allocationInfo, &depthImage, &depthImageAllocation, nullptr);
+
+	VkImageViewCreateInfo imageViewInfo{};
+	imageViewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
+	imageViewInfo.image = depthImage;
+	imageViewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
+	imageViewInfo.format = depthFormat;
+	imageViewInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
+	imageViewInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
+	imageViewInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
+	imageViewInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
+	imageViewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
+	imageViewInfo.subresourceRange.baseMipLevel = 0;
+	imageViewInfo.subresourceRange.levelCount = 1;
+	imageViewInfo.subresourceRange.baseArrayLayer = 0;
+	imageViewInfo.subresourceRange.layerCount = 1;
+
+	vkCreateImageView(device, &imageViewInfo, nullptr, &depthImageView);
+}
+
 void Graphics::createCommandPool() {
 	QueueFamilyIndices queueFamilyIndices = findQueueFamilies(physicalDevice);
 
@@ -1683,6 +1853,8 @@ void Graphics::cleanup() {
 }
 
 void Graphics::cleanupSwapChain() {
+	vkDestroyImageView(device, depthImageView, nullptr);
+	vmaDestroyImage(vmaAllocator, depthImage, depthImageAllocation);
 	for (const auto& [key, val] : framebuffers) {
 		vkDestroyFramebuffer(device, val, nullptr);
 	}
@@ -1701,6 +1873,7 @@ void Graphics::recreateSwapChain() {
 
 	createSwapChain();
 	createImageViews();
+	createDepthResources();
 }
 
 love::graphics::Graphics* createInstance() {

+ 10 - 2
src/modules/graphics/vulkan/Graphics.h

@@ -68,6 +68,8 @@ struct GraphicsPipelineConfiguration {
 	float viewportWidth;
 	float viewportHeight;
 	std::optional<Rect> scissorRect;
+	StencilState stencil;
+	DepthState depthState;
 
 	GraphicsPipelineConfiguration() {
 		memset(this, 0, sizeof(GraphicsPipelineConfiguration));
@@ -154,8 +156,8 @@ public:
 	void setColor(Colorf c) override;
 	void setScissor(const Rect& rect) override;
 	void setScissor() override;
-	void setStencilMode(StencilAction action, CompareMode compare, int value, love::uint32 readmask, love::uint32 writemask) override { }
-	void setDepthMode(CompareMode compare, bool write) override { }
+	void setStencilMode(StencilAction action, CompareMode compare, int value, love::uint32 readmask, love::uint32 writemask) override;
+	void setDepthMode(CompareMode compare, bool write) override;
 	void setFrontFaceWinding(Winding winding) override;
 	void setColorMask(ColorChannelMask mask) override;
 	void setBlendState(const BlendState& blend) override;
@@ -216,6 +218,9 @@ private:
 	void createDefaultShaders();
     VkRenderPass createRenderPass(RenderPassConfiguration);
 	VkPipeline createGraphicsPipeline(GraphicsPipelineConfiguration);
+	VkFormat findSupportedFormat(const std::vector<VkFormat>& candidates, VkImageTiling tiling, VkFormatFeatureFlags features);
+	VkFormat findDepthFormat();
+	void createDepthResources();
 	void createCommandPool();
 	void createCommandBuffers();
 	void createSyncObjects();
@@ -252,6 +257,9 @@ private:
 	std::vector<VkImageView> swapChainImageViews;
 	VkPipeline currentGraphicsPipeline = VK_NULL_HANDLE;
     VkRenderPass currentRenderPass = VK_NULL_HANDLE;
+	VkImage depthImage;
+	VkImageView depthImageView;
+	VmaAllocation depthImageAllocation;
     std::unordered_map<RenderPassConfiguration, VkRenderPass, RenderPassConfigurationHasher> renderPasses;
 	std::unordered_map<FramebufferConfiguration, VkFramebuffer, FramebufferConfigurationHasher> framebuffers;
 	std::unordered_map<GraphicsPipelineConfiguration, VkPipeline, GraphicsPipelineConfigurationHasher> graphicsPipelines;

+ 23 - 0
src/modules/graphics/vulkan/Vulkan.cpp

@@ -583,6 +583,29 @@ VkDescriptorType Vulkan::getDescriptorType(graphics::Shader::UniformType type) {
 	}
 }
 
+VkStencilOp Vulkan::getStencilOp(StencilAction action) {
+	switch (action) {
+	case STENCIL_KEEP:
+		return VK_STENCIL_OP_KEEP;
+	case STENCIL_ZERO:
+		return VK_STENCIL_OP_ZERO;
+	case STENCIL_REPLACE:
+		return VK_STENCIL_OP_REPLACE;
+	case STENCIL_INCREMENT:
+		return VK_STENCIL_OP_INCREMENT_AND_CLAMP;
+	case STENCIL_DECREMENT:
+		return VK_STENCIL_OP_DECREMENT_AND_CLAMP;
+	case STENCIL_INCREMENT_WRAP:
+		return VK_STENCIL_OP_INCREMENT_AND_WRAP;
+	case STENCIL_DECREMENT_WRAP:
+		return VK_STENCIL_OP_DECREMENT_AND_WRAP;
+	case STENCIL_INVERT:
+		return VK_STENCIL_OP_INVERT;
+	default:
+		throw love::Exception("unknown stencil action");
+	}
+}
+
 void Vulkan::cmdTransitionImageLayout(VkCommandBuffer commandBuffer, VkImage image, VkImageLayout oldLayout, VkImageLayout newLayout,
 	uint32_t baseLevel, uint32_t levelCount, uint32_t baseLayer, uint32_t layerCount) {
 	VkImageMemoryBarrier barrier{};

+ 1 - 0
src/modules/graphics/vulkan/Vulkan.h

@@ -52,6 +52,7 @@ public:
 	static VkCompareOp getCompareOp(CompareMode);
 	static VkSamplerMipmapMode getMipMapMode(SamplerState::MipmapFilterMode);
 	static VkDescriptorType getDescriptorType(graphics::Shader::UniformType);
+	static VkStencilOp getStencilOp(StencilAction);
 
 	static void cmdTransitionImageLayout(
 		VkCommandBuffer, VkImage, VkImageLayout oldLayout, VkImageLayout newLayout,