Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
8f8438f
Add video encode/decode round-trip sample
BjornSAIM May 5, 2026
c6c5ece
Update video sample dependencies
BjornSAIM May 5, 2026
85b03c2
Use upstream NRIFramework submodule URL
BjornSAIM May 5, 2026
cb6cf7a
Use simplified NRI video API
BjornSAIM May 6, 2026
cabd90d
Choose video bitstream memory per backend
BjornSAIM May 6, 2026
e37bf1d
Use readback encode bitstream buffers
BjornSAIM May 6, 2026
e90cfcd
Update video sample for queue selection cleanup
BjornSAIM May 6, 2026
47f6bb7
Update video encode decode sample
BjornSAIM May 7, 2026
91f66f9
Fix D3D12 AV1 decode preview
BjornSAIM May 7, 2026
e29fbcb
Keep AV1 decode info backend neutral
BjornSAIM May 7, 2026
43a0e6f
Use NRI AV1 metadata without header readback
BjornSAIM May 7, 2026
119ae0c
Exercise video EOS helper in encode sample
BjornSAIM May 21, 2026
c57ef45
Use video capability helpers in sample
BjornSAIM May 21, 2026
3d698c8
fix(video): stabilize codec sample
BjornSAIM May 28, 2026
cfe03bf
fix(video): refresh AV1 P preview
BjornSAIM May 28, 2026
fb83ed4
Fix Vulkan video sample decode preview
BjornSAIM Jun 13, 2026
8623cb9
Refactor video encode decode sample
BjornSAIM Jun 21, 2026
02513a3
Enhance video encode decode sample
BjornSAIM Jun 21, 2026
bec4947
Update NRIFramework video TODO pointer
BjornSAIM Jun 21, 2026
936723e
Improve video encode sample controls
BjornSAIM Jun 21, 2026
fc5f044
Improve video encode smoke coverage
BjornSAIM Jun 21, 2026
eb6895a
Update NRIFramework video TODO
BjornSAIM Jun 21, 2026
b87fef3
Update NRIFramework video validation
BjornSAIM Jun 21, 2026
ecb9738
Require video roundtrip in smoke
BjornSAIM Jun 21, 2026
087ffc0
Update NRIFramework Vulkan video cleanup
BjornSAIM Jun 21, 2026
50dbccf
Fix video encode smoke issues
BjornSAIM Jun 21, 2026
dfecdb1
Update NRIFramework video fixes
BjornSAIM Jun 21, 2026
097b51b
Update NRIFramework video queues
BjornSAIM Jun 23, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ _Build/
_Shaders/
_Data/
_Tmp/
_Smoke/
pso_cache.bin

# can be a symbolic link
Expand Down
13 changes: 13 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,19 @@ add_sample(Resources c)
add_sample(SceneViewer cpp)
add_sample(Triangle cpp)

add_sample(VideoEncodeDecode cpp)
target_sources(VideoEncodeDecode PRIVATE
Source/VideoEncodeDecode/Decoder.cpp
Source/VideoEncodeDecode/Decoder.h
Source/VideoEncodeDecode/Encoder.cpp
Source/VideoEncodeDecode/Encoder.h
Source/VideoEncodeDecode/Shared.h
)

if(WIN32)
target_link_libraries(VideoEncodeDecode PRIVATE d3d12)
endif()

# Wrapper depends on Vulkan SDK availability
if(DEFINED ENV{VULKAN_SDK})
add_sample(Wrapper cpp)
Expand Down
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,5 +57,13 @@ The executables from `_Bin` directory load resources from `_Data`, therefore the
- Resources - various resources allocation related stuff
- SceneViewer - loading & rendering of meshes with materials (also tests programmable sample locations, shading rate and pipeline statistics)
- Triangle - simple textured triangle rendering (also multiview demonstration in _FLEXIBLE_ mode)
- VideoEncodeDecode - H.264/H.265/AV1 NV12 video encode/decode round trip; built only when the selected NRI source provides the NRIVideo extension
- Wrapper - shows how to wrap native D3D11/D3D12/VK objects into *NRI* entities

### VideoEncodeDecode AV1 notes

`VideoEncodeDecode --codec=AV1 --av1Frame=P` encodes a static generated source image as an IDR reference followed by a P frame. The source is intentionally frozen for this permutation so the left generated image and right decoded image can be compared directly while validating AV1 reference/DPB handling. It is not intended to demonstrate animated AV1 P-frame motion.

The framework `--width` and `--height` arguments control the sample output window size only. Use `--videoWidth` and `--videoHeight` to select the NV12 encode/decode surface size. The default video size is 1920x1080.

The sample uses CQP rate control by default. Use `--qpI`, `--qpP`, and `--qpB` to change the per-frame-type quantizers, and `--av1BaseQIndex` to change AV1's frame base quantizer. H.264/H.265 QP values must be in the 0..51 range; AV1 quantizer values use the 0..255 range. Lower values generally produce higher quality and larger bitstreams.
231 changes: 231 additions & 0 deletions Scripts/SmokeVideoEncodeDecode.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
@echo off
setlocal EnableExtensions EnableDelayedExpansion

set "DURATION_SEC=6"
set "WINDOW_WIDTH=3840"
set "WINDOW_HEIGHT=2160"
set "VIDEO_WIDTH=1280"
set "VIDEO_HEIGHT=720"
set "APIS=D3D12"
set "CASE_FILTER="
set "NO_BUILD=0"
set "INCLUDE_DEBUG=0"
set "INCLUDE_VULKAN=0"

call :ParseArgs %*

set "SCRIPT_DIR=%~dp0"
for %%I in ("%SCRIPT_DIR%..") do set "REPO_ROOT=%%~fI"
set "EXE_PATH=%REPO_ROOT%\_Bin\Release\VideoEncodeDecode.exe"

set "TS=%DATE%_%TIME%"
set "TS=%TS:/=%"
set "TS=%TS:.=%"
set "TS=%TS::=%"
set "TS=%TS:,=%"
set "TS=%TS: =0%"
set "TIMESTAMP=%TS%"
set "OUT_DIR=%REPO_ROOT%\_Smoke\VideoEncodeDecode_%TIMESTAMP%"
set "LOG_PATH=%OUT_DIR%\VideoEncodeDecodeSmoke.log"
mkdir "%OUT_DIR%" >nul 2>nul

if "%NO_BUILD%"=="0" (
call :WriteLog "Building VideoEncodeDecode Release target..."
cmake --build "%REPO_ROOT%\_Build" --config Release --target VideoEncodeDecode > "%OUT_DIR%\build.tmp.log" 2> "%OUT_DIR%\build.tmp.err"
set "BUILD_EXIT=!ERRORLEVEL!"
type "%OUT_DIR%\build.tmp.log" >> "%LOG_PATH%" 2>nul
type "%OUT_DIR%\build.tmp.err" >> "%LOG_PATH%" 2>nul
del "%OUT_DIR%\build.tmp.log" "%OUT_DIR%\build.tmp.err" >nul 2>nul
if not "!BUILD_EXIT!"=="0" (
call :WriteLog "BUILD FAILED: exit code !BUILD_EXIT!"
exit /b !BUILD_EXIT!
)
)

if not exist "%EXE_PATH%" (
call :WriteLog "Executable not found: %EXE_PATH%"
exit /b 1
)

if "%INCLUDE_VULKAN%"=="1" call :AddApi VULKAN

set "CASE_COUNT=0"
set "PASS_COUNT=0"
set "FAIL_COUNT=0"

