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
19#include "gui/gui_throttle.h"
20
21#include "common/atomic.h"
22#include "common/dtpthread.h"
23#include "control/conf.h"
25
26#include <stdint.h>
27
28#define DT_GUI_THROTTLE_RUNTIME_CONF "processing/gui_throttle_runtime_us"
29
36
56
58
59static dt_gui_throttle_task_t *_find_task(gpointer source)
60{
61 for(GList *iter = _gui_throttle.pending_tasks.head; iter; iter = g_list_next(iter))
62 {
64 if(task->source == source) return task;
65 }
66
67 return NULL;
68}
69
70static guint _get_user_timeout_ms(void)
71{
72 return (guint)MAX(dt_conf_get_int("processing/timeout"), 0);
73}
74
75static guint _runtime_us_to_ms(const int runtime_us)
76{
77 if(runtime_us <= 0) return 0;
78 return (guint)MAX(1, (runtime_us + 999) / 1000);
79}
80
81static guint _effective_timeout_ms(void)
82{
83 const guint user_timeout_ms = _get_user_timeout_ms();
84 const guint runtime_timeout_ms = _runtime_us_to_ms(dt_atomic_get_int(&_gui_throttle.avg_runtime_us));
85 if(runtime_timeout_ms == 0) return user_timeout_ms;
86 return MIN(user_timeout_ms, runtime_timeout_ms);
87}
88
89static gboolean _dispatch_pending_tasks(gpointer user_data)
90{
91 (void)user_data;
92
94
95 GQueue ready = G_QUEUE_INIT;
96 ready.head = _gui_throttle.pending_tasks.head;
97 ready.tail = _gui_throttle.pending_tasks.tail;
98 ready.length = _gui_throttle.pending_tasks.length;
99 g_queue_init(&_gui_throttle.pending_tasks);
100
101 while(!g_queue_is_empty(&ready))
102 {
103 dt_gui_throttle_task_t *task = (dt_gui_throttle_task_t *)g_queue_pop_head(&ready);
104 if(task->callback) task->callback(task->user_data);
105 g_free(task);
106 }
107
108 return G_SOURCE_REMOVE;
109}
110
112{
114 g_queue_init(&_gui_throttle.pending_tasks);
115
116 const int saved_runtime_us = MAX(dt_conf_get_int(DT_GUI_THROTTLE_RUNTIME_CONF), 0);
120
121 if(saved_runtime_us > 0)
122 {
123 for(uint8_t i = 0; i < G_N_ELEMENTS(_gui_throttle.recent_runtime_us); i++)
124 {
125 _gui_throttle.recent_runtime_us[i] = (uint32_t)saved_runtime_us;
126 _gui_throttle.recent_full_runtime_us[i] = (uint32_t)saved_runtime_us;
127 _gui_throttle.recent_preview_runtime_us[i] = (uint32_t)saved_runtime_us;
128 }
129
136 }
137}
138
154
155void dt_gui_throttle_record_runtime(const dt_dev_pixelpipe_t *pipe, const gint64 runtime_us)
156{
157 if(IS_NULL_PTR(pipe) || runtime_us <= 0) return;
158 if(pipe->type != DT_DEV_PIXELPIPE_FULL && pipe->type != DT_DEV_PIXELPIPE_PREVIEW) return;
159
160 const uint32_t clamped_runtime_us = (uint32_t)MIN(runtime_us, (gint64)G_MAXUINT32);
161
167 = (uint8_t)((_gui_throttle.recent_runtime_pos + 1) % G_N_ELEMENTS(_gui_throttle.recent_runtime_us));
168
169 uint64_t runtime_sum = 0;
170 for(uint8_t i = 0; i < _gui_throttle.recent_runtime_count; i++)
171 runtime_sum += _gui_throttle.recent_runtime_us[i];
172
173 const int avg_runtime_us = (int)(runtime_sum / MAX(_gui_throttle.recent_runtime_count, (uint8_t)1));
175
176 if(pipe->type == DT_DEV_PIXELPIPE_FULL)
177 {
183
184 uint64_t full_runtime_sum = 0;
185 for(uint8_t i = 0; i < _gui_throttle.recent_full_runtime_count; i++)
186 full_runtime_sum += _gui_throttle.recent_full_runtime_us[i];
187
188 const int avg_full_runtime_us
189 = (int)(full_runtime_sum / MAX(_gui_throttle.recent_full_runtime_count, (uint8_t)1));
191 }
192 else if(pipe->type == DT_DEV_PIXELPIPE_PREVIEW)
193 {
200
201 uint64_t preview_runtime_sum = 0;
202 for(uint8_t i = 0; i < _gui_throttle.recent_preview_runtime_count; i++)
203 preview_runtime_sum += _gui_throttle.recent_preview_runtime_us[i];
204
205 const int avg_preview_runtime_us
206 = (int)(preview_runtime_sum / MAX(_gui_throttle.recent_preview_runtime_count, (uint8_t)1));
208 }
210}
211
216
234
236{
237 return _effective_timeout_ms();
238}
239
241{
242 const gint64 timeout_ms = _effective_timeout_ms();
243 if(timeout_ms <= 0) return 0;
244 return timeout_ms * 1000;
245}
246
247void dt_gui_throttle_queue(gpointer source, dt_gui_throttle_callback_t callback, gpointer user_data)
248{
249 if(IS_NULL_PTR(callback)) return;
250 if(IS_NULL_PTR(source)) source = user_data;
251
252 const guint timeout_ms = _effective_timeout_ms();
253 if(timeout_ms == 0)
254 {
256 callback(user_data);
257 return;
258 }
259
260 dt_gui_throttle_task_t *task = _find_task(source);
261 if(task)
262 {
263 task->callback = callback;
264 task->user_data = user_data;
265 }
266 else
267 {
268 task = g_malloc0(sizeof(*task));
269 task->source = source;
270 task->callback = callback;
271 task->user_data = user_data;
272 g_queue_push_tail(&_gui_throttle.pending_tasks, task);
273 }
274
276 _gui_throttle.timeout_source = g_timeout_add(timeout_ms, _dispatch_pending_tasks, NULL);
277}
278
279void dt_gui_throttle_cancel(gpointer source)
280{
281 if(IS_NULL_PTR(source)) return;
282
283 for(GList *iter = _gui_throttle.pending_tasks.head; iter; iter = g_list_next(iter))
284 {
286 if(task->source != source) continue;
287
288 g_queue_delete_link(&_gui_throttle.pending_tasks, iter);
289 g_free(task);
290 break;
291 }
292
294 {
295 g_source_remove(_gui_throttle.timeout_source);
297 }
298}
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:66
typedef void((*dt_cache_allocate_t)(void *userdata, dt_cache_entry_t *entry))
void dt_conf_set_int(const char *name, int val)
int dt_conf_get_int(const char *name)
#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 darktable.h:281
static int dt_pthread_mutex_unlock(dt_pthread_mutex_t *mutex) RELEASE(mutex) NO_THREAD_SAFETY_ANALYSIS
Definition dtpthread.h:374
static int dt_pthread_mutex_init(dt_pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr)
Definition dtpthread.h:359
static int dt_pthread_mutex_destroy(dt_pthread_mutex_t *mutex)
Definition dtpthread.h:379
static int dt_pthread_mutex_lock(dt_pthread_mutex_t *mutex) ACQUIRE(mutex) NO_THREAD_SAFETY_ANALYSIS
Definition dtpthread.h:364
#define DT_GUI_THROTTLE_RUNTIME_CONF
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_init(void)
static dt_gui_throttle_task_t * _find_task(gpointer source)
void dt_gui_throttle_record_runtime(const dt_dev_pixelpipe_t *pipe, const gint64 runtime_us)
int dt_gui_throttle_get_runtime_us(void)
void dt_gui_throttle_cleanup(void)
int dt_gui_throttle_get_pipe_runtime_us(const dt_dev_pixelpipe_type_t pipe_type)
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)
void(* dt_gui_throttle_callback_t)(gpointer user_data)
Definition gui_throttle.h:9
dt_dev_pixelpipe_type_t
Definition pixelpipe.h:36
@ DT_DEV_PIXELPIPE_THUMBNAIL
Definition pixelpipe.h:41
@ DT_DEV_PIXELPIPE_EXPORT
Definition pixelpipe.h:38
@ DT_DEV_PIXELPIPE_NONE
Definition pixelpipe.h:37
@ DT_DEV_PIXELPIPE_PREVIEW
Definition pixelpipe.h:40
@ DT_DEV_PIXELPIPE_FULL
Definition pixelpipe.h:39
unsigned __int64 uint64_t
Definition strptime.c:75
dt_dev_pixelpipe_type_t type
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