fixed_pipeline_state: Hash and compare the whole structure
Pad FixedPipelineState's size to 384 bytes to be a multiple of 16. Compare the whole struct with std::memcmp and hash with CityHash. Using CityHash instead of a naive hash should reduce the number of collisions. Improve used type traits to ensure this operation is safe. With these changes the improvements to the hashable pipeline state are: Optimized structure Hash: 89 ns Comparison: 103 ns Construction*: 164 ns Struct size: 384 bytes Original structure Hash: 148 ns Equal: 174 ns Construction*: 281 ns Size: 1384 bytes * Attribute state initialization is not measured These measures are averages taken with std::chrono::high_accuracy_clock on MSVC shipped on Visual Studio 16.6.0 Preview 2.1.
This commit is contained in:
		| @@ -140,66 +140,13 @@ void FixedPipelineState::BlendingAttachment::Fill(const Maxwell& regs, std::size | |||||||
|     enable.Assign(1); |     enable.Assign(1); | ||||||
| } | } | ||||||
|  |  | ||||||
| std::size_t FixedPipelineState::BlendingAttachment::Hash() const noexcept { | std::size_t FixedPipelineState::Hash() const noexcept { | ||||||
|     return raw; |     const u64 hash = Common::CityHash64(reinterpret_cast<const char*>(this), sizeof *this); | ||||||
| } |  | ||||||
|  |  | ||||||
| bool FixedPipelineState::BlendingAttachment::operator==(const BlendingAttachment& rhs) const |  | ||||||
|     noexcept { |  | ||||||
|     return raw == rhs.raw; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| std::size_t FixedPipelineState::VertexInput::Hash() const noexcept { |  | ||||||
|     // TODO(Rodrigo): Replace this |  | ||||||
|     return Common::CityHash64(reinterpret_cast<const char*>(this), sizeof *this); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| bool FixedPipelineState::VertexInput::operator==(const VertexInput& rhs) const noexcept { |  | ||||||
|     return std::memcmp(this, &rhs, sizeof *this) == 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| std::size_t FixedPipelineState::Rasterizer::Hash() const noexcept { |  | ||||||
|     u64 hash = static_cast<u64>(raw) << 32; |  | ||||||
|     std::memcpy(&hash, &point_size, sizeof(u32)); |  | ||||||
|     return static_cast<std::size_t>(hash); |     return static_cast<std::size_t>(hash); | ||||||
| } | } | ||||||
|  |  | ||||||
| bool FixedPipelineState::Rasterizer::operator==(const Rasterizer& rhs) const noexcept { |  | ||||||
|     return raw == rhs.raw && point_size == rhs.point_size; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| std::size_t FixedPipelineState::DepthStencil::Hash() const noexcept { |  | ||||||
|     return raw; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| bool FixedPipelineState::DepthStencil::operator==(const DepthStencil& rhs) const noexcept { |  | ||||||
|     return raw == rhs.raw; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| std::size_t FixedPipelineState::ColorBlending::Hash() const noexcept { |  | ||||||
|     std::size_t hash = 0; |  | ||||||
|     for (std::size_t rt = 0; rt < std::size(attachments); ++rt) { |  | ||||||
|         boost::hash_combine(hash, attachments[rt].Hash()); |  | ||||||
|     } |  | ||||||
|     return hash; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| bool FixedPipelineState::ColorBlending::operator==(const ColorBlending& rhs) const noexcept { |  | ||||||
|     return attachments == rhs.attachments; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| std::size_t FixedPipelineState::Hash() const noexcept { |  | ||||||
|     std::size_t hash = 0; |  | ||||||
|     boost::hash_combine(hash, vertex_input.Hash()); |  | ||||||
|     boost::hash_combine(hash, rasterizer.Hash()); |  | ||||||
|     boost::hash_combine(hash, depth_stencil.Hash()); |  | ||||||
|     boost::hash_combine(hash, color_blending.Hash()); |  | ||||||
|     return hash; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| bool FixedPipelineState::operator==(const FixedPipelineState& rhs) const noexcept { | bool FixedPipelineState::operator==(const FixedPipelineState& rhs) const noexcept { | ||||||
|     return std::tie(vertex_input, rasterizer, depth_stencil, color_blending) == |     return std::memcmp(this, &rhs, sizeof *this) == 0; | ||||||
|            std::tie(rhs.vertex_input, rhs.rasterizer, rhs.depth_stencil, rhs.color_blending); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| FixedPipelineState GetFixedPipelineState(const Maxwell& regs) { | FixedPipelineState GetFixedPipelineState(const Maxwell& regs) { | ||||||
| @@ -207,6 +154,7 @@ FixedPipelineState GetFixedPipelineState(const Maxwell& regs) { | |||||||
|     fixed_state.rasterizer.Fill(regs); |     fixed_state.rasterizer.Fill(regs); | ||||||
|     fixed_state.depth_stencil.Fill(regs); |     fixed_state.depth_stencil.Fill(regs); | ||||||
|     fixed_state.color_blending.Fill(regs); |     fixed_state.color_blending.Fill(regs); | ||||||
|  |     fixed_state.padding = {}; | ||||||
|     return fixed_state; |     return fixed_state; | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -17,13 +17,7 @@ namespace Vulkan { | |||||||
|  |  | ||||||
| using Maxwell = Tegra::Engines::Maxwell3D::Regs; | using Maxwell = Tegra::Engines::Maxwell3D::Regs; | ||||||
|  |  | ||||||
| // TODO(Rodrigo): Optimize this structure. | struct alignas(32) FixedPipelineState { | ||||||
|  |  | ||||||
| template <class T> |  | ||||||
| inline constexpr bool IsHashable = std::has_unique_object_representations_v<T>&& |  | ||||||
|     std::is_trivially_copyable_v<T>&& std::is_trivially_constructible_v<T>; |  | ||||||
|  |  | ||||||
| struct FixedPipelineState { |  | ||||||
|     static u32 PackComparisonOp(Maxwell::ComparisonOp op) noexcept; |     static u32 PackComparisonOp(Maxwell::ComparisonOp op) noexcept; | ||||||
|     static Maxwell::ComparisonOp UnpackComparisonOp(u32 packed) noexcept; |     static Maxwell::ComparisonOp UnpackComparisonOp(u32 packed) noexcept; | ||||||
|  |  | ||||||
| @@ -102,7 +96,6 @@ struct FixedPipelineState { | |||||||
|             return UnpackBlendFactor(factor_dest_a.Value()); |             return UnpackBlendFactor(factor_dest_a.Value()); | ||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
|     static_assert(IsHashable<BlendingAttachment>); |  | ||||||
|  |  | ||||||
|     struct VertexInput { |     struct VertexInput { | ||||||
|         union Binding { |         union Binding { | ||||||
| @@ -151,16 +144,7 @@ struct FixedPipelineState { | |||||||
|             attribute.type.Assign(static_cast<u32>(type)); |             attribute.type.Assign(static_cast<u32>(type)); | ||||||
|             attribute.size.Assign(static_cast<u32>(size)); |             attribute.size.Assign(static_cast<u32>(size)); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         std::size_t Hash() const noexcept; |  | ||||||
|  |  | ||||||
|         bool operator==(const VertexInput& rhs) const noexcept; |  | ||||||
|  |  | ||||||
|         bool operator!=(const VertexInput& rhs) const noexcept { |  | ||||||
|             return !operator==(rhs); |  | ||||||
|         } |  | ||||||
|     }; |     }; | ||||||
|     static_assert(IsHashable<VertexInput>); |  | ||||||
|  |  | ||||||
|     struct Rasterizer { |     struct Rasterizer { | ||||||
|         union { |         union { | ||||||
| @@ -187,14 +171,6 @@ struct FixedPipelineState { | |||||||
|  |  | ||||||
|         void Fill(const Maxwell& regs) noexcept; |         void Fill(const Maxwell& regs) noexcept; | ||||||
|  |  | ||||||
|         std::size_t Hash() const noexcept; |  | ||||||
|  |  | ||||||
|         bool operator==(const Rasterizer& rhs) const noexcept; |  | ||||||
|  |  | ||||||
|         bool operator!=(const Rasterizer& rhs) const noexcept { |  | ||||||
|             return !operator==(rhs); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         constexpr Maxwell::PrimitiveTopology Topology() const noexcept { |         constexpr Maxwell::PrimitiveTopology Topology() const noexcept { | ||||||
|             return static_cast<Maxwell::PrimitiveTopology>(topology.Value()); |             return static_cast<Maxwell::PrimitiveTopology>(topology.Value()); | ||||||
|         } |         } | ||||||
| @@ -207,7 +183,6 @@ struct FixedPipelineState { | |||||||
|             return UnpackFrontFace(front_face.Value()); |             return UnpackFrontFace(front_face.Value()); | ||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
|     static_assert(IsHashable<Rasterizer>); |  | ||||||
|  |  | ||||||
|     struct DepthStencil { |     struct DepthStencil { | ||||||
|         template <std::size_t Position> |         template <std::size_t Position> | ||||||
| @@ -247,39 +222,22 @@ struct FixedPipelineState { | |||||||
|  |  | ||||||
|         void Fill(const Maxwell& regs) noexcept; |         void Fill(const Maxwell& regs) noexcept; | ||||||
|  |  | ||||||
|         std::size_t Hash() const noexcept; |  | ||||||
|  |  | ||||||
|         bool operator==(const DepthStencil& rhs) const noexcept; |  | ||||||
|  |  | ||||||
|         bool operator!=(const DepthStencil& rhs) const noexcept { |  | ||||||
|             return !operator==(rhs); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         Maxwell::ComparisonOp DepthTestFunc() const noexcept { |         Maxwell::ComparisonOp DepthTestFunc() const noexcept { | ||||||
|             return UnpackComparisonOp(depth_test_func); |             return UnpackComparisonOp(depth_test_func); | ||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
|     static_assert(IsHashable<DepthStencil>); |  | ||||||
|  |  | ||||||
|     struct ColorBlending { |     struct ColorBlending { | ||||||
|         std::array<BlendingAttachment, Maxwell::NumRenderTargets> attachments; |         std::array<BlendingAttachment, Maxwell::NumRenderTargets> attachments; | ||||||
|  |  | ||||||
|         void Fill(const Maxwell& regs) noexcept; |         void Fill(const Maxwell& regs) noexcept; | ||||||
|  |  | ||||||
|         std::size_t Hash() const noexcept; |  | ||||||
|  |  | ||||||
|         bool operator==(const ColorBlending& rhs) const noexcept; |  | ||||||
|  |  | ||||||
|         bool operator!=(const ColorBlending& rhs) const noexcept { |  | ||||||
|             return !operator==(rhs); |  | ||||||
|         } |  | ||||||
|     }; |     }; | ||||||
|     static_assert(IsHashable<ColorBlending>); |  | ||||||
|  |  | ||||||
|     VertexInput vertex_input; |     VertexInput vertex_input; | ||||||
|     Rasterizer rasterizer; |     Rasterizer rasterizer; | ||||||
|     DepthStencil depth_stencil; |     DepthStencil depth_stencil; | ||||||
|     ColorBlending color_blending; |     ColorBlending color_blending; | ||||||
|  |     std::array<u8, 20> padding; | ||||||
|  |  | ||||||
|     std::size_t Hash() const noexcept; |     std::size_t Hash() const noexcept; | ||||||
|  |  | ||||||
| @@ -289,12 +247,10 @@ struct FixedPipelineState { | |||||||
|         return !operator==(rhs); |         return !operator==(rhs); | ||||||
|     } |     } | ||||||
| }; | }; | ||||||
| static_assert(std::is_trivially_copyable_v<FixedPipelineState::BlendingAttachment>); | static_assert(std::has_unique_object_representations_v<FixedPipelineState>); | ||||||
| static_assert(std::is_trivially_copyable_v<FixedPipelineState::VertexInput>); |  | ||||||
| static_assert(std::is_trivially_copyable_v<FixedPipelineState::Rasterizer>); |  | ||||||
| static_assert(std::is_trivially_copyable_v<FixedPipelineState::DepthStencil>); |  | ||||||
| static_assert(std::is_trivially_copyable_v<FixedPipelineState::ColorBlending>); |  | ||||||
| static_assert(std::is_trivially_copyable_v<FixedPipelineState>); | static_assert(std::is_trivially_copyable_v<FixedPipelineState>); | ||||||
|  | static_assert(std::is_trivially_constructible_v<FixedPipelineState>); | ||||||
|  | static_assert(sizeof(FixedPipelineState) % 32 == 0, "Size is not aligned"); | ||||||
|  |  | ||||||
| FixedPipelineState GetFixedPipelineState(const Maxwell& regs); | FixedPipelineState GetFixedPipelineState(const Maxwell& regs); | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user