call :WriteLog "VideoEncodeDecode smoke started"
echo %DATE% %TIME% Output directory: %OUT_DIR%
>> "%LOG_PATH%" echo %DATE% %TIME% Output directory: %OUT_DIR%
call :WriteLog "Duration per run: %DURATION_SEC% seconds"
call :WriteLog "Window: %WINDOW_WIDTH%x%WINDOW_HEIGHT%, video: %VIDEO_WIDTH%x%VIDEO_HEIGHT%"
echo %DATE% %TIME% APIs: %APIS%
>> "%LOG_PATH%" echo %DATE% %TIME% APIs: %APIS%

for %%A in (%APIS%) do (
for %%C in (H264 H265) do (
for %%F in (IDR P B) do (
call :MaybeRunCase "%%A-%%C-%%F-cqp" %%A %%C h26Frame %%F cqp 0
call :MaybeRunCase "%%A-%%C-%%F-lossless" %%A %%C h26Frame %%F lossless 1
)
)

for %%F in (IDR P) do (
call :MaybeRunCase "%%A-AV1-%%F-cqp" %%A AV1 av1Frame %%F cqp 0
call :MaybeRunCase "%%A-AV1-%%F-near-lossless" %%A AV1 av1Frame %%F near-lossless 1
)
)

if not "%CASE_FILTER%"=="" (
call :WriteLog "Case filter: %CASE_FILTER%"
call :WriteLog "Filtered cases: %CASE_COUNT%"
if "%CASE_COUNT%"=="0" (
call :WriteLog "No cases matched filter"
exit /b 1
)
)

call :WriteLog ""
call :WriteLog "VideoEncodeDecode smoke finished: %PASS_COUNT% passed, %FAIL_COUNT% failed"
call :WriteLog "Log: %LOG_PATH%"

if not "%FAIL_COUNT%"=="0" exit /b 1
exit /b 0

::========================================================================================
:ParseArgs
if "%~1"=="" exit /b 0
if /I "%~1"=="--videoWidth" set "VIDEO_WIDTH=%~2" & shift /1 & shift /1 & goto :ParseArgs
if /I "%~1"=="--videoHeight" set "VIDEO_HEIGHT=%~2" & shift /1 & shift /1 & goto :ParseArgs
set "ARG=%~1"

if /I "!ARG!"=="-NoBuild" set "NO_BUILD=1" & shift /1 & goto :ParseArgs
if /I "!ARG!"=="--no-build" set "NO_BUILD=1" & shift /1 & goto :ParseArgs
if /I "!ARG!"=="-IncludeVulkan" set "INCLUDE_VULKAN=1" & shift /1 & goto :ParseArgs
if /I "!ARG!"=="--include-vulkan" set "INCLUDE_VULKAN=1" & shift /1 & goto :ParseArgs
if /I "!ARG!"=="-IncludeDebug" set "INCLUDE_DEBUG=1" & shift /1 & goto :ParseArgs
if /I "!ARG!"=="--include-debug" set "INCLUDE_DEBUG=1" & shift /1 & goto :ParseArgs

