Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add dynamic primitive clipping example #902

Merged
merged 13 commits into from
Apr 4, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright (c) 2020-2023, Arm Limited and Contributors
# Copyright (c) 2020-2024, Arm Limited and Contributors
#
# SPDX-License-Identifier: Apache-2.0
#
Expand Down
5 changes: 3 additions & 2 deletions antora/modules/ROOT/nav.adoc
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
////
- Copyright (c) 2023, Holochip Inc
- Copyright (c) 2023, Sascha Willems
- Copyright (c) 2023-2024, Holochip Inc
- Copyright (c) 2023-2024, Sascha Willems
-
- SPDX-License-Identifier: Apache-2.0
-
Expand Down Expand Up @@ -53,6 +53,7 @@
** xref:samples/extensions/descriptor_buffer_basic/README.adoc[Descriptor buffer basic]
** xref:samples/extensions/descriptor_indexing/README.adoc[Descriptor indexing]
** xref:samples/extensions/dynamic_line_rasterization/README.adoc[Dynamic line rasterization]
** xref:samples/extensions/dynamic_primitive_clipping/README.adoc[Dynamic primitive clipping]
** xref:samples/extensions/dynamic_rendering/README.adoc[Dynamic rendering]
** xref:samples/extensions/extended_dynamic_state2/README.adoc[Extended dynamic state2]
** xref:samples/extensions/fragment_shader_barycentric/README.adoc[Fragment shader barycentric]
Expand Down
1 change: 1 addition & 0 deletions samples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ set(ORDER_LIST
"gshader_to_mshader"
"color_write_enable"
"sparse_image"
"dynamic_primitive_clipping"

#Performance Samples
"swapchain_images"
Expand Down
6 changes: 6 additions & 0 deletions samples/README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -507,6 +507,12 @@ Demonstrate how to use the blending related functions available in the VK_EXT_ex

Demonstrate methods for dynamically customizing the appearance of the rendered lines.

=== xref:./extensions/dynamic_primitive_clipping/README.adoc[Dynamic primitive clipping]

*Extension:* https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_EXT_extended_dynamic_state3.html[`VK_EXT_extended_dynamic_state3`]

Rendering using primitive clipping configured by dynamic pipeline state.

== Tooling Samples

The goal of these samples is to demonstrate usage of tooling functions and libraries that are not directly part of the api.
Expand Down
30 changes: 30 additions & 0 deletions samples/extensions/dynamic_primitive_clipping/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Copyright (c) 2024, Mobica Limited
#
# 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 "Mobica"
NAME "dynamic_primitive_clipping"
DESCRIPTION "Rendering using primitive clipping through VK_EXT_extended_dynamic_state3 extension"
SHADER_FILES_GLSL
"dynamic_primitive_clipping/primitive_clipping.vert"
"dynamic_primitive_clipping/primitive_clipping.frag")
103 changes: 103 additions & 0 deletions samples/extensions/dynamic_primitive_clipping/README.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
////
- Copyright (c) 2024, Mobica Limited
-
- 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.
-
////

== Dynamic primitive clipping
pumexx marked this conversation as resolved.
Show resolved Hide resolved

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/dynamic_depth_clipping[Khronos Vulkan samples github repository].
endif::[]

== Overview
pumexx marked this conversation as resolved.
Show resolved Hide resolved

This sample demonstrates how to apply primitive clipping using `vkCmdSetDepthClipEnableEXT()` command which is part of `VK_EXT_extended_dynamic_state3` extension.

# What is primitive clipping

Primitives produced by vertex/geometry/tesellation shaders are sent to fixed-function vertex post-processing.
Primitive clipping is a part of post-processing pipeline in which primitives such as points/lines/triangles are culled against cull volume and then clipped to clip volume.
And then they might be further clipped by results stored in `gl_ClipDistance[]` array - values in this array must be calculated in a vertex/geometry/tesellation shader.

In the past, fixed-function version of OpenGL API provided a method to specify parameters for up to 6 clipping planes ( half-spaces ) that could perform additional primitive clipping. Fixed-function hardware calculated proper distances to these planes and made a decision - should the primitive be clipped or not ( for historical study - search for the `glClipPlane()` description ).

