6d981e5a48821266c2b5878fb2fbdca025f4f02f
[platform/core/uifw/dali-core.git] / dali / graphics / vulkan / spirv / vulkan-spirv.cpp
1 /*
2  * Copyright (c) 2018 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 #include <iostream>
19
20 #include <dali/graphics/vulkan/spirv/vulkan-spirv.h>
21 #include <dali/graphics/vulkan/spirv/vulkan-spirv-opcode.h>
22
23 #define debug( x ) std::cout << x << std::endl;
24
25 namespace Dali
26 {
27 namespace Graphics
28 {
29 namespace Vulkan
30 {
31 namespace SpirV
32 {
33 SPIRVShader::Impl& SPIRVShader::GetImplementation() const
34 {
35   return *mImpl;
36 }
37
38 #pragma GCC diagnostic push
39 #pragma GCC diagnostic ignored "-Wframe-larger-than="
40 struct SPIRVShader::Impl
41 {
42   /**
43    * 32bit word needed to identify SPIRV code
44    */
45   static constexpr uint32_t MAGIC_NUMBER{0x07230203u};
46
47   /**
48    * SPIRV header binary structure
49    */
50   struct Header
51   {
52     uint32_t magicNumber;
53     uint32_t versionNumber; // 0 | major | minor | 0
54     uint32_t generatorMagicNumber;
55     uint32_t bound;
56     uint32_t reserved;
57   };
58
59   /**
60    * Stores descriptorset bindings and createinfo data used by reflection
61    */
62   struct DescriptorSetLayoutAndBindingInfo
63   {
64     std::vector<vk::DescriptorSetLayoutBinding> bindings;
65     vk::DescriptorSetLayoutCreateInfo createInfo;
66   };
67
68   SPIRVOpCode& FindByResultId( uint32_t resultId ) const
69   {
70     return *(opResults[resultId] );
71   }
72
73   SPIRVOpCode* FindByResultId( SPIRVOpCode& opcode ) const
74   {
75     if(!opcode.hasResult)
76     {
77       return nullptr;
78     }
79     return opResults[opcode.localData.resultId];
80   }
81
82   SPIRVOpCode* FindByResultPtrId( uint32_t resultId ) const
83   {
84     if( resultId < opResults.size() )
85     {
86       return opResults[resultId];
87     }
88     return nullptr;
89   }
90
91   /**
92    * Constructor
93    * @param pData
94    * @param size
95    * @param stages
96    */
97   Impl( void* pData, std::size_t size, vk::ShaderStageFlags stages )
98   {
99     data.resize( size );
100     auto begin = reinterpret_cast<uint32_t*>( pData );
101     auto end   = begin + size;
102     std::copy( begin, end, data.begin() );
103   }
104
105   /**
106    * Constructor
107    * @tparam T
108    * @param buffer
109    * @param stages
110    */
111   template<typename T>
112   explicit Impl( std::vector<T> buffer, vk::ShaderStageFlags stages )
113   {
114     data.resize( ( buffer.size() * sizeof( buffer[0] ) ) / sizeof( uint32_t ) );
115     auto begin = reinterpret_cast<uint32_t*>( &*buffer.begin() );
116     auto end   = reinterpret_cast<uint32_t*>( &*buffer.end() );
117     std::copy( begin, end, data.begin() );
118   }
119
120   auto FindDecorationsForId( uint32_t id )
121   {
122     std::vector<SPIRVOpCode*> retval{};
123     for( auto&& op : opCodes )
124     {
125       if( op.code == SpvOpDecorate && op.GetParameterU32( 0 ) == id )
126       {
127         retval.push_back( &op );
128       }
129     }
130     return retval;
131   }
132
133   auto FindMemberDecorationsForId( uint32_t id, uint32_t memberIndex )
134   {
135     std::vector<SPIRVOpCode*> retval{};
136     for( auto&& op : opCodes )
137     {
138       if( op.code == SpvOpMemberDecorate && op.GetParameterU32( 0 ) == id && op.GetParameterU32( 1 ) == memberIndex )
139       {
140         retval.push_back( &op );
141       }
142     }
143     return retval;
144   }
145
146   SPIRVOpCode& GetReferencedOpCode( const SPIRVOpCode& opCode, uint32_t refIndex ) const
147   {
148     return FindByResultId( opCode.GetParameterU32( refIndex ) );
149   }
150
151   auto GetDecorationsOpId( const SPIRVOpCode& resultOp )
152   {
153     std::vector<SPIRVOpCode*> retval;
154     if( resultOp.hasResult )
155     {
156       for( auto&& op : opCodes )
157       {
158         if( op == SpvOpDecorate && op.GetParameterU32( 0 ) == resultOp.localData.resultId )
159         {
160           retval.push_back( &op );
161         }
162       }
163     }
164     return retval;
165   }
166
167   bool CheckDecorationForOpId( const SPIRVOpCode& resultOp, SpvDecoration expectedDecoration )
168   {
169     if( resultOp.hasResult )
170     {
171       for( auto&& op : opCodes )
172       {
173         if( op == SpvOpDecorate && op.GetParameterU32( 0 ) == resultOp.localData.resultId &&
174             op.GetParameter<SpvDecoration>( 1 ) == expectedDecoration )
175         {
176           return true;
177         }
178       }
179     }
180     return false;
181   }
182
183   struct SPIRVReflectionData
184   {
185     SPIRVReflectionData() = default;
186     SPIRVReflectionData( const SPIRVReflectionData& ) = default;
187     SPIRVReflectionData& operator=( const SPIRVReflectionData& ) = default;
188     SPIRVReflectionData( SPIRVReflectionData&& ) = delete;
189     SPIRVReflectionData& operator=( SPIRVReflectionData&& ) = delete;
190
191     std::string                                     name{};
192     SPIRVOpCode*                                    op{nullptr};
193     SpvStorageClass                                 storage{SpvStorageClassMax};
194     vk::DescriptorType                              descriptorType{}; // only valid for uniforms
195     std::unordered_map<SpvDecoration, SPIRVOpCode*> decorations{};
196     std::vector<SPIRVReflectionData>                members{}; // used by structs only
197     uint32_t                                        structSize{0u};
198   };
199
200   template<class M, class K>
201   bool MapContains( const M& map, const K& key ) const
202   {
203     return map.find( key ) != map.end();
204   }
205
206
207   template<class V>
208   struct GetResult
209   {
210     GetResult( bool v, V& value)
211     : success(v), result(&value)
212     {
213     }
214
215     GetResult( bool v )
216       : success(v), result(nullptr)
217     {
218     }
219
220     operator V&()
221     {
222       return *result;
223     }
224
225     operator V*()
226     {
227       return result;
228     }
229
230     GetResult() : success( false ), result(nullptr){}
231     bool  success;
232     V*     result;
233   };
234
235   template<class K, class V>
236   GetResult<V> GetMapItem( std::unordered_map<K,V>& map, const K& key )
237   {
238     auto iter = map.find( key );
239     if( iter == map.end() )
240     {
241       return GetResult<V>( false );
242     }
243
244     return GetResult<V>( true, iter->second );
245   }
246
247   struct SPIRVTypeInfo
248   {
249     vk::Format  vkFormat;
250     uint32_t    sizeInBytes;
251     uint32_t    components;
252     uint32_t    rows;
253     uint32_t    componentSizeInBytes;
254   };
255
256   auto GetTypeInfo( const SPIRVOpCode& typeOpCode ) const
257   {
258     auto retval = SPIRVTypeInfo{};
259
260     const vk::Format VEC[] =
261                        {
262                          vk::Format::eUndefined,
263                          vk::Format::eR32Sfloat,
264                          vk::Format::eR32G32Sfloat,
265                          vk::Format::eR32G32B32Sfloat,
266                          vk::Format::eR32G32B32A32Sfloat,
267                        };
268
269     // note: always assumed:
270     // - not normalized
271     // - float
272     // - signed
273     if( typeOpCode == SpvOpTypeMatrix )
274     {
275       retval.components = typeOpCode.GetParameterU32( 2 );
276       retval.rows = retval.components;
277       retval.componentSizeInBytes = sizeof(float);
278       retval.sizeInBytes = (retval.components*retval.components)*retval.componentSizeInBytes;
279       retval.vkFormat = VEC[retval.components];
280     }
281     else if(typeOpCode == SpvOpTypeVector)
282     {
283       retval.components = typeOpCode.GetParameterU32( 2 );
284       retval.rows = 1;
285       retval.componentSizeInBytes = sizeof(float);
286       retval.sizeInBytes = retval.components*retval.componentSizeInBytes;
287       retval.vkFormat = VEC[retval.components];
288     }
289     else if(typeOpCode == SpvOpTypeFloat)
290     {
291       retval.components = 1;
292       retval.rows = 1;
293       retval.componentSizeInBytes = sizeof(float);
294       retval.sizeInBytes = sizeof(float);
295       retval.vkFormat = vk::Format::eR32Sfloat;
296     }
297     else if(typeOpCode == SpvOpTypeInt)
298     {
299       retval.components = 1;
300       retval.rows = 1;
301       retval.componentSizeInBytes = sizeof(uint32_t);
302       retval.sizeInBytes = sizeof(uint32_t);
303       retval.vkFormat = vk::Format::eR32Sint;
304     }
305     else
306     {
307       retval.components = 0;
308       retval.rows = 0;
309       retval.componentSizeInBytes = 0;
310       retval.sizeInBytes = 0;
311       retval.vkFormat = vk::Format::eUndefined;
312     }
313
314     return retval;
315   }
316
317
318
319   auto BuildReflection()
320   {
321     // collect variables
322     using MemberNameArray = std::vector<SPIRVOpCode*>;
323     auto vars = std::vector<SPIRVOpCode*>{};
324     auto opNames = std::unordered_map<uint32_t, SPIRVOpCode*>{};
325
326     // member names, key: struct id, value: ordered vector of OpMemberName ops
327     auto opMemberNames = std::unordered_map<uint32_t, MemberNameArray>{};
328     for( auto&& op : opCodes )
329     {
330       auto id = op.GetParameterU32(0);
331       if( op == SpvOpVariable )
332       {
333         vars.push_back( &op );
334       }
335       else if( op == SpvOpName )
336       {
337         opNames.emplace( id, &op );
338       }
339       else if( op == SpvOpMemberName )
340       {
341         GetResult<MemberNameArray> result{};
342         MemberNameArray* memberNames{ nullptr };
343         if( !(result = GetMapItem( opMemberNames, id )).success )
344         {
345           opMemberNames.emplace(id, MemberNameArray{} );
346           memberNames = &opMemberNames[id];
347         }
348         else
349         {
350           memberNames = result.result;
351         }
352
353         if(memberNames->size() <= op.GetParameterU32(1))
354           memberNames->resize( op.GetParameterU32(1)+1 );
355         (*memberNames)[op.GetParameterU32(1)] = &op;
356       }
357     }
358
359     // find uniforms and inputs
360     auto decorationVariables = std::unordered_map<uint32_t, SPIRVReflectionData>{};
361     auto uniformVariables = std::vector<SPIRVOpCode*>{};
362     auto inputVariables   = std::vector<SPIRVOpCode*>{};
363     auto outputVariables  = std::vector<SPIRVOpCode*>{};
364     for( auto&& op : vars )
365     {
366       auto storage = op->GetParameter<SpvStorageClass>( 2 );
367       bool varFound{false};
368       if( storage == SpvStorageClassUniform || storage == SpvStorageClassUniformConstant )
369       {
370         uniformVariables.emplace_back( op );
371         varFound = true;
372       }
373       else if( storage == SpvStorageClassInput )
374       {
375         inputVariables.emplace_back( op );
376         varFound = true;
377       }
378       else if( storage == SpvStorageClassOutput )
379       {
380         outputVariables.emplace_back( op );
381         varFound = true;
382       }
383
384       // find decorations if variable
385       if( varFound )
386       {
387         auto id = op->localData.resultId;
388         auto decorations = FindDecorationsForId( id );
389         SPIRVReflectionData decorationInfo;
390         decorationInfo.op = op;
391         decorationInfo.storage = storage;
392
393         // update descriptor type if viable
394         decorationInfo.descriptorType = FindDescriptorTypeForVariable( *op );
395
396         for( auto&& decoration : decorations )
397         {
398           auto decorationQualifier = decoration->GetParameter<SpvDecoration>( 1 );
399           decorationInfo.decorations.emplace( decorationQualifier, decoration );
400           std::cout << decorationQualifier << std::endl;
401         }
402         decorationVariables.emplace( id, decorationInfo );
403
404         // store name if element is named
405         GetResult<SPIRVOpCode*> name{};
406
407         bool foundName = false;
408         if( (name = GetMapItem( opNames, id )).success )
409         {
410
411           // variable may not be named ( global scope of the shader )
412           if( !(*name.result)->GetParameterAsString( 1 ).empty() )
413           {
414             std::cout <<"Found name\n";
415             decorationVariables[id].name = (*name.result)->GetParameterAsString( 1 );
416             foundName = true;
417           }
418         }
419
420         // continue if name hasn't been found, this means the variable is an uniform
421         // in the global scope
422         if( !foundName )
423         {
424           auto pointerId = op->GetParameterU32(0);
425           auto pointer = FindByResultId( pointerId );
426           auto pointerToType = FindByResultId( pointer.GetParameterU32(2) );
427
428           // find name of the structure
429           GetResult<SPIRVOpCode*> retval{};
430           if( (retval = GetMapItem( opNames, pointerToType.localData.resultId )).success )
431           {
432             std::cout << "Found: " << (*retval.result)->GetParameterAsString(1) << std::endl;
433             decorationVariables[id].name = (*retval.result)->GetParameterAsString(1);
434           }
435
436           // depending on the type, we may need to extract members, member types as well
437           // as other relevant data
438           if( pointerToType == SpvOpTypeStruct )
439           {
440
441             auto memberCount = pointerToType.localData.count-2;
442             std::cout << "Found struct, look for member names and member decorations: "
443                       "member count: " << memberCount << std::endl;
444
445             // for each member resolve type and compute size of the structure
446             auto memberNames = opMemberNames[ pointerToType.localData.resultId ];
447             for( auto i = 0u; i < memberCount; ++i )
448             {
449               auto& memberName = memberNames[i];
450               SPIRVReflectionData memberOpInfo;
451               memberOpInfo.name = memberName->GetParameterAsString(2);
452               auto memberResultId = pointerToType.GetParameterU32( i+1 );
453               memberOpInfo.op = FindByResultPtrId( memberResultId );
454
455               // look for decoration for each member ( needed in order to build data structures )
456               auto memberDecorationOps = FindMemberDecorationsForId( pointerToType.localData.resultId, i );
457               for( auto&& mop : memberDecorationOps )
458               {
459                 memberOpInfo.decorations.emplace( mop->GetParameter<SpvDecoration>( 2 ), mop );
460               }
461               decorationVariables[id].members.emplace_back(memberOpInfo);
462               std::cout << "memberName: " << memberName->GetParameterAsString(2);
463               std::cout << std::endl;
464             }
465
466             uint32_t structSize = 0u;
467
468             // for last member update size of the data structure ( for blocks only )
469             if(memberCount)
470             {
471               if( memberCount > 0 )
472               {
473                 auto& lastMember = decorationVariables[id].members.back();
474
475                 if( MapContains( lastMember.decorations, SpvDecorationOffset ) )
476                 {
477                   structSize = lastMember.decorations[SpvDecorationOffset]->GetParameterU32(3);
478                 }
479                 auto typeInfo = GetTypeInfo( *lastMember.op );
480                 structSize += typeInfo.sizeInBytes;
481               }
482               decorationVariables[id].structSize = structSize;
483             }
484             std::cout << "struct size: " << structSize << std::endl;
485           }
486         }
487       }
488     }
489
490     std::cout << "Found " << uniformVariables.size() << " variables\n";
491
492     return decorationVariables;
493   }
494
495   auto LoadOpCodes( const std::vector<SPIRVWord>& _data )
496   {
497     auto retval = std::vector<SPIRVOpCode>{};
498
499     // test if we have valid SPIRV header
500     auto iter = data.begin();
501     if( !CheckHeader() )
502     {
503       debug( "Not SPIRV!" );
504       return retval;
505     }
506
507     debug( "SPIR-V detected" );
508     std::advance( iter, 5u ); // skip header
509
510     while( iter != data.end() )
511     {
512       auto opword    = *iter;
513       auto wordCount = ( ( opword >> 16 ) & 0xFFFF );
514       auto opCode    = ( (opword)&0xFFFF );
515
516       auto& op = FindOpCode( opCode );
517
518       if( op != OP_CODE_NULL )
519       {
520         uint32_t resultIndex{0};
521         int      resultIndexOffset = 1;
522         int32_t  resultType{0u};
523
524         // make a copy
525         retval.emplace_back( op );
526         auto& opcode           = retval.back();
527         opcode.localData.start = &*iter;
528         opcode.localData.count = wordCount;
529
530         // update result type and index for non-void opcodes
531         if( op.hasResultType )
532         {
533           resultIndexOffset++;
534         }
535         if( op.hasResult )
536         {
537           if( op.hasResultType )
538           {
539             resultType = static_cast<int32_t>( *( iter + 1 ) );
540           }
541           resultIndex                 = *( iter + resultIndexOffset );
542           opcode.localData.resultId   = resultIndex;
543           opcode.localData.resultType = resultType;
544         }
545       }
546
547       // next instruction
548       std::advance( iter, wordCount );
549     }
550
551     return retval;
552   }
553
554   auto CreateOpResults( std::vector<SPIRVOpCode>& _opcodes )
555   {
556     auto retval = std::vector<SPIRVOpCode*>{};
557     for( auto i = 0u; i < _opcodes.size(); ++i )
558     {
559       const auto& op = _opcodes[i];
560       if( op.hasResult )
561       {
562         if( retval.size() <= op.localData.resultId )
563         {
564           retval.resize( op.localData.resultId + 1 );
565         }
566         retval[op.localData.resultId] = &_opcodes[i];
567       }
568     }
569     return retval;
570   }
571
572   vk::DescriptorType FindDescriptorTypeForVariable( SPIRVOpCode& opVariable )
573   {
574     vk::DescriptorType descriptorType{};
575
576     auto storageClass = opVariable.GetParameter<SpvStorageClass>(2);
577
578     // we need to detect storage by call into function
579     if (storageClass == SpvStorageClassUniformConstant)
580     {
581       auto& resource = opVariable;
582       if (TestStorageImageDescriptor(resource))
583       {
584         descriptorType = vk::DescriptorType::eStorageImage;
585       }
586       else if(TestSamplerDescriptor(resource))
587       {
588         descriptorType = vk::DescriptorType::eSampler;
589       }
590       else if(TestSampledImageDescriptor(resource))
591       {
592         descriptorType = vk::DescriptorType::eSampledImage;
593       }
594       else if(TestCombinedImageSamplerDescriptor(resource))
595       {
596         descriptorType = vk::DescriptorType::eCombinedImageSampler;
597       }
598       else if(TestUniformTexelBufferDescriptor(resource))
599       {
600         descriptorType = vk::DescriptorType::eUniformTexelBuffer;
601       }
602       else if(TestStorageTexelBufferDescriptor(resource))
603       {
604         descriptorType = vk::DescriptorType::eStorageTexelBuffer;
605       }
606       else
607       {
608         // @todo check the shader, something hasn't been recognized
609         descriptorType = vk::DescriptorType{};
610       }
611
612       if(descriptorType != vk::DescriptorType{})
613       {
614         //uniformResources.push_back( &resource );
615       }
616     }
617     else if(storageClass == SpvStorageClassUniform)
618     {
619       descriptorType = vk::DescriptorType::eUniformBuffer;
620     }
621     return descriptorType;
622   }
623
624   auto GenerateVulkanDescriptorSetLayouts()
625   {
626     // key: descriptor set, value: ds layout create info and binding
627     auto vkDescriptorSetLayoutCreateInfos = std::unordered_map<uint32_t, DescriptorSetLayoutAndBindingInfo>{};
628
629     for(auto&& symbol : reflectionData)
630     {
631       auto storage = symbol.second.storage;
632       auto& symbolData = symbol.second;
633       if( storage == SpvStorageClassUniform || storage == SpvStorageClassUniformConstant )
634       {
635         auto binding = MapContains( symbolData.decorations, SpvDecorationBinding ) ? symbolData.decorations[SpvDecorationBinding]->GetParameterU32(2) : 0u;
636         auto descriptorSet = MapContains( symbolData.decorations, SpvDecorationDescriptorSet ) ? symbolData.decorations[SpvDecorationDescriptorSet]->GetParameterU32(2) : 0u;
637         debug("found layout: binding: " << binding << " ds: " << descriptorSet << ", type: " << U32(symbolData.descriptorType) );
638
639         auto& ds = (MapContains( vkDescriptorSetLayoutCreateInfos, descriptorSet ) ?
640                     vkDescriptorSetLayoutCreateInfos[descriptorSet] :
641                     (*vkDescriptorSetLayoutCreateInfos.emplace( descriptorSet, DescriptorSetLayoutAndBindingInfo{} ).first).second);
642
643
644         ds.bindings.emplace_back( vk::DescriptorSetLayoutBinding{}.setBinding( binding )
645                                                                   .setDescriptorCount( 1 )
646                                                                   .setDescriptorType( symbolData.descriptorType )
647                                                                   .setStageFlags( vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment ) );
648       }
649     }
650
651     // sort bindings and complete create info structures
652     for( auto&& ds : vkDescriptorSetLayoutCreateInfos )
653     {
654       std::sort(ds.second.bindings.begin(), ds.second.bindings.end(), []( auto& a, auto& b ){ return a.binding < b.binding; });
655       ds.second.createInfo.setBindingCount( U32(ds.second.bindings.size()) );
656       ds.second.createInfo.pBindings = ds.second.bindings.data();
657     }
658
659     return vkDescriptorSetLayoutCreateInfos;
660   }
661
662   bool Initialise()
663   {
664     // load opcodes
665     opCodes = std::move(LoadOpCodes(data));
666
667     // create results lookup array
668     opResults = std::move(CreateOpResults(opCodes));
669
670     // build reflection map
671     reflectionData = std::move(BuildReflection());
672
673     // build vulkan descriptor set layout infos
674     descriptorSetLayoutCreateInfoMap = std::move(GenerateVulkanDescriptorSetLayouts());
675
676     // generate additional reflection structures
677     uint32_t blockIndex = 0u;
678     for( auto&& symbol : reflectionData )
679     {
680       auto& symbolData = symbol.second;
681       auto binding = MapContains( symbolData.decorations, SpvDecorationBinding ) ? symbolData.decorations[SpvDecorationBinding]->GetParameterU32(2) : 0u;
682       auto descriptorSet = MapContains( symbolData.decorations, SpvDecorationDescriptorSet ) ? symbolData.decorations[SpvDecorationDescriptorSet]->GetParameterU32(2) : 0u;
683
684       if( symbolData.storage == SpvStorageClassUniform )
685       {
686         auto block = SPIRVUniformBlock{};
687         block.name = symbolData.name;
688         block.size = symbolData.structSize;
689         block.binding = binding;
690         block.descriptorSet = descriptorSet;
691
692         uint32_t location{ 0u };
693         for( auto&& member : symbolData.members )
694         {
695           auto blockMember = SPIRVUniformBlockMember{};
696           blockMember.name = member.name;
697           blockMember.location = location++;
698           blockMember.offset = MapContains( member.decorations, SpvDecorationOffset ) ? member.decorations[SpvDecorationOffset]->GetParameterU32(3) : 0u;
699           blockMember.blockIndex = blockIndex;
700           block.members.emplace_back( blockMember );
701         }
702         blockIndex++;
703         uniformBlockReflection.emplace_back( block );
704
705       }
706       else if( symbolData.storage == SpvStorageClassUniformConstant )
707       {
708         auto opaque = SPIRVUniformOpaque{};
709         opaque.name = symbolData.name;
710         opaque.binding = binding;
711         opaque.descriptorSet = descriptorSet;
712         opaque.type =  symbolData.descriptorType;
713         uniformOpaqueReflection.emplace_back( opaque );
714       }
715     }
716
717     return true;
718   }
719
720   /**
721    * Recognizes descriptor type VkDescriptorTypeStorageImage
722    * GLSL:
723    *      layout (set=m, binding=n, r32f) uniform image2D myStorageImage;
724    *
725    * SPIR-V:
726    *      %7 = OpTypeImage %6 2D 0 0 0 2 R32f
727    *      %8 = OpTypePointer UniformConstant %7
728    *      %9 = OpVariable %8 UniformConstant
729    * @param resource
730    * @return
731    */
732   bool TestStorageImageDescriptor( SPIRVOpCode& opcode )
733   {
734     auto opPointer = &FindByResultId( opcode.GetParameterU32(0) );
735     if( (opPointer && *opPointer == SpvOpTypePointer) )
736     {
737       auto& opTypeImage = GetReferencedOpCode( *opPointer, 2 );
738       if( opTypeImage == SpvOpTypeImage && opTypeImage.GetParameterU32( 6 ) == 2 )
739       {
740         return true;
741       }
742     }
743     return false;
744   }
745
746   /**
747    * Recognizes descriptor type VkDescriptorTypeSampler
748    * GLSL:
749           layout (set=m, binding=n) uniform sampler mySampler;
750    *
751    * SPIR-V:
752           %3 = OpTypeFunction %2
753           %6 = OpTypeSampler
754           %7 = OpTypePointer UniformConstant %6
755           %8 = OpVariable %7 UniformConstant
756
757    * @param resource
758    * @return
759    */
760   bool TestSamplerDescriptor( SPIRVOpCode& opcode )
761   {
762     auto opPointer = &FindByResultId( opcode.GetParameterU32(0) );
763     if( (opPointer && *opPointer == SpvOpTypePointer) )
764     {
765       auto& opTypeSampler = GetReferencedOpCode( *opPointer, 2 );
766       if( opTypeSampler == SpvOpTypeSampler )
767       {
768         return true;
769       }
770     }
771     return false;
772   }
773
774   /**
775    * Recognizes descriptor type VkDescriptorTypeSampledImage
776    * GLSL:
777    *      layout (set=m, binding=n) uniform texture2D mySampledImage;
778    * SPIR_V:
779           %6 = OpTypeFloat 32
780           %7 = OpTypeImage %6 2D 0 0 0 1 Unknown
781           %8 = OpTypePointer UniformConstant %7
782           %9 = OpVariable %8 UniformConstant
783    *
784    * @param resource
785    * @return
786    */
787   bool TestSampledImageDescriptor( SPIRVOpCode& opcode)
788   {
789     auto opPointer = &FindByResultId( opcode.GetParameterU32(0) );
790     if( (opPointer && *opPointer == SpvOpTypePointer) )
791     {
792       auto& opTypeImage = GetReferencedOpCode( *opPointer, 2 );
793       if( opTypeImage == SpvOpTypeImage && opTypeImage.GetParameterU32( 6 ) == 1 )
794       {
795         return true;
796       }
797     }
798     return false;
799   }
800
801   /**
802    * Recognizes descriptor type VkDescriptorTypeCombinedImageSampler
803    * GLSL:
804    *      layout (set=m, binding=n) uniform sampler2D myCombinedImageSampler;
805    * SPIR-V:
806           %7 = OpTypeImage %6 2D 0 0 0 1 Unknown
807           %8 = OpTypeSampledImage %7
808           %9 = OpTypePointer UniformConstant %8
809          %10 = OpVariable %9 UniformConstant
810    * @param resource
811    * @return
812    */
813   bool TestCombinedImageSamplerDescriptor( SPIRVOpCode& opcode )
814   {
815     auto opPointer = &FindByResultId( opcode.GetParameterU32(0) );
816     if( (opPointer && *opPointer == SpvOpTypePointer) )
817     {
818       auto& opCode = GetReferencedOpCode( *opPointer, 2 );
819       if( opCode == SpvOpTypeSampledImage )
820       {
821         return true;
822       }
823     }
824     return false;
825   }
826
827   /**
828    * Recognizes descriptor type VkDescriptorTypeUniformTexelBuffer
829    * GLSL:
830    *      layout (set=m, binding=n) uniform samplerBuffer myUniformTexelBuffer;
831    * SPIR-V:
832             %6 = OpTypeFloat 32
833             %7 = OpTypeImage %6 Buffer 0 0 0 1 Unknown
834             %8 = OpTypePointer UniformConstant %7
835             %9 = OpVariable %8 UniformConstant
836    * @param resource
837    * @return
838    */
839   bool TestUniformTexelBufferDescriptor( SPIRVOpCode& opcode  )
840   {
841     auto opPointer = &FindByResultId( opcode.GetParameterU32(0) );
842     if( (opPointer && *opPointer == SpvOpTypePointer) )
843     {
844       auto& opCode = GetReferencedOpCode( *opPointer, 2 );
845       if( opCode == SpvOpTypeImage && opCode.GetParameter<SpvDim>( 2 ) == SpvDimBuffer &&
846           opCode.GetParameterU32( 6 ) == 1 )
847       {
848         return true;
849       }
850     }
851     return false;
852   }
853
854   /**
855    * Recognizes descriptor type VkDescriptorTypeStorageTexelBuffer
856    * GLSL:
857    *      layout (set=m, binding=n, r32f) uniform imageBuffer myStorageTexelBuffer;
858    * SPIR-V:
859           %7 = OpTypeImage %6 Buffer 0 0 0 2 R32f
860           %8 = OpTypePointer UniformConstant %7
861           %9 = OpVariable %8 UniformConstant
862    * @param resource
863    * @return
864    */
865   bool TestStorageTexelBufferDescriptor( SPIRVOpCode& opcode )
866   {
867     auto opPointer = &FindByResultId( opcode.GetParameterU32(0) );
868     if( (opPointer && *opPointer == SpvOpTypePointer) )
869     {
870       auto& opCode = GetReferencedOpCode( *opPointer, 2 );
871       if( opCode == SpvOpTypeImage && opCode.GetParameter<SpvDim>( 2 ) == SpvDimBuffer &&
872           opCode.GetParameterU32( 6 ) == 2 )
873       {
874         return true;
875       }
876     }
877     return false;
878   }
879
880   /**
881    * Recognizes descriptor type VkDescriptorTypeUniformBuffer
882    * GLSL:
883           layout (set=m, binding=n) uniform myUniformBuffer
884           {
885               vec4 myElement[32];
886           };
887    * SPIR-V:
888          %11 = OpTypeStruct %10
889          %12 = OpTypePointer Uniform %11
890          %13 = OpVariable %12 Uniform
891    * @todo pull data out of OpDecorate ( Block )
892    * @param resource
893    * @return
894    */
895   bool TestUniformBufferDescriptor( SPIRVOpCode& opcode )
896   {
897     auto opPointer = &FindByResultId( opcode.GetParameterU32(0) );
898     if( (opPointer && *opPointer == SpvOpTypePointer) && opPointer->GetParameter<SpvStorageClass>( 1 ) == SpvStorageClassUniform )
899     {
900       auto& opTypeStruct = GetReferencedOpCode( *opPointer, 2 );
901       if( opTypeStruct == SpvOpTypeStruct )
902       {
903         return CheckDecorationForOpId( opTypeStruct, SpvDecorationBlock );
904       }
905     }
906     return false;
907   }
908
909   bool TestStorageBufferDescriptor( SPIRVOpCode& opcode )
910   {
911     auto opPointer = FindByResultId( opcode );
912     if( (opPointer && *opPointer == SpvOpTypePointer) && opPointer->GetParameter<SpvStorageClass>( 1 ) == SpvStorageClassUniform )
913     {
914       auto& opTypeStruct = GetReferencedOpCode( *opPointer, 2 );
915       if( opTypeStruct == SpvOpTypeStruct )
916       {
917         return CheckDecorationForOpId( opTypeStruct, SpvDecorationBufferBlock );
918       }
919     }
920     return false;
921   }
922
923   std::vector<vk::DescriptorSetLayoutCreateInfo> GenerateDescriptorSetLayoutCreateInfo() const
924   {
925     auto retval = std::vector<vk::DescriptorSetLayoutCreateInfo>{};
926     for( auto& layout : descriptorSetLayoutCreateInfoMap )
927     {
928       retval.emplace_back( layout.second.createInfo );
929     }
930
931     return retval;
932   }
933
934   bool GetVertexInputAttributes( std::vector<SPIRVVertexInputAttribute>& out, bool canOverlap = false )
935   {
936     for( auto&& i : reflectionData )
937     {
938       if( i.second.storage == SpvStorageClassInput )
939       {
940         auto attr =  SPIRVVertexInputAttribute{};
941         attr.name = i.second.name;
942         attr.location = MapContains( i.second.decorations, SpvDecorationLocation ) ?
943                         i.second.decorations[SpvDecorationLocation]->GetParameterU32(2) : 0u;
944         attr.format = GetTypeInfo(
945           GetReferencedOpCode( GetReferencedOpCode( *i.second.op, 0 ), 2)
946         ).vkFormat;
947         out.emplace_back( attr );
948       }
949     }
950
951     return true;
952   }
953
954   /**
955    * Tests if the header is valid for SPIR-V
956    * @return
957    */
958   bool CheckHeader()
959   {
960     header = *reinterpret_cast<Header*>( data.data() );
961     return MAGIC_NUMBER == header.magicNumber;
962   }
963
964 public:
965   std::vector<SPIRVOpCode>  opCodes;   // contains all opcodes
966   std::vector<SPIRVOpCode*> opResults; // links to the resulting opcode or nullptr if opcode doesn't return
967
968   std::unordered_map<uint32_t, SPIRVReflectionData> reflectionData;
969   std::unordered_map<uint32_t, DescriptorSetLayoutAndBindingInfo>      descriptorSetLayoutCreateInfoMap;
970
971   std::vector<SPIRVWord>    data;
972
973
974   std::vector<SPIRVUniformBlock>                                       uniformBlockReflection;
975   std::vector<SPIRVUniformOpaque>                                      uniformOpaqueReflection;
976   Header                                                               header;
977
978 };
979 #pragma GCC diagnostic pop
980
981 /**************************************************************************************
982  * SPIRVShader
983  */
984
985 SPIRVShader::SPIRVShader() = default;
986
987 SPIRVShader::~SPIRVShader() = default;
988
989 SPIRVShader::SPIRVShader( SPIRVShader&& shader ) noexcept = default;
990
991 SPIRVShader::SPIRVShader( Impl& impl )
992 {
993   mImpl.reset( &impl );
994 }
995
996 SPIRVShader::SPIRVShader( std::vector<SPIRVWord> code, vk::ShaderStageFlags stages )
997 {
998   mImpl = std::make_unique<Impl>( code, stages );
999 }
1000
1001 std::vector<vk::DescriptorSetLayoutCreateInfo> SPIRVShader::GenerateDescriptorSetLayoutCreateInfo() const
1002 {
1003   return mImpl->GenerateDescriptorSetLayoutCreateInfo();
1004 }
1005
1006 uint32_t SPIRVShader::GetOpCodeCount() const
1007 {
1008   return static_cast<uint32_t>( mImpl->opCodes.size() );
1009 }
1010
1011 const SPIRVOpCode* SPIRVShader::GetOpCodeAt( uint32_t index ) const
1012 {
1013   return &mImpl->opCodes[index];
1014 }
1015
1016 const SPIRVOpCode* SPIRVShader::GetOpCodeForResultId( uint32_t resultId ) const
1017 {
1018   return mImpl->opResults[resultId];
1019 }
1020
1021 SPIRVWord SPIRVShader::GetOpCodeParameterWord( const SPIRVOpCode& opCode, uint32_t index ) const
1022 {
1023   return GetOpCodeParameter<SPIRVWord>( opCode, index );
1024 }
1025
1026 SpvOp SPIRVShader::GetOpCodeType( SPIRVOpCode& opCode )
1027 {
1028   return SpvOpMax;
1029 }
1030
1031 const uint32_t* SPIRVShader::GetOpCodeParameterPtr( const SPIRVOpCode& opCode, uint32_t index ) const
1032 {
1033   return ( opCode.localData.start + index + 1 );
1034 }
1035
1036 void SPIRVShader::GetVertexInputAttributes( std::vector<SPIRVVertexInputAttribute>& out ) const
1037 {
1038   mImpl->GetVertexInputAttributes( out );
1039 }
1040
1041 const std::vector<SPIRVUniformBlock>& SPIRVShader::GetUniformBlocks() const
1042 {
1043   return mImpl->uniformBlockReflection;
1044 }
1045
1046 const std::vector<SPIRVUniformOpaque>& SPIRVShader::GetOpaqueUniforms() const
1047 {
1048   return mImpl->uniformOpaqueReflection;
1049 }
1050
1051 bool SPIRVShader::FindUniformMemberByName( const std::string& uniformName, SPIRVUniformBlockMember& out ) const
1052 {
1053   for( auto&& ubo : mImpl->uniformBlockReflection )
1054   {
1055     for( auto&& member : ubo.members )
1056     {
1057       if( member.name == uniformName )
1058       {
1059         out = member;
1060         return true;
1061       }
1062     }
1063   }
1064   return false;
1065 }
1066
1067 /**************************************************************************************
1068  * SPIRVUtils
1069  */
1070
1071 /**
1072  * SPIRVUtils
1073  * @param data
1074  * @return
1075  */
1076 std::unique_ptr<SPIRVShader> SPIRVUtils::Parse( std::vector<SPIRVWord> data, vk::ShaderStageFlags stages )
1077 {
1078   auto shader = std::unique_ptr<SPIRVShader>( new SPIRVShader( data, stages ) );
1079   if( !shader->GetImplementation().Initialise() )
1080   {
1081     return nullptr;
1082   }
1083   return shader;
1084 }
1085
1086 std::unique_ptr<SPIRVShader> SPIRVUtils::Parse( const SPIRVWord* data, size_t sizeInBytes, vk::ShaderStageFlags stages )
1087 {
1088   std::vector<SPIRVWord> spirvCode{};
1089   auto                   wordSize = sizeInBytes / sizeof( SPIRVWord );
1090   spirvCode.resize( wordSize );
1091   std::copy( data, data + wordSize, spirvCode.begin() );
1092   return Parse( spirvCode, stages );
1093 }
1094
1095 } // namespace SpirV
1096
1097 } // namespace Vulkan
1098
1099 } // namespace Graphics
1100
1101 } // namespace Dali