17 changed files with 733 additions and 6 deletions
-
2src/shader_recompiler/CMakeLists.txt
-
3src/shader_recompiler/backend/spirv/emit_context.cpp
-
1src/shader_recompiler/backend/spirv/emit_context.h
-
3src/shader_recompiler/backend/spirv/emit_spirv.cpp
-
44src/shader_recompiler/backend/spirv/emit_spirv.h
-
182src/shader_recompiler/backend/spirv/emit_spirv_image_atomic.cpp
-
89src/shader_recompiler/frontend/ir/ir_emitter.cpp
-
26src/shader_recompiler/frontend/ir/ir_emitter.h
-
33src/shader_recompiler/frontend/ir/microinstruction.cpp
-
38src/shader_recompiler/frontend/ir/opcodes.inc
-
3src/shader_recompiler/frontend/maxwell/maxwell.inc
-
1src/shader_recompiler/frontend/maxwell/translate/impl/impl.h
-
4src/shader_recompiler/frontend/maxwell/translate/impl/not_implemented.cpp
-
204src/shader_recompiler/frontend/maxwell/translate/impl/surface_atomic_operations.cpp
-
36src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp
-
68src/shader_recompiler/ir_opt/texture_pass.cpp
-
2src/shader_recompiler/shader_info.h
@ -0,0 +1,182 @@ |
|||
// Copyright 2021 yuzu Emulator Project
|
|||
// Licensed under GPLv2 or any later version
|
|||
// Refer to the license.txt file included.
|
|||
|
|||
#include "shader_recompiler/backend/spirv/emit_spirv.h"
|
|||
#include "shader_recompiler/frontend/ir/modifiers.h"
|
|||
|
|||
namespace Shader::Backend::SPIRV { |
|||
namespace { |
|||
Id Image(EmitContext& ctx, const IR::Value& index, IR::TextureInstInfo info) { |
|||
if (!index.IsImmediate()) { |
|||
throw NotImplementedException("Indirect image indexing"); |
|||
} |
|||
if (info.type == TextureType::Buffer) { |
|||
const ImageBufferDefinition def{ctx.image_buffers.at(index.U32())}; |
|||
return def.id; |
|||
} else { |
|||
const ImageDefinition def{ctx.images.at(index.U32())}; |
|||
return def.id; |
|||
} |
|||
} |
|||
|
|||
std::pair<Id, Id> AtomicArgs(EmitContext& ctx) { |
|||
const Id scope{ctx.Const(static_cast<u32>(spv::Scope::Device))}; |
|||
const Id semantics{ctx.u32_zero_value}; |
|||
return {scope, semantics}; |
|||
} |
|||
|
|||
Id ImageAtomicU32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id value, |
|||
Id (Sirit::Module::*atomic_func)(Id, Id, Id, Id, Id)) { |
|||
const auto info{inst->Flags<IR::TextureInstInfo>()}; |
|||
const Id image{Image(ctx, index, info)}; |
|||
const Id pointer{ctx.OpImageTexelPointer(ctx.image_u32, image, coords, ctx.Const(0U))}; |
|||
const auto [scope, semantics]{AtomicArgs(ctx)}; |
|||
return (ctx.*atomic_func)(ctx.U32[1], pointer, scope, semantics, value); |
|||
} |
|||
} // Anonymous namespace
|
|||
|
|||
Id EmitImageAtomicIAdd32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, |
|||
Id value) { |
|||
return ImageAtomicU32(ctx, inst, index, coords, value, &Sirit::Module::OpAtomicIAdd); |
|||
} |
|||
|
|||
Id EmitImageAtomicSMin32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, |
|||
Id value) { |
|||
return ImageAtomicU32(ctx, inst, index, coords, value, &Sirit::Module::OpAtomicSMin); |
|||
} |
|||
|
|||
Id EmitImageAtomicUMin32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, |
|||
Id value) { |
|||
return ImageAtomicU32(ctx, inst, index, coords, value, &Sirit::Module::OpAtomicUMin); |
|||
} |
|||
|
|||
Id EmitImageAtomicSMax32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, |
|||
Id value) { |
|||
return ImageAtomicU32(ctx, inst, index, coords, value, &Sirit::Module::OpAtomicSMax); |
|||
} |
|||
|
|||
Id EmitImageAtomicUMax32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, |
|||
Id value) { |
|||
return ImageAtomicU32(ctx, inst, index, coords, value, &Sirit::Module::OpAtomicUMax); |
|||
} |
|||
|
|||
Id EmitImageAtomicInc32(EmitContext&, IR::Inst*, const IR::Value&, Id, Id) { |
|||
// TODO: This is not yet implemented
|
|||
throw NotImplementedException("SPIR-V Instruction"); |
|||
} |
|||
|
|||
Id EmitImageAtomicDec32(EmitContext&, IR::Inst*, const IR::Value&, Id, Id) { |
|||
// TODO: This is not yet implemented
|
|||
throw NotImplementedException("SPIR-V Instruction"); |
|||
} |
|||
|
|||
Id EmitImageAtomicAnd32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, |
|||
Id value) { |
|||
return ImageAtomicU32(ctx, inst, index, coords, value, &Sirit::Module::OpAtomicAnd); |
|||
} |
|||
|
|||
Id EmitImageAtomicOr32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, |
|||
Id value) { |
|||
return ImageAtomicU32(ctx, inst, index, coords, value, &Sirit::Module::OpAtomicOr); |
|||
} |
|||
|
|||
Id EmitImageAtomicXor32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, |
|||
Id value) { |
|||
return ImageAtomicU32(ctx, inst, index, coords, value, &Sirit::Module::OpAtomicXor); |
|||
} |
|||
|
|||
Id EmitImageAtomicExchange32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, |
|||
Id value) { |
|||
return ImageAtomicU32(ctx, inst, index, coords, value, &Sirit::Module::OpAtomicExchange); |
|||
} |
|||
|
|||
Id EmitBindlessImageAtomicIAdd32(EmitContext&) { |
|||
throw NotImplementedException("SPIR-V Instruction"); |
|||
} |
|||
|
|||
Id EmitBindlessImageAtomicSMin32(EmitContext&) { |
|||
throw NotImplementedException("SPIR-V Instruction"); |
|||
} |
|||
|
|||
Id EmitBindlessImageAtomicUMin32(EmitContext&) { |
|||
throw NotImplementedException("SPIR-V Instruction"); |
|||
} |
|||
|
|||
Id EmitBindlessImageAtomicSMax32(EmitContext&) { |
|||
throw NotImplementedException("SPIR-V Instruction"); |
|||
} |
|||
|
|||
Id EmitBindlessImageAtomicUMax32(EmitContext&) { |
|||
throw NotImplementedException("SPIR-V Instruction"); |
|||
} |
|||
|
|||
Id EmitBindlessImageAtomicInc32(EmitContext&) { |
|||
throw NotImplementedException("SPIR-V Instruction"); |
|||
} |
|||
|
|||
Id EmitBindlessImageAtomicDec32(EmitContext&) { |
|||
throw NotImplementedException("SPIR-V Instruction"); |
|||
} |
|||
|
|||
Id EmitBindlessImageAtomicAnd32(EmitContext&) { |
|||
throw NotImplementedException("SPIR-V Instruction"); |
|||
} |
|||
|
|||
Id EmitBindlessImageAtomicOr32(EmitContext&) { |
|||
throw NotImplementedException("SPIR-V Instruction"); |
|||
} |
|||
|
|||
Id EmitBindlessImageAtomicXor32(EmitContext&) { |
|||
throw NotImplementedException("SPIR-V Instruction"); |
|||
} |
|||
|
|||
Id EmitBindlessImageAtomicExchange32(EmitContext&) { |
|||
throw NotImplementedException("SPIR-V Instruction"); |
|||
} |
|||
|
|||
Id EmitBoundImageAtomicIAdd32(EmitContext&) { |
|||
throw NotImplementedException("SPIR-V Instruction"); |
|||
} |
|||
|
|||
Id EmitBoundImageAtomicSMin32(EmitContext&) { |
|||
throw NotImplementedException("SPIR-V Instruction"); |
|||
} |
|||
|
|||
Id EmitBoundImageAtomicUMin32(EmitContext&) { |
|||
throw NotImplementedException("SPIR-V Instruction"); |
|||
} |
|||
|
|||
Id EmitBoundImageAtomicSMax32(EmitContext&) { |
|||
throw NotImplementedException("SPIR-V Instruction"); |
|||
} |
|||
|
|||
Id EmitBoundImageAtomicUMax32(EmitContext&) { |
|||
throw NotImplementedException("SPIR-V Instruction"); |
|||
} |
|||
|
|||
Id EmitBoundImageAtomicInc32(EmitContext&) { |
|||
throw NotImplementedException("SPIR-V Instruction"); |
|||
} |
|||
|
|||
Id EmitBoundImageAtomicDec32(EmitContext&) { |
|||
throw NotImplementedException("SPIR-V Instruction"); |
|||
} |
|||
|
|||
Id EmitBoundImageAtomicAnd32(EmitContext&) { |
|||
throw NotImplementedException("SPIR-V Instruction"); |
|||
} |
|||
|
|||
Id EmitBoundImageAtomicOr32(EmitContext&) { |
|||
throw NotImplementedException("SPIR-V Instruction"); |
|||
} |
|||
|
|||
Id EmitBoundImageAtomicXor32(EmitContext&) { |
|||
throw NotImplementedException("SPIR-V Instruction"); |
|||
} |
|||
|
|||
Id EmitBoundImageAtomicExchange32(EmitContext&) { |
|||
throw NotImplementedException("SPIR-V Instruction"); |
|||
} |
|||
|
|||
} // namespace Shader::Backend::SPIRV
|
|||
@ -0,0 +1,204 @@ |
|||
// Copyright 2021 yuzu Emulator Project
|
|||
// Licensed under GPLv2 or any later version
|
|||
// Refer to the license.txt file included.
|
|||
|
|||
#include <array>
|
|||
#include <bit>
|
|||
|
|||
#include "common/bit_field.h"
|
|||
#include "common/common_types.h"
|
|||
#include "shader_recompiler/frontend/ir/modifiers.h"
|
|||
#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h"
|
|||
|
|||
namespace Shader::Maxwell { |
|||
namespace { |
|||
enum class Type : u64 { |
|||
_1D, |
|||
BUFFER_1D, |
|||
ARRAY_1D, |
|||
_2D, |
|||
ARRAY_2D, |
|||
_3D, |
|||
}; |
|||
|
|||
enum class Size : u64 { |
|||
U32, |
|||
S32, |
|||
U64, |
|||
S64, |
|||
F32FTZRN, |
|||
F16x2FTZRN, |
|||
SD32, |
|||
SD64, |
|||
}; |
|||
|
|||
enum class AtomicOp : u64 { |
|||
ADD, |
|||
MIN, |
|||
MAX, |
|||
INC, |
|||
DEC, |
|||
AND, |
|||
OR, |
|||
XOR, |
|||
EXCH, |
|||
}; |
|||
|
|||
enum class Clamp : u64 { |
|||
IGN, |
|||
Default, |
|||
TRAP, |
|||
}; |
|||
|
|||
TextureType GetType(Type type) { |
|||
switch (type) { |
|||
case Type::_1D: |
|||
return TextureType::Color1D; |
|||
case Type::BUFFER_1D: |
|||
return TextureType::Buffer; |
|||
case Type::ARRAY_1D: |
|||
return TextureType::ColorArray1D; |
|||
case Type::_2D: |
|||
return TextureType::Color2D; |
|||
case Type::ARRAY_2D: |
|||
return TextureType::ColorArray2D; |
|||
case Type::_3D: |
|||
return TextureType::Color3D; |
|||
} |
|||
throw NotImplementedException("Invalid type {}", type); |
|||
} |
|||
|
|||
IR::Value MakeCoords(TranslatorVisitor& v, IR::Reg reg, Type type) { |
|||
const auto array{[&](int index) { |
|||
return v.ir.BitFieldExtract(v.X(reg + index), v.ir.Imm32(0), v.ir.Imm32(16)); |
|||
}}; |
|||
switch (type) { |
|||
case Type::_1D: |
|||
case Type::BUFFER_1D: |
|||
return v.X(reg); |
|||
default: |
|||
break; |
|||
} |
|||
throw NotImplementedException("Invalid type {}", type); |
|||
} |
|||
|
|||
IR::Value ApplyAtomicOp(IR::IREmitter& ir, const IR::U32& handle, const IR::Value& coords, |
|||
const IR::Value& op_b, IR::TextureInstInfo info, AtomicOp op, |
|||
bool is_signed) { |
|||
switch (op) { |
|||
case AtomicOp::ADD: |
|||
return ir.ImageAtomicIAdd(handle, coords, op_b, info); |
|||
case AtomicOp::MIN: |
|||
return ir.ImageAtomicIMin(handle, coords, op_b, is_signed, info); |
|||
case AtomicOp::MAX: |
|||
return ir.ImageAtomicIMax(handle, coords, op_b, is_signed, info); |
|||
case AtomicOp::INC: |
|||
return ir.ImageAtomicInc(handle, coords, op_b, info); |
|||
case AtomicOp::DEC: |
|||
return ir.ImageAtomicDec(handle, coords, op_b, info); |
|||
case AtomicOp::AND: |
|||
return ir.ImageAtomicAnd(handle, coords, op_b, info); |
|||
case AtomicOp::OR: |
|||
return ir.ImageAtomicOr(handle, coords, op_b, info); |
|||
case AtomicOp::XOR: |
|||
return ir.ImageAtomicXor(handle, coords, op_b, info); |
|||
case AtomicOp::EXCH: |
|||
return ir.ImageAtomicExchange(handle, coords, op_b, info); |
|||
default: |
|||
throw NotImplementedException("Atomic Operation {}", op); |
|||
} |
|||
} |
|||
|
|||
ImageFormat Format(Size size) { |
|||
switch (size) { |
|||
case Size::U32: |
|||
case Size::S32: |
|||
case Size::SD32: |
|||
return ImageFormat::R32_UINT; |
|||
default: |
|||
break; |
|||
} |
|||
throw NotImplementedException("Invalid size {}", size); |
|||
} |
|||
|
|||
bool IsSizeInt32(Size size) { |
|||
switch (size) { |
|||
case Size::U32: |
|||
case Size::S32: |
|||
case Size::SD32: |
|||
return true; |
|||
default: |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
void ImageAtomOp(TranslatorVisitor& v, IR::Reg dest_reg, IR::Reg operand_reg, IR::Reg coord_reg, |
|||
IR::Reg bindless_reg, AtomicOp op, Clamp clamp, Size size, Type type, |
|||
u64 bound_offset, bool is_bindless, bool write_result) { |
|||
if (clamp != Clamp::IGN) { |
|||
throw NotImplementedException("Clamp {}", clamp); |
|||
} |
|||
if (!IsSizeInt32(size)) { |
|||
throw NotImplementedException("Size {}", size); |
|||
} |
|||
const bool is_signed{size == Size::S32}; |
|||
const ImageFormat format{Format(size)}; |
|||
const TextureType tex_type{GetType(type)}; |
|||
const IR::Value coords{MakeCoords(v, coord_reg, type)}; |
|||
|
|||
const IR::U32 handle{is_bindless != 0 ? v.X(bindless_reg) |
|||
: v.ir.Imm32(static_cast<u32>(bound_offset * 4))}; |
|||
IR::TextureInstInfo info{}; |
|||
info.type.Assign(tex_type); |
|||
info.image_format.Assign(format); |
|||
|
|||
// TODO: float/64-bit operand
|
|||
const IR::Value op_b{v.X(operand_reg)}; |
|||
const IR::Value color{ApplyAtomicOp(v.ir, handle, coords, op_b, info, op, is_signed)}; |
|||
|
|||
if (write_result) { |
|||
v.X(dest_reg, IR::U32{color}); |
|||
} |
|||
} |
|||
} // Anonymous namespace
|
|||
|
|||
void TranslatorVisitor::SUATOM(u64 insn) { |
|||
union { |
|||
u64 raw; |
|||
BitField<54, 1, u64> is_bindless; |
|||
BitField<29, 4, AtomicOp> op; |
|||
BitField<33, 3, Type> type; |
|||
BitField<51, 3, Size> size; |
|||
BitField<49, 2, Clamp> clamp; |
|||
BitField<0, 8, IR::Reg> dest_reg; |
|||
BitField<8, 8, IR::Reg> coord_reg; |
|||
BitField<20, 8, IR::Reg> operand_reg; |
|||
BitField<36, 13, u64> bound_offset; // !is_bindless
|
|||
BitField<39, 8, IR::Reg> bindless_reg; // is_bindless
|
|||
} const suatom{insn}; |
|||
|
|||
ImageAtomOp(*this, suatom.dest_reg, suatom.operand_reg, suatom.coord_reg, suatom.bindless_reg, |
|||
suatom.op, suatom.clamp, suatom.size, suatom.type, suatom.bound_offset, |
|||
suatom.is_bindless != 0, true); |
|||
} |
|||
|
|||
void TranslatorVisitor::SURED(u64 insn) { |
|||
// TODO: confirm offsets
|
|||
union { |
|||
u64 raw; |
|||
BitField<51, 1, u64> is_bound; |
|||
BitField<21, 3, AtomicOp> op; |
|||
BitField<33, 3, Type> type; |
|||
BitField<20, 3, Size> size; |
|||
BitField<49, 2, Clamp> clamp; |
|||
BitField<0, 8, IR::Reg> operand_reg; |
|||
BitField<8, 8, IR::Reg> coord_reg; |
|||
BitField<36, 13, u64> bound_offset; // is_bound
|
|||
BitField<39, 8, IR::Reg> bindless_reg; // !is_bound
|
|||
} const sured{insn}; |
|||
ImageAtomOp(*this, IR::Reg::RZ, sured.operand_reg, sured.coord_reg, sured.bindless_reg, |
|||
sured.op, sured.clamp, sured.size, sured.type, sured.bound_offset, |
|||
sured.is_bound == 0, false); |
|||
} |
|||
|
|||
} // namespace Shader::Maxwell
|
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue