Skip to content
Closed
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
cae4c46
refactor(agc): split the cached address space checks out of AcquireRe…
EagleBearerYS Oct 14, 2026
cb9b3b4
perf(agc): reuse address-based draw builds whose inputs are unchanged
EagleBearerYS Oct 14, 2026
ec17620
docs(agc): record what address-based draw reuse proves and leaves out
EagleBearerYS Oct 14, 2026
bae2cd4
fix(agc): reuse draws whose buffer descriptors reach past their alloc…
EagleBearerYS Oct 16, 2026
bea388d
fix(agc): refuse address-based reuse after rejections or a throw
EagleBearerYS Oct 16, 2026
a937541
Merge upstream main into followup/6636-review-f1-f3
EagleBearerYS Oct 16, 2026
82b35b7
fix(agc): release a draw build's use when its batch is discarded
EagleBearerYS Oct 16, 2026
3fe43a3
fix(agc): order a draw's snapshots the same on decode misses and hits
EagleBearerYS Oct 16, 2026
89ab0ea
perf(agc): count address-based draw reuses and refusals in the draw p…
EagleBearerYS Oct 16, 2026
6e5b5bd
fix(agc): complete recorded GPU writes over snapshots before an addre…
EagleBearerYS Oct 16, 2026
b5d0a2a
Merge main into perf/agc-reuse-address-draw-builds
EagleBearerYS Oct 17, 2026
3db18a2
Merge main into perf/agc-reuse-address-draw-builds
EagleBearerYS Oct 18, 2026
ff5fe23
Merge main into perf/agc-reuse-address-draw-builds
EagleBearerYS Oct 18, 2026
4bacab4
docs(environment): add the row for the address-based draw reuse switch
EagleBearerYS Oct 18, 2026
ca05f74
Merge main into perf/agc-reuse-address-draw-builds
EagleBearerYS Oct 18, 2026
88b9b7f
Merge main into perf/agc-reuse-address-draw-builds
EagleBearerYS Oct 18, 2026
28433ba
Merge main into perf/agc-reuse-address-draw-builds
EagleBearerYS Oct 19, 2026
084de35
Merge commit 6696def6e6 into perf/agc-reuse-address-draw-builds
EagleBearerYS Oct 21, 2026
fc9c541
test(agc): check reused draw builds over a new multisampled target wi…
EagleBearerYS Oct 21, 2026
477f7ee
Merge main into perf/agc-reuse-address-draw-builds
EagleBearerYS Oct 21, 2026
7ee1eb9
fix(agc): size a moved read-only buffer at the render target as its b…
EagleBearerYS Oct 21, 2026
7cef9bc
docs(agc): cut the reused address-based draw builds entry to its gaps
EagleBearerYS Oct 21, 2026
b38ea43
Merge main into perf/agc-reuse-address-draw-builds
EagleBearerYS Oct 21, 2026
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
Original file line number Diff line number Diff line change
Expand Up @@ -308,8 +308,20 @@ class GuestBufferMemory {
};
std::optional<SplitReads> SplitInPlaceReads() const;
std::vector<std::pair<std::uint64_t, std::uint64_t>> DeviceReads() const;
std::optional<std::vector<std::pair<std::uint64_t, std::uint64_t>>> SpaceRegions() const;
std::vector<std::pair<std::uint64_t, std::uint64_t>> DirectWrites() const;
bool SameSnapshots(std::span<const GuestMemorySnapshot> snapshots) const;
void SuspendSpace() noexcept;
bool ResumeSpace();
bool SpaceImportsCurrent();
std::shared_ptr<const void> SpaceHold() const { return space; }

