3
0
Fork 0
forked from suyu/suyu

shader: Implement indexed textures

This commit is contained in:
ReinUsesLisp 2021-04-22 16:17:59 -03:00 committed by ameerj
parent 7a9dc78398
commit d10cf55353
10 changed files with 284 additions and 157 deletions

View file

@ -380,6 +380,24 @@ Id CasLoop(EmitContext& ctx, Operation operation, Id array_pointer, Id element_p
ctx.OpFunctionEnd(); ctx.OpFunctionEnd();
return func; return func;
} }
template <typename Desc>
std::string NameOf(const Desc& desc, std::string_view prefix) {
if (desc.count > 1) {
return fmt::format("{}{}_{:02x}x{}", prefix, desc.cbuf_index, desc.cbuf_offset, desc.count);
} else {
return fmt::format("{}{}_{:02x}", prefix, desc.cbuf_index, desc.cbuf_offset);
}
}
Id DescType(EmitContext& ctx, Id sampled_type, Id pointer_type, u32 count) {
if (count > 1) {
const Id array_type{ctx.TypeArray(sampled_type, ctx.Const(count))};
return ctx.TypePointer(spv::StorageClass::UniformConstant, array_type);
} else {
return pointer_type;
}
}
} // Anonymous namespace } // Anonymous namespace
void VectorTypes::Define(Sirit::Module& sirit_ctx, Id base_type, std::string_view name) { void VectorTypes::Define(Sirit::Module& sirit_ctx, Id base_type, std::string_view name) {
@ -971,12 +989,15 @@ void EmitContext::DefineTextureBuffers(const Info& info, u32& binding) {
const Id id{AddGlobalVariable(type, spv::StorageClass::UniformConstant)}; const Id id{AddGlobalVariable(type, spv::StorageClass::UniformConstant)};
Decorate(id, spv::Decoration::Binding, binding); Decorate(id, spv::Decoration::Binding, binding);
Decorate(id, spv::Decoration::DescriptorSet, 0U); Decorate(id, spv::Decoration::DescriptorSet, 0U);
Name(id, fmt::format("texbuf{}_{:02x}", desc.cbuf_index, desc.cbuf_offset)); Name(id, NameOf(desc, "texbuf"));
texture_buffers.insert(texture_buffers.end(), desc.count, id); texture_buffers.push_back({
.id = id,
.count = desc.count,
});
if (profile.supported_spirv >= 0x00010400) { if (profile.supported_spirv >= 0x00010400) {
interfaces.push_back(id); interfaces.push_back(id);
} }
binding += desc.count; ++binding;
} }
} }
@ -992,44 +1013,41 @@ void EmitContext::DefineImageBuffers(const Info& info, u32& binding) {
const Id id{AddGlobalVariable(pointer_type, spv::StorageClass::UniformConstant)}; const Id id{AddGlobalVariable(pointer_type, spv::StorageClass::UniformConstant)};
Decorate(id, spv::Decoration::Binding, binding); Decorate(id, spv::Decoration::Binding, binding);
Decorate(id, spv::Decoration::DescriptorSet, 0U); Decorate(id, spv::Decoration::DescriptorSet, 0U);
Name(id, fmt::format("imgbuf{}_{:02x}", desc.cbuf_index, desc.cbuf_offset)); Name(id, NameOf(desc, "imgbuf"));
const ImageBufferDefinition def{ image_buffers.push_back({
.id = id, .id = id,
.image_type = image_type, .image_type = image_type,
}; .count = desc.count,
image_buffers.insert(image_buffers.end(), desc.count, def); });
if (profile.supported_spirv >= 0x00010400) { if (profile.supported_spirv >= 0x00010400) {
interfaces.push_back(id); interfaces.push_back(id);
} }
binding += desc.count; ++binding;
} }
} }
void EmitContext::DefineTextures(const Info& info, u32& binding) { void EmitContext::DefineTextures(const Info& info, u32& binding) {
textures.reserve(info.texture_descriptors.size()); textures.reserve(info.texture_descriptors.size());
for (const TextureDescriptor& desc : info.texture_descriptors) { for (const TextureDescriptor& desc : info.texture_descriptors) {
if (desc.count != 1) {
throw NotImplementedException("Array of textures");
}
const Id image_type{ImageType(*this, desc)}; const Id image_type{ImageType(*this, desc)};
const Id sampled_type{TypeSampledImage(image_type)}; const Id sampled_type{TypeSampledImage(image_type)};
const Id pointer_type{TypePointer(spv::StorageClass::UniformConstant, sampled_type)}; const Id pointer_type{TypePointer(spv::StorageClass::UniformConstant, sampled_type)};
const Id id{AddGlobalVariable(pointer_type, spv::StorageClass::UniformConstant)}; const Id desc_type{DescType(*this, sampled_type, pointer_type, desc.count)};
const Id id{AddGlobalVariable(desc_type, spv::StorageClass::UniformConstant)};
Decorate(id, spv::Decoration::Binding, binding); Decorate(id, spv::Decoration::Binding, binding);
Decorate(id, spv::Decoration::DescriptorSet, 0U); Decorate(id, spv::Decoration::DescriptorSet, 0U);
Name(id, fmt::format("tex{}_{:02x}", desc.cbuf_index, desc.cbuf_offset)); Name(id, NameOf(desc, "tex"));
for (u32 index = 0; index < desc.count; ++index) { textures.push_back({
// TODO: Pass count info .id = id,
textures.push_back(TextureDefinition{ .sampled_type = sampled_type,
.id{id}, .pointer_type = pointer_type,
.sampled_type{sampled_type}, .image_type = image_type,
.image_type{image_type}, .count = desc.count,
}); });
}
if (profile.supported_spirv >= 0x00010400) { if (profile.supported_spirv >= 0x00010400) {
interfaces.push_back(id); interfaces.push_back(id);
} }
binding += desc.count; ++binding;
} }
} }
@ -1037,24 +1055,23 @@ void EmitContext::DefineImages(const Info& info, u32& binding) {
images.reserve(info.image_descriptors.size()); images.reserve(info.image_descriptors.size());
for (const ImageDescriptor& desc : info.image_descriptors) { for (const ImageDescriptor& desc : info.image_descriptors) {
if (desc.count != 1) { if (desc.count != 1) {
throw NotImplementedException("Array of textures"); throw NotImplementedException("Array of images");
} }
const Id image_type{ImageType(*this, desc)}; const Id image_type{ImageType(*this, desc)};
const Id pointer_type{TypePointer(spv::StorageClass::UniformConstant, image_type)}; const Id pointer_type{TypePointer(spv::StorageClass::UniformConstant, image_type)};
const Id id{AddGlobalVariable(pointer_type, spv::StorageClass::UniformConstant)}; const Id id{AddGlobalVariable(pointer_type, spv::StorageClass::UniformConstant)};
Decorate(id, spv::Decoration::Binding, binding); Decorate(id, spv::Decoration::Binding, binding);
Decorate(id, spv::Decoration::DescriptorSet, 0U); Decorate(id, spv::Decoration::DescriptorSet, 0U);
Name(id, fmt::format("img{}_{:02x}", desc.cbuf_index, desc.cbuf_offset)); Name(id, NameOf(desc, "img"));
for (u32 index = 0; index < desc.count; ++index) { images.push_back({
images.push_back(ImageDefinition{ .id = id,
.id{id}, .image_type = image_type,
.image_type{image_type}, .count = desc.count,
}); });
}
if (profile.supported_spirv >= 0x00010400) { if (profile.supported_spirv >= 0x00010400) {
interfaces.push_back(id); interfaces.push_back(id);
} }
binding += desc.count; ++binding;
} }
} }

