mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-19 18:36:53 +02:00
429d0af62b
Co-authored-by: Ali Almohaya <AliAlmohaya@gmail.com>
185 lines
6.4 KiB
C++
185 lines
6.4 KiB
C++
#include "graphics/shader/recompiler/ir/passes/BindingLayout.h"
|
|
|
|
#include "common/assert.h"
|
|
#include "graphics/shader/recompiler/ir/ShaderIR.h"
|
|
|
|
#include <algorithm>
|
|
#include <array>
|
|
#include <utility>
|
|
|
|
namespace Libs::Graphics::ShaderRecompiler::IR {
|
|
namespace {
|
|
|
|
[[noreturn]] void BindingFail(const char* message) {
|
|
EXIT("shader binding layout failed: %s", message);
|
|
std::abort();
|
|
}
|
|
|
|
std::vector<uint32_t> CollectUserData(const Program& program) {
|
|
std::array<bool, NumScalarRegs> registers {};
|
|
for (const auto* block: program.blocks) {
|
|
for (const auto& inst: *block) {
|
|
if (inst.GetOpcode() != ValueOpcode::GetUserData || !inst.HasUses()) {
|
|
continue;
|
|
}
|
|
if (inst.Arg(0).GetType() != Type::ScalarReg) {
|
|
BindingFail("typed shader contains an invalid user-data register");
|
|
}
|
|
const auto index = RegIndex(inst.Arg(0).ScalarRegister());
|
|
if (index >= NumScalarRegs) {
|
|
BindingFail("typed shader contains an invalid user-data register");
|
|
}
|
|
registers[index] = true;
|
|
}
|
|
}
|
|
std::vector<uint32_t> result;
|
|
for (uint32_t index = 0; index < registers.size(); index++) {
|
|
if (registers[index]) {
|
|
result.push_back(index);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
void AddBinding(BindingLayout& layout, DescriptorBindingKind kind,
|
|
std::vector<uint32_t> resources = {}) {
|
|
layout.descriptors.push_back({kind, std::move(resources)});
|
|
}
|
|
|
|
} // namespace
|
|
|
|
bool CollectMemoryResources(const Program& program, std::vector<uint32_t>& buffers) {
|
|
std::array<bool, ShaderInfo::MaxBuffers> live_buffers {};
|
|
bool uses_gds = false;
|
|
for (const auto* block: program.blocks) {
|
|
for (const auto& inst: *block) {
|
|
const auto op = inst.GetOpcode();
|
|
if (BufferAccessOf(op) == BufferAccess::None &&
|
|
SharedAccessOf(op) == SharedAccess::None) {
|
|
continue;
|
|
}
|
|
const auto index = inst.Flags<MemoryFlags>().index;
|
|
if (index >= program.memory_info.size()) {
|
|
BindingFail("typed shader contains invalid memory metadata");
|
|
}
|
|
const auto& memory = program.memory_info[index];
|
|
if (memory.planning_only) {
|
|
continue;
|
|
}
|
|
if (SharedAccessOf(op) != SharedAccess::None) {
|
|
if (memory.kind != ResourceKind::Lds && memory.kind != ResourceKind::Gds) {
|
|
BindingFail("typed shader contains invalid shared-memory metadata");
|
|
}
|
|
uses_gds |= memory.kind == ResourceKind::Gds;
|
|
} else if (memory.kind == ResourceKind::Buffer || memory.kind == ResourceKind::ScalarBuffer) {
|
|
EXIT_IF(memory.resource >= program.info.buffers.size());
|
|
live_buffers.at(memory.resource) = true;
|
|
for (const auto child: program.info.buffers[memory.resource].indirect_resources) {
|
|
live_buffers.at(child) = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for (uint32_t i = 0; i < program.info.buffers.size(); i++) {
|
|
if (live_buffers.at(i)) {
|
|
buffers.push_back(i);
|
|
}
|
|
}
|
|
return uses_gds;
|
|
}
|
|
|
|
bool UsesFlattenedSrt(const Program& program) {
|
|
const auto uses_mapping = [](const auto& resource) {
|
|
return resource.indirect_search_iterations != 0u;
|
|
};
|
|
return std::ranges::any_of(program.blocks, [](const Block* block) {
|
|
return std::ranges::any_of(*block, [](const Inst& inst) {
|
|
return inst.GetOpcode() == ValueOpcode::ReadConst;
|
|
});
|
|
}) || std::ranges::any_of(program.info.buffers, uses_mapping) ||
|
|
std::ranges::any_of(program.info.images, uses_mapping);
|
|
}
|
|
|
|
void AllocateBindings(Program& program, uint32_t push_data_start_dword) {
|
|
if (!program.shader_info_complete || program.binding_layout_complete) {
|
|
EXIT("shader binding layout failed: %s", !program.shader_info_complete
|
|
? "shader info is not ready"
|
|
: "binding layout already allocated");
|
|
}
|
|
BindingLayout next;
|
|
std::vector<uint32_t> buffers;
|
|
const bool uses_gds = CollectMemoryResources(program, buffers);
|
|
next.user_data_registers = CollectUserData(program);
|
|
next.memory_offset_dword = static_cast<uint32_t>(next.user_data_registers.size());
|
|
next.memory_offset_count = static_cast<uint32_t>(buffers.size());
|
|
next.push_data_start_dword =
|
|
PushData::StartFor(push_data_start_dword, next.ShaderDataDwords());
|
|
|
|
if (!buffers.empty()) {
|
|
// Draw binding accesses this first group directly when memory_offset_count is nonzero.
|
|
AddBinding(next, DescriptorBindingKind::Buffers, std::move(buffers));
|
|
}
|
|
|
|
std::array<std::vector<uint32_t>, ImageBindingCount> image_groups;
|
|
for (uint32_t i = 0; i < program.info.images.size(); i++) {
|
|
const auto kind = DescriptorBindingForImage(program.info.images[i]);
|
|
if (!kind.has_value()) {
|
|
EXIT("shader binding layout failed: image %u has an invalid binding class", i);
|
|
}
|
|
const auto group = ImageBindingIndex(*kind);
|
|
if (group >= image_groups.size()) {
|
|
EXIT("shader binding layout failed: image %u has an unmapped binding class", i);
|
|
}
|
|
auto& resources = image_groups[group];
|
|
const auto dynamic = program.info.images[i].mip_mode == ImageMipMode::Dynamic;
|
|
const auto count = dynamic ? program.info.images[i].mip_count : 1u;
|
|
if (count == 0u || (!dynamic && program.info.images[i].mip_count != 1u)) {
|
|
EXIT("shader binding layout failed: image %u has invalid specialized mip count %u", i,
|
|
program.info.images[i].mip_count);
|
|
}
|
|
resources.insert(resources.end(), count, i);
|
|
}
|
|
for (uint32_t i = 0; i < image_groups.size(); i++) {
|
|
if (!image_groups[i].empty()) {
|
|
AddBinding(next, static_cast<DescriptorBindingKind>(FirstImageBinding + i),
|
|
std::move(image_groups[i]));
|
|
}
|
|
}
|
|
|
|
if (!program.info.samplers.empty()) {
|
|
std::vector<uint32_t> resources(program.info.samplers.size());
|
|
for (uint32_t i = 0; i < resources.size(); i++) {
|
|
resources[i] = i;
|
|
}
|
|
AddBinding(next, DescriptorBindingKind::Samplers, std::move(resources));
|
|
}
|
|
if (uses_gds) {
|
|
AddBinding(next, DescriptorBindingKind::Gds);
|
|
}
|
|
if (program.info.uses_dma) {
|
|
AddBinding(next, DescriptorBindingKind::BdaPagetable);
|
|
AddBinding(next, DescriptorBindingKind::FaultBuffer);
|
|
}
|
|
if (UsesFlattenedSrt(program)) {
|
|
AddBinding(next, DescriptorBindingKind::FlattenedSrt);
|
|
}
|
|
|
|
if (next.ShaderDataDwords() != 0 && !next.UsesPushData()) {
|
|
AddBinding(next, DescriptorBindingKind::ShaderData);
|
|
}
|
|
|
|
program.bindings = std::move(next);
|
|
program.binding_layout_complete = true;
|
|
}
|
|
|
|
const DescriptorBinding* FindBinding(const BindingLayout& layout, DescriptorBindingKind kind) {
|
|
for (const auto& binding: layout.descriptors) {
|
|
if (binding.kind == kind) {
|
|
return &binding;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
} // namespace Libs::Graphics::ShaderRecompiler::IR
|