diff --git a/app/render/rendermanager.h b/app/render/rendermanager.h index 325ba295a..39e01b01c 100644 --- a/app/render/rendermanager.h +++ b/app/render/rendermanager.h @@ -63,11 +63,11 @@ private: bool cancelled_; - Renderer *context_; + Renderer *context_ = nullptr; - DecoderCache *decoder_cache_; + DecoderCache *decoder_cache_ = nullptr; - ShaderCache *shader_cache_; + ShaderCache *shader_cache_ = nullptr; }; class RenderWorkerPool; @@ -239,21 +239,21 @@ private: static RenderManager *instance_; - Renderer *context_; + Renderer *context_ = nullptr; Backend backend_; Backend requested_backend_; - DecoderCache *decoder_cache_; + DecoderCache *decoder_cache_ = nullptr; - ShaderCache *shader_cache_; + ShaderCache *shader_cache_ = nullptr; static constexpr auto kDecoderMaximumInactivityAggressive = 1000; static constexpr auto kDecoderMaximumInactivity = 5000; - int aggressive_gc_; + int aggressive_gc_ = 0; - QTimer *decoder_clear_timer_; + QTimer *decoder_clear_timer_ = nullptr; RenderThread *dry_run_thread_ = nullptr; RenderThread *audio_thread_ = nullptr; @@ -263,9 +263,9 @@ private: std::list render_threads_; - PreviewAutoCacher *auto_cacher_; + PreviewAutoCacher *auto_cacher_ = nullptr; - RenderWorkerPool *worker_pool_; + RenderWorkerPool *worker_pool_ = nullptr; private slots: void ClearOldDecoders(); diff --git a/app/task/project/loadotio/loadotio.cpp b/app/task/project/loadotio/loadotio.cpp index 4b8eab907..1246b4439 100644 --- a/app/task/project/loadotio/loadotio.cpp +++ b/app/task/project/loadotio/loadotio.cpp @@ -94,6 +94,8 @@ bool LoadOTIOTask::Run() } else { // Unknown root, we don't know what to do with this SetError(tr("Unknown OpenTimelineIO root element")); + delete project_; + project_ = nullptr; return false; } diff --git a/app/widget/curvewidget/curveview.cpp b/app/widget/curvewidget/curveview.cpp index 25e608bf6..cc044ae91 100644 --- a/app/widget/curvewidget/curveview.cpp +++ b/app/widget/curvewidget/curveview.cpp @@ -91,7 +91,7 @@ void CurveView::SelectKeyframesOfInput(const NodeKeyframeTrackReference &ref) { DeselectAll(); - foreach (KeyframeViewInputConnection *con, track_connections_) { + if (KeyframeViewInputConnection *con = track_connections_.value(ref)) { foreach (NodeKeyframe *key, con->GetKeyframes()) { SelectKeyframe(key); } diff --git a/app/widget/timeruler/seekablewidget.cpp b/app/widget/timeruler/seekablewidget.cpp index cdeabc2f8..859d2824d 100644 --- a/app/widget/timeruler/seekablewidget.cpp +++ b/app/widget/timeruler/seekablewidget.cpp @@ -433,8 +433,9 @@ void SeekableWidget::SeekToScenePoint(qreal scene) playhead_time += movement; } - if (playhead_time != GetViewerNode()->GetPlayhead()) { - GetViewerNode()->SetPlayhead(playhead_time); + ViewerOutput *viewer = GetViewerNode(); + if (viewer && playhead_time != viewer->GetPlayhead()) { + viewer->SetPlayhead(playhead_time); } } diff --git a/tests/gtest/render_misc_test.cpp b/tests/gtest/render_misc_test.cpp index d16cff1db..aa9289199 100644 --- a/tests/gtest/render_misc_test.cpp +++ b/tests/gtest/render_misc_test.cpp @@ -712,3 +712,22 @@ TEST_F(RenderMiscAutoCacherTest, CacheProxyTaskCancelledClearsPendingJobs) cacher.SetProject(nullptr); } + +// With an unknown/dummy graphics backend the RenderManager never creates the +// GPU-side objects. Those pointers must be null (previously they were left +// uninitialized, so callers such as ViewerWidget dereferenced garbage and +// crashed). +TEST(RenderManagerDummyBackend, GpuMembersAreNullRatherThanUninitialized) +{ + const QVariant previous = + olive::Config::Current()[QStringLiteral("GraphicsBackend")]; + olive::Config::Current()[QStringLiteral("GraphicsBackend")] = + QStringLiteral("dummy"); + + olive::RenderManager::CreateInstance(); + + EXPECT_EQ(olive::RenderManager::instance()->GetCacher(), nullptr); + + olive::RenderManager::DestroyInstance(); + olive::Config::Current()[QStringLiteral("GraphicsBackend")] = previous; +} diff --git a/tests/gtest/widget_curve_keyframe_test.cpp b/tests/gtest/widget_curve_keyframe_test.cpp index 27b575cc7..031511e55 100644 --- a/tests/gtest/widget_curve_keyframe_test.cpp +++ b/tests/gtest/widget_curve_keyframe_test.cpp @@ -344,6 +344,34 @@ TEST_F(CurveViewTest, SetKeyframeTrackColorAppliesToBrush) EXPECT_EQ(connection->GetBrush().color(), QColor(Qt::blue)); } +TEST_F(CurveViewTest, SelectKeyframesOfInputSelectsOnlyRequestedTrack) +{ + class SelectionProbeCurveView : public CurveView { + public: + using KeyframeView::IsKeyframeSelected; + }; + + SelectionProbeCurveView view; + view.ConnectInput(ColorTrackRef(0)); + view.ConnectInput(ColorTrackRef(1)); + + NodeKeyframe *key0 = + InsertKeyframe(solid_, SolidGenerator::kColorInput, rational(0), 0.5, 0); + NodeKeyframe *key1 = + InsertKeyframe(solid_, SolidGenerator::kColorInput, rational(1), 0.6, 1); + + // Previously the reference was ignored and keyframes of every connected + // track got selected. + view.SelectKeyframesOfInput(ColorTrackRef(0)); + EXPECT_TRUE(view.IsKeyframeSelected(key0)); + EXPECT_FALSE(view.IsKeyframeSelected(key1)); + + // Selecting the other track replaces the selection (DeselectAll first) + view.SelectKeyframesOfInput(ColorTrackRef(1)); + EXPECT_FALSE(view.IsKeyframeSelected(key0)); + EXPECT_TRUE(view.IsKeyframeSelected(key1)); +} + TEST(CurveWidget, VerticalScaleRoundTripsThroughView) { ColorManager::SetUpDefaultConfig(); diff --git a/tests/gtest/widget_timeruler_playback_test.cpp b/tests/gtest/widget_timeruler_playback_test.cpp index 1c86594e9..b32010442 100644 --- a/tests/gtest/widget_timeruler_playback_test.cpp +++ b/tests/gtest/widget_timeruler_playback_test.cpp @@ -100,6 +100,20 @@ TEST(TimeRuler, SeekToScenePointWithoutTimebaseIsNoOp) SUCCEED(); } +TEST(TimeRuler, SeekToScenePointWithoutViewerIsNoOp) +{ + EnsureAppSingletons(); + + // Timebase set but no viewer connected: previously dereferenced a null + // GetViewerNode() and crashed. + TimeRuler ruler; + ruler.SetTimebase(rational(1, 30)); + ruler.SetScale(100.0); + + ruler.SeekToScenePoint(150.0); + SUCCEED(); +} + class PlaybackControlsTest : public ::testing::Test { protected: void SetUp() override