Skip to content

Commit 33516e8

Browse files
committed
feat: tensorrt engine building progress text & safety
1 parent 0b229d0 commit 33516e8

8 files changed

Lines changed: 203 additions & 45 deletions

File tree

.gitignore

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,20 @@ out/
2222

2323
# cmake
2424
compile_commands.json
25+
CMakeCache.txt
26+
CMakeFiles/
27+
cmake_install.cmake
28+
CTestTestfile.cmake
29+
DartConfiguration.tcl
30+
*_include.cmake
31+
build.ninja
32+
.ninja_deps
33+
.ninja_log
34+
*.a
35+
36+
# vcpkg
37+
vcpkg_installed/
38+
vcpkg-manifest-install.log
2539

2640
# Keep .gitkeep files
2741
!.gitkeep

src/common/common_pch.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include <ranges>
2424
#include <cfloat>
2525
#include <csignal>
26+
#include <deque>
2627

2728
// libs
2829
#include <nlohmann/json.hpp>

src/common/rendering/render_pipeline.cpp

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,15 @@ namespace {
5858

5959
if (progress_callback)
6060
progress_callback();
61-
62-
line.clear();
61+
}
62+
else {
63+
// not a frame update - e.g. a \r-terminated status line from a
64+
// TensorRT engine build. surface it as a log line instead of
65+
// silently gluing it onto whatever comes next.
66+
state->report_log_line(line);
6367
}
6468

69+
line.clear();
6570
continue;
6671
}
6772

@@ -70,6 +75,8 @@ namespace {
7075

7176
DEBUG_LOG("[vspipe error] {}", line);
7277

78+
state->report_log_line(line);
79+
7380
line.clear();
7481
continue;
7582
}
@@ -80,6 +87,7 @@ namespace {
8087
if (!line.empty()) {
8188
vspipe_errors << line << '\n';
8289
DEBUG_LOG("[vspipe error] {}", line);
90+
state->report_log_line(line);
8391
}
8492
}
8593

@@ -199,14 +207,22 @@ tl::expected<rendering::detail::PipelineResult, rendering::RenderError> renderin
199207
std::thread ffmpeg_stderr_thread(pump_ffmpeg_stderr, std::ref(ffmpeg_stderr), std::ref(ffmpeg_errors));
200208
std::thread ffmpeg_stdout_thread(extract_jpeg_stream, std::ref(ffmpeg_stdout), state);
201209

210+
// vspipe's tensorrt backend shells out to trtexec as a *grandchild* process
211+
// (blur.py -> vsmlrt.py -> subprocess.run). a plain child.terminate() only
212+
// kills vspipe itself, leaving trtexec running an engine build in the
213+
// background after cancel. put vspipe in a process group (job object on
214+
// windows) so terminating the group takes the whole tree down with it.
215+
bp::group vspipe_group;
216+
202217
auto vspipe_process = u::run_command(
203218
blur.vspipe_path,
204219
commands.vspipe_video,
205220
env,
206221
bp::std_out > vspipe_stdout,
207222
bp::std_err > vspipe_stderr,
208-
bp::std_in < bp::null // stdin is an invalid handle otherwise, which breaks
209-
// subprocess.run(stdout=sys.stderr) in rife-trt (FUN!)
223+
bp::std_in < bp::null, // stdin is an invalid handle otherwise, which breaks
224+
// subprocess.run(stdout=sys.stderr) in rife-trt (FUN!)
225+
vspipe_group
210226
);
211227

