// // ShapePipeline.h // render/src/render // // Created by Zach Pomerantz on 12/31/15. // Copyright 2015 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // #ifndef hifi_render_ShapePipeline_h #define hifi_render_ShapePipeline_h #include #include #include "Args.h" namespace render { class Item; class ShapePlumber; class ShapeKey { public: enum FlagBit { MATERIAL = 0, TRANSLUCENT, LIGHTMAP, TANGENTS, SPECULAR, UNLIT, SKINNED, DEPTH_ONLY, DEPTH_BIAS, WIREFRAME, NO_CULL_FACE, FADE, OWN_PIPELINE, INVALID, CUSTOM_0, CUSTOM_1, CUSTOM_2, CUSTOM_3, CUSTOM_4, CUSTOM_5, CUSTOM_6, CUSTOM_7, NUM_FLAGS, // Not a valid flag CUSTOM_MASK = (0xFF << CUSTOM_0), }; using Flags = std::bitset; Flags _flags; ShapeKey() : _flags{ 0 } {} ShapeKey(const Flags& flags) : _flags{flags} {} friend ShapeKey operator&(const ShapeKey& _Left, const ShapeKey& _Right) { return ShapeKey(_Left._flags & _Right._flags); } friend ShapeKey operator|(const ShapeKey& _Left, const ShapeKey& _Right) { return ShapeKey(_Left._flags | _Right._flags); } friend ShapeKey operator^(const ShapeKey& _Left, const ShapeKey& _Right) { return ShapeKey(_Left._flags ^ _Right._flags); } class Builder { public: Builder() {} Builder(ShapeKey key) : _flags{key._flags} {} ShapeKey build() const { return ShapeKey{_flags}; } Builder& withMaterial() { _flags.set(MATERIAL); return (*this); } Builder& withTranslucent() { _flags.set(TRANSLUCENT); return (*this); } Builder& withLightmap() { _flags.set(LIGHTMAP); return (*this); } Builder& withTangents() { _flags.set(TANGENTS); return (*this); } Builder& withSpecular() { _flags.set(SPECULAR); return (*this); } Builder& withUnlit() { _flags.set(UNLIT); return (*this); } Builder& withSkinned() { _flags.set(SKINNED); return (*this); } Builder& withDepthOnly() { _flags.set(DEPTH_ONLY); return (*this); } Builder& withDepthBias() { _flags.set(DEPTH_BIAS); return (*this); } Builder& withWireframe() { _flags.set(WIREFRAME); return (*this); } Builder& withoutCullFace() { _flags.set(NO_CULL_FACE); return (*this); } Builder& withFade() { _flags.set(FADE); return (*this); } Builder& withOwnPipeline() { _flags.set(OWN_PIPELINE); return (*this); } Builder& invalidate() { _flags.set(INVALID); return (*this); } Builder& withCustom(uint8_t custom) { _flags &= (~CUSTOM_MASK); _flags |= (custom << CUSTOM_0); return (*this); } static const ShapeKey ownPipeline() { return Builder().withOwnPipeline(); } static const ShapeKey invalid() { return Builder().invalidate(); } protected: friend class ShapeKey; Flags _flags{0}; }; ShapeKey(const Builder& builder) : ShapeKey{builder._flags} {} class Filter { public: Filter(Flags flags, Flags mask) : _flags{flags}, _mask{mask} {} Filter(const ShapeKey& key) : _flags{ key._flags } { _mask.set(); } // Build a standard filter (will always exclude OWN_PIPELINE, INVALID) class Builder { public: Builder(); Filter build() const { return Filter(_flags, _mask); } Builder& withMaterial() { _flags.set(MATERIAL); _mask.set(MATERIAL); return (*this); } Builder& withoutMaterial() { _flags.reset(MATERIAL); _mask.set(MATERIAL); return (*this); } Builder& withTranslucent() { _flags.set(TRANSLUCENT); _mask.set(TRANSLUCENT); return (*this); } Builder& withOpaque() { _flags.reset(TRANSLUCENT); _mask.set(TRANSLUCENT); return (*this); } Builder& withLightmap() { _flags.set(LIGHTMAP); _mask.set(LIGHTMAP); return (*this); } Builder& withoutLightmap() { _flags.reset(LIGHTMAP); _mask.set(LIGHTMAP); return (*this); } Builder& withTangents() { _flags.set(TANGENTS); _mask.set(TANGENTS); return (*this); } Builder& withoutTangents() { _flags.reset(TANGENTS); _mask.set(TANGENTS); return (*this); } Builder& withSpecular() { _flags.set(SPECULAR); _mask.set(SPECULAR); return (*this); } Builder& withoutSpecular() { _flags.reset(SPECULAR); _mask.set(SPECULAR); return (*this); } Builder& withUnlit() { _flags.set(UNLIT); _mask.set(UNLIT); return (*this); } Builder& withoutUnlit() { _flags.reset(UNLIT); _mask.set(UNLIT); return (*this); } Builder& withSkinned() { _flags.set(SKINNED); _mask.set(SKINNED); return (*this); } Builder& withoutSkinned() { _flags.reset(SKINNED); _mask.set(SKINNED); return (*this); } Builder& withDepthOnly() { _flags.set(DEPTH_ONLY); _mask.set(DEPTH_ONLY); return (*this); } Builder& withoutDepthOnly() { _flags.reset(DEPTH_ONLY); _mask.set(DEPTH_ONLY); return (*this); } Builder& withDepthBias() { _flags.set(DEPTH_BIAS); _mask.set(DEPTH_BIAS); return (*this); } Builder& withoutDepthBias() { _flags.reset(DEPTH_BIAS); _mask.set(DEPTH_BIAS); return (*this); } Builder& withWireframe() { _flags.set(WIREFRAME); _mask.set(WIREFRAME); return (*this); } Builder& withoutWireframe() { _flags.reset(WIREFRAME); _mask.set(WIREFRAME); return (*this); } Builder& withCullFace() { _flags.reset(NO_CULL_FACE); _mask.set(NO_CULL_FACE); return (*this); } Builder& withoutCullFace() { _flags.set(NO_CULL_FACE); _mask.set(NO_CULL_FACE); return (*this); } Builder& withFade() { _flags.set(FADE); _mask.set(FADE); return (*this); } Builder& withoutFade() { _flags.reset(FADE); _mask.set(FADE); return (*this); } Builder& withCustom(uint8_t custom) { _flags &= (~CUSTOM_MASK); _flags |= (custom << CUSTOM_0); _mask |= (CUSTOM_MASK); return (*this); } Builder& withoutCustom() { _flags &= (~CUSTOM_MASK); _mask |= (CUSTOM_MASK); return (*this); } protected: friend class Filter; Flags _flags{0}; Flags _mask{0}; }; Filter(const Filter::Builder& builder) : Filter(builder._flags, builder._mask) {} ShapeKey key() const { return ShapeKey(_flags); } protected: friend class ShapePlumber; Flags _flags{0}; Flags _mask{0}; }; bool useMaterial() const { return _flags[MATERIAL]; } bool hasLightmap() const { return _flags[LIGHTMAP]; } bool hasTangents() const { return _flags[TANGENTS]; } bool hasSpecular() const { return _flags[SPECULAR]; } bool isUnlit() const { return _flags[UNLIT]; } bool isTranslucent() const { return _flags[TRANSLUCENT]; } bool isSkinned() const { return _flags[SKINNED]; } bool isDepthOnly() const { return _flags[DEPTH_ONLY]; } bool isDepthBiased() const { return _flags[DEPTH_BIAS]; } bool isWireframe() const { return _flags[WIREFRAME]; } bool isCullFace() const { return !_flags[NO_CULL_FACE]; } bool isFaded() const { return _flags[FADE]; } bool hasOwnPipeline() const { return _flags[OWN_PIPELINE]; } bool isValid() const { return !_flags[INVALID]; } uint8_t getCustom() const { return (_flags.to_ulong() & CUSTOM_MASK) >> CUSTOM_0; } bool isCustom() const { return (_flags.to_ulong() & CUSTOM_MASK); } // Comparator for use in stl containers class Hash { public: size_t operator() (const ShapeKey& key) const { return std::hash()(key._flags); } }; // Comparator for use in stl containers class KeyEqual { public: bool operator()(const ShapeKey& lhs, const ShapeKey& rhs) const { return lhs._flags == rhs._flags; } }; }; inline QDebug operator<<(QDebug debug, const ShapeKey& key) { if (key.isValid()) { if (key.hasOwnPipeline()) { debug << "[ShapeKey: OWN_PIPELINE]"; } else { debug << "[ShapeKey:" << "useMaterial:" << key.useMaterial() << "hasLightmap:" << key.hasLightmap() << "hasTangents:" << key.hasTangents() << "hasSpecular:" << key.hasSpecular() << "isUnlit:" << key.isUnlit() << "isTranslucent:" << key.isTranslucent() << "isSkinned:" << key.isSkinned() << "isDepthOnly:" << key.isDepthOnly() << "isDepthBiased:" << key.isDepthBiased() << "isWireframe:" << key.isWireframe() << "isCullFace:" << key.isCullFace() << "isFaded:" << key.isFaded() << "]"; } } else { debug << "[ShapeKey: INVALID]"; } return debug; } // Rendering abstraction over gpu::Pipeline and map locations // Meta-information (pipeline and locations) to render a shape class ShapePipeline { public: class Slot { public: enum BUFFER { SKINNING = 0, MATERIAL, TEXMAPARRAY, LIGHTING_MODEL, LIGHT, LIGHT_AMBIENT_BUFFER, HAZE_MODEL, FADE_PARAMETERS, }; enum MAP { ALBEDO = 0, NORMAL, METALLIC, EMISSIVE_LIGHTMAP, ROUGHNESS, OCCLUSION, SCATTERING, LIGHT_AMBIENT, FADE_MASK, }; }; class Locations { public: int albedoTextureUnit; int normalTextureUnit; int roughnessTextureUnit; int metallicTextureUnit; int emissiveTextureUnit; int occlusionTextureUnit; int lightingModelBufferUnit; int skinClusterBufferUnit; int materialBufferUnit; int texMapArrayBufferUnit; int lightBufferUnit; int lightAmbientBufferUnit; int lightAmbientMapUnit; int fadeMaskTextureUnit; int fadeParameterBufferUnit; int hazeParameterBufferUnit; }; using LocationsPointer = std::shared_ptr; using BatchSetter = std::function; using ItemSetter = std::function; ShapePipeline(gpu::PipelinePointer pipeline, LocationsPointer locations, BatchSetter batchSetter = nullptr, ItemSetter itemSetter = nullptr) : pipeline(pipeline), locations(locations), _batchSetter(batchSetter), _itemSetter(itemSetter) {} // Normally, a pipeline is accessed through pickPipeline. If it needs to be set manually, // after calling setPipeline this method should be called to prepare the pipeline with default buffers. void prepare(gpu::Batch& batch, Args* args); gpu::PipelinePointer pipeline; std::shared_ptr locations; void prepareShapeItem(Args* args, const ShapeKey& key, const Item& shape); protected: friend class ShapePlumber; BatchSetter _batchSetter; ItemSetter _itemSetter; public: using CustomKey = uint8_t; using CustomFactory = std::function (const ShapePlumber& plumber, const ShapeKey& key, gpu::Batch& batch)>; using CustomFactoryMap = std::map; static CustomFactoryMap _globalCustomFactoryMap; static CustomKey registerCustomShapePipelineFactory(CustomFactory factory); }; using ShapePipelinePointer = std::shared_ptr; class ShapePlumber { public: using Key = ShapeKey; using Filter = Key::Filter; using Pipeline = ShapePipeline; using PipelinePointer = ShapePipelinePointer; using PipelineMap = std::unordered_map; using Slot = Pipeline::Slot; using Locations = Pipeline::Locations; using LocationsPointer = Pipeline::LocationsPointer; using BatchSetter = Pipeline::BatchSetter; using ItemSetter = Pipeline::ItemSetter; void addPipeline(const Key& key, const gpu::ShaderPointer& program, const gpu::StatePointer& state, BatchSetter batchSetter = nullptr, ItemSetter itemSetter = nullptr); void addPipeline(const Filter& filter, const gpu::ShaderPointer& program, const gpu::StatePointer& state, BatchSetter batchSetter = nullptr, ItemSetter itemSetter = nullptr); const PipelinePointer pickPipeline(RenderArgs* args, const Key& key) const; protected: void addPipelineHelper(const Filter& filter, Key key, int bit, const PipelinePointer& pipeline) const; mutable PipelineMap _pipelineMap; private: mutable std::unordered_set _missingKeys; }; using ShapePlumberPointer = std::shared_ptr; } #endif // hifi_render_ShapePipeline_h