1 //
2 // Copyright 2021 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6 // CLEventVk.cpp: Implements the class methods for CLEventVk.
7
8 #include "libANGLE/renderer/vulkan/CLEventVk.h"
9 #include "libANGLE/renderer/vulkan/CLCommandQueueVk.h"
10
11 #include "libANGLE/cl_utils.h"
12
13 namespace rx
14 {
15
CLEventVk(const cl::Event & event)16 CLEventVk::CLEventVk(const cl::Event &event)
17 : CLEventImpl(event),
18 mStatus(isUserEvent() ? CL_SUBMITTED : CL_QUEUED),
19 mProfilingTimestamps(ProfilingTimestamps{})
20 {
21 ANGLE_CL_IMPL_TRY(setTimestamp(*mStatus));
22 }
23
~CLEventVk()24 CLEventVk::~CLEventVk() {}
25
getCommandExecutionStatus(cl_int & executionStatus)26 angle::Result CLEventVk::getCommandExecutionStatus(cl_int &executionStatus)
27 {
28 executionStatus = *mStatus;
29 return angle::Result::Continue;
30 }
31
setUserEventStatus(cl_int executionStatus)32 angle::Result CLEventVk::setUserEventStatus(cl_int executionStatus)
33 {
34 ASSERT(isUserEvent());
35
36 // Not much to do here other than storing the user supplied state.
37 // Error checking and single call enforcement is responsibility of the front end.
38 ANGLE_TRY(setStatusAndExecuteCallback(executionStatus));
39
40 // User event set and callback(s) finished - notify those waiting
41 mUserEventCondition.notify_all();
42
43 return angle::Result::Continue;
44 }
45
setCallback(cl::Event & event,cl_int commandExecCallbackType)46 angle::Result CLEventVk::setCallback(cl::Event &event, cl_int commandExecCallbackType)
47 {
48 ASSERT(commandExecCallbackType >= CL_COMPLETE);
49 ASSERT(commandExecCallbackType < CL_QUEUED);
50
51 // Not much to do, acknowledge the presence of callback and returns
52 mHaveCallbacks->at(commandExecCallbackType) = true;
53
54 return angle::Result::Continue;
55 }
56
getProfilingInfo(cl::ProfilingInfo name,size_t valueSize,void * value,size_t * valueSizeRet)57 angle::Result CLEventVk::getProfilingInfo(cl::ProfilingInfo name,
58 size_t valueSize,
59 void *value,
60 size_t *valueSizeRet)
61 {
62 cl_ulong valueUlong = 0;
63 size_t copySize = 0;
64 const void *copyValue = nullptr;
65
66 auto profilingTimestamps = mProfilingTimestamps.synchronize();
67
68 switch (name)
69 {
70 case cl::ProfilingInfo::CommandQueued:
71 valueUlong = profilingTimestamps->commandQueuedTS;
72 break;
73 case cl::ProfilingInfo::CommandSubmit:
74 valueUlong = profilingTimestamps->commandSubmitTS;
75 break;
76 case cl::ProfilingInfo::CommandStart:
77 valueUlong = profilingTimestamps->commandStartTS;
78 break;
79 case cl::ProfilingInfo::CommandEnd:
80 valueUlong = profilingTimestamps->commandEndTS;
81 break;
82 case cl::ProfilingInfo::CommandComplete:
83 valueUlong = profilingTimestamps->commandCompleteTS;
84 break;
85 default:
86 UNREACHABLE();
87 }
88 copyValue = &valueUlong;
89 copySize = sizeof(valueUlong);
90
91 if ((value != nullptr) && (copyValue != nullptr))
92 {
93 memcpy(value, copyValue, std::min(valueSize, copySize));
94 }
95
96 if (valueSizeRet != nullptr)
97 {
98 *valueSizeRet = copySize;
99 }
100
101 return angle::Result::Continue;
102 }
103
waitForUserEventStatus()104 angle::Result CLEventVk::waitForUserEventStatus()
105 {
106 ASSERT(isUserEvent());
107
108 cl_int status = CL_QUEUED;
109 std::unique_lock<std::mutex> ul(mUserEventMutex);
110 ANGLE_TRY(getCommandExecutionStatus(status));
111 if (status > CL_COMPLETE)
112 {
113 // User is responsible for setting the user-event object, we need to wait for that event
114 // (We dont care what the outcome is, just need to wait until that event triggers)
115 INFO() << "Waiting for user-event (" << &mEvent
116 << ") to be set! (aka clSetUserEventStatus)";
117 mUserEventCondition.wait(ul);
118 }
119
120 return angle::Result::Continue;
121 }
122
setStatusAndExecuteCallback(cl_int status)123 angle::Result CLEventVk::setStatusAndExecuteCallback(cl_int status)
124 {
125 *mStatus = status;
126
127 ANGLE_TRY(setTimestamp(status));
128 if (status >= CL_COMPLETE && status < CL_QUEUED && mHaveCallbacks->at(status))
129 {
130 auto haveCallbacks = mHaveCallbacks.synchronize();
131
132 // Sanity check, callback(s) only from this exec status should be outstanding
133 ASSERT(std::count(haveCallbacks->begin() + status, haveCallbacks->end(), true) == 1);
134
135 getFrontendObject().callback(status);
136 haveCallbacks->at(status) = false;
137 }
138
139 return angle::Result::Continue;
140 }
141
setTimestamp(cl_int status)142 angle::Result CLEventVk::setTimestamp(cl_int status)
143 {
144 if (!isUserEvent() &&
145 mEvent.getCommandQueue()->getProperties().intersects(CL_QUEUE_PROFILING_ENABLE))
146 {
147 // TODO(aannestrand) Just get current CPU timestamp for now, look into Vulkan GPU device
148 // timestamp query instead and later make CPU timestamp a fallback if GPU timestamp cannot
149 // be queried http://anglebug.com/357902514
150 cl_ulong cpuTS =
151 std::chrono::time_point_cast<std::chrono::nanoseconds>(std::chrono::system_clock::now())
152 .time_since_epoch()
153 .count();
154
155 auto profilingTimestamps = mProfilingTimestamps.synchronize();
156
157 switch (status)
158 {
159 case CL_QUEUED:
160 profilingTimestamps->commandQueuedTS = cpuTS;
161 break;
162 case CL_SUBMITTED:
163 profilingTimestamps->commandSubmitTS = cpuTS;
164 break;
165 case CL_RUNNING:
166 profilingTimestamps->commandStartTS = cpuTS;
167 break;
168 case CL_COMPLETE:
169 profilingTimestamps->commandEndTS = cpuTS;
170
171 // Returns a value equivalent to passing CL_PROFILING_COMMAND_END if the device
172 // associated with event does not support device-side enqueue.
173 // https://registry.khronos.org/OpenCL/specs/3.0-unified/html/OpenCL_API.html#_device_side_enqueue
174 profilingTimestamps->commandCompleteTS = cpuTS;
175 break;
176 default:
177 UNREACHABLE();
178 }
179 }
180
181 return angle::Result::Continue;
182 }
183
184 } // namespace rx
185