summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
m---------externals/Vulkan-Headers0
-rw-r--r--src/core/CMakeLists.txt4
-rw-r--r--src/core/core.cpp2
-rw-r--r--src/core/crypto/key_manager.cpp3
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.cpp44
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.h43
-rw-r--r--src/core/hle/service/vi/display/vi_display.cpp22
-rw-r--r--src/core/hle/service/vi/display/vi_display.h28
-rw-r--r--src/core/hle/service/vi/layer/vi_layer.cpp14
-rw-r--r--src/core/hle/service/vi/layer/vi_layer.h25
-rw-r--r--src/video_core/CMakeLists.txt6
-rw-r--r--src/video_core/engines/kepler_memory.cpp7
-rw-r--r--src/video_core/engines/kepler_memory.h9
-rw-r--r--src/video_core/engines/maxwell_3d.cpp12
-rw-r--r--src/video_core/engines/maxwell_3d.h9
-rw-r--r--src/video_core/engines/maxwell_dma.cpp8
-rw-r--r--src/video_core/engines/maxwell_dma.h10
-rw-r--r--src/video_core/gpu.cpp8
-rw-r--r--src/video_core/gpu.h7
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.cpp2
-rw-r--r--src/video_core/renderer_opengl/gl_state.cpp144
-rw-r--r--src/video_core/renderer_vulkan/vk_memory_manager.cpp252
-rw-r--r--src/video_core/renderer_vulkan/vk_memory_manager.h87
-rw-r--r--src/video_core/renderer_vulkan/vk_scheduler.cpp60
-rw-r--r--src/video_core/renderer_vulkan/vk_scheduler.h69
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, &current_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