Updated shading rate sample to KHR extension

This commit is contained in:
Sascha Willems 2023-10-13 18:53:51 +02:00
parent 66dce3c991
commit f27a032570
5 changed files with 273 additions and 98 deletions

View file

@ -1,7 +1,7 @@
/* /*
* Vulkan Example - Variable rate shading * Vulkan Example - Variable rate shading
* *
* Copyright (C) 2020-2022 by Sascha Willems - www.saschawillems.de * Copyright (C) 2020-2023 by Sascha Willems - www.saschawillems.de
* *
* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) * This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
*/ */
@ -19,7 +19,8 @@ VulkanExample::VulkanExample() : VulkanExampleBase(ENABLE_VALIDATION)
camera.setPerspective(60.0f, (float)width / (float)height, 0.1f, 256.0f); camera.setPerspective(60.0f, (float)width / (float)height, 0.1f, 256.0f);
camera.setRotationSpeed(0.25f); camera.setRotationSpeed(0.25f);
enabledInstanceExtensions.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); enabledInstanceExtensions.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
enabledDeviceExtensions.push_back(VK_NV_SHADING_RATE_IMAGE_EXTENSION_NAME); enabledDeviceExtensions.push_back(VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME);
enabledDeviceExtensions.push_back(VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME);
} }
VulkanExample::~VulkanExample() VulkanExample::~VulkanExample()
@ -40,10 +41,11 @@ void VulkanExample::getEnabledFeatures()
{ {
enabledFeatures.samplerAnisotropy = deviceFeatures.samplerAnisotropy; enabledFeatures.samplerAnisotropy = deviceFeatures.samplerAnisotropy;
// POI // POI
enabledPhysicalDeviceShadingRateImageFeaturesNV = {}; enabledPhysicalDeviceShadingRateImageFeaturesKHR.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_FEATURES_KHR;
enabledPhysicalDeviceShadingRateImageFeaturesNV.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADING_RATE_IMAGE_FEATURES_NV; enabledPhysicalDeviceShadingRateImageFeaturesKHR.attachmentFragmentShadingRate = VK_TRUE;
enabledPhysicalDeviceShadingRateImageFeaturesNV.shadingRateImage = VK_TRUE; enabledPhysicalDeviceShadingRateImageFeaturesKHR.pipelineFragmentShadingRate = VK_FALSE;
deviceCreatepNextChain = &enabledPhysicalDeviceShadingRateImageFeaturesNV; enabledPhysicalDeviceShadingRateImageFeaturesKHR.primitiveFragmentShadingRate = VK_FALSE;
deviceCreatepNextChain = &enabledPhysicalDeviceShadingRateImageFeaturesKHR;
} }
/* /*
@ -60,6 +62,146 @@ void VulkanExample::handleResize()
resized = false; resized = false;
} }
void VulkanExample::setupFrameBuffer()
{
VkImageView attachments[3];
// Depth/Stencil attachment is the same for all frame buffers
attachments[1] = depthStencil.view;
// Fragment shading rate attachment
attachments[2] = shadingRateImage.view;
VkFramebufferCreateInfo frameBufferCreateInfo{};
frameBufferCreateInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
frameBufferCreateInfo.renderPass = renderPass;
frameBufferCreateInfo.attachmentCount = 3;
frameBufferCreateInfo.pAttachments = attachments;
frameBufferCreateInfo.width = width;
frameBufferCreateInfo.height = height;
frameBufferCreateInfo.layers = 1;
// Create frame buffers for every swap chain image
frameBuffers.resize(swapChain.imageCount);
for (uint32_t i = 0; i < frameBuffers.size(); i++)
{
attachments[0] = swapChain.buffers[i].view;
VK_CHECK_RESULT(vkCreateFramebuffer(device, &frameBufferCreateInfo, nullptr, &frameBuffers[i]));
}
}
void VulkanExample::setupRenderPass()
{
// Note that we need to use ...2KHR types in here, as fragment shading rate requires additional properties and structs to be passed at renderpass creation
if (!vkCreateRenderPass2KHR) {
vkCreateRenderPass2KHR = reinterpret_cast<PFN_vkCreateRenderPass2KHR>(vkGetInstanceProcAddr(instance, "vkCreateRenderPass2KHR"));
}
// Create an image with the shading rates to be used during rendering
prepareShadingRateImage();
std::array<VkAttachmentDescription2KHR, 3> attachments = {};
// Color attachment
attachments[0].sType = VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2;
attachments[0].format = swapChain.colorFormat;
attachments[0].samples = VK_SAMPLE_COUNT_1_BIT;
attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
attachments[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
attachments[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
attachments[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
attachments[0].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
attachments[0].finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
// Depth attachment
attachments[1].sType = VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2;
attachments[1].format = depthFormat;
attachments[1].samples = VK_SAMPLE_COUNT_1_BIT;
attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
attachments[1].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
attachments[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
attachments[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
attachments[1].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
attachments[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
// Fragment shading rate attachment
attachments[2].sType = VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2;
attachments[2].format = VK_FORMAT_R8_UINT;
attachments[2].samples = VK_SAMPLE_COUNT_1_BIT;
attachments[2].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
attachments[2].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
attachments[2].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
attachments[2].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
attachments[2].initialLayout = VK_IMAGE_LAYOUT_FRAGMENT_SHADING_RATE_ATTACHMENT_OPTIMAL_KHR;
attachments[2].finalLayout = VK_IMAGE_LAYOUT_FRAGMENT_SHADING_RATE_ATTACHMENT_OPTIMAL_KHR;
VkAttachmentReference2KHR colorReference = {};
colorReference.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2;
colorReference.attachment = 0;
colorReference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
colorReference.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
VkAttachmentReference2KHR depthReference = {};
depthReference.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2;
depthReference.attachment = 1;
depthReference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
depthReference.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
// Setup the attachment reference for the shading rate image attachment in slot 2
VkAttachmentReference2 fragmentShadingRateReference{};
fragmentShadingRateReference.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2;
fragmentShadingRateReference.attachment = 2;
fragmentShadingRateReference.layout = VK_IMAGE_LAYOUT_FRAGMENT_SHADING_RATE_ATTACHMENT_OPTIMAL_KHR;
// Setup the attachment info for the shading rate image, which will be added to the sub pass via structure chaining (in pNext)
VkFragmentShadingRateAttachmentInfoKHR fragmentShadingRateAttachmentInfo{};
fragmentShadingRateAttachmentInfo.sType = VK_STRUCTURE_TYPE_FRAGMENT_SHADING_RATE_ATTACHMENT_INFO_KHR;
fragmentShadingRateAttachmentInfo.pFragmentShadingRateAttachment = &fragmentShadingRateReference;
fragmentShadingRateAttachmentInfo.shadingRateAttachmentTexelSize.width = physicalDeviceShadingRateImageProperties.maxFragmentShadingRateAttachmentTexelSize.width;
fragmentShadingRateAttachmentInfo.shadingRateAttachmentTexelSize.height = physicalDeviceShadingRateImageProperties.maxFragmentShadingRateAttachmentTexelSize.height;
VkSubpassDescription2KHR subpassDescription = {};
subpassDescription.sType = VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_2;
subpassDescription.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpassDescription.colorAttachmentCount = 1;
subpassDescription.pColorAttachments = &colorReference;
subpassDescription.pDepthStencilAttachment = &depthReference;
subpassDescription.inputAttachmentCount = 0;
subpassDescription.pInputAttachments = nullptr;
subpassDescription.preserveAttachmentCount = 0;
subpassDescription.pPreserveAttachments = nullptr;
subpassDescription.pResolveAttachments = nullptr;
subpassDescription.pNext = &fragmentShadingRateAttachmentInfo;
// Subpass dependencies for layout transitions
std::array<VkSubpassDependency2KHR, 2> dependencies = {};
dependencies[0].sType = VK_STRUCTURE_TYPE_SUBPASS_DEPENDENCY_2;
dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL;
dependencies[0].dstSubpass = 0;
dependencies[0].srcStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
dependencies[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
dependencies[0].srcAccessMask = VK_ACCESS_MEMORY_READ_BIT;
dependencies[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
dependencies[0].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;
dependencies[1].sType = VK_STRUCTURE_TYPE_SUBPASS_DEPENDENCY_2;
dependencies[1].srcSubpass = 0;
dependencies[1].dstSubpass = VK_SUBPASS_EXTERNAL;
dependencies[1].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
dependencies[1].dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
dependencies[1].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
dependencies[1].dstAccessMask = VK_ACCESS_MEMORY_READ_BIT;
dependencies[1].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;
VkRenderPassCreateInfo2KHR renderPassCI = {};
renderPassCI.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO_2;
renderPassCI.attachmentCount = static_cast<uint32_t>(attachments.size());
renderPassCI.pAttachments = attachments.data();
renderPassCI.subpassCount = 1;
renderPassCI.pSubpasses = &subpassDescription;
renderPassCI.dependencyCount = static_cast<uint32_t>(dependencies.size());
renderPassCI.pDependencies = dependencies.data();
VK_CHECK_RESULT(vkCreateRenderPass2KHR(device, &renderPassCI, nullptr, &renderPass));
}
void VulkanExample::buildCommandBuffers() void VulkanExample::buildCommandBuffers()
{ {
if (resized) if (resized)
@ -67,12 +209,17 @@ void VulkanExample::buildCommandBuffers()
handleResize(); handleResize();
} }
// As this is an extension, we need to manually load the extension pointers
if (!vkCmdSetFragmentShadingRateKHR) {
vkCmdSetFragmentShadingRateKHR = reinterpret_cast<PFN_vkCmdSetFragmentShadingRateKHR>(vkGetDeviceProcAddr(device, "vkCmdSetFragmentShadingRateKHR"));
}
VkCommandBufferBeginInfo cmdBufInfo = vks::initializers::commandBufferBeginInfo(); VkCommandBufferBeginInfo cmdBufInfo = vks::initializers::commandBufferBeginInfo();
VkClearValue clearValues[2]; VkClearValue clearValues[3];
clearValues[0].color = defaultClearColor;
clearValues[0].color = { { 0.25f, 0.25f, 0.25f, 1.0f } };; clearValues[0].color = { { 0.25f, 0.25f, 0.25f, 1.0f } };;
clearValues[1].depthStencil = { 1.0f, 0 }; clearValues[1].depthStencil = { 1.0f, 0 };
clearValues[2].color = { {0.0f, 0.0f, 0.0f, 0.0f} };
VkRenderPassBeginInfo renderPassBeginInfo = vks::initializers::renderPassBeginInfo(); VkRenderPassBeginInfo renderPassBeginInfo = vks::initializers::renderPassBeginInfo();
renderPassBeginInfo.renderPass = renderPass; renderPassBeginInfo.renderPass = renderPass;
@ -80,7 +227,7 @@ void VulkanExample::buildCommandBuffers()
renderPassBeginInfo.renderArea.offset.y = 0; renderPassBeginInfo.renderArea.offset.y = 0;
renderPassBeginInfo.renderArea.extent.width = width; renderPassBeginInfo.renderArea.extent.width = width;
renderPassBeginInfo.renderArea.extent.height = height; renderPassBeginInfo.renderArea.extent.height = height;
renderPassBeginInfo.clearValueCount = 2; renderPassBeginInfo.clearValueCount = 3;
renderPassBeginInfo.pClearValues = clearValues; renderPassBeginInfo.pClearValues = clearValues;
const VkViewport viewport = vks::initializers::viewport((float)width, (float)height, 0.0f, 1.0f); const VkViewport viewport = vks::initializers::viewport((float)width, (float)height, 0.0f, 1.0f);
@ -95,10 +242,25 @@ void VulkanExample::buildCommandBuffers()
vkCmdSetScissor(drawCmdBuffers[i], 0, 1, &scissor); vkCmdSetScissor(drawCmdBuffers[i], 0, 1, &scissor);
vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSet, 0, nullptr); vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSet, 0, nullptr);
// POI: Bind the image that contains the shading rate patterns // Set the fragment shading rate state for the current pipeline
if (enableShadingRate) { VkExtent2D fragmentSize = { 1, 1 };
vkCmdBindShadingRateImageNV(drawCmdBuffers[i], shadingRateImage.view, VK_IMAGE_LAYOUT_SHADING_RATE_OPTIMAL_NV); VkFragmentShadingRateCombinerOpKHR combinerOps[2];
}; // The combiners determine how the different shading rate values for the pipeline, primitives and attachment are combined
if (enableShadingRate)
{
// If shading rate from attachment is enabled, we set the combiner, so that the values from the attachment are used
// Combiner for pipeline (A) and primitive (B) - Not used in this sample
combinerOps[0] = VK_FRAGMENT_SHADING_RATE_COMBINER_OP_KEEP_KHR;
// Combiner for pipeline (A) and attachment (B), replace the pipeline default value (fragment_size) with the fragment sizes stored in the attachment
combinerOps[1] = VK_FRAGMENT_SHADING_RATE_COMBINER_OP_REPLACE_KHR;
}
else
{
// If shading rate from attachment is disabled, we keep the value set via the dynamic state
combinerOps[0] = VK_FRAGMENT_SHADING_RATE_COMBINER_OP_KEEP_KHR;
combinerOps[1] = VK_FRAGMENT_SHADING_RATE_COMBINER_OP_KEEP_KHR;
}
vkCmdSetFragmentShadingRateKHR(drawCmdBuffers[i], &fragmentSize, combinerOps);
// Render the scene // Render the scene
Pipelines& pipelines = enableShadingRate ? shadingRatePipelines : basePipelines; Pipelines& pipelines = enableShadingRate ? shadingRatePipelines : basePipelines;
@ -155,17 +317,38 @@ void VulkanExample::setupDescriptors()
// [POI] // [POI]
void VulkanExample::prepareShadingRateImage() void VulkanExample::prepareShadingRateImage()
{ {
// As this is an extension, we need to manually load the extension pointers
if (!vkGetPhysicalDeviceFragmentShadingRatesKHR) {
vkGetPhysicalDeviceFragmentShadingRatesKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceFragmentShadingRatesKHR>(vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceFragmentShadingRatesKHR"));
}
// Get properties of this extensions, which also contains texel sizes required to setup the image
physicalDeviceShadingRateImageProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_PROPERTIES_KHR;
VkPhysicalDeviceProperties2 deviceProperties2{};
deviceProperties2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
deviceProperties2.pNext = &physicalDeviceShadingRateImageProperties;
vkGetPhysicalDeviceProperties2(physicalDevice, &deviceProperties2);
// We need to check if the requested format for the shading rate attachment supports the required flag
const VkFormat imageFormat = VK_FORMAT_R8_UINT;
VkFormatProperties formatProperties;
vkGetPhysicalDeviceFormatProperties(physicalDevice, imageFormat, &formatProperties);
if (!(formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR))
{
throw std::runtime_error("Selected shading rate attachment image format does not fragment shading rate");
}
// Shading rate image size depends on shading rate texel size // Shading rate image size depends on shading rate texel size
// For each texel in the target image, there is a corresponding shading texel size width x height block in the shading rate image // For each texel in the target image, there is a corresponding shading texel size width x height block in the shading rate image
VkExtent3D imageExtent{}; VkExtent3D imageExtent{};
imageExtent.width = static_cast<uint32_t>(ceil(width / (float)physicalDeviceShadingRateImagePropertiesNV.shadingRateTexelSize.width)); imageExtent.width = static_cast<uint32_t>(ceil(width / (float)physicalDeviceShadingRateImageProperties.maxFragmentShadingRateAttachmentTexelSize.width));
imageExtent.height = static_cast<uint32_t>(ceil(height / (float)physicalDeviceShadingRateImagePropertiesNV.shadingRateTexelSize.height)); imageExtent.height = static_cast<uint32_t>(ceil(height / (float)physicalDeviceShadingRateImageProperties.maxFragmentShadingRateAttachmentTexelSize.height));
imageExtent.depth = 1; imageExtent.depth = 1;
VkImageCreateInfo imageCI{}; VkImageCreateInfo imageCI{};
imageCI.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; imageCI.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
imageCI.imageType = VK_IMAGE_TYPE_2D; imageCI.imageType = VK_IMAGE_TYPE_2D;
imageCI.format = VK_FORMAT_R8_UINT; imageCI.format = imageFormat;
imageCI.extent = imageExtent; imageCI.extent = imageExtent;
imageCI.mipLevels = 1; imageCI.mipLevels = 1;
imageCI.arrayLayers = 1; imageCI.arrayLayers = 1;
@ -173,13 +356,11 @@ void VulkanExample::prepareShadingRateImage()
imageCI.tiling = VK_IMAGE_TILING_OPTIMAL; imageCI.tiling = VK_IMAGE_TILING_OPTIMAL;
imageCI.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; imageCI.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
imageCI.sharingMode = VK_SHARING_MODE_EXCLUSIVE; imageCI.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
imageCI.usage = VK_IMAGE_USAGE_SHADING_RATE_IMAGE_BIT_NV | VK_IMAGE_USAGE_TRANSFER_DST_BIT; imageCI.usage = VK_IMAGE_USAGE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
VK_CHECK_RESULT(vkCreateImage(device, &imageCI, nullptr, &shadingRateImage.image)); VK_CHECK_RESULT(vkCreateImage(device, &imageCI, nullptr, &shadingRateImage.image));
VkMemoryRequirements memReqs{}; VkMemoryRequirements memReqs{};
vkGetImageMemoryRequirements(device, shadingRateImage.image, &memReqs); vkGetImageMemoryRequirements(device, shadingRateImage.image, &memReqs);
VkDeviceSize bufferSize = imageExtent.width * imageExtent.height * sizeof(uint8_t);
VkMemoryAllocateInfo memAllloc{}; VkMemoryAllocateInfo memAllloc{};
memAllloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; memAllloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
memAllloc.allocationSize = memReqs.size; memAllloc.allocationSize = memReqs.size;
@ -199,26 +380,47 @@ void VulkanExample::prepareShadingRateImage()
imageViewCI.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; imageViewCI.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
VK_CHECK_RESULT(vkCreateImageView(device, &imageViewCI, nullptr, &shadingRateImage.view)); VK_CHECK_RESULT(vkCreateImageView(device, &imageViewCI, nullptr, &shadingRateImage.view));
// Populate with lowest possible shading rate pattern // The shading rates are stored in a buffer that'll be copied to the shading rate image
uint8_t val = VK_SHADING_RATE_PALETTE_ENTRY_1_INVOCATION_PER_4X4_PIXELS_NV; VkDeviceSize bufferSize = imageExtent.width * imageExtent.height * sizeof(uint8_t);
// Fragment sizes are encoded in a single texel as follows:
// size(w) = 2^((texel/4) & 3)
// size(h)h = 2^(texel & 3)
// Populate it with the lowest possible shading rate
uint8_t val = (4 >> 1) | (4 << 1);
uint8_t* shadingRatePatternData = new uint8_t[bufferSize]; uint8_t* shadingRatePatternData = new uint8_t[bufferSize];
memset(shadingRatePatternData, val, bufferSize); memset(shadingRatePatternData, val, bufferSize);
// Create a circular pattern with decreasing sampling rates outwards (max. range, pattern) // Get a list of available shading rate patterns
std::map<float, VkShadingRatePaletteEntryNV> patternLookup = { std::vector<VkPhysicalDeviceFragmentShadingRateKHR> fragmentShadingRates{};
{ 8.0f, VK_SHADING_RATE_PALETTE_ENTRY_1_INVOCATION_PER_PIXEL_NV }, uint32_t fragmentShadingRatesCount = 0;
{ 12.0f, VK_SHADING_RATE_PALETTE_ENTRY_1_INVOCATION_PER_2X1_PIXELS_NV }, vkGetPhysicalDeviceFragmentShadingRatesKHR(physicalDevice, &fragmentShadingRatesCount, nullptr);
{ 16.0f, VK_SHADING_RATE_PALETTE_ENTRY_1_INVOCATION_PER_1X2_PIXELS_NV }, if (fragmentShadingRatesCount > 0) {
{ 18.0f, VK_SHADING_RATE_PALETTE_ENTRY_1_INVOCATION_PER_2X2_PIXELS_NV }, fragmentShadingRates.resize(fragmentShadingRatesCount);
{ 20.0f, VK_SHADING_RATE_PALETTE_ENTRY_1_INVOCATION_PER_4X2_PIXELS_NV }, for (VkPhysicalDeviceFragmentShadingRateKHR& fragmentShadingRate : fragmentShadingRates) {
{ 24.0f, VK_SHADING_RATE_PALETTE_ENTRY_1_INVOCATION_PER_2X4_PIXELS_NV } // In addition to the value, we also need to set the sType for each rate to comply with the spec or else the call to vkGetPhysicalDeviceFragmentShadingRatesKHR will result in undefined behaviour
}; fragmentShadingRate.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_KHR;
}
vkGetPhysicalDeviceFragmentShadingRatesKHR(physicalDevice, &fragmentShadingRatesCount, fragmentShadingRates.data());
}
// Create a circular pattern from the available list of fragment shading rates with decreasing sampling rates outwards (max. range, pattern)
// Shading rates returned by vkGetPhysicalDeviceFragmentShadingRatesKHR are ordered from largest to smallest
std::map<float, uint8_t> patternLookup{};
float range = 25.0f / static_cast<uint32_t>(fragmentShadingRates.size());
float currentRange = 8.0f;
for (size_t i = fragmentShadingRates.size() - 1; i > 0; i--) {
uint32_t rate_v = fragmentShadingRates[i].fragmentSize.width == 1 ? 0 : (fragmentShadingRates[i].fragmentSize.width >> 1);
uint32_t rate_h = fragmentShadingRates[i].fragmentSize.height == 1 ? 0 : (fragmentShadingRates[i].fragmentSize.height << 1);
patternLookup[currentRange] = rate_v | rate_h;
currentRange += range;
}
uint8_t* ptrData = shadingRatePatternData; uint8_t* ptrData = shadingRatePatternData;
for (uint32_t y = 0; y < imageExtent.height; y++) { for (uint32_t y = 0; y < imageExtent.height; y++) {
for (uint32_t x = 0; x < imageExtent.width; x++) { for (uint32_t x = 0; x < imageExtent.width; x++) {
const float deltaX = (float)imageExtent.width / 2.0f - (float)x; const float deltaX = (static_cast<float>(imageExtent.width) / 2.0f - static_cast<float>(x)) / imageExtent.width * 100.0f;
const float deltaY = ((float)imageExtent.height / 2.0f - (float)y) * ((float)width / (float)height); const float deltaY = (static_cast<float>(imageExtent.height) / 2.0f - static_cast<float>(y)) / imageExtent.height * 100.0f;
const float dist = std::sqrt(deltaX * deltaX + deltaY * deltaY); const float dist = std::sqrt(deltaX * deltaX + deltaY * deltaY);
for (auto pattern : patternLookup) { for (auto pattern : patternLookup) {
if (dist < pattern.first) { if (dist < pattern.first) {
@ -230,6 +432,8 @@ void VulkanExample::prepareShadingRateImage()
} }
} }
// Copy the shading rate pattern to the shading rate image
VkBuffer stagingBuffer; VkBuffer stagingBuffer;
VkDeviceMemory stagingMemory; VkDeviceMemory stagingMemory;
@ -283,7 +487,7 @@ void VulkanExample::prepareShadingRateImage()
VkImageMemoryBarrier imageMemoryBarrier{}; VkImageMemoryBarrier imageMemoryBarrier{};
imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_SHADING_RATE_OPTIMAL_NV; imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_FRAGMENT_SHADING_RATE_ATTACHMENT_OPTIMAL_KHR;
imageMemoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; imageMemoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
imageMemoryBarrier.dstAccessMask = 0; imageMemoryBarrier.dstAccessMask = 0;
imageMemoryBarrier.image = shadingRateImage.image; imageMemoryBarrier.image = shadingRateImage.image;
@ -305,7 +509,7 @@ void VulkanExample::preparePipelines()
VkPipelineDepthStencilStateCreateInfo depthStencilStateCI = vks::initializers::pipelineDepthStencilStateCreateInfo(VK_TRUE, VK_TRUE, VK_COMPARE_OP_LESS_OR_EQUAL); VkPipelineDepthStencilStateCreateInfo depthStencilStateCI = vks::initializers::pipelineDepthStencilStateCreateInfo(VK_TRUE, VK_TRUE, VK_COMPARE_OP_LESS_OR_EQUAL);
VkPipelineViewportStateCreateInfo viewportStateCI = vks::initializers::pipelineViewportStateCreateInfo(1, 1, 0); VkPipelineViewportStateCreateInfo viewportStateCI = vks::initializers::pipelineViewportStateCreateInfo(1, 1, 0);
VkPipelineMultisampleStateCreateInfo multisampleStateCI = vks::initializers::pipelineMultisampleStateCreateInfo(VK_SAMPLE_COUNT_1_BIT, 0); VkPipelineMultisampleStateCreateInfo multisampleStateCI = vks::initializers::pipelineMultisampleStateCreateInfo(VK_SAMPLE_COUNT_1_BIT, 0);
const std::vector<VkDynamicState> dynamicStateEnables = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR }; const std::vector<VkDynamicState> dynamicStateEnables = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR, VK_DYNAMIC_STATE_FRAGMENT_SHADING_RATE_KHR };
VkPipelineDynamicStateCreateInfo dynamicStateCI = vks::initializers::pipelineDynamicStateCreateInfo(dynamicStateEnables.data(), static_cast<uint32_t>(dynamicStateEnables.size()), 0); VkPipelineDynamicStateCreateInfo dynamicStateCI = vks::initializers::pipelineDynamicStateCreateInfo(dynamicStateEnables.data(), static_cast<uint32_t>(dynamicStateEnables.size()), 0);
std::array<VkPipelineShaderStageCreateInfo, 2> shaderStages; std::array<VkPipelineShaderStageCreateInfo, 2> shaderStages;
@ -346,31 +550,6 @@ void VulkanExample::preparePipelines()
rasterizationStateCI.cullMode = VK_CULL_MODE_BACK_BIT; rasterizationStateCI.cullMode = VK_CULL_MODE_BACK_BIT;
specializationData.alphaMask = false; specializationData.alphaMask = false;
// Create pipeline with shading rate enabled
// [POI] Possible per-Viewport shading rate palette entries
const std::vector<VkShadingRatePaletteEntryNV> shadingRatePaletteEntries = {
VK_SHADING_RATE_PALETTE_ENTRY_NO_INVOCATIONS_NV,
VK_SHADING_RATE_PALETTE_ENTRY_16_INVOCATIONS_PER_PIXEL_NV,
VK_SHADING_RATE_PALETTE_ENTRY_8_INVOCATIONS_PER_PIXEL_NV,
VK_SHADING_RATE_PALETTE_ENTRY_4_INVOCATIONS_PER_PIXEL_NV,
VK_SHADING_RATE_PALETTE_ENTRY_2_INVOCATIONS_PER_PIXEL_NV,
VK_SHADING_RATE_PALETTE_ENTRY_1_INVOCATION_PER_PIXEL_NV,
VK_SHADING_RATE_PALETTE_ENTRY_1_INVOCATION_PER_2X1_PIXELS_NV,
VK_SHADING_RATE_PALETTE_ENTRY_1_INVOCATION_PER_1X2_PIXELS_NV,
VK_SHADING_RATE_PALETTE_ENTRY_1_INVOCATION_PER_2X2_PIXELS_NV,
VK_SHADING_RATE_PALETTE_ENTRY_1_INVOCATION_PER_4X2_PIXELS_NV,
VK_SHADING_RATE_PALETTE_ENTRY_1_INVOCATION_PER_2X4_PIXELS_NV,
VK_SHADING_RATE_PALETTE_ENTRY_1_INVOCATION_PER_4X4_PIXELS_NV,
};
VkShadingRatePaletteNV shadingRatePalette{};
shadingRatePalette.shadingRatePaletteEntryCount = static_cast<uint32_t>(shadingRatePaletteEntries.size());
shadingRatePalette.pShadingRatePaletteEntries = shadingRatePaletteEntries.data();
VkPipelineViewportShadingRateImageStateCreateInfoNV pipelineViewportShadingRateImageStateCI{};
pipelineViewportShadingRateImageStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_SHADING_RATE_IMAGE_STATE_CREATE_INFO_NV;
pipelineViewportShadingRateImageStateCI.shadingRateImageEnable = VK_TRUE;
pipelineViewportShadingRateImageStateCI.viewportCount = 1;
pipelineViewportShadingRateImageStateCI.pShadingRatePalettes = &shadingRatePalette;
viewportStateCI.pNext = &pipelineViewportShadingRateImageStateCI;
VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &shadingRatePipelines.opaque)); VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &shadingRatePipelines.opaque));
specializationData.alphaMask = true; specializationData.alphaMask = true;
rasterizationStateCI.cullMode = VK_CULL_MODE_NONE; rasterizationStateCI.cullMode = VK_CULL_MODE_NONE;
@ -401,16 +580,6 @@ void VulkanExample::prepare()
{ {
VulkanExampleBase::prepare(); VulkanExampleBase::prepare();
loadAssets(); loadAssets();
// [POI]
vkCmdBindShadingRateImageNV = reinterpret_cast<PFN_vkCmdBindShadingRateImageNV>(vkGetDeviceProcAddr(device, "vkCmdBindShadingRateImageNV"));
physicalDeviceShadingRateImagePropertiesNV.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADING_RATE_IMAGE_PROPERTIES_NV;
VkPhysicalDeviceProperties2 deviceProperties2{};
deviceProperties2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
deviceProperties2.pNext = &physicalDeviceShadingRateImagePropertiesNV;
vkGetPhysicalDeviceProperties2(physicalDevice, &deviceProperties2);
prepareShadingRateImage();
prepareUniformBuffers(); prepareUniformBuffers();
setupDescriptors(); setupDescriptors();
preparePipelines(); preparePipelines();

View file

@ -1,7 +1,7 @@
/* /*
* Vulkan Example - Variable rate shading * Vulkan Example - Variable rate shading
* *
* Copyright (C) 2020 by Sascha Willems - www.saschawillems.de * Copyright (C) 2020-2023 by Sascha Willems - www.saschawillems.de
* *
* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) * This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
*/ */
@ -49,9 +49,12 @@ public:
VkDescriptorSet descriptorSet; VkDescriptorSet descriptorSet;
VkDescriptorSetLayout descriptorSetLayout; VkDescriptorSetLayout descriptorSetLayout;
VkPhysicalDeviceShadingRateImagePropertiesNV physicalDeviceShadingRateImagePropertiesNV{}; VkPhysicalDeviceFragmentShadingRatePropertiesKHR physicalDeviceShadingRateImageProperties{};
VkPhysicalDeviceShadingRateImageFeaturesNV enabledPhysicalDeviceShadingRateImageFeaturesNV{}; VkPhysicalDeviceFragmentShadingRateFeaturesKHR enabledPhysicalDeviceShadingRateImageFeaturesKHR{};
PFN_vkCmdBindShadingRateImageNV vkCmdBindShadingRateImageNV;
PFN_vkGetPhysicalDeviceFragmentShadingRatesKHR vkGetPhysicalDeviceFragmentShadingRatesKHR{ nullptr };
PFN_vkCmdSetFragmentShadingRateKHR vkCmdSetFragmentShadingRateKHR{ nullptr };
PFN_vkCreateRenderPass2KHR vkCreateRenderPass2KHR{ nullptr };
VulkanExample(); VulkanExample();
~VulkanExample(); ~VulkanExample();
@ -66,6 +69,8 @@ public:
void prepareUniformBuffers(); void prepareUniformBuffers();
void updateUniformBuffers(); void updateUniformBuffers();
void prepare(); void prepare();
void setupFrameBuffer() override;
void setupRenderPass() override;
virtual void render(); virtual void render();
virtual void OnUpdateUIOverlay(vks::UIOverlay* overlay); virtual void OnUpdateUIOverlay(vks::UIOverlay* overlay);
}; };

