#pragma region CPL License /* Nuclex Native Framework Copyright (C) 2002-2013 Nuclex Development Labs This library is free software; you can redistribute it and/or modify it under the terms of the IBM Common Public License as published by the IBM Corporation; either version 1.0 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the IBM Common Public License for more details. You should have received a copy of the IBM Common Public License along with this library */ #pragma endregion // CPL License #ifndef NUCLEX_GRAPHICS_RASTERIZATION_DIRECT3D11INPUTLAYOUTMANAGER_H #define NUCLEX_GRAPHICS_RASTERIZATION_DIRECT3D11INPUTLAYOUTMANAGER_H #include "Nuclex/Graphics/Config.h" #include "Nuclex/Graphics/Rasterization/RasterizerSettings.h" #include "../../Helpers/LruMap.h" #include "Direct3D11Api.h" #include #include namespace Nuclex { namespace Graphics { // ------------------------------------------------------------------------------------------- // struct VertexElement; // ------------------------------------------------------------------------------------------- // }} // namespace Nuclex::Graphics namespace Nuclex { namespace Graphics { namespace Rasterization { // ------------------------------------------------------------------------------------------- // class GenericVertexBuffer; class Shader; // ------------------------------------------------------------------------------------------- // }}} // namespace Nuclex::Graphics::Rasterization namespace Nuclex { namespace Graphics { namespace Rasterization { // ------------------------------------------------------------------------------------------- // /// Creates and manages vertex input layout specifications /// /// /// One input layout must be created for each unique combination of shader input /// signature and vertex element composition (= vertex elements in each vertex buffer /// slot). Applications use only a limited number of these combinations (as the same /// shader normally gets applied to the same vertex buffers over and over each frame), /// so an LRU cache is a good match to store input layouts. /// /// /// Such a cache could aggressively try to avoid redundant input layouts (for example, /// if a shader is used with multiple vertex buffers sharing the same vertex layout), /// but this would add a lot of complexity to the implementation, so currently this /// manager will simply use two LRU caches, one for looking up the input layout of /// vertex buffer combinations and one two match these to input layouts (multiple /// shaders with identical input layouts are not considered). /// /// class Direct3D11InputLayoutManager { /// Number of unique vertex element list combinations that can be cached /// /// /// This is the number of unique combinations of vertex buffer types that can be /// cached. If an application, for example, used one vertex buffer type for rendering /// its GUI and two vertex buffers to render a terrain, this would be two unique /// combinations. Such combinations typically recur from frame to frame and even /// complex scenes only have a dozen or so unique combinations. /// /// /// If an application kept reusing some combinations but cycled through a large list /// of other combinations that defeats this LRU cache, we would (after the std::size_t /// exploded) eventually have an id collision, causing broken graphics or even /// a crash. That's why this cache is sized so ridiculously huge. /// /// private: const static std::size_t VertexCombinationCacheSize = 1024; /// Maximum number of input layouts active at the same time private: const static std::size_t InputLayoutCacheSize = 64; #pragma region struct VertexElementListSet /// A set of vertex element lists private: struct VertexElementListSet { #pragma region struct Hash /// Hash functor for storing the set in a hashed container public: struct Hash { /// Provides the hash value of the specified vertex element list set /// Set for which the hash value will be returned /// The hash value for the specified vertex element list set public: std::size_t operator()(const VertexElementListSet &listSet) const { return listSet.hash; } }; #pragma endregion // struct Hash /// Adds a vertex element list to the set /// List that will be added to the set /// /// This method mutates the list set and must therefore not be used after /// the list set has been added to a map. It also assumes that vertex element /// lists and immutable (thus it is satisfied with a pointer to each list's /// first element and does no checking of individual list elements). /// public: void AddVertexElementList(const VertexElement *elements) { this->vertexElementLists.push_back(elements); this->hash ^= reinterpret_cast(elements) << this->vertexElementLists.size(); } /// Removes all vertex element lists from the list set public: void Clear() { this->vertexElementLists.clear(); this->hash = 0; } /// Checks if this list set is identical to another instance /// Other instance the list set will be compared against /// True if the list sets are identical public: bool operator ==(const VertexElementListSet &other) const { std::size_t ownSize = this->vertexElementLists.size(); if(other.vertexElementLists.size() != ownSize) { return false; } for(std::size_t index = 0; index < ownSize; ++index) { if(this->vertexElementLists[index] != other.vertexElementLists[index]) { return false; } } return true; } /// Checks if this list set is different from another instance /// Other instance the list set will be compared against /// True if the list sets are different public: bool operator !=(const VertexElementListSet &other) const { std::size_t ownSize = this->vertexElementLists.size(); if(other.vertexElementLists.size() != ownSize) { return true; } for(std::size_t index = 0; index < ownSize; ++index) { if(this->vertexElementLists[index] != other.vertexElementLists[index]) { return true; } } return false; } /// Vertex element lists stored in the list set private: std::vector vertexElementLists; /// Hash code of this instance private: std::size_t hash; }; #pragma endregion // struct VertexElementListSet #pragma region struct ShaderVertexCombinationIdMapping /// Shader to vertex buffer combination mapping private: struct ShaderVertexCombinationIdMapping { #pragma region struct Hash /// Hash functor for storing the set in a hashed container public: struct Hash { /// Calculates the hash value of the specified mapping /// Mapping for which a hash value will be generated /// The hash value for the specified list set public: std::size_t operator()(const ShaderVertexCombinationIdMapping &mapping) const { return reinterpret_cast(mapping.shader) ^ mapping.combinationId; } }; #pragma endregion // struct Hash /// Initializes a shader to /// Shader that the combination will be for /// Unique id of the vertex element list combination public: ShaderVertexCombinationIdMapping( const Shader *shader, std::size_t combinationId ) : shader(shader), combinationId(combinationId) {} /// Checks if this list set is identical to another instance /// Other instance the list set will be compared against /// True if the list sets are identical public: bool operator ==(const ShaderVertexCombinationIdMapping &other) const { return (this->shader == other.shader) && (this->combinationId == other.combinationId); } /// Checks if this list set is different from another instance /// Other instance the list set will be compared against /// True if the list sets are different public: bool operator !=(const ShaderVertexCombinationIdMapping &other) const { return (this->shader != other.shader) || (this->combinationId != other.combinationId); } /// Shader the combination was created for /// /// The pointer has to be considered opaque since the actual memory contents it points /// to could be long gone by the time the combination checks up on it. /// private: const Shader *shader; /// /// Unique id of the vertex element list combination the shader is mapped to /// private: std::size_t combinationId; }; #pragma endregion // struct ShaderVertexCombinationIdMapping /// Initializes a new Direct3D 11 input layout manager public: Direct3D11InputLayoutManager() {} /// Frees all resources owned by the input layout manager public: virtual ~Direct3D11InputLayoutManager() {} /// Sets the Direct3D device for which settings will be managed /// Direct3D 11 device settings will be managed for public: void SetDirect3DDevice(const ID3D11DeviceNPtr &device) { this->device = device; } /// /// Retrieves the input layout for the specified vertex buffer combination /// /// Shader the input layout will be for /// /// Vertex buffers for which the input layout will be looked up or created /// /// Number of vertex buffers in the first argument /// The input layout for the specified vertex buffers public: ID3D11InputLayoutPtr GetInputLayout( const Shader *shader, const std::shared_ptr *vertexBuffers, std::size_t vertexBufferCount ); /// /// Creates a new input layout for the specified shader and vertex combination /// /// Shader the input layout will be for /// /// Vertex buffers for which the input layout will be looked up or created /// /// Number of vertex buffers in the first argument /// A new input layout for the specified vertex and shader combination private: ID3D11InputLayoutPtr createInputLayout( const Shader *shader, const std::shared_ptr *vertexBuffers, std::size_t vertexBufferCount ); /// /// Looks up or creates a new unique id for a combination of vertex element lists /// /// Vertex buffers in the combination /// Number of vertex buffers in the combination /// The unique combination id of the specified combination private: std::size_t getOrCreateCombinationID( const std::shared_ptr *vertexBuffers, std::size_t vertexBufferCount ); /// Maps lists of vertex buffer element lists to unique identifiers private: typedef Helpers::LruMap< VertexElementListSet, // Key std::size_t, // Value VertexElementListSet::Hash // Hash functor > VertexCombinationMap; /// Maps sets of vertex elements to input layouts private: typedef Helpers::LruMap< ShaderVertexCombinationIdMapping, // Key ID3D11InputLayoutPtr, // Value ShaderVertexCombinationIdMapping::Hash // Hash functor > InputLayoutMap; /// Direct3D device the settings will be applied to private: ID3D11DeviceNPtr device; /// /// Next unique identifier that will be generated for a vertex element list combination /// private: std::size_t nextVertexCombinationID; /// Helper used when looking up a combination id /// /// This could be a local variable, but by making the combination modifiable and /// keeping it around, the element list vector will not have to be reallocated. /// private: VertexElementListSet searchedCombination; /// Helpers used to store the sizes of the vertex element lists private: std::vector elementListSizes; /// Ids for unique vertex element list combinations encountered private: VertexCombinationMap vertexCombinations; /// Input layouts created for the vertex combination to shader mappings private: InputLayoutMap inputLayouts; }; // ------------------------------------------------------------------------------------------- // }}} // namespace Nuclex::Graphics::Rasterization #endif // NUCLEX_GRAPHICS_RASTERIZATION_DIRECT3D11INPUTLAYOUTMANAGER_H