Cycles: Texture cache eviction for viewport rendering

Evict unused texture cache tiles in viewport rendering. The heuristic is to
do this least 2 seconds after the last viewport navigation, and after
rendering has completed.

When using many render samples this is not ideal, as renders might rarely
complete. On the other hand, doing it too early would evict tiles that are
used by later render samples. So there is room improve this later, but
it's better than not evicting at all..

Pull Request: https://projects.blender.org/blender/blender/pulls/157244
This commit is contained in:
Brecht Van Lommel 2026-02-22 10:15:33 +01:00
parent c6d110e8ec
commit fc9f1ed6da
5 changed files with 75 additions and 7 deletions

View file

@ -904,6 +904,12 @@ void BlenderSession::view_draw(const int w, const int h)
/* pause in redraw in case update is not being called due to final render */
session->set_pause(BlenderSync::get_session_pause(*b_scene, background));
/* Update navigating state. */
const bool dimensions_changed = (width != w || height != h || pixelsize != blender::U.pixelsize);
const bool is_navigating = region_view3d_navigating_or_transforming(b_rv3d) ||
dimensions_changed;
session->set_navigating(is_navigating);
/* before drawing, we verify camera and viewport size changes, because
* we do not get update callbacks for those, we must detect them here */
if (session->ready_to_reset()) {
@ -911,8 +917,7 @@ void BlenderSession::view_draw(const int w, const int h)
/* If dimensions changed, reset. We need to check pixel size here because
* it's only valid during drawing, as it can change per window. */
const float new_pixelsize = blender::U.pixelsize;
if (width != w || height != h || pixelsize != new_pixelsize) {
if (dimensions_changed) {
if (start_resize_time == 0.0) {
/* don't react immediately to resizes to avoid flickery resizing
* of the viewport, and some window managers changing the window
@ -926,7 +931,7 @@ void BlenderSession::view_draw(const int w, const int h)
else {
width = w;
height = h;
pixelsize = new_pixelsize;
pixelsize = blender::U.pixelsize;
reset = true;
}
}

View file

@ -16,6 +16,20 @@ CacheEvictionManager::CacheEvictionManager(bool background) : background_(backgr
void CacheEvictionManager::reset()
{
render_tile_count_ = 0;
viewport_last_activity_ = 0.0;
viewport_was_navigating_ = false;
render_tile_count_ = 0;
}
void CacheEvictionManager::set_navigating(bool navigating)
{
navigating_ = navigating;
}
bool CacheEvictionManager::is_navigating() const
{
return (background_) ? false : navigating_;
}
bool CacheEvictionManager::need_eviction(bool idle, bool switched_to_new_tile)
@ -33,12 +47,44 @@ bool CacheEvictionManager::need_eviction(bool idle, bool switched_to_new_tile)
}
/* Viewport render. */
return false;
const bool navigating = navigating_;
if (navigating) {
/* No eviction while navigating. */
viewport_last_activity_ = 0.0;
}
else if (viewport_was_navigating_) {
/* Start eviction timer when navigating stops. */
viewport_last_activity_ = time_dt();
}
viewport_was_navigating_ = navigating;
if (!idle) {
/* Restart eviction timer while not idle. */
viewport_last_activity_ = time_dt();
return false;
}
if (wait_time(idle) != std::chrono::milliseconds::zero()) {
/* Not ready to evict yet. */
return false;
}
/* Eviction needed now, clear existing timer. */
viewport_last_activity_ = 0.0;
return true;
}
std::chrono::milliseconds CacheEvictionManager::wait_time(bool /*idle*/) const
std::chrono::milliseconds CacheEvictionManager::wait_time(bool idle) const
{
return std::chrono::milliseconds::max();
if (!idle || background_ || viewport_last_activity_ == 0.0 || viewport_was_navigating_) {
/* No eviction pending. */
return std::chrono::milliseconds::max();
}
/* Wait for VIEWPORT_EVICTION_DELAY after last activity. */
const double elapsed = time_dt() - viewport_last_activity_;
const double remaining = VIEWPORT_EVICTION_DELAY - elapsed;
return std::chrono::milliseconds(int64_t(max(0.0, remaining) * 1000.0));
}
CCL_NAMESPACE_END

View file

@ -11,7 +11,8 @@
CCL_NAMESPACE_BEGIN
/* CacheEvictionManager has decides when texture cache eviction should
* happen in a render session. */
* happen in a render session. Different policies are used for viewport
* and final rendering. */
class CacheEvictionManager {
public:
explicit CacheEvictionManager(bool background);
@ -19,6 +20,10 @@ class CacheEvictionManager {
/* Reset state when starting a new render. */
void reset();
/* Set and query if viewport navigation is happening. */
void set_navigating(bool navigating);
bool is_navigating() const;
/* For a render iteration, check if cache eviction is needed. */
bool need_eviction(bool idle, bool switched_to_new_tile);
@ -28,7 +33,13 @@ class CacheEvictionManager {
private:
const bool background_;
bool navigating_ = false;
double viewport_last_activity_ = 0.0;
bool viewport_was_navigating_ = false;
int render_tile_count_ = 0;
static constexpr double VIEWPORT_EVICTION_DELAY = 2.0;
};
CCL_NAMESPACE_END

View file

@ -655,6 +655,11 @@ void Session::set_pause(bool pause)
}
}
void Session::set_navigating(bool navigating)
{
eviction_manager_.set_navigating(navigating);
}
void Session::set_output_driver(unique_ptr<OutputDriver> driver)
{
path_trace_->set_output_driver(std::move(driver));

View file

@ -141,6 +141,7 @@ class Session {
void reset(const SessionParams &session_params, const BufferParams &buffer_params);
void set_pause(bool pause);
void set_navigating(bool navigating);
void set_samples(const int samples);
void set_time_limit(const double time_limit);