View file

@ -32,17 +32,26 @@ private:
struct TextureDefinition { struct TextureDefinition {
Id id; Id id;
Id sampled_type; Id sampled_type;
Id pointer_type;
Id image_type; Id image_type;
u32 count;
};
struct TextureBufferDefinition {
Id id;
u32 count;
}; };
struct ImageBufferDefinition { struct ImageBufferDefinition {
Id id; Id id;
Id image_type; Id image_type;
u32 count;
}; };
struct ImageDefinition { struct ImageDefinition {
Id id; Id id;
Id image_type; Id image_type;
u32 count;
}; };
struct UniformDefinitions { struct UniformDefinitions {
@ -162,7 +171,7 @@ public:
std::array<UniformDefinitions, Info::MAX_CBUFS> cbufs{}; std::array<UniformDefinitions, Info::MAX_CBUFS> cbufs{};
std::array<StorageDefinitions, Info::MAX_SSBOS> ssbos{}; std::array<StorageDefinitions, Info::MAX_SSBOS> ssbos{};
std::vector<Id> texture_buffers; std::vector<TextureBufferDefinition> texture_buffers;
std::vector<ImageBufferDefinition> image_buffers; std::vector<ImageBufferDefinition> image_buffers;
std::vector<TextureDefinition> textures; std::vector<TextureDefinition> textures;
std::vector<ImageDefinition> images; std::vector<ImageDefinition> images;

View file

@ -147,24 +147,31 @@ private:
spv::ImageOperandsMask mask{}; spv::ImageOperandsMask mask{};
}; };
Id Texture(EmitContext& ctx, const IR::Value& index) { Id Texture(EmitContext& ctx, IR::TextureInstInfo info, [[maybe_unused]] const IR::Value& index) {
if (index.IsImmediate()) { const TextureDefinition& def{ctx.textures.at(info.descriptor_index)};
const TextureDefinition def{ctx.textures.at(index.U32())}; if (def.count > 1) {
const Id pointer{ctx.OpAccessChain(def.pointer_type, def.id, ctx.Def(index))};
return ctx.OpLoad(def.sampled_type, pointer);
} else {
return ctx.OpLoad(def.sampled_type, def.id); return ctx.OpLoad(def.sampled_type, def.id);
} }
throw NotImplementedException("Indirect texture sample");
} }
Id TextureImage(EmitContext& ctx, const IR::Value& index, IR::TextureInstInfo info) { Id TextureImage(EmitContext& ctx, IR::TextureInstInfo info,
if (!index.IsImmediate()) { [[maybe_unused]] const IR::Value& index) {
throw NotImplementedException("Indirect texture sample");
}
if (info.type == TextureType::Buffer) { if (info.type == TextureType::Buffer) {
const Id sampler_id{ctx.texture_buffers.at(index.U32())}; const TextureBufferDefinition& def{ctx.texture_buffers.at(info.descriptor_index)};
if (def.count > 1) {
throw NotImplementedException("Indirect texture sample");
}
const Id sampler_id{def.id};
const Id id{ctx.OpLoad(ctx.sampled_texture_buffer_type, sampler_id)}; const Id id{ctx.OpLoad(ctx.sampled_texture_buffer_type, sampler_id)};
return ctx.OpImage(ctx.image_buffer_type, id); return ctx.OpImage(ctx.image_buffer_type, id);
} else { } else {
const TextureDefinition def{ctx.textures.at(index.U32())}; const TextureDefinition& def{ctx.textures.at(info.descriptor_index)};
if (def.count > 1) {
throw NotImplementedException("Indirect texture sample");
}
return ctx.OpImage(def.image_type, ctx.OpLoad(def.sampled_type, def.id)); return ctx.OpImage(def.image_type, ctx.OpLoad(def.sampled_type, def.id));
} }
} }
@ -311,7 +318,7 @@ Id EmitImageSampleImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value&
bias_lc, offset); bias_lc, offset);
return Emit(&EmitContext::OpImageSparseSampleImplicitLod, return Emit(&EmitContext::OpImageSparseSampleImplicitLod,
&EmitContext::OpImageSampleImplicitLod, ctx, inst, ctx.F32[4], &EmitContext::OpImageSampleImplicitLod, ctx, inst, ctx.F32[4],
Texture(ctx, index), coords, operands.Mask(), operands.Span()); Texture(ctx, info, index), coords, operands.Mask(), operands.Span());
} else { } else {
// We can't use implicit lods on non-fragment stages on SPIR-V. Maxwell hardware behaves as // We can't use implicit lods on non-fragment stages on SPIR-V. Maxwell hardware behaves as
// if the lod was explicitly zero. This may change on Turing with implicit compute // if the lod was explicitly zero. This may change on Turing with implicit compute
@ -320,7 +327,7 @@ Id EmitImageSampleImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value&
const ImageOperands operands(ctx, false, true, info.has_lod_clamp != 0, lod, offset); const ImageOperands operands(ctx, false, true, info.has_lod_clamp != 0, lod, offset);
return Emit(&EmitContext::OpImageSparseSampleExplicitLod, return Emit(&EmitContext::OpImageSparseSampleExplicitLod,
&EmitContext::OpImageSampleExplicitLod, ctx, inst, ctx.F32[4], &EmitContext::OpImageSampleExplicitLod, ctx, inst, ctx.F32[4],
Texture(ctx, index), coords, operands.Mask(), operands.Span()); Texture(ctx, info, index), coords, operands.Mask(), operands.Span());
} }
} }
@ -329,8 +336,8 @@ Id EmitImageSampleExplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value&
const auto info{inst->Flags<IR::TextureInstInfo>()}; const auto info{inst->Flags<IR::TextureInstInfo>()};
const ImageOperands operands(ctx, false, true, info.has_lod_clamp != 0, lod_lc, offset); const ImageOperands operands(ctx, false, true, info.has_lod_clamp != 0, lod_lc, offset);
return Emit(&EmitContext::OpImageSparseSampleExplicitLod, return Emit(&EmitContext::OpImageSparseSampleExplicitLod,
&EmitContext::OpImageSampleExplicitLod, ctx, inst, ctx.F32[4], Texture(ctx, index), &EmitContext::OpImageSampleExplicitLod, ctx, inst, ctx.F32[4],
coords, operands.Mask(), operands.Span()); Texture(ctx, info, index), coords, operands.Mask(), operands.Span());
} }
Id EmitImageSampleDrefImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id EmitImageSampleDrefImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index,
@ -340,7 +347,7 @@ Id EmitImageSampleDrefImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Va
offset); offset);
return Emit(&EmitContext::OpImageSparseSampleDrefImplicitLod, return Emit(&EmitContext::OpImageSparseSampleDrefImplicitLod,
&EmitContext::OpImageSampleDrefImplicitLod, ctx, inst, ctx.F32[1], &EmitContext::OpImageSampleDrefImplicitLod, ctx, inst, ctx.F32[1],
Texture(ctx, index), coords, dref, operands.Mask(), operands.Span()); Texture(ctx, info, index), coords, dref, operands.Mask(), operands.Span());
} }
Id EmitImageSampleDrefExplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id EmitImageSampleDrefExplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index,
@ -349,7 +356,7 @@ Id EmitImageSampleDrefExplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Va
const ImageOperands operands(ctx, false, true, info.has_lod_clamp != 0, lod_lc, offset); const ImageOperands operands(ctx, false, true, info.has_lod_clamp != 0, lod_lc, offset);
return Emit(&EmitContext::OpImageSparseSampleDrefExplicitLod, return Emit(&EmitContext::OpImageSparseSampleDrefExplicitLod,
&EmitContext::OpImageSampleDrefExplicitLod, ctx, inst, ctx.F32[1], &EmitContext::OpImageSampleDrefExplicitLod, ctx, inst, ctx.F32[1],
Texture(ctx, index), coords, dref, operands.Mask(), operands.Span()); Texture(ctx, info, index), coords, dref, operands.Mask(), operands.Span());
} }
Id EmitImageGather(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id EmitImageGather(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
@ -357,15 +364,17 @@ Id EmitImageGather(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id
const auto info{inst->Flags<IR::TextureInstInfo>()}; const auto info{inst->Flags<IR::TextureInstInfo>()};
const ImageOperands operands(ctx, offset, offset2); const ImageOperands operands(ctx, offset, offset2);
return Emit(&EmitContext::OpImageSparseGather, &EmitContext::OpImageGather, ctx, inst, return Emit(&EmitContext::OpImageSparseGather, &EmitContext::OpImageGather, ctx, inst,
ctx.F32[4], Texture(ctx, index), coords, ctx.Const(info.gather_component), ctx.F32[4], Texture(ctx, info, index), coords, ctx.Const(info.gather_component),
operands.Mask(), operands.Span()); operands.Mask(), operands.Span());
} }
Id EmitImageGatherDref(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id EmitImageGatherDref(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
const IR::Value& offset, const IR::Value& offset2, Id dref) { const IR::Value& offset, const IR::Value& offset2, Id dref) {
const auto info{inst->Flags<IR::TextureInstInfo>()};
const ImageOperands operands(ctx, offset, offset2); const ImageOperands operands(ctx, offset, offset2);
return Emit(&EmitContext::OpImageSparseDrefGather, &EmitContext::OpImageDrefGather, ctx, inst, return Emit(&EmitContext::OpImageSparseDrefGather, &EmitContext::OpImageDrefGather, ctx, inst,
ctx.F32[4], Texture(ctx, index), coords, dref, operands.Mask(), operands.Span()); ctx.F32[4], Texture(ctx, info, index), coords, dref, operands.Mask(),
operands.Span());
} }
Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id offset, Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id offset,
@ -376,12 +385,12 @@ Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id c
} }
const ImageOperands operands(offset, lod, ms); const ImageOperands operands(offset, lod, ms);
return Emit(&EmitContext::OpImageSparseFetch, &EmitContext::OpImageFetch, ctx, inst, ctx.F32[4], return Emit(&EmitContext::OpImageSparseFetch, &EmitContext::OpImageFetch, ctx, inst, ctx.F32[4],
TextureImage(ctx, index, info), coords, operands.Mask(), operands.Span()); TextureImage(ctx, info, index), coords, operands.Mask(), operands.Span());
} }
Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id lod) { Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id lod) {
const auto info{inst->Flags<IR::TextureInstInfo>()}; const auto info{inst->Flags<IR::TextureInstInfo>()};
const Id image{TextureImage(ctx, index, info)}; const Id image{TextureImage(ctx, info, index)};
const Id zero{ctx.u32_zero_value}; const Id zero{ctx.u32_zero_value};
const auto mips{[&] { return ctx.OpImageQueryLevels(ctx.U32[1], image); }}; const auto mips{[&] { return ctx.OpImageQueryLevels(ctx.U32[1], image); }};
switch (info.type) { switch (info.type) {
@ -405,9 +414,10 @@ Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& i
throw LogicError("Unspecified image type {}", info.type.Value()); throw LogicError("Unspecified image type {}", info.type.Value());
} }
Id EmitImageQueryLod(EmitContext& ctx, IR::Inst*, const IR::Value& index, Id coords) { Id EmitImageQueryLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords) {
const auto info{inst->Flags<IR::TextureInstInfo>()};
const Id zero{ctx.f32_zero_value}; const Id zero{ctx.f32_zero_value};
const Id sampler{Texture(ctx, index)}; const Id sampler{Texture(ctx, info, index)};
return ctx.OpCompositeConstruct(ctx.F32[4], ctx.OpImageQueryLod(ctx.F32[2], sampler, coords), return ctx.OpCompositeConstruct(ctx.F32[4], ctx.OpImageQueryLod(ctx.F32[2], sampler, coords),
zero, zero); zero, zero);
} }
@ -418,8 +428,8 @@ Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, I
const ImageOperands operands(ctx, info.has_lod_clamp != 0, derivates, info.num_derivates, const ImageOperands operands(ctx, info.has_lod_clamp != 0, derivates, info.num_derivates,
offset, lod_clamp); offset, lod_clamp);
return Emit(&EmitContext::OpImageSparseSampleExplicitLod, return Emit(&EmitContext::OpImageSparseSampleExplicitLod,
&EmitContext::OpImageSampleExplicitLod, ctx, inst, ctx.F32[4], Texture(ctx, index), &EmitContext::OpImageSampleExplicitLod, ctx, inst, ctx.F32[4],
coords, operands.Mask(), operands.Span()); Texture(ctx, info, index), coords, operands.Mask(), operands.Span());
} }
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