if /I "!ARG!"=="-DurationSec" set "DURATION_SEC=%~2" & shift /1 & shift /1 & goto :ParseArgs
if /I "!ARG!"=="--duration" set "DURATION_SEC=%~2" & shift /1 & shift /1 & goto :ParseArgs
if /I "!ARG!"=="--durationSec" set "DURATION_SEC=%~2" & shift /1 & shift /1 & goto :ParseArgs
if /I "!ARG!"=="-WindowWidth" set "WINDOW_WIDTH=%~2" & shift /1 & shift /1 & goto :ParseArgs
if /I "!ARG!"=="--width" set "WINDOW_WIDTH=%~2" & shift /1 & shift /1 & goto :ParseArgs
if /I "!ARG!"=="--windowWidth" set "WINDOW_WIDTH=%~2" & shift /1 & shift /1 & goto :ParseArgs
if /I "!ARG!"=="-WindowHeight" set "WINDOW_HEIGHT=%~2" & shift /1 & shift /1 & goto :ParseArgs
if /I "!ARG!"=="--height" set "WINDOW_HEIGHT=%~2" & shift /1 & shift /1 & goto :ParseArgs
if /I "!ARG!"=="--windowHeight" set "WINDOW_HEIGHT=%~2" & shift /1 & shift /1 & goto :ParseArgs
if /I "!ARG!"=="-VideoWidth" set "VIDEO_WIDTH=%~2" & shift /1 & shift /1 & goto :ParseArgs
if /I "!ARG!"=="--videoWidth" set "VIDEO_WIDTH=%~2" & shift /1 & shift /1 & goto :ParseArgs
if /I "!ARG!"=="-VideoHeight" set "VIDEO_HEIGHT=%~2" & shift /1 & shift /1 & goto :ParseArgs
if /I "!ARG!"=="--videoHeight" set "VIDEO_HEIGHT=%~2" & shift /1 & shift /1 & goto :ParseArgs
if /I "!ARG!"=="-Apis" set "APIS=%~2" & shift /1 & shift /1 & goto :ParseArgs
if /I "!ARG!"=="--apis" set "APIS=%~2" & shift /1 & shift /1 & goto :ParseArgs
if /I "!ARG!"=="-CaseFilter" set "CASE_FILTER=%~2" & shift /1 & shift /1 & goto :ParseArgs
if /I "!ARG!"=="--filter" set "CASE_FILTER=%~2" & shift /1 & shift /1 & goto :ParseArgs
if /I "!ARG!"=="--caseFilter" set "CASE_FILTER=%~2" & shift /1 & shift /1 & goto :ParseArgs

call :ParseEquals "%ARG%"
shift /1
goto :ParseArgs

::========================================================================================
:ParseEquals
set "ARG=%~1"
for /f "tokens=1,* delims==" %%K in ("!ARG!") do (
set "KEY=%%K"
set "VALUE=%%L"
)
if /I "!KEY!"=="--duration" set "DURATION_SEC=!VALUE!"
if /I "!KEY!"=="--durationSec" set "DURATION_SEC=!VALUE!"
if /I "!KEY!"=="-DurationSec" set "DURATION_SEC=!VALUE!"
if /I "!KEY!"=="--width" set "WINDOW_WIDTH=!VALUE!"
if /I "!KEY!"=="--windowWidth" set "WINDOW_WIDTH=!VALUE!"
if /I "!KEY!"=="-WindowWidth" set "WINDOW_WIDTH=!VALUE!"
if /I "!KEY!"=="--height" set "WINDOW_HEIGHT=!VALUE!"
if /I "!KEY!"=="--windowHeight" set "WINDOW_HEIGHT=!VALUE!"
if /I "!KEY!"=="-WindowHeight" set "WINDOW_HEIGHT=!VALUE!"
if /I "!KEY!"=="--videoWidth" set "VIDEO_WIDTH=!VALUE!"
if /I "!KEY!"=="-VideoWidth" set "VIDEO_WIDTH=!VALUE!"
if /I "!KEY!"=="--videoHeight" set "VIDEO_HEIGHT=!VALUE!"
if /I "!KEY!"=="-VideoHeight" set "VIDEO_HEIGHT=!VALUE!"
if /I "!KEY!"=="--apis" set "APIS=!VALUE!"
if /I "!KEY!"=="-Apis" set "APIS=!VALUE!"
if /I "!KEY!"=="--filter" set "CASE_FILTER=!VALUE!"
if /I "!KEY!"=="--caseFilter" set "CASE_FILTER=!VALUE!"
if /I "!KEY!"=="-CaseFilter" set "CASE_FILTER=!VALUE!"
exit /b 0

::========================================================================================
:AddApi
echo %APIS% | findstr /I /C:"%~1" >nul
if errorlevel 1 set "APIS=%APIS% %~1"
exit /b 0