212228
auto ffmpeg_process =
@@ -226,7 +242,12 @@ tl::expected<rendering::detail::PipelineResult, rendering::RenderError> renderin
226242
bool killed = false;
227243
while (ffmpeg_process.running()) {
228244
if (state->wants_stop()) {
229-
vspipe_process.terminate();
245+
// non-throwing: terminate() invalidates the group handle, and we
246+
// terminate it again unconditionally below - a throwing call there
247+
// on an already-invalid handle would escape as a generic error
248+
// instead of a clean "stopped" result.
249+
std::error_code ec;
250+
vspipe_group.terminate(ec);
230251
ffmpeg_process.terminate();
231252
killed = true;
232253
break;
@@ -240,8 +261,11 @@ tl::expected<rendering::detail::PipelineResult, rendering::RenderError> renderin
240261
std::this_thread::sleep_for(std::chrono::milliseconds(50));
241262
}
242263

243-
// stop stuff if they're stuck
244-
vspipe_process.terminate();
264+
// stop stuff if they're stuck (no-op if already terminated above)
265+
{
266+
std::error_code ec;
267+
vspipe_group.terminate(ec);
268+
}
245269

246270
// wait for threads to finish
247271
if (ffmpeg_stdout_thread.joinable())

src/common/rendering/render_state.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,26 @@
11
#include "render_state.h"
22

3+
namespace {
4+
constexpr size_t MAX_LOG_LINES = 6;
5+
6+
// printed by vsmlrt.py right before it shells out to trtexec/tensorrt_rtx
7+
constexpr std::string_view ENGINE_BUILD_SENTINEL = "[blur] Building TensorRT engine";
8+
}
9+
10+
void rendering::RenderState::report_log_line(const std::string& line) {
11+
if (line.empty())
12+
return;
13+
14+
std::lock_guard lock(m_mutex);
15+
16+
if (line.find(ENGINE_BUILD_SENTINEL) != std::string::npos)
17+
m_progress.building_engine = true;
18+
19+
m_progress.recent_log_lines.push_back(line);
20+
if (m_progress.recent_log_lines.size() > MAX_LOG_LINES)
21+
m_progress.recent_log_lines.pop_front();
22+
}
23+
324
void rendering::RenderState::report_frame_progress(int current_frame, int total_frames) {
425
std::lock_guard lock(m_mutex);
526

src/common/rendering/render_state.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,14 @@ namespace rendering {
1818
float fps = 0.f;
1919

2020
std::string string;
21+
22+
// set once a one-time TensorRT engine build is detected happening in place
23+
// of frame rendering (see RenderState::report_log_line)
24+
bool building_engine = false;
25+
26+
// tail of raw vspipe/child-process stderr lines, shown to the user while
27+
// there's no frame progress yet (e.g. during an engine build)
28+
std::deque<std::string> recent_log_lines;
2129
};
2230

2331
// -- control (called from the UI thread) --
@@ -71,6 +79,10 @@ namespace rendering {
7179
// fold a vspipe "Frame: n/m" update into progress + the status string
7280
void report_frame_progress(int current_frame, int total_frames);
7381

82+
// record a raw stderr line for display while there's no frame progress yet;
83+
// also flips building_engine on when the known TensorRT build sentinel appears
84+
void report_log_line(const std::string& line);
85+
7486
// -- preview frames (jpeg piped out of ffmpeg) --
7587

7688
void enable_preview_capture() {

src/gui/components/main.cpp

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,11 +215,37 @@ void main::render_progress(
215215
ui::add_text(
216216
"initialising render text",
217217
container,
218-
"Initialising render...",
218+
progress.building_engine ? "Building TensorRT engine..." : "Initialising render...",
219219
gfx::Color::white(),
220220
fonts::dejavu,
221221
FONT_CENTERED_X
222222
);
223+
224+
if (progress.building_engine) {
225+
ui::add_text(
226+
"initialising render subtext",
227+
container,
228+
"this only happens once per settings, and may take a few minutes",
229+
gfx::Color::white(renderer::MUTED_SHADE),
230+
fonts::dejavu,
231+
FONT_CENTERED_X
232+
);
233+
}
234+
235+
if (!progress.recent_log_lines.empty()) {
236+
container.push_element_gap(6);
237+
238+
for (const auto [i, log_line] : u::enumerate(progress.recent_log_lines)) {
239+
ui::add_text(
240+
std::format("initialising render log line {}", i),
241+
container,
242+
u::truncate_with_ellipsis(log_line, 90),
243+
gfx::Color::white(renderer::MUTED_SHADE),
244+
fonts::dejavu,
245+
FONT_CENTERED_X
246+
);
247+
}
248+
}
223249
}
224250
}
225251
}

src/vapoursynth/blur/interpolate.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,19 @@
55

66
import json
77
import math
8+
import sys
89
from fractions import Fraction
910
from typing import Any, Callable
1011
from pathlib import Path
1112

1213
import blur.utils as u
1314

14-
from external.vsmlrt import RIFE as VSMLRT_RIFE, BackendV2, RIFEModel
15+
# vsmlrt's plugin filters (trt/ort/ncnn/etc.) aren't available on macOS, and
16+
# importing it there raises "vsmlrt: cannot load any filters" at import time
17+
if sys.platform in ("win32", "linux"):
18+
from external.vsmlrt import RIFE as VSMLRT_RIFE, BackendV2, RIFEModel
19+
else:
20+
VSMLRT_RIFE = BackendV2 = RIFEModel = None
1521

1622
LEGACY_PRESETS = ["weak", "film", "smooth", "animation"]
1723
NEW_PRESETS = ["default", "test"]
@@ -293,6 +299,11 @@ def prepare_rife_vsmlrt(
293299
device_index: int,
294300
override_format: str | None = None,
295301
):
302+
if VSMLRT_RIFE is None:
303+
raise u.BlurException(
304+
"RIFE (TensorRT) is only supported on Windows and Linux."
305+
)
306+
296307
pad_mult: int | None = None
297308
target_format = vs.RGBH
298309

0 commit comments

Comments
 (0)