Implement VK_KHR_vulkan_memory_model
This is largely a no-op because CPUs typically have fully coherent
cache hierarchies (including on ccNUMA systems). Thus every write is
implicitly 'visible'. Atomic operations with memory-order semantics
likewise have system-wide 'scope'. And we don't support spare images yet
so 'availability' operations don't do anything.
Note that cache coherency would be lost if we were to use non-temporal
store instructions (e.g. MOVNTPS). A 'visibility' operation would
require inserting an SFENCE instruction.
The `VolatileTexel` image operand signifies that "This access cannot be
eliminated, duplicated, or combined with other accesses." There doesn't
appear to be dEQP-VK test coverage for it at this time, which makes it
non-trivial to optimally design how to handle it. It is therefore not
implemented at this time.
Bug: b/176819536
Tests: dEQP-VK.memory_model.*
Change-Id: Ifa97eed5a4506eef8ba77b8fb2042a0b1a624db5
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/56209
Kokoro-Result: kokoro <noreply+kokoro@google.com>
Reviewed-by: Alexis Hétu <sugoi@google.com>
Tested-by: Nicolas Capens <nicolascapens@google.com>
Commit-Queue: Nicolas Capens <nicolascapens@google.com>
diff --git a/src/Pipeline/SpirvShader.cpp b/src/Pipeline/SpirvShader.cpp
index 6a0134f..264a471 100644
--- a/src/Pipeline/SpirvShader.cpp
+++ b/src/Pipeline/SpirvShader.cpp
@@ -419,6 +419,8 @@
case spv::CapabilityDeviceGroup: capabilities.DeviceGroup = true; break;
case spv::CapabilityMultiView: capabilities.MultiView = true; break;
case spv::CapabilityStencilExportEXT: capabilities.StencilExportEXT = true; break;
+ case spv::CapabilityVulkanMemoryModel: capabilities.VulkanMemoryModel = true; break;
+ case spv::CapabilityVulkanMemoryModelDeviceScope: capabilities.VulkanMemoryModelDeviceScope = true; break;
default:
UNSUPPORTED("Unsupported capability %u", insn.word(1));
}
@@ -428,7 +430,11 @@
break;
case spv::OpMemoryModel:
- break; // Memory model does not affect our code generation until we decide to do Vulkan Memory Model support.
+ {
+ addressingModel = static_cast<spv::AddressingModel>(insn.word(1));
+ memoryModel = static_cast<spv::MemoryModel>(insn.word(2));
+ }
+ break;
case spv::OpFunction:
{
@@ -745,7 +751,7 @@
case spv::OpExtension:
{
- auto ext = insn.string(1);
+ const char *ext = insn.string(1);
// Part of core SPIR-V 1.3. Vulkan 1.1 implementations must also accept the pre-1.3
// extension per Appendix A, `Vulkan Environment for SPIR-V`.
if(!strcmp(ext, "SPV_KHR_storage_buffer_storage_class")) break;
@@ -756,6 +762,7 @@
if(!strcmp(ext, "SPV_KHR_multiview")) break;
if(!strcmp(ext, "SPV_EXT_shader_stencil_export")) break;
if(!strcmp(ext, "SPV_KHR_float_controls")) break;
+ if(!strcmp(ext, "SPV_KHR_vulkan_memory_model")) break;
UNSUPPORTED("SPIR-V Extension: %s", ext);
}
break;
diff --git a/src/Pipeline/SpirvShader.hpp b/src/Pipeline/SpirvShader.hpp
index efdd11a..d549c0e 100644
--- a/src/Pipeline/SpirvShader.hpp
+++ b/src/Pipeline/SpirvShader.hpp
@@ -680,6 +680,8 @@
bool DeviceGroup : 1;
bool MultiView : 1;
bool StencilExportEXT : 1;
+ bool VulkanMemoryModel : 1;
+ bool VulkanMemoryModelDeviceScope : 1;
};
const Capabilities &getUsedCapabilities() const
@@ -885,17 +887,20 @@
Function::ID entryPoint;
spv::ExecutionModel executionModel = spv::ExecutionModelMax; // Invalid prior to OpEntryPoint parsing.
-
ExecutionModes executionModes = {};
- Analysis analysis = {};
Capabilities capabilities = {};
+ spv::AddressingModel addressingModel = spv::AddressingModelLogical;
+ spv::MemoryModel memoryModel = spv::MemoryModelSimple;
+ HandleMap<Extension> extensionsByID;
+ std::unordered_set<uint32_t> extensionsImported;
+
+ Analysis analysis = {};
+ mutable bool imageWriteEmitted = false;
+
HandleMap<Type> types;
HandleMap<Object> defs;
HandleMap<Function> functions;
std::unordered_map<StringID, String> strings;
- HandleMap<Extension> extensionsByID;
- std::unordered_set<uint32_t> extensionsImported;
- mutable bool imageWriteEmitted = false;
// DeclareType creates a Type for the given OpTypeX instruction, storing
// it into the types map. It is called from the analysis pass (constructor).
diff --git a/src/Pipeline/SpirvShaderControlFlow.cpp b/src/Pipeline/SpirvShaderControlFlow.cpp
index 98bb04c..2b55ca4 100644
--- a/src/Pipeline/SpirvShaderControlFlow.cpp
+++ b/src/Pipeline/SpirvShaderControlFlow.cpp
@@ -623,8 +623,8 @@
{
auto executionScope = spv::Scope(GetConstScalarInt(insn.word(1)));
auto semantics = spv::MemorySemanticsMask(GetConstScalarInt(insn.word(3)));
- // TODO: We probably want to consider the memory scope here. For now,
- // just always emit the full fence.
+ // TODO(b/176819536): We probably want to consider the memory scope here.
+ // For now, just always emit the full fence.
Fence(semantics);
switch(executionScope)
@@ -713,15 +713,6 @@
}
}
-void SpirvShader::Fence(spv::MemorySemanticsMask semantics) const
-{
- if(semantics == spv::MemorySemanticsMaskNone)
- {
- return; // no-op
- }
- rr::Fence(MemoryOrder(semantics));
-}
-
void SpirvShader::Yield(YieldResult res) const
{
rr::Yield(RValue<Int>(int(res)));
diff --git a/src/Pipeline/SpirvShaderImage.cpp b/src/Pipeline/SpirvShaderImage.cpp
index 68727b1..d66a1a2 100644
--- a/src/Pipeline/SpirvShaderImage.cpp
+++ b/src/Pipeline/SpirvShaderImage.cpp
@@ -225,6 +225,34 @@
imageOperands &= ~spv::ImageOperandsSignExtendMask;
}
+ [[maybe_unused]] spv::Scope scope = spv::ScopeCrossDevice; // "Whilst the CrossDevice scope is defined in SPIR-V, it is disallowed in Vulkan."
+
+ if(imageOperands & spv::ImageOperandsMakeTexelAvailableMask)
+ {
+ scope = static_cast<spv::Scope>(insn.word(operandsIndex));
+ operandsIndex += 1;
+ imageOperands &= ~spv::ImageOperandsMakeTexelAvailableMask;
+ }
+
+ if(imageOperands & spv::ImageOperandsMakeTexelVisibleMask)
+ {
+ scope = static_cast<spv::Scope>(insn.word(operandsIndex));
+ operandsIndex += 1;
+ imageOperands &= ~spv::ImageOperandsMakeTexelVisibleMask;
+ }
+
+ if(imageOperands & spv::ImageOperandsNonPrivateTexelMask)
+ {
+ imageOperands &= ~spv::ImageOperandsNonPrivateTexelMask;
+ }
+
+ if(imageOperands & spv::ImageOperandsVolatileTexelMask)
+ {
+ UNIMPLEMENTED("b/176819536");
+ imageOperands &= ~spv::ImageOperandsVolatileTexelMask;
+ }
+
+ // There should be no remaining image operands.
if(imageOperands != 0)
{
UNSUPPORTED("Image operands 0x%08X", imageOperands);
diff --git a/src/Pipeline/SpirvShaderMemory.cpp b/src/Pipeline/SpirvShaderMemory.cpp
index 18ed889..8439011 100644
--- a/src/Pipeline/SpirvShaderMemory.cpp
+++ b/src/Pipeline/SpirvShaderMemory.cpp
@@ -279,8 +279,8 @@
SpirvShader::EmitResult SpirvShader::EmitMemoryBarrier(InsnIterator insn, EmitState *state) const
{
auto semantics = spv::MemorySemanticsMask(GetConstScalarInt(insn.word(2)));
- // TODO: We probably want to consider the memory scope here. For now,
- // just always emit the full fence.
+ // TODO(b/176819536): We probably want to consider the memory scope here.
+ // For now, just always emit the full fence.
Fence(semantics);
return EmitResult::Continue;
}
@@ -421,13 +421,21 @@
}
}
+void SpirvShader::Fence(spv::MemorySemanticsMask semantics) const
+{
+ if(semantics != spv::MemorySemanticsMaskNone)
+ {
+ rr::Fence(MemoryOrder(semantics));
+ }
+}
+
std::memory_order SpirvShader::MemoryOrder(spv::MemorySemanticsMask memorySemantics)
{
- auto control = static_cast<uint32_t>(memorySemantics) & static_cast<uint32_t>(
- spv::MemorySemanticsAcquireMask |
- spv::MemorySemanticsReleaseMask |
- spv::MemorySemanticsAcquireReleaseMask |
- spv::MemorySemanticsSequentiallyConsistentMask);
+ uint32_t control = static_cast<uint32_t>(memorySemantics) & static_cast<uint32_t>(
+ spv::MemorySemanticsAcquireMask |
+ spv::MemorySemanticsReleaseMask |
+ spv::MemorySemanticsAcquireReleaseMask |
+ spv::MemorySemanticsSequentiallyConsistentMask);
switch(control)
{
case spv::MemorySemanticsMaskNone: return std::memory_order_relaxed;
@@ -437,7 +445,7 @@
case spv::MemorySemanticsSequentiallyConsistentMask: return std::memory_order_acq_rel; // Vulkan 1.1: "SequentiallyConsistent is treated as AcquireRelease"
default:
// "it is invalid for more than one of these four bits to be set:
- // Acquire, Release, AcquireRelease, or SequentiallyConsistent."
+ // Acquire, Release, AcquireRelease, or SequentiallyConsistent."
UNREACHABLE("MemorySemanticsMask: %x", int(control));
return std::memory_order_acq_rel;
}
diff --git a/src/Vulkan/VkPhysicalDevice.cpp b/src/Vulkan/VkPhysicalDevice.cpp
index 109e6aa..8169fed 100644
--- a/src/Vulkan/VkPhysicalDevice.cpp
+++ b/src/Vulkan/VkPhysicalDevice.cpp
@@ -268,9 +268,9 @@
template<typename T>
static void getPhysicalDeviceVulkanMemoryModelFeatures(T *features)
{
- features->vulkanMemoryModel = VK_FALSE;
- features->vulkanMemoryModelDeviceScope = VK_FALSE;
- features->vulkanMemoryModelAvailabilityVisibilityChains = VK_FALSE;
+ features->vulkanMemoryModel = VK_TRUE;
+ features->vulkanMemoryModelDeviceScope = VK_TRUE;
+ features->vulkanMemoryModelAvailabilityVisibilityChains = VK_TRUE;
}
template<typename T>
diff --git a/src/Vulkan/libVulkan.cpp b/src/Vulkan/libVulkan.cpp
index a925849..2f8da98 100644
--- a/src/Vulkan/libVulkan.cpp
+++ b/src/Vulkan/libVulkan.cpp
@@ -429,6 +429,7 @@
{ { VK_KHR_COPY_COMMANDS_2_EXTENSION_NAME, VK_KHR_COPY_COMMANDS_2_SPEC_VERSION } },
{ { VK_KHR_SWAPCHAIN_MUTABLE_FORMAT_EXTENSION_NAME, VK_KHR_SWAPCHAIN_MUTABLE_FORMAT_SPEC_VERSION } },
{ { VK_KHR_FORMAT_FEATURE_FLAGS_2_EXTENSION_NAME, VK_KHR_FORMAT_FEATURE_FLAGS_2_SPEC_VERSION } },
+ { { VK_KHR_VULKAN_MEMORY_MODEL_EXTENSION_NAME, VK_KHR_VULKAN_MEMORY_MODEL_SPEC_VERSION } },
};
static uint32_t numSupportedExtensions(const ExtensionProperties *extensionProperties, uint32_t extensionPropertiesCount)
@@ -940,6 +941,7 @@
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:
+ case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_MEMORY_MODEL_FEATURES:
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]"