Skip to content

Commit e8db775

Browse files
author
Ewan Crawford
committed
Add CTS tests
Introduce command-buffer CTS tests for command event synchronization
1 parent 90d1d7d commit e8db775

File tree

2 files changed

+341
-0
lines changed

2 files changed

+341
-0
lines changed

test/conformance/exp_command_buffer/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,5 @@ add_conformance_test_with_kernels_environment(exp_command_buffer
1212
release.cpp
1313
retain.cpp
1414
invalid_update.cpp
15+
event_sync_kernel_command.cpp
1516
)
Lines changed: 340 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,340 @@
1+
// Copyright (C) 2024 Intel Corporation
2+
// Part of the Unified-Runtime Project, under the Apache License v2.0 with LLVM Exceptions.
3+
// See LICENSE.TXT
4+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
5+
6+
#include "fixtures.h"
7+
#include <cstring>
8+
9+
// Tests kernel commands using ur events for synchronization work as expected
10+
struct KernelCommandEventSyncTest
11+
: uur::command_buffer::urCommandBufferExpExecutionTest {
12+
13+
void SetUp() override {
14+
program_name = "saxpy_usm";
15+
UUR_RETURN_ON_FATAL_FAILURE(urCommandBufferExpExecutionTest::SetUp());
16+
17+
ur_bool_t event_support = false;
18+
ASSERT_SUCCESS(urDeviceGetInfo(
19+
device, UR_DEVICE_INFO_COMMAND_BUFFER_EVENT_SUPPORT_EXP,
20+
sizeof(ur_bool_t), &event_support, nullptr));
21+
if (!event_support) {
22+
GTEST_SKIP() << "External event sync is not supported by device.";
23+
}
24+
25+
for (auto &device_ptr : device_ptrs) {
26+
ASSERT_SUCCESS(urUSMDeviceAlloc(context, device, nullptr, nullptr,
27+
allocation_size, &device_ptr));
28+
ASSERT_NE(device_ptr, nullptr);
29+
}
30+
31+
// Index 0 is output
32+
ASSERT_SUCCESS(
33+
urKernelSetArgPointer(kernel, 0, nullptr, device_ptrs[2]));
34+
// Index 1 is A
35+
ASSERT_SUCCESS(urKernelSetArgValue(kernel, 1, sizeof(A), nullptr, &A));
36+
// Index 2 is X
37+
ASSERT_SUCCESS(
38+
urKernelSetArgPointer(kernel, 2, nullptr, device_ptrs[0]));
39+
// Index 3 is Y
40+
ASSERT_SUCCESS(
41+
urKernelSetArgPointer(kernel, 3, nullptr, device_ptrs[1]));
42+
43+
// Create second command-buffer
44+
ASSERT_SUCCESS(urCommandBufferCreateExp(context, device, nullptr,
45+
&second_cmd_buf_handle));
46+
ASSERT_NE(second_cmd_buf_handle, nullptr);
47+
}
48+
49+
virtual void TearDown() override {
50+
for (auto &device_ptr : device_ptrs) {
51+
if (device_ptr) {
52+
EXPECT_SUCCESS(urUSMFree(context, device_ptr));
53+
}
54+
}
55+
56+
for (auto &event : external_events) {
57+
if (event) {
58+
EXPECT_SUCCESS(urEventRelease(event));
59+
}
60+
}
61+
62+
if (second_cmd_buf_handle) {
63+
EXPECT_SUCCESS(urCommandBufferReleaseExp(second_cmd_buf_handle));
64+
}
65+
66+
UUR_RETURN_ON_FATAL_FAILURE(
67+
urCommandBufferExpExecutionTest::TearDown());
68+
}
69+
70+
// First two shared points are inputs to be tested, last is an output
71+
// generated by test command.
72+
std::array<void *, 3> device_ptrs = {nullptr, nullptr, nullptr};
73+
std::array<ur_event_handle_t, 2> external_events = {nullptr, nullptr};
74+
std::array<ur_exp_command_buffer_sync_point_t, 2> sync_points = {0, 0};
75+
ur_exp_command_buffer_handle_t second_cmd_buf_handle = nullptr;
76+
static constexpr size_t elements = 64;
77+
static constexpr size_t allocation_size = sizeof(uint32_t) * elements;
78+
static constexpr size_t A = 2;
79+
};
80+
81+
UUR_INSTANTIATE_DEVICE_TEST_SUITE_P(KernelCommandEventSyncTest);
82+
83+
// Tests a Saxpy kernel as follows:
84+
// 1. Enqueue work initializing X data on a queue to get an external ur event
85+
// to wait on.
86+
// 2. Append a command-buffer command initializing Y data, to be a dependency
87+
// of kernel command to test.
88+
// 3. Append a kernel command with both sync point and external
89+
// event dependencies on initialization of X and Y. Returning both a sync
90+
// point and event to synchronize with completion of command.
91+
// 4. Append a command-buffer command that depends on sync point from
92+
// tested command.
93+
// 5. Finalize and enqueue command-buffer.
94+
// 6. Enqueue a queue command waiting on event returned from command.
95+
// 7. Verify results.
96+
TEST_P(KernelCommandEventSyncTest, Basic) {
97+
// Initialize data X with queue submission
98+
uint32_t patternX = 42;
99+
ASSERT_SUCCESS(urEnqueueUSMFill(queue, device_ptrs[0], sizeof(patternX),
100+
&patternX, allocation_size, 0, nullptr,
101+
&external_events[0]));
102+
103+
// Initialize data Y with command-buffer command
104+
uint32_t patternY = 0xA;
105+
ASSERT_SUCCESS(urCommandBufferAppendUSMFillExp(
106+
cmd_buf_handle, device_ptrs[1], &patternY, sizeof(patternY),
107+
allocation_size, 0, nullptr, 0, nullptr, &sync_points[0], nullptr,
108+
nullptr));
109+
110+
// Kernel command for SAXPY waiting on command and signal event
111+
ASSERT_SUCCESS(urCommandBufferAppendKernelLaunchExp(
112+
cmd_buf_handle, kernel, 1, nullptr, &elements, nullptr, 1,
113+
&sync_points[0], 1, &external_events[0], &sync_points[1], nullptr,
114+
nullptr));
115+
116+
// command-buffer command that reads output to host
117+
std::array<uint32_t, elements> host_command_ptr{};
118+
ASSERT_SUCCESS(urCommandBufferAppendUSMMemcpyExp(
119+
cmd_buf_handle, host_command_ptr.data(), device_ptrs[2],
120+
allocation_size, 1, &sync_points[1], 0, nullptr, nullptr, nullptr,
121+
nullptr));
122+
ASSERT_SUCCESS(urCommandBufferFinalizeExp(cmd_buf_handle));
123+
124+
ASSERT_SUCCESS(
125+
urCommandBufferEnqueueExp(cmd_buf_handle, queue, 0, nullptr, nullptr));
126+
127+
// Queue command that reads output to host
128+
std::array<uint32_t, elements> host_enqueue_ptr{};
129+
ASSERT_SUCCESS(urEnqueueUSMMemcpy(queue, false, host_enqueue_ptr.data(),
130+
device_ptrs[2], allocation_size, 1,
131+
&external_events[1], nullptr));
132+
133+
ASSERT_SUCCESS(urQueueFinish(queue));
134+
135+
for (size_t i = 0; i < elements; i++) {
136+
auto ref = (patternX * A) + patternY;
137+
ASSERT_EQ(host_command_ptr[i], ref);
138+
ASSERT_EQ(host_enqueue_ptr[i], ref);
139+
}
140+
}
141+
142+
// Tests using events to synchronize between command-buffers:
143+
// 1. Initialize data in command-buffer A, returning event 1.
144+
// 2. Use data in command-buffer B, waiting on event 1 and signaling event 2
145+
// 3. Read back data to host in command-buffer A, waiting on event 1.
146+
// 4. Finalize and submit command-buffer, verify results
147+
TEST_P(KernelCommandEventSyncTest, InterCommandBuffer) {
148+
// Initialize data Y with command-buffer A command
149+
uint32_t patternX = 42;
150+
ASSERT_SUCCESS(urCommandBufferAppendUSMFillExp(
151+
cmd_buf_handle, device_ptrs[0], &patternX, sizeof(patternX),
152+
allocation_size, 0, nullptr, 0, nullptr, &sync_points[0], nullptr,
153+
nullptr));
154+
155+
// Initialize data Y with command-buffer A command
156+
uint32_t patternY = 0xA;
157+
ASSERT_SUCCESS(urCommandBufferAppendUSMFillExp(
158+
cmd_buf_handle, device_ptrs[1], &patternY, sizeof(patternY),
159+
allocation_size, 1, &sync_points[0], 0, nullptr, &sync_points[1],
160+
&external_events[0], nullptr));
161+
162+
// Run SAXPY kernel with command-buffer B command, waiting on an event.
163+
ASSERT_SUCCESS(urCommandBufferAppendKernelLaunchExp(
164+
second_cmd_buf_handle, kernel, 1, nullptr, &elements, nullptr, 0,
165+
nullptr, 1, &external_events[0], nullptr, &external_events[1],
166+
nullptr));
167+
168+
// Command-buffer A command that reads output to host, waiting on an event
169+
std::array<uint32_t, elements> host_command_ptr{};
170+
ASSERT_SUCCESS(urCommandBufferAppendUSMMemcpyExp(
171+
cmd_buf_handle, host_command_ptr.data(), device_ptrs[2],
172+
allocation_size, 1, &sync_points[1], 1, &external_events[1], nullptr,
173+
nullptr, nullptr));
174+
175+
// Finalize command-buffers
176+
ASSERT_SUCCESS(urCommandBufferFinalizeExp(cmd_buf_handle));
177+
ASSERT_SUCCESS(urCommandBufferFinalizeExp(second_cmd_buf_handle));
178+
179+
// Submit command-buffers
180+
ASSERT_SUCCESS(urCommandBufferEnqueueExp(second_cmd_buf_handle, queue, 0,
181+
nullptr, nullptr));
182+
ASSERT_SUCCESS(
183+
urCommandBufferEnqueueExp(cmd_buf_handle, queue, 0, nullptr, nullptr));
184+
185+
// Verify execution
186+
ASSERT_SUCCESS(urQueueFinish(queue));
187+
for (size_t i = 0; i < elements; i++) {
188+
auto ref = (patternX * A) + patternY;
189+
ASSERT_EQ(host_command_ptr[i], ref);
190+
}
191+
}
192+
193+
struct KernelCommandEventSyncUpdateTest
194+
: uur::command_buffer::urUpdatableCommandBufferExpExecutionTest {
195+
void SetUp() override {
196+
program_name = "saxpy_usm";
197+
UUR_RETURN_ON_FATAL_FAILURE(
198+
urUpdatableCommandBufferExpExecutionTest::SetUp());
199+
200+
ur_bool_t event_support = false;
201+
ASSERT_SUCCESS(urDeviceGetInfo(
202+
device, UR_DEVICE_INFO_COMMAND_BUFFER_EVENT_SUPPORT_EXP,
203+
sizeof(ur_bool_t), &event_support, nullptr));
204+
if (!event_support) {
205+
GTEST_SKIP() << "External event sync is not supported by device.";
206+
}
207+
208+
for (auto &device_ptr : device_ptrs) {
209+
ASSERT_SUCCESS(urUSMDeviceAlloc(context, device, nullptr, nullptr,
210+
allocation_size, &device_ptr));
211+
ASSERT_NE(device_ptr, nullptr);
212+
}
213+
214+
// Index 0 is output
215+
ASSERT_SUCCESS(
216+
urKernelSetArgPointer(kernel, 0, nullptr, device_ptrs[2]));
217+
// Index 1 is A
218+
ASSERT_SUCCESS(urKernelSetArgValue(kernel, 1, sizeof(A), nullptr, &A));
219+
// Index 2 is X
220+
ASSERT_SUCCESS(
221+
urKernelSetArgPointer(kernel, 2, nullptr, device_ptrs[0]));
222+
// Index 3 is Y
223+
ASSERT_SUCCESS(
224+
urKernelSetArgPointer(kernel, 3, nullptr, device_ptrs[1]));
225+
}
226+
227+
virtual void TearDown() override {
228+
for (auto &device_ptr : device_ptrs) {
229+
if (device_ptr) {
230+
EXPECT_SUCCESS(urUSMFree(context, device_ptr));
231+
}
232+
}
233+
234+
for (auto &event : external_events) {
235+
if (event) {
236+
EXPECT_SUCCESS(urEventRelease(event));
237+
}
238+
}
239+
240+
if (command_handle) {
241+
EXPECT_SUCCESS(urCommandBufferReleaseCommandExp(command_handle));
242+
}
243+
244+
UUR_RETURN_ON_FATAL_FAILURE(
245+
urUpdatableCommandBufferExpExecutionTest::TearDown());
246+
}
247+
248+
// First two shared points are inputs to be tested, last is an output
249+
// generated by test command.
250+
std::array<void *, 3> device_ptrs = {nullptr, nullptr, nullptr};
251+
std::array<ur_event_handle_t, 4> external_events = {nullptr, nullptr,
252+
nullptr, nullptr};
253+
std::array<ur_exp_command_buffer_sync_point_t, 2> sync_points = {0, 0};
254+
ur_exp_command_buffer_command_handle_t command_handle = nullptr;
255+
static constexpr size_t elements = 64;
256+
static constexpr size_t allocation_size = sizeof(uint32_t) * elements;
257+
static constexpr size_t A = 2;
258+
};
259+
260+
UUR_INSTANTIATE_DEVICE_TEST_SUITE_P(KernelCommandEventSyncUpdateTest);
261+
262+
// Tests updating the signal and wait event dependencies of the saxpy
263+
// command in a command-buffer.
264+
TEST_P(KernelCommandEventSyncUpdateTest, Basic) {
265+
// Initialize data X with queue submission
266+
uint32_t patternX = 42;
267+
ASSERT_SUCCESS(urEnqueueUSMFill(queue, device_ptrs[0], sizeof(patternX),
268+
&patternX, allocation_size, 0, nullptr,
269+
&external_events[0]));
270+
271+
// Initialize data Y with command-buffer command
272+
uint32_t patternY = 0xA;
273+
ASSERT_SUCCESS(urCommandBufferAppendUSMFillExp(
274+
updatable_cmd_buf_handle, device_ptrs[1], &patternY, sizeof(patternY),
275+
allocation_size, 0, nullptr, 0, nullptr, &sync_points[0], nullptr,
276+
nullptr));
277+
278+
// Kernel command for SAXPY waiting on command and signal event
279+
ASSERT_SUCCESS(urCommandBufferAppendKernelLaunchExp(
280+
updatable_cmd_buf_handle, kernel, 1, nullptr, &elements, nullptr, 1,
281+
&sync_points[0], 1, &external_events[0], &sync_points[1], nullptr,
282+
&command_handle));
283+
284+
// command-buffer command that reads output to host
285+
std::array<uint32_t, elements> host_command_ptr{};
286+
ASSERT_SUCCESS(urCommandBufferAppendUSMMemcpyExp(
287+
updatable_cmd_buf_handle, host_command_ptr.data(), device_ptrs[2],
288+
allocation_size, 1, &sync_points[1], 0, nullptr, nullptr, nullptr,
289+
nullptr));
290+
ASSERT_SUCCESS(urCommandBufferFinalizeExp(updatable_cmd_buf_handle));
291+
292+
ASSERT_SUCCESS(urCommandBufferEnqueueExp(updatable_cmd_buf_handle, queue, 0,
293+
nullptr, nullptr));
294+
295+
// Queue command that reads output to host
296+
std::array<uint32_t, elements> host_enqueue_ptr{};
297+
ASSERT_SUCCESS(urEnqueueUSMMemcpy(queue, false, host_enqueue_ptr.data(),
298+
device_ptrs[2], allocation_size, 1,
299+
&external_events[1], nullptr));
300+
301+
ASSERT_SUCCESS(urQueueFinish(queue));
302+
303+
for (size_t i = 0; i < elements; i++) {
304+
auto ref = (patternX * A) + patternY;
305+
ASSERT_EQ(host_command_ptr[i], ref);
306+
ASSERT_EQ(host_enqueue_ptr[i], ref);
307+
}
308+
309+
// Reset output data
310+
std::memset(host_command_ptr.data(), 0, allocation_size);
311+
std::memset(host_enqueue_ptr.data(), 0, allocation_size);
312+
313+
// Set data X to new value with queue submission
314+
patternX = -42;
315+
ASSERT_SUCCESS(urEnqueueUSMFill(queue, device_ptrs[0], sizeof(patternX),
316+
&patternX, allocation_size, 0, nullptr,
317+
&external_events[2]));
318+
319+
// Update kernel command-wait event to wait on fill of new x value
320+
ASSERT_SUCCESS(urCommandBufferUpdateWaitEventsExp(command_handle, 1,
321+
&external_events[2]));
322+
323+
// Get a new signal event for command-buffer
324+
ASSERT_SUCCESS(urCommandBufferUpdateSignalEventExp(command_handle,
325+
&external_events[3]));
326+
327+
// Read data back with a queue operation waiting on updated kernel command
328+
// signal event.
329+
ASSERT_SUCCESS(urEnqueueUSMMemcpy(queue, false, host_enqueue_ptr.data(),
330+
device_ptrs[2], allocation_size, 1,
331+
&external_events[3], nullptr));
332+
333+
// Verify results
334+
ASSERT_SUCCESS(urQueueFinish(queue));
335+
for (size_t i = 0; i < elements; i++) {
336+
auto ref = (patternX * A) + patternY;
337+
ASSERT_EQ(host_command_ptr[i], ref);
338+
ASSERT_EQ(host_enqueue_ptr[i], ref);
339+
}
340+
}

0 commit comments

Comments
 (0)