@ -34,14 +34,15 @@ static_assert(sizeof(FpControl) <= sizeof(u32));
union TextureInstInfo { union TextureInstInfo {
u32 raw; u32 raw;
BitField<0, 8, TextureType> type; BitField<0, 16, u32> descriptor_index;
BitField<8, 1, u32> is_depth; BitField<16, 3, TextureType> type;
BitField<9, 1, u32> has_bias; BitField<19, 1, u32> is_depth;
BitField<10, 1, u32> has_lod_clamp; BitField<20, 1, u32> has_bias;
BitField<11, 1, u32> relaxed_precision; BitField<21, 1, u32> has_lod_clamp;
BitField<12, 2, u32> gather_component; BitField<22, 1, u32> relaxed_precision;
BitField<14, 2, u32> num_derivates; BitField<23, 2, u32> gather_component;
BitField<16, 3, ImageFormat> image_format; BitField<25, 2, u32> num_derivates;
BitField<27, 3, ImageFormat> image_format;
}; };
static_assert(sizeof(TextureInstInfo) <= sizeof(u32)); static_assert(sizeof(TextureInstInfo) <= sizeof(u32));

View file

@ -482,18 +482,18 @@ OPCODE(BoundImageGradient, F32x4, U32,
OPCODE(BoundImageRead, U32x4, U32, Opaque, ) OPCODE(BoundImageRead, U32x4, U32, Opaque, )
OPCODE(BoundImageWrite, Void, U32, Opaque, U32x4, ) OPCODE(BoundImageWrite, Void, U32, Opaque, U32x4, )
OPCODE(ImageSampleImplicitLod, F32x4, U32, Opaque, Opaque, Opaque, ) OPCODE(ImageSampleImplicitLod, F32x4, Opaque, Opaque, Opaque, Opaque, )
OPCODE(ImageSampleExplicitLod, F32x4, U32, Opaque, Opaque, Opaque, ) OPCODE(ImageSampleExplicitLod, F32x4, Opaque, Opaque, Opaque, Opaque, )
OPCODE(ImageSampleDrefImplicitLod, F32, U32, Opaque, F32, Opaque, Opaque, ) OPCODE(ImageSampleDrefImplicitLod, F32, Opaque, Opaque, F32, Opaque, Opaque, )
OPCODE(ImageSampleDrefExplicitLod, F32, U32, Opaque, F32, Opaque, Opaque, ) OPCODE(ImageSampleDrefExplicitLod, F32, Opaque, Opaque, F32, Opaque, Opaque, )
OPCODE(ImageGather, F32x4, U32, Opaque, Opaque, Opaque, ) OPCODE(ImageGather, F32x4, Opaque, Opaque, Opaque, Opaque, )
OPCODE(ImageGatherDref, F32x4, U32, Opaque, Opaque, Opaque, F32, ) OPCODE(ImageGatherDref, F32x4, Opaque, Opaque, Opaque, Opaque, F32, )
OPCODE(ImageFetch, F32x4, U32, Opaque, Opaque, U32, Opaque, ) OPCODE(ImageFetch, F32x4, Opaque, Opaque, Opaque, U32, Opaque, )
OPCODE(ImageQueryDimensions, U32x4, U32, U32, ) OPCODE(ImageQueryDimensions, U32x4, Opaque, U32, )
OPCODE(ImageQueryLod, F32x4, U32, Opaque, ) OPCODE(ImageQueryLod, F32x4, Opaque, Opaque, )
OPCODE(ImageGradient, F32x4, U32, Opaque, Opaque, Opaque, Opaque, ) OPCODE(ImageGradient, F32x4, Opaque, Opaque, Opaque, Opaque, Opaque, )
OPCODE(ImageRead, U32x4, U32, Opaque, ) OPCODE(ImageRead, U32x4, Opaque, Opaque, )
OPCODE(ImageWrite, Void, U32, Opaque, U32x4, ) OPCODE(ImageWrite, Void, Opaque, Opaque, U32x4, )
// Warp operations // Warp operations
OPCODE(LaneId, U32, ) OPCODE(LaneId, U32, )

View file

@ -3,6 +3,7 @@
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include <algorithm> #include <algorithm>
#include <bit>
#include <optional> #include <optional>
#include <boost/container/small_vector.hpp> #include <boost/container/small_vector.hpp>
@ -21,6 +22,8 @@ struct ConstBufferAddr {
u32 offset; u32 offset;
u32 secondary_index; u32 secondary_index;
u32 secondary_offset; u32 secondary_offset;
IR::U32 dynamic_offset;
u32 count;
bool has_secondary; bool has_secondary;
}; };
@ -32,6 +35,9 @@ struct TextureInst {
using TextureInstVector = boost::container::small_vector<TextureInst, 24>; using TextureInstVector = boost::container::small_vector<TextureInst, 24>;
constexpr u32 DESCRIPTOR_SIZE = 8;
constexpr u32 DESCRIPTOR_SIZE_SHIFT = static_cast<u32>(std::countr_zero(DESCRIPTOR_SIZE));
IR::Opcode IndexedInstruction(const IR::Inst& inst) { IR::Opcode IndexedInstruction(const IR::Inst& inst) {
switch (inst.GetOpcode()) { switch (inst.GetOpcode()) {
case IR::Opcode::BindlessImageSampleImplicitLod: case IR::Opcode::BindlessImageSampleImplicitLod:
@ -131,6 +137,9 @@ std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst) {
if (lhs->has_secondary || rhs->has_secondary) { if (lhs->has_secondary || rhs->has_secondary) {
return std::nullopt; return std::nullopt;
} }
if (lhs->count > 1 || rhs->count > 1) {
return std::nullopt;
}
if (lhs->index > rhs->index || lhs->offset > rhs->offset) { if (lhs->index > rhs->index || lhs->offset > rhs->offset) {
std::swap(lhs, rhs); std::swap(lhs, rhs);
} }
@ -139,9 +148,12 @@ std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst) {
.offset = lhs->offset, .offset = lhs->offset,
.secondary_index = rhs->index, .secondary_index = rhs->index,
.secondary_offset = rhs->offset, .secondary_offset = rhs->offset,
.dynamic_offset = {},
.count = 1,
.has_secondary = true, .has_secondary = true,
}; };
} }
case IR::Opcode::GetCbufU32x2:
case IR::Opcode::GetCbufU32: case IR::Opcode::GetCbufU32:
break; break;
} }
@ -152,15 +164,39 @@ std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst) {
// but not supported here at the moment // but not supported here at the moment
return std::nullopt; return std::nullopt;
} }
if (!offset.IsImmediate()) { if (offset.IsImmediate()) {
// TODO: Support arrays of textures return ConstBufferAddr{
.index = index.U32(),
.offset = offset.U32(),
.secondary_index = 0,
.secondary_offset = 0,
.dynamic_offset = {},
.count = 1,
.has_secondary = false,
};
}
IR::Inst* const offset_inst{offset.InstRecursive()};
if (offset_inst->GetOpcode() != IR::Opcode::IAdd32) {
return std::nullopt;
}
u32 base_offset{};
IR::U32 dynamic_offset;
if (offset_inst->Arg(0).IsImmediate()) {
base_offset = offset_inst->Arg(0).U32();
dynamic_offset = IR::U32{offset_inst->Arg(1)};
} else if (offset_inst->Arg(1).IsImmediate()) {
base_offset = offset_inst->Arg(1).U32();
dynamic_offset = IR::U32{offset_inst->Arg(0)};
} else {
return std::nullopt; return std::nullopt;
} }
return ConstBufferAddr{ return ConstBufferAddr{
.index{index.U32()}, .index = index.U32(),
.offset{offset.U32()}, .offset = base_offset,
.secondary_index = 0, .secondary_index = 0,
.secondary_offset = 0, .secondary_offset = 0,
.dynamic_offset = dynamic_offset,
.count = 8,
.has_secondary = false, .has_secondary = false,
}; };
} }
@ -179,11 +215,13 @@ TextureInst MakeInst(Environment& env, IR::Block* block, IR::Inst& inst) {
.offset = inst.Arg(0).U32(), .offset = inst.Arg(0).U32(),
.secondary_index = 0, .secondary_index = 0,
.secondary_offset = 0, .secondary_offset = 0,
.dynamic_offset = {},
.count = 1,
.has_secondary = false, .has_secondary = false,
}; };
} }
return TextureInst{ return TextureInst{
.cbuf{addr}, .cbuf = addr,
.inst = &inst, .inst = &inst,
.block = block, .block = block,
}; };
@ -209,18 +247,20 @@ public:
u32 Add(const TextureBufferDescriptor& desc) { u32 Add(const TextureBufferDescriptor& desc) {
return Add(texture_buffer_descriptors, desc, [&desc](const auto& existing) { return Add(texture_buffer_descriptors, desc, [&desc](const auto& existing) {
return desc.has_secondary == existing.has_secondary && return desc.cbuf_index == existing.cbuf_index &&
desc.cbuf_index == existing.cbuf_index &&
desc.cbuf_offset == existing.cbuf_offset && desc.cbuf_offset == existing.cbuf_offset &&
desc.secondary_cbuf_index == existing.secondary_cbuf_index && desc.secondary_cbuf_index == existing.secondary_cbuf_index &&
desc.secondary_cbuf_offset == existing.secondary_cbuf_offset; desc.secondary_cbuf_offset == existing.secondary_cbuf_offset &&
desc.count == existing.count && desc.size_shift == existing.size_shift &&
desc.has_secondary == existing.has_secondary;
}); });
} }
u32 Add(const ImageBufferDescriptor& desc) { u32 Add(const ImageBufferDescriptor& desc) {
return Add(image_buffer_descriptors, desc, [&desc](const auto& existing) { return Add(image_buffer_descriptors, desc, [&desc](const auto& existing) {
return desc.format == existing.format && desc.cbuf_index == existing.cbuf_index && return desc.format == existing.format && desc.cbuf_index == existing.cbuf_index &&
desc.cbuf_offset == existing.cbuf_offset; desc.cbuf_offset == existing.cbuf_offset && desc.count == existing.count &&
desc.size_shift == existing.size_shift;
}); });
} }
@ -231,7 +271,8 @@ public:
desc.cbuf_index == existing.cbuf_index && desc.cbuf_index == existing.cbuf_index &&
desc.cbuf_offset == existing.cbuf_offset && desc.cbuf_offset == existing.cbuf_offset &&
desc.secondary_cbuf_index == existing.secondary_cbuf_index && desc.secondary_cbuf_index == existing.secondary_cbuf_index &&
desc.secondary_cbuf_offset == existing.secondary_cbuf_offset; desc.secondary_cbuf_offset == existing.secondary_cbuf_offset &&
desc.count == existing.count && desc.size_shift == existing.size_shift;
}); });
} }
@ -239,7 +280,8 @@ public:
const u32 index{Add(image_descriptors, desc, [&desc](const auto& existing) { const u32 index{Add(image_descriptors, desc, [&desc](const auto& existing) {
return desc.type == existing.type && desc.format == existing.format && return desc.type == existing.type && desc.format == existing.format &&
desc.cbuf_index == existing.cbuf_index && desc.cbuf_index == existing.cbuf_index &&
desc.cbuf_offset == existing.cbuf_offset; desc.cbuf_offset == existing.cbuf_offset && desc.count == existing.count &&
desc.size_shift == existing.size_shift;
})}; })};
image_descriptors[index].is_written |= desc.is_written; image_descriptors[index].is_written |= desc.is_written;
return index; return index;
@ -310,7 +352,6 @@ void TexturePass(Environment& env, IR::Program& program) {
// This happens on Fire Emblem: Three Houses // This happens on Fire Emblem: Three Houses
flags.type.Assign(TextureType::Buffer); flags.type.Assign(TextureType::Buffer);
} }
inst->SetFlags(flags);
break; break;
default: default:
break; break;
@ -329,7 +370,8 @@ void TexturePass(Environment& env, IR::Program& program) {
.is_written = is_written, .is_written = is_written,
.cbuf_index = cbuf.index, .cbuf_index = cbuf.index,
.cbuf_offset = cbuf.offset, .cbuf_offset = cbuf.offset,
.count = 1, .count = cbuf.count,
.size_shift = DESCRIPTOR_SIZE_SHIFT,
}); });
} else { } else {
index = descriptors.Add(ImageDescriptor{ index = descriptors.Add(ImageDescriptor{
@ -338,7 +380,8 @@ void TexturePass(Environment& env, IR::Program& program) {
.is_written = is_written, .is_written = is_written,
.cbuf_index = cbuf.index, .cbuf_index = cbuf.index,
.cbuf_offset = cbuf.offset, .cbuf_offset = cbuf.offset,
.count = 1, .count = cbuf.count,
.size_shift = DESCRIPTOR_SIZE_SHIFT,
}); });
} }
break; break;
@ -351,7 +394,8 @@ void TexturePass(Environment& env, IR::Program& program) {
.cbuf_offset = cbuf.offset, .cbuf_offset = cbuf.offset,
.secondary_cbuf_index = cbuf.secondary_index, .secondary_cbuf_index = cbuf.secondary_index,
.secondary_cbuf_offset = cbuf.secondary_offset, .secondary_cbuf_offset = cbuf.secondary_offset,
.count = 1, .count = cbuf.count,
.size_shift = DESCRIPTOR_SIZE_SHIFT,
}); });
} else { } else {
index = descriptors.Add(TextureDescriptor{ index = descriptors.Add(TextureDescriptor{
@ -362,12 +406,23 @@ void TexturePass(Environment& env, IR::Program& program) {
.cbuf_offset = cbuf.offset, .cbuf_offset = cbuf.offset,
.secondary_cbuf_index = cbuf.secondary_index, .secondary_cbuf_index = cbuf.secondary_index,
.secondary_cbuf_offset = cbuf.secondary_offset, .secondary_cbuf_offset = cbuf.secondary_offset,
.count = 1, .count = cbuf.count,
.size_shift = DESCRIPTOR_SIZE_SHIFT,
}); });
} }
break; break;
} }
inst->SetArg(0, IR::Value{index}); flags.descriptor_index.Assign(index);
inst->SetFlags(flags);
if (cbuf.count > 1) {
const auto insert_point{IR::Block::InstructionList::s_iterator_to(*inst)};
IR::IREmitter ir{*texture_inst.block, insert_point};
const IR::U32 shift{ir.Imm32(std::countr_zero(DESCRIPTOR_SIZE))};
inst->SetArg(0, ir.ShiftRightArithmetic(cbuf.dynamic_offset, shift));
} else {
inst->SetArg(0, IR::Value{});
}
} }
} }

