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