-
Notifications
You must be signed in to change notification settings - Fork 1.3k
agx: Fix crash during initialisation #19745
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
|
Cause: null pointer dereference during initialisation: slider params change in gui_init -> triggers gui_changed -> calls _update_curve_warnings, which tries to adjust not yet created UI elements. To prevent this, I added a Relevant part of backtrace: |
If you can fix this with this it would be better indeed as this is a common pattern already used in many places. |
|
The lock is actually added in imageop, I just have to use it. Thanks! |
|
This seems to break drawing the UI. I have to leave now, will continue in the evening/tomorrow. |
|
Is there a particular reason why you call |
Right, thanks. I think that can be removed. |
c23d9ad to
dd44fbf
Compare
|
Seems to be working fine for me with the latest updates. I'll publish an AppImage on pixls and ask others to test, as well. |
|
I've found some UI elements not properly initialised. In 1ef7e76, I've tried extracting all shared operations into functions, so gui_update and gui_changed can work independently without repeated code. I'm not aware of more issues, but let's see if people report anything. |
Probably due to the I like the pattern of always calling But if what you have now works for all cases I'm not going to argue over it. |
|
Before anyone misunderstands: I'm not arguing, I'm asking. I'm lost in the UI framework, never having worked with any UI at all, except for the most trivial 'hello world' demos, and even that not in C/GTK/bauhaus.
Without which (or adding a separate 'initialized' flag) we crash, because adjusting slider settings in gui_init triggers gui_changed, and we do not have all controls laid out yet. There are adjustments I need to do when the UI is (re)initialised, and some of that overlaps with things I need to do when the GUI is changed.
Then, when a picker callback is used:
When the GUI changes:
So, what I'm asking is: what would be the most robust way to make sure everything gets initialised/updated all the time it must be, and we don't run into crashes because of the not yet completely laid out UI? void gui_changed(dt_iop_module_t *self,
GtkWidget *widget,
void *previous)
{
dt_iop_agx_gui_data_t *g = self->gui_data;
if (darktable.gui->reset) return;
dt_iop_agx_params_t *p = self->params;
if(widget == g->black_exposure_picker) {...}
if(widget == g->white_exposure_picker) {...}
if(widget == g->security_factor) {...}
if(g && p->auto_gamma) {...}
// this is the redrawing part that we need for both gui_init and gui_change
if(!widget)
{
// called from gui_init
_update_pivot_slider_settings(g->basic_curve_controls.curve_pivot_x, p);
}
_update_redraw_dynamic_gui(self, g, p);
}
void gui_update(dt_iop_module_t *self)
{
const dt_iop_agx_gui_data_t* const g = self->gui_data;
const dt_iop_agx_params_t* const p = self->params;
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->auto_gamma),
p->auto_gamma);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->disable_primaries_adjustments),
p->disable_primaries_adjustments);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->completely_reverse_primaries),
p->completely_reverse_primaries);
// don't call _update_pivot_slider_settings and _update_redraw_dynamic_gui
gui_changed(self, NULL, NULL);
} |
|
Or I could remove that 'if': void gui_changed(dt_iop_module_t *self,
GtkWidget *widget,
void *previous)
{
dt_iop_agx_gui_data_t *g = self->gui_data;
if (darktable.gui->reset) return;
dt_iop_agx_params_t *p = self->params;
if(widget == g->black_exposure_picker)
{
...
_update_pivot_x(old_black_ev, old_white_ev, self);
}
if(widget == g->white_exposure_picker)
{
...
_update_pivot_x(old_black_ev, old_white_ev, self);
}
if(widget == g->security_factor)
{
...
_update_pivot_x(old_black_ev, old_white_ev, self);
darktable.gui->reset++;
dt_bauhaus_slider_set(g->black_exposure_picker, p->range_black_relative_ev);
dt_bauhaus_slider_set(g->white_exposure_picker, p->range_white_relative_ev);
darktable.gui->reset--;
}
if(g && p->auto_gamma) {...}
_update_pivot_slider_settings(self);
_update_redraw_dynamic_gui(self, g, p);
}And remove the call to
I'm sorry I'm commenting so much. |
No worries at all! I prefer more questions rather than less. The more people understand and think about this stuff, the better. And the more I explain, the more chance to realise opportunities for improvements. I'm just sorry I'm making you go through so much churn. When you're fed up, just start ignoring me; if you have no crashes and it works, your obligations are fulfilled.
I didn't spend enough time analysing to understand this. Are you saying not all widgets have been created by
wait! what? EDIT: oh, I guess the parameter to gui_changed that gui_update fills with NULL, ok.
Everything after this is dependent on changes that the user can make (
This. That if just means you didnt do it when called from gui_update when you just said it needed to be called. But as far as I can tell you still have which would completely prevent this from being executed when called from I don't have time to do a deep dive into this (this week) so I just hope you can make it all work without crashing and don't worry too much about whether you are using the intended patterns correctly. And also don't worry about optimisations in gui_changed where certain widget changes dont require layout/formatting changes; there seem to be few of those and the logic would just get unnecessarily complex. |
No: _create_basic_curve_controls_box adjusts a slider (dt_bauhaus_slider_set_soft_range). As a result, _slider_value_change -> dt_iop_gui_changed -> agx#gui_changed -> agx#_update_curve_warnings -> bauhaus#dt_bauhaus_widget_set_quad_paint is called. And the problem is, in _update_curve_warnings, we are trying to paint something that is only created later: agx#_create_basic_curve_controls_box will call bauhaus#dt_bauhaus_slider_set_soft_range: // curve_pivot_y_linear
slider = dt_color_picker_new(self, DT_COLOR_PICKER_AREA | DT_COLOR_PICKER_DENOISE, dt_bauhaus_slider_from_params(section, "curve_pivot_y_linear_output"));
controls->curve_pivot_y_linear = slider;
dt_bauhaus_slider_set_format(slider, "%");
dt_bauhaus_slider_set_digits(slider, 2);
dt_bauhaus_slider_set_factor(slider, 100.f);
dt_bauhaus_slider_set_soft_range(slider, 0.f, 1.f); <------ this line
... agx#_create_basic_curve_controls_box continues, and **later** sets up curve_toe_power:
// curve_toe_power
slider = dt_bauhaus_slider_from_params(section, "curve_toe_power");
controls->curve_toe_power = slider;
dt_bauhaus_slider_set_soft_range(slider, 1.f, 5.f);
gtk_widget_set_tooltip_text(slider, _("contrast in shadows\n"
"higher values keep the slope nearly constant for longer,\n"
"at the cost of a more sudden drop near black"));So, because of that slider value change, we ended up here: (-> bauhaus#_slider_value_change -> imageop#dt_iop_gui_changed -> agx#gui_changed -> agx#_update_curve_warnings): static void _update_curve_warnings(dt_iop_module_t *self)
{
const dt_iop_agx_gui_data_t *g = self->gui_data;
const dt_iop_agx_params_t *p = self->params;
if(!g) return;
const gboolean warnings_enabled = dt_conf_get_bool("plugins/darkroom/agx/enable_curve_warnings");
const tone_mapping_params_t params = _calculate_tone_mapping_params(p);
dt_bauhaus_widget_set_quad_paint(g->basic_curve_controls.curve_toe_power, <------ this line passes curve_toe_power, but it's still null
params.need_convex_toe && warnings_enabled
? dtgtk_cairo_paint_warning : NULL, CPF_ACTIVE, NULL);
dt_bauhaus_widget_set_quad_paint(g->basic_curve_controls.curve_shoulder_power,
params.need_concave_shoulder && warnings_enabled
? dtgtk_cairo_paint_warning : NULL, CPF_ACTIVE, NULL);
}And then: |
|
Does _create_basic_curve_controls_box get called from gui_init, where reset>0, or only later? That would also probably mean that shortcuts/actions don't get set up for those widgets? On phone so can't research myself, sorry. |
Yes.
|
|
I don't know why / how we got there. From my reading of static void _slider_set_normalized(dt_bauhaus_widget_t *w, float pos)
{
...
if(!darktable.gui->reset)
{
d->is_changed = -1;
_slider_value_change(w);
}
}Note that after the crash occurred, I restarted darktable, and was unable to crash it again. Also, no user has ever reported such a crash. Note: I updated the PR description with the original backtrace. |
Exactly. That's what mystified me.
Ah. Since this was in init, I assumed it was 100% reproducible. Unless it happens only while creating multiple instances? |
|
I'll make one more attempt at reproducing this. It occurred when I loaded the square-cropped version of https://discuss.pixls.us/t/editing-intent-and-choosing-a-direction/54140/23. Immediately after, opening the same images caused no crash. |
|
I cannot reproduce it with master -- and that's not a surprise, given what we have seen. The changes in this PR hardly count as a bugfix:
So, if it's not a bugfix, do we want to merge it before or after the release? |
After the release. |
…ore all controls created; also: remove redundant soft limits (matching hard limits)
… imageop#dt_iop_gui_init
…warnings to gui_update so initial state is painted correctly
…aws into function _update_redraw_dynamic_gui, called from gui_update and gui_changed; extract slider offset, scale, default and value updates into _update_pivot_slider_settings, called from _update_pivot_x (gui_changed and exposure picker callbacks) and from gui_update.
…ed (the idea is to disable (un)rotations and attenuation/purity boost only); use 60% hue preservation for Blender defaults (as that is the Blender default)
1ef7e76 to
7015fe7
Compare
Fixed a crash during initialisation caused by callback triggered before all controls created; also: remove redundant soft limits (matching hard limits).
bt.txt