diff options
36 files changed, 613 insertions, 224 deletions
diff --git a/externals/dynarmic b/externals/dynarmic | |||
| Subproject a1cbea7948372989218a4e6159a95998d65876a | Subproject befe547d5631024a70d81d2ccee808bbfcb3854 | ||
diff --git a/src/audio_core/renderer/command/resample/upsample.cpp b/src/audio_core/renderer/command/resample/upsample.cpp index 6c3ff31f7..5f7db12ca 100644 --- a/src/audio_core/renderer/command/resample/upsample.cpp +++ b/src/audio_core/renderer/command/resample/upsample.cpp | |||
| @@ -20,25 +20,25 @@ static void SrcProcessFrame(std::span<s32> output, std::span<const s32> input, | |||
| 20 | const u32 target_sample_count, const u32 source_sample_count, | 20 | const u32 target_sample_count, const u32 source_sample_count, |
| 21 | UpsamplerState* state) { | 21 | UpsamplerState* state) { |
| 22 | constexpr u32 WindowSize = 10; | 22 | constexpr u32 WindowSize = 10; |
| 23 | constexpr std::array<Common::FixedPoint<24, 8>, WindowSize> SincWindow1{ | 23 | constexpr std::array<Common::FixedPoint<17, 15>, WindowSize> WindowedSinc1{ |
| 24 | 51.93359375f, -18.80078125f, 9.73046875f, -5.33203125f, 2.84375f, | 24 | 0.95376587f, -0.12872314f, 0.060028076f, -0.032470703f, 0.017669678f, |
| 25 | -1.41015625f, 0.62109375f, -0.2265625f, 0.0625f, -0.00390625f, | 25 | -0.009124756f, 0.004272461f, -0.001739502f, 0.000579834f, -0.000091552734f, |
| 26 | }; | 26 | }; |
| 27 | constexpr std::array<Common::FixedPoint<24, 8>, WindowSize> SincWindow2{ | 27 | constexpr std::array<Common::FixedPoint<17, 15>, WindowSize> WindowedSinc2{ |
| 28 | 105.35546875f, -24.52734375f, 11.9609375f, -6.515625f, 3.52734375f, | 28 | 0.8230896f, -0.19161987f, 0.093444824f, -0.05090332f, 0.027557373f, |
| 29 | -1.796875f, 0.828125f, -0.32421875f, 0.1015625f, -0.015625f, | 29 | -0.014038086f, 0.0064697266f, -0.002532959f, 0.00079345703f, -0.00012207031f, |
| 30 | }; | 30 | }; |
| 31 | constexpr std::array<Common::FixedPoint<24, 8>, WindowSize> SincWindow3{ | 31 | constexpr std::array<Common::FixedPoint<17, 15>, WindowSize> WindowedSinc3{ |
| 32 | 122.08203125f, -16.47656250f, 7.68359375f, -4.15625000f, 2.26171875f, | 32 | 0.6298828f, -0.19274902f, 0.09725952f, -0.05319214f, 0.028625488f, |
| 33 | -1.16796875f, 0.54687500f, -0.22265625f, 0.07421875f, -0.01171875f, | 33 | -0.014373779f, 0.006500244f, -0.0024719238f, 0.0007324219f, -0.000091552734f, |
| 34 | }; | 34 | }; |
| 35 | constexpr std::array<Common::FixedPoint<24, 8>, WindowSize> SincWindow4{ | 35 | constexpr std::array<Common::FixedPoint<17, 15>, WindowSize> WindowedSinc4{ |
| 36 | 23.73437500f, -9.62109375f, 5.07812500f, -2.78125000f, 1.46875000f, | 36 | 0.4057312f, -0.1468811f, 0.07601929f, -0.041656494f, 0.022216797f, |
| 37 | -0.71484375f, 0.30859375f, -0.10546875f, 0.02734375f, 0.00000000f, | 37 | -0.011016846f, 0.004852295f, -0.0017700195f, 0.00048828125f, -0.000030517578f, |
| 38 | }; | 38 | }; |
| 39 | constexpr std::array<Common::FixedPoint<24, 8>, WindowSize> SincWindow5{ | 39 | constexpr std::array<Common::FixedPoint<17, 15>, WindowSize> WindowedSinc5{ |
| 40 | 80.62500000f, -24.67187500f, 12.44921875f, -6.80859375f, 3.66406250f, | 40 | 0.1854248f, -0.075164795f, 0.03967285f, -0.021728516f, 0.011474609f, |
| 41 | -1.83984375f, 0.83203125f, -0.31640625f, 0.09375000f, -0.01171875f, | 41 | -0.005584717f, 0.0024108887f, -0.0008239746f, 0.00021362305f, 0.0f, |
| 42 | }; | 42 | }; |
| 43 | 43 | ||
| 44 | if (!state->initialized) { | 44 | if (!state->initialized) { |
| @@ -91,52 +91,31 @@ static void SrcProcessFrame(std::span<s32> output, std::span<const s32> input, | |||
| 91 | static_cast<u16>((state->history_output_index + 1) % UpsamplerState::HistorySize); | 91 | static_cast<u16>((state->history_output_index + 1) % UpsamplerState::HistorySize); |
| 92 | }; | 92 | }; |
| 93 | 93 | ||
| 94 | auto calculate_sample = [&state](std::span<const Common::FixedPoint<24, 8>> coeffs1, | 94 | auto calculate_sample = [&state](std::span<const Common::FixedPoint<17, 15>> coeffs1, |
| 95 | std::span<const Common::FixedPoint<24, 8>> coeffs2) -> s32 { | 95 | std::span<const Common::FixedPoint<17, 15>> coeffs2) -> s32 { |
| 96 | auto output_index{state->history_output_index}; | 96 | auto output_index{state->history_output_index}; |
| 97 | auto start_pos{output_index - state->history_start_index + 1U}; | 97 | u64 result{0}; |
| 98 | auto end_pos{10U}; | ||
| 99 | 98 | ||
| 100 | if (start_pos < 10) { | 99 | for (u32 coeff_index = 0; coeff_index < 10; coeff_index++) { |
| 101 | end_pos = start_pos; | 100 | result += static_cast<u64>(state->history[output_index].to_raw()) * |
| 102 | } | 101 | coeffs1[coeff_index].to_raw(); |
| 103 | |||
| 104 | u64 prev_contrib{0}; | ||
| 105 | u32 coeff_index{0}; | ||
| 106 | for (; coeff_index < end_pos; coeff_index++, output_index--) { | ||
| 107 | prev_contrib += static_cast<u64>(state->history[output_index].to_raw()) * | ||
| 108 | coeffs1[coeff_index].to_raw(); | ||
| 109 | } | ||
| 110 | 102 | ||
| 111 | auto end_index{state->history_end_index}; | 103 | output_index = output_index == state->history_start_index ? state->history_end_index |
| 112 | for (; start_pos < 9; start_pos++, coeff_index++, end_index--) { | 104 | : output_index - 1; |
| 113 | prev_contrib += static_cast<u64>(state->history[end_index].to_raw()) * | ||
| 114 | coeffs1[coeff_index].to_raw(); | ||
| 115 | } | 105 | } |
| 116 | 106 | ||
| 117 | output_index = | 107 | output_index = |
| 118 | static_cast<u16>((state->history_output_index + 1) % UpsamplerState::HistorySize); | 108 | static_cast<u16>((state->history_output_index + 1) % UpsamplerState::HistorySize); |
| 119 | start_pos = state->history_end_index - output_index + 1U; | ||
| 120 | end_pos = 10U; | ||
| 121 | 109 | ||
| 122 | if (start_pos < 10) { | 110 | for (u32 coeff_index = 0; coeff_index < 10; coeff_index++) { |
| 123 | end_pos = start_pos; | 111 | result += static_cast<u64>(state->history[output_index].to_raw()) * |
| 124 | } | 112 | coeffs2[coeff_index].to_raw(); |
| 125 | |||
| 126 | u64 next_contrib{0}; | ||
| 127 | coeff_index = 0; | ||
| 128 | for (; coeff_index < end_pos; coeff_index++, output_index++) { | ||
| 129 | next_contrib += static_cast<u64>(state->history[output_index].to_raw()) * | ||
| 130 | coeffs2[coeff_index].to_raw(); | ||
| 131 | } | ||
| 132 | 113 | ||
| 133 | auto start_index{state->history_start_index}; | 114 | output_index = output_index == state->history_end_index ? state->history_start_index |
| 134 | for (; start_pos < 9; start_pos++, start_index++, coeff_index++) { | 115 | : output_index + 1; |
| 135 | next_contrib += static_cast<u64>(state->history[start_index].to_raw()) * | ||
| 136 | coeffs2[coeff_index].to_raw(); | ||
| 137 | } | 116 | } |
| 138 | 117 | ||
| 139 | return static_cast<s32>(((prev_contrib >> 15) + (next_contrib >> 15)) >> 8); | 118 | return static_cast<s32>(result >> (8 + 15)); |
| 140 | }; | 119 | }; |
| 141 | 120 | ||
| 142 | switch (state->ratio.to_int_floor()) { | 121 | switch (state->ratio.to_int_floor()) { |
| @@ -150,23 +129,23 @@ static void SrcProcessFrame(std::span<s32> output, std::span<const s32> input, | |||
| 150 | break; | 129 | break; |
| 151 | 130 | ||
| 152 | case 1: | 131 | case 1: |
| 153 | output[write_index] = calculate_sample(SincWindow3, SincWindow4); | 132 | output[write_index] = calculate_sample(WindowedSinc1, WindowedSinc5); |
| 154 | break; | 133 | break; |
| 155 | 134 | ||
| 156 | case 2: | 135 | case 2: |
| 157 | output[write_index] = calculate_sample(SincWindow2, SincWindow1); | 136 | output[write_index] = calculate_sample(WindowedSinc2, WindowedSinc4); |
| 158 | break; | 137 | break; |
| 159 | 138 | ||
| 160 | case 3: | 139 | case 3: |
| 161 | output[write_index] = calculate_sample(SincWindow5, SincWindow5); | 140 | output[write_index] = calculate_sample(WindowedSinc3, WindowedSinc3); |
| 162 | break; | 141 | break; |
| 163 | 142 | ||
| 164 | case 4: | 143 | case 4: |
| 165 | output[write_index] = calculate_sample(SincWindow1, SincWindow2); | 144 | output[write_index] = calculate_sample(WindowedSinc4, WindowedSinc2); |
| 166 | break; | 145 | break; |
| 167 | 146 | ||
| 168 | case 5: | 147 | case 5: |
| 169 | output[write_index] = calculate_sample(SincWindow4, SincWindow3); | 148 | output[write_index] = calculate_sample(WindowedSinc5, WindowedSinc1); |
| 170 | break; | 149 | break; |
| 171 | } | 150 | } |
| 172 | state->sample_index = static_cast<u8>((state->sample_index + 1) % 6); | 151 | state->sample_index = static_cast<u8>((state->sample_index + 1) % 6); |
| @@ -183,11 +162,11 @@ static void SrcProcessFrame(std::span<s32> output, std::span<const s32> input, | |||
| 183 | break; | 162 | break; |
| 184 | 163 | ||
| 185 | case 1: | 164 | case 1: |
| 186 | output[write_index] = calculate_sample(SincWindow2, SincWindow1); | 165 | output[write_index] = calculate_sample(WindowedSinc2, WindowedSinc4); |
| 187 | break; | 166 | break; |
| 188 | 167 | ||
| 189 | case 2: | 168 | case 2: |
| 190 | output[write_index] = calculate_sample(SincWindow1, SincWindow2); | 169 | output[write_index] = calculate_sample(WindowedSinc4, WindowedSinc2); |
| 191 | break; | 170 | break; |
| 192 | } | 171 | } |
| 193 | state->sample_index = static_cast<u8>((state->sample_index + 1) % 3); | 172 | state->sample_index = static_cast<u8>((state->sample_index + 1) % 3); |
| @@ -204,12 +183,12 @@ static void SrcProcessFrame(std::span<s32> output, std::span<const s32> input, | |||
| 204 | break; | 183 | break; |
| 205 | 184 | ||
| 206 | case 1: | 185 | case 1: |
| 207 | output[write_index] = calculate_sample(SincWindow1, SincWindow2); | 186 | output[write_index] = calculate_sample(WindowedSinc4, WindowedSinc2); |
| 208 | break; | 187 | break; |
| 209 | 188 | ||
| 210 | case 2: | 189 | case 2: |
| 211 | increment(); | 190 | increment(); |
| 212 | output[write_index] = calculate_sample(SincWindow2, SincWindow1); | 191 | output[write_index] = calculate_sample(WindowedSinc2, WindowedSinc4); |
| 213 | break; | 192 | break; |
| 214 | } | 193 | } |
| 215 | state->sample_index = static_cast<u8>((state->sample_index + 1) % 3); | 194 | state->sample_index = static_cast<u8>((state->sample_index + 1) % 3); |
diff --git a/src/common/settings.cpp b/src/common/settings.cpp index 1638b79f5..b1a2aa8b2 100644 --- a/src/common/settings.cpp +++ b/src/common/settings.cpp | |||
| @@ -129,6 +129,10 @@ void UpdateRescalingInfo() { | |||
| 129 | info.up_scale = 1; | 129 | info.up_scale = 1; |
| 130 | info.down_shift = 0; | 130 | info.down_shift = 0; |
| 131 | break; | 131 | break; |
| 132 | case ResolutionSetup::Res3_2X: | ||
| 133 | info.up_scale = 3; | ||
| 134 | info.down_shift = 1; | ||
| 135 | break; | ||
| 132 | case ResolutionSetup::Res2X: | 136 | case ResolutionSetup::Res2X: |
| 133 | info.up_scale = 2; | 137 | info.up_scale = 2; |
| 134 | info.down_shift = 0; | 138 | info.down_shift = 0; |
| @@ -149,6 +153,14 @@ void UpdateRescalingInfo() { | |||
| 149 | info.up_scale = 6; | 153 | info.up_scale = 6; |
| 150 | info.down_shift = 0; | 154 | info.down_shift = 0; |
| 151 | break; | 155 | break; |
| 156 | case ResolutionSetup::Res7X: | ||
| 157 | info.up_scale = 7; | ||
| 158 | info.down_shift = 0; | ||
| 159 | break; | ||
| 160 | case ResolutionSetup::Res8X: | ||
| 161 | info.up_scale = 8; | ||
| 162 | info.down_shift = 0; | ||
| 163 | break; | ||
| 152 | default: | 164 | default: |
| 153 | ASSERT(false); | 165 | ASSERT(false); |
| 154 | info.up_scale = 1; | 166 | info.up_scale = 1; |
diff --git a/src/common/settings.h b/src/common/settings.h index a457e3f23..80b2eeabc 100644 --- a/src/common/settings.h +++ b/src/common/settings.h | |||
| @@ -56,11 +56,14 @@ enum class ResolutionSetup : u32 { | |||
| 56 | Res1_2X = 0, | 56 | Res1_2X = 0, |
| 57 | Res3_4X = 1, | 57 | Res3_4X = 1, |
| 58 | Res1X = 2, | 58 | Res1X = 2, |
| 59 | Res2X = 3, | 59 | Res3_2X = 3, |
| 60 | Res3X = 4, | 60 | Res2X = 4, |
| 61 | Res4X = 5, | 61 | Res3X = 5, |
| 62 | Res5X = 6, | 62 | Res4X = 6, |
| 63 | Res6X = 7, | 63 | Res5X = 7, |
| 64 | Res6X = 8, | ||
| 65 | Res7X = 9, | ||
| 66 | Res8X = 10, | ||
| 64 | }; | 67 | }; |
| 65 | 68 | ||
| 66 | enum class ScalingFilter : u32 { | 69 | enum class ScalingFilter : u32 { |
diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index 0e7b5f943..6bac6722f 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp | |||
| @@ -142,16 +142,24 @@ void CoreTiming::ScheduleLoopingEvent(std::chrono::nanoseconds start_time, | |||
| 142 | } | 142 | } |
| 143 | 143 | ||
| 144 | void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type, | 144 | void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type, |
| 145 | std::uintptr_t user_data) { | 145 | std::uintptr_t user_data, bool wait) { |
| 146 | std::scoped_lock scope{basic_lock}; | 146 | { |
| 147 | const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) { | 147 | std::scoped_lock lk{basic_lock}; |
| 148 | return e.type.lock().get() == event_type.get() && e.user_data == user_data; | 148 | const auto itr = |
| 149 | }); | 149 | std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) { |
| 150 | 150 | return e.type.lock().get() == event_type.get() && e.user_data == user_data; | |
| 151 | // Removing random items breaks the invariant so we have to re-establish it. | 151 | }); |
| 152 | if (itr != event_queue.end()) { | 152 | |
| 153 | event_queue.erase(itr, event_queue.end()); | 153 | // Removing random items breaks the invariant so we have to re-establish it. |
| 154 | std::make_heap(event_queue.begin(), event_queue.end(), std::greater<>()); | 154 | if (itr != event_queue.end()) { |
| 155 | event_queue.erase(itr, event_queue.end()); | ||
| 156 | std::make_heap(event_queue.begin(), event_queue.end(), std::greater<>()); | ||
| 157 | } | ||
| 158 | } | ||
| 159 | |||
| 160 | // Force any in-progress events to finish | ||
| 161 | if (wait) { | ||
| 162 | std::scoped_lock lk{advance_lock}; | ||
| 155 | } | 163 | } |
| 156 | } | 164 | } |
| 157 | 165 | ||
| @@ -190,20 +198,6 @@ u64 CoreTiming::GetClockTicks() const { | |||
| 190 | return CpuCyclesToClockCycles(ticks); | 198 | return CpuCyclesToClockCycles(ticks); |
| 191 | } | 199 | } |
| 192 | 200 | ||
| 193 | void CoreTiming::RemoveEvent(const std::shared_ptr<EventType>& event_type) { | ||
| 194 | std::scoped_lock lock{basic_lock}; | ||
| 195 | |||
| 196 | const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) { | ||
| 197 | return e.type.lock().get() == event_type.get(); | ||
| 198 | }); | ||
| 199 | |||
| 200 | // Removing random items breaks the invariant so we have to re-establish it. | ||
| 201 | if (itr != event_queue.end()) { | ||
| 202 | event_queue.erase(itr, event_queue.end()); | ||
| 203 | std::make_heap(event_queue.begin(), event_queue.end(), std::greater<>()); | ||
| 204 | } | ||
| 205 | } | ||
| 206 | |||
| 207 | std::optional<s64> CoreTiming::Advance() { | 201 | std::optional<s64> CoreTiming::Advance() { |
| 208 | std::scoped_lock lock{advance_lock, basic_lock}; | 202 | std::scoped_lock lock{advance_lock, basic_lock}; |
| 209 | global_timer = GetGlobalTimeNs().count(); | 203 | global_timer = GetGlobalTimeNs().count(); |
diff --git a/src/core/core_timing.h b/src/core/core_timing.h index b5925193c..da366637b 100644 --- a/src/core/core_timing.h +++ b/src/core/core_timing.h | |||
| @@ -98,10 +98,13 @@ public: | |||
| 98 | const std::shared_ptr<EventType>& event_type, | 98 | const std::shared_ptr<EventType>& event_type, |
| 99 | std::uintptr_t user_data = 0, bool absolute_time = false); | 99 | std::uintptr_t user_data = 0, bool absolute_time = false); |
| 100 | 100 | ||
| 101 | void UnscheduleEvent(const std::shared_ptr<EventType>& event_type, std::uintptr_t user_data); | 101 | void UnscheduleEvent(const std::shared_ptr<EventType>& event_type, std::uintptr_t user_data, |
| 102 | bool wait = true); | ||
| 102 | 103 | ||
| 103 | /// We only permit one event of each type in the queue at a time. | 104 | void UnscheduleEventWithoutWait(const std::shared_ptr<EventType>& event_type, |
| 104 | void RemoveEvent(const std::shared_ptr<EventType>& event_type); | 105 | std::uintptr_t user_data) { |
| 106 | UnscheduleEvent(event_type, user_data, false); | ||
| 107 | } | ||
| 105 | 108 | ||
| 106 | void AddTicks(u64 ticks_to_add); | 109 | void AddTicks(u64 ticks_to_add); |
| 107 | 110 | ||
diff --git a/src/core/hle/kernel/k_hardware_timer.cpp b/src/core/hle/kernel/k_hardware_timer.cpp index 6bba79ea0..4dcd53821 100644 --- a/src/core/hle/kernel/k_hardware_timer.cpp +++ b/src/core/hle/kernel/k_hardware_timer.cpp | |||
| @@ -18,7 +18,8 @@ void KHardwareTimer::Initialize() { | |||
| 18 | } | 18 | } |
| 19 | 19 | ||
| 20 | void KHardwareTimer::Finalize() { | 20 | void KHardwareTimer::Finalize() { |
| 21 | this->DisableInterrupt(); | 21 | m_kernel.System().CoreTiming().UnscheduleEvent(m_event_type, reinterpret_cast<uintptr_t>(this)); |
| 22 | m_wakeup_time = std::numeric_limits<s64>::max(); | ||
| 22 | m_event_type.reset(); | 23 | m_event_type.reset(); |
| 23 | } | 24 | } |
| 24 | 25 | ||
| @@ -59,7 +60,8 @@ void KHardwareTimer::EnableInterrupt(s64 wakeup_time) { | |||
| 59 | } | 60 | } |
| 60 | 61 | ||
| 61 | void KHardwareTimer::DisableInterrupt() { | 62 | void KHardwareTimer::DisableInterrupt() { |
| 62 | m_kernel.System().CoreTiming().UnscheduleEvent(m_event_type, reinterpret_cast<uintptr_t>(this)); | 63 | m_kernel.System().CoreTiming().UnscheduleEventWithoutWait(m_event_type, |
| 64 | reinterpret_cast<uintptr_t>(this)); | ||
| 63 | m_wakeup_time = std::numeric_limits<s64>::max(); | 65 | m_wakeup_time = std::numeric_limits<s64>::max(); |
| 64 | } | 66 | } |
| 65 | 67 | ||
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp index d1cbadde4..f4416f5b2 100644 --- a/src/core/hle/service/nvflinger/nvflinger.cpp +++ b/src/core/hle/service/nvflinger/nvflinger.cpp | |||
| @@ -312,8 +312,6 @@ void NVFlinger::Compose() { | |||
| 312 | } | 312 | } |
| 313 | 313 | ||
| 314 | s64 NVFlinger::GetNextTicks() const { | 314 | s64 NVFlinger::GetNextTicks() const { |
| 315 | static constexpr s64 max_hertz = 120LL; | ||
| 316 | |||
| 317 | const auto& settings = Settings::values; | 315 | const auto& settings = Settings::values; |
| 318 | auto speed_scale = 1.f; | 316 | auto speed_scale = 1.f; |
| 319 | if (settings.use_multi_core.GetValue()) { | 317 | if (settings.use_multi_core.GetValue()) { |
| @@ -327,9 +325,11 @@ s64 NVFlinger::GetNextTicks() const { | |||
| 327 | } | 325 | } |
| 328 | } | 326 | } |
| 329 | 327 | ||
| 330 | const auto next_ticks = ((1000000000 * (1LL << swap_interval)) / max_hertz); | 328 | // As an extension, treat nonpositive swap interval as framerate multiplier. |
| 329 | const f32 effective_fps = swap_interval <= 0 ? 120.f * static_cast<f32>(1 - swap_interval) | ||
| 330 | : 60.f / static_cast<f32>(swap_interval); | ||
| 331 | 331 | ||
| 332 | return static_cast<s64>(speed_scale * static_cast<float>(next_ticks)); | 332 | return static_cast<s64>(speed_scale * (1000000000.f / effective_fps)); |
| 333 | } | 333 | } |
| 334 | 334 | ||
| 335 | } // namespace Service::NVFlinger | 335 | } // namespace Service::NVFlinger |
diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h index 9b22397db..3828cf272 100644 --- a/src/core/hle/service/nvflinger/nvflinger.h +++ b/src/core/hle/service/nvflinger/nvflinger.h | |||
| @@ -133,7 +133,7 @@ private: | |||
| 133 | /// layers. | 133 | /// layers. |
| 134 | u32 next_buffer_queue_id = 1; | 134 | u32 next_buffer_queue_id = 1; |
| 135 | 135 | ||
| 136 | u32 swap_interval = 1; | 136 | s32 swap_interval = 1; |
| 137 | 137 | ||
| 138 | /// Event that handles screen composition. | 138 | /// Event that handles screen composition. |
| 139 | std::shared_ptr<Core::Timing::EventType> multi_composition_event; | 139 | std::shared_ptr<Core::Timing::EventType> multi_composition_event; |
diff --git a/src/core/memory.cpp b/src/core/memory.cpp index a1e41faff..4e605fae4 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp | |||
| @@ -383,6 +383,10 @@ struct Memory::Impl { | |||
| 383 | return; | 383 | return; |
| 384 | } | 384 | } |
| 385 | 385 | ||
| 386 | if (Settings::IsFastmemEnabled()) { | ||
| 387 | system.DeviceMemory().buffer.Protect(vaddr, size, !debug, !debug); | ||
| 388 | } | ||
| 389 | |||
| 386 | // Iterate over a contiguous CPU address space, marking/unmarking the region. | 390 | // Iterate over a contiguous CPU address space, marking/unmarking the region. |
| 387 | // The region is at a granularity of CPU pages. | 391 | // The region is at a granularity of CPU pages. |
| 388 | 392 | ||
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index f617665de..b474eb363 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt | |||
| @@ -100,6 +100,8 @@ add_library(video_core STATIC | |||
| 100 | renderer_null/null_rasterizer.h | 100 | renderer_null/null_rasterizer.h |
| 101 | renderer_null/renderer_null.cpp | 101 | renderer_null/renderer_null.cpp |
| 102 | renderer_null/renderer_null.h | 102 | renderer_null/renderer_null.h |
| 103 | renderer_opengl/blit_image.cpp | ||
| 104 | renderer_opengl/blit_image.h | ||
| 103 | renderer_opengl/gl_buffer_cache.cpp | 105 | renderer_opengl/gl_buffer_cache.cpp |
| 104 | renderer_opengl/gl_buffer_cache.h | 106 | renderer_opengl/gl_buffer_cache.h |
| 105 | renderer_opengl/gl_compute_pipeline.cpp | 107 | renderer_opengl/gl_compute_pipeline.cpp |
diff --git a/src/video_core/engines/draw_manager.cpp b/src/video_core/engines/draw_manager.cpp index 2437121ce..1d22d25f1 100644 --- a/src/video_core/engines/draw_manager.cpp +++ b/src/video_core/engines/draw_manager.cpp | |||
| @@ -51,6 +51,10 @@ void DrawManager::ProcessMethodCall(u32 method, u32 argument) { | |||
| 51 | LOG_WARNING(HW_GPU, "(STUBBED) called"); | 51 | LOG_WARNING(HW_GPU, "(STUBBED) called"); |
| 52 | break; | 52 | break; |
| 53 | } | 53 | } |
| 54 | case MAXWELL3D_REG_INDEX(draw_texture.src_y0): { | ||
| 55 | DrawTexture(); | ||
| 56 | break; | ||
| 57 | } | ||
| 54 | default: | 58 | default: |
| 55 | break; | 59 | break; |
| 56 | } | 60 | } |
| @@ -179,6 +183,33 @@ void DrawManager::DrawIndexSmall(u32 argument) { | |||
| 179 | ProcessDraw(true, 1); | 183 | ProcessDraw(true, 1); |
| 180 | } | 184 | } |
| 181 | 185 | ||
| 186 | void DrawManager::DrawTexture() { | ||
| 187 | const auto& regs{maxwell3d->regs}; | ||
| 188 | draw_texture_state.dst_x0 = static_cast<float>(regs.draw_texture.dst_x0) / 4096.f; | ||
| 189 | draw_texture_state.dst_y0 = static_cast<float>(regs.draw_texture.dst_y0) / 4096.f; | ||
| 190 | const auto dst_width = static_cast<float>(regs.draw_texture.dst_width) / 4096.f; | ||
| 191 | const auto dst_height = static_cast<float>(regs.draw_texture.dst_height) / 4096.f; | ||
| 192 | const bool lower_left{regs.window_origin.mode != | ||
| 193 | Maxwell3D::Regs::WindowOrigin::Mode::UpperLeft}; | ||
| 194 | if (lower_left) { | ||
| 195 | draw_texture_state.dst_y0 -= dst_height; | ||
| 196 | } | ||
| 197 | draw_texture_state.dst_x1 = draw_texture_state.dst_x0 + dst_width; | ||
| 198 | draw_texture_state.dst_y1 = draw_texture_state.dst_y0 + dst_height; | ||
| 199 | draw_texture_state.src_x0 = static_cast<float>(regs.draw_texture.src_x0) / 4096.f; | ||
| 200 | draw_texture_state.src_y0 = static_cast<float>(regs.draw_texture.src_y0) / 4096.f; | ||
| 201 | draw_texture_state.src_x1 = | ||
| 202 | (static_cast<float>(regs.draw_texture.dx_du) / 4294967296.f) * dst_width + | ||
| 203 | draw_texture_state.src_x0; | ||
| 204 | draw_texture_state.src_y1 = | ||
| 205 | (static_cast<float>(regs.draw_texture.dy_dv) / 4294967296.f) * dst_height + | ||
| 206 | draw_texture_state.src_y0; | ||
| 207 | draw_texture_state.src_sampler = regs.draw_texture.src_sampler; | ||
| 208 | draw_texture_state.src_texture = regs.draw_texture.src_texture; | ||
| 209 | |||
| 210 | maxwell3d->rasterizer->DrawTexture(); | ||
| 211 | } | ||
| 212 | |||
| 182 | void DrawManager::UpdateTopology() { | 213 | void DrawManager::UpdateTopology() { |
| 183 | const auto& regs{maxwell3d->regs}; | 214 | const auto& regs{maxwell3d->regs}; |
| 184 | switch (regs.primitive_topology_control) { | 215 | switch (regs.primitive_topology_control) { |
diff --git a/src/video_core/engines/draw_manager.h b/src/video_core/engines/draw_manager.h index 58d1b2d59..7c22c49f1 100644 --- a/src/video_core/engines/draw_manager.h +++ b/src/video_core/engines/draw_manager.h | |||
| @@ -32,6 +32,19 @@ public: | |||
| 32 | std::vector<u8> inline_index_draw_indexes; | 32 | std::vector<u8> inline_index_draw_indexes; |
| 33 | }; | 33 | }; |
| 34 | 34 | ||
| 35 | struct DrawTextureState { | ||
| 36 | f32 dst_x0; | ||
| 37 | f32 dst_y0; | ||
| 38 | f32 dst_x1; | ||
| 39 | f32 dst_y1; | ||
| 40 | f32 src_x0; | ||
| 41 | f32 src_y0; | ||
| 42 | f32 src_x1; | ||
| 43 | f32 src_y1; | ||
| 44 | u32 src_sampler; | ||
| 45 | u32 src_texture; | ||
| 46 | }; | ||
| 47 | |||
| 35 | struct IndirectParams { | 48 | struct IndirectParams { |
| 36 | bool is_indexed; | 49 | bool is_indexed; |
| 37 | bool include_count; | 50 | bool include_count; |
| @@ -64,6 +77,10 @@ public: | |||
| 64 | return draw_state; | 77 | return draw_state; |
| 65 | } | 78 | } |
| 66 | 79 | ||
| 80 | const DrawTextureState& GetDrawTextureState() const { | ||
| 81 | return draw_texture_state; | ||
| 82 | } | ||
| 83 | |||
| 67 | IndirectParams& GetIndirectParams() { | 84 | IndirectParams& GetIndirectParams() { |
| 68 | return indirect_state; | 85 | return indirect_state; |
| 69 | } | 86 | } |
| @@ -81,6 +98,8 @@ private: | |||
| 81 | 98 | ||
| 82 | void DrawIndexSmall(u32 argument); | 99 | void DrawIndexSmall(u32 argument); |
| 83 | 100 | ||
| 101 | void DrawTexture(); | ||
| 102 | |||
| 84 | void UpdateTopology(); | 103 | void UpdateTopology(); |
| 85 | 104 | ||
| 86 | void ProcessDraw(bool draw_indexed, u32 instance_count); | 105 | void ProcessDraw(bool draw_indexed, u32 instance_count); |
| @@ -89,6 +108,7 @@ private: | |||
| 89 | 108 | ||
| 90 | Maxwell3D* maxwell3d{}; | 109 | Maxwell3D* maxwell3d{}; |
| 91 | State draw_state{}; | 110 | State draw_state{}; |
| 111 | DrawTextureState draw_texture_state{}; | ||
| 92 | IndirectParams indirect_state{}; | 112 | IndirectParams indirect_state{}; |
| 93 | }; | 113 | }; |
| 94 | } // namespace Tegra::Engines | 114 | } // namespace Tegra::Engines |
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp index 97f547789..ae9da6290 100644 --- a/src/video_core/engines/maxwell_3d.cpp +++ b/src/video_core/engines/maxwell_3d.cpp | |||
| @@ -149,6 +149,7 @@ bool Maxwell3D::IsMethodExecutable(u32 method) { | |||
| 149 | case MAXWELL3D_REG_INDEX(inline_index_4x8.index0): | 149 | case MAXWELL3D_REG_INDEX(inline_index_4x8.index0): |
| 150 | case MAXWELL3D_REG_INDEX(vertex_array_instance_first): | 150 | case MAXWELL3D_REG_INDEX(vertex_array_instance_first): |
| 151 | case MAXWELL3D_REG_INDEX(vertex_array_instance_subsequent): | 151 | case MAXWELL3D_REG_INDEX(vertex_array_instance_subsequent): |
| 152 | case MAXWELL3D_REG_INDEX(draw_texture.src_y0): | ||
| 152 | case MAXWELL3D_REG_INDEX(wait_for_idle): | 153 | case MAXWELL3D_REG_INDEX(wait_for_idle): |
| 153 | case MAXWELL3D_REG_INDEX(shadow_ram_control): | 154 | case MAXWELL3D_REG_INDEX(shadow_ram_control): |
| 154 | case MAXWELL3D_REG_INDEX(load_mme.instruction_ptr): | 155 | case MAXWELL3D_REG_INDEX(load_mme.instruction_ptr): |
| @@ -467,7 +468,7 @@ void Maxwell3D::ProcessMacroBind(u32 data) { | |||
| 467 | } | 468 | } |
| 468 | 469 | ||
| 469 | void Maxwell3D::ProcessFirmwareCall4() { | 470 | void Maxwell3D::ProcessFirmwareCall4() { |
| 470 | LOG_WARNING(HW_GPU, "(STUBBED) called"); | 471 | LOG_DEBUG(HW_GPU, "(STUBBED) called"); |
| 471 | 472 | ||
| 472 | // Firmware call 4 is a blob that changes some registers depending on its parameters. | 473 | // Firmware call 4 is a blob that changes some registers depending on its parameters. |
| 473 | // These registers don't affect emulation and so are stubbed by setting 0xd00 to 1. | 474 | // These registers don't affect emulation and so are stubbed by setting 0xd00 to 1. |
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h index 0b2fd2928..c89969bb4 100644 --- a/src/video_core/engines/maxwell_3d.h +++ b/src/video_core/engines/maxwell_3d.h | |||
| @@ -1599,6 +1599,20 @@ public: | |||
| 1599 | }; | 1599 | }; |
| 1600 | static_assert(sizeof(TIRModulationCoeff) == 0x4); | 1600 | static_assert(sizeof(TIRModulationCoeff) == 0x4); |
| 1601 | 1601 | ||
| 1602 | struct DrawTexture { | ||
| 1603 | s32 dst_x0; | ||
| 1604 | s32 dst_y0; | ||
| 1605 | s32 dst_width; | ||
| 1606 | s32 dst_height; | ||
| 1607 | s64 dx_du; | ||
| 1608 | s64 dy_dv; | ||
| 1609 | u32 src_sampler; | ||
| 1610 | u32 src_texture; | ||
| 1611 | s32 src_x0; | ||
| 1612 | s32 src_y0; | ||
| 1613 | }; | ||
| 1614 | static_assert(sizeof(DrawTexture) == 0x30); | ||
| 1615 | |||
| 1602 | struct ReduceColorThreshold { | 1616 | struct ReduceColorThreshold { |
| 1603 | union { | 1617 | union { |
| 1604 | BitField<0, 8, u32> all_hit_once; | 1618 | BitField<0, 8, u32> all_hit_once; |
| @@ -2751,7 +2765,7 @@ public: | |||
| 2751 | u32 reserved_sw_method2; ///< 0x102C | 2765 | u32 reserved_sw_method2; ///< 0x102C |
| 2752 | std::array<TIRModulationCoeff, 5> tir_modulation_coeff; ///< 0x1030 | 2766 | std::array<TIRModulationCoeff, 5> tir_modulation_coeff; ///< 0x1030 |
| 2753 | std::array<u32, 15> spare_nop; ///< 0x1044 | 2767 | std::array<u32, 15> spare_nop; ///< 0x1044 |
| 2754 | INSERT_PADDING_BYTES_NOINIT(0x30); | 2768 | DrawTexture draw_texture; ///< 0x1080 |
| 2755 | std::array<u32, 7> reserved_sw_method3_to_7; ///< 0x10B0 | 2769 | std::array<u32, 7> reserved_sw_method3_to_7; ///< 0x10B0 |
| 2756 | ReduceColorThreshold reduce_color_thresholds_unorm8; ///< 0x10CC | 2770 | ReduceColorThreshold reduce_color_thresholds_unorm8; ///< 0x10CC |
| 2757 | std::array<u32, 4> reserved_sw_method10_to_13; ///< 0x10D0 | 2771 | std::array<u32, 4> reserved_sw_method10_to_13; ///< 0x10D0 |
diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt index f275b2aa9..e968ae220 100644 --- a/src/video_core/host_shaders/CMakeLists.txt +++ b/src/video_core/host_shaders/CMakeLists.txt | |||
| @@ -11,6 +11,7 @@ set(GLSL_INCLUDES | |||
| 11 | 11 | ||
| 12 | set(SHADER_FILES | 12 | set(SHADER_FILES |
| 13 | astc_decoder.comp | 13 | astc_decoder.comp |
| 14 | blit_color_float.frag | ||
| 14 | block_linear_unswizzle_2d.comp | 15 | block_linear_unswizzle_2d.comp |
| 15 | block_linear_unswizzle_3d.comp | 16 | block_linear_unswizzle_3d.comp |
| 16 | convert_abgr8_to_d24s8.frag | 17 | convert_abgr8_to_d24s8.frag |
| @@ -36,7 +37,6 @@ set(SHADER_FILES | |||
| 36 | smaa_blending_weight_calculation.frag | 37 | smaa_blending_weight_calculation.frag |
| 37 | smaa_neighborhood_blending.vert | 38 | smaa_neighborhood_blending.vert |
| 38 | smaa_neighborhood_blending.frag | 39 | smaa_neighborhood_blending.frag |
| 39 | vulkan_blit_color_float.frag | ||
| 40 | vulkan_blit_depth_stencil.frag | 40 | vulkan_blit_depth_stencil.frag |
| 41 | vulkan_fidelityfx_fsr_easu_fp16.comp | 41 | vulkan_fidelityfx_fsr_easu_fp16.comp |
| 42 | vulkan_fidelityfx_fsr_easu_fp32.comp | 42 | vulkan_fidelityfx_fsr_easu_fp32.comp |
diff --git a/src/video_core/host_shaders/vulkan_blit_color_float.frag b/src/video_core/host_shaders/blit_color_float.frag index c0c832296..c0c832296 100644 --- a/src/video_core/host_shaders/vulkan_blit_color_float.frag +++ b/src/video_core/host_shaders/blit_color_float.frag | |||
diff --git a/src/video_core/host_shaders/full_screen_triangle.vert b/src/video_core/host_shaders/full_screen_triangle.vert index 2c976b19f..d16d98995 100644 --- a/src/video_core/host_shaders/full_screen_triangle.vert +++ b/src/video_core/host_shaders/full_screen_triangle.vert | |||
| @@ -4,13 +4,20 @@ | |||
| 4 | #version 450 | 4 | #version 450 |
| 5 | 5 | ||
| 6 | #ifdef VULKAN | 6 | #ifdef VULKAN |
| 7 | #define VERTEX_ID gl_VertexIndex | ||
| 7 | #define BEGIN_PUSH_CONSTANTS layout(push_constant) uniform PushConstants { | 8 | #define BEGIN_PUSH_CONSTANTS layout(push_constant) uniform PushConstants { |
| 8 | #define END_PUSH_CONSTANTS }; | 9 | #define END_PUSH_CONSTANTS }; |
| 9 | #define UNIFORM(n) | 10 | #define UNIFORM(n) |
| 11 | #define FLIPY 1 | ||
| 10 | #else // ^^^ Vulkan ^^^ // vvv OpenGL vvv | 12 | #else // ^^^ Vulkan ^^^ // vvv OpenGL vvv |
| 13 | #define VERTEX_ID gl_VertexID | ||
| 11 | #define BEGIN_PUSH_CONSTANTS | 14 | #define BEGIN_PUSH_CONSTANTS |
| 12 | #define END_PUSH_CONSTANTS | 15 | #define END_PUSH_CONSTANTS |
| 16 | #define FLIPY -1 | ||
| 13 | #define UNIFORM(n) layout (location = n) uniform | 17 | #define UNIFORM(n) layout (location = n) uniform |
| 18 | out gl_PerVertex { | ||
| 19 | vec4 gl_Position; | ||
| 20 | }; | ||
| 14 | #endif | 21 | #endif |
| 15 | 22 | ||
| 16 | BEGIN_PUSH_CONSTANTS | 23 | BEGIN_PUSH_CONSTANTS |
| @@ -21,8 +28,8 @@ END_PUSH_CONSTANTS | |||
| 21 | layout(location = 0) out vec2 texcoord; | 28 | layout(location = 0) out vec2 texcoord; |
| 22 | 29 | ||
| 23 | void main() { | 30 | void main() { |
| 24 | float x = float((gl_VertexIndex & 1) << 2); | 31 | float x = float((VERTEX_ID & 1) << 2); |
| 25 | float y = float((gl_VertexIndex & 2) << 1); | 32 | float y = float((VERTEX_ID & 2) << 1); |
| 26 | gl_Position = vec4(x - 1.0, y - 1.0, 0.0, 1.0); | 33 | gl_Position = vec4(x - 1.0, FLIPY * (y - 1.0), 0.0, 1.0); |
| 27 | texcoord = fma(vec2(x, y) / 2.0, tex_scale, tex_offset); | 34 | texcoord = fma(vec2(x, y) / 2.0, tex_scale, tex_offset); |
| 28 | } | 35 | } |
diff --git a/src/video_core/rasterizer_interface.h b/src/video_core/rasterizer_interface.h index 1735b6164..33e2610bc 100644 --- a/src/video_core/rasterizer_interface.h +++ b/src/video_core/rasterizer_interface.h | |||
| @@ -47,6 +47,9 @@ public: | |||
| 47 | /// Dispatches an indirect draw invocation | 47 | /// Dispatches an indirect draw invocation |
| 48 | virtual void DrawIndirect() {} | 48 | virtual void DrawIndirect() {} |
| 49 | 49 | ||
| 50 | /// Dispatches an draw texture invocation | ||
| 51 | virtual void DrawTexture() = 0; | ||
| 52 | |||
| 50 | /// Clear the current framebuffer | 53 | /// Clear the current framebuffer |
| 51 | virtual void Clear(u32 layer_count) = 0; | 54 | virtual void Clear(u32 layer_count) = 0; |
| 52 | 55 | ||
diff --git a/src/video_core/renderer_null/null_rasterizer.cpp b/src/video_core/renderer_null/null_rasterizer.cpp index 2c11345d7..2b5c7defa 100644 --- a/src/video_core/renderer_null/null_rasterizer.cpp +++ b/src/video_core/renderer_null/null_rasterizer.cpp | |||
| @@ -21,6 +21,7 @@ RasterizerNull::RasterizerNull(Core::Memory::Memory& cpu_memory_, Tegra::GPU& gp | |||
| 21 | RasterizerNull::~RasterizerNull() = default; | 21 | RasterizerNull::~RasterizerNull() = default; |
| 22 | 22 | ||
| 23 | void RasterizerNull::Draw(bool is_indexed, u32 instance_count) {} | 23 | void RasterizerNull::Draw(bool is_indexed, u32 instance_count) {} |
| 24 | void RasterizerNull::DrawTexture() {} | ||
| 24 | void RasterizerNull::Clear(u32 layer_count) {} | 25 | void RasterizerNull::Clear(u32 layer_count) {} |
| 25 | void RasterizerNull::DispatchCompute() {} | 26 | void RasterizerNull::DispatchCompute() {} |
| 26 | void RasterizerNull::ResetCounter(VideoCore::QueryType type) {} | 27 | void RasterizerNull::ResetCounter(VideoCore::QueryType type) {} |
diff --git a/src/video_core/renderer_null/null_rasterizer.h b/src/video_core/renderer_null/null_rasterizer.h index 2112aa70e..51f896e43 100644 --- a/src/video_core/renderer_null/null_rasterizer.h +++ b/src/video_core/renderer_null/null_rasterizer.h | |||
| @@ -31,6 +31,7 @@ public: | |||
| 31 | ~RasterizerNull() override; | 31 | ~RasterizerNull() override; |
| 32 | 32 | ||
| 33 | void Draw(bool is_indexed, u32 instance_count) override; | 33 | void Draw(bool is_indexed, u32 instance_count) override; |
| 34 | void DrawTexture() override; | ||
| 34 | void Clear(u32 layer_count) override; | 35 | void Clear(u32 layer_count) override; |
| 35 | void DispatchCompute() override; | 36 | void DispatchCompute() override; |
| 36 | void ResetCounter(VideoCore::QueryType type) override; | 37 | void ResetCounter(VideoCore::QueryType type) override; |
diff --git a/src/video_core/renderer_opengl/blit_image.cpp b/src/video_core/renderer_opengl/blit_image.cpp new file mode 100644 index 000000000..9a560a73b --- /dev/null +++ b/src/video_core/renderer_opengl/blit_image.cpp | |||
| @@ -0,0 +1,59 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include <algorithm> | ||
| 5 | |||
| 6 | #include "video_core/host_shaders/blit_color_float_frag.h" | ||
| 7 | #include "video_core/host_shaders/full_screen_triangle_vert.h" | ||
| 8 | #include "video_core/renderer_opengl/blit_image.h" | ||
| 9 | #include "video_core/renderer_opengl/gl_shader_manager.h" | ||
| 10 | #include "video_core/renderer_opengl/gl_shader_util.h" | ||
| 11 | |||
| 12 | namespace OpenGL { | ||
| 13 | |||
| 14 | BlitImageHelper::BlitImageHelper(ProgramManager& program_manager_) | ||
| 15 | : program_manager(program_manager_), | ||
| 16 | full_screen_vert(CreateProgram(HostShaders::FULL_SCREEN_TRIANGLE_VERT, GL_VERTEX_SHADER)), | ||
| 17 | blit_color_to_color_frag( | ||
| 18 | CreateProgram(HostShaders::BLIT_COLOR_FLOAT_FRAG, GL_FRAGMENT_SHADER)) {} | ||
| 19 | |||
| 20 | BlitImageHelper::~BlitImageHelper() = default; | ||
| 21 | |||
| 22 | void BlitImageHelper::BlitColor(GLuint dst_framebuffer, GLuint src_image_view, GLuint src_sampler, | ||
| 23 | const Region2D& dst_region, const Region2D& src_region, | ||
| 24 | const Extent3D& src_size) { | ||
| 25 | glEnable(GL_CULL_FACE); | ||
| 26 | glDisable(GL_COLOR_LOGIC_OP); | ||
| 27 | glDisable(GL_DEPTH_TEST); | ||
| 28 | glDisable(GL_STENCIL_TEST); | ||
| 29 | glDisable(GL_POLYGON_OFFSET_FILL); | ||
| 30 | glDisable(GL_RASTERIZER_DISCARD); | ||
| 31 | glDisable(GL_ALPHA_TEST); | ||
| 32 | glDisablei(GL_BLEND, 0); | ||
| 33 | glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); | ||
| 34 | glCullFace(GL_BACK); | ||
| 35 | glFrontFace(GL_CW); | ||
| 36 | glColorMaski(0, GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); | ||
| 37 | glDepthRangeIndexed(0, 0.0, 0.0); | ||
| 38 | |||
| 39 | program_manager.BindPresentPrograms(full_screen_vert.handle, blit_color_to_color_frag.handle); | ||
| 40 | glProgramUniform2f(full_screen_vert.handle, 0, | ||
| 41 | static_cast<float>(src_region.end.x - src_region.start.x) / | ||
| 42 | static_cast<float>(src_size.width), | ||
| 43 | static_cast<float>(src_region.end.y - src_region.start.y) / | ||
| 44 | static_cast<float>(src_size.height)); | ||
| 45 | glProgramUniform2f(full_screen_vert.handle, 1, | ||
| 46 | static_cast<float>(src_region.start.x) / static_cast<float>(src_size.width), | ||
| 47 | static_cast<float>(src_region.start.y) / | ||
| 48 | static_cast<float>(src_size.height)); | ||
| 49 | glViewport(std::min(dst_region.start.x, dst_region.end.x), | ||
| 50 | std::min(dst_region.start.y, dst_region.end.y), | ||
| 51 | std::abs(dst_region.end.x - dst_region.start.x), | ||
| 52 | std::abs(dst_region.end.y - dst_region.start.y)); | ||
| 53 | glBindFramebuffer(GL_DRAW_FRAMEBUFFER, dst_framebuffer); | ||
| 54 | glBindSampler(0, src_sampler); | ||
| 55 | glBindTextureUnit(0, src_image_view); | ||
| 56 | glClear(GL_COLOR_BUFFER_BIT); | ||
| 57 | glDrawArrays(GL_TRIANGLES, 0, 3); | ||
| 58 | } | ||
| 59 | } // namespace OpenGL | ||
diff --git a/src/video_core/renderer_opengl/blit_image.h b/src/video_core/renderer_opengl/blit_image.h new file mode 100644 index 000000000..5a2b12d16 --- /dev/null +++ b/src/video_core/renderer_opengl/blit_image.h | |||
| @@ -0,0 +1,38 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <glad/glad.h> | ||
| 7 | |||
| 8 | #include "video_core/engines/fermi_2d.h" | ||
| 9 | #include "video_core/renderer_opengl/gl_resource_manager.h" | ||
| 10 | #include "video_core/texture_cache/types.h" | ||
| 11 | |||
| 12 | namespace OpenGL { | ||
| 13 | |||
| 14 | using VideoCommon::Extent3D; | ||
| 15 | using VideoCommon::Offset2D; | ||
| 16 | using VideoCommon::Region2D; | ||
| 17 | |||
| 18 | class ProgramManager; | ||
| 19 | class Framebuffer; | ||
| 20 | class ImageView; | ||
| 21 | |||
| 22 | class BlitImageHelper { | ||
| 23 | public: | ||
| 24 | explicit BlitImageHelper(ProgramManager& program_manager); | ||
| 25 | ~BlitImageHelper(); | ||
| 26 | |||
| 27 | void BlitColor(GLuint dst_framebuffer, GLuint src_image_view, GLuint src_sampler, | ||
| 28 | const Region2D& dst_region, const Region2D& src_region, | ||
| 29 | const Extent3D& src_size); | ||
| 30 | |||
| 31 | private: | ||
| 32 | ProgramManager& program_manager; | ||
| 33 | |||
| 34 | OGLProgram full_screen_vert; | ||
| 35 | OGLProgram blit_color_to_color_frag; | ||
| 36 | }; | ||
| 37 | |||
| 38 | } // namespace OpenGL | ||
diff --git a/src/video_core/renderer_opengl/gl_device.cpp b/src/video_core/renderer_opengl/gl_device.cpp index cee5c3247..22ed16ebf 100644 --- a/src/video_core/renderer_opengl/gl_device.cpp +++ b/src/video_core/renderer_opengl/gl_device.cpp | |||
| @@ -166,6 +166,7 @@ Device::Device(Core::Frontend::EmuWindow& emu_window) { | |||
| 166 | has_shader_int64 = HasExtension(extensions, "GL_ARB_gpu_shader_int64"); | 166 | has_shader_int64 = HasExtension(extensions, "GL_ARB_gpu_shader_int64"); |
| 167 | has_amd_shader_half_float = GLAD_GL_AMD_gpu_shader_half_float; | 167 | has_amd_shader_half_float = GLAD_GL_AMD_gpu_shader_half_float; |
| 168 | has_sparse_texture_2 = GLAD_GL_ARB_sparse_texture2; | 168 | has_sparse_texture_2 = GLAD_GL_ARB_sparse_texture2; |
| 169 | has_draw_texture = GLAD_GL_NV_draw_texture; | ||
| 169 | warp_size_potentially_larger_than_guest = !is_nvidia && !is_intel; | 170 | warp_size_potentially_larger_than_guest = !is_nvidia && !is_intel; |
| 170 | need_fastmath_off = is_nvidia; | 171 | need_fastmath_off = is_nvidia; |
| 171 | can_report_memory = GLAD_GL_NVX_gpu_memory_info; | 172 | can_report_memory = GLAD_GL_NVX_gpu_memory_info; |
diff --git a/src/video_core/renderer_opengl/gl_device.h b/src/video_core/renderer_opengl/gl_device.h index 2a72d84be..3ff8cad83 100644 --- a/src/video_core/renderer_opengl/gl_device.h +++ b/src/video_core/renderer_opengl/gl_device.h | |||
| @@ -4,6 +4,8 @@ | |||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include <cstddef> | 6 | #include <cstddef> |
| 7 | #include <string> | ||
| 8 | |||
| 7 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 8 | #include "core/frontend/emu_window.h" | 10 | #include "core/frontend/emu_window.h" |
| 9 | #include "shader_recompiler/stage.h" | 11 | #include "shader_recompiler/stage.h" |
| @@ -146,6 +148,10 @@ public: | |||
| 146 | return has_sparse_texture_2; | 148 | return has_sparse_texture_2; |
| 147 | } | 149 | } |
| 148 | 150 | ||
| 151 | bool HasDrawTexture() const { | ||
| 152 | return has_draw_texture; | ||
| 153 | } | ||
| 154 | |||
| 149 | bool IsWarpSizePotentiallyLargerThanGuest() const { | 155 | bool IsWarpSizePotentiallyLargerThanGuest() const { |
| 150 | return warp_size_potentially_larger_than_guest; | 156 | return warp_size_potentially_larger_than_guest; |
| 151 | } | 157 | } |
| @@ -216,6 +222,7 @@ private: | |||
| 216 | bool has_shader_int64{}; | 222 | bool has_shader_int64{}; |
| 217 | bool has_amd_shader_half_float{}; | 223 | bool has_amd_shader_half_float{}; |
| 218 | bool has_sparse_texture_2{}; | 224 | bool has_sparse_texture_2{}; |
| 225 | bool has_draw_texture{}; | ||
| 219 | bool warp_size_potentially_larger_than_guest{}; | 226 | bool warp_size_potentially_larger_than_guest{}; |
| 220 | bool need_fastmath_off{}; | 227 | bool need_fastmath_off{}; |
| 221 | bool has_cbuf_ftou_bug{}; | 228 | bool has_cbuf_ftou_bug{}; |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 181857d9c..7bced675c 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp | |||
| @@ -64,7 +64,8 @@ RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& emu_window_, Tegra | |||
| 64 | shader_cache(*this, emu_window_, device, texture_cache, buffer_cache, program_manager, | 64 | shader_cache(*this, emu_window_, device, texture_cache, buffer_cache, program_manager, |
| 65 | state_tracker, gpu.ShaderNotify()), | 65 | state_tracker, gpu.ShaderNotify()), |
| 66 | query_cache(*this), accelerate_dma(buffer_cache), | 66 | query_cache(*this), accelerate_dma(buffer_cache), |
| 67 | fence_manager(*this, gpu, texture_cache, buffer_cache, query_cache) {} | 67 | fence_manager(*this, gpu, texture_cache, buffer_cache, query_cache), |
| 68 | blit_image(program_manager_) {} | ||
| 68 | 69 | ||
| 69 | RasterizerOpenGL::~RasterizerOpenGL() = default; | 70 | RasterizerOpenGL::~RasterizerOpenGL() = default; |
| 70 | 71 | ||
| @@ -320,6 +321,47 @@ void RasterizerOpenGL::DrawIndirect() { | |||
| 320 | buffer_cache.SetDrawIndirect(nullptr); | 321 | buffer_cache.SetDrawIndirect(nullptr); |
| 321 | } | 322 | } |
| 322 | 323 | ||
| 324 | void RasterizerOpenGL::DrawTexture() { | ||
| 325 | MICROPROFILE_SCOPE(OpenGL_Drawing); | ||
| 326 | |||
| 327 | SCOPE_EXIT({ gpu.TickWork(); }); | ||
| 328 | query_cache.UpdateCounters(); | ||
| 329 | |||
| 330 | texture_cache.SynchronizeGraphicsDescriptors(); | ||
| 331 | texture_cache.UpdateRenderTargets(false); | ||
| 332 | |||
| 333 | SyncState(); | ||
| 334 | |||
| 335 | const auto& draw_texture_state = maxwell3d->draw_manager->GetDrawTextureState(); | ||
| 336 | const auto& sampler = texture_cache.GetGraphicsSampler(draw_texture_state.src_sampler); | ||
| 337 | const auto& texture = texture_cache.GetImageView(draw_texture_state.src_texture); | ||
| 338 | |||
| 339 | if (device.HasDrawTexture()) { | ||
| 340 | state_tracker.BindFramebuffer(texture_cache.GetFramebuffer()->Handle()); | ||
| 341 | |||
| 342 | glDrawTextureNV(texture.DefaultHandle(), sampler->Handle(), draw_texture_state.dst_x0, | ||
| 343 | draw_texture_state.dst_y0, draw_texture_state.dst_x1, | ||
| 344 | draw_texture_state.dst_y1, 0, | ||
| 345 | draw_texture_state.src_x0 / static_cast<float>(texture.size.width), | ||
| 346 | draw_texture_state.src_y0 / static_cast<float>(texture.size.height), | ||
| 347 | draw_texture_state.src_x1 / static_cast<float>(texture.size.width), | ||
| 348 | draw_texture_state.src_y1 / static_cast<float>(texture.size.height)); | ||
| 349 | } else { | ||
| 350 | Region2D dst_region = {Offset2D{.x = static_cast<s32>(draw_texture_state.dst_x0), | ||
| 351 | .y = static_cast<s32>(draw_texture_state.dst_y0)}, | ||
| 352 | Offset2D{.x = static_cast<s32>(draw_texture_state.dst_x1), | ||
| 353 | .y = static_cast<s32>(draw_texture_state.dst_y1)}}; | ||
| 354 | Region2D src_region = {Offset2D{.x = static_cast<s32>(draw_texture_state.src_x0), | ||
| 355 | .y = static_cast<s32>(draw_texture_state.src_y0)}, | ||
| 356 | Offset2D{.x = static_cast<s32>(draw_texture_state.src_x1), | ||
| 357 | .y = static_cast<s32>(draw_texture_state.src_y1)}}; | ||
| 358 | blit_image.BlitColor(texture_cache.GetFramebuffer()->Handle(), texture.DefaultHandle(), | ||
| 359 | sampler->Handle(), dst_region, src_region, texture.size); | ||
| 360 | } | ||
| 361 | |||
| 362 | ++num_queued_commands; | ||
| 363 | } | ||
| 364 | |||
| 323 | void RasterizerOpenGL::DispatchCompute() { | 365 | void RasterizerOpenGL::DispatchCompute() { |
| 324 | gpu_memory->FlushCaching(); | 366 | gpu_memory->FlushCaching(); |
| 325 | ComputePipeline* const pipeline{shader_cache.CurrentComputePipeline()}; | 367 | ComputePipeline* const pipeline{shader_cache.CurrentComputePipeline()}; |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index be4f76c18..0c45832ae 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h | |||
| @@ -16,6 +16,7 @@ | |||
| 16 | #include "video_core/engines/maxwell_dma.h" | 16 | #include "video_core/engines/maxwell_dma.h" |
| 17 | #include "video_core/rasterizer_accelerated.h" | 17 | #include "video_core/rasterizer_accelerated.h" |
| 18 | #include "video_core/rasterizer_interface.h" | 18 | #include "video_core/rasterizer_interface.h" |
| 19 | #include "video_core/renderer_opengl/blit_image.h" | ||
| 19 | #include "video_core/renderer_opengl/gl_buffer_cache.h" | 20 | #include "video_core/renderer_opengl/gl_buffer_cache.h" |
| 20 | #include "video_core/renderer_opengl/gl_device.h" | 21 | #include "video_core/renderer_opengl/gl_device.h" |
| 21 | #include "video_core/renderer_opengl/gl_fence_manager.h" | 22 | #include "video_core/renderer_opengl/gl_fence_manager.h" |
| @@ -70,6 +71,7 @@ public: | |||
| 70 | 71 | ||
| 71 | void Draw(bool is_indexed, u32 instance_count) override; | 72 | void Draw(bool is_indexed, u32 instance_count) override; |
| 72 | void DrawIndirect() override; | 73 | void DrawIndirect() override; |
| 74 | void DrawTexture() override; | ||
| 73 | void Clear(u32 layer_count) override; | 75 | void Clear(u32 layer_count) override; |
| 74 | void DispatchCompute() override; | 76 | void DispatchCompute() override; |
| 75 | void ResetCounter(VideoCore::QueryType type) override; | 77 | void ResetCounter(VideoCore::QueryType type) override; |
| @@ -224,6 +226,8 @@ private: | |||
| 224 | AccelerateDMA accelerate_dma; | 226 | AccelerateDMA accelerate_dma; |
| 225 | FenceManagerOpenGL fence_manager; | 227 | FenceManagerOpenGL fence_manager; |
| 226 | 228 | ||
| 229 | BlitImageHelper blit_image; | ||
| 230 | |||
| 227 | boost::container::static_vector<u32, MAX_IMAGE_VIEWS> image_view_indices; | 231 | boost::container::static_vector<u32, MAX_IMAGE_VIEWS> image_view_indices; |
| 228 | std::array<ImageViewId, MAX_IMAGE_VIEWS> image_view_ids; | 232 | std::array<ImageViewId, MAX_IMAGE_VIEWS> image_view_ids; |
| 229 | boost::container::static_vector<GLuint, MAX_TEXTURES> sampler_handles; | 233 | boost::container::static_vector<GLuint, MAX_TEXTURES> sampler_handles; |
diff --git a/src/video_core/renderer_opengl/gl_shader_manager.cpp b/src/video_core/renderer_opengl/gl_shader_manager.cpp index d9c29d8b7..98841ae65 100644 --- a/src/video_core/renderer_opengl/gl_shader_manager.cpp +++ b/src/video_core/renderer_opengl/gl_shader_manager.cpp | |||
| @@ -1,2 +1,123 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | |||
| 4 | #include <glad/glad.h> | ||
| 5 | |||
| 6 | #include "video_core/renderer_opengl/gl_shader_manager.h" | ||
| 7 | |||
| 8 | namespace OpenGL { | ||
| 9 | |||
| 10 | static constexpr std::array ASSEMBLY_PROGRAM_ENUMS{ | ||
| 11 | GL_VERTEX_PROGRAM_NV, GL_TESS_CONTROL_PROGRAM_NV, GL_TESS_EVALUATION_PROGRAM_NV, | ||
| 12 | GL_GEOMETRY_PROGRAM_NV, GL_FRAGMENT_PROGRAM_NV, | ||
| 13 | }; | ||
| 14 | |||
| 15 | ProgramManager::ProgramManager(const Device& device) { | ||
| 16 | glCreateProgramPipelines(1, &pipeline.handle); | ||
| 17 | if (device.UseAssemblyShaders()) { | ||
| 18 | glEnable(GL_COMPUTE_PROGRAM_NV); | ||
| 19 | } | ||
| 20 | } | ||
| 21 | |||
| 22 | void ProgramManager::BindComputeProgram(GLuint program) { | ||
| 23 | glUseProgram(program); | ||
| 24 | is_compute_bound = true; | ||
| 25 | } | ||
| 26 | |||
| 27 | void ProgramManager::BindComputeAssemblyProgram(GLuint program) { | ||
| 28 | if (current_assembly_compute_program != program) { | ||
| 29 | current_assembly_compute_program = program; | ||
| 30 | glBindProgramARB(GL_COMPUTE_PROGRAM_NV, program); | ||
| 31 | } | ||
| 32 | UnbindPipeline(); | ||
| 33 | } | ||
| 34 | |||
| 35 | void ProgramManager::BindSourcePrograms(std::span<const OGLProgram, NUM_STAGES> programs) { | ||
| 36 | static constexpr std::array<GLenum, 5> stage_enums{ | ||
| 37 | GL_VERTEX_SHADER_BIT, GL_TESS_CONTROL_SHADER_BIT, GL_TESS_EVALUATION_SHADER_BIT, | ||
| 38 | GL_GEOMETRY_SHADER_BIT, GL_FRAGMENT_SHADER_BIT, | ||
| 39 | }; | ||
| 40 | for (size_t stage = 0; stage < NUM_STAGES; ++stage) { | ||
| 41 | if (current_programs[stage] != programs[stage].handle) { | ||
| 42 | current_programs[stage] = programs[stage].handle; | ||
| 43 | glUseProgramStages(pipeline.handle, stage_enums[stage], programs[stage].handle); | ||
| 44 | } | ||
| 45 | } | ||
| 46 | BindPipeline(); | ||
| 47 | } | ||
| 48 | |||
| 49 | void ProgramManager::BindPresentPrograms(GLuint vertex, GLuint fragment) { | ||
| 50 | if (current_programs[0] != vertex) { | ||
| 51 | current_programs[0] = vertex; | ||
| 52 | glUseProgramStages(pipeline.handle, GL_VERTEX_SHADER_BIT, vertex); | ||
| 53 | } | ||
| 54 | if (current_programs[4] != fragment) { | ||
| 55 | current_programs[4] = fragment; | ||
| 56 | glUseProgramStages(pipeline.handle, GL_FRAGMENT_SHADER_BIT, fragment); | ||
| 57 | } | ||
| 58 | glUseProgramStages( | ||
| 59 | pipeline.handle, | ||
| 60 | GL_TESS_CONTROL_SHADER_BIT | GL_TESS_EVALUATION_SHADER_BIT | GL_GEOMETRY_SHADER_BIT, 0); | ||
| 61 | current_programs[1] = 0; | ||
| 62 | current_programs[2] = 0; | ||
| 63 | current_programs[3] = 0; | ||
| 64 | |||
| 65 | if (current_stage_mask != 0) { | ||
| 66 | current_stage_mask = 0; | ||
| 67 | for (const GLenum program_type : ASSEMBLY_PROGRAM_ENUMS) { | ||
| 68 | glDisable(program_type); | ||
| 69 | } | ||
| 70 | } | ||
| 71 | BindPipeline(); | ||
| 72 | } | ||
| 73 | |||
| 74 | void ProgramManager::BindAssemblyPrograms(std::span<const OGLAssemblyProgram, NUM_STAGES> programs, | ||
| 75 | u32 stage_mask) { | ||
| 76 | const u32 changed_mask = current_stage_mask ^ stage_mask; | ||
| 77 | current_stage_mask = stage_mask; | ||
| 78 | |||
| 79 | if (changed_mask != 0) { | ||
| 80 | for (size_t stage = 0; stage < NUM_STAGES; ++stage) { | ||
| 81 | if (((changed_mask >> stage) & 1) != 0) { | ||
| 82 | if (((stage_mask >> stage) & 1) != 0) { | ||
| 83 | glEnable(ASSEMBLY_PROGRAM_ENUMS[stage]); | ||
| 84 | } else { | ||
| 85 | glDisable(ASSEMBLY_PROGRAM_ENUMS[stage]); | ||
| 86 | } | ||
| 87 | } | ||
| 88 | } | ||
| 89 | } | ||
| 90 | for (size_t stage = 0; stage < NUM_STAGES; ++stage) { | ||
| 91 | if (current_programs[stage] != programs[stage].handle) { | ||
| 92 | current_programs[stage] = programs[stage].handle; | ||
| 93 | glBindProgramARB(ASSEMBLY_PROGRAM_ENUMS[stage], programs[stage].handle); | ||
| 94 | } | ||
| 95 | } | ||
| 96 | UnbindPipeline(); | ||
| 97 | } | ||
| 98 | |||
| 99 | void ProgramManager::RestoreGuestCompute() {} | ||
| 100 | |||
| 101 | void ProgramManager::BindPipeline() { | ||
| 102 | if (!is_pipeline_bound) { | ||
| 103 | is_pipeline_bound = true; | ||
| 104 | glBindProgramPipeline(pipeline.handle); | ||
| 105 | } | ||
| 106 | UnbindCompute(); | ||
| 107 | } | ||
| 108 | |||
| 109 | void ProgramManager::UnbindPipeline() { | ||
| 110 | if (is_pipeline_bound) { | ||
| 111 | is_pipeline_bound = false; | ||
| 112 | glBindProgramPipeline(0); | ||
| 113 | } | ||
| 114 | UnbindCompute(); | ||
| 115 | } | ||
| 116 | |||
| 117 | void ProgramManager::UnbindCompute() { | ||
| 118 | if (is_compute_bound) { | ||
| 119 | is_compute_bound = false; | ||
| 120 | glUseProgram(0); | ||
| 121 | } | ||
| 122 | } | ||
| 123 | } // namespace OpenGL | ||
diff --git a/src/video_core/renderer_opengl/gl_shader_manager.h b/src/video_core/renderer_opengl/gl_shader_manager.h index a84f5aeb3..07ffab77f 100644 --- a/src/video_core/renderer_opengl/gl_shader_manager.h +++ b/src/video_core/renderer_opengl/gl_shader_manager.h | |||
| @@ -6,8 +6,6 @@ | |||
| 6 | #include <array> | 6 | #include <array> |
| 7 | #include <span> | 7 | #include <span> |
| 8 | 8 | ||
| 9 | #include <glad/glad.h> | ||
| 10 | |||
| 11 | #include "video_core/renderer_opengl/gl_device.h" | 9 | #include "video_core/renderer_opengl/gl_device.h" |
| 12 | #include "video_core/renderer_opengl/gl_resource_manager.h" | 10 | #include "video_core/renderer_opengl/gl_resource_manager.h" |
| 13 | 11 | ||
| @@ -16,121 +14,28 @@ namespace OpenGL { | |||
| 16 | class ProgramManager { | 14 | class ProgramManager { |
| 17 | static constexpr size_t NUM_STAGES = 5; | 15 | static constexpr size_t NUM_STAGES = 5; |
| 18 | 16 | ||
| 19 | static constexpr std::array ASSEMBLY_PROGRAM_ENUMS{ | ||
| 20 | GL_VERTEX_PROGRAM_NV, GL_TESS_CONTROL_PROGRAM_NV, GL_TESS_EVALUATION_PROGRAM_NV, | ||
| 21 | GL_GEOMETRY_PROGRAM_NV, GL_FRAGMENT_PROGRAM_NV, | ||
| 22 | }; | ||
| 23 | |||
| 24 | public: | 17 | public: |
| 25 | explicit ProgramManager(const Device& device) { | 18 | explicit ProgramManager(const Device& device); |
| 26 | glCreateProgramPipelines(1, &pipeline.handle); | 19 | |
| 27 | if (device.UseAssemblyShaders()) { | 20 | void BindComputeProgram(GLuint program); |
| 28 | glEnable(GL_COMPUTE_PROGRAM_NV); | 21 | |
| 29 | } | 22 | void BindComputeAssemblyProgram(GLuint program); |
| 30 | } | 23 | |
| 31 | 24 | void BindSourcePrograms(std::span<const OGLProgram, NUM_STAGES> programs); | |
| 32 | void BindComputeProgram(GLuint program) { | 25 | |
| 33 | glUseProgram(program); | 26 | void BindPresentPrograms(GLuint vertex, GLuint fragment); |
| 34 | is_compute_bound = true; | ||
| 35 | } | ||
| 36 | |||
| 37 | void BindComputeAssemblyProgram(GLuint program) { | ||
| 38 | if (current_assembly_compute_program != program) { | ||
| 39 | current_assembly_compute_program = program; | ||
| 40 | glBindProgramARB(GL_COMPUTE_PROGRAM_NV, program); | ||
| 41 | } | ||
| 42 | UnbindPipeline(); | ||
| 43 | } | ||
| 44 | |||
| 45 | void BindSourcePrograms(std::span<const OGLProgram, NUM_STAGES> programs) { | ||
| 46 | static constexpr std::array<GLenum, 5> stage_enums{ | ||
| 47 | GL_VERTEX_SHADER_BIT, GL_TESS_CONTROL_SHADER_BIT, GL_TESS_EVALUATION_SHADER_BIT, | ||
| 48 | GL_GEOMETRY_SHADER_BIT, GL_FRAGMENT_SHADER_BIT, | ||
| 49 | }; | ||
| 50 | for (size_t stage = 0; stage < NUM_STAGES; ++stage) { | ||
| 51 | if (current_programs[stage] != programs[stage].handle) { | ||
| 52 | current_programs[stage] = programs[stage].handle; | ||
| 53 | glUseProgramStages(pipeline.handle, stage_enums[stage], programs[stage].handle); | ||
| 54 | } | ||
| 55 | } | ||
| 56 | BindPipeline(); | ||
| 57 | } | ||
| 58 | |||
| 59 | void BindPresentPrograms(GLuint vertex, GLuint fragment) { | ||
| 60 | if (current_programs[0] != vertex) { | ||
| 61 | current_programs[0] = vertex; | ||
| 62 | glUseProgramStages(pipeline.handle, GL_VERTEX_SHADER_BIT, vertex); | ||
| 63 | } | ||
| 64 | if (current_programs[4] != fragment) { | ||
| 65 | current_programs[4] = fragment; | ||
| 66 | glUseProgramStages(pipeline.handle, GL_FRAGMENT_SHADER_BIT, fragment); | ||
| 67 | } | ||
| 68 | glUseProgramStages( | ||
| 69 | pipeline.handle, | ||
| 70 | GL_TESS_CONTROL_SHADER_BIT | GL_TESS_EVALUATION_SHADER_BIT | GL_GEOMETRY_SHADER_BIT, 0); | ||
| 71 | current_programs[1] = 0; | ||
| 72 | current_programs[2] = 0; | ||
| 73 | current_programs[3] = 0; | ||
| 74 | |||
| 75 | if (current_stage_mask != 0) { | ||
| 76 | current_stage_mask = 0; | ||
| 77 | for (const GLenum program_type : ASSEMBLY_PROGRAM_ENUMS) { | ||
| 78 | glDisable(program_type); | ||
| 79 | } | ||
| 80 | } | ||
| 81 | BindPipeline(); | ||
| 82 | } | ||
| 83 | 27 | ||
| 84 | void BindAssemblyPrograms(std::span<const OGLAssemblyProgram, NUM_STAGES> programs, | 28 | void BindAssemblyPrograms(std::span<const OGLAssemblyProgram, NUM_STAGES> programs, |
| 85 | u32 stage_mask) { | 29 | u32 stage_mask); |
| 86 | const u32 changed_mask = current_stage_mask ^ stage_mask; | 30 | |
| 87 | current_stage_mask = stage_mask; | 31 | void RestoreGuestCompute(); |
| 88 | |||
| 89 | if (changed_mask != 0) { | ||
| 90 | for (size_t stage = 0; stage < NUM_STAGES; ++stage) { | ||
| 91 | if (((changed_mask >> stage) & 1) != 0) { | ||
| 92 | if (((stage_mask >> stage) & 1) != 0) { | ||
| 93 | glEnable(ASSEMBLY_PROGRAM_ENUMS[stage]); | ||
| 94 | } else { | ||
| 95 | glDisable(ASSEMBLY_PROGRAM_ENUMS[stage]); | ||
| 96 | } | ||
| 97 | } | ||
| 98 | } | ||
| 99 | } | ||
| 100 | for (size_t stage = 0; stage < NUM_STAGES; ++stage) { | ||
| 101 | if (current_programs[stage] != programs[stage].handle) { | ||
| 102 | current_programs[stage] = programs[stage].handle; | ||
| 103 | glBindProgramARB(ASSEMBLY_PROGRAM_ENUMS[stage], programs[stage].handle); | ||
| 104 | } | ||
| 105 | } | ||
| 106 | UnbindPipeline(); | ||
| 107 | } | ||
| 108 | |||
| 109 | void RestoreGuestCompute() {} | ||
| 110 | 32 | ||
| 111 | private: | 33 | private: |
| 112 | void BindPipeline() { | 34 | void BindPipeline(); |
| 113 | if (!is_pipeline_bound) { | 35 | |
| 114 | is_pipeline_bound = true; | 36 | void UnbindPipeline(); |
| 115 | glBindProgramPipeline(pipeline.handle); | 37 | |
| 116 | } | 38 | void UnbindCompute(); |
| 117 | UnbindCompute(); | ||
| 118 | } | ||
| 119 | |||
| 120 | void UnbindPipeline() { | ||
| 121 | if (is_pipeline_bound) { | ||
| 122 | is_pipeline_bound = false; | ||
| 123 | glBindProgramPipeline(0); | ||
| 124 | } | ||
| 125 | UnbindCompute(); | ||
| 126 | } | ||
| 127 | |||
| 128 | void UnbindCompute() { | ||
| 129 | if (is_compute_bound) { | ||
| 130 | is_compute_bound = false; | ||
| 131 | glUseProgram(0); | ||
| 132 | } | ||
| 133 | } | ||
| 134 | 39 | ||
| 135 | OGLPipeline pipeline; | 40 | OGLPipeline pipeline; |
| 136 | bool is_pipeline_bound{}; | 41 | bool is_pipeline_bound{}; |
diff --git a/src/video_core/renderer_vulkan/blit_image.cpp b/src/video_core/renderer_vulkan/blit_image.cpp index 3f2b139e0..dd00d3edf 100644 --- a/src/video_core/renderer_vulkan/blit_image.cpp +++ b/src/video_core/renderer_vulkan/blit_image.cpp | |||
| @@ -4,13 +4,13 @@ | |||
| 4 | #include <algorithm> | 4 | #include <algorithm> |
| 5 | 5 | ||
| 6 | #include "common/settings.h" | 6 | #include "common/settings.h" |
| 7 | #include "video_core/host_shaders/blit_color_float_frag_spv.h" | ||
| 7 | #include "video_core/host_shaders/convert_abgr8_to_d24s8_frag_spv.h" | 8 | #include "video_core/host_shaders/convert_abgr8_to_d24s8_frag_spv.h" |
| 8 | #include "video_core/host_shaders/convert_d24s8_to_abgr8_frag_spv.h" | 9 | #include "video_core/host_shaders/convert_d24s8_to_abgr8_frag_spv.h" |
| 9 | #include "video_core/host_shaders/convert_depth_to_float_frag_spv.h" | 10 | #include "video_core/host_shaders/convert_depth_to_float_frag_spv.h" |
| 10 | #include "video_core/host_shaders/convert_float_to_depth_frag_spv.h" | 11 | #include "video_core/host_shaders/convert_float_to_depth_frag_spv.h" |
| 11 | #include "video_core/host_shaders/convert_s8d24_to_abgr8_frag_spv.h" | 12 | #include "video_core/host_shaders/convert_s8d24_to_abgr8_frag_spv.h" |
| 12 | #include "video_core/host_shaders/full_screen_triangle_vert_spv.h" | 13 | #include "video_core/host_shaders/full_screen_triangle_vert_spv.h" |
| 13 | #include "video_core/host_shaders/vulkan_blit_color_float_frag_spv.h" | ||
| 14 | #include "video_core/host_shaders/vulkan_blit_depth_stencil_frag_spv.h" | 14 | #include "video_core/host_shaders/vulkan_blit_depth_stencil_frag_spv.h" |
| 15 | #include "video_core/renderer_vulkan/blit_image.h" | 15 | #include "video_core/renderer_vulkan/blit_image.h" |
| 16 | #include "video_core/renderer_vulkan/maxwell_to_vk.h" | 16 | #include "video_core/renderer_vulkan/maxwell_to_vk.h" |
| @@ -303,7 +303,7 @@ void UpdateTwoTexturesDescriptorSet(const Device& device, VkDescriptorSet descri | |||
| 303 | } | 303 | } |
| 304 | 304 | ||
| 305 | void BindBlitState(vk::CommandBuffer cmdbuf, VkPipelineLayout layout, const Region2D& dst_region, | 305 | void BindBlitState(vk::CommandBuffer cmdbuf, VkPipelineLayout layout, const Region2D& dst_region, |
| 306 | const Region2D& src_region) { | 306 | const Region2D& src_region, const Extent3D& src_size = {1, 1, 1}) { |
| 307 | const VkOffset2D offset{ | 307 | const VkOffset2D offset{ |
| 308 | .x = std::min(dst_region.start.x, dst_region.end.x), | 308 | .x = std::min(dst_region.start.x, dst_region.end.x), |
| 309 | .y = std::min(dst_region.start.y, dst_region.end.y), | 309 | .y = std::min(dst_region.start.y, dst_region.end.y), |
| @@ -325,12 +325,15 @@ void BindBlitState(vk::CommandBuffer cmdbuf, VkPipelineLayout layout, const Regi | |||
| 325 | .offset = offset, | 325 | .offset = offset, |
| 326 | .extent = extent, | 326 | .extent = extent, |
| 327 | }; | 327 | }; |
| 328 | const float scale_x = static_cast<float>(src_region.end.x - src_region.start.x); | 328 | const float scale_x = static_cast<float>(src_region.end.x - src_region.start.x) / |
| 329 | const float scale_y = static_cast<float>(src_region.end.y - src_region.start.y); | 329 | static_cast<float>(src_size.width); |
| 330 | const float scale_y = static_cast<float>(src_region.end.y - src_region.start.y) / | ||
| 331 | static_cast<float>(src_size.height); | ||
| 330 | const PushConstants push_constants{ | 332 | const PushConstants push_constants{ |
| 331 | .tex_scale = {scale_x, scale_y}, | 333 | .tex_scale = {scale_x, scale_y}, |
| 332 | .tex_offset = {static_cast<float>(src_region.start.x), | 334 | .tex_offset = {static_cast<float>(src_region.start.x) / static_cast<float>(src_size.width), |
| 333 | static_cast<float>(src_region.start.y)}, | 335 | static_cast<float>(src_region.start.y) / |
| 336 | static_cast<float>(src_size.height)}, | ||
| 334 | }; | 337 | }; |
| 335 | cmdbuf.SetViewport(0, viewport); | 338 | cmdbuf.SetViewport(0, viewport); |
| 336 | cmdbuf.SetScissor(0, scissor); | 339 | cmdbuf.SetScissor(0, scissor); |
| @@ -347,6 +350,51 @@ VkExtent2D GetConversionExtent(const ImageView& src_image_view) { | |||
| 347 | .height = is_rescaled ? resolution.ScaleUp(height) : height, | 350 | .height = is_rescaled ? resolution.ScaleUp(height) : height, |
| 348 | }; | 351 | }; |
| 349 | } | 352 | } |
| 353 | |||
| 354 | void TransitionImageLayout(vk::CommandBuffer& cmdbuf, VkImage image, VkImageLayout target_layout, | ||
| 355 | VkImageLayout source_layout = VK_IMAGE_LAYOUT_GENERAL) { | ||
| 356 | constexpr VkFlags flags{VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | | ||
| 357 | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT}; | ||
| 358 | const VkImageMemoryBarrier barrier{ | ||
| 359 | .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, | ||
| 360 | .pNext = nullptr, | ||
| 361 | .srcAccessMask = flags, | ||
| 362 | .dstAccessMask = flags, | ||
| 363 | .oldLayout = source_layout, | ||
| 364 | .newLayout = target_layout, | ||
| 365 | .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, | ||
| 366 | .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, | ||
| 367 | .image = image, | ||
| 368 | .subresourceRange{ | ||
| 369 | .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, | ||
| 370 | .baseMipLevel = 0, | ||
| 371 | .levelCount = 1, | ||
| 372 | .baseArrayLayer = 0, | ||
| 373 | .layerCount = 1, | ||
| 374 | }, | ||
| 375 | }; | ||
| 376 | cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, | ||
| 377 | 0, barrier); | ||
| 378 | } | ||
| 379 | |||
| 380 | void BeginRenderPass(vk::CommandBuffer& cmdbuf, const Framebuffer* framebuffer) { | ||
| 381 | const VkRenderPass render_pass = framebuffer->RenderPass(); | ||
| 382 | const VkFramebuffer framebuffer_handle = framebuffer->Handle(); | ||
| 383 | const VkExtent2D render_area = framebuffer->RenderArea(); | ||
| 384 | const VkRenderPassBeginInfo renderpass_bi{ | ||
| 385 | .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, | ||
| 386 | .pNext = nullptr, | ||
| 387 | .renderPass = render_pass, | ||
| 388 | .framebuffer = framebuffer_handle, | ||
| 389 | .renderArea{ | ||
| 390 | .offset{}, | ||
| 391 | .extent = render_area, | ||
| 392 | }, | ||
| 393 | .clearValueCount = 0, | ||
| 394 | .pClearValues = nullptr, | ||
| 395 | }; | ||
| 396 | cmdbuf.BeginRenderPass(renderpass_bi, VK_SUBPASS_CONTENTS_INLINE); | ||
| 397 | } | ||
| 350 | } // Anonymous namespace | 398 | } // Anonymous namespace |
| 351 | 399 | ||
| 352 | BlitImageHelper::BlitImageHelper(const Device& device_, Scheduler& scheduler_, | 400 | BlitImageHelper::BlitImageHelper(const Device& device_, Scheduler& scheduler_, |
| @@ -365,7 +413,7 @@ BlitImageHelper::BlitImageHelper(const Device& device_, Scheduler& scheduler_, | |||
| 365 | two_textures_pipeline_layout(device.GetLogical().CreatePipelineLayout( | 413 | two_textures_pipeline_layout(device.GetLogical().CreatePipelineLayout( |
| 366 | PipelineLayoutCreateInfo(two_textures_set_layout.address()))), | 414 | PipelineLayoutCreateInfo(two_textures_set_layout.address()))), |
| 367 | full_screen_vert(BuildShader(device, FULL_SCREEN_TRIANGLE_VERT_SPV)), | 415 | full_screen_vert(BuildShader(device, FULL_SCREEN_TRIANGLE_VERT_SPV)), |
| 368 | blit_color_to_color_frag(BuildShader(device, VULKAN_BLIT_COLOR_FLOAT_FRAG_SPV)), | 416 | blit_color_to_color_frag(BuildShader(device, BLIT_COLOR_FLOAT_FRAG_SPV)), |
| 369 | blit_depth_stencil_frag(BuildShader(device, VULKAN_BLIT_DEPTH_STENCIL_FRAG_SPV)), | 417 | blit_depth_stencil_frag(BuildShader(device, VULKAN_BLIT_DEPTH_STENCIL_FRAG_SPV)), |
| 370 | convert_depth_to_float_frag(BuildShader(device, CONVERT_DEPTH_TO_FLOAT_FRAG_SPV)), | 418 | convert_depth_to_float_frag(BuildShader(device, CONVERT_DEPTH_TO_FLOAT_FRAG_SPV)), |
| 371 | convert_float_to_depth_frag(BuildShader(device, CONVERT_FLOAT_TO_DEPTH_FRAG_SPV)), | 419 | convert_float_to_depth_frag(BuildShader(device, CONVERT_FLOAT_TO_DEPTH_FRAG_SPV)), |
| @@ -404,6 +452,32 @@ void BlitImageHelper::BlitColor(const Framebuffer* dst_framebuffer, VkImageView | |||
| 404 | scheduler.InvalidateState(); | 452 | scheduler.InvalidateState(); |
| 405 | } | 453 | } |
| 406 | 454 | ||
| 455 | void BlitImageHelper::BlitColor(const Framebuffer* dst_framebuffer, VkImageView src_image_view, | ||
| 456 | VkImage src_image, VkSampler src_sampler, | ||
| 457 | const Region2D& dst_region, const Region2D& src_region, | ||
| 458 | const Extent3D& src_size) { | ||
| 459 | const BlitImagePipelineKey key{ | ||
| 460 | .renderpass = dst_framebuffer->RenderPass(), | ||
| 461 | .operation = Tegra::Engines::Fermi2D::Operation::SrcCopy, | ||
| 462 | }; | ||
| 463 | const VkPipelineLayout layout = *one_texture_pipeline_layout; | ||
| 464 | const VkPipeline pipeline = FindOrEmplaceColorPipeline(key); | ||
| 465 | scheduler.RequestOutsideRenderPassOperationContext(); | ||
| 466 | scheduler.Record([this, dst_framebuffer, src_image_view, src_image, src_sampler, dst_region, | ||
| 467 | src_region, src_size, pipeline, layout](vk::CommandBuffer cmdbuf) { | ||
| 468 | TransitionImageLayout(cmdbuf, src_image, VK_IMAGE_LAYOUT_READ_ONLY_OPTIMAL); | ||
| 469 | BeginRenderPass(cmdbuf, dst_framebuffer); | ||
| 470 | const VkDescriptorSet descriptor_set = one_texture_descriptor_allocator.Commit(); | ||
| 471 | UpdateOneTextureDescriptorSet(device, descriptor_set, src_sampler, src_image_view); | ||
| 472 | cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); | ||
| 473 | cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptor_set, | ||
| 474 | nullptr); | ||
| 475 | BindBlitState(cmdbuf, layout, dst_region, src_region, src_size); | ||
| 476 | cmdbuf.Draw(3, 1, 0, 0); | ||
| 477 | cmdbuf.EndRenderPass(); | ||
| 478 | }); | ||
| 479 | } | ||
| 480 | |||
| 407 | void BlitImageHelper::BlitDepthStencil(const Framebuffer* dst_framebuffer, | 481 | void BlitImageHelper::BlitDepthStencil(const Framebuffer* dst_framebuffer, |
| 408 | VkImageView src_depth_view, VkImageView src_stencil_view, | 482 | VkImageView src_depth_view, VkImageView src_stencil_view, |
| 409 | const Region2D& dst_region, const Region2D& src_region, | 483 | const Region2D& dst_region, const Region2D& src_region, |
diff --git a/src/video_core/renderer_vulkan/blit_image.h b/src/video_core/renderer_vulkan/blit_image.h index 5df679fb4..be8a9a2f6 100644 --- a/src/video_core/renderer_vulkan/blit_image.h +++ b/src/video_core/renderer_vulkan/blit_image.h | |||
| @@ -10,6 +10,8 @@ | |||
| 10 | 10 | ||
| 11 | namespace Vulkan { | 11 | namespace Vulkan { |
| 12 | 12 | ||
| 13 | using VideoCommon::Extent3D; | ||
| 14 | using VideoCommon::Offset2D; | ||
| 13 | using VideoCommon::Region2D; | 15 | using VideoCommon::Region2D; |
| 14 | 16 | ||
| 15 | class Device; | 17 | class Device; |
| @@ -36,6 +38,10 @@ public: | |||
| 36 | Tegra::Engines::Fermi2D::Filter filter, | 38 | Tegra::Engines::Fermi2D::Filter filter, |
| 37 | Tegra::Engines::Fermi2D::Operation operation); | 39 | Tegra::Engines::Fermi2D::Operation operation); |
| 38 | 40 | ||
| 41 | void BlitColor(const Framebuffer* dst_framebuffer, VkImageView src_image_view, | ||
| 42 | VkImage src_image, VkSampler src_sampler, const Region2D& dst_region, | ||
| 43 | const Region2D& src_region, const Extent3D& src_size); | ||
| 44 | |||
| 39 | void BlitDepthStencil(const Framebuffer* dst_framebuffer, VkImageView src_depth_view, | 45 | void BlitDepthStencil(const Framebuffer* dst_framebuffer, VkImageView src_depth_view, |
| 40 | VkImageView src_stencil_view, const Region2D& dst_region, | 46 | VkImageView src_stencil_view, const Region2D& dst_region, |
| 41 | const Region2D& src_region, Tegra::Engines::Fermi2D::Filter filter, | 47 | const Region2D& src_region, Tegra::Engines::Fermi2D::Filter filter, |
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index b75b8eec6..86ef0daeb 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp | |||
| @@ -266,6 +266,35 @@ void RasterizerVulkan::DrawIndirect() { | |||
| 266 | buffer_cache.SetDrawIndirect(nullptr); | 266 | buffer_cache.SetDrawIndirect(nullptr); |
| 267 | } | 267 | } |
| 268 | 268 | ||
| 269 | void RasterizerVulkan::DrawTexture() { | ||
| 270 | MICROPROFILE_SCOPE(Vulkan_Drawing); | ||
| 271 | |||
| 272 | SCOPE_EXIT({ gpu.TickWork(); }); | ||
| 273 | FlushWork(); | ||
| 274 | |||
| 275 | query_cache.UpdateCounters(); | ||
| 276 | |||
| 277 | texture_cache.SynchronizeGraphicsDescriptors(); | ||
| 278 | texture_cache.UpdateRenderTargets(false); | ||
| 279 | |||
| 280 | UpdateDynamicStates(); | ||
| 281 | |||
| 282 | const auto& draw_texture_state = maxwell3d->draw_manager->GetDrawTextureState(); | ||
| 283 | const auto& sampler = texture_cache.GetGraphicsSampler(draw_texture_state.src_sampler); | ||
| 284 | const auto& texture = texture_cache.GetImageView(draw_texture_state.src_texture); | ||
| 285 | Region2D dst_region = {Offset2D{.x = static_cast<s32>(draw_texture_state.dst_x0), | ||
| 286 | .y = static_cast<s32>(draw_texture_state.dst_y0)}, | ||
| 287 | Offset2D{.x = static_cast<s32>(draw_texture_state.dst_x1), | ||
| 288 | .y = static_cast<s32>(draw_texture_state.dst_y1)}}; | ||
| 289 | Region2D src_region = {Offset2D{.x = static_cast<s32>(draw_texture_state.src_x0), | ||
| 290 | .y = static_cast<s32>(draw_texture_state.src_y0)}, | ||
| 291 | Offset2D{.x = static_cast<s32>(draw_texture_state.src_x1), | ||
| 292 | .y = static_cast<s32>(draw_texture_state.src_y1)}}; | ||
| 293 | blit_image.BlitColor(texture_cache.GetFramebuffer(), texture.RenderTarget(), | ||
| 294 | texture.ImageHandle(), sampler->Handle(), dst_region, src_region, | ||
| 295 | texture.size); | ||
| 296 | } | ||
| 297 | |||
| 269 | void RasterizerVulkan::Clear(u32 layer_count) { | 298 | void RasterizerVulkan::Clear(u32 layer_count) { |
| 270 | MICROPROFILE_SCOPE(Vulkan_Clearing); | 299 | MICROPROFILE_SCOPE(Vulkan_Clearing); |
| 271 | 300 | ||
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index 472cc64d9..a0508b57c 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h | |||
| @@ -66,6 +66,7 @@ public: | |||
| 66 | 66 | ||
| 67 | void Draw(bool is_indexed, u32 instance_count) override; | 67 | void Draw(bool is_indexed, u32 instance_count) override; |
| 68 | void DrawIndirect() override; | 68 | void DrawIndirect() override; |
| 69 | void DrawTexture() override; | ||
| 69 | void Clear(u32 layer_count) override; | 70 | void Clear(u32 layer_count) override; |
| 70 | void DispatchCompute() override; | 71 | void DispatchCompute() override; |
| 71 | void ResetCounter(VideoCore::QueryType type) override; | 72 | void ResetCounter(VideoCore::QueryType type) override; |
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index 87152c8e9..1b01990a4 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h | |||
| @@ -149,6 +149,13 @@ typename P::ImageView& TextureCache<P>::GetImageView(ImageViewId id) noexcept { | |||
| 149 | } | 149 | } |
| 150 | 150 | ||
| 151 | template <class P> | 151 | template <class P> |
| 152 | typename P::ImageView& TextureCache<P>::GetImageView(u32 index) noexcept { | ||
| 153 | const auto image_view_id = VisitImageView(channel_state->graphics_image_table, | ||
| 154 | channel_state->graphics_image_view_ids, index); | ||
| 155 | return slot_image_views[image_view_id]; | ||
| 156 | } | ||
| 157 | |||
| 158 | template <class P> | ||
| 152 | void TextureCache<P>::MarkModification(ImageId id) noexcept { | 159 | void TextureCache<P>::MarkModification(ImageId id) noexcept { |
| 153 | MarkModification(slot_images[id]); | 160 | MarkModification(slot_images[id]); |
| 154 | } | 161 | } |
diff --git a/src/video_core/texture_cache/texture_cache_base.h b/src/video_core/texture_cache/texture_cache_base.h index 4eea1f609..485eaabaa 100644 --- a/src/video_core/texture_cache/texture_cache_base.h +++ b/src/video_core/texture_cache/texture_cache_base.h | |||
| @@ -129,6 +129,9 @@ public: | |||
| 129 | /// Return a reference to the given image view id | 129 | /// Return a reference to the given image view id |
| 130 | [[nodiscard]] ImageView& GetImageView(ImageViewId id) noexcept; | 130 | [[nodiscard]] ImageView& GetImageView(ImageViewId id) noexcept; |
| 131 | 131 | ||
| 132 | /// Get the imageview from the graphics descriptor table in the specified index | ||
| 133 | [[nodiscard]] ImageView& GetImageView(u32 index) noexcept; | ||
| 134 | |||
| 132 | /// Mark an image as modified from the GPU | 135 | /// Mark an image as modified from the GPU |
| 133 | void MarkModification(ImageId id) noexcept; | 136 | void MarkModification(ImageId id) noexcept; |
| 134 | 137 | ||
diff --git a/src/yuzu/configuration/configure_graphics.ui b/src/yuzu/configuration/configure_graphics.ui index aa02cc63c..bb9910a53 100644 --- a/src/yuzu/configuration/configure_graphics.ui +++ b/src/yuzu/configuration/configure_graphics.ui | |||
| @@ -366,6 +366,11 @@ | |||
| 366 | </item> | 366 | </item> |
| 367 | <item> | 367 | <item> |
| 368 | <property name="text"> | 368 | <property name="text"> |
| 369 | <string>1.5X (1080p/1620p) [EXPERIMENTAL]</string> | ||
| 370 | </property> | ||
| 371 | </item> | ||
| 372 | <item> | ||
| 373 | <property name="text"> | ||
| 369 | <string>2X (1440p/2160p)</string> | 374 | <string>2X (1440p/2160p)</string> |
| 370 | </property> | 375 | </property> |
| 371 | </item> | 376 | </item> |
| @@ -389,6 +394,16 @@ | |||
| 389 | <string>6X (4320p/6480p)</string> | 394 | <string>6X (4320p/6480p)</string> |
| 390 | </property> | 395 | </property> |
| 391 | </item> | 396 | </item> |
| 397 | <item> | ||
| 398 | <property name="text"> | ||
| 399 | <string>7X (5040p/7560p)</string> | ||
| 400 | </property> | ||
| 401 | </item> | ||
| 402 | <item> | ||
| 403 | <property name="text"> | ||
| 404 | <string>8X (5760p/8640p)</string> | ||
| 405 | </property> | ||
| 406 | </item> | ||
| 392 | </widget> | 407 | </widget> |
| 393 | </item> | 408 | </item> |
| 394 | </layout> | 409 | </layout> |