video_core: Add image support

This commit is contained in:
raphaelthegreat 2024-05-27 01:07:46 +03:00
parent 729e166cd3
commit d59b102b6f
48 changed files with 1264 additions and 259 deletions

View file

@ -218,8 +218,8 @@ void PatchPhiNodes(IR::Program& program, EmitContext& ctx) {
}
} // Anonymous namespace
std::vector<u32> EmitSPIRV(const Profile& profile, IR::Program& program, Bindings& bindings) {
EmitContext ctx{profile, program, bindings};
std::vector<u32> EmitSPIRV(const Profile& profile, IR::Program& program, u32& binding) {
EmitContext ctx{profile, program, binding};
const Id main{DefineMain(ctx, program)};
DefineEntryPoint(program, ctx, main);
if (program.info.stage == Stage::Vertex) {

View file

@ -4,18 +4,12 @@
#pragma once
#include <vector>
#include "shader_recompiler/backend/bindings.h"
#include "shader_recompiler/ir/program.h"
#include "shader_recompiler/profile.h"
namespace Shader::Backend::SPIRV {
[[nodiscard]] std::vector<u32> EmitSPIRV(const Profile& profile, IR::Program& program,
Bindings& bindings);
[[nodiscard]] inline std::vector<u32> EmitSPIRV(const Profile& profile, IR::Program& program) {
Bindings binding;
return EmitSPIRV(profile, program, binding);
}
u32& binding);
} // namespace Shader::Backend::SPIRV

View file

@ -61,14 +61,11 @@ Id EmitReadConst(EmitContext& ctx) {
throw LogicError("Unreachable instruction");
}
Id EmitReadConstBuffer(EmitContext& ctx, const IR::Value& binding, const IR::Value& addr,
const IR::Value& offset) {
throw LogicError("Unreachable instruction");
}
Id EmitReadConstBufferF32(EmitContext& ctx, const IR::Value& binding, const IR::Value& addr,
const IR::Value& offset) {
throw LogicError("Unreachable instruction");
Id EmitReadConstBuffer(EmitContext& ctx, u32 handle, Id index) {
const Id buffer = ctx.buffers[handle];
const Id type = ctx.info.buffers[handle].is_storage ? ctx.storage_f32 : ctx.uniform_f32;
const Id ptr{ctx.OpAccessChain(type, buffer, ctx.ConstU32(0U), index)};
return ctx.OpLoad(ctx.F32[1], ptr);
}
Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, u32 comp) {
@ -99,32 +96,28 @@ void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, Id value, u32 elemen
ctx.OpStore(pointer, value);
}
Id EmitLoadBufferF32(EmitContext& ctx, IR::Inst* inst, const IR::Value& handle,
const IR::Value& address) {
Id EmitLoadBufferF32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address) {
UNREACHABLE();
}
Id EmitLoadBufferF32x2(EmitContext& ctx, IR::Inst* inst, const IR::Value& handle,
const IR::Value& address) {
Id EmitLoadBufferF32x2(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address) {
UNREACHABLE();
}
Id EmitLoadBufferF32x3(EmitContext& ctx, IR::Inst* inst, const IR::Value& handle,
const IR::Value& address) {
Id EmitLoadBufferF32x3(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address) {
UNREACHABLE();
}
Id EmitLoadBufferF32x4(EmitContext& ctx, IR::Inst* inst, const IR::Value& handle,
const IR::Value& address) {
Id EmitLoadBufferF32x4(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address) {
const auto info = inst->Flags<IR::BufferInstInfo>();
const Id buffer = ctx.buffers[handle.U32()];
const Id type = ctx.info.buffers[handle.U32()].is_storage ? ctx.storage_f32 : ctx.uniform_f32;
const Id buffer = ctx.buffers[handle];
const Id type = ctx.info.buffers[handle].is_storage ? ctx.storage_f32 : ctx.uniform_f32;
if (info.index_enable && info.offset_enable) {
UNREACHABLE();
} else if (info.index_enable) {
boost::container::static_vector<Id, 4> ids;
for (u32 i = 0; i < 4; i++) {
const Id index{ctx.OpIAdd(ctx.U32[1], ctx.Def(address), ctx.ConstU32(i))};
const Id index{ctx.OpIAdd(ctx.U32[1], address, ctx.ConstU32(i))};
const Id ptr{ctx.OpAccessChain(type, buffer, ctx.ConstU32(0U), index)};
ids.push_back(ctx.OpLoad(ctx.F32[1], ptr));
}

View file

@ -6,9 +6,14 @@
namespace Shader::Backend::SPIRV {
Id EmitImageSampleImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
Id bias_lc, const IR::Value& offset) {
throw NotImplementedException("SPIR-V Instruction");
Id EmitImageSampleImplicitLod(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id bias_lc,
Id offset) {
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);
const auto info = inst->Flags<IR::TextureInstInfo>();
return ctx.OpImageSampleImplicitLod(ctx.F32[4], sampled_image, coords);
}
Id EmitImageSampleExplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,

View file

@ -43,18 +43,11 @@ void EmitSetGotoVariable(EmitContext& ctx);
void EmitGetGotoVariable(EmitContext& ctx);
void EmitSetScc(EmitContext& ctx);
Id EmitReadConst(EmitContext& ctx);
Id EmitReadConstBuffer(EmitContext& ctx, const IR::Value& handle, const IR::Value& index,
const IR::Value& offset);
Id EmitReadConstBufferF32(EmitContext& ctx, const IR::Value& handle, const IR::Value& index,
const IR::Value& offset);
Id EmitLoadBufferF32(EmitContext& ctx, IR::Inst* inst, const IR::Value& handle,
const IR::Value& address);
Id EmitLoadBufferF32x2(EmitContext& ctx, IR::Inst* inst, const IR::Value& handle,
const IR::Value& address);
Id EmitLoadBufferF32x3(EmitContext& ctx, IR::Inst* inst, const IR::Value& handle,
const IR::Value& address);
Id EmitLoadBufferF32x4(EmitContext& ctx, IR::Inst* inst, const IR::Value& handle,
const IR::Value& address);
Id EmitReadConstBuffer(EmitContext& ctx, u32 handle, Id index);
Id EmitLoadBufferF32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address);
Id EmitLoadBufferF32x2(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address);
Id EmitLoadBufferF32x3(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address);
Id EmitLoadBufferF32x4(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address);
Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, u32 comp);
Id EmitGetAttributeU32(EmitContext& ctx, IR::Attribute attr, u32 comp);
void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, Id value, u32 comp);
@ -319,8 +312,8 @@ Id EmitConvertF64U16(EmitContext& ctx, Id value);
Id EmitConvertF64U32(EmitContext& ctx, Id value);
Id EmitConvertF64U64(EmitContext& ctx, Id value);
Id EmitImageSampleImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
Id bias_lc, const IR::Value& offset);
Id EmitImageSampleImplicitLod(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id bias_lc,
Id offset);
Id EmitImageSampleExplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
Id lod, const IR::Value& offset);
Id EmitImageSampleDrefImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index,

View file

@ -35,17 +35,14 @@ void Name(EmitContext& ctx, Id object, std::string_view format_str, Args&&... ar
} // Anonymous namespace
EmitContext::EmitContext(const Profile& profile_, IR::Program& program, Bindings& bindings)
EmitContext::EmitContext(const Profile& profile_, IR::Program& program, u32& binding_)
: Sirit::Module(profile_.supported_spirv), info{program.info}, profile{profile_},
stage{program.info.stage} {
u32& uniform_binding{bindings.unified};
u32& storage_binding{bindings.unified};
u32& texture_binding{bindings.unified};
u32& image_binding{bindings.unified};
stage{program.info.stage}, binding{binding_} {
AddCapability(spv::Capability::Shader);
DefineArithmeticTypes();
DefineInterfaces(program);
DefineBuffers(program.info);
DefineImagesAndSamplers(program.info);
}
EmitContext::~EmitContext() = default;
@ -235,16 +232,15 @@ void EmitContext::DefineOutputs(const Info& info) {
}
void EmitContext::DefineBuffers(const Info& info) {
const auto define_buffer = [&](const BufferResource& buffer, Id type, u32 element_size,
char type_char, u32 index) {
ASSERT(buffer.stride % element_size == 0);
const u32 num_elements = buffer.stride * buffer.num_records / element_size;
for (u32 i = 0; const auto& buffer : info.buffers) {
ASSERT(True(buffer.used_types & IR::Type::F32));
ASSERT(buffer.stride % sizeof(float) == 0);
const u32 num_elements = buffer.stride * buffer.num_records / sizeof(float);
const Id record_array_type{TypeArray(F32[1], ConstU32(num_elements))};
Decorate(record_array_type, spv::Decoration::ArrayStride, element_size);
Decorate(record_array_type, spv::Decoration::ArrayStride, sizeof(float));
const Id struct_type{TypeStruct(record_array_type)};
const auto name =
fmt::format("{}_cbuf_block_{}{}", stage, type_char, element_size * CHAR_BIT);
const auto name = fmt::format("{}_cbuf_block_{}{}", stage, 'f', sizeof(float) * CHAR_BIT);
Name(struct_type, name);
Decorate(struct_type, spv::Decoration::Block);
MemberName(struct_type, 0, "data");
@ -254,25 +250,112 @@ void EmitContext::DefineBuffers(const Info& info) {
buffer.is_storage ? spv::StorageClass::StorageBuffer : spv::StorageClass::Uniform;
const Id struct_pointer_type{TypePointer(storage_class, struct_type)};
if (buffer.is_storage) {
storage_f32 = TypePointer(storage_class, type);
storage_f32 = TypePointer(storage_class, F32[1]);
} else {
uniform_f32 = TypePointer(storage_class, type);
uniform_f32 = TypePointer(storage_class, F32[1]);
}
const Id id{AddGlobalVariable(struct_pointer_type, storage_class)};
Decorate(id, spv::Decoration::Binding, binding);
Decorate(id, spv::Decoration::DescriptorSet, 0U);
Name(id, fmt::format("c{}", index));
Name(id, fmt::format("c{}", i));
binding++;
buffers.push_back(id);
interfaces.push_back(id);
};
for (u32 i = 0; const auto& buffer : info.buffers) {
ASSERT(True(buffer.used_types & IR::Type::F32));
define_buffer(buffer, F32[1], 4, 'f', i);
i++;
}
}
Id ImageType(EmitContext& ctx, const ImageResource& desc) {
const spv::ImageFormat format{spv::ImageFormat::Unknown};
const Id type{ctx.F32[1]};
const bool depth{desc.is_depth};
switch (desc.type) {
case AmdGpu::ImageType::Color1D:
return ctx.TypeImage(type, spv::Dim::Dim1D, depth, false, false, 1, format,
spv::AccessQualifier::ReadOnly);
case AmdGpu::ImageType::Color1DArray:
return ctx.TypeImage(type, spv::Dim::Dim1D, depth, true, false, 1, format,
spv::AccessQualifier::ReadOnly);
case AmdGpu::ImageType::Color2D:
case AmdGpu::ImageType::Color2DMsaa:
return ctx.TypeImage(type, spv::Dim::Dim2D, depth, false,
desc.type == AmdGpu::ImageType::Color2DMsaa, 1, format,
spv::AccessQualifier::ReadOnly);
case AmdGpu::ImageType::Color2DArray:
case AmdGpu::ImageType::Color2DMsaaArray:
return ctx.TypeImage(type, spv::Dim::Dim2D, depth, true,
desc.type == AmdGpu::ImageType::Color2DMsaaArray, 1, format,
spv::AccessQualifier::ReadOnly);
case AmdGpu::ImageType::Color3D:
return ctx.TypeImage(type, spv::Dim::Dim3D, depth, false, false, 1, format,
spv::AccessQualifier::ReadOnly);
case AmdGpu::ImageType::Cube:
return ctx.TypeImage(type, spv::Dim::Cube, depth, false, false, 1, format,
spv::AccessQualifier::ReadOnly);
case AmdGpu::ImageType::Buffer:
break;
}
throw InvalidArgument("Invalid texture type {}", desc.type);
}
Id ImageType(EmitContext& ctx, const ImageResource& desc, Id sampled_type) {
const auto format = spv::ImageFormat::Unknown; // Read this from tsharp?
switch (desc.type) {
case AmdGpu::ImageType::Color1D:
return ctx.TypeImage(sampled_type, spv::Dim::Dim1D, false, false, false, 1, format);
case AmdGpu::ImageType::Color1DArray:
return ctx.TypeImage(sampled_type, spv::Dim::Dim1D, false, true, false, 1, format);
case AmdGpu::ImageType::Color2D:
return ctx.TypeImage(sampled_type, spv::Dim::Dim2D, false, false, false, 1, format);
case AmdGpu::ImageType::Color2DArray:
return ctx.TypeImage(sampled_type, spv::Dim::Dim2D, false, true, false, 1, format);
case AmdGpu::ImageType::Color3D:
return ctx.TypeImage(sampled_type, spv::Dim::Dim3D, false, false, false, 2, format);
case AmdGpu::ImageType::Buffer:
throw NotImplementedException("Image buffer");
default:
break;
}
throw InvalidArgument("Invalid texture type {}", desc.type);
}
void EmitContext::DefineImagesAndSamplers(const Info& info) {
for (const auto& image_desc : info.images) {
const Id sampled_type{image_desc.nfmt == AmdGpu::NumberFormat::Uint ? U32[1] : F32[1]};
const Id image_type{ImageType(*this, image_desc, sampled_type)};
const Id pointer_type{TypePointer(spv::StorageClass::UniformConstant, image_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("{}_{}{}_{:02x}", stage, "img", image_desc.sgpr_base,
image_desc.dword_offset));
images.push_back({
.id = id,
.sampled_type = TypeSampledImage(image_type),
.pointer_type = pointer_type,
.image_type = image_type,
});
interfaces.push_back(id);
++binding;
}
if (info.samplers.empty()) {
return;
}
sampler_type = TypeSampler();
sampler_pointer_type = TypePointer(spv::StorageClass::UniformConstant, sampler_type);
for (const auto& samp_desc : info.samplers) {
const Id id{AddGlobalVariable(sampler_pointer_type, spv::StorageClass::UniformConstant)};
Decorate(id, spv::Decoration::Binding, binding);
Decorate(id, spv::Decoration::DescriptorSet, 0U);
Name(id, fmt::format("{}_{}{}_{:02x}", stage, "samp", samp_desc.sgpr_base,
samp_desc.dword_offset));
samplers.push_back(id);
interfaces.push_back(id);
++binding;
}
}
} // namespace Shader::Backend::SPIRV

View file

@ -6,7 +6,6 @@
#include <array>
#include <sirit/sirit.h>
#include "shader_recompiler/backend/bindings.h"
#include "shader_recompiler/ir/program.h"
#include "shader_recompiler/profile.h"
#include "shader_recompiler/runtime_info.h"
@ -29,7 +28,7 @@ struct VectorIds {
class EmitContext final : public Sirit::Module {
public:
explicit EmitContext(const Profile& profile, IR::Program& program, Bindings& binding);
explicit EmitContext(const Profile& profile, IR::Program& program, u32& binding);
~EmitContext();
Id Def(const IR::Value& value);
@ -152,8 +151,20 @@ public:
Id base_vertex{};
std::array<Id, 8> frag_color{};
u32 binding{};
struct TextureDefinition {
Id id;
Id sampled_type;
Id pointer_type;
Id image_type;
};
u32& binding;
boost::container::small_vector<Id, 4> buffers;
boost::container::small_vector<TextureDefinition, 4> images;
boost::container::small_vector<Id, 4> samplers;
Id sampler_type{};
Id sampler_pointer_type{};
struct SpirvAttribute {
Id id;
@ -170,6 +181,7 @@ private:
void DefineInputs(const Info& info);
void DefineOutputs(const Info& info);
void DefineBuffers(const Info& info);
void DefineImagesAndSamplers(const Info& info);
SpirvAttribute GetAttributeInfo(AmdGpu::NumberFormat fmt, Id id);
};