Difference between revisions of "Dev:Adding a Render progress bar"
(Create + 1st part) |
m (→synfig-studio/src/gui/docks/dock_info.cpp) |
||
(2 intermediate revisions by the same user not shown) | |||
Line 1: | Line 1: | ||
Now let's implement a Render Progress Bar!<br /> | Now let's implement a Render Progress Bar!<br /> | ||
It has been requested several time:<br /> | It has been requested several time:<br /> | ||
− | [https://github.com/synfig/synfig/issues/383 | + | [https://github.com/synfig/synfig/issues/383 Indication needed that rendering is in progress. #383]<br /> |
− | [https://github.com/synfig/synfig/issues/626 | + | [https://github.com/synfig/synfig/issues/626 Feature request: Give feedback when rendering is happening and complete #626]<br /> |
− | also in [https://github.com/synfig/synfig/issues/464 | + | also in [https://github.com/synfig/synfig/issues/464 Default render parameter are bad #464] |
== Things to take into account == | == Things to take into account == | ||
Line 14: | Line 14: | ||
After all, this is a kind of information! | After all, this is a kind of information! | ||
+ | === How does really a render work? === | ||
+ | '''App''' ([https://github.com/synfig/synfig/blob/master/synfig-studio/src/gui/app.h .h][https://github.com/synfig/synfig/blob/master/synfig-studio/src/gui/app.cpp .cpp]) is the root of everything and used to store globals.<br /> | ||
+ | But we start in fact from '''CanvasView''' ([https://github.com/synfig/synfig/blob/master/synfig-studio/src/gui/canvasview.h .h][https://github.com/synfig/synfig/blob/master/synfig-studio/src/gui/canvasview.cpp .cpp]) which contains the call to display the '''RenderSettings''' Dialog ([https://github.com/synfig/synfig/blob/master/synfig-studio/src/gui/render.h .h][https://github.com/synfig/synfig/blob/master/synfig-studio/src/gui/render.cpp .cpp]).<br /> | ||
+ | Then we press on render button which leads to execute an '''AsyncRenderer''' ([https://github.com/synfig/synfig/blob/master/synfig-studio/src/gui/asyncrenderer.h .h][https://github.com/synfig/synfig/blob/master/synfig-studio/src/gui/asyncrenderer.cpp .cpp]) (or 2, sequentially, if we have a second pass for Alpha extraction).<br /> | ||
+ | '''AsyncRenderer''' can have 4 types of targets, ''AsyncTarget_Cairo'', AsyncTarget_Cairo_Tile, ''AsyncTarget_Scanline'', AsyncTarget_Tile.<br /> | ||
+ | Only ''AsyncTarget_Cairo'' and ''AsyncTarget_Scanline'' have a ''frame_ready()'' function that we will use to implement our call to update to the Render ProgressBar.<br /> | ||
+ | |||
+ | Note that we can have 2 passes, this has to be considered when displaying the percents of accomplished render.<br /> | ||
+ | |||
+ | The render itself is done writing the data to the target, then the selected codec is fed through a pipe (for example ffmpeg). | ||
+ | |||
+ | === Where to do calls and implementation? === | ||
+ | In different files, as it is a "multi-level" task.<br /> | ||
+ | The details are described in each file touched in the next section. | ||
== Implementation == | == Implementation == | ||
=== synfig-studio/src/gui/docks/dock_info.h === | === synfig-studio/src/gui/docks/dock_info.h === | ||
* Declare the components and members<br /> | * Declare the components and members<br /> | ||
− | In #include section | + | In #include section: |
#include <gtkmm/progressbar.h> | #include <gtkmm/progressbar.h> | ||
− | In private section | + | In private section: |
Gtk::ProgressBar render_progress; | Gtk::ProgressBar render_progress; | ||
Line 29: | Line 43: | ||
int n_passes_pending; | int n_passes_pending; | ||
− | In public section | + | In public section: |
//! Current render progress - 0.0 to 1.0 | //! Current render progress - 0.0 to 1.0 | ||
// depends on n_passes_requested and current_pass | // depends on n_passes_requested and current_pass | ||
Line 37: | Line 51: | ||
=== synfig-studio/src/gui/docks/dock_info.cpp === | === synfig-studio/src/gui/docks/dock_info.cpp === | ||
+ | * Here we will implement the UI and members | ||
+ | In #include section: | ||
+ | #include "app.h" | ||
+ | #include <gtkmm/progressbar.h> | ||
+ | ''It will permit to access our App::dock_info_ as a static from anywhere in the application'' | ||
+ | |||
+ | In Dock_Info(), at the end: | ||
+ | ''just before table->show_all();'' | ||
+ | //Render Progress Bar | ||
+ | table->attach(*manage(new Gtk::Label(_("Render Progress: "))),0,1,5,6,Gtk::EXPAND|Gtk::FILL,Gtk::SHRINK|Gtk::FILL); | ||
+ | table->attach(render_progress, 0,5,6,7,Gtk::EXPAND|Gtk::FILL,Gtk::SHRINK|Gtk::FILL); | ||
+ | |||
+ | render_progress.set_show_text(true); | ||
+ | render_progress.set_text(strprintf("%.1f%%", 0.0)); | ||
+ | render_progress.set_fraction(0.0); | ||
+ | //Another spacer | ||
+ | table->attach(*manage(new Gtk::Label),0,5,7,8); | ||
+ | |||
+ | ''and after add(*table);'' | ||
+ | |||
+ | //Render progress | ||
+ | set_n_passes_requested(1); //Default | ||
+ | set_n_passes_pending (0); //Default | ||
+ | set_render_progress (0.0); //Default, 0.0% | ||
+ | |||
+ | Then at the end of the file, we add these 3 functions: | ||
+ | |||
+ | void studio::Dock_Info::set_n_passes_requested(int value) | ||
+ | { | ||
+ | n_passes_requested = value; | ||
+ | } | ||
+ | |||
+ | void studio::Dock_Info::set_n_passes_pending(int value) | ||
+ | { | ||
+ | n_passes_pending = value; | ||
+ | } | ||
+ | |||
+ | void studio::Dock_Info::set_render_progress(float value) | ||
+ | { | ||
+ | float coeff = (1.000 / (float)n_passes_requested); //% of fraction for 1 pass if more than 1 pass | ||
+ | float already_done = coeff * (float)(n_passes_requested - n_passes_pending -1); | ||
+ | float r = ( coeff * value ) + already_done; | ||
+ | |||
+ | render_progress.set_text( strprintf( "%.1f%%", r*100 )); | ||
+ | render_progress.set_fraction(r); | ||
+ | } | ||
+ | |||
+ | The 2 first ones are obvious, the last one does the calculation for the display of the current percents of the WHOLE TASK.<br /> | ||
+ | If we have only 1 pass, value will be reflected directly.<br /> | ||
+ | In case of 2 (or more, who knows what will be implemented later!), each pass will still continue to send its progress as if it was the only one in the world; we will do the adjustments here.<br /> | ||
+ | 100% of pass 1 while be displayed as 50%.<br /> | ||
+ | 100% of pass 2 while be displayed as 100%.<br /> | ||
+ | If we had 3 passes, it would be 33.3%, 66.6% and 100.0%<br /> | ||
+ | |||
+ | === synfig-studio/src/gui/docks/app.h === | ||
+ | * Lets add our ''static dock_info_''. A ''studio::dock_info'' is already defined in the .cpp but we need to access it as ''App::dock_info_''! | ||
+ | Inside the static declarations: | ||
+ | |||
+ | static Dock_Info* dock_info_; //For Render ProgressBar | ||
+ | === synfig-studio/src/gui/docks/app.cpp === | ||
+ | * Let's declare the static | ||
+ | Inside the declare of statics | ||
+ | Dock_Info* App::dock_info_ = 0; | ||
+ | |||
+ | At the end of the constructor App() | ||
+ | App::dock_info_ = dock_info; | ||
+ | ''It looks like some kind of alias!'' | ||
+ | |||
+ | === synfig-studio/src/gui/docks/render.cpp === | ||
+ | * Here the things start to become serious | ||
+ | In the #include section: | ||
+ | #include "docks/dockmanager.h" | ||
+ | #include "docks/dock_info.h" | ||
+ | |||
+ | In RenderSettings::on_render_pressed<br /> | ||
+ | ''Just before submit_next_render_pass();'' | ||
+ | App::dock_info_->set_n_passes_requested(render_passes.size()); | ||
+ | App::dock_info_->set_n_passes_pending(render_passes.size()); | ||
+ | App::dock_info_->set_render_progress(0.0); | ||
+ | App::dock_manager->find_dockable("info").present(); //Bring Dock_Info to front | ||
+ | ''We initialized our ProgressBar with its default parameters to display 0.0%''<br /> | ||
+ | ''Note that the Dock_Info will be brought to front to show the progression... It's its goal!'' | ||
+ | |||
+ | In RenderSettings::submit_next_render_pass() | ||
+ | ''Just after render_passes.pop_back();'' | ||
+ | App::dock_info_->set_n_passes_pending(render_passes.size()); //! Decrease until 0 | ||
+ | App::dock_info_->set_render_progress(0.0); //For this pass | ||
+ | ''We reinitialized the parameters for this specific pass!'' | ||
+ | |||
+ | '''Doing tests I noticed that with extract alpha option on, we have 2 passes and therefore the render done sound was played 2 times!<br /> | ||
+ | Let's correct this bad behaviour''' | ||
+ | In RenderSettings::on_finished(), around submit_next_render_pass(); | ||
+ | bool really_finished = (render_passes.size() == 0); //Must be checked BEFORE submit_next_render_pass(); | ||
+ | |||
+ | submit_next_render_pass(); | ||
+ | |||
+ | //Sound effect - RenderDone (-1 : play on first free channel, 0 : no repeat) | ||
+ | if (App::use_render_done_sound) Mix_PlayChannel( -1, App::gRenderDone, 0 ); | ||
+ | if (really_finished) { //Because of multi-pass render | ||
+ | if (App::use_render_done_sound) Mix_PlayChannel( -1, App::gRenderDone, 0 ); | ||
+ | App::dock_info_->set_render_progress(1.0); | ||
+ | } | ||
+ | |||
+ | ''This way, it will play only once the full render has occured!'' | ||
+ | |||
+ | === synfig-studio/src/gui/docks/asyncrender.cpp === | ||
+ | * Now the deepest part | ||
+ | In #include section: | ||
+ | #include <docks/dock_info.h> | ||
+ | |||
+ | In the beginning of AsyncRenderer::start() | ||
+ | App::dock_info_->set_render_progress(0.0); | ||
+ | |||
+ | |||
+ | In ''void frame_ready()'' '''of both''' ''AsyncTarget_Cairo'' and ''AsyncTarget_Scanline''<br /> | ||
+ | ''After ready_next=true;'' | ||
+ | int n_total_frames_to_render = warm_target->desc.get_frame_end() //120 | ||
+ | - warm_target->desc.get_frame_start() //0 | ||
+ | + 1; //->121 | ||
+ | int current_rendered_frames_count = warm_target->curr_frame_ | ||
+ | - warm_target->desc.get_frame_start(); | ||
+ | float r = (float) current_rendered_frames_count | ||
+ | / (float) n_total_frames_to_render; | ||
+ | App::dock_info_->set_render_progress(r); | ||
+ | |||
+ | Here the current progress is calculated according the starting, ending and current frame, in a range of 0.0 to 1.0<br /> | ||
+ | The pass (or target) thinks it is the only one in the world but it is compensated in the display :) | ||
+ | |||
+ | Hoping this will help you to come and join the effort in development of Synfig :) |
Latest revision as of 01:10, 8 January 2019
Now let's implement a Render Progress Bar!
It has been requested several time:
Indication needed that rendering is in progress. #383
Feature request: Give feedback when rendering is happening and complete #626
also in Default render parameter are bad #464
Things to take into account
- Where to place the Progress Bar?
- How does really a render work?
- Where to do calls and implementation?
Where to place the Progress Bar?
I chose to implement it in the Dock_Info panel.
After all, this is a kind of information!
How does really a render work?
App (.h.cpp) is the root of everything and used to store globals.
But we start in fact from CanvasView (.h.cpp) which contains the call to display the RenderSettings Dialog (.h.cpp).
Then we press on render button which leads to execute an AsyncRenderer (.h.cpp) (or 2, sequentially, if we have a second pass for Alpha extraction).
AsyncRenderer can have 4 types of targets, AsyncTarget_Cairo, AsyncTarget_Cairo_Tile, AsyncTarget_Scanline, AsyncTarget_Tile.
Only AsyncTarget_Cairo and AsyncTarget_Scanline have a frame_ready() function that we will use to implement our call to update to the Render ProgressBar.
Note that we can have 2 passes, this has to be considered when displaying the percents of accomplished render.
The render itself is done writing the data to the target, then the selected codec is fed through a pipe (for example ffmpeg).
Where to do calls and implementation?
In different files, as it is a "multi-level" task.
The details are described in each file touched in the next section.
Implementation
synfig-studio/src/gui/docks/dock_info.h
- Declare the components and members
In #include section:
#include <gtkmm/progressbar.h>
In private section:
Gtk::ProgressBar render_progress; //! Number of passes request - 1 or 2 (if alpha) int n_passes_requested; //! Number of passes pending - 2,1,0 int n_passes_pending;
In public section:
//! Current render progress - 0.0 to 1.0 // depends on n_passes_requested and current_pass void set_render_progress (float value); void set_n_passes_requested(int value); void set_n_passes_pending (int value);
synfig-studio/src/gui/docks/dock_info.cpp
- Here we will implement the UI and members
In #include section:
#include "app.h" #include <gtkmm/progressbar.h>
It will permit to access our App::dock_info_ as a static from anywhere in the application
In Dock_Info(), at the end: just before table->show_all();
//Render Progress Bar table->attach(*manage(new Gtk::Label(_("Render Progress: "))),0,1,5,6,Gtk::EXPAND|Gtk::FILL,Gtk::SHRINK|Gtk::FILL); table->attach(render_progress, 0,5,6,7,Gtk::EXPAND|Gtk::FILL,Gtk::SHRINK|Gtk::FILL);
render_progress.set_show_text(true); render_progress.set_text(strprintf("%.1f%%", 0.0)); render_progress.set_fraction(0.0); //Another spacer table->attach(*manage(new Gtk::Label),0,5,7,8);
and after add(*table);
//Render progress set_n_passes_requested(1); //Default set_n_passes_pending (0); //Default set_render_progress (0.0); //Default, 0.0%
Then at the end of the file, we add these 3 functions:
void studio::Dock_Info::set_n_passes_requested(int value) { n_passes_requested = value; } void studio::Dock_Info::set_n_passes_pending(int value) { n_passes_pending = value; }
void studio::Dock_Info::set_render_progress(float value) { float coeff = (1.000 / (float)n_passes_requested); //% of fraction for 1 pass if more than 1 pass float already_done = coeff * (float)(n_passes_requested - n_passes_pending -1); float r = ( coeff * value ) + already_done; render_progress.set_text( strprintf( "%.1f%%", r*100 )); render_progress.set_fraction(r); }
The 2 first ones are obvious, the last one does the calculation for the display of the current percents of the WHOLE TASK.
If we have only 1 pass, value will be reflected directly.
In case of 2 (or more, who knows what will be implemented later!), each pass will still continue to send its progress as if it was the only one in the world; we will do the adjustments here.
100% of pass 1 while be displayed as 50%.
100% of pass 2 while be displayed as 100%.
If we had 3 passes, it would be 33.3%, 66.6% and 100.0%
synfig-studio/src/gui/docks/app.h
- Lets add our static dock_info_. A studio::dock_info is already defined in the .cpp but we need to access it as App::dock_info_!
Inside the static declarations:
static Dock_Info* dock_info_; //For Render ProgressBar
synfig-studio/src/gui/docks/app.cpp
- Let's declare the static
Inside the declare of statics
Dock_Info* App::dock_info_ = 0;
At the end of the constructor App()
App::dock_info_ = dock_info;
It looks like some kind of alias!
synfig-studio/src/gui/docks/render.cpp
- Here the things start to become serious
In the #include section:
#include "docks/dockmanager.h" #include "docks/dock_info.h"
In RenderSettings::on_render_pressed
Just before submit_next_render_pass();
App::dock_info_->set_n_passes_requested(render_passes.size()); App::dock_info_->set_n_passes_pending(render_passes.size()); App::dock_info_->set_render_progress(0.0); App::dock_manager->find_dockable("info").present(); //Bring Dock_Info to front
We initialized our ProgressBar with its default parameters to display 0.0%
Note that the Dock_Info will be brought to front to show the progression... It's its goal!
In RenderSettings::submit_next_render_pass() Just after render_passes.pop_back();
App::dock_info_->set_n_passes_pending(render_passes.size()); //! Decrease until 0 App::dock_info_->set_render_progress(0.0); //For this pass
We reinitialized the parameters for this specific pass!
Doing tests I noticed that with extract alpha option on, we have 2 passes and therefore the render done sound was played 2 times!
Let's correct this bad behaviour
In RenderSettings::on_finished(), around submit_next_render_pass();
bool really_finished = (render_passes.size() == 0); //Must be checked BEFORE submit_next_render_pass();
submit_next_render_pass();
//Sound effect - RenderDone (-1 : play on first free channel, 0 : no repeat) if (App::use_render_done_sound) Mix_PlayChannel( -1, App::gRenderDone, 0 ); if (really_finished) { //Because of multi-pass render if (App::use_render_done_sound) Mix_PlayChannel( -1, App::gRenderDone, 0 ); App::dock_info_->set_render_progress(1.0); }
This way, it will play only once the full render has occured!
synfig-studio/src/gui/docks/asyncrender.cpp
- Now the deepest part
In #include section:
#include <docks/dock_info.h>
In the beginning of AsyncRenderer::start()
App::dock_info_->set_render_progress(0.0);
In void frame_ready() of both AsyncTarget_Cairo and AsyncTarget_Scanline
After ready_next=true;
int n_total_frames_to_render = warm_target->desc.get_frame_end() //120 - warm_target->desc.get_frame_start() //0 + 1; //->121 int current_rendered_frames_count = warm_target->curr_frame_ - warm_target->desc.get_frame_start(); float r = (float) current_rendered_frames_count / (float) n_total_frames_to_render; App::dock_info_->set_render_progress(r);
Here the current progress is calculated according the starting, ending and current frame, in a range of 0.0 to 1.0
The pass (or target) thinks it is the only one in the world but it is compensated in the display :)
Hoping this will help you to come and join the effort in development of Synfig :)