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 // CLContextVk.cpp: Implements the class methods for CLContextVk.
7
8 #include "libANGLE/renderer/vulkan/CLContextVk.h"
9 #include "libANGLE/renderer/vulkan/CLCommandQueueVk.h"
10 #include "libANGLE/renderer/vulkan/CLEventVk.h"
11 #include "libANGLE/renderer/vulkan/CLMemoryVk.h"
12 #include "libANGLE/renderer/vulkan/CLProgramVk.h"
13 #include "libANGLE/renderer/vulkan/CLSamplerVk.h"
14 #include "libANGLE/renderer/vulkan/DisplayVk.h"
15 #include "libANGLE/renderer/vulkan/vk_renderer.h"
16 #include "libANGLE/renderer/vulkan/vk_utils.h"
17
18 #include "libANGLE/CLBuffer.h"
19 #include "libANGLE/CLContext.h"
20 #include "libANGLE/CLEvent.h"
21 #include "libANGLE/CLImage.h"
22 #include "libANGLE/CLProgram.h"
23 #include "libANGLE/cl_utils.h"
24
25 namespace rx
26 {
27
CLContextVk(const cl::Context & context,const cl::DevicePtrs devicePtrs)28 CLContextVk::CLContextVk(const cl::Context &context, const cl::DevicePtrs devicePtrs)
29 : CLContextImpl(context),
30 vk::Context(getPlatform()->getRenderer()),
31 mAssociatedDevices(devicePtrs)
32 {
33 mDeviceQueueIndex = mRenderer->getDefaultDeviceQueueIndex();
34 }
35
~CLContextVk()36 CLContextVk::~CLContextVk()
37 {
38 mDescriptorSetLayoutCache.destroy(getRenderer());
39 mPipelineLayoutCache.destroy(getRenderer());
40 }
41
handleError(VkResult errorCode,const char * file,const char * function,unsigned int line)42 void CLContextVk::handleError(VkResult errorCode,
43 const char *file,
44 const char *function,
45 unsigned int line)
46 {
47 ASSERT(errorCode != VK_SUCCESS);
48
49 CLenum clErrorCode = CL_SUCCESS;
50 switch (errorCode)
51 {
52 case VK_ERROR_TOO_MANY_OBJECTS:
53 case VK_ERROR_OUT_OF_HOST_MEMORY:
54 case VK_ERROR_OUT_OF_DEVICE_MEMORY:
55 clErrorCode = CL_OUT_OF_HOST_MEMORY;
56 break;
57 default:
58 clErrorCode = CL_INVALID_OPERATION;
59 }
60 ERR() << "Internal Vulkan error (" << errorCode << "): " << VulkanResultString(errorCode);
61 ERR() << " CL error (" << clErrorCode << ")";
62
63 if (errorCode == VK_ERROR_DEVICE_LOST)
64 {
65 handleDeviceLost();
66 }
67 ANGLE_CL_SET_ERROR(clErrorCode);
68 }
69
handleDeviceLost() const70 void CLContextVk::handleDeviceLost() const
71 {
72 // For now just notify the renderer
73 getRenderer()->notifyDeviceLost();
74 }
75
getDevices(cl::DevicePtrs * devicePtrsOut) const76 angle::Result CLContextVk::getDevices(cl::DevicePtrs *devicePtrsOut) const
77 {
78 ASSERT(!mAssociatedDevices.empty());
79 *devicePtrsOut = mAssociatedDevices;
80 return angle::Result::Continue;
81 }
82
createCommandQueue(const cl::CommandQueue & commandQueue,CLCommandQueueImpl::Ptr * commandQueueOut)83 angle::Result CLContextVk::createCommandQueue(const cl::CommandQueue &commandQueue,
84 CLCommandQueueImpl::Ptr *commandQueueOut)
85 {
86 CLCommandQueueVk *queueImpl = new CLCommandQueueVk(commandQueue);
87 if (queueImpl == nullptr)
88 {
89 ANGLE_CL_RETURN_ERROR(CL_OUT_OF_HOST_MEMORY);
90 }
91 ANGLE_TRY(queueImpl->init());
92 *commandQueueOut = CLCommandQueueVk::Ptr(std::move(queueImpl));
93 return angle::Result::Continue;
94 }
95
createBuffer(const cl::Buffer & buffer,void * hostPtr,CLMemoryImpl::Ptr * bufferOut)96 angle::Result CLContextVk::createBuffer(const cl::Buffer &buffer,
97 void *hostPtr,
98 CLMemoryImpl::Ptr *bufferOut)
99 {
100 CLBufferVk *memory = new (std::nothrow) CLBufferVk(buffer);
101 if (memory == nullptr)
102 {
103 ANGLE_CL_RETURN_ERROR(CL_OUT_OF_HOST_MEMORY);
104 }
105 ANGLE_TRY(memory->create(hostPtr));
106 *bufferOut = CLMemoryImpl::Ptr(memory);
107 mAssociatedObjects->mMemories.emplace(buffer.getNative());
108 return angle::Result::Continue;
109 }
110
createImage(const cl::Image & image,void * hostPtr,CLMemoryImpl::Ptr * imageOut)111 angle::Result CLContextVk::createImage(const cl::Image &image,
112 void *hostPtr,
113 CLMemoryImpl::Ptr *imageOut)
114 {
115 CLImageVk *memory = new (std::nothrow) CLImageVk(image);
116 if (memory == nullptr)
117 {
118 ANGLE_CL_RETURN_ERROR(CL_OUT_OF_HOST_MEMORY);
119 }
120 ANGLE_TRY(memory->create(hostPtr));
121 *imageOut = CLMemoryImpl::Ptr(memory);
122 mAssociatedObjects->mMemories.emplace(image.getNative());
123 return angle::Result::Continue;
124 }
125
getVkFormatFromCL(cl_image_format format)126 VkFormat CLContextVk::getVkFormatFromCL(cl_image_format format)
127 {
128 angle::FormatID formatID;
129 switch (format.image_channel_order)
130 {
131 case CL_R:
132 formatID = angle::Format::CLRFormatToID(format.image_channel_data_type);
133 break;
134 case CL_RG:
135 formatID = angle::Format::CLRGFormatToID(format.image_channel_data_type);
136 break;
137 case CL_RGB:
138 formatID = angle::Format::CLRGBFormatToID(format.image_channel_data_type);
139 break;
140 case CL_RGBA:
141 formatID = angle::Format::CLRGBAFormatToID(format.image_channel_data_type);
142 break;
143 case CL_BGRA:
144 formatID = angle::Format::CLBGRAFormatToID(format.image_channel_data_type);
145 break;
146 case CL_sRGBA:
147 formatID = angle::Format::CLsRGBAFormatToID(format.image_channel_data_type);
148 break;
149 default:
150 return VK_FORMAT_UNDEFINED;
151 }
152 return getPlatform()->getRenderer()->getFormat(formatID).getActualRenderableImageVkFormat(
153 getPlatform()->getRenderer());
154 }
155
getSupportedImageFormats(cl::MemFlags flags,cl::MemObjectType imageType,cl_uint numEntries,cl_image_format * imageFormats,cl_uint * numImageFormats)156 angle::Result CLContextVk::getSupportedImageFormats(cl::MemFlags flags,
157 cl::MemObjectType imageType,
158 cl_uint numEntries,
159 cl_image_format *imageFormats,
160 cl_uint *numImageFormats)
161 {
162 VkPhysicalDevice physicalDevice = getPlatform()->getRenderer()->getPhysicalDevice();
163 std::vector<cl_image_format> supportedFormats;
164 std::vector<cl_image_format> minSupportedFormats;
165 if (flags.intersects((CL_MEM_READ_ONLY | CL_MEM_WRITE_ONLY)))
166 {
167 minSupportedFormats.insert(minSupportedFormats.end(),
168 std::begin(kMinSupportedFormatsReadOrWrite),
169 std::end(kMinSupportedFormatsReadOrWrite));
170 }
171 else
172 {
173 minSupportedFormats.insert(minSupportedFormats.end(),
174 std::begin(kMinSupportedFormatsReadAndWrite),
175 std::end(kMinSupportedFormatsReadAndWrite));
176 }
177 for (cl_image_format format : minSupportedFormats)
178 {
179 VkFormatProperties formatProperties;
180 VkFormat vkFormat = getVkFormatFromCL(format);
181 ASSERT(vkFormat != VK_FORMAT_UNDEFINED);
182 vkGetPhysicalDeviceFormatProperties(physicalDevice, vkFormat, &formatProperties);
183 if (formatProperties.optimalTilingFeatures != 0)
184 {
185 supportedFormats.push_back(format);
186 }
187 }
188 if (numImageFormats != nullptr)
189 {
190 *numImageFormats = static_cast<cl_uint>(supportedFormats.size());
191 }
192 if (imageFormats != nullptr)
193 {
194 memcpy(imageFormats, supportedFormats.data(),
195 sizeof(cl_image_format) *
196 std::min(static_cast<cl_uint>(supportedFormats.size()), numEntries));
197 }
198
199 return angle::Result::Continue;
200 }
201
createSampler(const cl::Sampler & sampler,CLSamplerImpl::Ptr * samplerOut)202 angle::Result CLContextVk::createSampler(const cl::Sampler &sampler, CLSamplerImpl::Ptr *samplerOut)
203 {
204 CLSamplerVk *samplerVk = new (std::nothrow) CLSamplerVk(sampler);
205 if (samplerVk == nullptr)
206 {
207 ANGLE_CL_RETURN_ERROR(CL_OUT_OF_HOST_MEMORY);
208 }
209 ANGLE_TRY(samplerVk->create());
210 *samplerOut = CLSamplerImpl::Ptr(samplerVk);
211 return angle::Result::Continue;
212 }
213
createProgramWithSource(const cl::Program & program,const std::string & source,CLProgramImpl::Ptr * programOut)214 angle::Result CLContextVk::createProgramWithSource(const cl::Program &program,
215 const std::string &source,
216 CLProgramImpl::Ptr *programOut)
217 {
218 CLProgramVk *programVk = new (std::nothrow) CLProgramVk(program);
219 if (programVk == nullptr)
220 {
221 ANGLE_CL_RETURN_ERROR(CL_OUT_OF_HOST_MEMORY);
222 }
223 ANGLE_TRY(programVk->init());
224 *programOut = CLProgramImpl::Ptr(std::move(programVk));
225
226 return angle::Result::Continue;
227 }
228
createProgramWithIL(const cl::Program & program,const void * il,size_t length,CLProgramImpl::Ptr * programOut)229 angle::Result CLContextVk::createProgramWithIL(const cl::Program &program,
230 const void *il,
231 size_t length,
232 CLProgramImpl::Ptr *programOut)
233 {
234 UNIMPLEMENTED();
235 ANGLE_CL_RETURN_ERROR(CL_OUT_OF_RESOURCES);
236 }
237
createProgramWithBinary(const cl::Program & program,const size_t * lengths,const unsigned char ** binaries,cl_int * binaryStatus,CLProgramImpl::Ptr * programOut)238 angle::Result CLContextVk::createProgramWithBinary(const cl::Program &program,
239 const size_t *lengths,
240 const unsigned char **binaries,
241 cl_int *binaryStatus,
242 CLProgramImpl::Ptr *programOut)
243 {
244 CLProgramVk *programVk = new (std::nothrow) CLProgramVk(program);
245 if (programVk == nullptr)
246 {
247 ANGLE_CL_RETURN_ERROR(CL_OUT_OF_HOST_MEMORY);
248 }
249 ANGLE_TRY(programVk->init(lengths, binaries, binaryStatus));
250 *programOut = CLProgramImpl::Ptr(std::move(programVk));
251
252 return angle::Result::Continue;
253 }
254
createProgramWithBuiltInKernels(const cl::Program & program,const char * kernel_names,CLProgramImpl::Ptr * programOut)255 angle::Result CLContextVk::createProgramWithBuiltInKernels(const cl::Program &program,
256 const char *kernel_names,
257 CLProgramImpl::Ptr *programOut)
258 {
259 UNIMPLEMENTED();
260 ANGLE_CL_RETURN_ERROR(CL_OUT_OF_RESOURCES);
261 }
262
linkProgram(const cl::Program & program,const cl::DevicePtrs & devices,const char * options,const cl::ProgramPtrs & inputPrograms,cl::Program * notify,CLProgramImpl::Ptr * programOut)263 angle::Result CLContextVk::linkProgram(const cl::Program &program,
264 const cl::DevicePtrs &devices,
265 const char *options,
266 const cl::ProgramPtrs &inputPrograms,
267 cl::Program *notify,
268 CLProgramImpl::Ptr *programOut)
269 {
270 const cl::DevicePtrs &devicePtrs = !devices.empty() ? devices : mContext.getDevices();
271
272 CLProgramVk::Ptr programImpl = CLProgramVk::Ptr(new (std::nothrow) CLProgramVk(program));
273 if (programImpl == nullptr)
274 {
275 ANGLE_CL_RETURN_ERROR(CL_OUT_OF_HOST_MEMORY);
276 }
277 ANGLE_TRY(programImpl->init());
278
279 cl::DevicePtrs linkDeviceList;
280 CLProgramVk::LinkProgramsList linkProgramsList;
281 cl::BitField libraryOrObject(CL_PROGRAM_BINARY_TYPE_LIBRARY |
282 CL_PROGRAM_BINARY_TYPE_COMPILED_OBJECT);
283 for (const cl::DevicePtr &devicePtr : devicePtrs)
284 {
285 CLProgramVk::LinkPrograms linkPrograms;
286 for (const cl::ProgramPtr &inputProgram : inputPrograms)
287 {
288 const CLProgramVk::DeviceProgramData *deviceProgramData =
289 inputProgram->getImpl<CLProgramVk>().getDeviceProgramData(devicePtr->getNative());
290
291 // Should be valid at this point
292 ASSERT(deviceProgramData != nullptr);
293
294 if (libraryOrObject.intersects(deviceProgramData->binaryType))
295 {
296 linkPrograms.push_back(deviceProgramData);
297 }
298 }
299 if (!linkPrograms.empty())
300 {
301 linkDeviceList.push_back(devicePtr);
302 linkProgramsList.push_back(linkPrograms);
303 }
304 }
305
306 programImpl->setBuildStatus(linkDeviceList, CL_BUILD_IN_PROGRESS);
307
308 // Perform link
309 if (notify)
310 {
311 std::shared_ptr<angle::WaitableEvent> asyncEvent =
312 mContext.getPlatform().getMultiThreadPool()->postWorkerTask(
313 std::make_shared<CLAsyncBuildTask>(
314 programImpl.get(), linkDeviceList, std::string(options ? options : ""), "",
315 CLProgramVk::BuildType::LINK, linkProgramsList, notify));
316 ASSERT(asyncEvent != nullptr);
317 }
318 else
319 {
320 if (!programImpl->buildInternal(linkDeviceList, std::string(options ? options : ""), "",
321 CLProgramVk::BuildType::LINK, linkProgramsList))
322 {
323 ANGLE_CL_RETURN_ERROR(CL_LINK_PROGRAM_FAILURE);
324 }
325 }
326
327 *programOut = std::move(programImpl);
328 return angle::Result::Continue;
329 }
330
createUserEvent(const cl::Event & event,CLEventImpl::Ptr * eventOut)331 angle::Result CLContextVk::createUserEvent(const cl::Event &event, CLEventImpl::Ptr *eventOut)
332 {
333 *eventOut = CLEventImpl::Ptr(new (std::nothrow) CLEventVk(event));
334 if (*eventOut == nullptr)
335 {
336 ANGLE_CL_RETURN_ERROR(CL_OUT_OF_HOST_MEMORY);
337 }
338 return angle::Result::Continue;
339 }
340
waitForEvents(const cl::EventPtrs & events)341 angle::Result CLContextVk::waitForEvents(const cl::EventPtrs &events)
342 {
343 for (auto &event : events)
344 {
345 CLEventVk *eventVk = &event.get()->getImpl<CLEventVk>();
346 if (eventVk->isUserEvent())
347 {
348 ANGLE_TRY(eventVk->waitForUserEventStatus());
349 }
350 else
351 {
352 // TODO rework this to instead (flush w/ ResourceUse serial wait) once we move away from
353 // spawning a submit-thread/Task for flush routine
354 // https://anglebug.com/42267107
355 ANGLE_TRY(event->getCommandQueue()->finish());
356 }
357 }
358
359 return angle::Result::Continue;
360 }
361
362 } // namespace rx
363