diff options
25 files changed, 709 insertions, 166 deletions
diff --git a/externals/Vulkan-Headers b/externals/Vulkan-Headers -Subproject 7f02d9bb810f371de0fe833c80004c34f7ff8c5 +Subproject 15e5c4db7500b936ae758236f2e72fc1aec2202 diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index f61bcd40d..988356c65 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -400,6 +400,10 @@ add_library(core STATIC hle/service/time/time.h hle/service/usb/usb.cpp hle/service/usb/usb.h + hle/service/vi/display/vi_display.cpp + hle/service/vi/display/vi_display.h + hle/service/vi/layer/vi_layer.cpp + hle/service/vi/layer/vi_layer.h hle/service/vi/vi.cpp hle/service/vi/vi.h hle/service/vi/vi_m.cpp diff --git a/src/core/core.cpp b/src/core/core.cpp index 8aa0932c5..ab7181a05 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -128,7 +128,7 @@ struct System::Impl { return ResultStatus::ErrorVideoCore; } - gpu_core = std::make_unique<Tegra::GPU>(renderer->Rasterizer()); + gpu_core = std::make_unique<Tegra::GPU>(system, renderer->Rasterizer()); cpu_core_manager.Initialize(system); is_powered_on = true; diff --git a/src/core/crypto/key_manager.cpp b/src/core/crypto/key_manager.cpp index ca12fb4ab..dfac9a4b3 100644 --- a/src/core/crypto/key_manager.cpp +++ b/src/core/crypto/key_manager.cpp @@ -398,7 +398,8 @@ static bool ValidCryptoRevisionString(std::string_view base, size_t begin, size_ } void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) { - std::ifstream file(filename); + std::ifstream file; + OpenFStream(file, filename, std::ios_base::in); if (!file.is_open()) return; diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp index 3babc3f7c..b5d452db1 100644 --- a/src/core/hle/service/nvflinger/nvflinger.cpp +++ b/src/core/hle/service/nvflinger/nvflinger.cpp @@ -14,11 +14,12 @@ #include "core/core_timing_util.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/readable_event.h" -#include "core/hle/kernel/writable_event.h" #include "core/hle/service/nvdrv/devices/nvdisp_disp0.h" #include "core/hle/service/nvdrv/nvdrv.h" #include "core/hle/service/nvflinger/buffer_queue.h" #include "core/hle/service/nvflinger/nvflinger.h" +#include "core/hle/service/vi/display/vi_display.h" +#include "core/hle/service/vi/layer/vi_layer.h" #include "core/perf_stats.h" #include "video_core/renderer_base.h" @@ -27,7 +28,9 @@ namespace Service::NVFlinger { constexpr std::size_t SCREEN_REFRESH_RATE = 60; constexpr u64 frame_ticks = static_cast<u64>(Core::Timing::BASE_CLOCK_RATE / SCREEN_REFRESH_RATE); -NVFlinger::NVFlinger(Core::Timing::CoreTiming& core_timing) : core_timing{core_timing} { +NVFlinger::NVFlinger(Core::Timing::CoreTiming& core_timing) + : displays{{0, "Default"}, {1, "External"}, {2, "Edid"}, {3, "Internal"}, {4, "Null"}}, + core_timing{core_timing} { // Schedule the screen composition events composition_event = core_timing.RegisterEvent("ScreenComposition", [this](u64 userdata, int cycles_late) { @@ -53,7 +56,7 @@ std::optional<u64> NVFlinger::OpenDisplay(std::string_view name) { ASSERT(name == "Default"); const auto itr = std::find_if(displays.begin(), displays.end(), - [&](const Display& display) { return display.name == name; }); + [&](const VI::Display& display) { return display.name == name; }); if (itr == displays.end()) { return {}; } @@ -106,9 +109,10 @@ std::shared_ptr<BufferQueue> NVFlinger::FindBufferQueue(u32 id) const { return *itr; } -Display* NVFlinger::FindDisplay(u64 display_id) { - const auto itr = std::find_if(displays.begin(), displays.end(), - [&](const Display& display) { return display.id == display_id; }); +VI::Display* NVFlinger::FindDisplay(u64 display_id) { + const auto itr = + std::find_if(displays.begin(), displays.end(), + [&](const VI::Display& display) { return display.id == display_id; }); if (itr == displays.end()) { return nullptr; @@ -117,9 +121,10 @@ Display* NVFlinger::FindDisplay(u64 display_id) { return &*itr; } -const Display* NVFlinger::FindDisplay(u64 display_id) const { - const auto itr = std::find_if(displays.begin(), displays.end(), - [&](const Display& display) { return display.id == display_id; }); +const VI::Display* NVFlinger::FindDisplay(u64 display_id) const { + const auto itr = + std::find_if(displays.begin(), displays.end(), + [&](const VI::Display& display) { return display.id == display_id; }); if (itr == displays.end()) { return nullptr; @@ -128,7 +133,7 @@ const Display* NVFlinger::FindDisplay(u64 display_id) const { return &*itr; } -Layer* NVFlinger::FindLayer(u64 display_id, u64 layer_id) { +VI::Layer* NVFlinger::FindLayer(u64 display_id, u64 layer_id) { auto* const display = FindDisplay(display_id); if (display == nullptr) { @@ -136,7 +141,7 @@ Layer* NVFlinger::FindLayer(u64 display_id, u64 layer_id) { } const auto itr = std::find_if(display->layers.begin(), display->layers.end(), - [&](const Layer& layer) { return layer.id == layer_id; }); + [&](const VI::Layer& layer) { return layer.id == layer_id; }); if (itr == display->layers.end()) { return nullptr; @@ -145,7 +150,7 @@ Layer* NVFlinger::FindLayer(u64 display_id, u64 layer_id) { return &*itr; } -const Layer* NVFlinger::FindLayer(u64 display_id, u64 layer_id) const { +const VI::Layer* NVFlinger::FindLayer(u64 display_id, u64 layer_id) const { const auto* const display = FindDisplay(display_id); if (display == nullptr) { @@ -153,7 +158,7 @@ const Layer* NVFlinger::FindLayer(u64 display_id, u64 layer_id) const { } const auto itr = std::find_if(display->layers.begin(), display->layers.end(), - [&](const Layer& layer) { return layer.id == layer_id; }); + [&](const VI::Layer& layer) { return layer.id == layer_id; }); if (itr == display->layers.end()) { return nullptr; @@ -174,7 +179,7 @@ void NVFlinger::Compose() { // TODO(Subv): Support more than 1 layer. ASSERT_MSG(display.layers.size() == 1, "Max 1 layer per display is supported"); - Layer& layer = display.layers[0]; + VI::Layer& layer = display.layers[0]; auto& buffer_queue = layer.buffer_queue; // Search for a queued buffer and acquire it @@ -207,15 +212,4 @@ void NVFlinger::Compose() { } } -Layer::Layer(u64 id, std::shared_ptr<BufferQueue> queue) : id(id), buffer_queue(std::move(queue)) {} -Layer::~Layer() = default; - -Display::Display(u64 id, std::string name) : id(id), name(std::move(name)) { - auto& kernel = Core::System::GetInstance().Kernel(); - vsync_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Sticky, - fmt::format("Display VSync Event {}", id)); -} - -Display::~Display() = default; - } // namespace Service::NVFlinger diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h index 437aa592d..2e000af91 100644 --- a/src/core/hle/service/nvflinger/nvflinger.h +++ b/src/core/hle/service/nvflinger/nvflinger.h @@ -4,7 +4,6 @@ #pragma once -#include <array> #include <memory> #include <optional> #include <string> @@ -26,31 +25,17 @@ class WritableEvent; namespace Service::Nvidia { class Module; -} +} // namespace Service::Nvidia + +namespace Service::VI { +struct Display; +struct Layer; +} // namespace Service::VI namespace Service::NVFlinger { class BufferQueue; -struct Layer { - Layer(u64 id, std::shared_ptr<BufferQueue> queue); - ~Layer(); - - u64 id; - std::shared_ptr<BufferQueue> buffer_queue; -}; - -struct Display { - Display(u64 id, std::string name); - ~Display(); - - u64 id; - std::string name; - - std::vector<Layer> layers; - Kernel::EventPair vsync_event; -}; - class NVFlinger final { public: explicit NVFlinger(Core::Timing::CoreTiming& core_timing); @@ -88,26 +73,20 @@ public: private: /// Finds the display identified by the specified ID. - Display* FindDisplay(u64 display_id); + VI::Display* FindDisplay(u64 display_id); /// Finds the display identified by the specified ID. - const Display* FindDisplay(u64 display_id) const; + const VI::Display* FindDisplay(u64 display_id) const; /// Finds the layer identified by the specified ID in the desired display. - Layer* FindLayer(u64 display_id, u64 layer_id); + VI::Layer* FindLayer(u64 display_id, u64 layer_id); /// Finds the layer identified by the specified ID in the desired display. - const Layer* FindLayer(u64 display_id, u64 layer_id) const; + const VI::Layer* FindLayer(u64 display_id, u64 layer_id) const; std::shared_ptr<Nvidia::Module> nvdrv; - std::array<Display, 5> displays{{ - {0, "Default"}, - {1, "External"}, - {2, "Edid"}, - {3, "Internal"}, - {4, "Null"}, - }}; + std::vector<VI::Display> displays; std::vector<std::shared_ptr<BufferQueue>> buffer_queues; /// Id to use for the next layer that is created, this counter is shared among all displays. diff --git a/src/core/hle/service/vi/display/vi_display.cpp b/src/core/hle/service/vi/display/vi_display.cpp new file mode 100644 index 000000000..a108e468f --- /dev/null +++ b/src/core/hle/service/vi/display/vi_display.cpp @@ -0,0 +1,22 @@ +// Copyright 2019 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <fmt/format.h> + +#include "core/core.h" +#include "core/hle/kernel/readable_event.h" +#include "core/hle/service/vi/display/vi_display.h" +#include "core/hle/service/vi/layer/vi_layer.h" + +namespace Service::VI { + +Display::Display(u64 id, std::string name) : id{id}, name{std::move(name)} { + auto& kernel = Core::System::GetInstance().Kernel(); + vsync_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Sticky, + fmt::format("Display VSync Event {}", id)); +} + +Display::~Display() = default; + +} // namespace Service::VI diff --git a/src/core/hle/service/vi/display/vi_display.h b/src/core/hle/service/vi/display/vi_display.h new file mode 100644 index 000000000..df44db306 --- /dev/null +++ b/src/core/hle/service/vi/display/vi_display.h @@ -0,0 +1,28 @@ +// Copyright 2019 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <string> +#include <vector> + +#include "common/common_types.h" +#include "core/hle/kernel/writable_event.h" + +namespace Service::VI { + +struct Layer; + +struct Display { + Display(u64 id, std::string name); + ~Display(); + + u64 id; + std::string name; + + std::vector<Layer> layers; + Kernel::EventPair vsync_event; +}; + +} // namespace Service::VI diff --git a/src/core/hle/service/vi/layer/vi_layer.cpp b/src/core/hle/service/vi/layer/vi_layer.cpp new file mode 100644 index 000000000..3a83e5b95 --- /dev/null +++ b/src/core/hle/service/vi/layer/vi_layer.cpp @@ -0,0 +1,14 @@ +// Copyright 2019 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/vi/layer/vi_layer.h" + +namespace Service::VI { + +Layer::Layer(u64 id, std::shared_ptr<NVFlinger::BufferQueue> queue) + : id{id}, buffer_queue{std::move(queue)} {} + +Layer::~Layer() = default; + +} // namespace Service::VI diff --git a/src/core/hle/service/vi/layer/vi_layer.h b/src/core/hle/service/vi/layer/vi_layer.h new file mode 100644 index 000000000..df328e09f --- /dev/null +++ b/src/core/hle/service/vi/layer/vi_layer.h @@ -0,0 +1,25 @@ +// Copyright 2019 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <memory> + +#include "common/common_types.h" + +namespace Service::NVFlinger { +class BufferQueue; +} + +namespace Service::VI { + +struct Layer { + Layer(u64 id, std::shared_ptr<NVFlinger::BufferQueue> queue); + ~Layer(); + + u64 id; + std::shared_ptr<NVFlinger::BufferQueue> buffer_queue; +}; + +} // namespace Service::VI diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index 59319f206..6036d6ed3 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -106,8 +106,12 @@ if (ENABLE_VULKAN) renderer_vulkan/declarations.h renderer_vulkan/vk_device.cpp renderer_vulkan/vk_device.h + renderer_vulkan/vk_memory_manager.cpp + renderer_vulkan/vk_memory_manager.h renderer_vulkan/vk_resource_manager.cpp - renderer_vulkan/vk_resource_manager.h) + renderer_vulkan/vk_resource_manager.h + renderer_vulkan/vk_scheduler.cpp + renderer_vulkan/vk_scheduler.h) target_include_directories(video_core PRIVATE ../../externals/Vulkan-Headers/include) target_compile_definitions(video_core PRIVATE HAS_VULKAN) diff --git a/src/video_core/engines/kepler_memory.cpp b/src/video_core/engines/kepler_memory.cpp index 5c1029ddf..4f6126116 100644 --- a/src/video_core/engines/kepler_memory.cpp +++ b/src/video_core/engines/kepler_memory.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/assert.h" #include "common/logging/log.h" #include "core/core.h" #include "core/memory.h" @@ -11,9 +12,9 @@ namespace Tegra::Engines { -KeplerMemory::KeplerMemory(VideoCore::RasterizerInterface& rasterizer, +KeplerMemory::KeplerMemory(Core::System& system, VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager) - : memory_manager(memory_manager), rasterizer{rasterizer} {} + : system{system}, memory_manager(memory_manager), rasterizer{rasterizer} {} KeplerMemory::~KeplerMemory() = default; @@ -50,7 +51,7 @@ void KeplerMemory::ProcessData(u32 data) { rasterizer.InvalidateRegion(*dest_address, sizeof(u32)); Memory::Write32(*dest_address, data); - Core::System::GetInstance().GPU().Maxwell3D().dirty_flags.OnMemoryWrite(); + system.GPU().Maxwell3D().dirty_flags.OnMemoryWrite(); state.write_offset++; } diff --git a/src/video_core/engines/kepler_memory.h b/src/video_core/engines/kepler_memory.h index fe9ebc5b9..f680c2ad9 100644 --- a/src/video_core/engines/kepler_memory.h +++ b/src/video_core/engines/kepler_memory.h @@ -5,13 +5,16 @@ #pragma once #include <array> -#include "common/assert.h" #include "common/bit_field.h" #include "common/common_funcs.h" #include "common/common_types.h" #include "video_core/gpu.h" #include "video_core/memory_manager.h" +namespace Core { +class System; +} + namespace VideoCore { class RasterizerInterface; } @@ -23,7 +26,8 @@ namespace Tegra::Engines { class KeplerMemory final { public: - KeplerMemory(VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager); + KeplerMemory(Core::System& system, VideoCore::RasterizerInterface& rasterizer, + MemoryManager& memory_manager); ~KeplerMemory(); /// Write the value to the register identified by method. @@ -76,6 +80,7 @@ public: } state{}; private: + Core::System& system; MemoryManager& memory_manager; VideoCore::RasterizerInterface& rasterizer; diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp index 86ede5faa..2d2136067 100644 --- a/src/video_core/engines/maxwell_3d.cpp +++ b/src/video_core/engines/maxwell_3d.cpp @@ -19,8 +19,10 @@ namespace Tegra::Engines { /// First register id that is actually a Macro call. constexpr u32 MacroRegistersStart = 0xE00; -Maxwell3D::Maxwell3D(VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager) - : memory_manager(memory_manager), rasterizer{rasterizer}, macro_interpreter(*this) { +Maxwell3D::Maxwell3D(Core::System& system, VideoCore::RasterizerInterface& rasterizer, + MemoryManager& memory_manager) + : memory_manager(memory_manager), system{system}, rasterizer{rasterizer}, + macro_interpreter(*this) { InitializeRegisterDefaults(); } @@ -103,7 +105,7 @@ void Maxwell3D::CallMacroMethod(u32 method, std::vector<u32> parameters) { } void Maxwell3D::CallMethod(const GPU::MethodCall& method_call) { - auto debug_context = Core::System::GetInstance().GetGPUDebugContext(); + auto debug_context = system.GetGPUDebugContext(); // It is an error to write to a register other than the current macro's ARG register before it // has finished execution. @@ -317,7 +319,7 @@ void Maxwell3D::ProcessQueryGet() { LongQueryResult query_result{}; query_result.value = result; // TODO(Subv): Generate a real GPU timestamp and write it here instead of CoreTiming - query_result.timestamp = Core::System::GetInstance().CoreTiming().GetTicks(); + query_result.timestamp = system.CoreTiming().GetTicks(); Memory::WriteBlock(*address, &query_result, sizeof(query_result)); } dirty_flags.OnMemoryWrite(); @@ -334,7 +336,7 @@ void Maxwell3D::DrawArrays() { regs.vertex_buffer.count); ASSERT_MSG(!(regs.index_array.count && regs.vertex_buffer.count), "Both indexed and direct?"); - auto debug_context = Core::System::GetInstance().GetGPUDebugContext(); + auto debug_context = system.GetGPUDebugContext(); if (debug_context) { debug_context->OnEvent(Tegra::DebugContext::Event::IncomingPrimitiveBatch, nullptr); diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h index 1f76aa670..0e3873ffd 100644 --- a/src/video_core/engines/maxwell_3d.h +++ b/src/video_core/engines/maxwell_3d.h @@ -17,6 +17,10 @@ #include "video_core/memory_manager.h" #include "video_core/textures/texture.h" +namespace Core { +class System; +} + namespace VideoCore { class RasterizerInterface; } @@ -28,7 +32,8 @@ namespace Tegra::Engines { class Maxwell3D final { public: - explicit Maxwell3D(VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager); + explicit Maxwell3D(Core::System& system, VideoCore::RasterizerInterface& rasterizer, + MemoryManager& memory_manager); ~Maxwell3D() = default; /// Register structure of the Maxwell3D engine. @@ -1131,6 +1136,8 @@ public: private: void InitializeRegisterDefaults(); + Core::System& system; + VideoCore::RasterizerInterface& rasterizer; /// Start offsets of each macro in macro_memory diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp index d6c41a5ae..529a14ec7 100644 --- a/src/video_core/engines/maxwell_dma.cpp +++ b/src/video_core/engines/maxwell_dma.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/assert.h" #include "core/core.h" #include "core/memory.h" #include "video_core/engines/maxwell_3d.h" @@ -11,8 +12,9 @@ namespace Tegra::Engines { -MaxwellDMA::MaxwellDMA(VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager) - : memory_manager(memory_manager), rasterizer{rasterizer} {} +MaxwellDMA::MaxwellDMA(Core::System& system, VideoCore::RasterizerInterface& rasterizer, + MemoryManager& memory_manager) + : memory_manager(memory_manager), system{system}, rasterizer{rasterizer} {} void MaxwellDMA::CallMethod(const GPU::MethodCall& method_call) { ASSERT_MSG(method_call.method < Regs::NUM_REGS, @@ -59,7 +61,7 @@ void MaxwellDMA::HandleCopy() { } // All copies here update the main memory, so mark all rasterizer states as invalid. - Core::System::GetInstance().GPU().Maxwell3D().dirty_flags.OnMemoryWrite(); + system.GPU().Maxwell3D().dirty_flags.OnMemoryWrite(); if (regs.exec.is_dst_linear && regs.exec.is_src_linear) { // When the enable_2d bit is disabled, the copy is performed as if we were copying a 1D diff --git a/src/video_core/engines/maxwell_dma.h b/src/video_core/engines/maxwell_dma.h index 1f8cd65d2..cf75aeb12 100644 --- a/src/video_core/engines/maxwell_dma.h +++ b/src/video_core/engines/maxwell_dma.h @@ -5,13 +5,16 @@ #pragma once #include <array> -#include "common/assert.h" #include "common/bit_field.h" #include "common/common_funcs.h" #include "common/common_types.h" #include "video_core/gpu.h" #include "video_core/memory_manager.h" +namespace Core { +class System; +} + namespace VideoCore { class RasterizerInterface; } @@ -20,7 +23,8 @@ namespace Tegra::Engines { class MaxwellDMA final { public: - explicit MaxwellDMA(VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager); + explicit MaxwellDMA(Core::System& system, VideoCore::RasterizerInterface& rasterizer, + MemoryManager& memory_manager); ~MaxwellDMA() = default; /// Write the value to the register identified by method. @@ -137,6 +141,8 @@ public: MemoryManager& memory_manager; private: + Core::System& system; + VideoCore::RasterizerInterface& rasterizer; /// Performs the copy from the source buffer to the destination buffer as configured in the diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp index b86265dfe..ac30d1a89 100644 --- a/src/video_core/gpu.cpp +++ b/src/video_core/gpu.cpp @@ -28,14 +28,14 @@ u32 FramebufferConfig::BytesPerPixel(PixelFormat format) { UNREACHABLE(); } -GPU::GPU(VideoCore::RasterizerInterface& rasterizer) { +GPU::GPU(Core::System& system, VideoCore::RasterizerInterface& rasterizer) { memory_manager = std::make_unique<Tegra::MemoryManager>(); dma_pusher = std::make_unique<Tegra::DmaPusher>(*this); - maxwell_3d = std::make_unique<Engines::Maxwell3D>(rasterizer, *memory_manager); + maxwell_3d = std::make_unique<Engines::Maxwell3D>(system, rasterizer, *memory_manager); fermi_2d = std::make_unique<Engines::Fermi2D>(rasterizer, *memory_manager); kepler_compute = std::make_unique<Engines::KeplerCompute>(*memory_manager); - maxwell_dma = std::make_unique<Engines::MaxwellDMA>(rasterizer, *memory_manager); - kepler_memory = std::make_unique<Engines::KeplerMemory>(rasterizer, *memory_manager); + maxwell_dma = std::make_unique<Engines::MaxwellDMA>(system, rasterizer, *memory_manager); + kepler_memory = std::make_unique<Engines::KeplerMemory>(system, rasterizer, *memory_manager); } GPU::~GPU() = default; diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h index a482196ea..0f5bfdcbf 100644 --- a/src/video_core/gpu.h +++ b/src/video_core/gpu.h @@ -6,12 +6,15 @@ #include <array> #include <memory> -#include <vector> #include "common/common_types.h" #include "core/hle/service/nvflinger/buffer_queue.h" #include "video_core/dma_pusher.h" #include "video_core/memory_manager.h" +namespace Core { +class System; +} + namespace VideoCore { class RasterizerInterface; } @@ -118,7 +121,7 @@ enum class EngineID { class GPU final { public: - explicit GPU(VideoCore::RasterizerInterface& rasterizer); + explicit GPU(Core::System& system, VideoCore::RasterizerInterface& rasterizer); ~GPU(); struct MethodCall { diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp index 59f671048..74200914e 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp @@ -423,7 +423,7 @@ void SwizzleFunc(const MortonSwizzleMode& mode, const SurfaceParams& params, for (u32 i = 0; i < params.depth; i++) { MortonSwizzle(mode, params.pixel_format, params.MipWidth(mip_level), params.MipBlockHeight(mip_level), params.MipHeight(mip_level), - params.MipBlockDepth(mip_level), params.tile_width_spacing, 1, + params.MipBlockDepth(mip_level), 1, params.tile_width_spacing, gl_buffer.data() + offset_gl, gl_size, params.addr + offset); offset += layer_size; offset_gl += gl_size; diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp index 81af803bc..219f08053 100644 --- a/src/video_core/renderer_opengl/gl_state.cpp +++ b/src/video_core/renderer_opengl/gl_state.cpp @@ -11,7 +11,9 @@ namespace OpenGL { OpenGLState OpenGLState::cur_state; + bool OpenGLState::s_rgb_used; + OpenGLState::OpenGLState() { // These all match default OpenGL values geometry_shaders.enabled = false; @@ -112,7 +114,6 @@ void OpenGLState::ApplyDefaultState() { } void OpenGLState::ApplySRgb() const { - // sRGB if (framebuffer_srgb.enabled != cur_state.framebuffer_srgb.enabled) { if (framebuffer_srgb.enabled) { // Track if sRGB is used @@ -125,23 +126,20 @@ void OpenGLState::ApplySRgb() const { } void OpenGLState::ApplyCulling() const { - // Culling - const bool cull_changed = cull.enabled != cur_state.cull.enabled; - if (cull_changed) { + if (cull.enabled != cur_state.cull.enabled) { if (cull.enabled) { glEnable(GL_CULL_FACE); } else { glDisable(GL_CULL_FACE); } } - if (cull.enabled) { - if (cull_changed || cull.mode != cur_state.cull.mode) { - glCullFace(cull.mode); - } - if (cull_changed || cull.front_face != cur_state.cull.front_face) { - glFrontFace(cull.front_face); - } + if (cull.mode != cur_state.cull.mode) { + glCullFace(cull.mode); + } + + if (cull.front_face != cur_state.cull.front_face) { + glFrontFace(cull.front_face); } } @@ -172,72 +170,63 @@ void OpenGLState::ApplyColorMask() const { } void OpenGLState::ApplyDepth() const { - // Depth test - const bool depth_test_changed = depth.test_enabled != cur_state.depth.test_enabled; - if (depth_test_changed) { + if (depth.test_enabled != cur_state.depth.test_enabled) { if (depth.test_enabled) { glEnable(GL_DEPTH_TEST); } else { glDisable(GL_DEPTH_TEST); } } - if (depth.test_enabled && - (depth_test_changed || depth.test_func != cur_state.depth.test_func)) { + + if (depth.test_func != cur_state.depth.test_func) { glDepthFunc(depth.test_func); } - // Depth mask + if (depth.write_mask != cur_state.depth.write_mask) { glDepthMask(depth.write_mask); } } void OpenGLState::ApplyPrimitiveRestart() const { - const bool primitive_restart_changed = - primitive_restart.enabled != cur_state.primitive_restart.enabled; - if (primitive_restart_changed) { + if (primitive_restart.enabled != cur_state.primitive_restart.enabled) { if (primitive_restart.enabled) { glEnable(GL_PRIMITIVE_RESTART); } else { glDisable(GL_PRIMITIVE_RESTART); } } - if (primitive_restart_changed || - (primitive_restart.enabled && - primitive_restart.index != cur_state.primitive_restart.index)) { + + if (primitive_restart.index != cur_state.primitive_restart.index) { glPrimitiveRestartIndex(primitive_restart.index); } } void OpenGLState::ApplyStencilTest() const { - const bool stencil_test_changed = stencil.test_enabled != cur_state.stencil.test_enabled; - if (stencil_test_changed) { + if (stencil.test_enabled != cur_state.stencil.test_enabled) { if (stencil.test_enabled) { glEnable(GL_STENCIL_TEST); } else { glDisable(GL_STENCIL_TEST); } } - if (stencil.test_enabled) { - auto config_stencil = [stencil_test_changed](GLenum face, const auto& config, - const auto& prev_config) { - if (stencil_test_changed || config.test_func != prev_config.test_func || - config.test_ref != prev_config.test_ref || - config.test_mask != prev_config.test_mask) { - glStencilFuncSeparate(face, config.test_func, config.test_ref, config.test_mask); - } - if (stencil_test_changed || config.action_depth_fail != prev_config.action_depth_fail || - config.action_depth_pass != prev_config.action_depth_pass || - config.action_stencil_fail != prev_config.action_stencil_fail) { - glStencilOpSeparate(face, config.action_stencil_fail, config.action_depth_fail, - config.action_depth_pass); - } - if (config.write_mask != prev_config.write_mask) { - glStencilMaskSeparate(face, config.write_mask); - } - }; - config_stencil(GL_FRONT, stencil.front, cur_state.stencil.front); - config_stencil(GL_BACK, stencil.back, cur_state.stencil.back); - } + + const auto ConfigStencil = [](GLenum face, const auto& config, const auto& prev_config) { + if (config.test_func != prev_config.test_func || config.test_ref != prev_config.test_ref || + config.test_mask != prev_config.test_mask) { + glStencilFuncSeparate(face, config.test_func, config.test_ref, config.test_mask); + } + if (config.action_depth_fail != prev_config.action_depth_fail || + config.action_depth_pass != prev_config.action_depth_pass || + config.action_stencil_fail != prev_config.action_stencil_fail) { + glStencilOpSeparate(face, config.action_stencil_fail, config.action_depth_fail, + config.action_depth_pass); + } + if (config.write_mask != prev_config.write_mask) { + glStencilMaskSeparate(face, config.write_mask); + } + }; + ConfigStencil(GL_FRONT, stencil.front, cur_state.stencil.front); + ConfigStencil(GL_BACK, stencil.back, cur_state.stencil.back); } // Viewport does not affects glClearBuffer so emulate viewport using scissor test void OpenGLState::EmulateViewportWithScissor() { @@ -278,19 +267,18 @@ void OpenGLState::ApplyViewport() const { updated.depth_range_far != current.depth_range_far) { glDepthRangeIndexed(i, updated.depth_range_near, updated.depth_range_far); } - const bool scissor_changed = updated.scissor.enabled != current.scissor.enabled; - if (scissor_changed) { + + if (updated.scissor.enabled != current.scissor.enabled) { if (updated.scissor.enabled) { glEnablei(GL_SCISSOR_TEST, i); } else { glDisablei(GL_SCISSOR_TEST, i); } } - if (updated.scissor.enabled && - (scissor_changed || updated.scissor.x != current.scissor.x || - updated.scissor.y != current.scissor.y || - updated.scissor.width != current.scissor.width || - updated.scissor.height != current.scissor.height)) { + + if (updated.scissor.x != current.scissor.x || updated.scissor.y != current.scissor.y || + updated.scissor.width != current.scissor.width || + updated.scissor.height != current.scissor.height) { glScissorIndexed(i, updated.scissor.x, updated.scissor.y, updated.scissor.width, updated.scissor.height); } @@ -302,22 +290,23 @@ void OpenGLState::ApplyViewport() const { updated.height != current.height) { glViewport(updated.x, updated.y, updated.width, updated.height); } + if (updated.depth_range_near != current.depth_range_near || updated.depth_range_far != current.depth_range_far) { glDepthRange(updated.depth_range_near, updated.depth_range_far); } - const bool scissor_changed = updated.scissor.enabled != current.scissor.enabled; - if (scissor_changed) { + + if (updated.scissor.enabled != current.scissor.enabled) { if (updated.scissor.enabled) { glEnable(GL_SCISSOR_TEST); } else { glDisable(GL_SCISSOR_TEST); } } - if (updated.scissor.enabled && (scissor_changed || updated.scissor.x != current.scissor.x || - updated.scissor.y != current.scissor.y || - updated.scissor.width != current.scissor.width || - updated.scissor.height != current.scissor.height)) { + + if (updated.scissor.x != current.scissor.x || updated.scissor.y != current.scissor.y || + updated.scissor.width != current.scissor.width || + updated.scissor.height != current.scissor.height) { glScissor(updated.scissor.x, updated.scissor.y, updated.scissor.width, updated.scissor.height); } @@ -327,8 +316,7 @@ void OpenGLState::ApplyViewport() const { void OpenGLState::ApplyGlobalBlending() const { const Blend& current = cur_state.blend[0]; const Blend& updated = blend[0]; - const bool blend_changed = updated.enabled != current.enabled; - if (blend_changed) { + if (updated.enabled != current.enabled) { if (updated.enabled) { glEnable(GL_BLEND); } else { @@ -338,15 +326,14 @@ void OpenGLState::ApplyGlobalBlending() const { if (!updated.enabled) { return; } - if (blend_changed || updated.src_rgb_func != current.src_rgb_func || + if (updated.src_rgb_func != current.src_rgb_func || updated.dst_rgb_func != current.dst_rgb_func || updated.src_a_func != current.src_a_func || updated.dst_a_func != current.dst_a_func) { glBlendFuncSeparate(updated.src_rgb_func, updated.dst_rgb_func, updated.src_a_func, updated.dst_a_func); } - if (blend_changed || updated.rgb_equation != current.rgb_equation || - updated.a_equation != current.a_equation) { + if (updated.rgb_equation != current.rgb_equation || updated.a_equation != current.a_equation) { glBlendEquationSeparate(updated.rgb_equation, updated.a_equation); } } @@ -354,26 +341,22 @@ void OpenGLState::ApplyGlobalBlending() const { void OpenGLState::ApplyTargetBlending(std::size_t target, bool force) const { const Blend& updated = blend[target]; const Blend& current = cur_state.blend[target]; - const bool blend_changed = updated.enabled != current.enabled || force; - if (blend_changed) { + if (updated.enabled != current.enabled || force) { if (updated.enabled) { glEnablei(GL_BLEND, static_cast<GLuint>(target)); } else { glDisablei(GL_BLEND, static_cast<GLuint>(target)); } } - if (!updated.enabled) { - return; - } - if (blend_changed || updated.src_rgb_func != current.src_rgb_func || + + if (updated.src_rgb_func != current.src_rgb_func || updated.dst_rgb_func != current.dst_rgb_func || updated.src_a_func != current.src_a_func || updated.dst_a_func != current.dst_a_func) { glBlendFuncSeparatei(static_cast<GLuint>(target), updated.src_rgb_func, updated.dst_rgb_func, updated.src_a_func, updated.dst_a_func); } - if (blend_changed || updated.rgb_equation != current.rgb_equation || - updated.a_equation != current.a_equation) { + if (updated.rgb_equation != current.rgb_equation || updated.a_equation != current.a_equation) { glBlendEquationSeparatei(static_cast<GLuint>(target), updated.rgb_equation, updated.a_equation); } @@ -397,8 +380,7 @@ void OpenGLState::ApplyBlending() const { } void OpenGLState::ApplyLogicOp() const { - const bool logic_op_changed = logic_op.enabled != cur_state.logic_op.enabled; - if (logic_op_changed) { + if (logic_op.enabled != cur_state.logic_op.enabled) { if (logic_op.enabled) { glEnable(GL_COLOR_LOGIC_OP); } else { @@ -406,14 +388,12 @@ void OpenGLState::ApplyLogicOp() const { } } - if (logic_op.enabled && - (logic_op_changed || logic_op.operation != cur_state.logic_op.operation)) { + if (logic_op.operation != cur_state.logic_op.operation) { glLogicOp(logic_op.operation); } } void OpenGLState::ApplyPolygonOffset() const { - const bool fill_enable_changed = polygon_offset.fill_enable != cur_state.polygon_offset.fill_enable; const bool line_enable_changed = @@ -448,9 +428,7 @@ void OpenGLState::ApplyPolygonOffset() const { } } - if ((polygon_offset.fill_enable || polygon_offset.line_enable || polygon_offset.point_enable) && - (factor_changed || units_changed || clamp_changed)) { - + if (factor_changed || units_changed || clamp_changed) { if (GLAD_GL_EXT_polygon_offset_clamp && polygon_offset.clamp != 0) { glPolygonOffsetClamp(polygon_offset.factor, polygon_offset.units, polygon_offset.clamp); } else { @@ -528,9 +506,9 @@ void OpenGLState::ApplyDepthClamp() const { depth_clamp.near_plane == cur_state.depth_clamp.near_plane) { return; } - if (depth_clamp.far_plane != depth_clamp.near_plane) { - UNIMPLEMENTED_MSG("Unimplemented Depth Clamp Separation!"); - } + UNIMPLEMENTED_IF_MSG(depth_clamp.far_plane != depth_clamp.near_plane, + "Unimplemented Depth Clamp Separation!"); + if (depth_clamp.far_plane || depth_clamp.near_plane) { glEnable(GL_DEPTH_CLAMP); } else { diff --git a/src/video_core/renderer_vulkan/vk_memory_manager.cpp b/src/video_core/renderer_vulkan/vk_memory_manager.cpp new file mode 100644 index 000000000..17ee93b91 --- /dev/null +++ b/src/video_core/renderer_vulkan/vk_memory_manager.cpp @@ -0,0 +1,252 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <algorithm> +#include <optional> +#include <tuple> +#include <vector> +#include "common/alignment.h" +#include "common/assert.h" +#include "common/common_types.h" +#include "common/logging/log.h" +#include "video_core/renderer_vulkan/declarations.h" +#include "video_core/renderer_vulkan/vk_device.h" +#include "video_core/renderer_vulkan/vk_memory_manager.h" + +namespace Vulkan { + +// TODO(Rodrigo): Fine tune this number +constexpr u64 ALLOC_CHUNK_SIZE = 64 * 1024 * 1024; + +class VKMemoryAllocation final { +public: + explicit VKMemoryAllocation(const VKDevice& device, vk::DeviceMemory memory, + vk::MemoryPropertyFlags properties, u64 alloc_size, u32 type) + : device{device}, memory{memory}, properties{properties}, alloc_size{alloc_size}, + shifted_type{ShiftType(type)}, is_mappable{properties & + vk::MemoryPropertyFlagBits::eHostVisible} { + if (is_mappable) { + const auto dev = device.GetLogical(); + const auto& dld = device.GetDispatchLoader(); + base_address = static_cast<u8*>(dev.mapMemory(memory, 0, alloc_size, {}, dld)); + } + } + + ~VKMemoryAllocation() { + const auto dev = device.GetLogical(); + const auto& dld = device.GetDispatchLoader(); + if (is_mappable) + dev.unmapMemory(memory, dld); + dev.free(memory, nullptr, dld); + } + + VKMemoryCommit Commit(vk::DeviceSize commit_size, vk::DeviceSize alignment) { + auto found = TryFindFreeSection(free_iterator, alloc_size, static_cast<u64>(commit_size), + static_cast<u64>(alignment)); + if (!found) { + found = TryFindFreeSection(0, free_iterator, static_cast<u64>(commit_size), + static_cast<u64>(alignment)); + if (!found) { + // Signal out of memory, it'll try to do more allocations. + return nullptr; + } + } + u8* address = is_mappable ? base_address + *found : nullptr; + auto commit = std::make_unique<VKMemoryCommitImpl>(this, memory, address, *found, + *found + commit_size); + commits.push_back(commit.get()); + + // Last commit's address is highly probable to be free. + free_iterator = *found + commit_size; + + return commit; + } + + void Free(const VKMemoryCommitImpl* commit) { + ASSERT(commit); + const auto it = + std::find_if(commits.begin(), commits.end(), + [&](const auto& stored_commit) { return stored_commit == commit; }); + if (it == commits.end()) { + LOG_CRITICAL(Render_Vulkan, "Freeing unallocated commit!"); + UNREACHABLE(); + return; + } + commits.erase(it); + } + + /// Returns whether this allocation is compatible with the arguments. + bool IsCompatible(vk::MemoryPropertyFlags wanted_properties, u32 type_mask) const { + return (wanted_properties & properties) != vk::MemoryPropertyFlagBits(0) && + (type_mask & shifted_type) != 0; + } + +private: + static constexpr u32 ShiftType(u32 type) { + return 1U << type; + } + + /// A memory allocator, it may return a free region between "start" and "end" with the solicited + /// requeriments. + std::optional<u64> TryFindFreeSection(u64 start, u64 end, u64 size, u64 alignment) const { + u64 iterator = start; + while (iterator + size < end) { + const u64 try_left = Common::AlignUp(iterator, alignment); + const u64 try_right = try_left + size; + + bool overlap = false; + for (const auto& commit : commits) { + const auto [commit_left, commit_right] = commit->interval; + if (try_left < commit_right && commit_left < try_right) { + // There's an overlap, continue the search where the overlapping commit ends. + iterator = commit_right; + overlap = true; + break; + } + } + if (!overlap) { + // A free address has been found. + return try_left; + } + } + // No free regions where found, return an empty optional. + return std::nullopt; + } + + const VKDevice& device; ///< Vulkan device. + const vk::DeviceMemory memory; ///< Vulkan memory allocation handler. + const vk::MemoryPropertyFlags properties; ///< Vulkan properties. + const u64 alloc_size; ///< Size of this allocation. + const u32 shifted_type; ///< Stored Vulkan type of this allocation, shifted. + const bool is_mappable; ///< Whether the allocation is mappable. + + /// Base address of the mapped pointer. + u8* base_address{}; + + /// Hints where the next free region is likely going to be. + u64 free_iterator{}; + + /// Stores all commits done from this allocation. + std::vector<const VKMemoryCommitImpl*> commits; +}; + +VKMemoryManager::VKMemoryManager(const VKDevice& device) + : device{device}, props{device.GetPhysical().getMemoryProperties(device.GetDispatchLoader())}, + is_memory_unified{GetMemoryUnified(props)} {} + +VKMemoryManager::~VKMemoryManager() = default; + +VKMemoryCommit VKMemoryManager::Commit(const vk::MemoryRequirements& reqs, bool host_visible) { + ASSERT(reqs.size < ALLOC_CHUNK_SIZE); + + // When a host visible commit is asked, search for host visible and coherent, otherwise search + // for a fast device local type. + const vk::MemoryPropertyFlags wanted_properties = + host_visible + ? vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent + : vk::MemoryPropertyFlagBits::eDeviceLocal; + + const auto TryCommit = [&]() -> VKMemoryCommit { + for (auto& alloc : allocs) { + if (!alloc->IsCompatible(wanted_properties, reqs.memoryTypeBits)) + continue; + + if (auto commit = alloc->Commit(reqs.size, reqs.alignment); commit) { + return commit; + } + } + return {}; + }; + + if (auto commit = TryCommit(); commit) { + return commit; + } + + // Commit has failed, allocate more memory. + if (!AllocMemory(wanted_properties, reqs.memoryTypeBits, ALLOC_CHUNK_SIZE)) { + // TODO(Rodrigo): Try to use host memory. + LOG_CRITICAL(Render_Vulkan, "Ran out of memory!"); + UNREACHABLE(); + } + + // Commit again, this time it won't fail since there's a fresh allocation above. If it does, + // there's a bug. + auto commit = TryCommit(); + ASSERT(commit); + return commit; +} + +VKMemoryCommit VKMemoryManager::Commit(vk::Buffer buffer, bool host_visible) { + const auto dev = device.GetLogical(); + const auto& dld = device.GetDispatchLoader(); + const auto requeriments = dev.getBufferMemoryRequirements(buffer, dld); + auto commit = Commit(requeriments, host_visible); + dev.bindBufferMemory(buffer, commit->GetMemory(), commit->GetOffset(), dld); + return commit; +} + +VKMemoryCommit VKMemoryManager::Commit(vk::Image image, bool host_visible) { + const auto dev = device.GetLogical(); + const auto& dld = device.GetDispatchLoader(); + const auto requeriments = dev.getImageMemoryRequirements(image, dld); + auto commit = Commit(requeriments, host_visible); + dev.bindImageMemory(image, commit->GetMemory(), commit->GetOffset(), dld); + return commit; +} + +bool VKMemoryManager::AllocMemory(vk::MemoryPropertyFlags wanted_properties, u32 type_mask, + u64 size) { + const u32 type = [&]() { + for (u32 type_index = 0; type_index < props.memoryTypeCount; ++type_index) { + const auto flags = props.memoryTypes[type_index].propertyFlags; + if ((type_mask & (1U << type_index)) && (flags & wanted_properties)) { + // The type matches in type and in the wanted properties. + return type_index; + } + } + LOG_CRITICAL(Render_Vulkan, "Couldn't find a compatible memory type!"); + UNREACHABLE(); + return 0u; + }(); + + const auto dev = device.GetLogical(); + const auto& dld = device.GetDispatchLoader(); + + // Try to allocate found type. + const vk::MemoryAllocateInfo memory_ai(size, type); + vk::DeviceMemory memory; + if (const vk::Result res = dev.allocateMemory(&memory_ai, nullptr, &memory, dld); + res != vk::Result::eSuccess) { + LOG_CRITICAL(Render_Vulkan, "Device allocation failed with code {}!", vk::to_string(res)); + return false; + } + allocs.push_back( + std::make_unique<VKMemoryAllocation>(device, memory, wanted_properties, size, type)); + return true; +} + +/*static*/ bool VKMemoryManager::GetMemoryUnified(const vk::PhysicalDeviceMemoryProperties& props) { + for (u32 heap_index = 0; heap_index < props.memoryHeapCount; ++heap_index) { + if (!(props.memoryHeaps[heap_index].flags & vk::MemoryHeapFlagBits::eDeviceLocal)) { + // Memory is considered unified when heaps are device local only. + return false; + } + } + return true; +} + +VKMemoryCommitImpl::VKMemoryCommitImpl(VKMemoryAllocation* allocation, vk::DeviceMemory memory, + u8* data, u64 begin, u64 end) + : allocation{allocation}, memory{memory}, data{data}, interval(std::make_pair(begin, end)) {} + +VKMemoryCommitImpl::~VKMemoryCommitImpl() { + allocation->Free(this); +} + +u8* VKMemoryCommitImpl::GetData() const { + ASSERT_MSG(data != nullptr, "Trying to access an unmapped commit."); + return data; +} + +} // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_memory_manager.h b/src/video_core/renderer_vulkan/vk_memory_manager.h new file mode 100644 index 000000000..073597b35 --- /dev/null +++ b/src/video_core/renderer_vulkan/vk_memory_manager.h @@ -0,0 +1,87 @@ +// Copyright 2019 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <memory> +#include <utility> +#include <vector> +#include "common/common_types.h" +#include "video_core/renderer_vulkan/declarations.h" + +namespace Vulkan { + +class VKDevice; +class VKMemoryAllocation; +class VKMemoryCommitImpl; + +using VKMemoryCommit = std::unique_ptr<VKMemoryCommitImpl>; + +class VKMemoryManager final { +public: + explicit VKMemoryManager(const VKDevice& device); + ~VKMemoryManager(); + + /** + * Commits a memory with the specified requeriments. + * @param reqs Requeriments returned from a Vulkan call. + * @param host_visible Signals the allocator that it *must* use host visible and coherent + * memory. When passing false, it will try to allocate device local memory. + * @returns A memory commit. + */ + VKMemoryCommit Commit(const vk::MemoryRequirements& reqs, bool host_visible); + + /// Commits memory required by the buffer and binds it. + VKMemoryCommit Commit(vk::Buffer buffer, bool host_visible); + + /// Commits memory required by the image and binds it. + VKMemoryCommit Commit(vk::Image image, bool host_visible); + + /// Returns true if the memory allocations are done always in host visible and coherent memory. + bool IsMemoryUnified() const { + return is_memory_unified; + } + +private: + /// Allocates a chunk of memory. + bool AllocMemory(vk::MemoryPropertyFlags wanted_properties, u32 type_mask, u64 size); + + /// Returns true if the device uses an unified memory model. + static bool GetMemoryUnified(const vk::PhysicalDeviceMemoryProperties& props); + + const VKDevice& device; ///< Device handler. + const vk::PhysicalDeviceMemoryProperties props; ///< Physical device properties. + const bool is_memory_unified; ///< True if memory model is unified. + std::vector<std::unique_ptr<VKMemoryAllocation>> allocs; ///< Current allocations. +}; + +class VKMemoryCommitImpl final { + friend VKMemoryAllocation; + +public: + explicit VKMemoryCommitImpl(VKMemoryAllocation* allocation, vk::DeviceMemory memory, u8* data, + u64 begin, u64 end); + ~VKMemoryCommitImpl(); + + /// Returns the writeable memory map. The commit has to be mappable. + u8* GetData() const; + + /// Returns the Vulkan memory handler. + vk::DeviceMemory GetMemory() const { + return memory; + } + + /// Returns the start position of the commit relative to the allocation. + vk::DeviceSize GetOffset() const { + return static_cast<vk::DeviceSize>(interval.first); + } + +private: + std::pair<u64, u64> interval{}; ///< Interval where the commit exists. + vk::DeviceMemory memory; ///< Vulkan device memory handler. + VKMemoryAllocation* allocation{}; ///< Pointer to the large memory allocation. + u8* data{}; ///< Pointer to the host mapped memory, it has the commit offset included. +}; + +} // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp new file mode 100644 index 000000000..f1fea1871 --- /dev/null +++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp @@ -0,0 +1,60 @@ +// Copyright 2019 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/assert.h" +#include "common/logging/log.h" +#include "video_core/renderer_vulkan/declarations.h" +#include "video_core/renderer_vulkan/vk_device.h" +#include "video_core/renderer_vulkan/vk_resource_manager.h" +#include "video_core/renderer_vulkan/vk_scheduler.h" + +namespace Vulkan { + +VKScheduler::VKScheduler(const VKDevice& device, VKResourceManager& resource_manager) + : device{device}, resource_manager{resource_manager} { + next_fence = &resource_manager.CommitFence(); + AllocateNewContext(); +} + +VKScheduler::~VKScheduler() = default; + +VKExecutionContext VKScheduler::GetExecutionContext() const { + return VKExecutionContext(current_fence, current_cmdbuf); +} + +VKExecutionContext VKScheduler::Flush(vk::Semaphore semaphore) { + SubmitExecution(semaphore); + current_fence->Release(); + AllocateNewContext(); + return GetExecutionContext(); +} + +VKExecutionContext VKScheduler::Finish(vk::Semaphore semaphore) { + SubmitExecution(semaphore); + current_fence->Wait(); + current_fence->Release(); + AllocateNewContext(); + return GetExecutionContext(); +} + +void VKScheduler::SubmitExecution(vk::Semaphore semaphore) { + const auto& dld = device.GetDispatchLoader(); + current_cmdbuf.end(dld); + + const auto queue = device.GetGraphicsQueue(); + const vk::SubmitInfo submit_info(0, nullptr, nullptr, 1, ¤t_cmdbuf, semaphore ? 1u : 0u, + &semaphore); + queue.submit({submit_info}, *current_fence, dld); +} + +void VKScheduler::AllocateNewContext() { + current_fence = next_fence; + current_cmdbuf = resource_manager.CommitCommandBuffer(*current_fence); + next_fence = &resource_manager.CommitFence(); + + const auto& dld = device.GetDispatchLoader(); + current_cmdbuf.begin({vk::CommandBufferUsageFlagBits::eOneTimeSubmit}, dld); +} + +} // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_scheduler.h b/src/video_core/renderer_vulkan/vk_scheduler.h new file mode 100644 index 000000000..cfaf5376f --- /dev/null +++ b/src/video_core/renderer_vulkan/vk_scheduler.h @@ -0,0 +1,69 @@ +// Copyright 2019 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "common/common_types.h" +#include "video_core/renderer_vulkan/declarations.h" + +namespace Vulkan { + +class VKDevice; +class VKExecutionContext; +class VKFence; +class VKResourceManager; + +/// The scheduler abstracts command buffer and fence management with an interface that's able to do +/// OpenGL-like operations on Vulkan command buffers. +class VKScheduler { +public: + explicit VKScheduler(const VKDevice& device, VKResourceManager& resource_manager); + ~VKScheduler(); + + /// Gets the current execution context. + [[nodiscard]] VKExecutionContext GetExecutionContext() const; + + /// Sends the current execution context to the GPU. It invalidates the current execution context + /// and returns a new one. + VKExecutionContext Flush(vk::Semaphore semaphore = nullptr); + + /// Sends the current execution context to the GPU and waits for it to complete. It invalidates + /// the current execution context and returns a new one. + VKExecutionContext Finish(vk::Semaphore semaphore = nullptr); + +private: + void SubmitExecution(vk::Semaphore semaphore); + + void AllocateNewContext(); + + const VKDevice& device; + VKResourceManager& resource_manager; + vk::CommandBuffer current_cmdbuf; + VKFence* current_fence = nullptr; + VKFence* next_fence = nullptr; +}; + +class VKExecutionContext { + friend class VKScheduler; + +public: + VKExecutionContext() = default; + + VKFence& GetFence() const { + return *fence; + } + + vk::CommandBuffer GetCommandBuffer() const { + return cmdbuf; + } + +private: + explicit VKExecutionContext(VKFence* fence, vk::CommandBuffer cmdbuf) + : fence{fence}, cmdbuf{cmdbuf} {} + + VKFence* fence{}; + vk::CommandBuffer cmdbuf; +}; + +} // namespace Vulkan |