diff options
26 files changed, 529 insertions, 52 deletions
diff --git a/src/core/core.cpp b/src/core/core.cpp index d741ef90d..eba2177d1 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp | |||
| @@ -36,7 +36,8 @@ | |||
| 36 | #include "frontend/applets/software_keyboard.h" | 36 | #include "frontend/applets/software_keyboard.h" |
| 37 | #include "frontend/applets/web_browser.h" | 37 | #include "frontend/applets/web_browser.h" |
| 38 | #include "video_core/debug_utils/debug_utils.h" | 38 | #include "video_core/debug_utils/debug_utils.h" |
| 39 | #include "video_core/gpu.h" | 39 | #include "video_core/gpu_asynch.h" |
| 40 | #include "video_core/gpu_synch.h" | ||
| 40 | #include "video_core/renderer_base.h" | 41 | #include "video_core/renderer_base.h" |
| 41 | #include "video_core/video_core.h" | 42 | #include "video_core/video_core.h" |
| 42 | 43 | ||
| @@ -129,10 +130,16 @@ struct System::Impl { | |||
| 129 | return ResultStatus::ErrorVideoCore; | 130 | return ResultStatus::ErrorVideoCore; |
| 130 | } | 131 | } |
| 131 | 132 | ||
| 132 | gpu_core = std::make_unique<Tegra::GPU>(system, renderer->Rasterizer()); | 133 | is_powered_on = true; |
| 134 | |||
| 135 | if (Settings::values.use_asynchronous_gpu_emulation) { | ||
| 136 | gpu_core = std::make_unique<VideoCommon::GPUAsynch>(system, *renderer); | ||
| 137 | } else { | ||
| 138 | gpu_core = std::make_unique<VideoCommon::GPUSynch>(system, *renderer); | ||
| 139 | } | ||
| 133 | 140 | ||
| 134 | cpu_core_manager.Initialize(system); | 141 | cpu_core_manager.Initialize(system); |
| 135 | is_powered_on = true; | 142 | |
| 136 | LOG_DEBUG(Core, "Initialized OK"); | 143 | LOG_DEBUG(Core, "Initialized OK"); |
| 137 | 144 | ||
| 138 | // Reset counters and set time origin to current frame | 145 | // Reset counters and set time origin to current frame |
diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp index dbe7ee6e8..20c7c39aa 100644 --- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp +++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp | |||
| @@ -36,7 +36,7 @@ void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u3 | |||
| 36 | 36 | ||
| 37 | auto& instance = Core::System::GetInstance(); | 37 | auto& instance = Core::System::GetInstance(); |
| 38 | instance.GetPerfStats().EndGameFrame(); | 38 | instance.GetPerfStats().EndGameFrame(); |
| 39 | instance.Renderer().SwapBuffers(framebuffer); | 39 | instance.GPU().SwapBuffers(framebuffer); |
| 40 | } | 40 | } |
| 41 | 41 | ||
| 42 | } // namespace Service::Nvidia::Devices | 42 | } // namespace Service::Nvidia::Devices |
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp index 466db7ccd..a34b9e753 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp | |||
| @@ -178,7 +178,7 @@ u32 nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& ou | |||
| 178 | auto& gpu = system_instance.GPU(); | 178 | auto& gpu = system_instance.GPU(); |
| 179 | auto cpu_addr = gpu.MemoryManager().GpuToCpuAddress(params.offset); | 179 | auto cpu_addr = gpu.MemoryManager().GpuToCpuAddress(params.offset); |
| 180 | ASSERT(cpu_addr); | 180 | ASSERT(cpu_addr); |
| 181 | system_instance.Renderer().Rasterizer().FlushAndInvalidateRegion(*cpu_addr, itr->second.size); | 181 | gpu.FlushAndInvalidateRegion(*cpu_addr, itr->second.size); |
| 182 | 182 | ||
| 183 | params.offset = gpu.MemoryManager().UnmapBuffer(params.offset, itr->second.size); | 183 | params.offset = gpu.MemoryManager().UnmapBuffer(params.offset, itr->second.size); |
| 184 | 184 | ||
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp index 0a650f36c..8ce7bc7a5 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp | |||
| @@ -136,16 +136,6 @@ u32 nvhost_gpu::AllocateObjectContext(const std::vector<u8>& input, std::vector< | |||
| 136 | return 0; | 136 | return 0; |
| 137 | } | 137 | } |
| 138 | 138 | ||
| 139 | static void PushGPUEntries(Tegra::CommandList&& entries) { | ||
| 140 | if (entries.empty()) { | ||
| 141 | return; | ||
| 142 | } | ||
| 143 | |||
| 144 | auto& dma_pusher{Core::System::GetInstance().GPU().DmaPusher()}; | ||
| 145 | dma_pusher.Push(std::move(entries)); | ||
| 146 | dma_pusher.DispatchCalls(); | ||
| 147 | } | ||
| 148 | |||
| 149 | u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& output) { | 139 | u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& output) { |
| 150 | if (input.size() < sizeof(IoctlSubmitGpfifo)) { | 140 | if (input.size() < sizeof(IoctlSubmitGpfifo)) { |
| 151 | UNIMPLEMENTED(); | 141 | UNIMPLEMENTED(); |
| @@ -163,7 +153,7 @@ u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& outp | |||
| 163 | std::memcpy(entries.data(), &input[sizeof(IoctlSubmitGpfifo)], | 153 | std::memcpy(entries.data(), &input[sizeof(IoctlSubmitGpfifo)], |
| 164 | params.num_entries * sizeof(Tegra::CommandListHeader)); | 154 | params.num_entries * sizeof(Tegra::CommandListHeader)); |
| 165 | 155 | ||
| 166 | PushGPUEntries(std::move(entries)); | 156 | Core::System::GetInstance().GPU().PushGPUEntries(std::move(entries)); |
| 167 | 157 | ||
| 168 | params.fence_out.id = 0; | 158 | params.fence_out.id = 0; |
| 169 | params.fence_out.value = 0; | 159 | params.fence_out.value = 0; |
| @@ -184,7 +174,7 @@ u32 nvhost_gpu::KickoffPB(const std::vector<u8>& input, std::vector<u8>& output) | |||
| 184 | Memory::ReadBlock(params.address, entries.data(), | 174 | Memory::ReadBlock(params.address, entries.data(), |
| 185 | params.num_entries * sizeof(Tegra::CommandListHeader)); | 175 | params.num_entries * sizeof(Tegra::CommandListHeader)); |
| 186 | 176 | ||
| 187 | PushGPUEntries(std::move(entries)); | 177 | Core::System::GetInstance().GPU().PushGPUEntries(std::move(entries)); |
| 188 | 178 | ||
| 189 | params.fence_out.id = 0; | 179 | params.fence_out.id = 0; |
| 190 | params.fence_out.value = 0; | 180 | params.fence_out.value = 0; |
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp index 56f31e2ac..fc496b654 100644 --- a/src/core/hle/service/nvflinger/nvflinger.cpp +++ b/src/core/hle/service/nvflinger/nvflinger.cpp | |||
| @@ -186,7 +186,7 @@ void NVFlinger::Compose() { | |||
| 186 | 186 | ||
| 187 | // There was no queued buffer to draw, render previous frame | 187 | // There was no queued buffer to draw, render previous frame |
| 188 | system_instance.GetPerfStats().EndGameFrame(); | 188 | system_instance.GetPerfStats().EndGameFrame(); |
| 189 | system_instance.Renderer().SwapBuffers({}); | 189 | system_instance.GPU().SwapBuffers({}); |
| 190 | continue; | 190 | continue; |
| 191 | } | 191 | } |
| 192 | 192 | ||
diff --git a/src/core/memory.cpp b/src/core/memory.cpp index ec279cef8..6591c45d2 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp | |||
| @@ -356,16 +356,16 @@ void RasterizerFlushVirtualRegion(VAddr start, u64 size, FlushMode mode) { | |||
| 356 | const VAddr overlap_end = std::min(end, region_end); | 356 | const VAddr overlap_end = std::min(end, region_end); |
| 357 | const VAddr overlap_size = overlap_end - overlap_start; | 357 | const VAddr overlap_size = overlap_end - overlap_start; |
| 358 | 358 | ||
| 359 | auto& rasterizer = system_instance.Renderer().Rasterizer(); | 359 | auto& gpu = system_instance.GPU(); |
| 360 | switch (mode) { | 360 | switch (mode) { |
| 361 | case FlushMode::Flush: | 361 | case FlushMode::Flush: |
| 362 | rasterizer.FlushRegion(overlap_start, overlap_size); | 362 | gpu.FlushRegion(overlap_start, overlap_size); |
| 363 | break; | 363 | break; |
| 364 | case FlushMode::Invalidate: | 364 | case FlushMode::Invalidate: |
| 365 | rasterizer.InvalidateRegion(overlap_start, overlap_size); | 365 | gpu.InvalidateRegion(overlap_start, overlap_size); |
| 366 | break; | 366 | break; |
| 367 | case FlushMode::FlushAndInvalidate: | 367 | case FlushMode::FlushAndInvalidate: |
| 368 | rasterizer.FlushAndInvalidateRegion(overlap_start, overlap_size); | 368 | gpu.FlushAndInvalidateRegion(overlap_start, overlap_size); |
| 369 | break; | 369 | break; |
| 370 | } | 370 | } |
| 371 | }; | 371 | }; |
diff --git a/src/core/settings.h b/src/core/settings.h index 7e76e0466..cdfb2f742 100644 --- a/src/core/settings.h +++ b/src/core/settings.h | |||
| @@ -393,6 +393,7 @@ struct Values { | |||
| 393 | u16 frame_limit; | 393 | u16 frame_limit; |
| 394 | bool use_disk_shader_cache; | 394 | bool use_disk_shader_cache; |
| 395 | bool use_accurate_gpu_emulation; | 395 | bool use_accurate_gpu_emulation; |
| 396 | bool use_asynchronous_gpu_emulation; | ||
| 396 | 397 | ||
| 397 | float bg_red; | 398 | float bg_red; |
| 398 | float bg_green; | 399 | float bg_green; |
diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp index 58dfcc4df..e1db06811 100644 --- a/src/core/telemetry_session.cpp +++ b/src/core/telemetry_session.cpp | |||
| @@ -162,6 +162,8 @@ TelemetrySession::TelemetrySession() { | |||
| 162 | Settings::values.use_disk_shader_cache); | 162 | Settings::values.use_disk_shader_cache); |
| 163 | AddField(Telemetry::FieldType::UserConfig, "Renderer_UseAccurateGpuEmulation", | 163 | AddField(Telemetry::FieldType::UserConfig, "Renderer_UseAccurateGpuEmulation", |
| 164 | Settings::values.use_accurate_gpu_emulation); | 164 | Settings::values.use_accurate_gpu_emulation); |
| 165 | AddField(Telemetry::FieldType::UserConfig, "Renderer_UseAsynchronousGpuEmulation", | ||
| 166 | Settings::values.use_asynchronous_gpu_emulation); | ||
| 165 | AddField(Telemetry::FieldType::UserConfig, "System_UseDockedMode", | 167 | AddField(Telemetry::FieldType::UserConfig, "System_UseDockedMode", |
| 166 | Settings::values.use_docked_mode); | 168 | Settings::values.use_docked_mode); |
| 167 | } | 169 | } |
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index c1ae83f4d..57f31cd58 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt | |||
| @@ -17,6 +17,12 @@ add_library(video_core STATIC | |||
| 17 | engines/shader_header.h | 17 | engines/shader_header.h |
| 18 | gpu.cpp | 18 | gpu.cpp |
| 19 | gpu.h | 19 | gpu.h |
| 20 | gpu_asynch.cpp | ||
| 21 | gpu_asynch.h | ||
| 22 | gpu_synch.cpp | ||
| 23 | gpu_synch.h | ||
| 24 | gpu_thread.cpp | ||
| 25 | gpu_thread.h | ||
| 20 | macro_interpreter.cpp | 26 | macro_interpreter.cpp |
| 21 | macro_interpreter.h | 27 | macro_interpreter.h |
| 22 | memory_manager.cpp | 28 | memory_manager.cpp |
diff --git a/src/video_core/engines/kepler_memory.cpp b/src/video_core/engines/kepler_memory.cpp index 4f6126116..aae2a4019 100644 --- a/src/video_core/engines/kepler_memory.cpp +++ b/src/video_core/engines/kepler_memory.cpp | |||
| @@ -48,7 +48,7 @@ void KeplerMemory::ProcessData(u32 data) { | |||
| 48 | // We have to invalidate the destination region to evict any outdated surfaces from the cache. | 48 | // We have to invalidate the destination region to evict any outdated surfaces from the cache. |
| 49 | // We do this before actually writing the new data because the destination address might contain | 49 | // We do this before actually writing the new data because the destination address might contain |
| 50 | // a dirty surface that will have to be written back to memory. | 50 | // a dirty surface that will have to be written back to memory. |
| 51 | rasterizer.InvalidateRegion(*dest_address, sizeof(u32)); | 51 | Core::System::GetInstance().GPU().InvalidateRegion(*dest_address, sizeof(u32)); |
| 52 | 52 | ||
| 53 | Memory::Write32(*dest_address, data); | 53 | Memory::Write32(*dest_address, data); |
| 54 | system.GPU().Maxwell3D().dirty_flags.OnMemoryWrite(); | 54 | system.GPU().Maxwell3D().dirty_flags.OnMemoryWrite(); |
diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp index 0474c7ba3..9dfea5999 100644 --- a/src/video_core/engines/maxwell_dma.cpp +++ b/src/video_core/engines/maxwell_dma.cpp | |||
| @@ -92,12 +92,12 @@ void MaxwellDMA::HandleCopy() { | |||
| 92 | const auto FlushAndInvalidate = [&](u32 src_size, u64 dst_size) { | 92 | const auto FlushAndInvalidate = [&](u32 src_size, u64 dst_size) { |
| 93 | // TODO(Subv): For now, manually flush the regions until we implement GPU-accelerated | 93 | // TODO(Subv): For now, manually flush the regions until we implement GPU-accelerated |
| 94 | // copying. | 94 | // copying. |
| 95 | rasterizer.FlushRegion(*source_cpu, src_size); | 95 | Core::System::GetInstance().GPU().FlushRegion(*source_cpu, src_size); |
| 96 | 96 | ||
| 97 | // We have to invalidate the destination region to evict any outdated surfaces from the | 97 | // We have to invalidate the destination region to evict any outdated surfaces from the |
| 98 | // cache. We do this before actually writing the new data because the destination address | 98 | // cache. We do this before actually writing the new data because the destination address |
| 99 | // might contain a dirty surface that will have to be written back to memory. | 99 | // might contain a dirty surface that will have to be written back to memory. |
| 100 | rasterizer.InvalidateRegion(*dest_cpu, dst_size); | 100 | Core::System::GetInstance().GPU().InvalidateRegion(*dest_cpu, dst_size); |
| 101 | }; | 101 | }; |
| 102 | 102 | ||
| 103 | if (regs.exec.is_dst_linear && !regs.exec.is_src_linear) { | 103 | if (regs.exec.is_dst_linear && !regs.exec.is_src_linear) { |
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp index ac30d1a89..08abf8ac9 100644 --- a/src/video_core/gpu.cpp +++ b/src/video_core/gpu.cpp | |||
| @@ -12,7 +12,7 @@ | |||
| 12 | #include "video_core/engines/maxwell_3d.h" | 12 | #include "video_core/engines/maxwell_3d.h" |
| 13 | #include "video_core/engines/maxwell_dma.h" | 13 | #include "video_core/engines/maxwell_dma.h" |
| 14 | #include "video_core/gpu.h" | 14 | #include "video_core/gpu.h" |
| 15 | #include "video_core/rasterizer_interface.h" | 15 | #include "video_core/renderer_base.h" |
| 16 | 16 | ||
| 17 | namespace Tegra { | 17 | namespace Tegra { |
| 18 | 18 | ||
| @@ -28,7 +28,8 @@ u32 FramebufferConfig::BytesPerPixel(PixelFormat format) { | |||
| 28 | UNREACHABLE(); | 28 | UNREACHABLE(); |
| 29 | } | 29 | } |
| 30 | 30 | ||
| 31 | GPU::GPU(Core::System& system, VideoCore::RasterizerInterface& rasterizer) { | 31 | GPU::GPU(Core::System& system, VideoCore::RendererBase& renderer) : renderer{renderer} { |
| 32 | auto& rasterizer{renderer.Rasterizer()}; | ||
| 32 | memory_manager = std::make_unique<Tegra::MemoryManager>(); | 33 | memory_manager = std::make_unique<Tegra::MemoryManager>(); |
| 33 | dma_pusher = std::make_unique<Tegra::DmaPusher>(*this); | 34 | dma_pusher = std::make_unique<Tegra::DmaPusher>(*this); |
| 34 | maxwell_3d = std::make_unique<Engines::Maxwell3D>(system, rasterizer, *memory_manager); | 35 | maxwell_3d = std::make_unique<Engines::Maxwell3D>(system, rasterizer, *memory_manager); |
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h index 6313702f2..14a421cc1 100644 --- a/src/video_core/gpu.h +++ b/src/video_core/gpu.h | |||
| @@ -16,8 +16,8 @@ class System; | |||
| 16 | } | 16 | } |
| 17 | 17 | ||
| 18 | namespace VideoCore { | 18 | namespace VideoCore { |
| 19 | class RasterizerInterface; | 19 | class RendererBase; |
| 20 | } | 20 | } // namespace VideoCore |
| 21 | 21 | ||
| 22 | namespace Tegra { | 22 | namespace Tegra { |
| 23 | 23 | ||
| @@ -119,9 +119,10 @@ enum class EngineID { | |||
| 119 | MAXWELL_DMA_COPY_A = 0xB0B5, | 119 | MAXWELL_DMA_COPY_A = 0xB0B5, |
| 120 | }; | 120 | }; |
| 121 | 121 | ||
| 122 | class GPU final { | 122 | class GPU { |
| 123 | public: | 123 | public: |
| 124 | explicit GPU(Core::System& system, VideoCore::RasterizerInterface& rasterizer); | 124 | explicit GPU(Core::System& system, VideoCore::RendererBase& renderer); |
| 125 | |||
| 125 | ~GPU(); | 126 | ~GPU(); |
| 126 | 127 | ||
| 127 | struct MethodCall { | 128 | struct MethodCall { |
| @@ -200,8 +201,42 @@ public: | |||
| 200 | }; | 201 | }; |
| 201 | } regs{}; | 202 | } regs{}; |
| 202 | 203 | ||
| 204 | /// Push GPU command entries to be processed | ||
| 205 | virtual void PushGPUEntries(Tegra::CommandList&& entries) = 0; | ||
| 206 | |||
| 207 | /// Swap buffers (render frame) | ||
| 208 | virtual void SwapBuffers( | ||
| 209 | std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer) = 0; | ||
| 210 | |||
| 211 | /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory | ||
| 212 | virtual void FlushRegion(VAddr addr, u64 size) = 0; | ||
| 213 | |||
| 214 | /// Notify rasterizer that any caches of the specified region should be invalidated | ||
| 215 | virtual void InvalidateRegion(VAddr addr, u64 size) = 0; | ||
| 216 | |||
| 217 | /// Notify rasterizer that any caches of the specified region should be flushed and invalidated | ||
| 218 | virtual void FlushAndInvalidateRegion(VAddr addr, u64 size) = 0; | ||
| 219 | |||
| 203 | private: | 220 | private: |
| 221 | void ProcessBindMethod(const MethodCall& method_call); | ||
| 222 | void ProcessSemaphoreTriggerMethod(); | ||
| 223 | void ProcessSemaphoreRelease(); | ||
| 224 | void ProcessSemaphoreAcquire(); | ||
| 225 | |||
| 226 | /// Calls a GPU puller method. | ||
| 227 | void CallPullerMethod(const MethodCall& method_call); | ||
| 228 | |||
| 229 | /// Calls a GPU engine method. | ||
| 230 | void CallEngineMethod(const MethodCall& method_call); | ||
| 231 | |||
| 232 | /// Determines where the method should be executed. | ||
| 233 | bool ExecuteMethodOnEngine(const MethodCall& method_call); | ||
| 234 | |||
| 235 | protected: | ||
| 204 | std::unique_ptr<Tegra::DmaPusher> dma_pusher; | 236 | std::unique_ptr<Tegra::DmaPusher> dma_pusher; |
| 237 | VideoCore::RendererBase& renderer; | ||
| 238 | |||
| 239 | private: | ||
| 205 | std::unique_ptr<Tegra::MemoryManager> memory_manager; | 240 | std::unique_ptr<Tegra::MemoryManager> memory_manager; |
| 206 | 241 | ||
| 207 | /// Mapping of command subchannels to their bound engine ids. | 242 | /// Mapping of command subchannels to their bound engine ids. |
| @@ -217,18 +252,6 @@ private: | |||
| 217 | std::unique_ptr<Engines::MaxwellDMA> maxwell_dma; | 252 | std::unique_ptr<Engines::MaxwellDMA> maxwell_dma; |
| 218 | /// Inline memory engine | 253 | /// Inline memory engine |
| 219 | std::unique_ptr<Engines::KeplerMemory> kepler_memory; | 254 | std::unique_ptr<Engines::KeplerMemory> kepler_memory; |
| 220 | |||
| 221 | void ProcessBindMethod(const MethodCall& method_call); | ||
| 222 | void ProcessSemaphoreTriggerMethod(); | ||
| 223 | void ProcessSemaphoreRelease(); | ||
| 224 | void ProcessSemaphoreAcquire(); | ||
| 225 | |||
| 226 | // Calls a GPU puller method. | ||
| 227 | void CallPullerMethod(const MethodCall& method_call); | ||
| 228 | // Calls a GPU engine method. | ||
| 229 | void CallEngineMethod(const MethodCall& method_call); | ||
| 230 | // Determines where the method should be executed. | ||
| 231 | bool ExecuteMethodOnEngine(const MethodCall& method_call); | ||
| 232 | }; | 255 | }; |
| 233 | 256 | ||
| 234 | #define ASSERT_REG_POSITION(field_name, position) \ | 257 | #define ASSERT_REG_POSITION(field_name, position) \ |
diff --git a/src/video_core/gpu_asynch.cpp b/src/video_core/gpu_asynch.cpp new file mode 100644 index 000000000..ad0a747e3 --- /dev/null +++ b/src/video_core/gpu_asynch.cpp | |||
| @@ -0,0 +1,37 @@ | |||
| 1 | // Copyright 2019 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "video_core/gpu_asynch.h" | ||
| 6 | #include "video_core/gpu_thread.h" | ||
| 7 | #include "video_core/renderer_base.h" | ||
| 8 | |||
| 9 | namespace VideoCommon { | ||
| 10 | |||
| 11 | GPUAsynch::GPUAsynch(Core::System& system, VideoCore::RendererBase& renderer) | ||
| 12 | : Tegra::GPU(system, renderer), gpu_thread{renderer, *dma_pusher} {} | ||
| 13 | |||
| 14 | GPUAsynch::~GPUAsynch() = default; | ||
| 15 | |||
| 16 | void GPUAsynch::PushGPUEntries(Tegra::CommandList&& entries) { | ||
| 17 | gpu_thread.SubmitList(std::move(entries)); | ||
| 18 | } | ||
| 19 | |||
| 20 | void GPUAsynch::SwapBuffers( | ||
| 21 | std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer) { | ||
| 22 | gpu_thread.SwapBuffers(std::move(framebuffer)); | ||
| 23 | } | ||
| 24 | |||
| 25 | void GPUAsynch::FlushRegion(VAddr addr, u64 size) { | ||
| 26 | gpu_thread.FlushRegion(addr, size); | ||
| 27 | } | ||
| 28 | |||
| 29 | void GPUAsynch::InvalidateRegion(VAddr addr, u64 size) { | ||
| 30 | gpu_thread.InvalidateRegion(addr, size); | ||
| 31 | } | ||
| 32 | |||
| 33 | void GPUAsynch::FlushAndInvalidateRegion(VAddr addr, u64 size) { | ||
| 34 | gpu_thread.FlushAndInvalidateRegion(addr, size); | ||
| 35 | } | ||
| 36 | |||
| 37 | } // namespace VideoCommon | ||
diff --git a/src/video_core/gpu_asynch.h b/src/video_core/gpu_asynch.h new file mode 100644 index 000000000..58046f3e9 --- /dev/null +++ b/src/video_core/gpu_asynch.h | |||
| @@ -0,0 +1,37 @@ | |||
| 1 | // Copyright 2019 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "video_core/gpu.h" | ||
| 8 | #include "video_core/gpu_thread.h" | ||
| 9 | |||
| 10 | namespace VideoCore { | ||
| 11 | class RendererBase; | ||
| 12 | } // namespace VideoCore | ||
| 13 | |||
| 14 | namespace VideoCommon { | ||
| 15 | |||
| 16 | namespace GPUThread { | ||
| 17 | class ThreadManager; | ||
| 18 | } // namespace GPUThread | ||
| 19 | |||
| 20 | /// Implementation of GPU interface that runs the GPU asynchronously | ||
| 21 | class GPUAsynch : public Tegra::GPU { | ||
| 22 | public: | ||
| 23 | explicit GPUAsynch(Core::System& system, VideoCore::RendererBase& renderer); | ||
| 24 | ~GPUAsynch(); | ||
| 25 | |||
| 26 | void PushGPUEntries(Tegra::CommandList&& entries) override; | ||
| 27 | void SwapBuffers( | ||
| 28 | std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer) override; | ||
| 29 | void FlushRegion(VAddr addr, u64 size) override; | ||
| 30 | void InvalidateRegion(VAddr addr, u64 size) override; | ||
| 31 | void FlushAndInvalidateRegion(VAddr addr, u64 size) override; | ||
| 32 | |||
| 33 | private: | ||
| 34 | GPUThread::ThreadManager gpu_thread; | ||
| 35 | }; | ||
| 36 | |||
| 37 | } // namespace VideoCommon | ||
diff --git a/src/video_core/gpu_synch.cpp b/src/video_core/gpu_synch.cpp new file mode 100644 index 000000000..4c00b96c7 --- /dev/null +++ b/src/video_core/gpu_synch.cpp | |||
| @@ -0,0 +1,37 @@ | |||
| 1 | // Copyright 2019 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "video_core/gpu_synch.h" | ||
| 6 | #include "video_core/renderer_base.h" | ||
| 7 | |||
| 8 | namespace VideoCommon { | ||
| 9 | |||
| 10 | GPUSynch::GPUSynch(Core::System& system, VideoCore::RendererBase& renderer) | ||
| 11 | : Tegra::GPU(system, renderer) {} | ||
| 12 | |||
| 13 | GPUSynch::~GPUSynch() = default; | ||
| 14 | |||
| 15 | void GPUSynch::PushGPUEntries(Tegra::CommandList&& entries) { | ||
| 16 | dma_pusher->Push(std::move(entries)); | ||
| 17 | dma_pusher->DispatchCalls(); | ||
| 18 | } | ||
| 19 | |||
| 20 | void GPUSynch::SwapBuffers( | ||
| 21 | std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer) { | ||
| 22 | renderer.SwapBuffers(std::move(framebuffer)); | ||
| 23 | } | ||
| 24 | |||
| 25 | void GPUSynch::FlushRegion(VAddr addr, u64 size) { | ||
| 26 | renderer.Rasterizer().FlushRegion(addr, size); | ||
| 27 | } | ||
| 28 | |||
| 29 | void GPUSynch::InvalidateRegion(VAddr addr, u64 size) { | ||
| 30 | renderer.Rasterizer().InvalidateRegion(addr, size); | ||
| 31 | } | ||
| 32 | |||
| 33 | void GPUSynch::FlushAndInvalidateRegion(VAddr addr, u64 size) { | ||
| 34 | renderer.Rasterizer().FlushAndInvalidateRegion(addr, size); | ||
| 35 | } | ||
| 36 | |||
| 37 | } // namespace VideoCommon | ||
diff --git a/src/video_core/gpu_synch.h b/src/video_core/gpu_synch.h new file mode 100644 index 000000000..658f683e2 --- /dev/null +++ b/src/video_core/gpu_synch.h | |||
| @@ -0,0 +1,29 @@ | |||
| 1 | // Copyright 2019 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "video_core/gpu.h" | ||
| 8 | |||
| 9 | namespace VideoCore { | ||
| 10 | class RendererBase; | ||
| 11 | } // namespace VideoCore | ||
| 12 | |||
| 13 | namespace VideoCommon { | ||
| 14 | |||
| 15 | /// Implementation of GPU interface that runs the GPU synchronously | ||
| 16 | class GPUSynch : public Tegra::GPU { | ||
| 17 | public: | ||
| 18 | explicit GPUSynch(Core::System& system, VideoCore::RendererBase& renderer); | ||
| 19 | ~GPUSynch(); | ||
| 20 | |||
| 21 | void PushGPUEntries(Tegra::CommandList&& entries) override; | ||
| 22 | void SwapBuffers( | ||
| 23 | std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer) override; | ||
| 24 | void FlushRegion(VAddr addr, u64 size) override; | ||
| 25 | void InvalidateRegion(VAddr addr, u64 size) override; | ||
| 26 | void FlushAndInvalidateRegion(VAddr addr, u64 size) override; | ||
| 27 | }; | ||
| 28 | |||
| 29 | } // namespace VideoCommon | ||
diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp new file mode 100644 index 000000000..c5bdd2a17 --- /dev/null +++ b/src/video_core/gpu_thread.cpp | |||
| @@ -0,0 +1,152 @@ | |||
| 1 | // Copyright 2019 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/assert.h" | ||
| 6 | #include "common/microprofile.h" | ||
| 7 | #include "core/frontend/scope_acquire_window_context.h" | ||
| 8 | #include "core/settings.h" | ||
| 9 | #include "video_core/dma_pusher.h" | ||
| 10 | #include "video_core/gpu.h" | ||
| 11 | #include "video_core/gpu_thread.h" | ||
| 12 | #include "video_core/renderer_base.h" | ||
| 13 | |||
| 14 | namespace VideoCommon::GPUThread { | ||
| 15 | |||
| 16 | /// Executes a single GPU thread command | ||
| 17 | static void ExecuteCommand(CommandData* command, VideoCore::RendererBase& renderer, | ||
| 18 | Tegra::DmaPusher& dma_pusher) { | ||
| 19 | if (const auto submit_list = std::get_if<SubmitListCommand>(command)) { | ||
| 20 | dma_pusher.Push(std::move(submit_list->entries)); | ||
| 21 | dma_pusher.DispatchCalls(); | ||
| 22 | } else if (const auto data = std::get_if<SwapBuffersCommand>(command)) { | ||
| 23 | renderer.SwapBuffers(data->framebuffer); | ||
| 24 | } else if (const auto data = std::get_if<FlushRegionCommand>(command)) { | ||
| 25 | renderer.Rasterizer().FlushRegion(data->addr, data->size); | ||
| 26 | } else if (const auto data = std::get_if<InvalidateRegionCommand>(command)) { | ||
| 27 | renderer.Rasterizer().InvalidateRegion(data->addr, data->size); | ||
| 28 | } else if (const auto data = std::get_if<FlushAndInvalidateRegionCommand>(command)) { | ||
| 29 | renderer.Rasterizer().FlushAndInvalidateRegion(data->addr, data->size); | ||
| 30 | } else { | ||
| 31 | UNREACHABLE(); | ||
| 32 | } | ||
| 33 | } | ||
| 34 | |||
| 35 | /// Runs the GPU thread | ||
| 36 | static void RunThread(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_pusher, | ||
| 37 | SynchState& state) { | ||
| 38 | |||
| 39 | MicroProfileOnThreadCreate("GpuThread"); | ||
| 40 | |||
| 41 | auto WaitForWakeup = [&]() { | ||
| 42 | std::unique_lock<std::mutex> lock{state.signal_mutex}; | ||
| 43 | state.signal_condition.wait(lock, [&] { return !state.is_idle || !state.is_running; }); | ||
| 44 | }; | ||
| 45 | |||
| 46 | // Wait for first GPU command before acquiring the window context | ||
| 47 | WaitForWakeup(); | ||
| 48 | |||
| 49 | // If emulation was stopped during disk shader loading, abort before trying to acquire context | ||
| 50 | if (!state.is_running) { | ||
| 51 | return; | ||
| 52 | } | ||
| 53 | |||
| 54 | Core::Frontend::ScopeAcquireWindowContext acquire_context{renderer.GetRenderWindow()}; | ||
| 55 | |||
| 56 | while (state.is_running) { | ||
| 57 | if (!state.is_running) { | ||
| 58 | return; | ||
| 59 | } | ||
| 60 | |||
| 61 | { | ||
| 62 | // Thread has been woken up, so make the previous write queue the next read queue | ||
| 63 | std::lock_guard<std::mutex> lock{state.signal_mutex}; | ||
| 64 | std::swap(state.push_queue, state.pop_queue); | ||
| 65 | } | ||
| 66 | |||
| 67 | // Execute all of the GPU commands | ||
| 68 | while (!state.pop_queue->empty()) { | ||
| 69 | ExecuteCommand(&state.pop_queue->front(), renderer, dma_pusher); | ||
| 70 | state.pop_queue->pop(); | ||
| 71 | } | ||
| 72 | |||
| 73 | state.UpdateIdleState(); | ||
| 74 | |||
| 75 | // Signal that the GPU thread has finished processing commands | ||
| 76 | if (state.is_idle) { | ||
| 77 | state.idle_condition.notify_one(); | ||
| 78 | } | ||
| 79 | |||
| 80 | // Wait for CPU thread to send more GPU commands | ||
| 81 | WaitForWakeup(); | ||
| 82 | } | ||
| 83 | } | ||
| 84 | |||
| 85 | ThreadManager::ThreadManager(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_pusher) | ||
| 86 | : renderer{renderer}, dma_pusher{dma_pusher}, thread{RunThread, std::ref(renderer), | ||
| 87 | std::ref(dma_pusher), std::ref(state)}, | ||
| 88 | thread_id{thread.get_id()} {} | ||
| 89 | |||
| 90 | ThreadManager::~ThreadManager() { | ||
| 91 | { | ||
| 92 | // Notify GPU thread that a shutdown is pending | ||
| 93 | std::lock_guard<std::mutex> lock{state.signal_mutex}; | ||
| 94 | state.is_running = false; | ||
| 95 | } | ||
| 96 | |||
| 97 | state.signal_condition.notify_one(); | ||
| 98 | thread.join(); | ||
| 99 | } | ||
| 100 | |||
| 101 | void ThreadManager::SubmitList(Tegra::CommandList&& entries) { | ||
| 102 | if (entries.empty()) { | ||
| 103 | return; | ||
| 104 | } | ||
| 105 | |||
| 106 | PushCommand(SubmitListCommand(std::move(entries)), false, false); | ||
| 107 | } | ||
| 108 | |||
| 109 | void ThreadManager::SwapBuffers( | ||
| 110 | std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer) { | ||
| 111 | PushCommand(SwapBuffersCommand(std::move(framebuffer)), true, false); | ||
| 112 | } | ||
| 113 | |||
| 114 | void ThreadManager::FlushRegion(VAddr addr, u64 size) { | ||
| 115 | // Block the CPU when using accurate emulation | ||
| 116 | PushCommand(FlushRegionCommand(addr, size), Settings::values.use_accurate_gpu_emulation, false); | ||
| 117 | } | ||
| 118 | |||
| 119 | void ThreadManager::InvalidateRegion(VAddr addr, u64 size) { | ||
| 120 | PushCommand(InvalidateRegionCommand(addr, size), true, true); | ||
| 121 | } | ||
| 122 | |||
| 123 | void ThreadManager::FlushAndInvalidateRegion(VAddr addr, u64 size) { | ||
| 124 | InvalidateRegion(addr, size); | ||
| 125 | } | ||
| 126 | |||
| 127 | void ThreadManager::PushCommand(CommandData&& command_data, bool wait_for_idle, bool allow_on_cpu) { | ||
| 128 | { | ||
| 129 | std::lock_guard<std::mutex> lock{state.signal_mutex}; | ||
| 130 | |||
| 131 | if ((allow_on_cpu && state.is_idle) || IsGpuThread()) { | ||
| 132 | // Execute the command synchronously on the current thread | ||
| 133 | ExecuteCommand(&command_data, renderer, dma_pusher); | ||
| 134 | return; | ||
| 135 | } | ||
| 136 | |||
| 137 | // Push the command to the GPU thread | ||
| 138 | state.UpdateIdleState(); | ||
| 139 | state.push_queue->emplace(command_data); | ||
| 140 | } | ||
| 141 | |||
| 142 | // Signal the GPU thread that commands are pending | ||
| 143 | state.signal_condition.notify_one(); | ||
| 144 | |||
| 145 | if (wait_for_idle) { | ||
| 146 | // Wait for the GPU to be idle (all commands to be executed) | ||
| 147 | std::unique_lock<std::mutex> lock{state.idle_mutex}; | ||
| 148 | state.idle_condition.wait(lock, [this] { return static_cast<bool>(state.is_idle); }); | ||
| 149 | } | ||
| 150 | } | ||
| 151 | |||
| 152 | } // namespace VideoCommon::GPUThread | ||
diff --git a/src/video_core/gpu_thread.h b/src/video_core/gpu_thread.h new file mode 100644 index 000000000..2ad8214cc --- /dev/null +++ b/src/video_core/gpu_thread.h | |||
| @@ -0,0 +1,136 @@ | |||
| 1 | // Copyright 2019 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <array> | ||
| 8 | #include <atomic> | ||
| 9 | #include <condition_variable> | ||
| 10 | #include <memory> | ||
| 11 | #include <mutex> | ||
| 12 | #include <optional> | ||
| 13 | #include <thread> | ||
| 14 | #include <variant> | ||
| 15 | |||
| 16 | namespace Tegra { | ||
| 17 | struct FramebufferConfig; | ||
| 18 | class DmaPusher; | ||
| 19 | } // namespace Tegra | ||
| 20 | |||
| 21 | namespace VideoCore { | ||
| 22 | class RendererBase; | ||
| 23 | } // namespace VideoCore | ||
| 24 | |||
| 25 | namespace VideoCommon::GPUThread { | ||
| 26 | |||
| 27 | /// Command to signal to the GPU thread that a command list is ready for processing | ||
| 28 | struct SubmitListCommand final { | ||
| 29 | explicit SubmitListCommand(Tegra::CommandList&& entries) : entries{std::move(entries)} {} | ||
| 30 | |||
| 31 | Tegra::CommandList entries; | ||
| 32 | }; | ||
| 33 | |||
| 34 | /// Command to signal to the GPU thread that a swap buffers is pending | ||
| 35 | struct SwapBuffersCommand final { | ||
| 36 | explicit SwapBuffersCommand(std::optional<const Tegra::FramebufferConfig> framebuffer) | ||
| 37 | : framebuffer{std::move(framebuffer)} {} | ||
| 38 | |||
| 39 | std::optional<const Tegra::FramebufferConfig> framebuffer; | ||
| 40 | }; | ||
| 41 | |||
| 42 | /// Command to signal to the GPU thread to flush a region | ||
| 43 | struct FlushRegionCommand final { | ||
| 44 | explicit constexpr FlushRegionCommand(VAddr addr, u64 size) : addr{addr}, size{size} {} | ||
| 45 | |||
| 46 | const VAddr addr; | ||
| 47 | const u64 size; | ||
| 48 | }; | ||
| 49 | |||
| 50 | /// Command to signal to the GPU thread to invalidate a region | ||
| 51 | struct InvalidateRegionCommand final { | ||
| 52 | explicit constexpr InvalidateRegionCommand(VAddr addr, u64 size) : addr{addr}, size{size} {} | ||
| 53 | |||
| 54 | const VAddr addr; | ||
| 55 | const u64 size; | ||
| 56 | }; | ||
| 57 | |||
| 58 | /// Command to signal to the GPU thread to flush and invalidate a region | ||
| 59 | struct FlushAndInvalidateRegionCommand final { | ||
| 60 | explicit constexpr FlushAndInvalidateRegionCommand(VAddr addr, u64 size) | ||
| 61 | : addr{addr}, size{size} {} | ||
| 62 | |||
| 63 | const VAddr addr; | ||
| 64 | const u64 size; | ||
| 65 | }; | ||
| 66 | |||
| 67 | using CommandData = std::variant<SubmitListCommand, SwapBuffersCommand, FlushRegionCommand, | ||
| 68 | InvalidateRegionCommand, FlushAndInvalidateRegionCommand>; | ||
| 69 | |||
| 70 | /// Struct used to synchronize the GPU thread | ||
| 71 | struct SynchState final { | ||
| 72 | std::atomic<bool> is_running{true}; | ||
| 73 | std::atomic<bool> is_idle{true}; | ||
| 74 | std::condition_variable signal_condition; | ||
| 75 | std::mutex signal_mutex; | ||
| 76 | std::condition_variable idle_condition; | ||
| 77 | std::mutex idle_mutex; | ||
| 78 | |||
| 79 | // We use two queues for sending commands to the GPU thread, one for writing (push_queue) to and | ||
| 80 | // one for reading from (pop_queue). These are swapped whenever the current pop_queue becomes | ||
| 81 | // empty. This allows for efficient thread-safe access, as it does not require any copies. | ||
| 82 | |||
| 83 | using CommandQueue = std::queue<CommandData>; | ||
| 84 | std::array<CommandQueue, 2> command_queues; | ||
| 85 | CommandQueue* push_queue{&command_queues[0]}; | ||
| 86 | CommandQueue* pop_queue{&command_queues[1]}; | ||
| 87 | |||
| 88 | void UpdateIdleState() { | ||
| 89 | std::lock_guard<std::mutex> lock{idle_mutex}; | ||
| 90 | is_idle = command_queues[0].empty() && command_queues[1].empty(); | ||
| 91 | } | ||
| 92 | }; | ||
| 93 | |||
| 94 | /// Class used to manage the GPU thread | ||
| 95 | class ThreadManager final { | ||
| 96 | public: | ||
| 97 | explicit ThreadManager(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_pusher); | ||
| 98 | ~ThreadManager(); | ||
| 99 | |||
| 100 | /// Push GPU command entries to be processed | ||
| 101 | void SubmitList(Tegra::CommandList&& entries); | ||
| 102 | |||
| 103 | /// Swap buffers (render frame) | ||
| 104 | void SwapBuffers( | ||
| 105 | std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer); | ||
| 106 | |||
| 107 | /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory | ||
| 108 | void FlushRegion(VAddr addr, u64 size); | ||
| 109 | |||
| 110 | /// Notify rasterizer that any caches of the specified region should be invalidated | ||
| 111 | void InvalidateRegion(VAddr addr, u64 size); | ||
| 112 | |||
| 113 | /// Notify rasterizer that any caches of the specified region should be flushed and invalidated | ||
| 114 | void FlushAndInvalidateRegion(VAddr addr, u64 size); | ||
| 115 | |||
| 116 | /// Waits the caller until the GPU thread is idle, used for synchronization | ||
| 117 | void WaitForIdle(); | ||
| 118 | |||
| 119 | private: | ||
| 120 | /// Pushes a command to be executed by the GPU thread | ||
| 121 | void PushCommand(CommandData&& command_data, bool wait_for_idle, bool allow_on_cpu); | ||
| 122 | |||
| 123 | /// Returns true if this is called by the GPU thread | ||
| 124 | bool IsGpuThread() const { | ||
| 125 | return std::this_thread::get_id() == thread_id; | ||
| 126 | } | ||
| 127 | |||
| 128 | private: | ||
| 129 | SynchState state; | ||
| 130 | std::thread thread; | ||
| 131 | std::thread::id thread_id; | ||
| 132 | VideoCore::RendererBase& renderer; | ||
| 133 | Tegra::DmaPusher& dma_pusher; | ||
| 134 | }; | ||
| 135 | |||
| 136 | } // namespace VideoCommon::GPUThread | ||
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 321d9dd3d..168288088 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp | |||
| @@ -749,11 +749,7 @@ void RasterizerOpenGL::FlushAll() {} | |||
| 749 | 749 | ||
| 750 | void RasterizerOpenGL::FlushRegion(VAddr addr, u64 size) { | 750 | void RasterizerOpenGL::FlushRegion(VAddr addr, u64 size) { |
| 751 | MICROPROFILE_SCOPE(OpenGL_CacheManagement); | 751 | MICROPROFILE_SCOPE(OpenGL_CacheManagement); |
| 752 | 752 | res_cache.FlushRegion(addr, size); | |
| 753 | if (Settings::values.use_accurate_gpu_emulation) { | ||
| 754 | // Only flush if use_accurate_gpu_emulation is enabled, as it incurs a performance hit | ||
| 755 | res_cache.FlushRegion(addr, size); | ||
| 756 | } | ||
| 757 | } | 753 | } |
| 758 | 754 | ||
| 759 | void RasterizerOpenGL::InvalidateRegion(VAddr addr, u64 size) { | 755 | void RasterizerOpenGL::InvalidateRegion(VAddr addr, u64 size) { |
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index 73b04b749..3b070bfbb 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp | |||
| @@ -20,10 +20,7 @@ | |||
| 20 | EmuThread::EmuThread(GRenderWindow* render_window) : render_window(render_window) {} | 20 | EmuThread::EmuThread(GRenderWindow* render_window) : render_window(render_window) {} |
| 21 | 21 | ||
| 22 | void EmuThread::run() { | 22 | void EmuThread::run() { |
| 23 | if (!Settings::values.use_multi_core) { | 23 | render_window->MakeCurrent(); |
| 24 | // Single core mode must acquire OpenGL context for entire emulation session | ||
| 25 | render_window->MakeCurrent(); | ||
| 26 | } | ||
| 27 | 24 | ||
| 28 | MicroProfileOnThreadCreate("EmuThread"); | 25 | MicroProfileOnThreadCreate("EmuThread"); |
| 29 | 26 | ||
| @@ -38,6 +35,11 @@ void EmuThread::run() { | |||
| 38 | 35 | ||
| 39 | emit LoadProgress(VideoCore::LoadCallbackStage::Complete, 0, 0); | 36 | emit LoadProgress(VideoCore::LoadCallbackStage::Complete, 0, 0); |
| 40 | 37 | ||
| 38 | if (Settings::values.use_asynchronous_gpu_emulation) { | ||
| 39 | // Release OpenGL context for the GPU thread | ||
| 40 | render_window->DoneCurrent(); | ||
| 41 | } | ||
| 42 | |||
| 41 | // holds whether the cpu was running during the last iteration, | 43 | // holds whether the cpu was running during the last iteration, |
| 42 | // so that the DebugModeLeft signal can be emitted before the | 44 | // so that the DebugModeLeft signal can be emitted before the |
| 43 | // next execution step | 45 | // next execution step |
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index e9546dadf..74dc6bb28 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp | |||
| @@ -374,6 +374,8 @@ void Config::ReadValues() { | |||
| 374 | qt_config->value("use_disk_shader_cache", false).toBool(); | 374 | qt_config->value("use_disk_shader_cache", false).toBool(); |
| 375 | Settings::values.use_accurate_gpu_emulation = | 375 | Settings::values.use_accurate_gpu_emulation = |
| 376 | qt_config->value("use_accurate_gpu_emulation", false).toBool(); | 376 | qt_config->value("use_accurate_gpu_emulation", false).toBool(); |
| 377 | Settings::values.use_asynchronous_gpu_emulation = | ||
| 378 | qt_config->value("use_asynchronous_gpu_emulation", false).toBool(); | ||
| 377 | 379 | ||
| 378 | Settings::values.bg_red = qt_config->value("bg_red", 0.0).toFloat(); | 380 | Settings::values.bg_red = qt_config->value("bg_red", 0.0).toFloat(); |
| 379 | Settings::values.bg_green = qt_config->value("bg_green", 0.0).toFloat(); | 381 | Settings::values.bg_green = qt_config->value("bg_green", 0.0).toFloat(); |
| @@ -633,6 +635,8 @@ void Config::SaveValues() { | |||
| 633 | qt_config->setValue("frame_limit", Settings::values.frame_limit); | 635 | qt_config->setValue("frame_limit", Settings::values.frame_limit); |
| 634 | qt_config->setValue("use_disk_shader_cache", Settings::values.use_disk_shader_cache); | 636 | qt_config->setValue("use_disk_shader_cache", Settings::values.use_disk_shader_cache); |
| 635 | qt_config->setValue("use_accurate_gpu_emulation", Settings::values.use_accurate_gpu_emulation); | 637 | qt_config->setValue("use_accurate_gpu_emulation", Settings::values.use_accurate_gpu_emulation); |
| 638 | qt_config->setValue("use_asynchronous_gpu_emulation", | ||
| 639 | Settings::values.use_asynchronous_gpu_emulation); | ||
| 636 | 640 | ||
| 637 | // Cast to double because Qt's written float values are not human-readable | 641 | // Cast to double because Qt's written float values are not human-readable |
| 638 | qt_config->setValue("bg_red", (double)Settings::values.bg_red); | 642 | qt_config->setValue("bg_red", (double)Settings::values.bg_red); |
diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp index 0f5dd534b..dd1d67488 100644 --- a/src/yuzu/configuration/configure_graphics.cpp +++ b/src/yuzu/configuration/configure_graphics.cpp | |||
| @@ -75,6 +75,8 @@ void ConfigureGraphics::setConfiguration() { | |||
| 75 | ui->frame_limit->setValue(Settings::values.frame_limit); | 75 | ui->frame_limit->setValue(Settings::values.frame_limit); |
| 76 | ui->use_disk_shader_cache->setChecked(Settings::values.use_disk_shader_cache); | 76 | ui->use_disk_shader_cache->setChecked(Settings::values.use_disk_shader_cache); |
| 77 | ui->use_accurate_gpu_emulation->setChecked(Settings::values.use_accurate_gpu_emulation); | 77 | ui->use_accurate_gpu_emulation->setChecked(Settings::values.use_accurate_gpu_emulation); |
| 78 | ui->use_asynchronous_gpu_emulation->setEnabled(!Core::System::GetInstance().IsPoweredOn()); | ||
| 79 | ui->use_asynchronous_gpu_emulation->setChecked(Settings::values.use_asynchronous_gpu_emulation); | ||
| 78 | UpdateBackgroundColorButton(QColor::fromRgbF(Settings::values.bg_red, Settings::values.bg_green, | 80 | UpdateBackgroundColorButton(QColor::fromRgbF(Settings::values.bg_red, Settings::values.bg_green, |
| 79 | Settings::values.bg_blue)); | 81 | Settings::values.bg_blue)); |
| 80 | } | 82 | } |
| @@ -86,6 +88,8 @@ void ConfigureGraphics::applyConfiguration() { | |||
| 86 | Settings::values.frame_limit = ui->frame_limit->value(); | 88 | Settings::values.frame_limit = ui->frame_limit->value(); |
| 87 | Settings::values.use_disk_shader_cache = ui->use_disk_shader_cache->isChecked(); | 89 | Settings::values.use_disk_shader_cache = ui->use_disk_shader_cache->isChecked(); |
| 88 | Settings::values.use_accurate_gpu_emulation = ui->use_accurate_gpu_emulation->isChecked(); | 90 | Settings::values.use_accurate_gpu_emulation = ui->use_accurate_gpu_emulation->isChecked(); |
| 91 | Settings::values.use_asynchronous_gpu_emulation = | ||
| 92 | ui->use_asynchronous_gpu_emulation->isChecked(); | ||
| 89 | Settings::values.bg_red = static_cast<float>(bg_color.redF()); | 93 | Settings::values.bg_red = static_cast<float>(bg_color.redF()); |
| 90 | Settings::values.bg_green = static_cast<float>(bg_color.greenF()); | 94 | Settings::values.bg_green = static_cast<float>(bg_color.greenF()); |
| 91 | Settings::values.bg_blue = static_cast<float>(bg_color.blueF()); | 95 | Settings::values.bg_blue = static_cast<float>(bg_color.blueF()); |
diff --git a/src/yuzu/configuration/configure_graphics.ui b/src/yuzu/configuration/configure_graphics.ui index 824f5810a..c6767e0ca 100644 --- a/src/yuzu/configuration/configure_graphics.ui +++ b/src/yuzu/configuration/configure_graphics.ui | |||
| @@ -64,6 +64,13 @@ | |||
| 64 | </widget> | 64 | </widget> |
| 65 | </item> | 65 | </item> |
| 66 | <item> | 66 | <item> |
| 67 | <widget class="QCheckBox" name="use_asynchronous_gpu_emulation"> | ||
| 68 | <property name="text"> | ||
| 69 | <string>Use asynchronous GPU emulation</string> | ||
| 70 | </property> | ||
| 71 | </widget> | ||
| 72 | </item> | ||
| 73 | <item> | ||
| 67 | <layout class="QHBoxLayout" name="horizontalLayout"> | 74 | <layout class="QHBoxLayout" name="horizontalLayout"> |
| 68 | <item> | 75 | <item> |
| 69 | <widget class="QLabel" name="label"> | 76 | <widget class="QLabel" name="label"> |
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp index ff05b3179..ca880dc65 100644 --- a/src/yuzu_cmd/config.cpp +++ b/src/yuzu_cmd/config.cpp | |||
| @@ -354,6 +354,8 @@ void Config::ReadValues() { | |||
| 354 | sdl2_config->GetBoolean("Renderer", "use_disk_shader_cache", false); | 354 | sdl2_config->GetBoolean("Renderer", "use_disk_shader_cache", false); |
| 355 | Settings::values.use_accurate_gpu_emulation = | 355 | Settings::values.use_accurate_gpu_emulation = |
| 356 | sdl2_config->GetBoolean("Renderer", "use_accurate_gpu_emulation", false); | 356 | sdl2_config->GetBoolean("Renderer", "use_accurate_gpu_emulation", false); |
| 357 | Settings::values.use_asynchronous_gpu_emulation = | ||
| 358 | sdl2_config->GetBoolean("Renderer", "use_asynchronous_gpu_emulation", false); | ||
| 357 | 359 | ||
| 358 | Settings::values.bg_red = (float)sdl2_config->GetReal("Renderer", "bg_red", 0.0); | 360 | Settings::values.bg_red = (float)sdl2_config->GetReal("Renderer", "bg_red", 0.0); |
| 359 | Settings::values.bg_green = (float)sdl2_config->GetReal("Renderer", "bg_green", 0.0); | 361 | Settings::values.bg_green = (float)sdl2_config->GetReal("Renderer", "bg_green", 0.0); |
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h index a81986f8e..6538af098 100644 --- a/src/yuzu_cmd/default_ini.h +++ b/src/yuzu_cmd/default_ini.h | |||
| @@ -118,6 +118,10 @@ use_disk_shader_cache = | |||
| 118 | # 0 (default): Off (fast), 1 : On (slow) | 118 | # 0 (default): Off (fast), 1 : On (slow) |
| 119 | use_accurate_gpu_emulation = | 119 | use_accurate_gpu_emulation = |
| 120 | 120 | ||
| 121 | # Whether to use asynchronous GPU emulation | ||
| 122 | # 0 : Off (slow), 1 (default): On (fast) | ||
| 123 | use_asynchronous_gpu_emulation = | ||
| 124 | |||
| 121 | # The clear color for the renderer. What shows up on the sides of the bottom screen. | 125 | # The clear color for the renderer. What shows up on the sides of the bottom screen. |
| 122 | # Must be in range of 0.0-1.0. Defaults to 1.0 for all. | 126 | # Must be in range of 0.0-1.0. Defaults to 1.0 for all. |
| 123 | bg_red = | 127 | bg_red = |