mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-10-18 23:07:07 -05:00
perf(agc): take a dword fill of any inline constant as a buffer fill
The zero fill kernel (v_mov_b32 v1, 0; buffer_store_format_x) is also emitted with other inline integer constants as its source. Run as compute dispatches, those writes are pending writes of unknown value, so a later read of DCC keys over their range waits for the dispatch. A constant of 0 to 64 or -1 to -16 is now matched like the zero fill and stored as a buffer fill of that value. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
016db923af
commit
b83755ac9d
4 files changed
+177
-139
No files matched your search
@@ -20,7 +20,7 @@ struct DwordPatternFill {
|
||||
};
|
||||
|
||||
std::optional<DwordPatternFill> MatchDwordPatternFill(std::span<const std::uint32_t> packet, std::span<const std::uint32_t> code, const std::vector<std::uint32_t>& userData, const ShaderRecompiler::ShaderComputeStageInfo& compute);
|
||||
std::optional<DwordPatternFill> MatchZeroDwordFill(std::span<const std::uint32_t> packet, std::span<const std::uint32_t> code, const std::vector<std::uint32_t>& userData, const ShaderRecompiler::ShaderComputeStageInfo& compute);
|
||||
std::optional<DwordPatternFill> MatchConstantDwordFill(std::span<const std::uint32_t> packet, std::span<const std::uint32_t> code, const std::vector<std::uint32_t>& userData, const ShaderRecompiler::ShaderComputeStageInfo& compute);
|
||||
std::optional<Graphics::MemoryValueFill> MatchMemoryValueBufferFill(std::span<const std::uint32_t> packet, std::span<const std::uint32_t> code, const std::vector<std::uint32_t>& userData, const ShaderRecompiler::ShaderComputeStageInfo& compute);
|
||||
bool IsByteFill(const std::array<std::uint32_t, 4>& pattern);
|
||||
std::optional<Graphics::DccKeys> KeysFillMemo(const std::array<std::uint32_t, 4>& pattern, bool coversStorageKeys, bool memoryValueFill);
|
||||
|
||||
@@ -69,11 +69,19 @@ std::optional<DwordPatternFill> MatchDwordPatternFill(std::span<const std::uint3
|
||||
return fill;
|
||||
}
|
||||
|
||||
std::optional<DwordPatternFill> MatchZeroDwordFill(std::span<const std::uint32_t> packet, std::span<const std::uint32_t> code, const std::vector<std::uint32_t>& userData, const ShaderRecompiler::ShaderComputeStageInfo& compute) {
|
||||
std::optional<DwordPatternFill> MatchConstantDwordFill(std::span<const std::uint32_t> packet, std::span<const std::uint32_t> code, const std::vector<std::uint32_t>& userData, const ShaderRecompiler::ShaderComputeStageInfo& compute) {
|
||||
static const bool enabled = std::getenv("APS5_NO_FILL_HLE") == nullptr;
|
||||
if (!enabled || userData.size() != 4 || packet.size() < 5 || !compute.groupIdEnable[0] || compute.numThreads[0] != 64 || compute.numThreads[1] != 1 || compute.numThreads[2] != 1) return std::nullopt;
|
||||
static constexpr std::array<std::uint32_t, 6> kernel{0xd7460000u, 0x04010c04u, 0x7e020280u, 0xe0102000u, 0x80000100u, 0xbf810000u};
|
||||
if (code.size() < kernel.size() || !std::equal(kernel.begin(), kernel.end(), code.begin())) return std::nullopt;
|
||||
static constexpr std::array<std::uint32_t, 6> kernel{0xd7460000u, 0x04010c04u, 0x7e020200u, 0xe0102000u, 0x80000100u, 0xbf810000u};
|
||||
if (code.size() < kernel.size()) return std::nullopt;
|
||||
for (std::size_t word = 0; word < kernel.size(); ++word) {
|
||||
if ((word == 2 ? code[word] & ~0x1ffu : code[word]) != kernel[word]) return std::nullopt;
|
||||
}
|
||||
const auto source = code[2] & 0x1ffu;
|
||||
std::uint32_t value;
|
||||
if (source >= 0x80u && source <= 0xc0u) value = source - 0x80u;
|
||||
else if (source >= 0xc1u && source <= 0xd0u) value = 0u - (source - 0xc0u);
|
||||
else return std::nullopt;
|
||||
const auto stride = (userData[1] >> 16u) & 0x3fffu;
|
||||
const bool swizzled = ((userData[1] >> 31u) & 1u) != 0;
|
||||
const bool addTid = ((userData[3] >> 23u) & 1u) != 0;
|
||||
@@ -84,7 +92,9 @@ std::optional<DwordPatternFill> MatchZeroDwordFill(std::span<const std::uint32_t
|
||||
const auto dwords = std::min<std::uint64_t>(static_cast<std::uint64_t>(packet[1]) * 64u, userData[2]);
|
||||
const auto base = userData[0] | (static_cast<std::uint64_t>(userData[1] & 0xffffu) << 32u);
|
||||
if (dwords == 0 || dwords % 4u != 0 || base % 16u != 0) return std::nullopt;
|
||||
return DwordPatternFill{base, static_cast<std::size_t>(dwords * 4u), {}};
|
||||
DwordPatternFill fill{base, static_cast<std::size_t>(dwords * 4u), {}};
|
||||
fill.pattern.fill(value);
|
||||
return fill;
|
||||
}
|
||||
|
||||
std::optional<Graphics::MemoryValueFill> MatchMemoryValueBufferFill(std::span<const std::uint32_t> packet, std::span<const std::uint32_t> code, const std::vector<std::uint32_t>& userData, const ShaderRecompiler::ShaderComputeStageInfo& compute) {
|
||||
@@ -179,7 +189,7 @@ void Driver::fillClearCount(const Graphics::StorageTexture::FillCoverage& covera
|
||||
|
||||
bool Driver::fillBuffer(QueueState& queue, std::uint32_t queueId, std::span<const std::uint32_t> packet, std::span<const std::uint32_t> code, const std::vector<std::uint32_t>& userData, const ShaderRecompiler::ShaderComputeStageInfo& compute, const std::shared_ptr<VulkanDevice>& localDevice) {
|
||||
auto dwordFill = MatchDwordPatternFill(packet, code, userData, compute);
|
||||
if (!dwordFill) dwordFill = MatchZeroDwordFill(packet, code, userData, compute);
|
||||
if (!dwordFill) dwordFill = MatchConstantDwordFill(packet, code, userData, compute);
|
||||
std::optional<Graphics::MemoryValueFill> memoryFill;
|
||||
if (!dwordFill) memoryFill = MatchMemoryValueBufferFill(packet, code, userData, compute);
|
||||
if (!dwordFill && !memoryFill && !matchesFillKernel(code, userData, compute)) return false;
|
||||
|
||||
@@ -0,0 +1,161 @@
|
||||
#include "prx/libSceAgcDriver/Execution/include/Driver.hpp"
|
||||
#include "prx/libSceAgcDriver/Execution/include/Driver/Memory/DwordPatternFill.hpp"
|
||||
#include "prx/libSceAgcDriver/Execution/include/GuestMemory.hpp"
|
||||
#include "prx/libSceAgcDriver/Graphics/include/Draw.hpp"
|
||||
#include "VulkanTestDevice.hpp"
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace {
|
||||
|
||||
using AgcDriver::Graphics::Require;
|
||||
using AgcDriver::DriverDetail::MatchConstantDwordFill;
|
||||
|
||||
alignas(256) constexpr std::array<std::uint32_t, 6> Code{0xd7460000u, 0x04010c04u, 0x7e020280u, 0xe0102000u, 0x80000100u, 0xbf810000u};
|
||||
alignas(256) constexpr std::array<std::uint32_t, 6> OneCode{0xd7460000u, 0x04010c04u, 0x7e020281u, 0xe0102000u, 0x80000100u, 0xbf810000u};
|
||||
alignas(256) constexpr std::array<std::uint32_t, 6> SixtyFourCode{0xd7460000u, 0x04010c04u, 0x7e0202c0u, 0xe0102000u, 0x80000100u, 0xbf810000u};
|
||||
alignas(256) constexpr std::array<std::uint32_t, 6> MinusOneCode{0xd7460000u, 0x04010c04u, 0x7e0202c1u, 0xe0102000u, 0x80000100u, 0xbf810000u};
|
||||
alignas(256) constexpr std::array<std::uint32_t, 6> MinusSixteenCode{0xd7460000u, 0x04010c04u, 0x7e0202d0u, 0xe0102000u, 0x80000100u, 0xbf810000u};
|
||||
alignas(256) constexpr std::array<std::uint32_t, 6> FloatHalfCode{0xd7460000u, 0x04010c04u, 0x7e0202f0u, 0xe0102000u, 0x80000100u, 0xbf810000u};
|
||||
alignas(256) constexpr std::array<std::uint32_t, 6> VgprCode{0xd7460000u, 0x04010c04u, 0x7e020300u, 0xe0102000u, 0x80000100u, 0xbf810000u};
|
||||
constexpr std::uint32_t Untouched = 0xcafebabeu;
|
||||
alignas(256) std::array<std::uint32_t, 1028> Output{};
|
||||
|
||||
std::vector<std::uint32_t> UserData(void* output, std::uint32_t records, std::uint32_t stride = 4, std::uint32_t format = 0x00014204u) {
|
||||
const auto address = reinterpret_cast<std::uintptr_t>(output);
|
||||
return {static_cast<std::uint32_t>(address), static_cast<std::uint32_t>((address >> 32u) & 0xffffu) | (stride << 16u), records, format};
|
||||
}
|
||||
|
||||
void MatchTests() {
|
||||
const ShaderRecompiler::ShaderComputeStageInfo compute{{64, 1, 1}, 0, {true, false, false}, false, 1};
|
||||
const std::array<std::uint32_t, 5> packet{0xc0031500u, 4, 1, 1, 0x41u};
|
||||
const auto data = UserData(Output.data(), 1024);
|
||||
const auto fill = MatchConstantDwordFill(packet, Code, data, compute);
|
||||
Require(fill && fill->base == reinterpret_cast<std::uintptr_t>(Output.data()) && fill->bytes == 1024 && fill->pattern == std::array<std::uint32_t, 4>{}, "the zero dword fill kernel was not recognized as a zero fill");
|
||||
for (std::size_t word = 0; word < Code.size(); ++word) {
|
||||
auto altered = Code;
|
||||
altered[word] ^= 0x10000u;
|
||||
Require(!MatchConstantDwordFill(packet, altered, data, compute), "a different shader matched the zero dword fill kernel");
|
||||
}
|
||||
Require(!MatchConstantDwordFill(packet, std::span(Code).first(5), data, compute), "a truncated shader matched");
|
||||
Require(!MatchConstantDwordFill(packet, Code, UserData(Output.data(), 1024, 8), compute), "a strided descriptor matched a contiguous fill");
|
||||
Require(!MatchConstantDwordFill(packet, Code, UserData(Output.data(), 1024, 4, 0x00814204u), compute), "an add-tid descriptor matched");
|
||||
Require(!MatchConstantDwordFill(packet, Code, UserData(Output.data(), 1024, 4, 0x30014204u), compute), "a raw out-of-bounds mode matched");
|
||||
Require(!MatchConstantDwordFill(packet, Code, UserData(Output.data() + 1, 1024), compute), "an unaligned destination matched");
|
||||
Require(!MatchConstantDwordFill(packet, Code, UserData(Output.data(), 1024, 4, 0x00011204u), compute), "an 8-bit output format matched");
|
||||
Require(!MatchConstantDwordFill(packet, Code, UserData(Output.data(), 1024, 4, 0x00017204u), compute), "an output format above 32_FLOAT matched");
|
||||
Require(MatchConstantDwordFill(packet, Code, UserData(Output.data(), 1024, 4, 0x00015204u), compute).has_value(), "32_SINT output did not match");
|
||||
Require(MatchConstantDwordFill(packet, Code, UserData(Output.data(), 1024, 4, 0x00016204u), compute).has_value(), "32_FLOAT output did not match");
|
||||
Require(!MatchConstantDwordFill(packet, Code, UserData(Output.data(), 102), compute), "a record count not filling 16-byte units matched");
|
||||
auto extra = data;
|
||||
extra.push_back(0);
|
||||
Require(!MatchConstantDwordFill(packet, Code, extra, compute), "a different user SGPR count matched (the group id moves)");
|
||||
auto partial = packet;
|
||||
partial[4] |= 0x20u;
|
||||
Require(!MatchConstantDwordFill(partial, Code, data, compute), "a partial dispatch matched");
|
||||
auto noGroup = compute;
|
||||
noGroup.groupIdEnable[0] = false;
|
||||
Require(!MatchConstantDwordFill(packet, Code, data, noGroup), "a dispatch without the group id matched");
|
||||
const auto clamped = MatchConstantDwordFill(packet, Code, UserData(Output.data(), 64), compute);
|
||||
Require(clamped && clamped->bytes == 256, "the fill exceeded the descriptor's records");
|
||||
struct Constant { std::span<const std::uint32_t> code; std::uint32_t value; };
|
||||
for (const auto& item : {Constant{OneCode, 1u}, Constant{SixtyFourCode, 64u}, Constant{MinusOneCode, 0xffffffffu}, Constant{MinusSixteenCode, 0xfffffff0u}}) {
|
||||
const auto constant = MatchConstantDwordFill(packet, item.code, data, compute);
|
||||
Require(constant && constant->bytes == 1024 && constant->pattern == std::array<std::uint32_t, 4>{item.value, item.value, item.value, item.value}, "an inline constant dword fill did not match with its value");
|
||||
}
|
||||
Require(!MatchConstantDwordFill(packet, FloatHalfCode, data, compute), "a float inline constant matched");
|
||||
Require(!MatchConstantDwordFill(packet, VgprCode, data, compute), "a fill of a vector register matched");
|
||||
}
|
||||
|
||||
std::vector<std::uint32_t> Commands(std::span<const std::uint32_t> code, const std::vector<std::uint32_t>& data, std::uint32_t groups, bool partial, std::uint32_t threads) {
|
||||
std::vector<std::uint32_t> words;
|
||||
const auto registers = [&](std::uint32_t first, std::span<const std::uint32_t> values) {
|
||||
words.push_back(0xc0007600u | (static_cast<std::uint32_t>(values.size()) << 16u));
|
||||
words.push_back(first);
|
||||
words.insert(words.end(), values.begin(), values.end());
|
||||
};
|
||||
const std::array<std::uint32_t, 3> shape{64, 1, 1};
|
||||
registers(0x207, shape);
|
||||
const auto address = reinterpret_cast<std::uintptr_t>(code.data());
|
||||
const std::array<std::uint32_t, 2> program{static_cast<std::uint32_t>(address >> 8u), static_cast<std::uint32_t>(address >> 40u)};
|
||||
registers(0x20c, program);
|
||||
const std::array<std::uint32_t, 1> resource{(static_cast<std::uint32_t>(data.size()) << 1u) | (1u << 7u)};
|
||||
registers(0x213, resource);
|
||||
registers(0x240, data);
|
||||
words.insert(words.end(), {0xc0031500u, partial ? threads : groups, 1, 1, 0x41u | (partial ? 0x20u : 0u)});
|
||||
return words;
|
||||
}
|
||||
|
||||
void ExecuteTests() {
|
||||
struct Case {
|
||||
std::uint32_t groups, records, stride;
|
||||
bool partial;
|
||||
std::uint32_t threads;
|
||||
};
|
||||
constexpr std::array<Case, 8> cases{{
|
||||
{4, 1024, 4, false, 0},
|
||||
{16, 1024, 4, false, 0},
|
||||
{4, 64, 4, false, 0},
|
||||
{4, 100, 4, false, 0},
|
||||
{4, 102, 4, false, 0},
|
||||
{2, 1024, 8, false, 0},
|
||||
{0, 1024, 4, true, 70},
|
||||
{0, 1024, 4, false, 0},
|
||||
}};
|
||||
for (const auto& item : cases) {
|
||||
Output.fill(Untouched);
|
||||
const auto words = Commands(Code, UserData(Output.data(), item.records, item.stride), item.groups, item.partial, item.threads);
|
||||
Packet packet{const_cast<std::uint32_t*>(words.data()), static_cast<std::uint32_t>(words.size()), 0, {}};
|
||||
AgcDriver::Submit(&packet, 0);
|
||||
AgcDriverWaitIdle_nid_postfix();
|
||||
AgcDriver::GuestMemory::FlushGpuWrites(reinterpret_cast<std::uintptr_t>(Output.data()), sizeof(Output));
|
||||
const auto launched = item.partial ? item.threads : item.groups * 64u;
|
||||
const auto filled = std::min(launched, item.records);
|
||||
const auto step = item.stride / 4u;
|
||||
for (std::uint32_t i = 0; i < Output.size(); ++i) {
|
||||
const bool written = i % step == 0 && i / step < filled;
|
||||
const auto expected = written ? 0u : Untouched;
|
||||
Require(Output[i] == expected, "zero dword fill mismatch at " + std::to_string(i) + " (groups " + std::to_string(item.groups) + ", records " + std::to_string(item.records) + ", stride " + std::to_string(item.stride) + "): got " + std::to_string(Output[i]) + ", expected " + std::to_string(expected));
|
||||
}
|
||||
}
|
||||
struct ConstantCase { std::span<const std::uint32_t> code; std::uint32_t value, groups, records; };
|
||||
for (const auto& item : {ConstantCase{OneCode, 1u, 4, 256}, ConstantCase{SixtyFourCode, 64u, 2, 1000}, ConstantCase{MinusOneCode, 0xffffffffu, 4, 255}, ConstantCase{MinusSixteenCode, 0xfffffff0u, 4, 256}}) {
|
||||
Output.fill(Untouched);
|
||||
const auto words = Commands(item.code, UserData(Output.data(), item.records), item.groups, false, 0);
|
||||
Packet packet{const_cast<std::uint32_t*>(words.data()), static_cast<std::uint32_t>(words.size()), 0, {}};
|
||||
AgcDriver::Submit(&packet, 0);
|
||||
AgcDriverWaitIdle_nid_postfix();
|
||||
AgcDriver::GuestMemory::FlushGpuWrites(reinterpret_cast<std::uintptr_t>(Output.data()), sizeof(Output));
|
||||
const auto filled = std::min(item.groups * 64u, item.records);
|
||||
for (std::uint32_t i = 0; i < Output.size(); ++i) {
|
||||
const auto expected = i < filled ? item.value : Untouched;
|
||||
Require(Output[i] == expected, "inline constant dword fill mismatch at " + std::to_string(i) + " (value " + std::to_string(item.value) + "): got " + std::to_string(Output[i]) + ", expected " + std::to_string(expected));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int main() {
|
||||
try {
|
||||
if (std::getenv("APS5_NO_FILL_HLE") == nullptr) MatchTests();
|
||||
{
|
||||
const auto device = OpenVulkanTestDevice();
|
||||
if (!device) return VulkanTestSkipped;
|
||||
}
|
||||
ExecuteTests();
|
||||
AgcDriverShutdown_nid_postfix();
|
||||
std::puts("constant dword fill tests passed");
|
||||
return 0;
|
||||
} catch (const std::exception& error) {
|
||||
std::cerr << error.what() << '\n';
|
||||
AgcDriverShutdown_nid_postfix();
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -1,133 +0,0 @@
|
||||
#include "prx/libSceAgcDriver/Execution/include/Driver.hpp"
|
||||
#include "prx/libSceAgcDriver/Execution/include/Driver/Memory/DwordPatternFill.hpp"
|
||||
#include "prx/libSceAgcDriver/Execution/include/GuestMemory.hpp"
|
||||
#include "prx/libSceAgcDriver/Graphics/include/Draw.hpp"
|
||||
#include "VulkanTestDevice.hpp"
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace {
|
||||
|
||||
using AgcDriver::Graphics::Require;
|
||||
using AgcDriver::DriverDetail::MatchZeroDwordFill;
|
||||
|
||||
alignas(256) constexpr std::array<std::uint32_t, 6> Code{0xd7460000u, 0x04010c04u, 0x7e020280u, 0xe0102000u, 0x80000100u, 0xbf810000u};
|
||||
constexpr std::uint32_t Untouched = 0xcafebabeu;
|
||||
alignas(256) std::array<std::uint32_t, 1028> Output{};
|
||||
|
||||
std::vector<std::uint32_t> UserData(void* output, std::uint32_t records, std::uint32_t stride = 4, std::uint32_t format = 0x00014204u) {
|
||||
const auto address = reinterpret_cast<std::uintptr_t>(output);
|
||||
return {static_cast<std::uint32_t>(address), static_cast<std::uint32_t>((address >> 32u) & 0xffffu) | (stride << 16u), records, format};
|
||||
}
|
||||
|
||||
void MatchTests() {
|
||||
const ShaderRecompiler::ShaderComputeStageInfo compute{{64, 1, 1}, 0, {true, false, false}, false, 1};
|
||||
const std::array<std::uint32_t, 5> packet{0xc0031500u, 4, 1, 1, 0x41u};
|
||||
const auto data = UserData(Output.data(), 1024);
|
||||
const auto fill = MatchZeroDwordFill(packet, Code, data, compute);
|
||||
Require(fill && fill->base == reinterpret_cast<std::uintptr_t>(Output.data()) && fill->bytes == 1024 && fill->pattern == std::array<std::uint32_t, 4>{}, "the zero dword fill kernel was not recognized as a zero fill");
|
||||
for (std::size_t word = 0; word < Code.size(); ++word) {
|
||||
auto altered = Code;
|
||||
altered[word] ^= 1u;
|
||||
Require(!MatchZeroDwordFill(packet, altered, data, compute), "a different shader matched the zero dword fill kernel");
|
||||
}
|
||||
Require(!MatchZeroDwordFill(packet, std::span(Code).first(5), data, compute), "a truncated shader matched");
|
||||
Require(!MatchZeroDwordFill(packet, Code, UserData(Output.data(), 1024, 8), compute), "a strided descriptor matched a contiguous fill");
|
||||
Require(!MatchZeroDwordFill(packet, Code, UserData(Output.data(), 1024, 4, 0x00814204u), compute), "an add-tid descriptor matched");
|
||||
Require(!MatchZeroDwordFill(packet, Code, UserData(Output.data(), 1024, 4, 0x30014204u), compute), "a raw out-of-bounds mode matched");
|
||||
Require(!MatchZeroDwordFill(packet, Code, UserData(Output.data() + 1, 1024), compute), "an unaligned destination matched");
|
||||
Require(!MatchZeroDwordFill(packet, Code, UserData(Output.data(), 1024, 4, 0x00011204u), compute), "an 8-bit output format matched");
|
||||
Require(!MatchZeroDwordFill(packet, Code, UserData(Output.data(), 1024, 4, 0x00017204u), compute), "an output format above 32_FLOAT matched");
|
||||
Require(MatchZeroDwordFill(packet, Code, UserData(Output.data(), 1024, 4, 0x00015204u), compute).has_value(), "32_SINT output did not match");
|
||||
Require(MatchZeroDwordFill(packet, Code, UserData(Output.data(), 1024, 4, 0x00016204u), compute).has_value(), "32_FLOAT output did not match");
|
||||
Require(!MatchZeroDwordFill(packet, Code, UserData(Output.data(), 102), compute), "a record count not filling 16-byte units matched");
|
||||
auto extra = data;
|
||||
extra.push_back(0);
|
||||
Require(!MatchZeroDwordFill(packet, Code, extra, compute), "a different user SGPR count matched (the group id moves)");
|
||||
auto partial = packet;
|
||||
partial[4] |= 0x20u;
|
||||
Require(!MatchZeroDwordFill(partial, Code, data, compute), "a partial dispatch matched");
|
||||
auto noGroup = compute;
|
||||
noGroup.groupIdEnable[0] = false;
|
||||
Require(!MatchZeroDwordFill(packet, Code, data, noGroup), "a dispatch without the group id matched");
|
||||
const auto clamped = MatchZeroDwordFill(packet, Code, UserData(Output.data(), 64), compute);
|
||||
Require(clamped && clamped->bytes == 256, "the fill exceeded the descriptor's records");
|
||||
}
|
||||
|
||||
std::vector<std::uint32_t> Commands(const std::vector<std::uint32_t>& data, std::uint32_t groups, bool partial, std::uint32_t threads) {
|
||||
std::vector<std::uint32_t> words;
|
||||
const auto registers = [&](std::uint32_t first, std::span<const std::uint32_t> values) {
|
||||
words.push_back(0xc0007600u | (static_cast<std::uint32_t>(values.size()) << 16u));
|
||||
words.push_back(first);
|
||||
words.insert(words.end(), values.begin(), values.end());
|
||||
};
|
||||
const std::array<std::uint32_t, 3> shape{64, 1, 1};
|
||||
registers(0x207, shape);
|
||||
const auto address = reinterpret_cast<std::uintptr_t>(Code.data());
|
||||
const std::array<std::uint32_t, 2> program{static_cast<std::uint32_t>(address >> 8u), static_cast<std::uint32_t>(address >> 40u)};
|
||||
registers(0x20c, program);
|
||||
const std::array<std::uint32_t, 1> resource{(static_cast<std::uint32_t>(data.size()) << 1u) | (1u << 7u)};
|
||||
registers(0x213, resource);
|
||||
registers(0x240, data);
|
||||
words.insert(words.end(), {0xc0031500u, partial ? threads : groups, 1, 1, 0x41u | (partial ? 0x20u : 0u)});
|
||||
return words;
|
||||
}
|
||||
|
||||
void ExecuteTests() {
|
||||
struct Case {
|
||||
std::uint32_t groups, records, stride;
|
||||
bool partial;
|
||||
std::uint32_t threads;
|
||||
};
|
||||
constexpr std::array<Case, 8> cases{{
|
||||
{4, 1024, 4, false, 0},
|
||||
{16, 1024, 4, false, 0},
|
||||
{4, 64, 4, false, 0},
|
||||
{4, 100, 4, false, 0},
|
||||
{4, 102, 4, false, 0},
|
||||
{2, 1024, 8, false, 0},
|
||||
{0, 1024, 4, true, 70},
|
||||
{0, 1024, 4, false, 0},
|
||||
}};
|
||||
for (const auto& item : cases) {
|
||||
Output.fill(Untouched);
|
||||
const auto words = Commands(UserData(Output.data(), item.records, item.stride), item.groups, item.partial, item.threads);
|
||||
Packet packet{const_cast<std::uint32_t*>(words.data()), static_cast<std::uint32_t>(words.size()), 0, {}};
|
||||
AgcDriver::Submit(&packet, 0);
|
||||
AgcDriverWaitIdle_nid_postfix();
|
||||
AgcDriver::GuestMemory::FlushGpuWrites(reinterpret_cast<std::uintptr_t>(Output.data()), sizeof(Output));
|
||||
const auto launched = item.partial ? item.threads : item.groups * 64u;
|
||||
const auto filled = std::min(launched, item.records);
|
||||
const auto step = item.stride / 4u;
|
||||
for (std::uint32_t i = 0; i < Output.size(); ++i) {
|
||||
const bool written = i % step == 0 && i / step < filled;
|
||||
const auto expected = written ? 0u : Untouched;
|
||||
Require(Output[i] == expected, "zero dword fill mismatch at " + std::to_string(i) + " (groups " + std::to_string(item.groups) + ", records " + std::to_string(item.records) + ", stride " + std::to_string(item.stride) + "): got " + std::to_string(Output[i]) + ", expected " + std::to_string(expected));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int main() {
|
||||
try {
|
||||
if (std::getenv("APS5_NO_FILL_HLE") == nullptr) MatchTests();
|
||||
{
|
||||
const auto device = OpenVulkanTestDevice();
|
||||
if (!device) return VulkanTestSkipped;
|
||||
}
|
||||
ExecuteTests();
|
||||
AgcDriverShutdown_nid_postfix();
|
||||
std::puts("zero dword fill tests passed");
|
||||
return 0;
|
||||
} catch (const std::exception& error) {
|
||||
std::cerr << error.what() << '\n';
|
||||
AgcDriverShutdown_nid_postfix();
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user