shader: parse vertex metadata once when a shader is mapped

This commit is contained in:
nmzik
2026-10-24 05:44:46 +02:00
parent dbc7b8b2ff
commit 8291cfeb04
6 changed files with 44 additions and 44 deletions
+17 -9
View File
@@ -40,9 +40,15 @@
namespace Libs::Graphics { namespace Libs::Graphics {
// A mapped shader and its vertex metadata, parsed once when it is mapped.
struct ShaderMapData: ShaderMappedData {
ShaderVertexMetadata vertex;
const char* vertex_error = nullptr;
};
struct ShaderMapEntry { struct ShaderMapEntry {
ShaderMappedData data; ShaderMapData data;
uint64_t hash; uint64_t hash;
}; };
static std::unique_ptr<std::unordered_map<uint64_t, ShaderMapEntry>> g_shader_map; static std::unique_ptr<std::unordered_map<uint64_t, ShaderMapEntry>> g_shader_map;
@@ -64,8 +70,11 @@ void ShaderMapUserData(uint64_t addr, const ShaderMappedData& data) {
addr, data.code_size_bytes); addr, data.code_size_bytes);
} }
const auto hash = XXH3_64bits(reinterpret_cast<const void*>(addr), data.code_size_bytes); const auto hash = XXH3_64bits(reinterpret_cast<const void*>(addr), data.code_size_bytes);
ShaderMapData mapped {data};
mapped.vertex_error =
ShaderReadVertexMetadata(data, HW::UserSgprInfo::SGPRS_MAX, mapped.vertex);
std::scoped_lock lock(g_shader_map_mutex); std::scoped_lock lock(g_shader_map_mutex);
(*g_shader_map)[addr] = {data, hash}; (*g_shader_map)[addr] = {mapped, hash};
} }
static ShaderMapEntry ShaderGetMappedData(uint64_t addr, const char* label) { static ShaderMapEntry ShaderGetMappedData(uint64_t addr, const char* label) {
@@ -261,8 +270,8 @@ static uint32_t ShaderCalcPsSystemInputBase(const HW::ShaderRegisters& regs) {
static bool ShaderGetStaticVertexInputInfo(uint64_t shader_addr, const HW::UserSgprInfo& user_sgpr, static bool ShaderGetStaticVertexInputInfo(uint64_t shader_addr, const HW::UserSgprInfo& user_sgpr,
uint32_t user_sgpr_num, const HW::ShaderRegisters& sh, uint32_t user_sgpr_num, const HW::ShaderRegisters& sh,
const ShaderMappedData& data, const ShaderMapData& data,
ShaderVertexInputInfo& info) { ShaderVertexInputInfo& info) {
KYTY_PROFILER_FUNCTION(); KYTY_PROFILER_FUNCTION();
info = {}; info = {};
@@ -277,13 +286,12 @@ static bool ShaderGetStaticVertexInputInfo(uint64_t shader_addr, const HW::UserS
" user_sgpr_num=%u\n", " user_sgpr_num=%u\n",
shader_addr, user_sgpr_num); shader_addr, user_sgpr_num);
} }
ShaderVertexMetadata metadata; if (data.vertex_error != nullptr) {
std::string metadata_error;
if (!ShaderReadVertexMetadata(data, HW::UserSgprInfo::SGPRS_MAX, metadata, &metadata_error)) {
LOGF("ShaderGetVertexInputInfo(): invalid AGC metadata shader=0x%016" PRIx64 ": %s\n", LOGF("ShaderGetVertexInputInfo(): invalid AGC metadata shader=0x%016" PRIx64 ": %s\n",
shader_addr, metadata_error.c_str()); shader_addr, data.vertex_error);
return false; return false;
} }
const auto& metadata = data.vertex;
if (metadata.vertex_attrib_reg >= 0) { if (metadata.vertex_attrib_reg >= 0) {
info.fetch_external = false; info.fetch_external = false;
-11
View File
@@ -15,17 +15,6 @@
namespace Libs::Graphics { namespace Libs::Graphics {
namespace ShaderError {
[[nodiscard]] inline bool Fail(std::string* error, std::string_view message) {
if (error != nullptr) {
error->assign(message.data(), message.size());
}
return false;
}
} // namespace ShaderError
namespace HW { namespace HW {
struct VertexShaderInfo; struct VertexShaderInfo;
struct PixelShaderInfo; struct PixelShaderInfo;
+13 -14
View File
@@ -5,10 +5,10 @@
namespace Libs::Graphics { namespace Libs::Graphics {
bool ShaderReadVertexMetadata(const ShaderMappedData& data, uint32_t max_user_sgprs, const char* ShaderReadVertexMetadata(const ShaderMappedData& data, uint32_t max_user_sgprs,
ShaderVertexMetadata& metadata, std::string* error) { ShaderVertexMetadata& metadata) {
if (data.user_data == nullptr) { if (data.user_data == nullptr) {
return ShaderError::Fail(error, "missing AGC user-data header"); return "missing AGC user-data header";
} }
ShaderUserData user_data {}; ShaderUserData user_data {};
@@ -17,8 +17,7 @@ bool ShaderReadVertexMetadata(const ShaderMappedData& data, uint32_t max_user_sg
constexpr uint32_t DirectResourceCount = constexpr uint32_t DirectResourceCount =
static_cast<uint32_t>(AgcDirectResourceType::Last) + 1u; static_cast<uint32_t>(AgcDirectResourceType::Last) + 1u;
if (user_data.direct_resource_count > DirectResourceCount) { if (user_data.direct_resource_count > DirectResourceCount) {
return ShaderError::Fail(error, return "AGC direct-resource count exceeds the known resource domain";
"AGC direct-resource count exceeds the known resource domain");
} }
std::array<uint16_t, DirectResourceCount> direct_offsets {}; std::array<uint16_t, DirectResourceCount> direct_offsets {};
@@ -26,7 +25,7 @@ bool ShaderReadVertexMetadata(const ShaderMappedData& data, uint32_t max_user_sg
static_cast<uint64_t>(user_data.direct_resource_count) * sizeof(uint16_t); static_cast<uint64_t>(user_data.direct_resource_count) * sizeof(uint16_t);
if (direct_size != 0) { if (direct_size != 0) {
if (user_data.direct_resource_offset == nullptr) { if (user_data.direct_resource_offset == nullptr) {
return ShaderError::Fail(error, "missing AGC direct-resource offsets"); return "missing AGC direct-resource offsets";
} }
std::memcpy(direct_offsets.data(), user_data.direct_resource_offset, direct_size); std::memcpy(direct_offsets.data(), user_data.direct_resource_offset, direct_size);
} }
@@ -47,33 +46,33 @@ bool ShaderReadVertexMetadata(const ShaderMappedData& data, uint32_t max_user_sg
} }
if (next.vertex_attrib_reg >= 0 && next.vertex_buffer_reg < 0) { if (next.vertex_attrib_reg >= 0 && next.vertex_buffer_reg < 0) {
return ShaderError::Fail(error, "vertex attribute table requires a vertex buffer table"); return "vertex attribute table requires a vertex buffer table";
} }
if (next.vertex_buffer_reg < 0) { if (next.vertex_buffer_reg < 0) {
metadata = next; metadata = next;
return true; return nullptr;
} }
if (static_cast<uint32_t>(next.vertex_buffer_reg) + 1u >= max_user_sgprs) { if (static_cast<uint32_t>(next.vertex_buffer_reg) + 1u >= max_user_sgprs) {
return ShaderError::Fail(error, "vertex table pointer exceeds the user-SGPR domain"); return "vertex table pointer exceeds the user-SGPR domain";
} }
if (next.vertex_attrib_reg < 0) { if (next.vertex_attrib_reg < 0) {
metadata = next; metadata = next;
return true; return nullptr;
} }
if (static_cast<uint32_t>(next.vertex_attrib_reg) + 1u >= max_user_sgprs) { if (static_cast<uint32_t>(next.vertex_attrib_reg) + 1u >= max_user_sgprs) {
return ShaderError::Fail(error, "vertex table pointer exceeds the user-SGPR domain"); return "vertex table pointer exceeds the user-SGPR domain";
} }
if (data.num_input_semantics == 0 || if (data.num_input_semantics == 0 ||
data.num_input_semantics > ShaderVertexInputInfo::RES_MAX) { data.num_input_semantics > ShaderVertexInputInfo::RES_MAX) {
return ShaderError::Fail(error, "vertex semantic count is outside the supported domain"); return "vertex semantic count is outside the supported domain";
} }
if (data.input_semantics == nullptr) { if (data.input_semantics == nullptr) {
return ShaderError::Fail(error, "missing vertex input semantics"); return "missing vertex input semantics";
} }
next.input_semantics = {data.input_semantics, data.num_input_semantics}; next.input_semantics = {data.input_semantics, data.num_input_semantics};
metadata = next; metadata = next;
return true; return nullptr;
} }
} // namespace Libs::Graphics } // namespace Libs::Graphics
+4 -4
View File
@@ -4,7 +4,6 @@
#include "graphics/shader/shader.h" #include "graphics/shader/shader.h"
#include <span> #include <span>
#include <string>
namespace Libs::Graphics { namespace Libs::Graphics {
@@ -14,9 +13,10 @@ struct ShaderVertexMetadata {
std::span<const ShaderSemantic> input_semantics; std::span<const ShaderSemantic> input_semantics;
}; };
// Validates the vertex metadata and returns a view into the shader header. // Reads the vertex metadata as a view into the shader header. Returns nullptr if it is
bool ShaderReadVertexMetadata(const ShaderMappedData& data, uint32_t max_user_sgprs, // valid, otherwise why it was rejected.
ShaderVertexMetadata& metadata, std::string* error); const char* ShaderReadVertexMetadata(const ShaderMappedData& data, uint32_t max_user_sgprs,
ShaderVertexMetadata& metadata);
} // namespace Libs::Graphics } // namespace Libs::Graphics
+3 -6
View File
@@ -40,9 +40,7 @@ void CheckRejected(const ShaderMappedData& data, const char* text) {
output.vertex_buffer_reg = 37; output.vertex_buffer_reg = 37;
output.vertex_attrib_reg = 41; output.vertex_attrib_reg = 41;
output.input_semantics = prior_semantics; output.input_semantics = prior_semantics;
std::string error; Check(ShaderReadVertexMetadata(data, 64, output) != nullptr, text);
Check(!ShaderReadVertexMetadata(data, 64, output, &error), text);
Check(!error.empty(), "metadata rejection omitted its diagnostic");
Check(output.vertex_buffer_reg == 37 && output.vertex_attrib_reg == 41 && Check(output.vertex_buffer_reg == 37 && output.vertex_attrib_reg == 41 &&
output.input_semantics.data() == prior_semantics.data() && output.input_semantics.data() == prior_semantics.data() &&
output.input_semantics.size() == prior_semantics.size(), output.input_semantics.size() == prior_semantics.size(),
@@ -52,8 +50,7 @@ void CheckRejected(const ShaderMappedData& data, const char* text) {
void TestValidAndInvalidMetadata() { void TestValidAndInvalidMetadata() {
Fixture fixture; Fixture fixture;
ShaderVertexMetadata output; ShaderVertexMetadata output;
std::string error; Check(ShaderReadVertexMetadata(fixture.mapped, 64, output) == nullptr,
Check(ShaderReadVertexMetadata(fixture.mapped, 64, output, &error),
"valid AGC vertex metadata was rejected"); "valid AGC vertex metadata was rejected");
Check(output.vertex_buffer_reg == 2 && output.vertex_attrib_reg == 4 && Check(output.vertex_buffer_reg == 2 && output.vertex_attrib_reg == 4 &&
output.input_semantics.size() == 1, output.input_semantics.size() == 1,
@@ -64,7 +61,7 @@ void TestValidAndInvalidMetadata() {
AGC_ILLEGAL_DIRECT_OFFSET; AGC_ILLEGAL_DIRECT_OFFSET;
buffer_only.mapped.input_semantics = nullptr; buffer_only.mapped.input_semantics = nullptr;
buffer_only.mapped.num_input_semantics = 0; buffer_only.mapped.num_input_semantics = 0;
Check(ShaderReadVertexMetadata(buffer_only.mapped, 64, output, &error), Check(ShaderReadVertexMetadata(buffer_only.mapped, 64, output) == nullptr,
"vertex-buffer-only AGC metadata was rejected"); "vertex-buffer-only AGC metadata was rejected");
Check(output.vertex_buffer_reg == 2 && output.vertex_attrib_reg == -1 && Check(output.vertex_buffer_reg == 2 && output.vertex_attrib_reg == -1 &&
output.input_semantics.empty(), output.input_semantics.empty(),
+7
View File
@@ -1906,6 +1906,13 @@ void TestVertexBufferGrouping() {
input.resources[2].Base48() == input.buffers[0].addr, input.resources[2].Base48() == input.buffers[0].addr,
"a later lower-address attribute or changed table left stale vertex offsets"); "a later lower-address attribute or changed table left stale vertex offsets");
} }
// The vertex table metadata is parsed when the shader is mapped.
user_data.direct_resource_count = 0;
(void)PrepareProgram(regs, HW::Context{}, HW::UserConfig{}, input);
Check(input.fetch_attrib_reg == 2, "a draw reread the vertex metadata of a mapped shader");
ShaderMapUserData(regs.es_regs.data_addr, mapped);
(void)PrepareProgram(regs, HW::Context{}, HW::UserConfig{}, input);
Check(!input.fetch_embedded, "remapping a shader kept its previous vertex metadata");
} }
void TestNggVertexEntryState() { void TestNggVertexEntryState() {