Implement VK_EXT_depth_clip_control
The extension allows for a depth range of [-1, 1], which aids in
layering OpenGL over Vulkan. After clipping, the depth is
renormalized to the standard Vulkan range of [0, 1].
Based on change by Sean Risser <srisser@google.com>.
Bug: b/207658159
Tests: dEQP-VK.pipeline.*.depth.*
Change-Id: I40297b39e3447296bf5f7c39386d47a0663aa792
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/67489
Tested-by: Shahbaz Youssefi <syoussefi@google.com>
Reviewed-by: Nicolas Capens <nicolascapens@google.com>
Presubmit-Ready: Shahbaz Youssefi <syoussefi@google.com>
Kokoro-Result: kokoro <noreply+kokoro@google.com>
Commit-Queue: Shahbaz Youssefi <syoussefi@google.com>
diff --git a/src/Device/Clipper.cpp b/src/Device/Clipper.cpp
index 54b27eb..e7f4caa 100644
--- a/src/Device/Clipper.cpp
+++ b/src/Device/Clipper.cpp
@@ -29,7 +29,7 @@
Vo.w = (dj * Vi.w - di * Vj.w) * D;
}
-void clipNear(sw::Polygon &polygon)
+void clipNear(sw::Polygon &polygon, bool depthClipNegativeOneToOne)
{
const sw::float4 **V = polygon.P[polygon.i];
const sw::float4 **T = polygon.P[polygon.i + 1];
@@ -43,6 +43,14 @@
float di = V[i]->z;
float dj = V[j]->z;
+ // When depthClipNegativeOneToOne is enabled, z is mapped to (z+w)/2. Division by 2
+ // is not done, as distances are relative as far as clipEdge is concerned.
+ if(depthClipNegativeOneToOne)
+ {
+ di += V[i]->w;
+ dj += V[j]->w;
+ }
+
if(di >= 0)
{
T[t++] = V[i];
@@ -81,6 +89,13 @@
float di = V[i]->w - V[i]->z;
float dj = V[j]->w - V[j]->z;
+ // When depthClipNegativeOneToOne is enabled, z is mapped to (z+w)/2. We have:
+ //
+ // w - (z+w)/2 = (w - z)/2
+ //
+ // But division by 2 is not done, as distances are relative as far as clipEdge is
+ // concerned. In short, no special handling is necessary.
+
if(di >= 0)
{
T[t++] = V[i];
@@ -265,7 +280,7 @@
{
if(clipFlagsOr & CLIP_FRUSTUM)
{
- if(clipFlagsOr & CLIP_NEAR) clipNear(polygon);
+ if(clipFlagsOr & CLIP_NEAR) clipNear(polygon, draw.depthClipNegativeOneToOne);
if(polygon.n >= 3)
{
if(clipFlagsOr & CLIP_FAR) clipFar(polygon);
diff --git a/src/Device/Context.cpp b/src/Device/Context.cpp
index 204ff55..84fc868 100644
--- a/src/Device/Context.cpp
+++ b/src/Device/Context.cpp
@@ -506,6 +506,27 @@
const VkPipelineDepthStencilStateCreateInfo *depthStencilState = pCreateInfo->pDepthStencilState;
const VkPipelineColorBlendStateCreateInfo *colorBlendState = pCreateInfo->pColorBlendState;
+ extensionCreateInfo = reinterpret_cast<const VkBaseInStructure *>(viewportState->pNext);
+ while(extensionCreateInfo != nullptr)
+ {
+ switch(extensionCreateInfo->sType)
+ {
+ case VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_DEPTH_CLIP_CONTROL_CREATE_INFO_EXT:
+ {
+ const auto *depthClipControl = reinterpret_cast<const VkPipelineViewportDepthClipControlCreateInfoEXT *>(extensionCreateInfo);
+ depthClipNegativeOneToOne = depthClipControl->negativeOneToOne != VK_FALSE;
+ }
+ break;
+ case VK_STRUCTURE_TYPE_MAX_ENUM:
+ // dEQP passes this value expecting the driver to ignore it.
+ break;
+ default:
+ UNSUPPORTED("pCreateInfo->pViewportState->pNext sType = %s", vk::Stringify(extensionCreateInfo->sType).c_str());
+ break;
+ }
+ extensionCreateInfo = extensionCreateInfo->pNext;
+ }
+
if(viewportState->flags != 0)
{
// Vulkan 1.2: "flags is reserved for future use." "flags must be 0"
diff --git a/src/Device/Context.hpp b/src/Device/Context.hpp
index 0e5acd0..f3173b5 100644
--- a/src/Device/Context.hpp
+++ b/src/Device/Context.hpp
@@ -181,6 +181,7 @@
inline bool hasDepthRangeUnrestricted() const { return depthRangeUnrestricted; }
inline bool getDepthClampEnable() const { return depthClampEnable; }
inline bool getDepthClipEnable() const { return depthClipEnable; }
+ inline bool getDepthClipNegativeOneToOne() const { return depthClipNegativeOneToOne; }
// Pixel processor states
inline bool hasRasterizerDiscard() const { return rasterizerDiscard; }
@@ -286,6 +287,7 @@
bool depthWriteEnable = false;
bool depthClampEnable = false;
bool depthClipEnable = false;
+ bool depthClipNegativeOneToOne = false;
float lineWidth = 0.0f;
diff --git a/src/Device/Renderer.cpp b/src/Device/Renderer.cpp
index ce30ec5..2b6949a 100644
--- a/src/Device/Renderer.cpp
+++ b/src/Device/Renderer.cpp
@@ -267,6 +267,7 @@
draw->descriptorSetObjects = inputs.getDescriptorSetObjects();
draw->pipelineLayout = pipelineState.getPipelineLayout();
draw->depthClipEnable = pipelineState.getDepthClipEnable();
+ draw->depthClipNegativeOneToOne = pipelineState.getDepthClipNegativeOneToOne();
draw->vertexRoutine = vertexRoutine;
draw->setupRoutine = setupRoutine;
@@ -356,6 +357,13 @@
data->slopeDepthBias = pipelineState.getSlopeDepthBias();
data->depthBiasClamp = pipelineState.getDepthBiasClamp();
+ // Adjust viewport transform based on the negativeOneToOne state.
+ if(pipelineState.getDepthClipNegativeOneToOne())
+ {
+ data->depthRange = Z * 0.5f;
+ data->depthNear = (F + N) * 0.5f;
+ }
+
const vk::Attachments attachments = pipeline->getAttachments();
if(attachments.depthBuffer)
{
diff --git a/src/Device/Renderer.hpp b/src/Device/Renderer.hpp
index 8263522..2f15e9f 100644
--- a/src/Device/Renderer.hpp
+++ b/src/Device/Renderer.hpp
@@ -153,6 +153,7 @@
VkLineRasterizationModeEXT lineRasterizationMode;
bool depthClipEnable;
+ bool depthClipNegativeOneToOne;
VertexProcessor::RoutineType vertexRoutine;
SetupProcessor::RoutineType setupRoutine;
diff --git a/src/Device/VertexProcessor.cpp b/src/Device/VertexProcessor.cpp
index 833fa6a..42df2d2 100644
--- a/src/Device/VertexProcessor.cpp
+++ b/src/Device/VertexProcessor.cpp
@@ -74,6 +74,7 @@
state.robustBufferAccess = pipelineState.getRobustBufferAccess();
state.isPoint = pipelineState.getTopology() == VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
state.depthClipEnable = pipelineState.getDepthClipEnable();
+ state.depthClipNegativeOneToOne = pipelineState.getDepthClipNegativeOneToOne();
for(size_t i = 0; i < MAX_INTERFACE_COMPONENTS / 4; i++)
{
diff --git a/src/Device/VertexProcessor.hpp b/src/Device/VertexProcessor.hpp
index e8f6b14..f4f7e3a 100644
--- a/src/Device/VertexProcessor.hpp
+++ b/src/Device/VertexProcessor.hpp
@@ -82,6 +82,7 @@
bool robustBufferAccess : 1;
bool isPoint : 1;
bool depthClipEnable : 1;
+ bool depthClipNegativeOneToOne : 1;
};
struct State : States
diff --git a/src/Pipeline/VertexRoutine.cpp b/src/Pipeline/VertexRoutine.cpp
index 36ce92c..7877eee 100644
--- a/src/Pipeline/VertexRoutine.cpp
+++ b/src/Pipeline/VertexRoutine.cpp
@@ -136,8 +136,9 @@
clipFlags |= minY & Clipper::CLIP_BOTTOM;
if(state.depthClipEnable)
{
+ // If depthClipNegativeOneToOne is enabled, depth values are in [-1, 1] instead of [0, 1].
SIMD::Int maxZ = CmpLT(posW, posZ);
- SIMD::Int minZ = CmpNLE(0.0f, posZ);
+ SIMD::Int minZ = CmpNLE(state.depthClipNegativeOneToOne ? -posW : 0.0f, posZ);
clipFlags |= maxZ & Clipper::CLIP_FAR;
clipFlags |= minZ & Clipper::CLIP_NEAR;
}
@@ -148,7 +149,7 @@
SIMD::Int finiteZ = CmpLE(Abs(posZ), maxPos);
SIMD::Int finiteXYZ = finiteX & finiteY & finiteZ;
- clipFlags |= finiteXYZ & 0x00000080;
+ clipFlags |= finiteXYZ & Clipper::CLIP_FINITE;
}
}
diff --git a/src/Vulkan/VkPhysicalDevice.cpp b/src/Vulkan/VkPhysicalDevice.cpp
index 1010d65..601bc23 100644
--- a/src/Vulkan/VkPhysicalDevice.cpp
+++ b/src/Vulkan/VkPhysicalDevice.cpp
@@ -454,6 +454,11 @@
features->rasterizationOrderStencilAttachmentAccess = VK_TRUE;
}
+static void getPhysicalDeviceDepthClipControlFeaturesExt(VkPhysicalDeviceDepthClipControlFeaturesEXT *features)
+{
+ features->depthClipControl = VK_TRUE;
+}
+
void PhysicalDevice::getFeatures2(VkPhysicalDeviceFeatures2 *features) const
{
features->features = getFeatures();
@@ -603,6 +608,9 @@
// Workaround for a test bug (see https://gitlab.khronos.org/Tracker/vk-gl-cts/-/issues/3564)
reinterpret_cast<struct VkPhysicalDeviceTexelBufferAlignmentFeaturesEXT *>(curExtension)->texelBufferAlignment = VK_TRUE;
break;
+ case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_CLIP_CONTROL_FEATURES_EXT:
+ getPhysicalDeviceDepthClipControlFeaturesExt(reinterpret_cast<struct VkPhysicalDeviceDepthClipControlFeaturesEXT *>(curExtension));
+ break;
case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBPASS_MERGE_FEEDBACK_FEATURES_EXT:
// TODO(b/216982034): Workaround for a test bug (see https://gitlab.khronos.org/Tracker/vk-gl-cts/-/issues/3879)
reinterpret_cast<struct VkPhysicalDeviceSubpassMergeFeedbackFeaturesEXT *>(curExtension)->subpassMergeFeedback = VK_FALSE;
diff --git a/src/Vulkan/libVulkan.cpp b/src/Vulkan/libVulkan.cpp
index 0476145..be35d3a 100644
--- a/src/Vulkan/libVulkan.cpp
+++ b/src/Vulkan/libVulkan.cpp
@@ -402,6 +402,7 @@
{ { VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME, VK_KHR_SYNCHRONIZATION_2_SPEC_VERSION } },
{ { VK_KHR_ZERO_INITIALIZE_WORKGROUP_MEMORY_EXTENSION_NAME, VK_KHR_ZERO_INITIALIZE_WORKGROUP_MEMORY_SPEC_VERSION } },
// Additional extension
+ { { VK_EXT_DEPTH_CLIP_CONTROL_EXTENSION_NAME, VK_EXT_DEPTH_CLIP_CONTROL_SPEC_VERSION } },
{ { VK_GOOGLE_DECORATE_STRING_EXTENSION_NAME, VK_GOOGLE_DECORATE_STRING_SPEC_VERSION } },
{ { VK_GOOGLE_HLSL_FUNCTIONALITY_1_EXTENSION_NAME, VK_GOOGLE_HLSL_FUNCTIONALITY_1_SPEC_VERSION } },
{ { VK_GOOGLE_USER_TYPE_EXTENSION_NAME, VK_GOOGLE_USER_TYPE_SPEC_VERSION } },
@@ -1060,6 +1061,7 @@
case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_MEMORY_MODEL_FEATURES:
case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_4_FEATURES:
case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_FEATURES_EXT:
+ case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_CLIP_CONTROL_FEATURES_EXT:
break;
case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TEXEL_BUFFER_ALIGNMENT_FEATURES_EXT:
// Workaround for a test bug (see https://gitlab.khronos.org/Tracker/vk-gl-cts/-/issues/3564)