Overte C++ Documentation
ShapePipeline.h
1 //
2 // ShapePipeline.h
3 // render/src/render
4 //
5 // Created by Zach Pomerantz on 12/31/15.
6 // Copyright 2015 High Fidelity, Inc.
7 // Copyright 2024 Overte e.V.
8 //
9 // Distributed under the Apache License, Version 2.0.
10 // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
11 //
12 
13 #ifndef hifi_render_ShapePipeline_h
14 #define hifi_render_ShapePipeline_h
15 
16 #include <unordered_set>
17 
18 #include <gpu/Batch.h>
19 #include <graphics/Material.h>
20 
21 #include "Args.h"
22 
23 namespace render {
24 class Item;
25 class ShapePlumber;
26 
27 class ShapeKey {
28 public:
29  enum FlagBit {
30  MATERIAL = 0,
31  TRANSLUCENT,
32  LIGHTMAP,
33  TANGENTS,
34  UNLIT,
35  DEFORMED,
36  DUAL_QUAT_SKINNED,
37  DEPTH_BIAS,
38  WIREFRAME,
39  FADE,
40  CULL_FACE_NONE, // if neither of these are set, we're CULL_FACE_BACK
41  CULL_FACE_FRONT,
42  MTOON,
43 
44  OWN_PIPELINE,
45  INVALID,
46 
47  CUSTOM_0,
48  CUSTOM_1,
49  CUSTOM_2,
50  CUSTOM_3,
51  CUSTOM_4,
52  CUSTOM_5,
53  CUSTOM_6,
54  CUSTOM_7,
55 
56  NUM_FLAGS, // Not a valid flag
57  NUM_NON_CUSTOM = INVALID,
58 
59  CUSTOM_MASK = (0xFF << CUSTOM_0),
60 
61  };
62  using Flags = std::bitset<NUM_FLAGS>;
63 
64  Flags _flags;
65 
66  ShapeKey() : _flags{ 0 } {}
67  ShapeKey(const Flags& flags) : _flags{flags} {}
68 
69  friend ShapeKey operator&(const ShapeKey& _Left, const ShapeKey& _Right) { return ShapeKey(_Left._flags & _Right._flags); }
70  friend ShapeKey operator|(const ShapeKey& _Left, const ShapeKey& _Right) { return ShapeKey(_Left._flags | _Right._flags); }
71  friend ShapeKey operator^(const ShapeKey& _Left, const ShapeKey& _Right) { return ShapeKey(_Left._flags ^ _Right._flags); }
72 
73  class Builder {
74  public:
75  Builder() {}
76  Builder(ShapeKey key) : _flags{key._flags} {}
77 
78  ShapeKey build() const { return ShapeKey{_flags}; }
79 
80  Builder& withMaterial() { _flags.set(MATERIAL); return (*this); }
81  Builder& withTranslucent() { _flags.set(TRANSLUCENT); return (*this); }
82  Builder& withLightMap() { _flags.set(LIGHTMAP); return (*this); }
83  Builder& withTangents() { _flags.set(TANGENTS); return (*this); }
84  Builder& withUnlit() { _flags.set(UNLIT); return (*this); }
85  Builder& withDeformed() { _flags.set(DEFORMED); return (*this); }
86  Builder& withDualQuatSkinned() { _flags.set(DUAL_QUAT_SKINNED); return (*this); }
87  Builder& withDepthBias() { _flags.set(DEPTH_BIAS); return (*this); }
88  Builder& withWireframe() { _flags.set(WIREFRAME); return (*this); }
89  Builder& withFade() { _flags.set(FADE); return (*this); }
90  Builder& withMToon() { _flags.set(MTOON); return (*this); }
91 
92  Builder& withoutCullFace() { return withCullFaceMode(graphics::MaterialKey::CullFaceMode::CULL_NONE); }
93  Builder& withCullFaceMode(graphics::MaterialKey::CullFaceMode cullFaceMode) {
94  switch (cullFaceMode) {
95  case graphics::MaterialKey::CullFaceMode::CULL_NONE:
96  _flags.set(CULL_FACE_NONE);
97  _flags.reset(CULL_FACE_FRONT);
98  break;
99  case graphics::MaterialKey::CullFaceMode::CULL_FRONT:
100  _flags.reset(CULL_FACE_NONE);
101  _flags.set(CULL_FACE_FRONT);
102  break;
103  case graphics::MaterialKey::CullFaceMode::CULL_BACK:
104  _flags.reset(CULL_FACE_NONE);
105  _flags.reset(CULL_FACE_FRONT);
106  break;
107  default:
108  break;
109  }
110  return (*this);
111  }
112 
113  Builder& withOwnPipeline() { _flags.set(OWN_PIPELINE); return (*this); }
114  Builder& invalidate() { _flags.set(INVALID); return (*this); }
115 
116  Builder& withCustom(uint8_t custom) { _flags &= (~CUSTOM_MASK); _flags |= (custom << CUSTOM_0); return (*this); }
117 
118  static const ShapeKey ownPipeline() { return Builder().withOwnPipeline(); }
119  static const ShapeKey invalid() { return Builder().invalidate(); }
120 
121  protected:
122  friend class ShapeKey;
123  Flags _flags{0};
124  };
125  ShapeKey(const Builder& builder) : ShapeKey{builder._flags} {}
126 
127  class Filter {
128  public:
129  Filter(Flags flags, Flags mask) : _flags{flags}, _mask{mask} {}
130  Filter(const ShapeKey& key) : _flags{ key._flags } { _mask.set(); }
131 
132  // Build a standard filter (will always exclude OWN_PIPELINE, INVALID)
133  class Builder {
134  public:
135  Builder();
136 
137  Filter build() const { return Filter(_flags, _mask); }
138 
139  Builder& withMaterial() { _flags.set(MATERIAL); _mask.set(MATERIAL); return (*this); }
140  Builder& withoutMaterial() { _flags.reset(MATERIAL); _mask.set(MATERIAL); return (*this); }
141 
142  Builder& withTranslucent() { _flags.set(TRANSLUCENT); _mask.set(TRANSLUCENT); return (*this); }
143  Builder& withOpaque() { _flags.reset(TRANSLUCENT); _mask.set(TRANSLUCENT); return (*this); }
144 
145  Builder& withLightMap() { _flags.set(LIGHTMAP); _mask.set(LIGHTMAP); return (*this); }
146  Builder& withoutLightMap() { _flags.reset(LIGHTMAP); _mask.set(LIGHTMAP); return (*this); }
147 
148  Builder& withTangents() { _flags.set(TANGENTS); _mask.set(TANGENTS); return (*this); }
149  Builder& withoutTangents() { _flags.reset(TANGENTS); _mask.set(TANGENTS); return (*this); }
150 
151  Builder& withUnlit() { _flags.set(UNLIT); _mask.set(UNLIT); return (*this); }
152  Builder& withoutUnlit() { _flags.reset(UNLIT); _mask.set(UNLIT); return (*this); }
153 
154  Builder& withDeformed() { _flags.set(DEFORMED); _mask.set(DEFORMED); return (*this); }
155  Builder& withoutDeformed() { _flags.reset(DEFORMED); _mask.set(DEFORMED); return (*this); }
156 
157  Builder& withDualQuatSkinned() { _flags.set(DUAL_QUAT_SKINNED); _mask.set(DUAL_QUAT_SKINNED); return (*this); }
158  Builder& withoutDualQuatSkinned() { _flags.reset(DUAL_QUAT_SKINNED); _mask.set(DUAL_QUAT_SKINNED); return (*this); }
159 
160  Builder& withDepthBias() { _flags.set(DEPTH_BIAS); _mask.set(DEPTH_BIAS); return (*this); }
161  Builder& withoutDepthBias() { _flags.reset(DEPTH_BIAS); _mask.set(DEPTH_BIAS); return (*this); }
162 
163  Builder& withWireframe() { _flags.set(WIREFRAME); _mask.set(WIREFRAME); return (*this); }
164  Builder& withoutWireframe() { _flags.reset(WIREFRAME); _mask.set(WIREFRAME); return (*this); }
165 
166  Builder& withCullFaceMode(graphics::MaterialKey::CullFaceMode cullFaceMode) {
167  switch (cullFaceMode) {
168  case graphics::MaterialKey::CullFaceMode::CULL_NONE:
169  _flags.set(CULL_FACE_NONE);
170  _flags.reset(CULL_FACE_FRONT);
171  break;
172  case graphics::MaterialKey::CullFaceMode::CULL_FRONT:
173  _flags.reset(CULL_FACE_NONE);
174  _flags.set(CULL_FACE_FRONT);
175  break;
176  case graphics::MaterialKey::CullFaceMode::CULL_BACK:
177  _flags.reset(CULL_FACE_NONE);
178  _flags.reset(CULL_FACE_FRONT);
179  break;
180  default:
181  break;
182  }
183  _mask.set(CULL_FACE_NONE);
184  _mask.set(CULL_FACE_FRONT);
185  return (*this);
186  }
187 
188  Builder& withFade() { _flags.set(FADE); _mask.set(FADE); return (*this); }
189  Builder& withoutFade() { _flags.reset(FADE); _mask.set(FADE); return (*this); }
190 
191  Builder& withMToon() { _flags.set(MTOON); _mask.set(MTOON); return (*this); }
192  Builder& withoutMToon() { _flags.reset(MTOON); _mask.set(MTOON); return (*this); }
193 
194  Builder& withCustom(uint8_t custom) { _flags &= (~CUSTOM_MASK); _flags |= (custom << CUSTOM_0); _mask |= (CUSTOM_MASK); return (*this); }
195  Builder& withoutCustom() { _flags &= (~CUSTOM_MASK); _mask |= (CUSTOM_MASK); return (*this); }
196 
197  protected:
198  friend class Filter;
199  Flags _flags{0};
200  Flags _mask{0};
201  };
202  Filter(const Filter::Builder& builder) : Filter(builder._flags, builder._mask) {}
203  ShapeKey key() const { return ShapeKey(_flags); }
204  protected:
205  friend class ShapePlumber;
206  Flags _flags{0};
207  Flags _mask{0};
208  };
209 
210  bool useMaterial() const { return _flags[MATERIAL]; }
211  bool hasLightMap() const { return _flags[LIGHTMAP]; }
212  bool hasTangents() const { return _flags[TANGENTS]; }
213  bool isUnlit() const { return _flags[UNLIT]; }
214  bool isTranslucent() const { return _flags[TRANSLUCENT]; }
215  bool isDeformed() const { return _flags[DEFORMED]; }
216  bool isDualQuatSkinned() const { return _flags[DUAL_QUAT_SKINNED]; }
217  bool isDepthBiased() const { return _flags[DEPTH_BIAS]; }
218  bool isWireframe() const { return _flags[WIREFRAME]; }
219  bool isCullFace() const { return !_flags[CULL_FACE_NONE] && !_flags[CULL_FACE_FRONT]; }
220  bool isCullFaceNone() const { return _flags[CULL_FACE_NONE] && !_flags[CULL_FACE_FRONT]; }
221  bool isCullFaceFront() const { return !_flags[CULL_FACE_NONE] && _flags[CULL_FACE_FRONT]; }
222  bool isFaded() const { return _flags[FADE]; }
223  bool isMToon() const { return _flags[MTOON]; }
224 
225  bool hasOwnPipeline() const { return _flags[OWN_PIPELINE]; }
226  bool isValid() const { return !_flags[INVALID]; }
227 
228  uint8_t getCustom() const { return (_flags.to_ulong() & CUSTOM_MASK) >> CUSTOM_0; }
229  bool isCustom() const { return (_flags.to_ulong() & CUSTOM_MASK); }
230 
231  // Comparator for use in stl containers
232  class Hash {
233  public:
234  size_t operator() (const ShapeKey& key) const {
235  return std::hash<ShapeKey::Flags>()(key._flags);
236  }
237  };
238 
239  // Comparator for use in stl containers
240  class KeyEqual {
241  public:
242  bool operator()(const ShapeKey& lhs, const ShapeKey& rhs) const { return lhs._flags == rhs._flags; }
243  };
244 };
245 
246 inline QDebug operator<<(QDebug debug, const ShapeKey& key) {
247  if (key.isValid()) {
248  if (key.hasOwnPipeline()) {
249  debug << "[ShapeKey: OWN_PIPELINE]";
250  } else {
251  debug << "[ShapeKey:"
252  << "useMaterial:" << key.useMaterial()
253  << "hasLightmap:" << key.hasLightMap()
254  << "hasTangents:" << key.hasTangents()
255  << "isUnlit:" << key.isUnlit()
256  << "isTranslucent:" << key.isTranslucent()
257  << "isDeformed:" << key.isDeformed()
258  << "isDualQuatSkinned:" << key.isDualQuatSkinned()
259  << "isDepthBiased:" << key.isDepthBiased()
260  << "isWireframe:" << key.isWireframe()
261  << "isCullFace:" << key.isCullFace()
262  << "isFaded:" << key.isFaded()
263  << "isMToon:" << key.isMToon()
264  << "]";
265  }
266  } else {
267  debug << "[ShapeKey: INVALID]";
268  }
269  return debug;
270 }
271 
272 // Rendering abstraction over gpu::Pipeline and map locations
273 // Meta-information (pipeline and locations) to render a shape
274 class ShapePipeline {
275 public:
276  class Locations {
277  public:
278  bool albedoTextureUnit{ false };
279  bool normalTextureUnit{ false };
280  bool roughnessTextureUnit{ false };
281  bool metallicTextureUnit{ false };
282  bool emissiveTextureUnit{ false };
283  bool occlusionTextureUnit{ false };
284  bool lightingModelBufferUnit{ false };
285  bool skinClusterBufferUnit{ false };
286  bool materialBufferUnit{ false };
287  bool keyLightBufferUnit{ false };
288  bool lightBufferUnit{ false };
289  bool lightAmbientBufferUnit{ false };
290  bool lightAmbientMapUnit{ false };
291  bool fadeMaskTextureUnit{ false };
292  bool fadeParameterBufferUnit{ false };
293  bool fadeObjectParameterBufferUnit{ false };
294  bool hazeParameterBufferUnit{ false };
295  bool lightClusterGridBufferUnit{ false };
296  bool lightClusterContentBufferUnit{ false };
297  bool lightClusterFrustumBufferUnit{ false };
298  };
299  using LocationsPointer = std::shared_ptr<Locations>;
300 
301  using BatchSetter = std::function<void(const ShapePipeline&, gpu::Batch&, render::Args*)>;
302 
303  using ItemSetter = std::function<void(const ShapePipeline&, render::Args*, const render::Item&)>;
304 
305  ShapePipeline(const gpu::PipelinePointer& pipeline, const LocationsPointer& locations, const BatchSetter& batchSetter = nullptr, const ItemSetter& itemSetter = nullptr) :
306  pipeline(pipeline),
307  locations(locations),
308  _batchSetter(batchSetter),
309  _itemSetter(itemSetter) {}
310 
311  // Normally, a pipeline is accessed through pickPipeline. If it needs to be set manually,
312  // after calling setPipeline this method should be called to prepare the pipeline with default buffers.
313  void prepare(gpu::Batch& batch, Args* args);
314 
315  gpu::PipelinePointer pipeline;
316  std::shared_ptr<Locations> locations;
317 
318  void prepareShapeItem(Args* args, const ShapeKey& key, const Item& shape);
319 
320 protected:
321  friend class ShapePlumber;
322 
323  BatchSetter _batchSetter;
324  ItemSetter _itemSetter;
325 public:
326  using CustomKey = uint8_t;
327  using CustomFactory = std::function<std::shared_ptr<ShapePipeline> (const ShapePlumber& plumber, const ShapeKey& key, RenderArgs* args)>;
328  using CustomFactoryMap = std::map<CustomKey, CustomFactory>;
329 
330  static CustomFactoryMap _globalCustomFactoryMap;
331 
332  static CustomKey registerCustomShapePipelineFactory(CustomFactory factory);
333 
334 };
335 using ShapePipelinePointer = std::shared_ptr<ShapePipeline>;
336 
337 class ShapePlumber {
338 public:
339  using Key = ShapeKey;
340  using Filter = Key::Filter;
341  using Pipeline = ShapePipeline;
342  using PipelinePointer = ShapePipelinePointer;
343  using PipelineMap = std::unordered_map<ShapeKey, PipelinePointer, ShapeKey::Hash, ShapeKey::KeyEqual>;
344  using Slot = int32_t;
345  using Locations = Pipeline::Locations;
346  using LocationsPointer = Pipeline::LocationsPointer;
347  using BatchSetter = Pipeline::BatchSetter;
348  using ItemSetter = Pipeline::ItemSetter;
349 
350  void addPipeline(const Key& key, const gpu::ShaderPointer& program, const gpu::StatePointer& state,
351  BatchSetter batchSetter = nullptr, ItemSetter itemSetter = nullptr);
352  void addPipeline(const Filter& filter, const gpu::ShaderPointer& program, const gpu::StatePointer& state,
353  BatchSetter batchSetter = nullptr, ItemSetter itemSetter = nullptr);
354 
355  const PipelinePointer pickPipeline(RenderArgs* args, const Key& key) const;
356 
357 protected:
358  void addPipelineHelper(const Filter& filter, Key key, int bit, const PipelinePointer& pipeline) const;
359  mutable PipelineMap _pipelineMap;
360 
361 private:
362  mutable std::unordered_set<Key, Key::Hash, Key::KeyEqual> _missingKeys;
363 };
364 
365 
366 using ShapePlumberPointer = std::shared_ptr<ShapePlumber>;
367 
368 }
369 
370 #endif // hifi_render_ShapePipeline_h