diff --git a/.clang-format b/.clang-format index 769cb635b..1ecd79864 100644 --- a/.clang-format +++ b/.clang-format @@ -1,94 +1,153 @@ --- Language: Cpp -AccessModifierOffset: -1 +AccessModifierOffset: -4 AlignAfterOpenBracket: Align -AlignConsecutiveAssignments: false -AlignConsecutiveDeclarations: false -AlignEscapedNewlinesLeft: true -AlignOperands: true -AlignTrailingComments: true +AlignArrayOfStructures: None +AlignConsecutiveAssignments: None +AlignConsecutiveBitFields: None +AlignConsecutiveDeclarations: None +AlignConsecutiveMacros: None +AlignConsecutiveShortCaseStatements: + Enabled: false +AlignEscapedNewlines: Left +AlignOperands: Align +AlignTrailingComments: Always +AllowAllArgumentsOnNextLine: true AllowAllParametersOfDeclarationOnNextLine: true -AllowShortBlocksOnASingleLine: false +AllowBreakBeforeNoexceptSpecifier: Never +AllowShortBlocksOnASingleLine: Never AllowShortCaseLabelsOnASingleLine: false -AllowShortFunctionsOnASingleLine: Empty -AllowShortIfStatementsOnASingleLine: true -AllowShortLoopsOnASingleLine: true -AlwaysBreakAfterDefinitionReturnType: None -AlwaysBreakAfterReturnType: None -AlwaysBreakBeforeMultilineStrings: true -AlwaysBreakTemplateDeclarations: true +AllowShortCompoundRequirementOnASingleLine: true +AllowShortEnumsOnASingleLine: true +AllowShortFunctionsOnASingleLine: All +AllowShortIfStatementsOnASingleLine: Never +AllowShortLambdasOnASingleLine: All +AllowShortLoopsOnASingleLine: false +AlwaysBreakBeforeMultilineStrings: false BinPackArguments: true BinPackParameters: true -BraceWrapping: - AfterClass: true - AfterControlStatement: true - AfterEnum: true - AfterFunction: true - AfterNamespace: true - AfterObjCDeclaration: true - AfterStruct: true - AfterUnion: false - BeforeCatch: true - BeforeElse: true - IndentBraces: false -BreakBeforeBinaryOperators: None +BitFieldColonSpacing: Both +BreakAdjacentStringLiterals: true +BreakAfterAttributes: Leave +BreakAfterJavaFieldAnnotations: false +BreakAfterReturnType: Automatic +BreakArrays: true +BreakBeforeBinaryOperators: NonAssignment BreakBeforeBraces: Allman +BreakBeforeConceptDeclarations: Always +BreakBeforeInlineASMColon: OnlyMultiline BreakBeforeTernaryOperators: true -BreakConstructorInitializersBeforeComma: false -BreakAfterJavaFieldAnnotations: false +BreakConstructorInitializers: BeforeComma +BreakInheritanceList: BeforeComma BreakStringLiterals: true +BreakTemplateDeclarations: MultiLine ColumnLimit: 80 CommentPragmas: '^ IWYU pragma:' -ConstructorInitializerAllOnOneLineOrOnePerLine: true +CompactNamespaces: false ConstructorInitializerIndentWidth: 4 ContinuationIndentWidth: 4 Cpp11BracedListStyle: true +DerivePointerAlignment: false DisableFormat: false -ExperimentalAutoDetectBinPacking: false -ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ] -IncludeCategories: - - Regex: '^<.*\.h>' - Priority: 1 - - Regex: '^<.*' - Priority: 2 - - Regex: '.*' - Priority: 3 -IncludeIsMainRegex: '([-_](test|unittest))?$' -IndentCaseLabels: true -IndentWidth: 2 +EmptyLineAfterAccessModifier: Never +EmptyLineBeforeAccessModifier: LogicalBlock +FixNamespaceComments: true +ForEachMacros: [foreach, Q_FOREACH, BOOST_FOREACH] +IndentAccessModifiers: false +IndentCaseBlocks: false +IndentCaseLabels: false +IndentExternBlock: Indent +IndentGotoLabels: true +IndentPPDirectives: None +IndentRequiresClause: true +IndentWidth: 4 IndentWrappedFunctionNames: false +InsertBraces: true +InsertNewlineAtEOF: true +InsertTrailingCommas: None +IntegerLiteralSeparator: + Binary: 0 + BinaryMinDigits: 0 + Decimal: 0 + DecimalMinDigits: 0 + Hex: 0 + HexMinDigits: 0 JavaScriptQuotes: Leave JavaScriptWrapImports: true -KeepEmptyLinesAtTheStartOfBlocks: false +KeepEmptyLinesAtTheStartOfBlocks: true +KeepEmptyLinesAtEOF: false +LambdaBodyIndentation: Signature +LineEnding: DeriveLF MacroBlockBegin: '' MacroBlockEnd: '' MaxEmptyLinesToKeep: 1 NamespaceIndentation: All -ObjCBlockIndentWidth: 2 +ObjCBinPackProtocolList: Auto +ObjCBlockIndentWidth: 4 +ObjCBreakBeforeNestedBlockParam: true ObjCSpaceAfterProperty: false -ObjCSpaceBeforeProtocolList: false -PenaltyBreakBeforeFirstCallParameter: 1 +ObjCSpaceBeforeProtocolList: true +PackConstructorInitializers: Never +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 19 PenaltyBreakComment: 300 PenaltyBreakFirstLessLess: 120 +PenaltyBreakOpenParenthesis: 0 +PenaltyBreakScopeResolution: 500 PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 PenaltyExcessCharacter: 1000000 -PenaltyReturnTypeOnItsOwnLine: 200 +PenaltyIndentedWhitespace: 0 +PenaltyReturnTypeOnItsOwnLine: 60 PointerAlignment: Left -ReflowComments: true -SortIncludes: false +PPIndentWidth: -1 +QualifierAlignment: Left +ReferenceAlignment: Pointer +RemoveBracesLLVM: false +RemoveParentheses: Leave +RemoveSemicolon: false +RequiresClausePosition: OwnLine +RequiresExpressionIndentation: OuterScope +SeparateDefinitionBlocks: Always +ShortNamespaceLines: 1 +SkipMacroDefinitionBody: false +SortIncludes: Never +SortJavaStaticImport: Before +SortUsingDeclarations: LexicographicNumeric SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: true +SpaceAroundPointerQualifiers: Default SpaceBeforeAssignmentOperators: true -SpaceBeforeParens: Never -SpaceInEmptyParentheses: false -SpacesBeforeTrailingComments: 2 -SpacesInAngles: false +SpaceBeforeCaseColon: false +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeJsonColon: false +SpaceBeforeParens: Custom +SpaceBeforeParensOptions: + AfterControlStatements: true + AfterForeachMacros: true + AfterFunctionDefinitionName: false + AfterFunctionDeclarationName: false + AfterIfMacros: true + AfterOverloadedOperator: false + AfterPlacementOperator: true + AfterRequiresInClause: false + AfterRequiresInExpression: false + BeforeNonEmptyParentheses: false +SpaceBeforeRangeBasedForLoopColon: true +SpaceBeforeSquareBrackets: false +SpaceInEmptyBlock: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: Never SpacesInContainerLiterals: true -SpacesInCStyleCastParentheses: false -SpacesInParentheses: true +SpacesInLineCommentPrefix: + Minimum: 1 + Maximum: -1 +SpacesInParens: Never SpacesInSquareBrackets: false -Standard: Auto -TabWidth: 2 +Standard: Latest +TabWidth: 8 UseTab: Never ---- -Language: JavaScript -DisableFormat: true \ No newline at end of file +... diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 000000000..2bf615398 --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,20 @@ +--- +Checks: > + bugprone-*, + cert-*, + clang-diagnostic-*, + clang-analyzer-*, + concurrency-*, + cppcoreguidelines-*, + -cppcoreguidelines-pro-type-reinterpret-cast, + google-explicit-constructor, + misc-*, + modernize-*, + -modernize-use-trailing-return-type, + performance-*, + readability-*, + +WarningsAsErrors: '' +HeaderFilterRegex: '' +FormatStyle: 'file' +... diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6d31bb7ba..f74d40778 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -173,7 +173,7 @@ jobs: dnf install -y epel-release dnf config-manager --set-enabled ${{ matrix.extra_repo }} devel dnf groupinstall "Development Tools" -y - dnf install -y alsa-lib-devel autoconf automake avahi-compat-libdns_sd-devel bison bzip2-devel cmake-gui curl-devel flex gcc gcc-c++ git libXcomposite libXi-devel libaio-devel libffi-devel nasm ncurses-devel nss libtool libxkbcommon libXcomposite libXdamage libXrandr libXtst libXcursor mesa-libOSMesa mesa-libOSMesa-devel meson openssl-devel patch pulseaudio-libs pulseaudio-libs-glib2 ocl-icd ocl-icd-devel opencl-headers qt5-qtbase-devel readline-devel sqlite-devel tcl-devel tcsh tk-devel yasm zip zlib-devel wget patchelf pcsc-lite libxkbfile perl-IPC-Cmd + dnf install -y alsa-lib-devel autoconf automake avahi-compat-libdns_sd-devel bison bzip2-devel cmake-gui curl-devel flex gcc gcc-c++ git libXcomposite libXi-devel libaio-devel libffi-devel nasm ncurses-devel nss libtool libxkbcommon libXcomposite libXdamage libXrandr libXtst libXcursor mesa-libOSMesa mesa-libOSMesa-devel meson openssl-devel patch pulseaudio-libs pulseaudio-libs-glib2 ocl-icd ocl-icd-devel opencl-headers qt5-qtbase-devel readline-devel sqlite-devel systemd-devel tcl-devel tcsh tk-devel yasm zip zlib-devel wget patchelf pcsc-lite libxkbfile perl-IPC-Cmd dnf install -y libX11-devel libXext-devel libXrender-devel libXrandr-devel libXcursor-devel libXi-devel libXxf86vm-devel libxkbcommon-devel dnf install -y xz-devel mesa-libGLU mesa-libGLU-devel dnf clean all @@ -793,4 +793,4 @@ jobs: - name: Install OpenRV run: | cmake --install _build --prefix $(pwd)/_install --config ${{ matrix.build-type }} - shell: msys2 {0} \ No newline at end of file + shell: msys2 {0} diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 30b6ae860..7af06bf06 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -8,3 +8,8 @@ repos: rev: 24.8.0 hooks: - id: black + + - repo: https://github.com/pre-commit/mirrors-clang-format + rev: 'v19.1.4' + hooks: + - id: clang-format diff --git a/cmake/dependencies/aja.cmake b/cmake/dependencies/aja.cmake index 7ddb2a976..6c4387f12 100644 --- a/cmake/dependencies/aja.cmake +++ b/cmake/dependencies/aja.cmake @@ -7,84 +7,78 @@ INCLUDE(ProcessorCount) # require CMake 3.15+ PROCESSORCOUNT(_cpu_count) -RV_CREATE_STANDARD_DEPS_VARIABLES("RV_DEPS_AJA" "16.2" "make" "") +RV_CREATE_STANDARD_DEPS_VARIABLES("RV_DEPS_AJA" "17.1.0" "make" "") RV_SHOW_STANDARD_DEPS_VARIABLES() -SET(_patch - "bugfix5" -) +STRING(REPLACE "." "_" _version_with_underscore ${_version}) SET(_download_url - "https://github.com/aja-video/ntv2/archive/refs/tags/v${_version}-${_patch}.zip" + "https://github.com/aja-video/libajantv2/archive/refs/tags/ntv2_${_version_with_underscore}.zip" ) SET(_download_hash - "5ec7f3f7ecfc322ca9307203155a4481" + "b9d189f77e18dbdff7c39a339b1a5dd4" ) -SET(_install_dir - ${RV_DEPS_BASE_DIR}/${_target}/install +IF(RV_TARGET_WINDOWS) + RV_MAKE_STANDARD_LIB_NAME(ajantv2_vs143_MT "" "SHARED" "d") +ELSE() + RV_MAKE_STANDARD_LIB_NAME(ajantv2 "" "SHARED" "d") +ENDIF() + +SET(_aja_ntv2_include_dir + ${_include_dir}/libajantv2/ajantv2/includes ) -SET(_include_dir - ${_install_dir}/include +SET(_aja_include_dir + ${_include_dir}/libajantv2 ) -IF(RV_TARGET_LINUX) - SET(_lib_dir - ${_install_dir}/lib64 +IF(RHEL_VERBOSE) + SET(_mbedtls_lib_dir + ${_build_dir}/ajantv2/mbedtls-install/lib64 ) ELSE() - SET(_lib_dir - ${_install_dir}/lib + SET(_mbedtls_lib_dir + ${_build_dir}/ajantv2/mbedtls-install/lib ) ENDIF() -SET(_make_command - make +SET(_mbedtls_lib + ${_mbedtls_lib_dir}/${CMAKE_STATIC_LIBRARY_PREFIX}mbedtls${CMAKE_STATIC_LIBRARY_SUFFIX} ) -SET(_configure_command - cmake +SET(_mbedx509_lib + ${_mbedtls_lib_dir}/${CMAKE_STATIC_LIBRARY_PREFIX}mbedx509${CMAKE_STATIC_LIBRARY_SUFFIX} +) +SET(_mbedcrypto_lib + ${_mbedtls_lib_dir}/${CMAKE_STATIC_LIBRARY_PREFIX}mbedcrypto${CMAKE_STATIC_LIBRARY_SUFFIX} ) -IF(${RV_OSX_EMULATION}) - SET(_darwin_x86_64 - "arch" "${RV_OSX_EMULATION_ARCH}" - ) +LIST(APPEND _byproducts ${_mbedtls_lib} ${_mbedx509_lib} ${_mbedcrypto_lib}) - SET(_make_command - ${_darwin_x86_64} ${_make_command} - ) - SET(_configure_command - ${_darwin_x86_64} ${_configure_command} +# There is an issue with the recent AJA SDK : the OS specific header files are no longer copied to _aja_ntv2_include_dir Adding custom paths here to work around +# this issue +IF(RV_TARGET_LINUX) + SET(_aja_ntv2_os_specific_include_dir + ${_include_dir}/libajantv2/ajantv2/src/lin ) -ENDIF() - -IF(RV_TARGET_WINDOWS) - # MSYS2/CMake defaults to Ninja - SET(_make_command - ninja +ELSEIF(RV_TARGET_DARWIN) + SET(_aja_ntv2_os_specific_include_dir + ${_include_dir}/libajantv2/ajantv2/src/mac ) -ENDIF() - -IF(${CMAKE_BUILD_TYPE} STREQUAL "Debug") - SET(AJA_DEBUG_POSTFIX - "d" +ELSEIF(RV_TARGET_WINDOWS) + SET(_aja_ntv2_os_specific_include_dir + ${_include_dir}/libajantv2/ajantv2/src/win ) ENDIF() -SET(_aja_ntv2_libname - ${CMAKE_STATIC_LIBRARY_PREFIX}ajantv2${AJA_DEBUG_POSTFIX}${CMAKE_STATIC_LIBRARY_SUFFIX} -) +LIST(APPEND _configure_options "-DAJANTV2_DISABLE_DEMOS=ON" "-DAJANTV2_DISABLE_TOOLS=ON" "-DAJANTV2_DISABLE_TESTS=ON" "-DAJANTV2_BUILD_SHARED=ON") -SET(_aja_ntv2_lib - ${_lib_dir}/${_aja_ntv2_libname} -) -SET(_aja_ntv2_include_dir - ${_include_dir}/ajalibraries/ajantv2/includes -) -SET(_aja_include_dir - ${_include_dir}/ajalibraries +# In Debug, the MSVC runtime library needs to be set to MultiThreadedDebug. Otherwise, it will be set to MultiThreaded. +IF(RV_TARGET_WINDOWS + AND CMAKE_BUILD_TYPE MATCHES "^Debug$" ) + LIST(APPEND _configure_options "-DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreadedDebug") +ENDIF() EXTERNALPROJECT_ADD( ${_target} @@ -93,34 +87,46 @@ EXTERNALPROJECT_ADD( DOWNLOAD_NAME ${_target}_${_version}.zip DOWNLOAD_DIR ${RV_DEPS_DOWNLOAD_DIR} DOWNLOAD_EXTRACT_TIMESTAMP TRUE - SOURCE_DIR ${RV_DEPS_BASE_DIR}/${_target}/src + SOURCE_DIR ${_source_dir} + BINARY_DIR ${_build_dir} INSTALL_DIR ${_install_dir} - CONFIGURE_COMMAND ${CMAKE_COMMAND} -G Ninja -DCMAKE_INSTALL_PREFIX=${_install_dir} -DCMAKE_OSX_ARCHITECTURES=${CMAKE_OSX_ARCHITECTURES} - -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DAJA_BUILD_APPS=OFF ${RV_DEPS_BASE_DIR}/${_target}/src - # Not using _cmake_build_command and _cmake_install_command since the build dir need to change. - BUILD_COMMAND ${CMAKE_COMMAND} --build ${RV_DEPS_BASE_DIR}/${_target}/src --config ${CMAKE_BUILD_TYPE} -j${_cpu_count} - INSTALL_COMMAND ${CMAKE_COMMAND} --install ${RV_DEPS_BASE_DIR}/${_target}/src --prefix ${_install_dir} --config ${CMAKE_BUILD_TYPE} - BUILD_IN_SOURCE TRUE + CONFIGURE_COMMAND ${CMAKE_COMMAND} ${_configure_options} + BUILD_COMMAND ${_cmake_build_command} + INSTALL_COMMAND ${_cmake_install_command} && ${CMAKE_COMMAND} -E copy_directory ${_mbedtls_lib_dir} ${_lib_dir} + BUILD_IN_SOURCE FALSE BUILD_ALWAYS FALSE - BUILD_BYPRODUCTS ${_aja_ntv2_lib} + BUILD_BYPRODUCTS ${_byproducts} USES_TERMINAL_BUILD TRUE ) -ADD_LIBRARY(aja::ntv2 STATIC IMPORTED GLOBAL) +RV_COPY_LIB_BIN_FOLDERS() + +ADD_LIBRARY(aja::ntv2 SHARED IMPORTED GLOBAL) ADD_DEPENDENCIES(aja::ntv2 ${_target}) SET_PROPERTY( TARGET aja::ntv2 - PROPERTY IMPORTED_LOCATION ${_aja_ntv2_lib} + PROPERTY IMPORTED_LOCATION ${_libpath} ) SET_PROPERTY( TARGET aja::ntv2 - PROPERTY IMPORTED_SONAME ${_aja_ntv2_libname} + PROPERTY IMPORTED_SONAME ${_libname} ) +IF(RV_TARGET_WINDOWS) + SET_PROPERTY( + TARGET aja::ntv2 + PROPERTY IMPORTED_IMPLIB ${_implibpath} + ) +ENDIF() -FILE(MAKE_DIRECTORY ${_aja_include_dir} ${_aja_ntv2_include_dir}) +FILE(MAKE_DIRECTORY ${_aja_include_dir} ${_aja_ntv2_include_dir} ${_aja_ntv2_os_specific_include_dir}) TARGET_INCLUDE_DIRECTORIES( aja::ntv2 - INTERFACE ${_aja_include_dir} ${_aja_ntv2_include_dir} + INTERFACE ${_aja_include_dir} ${_aja_ntv2_include_dir} ${_aja_ntv2_os_specific_include_dir} +) + +TARGET_LINK_LIBRARIES( + aja::ntv2 + INTERFACE ${_mbedtls_lib} ${_mbedx509_lib} ${_mbedcrypto_lib} ) IF(RV_TARGET_DARWIN) @@ -140,37 +146,9 @@ SET(RV_DEPS_AJA_COMPILE_OPTIONS LIST(APPEND RV_DEPS_LIST aja::ntv2) -IF(RV_TARGET_WINDOWS) - FILE(MAKE_DIRECTORY ${_install_dir}/lib) - FILE(MAKE_DIRECTORY ${_install_dir}/bin) - - ADD_CUSTOM_COMMAND( - TARGET ${_target} - POST_BUILD - COMMENT "Installing ${_target}'s libs and bin into ${RV_STAGE_LIB_DIR} and ${RV_STAGE_BIN_DIR}" - COMMAND ${CMAKE_COMMAND} -E copy_directory ${_install_dir}/lib ${RV_STAGE_LIB_DIR} - COMMAND ${CMAKE_COMMAND} -E copy_directory ${_install_dir}/bin ${RV_STAGE_BIN_DIR} - ) - ADD_CUSTOM_TARGET( - ${_target}-stage-target ALL - DEPENDS ${RV_STAGE_LIB_DIR}/${_aja_ntv2_libname} - ) -ELSE() - ADD_CUSTOM_COMMAND( - COMMENT "Installing ${_target}'s libs into ${RV_STAGE_LIB_DIR}" - OUTPUT ${RV_STAGE_LIB_DIR}/${_aja_ntv2_libname} - COMMAND ${CMAKE_COMMAND} -E copy_directory ${_lib_dir} ${RV_STAGE_LIB_DIR} - DEPENDS ${_target} - ) - ADD_CUSTOM_TARGET( - ${_target}-stage-target ALL - DEPENDS ${RV_STAGE_LIB_DIR}/${_aja_ntv2_libname} - ) -ENDIF() - ADD_DEPENDENCIES(dependencies ${_target}-stage-target) SET(RV_DEPS_AJA_VERSION ${_version} CACHE INTERNAL "" FORCE -) \ No newline at end of file +) diff --git a/cmake/dependencies/gc.cmake b/cmake/dependencies/gc.cmake index b8df4f410..265da4798 100644 --- a/cmake/dependencies/gc.cmake +++ b/cmake/dependencies/gc.cmake @@ -85,6 +85,7 @@ FILE(MAKE_DIRECTORY ${_include_dir}) LIST(APPEND _configure_options "-Denable_parallel_mark=ON") LIST(APPEND _configure_options "-Denable_cplusplus=ON") +LIST(APPEND _configure_options "-Denable_large_config=yes") IF(RV_TARGET_WINDOWS) LIST(APPEND _configure_options "-DCMAKE_USE_WIN32_THREADS_INIT=1") ELSE() diff --git a/docs/build_system/config_linux_centos7.md b/docs/build_system/config_linux_centos7.md index 2958cb1cd..964c2b83a 100644 --- a/docs/build_system/config_linux_centos7.md +++ b/docs/build_system/config_linux_centos7.md @@ -2,12 +2,14 @@ ## Summary -1. [Install Basics](#install-basics) -1. [Install devtoolset-9](#install-devtoolset-9) -1. [Install tools and build dependencies](#install-tools-and-build-dependencies) -1. [Install CMake](#install-cmake) -1. [Install nasm](#install-nasm) -1. [Install Qt5](#install-qt) +- [Summary](#summary) +- [Install Basics](#install-basics) +- [Install devtoolset-9](#install-devtoolset-9) +- [Install tools and build dependencies](#install-tools-and-build-dependencies) + - [Install the python requirements](#install-the-python-requirements) +- [Install CMake](#install-cmake) +- [Install nasm](#install-nasm) +- [Install Qt](#install-qt) ## Install Basics @@ -35,7 +37,7 @@ scl enable devtoolset-9 $SHELL Most of the build requirements can be installed using the following command: ```bash -sudo yum install alsa-lib-devel autoconf automake avahi-compat-libdns_sd-devel bison bzip2-devel cmake-gui curl-devel flex glew-devel libXcomposite libXi-devel libaio-devel libffi-devel ncurses-devel libtool libxkbcommon openssl-devel patch pulseaudio-libs pulseaudio-libs-glib2 mesa-libOSMesa mesa-libOSMesa-devel ocl-icd opencl-headers python3 python3-devel qt5-qtbase-devel readline-devel sqlite-devel tcl-devel tk-devel yasm zlib-devel +sudo yum install alsa-lib-devel autoconf automake avahi-compat-libdns_sd-devel bison bzip2-devel cmake-gui curl-devel flex glew-devel libXcomposite libXi-devel libaio-devel libffi-devel ncurses-devel libtool libudev-devel libxkbcommon openssl-devel patch pulseaudio-libs pulseaudio-libs-glib2 mesa-libOSMesa mesa-libOSMesa-devel ocl-icd opencl-headers python3 python3-devel qt5-qtbase-devel readline-devel sqlite-devel tcl-devel tk-devel yasm zlib-devel ``` ### Install the python requirements @@ -48,7 +50,7 @@ python3 -m pip install -r requirements.txt ## Install CMake -You need CMake version 3.24+ to build RV. The yum-installable version is not quite recent enough, you'll to build and install CMake from sources. +You need CMake version 3.24+ to build RV. The yum-installable version is not quite recent enough, you'll need to build and install CMake from source. ```bash wget https://github.com/Kitware/CMake/releases/download/v3.24.0/cmake-3.24.0.tar.gz diff --git a/docs/build_system/config_linux_rocky8.md b/docs/build_system/config_linux_rocky8.md index f01e41415..7535dc155 100644 --- a/docs/build_system/config_linux_rocky8.md +++ b/docs/build_system/config_linux_rocky8.md @@ -25,7 +25,7 @@ Some of the build dependencies come from outside the main AppStream repo. So fir ```bash sudo dnf install epel-release sudo dnf config-manager --set-enabled powertools -sudo dnf install alsa-lib-devel autoconf automake avahi-compat-libdns_sd-devel bison bzip2-devel cmake-gui curl-devel flex gcc gcc-c++ libXcomposite libXi-devel libaio-devel libffi-devel nasm ncurses-devel nss libtool libxkbcommon libXcomposite libXdamage libXrandr libXtst libXcursor mesa-libOSMesa mesa-libOSMesa-devel meson ninja-build openssl-devel patch pulseaudio-libs pulseaudio-libs-glib2 ocl-icd ocl-icd-devel opencl-headers python3 python3-devel qt5-qtbase-devel readline-devel sqlite-devel tcl-devel tcsh tk-devel yasm zip zlib-devel +sudo dnf install alsa-lib-devel autoconf automake avahi-compat-libdns_sd-devel bison bzip2-devel cmake-gui curl-devel flex gcc gcc-c++ libXcomposite libXi-devel libaio-devel libffi-devel nasm ncurses-devel nss libtool libxkbcommon libXcomposite libXdamage libXrandr libXtst libXcursor mesa-libOSMesa mesa-libOSMesa-devel meson ninja-build openssl-devel patch pulseaudio-libs pulseaudio-libs-glib2 ocl-icd ocl-icd-devel opencl-headers python3 python3-devel qt5-qtbase-devel readline-devel sqlite-devel systemd-devel tcl-devel tcsh tk-devel yasm zip zlib-devel ``` You can disable the devel repo afterwards since dnf will warn about it: @@ -51,7 +51,7 @@ python3 -m pip install -r requirements.txt ## Install CMake -You need CMake version 3.27+ to build RV. The dnf-installable version is not quite recent enough, you'll to build and install CMake from sources. +You need CMake version 3.27+ to build RV. The dnf-installable version is not quite recent enough, you'll need to build and install CMake from source. ```bash wget https://github.com/Kitware/CMake/releases/download/v3.30.3/cmake-3.30.3.tar.gz @@ -221,4 +221,4 @@ Container id is the same as the one used in the step [Go into the container](go_ ```bash docker cp :/home/rv/OpenRV/_build/stage ./openrv_stage -``` \ No newline at end of file +``` diff --git a/docs/build_system/config_linux_rocky9.md b/docs/build_system/config_linux_rocky9.md index 5f300d520..ef4d98e27 100644 --- a/docs/build_system/config_linux_rocky9.md +++ b/docs/build_system/config_linux_rocky9.md @@ -25,7 +25,7 @@ Some of the build dependencies come from outside the main AppStream repo. So fir ```bash sudo dnf install epel-release sudo dnf config-manager --set-enabled crb devel -sudo dnf install alsa-lib-devel autoconf automake avahi-compat-libdns_sd-devel bison bzip2-devel cmake-gui curl-devel flex gcc gcc-c++ libXcomposite libXi-devel libaio-devel libffi-devel nasm ncurses-devel nss libtool libxkbcommon libXcomposite libXdamage libXrandr libXtst libXcursor mesa-libOSMesa mesa-libOSMesa-devel meson ninja-build openssl-devel patch perl-FindBin pulseaudio-libs pulseaudio-libs-glib2 ocl-icd ocl-icd-devel opencl-headers python3 python3-devel qt5-qtbase-devel readline-devel sqlite-devel tcl-devel tcsh tk-devel yasm zip zlib-devel +sudo dnf install alsa-lib-devel autoconf automake avahi-compat-libdns_sd-devel bison bzip2-devel cmake-gui curl-devel flex gcc gcc-c++ libXcomposite libXi-devel libaio-devel libffi-devel nasm ncurses-devel nss libtool libxkbcommon libXcomposite libXdamage libXrandr libXtst libXcursor mesa-libOSMesa mesa-libOSMesa-devel meson ninja-build openssl-devel patch perl-FindBin pulseaudio-libs pulseaudio-libs-glib2 ocl-icd ocl-icd-devel opencl-headers python3 python3-devel qt5-qtbase-devel readline-devel sqlite-devel systemd-devel tcl-devel tcsh tk-devel yasm zip zlib-devel ``` You can disable the devel repo afterwards since dnf will warn about it: @@ -57,6 +57,22 @@ Some of the RV build scripts requires extra python packages. They can be install python3 -m pip install -r requirements.txt ``` +## Install CMake + +You need CMake version 3.27+ to build RV. The dnf-installable version is not quite recent enough, you'll need to build and install CMake from source. + +```bash +wget https://github.com/Kitware/CMake/releases/download/v3.30.3/cmake-3.30.3.tar.gz +tar -zxvf cmake-3.30.3.tar.gz +cd cmake-3.30.3 +./bootstrap --parallel=32 # 32 or whatever your machine allows +make -j 32 # 32 or whatever your machine allows +sudo make install + +cmake --version # confirm the version of your newly installed version of CMake +cmake version3.30.3 +``` + ## Install Qt Download the last version of Qt 5.15.x that you can get using the online installer on the [Qt page](https://www.qt.io/download-open-source). During Qt Setup's Select Components phase, check the "Archive" box on the right side of the window then click on "Filter" to see Qt 5.15.x options. Logs, Android, iOS and WebAssembly are not required to build OpenRV. Make sure to note the destination of the Qt install, as you will have to set the `QT_HOME` environment variable to this location's build dir. @@ -213,4 +229,4 @@ Container id is the same as the one used in the step [Go into the container](go_ ```bash docker cp :/home/rv/OpenRV/_build/stage ./openrv_stage -``` \ No newline at end of file +``` diff --git a/rvcmds.sh b/rvcmds.sh index bd7abd500..775751cfb 100755 --- a/rvcmds.sh +++ b/rvcmds.sh @@ -24,10 +24,12 @@ fi # Linux if [[ "$OSTYPE" == "linux"* ]]; then CMAKE_GENERATOR="${CMAKE_GENERATOR:-Ninja}" + RV_TOOLCHAIN="" # MacOS elif [[ "$OSTYPE" == "darwin"* ]]; then CMAKE_GENERATOR="${CMAKE_GENERATOR:-Ninja}" + RV_TOOLCHAIN="" # Windows elif [[ "$OSTYPE" == "msys"* ]]; then @@ -35,6 +37,7 @@ elif [[ "$OSTYPE" == "msys"* ]]; then WIN_PERL="${WIN_PERL:-c:/Strawberry/perl/bin}" CMAKE_WIN_ARCH="${CMAKE_WIN_ARCH:--A x64}" SETUPTOOLS_USE_DISTUTILS=stdlib + RV_TOOLCHAIN="-T v143,version=14.40" else echo "OS does not seem to be linux, darwin or msys. Exiting." @@ -97,11 +100,10 @@ RV_INST_DEBUG="${RV_INST_DEBUG:-${RV_HOME}/_install_debug}" RV_BUILD_PARALLELISM="${RV_BUILD_PARALLELISM:-$(python3 -c 'import os; print(os.cpu_count())')}" # ALIASES: Basic commands - alias rvenv="rvenv_shell" alias rvsetup="rvenv && SETUPTOOLS_USE_DISTUTILS=${SETUPTOOLS_USE_DISTUTILS} python3 -m pip install --upgrade -r ${RV_HOME}/requirements.txt" -alias rvcfg="rvenv && cmake -B ${RV_BUILD} -G \"${CMAKE_GENERATOR}\" ${CMAKE_WIN_ARCH} -DCMAKE_BUILD_TYPE=Release -DRV_DEPS_QT5_LOCATION=${QT_HOME} -DRV_DEPS_WIN_PERL_ROOT=${WIN_PERL}" -alias rvcfgd="rvenv && cmake -B ${RV_BUILD_DEBUG} -G \"${CMAKE_GENERATOR}\" ${CMAKE_WIN_ARCH} -DCMAKE_BUILD_TYPE=Debug -DRV_DEPS_QT5_LOCATION=${QT_HOME} -DRV_DEPS_WIN_PERL_ROOT=${WIN_PERL}" +alias rvcfg="rvenv && cmake -B ${RV_BUILD} -G \"${CMAKE_GENERATOR}\" ${RV_TOOLCHAIN} ${CMAKE_WIN_ARCH} -DCMAKE_BUILD_TYPE=Release -DRV_DEPS_QT5_LOCATION=${QT_HOME} -DRV_DEPS_WIN_PERL_ROOT=${WIN_PERL}" +alias rvcfgd="rvenv && cmake -B ${RV_BUILD_DEBUG} -G \"${CMAKE_GENERATOR}\" ${RV_TOOLCHAIN} ${CMAKE_WIN_ARCH} -DCMAKE_BUILD_TYPE=Debug -DRV_DEPS_QT5_LOCATION=${QT_HOME} -DRV_DEPS_WIN_PERL_ROOT=${WIN_PERL}" alias rvbuildt="rvenv && cmake --build ${RV_BUILD} --config Release -v --parallel=${RV_BUILD_PARALLELISM} --target " alias rvbuildtd="rvenv && cmake --build ${RV_BUILD_DEBUG} --config Debug -v --parallel=${RV_BUILD_PARALLELISM} --target " alias rvbuild="rvenv && rvbuildt main_executable" diff --git a/src/bin/imgtools/rvls/CMakeLists.txt b/src/bin/imgtools/rvls/CMakeLists.txt index 8fa7169f4..18798da19 100644 --- a/src/bin/imgtools/rvls/CMakeLists.txt +++ b/src/bin/imgtools/rvls/CMakeLists.txt @@ -30,6 +30,7 @@ FIND_PACKAGE( TARGET_LINK_LIBRARIES( ${_target} + PUBLIC MuTwkApp PRIVATE IOproxy OpenEXR::OpenEXR MovieFB @@ -45,6 +46,7 @@ TARGET_LINK_LIBRARIES( Boost::headers yaml_cpp Qt::Core + PyTwkApp ) IF(RV_TARGET_DARWIN) diff --git a/src/bin/imgtools/rvls/main.cpp b/src/bin/imgtools/rvls/main.cpp index 4ad4255f6..6e1ed47fd 100644 --- a/src/bin/imgtools/rvls/main.cpp +++ b/src/bin/imgtools/rvls/main.cpp @@ -19,6 +19,8 @@ #include #include #include +#include +#include #include #include #include @@ -676,6 +678,17 @@ int utf8Main(int argc, char** argv) TwkMovie::GenericIO::addPlugin(new MovieFBIO()); TwkMovie::GenericIO::addPlugin(new MovieProceduralIO()); + try + { + TwkApp::initMu(nullptr); + TwkApp::initPython(); + } + catch (const std::exception &e) + { + cerr << "ERROR: during initialization: " << e.what() << '\n'; + exit(-1); + } + if (showFormats) { TwkMovie::GenericIO::outputFormats(); diff --git a/src/lib/app/PyTwkApp/PyInterface.cpp b/src/lib/app/PyTwkApp/PyInterface.cpp index 166e579a4..9b9822b58 100644 --- a/src/lib/app/PyTwkApp/PyInterface.cpp +++ b/src/lib/app/PyTwkApp/PyInterface.cpp @@ -257,6 +257,15 @@ namespace TwkApp void initPython( int argc, char** argv ) { + // PreInitialize Python + // Note: This is necessary for Python to utilize environment variables like PYTHONUTF8 + PyPreConfig preconfig; + PyPreConfig_InitPythonConfig(&preconfig); + PyStatus status=Py_PreInitialize(&preconfig); + if (PyStatus_Exception(status)) { + Py_ExitStatusException(status); + } + Py_InitializeEx( 1 ); static wchar_t delim = L'\0'; diff --git a/src/lib/image/MovieFFMpeg/MovieFFMpeg.cpp b/src/lib/image/MovieFFMpeg/MovieFFMpeg.cpp index 45267f488..e6a6f9290 100644 --- a/src/lib/image/MovieFFMpeg/MovieFFMpeg.cpp +++ b/src/lib/image/MovieFFMpeg/MovieFFMpeg.cpp @@ -712,6 +712,15 @@ isMP4format(AVFormatContext* avFormatContext) && strstr(avFormatContext->iformat->name, "mp4") != nullptr; } +bool +isMOVformat(AVFormatContext* avFormatContext) +{ + return avFormatContext!=nullptr + && avFormatContext->iformat!=nullptr + && avFormatContext->iformat->name!=nullptr + && strstr(avFormatContext->iformat->name, "mov") != nullptr; +} + int64_t findBestTS(int64_t goalTS, double frameDur, VideoTrack* track, bool finalPacket) { @@ -1340,7 +1349,12 @@ MovieFFMpegReader::getFirstFrame(AVRational rate) AVStream *tsStream = m_avFormatContext->streams[i]; AVRational tcRate = {tsStream->time_base.den, - tsStream->time_base.num}; + tsStream->time_base.num}; + + if (isMOVformat(m_avFormatContext)) + { + tcRate = tsStream->avg_frame_rate; + } AVDictionaryEntry *tcrEntry; tcrEntry = av_dict_get(tsStream->metadata, "reel_name", NULL, 0); @@ -3621,6 +3635,11 @@ MovieFFMpegReader::decodeImageAtFrame(int inframe, VideoTrack* track) AVRational rate = {tsStream->time_base.den, tsStream->time_base.num}; + if (isMOVformat(m_avFormatContext)) + { + rate = tsStream->avg_frame_rate; + } + // Correct wrong frame rates that seem to be generated by some codecs if ( rate.num > 1000 && rate.den == 1) { diff --git a/src/lib/ip/IPBaseNodes/FileSourceIPNode.cpp b/src/lib/ip/IPBaseNodes/FileSourceIPNode.cpp index a61a3f58e..cd3099cab 100644 --- a/src/lib/ip/IPBaseNodes/FileSourceIPNode.cpp +++ b/src/lib/ip/IPBaseNodes/FileSourceIPNode.cpp @@ -2600,6 +2600,8 @@ namespace IPCore } else { + filename = lookupFilenameInMediaLibrary( filename ); + openMovieTask( filename, SharedMediaPointer() ); } } @@ -2608,6 +2610,17 @@ namespace IPCore { if( !media.empty() ) { + // Lookup the filenames in the media library, save associated HTTP header and cookies + // if any, and return the actual filename to be used for opening the movie in case of + // redirection. + // Note: This needs to be done in the main thread, NOT in the worker thread + // because the media library is python based and thus should only be executed + // in the main thread. + for( size_t i = 0; i < media.size(); i++ ) + { + media[i] = lookupFilenameInMediaLibrary( media[i] ); + } + // Then add a work item to load the actual media movies. m_workItemID = graph()->addWorkItem( [this, sharedMedias, media]() @@ -2639,87 +2652,6 @@ namespace IPCore // graph using this thread (which is usually some worker thread // in IPGraph). // - Bundle* bundle = Bundle::mainBundle(); - - string file = filename; - if( TwkMediaLibrary::isLibraryURL( file ) ) - { - // - // If this file looks like a library URL try to convert it - // into a local file URL and then a path - // - - string local = TwkMediaLibrary::libraryURLtoMediaURL( file ); - local.erase( 0, 7 ); // assuming "file://" not good - file = local; - } - else if( TwkMediaLibrary::isLibraryMediaURL( file ) ) - { - // - // URL is a media URL tracked by one of the libraries. Find its - // media node and find out if its streaming. - // - - TwkMediaLibrary::NodeVector nodes = - TwkMediaLibrary::libraryNodesAssociatedWithURL( file ); - for( int n = 0; n < nodes.size(); n++ ) - { - const TwkMediaLibrary::Node* node = nodes[n]; - std::string nodeName = node->name(); - - if( const TwkMediaLibrary::MediaAPI* api = - TwkMediaLibrary::api_cast( node ) ) - { - if( api->isStreaming() ) - { - TwkMediaLibrary::HTTPCookieVector cookies = api->httpCookies(); - if( !cookies.empty() ) - { - ostringstream cookieStm; - - for( int c = 0; c < cookies.size(); c++ ) - { - if( c > 0 ) cookieStm << "\n"; - cookieStm << cookies[c].name << "=" << cookies[c].value; - - if( !cookies[c].path.empty() ) - cookieStm << "; path=" << cookies[c].path; - if( !cookies[c].domain.empty() ) - cookieStm << "; domain=" << cookies[c].domain; - } - - if (evDebugCookies.getValue()) std::cout << "Cookies:\n" << cookieStm.str() << std::endl; - - m_inparams.push_back( StringPair( "cookies", cookieStm.str() ) ); - } - - TwkMediaLibrary::HTTPHeaderVector headers = api->httpHeaders(); - if( !headers.empty() ) - { - ostringstream headersStm; - - for( size_t h = 0, size = headers.size(); h < size; ++h ) - { - if( h > 0 ) headersStm << "\r\n"; - headersStm << headers[h].name << ": " << headers[h].value; - } - - if (evDebugCookies.getValue()) std::cout << "Headers:\n" << headersStm.str() << std::endl; - - m_inparams.push_back( StringPair( "headers", headersStm.str() ) ); - } - } - - if( api->isRedirecting() ) - { - std::string redirection = api->httpRedirection(); - std::cout << "INFO: " << nodeName << " is redirecting " << file - << " to " << redirection << std::endl; - file = redirection; - } - } - } - } Movie* mov = 0; bool failed = false; @@ -2727,12 +2659,12 @@ namespace IPCore try { - mov = openMovie( file ); + mov = openMovie( filename ); } catch( std::exception& exc ) { failed = true; - errMsg << "Open of '" << file << "' failed: " << exc.what(); + errMsg << "Open of '" << filename << "' failed: " << exc.what(); cerr << "ERROR: " << errMsg.str() << endl; } @@ -2745,14 +2677,14 @@ namespace IPCore { if( errMsg.tellp() == std::streampos( 0 ) ) { - errMsg << "Could not locate \"" << file + errMsg << "Could not locate \"" << filename << "\". Relocate source to fix."; } mov = openProxyMovie( errMsg.str(), 0.0, filename, defaultFPS ); ostringstream str; - str << name() << ";;" << file << ";;" << mediaRepName(); + str << name() << ";;" << filename << ";;" << mediaRepName(); TwkApp::GenericStringEvent event( "source-media-unavailable", graph(), str.str() ); // The following instructions can only be executed on the main thread. @@ -2842,6 +2774,93 @@ namespace IPCore return hashString; } + string FileSourceIPNode::lookupFilenameInMediaLibrary( const string& filename ) + { + string file(filename); + + if( TwkMediaLibrary::isLibraryURL( file ) ) + { + // + // If this file looks like a library URL try to convert it + // into a local file URL and then a path + // + + file = TwkMediaLibrary::libraryURLtoMediaURL( file ); + file.erase( 0, 7 ); // assuming "file://" not good + return file; + } + + if( TwkMediaLibrary::isLibraryMediaURL( file ) ) + { + // + // URL is a media URL tracked by one of the libraries. Find its + // media node and find out if its streaming. + // + + TwkMediaLibrary::NodeVector nodes = + TwkMediaLibrary::libraryNodesAssociatedWithURL( file ); + for( int n = 0; n < nodes.size(); n++ ) + { + const TwkMediaLibrary::Node* node = nodes[n]; + std::string nodeName = node->name(); + + if( const TwkMediaLibrary::MediaAPI* api = + TwkMediaLibrary::api_cast( node ) ) + { + if( api->isStreaming() ) + { + TwkMediaLibrary::HTTPCookieVector cookies = api->httpCookies(); + if( !cookies.empty() ) + { + ostringstream cookieStm; + + for( int c = 0; c < cookies.size(); c++ ) + { + if( c > 0 ) cookieStm << "\n"; + cookieStm << cookies[c].name << "=" << cookies[c].value; + + if( !cookies[c].path.empty() ) + cookieStm << "; path=" << cookies[c].path; + if( !cookies[c].domain.empty() ) + cookieStm << "; domain=" << cookies[c].domain; + } + + if (evDebugCookies.getValue()) std::cout << "Cookies:\n" << cookieStm.str() << std::endl; + + m_inparams.push_back( StringPair( "cookies", cookieStm.str() ) ); + } + + TwkMediaLibrary::HTTPHeaderVector headers = api->httpHeaders(); + if( !headers.empty() ) + { + ostringstream headersStm; + + for( size_t h = 0, size = headers.size(); h < size; ++h ) + { + if( h > 0 ) headersStm << "\r\n"; + headersStm << headers[h].name << ": " << headers[h].value; + } + + if (evDebugCookies.getValue()) std::cout << "Headers:\n" << headersStm.str() << std::endl; + + m_inparams.push_back( StringPair( "headers", headersStm.str() ) ); + } + } + + if( api->isRedirecting() ) + { + std::string redirection = api->httpRedirection(); + std::cout << "INFO: " << nodeName << " is redirecting " << file + << " to " << redirection << std::endl; + file = redirection; + } + } + } + } + + return file; + } + bool FileSourceIPNode::findCachedMediaMetaData( const string& filename, PropertyContainer* pc ) { diff --git a/src/lib/ip/IPBaseNodes/IPBaseNodes/FileSourceIPNode.h b/src/lib/ip/IPBaseNodes/IPBaseNodes/FileSourceIPNode.h index 5bc500b5a..110912d9e 100644 --- a/src/lib/ip/IPBaseNodes/IPBaseNodes/FileSourceIPNode.h +++ b/src/lib/ip/IPBaseNodes/IPBaseNodes/FileSourceIPNode.h @@ -170,6 +170,10 @@ class FileSourceIPNode : public SourceIPNode static std::string cacheHash(const std::string& filename, const std::string& prefix); + // Lookup filename in the media library, save associated HTTP header and cookies if any, + // and return the actual filename to be used for opening the movie in case of redirection. + std::string lookupFilenameInMediaLibrary(const std::string& filename); + static void setSourceNameInID(bool b) { m_sourceNameInID = b; } static bool sourceNameInID() { return m_sourceNameInID; }; diff --git a/src/lib/ip/OCIONodes/OCIOIPNode.cpp b/src/lib/ip/OCIONodes/OCIOIPNode.cpp index 9bec40648..0ed568314 100644 --- a/src/lib/ip/OCIONodes/OCIOIPNode.cpp +++ b/src/lib/ip/OCIONodes/OCIOIPNode.cpp @@ -1,9 +1,9 @@ // -// Copyright (c) 2012 Tweak Software. +// Copyright (c) 2012 Tweak Software. // All rights reserved. -// +// // SPDX-License-Identifier: Apache-2.0 -// +// // #include #include @@ -23,663 +23,769 @@ #include #include -namespace IPCore { -using namespace std; -using namespace boost; - -// In the eventualy of an RV user wanting to get the exact same results -// as in RV 2024 and earlier versions of RV, the following environment -// variable can be defined to tell OCIO to use the legacy GPU processor -// implementation (OCIO::getOptimizedLegacyGPUProcessor()): -static ENVVAR_BOOL( evOCIOUseLegacyGPUProcessor, "RV_OCIO_USE_LEGACY_GPU_PROCESSOR", false ); +namespace IPCore +{ + using namespace std; + using namespace boost; -// Note: This env var is only taken into account when using the legacy GPU -// processor implementation (see above) -static ENVVAR_INT( evOCIOLegacyLut3DSize, "RV_OCIO_3D_LUT_SIZE", 32 ); + // In the eventualy of an RV user wanting to get the exact same results + // as in RV 2024 and earlier versions of RV, the following environment + // variable can be defined to tell OCIO to use the legacy GPU processor + // implementation (OCIO::getOptimizedLegacyGPUProcessor()): + static ENVVAR_BOOL(evOCIOUseLegacyGPUProcessor, + "RV_OCIO_USE_LEGACY_GPU_PROCESSOR", false); -struct OCIOState -{ - OCIO::ConstConfigRcPtr config; - OCIO::ContextRcPtr context; - string display; - string view; - string linear; - string shaderID; - Shader::Function* function; - - OCIOState() : function(0) {} -}; - -namespace { -#define GPU_LANGUAGE_UNKNOWN OCIO::GpuLanguage::GPU_LANGUAGE_CG -OCIO::GpuLanguage GPULanguage = GPU_LANGUAGE_UNKNOWN; -} + // Note: This env var is only taken into account when using the legacy GPU + // processor implementation (see above) + static ENVVAR_INT(evOCIOLegacyLut3DSize, "RV_OCIO_3D_LUT_SIZE", 32); -string -OCIOIPNode::stringProp(const string& name, const string& defaultValue) const -{ - if (const StringProperty* p = property(name)) + struct OCIOState { - if (!p->empty() && p->front() != "") return p->front(); - } + OCIO::ConstConfigRcPtr config; + OCIO::ContextRcPtr context; + string display; + string view; + string linear; + string shaderID; + Shader::Function* function; + + OCIOState() + : function(0) + { + } + }; - return defaultValue; -} + namespace + { +#define GPU_LANGUAGE_UNKNOWN OCIO::GpuLanguage::GPU_LANGUAGE_CG + OCIO::GpuLanguage GPULanguage = GPU_LANGUAGE_UNKNOWN; + } // namespace -int -OCIOIPNode::intProp(const string& name, int defaultValue) const -{ - if (const IntProperty* p = property(name)) + string OCIOIPNode::stringProp(const string& name, + const string& defaultValue) const { - if (!p->empty()) return p->front(); - } + if (const StringProperty* p = property(name)) + { + if (!p->empty() && p->front() != "") + { + return p->front(); + } + } - return defaultValue; -} + return defaultValue; + } -OCIOIPNode::OCIOIPNode(const string& name, - const NodeDefinition* def, - IPGraph* graph, - GroupIPNode* group) - : IPNode(name, def, graph, group), - m_useRawConfig(false) -{ - Property::Info* info = new Property::Info(); - info->setPersistent(false); + int OCIOIPNode::intProp(const string& name, int defaultValue) const + { + if (const IntProperty* p = property(name)) + { + if (!p->empty()) + { + return p->front(); + } + } - string func = def->stringValue("defaults.function", "color"); - declareProperty("ocio.function", func); + return defaultValue; + } - m_activeProperty = declareProperty("ocio.active", 1); + OCIOIPNode::OCIOIPNode(const string& name, const NodeDefinition* def, + IPGraph* graph, GroupIPNode* group) + : IPNode(name, def, graph, group) + , m_useRawConfig(false) + { + Property::Info* info = new Property::Info(); + info->setPersistent(false); - declareProperty("ocio.lut", info); - declareProperty("ocio.lut3DSize", evOCIOLegacyLut3DSize.getValue()); - declareProperty("ocio.inColorSpace", ""); + string func = def->stringValue("defaults.function", "color"); + declareProperty("ocio.function", func); - declareProperty("ocio_color.outColorSpace", ""); + m_activeProperty = declareProperty("ocio.active", 1); - declareProperty("ocio_look.look", ""); - declareProperty("ocio_look.direction", 0); - declareProperty("ocio_look.outColorSpace", ""); + declareProperty("ocio.lut", info); + declareProperty("ocio.lut3DSize", + evOCIOLegacyLut3DSize.getValue()); + declareProperty("ocio.inColorSpace", ""); - declareProperty("ocio_display.display", ""); - declareProperty("ocio_display.view", ""); + declareProperty("ocio_color.outColorSpace", ""); - if (func == "synlinearize") - { - m_inTransformURL = declareProperty("inTransform.url", "", info); - m_inTransformData = declareProperty("inTransform.data", info); - m_useRawConfig = true; - } - - if (func == "syndisplay") - { - m_outTransformURL = declareProperty("outTransform.url", "", info); - m_useRawConfig = true; - } + declareProperty("ocio_look.look", ""); + declareProperty("ocio_look.direction", 0); + declareProperty("ocio_look.outColorSpace", ""); - // - // Read-only properties to report config info to the user - // - m_configDescription = declareProperty("config.description", ""); - m_configWorkingDir = declareProperty("config.workingDir", ""); + declareProperty("ocio_display.display", ""); + declareProperty("ocio_display.view", ""); - m_state = new OCIOState; + if (func == "synlinearize") + { + m_inTransformURL = + declareProperty("inTransform.url", "", info); + m_inTransformData = + declareProperty("inTransform.data", info); + m_useRawConfig = true; + } - if (!getenv("OCIO_LOGGING_LEVEL")) OCIO::SetLoggingLevel(OCIO::LOGGING_LEVEL_WARNING); + if (func == "syndisplay") + { + m_outTransformURL = + declareProperty("outTransform.url", "", info); + m_useRawConfig = true; + } - updateConfig(); + // + // Read-only properties to report config info to the user + // + m_configDescription = + declareProperty("config.description", ""); + m_configWorkingDir = + declareProperty("config.workingDir", ""); - if (GPULanguage == GPU_LANGUAGE_UNKNOWN) - { - IPNode* session = graph->sessionNode(); - int major = session->property("opengl.glsl.majorVersion")->front(); - int minor = session->property("opengl.glsl.minorVersion")->front(); + m_state = new OCIOState; - if (major == 1 && minor < 30) + if (!getenv("OCIO_LOGGING_LEVEL")) { - // Note: 1.2 is currently the lowest available value in OCIO - GPULanguage = OCIO::GPU_LANGUAGE_GLSL_1_2; + OCIO::SetLoggingLevel(OCIO::LOGGING_LEVEL_WARNING); } - else + + updateConfig(); + + if (GPULanguage == GPU_LANGUAGE_UNKNOWN) { - GPULanguage = OCIO::GPU_LANGUAGE_GLSL_1_3; + IPNode* session = graph->sessionNode(); + int major = + session->property("opengl.glsl.majorVersion") + ->front(); + int minor = + session->property("opengl.glsl.minorVersion") + ->front(); + + if (major == 1 && minor < 30) + { + // Note: 1.2 is currently the lowest available value in OCIO + GPULanguage = OCIO::GPU_LANGUAGE_GLSL_1_2; + } + else + { + GPULanguage = OCIO::GPU_LANGUAGE_GLSL_1_3; + } } } -} - -OCIOIPNode::~OCIOIPNode() -{ - if (m_state->function) m_state->function->retire(); - delete m_state; -} -void -OCIOIPNode::updateConfig() -{ - try + OCIOIPNode::~OCIOIPNode() { - if (useRawConfig()) - { - m_state->config = OCIO::Config::CreateFromConfigIOProxy(std::make_shared(this)); - } - else + if (m_state->function) { - m_state->config = OCIO::GetCurrentConfig(); + m_state->function->retire(); } - } - catch (std::exception& exc) - { delete m_state; - cerr << "ERROR: OCIOIPNode updateConfig caught: " << exc.what() << endl; - m_state = 0; - throw; } - // - // XXX we _could_ serialize the config and store it in the session, which - // would enable us to lock down the OCIO config of OCIO nodes read from a - // session file. Currently their config will be semi arbitrary. - // - // stringstream s; - // m_state->config->serialize(s); - // m_configText->front() = s.str(); - // + void OCIOIPNode::updateConfig() + { + try + { + if (useRawConfig()) + { + m_state->config = OCIO::Config::CreateFromConfigIOProxy( + std::make_shared(this)); + } + else + { + m_state->config = OCIO::GetCurrentConfig(); + } + } + catch (std::exception& exc) + { + delete m_state; + cerr << "ERROR: OCIOIPNode updateConfig caught: " << exc.what() + << endl; + m_state = 0; + throw; + } - m_configDescription->front() = m_state->config->getDescription(); - m_configWorkingDir->front() = m_state->config->getWorkingDir(); + // + // XXX we _could_ serialize the config and store it in the session, + // which would enable us to lock down the OCIO config of OCIO nodes + // read from a session file. Currently their config will be semi + // arbitrary. + // + // stringstream s; + // m_state->config->serialize(s); + // m_configText->front() = s.str(); + // - m_state->display = m_state->config->getDefaultDisplay(); - m_state->view = m_state->config->getDefaultView(m_state->display.c_str()); + m_configDescription->front() = m_state->config->getDescription(); + m_configWorkingDir->front() = m_state->config->getWorkingDir(); - if (useRawConfig()) - { - m_state->linear = ""; - } - else if (getenv("OCIO")) - { - OCIO::ConstColorSpaceRcPtr linearColorSpace = - m_state->config->getColorSpace( OCIO::ROLE_SCENE_LINEAR ); - m_state->linear = linearColorSpace ? linearColorSpace->getName() : ""; - } - else - { - m_state->linear = ""; - std::cerr << "ERROR: OCIO environment variable not set" << std::endl; - } + m_state->display = m_state->config->getDefaultDisplay(); + m_state->view = + m_state->config->getDefaultView(m_state->display.c_str()); - m_state->shaderID = ""; + if (useRawConfig()) + { + m_state->linear = ""; + } + else if (getenv("OCIO")) + { + OCIO::ConstColorSpaceRcPtr linearColorSpace = + m_state->config->getColorSpace(OCIO::ROLE_SCENE_LINEAR); + m_state->linear = + linearColorSpace ? linearColorSpace->getName() : ""; + } + else + { + m_state->linear = ""; + std::cerr << "ERROR: OCIO environment variable not set" + << std::endl; + } - if (m_state->function) m_state->function->retire(); - m_state->function = 0; + m_state->shaderID = ""; - updateContext(); -} + if (m_state->function) + { + m_state->function->retire(); + } + m_state->function = 0; -void -OCIOIPNode::updateContext() -{ - m_state->context = m_state->config->getCurrentContext()->createEditableCopy(); + updateContext(); + } - if (Component* context = component("ocio_context")) + void OCIOIPNode::updateContext() { - const Component::Container& props = context->properties(); + m_state->context = + m_state->config->getCurrentContext()->createEditableCopy(); - for (size_t i = 0; i < props.size(); i++) + if (Component* context = component("ocio_context")) { - if (StringProperty* sp = dynamic_cast(props[i])) + const Component::Container& props = context->properties(); + + for (size_t i = 0; i < props.size(); i++) { - if (!sp->empty()) + if (StringProperty* sp = + dynamic_cast(props[i])) { - m_state->context->setStringVar(sp->name().c_str(), sp->front().c_str()); + if (!sp->empty()) + { + m_state->context->setStringVar(sp->name().c_str(), + sp->front().c_str()); + } } } } } -} -namespace { + namespace + { -char -op_shaderLegal(char c) -{ - return (isalnum(c)) ? c : '_'; -} + char op_shaderLegal(char c) { return (isalnum(c)) ? c : '_'; } -string -shaderLegal(const string& s) -{ - string ns; - ns.resize(s.size()); - - transform(s.begin(), s.end(), ns.begin(), op_shaderLegal); - - // OCIO replaces any consecutive '_' with just one '_' (See: OCIO:GPUShaderDesc::setFunctionName) - // In order to keep the names aligned use: GPUShaderDesc::getFunctionName() to get the name OCIO uses to set RV's Shader Function and bind the parameters. - ns = std::regex_replace(ns, std::regex("_+"), "_"); // one or more underscore: replace by only one. - ns = std::regex_replace(ns, std::regex("_+$"), ""); // do not end by an underscore because the code will append another one - - return ns; -} - -// Add the 1D/3D LUT uniform as a shader function parameter to leverage RV's -// current shader variables binding mechanism which rely on shader variables -// being passed as function arguments for all its shaders. Note that the -// OCIOv2 generated shader no longer passes the LUTs as function arguments. -void shaderAddLutAsParameter( std::string& inout_glsl, - const std::string& lutSamplerName, - const std::string& lutSamplerType ) -{ - const std::string from = "vec4 inPixel"; - std::string to = from + std::string( ", " ) + lutSamplerType + - std::string( " " ) + lutSamplerName; - inout_glsl = std::regex_replace( inout_glsl, std::regex( from ), to ); -} + string shaderLegal(const string& s) + { + string ns; + ns.resize(s.size()); + + transform(s.begin(), s.end(), ns.begin(), op_shaderLegal); + + // OCIO replaces any consecutive '_' with just one '_' (See: + // OCIO:GPUShaderDesc::setFunctionName) In order to keep the names + // aligned use: GPUShaderDesc::getFunctionName() to get the name + // OCIO uses to set RV's Shader Function and bind the parameters. + ns = std::regex_replace( + ns, std::regex("_+"), + "_"); // one or more underscore: replace by only one. + ns = std::regex_replace(ns, std::regex("_+$"), + ""); // do not end by an underscore because + // the code will append another one + + return ns; + } -}; // namespace + // Add the 1D/3D LUT uniform as a shader function parameter to leverage + // RV's current shader variables binding mechanism which rely on shader + // variables being passed as function arguments for all its shaders. + // Note that the OCIOv2 generated shader no longer passes the LUTs as + // function arguments. + void shaderAddLutAsParameter(std::string& inout_glsl, + const std::string& lutSamplerName, + const std::string& lutSamplerType) + { + const std::string from = "vec4 inPixel"; + std::string to = from + std::string(", ") + lutSamplerType + + std::string(" ") + lutSamplerName; + inout_glsl = std::regex_replace(inout_glsl, std::regex(from), to); + } -OCIO::MatrixTransformRcPtr OCIOIPNode::createMatrixTransformXYZToRec709() const -{ - OCIO::MatrixTransformRcPtr matrix_xyz_to_rec709 = OCIO::MatrixTransform::Create(); - double m44[16] = { 3.240969941905, -1.537383177570, -0.498610760293, 0, - -0.969243636281, 1.875967501508, 0.041555057407, 0, - 0.055630079697, -0.203976958889, 1.056971514243, 0, - 0, 0, 0, 1 }; - matrix_xyz_to_rec709->setMatrix(m44); + }; // namespace - return matrix_xyz_to_rec709; -} + OCIO::MatrixTransformRcPtr + OCIOIPNode::createMatrixTransformXYZToRec709() const + { + OCIO::MatrixTransformRcPtr matrix_xyz_to_rec709 = + OCIO::MatrixTransform::Create(); + double m44[16] = {3.240969941905, + -1.537383177570, + -0.498610760293, + 0, + -0.969243636281, + 1.875967501508, + 0.041555057407, + 0, + 0.055630079697, + -0.203976958889, + 1.056971514243, + 0, + 0, + 0, + 0, + 1}; + matrix_xyz_to_rec709->setMatrix(m44); + + return matrix_xyz_to_rec709; + } -OCIO::MatrixTransformRcPtr OCIOIPNode::getMatrixTransformXYZToRec709() -{ - if (!m_matrix_xyz_to_rec709) + // Note: Ensure that the m_lock mutex is locked prior to calling this + // function + OCIO::MatrixTransformRcPtr OCIOIPNode::getMatrixTransformXYZToRec709() { - QMutexLocker lock(&this->m_lock); if (!m_matrix_xyz_to_rec709) { - m_matrix_xyz_to_rec709 = createMatrixTransformXYZToRec709(); + if (!m_matrix_xyz_to_rec709) + { + m_matrix_xyz_to_rec709 = createMatrixTransformXYZToRec709(); + } } - } - return m_matrix_xyz_to_rec709; -} + return m_matrix_xyz_to_rec709; + } -OCIO::MatrixTransformRcPtr OCIOIPNode::getMatrixTransformRec709ToXYZ() -{ - if (!m_matrix_rec709_to_xyz) + // Note: Ensure that the m_lock mutex is locked prior to calling this + // function + OCIO::MatrixTransformRcPtr OCIOIPNode::getMatrixTransformRec709ToXYZ() { - QMutexLocker lock(&this->m_lock); if (!m_matrix_rec709_to_xyz) { - m_matrix_rec709_to_xyz = createMatrixTransformXYZToRec709(); - m_matrix_rec709_to_xyz->setDirection(OCIO::TRANSFORM_DIR_INVERSE); + if (!m_matrix_rec709_to_xyz) + { + m_matrix_rec709_to_xyz = createMatrixTransformXYZToRec709(); + m_matrix_rec709_to_xyz->setDirection( + OCIO::TRANSFORM_DIR_INVERSE); + } } - } - - return m_matrix_rec709_to_xyz; -} -IPImage* -OCIOIPNode::evaluate(const Context& context) -{ - string ociofunction = stringProp("ocio.function", "color"); - IPImage* image = 0; - - // - // We don't really know _why_ we need an intermediate-buffer-generating - // node in the Display Pipeline, but we seem to. In particular, if you've - // replaced the RVColorNode with an OCIODisplay node, and you don't - // generate an intermediate buffer, the result is much lower res. So now - // we generate an intermediate buffer in the "display" case. - // - - if (ociofunction != "display" && ociofunction != "syndisplay") - { - image = IPNode::evaluate(context); - if (!image) return IPImage::newNoImage(this, "No Input"); - if (m_activeProperty->size() != 1 || m_activeProperty->front() == 0) return image; - - IPImageVector images(1); - IPImageSet modifiedImages; - images[0] = image; - - convertBlendRenderTypeToIntermediate(images, modifiedImages); - balanceResourceUsage(IPNode::accumulate, - images, - modifiedImages, - 8, 8, 81, 1); + return m_matrix_rec709_to_xyz; } - else - { - Context newContext = context; - IPImage* child = IPNode::evaluate(newContext); - if (!child) return IPImage::newNoImage(this, "No Input"); - if (m_activeProperty->size() != 1 || m_activeProperty->front() == 0) return child; + IPImage* OCIOIPNode::evaluate(const Context& context) + { + string ociofunction = stringProp("ocio.function", "color"); + IPImage* image = 0; // - // Don't make a separate intermediate images unless we really have to. + // We don't really know _why_ we need an intermediate-buffer-generating + // node in the Display Pipeline, but we seem to. In particular, if + // you've replaced the RVColorNode with an OCIODisplay node, and you + // don't generate an intermediate buffer, the result is much lower res. + // So now we generate an intermediate buffer in the "display" case. // - if (willConvertToIntermediate(child)) - { - image = new IPImage(this, - IPImage::BlendRenderType, - context.viewWidth, - context.viewHeight, - 1.0, - IPImage::IntermediateBuffer, - IPImage::FloatDataType); - - image->appendChild(child); - image->shaderExpr = Shader::newSourceRGBA(image); - } - else + + if (ociofunction != "display" && ociofunction != "syndisplay") { + image = IPNode::evaluate(context); + if (!image) + { + return IPImage::newNoImage(this, "No Input"); + } + if (m_activeProperty->size() != 1 || m_activeProperty->front() == 0) + { + return image; + } + IPImageVector images(1); IPImageSet modifiedImages; - images[0] = image = child; + images[0] = image; convertBlendRenderTypeToIntermediate(images, modifiedImages); - balanceResourceUsage(IPNode::accumulate, - images, - modifiedImages, - 8, 8, 81, 1); - image = images[0]; + balanceResourceUsage(IPNode::accumulate, images, modifiedImages, 8, + 8, 81, 1); } - } - boost::hash string_hash; - string inName = stringProp("ocio.inColorSpace", m_state->linear); - - try - { - OCIO::ConstColorSpaceRcPtr srcCS = m_state->config->getColorSpace(inName.c_str()); - OCIO::ConstProcessorRcPtr processor; - ostringstream shaderName; - - if (ociofunction == "color") + else { - // - // Emulate the nuke OCIOColor node - // + Context newContext = context; + IPImage* child = IPNode::evaluate(newContext); + if (!child) + { + return IPImage::newNoImage(this, "No Input"); + } - string outName = stringProp("ocio_color.outColorSpace", ""); - OCIO::ConstColorSpaceRcPtr dstCS = m_state->config->getColorSpace(outName.c_str()); - processor = m_state->config->getProcessor(m_state->context, srcCS, dstCS); + if (m_activeProperty->size() != 1 || m_activeProperty->front() == 0) + { + return child; + } - size_t hashValue = string_hash(inName + outName); - shaderName << "OCIO_c_" << shaderLegal(inName) << "_2_" << shaderLegal(outName) << "_" << name() << "_" << hex << hashValue; - } - else if (ociofunction == "look") - { // - // Emulate the nuke OCIOLook node + // Don't make a separate intermediate images unless we really have + // to. // + if (willConvertToIntermediate(child)) + { + image = new IPImage(this, IPImage::BlendRenderType, + context.viewWidth, context.viewHeight, 1.0, + IPImage::IntermediateBuffer, + IPImage::FloatDataType); - OCIO::LookTransformRcPtr transform = OCIO::LookTransform::Create(); - OCIO::TransformDirection direction = OCIO::TRANSFORM_DIR_FORWARD; - string looksName = stringProp("ocio_look.look", ""); - string outName = stringProp("ocio_look.outColorSpace", m_state->linear); - bool reverse = intProp("ocio_look.direction", 0) == 1; - OCIO::ConstColorSpaceRcPtr dstCS = m_state->config->getColorSpace(outName.c_str()); + image->appendChild(child); + image->shaderExpr = Shader::newSourceRGBA(image); + } + else + { + IPImageVector images(1); + IPImageSet modifiedImages; + images[0] = image = child; + + convertBlendRenderTypeToIntermediate(images, modifiedImages); + balanceResourceUsage(IPNode::accumulate, images, modifiedImages, + 8, 8, 81, 1); + image = images[0]; + } + } + boost::hash string_hash; + string inName = stringProp("ocio.inColorSpace", m_state->linear); - transform->setLooks(looksName.c_str()); + try + { + OCIO::ConstColorSpaceRcPtr srcCS = + m_state->config->getColorSpace(inName.c_str()); + OCIO::ConstProcessorRcPtr processor; + ostringstream shaderName; - if (reverse) + if (ociofunction == "color") { // - // NOTE: src and dst are SWAPPED here on purpose + // Emulate the nuke OCIOColor node // - transform->setSrc(outName.c_str()); - transform->setDst(inName.c_str()); - direction = OCIO::TRANSFORM_DIR_INVERSE; + string outName = stringProp("ocio_color.outColorSpace", ""); + OCIO::ConstColorSpaceRcPtr dstCS = + m_state->config->getColorSpace(outName.c_str()); + processor = m_state->config->getProcessor(m_state->context, + srcCS, dstCS); + + size_t hashValue = string_hash(inName + outName); + shaderName << "OCIO_c_" << shaderLegal(inName) << "_2_" + << shaderLegal(outName) << "_" << name() << "_" + << hex << hashValue; } - else + else if (ociofunction == "look") { - transform->setSrc(inName.c_str()); - transform->setDst(outName.c_str()); - direction = OCIO::TRANSFORM_DIR_FORWARD; - } + // + // Emulate the nuke OCIOLook node + // - processor = m_state->config->getProcessor(m_state->context, transform, direction); + OCIO::LookTransformRcPtr transform = + OCIO::LookTransform::Create(); + OCIO::TransformDirection direction = + OCIO::TRANSFORM_DIR_FORWARD; + string looksName = stringProp("ocio_look.look", ""); + string outName = + stringProp("ocio_look.outColorSpace", m_state->linear); + bool reverse = intProp("ocio_look.direction", 0) == 1; + OCIO::ConstColorSpaceRcPtr dstCS = + m_state->config->getColorSpace(outName.c_str()); - size_t hashValue = string_hash(inName + outName); - shaderName << "OCIO_l_" << shaderLegal(looksName) << "_" << name() << "_" << hex << hashValue << "_" << direction; - } - else if (ociofunction == "display") - { - // - // Emulate the nuke OCIODisplay node - // + transform->setLooks(looksName.c_str()); - OCIO::DisplayViewTransformRcPtr transform = OCIO::DisplayViewTransform::Create(); - string display = stringProp("ocio_display.display", ""); - string view = stringProp("ocio_display.view", ""); + if (reverse) + { + // + // NOTE: src and dst are SWAPPED here on purpose + // - transform->setSrc(inName.c_str()); - transform->setDisplay(display.c_str()); - transform->setView(view.c_str()); - processor = m_state->config->getProcessor(m_state->context, - transform, - OCIO::TRANSFORM_DIR_FORWARD); + transform->setSrc(outName.c_str()); + transform->setDst(inName.c_str()); + direction = OCIO::TRANSFORM_DIR_INVERSE; + } + else + { + transform->setSrc(inName.c_str()); + transform->setDst(outName.c_str()); + direction = OCIO::TRANSFORM_DIR_FORWARD; + } - size_t hashValue = string_hash(inName + display + view); - shaderName << "OCIO_d_" << shaderLegal(display) << "_" << shaderLegal(view) << "_" << name() << "_" << hex << hashValue; - } - else if (ociofunction == "synlinearize") - { - // The input transform can be specified in two ways: via a url or via a data array - string inTransformURL = stringProp("inTransform.url", ""); - if ( inTransformURL.empty() && (!m_inTransformData || m_inTransformData->size()==0)) - { - TWK_THROW_EXC_STREAM("Either inTransform.url or inTransform.data property needs to be set for synlinearize function"); + processor = m_state->config->getProcessor(m_state->context, + transform, direction); + + size_t hashValue = string_hash(inName + outName); + shaderName << "OCIO_l_" << shaderLegal(looksName) << "_" + << name() << "_" << hex << hashValue << "_" + << direction; } + else if (ociofunction == "display") + { + // + // Emulate the nuke OCIODisplay node + // - if (!m_transform) + OCIO::DisplayViewTransformRcPtr transform = + OCIO::DisplayViewTransform::Create(); + string display = stringProp("ocio_display.display", ""); + string view = stringProp("ocio_display.view", ""); + + transform->setSrc(inName.c_str()); + transform->setDisplay(display.c_str()); + transform->setView(view.c_str()); + processor = m_state->config->getProcessor( + m_state->context, transform, OCIO::TRANSFORM_DIR_FORWARD); + + size_t hashValue = string_hash(inName + display + view); + shaderName << "OCIO_d_" << shaderLegal(display) << "_" + << shaderLegal(view) << "_" << name() << "_" << hex + << hashValue; + } + else if (ociofunction == "synlinearize") { - QMutexLocker lock(&this->m_lock); - if (!m_transform) + // The input transform can be specified in two ways: via a url + // or via a data array + string inTransformURL = stringProp("inTransform.url", ""); + if (inTransformURL.empty() + && (!m_inTransformData || m_inTransformData->size() == 0)) { - m_transform = OCIO::GroupTransform::Create(); + TWK_THROW_EXC_STREAM( + "Either inTransform.url or inTransform.data property " + "needs to be set for synlinearize function"); + } - // Is the input transform specified via a data array ? - if (inTransformURL.empty()) + if (!m_transform) + { + QMutexLocker lock(&this->m_lock); + if (!m_transform) { - // We need to provide a unique name to OCIOv2, otherwise it might use a - // potentially incorrect color transform with the same name already in its cache. - static int uniqueCounter = 0; - inTransformURL = name()+"."+std::to_string(uniqueCounter++)+"."+ConfigIOProxy::USE_IN_TRANSFORM_DATA_PROPERTY; + m_transform = OCIO::GroupTransform::Create(); + + // Is the input transform specified via a data array ? + if (inTransformURL.empty()) + { + // We need to provide a unique name to OCIOv2, + // otherwise it might use a potentially incorrect + // color transform with the same name already in its + // cache. + static int uniqueCounter = 0; + inTransformURL = + name() + "." + std::to_string(uniqueCounter++) + + "." + + ConfigIOProxy::USE_IN_TRANSFORM_DATA_PROPERTY; + } + + // Inverse the ICC transform + OCIO::FileTransformRcPtr transform = + OCIO::FileTransform::Create(); + transform->setSrc(inTransformURL.c_str()); + transform->setInterpolation(OCIO::INTERP_BEST); + transform->setDirection(OCIO::TRANSFORM_DIR_INVERSE); + m_transform->appendTransform(transform); + + // Concatenate it with the RV's working space transform + // which is currently assumed to be 709 linear like the + // original EXR spec. + m_transform->appendTransform( + getMatrixTransformXYZToRec709()); } + } + + processor = m_state->config->getProcessor( + m_state->context, m_transform, OCIO::TRANSFORM_DIR_FORWARD); - // Inverse the ICC transform - OCIO::FileTransformRcPtr transform = OCIO::FileTransform::Create(); - transform->setSrc(inTransformURL.c_str()); - transform->setInterpolation(OCIO::INTERP_BEST); - transform->setDirection(OCIO::TRANSFORM_DIR_INVERSE); - m_transform->appendTransform(transform); + size_t hashValue = string_hash(name()); + shaderName << "OCIO_sl_" << name() << "_" << hex << hashValue; + } + else if (ociofunction == "syndisplay") + { + // The outTransform.url property typically refers to an ICC + // monitor profile + const string outTransformURL = + stringProp("outTransform.url", ""); + if (outTransformURL.empty()) + { + TWK_THROW_EXC_STREAM("outTransform.url property needs to " + "be set for syndisplay function"); + } - // Concatenate it with the RV's working space transform which is currently assumed to be - // 709 linear like the original EXR spec. - m_transform->appendTransform(getMatrixTransformXYZToRec709()); + if (!m_transform) + { + QMutexLocker lock(&this->m_lock); + if (!m_transform) + { + m_transform = OCIO::GroupTransform::Create(); + + // RV's working space is currently assumed to be 709 + // linear like the original EXR spec. In the display + // case this transform is inverted. + m_transform->appendTransform( + getMatrixTransformRec709ToXYZ()); + + // Concatenate the inverse workingSpaceTransform with + // the ICC monitor profile transforma + OCIO::FileTransformRcPtr transform = + OCIO::FileTransform::Create(); + transform->setSrc(outTransformURL.c_str()); + transform->setInterpolation(OCIO::INTERP_BEST); + m_transform->appendTransform(transform); + } } + + processor = m_state->config->getProcessor( + m_state->context, m_transform, OCIO::TRANSFORM_DIR_FORWARD); + + size_t hashValue = string_hash(outTransformURL); + shaderName << "OCIO_sd_" << name() << "_" << hex << hashValue; } - processor = m_state->config->getProcessor(m_state->context, - m_transform, - OCIO::TRANSFORM_DIR_FORWARD); + QMutexLocker lock(&this->m_lock); + OCIO::GpuShaderDescRcPtr shaderDesc = + OCIO::GpuShaderDesc::CreateShaderDesc(); + shaderDesc->setFunctionName(shaderName.str().c_str()); + shaderDesc->setLanguage(GPULanguage); + OCIO::ConstGPUProcessorRcPtr gpuProcessor; - size_t hashValue = string_hash(name()); - shaderName << "OCIO_sl_" << name() << "_" << hex << hashValue; - } - else if (ociofunction == "syndisplay") - { - // The outTransform.url property typically refers to an ICC monitor profile - const string outTransformURL = stringProp("outTransform.url", ""); - if (outTransformURL.empty()) + if (evOCIOUseLegacyGPUProcessor.getValue()) { - TWK_THROW_EXC_STREAM("outTransform.url property needs to be set for syndisplay function"); + const int legacyLutSize = + intProp("ocio.lut3DSize", evOCIOLegacyLut3DSize.getValue()); + gpuProcessor = processor->getOptimizedLegacyGPUProcessor( + OCIO::OPTIMIZATION_DEFAULT, legacyLutSize); } + else + { + gpuProcessor = processor->getOptimizedGPUProcessor( + OCIO::OPTIMIZATION_DEFAULT); + } + + // Fills the shaderDesc from the proc. + gpuProcessor->extractGpuShaderInfo(shaderDesc); - if (!m_transform) + string shaderCacheID = gpuProcessor->getCacheID(); + if (m_state->shaderID != shaderCacheID) { - QMutexLocker lock(&this->m_lock); - if (!m_transform) + if (m_state->function) { - m_transform = OCIO::GroupTransform::Create(); + m_state->function->retire(); + } - // RV's working space is currently assumed to be 709 linear like the original - // EXR spec. In the display case this transform is inverted. - m_transform->appendTransform(getMatrixTransformRec709ToXYZ()); + string glsl(shaderDesc->getShaderText()); + + m_1DLUTs.clear(); + const unsigned int numTextures = shaderDesc->getNumTextures(); + for (unsigned idx = 0; idx < numTextures; ++idx) + { + m_1DLUTs.push_back(std::make_shared( + shaderDesc, idx, shaderCacheID)); - // Concatenate the inverse workingSpaceTransform with the ICC monitor profile transforma - OCIO::FileTransformRcPtr transform = OCIO::FileTransform::Create(); - transform->setSrc(outTransformURL.c_str()); - transform->setInterpolation(OCIO::INTERP_BEST); - m_transform->appendTransform(transform); + // Add the LUTs'shader uniform as a shader function + // parameter + shaderAddLutAsParameter(glsl, m_1DLUTs[idx]->samplerName(), + m_1DLUTs[idx]->samplerType()); } - } - processor = m_state->config->getProcessor( - m_state->context, m_transform, OCIO::TRANSFORM_DIR_FORWARD ); - - size_t hashValue = string_hash( outTransformURL ); - shaderName << "OCIO_sd_" << name() << "_" << hex << hashValue; - } - - QMutexLocker lock(&this->m_lock); - OCIO::GpuShaderDescRcPtr shaderDesc = - OCIO::GpuShaderDesc::CreateShaderDesc(); - shaderDesc->setFunctionName( shaderName.str().c_str() ); - shaderDesc->setLanguage( GPULanguage ); - OCIO::ConstGPUProcessorRcPtr gpuProcessor; - - if (evOCIOUseLegacyGPUProcessor.getValue()) - { - const int legacyLutSize= intProp("ocio.lut3DSize", evOCIOLegacyLut3DSize.getValue()); - gpuProcessor = processor->getOptimizedLegacyGPUProcessor(OCIO::OPTIMIZATION_DEFAULT, legacyLutSize); - } - else - { - gpuProcessor = processor->getOptimizedGPUProcessor( OCIO::OPTIMIZATION_DEFAULT ); - } - - // Fills the shaderDesc from the proc. - gpuProcessor->extractGpuShaderInfo( shaderDesc ); - - string shaderCacheID = gpuProcessor->getCacheID(); - if( m_state->shaderID != shaderCacheID ) - { - if( m_state->function ) m_state->function->retire(); - - string glsl( shaderDesc->getShaderText() ); - - m_1DLUTs.clear(); - const unsigned int numTextures = shaderDesc->getNumTextures(); - for( unsigned idx = 0; idx < numTextures; ++idx ) - { - m_1DLUTs.push_back( - std::make_shared( shaderDesc, idx, shaderCacheID ) ); + m_3DLUTs.clear(); + const unsigned int num3DTextures = + shaderDesc->getNum3DTextures(); + for (unsigned idx = 0; idx < num3DTextures; ++idx) + { + m_3DLUTs.push_back(std::make_shared( + shaderDesc, idx, shaderCacheID)); - // Add the LUTs'shader uniform as a shader function parameter - shaderAddLutAsParameter( glsl, m_1DLUTs[idx]->samplerName(), - m_1DLUTs[idx]->samplerType() ); - } + // Add the LUTs'shader uniform as a shader function + // parameter + shaderAddLutAsParameter(glsl, m_3DLUTs[idx]->samplerName(), + m_3DLUTs[idx]->samplerType()); + } - m_3DLUTs.clear(); - const unsigned int num3DTextures = shaderDesc->getNum3DTextures(); - for( unsigned idx = 0; idx < num3DTextures; ++idx ) - { - m_3DLUTs.push_back( - std::make_shared( shaderDesc, idx, shaderCacheID ) ); + m_state->function = new Shader::Function( + shaderDesc->getFunctionName(), glsl, + Shader::Function::Color, 1 /*numFetchesApprox*/); + m_state->shaderID = shaderCacheID; - // Add the LUTs'shader uniform as a shader function parameter - shaderAddLutAsParameter( glsl, m_3DLUTs[idx]->samplerName(), - m_3DLUTs[idx]->samplerType() ); - } + if (Shader::debuggingType() != Shader::NoDebugInfo) + { + cout << "OCIONode: " << name() << " new shaderID " + << shaderCacheID << endl + << "OCIONode: " << numTextures << "x 1D LUTs, " + << num3DTextures << "x 3D LUTs" + << "OCIONode: new Shader '" + << shaderDesc->getFunctionName() << "':" << endl + << glsl << endl; + } + } - m_state->function = new Shader::Function( - shaderDesc->getFunctionName(), glsl, Shader::Function::Color, 1 /*numFetchesApprox*/); - m_state->shaderID = shaderCacheID; + if (image->mergeExpr || image->shaderExpr) + { + const Shader::Function* F = m_state->function; + Shader::ArgumentVector args(F->parameters().size()); + Shader::Expression*& expr = + image->mergeExpr ? image->mergeExpr : image->shaderExpr; - if( Shader::debuggingType() != Shader::NoDebugInfo ) - { - cout << "OCIONode: " << name() << " new shaderID " << shaderCacheID - << endl - << "OCIONode: " << numTextures << "x 1D LUTs, " << num3DTextures << "x 3D LUTs" - << "OCIONode: new Shader '" << shaderDesc->getFunctionName() - << "':" << endl - << glsl << endl; - } - } + args[0] = new Shader::BoundExpression(F->parameters()[0], expr); - if (image->mergeExpr || image->shaderExpr ) - { - const Shader::Function* F = m_state->function; - Shader::ArgumentVector args( F->parameters().size() ); - Shader::Expression*& expr = image->mergeExpr ? image->mergeExpr : image->shaderExpr; + // Add the 3D LUTs expressions (if any) + for (unsigned idx = 0; idx < m_3DLUTs.size(); ++idx) + { + args[idx + 1] = new Shader::BoundSampler( + F->parameters()[idx + 1], + Shader::ImageOrFB( + m_3DLUTs[m_3DLUTs.size() - 1 - idx]->lutfb(), 0)); + } + + // Add the 1D LUTs expressions (if any) + for (unsigned idx = 0; idx < m_1DLUTs.size(); ++idx) + { + args[idx + m_3DLUTs.size() + 1] = new Shader::BoundSampler( + F->parameters()[idx + m_3DLUTs.size() + 1], + Shader::ImageOrFB( + m_1DLUTs[m_1DLUTs.size() - 1 - idx]->lutfb(), 0)); + } - args[0] = - new Shader::BoundExpression( F->parameters()[0], - expr ); + expr = new Shader::Expression(F, args, image); - // Add the 3D LUTs expressions (if any) - for( unsigned idx = 0; idx < m_3DLUTs.size(); ++idx ) - { - args[idx + 1] = new Shader::BoundSampler( - F->parameters()[idx + 1], - Shader::ImageOrFB( m_3DLUTs[m_3DLUTs.size() - 1 - idx]->lutfb(), - 0 ) ); + if (ociofunction == "display" && image->shaderExpr) + { + image->resourceUsage = + image->shaderExpr->computeResourceUsageRecursive(); + } + } } - - // Add the 1D LUTs expressions (if any) - for( unsigned idx = 0; idx < m_1DLUTs.size(); ++idx ) + catch (std::exception& exc) { - args[idx + m_3DLUTs.size() + 1] = new Shader::BoundSampler( - F->parameters()[idx + m_3DLUTs.size() + 1], - Shader::ImageOrFB( m_1DLUTs[m_1DLUTs.size() - 1 - idx]->lutfb(), - 0 ) ); + cerr << "ERROR: OCIOIPNode: " << exc.what() << endl; } - expr = new Shader::Expression( F, args, image ); + return image; + } - if( ociofunction == "display" && image->shaderExpr ) + void OCIOIPNode::propertyChanged(const Property* p) + { + if (Component* context = component("ocio_context")) { - image->resourceUsage = - image->shaderExpr->computeResourceUsageRecursive(); + if (context->hasProperty(p)) + { + updateContext(); + } } - } - } - catch( std::exception& exc ) - { - cerr << "ERROR: OCIOIPNode: " << exc.what() << endl; - } - return image; - } + // synlinearize/syndisplay functions: + // Reset transforms and associated color transform files if a linked + // property changed + if (p == m_inTransformURL || p == m_inTransformData + || p == m_outTransformURL) + { + m_transform.reset(); + } - void OCIOIPNode::propertyChanged( const Property* p ) - { - if (Component* context = component("ocio_context")) - { - if (context->hasProperty(p)) updateContext(); + IPNode::propertyChanged(p); } - // synlinearize/syndisplay functions: - // Reset transforms and associated color transform files if a linked property changed - if (p == m_inTransformURL || p == m_inTransformData || p == m_outTransformURL) + void OCIOIPNode::readCompleted(const string& t, unsigned int v) { - m_transform.reset(); - } - - IPNode::propertyChanged(p); -} + updateContext(); -void -OCIOIPNode::readCompleted(const string& t, unsigned int v) -{ - updateContext(); - - IPNode::readCompleted(t,v); -} + IPNode::readCompleted(t, v); + } -} // IPCore +} // namespace IPCore diff --git a/src/plugins/output/AJADevices/AJADevices/AJAModule.h b/src/plugins/output/AJADevices/AJADevices/AJAModule.h index 56449553b..754150ccf 100644 --- a/src/plugins/output/AJADevices/AJADevices/AJAModule.h +++ b/src/plugins/output/AJADevices/AJADevices/AJAModule.h @@ -28,26 +28,19 @@ namespace AJADevices SimpleMode }; - AJAModule( NativeDisplayPtr, unsigned int app4CC, OperationMode ); - virtual ~AJAModule(); + AJAModule( NativeDisplayPtr, unsigned int appID, OperationMode ); + ~AJAModule() override; - virtual std::string name() const; - virtual std::string SDKIdentifier() const; - virtual std::string SDKInfo() const; - virtual void open(); - virtual void close(); - virtual bool isOpen() const; - - void* deviceScan() const - { - return m_devicescan; - } + [[nodiscard]] std::string name() const override; + [[nodiscard]] std::string SDKIdentifier() const override; + [[nodiscard]] std::string SDKInfo() const override; + void open() override; + void close() override; + [[nodiscard]] bool isOpen() const override; private: - void* m_devicescan{ nullptr }; OperationMode m_mode{ OperationMode::ProMode }; unsigned int m_appID{ 0 }; }; } // namespace AJADevices - diff --git a/src/plugins/output/AJADevices/AJADevices/KonaVideoDevice.h b/src/plugins/output/AJADevices/AJADevices/KonaVideoDevice.h index fa5c2b0ed..428215dc8 100644 --- a/src/plugins/output/AJADevices/AJADevices/KonaVideoDevice.h +++ b/src/plugins/output/AJADevices/AJADevices/KonaVideoDevice.h @@ -25,29 +25,21 @@ #include #endif -#if defined( PLATFORM_DARWIN ) -#include -#endif - #include #include #include #include #include "ajatypes.h" #include "ntv2enums.h" -#include "ntv2devicefeatures.h" -#include "ntv2devicescanner.h" -#include "ntv2utils.h" #include "ntv2card.h" -#include "ntv2vpid.h" namespace AJADevices { class AJAModule; - typedef boost::mutex::scoped_lock ScopedLock; - typedef boost::mutex Mutex; - typedef boost::condition_variable Condition; + using ScopedLock = boost::mutex::scoped_lock; + using Mutex = boost::mutex; + using Condition = boost::condition_variable; struct KonaAudioFormat { @@ -90,8 +82,8 @@ namespace AJADevices unsigned int flags{ 0 }; }; - typedef std::vector KonaVideoFormatVector; - typedef std::vector KonaDataFormatVector; + using KonaVideoFormatVector = std::vector; + using KonaDataFormatVector = std::vector; struct KonaSyncMode { @@ -188,17 +180,17 @@ namespace AJADevices // Types // - typedef TwkUtil::Timer Timer; - typedef TwkGLF::GLFence GLFence; - typedef std::vector BufferVector; - typedef stl_ext::thread_group ThreadGroup; - typedef std::vector AudioBuffer; - typedef std::vector AudioBufferVector; - typedef std::vector