Commit cda86eff by Nicolas Capens Committed by Nicolas Capens

Implement shaderStorageImageMultisample support

This fixes a bug in the descriptor data where the sample pitch was set to the layer pitch for array images (it should always be the slice pitch), and the descriptor's slice pitch (which stores the layer pitch in case of array images), was taking the sample count into account twice. Note that while the image robustness extensions still allow read operations to return undefined values "if only the sample index was invalid", so clamping that value would suffice (as currently done in our OpImageFetch implementation), write operations always have to be no-op. This change also 'nullifies' the OpImageRead instructions for invalid sample indices. Bug: b/163142358 Tests: dEQP-VK.* Change-Id: I9d816c249e14a17722d29f56015c8dc8cc7d51d4 Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/47548 Presubmit-Ready: Nicolas Capens <nicolascapens@google.com> Kokoro-Result: kokoro <noreply+kokoro@google.com> Reviewed-by: 's avatarAlexis Hétu <sugoi@google.com> Tested-by: 's avatarNicolas Capens <nicolascapens@google.com>
parent c4f3b713
...@@ -361,15 +361,17 @@ SpirvShader::SpirvShader( ...@@ -361,15 +361,17 @@ SpirvShader::SpirvShader(
{ {
case spv::CapabilityMatrix: capabilities.Matrix = true; break; case spv::CapabilityMatrix: capabilities.Matrix = true; break;
case spv::CapabilityShader: capabilities.Shader = true; break; case spv::CapabilityShader: capabilities.Shader = true; break;
case spv::CapabilityStorageImageMultisample: capabilities.StorageImageMultisample = true; break;
case spv::CapabilityClipDistance: capabilities.ClipDistance = true; break; case spv::CapabilityClipDistance: capabilities.ClipDistance = true; break;
case spv::CapabilityCullDistance: capabilities.CullDistance = true; break; case spv::CapabilityCullDistance: capabilities.CullDistance = true; break;
case spv::CapabilityImageCubeArray: capabilities.ImageCubeArray = true; break;
case spv::CapabilityInputAttachment: capabilities.InputAttachment = true; break; case spv::CapabilityInputAttachment: capabilities.InputAttachment = true; break;
case spv::CapabilitySampled1D: capabilities.Sampled1D = true; break; case spv::CapabilitySampled1D: capabilities.Sampled1D = true; break;
case spv::CapabilityImage1D: capabilities.Image1D = true; break; case spv::CapabilityImage1D: capabilities.Image1D = true; break;
case spv::CapabilityImageCubeArray: capabilities.ImageCubeArray = true; break;
case spv::CapabilitySampledBuffer: capabilities.SampledBuffer = true; break; case spv::CapabilitySampledBuffer: capabilities.SampledBuffer = true; break;
case spv::CapabilitySampledCubeArray: capabilities.SampledCubeArray = true; break; case spv::CapabilitySampledCubeArray: capabilities.SampledCubeArray = true; break;
case spv::CapabilityImageBuffer: capabilities.ImageBuffer = true; break; case spv::CapabilityImageBuffer: capabilities.ImageBuffer = true; break;
case spv::CapabilityImageMSArray: capabilities.ImageMSArray = true; break;
case spv::CapabilityStorageImageExtendedFormats: capabilities.StorageImageExtendedFormats = true; break; case spv::CapabilityStorageImageExtendedFormats: capabilities.StorageImageExtendedFormats = true; break;
case spv::CapabilityImageQuery: capabilities.ImageQuery = true; break; case spv::CapabilityImageQuery: capabilities.ImageQuery = true; break;
case spv::CapabilityDerivativeControl: capabilities.DerivativeControl = true; break; case spv::CapabilityDerivativeControl: capabilities.DerivativeControl = true; break;
......
...@@ -569,15 +569,17 @@ public: ...@@ -569,15 +569,17 @@ public:
{ {
bool Matrix : 1; bool Matrix : 1;
bool Shader : 1; bool Shader : 1;
bool StorageImageMultisample : 1;
bool ClipDistance : 1; bool ClipDistance : 1;
bool CullDistance : 1; bool CullDistance : 1;
bool ImageCubeArray : 1;
bool InputAttachment : 1; bool InputAttachment : 1;
bool Sampled1D : 1; bool Sampled1D : 1;
bool Image1D : 1; bool Image1D : 1;
bool ImageCubeArray : 1;
bool SampledBuffer : 1; bool SampledBuffer : 1;
bool SampledCubeArray : 1; bool SampledCubeArray : 1;
bool ImageBuffer : 1; bool ImageBuffer : 1;
bool ImageMSArray : 1;
bool StorageImageExtendedFormats : 1; bool StorageImageExtendedFormats : 1;
bool ImageQuery : 1; bool ImageQuery : 1;
bool DerivativeControl : 1; bool DerivativeControl : 1;
......
...@@ -115,7 +115,7 @@ SpirvShader::EmitResult SpirvShader::EmitImageSampleExplicitLod(Variant variant, ...@@ -115,7 +115,7 @@ SpirvShader::EmitResult SpirvShader::EmitImageSampleExplicitLod(Variant variant,
return EmitImageSample({ variant, Grad }, insn, state); return EmitImageSample({ variant, Grad }, insn, state);
} }
else else
UNSUPPORTED("Image Operands %x", imageOperands); UNSUPPORTED("Image operands 0x%08X", imageOperands);
return EmitResult::Continue; return EmitResult::Continue;
} }
...@@ -234,7 +234,7 @@ void SpirvShader::EmitImageSampleUnconditional(Array<SIMD::Float> &out, ImageIns ...@@ -234,7 +234,7 @@ void SpirvShader::EmitImageSampleUnconditional(Array<SIMD::Float> &out, ImageIns
if(imageOperands != 0) if(imageOperands != 0)
{ {
UNSUPPORTED("Image operand %x", imageOperands); UNSUPPORTED("Image operands 0x%08X", imageOperands);
} }
} }
...@@ -558,10 +558,12 @@ SIMD::Pointer SpirvShader::GetTexelAddress(EmitState const *state, Pointer<Byte> ...@@ -558,10 +558,12 @@ SIMD::Pointer SpirvShader::GetTexelAddress(EmitState const *state, Pointer<Byte>
ptrOffset += SIMD::Int(routine->viewID) * slicePitch; ptrOffset += SIMD::Int(routine->viewID) * slicePitch;
} }
SIMD::Int n = 0;
if(sampleId.value()) if(sampleId.value())
{ {
Operand sample(this, state, sampleId); Operand sample(this, state, sampleId);
ptrOffset += sample.Int(0) * samplePitch; n = sample.Int(0);
ptrOffset += n * samplePitch;
} }
// If the out-of-bounds behavior is set to nullify, then each coordinate must be tested individually. // If the out-of-bounds behavior is set to nullify, then each coordinate must be tested individually.
...@@ -581,7 +583,13 @@ SIMD::Pointer SpirvShader::GetTexelAddress(EmitState const *state, Pointer<Byte> ...@@ -581,7 +583,13 @@ SIMD::Pointer SpirvShader::GetTexelAddress(EmitState const *state, Pointer<Byte>
{ {
auto depth = *Pointer<UInt>(descriptor + OFFSET(vk::StorageImageDescriptor, extent.depth)); auto depth = *Pointer<UInt>(descriptor + OFFSET(vk::StorageImageDescriptor, extent.depth));
auto arrayLayers = *Pointer<UInt>(descriptor + OFFSET(vk::StorageImageDescriptor, arrayLayers)); auto arrayLayers = *Pointer<UInt>(descriptor + OFFSET(vk::StorageImageDescriptor, arrayLayers));
oobMask |= As<SIMD::Int>(CmpNLT(As<SIMD::UInt>(w), SIMD::UInt(depth * arrayLayers))); oobMask |= As<SIMD::Int>(CmpNLT(As<SIMD::UInt>(w), SIMD::UInt(depth * arrayLayers))); // TODO: Precompute extent. 3D image can't have layers.
}
if(sampleId.value())
{
SIMD::UInt sampleCount = *Pointer<UInt>(descriptor + OFFSET(vk::StorageImageDescriptor, sampleCount));
oobMask |= As<SIMD::Int>(CmpNLT(As<SIMD::UInt>(n), sampleCount));
} }
constexpr int32_t OOB_OFFSET = 0x7FFFFFFF - 16; // SIMD pointer offsets are signed 32-bit, so this is the largest offset (for 16-byte texels). constexpr int32_t OOB_OFFSET = 0x7FFFFFFF - 16; // SIMD pointer offsets are signed 32-bit, so this is the largest offset (for 16-byte texels).
...@@ -605,7 +613,7 @@ SpirvShader::EmitResult SpirvShader::EmitImageRead(InsnIterator insn, EmitState ...@@ -605,7 +613,7 @@ SpirvShader::EmitResult SpirvShader::EmitImageRead(InsnIterator insn, EmitState
if(insn.wordCount() > 5) if(insn.wordCount() > 5)
{ {
int operand = 6; int operand = 6;
auto imageOperands = insn.word(5); uint32_t imageOperands = insn.word(5);
if(imageOperands & spv::ImageOperandsSampleMask) if(imageOperands & spv::ImageOperandsSampleMask)
{ {
sampleId = insn.word(operand++); sampleId = insn.word(operand++);
...@@ -613,7 +621,10 @@ SpirvShader::EmitResult SpirvShader::EmitImageRead(InsnIterator insn, EmitState ...@@ -613,7 +621,10 @@ SpirvShader::EmitResult SpirvShader::EmitImageRead(InsnIterator insn, EmitState
} }
// Should be no remaining image operands. // Should be no remaining image operands.
ASSERT(!imageOperands); if(imageOperands != 0)
{
UNSUPPORTED("Image operands 0x%08X", imageOperands);
}
} }
ASSERT(imageType.definition.opcode() == spv::OpTypeImage); ASSERT(imageType.definition.opcode() == spv::OpTypeImage);
...@@ -990,8 +1001,24 @@ SpirvShader::EmitResult SpirvShader::EmitImageWrite(InsnIterator insn, EmitState ...@@ -990,8 +1001,24 @@ SpirvShader::EmitResult SpirvShader::EmitImageWrite(InsnIterator insn, EmitState
ASSERT(imageType.definition.opcode() == spv::OpTypeImage); ASSERT(imageType.definition.opcode() == spv::OpTypeImage);
// TODO(b/131171141): Not handling any image operands yet. Object::ID sampleId = 0;
ASSERT(insn.wordCount() == 4);
if(insn.wordCount() > 4)
{
int operand = 5;
uint32_t imageOperands = insn.word(4);
if(imageOperands & spv::ImageOperandsSampleMask)
{
sampleId = insn.word(operand++);
imageOperands &= ~spv::ImageOperandsSampleMask;
}
// Should be no remaining image operands.
if(imageOperands != 0)
{
UNSUPPORTED("Image operands 0x%08X", (int)imageOperands);
}
}
auto coordinate = Operand(this, state, insn.word(2)); auto coordinate = Operand(this, state, insn.word(2));
auto texel = Operand(this, state, insn.word(3)); auto texel = Operand(this, state, insn.word(3));
...@@ -1176,7 +1203,7 @@ SpirvShader::EmitResult SpirvShader::EmitImageWrite(InsnIterator insn, EmitState ...@@ -1176,7 +1203,7 @@ SpirvShader::EmitResult SpirvShader::EmitImageWrite(InsnIterator insn, EmitState
// - https://www.khronos.org/registry/vulkan/specs/1.2/html/chap16.html#textures-output-coordinate-validation // - https://www.khronos.org/registry/vulkan/specs/1.2/html/chap16.html#textures-output-coordinate-validation
auto robustness = OutOfBoundsBehavior::Nullify; auto robustness = OutOfBoundsBehavior::Nullify;
auto texelPtr = GetTexelAddress(state, imageBase, imageSizeInBytes, coordinate, imageType, binding, texelSize, 0, false, robustness); auto texelPtr = GetTexelAddress(state, imageBase, imageSizeInBytes, coordinate, imageType, binding, texelSize, sampleId, false, robustness);
// Scatter packed texel data. // Scatter packed texel data.
// TODO(b/160531165): Provide scatter abstractions for various element sizes. // TODO(b/160531165): Provide scatter abstractions for various element sizes.
...@@ -1235,6 +1262,7 @@ SpirvShader::EmitResult SpirvShader::EmitImageTexelPointer(InsnIterator insn, Em ...@@ -1235,6 +1262,7 @@ SpirvShader::EmitResult SpirvShader::EmitImageTexelPointer(InsnIterator insn, Em
ASSERT(getType(resultType.element).opcode() == spv::OpTypeInt); ASSERT(getType(resultType.element).opcode() == spv::OpTypeInt);
auto coordinate = Operand(this, state, insn.word(4)); auto coordinate = Operand(this, state, insn.word(4));
Object::ID sampleId = insn.word(5);
Pointer<Byte> binding = state->getPointer(imageId).base; Pointer<Byte> binding = state->getPointer(imageId).base;
Pointer<Byte> imageBase = *Pointer<Pointer<Byte>>(binding + OFFSET(vk::StorageImageDescriptor, ptr)); Pointer<Byte> imageBase = *Pointer<Pointer<Byte>>(binding + OFFSET(vk::StorageImageDescriptor, ptr));
...@@ -1244,7 +1272,7 @@ SpirvShader::EmitResult SpirvShader::EmitImageTexelPointer(InsnIterator insn, Em ...@@ -1244,7 +1272,7 @@ SpirvShader::EmitResult SpirvShader::EmitImageTexelPointer(InsnIterator insn, Em
// TODO(b/162327166): Only perform bounds checks when VK_EXT_image_robustness is enabled. // TODO(b/162327166): Only perform bounds checks when VK_EXT_image_robustness is enabled.
auto robustness = OutOfBoundsBehavior::Nullify; auto robustness = OutOfBoundsBehavior::Nullify;
auto ptr = GetTexelAddress(state, imageBase, imageSizeInBytes, coordinate, imageType, binding, sizeof(uint32_t), 0, false, robustness); auto ptr = GetTexelAddress(state, imageBase, imageSizeInBytes, coordinate, imageType, binding, sizeof(uint32_t), sampleId, false, robustness);
state->createPointer(resultId, ptr); state->createPointer(resultId, ptr);
......
...@@ -435,10 +435,10 @@ void DescriptorSetLayout::WriteDescriptorSet(Device *device, DescriptorSet *dstS ...@@ -435,10 +435,10 @@ void DescriptorSetLayout::WriteDescriptorSet(Device *device, DescriptorSet *dstS
descriptor[i].ptr = imageView->getOffsetPointer({ 0, 0, 0 }, VK_IMAGE_ASPECT_COLOR_BIT, 0, 0); descriptor[i].ptr = imageView->getOffsetPointer({ 0, 0, 0 }, VK_IMAGE_ASPECT_COLOR_BIT, 0, 0);
descriptor[i].extent = imageView->getMipLevelExtent(0); descriptor[i].extent = imageView->getMipLevelExtent(0);
descriptor[i].rowPitchBytes = imageView->rowPitchBytes(VK_IMAGE_ASPECT_COLOR_BIT, 0); descriptor[i].rowPitchBytes = imageView->rowPitchBytes(VK_IMAGE_ASPECT_COLOR_BIT, 0);
descriptor[i].samplePitchBytes = imageView->getSubresourceRange().layerCount > 1 descriptor[i].samplePitchBytes = imageView->slicePitchBytes(VK_IMAGE_ASPECT_COLOR_BIT, 0);
descriptor[i].slicePitchBytes = imageView->getSubresourceRange().layerCount > 1
? imageView->layerPitchBytes(VK_IMAGE_ASPECT_COLOR_BIT) ? imageView->layerPitchBytes(VK_IMAGE_ASPECT_COLOR_BIT)
: imageView->slicePitchBytes(VK_IMAGE_ASPECT_COLOR_BIT, 0); : imageView->slicePitchBytes(VK_IMAGE_ASPECT_COLOR_BIT, 0);
descriptor[i].slicePitchBytes = descriptor[i].samplePitchBytes * imageView->getSampleCount();
descriptor[i].arrayLayers = imageView->getSubresourceRange().layerCount; descriptor[i].arrayLayers = imageView->getSubresourceRange().layerCount;
descriptor[i].sampleCount = imageView->getSampleCount(); descriptor[i].sampleCount = imageView->getSampleCount();
descriptor[i].sizeInBytes = static_cast<int>(imageView->getSizeInBytes()); descriptor[i].sizeInBytes = static_cast<int>(imageView->getSizeInBytes());
...@@ -448,10 +448,10 @@ void DescriptorSetLayout::WriteDescriptorSet(Device *device, DescriptorSet *dstS ...@@ -448,10 +448,10 @@ void DescriptorSetLayout::WriteDescriptorSet(Device *device, DescriptorSet *dstS
{ {
descriptor[i].stencilPtr = imageView->getOffsetPointer({ 0, 0, 0 }, VK_IMAGE_ASPECT_STENCIL_BIT, 0, 0); descriptor[i].stencilPtr = imageView->getOffsetPointer({ 0, 0, 0 }, VK_IMAGE_ASPECT_STENCIL_BIT, 0, 0);
descriptor[i].stencilRowPitchBytes = imageView->rowPitchBytes(VK_IMAGE_ASPECT_STENCIL_BIT, 0); descriptor[i].stencilRowPitchBytes = imageView->rowPitchBytes(VK_IMAGE_ASPECT_STENCIL_BIT, 0);
descriptor[i].stencilSamplePitchBytes = (imageView->getSubresourceRange().layerCount > 1) descriptor[i].stencilSamplePitchBytes = imageView->slicePitchBytes(VK_IMAGE_ASPECT_STENCIL_BIT, 0);
descriptor[i].stencilSlicePitchBytes = (imageView->getSubresourceRange().layerCount > 1)
? imageView->layerPitchBytes(VK_IMAGE_ASPECT_STENCIL_BIT) ? imageView->layerPitchBytes(VK_IMAGE_ASPECT_STENCIL_BIT)
: imageView->slicePitchBytes(VK_IMAGE_ASPECT_STENCIL_BIT, 0); : imageView->slicePitchBytes(VK_IMAGE_ASPECT_STENCIL_BIT, 0);
descriptor[i].stencilSlicePitchBytes = descriptor[i].stencilSamplePitchBytes * imageView->getSampleCount();
} }
} }
} }
......
...@@ -59,7 +59,7 @@ struct alignas(16) StorageImageDescriptor ...@@ -59,7 +59,7 @@ struct alignas(16) StorageImageDescriptor
void *ptr; void *ptr;
VkExtent3D extent; VkExtent3D extent;
int rowPitchBytes; int rowPitchBytes;
int slicePitchBytes; int slicePitchBytes; // Layer pitch in case of array image
int samplePitchBytes; int samplePitchBytes;
int arrayLayers; int arrayLayers;
int sampleCount; int sampleCount;
...@@ -67,7 +67,7 @@ struct alignas(16) StorageImageDescriptor ...@@ -67,7 +67,7 @@ struct alignas(16) StorageImageDescriptor
void *stencilPtr; void *stencilPtr;
int stencilRowPitchBytes; int stencilRowPitchBytes;
int stencilSlicePitchBytes; int stencilSlicePitchBytes; // Layer pitch in case of array image
int stencilSamplePitchBytes; int stencilSamplePitchBytes;
ImageView *memoryOwner; // Pointer to the view which owns the memory used by the descriptor set ImageView *memoryOwner; // Pointer to the view which owns the memory used by the descriptor set
......
...@@ -98,7 +98,7 @@ const VkPhysicalDeviceFeatures &PhysicalDevice::getFeatures() const ...@@ -98,7 +98,7 @@ const VkPhysicalDeviceFeatures &PhysicalDevice::getFeatures() const
VK_FALSE, // shaderTessellationAndGeometryPointSize VK_FALSE, // shaderTessellationAndGeometryPointSize
VK_FALSE, // shaderImageGatherExtended VK_FALSE, // shaderImageGatherExtended
VK_TRUE, // shaderStorageImageExtendedFormats VK_TRUE, // shaderStorageImageExtendedFormats
VK_FALSE, // shaderStorageImageMultisample VK_TRUE, // shaderStorageImageMultisample
VK_FALSE, // shaderStorageImageReadWithoutFormat VK_FALSE, // shaderStorageImageReadWithoutFormat
VK_FALSE, // shaderStorageImageWriteWithoutFormat VK_FALSE, // shaderStorageImageWriteWithoutFormat
VK_TRUE, // shaderUniformBufferArrayDynamicIndexing VK_TRUE, // shaderUniformBufferArrayDynamicIndexing
...@@ -295,7 +295,7 @@ const VkPhysicalDeviceLimits &PhysicalDevice::getLimits() const ...@@ -295,7 +295,7 @@ const VkPhysicalDeviceLimits &PhysicalDevice::getLimits() const
sampleCounts, // sampledImageIntegerSampleCounts sampleCounts, // sampledImageIntegerSampleCounts
sampleCounts, // sampledImageDepthSampleCounts sampleCounts, // sampledImageDepthSampleCounts
sampleCounts, // sampledImageStencilSampleCounts sampleCounts, // sampledImageStencilSampleCounts
VK_SAMPLE_COUNT_1_BIT, // storageImageSampleCounts (unsupported) sampleCounts, // storageImageSampleCounts
1, // maxSampleMaskWords 1, // maxSampleMaskWords
VK_FALSE, // timestampComputeAndGraphics VK_FALSE, // timestampComputeAndGraphics
60, // timestampPeriod 60, // timestampPeriod
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment