diff options
author | ReinUsesLisp <reinuseslisp@airmail.cc> | 2020-04-18 09:03:29 +0200 |
---|---|---|
committer | ReinUsesLisp <reinuseslisp@airmail.cc> | 2020-04-19 00:22:57 +0200 |
commit | 548dd27f4567f751d54073f1408d6f8949344fa9 (patch) | |
tree | b77bdf3235fb45bac3e6fee37d5728054b7dd5ef /src/video_core | |
parent | fixed_pipeline_state: Pack depth stencil state (diff) | |
download | yuzu-548dd27f4567f751d54073f1408d6f8949344fa9.tar yuzu-548dd27f4567f751d54073f1408d6f8949344fa9.tar.gz yuzu-548dd27f4567f751d54073f1408d6f8949344fa9.tar.bz2 yuzu-548dd27f4567f751d54073f1408d6f8949344fa9.tar.lz yuzu-548dd27f4567f751d54073f1408d6f8949344fa9.tar.xz yuzu-548dd27f4567f751d54073f1408d6f8949344fa9.tar.zst yuzu-548dd27f4567f751d54073f1408d6f8949344fa9.zip |
Diffstat (limited to 'src/video_core')
4 files changed, 155 insertions, 163 deletions
diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp index 8734045e5..1a23de07f 100644 --- a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp +++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <cstring> #include <tuple> #include <boost/functional/hash.hpp> @@ -12,6 +13,31 @@ namespace Vulkan { +namespace { + +constexpr std::size_t POINT = 0; +constexpr std::size_t LINE = 1; +constexpr std::size_t POLYGON = 2; +constexpr std::array POLYGON_OFFSET_ENABLE_LUT = { + POINT, // Points + LINE, // Lines + LINE, // LineLoop + LINE, // LineStrip + POLYGON, // Triangles + POLYGON, // TriangleStrip + POLYGON, // TriangleFan + POLYGON, // Quads + POLYGON, // QuadStrip + POLYGON, // Polygon + LINE, // LinesAdjacency + LINE, // LineStripAdjacency + POLYGON, // TrianglesAdjacency + POLYGON, // TriangleStripAdjacency + POLYGON, // Patches +}; + +} // Anonymous namespace + void FixedPipelineState::DepthStencil::Fill(const Maxwell& regs) noexcept { raw = 0; front.action_stencil_fail.Assign(PackStencilOp(regs.stencil_front_op_fail)); @@ -36,14 +62,41 @@ void FixedPipelineState::DepthStencil::Fill(const Maxwell& regs) noexcept { depth_test_func.Assign(PackComparisonOp(regs.depth_test_func)); } -namespace { +void FixedPipelineState::Rasterizer::Fill(const Maxwell& regs) noexcept { + const auto& clip = regs.view_volume_clip_control; + const std::array enabled_lut = {regs.polygon_offset_point_enable, + regs.polygon_offset_line_enable, + regs.polygon_offset_fill_enable}; + const u32 topology_index = static_cast<u32>(regs.draw.topology.Value()); + + u32 packed_front_face = PackFrontFace(regs.front_face); + if (regs.screen_y_control.triangle_rast_flip != 0 && + regs.viewport_transform[0].scale_y > 0.0f) { + // Flip front face + packed_front_face = 1 - packed_front_face; + } -constexpr FixedPipelineState::InputAssembly GetInputAssemblyState(const Maxwell& regs) { - return FixedPipelineState::InputAssembly( - regs.draw.topology, regs.primitive_restart.enabled, - regs.draw.topology == Maxwell::PrimitiveTopology::Points ? regs.point_size : 0.0f); + raw = 0; + topology.Assign(topology_index); + primitive_restart_enable.Assign(regs.primitive_restart.enabled != 0 ? 1 : 0); + cull_enable.Assign(regs.cull_test_enabled != 0 ? 1 : 0); + depth_bias_enable.Assign(enabled_lut[POLYGON_OFFSET_ENABLE_LUT[topology_index]] != 0 ? 1 : 0); + depth_clamp_enable.Assign(clip.depth_clamp_near == 1 || clip.depth_clamp_far == 1 ? 1 : 0); + ndc_minus_one_to_one.Assign(regs.depth_mode == Maxwell::DepthMode::MinusOneToOne ? 1 : 0); + cull_face.Assign(PackCullFace(regs.cull_face)); + front_face.Assign(packed_front_face); + polygon_mode.Assign(PackPolygonMode(regs.polygon_mode_front)); + patch_control_points_minus_one.Assign(regs.patch_vertices - 1); + tessellation_primitive.Assign(static_cast<u32>(regs.tess_mode.prim.Value())); + tessellation_spacing.Assign(static_cast<u32>(regs.tess_mode.spacing.Value())); + tessellation_clockwise.Assign(regs.tess_mode.cw.Value()); + logic_op_enable.Assign(regs.logic_op.enable != 0 ? 1 : 0); + logic_op.Assign(PackLogicOp(regs.logic_op.operation)); + std::memcpy(&point_size, ®s.point_size, sizeof(point_size)); // TODO: C++20 std::bit_cast } +namespace { + constexpr FixedPipelineState::BlendingAttachment GetBlendingAttachmentState( const Maxwell& regs, std::size_t render_target) { const auto& mask = regs.color_mask[regs.color_mask_common ? 0 : render_target]; @@ -86,56 +139,6 @@ constexpr FixedPipelineState::ColorBlending GetColorBlendingState(const Maxwell& GetBlendingAttachmentState(regs, 6), GetBlendingAttachmentState(regs, 7)}); } -constexpr FixedPipelineState::Tessellation GetTessellationState(const Maxwell& regs) { - return FixedPipelineState::Tessellation(regs.patch_vertices, regs.tess_mode.prim, - regs.tess_mode.spacing, regs.tess_mode.cw != 0); -} - -constexpr std::size_t Point = 0; -constexpr std::size_t Line = 1; -constexpr std::size_t Polygon = 2; -constexpr std::array PolygonOffsetEnableLUT = { - Point, // Points - Line, // Lines - Line, // LineLoop - Line, // LineStrip - Polygon, // Triangles - Polygon, // TriangleStrip - Polygon, // TriangleFan - Polygon, // Quads - Polygon, // QuadStrip - Polygon, // Polygon - Line, // LinesAdjacency - Line, // LineStripAdjacency - Polygon, // TrianglesAdjacency - Polygon, // TriangleStripAdjacency - Polygon, // Patches -}; - -constexpr FixedPipelineState::Rasterizer GetRasterizerState(const Maxwell& regs) { - const std::array enabled_lut = {regs.polygon_offset_point_enable, - regs.polygon_offset_line_enable, - regs.polygon_offset_fill_enable}; - const auto topology = static_cast<std::size_t>(regs.draw.topology.Value()); - const bool depth_bias_enabled = enabled_lut[PolygonOffsetEnableLUT[topology]]; - - const auto& clip = regs.view_volume_clip_control; - const bool depth_clamp_enabled = clip.depth_clamp_near == 1 || clip.depth_clamp_far == 1; - - Maxwell::FrontFace front_face = regs.front_face; - if (regs.screen_y_control.triangle_rast_flip != 0 && - regs.viewport_transform[0].scale_y > 0.0f) { - if (front_face == Maxwell::FrontFace::CounterClockWise) - front_face = Maxwell::FrontFace::ClockWise; - else if (front_face == Maxwell::FrontFace::ClockWise) - front_face = Maxwell::FrontFace::CounterClockWise; - } - - const bool gl_ndc = regs.depth_mode == Maxwell::DepthMode::MinusOneToOne; - return FixedPipelineState::Rasterizer(regs.cull_test_enabled, depth_bias_enabled, - depth_clamp_enabled, gl_ndc, regs.cull_face, front_face); -} - } // Anonymous namespace std::size_t FixedPipelineState::BlendingAttachment::Hash() const noexcept { @@ -168,43 +171,14 @@ bool FixedPipelineState::VertexInput::operator==(const VertexInput& rhs) const n return std::memcmp(this, &rhs, sizeof *this) == 0; } -std::size_t FixedPipelineState::InputAssembly::Hash() const noexcept { - std::size_t point_size_int = 0; - std::memcpy(&point_size_int, &point_size, sizeof(point_size)); - return (static_cast<std::size_t>(topology) << 24) ^ (point_size_int << 32) ^ - static_cast<std::size_t>(primitive_restart_enable); -} - -bool FixedPipelineState::InputAssembly::operator==(const InputAssembly& rhs) const noexcept { - return std::tie(topology, primitive_restart_enable, point_size) == - std::tie(rhs.topology, rhs.primitive_restart_enable, rhs.point_size); -} - -std::size_t FixedPipelineState::Tessellation::Hash() const noexcept { - return static_cast<std::size_t>(patch_control_points) ^ - (static_cast<std::size_t>(primitive) << 6) ^ (static_cast<std::size_t>(spacing) << 8) ^ - (static_cast<std::size_t>(clockwise) << 10); -} - -bool FixedPipelineState::Tessellation::operator==(const Tessellation& rhs) const noexcept { - return std::tie(patch_control_points, primitive, spacing, clockwise) == - std::tie(rhs.patch_control_points, rhs.primitive, rhs.spacing, rhs.clockwise); -} - std::size_t FixedPipelineState::Rasterizer::Hash() const noexcept { - return static_cast<std::size_t>(cull_enable) ^ - (static_cast<std::size_t>(depth_bias_enable) << 1) ^ - (static_cast<std::size_t>(depth_clamp_enable) << 2) ^ - (static_cast<std::size_t>(ndc_minus_one_to_one) << 3) ^ - (static_cast<std::size_t>(cull_face) << 24) ^ - (static_cast<std::size_t>(front_face) << 48); + u64 hash = static_cast<u64>(raw) << 32; + std::memcpy(&hash, &point_size, sizeof(u32)); + return static_cast<std::size_t>(hash); } bool FixedPipelineState::Rasterizer::operator==(const Rasterizer& rhs) const noexcept { - return std::tie(cull_enable, depth_bias_enable, depth_clamp_enable, ndc_minus_one_to_one, - cull_face, front_face) == - std::tie(rhs.cull_enable, rhs.depth_bias_enable, rhs.depth_clamp_enable, - rhs.ndc_minus_one_to_one, rhs.cull_face, rhs.front_face); + return raw == rhs.raw && point_size == rhs.point_size; } std::size_t FixedPipelineState::DepthStencil::Hash() const noexcept { @@ -231,8 +205,6 @@ bool FixedPipelineState::ColorBlending::operator==(const ColorBlending& rhs) con std::size_t FixedPipelineState::Hash() const noexcept { std::size_t hash = 0; boost::hash_combine(hash, vertex_input.Hash()); - boost::hash_combine(hash, input_assembly.Hash()); - boost::hash_combine(hash, tessellation.Hash()); boost::hash_combine(hash, rasterizer.Hash()); boost::hash_combine(hash, depth_stencil.Hash()); boost::hash_combine(hash, color_blending.Hash()); @@ -240,17 +212,13 @@ std::size_t FixedPipelineState::Hash() const noexcept { } bool FixedPipelineState::operator==(const FixedPipelineState& rhs) const noexcept { - return std::tie(vertex_input, input_assembly, tessellation, rasterizer, depth_stencil, - color_blending) == std::tie(rhs.vertex_input, rhs.input_assembly, - rhs.tessellation, rhs.rasterizer, rhs.depth_stencil, - rhs.color_blending); + return std::tie(vertex_input, rasterizer, depth_stencil, color_blending) == + std::tie(rhs.vertex_input, rhs.rasterizer, rhs.depth_stencil, rhs.color_blending); } FixedPipelineState GetFixedPipelineState(const Maxwell& regs) { FixedPipelineState fixed_state; - fixed_state.input_assembly = GetInputAssemblyState(regs); - fixed_state.tessellation = GetTessellationState(regs); - fixed_state.rasterizer = GetRasterizerState(regs); + fixed_state.rasterizer.Fill(regs); fixed_state.depth_stencil.Fill(regs); fixed_state.color_blending = GetColorBlendingState(regs); return fixed_state; @@ -307,4 +275,41 @@ Maxwell::StencilOp FixedPipelineState::UnpackStencilOp(u32 packed) noexcept { return LUT[packed]; } +u32 FixedPipelineState::PackCullFace(Maxwell::CullFace cull) noexcept { + // FrontAndBack is 0x408, by substracting 0x406 in it we get 2. + // Individual cull faces are in 0x404 and 0x405, substracting 0x404 we get 0 and 1. + const u32 value = static_cast<u32>(cull); + return value - (value == 0x408 ? 0x406 : 0x404); +} + +Maxwell::CullFace FixedPipelineState::UnpackCullFace(u32 packed) noexcept { + static constexpr std::array LUT = {Maxwell::CullFace::Front, Maxwell::CullFace::Back, + Maxwell::CullFace::FrontAndBack}; + return LUT[packed]; +} + +u32 FixedPipelineState::PackFrontFace(Maxwell::FrontFace face) noexcept { + return static_cast<u32>(face) - 0x900; +} + +Maxwell::FrontFace FixedPipelineState::UnpackFrontFace(u32 packed) noexcept { + return static_cast<Maxwell::FrontFace>(packed + 0x900); +} + +u32 FixedPipelineState::PackPolygonMode(Maxwell::PolygonMode mode) noexcept { + return static_cast<u32>(mode) - 0x1B00; +} + +Maxwell::PolygonMode FixedPipelineState::UnpackPolygonMode(u32 packed) noexcept { + return static_cast<Maxwell::PolygonMode>(packed + 0x1B00); +} + +u32 FixedPipelineState::PackLogicOp(Maxwell::LogicOperation op) noexcept { + return static_cast<u32>(op) - 0x1500; +} + +Maxwell::LogicOperation FixedPipelineState::UnpackLogicOp(u32 packed) noexcept { + return static_cast<Maxwell::LogicOperation>(packed + 0x1500); +} + } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.h b/src/video_core/renderer_vulkan/fixed_pipeline_state.h index e30877e77..75b093e90 100644 --- a/src/video_core/renderer_vulkan/fixed_pipeline_state.h +++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.h @@ -30,6 +30,18 @@ struct FixedPipelineState { static u32 PackStencilOp(Maxwell::StencilOp op) noexcept; static Maxwell::StencilOp UnpackStencilOp(u32 packed) noexcept; + static u32 PackCullFace(Maxwell::CullFace cull) noexcept; + static Maxwell::CullFace UnpackCullFace(u32 packed) noexcept; + + static u32 PackFrontFace(Maxwell::FrontFace face) noexcept; + static Maxwell::FrontFace UnpackFrontFace(u32 packed) noexcept; + + static u32 PackPolygonMode(Maxwell::PolygonMode mode) noexcept; + static Maxwell::PolygonMode UnpackPolygonMode(u32 packed) noexcept; + + static u32 PackLogicOp(Maxwell::LogicOperation op) noexcept; + static Maxwell::LogicOperation UnpackLogicOp(u32 packed) noexcept; + struct BlendingAttachment { constexpr BlendingAttachment(bool enable, Maxwell::Blend::Equation rgb_equation, Maxwell::Blend::Factor src_rgb_func, @@ -119,71 +131,52 @@ struct FixedPipelineState { }; static_assert(IsHashable<VertexInput>); - struct InputAssembly { - constexpr InputAssembly(Maxwell::PrimitiveTopology topology, bool primitive_restart_enable, - float point_size) - : topology{topology}, primitive_restart_enable{primitive_restart_enable}, - point_size{point_size} {} - InputAssembly() = default; - - Maxwell::PrimitiveTopology topology; - bool primitive_restart_enable; - float point_size; - - std::size_t Hash() const noexcept; - - bool operator==(const InputAssembly& rhs) const noexcept; - - bool operator!=(const InputAssembly& rhs) const noexcept { - return !operator==(rhs); - } - }; + struct Rasterizer { + union { + u32 raw; + BitField<0, 4, u32> topology; + BitField<4, 1, u32> primitive_restart_enable; + BitField<5, 1, u32> cull_enable; + BitField<6, 1, u32> depth_bias_enable; + BitField<7, 1, u32> depth_clamp_enable; + BitField<8, 1, u32> ndc_minus_one_to_one; + BitField<9, 2, u32> cull_face; + BitField<11, 1, u32> front_face; + BitField<12, 2, u32> polygon_mode; + BitField<14, 5, u32> patch_control_points_minus_one; + BitField<19, 2, u32> tessellation_primitive; + BitField<21, 2, u32> tessellation_spacing; + BitField<23, 1, u32> tessellation_clockwise; + BitField<24, 1, u32> logic_op_enable; + BitField<25, 4, u32> logic_op; + }; - struct Tessellation { - constexpr Tessellation(u32 patch_control_points, Maxwell::TessellationPrimitive primitive, - Maxwell::TessellationSpacing spacing, bool clockwise) - : patch_control_points{patch_control_points}, primitive{primitive}, spacing{spacing}, - clockwise{clockwise} {} - Tessellation() = default; + // TODO(Rodrigo): Move this to push constants + u32 point_size; - u32 patch_control_points; - Maxwell::TessellationPrimitive primitive; - Maxwell::TessellationSpacing spacing; - bool clockwise; + void Fill(const Maxwell& regs) noexcept; std::size_t Hash() const noexcept; - bool operator==(const Tessellation& rhs) const noexcept; + bool operator==(const Rasterizer& rhs) const noexcept; - bool operator!=(const Tessellation& rhs) const noexcept { + bool operator!=(const Rasterizer& rhs) const noexcept { return !operator==(rhs); } - }; - - struct Rasterizer { - constexpr Rasterizer(bool cull_enable, bool depth_bias_enable, bool depth_clamp_enable, - bool ndc_minus_one_to_one, Maxwell::CullFace cull_face, - Maxwell::FrontFace front_face) - : cull_enable{cull_enable}, depth_bias_enable{depth_bias_enable}, - depth_clamp_enable{depth_clamp_enable}, ndc_minus_one_to_one{ndc_minus_one_to_one}, - cull_face{cull_face}, front_face{front_face} {} - Rasterizer() = default; - - bool cull_enable; - bool depth_bias_enable; - bool depth_clamp_enable; - bool ndc_minus_one_to_one; - Maxwell::CullFace cull_face; - Maxwell::FrontFace front_face; - std::size_t Hash() const noexcept; + constexpr Maxwell::PrimitiveTopology Topology() const noexcept { + return static_cast<Maxwell::PrimitiveTopology>(topology.Value()); + } - bool operator==(const Rasterizer& rhs) const noexcept; + Maxwell::CullFace CullFace() const noexcept { + return UnpackCullFace(cull_face.Value()); + } - bool operator!=(const Rasterizer& rhs) const noexcept { - return !operator==(rhs); + Maxwell::FrontFace FrontFace() const noexcept { + return UnpackFrontFace(front_face.Value()); } }; + static_assert(IsHashable<Rasterizer>); struct DepthStencil { template <std::size_t Position> @@ -257,8 +250,6 @@ struct FixedPipelineState { }; VertexInput vertex_input; - InputAssembly input_assembly; - Tessellation tessellation; Rasterizer rasterizer; DepthStencil depth_stencil; ColorBlending color_blending; @@ -273,8 +264,6 @@ struct FixedPipelineState { }; static_assert(std::is_trivially_copyable_v<FixedPipelineState::BlendingAttachment>); static_assert(std::is_trivially_copyable_v<FixedPipelineState::VertexInput>); -static_assert(std::is_trivially_copyable_v<FixedPipelineState::InputAssembly>); -static_assert(std::is_trivially_copyable_v<FixedPipelineState::Tessellation>); 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>); diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index 0dd3ea5bc..e12c26076 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -158,10 +158,8 @@ std::vector<vk::ShaderModule> VKGraphicsPipeline::CreateShaderModules( vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpass_params, const SPIRVProgram& program) const { const auto& vi = fixed_state.vertex_input; - const auto& ia = fixed_state.input_assembly; const auto& ds = fixed_state.depth_stencil; const auto& cd = fixed_state.color_blending; - const auto& ts = fixed_state.tessellation; const auto& rs = fixed_state.rasterizer; std::vector<VkVertexInputBindingDescription> vertex_bindings; @@ -226,15 +224,15 @@ vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpa input_assembly_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; input_assembly_ci.pNext = nullptr; input_assembly_ci.flags = 0; - input_assembly_ci.topology = MaxwellToVK::PrimitiveTopology(device, ia.topology); + input_assembly_ci.topology = MaxwellToVK::PrimitiveTopology(device, rs.Topology()); input_assembly_ci.primitiveRestartEnable = - ia.primitive_restart_enable && SupportsPrimitiveRestart(input_assembly_ci.topology); + rs.primitive_restart_enable != 0 && SupportsPrimitiveRestart(input_assembly_ci.topology); VkPipelineTessellationStateCreateInfo tessellation_ci; tessellation_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO; tessellation_ci.pNext = nullptr; tessellation_ci.flags = 0; - tessellation_ci.patchControlPoints = ts.patch_control_points; + tessellation_ci.patchControlPoints = rs.patch_control_points_minus_one.Value() + 1; VkPipelineViewportStateCreateInfo viewport_ci; viewport_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; @@ -253,8 +251,8 @@ vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpa rasterization_ci.rasterizerDiscardEnable = VK_FALSE; rasterization_ci.polygonMode = VK_POLYGON_MODE_FILL; rasterization_ci.cullMode = - rs.cull_enable ? MaxwellToVK::CullFace(rs.cull_face) : VK_CULL_MODE_NONE; - rasterization_ci.frontFace = MaxwellToVK::FrontFace(rs.front_face); + rs.cull_enable ? MaxwellToVK::CullFace(rs.CullFace()) : VK_CULL_MODE_NONE; + rasterization_ci.frontFace = MaxwellToVK::FrontFace(rs.FrontFace()); rasterization_ci.depthBiasEnable = rs.depth_bias_enable; rasterization_ci.depthBiasConstantFactor = 0.0f; rasterization_ci.depthBiasClamp = 0.0f; diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 083da9999..8fdc6400d 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -329,9 +329,9 @@ VKPipelineCache::DecompileShaders(const GraphicsPipelineCacheKey& key) { const auto& gpu = system.GPU().Maxwell3D(); Specialization specialization; - if (fixed_state.input_assembly.topology == Maxwell::PrimitiveTopology::Points) { - ASSERT(fixed_state.input_assembly.point_size != 0.0f); - specialization.point_size = fixed_state.input_assembly.point_size; + if (fixed_state.rasterizer.Topology() == Maxwell::PrimitiveTopology::Points) { + ASSERT(fixed_state.rasterizer.point_size != 0); + std::memcpy(&specialization.point_size, &fixed_state.rasterizer.point_size, sizeof(u32)); } for (std::size_t i = 0; i < Maxwell::NumVertexAttributes; ++i) { specialization.attribute_types[i] = fixed_state.vertex_input.attributes[i].Type(); |