| //===------- Offload API tests - olLaunchKernel --------------------===// |
| // |
| // 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 |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "../common/Fixtures.hpp" |
| #include <OffloadAPI.h> |
| #include <gtest/gtest.h> |
| |
| #define KERNEL_TEST(NAME, KERNEL) \ |
| struct olLaunchKernel##NAME##Test : LaunchSingleKernelTestBase { \ |
| void SetUp() override { SetUpKernel(#KERNEL); } \ |
| }; \ |
| OFFLOAD_TESTS_INSTANTIATE_DEVICE_FIXTURE(olLaunchKernel##NAME##Test); |
| |
| KERNEL_TEST(Foo, foo) |
| KERNEL_TEST(NoArgs, noargs) |
| KERNEL_TEST(MultiArgs, multiargs) |
| KERNEL_TEST(Composite, composite) |
| KERNEL_TEST(Byte, byte) |
| KERNEL_TEST(LocalMem, localmem) |
| KERNEL_TEST(LocalMemReduction, localmem_reduction) |
| KERNEL_TEST(LocalMemStatic, localmem_static) |
| KERNEL_TEST(SingleCounterSyncEvent, single_counter) |
| KERNEL_TEST(GlobalCtor, global_ctor) |
| KERNEL_TEST(GlobalDtor, global_dtor) |
| KERNEL_TEST(GridSize, gridsize) |
| |
| struct LaunchMultipleKernelTestBase : LaunchKernelTestBase { |
| void SetUpKernels(const char *program, std::vector<const char *> kernels) { |
| RETURN_ON_FATAL_FAILURE(SetUpProgram(program)); |
| |
| Kernels.resize(kernels.size()); |
| size_t I = 0; |
| for (auto K : kernels) |
| ASSERT_SUCCESS( |
| olGetSymbol(Program, K, OL_SYMBOL_KIND_KERNEL, &Kernels[I++])); |
| } |
| |
| std::vector<ol_symbol_handle_t> Kernels; |
| }; |
| |
| struct ArgsSingleCounter { |
| int32_t InitLoop; |
| int32_t Addend; |
| uint32_t *InitVal; |
| uint32_t *Out; |
| }; |
| |
| #define KERNEL_MULTI_TEST(NAME, PROGRAM, ...) \ |
| struct olLaunchKernel##NAME##Test : LaunchMultipleKernelTestBase { \ |
| void SetUp() override { SetUpKernels(#PROGRAM, {__VA_ARGS__}); } \ |
| }; \ |
| OFFLOAD_TESTS_INSTANTIATE_DEVICE_FIXTURE(olLaunchKernel##NAME##Test); |
| |
| KERNEL_MULTI_TEST(Global, global, "write", "read") |
| |
| TEST_P(olLaunchKernelFooTest, Success) { |
| void *Mem; |
| ASSERT_SUCCESS(olMemAlloc(Device, OL_ALLOC_TYPE_MANAGED, |
| LaunchArgs.GroupSize.x * sizeof(uint32_t), &Mem)); |
| |
| void *ArgPtrs[] = {&Mem}; |
| size_t ArgSizes[] = {sizeof(Mem)}; |
| |
| ASSERT_SUCCESS(olLaunchKernel(Queue, Device, Kernel, &LaunchArgs, nullptr, |
| std::size(ArgPtrs), ArgPtrs, ArgSizes)); |
| |
| ASSERT_SUCCESS(olSyncQueue(Queue)); |
| |
| uint32_t *Data = (uint32_t *)Mem; |
| for (uint32_t i = 0; i < 64; i++) { |
| ASSERT_EQ(Data[i], i); |
| } |
| |
| ASSERT_SUCCESS(olMemFree(Mem)); |
| } |
| |
| TEST_P(olLaunchKernelFooTest, SuccessThreaded) { |
| SKIP_KNOWN_FAILURE(LevelZero{"thread-safety issues"}); |
| |
| threadify([&](size_t) { |
| void *DevAlloc, *HstAlloc; |
| size_t Size = LaunchArgs.GroupSize.x * sizeof(uint32_t); |
| ASSERT_SUCCESS(olMemAlloc(Device, OL_ALLOC_TYPE_DEVICE, Size, &DevAlloc)); |
| ASSERT_SUCCESS(olMemAlloc(Device, OL_ALLOC_TYPE_HOST, Size, &HstAlloc)); |
| |
| void *ArgPtrs[] = {&DevAlloc}; |
| size_t ArgSizes[] = {sizeof(DevAlloc)}; |
| |
| ASSERT_SUCCESS(olLaunchKernel(Queue, Device, Kernel, &LaunchArgs, nullptr, |
| std::size(ArgPtrs), ArgPtrs, ArgSizes)); |
| |
| ASSERT_SUCCESS(olMemcpy(Queue, HstAlloc, Host, DevAlloc, Device, Size)); |
| |
| ASSERT_SUCCESS(olSyncQueue(Queue)); |
| |
| uint32_t *Data = static_cast<uint32_t *>(HstAlloc); |
| for (uint32_t i = 0; i < LaunchArgs.GroupSize.x; i++) { |
| ASSERT_EQ(Data[i], i); |
| } |
| |
| ASSERT_SUCCESS(olMemFree(DevAlloc)); |
| ASSERT_SUCCESS(olMemFree(HstAlloc)); |
| }); |
| } |
| |
| TEST_P(olLaunchKernelNoArgsTest, Success) { |
| ASSERT_SUCCESS(olLaunchKernel(Queue, Device, Kernel, &LaunchArgs, nullptr, 0, |
| nullptr, nullptr)); |
| |
| ASSERT_SUCCESS(olSyncQueue(Queue)); |
| } |
| |
| TEST_P(olLaunchKernelMultiArgsTest, Success) { |
| void *Mem; |
| ASSERT_SUCCESS(olMemAlloc(Device, OL_ALLOC_TYPE_MANAGED, |
| LaunchArgs.GroupSize.x * sizeof(int), &Mem)); |
| |
| char A = 3; |
| int *B = (int *)Mem; |
| short C = 5; |
| |
| void *ArgPtrs[] = {&A, &B, &C}; |
| size_t ArgSizes[] = {sizeof(A), sizeof(B), sizeof(C)}; |
| |
| ASSERT_SUCCESS(olLaunchKernel(Queue, Device, Kernel, &LaunchArgs, nullptr, |
| std::size(ArgPtrs), ArgPtrs, ArgSizes)); |
| |
| ASSERT_SUCCESS(olSyncQueue(Queue)); |
| |
| int *Data = (int *)Mem; |
| for (uint32_t i = 0; i < LaunchArgs.GroupSize.x; i++) |
| ASSERT_EQ(Data[i], A + C + static_cast<int>(i)); |
| |
| ASSERT_SUCCESS(olMemFree(Mem)); |
| } |
| |
| struct Foo { |
| uint32_t a; |
| uint32_t b; |
| }; |
| |
| TEST_P(olLaunchKernelCompositeTest, Success) { |
| void *Mem; |
| ASSERT_SUCCESS(olMemAlloc(Device, OL_ALLOC_TYPE_MANAGED, |
| LaunchArgs.GroupSize.x * sizeof(uint32_t), &Mem)); |
| |
| uint8_t N = 1; |
| Foo F{2, 3}; |
| uint32_t *Out = (uint32_t *)Mem; |
| |
| void *ArgPtrs[] = {&N, &F, &Out}; |
| size_t ArgSizes[] = {sizeof(N), sizeof(F), sizeof(Out)}; |
| |
| ASSERT_SUCCESS(olLaunchKernel(Queue, Device, Kernel, &LaunchArgs, nullptr, |
| std::size(ArgPtrs), ArgPtrs, ArgSizes)); |
| |
| ASSERT_SUCCESS(olSyncQueue(Queue)); |
| |
| uint32_t *Data = (uint32_t *)Mem; |
| for (uint32_t i = 0; i < LaunchArgs.GroupSize.x; i++) |
| ASSERT_EQ(Data[i], N + F.a + F.b + i); |
| |
| ASSERT_SUCCESS(olMemFree(Mem)); |
| } |
| |
| TEST_P(olLaunchKernelFooTest, SuccessSynchronous) { |
| void *Mem; |
| ASSERT_SUCCESS(olMemAlloc(Device, OL_ALLOC_TYPE_MANAGED, |
| LaunchArgs.GroupSize.x * sizeof(uint32_t), &Mem)); |
| |
| void *ArgPtrs[] = {&Mem}; |
| size_t ArgSizes[] = {sizeof(Mem)}; |
| |
| ASSERT_SUCCESS(olLaunchKernel(nullptr, Device, Kernel, &LaunchArgs, nullptr, |
| std::size(ArgPtrs), ArgPtrs, ArgSizes)); |
| |
| uint32_t *Data = (uint32_t *)Mem; |
| for (uint32_t i = 0; i < 64; i++) { |
| ASSERT_EQ(Data[i], i); |
| } |
| |
| ASSERT_SUCCESS(olMemFree(Mem)); |
| } |
| |
| TEST_P(olLaunchKernelByteTest, Success) { |
| unsigned char C = 42; |
| |
| void *ArgPtrs[] = {&C}; |
| size_t ArgSizes[] = {sizeof(C)}; |
| |
| ASSERT_SUCCESS(olLaunchKernel(Queue, Device, Kernel, &LaunchArgs, nullptr, |
| std::size(ArgPtrs), ArgPtrs, ArgSizes)); |
| |
| ASSERT_SUCCESS(olSyncQueue(Queue)); |
| } |
| |
| TEST_P(olLaunchKernelLocalMemTest, Success) { |
| SKIP_KNOWN_FAILURE(LevelZero{"unsupported DynSharedMemory"}); |
| |
| LaunchArgs.NumGroups.x = 4; |
| LaunchArgs.DynSharedMemory = 64 * sizeof(uint32_t); |
| |
| void *Mem; |
| ASSERT_SUCCESS(olMemAlloc(Device, OL_ALLOC_TYPE_MANAGED, |
| LaunchArgs.GroupSize.x * LaunchArgs.NumGroups.x * |
| sizeof(uint32_t), |
| &Mem)); |
| |
| void *ArgPtrs[] = {&Mem}; |
| size_t ArgSizes[] = {sizeof(Mem)}; |
| |
| ASSERT_SUCCESS(olLaunchKernel(Queue, Device, Kernel, &LaunchArgs, nullptr, |
| std::size(ArgPtrs), ArgPtrs, ArgSizes)); |
| |
| ASSERT_SUCCESS(olSyncQueue(Queue)); |
| |
| uint32_t *Data = (uint32_t *)Mem; |
| for (uint32_t i = 0; i < LaunchArgs.GroupSize.x * LaunchArgs.NumGroups.x; i++) |
| ASSERT_EQ(Data[i], (i % 64) * 2); |
| |
| ASSERT_SUCCESS(olMemFree(Mem)); |
| } |
| |
| TEST_P(olLaunchKernelLocalMemReductionTest, Success) { |
| SKIP_KNOWN_FAILURE(LevelZero{"unsupported DynSharedMemory"}); |
| |
| LaunchArgs.NumGroups.x = 4; |
| LaunchArgs.DynSharedMemory = 64 * sizeof(uint32_t); |
| |
| void *Mem; |
| ASSERT_SUCCESS(olMemAlloc(Device, OL_ALLOC_TYPE_MANAGED, |
| LaunchArgs.NumGroups.x * sizeof(uint32_t), &Mem)); |
| |
| void *ArgPtrs[] = {&Mem}; |
| size_t ArgSizes[] = {sizeof(Mem)}; |
| |
| ASSERT_SUCCESS(olLaunchKernel(Queue, Device, Kernel, &LaunchArgs, nullptr, |
| std::size(ArgPtrs), ArgPtrs, ArgSizes)); |
| |
| ASSERT_SUCCESS(olSyncQueue(Queue)); |
| |
| uint32_t *Data = (uint32_t *)Mem; |
| for (uint32_t i = 0; i < LaunchArgs.NumGroups.x; i++) |
| ASSERT_EQ(Data[i], 2 * LaunchArgs.GroupSize.x); |
| |
| ASSERT_SUCCESS(olMemFree(Mem)); |
| } |
| |
| TEST_P(olLaunchKernelLocalMemStaticTest, Success) { |
| SKIP_KNOWN_FAILURE(LevelZero{"unsupported DynSharedMemory"}); |
| |
| LaunchArgs.NumGroups.x = 4; |
| LaunchArgs.DynSharedMemory = 0; |
| |
| void *Mem; |
| ASSERT_SUCCESS(olMemAlloc(Device, OL_ALLOC_TYPE_MANAGED, |
| LaunchArgs.NumGroups.x * sizeof(uint32_t), &Mem)); |
| |
| void *ArgPtrs[] = {&Mem}; |
| size_t ArgSizes[] = {sizeof(Mem)}; |
| |
| ASSERT_SUCCESS(olLaunchKernel(Queue, Device, Kernel, &LaunchArgs, nullptr, |
| std::size(ArgPtrs), ArgPtrs, ArgSizes)); |
| |
| ASSERT_SUCCESS(olSyncQueue(Queue)); |
| |
| uint32_t *Data = (uint32_t *)Mem; |
| for (uint32_t i = 0; i < LaunchArgs.NumGroups.x; i++) |
| ASSERT_EQ(Data[i], 2 * LaunchArgs.GroupSize.x); |
| |
| ASSERT_SUCCESS(olMemFree(Mem)); |
| } |
| |
| // The test intends to verify the correctness of the current implementation of |
| // the event synchronisation. |
| TEST_P(olLaunchKernelSingleCounterSyncEventTest, SuccessSyncEvent) { |
| void *InitValuePassed; |
| void *ResNum; |
| |
| size_t Size = sizeof(uint32_t); |
| |
| ASSERT_SUCCESS( |
| olMemAlloc(Device, OL_ALLOC_TYPE_DEVICE, Size, &InitValuePassed)); |
| ASSERT_SUCCESS(olMemAlloc(Device, OL_ALLOC_TYPE_DEVICE, Size, &ResNum)); |
| |
| uint32_t HostInitVal = 0; |
| ASSERT_SUCCESS( |
| olMemcpy(Queue, InitValuePassed, Device, &HostInitVal, Host, Size)); |
| ASSERT_SUCCESS(olMemcpy(Queue, ResNum, Device, &HostInitVal, Host, Size)); |
| ASSERT_SUCCESS(olSyncQueue(Queue)); |
| |
| // The execution time of the provided kernel should be high enough to ensure |
| // that the read value of the final result is not correct without explicit |
| // synchronization: explicit waiting for the event following the submitted |
| // operation. LoopRange is arbitrarily set with the goal of prolonging the |
| // kernel execution through a high number of loop iterations. In each |
| // iteration, NumberToAdd is added to the final sum, which is stored in |
| // ResNum. |
| int32_t LoopRange = 1000000; |
| int32_t NumberToAdd = 2; |
| |
| ArgsSingleCounter Args{LoopRange, NumberToAdd, (uint32_t *)InitValuePassed, |
| (uint32_t *)ResNum}; |
| |
| void *ArgPtrs[] = {&Args.InitLoop, &Args.Addend, &Args.InitVal, &Args.Out}; |
| size_t ArgSizes[] = {sizeof(Args.InitLoop), sizeof(Args.Addend), |
| sizeof(Args.InitVal), sizeof(Args.Out)}; |
| |
| ASSERT_SUCCESS(olLaunchKernel(Queue, Device, Kernel, &LaunchArgs, nullptr, |
| std::size(ArgPtrs), ArgPtrs, ArgSizes)); |
| |
| uint32_t FinalResVal = 0; |
| ASSERT_SUCCESS(olMemcpy(Queue, &FinalResVal, Host, ResNum, Device, Size)); |
| |
| ol_event_handle_t Event = nullptr; |
| ASSERT_SUCCESS(olCreateEvent(Queue, OL_EVENT_FLAGS_NONE, &Event)); |
| ASSERT_SUCCESS(olSyncEvent(Event)); |
| |
| ASSERT_EQ(FinalResVal, NumberToAdd * LoopRange); |
| |
| ASSERT_SUCCESS(olMemFree(InitValuePassed)); |
| ASSERT_SUCCESS(olMemFree(ResNum)); |
| } |
| |
| // The test checks the correctness of the synchronization between queues using |
| // events. Enqueueing the kernel on the queue `Q1` should produce `Result1`, |
| // which is used as an initial value for the queue `Q2`. Therefore, before |
| // executing any future work, `Q2` should wait for the event `Event1`, which is |
| // created after submitting work on `Q1`. The required synchronization is |
| // ensured by `olWaitEvents(Q2, &Event1, 1)`. If `Q2` uses the value passed as |
| // `Result1` before the work on `Q1` has completed, the result of the kernel |
| // enqueued on `Q2` would be incorrect. |
| TEST_P(olLaunchKernelSingleCounterSyncEventTest, SuccessTwoQueues) { |
| void *InitValuePassed; |
| void *ResNum1; |
| void *ResNum2; |
| |
| size_t Size = sizeof(uint32_t); |
| |
| ASSERT_SUCCESS( |
| olMemAlloc(Device, OL_ALLOC_TYPE_DEVICE, Size, &InitValuePassed)); |
| ASSERT_SUCCESS(olMemAlloc(Device, OL_ALLOC_TYPE_DEVICE, Size, &ResNum1)); |
| ASSERT_SUCCESS(olMemAlloc(Device, OL_ALLOC_TYPE_DEVICE, Size, &ResNum2)); |
| |
| uint32_t HostInitVal = 0; |
| ASSERT_SUCCESS( |
| olMemcpy(Queue, InitValuePassed, Device, &HostInitVal, Host, Size)); |
| ASSERT_SUCCESS(olMemcpy(Queue, ResNum1, Device, &HostInitVal, Host, Size)); |
| ASSERT_SUCCESS(olMemcpy(Queue, ResNum2, Device, &HostInitVal, Host, Size)); |
| ASSERT_SUCCESS(olSyncQueue(Queue)); |
| |
| ol_queue_handle_t Queue2 = nullptr; |
| ASSERT_SUCCESS(olCreateQueue(Device, &Queue2)); |
| |
| // For the explanation of the reasoning behind particular values assigned to |
| // parameters, see the comment in the Success test from the same test suite |
| int32_t LoopRange = 1000000; |
| int32_t NumberToAdd = 2; |
| |
| ArgsSingleCounter Args{LoopRange, NumberToAdd, (uint32_t *)InitValuePassed, |
| (uint32_t *)ResNum1}; |
| void *ArgPtrs[] = {&Args.InitLoop, &Args.Addend, &Args.InitVal, &Args.Out}; |
| size_t ArgSizes[] = {sizeof(Args.InitLoop), sizeof(Args.Addend), |
| sizeof(Args.InitVal), sizeof(Args.Out)}; |
| ASSERT_SUCCESS(olLaunchKernel(Queue, Device, Kernel, &LaunchArgs, nullptr, |
| std::size(ArgPtrs), ArgPtrs, ArgSizes)); |
| |
| ol_event_handle_t Event = nullptr; |
| ASSERT_SUCCESS(olCreateEvent(Queue, OL_EVENT_FLAGS_NONE, &Event)); |
| ASSERT_SUCCESS(olWaitEvents(Queue2, &Event, 1)); |
| ArgsSingleCounter Args2{LoopRange, NumberToAdd, (uint32_t *)ResNum1, |
| (uint32_t *)ResNum2}; |
| void *ArgPtrs2[] = {&Args2.InitLoop, &Args2.Addend, &Args2.InitVal, |
| &Args2.Out}; |
| |
| // At the beginning of the kernel, ResNum from the first queue is saved |
| // locally as the initial value. If operations enqueued before Event have not |
| // been completed by the time the kernel is executed using the second queue, |
| // the FinalResVal would be incorrect. |
| ASSERT_SUCCESS(olLaunchKernel(Queue2, Device, Kernel, &LaunchArgs, nullptr, |
| std::size(ArgPtrs2), ArgPtrs2, ArgSizes)); |
| |
| ASSERT_SUCCESS(olSyncQueue(Queue2)); |
| uint32_t FinalResVal = 0; |
| ASSERT_SUCCESS(olMemcpy(Queue2, &FinalResVal, Host, ResNum2, Device, Size)); |
| ASSERT_SUCCESS(olSyncQueue(Queue2)); |
| |
| ASSERT_EQ(FinalResVal, 2 * NumberToAdd * LoopRange); |
| |
| ASSERT_SUCCESS(olMemFree(InitValuePassed)); |
| ASSERT_SUCCESS(olMemFree(ResNum1)); |
| ASSERT_SUCCESS(olMemFree(ResNum2)); |
| } |
| |
| TEST_P(olLaunchKernelGlobalTest, Success) { |
| void *Mem; |
| ASSERT_SUCCESS(olMemAlloc(Device, OL_ALLOC_TYPE_MANAGED, |
| LaunchArgs.GroupSize.x * sizeof(uint32_t), &Mem)); |
| |
| void *ArgPtrs[] = {&Mem}; |
| size_t ArgSizes[] = {sizeof(Mem)}; |
| |
| ASSERT_SUCCESS(olLaunchKernel(Queue, Device, Kernels[0], &LaunchArgs, nullptr, |
| 0, nullptr, nullptr)); |
| ASSERT_SUCCESS(olSyncQueue(Queue)); |
| ASSERT_SUCCESS(olLaunchKernel(Queue, Device, Kernels[1], &LaunchArgs, nullptr, |
| std::size(ArgPtrs), ArgPtrs, ArgSizes)); |
| ASSERT_SUCCESS(olSyncQueue(Queue)); |
| |
| uint32_t *Data = (uint32_t *)Mem; |
| for (uint32_t i = 0; i < 64; i++) { |
| ASSERT_EQ(Data[i], i * 2); |
| } |
| |
| ASSERT_SUCCESS(olMemFree(Mem)); |
| } |
| |
| TEST_P(olLaunchKernelGlobalTest, InvalidNotAKernel) { |
| ol_symbol_handle_t Global = nullptr; |
| ASSERT_SUCCESS( |
| olGetSymbol(Program, "global", OL_SYMBOL_KIND_GLOBAL_VARIABLE, &Global)); |
| ASSERT_ERROR(OL_ERRC_SYMBOL_KIND, |
| olLaunchKernel(Queue, Device, Global, &LaunchArgs, nullptr, 0, |
| nullptr, nullptr)); |
| } |
| |
| TEST_P(olLaunchKernelGlobalCtorTest, Success) { |
| SKIP_KNOWN_FAILURE(LevelZero{"unsupported feature"}); |
| |
| void *Mem; |
| ASSERT_SUCCESS(olMemAlloc(Device, OL_ALLOC_TYPE_MANAGED, |
| LaunchArgs.GroupSize.x * sizeof(uint32_t), &Mem)); |
| |
| void *ArgPtrs[] = {&Mem}; |
| size_t ArgSizes[] = {sizeof(Mem)}; |
| |
| ASSERT_SUCCESS(olLaunchKernel(Queue, Device, Kernel, &LaunchArgs, nullptr, |
| std::size(ArgPtrs), ArgPtrs, ArgSizes)); |
| ASSERT_SUCCESS(olSyncQueue(Queue)); |
| |
| uint32_t *Data = (uint32_t *)Mem; |
| for (uint32_t i = 0; i < 64; i++) { |
| ASSERT_EQ(Data[i], i + 100); |
| } |
| |
| ASSERT_SUCCESS(olMemFree(Mem)); |
| } |
| |
| TEST_P(olLaunchKernelGlobalDtorTest, Success) { |
| // TODO: We can't inspect the result of a destructor yet, once we |
| // find/implement a way, update this test. For now we just check that nothing |
| // crashes |
| ASSERT_SUCCESS(olLaunchKernel(Queue, Device, Kernel, &LaunchArgs, nullptr, 0, |
| nullptr, nullptr)); |
| ASSERT_SUCCESS(olSyncQueue(Queue)); |
| } |
| |
| TEST_P(olLaunchKernelGridSizeTest, Success) { |
| void *Mem; |
| ASSERT_SUCCESS( |
| olMemAlloc(Device, OL_ALLOC_TYPE_MANAGED, 6 * sizeof(uint32_t), &Mem)); |
| |
| uint32_t *NumBlocks = static_cast<uint32_t *>(Mem); |
| uint32_t *NumThreads = static_cast<uint32_t *>(Mem) + 3; |
| |
| void *ArgPtrs[] = {&NumBlocks, &NumThreads}; |
| size_t ArgSizes[] = {sizeof(NumBlocks), sizeof(NumThreads)}; |
| |
| const uint32_t BaseBlocks[3] = {64, 16, 8}; |
| const uint32_t BaseThreads[3] = {32, 4, 2}; |
| |
| for (uint32_t Dim = 1; Dim <= 3; ++Dim) { |
| NumBlocks[0] = NumBlocks[1] = NumBlocks[2] = 0; |
| NumThreads[0] = NumThreads[1] = NumThreads[2] = 0; |
| |
| LaunchArgs.Dimensions = 3; |
| LaunchArgs.NumGroups = {BaseBlocks[0], Dim >= 2 ? BaseBlocks[1] : 1, |
| Dim >= 3 ? BaseBlocks[2] : 1}; |
| LaunchArgs.GroupSize = {BaseThreads[0], Dim >= 2 ? BaseThreads[1] : 1, |
| Dim >= 3 ? BaseThreads[2] : 1}; |
| |
| ASSERT_SUCCESS(olLaunchKernel(Queue, Device, Kernel, &LaunchArgs, nullptr, |
| std::size(ArgPtrs), ArgPtrs, ArgSizes)); |
| |
| ASSERT_SUCCESS(olSyncQueue(Queue)); |
| |
| ASSERT_EQ(NumBlocks[0], LaunchArgs.NumGroups.x); |
| ASSERT_EQ(NumBlocks[1], LaunchArgs.NumGroups.y); |
| ASSERT_EQ(NumBlocks[2], LaunchArgs.NumGroups.z); |
| ASSERT_EQ(NumThreads[0], LaunchArgs.GroupSize.x); |
| ASSERT_EQ(NumThreads[1], LaunchArgs.GroupSize.y); |
| ASSERT_EQ(NumThreads[2], LaunchArgs.GroupSize.z); |
| } |
| |
| ASSERT_SUCCESS(olMemFree(Mem)); |
| } |