Initial support for VK_EXT_pipeline_robustness
In this initial implementation, enabling robustBufferAccess on any of
storage, uniform or vertex buffers makes it enabled for all of them.
This can be optimized in a future change, by appropriately tracking
separate robustBufferAccess flags for storage, uniform and vertex
buffers and using the right flag for the right buffer.
Bug: b/185122256
Tests: dEQP-VK.robustness.pipeline_robustness*
Change-Id: Idb9f841e2a850b866cdfbc1d558efb2d33203c4d
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/67948
Presubmit-Ready: Shahbaz Youssefi <syoussefi@google.com>
Kokoro-Result: kokoro <noreply+kokoro@google.com>
Tested-by: Shahbaz Youssefi <syoussefi@google.com>
Commit-Queue: Shahbaz Youssefi <syoussefi@google.com>
Reviewed-by: Nicolas Capens <nicolascapens@google.com>
Reviewed-by: Alexis Hétu <sugoi@google.com>
diff --git a/src/Device/Context.cpp b/src/Device/Context.cpp
index a338198..65b9d97 100644
--- a/src/Device/Context.cpp
+++ b/src/Device/Context.cpp
@@ -382,9 +382,8 @@
}
GraphicsState::GraphicsState(const Device *device, const VkGraphicsPipelineCreateInfo *pCreateInfo,
- const PipelineLayout *layout, bool robustBufferAccess)
+ const PipelineLayout *layout)
: pipelineLayout(layout)
- , robustBufferAccess(robustBufferAccess)
, dynamicStateFlags(ParseDynamicStateFlags(pCreateInfo->pDynamicState))
{
if((pCreateInfo->flags &
diff --git a/src/Device/Context.hpp b/src/Device/Context.hpp
index f3173b5..cbd4a0b 100644
--- a/src/Device/Context.hpp
+++ b/src/Device/Context.hpp
@@ -154,12 +154,11 @@
struct GraphicsState
{
- GraphicsState(const Device *device, const VkGraphicsPipelineCreateInfo *pCreateInfo, const PipelineLayout *layout, bool robustBufferAccess);
+ GraphicsState(const Device *device, const VkGraphicsPipelineCreateInfo *pCreateInfo, const PipelineLayout *layout);
const GraphicsState combineStates(const DynamicState &dynamicState) const;
inline const PipelineLayout *getPipelineLayout() const { return pipelineLayout; }
- inline bool getRobustBufferAccess() const { return robustBufferAccess; }
inline VkPrimitiveTopology getTopology() const { return topology; }
inline VkProvokingVertexModeEXT getProvokingVertexMode() const { return provokingVertexMode; }
@@ -255,7 +254,6 @@
bool colorWriteActive(const Attachments &attachments) const;
const PipelineLayout *pipelineLayout = nullptr;
- const bool robustBufferAccess = false;
const DynamicStateFlags dynamicStateFlags = {};
VkPrimitiveTopology topology = VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
diff --git a/src/Device/VertexProcessor.cpp b/src/Device/VertexProcessor.cpp
index 42df2d2..23e8e9d 100644
--- a/src/Device/VertexProcessor.cpp
+++ b/src/Device/VertexProcessor.cpp
@@ -71,7 +71,7 @@
state.shaderID = vertexShader->getIdentifier();
state.pipelineLayoutIdentifier = pipelineState.getPipelineLayout()->identifier;
- state.robustBufferAccess = pipelineState.getRobustBufferAccess();
+ state.robustBufferAccess = vertexShader->getRobustBufferAccess();
state.isPoint = pipelineState.getTopology() == VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
state.depthClipEnable = pipelineState.getDepthClipEnable();
state.depthClipNegativeOneToOne = pipelineState.getDepthClipNegativeOneToOne();
diff --git a/src/Pipeline/SpirvShader.hpp b/src/Pipeline/SpirvShader.hpp
index 74dc1c6..ceaa72f 100644
--- a/src/Pipeline/SpirvShader.hpp
+++ b/src/Pipeline/SpirvShader.hpp
@@ -951,6 +951,7 @@
uint32_t getWorkgroupSizeZ() const;
bool containsImageWrite() const { return imageWriteEmitted; }
+ bool getRobustBufferAccess() const { return robustBufferAccess; }
using BuiltInHash = std::hash<std::underlying_type<spv::BuiltIn>::type>;
std::unordered_map<spv::BuiltIn, BuiltinMapping, BuiltInHash> inputBuiltins;
diff --git a/src/Vulkan/VkPhysicalDevice.cpp b/src/Vulkan/VkPhysicalDevice.cpp
index 499f1ee..a7352a3 100644
--- a/src/Vulkan/VkPhysicalDevice.cpp
+++ b/src/Vulkan/VkPhysicalDevice.cpp
@@ -377,6 +377,12 @@
}
template<typename T>
+static void getPhysicalDevicePipelineRobustnessFeatures(T *features)
+{
+ features->pipelineRobustness = VK_TRUE;
+}
+
+template<typename T>
static void getPhysicalDeviceVulkan12Features(T *features)
{
features->samplerMirrorClampToEdge = VK_TRUE;
@@ -601,6 +607,9 @@
case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIMITIVE_TOPOLOGY_LIST_RESTART_FEATURES_EXT:
getPhysicalDevicePrimitiveTopologyListRestartFeatures(reinterpret_cast<struct VkPhysicalDevicePrimitiveTopologyListRestartFeaturesEXT *>(curExtension));
break;
+ case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PIPELINE_ROBUSTNESS_FEATURES_EXT:
+ getPhysicalDevicePipelineRobustnessFeatures(reinterpret_cast<struct VkPhysicalDevicePipelineRobustnessFeaturesEXT *>(curExtension));
+ break;
case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_FEATURES_EXT:
getPhysicalDeviceRasterizationOrderAttachmentAccessFeaturesEXT(reinterpret_cast<struct VkPhysicalDeviceRasterizationOrderAttachmentAccessFeaturesEXT *>(curExtension));
break;
@@ -1338,6 +1347,24 @@
getTimelineSemaphoreProperties(properties);
}
+template<typename T>
+static void getPipelineRobustnessProperties(T *properties)
+{
+ // Buffer access is not robust by default.
+ properties->defaultRobustnessStorageBuffers = VK_PIPELINE_ROBUSTNESS_BUFFER_BEHAVIOR_DISABLED_EXT;
+ properties->defaultRobustnessUniformBuffers = VK_PIPELINE_ROBUSTNESS_BUFFER_BEHAVIOR_DISABLED_EXT;
+ properties->defaultRobustnessVertexInputs = VK_PIPELINE_ROBUSTNESS_BUFFER_BEHAVIOR_DISABLED_EXT;
+ // SwiftShader currently provides robustImageAccess robustness unconditionally.
+ // robustImageAccess2 is not supported.
+ // TODO(b/162327166): Only provide robustness when requested.
+ properties->defaultRobustnessImages = VK_PIPELINE_ROBUSTNESS_IMAGE_BEHAVIOR_ROBUST_IMAGE_ACCESS_EXT;
+}
+
+void PhysicalDevice::getProperties(VkPhysicalDevicePipelineRobustnessPropertiesEXT *properties) const
+{
+ getPipelineRobustnessProperties(properties);
+}
+
void PhysicalDevice::getProperties(VkPhysicalDeviceVulkan12Properties *properties) const
{
getDriverProperties(properties);
@@ -1618,6 +1645,13 @@
CheckFeature(requested, supported, descriptorBindingVariableDescriptorCount) &&
CheckFeature(requested, supported, runtimeDescriptorArray);
}
+
+bool PhysicalDevice::hasExtendedFeatures(const VkPhysicalDevicePipelineRobustnessFeaturesEXT *requested) const
+{
+ auto supported = getSupportedFeatures(requested);
+
+ return CheckFeature(requested, supported, pipelineRobustness);
+}
#undef CheckFeature
void PhysicalDevice::GetFormatProperties(Format format, VkFormatProperties *pFormatProperties)
diff --git a/src/Vulkan/VkPhysicalDevice.hpp b/src/Vulkan/VkPhysicalDevice.hpp
index 21a519d..40acb1f 100644
--- a/src/Vulkan/VkPhysicalDevice.hpp
+++ b/src/Vulkan/VkPhysicalDevice.hpp
@@ -56,6 +56,7 @@
bool hasExtendedFeatures(const VkPhysicalDeviceZeroInitializeWorkgroupMemoryFeatures *requested) const;
bool hasExtendedFeatures(const VkPhysicalDevicePrimitiveTopologyListRestartFeaturesEXT *requested) const;
bool hasExtendedFeatures(const VkPhysicalDeviceDescriptorIndexingFeatures *requested) const;
+ bool hasExtendedFeatures(const VkPhysicalDevicePipelineRobustnessFeaturesEXT *requested) const;
const VkPhysicalDeviceProperties &getProperties() const;
void getProperties(VkPhysicalDeviceIDProperties *properties) const;
@@ -82,8 +83,6 @@
void getProperties(VkPhysicalDeviceFloatControlsProperties *) const;
void getProperties(VkPhysicalDeviceSamplerFilterMinmaxProperties *properties) const;
void getProperties(VkPhysicalDeviceTimelineSemaphoreProperties *properties) const;
- void getProperties(VkPhysicalDeviceVulkan12Properties *properties) const;
- void getProperties(VkPhysicalDeviceVulkan13Properties *properties) const;
void getProperties(VkPhysicalDeviceDescriptorIndexingProperties *properties) const;
void getProperties(VkPhysicalDeviceDepthStencilResolveProperties *properties) const;
void getProperties(VkPhysicalDeviceCustomBorderColorPropertiesEXT *properties) const;
@@ -92,7 +91,10 @@
void getProperties(VkPhysicalDeviceInlineUniformBlockProperties *properties) const;
void getProperties(VkPhysicalDeviceTexelBufferAlignmentProperties *properties) const;
void getProperties(VkPhysicalDeviceShaderIntegerDotProductProperties *properties) const;
+ void getProperties(VkPhysicalDevicePipelineRobustnessPropertiesEXT *properties) const;
void getProperties(VkPhysicalDeviceVulkan11Properties *properties) const;
+ void getProperties(VkPhysicalDeviceVulkan12Properties *properties) const;
+ void getProperties(VkPhysicalDeviceVulkan13Properties *properties) const;
static void GetFormatProperties(Format format, VkFormatProperties *pFormatProperties);
static void GetFormatProperties(Format format, VkFormatProperties3 *pFormatProperties);
diff --git a/src/Vulkan/VkPipeline.cpp b/src/Vulkan/VkPipeline.cpp
index 66a3c21..07701c5 100644
--- a/src/Vulkan/VkPipeline.cpp
+++ b/src/Vulkan/VkPipeline.cpp
@@ -240,14 +240,85 @@
const VkPipelineCreationFeedbackCreateInfo *pipelineCreationFeedback = nullptr;
};
+bool getRobustBufferAccess(VkPipelineRobustnessBufferBehaviorEXT behavior, bool inheritRobustBufferAccess)
+{
+ // Based on behavior:
+ // - <not provided>:
+ // * For pipelines, use device's robustBufferAccess
+ // * For shaders, use pipeline's robustBufferAccess
+ // Note that pipeline's robustBufferAccess is already set to device's if not overriden.
+ // - Default: Use device's robustBufferAccess
+ // - Disabled / Enabled: Override to disabled or enabled
+ //
+ // This function is passed "DEFAULT" when override is not provided, and
+ // inheritRobustBufferAccess is appropriately set to the device or pipeline's
+ // robustBufferAccess
+ switch(behavior)
+ {
+ case VK_PIPELINE_ROBUSTNESS_BUFFER_BEHAVIOR_DEVICE_DEFAULT_EXT:
+ return inheritRobustBufferAccess;
+ case VK_PIPELINE_ROBUSTNESS_BUFFER_BEHAVIOR_DISABLED_EXT:
+ return false;
+ case VK_PIPELINE_ROBUSTNESS_BUFFER_BEHAVIOR_ROBUST_BUFFER_ACCESS_EXT:
+ return true;
+ default:
+ UNSUPPORTED("Unsupported robustness behavior");
+ return true;
+ }
+}
+
+bool getRobustBufferAccess(const VkPipelineRobustnessCreateInfoEXT *overrideRobustness, bool deviceRobustBufferAccess, bool inheritRobustBufferAccess)
+{
+ VkPipelineRobustnessBufferBehaviorEXT storageBehavior = VK_PIPELINE_ROBUSTNESS_BUFFER_BEHAVIOR_DEVICE_DEFAULT_EXT;
+ VkPipelineRobustnessBufferBehaviorEXT uniformBehavior = VK_PIPELINE_ROBUSTNESS_BUFFER_BEHAVIOR_DEVICE_DEFAULT_EXT;
+ VkPipelineRobustnessBufferBehaviorEXT vertexBehavior = VK_PIPELINE_ROBUSTNESS_BUFFER_BEHAVIOR_DEVICE_DEFAULT_EXT;
+
+ if(overrideRobustness)
+ {
+ storageBehavior = overrideRobustness->storageBuffers;
+ uniformBehavior = overrideRobustness->uniformBuffers;
+ vertexBehavior = overrideRobustness->vertexInputs;
+ }
+ else
+ {
+ inheritRobustBufferAccess = deviceRobustBufferAccess;
+ }
+
+ bool storageRobustBufferAccess = getRobustBufferAccess(storageBehavior, inheritRobustBufferAccess);
+ bool uniformRobustBufferAccess = getRobustBufferAccess(uniformBehavior, inheritRobustBufferAccess);
+ bool vertexRobustBufferAccess = getRobustBufferAccess(vertexBehavior, inheritRobustBufferAccess);
+
+ // Note: in the initial implementation, enabling robust access for any buffer enables it for
+ // all. TODO(b/185122256) split robustBufferAccess in the pipeline and shaders into three
+ // categories and provide robustness for storage, uniform and vertex buffers accordingly.
+ return storageRobustBufferAccess || uniformRobustBufferAccess || vertexRobustBufferAccess;
+}
+
+bool getPipelineRobustBufferAccess(const void *pNext, vk::Device *device)
+{
+ const VkPipelineRobustnessCreateInfoEXT *overrideRobustness = vk::GetExtendedStruct<VkPipelineRobustnessCreateInfoEXT>(pNext, VK_STRUCTURE_TYPE_PIPELINE_ROBUSTNESS_CREATE_INFO_EXT);
+ const bool deviceRobustBufferAccess = device->getEnabledFeatures().robustBufferAccess;
+
+ // For pipelines, there's no robustBufferAccess to inherit from. Default and no-override
+ // both lead to using the device's robustBufferAccess.
+ return getRobustBufferAccess(overrideRobustness, deviceRobustBufferAccess, deviceRobustBufferAccess);
+}
+
+bool getPipelineStageRobustBufferAccess(const void *pNext, vk::Device *device, bool pipelineRobustBufferAccess)
+{
+ const VkPipelineRobustnessCreateInfoEXT *overrideRobustness = vk::GetExtendedStruct<VkPipelineRobustnessCreateInfoEXT>(pNext, VK_STRUCTURE_TYPE_PIPELINE_ROBUSTNESS_CREATE_INFO_EXT);
+ const bool deviceRobustBufferAccess = device->getEnabledFeatures().robustBufferAccess;
+
+ return getRobustBufferAccess(overrideRobustness, deviceRobustBufferAccess, pipelineRobustBufferAccess);
+}
+
} // anonymous namespace
namespace vk {
-
-Pipeline::Pipeline(PipelineLayout *layout, Device *device)
+Pipeline::Pipeline(PipelineLayout *layout, Device *device, bool robustBufferAccess)
: layout(layout)
, device(device)
- , robustBufferAccess(device->getEnabledFeatures().robustBufferAccess)
+ , robustBufferAccess(robustBufferAccess)
{
layout->incRefCount();
}
@@ -260,8 +331,8 @@
}
GraphicsPipeline::GraphicsPipeline(const VkGraphicsPipelineCreateInfo *pCreateInfo, void *mem, Device *device)
- : Pipeline(vk::Cast(pCreateInfo->layout), device)
- , state(device, pCreateInfo, layout, robustBufferAccess)
+ : Pipeline(vk::Cast(pCreateInfo->layout), device, getPipelineRobustBufferAccess(pCreateInfo->pNext, device))
+ , state(device, pCreateInfo, layout)
, inputs(pCreateInfo->pVertexInputState)
{
}
@@ -376,9 +447,11 @@
}
}
+ const bool stageRobustBufferAccess = getPipelineStageRobustBufferAccess(stageInfo.pNext, device, robustBufferAccess);
+
// TODO(b/201798871): use allocator.
auto shader = std::make_shared<sw::SpirvShader>(stageInfo.stage, stageInfo.pName, spirv,
- vk::Cast(pCreateInfo->renderPass), pCreateInfo->subpass, robustBufferAccess, dbgctx, getOrCreateSpirvProfiler());
+ vk::Cast(pCreateInfo->renderPass), pCreateInfo->subpass, stageRobustBufferAccess, dbgctx, getOrCreateSpirvProfiler());
setShader(stageInfo.stage, shader);
@@ -389,7 +462,7 @@
}
ComputePipeline::ComputePipeline(const VkComputePipelineCreateInfo *pCreateInfo, void *mem, Device *device)
- : Pipeline(vk::Cast(pCreateInfo->layout), device)
+ : Pipeline(vk::Cast(pCreateInfo->layout), device, getPipelineRobustBufferAccess(pCreateInfo->pNext, device))
{
}
@@ -450,9 +523,11 @@
}
}
+ const bool stageRobustBufferAccess = getPipelineStageRobustBufferAccess(stage.pNext, device, robustBufferAccess);
+
// TODO(b/201798871): use allocator.
shader = std::make_shared<sw::SpirvShader>(stage.stage, stage.pName, spirv,
- nullptr, 0, robustBufferAccess, dbgctx, getOrCreateSpirvProfiler());
+ nullptr, 0, stageRobustBufferAccess, dbgctx, getOrCreateSpirvProfiler());
const PipelineCache::ComputeProgramKey programKey(shader->getIdentifier(), layout->identifier);
diff --git a/src/Vulkan/VkPipeline.hpp b/src/Vulkan/VkPipeline.hpp
index 39e605d..153329b 100644
--- a/src/Vulkan/VkPipeline.hpp
+++ b/src/Vulkan/VkPipeline.hpp
@@ -37,7 +37,7 @@
class Pipeline
{
public:
- Pipeline(PipelineLayout *layout, Device *device);
+ Pipeline(PipelineLayout *layout, Device *device, bool robustBufferAccess);
virtual ~Pipeline() = default;
operator VkPipeline()
diff --git a/src/Vulkan/libVulkan.cpp b/src/Vulkan/libVulkan.cpp
index 43fb688..c1b8e63 100644
--- a/src/Vulkan/libVulkan.cpp
+++ b/src/Vulkan/libVulkan.cpp
@@ -424,6 +424,7 @@
{ { VK_EXT_BLEND_OPERATION_ADVANCED_EXTENSION_NAME, VK_EXT_BLEND_OPERATION_ADVANCED_SPEC_VERSION } },
// Used by ANGLE to implement triangle/etc list restarts as possible in OpenGL
{ { VK_EXT_PRIMITIVE_TOPOLOGY_LIST_RESTART_EXTENSION_NAME, VK_EXT_PRIMITIVE_TOPOLOGY_LIST_RESTART_SPEC_VERSION } },
+ { { VK_EXT_PIPELINE_ROBUSTNESS_EXTENSION_NAME, VK_EXT_PIPELINE_ROBUSTNESS_SPEC_VERSION } },
{ { VK_EXT_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_EXTENSION_NAME, VK_EXT_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_SPEC_VERSION } }
};
@@ -1064,7 +1065,7 @@
}
}
break;
- // These structs are supported, but no behavior changes based on their feature bools
+ // These structs are supported, but no behavior changes based on their feature flags
case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_UNIFORM_BUFFER_STANDARD_LAYOUT_FEATURES:
case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SUBGROUP_EXTENDED_TYPES_FEATURES:
case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_4444_FORMATS_FEATURES_EXT:
@@ -1073,6 +1074,7 @@
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:
+ case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PIPELINE_ROBUSTNESS_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)
@@ -3685,6 +3687,12 @@
vk::Cast(physicalDevice)->getProperties(properties);
}
break;
+ case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PIPELINE_ROBUSTNESS_PROPERTIES_EXT:
+ {
+ auto properties = reinterpret_cast<VkPhysicalDevicePipelineRobustnessPropertiesEXT *>(extensionProperties);
+ vk::Cast(physicalDevice)->getProperties(properties);
+ }
+ break;
default:
// "the [driver] must skip over, without processing (other than reading the sType and pNext members) any structures in the chain with sType values not defined by [supported extenions]"
UNSUPPORTED("pProperties->pNext sType = %s", vk::Stringify(extensionProperties->sType).c_str());