-
-
Notifications
You must be signed in to change notification settings - Fork 1k
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
SlicerT UI update #7453
SlicerT UI update #7453
Conversation
4cce529
to
917436e
Compare
917436e
to
0c356d5
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Style review.
Co-authored-by: Rossmaxx <[email protected]>
QString sampleName = m_slicerTParent->getSampleName(); | ||
if (sampleName == "") { sampleName = "No sample loaded"; } | ||
|
||
brush.drawText(5, boxTopY - s_sampleBoxHeight, width(), s_sampleBoxHeight, Qt::AlignLeft, sampleName); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems like lots of manual layout computations. Ideally the different components would be implemented as widgets of their own which would then be put into Qt's layouts so that they can resize dynamically.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pretty sure that using Qt's layouts would be more work and more complex. I use that many offsets to get everything to align pixel-perfectly, so even if I used layouts, I would have to add the offset in some way or another. And the move parts are honestly the simplest, the complex part is drawing the text, dividers, gradients, knobs and backgrounds. Don't really see how that could be implemented any differently.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would definitively be possible with the right resize policies and margins. Layouts are used exactly for situations like these so that complex layout calculations do not have to be reimplemented over and over again. Using widgets in layouts might also lead to more reusable components. However, I guess it's nothing for now.
@DanielKauss, I only did a very quick review but overall I like that this PR lays the base to free the instruments from their confined and tiny windows. 😃 Another good candidate for a resizable and large instrument window would be AudioFileProcessor. Did you also test the rendering performance of the wave view with very large samples? It looks like you are caching the waveform in a |
Did this already on the original PR. The waveform only gets drawn if the seeker changes, and a while ago I also optimized the waveform drawing code, although I don't know if that is still in the codebase. Also tested it again just now and seems fine. |
I have just tested the PR locally and noticed that resizing is very slow and sluggish after having loaded a sample that's 15 seconds long. I guess it will be even worse with longer samples. Based on the performance I get the impression that the code simply renders every sample into the caching pixmap. This will then lead to problems as soon as the pixmap needs to be updated. A 15 seconds long sample has around 720000 samples at a standard sample rate of 48 kHz. If the view is around 1000 pixels wide then this means that 720 samples are rendered on one pixel column. @DanielKauss, if I remember correctly, the view for the samples in the sample track has some optimizations. Can you please check? |
This is somewhat strange. I just tested with a 2 minute sample, and everything worked smoothly. It didn't update at the full 60 fps when resizing, only something like 25, which I think is acceptable for something the user won't do much. And this was with a 2 minute sample, which is exaggerated anyway. And yes, while resizing, the sample gets redrawn on every
No, the waveform drawing function is optimized so that it only draws a maximum of 512 samples per pixel, and I think that is the worst case scenario.
Doesn't look like it, seems like we both just use the Is the lag on resizing really that noticeable? I didn't change any of the drawing code from the original SlicerT, and there everything ran smoothly. If the lag is only minimal, and only present while resizing, I don't think it's worth optimizing for. |
Unfortunately for me it can go well into "several seconds" territory. I have recorded a resizing action here: Bildschirmaufnahme_20240818_103949.webmWhat's interesting is that at the beginning it's ok but that it gets slower and slower as the window gets larger. This might indicate that it is not so much the sample processing but rather the rendering that creates the problems. |
Pretty strange. I have created a small patch to measure the timings. Can you please apply it to this branch and send me the resulting console output when you resize the window? If you don't have a build system set up or something I can send you an executable. Patch is here: timing_patch_slicerT.patch.txt For me, I get the following output, using a 2 minute sample and after resizing for around 5 minutes to check for memory leaks:
Time is in microseconds, and as you can see drawing the big waveform is indeed what takes up the most time. However, for me it still only takes 26 milliseconds, so no idea how you would get a multiple second long delay
This makes sense, since we have to process ~512 samples per pixel-column, and with a wider resolution this just increases. So both the processing time and rendering have to increase. I would be grateful if you could test this out for me, and maybe also provide me with some of your system details. Sorry if this is asking for too much, but thank you anyway. |
@DanielKauss, I have applied the patch and have also added the current time at the start of the resize event to the output. It surprised me that the output indicated an update rate of around 20 Hz/frames per second, i.e. 0.05 seconds per resize event. The visual appearance was laggy like in the video above though. Then it dawned on me that the differences we both observe in performance might be a "Wayland vs. X11" effect as I am running under Wayland. Testing everything under X11 gave a rather smooth output. Attached you can find the output of a resizing action under Wayland and under X11: So the problem seems to be that the Wayland compositor runs at a certain frame rate, in my case 60 Hz, and that if an application is not able to render at that rate then some of its output is dropped. This is a consequence of Waylands "every frame is perfect" approach. This is likely also the reason that I see some updates at the beginning of the resize action but then only very few intermediate ones in between. All other frames are simply dropped and I see the previous rendering of the application. So the problem statement must be corrected to: Resizing the SlicerT window with large samples is laggy under Wayland. Or put differently: LMMS must ensure that it can render at 60 Hz. Otherwise these effects might also show up in other places. |
Wayland could definitely be the problem here. I tried switching to Wayland, but opening LMMS immediately crashes the whole WM (cinnamon). Wayland in general barely works on my system at all, and I have no experience with it either, so I can't really work on this. Optimizing the UI even further to always render more than 60 fps seems unfeasible, especially when for the resize we have to redraw everything. The only band aid fix I can think of is to only redraw the window once the user has stopped resizing, but this would involve some kind of timer, similar to this SO question, and would look worse on all the other systems. Anyway, I can't really work on this, so you should decide what we should do with this. However, this seems like an underlying issue in either Qt, Wayland or the compositor, so this might be difficult to work out. |
@DanielKauss, I have created #7462 to deal with the inefficient rendering. If Wayland becomes more and more commonplace it will make LMMS look bad if it drops so many frames in certain situations. Other applications also manage to render large waveform views so it should be doable. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Like noted, I only did a quick review and tested the running application. I leave the more detailed code style review to @Rossmaxx. 😉
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Style and comment review.
|
||
m_wf = new SlicerTWaveform(248, 128, instrument, this); | ||
m_wf->move(2, 6); | ||
m_wf->move(0, s_topBarHeight); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Michael did point this out, basically, you can use a QVBoxLayout to get the elements listed vertically, and the bottom toolbox can be wrapped in a QHBoxLayout. That way, you'll have to do little calculation.
connect(m_resetButton, &PixmapButton::clicked, m_slicerTParent, &SlicerT::updateSlices); | ||
|
||
update(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Once you switch to layouts, you might not even need this update call
connect(m_resetButton, &PixmapButton::clicked, m_slicerTParent, &SlicerT::updateSlices); | ||
|
||
update(); | ||
} | ||
|
||
Knob* SlicerTView::createStyledKnob() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be easier to add another knob style in Knob.cpp and use that here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How would that be any easier? Even if it were, the knob styles are supposed to be for general use, not something specific like knobs used by only one plugin.
I'll leave this here in case someone else does a review. Using Qt layouts for the resizing is a waste of time, and would only complicate the code even further. With the current method, I can place each object on the exact pixel that I want, where as layouts do not have that level of precision. Even if I did use layouts, all the hard coded numbers would have to be implemented as well, just in the form of offsets. And it would only really affect around 12 lines: the knob and button move functions in the resize event, and the text drawing in the paint event. As for performance, I would hope that doing a few additions is insubstantial in comparison to all the drawing stuff, which would not be affected at all if I switched to layouts. And before this PR, SlicerT also had these values hard coded, just without taking into account the window size. The same is true for all the other plugins, they all move the knobs to hard coded positions without layouts (even something like Monstro) because it is way simpler to get perfect positions that way. |
The other plugins all have a set size and cannot be resized though. Layouts usually make life easier when working with resizable widgets. However, I agree that adding layouts is not part of this PR because it already worked with hard-coded layouts before. |
I'll slack on the layout for now, but would like to see #6992 (or atleast the bugfix) merged before this. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
@DanielKauss This seem to have messed a bit with the LV2 layout. It mostly works though but in the case below a part of the gui has been cut off. |
I opened a separate issue for the layout regressions: #7510 |
* Update SlicerT UI * Style review Co-authored-by: Rossmaxx <[email protected]> * Style fixes --------- Co-authored-by: Rossmaxx <[email protected]>
I wholeheartedly disagree. The whole point of using Qt layouts is that you do not have to worry about the precision yourself, because it will get it right for you anyways. There's a good chance that you wont get it right however, and something will feel misplaced in the arrangement of widgets. There were advancements to move away from using hard-coded positions, like in #6591, so I really don't like how we are taking two steps back here. I find this to be a lot more complicated than just using layouts. I mean, just look at all the manual offset calculations done here, and when something changes (maybe we add a knob somewhere or something else that affects the widget arrangement), all of those numbers will be imprecise (assuming they aren't imprecise already) and will have to be changed. This is a major headache and we should not be making the problem bigger for ourselves. You must remember that software is soft and malleable, which makes it very susceptible to change. Please take into consideration any changes that can be made in the future before making software decisions, even if you can't see the changes yourself in the moment.
Just because other instruments still use hard coded positions does not mean that is how the codebase should be arranging its widgets. There are many things in the codebase that fall into the criteria of "this is the way it is done in the codebase, but its definitely not how we want to do things generally speaking". It is far simpler to get perfect positioning using layouts, because all that needs to be done is to just add the widget to the layout, which lays out the child widgets precisely in the parent widget. If there's any more customization necessary to the layout afterwards, that is always an option. |
@DanielKauss I am thinking of a compromise regarding the current glitches relating to effect view. How about reverting this PR partially, ie the resizeability but change the minimum size to something like 600 x 400 for slicer t alone? @sakertooth is this fine with you too? |
I was already in the process of reverting this partially, so it's fine with me. |
I don't mind if you revert this, but you could also only revert the changes to the effect classes, it's only a few lines. As for using layouts, if you have a good idea of how to implement them to actually make the code simpler, I would be happy to use them. However, I will once again say that layouts are unnecessary here and would only complicate the code further. There isn't really any need for dynamic positioning, since the only coordinates that change are the y coords of the lower components. Implementing layouts would only really "remove" the code that is in the resize event, which is just a few lines long. And then, the hardcoded offsets would have to be somewhere anyway, we would just be moving them around. |
How familiar are you with Qt layouts? It would effectively remove both the |
We should not throw away all the code of this PR as it prepares SlicerT for being resizable. And making the instrument windows resizable should definitively be a goal IMO. As @DanielKauss has already noted it would suffice to revert the changes to the effect classes to fix the problems with the LV2 plugin. To fix the problem with the "distorted" tabs it would suffice to remove the following line from the constructor in
Once the problem with the instrument tabs is fixed the line can then simply be put back and SlicerT would be immediately resizable without having to recode everything or to do hunting in Git commits and messages. Keeping much of the code would also be good for testing the resize functionality of the instrument tabs when it is being fixed. |
I disagree (but not completely). The thing I'm worried about is if this is the implementation for resizability we want. If for whatever reason it isn't, one way or another we would have to start over. There might be also prerequisites before we can safely make instrument windows bigger/resizable (e.g. things like the Envelope and LFO graphs may need to be painted using I will do the partial revert that @michaelgregorius and @DanielKauss mentioned and see that's all that is needed to fix the regressions, anyhow. |
The simplest implementation would be to enable the resizing of the individual instrument view, e.g. the view that shows the waveform in SlicerT, and to have the other tabs at "fixed" sizes, i.e. such that they are in well spaced layouts but do not grow if the window becomes bigger for now. From there on one could then think about other implementations. Some months ago I made a proposal for some changes which would already separate the individual instrument view from the other elements that are always the same anyway. Unfortunately, it was not met with great enthusiasm. You can find it here: #2510 (comment).
I agree. Having the text "Envelope" and "LFO" in the window that shows the envelope and the LFO waveform respectively is not that practical anyway.
👍 |
Did the partial revert as described, but the widget layout in SlicerT is incorrect now, so I have to fix this as well. |
I'm currently trying to do a fixup of #7512 which is a fixup of this PR. Some things I would like to change about
@michaelgregorius What do you think about my proposed changes? |
@JohannesLorenz, I definitively agree with 1. Whether or not an effect can be resized depends on the implementation and therefore it should be a virtual function. Another argument is that such a function would indicate whether an effect would be resizable at all, i.e. it describes the underlying ability. It would then be up to the clients of that method to decide how to treat the result of I don't have a good overview of the instrument and effect inheritance hierarchy. But if I understand correctly, you find it confusing that |
* Update SlicerT UI * Style review Co-authored-by: Rossmaxx <[email protected]> * Style fixes --------- Co-authored-by: Rossmaxx <[email protected]>
@DanielKauss Could you please explain the reasoning behind putting size restrictions on subWin instead of ITW itself? It seems to me it's just hardcoding more values into it without any benefit. |
I honestly can't tell you why I chose the sub-window instead of the instrument track window, but if I remember correctly I copied the code from the
But instead of changing the size of all tabs individually, I changed the size of the sub-window with all tabs in it. And as you can see here, the hard-coded values that you say I have added without benefit were already in the code, just in the form above. It's been a while, so I may not remember everything correctly, sorry. |
Most of the credit goes to @RoxasKH who designed the new UI.
Fixes #7060. This PR updates the existing SlicerT UI to make it bigger and resizable. Also has some small bug fixes and changes to the plugin window related files to allow them to be resizable. Here is the new UI:
The new minimum size is 516x400, and it can be resized without limits.
Changes outside SlicerT
I started this PR a few months ago, so these changes may have been implemented already somewhere else.
The
pluginView
class now has the functionssetResizable
andisResizable
to allow plugins to make their window resizable. This gets checked in theInstrumentTrackWindow
to set a few window flags.Also changed a few things about how effects, LFO and arpeggio act when resized. Again, this might not be needed anymore.
SlicerT bugfixes
Sorry for the huge amount of random numbers in the code, but it's mostly just correcting the UI elements offsets. Anyway, if there is anything I should fix/change please let me know, thank you.