aboutsummaryrefslogtreecommitdiff
path: root/src/video_core/texture_cache/texture_cache.h
diff options
context:
space:
mode:
authorlizzie <lizzie@eden-emu.dev>2026-05-29 03:28:47 +0200
committercrueter <crueter@eden-emu.dev>2026-05-29 03:28:47 +0200
commitdef03f6589007026572816baed73f762818f05bb (patch)
tree100949ba575fe2eb66492d10a7c7cc0b825bf3f6 /src/video_core/texture_cache/texture_cache.h
parent5ea24621cf8e75fe20ff35c4e41a05baf9497be8 (diff)
[video_core] fix redundant resize-copy overload and just use default-init resize, to reduce stutter on Mario BP (#3874)
before vs. after Mario Brothership kept remaking vectors of sizes 256 AND 4095 (TIC) and 1215 AND 524287 (TSC) every single frame, which resulted in a noticeable overhead the main cause was because of using `resize(n, c)` instead of `resize(n)` (also to aggressively resize for more room beforehand), the copy overload of resize does a copy of... well.. the value over the entire vector, additionally __append() keeps getting called because the capacity goes bonkers and all over the place ![image](/attachments/e3ba07fb-1c85-4d56-9b81-bb16a8150c15) ![image](/attachments/5c4eba26-015a-4c95-9b24-b41695a62e51) Signed-off-by: lizzie <lizzie@eden-emu.dev> Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3874 Reviewed-by: crueter <crueter@eden-emu.dev> Reviewed-by: CamilleLaVey <camillelavey99@gmail.com> Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Diffstat (limited to 'src/video_core/texture_cache/texture_cache.h')
-rw-r--r--src/video_core/texture_cache/texture_cache.h314
1 files changed, 120 insertions, 194 deletions
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h
index cb6b5b0a94..c0a0c3736b 100644
--- a/src/video_core/texture_cache/texture_cache.h
+++ b/src/video_core/texture_cache/texture_cache.h
@@ -14,6 +14,7 @@
#include "common/alignment.h"
#include "common/settings.h"
+#include "common/slot_vector.h"
#include "video_core/control/channel_state.h"
#include "video_core/dirty_flags.h"
#include "video_core/engines/kepler_compute.h"
@@ -204,8 +205,8 @@ typename P::ImageView& TextureCache<P>::GetImageView(ImageViewId id) noexcept {
template <class P>
typename P::ImageView& TextureCache<P>::GetImageView(u32 index) noexcept {
- const auto image_view_id = VisitImageView(channel_state->graphics_image_table,
- channel_state->graphics_image_view_ids, index);
+ // Not compute!
+ const auto image_view_id = VisitImageView(index, false);
return slot_image_views[image_view_id];
}
@@ -215,16 +216,25 @@ void TextureCache<P>::MarkModification(ImageId id) noexcept {
}
template <class P>
-template <bool has_blacklists>
-void TextureCache<P>::FillGraphicsImageViews(std::span<ImageViewInOut> views) {
- FillImageViews<has_blacklists>(channel_state->graphics_image_table,
- channel_state->graphics_image_view_ids, views);
-}
-
-template <class P>
-void TextureCache<P>::FillComputeImageViews(std::span<ImageViewInOut> views) {
- FillImageViews<true>(channel_state->compute_image_table, channel_state->compute_image_view_ids,
- views);
+void TextureCache<P>::FillImageViews(std::span<ImageViewInOut> views, bool compute, bool blacklist) {
+ bool has_blacklisted = false;
+ do {
+ has_deleted_images = false;
+ if (blacklist) {
+ has_blacklisted = false;
+ }
+ for (ImageViewInOut& view : views) {
+ view.id = VisitImageView(view.index, compute);
+ if (blacklist) {
+ if (view.blacklist && view.id != NULL_IMAGE_VIEW_ID) {
+ const ImageViewBase& image_view = slot_image_views[view.id];
+ auto& image = slot_images[image_view.image_id];
+ has_blacklisted |= ScaleDown(image);
+ image.scale_rating = 0;
+ }
+ }
+ }
+ } while (has_deleted_images || (blacklist && has_blacklisted));
}
template <class P>
@@ -292,41 +302,24 @@ void TextureCache<P>::CheckFeedbackLoop(std::span<const ImageViewInOut> views) {
}
template <class P>
-typename P::Sampler* TextureCache<P>::GetGraphicsSampler(u32 index) {
- return &slot_samplers[GetGraphicsSamplerId(index)];
-}
-
-template <class P>
-typename P::Sampler* TextureCache<P>::GetComputeSampler(u32 index) {
- return &slot_samplers[GetComputeSamplerId(index)];
+typename P::Sampler* TextureCache<P>::GetSampler(u32 index, bool compute) {
+ return &slot_samplers[GetSamplerId(index, compute)];
}
template <class P>
-SamplerId TextureCache<P>::GetGraphicsSamplerId(u32 index) {
- if (index > channel_state->graphics_sampler_table.Limit()) {
+SamplerId TextureCache<P>::GetSamplerId(u32 index, bool compute) {
+ auto& table = compute ? channel_state->compute_sampler_table : channel_state->graphics_sampler_table;
+ if (index > table.current_limit) {
LOG_DEBUG(HW_GPU, "Invalid sampler index={}", index);
return NULL_SAMPLER_ID;
}
- const auto [descriptor, is_new] = channel_state->graphics_sampler_table.Read(index);
- SamplerId& id = channel_state->graphics_sampler_ids[index];
+ auto const [descriptor, is_new] = table.Read(*gpu_memory, index);
if (is_new) {
- id = FindSampler(descriptor);
+ auto const id = FindSampler(descriptor, compute);
+ channel_state->sampler_ids.insert_or_assign(index | (compute ? Common::SlotId::TAGGED_VALUE : 0), id);
+ return id;
}
- return id;
-}
-
-template <class P>
-SamplerId TextureCache<P>::GetComputeSamplerId(u32 index) {
- if (index > channel_state->compute_sampler_table.Limit()) {
- LOG_DEBUG(HW_GPU, "Invalid sampler index={}", index);
- return NULL_SAMPLER_ID;
- }
- const auto [descriptor, is_new] = channel_state->compute_sampler_table.Read(index);
- SamplerId& id = channel_state->compute_sampler_ids[index];
- if (is_new) {
- id = FindSampler(descriptor);
- }
- return id;
+ return channel_state->sampler_ids.find(index | (compute ? Common::SlotId::TAGGED_VALUE : 0))->second;
}
template <class P>
@@ -340,45 +333,31 @@ typename P::Sampler& TextureCache<P>::GetSampler(SamplerId id) noexcept {
}
template <class P>
-void TextureCache<P>::SynchronizeGraphicsDescriptors() {
- using SamplerBinding = Tegra::Engines::Maxwell3D::Regs::SamplerBinding;
- const bool linked_tsc = maxwell3d->regs.sampler_binding == SamplerBinding::ViaHeaderBinding;
- const u32 tic_limit = maxwell3d->regs.tex_header.limit;
- const u32 tsc_limit = linked_tsc ? tic_limit : maxwell3d->regs.tex_sampler.limit;
- bool bindings_changed = false;
- if (channel_state->graphics_sampler_table.Synchronize(maxwell3d->regs.tex_sampler.Address(),
- tsc_limit)) {
- channel_state->graphics_sampler_ids.resize(tsc_limit + 1, CORRUPT_ID);
- bindings_changed = true;
- }
- if (channel_state->graphics_image_table.Synchronize(maxwell3d->regs.tex_header.Address(),
- tic_limit)) {
- channel_state->graphics_image_view_ids.resize(tic_limit + 1, CORRUPT_ID);
- bindings_changed = true;
- }
- if (bindings_changed) {
- ++texture_bindings_serial;
- }
-}
-
-template <class P>
-void TextureCache<P>::SynchronizeComputeDescriptors() {
- const bool linked_tsc = kepler_compute->launch_description.linked_tsc;
- const u32 tic_limit = kepler_compute->regs.tic.limit;
- const u32 tsc_limit = linked_tsc ? tic_limit : kepler_compute->regs.tsc.limit;
- const GPUVAddr tsc_gpu_addr = kepler_compute->regs.tsc.Address();
- bool bindings_changed = false;
- if (channel_state->compute_sampler_table.Synchronize(tsc_gpu_addr, tsc_limit)) {
- channel_state->compute_sampler_ids.resize(tsc_limit + 1, CORRUPT_ID);
- bindings_changed = true;
- }
- if (channel_state->compute_image_table.Synchronize(kepler_compute->regs.tic.Address(),
- tic_limit)) {
- channel_state->compute_image_view_ids.resize(tic_limit + 1, CORRUPT_ID);
- bindings_changed = true;
- }
- if (bindings_changed) {
- ++texture_bindings_serial;
+void TextureCache<P>::SynchronizeDescriptors(bool compute) {
+ if (compute) {
+ const bool linked_tsc = kepler_compute->launch_description.linked_tsc;
+ const u32 tic_limit = kepler_compute->regs.tic.limit;
+ const u32 tsc_limit = linked_tsc ? tic_limit : kepler_compute->regs.tsc.limit;
+ bool bindings_changed = false;
+ if (channel_state->compute_sampler_table.Synchronize(kepler_compute->regs.tsc.Address(), tsc_limit))
+ bindings_changed = true;
+ if (channel_state->compute_image_table.Synchronize(kepler_compute->regs.tic.Address(), tic_limit))
+ bindings_changed = true;
+ if (bindings_changed) {
+ ++texture_bindings_serial;
+ }
+ } else {
+ const bool linked_tsc = maxwell3d->regs.sampler_binding == Tegra::Engines::Maxwell3D::Regs::SamplerBinding::ViaHeaderBinding;
+ const u32 tic_limit = maxwell3d->regs.tex_header.limit;
+ const u32 tsc_limit = linked_tsc ? tic_limit : maxwell3d->regs.tex_sampler.limit;
+ bool bindings_changed = false;
+ if (channel_state->graphics_sampler_table.Synchronize(maxwell3d->regs.tex_sampler.Address(), tsc_limit))
+ bindings_changed = true;
+ if (channel_state->graphics_image_table.Synchronize(maxwell3d->regs.tex_header.Address(), tic_limit))
+ bindings_changed = true;
+ if (bindings_changed) {
+ ++texture_bindings_serial;
+ }
}
}
@@ -557,47 +536,30 @@ typename P::Framebuffer* TextureCache<P>::GetFramebuffer() {
}
template <class P>
-template <bool has_blacklists>
-void TextureCache<P>::FillImageViews(DescriptorTable<TICEntry>& table,
- std::span<ImageViewId> cached_image_view_ids,
- std::span<ImageViewInOut> views) {
- bool has_blacklisted = false;
- do {
- has_deleted_images = false;
- if constexpr (has_blacklists) {
- has_blacklisted = false;
- }
- for (ImageViewInOut& view : views) {
- view.id = VisitImageView(table, cached_image_view_ids, view.index);
- if constexpr (has_blacklists) {
- if (view.blacklist && view.id != NULL_IMAGE_VIEW_ID) {
- const ImageViewBase& image_view{slot_image_views[view.id]};
- auto& image = slot_images[image_view.image_id];
- has_blacklisted |= ScaleDown(image);
- image.scale_rating = 0;
- }
- }
- }
- } while (has_deleted_images || (has_blacklists && has_blacklisted));
-}
-
-template <class P>
-ImageViewId TextureCache<P>::VisitImageView(DescriptorTable<TICEntry>& table,
- std::span<ImageViewId> cached_image_view_ids,
- u32 index) {
- if (index > table.Limit()) {
+ImageViewId TextureCache<P>::VisitImageView(u32 index, bool compute) {
+ auto& table = compute ? channel_state->compute_image_table : channel_state->graphics_image_table;
+ if (index > table.current_limit) {
LOG_DEBUG(HW_GPU, "Invalid image view index={}", index);
return NULL_IMAGE_VIEW_ID;
}
- const auto [descriptor, is_new] = table.Read(index);
- ImageViewId& image_view_id = cached_image_view_ids[index];
+ // Is new (on the tegra engine side)?
+ auto const [descriptor, is_new] = table.Read(*gpu_memory, index);
if (is_new) {
- image_view_id = FindImageView(descriptor);
- }
- if (image_view_id != NULL_IMAGE_VIEW_ID) {
- PrepareImageView(image_view_id, false, false);
+ if (IsValidEntry(*gpu_memory, descriptor)) {
+ // Is new (registered view) on the texture cache side?
+ const auto [pair, is_new_tc] = channel_state->image_views.try_emplace(descriptor);
+ if (is_new_tc)
+ pair->second = CreateImageView(descriptor);
+ PrepareImageView(pair->second, false, false);
+ channel_state->image_view_ids.insert_or_assign(index | (compute ? Common::SlotId::TAGGED_VALUE : 0), pair->second);
+ return pair->second;
+ }
+ channel_state->image_view_ids.insert_or_assign(index | (compute ? Common::SlotId::TAGGED_VALUE : 0), NULL_IMAGE_VIEW_ID);
+ return NULL_IMAGE_VIEW_ID;
}
- return image_view_id;
+ auto const it = channel_state->image_view_ids.find(index | (compute ? Common::SlotId::TAGGED_VALUE : 0));
+ PrepareImageView(it->second, false, false);
+ return it->second;
}
template <class P>
@@ -1197,19 +1159,6 @@ void TextureCache<P>::UploadImageContents(Image& image, StagingBuffer& staging)
}
template <class P>
-ImageViewId TextureCache<P>::FindImageView(const TICEntry& config) {
- if (!IsValidEntry(*gpu_memory, config)) {
- return NULL_IMAGE_VIEW_ID;
- }
- const auto [pair, is_new] = channel_state->image_views.try_emplace(config);
- ImageViewId& image_view_id = pair->second;
- if (is_new) {
- image_view_id = CreateImageView(config);
- }
- return image_view_id;
-}
-
-template <class P>
ImageViewId TextureCache<P>::CreateImageView(const TICEntry& config) {
const ImageInfo info(config);
if (info.type == ImageType::Buffer) {
@@ -1350,10 +1299,10 @@ void TextureCache<P>::InvalidateScale(Image& image) {
image.image_view_infos.clear();
for (size_t c : active_channel_ids) {
auto& channel_info = channel_storage[c];
- if constexpr (ENABLE_VALIDATION) {
- std::ranges::fill(channel_info.graphics_image_view_ids, CORRUPT_ID);
- std::ranges::fill(channel_info.compute_image_view_ids, CORRUPT_ID);
- }
+
+ if constexpr (ENABLE_VALIDATION)
+ for (auto& e : channel_info.image_view_ids)
+ e.second = CORRUPT_ID;
channel_info.graphics_image_table.Invalidate();
channel_info.compute_image_table.Invalidate();
}
@@ -1918,7 +1867,7 @@ std::pair<u32, u32> TextureCache<P>::PrepareDmaImage(ImageId dst_id, GPUVAddr ba
}
template <class P>
-SamplerId TextureCache<P>::FindSampler(const TSCEntry& config) {
+SamplerId TextureCache<P>::FindSampler(const TSCEntry& config, bool compute) {
if (std::ranges::all_of(config.raw, [](u64 value) { return value == 0; })) {
return NULL_SAMPLER_ID;
}
@@ -1941,70 +1890,49 @@ std::optional<size_t> TextureCache<P>::QuerySamplerBudget() const {
template <class P>
void TextureCache<P>::EnforceSamplerBudget() {
- const auto budget = QuerySamplerBudget();
- if (!budget) {
- return;
- }
- if (slot_samplers.size() < *budget) {
- return;
- }
- if (!channel_state) {
- return;
- }
- if (last_sampler_gc_frame == frame_tick) {
- return;
+ if (auto const budget = QuerySamplerBudget(); budget) {
+ if (slot_samplers.size() < *budget) {
+ return;
+ }
+ if (!channel_state) {
+ return;
+ }
+ if (last_sampler_gc_frame == frame_tick) {
+ return;
+ }
+ last_sampler_gc_frame = frame_tick;
+ TrimInactiveSamplers(*budget);
}
- last_sampler_gc_frame = frame_tick;
- TrimInactiveSamplers(*budget);
}
template <class P>
void TextureCache<P>::TrimInactiveSamplers(size_t budget) {
- if (channel_state->samplers.empty()) {
- return;
- }
- constexpr size_t SAMPLER_GC_SLACK = 1024;
- auto mark_active = [](auto& set, SamplerId id) {
- if (!id || id == CORRUPT_ID || id == NULL_SAMPLER_ID) {
- return;
- }
- set.insert(id);
- };
- ankerl::unordered_dense::set<SamplerId> active;
- active.reserve(channel_state->graphics_sampler_ids.size() +
- channel_state->compute_sampler_ids.size());
- for (const SamplerId id : channel_state->graphics_sampler_ids) {
- mark_active(active, id);
- }
- for (const SamplerId id : channel_state->compute_sampler_ids) {
- mark_active(active, id);
- }
-
- size_t removed = 0;
- auto& sampler_map = channel_state->samplers;
- for (auto it = sampler_map.begin(); it != sampler_map.end();) {
- const SamplerId sampler_id = it->second;
- if (!sampler_id || sampler_id == CORRUPT_ID) {
- it = sampler_map.erase(it);
- continue;
- }
- if (active.find(sampler_id) != active.end()) {
- ++it;
- continue;
+ if (channel_state->samplers.size() > 0) {
+ constexpr size_t SAMPLER_GC_SLACK = 1024;
+ ankerl::unordered_dense::set<SamplerId> active_sampler_ids;
+ for (auto const& e : channel_state->sampler_ids)
+ active_sampler_ids.insert(e.second);
+ // Elements in the map must be necesarily valid
+ size_t removed = 0;
+ for (auto it = channel_state->samplers.begin(); it != channel_state->samplers.end();) {
+ const SamplerId sampler_id = it->second;
+ if (!sampler_id || sampler_id == CORRUPT_ID) {
+ it = channel_state->samplers.erase(it);
+ } else if (std::ranges::find(active_sampler_ids, sampler_id) != active_sampler_ids.end()) {
+ ++it;
+ } else {
+ slot_samplers.erase(sampler_id);
+ it = channel_state->samplers.erase(it);
+ ++removed;
+ if (slot_samplers.size() + SAMPLER_GC_SLACK <= budget) {
+ break;
+ }
+ }
}
- slot_samplers.erase(sampler_id);
- it = sampler_map.erase(it);
- ++removed;
- if (slot_samplers.size() + SAMPLER_GC_SLACK <= budget) {
- break;
+ if (removed != 0) {
+ LOG_WARNING(HW_GPU, "Sampler cache exceeded {} entries on this driver; reclaimed {} inactive samplers", budget, removed);
}
}
-
- if (removed != 0) {
- LOG_WARNING(HW_GPU,
- "Sampler cache exceeded {} entries on this driver; reclaimed {} inactive samplers",
- budget, removed);
- }
}
template <class P>
@@ -2243,8 +2171,7 @@ ImageViewId TextureCache<P>::FindOrEmplaceImageView(ImageId image_id, const Imag
if (const ImageViewId image_view_id = image.FindView(info); image_view_id) {
return image_view_id;
}
- const ImageViewId image_view_id =
- slot_image_views.insert(runtime, info, image_id, image, slot_images);
+ const ImageViewId image_view_id = slot_image_views.insert(runtime, info, image_id, image, slot_images);
image.InsertView(info, image_view_id);
return image_view_id;
}
@@ -2504,10 +2431,9 @@ void TextureCache<P>::DeleteImage(ImageId image_id, bool immediate_delete) {
}
for (size_t c : active_channel_ids) {
auto& channel_info = channel_storage[c];
- if constexpr (ENABLE_VALIDATION) {
- std::ranges::fill(channel_info.graphics_image_view_ids, CORRUPT_ID);
- std::ranges::fill(channel_info.compute_image_view_ids, CORRUPT_ID);
- }
+ if constexpr (ENABLE_VALIDATION)
+ for (auto& e : channel_info.image_view_ids)
+ e.second = CORRUPT_ID;
channel_info.graphics_image_table.Invalidate();
channel_info.compute_image_table.Invalidate();
}