mirror of
https://github.com/jpd002/Play-.git
synced 2025-04-28 13:47:57 +03:00
785 lines
29 KiB
C++
785 lines
29 KiB
C++
#include "GSH_VulkanPresent.h"
|
|
#include "GSH_VulkanMemoryUtils.h"
|
|
#include "MemStream.h"
|
|
#include "vulkan/StructDefs.h"
|
|
#include "nuanceur/Builder.h"
|
|
#include "nuanceur/generators/SpirvShaderGenerator.h"
|
|
|
|
using namespace GSH_Vulkan;
|
|
|
|
#define DESCRIPTOR_LOCATION_BUFFER_MEMORY 0
|
|
#define DESCRIPTOR_LOCATION_IMAGE_SWIZZLETABLE 2
|
|
|
|
//Module responsible for presenting frame buffer to surface
|
|
|
|
// clang-format off
|
|
const CPresent::PRESENT_VERTEX CPresent::g_vertexBufferContents[4] =
|
|
{
|
|
//Pos UV
|
|
{ 0.0f, 0.0f, 0.0f, 0.0f, },
|
|
{ 1.0f, 0.0f, 1.0f, 0.0f, },
|
|
{ 0.0f, 1.0f, 0.0f, 1.0f, },
|
|
{ 1.0f, 1.0f, 1.0f, 1.0f, },
|
|
};
|
|
// clang-format on
|
|
|
|
CPresent::CPresent(const ContextPtr& context)
|
|
: m_context(context)
|
|
, m_pipelineCache(context->device)
|
|
{
|
|
CreateRenderPass();
|
|
CreateVertexBuffer();
|
|
|
|
CreateSwapChain();
|
|
}
|
|
|
|
CPresent::~CPresent()
|
|
{
|
|
DestroySwapChain();
|
|
for(const auto& presentCommandBuffer : m_presentCommandBuffers)
|
|
{
|
|
m_context->device.vkDestroyFence(m_context->device, presentCommandBuffer.execCompleteFence, nullptr);
|
|
}
|
|
m_context->device.vkDestroyRenderPass(m_context->device, m_renderPass, nullptr);
|
|
}
|
|
|
|
void CPresent::ValidateSwapChain(const CGSHandler::PRESENTATION_PARAMS& presentationParams)
|
|
{
|
|
m_swapChainValid =
|
|
(presentationParams.windowWidth == m_surfaceExtents.width) &&
|
|
(presentationParams.windowHeight == m_surfaceExtents.height);
|
|
}
|
|
|
|
void CPresent::SetPresentationViewport(const CGSHandler::PRESENTATION_VIEWPORT& presentationViewport)
|
|
{
|
|
m_presentationViewport = presentationViewport;
|
|
}
|
|
|
|
void CPresent::DoPresent(const CGSHandler::DISPLAY_INFO& dispInfo)
|
|
{
|
|
auto result = VK_SUCCESS;
|
|
|
|
if(!m_swapChainValid && (m_swapChain != VK_NULL_HANDLE))
|
|
{
|
|
m_context->device.vkQueueWaitIdle(m_context->queue);
|
|
DestroySwapChain();
|
|
}
|
|
|
|
if(m_swapChain == VK_NULL_HANDLE)
|
|
{
|
|
//Try creating the swap chain
|
|
CreateSwapChain();
|
|
//If it's still not valid, nevermind presenting, try later
|
|
if(m_swapChain == VK_NULL_HANDLE) return;
|
|
assert(m_swapChainValid);
|
|
}
|
|
|
|
const auto& swapChainSemaphores = m_swapChainSemaphores[m_currentSwapChainSemaphoreIndex++];
|
|
m_currentSwapChainSemaphoreIndex %= m_swapChainSemaphores.size();
|
|
|
|
uint32_t imageIndex = 0;
|
|
result = m_context->device.vkAcquireNextImageKHR(m_context->device, m_swapChain, UINT64_MAX, swapChainSemaphores.imageAcquireSemaphore, VK_NULL_HANDLE, &imageIndex);
|
|
if((result == VK_ERROR_OUT_OF_DATE_KHR) || (result == VK_ERROR_SURFACE_LOST_KHR))
|
|
{
|
|
m_context->device.vkQueueWaitIdle(m_context->queue);
|
|
DestroySwapChain();
|
|
return;
|
|
}
|
|
if(result != VK_SUBOPTIMAL_KHR) CHECKVULKANERROR(result);
|
|
|
|
UpdateBackbuffer(imageIndex, swapChainSemaphores, dispInfo);
|
|
|
|
//Queue present
|
|
{
|
|
auto presentInfo = Framework::Vulkan::PresentInfoKHR();
|
|
presentInfo.swapchainCount = 1;
|
|
presentInfo.pSwapchains = &m_swapChain;
|
|
presentInfo.pImageIndices = &imageIndex;
|
|
presentInfo.waitSemaphoreCount = 1;
|
|
presentInfo.pWaitSemaphores = &swapChainSemaphores.renderCompleteSemaphore;
|
|
result = m_context->device.vkQueuePresentKHR(m_context->queue, &presentInfo);
|
|
if(result == VK_ERROR_OUT_OF_DATE_KHR)
|
|
{
|
|
m_context->device.vkQueueWaitIdle(m_context->queue);
|
|
DestroySwapChain();
|
|
return;
|
|
}
|
|
if(result != VK_SUBOPTIMAL_KHR) CHECKVULKANERROR(result);
|
|
}
|
|
}
|
|
|
|
void CPresent::UpdateBackbuffer(uint32 imageIndex, const SWAPCHAIN_SEMAPHORES& swapChainSemaphores, const CGSHandler::DISPLAY_INFO& dispInfo)
|
|
{
|
|
auto result = VK_SUCCESS;
|
|
|
|
auto swapChainImage = m_swapChainImages[imageIndex];
|
|
auto framebuffer = m_swapChainFramebuffers[imageIndex];
|
|
|
|
auto frameCommandBuffer = PrepareCommandBuffer();
|
|
auto commandBuffer = frameCommandBuffer.commandBuffer;
|
|
|
|
auto commandBufferBeginInfo = Framework::Vulkan::CommandBufferBeginInfo();
|
|
commandBufferBeginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
|
|
result = m_context->device.vkBeginCommandBuffer(commandBuffer, &commandBufferBeginInfo);
|
|
CHECKVULKANERROR(result);
|
|
|
|
m_context->annotations.PushCommandLabel(commandBuffer, "Present");
|
|
|
|
//Transition image from present to color attachment
|
|
{
|
|
auto imageMemoryBarrier = Framework::Vulkan::ImageMemoryBarrier();
|
|
imageMemoryBarrier.image = swapChainImage;
|
|
imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
|
//imageMemoryBarrier.srcAccessMask = VK_ACCESS_MEMORY_READ_BIT;
|
|
imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
|
imageMemoryBarrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
|
imageMemoryBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
|
imageMemoryBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
|
imageMemoryBarrier.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1};
|
|
|
|
m_context->device.vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
|
0, 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier);
|
|
}
|
|
|
|
VkClearValue clearValue;
|
|
clearValue.color = {{0.0f, 0.0f, 0.0f, 1.0f}};
|
|
|
|
//Begin render pass
|
|
auto renderPassBeginInfo = Framework::Vulkan::RenderPassBeginInfo();
|
|
renderPassBeginInfo.renderPass = m_renderPass;
|
|
renderPassBeginInfo.renderArea.extent = m_surfaceExtents;
|
|
renderPassBeginInfo.clearValueCount = 1;
|
|
renderPassBeginInfo.pClearValues = &clearValue;
|
|
renderPassBeginInfo.framebuffer = framebuffer;
|
|
|
|
m_context->device.vkCmdBeginRenderPass(commandBuffer, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
|
|
|
|
{
|
|
VkViewport viewport = {};
|
|
viewport.x = m_presentationViewport.offsetX;
|
|
viewport.y = m_presentationViewport.offsetY;
|
|
viewport.width = m_presentationViewport.width;
|
|
viewport.height = m_presentationViewport.height;
|
|
viewport.maxDepth = 1.0f;
|
|
m_context->device.vkCmdSetViewport(commandBuffer, 0, 1, &viewport);
|
|
|
|
VkRect2D scissor = {};
|
|
scissor.extent = m_surfaceExtents;
|
|
m_context->device.vkCmdSetScissor(commandBuffer, 0, 1, &scissor);
|
|
}
|
|
|
|
for(const auto& dispLayer : dispInfo.layers)
|
|
{
|
|
if(!dispLayer.enabled) continue;
|
|
|
|
float blendAlpha = std::min(static_cast<float>(dispLayer.constantAlpha) * 2.f / 255.f, 1.f);
|
|
|
|
auto caps = make_convertible<PIPELINE_CAPS>(0);
|
|
caps.bufPsm = dispLayer.psm;
|
|
if(dispLayer.useConstantAlpha)
|
|
{
|
|
caps.blendMode = (blendAlpha != 1.0f) ? BLEND_MODE_CST_ALPHA : BLEND_MODE_NONE;
|
|
}
|
|
else
|
|
{
|
|
caps.blendMode = BLEND_MODE_SRC_ALPHA;
|
|
}
|
|
|
|
//Find pipeline and create it if we've never encountered it before
|
|
auto drawPipeline = m_pipelineCache.TryGetPipeline(caps);
|
|
if(!drawPipeline)
|
|
{
|
|
drawPipeline = m_pipelineCache.RegisterPipeline(caps, CreateDrawPipeline(caps));
|
|
}
|
|
|
|
PRESENT_PARAMS presentParams = {};
|
|
presentParams.bufAddress = dispLayer.bufPtr;
|
|
presentParams.bufWidth = dispLayer.bufWidth;
|
|
presentParams.dispWidth = dispInfo.width;
|
|
presentParams.dispHeight = dispInfo.height;
|
|
presentParams.layerX = dispLayer.offsetX;
|
|
presentParams.layerY = dispLayer.offsetY;
|
|
presentParams.layerWidth = dispLayer.width;
|
|
presentParams.layerHeight = dispLayer.height;
|
|
|
|
auto descriptorSet = PrepareDescriptorSet(drawPipeline->descriptorSetLayout, dispLayer.psm);
|
|
|
|
m_context->device.vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, drawPipeline->pipelineLayout,
|
|
0, 1, &descriptorSet, 0, nullptr);
|
|
|
|
m_context->device.vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, drawPipeline->pipeline);
|
|
|
|
float blendConstants[4] = {0, 0, 0, blendAlpha};
|
|
m_context->device.vkCmdSetBlendConstants(commandBuffer, blendConstants);
|
|
m_context->device.vkCmdPushConstants(commandBuffer, drawPipeline->pipelineLayout, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(PRESENT_PARAMS), &presentParams);
|
|
|
|
VkDeviceSize vertexBufferOffset = 0;
|
|
VkBuffer vertexBuffer = m_vertexBuffer;
|
|
m_context->device.vkCmdBindVertexBuffers(commandBuffer, 0, 1, &vertexBuffer, &vertexBufferOffset);
|
|
|
|
m_context->device.vkCmdDraw(commandBuffer, 4, 1, 0, 0);
|
|
}
|
|
|
|
m_context->device.vkCmdEndRenderPass(commandBuffer);
|
|
|
|
m_context->annotations.PopCommandLabel(commandBuffer);
|
|
|
|
m_context->device.vkEndCommandBuffer(commandBuffer);
|
|
|
|
//Submit command buffer
|
|
{
|
|
VkPipelineStageFlags pipelineStageFlags = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
|
auto submitInfo = Framework::Vulkan::SubmitInfo();
|
|
submitInfo.waitSemaphoreCount = 1;
|
|
submitInfo.pWaitSemaphores = &swapChainSemaphores.imageAcquireSemaphore;
|
|
submitInfo.pWaitDstStageMask = &pipelineStageFlags;
|
|
submitInfo.commandBufferCount = 1;
|
|
submitInfo.pCommandBuffers = &commandBuffer;
|
|
submitInfo.signalSemaphoreCount = 1;
|
|
submitInfo.pSignalSemaphores = &swapChainSemaphores.renderCompleteSemaphore;
|
|
result = m_context->device.vkQueueSubmit(m_context->queue, 1, &submitInfo, frameCommandBuffer.execCompleteFence);
|
|
CHECKVULKANERROR(result);
|
|
}
|
|
}
|
|
|
|
CPresent::PRESENT_COMMANDBUFFER CPresent::PrepareCommandBuffer()
|
|
{
|
|
auto result = VK_SUCCESS;
|
|
|
|
//Find an available command buffer
|
|
for(const auto& presentCommandBuffer : m_presentCommandBuffers)
|
|
{
|
|
result = m_context->device.vkGetFenceStatus(m_context->device, presentCommandBuffer.execCompleteFence);
|
|
if(result == VK_SUCCESS)
|
|
{
|
|
result = m_context->device.vkResetFences(m_context->device, 1, &presentCommandBuffer.execCompleteFence);
|
|
CHECKVULKANERROR(result);
|
|
|
|
result = m_context->device.vkResetCommandBuffer(presentCommandBuffer.commandBuffer, VK_COMMAND_BUFFER_RESET_RELEASE_RESOURCES_BIT);
|
|
CHECKVULKANERROR(result);
|
|
|
|
return presentCommandBuffer;
|
|
}
|
|
}
|
|
|
|
auto presentCommandBuffer = PRESENT_COMMANDBUFFER();
|
|
presentCommandBuffer.commandBuffer = m_context->commandBufferPool.AllocateBuffer();
|
|
|
|
{
|
|
auto fenceCreateInfo = Framework::Vulkan::FenceCreateInfo();
|
|
result = m_context->device.vkCreateFence(m_context->device, &fenceCreateInfo, nullptr, &presentCommandBuffer.execCompleteFence);
|
|
CHECKVULKANERROR(result);
|
|
}
|
|
|
|
m_presentCommandBuffers.push_back(presentCommandBuffer);
|
|
return presentCommandBuffer;
|
|
}
|
|
|
|
VkDescriptorSet CPresent::PrepareDescriptorSet(VkDescriptorSetLayout descriptorSetLayout, uint32 bufPsm)
|
|
{
|
|
auto descriptorSetIterator = m_descriptorSetCache.find(bufPsm);
|
|
if(descriptorSetIterator != std::end(m_descriptorSetCache))
|
|
{
|
|
return descriptorSetIterator->second;
|
|
}
|
|
|
|
auto result = VK_SUCCESS;
|
|
VkDescriptorSet descriptorSet = VK_NULL_HANDLE;
|
|
|
|
//Allocate descriptor set
|
|
{
|
|
auto setAllocateInfo = Framework::Vulkan::DescriptorSetAllocateInfo();
|
|
setAllocateInfo.descriptorPool = m_context->descriptorPool;
|
|
setAllocateInfo.descriptorSetCount = 1;
|
|
setAllocateInfo.pSetLayouts = &descriptorSetLayout;
|
|
|
|
result = m_context->device.vkAllocateDescriptorSets(m_context->device, &setAllocateInfo, &descriptorSet);
|
|
CHECKVULKANERROR(result);
|
|
}
|
|
|
|
//Update descriptor set
|
|
{
|
|
VkDescriptorBufferInfo descriptorMemoryBufferInfo = {};
|
|
descriptorMemoryBufferInfo.buffer = m_context->memoryBuffer;
|
|
descriptorMemoryBufferInfo.range = VK_WHOLE_SIZE;
|
|
|
|
VkDescriptorImageInfo descriptorSwizzleTableImageInfo = {};
|
|
descriptorSwizzleTableImageInfo.imageView = m_context->GetSwizzleTable(bufPsm);
|
|
descriptorSwizzleTableImageInfo.imageLayout = VK_IMAGE_LAYOUT_GENERAL;
|
|
|
|
std::vector<VkWriteDescriptorSet> writes;
|
|
|
|
{
|
|
auto writeSet = Framework::Vulkan::WriteDescriptorSet();
|
|
writeSet.dstSet = descriptorSet;
|
|
writeSet.dstBinding = DESCRIPTOR_LOCATION_BUFFER_MEMORY;
|
|
writeSet.descriptorCount = 1;
|
|
writeSet.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
|
|
writeSet.pBufferInfo = &descriptorMemoryBufferInfo;
|
|
writes.push_back(writeSet);
|
|
}
|
|
|
|
{
|
|
auto writeSet = Framework::Vulkan::WriteDescriptorSet();
|
|
writeSet.dstSet = descriptorSet;
|
|
writeSet.dstBinding = DESCRIPTOR_LOCATION_IMAGE_SWIZZLETABLE;
|
|
writeSet.descriptorCount = 1;
|
|
writeSet.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
|
|
writeSet.pImageInfo = &descriptorSwizzleTableImageInfo;
|
|
writes.push_back(writeSet);
|
|
}
|
|
|
|
m_context->device.vkUpdateDescriptorSets(m_context->device, writes.size(), writes.data(), 0, nullptr);
|
|
}
|
|
|
|
m_descriptorSetCache.insert(std::make_pair(bufPsm, descriptorSet));
|
|
|
|
return descriptorSet;
|
|
}
|
|
|
|
void CPresent::CreateSwapChain()
|
|
{
|
|
assert(!m_context->device.IsEmpty());
|
|
assert(m_swapChain == VK_NULL_HANDLE);
|
|
assert(m_swapChainImages.empty());
|
|
|
|
auto result = VK_SUCCESS;
|
|
|
|
VkSurfaceCapabilitiesKHR surfaceCaps = {};
|
|
result = m_context->instance->vkGetPhysicalDeviceSurfaceCapabilitiesKHR(m_context->physicalDevice, m_context->surface, &surfaceCaps);
|
|
if(result == VK_ERROR_SURFACE_LOST_KHR)
|
|
{
|
|
return;
|
|
}
|
|
CHECKVULKANERROR(result);
|
|
m_surfaceExtents = surfaceCaps.currentExtent;
|
|
|
|
auto swapChainCreateInfo = Framework::Vulkan::SwapchainCreateInfoKHR();
|
|
swapChainCreateInfo.surface = m_context->surface;
|
|
//Make sure to check that MAX_FRAMES in CFrameCommandBuffer is at least as big as minImageCount
|
|
swapChainCreateInfo.minImageCount = 3; //Recommended by nVidia in UsingtheVulkanAPI_20160216.pdf
|
|
swapChainCreateInfo.imageFormat = m_context->surfaceFormat.format;
|
|
swapChainCreateInfo.imageColorSpace = m_context->surfaceFormat.colorSpace;
|
|
swapChainCreateInfo.imageExtent = m_surfaceExtents;
|
|
swapChainCreateInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
|
|
swapChainCreateInfo.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
|
|
swapChainCreateInfo.imageArrayLayers = 1;
|
|
swapChainCreateInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
|
swapChainCreateInfo.queueFamilyIndexCount = 0;
|
|
swapChainCreateInfo.pQueueFamilyIndices = nullptr;
|
|
swapChainCreateInfo.presentMode = VK_PRESENT_MODE_MAILBOX_KHR;
|
|
swapChainCreateInfo.clipped = VK_TRUE;
|
|
swapChainCreateInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
|
|
|
|
result = m_context->device.vkCreateSwapchainKHR(m_context->device, &swapChainCreateInfo, nullptr, &m_swapChain);
|
|
if(result == VK_ERROR_SURFACE_LOST_KHR)
|
|
{
|
|
return;
|
|
}
|
|
CHECKVULKANERROR(result);
|
|
|
|
uint32_t imageCount = 0;
|
|
result = m_context->device.vkGetSwapchainImagesKHR(m_context->device, m_swapChain, &imageCount, nullptr);
|
|
CHECKVULKANERROR(result);
|
|
|
|
m_swapChainImages.resize(imageCount);
|
|
result = m_context->device.vkGetSwapchainImagesKHR(m_context->device, m_swapChain, &imageCount, m_swapChainImages.data());
|
|
CHECKVULKANERROR(result);
|
|
|
|
for(int i = 0; i < imageCount; i++)
|
|
{
|
|
SWAPCHAIN_SEMAPHORES swapChainSemaphores;
|
|
|
|
{
|
|
auto semaphoreCreateInfo = Framework::Vulkan::SemaphoreCreateInfo();
|
|
auto result = m_context->device.vkCreateSemaphore(m_context->device, &semaphoreCreateInfo, nullptr, &swapChainSemaphores.imageAcquireSemaphore);
|
|
CHECKVULKANERROR(result);
|
|
}
|
|
|
|
{
|
|
auto semaphoreCreateInfo = Framework::Vulkan::SemaphoreCreateInfo();
|
|
auto result = m_context->device.vkCreateSemaphore(m_context->device, &semaphoreCreateInfo, nullptr, &swapChainSemaphores.renderCompleteSemaphore);
|
|
CHECKVULKANERROR(result);
|
|
}
|
|
|
|
m_swapChainSemaphores.push_back(swapChainSemaphores);
|
|
}
|
|
|
|
CreateSwapChainImageViews();
|
|
CreateSwapChainFramebuffers();
|
|
|
|
m_swapChainValid = true;
|
|
}
|
|
|
|
void CPresent::CreateSwapChainImageViews()
|
|
{
|
|
assert(!m_context->device.IsEmpty());
|
|
assert(m_swapChainImageViews.empty());
|
|
|
|
for(const auto& image : m_swapChainImages)
|
|
{
|
|
auto imageViewCreateInfo = Framework::Vulkan::ImageViewCreateInfo();
|
|
imageViewCreateInfo.format = m_context->surfaceFormat.format;
|
|
imageViewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
|
imageViewCreateInfo.image = image;
|
|
imageViewCreateInfo.components =
|
|
{
|
|
VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G,
|
|
VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A};
|
|
imageViewCreateInfo.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1};
|
|
|
|
VkImageView imageView = VK_NULL_HANDLE;
|
|
auto result = m_context->device.vkCreateImageView(m_context->device, &imageViewCreateInfo, nullptr, &imageView);
|
|
CHECKVULKANERROR(result);
|
|
m_swapChainImageViews.push_back(imageView);
|
|
}
|
|
}
|
|
|
|
void CPresent::CreateSwapChainFramebuffers()
|
|
{
|
|
assert(!m_context->device.IsEmpty());
|
|
assert(!m_swapChainImageViews.empty());
|
|
|
|
for(const auto& imageView : m_swapChainImageViews)
|
|
{
|
|
auto frameBufferCreateInfo = Framework::Vulkan::FramebufferCreateInfo();
|
|
frameBufferCreateInfo.renderPass = m_renderPass;
|
|
frameBufferCreateInfo.attachmentCount = 1;
|
|
frameBufferCreateInfo.pAttachments = &imageView;
|
|
frameBufferCreateInfo.width = m_surfaceExtents.width;
|
|
frameBufferCreateInfo.height = m_surfaceExtents.height;
|
|
frameBufferCreateInfo.layers = 1;
|
|
|
|
VkFramebuffer framebuffer = VK_NULL_HANDLE;
|
|
auto result = m_context->device.vkCreateFramebuffer(m_context->device, &frameBufferCreateInfo, nullptr, &framebuffer);
|
|
CHECKVULKANERROR(result);
|
|
m_swapChainFramebuffers.push_back(framebuffer);
|
|
}
|
|
}
|
|
|
|
void CPresent::DestroySwapChain()
|
|
{
|
|
for(auto swapChainFramebuffer : m_swapChainFramebuffers)
|
|
{
|
|
m_context->device.vkDestroyFramebuffer(m_context->device, swapChainFramebuffer, nullptr);
|
|
}
|
|
for(auto swapChainImageView : m_swapChainImageViews)
|
|
{
|
|
m_context->device.vkDestroyImageView(m_context->device, swapChainImageView, nullptr);
|
|
}
|
|
for(const auto& swapChainSemaphores : m_swapChainSemaphores)
|
|
{
|
|
m_context->device.vkDestroySemaphore(m_context->device, swapChainSemaphores.imageAcquireSemaphore, nullptr);
|
|
m_context->device.vkDestroySemaphore(m_context->device, swapChainSemaphores.renderCompleteSemaphore, nullptr);
|
|
}
|
|
m_context->device.vkDestroySwapchainKHR(m_context->device, m_swapChain, nullptr);
|
|
|
|
m_swapChainImages.clear();
|
|
m_swapChainImageViews.clear();
|
|
m_swapChainFramebuffers.clear();
|
|
m_swapChainSemaphores.clear();
|
|
m_currentSwapChainSemaphoreIndex = 0;
|
|
m_swapChain = VK_NULL_HANDLE;
|
|
}
|
|
|
|
void CPresent::CreateRenderPass()
|
|
{
|
|
assert(m_renderPass == VK_NULL_HANDLE);
|
|
|
|
auto result = VK_SUCCESS;
|
|
|
|
VkAttachmentDescription colorAttachment = {};
|
|
colorAttachment.format = m_context->surfaceFormat.format;
|
|
colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
|
|
colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
|
|
colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
|
|
colorAttachment.initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
|
colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
|
|
|
|
VkAttachmentReference colorRef = {};
|
|
colorRef.attachment = 0;
|
|
colorRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
|
|
|
VkSubpassDescription subpass = {};
|
|
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
|
|
subpass.colorAttachmentCount = 1;
|
|
subpass.pColorAttachments = &colorRef;
|
|
|
|
auto renderPassCreateInfo = Framework::Vulkan::RenderPassCreateInfo();
|
|
renderPassCreateInfo.attachmentCount = 1;
|
|
renderPassCreateInfo.pAttachments = &colorAttachment;
|
|
renderPassCreateInfo.subpassCount = 1;
|
|
renderPassCreateInfo.pSubpasses = &subpass;
|
|
|
|
result = m_context->device.vkCreateRenderPass(m_context->device, &renderPassCreateInfo, nullptr, &m_renderPass);
|
|
CHECKVULKANERROR(result);
|
|
}
|
|
|
|
PIPELINE CPresent::CreateDrawPipeline(const PIPELINE_CAPS& caps)
|
|
{
|
|
PIPELINE drawPipeline;
|
|
|
|
auto vertexShader = CreateVertexShader();
|
|
auto fragmentShader = CreateFragmentShader(caps);
|
|
|
|
auto result = VK_SUCCESS;
|
|
|
|
{
|
|
std::vector<VkDescriptorSetLayoutBinding> setLayoutBindings;
|
|
|
|
{
|
|
VkDescriptorSetLayoutBinding setLayoutBinding = {};
|
|
setLayoutBinding.binding = DESCRIPTOR_LOCATION_BUFFER_MEMORY;
|
|
setLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
|
|
setLayoutBinding.descriptorCount = 1;
|
|
setLayoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
|
|
setLayoutBindings.push_back(setLayoutBinding);
|
|
}
|
|
|
|
{
|
|
VkDescriptorSetLayoutBinding setLayoutBinding = {};
|
|
setLayoutBinding.binding = DESCRIPTOR_LOCATION_IMAGE_SWIZZLETABLE;
|
|
setLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
|
|
setLayoutBinding.descriptorCount = 1;
|
|
setLayoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
|
|
setLayoutBindings.push_back(setLayoutBinding);
|
|
}
|
|
|
|
auto setLayoutCreateInfo = Framework::Vulkan::DescriptorSetLayoutCreateInfo();
|
|
setLayoutCreateInfo.bindingCount = static_cast<uint32>(setLayoutBindings.size());
|
|
setLayoutCreateInfo.pBindings = setLayoutBindings.data();
|
|
|
|
result = m_context->device.vkCreateDescriptorSetLayout(m_context->device, &setLayoutCreateInfo, nullptr, &drawPipeline.descriptorSetLayout);
|
|
CHECKVULKANERROR(result);
|
|
}
|
|
|
|
{
|
|
VkPushConstantRange pushConstantInfo = {};
|
|
pushConstantInfo.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
|
|
pushConstantInfo.offset = 0;
|
|
pushConstantInfo.size = sizeof(PRESENT_PARAMS);
|
|
|
|
auto pipelineLayoutCreateInfo = Framework::Vulkan::PipelineLayoutCreateInfo();
|
|
pipelineLayoutCreateInfo.pushConstantRangeCount = 1;
|
|
pipelineLayoutCreateInfo.pPushConstantRanges = &pushConstantInfo;
|
|
pipelineLayoutCreateInfo.setLayoutCount = 1;
|
|
pipelineLayoutCreateInfo.pSetLayouts = &drawPipeline.descriptorSetLayout;
|
|
|
|
result = m_context->device.vkCreatePipelineLayout(m_context->device, &pipelineLayoutCreateInfo, nullptr, &drawPipeline.pipelineLayout);
|
|
CHECKVULKANERROR(result);
|
|
}
|
|
|
|
auto inputAssemblyInfo = Framework::Vulkan::PipelineInputAssemblyStateCreateInfo();
|
|
inputAssemblyInfo.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
|
|
|
|
std::vector<VkVertexInputAttributeDescription> vertexAttributes;
|
|
|
|
{
|
|
VkVertexInputAttributeDescription positionVertexAttributeDesc = {};
|
|
positionVertexAttributeDesc.format = VK_FORMAT_R32G32_SFLOAT;
|
|
positionVertexAttributeDesc.offset = offsetof(PRESENT_VERTEX, x);
|
|
positionVertexAttributeDesc.location = 0;
|
|
vertexAttributes.push_back(positionVertexAttributeDesc);
|
|
}
|
|
|
|
{
|
|
VkVertexInputAttributeDescription texCoordVertexAttributeDesc = {};
|
|
texCoordVertexAttributeDesc.format = VK_FORMAT_R32G32_SFLOAT;
|
|
texCoordVertexAttributeDesc.offset = offsetof(PRESENT_VERTEX, u);
|
|
texCoordVertexAttributeDesc.location = 1;
|
|
vertexAttributes.push_back(texCoordVertexAttributeDesc);
|
|
}
|
|
|
|
VkVertexInputBindingDescription binding = {};
|
|
binding.stride = sizeof(PRESENT_VERTEX);
|
|
binding.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
|
|
|
|
auto vertexInputInfo = Framework::Vulkan::PipelineVertexInputStateCreateInfo();
|
|
vertexInputInfo.vertexBindingDescriptionCount = 1;
|
|
vertexInputInfo.pVertexBindingDescriptions = &binding;
|
|
vertexInputInfo.vertexAttributeDescriptionCount = vertexAttributes.size();
|
|
vertexInputInfo.pVertexAttributeDescriptions = vertexAttributes.data();
|
|
|
|
auto rasterStateInfo = Framework::Vulkan::PipelineRasterizationStateCreateInfo();
|
|
rasterStateInfo.polygonMode = VK_POLYGON_MODE_FILL;
|
|
rasterStateInfo.cullMode = VK_CULL_MODE_NONE;
|
|
rasterStateInfo.lineWidth = 1.0f;
|
|
|
|
VkPipelineColorBlendAttachmentState blendAttachment = {};
|
|
blendAttachment.colorWriteMask = 0xF;
|
|
switch(caps.blendMode)
|
|
{
|
|
case BLEND_MODE_NONE:
|
|
break;
|
|
case BLEND_MODE_SRC_ALPHA:
|
|
blendAttachment.blendEnable = true;
|
|
blendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
|
|
blendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
|
|
break;
|
|
case BLEND_MODE_CST_ALPHA:
|
|
blendAttachment.blendEnable = true;
|
|
blendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_CONSTANT_COLOR;
|
|
blendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR;
|
|
}
|
|
|
|
auto colorBlendStateInfo = Framework::Vulkan::PipelineColorBlendStateCreateInfo();
|
|
colorBlendStateInfo.attachmentCount = 1;
|
|
colorBlendStateInfo.pAttachments = &blendAttachment;
|
|
|
|
auto viewportStateInfo = Framework::Vulkan::PipelineViewportStateCreateInfo();
|
|
viewportStateInfo.viewportCount = 1;
|
|
viewportStateInfo.scissorCount = 1;
|
|
|
|
auto depthStencilStateInfo = Framework::Vulkan::PipelineDepthStencilStateCreateInfo();
|
|
|
|
auto multisampleStateInfo = Framework::Vulkan::PipelineMultisampleStateCreateInfo();
|
|
multisampleStateInfo.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
|
|
|
|
static const VkDynamicState dynamicStates[] =
|
|
{
|
|
VK_DYNAMIC_STATE_VIEWPORT,
|
|
VK_DYNAMIC_STATE_SCISSOR,
|
|
VK_DYNAMIC_STATE_BLEND_CONSTANTS,
|
|
};
|
|
auto dynamicStateInfo = Framework::Vulkan::PipelineDynamicStateCreateInfo();
|
|
dynamicStateInfo.pDynamicStates = dynamicStates;
|
|
dynamicStateInfo.dynamicStateCount = sizeof(dynamicStates) / sizeof(dynamicStates[0]);
|
|
|
|
VkPipelineShaderStageCreateInfo shaderStages[2] =
|
|
{
|
|
{VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO},
|
|
{VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO},
|
|
};
|
|
|
|
shaderStages[0].stage = VK_SHADER_STAGE_VERTEX_BIT;
|
|
shaderStages[0].module = vertexShader;
|
|
shaderStages[0].pName = "main";
|
|
shaderStages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;
|
|
shaderStages[1].module = fragmentShader;
|
|
shaderStages[1].pName = "main";
|
|
|
|
auto pipelineCreateInfo = Framework::Vulkan::GraphicsPipelineCreateInfo();
|
|
pipelineCreateInfo.stageCount = 2;
|
|
pipelineCreateInfo.pStages = shaderStages;
|
|
pipelineCreateInfo.pInputAssemblyState = &inputAssemblyInfo;
|
|
pipelineCreateInfo.pVertexInputState = &vertexInputInfo;
|
|
pipelineCreateInfo.pRasterizationState = &rasterStateInfo;
|
|
pipelineCreateInfo.pColorBlendState = &colorBlendStateInfo;
|
|
pipelineCreateInfo.pViewportState = &viewportStateInfo;
|
|
pipelineCreateInfo.pDepthStencilState = &depthStencilStateInfo;
|
|
pipelineCreateInfo.pMultisampleState = &multisampleStateInfo;
|
|
pipelineCreateInfo.pDynamicState = &dynamicStateInfo;
|
|
pipelineCreateInfo.renderPass = m_renderPass;
|
|
pipelineCreateInfo.layout = drawPipeline.pipelineLayout;
|
|
|
|
result = m_context->device.vkCreateGraphicsPipelines(m_context->device, VK_NULL_HANDLE, 1, &pipelineCreateInfo, nullptr, &drawPipeline.pipeline);
|
|
CHECKVULKANERROR(result);
|
|
|
|
return drawPipeline;
|
|
}
|
|
|
|
Framework::Vulkan::CShaderModule CPresent::CreateVertexShader()
|
|
{
|
|
using namespace Nuanceur;
|
|
|
|
auto b = CShaderBuilder();
|
|
|
|
{
|
|
auto inputPosition = CFloat4Lvalue(b.CreateInput(Nuanceur::SEMANTIC_POSITION));
|
|
auto inputTexCoord = CFloat4Lvalue(b.CreateInput(Nuanceur::SEMANTIC_TEXCOORD));
|
|
|
|
auto outputPosition = CFloat4Lvalue(b.CreateOutput(Nuanceur::SEMANTIC_SYSTEM_POSITION));
|
|
auto outputTexCoord = CFloat4Lvalue(b.CreateOutput(Nuanceur::SEMANTIC_TEXCOORD));
|
|
|
|
auto presentParams = CInt4Lvalue(b.CreateUniformInt4("presentParams", Nuanceur::UNIFORM_UNIT_PUSHCONSTANT));
|
|
auto presentRectParams = CInt4Lvalue(b.CreateUniformInt4("presentRectParams", Nuanceur::UNIFORM_UNIT_PUSHCONSTANT));
|
|
auto dispSize = presentParams->zw();
|
|
auto layerPos = presentRectParams->xy();
|
|
auto layerSize = presentRectParams->zw();
|
|
|
|
auto localPos = (inputPosition->xy() * ToFloat(layerSize)) + ToFloat(layerPos);
|
|
|
|
auto projScale = NewFloat2(b, 2, 2) / ToFloat(dispSize);
|
|
auto projOffset = NewFloat2(b, -1, -1);
|
|
auto projPos = (localPos * projScale) + projOffset;
|
|
|
|
outputPosition = NewFloat4(projPos, NewFloat2(b, 0.0f, 1.0f));
|
|
outputTexCoord = inputTexCoord->xyzw();
|
|
}
|
|
|
|
Framework::CMemStream shaderStream;
|
|
Nuanceur::CSpirvShaderGenerator::Generate(shaderStream, b, Nuanceur::CSpirvShaderGenerator::SHADER_TYPE_VERTEX);
|
|
shaderStream.Seek(0, Framework::STREAM_SEEK_SET);
|
|
return Framework::Vulkan::CShaderModule(m_context->device, shaderStream);
|
|
}
|
|
|
|
Framework::Vulkan::CShaderModule CPresent::CreateFragmentShader(const PIPELINE_CAPS& caps)
|
|
{
|
|
using namespace Nuanceur;
|
|
|
|
auto b = CShaderBuilder();
|
|
|
|
{
|
|
auto inputPosition = CFloat4Lvalue(b.CreateInput(Nuanceur::SEMANTIC_SYSTEM_POSITION));
|
|
auto inputTexCoord = CFloat4Lvalue(b.CreateInput(Nuanceur::SEMANTIC_TEXCOORD));
|
|
|
|
auto outputColor = CFloat4Lvalue(b.CreateOutput(Nuanceur::SEMANTIC_SYSTEM_COLOR));
|
|
auto memoryBuffer = CArrayUintValue(b.CreateUniformArrayUint("memoryBuffer", DESCRIPTOR_LOCATION_BUFFER_MEMORY));
|
|
auto swizzleTable = CImageUint2DValue(b.CreateImage2DUint(DESCRIPTOR_LOCATION_IMAGE_SWIZZLETABLE));
|
|
|
|
auto presentBufParams = CInt4Lvalue(b.CreateUniformInt4("presentBufParams", Nuanceur::UNIFORM_UNIT_PUSHCONSTANT));
|
|
auto presentRectParams = CInt4Lvalue(b.CreateUniformInt4("presentRectParams", Nuanceur::UNIFORM_UNIT_PUSHCONSTANT));
|
|
auto bufAddress = presentBufParams->x();
|
|
auto bufWidth = presentBufParams->y();
|
|
auto layerSize = presentRectParams->zw();
|
|
|
|
auto screenPos = ToInt(inputTexCoord->xy() * ToFloat(layerSize));
|
|
|
|
switch(caps.bufPsm)
|
|
{
|
|
default:
|
|
assert(false);
|
|
[[fallthrough]];
|
|
case CGSHandler::PSMCT32:
|
|
case CGSHandler::PSMCT24:
|
|
{
|
|
auto address = CMemoryUtils::GetPixelAddress<CGsPixelFormats::STORAGEPSMCT32>(
|
|
b, swizzleTable, bufAddress, bufWidth, screenPos);
|
|
auto imageColor = CMemoryUtils::Memory_Read32(b, memoryBuffer, address);
|
|
outputColor = CMemoryUtils::PSM32ToVec4(b, imageColor);
|
|
}
|
|
break;
|
|
case CGSHandler::PSMCT16:
|
|
case CGSHandler::PSMCT16S:
|
|
{
|
|
auto address = CMemoryUtils::GetPixelAddress<CGsPixelFormats::STORAGEPSMCT16>(
|
|
b, swizzleTable, bufAddress, bufWidth, screenPos);
|
|
auto imageColor = CMemoryUtils::Memory_Read16(b, memoryBuffer, address);
|
|
outputColor = CMemoryUtils::PSM16ToVec4(b, imageColor);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
Framework::CMemStream shaderStream;
|
|
Nuanceur::CSpirvShaderGenerator::Generate(shaderStream, b, Nuanceur::CSpirvShaderGenerator::SHADER_TYPE_FRAGMENT);
|
|
shaderStream.Seek(0, Framework::STREAM_SEEK_SET);
|
|
return Framework::Vulkan::CShaderModule(m_context->device, shaderStream);
|
|
}
|
|
|
|
void CPresent::CreateVertexBuffer()
|
|
{
|
|
auto result = VK_SUCCESS;
|
|
|
|
m_vertexBuffer = Framework::Vulkan::CBuffer(
|
|
m_context->device, m_context->physicalDeviceMemoryProperties,
|
|
VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, sizeof(g_vertexBufferContents));
|
|
|
|
{
|
|
void* bufferMemoryData = nullptr;
|
|
result = m_context->device.vkMapMemory(m_context->device, m_vertexBuffer.GetMemory(), 0, VK_WHOLE_SIZE, 0, &bufferMemoryData);
|
|
CHECKVULKANERROR(result);
|
|
memcpy(bufferMemoryData, g_vertexBufferContents, sizeof(g_vertexBufferContents));
|
|
m_context->device.vkUnmapMemory(m_context->device, m_vertexBuffer.GetMemory());
|
|
}
|
|
}
|