Mimir/docs/obs-stem-plugin.md
2026-06-08 21:01:48 +01:00

5.1 KiB

OBS Native Plugin

The production OBS path is a Mimir/Fensalir-owned program texture plus a Mimir/Fensalir-owned stem bus, exposed through a narrow native OBS plugin.

Authority

  • Fensalir owns the completed program pixels.
  • Mimir/Fensalir own timing, alignment, DSP, and stem identity.
  • OBS owns broadcast mixing and scene composition.
  • The plugin imports already-produced program pixels and reads already-processed stem samples. It does not synchronize raw sources, estimate clocks, render the field, or decide which payload is true.

Current Contract

Video

Fensalir publishes the completed backbuffer into a named shared D3D12 B8G8R8A8_UNorm texture when these environment variables are set:

FENSALIR_PROGRAM_OUTPUT_D3D12=1
FENSALIR_PROGRAM_OUTPUT_NAME=Global\MimirFensalirProgramTexture

The native OBS plugin in native/obs_stem_source exposes a Mimir Program Texture video source. Set:

Shared D3D12 texture name: Global\MimirFensalirProgramTexture
Shared D3D12 fence name: Global\MimirFensalirProgramFence
Consumer D3D12 fence name: Global\MimirObsProgramConsumerFence
Texture ring slots: 1

OBS/libobs is D3D11 internally on Windows, so this source opens the named D3D12 texture, creates an OBS-readable legacy shared D3D11 texture, opens that same bridge texture from D3D12, and performs a GPU-to-GPU copy during OBS render before drawing the source. This avoids CPU readback and does not require Spout2. It is not pure zero-copy yet; the remaining copy is the D3D12-to-libobs-D3D11 boundary. Fensalir also publishes a named D3D12 program-output fence; the OBS source opens it and observes publication completion before copying. This prevents blind read-before-producer-completion behavior without coupling OBS to Fensalir's private frame fence.

For OBS-only stress testing, Fensalir and the OBS source can agree on a bounded texture ring:

FENSALIR_PROGRAM_OUTPUT_RING_COUNT=3
FENSALIR_PROGRAM_OUTPUT_CONSUMER_FENCE_NAME=Global\MimirObsProgramConsumerFence
Texture ring slots: 3
Consumer D3D12 fence name: Global\MimirObsProgramConsumerFence

With a ring count above one, Fensalir publishes textures named Global\MimirFensalirProgramTexture.0, .1, and so on. The OBS source uses the program-output fence value to copy the latest completed slot and signals the consumer fence after its GPU copy. When Fensalir is launched with the matching consumer-fence environment variable, it will not reuse a ring slot until OBS has acknowledged a copy at or beyond that slot's published fence value. Leave the ring count at 1 for EVE until the EVE relay speaks the same ring contract.

For a visible interop witness while the real Mimir scene is sparse, launch Mimir with:

MIMIR_OBS_PROOF_VISUAL=1

That enables a small bright SDF/starfield proof visual. It is a debug witness, not production scene policy.

Audio

MimirObsStemSharedMemoryPublisher writes the latest validated MimirObsStemPublicationSnapshot into a Windows shared-memory map named Local\MimirObsStemBus by default. The native OBS plugin in native/obs_stem_source exposes a Mimir Audio Stem input source. Add one source per stem and set:

Shared memory map: Local\MimirObsStemBus
Stem id: aligned_source_0

Use aligned_source_0 through aligned_source_5 for the first actuator-stage source lanes.

Build

.\scripts\build-obs-stem-plugin.ps1

The script stages obsproject/obs-plugintemplate under artifacts/obs-sdk/, lets the upstream template fetch/build the matching OBS/libobs development surface, then builds native/obs_stem_source against that SDK. The staged SDK and native build products are local artifacts, not repository state.

The managed shared-memory publisher is verified by:

dotnet run --no-build --project .\src\Mimir.BufferSmoke\Mimir.BufferSmoke.csproj -- --obs-stem-shared-memory-smoke

The local native plugin build currently succeeds and emits:

native\obs_stem_source\build\Release\mimir_obs_stem_source.dll

That DLL currently registers both source types:

Mimir Program Texture
Mimir Audio Stem
Muninn Stream

Muninn Streams

The Muninn Stream source is a thin OBS wrapper over Muninn's stream catalog. It reads:

C:\Meta\Odin\state\muninn-obs-streams.tsv

Each catalog line is:

stream_id<TAB>label<TAB>url<TAB>state

Muninn owns that catalog as a compatibility lowering of its typed CultMesh/CultCache state. OBS owns only the selected scene source and media rendering. The source does not probe devices and does not activate streams by itself; use Muninn's explicit activation command before selecting a stream that requires capture resources.

Install

For the standard OBS plugin search path:

.\scripts\install-obs-mimir-plugin.ps1

By default this writes:

C:\ProgramData\obs-studio\plugins\mimir_obs_stem_source\bin\64bit\mimir_obs_stem_source.dll

For a portable OBS tree or direct OBS installation root:

.\scripts\install-obs-mimir-plugin.ps1 -ObsRoot D:\Tools\obs-studio

The script does not create scene items or alter OBS profiles.