summaryrefslogtreecommitdiffstats
path: root/src/shader_recompiler/backend
diff options
context:
space:
mode:
authorReinUsesLisp <reinuseslisp@airmail.cc>2021-03-08 22:31:53 +0100
committerameerj <52414509+ameerj@users.noreply.github.com>2021-07-23 03:51:23 +0200
commitab463712474de5f99eec137a9c6233e55fe184f0 (patch)
tree30d79ac64dd03d5cfafd07c0c42c2baadc82de98 /src/shader_recompiler/backend
parentshader: Implement R2P (diff)
downloadyuzu-ab463712474de5f99eec137a9c6233e55fe184f0.tar
yuzu-ab463712474de5f99eec137a9c6233e55fe184f0.tar.gz
yuzu-ab463712474de5f99eec137a9c6233e55fe184f0.tar.bz2
yuzu-ab463712474de5f99eec137a9c6233e55fe184f0.tar.lz
yuzu-ab463712474de5f99eec137a9c6233e55fe184f0.tar.xz
yuzu-ab463712474de5f99eec137a9c6233e55fe184f0.tar.zst
yuzu-ab463712474de5f99eec137a9c6233e55fe184f0.zip
Diffstat (limited to 'src/shader_recompiler/backend')
-rw-r--r--src/shader_recompiler/backend/spirv/emit_context.cpp69
-rw-r--r--src/shader_recompiler/backend/spirv/emit_context.h7
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv.cpp12
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv.h32
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_convert.cpp48
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_image.cpp146
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_memory.cpp18
7 files changed, 327 insertions, 5 deletions
diff --git a/src/shader_recompiler/backend/spirv/emit_context.cpp b/src/shader_recompiler/backend/spirv/emit_context.cpp
index d2dbd56d4..21900d387 100644
--- a/src/shader_recompiler/backend/spirv/emit_context.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_context.cpp
@@ -12,6 +12,43 @@
#include "shader_recompiler/backend/spirv/emit_context.h"
namespace Shader::Backend::SPIRV {
+namespace {
+Id ImageType(EmitContext& ctx, const TextureDescriptor& desc) {
+ const spv::ImageFormat format{spv::ImageFormat::Unknown};
+ const Id type{ctx.F32[1]};
+ switch (desc.type) {
+ case TextureType::Color1D:
+ return ctx.TypeImage(type, spv::Dim::Dim1D, false, false, false, 1, format);
+ case TextureType::ColorArray1D:
+ return ctx.TypeImage(type, spv::Dim::Dim1D, false, true, false, 1, format);
+ case TextureType::Color2D:
+ return ctx.TypeImage(type, spv::Dim::Dim2D, false, false, false, 1, format);
+ case TextureType::ColorArray2D:
+ return ctx.TypeImage(type, spv::Dim::Dim2D, false, true, false, 1, format);
+ case TextureType::Color3D:
+ return ctx.TypeImage(type, spv::Dim::Dim3D, false, false, false, 1, format);
+ case TextureType::ColorCube:
+ return ctx.TypeImage(type, spv::Dim::Cube, false, false, false, 1, format);
+ case TextureType::ColorArrayCube:
+ return ctx.TypeImage(type, spv::Dim::Cube, false, true, false, 1, format);
+ case TextureType::Shadow1D:
+ return ctx.TypeImage(type, spv::Dim::Dim1D, true, false, false, 1, format);
+ case TextureType::ShadowArray1D:
+ return ctx.TypeImage(type, spv::Dim::Dim1D, true, true, false, 1, format);
+ case TextureType::Shadow2D:
+ return ctx.TypeImage(type, spv::Dim::Dim2D, true, false, false, 1, format);
+ case TextureType::ShadowArray2D:
+ return ctx.TypeImage(type, spv::Dim::Dim2D, true, true, false, 1, format);
+ case TextureType::Shadow3D:
+ return ctx.TypeImage(type, spv::Dim::Dim3D, true, false, false, 1, format);
+ case TextureType::ShadowCube:
+ return ctx.TypeImage(type, spv::Dim::Cube, true, false, false, 1, format);
+ case TextureType::ShadowArrayCube:
+ return ctx.TypeImage(type, spv::Dim::Cube, false, true, false, 1, format);
+ }
+ throw InvalidArgument("Invalid texture type {}", desc.type);
+}
+} // Anonymous namespace
void VectorTypes::Define(Sirit::Module& sirit_ctx, Id base_type, std::string_view name) {
defs[0] = sirit_ctx.Name(base_type, name);
@@ -35,6 +72,7 @@ EmitContext::EmitContext(const Profile& profile_, IR::Program& program)
u32 binding{};
DefineConstantBuffers(program.info, binding);
DefineStorageBuffers(program.info, binding);
+ DefineTextures(program.info, binding);
DefineLabels(program);
}
@@ -46,6 +84,10 @@ Id EmitContext::Def(const IR::Value& value) {
return value.Inst()->Definition<Id>();
}
switch (value.Type()) {
+ case IR::Type::Void:
+ // Void instructions are used for optional arguments (e.g. texture offsets)
+ // They are not meant to be used in the SPIR-V module
+ return Id{};
case IR::Type::U1:
return value.U1() ? true_value : false_value;
case IR::Type::U32:
@@ -122,7 +164,7 @@ void EmitContext::DefineConstantBuffers(const Info& info, u32& binding) {
uniform_u32 = TypePointer(spv::StorageClass::Uniform, U32[1]);
u32 index{};
- for (const Info::ConstantBufferDescriptor& desc : info.constant_buffer_descriptors) {
+ for (const ConstantBufferDescriptor& desc : info.constant_buffer_descriptors) {
const Id id{AddGlobalVariable(uniform_type, spv::StorageClass::Uniform)};
Decorate(id, spv::Decoration::Binding, binding);
Decorate(id, spv::Decoration::DescriptorSet, 0U);
@@ -152,7 +194,7 @@ void EmitContext::DefineStorageBuffers(const Info& info, u32& binding) {
storage_u32 = TypePointer(spv::StorageClass::StorageBuffer, U32[1]);
u32 index{};
- for (const Info::StorageBufferDescriptor& desc : info.storage_buffers_descriptors) {
+ for (const StorageBufferDescriptor& desc : info.storage_buffers_descriptors) {
const Id id{AddGlobalVariable(storage_type, spv::StorageClass::StorageBuffer)};
Decorate(id, spv::Decoration::Binding, binding);
Decorate(id, spv::Decoration::DescriptorSet, 0U);
@@ -163,6 +205,29 @@ void EmitContext::DefineStorageBuffers(const Info& info, u32& binding) {
}
}
+void EmitContext::DefineTextures(const Info& info, u32& binding) {
+ textures.reserve(info.texture_descriptors.size());
+ for (const TextureDescriptor& desc : info.texture_descriptors) {
+ if (desc.count != 1) {
+ throw NotImplementedException("Array of textures");
+ }
+ const Id type{TypeSampledImage(ImageType(*this, desc))};
+ const Id pointer_type{TypePointer(spv::StorageClass::UniformConstant, type)};
+ const Id id{AddGlobalVariable(pointer_type, spv::StorageClass::UniformConstant)};
+ Decorate(id, spv::Decoration::Binding, binding);
+ Decorate(id, spv::Decoration::DescriptorSet, 0U);
+ Name(id, fmt::format("tex{}_{:02x}", desc.cbuf_index, desc.cbuf_offset));
+ for (u32 index = 0; index < desc.count; ++index) {
+ // TODO: Pass count info
+ textures.push_back(TextureDefinition{
+ .id{id},
+ .type{type},
+ });
+ }
+ binding += desc.count;
+ }
+}
+
void EmitContext::DefineLabels(IR::Program& program) {
for (const IR::Function& function : program.functions) {
for (IR::Block* const block : function.blocks) {
diff --git a/src/shader_recompiler/backend/spirv/emit_context.h b/src/shader_recompiler/backend/spirv/emit_context.h
index d20cf387e..8b3109eb8 100644
--- a/src/shader_recompiler/backend/spirv/emit_context.h
+++ b/src/shader_recompiler/backend/spirv/emit_context.h
@@ -29,6 +29,11 @@ private:
std::array<Id, 4> defs{};
};
+struct TextureDefinition {
+ Id id;
+ Id type;
+};
+
class EmitContext final : public Sirit::Module {
public:
explicit EmitContext(const Profile& profile, IR::Program& program);
@@ -56,6 +61,7 @@ public:
std::array<Id, Info::MAX_CBUFS> cbufs{};
std::array<Id, Info::MAX_SSBOS> ssbos{};
+ std::vector<TextureDefinition> textures;
Id workgroup_id{};
Id local_invocation_id{};
@@ -66,6 +72,7 @@ private:
void DefineSpecialVariables(const Info& info);
void DefineConstantBuffers(const Info& info, u32& binding);
void DefineStorageBuffers(const Info& info, u32& binding);
+ void DefineTextures(const Info& info, u32& binding);
void DefineLabels(IR::Program& program);
};
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.cpp b/src/shader_recompiler/backend/spirv/emit_spirv.cpp
index 8097fe82d..a94e9cb2d 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv.cpp
@@ -221,6 +221,14 @@ std::vector<u32> EmitSPIRV(const Profile& profile, Environment& env, IR::Program
workgroup_size[2]);
SetupDenormControl(profile, program, ctx, func);
+ if (info.uses_sampled_1d) {
+ ctx.AddCapability(spv::Capability::Sampled1D);
+ }
+ if (info.uses_sparse_residency) {
+ ctx.AddCapability(spv::Capability::SparseResidency);
+ }
+ // TODO: Track this usage
+ ctx.AddCapability(spv::Capability::ImageGatherExtended);
return ctx.Assemble();
}
@@ -259,4 +267,8 @@ void EmitGetOverflowFromOp(EmitContext&) {
throw LogicError("Unreachable instruction");
}
+void EmitGetSparseFromOp(EmitContext&) {
+ throw LogicError("Unreachable instruction");
+}
+
} // namespace Shader::Backend::SPIRV
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.h b/src/shader_recompiler/backend/spirv/emit_spirv.h
index 92387ca28..69698c478 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv.h
+++ b/src/shader_recompiler/backend/spirv/emit_spirv.h
@@ -83,7 +83,8 @@ void EmitWriteStorage32(EmitContext& ctx, const IR::Value& binding, const IR::Va
Id value);
void EmitWriteStorage64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
Id value);
-void EmitWriteStorage128(EmitContext& ctx);
+void EmitWriteStorage128(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
+ Id value);
Id EmitCompositeConstructU32x2(EmitContext& ctx, Id e1, Id e2);
Id EmitCompositeConstructU32x3(EmitContext& ctx, Id e1, Id e2, Id e3);
Id EmitCompositeConstructU32x4(EmitContext& ctx, Id e1, Id e2, Id e3, Id e4);
@@ -145,6 +146,7 @@ void EmitGetZeroFromOp(EmitContext& ctx);
void EmitGetSignFromOp(EmitContext& ctx);
void EmitGetCarryFromOp(EmitContext& ctx);
void EmitGetOverflowFromOp(EmitContext& ctx);
+void EmitGetSparseFromOp(EmitContext& ctx);
Id EmitFPAbs16(EmitContext& ctx, Id value);
Id EmitFPAbs32(EmitContext& ctx, Id value);
Id EmitFPAbs64(EmitContext& ctx, Id value);
@@ -291,5 +293,33 @@ Id EmitConvertF16F32(EmitContext& ctx, Id value);
Id EmitConvertF32F16(EmitContext& ctx, Id value);
Id EmitConvertF32F64(EmitContext& ctx, Id value);
Id EmitConvertF64F32(EmitContext& ctx, Id value);
+Id EmitConvertF16S32(EmitContext& ctx, Id value);
+Id EmitConvertF16S64(EmitContext& ctx, Id value);
+Id EmitConvertF16U32(EmitContext& ctx, Id value);
+Id EmitConvertF16U64(EmitContext& ctx, Id value);
+Id EmitConvertF32S32(EmitContext& ctx, Id value);
+Id EmitConvertF32S64(EmitContext& ctx, Id value);
+Id EmitConvertF32U32(EmitContext& ctx, Id value);
+Id EmitConvertF32U64(EmitContext& ctx, Id value);
+Id EmitConvertF64S32(EmitContext& ctx, Id value);
+Id EmitConvertF64S64(EmitContext& ctx, Id value);
+Id EmitConvertF64U32(EmitContext& ctx, Id value);
+Id EmitConvertF64U64(EmitContext& ctx, Id value);
+Id EmitBindlessImageSampleImplicitLod(EmitContext&);
+Id EmitBindlessImageSampleExplicitLod(EmitContext&);
+Id EmitBindlessImageSampleDrefImplicitLod(EmitContext&);
+Id EmitBindlessImageSampleDrefExplicitLod(EmitContext&);
+Id EmitBoundImageSampleImplicitLod(EmitContext&);
+Id EmitBoundImageSampleExplicitLod(EmitContext&);
+Id EmitBoundImageSampleDrefImplicitLod(EmitContext&);
+Id EmitBoundImageSampleDrefExplicitLod(EmitContext&);
+Id EmitImageSampleImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
+ Id bias_lc, Id offset);
+Id EmitImageSampleExplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
+ Id lod_lc, Id offset);
+Id EmitImageSampleDrefImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index,
+ Id coords, Id dref, Id bias_lc, Id offset);
+Id EmitImageSampleDrefExplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index,
+ Id coords, Id dref, Id lod_lc, Id offset);
} // namespace Shader::Backend::SPIRV
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_convert.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_convert.cpp
index edcc2a1cc..2aff673aa 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_convert.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_convert.cpp
@@ -102,4 +102,52 @@ Id EmitConvertF64F32(EmitContext& ctx, Id value) {
return ctx.OpFConvert(ctx.F64[1], value);
}
+Id EmitConvertF16S32(EmitContext& ctx, Id value) {
+ return ctx.OpConvertSToF(ctx.F16[1], value);
+}
+
+Id EmitConvertF16S64(EmitContext& ctx, Id value) {
+ return ctx.OpConvertSToF(ctx.F16[1], value);
+}
+
+Id EmitConvertF16U32(EmitContext& ctx, Id value) {
+ return ctx.OpConvertUToF(ctx.F16[1], value);
+}
+
+Id EmitConvertF16U64(EmitContext& ctx, Id value) {
+ return ctx.OpConvertUToF(ctx.F16[1], value);
+}
+
+Id EmitConvertF32S32(EmitContext& ctx, Id value) {
+ return ctx.OpConvertSToF(ctx.F32[1], value);
+}
+
+Id EmitConvertF32S64(EmitContext& ctx, Id value) {
+ return ctx.OpConvertSToF(ctx.F32[1], value);
+}
+
+Id EmitConvertF32U32(EmitContext& ctx, Id value) {
+ return ctx.OpConvertUToF(ctx.F32[1], value);
+}
+
+Id EmitConvertF32U64(EmitContext& ctx, Id value) {
+ return ctx.OpConvertUToF(ctx.F32[1], value);
+}
+
+Id EmitConvertF64S32(EmitContext& ctx, Id value) {
+ return ctx.OpConvertSToF(ctx.F64[1], value);
+}
+
+Id EmitConvertF64S64(EmitContext& ctx, Id value) {
+ return ctx.OpConvertSToF(ctx.F64[1], value);
+}
+
+Id EmitConvertF64U32(EmitContext& ctx, Id value) {
+ return ctx.OpConvertUToF(ctx.F64[1], value);
+}
+
+Id EmitConvertF64U64(EmitContext& ctx, Id value) {
+ return ctx.OpConvertUToF(ctx.F64[1], value);
+}
+
} // namespace Shader::Backend::SPIRV
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp
new file mode 100644
index 000000000..5f4783c95
--- /dev/null
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp
@@ -0,0 +1,146 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <boost/container/static_vector.hpp>
+
+#include "shader_recompiler/backend/spirv/emit_spirv.h"
+#include "shader_recompiler/frontend/ir/modifiers.h"
+
+namespace Shader::Backend::SPIRV {
+namespace {
+class ImageOperands {
+public:
+ explicit ImageOperands(EmitContext& ctx, bool has_bias, bool has_lod, bool has_lod_clamp,
+ Id lod, Id offset) {
+ if (has_bias) {
+ const Id bias{has_lod_clamp ? ctx.OpCompositeExtract(ctx.F32[1], lod, 0) : lod};
+ Add(spv::ImageOperandsMask::Bias, bias);
+ }
+ if (has_lod) {
+ const Id lod_value{has_lod_clamp ? ctx.OpCompositeExtract(ctx.F32[1], lod, 0) : lod};
+ Add(spv::ImageOperandsMask::Lod, lod_value);
+ }
+ if (Sirit::ValidId(offset)) {
+ Add(spv::ImageOperandsMask::Offset, offset);
+ }
+ if (has_lod_clamp) {
+ const Id lod_clamp{has_bias ? ctx.OpCompositeExtract(ctx.F32[1], lod, 1) : lod};
+ Add(spv::ImageOperandsMask::MinLod, lod_clamp);
+ }
+ }
+
+ void Add(spv::ImageOperandsMask new_mask, Id value) {
+ mask = static_cast<spv::ImageOperandsMask>(static_cast<unsigned>(mask) |
+ static_cast<unsigned>(new_mask));
+ operands.push_back(value);
+ }
+
+ std::span<const Id> Span() const noexcept {
+ return std::span{operands.data(), operands.size()};
+ }
+
+ spv::ImageOperandsMask Mask() const noexcept {
+ return mask;
+ }
+
+private:
+ boost::container::static_vector<Id, 3> operands;
+ spv::ImageOperandsMask mask{};
+};
+
+Id Texture(EmitContext& ctx, const IR::Value& index) {
+ if (index.IsImmediate()) {
+ const TextureDefinition def{ctx.textures.at(index.U32())};
+ return ctx.OpLoad(def.type, def.id);
+ }
+ throw NotImplementedException("Indirect texture sample");
+}
+
+template <typename MethodPtrType, typename... Args>
+Id Emit(MethodPtrType sparse_ptr, MethodPtrType non_sparse_ptr, EmitContext& ctx, IR::Inst* inst,
+ Id result_type, Args&&... args) {
+ IR::Inst* const sparse{inst->GetAssociatedPseudoOperation(IR::Opcode::GetSparseFromOp)};
+ if (!sparse) {
+ return (ctx.*non_sparse_ptr)(result_type, std::forward<Args>(args)...);
+ }
+ const Id struct_type{ctx.TypeStruct(ctx.U32[1], result_type)};
+ const Id sample{(ctx.*sparse_ptr)(struct_type, std::forward<Args>(args)...)};
+ const Id resident_code{ctx.OpCompositeExtract(ctx.U32[1], sample, 0U)};
+ sparse->SetDefinition(ctx.OpImageSparseTexelsResident(ctx.U1, resident_code));
+ sparse->Invalidate();
+ return ctx.OpCompositeExtract(result_type, sample, 1U);
+}
+} // Anonymous namespace
+
+Id EmitBindlessImageSampleImplicitLod(EmitContext&) {
+ throw LogicError("Unreachable instruction");
+}
+
+Id EmitBindlessImageSampleExplicitLod(EmitContext&) {
+ throw LogicError("Unreachable instruction");
+}
+
+Id EmitBindlessImageSampleDrefImplicitLod(EmitContext&) {
+ throw LogicError("Unreachable instruction");
+}
+
+Id EmitBindlessImageSampleDrefExplicitLod(EmitContext&) {
+ throw LogicError("Unreachable instruction");
+}
+
+Id EmitBoundImageSampleImplicitLod(EmitContext&) {
+ throw LogicError("Unreachable instruction");
+}
+
+Id EmitBoundImageSampleExplicitLod(EmitContext&) {
+ throw LogicError("Unreachable instruction");
+}
+
+Id EmitBoundImageSampleDrefImplicitLod(EmitContext&) {
+ throw LogicError("Unreachable instruction");
+}
+
+Id EmitBoundImageSampleDrefExplicitLod(EmitContext&) {
+ throw LogicError("Unreachable instruction");
+}
+
+Id EmitImageSampleImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
+ Id bias_lc, Id offset) {
+ const auto info{inst->Flags<IR::TextureInstInfo>()};
+ const ImageOperands operands(ctx, info.has_bias != 0, false, info.has_lod_clamp != 0, bias_lc,
+ offset);
+ return Emit(&EmitContext::OpImageSparseSampleImplicitLod,
+ &EmitContext::OpImageSampleImplicitLod, ctx, inst, ctx.F32[4], Texture(ctx, index),
+ coords, operands.Mask(), operands.Span());
+}
+
+Id EmitImageSampleExplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
+ Id lod_lc, Id offset) {
+ const auto info{inst->Flags<IR::TextureInstInfo>()};
+ const ImageOperands operands(ctx, false, true, info.has_lod_clamp != 0, lod_lc, offset);
+ return Emit(&EmitContext::OpImageSparseSampleExplicitLod,
+ &EmitContext::OpImageSampleExplicitLod, ctx, inst, ctx.F32[4], Texture(ctx, index),
+ coords, operands.Mask(), operands.Span());
+}
+
+Id EmitImageSampleDrefImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index,
+ Id coords, Id dref, Id bias_lc, Id offset) {
+ const auto info{inst->Flags<IR::TextureInstInfo>()};
+ const ImageOperands operands(ctx, info.has_bias != 0, false, info.has_lod_clamp != 0, bias_lc,
+ offset);
+ return Emit(&EmitContext::OpImageSparseSampleDrefImplicitLod,
+ &EmitContext::OpImageSampleDrefImplicitLod, ctx, inst, ctx.F32[1],
+ Texture(ctx, index), coords, dref, operands.Mask(), operands.Span());
+}
+
+Id EmitImageSampleDrefExplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index,
+ Id coords, Id dref, Id lod_lc, Id offset) {
+ const auto info{inst->Flags<IR::TextureInstInfo>()};
+ const ImageOperands operands(ctx, false, true, info.has_lod_clamp != 0, lod_lc, offset);
+ return Emit(&EmitContext::OpImageSparseSampleDrefExplicitLod,
+ &EmitContext::OpImageSampleDrefExplicitLod, ctx, inst, ctx.F32[1],
+ Texture(ctx, index), coords, dref, operands.Mask(), operands.Span());
+}
+
+} // namespace Shader::Backend::SPIRV
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_memory.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_memory.cpp
index 808c1b401..7d3efc741 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_memory.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_memory.cpp
@@ -154,8 +154,22 @@ void EmitWriteStorage64(EmitContext& ctx, const IR::Value& binding, const IR::Va
ctx.OpStore(high_pointer, ctx.OpCompositeExtract(ctx.U32[1], value, 1U));
}
-void EmitWriteStorage128(EmitContext&) {
- throw NotImplementedException("SPIR-V Instruction");
+void EmitWriteStorage128(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
+ Id value) {
+ if (!binding.IsImmediate()) {
+ throw NotImplementedException("Dynamic storage buffer indexing");
+ }
+ // TODO: Support reinterpreting bindings, guaranteed to be aligned
+ const Id ssbo{ctx.ssbos[binding.U32()]};
+ const Id base_index{StorageIndex(ctx, offset, sizeof(u32))};
+ for (u32 element = 0; element < 4; ++element) {
+ Id index = base_index;
+ if (element > 0) {
+ index = ctx.OpIAdd(ctx.U32[1], base_index, ctx.Constant(ctx.U32[1], element));
+ }
+ const Id pointer{ctx.OpAccessChain(ctx.storage_u32, ssbo, ctx.u32_zero_value, index)};
+ ctx.OpStore(pointer, ctx.OpCompositeExtract(ctx.U32[1], value, element));
+ }
}
} // namespace Shader::Backend::SPIRV