<reponame>IcanCUthere/Gravity-Engine
module;
#include <vulkan/vulkan.h>
#include "Core/DebugUtils.h"

module Graphics:Vulkan.CmdList;
import :Vulkan.Context;
import :Vulkan.Viewport;
import :Vulkan.Queue;

namespace GFX
{
	AVulkanCmdList::AVulkanCmdList(const VkCommandBuffer handle, const VkCommandBufferLevel level, const VkPipelineStageFlags waitStages)
		: Handle(handle), Semaphore(true), Level(level), WaitStages(waitStages)
	{
	}

	AVulkanCmdList::AVulkanCmdList(AVulkanCmdList&& other)
		: Handle(other.Handle), Level(other.Level), WaitStages(other.WaitStages), Semaphore(std::move(other.Semaphore))
	{
		other.Handle = VK_NULL_HANDLE;
		other.Level = VK_COMMAND_BUFFER_LEVEL_MAX_ENUM;
		other.WaitStages = VK_PIPELINE_STAGE_FLAG_BITS_MAX_ENUM;
	}

	void AVulkanCmdList::operator=(AVulkanCmdList&& other)
	{
		Handle = other.Handle;
		Level = other.Level;
		WaitStages = other.WaitStages;
		Semaphore = std::move(other.Semaphore);
		other.Handle = VK_NULL_HANDLE;
		other.Level = VK_COMMAND_BUFFER_LEVEL_MAX_ENUM;
		other.WaitStages = VK_PIPELINE_STAGE_FLAG_BITS_MAX_ENUM;
	}

	void AVulkanCmdList::BeginRecording(const ERenderStage stage, const IViewport* viewport)
	{
		const AVulkanViewport* vViewport = (AVulkanViewport*)viewport;

		VkCommandBufferInheritanceInfo inheritanceInfo;
		inheritanceInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO;
		inheritanceInfo.pNext = nullptr;
		inheritanceInfo.occlusionQueryEnable = false;
		inheritanceInfo.queryFlags = 0;
		inheritanceInfo.pipelineStatistics = 0;

		VkCommandBufferBeginInfo beginInfo;
		beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
		beginInfo.pNext = nullptr;

		if (stage == ERenderStage::Transfer) {
			inheritanceInfo.framebuffer = VK_NULL_HANDLE;
			inheritanceInfo.renderPass = VK_NULL_HANDLE;
			inheritanceInfo.subpass = (uint32_t)stage;
			beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
			beginInfo.pInheritanceInfo = nullptr;
		}
		else {
			inheritanceInfo.framebuffer = vViewport->GetSwapchain().GetFramebuffer().GetHandle();
			inheritanceInfo.renderPass = vViewport->GetSwapchain().GetRenderPass().GetHandle();
			inheritanceInfo.subpass = (uint32_t)stage;
			beginInfo.flags = VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT | VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
			beginInfo.pInheritanceInfo = (Level == VK_COMMAND_BUFFER_LEVEL_SECONDARY ? &inheritanceInfo : nullptr);
		}

		vkValidate(vkf::vkBeginCommandBuffer(Handle, &beginInfo))
	}

	void AVulkanCmdList::StopRecording()
	{
		vkValidate(vkf::vkEndCommandBuffer(Handle))
	}

	void AVulkanCmdList::Submit(const ISemaphore* waitSemaphore)
	{
		const AVulkanSemaphore* vSemaphore = (AVulkanSemaphore*)waitSemaphore;

		Semaphore.WaitForSignal();
		Semaphore.ResetFence();

		VkQueue submitQueue = nullptr;
		switch (WaitStages) {
		case VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT: submitQueue = AVulkanContext::GetDevice().GetRenderQueue().GetHandle(); break;
		case VK_PIPELINE_STAGE_TRANSFER_BIT: submitQueue = AVulkanContext::GetDevice().GetTransferQueue().GetHandle(); break;
		case VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT: /*TODO: implement compute queue*/ break;
		default: verify(0, "No valid submit queue found.");
		}

		VkSubmitInfo submitInfo = {};
		submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
		submitInfo.pNext = nullptr;
		submitInfo.pWaitDstStageMask = &WaitStages;
		submitInfo.waitSemaphoreCount = (vSemaphore ? 1 : 0);
		submitInfo.pWaitSemaphores = &vSemaphore->GetSemaphore();
		submitInfo.commandBufferCount = 1;
		submitInfo.pCommandBuffers = &Handle;
		submitInfo.signalSemaphoreCount = 1;
		submitInfo.pSignalSemaphores = &Semaphore.GetSemaphore();

		vkValidate(vkf::vkQueueSubmit(submitQueue, 1, &submitInfo, Semaphore.GetFence()))
	}

	void AVulkanCmdList::Reset()
	{
		Semaphore.WaitForSignal();
		vkValidate(vkf::vkResetCommandBuffer(Handle, 0))
	}

	void AVulkanCmdList::ConsumeLists(const std::vector<ICmdList*>& lists)
	{
		std::vector<VkCommandBuffer> handles;
		for (const ICmdList* list : lists) {
			AVulkanCmdList* vList = (AVulkanCmdList*)list;
			handles.push_back(vList->GetHandle());
		}

		vkf::vkCmdExecuteCommands(Handle, (uint32_t)handles.size(), handles.data());
	}

	void AVulkanCmdList::WaitToFinish() const
	{
		Semaphore.WaitForSignal();
	}

	ISemaphore* AVulkanCmdList::GetSemaphore() const
	{
		return (ISemaphore*)&Semaphore;
	}
}


