mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-27 07:52:39 +00:00
graphics: hold image heap descriptors whose address another extent pins
This commit is contained in:
1 parent
c5f7644751
commit
95bbc9c65d
6 files changed
+100
-1
No files matched your search
@@ -1302,6 +1302,19 @@ ImageId TextureCache::FindImage(ImageDesc& desc, bool exact_format) {
|
||||
view_layer = overlap.layer;
|
||||
}
|
||||
}
|
||||
// PS5: image heaps hold a descriptor per dynamic-resolution extent of a render target,
|
||||
// and only one extent at an address holds data, so heap lookups create no second one.
|
||||
if (!result && desc.heap &&
|
||||
std::any_of(candidates.begin(), candidates.end(), [&](ImageId id) {
|
||||
const auto* image = m_slot_images.try_get(id);
|
||||
return image != nullptr && image->registered &&
|
||||
image->heap_refs.load(std::memory_order_relaxed) != 0u &&
|
||||
image->info.data.address == desc.info.data.address &&
|
||||
image->info.type == desc.info.type &&
|
||||
image->info.extent != desc.info.extent;
|
||||
})) {
|
||||
return {};
|
||||
}
|
||||
} else if (block_alias) {
|
||||
for (const auto candidate: candidates) {
|
||||
const auto& owner = m_slot_images[candidate];
|
||||
|
||||
@@ -43,6 +43,7 @@ public:
|
||||
ImageInfo info;
|
||||
ImageViewInfo view_info;
|
||||
BindingType type = BindingType::Texture;
|
||||
bool heap = false; // PS5: resolved for a resident image heap.
|
||||
};
|
||||
|
||||
TextureCache(GraphicContext& graphics, CommandScheduler& scheduler, PageManager& page_manager,
|
||||
|
||||
@@ -604,8 +604,13 @@ TextureBinding RenderExecutor::ResolveTexture(const ShaderRecompiler::IR::ImageR
|
||||
view_levels, desc.info.resources.layers);
|
||||
desc.view_info.base_level = view_base;
|
||||
desc.type = storage ? TextureCache::BindingType::Storage : TextureCache::BindingType::Texture;
|
||||
desc.heap = resource.heap;
|
||||
|
||||
auto id = texture_cache.FindImage(desc, shader_conversion);
|
||||
auto id = texture_cache.FindImage(desc, shader_conversion);
|
||||
if (!id) {
|
||||
// Another extent at the address holds it; the heap retries once an image is created.
|
||||
return {.desc = std::move(desc), .layout = vk::ImageLayout::eGeneral};
|
||||
}
|
||||
auto* image = &texture_cache.GetImage(id);
|
||||
const bool stencil_association = static_cast<bool>(image->depth_id);
|
||||
if (stencil_association) {
|
||||
|
||||
@@ -394,6 +394,9 @@ uint32_t ImageHeap::Allocate() {
|
||||
}
|
||||
|
||||
void ImageHeap::Retire(View& view) {
|
||||
if (view.released.empty()) return;
|
||||
// A retired index is reused once its tick ends, so no held entry may keep it.
|
||||
std::erase_if(view.held, [&](uint32_t index) { return m_elements[index].refs == 0u; });
|
||||
const auto tick = m_context.GetCommandScheduler().CurrentTick();
|
||||
for (const auto index: view.released) m_retired.emplace_back(tick, index);
|
||||
view.released.clear();
|
||||
@@ -440,6 +443,7 @@ void ImageHeap::Rebind(RenderExecutor& executor) {
|
||||
KYTY_PROFILER_FUNCTION();
|
||||
auto& textures = m_context.GetTextureCache();
|
||||
const auto epoch = Image::heap_epoch.load(std::memory_order_relaxed);
|
||||
const auto serial = Image::serials.load(std::memory_order_relaxed);
|
||||
for (auto* view: m_active) {
|
||||
bool stale = false;
|
||||
const auto mark = [&](uint32_t index) { m_elements[index].stale = stale = true; };
|
||||
@@ -468,6 +472,19 @@ void ImageHeap::Rebind(RenderExecutor& executor) {
|
||||
mark(index);
|
||||
return true;
|
||||
});
|
||||
// Held elements retry once an image is created, such as a render target of their extent.
|
||||
if (view->held_serial != serial) {
|
||||
view->held_serial = serial;
|
||||
std::erase_if(view->held, [&](uint32_t index) {
|
||||
const auto& element = m_elements[index];
|
||||
if (element.refs == 0u || element.stale) return true;
|
||||
const auto binding = executor.ResolveTexture(
|
||||
IR::ImageHeapResource(view->profile, element.kind), element.value);
|
||||
if (binding.image_id) mark(index);
|
||||
// An unmapped texture stays null.
|
||||
return binding.image_id || binding.desc.info.data.Empty();
|
||||
});
|
||||
}
|
||||
if (stale) {
|
||||
// A changed image needs a new element; pending draws still read the old one. The new
|
||||
// element keeps the old value, since a view takes table changes only when acquired.
|
||||
@@ -496,6 +513,8 @@ void ImageHeap::Rebind(RenderExecutor& executor) {
|
||||
view->dcc.push_back(index);
|
||||
}
|
||||
view->away.push_back(index);
|
||||
} else if (!binding.desc.info.data.Empty()) {
|
||||
view->held.push_back(index);
|
||||
}
|
||||
m_elements[index].binding = std::move(binding);
|
||||
m_writes.push_back(index);
|
||||
|
||||
@@ -81,6 +81,7 @@ private:
|
||||
std::vector<uint32_t> released; // No slot selects them after the next upload.
|
||||
std::vector<uint32_t> away; // Not yet moved to the read layout.
|
||||
std::vector<uint32_t> dcc; // Color metadata clears apply on each discovery.
|
||||
std::vector<uint32_t> held; // Another extent holds their address.
|
||||
std::unique_ptr<Buffer> buffer;
|
||||
size_t dirty_begin = 0; // Slots to upload.
|
||||
size_t dirty_end = 0;
|
||||
@@ -88,6 +89,7 @@ private:
|
||||
size_t changed_end = 0;
|
||||
uint64_t resolve_epoch = 0;
|
||||
uint64_t layout_epoch = 0;
|
||||
uint64_t held_serial = 0; // Image::serials when held was last retried.
|
||||
};
|
||||
|
||||
struct Memory {
|
||||
|
||||
@@ -5511,6 +5511,65 @@ public:
|
||||
ImageHeapTestAccess::ImageOf(heap, filtered.slots[240]),
|
||||
"a reactivated view kept an element whose image slot holds a new image");
|
||||
|
||||
// Dynamic-resolution tables hold a descriptor per extent of one render target, each with
|
||||
// its own pitch. Only the first gets an image; the others wait until an image of their
|
||||
// extent exists.
|
||||
const auto extent = [&](uint32_t width) {
|
||||
auto words = descriptor(50, Format::k8_8_8_8UNorm);
|
||||
words[2] = (width - 1u) | (3u << 14u);
|
||||
return words;
|
||||
};
|
||||
const auto resolve = [&](const auto &root, const auto &words) {
|
||||
return executor.ResolveTexture(root, {.dwords = words, .dword_count = 8u}).image_id;
|
||||
};
|
||||
write(50, extent(64));
|
||||
write(51, extent(128));
|
||||
Require(name, "extents", acquire(sampled), "the view was declined");
|
||||
draw();
|
||||
Require(name, "held extent",
|
||||
ImageHeapTestAccess::ImageOf(heap, filtered.slots[400]) &&
|
||||
!ImageHeapTestAccess::ImageOf(heap, filtered.slots[408]),
|
||||
"a second extent of one address got its own image");
|
||||
// A non-heap lookup, as for a render target, creates the second extent.
|
||||
const auto rendered = resolve(sampled, extent(128));
|
||||
Require(name, "rendered extent", acquire(sampled), "the view was declined");
|
||||
draw();
|
||||
Require(name, "held descriptor",
|
||||
rendered && ImageHeapTestAccess::ImageOf(heap, filtered.slots[408]) == rendered,
|
||||
"a held descriptor missed the image made for its extent");
|
||||
// A released held element's index is reused once its tick ends; it is no longer held.
|
||||
write(52, extent(256));
|
||||
Require(name, "third extent", acquire(sampled), "the view was declined");
|
||||
draw();
|
||||
const auto held = filtered.slots[416];
|
||||
write(52, {});
|
||||
Require(name, "released extent", acquire(sampled), "the view was declined");
|
||||
draw();
|
||||
write(53, descriptor(53, Format::k8_8_8_8UNorm));
|
||||
Require(name, "reused element", acquire(sampled), "the view was declined");
|
||||
draw();
|
||||
Require(name, "retry", acquire(sampled), "the view was declined");
|
||||
draw();
|
||||
Require(name, "reused held element", held != 0u && filtered.slots[424] == held,
|
||||
"a reused element was retried as the held element it replaced");
|
||||
// A 2D view of a heap-pinned volume is another image type, not another extent.
|
||||
auto plane_root = sampled;
|
||||
plane_root.heap = true;
|
||||
auto volume_root = plane_root;
|
||||
volume_root.dimension = ShaderRecompiler::Decoder::ImageDimension::Dim3D;
|
||||
auto volume_words = descriptor(54, Format::k8_8_8_8UNorm);
|
||||
volume_words[3] = DstSel(4, 5, 6, 7) |
|
||||
(static_cast<uint32_t>(Prospero::ImageType::kColor3D) << 28u);
|
||||
volume_words[4] = 3u;
|
||||
const auto volume = resolve(volume_root, volume_words);
|
||||
Require(name, "volume", static_cast<bool>(volume), "a volume texture was not resolved");
|
||||
auto &volume_refs = textures.GetImage(volume).heap_refs;
|
||||
volume_refs.fetch_add(1u);
|
||||
const auto plane = resolve(plane_root, descriptor(54, Format::k8_8_8_8UNorm));
|
||||
volume_refs.fetch_sub(1u);
|
||||
Require(name, "volume plane", static_cast<bool>(plane),
|
||||
"a 2D view of a pinned volume was held as another extent");
|
||||
|
||||
// A table follows its descriptor's size and keeps its elements. Growing probes the new
|
||||
// words, shrinking releases them, and a draw sees one size.
|
||||
write(table_size / 32u, descriptor(40, Format::k8_8_8_8UNorm));
|
||||
|
||||
Reference in new issue
Block a user