diff --git a/antora/modules/ROOT/nav.adoc b/antora/modules/ROOT/nav.adoc index 74164bdcdb..ede8873141 100644 --- a/antora/modules/ROOT/nav.adoc +++ b/antora/modules/ROOT/nav.adoc @@ -89,6 +89,7 @@ ** xref:samples/extensions/ray_tracing_reflection/README.adoc[Ray tracing reflection] ** xref:samples/extensions/ray_tracing_position_fetch/README.adoc[Ray tracing position fetch] ** xref:samples/extensions/shader_object/README.adoc[Shader Object] +** xref:samples/extensions/shader_quad_control/README.adoc[Shader quad control] ** xref:samples/extensions/shader_debugprintf/README.adoc[Shader Debug Printf] ** xref:samples/extensions/sparse_image/README.adoc[Sparse Image] ** xref:samples/extensions/synchronization_2/README.adoc[Synchronization 2] diff --git a/framework/vulkan_type_mapping.h b/framework/vulkan_type_mapping.h index b86b37a834..06baedbd41 100644 --- a/framework/vulkan_type_mapping.h +++ b/framework/vulkan_type_mapping.h @@ -175,6 +175,12 @@ struct HPPType using Type = vk::PhysicalDeviceFragmentShaderBarycentricFeaturesKHR; }; +template <> +struct HPPType +{ + using Type = vk::PhysicalDeviceShaderQuadControlFeaturesKHR; +}; + template <> struct HPPType { diff --git a/samples/extensions/README.adoc b/samples/extensions/README.adoc index a81c60ef33..c9120ecb30 100644 --- a/samples/extensions/README.adoc +++ b/samples/extensions/README.adoc @@ -313,8 +313,13 @@ Demonstrate how to build data graph pipelines and execute neural networks: * xref:./{extension_samplespath}tensor_and_data_graph/simple_tensor_and_data_graph/README.adoc[simple_tensor_and_data_graph] - Explains how to set up and execute a simple neural network using a data graph pipeline. +=== xref:./{extension_samplespath}shader_quad_control/README.adoc[Shader quad control] + +*Extension*: https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_KHR_shader_quad_control.html[`VK_KHR_shader_quad_control`] + +Demonstrates quad‑scope operations in fragment shaders, such as broadcasting values within a 2x2 quad, and explains the `layout(full_quads)` and `layout(quad_derivatives)` execution modes. === xref:./{extension_samplespath}ray_tracing_invocation_reorder/README.adoc[Ray Tracing Invocation Reorder] *Extensions:* https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_EXT_ray_tracing_invocation_reorder.html[`VK_EXT_ray_tracing_invocation_reorder`] -Demonstrate how to optimize ray tracing pipelines by reordering the invocation order. \ No newline at end of file +Demonstrate how to optimize ray tracing pipelines by reordering the invocation order. diff --git a/samples/extensions/shader_quad_control/CMakeLists.txt b/samples/extensions/shader_quad_control/CMakeLists.txt new file mode 100644 index 0000000000..25a988298d --- /dev/null +++ b/samples/extensions/shader_quad_control/CMakeLists.txt @@ -0,0 +1,29 @@ +# Copyright (c) 2025, Holochip Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 the "License"; +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +get_filename_component(FOLDER_NAME ${CMAKE_CURRENT_LIST_DIR} NAME) +get_filename_component(PARENT_DIR ${CMAKE_CURRENT_LIST_DIR} PATH) +get_filename_component(CATEGORY_NAME ${PARENT_DIR} NAME) + +add_sample( + ID ${FOLDER_NAME} + CATEGORY ${CATEGORY_NAME} + AUTHOR "Holochip" + NAME "Shader quad control" + DESCRIPTION "Demonstrates VK_KHR_shader_quad_control with a simple fullscreen draw using quad vote ops." + SHADER_FILES_GLSL + "shader_quad_control/glsl/quad_control.vert" + "shader_quad_control/glsl/quad_control.frag") diff --git a/samples/extensions/shader_quad_control/README.adoc b/samples/extensions/shader_quad_control/README.adoc new file mode 100644 index 0000000000..760406fcc6 --- /dev/null +++ b/samples/extensions/shader_quad_control/README.adoc @@ -0,0 +1,84 @@ +//// +- Copyright (c) 2025, Holochip Inc. +- +- SPDX-License-Identifier: Apache-2.0 +- +- Licensed under the Apache License, Version 2.0 the "License"; +- you may not use this file except in compliance with the License. +- You may obtain a copy of the License at +- +- http://www.apache.org/licenses/LICENSE-2.0 +- +- Unless required by applicable law or agreed to in writing, software +- distributed under the License is distributed on an "AS IS" BASIS, +- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +- See the License for the specific language governing permissions and +- limitations under the License. +- +//// + +ifdef::site-gen-antora[] +TIP: The source for this sample can be found in the https://github.com/KhronosGroup/Vulkan-Samples/tree/main/samples/extensions/shader_quad_control[Khronos Vulkan samples github repository]. +endif::[] + += Shader quad control (VK_KHR_shader_quad_control) + +This sample demonstrates VK_KHR_shader_quad_control in a minimal graphics pipeline. +It renders a full‑screen triangle and uses quad‑scope operations in the fragment shader to +broadcast values within a 2x2 pixel quad. This produces a characteristic “blocky” 2x2 pattern, +clearly showing how quad operations affect shading. + +== What is shader quad control? +VK_KHR_shader_quad_control exposes SPIR‑V and shading language capabilities that operate at the +scope of a fragment “quad” – the 2x2 group of fragment shader invocations that participate in +coherent derivative calculation. The extension adds: + +- A shader execution mode to control quad formation: `layout(full_quads)` or `layout(quad_derivatives)`. +- Built‑ins and functions to communicate within a quad, like `subgroupQuadBroadcast(...)`, and vote + operations `subgroupQuadAll(...)` / `subgroupQuadAny(...)`. + +Typical uses include: + +- Stabilizing derivatives, LOD selection, and gradient‑sensitive operations across the 2x2 quad. +- Sharing values between lanes in a quad without using shared memory. + +== What this sample does +- Creates a simple graphics pipeline (full‑screen triangle, no descriptors). +- The fragment shader enables `GL_EXT_shader_quad_control` and declares `layout(full_quads) in;`. +- It calls `subgroupQuadBroadcast(vUV, 0)` to broadcast the top‑left lane’s interpolant to the + whole quad, so each 2x2 block shows one color based on the leader lane. + +This minimal approach keeps focus on the quad control feature rather than complex rendering. + +== Required extensions and features +To run this sample, the device must support `VK_KHR_shader_quad_control`. + +No descriptors or additional states are required for this minimal demo. The sample uses the +framework’s default render pass and a basic pipeline configuration. + +== Fragment shader +The fragment shader uses quad control to broadcast a varying from the quad leader: + +[source,glsl] +---- +#version 450 +#extension GL_EXT_shader_quad_control : require + +layout(full_quads) in; // Control quad scope +layout(location = 0) in vec2 vUV; // Interpolated UV from VS +layout(location = 0) out vec4 outColor; + +void main() +{ + vec2 uv_leader = subgroupQuadBroadcast(vUV, 0); // From top-left of the 2x2 quad + outColor = vec4(uv_leader, 0.5, 1.0); +} +---- + +Switching to `layout(quad_derivatives) in;` would instead ensure that implicit derivatives are +computed in a quad‑coherent way (useful if you perform gradient operations like texture LODs). +This sample focuses on the broadcasting operation for clarity. + +== Specification +- Vulkan: https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_KHR_shader_quad_control.html[VK_KHR_shader_quad_control] +- GLSL: https://github.com/KhronosGroup/GLSL/blob/main/extensions/ext/GLSL_EXT_shader_quad_control.txt[GLSL_EXT_shader_quad_control] diff --git a/samples/extensions/shader_quad_control/shader_quad_control.cpp b/samples/extensions/shader_quad_control/shader_quad_control.cpp new file mode 100644 index 0000000000..513306d3d6 --- /dev/null +++ b/samples/extensions/shader_quad_control/shader_quad_control.cpp @@ -0,0 +1,188 @@ +/* Copyright (c) 2025, Holochip Inc. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 the "License"; + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "shader_quad_control.h" + +ShaderQuadControl::ShaderQuadControl() +{ + title = "Shader quad control"; + set_api_version(VK_API_VERSION_1_2); + + add_instance_extension(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); + // VK_KHR_shader_quad_control requires VK_KHR_shader_maximal_reconvergence per spec + add_device_extension(VK_KHR_SHADER_MAXIMAL_RECONVERGENCE_EXTENSION_NAME); + add_device_extension(VK_KHR_SHADER_QUAD_CONTROL_EXTENSION_NAME); +} + +ShaderQuadControl::~ShaderQuadControl() +{ + if (has_device()) + { + if (pipeline != VK_NULL_HANDLE) + { + vkDestroyPipeline(get_device().get_handle(), pipeline, nullptr); + } + if (pipeline_layout != VK_NULL_HANDLE) + { + vkDestroyPipelineLayout(get_device().get_handle(), pipeline_layout, nullptr); + } + } +} + +bool ShaderQuadControl::prepare(const vkb::ApplicationOptions &options) +{ + if (!ApiVulkanSample::prepare(options)) + { + return false; + } + + create_pipeline_layout(); + create_pipeline(); + build_command_buffers(); + + prepared = true; + return true; +} + +void ShaderQuadControl::request_gpu_features(vkb::core::PhysicalDeviceC &gpu) +{ + REQUEST_REQUIRED_FEATURE(gpu, VkPhysicalDeviceShaderQuadControlFeaturesKHR, shaderQuadControl); +} + +void ShaderQuadControl::create_pipeline_layout() +{ + VkPipelineLayoutCreateInfo pipeline_layout_ci{VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO}; + VK_CHECK(vkCreatePipelineLayout(get_device().get_handle(), &pipeline_layout_ci, nullptr, &pipeline_layout)); +} + +void ShaderQuadControl::create_pipeline() +{ + std::array stages{}; + stages[0] = load_shader("shader_quad_control/glsl/quad_control.vert.spv", VK_SHADER_STAGE_VERTEX_BIT); + stages[1] = load_shader("shader_quad_control/glsl/quad_control.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT); + + VkPipelineVertexInputStateCreateInfo vertex_input{VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO}; + + VkPipelineInputAssemblyStateCreateInfo input_assembly{VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO}; + input_assembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + + VkPipelineViewportStateCreateInfo viewport_state{VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO}; + viewport_state.viewportCount = 1; + viewport_state.scissorCount = 1; + + VkPipelineRasterizationStateCreateInfo raster{VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO}; + raster.polygonMode = VK_POLYGON_MODE_FILL; + raster.cullMode = VK_CULL_MODE_NONE; + raster.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; + raster.lineWidth = 1.0f; + + VkPipelineMultisampleStateCreateInfo msaa{VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO}; + msaa.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + + VkPipelineDepthStencilStateCreateInfo depth_stencil{VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO}; + + VkPipelineColorBlendAttachmentState color_blend_attachment{}; + color_blend_attachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; + + VkPipelineColorBlendStateCreateInfo color_blend{VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO}; + color_blend.attachmentCount = 1; + color_blend.pAttachments = &color_blend_attachment; + + std::array dynamic_states{VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR}; + VkPipelineDynamicStateCreateInfo dynamic_state{VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO}; + dynamic_state.dynamicStateCount = static_cast(dynamic_states.size()); + dynamic_state.pDynamicStates = dynamic_states.data(); + + VkGraphicsPipelineCreateInfo pipeline_ci{VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO}; + pipeline_ci.stageCount = static_cast(stages.size()); + pipeline_ci.pStages = stages.data(); + pipeline_ci.pVertexInputState = &vertex_input; + pipeline_ci.pInputAssemblyState = &input_assembly; + pipeline_ci.pViewportState = &viewport_state; + pipeline_ci.pRasterizationState = &raster; + pipeline_ci.pMultisampleState = &msaa; + pipeline_ci.pDepthStencilState = &depth_stencil; + pipeline_ci.pColorBlendState = &color_blend; + pipeline_ci.pDynamicState = &dynamic_state; + pipeline_ci.layout = pipeline_layout; + pipeline_ci.renderPass = render_pass; + + VK_CHECK(vkCreateGraphicsPipelines(get_device().get_handle(), VK_NULL_HANDLE, 1, &pipeline_ci, nullptr, &pipeline)); +} + +void ShaderQuadControl::build_command_buffers() +{ + VkCommandBufferBeginInfo begin_info{VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO}; + VkClearValue clear_values[2]; + clear_values[0].color = {{0.0f, 0.0f, 0.0f, 1.0f}}; + clear_values[1].depthStencil = {1.0f, 0}; + + VkRenderPassBeginInfo render_pass_begin{VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO}; + render_pass_begin.renderPass = render_pass; + render_pass_begin.renderArea.offset = {0, 0}; + render_pass_begin.renderArea.extent = get_render_context().get_surface_extent(); + render_pass_begin.clearValueCount = 2; + render_pass_begin.pClearValues = clear_values; + + for (size_t i = 0; i < draw_cmd_buffers.size(); i++) + { + render_pass_begin.framebuffer = framebuffers[i]; + VK_CHECK(vkBeginCommandBuffer(draw_cmd_buffers[i], &begin_info)); + + vkCmdBeginRenderPass(draw_cmd_buffers[i], &render_pass_begin, VK_SUBPASS_CONTENTS_INLINE); + + VkViewport viewport{}; + viewport.width = static_cast(get_render_context().get_surface_extent().width); + viewport.height = static_cast(get_render_context().get_surface_extent().height); + viewport.minDepth = 0.0f; + viewport.maxDepth = 1.0f; + vkCmdSetViewport(draw_cmd_buffers[i], 0, 1, &viewport); + + VkRect2D scissor{{0, 0}, get_render_context().get_surface_extent()}; + vkCmdSetScissor(draw_cmd_buffers[i], 0, 1, &scissor); + + vkCmdBindPipeline(draw_cmd_buffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); + vkCmdDraw(draw_cmd_buffers[i], 3, 1, 0, 0); + + draw_ui(draw_cmd_buffers[i]); + vkCmdEndRenderPass(draw_cmd_buffers[i]); + VK_CHECK(vkEndCommandBuffer(draw_cmd_buffers[i])); + } +} + +void ShaderQuadControl::draw() +{ + prepare_frame(); + submit_info.commandBufferCount = 1; + submit_info.pCommandBuffers = &draw_cmd_buffers[current_buffer]; + VK_CHECK(vkQueueSubmit(queue, 1, &submit_info, VK_NULL_HANDLE)); + submit_frame(); +} + +void ShaderQuadControl::render(float) +{ + if (!prepared) + { + return; + } + draw(); +} + +std::unique_ptr create_shader_quad_control() +{ + return std::make_unique(); +} diff --git a/samples/extensions/shader_quad_control/shader_quad_control.h b/samples/extensions/shader_quad_control/shader_quad_control.h new file mode 100644 index 0000000000..36247eeb09 --- /dev/null +++ b/samples/extensions/shader_quad_control/shader_quad_control.h @@ -0,0 +1,42 @@ +/* Copyright (c) 2025, Holochip Inc. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 the "License"; + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "api_vulkan_sample.h" + +class ShaderQuadControl : public ApiVulkanSample +{ + public: + ShaderQuadControl(); + ~ShaderQuadControl() override; + + bool prepare(const vkb::ApplicationOptions &options) override; + void build_command_buffers() override; + void request_gpu_features(vkb::core::PhysicalDeviceC &gpu) override; + void render(float delta_time) override; + + private: + void create_pipeline_layout(); + void create_pipeline(); + void draw(); + + VkPipeline pipeline{VK_NULL_HANDLE}; + VkPipelineLayout pipeline_layout{VK_NULL_HANDLE}; +}; + +std::unique_ptr create_shader_quad_control(); diff --git a/shaders/shader_quad_control/glsl/quad_control.frag b/shaders/shader_quad_control/glsl/quad_control.frag new file mode 100644 index 0000000000..f627c6459a --- /dev/null +++ b/shaders/shader_quad_control/glsl/quad_control.frag @@ -0,0 +1,34 @@ +/* Copyright (c) 2025, Holochip Inc. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 the "License"; + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#version 450 +#extension GL_EXT_shader_quad_control : require + +// Demonstrate quad control using the execution mode only. We avoid explicit +// subgroup quad operations to keep SPIR-V requirements minimal. +layout(full_quads) in; + +layout(location = 0) in vec2 vUV; +layout(location = 0) out vec4 outColor; + +void main() +{ + // Visualize UVs as color. With full_quads, helper invocations are managed + // to form full quads which can affect implicit derivatives. For a simple + // visualization, just output vUV. + outColor = vec4(vUV, 0.5, 1.0); +} diff --git a/shaders/shader_quad_control/glsl/quad_control.frag.spv b/shaders/shader_quad_control/glsl/quad_control.frag.spv new file mode 100644 index 0000000000..0105d5da6b Binary files /dev/null and b/shaders/shader_quad_control/glsl/quad_control.frag.spv differ diff --git a/shaders/shader_quad_control/glsl/quad_control.vert b/shaders/shader_quad_control/glsl/quad_control.vert new file mode 100644 index 0000000000..3de96d7295 --- /dev/null +++ b/shaders/shader_quad_control/glsl/quad_control.vert @@ -0,0 +1,33 @@ +/* Copyright (c) 2025, Holochip Inc. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 the "License"; + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#version 450 + +layout(location = 0) out vec2 vUV; + +void main() +{ + // Fullscreen triangle without vertex buffers + const vec2 positions[3] = vec2[]( + vec2(-1.0, -1.0), + vec2( 3.0, -1.0), + vec2(-1.0, 3.0) + ); + vec2 pos = positions[gl_VertexIndex]; + vUV = 0.5 * pos + 0.5; + gl_Position = vec4(pos, 0.0, 1.0); +} diff --git a/shaders/shader_quad_control/glsl/quad_control.vert.spv b/shaders/shader_quad_control/glsl/quad_control.vert.spv new file mode 100644 index 0000000000..3ce76989b0 Binary files /dev/null and b/shaders/shader_quad_control/glsl/quad_control.vert.spv differ