diff --git a/examples/variablerateshading/variablerateshading.cpp b/examples/variablerateshading/variablerateshading.cpp index f5c3592e..5a767db4 100644 --- a/examples/variablerateshading/variablerateshading.cpp +++ b/examples/variablerateshading/variablerateshading.cpp @@ -1,7 +1,7 @@ /* * 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) */ @@ -19,7 +19,8 @@ VulkanExample::VulkanExample() : VulkanExampleBase(ENABLE_VALIDATION) camera.setPerspective(60.0f, (float)width / (float)height, 0.1f, 256.0f); camera.setRotationSpeed(0.25f); 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() @@ -40,10 +41,11 @@ void VulkanExample::getEnabledFeatures() { enabledFeatures.samplerAnisotropy = deviceFeatures.samplerAnisotropy; // POI - enabledPhysicalDeviceShadingRateImageFeaturesNV = {}; - enabledPhysicalDeviceShadingRateImageFeaturesNV.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADING_RATE_IMAGE_FEATURES_NV; - enabledPhysicalDeviceShadingRateImageFeaturesNV.shadingRateImage = VK_TRUE; - deviceCreatepNextChain = &enabledPhysicalDeviceShadingRateImageFeaturesNV; + enabledPhysicalDeviceShadingRateImageFeaturesKHR.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_FEATURES_KHR; + enabledPhysicalDeviceShadingRateImageFeaturesKHR.attachmentFragmentShadingRate = VK_TRUE; + enabledPhysicalDeviceShadingRateImageFeaturesKHR.pipelineFragmentShadingRate = VK_FALSE; + enabledPhysicalDeviceShadingRateImageFeaturesKHR.primitiveFragmentShadingRate = VK_FALSE; + deviceCreatepNextChain = &enabledPhysicalDeviceShadingRateImageFeaturesKHR; } /* @@ -60,6 +62,146 @@ void VulkanExample::handleResize() 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(vkGetInstanceProcAddr(instance, "vkCreateRenderPass2KHR")); + } + + // Create an image with the shading rates to be used during rendering + prepareShadingRateImage(); + + std::array 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 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(attachments.size()); + renderPassCI.pAttachments = attachments.data(); + renderPassCI.subpassCount = 1; + renderPassCI.pSubpasses = &subpassDescription; + renderPassCI.dependencyCount = static_cast(dependencies.size()); + renderPassCI.pDependencies = dependencies.data(); + + VK_CHECK_RESULT(vkCreateRenderPass2KHR(device, &renderPassCI, nullptr, &renderPass)); +} + void VulkanExample::buildCommandBuffers() { if (resized) @@ -67,12 +209,17 @@ void VulkanExample::buildCommandBuffers() handleResize(); } + // As this is an extension, we need to manually load the extension pointers + if (!vkCmdSetFragmentShadingRateKHR) { + vkCmdSetFragmentShadingRateKHR = reinterpret_cast(vkGetDeviceProcAddr(device, "vkCmdSetFragmentShadingRateKHR")); + } + VkCommandBufferBeginInfo cmdBufInfo = vks::initializers::commandBufferBeginInfo(); - VkClearValue clearValues[2]; - clearValues[0].color = defaultClearColor; + VkClearValue clearValues[3]; clearValues[0].color = { { 0.25f, 0.25f, 0.25f, 1.0f } };; clearValues[1].depthStencil = { 1.0f, 0 }; + clearValues[2].color = { {0.0f, 0.0f, 0.0f, 0.0f} }; VkRenderPassBeginInfo renderPassBeginInfo = vks::initializers::renderPassBeginInfo(); renderPassBeginInfo.renderPass = renderPass; @@ -80,7 +227,7 @@ void VulkanExample::buildCommandBuffers() renderPassBeginInfo.renderArea.offset.y = 0; renderPassBeginInfo.renderArea.extent.width = width; renderPassBeginInfo.renderArea.extent.height = height; - renderPassBeginInfo.clearValueCount = 2; + renderPassBeginInfo.clearValueCount = 3; renderPassBeginInfo.pClearValues = clearValues; 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); vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSet, 0, nullptr); - // POI: Bind the image that contains the shading rate patterns - if (enableShadingRate) { - vkCmdBindShadingRateImageNV(drawCmdBuffers[i], shadingRateImage.view, VK_IMAGE_LAYOUT_SHADING_RATE_OPTIMAL_NV); - }; + // Set the fragment shading rate state for the current pipeline + VkExtent2D fragmentSize = { 1, 1 }; + 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 Pipelines& pipelines = enableShadingRate ? shadingRatePipelines : basePipelines; @@ -155,17 +317,38 @@ void VulkanExample::setupDescriptors() // [POI] void VulkanExample::prepareShadingRateImage() { + // As this is an extension, we need to manually load the extension pointers + if (!vkGetPhysicalDeviceFragmentShadingRatesKHR) { + vkGetPhysicalDeviceFragmentShadingRatesKHR = reinterpret_cast(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 // 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{}; - imageExtent.width = static_cast(ceil(width / (float)physicalDeviceShadingRateImagePropertiesNV.shadingRateTexelSize.width)); - imageExtent.height = static_cast(ceil(height / (float)physicalDeviceShadingRateImagePropertiesNV.shadingRateTexelSize.height)); + imageExtent.width = static_cast(ceil(width / (float)physicalDeviceShadingRateImageProperties.maxFragmentShadingRateAttachmentTexelSize.width)); + imageExtent.height = static_cast(ceil(height / (float)physicalDeviceShadingRateImageProperties.maxFragmentShadingRateAttachmentTexelSize.height)); imageExtent.depth = 1; VkImageCreateInfo imageCI{}; imageCI.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; imageCI.imageType = VK_IMAGE_TYPE_2D; - imageCI.format = VK_FORMAT_R8_UINT; + imageCI.format = imageFormat; imageCI.extent = imageExtent; imageCI.mipLevels = 1; imageCI.arrayLayers = 1; @@ -173,13 +356,11 @@ void VulkanExample::prepareShadingRateImage() imageCI.tiling = VK_IMAGE_TILING_OPTIMAL; imageCI.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; 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)); VkMemoryRequirements memReqs{}; vkGetImageMemoryRequirements(device, shadingRateImage.image, &memReqs); - VkDeviceSize bufferSize = imageExtent.width * imageExtent.height * sizeof(uint8_t); - VkMemoryAllocateInfo memAllloc{}; memAllloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; memAllloc.allocationSize = memReqs.size; @@ -199,26 +380,47 @@ void VulkanExample::prepareShadingRateImage() imageViewCI.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; VK_CHECK_RESULT(vkCreateImageView(device, &imageViewCI, nullptr, &shadingRateImage.view)); - // Populate with lowest possible shading rate pattern - uint8_t val = VK_SHADING_RATE_PALETTE_ENTRY_1_INVOCATION_PER_4X4_PIXELS_NV; + // The shading rates are stored in a buffer that'll be copied to the shading rate image + 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]; memset(shadingRatePatternData, val, bufferSize); - // Create a circular pattern with decreasing sampling rates outwards (max. range, pattern) - std::map patternLookup = { - { 8.0f, VK_SHADING_RATE_PALETTE_ENTRY_1_INVOCATION_PER_PIXEL_NV }, - { 12.0f, VK_SHADING_RATE_PALETTE_ENTRY_1_INVOCATION_PER_2X1_PIXELS_NV }, - { 16.0f, VK_SHADING_RATE_PALETTE_ENTRY_1_INVOCATION_PER_1X2_PIXELS_NV }, - { 18.0f, VK_SHADING_RATE_PALETTE_ENTRY_1_INVOCATION_PER_2X2_PIXELS_NV }, - { 20.0f, VK_SHADING_RATE_PALETTE_ENTRY_1_INVOCATION_PER_4X2_PIXELS_NV }, - { 24.0f, VK_SHADING_RATE_PALETTE_ENTRY_1_INVOCATION_PER_2X4_PIXELS_NV } - }; + // Get a list of available shading rate patterns + std::vector fragmentShadingRates{}; + uint32_t fragmentShadingRatesCount = 0; + vkGetPhysicalDeviceFragmentShadingRatesKHR(physicalDevice, &fragmentShadingRatesCount, nullptr); + if (fragmentShadingRatesCount > 0) { + fragmentShadingRates.resize(fragmentShadingRatesCount); + for (VkPhysicalDeviceFragmentShadingRateKHR& fragmentShadingRate : fragmentShadingRates) { + // 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 patternLookup{}; + float range = 25.0f / static_cast(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; for (uint32_t y = 0; y < imageExtent.height; y++) { for (uint32_t x = 0; x < imageExtent.width; x++) { - const float deltaX = (float)imageExtent.width / 2.0f - (float)x; - const float deltaY = ((float)imageExtent.height / 2.0f - (float)y) * ((float)width / (float)height); + const float deltaX = (static_cast(imageExtent.width) / 2.0f - static_cast(x)) / imageExtent.width * 100.0f; + const float deltaY = (static_cast(imageExtent.height) / 2.0f - static_cast(y)) / imageExtent.height * 100.0f; const float dist = std::sqrt(deltaX * deltaX + deltaY * deltaY); for (auto pattern : patternLookup) { if (dist < pattern.first) { @@ -230,6 +432,8 @@ void VulkanExample::prepareShadingRateImage() } } + // Copy the shading rate pattern to the shading rate image + VkBuffer stagingBuffer; VkDeviceMemory stagingMemory; @@ -283,7 +487,7 @@ void VulkanExample::prepareShadingRateImage() VkImageMemoryBarrier imageMemoryBarrier{}; imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; 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.dstAccessMask = 0; 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); VkPipelineViewportStateCreateInfo viewportStateCI = vks::initializers::pipelineViewportStateCreateInfo(1, 1, 0); VkPipelineMultisampleStateCreateInfo multisampleStateCI = vks::initializers::pipelineMultisampleStateCreateInfo(VK_SAMPLE_COUNT_1_BIT, 0); - const std::vector dynamicStateEnables = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR }; + const std::vector 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(dynamicStateEnables.size()), 0); std::array shaderStages; @@ -346,31 +550,6 @@ void VulkanExample::preparePipelines() rasterizationStateCI.cullMode = VK_CULL_MODE_BACK_BIT; specializationData.alphaMask = false; - // Create pipeline with shading rate enabled - // [POI] Possible per-Viewport shading rate palette entries - const std::vector 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(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)); specializationData.alphaMask = true; rasterizationStateCI.cullMode = VK_CULL_MODE_NONE; @@ -401,16 +580,6 @@ void VulkanExample::prepare() { VulkanExampleBase::prepare(); loadAssets(); - - // [POI] - vkCmdBindShadingRateImageNV = reinterpret_cast(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(); setupDescriptors(); preparePipelines(); diff --git a/examples/variablerateshading/variablerateshading.h b/examples/variablerateshading/variablerateshading.h index 8b7095f0..4d522218 100644 --- a/examples/variablerateshading/variablerateshading.h +++ b/examples/variablerateshading/variablerateshading.h @@ -1,7 +1,7 @@ /* * 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) */ @@ -49,9 +49,12 @@ public: VkDescriptorSet descriptorSet; VkDescriptorSetLayout descriptorSetLayout; - VkPhysicalDeviceShadingRateImagePropertiesNV physicalDeviceShadingRateImagePropertiesNV{}; - VkPhysicalDeviceShadingRateImageFeaturesNV enabledPhysicalDeviceShadingRateImageFeaturesNV{}; - PFN_vkCmdBindShadingRateImageNV vkCmdBindShadingRateImageNV; + VkPhysicalDeviceFragmentShadingRatePropertiesKHR physicalDeviceShadingRateImageProperties{}; + VkPhysicalDeviceFragmentShadingRateFeaturesKHR enabledPhysicalDeviceShadingRateImageFeaturesKHR{}; + + PFN_vkGetPhysicalDeviceFragmentShadingRatesKHR vkGetPhysicalDeviceFragmentShadingRatesKHR{ nullptr }; + PFN_vkCmdSetFragmentShadingRateKHR vkCmdSetFragmentShadingRateKHR{ nullptr }; + PFN_vkCreateRenderPass2KHR vkCreateRenderPass2KHR{ nullptr }; VulkanExample(); ~VulkanExample(); @@ -66,6 +69,8 @@ public: void prepareUniformBuffers(); void updateUniformBuffers(); void prepare(); + void setupFrameBuffer() override; + void setupRenderPass() override; virtual void render(); virtual void OnUpdateUIOverlay(vks::UIOverlay* overlay); }; \ No newline at end of file diff --git a/shaders/glsl/variablerateshading/scene.frag b/shaders/glsl/variablerateshading/scene.frag index 1236713a..b2b4a473 100644 --- a/shaders/glsl/variablerateshading/scene.frag +++ b/shaders/glsl/variablerateshading/scene.frag @@ -1,6 +1,6 @@ #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 = 1) uniform sampler2D samplerNormalMap; @@ -52,33 +52,34 @@ void main() outFragColor = vec4(diffuse * color.rgb + specular, color.a); if (uboScene.colorShadingRates == 1) { - if (gl_FragmentSizeNV.x == 1 && gl_FragmentSizeNV.y == 1) { - outFragColor.rgb *= vec3(0.0, 0.8, 0.4); - return; + int v = 1; + int h = 1; + + if ((gl_ShadingRateEXT & gl_ShadingRateFlag2VerticalPixelsEXT) == gl_ShadingRateFlag2VerticalPixelsEXT) { + v = 2; } - if (gl_FragmentSizeNV.x == 2 && gl_FragmentSizeNV.y == 1) { - outFragColor.rgb *= vec3(0.2, 0.6, 1.0); - return; + if ((gl_ShadingRateEXT & gl_ShadingRateFlag4VerticalPixelsEXT) == gl_ShadingRateFlag4VerticalPixelsEXT) { + v = 4; } - if (gl_FragmentSizeNV.x == 1 && gl_FragmentSizeNV.y == 2) { - outFragColor.rgb *= vec3(0.0, 0.4, 0.8); - return; + if ((gl_ShadingRateEXT & gl_ShadingRateFlag2HorizontalPixelsEXT) == gl_ShadingRateFlag2HorizontalPixelsEXT) { + h = 2; } - if (gl_FragmentSizeNV.x == 2 && gl_FragmentSizeNV.y == 2) { - outFragColor.rgb *= vec3(1.0, 1.0, 0.2); - 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 ((gl_ShadingRateEXT & gl_ShadingRateFlag4HorizontalPixelsEXT) == gl_ShadingRateFlag4HorizontalPixelsEXT) { + h = 4; } + + 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); } } \ No newline at end of file diff --git a/shaders/glsl/variablerateshading/scene.frag.spv b/shaders/glsl/variablerateshading/scene.frag.spv index bc40abee..7d356186 100644 Binary files a/shaders/glsl/variablerateshading/scene.frag.spv and b/shaders/glsl/variablerateshading/scene.frag.spv differ diff --git a/shaders/hlsl/variablerateshading/scene.frag.spv b/shaders/hlsl/variablerateshading/scene.frag.spv index 48d484d8..d06bb483 100644 Binary files a/shaders/hlsl/variablerateshading/scene.frag.spv and b/shaders/hlsl/variablerateshading/scene.frag.spv differ