// Copyright 2019 yuzu Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. #include #include #include #include "common/cityhash.h" #include "common/common_types.h" #include "video_core/renderer_vulkan/fixed_pipeline_state.h" 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)); front.action_depth_fail.Assign(PackStencilOp(regs.stencil_front_op_zfail)); front.action_depth_pass.Assign(PackStencilOp(regs.stencil_front_op_zpass)); front.test_func.Assign(PackComparisonOp(regs.stencil_front_func_func)); if (regs.stencil_two_side_enable) { back.action_stencil_fail.Assign(PackStencilOp(regs.stencil_back_op_fail)); back.action_depth_fail.Assign(PackStencilOp(regs.stencil_back_op_zfail)); back.action_depth_pass.Assign(PackStencilOp(regs.stencil_back_op_zpass)); back.test_func.Assign(PackComparisonOp(regs.stencil_back_func_func)); } else { back.action_stencil_fail.Assign(front.action_stencil_fail); back.action_depth_fail.Assign(front.action_depth_fail); back.action_depth_pass.Assign(front.action_depth_pass); back.test_func.Assign(front.test_func); } depth_test_enable.Assign(regs.depth_test_enable); depth_write_enable.Assign(regs.depth_write_enabled); depth_bounds_enable.Assign(regs.depth_bounds_enable); stencil_enable.Assign(regs.stencil_enable); depth_test_func.Assign(PackComparisonOp(regs.depth_test_func)); } 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(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; } 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(regs.tess_mode.prim.Value())); tessellation_spacing.Assign(static_cast(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 } void FixedPipelineState::ColorBlending::Fill(const Maxwell& regs) noexcept { for (std::size_t index = 0; index < std::size(attachments); ++index) { attachments[index].Fill(regs, index); } } void FixedPipelineState::BlendingAttachment::Fill(const Maxwell& regs, std::size_t index) { const auto& mask = regs.color_mask[regs.color_mask_common ? 0 : index]; raw = 0; mask_r.Assign(mask.R); mask_g.Assign(mask.G); mask_b.Assign(mask.B); mask_a.Assign(mask.A); // TODO: C++20 Use templated lambda to deduplicate code if (!regs.independent_blend_enable) { const auto& src = regs.blend; if (!src.enable[index]) { return; } equation_rgb.Assign(PackBlendEquation(src.equation_rgb)); equation_a.Assign(PackBlendEquation(src.equation_a)); factor_source_rgb.Assign(PackBlendFactor(src.factor_source_rgb)); factor_dest_rgb.Assign(PackBlendFactor(src.factor_dest_rgb)); factor_source_a.Assign(PackBlendFactor(src.factor_source_a)); factor_dest_a.Assign(PackBlendFactor(src.factor_dest_a)); enable.Assign(1); return; } if (!regs.blend.enable[index]) { return; } const auto& src = regs.independent_blend[index]; equation_rgb.Assign(PackBlendEquation(src.equation_rgb)); equation_a.Assign(PackBlendEquation(src.equation_a)); factor_source_rgb.Assign(PackBlendFactor(src.factor_source_rgb)); factor_dest_rgb.Assign(PackBlendFactor(src.factor_dest_rgb)); factor_source_a.Assign(PackBlendFactor(src.factor_source_a)); factor_dest_a.Assign(PackBlendFactor(src.factor_dest_a)); enable.Assign(1); } std::size_t FixedPipelineState::Hash() const noexcept { const u64 hash = Common::CityHash64(reinterpret_cast(this), sizeof *this); return static_cast(hash); } bool FixedPipelineState::operator==(const FixedPipelineState& rhs) const noexcept { return std::memcmp(this, &rhs, sizeof *this) == 0; } FixedPipelineState GetFixedPipelineState(const Maxwell& regs) { FixedPipelineState fixed_state; fixed_state.rasterizer.Fill(regs); fixed_state.depth_stencil.Fill(regs); fixed_state.color_blending.Fill(regs); fixed_state.padding = {}; return fixed_state; } u32 FixedPipelineState::PackComparisonOp(Maxwell::ComparisonOp op) noexcept { // OpenGL enums go from 0x200 to 0x207 and the others from 1 to 8 // If we substract 0x200 to OpenGL enums and 1 to the others we get a 0-7 range. // Perfect for a hash. const u32 value = static_cast(op); return value - (value >= 0x200 ? 0x200 : 1); } Maxwell::ComparisonOp FixedPipelineState::UnpackComparisonOp(u32 packed) noexcept { // Read PackComparisonOp for the logic behind this. return static_cast(packed + 1); } u32 FixedPipelineState::PackStencilOp(Maxwell::StencilOp op) noexcept { switch (op) { case Maxwell::StencilOp::Keep: case Maxwell::StencilOp::KeepOGL: return 0; case Maxwell::StencilOp::Zero: case Maxwell::StencilOp::ZeroOGL: return 1; case Maxwell::StencilOp::Replace: case Maxwell::StencilOp::ReplaceOGL: return 2; case Maxwell::StencilOp::Incr: case Maxwell::StencilOp::IncrOGL: return 3; case Maxwell::StencilOp::Decr: case Maxwell::StencilOp::DecrOGL: return 4; case Maxwell::StencilOp::Invert: case Maxwell::StencilOp::InvertOGL: return 5; case Maxwell::StencilOp::IncrWrap: case Maxwell::StencilOp::IncrWrapOGL: return 6; case Maxwell::StencilOp::DecrWrap: case Maxwell::StencilOp::DecrWrapOGL: return 7; } return 0; } Maxwell::StencilOp FixedPipelineState::UnpackStencilOp(u32 packed) noexcept { static constexpr std::array LUT = {Maxwell::StencilOp::Keep, Maxwell::StencilOp::Zero, Maxwell::StencilOp::Replace, Maxwell::StencilOp::Incr, Maxwell::StencilOp::Decr, Maxwell::StencilOp::Invert, Maxwell::StencilOp::IncrWrap, Maxwell::StencilOp::DecrWrap}; 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(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(face) - 0x900; } Maxwell::FrontFace FixedPipelineState::UnpackFrontFace(u32 packed) noexcept { return static_cast(packed + 0x900); } u32 FixedPipelineState::PackPolygonMode(Maxwell::PolygonMode mode) noexcept { return static_cast(mode) - 0x1B00; } Maxwell::PolygonMode FixedPipelineState::UnpackPolygonMode(u32 packed) noexcept { return static_cast(packed + 0x1B00); } u32 FixedPipelineState::PackLogicOp(Maxwell::LogicOperation op) noexcept { return static_cast(op) - 0x1500; } Maxwell::LogicOperation FixedPipelineState::UnpackLogicOp(u32 packed) noexcept { return static_cast(packed + 0x1500); } u32 FixedPipelineState::PackBlendEquation(Maxwell::Blend::Equation equation) noexcept { switch (equation) { case Maxwell::Blend::Equation::Add: case Maxwell::Blend::Equation::AddGL: return 0; case Maxwell::Blend::Equation::Subtract: case Maxwell::Blend::Equation::SubtractGL: return 1; case Maxwell::Blend::Equation::ReverseSubtract: case Maxwell::Blend::Equation::ReverseSubtractGL: return 2; case Maxwell::Blend::Equation::Min: case Maxwell::Blend::Equation::MinGL: return 3; case Maxwell::Blend::Equation::Max: case Maxwell::Blend::Equation::MaxGL: return 4; } return 0; } Maxwell::Blend::Equation FixedPipelineState::UnpackBlendEquation(u32 packed) noexcept { static constexpr std::array LUT = { Maxwell::Blend::Equation::Add, Maxwell::Blend::Equation::Subtract, Maxwell::Blend::Equation::ReverseSubtract, Maxwell::Blend::Equation::Min, Maxwell::Blend::Equation::Max}; return LUT[packed]; } u32 FixedPipelineState::PackBlendFactor(Maxwell::Blend::Factor factor) noexcept { switch (factor) { case Maxwell::Blend::Factor::Zero: case Maxwell::Blend::Factor::ZeroGL: return 0; case Maxwell::Blend::Factor::One: case Maxwell::Blend::Factor::OneGL: return 1; case Maxwell::Blend::Factor::SourceColor: case Maxwell::Blend::Factor::SourceColorGL: return 2; case Maxwell::Blend::Factor::OneMinusSourceColor: case Maxwell::Blend::Factor::OneMinusSourceColorGL: return 3; case Maxwell::Blend::Factor::SourceAlpha: case Maxwell::Blend::Factor::SourceAlphaGL: return 4; case Maxwell::Blend::Factor::OneMinusSourceAlpha: case Maxwell::Blend::Factor::OneMinusSourceAlphaGL: return 5; case Maxwell::Blend::Factor::DestAlpha: case Maxwell::Blend::Factor::DestAlphaGL: return 6; case Maxwell::Blend::Factor::OneMinusDestAlpha: case Maxwell::Blend::Factor::OneMinusDestAlphaGL: return 7; case Maxwell::Blend::Factor::DestColor: case Maxwell::Blend::Factor::DestColorGL: return 8; case Maxwell::Blend::Factor::OneMinusDestColor: case Maxwell::Blend::Factor::OneMinusDestColorGL: return 9; case Maxwell::Blend::Factor::SourceAlphaSaturate: case Maxwell::Blend::Factor::SourceAlphaSaturateGL: return 10; case Maxwell::Blend::Factor::Source1Color: case Maxwell::Blend::Factor::Source1ColorGL: return 11; case Maxwell::Blend::Factor::OneMinusSource1Color: case Maxwell::Blend::Factor::OneMinusSource1ColorGL: return 12; case Maxwell::Blend::Factor::Source1Alpha: case Maxwell::Blend::Factor::Source1AlphaGL: return 13; case Maxwell::Blend::Factor::OneMinusSource1Alpha: case Maxwell::Blend::Factor::OneMinusSource1AlphaGL: return 14; case Maxwell::Blend::Factor::ConstantColor: case Maxwell::Blend::Factor::ConstantColorGL: return 15; case Maxwell::Blend::Factor::OneMinusConstantColor: case Maxwell::Blend::Factor::OneMinusConstantColorGL: return 16; case Maxwell::Blend::Factor::ConstantAlpha: case Maxwell::Blend::Factor::ConstantAlphaGL: return 17; case Maxwell::Blend::Factor::OneMinusConstantAlpha: case Maxwell::Blend::Factor::OneMinusConstantAlphaGL: return 18; } return 0; } Maxwell::Blend::Factor FixedPipelineState::UnpackBlendFactor(u32 packed) noexcept { static constexpr std::array LUT = { Maxwell::Blend::Factor::Zero, Maxwell::Blend::Factor::One, Maxwell::Blend::Factor::SourceColor, Maxwell::Blend::Factor::OneMinusSourceColor, Maxwell::Blend::Factor::SourceAlpha, Maxwell::Blend::Factor::OneMinusSourceAlpha, Maxwell::Blend::Factor::DestAlpha, Maxwell::Blend::Factor::OneMinusDestAlpha, Maxwell::Blend::Factor::DestColor, Maxwell::Blend::Factor::OneMinusDestColor, Maxwell::Blend::Factor::SourceAlphaSaturate, Maxwell::Blend::Factor::Source1Color, Maxwell::Blend::Factor::OneMinusSource1Color, Maxwell::Blend::Factor::Source1Alpha, Maxwell::Blend::Factor::OneMinusSource1Alpha, Maxwell::Blend::Factor::ConstantColor, Maxwell::Blend::Factor::OneMinusConstantColor, Maxwell::Blend::Factor::ConstantAlpha, Maxwell::Blend::Factor::OneMinusConstantAlpha, }; return LUT[packed]; } } // namespace Vulkan