diff --git a/.clang-format b/.clang-format new file mode 100644 index 00000000..44abc84e --- /dev/null +++ b/.clang-format @@ -0,0 +1,321 @@ +--- +Language: Cpp +AccessModifierOffset: -1 +AlignAfterOpenBracket: Align +AlignArrayOfStructures: None +AlignConsecutiveAssignments: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionDeclarations: false + AlignFunctionPointers: false + PadOperators: true +AlignConsecutiveBitFields: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionDeclarations: false + AlignFunctionPointers: false + PadOperators: false +AlignConsecutiveDeclarations: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionDeclarations: true + AlignFunctionPointers: false + PadOperators: false +AlignConsecutiveMacros: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionDeclarations: false + AlignFunctionPointers: false + PadOperators: false +AlignConsecutiveShortCaseStatements: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCaseArrows: false + AlignCaseColons: false +AlignConsecutiveTableGenBreakingDAGArgColons: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionDeclarations: false + AlignFunctionPointers: false + PadOperators: false +AlignConsecutiveTableGenCondOperatorColons: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionDeclarations: false + AlignFunctionPointers: false + PadOperators: false +AlignConsecutiveTableGenDefinitionColons: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionDeclarations: false + AlignFunctionPointers: false + PadOperators: false +AlignEscapedNewlines: Left +AlignOperands: Align +AlignTrailingComments: + Kind: Always + OverEmptyLines: 0 +AllowAllArgumentsOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowBreakBeforeNoexceptSpecifier: Never +AllowShortBlocksOnASingleLine: Never +AllowShortCaseExpressionOnASingleLine: true +AllowShortCaseLabelsOnASingleLine: false +AllowShortCompoundRequirementOnASingleLine: true +AllowShortEnumsOnASingleLine: true +AllowShortFunctionsOnASingleLine: Inline +AllowShortIfStatementsOnASingleLine: WithoutElse +AllowShortLambdasOnASingleLine: All +AllowShortLoopsOnASingleLine: true +AllowShortNamespacesOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakBeforeMultilineStrings: true +AttributeMacros: + - __capability +BinPackArguments: true +BinPackParameters: BinPack +BitFieldColonSpacing: Both +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: Never + AfterEnum: false + AfterExternBlock: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + BeforeCatch: false + BeforeElse: false + BeforeLambdaBody: false + BeforeWhile: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakAdjacentStringLiterals: true +BreakAfterAttributes: Leave +BreakAfterJavaFieldAnnotations: false +BreakAfterReturnType: None +BreakArrays: true +BreakBeforeBinaryOperators: None +BreakBeforeConceptDeclarations: Always +BreakBeforeBraces: Allman +BreakBeforeInlineASMColon: OnlyMultiline +BreakBeforeTernaryOperators: true +BreakBinaryOperations: Never +BreakConstructorInitializers: BeforeColon +BreakFunctionDefinitionParameters: false +BreakInheritanceList: BeforeColon +BreakStringLiterals: true +BreakTemplateDeclarations: Yes +ColumnLimit: 100 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DerivePointerAlignment: true +DisableFormat: false +EmptyLineAfterAccessModifier: Never +EmptyLineBeforeAccessModifier: LogicalBlock +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IfMacros: + - KJ_IF_MAYBE +IncludeBlocks: Regroup +IncludeCategories: + - Regex: '^' + Priority: 2 + SortPriority: 0 + CaseSensitive: false + - Regex: '^<.*\.h>' + Priority: 1 + SortPriority: 0 + CaseSensitive: false + - Regex: '^<.*' + Priority: 2 + SortPriority: 0 + CaseSensitive: false + - Regex: '.*' + Priority: 3 + SortPriority: 0 + CaseSensitive: false +IncludeIsMainRegex: '([-_](test|unittest))?$' +IncludeIsMainSourceRegex: '' +IndentAccessModifiers: true +IndentCaseBlocks: false +IndentCaseLabels: true +IndentExportBlock: true +IndentExternBlock: AfterExternBlock +IndentGotoLabels: true +IndentPPDirectives: None +IndentRequiresClause: true +IndentWidth: 4 +IndentWrappedFunctionNames: false +InsertBraces: false +InsertNewlineAtEOF: false +InsertTrailingCommas: None +IntegerLiteralSeparator: + Binary: 0 + BinaryMinDigits: 0 + Decimal: 0 + DecimalMinDigits: 0 + Hex: 0 + HexMinDigits: 0 +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLines: + AtEndOfFile: false + AtStartOfBlock: false + AtStartOfFile: true +KeepFormFeed: false +LambdaBodyIndentation: Signature +LineEnding: DeriveLF +MacroBlockBegin: '' +MacroBlockEnd: '' +MainIncludeChar: Quote +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBinPackProtocolList: Never +ObjCBlockIndentWidth: 2 +ObjCBreakBeforeNestedBlockParam: true +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PackConstructorInitializers: NextLine +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 1 +PenaltyBreakBeforeMemberAccess: 150 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakOpenParenthesis: 0 +PenaltyBreakScopeResolution: 500 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PenaltyIndentedWhitespace: 0 +PenaltyReturnTypeOnItsOwnLine: 200 +PointerAlignment: Left +PPIndentWidth: -1 +QualifierAlignment: Leave +RawStringFormats: + - Language: Cpp + Delimiters: + - cc + - CC + - cpp + - Cpp + - CPP + - 'c++' + - 'C++' + CanonicalDelimiter: '' + BasedOnStyle: google + - Language: TextProto + Delimiters: + - pb + - PB + - proto + - PROTO + EnclosingFunctions: + - EqualsProto + - EquivToProto + - PARSE_PARTIAL_TEXT_PROTO + - PARSE_TEST_PROTO + - PARSE_TEXT_PROTO + - ParseTextOrDie + - ParseTextProtoOrDie + - ParseTestProto + - ParsePartialTestProto + CanonicalDelimiter: pb + BasedOnStyle: google +ReferenceAlignment: Pointer +ReflowComments: Always +RemoveBracesLLVM: false +RemoveEmptyLinesInUnwrappedLines: false +RemoveParentheses: Leave +RemoveSemicolon: false +RequiresClausePosition: OwnLine +RequiresExpressionIndentation: OuterScope +SeparateDefinitionBlocks: Leave +ShortNamespaceLines: 1 +SkipMacroDefinitionBody: false +SortIncludes: CaseSensitive +SortJavaStaticImport: Before +SortUsingDeclarations: LexicographicNumeric +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: true +SpaceAroundPointerQualifiers: Default +SpaceBeforeAssignmentOperators: true +SpaceBeforeCaseColon: false +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeJsonColon: false +SpaceBeforeParens: ControlStatements +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: 2 +SpacesInAngles: Never +SpacesInContainerLiterals: true +SpacesInLineCommentPrefix: + Minimum: 1 + Maximum: -1 +SpacesInParens: Never +SpacesInParensOptions: + ExceptDoubleParentheses: false + InCStyleCasts: false + InConditionalStatements: false + InEmptyParentheses: false + Other: false +SpacesInSquareBrackets: false +Standard: Auto +StatementAttributeLikeMacros: + - Q_EMIT +StatementMacros: + - Q_UNUSED + - QT_REQUIRE_VERSION +TableGenBreakInsideDAGArg: DontBreak +TabWidth: 4 +UseTab: Never +VerilogBreakBetweenInstancePorts: true +WhitespaceSensitiveMacros: + - BOOST_PP_STRINGIZE + - CF_SWIFT_NAME + - NS_SWIFT_NAME + - PP_STRINGIZE + - STRINGIZE +WrapNamespaceBodyWithEmptyLines: Leave +... diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 00000000..b7f1911f --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,52 @@ +--- +Checks: ' + bugprone-*, + cert-*, + clang-diagnostic-*, + clang-analyzer-*, + concurrency-*, + cppcoreguidelines-*, + modernize-*, + performance-*, + portability-*, + readability-* + ' +WarningsAsErrors: '' +HeaderFileExtensions: + - '' + - h + - hh + - hpp + - hxx +ImplementationFileExtensions: + - c + - cc + - cpp + - cxx +HeaderFilterRegex: '' +ExcludeHeaderFilterRegex: '' +FormatStyle: file +User: Andrew +CheckOptions: + cert-arr39-c.WarnOnSizeOfCompareToConstant: 'false' + cert-arr39-c.WarnOnSizeOfConstant: 'false' + cert-arr39-c.WarnOnSizeOfIntegerExpression: 'false' + cert-arr39-c.WarnOnSizeOfPointer: 'false' + cert-arr39-c.WarnOnSizeOfPointerToAggregate: 'false' + cert-arr39-c.WarnOnSizeOfThis: 'false' + cert-dcl16-c.NewSuffixes: 'L;LL;LU;LLU' + cert-err33-c.AllowCastToVoid: 'true' + cert-err33-c.CheckedFunctions: '^::aligned_alloc;^::asctime_s;^::at_quick_exit;^::atexit;^::bsearch;^::bsearch_s;^::btowc;^::c16rtomb;^::c32rtomb;^::calloc;^::clock;^::cnd_broadcast;^::cnd_init;^::cnd_signal;^::cnd_timedwait;^::cnd_wait;^::ctime_s;^::fclose;^::fflush;^::fgetc;^::fgetpos;^::fgets;^::fgetwc;^::fopen;^::fopen_s;^::fprintf;^::fprintf_s;^::fputc;^::fputs;^::fputwc;^::fputws;^::fread;^::freopen;^::freopen_s;^::fscanf;^::fscanf_s;^::fseek;^::fsetpos;^::ftell;^::fwprintf;^::fwprintf_s;^::fwrite;^::fwscanf;^::fwscanf_s;^::getc;^::getchar;^::getenv;^::getenv_s;^::gets_s;^::getwc;^::getwchar;^::gmtime;^::gmtime_s;^::localtime;^::localtime_s;^::malloc;^::mbrtoc16;^::mbrtoc32;^::mbsrtowcs;^::mbsrtowcs_s;^::mbstowcs;^::mbstowcs_s;^::memchr;^::mktime;^::mtx_init;^::mtx_lock;^::mtx_timedlock;^::mtx_trylock;^::mtx_unlock;^::printf_s;^::putc;^::putwc;^::raise;^::realloc;^::remove;^::rename;^::scanf;^::scanf_s;^::setlocale;^::setvbuf;^::signal;^::snprintf;^::snprintf_s;^::sprintf;^::sprintf_s;^::sscanf;^::sscanf_s;^::strchr;^::strerror_s;^::strftime;^::strpbrk;^::strrchr;^::strstr;^::strtod;^::strtof;^::strtoimax;^::strtok;^::strtok_s;^::strtol;^::strtold;^::strtoll;^::strtoul;^::strtoull;^::strtoumax;^::strxfrm;^::swprintf;^::swprintf_s;^::swscanf;^::swscanf_s;^::thrd_create;^::thrd_detach;^::thrd_join;^::thrd_sleep;^::time;^::timespec_get;^::tmpfile;^::tmpfile_s;^::tmpnam;^::tmpnam_s;^::tss_create;^::tss_get;^::tss_set;^::ungetc;^::ungetwc;^::vfprintf;^::vfprintf_s;^::vfscanf;^::vfscanf_s;^::vfwprintf;^::vfwprintf_s;^::vfwscanf;^::vfwscanf_s;^::vprintf_s;^::vscanf;^::vscanf_s;^::vsnprintf;^::vsnprintf_s;^::vsprintf;^::vsprintf_s;^::vsscanf;^::vsscanf_s;^::vswprintf;^::vswprintf_s;^::vswscanf;^::vswscanf_s;^::vwprintf_s;^::vwscanf;^::vwscanf_s;^::wcrtomb;^::wcschr;^::wcsftime;^::wcspbrk;^::wcsrchr;^::wcsrtombs;^::wcsrtombs_s;^::wcsstr;^::wcstod;^::wcstof;^::wcstoimax;^::wcstok;^::wcstok_s;^::wcstol;^::wcstold;^::wcstoll;^::wcstombs;^::wcstombs_s;^::wcstoul;^::wcstoull;^::wcstoumax;^::wcsxfrm;^::wctob;^::wctrans;^::wctype;^::wmemchr;^::wprintf_s;^::wscanf;^::wscanf_s;' + cert-oop54-cpp.WarnOnlyIfThisHasSuspiciousField: 'false' + cert-str34-c.DiagnoseSignedUnsignedCharComparisons: 'false' + cppcoreguidelines-non-private-member-variables-in-classes.IgnoreClassesWithAllMemberVariablesBeingPublic: 'true' + google-readability-braces-around-statements.ShortStatementLines: '1' + google-readability-function-size.StatementThreshold: '800' + google-readability-namespace-comments.ShortNamespaceLines: '10' + google-readability-namespace-comments.SpacesBeforeComments: '2' + llvm-else-after-return.WarnOnConditionVariables: 'false' + llvm-else-after-return.WarnOnUnfixable: 'false' + llvm-qualified-auto.AddConstToQualified: 'false' +SystemHeaders: false +... + diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 894cdc57..19b0b652 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,15 +23,15 @@ jobs: path: | ~/.cache/bazelisk ~/.cache/bazel - key: ${{ runner.os }}-${{ env.cache-name }}-${{ github.ref }} + key: ${{ runner.os }}-bazel-cache-${{ hashFiles('WORKSPACE', '**/BUILD', '**/*.bzl', '.bazelrc') }} restore-keys: | - ${{ runner.os }}-${{ env.cache-name }}-development + ${{ runner.os }}-bazel-cache- # Install dependencies (project has additional setup requirements) - name: Install Dependencies run: | sudo apt-get update - sudo apt-get install -y libxml2-dev ocl-icd-opencl-dev + sudo apt-get install -y libxml2-dev ocl-icd-opencl-dev gdb # Checks-out your repository under $GITHUB_WORKSPACE, which is the CWD for # the rest of the steps diff --git a/.github/workflows/code_quality.yml b/.github/workflows/code_quality.yml new file mode 100644 index 00000000..08ad3983 --- /dev/null +++ b/.github/workflows/code_quality.yml @@ -0,0 +1,76 @@ +name: Static Analysis + +on: + push: + branches: [main] + pull_request: + +jobs: + cppcheck: + name: Run Cppcheck + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + submodules: true + lfs: true + + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get install -y cppcheck + + - name: Run cppcheck + run: | + mkdir -p cppcheck-report + cppcheck --enable=all --inconclusive --quiet \ + --output-file=cppcheck-report/cppcheck.txt \ + $GITHUB_WORKSPACE/framework/src/ \ + -I $GITHUB_WORKSPACE/include/ \ + -I $GITHUB_WORKSPACE/framework/include/ + cat cppcheck-report/cppcheck.txt + + - name: Upload cppcheck report artifact + uses: actions/upload-artifact@v4 + with: + name: cppcheck-report + path: cppcheck-report/cppcheck.txt + + clang-tidy: + name: Run Clang-Tidy + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + submodules: true + lfs: true + + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get install -y clang-tidy + + - name: Run clang-tidy + run: | + bazel run @hedron_compile_commands//:refresh_all + mkdir -p clang-tidy-report + clang-tidy -p $GITHUB_WORKSPACE/compile_commands.json \ + --config-file=$GITHUB_WORKSPACE/.clang-tidy \ + --export-fixes=clang-tidy-report/clang-tidy-fixes.yaml \ + --quiet \ + $GITHUB_WORKSPACE/framework/src/vx_*.cpp \ + $GITHUB_WORKSPACE/framework/include/vx_*.h* \ + $GITHUB_WORKSPACE/include/VX/vx*.h \ + > clang-tidy-report/clang-tidy.txt \ + 2>&1 || true + cat clang-tidy-report/clang-tidy.txt + + - name: Upload clang-tidy report artifact + uses: actions/upload-artifact@v4 + with: + name: clang-tidy-report + path: | + clang-tidy-report/clang-tidy.txt + clang-tidy-report/clang-tidy-fixes.yaml diff --git a/.github/workflows/cppcheck.yml b/.github/workflows/cppcheck.yml deleted file mode 100644 index e4a8d9af..00000000 --- a/.github/workflows/cppcheck.yml +++ /dev/null @@ -1,39 +0,0 @@ -name: Static Analysis - -on: - push: - branches: [main] - pull_request: - -jobs: - cppcheck: - name: Run Cppcheck - runs-on: ubuntu-latest - - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - submodules: true - lfs: true - - - name: Install cppcheck - run: | - sudo apt-get update - sudo apt-get install -y cppcheck - - - name: Run cppcheck - run: | - mkdir -p cppcheck-report - cppcheck --enable=all --inconclusive --quiet \ - --output-file=cppcheck-report/cppcheck.txt \ - $GITHUB_WORKSPACE/framework/src/ \ - -I $GITHUB_WORKSPACE/include/ \ - -I $GITHUB_WORKSPACE/framework/include/ - cat cppcheck-report/cppcheck.txt - - - name: Upload cppcheck report artifact - uses: actions/upload-artifact@v4 - with: - name: cppcheck-report - path: cppcheck-report/cppcheck.txt diff --git a/cts b/cts index a5d7b1f9..3ba23a1e 160000 --- a/cts +++ b/cts @@ -1 +1 @@ -Subproject commit a5d7b1f919f60ab475a6b94c2091f08468e57e36 +Subproject commit 3ba23a1eae97ee221cbd9fb34b4259763bd6c215 diff --git a/framework/include/vx_context.h b/framework/include/vx_context.h index f54f4a80..047d5e60 100644 --- a/framework/include/vx_context.h +++ b/framework/include/vx_context.h @@ -267,10 +267,14 @@ class Context : public Reference cl_context opencl_context; cl_command_queue opencl_command_queue; #endif -#ifdef OPENVX_USE_EVENTS +#ifdef OPENVX_USE_PIPELINING /*! \brief The event queue for the context */ EventQueue event_queue; #endif + /*! \brief The graph queue for the context */ + vx_value_set_t graph_queue[VX_INT_MAX_QUEUE_DEPTH]; + /*! \brief The number of graphs in the queue */ + vx_size numGraphsQueued; }; #endif /* VX_CONTEXT_H */ diff --git a/framework/include/vx_event_queue.hpp b/framework/include/vx_event_queue.hpp index ae81ff26..c75f5e10 100644 --- a/framework/include/vx_event_queue.hpp +++ b/framework/include/vx_event_queue.hpp @@ -64,6 +64,7 @@ class EventQueue { std::lock_guard lock(mutex_); enabled_ = status; + cv_.notify_all(); return VX_SUCCESS; } @@ -148,7 +149,6 @@ class EventQueue { vx_status status = VX_SUCCESS; std::optional evt; - std::unique_lock lock(mutex_); if (!enabled_) { @@ -201,6 +201,7 @@ private : bool enabled_; std::condition_variable cv_; std::deque queue_; size_t max_size_; + static constexpr int timeout_ms_ = 10000; std::vector registrations_; /** @@ -230,10 +231,11 @@ private : bool enabled_; * @return std::optional Event if available, otherwise std::nullopt */ std::optional wait_and_pop( - std::chrono::milliseconds timeout = std::chrono::milliseconds::max()) + std::chrono::milliseconds timeout = std::chrono::milliseconds(timeout_ms_)) { std::unique_lock lock(mutex_); - if (!cv_.wait_for(lock, timeout, [this] { return !queue_.empty(); })) + cv_.wait_for(lock, timeout, [this] { return !queue_.empty(); }); + if (queue_.empty()) { return std::nullopt; // Timeout } diff --git a/framework/include/vx_graph.h b/framework/include/vx_graph.h index 46cd5c86..559045fd 100644 --- a/framework/include/vx_graph.h +++ b/framework/include/vx_graph.h @@ -16,6 +16,9 @@ #ifndef VX_GRAPH_H #define VX_GRAPH_H +#include + +#include "vx_internal.h" #include "vx_reference.h" /*! @@ -158,6 +161,16 @@ class Graph : public Reference vx_status traverseGraph(vx_uint32 parentIndex, vx_uint32 childIndex); + /** + * @brief Validate the graph parameters queue references list + * + * @param graph_parameters_queue_param + * @return vx_status + * @ingroup group_int_graph + */ + vx_status pipelineValidateRefsList( + const vx_graph_parameter_queue_params_t graph_parameters_queue_param); + /** * @brief Destruct function for the Graph object * @ingroup group_int_graph @@ -188,15 +201,33 @@ class Graph : public Reference vx_node node; /*! \brief The index to the parameter on the node. */ vx_uint32 index; +#ifdef OPENVX_USE_PIPELINING + /*! \brief Set to an enum value in \ref vx_type_e. */ + vx_enum type; + /*! \brief the max buffers that can be enqueued */ + vx_uint32 numBufs; + /*! \brief The internal data ref queue */ + ExecutionQueue queue; + /*! \brief references that can be queued into data ref queue */ + vx_reference refs_list[VX_INT_MAX_QUEUE_DEPTH]; +#endif } parameters[VX_INT_MAX_PARAMS]; /*! \brief The number of graph parameters. */ vx_uint32 numParams; /*! \brief A switch to turn off SMP mode */ - vx_bool should_serialize; + vx_bool shouldSerialize; /*! \brief [hidden] If non-NULL, the parent graph, for scope handling. */ vx_graph parentGraph; /*! \brief The array of all delays in this graph */ vx_delay delays[VX_INT_MAX_REF]; + /*! \brief The graph scheduling mode */ + vx_graph_schedule_mode_type_e scheduleMode; +#ifdef OPENVX_USE_PIPELINING + /*! \brief The number of enqueable parameters */ + vx_uint32 numEnqueableParams; + /*! \brief The number of times to schedule a graph */ + vx_size scheduleCount; +#endif }; #endif /* VX_GRAPH_H */ diff --git a/framework/include/vx_internal.h b/framework/include/vx_internal.h index 9358aa5d..f6e8b202 100644 --- a/framework/include/vx_internal.h +++ b/framework/include/vx_internal.h @@ -176,7 +176,7 @@ /*! \brief Maximum queue depth. * \ingroup group_int_defines */ -#define VX_INT_MAX_QUEUE_DEPTH (32) +#define VX_INT_MAX_QUEUE_DEPTH (100002) /*! \brief The value to use in event waiting which never returns. * \ingroup group_int_defines @@ -467,7 +467,7 @@ typedef struct vx_threadpool_t { /*! \brief Unit size of a work item */ uint32_t sizeWorkItem; /*! \brief The number of corrent items in the queue */ - int32_t numCurrentItems; + int32_t numCurrentItems; /*! \brief The array of workers */ vx_threadpool_worker_t *workers; /*! \brief The next index to submit work to */ diff --git a/framework/include/vx_meta_format.h b/framework/include/vx_meta_format.h index 537e0918..9a28fe2d 100644 --- a/framework/include/vx_meta_format.h +++ b/framework/include/vx_meta_format.h @@ -54,6 +54,120 @@ class MetaFormat : public Reference */ ~MetaFormat() = default; + /** + * @brief Check if two meta formats are valid + * + * @param meta1 The first meta format + * @param meta2 The second meta format + * @return vx_bool True if valid, false otherwise + * @ingroup group_int_meta_format + */ + static vx_bool isValidMetaFormat(vx_meta_format meta1, vx_meta_format meta2); + + /** + * @brief Check if two meta formats are equal + * + * @param meta1 The first meta format + * @param meta2 The second meta format + * @param ref_type The reference type + * @return vx_bool True if equal, false otherwise + * @ingroup group_int_meta_format + * + */ + static vx_bool isMetaFormatEqual( + vx_meta_format meta1, vx_meta_format meta2, vx_enum ref_type); + + /*! \brief Check if two image meta formats are equal + * \param [in] meta1 The first meta format + * \param [in] meta2 The second meta format + * \ingroup group_int_meta_format + */ + static vx_bool isMetaFormatImageEqual(vx_meta_format meta1, vx_meta_format meta2); + + /*! \brief Check if two array meta formats are equal + * \param [in] meta1 The first meta format + * \param [in] meta2 The second meta format + * \ingroup group_int_meta_format + */ + static vx_bool isMetaFormatArrayEqual(vx_meta_format meta1, vx_meta_format meta2); + + /*! \brief Check if two scalar meta formats are equal + * \param [in] meta1 The first meta format + * \param [in] meta2 The second meta format + * \ingroup group_int_meta_format + */ + static vx_bool isMetaFormatScalarEqual(vx_meta_format meta1, vx_meta_format meta2); + + /*! \brief Check if two pyramid meta formats are equal + * \param [in] meta1 The first meta format + * \param [in] meta2 The second meta format + * \ingroup group_int_meta_format + */ + static vx_bool isMetaFormatPyramidEqual(vx_meta_format meta1, vx_meta_format meta2); + + /*! \brief Check if two matrix meta formats are equal + * \param [in] meta1 The first meta format + * \param [in] meta2 The second meta format + * \ingroup group_int_meta_format + */ + static vx_bool isMetaFormatMatrixEqual(vx_meta_format meta1, vx_meta_format meta2); + + /*! \brief Check if two distribution meta formats are equal + * \param [in] meta1 The first meta format + * \param [in] meta2 The second meta format + * \ingroup group_int_meta_format + */ + static vx_bool isMetaFormatDistributionEqual(vx_meta_format meta1, vx_meta_format meta2); + + /*! \brief Check if two convolution meta formats are equal + * \param [in] meta1 The first meta format + * \param [in] meta2 The second meta format + * \ingroup group_int_meta_format + */ + static vx_bool isMetaFormatConvolutionEqual(vx_meta_format meta1, vx_meta_format meta2); + + /*! \brief Check if two threshold meta formats are equal + * \param [in] meta1 The first meta format + * \param [in] meta2 The second meta format + * \ingroup group_int_meta_format + */ + static vx_bool isMetaFormatThresholdEqual(vx_meta_format meta1, vx_meta_format meta2); + + /*! \brief Check if two remap meta formats are equal + * \param [in] meta1 The first meta format + * \param [in] meta2 The second meta format + * \ingroup group_int_meta_format + */ + static vx_bool isMetaFormatRemapEqual(vx_meta_format meta1, vx_meta_format meta2); + + /*! \brief Check if two LUT meta formats are equal + * \param [in] meta1 The first meta format + * \param [in] meta2 The second meta format + * \ingroup group_int_meta_format + */ + static vx_bool isMetaFormatLutEqual(vx_meta_format meta1, vx_meta_format meta2); + + /*! \brief Check if two object array meta formats are equal + * \param [in] meta1 The first meta format + * \param [in] meta2 The second meta format + * \ingroup group_int_meta_format + */ + static vx_bool isMetaFormatObjectArrayEqual(vx_meta_format meta1, vx_meta_format meta2); + + /*! \brief Check if two tensor meta formats are equal + * \param [in] meta1 The first meta format + * \param [in] meta2 The second meta format + * \ingroup group_int_meta_format + */ + static vx_bool isMetaFormatTensorEqual(vx_meta_format meta1, vx_meta_format meta2); + + /*! \brief Check if two user data object meta formats are equal + * \param [in] meta1 The first meta format + * \param [in] meta2 The second meta format + * \ingroup group_int_meta_format + */ + static vx_bool isMetaFormatUserDataObjectEqual(vx_meta_format meta1, vx_meta_format meta2); + /*!< \brief The size of struct. */ vx_size size; /*!< \brief The \ref vx_type_e or \ref vx_df_image_e code */ diff --git a/framework/src/vx_context.cpp b/framework/src/vx_context.cpp index b0357629..21577fbc 100644 --- a/framework/src/vx_context.cpp +++ b/framework/src/vx_context.cpp @@ -61,34 +61,52 @@ static vx_sem_t global_lock; /*****************************************************************************/ // INTERNAL CONTEXT APIS /*****************************************************************************/ -Context::Context() : Reference(nullptr, VX_TYPE_CONTEXT, nullptr), -p_global_lock(nullptr), -reftable(), -num_references(0), -modules(), -num_modules(0), -proc(), -num_kernels(0), -num_unique_kernels(0), -num_targets(0), -targets(), -priority_targets(), -log_callback(nullptr), -log_lock(), -log_enabled(vx_false_e), -log_reentrant(vx_false_e), -perf_enabled(vx_true_e), -accessors(), -memory_maps_lock(), -memory_maps(), -user_structs(), -workers(nullptr), -imm_border(), -imm_border_policy(), -next_dynamic_user_kernel_id(0), -next_dynamic_user_library_id(0), -imm_target_enum(), -imm_target_string() +Context::Context() + : Reference(nullptr, VX_TYPE_CONTEXT, nullptr), + p_global_lock(nullptr), + reftable(), + num_references(0), + modules(), + num_modules(0), + proc(), + num_kernels(0), + num_unique_kernels(0), + num_targets(0), + targets(), + priority_targets(), + log_callback(nullptr), + log_lock(), + log_enabled(vx_false_e), + log_reentrant(vx_false_e), + perf_enabled(vx_true_e), + accessors(), + memory_maps_lock(), + memory_maps(), + user_structs(), + workers(nullptr), +#if defined(EXPERIMENTAL_USE_OPENCL) + platforms(), + num_platforms(0), + devices(), + num_devices(), + global(), + queues(), +#endif + imm_border(), + imm_border_policy(), + next_dynamic_user_kernel_id(0), + next_dynamic_user_library_id(0), + imm_target_enum(), + imm_target_string(), +#ifdef OPENVX_USE_OPENCL_INTEROP + opencl_context(nullptr), + opencl_command_queue(nullptr), +#endif +#ifdef OPENVX_USE_PIPELINING + event_queue(), +#endif + graph_queue(), + numGraphsQueued(0ul) { } diff --git a/framework/src/vx_event_queue.cpp b/framework/src/vx_event_queue.cpp index 0a27eb63..2876b9eb 100755 --- a/framework/src/vx_event_queue.cpp +++ b/framework/src/vx_event_queue.cpp @@ -19,7 +19,7 @@ #include "vx_internal.h" -#ifdef OPENVX_USE_EVENTS +#ifdef OPENVX_USE_PIPELINING VX_API_ENTRY vx_status VX_API_CALL vxEnableEvents(vx_context context) { @@ -65,7 +65,7 @@ VX_API_ENTRY vx_status VX_API_CALL vxSendUserEvent(vx_context context, vx_uint32 if (Context::isValidContext(context) == vx_false_e) { VX_PRINT(VX_ZONE_ERROR, "context is invalid\n"); - status = (vx_status)VX_ERROR_INVALID_REFERENCE; + status = VX_ERROR_INVALID_REFERENCE; } if (VX_SUCCESS == status) @@ -83,6 +83,7 @@ VX_API_ENTRY vx_status VX_API_CALL vxSendUserEvent(vx_context context, vx_uint32 event_info.user_event.user_event_parameter = parameter; status = context->event_queue.push(VX_EVENT_USER, id, &event_info); } + return status; } @@ -95,7 +96,7 @@ VX_API_ENTRY vx_status VX_API_CALL vxWaitEvent( if (Context::isValidContext(context) == vx_false_e) { VX_PRINT(VX_ZONE_ERROR,"context is invalid\n"); - status = (vx_status)VX_ERROR_INVALID_REFERENCE; + status = VX_ERROR_INVALID_REFERENCE; } if (VX_SUCCESS == status) @@ -124,7 +125,7 @@ VX_API_ENTRY vx_status VX_API_CALL vxRegisterEvent( if (vx_false_e == Reference::isValidReference(ref)) { VX_PRINT(VX_ZONE_ERROR, "ref is invalid\n"); - status = (vx_status)VX_ERROR_INVALID_REFERENCE; + status = VX_ERROR_INVALID_REFERENCE; } if (VX_SUCCESS == status) @@ -152,6 +153,7 @@ VX_API_ENTRY vx_status VX_API_CALL vxRegisterEvent( // Only some event types are allowed per spec switch (type) { + case VX_EVENT_GRAPH_PARAMETER_CONSUMED: case VX_EVENT_GRAPH_COMPLETED: case VX_EVENT_NODE_COMPLETED: case VX_EVENT_NODE_ERROR: diff --git a/framework/src/vx_graph.cpp b/framework/src/vx_graph.cpp index eef05f3d..bb5187fb 100644 --- a/framework/src/vx_graph.cpp +++ b/framework/src/vx_graph.cpp @@ -13,12 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -#include "vx_internal.h" #include "vx_graph.h" -static vx_value_set_t graph_queue[10]; -static vx_size numGraphsQueued = 0ul; +#include +#include + +#include "vx_internal.h" /******************************************************************************/ /* STATIC FUNCTIONS */ @@ -242,7 +242,7 @@ reverify(vx_false_e), lock(), parameters(), numParams(0), -should_serialize(vx_false_e), +shouldSerialize(vx_false_e), parentGraph(nullptr), delays() { @@ -1644,6 +1644,85 @@ vx_bool Graph::postprocessOutput(vx_uint32 n, vx_uint32 p, vx_reference* vref, v return vx_true_e; } /* postprocessOutput() */ +vx_status Graph::pipelineValidateRefsList( + const vx_graph_parameter_queue_params_t graph_parameters_queue_param) +{ + vx_status status = VX_SUCCESS; + vx_meta_format meta_base = nullptr, meta = nullptr; + vx_uint32 i; + + if (nullptr != graph_parameters_queue_param.refs_list[0]) + { + meta_base = vxCreateMetaFormat(graph_parameters_queue_param.refs_list[0]->context); + status = vxSetMetaFormatFromReference(meta_base, graph_parameters_queue_param.refs_list[0]); + } + + if ( (VX_SUCCESS == status) + && (nullptr != meta_base) ) + { + for (i = 1; i < graph_parameters_queue_param.refs_list_size; i++) + { + if (nullptr != graph_parameters_queue_param.refs_list[i]) + { + meta = vxCreateMetaFormat(graph_parameters_queue_param.refs_list[i]->context); + + if (nullptr != meta) + { + status = vxSetMetaFormatFromReference(meta, graph_parameters_queue_param.refs_list[i]); + } + else + { + status = VX_FAILURE; + VX_PRINT(VX_ZONE_ERROR, "Meta Format is NULL\n"); + } + + if (VX_SUCCESS == status) + { + if (graph_parameters_queue_param.refs_list[0]->type == + graph_parameters_queue_param.refs_list[i]->type) + { + if (vx_true_e != MetaFormat::isMetaFormatEqual(meta_base, meta, graph_parameters_queue_param.refs_list[0]->type)) + { + status = VX_ERROR_INVALID_PARAMETERS; + VX_PRINT(VX_ZONE_ERROR, "Invalid meta data of reference list!\n"); + } + } + } + + if (Reference::isValidReference(meta, VX_TYPE_META_FORMAT) == vx_true_e) + { + status |= vxReleaseMetaFormat(&meta); + if (VX_SUCCESS != status) + { + VX_PRINT(VX_ZONE_ERROR, "Failed to release meta format object \n"); + } + } + + if (VX_SUCCESS != status) + { + break; + } + } + else + { + status = VX_ERROR_INVALID_PARAMETERS; + VX_PRINT(VX_ZONE_ERROR, "Invalid graph parameter ref list!\n"); + } + } + } + + if (Reference::isValidReference(meta_base, VX_TYPE_META_FORMAT) == vx_true_e) + { + status |= vxReleaseMetaFormat(&meta_base); + if (VX_SUCCESS != status) + { + VX_PRINT(VX_ZONE_ERROR, "Failed to release meta format object \n"); + } + } + + return status; +} + void Graph::destruct() { while (numNodes) @@ -1982,7 +2061,7 @@ VX_API_ENTRY vx_status VX_API_CALL vxVerifyGraph(vx_graph graph) else /* old style validators */ { VX_PRINT(VX_ZONE_GRAPH, "Using old style validators\n"); - vx_meta_format meta = 0; + vx_meta_format metas[VX_INT_MAX_PARAMS] = {nullptr}; /* first pass for inputs */ for (p = 0; p < graph->nodes[n]->kernel->signature.num_parameters; p++) @@ -2018,12 +2097,15 @@ VX_API_ENTRY vx_status VX_API_CALL vxVerifyGraph(vx_graph graph) if (graph->nodes[n]->kernel->signature.directions[p] == VX_OUTPUT) { vx_status output_validation_status = VX_SUCCESS; - if (graph->setupOutput(n, p, &vref, &meta, &status, &num_errors) == vx_false_e) + if (graph->setupOutput(n, p, &vref, &metas[p], &status, &num_errors) == + vx_false_e) break; - output_validation_status = graph->nodes[n]->kernel->validate_output((vx_node)graph->nodes[n], p, meta); + output_validation_status = graph->nodes[n]->kernel->validate_output( + (vx_node)graph->nodes[n], p, metas[p]); if (output_validation_status == VX_SUCCESS) { - if (graph->postprocessOutput(n, p, &vref, meta, &status, &num_errors) == vx_false_e) + if (graph->postprocessOutput(n, p, &vref, metas[p], &status, + &num_errors) == vx_false_e) { break; } @@ -2040,8 +2122,14 @@ VX_API_ENTRY vx_status VX_API_CALL vxVerifyGraph(vx_graph graph) } } } - if (meta) - vxReleaseMetaFormat(&meta); + + for (p = 0; p < dimof(metas); p++) + { + if (metas[p]) + { + vxReleaseMetaFormat(&metas[p]); + } + } } } @@ -2202,6 +2290,15 @@ VX_API_ENTRY vx_status VX_API_CALL vxVerifyGraph(vx_graph graph) /* if the parameter is referenced elsewhere */ if (vxCheckWriteDependency(graph->nodes[n]->parameters[p], graph->nodes[n1]->parameters[p1])) { + /* @TODO: this was added by AI; deep dive this logic */ + vx_reference refA = graph->nodes[n]->parameters[p]; + vx_reference refB = graph->nodes[n1]->parameters[p1]; + if (refA->type == refB->type && refA->delay && refB->delay && + refA->delay == refB->delay) + { + /* skip delay slot dependency for head node detection */ + continue; + } VX_PRINT(VX_ZONE_GRAPH,"\tnodes[%u].parameter[%u] referenced in nodes[%u].parameter[%u]\n", n,p,n1,p1); isAHead = vx_false_e; /* this will cause all the loops to break too. */ } @@ -2423,6 +2520,59 @@ static vx_status vxExecuteGraph(vx_graph graph, vx_uint32 depth) { return VX_ERROR_INVALID_REFERENCE; } + +#ifdef OPENVX_USE_PIPELINING + // Dequeue graph parameters if pipelining is enabled + if (graph->scheduleMode == VX_GRAPH_SCHEDULE_MODE_QUEUE_AUTO || + graph->scheduleMode == VX_GRAPH_SCHEDULE_MODE_QUEUE_MANUAL) + { + for (vx_uint32 i = 0; i < graph->numEnqueableParams; i++) + { + auto& paramQueue = graph->parameters[i].queue; + vx_reference ref; + + // Dequeue a reference from the "ready" queue + if (paramQueue.peekReady(ref)) + { + vx_node node = graph->parameters[i].node; + vx_uint32 param_index = graph->parameters[i].index; + // Save the old reference for this graph parameter + vx_reference old_ref = node->parameters[param_index]; + + // Update ALL node parameters that point to this old reference + if (node->parameters[param_index] != ref) + { + for (vx_uint32 n = 0; n < graph->numNodes; n++) + { + for (vx_uint32 p = 0; p < graph->nodes[n]->kernel->signature.num_parameters; + p++) + { + // Assign the dequeued reference to the corresponding node parameter + if (graph->nodes[n]->parameters[p] == old_ref) + { + graph->context->removeReference(graph->nodes[n]->parameters[p]); + ref->incrementReference(VX_INTERNAL); + graph->nodes[n]->parameters[p] = ref; + } + } + } + } + + VX_PRINT(VX_ZONE_GRAPH, + "Dequeued reference for graph parameter %u and \ + assigned to node parameter %u\n", + i, param_index); + } + else + { + VX_PRINT(VX_ZONE_ERROR, "Failed to dequeue reference for graph parameter %u\n", i); + std::cerr << "Failed to dequeue reference for graph parameter " << i << std::endl; + return VX_ERROR_NO_RESOURCES; + } + } + } +#endif + if (graph->verified == vx_false_e) { status = vxVerifyGraph((vx_graph)graph); @@ -2442,6 +2592,7 @@ static vx_status vxExecuteGraph(vx_graph graph, vx_uint32 depth) { Osal::startCapture(&graph->perf); } + /* initialize the next_nodes as the graph heads */ memcpy(next_nodes, graph->heads, graph->numHeads * sizeof(vx_uint32)); numNext = graph->numHeads; @@ -2459,10 +2610,10 @@ static vx_status vxExecuteGraph(vx_graph graph, vx_uint32 depth) { vx_uint32 t = graph->nodes[next_nodes[n]]->affinity; #if defined(OPENVX_USE_SMP) - if (depth == 1 && graph->should_serialize == vx_false_e) + if (depth == 1 && graph->shouldSerialize == vx_false_e) { vx_value_set_t *work = &workitems[n]; - vx_target target = &graph->context->targets[t]; + vx_target target = graph->context->targets[t]; vx_node node = graph->nodes[next_nodes[n]]; work->v1 = (vx_value_t)target; work->v2 = (vx_value_t)node; @@ -2509,54 +2660,54 @@ static vx_status vxExecuteGraph(vx_graph graph, vx_uint32 depth) } } - if (action == VX_ACTION_CONTINUE) +#ifdef OPENVX_USE_PIPELINING + /* Raise a node completed event. */ + vx_event_info_t event_info; + event_info.node_completed.graph = graph; + event_info.node_completed.node = node; + if (graph->context->event_queue.isEnabled() && + VX_SUCCESS != graph->context->event_queue.push(VX_EVENT_NODE_COMPLETED, 0, + &event_info, + (vx_reference)node)) + { + VX_PRINT(VX_ZONE_ERROR, "Failed to push node completed event for node %s\n", + node->kernel->name); + } + + /* Raise a graph parameter consumed event */ + for (vx_uint32 gp = 0; gp < graph->numEnqueableParams; gp++) { - if (graph->context->event_queue.isEnabled()) + vx_node param_node = graph->parameters[gp].node; + vx_uint32 param_index = graph->parameters[gp].index; + + /* If this node just executed and consumed a graph parameter */ + if (param_node == node) { - // Raise a node completed event. - vx_event_info_t event_info; - event_info.node_completed.graph = graph; - event_info.node_completed.node = node; - if (VX_SUCCESS != - graph->context->event_queue.push(VX_EVENT_NODE_COMPLETED, 0, - &event_info, (vx_reference)node)) - { - VX_PRINT(VX_ZONE_ERROR, - "Failed to push node completed event for node %s\n", - node->kernel->name); - } + vx_event_info_t event_info = {}; + event_info.graph_parameter_consumed.graph = graph; + event_info.graph_parameter_consumed.graph_parameter_index = param_index; - for (vx_uint32 gp = 0; gp < graph->numParams; gp++) - { - vx_node param_node = graph->parameters[gp].node; - vx_uint32 param_index = graph->parameters[gp].index; + (void)graph->parameters[gp].queue.moveReadyToDone(); - // If this node just executed and consumed a graph parameter - if (param_node == node) - { - vx_event_info_t event_info = {}; - event_info.graph_parameter_consumed.graph = graph; - event_info.graph_parameter_consumed.graph_parameter_index = - param_index; - - if (VX_SUCCESS != graph->context->event_queue.push( - VX_EVENT_GRAPH_PARAMETER_CONSUMED, 0, - &event_info, (vx_reference)graph)) - { - VX_PRINT( - VX_ZONE_ERROR, - "Failed to push graph parameter consumed event for " - "graph %p, param %u\n", - graph, gp); - } - } + if (graph->context->event_queue.isEnabled() && + param_node->kernel->signature.directions[param_index] == VX_INPUT && + VX_SUCCESS != graph->context->event_queue.push( + VX_EVENT_GRAPH_PARAMETER_CONSUMED, 0, &event_info, + (vx_reference)graph)) + { + VX_PRINT(VX_ZONE_ERROR, + "Failed to push graph parameter consumed event for " + "graph %p, param %u\n", + graph, gp); } } } +#endif if (action == VX_ACTION_ABANDON) { - // Raise a node error event. +#ifdef OPENVX_USE_PIPELINING + /* Raise a node error event. */ vx_event_info_t event_info; event_info.node_error.graph = graph; event_info.node_error.node = node; @@ -2569,6 +2720,7 @@ static vx_status vxExecuteGraph(vx_graph graph, vx_uint32 depth) VX_PRINT(VX_ZONE_ERROR, "Failed to push node error event for node %s\n", node->kernel->name); } +#endif break; } } @@ -2581,7 +2733,7 @@ static vx_status vxExecuteGraph(vx_graph graph, vx_uint32 depth) } #if defined(OPENVX_USE_SMP) - if (depth == 1 && graph->should_serialize == vx_false_e) + if (depth == 1 && graph->shouldSerialize == vx_false_e) { if (Osal::issueThreadpool(graph->context->workers, workitems, numNext) == vx_true_e) { @@ -2639,6 +2791,19 @@ static vx_status vxExecuteGraph(vx_graph graph, vx_uint32 depth) } VX_PRINT(VX_ZONE_GRAPH,"Process returned status %d\n", status); + +#ifdef OPENVX_USE_PIPELINING + /* Raise a graph completed event. */ + vx_event_info_t event_info; + event_info.graph_completed.graph = graph; + if (graph->context->event_queue.isEnabled() && + VX_SUCCESS != graph->context->event_queue.push(VX_EVENT_GRAPH_COMPLETED, 0, &event_info, + (vx_reference)graph)) + { + VX_PRINT(VX_ZONE_ERROR, "Failed to push graph completed event for graph %p\n", graph); + } +#endif + // Report the performance of the graph execution. if (context->perf_enabled) { @@ -2659,15 +2824,6 @@ static vx_status vxExecuteGraph(vx_graph graph, vx_uint32 depth) if (status == VX_SUCCESS) { graph->state = VX_GRAPH_STATE_COMPLETED; - // Raise a graph completed event. - vx_event_info_t event_info; - event_info.graph_completed.graph = graph; - if (graph->context->event_queue.isEnabled() && - VX_SUCCESS != graph->context->event_queue.push(VX_EVENT_GRAPH_COMPLETED, 0, &event_info, - (vx_reference)graph)) - { - VX_PRINT(VX_ZONE_ERROR, "Failed to push graph completed event for graph %p\n", graph); - } } else { @@ -2694,20 +2850,41 @@ VX_API_ENTRY vx_status VX_API_CALL vxScheduleGraph(vx_graph graph) } } - if (Osal::semTryWait(&graph->lock) == vx_true_e) +#ifdef OPENVX_USE_PIPELINING + vx_uint32 numParams = std::min(graph->numParams, graph->numEnqueableParams); + vx_size batch_depth = 1u; + if (graph->scheduleMode == VX_GRAPH_SCHEDULE_MODE_QUEUE_MANUAL) { + batch_depth = UINT32_MAX; // Use UINT32_MAX to indicate no limit on batch depth + for (vx_uint32 i = 0; i < numParams; ++i) + { + batch_depth = std::min(batch_depth, graph->parameters[i].queue.readyQueueSize()); + } + + if (batch_depth == 0 || batch_depth == UINT32_MAX) + { + // Not enough data to schedule a batch + return VX_ERROR_NOT_SUFFICIENT; + } + } + + for (vx_uint32 i = 0; i < batch_depth; i++) +#endif + // if (Osal::semTryWait(&graph->lock) == vx_true_e) + { + Osal::semTryWait(&graph->lock); vx_sem_t* p_graph_queue_lock = graph->context->p_global_lock; vx_uint32 q = 0u; vx_value_set_t *pq = nullptr; Osal::semWait(p_graph_queue_lock); /* acquire a position in the graph queue */ - for (q = 0; q < dimof(graph_queue); q++) + for (q = 0; q < dimof(graph->context->graph_queue); q++) { - if (graph_queue[q].v1 == 0) + if (graph->context->graph_queue[q].v1 == 0) { - pq = &graph_queue[q]; - numGraphsQueued++; + pq = &graph->context->graph_queue[q]; + graph->context->numGraphsQueued++; break; } } @@ -2716,6 +2893,11 @@ VX_API_ENTRY vx_status VX_API_CALL vxScheduleGraph(vx_graph graph) { memset(pq, 0, sizeof(vx_value_set_t)); pq->v1 = (vx_value_t)graph; + +#ifdef OPENVX_USE_PIPELINING + /* Increment the schedule count */ + graph->scheduleCount++; +#endif /* now add the graph to the queue */ VX_PRINT(VX_ZONE_GRAPH,"Writing graph=" VX_FMT_REF ", status=%d\n",graph, status); if (Osal::writeQueue(&graph->context->proc.input, pq) == vx_true_e) @@ -2725,6 +2907,7 @@ VX_API_ENTRY vx_status VX_API_CALL vxScheduleGraph(vx_graph graph) else { Osal::semPost(&graph->lock); + VX_PRINT(VX_ZONE_ERROR, "Failed to write graph to queue"); status = VX_ERROR_NO_RESOURCES; } } @@ -2734,11 +2917,12 @@ VX_API_ENTRY vx_status VX_API_CALL vxScheduleGraph(vx_graph graph) status = VX_ERROR_NO_RESOURCES; } } - else - { - /* graph is already scheduled */ - status = VX_ERROR_GRAPH_SCHEDULED; - } + // else + // { + // /* graph is already scheduled */ + // VX_PRINT(VX_ZONE_WARNING, "Graph is already scheduled!\n"); + // // status = VX_ERROR_GRAPH_SCHEDULED; + // } return status; } @@ -2752,7 +2936,8 @@ VX_API_ENTRY vx_status VX_API_CALL vxWaitGraph(vx_graph graph) return VX_ERROR_INVALID_REFERENCE; } - if (Osal::semTryWait(&graph->lock) == vx_false_e) /* locked */ + if (Osal::semTryWait(&graph->lock) == vx_false_e || + graph->scheduleMode == VX_GRAPH_SCHEDULE_MODE_QUEUE_MANUAL) /* locked */ { vx_sem_t* p_graph_queue_lock = graph->context->p_global_lock; vx_graph g2; @@ -2776,17 +2961,29 @@ VX_API_ENTRY vx_status VX_API_CALL vxWaitGraph(vx_graph graph) vx_uint32 q = 0u; Osal::semWait(p_graph_queue_lock); /* find graph in the graph queue */ - for (q = 0; q < dimof(graph_queue); q++) + for (q = 0; q < dimof(graph->context->graph_queue); q++) { - if (graph_queue[q].v1 == (vx_value_t)graph) + if (graph->context->graph_queue[q].v1 == (vx_value_t)graph) { - graph_queue[q].v1 = 0; - numGraphsQueued--; + graph->context->graph_queue[q].v1 = 0; + graph->context->numGraphsQueued--; break; } } Osal::semPost(p_graph_queue_lock); + +#ifdef OPENVX_USE_PIPELINING + /* Decrement the schedule count */ + graph->scheduleCount--; + /* Unlock the graph only if all scheduled executions are completed */ + if (graph->scheduleCount == 0) + { + Osal::semPost(&graph->lock); + break; + } +#else break; +#endif } else { @@ -2799,7 +2996,7 @@ VX_API_ENTRY vx_status VX_API_CALL vxWaitGraph(vx_graph graph) } else { - status = VX_FAILURE; + // status = VX_FAILURE; Osal::semPost(&graph->lock); /* was free, release */ } diff --git a/framework/src/vx_graph_pipeline.cpp b/framework/src/vx_graph_pipeline.cpp index eb38f160..5087da86 100644 --- a/framework/src/vx_graph_pipeline.cpp +++ b/framework/src/vx_graph_pipeline.cpp @@ -13,15 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -#ifdef OPENVX_USE_PIPELINING - #include -#include #include +#include + +#include #include "vx_internal.h" +#ifdef OPENVX_USE_PIPELINING + VX_API_ENTRY vx_status vxSetGraphScheduleConfig( vx_graph graph, vx_enum graph_schedule_mode, @@ -29,7 +30,94 @@ VX_API_ENTRY vx_status vxSetGraphScheduleConfig( const vx_graph_parameter_queue_params_t graph_parameters_queue_params_list[] ) { - return VX_ERROR_NOT_IMPLEMENTED; + vx_status status = VX_SUCCESS; + + if (vx_true_e != Reference::isValidReference(graph, VX_TYPE_GRAPH)) + { + VX_PRINT(VX_ZONE_ERROR, "Invalid reference\n"); + status = VX_ERROR_INVALID_REFERENCE; + } + + if (VX_SUCCESS == status) + { + if (graph->verified == vx_true_e) + { + VX_PRINT(VX_ZONE_ERROR, "Not supported on verified graph\n"); + status = VX_ERROR_NOT_SUPPORTED; + } + } + + if (VX_SUCCESS == status) + { + if ((graph_schedule_mode < VX_GRAPH_SCHEDULE_MODE_NORMAL) || + (graph_schedule_mode > VX_GRAPH_SCHEDULE_MODE_QUEUE_MANUAL)) + { + VX_PRINT(VX_ZONE_ERROR, "Invalid graph schedule mode\n"); + status = VX_ERROR_INVALID_PARAMETERS; + } + } + + if (VX_SUCCESS == status) + { + if (graph_parameters_list_size > graph->numParams) + { + VX_PRINT(VX_ZONE_ERROR, + "user parameter list (%d) > number of graph parameters (%d)\n", + graph_parameters_list_size, graph->numParams); + status = VX_ERROR_INVALID_PARAMETERS; + } + /* Set the number of params that can possibly be enqueued */ + graph->numEnqueableParams = graph_parameters_list_size; + } + + if (VX_SUCCESS == status) + { + graph->scheduleMode = (vx_graph_schedule_mode_type_e)graph_schedule_mode; + + if (((graph_schedule_mode == VX_GRAPH_SCHEDULE_MODE_QUEUE_AUTO) || + (graph_schedule_mode == VX_GRAPH_SCHEDULE_MODE_QUEUE_MANUAL))) + { + for (vx_uint32 i = 0; (i < graph_parameters_list_size) && (status == VX_SUCCESS); i++) + { + if ((graph_parameters_queue_params_list[i].refs_list == nullptr) || + (graph_parameters_queue_params_list[i].graph_parameter_index >= + graph->numParams) || + (graph_parameters_queue_params_list[i].refs_list_size >= + VX_INT_MAX_QUEUE_DEPTH)) + { + VX_PRINT(VX_ZONE_ERROR, + "Invalid parameters: graph_parameters_queue_params_list at index " + "%d is NULL\n", + i); + status = VX_ERROR_INVALID_PARAMETERS; + break; + } + + // Validate the refs list types + if (VX_SUCCESS != graph->pipelineValidateRefsList(graph_parameters_queue_params_list[i])) + { + VX_PRINT(VX_ZONE_ERROR, + "Graph parameter refs list at index %d contains " + "inconsistent meta data. Please ensure that all buffers " + "in list contain the same meta data\n", + i); + status = VX_ERROR_INVALID_PARAMETERS; + break; + } + + // Associate refs with the graph parameter + graph->parameters[i].numBufs = graph_parameters_queue_params_list[i].refs_list_size; + graph->parameters[i].type = graph_parameters_queue_params_list[i].refs_list[0]->type; + + for (vx_uint32 buf_id = 0; buf_id < graph->parameters[i].numBufs; buf_id++) + { + graph->parameters[i].refs_list[buf_id] = graph_parameters_queue_params_list[i].refs_list[buf_id]; + } + } + } + } + + return status; } VX_API_ENTRY vx_status VX_API_CALL vxGraphParameterEnqueueReadyRef(vx_graph graph, @@ -37,7 +125,69 @@ VX_API_ENTRY vx_status VX_API_CALL vxGraphParameterEnqueueReadyRef(vx_graph grap vx_reference *refs, vx_uint32 num_refs) { - return VX_ERROR_NOT_IMPLEMENTED; + vx_status status = VX_SUCCESS; + + if (vx_false_e == Reference::isValidReference(graph) || + graph_parameter_index >= graph->numParams || + !refs || + num_refs == 0) + { + status = VX_ERROR_INVALID_PARAMETERS; + } + + if (VX_SUCCESS == status) + { + auto& paramQueue = graph->parameters[graph_parameter_index].queue; + + for (vx_uint32 i = 0; i < num_refs; ++i) + { + // Sanity check to validate the reference metadata + if (!Reference::isValidReference(refs[i], graph->parameters[graph_parameter_index].type)) + { + VX_PRINT(VX_ZONE_ERROR, "Invalid reference metadata for graph parameter %u\n", graph_parameter_index); + status = VX_ERROR_INVALID_PARAMETERS; + } + + if (VX_SUCCESS == status && !paramQueue.enqueuePending(refs[i])) + { + status = VX_ERROR_NO_RESOURCES; + } + } + } + + if (VX_SUCCESS == status) + { + vx_bool readyToSchedule = vx_true_e; + vx_uint32 numParams = std::min(graph->numParams, graph->numEnqueableParams); + + while (readyToSchedule) + { + for (vx_uint32 i = 0; i < numParams; ++i) + { + if (graph->parameters[i].queue.pendingQueueSize() == 0) + { + readyToSchedule = vx_false_e; + break; + } + } + + if (readyToSchedule) + { + for (vx_uint32 i = 0; i < numParams; ++i) + { + graph->parameters[i].queue.movePendingToReady(); + } + + if (graph->scheduleMode == VX_GRAPH_SCHEDULE_MODE_QUEUE_AUTO) + { + /* Schedule the graph */ + status = vxScheduleGraph(graph); + } + } + } + } + + return status; } VX_API_ENTRY vx_status VX_API_CALL vxGraphParameterDequeueDoneRef(vx_graph graph, @@ -46,7 +196,33 @@ VX_API_ENTRY vx_status VX_API_CALL vxGraphParameterDequeueDoneRef(vx_graph graph vx_uint32 max_refs, vx_uint32 *num_refs) { - return VX_ERROR_NOT_IMPLEMENTED; + vx_status status = VX_SUCCESS; + vx_uint32 count = 0; + vx_reference ref; + + if (vx_false_e == Reference::isValidReference(graph) || + graph_parameter_index >= graph->numEnqueableParams || !refs || !num_refs) + { + status = VX_ERROR_INVALID_PARAMETERS; + } + + if (VX_SUCCESS == status) + { + auto& paramQueue = graph->parameters[graph_parameter_index].queue; + + // Block until at least one "done" reference is available + paramQueue.waitForDoneRef(); + + while (count < max_refs && paramQueue.doneQueueSize() > 0 && paramQueue.dequeueDone(ref)) + { + refs[count++] = ref; + } + *num_refs = count; + + status = (count > 0) ? VX_SUCCESS : VX_FAILURE; + } + + return status; } @@ -54,7 +230,21 @@ VX_API_ENTRY vx_status VX_API_CALL vxGraphParameterCheckDoneRef(vx_graph graph, vx_uint32 graph_parameter_index, vx_uint32 *num_refs) { - return VX_ERROR_NOT_IMPLEMENTED; + vx_status status = VX_SUCCESS; + + if (vx_false_e == Reference::isValidReference(graph) || + graph_parameter_index >= graph->numEnqueableParams || !num_refs) + { + status = VX_ERROR_INVALID_PARAMETERS; + } + + if (VX_SUCCESS == status) + { + auto& paramQueue = graph->parameters[graph_parameter_index].queue; + *num_refs = static_cast(paramQueue.doneQueueSize()); + } + + return status; } #endif /* OPENVX_USE_PIPELINING */ diff --git a/framework/src/vx_meta_format.cpp b/framework/src/vx_meta_format.cpp index d676b31c..084b498a 100644 --- a/framework/src/vx_meta_format.cpp +++ b/framework/src/vx_meta_format.cpp @@ -29,6 +29,392 @@ set_valid_rectangle_callback(nullptr) } +vx_bool MetaFormat::isValidMetaFormat(vx_meta_format meta1, vx_meta_format meta2) +{ + vx_bool isValid = vx_false_e; + + if ((Reference::isValidReference(meta1, VX_TYPE_META_FORMAT) == vx_true_e) && + (Reference::isValidReference(meta2, VX_TYPE_META_FORMAT) == vx_true_e)) + { + isValid = vx_true_e; + } + + if (isValid == vx_false_e) + { + VX_PRINT(VX_ZONE_ERROR, "Meta format is invalid!\n"); + } + + return isValid; +} + +vx_bool MetaFormat::isMetaFormatEqual( + vx_meta_format meta1, vx_meta_format meta2, vx_enum ref_type) +{ + vx_bool isEqual = vx_false_e; + + if (vx_true_e == MetaFormat::isValidMetaFormat(meta1, meta2)) + { + switch (ref_type) + { + case VX_TYPE_IMAGE: + isEqual = MetaFormat::isMetaFormatImageEqual(meta1, meta2); + break; + case VX_TYPE_ARRAY: + isEqual = MetaFormat::isMetaFormatArrayEqual(meta1, meta2); + break; + case VX_TYPE_SCALAR: + isEqual = MetaFormat::isMetaFormatScalarEqual(meta1, meta2); + break; + case VX_TYPE_PYRAMID: + isEqual = MetaFormat::isMetaFormatPyramidEqual(meta1, meta2); + break; + case VX_TYPE_MATRIX: + isEqual = MetaFormat::isMetaFormatMatrixEqual(meta1, meta2); + break; + case VX_TYPE_DISTRIBUTION: + isEqual = MetaFormat::isMetaFormatDistributionEqual(meta1, meta2); + break; + case VX_TYPE_CONVOLUTION: + isEqual = MetaFormat::isMetaFormatConvolutionEqual(meta1, meta2); + break; + case VX_TYPE_THRESHOLD: + isEqual = MetaFormat::isMetaFormatThresholdEqual(meta1, meta2); + break; + case VX_TYPE_REMAP: + isEqual = MetaFormat::isMetaFormatRemapEqual(meta1, meta2); + break; + case VX_TYPE_LUT: + isEqual = MetaFormat::isMetaFormatLutEqual(meta1, meta2); + break; + case VX_TYPE_OBJECT_ARRAY: + isEqual = MetaFormat::isMetaFormatObjectArrayEqual(meta1, meta2); + break; + case VX_TYPE_TENSOR: + isEqual = MetaFormat::isMetaFormatTensorEqual(meta1, meta2); + break; +#if defined(OPENVX_USE_USER_DATA_OBJECT) + case VX_TYPE_USER_DATA_OBJECT: + isEqual = MetaFormat::isMetaFormatUserDataObjectEqual(meta1, meta2); + break; +#endif + default: + VX_PRINT(VX_ZONE_ERROR, "invalid ref_type attribute\n"); + break; + } + } + else + { + VX_PRINT(VX_ZONE_ERROR, "Meta format is invalid!\n"); + } + + return isEqual; +} + +vx_bool MetaFormat::isMetaFormatImageEqual( + vx_meta_format meta1, vx_meta_format meta2) +{ + vx_bool is_equal = vx_false_e; + + if (MetaFormat::isValidMetaFormat(meta1, meta2) == vx_true_e ) + { + if ( (meta1->dim.image.width == meta2->dim.image.width) && + (meta1->dim.image.height == meta2->dim.image.height) && + (meta1->dim.image.format == meta2->dim.image.format) ) + { + is_equal = vx_true_e; + } + else + { + VX_PRINT(VX_ZONE_INFO, "Image object meta data are not equivalent!\n"); + } + } + + return is_equal; +} + +vx_bool MetaFormat::isMetaFormatArrayEqual( + vx_meta_format meta1, vx_meta_format meta2) +{ + vx_bool is_equal = vx_false_e; + + if (MetaFormat::isValidMetaFormat(meta1, meta2) == vx_true_e ) + { + if ( (meta1->dim.array.item_type == meta2->dim.array.item_type) && + (meta1->dim.array.capacity == meta2->dim.array.capacity) // && + // (meta1->dim.array.item_size == meta2->dim.array.item_size) + ) + { + is_equal = vx_true_e; + } + else + { + VX_PRINT(VX_ZONE_INFO, "Array object meta data are not equivalent!\n"); + } + } + + return is_equal; +} + +vx_bool MetaFormat::isMetaFormatScalarEqual( + vx_meta_format meta1, vx_meta_format meta2) +{ + vx_bool is_equal = vx_false_e; + + if (MetaFormat::isValidMetaFormat(meta1, meta2) == vx_true_e ) + { + if ( meta1->dim.scalar.type == meta2->dim.scalar.type ) + { + is_equal = vx_true_e; + } + else + { + VX_PRINT(VX_ZONE_INFO, "Scalar object meta data are not equivalent!\n"); + } + } + + return is_equal; +} + +vx_bool MetaFormat::isMetaFormatPyramidEqual( + vx_meta_format meta1, vx_meta_format meta2) +{ + vx_bool is_equal = vx_false_e; + + if (MetaFormat::isValidMetaFormat(meta1, meta2) == vx_true_e ) + { + if ( (meta1->dim.pyramid.width == meta2->dim.pyramid.width) && + (meta1->dim.pyramid.height == meta2->dim.pyramid.height) && + (meta1->dim.pyramid.format == meta2->dim.pyramid.format) && + (meta1->dim.pyramid.levels == meta2->dim.pyramid.levels) && + (meta1->dim.pyramid.scale == meta2->dim.pyramid.scale) ) + { + is_equal = vx_true_e; + } + else + { + VX_PRINT(VX_ZONE_INFO, "Pyramid object meta data are not equivalent!\n"); + } + } + + return is_equal; +} + +vx_bool MetaFormat::isMetaFormatMatrixEqual( + vx_meta_format meta1, vx_meta_format meta2) +{ + vx_bool is_equal = vx_false_e; + + if (MetaFormat::isValidMetaFormat(meta1, meta2) == vx_true_e ) + { + if ( (meta1->dim.matrix.type == meta2->dim.matrix.type) && + (meta1->dim.matrix.rows == meta2->dim.matrix.rows) && + (meta1->dim.matrix.cols == meta2->dim.matrix.cols) // && + // (meta1->dim.matrix.size == meta2->dim.matrix.size) && + // (meta1->dim.matrix.pattern == meta2->dim.matrix.pattern) && + // (meta1->dim.matrix.origin.x == meta2->dim.matrix.origin.x) && + // (meta1->dim.matrix.origin.y == meta2->dim.matrix.origin.y) + ) + { + is_equal = vx_true_e; + } + else + { + VX_PRINT(VX_ZONE_INFO, "Matrix object meta data are not equivalent!\n"); + } + } + + return is_equal; +} + +vx_bool MetaFormat::isMetaFormatDistributionEqual( + vx_meta_format meta1, vx_meta_format meta2) +{ + vx_bool is_equal = vx_false_e; + + if (MetaFormat::isValidMetaFormat(meta1, meta2) == vx_true_e ) + { + if ( (meta1->dim.distribution.bins == meta2->dim.distribution.bins) && + (meta1->dim.distribution.offset == meta2->dim.distribution.offset) && + (meta1->dim.distribution.range == meta2->dim.distribution.range) ) + { + is_equal = vx_true_e; + } + else + { + VX_PRINT(VX_ZONE_INFO, "Distribution object meta data are not equivalent!\n"); + } + } + + return is_equal; +} + +vx_bool MetaFormat::isMetaFormatConvolutionEqual( + vx_meta_format meta1, vx_meta_format meta2) +{ + vx_bool is_equal = vx_false_e; + + if (MetaFormat::isValidMetaFormat(meta1, meta2) == vx_true_e ) + { + // if ( (meta1->dim.convolution.rows == meta2->dim.convolution.rows) && + // (meta1->dim.convolution.cols == meta2->dim.convolution.cols) && + // (meta1->dim.convolution.scale == meta2->dim.convolution.scale) && + // (meta1->dim.convolution.size == meta2->dim.convolution.size) ) + // { + // is_equal = vx_true_e; + // } + // else + // { + // VX_PRINT(VX_ZONE_INFO, "Convolution object meta data are not equivalent!\n"); + // } + } + + return is_equal; +} + +vx_bool MetaFormat::isMetaFormatRemapEqual( + vx_meta_format meta1, vx_meta_format meta2) +{ + vx_bool is_equal = vx_false_e; + + if (MetaFormat::isValidMetaFormat(meta1, meta2) == vx_true_e ) + { + if ( (meta1->dim.remap.src_width == meta2->dim.remap.src_width) && + (meta1->dim.remap.src_height == meta2->dim.remap.src_height) && + (meta1->dim.remap.dst_width == meta2->dim.remap.dst_width) && + (meta1->dim.remap.dst_height == meta2->dim.remap.dst_height) ) + { + is_equal = vx_true_e; + } + else + { + VX_PRINT(VX_ZONE_INFO, "Remap object meta data are not equivalent!\n"); + } + } + + return is_equal; +} + +vx_bool MetaFormat::isMetaFormatThresholdEqual( + vx_meta_format meta1, vx_meta_format meta2) +{ + vx_bool is_equal = vx_false_e; + + if (MetaFormat::isValidMetaFormat(meta1, meta2) == vx_true_e ) + { + if ( meta1->dim.threshold.type == meta2->dim.threshold.type ) + { + is_equal = vx_true_e; + } + else + { + VX_PRINT(VX_ZONE_INFO, "Threshold object meta data are not equivalent!\n"); + } + } + + return is_equal; +} + +vx_bool MetaFormat::isMetaFormatObjectArrayEqual( + vx_meta_format meta1, vx_meta_format meta2) +{ + vx_bool is_equal = vx_false_e; + + if (MetaFormat::isValidMetaFormat(meta1, meta2) == vx_true_e ) + { + if ( (meta1->dim.object_array.item_type == meta2->dim.object_array.item_type) && + (meta1->dim.object_array.num_items == meta2->dim.object_array.num_items) ) + { + is_equal = vx_true_e; + } + else + { + VX_PRINT(VX_ZONE_INFO, "Object Array object meta data are not equivalent!\n"); + } + } + + return is_equal; +} + +vx_bool MetaFormat::isMetaFormatLutEqual( + vx_meta_format meta1, vx_meta_format meta2) +{ + vx_bool is_equal = vx_false_e; + + if (MetaFormat::isValidMetaFormat(meta1, meta2) == vx_true_e ) + { + if ( (meta1->dim.lut.type == meta2->dim.lut.type) && + (meta1->dim.lut.count == meta2->dim.lut.count) ) + { + is_equal = vx_true_e; + } + else + { + VX_PRINT(VX_ZONE_INFO, "LUT object meta data are not equivalent!\n"); + } + } + + return is_equal; +} + +vx_bool MetaFormat::isMetaFormatTensorEqual( + vx_meta_format meta1, vx_meta_format meta2) +{ + vx_bool is_equal = vx_false_e; + vx_uint32 i; + + if (MetaFormat::isValidMetaFormat(meta1, meta2) == vx_true_e ) + { + if ( (meta1->dim.tensor.number_of_dimensions == meta2->dim.tensor.number_of_dimensions) && + (meta1->dim.tensor.data_type == meta2->dim.tensor.data_type) && + (meta1->dim.tensor.fixed_point_position == meta2->dim.tensor.fixed_point_position) // && + // (meta1->dim.tensor.scaling_divisor == meta2->dim.tensor.scaling_divisor) && + // (meta1->dim.tensor.scaling_divisor_fixed_point_position == meta2->dim.tensor.scaling_divisor_fixed_point_position) + ) + { + for (i = 0; i < meta1->dim.tensor.number_of_dimensions; i++) + { + if ((meta1->dim.tensor.dimensions[i] != meta2->dim.tensor.dimensions[i]) // || + // (meta1->dim.tensor.strides[i] != meta2->dim.tensor.strides[i]) + ) + { + break; + } + } + if (i == meta1->dim.tensor.number_of_dimensions) + { + is_equal = vx_true_e; + } + } + else + { + VX_PRINT(VX_ZONE_INFO, "Tensor object meta data are not equivalent!\n"); + } + } + + return is_equal; +} + +vx_bool MetaFormat::isMetaFormatUserDataObjectEqual( + vx_meta_format meta1, vx_meta_format meta2) +{ + vx_bool is_equal = vx_false_e; + + if (MetaFormat::isValidMetaFormat(meta1, meta2) == vx_true_e ) + { + if ( (meta1->dim.user_data_object.size == meta2->dim.user_data_object.size) && + ( 0 == strncmp(meta1->dim.user_data_object.type_name, meta2->dim.user_data_object.type_name, VX_MAX_REFERENCE_NAME) ) ) + { + is_equal = vx_true_e; + } + else + { + VX_PRINT(VX_ZONE_INFO, "User data object meta data are not equivalent!\n"); + } + } + + return is_equal; +} + /******************************************************************************/ /* PUBLIC INTERFACE */ /******************************************************************************/ diff --git a/framework/src/vx_node.cpp b/framework/src/vx_node.cpp index b790dbcd..7c0857a5 100644 --- a/framework/src/vx_node.cpp +++ b/framework/src/vx_node.cpp @@ -99,8 +99,7 @@ void Node::destruct() VX_PRINT(VX_ZONE_ERROR, "Internal error removing delay association\n"); } } - Reference::releaseReference(&ref, ref->type, VX_INTERNAL, nullptr); - parameters[p] = nullptr; + Reference::releaseReference(¶meters[p], parameters[p]->type, VX_INTERNAL, nullptr); } } diff --git a/framework/src/vx_reference.cpp b/framework/src/vx_reference.cpp index 93aa9f46..efe444f5 100644 --- a/framework/src/vx_reference.cpp +++ b/framework/src/vx_reference.cpp @@ -149,7 +149,7 @@ vx_uint32 Reference::decrementReference(vx_reftype_e refType) Osal::semWait(&lock); if (refType == VX_INTERNAL || refType == VX_BOTH) { if (internal_count == 0) { - VX_PRINT(VX_ZONE_WARNING, "#### INTERNAL REF COUNT IS ALREADY ZERO!!! " VX_FMT_REF " type:%08x #####\n", this, type); + VX_PRINT(VX_ZONE_ERROR, "#### INTERNAL REF COUNT IS ALREADY ZERO!!! " VX_FMT_REF " type:%08x #####\n", this, type); DEBUG_BREAK(); } else { internal_count--; @@ -158,7 +158,7 @@ vx_uint32 Reference::decrementReference(vx_reftype_e refType) if (refType == VX_EXTERNAL || refType == VX_BOTH) { if (external_count == 0) { - VX_PRINT(VX_ZONE_WARNING, "#### EXTERNAL REF COUNT IS ALREADY ZERO!!! " VX_FMT_REF " type:%08x #####\n", this, type); + VX_PRINT(VX_ZONE_ERROR, "#### EXTERNAL REF COUNT IS ALREADY ZERO!!! " VX_FMT_REF " type:%08x #####\n", this, type); DEBUG_BREAK(); } else @@ -361,9 +361,14 @@ vx_status Reference::releaseReference(vx_reference* r, { vx_status status = VX_SUCCESS; - if (nullptr != r && - Reference::isValidReference(*r, type) == vx_true_e && - type == (*r)->type) + if (nullptr == r || + Reference::isValidReference(*r, type) == vx_false_e || + type != (*r)->type) + { + status = VX_ERROR_INVALID_REFERENCE; + } + + if (VX_SUCCESS == status) { vx_reference ref = *r; if (ref->decrementReference(reftype) == 0) @@ -380,15 +385,15 @@ vx_status Reference::releaseReference(vx_reference* r, if (ref->context->removeReference(ref) == vx_false_e) { + VX_PRINT(VX_ZONE_ERROR, "Failed to remove reference %p from context\n", ref); status = VX_FAILURE; - return status; } } - *r = nullptr; } - else + + if (VX_SUCCESS == status) { - status = VX_ERROR_INVALID_REFERENCE; + *r = nullptr; } return status; diff --git a/include/COREVX/circular_queue.hpp b/include/COREVX/circular_queue.hpp new file mode 100644 index 00000000..c20ddc63 --- /dev/null +++ b/include/COREVX/circular_queue.hpp @@ -0,0 +1,115 @@ +/** + * @file circular_queue.hpp + * @brief Circular queue implementation + * @version 0.1 + * @date 2025-05-13 + * + * @copyright Copyright (c) 2025 + * + */ +#include +#include +#include + +/** + * @brief Circular queue implementation + * + * @tparam T Type of elements in the queue + * @tparam MaxDepth Maximum depth of the queue + * @ingroup group_int_corevx + */ +template +class CircularQueue +{ +public: + /** + * @brief Construct a new Circular Queue object + * + */ + CircularQueue() : head_(0), tail_(0) {} + + /** + * @brief Enqueue an element into the queue + * + * @param ref Element to enqueue + * @return true if successful, false if the queue is full + */ + bool enqueue(const T& ref) + { + size_t tail = tail_.load(std::memory_order_relaxed); + size_t next_tail = (tail + 1) % MaxDepth; + if (next_tail == head_.load(std::memory_order_acquire)) + { + return false; // full + } + buffer_[tail] = ref; + tail_.store(next_tail, std::memory_order_release); + return true; + } + + /** + * @brief Dequeue an element from the queue + * + * @param out Reference to store the dequeued element + * @return true if successful, false if the queue is empty + */ + bool dequeue(T& out) + { + size_t head = head_.load(std::memory_order_relaxed); + if (head == tail_.load(std::memory_order_acquire)) + { + return false; // empty + } + out = buffer_[head]; + head_.store((head + 1) % MaxDepth, std::memory_order_release); + return true; + } + + /** + * @brief Peek at the element at the front of the queue without removing it + * + * @param out Reference to store the peeked element + * @return true if successful, false if the queue is empty + */ + bool peek(T& out) const + { + size_t head = head_.load(std::memory_order_relaxed); + if (head == tail_.load(std::memory_order_acquire)) + { + return false; // empty + } + out = buffer_[head]; + return true; + } + + /** + * @brief Get the size of the queue + * + * @return std::size_t Size of the queue + */ + std::size_t size() const + { + size_t head = head_.load(std::memory_order_acquire); + size_t tail = tail_.load(std::memory_order_acquire); + return (tail + MaxDepth - head) % MaxDepth; + } + + /** + * @brief Check if the queue is empty + * + * @return true if empty, false otherwise + */ + bool empty() const { return size() == 0; } + + /** + * @brief Check if the queue is full + * + * @return true if full, false otherwise + */ + bool full() const { return ((tail_.load(std::memory_order_acquire) + 1) % MaxDepth) == \ + head_.load(std::memory_order_acquire); } + +private: + std::array buffer_; + std::atomic head_, tail_; +}; \ No newline at end of file diff --git a/include/COREVX/execution_queue.hpp b/include/COREVX/execution_queue.hpp new file mode 100644 index 00000000..a3f6b62a --- /dev/null +++ b/include/COREVX/execution_queue.hpp @@ -0,0 +1,229 @@ +/** + * @file execution_queue.hpp + * @brief Execution queue implementation + * @version 0.1 + * @date 2025-05-16 + * + * @copyright Copyright (c) 2025 + * + */ +#include +#include + +#include "circular_queue.hpp" + +template +class ExecutionQueue +{ +public: + /** + * @brief Enqueue an item into the "pending" queue + * + * @param item The item to enqueue + * @return true if successful, false if the queue is full + */ + bool enqueuePending(const T& item) + { + std::unique_lock lock(mutex_); + return pending_queue_.enqueue(item); + } + + /** + * @brief Dequeue an item from the "pending" queue + * + * @param item Reference to store the dequeued item + * @return true if successful, false if the queue is empty + */ + bool dequeuePending(T& item) + { + std::unique_lock lock(mutex_); + return pending_queue_.dequeue(item); + } + + /** + * @brief Move an item from the "pending" queue to the "ready" queue + * + * @return true if successful, false if the "pending" queue is empty or the "ready" queue is + * full + */ + bool movePendingToReady() + { + std::unique_lock lock(mutex_); + T item; + if (!pending_queue_.dequeue(item)) + { + return false; // "Pending" queue is empty + } + if (!ready_queue_.enqueue(item)) + { + return false; // "Ready" queue is full + } + return true; + } + + /** + * @brief Enqueue an item into the "ready" queue + * + * @param item The item to enqueue + * @return true if successful, false if the queue is full + */ + bool enqueueReady(const T& item) + { + std::unique_lock lock(mutex_); + return ready_queue_.enqueue(item); + } + + /** + * @brief Dequeue an item from the "ready" queue for processing + * + * @param item Reference to store the dequeued item + * @return true if successful, false if the queue is empty + */ + bool dequeueReady(T& item) + { + std::unique_lock lock(mutex_); + return ready_queue_.dequeue(item); + } + + /** + * @brief Peek at the item at the front of the "ready" queue without removing it + * + * @param item Reference to store the peeked item + * @return true if successful, false if the queue is empty + */ + bool peekReady(T& item) const + { + std::unique_lock lock(mutex_); + return ready_queue_.peek(item); + } + + /** + * @brief Enqueue an item into the "done" queue after processing + * + * @param item The item to enqueue + * @return true if successful, false if the queue is full + */ + bool enqueueDone(const T& item) + { + std::unique_lock lock(mutex_); + bool ans = done_queue_.enqueue(item); + cond_var_.notify_one(); + return ans; + } + + /** + * @brief Dequeue an item from the "done" queue + * + * @param item Reference to store the dequeued item + * @return true if successful, false if the queue is empty + */ + bool dequeueDone(T& item) + { + std::unique_lock lock(mutex_); + return done_queue_.dequeue(item); + } + + /** + * @brief Block until at least one item is available in the "done" queue + */ + void waitForDoneRef() + { + std::unique_lock lock(mutex_); + cond_var_.wait_for(lock, std::chrono::milliseconds(timeout_ms_), + [this]() { return !done_queue_.empty(); }); + } + + /** + * @brief Move an item from the "ready" queue to the "done" queue + * + * @return true if successful, false if the "ready" queue is empty or the "done" queue is full + */ + bool moveReadyToDone() + { + std::unique_lock lock(mutex_); + T item; + if (!ready_queue_.dequeue(item)) + { + return false; // "Ready" queue is empty + } + if (!done_queue_.enqueue(item)) + { + return false; // "Done" queue is full + } + cond_var_.notify_one(); // Notify that an item is available in the "done" queue + return true; + } + + /** + * @brief Check if the "pending" queue is empty + * + * @return true if empty, false otherwise + */ + bool isPendingEmpty() const + { + std::unique_lock lock(mutex_); + return pending_queue_.empty(); + } + + /** + * @brief Check if the "ready" queue is empty + * + * @return true if empty, false otherwise + */ + bool isReadyEmpty() const + { + std::unique_lock lock(mutex_); + return ready_queue_.empty(); + } + + /** + * @brief Check if the "done" queue is empty + * + * @return true if empty, false otherwise + */ + bool isDoneEmpty() const + { + std::unique_lock lock(mutex_); + return done_queue_.empty(); + } + + /** + * @brief Get the size of the "pending" queue + * + * @return std::size_t Size of the "pending" queue + */ + std::size_t pendingQueueSize() const + { + std::unique_lock lock(mutex_); + return pending_queue_.size(); + } + + /** + * @brief Get the size of the "ready" queue + * + * @return std::size_t Size of the "ready" queue + */ + std::size_t readyQueueSize() const + { + std::unique_lock lock(mutex_); + return ready_queue_.size(); + } + + /** + * @brief Get the size of the "done" queue + * + * @return std::size_t Size of the "done" queue + */ + std::size_t doneQueueSize() const + { + std::unique_lock lock(mutex_); + return done_queue_.size(); + } +private: + static constexpr int timeout_ms_ = 10000; + CircularQueue pending_queue_; + CircularQueue ready_queue_; + CircularQueue done_queue_; + mutable std::mutex mutex_; + std::condition_variable cond_var_; +}; \ No newline at end of file diff --git a/include/VX/vx_corevx_ext.h b/include/VX/vx_corevx_ext.h index 0c4ffe6c..30926449 100644 --- a/include/VX/vx_corevx_ext.h +++ b/include/VX/vx_corevx_ext.h @@ -112,23 +112,23 @@ VX_API_ENTRY vx_status VX_API_CALL vxSetObjectArrayItem(vx_object_array arr, vx_ VX_API_ENTRY vx_status VX_API_CALL vxImportGraphFromDot(vx_graph graph, vx_char dotfile[], vx_bool acceptData); /* ENABLED FEATURES IN COREVX ONLY */ -#define OPENVX_USE_USER_DATA_OBJECT -#define OPENVX_USE_IX -#define OPENVX_USE_XML -#define OPENVX_USE_S16 -#define OPENVX_USE_OPENCL_INTEROP -#define OPENVX_USE_NN -#define OPENVX_USE_NN_16 -#define OPENVX_USE_EVENTS +#define OPENVX_USE_USER_DATA_OBJECT 1 +#define OPENVX_USE_IX 1 +#define OPENVX_USE_XML 1 +#define OPENVX_USE_S16 1 +#define OPENVX_USE_OPENCL_INTEROP 1 +#define OPENVX_USE_NN 1 +#define OPENVX_USE_NN_16 1 +// #define OPENVX_USE_PIPELINING 1 #if defined(__arm__) || defined(__arm64__) -#define OPENVX_USE_TILING -#define OPENVX_KHR_TILING +#define OPENVX_USE_TILING 1 +#define OPENVX_KHR_TILING 1 #define EXPERIMENTAL_USE_VENUM #endif /* defined(__arm__) || defined(__arm64__) */ -#define OPENVX_CONFORMANCE_NNEF_IMPORT -#define OPENVX_CONFORMANCE_NEURAL_NETWORKS +#define OPENVX_CONFORMANCE_NNEF_IMPORT 1 +#define OPENVX_CONFORMANCE_NEURAL_NETWORKS 1 #define EXPERIMENTAL_PLATFORM_SUPPORTS_16_FLOAT #define EXPERIMENTAL_USE_DOT #define EXPERIMENTAL_USE_OPENCL diff --git a/targets/opencl/vx_interface.cpp b/targets/opencl/vx_interface.cpp index e255f73d..76dc0228 100644 --- a/targets/opencl/vx_interface.cpp +++ b/targets/opencl/vx_interface.cpp @@ -123,10 +123,11 @@ extern "C" vx_status vxTargetInit(vx_target target) for (p = 0; p < context->num_platforms; p++) { - err = clGetDeviceIDs(context->platforms[p], CL_DEVICE_TYPE_GPU, - 0, nullptr, &context->num_devices[p]); + err = clGetDeviceIDs(context->platforms[p], CL_DEVICE_TYPE_DEFAULT, 0, nullptr, + &context->num_devices[p]); - err |= clGetDeviceIDs(context->platforms[p], CL_DEVICE_TYPE_GPU, + err |= clGetDeviceIDs( + context->platforms[p], CL_DEVICE_TYPE_DEFAULT, context->num_devices[p] > CL_MAX_DEVICES ? CL_MAX_DEVICES : context->num_devices[p], context->devices[p], nullptr); if (err == CL_SUCCESS) @@ -398,16 +399,20 @@ extern "C" vx_status vxTargetDeinit(vx_target target) if (target->kernels[k]) { target->kernels[k]->decrementReference(VX_INTERNAL); - clReleaseKernel(cl_kernels[k]->kernels[p]); - clReleaseProgram(cl_kernels[k]->program[p]); + if (cl_kernels[k]->kernels[p]) clReleaseKernel(cl_kernels[k]->kernels[p]); + if (cl_kernels[k]->program[p]) clReleaseProgram(cl_kernels[k]->program[p]); } } for (d = 0; d < context->num_devices[p]; d++) { - clReleaseCommandQueue(context->queues[p][d]); + if (context->queues[p][d]) + { + clFinish(context->queues[p][d]); + clReleaseCommandQueue(context->queues[p][d]); + } } - clReleaseContext(context->global[p]); + if (context->global[p]) clReleaseContext(context->global[p]); } } return VX_SUCCESS; diff --git a/test_vx_conformance.sh b/test_vx_conformance.sh index d899c2b3..def1966b 100755 --- a/test_vx_conformance.sh +++ b/test_vx_conformance.sh @@ -4,12 +4,16 @@ set -e TARGET_DIR="${GITHUB_WORKSPACE:-$PWD}" echo "Running from TARGET_DIR=$TARGET_DIR" +if [ -d "$TARGET_DIR/cts/build" ]; then + rm -rf $TARGET_DIR/cts/build +fi + mkdir -p $TARGET_DIR/cts/build # Set the environment variables export OPENVX_DIR=$TARGET_DIR -export DYLD_LIBRARY_PATH=$DYLD_LIBRARY_PATH:$OPENVX_DIR/bazel-bin:$OPENVX_DIR/bazel-bin/vxu:$OPENVX_DIR/bazel-bin/targets/c_model:$OPENVX_DIR/bazel-bin/targets/extras:$OPENVX_DIR/bazel-bin/targets/debug:$OPENVX_DIR/bazel-bin/targets/opencl:$OPENVX_DIR/cts/build/lib/ -export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$OPENVX_DIR/bazel-bin:$OPENVX_DIR/bazel-bin/vxu:$OPENVX_DIR/bazel-bin/targets/c_model:$OPENVX_DIR/bazel-bin/targets/extras:$OPENVX_DIR/bazel-bin/targets/debug:$OPENVX_DIR/bazel-bin/targets/opencl:$OPENVX_DIR/cts/build/lib/ +export DYLD_LIBRARY_PATH=$DYLD_LIBRARY_PATH:$OPENVX_DIR/bazel-bin:$OPENVX_DIR/bazel-bin/vxu:$OPENVX_DIR/bazel-bin/targets/ai_server:$OPENVX_DIR/bazel-bin/targets/c_model:$OPENVX_DIR/bazel-bin/targets/extras:$OPENVX_DIR/bazel-bin/targets/debug:$OPENVX_DIR/bazel-bin/targets/executorch:$OPENVX_DIR/bazel-bin/targets/liteRT:$OPENVX_DIR/bazel-bin/targets/opencl:$OPENVX_DIR/bazel-bin/targets/onnxRT:$OPENVX_DIR/cts/build/lib/ +export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$OPENVX_DIR/bazel-bin:$OPENVX_DIR/bazel-bin/vxu:$OPENVX_DIR/bazel-bin/targets/ai_server:$OPENVX_DIR/bazel-bin/targets/c_model:$OPENVX_DIR/bazel-bin/targets/extras:$OPENVX_DIR/bazel-bin/targets/debug:$OPENVX_DIR/bazel-bin/targets/executorch:$OPENVX_DIR/bazel-bin/targets/liteRT:$OPENVX_DIR/bazel-bin/targets/opencl:$OPENVX_DIR/bazel-bin/targets/onnxRT:$OPENVX_DIR/cts/build/lib/ export VX_TEST_DATA_PATH=$OPENVX_DIR/cts/test_data/ export VX_CL_SOURCE_DIR=$OPENVX_DIR/kernels/opencl/ @@ -36,15 +40,20 @@ cmake \ -DOPENVX_CONFORMANCE_NEURAL_NETWORKS=ON \ .. +# -DOPENVX_USE_PIPELINING=ON \ # -DOPENVX_CONFORMANCE_VISION=ON \ # -DOPENVX_USE_ENHANCED_VISION=ON \ -# -DOPENVX_USE_PIPELINING=ON \ # -DOPENVX_USE_STREAMING=ON \ cmake --build . # Run the conformance test suite -./bin/vx_test_conformance +if [[ "$DEBUG" == "1" ]]; then + echo "Running under GDB since CI is set" + gdb -batch -ex "run" -ex "bt" --args ./bin/vx_test_conformance +else + ./bin/vx_test_conformance +fi # Clean up cd $TARGET_DIR diff --git a/tests/unit_test/BUILD b/tests/unit_test/BUILD index a4f46887..86b649e0 100644 --- a/tests/unit_test/BUILD +++ b/tests/unit_test/BUILD @@ -3,10 +3,6 @@ cc_test( srcs = [ "test_array.cpp" ], - includes = [ - "include", - "framework/include" - ], deps = [ "//:corevx", "@googletest//:gtest_main", @@ -18,15 +14,23 @@ cc_test( size = "small" ) +cc_test( + name = "test_circular_queue", + srcs = [ + "test_circular_queue.cpp", + ], + deps = [ + "//:corevx", + "@googletest//:gtest_main", + ], + size = "small" +) + cc_test( name = "test_context", srcs = [ "test_context.cpp" ], - includes = [ - "include", - "framework/include" - ], deps = [ "//:corevx", "@googletest//:gtest_main", @@ -43,10 +47,6 @@ cc_test( srcs = [ "test_delay.cpp" ], - includes = [ - "include", - "framework/include" - ], deps = [ "//:corevx", "@googletest//:gtest_main", @@ -63,10 +63,6 @@ cc_test( srcs = [ "test_distribution.cpp" ], - includes = [ - "include", - "framework/include" - ], deps = [ "//:corevx", "@googletest//:gtest_main", @@ -83,10 +79,6 @@ cc_test( srcs = [ "test_dot.cpp" ], - includes = [ - "include", - "framework/include" - ], deps = [ "//:corevx", "@googletest//:gtest_main", @@ -103,10 +95,6 @@ cc_test( srcs = [ "test_error.cpp" ], - includes = [ - "include", - "framework/include" - ], deps = [ "//:corevx", "@googletest//:gtest_main", @@ -123,10 +111,6 @@ cc_test( srcs = [ "test_image.cpp" ], - includes = [ - "include", - "framework/include" - ], deps = [ "//:corevx", "@googletest//:gtest_main", @@ -143,10 +127,6 @@ cc_test( srcs = [ "test_import.cpp" ], - includes = [ - "include", - "framework/include" - ], deps = [ "//:corevx", "@googletest//:gtest_main", @@ -163,10 +143,6 @@ cc_test( srcs = [ "test_parameter.cpp" ], - includes = [ - "include", - "framework/include" - ], deps = [ "//:corevx", "@googletest//:gtest_main", @@ -183,10 +159,6 @@ cc_test( srcs = [ "test_remap.cpp" ], - includes = [ - "include", - "framework/include" - ], deps = [ "//:corevx", "@googletest//:gtest_main", @@ -203,10 +175,6 @@ cc_test( srcs = [ "test_scalar.cpp" ], - includes = [ - "include", - "framework/include" - ], deps = [ "//:corevx", "@googletest//:gtest_main", @@ -223,10 +191,6 @@ cc_test( srcs = [ "test_target.cpp" ], - includes = [ - "include", - "framework/include" - ], deps = [ "//:corevx", "@googletest//:gtest_main", @@ -243,10 +207,6 @@ cc_test( srcs = [ "test_threshold.cpp" ], - includes = [ - "include", - "framework/include" - ], deps = [ "//:corevx", "@googletest//:gtest_main", @@ -263,10 +223,6 @@ cc_test( srcs = [ "test_userdataobject.cpp" ], - includes = [ - "include", - "framework/include" - ], deps = [ "//:corevx", "@googletest//:gtest_main", diff --git a/tests/unit_test/test_circular_queue.cpp b/tests/unit_test/test_circular_queue.cpp new file mode 100644 index 00000000..f9b81b9b --- /dev/null +++ b/tests/unit_test/test_circular_queue.cpp @@ -0,0 +1,116 @@ +/** + * @file test_circular_queue.cpp + * @brief Test Circular Queue + * @version 0.1 + * @date 2025-05-13 + * + * @copyright Copyright (c) 2025 + * + */ +#include +#include + +class CircularQueueTest : public ::testing::Test +{ +protected: + static constexpr size_t kDepth = 4; + CircularQueue queue; +}; + +TEST_F(CircularQueueTest, ConstructEmpty) +{ + EXPECT_TRUE(queue.empty()); + EXPECT_EQ(queue.size(), 0u); + EXPECT_FALSE(queue.full()); +} + +TEST_F(CircularQueueTest, EnqueueDequeueSingle) +{ + int value = 42; + EXPECT_TRUE(queue.enqueue(value)); + EXPECT_FALSE(queue.empty()); + EXPECT_EQ(queue.size(), 1u); + + int out = 0; + EXPECT_TRUE(queue.dequeue(out)); + EXPECT_EQ(out, value); + EXPECT_TRUE(queue.empty()); + EXPECT_EQ(queue.size(), 0u); +} + +TEST_F(CircularQueueTest, FillAndFull) +{ + for (size_t i = 0; i < kDepth - 1; ++i) + { + EXPECT_TRUE(queue.enqueue(i)); + } + EXPECT_TRUE(queue.full()); + EXPECT_EQ(queue.size(), kDepth - 1); + + // Should not enqueue when full + EXPECT_FALSE(queue.enqueue(100)); +} + +TEST_F(CircularQueueTest, DequeueEmpty) +{ + int out = 0; + EXPECT_FALSE(queue.dequeue(out)); +} + +TEST_F(CircularQueueTest, WrapAround) +{ + // Fill the queue + for (size_t i = 0; i < kDepth - 1; ++i) + { + EXPECT_TRUE(queue.enqueue(i)); + } + EXPECT_TRUE(queue.full()); + + // Dequeue two elements + int out = 0; + EXPECT_TRUE(queue.dequeue(out)); + EXPECT_EQ(out, 0); + EXPECT_TRUE(queue.dequeue(out)); + EXPECT_EQ(out, 1); + + // Enqueue two more elements (should wrap around) + EXPECT_TRUE(queue.enqueue(100)); + EXPECT_TRUE(queue.enqueue(101)); + EXPECT_TRUE(queue.full()); + + // Dequeue all and check order + EXPECT_TRUE(queue.dequeue(out)); + EXPECT_EQ(out, 2); + EXPECT_TRUE(queue.dequeue(out)); + EXPECT_EQ(out, 100); + EXPECT_TRUE(queue.dequeue(out)); + EXPECT_EQ(out, 101); + EXPECT_TRUE(queue.empty()); +} + +TEST_F(CircularQueueTest, SizeAfterOperations) +{ + EXPECT_EQ(queue.size(), 0u); + EXPECT_TRUE(queue.enqueue(1)); + EXPECT_EQ(queue.size(), 1u); + EXPECT_TRUE(queue.enqueue(2)); + EXPECT_EQ(queue.size(), 2u); + + int out = 0; + EXPECT_TRUE(queue.dequeue(out)); + EXPECT_EQ(queue.size(), 1u); + EXPECT_TRUE(queue.dequeue(out)); + EXPECT_EQ(queue.size(), 0u); +} + +TEST_F(CircularQueueTest, EnqueueDequeueAlternating) +{ + int out = 0; + for (int i = 0; i < 10; ++i) + { + EXPECT_TRUE(queue.enqueue(i)); + EXPECT_TRUE(queue.dequeue(out)); + EXPECT_EQ(out, i); + EXPECT_TRUE(queue.empty()); + } +} \ No newline at end of file diff --git a/tests/unit_test/test_import.cpp b/tests/unit_test/test_import.cpp index 62a28bcd..f42f7f7b 100644 --- a/tests/unit_test/test_import.cpp +++ b/tests/unit_test/test_import.cpp @@ -12,6 +12,8 @@ #include "vx_internal.h" +#if defined(OPENVX_USE_XML) || defined(OPENVX_USE_IX) + class ImportTest : public ::testing::Test { protected: @@ -51,3 +53,5 @@ TEST_F(ImportTest, DestructImport) import->destruct(); EXPECT_EQ(import->refs, nullptr); } + +#endif /* defined(OPENVX_USE_XML) || defined(OPENVX_USE_IX) */ \ No newline at end of file diff --git a/tests/unit_test/test_userdataobject.cpp b/tests/unit_test/test_userdataobject.cpp index 91130371..fc041613 100644 --- a/tests/unit_test/test_userdataobject.cpp +++ b/tests/unit_test/test_userdataobject.cpp @@ -12,6 +12,8 @@ #include "vx_internal.h" +#ifdef OPENVX_USE_USER_DATA_OBJECT + class UserDataObjectTest : public ::testing::Test { protected: @@ -71,3 +73,5 @@ TEST_F(UserDataObjectTest, UserDataObjectDestructor) for (vx_uint32 p = 0; p < udata->memory.nptrs; p++) EXPECT_EQ(udata->memory.ptrs[p], nullptr); } + +#endif /* OPENVX_USE_USER_DATA_OBJECT */ \ No newline at end of file