天天看點

Vulkan_基于查詢池的遮擋剔除

前面章節我們簡單的使用了解過Vulkan_基于GPU的視錐體剔除和LOD與CPU中實作的視錐體剔除,本次我們主要來使用Vulkan的遮擋查詢來實作視錐體内的遮擋剔除功能。

Vulkan_基于查詢池的遮擋剔除

想要具體了解所有剔除原理的同學,可以研讀知乎圖形大佬的這篇文章剔除:從軟體到硬體。

一、vulkan查詢

從Vulkan中讀取沖統計資料的主要機制是依靠查詢對象。查詢對象通過池進行建立和管理,每個對象實際上是池中的一個槽(slot),而不是一個單獨管理的離散對象。針對使用者送出給裝置的業務,有多種不同的查詢對象可供使用,每種用于測量裝置上指令執行的一個方面。因為所有類型的查詢對象都通過池來管理,是以查詢的第一步是要建立一個查詢池對象,用于儲存這些查詢對象。

1.1 遮擋查詢

如果查詢池的查詢類型為VK_QUERY_TYPE_OCCLUSION,則統計通過深度和模闆測試的片段數。這可以用于确定可見性,甚至可以以像素為機關測量幾何體區域。

如果不太在意對象的可見區域,隻關心對象是否可見,則在建立查詢池的時候不在參數flags中設定VK_QUERY_CONTROL_PRECISE_BIT辨別。如果未設定此辨別,則查詢結果可視為布爾值(即0不可見,非零可見)。

二、主要實作

2.1 建立查詢池

我們建立一個setupQueryPool函數來建立用于存儲遮擋查詢結果的查詢池,如下:

// 查詢池
	VkQueryPool queryPool;

	// 片段數
	uint64_t passedSamples[2] = { 1,1 };

	void setupQueryPool()
	{
		VkQueryPoolCreateInfo queryPoolInfo = {};
		queryPoolInfo.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO;
		queryPoolInfo.queryType = VK_QUERY_TYPE_OCCLUSION;
		queryPoolInfo.queryCount = 2;
		vkCreateQueryPool(device, &queryPoolInfo, NULL, &queryPool);
	}
           

之後我們建立一個函數getQueryResults來檢索送出到指令緩沖區的阻塞查詢的結果:

void getQueryResults()
	{
		vkGetQueryPoolResults(
			device,
			queryPool,
			0,
			2,
			sizeof(passedSamples),
			passedSamples,
			sizeof(uint64_t),
			VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WAIT_BIT);
	}
           

上邊的擷取函數中我們使用vkGetQueryResults将結果複制到主機可見的緩沖區中并且将結果存儲為64位值,并等待結果完成。如果不想等待,可以使用VK_QUERY_RESULT_WITH_AVAILABILITY_BIT立刻傳回結果的狀态。

2.2 執行查詢

查詢通過在指令緩沖區中包含特定的開始和停止指令:vkCmdBeginQuery()和vkCmdEndQuery()來執行。

是以我們在buildCommandBuffers函數中來執行查詢操作:

void buildCommandBuffers()
	{
		...
		for (int32_t i = 0; i < drawCmdBuffers.size(); ++i)
		{
			...

			// 首先必須在渲染通道之前重置查詢池
			vkCmdResetQueryPool(drawCmdBuffers[i], queryPool, 0, 2);

			...
			// 查詢階段
			// 茶壺繪制查詢
			vkCmdBeginQuery(drawCmdBuffers[i], queryPool, 0, VK_FLAGS_NONE);

			vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSets.teapot, 0, NULL);
			vkCmdBindVertexBuffers(drawCmdBuffers[i], VERTEX_BUFFER_BIND_ID, 1, &models.teapot.vertices.buffer, offsets);
			vkCmdBindIndexBuffer(drawCmdBuffers[i], models.teapot.indices.buffer, 0, VK_INDEX_TYPE_UINT32);
			vkCmdDrawIndexed(drawCmdBuffers[i], models.teapot.indexCount, 1, 0, 0, 0);
			//結束查詢
			vkCmdEndQuery(drawCmdBuffers[i], queryPool, 0);

			// 圓球繪制開始查詢
			vkCmdBeginQuery(drawCmdBuffers[i], queryPool, 1, VK_FLAGS_NONE);

			vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSets.sphere, 0, NULL);
			vkCmdBindVertexBuffers(drawCmdBuffers[i], VERTEX_BUFFER_BIND_ID, 1, &models.sphere.vertices.buffer, offsets);
			vkCmdBindIndexBuffer(drawCmdBuffers[i], models.sphere.indices.buffer, 0, VK_INDEX_TYPE_UINT32);
			vkCmdDrawIndexed(drawCmdBuffers[i], models.sphere.indexCount, 1, 0, 0, 0);
			//結束查詢
			vkCmdEndQuery(drawCmdBuffers[i], queryPool, 1);

			// Visible pass
			// Clear color and depth attachments
			VkClearAttachment clearAttachments[2] = {};

			clearAttachments[0].aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
			clearAttachments[0].clearValue.color = defaultClearColor;
			clearAttachments[0].colorAttachment = 0;

			clearAttachments[1].aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
			clearAttachments[1].clearValue.depthStencil = { 1.0f, 0 };

			VkClearRect clearRect = {};
			clearRect.layerCount = 1;
			clearRect.rect.offset = { 0, 0 };
			clearRect.rect.extent = { width, height };

			vkCmdClearAttachments(
				drawCmdBuffers[i],
				2,
				clearAttachments,
				1,
				&clearRect);

			vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.solid);

			// 是否繪制茶壺
			if (passedSamples[0] > 0)
			{
			vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSets.teapot, 0, NULL);
			vkCmdBindVertexBuffers(drawCmdBuffers[i], VERTEX_BUFFER_BIND_ID, 1, &models.teapot.vertices.buffer, offsets);
			vkCmdBindIndexBuffer(drawCmdBuffers[i], models.teapot.indices.buffer, 0, VK_INDEX_TYPE_UINT32);
			vkCmdDrawIndexed(drawCmdBuffers[i], models.teapot.indexCount, 1, 0, 0, 0);
			}
			// 着色可見階段
			// 是否繪制茶壺球體
			if (passedSamples[1] > 0)
			{
			vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSets.sphere, 0, NULL);
			vkCmdBindVertexBuffers(drawCmdBuffers[i], VERTEX_BUFFER_BIND_ID, 1, &models.sphere.vertices.buffer, offsets);
			vkCmdBindIndexBuffer(drawCmdBuffers[i], models.sphere.indices.buffer, 0, VK_INDEX_TYPE_UINT32);
			vkCmdDrawIndexed(drawCmdBuffers[i], models.sphere.indexCount, 1, 0, 0, 0);
			}
			// 遮擋面繪制
			vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.occluder);
			vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSet, 0, NULL);
			vkCmdBindVertexBuffers(drawCmdBuffers[i], VERTEX_BUFFER_BIND_ID, 1, &models.plane.vertices.buffer, offsets);
			vkCmdBindIndexBuffer(drawCmdBuffers[i], models.plane.indices.buffer, 0, VK_INDEX_TYPE_UINT32);
			vkCmdDrawIndexed(drawCmdBuffers[i], models.plane.indexCount, 1, 0, 0, 0);

			vkCmdEndRenderPass(drawCmdBuffers[i]);

			vkEndCommandBuffer(drawCmdBuffers[i]);
		}
	}
           

2.3 更新查詢結果

通過上述執行查詢後我們需要在renderLoop中實時更新擷取到的片段數:

void draw()
	{
		updateUniformBuffers();
		VulkanExampleBase::prepareFrame();

		submitInfo.commandBufferCount = 1;
		submitInfo.pCommandBuffers = &drawCmdBuffers[currentBuffer];
		VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE));

		// 讀取查詢結果,以便在下一幀中使用
		getQueryResults();

		VulkanExampleBase::submitFrame();
	}
           

至此整個查詢過程結束,可以通過下圖對下遮擋剔除效果:

Vulkan_基于查詢池的遮擋剔除
Vulkan_基于查詢池的遮擋剔除