Skip to content

Commit d9524ac

Browse files
committed
add shader quad control sample demonstrating VK_KHR_shader_quad_control features.
1 parent 19c2432 commit d9524ac

File tree

11 files changed

+422
-1
lines changed

11 files changed

+422
-1
lines changed

antora/modules/ROOT/nav.adoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@
8686
** xref:samples/extensions/ray_tracing_reflection/README.adoc[Ray tracing reflection]
8787
** xref:samples/extensions/ray_tracing_position_fetch/README.adoc[Ray tracing position fetch]
8888
** xref:samples/extensions/shader_object/README.adoc[Shader Object]
89+
** xref:samples/extensions/shader_quad_control/README.adoc[Shader quad control]
8990
** xref:samples/extensions/shader_debugprintf/README.adoc[Shader Debug Printf]
9091
** xref:samples/extensions/sparse_image/README.adoc[Sparse Image]
9192
** xref:samples/extensions/synchronization_2/README.adoc[Synchronization 2]

framework/vulkan_type_mapping.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,12 @@ struct HPPType<VkPhysicalDeviceFragmentShaderBarycentricFeaturesKHR>
162162
using Type = vk::PhysicalDeviceFragmentShaderBarycentricFeaturesKHR;
163163
};
164164

165+
template <>
166+
struct HPPType<VkPhysicalDeviceShaderQuadControlFeaturesKHR>
167+
{
168+
using Type = vk::PhysicalDeviceShaderQuadControlFeaturesKHR;
169+
};
170+
165171
template <>
166172
struct HPPType<VkPhysicalDeviceFragmentShadingRateFeaturesKHR>
167173
{

samples/extensions/README.adoc

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
////
2-
- Copyright (c) 2021-2024, The Khronos Group
2+
- Copyright (c) 2021-2025, The Khronos Group
33
-
44
- SPDX-License-Identifier: Apache-2.0
55
-
@@ -302,3 +302,9 @@ Demonstrate the use of the host image extension to directly copy from a host buf
302302
*Extensions:* https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_EXT_extended_dynamic_state3.html[`VK_EXT_line_rasterization`], https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_EXT_extended_dynamic_state3.html[`VK_EXT_extended_dynamic_state3`]
303303

304304
Demonstrate how to use dynamic multisample rasterization (MSAA)
305+
306+
=== xref:./{extension_samplespath}shader_quad_control/README.adoc[Shader quad control]
307+
308+
*Extension*: https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_KHR_shader_quad_control.html[`VK_KHR_shader_quad_control`]
309+
310+
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.
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Copyright (c) 2025, Holochip Inc.
2+
#
3+
# SPDX-License-Identifier: Apache-2.0
4+
#
5+
# Licensed under the Apache License, Version 2.0 the "License";
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
17+
get_filename_component(FOLDER_NAME ${CMAKE_CURRENT_LIST_DIR} NAME)
18+
get_filename_component(PARENT_DIR ${CMAKE_CURRENT_LIST_DIR} PATH)
19+
get_filename_component(CATEGORY_NAME ${PARENT_DIR} NAME)
20+
21+
add_sample(
22+
ID ${FOLDER_NAME}
23+
CATEGORY ${CATEGORY_NAME}
24+
AUTHOR "Holochip"
25+
NAME "Shader quad control"
26+
DESCRIPTION "Demonstrates VK_KHR_shader_quad_control with a simple fullscreen draw using quad vote ops."
27+
SHADER_FILES_GLSL
28+
"shader_quad_control/glsl/quad_control.vert"
29+
"shader_quad_control/glsl/quad_control.frag")
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
////
2+
- Copyright (c) 2025, Holochip Inc.
3+
-
4+
- SPDX-License-Identifier: Apache-2.0
5+
-
6+
- Licensed under the Apache License, Version 2.0 the "License";
7+
- you may not use this file except in compliance with the License.
8+
- You may obtain a copy of the License at
9+
-
10+
- http://www.apache.org/licenses/LICENSE-2.0
11+
-
12+
- Unless required by applicable law or agreed to in writing, software
13+
- distributed under the License is distributed on an "AS IS" BASIS,
14+
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
- See the License for the specific language governing permissions and
16+
- limitations under the License.
17+
-
18+
////
19+
20+
= Shader quad control (VK_KHR_shader_quad_control)
21+
22+
This sample demonstrates VK_KHR_shader_quad_control in a minimal graphics pipeline.
23+
It renders a full‑screen triangle and uses quad‑scope operations in the fragment shader to
24+
broadcast values within a 2x2 pixel quad. This produces a characteristic “blocky” 2x2 pattern,
25+
clearly showing how quad operations affect shading.
26+
27+
== What is shader quad control?
28+
VK_KHR_shader_quad_control exposes SPIR‑V and shading language capabilities that operate at the
29+
scope of a fragment “quad” – the 2x2 group of fragment shader invocations that participate in
30+
coherent derivative calculation. The extension adds:
31+
32+
- A shader execution mode to control quad formation: `layout(full_quads)` or `layout(quad_derivatives)`.
33+
- Built‑ins and functions to communicate within a quad, like `subgroupQuadBroadcast(...)`, and vote
34+
operations `subgroupQuadAll(...)` / `subgroupQuadAny(...)`.
35+
36+
Typical uses include:
37+
38+
- Stabilizing derivatives, LOD selection, and gradient‑sensitive operations across the 2x2 quad.
39+
- Sharing values between lanes in a quad without using shared memory.
40+
41+
== What this sample does
42+
- Creates a simple graphics pipeline (full‑screen triangle, no descriptors).
43+
- The fragment shader enables `GL_EXT_shader_quad_control` and declares `layout(full_quads) in;`.
44+
- It calls `subgroupQuadBroadcast(vUV, 0)` to broadcast the top‑left lane’s interpolant to the
45+
whole quad, so each 2x2 block shows one color based on the leader lane.
46+
47+
This minimal approach keeps focus on the quad control feature rather than complex rendering.
48+
49+
== Required extensions and features
50+
To run this sample, the device must support `VK_KHR_shader_quad_control`.
51+
52+
No descriptors or additional states are required for this minimal demo. The sample uses the
53+
framework’s default render pass and a basic pipeline configuration.
54+
55+
== Fragment shader
56+
The fragment shader uses quad control to broadcast a varying from the quad leader:
57+
58+
[source,glsl]
59+
----
60+
#version 450
61+
#extension GL_EXT_shader_quad_control : require
62+
63+
layout(full_quads) in; // Control quad scope
64+
layout(location = 0) in vec2 vUV; // Interpolated UV from VS
65+
layout(location = 0) out vec4 outColor;
66+
67+
void main()
68+
{
69+
vec2 uv_leader = subgroupQuadBroadcast(vUV, 0); // From top-left of the 2x2 quad
70+
outColor = vec4(uv_leader, 0.5, 1.0);
71+
}
72+
----
73+
74+
Switching to `layout(quad_derivatives) in;` would instead ensure that implicit derivatives are
75+
computed in a quad‑coherent way (useful if you perform gradient operations like texture LODs).
76+
This sample focuses on the broadcasting operation for clarity.
77+
78+
== Specification
79+
- Vulkan: https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_KHR_shader_quad_control.html[VK_KHR_shader_quad_control]
80+
- GLSL: https://github.com/KhronosGroup/GLSL/blob/main/extensions/ext/GLSL_EXT_shader_quad_control.txt[GLSL_EXT_shader_quad_control]
Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
/* Copyright (c) 2025, Holochip Inc.
2+
*
3+
* SPDX-License-Identifier: Apache-2.0
4+
*
5+
* Licensed under the Apache License, Version 2.0 the "License";
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
#include "shader_quad_control.h"
19+
20+
ShaderQuadControl::ShaderQuadControl()
21+
{
22+
title = "Shader quad control";
23+
set_api_version(VK_API_VERSION_1_2);
24+
25+
add_instance_extension(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
26+
// VK_KHR_shader_quad_control requires VK_KHR_shader_maximal_reconvergence per spec
27+
add_device_extension(VK_KHR_SHADER_MAXIMAL_RECONVERGENCE_EXTENSION_NAME);
28+
add_device_extension(VK_KHR_SHADER_QUAD_CONTROL_EXTENSION_NAME);
29+
}
30+
31+
ShaderQuadControl::~ShaderQuadControl()
32+
{
33+
if (has_device())
34+
{
35+
if (pipeline != VK_NULL_HANDLE)
36+
{
37+
vkDestroyPipeline(get_device().get_handle(), pipeline, nullptr);
38+
}
39+
if (pipeline_layout != VK_NULL_HANDLE)
40+
{
41+
vkDestroyPipelineLayout(get_device().get_handle(), pipeline_layout, nullptr);
42+
}
43+
}
44+
}
45+
46+
bool ShaderQuadControl::prepare(const vkb::ApplicationOptions &options)
47+
{
48+
if (!ApiVulkanSample::prepare(options))
49+
{
50+
return false;
51+
}
52+
53+
create_pipeline_layout();
54+
create_pipeline();
55+
build_command_buffers();
56+
57+
prepared = true;
58+
return true;
59+
}
60+
61+
void ShaderQuadControl::request_gpu_features(vkb::core::PhysicalDeviceC &gpu)
62+
{
63+
REQUEST_REQUIRED_FEATURE(gpu, VkPhysicalDeviceShaderQuadControlFeaturesKHR, shaderQuadControl);
64+
}
65+
66+
void ShaderQuadControl::create_pipeline_layout()
67+
{
68+
VkPipelineLayoutCreateInfo pipeline_layout_ci{VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO};
69+
VK_CHECK(vkCreatePipelineLayout(get_device().get_handle(), &pipeline_layout_ci, nullptr, &pipeline_layout));
70+
}
71+
72+
void ShaderQuadControl::create_pipeline()
73+
{
74+
std::array<VkPipelineShaderStageCreateInfo, 2> stages{};
75+
stages[0] = load_shader("shader_quad_control/glsl/quad_control.vert.spv", VK_SHADER_STAGE_VERTEX_BIT);
76+
stages[1] = load_shader("shader_quad_control/glsl/quad_control.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT);
77+
78+
VkPipelineVertexInputStateCreateInfo vertex_input{VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO};
79+
80+
VkPipelineInputAssemblyStateCreateInfo input_assembly{VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO};
81+
input_assembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
82+
83+
VkPipelineViewportStateCreateInfo viewport_state{VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO};
84+
viewport_state.viewportCount = 1;
85+
viewport_state.scissorCount = 1;
86+
87+
VkPipelineRasterizationStateCreateInfo raster{VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO};
88+
raster.polygonMode = VK_POLYGON_MODE_FILL;
89+
raster.cullMode = VK_CULL_MODE_NONE;
90+
raster.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
91+
raster.lineWidth = 1.0f;
92+
93+
VkPipelineMultisampleStateCreateInfo msaa{VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO};
94+
msaa.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
95+
96+
VkPipelineDepthStencilStateCreateInfo depth_stencil{VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO};
97+
depth_stencil.depthTestEnable = VK_FALSE;
98+
depth_stencil.depthWriteEnable = VK_FALSE;
99+
100+
VkPipelineColorBlendAttachmentState color_blend_attachment{};
101+
color_blend_attachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
102+
103+
VkPipelineColorBlendStateCreateInfo color_blend{VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO};
104+
color_blend.attachmentCount = 1;
105+
color_blend.pAttachments = &color_blend_attachment;
106+
107+
std::array<VkDynamicState, 2> dynamic_states{VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR};
108+
VkPipelineDynamicStateCreateInfo dynamic_state{VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO};
109+
dynamic_state.dynamicStateCount = static_cast<uint32_t>(dynamic_states.size());
110+
dynamic_state.pDynamicStates = dynamic_states.data();
111+
112+
VkGraphicsPipelineCreateInfo pipeline_ci{VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO};
113+
pipeline_ci.stageCount = static_cast<uint32_t>(stages.size());
114+
pipeline_ci.pStages = stages.data();
115+
pipeline_ci.pVertexInputState = &vertex_input;
116+
pipeline_ci.pInputAssemblyState = &input_assembly;
117+
pipeline_ci.pViewportState = &viewport_state;
118+
pipeline_ci.pRasterizationState = &raster;
119+
pipeline_ci.pMultisampleState = &msaa;
120+
pipeline_ci.pDepthStencilState = &depth_stencil;
121+
pipeline_ci.pColorBlendState = &color_blend;
122+
pipeline_ci.pDynamicState = &dynamic_state;
123+
pipeline_ci.layout = pipeline_layout;
124+
pipeline_ci.renderPass = render_pass;
125+
126+
VK_CHECK(vkCreateGraphicsPipelines(get_device().get_handle(), VK_NULL_HANDLE, 1, &pipeline_ci, nullptr, &pipeline));
127+
}
128+
129+
void ShaderQuadControl::build_command_buffers()
130+
{
131+
VkCommandBufferBeginInfo begin_info{VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO};
132+
VkClearValue clear_values[2];
133+
clear_values[0].color = {{0.0f, 0.0f, 0.0f, 1.0f}};
134+
clear_values[1].depthStencil = {1.0f, 0};
135+
136+
VkRenderPassBeginInfo render_pass_begin{VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO};
137+
render_pass_begin.renderPass = render_pass;
138+
render_pass_begin.renderArea.offset = {0, 0};
139+
render_pass_begin.renderArea.extent = get_render_context().get_surface_extent();
140+
render_pass_begin.clearValueCount = 2;
141+
render_pass_begin.pClearValues = clear_values;
142+
143+
for (size_t i = 0; i < draw_cmd_buffers.size(); i++)
144+
{
145+
render_pass_begin.framebuffer = framebuffers[i];
146+
VK_CHECK(vkBeginCommandBuffer(draw_cmd_buffers[i], &begin_info));
147+
148+
vkCmdBeginRenderPass(draw_cmd_buffers[i], &render_pass_begin, VK_SUBPASS_CONTENTS_INLINE);
149+
150+
VkViewport viewport{};
151+
viewport.width = static_cast<float>(get_render_context().get_surface_extent().width);
152+
viewport.height = static_cast<float>(get_render_context().get_surface_extent().height);
153+
viewport.minDepth = 0.0f;
154+
viewport.maxDepth = 1.0f;
155+
vkCmdSetViewport(draw_cmd_buffers[i], 0, 1, &viewport);
156+
157+
VkRect2D scissor{{0, 0}, get_render_context().get_surface_extent()};
158+
vkCmdSetScissor(draw_cmd_buffers[i], 0, 1, &scissor);
159+
160+
vkCmdBindPipeline(draw_cmd_buffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
161+
vkCmdDraw(draw_cmd_buffers[i], 3, 1, 0, 0);
162+
163+
draw_ui(draw_cmd_buffers[i]);
164+
vkCmdEndRenderPass(draw_cmd_buffers[i]);
165+
VK_CHECK(vkEndCommandBuffer(draw_cmd_buffers[i]));
166+
}
167+
}
168+
169+
void ShaderQuadControl::draw()
170+
{
171+
prepare_frame();
172+
submit_info.commandBufferCount = 1;
173+
submit_info.pCommandBuffers = &draw_cmd_buffers[current_buffer];
174+
VK_CHECK(vkQueueSubmit(queue, 1, &submit_info, VK_NULL_HANDLE));
175+
submit_frame();
176+
}
177+
178+
void ShaderQuadControl::render(float)
179+
{
180+
if (!prepared)
181+
{
182+
return;
183+
}
184+
draw();
185+
}
186+
187+
std::unique_ptr<vkb::VulkanSampleC> create_shader_quad_control()
188+
{
189+
return std::make_unique<ShaderQuadControl>();
190+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/* Copyright (c) 2025, Holochip Inc.
2+
*
3+
* SPDX-License-Identifier: Apache-2.0
4+
*
5+
* Licensed under the Apache License, Version 2.0 the "License";
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
#pragma once
19+
20+
#include "api_vulkan_sample.h"
21+
22+
class ShaderQuadControl : public ApiVulkanSample
23+
{
24+
public:
25+
ShaderQuadControl();
26+
~ShaderQuadControl() override;
27+
28+
bool prepare(const vkb::ApplicationOptions &options) override;
29+
void build_command_buffers() override;
30+
void request_gpu_features(vkb::core::PhysicalDeviceC &gpu) override;
31+
void render(float delta_time) override;
32+
33+
private:
34+
void create_pipeline_layout();
35+
void create_pipeline();
36+
void draw();
37+
38+
VkPipeline pipeline{VK_NULL_HANDLE};
39+
VkPipelineLayout pipeline_layout{VK_NULL_HANDLE};
40+
};
41+
42+
std::unique_ptr<vkb::VulkanSampleC> create_shader_quad_control();

0 commit comments

Comments
 (0)