diff --git a/unified-runtime/source/adapters/level_zero/common/device.cpp b/unified-runtime/source/adapters/level_zero/common/device.cpp index dc6588f29a604..16e5d01672427 100644 --- a/unified-runtime/source/adapters/level_zero/common/device.cpp +++ b/unified-runtime/source/adapters/level_zero/common/device.cpp @@ -2177,32 +2177,26 @@ ur_result_t ur_device_handle_t_::initialize(int SubSubDeviceOrdinal, ZeQueuePriorityRange.Compute = [this, ZeDevice](ze_queue_priority_range &Range) { + std::optional> DriverLevels; + int32_t Lowest = 0, Highest = 0; auto GetPriorityLevels = Platform->ZeQueuePriorityExt.zeDeviceGetPriorityLevels; - if (!GetPriorityLevels) - return; // The function is loaded directly from the driver, so it expects a driver // handle rather than a loader handle. ze_device_handle_t DriverDevice = nullptr; - if (ZE_CALL_NOCHECK(zelLoaderTranslateHandle, + if (GetPriorityLevels && + ZE_CALL_NOCHECK(zelLoaderTranslateHandle, (ZEL_HANDLE_DEVICE, ZeDevice, - reinterpret_cast(&DriverDevice))) != - ZE_RESULT_SUCCESS || - ZE_CALL_NOCHECK(GetPriorityLevels, - (DriverDevice, &Range.Lowest, &Range.Highest)) != + reinterpret_cast(&DriverDevice))) == + ZE_RESULT_SUCCESS && + ZE_CALL_NOCHECK(GetPriorityLevels, (DriverDevice, &Lowest, &Highest)) == ZE_RESULT_SUCCESS) - return; - // A single level or an empty range means there are no levels to choose - // from. - if (Range.Highest >= Range.Lowest) - return; + DriverLevels = {Lowest, Highest}; // The UR API guarantees that the range contains the default priority 0. - if (Range.Highest > 0 || Range.Lowest < 0) { - UR_LOG(WARN, "Ignoring queue priority range [{}, {}] without 0.", - Range.Highest, Range.Lowest); - return; - } - Range.Supported = true; + if (DriverLevels && (Highest > 0 || Lowest < 0)) + UR_LOG(WARN, "Ignoring queue priority range [{}, {}] without 0.", Highest, + Lowest); + Range = makeQueuePriorityRange(DriverLevels); }; #ifdef ZE_INTEL_DEVICE_BLOCK_ARRAY_EXP_NAME diff --git a/unified-runtime/source/adapters/level_zero/common/device.hpp b/unified-runtime/source/adapters/level_zero/common/device.hpp index 5cefd5af91efc..d221b51b8a84e 100644 --- a/unified-runtime/source/adapters/level_zero/common/device.hpp +++ b/unified-runtime/source/adapters/level_zero/common/device.hpp @@ -8,6 +8,7 @@ //===----------------------------------------------------------------------===// #pragma once +#include #include #include #include @@ -17,6 +18,7 @@ #include #include #include +#include #include #include "api.hpp" @@ -69,13 +71,69 @@ struct ze_global_memsize { uint64_t value; }; -// Queue priority range reported by zeDeviceGetPriorityLevels. +// Queue priority range, see makeQueuePriorityRange. struct ze_queue_priority_range { - bool Supported; int32_t Highest; + // Maps to ZE_COMMAND_QUEUE_PRIORITY_PRIORITY_LOW instead of a priority level. int32_t Lowest; + // Whether the levels above Lowest are the levels of + // zeDeviceGetPriorityLevels. Otherwise the range is {1, -1}, and -1 maps to + // ZE_COMMAND_QUEUE_PRIORITY_PRIORITY_HIGH instead of a priority level. + bool HasLevels; }; +// Makes the queue priority range from the {lowest, highest} levels reported by +// zeDeviceGetPriorityLevels, if any. The driver levels only cover the context +// group, while the low priority class uses a separate context below all of +// them, so it's exposed as an extra lowest level. Without at least two levels +// around the default priority 0, the range is made of the low, normal and high +// priority classes instead. +inline ze_queue_priority_range makeQueuePriorityRange( + std::optional> DriverLevels) { + if (!DriverLevels) + return {-1, 1, false}; + const auto [Lowest, Highest] = *DriverLevels; + if (Highest >= Lowest || Highest > 0 || Lowest < 0) + return {-1, 1, false}; + return {Highest, Lowest + 1, true}; +} + +// The Level Zero priority class and level of a queue. +struct ze_queue_priority { + ze_command_queue_priority_t Class; + std::optional Level; +}; + +// Returns the Level Zero priority of a queue created with the given flags or, +// if set, the given numeric priority. +inline ze_queue_priority +getZeQueuePriority(const ze_queue_priority_range &Range, ur_queue_flags_t Flags, + std::optional Priority) { + if (!Priority) { + if (Flags & UR_QUEUE_FLAG_PRIORITY_LOW) + return {ZE_COMMAND_QUEUE_PRIORITY_PRIORITY_LOW, std::nullopt}; + if (!(Flags & UR_QUEUE_FLAG_PRIORITY_HIGH)) + return {ZE_COMMAND_QUEUE_PRIORITY_NORMAL, std::nullopt}; + // The high priority class alone gets the driver's default priority of high + // priority queues, which may be below the highest priority level. + if (Range.HasLevels) + return {ZE_COMMAND_QUEUE_PRIORITY_NORMAL, Range.Highest}; + return {ZE_COMMAND_QUEUE_PRIORITY_PRIORITY_HIGH, std::nullopt}; + } + // Clamp here rather than relying on the driver, so that values mapping to + // the same priority also share cached command lists. + const int32_t Level = std::clamp(*Priority, Range.Highest, Range.Lowest); + // The driver derives the priority class from a priority level, so the + // lowest level can only be expressed by the low class without a level. + if (Level == Range.Lowest) + return {ZE_COMMAND_QUEUE_PRIORITY_PRIORITY_LOW, std::nullopt}; + if (Range.HasLevels) + return {ZE_COMMAND_QUEUE_PRIORITY_NORMAL, Level}; + return {Level < 0 ? ZE_COMMAND_QUEUE_PRIORITY_PRIORITY_HIGH + : ZE_COMMAND_QUEUE_PRIORITY_NORMAL, + std::nullopt}; +} + enum ur_ze_external_memory_desc_type { UR_ZE_EXTERNAL_OPAQUE_FD, UR_ZE_EXTERNAL_WIN32, diff --git a/unified-runtime/source/adapters/level_zero/device.cpp b/unified-runtime/source/adapters/level_zero/device.cpp index 5b64f901b97c4..894b1aa6b2f1d 100644 --- a/unified-runtime/source/adapters/level_zero/device.cpp +++ b/unified-runtime/source/adapters/level_zero/device.cpp @@ -36,11 +36,14 @@ ur_result_t urDeviceGetInfo(::ur_device_handle_t hDeviceOpque, return ReturnValue(false); case UR_DEVICE_INFO_IPC_EVENT_SUPPORT_EXP: return ReturnValue(false); + // The low, normal and high priority classes are the priority levels 1, 0 + // and -1, see urQueueCreate. case UR_DEVICE_INFO_QUEUE_PRIORITY_SUPPORT_EXP: - return ReturnValue(static_cast(false)); + return ReturnValue(static_cast(true)); case UR_DEVICE_INFO_QUEUE_PRIORITY_HIGHEST_EXP: + return ReturnValue(int32_t{-1}); case UR_DEVICE_INFO_QUEUE_PRIORITY_LOWEST_EXP: - return UR_RESULT_ERROR_UNSUPPORTED_ENUMERATION; + return ReturnValue(int32_t{1}); default: return ur::level_zero::urDeviceGetInfo(hDeviceOpque, propName, propSize, propValue, propSizeRet); diff --git a/unified-runtime/source/adapters/level_zero/queue.cpp b/unified-runtime/source/adapters/level_zero/queue.cpp index 53532eccd4130..95e734d4f69a5 100644 --- a/unified-runtime/source/adapters/level_zero/queue.cpp +++ b/unified-runtime/source/adapters/level_zero/queue.cpp @@ -501,6 +501,7 @@ ur_result_t urQueueCreate( } int ForceComputeIndex = -1; // Use default/round-robin. + std::optional Priority; if (Props) { if (Props->pNext) { const ur_base_properties_t *extendedDesc = @@ -511,8 +512,12 @@ ur_result_t urQueueCreate( ForceComputeIndex = IndexProperties->computeIndex; } } - if (find_stype_node(Props)) - return UR_RESULT_ERROR_UNSUPPORTED_FEATURE; + if (const auto *PriorityProperties = + find_stype_node(Props)) { + if (Flags & (UR_QUEUE_FLAG_PRIORITY_LOW | UR_QUEUE_FLAG_PRIORITY_HIGH)) + return UR_RESULT_ERROR_INVALID_QUEUE_PROPERTIES; + Priority = PriorityProperties->priority; + } } UR_ASSERT(Context->isValidDevice(Device), UR_RESULT_ERROR_INVALID_DEVICE); @@ -550,7 +555,7 @@ ur_result_t urQueueCreate( try { *Queue = new ur::level_zero::v1::ur_queue_handle_t_( ZeComputeCommandQueues, ZeCopyCommandQueues, Context, Device, true, - Flags, ForceComputeIndex); + Flags, ForceComputeIndex, Priority); } catch (const std::bad_alloc &) { return UR_RESULT_ERROR_OUT_OF_HOST_MEMORY; } catch (...) { @@ -1179,9 +1184,16 @@ ur::level_zero::v1::ur_queue_handle_t_::ur_queue_handle_t_( std::vector &ComputeQueues, std::vector &CopyQueues, ur_context_handle_t Context, ur_device_handle_t Device, - bool OwnZeCommandQueue, ur_queue_flags_t Properties, int ForceComputeIndex) + bool OwnZeCommandQueue, ur_queue_flags_t Properties, int ForceComputeIndex, + std::optional Priority) : Context{Context}, Device{Device}, OwnZeCommandQueue{OwnZeCommandQueue}, Properties(Properties) { + if (Priority) + PriorityClass = std::clamp(*Priority, int32_t{-1}, int32_t{1}); + else if (Properties & UR_QUEUE_FLAG_PRIORITY_LOW) + PriorityClass = 1; + else if (Properties & UR_QUEUE_FLAG_PRIORITY_HIGH) + PriorityClass = -1; // Set the type of commandlists the queue will use when user-selected // submission mode. Otherwise use env var setting and if unset, use default. if (isBatchedSubmission()) @@ -1730,11 +1742,11 @@ bool ur::level_zero::v1::ur_queue_handle_t_::isDiscardEvents() const { } bool ur::level_zero::v1::ur_queue_handle_t_::isPriorityLow() const { - return ((this->Properties & UR_QUEUE_FLAG_PRIORITY_LOW) != 0); + return PriorityClass > 0; } bool ur::level_zero::v1::ur_queue_handle_t_::isPriorityHigh() const { - return ((this->Properties & UR_QUEUE_FLAG_PRIORITY_HIGH) != 0); + return PriorityClass < 0; } bool ur::level_zero::v1::ur_queue_handle_t_::isBatchedSubmission() const { diff --git a/unified-runtime/source/adapters/level_zero/queue.hpp b/unified-runtime/source/adapters/level_zero/queue.hpp index 801e063040279..2052de3c9f77d 100644 --- a/unified-runtime/source/adapters/level_zero/queue.hpp +++ b/unified-runtime/source/adapters/level_zero/queue.hpp @@ -229,7 +229,8 @@ struct ur_queue_handle_t_ : ur_object_t { std::vector &CopyQueues, ur_context_handle_t Context, ur_device_handle_t Device, bool OwnZeCommandQueue, ur_queue_flags_t Properties = 0, - int ForceComputeIndex = -1); + int ForceComputeIndex = -1, + std::optional Priority = std::nullopt); using queue_type = ur_device_handle_t_::queue_group_info_t::type; // PI queue is in general a one to many mapping to L0 native queues. @@ -372,6 +373,10 @@ struct ur_queue_handle_t_ : ur_object_t { // Keeps the properties of this queue. ur_queue_flags_t Properties; + // The priority levels 1, 0 and -1 for the low, normal and high priority + // classes. Set by a numeric priority or by the priority flags. + int32_t PriorityClass = 0; + // Keeps track of whether we are using Counter-based Events bool CounterBasedEventsEnabled = false; diff --git a/unified-runtime/source/adapters/level_zero/v2/device.cpp b/unified-runtime/source/adapters/level_zero/v2/device.cpp index 452bf9cfdcb70..aea19c26046b8 100644 --- a/unified-runtime/source/adapters/level_zero/v2/device.cpp +++ b/unified-runtime/source/adapters/level_zero/v2/device.cpp @@ -83,13 +83,10 @@ ur_result_t urDeviceGetInfo(::ur_device_handle_t hDeviceOpque, #endif } case UR_DEVICE_INFO_QUEUE_PRIORITY_SUPPORT_EXP: - return ReturnValue( - static_cast(hDevice->ZeQueuePriorityRange->Supported)); + return ReturnValue(static_cast(true)); case UR_DEVICE_INFO_QUEUE_PRIORITY_HIGHEST_EXP: case UR_DEVICE_INFO_QUEUE_PRIORITY_LOWEST_EXP: { const auto &Range = hDevice->ZeQueuePriorityRange.get(); - if (!Range.Supported) - return UR_RESULT_ERROR_UNSUPPORTED_ENUMERATION; return ReturnValue(propName == UR_DEVICE_INFO_QUEUE_PRIORITY_HIGHEST_EXP ? Range.Highest : Range.Lowest); diff --git a/unified-runtime/source/adapters/level_zero/v2/queue_create.cpp b/unified-runtime/source/adapters/level_zero/v2/queue_create.cpp index 955f467ebb520..f23d202eb27e5 100644 --- a/unified-runtime/source/adapters/level_zero/v2/queue_create.cpp +++ b/unified-runtime/source/adapters/level_zero/v2/queue_create.cpp @@ -16,8 +16,6 @@ #include "queue_immediate_in_order.hpp" #include "unified-runtime/ur_api.h" -#include - static const bool ForceBatched = getenv_tobool("UR_L0_V2_FORCE_BATCHED"); namespace ur::level_zero::v2 { @@ -43,14 +41,6 @@ getPriorityLevel(const ur_queue_properties_t *pProps) { return std::nullopt; } -static ze_command_queue_priority_t getZePriority(ur_queue_flags_t flags) { - if ((flags & UR_QUEUE_FLAG_PRIORITY_LOW) != 0) - return ZE_COMMAND_QUEUE_PRIORITY_PRIORITY_LOW; - if ((flags & UR_QUEUE_FLAG_PRIORITY_HIGH) != 0) - return ZE_COMMAND_QUEUE_PRIORITY_PRIORITY_HIGH; - return ZE_COMMAND_QUEUE_PRIORITY_NORMAL; -} - static event_flags_t eventFlagsFromQueueFlags(ur_queue_flags_t flags) { event_flags_t eventFlags = EVENT_FLAGS_COUNTER; if (flags & UR_QUEUE_FLAG_PROFILING_ENABLE) @@ -104,37 +94,29 @@ ur_result_t urQueueCreate(::ur_context_handle_t hContextOpque, } auto zeIndex = v2::getZeIndex(pProperties); - auto priorityLevel = v2::getPriorityLevel(pProperties); - - if (priorityLevel) { - if (flags & (UR_QUEUE_FLAG_PRIORITY_LOW | UR_QUEUE_FLAG_PRIORITY_HIGH)) - return UR_RESULT_ERROR_INVALID_QUEUE_PROPERTIES; - const auto &Range = hDevice->ZeQueuePriorityRange.get(); - if (!Range.Supported) - return UR_RESULT_ERROR_UNSUPPORTED_FEATURE; - // Clamp here rather than relying on the driver, so that values mapping to - // the same priority also share cached command lists. - priorityLevel = std::clamp(*priorityLevel, Range.Highest, Range.Lowest); - } + auto priority = v2::getPriorityLevel(pProperties); + if (priority && + (flags & (UR_QUEUE_FLAG_PRIORITY_LOW | UR_QUEUE_FLAG_PRIORITY_HIGH))) + return UR_RESULT_ERROR_INVALID_QUEUE_PROPERTIES; + const auto [zePriority, priorityLevel] = + getZeQueuePriority(hDevice->ZeQueuePriorityRange.get(), flags, priority); if (isBatched) { // out of order not supported *phQueue = ur_queue_handle_t_::create( - hContext, hDevice, v2::getZeOrdinal(hDevice), v2::getZePriority(flags), - zeIndex, priorityLevel, v2::eventFlagsFromQueueFlags(flags), flags); + hContext, hDevice, v2::getZeOrdinal(hDevice), zePriority, zeIndex, + priorityLevel, v2::eventFlagsFromQueueFlags(flags), flags); } else { if (isOutOfOrder) { *phQueue = ur_queue_handle_t_::create( - hContext, hDevice, v2::getZeOrdinal(hDevice), - v2::getZePriority(flags), zeIndex, priorityLevel, - v2::eventFlagsFromQueueFlags(flags), flags); + hContext, hDevice, v2::getZeOrdinal(hDevice), zePriority, zeIndex, + priorityLevel, v2::eventFlagsFromQueueFlags(flags), flags); } else { // immediate *phQueue = ur_queue_handle_t_::create( - hContext, hDevice, v2::getZeOrdinal(hDevice), - v2::getZePriority(flags), zeIndex, priorityLevel, - v2::eventFlagsFromQueueFlags(flags), flags); + hContext, hDevice, v2::getZeOrdinal(hDevice), zePriority, zeIndex, + priorityLevel, v2::eventFlagsFromQueueFlags(flags), flags); } } diff --git a/unified-runtime/test/adapters/level_zero/v2/CMakeLists.txt b/unified-runtime/test/adapters/level_zero/v2/CMakeLists.txt index 84ba44c9ab912..71f3bd6b840a9 100644 --- a/unified-runtime/test/adapters/level_zero/v2/CMakeLists.txt +++ b/unified-runtime/test/adapters/level_zero/v2/CMakeLists.txt @@ -57,6 +57,10 @@ add_l0_v2_devices_test(batched_queue batched_queue_test.cpp ) +add_l0_v2_devices_test(queue_priority + queue_priority_test.cpp +) + if(NOT UR_FOUND_DPCXX) # Tests that require kernels can't be used if we aren't generating # device binaries diff --git a/unified-runtime/test/adapters/level_zero/v2/command_list_cache_test.cpp b/unified-runtime/test/adapters/level_zero/v2/command_list_cache_test.cpp index 301622b7db4f3..afa547cd29b71 100644 --- a/unified-runtime/test/adapters/level_zero/v2/command_list_cache_test.cpp +++ b/unified-runtime/test/adapters/level_zero/v2/command_list_cache_test.cpp @@ -99,8 +99,8 @@ TEST_P(CommandListCacheTest, CanStoreAndRetriveImmediateAndRegularCmdLists) { } TEST_P(CommandListCacheTest, ImmediateCommandListsAreCachedPerPriorityLevel) { - if (!common_cast(device)->ZeQueuePriorityRange->Supported) { - GTEST_SKIP() << "Queue priority levels are not supported."; + if (!common_cast(device)->ZeQueuePriorityRange->HasLevels) { + GTEST_SKIP() << "Device has no priority levels."; } v2::supported_extensions_descriptor_t supportedExtensions(false, false, @@ -291,10 +291,6 @@ TEST_P(CommandListCacheTest, CommandListsAreReusedByQueues) { TEST_P(CommandListCacheTest, OutOfRangePriorityLevelsAreClamped) { const auto &Range = common_cast(device)->ZeQueuePriorityRange.get(); - if (!Range.Supported) { - GTEST_SKIP() << "Queue priority levels are not supported."; - } - auto &clCache = v2::v2_cast(context)->getCommandListCache(); auto createQueue = [&](int32_t Priority, uur::raii::Queue &Queue) { ur_exp_queue_priority_properties_t PriorityProps{ @@ -324,6 +320,70 @@ TEST_P(CommandListCacheTest, OutOfRangePriorityLevelsAreClamped) { } } +TEST_P(CommandListCacheTest, LowestPriorityLevelUsesLowPriorityClass) { + auto hDevice = common_cast(device); + const auto &Range = hDevice->ZeQueuePriorityRange.get(); + int32_t Lowest = 0; + ASSERT_SUCCESS(::urDeviceGetInfo(device, + UR_DEVICE_INFO_QUEUE_PRIORITY_LOWEST_EXP, + sizeof(Lowest), &Lowest, nullptr)); + ASSERT_EQ(Lowest, Range.Lowest); + + { + ur_exp_queue_priority_properties_t PriorityProps{ + UR_STRUCTURE_TYPE_EXP_QUEUE_PRIORITY_PROPERTIES, nullptr, Lowest}; + ur_queue_properties_t QueueProps{UR_STRUCTURE_TYPE_QUEUE_PROPERTIES, + &PriorityProps, + UR_QUEUE_FLAG_SUBMISSION_IMMEDIATE}; + uur::raii::Queue Queue; + ASSERT_SUCCESS(urQueueCreate(context, device, &QueueProps, Queue.ptr())); + } + + // The queue's command list must have been created with the low priority + // class and without a priority level. + auto &clCache = v2::v2_cast(context)->getCommandListCache(); + const size_t NumCached = clCache.getNumImmediateCommandLists(); + v2::command_list_desc_t Desc; + Desc.IsInOrder = true; + using queue_group_type = + ur::level_zero::ur_device_handle_t_::queue_group_info_t::type; + Desc.Ordinal = hDevice->QueueGroup[queue_group_type::Compute].ZeOrdinal; + Desc.CopyOffloadEnable = true; + auto CmdList = clCache.getImmediateCommandList( + hDevice->ZeDevice, Desc, ZE_COMMAND_QUEUE_MODE_ASYNCHRONOUS, + ZE_COMMAND_QUEUE_PRIORITY_PRIORITY_LOW); + ASSERT_NE(CmdList, nullptr); + ASSERT_EQ(clCache.getNumImmediateCommandLists(), NumCached - 1); +} + +TEST_P(CommandListCacheTest, HighPriorityFlagUsesHighestPriorityLevel) { + const auto &Range = common_cast(device)->ZeQueuePriorityRange.get(); + if (!Range.HasLevels || Range.Highest >= 0) { + GTEST_SKIP() << "Device has no priority level above the default one."; + } + + auto &clCache = v2::v2_cast(context)->getCommandListCache(); + const size_t NumCached = clCache.getNumImmediateCommandLists(); + { + ur_queue_properties_t QueueProps{ + UR_STRUCTURE_TYPE_QUEUE_PROPERTIES, nullptr, + UR_QUEUE_FLAG_SUBMISSION_IMMEDIATE | UR_QUEUE_FLAG_PRIORITY_HIGH}; + uur::raii::Queue Queue; + ASSERT_SUCCESS(urQueueCreate(context, device, &QueueProps, Queue.ptr())); + } + ASSERT_GT(clCache.getNumImmediateCommandLists(), NumCached); + + // A queue with the highest priority level must reuse all command lists. + ur_exp_queue_priority_properties_t PriorityProps{ + UR_STRUCTURE_TYPE_EXP_QUEUE_PRIORITY_PROPERTIES, nullptr, Range.Highest}; + ur_queue_properties_t QueueProps{UR_STRUCTURE_TYPE_QUEUE_PROPERTIES, + &PriorityProps, + UR_QUEUE_FLAG_SUBMISSION_IMMEDIATE}; + uur::raii::Queue Queue; + ASSERT_SUCCESS(urQueueCreate(context, device, &QueueProps, Queue.ptr())); + ASSERT_EQ(clCache.getNumImmediateCommandLists(), NumCached); +} + TEST_P(CommandListCacheTest, CommandListsCacheIsThreadSafe) { static constexpr int NumThreads = 10; static constexpr int NumIters = 10; diff --git a/unified-runtime/test/adapters/level_zero/v2/queue_priority_test.cpp b/unified-runtime/test/adapters/level_zero/v2/queue_priority_test.cpp new file mode 100644 index 0000000000000..66f76bc4324ba --- /dev/null +++ b/unified-runtime/test/adapters/level_zero/v2/queue_priority_test.cpp @@ -0,0 +1,105 @@ +// Part of the LLVM Project, under the Apache License v2.0 with LLVM +// Exceptions. See https://llvm.org/LICENSE.txt for license information. +// +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +// RUN: %with-v2 ./queue_priority-test +// REQUIRES: v2 + +#include "level_zero/common/device.hpp" + +#include +#include +#include +#include + +using namespace ur::level_zero; + +namespace { + +constexpr int32_t Min = std::numeric_limits::min(); +constexpr int32_t Max = std::numeric_limits::max(); + +void expectPriority(const ze_queue_priority &Actual, + ze_command_queue_priority_t Class, + std::optional Level) { + EXPECT_EQ(Actual.Class, Class); + EXPECT_EQ(Actual.Level, Level); +} + +} // namespace + +TEST(QueuePriorityRangeTest, WithoutDriverLevelsUsesPriorityClasses) { + // No zeDeviceGetPriorityLevels, a single level, or a range without 0. + for (auto DriverLevels : + {std::optional>{}, + std::optional{std::pair{0, 0}}, std::optional{std::pair{2, 1}}}) { + const auto Range = makeQueuePriorityRange(DriverLevels); + EXPECT_EQ(Range.Highest, -1); + EXPECT_EQ(Range.Lowest, 1); + EXPECT_FALSE(Range.HasLevels); + } +} + +TEST(QueuePriorityRangeTest, WithDriverLevelsAddsLowestLevel) { + const auto Range = makeQueuePriorityRange(std::pair{0, -3}); + EXPECT_EQ(Range.Highest, -3); + EXPECT_EQ(Range.Lowest, 1); + EXPECT_TRUE(Range.HasLevels); +} + +TEST(QueuePriorityTest, WithoutDriverLevelsUsesPriorityClasses) { + const auto Range = makeQueuePriorityRange(std::nullopt); + + expectPriority(getZeQueuePriority(Range, 0, std::nullopt), + ZE_COMMAND_QUEUE_PRIORITY_NORMAL, std::nullopt); + expectPriority( + getZeQueuePriority(Range, UR_QUEUE_FLAG_PRIORITY_LOW, std::nullopt), + ZE_COMMAND_QUEUE_PRIORITY_PRIORITY_LOW, std::nullopt); + expectPriority( + getZeQueuePriority(Range, UR_QUEUE_FLAG_PRIORITY_HIGH, std::nullopt), + ZE_COMMAND_QUEUE_PRIORITY_PRIORITY_HIGH, std::nullopt); + + for (int32_t Priority : {1, Max}) + expectPriority(getZeQueuePriority(Range, 0, Priority), + ZE_COMMAND_QUEUE_PRIORITY_PRIORITY_LOW, std::nullopt); + expectPriority(getZeQueuePriority(Range, 0, 0), + ZE_COMMAND_QUEUE_PRIORITY_NORMAL, std::nullopt); + for (int32_t Priority : {-1, Min}) + expectPriority(getZeQueuePriority(Range, 0, Priority), + ZE_COMMAND_QUEUE_PRIORITY_PRIORITY_HIGH, std::nullopt); +} + +TEST(QueuePriorityTest, WithDriverLevelsUsesPriorityLevels) { + const auto Range = makeQueuePriorityRange(std::pair{0, -3}); + + expectPriority(getZeQueuePriority(Range, 0, std::nullopt), + ZE_COMMAND_QUEUE_PRIORITY_NORMAL, std::nullopt); + expectPriority( + getZeQueuePriority(Range, UR_QUEUE_FLAG_PRIORITY_LOW, std::nullopt), + ZE_COMMAND_QUEUE_PRIORITY_PRIORITY_LOW, std::nullopt); + expectPriority( + getZeQueuePriority(Range, UR_QUEUE_FLAG_PRIORITY_HIGH, std::nullopt), + ZE_COMMAND_QUEUE_PRIORITY_NORMAL, -3); + + for (int32_t Priority : {1, Max}) + expectPriority(getZeQueuePriority(Range, 0, Priority), + ZE_COMMAND_QUEUE_PRIORITY_PRIORITY_LOW, std::nullopt); + for (int32_t Priority : {0, -2}) + expectPriority(getZeQueuePriority(Range, 0, Priority), + ZE_COMMAND_QUEUE_PRIORITY_NORMAL, Priority); + expectPriority(getZeQueuePriority(Range, 0, Min), + ZE_COMMAND_QUEUE_PRIORITY_NORMAL, -3); +} + +TEST(QueuePriorityTest, HighFlagMatchesHighestLevelWhenHighestIsZero) { + const auto Range = makeQueuePriorityRange(std::pair{1, 0}); + ASSERT_TRUE(Range.HasLevels); + ASSERT_EQ(Range.Highest, 0); + + const auto HighFlag = + getZeQueuePriority(Range, UR_QUEUE_FLAG_PRIORITY_HIGH, std::nullopt); + const auto HighestLevel = getZeQueuePriority(Range, 0, Range.Highest); + expectPriority(HighFlag, ZE_COMMAND_QUEUE_PRIORITY_NORMAL, 0); + expectPriority(HighestLevel, HighFlag.Class, HighFlag.Level); +} diff --git a/unified-runtime/test/conformance/exp_queue_priority/urQueueCreatePriorityExp.cpp b/unified-runtime/test/conformance/exp_queue_priority/urQueueCreatePriorityExp.cpp index 846788969815e..d13d35f3fc059 100644 --- a/unified-runtime/test/conformance/exp_queue_priority/urQueueCreatePriorityExp.cpp +++ b/unified-runtime/test/conformance/exp_queue_priority/urQueueCreatePriorityExp.cpp @@ -109,6 +109,26 @@ TEST_P(urQueueCreatePriorityExpTest, SuccessWithQueueIndex) { UUR_RETURN_ON_FATAL_FAILURE(checkQueue(queue)); } +TEST_P(urQueueCreatePriorityExpTest, FlagsHaveNoPriorityFlags) { + for (int32_t priority : {lowest, 0, highest}) { + uur::raii::Queue queue; + ASSERT_SUCCESS(createQueue(priority, 0, queue.ptr())); + ur_queue_flags_t flags = 0; + ASSERT_SUCCESS(urQueueGetInfo(queue, UR_QUEUE_INFO_FLAGS, sizeof(flags), + &flags, nullptr)); + ASSERT_EQ( + flags & (UR_QUEUE_FLAG_PRIORITY_LOW | UR_QUEUE_FLAG_PRIORITY_HIGH), 0u); + + // The queried flags can be combined with the priority again. + ur_exp_queue_priority_properties_t priorityProps{ + UR_STRUCTURE_TYPE_EXP_QUEUE_PRIORITY_PROPERTIES, nullptr, priority}; + ur_queue_properties_t props{UR_STRUCTURE_TYPE_QUEUE_PROPERTIES, + &priorityProps, flags}; + uur::raii::Queue sameQueue; + ASSERT_SUCCESS(urQueueCreate(context, device, &props, sameQueue.ptr())); + } +} + TEST_P(urQueueCreatePriorityExpTest, InvalidWithPriorityFlags) { for (ur_queue_flags_t flag : {UR_QUEUE_FLAG_PRIORITY_LOW, UR_QUEUE_FLAG_PRIORITY_HIGH}) {