Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion core/libs/prx/libSceAgcDriver/Graphics/src/Pipeline.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@ void ValidateViewport(const Context& context, const VkViewport& viewport) {
}

bool ShadersWriteMemory(std::span<const CompiledShader> shaders) {
const auto any = [](const std::vector<bool>& written, std::uint32_t count) { return written.size() < count || std::ranges::any_of(written, [](bool element) { return element; }); };
const auto any = [](const ShaderRecompiler::ElementFlags& written, std::uint32_t count) { return written.size() < count || std::ranges::any_of(written, [](bool element) { return element; }); };
for (const auto& shader : shaders) {
if (shader.program == nullptr) continue;
for (const auto& binding : shader.program->bindings) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1984,7 +1984,7 @@ struct ReusedTable {
std::uint64_t pendingSerial = 0;
std::vector<std::uint32_t> words;
std::vector<ShaderRecompiler::DescriptorImageShape> shapes;
std::vector<bool> depthCompare;
ShaderRecompiler::ElementFlags depthCompare;
std::vector<ReusedTableElement> elements;
std::vector<GuestMemory::UnchangedQuery> queries;
std::vector<std::pair<std::uint64_t, std::uint64_t>> ranges;
Expand Down Expand Up @@ -2820,7 +2820,7 @@ void VisitContentKey(const CompiledShader& shader, bool dataWords, bool movableB
visit(static_cast<std::uint32_t>(program.PipelineVariantId()));
visit(static_cast<std::uint32_t>(program.PipelineVariantId() >> 32u));
visit(static_cast<std::uint32_t>(program.bindings.size()));
const auto packBits = [&](const std::vector<bool>& bits) {
const auto packBits = [&](const ShaderRecompiler::ElementFlags& bits) {
visit(static_cast<std::uint32_t>(bits.size()));
std::uint32_t word = 0;
for (std::size_t i = 0; i < bits.size(); ++i) {
Expand Down
2 changes: 1 addition & 1 deletion core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2065,7 +2065,7 @@ void TuningFieldTests() {
}

void ShadersWriteMemoryTests() {
const auto binding = [](ShaderRecompiler::DescriptorKind kind, ShaderRecompiler::DescriptorRole role, std::vector<bool> written) {
const auto binding = [](ShaderRecompiler::DescriptorKind kind, ShaderRecompiler::DescriptorRole role, ShaderRecompiler::ElementFlags written) {
ShaderRecompiler::DescriptorBinding result{};
result.kind = kind;
result.role = role;
Expand Down
2 changes: 1 addition & 1 deletion core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11141,7 +11141,7 @@ void imageMemoTests(const Device& device, Recorder& recorder) {
Require(replaced != other && sample(second) == replaced && sample(first) == remade, "the cache does not answer with the replacements");
const auto firstWords = linearTextureWords(reinterpret_cast<std::uint64_t>(first), width, height, 56);
const auto secondWords = linearTextureWords(reinterpret_cast<std::uint64_t>(second), width, height, 56);
const auto bindElements = [&](std::initializer_list<std::array<std::uint32_t, 8>> elements, std::vector<bool> depthCompare) {
const auto bindElements = [&](std::initializer_list<std::array<std::uint32_t, 8>> elements, ShaderRecompiler::ElementFlags depthCompare) {
program.bindings[0].count = static_cast<std::uint32_t>(elements.size());
program.bindings[0].guestDescriptor.clear();
for (const auto& words : elements) program.bindings[0].guestDescriptor.insert(program.bindings[0].guestDescriptor.end(), words.begin(), words.end());
Expand Down
35 changes: 35 additions & 0 deletions core/libs/prx/libSceAgcDriver/tests/ShaderDiskCache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -477,6 +477,40 @@ void verifyResultRoundTrip() {
require(!ShaderDiskCache::DecodeResult(bytes, decoded), "an incompatible runtime ABI result decodes");
}

void verifyElementFlags() {
ElementFlags flags;
std::vector<bool> expected;
for (std::size_t index = 0; index < 70; ++index) {
flags.push_back(index % 3 == 0);
expected.push_back(index % 3 == 0);
require(flags == expected, "element flags differ from a vector after " + std::to_string(index + 1) + " entries");
}
const auto copy = flags;
require(copy == flags && copy.size() == 70 && copy.at(69) && !copy.at(68), "a copy of spilled element flags differs");
require(std::count(flags.begin(), flags.end(), true) == 24, "spilled element flags do not iterate");
auto writable = flags;
writable[69] = false;
writable.back().flip();
writable[0] = writable[1];
require(writable.size() == 70 && writable.back() && !writable[0] && writable[3], "a write through a spilled flag was lost");
writable.resize(3);
require(writable == std::vector<bool>{false, false, false}, "shrinking spilled element flags kept the wrong entries");
writable.resize(5, true);
writable[1] = true;
writable.back().flip();
require(writable == std::vector<bool>{false, true, false, true, false}, "a write through an inline flag was lost");
auto result = sampleResult();
result.bindings[1].bufferWritten = flags;
std::vector<std::byte> bytes;
ShaderDiskCache::EncodeResult(result, bytes);
RecompileResult decoded;
require(ShaderDiskCache::DecodeResult(bytes, decoded) && decoded.bindings[1].bufferWritten == expected, "spilled element flags do not round trip");
flags.assign(3, true);
require(flags == std::vector<bool>{true, true, true}, "assigned element flags kept spilled entries");
flags.clear();
require(flags.empty() && flags == ElementFlags{}, "cleared element flags are not empty");
}

void verifyEntryRoundTrip() {
SampleRequest sample;
const auto key = sample.Key();
Expand Down Expand Up @@ -2667,6 +2701,7 @@ int main(int argc, char** argv) {
setPathEnvironment("ANYPS5_SHADER_CACHE_DIR", directory);
verifyAtomicReplacement(directory);
verifyResultRoundTrip();
verifyElementFlags();
verifyEntryRoundTrip();
verifyPrebuiltPack(directory / "packs");
verifyArtifactStorageIsolation();
Expand Down
4 changes: 4 additions & 0 deletions core/shader/recompiler/Recompiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1612,6 +1612,10 @@ std::uint64_t vectorBytes(const std::vector<bool>& values) {
return static_cast<std::uint64_t>(values.capacity() + 7u) / 8u;
}

std::uint64_t vectorBytes(const ElementFlags& values) {
return values.size() > 64u ? (static_cast<std::uint64_t>(values.size()) + 7u) / 8u : 0u;
}

std::uint64_t descriptorBindingBytes(const DescriptorBinding& binding) {
return vectorBytes(binding.guestDescriptor) + vectorBytes(binding.imageShapes) + vectorBytes(binding.imageSamplers) + vectorBytes(binding.bufferExtent) + vectorBytes(binding.samplerDepthCompare) + vectorBytes(binding.imageWritten) + vectorBytes(binding.imageDepthCompare) + vectorBytes(binding.imageAtomic) + vectorBytes(binding.imageAtomic64) + vectorBytes(binding.bufferAtomic) + vectorBytes(binding.bufferWritten) + vectorBytes(binding.samplerUnnormalized) + vectorBytes(binding.samplerIntegerBorder) + vectorBytes(binding.samplerCoordinatesIgnored) + vectorBytes(binding.imageUnnormalized) + vectorBytes(binding.imageUnnormalizedBaseLevel) + vectorBytes(binding.bufferRead);
}
Expand Down
150 changes: 137 additions & 13 deletions core/shader/recompiler/Recompiler.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@
#include <cstddef>
#include <cstdint>
#include <initializer_list>
#include <iterator>
#include <memory>
#include <optional>
#include <span>
#include <ranges>
#include <stdexcept>
#include <functional>
#include <utility>
#include <string_view>
Expand Down Expand Up @@ -531,6 +533,128 @@ enum class DescriptorRole {
ImageStorageTable
};

class ElementFlags {
public:
class reference {
public:
reference(ElementFlags& flags, std::size_t index) : flags(flags), index(index) {}
operator bool() const { return static_cast<const ElementFlags&>(flags)[index]; }
reference& operator=(bool flag) {
flags.set(index, flag);
return *this;
}
reference& operator=(const reference& other) { return *this = static_cast<bool>(other); }
void flip() { flags.set(index, !static_cast<bool>(*this)); }

private:
ElementFlags& flags;
std::size_t index;
};

class const_iterator {
public:
using iterator_category = std::forward_iterator_tag;
using value_type = bool;
using difference_type = std::ptrdiff_t;
using pointer = void;
using reference = bool;
const_iterator() = default;
const_iterator(const ElementFlags* flags, std::size_t index) : flags(flags), index(index) {}
bool operator*() const { return (*flags)[index]; }
const_iterator& operator++() {
++index;
return *this;
}
const_iterator operator++(int) {
auto previous = *this;
++index;
return previous;
}
bool operator==(const const_iterator& other) const { return index == other.index; }
bool operator!=(const const_iterator& other) const { return index != other.index; }

private:
const ElementFlags* flags = nullptr;
std::size_t index = 0;
};

ElementFlags() = default;
ElementFlags(std::initializer_list<bool> flags) {
for (const bool flag : flags) push_back(flag);
}
std::size_t size() const { return count; }
bool empty() const { return count == 0; }
bool operator[](std::size_t index) const { return count <= InlineFlags ? ((bits >> index) & 1u) != 0 : spill[index]; }
reference operator[](std::size_t index) { return {*this, index}; }
bool back() const { return (*this)[count - 1]; }
reference back() { return {*this, count - 1}; }
bool at(std::size_t index) const {
if (index >= count) throw std::out_of_range("ElementFlags::at");
return (*this)[index];
}
void push_back(bool flag) {
if (count < InlineFlags) {
if (flag) bits |= std::uint64_t{1} << count;
++count;
return;
}
if (count == InlineFlags) {
for (std::size_t index = 0; index < InlineFlags; ++index) spill.push_back(((bits >> index) & 1u) != 0);
}
spill.push_back(flag);
++count;
}
void clear() {
bits = 0;
count = 0;
spill.clear();
}
void assign(std::size_t size, bool flag) {
clear();
for (std::size_t index = 0; index < size; ++index) push_back(flag);
}
void resize(std::size_t size, bool flag = false) {
if (size < count) {
ElementFlags kept;
for (std::size_t index = 0; index < size; ++index) kept.push_back((*this)[index]);
*this = std::move(kept);
return;
}
while (count < size) push_back(flag);
}
const_iterator begin() const { return {this, 0}; }
const_iterator end() const { return {this, count}; }
bool operator==(const ElementFlags& other) const {
if (count != other.count) return false;
for (std::size_t index = 0; index < count; ++index) {
if ((*this)[index] != other[index]) return false;
}
return true;
}
bool operator==(const std::vector<bool>& other) const {
if (count != other.size()) return false;
for (std::size_t index = 0; index < count; ++index) {
if ((*this)[index] != other[index]) return false;
}
return true;
}

private:
void set(std::size_t index, bool flag) {
if (count > InlineFlags) {
spill[index] = flag;
return;
}
const auto bit = std::uint64_t{1} << index;
bits = flag ? bits | bit : bits & ~bit;
}

static constexpr std::size_t InlineFlags = 64;
std::uint64_t bits = 0;
std::size_t count = 0;
std::vector<bool> spill;
};

struct DescriptorBinding {
DescriptorKind kind;
DescriptorRole role;
Expand All @@ -540,30 +664,30 @@ struct DescriptorBinding {
std::vector<std::uint32_t> guestDescriptor;
bool readOnly = false;
std::optional<DescriptorImageShape> imageShape;
std::vector<bool> samplerDepthCompare;
ElementFlags samplerDepthCompare;
// Guest image elements the shader stores to (or updates atomically); the others are only read.
std::vector<bool> imageWritten;
std::vector<bool> imageDepthCompare;
std::vector<bool> imageAtomic;
std::vector<bool> imageAtomic64;
ElementFlags imageWritten;
ElementFlags imageDepthCompare;
ElementFlags imageAtomic;
ElementFlags imageAtomic64;
std::vector<DescriptorImageShape> imageShapes;
// Guest buffer elements the shader updates atomically (one entry per element of a GuestBuffers
// binding, empty otherwise). An atomic on a host-imported range is a serialized PCIe round trip
// (~0.4-0.5 us each on NVIDIA), so a driver may keep these elements in device-local memory.
std::vector<bool> bufferAtomic;
ElementFlags bufferAtomic;
// Guest buffer elements the shader may store to through this V# (any store or atomic in the
// program, whatever its offset), one entry per element of a GuestBuffers binding, empty
// otherwise. A false entry is proved: every access of that element is a load. A driver may then
// skip the write-back and the pending-write note for the element; an element beyond the vector
// (a producer that does not fill it) must be treated as written.
std::vector<bool> bufferWritten;
std::vector<bool> samplerUnnormalized;
std::vector<bool> samplerIntegerBorder;
std::vector<bool> samplerCoordinatesIgnored;
std::vector<bool> imageUnnormalized;
std::vector<bool> imageUnnormalizedBaseLevel;
ElementFlags bufferWritten;
ElementFlags samplerUnnormalized;
ElementFlags samplerIntegerBorder;
ElementFlags samplerCoordinatesIgnored;
ElementFlags imageUnnormalized;
ElementFlags imageUnnormalizedBaseLevel;
std::vector<std::uint64_t> imageSamplers;
std::vector<bool> bufferRead;
ElementFlags bufferRead;
std::vector<std::uint32_t> bufferExtent;
};

Expand Down
10 changes: 5 additions & 5 deletions core/shader/recompiler/ShaderDiskCache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ static_assert(sizeof(ShaderInvocation) == 216, "ShaderInvocation changed: update
#if defined(__cpp_lib_atomic_shared_ptr)
static_assert(sizeof(RecompileResult) == 552, "RecompileResult changed: update EncodeResult and DecodeResult");
#endif
static_assert(sizeof(DescriptorBinding) == 656, "DescriptorBinding changed: update the binding encoder");
static_assert(sizeof(DescriptorBinding) == 864, "DescriptorBinding changed: update the binding encoder");
static_assert(sizeof(VertexAttribute) == 32, "VertexAttribute changed: update the attribute encoder");
static_assert(sizeof(VertexInput) == 16, "VertexInput changed: update the vertex input encoder");
static_assert(sizeof(FragmentParameter) == 16, "FragmentParameter changed: update the parameter encoder");
Expand Down Expand Up @@ -303,7 +303,7 @@ class Writer {
out.insert(out.end(), bytes.begin(), bytes.end());
}

void Flags(const std::vector<bool>& flags) {
void Flags(const ElementFlags& flags) {
Value<std::uint64_t>(flags.size());
for (const bool flag : flags) Value(flag);
}
Expand Down Expand Up @@ -404,10 +404,10 @@ class Reader {
return SharedSpirv(std::move(words));
}

void Flags(std::vector<bool>& flags) {
void Flags(ElementFlags& flags) {
const auto count = Count(1);
flags.assign(count, false);
for (std::size_t i = 0; i < count; ++i) flags[i] = Get<bool>();
flags.clear();
for (std::size_t i = 0; i < count; ++i) flags.push_back(Get<bool>());
}

void Text(std::string& text) {
Expand Down
Loading