shaderc_spirv.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722
  1. /*
  2. * Copyright 2011-2016 Branimir Karadzic. All rights reserved.
  3. * License: https://github.com/bkaradzic/bgfx#license-bsd-2-clause
  4. */
  5. #include "shaderc.h"
  6. #include <ShaderLang.h>
  7. #include <ResourceLimits.h>
  8. #include <SPIRV/SPVRemapper.h>
  9. //#include <spirv-tools/libspirv.hpp>
  10. //#include <spirv-tools/optimizer.hpp>
  11. namespace bgfx
  12. {
  13. static bx::CrtAllocator s_allocator;
  14. bx::AllocatorI* g_allocator = &s_allocator;
  15. struct TinyStlAllocator
  16. {
  17. static void* static_allocate(size_t _bytes);
  18. static void static_deallocate(void* _ptr, size_t /*_bytes*/);
  19. };
  20. void* TinyStlAllocator::static_allocate(size_t _bytes)
  21. {
  22. return BX_ALLOC(g_allocator, _bytes);
  23. }
  24. void TinyStlAllocator::static_deallocate(void* _ptr, size_t /*_bytes*/)
  25. {
  26. if (NULL != _ptr)
  27. {
  28. BX_FREE(g_allocator, _ptr);
  29. }
  30. }
  31. } // namespace bgfx
  32. #define TINYSTL_ALLOCATOR bgfx::TinyStlAllocator
  33. #include <tinystl/allocator.h>
  34. #include <tinystl/string.h>
  35. #include <tinystl/unordered_map.h>
  36. #include <tinystl/vector.h>
  37. namespace stl = tinystl;
  38. #include "../../src/shader_spirv.h"
  39. namespace glslang
  40. {
  41. void GlslangToSpv(const glslang::TIntermediate& _intermediate, std::vector<uint32_t>& _spirv);
  42. } // namespace glslang
  43. namespace bgfx { namespace spirv
  44. {
  45. const TBuiltInResource resourceLimits =
  46. {
  47. 32, // MaxLights
  48. 6, // MaxClipPlanes
  49. 32, // MaxTextureUnits
  50. 32, // MaxTextureCoords
  51. 64, // MaxVertexAttribs
  52. 4096, // MaxVertexUniformComponents
  53. 64, // MaxVaryingFloats
  54. 32, // MaxVertexTextureImageUnits
  55. 80, // MaxCombinedTextureImageUnits
  56. 32, // MaxTextureImageUnits
  57. 4096, // MaxFragmentUniformComponents
  58. 32, // MaxDrawBuffers
  59. 128, // MaxVertexUniformVectors
  60. 8, // MaxVaryingVectors
  61. 16, // MaxFragmentUniformVectors
  62. 16, // MaxVertexOutputVectors
  63. 15, // MaxFragmentInputVectors
  64. -8, // MinProgramTexelOffset
  65. 7, // MaxProgramTexelOffset
  66. 8, // MaxClipDistances
  67. 65535, // MaxComputeWorkGroupCountX
  68. 65535, // MaxComputeWorkGroupCountY
  69. 65535, // MaxComputeWorkGroupCountZ
  70. 1024, // MaxComputeWorkGroupSizeX
  71. 1024, // MaxComputeWorkGroupSizeY
  72. 64, // MaxComputeWorkGroupSizeZ
  73. 1024, // MaxComputeUniformComponents
  74. 16, // MaxComputeTextureImageUnits
  75. 8, // MaxComputeImageUniforms
  76. 8, // MaxComputeAtomicCounters
  77. 1, // MaxComputeAtomicCounterBuffers
  78. 60, // MaxVaryingComponents
  79. 64, // MaxVertexOutputComponents
  80. 64, // MaxGeometryInputComponents
  81. 128, // MaxGeometryOutputComponents
  82. 128, // MaxFragmentInputComponents
  83. 8, // MaxImageUnits
  84. 8, // MaxCombinedImageUnitsAndFragmentOutputs
  85. 8, // MaxCombinedShaderOutputResources
  86. 0, // MaxImageSamples
  87. 0, // MaxVertexImageUniforms
  88. 0, // MaxTessControlImageUniforms
  89. 0, // MaxTessEvaluationImageUniforms
  90. 0, // MaxGeometryImageUniforms
  91. 8, // MaxFragmentImageUniforms
  92. 8, // MaxCombinedImageUniforms
  93. 16, // MaxGeometryTextureImageUnits
  94. 256, // MaxGeometryOutputVertices
  95. 1024, // MaxGeometryTotalOutputComponents
  96. 1024, // MaxGeometryUniformComponents
  97. 64, // MaxGeometryVaryingComponents
  98. 128, // MaxTessControlInputComponents
  99. 128, // MaxTessControlOutputComponents
  100. 16, // MaxTessControlTextureImageUnits
  101. 1024, // MaxTessControlUniformComponents
  102. 4096, // MaxTessControlTotalOutputComponents
  103. 128, // MaxTessEvaluationInputComponents
  104. 128, // MaxTessEvaluationOutputComponents
  105. 16, // MaxTessEvaluationTextureImageUnits
  106. 1024, // MaxTessEvaluationUniformComponents
  107. 120, // MaxTessPatchComponents
  108. 32, // MaxPatchVertices
  109. 64, // MaxTessGenLevel
  110. 16, // MaxViewports
  111. 0, // MaxVertexAtomicCounters
  112. 0, // MaxTessControlAtomicCounters
  113. 0, // MaxTessEvaluationAtomicCounters
  114. 0, // MaxGeometryAtomicCounters
  115. 8, // MaxFragmentAtomicCounters
  116. 8, // MaxCombinedAtomicCounters
  117. 1, // MaxAtomicCounterBindings
  118. 0, // MaxVertexAtomicCounterBuffers
  119. 0, // MaxTessControlAtomicCounterBuffers
  120. 0, // MaxTessEvaluationAtomicCounterBuffers
  121. 0, // MaxGeometryAtomicCounterBuffers
  122. 1, // MaxFragmentAtomicCounterBuffers
  123. 1, // MaxCombinedAtomicCounterBuffers
  124. 16384, // MaxAtomicCounterBufferSize
  125. 4, // MaxTransformFeedbackBuffers
  126. 64, // MaxTransformFeedbackInterleavedComponents
  127. 8, // MaxCullDistances
  128. 8, // MaxCombinedClipAndCullDistances
  129. 4, // MaxSamples
  130. { // limits
  131. 1, // nonInductiveForLoops
  132. 1, // whileLoops
  133. 1, // doWhileLoops
  134. 1, // generalUniformIndexing
  135. 1, // generalAttributeMatrixVectorIndexing
  136. 1, // generalVaryingIndexing
  137. 1, // generalSamplerIndexing
  138. 1, // generalVariableIndexing
  139. 1, // generalConstantMatrixVectorIndexing
  140. },
  141. };
  142. bool printAsm(uint32_t _offset, const SpvInstruction& _instruction, void* _userData)
  143. {
  144. BX_UNUSED(_userData);
  145. char temp[512];
  146. toString(temp, sizeof(temp), _instruction);
  147. BX_TRACE("%5d: %s", _offset, temp);
  148. return true;
  149. }
  150. struct SpvReflection
  151. {
  152. struct TypeId
  153. {
  154. enum Enum
  155. {
  156. Void,
  157. Bool,
  158. Int32,
  159. Int64,
  160. Uint32,
  161. Uint64,
  162. Float,
  163. Double,
  164. Vector,
  165. Matrix,
  166. Count
  167. };
  168. TypeId()
  169. : baseType(Enum::Count)
  170. , type(Enum::Count)
  171. {
  172. }
  173. Enum baseType;
  174. Enum type;
  175. uint32_t numComponents;
  176. stl::string toString()
  177. {
  178. stl::string result;
  179. switch (type)
  180. {
  181. case Float:
  182. result.append("float");
  183. break;
  184. case Vector:
  185. bx::stringPrintf(result, "vec%d"
  186. , numComponents
  187. );
  188. break;
  189. case Matrix:
  190. bx::stringPrintf(result, "mat%d"
  191. , numComponents
  192. );
  193. default:
  194. break;
  195. }
  196. return result;
  197. }
  198. };
  199. struct Id
  200. {
  201. struct Variable
  202. {
  203. Variable()
  204. : decoration(SpvDecoration::Count)
  205. , builtin(SpvBuiltin::Count)
  206. , storageClass(SpvStorageClass::Count)
  207. , location(UINT32_MAX)
  208. , offset(UINT32_MAX)
  209. , type(UINT32_MAX)
  210. {
  211. }
  212. stl::string name;
  213. SpvDecoration::Enum decoration;
  214. SpvBuiltin::Enum builtin;
  215. SpvStorageClass::Enum storageClass;
  216. uint32_t location;
  217. uint32_t offset;
  218. uint32_t type;
  219. };
  220. typedef stl::vector<Variable> MemberArray;
  221. Variable var;
  222. MemberArray members;
  223. };
  224. typedef stl::unordered_map<uint32_t, TypeId> TypeIdMap;
  225. typedef stl::unordered_map<uint32_t, Id> IdMap;
  226. TypeIdMap typeIdMap;
  227. IdMap idMap;
  228. stl::string getTypeName(uint32_t _typeId)
  229. {
  230. return getTypeId(_typeId).toString();
  231. }
  232. Id& getId(uint32_t _id)
  233. {
  234. IdMap::iterator it = idMap.find(_id);
  235. if (it == idMap.end() )
  236. {
  237. Id id;
  238. stl::pair<IdMap::iterator, bool> result = idMap.insert(stl::make_pair(_id, id) );
  239. it = result.first;
  240. }
  241. return it->second;
  242. }
  243. Id::Variable& get(uint32_t _id, uint32_t _idx)
  244. {
  245. Id& id = getId(_id);
  246. id.members.resize(bx::uint32_max(_idx+1, uint32_t(id.members.size() ) ) );
  247. return id.members[_idx];
  248. }
  249. TypeId& getTypeId(uint32_t _id)
  250. {
  251. TypeIdMap::iterator it = typeIdMap.find(_id);
  252. if (it == typeIdMap.end() )
  253. {
  254. TypeId id;
  255. stl::pair<TypeIdMap::iterator, bool> result = typeIdMap.insert(stl::make_pair(_id, id) );
  256. it = result.first;
  257. }
  258. return it->second;
  259. }
  260. void update(uint32_t _id, const stl::string& _name)
  261. {
  262. getId(_id).var.name = _name;
  263. }
  264. BX_NO_INLINE void update(Id::Variable& _variable, SpvDecoration::Enum _decoration, uint32_t _literal)
  265. {
  266. _variable.decoration = _decoration;
  267. switch (_decoration)
  268. {
  269. case SpvDecoration::Location:
  270. _variable.location = _literal;
  271. break;
  272. case SpvDecoration::Offset:
  273. _variable.offset = _literal;
  274. break;
  275. case SpvDecoration::BuiltIn:
  276. _variable.builtin = SpvBuiltin::Enum(_literal);
  277. break;
  278. default:
  279. break;
  280. }
  281. }
  282. BX_NO_INLINE void update(Id::Variable& _variable, uint32_t _type, SpvStorageClass::Enum _storageClass)
  283. {
  284. _variable.type = _type;
  285. _variable.storageClass = _storageClass;
  286. }
  287. void update(uint32_t _id, SpvDecoration::Enum _decoration, uint32_t _literal)
  288. {
  289. update(getId(_id).var, _decoration, _literal);
  290. }
  291. void update(uint32_t _id, uint32_t _type, SpvStorageClass::Enum _storageClass)
  292. {
  293. update(getId(_id).var, _type, _storageClass);
  294. }
  295. void update(uint32_t _id, uint32_t _idx, const stl::string& _name)
  296. {
  297. Id::Variable& var = get(_id, _idx);
  298. var.name = _name;
  299. }
  300. BX_NO_INLINE void update(uint32_t _id, uint32_t _idx, SpvDecoration::Enum _decoration, uint32_t _literal)
  301. {
  302. update(get(_id, _idx), _decoration, _literal);
  303. }
  304. void update(uint32_t _id, TypeId::Enum _type)
  305. {
  306. TypeId& type = getTypeId(_id);
  307. type.type = _type;
  308. }
  309. void update(uint32_t _id, TypeId::Enum _type, uint32_t _baseTypeId, uint32_t _numComonents)
  310. {
  311. TypeId& type = getTypeId(_id);
  312. type.type = _type;
  313. type.baseType = getTypeId(_baseTypeId).type;
  314. type.numComponents = _numComonents;
  315. }
  316. };
  317. bool spvParse(uint32_t _offset, const SpvInstruction& _instruction, void* _userData)
  318. {
  319. BX_UNUSED(_offset);
  320. SpvReflection* spv = (SpvReflection*)_userData;
  321. switch (_instruction.opcode)
  322. {
  323. case SpvOpcode::Name:
  324. spv->update(_instruction.result
  325. , _instruction.operand[0].literalString
  326. );
  327. break;
  328. case SpvOpcode::Decorate:
  329. spv->update(_instruction.operand[0].data
  330. , SpvDecoration::Enum(_instruction.operand[1].data)
  331. , _instruction.operand[2].data
  332. );
  333. break;
  334. case SpvOpcode::MemberName:
  335. spv->update(_instruction.result
  336. , _instruction.operand[0].data
  337. , _instruction.operand[1].literalString
  338. );
  339. break;
  340. case SpvOpcode::MemberDecorate:
  341. spv->update(_instruction.operand[0].data
  342. , _instruction.operand[1].data
  343. , SpvDecoration::Enum(_instruction.operand[2].data)
  344. , _instruction.operand[3].data
  345. );
  346. break;
  347. case SpvOpcode::Variable:
  348. spv->update(_instruction.result
  349. , _instruction.type
  350. , SpvStorageClass::Enum(_instruction.operand[0].data)
  351. );
  352. break;
  353. case SpvOpcode::TypeVoid:
  354. spv->update(_instruction.result, SpvReflection::TypeId::Void);
  355. break;
  356. case SpvOpcode::TypeBool:
  357. spv->update(_instruction.result, SpvReflection::TypeId::Bool);
  358. break;
  359. case SpvOpcode::TypeInt:
  360. spv->update(_instruction.result
  361. , 32 == _instruction.operand[0].data
  362. ? 0 == _instruction.operand[1].data
  363. ? SpvReflection::TypeId::Uint32
  364. : SpvReflection::TypeId::Int32
  365. : 0 == _instruction.operand[1].data
  366. ? SpvReflection::TypeId::Uint64
  367. : SpvReflection::TypeId::Int64
  368. );
  369. break;
  370. case SpvOpcode::TypeFloat:
  371. spv->update(_instruction.result
  372. , 32 == _instruction.operand[0].data
  373. ? SpvReflection::TypeId::Float
  374. : SpvReflection::TypeId::Double
  375. );
  376. break;
  377. case SpvOpcode::TypeVector:
  378. spv->update(_instruction.result
  379. , SpvReflection::TypeId::Vector
  380. , _instruction.operand[0].data
  381. , _instruction.operand[1].data
  382. );
  383. break;
  384. case SpvOpcode::TypeMatrix:
  385. spv->update(_instruction.result
  386. , SpvReflection::TypeId::Matrix
  387. , _instruction.operand[0].data
  388. , _instruction.operand[1].data
  389. );
  390. break;
  391. case SpvOpcode::TypeImage:
  392. case SpvOpcode::TypeSampler:
  393. case SpvOpcode::TypeSampledImage:
  394. break;
  395. case SpvOpcode::TypeStruct:
  396. for (uint32_t ii = 0, num = _instruction.numOperands; ii < num; ++ii)
  397. {
  398. SpvReflection::Id::Variable& var = spv->get(_instruction.result, ii);
  399. var.type = _instruction.operand[ii].data;
  400. }
  401. break;
  402. default:
  403. break;
  404. }
  405. return true;
  406. }
  407. void disassemble(bx::WriterI* _writer, bx::ReaderSeekerI* _reader, bx::Error* _err)
  408. {
  409. BX_UNUSED(_writer);
  410. uint32_t magic;
  411. bx::peek(_reader, magic);
  412. SpvReflection spvx;
  413. if (magic == SPV_CHUNK_HEADER)
  414. {
  415. SpirV spirv;
  416. read(_reader, spirv, _err);
  417. parse(spirv.shader, spvParse, &spvx, _err);
  418. for (SpvReflection::IdMap::const_iterator it = spvx.idMap.begin(), itEnd = spvx.idMap.end(); it != itEnd; ++it)
  419. {
  420. const SpvReflection::Id& id = it->second;
  421. uint32_t num = uint32_t(id.members.size() );
  422. if (0 < num
  423. && 0 != strcmp(id.var.name.c_str(), "gl_PerVertex") )
  424. {
  425. printf("%3d: %s %d %s\n"
  426. , it->first
  427. , id.var.name.c_str()
  428. , id.var.location
  429. , getName(id.var.storageClass)
  430. );
  431. printf("{\n");
  432. for (uint32_t ii = 0; ii < num; ++ii)
  433. {
  434. const SpvReflection::Id::Variable& var = id.members[ii];
  435. printf("\t\t%s %s %d %s\n"
  436. , spvx.getTypeName(var.type).c_str()
  437. , var.name.c_str()
  438. , var.offset
  439. , getName(var.storageClass)
  440. );
  441. }
  442. printf("}\n");
  443. }
  444. }
  445. }
  446. }
  447. struct DebugOutputWriter : public bx::WriterI
  448. {
  449. virtual int32_t write(const void* _data, int32_t _size, bx::Error*) BX_OVERRIDE
  450. {
  451. char* out = (char*)alloca(_size + 1);
  452. memcpy(out, _data, _size);
  453. out[_size] = '\0';
  454. printf("%s", out);
  455. return _size;
  456. }
  457. };
  458. static EShLanguage getLang(char _p)
  459. {
  460. switch (_p)
  461. {
  462. case 'c': return EShLangCompute;
  463. case 'f': return EShLangFragment;
  464. default: break;
  465. }
  466. return EShLangVertex;
  467. }
  468. // static void printError(spv_message_level_t, const char*, const spv_position_t&, const char* _message)
  469. // {
  470. // fprintf(stderr, "%s\n", _message);
  471. // }
  472. static bool compile(bx::CommandLine& _cmdLine, uint32_t _version, const std::string& _code, bx::WriterI* _writer)
  473. {
  474. BX_UNUSED(_cmdLine, _version, _code, _writer);
  475. const char* profile = _cmdLine.findOption('p', "profile");
  476. if (NULL == profile)
  477. {
  478. fprintf(stderr, "Error: Shader profile must be specified.\n");
  479. return false;
  480. }
  481. glslang::InitializeProcess();
  482. glslang::TProgram* program = new glslang::TProgram;
  483. EShLanguage stage = getLang(profile[0]);
  484. glslang::TShader* shader = new glslang::TShader(stage);
  485. EShMessages messages = EShMessages(0
  486. | EShMsgDefault
  487. | EShMsgReadHlsl
  488. | EShMsgVulkanRules
  489. | EShMsgSpvRules
  490. );
  491. const char* shaderStrings[] = { _code.c_str() };
  492. const char* shaderNames[] = { "" };
  493. shader->setStringsWithLengthsAndNames(
  494. shaderStrings
  495. , NULL
  496. , shaderNames
  497. , BX_COUNTOF(shaderNames)
  498. );
  499. bool compiled = shader->parse(&resourceLimits
  500. , 110
  501. , false
  502. , messages
  503. );
  504. bool linked = false;
  505. bool validated = true;
  506. bool optimized = true;
  507. if (!compiled)
  508. {
  509. const char* log = shader->getInfoLog();
  510. if (NULL != log)
  511. {
  512. int32_t source = 0;
  513. int32_t line = 0;
  514. int32_t column = 0;
  515. int32_t start = 0;
  516. int32_t end = INT32_MAX;
  517. const char* err = strstr(log, "ERROR:");
  518. bool found = false;
  519. if (NULL != err)
  520. {
  521. found = 2 == sscanf(err, "ERROR: %u:%u: '", &source, &line);
  522. if (found)
  523. {
  524. ++line;
  525. }
  526. }
  527. if (found)
  528. {
  529. start = bx::uint32_imax(1, line-10);
  530. end = start + 20;
  531. }
  532. printCode(_code.c_str(), line, start, end, column);
  533. fprintf(stderr, "%s\n", log);
  534. }
  535. }
  536. else
  537. {
  538. program->addShader(shader);
  539. linked = true
  540. && program->link(messages)
  541. && program->mapIO()
  542. ;
  543. if (!linked)
  544. {
  545. const char* log = program->getInfoLog();
  546. if (NULL != log)
  547. {
  548. fprintf(stderr, "%s\n", log);
  549. }
  550. }
  551. else
  552. {
  553. // program->buildReflection();
  554. // fprintf(stderr, "attributes %d, uniforms %d\n"
  555. // , program->getNumLiveAttributes()
  556. // , program->getNumLiveUniformVariables()
  557. // );
  558. // program->dumpReflection();
  559. glslang::TIntermediate* intermediate = program->getIntermediate(stage);
  560. std::vector<uint32_t> spirv;
  561. glslang::GlslangToSpv(*intermediate, spirv);
  562. spv::spirvbin_t spvBin;
  563. spvBin.remap(
  564. spirv
  565. , 0
  566. | spv::spirvbin_t::DCE_ALL
  567. | spv::spirvbin_t::OPT_ALL
  568. | spv::spirvbin_t::MAP_ALL
  569. // | spv::spirvbin_t::STRIP
  570. );
  571. bx::Error err;
  572. DebugOutputWriter writer;
  573. bx::MemoryReader reader(spirv.data(), uint32_t(spirv.size()*4) );
  574. disassemble(&writer, &reader, &err);
  575. #if 0
  576. spvtools::SpirvTools tools(SPV_ENV_VULKAN_1_0);
  577. tools.SetMessageConsumer(printError);
  578. validated = tools.Validate(spirv);
  579. if (!validated)
  580. {
  581. std::string out;
  582. tools.Disassemble(spirv, &out);
  583. printf("%s\n", out.c_str());
  584. }
  585. if (validated)
  586. {
  587. spvtools::Optimizer optm(SPV_ENV_VULKAN_1_0);
  588. optm.SetMessageConsumer(printError);
  589. optm
  590. .RegisterPass(spvtools::CreateStripDebugInfoPass() )
  591. // .RegisterPass(spvtools::CreateSetSpecConstantDefaultValuePass({ {1, "42" } }) )
  592. .RegisterPass(spvtools::CreateFreezeSpecConstantValuePass() )
  593. .RegisterPass(spvtools::CreateFoldSpecConstantOpAndCompositePass() )
  594. .RegisterPass(spvtools::CreateEliminateDeadConstantPass() )
  595. .RegisterPass(spvtools::CreateUnifyConstantPass() )
  596. ;
  597. optimized = optm.Run(spirv.data(), spirv.size(), &spirv);
  598. }
  599. #endif // 0
  600. if (optimized)
  601. {
  602. uint16_t shaderSize = (uint16_t)spirv.size()*sizeof(uint32_t);
  603. bx::write(_writer, shaderSize);
  604. bx::write(_writer, spirv.data(), shaderSize);
  605. uint8_t nul = 0;
  606. bx::write(_writer, nul);
  607. }
  608. }
  609. }
  610. delete program;
  611. delete shader;
  612. glslang::FinalizeProcess();
  613. return compiled && linked && validated && optimized;
  614. }
  615. } // namespace spirv
  616. bool compileSPIRVShader(bx::CommandLine& _cmdLine, uint32_t _version, const std::string& _code, bx::WriterI* _writer)
  617. {
  618. return spirv::compile(_cmdLine, _version, _code, _writer);
  619. }
  620. } // namespace bgfx