View file

@ -67,6 +67,7 @@ struct TextureBufferDescriptor {
u32 secondary_cbuf_index; u32 secondary_cbuf_index;
u32 secondary_cbuf_offset; u32 secondary_cbuf_offset;
u32 count; u32 count;
u32 size_shift;
}; };
using TextureBufferDescriptors = boost::container::small_vector<TextureBufferDescriptor, 6>; using TextureBufferDescriptors = boost::container::small_vector<TextureBufferDescriptor, 6>;
@ -76,6 +77,7 @@ struct ImageBufferDescriptor {
u32 cbuf_index; u32 cbuf_index;
u32 cbuf_offset; u32 cbuf_offset;
u32 count; u32 count;
u32 size_shift;
}; };
using ImageBufferDescriptors = boost::container::small_vector<ImageBufferDescriptor, 2>; using ImageBufferDescriptors = boost::container::small_vector<ImageBufferDescriptor, 2>;
@ -88,6 +90,7 @@ struct TextureDescriptor {
u32 secondary_cbuf_index; u32 secondary_cbuf_index;
u32 secondary_cbuf_offset; u32 secondary_cbuf_offset;
u32 count; u32 count;
u32 size_shift;
}; };
using TextureDescriptors = boost::container::small_vector<TextureDescriptor, 12>; using TextureDescriptors = boost::container::small_vector<TextureDescriptor, 12>;
@ -98,6 +101,7 @@ struct ImageDescriptor {
u32 cbuf_index; u32 cbuf_index;
u32 cbuf_offset; u32 cbuf_offset;
u32 count; u32 count;
u32 size_shift;
}; };
using ImageDescriptors = boost::container::small_vector<ImageDescriptor, 4>; using ImageDescriptors = boost::container::small_vector<ImageDescriptor, 4>;

