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,const cl::ExecutionStatus initialStatus,const QueueSerial eventSerial)16 CLEventVk::CLEventVk(const cl::Event &event,
17 const cl::ExecutionStatus initialStatus,
18 const QueueSerial eventSerial)
19 : CLEventImpl(event),
20 mStatus(cl::ToCLenum(initialStatus)),
21 mProfilingTimestamps(ProfilingTimestamps{}),
22 mQueueSerial(eventSerial)
23 {
24 ANGLE_CL_IMPL_TRY(setTimestamp(*mStatus));
25 }
26
~CLEventVk()27 CLEventVk::~CLEventVk() {}
28
onEventCreate()29 angle::Result CLEventVk::onEventCreate()
30 {
31 ASSERT(!isUserEvent());
32 ASSERT(mQueueSerial.valid());
33
34 if (cl::FromCLenum<cl::ExecutionStatus>(*mStatus) == cl::ExecutionStatus::Complete)
35 {
36 // Submission finished at this point, just set event to complete
37 ANGLE_TRY(setStatusAndExecuteCallback(CL_COMPLETE));
38 }
39 else
40 {
41 getFrontendObject().getCommandQueue()->getImpl<CLCommandQueueVk>().addEventReference(*this);
42 if (getFrontendObject().getCommandQueue()->getProperties().intersects(
43 CL_QUEUE_PROFILING_ENABLE))
44 {
45 // Block for profiling so that we get timestamps per-command
46 ANGLE_TRY(getFrontendObject().getCommandQueue()->getImpl<CLCommandQueueVk>().finish());
47 }
48 }
49 return angle::Result::Continue;
50 }
51
getCommandExecutionStatus(cl_int & executionStatus)52 angle::Result CLEventVk::getCommandExecutionStatus(cl_int &executionStatus)
53 {
54 executionStatus = *mStatus;
55 return angle::Result::Continue;
56 }
57
setUserEventStatus(cl_int executionStatus)58 angle::Result CLEventVk::setUserEventStatus(cl_int executionStatus)
59 {
60 ASSERT(isUserEvent());
61
62 // Not much to do here other than storing the user supplied state.
63 // Error checking and single call enforcement is responsibility of the front end.
64 ANGLE_TRY(setStatusAndExecuteCallback(executionStatus));
65
66 // User event set and callback(s) finished - notify those waiting
67 mUserEventCondition.notify_all();
68
69 return angle::Result::Continue;
70 }
71
setCallback(cl::Event & event,cl_int commandExecCallbackType)72 angle::Result CLEventVk::setCallback(cl::Event &event, cl_int commandExecCallbackType)
73 {
74 ASSERT(commandExecCallbackType >= CL_COMPLETE);
75 ASSERT(commandExecCallbackType < CL_QUEUED);
76
77 // Not much to do, acknowledge the presence of callback and returns
78 mHaveCallbacks->at(commandExecCallbackType) = true;
79
80 return angle::Result::Continue;
81 }
82
getProfilingInfo(cl::ProfilingInfo name,size_t valueSize,void * value,size_t * valueSizeRet)83 angle::Result CLEventVk::getProfilingInfo(cl::ProfilingInfo name,
84 size_t valueSize,
85 void *value,
86 size_t *valueSizeRet)
87 {
88 cl_ulong valueUlong = 0;
89 size_t copySize = 0;
90 const void *copyValue = nullptr;
91
92 auto profilingTimestamps = mProfilingTimestamps.synchronize();
93
94 switch (name)
95 {
96 case cl::ProfilingInfo::CommandQueued:
97 valueUlong = profilingTimestamps->commandQueuedTS;
98 break;
99 case cl::ProfilingInfo::CommandSubmit:
100 valueUlong = profilingTimestamps->commandSubmitTS;
101 break;
102 case cl::ProfilingInfo::CommandStart:
103 valueUlong = profilingTimestamps->commandStartTS;
104 break;
105 case cl::ProfilingInfo::CommandEnd:
106 valueUlong = profilingTimestamps->commandEndTS;
107 break;
108 case cl::ProfilingInfo::CommandComplete:
109 valueUlong = profilingTimestamps->commandCompleteTS;
110 break;
111 default:
112 UNREACHABLE();
113 }
114 copyValue = &valueUlong;
115 copySize = sizeof(valueUlong);
116
117 if ((value != nullptr) && (copyValue != nullptr))
118 {
119 memcpy(value, copyValue, std::min(valueSize, copySize));
120 }
121
122 if (valueSizeRet != nullptr)
123 {
124 *valueSizeRet = copySize;
125 }
126
127 return angle::Result::Continue;
128 }
129
waitForUserEventStatus()130 angle::Result CLEventVk::waitForUserEventStatus()
131 {
132 ASSERT(isUserEvent());
133
134 cl_int status = CL_QUEUED;
135 std::unique_lock<std::mutex> ul(mUserEventMutex);
136 ANGLE_TRY(getCommandExecutionStatus(status));
137 if (status > CL_COMPLETE)
138 {
139 // User is responsible for setting the user-event object, we need to wait for that event
140 // (We dont care what the outcome is, just need to wait until that event triggers)
141 INFO() << "Waiting for user-event (" << &mEvent
142 << ") to be set! (aka clSetUserEventStatus)";
143 mUserEventCondition.wait(ul);
144 }
145
146 return angle::Result::Continue;
147 }
148
setStatusAndExecuteCallback(cl_int status)149 angle::Result CLEventVk::setStatusAndExecuteCallback(cl_int status)
150 {
151 auto statusHandle = mStatus.synchronize();
152 auto haveCallbacksHandle = mHaveCallbacks.synchronize();
153
154 // we might skip states in some cases i.e. move from QUEUED to COMPLETE, so
155 // make sure we are setting time stamps for all transitions
156 ASSERT(*statusHandle >= status);
157 while (*statusHandle > status)
158 {
159 (*statusHandle)--;
160 ANGLE_TRY(setTimestamp(*statusHandle));
161 if (*statusHandle >= CL_COMPLETE && *statusHandle < CL_QUEUED &&
162 haveCallbacksHandle->at(*statusHandle))
163 {
164 getFrontendObject().callback(*statusHandle);
165 haveCallbacksHandle->at(*statusHandle) = false;
166 }
167 }
168
169 return angle::Result::Continue;
170 }
171
setTimestamp(cl_int status)172 angle::Result CLEventVk::setTimestamp(cl_int status)
173 {
174 if (!isUserEvent() &&
175 mEvent.getCommandQueue()->getProperties().intersects(CL_QUEUE_PROFILING_ENABLE))
176 {
177 // TODO(aannestrand) Just get current CPU timestamp for now, look into Vulkan GPU device
178 // timestamp query instead and later make CPU timestamp a fallback if GPU timestamp cannot
179 // be queried http://anglebug.com/357902514
180 cl_ulong cpuTS =
181 std::chrono::time_point_cast<std::chrono::nanoseconds>(std::chrono::system_clock::now())
182 .time_since_epoch()
183 .count();
184
185 auto profilingTimestamps = mProfilingTimestamps.synchronize();
186
187 switch (status)
188 {
189 case CL_QUEUED:
190 profilingTimestamps->commandQueuedTS = cpuTS;
191 break;
192 case CL_SUBMITTED:
193 profilingTimestamps->commandSubmitTS = cpuTS;
194 break;
195 case CL_RUNNING:
196 profilingTimestamps->commandStartTS = cpuTS;
197 break;
198 case CL_COMPLETE:
199 profilingTimestamps->commandEndTS = cpuTS;
200
201 // Returns a value equivalent to passing CL_PROFILING_COMMAND_END if the device
202 // associated with event does not support device-side enqueue.
203 // https://registry.khronos.org/OpenCL/specs/3.0-unified/html/OpenCL_API.html#_device_side_enqueue
204 profilingTimestamps->commandCompleteTS = cpuTS;
205 break;
206 default:
207 UNREACHABLE();
208 }
209 }
210
211 return angle::Result::Continue;
212 }
213
214 } // namespace rx
215