This PR changes olLaunchKernel to accept an array of pointers to
arguments:
```
void *ArgPtrs[] = {&A, &B, &C};
size_t ArgSizes[] = {sizeof(A), sizeof(B), sizeof(C)};
olLaunchKernel(Queue, Device, Kernel, &LaunchArgs, std::size(ArgPtrs), ArgPtrs, ArgSizes);
```
The newly proposed interface is implementable by existing and
anticipated
backends, is familiar to CUDA programmers, eliminates the extraneous
construction of a contiguous arguments buffer, replacing it with
constructing
an array of pointers, sidesteps the alignment requirements, does not
require reading program image metadata where it's impractical, and
enables
a compliant SYCL implementation to be built on top of it.
The ArgSizes array is required to support OpenCL, which does not have
native support for launching a kernel with an argument pointer array, or
a reliable way of retrieving argument sizes for a kernel.
## Mapping the proposed API to backends
CUDA and Level-Zero both support accepting an array of pointers to
kernel arguments, through `cuLaunchKernel`
(https://docs.nvidia.com/cuda/cuda-driver-api/group__CUDA__EXEC.html#group__CUDA__EXEC_1gb8f3dc3031b40da29d5f9a7139e52e15)
and `zeCommandListAppendLaunchKernelWithArguments`
(https://oneapi-src.github.io/level-zero-spec/level-zero/latest/core/api.html#zecommandlistappendlaunchkernelwitharguments)
respectively.
For OpenCL, which requires the kernel arguments to be set separately
from the kernel launch, a potential implementation can extract the
number of arguments from the OpenCL API, and then iterate over the
argument pointer and size arrays:
```
cl_uint num_args = 0;
cl_int err = clGetKernelInfo(kernel, CL_KERNEL_NUM_ARGS,
sizeof(num_args), &num_args, NULL);
for (cl_uint i = 0; i < num_args; ++i) {
clSetKernelArg(kernel, i, ArgSizes[i], ArgPtrs[i]);
}
```
The AMDGPU plugin needs to construct a contiguous buffer using the array
of argument pointers. To do this conversion, we need to have offsets
at which to place the arguments. Here, luckily, as mentioned before,
the AMD plugin already reads kernel metadata from the program image.
The implementation simply retrieves size and offset for each argument
from the kernel image, and then uses it to populate a buffer.
460 lines
16 KiB
C++
460 lines
16 KiB
C++
//===------- 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)
|
|
|
|
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));
|
|
}
|