diff options
19 files changed, 686 insertions, 284 deletions
diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE/bug-report-feature-request.md index 70e1bba67..5706243bb 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE/bug-report-feature-request.md | |||
| @@ -1,4 +1,13 @@ | |||
| 1 | <!-- | 1 | --- |
| 2 | name: Bug Report / Feature Request | ||
| 3 | about: Tech support does not belong here. You should only file an issue here if you think you have experienced an actual bug with yuzu or you are requesting a feature you believe would make yuzu better. | ||
| 4 | title: '' | ||
| 5 | labels: '' | ||
| 6 | assignees: '' | ||
| 7 | |||
| 8 | --- | ||
| 9 | |||
| 10 | <!--- | ||
| 2 | Please keep in mind yuzu is EXPERIMENTAL SOFTWARE. | 11 | Please keep in mind yuzu is EXPERIMENTAL SOFTWARE. |
| 3 | 12 | ||
| 4 | Please read the FAQ: | 13 | Please read the FAQ: |
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 000000000..52faafad3 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | blank_issues_enabled: false | ||
| 2 | contact_links: | ||
| 3 | - name: yuzu Discord | ||
| 4 | url: https://discord.com/invite/u77vRWY | ||
| 5 | about: If you are experiencing an issue with yuzu, and you need tech support, or if you have a general question, try asking in the official yuzu Discord linked here. Piracy is not allowed. | ||
| 6 | - name: Community forums | ||
| 7 | url: https://community.citra-emu.org | ||
| 8 | about: This is an alternative place for tech support, however helpers there are not as active. | ||
diff --git a/externals/Vulkan-Headers b/externals/Vulkan-Headers | |||
| Subproject 9250d5ae8f50202005233dc0512a1d460c8b483 | Subproject 8188e3fbbc105591064093440f88081fb957d4f | ||
diff --git a/src/core/arm/cpu_interrupt_handler.cpp b/src/core/arm/cpu_interrupt_handler.cpp index 2f1a1a269..df0350881 100644 --- a/src/core/arm/cpu_interrupt_handler.cpp +++ b/src/core/arm/cpu_interrupt_handler.cpp | |||
| @@ -2,8 +2,6 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "common/thread.h" | 5 | #include "common/thread.h" |
| 8 | #include "core/arm/cpu_interrupt_handler.h" | 6 | #include "core/arm/cpu_interrupt_handler.h" |
| 9 | 7 | ||
diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp index 424278816..d1f0ea932 100644 --- a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp +++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp | |||
| @@ -39,52 +39,18 @@ constexpr std::array POLYGON_OFFSET_ENABLE_LUT = { | |||
| 39 | 39 | ||
| 40 | } // Anonymous namespace | 40 | } // Anonymous namespace |
| 41 | 41 | ||
| 42 | void FixedPipelineState::DepthStencil::Fill(const Maxwell& regs) noexcept { | 42 | void FixedPipelineState::Fill(const Maxwell& regs, bool has_extended_dynamic_state) { |
| 43 | raw = 0; | ||
| 44 | front.action_stencil_fail.Assign(PackStencilOp(regs.stencil_front_op_fail)); | ||
| 45 | front.action_depth_fail.Assign(PackStencilOp(regs.stencil_front_op_zfail)); | ||
| 46 | front.action_depth_pass.Assign(PackStencilOp(regs.stencil_front_op_zpass)); | ||
| 47 | front.test_func.Assign(PackComparisonOp(regs.stencil_front_func_func)); | ||
| 48 | if (regs.stencil_two_side_enable) { | ||
| 49 | back.action_stencil_fail.Assign(PackStencilOp(regs.stencil_back_op_fail)); | ||
| 50 | back.action_depth_fail.Assign(PackStencilOp(regs.stencil_back_op_zfail)); | ||
| 51 | back.action_depth_pass.Assign(PackStencilOp(regs.stencil_back_op_zpass)); | ||
| 52 | back.test_func.Assign(PackComparisonOp(regs.stencil_back_func_func)); | ||
| 53 | } else { | ||
| 54 | back.action_stencil_fail.Assign(front.action_stencil_fail); | ||
| 55 | back.action_depth_fail.Assign(front.action_depth_fail); | ||
| 56 | back.action_depth_pass.Assign(front.action_depth_pass); | ||
| 57 | back.test_func.Assign(front.test_func); | ||
| 58 | } | ||
| 59 | depth_test_enable.Assign(regs.depth_test_enable); | ||
| 60 | depth_write_enable.Assign(regs.depth_write_enabled); | ||
| 61 | depth_bounds_enable.Assign(regs.depth_bounds_enable); | ||
| 62 | stencil_enable.Assign(regs.stencil_enable); | ||
| 63 | depth_test_func.Assign(PackComparisonOp(regs.depth_test_func)); | ||
| 64 | } | ||
| 65 | |||
| 66 | void FixedPipelineState::Rasterizer::Fill(const Maxwell& regs) noexcept { | ||
| 67 | const auto& clip = regs.view_volume_clip_control; | 43 | const auto& clip = regs.view_volume_clip_control; |
| 68 | const std::array enabled_lut = {regs.polygon_offset_point_enable, | 44 | const std::array enabled_lut = {regs.polygon_offset_point_enable, |
| 69 | regs.polygon_offset_line_enable, | 45 | regs.polygon_offset_line_enable, |
| 70 | regs.polygon_offset_fill_enable}; | 46 | regs.polygon_offset_fill_enable}; |
| 71 | const u32 topology_index = static_cast<u32>(regs.draw.topology.Value()); | 47 | const u32 topology_index = static_cast<u32>(regs.draw.topology.Value()); |
| 72 | 48 | ||
| 73 | u32 packed_front_face = PackFrontFace(regs.front_face); | ||
| 74 | if (regs.screen_y_control.triangle_rast_flip != 0) { | ||
| 75 | // Flip front face | ||
| 76 | packed_front_face = 1 - packed_front_face; | ||
| 77 | } | ||
| 78 | |||
| 79 | raw = 0; | 49 | raw = 0; |
| 80 | topology.Assign(topology_index); | ||
| 81 | primitive_restart_enable.Assign(regs.primitive_restart.enabled != 0 ? 1 : 0); | 50 | primitive_restart_enable.Assign(regs.primitive_restart.enabled != 0 ? 1 : 0); |
| 82 | cull_enable.Assign(regs.cull_test_enabled != 0 ? 1 : 0); | ||
| 83 | depth_bias_enable.Assign(enabled_lut[POLYGON_OFFSET_ENABLE_LUT[topology_index]] != 0 ? 1 : 0); | 51 | depth_bias_enable.Assign(enabled_lut[POLYGON_OFFSET_ENABLE_LUT[topology_index]] != 0 ? 1 : 0); |
| 84 | depth_clamp_disabled.Assign(regs.view_volume_clip_control.depth_clamp_disabled.Value()); | 52 | depth_clamp_disabled.Assign(regs.view_volume_clip_control.depth_clamp_disabled.Value()); |
| 85 | ndc_minus_one_to_one.Assign(regs.depth_mode == Maxwell::DepthMode::MinusOneToOne ? 1 : 0); | 53 | ndc_minus_one_to_one.Assign(regs.depth_mode == Maxwell::DepthMode::MinusOneToOne ? 1 : 0); |
| 86 | cull_face.Assign(PackCullFace(regs.cull_face)); | ||
| 87 | front_face.Assign(packed_front_face); | ||
| 88 | polygon_mode.Assign(PackPolygonMode(regs.polygon_mode_front)); | 54 | polygon_mode.Assign(PackPolygonMode(regs.polygon_mode_front)); |
| 89 | patch_control_points_minus_one.Assign(regs.patch_vertices - 1); | 55 | patch_control_points_minus_one.Assign(regs.patch_vertices - 1); |
| 90 | tessellation_primitive.Assign(static_cast<u32>(regs.tess_mode.prim.Value())); | 56 | tessellation_primitive.Assign(static_cast<u32>(regs.tess_mode.prim.Value())); |
| @@ -93,19 +59,37 @@ void FixedPipelineState::Rasterizer::Fill(const Maxwell& regs) noexcept { | |||
| 93 | logic_op_enable.Assign(regs.logic_op.enable != 0 ? 1 : 0); | 59 | logic_op_enable.Assign(regs.logic_op.enable != 0 ? 1 : 0); |
| 94 | logic_op.Assign(PackLogicOp(regs.logic_op.operation)); | 60 | logic_op.Assign(PackLogicOp(regs.logic_op.operation)); |
| 95 | rasterize_enable.Assign(regs.rasterize_enable != 0 ? 1 : 0); | 61 | rasterize_enable.Assign(regs.rasterize_enable != 0 ? 1 : 0); |
| 62 | |||
| 96 | std::memcpy(&point_size, ®s.point_size, sizeof(point_size)); // TODO: C++20 std::bit_cast | 63 | std::memcpy(&point_size, ®s.point_size, sizeof(point_size)); // TODO: C++20 std::bit_cast |
| 97 | } | ||
| 98 | 64 | ||
| 99 | void FixedPipelineState::ColorBlending::Fill(const Maxwell& regs) noexcept { | 65 | for (std::size_t index = 0; index < Maxwell::NumVertexArrays; ++index) { |
| 66 | binding_divisors[index] = | ||
| 67 | regs.instanced_arrays.IsInstancingEnabled(index) ? regs.vertex_array[index].divisor : 0; | ||
| 68 | } | ||
| 69 | |||
| 70 | for (std::size_t index = 0; index < Maxwell::NumVertexAttributes; ++index) { | ||
| 71 | const auto& input = regs.vertex_attrib_format[index]; | ||
| 72 | auto& attribute = attributes[index]; | ||
| 73 | attribute.raw = 0; | ||
| 74 | attribute.enabled.Assign(input.IsConstant() ? 0 : 1); | ||
| 75 | attribute.buffer.Assign(input.buffer); | ||
| 76 | attribute.offset.Assign(input.offset); | ||
| 77 | attribute.type.Assign(static_cast<u32>(input.type.Value())); | ||
| 78 | attribute.size.Assign(static_cast<u32>(input.size.Value())); | ||
| 79 | } | ||
| 80 | |||
| 100 | for (std::size_t index = 0; index < std::size(attachments); ++index) { | 81 | for (std::size_t index = 0; index < std::size(attachments); ++index) { |
| 101 | attachments[index].Fill(regs, index); | 82 | attachments[index].Fill(regs, index); |
| 102 | } | 83 | } |
| 103 | } | ||
| 104 | 84 | ||
| 105 | void FixedPipelineState::ViewportSwizzles::Fill(const Maxwell& regs) noexcept { | ||
| 106 | const auto& transform = regs.viewport_transform; | 85 | const auto& transform = regs.viewport_transform; |
| 107 | std::transform(transform.begin(), transform.end(), swizzles.begin(), | 86 | std::transform(transform.begin(), transform.end(), viewport_swizzles.begin(), |
| 108 | [](const auto& viewport) { return static_cast<u16>(viewport.swizzle.raw); }); | 87 | [](const auto& viewport) { return static_cast<u16>(viewport.swizzle.raw); }); |
| 88 | |||
| 89 | if (!has_extended_dynamic_state) { | ||
| 90 | no_extended_dynamic_state.Assign(1); | ||
| 91 | dynamic_state.Fill(regs); | ||
| 92 | } | ||
| 109 | } | 93 | } |
| 110 | 94 | ||
| 111 | void FixedPipelineState::BlendingAttachment::Fill(const Maxwell& regs, std::size_t index) { | 95 | void FixedPipelineState::BlendingAttachment::Fill(const Maxwell& regs, std::size_t index) { |
| @@ -147,20 +131,57 @@ void FixedPipelineState::BlendingAttachment::Fill(const Maxwell& regs, std::size | |||
| 147 | enable.Assign(1); | 131 | enable.Assign(1); |
| 148 | } | 132 | } |
| 149 | 133 | ||
| 150 | void FixedPipelineState::Fill(const Maxwell& regs) { | 134 | void FixedPipelineState::DynamicState::Fill(const Maxwell& regs) { |
| 151 | rasterizer.Fill(regs); | 135 | const u32 topology_index = static_cast<u32>(regs.draw.topology.Value()); |
| 152 | depth_stencil.Fill(regs); | 136 | u32 packed_front_face = PackFrontFace(regs.front_face); |
| 153 | color_blending.Fill(regs); | 137 | if (regs.screen_y_control.triangle_rast_flip != 0) { |
| 154 | viewport_swizzles.Fill(regs); | 138 | // Flip front face |
| 139 | packed_front_face = 1 - packed_front_face; | ||
| 140 | } | ||
| 141 | |||
| 142 | raw1 = 0; | ||
| 143 | raw2 = 0; | ||
| 144 | front.action_stencil_fail.Assign(PackStencilOp(regs.stencil_front_op_fail)); | ||
| 145 | front.action_depth_fail.Assign(PackStencilOp(regs.stencil_front_op_zfail)); | ||
| 146 | front.action_depth_pass.Assign(PackStencilOp(regs.stencil_front_op_zpass)); | ||
| 147 | front.test_func.Assign(PackComparisonOp(regs.stencil_front_func_func)); | ||
| 148 | if (regs.stencil_two_side_enable) { | ||
| 149 | back.action_stencil_fail.Assign(PackStencilOp(regs.stencil_back_op_fail)); | ||
| 150 | back.action_depth_fail.Assign(PackStencilOp(regs.stencil_back_op_zfail)); | ||
| 151 | back.action_depth_pass.Assign(PackStencilOp(regs.stencil_back_op_zpass)); | ||
| 152 | back.test_func.Assign(PackComparisonOp(regs.stencil_back_func_func)); | ||
| 153 | } else { | ||
| 154 | back.action_stencil_fail.Assign(front.action_stencil_fail); | ||
| 155 | back.action_depth_fail.Assign(front.action_depth_fail); | ||
| 156 | back.action_depth_pass.Assign(front.action_depth_pass); | ||
| 157 | back.test_func.Assign(front.test_func); | ||
| 158 | } | ||
| 159 | stencil_enable.Assign(regs.stencil_enable); | ||
| 160 | depth_write_enable.Assign(regs.depth_write_enabled); | ||
| 161 | depth_bounds_enable.Assign(regs.depth_bounds_enable); | ||
| 162 | depth_test_enable.Assign(regs.depth_test_enable); | ||
| 163 | front_face.Assign(packed_front_face); | ||
| 164 | depth_test_func.Assign(PackComparisonOp(regs.depth_test_func)); | ||
| 165 | topology.Assign(topology_index); | ||
| 166 | cull_face.Assign(PackCullFace(regs.cull_face)); | ||
| 167 | cull_enable.Assign(regs.cull_test_enabled != 0 ? 1 : 0); | ||
| 168 | |||
| 169 | for (std::size_t index = 0; index < Maxwell::NumVertexArrays; ++index) { | ||
| 170 | const auto& input = regs.vertex_array[index]; | ||
| 171 | VertexBinding& binding = vertex_bindings[index]; | ||
| 172 | binding.raw = 0; | ||
| 173 | binding.enabled.Assign(input.IsEnabled() ? 1 : 0); | ||
| 174 | binding.stride.Assign(static_cast<u16>(input.stride.Value())); | ||
| 175 | } | ||
| 155 | } | 176 | } |
| 156 | 177 | ||
| 157 | std::size_t FixedPipelineState::Hash() const noexcept { | 178 | std::size_t FixedPipelineState::Hash() const noexcept { |
| 158 | const u64 hash = Common::CityHash64(reinterpret_cast<const char*>(this), sizeof *this); | 179 | const u64 hash = Common::CityHash64(reinterpret_cast<const char*>(this), Size()); |
| 159 | return static_cast<std::size_t>(hash); | 180 | return static_cast<std::size_t>(hash); |
| 160 | } | 181 | } |
| 161 | 182 | ||
| 162 | bool FixedPipelineState::operator==(const FixedPipelineState& rhs) const noexcept { | 183 | bool FixedPipelineState::operator==(const FixedPipelineState& rhs) const noexcept { |
| 163 | return std::memcmp(this, &rhs, sizeof *this) == 0; | 184 | return std::memcmp(this, &rhs, Size()) == 0; |
| 164 | } | 185 | } |
| 165 | 186 | ||
| 166 | u32 FixedPipelineState::PackComparisonOp(Maxwell::ComparisonOp op) noexcept { | 187 | u32 FixedPipelineState::PackComparisonOp(Maxwell::ComparisonOp op) noexcept { |
diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.h b/src/video_core/renderer_vulkan/fixed_pipeline_state.h index 31a6398f2..cdcbb65f5 100644 --- a/src/video_core/renderer_vulkan/fixed_pipeline_state.h +++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.h | |||
| @@ -60,14 +60,6 @@ struct FixedPipelineState { | |||
| 60 | 60 | ||
| 61 | void Fill(const Maxwell& regs, std::size_t index); | 61 | void Fill(const Maxwell& regs, std::size_t index); |
| 62 | 62 | ||
| 63 | std::size_t Hash() const noexcept; | ||
| 64 | |||
| 65 | bool operator==(const BlendingAttachment& rhs) const noexcept; | ||
| 66 | |||
| 67 | bool operator!=(const BlendingAttachment& rhs) const noexcept { | ||
| 68 | return !operator==(rhs); | ||
| 69 | } | ||
| 70 | |||
| 71 | constexpr std::array<bool, 4> Mask() const noexcept { | 63 | constexpr std::array<bool, 4> Mask() const noexcept { |
| 72 | return {mask_r != 0, mask_g != 0, mask_b != 0, mask_a != 0}; | 64 | return {mask_r != 0, mask_g != 0, mask_b != 0, mask_a != 0}; |
| 73 | } | 65 | } |
| @@ -97,156 +89,116 @@ struct FixedPipelineState { | |||
| 97 | } | 89 | } |
| 98 | }; | 90 | }; |
| 99 | 91 | ||
| 100 | struct VertexInput { | 92 | union VertexAttribute { |
| 101 | union Binding { | 93 | u32 raw; |
| 102 | u16 raw; | 94 | BitField<0, 1, u32> enabled; |
| 103 | BitField<0, 1, u16> enabled; | 95 | BitField<1, 5, u32> buffer; |
| 104 | BitField<1, 12, u16> stride; | 96 | BitField<6, 14, u32> offset; |
| 105 | }; | 97 | BitField<20, 3, u32> type; |
| 98 | BitField<23, 6, u32> size; | ||
| 106 | 99 | ||
| 107 | union Attribute { | 100 | constexpr Maxwell::VertexAttribute::Type Type() const noexcept { |
| 108 | u32 raw; | 101 | return static_cast<Maxwell::VertexAttribute::Type>(type.Value()); |
| 109 | BitField<0, 1, u32> enabled; | ||
| 110 | BitField<1, 5, u32> buffer; | ||
| 111 | BitField<6, 14, u32> offset; | ||
| 112 | BitField<20, 3, u32> type; | ||
| 113 | BitField<23, 6, u32> size; | ||
| 114 | |||
| 115 | constexpr Maxwell::VertexAttribute::Type Type() const noexcept { | ||
| 116 | return static_cast<Maxwell::VertexAttribute::Type>(type.Value()); | ||
| 117 | } | ||
| 118 | |||
| 119 | constexpr Maxwell::VertexAttribute::Size Size() const noexcept { | ||
| 120 | return static_cast<Maxwell::VertexAttribute::Size>(size.Value()); | ||
| 121 | } | ||
| 122 | }; | ||
| 123 | |||
| 124 | std::array<Binding, Maxwell::NumVertexArrays> bindings; | ||
| 125 | std::array<u32, Maxwell::NumVertexArrays> binding_divisors; | ||
| 126 | std::array<Attribute, Maxwell::NumVertexAttributes> attributes; | ||
| 127 | |||
| 128 | void SetBinding(std::size_t index, bool enabled, u32 stride, u32 divisor) noexcept { | ||
| 129 | auto& binding = bindings[index]; | ||
| 130 | binding.raw = 0; | ||
| 131 | binding.enabled.Assign(enabled ? 1 : 0); | ||
| 132 | binding.stride.Assign(static_cast<u16>(stride)); | ||
| 133 | binding_divisors[index] = divisor; | ||
| 134 | } | 102 | } |
| 135 | 103 | ||
| 136 | void SetAttribute(std::size_t index, bool enabled, u32 buffer, u32 offset, | 104 | constexpr Maxwell::VertexAttribute::Size Size() const noexcept { |
| 137 | Maxwell::VertexAttribute::Type type, | 105 | return static_cast<Maxwell::VertexAttribute::Size>(size.Value()); |
| 138 | Maxwell::VertexAttribute::Size size) noexcept { | ||
| 139 | auto& attribute = attributes[index]; | ||
| 140 | attribute.raw = 0; | ||
| 141 | attribute.enabled.Assign(enabled ? 1 : 0); | ||
| 142 | attribute.buffer.Assign(buffer); | ||
| 143 | attribute.offset.Assign(offset); | ||
| 144 | attribute.type.Assign(static_cast<u32>(type)); | ||
| 145 | attribute.size.Assign(static_cast<u32>(size)); | ||
| 146 | } | 106 | } |
| 147 | }; | 107 | }; |
| 148 | 108 | ||
| 149 | struct Rasterizer { | 109 | template <std::size_t Position> |
| 150 | union { | 110 | union StencilFace { |
| 151 | u32 raw; | 111 | BitField<Position + 0, 3, u32> action_stencil_fail; |
| 152 | BitField<0, 4, u32> topology; | 112 | BitField<Position + 3, 3, u32> action_depth_fail; |
| 153 | BitField<4, 1, u32> primitive_restart_enable; | 113 | BitField<Position + 6, 3, u32> action_depth_pass; |
| 154 | BitField<5, 1, u32> cull_enable; | 114 | BitField<Position + 9, 3, u32> test_func; |
| 155 | BitField<6, 1, u32> depth_bias_enable; | ||
| 156 | BitField<7, 1, u32> depth_clamp_disabled; | ||
| 157 | BitField<8, 1, u32> ndc_minus_one_to_one; | ||
| 158 | BitField<9, 2, u32> cull_face; | ||
| 159 | BitField<11, 1, u32> front_face; | ||
| 160 | BitField<12, 2, u32> polygon_mode; | ||
| 161 | BitField<14, 5, u32> patch_control_points_minus_one; | ||
| 162 | BitField<19, 2, u32> tessellation_primitive; | ||
| 163 | BitField<21, 2, u32> tessellation_spacing; | ||
| 164 | BitField<23, 1, u32> tessellation_clockwise; | ||
| 165 | BitField<24, 1, u32> logic_op_enable; | ||
| 166 | BitField<25, 4, u32> logic_op; | ||
| 167 | BitField<29, 1, u32> rasterize_enable; | ||
| 168 | }; | ||
| 169 | |||
| 170 | // TODO(Rodrigo): Move this to push constants | ||
| 171 | u32 point_size; | ||
| 172 | 115 | ||
| 173 | void Fill(const Maxwell& regs) noexcept; | 116 | Maxwell::StencilOp ActionStencilFail() const noexcept { |
| 117 | return UnpackStencilOp(action_stencil_fail); | ||
| 118 | } | ||
| 174 | 119 | ||
| 175 | constexpr Maxwell::PrimitiveTopology Topology() const noexcept { | 120 | Maxwell::StencilOp ActionDepthFail() const noexcept { |
| 176 | return static_cast<Maxwell::PrimitiveTopology>(topology.Value()); | 121 | return UnpackStencilOp(action_depth_fail); |
| 177 | } | 122 | } |
| 178 | 123 | ||
| 179 | Maxwell::CullFace CullFace() const noexcept { | 124 | Maxwell::StencilOp ActionDepthPass() const noexcept { |
| 180 | return UnpackCullFace(cull_face.Value()); | 125 | return UnpackStencilOp(action_depth_pass); |
| 181 | } | 126 | } |
| 182 | 127 | ||
| 183 | Maxwell::FrontFace FrontFace() const noexcept { | 128 | Maxwell::ComparisonOp TestFunc() const noexcept { |
| 184 | return UnpackFrontFace(front_face.Value()); | 129 | return UnpackComparisonOp(test_func); |
| 185 | } | 130 | } |
| 186 | }; | 131 | }; |
| 187 | 132 | ||
| 188 | struct DepthStencil { | 133 | union VertexBinding { |
| 189 | template <std::size_t Position> | 134 | u16 raw; |
| 190 | union StencilFace { | 135 | BitField<0, 12, u16> stride; |
| 191 | BitField<Position + 0, 3, u32> action_stencil_fail; | 136 | BitField<12, 1, u16> enabled; |
| 192 | BitField<Position + 3, 3, u32> action_depth_fail; | 137 | }; |
| 193 | BitField<Position + 6, 3, u32> action_depth_pass; | ||
| 194 | BitField<Position + 9, 3, u32> test_func; | ||
| 195 | |||
| 196 | Maxwell::StencilOp ActionStencilFail() const noexcept { | ||
| 197 | return UnpackStencilOp(action_stencil_fail); | ||
| 198 | } | ||
| 199 | |||
| 200 | Maxwell::StencilOp ActionDepthFail() const noexcept { | ||
| 201 | return UnpackStencilOp(action_depth_fail); | ||
| 202 | } | ||
| 203 | |||
| 204 | Maxwell::StencilOp ActionDepthPass() const noexcept { | ||
| 205 | return UnpackStencilOp(action_depth_pass); | ||
| 206 | } | ||
| 207 | |||
| 208 | Maxwell::ComparisonOp TestFunc() const noexcept { | ||
| 209 | return UnpackComparisonOp(test_func); | ||
| 210 | } | ||
| 211 | }; | ||
| 212 | 138 | ||
| 139 | struct DynamicState { | ||
| 213 | union { | 140 | union { |
| 214 | u32 raw; | 141 | u32 raw1; |
| 215 | StencilFace<0> front; | 142 | StencilFace<0> front; |
| 216 | StencilFace<12> back; | 143 | StencilFace<12> back; |
| 217 | BitField<24, 1, u32> depth_test_enable; | 144 | BitField<24, 1, u32> stencil_enable; |
| 218 | BitField<25, 1, u32> depth_write_enable; | 145 | BitField<25, 1, u32> depth_write_enable; |
| 219 | BitField<26, 1, u32> depth_bounds_enable; | 146 | BitField<26, 1, u32> depth_bounds_enable; |
| 220 | BitField<27, 1, u32> stencil_enable; | 147 | BitField<27, 1, u32> depth_test_enable; |
| 221 | BitField<28, 3, u32> depth_test_func; | 148 | BitField<28, 1, u32> front_face; |
| 149 | BitField<29, 3, u32> depth_test_func; | ||
| 150 | }; | ||
| 151 | union { | ||
| 152 | u32 raw2; | ||
| 153 | BitField<0, 4, u32> topology; | ||
| 154 | BitField<4, 2, u32> cull_face; | ||
| 155 | BitField<6, 1, u32> cull_enable; | ||
| 222 | }; | 156 | }; |
| 157 | std::array<VertexBinding, Maxwell::NumVertexArrays> vertex_bindings; | ||
| 223 | 158 | ||
| 224 | void Fill(const Maxwell& regs) noexcept; | 159 | void Fill(const Maxwell& regs); |
| 225 | 160 | ||
| 226 | Maxwell::ComparisonOp DepthTestFunc() const noexcept { | 161 | Maxwell::ComparisonOp DepthTestFunc() const noexcept { |
| 227 | return UnpackComparisonOp(depth_test_func); | 162 | return UnpackComparisonOp(depth_test_func); |
| 228 | } | 163 | } |
| 229 | }; | ||
| 230 | |||
| 231 | struct ColorBlending { | ||
| 232 | std::array<BlendingAttachment, Maxwell::NumRenderTargets> attachments; | ||
| 233 | 164 | ||
| 234 | void Fill(const Maxwell& regs) noexcept; | 165 | Maxwell::CullFace CullFace() const noexcept { |
| 235 | }; | 166 | return UnpackCullFace(cull_face.Value()); |
| 167 | } | ||
| 236 | 168 | ||
| 237 | struct ViewportSwizzles { | 169 | Maxwell::FrontFace FrontFace() const noexcept { |
| 238 | std::array<u16, Maxwell::NumViewports> swizzles; | 170 | return UnpackFrontFace(front_face.Value()); |
| 171 | } | ||
| 239 | 172 | ||
| 240 | void Fill(const Maxwell& regs) noexcept; | 173 | constexpr Maxwell::PrimitiveTopology Topology() const noexcept { |
| 174 | return static_cast<Maxwell::PrimitiveTopology>(topology.Value()); | ||
| 175 | } | ||
| 241 | }; | 176 | }; |
| 242 | 177 | ||
| 243 | VertexInput vertex_input; | 178 | union { |
| 244 | Rasterizer rasterizer; | 179 | u32 raw; |
| 245 | DepthStencil depth_stencil; | 180 | BitField<0, 1, u32> no_extended_dynamic_state; |
| 246 | ColorBlending color_blending; | 181 | BitField<2, 1, u32> primitive_restart_enable; |
| 247 | ViewportSwizzles viewport_swizzles; | 182 | BitField<3, 1, u32> depth_bias_enable; |
| 183 | BitField<4, 1, u32> depth_clamp_disabled; | ||
| 184 | BitField<5, 1, u32> ndc_minus_one_to_one; | ||
| 185 | BitField<6, 2, u32> polygon_mode; | ||
| 186 | BitField<8, 5, u32> patch_control_points_minus_one; | ||
| 187 | BitField<13, 2, u32> tessellation_primitive; | ||
| 188 | BitField<15, 2, u32> tessellation_spacing; | ||
| 189 | BitField<17, 1, u32> tessellation_clockwise; | ||
| 190 | BitField<18, 1, u32> logic_op_enable; | ||
| 191 | BitField<19, 4, u32> logic_op; | ||
| 192 | BitField<23, 1, u32> rasterize_enable; | ||
| 193 | }; | ||
| 194 | u32 point_size; | ||
| 195 | std::array<u32, Maxwell::NumVertexArrays> binding_divisors; | ||
| 196 | std::array<VertexAttribute, Maxwell::NumVertexAttributes> attributes; | ||
| 197 | std::array<BlendingAttachment, Maxwell::NumRenderTargets> attachments; | ||
| 198 | std::array<u16, Maxwell::NumViewports> viewport_swizzles; | ||
| 199 | DynamicState dynamic_state; | ||
| 248 | 200 | ||
| 249 | void Fill(const Maxwell& regs); | 201 | void Fill(const Maxwell& regs, bool has_extended_dynamic_state); |
| 250 | 202 | ||
| 251 | std::size_t Hash() const noexcept; | 203 | std::size_t Hash() const noexcept; |
| 252 | 204 | ||
| @@ -255,6 +207,11 @@ struct FixedPipelineState { | |||
| 255 | bool operator!=(const FixedPipelineState& rhs) const noexcept { | 207 | bool operator!=(const FixedPipelineState& rhs) const noexcept { |
| 256 | return !operator==(rhs); | 208 | return !operator==(rhs); |
| 257 | } | 209 | } |
| 210 | |||
| 211 | std::size_t Size() const noexcept { | ||
| 212 | const std::size_t total_size = sizeof *this; | ||
| 213 | return total_size - (no_extended_dynamic_state != 0 ? 0 : sizeof(DynamicState)); | ||
| 214 | } | ||
| 258 | }; | 215 | }; |
| 259 | static_assert(std::has_unique_object_representations_v<FixedPipelineState>); | 216 | static_assert(std::has_unique_object_representations_v<FixedPipelineState>); |
| 260 | static_assert(std::is_trivially_copyable_v<FixedPipelineState>); | 217 | static_assert(std::is_trivially_copyable_v<FixedPipelineState>); |
diff --git a/src/video_core/renderer_vulkan/vk_device.cpp b/src/video_core/renderer_vulkan/vk_device.cpp index 9fd8ac3f6..fdaea4210 100644 --- a/src/video_core/renderer_vulkan/vk_device.cpp +++ b/src/video_core/renderer_vulkan/vk_device.cpp | |||
| @@ -313,6 +313,16 @@ bool VKDevice::Create() { | |||
| 313 | LOG_INFO(Render_Vulkan, "Device doesn't support custom border colors"); | 313 | LOG_INFO(Render_Vulkan, "Device doesn't support custom border colors"); |
| 314 | } | 314 | } |
| 315 | 315 | ||
| 316 | VkPhysicalDeviceExtendedDynamicStateFeaturesEXT dynamic_state; | ||
| 317 | if (ext_extended_dynamic_state) { | ||
| 318 | dynamic_state.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_FEATURES_EXT; | ||
| 319 | dynamic_state.pNext = nullptr; | ||
| 320 | dynamic_state.extendedDynamicState = VK_TRUE; | ||
| 321 | SetNext(next, dynamic_state); | ||
| 322 | } else { | ||
| 323 | LOG_INFO(Render_Vulkan, "Device doesn't support extended dynamic state"); | ||
| 324 | } | ||
| 325 | |||
| 316 | if (!ext_depth_range_unrestricted) { | 326 | if (!ext_depth_range_unrestricted) { |
| 317 | LOG_INFO(Render_Vulkan, "Device doesn't support depth range unrestricted"); | 327 | LOG_INFO(Render_Vulkan, "Device doesn't support depth range unrestricted"); |
| 318 | } | 328 | } |
| @@ -541,6 +551,7 @@ std::vector<const char*> VKDevice::LoadExtensions() { | |||
| 541 | bool has_ext_subgroup_size_control{}; | 551 | bool has_ext_subgroup_size_control{}; |
| 542 | bool has_ext_transform_feedback{}; | 552 | bool has_ext_transform_feedback{}; |
| 543 | bool has_ext_custom_border_color{}; | 553 | bool has_ext_custom_border_color{}; |
| 554 | bool has_ext_extended_dynamic_state{}; | ||
| 544 | for (const auto& extension : physical.EnumerateDeviceExtensionProperties()) { | 555 | for (const auto& extension : physical.EnumerateDeviceExtensionProperties()) { |
| 545 | Test(extension, nv_viewport_swizzle, VK_NV_VIEWPORT_SWIZZLE_EXTENSION_NAME, true); | 556 | Test(extension, nv_viewport_swizzle, VK_NV_VIEWPORT_SWIZZLE_EXTENSION_NAME, true); |
| 546 | Test(extension, khr_uniform_buffer_standard_layout, | 557 | Test(extension, khr_uniform_buffer_standard_layout, |
| @@ -558,6 +569,8 @@ std::vector<const char*> VKDevice::LoadExtensions() { | |||
| 558 | false); | 569 | false); |
| 559 | Test(extension, has_ext_custom_border_color, VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME, | 570 | Test(extension, has_ext_custom_border_color, VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME, |
| 560 | false); | 571 | false); |
| 572 | Test(extension, has_ext_extended_dynamic_state, | ||
| 573 | VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME, false); | ||
| 561 | if (Settings::values.renderer_debug) { | 574 | if (Settings::values.renderer_debug) { |
| 562 | Test(extension, nv_device_diagnostics_config, | 575 | Test(extension, nv_device_diagnostics_config, |
| 563 | VK_NV_DEVICE_DIAGNOSTICS_CONFIG_EXTENSION_NAME, true); | 576 | VK_NV_DEVICE_DIAGNOSTICS_CONFIG_EXTENSION_NAME, true); |
| @@ -643,6 +656,19 @@ std::vector<const char*> VKDevice::LoadExtensions() { | |||
| 643 | } | 656 | } |
| 644 | } | 657 | } |
| 645 | 658 | ||
| 659 | if (has_ext_extended_dynamic_state) { | ||
| 660 | VkPhysicalDeviceExtendedDynamicStateFeaturesEXT dynamic_state; | ||
| 661 | dynamic_state.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_FEATURES_EXT; | ||
| 662 | dynamic_state.pNext = nullptr; | ||
| 663 | features.pNext = &dynamic_state; | ||
| 664 | physical.GetFeatures2KHR(features); | ||
| 665 | |||
| 666 | if (dynamic_state.extendedDynamicState) { | ||
| 667 | extensions.push_back(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME); | ||
| 668 | ext_extended_dynamic_state = true; | ||
| 669 | } | ||
| 670 | } | ||
| 671 | |||
| 646 | return extensions; | 672 | return extensions; |
| 647 | } | 673 | } |
| 648 | 674 | ||
diff --git a/src/video_core/renderer_vulkan/vk_device.h b/src/video_core/renderer_vulkan/vk_device.h index 6b9227b09..ae5c21baa 100644 --- a/src/video_core/renderer_vulkan/vk_device.h +++ b/src/video_core/renderer_vulkan/vk_device.h | |||
| @@ -182,6 +182,11 @@ public: | |||
| 182 | return ext_custom_border_color; | 182 | return ext_custom_border_color; |
| 183 | } | 183 | } |
| 184 | 184 | ||
| 185 | /// Returns true if the device supports VK_EXT_extended_dynamic_state. | ||
| 186 | bool IsExtExtendedDynamicStateSupported() const { | ||
| 187 | return ext_extended_dynamic_state; | ||
| 188 | } | ||
| 189 | |||
| 185 | /// Returns the vendor name reported from Vulkan. | 190 | /// Returns the vendor name reported from Vulkan. |
| 186 | std::string_view GetVendorName() const { | 191 | std::string_view GetVendorName() const { |
| 187 | return vendor_name; | 192 | return vendor_name; |
| @@ -239,6 +244,7 @@ private: | |||
| 239 | bool ext_shader_viewport_index_layer{}; ///< Support for VK_EXT_shader_viewport_index_layer. | 244 | bool ext_shader_viewport_index_layer{}; ///< Support for VK_EXT_shader_viewport_index_layer. |
| 240 | bool ext_transform_feedback{}; ///< Support for VK_EXT_transform_feedback. | 245 | bool ext_transform_feedback{}; ///< Support for VK_EXT_transform_feedback. |
| 241 | bool ext_custom_border_color{}; ///< Support for VK_EXT_custom_border_color. | 246 | bool ext_custom_border_color{}; ///< Support for VK_EXT_custom_border_color. |
| 247 | bool ext_extended_dynamic_state{}; ///< Support for VK_EXT_extended_dynamic_state. | ||
| 242 | bool nv_device_diagnostics_config{}; ///< Support for VK_NV_device_diagnostics_config. | 248 | bool nv_device_diagnostics_config{}; ///< Support for VK_NV_device_diagnostics_config. |
| 243 | 249 | ||
| 244 | // Telemetry parameters | 250 | // Telemetry parameters |
diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index 69b6bba00..844445105 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp | |||
| @@ -176,20 +176,32 @@ std::vector<vk::ShaderModule> VKGraphicsPipeline::CreateShaderModules( | |||
| 176 | 176 | ||
| 177 | vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpass_params, | 177 | vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpass_params, |
| 178 | const SPIRVProgram& program) const { | 178 | const SPIRVProgram& program) const { |
| 179 | const auto& vi = fixed_state.vertex_input; | 179 | const auto& state = fixed_state; |
| 180 | const auto& ds = fixed_state.depth_stencil; | 180 | const auto& viewport_swizzles = state.viewport_swizzles; |
| 181 | const auto& cd = fixed_state.color_blending; | 181 | |
| 182 | const auto& rs = fixed_state.rasterizer; | 182 | FixedPipelineState::DynamicState dynamic; |
| 183 | const auto& viewport_swizzles = fixed_state.viewport_swizzles.swizzles; | 183 | if (device.IsExtExtendedDynamicStateSupported()) { |
| 184 | // Insert dummy values, as long as they are valid they don't matter as extended dynamic | ||
| 185 | // state is ignored | ||
| 186 | dynamic.raw1 = 0; | ||
| 187 | dynamic.raw2 = 0; | ||
| 188 | for (FixedPipelineState::VertexBinding& binding : dynamic.vertex_bindings) { | ||
| 189 | // Enable all vertex bindings | ||
| 190 | binding.raw = 0; | ||
| 191 | binding.enabled.Assign(1); | ||
| 192 | } | ||
| 193 | } else { | ||
| 194 | dynamic = state.dynamic_state; | ||
| 195 | } | ||
| 184 | 196 | ||
| 185 | std::vector<VkVertexInputBindingDescription> vertex_bindings; | 197 | std::vector<VkVertexInputBindingDescription> vertex_bindings; |
| 186 | std::vector<VkVertexInputBindingDivisorDescriptionEXT> vertex_binding_divisors; | 198 | std::vector<VkVertexInputBindingDivisorDescriptionEXT> vertex_binding_divisors; |
| 187 | for (std::size_t index = 0; index < std::size(vi.bindings); ++index) { | 199 | for (std::size_t index = 0; index < Maxwell::NumVertexArrays; ++index) { |
| 188 | const auto& binding = vi.bindings[index]; | 200 | const auto& binding = dynamic.vertex_bindings[index]; |
| 189 | if (!binding.enabled) { | 201 | if (!binding.enabled) { |
| 190 | continue; | 202 | continue; |
| 191 | } | 203 | } |
| 192 | const bool instanced = vi.binding_divisors[index] != 0; | 204 | const bool instanced = state.binding_divisors[index] != 0; |
| 193 | const auto rate = instanced ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX; | 205 | const auto rate = instanced ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX; |
| 194 | 206 | ||
| 195 | auto& vertex_binding = vertex_bindings.emplace_back(); | 207 | auto& vertex_binding = vertex_bindings.emplace_back(); |
| @@ -200,14 +212,14 @@ vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpa | |||
| 200 | if (instanced) { | 212 | if (instanced) { |
| 201 | auto& binding_divisor = vertex_binding_divisors.emplace_back(); | 213 | auto& binding_divisor = vertex_binding_divisors.emplace_back(); |
| 202 | binding_divisor.binding = static_cast<u32>(index); | 214 | binding_divisor.binding = static_cast<u32>(index); |
| 203 | binding_divisor.divisor = vi.binding_divisors[index]; | 215 | binding_divisor.divisor = state.binding_divisors[index]; |
| 204 | } | 216 | } |
| 205 | } | 217 | } |
| 206 | 218 | ||
| 207 | std::vector<VkVertexInputAttributeDescription> vertex_attributes; | 219 | std::vector<VkVertexInputAttributeDescription> vertex_attributes; |
| 208 | const auto& input_attributes = program[0]->entries.attributes; | 220 | const auto& input_attributes = program[0]->entries.attributes; |
| 209 | for (std::size_t index = 0; index < std::size(vi.attributes); ++index) { | 221 | for (std::size_t index = 0; index < state.attributes.size(); ++index) { |
| 210 | const auto& attribute = vi.attributes[index]; | 222 | const auto& attribute = state.attributes[index]; |
| 211 | if (!attribute.enabled) { | 223 | if (!attribute.enabled) { |
| 212 | continue; | 224 | continue; |
| 213 | } | 225 | } |
| @@ -244,15 +256,15 @@ vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpa | |||
| 244 | input_assembly_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; | 256 | input_assembly_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; |
| 245 | input_assembly_ci.pNext = nullptr; | 257 | input_assembly_ci.pNext = nullptr; |
| 246 | input_assembly_ci.flags = 0; | 258 | input_assembly_ci.flags = 0; |
| 247 | input_assembly_ci.topology = MaxwellToVK::PrimitiveTopology(device, rs.Topology()); | 259 | input_assembly_ci.topology = MaxwellToVK::PrimitiveTopology(device, dynamic.Topology()); |
| 248 | input_assembly_ci.primitiveRestartEnable = | 260 | input_assembly_ci.primitiveRestartEnable = |
| 249 | rs.primitive_restart_enable != 0 && SupportsPrimitiveRestart(input_assembly_ci.topology); | 261 | state.primitive_restart_enable != 0 && SupportsPrimitiveRestart(input_assembly_ci.topology); |
| 250 | 262 | ||
| 251 | VkPipelineTessellationStateCreateInfo tessellation_ci; | 263 | VkPipelineTessellationStateCreateInfo tessellation_ci; |
| 252 | tessellation_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO; | 264 | tessellation_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO; |
| 253 | tessellation_ci.pNext = nullptr; | 265 | tessellation_ci.pNext = nullptr; |
| 254 | tessellation_ci.flags = 0; | 266 | tessellation_ci.flags = 0; |
| 255 | tessellation_ci.patchControlPoints = rs.patch_control_points_minus_one.Value() + 1; | 267 | tessellation_ci.patchControlPoints = state.patch_control_points_minus_one.Value() + 1; |
| 256 | 268 | ||
| 257 | VkPipelineViewportStateCreateInfo viewport_ci; | 269 | VkPipelineViewportStateCreateInfo viewport_ci; |
| 258 | viewport_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; | 270 | viewport_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; |
| @@ -280,13 +292,13 @@ vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpa | |||
| 280 | rasterization_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; | 292 | rasterization_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; |
| 281 | rasterization_ci.pNext = nullptr; | 293 | rasterization_ci.pNext = nullptr; |
| 282 | rasterization_ci.flags = 0; | 294 | rasterization_ci.flags = 0; |
| 283 | rasterization_ci.depthClampEnable = rs.depth_clamp_disabled == 0 ? VK_TRUE : VK_FALSE; | 295 | rasterization_ci.depthClampEnable = state.depth_clamp_disabled == 0 ? VK_TRUE : VK_FALSE; |
| 284 | rasterization_ci.rasterizerDiscardEnable = rs.rasterize_enable == 0 ? VK_TRUE : VK_FALSE; | 296 | rasterization_ci.rasterizerDiscardEnable = state.rasterize_enable == 0 ? VK_TRUE : VK_FALSE; |
| 285 | rasterization_ci.polygonMode = VK_POLYGON_MODE_FILL; | 297 | rasterization_ci.polygonMode = VK_POLYGON_MODE_FILL; |
| 286 | rasterization_ci.cullMode = | 298 | rasterization_ci.cullMode = |
| 287 | rs.cull_enable ? MaxwellToVK::CullFace(rs.CullFace()) : VK_CULL_MODE_NONE; | 299 | dynamic.cull_enable ? MaxwellToVK::CullFace(dynamic.CullFace()) : VK_CULL_MODE_NONE; |
| 288 | rasterization_ci.frontFace = MaxwellToVK::FrontFace(rs.FrontFace()); | 300 | rasterization_ci.frontFace = MaxwellToVK::FrontFace(dynamic.FrontFace()); |
| 289 | rasterization_ci.depthBiasEnable = rs.depth_bias_enable; | 301 | rasterization_ci.depthBiasEnable = state.depth_bias_enable; |
| 290 | rasterization_ci.depthBiasConstantFactor = 0.0f; | 302 | rasterization_ci.depthBiasConstantFactor = 0.0f; |
| 291 | rasterization_ci.depthBiasClamp = 0.0f; | 303 | rasterization_ci.depthBiasClamp = 0.0f; |
| 292 | rasterization_ci.depthBiasSlopeFactor = 0.0f; | 304 | rasterization_ci.depthBiasSlopeFactor = 0.0f; |
| @@ -307,14 +319,15 @@ vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpa | |||
| 307 | depth_stencil_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; | 319 | depth_stencil_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; |
| 308 | depth_stencil_ci.pNext = nullptr; | 320 | depth_stencil_ci.pNext = nullptr; |
| 309 | depth_stencil_ci.flags = 0; | 321 | depth_stencil_ci.flags = 0; |
| 310 | depth_stencil_ci.depthTestEnable = ds.depth_test_enable; | 322 | depth_stencil_ci.depthTestEnable = dynamic.depth_test_enable; |
| 311 | depth_stencil_ci.depthWriteEnable = ds.depth_write_enable; | 323 | depth_stencil_ci.depthWriteEnable = dynamic.depth_write_enable; |
| 312 | depth_stencil_ci.depthCompareOp = | 324 | depth_stencil_ci.depthCompareOp = dynamic.depth_test_enable |
| 313 | ds.depth_test_enable ? MaxwellToVK::ComparisonOp(ds.DepthTestFunc()) : VK_COMPARE_OP_ALWAYS; | 325 | ? MaxwellToVK::ComparisonOp(dynamic.DepthTestFunc()) |
| 314 | depth_stencil_ci.depthBoundsTestEnable = ds.depth_bounds_enable; | 326 | : VK_COMPARE_OP_ALWAYS; |
| 315 | depth_stencil_ci.stencilTestEnable = ds.stencil_enable; | 327 | depth_stencil_ci.depthBoundsTestEnable = dynamic.depth_bounds_enable; |
| 316 | depth_stencil_ci.front = GetStencilFaceState(ds.front); | 328 | depth_stencil_ci.stencilTestEnable = dynamic.stencil_enable; |
| 317 | depth_stencil_ci.back = GetStencilFaceState(ds.back); | 329 | depth_stencil_ci.front = GetStencilFaceState(dynamic.front); |
| 330 | depth_stencil_ci.back = GetStencilFaceState(dynamic.back); | ||
| 318 | depth_stencil_ci.minDepthBounds = 0.0f; | 331 | depth_stencil_ci.minDepthBounds = 0.0f; |
| 319 | depth_stencil_ci.maxDepthBounds = 0.0f; | 332 | depth_stencil_ci.maxDepthBounds = 0.0f; |
| 320 | 333 | ||
| @@ -324,7 +337,7 @@ vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpa | |||
| 324 | static constexpr std::array COMPONENT_TABLE = { | 337 | static constexpr std::array COMPONENT_TABLE = { |
| 325 | VK_COLOR_COMPONENT_R_BIT, VK_COLOR_COMPONENT_G_BIT, VK_COLOR_COMPONENT_B_BIT, | 338 | VK_COLOR_COMPONENT_R_BIT, VK_COLOR_COMPONENT_G_BIT, VK_COLOR_COMPONENT_B_BIT, |
| 326 | VK_COLOR_COMPONENT_A_BIT}; | 339 | VK_COLOR_COMPONENT_A_BIT}; |
| 327 | const auto& blend = cd.attachments[index]; | 340 | const auto& blend = state.attachments[index]; |
| 328 | 341 | ||
| 329 | VkColorComponentFlags color_components = 0; | 342 | VkColorComponentFlags color_components = 0; |
| 330 | for (std::size_t i = 0; i < COMPONENT_TABLE.size(); ++i) { | 343 | for (std::size_t i = 0; i < COMPONENT_TABLE.size(); ++i) { |
| @@ -354,11 +367,27 @@ vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpa | |||
| 354 | color_blend_ci.pAttachments = cb_attachments.data(); | 367 | color_blend_ci.pAttachments = cb_attachments.data(); |
| 355 | std::memset(color_blend_ci.blendConstants, 0, sizeof(color_blend_ci.blendConstants)); | 368 | std::memset(color_blend_ci.blendConstants, 0, sizeof(color_blend_ci.blendConstants)); |
| 356 | 369 | ||
| 357 | static constexpr std::array dynamic_states = { | 370 | std::vector dynamic_states = { |
| 358 | VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR, | 371 | VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR, |
| 359 | VK_DYNAMIC_STATE_DEPTH_BIAS, VK_DYNAMIC_STATE_BLEND_CONSTANTS, | 372 | VK_DYNAMIC_STATE_DEPTH_BIAS, VK_DYNAMIC_STATE_BLEND_CONSTANTS, |
| 360 | VK_DYNAMIC_STATE_DEPTH_BOUNDS, VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK, | 373 | VK_DYNAMIC_STATE_DEPTH_BOUNDS, VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK, |
| 361 | VK_DYNAMIC_STATE_STENCIL_WRITE_MASK, VK_DYNAMIC_STATE_STENCIL_REFERENCE}; | 374 | VK_DYNAMIC_STATE_STENCIL_WRITE_MASK, VK_DYNAMIC_STATE_STENCIL_REFERENCE, |
| 375 | }; | ||
| 376 | if (device.IsExtExtendedDynamicStateSupported()) { | ||
| 377 | static constexpr std::array extended = { | ||
| 378 | VK_DYNAMIC_STATE_CULL_MODE_EXT, | ||
| 379 | VK_DYNAMIC_STATE_FRONT_FACE_EXT, | ||
| 380 | VK_DYNAMIC_STATE_PRIMITIVE_TOPOLOGY_EXT, | ||
| 381 | VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE_EXT, | ||
| 382 | VK_DYNAMIC_STATE_DEPTH_TEST_ENABLE_EXT, | ||
| 383 | VK_DYNAMIC_STATE_DEPTH_WRITE_ENABLE_EXT, | ||
| 384 | VK_DYNAMIC_STATE_DEPTH_COMPARE_OP_EXT, | ||
| 385 | VK_DYNAMIC_STATE_DEPTH_BOUNDS_TEST_ENABLE_EXT, | ||
| 386 | VK_DYNAMIC_STATE_STENCIL_TEST_ENABLE_EXT, | ||
| 387 | VK_DYNAMIC_STATE_STENCIL_OP_EXT, | ||
| 388 | }; | ||
| 389 | dynamic_states.insert(dynamic_states.end(), extended.begin(), extended.end()); | ||
| 390 | } | ||
| 362 | 391 | ||
| 363 | VkPipelineDynamicStateCreateInfo dynamic_state_ci; | 392 | VkPipelineDynamicStateCreateInfo dynamic_state_ci; |
| 364 | dynamic_state_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; | 393 | dynamic_state_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; |
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index ea66e621e..3da835324 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp | |||
| @@ -116,12 +116,12 @@ u32 FillDescriptorLayout(const ShaderEntries& entries, | |||
| 116 | } // Anonymous namespace | 116 | } // Anonymous namespace |
| 117 | 117 | ||
| 118 | std::size_t GraphicsPipelineCacheKey::Hash() const noexcept { | 118 | std::size_t GraphicsPipelineCacheKey::Hash() const noexcept { |
| 119 | const u64 hash = Common::CityHash64(reinterpret_cast<const char*>(this), sizeof *this); | 119 | const u64 hash = Common::CityHash64(reinterpret_cast<const char*>(this), Size()); |
| 120 | return static_cast<std::size_t>(hash); | 120 | return static_cast<std::size_t>(hash); |
| 121 | } | 121 | } |
| 122 | 122 | ||
| 123 | bool GraphicsPipelineCacheKey::operator==(const GraphicsPipelineCacheKey& rhs) const noexcept { | 123 | bool GraphicsPipelineCacheKey::operator==(const GraphicsPipelineCacheKey& rhs) const noexcept { |
| 124 | return std::memcmp(&rhs, this, sizeof *this) == 0; | 124 | return std::memcmp(&rhs, this, Size()) == 0; |
| 125 | } | 125 | } |
| 126 | 126 | ||
| 127 | std::size_t ComputePipelineCacheKey::Hash() const noexcept { | 127 | std::size_t ComputePipelineCacheKey::Hash() const noexcept { |
| @@ -312,18 +312,19 @@ VKPipelineCache::DecompileShaders(const GraphicsPipelineCacheKey& key) { | |||
| 312 | const auto& gpu = system.GPU().Maxwell3D(); | 312 | const auto& gpu = system.GPU().Maxwell3D(); |
| 313 | 313 | ||
| 314 | Specialization specialization; | 314 | Specialization specialization; |
| 315 | if (fixed_state.rasterizer.Topology() == Maxwell::PrimitiveTopology::Points) { | 315 | if (fixed_state.dynamic_state.Topology() == Maxwell::PrimitiveTopology::Points || |
| 316 | device.IsExtExtendedDynamicStateSupported()) { | ||
| 316 | float point_size; | 317 | float point_size; |
| 317 | std::memcpy(&point_size, &fixed_state.rasterizer.point_size, sizeof(float)); | 318 | std::memcpy(&point_size, &fixed_state.point_size, sizeof(float)); |
| 318 | specialization.point_size = point_size; | 319 | specialization.point_size = point_size; |
| 319 | ASSERT(point_size != 0.0f); | 320 | ASSERT(point_size != 0.0f); |
| 320 | } | 321 | } |
| 321 | for (std::size_t i = 0; i < Maxwell::NumVertexAttributes; ++i) { | 322 | for (std::size_t i = 0; i < Maxwell::NumVertexAttributes; ++i) { |
| 322 | const auto& attribute = fixed_state.vertex_input.attributes[i]; | 323 | const auto& attribute = fixed_state.attributes[i]; |
| 323 | specialization.enabled_attributes[i] = attribute.enabled.Value() != 0; | 324 | specialization.enabled_attributes[i] = attribute.enabled.Value() != 0; |
| 324 | specialization.attribute_types[i] = attribute.Type(); | 325 | specialization.attribute_types[i] = attribute.Type(); |
| 325 | } | 326 | } |
| 326 | specialization.ndc_minus_one_to_one = fixed_state.rasterizer.ndc_minus_one_to_one; | 327 | specialization.ndc_minus_one_to_one = fixed_state.ndc_minus_one_to_one; |
| 327 | 328 | ||
| 328 | SPIRVProgram program; | 329 | SPIRVProgram program; |
| 329 | std::vector<VkDescriptorSetLayoutBinding> bindings; | 330 | std::vector<VkDescriptorSetLayoutBinding> bindings; |
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.h b/src/video_core/renderer_vulkan/vk_pipeline_cache.h index 0a36e5112..0a3fe65fb 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.h +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.h | |||
| @@ -44,10 +44,10 @@ class VKUpdateDescriptorQueue; | |||
| 44 | using Maxwell = Tegra::Engines::Maxwell3D::Regs; | 44 | using Maxwell = Tegra::Engines::Maxwell3D::Regs; |
| 45 | 45 | ||
| 46 | struct GraphicsPipelineCacheKey { | 46 | struct GraphicsPipelineCacheKey { |
| 47 | FixedPipelineState fixed_state; | ||
| 48 | RenderPassParams renderpass_params; | 47 | RenderPassParams renderpass_params; |
| 48 | u32 padding; | ||
| 49 | std::array<GPUVAddr, Maxwell::MaxShaderProgram> shaders; | 49 | std::array<GPUVAddr, Maxwell::MaxShaderProgram> shaders; |
| 50 | u64 padding; // This is necessary for unique object representations | 50 | FixedPipelineState fixed_state; |
| 51 | 51 | ||
| 52 | std::size_t Hash() const noexcept; | 52 | std::size_t Hash() const noexcept; |
| 53 | 53 | ||
| @@ -56,6 +56,10 @@ struct GraphicsPipelineCacheKey { | |||
| 56 | bool operator!=(const GraphicsPipelineCacheKey& rhs) const noexcept { | 56 | bool operator!=(const GraphicsPipelineCacheKey& rhs) const noexcept { |
| 57 | return !operator==(rhs); | 57 | return !operator==(rhs); |
| 58 | } | 58 | } |
| 59 | |||
| 60 | std::size_t Size() const noexcept { | ||
| 61 | return sizeof(renderpass_params) + sizeof(padding) + sizeof(shaders) + fixed_state.Size(); | ||
| 62 | } | ||
| 59 | }; | 63 | }; |
| 60 | static_assert(std::has_unique_object_representations_v<GraphicsPipelineCacheKey>); | 64 | static_assert(std::has_unique_object_representations_v<GraphicsPipelineCacheKey>); |
| 61 | static_assert(std::is_trivially_copyable_v<GraphicsPipelineCacheKey>); | 65 | static_assert(std::is_trivially_copyable_v<GraphicsPipelineCacheKey>); |
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index a8d94eac3..380ed532b 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp | |||
| @@ -186,13 +186,22 @@ bool HasToPreserveDepthContents(bool is_clear, const Maxwell& regs) { | |||
| 186 | scissor.max_y < regs.zeta_height; | 186 | scissor.max_y < regs.zeta_height; |
| 187 | } | 187 | } |
| 188 | 188 | ||
| 189 | template <std::size_t N> | ||
| 190 | std::array<VkDeviceSize, N> ExpandStrides(const std::array<u16, N>& strides) { | ||
| 191 | std::array<VkDeviceSize, N> expanded; | ||
| 192 | std::copy(strides.begin(), strides.end(), expanded.begin()); | ||
| 193 | return expanded; | ||
| 194 | } | ||
| 195 | |||
| 189 | } // Anonymous namespace | 196 | } // Anonymous namespace |
| 190 | 197 | ||
| 191 | class BufferBindings final { | 198 | class BufferBindings final { |
| 192 | public: | 199 | public: |
| 193 | void AddVertexBinding(VkBuffer buffer, VkDeviceSize offset) { | 200 | void AddVertexBinding(VkBuffer buffer, VkDeviceSize offset, VkDeviceSize size, u32 stride) { |
| 194 | vertex.buffers[vertex.num_buffers] = buffer; | 201 | vertex.buffers[vertex.num_buffers] = buffer; |
| 195 | vertex.offsets[vertex.num_buffers] = offset; | 202 | vertex.offsets[vertex.num_buffers] = offset; |
| 203 | vertex.sizes[vertex.num_buffers] = size; | ||
| 204 | vertex.strides[vertex.num_buffers] = static_cast<u16>(stride); | ||
| 196 | ++vertex.num_buffers; | 205 | ++vertex.num_buffers; |
| 197 | } | 206 | } |
| 198 | 207 | ||
| @@ -202,76 +211,76 @@ public: | |||
| 202 | index.type = type; | 211 | index.type = type; |
| 203 | } | 212 | } |
| 204 | 213 | ||
| 205 | void Bind(VKScheduler& scheduler) const { | 214 | void Bind(const VKDevice& device, VKScheduler& scheduler) const { |
| 206 | // Use this large switch case to avoid dispatching more memory in the record lambda than | 215 | // Use this large switch case to avoid dispatching more memory in the record lambda than |
| 207 | // what we need. It looks horrible, but it's the best we can do on standard C++. | 216 | // what we need. It looks horrible, but it's the best we can do on standard C++. |
| 208 | switch (vertex.num_buffers) { | 217 | switch (vertex.num_buffers) { |
| 209 | case 0: | 218 | case 0: |
| 210 | return BindStatic<0>(scheduler); | 219 | return BindStatic<0>(device, scheduler); |
| 211 | case 1: | 220 | case 1: |
| 212 | return BindStatic<1>(scheduler); | 221 | return BindStatic<1>(device, scheduler); |
| 213 | case 2: | 222 | case 2: |
| 214 | return BindStatic<2>(scheduler); | 223 | return BindStatic<2>(device, scheduler); |
| 215 | case 3: | 224 | case 3: |
| 216 | return BindStatic<3>(scheduler); | 225 | return BindStatic<3>(device, scheduler); |
| 217 | case 4: | 226 | case 4: |
| 218 | return BindStatic<4>(scheduler); | 227 | return BindStatic<4>(device, scheduler); |
| 219 | case 5: | 228 | case 5: |
| 220 | return BindStatic<5>(scheduler); | 229 | return BindStatic<5>(device, scheduler); |
| 221 | case 6: | 230 | case 6: |
| 222 | return BindStatic<6>(scheduler); | 231 | return BindStatic<6>(device, scheduler); |
| 223 | case 7: | 232 | case 7: |
| 224 | return BindStatic<7>(scheduler); | 233 | return BindStatic<7>(device, scheduler); |
| 225 | case 8: | 234 | case 8: |
| 226 | return BindStatic<8>(scheduler); | 235 | return BindStatic<8>(device, scheduler); |
| 227 | case 9: | 236 | case 9: |
| 228 | return BindStatic<9>(scheduler); | 237 | return BindStatic<9>(device, scheduler); |
| 229 | case 10: | 238 | case 10: |
| 230 | return BindStatic<10>(scheduler); | 239 | return BindStatic<10>(device, scheduler); |
| 231 | case 11: | 240 | case 11: |
| 232 | return BindStatic<11>(scheduler); | 241 | return BindStatic<11>(device, scheduler); |
| 233 | case 12: | 242 | case 12: |
| 234 | return BindStatic<12>(scheduler); | 243 | return BindStatic<12>(device, scheduler); |
| 235 | case 13: | 244 | case 13: |
| 236 | return BindStatic<13>(scheduler); | 245 | return BindStatic<13>(device, scheduler); |
| 237 | case 14: | 246 | case 14: |
| 238 | return BindStatic<14>(scheduler); | 247 | return BindStatic<14>(device, scheduler); |
| 239 | case 15: | 248 | case 15: |
| 240 | return BindStatic<15>(scheduler); | 249 | return BindStatic<15>(device, scheduler); |
| 241 | case 16: | 250 | case 16: |
| 242 | return BindStatic<16>(scheduler); | 251 | return BindStatic<16>(device, scheduler); |
| 243 | case 17: | 252 | case 17: |
| 244 | return BindStatic<17>(scheduler); | 253 | return BindStatic<17>(device, scheduler); |
| 245 | case 18: | 254 | case 18: |
| 246 | return BindStatic<18>(scheduler); | 255 | return BindStatic<18>(device, scheduler); |
| 247 | case 19: | 256 | case 19: |
| 248 | return BindStatic<19>(scheduler); | 257 | return BindStatic<19>(device, scheduler); |
| 249 | case 20: | 258 | case 20: |
| 250 | return BindStatic<20>(scheduler); | 259 | return BindStatic<20>(device, scheduler); |
| 251 | case 21: | 260 | case 21: |
| 252 | return BindStatic<21>(scheduler); | 261 | return BindStatic<21>(device, scheduler); |
| 253 | case 22: | 262 | case 22: |
| 254 | return BindStatic<22>(scheduler); | 263 | return BindStatic<22>(device, scheduler); |
| 255 | case 23: | 264 | case 23: |
| 256 | return BindStatic<23>(scheduler); | 265 | return BindStatic<23>(device, scheduler); |
| 257 | case 24: | 266 | case 24: |
| 258 | return BindStatic<24>(scheduler); | 267 | return BindStatic<24>(device, scheduler); |
| 259 | case 25: | 268 | case 25: |
| 260 | return BindStatic<25>(scheduler); | 269 | return BindStatic<25>(device, scheduler); |
| 261 | case 26: | 270 | case 26: |
| 262 | return BindStatic<26>(scheduler); | 271 | return BindStatic<26>(device, scheduler); |
| 263 | case 27: | 272 | case 27: |
| 264 | return BindStatic<27>(scheduler); | 273 | return BindStatic<27>(device, scheduler); |
| 265 | case 28: | 274 | case 28: |
| 266 | return BindStatic<28>(scheduler); | 275 | return BindStatic<28>(device, scheduler); |
| 267 | case 29: | 276 | case 29: |
| 268 | return BindStatic<29>(scheduler); | 277 | return BindStatic<29>(device, scheduler); |
| 269 | case 30: | 278 | case 30: |
| 270 | return BindStatic<30>(scheduler); | 279 | return BindStatic<30>(device, scheduler); |
| 271 | case 31: | 280 | case 31: |
| 272 | return BindStatic<31>(scheduler); | 281 | return BindStatic<31>(device, scheduler); |
| 273 | case 32: | 282 | case 32: |
| 274 | return BindStatic<32>(scheduler); | 283 | return BindStatic<32>(device, scheduler); |
| 275 | } | 284 | } |
| 276 | UNREACHABLE(); | 285 | UNREACHABLE(); |
| 277 | } | 286 | } |
| @@ -282,6 +291,8 @@ private: | |||
| 282 | std::size_t num_buffers = 0; | 291 | std::size_t num_buffers = 0; |
| 283 | std::array<VkBuffer, Maxwell::NumVertexArrays> buffers; | 292 | std::array<VkBuffer, Maxwell::NumVertexArrays> buffers; |
| 284 | std::array<VkDeviceSize, Maxwell::NumVertexArrays> offsets; | 293 | std::array<VkDeviceSize, Maxwell::NumVertexArrays> offsets; |
| 294 | std::array<VkDeviceSize, Maxwell::NumVertexArrays> sizes; | ||
| 295 | std::array<u16, Maxwell::NumVertexArrays> strides; | ||
| 285 | } vertex; | 296 | } vertex; |
| 286 | 297 | ||
| 287 | struct { | 298 | struct { |
| @@ -291,15 +302,23 @@ private: | |||
| 291 | } index; | 302 | } index; |
| 292 | 303 | ||
| 293 | template <std::size_t N> | 304 | template <std::size_t N> |
| 294 | void BindStatic(VKScheduler& scheduler) const { | 305 | void BindStatic(const VKDevice& device, VKScheduler& scheduler) const { |
| 295 | if (index.buffer) { | 306 | if (device.IsExtExtendedDynamicStateSupported()) { |
| 296 | BindStatic<N, true>(scheduler); | 307 | if (index.buffer) { |
| 308 | BindStatic<N, true, true>(scheduler); | ||
| 309 | } else { | ||
| 310 | BindStatic<N, false, true>(scheduler); | ||
| 311 | } | ||
| 297 | } else { | 312 | } else { |
| 298 | BindStatic<N, false>(scheduler); | 313 | if (index.buffer) { |
| 314 | BindStatic<N, true, false>(scheduler); | ||
| 315 | } else { | ||
| 316 | BindStatic<N, false, false>(scheduler); | ||
| 317 | } | ||
| 299 | } | 318 | } |
| 300 | } | 319 | } |
| 301 | 320 | ||
| 302 | template <std::size_t N, bool is_indexed> | 321 | template <std::size_t N, bool is_indexed, bool has_extended_dynamic_state> |
| 303 | void BindStatic(VKScheduler& scheduler) const { | 322 | void BindStatic(VKScheduler& scheduler) const { |
| 304 | static_assert(N <= Maxwell::NumVertexArrays); | 323 | static_assert(N <= Maxwell::NumVertexArrays); |
| 305 | if constexpr (N == 0) { | 324 | if constexpr (N == 0) { |
| @@ -311,6 +330,31 @@ private: | |||
| 311 | std::copy(vertex.buffers.begin(), vertex.buffers.begin() + N, buffers.begin()); | 330 | std::copy(vertex.buffers.begin(), vertex.buffers.begin() + N, buffers.begin()); |
| 312 | std::copy(vertex.offsets.begin(), vertex.offsets.begin() + N, offsets.begin()); | 331 | std::copy(vertex.offsets.begin(), vertex.offsets.begin() + N, offsets.begin()); |
| 313 | 332 | ||
| 333 | if constexpr (has_extended_dynamic_state) { | ||
| 334 | // With extended dynamic states we can specify the length and stride of a vertex buffer | ||
| 335 | // std::array<VkDeviceSize, N> sizes; | ||
| 336 | std::array<u16, N> strides; | ||
| 337 | // std::copy(vertex.sizes.begin(), vertex.sizes.begin() + N, sizes.begin()); | ||
| 338 | std::copy(vertex.strides.begin(), vertex.strides.begin() + N, strides.begin()); | ||
| 339 | |||
| 340 | if constexpr (is_indexed) { | ||
| 341 | scheduler.Record( | ||
| 342 | [buffers, offsets, strides, index = index](vk::CommandBuffer cmdbuf) { | ||
| 343 | cmdbuf.BindIndexBuffer(index.buffer, index.offset, index.type); | ||
| 344 | cmdbuf.BindVertexBuffers2EXT(0, static_cast<u32>(N), buffers.data(), | ||
| 345 | offsets.data(), nullptr, | ||
| 346 | ExpandStrides(strides).data()); | ||
| 347 | }); | ||
| 348 | } else { | ||
| 349 | scheduler.Record([buffers, offsets, strides](vk::CommandBuffer cmdbuf) { | ||
| 350 | cmdbuf.BindVertexBuffers2EXT(0, static_cast<u32>(N), buffers.data(), | ||
| 351 | offsets.data(), nullptr, | ||
| 352 | ExpandStrides(strides).data()); | ||
| 353 | }); | ||
| 354 | } | ||
| 355 | return; | ||
| 356 | } | ||
| 357 | |||
| 314 | if constexpr (is_indexed) { | 358 | if constexpr (is_indexed) { |
| 315 | // Indexed draw | 359 | // Indexed draw |
| 316 | scheduler.Record([buffers, offsets, index = index](vk::CommandBuffer cmdbuf) { | 360 | scheduler.Record([buffers, offsets, index = index](vk::CommandBuffer cmdbuf) { |
| @@ -369,7 +413,7 @@ void RasterizerVulkan::Draw(bool is_indexed, bool is_instanced) { | |||
| 369 | 413 | ||
| 370 | const auto& gpu = system.GPU().Maxwell3D(); | 414 | const auto& gpu = system.GPU().Maxwell3D(); |
| 371 | GraphicsPipelineCacheKey key; | 415 | GraphicsPipelineCacheKey key; |
| 372 | key.fixed_state.Fill(gpu.regs); | 416 | key.fixed_state.Fill(gpu.regs, device.IsExtExtendedDynamicStateSupported()); |
| 373 | 417 | ||
| 374 | buffer_cache.Map(CalculateGraphicsStreamBufferSize(is_indexed)); | 418 | buffer_cache.Map(CalculateGraphicsStreamBufferSize(is_indexed)); |
| 375 | 419 | ||
| @@ -402,7 +446,7 @@ void RasterizerVulkan::Draw(bool is_indexed, bool is_instanced) { | |||
| 402 | 446 | ||
| 403 | UpdateDynamicStates(); | 447 | UpdateDynamicStates(); |
| 404 | 448 | ||
| 405 | buffer_bindings.Bind(scheduler); | 449 | buffer_bindings.Bind(device, scheduler); |
| 406 | 450 | ||
| 407 | BeginTransformFeedback(); | 451 | BeginTransformFeedback(); |
| 408 | 452 | ||
| @@ -822,7 +866,7 @@ RasterizerVulkan::DrawParameters RasterizerVulkan::SetupGeometry(FixedPipelineSt | |||
| 822 | const auto& gpu = system.GPU().Maxwell3D(); | 866 | const auto& gpu = system.GPU().Maxwell3D(); |
| 823 | const auto& regs = gpu.regs; | 867 | const auto& regs = gpu.regs; |
| 824 | 868 | ||
| 825 | SetupVertexArrays(fixed_state.vertex_input, buffer_bindings); | 869 | SetupVertexArrays(buffer_bindings); |
| 826 | 870 | ||
| 827 | const u32 base_instance = regs.vb_base_instance; | 871 | const u32 base_instance = regs.vb_base_instance; |
| 828 | const u32 num_instances = is_instanced ? gpu.mme_draw.instance_count : 1; | 872 | const u32 num_instances = is_instanced ? gpu.mme_draw.instance_count : 1; |
| @@ -893,6 +937,17 @@ void RasterizerVulkan::UpdateDynamicStates() { | |||
| 893 | UpdateBlendConstants(regs); | 937 | UpdateBlendConstants(regs); |
| 894 | UpdateDepthBounds(regs); | 938 | UpdateDepthBounds(regs); |
| 895 | UpdateStencilFaces(regs); | 939 | UpdateStencilFaces(regs); |
| 940 | if (device.IsExtExtendedDynamicStateSupported()) { | ||
| 941 | UpdateCullMode(regs); | ||
| 942 | UpdateDepthBoundsTestEnable(regs); | ||
| 943 | UpdateDepthTestEnable(regs); | ||
| 944 | UpdateDepthWriteEnable(regs); | ||
| 945 | UpdateDepthCompareOp(regs); | ||
| 946 | UpdateFrontFace(regs); | ||
| 947 | UpdatePrimitiveTopology(regs); | ||
| 948 | UpdateStencilOp(regs); | ||
| 949 | UpdateStencilTestEnable(regs); | ||
| 950 | } | ||
| 896 | } | 951 | } |
| 897 | 952 | ||
| 898 | void RasterizerVulkan::BeginTransformFeedback() { | 953 | void RasterizerVulkan::BeginTransformFeedback() { |
| @@ -940,41 +995,25 @@ void RasterizerVulkan::EndTransformFeedback() { | |||
| 940 | [](vk::CommandBuffer cmdbuf) { cmdbuf.EndTransformFeedbackEXT(0, 0, nullptr, nullptr); }); | 995 | [](vk::CommandBuffer cmdbuf) { cmdbuf.EndTransformFeedbackEXT(0, 0, nullptr, nullptr); }); |
| 941 | } | 996 | } |
| 942 | 997 | ||
| 943 | void RasterizerVulkan::SetupVertexArrays(FixedPipelineState::VertexInput& vertex_input, | 998 | void RasterizerVulkan::SetupVertexArrays(BufferBindings& buffer_bindings) { |
| 944 | BufferBindings& buffer_bindings) { | ||
| 945 | const auto& regs = system.GPU().Maxwell3D().regs; | 999 | const auto& regs = system.GPU().Maxwell3D().regs; |
| 946 | 1000 | ||
| 947 | for (std::size_t index = 0; index < Maxwell::NumVertexAttributes; ++index) { | ||
| 948 | const auto& attrib = regs.vertex_attrib_format[index]; | ||
| 949 | if (attrib.IsConstant()) { | ||
| 950 | vertex_input.SetAttribute(index, false, 0, 0, {}, {}); | ||
| 951 | continue; | ||
| 952 | } | ||
| 953 | vertex_input.SetAttribute(index, true, attrib.buffer, attrib.offset, attrib.type.Value(), | ||
| 954 | attrib.size.Value()); | ||
| 955 | } | ||
| 956 | |||
| 957 | for (std::size_t index = 0; index < Maxwell::NumVertexArrays; ++index) { | 1001 | for (std::size_t index = 0; index < Maxwell::NumVertexArrays; ++index) { |
| 958 | const auto& vertex_array = regs.vertex_array[index]; | 1002 | const auto& vertex_array = regs.vertex_array[index]; |
| 959 | if (!vertex_array.IsEnabled()) { | 1003 | if (!vertex_array.IsEnabled()) { |
| 960 | vertex_input.SetBinding(index, false, 0, 0); | ||
| 961 | continue; | 1004 | continue; |
| 962 | } | 1005 | } |
| 963 | vertex_input.SetBinding( | ||
| 964 | index, true, vertex_array.stride, | ||
| 965 | regs.instanced_arrays.IsInstancingEnabled(index) ? vertex_array.divisor : 0); | ||
| 966 | |||
| 967 | const GPUVAddr start{vertex_array.StartAddress()}; | 1006 | const GPUVAddr start{vertex_array.StartAddress()}; |
| 968 | const GPUVAddr end{regs.vertex_array_limit[index].LimitAddress()}; | 1007 | const GPUVAddr end{regs.vertex_array_limit[index].LimitAddress()}; |
| 969 | 1008 | ||
| 970 | ASSERT(end >= start); | 1009 | ASSERT(end >= start); |
| 971 | const std::size_t size{end - start}; | 1010 | const std::size_t size = end - start; |
| 972 | if (size == 0) { | 1011 | if (size == 0) { |
| 973 | buffer_bindings.AddVertexBinding(DefaultBuffer(), 0); | 1012 | buffer_bindings.AddVertexBinding(DefaultBuffer(), 0, DEFAULT_BUFFER_SIZE, 0); |
| 974 | continue; | 1013 | continue; |
| 975 | } | 1014 | } |
| 976 | const auto info = buffer_cache.UploadMemory(start, size); | 1015 | const auto info = buffer_cache.UploadMemory(start, size); |
| 977 | buffer_bindings.AddVertexBinding(info.handle, info.offset); | 1016 | buffer_bindings.AddVertexBinding(info.handle, info.offset, size, vertex_array.stride); |
| 978 | } | 1017 | } |
| 979 | } | 1018 | } |
| 980 | 1019 | ||
| @@ -1326,6 +1365,117 @@ void RasterizerVulkan::UpdateStencilFaces(Tegra::Engines::Maxwell3D::Regs& regs) | |||
| 1326 | } | 1365 | } |
| 1327 | } | 1366 | } |
| 1328 | 1367 | ||
| 1368 | void RasterizerVulkan::UpdateCullMode(Tegra::Engines::Maxwell3D::Regs& regs) { | ||
| 1369 | if (!state_tracker.TouchCullMode()) { | ||
| 1370 | return; | ||
| 1371 | } | ||
| 1372 | scheduler.Record( | ||
| 1373 | [enabled = regs.cull_test_enabled, cull_face = regs.cull_face](vk::CommandBuffer cmdbuf) { | ||
| 1374 | cmdbuf.SetCullModeEXT(enabled ? MaxwellToVK::CullFace(cull_face) : VK_CULL_MODE_NONE); | ||
| 1375 | }); | ||
| 1376 | } | ||
| 1377 | |||
| 1378 | void RasterizerVulkan::UpdateDepthBoundsTestEnable(Tegra::Engines::Maxwell3D::Regs& regs) { | ||
| 1379 | if (!state_tracker.TouchDepthBoundsTestEnable()) { | ||
| 1380 | return; | ||
| 1381 | } | ||
| 1382 | scheduler.Record([enable = regs.depth_bounds_enable](vk::CommandBuffer cmdbuf) { | ||
| 1383 | cmdbuf.SetDepthBoundsTestEnableEXT(enable); | ||
| 1384 | }); | ||
| 1385 | } | ||
| 1386 | |||
| 1387 | void RasterizerVulkan::UpdateDepthTestEnable(Tegra::Engines::Maxwell3D::Regs& regs) { | ||
| 1388 | if (!state_tracker.TouchDepthTestEnable()) { | ||
| 1389 | return; | ||
| 1390 | } | ||
| 1391 | scheduler.Record([enable = regs.depth_test_enable](vk::CommandBuffer cmdbuf) { | ||
| 1392 | cmdbuf.SetDepthTestEnableEXT(enable); | ||
| 1393 | }); | ||
| 1394 | } | ||
| 1395 | |||
| 1396 | void RasterizerVulkan::UpdateDepthWriteEnable(Tegra::Engines::Maxwell3D::Regs& regs) { | ||
| 1397 | if (!state_tracker.TouchDepthWriteEnable()) { | ||
| 1398 | return; | ||
| 1399 | } | ||
| 1400 | scheduler.Record([enable = regs.depth_write_enabled](vk::CommandBuffer cmdbuf) { | ||
| 1401 | cmdbuf.SetDepthWriteEnableEXT(enable); | ||
| 1402 | }); | ||
| 1403 | } | ||
| 1404 | |||
| 1405 | void RasterizerVulkan::UpdateDepthCompareOp(Tegra::Engines::Maxwell3D::Regs& regs) { | ||
| 1406 | if (!state_tracker.TouchDepthCompareOp()) { | ||
| 1407 | return; | ||
| 1408 | } | ||
| 1409 | scheduler.Record([func = regs.depth_test_func](vk::CommandBuffer cmdbuf) { | ||
| 1410 | cmdbuf.SetDepthCompareOpEXT(MaxwellToVK::ComparisonOp(func)); | ||
| 1411 | }); | ||
| 1412 | } | ||
| 1413 | |||
| 1414 | void RasterizerVulkan::UpdateFrontFace(Tegra::Engines::Maxwell3D::Regs& regs) { | ||
| 1415 | if (!state_tracker.TouchFrontFace()) { | ||
| 1416 | return; | ||
| 1417 | } | ||
| 1418 | |||
| 1419 | VkFrontFace front_face = MaxwellToVK::FrontFace(regs.front_face); | ||
| 1420 | if (regs.screen_y_control.triangle_rast_flip != 0) { | ||
| 1421 | front_face = front_face == VK_FRONT_FACE_CLOCKWISE ? VK_FRONT_FACE_COUNTER_CLOCKWISE | ||
| 1422 | : VK_FRONT_FACE_CLOCKWISE; | ||
| 1423 | } | ||
| 1424 | scheduler.Record( | ||
| 1425 | [front_face](vk::CommandBuffer cmdbuf) { cmdbuf.SetFrontFaceEXT(front_face); }); | ||
| 1426 | } | ||
| 1427 | |||
| 1428 | void RasterizerVulkan::UpdatePrimitiveTopology(Tegra::Engines::Maxwell3D::Regs& regs) { | ||
| 1429 | if (!state_tracker.TouchPrimitiveTopology()) { | ||
| 1430 | return; | ||
| 1431 | } | ||
| 1432 | const Maxwell::PrimitiveTopology primitive_topology = regs.draw.topology.Value(); | ||
| 1433 | scheduler.Record([this, primitive_topology](vk::CommandBuffer cmdbuf) { | ||
| 1434 | cmdbuf.SetPrimitiveTopologyEXT(MaxwellToVK::PrimitiveTopology(device, primitive_topology)); | ||
| 1435 | }); | ||
| 1436 | } | ||
| 1437 | |||
| 1438 | void RasterizerVulkan::UpdateStencilOp(Tegra::Engines::Maxwell3D::Regs& regs) { | ||
| 1439 | if (!state_tracker.TouchStencilOp()) { | ||
| 1440 | return; | ||
| 1441 | } | ||
| 1442 | const Maxwell::StencilOp fail = regs.stencil_front_op_fail; | ||
| 1443 | const Maxwell::StencilOp zfail = regs.stencil_front_op_zfail; | ||
| 1444 | const Maxwell::StencilOp zpass = regs.stencil_front_op_zpass; | ||
| 1445 | const Maxwell::ComparisonOp compare = regs.stencil_front_func_func; | ||
| 1446 | if (regs.stencil_two_side_enable) { | ||
| 1447 | scheduler.Record([fail, zfail, zpass, compare](vk::CommandBuffer cmdbuf) { | ||
| 1448 | cmdbuf.SetStencilOpEXT(VK_STENCIL_FACE_FRONT_AND_BACK, MaxwellToVK::StencilOp(fail), | ||
| 1449 | MaxwellToVK::StencilOp(zpass), MaxwellToVK::StencilOp(zfail), | ||
| 1450 | MaxwellToVK::ComparisonOp(compare)); | ||
| 1451 | }); | ||
| 1452 | } else { | ||
| 1453 | const Maxwell::StencilOp back_fail = regs.stencil_back_op_fail; | ||
| 1454 | const Maxwell::StencilOp back_zfail = regs.stencil_back_op_zfail; | ||
| 1455 | const Maxwell::StencilOp back_zpass = regs.stencil_back_op_zpass; | ||
| 1456 | const Maxwell::ComparisonOp back_compare = regs.stencil_back_func_func; | ||
| 1457 | scheduler.Record([fail, zfail, zpass, compare, back_fail, back_zfail, back_zpass, | ||
| 1458 | back_compare](vk::CommandBuffer cmdbuf) { | ||
| 1459 | cmdbuf.SetStencilOpEXT(VK_STENCIL_FACE_FRONT_BIT, MaxwellToVK::StencilOp(fail), | ||
| 1460 | MaxwellToVK::StencilOp(zpass), MaxwellToVK::StencilOp(zfail), | ||
| 1461 | MaxwellToVK::ComparisonOp(compare)); | ||
| 1462 | cmdbuf.SetStencilOpEXT(VK_STENCIL_FACE_BACK_BIT, MaxwellToVK::StencilOp(back_fail), | ||
| 1463 | MaxwellToVK::StencilOp(back_zpass), | ||
| 1464 | MaxwellToVK::StencilOp(back_zfail), | ||
| 1465 | MaxwellToVK::ComparisonOp(back_compare)); | ||
| 1466 | }); | ||
| 1467 | } | ||
| 1468 | } | ||
| 1469 | |||
| 1470 | void RasterizerVulkan::UpdateStencilTestEnable(Tegra::Engines::Maxwell3D::Regs& regs) { | ||
| 1471 | if (!state_tracker.TouchStencilTestEnable()) { | ||
| 1472 | return; | ||
| 1473 | } | ||
| 1474 | scheduler.Record([enable = regs.stencil_enable](vk::CommandBuffer cmdbuf) { | ||
| 1475 | cmdbuf.SetStencilTestEnableEXT(enable); | ||
| 1476 | }); | ||
| 1477 | } | ||
| 1478 | |||
| 1329 | std::size_t RasterizerVulkan::CalculateGraphicsStreamBufferSize(bool is_indexed) const { | 1479 | std::size_t RasterizerVulkan::CalculateGraphicsStreamBufferSize(bool is_indexed) const { |
| 1330 | std::size_t size = CalculateVertexArraysSize(); | 1480 | std::size_t size = CalculateVertexArraysSize(); |
| 1331 | if (is_indexed) { | 1481 | if (is_indexed) { |
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index 83e00e7e9..923178b0b 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h | |||
| @@ -185,8 +185,7 @@ private: | |||
| 185 | 185 | ||
| 186 | bool WalkAttachmentOverlaps(const CachedSurfaceView& attachment); | 186 | bool WalkAttachmentOverlaps(const CachedSurfaceView& attachment); |
| 187 | 187 | ||
| 188 | void SetupVertexArrays(FixedPipelineState::VertexInput& vertex_input, | 188 | void SetupVertexArrays(BufferBindings& buffer_bindings); |
| 189 | BufferBindings& buffer_bindings); | ||
| 190 | 189 | ||
| 191 | void SetupIndexBuffer(BufferBindings& buffer_bindings, DrawParameters& params, bool is_indexed); | 190 | void SetupIndexBuffer(BufferBindings& buffer_bindings, DrawParameters& params, bool is_indexed); |
| 192 | 191 | ||
| @@ -246,6 +245,16 @@ private: | |||
| 246 | void UpdateDepthBounds(Tegra::Engines::Maxwell3D::Regs& regs); | 245 | void UpdateDepthBounds(Tegra::Engines::Maxwell3D::Regs& regs); |
| 247 | void UpdateStencilFaces(Tegra::Engines::Maxwell3D::Regs& regs); | 246 | void UpdateStencilFaces(Tegra::Engines::Maxwell3D::Regs& regs); |
| 248 | 247 | ||
| 248 | void UpdateCullMode(Tegra::Engines::Maxwell3D::Regs& regs); | ||
| 249 | void UpdateDepthBoundsTestEnable(Tegra::Engines::Maxwell3D::Regs& regs); | ||
| 250 | void UpdateDepthTestEnable(Tegra::Engines::Maxwell3D::Regs& regs); | ||
| 251 | void UpdateDepthWriteEnable(Tegra::Engines::Maxwell3D::Regs& regs); | ||
| 252 | void UpdateDepthCompareOp(Tegra::Engines::Maxwell3D::Regs& regs); | ||
| 253 | void UpdateFrontFace(Tegra::Engines::Maxwell3D::Regs& regs); | ||
| 254 | void UpdatePrimitiveTopology(Tegra::Engines::Maxwell3D::Regs& regs); | ||
| 255 | void UpdateStencilOp(Tegra::Engines::Maxwell3D::Regs& regs); | ||
| 256 | void UpdateStencilTestEnable(Tegra::Engines::Maxwell3D::Regs& regs); | ||
| 257 | |||
| 249 | std::size_t CalculateGraphicsStreamBufferSize(bool is_indexed) const; | 258 | std::size_t CalculateGraphicsStreamBufferSize(bool is_indexed) const; |
| 250 | 259 | ||
| 251 | std::size_t CalculateComputeStreamBufferSize() const; | 260 | std::size_t CalculateComputeStreamBufferSize() const; |
diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.cpp b/src/video_core/renderer_vulkan/vk_state_tracker.cpp index 94a89e388..e5a583dd5 100644 --- a/src/video_core/renderer_vulkan/vk_state_tracker.cpp +++ b/src/video_core/renderer_vulkan/vk_state_tracker.cpp | |||
| @@ -36,6 +36,15 @@ Flags MakeInvalidationFlags() { | |||
| 36 | flags[BlendConstants] = true; | 36 | flags[BlendConstants] = true; |
| 37 | flags[DepthBounds] = true; | 37 | flags[DepthBounds] = true; |
| 38 | flags[StencilProperties] = true; | 38 | flags[StencilProperties] = true; |
| 39 | flags[CullMode] = true; | ||
| 40 | flags[DepthBoundsEnable] = true; | ||
| 41 | flags[DepthTestEnable] = true; | ||
| 42 | flags[DepthWriteEnable] = true; | ||
| 43 | flags[DepthCompareOp] = true; | ||
| 44 | flags[FrontFace] = true; | ||
| 45 | flags[PrimitiveTopology] = true; | ||
| 46 | flags[StencilOp] = true; | ||
| 47 | flags[StencilTestEnable] = true; | ||
| 39 | return flags; | 48 | return flags; |
| 40 | } | 49 | } |
| 41 | 50 | ||
| @@ -75,6 +84,57 @@ void SetupDirtyStencilProperties(Tables& tables) { | |||
| 75 | table[OFF(stencil_back_func_mask)] = StencilProperties; | 84 | table[OFF(stencil_back_func_mask)] = StencilProperties; |
| 76 | } | 85 | } |
| 77 | 86 | ||
| 87 | void SetupDirtyCullMode(Tables& tables) { | ||
| 88 | auto& table = tables[0]; | ||
| 89 | table[OFF(cull_face)] = CullMode; | ||
| 90 | table[OFF(cull_test_enabled)] = CullMode; | ||
| 91 | } | ||
| 92 | |||
| 93 | void SetupDirtyDepthBoundsEnable(Tables& tables) { | ||
| 94 | tables[0][OFF(depth_bounds_enable)] = DepthBoundsEnable; | ||
| 95 | } | ||
| 96 | |||
| 97 | void SetupDirtyDepthTestEnable(Tables& tables) { | ||
| 98 | tables[0][OFF(depth_test_enable)] = DepthTestEnable; | ||
| 99 | } | ||
| 100 | |||
| 101 | void SetupDirtyDepthWriteEnable(Tables& tables) { | ||
| 102 | tables[0][OFF(depth_write_enabled)] = DepthWriteEnable; | ||
| 103 | } | ||
| 104 | |||
| 105 | void SetupDirtyDepthCompareOp(Tables& tables) { | ||
| 106 | tables[0][OFF(depth_test_func)] = DepthCompareOp; | ||
| 107 | } | ||
| 108 | |||
| 109 | void SetupDirtyFrontFace(Tables& tables) { | ||
| 110 | auto& table = tables[0]; | ||
| 111 | table[OFF(front_face)] = FrontFace; | ||
| 112 | table[OFF(screen_y_control)] = FrontFace; | ||
| 113 | } | ||
| 114 | |||
| 115 | void SetupDirtyPrimitiveTopology(Tables& tables) { | ||
| 116 | tables[0][OFF(draw.topology)] = PrimitiveTopology; | ||
| 117 | } | ||
| 118 | |||
| 119 | void SetupDirtyStencilOp(Tables& tables) { | ||
| 120 | auto& table = tables[0]; | ||
| 121 | table[OFF(stencil_front_op_fail)] = StencilOp; | ||
| 122 | table[OFF(stencil_front_op_zfail)] = StencilOp; | ||
| 123 | table[OFF(stencil_front_op_zpass)] = StencilOp; | ||
| 124 | table[OFF(stencil_front_func_func)] = StencilOp; | ||
| 125 | table[OFF(stencil_back_op_fail)] = StencilOp; | ||
| 126 | table[OFF(stencil_back_op_zfail)] = StencilOp; | ||
| 127 | table[OFF(stencil_back_op_zpass)] = StencilOp; | ||
| 128 | table[OFF(stencil_back_func_func)] = StencilOp; | ||
| 129 | |||
| 130 | // Table 0 is used by StencilProperties | ||
| 131 | tables[1][OFF(stencil_two_side_enable)] = StencilOp; | ||
| 132 | } | ||
| 133 | |||
| 134 | void SetupDirtyStencilTestEnable(Tables& tables) { | ||
| 135 | tables[0][OFF(stencil_enable)] = StencilTestEnable; | ||
| 136 | } | ||
| 137 | |||
| 78 | } // Anonymous namespace | 138 | } // Anonymous namespace |
| 79 | 139 | ||
| 80 | StateTracker::StateTracker(Core::System& system) | 140 | StateTracker::StateTracker(Core::System& system) |
| @@ -90,6 +150,14 @@ void StateTracker::Initialize() { | |||
| 90 | SetupDirtyBlendConstants(tables); | 150 | SetupDirtyBlendConstants(tables); |
| 91 | SetupDirtyDepthBounds(tables); | 151 | SetupDirtyDepthBounds(tables); |
| 92 | SetupDirtyStencilProperties(tables); | 152 | SetupDirtyStencilProperties(tables); |
| 153 | SetupDirtyCullMode(tables); | ||
| 154 | SetupDirtyDepthBoundsEnable(tables); | ||
| 155 | SetupDirtyDepthTestEnable(tables); | ||
| 156 | SetupDirtyDepthWriteEnable(tables); | ||
| 157 | SetupDirtyDepthCompareOp(tables); | ||
| 158 | SetupDirtyFrontFace(tables); | ||
| 159 | SetupDirtyPrimitiveTopology(tables); | ||
| 160 | SetupDirtyStencilOp(tables); | ||
| 93 | } | 161 | } |
| 94 | 162 | ||
| 95 | void StateTracker::InvalidateCommandBufferState() { | 163 | void StateTracker::InvalidateCommandBufferState() { |
diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.h b/src/video_core/renderer_vulkan/vk_state_tracker.h index 03bc415b2..54ca0d6c6 100644 --- a/src/video_core/renderer_vulkan/vk_state_tracker.h +++ b/src/video_core/renderer_vulkan/vk_state_tracker.h | |||
| @@ -26,6 +26,16 @@ enum : u8 { | |||
| 26 | DepthBounds, | 26 | DepthBounds, |
| 27 | StencilProperties, | 27 | StencilProperties, |
| 28 | 28 | ||
| 29 | CullMode, | ||
| 30 | DepthBoundsEnable, | ||
| 31 | DepthTestEnable, | ||
| 32 | DepthWriteEnable, | ||
| 33 | DepthCompareOp, | ||
| 34 | FrontFace, | ||
| 35 | PrimitiveTopology, | ||
| 36 | StencilOp, | ||
| 37 | StencilTestEnable, | ||
| 38 | |||
| 29 | Last | 39 | Last |
| 30 | }; | 40 | }; |
| 31 | static_assert(Last <= std::numeric_limits<u8>::max()); | 41 | static_assert(Last <= std::numeric_limits<u8>::max()); |
| @@ -64,6 +74,46 @@ public: | |||
| 64 | return Exchange(Dirty::StencilProperties, false); | 74 | return Exchange(Dirty::StencilProperties, false); |
| 65 | } | 75 | } |
| 66 | 76 | ||
| 77 | bool TouchCullMode() { | ||
| 78 | return Exchange(Dirty::CullMode, false); | ||
| 79 | } | ||
| 80 | |||
| 81 | bool TouchDepthBoundsTestEnable() { | ||
| 82 | return Exchange(Dirty::DepthBoundsEnable, false); | ||
| 83 | } | ||
| 84 | |||
| 85 | bool TouchDepthTestEnable() { | ||
| 86 | return Exchange(Dirty::DepthTestEnable, false); | ||
| 87 | } | ||
| 88 | |||
| 89 | bool TouchDepthBoundsEnable() { | ||
| 90 | return Exchange(Dirty::DepthBoundsEnable, false); | ||
| 91 | } | ||
| 92 | |||
| 93 | bool TouchDepthWriteEnable() { | ||
| 94 | return Exchange(Dirty::DepthWriteEnable, false); | ||
| 95 | } | ||
| 96 | |||
| 97 | bool TouchDepthCompareOp() { | ||
| 98 | return Exchange(Dirty::DepthCompareOp, false); | ||
| 99 | } | ||
| 100 | |||
| 101 | bool TouchFrontFace() { | ||
| 102 | return Exchange(Dirty::FrontFace, false); | ||
| 103 | } | ||
| 104 | |||
| 105 | bool TouchPrimitiveTopology() { | ||
| 106 | return Exchange(Dirty::PrimitiveTopology, false); | ||
| 107 | } | ||
| 108 | |||
| 109 | bool TouchStencilOp() { | ||
| 110 | return Exchange(Dirty::StencilOp, false); | ||
| 111 | } | ||
| 112 | |||
| 113 | bool TouchStencilTestEnable() { | ||
| 114 | return Exchange(Dirty::StencilTestEnable, false); | ||
| 115 | } | ||
| 116 | |||
| 67 | private: | 117 | private: |
| 68 | bool Exchange(std::size_t id, bool new_value) const noexcept { | 118 | bool Exchange(std::size_t id, bool new_value) const noexcept { |
| 69 | auto& flags = system.GPU().Maxwell3D().dirty.flags; | 119 | auto& flags = system.GPU().Maxwell3D().dirty.flags; |
diff --git a/src/video_core/renderer_vulkan/wrapper.cpp b/src/video_core/renderer_vulkan/wrapper.cpp index 0d485a662..051298cc8 100644 --- a/src/video_core/renderer_vulkan/wrapper.cpp +++ b/src/video_core/renderer_vulkan/wrapper.cpp | |||
| @@ -88,6 +88,16 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept { | |||
| 88 | X(vkCmdSetStencilWriteMask); | 88 | X(vkCmdSetStencilWriteMask); |
| 89 | X(vkCmdSetViewport); | 89 | X(vkCmdSetViewport); |
| 90 | X(vkCmdWaitEvents); | 90 | X(vkCmdWaitEvents); |
| 91 | X(vkCmdBindVertexBuffers2EXT); | ||
| 92 | X(vkCmdSetCullModeEXT); | ||
| 93 | X(vkCmdSetDepthBoundsTestEnableEXT); | ||
| 94 | X(vkCmdSetDepthCompareOpEXT); | ||
| 95 | X(vkCmdSetDepthTestEnableEXT); | ||
| 96 | X(vkCmdSetDepthWriteEnableEXT); | ||
| 97 | X(vkCmdSetFrontFaceEXT); | ||
| 98 | X(vkCmdSetPrimitiveTopologyEXT); | ||
| 99 | X(vkCmdSetStencilOpEXT); | ||
| 100 | X(vkCmdSetStencilTestEnableEXT); | ||
| 91 | X(vkCreateBuffer); | 101 | X(vkCreateBuffer); |
| 92 | X(vkCreateBufferView); | 102 | X(vkCreateBufferView); |
| 93 | X(vkCreateCommandPool); | 103 | X(vkCreateCommandPool); |
diff --git a/src/video_core/renderer_vulkan/wrapper.h b/src/video_core/renderer_vulkan/wrapper.h index d56fdb3f9..71daac9d7 100644 --- a/src/video_core/renderer_vulkan/wrapper.h +++ b/src/video_core/renderer_vulkan/wrapper.h | |||
| @@ -207,6 +207,16 @@ struct DeviceDispatch : public InstanceDispatch { | |||
| 207 | PFN_vkCmdSetStencilWriteMask vkCmdSetStencilWriteMask; | 207 | PFN_vkCmdSetStencilWriteMask vkCmdSetStencilWriteMask; |
| 208 | PFN_vkCmdSetViewport vkCmdSetViewport; | 208 | PFN_vkCmdSetViewport vkCmdSetViewport; |
| 209 | PFN_vkCmdWaitEvents vkCmdWaitEvents; | 209 | PFN_vkCmdWaitEvents vkCmdWaitEvents; |
| 210 | PFN_vkCmdBindVertexBuffers2EXT vkCmdBindVertexBuffers2EXT; | ||
| 211 | PFN_vkCmdSetCullModeEXT vkCmdSetCullModeEXT; | ||
| 212 | PFN_vkCmdSetDepthBoundsTestEnableEXT vkCmdSetDepthBoundsTestEnableEXT; | ||
| 213 | PFN_vkCmdSetDepthCompareOpEXT vkCmdSetDepthCompareOpEXT; | ||
| 214 | PFN_vkCmdSetDepthTestEnableEXT vkCmdSetDepthTestEnableEXT; | ||
| 215 | PFN_vkCmdSetDepthWriteEnableEXT vkCmdSetDepthWriteEnableEXT; | ||
| 216 | PFN_vkCmdSetFrontFaceEXT vkCmdSetFrontFaceEXT; | ||
| 217 | PFN_vkCmdSetPrimitiveTopologyEXT vkCmdSetPrimitiveTopologyEXT; | ||
| 218 | PFN_vkCmdSetStencilOpEXT vkCmdSetStencilOpEXT; | ||
| 219 | PFN_vkCmdSetStencilTestEnableEXT vkCmdSetStencilTestEnableEXT; | ||
| 210 | PFN_vkCreateBuffer vkCreateBuffer; | 220 | PFN_vkCreateBuffer vkCreateBuffer; |
| 211 | PFN_vkCreateBufferView vkCreateBufferView; | 221 | PFN_vkCreateBufferView vkCreateBufferView; |
| 212 | PFN_vkCreateCommandPool vkCreateCommandPool; | 222 | PFN_vkCreateCommandPool vkCreateCommandPool; |
| @@ -969,6 +979,50 @@ public: | |||
| 969 | buffer_barriers.data(), image_barriers.size(), image_barriers.data()); | 979 | buffer_barriers.data(), image_barriers.size(), image_barriers.data()); |
| 970 | } | 980 | } |
| 971 | 981 | ||
| 982 | void BindVertexBuffers2EXT(u32 first_binding, u32 binding_count, const VkBuffer* buffers, | ||
| 983 | const VkDeviceSize* offsets, const VkDeviceSize* sizes, | ||
| 984 | const VkDeviceSize* strides) const noexcept { | ||
| 985 | dld->vkCmdBindVertexBuffers2EXT(handle, first_binding, binding_count, buffers, offsets, | ||
| 986 | sizes, strides); | ||
| 987 | } | ||
| 988 | |||
| 989 | void SetCullModeEXT(VkCullModeFlags cull_mode) const noexcept { | ||
| 990 | dld->vkCmdSetCullModeEXT(handle, cull_mode); | ||
| 991 | } | ||
| 992 | |||
| 993 | void SetDepthBoundsTestEnableEXT(bool enable) const noexcept { | ||
| 994 | dld->vkCmdSetDepthBoundsTestEnableEXT(handle, enable ? VK_TRUE : VK_FALSE); | ||
| 995 | } | ||
| 996 | |||
| 997 | void SetDepthCompareOpEXT(VkCompareOp compare_op) const noexcept { | ||
| 998 | dld->vkCmdSetDepthCompareOpEXT(handle, compare_op); | ||
| 999 | } | ||
| 1000 | |||
| 1001 | void SetDepthTestEnableEXT(bool enable) const noexcept { | ||
| 1002 | dld->vkCmdSetDepthTestEnableEXT(handle, enable ? VK_TRUE : VK_FALSE); | ||
| 1003 | } | ||
| 1004 | |||
| 1005 | void SetDepthWriteEnableEXT(bool enable) const noexcept { | ||
| 1006 | dld->vkCmdSetDepthWriteEnableEXT(handle, enable ? VK_TRUE : VK_FALSE); | ||
| 1007 | } | ||
| 1008 | |||
| 1009 | void SetFrontFaceEXT(VkFrontFace front_face) const noexcept { | ||
| 1010 | dld->vkCmdSetFrontFaceEXT(handle, front_face); | ||
| 1011 | } | ||
| 1012 | |||
| 1013 | void SetPrimitiveTopologyEXT(VkPrimitiveTopology primitive_topology) const noexcept { | ||
| 1014 | dld->vkCmdSetPrimitiveTopologyEXT(handle, primitive_topology); | ||
| 1015 | } | ||
| 1016 | |||
| 1017 | void SetStencilOpEXT(VkStencilFaceFlags face_mask, VkStencilOp fail_op, VkStencilOp pass_op, | ||
| 1018 | VkStencilOp depth_fail_op, VkCompareOp compare_op) const noexcept { | ||
| 1019 | dld->vkCmdSetStencilOpEXT(handle, face_mask, fail_op, pass_op, depth_fail_op, compare_op); | ||
| 1020 | } | ||
| 1021 | |||
| 1022 | void SetStencilTestEnableEXT(bool enable) const noexcept { | ||
| 1023 | dld->vkCmdSetStencilTestEnableEXT(handle, enable ? VK_TRUE : VK_FALSE); | ||
| 1024 | } | ||
| 1025 | |||
| 972 | void BindTransformFeedbackBuffersEXT(u32 first, u32 count, const VkBuffer* buffers, | 1026 | void BindTransformFeedbackBuffersEXT(u32 first, u32 count, const VkBuffer* buffers, |
| 973 | const VkDeviceSize* offsets, | 1027 | const VkDeviceSize* offsets, |
| 974 | const VkDeviceSize* sizes) const noexcept { | 1028 | const VkDeviceSize* sizes) const noexcept { |
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index bbbd96113..5e0d0e7af 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp | |||
| @@ -212,7 +212,7 @@ const std::array<int, Settings::NativeKeyboard::NumKeyboardMods> Config::default | |||
| 212 | // UISetting::values.shortcuts, which is alphabetically ordered. | 212 | // UISetting::values.shortcuts, which is alphabetically ordered. |
| 213 | // clang-format off | 213 | // clang-format off |
| 214 | const std::array<UISettings::Shortcut, 16> Config::default_hotkeys{{ | 214 | const std::array<UISettings::Shortcut, 16> Config::default_hotkeys{{ |
| 215 | {QStringLiteral("Capture Screenshot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+P"), Qt::ApplicationShortcut}}, | 215 | {QStringLiteral("Capture Screenshot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+P"), Qt::WidgetWithChildrenShortcut}}, |
| 216 | {QStringLiteral("Change Docked Mode"), QStringLiteral("Main Window"), {QStringLiteral("F10"), Qt::ApplicationShortcut}}, | 216 | {QStringLiteral("Change Docked Mode"), QStringLiteral("Main Window"), {QStringLiteral("F10"), Qt::ApplicationShortcut}}, |
| 217 | {QStringLiteral("Continue/Pause Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F4"), Qt::WindowShortcut}}, | 217 | {QStringLiteral("Continue/Pause Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F4"), Qt::WindowShortcut}}, |
| 218 | {QStringLiteral("Decrease Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("-"), Qt::ApplicationShortcut}}, | 218 | {QStringLiteral("Decrease Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("-"), Qt::ApplicationShortcut}}, |
| @@ -220,8 +220,8 @@ const std::array<UISettings::Shortcut, 16> Config::default_hotkeys{{ | |||
| 220 | {QStringLiteral("Exit yuzu"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Q"), Qt::WindowShortcut}}, | 220 | {QStringLiteral("Exit yuzu"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Q"), Qt::WindowShortcut}}, |
| 221 | {QStringLiteral("Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("F11"), Qt::WindowShortcut}}, | 221 | {QStringLiteral("Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("F11"), Qt::WindowShortcut}}, |
| 222 | {QStringLiteral("Increase Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("+"), Qt::ApplicationShortcut}}, | 222 | {QStringLiteral("Increase Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("+"), Qt::ApplicationShortcut}}, |
| 223 | {QStringLiteral("Load Amiibo"), QStringLiteral("Main Window"), {QStringLiteral("F2"), Qt::ApplicationShortcut}}, | 223 | {QStringLiteral("Load Amiibo"), QStringLiteral("Main Window"), {QStringLiteral("F2"), Qt::WidgetWithChildrenShortcut}}, |
| 224 | {QStringLiteral("Load File"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+O"), Qt::WindowShortcut}}, | 224 | {QStringLiteral("Load File"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+O"), Qt::WidgetWithChildrenShortcut}}, |
| 225 | {QStringLiteral("Mute Audio"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+M"), Qt::WindowShortcut}}, | 225 | {QStringLiteral("Mute Audio"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+M"), Qt::WindowShortcut}}, |
| 226 | {QStringLiteral("Restart Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F6"), Qt::WindowShortcut}}, | 226 | {QStringLiteral("Restart Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F6"), Qt::WindowShortcut}}, |
| 227 | {QStringLiteral("Stop Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F5"), Qt::WindowShortcut}}, | 227 | {QStringLiteral("Stop Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F5"), Qt::WindowShortcut}}, |
| @@ -665,11 +665,13 @@ void Config::ReadShortcutValues() { | |||
| 665 | const auto& [keyseq, context] = shortcut; | 665 | const auto& [keyseq, context] = shortcut; |
| 666 | qt_config->beginGroup(group); | 666 | qt_config->beginGroup(group); |
| 667 | qt_config->beginGroup(name); | 667 | qt_config->beginGroup(name); |
| 668 | // No longer using ReadSetting for shortcut.second as it innacurately returns a value of 1 | ||
| 669 | // for WidgetWithChildrenShortcut which is a value of 3. Needed to fix shortcuts the open | ||
| 670 | // a file dialog in windowed mode | ||
| 668 | UISettings::values.shortcuts.push_back( | 671 | UISettings::values.shortcuts.push_back( |
| 669 | {name, | 672 | {name, |
| 670 | group, | 673 | group, |
| 671 | {ReadSetting(QStringLiteral("KeySeq"), keyseq).toString(), | 674 | {ReadSetting(QStringLiteral("KeySeq"), keyseq).toString(), shortcut.second}}); |
| 672 | ReadSetting(QStringLiteral("Context"), context).toInt()}}); | ||
| 673 | qt_config->endGroup(); | 675 | qt_config->endGroup(); |
| 674 | qt_config->endGroup(); | 676 | qt_config->endGroup(); |
| 675 | } | 677 | } |
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index fb299a39b..9844e4764 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp | |||
| @@ -753,7 +753,7 @@ void GMainWindow::InitializeHotkeys() { | |||
| 753 | }); | 753 | }); |
| 754 | connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Capture Screenshot"), this), | 754 | connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Capture Screenshot"), this), |
| 755 | &QShortcut::activated, this, [&] { | 755 | &QShortcut::activated, this, [&] { |
| 756 | if (emu_thread->IsRunning()) { | 756 | if (emu_thread != nullptr && emu_thread->IsRunning()) { |
| 757 | OnCaptureScreenshot(); | 757 | OnCaptureScreenshot(); |
| 758 | } | 758 | } |
| 759 | }); | 759 | }); |