Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
gui_throttle.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2026 Aurélien PIERRE.
4
5 darktable is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 darktable is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with darktable. If not, see <http://www.gnu.org/licenses/>.
17*/
18
20#include "system/macros.h" // IS_NULL_PTR -- a pure macro header, no state
21
22#include "system/atomic.h"
23#include "system/dtpthread.h"
24
25#include <stdint.h>
26
27#define DT_GUI_THROTTLE_RUNTIME_CONF "processing/gui_throttle_runtime_us"
28
35
55
57
58static dt_gui_throttle_task_t *_find_task(gpointer source)
59{
60 for(GList *iter = _gui_throttle.pending_tasks.head; iter; iter = g_list_next(iter))
61 {
63 if(task->source == source) return task;
64 }
65
66 return NULL;
67}
68
69// Pushed by the host from its preferences; 0 means no timeout.
70static guint _user_timeout_ms = 0;
71
72void dt_gui_throttle_set_timeout_ms(guint timeout_ms)
73{
74 _user_timeout_ms = timeout_ms;
75}
76
77static guint _get_user_timeout_ms(void)
78{
79 return _user_timeout_ms;
80}
81
82static guint _runtime_us_to_ms(const int runtime_us)
83{
84 if(runtime_us <= 0) return 0;
85 return (guint)MAX(1, (runtime_us + 999) / 1000);
86}
87
88static guint _effective_timeout_ms(void)
89{
90 const guint user_timeout_ms = _get_user_timeout_ms();
91 const guint runtime_timeout_ms = _runtime_us_to_ms(dt_atomic_get_int(&_gui_throttle.avg_runtime_us));
92 if(runtime_timeout_ms == 0) return user_timeout_ms;
93 return MIN(user_timeout_ms, runtime_timeout_ms);
94}
95
96static gboolean _dispatch_pending_tasks(gpointer user_data)
97{
98 (void)user_data;
99
101
102 GQueue ready = G_QUEUE_INIT;
103 ready.head = _gui_throttle.pending_tasks.head;
104 ready.tail = _gui_throttle.pending_tasks.tail;
105 ready.length = _gui_throttle.pending_tasks.length;
106 g_queue_init(&_gui_throttle.pending_tasks);
107
108 while(!g_queue_is_empty(&ready))
109 {
110 dt_gui_throttle_task_t *task = (dt_gui_throttle_task_t *)g_queue_pop_head(&ready);
111 if(task->callback) task->callback(task->user_data);
112 g_free(task);
113 }
114
115 return G_SOURCE_REMOVE;
116}
117
118void dt_gui_throttle_init(int saved_runtime_us_in)
119{
121 g_queue_init(&_gui_throttle.pending_tasks);
122
123 const int saved_runtime_us = MAX(saved_runtime_us_in, 0);
127
128 if(saved_runtime_us > 0)
129 {
130 for(uint8_t i = 0; i < G_N_ELEMENTS(_gui_throttle.recent_runtime_us); i++)
131 {
132 _gui_throttle.recent_runtime_us[i] = (uint32_t)saved_runtime_us;
133 _gui_throttle.recent_full_runtime_us[i] = (uint32_t)saved_runtime_us;
134 _gui_throttle.recent_preview_runtime_us[i] = (uint32_t)saved_runtime_us;
135 }
136
143 }
144}
145
147{
148
149
151 {
152 g_source_remove(_gui_throttle.timeout_source);
154 }
155
156 while(!g_queue_is_empty(&_gui_throttle.pending_tasks))
157 g_free(g_queue_pop_head(&_gui_throttle.pending_tasks));
158
160}
161
162void dt_gui_throttle_record_runtime(const dt_throttle_slot_t slot, const gint64 runtime_us)
163{
164 if(runtime_us <= 0 || slot == DT_THROTTLE_SLOT_OTHER) return;
165
166 const uint32_t clamped_runtime_us = (uint32_t)MIN(runtime_us, (gint64)G_MAXUINT32);
167
173 = (uint8_t)((_gui_throttle.recent_runtime_pos + 1) % G_N_ELEMENTS(_gui_throttle.recent_runtime_us));
174
175 uint64_t runtime_sum = 0;
176 for(uint8_t i = 0; i < _gui_throttle.recent_runtime_count; i++)
177 runtime_sum += _gui_throttle.recent_runtime_us[i];
178
179 const int avg_runtime_us = (int)(runtime_sum / MAX(_gui_throttle.recent_runtime_count, (uint8_t)1));
181
182 if(slot == DT_THROTTLE_SLOT_MAIN)
183 {
189
190 uint64_t full_runtime_sum = 0;
191 for(uint8_t i = 0; i < _gui_throttle.recent_full_runtime_count; i++)
192 full_runtime_sum += _gui_throttle.recent_full_runtime_us[i];
193
194 const int avg_full_runtime_us
195 = (int)(full_runtime_sum / MAX(_gui_throttle.recent_full_runtime_count, (uint8_t)1));
197 }
198 else if(slot == DT_THROTTLE_SLOT_PREVIEW)
199 {
206
207 uint64_t preview_runtime_sum = 0;
208 for(uint8_t i = 0; i < _gui_throttle.recent_preview_runtime_count; i++)
209 preview_runtime_sum += _gui_throttle.recent_preview_runtime_us[i];
210
211 const int avg_preview_runtime_us
212 = (int)(preview_runtime_sum / MAX(_gui_throttle.recent_preview_runtime_count, (uint8_t)1));
214 }
216}
217
222
238
240{
241 return _effective_timeout_ms();
242}
243
245{
246 const gint64 timeout_ms = _effective_timeout_ms();
247 if(timeout_ms <= 0) return 0;
248 return timeout_ms * 1000;
249}
250
251void dt_gui_throttle_queue(gpointer source, dt_gui_throttle_callback_t callback, gpointer user_data)
252{
253 if(IS_NULL_PTR(callback)) return;
254 if(IS_NULL_PTR(source)) source = user_data;
255
256 const guint timeout_ms = _effective_timeout_ms();
257 if(timeout_ms == 0)
258 {
260 callback(user_data);
261 return;
262 }
263
264 dt_gui_throttle_task_t *task = _find_task(source);
265 if(task)
266 {
267 task->callback = callback;
268 task->user_data = user_data;
269 }
270 else
271 {
272 task = g_malloc0(sizeof(*task));
273 task->source = source;
274 task->callback = callback;
275 task->user_data = user_data;
276 g_queue_push_tail(&_gui_throttle.pending_tasks, task);
277 }
278
280 _gui_throttle.timeout_source = g_timeout_add(timeout_ms, _dispatch_pending_tasks, NULL);
281}
282
283void dt_gui_throttle_cancel(gpointer source)
284{
285 if(IS_NULL_PTR(source)) return;
286
287 for(GList *iter = _gui_throttle.pending_tasks.head; iter; iter = g_list_next(iter))
288 {
290 if(task->source != source) continue;
291
292 g_queue_delete_link(&_gui_throttle.pending_tasks, iter);
293 g_free(task);
294 break;
295 }
296
298 {
299 g_source_remove(_gui_throttle.timeout_source);
301 }
302}
void dt_atomic_set_int(dt_atomic_int *var, int value)
int dt_atomic_get_int(dt_atomic_int *var)
atomic_int dt_atomic_int
Definition atomic.h:68
typedef void((*dt_cache_allocate_t)(void *userdata, dt_cache_entry_t *entry))
static int dt_pthread_mutex_unlock(dt_pthread_mutex_t *mutex) RELEASE(mutex) NO_THREAD_SAFETY_ANALYSIS
Definition dtpthread.h:127
static int dt_pthread_mutex_init(dt_pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr)
Initialise a mutex. With mutexattr NULL – which is how 54 of the 56 call sites in this tree spell it ...
Definition dtpthread.h:104
static int dt_pthread_mutex_destroy(dt_pthread_mutex_t *mutex)
Definition dtpthread.h:132
static int dt_pthread_mutex_lock(dt_pthread_mutex_t *mutex) ACQUIRE(mutex) NO_THREAD_SAFETY_ANALYSIS
Definition dtpthread.h:117
static gboolean _dispatch_pending_tasks(gpointer user_data)
static dt_gui_throttle_state_t _gui_throttle
gint64 dt_gui_throttle_get_timeout_us(void)
void dt_gui_throttle_cancel(gpointer source)
static guint _effective_timeout_ms(void)
static guint _get_user_timeout_ms(void)
void dt_gui_throttle_set_timeout_ms(guint timeout_ms)
static guint _user_timeout_ms
void dt_gui_throttle_record_runtime(const dt_throttle_slot_t slot, const gint64 runtime_us)
int dt_gui_throttle_get_pipe_runtime_us(const dt_throttle_slot_t slot)
static dt_gui_throttle_task_t * _find_task(gpointer source)
int dt_gui_throttle_get_runtime_us(void)
void dt_gui_throttle_cleanup(void)
void dt_gui_throttle_init(int saved_runtime_us_in)
static guint _runtime_us_to_ms(const int runtime_us)
guint dt_gui_throttle_get_timeout_ms(void)
void dt_gui_throttle_queue(gpointer source, dt_gui_throttle_callback_t callback, gpointer user_data)
dt_throttle_slot_t
@ DT_THROTTLE_SLOT_MAIN
@ DT_THROTTLE_SLOT_PREVIEW
@ DT_THROTTLE_SLOT_OTHER
void(* dt_gui_throttle_callback_t)(gpointer user_data)
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
Definition macros.h:96
unsigned __int64 uint64_t
Definition strptime.c:75
dt_pthread_mutex_t runtime_mutex
dt_atomic_int avg_preview_runtime_us
uint8_t recent_preview_runtime_count
uint32_t recent_preview_runtime_us[5]
dt_atomic_int avg_full_runtime_us
uint32_t recent_full_runtime_us[5]
uint32_t recent_runtime_us[5]
dt_atomic_int avg_runtime_us
dt_gui_throttle_callback_t callback
#define MIN(a, b)
Definition thinplate.c:32
#define MAX(a, b)
Definition thinplate.c:29