Compare commits

..

3 Commits

16 changed files with 226 additions and 91 deletions
@@ -209,20 +209,20 @@ int BufferTableAttribFromOffset(uint32_t raw_offset, int dword) {
}
Frontend::EmbeddedFetchPlan DetectEmbeddedVertexFetch(
const Decoder::Program& decoded, const ShaderVertexInputInfo* input_info,
const Decoder::Program& decoded, const ShaderVertexInputInfo& input_info,
uint32_t user_data_base, uint32_t user_data_count, uint32_t wave_size) {
const uint32_t vertex_index_reg = input_info->logical_stage == ShaderType::Local ? 2u : 5u;
const uint32_t instance_index_reg = input_info->logical_stage == ShaderType::Local ? 5u : 8u;
const uint32_t vertex_index_reg = input_info.logical_stage == ShaderType::Local ? 2u : 5u;
const uint32_t instance_index_reg = input_info.logical_stage == ShaderType::Local ? 5u : 8u;
Frontend::EmbeddedFetchPlan data;
data.loads.reserve(input_info->resources_num);
data.loads.reserve(input_info.resources_num);
int32_t vertex_offset_candidate = -1;
int32_t instance_offset_candidate = -1;
bool vertex_offset_conflict = false;
bool instance_offset_conflict = false;
const int shift_regs = 8;
const int attrib_reg = input_info->fetch_attrib_reg + shift_regs;
const int buffer_reg = input_info->fetch_buffer_reg + shift_regs;
const int attrib_reg = input_info.fetch_attrib_reg + shift_regs;
const int buffer_reg = input_info.fetch_buffer_reg + shift_regs;
std::array<EmbeddedFetchSgprInfo, 108> sgprs {};
std::array<bool, 256> vgpr_is_index {};
@@ -488,7 +488,6 @@ void PrepareCallTargets(ShaderSource& source, const CompileOptions& options) {
if (source.decoded.instructions[i].opcode != Decoder::Opcode::S_SWAPPC_B64) continue;
source.calls.push_back({.instruction = i});
}
EXIT_IF(options.input_info.compute == nullptr);
const auto graph = CFG::BuildGraph(source.decoded);
Program query;
query.stage = options.stage;
@@ -809,9 +808,9 @@ TranslateResult TranslateProgram(const Decoder::Program& decoded, const CompileO
const auto& cfg = *selected_cfg;
Frontend::EmbeddedFetchPlan embedded_fetch;
if ((options.stage == ShaderType::Vertex || options.stage == ShaderType::Local) &&
options.input_info.vertex != nullptr && options.input_info.vertex->fetch_embedded) {
options.input_info.vertex->fetch_embedded) {
embedded_fetch = DetectEmbeddedVertexFetch(
decoded, options.input_info.vertex, options.user_data_base,
decoded, *options.input_info.vertex, options.user_data_base,
static_cast<uint32_t>(options.user_data.size()), options.wave_size);
if (!embedded_fetch.loads.empty()) {
LOGF("%s embedded vertex fetch plan: detected=%" PRIu64 "\n", GetDumpLabel(options),
@@ -233,8 +233,8 @@ std::vector<uint32_t> EmitProgram(IR::Program& program, ShaderStageInputInfo inp
? 2u
: 1u;
DefineModule(state);
ValidateNativeProgram(program, program.stage == ShaderType::Compute &&
input_info.compute != nullptr && input_info.compute->lds_storage);
ValidateNativeProgram(program,
program.stage == ShaderType::Compute && input_info.compute->lds_storage);
EmitProgram(state);
state.builder.AddEntryPoint(ExecutionModelForStage(state.program.stage), state.main_func,
"main", state.interface_variables);
@@ -53,8 +53,7 @@ uint32_t EmitBuiltinU32(EmitterState& state, IR::StageInputKind kind, uint32_t c
const auto bits = state.builder.AllocateId();
state.builder.AddFunction(spv::OpLoad, TypeBool(state), value, variable);
// SPI_BARYC_CNTL.FRONT_FACE_ALL_BITS selects uint masks or float +/-1.
const bool all_bits =
state.input_info.pixel != nullptr && state.input_info.pixel->ps_front_face_all_bits;
const bool all_bits = state.input_info.pixel->ps_front_face_all_bits;
const auto front = ConstantU32(state, all_bits ? 0xffffffffu : 0x3f800000u);
const auto back = ConstantU32(state, all_bits ? 0u : 0xbf800000u);
state.builder.AddFunction(spv::OpSelect, TypeU32(state), bits, value, front, back);
@@ -80,7 +80,7 @@ struct EmitterState {
lds_storage_class = spv::StorageClassWorkgroup;
}
if (program.info.uses_lds && program.stage == ShaderType::Compute &&
input_info.compute != nullptr && input_info.compute->lds_storage) {
input_info.compute->lds_storage) {
lds_storage_class = spv::StorageClassStorageBuffer;
}
}
@@ -169,10 +169,10 @@ uint32_t LdsDwordCount(const EmitterState& state) {
if (const auto* workgroup = ShaderWorkgroupInput(state.program.stage, state.input_info)) {
return workgroup->lds_size_dwords;
}
if (state.program.stage == ShaderType::Pixel && state.input_info.pixel != nullptr) {
if (state.program.stage == ShaderType::Pixel) {
return std::max(state.input_info.pixel->lds_size_dwords, 1u);
}
if (state.program.stage == ShaderType::Vertex && state.input_info.vertex != nullptr) {
if (state.program.stage == ShaderType::Vertex) {
return std::max(state.input_info.vertex->lds_size_dwords, 1u);
}
return FUNCTION_LDS_DWORDS;
@@ -470,8 +470,10 @@ bool ResolveSetpcDwordJumpTable(const Decoder::Program& program, uint32_t setpc_
if (!ResolveJumpTableEntryCount(program, load_index, load.src1, 2u, &entry_count)) {
return false;
}
// Code-table loads are not translated, so a selector that is also the load destination
// keeps its scaled index at the branch.
uint32_t selector_code = UINT32_MAX;
if (!ScalarOperandCode(load.src1, selector_code) || selector_code == offset_code ||
if (!ScalarOperandCode(load.src1, selector_code) ||
ScalarCodeWrittenInRange(program, load_index + 1u, setpc_index, selector_code)) {
return false;
}
@@ -987,30 +987,6 @@ void ValidateTranslateOptions(const TranslateOptions& options) {
options.stage != ShaderType::Local) {
EXIT("embedded fetch requires a vertex or local shader");
}
switch (options.stage) {
case ShaderType::Vertex:
case ShaderType::Local:
case ShaderType::TessellationControl:
case ShaderType::TessellationEvaluation:
case ShaderType::Mesh:
if (options.input_info.vertex == nullptr) {
EXIT("vertex shader translation has no vertex input metadata");
}
return;
case ShaderType::Pixel:
if (options.input_info.pixel == nullptr) {
EXIT("pixel shader translation has no pixel input metadata");
}
return;
case ShaderType::Compute:
if (options.input_info.compute == nullptr) {
EXIT("compute shader translation has no compute input metadata");
}
return;
default:
EXIT("shader translation has unsupported stage %u",
static_cast<uint32_t>(options.stage));
}
}
} // namespace
@@ -1044,7 +1020,9 @@ IR::Program TranslateProgram(const Decoder::Program& decoded, const CFG::Graph&
case ShaderType::Compute:
result.scratch_dwords = options.input_info.compute->scratch_size_dwords;
break;
default: break; // ValidateTranslateOptions rejects unsupported stages.
default:
EXIT("shader translation has unsupported stage %u",
static_cast<uint32_t>(options.stage));
}
result.dispatcher_fallback = cfg.irreducible || cfg.unsupported;
result.cfg_failure_kind = cfg.failure_kind;
@@ -362,8 +362,7 @@ class Tracker {
public:
Tracker(Program& program, const Decoder::Program& decoded, const CFG::Graph& native_cfg,
ShaderStageInputInfo input)
: m_program(program), m_decoded(decoded), m_native_cfg(native_cfg),
m_workgroup(program.stage == ShaderType::Compute ? input.compute : nullptr),
: m_program(program), m_decoded(decoded), m_native_cfg(native_cfg), m_input(input),
m_scalar_writes(std::move(program.scalar_writes)), m_info(program.info) {
std::ranges::sort(m_scalar_writes, {}, &Program::ScalarWrite::pc);
m_info.buffers.clear();
@@ -1853,7 +1852,7 @@ private:
if (inst->GetOpcode() == ValueOpcode::GetBuiltin &&
inst->Arg(0) == Value(static_cast<uint32_t>(StageInputKind::LocalInvocationId)) &&
ImmediateU32(inst->Arg(1), axis) && axis < 3u &&
m_workgroup->threads_num[axis] <= (uint64_t {1} << bits)) return axis + 1u;
m_input.compute->threads_num[axis] <= (uint64_t {1} << bits)) return axis + 1u;
if (!allow_induction || inst->GetOpcode() != ValueOpcode::Phi ||
(!induction.IsEmpty() && induction != at)) return {};
induction = at;
@@ -1864,20 +1863,20 @@ private:
bool LinearLocalIndex(const std::array<uint64_t, 5>& index) const {
uint64_t stride = 1u;
for (uint32_t axis = 0; axis < 3u; ++axis) {
if (m_workgroup->threads_num[axis] != 1u && index[axis + 1u] != stride) return false;
stride *= m_workgroup->threads_num[axis];
if (m_input.compute->threads_num[axis] != 1u && index[axis + 1u] != stride) return false;
stride *= m_input.compute->threads_num[axis];
}
return index[0] == 0u;
}
void ForwardPrivateLds() {
if (m_workgroup == nullptr) return;
if (m_program.stage != ShaderType::Compute) return;
uint64_t bytes = 4u;
for (const auto size: m_workgroup->threads_num) {
for (const auto size: m_input.compute->threads_num) {
if (size == 0u || size > 1024u) return;
bytes *= size;
}
if (bytes > uint64_t {m_workgroup->lds_size_dwords} * 4u) return;
if (bytes > uint64_t {m_input.compute->lds_size_dwords} * 4u) return;
struct Access { Inst* inst; uint32_t offset; };
std::vector<Access> reads, writes;
Value address;
@@ -1893,7 +1892,7 @@ private:
if ((op != ValueOpcode::LoadSharedU32 && op != ValueOpcode::WriteSharedU32) ||
memory.data_bits != 32u || memory.secondary_offset != 0u ||
(memory.offset & 3u) != 0u ||
uint64_t {memory.offset} + bytes > uint64_t {m_workgroup->lds_size_dwords} * 4u)
uint64_t {memory.offset} + bytes > uint64_t {m_input.compute->lds_size_dwords} * 4u)
return;
if (address.IsEmpty()) address = inst.Arg(0).Resolve();
else if (!EquivalentValue(m_program, address, inst.Arg(0))) return;
@@ -2039,16 +2038,16 @@ private:
Value SharedLoadBound(Value value, Value active) {
const auto* read = ActiveValue(value, active).TryInstruction();
if (read == nullptr || read->GetOpcode() != ValueOpcode::LoadSharedU32 ||
m_workgroup == nullptr || m_program.stage != ShaderType::Compute ||
m_program.stage != ShaderType::Compute ||
read->Arg(1).Resolve() != Value(true)) return {};
uint64_t group_size = 1u;
for (const auto dimension: m_workgroup->threads_num) {
if (dimension == 0u || dimension > m_workgroup->wave_size) return {};
for (const auto dimension: m_input.compute->threads_num) {
if (dimension == 0u || dimension > m_input.compute->wave_size) return {};
group_size *= dimension;
}
// Indexed LDS operations within one native wave execute in order. Multiwave
// append lists require a separate workgroup synchronization proof.
if (group_size != m_workgroup->wave_size || group_size != m_program.wave_size ||
if (group_size != m_input.compute->wave_size || group_size != m_program.wave_size ||
(group_size != 32u && group_size != 64u)) return {};
const auto memory = [&](const Inst& inst) -> const MemoryInfo* {
const auto index = inst.Flags<MemoryFlags>().index;
@@ -2101,7 +2100,7 @@ private:
if (op != ValueOpcode::SharedAtomicIAdd32 ||
ActiveValue(inst.Arg(1), guard) != Value(1u) ||
!constant_address(inst, address) ||
uint64_t {address} + 4u > uint64_t {m_workgroup->lds_size_dwords} * 4u ||
uint64_t {address} + 4u > uint64_t {m_input.compute->lds_size_dwords} * 4u ||
address >= 0x10000u ||
std::ranges::any_of(appends, [&](const Append& at) { return at.counter == address; })) return {};
appends.push_back({.atomic = &inst, .counter = address});
@@ -4059,7 +4058,7 @@ private:
Program& m_program;
const Decoder::Program& m_decoded;
const CFG::Graph& m_native_cfg;
const ShaderWorkgroupInputInfo* m_workgroup;
ShaderStageInputInfo m_input;
std::vector<Program::ScalarWrite> m_scalar_writes;
std::vector<ResolvedHandle> m_resolved_handles;
std::vector<const Inst*> m_srt_visiting;
@@ -4092,8 +4091,8 @@ UniformFillPlan RecognizePatternFill(Program& program, const Decoder::Program& d
// Dispatch thread dimensions, valid in both wave sizes.
Pattern {.code_words = 38, .hash = 0xacbda1a1ccdd4198ull, .wave_size_mask = 32 | 64, .bounded = false},
};
if (program.stage != ShaderType::Compute || input.compute == nullptr ||
decoded.instructions.empty() || program.user_data_base != 0 || program.user_data_count < 10 ||
if (program.stage != ShaderType::Compute || decoded.instructions.empty() ||
program.user_data_base != 0 || program.user_data_count < 10 ||
program.info.buffers.size() != 1 || !program.info.images.empty()) return {};
const auto& cs = *input.compute;
const bool matching_inputs = cs.workgroup_register == 10 && cs.thread_ids_num == 1 &&
@@ -7,7 +7,7 @@ namespace Libs::Graphics::ShaderRecompiler::IR {
// Resolves native descriptor sources, plans their scalar reads, and assigns dense resource bindings.
void TrackResources(Program& program, const Decoder::Program& decoded, const CFG::Graph& native_cfg,
ShaderStageInputInfo input = {});
ShaderStageInputInfo input);
} // namespace Libs::Graphics::ShaderRecompiler::IR
@@ -47,26 +47,17 @@ void ValidateOptions(const Program& program, ShaderStageInputInfo input_info) {
case ShaderType::TessellationControl:
case ShaderType::TessellationEvaluation:
case ShaderType::Mesh:
if (input_info.vertex == nullptr) {
return Fail("vertex shader has no input metadata");
}
if (input_info.vertex->resources_num < 0 ||
input_info.vertex->resources_num > ShaderVertexInputInfo::RES_MAX) {
return Fail("vertex resource count is out of range");
}
return;
case ShaderType::Pixel:
if (input_info.pixel == nullptr) {
return Fail("pixel shader has no input metadata");
}
if (input_info.pixel->input_num > std::size(input_info.pixel->interpolator_settings)) {
return Fail("pixel input count is out of range");
}
return;
case ShaderType::Compute:
if (input_info.compute == nullptr) {
return Fail("compute shader has no input metadata");
}
if (input_info.compute->thread_ids_num < 0 || input_info.compute->thread_ids_num > 3) {
return Fail("compute thread ID count is out of range");
}
@@ -191,7 +182,7 @@ void AddBuiltinInput(ShaderInfo& info, StageInputKind kind) {
void CollectOutput(const Program& program, ShaderStageInputInfo input_info,
ShaderInfo& info, const Inst& inst) {
const bool alpha_remap = program.stage == ShaderType::Pixel && input_info.pixel != nullptr &&
const bool alpha_remap = program.stage == ShaderType::Pixel &&
input_info.pixel->alpha_blend_source != ShaderAlphaBlendSource::None;
const auto& export_info = program.export_info[inst.Flags<ExportFlags>().index];
if (export_info.kind == ExportTargetKind::MrtZ) {
+9
View File
@@ -739,6 +739,15 @@ LIB_DEFINE(InitNet_1_Http) {
LIB_FUNC("yigr4V0-HTM", Http::HttpSetRecvTimeOut);
LIB_FUNC("T-mGo9f3Pu4", Http::HttpSetAutoRedirect);
LIB_FUNC("qFg2SuyTJJY", Http::HttpSetAuthEnabled);
LIB_FUNC("h9wmFZX4i-4", Http::HttpSetRedirectCallback);
LIB_FUNC("Kh6bS2HQKbo", Http::HttpSetCookieRecvCallback);
LIB_FUNC("vO4B-42ef-k", Http::HttpSetRequestStatusCallback);
LIB_FUNC("i9mhafzkEi8", Http::HttpSetInflateGZIPEnabled);
LIB_FUNC("PDxS48xGQLs", Http::HttpSetChunkedTransferEnabled);
LIB_FUNC("DK+GoXCNT04", Http::HttpsLoadCert);
LIB_FUNC("zXqcE0fizz0", Http::HttpsUnloadCert);
LIB_FUNC("JBN6N-EY+3M", Http::HttpsGetSslError);
LIB_FUNC("0onIrKx9NIE", Http::HttpGetLastErrno);
LIB_FUNC("IWalAn-guFs", LibHttp::HttpUriParse);
LIB_FUNC("YuOW3dDAKYc", LibHttp::HttpUriEscape);
LIB_FUNC("5LZA+KPISVA", LibHttp::HttpUriBuild);
+144
View File
@@ -136,6 +136,7 @@ public:
bool HttpValidTemplate(Id tmpl_id);
bool HttpValidConnection(Id conn_id);
bool HttpValidRequest(Id req_id);
bool HttpValidBase(Id id);
Id HttpCreateConnection(Id tmpl_id, const char* server_name, const char* scheme, uint16_t port,
bool enable_keep_alive);
Id HttpCreateConnectionWithURL(Id tmpl_id, const char* url, bool enable_keep_alive);
@@ -410,6 +411,12 @@ bool Network::HttpValidRequest(Id req_id) {
m_requests[req_id.GetId()].used);
}
bool Network::HttpValidBase(Id id) {
Common::LockGuard lock(m_mutex);
return FindHttpBase(id, true) != nullptr;
}
Network::HttpBase* Network::FindHttpBase(Id id, bool include_request) {
const auto index = id.GetId();
@@ -3013,6 +3020,143 @@ int KYTY_SYSV_ABI HttpSetAuthEnabled(int id, int enable) {
return OK;
}
static int HttpCheckBase(int id) {
EXIT_IF(g_net == nullptr);
return g_net->HttpValidBase(Network::Id(id)) ? OK : HTTP_ERROR_INVALID_ID;
}
int KYTY_SYSV_ABI HttpSetRedirectCallback(int id, void* /*cbfunc*/, void* /*user_arg*/) {
PRINT_NAME();
LOGF("\t id = %d\n", id);
return HttpCheckBase(id);
}
int KYTY_SYSV_ABI HttpSetCookieRecvCallback(int id, void* /*cbfunc*/, void* /*user_arg*/) {
PRINT_NAME();
LOGF("\t id = %d\n", id);
return HttpCheckBase(id);
}
int KYTY_SYSV_ABI HttpSetRequestStatusCallback(int id, void* /*cbfunc*/, void* /*user_arg*/) {
PRINT_NAME();
LOGF("\t id = %d\n", id);
return HttpCheckBase(id);
}
int KYTY_SYSV_ABI HttpSetInflateGZIPEnabled(int id, int is_enable) {
PRINT_NAME();
LOGF("\t id = %d\n"
"\t is_enable = %d\n",
id, is_enable);
return HttpCheckBase(id);
}
int KYTY_SYSV_ABI HttpSetChunkedTransferEnabled(int id, int is_enabled) {
PRINT_NAME();
LOGF("\t id = %d\n"
"\t is_enabled = %d\n",
id, is_enabled);
EXIT_IF(g_net == nullptr);
if (is_enabled != 0 && is_enabled != 1) {
return HTTP_ERROR_INVALID_VALUE;
}
if (!g_net->HttpValidRequest(Network::Id(id))) {
return HTTP_ERROR_INVALID_ID;
}
return OK;
}
int KYTY_SYSV_ABI HttpsLoadCert(int http_ctx_id, int ca_cert_num, const void** /*ca_list*/,
const void* /*cert*/, const void* /*priv_key*/) {
PRINT_NAME();
LOGF("\t http_ctx_id = %d\n"
"\t ca_cert_num = %d\n",
http_ctx_id, ca_cert_num);
EXIT_IF(g_net == nullptr);
if (!g_net->HttpValid(Network::Id(http_ctx_id))) {
return HTTP_ERROR_INVALID_ID;
}
return OK;
}
int KYTY_SYSV_ABI HttpsUnloadCert(int http_ctx_id) {
PRINT_NAME();
LOGF("\t http_ctx_id = %d\n", http_ctx_id);
EXIT_IF(g_net == nullptr);
if (!g_net->HttpValid(Network::Id(http_ctx_id))) {
return HTTP_ERROR_INVALID_ID;
}
return OK;
}
int KYTY_SYSV_ABI HttpsGetSslError(int request_id, int32_t* err_num, uint32_t* detail) {
PRINT_NAME();
LOGF("\t request_id = %d\n"
"\t err_num = 0x%016" PRIx64 "\n"
"\t detail = 0x%016" PRIx64 "\n",
request_id, reinterpret_cast<uint64_t>(err_num), reinterpret_cast<uint64_t>(detail));
if (err_num == nullptr || detail == nullptr) {
return HTTP_ERROR_INVALID_VALUE;
}
*err_num = 0;
*detail = 0;
EXIT_IF(g_net == nullptr);
if (!g_net->HttpValidRequest(Network::Id(request_id))) {
return HTTP_ERROR_INVALID_ID;
}
return OK;
}
int KYTY_SYSV_ABI HttpGetLastErrno(int request_id, int* err_num) {
PRINT_NAME();
LOGF("\t request_id = %d\n"
"\t err_num = 0x%016" PRIx64 "\n",
request_id, reinterpret_cast<uint64_t>(err_num));
if (err_num == nullptr) {
return HTTP_ERROR_INVALID_VALUE;
}
*err_num = 0;
EXIT_IF(g_net == nullptr);
if (!g_net->HttpValidRequest(Network::Id(request_id))) {
return HTTP_ERROR_INVALID_ID;
}
return OK;
}
int KYTY_SYSV_ABI HttpAddRequestHeader(int id, const char* name, const char* value, uint32_t mode) {
PRINT_NAME();
+10
View File
@@ -136,6 +136,16 @@ int KYTY_SYSV_ABI HttpSetSendTimeOut(int id, uint32_t usec);
int KYTY_SYSV_ABI HttpSetRecvTimeOut(int id, uint32_t usec);
int KYTY_SYSV_ABI HttpSetAutoRedirect(int id, int enable);
int KYTY_SYSV_ABI HttpSetAuthEnabled(int id, int enable);
int KYTY_SYSV_ABI HttpSetRedirectCallback(int id, void* cbfunc, void* user_arg);
int KYTY_SYSV_ABI HttpSetCookieRecvCallback(int id, void* cbfunc, void* user_arg);
int KYTY_SYSV_ABI HttpSetRequestStatusCallback(int id, void* cbfunc, void* user_arg);
int KYTY_SYSV_ABI HttpSetInflateGZIPEnabled(int id, int is_enable);
int KYTY_SYSV_ABI HttpSetChunkedTransferEnabled(int id, int is_enabled);
int KYTY_SYSV_ABI HttpsLoadCert(int http_ctx_id, int ca_cert_num, const void** ca_list,
const void* cert, const void* priv_key);
int KYTY_SYSV_ABI HttpsUnloadCert(int http_ctx_id);
int KYTY_SYSV_ABI HttpsGetSslError(int request_id, int32_t* err_num, uint32_t* detail);
int KYTY_SYSV_ABI HttpGetLastErrno(int request_id, int* err_num);
} // namespace Http
+4 -1
View File
@@ -52,6 +52,7 @@ void CheckFatal(F &&function, std::string_view expected, const char *message) {
struct Fixture {
Program program;
Block *block = nullptr;
ShaderComputeInputInfo compute_info{};
explicit Fixture(ShaderType stage = ShaderType::Compute) {
program.stage = stage;
@@ -131,7 +132,9 @@ struct Fixture {
Value(0u), Value(0u), Value(0u)});
}
void PlanAndTrack(Libs::Graphics::ShaderStageInputInfo input = {}) {
void PlanAndTrack() { PlanAndTrack({.compute = &compute_info}); }
void PlanAndTrack(Libs::Graphics::ShaderStageInputInfo input) {
for (size_t index = 0; index < program.blocks.size(); ++index) {
const auto condition = program.blocks[index]->condition;
if (!condition.IsEmpty())
+5 -3
View File
@@ -14,6 +14,7 @@
namespace {
using namespace Libs::Graphics::ShaderRecompiler::IR;
using Libs::Graphics::ShaderComputeInputInfo;
using Libs::Graphics::ShaderType;
void Check(bool condition, const char *message) {
@@ -37,6 +38,7 @@ void CheckFatal(F &&function, std::string_view expected, const char *message) {
struct Fixture {
Program program;
ShaderComputeInputInfo compute{};
explicit Fixture(uint32_t block_count = 1) {
program.stage = ShaderType::Compute;
@@ -73,7 +75,7 @@ struct Fixture {
return static_cast<uint32_t>(program.memory_info.size() - 1u);
}
void Plan() { TrackResources(program, {}, {}); }
void Plan() { TrackResources(program, {}, {}, {.compute = &compute}); }
};
struct TestMemory {
@@ -158,7 +160,7 @@ void TestScalarMemoryDomainMismatchFails() {
Fixture raw;
const auto raw_memory = raw.AddMemory(ResourceKind::ScalarBuffer);
RawRead(raw, Address(raw, Value(0x1000u), Value(0u)), Value(0u), raw_memory);
CheckFatal([&] { TrackResources(raw.program, {}, {}); },
CheckFatal([&] { raw.Plan(); },
"incompatible scalar memory metadata",
"raw scalar load accepted descriptor-buffer metadata");
@@ -169,7 +171,7 @@ void TestScalarMemoryDomainMismatchFails() {
{Value(0x1000u), Value(0u), Value(16u), Value(0u)});
buffer.EmitMemory(ValueOpcode::ReadConstBuffer, {resource, Value(0u)},
buffer_memory);
CheckFatal([&] { TrackResources(buffer.program, {}, {}); },
CheckFatal([&] { buffer.Plan(); },
"incompatible scalar memory metadata",
"descriptor scalar load accepted raw-address metadata");
}
+16 -17
View File
@@ -11740,7 +11740,7 @@ void TestNewShaderRecompilerSetpcJumpTable() {
Check((*jump)->terminator.cases.size() == 2,
"S_SETPC_B64 jump table did not retain selector mapping");
Check(
(result.ir_dump.find("SLoadDword") == std::string::npos),
(result.ir_dump.find("LoadAddressU32") == std::string::npos),
"S_SETPC_B64 jump table load was reflected as a raw scalar buffer load");
Check(SpirvContainsOpcode(result.spirv, 251),
"dispatcher SPIR-V lacks OpSwitch");
@@ -11801,7 +11801,9 @@ void TestNewShaderRecompilerPrunesUnreachableSetpcMetadata() {
"unreachable S_SETPC metadata suppressed a reachable scalar load component");
}
void TestNewShaderRecompilerSetpcDwordJumpTable() {
// offset_sgpr 106 aliases the vcc_lo selector, like PPSA34015's
// s_load_dword s17, s[22:23], s17.
void TestNewShaderRecompilerSetpcDwordJumpTable(uint32_t offset_sgpr) {
const uint32_t shader[] = {
EncodeSopp(0x02, 1), // skip one unreachable block
EncodeSMovB32(100, 129), // unreachable
@@ -11811,20 +11813,20 @@ void TestNewShaderRecompilerSetpcDwordJumpTable() {
EncodeSop2(0x00, 4, 4, 255), // s_add_u32 s4, s4, literal
0x00000034u,
EncodeSop2(0x04, 5, 5, 128), // s_addc_u32 s5, s5, 0
EncodeSmem0(0x00, 6, 2),
(106u << 25u) | 4u, // s_load_dword s6, s[4:5], vcc_lo offset:4
EncodeSopp(0x0c, 0), // s_waitcnt 0
EncodeSop2(0x01, 4, 4, 6), // s_sub_u32 s4, s4, s6
EncodeSop2(0x05, 5, 5, 128), // s_subb_u32 s5, s5, 0
EncodeSop1(0x20, 0, 4), // s_setpc_b64 s[4:5]
EncodeSMovB32(1, 129), // case 0
EncodeSmem0(0x00, offset_sgpr, 2),
(106u << 25u) | 4u, // s_load_dword offset, s[4:5], vcc_lo offset:4
EncodeSopp(0x0c, 0), // s_waitcnt 0
EncodeSop2(0x01, 4, 4, offset_sgpr), // s_sub_u32 s4, s4, offset
EncodeSop2(0x05, 5, 5, 128), // s_subb_u32 s5, s5, 0
EncodeSop1(0x20, 0, 4), // s_setpc_b64 s[4:5]
EncodeSMovB32(1, 129), // case 0
EncodeSopp(0x02, 1),
EncodeSMovB32(2, 129), // case 1
EncodeSopp(0x01, 0),
0u,
0x00000010u,
0x00000008u,
0x00000010u, // table at pc 0x44, targets relative backward from pc 0x40
0x00000010u, // table at pc 0x4c, targets relative backward from pc 0x48
};
auto options = MakeCompileOptions(ShaderType::Compute);
@@ -11850,7 +11852,7 @@ void TestNewShaderRecompilerSetpcDwordJumpTable() {
result.program.blocks,
[](const auto *block) { return block->start_pc == 0x04u; }),
"subtractive S_SETPC_B64 table targets or selector mapping changed");
Check((result.ir_dump.find("SLoadDword") == std::string::npos),
Check((result.ir_dump.find("LoadAddressU32") == std::string::npos),
"subtractive S_SETPC_B64 table load reached normal IR");
Check(SpirvContainsOpcode(result.spirv, 251),
"subtractive S_SETPC_B64 dispatcher lacks OpSwitch");
@@ -14343,7 +14345,7 @@ void BuildTypedPlan(const uint32_t *code, uint32_t words,
ShaderRecompiler::IR::RemoveIdentities(ir);
ShaderRecompiler::IR::EliminateDeadCode(ir.blocks);
}
ShaderRecompiler::IR::TrackResources(ir, decoded, cfg);
ShaderRecompiler::IR::TrackResources(ir, decoded, cfg, options.input_info);
ShaderRecompiler::IR::EliminateDeadCode(ir.blocks);
}
@@ -16182,10 +16184,6 @@ void TestCompilerStageInputOwnership() {
CheckSpirvBinaryValidates(compiled.spirv);
#if KYTY_PLATFORM != KYTY_PLATFORM_WINDOWS
options.stage = ShaderType::Vertex;
options.input_info.vertex = nullptr;
ExpectFatal([&] { (void)TranslateProgram(shader, options); },
"missing stage metadata bypassed translation validation");
const uint32_t auxiliary_position[] = {
EncodeExp0(0x0d, 0x1), EncodeExp1(0, 0, 0, 0), EncodeSopp(0x01)};
for (const auto stage : {ShaderType::Pixel, ShaderType::Compute}) {
@@ -16768,7 +16766,8 @@ int main() {
TestEmbeddedVertexFormatSwizzle();
TestNewShaderRecompilerSetpcJumpTable();
TestNewShaderRecompilerPrunesUnreachableSetpcMetadata();
TestNewShaderRecompilerSetpcDwordJumpTable();
TestNewShaderRecompilerSetpcDwordJumpTable(6);
TestNewShaderRecompilerSetpcDwordJumpTable(106);
TestTypedEntryStateIsMinimal();
#if KYTY_PLATFORM != KYTY_PLATFORM_WINDOWS
TestFinalSsaRejectsRegisterStatePseudos();