minor cleanup and documentation of render functions
This commit is contained in:
+17
-19
@@ -200,7 +200,7 @@ GLuint compose_sequence(ComposeSequenceParams ¶ms) {
|
||||
|
||||
// open if not open
|
||||
if (!c->open) {
|
||||
open_clip(c, !params.rendering);
|
||||
open_clip(c, !params.single_threaded);
|
||||
}
|
||||
|
||||
clip_is_active = true;
|
||||
@@ -225,7 +225,7 @@ GLuint compose_sequence(ComposeSequenceParams ¶ms) {
|
||||
// if the clip is a nested sequence or null clip, just open it
|
||||
|
||||
if (is_clip_active(c, playhead)) {
|
||||
if (!c->open) open_clip(c, !params.rendering);
|
||||
if (!c->open) open_clip(c, !params.single_threaded);
|
||||
clip_is_active = true;
|
||||
} else if (c->finished_opening) {
|
||||
close_clip(c, false);
|
||||
@@ -584,19 +584,18 @@ GLuint compose_sequence(ComposeSequenceParams ¶ms) {
|
||||
glPopMatrix();
|
||||
}
|
||||
} else {
|
||||
if (params.render_audio || (config.enable_audio_scrubbing && audio_scrub && params.seq->playhead > c->timeline_in)) {
|
||||
if (c->media != nullptr && c->media->get_type() == MEDIA_TYPE_SEQUENCE) {
|
||||
params.nests.append(c);
|
||||
compose_sequence(params);
|
||||
params.nests.removeLast();
|
||||
} else {
|
||||
if (c->lock.tryLock()) {
|
||||
// clip is not caching, start caching audio
|
||||
cache_clip(c, playhead, c->audio_reset, !params.render_audio, params.nests, params.playback_speed);
|
||||
c->lock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (c->media != nullptr && c->media->get_type() == MEDIA_TYPE_SEQUENCE) {
|
||||
params.nests.append(c);
|
||||
compose_sequence(params);
|
||||
params.nests.removeLast();
|
||||
} else {
|
||||
if (c->lock.tryLock()) {
|
||||
// Check whether cacher is currently active, if not activate it now
|
||||
|
||||
cache_clip(c, playhead, c->audio_reset, (params.viewer != nullptr && !params.viewer->playing), params.nests, params.playback_speed);
|
||||
c->lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
// visually update all the keyframe values
|
||||
if (c->sequence == params.seq) { // only if you can currently see them
|
||||
@@ -631,15 +630,14 @@ GLuint compose_sequence(ComposeSequenceParams ¶ms) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void compose_audio(Viewer* viewer, Sequence* seq, bool render_audio, int playback_speed) {
|
||||
void compose_audio(Viewer* viewer, Sequence* seq, int playback_speed) {
|
||||
ComposeSequenceParams params;
|
||||
params.viewer = viewer;
|
||||
params.ctx = nullptr;
|
||||
params.seq = seq;
|
||||
params.video = false;
|
||||
params.render_audio = render_audio;
|
||||
params.video = false;
|
||||
params.gizmos = nullptr;
|
||||
params.rendering = audio_rendering;
|
||||
params.single_threaded = audio_rendering;
|
||||
params.playback_speed = playback_speed;
|
||||
params.blend_mode_program = nullptr;
|
||||
compose_sequence(params);
|
||||
|
||||
+201
-6
@@ -30,31 +30,226 @@ class QOpenGLShaderProgram;
|
||||
struct Sequence;
|
||||
class Clip;
|
||||
|
||||
/**
|
||||
* @brief The ComposeSequenceParams struct
|
||||
*
|
||||
* Struct sent to the compose_sequence() function.
|
||||
*/
|
||||
struct ComposeSequenceParams {
|
||||
|
||||
/**
|
||||
* @brief Reference to the Viewer class that's calling compose_sequence()
|
||||
*
|
||||
* Primarily used for calling Viewer::play_wake() when appropriate.
|
||||
*/
|
||||
Viewer* viewer;
|
||||
|
||||
/**
|
||||
* @brief The OpenGL context to use while rendering.
|
||||
*
|
||||
* For video rendering, this must be a valid OpenGL context. For audio, this variable is never accessed.
|
||||
*
|
||||
* \see ComposeSequenceParams::video
|
||||
*/
|
||||
QOpenGLContext* ctx;
|
||||
|
||||
/**
|
||||
* @brief The sequence to compose
|
||||
*
|
||||
* In addition to clips, sequences also contain the playhead position so compose_sequence() knows which frame
|
||||
* to render.
|
||||
*/
|
||||
Sequence* seq;
|
||||
|
||||
/**
|
||||
* @brief Array to store the nested sequence hierarchy
|
||||
*
|
||||
* Should be left empty. This array gets passed around compose_sequence() as it calls itself recursively to
|
||||
* handle nested sequences.
|
||||
*/
|
||||
QVector<Clip*> nests;
|
||||
bool video;
|
||||
bool render_audio;
|
||||
|
||||
/**
|
||||
* @brief Set compose mode to video or audio
|
||||
*
|
||||
* **TRUE** if this function should render video, **FALSE** if this function should render audio.
|
||||
*/
|
||||
bool video;
|
||||
|
||||
/**
|
||||
* @brief Set to the Effect whose gizmos were chosen to be drawn on screen
|
||||
*
|
||||
* A pointer to a pointer that will be set to the Effect whose gizmos are being rendered and should therefore
|
||||
* be interacted with if the user uses them.
|
||||
*/
|
||||
Effect** gizmos;
|
||||
|
||||
/**
|
||||
* @brief A variable that compose_sequence() will set to **TRUE** if any of the clips couldn't be shown.
|
||||
*
|
||||
* A footage item or shader may not be ready at the time this frame is drawn. If compose_sequence() couldn't draw
|
||||
* any of the clips in the scene, this variable is set to **TRUE** indicating that the image rendered is a
|
||||
* "best effort", but not the actual image.
|
||||
*
|
||||
* This variable should be checked after compose_sequence() and a repaint should be triggered if it's **TRUE**.
|
||||
*
|
||||
* \note This variable is probably bad design and is a relic of an earlier rendering backend. There may be a better
|
||||
* way to communicate this information.
|
||||
*
|
||||
* Additionally, since
|
||||
* compose_sequence() for video will now always run in a separate thread anyway, there's no real issue with
|
||||
* stalling it to wait for footage to complete opening or whatever may be lagging behind. A possible side effect
|
||||
* of this though is that the preview may become less responsive if it's stuck trying to render one frame. With
|
||||
* the current system, the preview may show incomplete frames occasionally but at least it will show something.
|
||||
* This may be preferable. See ComposeSequenceParams::single_threaded for a similar function that could be
|
||||
* removed.
|
||||
*/
|
||||
bool texture_failed;
|
||||
bool rendering;
|
||||
|
||||
/**
|
||||
* @brief Run all cachers in the same thread that compose_sequence() is in
|
||||
*
|
||||
* Standard behavior is that all clips cache frames in their own thread and signals are sent between
|
||||
* compose_sequence() and the clip's cacher thread regarding which frames to display and cache without stalling
|
||||
* the compose_sequence() thread. Setting this to **TRUE** will run all cachers in the same thread creating a
|
||||
* technically more "perfect" connection between them that will also stall the compose_sequence() thread. Used
|
||||
* when rendering as timing isn't as important as creating output frames as quickly as possible.
|
||||
*
|
||||
* \note Exporting should probably be rewritten without this. While running all the cachers in one thread makes
|
||||
* it easier to synchronize everything, export performance could probably benefit from keeping them in separate
|
||||
* threads and syncing up with them. See ComposeSequenceParams::texture_failed for a similar function that could
|
||||
* be removed.
|
||||
*/
|
||||
bool single_threaded;
|
||||
|
||||
/**
|
||||
* @brief Set the current playback speed (adjusted with Shuttle Left/Right)
|
||||
*
|
||||
* Only used for audio rendering to determine how many samples to skip in order to play audio at the correct speed.
|
||||
*
|
||||
* \see ComposeSequenceParams::video
|
||||
*/
|
||||
int playback_speed;
|
||||
|
||||
/**
|
||||
* @brief Blending mode shader
|
||||
*
|
||||
* Used only for video rendering. Never accessed with audio rendering.
|
||||
*
|
||||
* A program containing the current active
|
||||
* blending mode shader that can be bound during rendering. Must be compiled and linked beforehand. See
|
||||
* RenderThread::blend_mode_program for how this is properly set up.
|
||||
*
|
||||
* \see ComposeSequenceParams::video
|
||||
*/
|
||||
QOpenGLShaderProgram* blend_mode_program;
|
||||
|
||||
/**
|
||||
* @brief Premultiply alpha shader
|
||||
*
|
||||
* Used only for video rendering. Never accessed with audio rendering.
|
||||
*
|
||||
* compose_sequence()'s internal composition
|
||||
* expects premultipled alpha, but it will pre-emptively multiply any footage that is not set as already
|
||||
* premultiplied (see Footage::alpha_is_premultiplied) using this shader. Must be compiled and linked beforehand.
|
||||
* See RenderThread::premultiply_program for how this is properly set up.
|
||||
*/
|
||||
QOpenGLShaderProgram* premultiply_program;
|
||||
|
||||
/**
|
||||
* @brief The OpenGL framebuffer object that the final texture to be shown is rendered to.
|
||||
*
|
||||
* Used only for video rendering. Never accessed with audio rendering.
|
||||
*
|
||||
* When compose_sequence() is rendering the final image, this framebuffer will be bound.
|
||||
*/
|
||||
GLuint main_buffer;
|
||||
|
||||
/**
|
||||
* @brief The attachment to the framebuffer in main_buffer
|
||||
*
|
||||
* Used only for video rendering. Never accessed with audio rendering.
|
||||
*
|
||||
* The OpenGL texture attached to the framebuffer referenced by main_buffer.
|
||||
*/
|
||||
GLuint main_attachment;
|
||||
|
||||
/**
|
||||
* @brief Backend OpenGL framebuffer 1 used for further processing before rendering to main_buffer
|
||||
*
|
||||
* In some situations, compose_sequence() will do some processing through shaders that requires "ping-ponging"
|
||||
* between framebuffers. backend_buffer1 and backend_buffer2 are used for this purpose.
|
||||
*/
|
||||
GLuint backend_buffer1;
|
||||
|
||||
/**
|
||||
* @brief Backend OpenGL framebuffer 1's texture attachment
|
||||
*
|
||||
* The texture that ComposeSequenceParams::backend_buffer1 renders to. Bound and drawn to
|
||||
* ComposeSequenceParams::backend_buffer2 to "ping-pong" between them and various shaders.
|
||||
*/
|
||||
GLuint backend_attachment1;
|
||||
|
||||
/**
|
||||
* @brief Backend OpenGL framebuffer 2 used for further processing before rendering to main_buffer
|
||||
*
|
||||
* In some situations, compose_sequence() will do some processing through shaders that requires "ping-ponging"
|
||||
* between framebuffers. backend_buffer1 and backend_buffer2 are used for this purpose.
|
||||
*/
|
||||
GLuint backend_buffer2;
|
||||
|
||||
/**
|
||||
* @brief Backend OpenGL framebuffer 2's texture attachment
|
||||
*
|
||||
* The texture that ComposeSequenceParams::backend_buffer2 renders to. Bound and drawn to
|
||||
* ComposeSequenceParams::backend_buffer1 to "ping-pong" between them and various shaders.
|
||||
*/
|
||||
GLuint backend_attachment2;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Compose a frame of a given sequence
|
||||
*
|
||||
* For any given Sequence, this function will render the current frame indicated by Sequence::playhead. Will
|
||||
* automatically open and close clips (memory allocation and file handles) as necessary, communicate with the
|
||||
* Clip::cacher objects to retrieve upcoming frames and store them in memory, run Effect processing functions, and
|
||||
* finally composite all the currently active clips together into a final texture.
|
||||
*
|
||||
* Will sometimes render a frame incomplete or inaccurately, e.g. if a video file hadn't finished opening by the time
|
||||
* of the render or a clip's cacher didn't have the requested frame available at the time of the render. If so,
|
||||
* the `texture_failed` variable of `params` will be set to **TRUE**. Check this after calling compose_sequence() and
|
||||
* if it is **TRUE**, compose_sequence() should be called again later to attempt another render (unless the Sequence
|
||||
* is being played, in which case just play the next frame rather than redrawing an old frame).
|
||||
*
|
||||
* @param params
|
||||
*
|
||||
* A struct of parameters to use while rendering.
|
||||
*
|
||||
* @return A reference to the OpenGL texture resulting from the render. Will usually be equal to
|
||||
* ComposeSequenceParams::main_attachment unless it's rendering a nested sequence, in which case it'll be a reference
|
||||
* to one of the textures referenced by Clip::fbo. Can be used directly to draw the rendered frame.
|
||||
*/
|
||||
GLuint compose_sequence(ComposeSequenceParams ¶ms);
|
||||
|
||||
void compose_audio(Viewer* viewer, Sequence* seq, bool render_audio, int playback_speed);
|
||||
|
||||
void viewport_render();
|
||||
/**
|
||||
* @brief Convenience wrapper function for compose_sequence() to render audio
|
||||
*
|
||||
* Much of the functionality provided (and parameters required) by compose_sequence() is only useful/necessary for
|
||||
* video rendering. For audio rendering, this function is easier to handle and will correctly set up
|
||||
* compose_sequence() to render audio without the cumbersome effort of setting up a ComposeSequenceParams object.
|
||||
*
|
||||
* @param viewer
|
||||
*
|
||||
* The Viewer object calling this function
|
||||
*
|
||||
* @param seq
|
||||
*
|
||||
* The Sequence whose audio to render.
|
||||
*
|
||||
* @param playback_speed
|
||||
*
|
||||
* The current playback speed (controlled by Shuttle Left/Right)
|
||||
*/
|
||||
void compose_audio(Viewer* viewer, Sequence* seq, int playback_speed);
|
||||
|
||||
#endif // RENDERFUNCTIONS_H
|
||||
|
||||
+2
-3
@@ -180,10 +180,9 @@ void RenderThread::paint() {
|
||||
params.ctx = ctx;
|
||||
params.seq = seq;
|
||||
params.video = true;
|
||||
params.texture_failed = false;
|
||||
params.render_audio = false;
|
||||
params.texture_failed = false;
|
||||
params.gizmos = &gizmos;
|
||||
params.rendering = false;
|
||||
params.single_threaded = false;
|
||||
params.playback_speed = 1;
|
||||
params.blend_mode_program = blend_mode_program;
|
||||
params.premultiply_program = premultiply_program;
|
||||
|
||||
@@ -364,7 +364,11 @@ void TimelineWidget::wheelEvent(QWheelEvent *event) {
|
||||
// shift used to toggle zooming instead of scrolling
|
||||
bool shift = (event->modifiers() & Qt::ShiftModifier);
|
||||
|
||||
if (!event->pixelDelta().isNull()) {
|
||||
//
|
||||
// NOTE/FIXME: CURRENTLY disabling pixel scrolling because it needs more testing
|
||||
//
|
||||
|
||||
/*if (!event->pixelDelta().isNull()) {
|
||||
// if we got pixel scrolling data, prefer it over the angleDelta data
|
||||
|
||||
QScrollBar* horiz_bar = panel_timeline->horizontalScrollBar;
|
||||
@@ -373,7 +377,7 @@ void TimelineWidget::wheelEvent(QWheelEvent *event) {
|
||||
horiz_bar->setValue(horiz_bar->value() + event->pixelDelta().x());
|
||||
vert_bar->setValue(vert_bar->value() + event->pixelDelta().y());
|
||||
|
||||
} else if (!event->angleDelta().isNull()) {
|
||||
} else*/ if (!event->angleDelta().isNull()) {
|
||||
|
||||
// alt is used to swap horizontal and vertical scrolling
|
||||
bool alt = (event->modifiers() & Qt::AltModifier);
|
||||
|
||||
+3
-5
@@ -235,9 +235,7 @@ void ViewerWidget::initializeGL() {
|
||||
}
|
||||
|
||||
void ViewerWidget::frame_update() {
|
||||
if (viewer->seq != nullptr) {
|
||||
bool render_audio = (viewer->playing || audio_rendering);
|
||||
|
||||
if (viewer->seq != nullptr) {
|
||||
// send context to other thread for drawing
|
||||
if (waveform) {
|
||||
update();
|
||||
@@ -247,7 +245,7 @@ void ViewerWidget::frame_update() {
|
||||
}
|
||||
|
||||
// render the audio
|
||||
compose_audio(viewer, viewer->seq, render_audio, viewer->get_playback_speed());
|
||||
compose_audio(viewer, viewer->seq, viewer->get_playback_speed());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -628,7 +626,7 @@ void ViewerWidget::paintGL() {
|
||||
|
||||
renderer->mutex.unlock();
|
||||
|
||||
if (renderer->did_texture_fail()) {
|
||||
if (renderer->did_texture_fail() && !viewer->playing) {
|
||||
doneCurrent();
|
||||
renderer->start_render(context(), viewer->seq);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user