Add support for OpImageWrite
Bug: b/130768731
Test: dEQP-VK.image.*
Change-Id: I63334478b014d36ed789604154797717159d4f37
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/29448
Reviewed-by: Nicolas Capens <nicolascapens@google.com>
Tested-by: Chris Forbes <chrisforbes@google.com>
Presubmit-Ready: Chris Forbes <chrisforbes@google.com>
Kokoro-Presubmit: kokoro <noreply+kokoro@google.com>
diff --git a/src/Pipeline/SpirvShader.cpp b/src/Pipeline/SpirvShader.cpp
index 188b647..682c7d6 100644
--- a/src/Pipeline/SpirvShader.cpp
+++ b/src/Pipeline/SpirvShader.cpp
@@ -687,6 +687,7 @@
case spv::OpStore:
case spv::OpAtomicStore:
+ case spv::OpImageWrite:
// Don't need to do anything during analysis pass
break;
@@ -2139,6 +2140,9 @@
case spv::OpImageQuerySize:
return EmitImageQuerySize(insn, state);
+ case spv::OpImageWrite:
+ return EmitImageWrite(insn, state);
+
default:
UNIMPLEMENTED("opcode: %s", OpcodeName(opcode).c_str());
break;
@@ -4404,6 +4408,126 @@
return EmitResult::Continue;
}
+ SpirvShader::EmitResult SpirvShader::EmitImageWrite(InsnIterator insn, EmitState *state) const
+ {
+ auto imageId = Object::ID(insn.word(1));
+ auto &image = getObject(imageId);
+ auto &imageType = getType(image.type);
+
+ ASSERT(imageType.definition.opcode() == spv::OpTypeImage);
+
+ // Not handling any image operands yet.
+ ASSERT(insn.wordCount() == 4);
+
+ const DescriptorDecorations &d = descriptorDecorations.at(imageId);
+ uint32_t arrayIndex = 0; // TODO(b/129523279)
+ auto setLayout = state->routine->pipelineLayout->getDescriptorSetLayout(d.DescriptorSet);
+ size_t bindingOffset = setLayout->getBindingOffset(d.Binding, arrayIndex);
+
+ auto coordinate = GenericValue(this, state->routine, insn.word(2));
+ auto texel = GenericValue(this, state->routine, insn.word(3));
+
+ Pointer<Byte> set = state->routine->descriptorSets[d.DescriptorSet]; // DescriptorSet*
+ Pointer<Byte> binding = Pointer<Byte>(set + bindingOffset); // StorageImageDescriptor*
+ Pointer<Byte> imageBase = *Pointer<Pointer<Byte>>(binding + OFFSET(vk::StorageImageDescriptor, ptr));
+
+ SIMD::Int packed[4];
+ auto numPackedElements = 0u;
+ int texelSize = 0;
+ auto format = static_cast<spv::ImageFormat>(imageType.definition.word(8));
+ switch (format)
+ {
+ case spv::ImageFormatRgba32f:
+ case spv::ImageFormatRgba32i:
+ case spv::ImageFormatRgba32ui:
+ texelSize = 16;
+ packed[0] = texel.Int(0);
+ packed[1] = texel.Int(1);
+ packed[2] = texel.Int(2);
+ packed[3] = texel.Int(3);
+ numPackedElements = 4;
+ break;
+ case spv::ImageFormatR32f:
+ case spv::ImageFormatR32i:
+ case spv::ImageFormatR32ui:
+ texelSize = 4;
+ packed[0] = texel.Int(0);
+ numPackedElements = 1;
+ break;
+ case spv::ImageFormatRgba8:
+ texelSize = 4;
+ packed[0] = (SIMD::UInt(Round(Min(Max(texel.Float(0), SIMD::Float(0.0f)), SIMD::Float(1.0f)) * SIMD::Float(255.0f)))) |
+ ((SIMD::UInt(Round(Min(Max(texel.Float(1), SIMD::Float(0.0f)), SIMD::Float(1.0f)) * SIMD::Float(255.0f)))) << 8) |
+ ((SIMD::UInt(Round(Min(Max(texel.Float(2), SIMD::Float(0.0f)), SIMD::Float(1.0f)) * SIMD::Float(255.0f)))) << 16) |
+ ((SIMD::UInt(Round(Min(Max(texel.Float(3), SIMD::Float(0.0f)), SIMD::Float(1.0f)) * SIMD::Float(255.0f)))) << 24);
+ numPackedElements = 1;
+ break;
+ case spv::ImageFormatRgba8Snorm:
+ texelSize = 4;
+ packed[0] = (SIMD::Int(Round(Min(Max(texel.Float(0), SIMD::Float(-1.0f)), SIMD::Float(1.0f)) * SIMD::Float(127.0f))) &
+ SIMD::Int(0xFF)) |
+ ((SIMD::Int(Round(Min(Max(texel.Float(1), SIMD::Float(-1.0f)), SIMD::Float(1.0f)) * SIMD::Float(127.0f))) &
+ SIMD::Int(0xFF)) << 8) |
+ ((SIMD::Int(Round(Min(Max(texel.Float(2), SIMD::Float(-1.0f)), SIMD::Float(1.0f)) * SIMD::Float(127.0f))) &
+ SIMD::Int(0xFF)) << 16) |
+ ((SIMD::Int(Round(Min(Max(texel.Float(3), SIMD::Float(-1.0f)), SIMD::Float(1.0f)) * SIMD::Float(127.0f))) &
+ SIMD::Int(0xFF)) << 24);
+ numPackedElements = 1;
+ break;
+ case spv::ImageFormatRgba8i:
+ case spv::ImageFormatRgba8ui:
+ texelSize = 4;
+ packed[0] = (SIMD::UInt(texel.UInt(0) & SIMD::UInt(0xff))) |
+ (SIMD::UInt(texel.UInt(1) & SIMD::UInt(0xff)) << 8) |
+ (SIMD::UInt(texel.UInt(2) & SIMD::UInt(0xff)) << 16) |
+ (SIMD::UInt(texel.UInt(3) & SIMD::UInt(0xff)) << 24);
+ numPackedElements = 1;
+ break;
+ case spv::ImageFormatRgba16f:
+ texelSize = 8;
+ packed[0] = FloatToHalfBits(texel.UInt(0), false) | FloatToHalfBits(texel.UInt(1), true);
+ packed[1] = FloatToHalfBits(texel.UInt(2), false) | FloatToHalfBits(texel.UInt(3), true);
+ numPackedElements = 2;
+ break;
+ case spv::ImageFormatRgba16i:
+ case spv::ImageFormatRgba16ui:
+ texelSize = 8;
+ packed[0] = SIMD::UInt(texel.UInt(0) & SIMD::UInt(0xffff)) | (SIMD::UInt(texel.UInt(1) & SIMD::UInt(0xffff)) << 16);
+ packed[1] = SIMD::UInt(texel.UInt(2) & SIMD::UInt(0xffff)) | (SIMD::UInt(texel.UInt(3) & SIMD::UInt(0xffff)) << 16);
+ numPackedElements = 2;
+ break;
+ default:
+ UNIMPLEMENTED("spv::ImageFormat %u", format);
+ }
+
+ SIMD::Int texelOffset = coordinate.Int(0) * SIMD::Int(texelSize);
+ if (getType(coordinate.type).sizeInComponents > 1)
+ {
+ texelOffset += coordinate.Int(1) * SIMD::Int(
+ *Pointer<Int>(binding + OFFSET(vk::StorageImageDescriptor, rowPitchBytes)));
+ }
+ if (getType(coordinate.type).sizeInComponents > 2)
+ {
+ texelOffset += coordinate.Int(2) * SIMD::Int(
+ *Pointer<Int>(binding + OFFSET(vk::StorageImageDescriptor, slicePitchBytes)));
+ }
+
+ for (auto i = 0u; i < numPackedElements; i++)
+ {
+ for (int j = 0; j < 4; j++)
+ {
+ If(Extract(state->activeLaneMask(), j) != 0)
+ {
+ Int offset = Int(sizeof(float) * i) + Extract(texelOffset, j);
+ Store(Extract(packed[i], j), RValue<Pointer<Int>>(&imageBase[offset]), sizeof(uint32_t), false,
+ std::memory_order_relaxed);
+ }
+ }
+ }
+
+ return EmitResult::Continue;
+ }
+
void SpirvShader::emitEpilog(SpirvRoutine *routine) const
{
for (auto insn : *this)
diff --git a/src/Pipeline/SpirvShader.hpp b/src/Pipeline/SpirvShader.hpp
index 48cb060..e70571f 100644
--- a/src/Pipeline/SpirvShader.hpp
+++ b/src/Pipeline/SpirvShader.hpp
@@ -724,6 +724,7 @@
EmitResult EmitPhi(InsnIterator insn, EmitState *state) const;
EmitResult EmitImageSampleImplicitLod(InsnIterator insn, EmitState *state) const;
EmitResult EmitImageQuerySize(InsnIterator insn, EmitState *state) const;
+ EmitResult EmitImageWrite(InsnIterator insn, EmitState *state) const;
// OpcodeName() returns the name of the opcode op.
// If NDEBUG is defined, then OpcodeName() will only return the numerical code.