1 // Copyright 2018 The SwiftShader Authors. All Rights Reserved.
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
7 // http://www.apache.org/licenses/LICENSE-2.0
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
15 #ifndef sw_SpirvShader_hpp
16 #define sw_SpirvShader_hpp
18 #include "ShaderCore.hpp"
19 #include "SpirvID.hpp"
20 #include "System/Types.hpp"
21 #include "Vulkan/VkDebug.hpp"
22 #include "Vulkan/VkConfig.h"
28 #include <unordered_map>
30 #include <type_traits>
32 #include <spirv/unified1/spirv.hpp>
33 #include <Device/Config.hpp>
42 // Forward declarations.
45 // SIMD contains types that represent multiple scalars packed into a single
46 // vector data type. Types in the SIMD namespace provide a semantic hint
47 // that the data should be treated as a per-execution-lane scalar instead of
48 // a typical euclidean-style vector type.
51 // Width is the number of per-lane scalars packed into each SIMD vector.
52 static constexpr int Width = 4;
54 using Float = rr::Float4;
56 using UInt = rr::UInt4;
59 // Incrementally constructed complex bundle of rvalues
60 // Effectively a restricted vector, supporting only:
61 // - allocation to a (runtime-known) fixed size
62 // - in-place construction of elements
67 using Scalar = RValue<SIMD::Float>;
69 Intermediate(uint32_t size) : contents(new ContentsType[size]), size(size) {
70 #if !defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)
71 memset(contents, 0, sizeof(ContentsType[size]));
77 for (auto i = 0u; i < size; i++)
78 reinterpret_cast<Scalar *>(&contents[i])->~Scalar();
82 void emplace(uint32_t n, Scalar&& value)
85 ASSERT(reinterpret_cast<Scalar const *>(&contents[n])->value == nullptr);
86 new (&contents[n]) Scalar(value);
89 void emplace(uint32_t n, const Scalar& value)
92 ASSERT(reinterpret_cast<Scalar const *>(&contents[n])->value == nullptr);
93 new (&contents[n]) Scalar(value);
96 Scalar const & operator[](uint32_t n) const
99 auto scalar = reinterpret_cast<Scalar const *>(&contents[n]);
100 ASSERT(scalar->value != nullptr);
104 // No copy/move construction or assignment
105 Intermediate(Intermediate const &) = delete;
106 Intermediate(Intermediate &&) = delete;
107 Intermediate & operator=(Intermediate const &) = delete;
108 Intermediate & operator=(Intermediate &&) = delete;
111 using ContentsType = std::aligned_storage<sizeof(Scalar), alignof(Scalar)>::type;
113 ContentsType *contents;
120 using InsnStore = std::vector<uint32_t>;
123 /* Pseudo-iterator over SPIRV instructions, designed to support range-based-for. */
126 InsnStore::const_iterator iter;
129 spv::Op opcode() const
131 return static_cast<spv::Op>(*iter & spv::OpCodeMask);
134 uint32_t wordCount() const
136 return *iter >> spv::WordCountShift;
139 uint32_t word(uint32_t n) const
141 ASSERT(n < wordCount());
145 uint32_t const * wordPointer(uint32_t n) const
147 ASSERT(n < wordCount());
151 bool operator!=(InsnIterator const &other) const
153 return iter != other.iter;
156 InsnIterator operator*() const
161 InsnIterator &operator++()
167 InsnIterator const operator++(int)
169 InsnIterator ret{*this};
174 InsnIterator(InsnIterator const &other) = default;
176 InsnIterator() = default;
178 explicit InsnIterator(InsnStore::const_iterator iter) : iter{iter}
183 /* range-based-for interface */
184 InsnIterator begin() const
186 return InsnIterator{insns.cbegin() + 5};
189 InsnIterator end() const
191 return InsnIterator{insns.cend()};
195 using TypeID = SpirvID<Type>;
200 InsnIterator definition;
201 spv::StorageClass storageClass = static_cast<spv::StorageClass>(-1);
202 uint32_t sizeInComponents = 0;
203 bool isBuiltInBlock = false;
205 // Inner element type for pointers, arrays, vectors and matrices.
210 using ObjectID = SpirvID<Object>;
215 InsnIterator definition;
217 ObjectID pointerBase;
218 std::unique_ptr<uint32_t[]> constantValue = nullptr;
222 Unknown, /* for paranoia -- if we get left with an object in this state, the module was broken */
227 } kind = Kind::Unknown;
230 struct TypeOrObject {}; // Dummy struct to represent a Type or Object.
232 // TypeOrObjectID is an identifier that represents a Type or an Object,
233 // and supports implicit casting to and from TypeID or ObjectID.
234 class TypeOrObjectID : public SpirvID<TypeOrObject>
237 using Hash = std::hash<SpirvID<TypeOrObject>>;
239 inline TypeOrObjectID(uint32_t id) : SpirvID(id) {}
240 inline TypeOrObjectID(TypeID id) : SpirvID(id.value()) {}
241 inline TypeOrObjectID(ObjectID id) : SpirvID(id.value()) {}
242 inline operator TypeID() const { return TypeID(value()); }
243 inline operator ObjectID() const { return ObjectID(value()); }
246 int getSerialID() const
251 explicit SpirvShader(InsnStore const &insns);
255 bool EarlyFragmentTests : 1;
256 bool DepthReplacing : 1;
257 bool DepthGreater : 1;
259 bool DepthUnchanged : 1;
260 bool ContainsKill : 1;
261 bool NeedsCentroid : 1;
263 // Compute workgroup dimensions
264 int LocalSizeX, LocalSizeY, LocalSizeZ;
267 Modes const &getModes() const
272 enum AttribType : unsigned char
279 ATTRIBTYPE_LAST = ATTRIBTYPE_UINT
282 bool hasBuiltinInput(spv::BuiltIn b) const
284 return inputBuiltins.find(b) != inputBuiltins.end();
291 int32_t DescriptorSet;
293 spv::BuiltIn BuiltIn;
294 bool HasLocation : 1;
295 bool HasComponent : 1;
296 bool HasDescriptorSet : 1;
301 bool NoPerspective : 1;
303 bool BufferBlock : 1;
306 : Location{-1}, Component{0}, DescriptorSet{-1}, Binding{-1},
307 BuiltIn{static_cast<spv::BuiltIn>(-1)},
308 HasLocation{false}, HasComponent{false},
309 HasDescriptorSet{false}, HasBinding{false},
310 HasBuiltIn{false}, Flat{false}, Centroid{false},
311 NoPerspective{false}, Block{false}, BufferBlock{false}
315 Decorations(Decorations const &) = default;
317 void Apply(Decorations const &src);
319 void Apply(spv::Decoration decoration, uint32_t arg);
322 std::unordered_map<TypeOrObjectID, Decorations, TypeOrObjectID::Hash> decorations;
323 std::unordered_map<TypeID, std::vector<Decorations>> memberDecorations;
325 struct InterfaceComponent
330 bool NoPerspective : 1;
333 : Type{ATTRIBTYPE_UNUSED}, Flat{false}, Centroid{false}, NoPerspective{false}
338 struct BuiltinMapping
341 uint32_t FirstComponent;
342 uint32_t SizeInComponents;
345 std::vector<InterfaceComponent> inputs;
346 std::vector<InterfaceComponent> outputs;
348 void emitProlog(SpirvRoutine *routine) const;
349 void emit(SpirvRoutine *routine) const;
350 void emitEpilog(SpirvRoutine *routine) const;
352 using BuiltInHash = std::hash<std::underlying_type<spv::BuiltIn>::type>;
353 std::unordered_map<spv::BuiltIn, BuiltinMapping, BuiltInHash> inputBuiltins;
354 std::unordered_map<spv::BuiltIn, BuiltinMapping, BuiltInHash> outputBuiltins;
356 Type const &getType(TypeID id) const
358 auto it = types.find(id);
359 ASSERT(it != types.end());
363 Object const &getObject(ObjectID id) const
365 auto it = defs.find(id);
366 ASSERT(it != defs.end());
372 static volatile int serialCounter;
374 HandleMap<Type> types;
375 HandleMap<Object> defs;
377 // DeclareType creates a Type for the given OpTypeX instruction, storing
378 // it into the types map. It is called from the analysis pass (constructor).
379 void DeclareType(InsnIterator insn);
381 void ProcessExecutionMode(InsnIterator it);
383 uint32_t ComputeTypeSize(InsnIterator insn);
384 void ApplyDecorationsForId(Decorations *d, TypeOrObjectID id) const;
385 void ApplyDecorationsForIdMember(Decorations *d, TypeID id, uint32_t member) const;
388 int VisitInterfaceInner(TypeID id, Decorations d, F f) const;
391 void VisitInterface(ObjectID id, F f) const;
393 uint32_t GetConstantInt(ObjectID id) const;
394 Object& CreateConstant(InsnIterator it);
396 void ProcessInterfaceVariable(Object &object);
398 SIMD::Int WalkAccessChain(ObjectID id, uint32_t numIndexes, uint32_t const *indexIds, SpirvRoutine *routine) const;
399 uint32_t WalkLiteralAccessChain(TypeID id, uint32_t numIndexes, uint32_t const *indexes) const;
401 // Emit pass instructions:
402 void EmitVariable(InsnIterator insn, SpirvRoutine *routine) const;
403 void EmitLoad(InsnIterator insn, SpirvRoutine *routine) const;
404 void EmitStore(InsnIterator insn, SpirvRoutine *routine) const;
405 void EmitAccessChain(InsnIterator insn, SpirvRoutine *routine) const;
406 void EmitCompositeConstruct(InsnIterator insn, SpirvRoutine *routine) const;
407 void EmitCompositeInsert(InsnIterator insn, SpirvRoutine *routine) const;
408 void EmitCompositeExtract(InsnIterator insn, SpirvRoutine *routine) const;
409 void EmitVectorShuffle(InsnIterator insn, SpirvRoutine *routine) const;
410 void EmitUnaryOp(InsnIterator insn, SpirvRoutine *routine) const;
411 void EmitBinaryOp(InsnIterator insn, SpirvRoutine *routine) const;
412 void EmitDot(InsnIterator insn, SpirvRoutine *routine) const;
414 // OpcodeName returns the name of the opcode op.
415 // If NDEBUG is defined, then OpcodeName will only return the numerical code.
416 static std::string OpcodeName(spv::Op op);
422 SpirvRoutine(vk::PipelineLayout const *pipelineLayout);
424 using Value = Array<SIMD::Float>;
426 vk::PipelineLayout const * const pipelineLayout;
428 std::unordered_map<SpirvShader::ObjectID, Value> lvalues;
430 std::unordered_map<SpirvShader::ObjectID, Intermediate> intermediates;
432 Value inputs = Value{MAX_INTERFACE_COMPONENTS};
433 Value outputs = Value{MAX_INTERFACE_COMPONENTS};
435 std::array<Pointer<Byte>, vk::MAX_BOUND_DESCRIPTOR_SETS> descriptorSets;
437 void createLvalue(SpirvShader::ObjectID id, uint32_t size)
439 lvalues.emplace(id, Value(size));
442 Intermediate& createIntermediate(SpirvShader::ObjectID id, uint32_t size)
444 auto it = intermediates.emplace(std::piecewise_construct,
445 std::forward_as_tuple(id),
446 std::forward_as_tuple(size));
447 return it.first->second;
450 Value& getValue(SpirvShader::ObjectID id)
452 auto it = lvalues.find(id);
453 ASSERT(it != lvalues.end());
457 Intermediate const& getIntermediate(SpirvShader::ObjectID id) const
459 auto it = intermediates.find(id);
460 ASSERT(it != intermediates.end());
467 // Generic wrapper over either per-lane intermediate value, or a constant.
468 // Constants are transparently widened to per-lane values in operator[].
469 // This is appropriate in most cases -- if we're not going to do something
470 // significantly different based on whether the value is uniform across lanes.
472 SpirvShader::Object const &obj;
473 Intermediate const *intermediate;
476 GenericValue(SpirvShader const *shader, SpirvRoutine const *routine, SpirvShader::ObjectID objId) :
477 obj(shader->getObject(objId)),
478 intermediate(obj.kind == SpirvShader::Object::Kind::Value ? &routine->getIntermediate(objId) : nullptr) {}
480 RValue<SIMD::Float> operator[](uint32_t i) const
483 return (*intermediate)[i];
485 auto constantValue = reinterpret_cast<float *>(obj.constantValue.get());
486 return RValue<SIMD::Float>(constantValue[i]);
492 #endif // sw_SpirvShader_hpp