16bit processing
This commit is contained in:
@@ -31,14 +31,6 @@ jobs:
|
|||||||
- name: Configure Debug
|
- name: Configure Debug
|
||||||
shell: powershell
|
shell: powershell
|
||||||
run: |
|
run: |
|
||||||
$gpudirectDir = "${{ vars.GPUDIRECT_DIR }}"
|
|
||||||
if ([string]::IsNullOrWhiteSpace($gpudirectDir)) {
|
|
||||||
$gpudirectDir = $env:GPUDIRECT_DIR
|
|
||||||
}
|
|
||||||
if ([string]::IsNullOrWhiteSpace($gpudirectDir)) {
|
|
||||||
$gpudirectDir = Join-Path $PWD "3rdParty\Blackmagic DeckLink SDK 16.0\Win\Samples\NVIDIA_GPUDirect"
|
|
||||||
}
|
|
||||||
|
|
||||||
$slangRoot = "${{ vars.SLANG_ROOT }}"
|
$slangRoot = "${{ vars.SLANG_ROOT }}"
|
||||||
if ([string]::IsNullOrWhiteSpace($slangRoot)) {
|
if ([string]::IsNullOrWhiteSpace($slangRoot)) {
|
||||||
$slangRoot = $env:SLANG_ROOT
|
$slangRoot = $env:SLANG_ROOT
|
||||||
@@ -48,9 +40,6 @@ jobs:
|
|||||||
}
|
}
|
||||||
|
|
||||||
$requiredFiles = @(
|
$requiredFiles = @(
|
||||||
(Join-Path $gpudirectDir "include\DVPAPI.h"),
|
|
||||||
(Join-Path $gpudirectDir "lib\x64\dvp.lib"),
|
|
||||||
(Join-Path $gpudirectDir "bin\x64\dvp.dll"),
|
|
||||||
(Join-Path $slangRoot "bin\slangc.exe"),
|
(Join-Path $slangRoot "bin\slangc.exe"),
|
||||||
(Join-Path $slangRoot "bin\slang-compiler.dll"),
|
(Join-Path $slangRoot "bin\slang-compiler.dll"),
|
||||||
(Join-Path $slangRoot "bin\slang-glslang.dll"),
|
(Join-Path $slangRoot "bin\slang-glslang.dll"),
|
||||||
@@ -59,13 +48,12 @@ jobs:
|
|||||||
|
|
||||||
$missingFiles = @($requiredFiles | Where-Object { -not (Test-Path -LiteralPath $_) })
|
$missingFiles = @($requiredFiles | Where-Object { -not (Test-Path -LiteralPath $_) })
|
||||||
if ($missingFiles.Count -gt 0) {
|
if ($missingFiles.Count -gt 0) {
|
||||||
Write-Error "Missing native third-party dependencies. Set Gitea repository variables GPUDIRECT_DIR and SLANG_ROOT, or pre-populate the repo-local 3rdParty folder on the Windows runner. Missing: $($missingFiles -join ', ')"
|
Write-Error "Missing native third-party dependencies. Set Gitea repository variable SLANG_ROOT, or pre-populate the repo-local 3rdParty folder on the Windows runner. Missing: $($missingFiles -join ', ')"
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
Write-Host "Using GPUDIRECT_DIR=$gpudirectDir"
|
|
||||||
Write-Host "Using SLANG_ROOT=$slangRoot"
|
Write-Host "Using SLANG_ROOT=$slangRoot"
|
||||||
cmake --preset vs2022-x64-debug -DGPUDIRECT_DIR="$gpudirectDir" -DSLANG_ROOT="$slangRoot"
|
cmake --preset vs2022-x64-debug -DSLANG_ROOT="$slangRoot"
|
||||||
|
|
||||||
- name: Build Debug
|
- name: Build Debug
|
||||||
shell: powershell
|
shell: powershell
|
||||||
@@ -122,14 +110,6 @@ jobs:
|
|||||||
- name: Configure Release
|
- name: Configure Release
|
||||||
shell: powershell
|
shell: powershell
|
||||||
run: |
|
run: |
|
||||||
$gpudirectDir = "${{ vars.GPUDIRECT_DIR }}"
|
|
||||||
if ([string]::IsNullOrWhiteSpace($gpudirectDir)) {
|
|
||||||
$gpudirectDir = $env:GPUDIRECT_DIR
|
|
||||||
}
|
|
||||||
if ([string]::IsNullOrWhiteSpace($gpudirectDir)) {
|
|
||||||
$gpudirectDir = Join-Path $PWD "3rdParty\Blackmagic DeckLink SDK 16.0\Win\Samples\NVIDIA_GPUDirect"
|
|
||||||
}
|
|
||||||
|
|
||||||
$slangRoot = "${{ vars.SLANG_ROOT }}"
|
$slangRoot = "${{ vars.SLANG_ROOT }}"
|
||||||
if ([string]::IsNullOrWhiteSpace($slangRoot)) {
|
if ([string]::IsNullOrWhiteSpace($slangRoot)) {
|
||||||
$slangRoot = $env:SLANG_ROOT
|
$slangRoot = $env:SLANG_ROOT
|
||||||
@@ -139,9 +119,6 @@ jobs:
|
|||||||
}
|
}
|
||||||
|
|
||||||
$requiredFiles = @(
|
$requiredFiles = @(
|
||||||
(Join-Path $gpudirectDir "include\DVPAPI.h"),
|
|
||||||
(Join-Path $gpudirectDir "lib\x64\dvp.lib"),
|
|
||||||
(Join-Path $gpudirectDir "bin\x64\dvp.dll"),
|
|
||||||
(Join-Path $slangRoot "bin\slangc.exe"),
|
(Join-Path $slangRoot "bin\slangc.exe"),
|
||||||
(Join-Path $slangRoot "bin\slang-compiler.dll"),
|
(Join-Path $slangRoot "bin\slang-compiler.dll"),
|
||||||
(Join-Path $slangRoot "bin\slang-glslang.dll"),
|
(Join-Path $slangRoot "bin\slang-glslang.dll"),
|
||||||
@@ -150,13 +127,12 @@ jobs:
|
|||||||
|
|
||||||
$missingFiles = @($requiredFiles | Where-Object { -not (Test-Path -LiteralPath $_) })
|
$missingFiles = @($requiredFiles | Where-Object { -not (Test-Path -LiteralPath $_) })
|
||||||
if ($missingFiles.Count -gt 0) {
|
if ($missingFiles.Count -gt 0) {
|
||||||
Write-Error "Missing native third-party dependencies. Set Gitea repository variables GPUDIRECT_DIR and SLANG_ROOT, or pre-populate the repo-local 3rdParty folder on the Windows runner. Missing: $($missingFiles -join ', ')"
|
Write-Error "Missing native third-party dependencies. Set Gitea repository variable SLANG_ROOT, or pre-populate the repo-local 3rdParty folder on the Windows runner. Missing: $($missingFiles -join ', ')"
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
Write-Host "Using GPUDIRECT_DIR=$gpudirectDir"
|
|
||||||
Write-Host "Using SLANG_ROOT=$slangRoot"
|
Write-Host "Using SLANG_ROOT=$slangRoot"
|
||||||
cmake --preset vs2022-x64-release -DGPUDIRECT_DIR="$gpudirectDir" -DSLANG_ROOT="$slangRoot"
|
cmake --preset vs2022-x64-release -DSLANG_ROOT="$slangRoot"
|
||||||
|
|
||||||
- name: Build Release
|
- name: Build Release
|
||||||
shell: powershell
|
shell: powershell
|
||||||
|
|||||||
@@ -7,17 +7,12 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|||||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||||
|
|
||||||
set(APP_DIR "${CMAKE_CURRENT_SOURCE_DIR}/apps/LoopThroughWithOpenGLCompositing")
|
set(APP_DIR "${CMAKE_CURRENT_SOURCE_DIR}/apps/LoopThroughWithOpenGLCompositing")
|
||||||
set(GPUDIRECT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/3rdParty/Blackmagic DeckLink SDK 16.0/Win/Samples/NVIDIA_GPUDirect" CACHE PATH "Path to the NVIDIA_GPUDirect sample directory from the Blackmagic DeckLink SDK")
|
|
||||||
set(SLANG_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/3rdParty/slang-2026.8-windows-x86_64" CACHE PATH "Path to a Slang binary release containing bin/slangc.exe")
|
set(SLANG_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/3rdParty/slang-2026.8-windows-x86_64" CACHE PATH "Path to a Slang binary release containing bin/slangc.exe")
|
||||||
|
|
||||||
if(NOT EXISTS "${APP_DIR}/LoopThroughWithOpenGLCompositing.cpp")
|
if(NOT EXISTS "${APP_DIR}/LoopThroughWithOpenGLCompositing.cpp")
|
||||||
message(FATAL_ERROR "Imported app sources were not found under ${APP_DIR}")
|
message(FATAL_ERROR "Imported app sources were not found under ${APP_DIR}")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(NOT EXISTS "${GPUDIRECT_DIR}/lib/x64/dvp.lib")
|
|
||||||
message(FATAL_ERROR "NVIDIA GPUDirect library not found under ${GPUDIRECT_DIR}")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set(SLANG_RUNTIME_FILES
|
set(SLANG_RUNTIME_FILES
|
||||||
"${SLANG_ROOT}/bin/slangc.exe"
|
"${SLANG_ROOT}/bin/slangc.exe"
|
||||||
"${SLANG_ROOT}/bin/slang-compiler.dll"
|
"${SLANG_ROOT}/bin/slang-compiler.dll"
|
||||||
@@ -51,6 +46,8 @@ set(APP_SOURCES
|
|||||||
"${APP_DIR}/decklink/DeckLinkFrameTransfer.h"
|
"${APP_DIR}/decklink/DeckLinkFrameTransfer.h"
|
||||||
"${APP_DIR}/decklink/DeckLinkSession.cpp"
|
"${APP_DIR}/decklink/DeckLinkSession.cpp"
|
||||||
"${APP_DIR}/decklink/DeckLinkSession.h"
|
"${APP_DIR}/decklink/DeckLinkSession.h"
|
||||||
|
"${APP_DIR}/decklink/VideoIOFormat.cpp"
|
||||||
|
"${APP_DIR}/decklink/VideoIOFormat.h"
|
||||||
"${APP_DIR}/gl/GLExtensions.cpp"
|
"${APP_DIR}/gl/GLExtensions.cpp"
|
||||||
"${APP_DIR}/gl/GLExtensions.h"
|
"${APP_DIR}/gl/GLExtensions.h"
|
||||||
"${APP_DIR}/gl/GlobalParamsBuffer.cpp"
|
"${APP_DIR}/gl/GlobalParamsBuffer.cpp"
|
||||||
@@ -83,8 +80,6 @@ set(APP_SOURCES
|
|||||||
"${APP_DIR}/gl/TextureAssetLoader.h"
|
"${APP_DIR}/gl/TextureAssetLoader.h"
|
||||||
"${APP_DIR}/gl/TemporalHistoryBuffers.cpp"
|
"${APP_DIR}/gl/TemporalHistoryBuffers.cpp"
|
||||||
"${APP_DIR}/gl/TemporalHistoryBuffers.h"
|
"${APP_DIR}/gl/TemporalHistoryBuffers.h"
|
||||||
"${APP_DIR}/gl/VideoFrameTransfer.cpp"
|
|
||||||
"${APP_DIR}/gl/VideoFrameTransfer.h"
|
|
||||||
"${APP_DIR}/LoopThroughWithOpenGLCompositing.cpp"
|
"${APP_DIR}/LoopThroughWithOpenGLCompositing.cpp"
|
||||||
"${APP_DIR}/LoopThroughWithOpenGLCompositing.h"
|
"${APP_DIR}/LoopThroughWithOpenGLCompositing.h"
|
||||||
"${APP_DIR}/LoopThroughWithOpenGLCompositing.rc"
|
"${APP_DIR}/LoopThroughWithOpenGLCompositing.rc"
|
||||||
@@ -119,15 +114,9 @@ target_include_directories(LoopThroughWithOpenGLCompositing PRIVATE
|
|||||||
"${APP_DIR}/platform"
|
"${APP_DIR}/platform"
|
||||||
"${APP_DIR}/runtime"
|
"${APP_DIR}/runtime"
|
||||||
"${APP_DIR}/shader"
|
"${APP_DIR}/shader"
|
||||||
"${GPUDIRECT_DIR}/include"
|
|
||||||
)
|
|
||||||
|
|
||||||
target_link_directories(LoopThroughWithOpenGLCompositing PRIVATE
|
|
||||||
"${GPUDIRECT_DIR}/lib/x64"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
target_link_libraries(LoopThroughWithOpenGLCompositing PRIVATE
|
target_link_libraries(LoopThroughWithOpenGLCompositing PRIVATE
|
||||||
dvp.lib
|
|
||||||
opengl32
|
opengl32
|
||||||
glu32
|
glu32
|
||||||
Ws2_32
|
Ws2_32
|
||||||
@@ -250,20 +239,26 @@ endif()
|
|||||||
|
|
||||||
add_test(NAME OscServerTests COMMAND OscServerTests)
|
add_test(NAME OscServerTests COMMAND OscServerTests)
|
||||||
|
|
||||||
add_custom_command(TARGET LoopThroughWithOpenGLCompositing POST_BUILD
|
add_executable(VideoIOFormatTests
|
||||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
"${APP_DIR}/decklink/VideoIOFormat.cpp"
|
||||||
"${GPUDIRECT_DIR}/bin/x64/dvp.dll"
|
"${CMAKE_CURRENT_SOURCE_DIR}/tests/VideoIOFormatTests.cpp"
|
||||||
"$<TARGET_FILE_DIR:LoopThroughWithOpenGLCompositing>/dvp.dll"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
target_include_directories(VideoIOFormatTests PRIVATE
|
||||||
|
"${APP_DIR}"
|
||||||
|
"${APP_DIR}/decklink"
|
||||||
|
)
|
||||||
|
|
||||||
|
if(MSVC)
|
||||||
|
target_compile_options(VideoIOFormatTests PRIVATE /W3)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_test(NAME VideoIOFormatTests COMMAND VideoIOFormatTests)
|
||||||
|
|
||||||
install(TARGETS LoopThroughWithOpenGLCompositing
|
install(TARGETS LoopThroughWithOpenGLCompositing
|
||||||
RUNTIME DESTINATION "."
|
RUNTIME DESTINATION "."
|
||||||
)
|
)
|
||||||
|
|
||||||
install(FILES "${GPUDIRECT_DIR}/bin/x64/dvp.dll"
|
|
||||||
DESTINATION "."
|
|
||||||
)
|
|
||||||
|
|
||||||
install(FILES ${SLANG_RUNTIME_FILES}
|
install(FILES ${SLANG_RUNTIME_FILES}
|
||||||
DESTINATION "3rdParty/slang/bin"
|
DESTINATION "3rdParty/slang/bin"
|
||||||
)
|
)
|
||||||
|
|||||||
26
README.md
26
README.md
@@ -20,23 +20,9 @@ The app loads shader packages from `shaders/`, compiles Slang to GLSL at runtime
|
|||||||
- Windows with Visual Studio 2022 C++ tooling.
|
- Windows with Visual Studio 2022 C++ tooling.
|
||||||
- CMake 3.24 or newer.
|
- CMake 3.24 or newer.
|
||||||
- Node.js and npm for the control UI.
|
- Node.js and npm for the control UI.
|
||||||
- Blackmagic DeckLink SDK 16.0 with the NVIDIA GPUDirect sample files available locally.
|
- Blackmagic Desktop Video drivers and a DeckLink device.
|
||||||
- Slang binary release with `slangc.exe`, `slang-compiler.dll`, `slang-glslang.dll`, and `LICENSE`.
|
- Slang binary release with `slangc.exe`, `slang-compiler.dll`, `slang-glslang.dll`, and `LICENSE`.
|
||||||
|
|
||||||
The Blackmagic/GPUDirect SDK should not be committed to this repository. `CMakeLists.txt` exposes `GPUDIRECT_DIR` as a cache path so local machines and CI runners can point at their installed SDK location.
|
|
||||||
|
|
||||||
Default expected SDK path:
|
|
||||||
|
|
||||||
```text
|
|
||||||
3rdParty/Blackmagic DeckLink SDK 16.0/Win/Samples/NVIDIA_GPUDirect
|
|
||||||
```
|
|
||||||
|
|
||||||
Override example:
|
|
||||||
|
|
||||||
```powershell
|
|
||||||
cmake --preset vs2022-x64-debug -DGPUDIRECT_DIR="D:/SDKs/Blackmagic DeckLink SDK 16.0/Win/Samples/NVIDIA_GPUDirect"
|
|
||||||
```
|
|
||||||
|
|
||||||
Default expected Slang path:
|
Default expected Slang path:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
@@ -87,7 +73,6 @@ The package folder will contain:
|
|||||||
```text
|
```text
|
||||||
dist/VideoShader/
|
dist/VideoShader/
|
||||||
LoopThroughWithOpenGLCompositing.exe
|
LoopThroughWithOpenGLCompositing.exe
|
||||||
dvp.dll
|
|
||||||
config/
|
config/
|
||||||
shaders/
|
shaders/
|
||||||
3rdParty/slang/bin/
|
3rdParty/slang/bin/
|
||||||
@@ -227,9 +212,8 @@ The Gitea workflow expects two act runners:
|
|||||||
- `windows-2022`: builds the native app and runs native tests.
|
- `windows-2022`: builds the native app and runs native tests.
|
||||||
- `ubuntu-latest`: installs UI dependencies and runs the Vite build.
|
- `ubuntu-latest`: installs UI dependencies and runs the Vite build.
|
||||||
|
|
||||||
The Windows jobs validate native third-party dependencies before configuring CMake. Because `3rdParty/` is ignored, configure these paths on the runner or in Gitea repository variables:
|
The Windows jobs validate native third-party dependencies before configuring CMake. Because `3rdParty/` is ignored, configure this path on the runner or in a Gitea repository variable:
|
||||||
|
|
||||||
- `GPUDIRECT_DIR`: path to `Blackmagic DeckLink SDK 16.0/Win/Samples/NVIDIA_GPUDirect`.
|
|
||||||
- `SLANG_ROOT`: path to the Slang binary release folder containing `bin/slangc.exe`.
|
- `SLANG_ROOT`: path to the Slang binary release folder containing `bin/slangc.exe`.
|
||||||
|
|
||||||
The Windows runner also needs the Visual Studio ATL component installed. In Visual Studio Build Tools 2022, add `C++ ATL for latest v143 build tools (x86 & x64)`, component ID `Microsoft.VisualStudio.Component.VC.ATL`.
|
The Windows runner also needs the Visual Studio ATL component installed. In Visual Studio Build Tools 2022, add `C++ ATL for latest v143 build tools (x86 & x64)`, component ID `Microsoft.VisualStudio.Component.VC.ATL`.
|
||||||
@@ -237,11 +221,10 @@ The Windows runner also needs the Visual Studio ATL component installed. In Visu
|
|||||||
Example runner paths:
|
Example runner paths:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
D:\SDKs\Blackmagic DeckLink SDK 16.0\Win\Samples\NVIDIA_GPUDirect
|
|
||||||
D:\SDKs\slang-2026.8-windows-x86_64
|
D:\SDKs\slang-2026.8-windows-x86_64
|
||||||
```
|
```
|
||||||
|
|
||||||
If neither variable is set, the workflow falls back to the repo-local defaults under `3rdParty/`.
|
If `SLANG_ROOT` is not set, the workflow falls back to the repo-local default under `3rdParty/`.
|
||||||
|
|
||||||
## Still Todo
|
## Still Todo
|
||||||
|
|
||||||
@@ -261,4 +244,5 @@ If neither variable is set, the workflow falls back to the repo-local defaults u
|
|||||||
- compute shaders or a small 1x1 or nx1 RGBA16f render target for abritary data store
|
- compute shaders or a small 1x1 or nx1 RGBA16f render target for abritary data store
|
||||||
- allow shaders to read other shaders data store based on name? or putput over OSC
|
- allow shaders to read other shaders data store based on name? or putput over OSC
|
||||||
- Mipmappong needed?
|
- Mipmappong needed?
|
||||||
- unwrap a fish eyelens and mirror it and map it to equirectangulr for environmnet map purposes
|
- unwrap a fish eyelens and mirror it and map it to equirectangulr for environmnet map purposes
|
||||||
|
- add a random flaot between 0-1 that changes each time the application loads for shaders to use
|
||||||
|
|||||||
@@ -172,6 +172,13 @@ Fields:
|
|||||||
- `sourceHistoryLength`: number of usable source-history frames currently available.
|
- `sourceHistoryLength`: number of usable source-history frames currently available.
|
||||||
- `temporalHistoryLength`: number of usable temporal frames currently available for this layer.
|
- `temporalHistoryLength`: number of usable temporal frames currently available for this layer.
|
||||||
|
|
||||||
|
Color/precision notes:
|
||||||
|
|
||||||
|
- `context.sourceColor`, `sampleVideo()`, and temporal history samples are display-referred Rec.709-like RGB, not linear-light RGB.
|
||||||
|
- The host prefers 10-bit DeckLink YUV capture and output when the card/mode supports it, with automatic 8-bit fallback.
|
||||||
|
- Internal decoded, layer, composite, output, and temporal render targets are 16-bit floating point, so gradients and LUT work have more headroom than the packed DeckLink byte formats.
|
||||||
|
- Do not add extra Rec.709 or linear conversions unless the shader intentionally documents that behavior.
|
||||||
|
|
||||||
## Helper Functions
|
## Helper Functions
|
||||||
|
|
||||||
The wrapper provides:
|
The wrapper provides:
|
||||||
|
|||||||
@@ -89,7 +89,7 @@
|
|||||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||||
<ClCompile>
|
<ClCompile>
|
||||||
<Optimization>Disabled</Optimization>
|
<Optimization>Disabled</Optimization>
|
||||||
<AdditionalIncludeDirectories>.;control;decklink;gl;..\..\3rdParty\Blackmagic DeckLink SDK 16.0\Win\Samples\NVIDIA_GPUDirect\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>.;control;decklink;gl;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
|
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
|
||||||
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
|
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
|
||||||
@@ -99,15 +99,11 @@
|
|||||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<Link>
|
<Link>
|
||||||
<AdditionalDependencies>dvp.lib;opengl32.lib;Glu32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
<AdditionalDependencies>opengl32.lib;Glu32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
<AdditionalLibraryDirectories>..\..\3rdParty\Blackmagic DeckLink SDK 16.0\Win\Samples\NVIDIA_GPUDirect\lib\win32;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
|
||||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
<SubSystem>Windows</SubSystem>
|
<SubSystem>Windows</SubSystem>
|
||||||
<TargetMachine>MachineX86</TargetMachine>
|
<TargetMachine>MachineX86</TargetMachine>
|
||||||
</Link>
|
</Link>
|
||||||
<PostBuildEvent>
|
|
||||||
<Command>copy /y "..\..\3rdParty\Blackmagic DeckLink SDK 16.0\Win\Samples\NVIDIA_GPUDirect\bin\$(Platform)\dvp.dll" "$(TargetDir)"</Command>
|
|
||||||
</PostBuildEvent>
|
|
||||||
</ItemDefinitionGroup>
|
</ItemDefinitionGroup>
|
||||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||||
<Midl>
|
<Midl>
|
||||||
@@ -115,7 +111,7 @@
|
|||||||
</Midl>
|
</Midl>
|
||||||
<ClCompile>
|
<ClCompile>
|
||||||
<Optimization>Disabled</Optimization>
|
<Optimization>Disabled</Optimization>
|
||||||
<AdditionalIncludeDirectories>.;control;decklink;gl;..\..\3rdParty\Blackmagic DeckLink SDK 16.0\Win\Samples\NVIDIA_GPUDirect\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>.;control;decklink;gl;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
|
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
|
||||||
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
|
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
|
||||||
@@ -125,21 +121,17 @@
|
|||||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<Link>
|
<Link>
|
||||||
<AdditionalDependencies>dvp.lib;opengl32.lib;Glu32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
<AdditionalDependencies>opengl32.lib;Glu32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
<AdditionalLibraryDirectories>..\..\3rdParty\Blackmagic DeckLink SDK 16.0\Win\Samples\NVIDIA_GPUDirect\lib\x64;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
|
||||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
<SubSystem>Windows</SubSystem>
|
<SubSystem>Windows</SubSystem>
|
||||||
<TargetMachine>MachineX64</TargetMachine>
|
<TargetMachine>MachineX64</TargetMachine>
|
||||||
</Link>
|
</Link>
|
||||||
<PostBuildEvent>
|
|
||||||
<Command>copy /y "..\..\3rdParty\Blackmagic DeckLink SDK 16.0\Win\Samples\NVIDIA_GPUDirect\bin\$(Platform)\dvp.dll" "$(TargetDir)"</Command>
|
|
||||||
</PostBuildEvent>
|
|
||||||
</ItemDefinitionGroup>
|
</ItemDefinitionGroup>
|
||||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||||
<ClCompile>
|
<ClCompile>
|
||||||
<Optimization>MaxSpeed</Optimization>
|
<Optimization>MaxSpeed</Optimization>
|
||||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||||
<AdditionalIncludeDirectories>.;control;decklink;gl;..\..\3rdParty\Blackmagic DeckLink SDK 16.0\Win\Samples\NVIDIA_GPUDirect\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>.;control;decklink;gl;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
|
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
|
||||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||||
@@ -149,18 +141,13 @@
|
|||||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<Link>
|
<Link>
|
||||||
<AdditionalDependencies>dvp.lib;opengl32.lib;Glu32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
<AdditionalDependencies>opengl32.lib;Glu32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
<AdditionalLibraryDirectories>..\..\3rdParty\Blackmagic DeckLink SDK 16.0\Win\Samples\NVIDIA_GPUDirect\lib\win32;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
|
||||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
<SubSystem>Windows</SubSystem>
|
<SubSystem>Windows</SubSystem>
|
||||||
<OptimizeReferences>true</OptimizeReferences>
|
<OptimizeReferences>true</OptimizeReferences>
|
||||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||||
<TargetMachine>MachineX86</TargetMachine>
|
<TargetMachine>MachineX86</TargetMachine>
|
||||||
</Link>
|
</Link>
|
||||||
<PostBuildEvent>
|
|
||||||
<Message>Copy nececssary DLLs to target directory</Message>
|
|
||||||
<Command>copy /y "..\..\3rdParty\Blackmagic DeckLink SDK 16.0\Win\Samples\NVIDIA_GPUDirect\bin\$(Platform)\dvp.dll" "$(TargetDir)"</Command>
|
|
||||||
</PostBuildEvent>
|
|
||||||
</ItemDefinitionGroup>
|
</ItemDefinitionGroup>
|
||||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
<Midl>
|
<Midl>
|
||||||
@@ -169,7 +156,7 @@
|
|||||||
<ClCompile>
|
<ClCompile>
|
||||||
<Optimization>MaxSpeed</Optimization>
|
<Optimization>MaxSpeed</Optimization>
|
||||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||||
<AdditionalIncludeDirectories>.;control;decklink;gl;..\..\3rdParty\Blackmagic DeckLink SDK 16.0\Win\Samples\NVIDIA_GPUDirect\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>.;control;decklink;gl;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
|
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
|
||||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||||
@@ -179,17 +166,13 @@
|
|||||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<Link>
|
<Link>
|
||||||
<AdditionalDependencies>dvp.lib;opengl32.lib;Glu32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
<AdditionalDependencies>opengl32.lib;Glu32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
<AdditionalLibraryDirectories>..\..\3rdParty\Blackmagic DeckLink SDK 16.0\Win\Samples\NVIDIA_GPUDirect\lib\x64;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
|
||||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
<SubSystem>Windows</SubSystem>
|
<SubSystem>Windows</SubSystem>
|
||||||
<OptimizeReferences>true</OptimizeReferences>
|
<OptimizeReferences>true</OptimizeReferences>
|
||||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||||
<TargetMachine>MachineX64</TargetMachine>
|
<TargetMachine>MachineX64</TargetMachine>
|
||||||
</Link>
|
</Link>
|
||||||
<PostBuildEvent>
|
|
||||||
<Command>copy /y "..\..\3rdParty\Blackmagic DeckLink SDK 16.0\Win\Samples\NVIDIA_GPUDirect\bin\$(Platform)\dvp.dll" "$(TargetDir)"</Command>
|
|
||||||
</PostBuildEvent>
|
|
||||||
</ItemDefinitionGroup>
|
</ItemDefinitionGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="gl\GLExtensions.cpp" />
|
<ClCompile Include="gl\GLExtensions.cpp" />
|
||||||
@@ -206,10 +189,10 @@
|
|||||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
|
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
|
||||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
|
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="gl\VideoFrameTransfer.cpp" />
|
|
||||||
<ClCompile Include="DeckLinkAPI_i.c" />
|
<ClCompile Include="DeckLinkAPI_i.c" />
|
||||||
<ClCompile Include="control\RuntimeServices.cpp" />
|
<ClCompile Include="control\RuntimeServices.cpp" />
|
||||||
<ClCompile Include="decklink\DeckLinkSession.cpp" />
|
<ClCompile Include="decklink\DeckLinkSession.cpp" />
|
||||||
|
<ClCompile Include="decklink\VideoIOFormat.cpp" />
|
||||||
<ClCompile Include="runtime\RuntimeClock.cpp" />
|
<ClCompile Include="runtime\RuntimeClock.cpp" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@@ -224,9 +207,9 @@
|
|||||||
<ClInclude Include="resource.h" />
|
<ClInclude Include="resource.h" />
|
||||||
<ClInclude Include="stdafx.h" />
|
<ClInclude Include="stdafx.h" />
|
||||||
<ClInclude Include="targetver.h" />
|
<ClInclude Include="targetver.h" />
|
||||||
<ClInclude Include="gl\VideoFrameTransfer.h" />
|
|
||||||
<ClInclude Include="control\RuntimeServices.h" />
|
<ClInclude Include="control\RuntimeServices.h" />
|
||||||
<ClInclude Include="decklink\DeckLinkSession.h" />
|
<ClInclude Include="decklink\DeckLinkSession.h" />
|
||||||
|
<ClInclude Include="decklink\VideoIOFormat.h" />
|
||||||
<ClInclude Include="runtime\RuntimeClock.h" />
|
<ClInclude Include="runtime\RuntimeClock.h" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -45,9 +45,6 @@
|
|||||||
<ClCompile Include="stdafx.cpp">
|
<ClCompile Include="stdafx.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="gl\VideoFrameTransfer.cpp">
|
|
||||||
<Filter>Source Files</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="DeckLinkAPI_i.c">
|
<ClCompile Include="DeckLinkAPI_i.c">
|
||||||
<Filter>DeckLink API</Filter>
|
<Filter>DeckLink API</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
@@ -57,6 +54,9 @@
|
|||||||
<ClCompile Include="decklink\DeckLinkSession.cpp">
|
<ClCompile Include="decklink\DeckLinkSession.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="decklink\VideoIOFormat.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
<ClCompile Include="runtime\RuntimeClock.cpp">
|
<ClCompile Include="runtime\RuntimeClock.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
@@ -95,15 +95,15 @@
|
|||||||
<ClInclude Include="targetver.h">
|
<ClInclude Include="targetver.h">
|
||||||
<Filter>Header Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="gl\VideoFrameTransfer.h">
|
|
||||||
<Filter>Header Files</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="control\RuntimeServices.h">
|
<ClInclude Include="control\RuntimeServices.h">
|
||||||
<Filter>Header Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="decklink\DeckLinkSession.h">
|
<ClInclude Include="decklink\DeckLinkSession.h">
|
||||||
<Filter>Header Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="decklink\VideoIOFormat.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
<ClInclude Include="runtime\RuntimeClock.h">
|
<ClInclude Include="runtime\RuntimeClock.h">
|
||||||
<Filter>Header Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
|||||||
@@ -2,311 +2,6 @@
|
|||||||
|
|
||||||
#include "OpenGLComposite.h"
|
#include "OpenGLComposite.h"
|
||||||
|
|
||||||
#include <initguid.h>
|
|
||||||
|
|
||||||
DEFINE_GUID(IID_PinnedMemoryAllocator,
|
|
||||||
0xddf921a6, 0x279d, 0x4dcd, 0x86, 0x26, 0x75, 0x7f, 0x58, 0xa8, 0xc4, 0x35);
|
|
||||||
|
|
||||||
////////////////////////////////////////////
|
|
||||||
// PinnedMemoryAllocator
|
|
||||||
////////////////////////////////////////////
|
|
||||||
|
|
||||||
// PinnedMemoryAllocator implements the IDeckLinkVideoBufferAllocator interface to be used instead of the
|
|
||||||
// built-in buffer allocator.
|
|
||||||
//
|
|
||||||
// For this sample application a custom buffer allocator is used to ensure each address
|
|
||||||
// of buffer memory is aligned on a 4kB boundary required by the OpenGL pinned memory extension.
|
|
||||||
// If the pinned memory extension is not available, this allocator will still be used and
|
|
||||||
// demonstrates how to cache buffer allocations for efficiency.
|
|
||||||
//
|
|
||||||
// The frame cache delays the releasing of buffers until the cache fills up, thereby avoiding an
|
|
||||||
// allocate plus pin operation for every frame, followed by an unpin and deallocate on every frame.
|
|
||||||
PinnedMemoryAllocator::PinnedMemoryAllocator(HDC hdc, HGLRC hglrc, VideoFrameTransfer::Direction direction, unsigned cacheSize, unsigned bufferSize) :
|
|
||||||
mHGLDC(hdc),
|
|
||||||
mHGLRC(hglrc),
|
|
||||||
mRefCount(1),
|
|
||||||
mDirection(direction),
|
|
||||||
mBufferSize(bufferSize),
|
|
||||||
mFrameCacheSize(cacheSize)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
PinnedMemoryAllocator::~PinnedMemoryAllocator()
|
|
||||||
{
|
|
||||||
// Cleanup any unused buffers that remain in the cache
|
|
||||||
while (!mFrameCache.empty())
|
|
||||||
{
|
|
||||||
unPinAddress(mFrameCache.back());
|
|
||||||
VirtualFree(mFrameCache.back(), 0, MEM_RELEASE);
|
|
||||||
mFrameCache.pop_back();
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto iter = mFrameTransfer.begin(); iter != mFrameTransfer.end(); ++iter)
|
|
||||||
delete iter->second;
|
|
||||||
mFrameTransfer.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool PinnedMemoryAllocator::transferFrame(void* address, GLuint gpuTexture)
|
|
||||||
{
|
|
||||||
if (mFrameTransfer.count(address) == 0)
|
|
||||||
{
|
|
||||||
// VideoFrameTransfer prepares and pins address
|
|
||||||
mFrameTransfer[address] = new VideoFrameTransfer(mBufferSize, address, mDirection);
|
|
||||||
}
|
|
||||||
|
|
||||||
return mFrameTransfer[address]->performFrameTransfer();
|
|
||||||
}
|
|
||||||
|
|
||||||
void PinnedMemoryAllocator::waitForTransferComplete(void* address)
|
|
||||||
{
|
|
||||||
if (mFrameTransfer.count(address))
|
|
||||||
mFrameTransfer[address]->waitForTransferComplete();
|
|
||||||
}
|
|
||||||
|
|
||||||
void PinnedMemoryAllocator::unPinAddress(void* address)
|
|
||||||
{
|
|
||||||
// un-pin address only if it has been pinned for transfer
|
|
||||||
if (mFrameTransfer.count(address) > 0)
|
|
||||||
{
|
|
||||||
wglMakeCurrent(mHGLDC, mHGLRC);
|
|
||||||
mFrameTransfer.erase(address);
|
|
||||||
wglMakeCurrent(NULL, NULL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
HRESULT STDMETHODCALLTYPE PinnedMemoryAllocator::QueryInterface(REFIID iid, LPVOID* ppv)
|
|
||||||
{
|
|
||||||
if (!ppv)
|
|
||||||
return E_POINTER;
|
|
||||||
|
|
||||||
if (iid == IID_IUnknown || iid == IID_PinnedMemoryAllocator)
|
|
||||||
{
|
|
||||||
*ppv = this;
|
|
||||||
}
|
|
||||||
else if (iid == IID_IDeckLinkVideoBufferAllocator)
|
|
||||||
{
|
|
||||||
*ppv = static_cast<IDeckLinkVideoBufferAllocator*>(this);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
*ppv = nullptr;
|
|
||||||
return E_NOINTERFACE;
|
|
||||||
}
|
|
||||||
AddRef();
|
|
||||||
return S_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
ULONG STDMETHODCALLTYPE PinnedMemoryAllocator::AddRef(void)
|
|
||||||
{
|
|
||||||
return ++mRefCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
ULONG STDMETHODCALLTYPE PinnedMemoryAllocator::Release(void)
|
|
||||||
{
|
|
||||||
int newCount = --mRefCount;
|
|
||||||
if (newCount == 0)
|
|
||||||
delete this;
|
|
||||||
return newCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
HRESULT STDMETHODCALLTYPE PinnedMemoryAllocator::AllocateVideoBuffer(IDeckLinkVideoBuffer** allocatedBuffer)
|
|
||||||
{
|
|
||||||
std::shared_ptr<void> sharedMemBuffer;
|
|
||||||
|
|
||||||
// Manage caching of allocated buffers via shared_ptr deleter.
|
|
||||||
auto deleter = [this](void* buffer) mutable {
|
|
||||||
if (mFrameCache.size() < mFrameCacheSize)
|
|
||||||
{
|
|
||||||
mFrameCache.push_back(buffer);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// No room left in cache, so un-pin (if it was pinned) and free this buffer
|
|
||||||
unPinAddress(buffer);
|
|
||||||
VirtualFree(buffer, 0, MEM_RELEASE);
|
|
||||||
}
|
|
||||||
// We AddRef this class once the deleter is used because this class owns the mem
|
|
||||||
Release();
|
|
||||||
};
|
|
||||||
|
|
||||||
if (mFrameCache.empty())
|
|
||||||
{
|
|
||||||
// Allocate memory on a page boundary
|
|
||||||
void* memBuffer = VirtualAlloc(NULL, mBufferSize, MEM_COMMIT | MEM_RESERVE | MEM_WRITE_WATCH, PAGE_READWRITE);
|
|
||||||
if (!memBuffer)
|
|
||||||
return E_OUTOFMEMORY;
|
|
||||||
|
|
||||||
sharedMemBuffer = std::shared_ptr<void>(memBuffer, deleter);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Re-use most recently released address
|
|
||||||
sharedMemBuffer = std::shared_ptr<void>(mFrameCache.back(), deleter);
|
|
||||||
mFrameCache.pop_back();
|
|
||||||
}
|
|
||||||
|
|
||||||
// This class owns the mem so the buffer we return needs to AddRef() this, and Release() in the deleter
|
|
||||||
AddRef();
|
|
||||||
|
|
||||||
*allocatedBuffer = new DeckLinkVideoBuffer(sharedMemBuffer, this);
|
|
||||||
return S_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////
|
|
||||||
// InputAllocatorPool Class
|
|
||||||
////////////////////////////////////////////
|
|
||||||
InputAllocatorPool::InputAllocatorPool(HDC hdc, HGLRC hglrc)
|
|
||||||
{
|
|
||||||
mHDC = hdc;
|
|
||||||
mHGLRC = hglrc;
|
|
||||||
}
|
|
||||||
|
|
||||||
HRESULT InputAllocatorPool::QueryInterface(REFIID iid, void** ppv)
|
|
||||||
{
|
|
||||||
if (!ppv)
|
|
||||||
return E_POINTER;
|
|
||||||
|
|
||||||
if (iid == IID_IUnknown)
|
|
||||||
{
|
|
||||||
*ppv = this;
|
|
||||||
}
|
|
||||||
else if (iid == IID_IDeckLinkVideoBufferAllocatorProvider)
|
|
||||||
{
|
|
||||||
*ppv = static_cast<IDeckLinkVideoBufferAllocatorProvider*>(this);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
*ppv = nullptr;
|
|
||||||
return E_NOINTERFACE;
|
|
||||||
}
|
|
||||||
AddRef();
|
|
||||||
return S_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
ULONG InputAllocatorPool::AddRef(void)
|
|
||||||
{
|
|
||||||
return ++mRefCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
ULONG InputAllocatorPool::Release(void)
|
|
||||||
{
|
|
||||||
int newCount = --mRefCount;
|
|
||||||
if (newCount == 0)
|
|
||||||
delete this;
|
|
||||||
return newCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
HRESULT InputAllocatorPool::GetVideoBufferAllocator(
|
|
||||||
/* [in] */ unsigned int bufferSize,
|
|
||||||
/* [in] */ unsigned int,
|
|
||||||
/* [in] */ unsigned int,
|
|
||||||
/* [in] */ unsigned int,
|
|
||||||
/* [in] */ BMDPixelFormat,
|
|
||||||
/* [out] */ IDeckLinkVideoBufferAllocator** allocator)
|
|
||||||
{
|
|
||||||
if (!allocator)
|
|
||||||
return E_POINTER;
|
|
||||||
|
|
||||||
auto existing = mAllocatorBySize.find(bufferSize);
|
|
||||||
if (existing != mAllocatorBySize.end())
|
|
||||||
{
|
|
||||||
*allocator = &*existing->second;
|
|
||||||
(*allocator)->AddRef();
|
|
||||||
return S_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
CComPtr<PinnedMemoryAllocator> newAllocator;
|
|
||||||
newAllocator.Attach(new (std::nothrow) PinnedMemoryAllocator(mHDC, mHGLRC, VideoFrameTransfer::CPUtoGPU, 3, bufferSize));
|
|
||||||
if (!newAllocator)
|
|
||||||
return E_OUTOFMEMORY;
|
|
||||||
|
|
||||||
mAllocatorBySize.emplace(std::make_pair(bufferSize, newAllocator));
|
|
||||||
*allocator = newAllocator.Detach();
|
|
||||||
return S_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////
|
|
||||||
// DeckLink Video Buffer Class
|
|
||||||
////////////////////////////////////////////
|
|
||||||
DeckLinkVideoBuffer::DeckLinkVideoBuffer(std::shared_ptr<void>& buffer, PinnedMemoryAllocator* parent) :
|
|
||||||
mParentAllocator(parent),
|
|
||||||
mRefCount(1),
|
|
||||||
mBuffer(buffer)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
HRESULT STDMETHODCALLTYPE DeckLinkVideoBuffer::QueryInterface(REFIID riid, void** ppvObject)
|
|
||||||
{
|
|
||||||
HRESULT result = S_OK;
|
|
||||||
|
|
||||||
if (ppvObject == nullptr)
|
|
||||||
return E_POINTER;
|
|
||||||
|
|
||||||
if (riid == IID_IUnknown)
|
|
||||||
{
|
|
||||||
*ppvObject = this;
|
|
||||||
AddRef();
|
|
||||||
}
|
|
||||||
else if (riid == IID_IDeckLinkVideoBuffer)
|
|
||||||
{
|
|
||||||
*ppvObject = static_cast<IDeckLinkVideoBuffer*>(this);
|
|
||||||
AddRef();
|
|
||||||
}
|
|
||||||
else if (riid == IID_PinnedMemoryAllocator)
|
|
||||||
{
|
|
||||||
result = mParentAllocator->QueryInterface(riid, ppvObject);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
*ppvObject = nullptr;
|
|
||||||
result = E_NOINTERFACE;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
ULONG STDMETHODCALLTYPE DeckLinkVideoBuffer::AddRef()
|
|
||||||
{
|
|
||||||
return ++mRefCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
ULONG STDMETHODCALLTYPE DeckLinkVideoBuffer::Release()
|
|
||||||
{
|
|
||||||
int newValue = --mRefCount;
|
|
||||||
if (newValue == 0)
|
|
||||||
delete this;
|
|
||||||
|
|
||||||
return newValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
HRESULT STDMETHODCALLTYPE DeckLinkVideoBuffer::GetBytes(void** buffer)
|
|
||||||
{
|
|
||||||
if (buffer == nullptr)
|
|
||||||
return E_POINTER;
|
|
||||||
|
|
||||||
*buffer = mBuffer.get();
|
|
||||||
return S_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
HRESULT STDMETHODCALLTYPE DeckLinkVideoBuffer::GetSize(uint64_t* size)
|
|
||||||
{
|
|
||||||
if (size == nullptr)
|
|
||||||
return E_POINTER;
|
|
||||||
|
|
||||||
*size = mParentAllocator->bufferSize();
|
|
||||||
return S_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
HRESULT STDMETHODCALLTYPE DeckLinkVideoBuffer::StartAccess(BMDBufferAccessFlags)
|
|
||||||
{
|
|
||||||
return S_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
HRESULT STDMETHODCALLTYPE DeckLinkVideoBuffer::EndAccess(BMDBufferAccessFlags)
|
|
||||||
{
|
|
||||||
return S_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////
|
////////////////////////////////////////////
|
||||||
// DeckLink Capture Delegate Class
|
// DeckLink Capture Delegate Class
|
||||||
////////////////////////////////////////////
|
////////////////////////////////////////////
|
||||||
|
|||||||
@@ -1,111 +1,13 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <gl/gl.h>
|
|
||||||
|
|
||||||
#include <atlbase.h>
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <map>
|
|
||||||
#include <memory>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "DeckLinkAPI_h.h"
|
#include "DeckLinkAPI_h.h"
|
||||||
#include "VideoFrameTransfer.h"
|
|
||||||
|
|
||||||
extern "C" const IID IID_PinnedMemoryAllocator;
|
|
||||||
|
|
||||||
class OpenGLComposite;
|
class OpenGLComposite;
|
||||||
|
|
||||||
////////////////////////////////////////////
|
|
||||||
// PinnedMemoryAllocator
|
|
||||||
////////////////////////////////////////////
|
|
||||||
class PinnedMemoryAllocator : public IDeckLinkVideoBufferAllocator
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
PinnedMemoryAllocator(HDC hdc, HGLRC hglrc, VideoFrameTransfer::Direction direction, unsigned cacheSize, unsigned bufferSize);
|
|
||||||
virtual ~PinnedMemoryAllocator();
|
|
||||||
|
|
||||||
bool transferFrame(void* address, GLuint gpuTexture);
|
|
||||||
void waitForTransferComplete(void* address);
|
|
||||||
unsigned bufferSize() { return mBufferSize; }
|
|
||||||
|
|
||||||
// IUnknown methods
|
|
||||||
virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, LPVOID* ppv) override;
|
|
||||||
virtual ULONG STDMETHODCALLTYPE AddRef(void) override;
|
|
||||||
virtual ULONG STDMETHODCALLTYPE Release(void) override;
|
|
||||||
|
|
||||||
// IDeckLinkVideoBufferAllocator methods
|
|
||||||
virtual HRESULT STDMETHODCALLTYPE AllocateVideoBuffer(IDeckLinkVideoBuffer** allocatedBuffer) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
void unPinAddress(void* address);
|
|
||||||
|
|
||||||
private:
|
|
||||||
HDC mHGLDC;
|
|
||||||
HGLRC mHGLRC;
|
|
||||||
std::atomic<ULONG> mRefCount;
|
|
||||||
VideoFrameTransfer::Direction mDirection;
|
|
||||||
std::map<void*, VideoFrameTransfer*> mFrameTransfer;
|
|
||||||
unsigned mBufferSize;
|
|
||||||
std::vector<void*> mFrameCache;
|
|
||||||
unsigned mFrameCacheSize;
|
|
||||||
};
|
|
||||||
|
|
||||||
////////////////////////////////////////////
|
|
||||||
// InputAllocatorPool
|
|
||||||
////////////////////////////////////////////
|
|
||||||
class InputAllocatorPool : public IDeckLinkVideoBufferAllocatorProvider
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
InputAllocatorPool(HDC hdc, HGLRC hglrc);
|
|
||||||
|
|
||||||
// IUnknown interface
|
|
||||||
ULONG STDMETHODCALLTYPE AddRef() override;
|
|
||||||
ULONG STDMETHODCALLTYPE Release() override;
|
|
||||||
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void** ppv) override;
|
|
||||||
|
|
||||||
// IDeckLinkVideoBufferAllocatorProvider interface
|
|
||||||
HRESULT STDMETHODCALLTYPE GetVideoBufferAllocator(
|
|
||||||
/* [in] */ unsigned int bufferSize,
|
|
||||||
/* [in] */ unsigned int width,
|
|
||||||
/* [in] */ unsigned int height,
|
|
||||||
/* [in] */ unsigned int rowBytes,
|
|
||||||
/* [in] */ BMDPixelFormat pixelFormat,
|
|
||||||
/* [out] */ IDeckLinkVideoBufferAllocator** allocator) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::atomic<ULONG> mRefCount;
|
|
||||||
std::map<unsigned int, CComPtr<PinnedMemoryAllocator> > mAllocatorBySize;
|
|
||||||
HDC mHDC;
|
|
||||||
HGLRC mHGLRC;
|
|
||||||
};
|
|
||||||
|
|
||||||
////////////////////////////////////////////
|
|
||||||
// DeckLinkVideoBuffer
|
|
||||||
////////////////////////////////////////////
|
|
||||||
class DeckLinkVideoBuffer : public IDeckLinkVideoBuffer
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit DeckLinkVideoBuffer(std::shared_ptr<void>& buffer, PinnedMemoryAllocator* parent);
|
|
||||||
virtual ~DeckLinkVideoBuffer() = default;
|
|
||||||
|
|
||||||
// IUnknown interface
|
|
||||||
virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject) override;
|
|
||||||
virtual ULONG STDMETHODCALLTYPE AddRef(void) override;
|
|
||||||
virtual ULONG STDMETHODCALLTYPE Release(void) override;
|
|
||||||
|
|
||||||
// IDeckLinkVideoBuffer interface
|
|
||||||
virtual HRESULT STDMETHODCALLTYPE GetBytes(void** buffer) override;
|
|
||||||
virtual HRESULT STDMETHODCALLTYPE GetSize(uint64_t* size) override;
|
|
||||||
virtual HRESULT STDMETHODCALLTYPE StartAccess(BMDBufferAccessFlags flags) override;
|
|
||||||
virtual HRESULT STDMETHODCALLTYPE EndAccess(BMDBufferAccessFlags flags) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
CComPtr<PinnedMemoryAllocator> mParentAllocator;
|
|
||||||
std::atomic<ULONG> mRefCount;
|
|
||||||
std::shared_ptr<void> mBuffer;
|
|
||||||
};
|
|
||||||
|
|
||||||
////////////////////////////////////////////
|
////////////////////////////////////////////
|
||||||
// Capture Delegate Class
|
// Capture Delegate Class
|
||||||
////////////////////////////////////////////
|
////////////////////////////////////////////
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <new>
|
#include <new>
|
||||||
|
#include <sstream>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
@@ -25,6 +26,42 @@ std::string BstrToUtf8(BSTR value)
|
|||||||
|
|
||||||
return std::string(utf8Name.data());
|
return std::string(utf8Name.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool InputSupportsFormat(IDeckLinkInput* input, BMDDisplayMode displayMode, BMDPixelFormat pixelFormat)
|
||||||
|
{
|
||||||
|
if (input == nullptr)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
BOOL supported = FALSE;
|
||||||
|
BMDDisplayMode actualMode = bmdModeUnknown;
|
||||||
|
const HRESULT result = input->DoesSupportVideoMode(
|
||||||
|
bmdVideoConnectionUnspecified,
|
||||||
|
displayMode,
|
||||||
|
pixelFormat,
|
||||||
|
bmdNoVideoInputConversion,
|
||||||
|
bmdSupportedVideoModeDefault,
|
||||||
|
&actualMode,
|
||||||
|
&supported);
|
||||||
|
return result == S_OK && supported != FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OutputSupportsFormat(IDeckLinkOutput* output, BMDDisplayMode displayMode, BMDPixelFormat pixelFormat)
|
||||||
|
{
|
||||||
|
if (output == nullptr)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
BOOL supported = FALSE;
|
||||||
|
BMDDisplayMode actualMode = bmdModeUnknown;
|
||||||
|
const HRESULT result = output->DoesSupportVideoMode(
|
||||||
|
bmdVideoConnectionUnspecified,
|
||||||
|
displayMode,
|
||||||
|
pixelFormat,
|
||||||
|
bmdNoVideoOutputConversion,
|
||||||
|
bmdSupportedVideoModeDefault,
|
||||||
|
&actualMode,
|
||||||
|
&supported);
|
||||||
|
return result == S_OK && supported != FALSE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DeckLinkSession::~DeckLinkSession()
|
DeckLinkSession::~DeckLinkSession()
|
||||||
@@ -52,8 +89,6 @@ void DeckLinkSession::ReleaseResources()
|
|||||||
playoutDelegate.Release();
|
playoutDelegate.Release();
|
||||||
outputVideoFrameQueue.clear();
|
outputVideoFrameQueue.clear();
|
||||||
output.Release();
|
output.Release();
|
||||||
|
|
||||||
playoutAllocator.Release();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DeckLinkSession::DiscoverDevicesAndModes(const VideoFormatSelection& videoModes, std::string& error)
|
bool DeckLinkSession::DiscoverDevicesAndModes(const VideoFormatSelection& videoModes, std::string& error)
|
||||||
@@ -173,11 +208,75 @@ bool DeckLinkSession::DiscoverDevicesAndModes(const VideoFormatSelection& videoM
|
|||||||
inputDisplayModeName = "No input - black frame";
|
inputDisplayModeName = "No input - black frame";
|
||||||
outputMode->GetFrameRate(&frameDuration, &frameTimescale);
|
outputMode->GetFrameRate(&frameDuration, &frameTimescale);
|
||||||
|
|
||||||
|
inputFrameRowBytes = inputFrameSize.width * 2u;
|
||||||
|
outputFrameRowBytes = outputFrameSize.width * 4u;
|
||||||
|
captureTextureWidth = inputFrameSize.width / 2u;
|
||||||
|
outputPackTextureWidth = outputFrameSize.width;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DeckLinkSession::SelectPreferredFormats(const VideoFormatSelection& videoModes, std::string& error)
|
||||||
|
{
|
||||||
|
if (!output)
|
||||||
|
{
|
||||||
|
error = "Expected an Output DeckLink device";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
formatStatusMessage.clear();
|
||||||
|
|
||||||
|
const bool inputTenBitSupported = input != nullptr && InputSupportsFormat(input, videoModes.input.displayMode, bmdFormat10BitYUV);
|
||||||
|
inputPixelFormat = input != nullptr ? ChoosePreferredVideoIOFormat(inputTenBitSupported) : VideoIOPixelFormat::Uyvy8;
|
||||||
|
if (input != nullptr && !inputTenBitSupported)
|
||||||
|
formatStatusMessage += "DeckLink input does not report 10-bit YUV support for the configured mode; using 8-bit capture. ";
|
||||||
|
|
||||||
|
const bool outputTenBitSupported = OutputSupportsFormat(output, videoModes.output.displayMode, bmdFormat10BitYUV);
|
||||||
|
outputPixelFormat = ChoosePreferredVideoIOFormat(outputTenBitSupported);
|
||||||
|
if (!outputTenBitSupported)
|
||||||
|
formatStatusMessage += "DeckLink output does not report 10-bit YUV support for the configured mode; using 8-bit BGRA output. ";
|
||||||
|
|
||||||
|
int deckLinkOutputRowBytes = 0;
|
||||||
|
if (output->RowBytesForPixelFormat(OutputIsTenBit() ? bmdFormat10BitYUV : bmdFormat8BitBGRA, outputFrameSize.width, &deckLinkOutputRowBytes) != S_OK)
|
||||||
|
{
|
||||||
|
error = "DeckLink output setup failed while calculating output row bytes.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
outputFrameRowBytes = static_cast<unsigned>(deckLinkOutputRowBytes);
|
||||||
|
outputPackTextureWidth = OutputIsTenBit()
|
||||||
|
? PackedTextureWidthFromRowBytes(outputFrameRowBytes)
|
||||||
|
: outputFrameSize.width;
|
||||||
|
|
||||||
|
if (InputIsTenBit())
|
||||||
|
{
|
||||||
|
int deckLinkInputRowBytes = 0;
|
||||||
|
if (output->RowBytesForPixelFormat(bmdFormat10BitYUV, inputFrameSize.width, &deckLinkInputRowBytes) == S_OK)
|
||||||
|
inputFrameRowBytes = static_cast<unsigned>(deckLinkInputRowBytes);
|
||||||
|
else
|
||||||
|
inputFrameRowBytes = MinimumV210RowBytes(inputFrameSize.width);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
inputFrameRowBytes = inputFrameSize.width * 2u;
|
||||||
|
}
|
||||||
|
captureTextureWidth = InputIsTenBit()
|
||||||
|
? PackedTextureWidthFromRowBytes(inputFrameRowBytes)
|
||||||
|
: inputFrameSize.width / 2u;
|
||||||
|
|
||||||
|
std::ostringstream status;
|
||||||
|
status << "DeckLink formats: capture " << (input ? VideoIOPixelFormatName(inputPixelFormat) : "none")
|
||||||
|
<< ", output " << (OutputIsTenBit() ? "10-bit YUV v210" : "8-bit BGRA") << ".";
|
||||||
|
if (!formatStatusMessage.empty())
|
||||||
|
status << " " << formatStatusMessage;
|
||||||
|
formatStatusMessage = status.str();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DeckLinkSession::ConfigureInput(OpenGLComposite* owner, HDC hdc, HGLRC hglrc, const VideoFormat& inputVideoMode, std::string& error)
|
bool DeckLinkSession::ConfigureInput(OpenGLComposite* owner, HDC hdc, HGLRC hglrc, const VideoFormat& inputVideoMode, std::string& error)
|
||||||
{
|
{
|
||||||
|
(void)hdc;
|
||||||
|
(void)hglrc;
|
||||||
|
|
||||||
if (!input)
|
if (!input)
|
||||||
{
|
{
|
||||||
hasNoInputSource = true;
|
hasNoInputSource = true;
|
||||||
@@ -185,10 +284,26 @@ bool DeckLinkSession::ConfigureInput(OpenGLComposite* owner, HDC hdc, HGLRC hglr
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
CComPtr<IDeckLinkVideoBufferAllocatorProvider> captureAllocator(new (std::nothrow) InputAllocatorPool(hdc, hglrc));
|
const BMDPixelFormat deckLinkInputPixelFormat = DeckLinkPixelFormatForVideoIO(inputPixelFormat);
|
||||||
|
if (input->EnableVideoInput(inputVideoMode.displayMode, deckLinkInputPixelFormat, bmdVideoInputFlagDefault) != S_OK)
|
||||||
if (input->EnableVideoInputWithAllocatorProvider(inputVideoMode.displayMode, bmdFormat8BitYUV, bmdVideoInputFlagDefault, captureAllocator) != S_OK)
|
|
||||||
{
|
{
|
||||||
|
if (inputPixelFormat == VideoIOPixelFormat::V210)
|
||||||
|
{
|
||||||
|
OutputDebugStringA("DeckLink 10-bit input could not be enabled; falling back to 8-bit capture.\n");
|
||||||
|
inputPixelFormat = VideoIOPixelFormat::Uyvy8;
|
||||||
|
inputFrameRowBytes = inputFrameSize.width * 2u;
|
||||||
|
captureTextureWidth = inputFrameSize.width / 2u;
|
||||||
|
if (input->EnableVideoInput(inputVideoMode.displayMode, bmdFormat8BitYUV, bmdVideoInputFlagDefault) == S_OK)
|
||||||
|
{
|
||||||
|
std::ostringstream status;
|
||||||
|
status << "DeckLink formats: capture " << VideoIOPixelFormatName(inputPixelFormat)
|
||||||
|
<< ", output " << (OutputIsTenBit() ? "10-bit YUV v210" : "8-bit BGRA")
|
||||||
|
<< ". DeckLink 10-bit input enable failed; using 8-bit capture.";
|
||||||
|
formatStatusMessage = status.str();
|
||||||
|
goto input_enabled;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
OutputDebugStringA("DeckLink input could not be enabled; continuing in output-only black-frame mode.\n");
|
OutputDebugStringA("DeckLink input could not be enabled; continuing in output-only black-frame mode.\n");
|
||||||
input.Release();
|
input.Release();
|
||||||
hasNoInputSource = true;
|
hasNoInputSource = true;
|
||||||
@@ -196,6 +311,7 @@ bool DeckLinkSession::ConfigureInput(OpenGLComposite* owner, HDC hdc, HGLRC hglr
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input_enabled:
|
||||||
captureDelegate.Attach(new (std::nothrow) CaptureDelegate(owner));
|
captureDelegate.Attach(new (std::nothrow) CaptureDelegate(owner));
|
||||||
if (captureDelegate == nullptr)
|
if (captureDelegate == nullptr)
|
||||||
{
|
{
|
||||||
@@ -213,19 +329,8 @@ bool DeckLinkSession::ConfigureInput(OpenGLComposite* owner, HDC hdc, HGLRC hglr
|
|||||||
|
|
||||||
bool DeckLinkSession::ConfigureOutput(OpenGLComposite* owner, HDC hdc, HGLRC hglrc, const VideoFormat& outputVideoMode, bool externalKeyingEnabled, std::string& error)
|
bool DeckLinkSession::ConfigureOutput(OpenGLComposite* owner, HDC hdc, HGLRC hglrc, const VideoFormat& outputVideoMode, bool externalKeyingEnabled, std::string& error)
|
||||||
{
|
{
|
||||||
int outputFrameRowBytes = 0;
|
(void)hdc;
|
||||||
if (output->RowBytesForPixelFormat(bmdFormat8BitBGRA, outputFrameSize.width, &outputFrameRowBytes) != S_OK)
|
(void)hglrc;
|
||||||
{
|
|
||||||
error = "DeckLink output setup failed while calculating BGRA row bytes.";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
playoutAllocator.Attach(new (std::nothrow) PinnedMemoryAllocator(hdc, hglrc, VideoFrameTransfer::GPUtoCPU, 1, outputFrameRowBytes * outputFrameSize.height));
|
|
||||||
if (playoutAllocator == nullptr)
|
|
||||||
{
|
|
||||||
error = "DeckLink output setup failed while creating the playout allocator.";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (output->EnableVideoOutput(outputVideoMode.displayMode, bmdVideoOutputFlagDefault) != S_OK)
|
if (output->EnableVideoOutput(outputVideoMode.displayMode, bmdVideoOutputFlagDefault) != S_OK)
|
||||||
{
|
{
|
||||||
@@ -264,15 +369,9 @@ bool DeckLinkSession::ConfigureOutput(OpenGLComposite* owner, HDC hdc, HGLRC hgl
|
|||||||
for (int i = 0; i < 10; i++)
|
for (int i = 0; i < 10; i++)
|
||||||
{
|
{
|
||||||
CComPtr<IDeckLinkMutableVideoFrame> outputFrame;
|
CComPtr<IDeckLinkMutableVideoFrame> outputFrame;
|
||||||
CComPtr<IDeckLinkVideoBuffer> outputFrameBuffer;
|
|
||||||
|
|
||||||
if (playoutAllocator->AllocateVideoBuffer(&outputFrameBuffer) != S_OK)
|
const BMDPixelFormat deckLinkOutputPixelFormat = OutputIsTenBit() ? bmdFormat10BitYUV : bmdFormat8BitBGRA;
|
||||||
{
|
if (output->CreateVideoFrame(outputFrameSize.width, outputFrameSize.height, outputFrameRowBytes, deckLinkOutputPixelFormat, bmdFrameFlagFlipVertical, &outputFrame) != S_OK)
|
||||||
error = "DeckLink output setup failed while allocating an output frame buffer.";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (output->CreateVideoFrameWithBuffer(outputFrameSize.width, outputFrameSize.height, outputFrameRowBytes, bmdFormat8BitBGRA, bmdFrameFlagFlipVertical, outputFrameBuffer, &outputFrame) != S_OK)
|
|
||||||
{
|
{
|
||||||
error = "DeckLink output setup failed while creating an output video frame.";
|
error = "DeckLink output setup failed while creating an output video frame.";
|
||||||
return false;
|
return false;
|
||||||
@@ -294,6 +393,9 @@ bool DeckLinkSession::ConfigureOutput(OpenGLComposite* owner, HDC hdc, HGLRC hgl
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!formatStatusMessage.empty())
|
||||||
|
statusMessage = statusMessage.empty() ? formatStatusMessage : formatStatusMessage + " " + statusMessage;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -312,17 +414,6 @@ IDeckLinkMutableVideoFrame* DeckLinkSession::RotateOutputFrame()
|
|||||||
return outputVideoFrame.p;
|
return outputVideoFrame.p;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DeckLinkSession::TransferPlayoutFrame(void* address, GLuint outputTexture)
|
|
||||||
{
|
|
||||||
return playoutAllocator != nullptr && playoutAllocator->transferFrame(address, outputTexture);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DeckLinkSession::WaitForPlayoutTransferComplete(void* address)
|
|
||||||
{
|
|
||||||
if (playoutAllocator != nullptr)
|
|
||||||
playoutAllocator->waitForTransferComplete(address);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DeckLinkSession::AccountForCompletionResult(BMDOutputFrameCompletionResult completionResult)
|
void DeckLinkSession::AccountForCompletionResult(BMDOutputFrameCompletionResult completionResult)
|
||||||
{
|
{
|
||||||
if (completionResult == bmdOutputFrameDisplayedLate || completionResult == bmdOutputFrameDropped)
|
if (completionResult == bmdOutputFrameDisplayedLate || completionResult == bmdOutputFrameDropped)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#include "DeckLinkAPI_h.h"
|
#include "DeckLinkAPI_h.h"
|
||||||
#include "DeckLinkDisplayMode.h"
|
#include "DeckLinkDisplayMode.h"
|
||||||
#include "DeckLinkFrameTransfer.h"
|
#include "DeckLinkFrameTransfer.h"
|
||||||
|
#include "VideoIOFormat.h"
|
||||||
|
|
||||||
#include <atlbase.h>
|
#include <atlbase.h>
|
||||||
#include <deque>
|
#include <deque>
|
||||||
@@ -18,6 +19,7 @@ public:
|
|||||||
|
|
||||||
void ReleaseResources();
|
void ReleaseResources();
|
||||||
bool DiscoverDevicesAndModes(const VideoFormatSelection& videoModes, std::string& error);
|
bool DiscoverDevicesAndModes(const VideoFormatSelection& videoModes, std::string& error);
|
||||||
|
bool SelectPreferredFormats(const VideoFormatSelection& videoModes, std::string& error);
|
||||||
bool ConfigureInput(OpenGLComposite* owner, HDC hdc, HGLRC hglrc, const VideoFormat& inputVideoMode, std::string& error);
|
bool ConfigureInput(OpenGLComposite* owner, HDC hdc, HGLRC hglrc, const VideoFormat& inputVideoMode, std::string& error);
|
||||||
bool ConfigureOutput(OpenGLComposite* owner, HDC hdc, HGLRC hglrc, const VideoFormat& outputVideoMode, bool externalKeyingEnabled, std::string& error);
|
bool ConfigureOutput(OpenGLComposite* owner, HDC hdc, HGLRC hglrc, const VideoFormat& outputVideoMode, bool externalKeyingEnabled, std::string& error);
|
||||||
bool Start();
|
bool Start();
|
||||||
@@ -33,6 +35,15 @@ public:
|
|||||||
unsigned InputFrameHeight() const { return inputFrameSize.height; }
|
unsigned InputFrameHeight() const { return inputFrameSize.height; }
|
||||||
unsigned OutputFrameWidth() const { return outputFrameSize.width; }
|
unsigned OutputFrameWidth() const { return outputFrameSize.width; }
|
||||||
unsigned OutputFrameHeight() const { return outputFrameSize.height; }
|
unsigned OutputFrameHeight() const { return outputFrameSize.height; }
|
||||||
|
VideoIOPixelFormat InputPixelFormat() const { return inputPixelFormat; }
|
||||||
|
VideoIOPixelFormat OutputPixelFormat() const { return outputPixelFormat; }
|
||||||
|
bool InputIsTenBit() const { return VideoIOPixelFormatIsTenBit(inputPixelFormat); }
|
||||||
|
bool OutputIsTenBit() const { return VideoIOPixelFormatIsTenBit(outputPixelFormat); }
|
||||||
|
unsigned InputFrameRowBytes() const { return inputFrameRowBytes; }
|
||||||
|
unsigned OutputFrameRowBytes() const { return outputFrameRowBytes; }
|
||||||
|
unsigned CaptureTextureWidth() const { return captureTextureWidth; }
|
||||||
|
unsigned OutputPackTextureWidth() const { return outputPackTextureWidth; }
|
||||||
|
const std::string& FormatStatusMessage() const { return formatStatusMessage; }
|
||||||
const std::string& InputDisplayModeName() const { return inputDisplayModeName; }
|
const std::string& InputDisplayModeName() const { return inputDisplayModeName; }
|
||||||
const std::string& OutputModelName() const { return outputModelName; }
|
const std::string& OutputModelName() const { return outputModelName; }
|
||||||
bool SupportsInternalKeying() const { return supportsInternalKeying; }
|
bool SupportsInternalKeying() const { return supportsInternalKeying; }
|
||||||
@@ -43,8 +54,6 @@ public:
|
|||||||
void SetStatusMessage(const std::string& message) { statusMessage = message; }
|
void SetStatusMessage(const std::string& message) { statusMessage = message; }
|
||||||
double FrameBudgetMilliseconds() const;
|
double FrameBudgetMilliseconds() const;
|
||||||
IDeckLinkMutableVideoFrame* RotateOutputFrame();
|
IDeckLinkMutableVideoFrame* RotateOutputFrame();
|
||||||
bool TransferPlayoutFrame(void* address, GLuint outputTexture);
|
|
||||||
void WaitForPlayoutTransferComplete(void* address);
|
|
||||||
void AccountForCompletionResult(BMDOutputFrameCompletionResult completionResult);
|
void AccountForCompletionResult(BMDOutputFrameCompletionResult completionResult);
|
||||||
bool ScheduleOutputFrame(IDeckLinkMutableVideoFrame* outputVideoFrame);
|
bool ScheduleOutputFrame(IDeckLinkMutableVideoFrame* outputVideoFrame);
|
||||||
|
|
||||||
@@ -55,12 +64,17 @@ private:
|
|||||||
CComPtr<IDeckLinkOutput> output;
|
CComPtr<IDeckLinkOutput> output;
|
||||||
CComPtr<IDeckLinkKeyer> keyer;
|
CComPtr<IDeckLinkKeyer> keyer;
|
||||||
std::deque<CComPtr<IDeckLinkMutableVideoFrame>> outputVideoFrameQueue;
|
std::deque<CComPtr<IDeckLinkMutableVideoFrame>> outputVideoFrameQueue;
|
||||||
CComPtr<PinnedMemoryAllocator> playoutAllocator;
|
|
||||||
BMDTimeValue frameDuration = 0;
|
BMDTimeValue frameDuration = 0;
|
||||||
BMDTimeScale frameTimescale = 0;
|
BMDTimeScale frameTimescale = 0;
|
||||||
unsigned totalPlayoutFrames = 0;
|
unsigned totalPlayoutFrames = 0;
|
||||||
FrameSize inputFrameSize;
|
FrameSize inputFrameSize;
|
||||||
FrameSize outputFrameSize;
|
FrameSize outputFrameSize;
|
||||||
|
VideoIOPixelFormat inputPixelFormat = VideoIOPixelFormat::Uyvy8;
|
||||||
|
VideoIOPixelFormat outputPixelFormat = VideoIOPixelFormat::Uyvy8;
|
||||||
|
unsigned inputFrameRowBytes = 0;
|
||||||
|
unsigned outputFrameRowBytes = 0;
|
||||||
|
unsigned captureTextureWidth = 0;
|
||||||
|
unsigned outputPackTextureWidth = 0;
|
||||||
std::string inputDisplayModeName = "1080p59.94";
|
std::string inputDisplayModeName = "1080p59.94";
|
||||||
std::string outputDisplayModeName = "1080p59.94";
|
std::string outputDisplayModeName = "1080p59.94";
|
||||||
bool hasNoInputSource = true;
|
bool hasNoInputSource = true;
|
||||||
@@ -70,4 +84,5 @@ private:
|
|||||||
bool keyerInterfaceAvailable = false;
|
bool keyerInterfaceAvailable = false;
|
||||||
bool externalKeyingActive = false;
|
bool externalKeyingActive = false;
|
||||||
std::string statusMessage;
|
std::string statusMessage;
|
||||||
|
std::string formatStatusMessage;
|
||||||
};
|
};
|
||||||
|
|||||||
139
apps/LoopThroughWithOpenGLCompositing/decklink/VideoIOFormat.cpp
Normal file
139
apps/LoopThroughWithOpenGLCompositing/decklink/VideoIOFormat.cpp
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
#include "VideoIOFormat.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
#ifdef min
|
||||||
|
#undef min
|
||||||
|
#endif
|
||||||
|
#ifdef max
|
||||||
|
#undef max
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
uint16_t Clamp10(int value, int minimum, int maximum)
|
||||||
|
{
|
||||||
|
return static_cast<uint16_t>(std::max(minimum, std::min(maximum, value)));
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t MakeV210Word(uint16_t a, uint16_t b, uint16_t c)
|
||||||
|
{
|
||||||
|
return (static_cast<uint32_t>(a) & 0x3ffu)
|
||||||
|
| ((static_cast<uint32_t>(b) & 0x3ffu) << 10)
|
||||||
|
| ((static_cast<uint32_t>(c) & 0x3ffu) << 20);
|
||||||
|
}
|
||||||
|
|
||||||
|
void StoreWord(std::array<uint8_t, 16>& bytes, std::size_t wordIndex, uint32_t word)
|
||||||
|
{
|
||||||
|
const std::size_t offset = wordIndex * 4;
|
||||||
|
bytes[offset + 0] = static_cast<uint8_t>(word & 0xffu);
|
||||||
|
bytes[offset + 1] = static_cast<uint8_t>((word >> 8) & 0xffu);
|
||||||
|
bytes[offset + 2] = static_cast<uint8_t>((word >> 16) & 0xffu);
|
||||||
|
bytes[offset + 3] = static_cast<uint8_t>((word >> 24) & 0xffu);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t LoadWord(const std::array<uint8_t, 16>& bytes, std::size_t wordIndex)
|
||||||
|
{
|
||||||
|
const std::size_t offset = wordIndex * 4;
|
||||||
|
return static_cast<uint32_t>(bytes[offset + 0])
|
||||||
|
| (static_cast<uint32_t>(bytes[offset + 1]) << 8)
|
||||||
|
| (static_cast<uint32_t>(bytes[offset + 2]) << 16)
|
||||||
|
| (static_cast<uint32_t>(bytes[offset + 3]) << 24);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t Component(uint32_t word, unsigned index)
|
||||||
|
{
|
||||||
|
return static_cast<uint16_t>((word >> (index * 10)) & 0x3ffu);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* VideoIOPixelFormatName(VideoIOPixelFormat format)
|
||||||
|
{
|
||||||
|
return format == VideoIOPixelFormat::V210 ? "10-bit YUV v210" : "8-bit YUV UYVY";
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VideoIOPixelFormatIsTenBit(VideoIOPixelFormat format)
|
||||||
|
{
|
||||||
|
return format == VideoIOPixelFormat::V210;
|
||||||
|
}
|
||||||
|
|
||||||
|
BMDPixelFormat DeckLinkPixelFormatForVideoIO(VideoIOPixelFormat format)
|
||||||
|
{
|
||||||
|
return format == VideoIOPixelFormat::V210 ? bmdFormat10BitYUV : bmdFormat8BitYUV;
|
||||||
|
}
|
||||||
|
|
||||||
|
VideoIOPixelFormat VideoIOPixelFormatFromDeckLink(BMDPixelFormat format)
|
||||||
|
{
|
||||||
|
return format == bmdFormat10BitYUV ? VideoIOPixelFormat::V210 : VideoIOPixelFormat::Uyvy8;
|
||||||
|
}
|
||||||
|
|
||||||
|
VideoIOPixelFormat ChoosePreferredVideoIOFormat(bool tenBitSupported)
|
||||||
|
{
|
||||||
|
return tenBitSupported ? VideoIOPixelFormat::V210 : VideoIOPixelFormat::Uyvy8;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned PackedTextureWidthFromRowBytes(unsigned rowBytes)
|
||||||
|
{
|
||||||
|
return (rowBytes + 3u) / 4u;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned MinimumV210RowBytes(unsigned frameWidth)
|
||||||
|
{
|
||||||
|
return ((frameWidth + 5u) / 6u) * 16u;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned ActiveV210WordsForWidth(unsigned frameWidth)
|
||||||
|
{
|
||||||
|
return ((frameWidth + 5u) / 6u) * 4u;
|
||||||
|
}
|
||||||
|
|
||||||
|
V210CodeValues Rec709RgbToLegalV210(float red, float green, float blue)
|
||||||
|
{
|
||||||
|
red = std::max(0.0f, std::min(1.0f, red));
|
||||||
|
green = std::max(0.0f, std::min(1.0f, green));
|
||||||
|
blue = std::max(0.0f, std::min(1.0f, blue));
|
||||||
|
|
||||||
|
const float y = 0.2126f * red + 0.7152f * green + 0.0722f * blue;
|
||||||
|
const float cb = (blue - y) / 1.8556f + 0.5f;
|
||||||
|
const float cr = (red - y) / 1.5748f + 0.5f;
|
||||||
|
|
||||||
|
V210CodeValues values;
|
||||||
|
values.y = Clamp10(static_cast<int>(std::lround(64.0f + y * 876.0f)), 64, 940);
|
||||||
|
values.cb = Clamp10(static_cast<int>(std::lround(64.0f + cb * 896.0f)), 64, 960);
|
||||||
|
values.cr = Clamp10(static_cast<int>(std::lround(64.0f + cr * 896.0f)), 64, 960);
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::array<uint8_t, 16> PackV210Block(const V210SixPixelBlock& block)
|
||||||
|
{
|
||||||
|
std::array<uint8_t, 16> bytes = {};
|
||||||
|
StoreWord(bytes, 0, MakeV210Word(block.cb[0], block.y[0], block.cr[0]));
|
||||||
|
StoreWord(bytes, 1, MakeV210Word(block.y[1], block.cb[1], block.y[2]));
|
||||||
|
StoreWord(bytes, 2, MakeV210Word(block.cr[1], block.y[3], block.cb[2]));
|
||||||
|
StoreWord(bytes, 3, MakeV210Word(block.y[4], block.cr[2], block.y[5]));
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
V210SixPixelBlock UnpackV210Block(const std::array<uint8_t, 16>& bytes)
|
||||||
|
{
|
||||||
|
const uint32_t word0 = LoadWord(bytes, 0);
|
||||||
|
const uint32_t word1 = LoadWord(bytes, 1);
|
||||||
|
const uint32_t word2 = LoadWord(bytes, 2);
|
||||||
|
const uint32_t word3 = LoadWord(bytes, 3);
|
||||||
|
|
||||||
|
V210SixPixelBlock block;
|
||||||
|
block.cb[0] = Component(word0, 0);
|
||||||
|
block.y[0] = Component(word0, 1);
|
||||||
|
block.cr[0] = Component(word0, 2);
|
||||||
|
block.y[1] = Component(word1, 0);
|
||||||
|
block.cb[1] = Component(word1, 1);
|
||||||
|
block.y[2] = Component(word1, 2);
|
||||||
|
block.cr[1] = Component(word2, 0);
|
||||||
|
block.y[3] = Component(word2, 1);
|
||||||
|
block.cb[2] = Component(word2, 2);
|
||||||
|
block.y[4] = Component(word3, 0);
|
||||||
|
block.cr[2] = Component(word3, 1);
|
||||||
|
block.y[5] = Component(word3, 2);
|
||||||
|
return block;
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "DeckLinkAPI_h.h"
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
enum class VideoIOPixelFormat
|
||||||
|
{
|
||||||
|
Uyvy8,
|
||||||
|
V210
|
||||||
|
};
|
||||||
|
|
||||||
|
struct V210CodeValues
|
||||||
|
{
|
||||||
|
uint16_t y = 64;
|
||||||
|
uint16_t cb = 512;
|
||||||
|
uint16_t cr = 512;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct V210SixPixelBlock
|
||||||
|
{
|
||||||
|
std::array<uint16_t, 6> y = {};
|
||||||
|
std::array<uint16_t, 3> cb = {};
|
||||||
|
std::array<uint16_t, 3> cr = {};
|
||||||
|
};
|
||||||
|
|
||||||
|
const char* VideoIOPixelFormatName(VideoIOPixelFormat format);
|
||||||
|
bool VideoIOPixelFormatIsTenBit(VideoIOPixelFormat format);
|
||||||
|
BMDPixelFormat DeckLinkPixelFormatForVideoIO(VideoIOPixelFormat format);
|
||||||
|
VideoIOPixelFormat VideoIOPixelFormatFromDeckLink(BMDPixelFormat format);
|
||||||
|
VideoIOPixelFormat ChoosePreferredVideoIOFormat(bool tenBitSupported);
|
||||||
|
unsigned PackedTextureWidthFromRowBytes(unsigned rowBytes);
|
||||||
|
unsigned MinimumV210RowBytes(unsigned frameWidth);
|
||||||
|
unsigned ActiveV210WordsForWidth(unsigned frameWidth);
|
||||||
|
V210CodeValues Rec709RgbToLegalV210(float red, float green, float blue);
|
||||||
|
std::array<uint8_t, 16> PackV210Block(const V210SixPixelBlock& block);
|
||||||
|
V210SixPixelBlock UnpackV210Block(const std::array<uint8_t, 16>& bytes);
|
||||||
@@ -60,6 +60,9 @@
|
|||||||
#define GL_DYNAMIC_DRAW 0x88E8
|
#define GL_DYNAMIC_DRAW 0x88E8
|
||||||
#define GL_UNIFORM_BUFFER 0x8A11
|
#define GL_UNIFORM_BUFFER 0x8A11
|
||||||
#define GL_RGBA8 0x8058
|
#define GL_RGBA8 0x8058
|
||||||
|
#define GL_RGBA16F 0x881A
|
||||||
|
#define GL_TEXTURE0 0x84C0
|
||||||
|
#define GL_ACTIVE_TEXTURE 0x84E0
|
||||||
#define GL_ARRAY_BUFFER 0x8892
|
#define GL_ARRAY_BUFFER 0x8892
|
||||||
#define GL_PIXEL_PACK_BUFFER 0x88EB
|
#define GL_PIXEL_PACK_BUFFER 0x88EB
|
||||||
#define GL_PIXEL_UNPACK_BUFFER 0x88EC
|
#define GL_PIXEL_UNPACK_BUFFER 0x88EC
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ const char* kDecodeFragmentShaderSource =
|
|||||||
"layout(binding = 2) uniform sampler2D uPackedVideoInput;\n"
|
"layout(binding = 2) uniform sampler2D uPackedVideoInput;\n"
|
||||||
"uniform vec2 uPackedVideoResolution;\n"
|
"uniform vec2 uPackedVideoResolution;\n"
|
||||||
"uniform vec2 uDecodedVideoResolution;\n"
|
"uniform vec2 uDecodedVideoResolution;\n"
|
||||||
|
"uniform int uInputPixelFormat;\n"
|
||||||
"in vec2 vTexCoord;\n"
|
"in vec2 vTexCoord;\n"
|
||||||
"layout(location = 0) out vec4 fragColor;\n"
|
"layout(location = 0) out vec4 fragColor;\n"
|
||||||
"vec4 rec709YCbCr2rgba(float Y, float Cb, float Cr, float a)\n"
|
"vec4 rec709YCbCr2rgba(float Y, float Cb, float Cr, float a)\n"
|
||||||
@@ -25,14 +26,116 @@ const char* kDecodeFragmentShaderSource =
|
|||||||
" Cr = (Cr * 256.0 - 16.0) / 224.0 - 0.5;\n"
|
" Cr = (Cr * 256.0 - 16.0) / 224.0 - 0.5;\n"
|
||||||
" return vec4(Y + 1.5748 * Cr, Y - 0.1873 * Cb - 0.4681 * Cr, Y + 1.8556 * Cb, a);\n"
|
" return vec4(Y + 1.5748 * Cr, Y - 0.1873 * Cb - 0.4681 * Cr, Y + 1.8556 * Cb, a);\n"
|
||||||
"}\n"
|
"}\n"
|
||||||
|
"vec4 rec709YCbCr10_2rgba(float Y, float Cb, float Cr, float a)\n"
|
||||||
|
"{\n"
|
||||||
|
" Y = (Y - 64.0) / 876.0;\n"
|
||||||
|
" Cb = (Cb - 64.0) / 896.0 - 0.5;\n"
|
||||||
|
" Cr = (Cr - 64.0) / 896.0 - 0.5;\n"
|
||||||
|
" return vec4(Y + 1.5748 * Cr, Y - 0.1873 * Cb - 0.4681 * Cr, Y + 1.8556 * Cb, a);\n"
|
||||||
|
"}\n"
|
||||||
|
"uint loadV210Word(ivec2 coord)\n"
|
||||||
|
"{\n"
|
||||||
|
" vec4 b = round(texelFetch(uPackedVideoInput, coord, 0) * 255.0);\n"
|
||||||
|
" return uint(b.r) | (uint(b.g) << 8) | (uint(b.b) << 16) | (uint(b.a) << 24);\n"
|
||||||
|
"}\n"
|
||||||
|
"float v210Component(uint word, int index)\n"
|
||||||
|
"{\n"
|
||||||
|
" return float((word >> uint(index * 10)) & 1023u);\n"
|
||||||
|
"}\n"
|
||||||
|
"vec4 decodeUyvy8(ivec2 outputCoord, ivec2 packedSize)\n"
|
||||||
|
"{\n"
|
||||||
|
" ivec2 packedCoord = ivec2(clamp(outputCoord.x / 2, 0, packedSize.x - 1), clamp(outputCoord.y, 0, packedSize.y - 1));\n"
|
||||||
|
" vec4 macroPixel = texelFetch(uPackedVideoInput, packedCoord, 0);\n"
|
||||||
|
" float ySample = (outputCoord.x & 1) != 0 ? macroPixel.a : macroPixel.g;\n"
|
||||||
|
" return rec709YCbCr2rgba(ySample, macroPixel.b, macroPixel.r, 1.0);\n"
|
||||||
|
"}\n"
|
||||||
|
"vec4 decodeV210(ivec2 outputCoord, ivec2 packedSize)\n"
|
||||||
|
"{\n"
|
||||||
|
" int group = outputCoord.x / 6;\n"
|
||||||
|
" int pixel = outputCoord.x - group * 6;\n"
|
||||||
|
" int wordBase = group * 4;\n"
|
||||||
|
" ivec2 rowBase = ivec2(wordBase, clamp(outputCoord.y, 0, packedSize.y - 1));\n"
|
||||||
|
" uint w0 = loadV210Word(ivec2(min(rowBase.x + 0, packedSize.x - 1), rowBase.y));\n"
|
||||||
|
" uint w1 = loadV210Word(ivec2(min(rowBase.x + 1, packedSize.x - 1), rowBase.y));\n"
|
||||||
|
" uint w2 = loadV210Word(ivec2(min(rowBase.x + 2, packedSize.x - 1), rowBase.y));\n"
|
||||||
|
" uint w3 = loadV210Word(ivec2(min(rowBase.x + 3, packedSize.x - 1), rowBase.y));\n"
|
||||||
|
" float y0 = v210Component(w0, 1);\n"
|
||||||
|
" float y1 = v210Component(w1, 0);\n"
|
||||||
|
" float y2 = v210Component(w1, 2);\n"
|
||||||
|
" float y3 = v210Component(w2, 1);\n"
|
||||||
|
" float y4 = v210Component(w3, 0);\n"
|
||||||
|
" float y5 = v210Component(w3, 2);\n"
|
||||||
|
" float cb0 = v210Component(w0, 0);\n"
|
||||||
|
" float cr0 = v210Component(w0, 2);\n"
|
||||||
|
" float cb2 = v210Component(w1, 1);\n"
|
||||||
|
" float cr2 = v210Component(w2, 0);\n"
|
||||||
|
" float cb4 = v210Component(w2, 2);\n"
|
||||||
|
" float cr4 = v210Component(w3, 1);\n"
|
||||||
|
" float ySample = pixel == 0 ? y0 : pixel == 1 ? y1 : pixel == 2 ? y2 : pixel == 3 ? y3 : pixel == 4 ? y4 : y5;\n"
|
||||||
|
" float cbSample = pixel < 2 ? cb0 : pixel < 4 ? cb2 : cb4;\n"
|
||||||
|
" float crSample = pixel < 2 ? cr0 : pixel < 4 ? cr2 : cr4;\n"
|
||||||
|
" return rec709YCbCr10_2rgba(ySample, cbSample, crSample, 1.0);\n"
|
||||||
|
"}\n"
|
||||||
"void main()\n"
|
"void main()\n"
|
||||||
"{\n"
|
"{\n"
|
||||||
" vec2 correctedUv = vec2(vTexCoord.x, 1.0 - vTexCoord.y);\n"
|
" vec2 correctedUv = vec2(vTexCoord.x, 1.0 - vTexCoord.y);\n"
|
||||||
" ivec2 decodedSize = ivec2(max(uDecodedVideoResolution, vec2(1.0, 1.0)));\n"
|
" ivec2 decodedSize = ivec2(max(uDecodedVideoResolution, vec2(1.0, 1.0)));\n"
|
||||||
" ivec2 outputCoord = clamp(ivec2(correctedUv * vec2(decodedSize)), ivec2(0, 0), decodedSize - ivec2(1, 1));\n"
|
" ivec2 outputCoord = clamp(ivec2(correctedUv * vec2(decodedSize)), ivec2(0, 0), decodedSize - ivec2(1, 1));\n"
|
||||||
" ivec2 packedSize = ivec2(max(uPackedVideoResolution, vec2(1.0, 1.0)));\n"
|
" ivec2 packedSize = ivec2(max(uPackedVideoResolution, vec2(1.0, 1.0)));\n"
|
||||||
" ivec2 packedCoord = ivec2(clamp(outputCoord.x / 2, 0, packedSize.x - 1), clamp(outputCoord.y, 0, packedSize.y - 1));\n"
|
" fragColor = uInputPixelFormat == 1 ? decodeV210(outputCoord, packedSize) : decodeUyvy8(outputCoord, packedSize);\n"
|
||||||
" vec4 macroPixel = texelFetch(uPackedVideoInput, packedCoord, 0);\n"
|
"}\n";
|
||||||
" float ySample = (outputCoord.x & 1) != 0 ? macroPixel.a : macroPixel.g;\n"
|
|
||||||
" fragColor = rec709YCbCr2rgba(ySample, macroPixel.b, macroPixel.r, 1.0);\n"
|
const char* kOutputPackFragmentShaderSource =
|
||||||
|
"#version 430 core\n"
|
||||||
|
"layout(binding = 0) uniform sampler2D uOutputRgb;\n"
|
||||||
|
"uniform vec2 uOutputVideoResolution;\n"
|
||||||
|
"uniform float uActiveV210Words;\n"
|
||||||
|
"in vec2 vTexCoord;\n"
|
||||||
|
"layout(location = 0) out vec4 fragColor;\n"
|
||||||
|
"vec3 rgbAt(int x, int y)\n"
|
||||||
|
"{\n"
|
||||||
|
" ivec2 size = ivec2(max(uOutputVideoResolution, vec2(1.0, 1.0)));\n"
|
||||||
|
" return clamp(texelFetch(uOutputRgb, ivec2(clamp(x, 0, size.x - 1), clamp(y, 0, size.y - 1)), 0).rgb, vec3(0.0), vec3(1.0));\n"
|
||||||
|
"}\n"
|
||||||
|
"vec3 rgbToLegalYcbcr10(vec3 rgb)\n"
|
||||||
|
"{\n"
|
||||||
|
" float y = dot(rgb, vec3(0.2126, 0.7152, 0.0722));\n"
|
||||||
|
" float cb = (rgb.b - y) / 1.8556 + 0.5;\n"
|
||||||
|
" float cr = (rgb.r - y) / 1.5748 + 0.5;\n"
|
||||||
|
" return vec3(clamp(round(64.0 + y * 876.0), 64.0, 940.0), clamp(round(64.0 + cb * 896.0), 64.0, 960.0), clamp(round(64.0 + cr * 896.0), 64.0, 960.0));\n"
|
||||||
|
"}\n"
|
||||||
|
"uint makeWord(float a, float b, float c)\n"
|
||||||
|
"{\n"
|
||||||
|
" return (uint(a) & 1023u) | ((uint(b) & 1023u) << 10) | ((uint(c) & 1023u) << 20);\n"
|
||||||
|
"}\n"
|
||||||
|
"vec4 wordToBytes(uint word)\n"
|
||||||
|
"{\n"
|
||||||
|
" return vec4(float(word & 255u), float((word >> 8) & 255u), float((word >> 16) & 255u), float((word >> 24) & 255u)) / 255.0;\n"
|
||||||
|
"}\n"
|
||||||
|
"void main()\n"
|
||||||
|
"{\n"
|
||||||
|
" ivec2 outCoord = ivec2(gl_FragCoord.xy);\n"
|
||||||
|
" if (float(outCoord.x) >= uActiveV210Words)\n"
|
||||||
|
" {\n"
|
||||||
|
" fragColor = vec4(0.0);\n"
|
||||||
|
" return;\n"
|
||||||
|
" }\n"
|
||||||
|
" int group = outCoord.x / 4;\n"
|
||||||
|
" int wordIndex = outCoord.x - group * 4;\n"
|
||||||
|
" int pixelBase = group * 6;\n"
|
||||||
|
" int y = outCoord.y;\n"
|
||||||
|
" vec3 c0 = rgbToLegalYcbcr10(rgbAt(pixelBase + 0, y));\n"
|
||||||
|
" vec3 c1 = rgbToLegalYcbcr10(rgbAt(pixelBase + 1, y));\n"
|
||||||
|
" vec3 c2 = rgbToLegalYcbcr10(rgbAt(pixelBase + 2, y));\n"
|
||||||
|
" vec3 c3 = rgbToLegalYcbcr10(rgbAt(pixelBase + 3, y));\n"
|
||||||
|
" vec3 c4 = rgbToLegalYcbcr10(rgbAt(pixelBase + 4, y));\n"
|
||||||
|
" vec3 c5 = rgbToLegalYcbcr10(rgbAt(pixelBase + 5, y));\n"
|
||||||
|
" float cb0 = round((c0.y + c1.y) * 0.5);\n"
|
||||||
|
" float cr0 = round((c0.z + c1.z) * 0.5);\n"
|
||||||
|
" float cb2 = round((c2.y + c3.y) * 0.5);\n"
|
||||||
|
" float cr2 = round((c2.z + c3.z) * 0.5);\n"
|
||||||
|
" float cb4 = round((c4.y + c5.y) * 0.5);\n"
|
||||||
|
" float cr4 = round((c4.z + c5.z) * 0.5);\n"
|
||||||
|
" uint word = wordIndex == 0 ? makeWord(cb0, c0.x, cr0) : wordIndex == 1 ? makeWord(c1.x, cb2, c2.x) : wordIndex == 2 ? makeWord(cr2, c3.x, cb4) : makeWord(c4.x, cr4, c5.x);\n"
|
||||||
|
" fragColor = wordToBytes(word);\n"
|
||||||
"}\n";
|
"}\n";
|
||||||
|
|||||||
@@ -2,3 +2,4 @@
|
|||||||
|
|
||||||
extern const char* kFullscreenTriangleVertexShaderSource;
|
extern const char* kFullscreenTriangleVertexShaderSource;
|
||||||
extern const char* kDecodeFragmentShaderSource;
|
extern const char* kDecodeFragmentShaderSource;
|
||||||
|
extern const char* kOutputPackFragmentShaderSource;
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
#include "DeckLinkDisplayMode.h"
|
#include "DeckLinkDisplayMode.h"
|
||||||
#include "DeckLinkFrameTransfer.h"
|
|
||||||
#include "DeckLinkSession.h"
|
#include "DeckLinkSession.h"
|
||||||
#include "OpenGLComposite.h"
|
#include "OpenGLComposite.h"
|
||||||
#include "GLExtensions.h"
|
#include "GLExtensions.h"
|
||||||
@@ -87,17 +86,14 @@ bool OpenGLComposite::InitDeckLink()
|
|||||||
MessageBoxA(NULL, initFailureReason.c_str(), title, MB_OK | MB_ICONERROR);
|
MessageBoxA(NULL, initFailureReason.c_str(), title, MB_OK | MB_ICONERROR);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (!mDeckLink->SelectPreferredFormats(videoModes, initFailureReason))
|
||||||
|
goto error;
|
||||||
|
|
||||||
if (! CheckOpenGLExtensions())
|
if (! CheckOpenGLExtensions())
|
||||||
{
|
{
|
||||||
initFailureReason = "OpenGL extension checks failed.";
|
initFailureReason = "OpenGL extension checks failed.";
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
if (mDeckLink->InputOutputDimensionsDiffer())
|
|
||||||
{
|
|
||||||
mRenderer->SetFastTransferAvailable(false);
|
|
||||||
OutputDebugStringA("Input/output dimensions differ; using regular OpenGL transfer fallback instead of fast transfer.\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! InitOpenGLState())
|
if (! InitOpenGLState())
|
||||||
{
|
{
|
||||||
@@ -115,16 +111,6 @@ bool OpenGLComposite::InitDeckLink()
|
|||||||
else
|
else
|
||||||
resizeWindow(mDeckLink->OutputFrameWidth() / 2, mDeckLink->OutputFrameHeight() / 2);
|
resizeWindow(mDeckLink->OutputFrameWidth() / 2, mDeckLink->OutputFrameHeight() / 2);
|
||||||
|
|
||||||
if (mRenderer->FastTransferAvailable())
|
|
||||||
{
|
|
||||||
// Initialize fast video frame transfers
|
|
||||||
if (! VideoFrameTransfer::initialize(mDeckLink->InputFrameWidth(), mDeckLink->InputFrameHeight(), mRenderer->CaptureTexture(), mRenderer->OutputTexture()))
|
|
||||||
{
|
|
||||||
MessageBox(NULL, _T("Cannot initialize video transfers."), _T("VideoFrameTransfer error."), MB_OK);
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!mDeckLink->ConfigureInput(this, hGLDC, hGLRC, videoModes.input, initFailureReason))
|
if (!mDeckLink->ConfigureInput(this, hGLDC, hGLRC, videoModes.input, initFailureReason))
|
||||||
{
|
{
|
||||||
goto error;
|
goto error;
|
||||||
@@ -222,6 +208,11 @@ bool OpenGLComposite::InitOpenGLState()
|
|||||||
MessageBoxA(NULL, compilerErrorMessage, "OpenGL decode shader failed to load or compile", MB_OK);
|
MessageBoxA(NULL, compilerErrorMessage, "OpenGL decode shader failed to load or compile", MB_OK);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (!mShaderPrograms->CompileOutputPackShader(sizeof(compilerErrorMessage), compilerErrorMessage))
|
||||||
|
{
|
||||||
|
MessageBoxA(NULL, compilerErrorMessage, "OpenGL output pack shader failed to load or compile", MB_OK);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (!mShaderPrograms->CompileLayerPrograms(mDeckLink->InputFrameWidth(), mDeckLink->InputFrameHeight(), sizeof(compilerErrorMessage), compilerErrorMessage))
|
if (!mShaderPrograms->CompileLayerPrograms(mDeckLink->InputFrameWidth(), mDeckLink->InputFrameHeight(), sizeof(compilerErrorMessage), compilerErrorMessage))
|
||||||
{
|
{
|
||||||
@@ -233,7 +224,14 @@ bool OpenGLComposite::InitOpenGLState()
|
|||||||
mShaderPrograms->ResetTemporalHistoryState();
|
mShaderPrograms->ResetTemporalHistoryState();
|
||||||
|
|
||||||
std::string rendererError;
|
std::string rendererError;
|
||||||
if (!mRenderer->InitializeResources(mDeckLink->InputFrameWidth(), mDeckLink->InputFrameHeight(), mDeckLink->OutputFrameWidth(), mDeckLink->OutputFrameHeight(), rendererError))
|
if (!mRenderer->InitializeResources(
|
||||||
|
mDeckLink->InputFrameWidth(),
|
||||||
|
mDeckLink->InputFrameHeight(),
|
||||||
|
mDeckLink->CaptureTextureWidth(),
|
||||||
|
mDeckLink->OutputFrameWidth(),
|
||||||
|
mDeckLink->OutputFrameHeight(),
|
||||||
|
mDeckLink->OutputPackTextureWidth(),
|
||||||
|
rendererError))
|
||||||
{
|
{
|
||||||
MessageBoxA(NULL, rendererError.c_str(), "OpenGL initialization error.", MB_OK);
|
MessageBoxA(NULL, rendererError.c_str(), "OpenGL initialization error.", MB_OK);
|
||||||
return false;
|
return false;
|
||||||
@@ -315,6 +313,8 @@ void OpenGLComposite::renderEffect()
|
|||||||
layerStates,
|
layerStates,
|
||||||
mDeckLink->InputFrameWidth(),
|
mDeckLink->InputFrameWidth(),
|
||||||
mDeckLink->InputFrameHeight(),
|
mDeckLink->InputFrameHeight(),
|
||||||
|
mDeckLink->CaptureTextureWidth(),
|
||||||
|
mDeckLink->InputPixelFormat(),
|
||||||
historyCap,
|
historyCap,
|
||||||
[this](const RuntimeRenderState& state, LayerProgram::TextBinding& textBinding, std::string& error) {
|
[this](const RuntimeRenderState& state, LayerProgram::TextBinding& textBinding, std::string& error) {
|
||||||
return mShaderPrograms->UpdateTextBindingTexture(state, textBinding, error);
|
return mShaderPrograms->UpdateTextBindingTexture(state, textBinding, error);
|
||||||
@@ -392,11 +392,6 @@ void OpenGLComposite::resetTemporalHistoryState()
|
|||||||
|
|
||||||
bool OpenGLComposite::CheckOpenGLExtensions()
|
bool OpenGLComposite::CheckOpenGLExtensions()
|
||||||
{
|
{
|
||||||
mRenderer->SetFastTransferAvailable(VideoFrameTransfer::checkFastMemoryTransferAvailable());
|
|
||||||
|
|
||||||
if (!mRenderer->FastTransferAvailable())
|
|
||||||
OutputDebugStringA("Fast memory transfer extension not available, using regular OpenGL transfer fallback instead\n");
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,9 @@
|
|||||||
#include "OpenGLDeckLinkBridge.h"
|
#include "OpenGLDeckLinkBridge.h"
|
||||||
|
|
||||||
#include "DeckLinkFrameTransfer.h"
|
|
||||||
#include "DeckLinkSession.h"
|
#include "DeckLinkSession.h"
|
||||||
#include "OpenGLRenderer.h"
|
#include "OpenGLRenderer.h"
|
||||||
#include "RuntimeHost.h"
|
#include "RuntimeHost.h"
|
||||||
#include "VideoFrameTransfer.h"
|
|
||||||
|
|
||||||
#include <atlbase.h>
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <gl/gl.h>
|
#include <gl/gl.h>
|
||||||
|
|
||||||
@@ -87,29 +84,20 @@ void OpenGLDeckLinkBridge::VideoFrameArrived(IDeckLinkVideoInputFrame* inputFram
|
|||||||
|
|
||||||
wglMakeCurrent(mHdc, mHglrc); // make OpenGL context current in this thread
|
wglMakeCurrent(mHdc, mHglrc); // make OpenGL context current in this thread
|
||||||
|
|
||||||
if (mRenderer.FastTransferAvailable())
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
|
||||||
{
|
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
||||||
CComQIPtr<PinnedMemoryAllocator, &IID_PinnedMemoryAllocator> allocator(inputFrameBuffer);
|
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, mRenderer.TextureUploadBuffer());
|
||||||
if (!allocator || !allocator->transferFrame(videoPixels, mRenderer.CaptureTexture()))
|
glBufferData(GL_PIXEL_UNPACK_BUFFER, textureSize, videoPixels, GL_DYNAMIC_DRAW);
|
||||||
OutputDebugStringA("Capture: transferFrame() failed\n");
|
glBindTexture(GL_TEXTURE_2D, mRenderer.CaptureTexture());
|
||||||
|
|
||||||
allocator->waitForTransferComplete(videoPixels);
|
// NULL for last arg indicates use current GL_PIXEL_UNPACK_BUFFER target as texture data.
|
||||||
}
|
if (mDeckLink.InputPixelFormat() == VideoIOPixelFormat::V210)
|
||||||
|
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, mDeckLink.CaptureTextureWidth(), mDeckLink.InputFrameHeight(), GL_RGBA, GL_UNSIGNED_BYTE, NULL);
|
||||||
else
|
else
|
||||||
{
|
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, mDeckLink.CaptureTextureWidth(), mDeckLink.InputFrameHeight(), GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, NULL);
|
||||||
// Use a straightforward texture buffer
|
|
||||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
|
|
||||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
|
||||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, mRenderer.UnpinnedTextureBuffer());
|
|
||||||
glBufferData(GL_PIXEL_UNPACK_BUFFER, textureSize, videoPixels, GL_DYNAMIC_DRAW);
|
|
||||||
glBindTexture(GL_TEXTURE_2D, mRenderer.CaptureTexture());
|
|
||||||
|
|
||||||
// NULL for last arg indicates use current GL_PIXEL_UNPACK_BUFFER target as texture data
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, mDeckLink.InputFrameWidth() / 2, mDeckLink.InputFrameHeight(), GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, NULL);
|
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||||
|
|
||||||
glBindTexture(GL_TEXTURE_2D, 0);
|
|
||||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
wglMakeCurrent(NULL, NULL);
|
wglMakeCurrent(NULL, NULL);
|
||||||
|
|
||||||
@@ -135,17 +123,35 @@ void OpenGLDeckLinkBridge::PlayoutFrameCompleted(IDeckLinkVideoFrame* completedF
|
|||||||
|
|
||||||
// Draw the effect output to the off-screen framebuffer.
|
// Draw the effect output to the off-screen framebuffer.
|
||||||
const auto renderStartTime = std::chrono::steady_clock::now();
|
const auto renderStartTime = std::chrono::steady_clock::now();
|
||||||
if (mRenderer.FastTransferAvailable())
|
|
||||||
VideoFrameTransfer::beginTextureInUse(VideoFrameTransfer::GPUtoCPU);
|
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, mRenderer.CompositeFramebuffer());
|
glBindFramebuffer(GL_FRAMEBUFFER, mRenderer.CompositeFramebuffer());
|
||||||
mRenderEffect();
|
mRenderEffect();
|
||||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, mRenderer.CompositeFramebuffer());
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, mRenderer.CompositeFramebuffer());
|
||||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mRenderer.OutputFramebuffer());
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mRenderer.OutputFramebuffer());
|
||||||
glBlitFramebuffer(0, 0, mDeckLink.InputFrameWidth(), mDeckLink.InputFrameHeight(), 0, 0, mDeckLink.OutputFrameWidth(), mDeckLink.OutputFrameHeight(), GL_COLOR_BUFFER_BIT, GL_LINEAR);
|
glBlitFramebuffer(0, 0, mDeckLink.InputFrameWidth(), mDeckLink.InputFrameHeight(), 0, 0, mDeckLink.OutputFrameWidth(), mDeckLink.OutputFrameHeight(), GL_COLOR_BUFFER_BIT, GL_LINEAR);
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, mRenderer.OutputFramebuffer());
|
glBindFramebuffer(GL_FRAMEBUFFER, mRenderer.OutputFramebuffer());
|
||||||
|
if (mDeckLink.OutputIsTenBit())
|
||||||
|
{
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, mRenderer.OutputPackFramebuffer());
|
||||||
|
glViewport(0, 0, mDeckLink.OutputPackTextureWidth(), mDeckLink.OutputFrameHeight());
|
||||||
|
glDisable(GL_SCISSOR_TEST);
|
||||||
|
glDisable(GL_BLEND);
|
||||||
|
glDisable(GL_DEPTH_TEST);
|
||||||
|
glActiveTexture(GL_TEXTURE0);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, mRenderer.OutputTexture());
|
||||||
|
glBindVertexArray(mRenderer.FullscreenVertexArray());
|
||||||
|
glUseProgram(mRenderer.OutputPackProgram());
|
||||||
|
const GLint outputResolutionLocation = glGetUniformLocation(mRenderer.OutputPackProgram(), "uOutputVideoResolution");
|
||||||
|
const GLint activeWordsLocation = glGetUniformLocation(mRenderer.OutputPackProgram(), "uActiveV210Words");
|
||||||
|
if (outputResolutionLocation >= 0)
|
||||||
|
glUniform2f(outputResolutionLocation, static_cast<float>(mDeckLink.OutputFrameWidth()), static_cast<float>(mDeckLink.OutputFrameHeight()));
|
||||||
|
if (activeWordsLocation >= 0)
|
||||||
|
glUniform1f(activeWordsLocation, static_cast<float>(ActiveV210WordsForWidth(mDeckLink.OutputFrameWidth())));
|
||||||
|
glDrawArrays(GL_TRIANGLES, 0, 3);
|
||||||
|
glUseProgram(0);
|
||||||
|
glBindVertexArray(0);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
|
}
|
||||||
glFlush();
|
glFlush();
|
||||||
if (mRenderer.FastTransferAvailable())
|
|
||||||
VideoFrameTransfer::endTextureInUse(VideoFrameTransfer::GPUtoCPU);
|
|
||||||
const auto renderEndTime = std::chrono::steady_clock::now();
|
const auto renderEndTime = std::chrono::steady_clock::now();
|
||||||
const double frameBudgetMilliseconds = mDeckLink.FrameBudgetMilliseconds();
|
const double frameBudgetMilliseconds = mDeckLink.FrameBudgetMilliseconds();
|
||||||
const double renderMilliseconds = std::chrono::duration_cast<std::chrono::duration<double, std::milli>>(renderEndTime - renderStartTime).count();
|
const double renderMilliseconds = std::chrono::duration_cast<std::chrono::duration<double, std::milli>>(renderEndTime - renderStartTime).count();
|
||||||
@@ -171,23 +177,19 @@ void OpenGLDeckLinkBridge::PlayoutFrameCompleted(IDeckLinkVideoFrame* completedF
|
|||||||
void* pFrame;
|
void* pFrame;
|
||||||
outputVideoFrameBuffer->GetBytes(&pFrame);
|
outputVideoFrameBuffer->GetBytes(&pFrame);
|
||||||
|
|
||||||
if (mRenderer.FastTransferAvailable())
|
glPixelStorei(GL_PACK_ALIGNMENT, 4);
|
||||||
|
glPixelStorei(GL_PACK_ROW_LENGTH, 0);
|
||||||
|
if (mDeckLink.OutputIsTenBit())
|
||||||
{
|
{
|
||||||
if (!mDeckLink.TransferPlayoutFrame(pFrame, mRenderer.OutputTexture()))
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, mRenderer.OutputPackFramebuffer());
|
||||||
OutputDebugStringA("Playback: transferFrame() failed\n");
|
glReadPixels(0, 0, mDeckLink.OutputPackTextureWidth(), mDeckLink.OutputFrameHeight(), GL_RGBA, GL_UNSIGNED_BYTE, pFrame);
|
||||||
|
|
||||||
// Wait for transfer to system memory to complete
|
|
||||||
mDeckLink.WaitForPlayoutTransferComplete(pFrame);
|
|
||||||
mPaint();
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
glPixelStorei(GL_PACK_ALIGNMENT, 4);
|
|
||||||
glPixelStorei(GL_PACK_ROW_LENGTH, 0);
|
|
||||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, mRenderer.OutputFramebuffer());
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, mRenderer.OutputFramebuffer());
|
||||||
glReadPixels(0, 0, mDeckLink.OutputFrameWidth(), mDeckLink.OutputFrameHeight(), GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, pFrame);
|
glReadPixels(0, 0, mDeckLink.OutputFrameWidth(), mDeckLink.OutputFrameHeight(), GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, pFrame);
|
||||||
mPaint();
|
|
||||||
}
|
}
|
||||||
|
mPaint();
|
||||||
|
|
||||||
outputVideoFrameBuffer->EndAccess(bmdBufferAccessWrite);
|
outputVideoFrameBuffer->EndAccess(bmdBufferAccessWrite);
|
||||||
outputVideoFrameBuffer->Release();
|
outputVideoFrameBuffer->Release();
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
#include "OpenGLRenderPass.h"
|
#include "OpenGLRenderPass.h"
|
||||||
|
|
||||||
#include "GlRenderConstants.h"
|
#include "GlRenderConstants.h"
|
||||||
#include "VideoFrameTransfer.h"
|
|
||||||
|
|
||||||
OpenGLRenderPass::OpenGLRenderPass(OpenGLRenderer& renderer) :
|
OpenGLRenderPass::OpenGLRenderPass(OpenGLRenderer& renderer) :
|
||||||
mRenderer(renderer)
|
mRenderer(renderer)
|
||||||
@@ -13,22 +12,18 @@ void OpenGLRenderPass::Render(
|
|||||||
const std::vector<RuntimeRenderState>& layerStates,
|
const std::vector<RuntimeRenderState>& layerStates,
|
||||||
unsigned inputFrameWidth,
|
unsigned inputFrameWidth,
|
||||||
unsigned inputFrameHeight,
|
unsigned inputFrameHeight,
|
||||||
|
unsigned captureTextureWidth,
|
||||||
|
VideoIOPixelFormat inputPixelFormat,
|
||||||
unsigned historyCap,
|
unsigned historyCap,
|
||||||
const TextBindingUpdater& updateTextBinding,
|
const TextBindingUpdater& updateTextBinding,
|
||||||
const GlobalParamsUpdater& updateGlobalParams)
|
const GlobalParamsUpdater& updateGlobalParams)
|
||||||
{
|
{
|
||||||
if (hasInputSource && mRenderer.FastTransferAvailable())
|
|
||||||
{
|
|
||||||
// Signal that the capture texture is about to be sampled into the composite framebuffer.
|
|
||||||
VideoFrameTransfer::beginTextureInUse(VideoFrameTransfer::CPUtoGPU);
|
|
||||||
}
|
|
||||||
|
|
||||||
glDisable(GL_SCISSOR_TEST);
|
glDisable(GL_SCISSOR_TEST);
|
||||||
glDisable(GL_BLEND);
|
glDisable(GL_BLEND);
|
||||||
glDisable(GL_DEPTH_TEST);
|
glDisable(GL_DEPTH_TEST);
|
||||||
if (hasInputSource)
|
if (hasInputSource)
|
||||||
{
|
{
|
||||||
RenderDecodePass(inputFrameWidth, inputFrameHeight);
|
RenderDecodePass(inputFrameWidth, inputFrameHeight, captureTextureWidth, inputPixelFormat);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -72,12 +67,9 @@ void OpenGLRenderPass::Render(
|
|||||||
}
|
}
|
||||||
|
|
||||||
mRenderer.TemporalHistory().PushSourceFramebuffer(mRenderer.DecodeFramebuffer(), inputFrameWidth, inputFrameHeight);
|
mRenderer.TemporalHistory().PushSourceFramebuffer(mRenderer.DecodeFramebuffer(), inputFrameWidth, inputFrameHeight);
|
||||||
|
|
||||||
if (hasInputSource && mRenderer.FastTransferAvailable())
|
|
||||||
VideoFrameTransfer::endTextureInUse(VideoFrameTransfer::CPUtoGPU);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenGLRenderPass::RenderDecodePass(unsigned inputFrameWidth, unsigned inputFrameHeight)
|
void OpenGLRenderPass::RenderDecodePass(unsigned inputFrameWidth, unsigned inputFrameHeight, unsigned captureTextureWidth, VideoIOPixelFormat inputPixelFormat)
|
||||||
{
|
{
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, mRenderer.DecodeFramebuffer());
|
glBindFramebuffer(GL_FRAMEBUFFER, mRenderer.DecodeFramebuffer());
|
||||||
glViewport(0, 0, inputFrameWidth, inputFrameHeight);
|
glViewport(0, 0, inputFrameWidth, inputFrameHeight);
|
||||||
@@ -89,10 +81,13 @@ void OpenGLRenderPass::RenderDecodePass(unsigned inputFrameWidth, unsigned input
|
|||||||
|
|
||||||
const GLint packedResolutionLocation = glGetUniformLocation(mRenderer.DecodeProgram(), "uPackedVideoResolution");
|
const GLint packedResolutionLocation = glGetUniformLocation(mRenderer.DecodeProgram(), "uPackedVideoResolution");
|
||||||
const GLint decodedResolutionLocation = glGetUniformLocation(mRenderer.DecodeProgram(), "uDecodedVideoResolution");
|
const GLint decodedResolutionLocation = glGetUniformLocation(mRenderer.DecodeProgram(), "uDecodedVideoResolution");
|
||||||
|
const GLint inputPixelFormatLocation = glGetUniformLocation(mRenderer.DecodeProgram(), "uInputPixelFormat");
|
||||||
if (packedResolutionLocation >= 0)
|
if (packedResolutionLocation >= 0)
|
||||||
glUniform2f(packedResolutionLocation, static_cast<float>(inputFrameWidth / 2), static_cast<float>(inputFrameHeight));
|
glUniform2f(packedResolutionLocation, static_cast<float>(captureTextureWidth), static_cast<float>(inputFrameHeight));
|
||||||
if (decodedResolutionLocation >= 0)
|
if (decodedResolutionLocation >= 0)
|
||||||
glUniform2f(decodedResolutionLocation, static_cast<float>(inputFrameWidth), static_cast<float>(inputFrameHeight));
|
glUniform2f(decodedResolutionLocation, static_cast<float>(inputFrameWidth), static_cast<float>(inputFrameHeight));
|
||||||
|
if (inputPixelFormatLocation >= 0)
|
||||||
|
glUniform1i(inputPixelFormatLocation, inputPixelFormat == VideoIOPixelFormat::V210 ? 1 : 0);
|
||||||
|
|
||||||
glDrawArrays(GL_TRIANGLES, 0, 3);
|
glDrawArrays(GL_TRIANGLES, 0, 3);
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include "OpenGLRenderer.h"
|
#include "OpenGLRenderer.h"
|
||||||
#include "ShaderTypes.h"
|
#include "ShaderTypes.h"
|
||||||
|
#include "VideoIOFormat.h"
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <string>
|
#include <string>
|
||||||
@@ -21,12 +22,14 @@ public:
|
|||||||
const std::vector<RuntimeRenderState>& layerStates,
|
const std::vector<RuntimeRenderState>& layerStates,
|
||||||
unsigned inputFrameWidth,
|
unsigned inputFrameWidth,
|
||||||
unsigned inputFrameHeight,
|
unsigned inputFrameHeight,
|
||||||
|
unsigned captureTextureWidth,
|
||||||
|
VideoIOPixelFormat inputPixelFormat,
|
||||||
unsigned historyCap,
|
unsigned historyCap,
|
||||||
const TextBindingUpdater& updateTextBinding,
|
const TextBindingUpdater& updateTextBinding,
|
||||||
const GlobalParamsUpdater& updateGlobalParams);
|
const GlobalParamsUpdater& updateGlobalParams);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void RenderDecodePass(unsigned inputFrameWidth, unsigned inputFrameHeight);
|
void RenderDecodePass(unsigned inputFrameWidth, unsigned inputFrameHeight, unsigned captureTextureWidth, VideoIOPixelFormat inputPixelFormat);
|
||||||
void RenderShaderProgram(
|
void RenderShaderProgram(
|
||||||
GLuint sourceTexture,
|
GLuint sourceTexture,
|
||||||
GLuint destinationFrameBuffer,
|
GLuint destinationFrameBuffer,
|
||||||
|
|||||||
@@ -4,43 +4,52 @@
|
|||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
void ConfigureFrameTexture(unsigned width, unsigned height)
|
void ConfigureByteFrameTexture(unsigned width, unsigned height)
|
||||||
{
|
{
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, NULL);
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConfigureDisplayFrameTexture(unsigned width, unsigned height)
|
||||||
|
{
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, width, height, 0, GL_RGBA, GL_FLOAT, NULL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OpenGLRenderer::InitializeResources(unsigned inputFrameWidth, unsigned inputFrameHeight, unsigned outputFrameWidth, unsigned outputFrameHeight, std::string& error)
|
bool OpenGLRenderer::InitializeResources(unsigned inputFrameWidth, unsigned inputFrameHeight, unsigned captureTextureWidth, unsigned outputFrameWidth, unsigned outputFrameHeight, unsigned outputPackTextureWidth, std::string& error)
|
||||||
{
|
{
|
||||||
glClearColor(0.0f, 0.0f, 0.0f, 0.5f);
|
glClearColor(0.0f, 0.0f, 0.0f, 0.5f);
|
||||||
glDisable(GL_DEPTH_TEST);
|
glDisable(GL_DEPTH_TEST);
|
||||||
|
|
||||||
if (!mFastTransferExtensionAvailable)
|
glGenBuffers(1, &mTextureUploadBuffer);
|
||||||
glGenBuffers(1, &mUnpinnedTextureBuffer);
|
|
||||||
|
|
||||||
glGenTextures(1, &mCaptureTexture);
|
glGenTextures(1, &mCaptureTexture);
|
||||||
glBindTexture(GL_TEXTURE_2D, mCaptureTexture);
|
glBindTexture(GL_TEXTURE_2D, mCaptureTexture);
|
||||||
ConfigureFrameTexture(inputFrameWidth / 2, inputFrameHeight);
|
ConfigureByteFrameTexture(captureTextureWidth, inputFrameHeight);
|
||||||
glBindTexture(GL_TEXTURE_2D, 0);
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
|
|
||||||
glGenTextures(1, &mDecodedTexture);
|
glGenTextures(1, &mDecodedTexture);
|
||||||
glBindTexture(GL_TEXTURE_2D, mDecodedTexture);
|
glBindTexture(GL_TEXTURE_2D, mDecodedTexture);
|
||||||
ConfigureFrameTexture(inputFrameWidth, inputFrameHeight);
|
ConfigureDisplayFrameTexture(inputFrameWidth, inputFrameHeight);
|
||||||
glBindTexture(GL_TEXTURE_2D, 0);
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
|
|
||||||
glGenTextures(1, &mLayerTempTexture);
|
glGenTextures(1, &mLayerTempTexture);
|
||||||
glBindTexture(GL_TEXTURE_2D, mLayerTempTexture);
|
glBindTexture(GL_TEXTURE_2D, mLayerTempTexture);
|
||||||
ConfigureFrameTexture(inputFrameWidth, inputFrameHeight);
|
ConfigureDisplayFrameTexture(inputFrameWidth, inputFrameHeight);
|
||||||
glBindTexture(GL_TEXTURE_2D, 0);
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
|
|
||||||
glGenFramebuffers(1, &mDecodeFrameBuf);
|
glGenFramebuffers(1, &mDecodeFrameBuf);
|
||||||
glGenFramebuffers(1, &mLayerTempFrameBuf);
|
glGenFramebuffers(1, &mLayerTempFrameBuf);
|
||||||
glGenFramebuffers(1, &mIdFrameBuf);
|
glGenFramebuffers(1, &mIdFrameBuf);
|
||||||
glGenFramebuffers(1, &mOutputFrameBuf);
|
glGenFramebuffers(1, &mOutputFrameBuf);
|
||||||
|
glGenFramebuffers(1, &mOutputPackFrameBuf);
|
||||||
glGenRenderbuffers(1, &mIdColorBuf);
|
glGenRenderbuffers(1, &mIdColorBuf);
|
||||||
glGenRenderbuffers(1, &mIdDepthBuf);
|
glGenRenderbuffers(1, &mIdDepthBuf);
|
||||||
glGenVertexArrays(1, &mFullscreenVAO);
|
glGenVertexArrays(1, &mFullscreenVAO);
|
||||||
@@ -65,7 +74,7 @@ bool OpenGLRenderer::InitializeResources(unsigned inputFrameWidth, unsigned inpu
|
|||||||
glBindFramebuffer(GL_FRAMEBUFFER, mIdFrameBuf);
|
glBindFramebuffer(GL_FRAMEBUFFER, mIdFrameBuf);
|
||||||
glGenTextures(1, &mFBOTexture);
|
glGenTextures(1, &mFBOTexture);
|
||||||
glBindTexture(GL_TEXTURE_2D, mFBOTexture);
|
glBindTexture(GL_TEXTURE_2D, mFBOTexture);
|
||||||
ConfigureFrameTexture(inputFrameWidth, inputFrameHeight);
|
ConfigureDisplayFrameTexture(inputFrameWidth, inputFrameHeight);
|
||||||
|
|
||||||
glBindRenderbuffer(GL_RENDERBUFFER, mIdDepthBuf);
|
glBindRenderbuffer(GL_RENDERBUFFER, mIdDepthBuf);
|
||||||
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, inputFrameWidth, inputFrameHeight);
|
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, inputFrameWidth, inputFrameHeight);
|
||||||
@@ -79,7 +88,7 @@ bool OpenGLRenderer::InitializeResources(unsigned inputFrameWidth, unsigned inpu
|
|||||||
|
|
||||||
glGenTextures(1, &mOutputTexture);
|
glGenTextures(1, &mOutputTexture);
|
||||||
glBindTexture(GL_TEXTURE_2D, mOutputTexture);
|
glBindTexture(GL_TEXTURE_2D, mOutputTexture);
|
||||||
ConfigureFrameTexture(outputFrameWidth, outputFrameHeight);
|
ConfigureDisplayFrameTexture(outputFrameWidth, outputFrameHeight);
|
||||||
|
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, mOutputFrameBuf);
|
glBindFramebuffer(GL_FRAMEBUFFER, mOutputFrameBuf);
|
||||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mOutputTexture, 0);
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mOutputTexture, 0);
|
||||||
@@ -89,6 +98,18 @@ bool OpenGLRenderer::InitializeResources(unsigned inputFrameWidth, unsigned inpu
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
glGenTextures(1, &mOutputPackTexture);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, mOutputPackTexture);
|
||||||
|
ConfigureByteFrameTexture(outputPackTextureWidth, outputFrameHeight);
|
||||||
|
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, mOutputPackFrameBuf);
|
||||||
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mOutputPackTexture, 0);
|
||||||
|
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
|
||||||
|
{
|
||||||
|
error = "Cannot initialize output pack framebuffer.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
glBindTexture(GL_TEXTURE_2D, 0);
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
glBindRenderbuffer(GL_RENDERBUFFER, 0);
|
glBindRenderbuffer(GL_RENDERBUFFER, 0);
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||||
@@ -109,6 +130,13 @@ void OpenGLRenderer::SetDecodeShaderProgram(GLuint program, GLuint vertexShader,
|
|||||||
mDecodeFragmentShader = fragmentShader;
|
mDecodeFragmentShader = fragmentShader;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void OpenGLRenderer::SetOutputPackShaderProgram(GLuint program, GLuint vertexShader, GLuint fragmentShader)
|
||||||
|
{
|
||||||
|
mOutputPackProgram = program;
|
||||||
|
mOutputPackVertexShader = vertexShader;
|
||||||
|
mOutputPackFragmentShader = fragmentShader;
|
||||||
|
}
|
||||||
|
|
||||||
void OpenGLRenderer::ResizeView(int width, int height)
|
void OpenGLRenderer::ResizeView(int width, int height)
|
||||||
{
|
{
|
||||||
mViewWidth = width;
|
mViewWidth = width;
|
||||||
@@ -166,6 +194,8 @@ void OpenGLRenderer::DestroyResources()
|
|||||||
glDeleteFramebuffers(1, &mIdFrameBuf);
|
glDeleteFramebuffers(1, &mIdFrameBuf);
|
||||||
if (mOutputFrameBuf != 0)
|
if (mOutputFrameBuf != 0)
|
||||||
glDeleteFramebuffers(1, &mOutputFrameBuf);
|
glDeleteFramebuffers(1, &mOutputFrameBuf);
|
||||||
|
if (mOutputPackFrameBuf != 0)
|
||||||
|
glDeleteFramebuffers(1, &mOutputPackFrameBuf);
|
||||||
if (mIdColorBuf != 0)
|
if (mIdColorBuf != 0)
|
||||||
glDeleteRenderbuffers(1, &mIdColorBuf);
|
glDeleteRenderbuffers(1, &mIdColorBuf);
|
||||||
if (mIdDepthBuf != 0)
|
if (mIdDepthBuf != 0)
|
||||||
@@ -180,8 +210,10 @@ void OpenGLRenderer::DestroyResources()
|
|||||||
glDeleteTextures(1, &mFBOTexture);
|
glDeleteTextures(1, &mFBOTexture);
|
||||||
if (mOutputTexture != 0)
|
if (mOutputTexture != 0)
|
||||||
glDeleteTextures(1, &mOutputTexture);
|
glDeleteTextures(1, &mOutputTexture);
|
||||||
if (mUnpinnedTextureBuffer != 0)
|
if (mOutputPackTexture != 0)
|
||||||
glDeleteBuffers(1, &mUnpinnedTextureBuffer);
|
glDeleteTextures(1, &mOutputPackTexture);
|
||||||
|
if (mTextureUploadBuffer != 0)
|
||||||
|
glDeleteBuffers(1, &mTextureUploadBuffer);
|
||||||
|
|
||||||
mFullscreenVAO = 0;
|
mFullscreenVAO = 0;
|
||||||
mGlobalParamsUBO = 0;
|
mGlobalParamsUBO = 0;
|
||||||
@@ -189,6 +221,7 @@ void OpenGLRenderer::DestroyResources()
|
|||||||
mLayerTempFrameBuf = 0;
|
mLayerTempFrameBuf = 0;
|
||||||
mIdFrameBuf = 0;
|
mIdFrameBuf = 0;
|
||||||
mOutputFrameBuf = 0;
|
mOutputFrameBuf = 0;
|
||||||
|
mOutputPackFrameBuf = 0;
|
||||||
mIdColorBuf = 0;
|
mIdColorBuf = 0;
|
||||||
mIdDepthBuf = 0;
|
mIdDepthBuf = 0;
|
||||||
mCaptureTexture = 0;
|
mCaptureTexture = 0;
|
||||||
@@ -196,12 +229,14 @@ void OpenGLRenderer::DestroyResources()
|
|||||||
mLayerTempTexture = 0;
|
mLayerTempTexture = 0;
|
||||||
mFBOTexture = 0;
|
mFBOTexture = 0;
|
||||||
mOutputTexture = 0;
|
mOutputTexture = 0;
|
||||||
mUnpinnedTextureBuffer = 0;
|
mOutputPackTexture = 0;
|
||||||
|
mTextureUploadBuffer = 0;
|
||||||
mGlobalParamsUBOSize = 0;
|
mGlobalParamsUBOSize = 0;
|
||||||
|
|
||||||
mTemporalHistory.DestroyResources();
|
mTemporalHistory.DestroyResources();
|
||||||
DestroyLayerPrograms();
|
DestroyLayerPrograms();
|
||||||
DestroyDecodeShaderProgram();
|
DestroyDecodeShaderProgram();
|
||||||
|
DestroyOutputPackShaderProgram();
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenGLRenderer::DestroySingleLayerProgram(LayerProgram& layerProgram)
|
void OpenGLRenderer::DestroySingleLayerProgram(LayerProgram& layerProgram)
|
||||||
@@ -272,3 +307,24 @@ void OpenGLRenderer::DestroyDecodeShaderProgram()
|
|||||||
mDecodeVertexShader = 0;
|
mDecodeVertexShader = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void OpenGLRenderer::DestroyOutputPackShaderProgram()
|
||||||
|
{
|
||||||
|
if (mOutputPackProgram != 0)
|
||||||
|
{
|
||||||
|
glDeleteProgram(mOutputPackProgram);
|
||||||
|
mOutputPackProgram = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mOutputPackFragmentShader != 0)
|
||||||
|
{
|
||||||
|
glDeleteShader(mOutputPackFragmentShader);
|
||||||
|
mOutputPackFragmentShader = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mOutputPackVertexShader != 0)
|
||||||
|
{
|
||||||
|
glDeleteShader(mOutputPackVertexShader);
|
||||||
|
mOutputPackVertexShader = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -44,21 +44,22 @@ public:
|
|||||||
std::vector<TextBinding> textBindings;
|
std::vector<TextBinding> textBindings;
|
||||||
};
|
};
|
||||||
|
|
||||||
bool FastTransferAvailable() const { return mFastTransferExtensionAvailable; }
|
|
||||||
void SetFastTransferAvailable(bool available) { mFastTransferExtensionAvailable = available; }
|
|
||||||
GLuint CaptureTexture() const { return mCaptureTexture; }
|
GLuint CaptureTexture() const { return mCaptureTexture; }
|
||||||
GLuint DecodedTexture() const { return mDecodedTexture; }
|
GLuint DecodedTexture() const { return mDecodedTexture; }
|
||||||
GLuint LayerTempTexture() const { return mLayerTempTexture; }
|
GLuint LayerTempTexture() const { return mLayerTempTexture; }
|
||||||
GLuint CompositeTexture() const { return mFBOTexture; }
|
GLuint CompositeTexture() const { return mFBOTexture; }
|
||||||
GLuint OutputTexture() const { return mOutputTexture; }
|
GLuint OutputTexture() const { return mOutputTexture; }
|
||||||
GLuint UnpinnedTextureBuffer() const { return mUnpinnedTextureBuffer; }
|
GLuint OutputPackTexture() const { return mOutputPackTexture; }
|
||||||
|
GLuint TextureUploadBuffer() const { return mTextureUploadBuffer; }
|
||||||
GLuint DecodeFramebuffer() const { return mDecodeFrameBuf; }
|
GLuint DecodeFramebuffer() const { return mDecodeFrameBuf; }
|
||||||
GLuint LayerTempFramebuffer() const { return mLayerTempFrameBuf; }
|
GLuint LayerTempFramebuffer() const { return mLayerTempFrameBuf; }
|
||||||
GLuint CompositeFramebuffer() const { return mIdFrameBuf; }
|
GLuint CompositeFramebuffer() const { return mIdFrameBuf; }
|
||||||
GLuint OutputFramebuffer() const { return mOutputFrameBuf; }
|
GLuint OutputFramebuffer() const { return mOutputFrameBuf; }
|
||||||
|
GLuint OutputPackFramebuffer() const { return mOutputPackFrameBuf; }
|
||||||
GLuint FullscreenVertexArray() const { return mFullscreenVAO; }
|
GLuint FullscreenVertexArray() const { return mFullscreenVAO; }
|
||||||
GLuint GlobalParamsUBO() const { return mGlobalParamsUBO; }
|
GLuint GlobalParamsUBO() const { return mGlobalParamsUBO; }
|
||||||
GLuint DecodeProgram() const { return mDecodeProgram; }
|
GLuint DecodeProgram() const { return mDecodeProgram; }
|
||||||
|
GLuint OutputPackProgram() const { return mOutputPackProgram; }
|
||||||
GLsizeiptr GlobalParamsUBOSize() const { return mGlobalParamsUBOSize; }
|
GLsizeiptr GlobalParamsUBOSize() const { return mGlobalParamsUBOSize; }
|
||||||
void SetGlobalParamsUBOSize(GLsizeiptr size) { mGlobalParamsUBOSize = size; }
|
void SetGlobalParamsUBOSize(GLsizeiptr size) { mGlobalParamsUBOSize = size; }
|
||||||
void ReplaceLayerPrograms(std::vector<LayerProgram>& newPrograms) { mLayerPrograms.swap(newPrograms); }
|
void ReplaceLayerPrograms(std::vector<LayerProgram>& newPrograms) { mLayerPrograms.swap(newPrograms); }
|
||||||
@@ -67,26 +68,29 @@ public:
|
|||||||
TemporalHistoryBuffers& TemporalHistory() { return mTemporalHistory; }
|
TemporalHistoryBuffers& TemporalHistory() { return mTemporalHistory; }
|
||||||
const TemporalHistoryBuffers& TemporalHistory() const { return mTemporalHistory; }
|
const TemporalHistoryBuffers& TemporalHistory() const { return mTemporalHistory; }
|
||||||
void SetDecodeShaderProgram(GLuint program, GLuint vertexShader, GLuint fragmentShader);
|
void SetDecodeShaderProgram(GLuint program, GLuint vertexShader, GLuint fragmentShader);
|
||||||
bool InitializeResources(unsigned inputFrameWidth, unsigned inputFrameHeight, unsigned outputFrameWidth, unsigned outputFrameHeight, std::string& error);
|
void SetOutputPackShaderProgram(GLuint program, GLuint vertexShader, GLuint fragmentShader);
|
||||||
|
bool InitializeResources(unsigned inputFrameWidth, unsigned inputFrameHeight, unsigned captureTextureWidth, unsigned outputFrameWidth, unsigned outputFrameHeight, unsigned outputPackTextureWidth, std::string& error);
|
||||||
void ResizeView(int width, int height);
|
void ResizeView(int width, int height);
|
||||||
void PresentToWindow(HDC hdc, unsigned outputFrameWidth, unsigned outputFrameHeight);
|
void PresentToWindow(HDC hdc, unsigned outputFrameWidth, unsigned outputFrameHeight);
|
||||||
void DestroyResources();
|
void DestroyResources();
|
||||||
void DestroySingleLayerProgram(LayerProgram& layerProgram);
|
void DestroySingleLayerProgram(LayerProgram& layerProgram);
|
||||||
void DestroyLayerPrograms();
|
void DestroyLayerPrograms();
|
||||||
void DestroyDecodeShaderProgram();
|
void DestroyDecodeShaderProgram();
|
||||||
|
void DestroyOutputPackShaderProgram();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool mFastTransferExtensionAvailable = false;
|
|
||||||
GLuint mCaptureTexture = 0;
|
GLuint mCaptureTexture = 0;
|
||||||
GLuint mDecodedTexture = 0;
|
GLuint mDecodedTexture = 0;
|
||||||
GLuint mLayerTempTexture = 0;
|
GLuint mLayerTempTexture = 0;
|
||||||
GLuint mFBOTexture = 0;
|
GLuint mFBOTexture = 0;
|
||||||
GLuint mOutputTexture = 0;
|
GLuint mOutputTexture = 0;
|
||||||
GLuint mUnpinnedTextureBuffer = 0;
|
GLuint mOutputPackTexture = 0;
|
||||||
|
GLuint mTextureUploadBuffer = 0;
|
||||||
GLuint mDecodeFrameBuf = 0;
|
GLuint mDecodeFrameBuf = 0;
|
||||||
GLuint mLayerTempFrameBuf = 0;
|
GLuint mLayerTempFrameBuf = 0;
|
||||||
GLuint mIdFrameBuf = 0;
|
GLuint mIdFrameBuf = 0;
|
||||||
GLuint mOutputFrameBuf = 0;
|
GLuint mOutputFrameBuf = 0;
|
||||||
|
GLuint mOutputPackFrameBuf = 0;
|
||||||
GLuint mIdColorBuf = 0;
|
GLuint mIdColorBuf = 0;
|
||||||
GLuint mIdDepthBuf = 0;
|
GLuint mIdDepthBuf = 0;
|
||||||
GLuint mFullscreenVAO = 0;
|
GLuint mFullscreenVAO = 0;
|
||||||
@@ -94,6 +98,9 @@ private:
|
|||||||
GLuint mDecodeProgram = 0;
|
GLuint mDecodeProgram = 0;
|
||||||
GLuint mDecodeVertexShader = 0;
|
GLuint mDecodeVertexShader = 0;
|
||||||
GLuint mDecodeFragmentShader = 0;
|
GLuint mDecodeFragmentShader = 0;
|
||||||
|
GLuint mOutputPackProgram = 0;
|
||||||
|
GLuint mOutputPackVertexShader = 0;
|
||||||
|
GLuint mOutputPackFragmentShader = 0;
|
||||||
GLsizeiptr mGlobalParamsUBOSize = 0;
|
GLsizeiptr mGlobalParamsUBOSize = 0;
|
||||||
int mViewWidth = 0;
|
int mViewWidth = 0;
|
||||||
int mViewHeight = 0;
|
int mViewHeight = 0;
|
||||||
|
|||||||
@@ -115,6 +115,11 @@ bool OpenGLShaderPrograms::CompileDecodeShader(int errorMessageSize, char* error
|
|||||||
return mCompiler.CompileDecodeShader(errorMessageSize, errorMessage);
|
return mCompiler.CompileDecodeShader(errorMessageSize, errorMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool OpenGLShaderPrograms::CompileOutputPackShader(int errorMessageSize, char* errorMessage)
|
||||||
|
{
|
||||||
|
return mCompiler.CompileOutputPackShader(errorMessageSize, errorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
void OpenGLShaderPrograms::DestroySingleLayerProgram(LayerProgram& layerProgram)
|
void OpenGLShaderPrograms::DestroySingleLayerProgram(LayerProgram& layerProgram)
|
||||||
{
|
{
|
||||||
mRenderer.DestroySingleLayerProgram(layerProgram);
|
mRenderer.DestroySingleLayerProgram(layerProgram);
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ public:
|
|||||||
bool CompileLayerPrograms(unsigned inputFrameWidth, unsigned inputFrameHeight, int errorMessageSize, char* errorMessage);
|
bool CompileLayerPrograms(unsigned inputFrameWidth, unsigned inputFrameHeight, int errorMessageSize, char* errorMessage);
|
||||||
bool CommitPreparedLayerPrograms(const PreparedShaderBuild& preparedBuild, unsigned inputFrameWidth, unsigned inputFrameHeight, int errorMessageSize, char* errorMessage);
|
bool CommitPreparedLayerPrograms(const PreparedShaderBuild& preparedBuild, unsigned inputFrameWidth, unsigned inputFrameHeight, int errorMessageSize, char* errorMessage);
|
||||||
bool CompileDecodeShader(int errorMessageSize, char* errorMessage);
|
bool CompileDecodeShader(int errorMessageSize, char* errorMessage);
|
||||||
|
bool CompileOutputPackShader(int errorMessageSize, char* errorMessage);
|
||||||
void DestroyLayerPrograms();
|
void DestroyLayerPrograms();
|
||||||
void DestroySingleLayerProgram(LayerProgram& layerProgram);
|
void DestroySingleLayerProgram(LayerProgram& layerProgram);
|
||||||
void DestroyDecodeShaderProgram();
|
void DestroyDecodeShaderProgram();
|
||||||
|
|||||||
@@ -192,3 +192,53 @@ bool ShaderProgramCompiler::CompileDecodeShader(int errorMessageSize, char* erro
|
|||||||
mRenderer.SetDecodeShaderProgram(newProgram.release(), newVertexShader.release(), newFragmentShader.release());
|
mRenderer.SetDecodeShaderProgram(newProgram.release(), newVertexShader.release(), newFragmentShader.release());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ShaderProgramCompiler::CompileOutputPackShader(int errorMessageSize, char* errorMessage)
|
||||||
|
{
|
||||||
|
GLsizei errorBufferSize = 0;
|
||||||
|
GLint compileResult = GL_FALSE;
|
||||||
|
GLint linkResult = GL_FALSE;
|
||||||
|
const char* vertexSource = kFullscreenTriangleVertexShaderSource;
|
||||||
|
const char* fragmentSource = kOutputPackFragmentShaderSource;
|
||||||
|
|
||||||
|
ScopedGlShader newVertexShader(glCreateShader(GL_VERTEX_SHADER));
|
||||||
|
glShaderSource(newVertexShader.get(), 1, (const GLchar**)&vertexSource, NULL);
|
||||||
|
glCompileShader(newVertexShader.get());
|
||||||
|
glGetShaderiv(newVertexShader.get(), GL_COMPILE_STATUS, &compileResult);
|
||||||
|
if (compileResult == GL_FALSE)
|
||||||
|
{
|
||||||
|
glGetShaderInfoLog(newVertexShader.get(), errorMessageSize, &errorBufferSize, errorMessage);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ScopedGlShader newFragmentShader(glCreateShader(GL_FRAGMENT_SHADER));
|
||||||
|
glShaderSource(newFragmentShader.get(), 1, (const GLchar**)&fragmentSource, NULL);
|
||||||
|
glCompileShader(newFragmentShader.get());
|
||||||
|
glGetShaderiv(newFragmentShader.get(), GL_COMPILE_STATUS, &compileResult);
|
||||||
|
if (compileResult == GL_FALSE)
|
||||||
|
{
|
||||||
|
glGetShaderInfoLog(newFragmentShader.get(), errorMessageSize, &errorBufferSize, errorMessage);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ScopedGlProgram newProgram(glCreateProgram());
|
||||||
|
glAttachShader(newProgram.get(), newVertexShader.get());
|
||||||
|
glAttachShader(newProgram.get(), newFragmentShader.get());
|
||||||
|
glLinkProgram(newProgram.get());
|
||||||
|
glGetProgramiv(newProgram.get(), GL_LINK_STATUS, &linkResult);
|
||||||
|
if (linkResult == GL_FALSE)
|
||||||
|
{
|
||||||
|
glGetProgramInfoLog(newProgram.get(), errorMessageSize, &errorBufferSize, errorMessage);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
glUseProgram(newProgram.get());
|
||||||
|
const GLint outputSamplerLocation = glGetUniformLocation(newProgram.get(), "uOutputRgb");
|
||||||
|
if (outputSamplerLocation >= 0)
|
||||||
|
glUniform1i(outputSamplerLocation, 0);
|
||||||
|
glUseProgram(0);
|
||||||
|
|
||||||
|
mRenderer.DestroyOutputPackShaderProgram();
|
||||||
|
mRenderer.SetOutputPackShaderProgram(newProgram.release(), newVertexShader.release(), newFragmentShader.release());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ public:
|
|||||||
bool CompileLayerProgram(const RuntimeRenderState& state, LayerProgram& layerProgram, int errorMessageSize, char* errorMessage);
|
bool CompileLayerProgram(const RuntimeRenderState& state, LayerProgram& layerProgram, int errorMessageSize, char* errorMessage);
|
||||||
bool CompilePreparedLayerProgram(const RuntimeRenderState& state, const std::string& fragmentShaderSource, LayerProgram& layerProgram, int errorMessageSize, char* errorMessage);
|
bool CompilePreparedLayerProgram(const RuntimeRenderState& state, const std::string& fragmentShaderSource, LayerProgram& layerProgram, int errorMessageSize, char* errorMessage);
|
||||||
bool CompileDecodeShader(int errorMessageSize, char* errorMessage);
|
bool CompileDecodeShader(int errorMessageSize, char* errorMessage);
|
||||||
|
bool CompileOutputPackShader(int errorMessageSize, char* errorMessage);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
OpenGLRenderer& mRenderer;
|
OpenGLRenderer& mRenderer;
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ bool TemporalHistoryBuffers::CreateRing(Ring& ring, unsigned effectiveLength, Te
|
|||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, frameWidth, frameHeight, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, NULL);
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, frameWidth, frameHeight, 0, GL_RGBA, GL_FLOAT, NULL);
|
||||||
|
|
||||||
glGenFramebuffers(1, &slot.framebuffer);
|
glGenFramebuffers(1, &slot.framebuffer);
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, slot.framebuffer);
|
glBindFramebuffer(GL_FRAMEBUFFER, slot.framebuffer);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "GLExtensions.h"
|
||||||
#include "ShaderTypes.h"
|
#include "ShaderTypes.h"
|
||||||
|
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
|||||||
@@ -1,377 +0,0 @@
|
|||||||
/* -LICENSE-START-
|
|
||||||
** Copyright (c) 2012 Blackmagic Design
|
|
||||||
**
|
|
||||||
** Permission is hereby granted, free of charge, to any person or organization
|
|
||||||
** obtaining a copy of the software and accompanying documentation (the
|
|
||||||
** "Software") to use, reproduce, display, distribute, sub-license, execute,
|
|
||||||
** and transmit the Software, and to prepare derivative works of the Software,
|
|
||||||
** and to permit third-parties to whom the Software is furnished to do so, in
|
|
||||||
** accordance with:
|
|
||||||
**
|
|
||||||
** (1) if the Software is obtained from Blackmagic Design, the End User License
|
|
||||||
** Agreement for the Software Development Kit ("EULA") available at
|
|
||||||
** https://www.blackmagicdesign.com/EULA/DeckLinkSDK; or
|
|
||||||
**
|
|
||||||
** (2) if the Software is obtained from any third party, such licensing terms
|
|
||||||
** as notified by that third party,
|
|
||||||
**
|
|
||||||
** and all subject to the following:
|
|
||||||
**
|
|
||||||
** (3) the copyright notices in the Software and this entire statement,
|
|
||||||
** including the above license grant, this restriction and the following
|
|
||||||
** disclaimer, must be included in all copies of the Software, in whole or in
|
|
||||||
** part, and all derivative works of the Software, unless such copies or
|
|
||||||
** derivative works are solely in the form of machine-executable object code
|
|
||||||
** generated by a source language processor.
|
|
||||||
**
|
|
||||||
** (4) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
||||||
** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
|
||||||
** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
|
||||||
** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
|
||||||
** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
||||||
** DEALINGS IN THE SOFTWARE.
|
|
||||||
**
|
|
||||||
** A copy of the Software is available free of charge at
|
|
||||||
** https://www.blackmagicdesign.com/desktopvideo_sdk under the EULA.
|
|
||||||
**
|
|
||||||
** -LICENSE-END-
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "VideoFrameTransfer.h"
|
|
||||||
#include "NativeHandles.h"
|
|
||||||
|
|
||||||
|
|
||||||
#define DVP_CHECK(cmd) { \
|
|
||||||
DVPStatus hr = (cmd); \
|
|
||||||
if (DVP_STATUS_OK != hr) { \
|
|
||||||
OutputDebugStringA( #cmd " failed\n" ); \
|
|
||||||
ExitProcess(hr); \
|
|
||||||
} \
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Initialise static members
|
|
||||||
bool VideoFrameTransfer::mInitialized = false;
|
|
||||||
bool VideoFrameTransfer::mUseDvp = false;
|
|
||||||
unsigned VideoFrameTransfer::mWidth = 0;
|
|
||||||
unsigned VideoFrameTransfer::mHeight = 0;
|
|
||||||
GLuint VideoFrameTransfer::mCaptureTexture = 0;
|
|
||||||
|
|
||||||
// NVIDIA specific static members
|
|
||||||
DVPBufferHandle VideoFrameTransfer::mDvpCaptureTextureHandle = 0;
|
|
||||||
DVPBufferHandle VideoFrameTransfer::mDvpPlaybackTextureHandle = 0;
|
|
||||||
uint32_t VideoFrameTransfer::mBufferAddrAlignment = 0;
|
|
||||||
uint32_t VideoFrameTransfer::mBufferGpuStrideAlignment = 0;
|
|
||||||
uint32_t VideoFrameTransfer::mSemaphoreAddrAlignment = 0;
|
|
||||||
uint32_t VideoFrameTransfer::mSemaphoreAllocSize = 0;
|
|
||||||
uint32_t VideoFrameTransfer::mSemaphorePayloadOffset = 0;
|
|
||||||
uint32_t VideoFrameTransfer::mSemaphorePayloadSize = 0;
|
|
||||||
|
|
||||||
|
|
||||||
bool VideoFrameTransfer::isNvidiaDvpAvailable()
|
|
||||||
{
|
|
||||||
// Look for supported graphics boards
|
|
||||||
const GLubyte* renderer = glGetString(GL_RENDERER);
|
|
||||||
if (renderer == NULL)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
bool hasDvp = (strstr((char*)renderer, "Quadro") != NULL);
|
|
||||||
return hasDvp;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool VideoFrameTransfer::isAMDPinnedMemoryAvailable()
|
|
||||||
{
|
|
||||||
// GL_AMD_pinned_memory presence indicates GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD buffer target is supported
|
|
||||||
const GLubyte* strExt = glGetString(GL_EXTENSIONS);
|
|
||||||
if (strExt == NULL)
|
|
||||||
{
|
|
||||||
// In a core profile context GL_EXTENSIONS is no longer queryable via glGetString().
|
|
||||||
// Treat this as "extension unavailable" for now; the fast-transfer path is optional.
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool hasAMDPinned = (strstr((char*)strExt, "GL_AMD_pinned_memory") != NULL);
|
|
||||||
return hasAMDPinned;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool VideoFrameTransfer::checkFastMemoryTransferAvailable()
|
|
||||||
{
|
|
||||||
return (isNvidiaDvpAvailable() || isAMDPinnedMemoryAvailable());
|
|
||||||
}
|
|
||||||
|
|
||||||
bool VideoFrameTransfer::initialize(unsigned width, unsigned height, GLuint captureTexture, GLuint playbackTexture)
|
|
||||||
{
|
|
||||||
if (mInitialized)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
bool hasDvp = isNvidiaDvpAvailable();
|
|
||||||
bool hasAMDPinned = isAMDPinnedMemoryAvailable();
|
|
||||||
|
|
||||||
if (!hasDvp && !hasAMDPinned)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
mUseDvp = hasDvp;
|
|
||||||
mWidth = width;
|
|
||||||
mHeight = height;
|
|
||||||
mCaptureTexture = captureTexture;
|
|
||||||
|
|
||||||
if (! initializeMemoryLocking(mWidth * mHeight * 4)) // BGRA uses 4 bytes per pixel
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (mUseDvp)
|
|
||||||
{
|
|
||||||
// DVP initialisation
|
|
||||||
DVP_CHECK(dvpInitGLContext(DVP_DEVICE_FLAGS_SHARE_APP_CONTEXT));
|
|
||||||
DVP_CHECK(dvpGetRequiredConstantsGLCtx( &mBufferAddrAlignment, &mBufferGpuStrideAlignment,
|
|
||||||
&mSemaphoreAddrAlignment, &mSemaphoreAllocSize,
|
|
||||||
&mSemaphorePayloadOffset, &mSemaphorePayloadSize));
|
|
||||||
|
|
||||||
// Register textures with DVP
|
|
||||||
DVP_CHECK(dvpCreateGPUTextureGL(captureTexture, &mDvpCaptureTextureHandle));
|
|
||||||
DVP_CHECK(dvpCreateGPUTextureGL(playbackTexture, &mDvpPlaybackTextureHandle));
|
|
||||||
}
|
|
||||||
|
|
||||||
mInitialized = true;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool VideoFrameTransfer::initializeMemoryLocking(unsigned memSize)
|
|
||||||
{
|
|
||||||
// Increase the process working set size to allow pinning of memory.
|
|
||||||
static SIZE_T dwMin = 0, dwMax = 0;
|
|
||||||
UniqueHandle processHandle(OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_SET_QUOTA, FALSE, GetCurrentProcessId()));
|
|
||||||
if (!processHandle.valid())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Retrieve the working set size of the process.
|
|
||||||
if (!dwMin && !GetProcessWorkingSetSize(processHandle.get(), &dwMin, &dwMax))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Allow for 80 frames to be locked
|
|
||||||
BOOL res = SetProcessWorkingSetSize(processHandle.get(), memSize * 80 + dwMin, memSize * 80 + (dwMax-dwMin));
|
|
||||||
if (!res)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// SyncInfo sets up a semaphore which is shared between the GPU and CPU and used to
|
|
||||||
// synchronise access to DVP buffers.
|
|
||||||
struct SyncInfo
|
|
||||||
{
|
|
||||||
SyncInfo(uint32_t semaphoreAllocSize, uint32_t semaphoreAddrAlignment);
|
|
||||||
~SyncInfo();
|
|
||||||
|
|
||||||
volatile uint32_t* mSem;
|
|
||||||
volatile uint32_t mReleaseValue;
|
|
||||||
volatile uint32_t mAcquireValue;
|
|
||||||
DVPSyncObjectHandle mDvpSync;
|
|
||||||
};
|
|
||||||
|
|
||||||
SyncInfo::SyncInfo(uint32_t semaphoreAllocSize, uint32_t semaphoreAddrAlignment)
|
|
||||||
{
|
|
||||||
mSem = (uint32_t*)_aligned_malloc(semaphoreAllocSize, semaphoreAddrAlignment);
|
|
||||||
|
|
||||||
// Initialise
|
|
||||||
mSem[0] = 0;
|
|
||||||
mReleaseValue = 0;
|
|
||||||
mAcquireValue = 0;
|
|
||||||
|
|
||||||
// Setup DVP sync object and import it
|
|
||||||
DVPSyncObjectDesc syncObjectDesc;
|
|
||||||
syncObjectDesc.externalClientWaitFunc = NULL;
|
|
||||||
syncObjectDesc.sem = (uint32_t*)mSem;
|
|
||||||
|
|
||||||
DVP_CHECK(dvpImportSyncObject(&syncObjectDesc, &mDvpSync));
|
|
||||||
}
|
|
||||||
|
|
||||||
SyncInfo::~SyncInfo()
|
|
||||||
{
|
|
||||||
DVP_CHECK(dvpFreeSyncObject(mDvpSync));
|
|
||||||
_aligned_free((void*)mSem);
|
|
||||||
}
|
|
||||||
|
|
||||||
VideoFrameTransfer::VideoFrameTransfer(unsigned long memSize, void* address, Direction direction) :
|
|
||||||
mBuffer(address),
|
|
||||||
mMemSize(memSize),
|
|
||||||
mDirection(direction),
|
|
||||||
mExtSync(NULL),
|
|
||||||
mGpuSync(NULL),
|
|
||||||
mDvpSysMemHandle(0),
|
|
||||||
mBufferHandle(0)
|
|
||||||
{
|
|
||||||
if (mUseDvp)
|
|
||||||
{
|
|
||||||
// Pin the memory
|
|
||||||
if (! VirtualLock(mBuffer, mMemSize))
|
|
||||||
throw std::runtime_error("Error pinning memory with VirtualLock");
|
|
||||||
|
|
||||||
// Create necessary sysmem and gpu sync objects
|
|
||||||
mExtSync = new SyncInfo(mSemaphoreAllocSize, mSemaphoreAddrAlignment);
|
|
||||||
mGpuSync = new SyncInfo(mSemaphoreAllocSize, mSemaphoreAddrAlignment);
|
|
||||||
|
|
||||||
// Register system memory buffers with DVP
|
|
||||||
DVPSysmemBufferDesc sysMemBuffersDesc;
|
|
||||||
sysMemBuffersDesc.width = mWidth;
|
|
||||||
sysMemBuffersDesc.height = mHeight;
|
|
||||||
sysMemBuffersDesc.stride = mWidth * 4;
|
|
||||||
sysMemBuffersDesc.format = DVP_BGRA;
|
|
||||||
sysMemBuffersDesc.type = DVP_UNSIGNED_BYTE;
|
|
||||||
sysMemBuffersDesc.size = mMemSize;
|
|
||||||
sysMemBuffersDesc.bufAddr = mBuffer;
|
|
||||||
|
|
||||||
if (mDirection == CPUtoGPU)
|
|
||||||
{
|
|
||||||
// A UYVY 4:2:2 frame is transferred to the GPU, rather than RGB 4:4:4, so width is halved
|
|
||||||
sysMemBuffersDesc.width /= 2;
|
|
||||||
sysMemBuffersDesc.stride /= 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
DVP_CHECK(dvpCreateBuffer(&sysMemBuffersDesc, &mDvpSysMemHandle));
|
|
||||||
DVP_CHECK(dvpBindToGLCtx(mDvpSysMemHandle));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Create an OpenGL buffer handle to use for pinned memory
|
|
||||||
GLuint bufferHandle;
|
|
||||||
glGenBuffers(1, &bufferHandle);
|
|
||||||
|
|
||||||
// Pin memory by binding buffer to special AMD target.
|
|
||||||
glBindBuffer(GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD, bufferHandle);
|
|
||||||
|
|
||||||
// glBufferData() sets up the address so any OpenGL operation on this buffer will use system memory directly
|
|
||||||
// (assumes address is aligned to 4k boundary).
|
|
||||||
glBufferData(GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD, mMemSize, address, GL_STREAM_DRAW);
|
|
||||||
GLenum result = glGetError();
|
|
||||||
if (result != GL_NO_ERROR)
|
|
||||||
{
|
|
||||||
throw std::runtime_error("Error pinning memory with glBufferData(GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD, ...)");
|
|
||||||
}
|
|
||||||
glBindBuffer(GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD, 0); // Unbind buffer to target
|
|
||||||
|
|
||||||
mBufferHandle = bufferHandle;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
VideoFrameTransfer::~VideoFrameTransfer()
|
|
||||||
{
|
|
||||||
if (mUseDvp)
|
|
||||||
{
|
|
||||||
DVP_CHECK(dvpUnbindFromGLCtx(mDvpSysMemHandle));
|
|
||||||
DVP_CHECK(dvpDestroyBuffer(mDvpSysMemHandle));
|
|
||||||
|
|
||||||
delete mExtSync;
|
|
||||||
delete mGpuSync;
|
|
||||||
|
|
||||||
VirtualUnlock(mBuffer, mMemSize);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// The buffer is un-pinned by the GPU when the buffer is deleted
|
|
||||||
glDeleteBuffers(1, &mBufferHandle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool VideoFrameTransfer::performFrameTransfer()
|
|
||||||
{
|
|
||||||
if (mUseDvp)
|
|
||||||
{
|
|
||||||
// NVIDIA DVP transfers
|
|
||||||
DVPStatus status;
|
|
||||||
|
|
||||||
mGpuSync->mReleaseValue++;
|
|
||||||
|
|
||||||
dvpBegin();
|
|
||||||
if (mDirection == CPUtoGPU)
|
|
||||||
{
|
|
||||||
// Copy from system memory to GPU texture
|
|
||||||
dvpMapBufferWaitDVP(mDvpCaptureTextureHandle);
|
|
||||||
status = dvpMemcpyLined( mDvpSysMemHandle, mExtSync->mDvpSync, mExtSync->mAcquireValue, DVP_TIMEOUT_IGNORED,
|
|
||||||
mDvpCaptureTextureHandle, mGpuSync->mDvpSync, mGpuSync->mReleaseValue, 0, mHeight);
|
|
||||||
dvpMapBufferEndDVP(mDvpCaptureTextureHandle);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Copy from GPU texture to system memory
|
|
||||||
dvpMapBufferWaitDVP(mDvpPlaybackTextureHandle);
|
|
||||||
status = dvpMemcpyLined( mDvpPlaybackTextureHandle, mExtSync->mDvpSync, mExtSync->mReleaseValue, DVP_TIMEOUT_IGNORED,
|
|
||||||
mDvpSysMemHandle, mGpuSync->mDvpSync, mGpuSync->mReleaseValue, 0, mHeight);
|
|
||||||
dvpMapBufferEndDVP(mDvpPlaybackTextureHandle);
|
|
||||||
}
|
|
||||||
dvpEnd();
|
|
||||||
|
|
||||||
return (status == DVP_STATUS_OK);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// AMD pinned memory transfers
|
|
||||||
if (mDirection == CPUtoGPU)
|
|
||||||
{
|
|
||||||
glEnable(GL_TEXTURE_2D);
|
|
||||||
|
|
||||||
// Use a pinned buffer for the GL_PIXEL_UNPACK_BUFFER target
|
|
||||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, mBufferHandle);
|
|
||||||
glBindTexture(GL_TEXTURE_2D, mCaptureTexture);
|
|
||||||
|
|
||||||
// NULL for last arg indicates use current GL_PIXEL_UNPACK_BUFFER target as texture data
|
|
||||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, mWidth/2, mHeight, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, NULL);
|
|
||||||
|
|
||||||
// Ensure pinned texture has been transferred to GPU before we draw with it
|
|
||||||
GLsync fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
|
||||||
glClientWaitSync(fence, GL_SYNC_FLUSH_COMMANDS_BIT, 40 * 1000 * 1000); // timeout in nanosec
|
|
||||||
glDeleteSync(fence);
|
|
||||||
|
|
||||||
glBindTexture(GL_TEXTURE_2D, 0);
|
|
||||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
|
||||||
glDisable(GL_TEXTURE_2D);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Use a PIXEL PACK BUFFER to read back pixels
|
|
||||||
glBindBuffer(GL_PIXEL_PACK_BUFFER, mBufferHandle);
|
|
||||||
glReadPixels(0, 0, mWidth, mHeight, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, NULL);
|
|
||||||
|
|
||||||
// Ensure GPU has processed all commands in the pipeline up to this point, before memory is read by the CPU
|
|
||||||
GLsync fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
|
||||||
glClientWaitSync(fence, GL_SYNC_FLUSH_COMMANDS_BIT, 40 * 1000 * 1000); // timeout in nanosec
|
|
||||||
glDeleteSync(fence);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (glGetError() == GL_NO_ERROR);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void VideoFrameTransfer::waitForTransferComplete()
|
|
||||||
{
|
|
||||||
if (!mUseDvp)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Block until buffer has completely transferred between GPU and CPU buffer
|
|
||||||
dvpBegin();
|
|
||||||
dvpSyncObjClientWaitComplete(mGpuSync->mDvpSync, DVP_TIMEOUT_IGNORED);
|
|
||||||
dvpEnd();
|
|
||||||
}
|
|
||||||
|
|
||||||
void VideoFrameTransfer::beginTextureInUse(Direction direction)
|
|
||||||
{
|
|
||||||
if (!mUseDvp)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (direction == CPUtoGPU)
|
|
||||||
dvpMapBufferWaitAPI(mDvpCaptureTextureHandle);
|
|
||||||
else
|
|
||||||
dvpMapBufferWaitAPI(mDvpPlaybackTextureHandle);
|
|
||||||
}
|
|
||||||
|
|
||||||
void VideoFrameTransfer::endTextureInUse(Direction direction)
|
|
||||||
{
|
|
||||||
if (!mUseDvp)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (direction == CPUtoGPU)
|
|
||||||
dvpMapBufferEndAPI(mDvpCaptureTextureHandle);
|
|
||||||
else
|
|
||||||
dvpMapBufferEndAPI(mDvpPlaybackTextureHandle);
|
|
||||||
}
|
|
||||||
@@ -1,109 +0,0 @@
|
|||||||
/* -LICENSE-START-
|
|
||||||
** Copyright (c) 2012 Blackmagic Design
|
|
||||||
**
|
|
||||||
** Permission is hereby granted, free of charge, to any person or organization
|
|
||||||
** obtaining a copy of the software and accompanying documentation (the
|
|
||||||
** "Software") to use, reproduce, display, distribute, sub-license, execute,
|
|
||||||
** and transmit the Software, and to prepare derivative works of the Software,
|
|
||||||
** and to permit third-parties to whom the Software is furnished to do so, in
|
|
||||||
** accordance with:
|
|
||||||
**
|
|
||||||
** (1) if the Software is obtained from Blackmagic Design, the End User License
|
|
||||||
** Agreement for the Software Development Kit ("EULA") available at
|
|
||||||
** https://www.blackmagicdesign.com/EULA/DeckLinkSDK; or
|
|
||||||
**
|
|
||||||
** (2) if the Software is obtained from any third party, such licensing terms
|
|
||||||
** as notified by that third party,
|
|
||||||
**
|
|
||||||
** and all subject to the following:
|
|
||||||
**
|
|
||||||
** (3) the copyright notices in the Software and this entire statement,
|
|
||||||
** including the above license grant, this restriction and the following
|
|
||||||
** disclaimer, must be included in all copies of the Software, in whole or in
|
|
||||||
** part, and all derivative works of the Software, unless such copies or
|
|
||||||
** derivative works are solely in the form of machine-executable object code
|
|
||||||
** generated by a source language processor.
|
|
||||||
**
|
|
||||||
** (4) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
||||||
** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
|
||||||
** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
|
||||||
** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
|
||||||
** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
||||||
** DEALINGS IN THE SOFTWARE.
|
|
||||||
**
|
|
||||||
** A copy of the Software is available free of charge at
|
|
||||||
** https://www.blackmagicdesign.com/desktopvideo_sdk under the EULA.
|
|
||||||
**
|
|
||||||
** -LICENSE-END-
|
|
||||||
*/
|
|
||||||
#ifndef __VIDEO_FRAME_TRANSFER_H__
|
|
||||||
#define __VIDEO_FRAME_TRANSFER_H__
|
|
||||||
|
|
||||||
#include "GLExtensions.h"
|
|
||||||
#include <stdexcept>
|
|
||||||
#include <map>
|
|
||||||
|
|
||||||
// NVIDIA GPU Direct For Video with OpenGL requires the following two headers.
|
|
||||||
// See the NVIDIA website to check if your graphics card is supported.
|
|
||||||
#include <DVPAPI.h>
|
|
||||||
#include <dvpapi_gl.h>
|
|
||||||
|
|
||||||
struct SyncInfo;
|
|
||||||
|
|
||||||
|
|
||||||
// Class for performing efficient frame memory transfers between the CPU and GPU,
|
|
||||||
// using NVIDIA and AMD extensions.
|
|
||||||
class VideoFrameTransfer
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
enum Direction
|
|
||||||
{
|
|
||||||
CPUtoGPU,
|
|
||||||
GPUtoCPU
|
|
||||||
};
|
|
||||||
|
|
||||||
VideoFrameTransfer(unsigned long memSize, void* address, Direction direction);
|
|
||||||
~VideoFrameTransfer();
|
|
||||||
|
|
||||||
static bool checkFastMemoryTransferAvailable();
|
|
||||||
static bool initialize(unsigned width, unsigned height, GLuint captureTexture, GLuint playbackTexture);
|
|
||||||
static void beginTextureInUse(Direction direction);
|
|
||||||
static void endTextureInUse(Direction direction);
|
|
||||||
|
|
||||||
bool performFrameTransfer();
|
|
||||||
void waitForTransferComplete();
|
|
||||||
|
|
||||||
private:
|
|
||||||
static bool isNvidiaDvpAvailable();
|
|
||||||
static bool isAMDPinnedMemoryAvailable();
|
|
||||||
static bool initializeMemoryLocking(unsigned memSize);
|
|
||||||
|
|
||||||
void* mBuffer;
|
|
||||||
unsigned long mMemSize;
|
|
||||||
Direction mDirection;
|
|
||||||
static bool mInitialized;
|
|
||||||
static bool mUseDvp;
|
|
||||||
static unsigned mWidth;
|
|
||||||
static unsigned mHeight;
|
|
||||||
static GLuint mCaptureTexture;
|
|
||||||
|
|
||||||
// NVIDIA GPU Direct for Video support
|
|
||||||
SyncInfo* mExtSync;
|
|
||||||
SyncInfo* mGpuSync;
|
|
||||||
DVPBufferHandle mDvpSysMemHandle;
|
|
||||||
|
|
||||||
static DVPBufferHandle mDvpCaptureTextureHandle;
|
|
||||||
static DVPBufferHandle mDvpPlaybackTextureHandle;
|
|
||||||
static uint32_t mBufferAddrAlignment;
|
|
||||||
static uint32_t mBufferGpuStrideAlignment;
|
|
||||||
static uint32_t mSemaphoreAddrAlignment;
|
|
||||||
static uint32_t mSemaphoreAllocSize;
|
|
||||||
static uint32_t mSemaphorePayloadOffset;
|
|
||||||
static uint32_t mSemaphorePayloadSize;
|
|
||||||
|
|
||||||
// GPU buffer bound to the target GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD for pinned memory
|
|
||||||
GLuint mBufferHandle;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
79
tests/VideoIOFormatTests.cpp
Normal file
79
tests/VideoIOFormatTests.cpp
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
#include "VideoIOFormat.h"
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
int gFailures = 0;
|
||||||
|
|
||||||
|
void Expect(bool condition, const char* message)
|
||||||
|
{
|
||||||
|
if (condition)
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::cerr << "FAIL: " << message << "\n";
|
||||||
|
++gFailures;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestPreferredFormatSelection()
|
||||||
|
{
|
||||||
|
Expect(ChoosePreferredVideoIOFormat(true) == VideoIOPixelFormat::V210, "10-bit is preferred when supported");
|
||||||
|
Expect(ChoosePreferredVideoIOFormat(false) == VideoIOPixelFormat::Uyvy8, "8-bit is used as fallback");
|
||||||
|
Expect(DeckLinkPixelFormatForVideoIO(VideoIOPixelFormat::V210) == bmdFormat10BitYUV, "v210 maps to DeckLink 10-bit YUV");
|
||||||
|
Expect(DeckLinkPixelFormatForVideoIO(VideoIOPixelFormat::Uyvy8) == bmdFormat8BitYUV, "UYVY maps to DeckLink 8-bit YUV");
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestRowByteHelpers()
|
||||||
|
{
|
||||||
|
Expect(MinimumV210RowBytes(1920) == 5120, "1920-wide v210 active row bytes");
|
||||||
|
Expect(MinimumV210RowBytes(1280) == 3424, "1280-wide v210 active row bytes rounds up to six-pixel group");
|
||||||
|
Expect(MinimumV210RowBytes(3840) == 10240, "3840-wide v210 active row bytes");
|
||||||
|
Expect(PackedTextureWidthFromRowBytes(5120) == 1280, "packed texture width is row bytes divided into RGBA byte texels");
|
||||||
|
Expect(ActiveV210WordsForWidth(1920) == 1280, "active v210 words match 1920 width");
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestV210PackUnpack()
|
||||||
|
{
|
||||||
|
V210SixPixelBlock input;
|
||||||
|
input.y = { 64, 128, 256, 512, 768, 940 };
|
||||||
|
input.cb = { 64, 512, 960 };
|
||||||
|
input.cr = { 960, 512, 64 };
|
||||||
|
|
||||||
|
const V210SixPixelBlock output = UnpackV210Block(PackV210Block(input));
|
||||||
|
Expect(output.y == input.y, "v210 luma survives pack/unpack");
|
||||||
|
Expect(output.cb == input.cb, "v210 Cb survives pack/unpack");
|
||||||
|
Expect(output.cr == input.cr, "v210 Cr survives pack/unpack");
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestRec709LegalRanges()
|
||||||
|
{
|
||||||
|
const V210CodeValues black = Rec709RgbToLegalV210(0.0f, 0.0f, 0.0f);
|
||||||
|
const V210CodeValues grey = Rec709RgbToLegalV210(0.5f, 0.5f, 0.5f);
|
||||||
|
const V210CodeValues white = Rec709RgbToLegalV210(1.0f, 1.0f, 1.0f);
|
||||||
|
|
||||||
|
Expect(black.y == 64, "black maps to legal-range 10-bit luma minimum");
|
||||||
|
Expect(white.y == 940, "white maps to legal-range 10-bit luma maximum");
|
||||||
|
Expect(std::abs(static_cast<int>(grey.y) - 502) <= 1, "middle grey maps near legal-range midpoint");
|
||||||
|
Expect(black.cb == 512 && black.cr == 512, "black keeps neutral chroma");
|
||||||
|
Expect(grey.cb == 512 && grey.cr == 512, "grey keeps neutral chroma");
|
||||||
|
Expect(white.cb == 512 && white.cr == 512, "white keeps neutral chroma");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
TestPreferredFormatSelection();
|
||||||
|
TestRowByteHelpers();
|
||||||
|
TestV210PackUnpack();
|
||||||
|
TestRec709LegalRanges();
|
||||||
|
|
||||||
|
if (gFailures != 0)
|
||||||
|
{
|
||||||
|
std::cerr << gFailures << " VideoIOFormat test failure(s).\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "VideoIOFormat tests passed.\n";
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user