Refactor
This commit is contained in:
@@ -36,43 +36,62 @@ if(NOT EXISTS "${SLANG_LICENSE_FILE}")
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(APP_SOURCES
|
set(APP_SOURCES
|
||||||
"${APP_DIR}/ControlServer.cpp"
|
|
||||||
"${APP_DIR}/ControlServer.h"
|
|
||||||
"${APP_DIR}/DeckLinkAPI_i.c"
|
"${APP_DIR}/DeckLinkAPI_i.c"
|
||||||
"${APP_DIR}/GLExtensions.cpp"
|
"${APP_DIR}/control/ControlServer.cpp"
|
||||||
"${APP_DIR}/GLExtensions.h"
|
"${APP_DIR}/control/ControlServer.h"
|
||||||
|
"${APP_DIR}/control/OscServer.cpp"
|
||||||
|
"${APP_DIR}/control/OscServer.h"
|
||||||
|
"${APP_DIR}/control/RuntimeControlBridge.cpp"
|
||||||
|
"${APP_DIR}/control/RuntimeControlBridge.h"
|
||||||
|
"${APP_DIR}/decklink/DeckLinkDisplayMode.cpp"
|
||||||
|
"${APP_DIR}/decklink/DeckLinkDisplayMode.h"
|
||||||
|
"${APP_DIR}/decklink/DeckLinkFrameTransfer.cpp"
|
||||||
|
"${APP_DIR}/decklink/DeckLinkFrameTransfer.h"
|
||||||
|
"${APP_DIR}/gl/GLExtensions.cpp"
|
||||||
|
"${APP_DIR}/gl/GLExtensions.h"
|
||||||
|
"${APP_DIR}/gl/GlScopedObjects.h"
|
||||||
|
"${APP_DIR}/gl/OpenGLComposite.cpp"
|
||||||
|
"${APP_DIR}/gl/OpenGLComposite.h"
|
||||||
|
"${APP_DIR}/gl/OpenGLCompositeRuntimeControls.cpp"
|
||||||
|
"${APP_DIR}/gl/Std140Buffer.h"
|
||||||
|
"${APP_DIR}/gl/TextRasterizer.cpp"
|
||||||
|
"${APP_DIR}/gl/TextRasterizer.h"
|
||||||
|
"${APP_DIR}/gl/TextureAssetLoader.cpp"
|
||||||
|
"${APP_DIR}/gl/TextureAssetLoader.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"
|
||||||
"${APP_DIR}/NativeHandles.h"
|
"${APP_DIR}/platform/NativeHandles.h"
|
||||||
"${APP_DIR}/NativeSockets.h"
|
"${APP_DIR}/platform/NativeSockets.h"
|
||||||
"${APP_DIR}/OpenGLComposite.cpp"
|
|
||||||
"${APP_DIR}/OpenGLComposite.h"
|
|
||||||
"${APP_DIR}/OscServer.cpp"
|
|
||||||
"${APP_DIR}/OscServer.h"
|
|
||||||
"${APP_DIR}/resource.h"
|
"${APP_DIR}/resource.h"
|
||||||
"${APP_DIR}/RuntimeHost.cpp"
|
"${APP_DIR}/runtime/RuntimeHost.cpp"
|
||||||
"${APP_DIR}/RuntimeHost.h"
|
"${APP_DIR}/runtime/RuntimeHost.h"
|
||||||
"${APP_DIR}/RuntimeJson.cpp"
|
"${APP_DIR}/runtime/RuntimeJson.cpp"
|
||||||
"${APP_DIR}/RuntimeJson.h"
|
"${APP_DIR}/runtime/RuntimeJson.h"
|
||||||
"${APP_DIR}/RuntimeParameterUtils.cpp"
|
"${APP_DIR}/runtime/RuntimeParameterUtils.cpp"
|
||||||
"${APP_DIR}/RuntimeParameterUtils.h"
|
"${APP_DIR}/runtime/RuntimeParameterUtils.h"
|
||||||
"${APP_DIR}/ShaderCompiler.cpp"
|
"${APP_DIR}/shader/ShaderCompiler.cpp"
|
||||||
"${APP_DIR}/ShaderCompiler.h"
|
"${APP_DIR}/shader/ShaderCompiler.h"
|
||||||
"${APP_DIR}/ShaderPackageRegistry.cpp"
|
"${APP_DIR}/shader/ShaderPackageRegistry.cpp"
|
||||||
"${APP_DIR}/ShaderPackageRegistry.h"
|
"${APP_DIR}/shader/ShaderPackageRegistry.h"
|
||||||
"${APP_DIR}/ShaderTypes.h"
|
"${APP_DIR}/shader/ShaderTypes.h"
|
||||||
"${APP_DIR}/stdafx.cpp"
|
"${APP_DIR}/stdafx.cpp"
|
||||||
"${APP_DIR}/stdafx.h"
|
"${APP_DIR}/stdafx.h"
|
||||||
"${APP_DIR}/targetver.h"
|
"${APP_DIR}/targetver.h"
|
||||||
"${APP_DIR}/VideoFrameTransfer.cpp"
|
|
||||||
"${APP_DIR}/VideoFrameTransfer.h"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
add_executable(LoopThroughWithOpenGLCompositing WIN32 ${APP_SOURCES})
|
add_executable(LoopThroughWithOpenGLCompositing WIN32 ${APP_SOURCES})
|
||||||
|
|
||||||
target_include_directories(LoopThroughWithOpenGLCompositing PRIVATE
|
target_include_directories(LoopThroughWithOpenGLCompositing PRIVATE
|
||||||
"${APP_DIR}"
|
"${APP_DIR}"
|
||||||
|
"${APP_DIR}/control"
|
||||||
|
"${APP_DIR}/decklink"
|
||||||
|
"${APP_DIR}/gl"
|
||||||
|
"${APP_DIR}/platform"
|
||||||
|
"${APP_DIR}/runtime"
|
||||||
|
"${APP_DIR}/shader"
|
||||||
"${GPUDIRECT_DIR}/include"
|
"${GPUDIRECT_DIR}/include"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -100,12 +119,13 @@ if(MSVC)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_executable(RuntimeJsonTests
|
add_executable(RuntimeJsonTests
|
||||||
"${APP_DIR}/RuntimeJson.cpp"
|
"${APP_DIR}/runtime/RuntimeJson.cpp"
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/tests/RuntimeJsonTests.cpp"
|
"${CMAKE_CURRENT_SOURCE_DIR}/tests/RuntimeJsonTests.cpp"
|
||||||
)
|
)
|
||||||
|
|
||||||
target_include_directories(RuntimeJsonTests PRIVATE
|
target_include_directories(RuntimeJsonTests PRIVATE
|
||||||
"${APP_DIR}"
|
"${APP_DIR}"
|
||||||
|
"${APP_DIR}/runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
if(MSVC)
|
if(MSVC)
|
||||||
@@ -116,13 +136,15 @@ enable_testing()
|
|||||||
add_test(NAME RuntimeJsonTests COMMAND RuntimeJsonTests)
|
add_test(NAME RuntimeJsonTests COMMAND RuntimeJsonTests)
|
||||||
|
|
||||||
add_executable(RuntimeParameterUtilsTests
|
add_executable(RuntimeParameterUtilsTests
|
||||||
"${APP_DIR}/RuntimeJson.cpp"
|
"${APP_DIR}/runtime/RuntimeJson.cpp"
|
||||||
"${APP_DIR}/RuntimeParameterUtils.cpp"
|
"${APP_DIR}/runtime/RuntimeParameterUtils.cpp"
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/tests/RuntimeParameterUtilsTests.cpp"
|
"${CMAKE_CURRENT_SOURCE_DIR}/tests/RuntimeParameterUtilsTests.cpp"
|
||||||
)
|
)
|
||||||
|
|
||||||
target_include_directories(RuntimeParameterUtilsTests PRIVATE
|
target_include_directories(RuntimeParameterUtilsTests PRIVATE
|
||||||
"${APP_DIR}"
|
"${APP_DIR}"
|
||||||
|
"${APP_DIR}/runtime"
|
||||||
|
"${APP_DIR}/shader"
|
||||||
)
|
)
|
||||||
|
|
||||||
if(MSVC)
|
if(MSVC)
|
||||||
@@ -131,14 +153,31 @@ endif()
|
|||||||
|
|
||||||
add_test(NAME RuntimeParameterUtilsTests COMMAND RuntimeParameterUtilsTests)
|
add_test(NAME RuntimeParameterUtilsTests COMMAND RuntimeParameterUtilsTests)
|
||||||
|
|
||||||
|
add_executable(Std140BufferTests
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/tests/Std140BufferTests.cpp"
|
||||||
|
)
|
||||||
|
|
||||||
|
target_include_directories(Std140BufferTests PRIVATE
|
||||||
|
"${APP_DIR}"
|
||||||
|
"${APP_DIR}/gl"
|
||||||
|
)
|
||||||
|
|
||||||
|
if(MSVC)
|
||||||
|
target_compile_options(Std140BufferTests PRIVATE /W3)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_test(NAME Std140BufferTests COMMAND Std140BufferTests)
|
||||||
|
|
||||||
add_executable(ShaderPackageRegistryTests
|
add_executable(ShaderPackageRegistryTests
|
||||||
"${APP_DIR}/RuntimeJson.cpp"
|
"${APP_DIR}/runtime/RuntimeJson.cpp"
|
||||||
"${APP_DIR}/ShaderPackageRegistry.cpp"
|
"${APP_DIR}/shader/ShaderPackageRegistry.cpp"
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/tests/ShaderPackageRegistryTests.cpp"
|
"${CMAKE_CURRENT_SOURCE_DIR}/tests/ShaderPackageRegistryTests.cpp"
|
||||||
)
|
)
|
||||||
|
|
||||||
target_include_directories(ShaderPackageRegistryTests PRIVATE
|
target_include_directories(ShaderPackageRegistryTests PRIVATE
|
||||||
"${APP_DIR}"
|
"${APP_DIR}"
|
||||||
|
"${APP_DIR}/runtime"
|
||||||
|
"${APP_DIR}/shader"
|
||||||
)
|
)
|
||||||
|
|
||||||
if(MSVC)
|
if(MSVC)
|
||||||
@@ -148,12 +187,14 @@ endif()
|
|||||||
add_test(NAME ShaderPackageRegistryTests COMMAND ShaderPackageRegistryTests)
|
add_test(NAME ShaderPackageRegistryTests COMMAND ShaderPackageRegistryTests)
|
||||||
|
|
||||||
add_executable(OscServerTests
|
add_executable(OscServerTests
|
||||||
"${APP_DIR}/OscServer.cpp"
|
"${APP_DIR}/control/OscServer.cpp"
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/tests/OscServerTests.cpp"
|
"${CMAKE_CURRENT_SOURCE_DIR}/tests/OscServerTests.cpp"
|
||||||
)
|
)
|
||||||
|
|
||||||
target_include_directories(OscServerTests PRIVATE
|
target_include_directories(OscServerTests PRIVATE
|
||||||
"${APP_DIR}"
|
"${APP_DIR}"
|
||||||
|
"${APP_DIR}/control"
|
||||||
|
"${APP_DIR}/platform"
|
||||||
)
|
)
|
||||||
|
|
||||||
target_link_libraries(OscServerTests PRIVATE
|
target_link_libraries(OscServerTests PRIVATE
|
||||||
|
|||||||
@@ -236,7 +236,6 @@ improve text rendering
|
|||||||
genlock
|
genlock
|
||||||
find a better UI libary
|
find a better UI libary
|
||||||
Logs
|
Logs
|
||||||
anamorphic desqueeze
|
|
||||||
refactor, cleanup of source files
|
refactor, cleanup of source files
|
||||||
display URL (Maybe clicakable) for control in the windows app (Not on the output)
|
display URL (Maybe clicakable) for control in the windows app (Not on the output)
|
||||||
Sound shader as seperate .slang in shader package?
|
Sound shader as seperate .slang in shader package?
|
||||||
|
|||||||
@@ -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>..\..\3rdParty\Blackmagic DeckLink SDK 16.0\Win\Samples\NVIDIA_GPUDirect\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>.;control;decklink;gl;..\..\3rdParty\Blackmagic DeckLink SDK 16.0\Win\Samples\NVIDIA_GPUDirect\include;%(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>
|
||||||
@@ -115,7 +115,7 @@
|
|||||||
</Midl>
|
</Midl>
|
||||||
<ClCompile>
|
<ClCompile>
|
||||||
<Optimization>Disabled</Optimization>
|
<Optimization>Disabled</Optimization>
|
||||||
<AdditionalIncludeDirectories>..\..\3rdParty\Blackmagic DeckLink SDK 16.0\Win\Samples\NVIDIA_GPUDirect\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>.;control;decklink;gl;..\..\3rdParty\Blackmagic DeckLink SDK 16.0\Win\Samples\NVIDIA_GPUDirect\include;%(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>
|
||||||
@@ -139,7 +139,7 @@
|
|||||||
<ClCompile>
|
<ClCompile>
|
||||||
<Optimization>MaxSpeed</Optimization>
|
<Optimization>MaxSpeed</Optimization>
|
||||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||||
<AdditionalIncludeDirectories>..\..\3rdParty\Blackmagic DeckLink SDK 16.0\Win\Samples\NVIDIA_GPUDirect\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>.;control;decklink;gl;..\..\3rdParty\Blackmagic DeckLink SDK 16.0\Win\Samples\NVIDIA_GPUDirect\include;%(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>
|
||||||
@@ -169,7 +169,7 @@
|
|||||||
<ClCompile>
|
<ClCompile>
|
||||||
<Optimization>MaxSpeed</Optimization>
|
<Optimization>MaxSpeed</Optimization>
|
||||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||||
<AdditionalIncludeDirectories>..\..\3rdParty\Blackmagic DeckLink SDK 16.0\Win\Samples\NVIDIA_GPUDirect\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>.;control;decklink;gl;..\..\3rdParty\Blackmagic DeckLink SDK 16.0\Win\Samples\NVIDIA_GPUDirect\include;%(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>
|
||||||
@@ -192,26 +192,26 @@
|
|||||||
</PostBuildEvent>
|
</PostBuildEvent>
|
||||||
</ItemDefinitionGroup>
|
</ItemDefinitionGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="GLExtensions.cpp" />
|
<ClCompile Include="gl\GLExtensions.cpp" />
|
||||||
<ClCompile Include="LoopThroughWithOpenGLCompositing.cpp" />
|
<ClCompile Include="LoopThroughWithOpenGLCompositing.cpp" />
|
||||||
<ClCompile Include="OpenGLComposite.cpp" />
|
<ClCompile Include="gl\OpenGLComposite.cpp" />
|
||||||
<ClCompile Include="stdafx.cpp">
|
<ClCompile Include="stdafx.cpp">
|
||||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
|
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
|
||||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
|
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
|
||||||
<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="VideoFrameTransfer.cpp" />
|
<ClCompile Include="gl\VideoFrameTransfer.cpp" />
|
||||||
<ClCompile Include="DeckLinkAPI_i.c" />
|
<ClCompile Include="DeckLinkAPI_i.c" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="GLExtensions.h" />
|
<ClInclude Include="gl\GLExtensions.h" />
|
||||||
<ClInclude Include="LoopThroughWithOpenGLCompositing.h" />
|
<ClInclude Include="LoopThroughWithOpenGLCompositing.h" />
|
||||||
<ClInclude Include="OpenGLComposite.h" />
|
<ClInclude Include="gl\OpenGLComposite.h" />
|
||||||
<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="VideoFrameTransfer.h" />
|
<ClInclude Include="gl\VideoFrameTransfer.h" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Image Include="LoopThroughWithOpenGLCompositing.ico" />
|
<Image Include="LoopThroughWithOpenGLCompositing.ico" />
|
||||||
|
|||||||
@@ -18,19 +18,19 @@
|
|||||||
</Filter>
|
</Filter>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="GLExtensions.cpp">
|
<ClCompile Include="gl\GLExtensions.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="LoopThroughWithOpenGLCompositing.cpp">
|
<ClCompile Include="LoopThroughWithOpenGLCompositing.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="OpenGLComposite.cpp">
|
<ClCompile Include="gl\OpenGLComposite.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="stdafx.cpp">
|
<ClCompile Include="stdafx.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="VideoFrameTransfer.cpp">
|
<ClCompile Include="gl\VideoFrameTransfer.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="DeckLinkAPI_i.c">
|
<ClCompile Include="DeckLinkAPI_i.c">
|
||||||
@@ -38,13 +38,13 @@
|
|||||||
</ClCompile>
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="GLExtensions.h">
|
<ClInclude Include="gl\GLExtensions.h">
|
||||||
<Filter>Header Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="LoopThroughWithOpenGLCompositing.h">
|
<ClInclude Include="LoopThroughWithOpenGLCompositing.h">
|
||||||
<Filter>Header Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="OpenGLComposite.h">
|
<ClInclude Include="gl\OpenGLComposite.h">
|
||||||
<Filter>Header Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="resource.h">
|
<ClInclude Include="resource.h">
|
||||||
@@ -56,7 +56,7 @@
|
|||||||
<ClInclude Include="targetver.h">
|
<ClInclude Include="targetver.h">
|
||||||
<Filter>Header Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="VideoFrameTransfer.h">
|
<ClInclude Include="gl\VideoFrameTransfer.h">
|
||||||
<Filter>Header Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
#include "RuntimeControlBridge.h"
|
||||||
|
|
||||||
|
#include "ControlServer.h"
|
||||||
|
#include "OpenGLComposite.h"
|
||||||
|
#include "OscServer.h"
|
||||||
|
#include "RuntimeHost.h"
|
||||||
|
|
||||||
|
bool StartRuntimeControlServices(
|
||||||
|
OpenGLComposite& composite,
|
||||||
|
RuntimeHost& runtimeHost,
|
||||||
|
ControlServer& controlServer,
|
||||||
|
OscServer& oscServer,
|
||||||
|
std::string& error)
|
||||||
|
{
|
||||||
|
ControlServer::Callbacks callbacks;
|
||||||
|
callbacks.getStateJson = [&composite]() { return composite.GetRuntimeStateJson(); };
|
||||||
|
callbacks.addLayer = [&composite](const std::string& shaderId, std::string& actionError) { return composite.AddLayer(shaderId, actionError); };
|
||||||
|
callbacks.removeLayer = [&composite](const std::string& layerId, std::string& actionError) { return composite.RemoveLayer(layerId, actionError); };
|
||||||
|
callbacks.moveLayer = [&composite](const std::string& layerId, int direction, std::string& actionError) { return composite.MoveLayer(layerId, direction, actionError); };
|
||||||
|
callbacks.moveLayerToIndex = [&composite](const std::string& layerId, std::size_t targetIndex, std::string& actionError) { return composite.MoveLayerToIndex(layerId, targetIndex, actionError); };
|
||||||
|
callbacks.setLayerBypass = [&composite](const std::string& layerId, bool bypassed, std::string& actionError) { return composite.SetLayerBypass(layerId, bypassed, actionError); };
|
||||||
|
callbacks.setLayerShader = [&composite](const std::string& layerId, const std::string& shaderId, std::string& actionError) { return composite.SetLayerShader(layerId, shaderId, actionError); };
|
||||||
|
callbacks.updateLayerParameter = [&composite](const std::string& layerId, const std::string& parameterId, const std::string& valueJson, std::string& actionError) {
|
||||||
|
return composite.UpdateLayerParameterJson(layerId, parameterId, valueJson, actionError);
|
||||||
|
};
|
||||||
|
callbacks.resetLayerParameters = [&composite](const std::string& layerId, std::string& actionError) { return composite.ResetLayerParameters(layerId, actionError); };
|
||||||
|
callbacks.saveStackPreset = [&composite](const std::string& presetName, std::string& actionError) { return composite.SaveStackPreset(presetName, actionError); };
|
||||||
|
callbacks.loadStackPreset = [&composite](const std::string& presetName, std::string& actionError) { return composite.LoadStackPreset(presetName, actionError); };
|
||||||
|
callbacks.reloadShader = [&composite](std::string& actionError) {
|
||||||
|
if (!composite.ReloadShader())
|
||||||
|
{
|
||||||
|
actionError = "Shader reload failed. See native app status for details.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!controlServer.Start(runtimeHost.GetUiRoot(), runtimeHost.GetDocsRoot(), runtimeHost.GetServerPort(), callbacks, error))
|
||||||
|
return false;
|
||||||
|
runtimeHost.SetServerPort(controlServer.GetPort());
|
||||||
|
|
||||||
|
OscServer::Callbacks oscCallbacks;
|
||||||
|
oscCallbacks.updateParameter = [&composite](const std::string& layerKey, const std::string& parameterKey, const std::string& valueJson, std::string& actionError) {
|
||||||
|
return composite.UpdateLayerParameterByControlKeyJson(layerKey, parameterKey, valueJson, actionError);
|
||||||
|
};
|
||||||
|
if (runtimeHost.GetOscPort() > 0 && !oscServer.Start(runtimeHost.GetOscPort(), oscCallbacks, error))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
class ControlServer;
|
||||||
|
class OpenGLComposite;
|
||||||
|
class OscServer;
|
||||||
|
class RuntimeHost;
|
||||||
|
|
||||||
|
bool StartRuntimeControlServices(
|
||||||
|
OpenGLComposite& composite,
|
||||||
|
RuntimeHost& runtimeHost,
|
||||||
|
ControlServer& controlServer,
|
||||||
|
OscServer& oscServer,
|
||||||
|
std::string& error);
|
||||||
@@ -0,0 +1,110 @@
|
|||||||
|
#include "DeckLinkDisplayMode.h"
|
||||||
|
|
||||||
|
#include <cctype>
|
||||||
|
|
||||||
|
std::string NormalizeModeToken(const std::string& value)
|
||||||
|
{
|
||||||
|
std::string normalized;
|
||||||
|
for (unsigned char ch : value)
|
||||||
|
{
|
||||||
|
if (std::isalnum(ch))
|
||||||
|
normalized.push_back(static_cast<char>(std::tolower(ch)));
|
||||||
|
}
|
||||||
|
return normalized;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ResolveConfiguredDisplayMode(const std::string& videoFormat, const std::string& frameRate, BMDDisplayMode& displayMode, std::string& displayModeName)
|
||||||
|
{
|
||||||
|
const std::string formatToken = NormalizeModeToken(videoFormat);
|
||||||
|
const std::string frameToken = NormalizeModeToken(frameRate);
|
||||||
|
const std::string combinedToken = formatToken + frameToken;
|
||||||
|
|
||||||
|
struct ModeOption
|
||||||
|
{
|
||||||
|
const char* token;
|
||||||
|
BMDDisplayMode mode;
|
||||||
|
const char* displayName;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const ModeOption options[] =
|
||||||
|
{
|
||||||
|
{ "720p50", bmdModeHD720p50, "720p50" },
|
||||||
|
{ "hd720p50", bmdModeHD720p50, "720p50" },
|
||||||
|
{ "720p5994", bmdModeHD720p5994, "720p59.94" },
|
||||||
|
{ "hd720p5994", bmdModeHD720p5994, "720p59.94" },
|
||||||
|
{ "720p60", bmdModeHD720p60, "720p60" },
|
||||||
|
{ "hd720p60", bmdModeHD720p60, "720p60" },
|
||||||
|
{ "1080i50", bmdModeHD1080i50, "1080i50" },
|
||||||
|
{ "hd1080i50", bmdModeHD1080i50, "1080i50" },
|
||||||
|
{ "1080i5994", bmdModeHD1080i5994, "1080i59.94" },
|
||||||
|
{ "hd1080i5994", bmdModeHD1080i5994, "1080i59.94" },
|
||||||
|
{ "1080i60", bmdModeHD1080i6000, "1080i60" },
|
||||||
|
{ "hd1080i60", bmdModeHD1080i6000, "1080i60" },
|
||||||
|
{ "1080p2398", bmdModeHD1080p2398, "1080p23.98" },
|
||||||
|
{ "hd1080p2398", bmdModeHD1080p2398, "1080p23.98" },
|
||||||
|
{ "1080p24", bmdModeHD1080p24, "1080p24" },
|
||||||
|
{ "hd1080p24", bmdModeHD1080p24, "1080p24" },
|
||||||
|
{ "1080p25", bmdModeHD1080p25, "1080p25" },
|
||||||
|
{ "hd1080p25", bmdModeHD1080p25, "1080p25" },
|
||||||
|
{ "1080p2997", bmdModeHD1080p2997, "1080p29.97" },
|
||||||
|
{ "hd1080p2997", bmdModeHD1080p2997, "1080p29.97" },
|
||||||
|
{ "1080p30", bmdModeHD1080p30, "1080p30" },
|
||||||
|
{ "hd1080p30", bmdModeHD1080p30, "1080p30" },
|
||||||
|
{ "1080p50", bmdModeHD1080p50, "1080p50" },
|
||||||
|
{ "hd1080p50", bmdModeHD1080p50, "1080p50" },
|
||||||
|
{ "1080p5994", bmdModeHD1080p5994, "1080p59.94" },
|
||||||
|
{ "hd1080p5994", bmdModeHD1080p5994, "1080p59.94" },
|
||||||
|
{ "1080p60", bmdModeHD1080p6000, "1080p60" },
|
||||||
|
{ "hd1080p60", bmdModeHD1080p6000, "1080p60" },
|
||||||
|
{ "2160p2398", bmdMode4K2160p2398, "2160p23.98" },
|
||||||
|
{ "4k2160p2398", bmdMode4K2160p2398, "2160p23.98" },
|
||||||
|
{ "2160p24", bmdMode4K2160p24, "2160p24" },
|
||||||
|
{ "4k2160p24", bmdMode4K2160p24, "2160p24" },
|
||||||
|
{ "2160p25", bmdMode4K2160p25, "2160p25" },
|
||||||
|
{ "4k2160p25", bmdMode4K2160p25, "2160p25" },
|
||||||
|
{ "2160p2997", bmdMode4K2160p2997, "2160p29.97" },
|
||||||
|
{ "4k2160p2997", bmdMode4K2160p2997, "2160p29.97" },
|
||||||
|
{ "2160p30", bmdMode4K2160p30, "2160p30" },
|
||||||
|
{ "4k2160p30", bmdMode4K2160p30, "2160p30" },
|
||||||
|
{ "2160p50", bmdMode4K2160p50, "2160p50" },
|
||||||
|
{ "4k2160p50", bmdMode4K2160p50, "2160p50" },
|
||||||
|
{ "2160p5994", bmdMode4K2160p5994, "2160p59.94" },
|
||||||
|
{ "4k2160p5994", bmdMode4K2160p5994, "2160p59.94" },
|
||||||
|
{ "2160p60", bmdMode4K2160p60, "2160p60" },
|
||||||
|
{ "4k2160p60", bmdMode4K2160p60, "2160p60" }
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const ModeOption& option : options)
|
||||||
|
{
|
||||||
|
if (combinedToken == option.token || (frameToken.empty() && formatToken == option.token))
|
||||||
|
{
|
||||||
|
displayMode = option.mode;
|
||||||
|
displayModeName = option.displayName;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FindDeckLinkDisplayMode(IDeckLinkDisplayModeIterator* iterator, BMDDisplayMode targetMode, IDeckLinkDisplayMode** foundMode)
|
||||||
|
{
|
||||||
|
if (!iterator || !foundMode)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
*foundMode = NULL;
|
||||||
|
IDeckLinkDisplayMode* candidate = NULL;
|
||||||
|
while (iterator->Next(&candidate) == S_OK)
|
||||||
|
{
|
||||||
|
if (candidate->GetDisplayMode() == targetMode)
|
||||||
|
{
|
||||||
|
*foundMode = candidate;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
candidate->Release();
|
||||||
|
candidate = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "DeckLinkAPI_h.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
std::string NormalizeModeToken(const std::string& value);
|
||||||
|
bool ResolveConfiguredDisplayMode(const std::string& videoFormat, const std::string& frameRate, BMDDisplayMode& displayMode, std::string& displayModeName);
|
||||||
|
bool FindDeckLinkDisplayMode(IDeckLinkDisplayModeIterator* iterator, BMDDisplayMode targetMode, IDeckLinkDisplayMode** foundMode);
|
||||||
@@ -0,0 +1,409 @@
|
|||||||
|
#include "DeckLinkFrameTransfer.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
|
||||||
|
////////////////////////////////////////////
|
||||||
|
CaptureDelegate::CaptureDelegate(OpenGLComposite* pOwner) :
|
||||||
|
m_pOwner(pOwner),
|
||||||
|
mRefCount(1)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT CaptureDelegate::QueryInterface(REFIID, LPVOID* ppv)
|
||||||
|
{
|
||||||
|
*ppv = NULL;
|
||||||
|
return E_NOINTERFACE;
|
||||||
|
}
|
||||||
|
|
||||||
|
ULONG CaptureDelegate::AddRef()
|
||||||
|
{
|
||||||
|
return InterlockedIncrement(&mRefCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
ULONG CaptureDelegate::Release()
|
||||||
|
{
|
||||||
|
int newCount = InterlockedDecrement(&mRefCount);
|
||||||
|
if (newCount == 0)
|
||||||
|
delete this;
|
||||||
|
return newCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT CaptureDelegate::VideoInputFrameArrived(IDeckLinkVideoInputFrame* inputFrame, IDeckLinkAudioInputPacket*)
|
||||||
|
{
|
||||||
|
if (!inputFrame)
|
||||||
|
{
|
||||||
|
// It's possible to receive a NULL inputFrame, but a valid audioPacket. Ignore audio-only frame.
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hasNoInputSource = (inputFrame->GetFlags() & bmdFrameHasNoInputSource) == bmdFrameHasNoInputSource;
|
||||||
|
m_pOwner->VideoFrameArrived(inputFrame, hasNoInputSource);
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT CaptureDelegate::VideoInputFormatChanged(BMDVideoInputFormatChangedEvents, IDeckLinkDisplayMode*, BMDDetectedVideoInputFormatFlags)
|
||||||
|
{
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////
|
||||||
|
// DeckLink Playout Delegate Class
|
||||||
|
////////////////////////////////////////////
|
||||||
|
PlayoutDelegate::PlayoutDelegate(OpenGLComposite* pOwner) :
|
||||||
|
m_pOwner(pOwner),
|
||||||
|
mRefCount(1)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT PlayoutDelegate::QueryInterface(REFIID, LPVOID* ppv)
|
||||||
|
{
|
||||||
|
*ppv = NULL;
|
||||||
|
return E_NOINTERFACE;
|
||||||
|
}
|
||||||
|
|
||||||
|
ULONG PlayoutDelegate::AddRef()
|
||||||
|
{
|
||||||
|
return InterlockedIncrement(&mRefCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
ULONG PlayoutDelegate::Release()
|
||||||
|
{
|
||||||
|
int newCount = InterlockedDecrement(&mRefCount);
|
||||||
|
if (newCount == 0)
|
||||||
|
delete this;
|
||||||
|
return newCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT PlayoutDelegate::ScheduledFrameCompleted(IDeckLinkVideoFrame* completedFrame, BMDOutputFrameCompletionResult result)
|
||||||
|
{
|
||||||
|
switch (result)
|
||||||
|
{
|
||||||
|
case bmdOutputFrameDisplayedLate:
|
||||||
|
OutputDebugStringA("ScheduledFrameCompleted() frame did not complete: Frame Displayed Late\n");
|
||||||
|
break;
|
||||||
|
case bmdOutputFrameDropped:
|
||||||
|
OutputDebugStringA("ScheduledFrameCompleted() frame did not complete: Frame Dropped\n");
|
||||||
|
break;
|
||||||
|
case bmdOutputFrameCompleted:
|
||||||
|
case bmdOutputFrameFlushed:
|
||||||
|
// Don't log bmdOutputFrameFlushed result since it is expected when Stop() is called
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
OutputDebugStringA("ScheduledFrameCompleted() frame did not complete: Unknown error\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
m_pOwner->PlayoutFrameCompleted(completedFrame, result);
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT PlayoutDelegate::ScheduledPlaybackHasStopped()
|
||||||
|
{
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
@@ -0,0 +1,147 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
#include <gl/gl.h>
|
||||||
|
|
||||||
|
#include <atlbase.h>
|
||||||
|
#include <atomic>
|
||||||
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "DeckLinkAPI_h.h"
|
||||||
|
#include "VideoFrameTransfer.h"
|
||||||
|
|
||||||
|
extern "C" const IID IID_PinnedMemoryAllocator;
|
||||||
|
|
||||||
|
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
|
||||||
|
////////////////////////////////////////////
|
||||||
|
class CaptureDelegate : public IDeckLinkInputCallback
|
||||||
|
{
|
||||||
|
OpenGLComposite* m_pOwner;
|
||||||
|
LONG mRefCount;
|
||||||
|
|
||||||
|
public:
|
||||||
|
CaptureDelegate(OpenGLComposite* pOwner);
|
||||||
|
|
||||||
|
// IUnknown needs only a dummy implementation
|
||||||
|
virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, LPVOID* ppv);
|
||||||
|
virtual ULONG STDMETHODCALLTYPE AddRef();
|
||||||
|
virtual ULONG STDMETHODCALLTYPE Release();
|
||||||
|
|
||||||
|
virtual HRESULT STDMETHODCALLTYPE VideoInputFrameArrived(IDeckLinkVideoInputFrame* videoFrame, IDeckLinkAudioInputPacket* audioPacket);
|
||||||
|
virtual HRESULT STDMETHODCALLTYPE VideoInputFormatChanged(BMDVideoInputFormatChangedEvents notificationEvents, IDeckLinkDisplayMode* newDisplayMode, BMDDetectedVideoInputFormatFlags detectedSignalFlags);
|
||||||
|
};
|
||||||
|
|
||||||
|
////////////////////////////////////////////
|
||||||
|
// Render Delegate Class
|
||||||
|
////////////////////////////////////////////
|
||||||
|
class PlayoutDelegate : public IDeckLinkVideoOutputCallback
|
||||||
|
{
|
||||||
|
OpenGLComposite* m_pOwner;
|
||||||
|
LONG mRefCount;
|
||||||
|
|
||||||
|
public:
|
||||||
|
PlayoutDelegate(OpenGLComposite* pOwner);
|
||||||
|
|
||||||
|
// IUnknown needs only a dummy implementation
|
||||||
|
virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, LPVOID* ppv);
|
||||||
|
virtual ULONG STDMETHODCALLTYPE AddRef();
|
||||||
|
virtual ULONG STDMETHODCALLTYPE Release();
|
||||||
|
|
||||||
|
virtual HRESULT STDMETHODCALLTYPE ScheduledFrameCompleted(IDeckLinkVideoFrame* completedFrame, BMDOutputFrameCompletionResult result);
|
||||||
|
virtual HRESULT STDMETHODCALLTYPE ScheduledPlaybackHasStopped();
|
||||||
|
};
|
||||||
57
apps/LoopThroughWithOpenGLCompositing/gl/GlScopedObjects.h
Normal file
57
apps/LoopThroughWithOpenGLCompositing/gl/GlScopedObjects.h
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <gl/gl.h>
|
||||||
|
|
||||||
|
class ScopedGlShader
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit ScopedGlShader(GLuint shader = 0) : mShader(shader) {}
|
||||||
|
~ScopedGlShader() { reset(); }
|
||||||
|
|
||||||
|
ScopedGlShader(const ScopedGlShader&) = delete;
|
||||||
|
ScopedGlShader& operator=(const ScopedGlShader&) = delete;
|
||||||
|
|
||||||
|
GLuint get() const { return mShader; }
|
||||||
|
GLuint release()
|
||||||
|
{
|
||||||
|
GLuint shader = mShader;
|
||||||
|
mShader = 0;
|
||||||
|
return shader;
|
||||||
|
}
|
||||||
|
void reset(GLuint shader = 0)
|
||||||
|
{
|
||||||
|
if (mShader != 0)
|
||||||
|
glDeleteShader(mShader);
|
||||||
|
mShader = shader;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
GLuint mShader;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ScopedGlProgram
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit ScopedGlProgram(GLuint program = 0) : mProgram(program) {}
|
||||||
|
~ScopedGlProgram() { reset(); }
|
||||||
|
|
||||||
|
ScopedGlProgram(const ScopedGlProgram&) = delete;
|
||||||
|
ScopedGlProgram& operator=(const ScopedGlProgram&) = delete;
|
||||||
|
|
||||||
|
GLuint get() const { return mProgram; }
|
||||||
|
GLuint release()
|
||||||
|
{
|
||||||
|
GLuint program = mProgram;
|
||||||
|
mProgram = 0;
|
||||||
|
return program;
|
||||||
|
}
|
||||||
|
void reset(GLuint program = 0)
|
||||||
|
{
|
||||||
|
if (mProgram != 0)
|
||||||
|
glDeleteProgram(mProgram);
|
||||||
|
mProgram = program;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
GLuint mProgram;
|
||||||
|
};
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -52,7 +52,7 @@
|
|||||||
#include <comutil.h>
|
#include <comutil.h>
|
||||||
#include "DeckLinkAPI_h.h"
|
#include "DeckLinkAPI_h.h"
|
||||||
|
|
||||||
#include "VideoFrameTransfer.h"
|
#include "GLExtensions.h"
|
||||||
#include "RuntimeHost.h"
|
#include "RuntimeHost.h"
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
@@ -237,139 +237,4 @@ private:
|
|||||||
unsigned temporalHistoryAvailableCountForLayer(const std::string& layerId) const;
|
unsigned temporalHistoryAvailableCountForLayer(const std::string& layerId) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
////////////////////////////////////////////
|
|
||||||
// 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; // Dual-purpose: allocator owns mem this points to, and to access transferFrame() via a QueryInterface
|
|
||||||
std::atomic<ULONG> mRefCount;
|
|
||||||
std::shared_ptr<void> mBuffer;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////
|
|
||||||
// Capture Delegate Class
|
|
||||||
////////////////////////////////////////////
|
|
||||||
|
|
||||||
class CaptureDelegate : public IDeckLinkInputCallback
|
|
||||||
{
|
|
||||||
OpenGLComposite* m_pOwner;
|
|
||||||
LONG mRefCount;
|
|
||||||
|
|
||||||
public:
|
|
||||||
CaptureDelegate (OpenGLComposite* pOwner);
|
|
||||||
|
|
||||||
// IUnknown needs only a dummy implementation
|
|
||||||
virtual HRESULT STDMETHODCALLTYPE QueryInterface (REFIID iid, LPVOID *ppv);
|
|
||||||
virtual ULONG STDMETHODCALLTYPE AddRef ();
|
|
||||||
virtual ULONG STDMETHODCALLTYPE Release ();
|
|
||||||
|
|
||||||
virtual HRESULT STDMETHODCALLTYPE VideoInputFrameArrived (IDeckLinkVideoInputFrame *videoFrame, IDeckLinkAudioInputPacket *audioPacket);
|
|
||||||
virtual HRESULT STDMETHODCALLTYPE VideoInputFormatChanged (BMDVideoInputFormatChangedEvents notificationEvents, IDeckLinkDisplayMode *newDisplayMode, BMDDetectedVideoInputFormatFlags detectedSignalFlags);
|
|
||||||
};
|
|
||||||
|
|
||||||
////////////////////////////////////////////
|
|
||||||
// Render Delegate Class
|
|
||||||
////////////////////////////////////////////
|
|
||||||
|
|
||||||
class PlayoutDelegate : public IDeckLinkVideoOutputCallback
|
|
||||||
{
|
|
||||||
OpenGLComposite* m_pOwner;
|
|
||||||
LONG mRefCount;
|
|
||||||
|
|
||||||
public:
|
|
||||||
PlayoutDelegate (OpenGLComposite* pOwner);
|
|
||||||
|
|
||||||
// IUnknown needs only a dummy implementation
|
|
||||||
virtual HRESULT STDMETHODCALLTYPE QueryInterface (REFIID iid, LPVOID *ppv);
|
|
||||||
virtual ULONG STDMETHODCALLTYPE AddRef ();
|
|
||||||
virtual ULONG STDMETHODCALLTYPE Release ();
|
|
||||||
|
|
||||||
virtual HRESULT STDMETHODCALLTYPE ScheduledFrameCompleted (IDeckLinkVideoFrame* completedFrame, BMDOutputFrameCompletionResult result);
|
|
||||||
virtual HRESULT STDMETHODCALLTYPE ScheduledPlaybackHasStopped ();
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // __OPENGL_COMPOSITE_H__
|
#endif // __OPENGL_COMPOSITE_H__
|
||||||
@@ -0,0 +1,127 @@
|
|||||||
|
#include "OpenGLComposite.h"
|
||||||
|
|
||||||
|
std::string OpenGLComposite::GetRuntimeStateJson() const
|
||||||
|
{
|
||||||
|
return mRuntimeHost ? mRuntimeHost->BuildStateJson() : "{}";
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenGLComposite::AddLayer(const std::string& shaderId, std::string& error)
|
||||||
|
{
|
||||||
|
if (!mRuntimeHost->AddLayer(shaderId, error))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
ReloadShader();
|
||||||
|
resetTemporalHistoryState();
|
||||||
|
broadcastRuntimeState();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenGLComposite::RemoveLayer(const std::string& layerId, std::string& error)
|
||||||
|
{
|
||||||
|
if (!mRuntimeHost->RemoveLayer(layerId, error))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
ReloadShader();
|
||||||
|
resetTemporalHistoryState();
|
||||||
|
broadcastRuntimeState();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenGLComposite::MoveLayer(const std::string& layerId, int direction, std::string& error)
|
||||||
|
{
|
||||||
|
if (!mRuntimeHost->MoveLayer(layerId, direction, error))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
ReloadShader();
|
||||||
|
resetTemporalHistoryState();
|
||||||
|
broadcastRuntimeState();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenGLComposite::MoveLayerToIndex(const std::string& layerId, std::size_t targetIndex, std::string& error)
|
||||||
|
{
|
||||||
|
if (!mRuntimeHost->MoveLayerToIndex(layerId, targetIndex, error))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
ReloadShader();
|
||||||
|
resetTemporalHistoryState();
|
||||||
|
broadcastRuntimeState();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenGLComposite::SetLayerBypass(const std::string& layerId, bool bypassed, std::string& error)
|
||||||
|
{
|
||||||
|
if (!mRuntimeHost->SetLayerBypass(layerId, bypassed, error))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
ReloadShader();
|
||||||
|
resetTemporalHistoryState();
|
||||||
|
broadcastRuntimeState();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenGLComposite::SetLayerShader(const std::string& layerId, const std::string& shaderId, std::string& error)
|
||||||
|
{
|
||||||
|
if (!mRuntimeHost->SetLayerShader(layerId, shaderId, error))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
ReloadShader();
|
||||||
|
resetTemporalHistoryState();
|
||||||
|
broadcastRuntimeState();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenGLComposite::UpdateLayerParameterJson(const std::string& layerId, const std::string& parameterId, const std::string& valueJson, std::string& error)
|
||||||
|
{
|
||||||
|
JsonValue parsedValue;
|
||||||
|
if (!ParseJson(valueJson, parsedValue, error))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!mRuntimeHost->UpdateLayerParameter(layerId, parameterId, parsedValue, error))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
broadcastRuntimeState();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenGLComposite::UpdateLayerParameterByControlKeyJson(const std::string& layerKey, const std::string& parameterKey, const std::string& valueJson, std::string& error)
|
||||||
|
{
|
||||||
|
JsonValue parsedValue;
|
||||||
|
if (!ParseJson(valueJson, parsedValue, error))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!mRuntimeHost->UpdateLayerParameterByControlKey(layerKey, parameterKey, parsedValue, error))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
broadcastRuntimeState();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenGLComposite::ResetLayerParameters(const std::string& layerId, std::string& error)
|
||||||
|
{
|
||||||
|
if (!mRuntimeHost->ResetLayerParameters(layerId, error))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
broadcastRuntimeState();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenGLComposite::SaveStackPreset(const std::string& presetName, std::string& error)
|
||||||
|
{
|
||||||
|
if (!mRuntimeHost->SaveStackPreset(presetName, error))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
broadcastRuntimeState();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenGLComposite::LoadStackPreset(const std::string& presetName, std::string& error)
|
||||||
|
{
|
||||||
|
if (!mRuntimeHost->LoadStackPreset(presetName, error))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
ReloadShader();
|
||||||
|
resetTemporalHistoryState();
|
||||||
|
broadcastRuntimeState();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
48
apps/LoopThroughWithOpenGLCompositing/gl/Std140Buffer.h
Normal file
48
apps/LoopThroughWithOpenGLCompositing/gl/Std140Buffer.h
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstring>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
inline std::size_t AlignStd140(std::size_t offset, std::size_t alignment)
|
||||||
|
{
|
||||||
|
const std::size_t mask = alignment - 1;
|
||||||
|
return (offset + mask) & ~mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename TValue>
|
||||||
|
inline void AppendStd140Value(std::vector<unsigned char>& buffer, std::size_t alignment, const TValue& value)
|
||||||
|
{
|
||||||
|
const std::size_t offset = AlignStd140(buffer.size(), alignment);
|
||||||
|
if (buffer.size() < offset + sizeof(TValue))
|
||||||
|
buffer.resize(offset + sizeof(TValue), 0);
|
||||||
|
std::memcpy(buffer.data() + offset, &value, sizeof(TValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void AppendStd140Float(std::vector<unsigned char>& buffer, float value)
|
||||||
|
{
|
||||||
|
AppendStd140Value(buffer, 4, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void AppendStd140Int(std::vector<unsigned char>& buffer, int value)
|
||||||
|
{
|
||||||
|
AppendStd140Value(buffer, 4, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void AppendStd140Vec2(std::vector<unsigned char>& buffer, float x, float y)
|
||||||
|
{
|
||||||
|
const std::size_t offset = AlignStd140(buffer.size(), 8);
|
||||||
|
if (buffer.size() < offset + sizeof(float) * 2)
|
||||||
|
buffer.resize(offset + sizeof(float) * 2, 0);
|
||||||
|
float values[2] = { x, y };
|
||||||
|
std::memcpy(buffer.data() + offset, values, sizeof(values));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void AppendStd140Vec4(std::vector<unsigned char>& buffer, float x, float y, float z, float w)
|
||||||
|
{
|
||||||
|
const std::size_t offset = AlignStd140(buffer.size(), 16);
|
||||||
|
if (buffer.size() < offset + sizeof(float) * 4)
|
||||||
|
buffer.resize(offset + sizeof(float) * 4, 0);
|
||||||
|
float values[4] = { x, y, z, w };
|
||||||
|
std::memcpy(buffer.data() + offset, values, sizeof(values));
|
||||||
|
}
|
||||||
316
apps/LoopThroughWithOpenGLCompositing/gl/TextRasterizer.cpp
Normal file
316
apps/LoopThroughWithOpenGLCompositing/gl/TextRasterizer.cpp
Normal file
@@ -0,0 +1,316 @@
|
|||||||
|
#include "TextRasterizer.h"
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cmath>
|
||||||
|
#include <cstring>
|
||||||
|
#include <fstream>
|
||||||
|
#include <gdiplus.h>
|
||||||
|
#include <memory>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
constexpr int kTextSdfSpread = 20;
|
||||||
|
constexpr unsigned kTextSdfBlurPasses = 1;
|
||||||
|
constexpr float kTextFontPixelSize = 144.0f;
|
||||||
|
constexpr float kTextLayoutPadding = 48.0f;
|
||||||
|
|
||||||
|
class GdiplusSession
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
GdiplusSession()
|
||||||
|
{
|
||||||
|
Gdiplus::GdiplusStartupInput startupInput;
|
||||||
|
mStarted = Gdiplus::GdiplusStartup(&mToken, &startupInput, NULL) == Gdiplus::Ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
~GdiplusSession()
|
||||||
|
{
|
||||||
|
if (mStarted)
|
||||||
|
Gdiplus::GdiplusShutdown(mToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
GdiplusSession(const GdiplusSession&) = delete;
|
||||||
|
GdiplusSession& operator=(const GdiplusSession&) = delete;
|
||||||
|
|
||||||
|
bool started() const { return mStarted; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
ULONG_PTR mToken = 0;
|
||||||
|
bool mStarted = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::wstring Utf8ToWide(const std::string& text)
|
||||||
|
{
|
||||||
|
if (text.empty())
|
||||||
|
return std::wstring();
|
||||||
|
const int required = MultiByteToWideChar(CP_UTF8, 0, text.c_str(), -1, NULL, 0);
|
||||||
|
if (required <= 1)
|
||||||
|
return std::wstring();
|
||||||
|
std::wstring wide(static_cast<std::size_t>(required - 1), L'\0');
|
||||||
|
MultiByteToWideChar(CP_UTF8, 0, text.c_str(), -1, wide.data(), required);
|
||||||
|
return wide;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<unsigned char> BuildLocalSdf(const std::vector<unsigned char>& alpha, unsigned width, unsigned height)
|
||||||
|
{
|
||||||
|
std::vector<unsigned char> sdf(static_cast<std::size_t>(width) * height * 4, 0);
|
||||||
|
for (unsigned y = 0; y < height; ++y)
|
||||||
|
{
|
||||||
|
for (unsigned x = 0; x < width; ++x)
|
||||||
|
{
|
||||||
|
const bool inside = alpha[static_cast<std::size_t>(y) * width + x] > 127;
|
||||||
|
int bestDistanceSq = kTextSdfSpread * kTextSdfSpread;
|
||||||
|
for (int oy = -kTextSdfSpread; oy <= kTextSdfSpread; ++oy)
|
||||||
|
{
|
||||||
|
const int sy = static_cast<int>(y) + oy;
|
||||||
|
if (sy < 0 || sy >= static_cast<int>(height))
|
||||||
|
continue;
|
||||||
|
for (int ox = -kTextSdfSpread; ox <= kTextSdfSpread; ++ox)
|
||||||
|
{
|
||||||
|
const int sx = static_cast<int>(x) + ox;
|
||||||
|
if (sx < 0 || sx >= static_cast<int>(width))
|
||||||
|
continue;
|
||||||
|
const bool sampleInside = alpha[static_cast<std::size_t>(sy) * width + sx] > 127;
|
||||||
|
if (sampleInside == inside)
|
||||||
|
continue;
|
||||||
|
const int distanceSq = ox * ox + oy * oy;
|
||||||
|
if (distanceSq < bestDistanceSq)
|
||||||
|
bestDistanceSq = distanceSq;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const float distance = std::sqrt(static_cast<float>(bestDistanceSq));
|
||||||
|
const float signedDistance = (inside ? 1.0f : -1.0f) * distance;
|
||||||
|
float normalized = 0.5f + signedDistance / static_cast<float>(kTextSdfSpread * 2);
|
||||||
|
const unsigned char sourceAlpha = alpha[static_cast<std::size_t>(y) * width + x];
|
||||||
|
if (sourceAlpha > 0 && sourceAlpha < 255)
|
||||||
|
normalized = static_cast<float>(sourceAlpha) / 255.0f;
|
||||||
|
if (normalized < 0.0f)
|
||||||
|
normalized = 0.0f;
|
||||||
|
if (normalized > 1.0f)
|
||||||
|
normalized = 1.0f;
|
||||||
|
const unsigned char value = static_cast<unsigned char>(normalized * 255.0f + 0.5f);
|
||||||
|
const std::size_t out = (static_cast<std::size_t>(y) * width + x) * 4;
|
||||||
|
sdf[out + 0] = value;
|
||||||
|
sdf[out + 1] = value;
|
||||||
|
sdf[out + 2] = value;
|
||||||
|
sdf[out + 3] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sdf;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<unsigned char> BuildTextCoverageTexture(const std::vector<unsigned char>& alpha, unsigned width, unsigned height)
|
||||||
|
{
|
||||||
|
std::vector<unsigned char> coverage(static_cast<std::size_t>(width) * height * 4, 0);
|
||||||
|
for (unsigned y = 0; y < height; ++y)
|
||||||
|
{
|
||||||
|
for (unsigned x = 0; x < width; ++x)
|
||||||
|
{
|
||||||
|
const unsigned char value = alpha[static_cast<std::size_t>(y) * width + x];
|
||||||
|
const std::size_t out = (static_cast<std::size_t>(y) * width + x) * 4;
|
||||||
|
coverage[out + 0] = value;
|
||||||
|
coverage[out + 1] = value;
|
||||||
|
coverage[out + 2] = value;
|
||||||
|
coverage[out + 3] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return coverage;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<unsigned char> FlipTextTextureForShaderUv(const std::vector<unsigned char>& pixels, unsigned width, unsigned height)
|
||||||
|
{
|
||||||
|
std::vector<unsigned char> flipped(pixels.size(), 0);
|
||||||
|
const std::size_t stride = static_cast<std::size_t>(width) * 4;
|
||||||
|
for (unsigned y = 0; y < height; ++y)
|
||||||
|
{
|
||||||
|
const std::size_t srcOffset = static_cast<std::size_t>(y) * stride;
|
||||||
|
const std::size_t dstOffset = static_cast<std::size_t>(height - 1 - y) * stride;
|
||||||
|
std::memcpy(flipped.data() + dstOffset, pixels.data() + srcOffset, stride);
|
||||||
|
}
|
||||||
|
return flipped;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<unsigned char> BlurTextSdf(const std::vector<unsigned char>& pixels, unsigned width, unsigned height, unsigned passes)
|
||||||
|
{
|
||||||
|
std::vector<unsigned char> current = pixels;
|
||||||
|
std::vector<unsigned char> next(pixels.size(), 0);
|
||||||
|
for (unsigned pass = 0; pass < passes; ++pass)
|
||||||
|
{
|
||||||
|
for (unsigned y = 0; y < height; ++y)
|
||||||
|
{
|
||||||
|
for (unsigned x = 0; x < width; ++x)
|
||||||
|
{
|
||||||
|
unsigned weightedTotal = 0;
|
||||||
|
unsigned weightSum = 0;
|
||||||
|
for (int oy = -1; oy <= 1; ++oy)
|
||||||
|
{
|
||||||
|
const int sy = static_cast<int>(y) + oy;
|
||||||
|
if (sy < 0 || sy >= static_cast<int>(height))
|
||||||
|
continue;
|
||||||
|
for (int ox = -1; ox <= 1; ++ox)
|
||||||
|
{
|
||||||
|
const int sx = static_cast<int>(x) + ox;
|
||||||
|
if (sx < 0 || sx >= static_cast<int>(width))
|
||||||
|
continue;
|
||||||
|
const unsigned weight = (ox == 0 && oy == 0) ? 4u : ((ox == 0 || oy == 0) ? 2u : 1u);
|
||||||
|
const std::size_t sample = (static_cast<std::size_t>(sy) * width + sx) * 4;
|
||||||
|
weightedTotal += static_cast<unsigned>(current[sample]) * weight;
|
||||||
|
weightSum += weight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const unsigned char value = static_cast<unsigned char>((weightedTotal + weightSum / 2) / weightSum);
|
||||||
|
const std::size_t out = (static_cast<std::size_t>(y) * width + x) * 4;
|
||||||
|
next[out + 0] = value;
|
||||||
|
next[out + 1] = value;
|
||||||
|
next[out + 2] = value;
|
||||||
|
next[out + 3] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
current.swap(next);
|
||||||
|
}
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteTextMaskDebugDump(const std::string& text, const std::vector<unsigned char>& alpha, const std::vector<unsigned char>& sdf, unsigned width, unsigned height)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
std::filesystem::path debugDir = std::filesystem::current_path() / "runtime";
|
||||||
|
std::filesystem::create_directories(debugDir);
|
||||||
|
|
||||||
|
auto writePgm = [width, height](const std::filesystem::path& path, const std::vector<unsigned char>& gray, std::size_t stride)
|
||||||
|
{
|
||||||
|
std::ofstream out(path, std::ios::binary);
|
||||||
|
if (!out)
|
||||||
|
return;
|
||||||
|
out << "P5\n" << width << " " << height << "\n255\n";
|
||||||
|
for (unsigned y = 0; y < height; ++y)
|
||||||
|
{
|
||||||
|
for (unsigned x = 0; x < width; ++x)
|
||||||
|
out.put(static_cast<char>(gray[(static_cast<std::size_t>(y) * width + x) * stride]));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
writePgm(debugDir / "text-mask-alpha-debug.pgm", alpha, 1);
|
||||||
|
writePgm(debugDir / "text-mask-sdf-debug.pgm", sdf, 4);
|
||||||
|
|
||||||
|
unsigned alphaMin = 255;
|
||||||
|
unsigned alphaMax = 0;
|
||||||
|
unsigned sdfMin = 255;
|
||||||
|
unsigned sdfMax = 0;
|
||||||
|
std::size_t alphaLit = 0;
|
||||||
|
std::size_t sdfLit = 0;
|
||||||
|
for (unsigned char value : alpha)
|
||||||
|
{
|
||||||
|
alphaMin = std::min<unsigned>(alphaMin, value);
|
||||||
|
alphaMax = std::max<unsigned>(alphaMax, value);
|
||||||
|
if (value > 0)
|
||||||
|
++alphaLit;
|
||||||
|
}
|
||||||
|
for (std::size_t index = 0; index < sdf.size(); index += 4)
|
||||||
|
{
|
||||||
|
const unsigned char value = sdf[index];
|
||||||
|
sdfMin = std::min<unsigned>(sdfMin, value);
|
||||||
|
sdfMax = std::max<unsigned>(sdfMax, value);
|
||||||
|
if (value > 127)
|
||||||
|
++sdfLit;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostringstream message;
|
||||||
|
message << "Text mask debug for '" << text << "': alpha min/max/lit=" << alphaMin << "/" << alphaMax << "/" << alphaLit
|
||||||
|
<< ", sdf min/max/gt127=" << sdfMin << "/" << sdfMax << "/" << sdfLit << "\n";
|
||||||
|
OutputDebugStringA(message.str().c_str());
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
OutputDebugStringA("Failed to write text mask debug dump.\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RasterizeTextSdf(const std::string& text, const std::filesystem::path& fontPath, std::vector<unsigned char>& sdf, std::string& error)
|
||||||
|
{
|
||||||
|
GdiplusSession gdiplus;
|
||||||
|
if (!gdiplus.started())
|
||||||
|
{
|
||||||
|
error = "Could not start GDI+ for text rendering.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Gdiplus::PrivateFontCollection fontCollection;
|
||||||
|
Gdiplus::FontFamily fallbackFamily(L"Arial");
|
||||||
|
Gdiplus::FontFamily* fontFamily = &fallbackFamily;
|
||||||
|
std::unique_ptr<Gdiplus::FontFamily[]> families;
|
||||||
|
const std::wstring wideFontPath = fontPath.empty() ? std::wstring() : fontPath.wstring();
|
||||||
|
if (!wideFontPath.empty())
|
||||||
|
{
|
||||||
|
if (fontCollection.AddFontFile(wideFontPath.c_str()) != Gdiplus::Ok)
|
||||||
|
{
|
||||||
|
error = "Could not load packaged font file for text rendering: " + fontPath.string();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const INT familyCount = fontCollection.GetFamilyCount();
|
||||||
|
if (familyCount <= 0)
|
||||||
|
{
|
||||||
|
error = "Packaged font did not contain a usable font family: " + fontPath.string();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
families.reset(new Gdiplus::FontFamily[familyCount]);
|
||||||
|
INT found = 0;
|
||||||
|
if (fontCollection.GetFamilies(familyCount, families.get(), &found) != Gdiplus::Ok || found <= 0)
|
||||||
|
{
|
||||||
|
error = "Could not read the packaged font family: " + fontPath.string();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
fontFamily = &families[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
Gdiplus::Bitmap bitmap(kTextTextureWidth, kTextTextureHeight, PixelFormat32bppARGB);
|
||||||
|
Gdiplus::Graphics graphics(&bitmap);
|
||||||
|
graphics.SetCompositingMode(Gdiplus::CompositingModeSourceCopy);
|
||||||
|
graphics.Clear(Gdiplus::Color(255, 0, 0, 0));
|
||||||
|
graphics.SetCompositingMode(Gdiplus::CompositingModeSourceOver);
|
||||||
|
graphics.SetTextRenderingHint(Gdiplus::TextRenderingHintAntiAlias);
|
||||||
|
graphics.SetSmoothingMode(Gdiplus::SmoothingModeHighQuality);
|
||||||
|
Gdiplus::Font font(fontFamily, kTextFontPixelSize, Gdiplus::FontStyleRegular, Gdiplus::UnitPixel);
|
||||||
|
Gdiplus::SolidBrush brush(Gdiplus::Color(255, 255, 255, 255));
|
||||||
|
Gdiplus::StringFormat format;
|
||||||
|
format.SetAlignment(Gdiplus::StringAlignmentNear);
|
||||||
|
format.SetLineAlignment(Gdiplus::StringAlignmentCenter);
|
||||||
|
format.SetFormatFlags(Gdiplus::StringFormatFlagsNoWrap | Gdiplus::StringFormatFlagsMeasureTrailingSpaces);
|
||||||
|
const Gdiplus::RectF layout(
|
||||||
|
kTextLayoutPadding,
|
||||||
|
0.0f,
|
||||||
|
static_cast<Gdiplus::REAL>(kTextTextureWidth) - (kTextLayoutPadding * 2.0f),
|
||||||
|
static_cast<Gdiplus::REAL>(kTextTextureHeight));
|
||||||
|
const std::wstring wideText = Utf8ToWide(text);
|
||||||
|
graphics.DrawString(wideText.c_str(), -1, &font, layout, &format, &brush);
|
||||||
|
|
||||||
|
std::vector<unsigned char> alpha(static_cast<std::size_t>(kTextTextureWidth) * kTextTextureHeight, 0);
|
||||||
|
for (unsigned y = 0; y < kTextTextureHeight; ++y)
|
||||||
|
{
|
||||||
|
for (unsigned x = 0; x < kTextTextureWidth; ++x)
|
||||||
|
{
|
||||||
|
Gdiplus::Color pixel;
|
||||||
|
bitmap.GetPixel(x, y, &pixel);
|
||||||
|
BYTE luminance = pixel.GetRed();
|
||||||
|
if (pixel.GetGreen() > luminance)
|
||||||
|
luminance = pixel.GetGreen();
|
||||||
|
if (pixel.GetBlue() > luminance)
|
||||||
|
luminance = pixel.GetBlue();
|
||||||
|
alpha[static_cast<std::size_t>(y) * kTextTextureWidth + x] = static_cast<unsigned char>(luminance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sdf = BuildTextCoverageTexture(alpha, kTextTextureWidth, kTextTextureHeight);
|
||||||
|
sdf = BlurTextSdf(sdf, kTextTextureWidth, kTextTextureHeight, kTextSdfBlurPasses);
|
||||||
|
sdf = FlipTextTextureForShaderUv(sdf, kTextTextureWidth, kTextTextureHeight);
|
||||||
|
WriteTextMaskDebugDump(text, alpha, sdf, kTextTextureWidth, kTextTextureHeight);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
10
apps/LoopThroughWithOpenGLCompositing/gl/TextRasterizer.h
Normal file
10
apps/LoopThroughWithOpenGLCompositing/gl/TextRasterizer.h
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <filesystem>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
constexpr unsigned kTextTextureWidth = 2048;
|
||||||
|
constexpr unsigned kTextTextureHeight = 256;
|
||||||
|
|
||||||
|
bool RasterizeTextSdf(const std::string& text, const std::filesystem::path& fontPath, std::vector<unsigned char>& sdf, std::string& error);
|
||||||
114
apps/LoopThroughWithOpenGLCompositing/gl/TextureAssetLoader.cpp
Normal file
114
apps/LoopThroughWithOpenGLCompositing/gl/TextureAssetLoader.cpp
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
#include "TextureAssetLoader.h"
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
#include <wincodec.h>
|
||||||
|
|
||||||
|
#include <atlbase.h>
|
||||||
|
#include <cstring>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
bool LoadTextureAsset(const ShaderTextureAsset& textureAsset, GLuint& textureId, std::string& error)
|
||||||
|
{
|
||||||
|
textureId = 0;
|
||||||
|
|
||||||
|
HRESULT comInitResult = CoInitializeEx(NULL, COINIT_MULTITHREADED);
|
||||||
|
const bool shouldUninitializeCom = (comInitResult == S_OK || comInitResult == S_FALSE);
|
||||||
|
if (FAILED(comInitResult) && comInitResult != RPC_E_CHANGED_MODE)
|
||||||
|
{
|
||||||
|
error = "Could not initialize COM to load shader texture assets.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
CComPtr<IWICImagingFactory> imagingFactory;
|
||||||
|
HRESULT result = CoCreateInstance(CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&imagingFactory));
|
||||||
|
if (FAILED(result) || !imagingFactory)
|
||||||
|
{
|
||||||
|
if (shouldUninitializeCom)
|
||||||
|
CoUninitialize();
|
||||||
|
error = "Could not create a WIC imaging factory to load shader texture assets.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
CComPtr<IWICBitmapDecoder> bitmapDecoder;
|
||||||
|
result = imagingFactory->CreateDecoderFromFilename(textureAsset.path.wstring().c_str(), NULL, GENERIC_READ, WICDecodeMetadataCacheOnLoad, &bitmapDecoder);
|
||||||
|
if (FAILED(result) || !bitmapDecoder)
|
||||||
|
{
|
||||||
|
if (shouldUninitializeCom)
|
||||||
|
CoUninitialize();
|
||||||
|
error = "Could not open shader texture asset: " + textureAsset.path.string();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
CComPtr<IWICBitmapFrameDecode> bitmapFrame;
|
||||||
|
result = bitmapDecoder->GetFrame(0, &bitmapFrame);
|
||||||
|
if (FAILED(result) || !bitmapFrame)
|
||||||
|
{
|
||||||
|
if (shouldUninitializeCom)
|
||||||
|
CoUninitialize();
|
||||||
|
error = "Could not decode the first frame of shader texture asset: " + textureAsset.path.string();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
CComPtr<IWICFormatConverter> formatConverter;
|
||||||
|
result = imagingFactory->CreateFormatConverter(&formatConverter);
|
||||||
|
if (FAILED(result) || !formatConverter)
|
||||||
|
{
|
||||||
|
if (shouldUninitializeCom)
|
||||||
|
CoUninitialize();
|
||||||
|
error = "Could not create a WIC format converter for shader texture asset: " + textureAsset.path.string();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = formatConverter->Initialize(bitmapFrame, GUID_WICPixelFormat32bppBGRA, WICBitmapDitherTypeNone, NULL, 0.0, WICBitmapPaletteTypeCustom);
|
||||||
|
if (FAILED(result))
|
||||||
|
{
|
||||||
|
if (shouldUninitializeCom)
|
||||||
|
CoUninitialize();
|
||||||
|
error = "Could not convert shader texture asset to BGRA: " + textureAsset.path.string();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
UINT width = 0;
|
||||||
|
UINT height = 0;
|
||||||
|
result = formatConverter->GetSize(&width, &height);
|
||||||
|
if (FAILED(result) || width == 0 || height == 0)
|
||||||
|
{
|
||||||
|
if (shouldUninitializeCom)
|
||||||
|
CoUninitialize();
|
||||||
|
error = "Shader texture asset has an invalid size: " + textureAsset.path.string();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const UINT stride = width * 4;
|
||||||
|
std::vector<unsigned char> pixels(static_cast<std::size_t>(stride) * static_cast<std::size_t>(height));
|
||||||
|
result = formatConverter->CopyPixels(NULL, stride, static_cast<UINT>(pixels.size()), pixels.data());
|
||||||
|
if (FAILED(result))
|
||||||
|
{
|
||||||
|
if (shouldUninitializeCom)
|
||||||
|
CoUninitialize();
|
||||||
|
error = "Could not read shader texture pixels: " + textureAsset.path.string();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<unsigned char> flippedPixels(pixels.size());
|
||||||
|
for (UINT row = 0; row < height; ++row)
|
||||||
|
{
|
||||||
|
const std::size_t srcOffset = static_cast<std::size_t>(row) * stride;
|
||||||
|
const std::size_t dstOffset = static_cast<std::size_t>(height - 1 - row) * stride;
|
||||||
|
std::memcpy(flippedPixels.data() + dstOffset, pixels.data() + srcOffset, stride);
|
||||||
|
}
|
||||||
|
|
||||||
|
glGenTextures(1, &textureId);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, textureId);
|
||||||
|
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_RGBA8, static_cast<GLsizei>(width), static_cast<GLsizei>(height), 0, GL_BGRA, GL_UNSIGNED_BYTE, flippedPixels.data());
|
||||||
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
|
|
||||||
|
if (shouldUninitializeCom)
|
||||||
|
CoUninitialize();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ShaderTypes.h"
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
#include <gl/gl.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
bool LoadTextureAsset(const ShaderTextureAsset& textureAsset, GLuint& textureId, std::string& error);
|
||||||
73
tests/Std140BufferTests.cpp
Normal file
73
tests/Std140BufferTests.cpp
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
#include "Std140Buffer.h"
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
#include <iostream>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
int gFailures = 0;
|
||||||
|
|
||||||
|
void Expect(bool condition, const char* message)
|
||||||
|
{
|
||||||
|
if (condition)
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::cerr << "FAIL: " << message << "\n";
|
||||||
|
++gFailures;
|
||||||
|
}
|
||||||
|
|
||||||
|
float ReadFloat(const std::vector<unsigned char>& buffer, std::size_t offset)
|
||||||
|
{
|
||||||
|
float value = 0.0f;
|
||||||
|
std::memcpy(&value, buffer.data() + offset, sizeof(float));
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ReadInt(const std::vector<unsigned char>& buffer, std::size_t offset)
|
||||||
|
{
|
||||||
|
int value = 0;
|
||||||
|
std::memcpy(&value, buffer.data() + offset, sizeof(int));
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestScalarPacking()
|
||||||
|
{
|
||||||
|
std::vector<unsigned char> buffer;
|
||||||
|
AppendStd140Float(buffer, 1.25f);
|
||||||
|
AppendStd140Int(buffer, 7);
|
||||||
|
|
||||||
|
Expect(buffer.size() == 8, "scalar values pack tightly on 4-byte alignment");
|
||||||
|
Expect(ReadFloat(buffer, 0) == 1.25f, "float value is written at offset 0");
|
||||||
|
Expect(ReadInt(buffer, 4) == 7, "int value is written at offset 4");
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestVectorAlignment()
|
||||||
|
{
|
||||||
|
std::vector<unsigned char> buffer;
|
||||||
|
AppendStd140Float(buffer, 1.0f);
|
||||||
|
AppendStd140Vec2(buffer, 2.0f, 3.0f);
|
||||||
|
AppendStd140Vec4(buffer, 4.0f, 5.0f, 6.0f, 7.0f);
|
||||||
|
|
||||||
|
Expect(buffer.size() == 32, "vec2 aligns to 8 bytes and vec4 aligns to 16 bytes");
|
||||||
|
Expect(ReadFloat(buffer, 8) == 2.0f, "vec2 x value is aligned");
|
||||||
|
Expect(ReadFloat(buffer, 12) == 3.0f, "vec2 y value follows x");
|
||||||
|
Expect(ReadFloat(buffer, 16) == 4.0f, "vec4 x value is aligned");
|
||||||
|
Expect(ReadFloat(buffer, 28) == 7.0f, "vec4 w value is written");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
TestScalarPacking();
|
||||||
|
TestVectorAlignment();
|
||||||
|
|
||||||
|
if (gFailures != 0)
|
||||||
|
{
|
||||||
|
std::cerr << gFailures << " Std140Buffer test failure(s).\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "Std140Buffer tests passed.\n";
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user