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 // CLProgramVk.h: Defines the class interface for CLProgramVk, implementing CLProgramImpl. 7 8 #ifndef LIBANGLE_RENDERER_VULKAN_CLPROGRAMVK_H_ 9 #define LIBANGLE_RENDERER_VULKAN_CLPROGRAMVK_H_ 10 11 #include <cstdint> 12 13 #include "common/SimpleMutex.h" 14 #include "common/hash_containers.h" 15 16 #include "libANGLE/CLSampler.h" 17 #include "libANGLE/renderer/vulkan/CLContextVk.h" 18 #include "libANGLE/renderer/vulkan/CLKernelVk.h" 19 #include "libANGLE/renderer/vulkan/cl_types.h" 20 #include "libANGLE/renderer/vulkan/clspv_utils.h" 21 #include "libANGLE/renderer/vulkan/vk_cache_utils.h" 22 #include "libANGLE/renderer/vulkan/vk_helpers.h" 23 24 #include "libANGLE/renderer/CLProgramImpl.h" 25 26 #include "libANGLE/CLProgram.h" 27 28 #include "clspv/Compiler.h" 29 30 #include "vulkan/vulkan_core.h" 31 32 #include "spirv/unified1/NonSemanticClspvReflection.h" 33 34 namespace rx 35 { 36 37 class CLProgramVk : public CLProgramImpl 38 { 39 public: 40 using Ptr = std::unique_ptr<CLProgramVk>; 41 // TODO: Look into moving this information in CLKernelArgument 42 // https://anglebug.com/378514267 43 struct ImagePushConstant 44 { 45 VkPushConstantRange pcRange; 46 uint32_t ordinal; 47 }; 48 struct SpvReflectionData 49 { 50 angle::HashMap<uint32_t, uint32_t> spvIntLookup; 51 angle::HashMap<uint32_t, std::string> spvStrLookup; 52 angle::HashMap<uint32_t, CLKernelVk::ArgInfo> kernelArgInfos; 53 angle::HashMap<std::string, uint32_t> kernelFlags; 54 angle::HashMap<std::string, std::string> kernelAttributes; 55 angle::HashMap<std::string, std::array<uint32_t, 3>> kernelCompileWorkgroupSize; 56 angle::HashMap<uint32_t, VkPushConstantRange> pushConstants; 57 angle::PackedEnumMap<SpecConstantType, uint32_t> specConstantIDs; 58 angle::PackedEnumBitSet<SpecConstantType, uint32_t> specConstantsUsed; 59 angle::HashMap<uint32_t, std::vector<ImagePushConstant>> imagePushConstants; 60 CLKernelArgsMap kernelArgsMap; 61 angle::HashMap<std::string, CLKernelArgument> kernelArgMap; 62 angle::HashSet<uint32_t> kernelIDs; 63 ClspvPrintfBufferStorage printfBufferStorage; 64 angle::HashMap<uint32_t, ClspvPrintfInfo> printfInfoMap; 65 std::vector<ClspvLiteralSampler> literalSamplers; 66 }; 67 68 // Output binary structure (for CL_PROGRAM_BINARIES query) 69 static constexpr uint32_t kBinaryVersion = 2; 70 struct ProgramBinaryOutputHeader 71 { 72 uint32_t headerVersion{kBinaryVersion}; 73 cl_program_binary_type binaryType{CL_PROGRAM_BINARY_TYPE_NONE}; 74 cl_build_status buildStatus{CL_BUILD_NONE}; 75 }; 76 77 struct ScopedClspvContext : angle::NonCopyable 78 { 79 ScopedClspvContext() = default; ~ScopedClspvContextScopedClspvContext80 ~ScopedClspvContext() { clspvFreeOutputBuildObjs(mOutputBin, mOutputBuildLog); } 81 82 size_t mOutputBinSize{0}; 83 char *mOutputBin{nullptr}; 84 char *mOutputBuildLog{nullptr}; 85 }; 86 87 struct ScopedProgramCallback : angle::NonCopyable 88 { 89 ScopedProgramCallback() = delete; ScopedProgramCallbackScopedProgramCallback90 ScopedProgramCallback(cl::Program *notify) : mNotify(notify) {} ~ScopedProgramCallbackScopedProgramCallback91 ~ScopedProgramCallback() 92 { 93 if (mNotify) 94 { 95 mNotify->callback(); 96 } 97 } 98 99 cl::Program *mNotify{nullptr}; 100 }; 101 102 enum class BuildType 103 { 104 BUILD = 0, 105 COMPILE, 106 LINK, 107 BINARY 108 }; 109 110 struct DeviceProgramData 111 { 112 std::vector<char> IR; 113 std::string buildLog; 114 angle::spirv::Blob binary; 115 SpvReflectionData reflectionData; 116 VkPushConstantRange pushConstRange{}; 117 cl_build_status buildStatus{CL_BUILD_NONE}; 118 cl_program_binary_type binaryType{CL_PROGRAM_BINARY_TYPE_NONE}; 119 spv_target_env spirvVersion; 120 numKernelsDeviceProgramData121 size_t numKernels() const { return reflectionData.kernelArgsMap.size(); } 122 numKernelArgsDeviceProgramData123 size_t numKernelArgs(const std::string &kernelName) const 124 { 125 return containsKernel(kernelName) ? getKernelArgsMap().at(kernelName).size() : 0; 126 } 127 getKernelArgsMapDeviceProgramData128 const CLKernelArgsMap &getKernelArgsMap() const { return reflectionData.kernelArgsMap; } 129 containsKernelDeviceProgramData130 bool containsKernel(const std::string &name) const 131 { 132 return reflectionData.kernelArgsMap.contains(name); 133 } 134 getKernelNamesDeviceProgramData135 std::string getKernelNames() const 136 { 137 std::string names; 138 for (auto name = getKernelArgsMap().begin(); name != getKernelArgsMap().end(); ++name) 139 { 140 names += name->first + (std::next(name) != getKernelArgsMap().end() ? ";" : "\0"); 141 } 142 return names; 143 } 144 getKernelFlagsDeviceProgramData145 uint32_t getKernelFlags(const std::string &kernelName) const 146 { 147 if (containsKernel(kernelName)) 148 { 149 return reflectionData.kernelFlags.at(kernelName); 150 } 151 return 0; 152 } 153 getKernelArgumentsDeviceProgramData154 CLKernelArguments getKernelArguments(const std::string &kernelName) const 155 { 156 CLKernelArguments kargsCopy; 157 if (containsKernel(kernelName)) 158 { 159 const CLKernelArguments &kargs = getKernelArgsMap().at(kernelName); 160 for (const CLKernelArgument &karg : kargs) 161 { 162 kargsCopy.push_back(karg); 163 } 164 } 165 return kargsCopy; 166 } 167 getCompiledWorkgroupSizeDeviceProgramData168 cl::WorkgroupSize getCompiledWorkgroupSize(const std::string &kernelName) const 169 { 170 cl::WorkgroupSize compiledWorkgroupSize{0, 0, 0}; 171 if (reflectionData.kernelCompileWorkgroupSize.contains(kernelName)) 172 { 173 for (size_t i = 0; i < compiledWorkgroupSize.size(); ++i) 174 { 175 compiledWorkgroupSize[i] = 176 reflectionData.kernelCompileWorkgroupSize.at(kernelName)[i]; 177 } 178 } 179 return compiledWorkgroupSize; 180 } 181 getKernelAttributesDeviceProgramData182 std::string getKernelAttributes(const std::string &kernelName) const 183 { 184 if (containsKernel(kernelName)) 185 { 186 return reflectionData.kernelAttributes.at(kernelName.c_str()); 187 } 188 return std::string{}; 189 } 190 getPushConstantRangeFromClspvReflectionTypeDeviceProgramData191 const VkPushConstantRange *getPushConstantRangeFromClspvReflectionType( 192 NonSemanticClspvReflectionInstructions type) const 193 { 194 const VkPushConstantRange *pushConstantRangePtr = nullptr; 195 if (reflectionData.pushConstants.contains(type)) 196 { 197 pushConstantRangePtr = &reflectionData.pushConstants.at(type); 198 } 199 return pushConstantRangePtr; 200 } 201 getGlobalOffsetRangeDeviceProgramData202 inline const VkPushConstantRange *getGlobalOffsetRange() const 203 { 204 return getPushConstantRangeFromClspvReflectionType( 205 NonSemanticClspvReflectionPushConstantGlobalOffset); 206 } 207 getGlobalSizeRangeDeviceProgramData208 inline const VkPushConstantRange *getGlobalSizeRange() const 209 { 210 return getPushConstantRangeFromClspvReflectionType( 211 NonSemanticClspvReflectionPushConstantGlobalSize); 212 } 213 getEnqueuedLocalSizeRangeDeviceProgramData214 inline const VkPushConstantRange *getEnqueuedLocalSizeRange() const 215 { 216 return getPushConstantRangeFromClspvReflectionType( 217 NonSemanticClspvReflectionPushConstantEnqueuedLocalSize); 218 } 219 getNumWorkgroupsRangeDeviceProgramData220 inline const VkPushConstantRange *getNumWorkgroupsRange() const 221 { 222 return getPushConstantRangeFromClspvReflectionType( 223 NonSemanticClspvReflectionPushConstantNumWorkgroups); 224 } 225 getRegionOffsetRangeDeviceProgramData226 inline const VkPushConstantRange *getRegionOffsetRange() const 227 { 228 return getPushConstantRangeFromClspvReflectionType( 229 NonSemanticClspvReflectionPushConstantRegionOffset); 230 } 231 getRegionGroupOffsetRangeDeviceProgramData232 inline const VkPushConstantRange *getRegionGroupOffsetRange() const 233 { 234 return getPushConstantRangeFromClspvReflectionType( 235 NonSemanticClspvReflectionPushConstantRegionGroupOffset); 236 } 237 getImageDataChannelOrderRangeDeviceProgramData238 const VkPushConstantRange *getImageDataChannelOrderRange(size_t ordinal) const 239 { 240 const VkPushConstantRange *pushConstantRangePtr = nullptr; 241 if (reflectionData.imagePushConstants.contains( 242 NonSemanticClspvReflectionImageArgumentInfoChannelOrderPushConstant)) 243 { 244 for (const auto &imageConstant : reflectionData.imagePushConstants.at( 245 NonSemanticClspvReflectionImageArgumentInfoChannelOrderPushConstant)) 246 { 247 if (static_cast<size_t>(imageConstant.ordinal) == ordinal) 248 { 249 pushConstantRangePtr = &imageConstant.pcRange; 250 } 251 } 252 } 253 return pushConstantRangePtr; 254 } 255 getImageDataChannelDataTypeRangeDeviceProgramData256 const VkPushConstantRange *getImageDataChannelDataTypeRange(size_t ordinal) const 257 { 258 const VkPushConstantRange *pushConstantRangePtr = nullptr; 259 if (reflectionData.imagePushConstants.contains( 260 NonSemanticClspvReflectionImageArgumentInfoChannelDataTypePushConstant)) 261 { 262 for (const auto &imageConstant : reflectionData.imagePushConstants.at( 263 NonSemanticClspvReflectionImageArgumentInfoChannelDataTypePushConstant)) 264 { 265 if (static_cast<size_t>(imageConstant.ordinal) == ordinal) 266 { 267 pushConstantRangePtr = &imageConstant.pcRange; 268 } 269 } 270 } 271 return pushConstantRangePtr; 272 } 273 getNormalizedSamplerMaskRangeDeviceProgramData274 const VkPushConstantRange *getNormalizedSamplerMaskRange(size_t ordinal) const 275 { 276 const VkPushConstantRange *pushConstantRangePtr = nullptr; 277 if (reflectionData.imagePushConstants.contains( 278 NonSemanticClspvReflectionNormalizedSamplerMaskPushConstant)) 279 { 280 for (const auto &imageConstant : reflectionData.imagePushConstants.at( 281 NonSemanticClspvReflectionNormalizedSamplerMaskPushConstant)) 282 { 283 if (static_cast<size_t>(imageConstant.ordinal) == ordinal) 284 { 285 pushConstantRangePtr = &imageConstant.pcRange; 286 } 287 } 288 } 289 return pushConstantRangePtr; 290 } 291 }; 292 using DevicePrograms = angle::HashMap<const _cl_device_id *, DeviceProgramData>; 293 using LinkPrograms = std::vector<const DeviceProgramData *>; 294 using LinkProgramsList = std::vector<LinkPrograms>; 295 296 CLProgramVk(const cl::Program &program); 297 298 ~CLProgramVk() override; 299 300 angle::Result init(); 301 angle::Result init(const size_t *lengths, const unsigned char **binaries, cl_int *binaryStatus); 302 303 angle::Result build(const cl::DevicePtrs &devices, 304 const char *options, 305 cl::Program *notify) override; 306 307 angle::Result compile(const cl::DevicePtrs &devices, 308 const char *options, 309 const cl::ProgramPtrs &inputHeaders, 310 const char **headerIncludeNames, 311 cl::Program *notify) override; 312 313 angle::Result getInfo(cl::ProgramInfo name, 314 size_t valueSize, 315 void *value, 316 size_t *valueSizeRet) const override; 317 318 angle::Result getBuildInfo(const cl::Device &device, 319 cl::ProgramBuildInfo name, 320 size_t valueSize, 321 void *value, 322 size_t *valueSizeRet) const override; 323 324 angle::Result createKernel(const cl::Kernel &kernel, 325 const char *name, 326 CLKernelImpl::Ptr *kernelOut) override; 327 328 angle::Result createKernels(cl_uint numKernels, 329 CLKernelImpl::CreateFuncs &createFuncs, 330 cl_uint *numKernelsRet) override; 331 332 const DeviceProgramData *getDeviceProgramData(const char *kernelName) const; 333 const DeviceProgramData *getDeviceProgramData(const _cl_device_id *device) const; getPlatform()334 CLPlatformVk *getPlatform() { return mContext->getPlatform(); } getShaderModule()335 const vk::ShaderModulePtr &getShaderModule() const { return mShader; } 336 337 bool buildInternal(const cl::DevicePtrs &devices, 338 std::string options, 339 std::string internalOptions, 340 BuildType buildType, 341 const LinkProgramsList &LinkProgramsList); 342 angle::spirv::Blob stripReflection(const DeviceProgramData *deviceProgramData); 343 344 // Sets the status for given associated device programs 345 void setBuildStatus(const cl::DevicePtrs &devices, cl_build_status status); 346 347 const angle::HashMap<uint32_t, ClspvPrintfInfo> *getPrintfDescriptors( 348 const std::string &kernelName) const; 349 350 private: 351 CLContextVk *mContext; 352 std::string mProgramOpts; 353 vk::ShaderModulePtr mShader; 354 DevicePrograms mAssociatedDevicePrograms; 355 angle::SimpleMutex mProgramMutex; 356 357 std::shared_ptr<angle::WaitableEvent> mAsyncBuildEvent; 358 }; 359 360 class CLAsyncBuildTask : public angle::Closure 361 { 362 public: CLAsyncBuildTask(CLProgramVk * programVk,const cl::DevicePtrs & devices,std::string options,std::string internalOptions,CLProgramVk::BuildType buildType,const CLProgramVk::LinkProgramsList & LinkProgramsList,cl::Program * notify)363 CLAsyncBuildTask(CLProgramVk *programVk, 364 const cl::DevicePtrs &devices, 365 std::string options, 366 std::string internalOptions, 367 CLProgramVk::BuildType buildType, 368 const CLProgramVk::LinkProgramsList &LinkProgramsList, 369 cl::Program *notify) 370 : mProgramVk(programVk), 371 mDevices(devices), 372 mOptions(options), 373 mInternalOptions(internalOptions), 374 mBuildType(buildType), 375 mLinkProgramsList(LinkProgramsList), 376 mNotify(notify) 377 {} 378 379 void operator()() override; 380 381 private: 382 CLProgramVk *mProgramVk; 383 const cl::DevicePtrs mDevices; 384 std::string mOptions; 385 std::string mInternalOptions; 386 CLProgramVk::BuildType mBuildType; 387 const CLProgramVk::LinkProgramsList mLinkProgramsList; 388 cl::Program *mNotify; 389 }; 390 391 } // namespace rx 392 393 #endif // LIBANGLE_RENDERER_VULKAN_CLPROGRAMVK_H_ 394