View file

@ -1,6 +1,6 @@
#version 450 #version 450
#extension GL_NV_shading_rate_image : require #extension GL_EXT_fragment_shading_rate : require
layout (set = 1, binding = 0) uniform sampler2D samplerColorMap; layout (set = 1, binding = 0) uniform sampler2D samplerColorMap;
layout (set = 1, binding = 1) uniform sampler2D samplerNormalMap; layout (set = 1, binding = 1) uniform sampler2D samplerNormalMap;
@ -52,33 +52,34 @@ void main()
outFragColor = vec4(diffuse * color.rgb + specular, color.a); outFragColor = vec4(diffuse * color.rgb + specular, color.a);
if (uboScene.colorShadingRates == 1) { if (uboScene.colorShadingRates == 1) {
if (gl_FragmentSizeNV.x == 1 && gl_FragmentSizeNV.y == 1) { int v = 1;
outFragColor.rgb *= vec3(0.0, 0.8, 0.4); int h = 1;
return;
if ((gl_ShadingRateEXT & gl_ShadingRateFlag2VerticalPixelsEXT) == gl_ShadingRateFlag2VerticalPixelsEXT) {
v = 2;
} }
if (gl_FragmentSizeNV.x == 2 && gl_FragmentSizeNV.y == 1) { if ((gl_ShadingRateEXT & gl_ShadingRateFlag4VerticalPixelsEXT) == gl_ShadingRateFlag4VerticalPixelsEXT) {
outFragColor.rgb *= vec3(0.2, 0.6, 1.0); v = 4;
return;
} }
if (gl_FragmentSizeNV.x == 1 && gl_FragmentSizeNV.y == 2) { if ((gl_ShadingRateEXT & gl_ShadingRateFlag2HorizontalPixelsEXT) == gl_ShadingRateFlag2HorizontalPixelsEXT) {
outFragColor.rgb *= vec3(0.0, 0.4, 0.8); h = 2;
return;
} }
if (gl_FragmentSizeNV.x == 2 && gl_FragmentSizeNV.y == 2) { if ((gl_ShadingRateEXT & gl_ShadingRateFlag4HorizontalPixelsEXT) == gl_ShadingRateFlag4HorizontalPixelsEXT) {
outFragColor.rgb *= vec3(1.0, 1.0, 0.2); h = 4;
return;
}
if (gl_FragmentSizeNV.x == 4 && gl_FragmentSizeNV.y == 2) {
outFragColor.rgb *= vec3(0.8, 0.8, 0.0);
return;
}
if (gl_FragmentSizeNV.x == 2 && gl_FragmentSizeNV.y == 4) {
outFragColor.rgb *= vec3(1.0, 0.4, 0.2);
return;
}
if (gl_FragmentSizeNV.x == 4 && gl_FragmentSizeNV.y == 4) {
outFragColor.rgb *= vec3(0.8, 0.0, 0.0);
return;
} }
if (v == 1 && h == 1) { outFragColor *= vec4(0.0, 0.8, 0.4, 1.0); return; }
if (v == 2 && h == 1) { outFragColor *= vec4(0.2, 0.6, 1.0, 1.0); return; }
if (v == 1 && h == 2) { outFragColor *= vec4(0.0, 0.4, 0.8, 1.0); return; }
if (v == 2 && h == 2) { outFragColor *= vec4(1.0, 1.0, 0.2, 1.0); return; }
if (v == 4 && h == 2) { outFragColor *= vec4(0.8, 0.8, 0.0, 1.0); return; }
if (v == 2 && h == 4) { outFragColor *= vec4(1.0, 0.4, 0.2, 1.0); return; }
outFragColor *= vec4(0.0, 0.8, 0.4, 1.0);
} }
} }