spirv: Implement image derivatives

This commit is contained in:
IndecisiveTurtle 2024-08-23 02:04:11 +03:00
parent 4eda0452b5
commit 03c8b3515d
9 changed files with 56 additions and 16 deletions

View File

@ -1,6 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#pragma clang optimize off
#include <boost/container/static_vector.hpp> #include <boost/container/static_vector.hpp>
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" #include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
#include "shader_recompiler/backend/spirv/spirv_emit_context.h" #include "shader_recompiler/backend/spirv/spirv_emit_context.h"
@ -16,6 +16,12 @@ struct ImageOperands {
static_cast<u32>(new_mask)); static_cast<u32>(new_mask));
operands.push_back(value); operands.push_back(value);
} }
void Add(spv::ImageOperandsMask new_mask, Id value1, Id value2) {
mask = static_cast<spv::ImageOperandsMask>(static_cast<u32>(mask) |
static_cast<u32>(new_mask));
operands.push_back(value1);
operands.push_back(value2);
}
void AddOffset(EmitContext& ctx, const IR::Value& offset, void AddOffset(EmitContext& ctx, const IR::Value& offset,
bool can_use_runtime_offsets = false) { bool can_use_runtime_offsets = false) {
@ -53,6 +59,15 @@ struct ImageOperands {
} }
} }
void AddDerivatives(EmitContext& ctx, Id derivatives) {
if (!Sirit::ValidId(derivatives)) {
return;
}
const Id dx{ctx.OpVectorShuffle(ctx.F32[2], derivatives, derivatives, 0, 1)};
const Id dy{ctx.OpVectorShuffle(ctx.F32[2], derivatives, derivatives, 2, 3)};
Add(spv::ImageOperandsMask::Grad, dx, dy);
}
spv::ImageOperandsMask mask{}; spv::ImageOperandsMask mask{};
boost::container::static_vector<Id, 4> operands; boost::container::static_vector<Id, 4> operands;
}; };
@ -181,9 +196,17 @@ Id EmitImageQueryLod(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords) {
return ctx.OpImageQueryLod(ctx.F32[2], sampled_image, coords); return ctx.OpImageQueryLod(ctx.F32[2], sampled_image, coords);
} }
Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords,
Id derivatives, const IR::Value& offset, Id lod_clamp) { Id derivatives, const IR::Value& offset, Id lod_clamp) {
UNREACHABLE_MSG("SPIR-V Instruction"); const auto& texture = ctx.images[handle & 0xFFFF];
const Id image = ctx.OpLoad(texture.image_type, texture.id);
const Id sampler = ctx.OpLoad(ctx.sampler_type, ctx.samplers[handle >> 16]);
const Id sampled_image = ctx.OpSampledImage(texture.sampled_type, image, sampler);
ImageOperands operands;
operands.AddDerivatives(ctx, derivatives);
operands.AddOffset(ctx, offset);
return ctx.OpImageSampleExplicitLod(ctx.F32[4], sampled_image, coords, operands.mask,
operands.operands);
} }
Id EmitImageRead(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords) { Id EmitImageRead(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords) {

View File

@ -387,7 +387,7 @@ Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, const
Id lod, Id ms); Id lod, Id ms);
Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, u32 handle, Id lod, bool skip_mips); Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, u32 handle, Id lod, bool skip_mips);
Id EmitImageQueryLod(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords); Id EmitImageQueryLod(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords);
Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords,
Id derivatives, const IR::Value& offset, Id lod_clamp); Id derivatives, const IR::Value& offset, Id lod_clamp);
Id EmitImageRead(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords); Id EmitImageRead(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords);
void EmitImageWrite(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id color); void EmitImageWrite(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id color);

View File