Vulkan inherited the idea of primitive clipping, but with one important difference: user has to calculate the distance to the clip planes on its own in the vertex shader.
And - because user does it in a shader - he does not have to use clip planes. It can be any kind of calculation, as long as the results are put in `gl_ClipDistance[]` array.
Values that are less than 0.0 cause the vertex to be be clipped. In this example we show a group of different calculations that achieve such results ( see shaders/dynamic_depth_clipping/clipping.vert for details ).

In case of triangle primitive the whole triangle is clipped if all of its vertices have values stored in `gl_ClipDistance[]` below 0.0.
When some of these values are above 0.0 - triangle is split into new triangles as described in Vulkan specification.

== How to apply primitive clipping in Vulkan

There are few ways of applying primitive clipping in Vulkan API:

- statically: when `VkPipelineRasterizationDepthClipStateCreateInfoEXT` is present in `VkGraphicsPipelineCreateInfo::pNext` chain and `VkPipelineRasterizationDepthClipStateCreateInfoEXT::depthClipEnable` is set to `VK_TRUE` ( requires extension `VK_EXT_depth_clip_enable` )

- statically: when `VkPipelineRasterizationDepthClipStateCreateInfoEXT` IS NOT present and `VkPipelineRasterizationStateCreateInfo::depthClampEnable` is VK_FALSE

- using shader objects with `vkCmdSetDepthClipEnableEXT(cmd, VK_TRUE)` called before `vkCmdDraw*(cmd, ... )` command.

- dynamically: when `VK_DYNAMIC_STATE_DEPTH_CLAMP_ENABLE_EXT` is present in `VkPipelineDynamicStateCreateInfo::pDynamicStates` and command `vkCmdSetDepthClipEnableEXT(cmd, VK_TRUE)` is called before `vkCmdDraw*(cmd, ... )` command.

In all above mentioned cases - vertex/geometry/tesselation shaders must fill `gl_ClipDistance[]` values.

This example focuses on the last, dynamic case.

In order to use the dynamic primitive clipping we need to:

- create `VkInstance` with extension `VK_KHR_get_physical_device_properties2`

- create `VkDevice` with extension `VK_EXT_extended_dynamic_state3`

- `VkPhysicalDeviceFeatures::shaderClipDistance` must be set to VK_TRUE - in order to use `gl_ClipDistance[]` variable in shaders

- `VkPhysicalDeviceDepthClipEnableFeaturesEXT::depthClipEnable` must be set to VK_TRUE

- `VkPhysicalDeviceExtendedDynamicState3FeaturesEXT::extendedDynamicState3DepthClipEnable` must be set to VK_TRUE - in order to use `vkCmdSetDepthClipEnableEXT()` command

- during graphics pipeline creation `VkPipelineDynamicStateCreateInfo::pDynamicStates` must contain `VK_DYNAMIC_STATE_DEPTH_CLAMP_ENABLE_EXT`

- command `vkCmdSetDepthClipEnableEXT(cmd, VK_TRUE)` is called before `vkCmdDraw*(cmd, ... )` command

- `gl_ClipDistance[]` must be added to a definition of `gl_PerVertex` structure in a vertex shader. Simplest form with one value per vertex will look like this:

[,glsl]
----
out gl_PerVertex
{
vec4 gl_Position;
float gl_ClipDistance[1];
};
----

The size of `gl_ClipDistance[]` array may not be larger than `VkPhysicalDeviceLimits::maxClipDistances`.

== Potential applications

In the past primitive clipping was used in different CAD applications to make cross-sections of different objects.
We still can use it in similar fashion, but other applications also come to mind:

- we can hide parts of the 3D model

- we can make holes in a terrain

- we can use it in some special effects

Advantage of using primitive clipping over using `discard` keyword in a fragment shader is obvious: we are doing it earlier in a pipeline which may result in better performance ( or may not, you have to measure it ).

But beware of vertex density: because this technique is vertex based it may have some nasty results when vertices are too sparse. See "Torusknot" object type with "Clip space Y" visualization in a sample to see where the problem may arise.
Loading
Loading