private:
struct SnapshotUse {
std::uint64_t address;
std::size_t bytes;
bool live;
};
struct Region {
std::uint64_t begin;
std::uint64_t end;
Expand Down Expand Up @@ -433,6 +445,8 @@ class GuestBufferMemory {
std::vector<std::pair<std::uint64_t, std::uint64_t>> writes;
std::vector<std::pair<std::uint64_t, std::uint64_t>> readsOverBase;
std::vector<std::pair<std::uint64_t, std::vector<std::byte>>> heapReferences;
std::vector<SnapshotUse> snapshotUses;
std::uint64_t suspendedSpace = 0;
// UploadPrepare ran (regions are frozen); `uploaded` once UploadFinish ran.
bool prepared = false;
bool uploaded = false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "Recompiler.hpp"
#include "prx/libSceAgcDriver/Graphics/include/Shaders.hpp"
#include <array>
#include <atomic>
#include <chrono>
#include <limits>
#include <list>
Expand Down Expand Up @@ -229,6 +230,10 @@ class ShaderResources {
const std::vector<std::shared_ptr<Texture>>& SampledTextures() const { return textures; }
bool ChecksFaults() const { return bda != nullptr; }
void CheckFaults() const;
bool InUse() const { return usesInFlight.load(std::memory_order_acquire) != 0; }
std::shared_ptr<const void> BeginUse();
void CompleteUse();
void EndUse() noexcept;
// `shaders` are the stages the object was built from, in build order (a recorded draw's vertex
// and fragment stages, or one compute stage): their bindings are walked like the build did.
// How a Revalidate proved (or refused) the object, for the [recipe] line: the proof path taken
Expand All @@ -239,7 +244,7 @@ class ShaderResources {
ProofPath path = ProofPath::Fast;
ProofFailure failure = ProofFailure::None;
};
bool Revalidate(std::span<const CompiledShader> shaders, ProofReport* report = nullptr);
bool Revalidate(std::span<const CompiledShader> shaders, ProofReport* report = nullptr, std::span<const GuestMemorySnapshot> snapshots = {});
bool Revalidate(const CompiledShader& shader, ProofReport* report = nullptr) { return Revalidate(std::span<const CompiledShader>(&shader, 1), report); }
// The proof a recipe hit runs on its template (design_cpu_final M4): Revalidate by that name.
bool ProveCurrent(std::span<const CompiledShader> shaders, ProofReport* report = nullptr) { return Revalidate(shaders, report); }
Expand Down Expand Up @@ -425,6 +430,7 @@ class ShaderResources {
void prepareAddressBindings(std::span<const CompiledShader> shaders, std::span<const GuestMemorySnapshot> snapshots);
VkDescriptorBufferInfo descriptor(Allocation& allocation);
void noteReusable();
bool proveBuild(std::span<const CompiledShader> shaders, ProofReport* report);
void reportDescriptorCaches() const;
// What a sampled texture was proved current against when the build (or the last full Revalidate)
// looked it up, so the next Revalidate can repeat the proof from write stamps and the DCC keys
Expand Down Expand Up @@ -548,6 +554,9 @@ class ShaderResources {
};
std::vector<SamplerSource> samplerSources;
bool reusable = false;
bool addressReuse = false;
std::atomic<std::uint32_t> usesInFlight{0};
std::vector<std::pair<std::uint64_t, std::uint64_t>> addressWrites;
std::vector<DirectRegion> directRegions;
std::vector<ValidatedSurface> validatedTextures;
// The pending registry's serial at the last Revalidate that proved this object, taken before
Expand Down
32 changes: 26 additions & 6 deletions core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1254,6 +1254,16 @@ std::shared_ptr<StorageTexture> refreshResidentTarget(const Context& context, co
return resident;
}

struct ResourceUse {
std::shared_ptr<ShaderResources> resources;
ResourceUse() = default;
ResourceUse(ResourceUse&& other) noexcept : resources(std::move(other.resources)) {}
ResourceUse& operator=(ResourceUse&& other) = delete;
~ResourceUse() {
if (resources != nullptr) resources->EndUse();
}
};

// A draw's resources (resolveDrawResources): the resource-cache template a recordable draw's
// stages repeat, or a fresh build.
struct ResolvedResources {
Expand All @@ -1262,6 +1272,7 @@ struct ResolvedResources {
ResourceCache::Key contentKey;
bool cacheable = false;
const ShaderResources::BuildTiming* built = nullptr;
ResourceUse use;
};

ResolvedResources resolveDrawResources(const Context& context, const State& state, const Pm4::DrawParameters& draw, std::span<const CompiledShader> shaders, std::span<const GuestMemorySnapshot> snapshots, std::uint64_t indexBytes, bool recordable, DrawOutcome& outcome, DrawTimer& timer) {
Expand All @@ -1281,18 +1292,20 @@ ResolvedResources resolveDrawResources(const Context& context, const State& stat
resolved.cacheable = recordable && !noDrawResourceCache && !noTextureCache && std::all_of(shaders.begin(), shaders.end(), [](const CompiledShader& shader) { return shader.program != nullptr && shader.program->PipelineVariantId() != 0; }) && !ShaderResources::NeverReusable(shaders);
if (resolved.cacheable) {
resolved.contentKey = DrawResourceKey(context, shaders, state.color, static_cast<std::uint32_t>(state.colors.size()), draw.indexAddress, indexBytes, !trimKey);
if (auto cached = SharedResourceCache().Find(resolved.contentKey)) {
const bool valid = cached->Revalidate(shaders);
if (auto cached = SharedResourceCache().Find(resolved.contentKey); cached != nullptr && !cached->InUse()) {
const bool valid = cached->Revalidate(shaders, nullptr, snapshots);
auto* recorder = Recorder::Active();
std::optional<std::vector<ShaderResources::MovedBuffer>> moved;
if (valid && recorder != nullptr) moved = cached->MovedReadOnlyBuffers(shaders, *recorder);
if (moved.has_value()) {
if (trimKey) CheckBufferAliases(shaders, state.color, draw.indexAddress, indexBytes);
resolved.resources = std::move(cached);
resolved.use.resources = resolved.resources;
resolved.moved = std::move(*moved);
if (trimKey) CheckBufferAliases(shaders, state.color, draw.indexAddress, indexBytes);
outcome.kind = KindTemplateHit;
countCache(&DrawProfile::cacheHits);
} else if (valid) {
cached->EndUse();
countCache(&DrawProfile::cacheMisses);
} else {
SharedResourceCache().Remove(resolved.contentKey);
Expand All @@ -1305,6 +1318,7 @@ ResolvedResources resolveDrawResources(const Context& context, const State& stat
timer.phase(PhaseLookup);
if (resolved.resources == nullptr) {
resolved.resources = std::make_shared<ShaderResources>(context, shaders, state.color, static_cast<std::uint32_t>(state.colors.size()), draw.indexAddress, static_cast<std::size_t>(indexBytes), snapshots);
resolved.use.resources = resolved.resources;
resolved.built = &resolved.resources->Timing();
outcome.addressBased = resolved.resources->HoldsLease();
outcome.kind = outcome.addressBased ? KindBda : KindBuild;
Expand Down Expand Up @@ -1529,8 +1543,14 @@ void keepRecordedDraw(Recorder& recorder, const std::shared_ptr<ShaderResources>
});
writers->push_back(resources);
} else if (completion) {
if (resources->Reusable()) recorder.OnComplete([resources] { resources->CheckFaults(); });
else recorder.OnComplete([resources] { resources->WriteBackBuffers(); });
if (resources->Reusable()) {
recorder.OnComplete([resources, hold = resources->BeginUse()]() mutable {
const auto held = std::move(hold);
resources->CompleteUse();
});
} else {
recorder.OnComplete([resources] { resources->WriteBackBuffers(); });
}
}
for (std::size_t index = 0; index < residents.size(); ++index) {
if (residents[index] == nullptr) continue;
Expand Down Expand Up @@ -2267,7 +2287,7 @@ void Draw(const Context& context, const State& state, const Pm4::DrawParameters&
// only a template the resource cache serves under this content key (reusable: no lease,
// no copied writes, every direct region import- or mirror-served) of a direct draw, so a
// hit's proof is the template's ProveCurrent and nothing needs completion work.
if (recipeOut != nullptr && DrawRecipes() && cacheable && !outcome.waited && args == nullptr && resources->Reusable() && resolved.moved.empty() && !multisampled) {
if (recipeOut != nullptr && DrawRecipes() && cacheable && !outcome.waited && args == nullptr && resources->Reusable() && !resources->UsesAddressTable() && resolved.moved.empty() && !multisampled) {
auto recipe = std::make_shared<DrawRecipe>();
recipe->device = context.device;
recipe->templateRef = resources;
Expand Down
Loading
Loading