::========================================================================================
:WriteLog
echo %DATE% %TIME% %~1
>> "%LOG_PATH%" echo %DATE% %TIME% %~1
exit /b 0

::========================================================================================
:MaybeRunCase
set "CASE_NAME=%~1"
if not "%CASE_FILTER%"=="" (
echo %CASE_NAME% | findstr /I /R /C:"%CASE_FILTER%" >nul
if errorlevel 1 exit /b 0
)

call :RunCase %*
exit /b 0

::========================================================================================
:RunCase
set "CASE_NAME=%~1"
set "API=%~2"
set "CODEC=%~3"
set "FRAME_ARG_NAME=%~4"
set "FRAME_ARG_VALUE=%~5"
set "SUFFIX=%~6"
set "LOSSLESS=%~7"

set /a CASE_COUNT+=1
set "CASE_OUT=%OUT_DIR%\%CASE_NAME%.stdout.tmp"
set "CASE_ERR=%OUT_DIR%\%CASE_NAME%.stderr.tmp"

set "CASE_ARGS=--api=%API% --width=%WINDOW_WIDTH% --height=%WINDOW_HEIGHT% --videoWidth=%VIDEO_WIDTH% --videoHeight=%VIDEO_HEIGHT% --codec=%CODEC% --timeLimit=%DURATION_SEC% --alwaysActive --requireVideoRoundTrip --%FRAME_ARG_NAME%=%FRAME_ARG_VALUE%"
if "%LOSSLESS%"=="1" set "CASE_ARGS=%CASE_ARGS% --lossless"
if "%INCLUDE_DEBUG%"=="1" set "CASE_ARGS=%CASE_ARGS% --debugAPI --debugNRI"

call :WriteLog ""
call :WriteLog "[%CASE_COUNT%] START %CASE_NAME%"
call :WriteLog "Command: ""%EXE_PATH%"" %CASE_ARGS%"

pushd "%REPO_ROOT%" >nul
"%EXE_PATH%" %CASE_ARGS% > "%CASE_OUT%" 2> "%CASE_ERR%"
set "EXIT_CODE=%ERRORLEVEL%"
popd >nul

call :WriteLog "ExitCode: %EXIT_CODE%"
>> "%LOG_PATH%" echo ----- stdout: %CASE_NAME% -----
type "%CASE_OUT%" >> "%LOG_PATH%" 2>nul
>> "%LOG_PATH%" echo ----- stderr: %CASE_NAME% -----
type "%CASE_ERR%" >> "%LOG_PATH%" 2>nul

set "CASE_FAILED=0"
if not "%EXIT_CODE%"=="0" set "CASE_FAILED=1"
findstr /R /C:"\<ERROR\>" /C:"\<FAILED\>" "%CASE_OUT%" "%CASE_ERR%" >nul 2>nul
if not errorlevel 1 set "CASE_FAILED=1"
findstr /C:"VIDEO_ROUND_TRIP_OK" "%CASE_OUT%" >nul 2>nul
if errorlevel 1 set "CASE_FAILED=1"

del "%CASE_OUT%" "%CASE_ERR%" >nul 2>nul

if "%CASE_FAILED%"=="1" (
set /a FAIL_COUNT+=1
call :WriteLog "FAILED %CASE_NAME%"
) else (
set /a PASS_COUNT+=1
call :WriteLog "OK %CASE_NAME%"
)

exit /b 0
1 change: 1 addition & 0 deletions Shaders/Shaders.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ Box5.fs.hlsl -T ps
Box6.fs.hlsl -T ps
Box7.fs.hlsl -T ps
Compute.cs.hlsl -T cs
VideoEncodePattern.cs.hlsl -T cs
DescriptorHeapIndexing.cs.hlsl -T cs -m 6_6
GenerateSceneDrawCalls.cs.hlsl -T cs
Forward.fs.hlsl -T ps
Expand Down
Loading