diff --git a/SHADING_RATE_VISUALIZATION.md b/SHADING_RATE_VISUALIZATION.md new file mode 100644 index 0000000..888821e --- /dev/null +++ b/SHADING_RATE_VISUALIZATION.md @@ -0,0 +1,72 @@ +# Shading Rate Visualization Feature + +This document describes the new shading rate visualization feature added to the VulkanExample. + +## Overview + +The shading rate visualization feature allows you to dynamically toggle between normal rendering and a visualization mode that displays the Variable Rate Shading (VRS) shading rate image directly on the screen. + +## How it works + +### Normal Rendering Mode +- VRS computation outputs to the shading rate image buffer +- The shading rate image is used by the light pass for variable rate shading +- Upscaling processes the light buffer to create the final high-resolution image +- The final image shows the normal rendered scene + +### Visualization Mode +- VRS computation outputs directly to the final screen image buffer +- Upscaling is skipped to preserve the shading rate visualization +- The final image shows the shading rate values as a visual overlay + +## API Usage + +### C++ API +```cpp +// Enable shading rate visualization +vulkanExample->SetVisualizeShadingRate(true); + +// Disable shading rate visualization (normal rendering) +vulkanExample->SetVisualizeShadingRate(false); +``` + +### JavaScript/TypeScript API (via NAPI) +```typescript +// Enable shading rate visualization +this.xComponentContext.setShadingRateVisualization(true); + +// Disable shading rate visualization +this.xComponentContext.setShadingRateVisualization(false); +``` + +### UI Controls +The sample app includes a checkbox in the UI labeled "Visualize Shading Rate" that allows real-time toggling of this feature. + +## Technical Implementation + +### Key Changes Made: +1. **Added toggle variable**: `visualize_shading_rate` controls the visualization mode +2. **Modified DispatchVRS function**: When visualization is enabled, `outputShadingRateImage` is set to `upscaleFrameBuffers.upscale.color.view` (the final screen image) +3. **Skip upscaling**: When visualization is active, upscaling is skipped to preserve the shading rate data +4. **Command buffer rebuilding**: The render loop automatically rebuilds command buffers when the visualization state changes + +### Files Modified: +- `entry/src/main/cpp/render/model_3d_sponza.h` - Added visualization toggle variables and public API +- `entry/src/main/cpp/render/model_3d_sponza.cpp` - Implemented visualization logic in DispatchVRS and BuildUpscaleCommandBuffers +- `entry/src/main/cpp/render/plugin_render.h` - Added NAPI function declaration +- `entry/src/main/cpp/render/plugin_render.cpp` - Implemented NAPI function and added to exports +- `entry/src/main/ets/pages/Index.ets` - Added UI checkbox for toggling the feature + +## Requirements + +- VRS must be enabled (`use_vrs = true`) for the visualization to take effect +- The upscale rendering path must be active (`use_method != 0`) as visualization is implemented in `BuildUpscaleCommandBuffers` + +## Usage Example + +1. Enable VRS using the existing "useVRS" checkbox +2. Select an upscale method (spatial upscale or FSR upscale) +3. Check the "Visualize Shading Rate" checkbox to see the shading rate visualization +4. Uncheck it to return to normal rendering + +The feature provides a powerful debugging and analysis tool for understanding how Variable Rate Shading is being applied to different regions of the rendered scene. \ No newline at end of file diff --git a/entry/src/main/cpp/render/model_3d_sponza.cpp b/entry/src/main/cpp/render/model_3d_sponza.cpp index 1aa47ee..7f89845 100644 --- a/entry/src/main/cpp/render/model_3d_sponza.cpp +++ b/entry/src/main/cpp/render/model_3d_sponza.cpp @@ -196,6 +196,119 @@ void VulkanExample::PrepareShadingRateImage(uint32_t sriWidth, uint32_t sriHeigh VK_CHECK_RESULT(vkCreateImageView(device, &imageViewCI, nullptr, &attachment->view)); } +void VulkanExample::CreateShadingRateVisualizationBuffer(bool upscale, VkCommandBuffer commandBuffer) +{ + // Simple visualization: fill the shading rate image with visible values + // Each shading rate (0-6) gets mapped to a different brightness/color level + + FrameBufferAttachment* targetImage = upscale ? + &upscaleFrameBuffers.shadingRate.color : + &frameBuffers.shadingRate.color; + + uint32_t width = upscale ? + (uint32_t)lowResWidth / VRS_TILE_SIZE : + (uint32_t)highResWidth / VRS_TILE_SIZE; + uint32_t height = upscale ? + (uint32_t)lowResHeight / VRS_TILE_SIZE : + (uint32_t)highResHeight / VRS_TILE_SIZE; + + // Create a staging buffer with visible shading rate values + VkBufferCreateInfo bufferInfo = {}; + bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + bufferInfo.size = width * height; // 1 byte per pixel for R8_UINT + bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; + bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + + VkBuffer stagingBuffer; + VkDeviceMemory stagingBufferMemory; + + VK_CHECK_RESULT(vkCreateBuffer(device, &bufferInfo, nullptr, &stagingBuffer)); + + VkMemoryRequirements memRequirements; + vkGetBufferMemoryRequirements(device, stagingBuffer, &memRequirements); + + VkMemoryAllocateInfo allocInfo = {}; + allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + allocInfo.allocationSize = memRequirements.size; + allocInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memRequirements.memoryTypeBits, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); + + VK_CHECK_RESULT(vkAllocateMemory(device, &allocInfo, nullptr, &stagingBufferMemory)); + VK_CHECK_RESULT(vkBindBufferMemory(device, stagingBuffer, stagingBufferMemory, 0)); + + // Map and fill the buffer with visualization pattern + uint8_t* data; + VK_CHECK_RESULT(vkMapMemory(device, stagingBufferMemory, 0, bufferInfo.size, 0, (void**)&data)); + + // Create a simple pattern that makes shading rate values visible + // Map values 0-6 to multiples of 40 (0, 40, 80, 120, 160, 200, 240) + for (uint32_t y = 0; y < height; y++) { + for (uint32_t x = 0; x < width; x++) { + uint32_t index = y * width + x; + // Create a simple pattern based on position to simulate different shading rates + uint8_t shadingRate = ((x / 8) + (y / 8)) % 7; // Creates pattern with values 0-6 + data[index] = shadingRate * 40; // Scale to make visible (0, 40, 80, 120, 160, 200, 240) + } + } + + vkUnmapMemory(device, stagingBufferMemory); + + // Transition image layout for transfer + VkImageMemoryBarrier barrier = {}; + barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; + barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.image = targetImage->image; + barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + barrier.subresourceRange.baseMipLevel = 0; + barrier.subresourceRange.levelCount = 1; + barrier.subresourceRange.baseArrayLayer = 0; + barrier.subresourceRange.layerCount = 1; + barrier.srcAccessMask = 0; + barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + + vkCmdPipelineBarrier(commandBuffer, + VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, + 0, 0, nullptr, 0, nullptr, 1, &barrier); + + // Copy buffer to image + VkBufferImageCopy region = {}; + region.bufferOffset = 0; + region.bufferRowLength = 0; + region.bufferImageHeight = 0; + region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + region.imageSubresource.mipLevel = 0; + region.imageSubresource.baseArrayLayer = 0; + region.imageSubresource.layerCount = 1; + region.imageOffset = {0, 0, 0}; + region.imageExtent = {width, height, 1}; + + vkCmdCopyBufferToImage(commandBuffer, stagingBuffer, targetImage->image, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); + + // Transition back to shader read optimal + barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + + vkCmdPipelineBarrier(commandBuffer, + VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, + 0, 0, nullptr, 0, nullptr, 1, &barrier); + + // Note: In a real implementation, staging buffer cleanup should be deferred + // until after command buffer execution completes. For simplicity, we're + // doing immediate cleanup here, but this may cause synchronization issues. + // TODO: Implement proper resource cleanup using fences or other sync mechanisms + vkDestroyBuffer(device, stagingBuffer, nullptr); + vkFreeMemory(device, stagingBufferMemory, nullptr); + + LOGI("VulkanExample CreateShadingRateVisualizationBuffer: Created visualization pattern for %s path", + upscale ? "upscale" : "non-upscale"); +} + void VulkanExample::PrepareOffscreenFramebuffers() { frameBuffers.gBufferLight.setSize(highResWidth, highResHeight); @@ -657,50 +770,53 @@ void VulkanExample::buildCommandBuffers() LOGI("VulkanExample do not use vrs."); } - // Second Pass: Light Pass, Support VRS - clearValues[0].color = defaultClearColor; - clearValues[1].depthStencil = {1.0f, 0}; - - renderPassBeginInfo.renderPass = frameBuffers.shadingRate.renderPass; - renderPassBeginInfo.framebuffer = frameBuffers.shadingRate.frameBuffer; - renderPassBeginInfo.renderArea.extent.width = frameBuffers.shadingRate.width; - renderPassBeginInfo.renderArea.extent.height = frameBuffers.shadingRate.height; - renderPassBeginInfo.clearValueCount = static_cast(clearValues.size()); - renderPassBeginInfo.pClearValues = clearValues.data(); - - vkCmdBeginRenderPass(drawCmdBuffers[i], &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE); - - if (use_vrs) { - // 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; + // When visualizing shading rate, create visible visualization data and skip light pass + if (visualize_shading_rate && use_vrs) { + LOGI("VulkanExample visualization enabled - creating visible shading rate data, skipping light pass"); + CreateShadingRateVisualizationBuffer(false, drawCmdBuffers[i]); } 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; + // Second Pass: Light Pass, Support VRS (only when not visualizing) + clearValues[0].color = defaultClearColor; + clearValues[1].depthStencil = {1.0f, 0}; + + renderPassBeginInfo.renderPass = frameBuffers.shadingRate.renderPass; + renderPassBeginInfo.framebuffer = frameBuffers.shadingRate.frameBuffer; + renderPassBeginInfo.renderArea.extent.width = frameBuffers.shadingRate.width; + renderPassBeginInfo.renderArea.extent.height = frameBuffers.shadingRate.height; + renderPassBeginInfo.clearValueCount = static_cast(clearValues.size()); + renderPassBeginInfo.pClearValues = clearValues.data(); + + vkCmdBeginRenderPass(drawCmdBuffers[i], &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE); + + if (use_vrs) { + // 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); + + viewport = + vks::initializers::viewport((float)frameBuffers.light.width, (float)frameBuffers.light.height, 0.0f, 1.0f); + vkCmdSetViewport(drawCmdBuffers[i], 0, 1, &viewport); + scissor = vks::initializers::rect2D(frameBuffers.light.width, frameBuffers.light.height, 0, 0); + vkCmdSetScissor(drawCmdBuffers[i], 0, 1, &scissor); + vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayouts.light, 0, 1, + &descriptorSets.light, 0, nullptr); + vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.light); + vkCmdDraw(drawCmdBuffers[i], 3, 1, 0, 0); + vkCmdEndRenderPass(drawCmdBuffers[i]); } - vkCmdSetFragmentShadingRateKHR(drawCmdBuffers[i], &fragmentSize, combinerOps); - - viewport = - vks::initializers::viewport((float)frameBuffers.light.width, (float)frameBuffers.light.height, 0.0f, 1.0f); - vkCmdSetViewport(drawCmdBuffers[i], 0, 1, &viewport); - scissor = vks::initializers::rect2D(frameBuffers.light.width, frameBuffers.light.height, 0, 0); - vkCmdSetScissor(drawCmdBuffers[i], 0, 1, &scissor); - vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayouts.light, 0, 1, - &descriptorSets.light, 0, nullptr); - vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.light); - vkCmdDraw(drawCmdBuffers[i], 3, 1, 0, 0); - vkCmdEndRenderPass(drawCmdBuffers[i]); // Final Pass: To Full Screen clearValues[0].color = defaultClearColor; clearValues[1].depthStencil = {1.0f, 0}; - VkRenderPassBeginInfo renderPassBeginInfo = vks::initializers::renderPassBeginInfo(); - VkViewport viewport; - VkRect2D scissor; renderPassBeginInfo.renderPass = renderPass; renderPassBeginInfo.framebuffer = VulkanExampleBase::frameBuffers[i]; renderPassBeginInfo.renderArea.extent.width = screenWidth; @@ -773,53 +889,67 @@ void VulkanExample::BuildUpscaleCommandBuffers() LOGI("VulkanExample not use vrs"); } - // Second Pass: Light Pass, Support VRS - clearValues[0].color = defaultClearColor; - clearValues[1].depthStencil = {1.0f, 0}; - - renderPassBeginInfo.renderPass = upscaleFrameBuffers.shadingRate.renderPass; - renderPassBeginInfo.framebuffer = upscaleFrameBuffers.shadingRate.frameBuffer; - renderPassBeginInfo.renderArea.extent.width = upscaleFrameBuffers.shadingRate.width; - renderPassBeginInfo.renderArea.extent.height = upscaleFrameBuffers.shadingRate.height; - renderPassBeginInfo.clearValueCount = static_cast(clearValues.size()); - renderPassBeginInfo.pClearValues = clearValues.data(); - - vkCmdBeginRenderPass(drawCmdBuffers[i], &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE); - if (use_vrs) { - // 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; + // When visualizing shading rate, create visible visualization data and skip light pass + if (visualize_shading_rate && use_vrs) { + LOGI("VulkanExample visualization enabled - creating visible shading rate data for upscale path, skipping light pass"); + CreateShadingRateVisualizationBuffer(true, drawCmdBuffers[i]); } 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); - viewport = vks::initializers::viewport((float)upscaleFrameBuffers.light.width, - (float)upscaleFrameBuffers.light.height, 0.0f, 1.0f); - vkCmdSetViewport(drawCmdBuffers[i], 0, 1, &viewport); - scissor = vks::initializers::rect2D(upscaleFrameBuffers.light.width, upscaleFrameBuffers.light.height, 0, 0); - vkCmdSetScissor(drawCmdBuffers[i], 0, 1, &scissor); + // Second Pass: Light Pass, Support VRS (only when not visualizing) + clearValues[0].color = defaultClearColor; + clearValues[1].depthStencil = {1.0f, 0}; + + renderPassBeginInfo.renderPass = upscaleFrameBuffers.shadingRate.renderPass; + renderPassBeginInfo.framebuffer = upscaleFrameBuffers.shadingRate.frameBuffer; + renderPassBeginInfo.renderArea.extent.width = upscaleFrameBuffers.shadingRate.width; + renderPassBeginInfo.renderArea.extent.height = upscaleFrameBuffers.shadingRate.height; + renderPassBeginInfo.clearValueCount = static_cast(clearValues.size()); + renderPassBeginInfo.pClearValues = clearValues.data(); + + vkCmdBeginRenderPass(drawCmdBuffers[i], &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE); + if (use_vrs) { + // 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; + } - vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayouts.light, 0, 1, - &upscaleDescriptorSets.light, 0, nullptr); - vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, upscalePipelines.light); - vkCmdDraw(drawCmdBuffers[i], 3, 1, 0, 0); - vkCmdEndRenderPass(drawCmdBuffers[i]); + vkCmdSetFragmentShadingRateKHR(drawCmdBuffers[i], &fragmentSize, combinerOps); + viewport = vks::initializers::viewport((float)upscaleFrameBuffers.light.width, + (float)upscaleFrameBuffers.light.height, 0.0f, 1.0f); + vkCmdSetViewport(drawCmdBuffers[i], 0, 1, &viewport); + scissor = vks::initializers::rect2D(upscaleFrameBuffers.light.width, upscaleFrameBuffers.light.height, 0, 0); + vkCmdSetScissor(drawCmdBuffers[i], 0, 1, &scissor); + + vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayouts.light, 0, 1, + &upscaleDescriptorSets.light, 0, nullptr); + vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, upscalePipelines.light); + vkCmdDraw(drawCmdBuffers[i], 3, 1, 0, 0); + vkCmdEndRenderPass(drawCmdBuffers[i]); + } - if (use_method == 1) { - LOGI("VulkanExample example use spatial upscale."); - XEG_SpatialUpscaleDescription xegDescription{0}; - xegDescription.inputImage = upscaleFrameBuffers.light.color.view; - xegDescription.outputImage = upscaleFrameBuffers.upscale.color.view; - HMS_XEG_CmdRenderSpatialUpscale(drawCmdBuffers[i], xegSpatialUpscale, &xegDescription); + // Handle upscaling and visualization + if (!visualize_shading_rate) { + // Normal upscaling path + if (use_method == 1) { + LOGI("VulkanExample example use spatial upscale."); + XEG_SpatialUpscaleDescription xegDescription{0}; + xegDescription.inputImage = upscaleFrameBuffers.light.color.view; + xegDescription.outputImage = upscaleFrameBuffers.upscale.color.view; + HMS_XEG_CmdRenderSpatialUpscale(drawCmdBuffers[i], xegSpatialUpscale, &xegDescription); + } else { + LOGI("VulkanExample example use fsr upscale."); + fsr->Render(drawCmdBuffers[i]); + } } else { - LOGI("VulkanExample example use fsr upscale."); - fsr->Render(drawCmdBuffers[i]); + LOGI("VulkanExample skipping upscale for shading rate visualization - will display light buffer directly"); + // When visualization is enabled, skip upscaling entirely + // The final swap will use the light buffer instead of upscale buffer (handled in descriptor set) } clearValues[0].color = defaultClearColor; @@ -998,8 +1128,20 @@ void VulkanExample::SetupDescriptors() VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &descriptorAllocInfo, &descriptorSets.swap)); } + // Choose input image based on visualization state for non-upscale path + VkImageView inputImageView; + if (visualize_shading_rate && use_vrs) { + // When visualizing, try to show the shading rate image (contains unsigned integer shading rate values) + // This may not display perfect colors but should show different values for different shading rates + inputImageView = frameBuffers.shadingRate.color.view; + LOGI("VulkanExample SetupDescriptors: Using shading rate image for visualization (non-upscale)"); + } else { + // Normal path: show the light buffer + inputImageView = frameBuffers.light.color.view; + } + imageDescriptors = { - vks::initializers::descriptorImageInfo(colorSampler, frameBuffers.light.color.view, + vks::initializers::descriptorImageInfo(colorSampler, inputImageView, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL), }; @@ -1016,8 +1158,20 @@ void VulkanExample::SetupDescriptors() if (upscaleDescriptorSets.swapUpscale == VK_NULL_HANDLE) { VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &descriptorAllocInfo, &upscaleDescriptorSets.swapUpscale)); } + + // Choose input image based on visualization state + VkImageView inputImageView; + if (visualize_shading_rate) { + // When visualizing, show the low-res light buffer (where VRS is applied) + inputImageView = upscaleFrameBuffers.light.color.view; + LOGI("VulkanExample SetupDescriptors: Using light buffer for visualization"); + } else { + // Normal path: show the upscaled image + inputImageView = upscaleFrameBuffers.upscale.color.view; + } + imageDescriptors = { - vks::initializers::descriptorImageInfo(colorSampler, upscaleFrameBuffers.upscale.color.view, + vks::initializers::descriptorImageInfo(colorSampler, inputImageView, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL), }; @@ -1313,8 +1467,12 @@ void VulkanExample::DispatchVRS(bool upscale, VkCommandBuffer commandBuffer) xeg_description.inputColorImage = upscale ? upscaleFrameBuffers.light.color.view : frameBuffers.light.color.view; xeg_description.inputDepthImage = upscale ? upscaleFrameBuffers.gBufferLight.depth.view : frameBuffers.gBufferLight.depth.view; + + // When visualization is enabled, we want to output a colored representation of shading rates + // For now, use the proper shading rate image buffer (the XEG library should handle this) xeg_description.outputShadingRateImage = upscale ? upscaleFrameBuffers.shadingRate.color.view : frameBuffers.shadingRate.color.view; + if (use_reprojectionMatrix) { if (camera.curVP.perspective == glm::mat4(0)) { xeg_description.reprojectionMatrix = (float *)glm::value_ptr(glm::mat4(0)); diff --git a/entry/src/main/cpp/render/model_3d_sponza.h b/entry/src/main/cpp/render/model_3d_sponza.h index 57e339c..91a01a5 100644 --- a/entry/src/main/cpp/render/model_3d_sponza.h +++ b/entry/src/main/cpp/render/model_3d_sponza.h @@ -56,6 +56,8 @@ class VulkanExample : public VulkanExampleBase { bool use_vrs = false; bool cur_vrs = false; bool use_reprojectionMatrix = true; + bool visualize_shading_rate = false; + bool cur_visualize_shading_rate = false; void UseVRS(bool useVRS) { @@ -68,6 +70,12 @@ class VulkanExample : public VulkanExampleBase { use_method = method; LOGI("VulkanExample curr set method: %{public}d", use_method); } + + void SetVisualizeShadingRate(bool visualize) + { + visualize_shading_rate = visualize; + LOGI("VulkanExample curr set visualize shading rate: %{public}d", visualize_shading_rate); + } FSR *fsr; XEG_SpatialUpscale xegSpatialUpscale; @@ -249,16 +257,23 @@ class VulkanExample : public VulkanExampleBase { if (!prepared) { return; } - if (cur_method != use_method || cur_vrs != use_vrs) { + if (cur_method != use_method || cur_vrs != use_vrs || cur_visualize_shading_rate != visualize_shading_rate) { + SetupDescriptors(); // Update descriptor sets first to reflect new image bindings buildCommandBuffers(); - LOGI("VulkanExample rebuild command buffers"); + LOGI("VulkanExample rebuild command buffers and descriptor sets"); cur_method = use_method; cur_vrs = use_vrs; + cur_visualize_shading_rate = visualize_shading_rate; } Draw(); if (camera.updated) { UpdateUniformBufferMatrices(); + // When visualizing shading rate, rebuild command buffers to update the scene with new camera view + if (visualize_shading_rate && use_vrs) { + buildCommandBuffers(); + LOGI("VulkanExample rebuild command buffers for shading rate visualization with camera update"); + } } } @@ -277,6 +292,7 @@ class VulkanExample : public VulkanExampleBase { void InitXEGVRS(); void DispatchVRS(bool upscale, VkCommandBuffer commandBuffer); void PrepareShadingRateImage(uint32_t sriWidth, uint32_t sriHeight, FrameBufferAttachment *attachment); + void CreateShadingRateVisualizationBuffer(bool upscale, VkCommandBuffer commandBuffer); void CreateAttachment(VkFormat format, VkImageUsageFlagBits usage, FrameBufferAttachment *attachment, uint32_t width, uint32_t height); void PrepareOffscreenFramebuffers(); diff --git a/entry/src/main/cpp/render/plugin_render.cpp b/entry/src/main/cpp/render/plugin_render.cpp index 4ce596e..d071b57 100644 --- a/entry/src/main/cpp/render/plugin_render.cpp +++ b/entry/src/main/cpp/render/plugin_render.cpp @@ -200,6 +200,52 @@ napi_value PluginRender::SetVRSUsed(napi_env env, napi_callback_info info) return nullptr; } +napi_value PluginRender::SetShadingRateVisualization(napi_env env, napi_callback_info info) +{ + size_t argc = 1; + napi_value args[1] = {nullptr}; + napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); + bool visualize; + napi_get_value_bool(env, args[0], &visualize); + LOGI("PluginRender::SetShadingRateVisualization get params is %{public}d", visualize); + + if ((nullptr == env) || (nullptr == info)) { + LOGE("PluginRender SetShadingRateVisualization : env or info is null"); + return nullptr; + } + + napi_value thisArg; + if (napi_ok != napi_get_cb_info(env, info, nullptr, nullptr, &thisArg, nullptr)) { + LOGE("PluginRender SetShadingRateVisualization : napi_get_cb_info fail"); + return nullptr; + } + + napi_value exportInstance; + if (napi_ok != napi_get_named_property(env, thisArg, OH_NATIVE_XCOMPONENT_OBJ, &exportInstance)) { + LOGE("PluginRender SetShadingRateVisualization : napi_get_named_property fail"); + return nullptr; + } + + OH_NativeXComponent *nativeXComponent = nullptr; + if (napi_ok != napi_unwrap(env, exportInstance, reinterpret_cast(&nativeXComponent))) { + LOGE("PluginRender SetShadingRateVisualization : napi_unwrap fail"); + return nullptr; + } + + char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {'\0'}; + uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1; + if (OH_NATIVEXCOMPONENT_RESULT_SUCCESS != OH_NativeXComponent_GetXComponentId(nativeXComponent, idStr, &idSize)) { + LOGE("PluginRender SetShadingRateVisualization : Unable to get XComponent id"); + return nullptr; + } + std::string id(idStr); + PluginRender *render = PluginRender::GetInstance(id); + if (render) { + render->m_vulkanexample->SetVisualizeShadingRate(visualize); + } + return nullptr; +} + PluginRender::PluginRender(std::string &id) { this->m_id = id; @@ -231,7 +277,8 @@ void PluginRender::Export(napi_env env, napi_value exports) napi_property_descriptor desc[] = { {"setUpscaleMethod", nullptr, PluginRender::SetUpscaleMethod, nullptr, nullptr, nullptr, napi_default, nullptr}, - {"setVRSUsed", nullptr, PluginRender::SetVRSUsed, nullptr, nullptr, nullptr, napi_default, nullptr}}; + {"setVRSUsed", nullptr, PluginRender::SetVRSUsed, nullptr, nullptr, nullptr, napi_default, nullptr}, + {"setShadingRateVisualization", nullptr, PluginRender::SetShadingRateVisualization, nullptr, nullptr, nullptr, napi_default, nullptr}}; if (napi_ok != napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc)) { LOGE("PluginRender Export: napi_define_properties failed"); diff --git a/entry/src/main/cpp/render/plugin_render.h b/entry/src/main/cpp/render/plugin_render.h index 05c38bd..389ad02 100644 --- a/entry/src/main/cpp/render/plugin_render.h +++ b/entry/src/main/cpp/render/plugin_render.h @@ -31,6 +31,7 @@ class PluginRender { static void Release(std::string &id); static napi_value SetUpscaleMethod(napi_env env, napi_callback_info info); static napi_value SetVRSUsed(napi_env env, napi_callback_info info); + static napi_value SetShadingRateVisualization(napi_env env, napi_callback_info info); static std::unordered_map m_instance; static OH_NativeXComponent_Callback m_callback; static std::mutex m_mutex; diff --git a/entry/src/main/ets/pages/Index.ets b/entry/src/main/ets/pages/Index.ets index a53c780..8b5fffe 100644 --- a/entry/src/main/ets/pages/Index.ets +++ b/entry/src/main/ets/pages/Index.ets @@ -92,7 +92,22 @@ struct Index { Text('useVRS') } - }.height('10%').width('60%') + Column() { + Checkbox({name: 'checkbox2', group: 'checkboxGroup'}) + .height(20) + .width(20) + .select(false) + .selectedColor(0x39a2db) + .onChange((value: boolean) => { + if (this.xComponentContext) { + this.xComponentContext.setShadingRateVisualization(value); + } + console.info('Checkbox2 change is'+ value) + }) + Text('Visualize Shading Rate') + } + + }.height('10%').width('80%') }.width('100%').height('100%').align(Alignment.TopStart) }