vstrt_rtx with JIT cache and engine reuse - source
==================================================

Two changes on top of vs-mlrt master (commit 8cd6cf2), both inside
#ifdef TRT_MAJOR_RTX, so the regular vstrt build is untouched.

1) JIT runtime cache
   TensorRT-RTX compiles kernels when an execution context is created, not when
   the engine is deserialized. Measured on an RTX 5090 Laptop with RIFE 4.6 at
   1920x1088: deserialize 0.04 s, context creation 3.06 s for one stream,
   4.14 s for two, 8 s for four.
   The plugin now keeps NVIDIA's IRuntimeCache in "<engine path>.jit"
   (about 3 MB per engine) and passes it through IRuntimeConfig:

       config = engine->createRuntimeConfig();
       cache  = config->createRuntimeCache();
       cache->deserialize(blob, size);           // from <engine>.jit if present
       config->setRuntimeCache(*cache);
       ctx    = engine->createExecutionContext(config);
       ...
       cache->serialize();                       // written back after contexts exist

   One cache file per engine file on purpose: a single shared cache accumulates
   kernels for every engine and shape and eventually costs more to deserialize
   than compiling.
   Disable with VSTRT_RTX_NO_JIT_CACHE=1.

2) Engine reuse across filter rebuilds
   A player destroys its whole VapourSynth graph on every seek (mpv calls
   destroy_vs from vf_vapoursynth_reset), so the engine and its contexts were
   rebuilt each time. With the cache warm that still took about 2 s inside a
   player, because context creation contends with the playback already running
   on the device; the same two contexts take 0.19 s in an idle process.
   vsTrtFree now moves the engine, its contexts and the cache into a small pool
   (2 entries) instead of destroying them, and vsTrtCreate takes them back when
   the key matches: engine path plus the file's size and modification time (a
   rebuilt engine never matches), device id, stream count, CUDA graph flag,
   flexible output property, node count, input width and height, tilesize.
   Disable with VSTRT_RTX_NO_REUSE=1.

   The pool is a static of the plugin module, and VapourSynth unloads plugins
   with the core, so the module is pinned (GetModuleHandleEx with
   GET_MODULE_HANDLE_EX_FLAG_PIN) and the pool is only active when that pin
   succeeded. That makes the pool Windows only for now; elsewhere the plugin
   behaves as stock. The pool is deliberately never destroyed at process exit:
   static destructors run after the CUDA driver has shut down, and releasing
   contexts there crashed VSPipe on exit (exit code 127 in the first version).

   Cost: the pooled engine and its per-stream device memory stay allocated
   until the entry is evicted or the process exits. Measured for RIFE 4.6 at
   1920x1088 with 2 streams: freeing the filter releases 464 MiB without the
   pool and 0 with it. Closing the player frees everything either way.

VSTRT_RTX_TIMING=1 prints where the load time goes (deserialize + cache load,
context creation, cache save, pool hits).

Seek time in mpv, same clip and model, measured seek command to playback-restart
from inside mpv: 7.7 s stock, 3.1 s with the cache only, 0.45 s with both.
The regular TensorRT backend is 0.9 s for reference.

Files
-----
vstrt_rtx_jitcache.patch   diff against vs-mlrt master 8cd6cf2
vstrt/trt_utils.h          patched file
vstrt/vs_tensorrt.cpp      patched file
build.cmd                  how the DLL was built (adjust the paths at the top)

The build needs no CUDA toolkit install: VapourSynth headers, the TensorRT-RTX
SDK, and CUDA headers plus cudart_static.lib from the nvidia-cuda-runtime-cu12
wheel (its crt\ headers come from the nvidia-cuda-nvcc-cu12 wheel).