View file

@ -85,28 +85,30 @@ public:
} }
void Add(const Shader::Info& info, VkShaderStageFlags stage) { void Add(const Shader::Info& info, VkShaderStageFlags stage) {
Add(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, stage, info.constant_buffer_descriptors.size()); Add(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, stage, info.constant_buffer_descriptors);
Add(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, stage, info.storage_buffers_descriptors.size()); Add(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, stage, info.storage_buffers_descriptors);
Add(VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, stage, info.texture_buffer_descriptors.size()); Add(VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, stage, info.texture_buffer_descriptors);
Add(VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, stage, info.image_buffer_descriptors.size()); Add(VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, stage, info.image_buffer_descriptors);
Add(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, stage, info.texture_descriptors.size()); Add(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, stage, info.texture_descriptors);
Add(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, stage, info.image_descriptors.size()); Add(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, stage, info.image_descriptors);
} }
private: private:
void Add(VkDescriptorType type, VkShaderStageFlags stage, size_t num) { template <typename Descriptors>
void Add(VkDescriptorType type, VkShaderStageFlags stage, const Descriptors& descriptors) {
const size_t num{descriptors.size()};
for (size_t i = 0; i < num; ++i) { for (size_t i = 0; i < num; ++i) {
bindings.push_back({ bindings.push_back({
.binding = binding, .binding = binding,
.descriptorType = type, .descriptorType = type,
.descriptorCount = 1, .descriptorCount = descriptors[i].count,
.stageFlags = stage, .stageFlags = stage,
.pImmutableSamplers = nullptr, .pImmutableSamplers = nullptr,
}); });
entries.push_back({ entries.push_back({
.dstBinding = binding, .dstBinding = binding,
.dstArrayElement = 0, .dstArrayElement = 0,
.descriptorCount = 1, .descriptorCount = descriptors[i].count,
.descriptorType = type, .descriptorType = type,
.offset = offset, .offset = offset,
.stride = sizeof(DescriptorUpdateEntry), .stride = sizeof(DescriptorUpdateEntry),
@ -126,21 +128,29 @@ private:
inline void PushImageDescriptors(const Shader::Info& info, const VkSampler*& samplers, inline void PushImageDescriptors(const Shader::Info& info, const VkSampler*& samplers,
const ImageId*& image_view_ids, TextureCache& texture_cache, const ImageId*& image_view_ids, TextureCache& texture_cache,
VKUpdateDescriptorQueue& update_descriptor_queue) { VKUpdateDescriptorQueue& update_descriptor_queue) {
image_view_ids += info.texture_buffer_descriptors.size(); for (const auto& desc : info.texture_buffer_descriptors) {
image_view_ids += info.image_buffer_descriptors.size(); image_view_ids += desc.count;
}
for (const auto& desc : info.image_buffer_descriptors) {
image_view_ids += desc.count;
}
for (const auto& desc : info.texture_descriptors) { for (const auto& desc : info.texture_descriptors) {
const VkSampler sampler{*(samplers++)}; for (u32 index = 0; index < desc.count; ++index) {
ImageView& image_view{texture_cache.GetImageView(*(image_view_ids++))}; const VkSampler sampler{*(samplers++)};
const VkImageView vk_image_view{image_view.Handle(desc.type)}; ImageView& image_view{texture_cache.GetImageView(*(image_view_ids++))};
update_descriptor_queue.AddSampledImage(vk_image_view, sampler); const VkImageView vk_image_view{image_view.Handle(desc.type)};
update_descriptor_queue.AddSampledImage(vk_image_view, sampler);
}
} }
for (const auto& desc : info.image_descriptors) { for (const auto& desc : info.image_descriptors) {
ImageView& image_view{texture_cache.GetImageView(*(image_view_ids++))}; for (u32 index = 0; index < desc.count; ++index) {
if (desc.is_written) { ImageView& image_view{texture_cache.GetImageView(*(image_view_ids++))};
texture_cache.MarkModification(image_view.image_id); if (desc.is_written) {
texture_cache.MarkModification(image_view.image_id);
}
const VkImageView vk_image_view{image_view.StorageView(desc.type, desc.format)};
update_descriptor_queue.AddImage(vk_image_view);
} }
const VkImageView vk_image_view{image_view.StorageView(desc.type, desc.format)};
update_descriptor_queue.AddImage(vk_image_view);
} }
} }

View file

@ -91,35 +91,41 @@ void ComputePipeline::Configure(Tegra::Engines::KeplerCompute& kepler_compute,
const auto& qmd{kepler_compute.launch_description}; const auto& qmd{kepler_compute.launch_description};
const auto& cbufs{qmd.const_buffer_config}; const auto& cbufs{qmd.const_buffer_config};
const bool via_header_index{qmd.linked_tsc != 0}; const bool via_header_index{qmd.linked_tsc != 0};
const auto read_handle{[&](const auto& desc) { const auto read_handle{[&](const auto& desc, u32 index) {
ASSERT(((qmd.const_buffer_enable_mask >> desc.cbuf_index) & 1) != 0); ASSERT(((qmd.const_buffer_enable_mask >> desc.cbuf_index) & 1) != 0);
const u32 index_offset{index << desc.size_shift};
const u32 offset{desc.cbuf_offset + index_offset};
const GPUVAddr addr{cbufs[desc.cbuf_index].Address() + desc.cbuf_offset}; const GPUVAddr addr{cbufs[desc.cbuf_index].Address() + desc.cbuf_offset};
if constexpr (std::is_same_v<decltype(desc), const Shader::TextureDescriptor&> || if constexpr (std::is_same_v<decltype(desc), const Shader::TextureDescriptor&> ||
std::is_same_v<decltype(desc), const Shader::TextureBufferDescriptor&>) { std::is_same_v<decltype(desc), const Shader::TextureBufferDescriptor&>) {
if (desc.has_secondary) { if (desc.has_secondary) {
ASSERT(((qmd.const_buffer_enable_mask >> desc.secondary_cbuf_index) & 1) != 0); ASSERT(((qmd.const_buffer_enable_mask >> desc.secondary_cbuf_index) & 1) != 0);
const u32 secondary_offset{desc.secondary_cbuf_offset + index_offset};
const GPUVAddr separate_addr{cbufs[desc.secondary_cbuf_index].Address() + const GPUVAddr separate_addr{cbufs[desc.secondary_cbuf_index].Address() +
desc.secondary_cbuf_offset}; secondary_offset};
const u32 lhs_raw{gpu_memory.Read<u32>(addr)}; const u32 lhs_raw{gpu_memory.Read<u32>(addr)};
const u32 rhs_raw{gpu_memory.Read<u32>(separate_addr)}; const u32 rhs_raw{gpu_memory.Read<u32>(separate_addr)};
const u32 raw{lhs_raw | rhs_raw}; return TextureHandle{lhs_raw | rhs_raw, via_header_index};
return TextureHandle{raw, via_header_index};
} }
} }
return TextureHandle{gpu_memory.Read<u32>(addr), via_header_index}; return TextureHandle{gpu_memory.Read<u32>(addr), via_header_index};
}}; }};
const auto add_image{[&](const auto& desc) { const auto add_image{[&](const auto& desc) {
const TextureHandle handle{read_handle(desc)}; for (u32 index = 0; index < desc.count; ++index) {
image_view_indices.push_back(handle.image); const TextureHandle handle{read_handle(desc, index)};
image_view_indices.push_back(handle.image);
}
}}; }};
std::ranges::for_each(info.texture_buffer_descriptors, add_image); std::ranges::for_each(info.texture_buffer_descriptors, add_image);
std::ranges::for_each(info.image_buffer_descriptors, add_image); std::ranges::for_each(info.image_buffer_descriptors, add_image);
for (const auto& desc : info.texture_descriptors) { for (const auto& desc : info.texture_descriptors) {
const TextureHandle handle{read_handle(desc)}; for (u32 index = 0; index < desc.count; ++index) {
image_view_indices.push_back(handle.image); const TextureHandle handle{read_handle(desc, index)};
image_view_indices.push_back(handle.image);
Sampler* const sampler = texture_cache.GetComputeSampler(handle.sampler); Sampler* const sampler = texture_cache.GetComputeSampler(handle.sampler);
samplers.push_back(sampler->Handle()); samplers.push_back(sampler->Handle());
}
} }
std::ranges::for_each(info.image_descriptors, add_image); std::ranges::for_each(info.image_descriptors, add_image);
@ -130,16 +136,18 @@ void ComputePipeline::Configure(Tegra::Engines::KeplerCompute& kepler_compute,
ImageId* texture_buffer_ids{image_view_ids.data()}; ImageId* texture_buffer_ids{image_view_ids.data()};
size_t index{}; size_t index{};
const auto add_buffer{[&](const auto& desc) { const auto add_buffer{[&](const auto& desc) {
ASSERT(desc.count == 1); for (u32 index = 0; index < desc.count; ++index) {
bool is_written{false}; bool is_written{false};
if constexpr (std::is_same_v<decltype(desc), const Shader::ImageBufferDescriptor&>) { if constexpr (std::is_same_v<decltype(desc), const Shader::ImageBufferDescriptor&>) {
is_written = desc.is_written; is_written = desc.is_written;
}
ImageView& image_view = texture_cache.GetImageView(*texture_buffer_ids);
buffer_cache.BindComputeTextureBuffer(index, image_view.GpuAddr(),
image_view.BufferSize(), image_view.format,
is_written);
++texture_buffer_ids;
++index;
} }
ImageView& image_view = texture_cache.GetImageView(*texture_buffer_ids);
buffer_cache.BindComputeTextureBuffer(index, image_view.GpuAddr(), image_view.BufferSize(),
image_view.format, is_written);
++texture_buffer_ids;
++index;
}}; }};
std::ranges::for_each(info.texture_buffer_descriptors, add_buffer); std::ranges::for_each(info.texture_buffer_descriptors, add_buffer);
std::ranges::for_each(info.image_buffer_descriptors, add_buffer); std::ranges::for_each(info.image_buffer_descriptors, add_buffer);

View file

@ -161,23 +161,26 @@ void GraphicsPipeline::Configure(bool is_indexed) {
const Shader::Info& info{stage_infos[stage]}; const Shader::Info& info{stage_infos[stage]};
buffer_cache.SetEnabledUniformBuffers(stage, info.constant_buffer_mask); buffer_cache.SetEnabledUniformBuffers(stage, info.constant_buffer_mask);
buffer_cache.UnbindGraphicsStorageBuffers(stage); buffer_cache.UnbindGraphicsStorageBuffers(stage);
size_t index{}; size_t ssbo_index{};
for (const auto& desc : info.storage_buffers_descriptors) { for (const auto& desc : info.storage_buffers_descriptors) {
ASSERT(desc.count == 1); ASSERT(desc.count == 1);
buffer_cache.BindGraphicsStorageBuffer(stage, index, desc.cbuf_index, desc.cbuf_offset, buffer_cache.BindGraphicsStorageBuffer(stage, ssbo_index, desc.cbuf_index,
desc.is_written); desc.cbuf_offset, desc.is_written);
++index; ++ssbo_index;
} }
const auto& cbufs{maxwell3d.state.shader_stages[stage].const_buffers}; const auto& cbufs{maxwell3d.state.shader_stages[stage].const_buffers};
const auto read_handle{[&](const auto& desc) { const auto read_handle{[&](const auto& desc, u32 index) {
ASSERT(cbufs[desc.cbuf_index].enabled); ASSERT(cbufs[desc.cbuf_index].enabled);
const GPUVAddr addr{cbufs[desc.cbuf_index].address + desc.cbuf_offset}; const u32 index_offset{index << desc.size_shift};
const u32 offset{desc.cbuf_offset + index_offset};
const GPUVAddr addr{cbufs[desc.cbuf_index].address + offset};
if constexpr (std::is_same_v<decltype(desc), const Shader::TextureDescriptor&> || if constexpr (std::is_same_v<decltype(desc), const Shader::TextureDescriptor&> ||
std::is_same_v<decltype(desc), const Shader::TextureBufferDescriptor&>) { std::is_same_v<decltype(desc), const Shader::TextureBufferDescriptor&>) {
if (desc.has_secondary) { if (desc.has_secondary) {
ASSERT(cbufs[desc.secondary_cbuf_index].enabled); ASSERT(cbufs[desc.secondary_cbuf_index].enabled);
const u32 second_offset{desc.secondary_cbuf_offset + index_offset};
const GPUVAddr separate_addr{cbufs[desc.secondary_cbuf_index].address + const GPUVAddr separate_addr{cbufs[desc.secondary_cbuf_index].address +
desc.secondary_cbuf_offset}; second_offset};
const u32 lhs_raw{gpu_memory.Read<u32>(addr)}; const u32 lhs_raw{gpu_memory.Read<u32>(addr)};
const u32 rhs_raw{gpu_memory.Read<u32>(separate_addr)}; const u32 rhs_raw{gpu_memory.Read<u32>(separate_addr)};
const u32 raw{lhs_raw | rhs_raw}; const u32 raw{lhs_raw | rhs_raw};
@ -187,17 +190,21 @@ void GraphicsPipeline::Configure(bool is_indexed) {
return TextureHandle{gpu_memory.Read<u32>(addr), via_header_index}; return TextureHandle{gpu_memory.Read<u32>(addr), via_header_index};
}}; }};
const auto add_image{[&](const auto& desc) { const auto add_image{[&](const auto& desc) {
const TextureHandle handle{read_handle(desc)}; for (u32 index = 0; index < desc.count; ++index) {
image_view_indices.push_back(handle.image); const TextureHandle handle{read_handle(desc, index)};
image_view_indices.push_back(handle.image);
}
}}; }};
std::ranges::for_each(info.texture_buffer_descriptors, add_image); std::ranges::for_each(info.texture_buffer_descriptors, add_image);
std::ranges::for_each(info.image_buffer_descriptors, add_image); std::ranges::for_each(info.image_buffer_descriptors, add_image);
for (const auto& desc : info.texture_descriptors) { for (const auto& desc : info.texture_descriptors) {
const TextureHandle handle{read_handle(desc)}; for (u32 index = 0; index < desc.count; ++index) {
image_view_indices.push_back(handle.image); const TextureHandle handle{read_handle(desc, index)};
image_view_indices.push_back(handle.image);
Sampler* const sampler{texture_cache.GetGraphicsSampler(handle.sampler)}; Sampler* const sampler{texture_cache.GetGraphicsSampler(handle.sampler)};
samplers.push_back(sampler->Handle()); samplers.push_back(sampler->Handle());
}
} }
std::ranges::for_each(info.image_descriptors, add_image); std::ranges::for_each(info.image_descriptors, add_image);
} }
@ -208,24 +215,30 @@ void GraphicsPipeline::Configure(bool is_indexed) {
for (size_t stage = 0; stage < Maxwell::MaxShaderStage; ++stage) { for (size_t stage = 0; stage < Maxwell::MaxShaderStage; ++stage) {
size_t index{}; size_t index{};
const auto add_buffer{[&](const auto& desc) { const auto add_buffer{[&](const auto& desc) {
ASSERT(desc.count == 1); for (u32 index = 0; index < desc.count; ++index) {
bool is_written{false}; bool is_written{false};
if constexpr (std::is_same_v<decltype(desc), const Shader::ImageBufferDescriptor&>) { if constexpr (std::is_same_v<decltype(desc),
is_written = desc.is_written; const Shader::ImageBufferDescriptor&>) {
is_written = desc.is_written;
}
ImageView& image_view{texture_cache.GetImageView(*texture_buffer_index)};
buffer_cache.BindGraphicsTextureBuffer(stage, index, image_view.GpuAddr(),
image_view.BufferSize(), image_view.format,
is_written);
++index;
++texture_buffer_index;
} }
ImageView& image_view{texture_cache.GetImageView(*texture_buffer_index)};
buffer_cache.BindGraphicsTextureBuffer(stage, index, image_view.GpuAddr(),
image_view.BufferSize(), image_view.format,
is_written);
++index;
++texture_buffer_index;
}}; }};
const Shader::Info& info{stage_infos[stage]}; const Shader::Info& info{stage_infos[stage]};
buffer_cache.UnbindGraphicsTextureBuffers(stage); buffer_cache.UnbindGraphicsTextureBuffers(stage);
std::ranges::for_each(info.texture_buffer_descriptors, add_buffer); std::ranges::for_each(info.texture_buffer_descriptors, add_buffer);
std::ranges::for_each(info.image_buffer_descriptors, add_buffer); std::ranges::for_each(info.image_buffer_descriptors, add_buffer);
texture_buffer_index += info.texture_descriptors.size(); for (const auto& desc : info.texture_descriptors) {
texture_buffer_index += info.image_descriptors.size(); texture_buffer_index += desc.count;
}
for (const auto& desc : info.image_descriptors) {
texture_buffer_index += desc.count;
}
} }
buffer_cache.UpdateGraphicsBuffers(is_indexed); buffer_cache.UpdateGraphicsBuffers(is_indexed);