graphics: store vertex attribute bindings without duplicated arrays

This commit is contained in:
nmzik
2026-09-29 03:25:22 +04:00
parent 8e42e99788
commit b43bf9f7fb
6 changed files with 115 additions and 79 deletions
@@ -761,24 +761,20 @@ PipelineCache::Pipeline& PipelineCache::GetGraphicsPipeline(
vs_input_info.resources_num > ShaderVertexInputInfo::RES_MAX);
key.vertex_input.binding_count = static_cast<uint8_t>(vs_input_info.buffers_num);
key.vertex_input.attribute_count = static_cast<uint8_t>(vs_input_info.resources_num);
uint32_t attributes_num = 0;
for (int binding = 0; binding < vs_input_info.buffers_num; binding++) {
const auto& buffer = vs_input_info.buffers[binding];
EXIT_IF(buffer.attr_num < 0 || buffer.attr_num > ShaderVertexInputBuffer::ATTR_MAX);
attributes_num += static_cast<uint32_t>(buffer.attr_num);
EXIT_IF(attributes_num > static_cast<uint32_t>(vs_input_info.resources_num));
key.vertex_input.bindings[binding] = {.stride = buffer.stride,
.instance = buffer.fetch_index != 0};
for (int attribute = 0; attribute < buffer.attr_num; attribute++) {
const auto index = buffer.attr_indices[attribute];
EXIT_IF(index < 0 || index >= vs_input_info.resources_num);
key.vertex_input.attributes[index] = {
.offset = buffer.attr_offsets[attribute],
.binding = static_cast<uint8_t>(binding),
};
}
}
EXIT_IF(attributes_num != static_cast<uint32_t>(vs_input_info.resources_num));
for (int attribute = 0; attribute < vs_input_info.resources_num; attribute++) {
const auto binding = vs_input_info.resources_dst[attribute].buffer_index;
EXIT_IF(binding < 0 || binding >= vs_input_info.buffers_num);
key.vertex_input.attributes[attribute] = {
.offset = static_cast<uint32_t>(vs_input_info.resources[attribute].Base48() -
vs_input_info.buffers[binding].addr),
.binding = static_cast<uint8_t>(binding),
};
}
}
if (auto iter = m_graphics_pipelines.find(key); iter != m_graphics_pipelines.end()) {
+27 -20
View File
@@ -249,8 +249,8 @@ static void LogDrawInputState(const CommandBuffer& buffer, const RenderColorInfo
for (int bi = 0; bi < vs_input_info.buffers_num; bi++) {
const auto& b = vs_input_info.buffers[bi];
LOGF("DrawInputState[%u]: vb[%d] addr=0x%010" PRIx64
" stride=%u records=%u fetch_index=%u attr_num=%d\n",
log_id, bi, b.addr, b.stride, b.num_records, b.fetch_index, b.attr_num);
" stride=%u records=%u fetch_index=%u\n",
log_id, bi, b.addr, b.stride, b.num_records, b.fetch_index);
const auto* bytes = reinterpret_cast<const uint8_t*>(b.addr);
if (bytes != nullptr && b.stride != 0) {
@@ -272,11 +272,13 @@ static void LogDrawInputState(const CommandBuffer& buffer, const RenderColorInfo
raw[5], raw[6], raw[7], raw[8], flt[0], flt[1], flt[2], flt[3], flt[4], flt[5],
flt[6], flt[7], flt[8]);
for (int ai = 0; ai < b.attr_num; ai++) {
const auto res_index = b.attr_indices[ai];
const auto& r = vs_input_info.resources[res_index];
const auto& rd = vs_input_info.resources_dst[res_index];
const auto offset = b.attr_offsets[ai];
for (int ai = 0; ai < vs_input_info.resources_num; ai++) {
const auto& r = vs_input_info.resources[ai];
const auto& rd = vs_input_info.resources_dst[ai];
if (rd.buffer_index != bi) {
continue;
}
const auto offset = static_cast<uint32_t>(r.Base48() - b.addr);
if (offset + 4u <= b.stride &&
r.Format() == Prospero::BufferFormat::k8_8_8_8UNorm) {
uint32_t packed = 0;
@@ -296,14 +298,16 @@ static void LogDrawInputState(const CommandBuffer& buffer, const RenderColorInfo
}
}
for (int ai = 0; ai < b.attr_num; ai++) {
const auto res_index = b.attr_indices[ai];
const auto& r = vs_input_info.resources[res_index];
const auto& rd = vs_input_info.resources_dst[res_index];
LOGF("DrawInputState[%u]: attr[%d] res=%d offset=%u dst=v%d regs=%d fetch_index=%u "
for (int ai = 0; ai < vs_input_info.resources_num; ai++) {
const auto& r = vs_input_info.resources[ai];
const auto& rd = vs_input_info.resources_dst[ai];
if (rd.buffer_index != bi) {
continue;
}
LOGF("DrawInputState[%u]: attr[%d] offset=%u dst=v%d regs=%d fetch_index=%u "
"sharp=%08" PRIx32 " %08" PRIx32 " %08" PRIx32 " %08" PRIx32 "\n",
log_id, ai, res_index, b.attr_offsets[ai], rd.register_start, rd.registers_num,
rd.fetch_index, r.fields[0], r.fields[1], r.fields[2], r.fields[3]);
log_id, ai, static_cast<uint32_t>(r.Base48() - b.addr), rd.register_start,
rd.registers_num, rd.fetch_index, r.fields[0], r.fields[1], r.fields[2], r.fields[3]);
}
}
}
@@ -630,19 +634,22 @@ struct PreparedIndexBuffer {
vk::IndexType type = vk::IndexType::eUint16;
};
static uint64_t VertexBufferDescriptorSize(const ShaderVertexInputBuffer& buffer,
const ShaderVertexInputInfo& info) {
static uint64_t VertexBufferDescriptorSize(int binding, const ShaderVertexInputInfo& info) {
const auto& buffer = info.buffers[binding];
if (buffer.stride != 0 || buffer.num_records == 0) {
return static_cast<uint64_t>(buffer.stride) * buffer.num_records;
}
uint64_t size = 0;
for (int i = 0; i < buffer.attr_num; i++) {
const auto& resource = info.resources[buffer.attr_indices[i]];
for (int i = 0; i < info.resources_num; i++) {
if (info.resources_dst[i].buffer_index != binding) {
continue;
}
const auto& resource = info.resources[i];
// RDNA2 OOB_SELECT=2 only checks NumRecords != 0. A constant attribute still
// fetches its entire format; NumRecords is not a byte count in this mode.
const uint64_t extent = resource.OutOfBounds() == 2
? static_cast<uint64_t>(buffer.attr_offsets[i]) +
? resource.Base48() - buffer.addr +
ShaderRecompiler::Format::GetFormatInfo(resource.Format()).byte_size
: buffer.num_records;
size = std::max(size, extent);
@@ -679,7 +686,7 @@ static PreparedVertexBuffers AcquireVertexBuffers(CommandBuffer& b
uint32_t range_count = 0;
for (int i = 0; i < vs_input_info.buffers_num; i++) {
const auto& vertex = vs_input_info.buffers[i];
const auto size = VertexBufferDescriptorSize(vertex, vs_input_info);
const auto size = VertexBufferDescriptorSize(i, vs_input_info);
sizes[i] = size;
if (size == 0) {
continue;
+16 -31
View File
@@ -336,14 +336,15 @@ static void ShaderDetectBuffers(ShaderVertexInputInfo& info) {
for (int ri = 0; ri < info.resources_num; ri++) {
const auto& r = info.resources[ri];
bool merged = false;
for (int bi = 0; bi < info.buffers_num; bi++) {
auto& destination = info.resources_dst[ri];
int bi = 0;
for (; bi < info.buffers_num; bi++) {
auto& b = info.buffers[bi];
uint64_t stride = b.stride;
if (stride == r.Stride() &&
b.fetch_index == static_cast<uint32_t>(info.resources_dst[ri].fetch_index)) {
b.fetch_index == destination.fetch_index) {
uint64_t rbase = r.Base48();
uint64_t base = std::min(rbase, b.addr);
uint64_t offset1 = rbase - base;
@@ -352,31 +353,20 @@ static void ShaderDetectBuffers(ShaderVertexInputInfo& info) {
if (offset1 < stride && offset2 < stride) {
EXIT_NOT_IMPLEMENTED(b.num_records != r.NumRecords());
b.addr = base;
EXIT_NOT_IMPLEMENTED(b.attr_num >= ShaderVertexInputBuffer::ATTR_MAX);
b.attr_indices[b.attr_num++] = ri;
merged = true;
break;
}
}
}
if (!merged) {
if (bi == info.buffers_num) {
EXIT_NOT_IMPLEMENTED(info.buffers_num >= ShaderVertexInputInfo::RES_MAX);
int bi = info.buffers_num++;
info.buffers[bi].addr = r.Base48();
info.buffers[bi].stride = r.Stride();
info.buffers[bi].num_records = r.NumRecords();
info.buffers[bi].fetch_index = info.resources_dst[ri].fetch_index;
info.buffers[bi].attr_num = 1;
info.buffers[bi].attr_indices[0] = ri;
}
}
for (int bi = 0; bi < info.buffers_num; bi++) {
auto& b = info.buffers[bi];
for (int ri = 0; ri < b.attr_num; ri++) {
b.attr_offsets[ri] = info.resources[b.attr_indices[ri]].Base48() - b.addr;
info.buffers[bi] = {.addr = r.Base48(),
.stride = r.Stride(),
.num_records = r.NumRecords(),
.fetch_index = destination.fetch_index};
info.buffers_num++;
}
destination.buffer_index = bi;
}
}
@@ -907,8 +897,9 @@ void ShaderDbgDumpInputInfo(const ShaderVertexInputInfo& info) {
LOGF("\t\t register_start = %d\n"
"\t\t registers_num = %d\n"
"\t\t fetch_index = %" PRIu32 "\n",
rd.register_start, rd.registers_num, rd.fetch_index);
"\t\t fetch_index = %" PRIu32 "\n"
"\t\t buffer_index = %d\n",
rd.register_start, rd.registers_num, rd.fetch_index, rd.buffer_index);
LOGF("\t\t fields = %08" PRIx32 "%08" PRIx32 "%08" PRIx32 "%08" PRIx32 "\n",
r.fields[3], r.fields[2], r.fields[1], r.fields[0]);
LOGF("\t\t Base() = %" PRIx64 "\n"
@@ -934,14 +925,8 @@ void ShaderDbgDumpInputInfo(const ShaderVertexInputInfo& info) {
LOGF("\t\t addr = %" PRIx64 "\n"
"\t\t stride = %" PRIu32 "\n"
"\t\t num_records = %" PRIu32 "\n"
"\t\t fetch_index = %" PRIu32 "\n"
"\t\t attr_num = %" PRId32 "\n",
r.addr, r.stride, r.num_records, r.fetch_index, r.attr_num);
for (int j = 0; j < r.attr_num; j++) {
LOGF("\t\t attr_indices[%d] = %d\n"
"\t\t attr_offsets[%d] = %u\n",
j, r.attr_indices[j], j, r.attr_offsets[j]);
}
"\t\t fetch_index = %" PRIu32 "\n",
r.addr, r.stride, r.num_records, r.fetch_index);
}
}
+5 -9
View File
@@ -174,15 +174,10 @@ struct ShaderSamplerResource {
};
struct ShaderVertexInputBuffer {
static constexpr int ATTR_MAX = 32;
uint64_t addr = 0;
uint32_t stride = 0;
uint32_t num_records = 0;
uint32_t fetch_index = 0;
int attr_num = 0;
int attr_indices[ATTR_MAX] = {0};
uint32_t attr_offsets[ATTR_MAX] = {0};
uint64_t addr = 0;
uint32_t stride = 0;
uint32_t num_records = 0;
uint32_t fetch_index = 0;
};
struct ShaderVertexDestination {
@@ -190,6 +185,7 @@ struct ShaderVertexDestination {
int registers_num = 0;
int attr_id = -1;
uint32_t fetch_index = 0;
int buffer_index = 0;
};
enum class ShaderStorageUsage {
+1 -3
View File
@@ -14062,9 +14062,7 @@ public:
vertex.resources_num = 2;
vertex.buffers_num = 1;
vertex.buffers[0].stride = 6 * sizeof(float);
vertex.buffers[0].attr_num = 2;
vertex.buffers[0].attr_indices[1] = 1;
vertex.buffers[0].attr_offsets[1] = 2 * sizeof(float);
vertex.resources[1].UpdateAddress48(2 * sizeof(float));
for (uint32_t i = 0; i < 2; i++) {
const auto format = i == 0 ? Prospero::BufferFormat::k32_32Float
: packed_vertex_color ? Prospero::BufferFormat::k11_11_10Float
+57 -3
View File
@@ -1702,6 +1702,61 @@ void TestShaderStageBarriers() {
"independent vertex invocations retained a workgroup barrier");
}
void TestVertexBufferGrouping() {
const uint32_t shader[] = {EncodeSopp(0x01)};
std::array<uint16_t, static_cast<size_t>(AgcDirectResourceType::Last) + 1> offsets;
offsets.fill(AGC_ILLEGAL_DIRECT_OFFSET);
offsets[static_cast<size_t>(AgcDirectResourceType::PtrVertexBufferTable)] = 0;
offsets[static_cast<size_t>(AgcDirectResourceType::PtrVertexAttribDescTable)] = 2;
ShaderUserData user_data{};
user_data.direct_resource_count = offsets.size();
user_data.direct_resource_offset = offsets.data();
std::array<ShaderSemantic, 4> semantics{};
for (uint32_t i = 0; i < semantics.size(); i++) {
semantics[i].semantic = i;
semantics[i].hardware_mapping = i;
semantics[i].size_in_elements = 1;
}
ShaderMappedData mapped{};
mapped.user_data = &user_data;
mapped.code_size_bytes = sizeof(shader);
mapped.input_semantics = semantics.data();
mapped.num_input_semantics = semantics.size();
HW::VertexShaderInfo regs{};
regs.es_regs.data_addr = reinterpret_cast<uint64_t>(shader);
regs.gs_regs.rsrc2.user_sgpr = 4;
ShaderMapUserData(regs.es_regs.data_addr, mapped);
std::array<ShaderBufferResource, 4> descriptors{};
const std::array<uint32_t, 4> attributes{0, 1u | (1u << 26u), 2, 3};
const std::array<uint64_t, 2> pointers{
reinterpret_cast<uint64_t>(descriptors.data()),
reinterpret_cast<uint64_t>(attributes.data())};
std::memcpy(regs.gs_user_sgpr.value, pointers.data(), sizeof(pointers));
ShaderVertexInputInfo input{};
for (const uint64_t base : {0x1000u, 0x3000u}) {
for (auto &descriptor : descriptors) {
descriptor.fields[1] = 16u << 16u;
descriptor.fields[2] = 8;
descriptor.UpdateAddress48(base);
}
descriptors[0].UpdateAddress48(base + 8);
descriptors[1].UpdateAddress48(base + 4);
descriptors[3].fields[1] = 0; // Constant attributes use their own buffer.
(void)PrepareProgram(regs, HW::Context{}, HW::UserConfig{}, input);
Check(input.resources_num == 4 && input.buffers_num == 3 &&
input.resources_dst[0].buffer_index == 0 &&
input.resources_dst[1].buffer_index == 1 &&
input.resources_dst[2].buffer_index == 0 &&
input.resources_dst[3].buffer_index == 2,
"interleaved vertex attributes lost their stride or instance-rate binding");
Check(input.buffers[0].addr == base && input.buffers[1].addr == base + 4 &&
input.buffers[1].fetch_index == 1 && input.buffers[2].stride == 0 &&
input.resources[0].Base48() - input.buffers[0].addr == 8 &&
input.resources[2].Base48() == input.buffers[0].addr,
"a later lower-address attribute or changed table left stale vertex offsets");
}
}
void TestNggVertexEntryState() {
using namespace ShaderRecompiler;
// PPSA03309 shader 5a39eb2021a5d2c1: retain its NGG prologue and replace
@@ -10571,13 +10626,11 @@ void TestNewShaderRecompilerClipDisabledPosition() {
ShaderVertexInputInfo layout_a{};
layout_a.resources_num = 1;
layout_a.buffers_num = 1;
layout_a.buffers[0].attr_num = 1;
layout_a.buffers[0].attr_indices[0] = 0;
layout_a.buffers[0].stride = 16;
auto layout_b = layout_a;
layout_b.buffers[0].stride = 32;
layout_b.buffers[0].fetch_index = 1;
layout_b.buffers[0].attr_offsets[0] = 4;
layout_b.resources[0].UpdateAddress48(4);
Check(MakeStageStaticKey(layout_a) == MakeStageStaticKey(layout_b),
"pipeline-only vertex layout fragmented the shader module cache key");
}
@@ -13855,6 +13908,7 @@ int main() {
TestDemandDrivenSpirvDeclarations();
TestNewShaderRecompilerSMovB32();
TestShaderStageBarriers();
TestVertexBufferGrouping();
TestNggVertexEntryState();
TestNewShaderRecompilerClipDisabledPosition();
TestNewShaderRecompilerAuxPositionExports();