@ -1,6 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#pragma clang optimize off
#include "shader_recompiler/frontend/translate/translate.h" #include "shader_recompiler/frontend/translate/translate.h"
namespace Shader::Gcn { namespace Shader::Gcn {

View File

@ -17,6 +17,7 @@ void Translator::EmitVectorMemory(const GcnInst& inst) {
case Opcode::IMAGE_SAMPLE_C_O: case Opcode::IMAGE_SAMPLE_C_O:
case Opcode::IMAGE_SAMPLE_B: case Opcode::IMAGE_SAMPLE_B:
case Opcode::IMAGE_SAMPLE_C_LZ_O: case Opcode::IMAGE_SAMPLE_C_LZ_O:
case Opcode::IMAGE_SAMPLE_D:
return IMAGE_SAMPLE(inst); return IMAGE_SAMPLE(inst);
case Opcode::IMAGE_GATHER4_C: case Opcode::IMAGE_GATHER4_C:
case Opcode::IMAGE_GATHER4_LZ: case Opcode::IMAGE_GATHER4_LZ:
@ -162,12 +163,16 @@ void Translator::IMAGE_SAMPLE(const GcnInst& inst) {
flags.test(MimgModifier::LodBias) ? ir.GetVectorReg<IR::F32>(addr_reg++) : IR::F32{}; flags.test(MimgModifier::LodBias) ? ir.GetVectorReg<IR::F32>(addr_reg++) : IR::F32{};
const IR::F32 dref = const IR::F32 dref =
flags.test(MimgModifier::Pcf) ? ir.GetVectorReg<IR::F32>(addr_reg++) : IR::F32{}; flags.test(MimgModifier::Pcf) ? ir.GetVectorReg<IR::F32>(addr_reg++) : IR::F32{};
const IR::Value derivatives = [&] -> IR::Value {
// Derivatives are tricky because their number depends on the texture type which is located in if (!flags.test(MimgModifier::Derivative)) {
// T#. We don't have access to T# though until resource tracking pass. For now assume no return {};
// derivatives are present, otherwise we don't know where coordinates are placed in the address }
// stream. addr_reg = addr_reg + 4;
ASSERT_MSG(!flags.test(MimgModifier::Derivative), "Derivative image instruction"); return ir.CompositeConstruct(ir.GetVectorReg<IR::F32>(addr_reg - 4),
ir.GetVectorReg<IR::F32>(addr_reg - 3),
ir.GetVectorReg<IR::F32>(addr_reg - 2),
ir.GetVectorReg<IR::F32>(addr_reg - 1));
}();
// Now we can load body components as noted in Table 8.9 Image Opcodes with Sampler // Now we can load body components as noted in Table 8.9 Image Opcodes with Sampler
// Since these are at most 4 dwords, we load them into a single uvec4 and place them // Since these are at most 4 dwords, we load them into a single uvec4 and place them
@ -177,6 +182,10 @@ void Translator::IMAGE_SAMPLE(const GcnInst& inst) {
ir.GetVectorReg<IR::F32>(addr_reg), ir.GetVectorReg<IR::F32>(addr_reg + 1), ir.GetVectorReg<IR::F32>(addr_reg), ir.GetVectorReg<IR::F32>(addr_reg + 1),
ir.GetVectorReg<IR::F32>(addr_reg + 2), ir.GetVectorReg<IR::F32>(addr_reg + 3)); ir.GetVectorReg<IR::F32>(addr_reg + 2), ir.GetVectorReg<IR::F32>(addr_reg + 3));
// Derivatives are tricky because their number depends on the texture type which is located in
// T#. We don't have access to T# though until resource tracking pass. For now assume if
// derivatives are present, that a 2D image is bound.
const bool has_derivatives = flags.test(MimgModifier::Derivative);
const bool explicit_lod = flags.any(MimgModifier::Level0, MimgModifier::Lod); const bool explicit_lod = flags.any(MimgModifier::Level0, MimgModifier::Lod);
IR::TextureInstInfo info{}; IR::TextureInstInfo info{};
@ -186,9 +195,13 @@ void Translator::IMAGE_SAMPLE(const GcnInst& inst) {
info.force_level0.Assign(flags.test(MimgModifier::Level0)); info.force_level0.Assign(flags.test(MimgModifier::Level0));
info.has_offset.Assign(flags.test(MimgModifier::Offset)); info.has_offset.Assign(flags.test(MimgModifier::Offset));
info.explicit_lod.Assign(explicit_lod); info.explicit_lod.Assign(explicit_lod);
info.has_derivatives.Assign(has_derivatives);
// Issue IR instruction, leaving unknown fields blank to patch later. // Issue IR instruction, leaving unknown fields blank to patch later.
const IR::Value texel = [&]() -> IR::Value { const IR::Value texel = [&]() -> IR::Value {
if (has_derivatives) {
return ir.ImageGradient(handle, body, derivatives, offset, {}, info);
}
if (!flags.test(MimgModifier::Pcf)) { if (!flags.test(MimgModifier::Pcf)) {
if (explicit_lod) { if (explicit_lod) {
return ir.ImageSampleExplicitLod(handle, body, offset, info); return ir.ImageSampleExplicitLod(handle, body, offset, info);

View File

@ -1,6 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#pragma clang optimize off
#include <bit> #include <bit>
#include <optional> #include <optional>
#include <type_traits> #include <type_traits>

View File

@ -1,6 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#pragma clang optimize off
#include <algorithm> #include <algorithm>
#include <boost/container/small_vector.hpp> #include <boost/container/small_vector.hpp>
#include "shader_recompiler/ir/basic_block.h" #include "shader_recompiler/ir/basic_block.h"
@ -631,7 +631,10 @@ void PatchImageInstruction(IR::Block& block, IR::Inst& inst, Info& info, Descrip
UNREACHABLE(); UNREACHABLE();
} }
} }
if (inst_info.has_derivatives) {
ASSERT_MSG(image.GetType() == AmdGpu::ImageType::Color2D,
"User derivatives only supported for 2D images");
}
if (inst_info.has_lod_clamp) { if (inst_info.has_lod_clamp) {
const u32 arg_pos = [&]() -> u32 { const u32 arg_pos = [&]() -> u32 {
switch (inst.GetOpcode()) { switch (inst.GetOpcode()) {

View File

@ -58,6 +58,7 @@ union TextureInstInfo {
BitField<4, 1, u32> explicit_lod; BitField<4, 1, u32> explicit_lod;
BitField<5, 1, u32> has_offset; BitField<5, 1, u32> has_offset;
BitField<6, 2, u32> gather_comp; BitField<6, 2, u32> gather_comp;
BitField<8, 1, u32> has_derivatives;
}; };
union BufferInstInfo { union BufferInstInfo {

View File

@ -64,7 +64,7 @@ IR::Program TranslateProgram(Common::ObjectPool<IR::Inst>& inst_pool,
Shader::Optimization::IdentityRemovalPass(program.blocks); Shader::Optimization::IdentityRemovalPass(program.blocks);
Shader::Optimization::DeadCodeEliminationPass(program); Shader::Optimization::DeadCodeEliminationPass(program);
Shader::Optimization::CollectShaderInfoPass(program); Shader::Optimization::CollectShaderInfoPass(program);
LOG_INFO(Render_Vulkan, "{}", Shader::IR::DumpProgram(program)); LOG_DEBUG(Render_Vulkan, "{}", Shader::IR::DumpProgram(program));
return program; return program;
} }

View File

@ -1,6 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#pragma clang optimize off
#include <xxhash.h> #include <xxhash.h>
#include "common/assert.h" #include "common/assert.h"
#include "video_core/buffer_cache/buffer_cache.h" #include "video_core/buffer_cache/buffer_cache.h"