// Copyright 2021 yuzu Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. #include #include "shader_recompiler/backend/spirv/emit_spirv.h" namespace Shader::Backend::SPIRV { namespace { Id StorageIndex(EmitContext& ctx, const IR::Value& offset, size_t element_size) { if (offset.IsImmediate()) { const u32 imm_offset{static_cast(offset.U32() / element_size)}; return ctx.Constant(ctx.U32[1], imm_offset); } const u32 shift{static_cast(std::countr_zero(element_size))}; const Id index{ctx.Def(offset)}; if (shift == 0) { return index; } const Id shift_id{ctx.Constant(ctx.U32[1], shift)}; return ctx.OpShiftRightLogical(ctx.U32[1], index, shift_id); } Id EmitLoadStorage(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, u32 num_components) { // TODO: Support reinterpreting bindings, guaranteed to be aligned if (!binding.IsImmediate()) { throw NotImplementedException("Dynamic storage buffer indexing"); } const Id ssbo{ctx.ssbos[binding.U32()]}; const Id base_index{StorageIndex(ctx, offset, sizeof(u32))}; std::array components; for (u32 element = 0; element < num_components; ++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)}; components[element] = ctx.OpLoad(ctx.U32[1], pointer); } if (num_components == 1) { return components[0]; } else { const std::span components_span(components.data(), num_components); return ctx.OpCompositeConstruct(ctx.U32[num_components], components_span); } } } // Anonymous namespace void EmitLoadGlobalU8(EmitContext&) { throw NotImplementedException("SPIR-V Instruction"); } void EmitLoadGlobalS8(EmitContext&) { throw NotImplementedException("SPIR-V Instruction"); } void EmitLoadGlobalU16(EmitContext&) { throw NotImplementedException("SPIR-V Instruction"); } void EmitLoadGlobalS16(EmitContext&) { throw NotImplementedException("SPIR-V Instruction"); } void EmitLoadGlobal32(EmitContext&) { throw NotImplementedException("SPIR-V Instruction"); } void EmitLoadGlobal64(EmitContext&) { throw NotImplementedException("SPIR-V Instruction"); } void EmitLoadGlobal128(EmitContext&) { throw NotImplementedException("SPIR-V Instruction"); } void EmitWriteGlobalU8(EmitContext&) { throw NotImplementedException("SPIR-V Instruction"); } void EmitWriteGlobalS8(EmitContext&) { throw NotImplementedException("SPIR-V Instruction"); } void EmitWriteGlobalU16(EmitContext&) { throw NotImplementedException("SPIR-V Instruction"); } void EmitWriteGlobalS16(EmitContext&) { throw NotImplementedException("SPIR-V Instruction"); } void EmitWriteGlobal32(EmitContext&) { throw NotImplementedException("SPIR-V Instruction"); } void EmitWriteGlobal64(EmitContext&) { throw NotImplementedException("SPIR-V Instruction"); } void EmitWriteGlobal128(EmitContext&) { throw NotImplementedException("SPIR-V Instruction"); } void EmitLoadStorageU8(EmitContext&) { throw NotImplementedException("SPIR-V Instruction"); } void EmitLoadStorageS8(EmitContext&) { throw NotImplementedException("SPIR-V Instruction"); } void EmitLoadStorageU16(EmitContext&) { throw NotImplementedException("SPIR-V Instruction"); } void EmitLoadStorageS16(EmitContext&) { throw NotImplementedException("SPIR-V Instruction"); } Id EmitLoadStorage32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { return EmitLoadStorage(ctx, binding, offset, 1); } Id EmitLoadStorage64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { return EmitLoadStorage(ctx, binding, offset, 2); } Id EmitLoadStorage128(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { return EmitLoadStorage(ctx, binding, offset, 4); } void EmitWriteStorageU8(EmitContext&) { throw NotImplementedException("SPIR-V Instruction"); } void EmitWriteStorageS8(EmitContext&) { throw NotImplementedException("SPIR-V Instruction"); } void EmitWriteStorageU16(EmitContext&) { throw NotImplementedException("SPIR-V Instruction"); } void EmitWriteStorageS16(EmitContext&) { throw NotImplementedException("SPIR-V Instruction"); } void EmitWriteStorage32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, Id value) { if (!binding.IsImmediate()) { throw NotImplementedException("Dynamic storage buffer indexing"); } const Id ssbo{ctx.ssbos[binding.U32()]}; const Id index{StorageIndex(ctx, offset, sizeof(u32))}; const Id pointer{ctx.OpAccessChain(ctx.storage_u32, ssbo, ctx.u32_zero_value, index)}; ctx.OpStore(pointer, value); } void EmitWriteStorage64(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 low_index{StorageIndex(ctx, offset, sizeof(u32))}; const Id high_index{ctx.OpIAdd(ctx.U32[1], low_index, ctx.Constant(ctx.U32[1], 1U))}; const Id low_pointer{ctx.OpAccessChain(ctx.storage_u32, ssbo, ctx.u32_zero_value, low_index)}; const Id high_pointer{ctx.OpAccessChain(ctx.storage_u32, ssbo, ctx.u32_zero_value, high_index)}; ctx.OpStore(low_pointer, ctx.OpCompositeExtract(ctx.U32[1], value, 0U)); ctx.OpStore(high_pointer, ctx.OpCompositeExtract(ctx.U32[1], value, 1U)); } 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