From 2334e475dea450827d3497839b30e38bba575e59 Mon Sep 17 00:00:00 2001 From: Triang3l Date: Thu, 8 Aug 2019 00:08:20 +0300 Subject: [PATCH] [Vulkan v2] Physical device, [D3D12] Small cleanup --- premake5.lua | 2 + src/xenia/app/premake5.lua | 2 + src/xenia/app/xenia_main.cc | 10 +- src/xenia/gpu/d3d12/d3d12_graphics_system.h | 3 +- src/xenia/gpu/vk/premake5.lua | 19 ++ src/xenia/gpu/vk/vulkan_command_processor.cc | 46 ++++ src/xenia/gpu/vk/vulkan_command_processor.h | 47 ++++ src/xenia/gpu/vk/vulkan_graphics_system.cc | 54 +++++ src/xenia/gpu/vk/vulkan_graphics_system.h | 49 ++++ src/xenia/ui/d3d12/d3d12_immediate_drawer.cc | 20 +- src/xenia/ui/d3d12/d3d12_immediate_drawer.h | 2 +- src/xenia/ui/d3d12/d3d12_provider.cc | 37 ++- src/xenia/ui/d3d12/d3d12_provider.h | 8 +- src/xenia/ui/vk/premake5.lua | 17 ++ src/xenia/ui/vk/vulkan_context.cc | 65 ++++++ src/xenia/ui/vk/vulkan_context.h | 63 +++++ src/xenia/ui/vk/vulkan_immediate_drawer.cc | 68 ++++++ src/xenia/ui/vk/vulkan_immediate_drawer.h | 51 ++++ src/xenia/ui/vk/vulkan_provider.cc | 230 +++++++++++++++++++ src/xenia/ui/vk/vulkan_provider.h | 54 +++++ src/xenia/ui/vk/vulkan_util.h | 35 +++ 21 files changed, 835 insertions(+), 47 deletions(-) create mode 100644 src/xenia/gpu/vk/premake5.lua create mode 100644 src/xenia/gpu/vk/vulkan_command_processor.cc create mode 100644 src/xenia/gpu/vk/vulkan_command_processor.h create mode 100644 src/xenia/gpu/vk/vulkan_graphics_system.cc create mode 100644 src/xenia/gpu/vk/vulkan_graphics_system.h create mode 100644 src/xenia/ui/vk/premake5.lua create mode 100644 src/xenia/ui/vk/vulkan_context.cc create mode 100644 src/xenia/ui/vk/vulkan_context.h create mode 100644 src/xenia/ui/vk/vulkan_immediate_drawer.cc create mode 100644 src/xenia/ui/vk/vulkan_immediate_drawer.h create mode 100644 src/xenia/ui/vk/vulkan_provider.cc create mode 100644 src/xenia/ui/vk/vulkan_provider.h create mode 100644 src/xenia/ui/vk/vulkan_util.h diff --git a/premake5.lua b/premake5.lua index 24c783782..212ffc9e1 100644 --- a/premake5.lua +++ b/premake5.lua @@ -244,12 +244,14 @@ solution("xenia") include("src/xenia/debug/ui") include("src/xenia/gpu") include("src/xenia/gpu/null") + include("src/xenia/gpu/vk") include("src/xenia/gpu/vulkan") include("src/xenia/hid") include("src/xenia/hid/nop") include("src/xenia/kernel") include("src/xenia/ui") include("src/xenia/ui/spirv") + include("src/xenia/ui/vk") include("src/xenia/ui/vulkan") include("src/xenia/vfs") diff --git a/src/xenia/app/premake5.lua b/src/xenia/app/premake5.lua index 7fa8c7050..3b9b79486 100644 --- a/src/xenia/app/premake5.lua +++ b/src/xenia/app/premake5.lua @@ -31,12 +31,14 @@ project("xenia-app") "xenia-debug-ui", "xenia-gpu", "xenia-gpu-null", + "xenia-gpu-vk", "xenia-gpu-vulkan", "xenia-hid", "xenia-hid-nop", "xenia-kernel", "xenia-ui", "xenia-ui-spirv", + "xenia-ui-vk", "xenia-ui-vulkan", "xenia-vfs", "xxhash", diff --git a/src/xenia/app/xenia_main.cc b/src/xenia/app/xenia_main.cc index 72284a017..7f5e89b49 100644 --- a/src/xenia/app/xenia_main.cc +++ b/src/xenia/app/xenia_main.cc @@ -29,6 +29,7 @@ // Available graphics systems: #include "xenia/gpu/null/null_graphics_system.h" +#include "xenia/gpu/vk/vulkan_graphics_system.h" #include "xenia/gpu/vulkan/vulkan_graphics_system.h" #if XE_PLATFORM_WIN32 #include "xenia/gpu/d3d12/d3d12_graphics_system.h" @@ -44,8 +45,8 @@ #include "third_party/xbyak/xbyak/xbyak_util.h" DEFINE_string(apu, "any", "Audio system. Use: [any, nop, xaudio2]", "APU"); -DEFINE_string(gpu, "any", "Graphics system. Use: [any, d3d12, vulkan, null]", - "GPU"); +DEFINE_string(gpu, "any", + "Graphics system. Use: [any, d3d12, vulkan, vk, null]", "GPU"); DEFINE_string(hid, "any", "Input system. Use: [any, nop, winkey, xinput]", "HID"); @@ -156,7 +157,12 @@ std::unique_ptr CreateGraphicsSystem() { #if XE_PLATFORM_WIN32 factory.Add("d3d12"); #endif // XE_PLATFORM_WIN32 + // Abandoned Vulkan graphics system. factory.Add("vulkan"); + // New Vulkan graphics system. + // TODO(Triang3l): Move this higher when it's more ready, then drop the old + // Vulkan graphics system. + factory.Add("vk"); factory.Add("null"); return factory.Create(cvars::gpu); } diff --git a/src/xenia/gpu/d3d12/d3d12_graphics_system.h b/src/xenia/gpu/d3d12/d3d12_graphics_system.h index 36b3ad24f..eb8be31d4 100644 --- a/src/xenia/gpu/d3d12/d3d12_graphics_system.h +++ b/src/xenia/gpu/d3d12/d3d12_graphics_system.h @@ -50,11 +50,12 @@ class D3D12GraphicsSystem : public GraphicsSystem { D3D12_GPU_DESCRIPTOR_HANDLE* gamma_ramp_handle, float gamma_ramp_inv_size, DeferredCommandList& command_list); - private: + protected: std::unique_ptr CreateCommandProcessor() override; void Swap(xe::ui::UIEvent* e) override; + private: ui::d3d12::D3D12Context* display_context_ = nullptr; ID3D12RootSignature* stretch_root_signature_ = nullptr; diff --git a/src/xenia/gpu/vk/premake5.lua b/src/xenia/gpu/vk/premake5.lua new file mode 100644 index 000000000..a2e9f01ba --- /dev/null +++ b/src/xenia/gpu/vk/premake5.lua @@ -0,0 +1,19 @@ +project_root = "../../../.." +include(project_root.."/tools/build") + +group("src") +project("xenia-gpu-vk") + uuid("66c9afbb-798a-405d-80a1-7bda473e700d") + kind("StaticLib") + language("C++") + links({ + "xenia-base", + "xenia-gpu", + "xenia-ui", + "xenia-ui-vk", + "xxhash", + }) + local_platform_files() + files({ + "shaders/bin/*.h", + }) diff --git a/src/xenia/gpu/vk/vulkan_command_processor.cc b/src/xenia/gpu/vk/vulkan_command_processor.cc new file mode 100644 index 000000000..c2817c139 --- /dev/null +++ b/src/xenia/gpu/vk/vulkan_command_processor.cc @@ -0,0 +1,46 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2019 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#include "xenia/gpu/vk/vulkan_command_processor.h" + +namespace xe { +namespace gpu { +namespace vk { + +VulkanCommandProcessor::VulkanCommandProcessor( + VulkanGraphicsSystem* graphics_system, kernel::KernelState* kernel_state) + : CommandProcessor(graphics_system, kernel_state) {} +VulkanCommandProcessor::~VulkanCommandProcessor() = default; + +bool VulkanCommandProcessor::SetupContext() { return true; } + +void VulkanCommandProcessor::ShutdownContext() {} + +void VulkanCommandProcessor::PerformSwap(uint32_t frontbuffer_ptr, + uint32_t frontbuffer_width, + uint32_t frontbuffer_height) {} + +Shader* VulkanCommandProcessor::LoadShader(ShaderType shader_type, + uint32_t guest_address, + const uint32_t* host_address, + uint32_t dword_count) { + return nullptr; +} + +bool VulkanCommandProcessor::IssueDraw(PrimitiveType primitive_type, + uint32_t index_count, + IndexBufferInfo* index_buffer_info) { + return true; +} + +bool VulkanCommandProcessor::IssueCopy() { return true; } + +} // namespace vk +} // namespace gpu +} // namespace xe diff --git a/src/xenia/gpu/vk/vulkan_command_processor.h b/src/xenia/gpu/vk/vulkan_command_processor.h new file mode 100644 index 000000000..e2f4d3b8d --- /dev/null +++ b/src/xenia/gpu/vk/vulkan_command_processor.h @@ -0,0 +1,47 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2019 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#ifndef XENIA_GPU_VK_VULKAN_COMMAND_PROCESSOR_H_ +#define XENIA_GPU_VK_VULKAN_COMMAND_PROCESSOR_H_ + +#include "xenia/gpu/command_processor.h" +#include "xenia/gpu/vk/vulkan_graphics_system.h" +#include "xenia/kernel/kernel_state.h" + +namespace xe { +namespace gpu { +namespace vk { + +class VulkanCommandProcessor : public CommandProcessor { + public: + explicit VulkanCommandProcessor(VulkanGraphicsSystem* graphics_system, + kernel::KernelState* kernel_state); + ~VulkanCommandProcessor(); + + protected: + bool SetupContext() override; + void ShutdownContext() override; + + void PerformSwap(uint32_t frontbuffer_ptr, uint32_t frontbuffer_width, + uint32_t frontbuffer_height) override; + + Shader* LoadShader(ShaderType shader_type, uint32_t guest_address, + const uint32_t* host_address, + uint32_t dword_count) override; + + bool IssueDraw(PrimitiveType primitive_type, uint32_t index_count, + IndexBufferInfo* index_buffer_info) override; + bool IssueCopy() override; +}; + +} // namespace vk +} // namespace gpu +} // namespace xe + +#endif // XENIA_GPU_VK_VULKAN_COMMAND_PROCESSOR_H_ diff --git a/src/xenia/gpu/vk/vulkan_graphics_system.cc b/src/xenia/gpu/vk/vulkan_graphics_system.cc new file mode 100644 index 000000000..3cb40579a --- /dev/null +++ b/src/xenia/gpu/vk/vulkan_graphics_system.cc @@ -0,0 +1,54 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2019 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#include "xenia/gpu/vk/vulkan_graphics_system.h" + +#include "xenia/gpu/vk/vulkan_command_processor.h" + +namespace xe { +namespace gpu { +namespace vk { + +VulkanGraphicsSystem::VulkanGraphicsSystem() {} + +VulkanGraphicsSystem::~VulkanGraphicsSystem() {} + +std::wstring VulkanGraphicsSystem::name() const { return L"Vulkan Prototype"; } + +X_STATUS VulkanGraphicsSystem::Setup(cpu::Processor* processor, + kernel::KernelState* kernel_state, + ui::Window* target_window) { + provider_ = xe::ui::vk::VulkanProvider::Create(target_window); + + auto result = GraphicsSystem::Setup(processor, kernel_state, target_window); + if (result != X_STATUS_SUCCESS) { + return result; + } + + if (target_window) { + display_context_ = + reinterpret_cast(target_window->context()); + } + + return X_STATUS_SUCCESS; +} + +void VulkanGraphicsSystem::Shutdown() { GraphicsSystem::Shutdown(); } + +std::unique_ptr +VulkanGraphicsSystem::CreateCommandProcessor() { + return std::unique_ptr( + new VulkanCommandProcessor(this, kernel_state_)); +} + +void VulkanGraphicsSystem::Swap(xe::ui::UIEvent* e) {} + +} // namespace vk +} // namespace gpu +} // namespace xe diff --git a/src/xenia/gpu/vk/vulkan_graphics_system.h b/src/xenia/gpu/vk/vulkan_graphics_system.h new file mode 100644 index 000000000..617fd49a3 --- /dev/null +++ b/src/xenia/gpu/vk/vulkan_graphics_system.h @@ -0,0 +1,49 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2019 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#ifndef XENIA_GPU_VK_VULKAN_GRAPHICS_SYSTEM_H_ +#define XENIA_GPU_VK_VULKAN_GRAPHICS_SYSTEM_H_ + +#include + +#include "xenia/gpu/command_processor.h" +#include "xenia/gpu/graphics_system.h" +#include "xenia/ui/vk/vulkan_context.h" + +namespace xe { +namespace gpu { +namespace vk { + +class VulkanGraphicsSystem : public GraphicsSystem { + public: + VulkanGraphicsSystem(); + ~VulkanGraphicsSystem() override; + + static bool IsAvailable() { return true; } + + std::wstring name() const override; + + X_STATUS Setup(cpu::Processor* processor, kernel::KernelState* kernel_state, + ui::Window* target_window) override; + void Shutdown() override; + + protected: + std::unique_ptr CreateCommandProcessor() override; + + void Swap(xe::ui::UIEvent* e) override; + + private: + ui::vk::VulkanContext* display_context_ = nullptr; +}; + +} // namespace vk +} // namespace gpu +} // namespace xe + +#endif // XENIA_GPU_VK_VULKAN_GRAPHICS_SYSTEM_H_ diff --git a/src/xenia/ui/d3d12/d3d12_immediate_drawer.cc b/src/xenia/ui/d3d12/d3d12_immediate_drawer.cc index 428adc825..1ba4c293a 100644 --- a/src/xenia/ui/d3d12/d3d12_immediate_drawer.cc +++ b/src/xenia/ui/d3d12/d3d12_immediate_drawer.cc @@ -309,24 +309,12 @@ void D3D12ImmediateDrawer::Shutdown() { texture_descriptor_pool_.reset(); vertex_buffer_pool_.reset(); - if (sampler_heap_ != nullptr) { - sampler_heap_->Release(); - sampler_heap_ = nullptr; - } + util::ReleaseAndNull(sampler_heap_); - if (pipeline_line_ != nullptr) { - pipeline_line_->Release(); - pipeline_line_ = nullptr; - } - if (pipeline_triangle_ != nullptr) { - pipeline_triangle_->Release(); - pipeline_triangle_ = nullptr; - } + util::ReleaseAndNull(pipeline_line_); + util::ReleaseAndNull(pipeline_triangle_); - if (root_signature_ != nullptr) { - root_signature_->Release(); - root_signature_ = nullptr; - } + util::ReleaseAndNull(root_signature_); } std::unique_ptr D3D12ImmediateDrawer::CreateTexture( diff --git a/src/xenia/ui/d3d12/d3d12_immediate_drawer.h b/src/xenia/ui/d3d12/d3d12_immediate_drawer.h index 64a32a652..bc5de5bd7 100644 --- a/src/xenia/ui/d3d12/d3d12_immediate_drawer.h +++ b/src/xenia/ui/d3d12/d3d12_immediate_drawer.h @@ -43,7 +43,7 @@ class D3D12ImmediateDrawer : public ImmediateDrawer { void BeginDrawBatch(const ImmediateDrawBatch& batch) override; void Draw(const ImmediateDraw& draw) override; void EndDrawBatch() override; - void End(); + void End() override; private: D3D12Context* context_ = nullptr; diff --git a/src/xenia/ui/d3d12/d3d12_provider.cc b/src/xenia/ui/d3d12/d3d12_provider.cc index 5779b7dd2..d1a9194ca 100644 --- a/src/xenia/ui/d3d12/d3d12_provider.cc +++ b/src/xenia/ui/d3d12/d3d12_provider.cc @@ -38,18 +38,15 @@ bool D3D12Provider::IsD3D12APIAvailable() { std::unique_ptr D3D12Provider::Create(Window* main_window) { std::unique_ptr provider(new D3D12Provider(main_window)); - InitializationResult result = provider->Initialize(); - if (result != InitializationResult::kSucceeded) { - if (result != InitializationResult::kLibraryLoadFailed) { - xe::FatalError( - "Unable to initialize Direct3D 12 graphics subsystem.\n" - "\n" - "Ensure that you have the latest drivers for your GPU and it " - "supports Direct3D 12 feature level 11_0.\n" - "\n" - "See https://xenia.jp/faq/ for more information and a list of " - "supported GPUs."); - } + if (!provider->Initialize()) { + xe::FatalError( + "Unable to initialize Direct3D 12 graphics subsystem.\n" + "\n" + "Ensure that you have the latest drivers for your GPU and it supports " + "Direct3D 12 feature level 11_0.\n" + "\n" + "See https://xenia.jp/faq/ for more information and a list of " + "supported GPUs."); return nullptr; } return provider; @@ -83,7 +80,7 @@ D3D12Provider::~D3D12Provider() { } } -D3D12Provider::InitializationResult D3D12Provider::Initialize() { +bool D3D12Provider::Initialize() { // Load the libraries. library_dxgi_ = LoadLibrary(L"dxgi.dll"); library_d3d12_ = LoadLibrary(L"D3D12.dll"); @@ -91,7 +88,7 @@ D3D12Provider::InitializationResult D3D12Provider::Initialize() { if (library_dxgi_ == nullptr || library_d3d12_ == nullptr || library_d3dcompiler_ == nullptr) { XELOGE("Failed to load dxgi.dll, D3D12.dll and D3DCompiler_47.dll."); - return InitializationResult::kLibraryLoadFailed; + return false; } bool libraries_loaded = true; libraries_loaded &= @@ -114,7 +111,7 @@ D3D12Provider::InitializationResult D3D12Provider::Initialize() { libraries_loaded &= (pfn_d3d_disassemble_ = pD3DDisassemble(GetProcAddress( library_d3dcompiler_, "D3DDisassemble"))) != nullptr; if (!libraries_loaded) { - return InitializationResult::kLibraryLoadFailed; + return false; } // Enable the debug layer. @@ -136,7 +133,7 @@ D3D12Provider::InitializationResult D3D12Provider::Initialize() { if (FAILED(pfn_create_dxgi_factory2_(debug ? DXGI_CREATE_FACTORY_DEBUG : 0, IID_PPV_ARGS(&dxgi_factory)))) { XELOGE("Failed to create a DXGI factory"); - return InitializationResult::kDeviceInitializationFailed; + return false; } // Choose the adapter. @@ -169,14 +166,14 @@ D3D12Provider::InitializationResult D3D12Provider::Initialize() { if (adapter == nullptr) { XELOGE("Failed to get an adapter supporting Direct3D feature level 11_0."); dxgi_factory->Release(); - return InitializationResult::kDeviceInitializationFailed; + return false; } DXGI_ADAPTER_DESC adapter_desc; if (FAILED(adapter->GetDesc(&adapter_desc))) { XELOGE("Failed to get the DXGI adapter description."); adapter->Release(); dxgi_factory->Release(); - return InitializationResult::kDeviceInitializationFailed; + return false; } adapter_vendor_id_ = adapter_desc.VendorId; int adapter_name_mb_size = WideCharToMultiByte( @@ -199,7 +196,7 @@ D3D12Provider::InitializationResult D3D12Provider::Initialize() { XELOGE("Failed to create a Direct3D 12 feature level 11_0 device."); adapter->Release(); dxgi_factory->Release(); - return InitializationResult::kDeviceInitializationFailed; + return false; } adapter->Release(); @@ -268,7 +265,7 @@ D3D12Provider::InitializationResult D3D12Provider::Initialize() { // attached. pfn_dxgi_get_debug_interface1_(0, IID_PPV_ARGS(&graphics_analysis_)); - return InitializationResult::kSucceeded; + return true; } std::unique_ptr D3D12Provider::CreateContext( diff --git a/src/xenia/ui/d3d12/d3d12_provider.h b/src/xenia/ui/d3d12/d3d12_provider.h index b527189c0..49f4074f0 100644 --- a/src/xenia/ui/d3d12/d3d12_provider.h +++ b/src/xenia/ui/d3d12/d3d12_provider.h @@ -97,13 +97,7 @@ class D3D12Provider : public GraphicsProvider { private: explicit D3D12Provider(Window* main_window); - enum InitializationResult : uint32_t { - kSucceeded, - kDeviceInitializationFailed, - kLibraryLoadFailed, - }; - - InitializationResult Initialize(); + bool Initialize(); HMODULE library_dxgi_ = nullptr; HMODULE library_d3d12_ = nullptr; diff --git a/src/xenia/ui/vk/premake5.lua b/src/xenia/ui/vk/premake5.lua new file mode 100644 index 000000000..1ae845c04 --- /dev/null +++ b/src/xenia/ui/vk/premake5.lua @@ -0,0 +1,17 @@ +project_root = "../../../.." +include(project_root.."/tools/build") + +group("src") +project("xenia-ui-vk") + uuid("758e31de-c91b-44ce-acef-27752939d37f") + kind("StaticLib") + language("C++") + links({ + "volk", + "xenia-base", + "xenia-ui", + }) + local_platform_files() + files({ + "shaders/bin/*.h", + }) diff --git a/src/xenia/ui/vk/vulkan_context.cc b/src/xenia/ui/vk/vulkan_context.cc new file mode 100644 index 000000000..a9e677e80 --- /dev/null +++ b/src/xenia/ui/vk/vulkan_context.cc @@ -0,0 +1,65 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2019 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#include "xenia/ui/vk/vulkan_context.h" + +#include "xenia/ui/vk/vulkan_immediate_drawer.h" + +namespace xe { +namespace ui { +namespace vk { + +VulkanContext::VulkanContext(VulkanProvider* provider, Window* target_window) + : GraphicsContext(provider, target_window) {} + +VulkanContext::~VulkanContext() { Shutdown(); } + +bool VulkanContext::Initialize() { + context_lost_ = false; + + if (target_window_) { + // Initialize the immediate mode drawer if not offscreen. + immediate_drawer_ = std::make_unique(this); + if (!immediate_drawer_->Initialize()) { + Shutdown(); + return false; + } + } + + return true; +} + +void VulkanContext::Shutdown() { + initialized_fully_ = false; + + immediate_drawer_.reset(); +} + +ImmediateDrawer* VulkanContext::immediate_drawer() { + return immediate_drawer_.get(); +} + +bool VulkanContext::is_current() { return false; } + +bool VulkanContext::MakeCurrent() { return true; } + +void VulkanContext::ClearCurrent() {} + +void VulkanContext::BeginSwap() {} + +void VulkanContext::EndSwap() {} + +std::unique_ptr VulkanContext::Capture() { + // TODO(Triang3l): Read back swap chain front buffer. + return nullptr; +} + +} // namespace vk +} // namespace ui +} // namespace xe diff --git a/src/xenia/ui/vk/vulkan_context.h b/src/xenia/ui/vk/vulkan_context.h new file mode 100644 index 000000000..a58e6b057 --- /dev/null +++ b/src/xenia/ui/vk/vulkan_context.h @@ -0,0 +1,63 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2019 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#ifndef XENIA_UI_VK_VULKAN_CONTEXT_H_ +#define XENIA_UI_VK_VULKAN_CONTEXT_H_ + +#include + +#include "xenia/ui/graphics_context.h" +#include "xenia/ui/vk/vulkan_provider.h" + +#define FINE_GRAINED_DRAW_SCOPES 1 + +namespace xe { +namespace ui { +namespace vk { + +class VulkanImmediateDrawer; + +class VulkanContext : public GraphicsContext { + public: + ~VulkanContext() override; + + ImmediateDrawer* immediate_drawer() override; + + bool is_current() override; + bool MakeCurrent() override; + void ClearCurrent() override; + + bool WasLost() override { return context_lost_; } + + void BeginSwap() override; + void EndSwap() override; + + std::unique_ptr Capture() override; + + private: + friend class VulkanProvider; + + explicit VulkanContext(VulkanProvider* provider, Window* target_window); + + private: + bool Initialize(); + void Shutdown(); + + bool initialized_fully_ = false; + + bool context_lost_ = false; + + std::unique_ptr immediate_drawer_ = nullptr; +}; + +} // namespace vk +} // namespace ui +} // namespace xe + +#endif // XENIA_UI_VK_VULKAN_CONTEXT_H_ diff --git a/src/xenia/ui/vk/vulkan_immediate_drawer.cc b/src/xenia/ui/vk/vulkan_immediate_drawer.cc new file mode 100644 index 000000000..6dd99ec6c --- /dev/null +++ b/src/xenia/ui/vk/vulkan_immediate_drawer.cc @@ -0,0 +1,68 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2019 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#include "xenia/ui/vk/vulkan_immediate_drawer.h" + +namespace xe { +namespace ui { +namespace vk { + +class VulkanImmediateTexture : public ImmediateTexture { + public: + VulkanImmediateTexture(uint32_t width, uint32_t height, + ImmediateTextureFilter filter, bool repeat); + ~VulkanImmediateTexture() override; +}; + +VulkanImmediateTexture::VulkanImmediateTexture(uint32_t width, uint32_t height, + ImmediateTextureFilter filter, + bool repeat) + : ImmediateTexture(width, height) { + handle = reinterpret_cast(this); +} + +VulkanImmediateTexture::~VulkanImmediateTexture() {} + +VulkanImmediateDrawer::VulkanImmediateDrawer(VulkanContext* graphics_context) + : ImmediateDrawer(graphics_context), context_(graphics_context) {} + +VulkanImmediateDrawer::~VulkanImmediateDrawer() { Shutdown(); } + +bool VulkanImmediateDrawer::Initialize() { return true; } + +void VulkanImmediateDrawer::Shutdown() {} + +std::unique_ptr VulkanImmediateDrawer::CreateTexture( + uint32_t width, uint32_t height, ImmediateTextureFilter filter, bool repeat, + const uint8_t* data) { + auto texture = + std::make_unique(width, height, filter, repeat); + if (data != nullptr) { + UpdateTexture(texture.get(), data); + } + return std::unique_ptr(texture.release()); +} + +void VulkanImmediateDrawer::UpdateTexture(ImmediateTexture* texture, + const uint8_t* data) {} + +void VulkanImmediateDrawer::Begin(int render_target_width, + int render_target_height) {} + +void VulkanImmediateDrawer::BeginDrawBatch(const ImmediateDrawBatch& batch) {} + +void VulkanImmediateDrawer::Draw(const ImmediateDraw& draw) {} + +void VulkanImmediateDrawer::EndDrawBatch() {} + +void VulkanImmediateDrawer::End() {} + +} // namespace vk +} // namespace ui +} // namespace xe diff --git a/src/xenia/ui/vk/vulkan_immediate_drawer.h b/src/xenia/ui/vk/vulkan_immediate_drawer.h new file mode 100644 index 000000000..6230e9511 --- /dev/null +++ b/src/xenia/ui/vk/vulkan_immediate_drawer.h @@ -0,0 +1,51 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2019 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#ifndef XENIA_UI_VK_VULKAN_IMMEDIATE_DRAWER_H_ +#define XENIA_UI_VK_VULKAN_IMMEDIATE_DRAWER_H_ + +#include + +#include "xenia/ui/immediate_drawer.h" +#include "xenia/ui/vk/vulkan_context.h" + +namespace xe { +namespace ui { +namespace vk { + +class VulkanImmediateDrawer : public ImmediateDrawer { + public: + VulkanImmediateDrawer(VulkanContext* graphics_context); + ~VulkanImmediateDrawer() override; + + bool Initialize(); + void Shutdown(); + + std::unique_ptr CreateTexture(uint32_t width, + uint32_t height, + ImmediateTextureFilter filter, + bool repeat, + const uint8_t* data) override; + void UpdateTexture(ImmediateTexture* texture, const uint8_t* data) override; + + void Begin(int render_target_width, int render_target_height) override; + void BeginDrawBatch(const ImmediateDrawBatch& batch) override; + void Draw(const ImmediateDraw& draw) override; + void EndDrawBatch() override; + void End() override; + + private: + VulkanContext* context_ = nullptr; +}; + +} // namespace vk +} // namespace ui +} // namespace xe + +#endif // XENIA_UI_VK_VULKAN_IMMEDIATE_DRAWER_H_ diff --git a/src/xenia/ui/vk/vulkan_provider.cc b/src/xenia/ui/vk/vulkan_provider.cc new file mode 100644 index 000000000..aa829437b --- /dev/null +++ b/src/xenia/ui/vk/vulkan_provider.cc @@ -0,0 +1,230 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2019 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#include "xenia/ui/vk/vulkan_provider.h" + +#include + +#include "xenia/base/cvar.h" +#include "xenia/base/logging.h" +#include "xenia/ui/vk/vulkan_context.h" +#include "xenia/ui/vk/vulkan_util.h" + +DEFINE_bool(vk_validation, false, "Enable Vulkan validation layers.", "Vulkan"); +DEFINE_int32(vk_device, -1, + "Index of the Vulkan physical device to use. -1 to use any " + "compatible.", + "Vulkan"); + +namespace xe { +namespace ui { +namespace vk { + +std::unique_ptr VulkanProvider::Create(Window* main_window) { + std::unique_ptr provider(new VulkanProvider(main_window)); + if (!provider->Initialize()) { + xe::FatalError( + "Unable to initialize Vulkan 1.1 graphics subsystem.\n" + "\n" + "Ensure you have the latest drivers for your GPU and that it supports " + "Vulkan, and install the latest Vulkan runtime from " + "https://vulkan.lunarg.com/sdk/home.\n" + "\n" + "See https://xenia.jp/faq/ for more information and a list of " + "supported GPUs."); + return nullptr; + } + return provider; +} + +VulkanProvider::VulkanProvider(Window* main_window) + : GraphicsProvider(main_window) {} + +VulkanProvider::~VulkanProvider() { + if (device_ != VK_NULL_HANDLE) { + vkDestroyDevice(device_, nullptr); + } + if (instance_ != VK_NULL_HANDLE) { + vkDestroyInstance(instance_, nullptr); + } +} + +bool VulkanProvider::Initialize() { + if (volkInitialize() != VK_SUCCESS) { + XELOGE("Failed to initialize the Vulkan loader volk."); + return false; + } + + const uint32_t api_version = VK_MAKE_VERSION(1, 1, 0); + + // Create the instance. + VkApplicationInfo application_info; + application_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; + application_info.pNext = nullptr; + application_info.pApplicationName = "Xenia"; + application_info.applicationVersion = 1; + application_info.pEngineName = "Xenia"; + application_info.engineVersion = 1; + application_info.apiVersion = api_version; + const char* const validation_layers[] = { + "VK_LAYER_LUNARG_standard_validation", + }; + const char* const instance_extensions[] = { + "VK_KHR_surface", +#if XE_PLATFORM_WIN32 + "VK_KHR_win32_surface", +#endif + }; + VkInstanceCreateInfo instance_create_info; + instance_create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + instance_create_info.pNext = nullptr; + instance_create_info.flags = 0; + instance_create_info.pApplicationInfo = &application_info; + if (cvars::vk_validation) { + instance_create_info.enabledLayerCount = + uint32_t(xe::countof(validation_layers)); + instance_create_info.ppEnabledLayerNames = validation_layers; + } else { + instance_create_info.enabledLayerCount = 0; + instance_create_info.ppEnabledLayerNames = nullptr; + } + instance_create_info.enabledExtensionCount = + uint32_t(xe::countof(instance_extensions)); + instance_create_info.ppEnabledExtensionNames = instance_extensions; + if (vkCreateInstance(&instance_create_info, nullptr, &instance_) != + VK_SUCCESS) { + XELOGE("Failed to create a Vulkan instance."); + return false; + } + volkLoadInstance(instance_); + + // Get a supported physical device. + physical_device_ = nullptr; + std::vector physical_devices; + uint32_t physical_device_count; + if (vkEnumeratePhysicalDevices(instance_, &physical_device_count, nullptr) != + VK_SUCCESS) { + XELOGE("Failed to get Vulkan physical device count."); + return false; + } + physical_devices.resize(physical_device_count); + if (vkEnumeratePhysicalDevices(instance_, &physical_device_count, + physical_devices.data()) != VK_SUCCESS) { + XELOGE("Failed to get Vulkan physical devices."); + return false; + } + physical_devices.resize(physical_device_count); + uint32_t physical_device_index, physical_device_index_end; + if (cvars::vk_device >= 0) { + physical_device_index = uint32_t(cvars::vk_device); + physical_device_index_end = + std::min(physical_device_index + 1, physical_device_count); + } else { + physical_device_index = 0; + physical_device_index_end = physical_device_count; + } + VkPhysicalDeviceFeatures physical_device_features; + std::vector queue_families; + uint32_t queue_family = UINT32_MAX; + bool sparse_residency_buffer = false; + for (; physical_device_index < physical_device_index_end; + ++physical_device_index) { + VkPhysicalDevice physical_device = physical_devices[physical_device_index]; + vkGetPhysicalDeviceFeatures(physical_device, &physical_device_features); + sparse_residency_buffer = physical_device_features.sparseBinding && + physical_device_features.sparseResidencyBuffer; + // Get a queue supporting graphics, compute and transfer, and if available, + // also sparse memory management. + queue_family = UINT32_MAX; + uint32_t queue_family_count; + vkGetPhysicalDeviceQueueFamilyProperties(physical_device, + &queue_family_count, nullptr); + queue_families.resize(queue_family_count); + vkGetPhysicalDeviceQueueFamilyProperties( + physical_device, &queue_family_count, queue_families.data()); + const uint32_t queue_flags_required = + VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT | VK_QUEUE_TRANSFER_BIT; + for (uint32_t i = 0; i < queue_family_count; ++i) { + const VkQueueFamilyProperties& queue_family_properties = + queue_families[i]; + // Arbitrary copying done when loading textures. + if (queue_family_properties.minImageTransferGranularity.width > 1 || + queue_family_properties.minImageTransferGranularity.height > 1 || + queue_family_properties.minImageTransferGranularity.depth > 1) { + continue; + } + if ((queue_family_properties.queueFlags & queue_flags_required) != + queue_flags_required) { + continue; + } + queue_family = i; + if (!sparse_residency_buffer || + (queue_family_properties.queueFlags & VK_QUEUE_SPARSE_BINDING_BIT)) { + // Found a fully compatible queue family, stop searching for a family + // that support both graphics/compute/transfer and sparse binding. + break; + } + } + if (queue_family == UINT32_MAX) { + continue; + } + if (!(queue_families[queue_family].queueFlags & + VK_QUEUE_SPARSE_BINDING_BIT)) { + sparse_residency_buffer = false; + } + physical_device_ = physical_device; + break; + } + if (physical_device_ == VK_NULL_HANDLE) { + XELOGE("Failed to get a supported Vulkan physical device."); + return false; + } + supports_sparse_residency_buffer_ = sparse_residency_buffer; + supports_texture_compression_bc_ = + physical_device_features.textureCompressionBC != VK_FALSE; + // TODO(Triang3l): Check if VK_EXT_fragment_shader_interlock and + // fragmentShaderSampleInterlock are supported. + + // Log physical device properties. + VkPhysicalDeviceProperties physical_device_properties; + vkGetPhysicalDeviceProperties(physical_device_, &physical_device_properties); + XELOGVK("Vulkan physical device: %s (vendor %.4X, device %.4X)", + physical_device_properties.deviceName, + physical_device_properties.vendorID, + physical_device_properties.deviceID); + XELOGVK("* Sparse buffer residency: %s", + supports_sparse_residency_buffer_ ? "yes" : "no"); + XELOGVK("* BC texture compression: %s", + supports_texture_compression_bc_ ? "yes" : "no"); + + return true; +} + +std::unique_ptr VulkanProvider::CreateContext( + Window* target_window) { + auto new_context = + std::unique_ptr(new VulkanContext(this, target_window)); + if (!new_context->Initialize()) { + return nullptr; + } + return std::unique_ptr(new_context.release()); +} + +std::unique_ptr VulkanProvider::CreateOffscreenContext() { + auto new_context = + std::unique_ptr(new VulkanContext(this, nullptr)); + if (!new_context->Initialize()) { + return nullptr; + } + return std::unique_ptr(new_context.release()); +} + +} // namespace vk +} // namespace ui +} // namespace xe diff --git a/src/xenia/ui/vk/vulkan_provider.h b/src/xenia/ui/vk/vulkan_provider.h new file mode 100644 index 000000000..6672078c2 --- /dev/null +++ b/src/xenia/ui/vk/vulkan_provider.h @@ -0,0 +1,54 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2019 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#ifndef XENIA_UI_VK_VULKAN_PROVIDER_H_ +#define XENIA_UI_VK_VULKAN_PROVIDER_H_ + +#include + +#include "third_party/volk/volk.h" + +#define XELOGVK XELOGI + +#include "xenia/ui/graphics_provider.h" + +namespace xe { +namespace ui { +namespace vk { + +class VulkanImmediateDrawer; + +class VulkanProvider : public GraphicsProvider { + public: + ~VulkanProvider() override; + + static std::unique_ptr Create(Window* main_window); + + std::unique_ptr CreateContext( + Window* target_window) override; + std::unique_ptr CreateOffscreenContext() override; + + private: + explicit VulkanProvider(Window* main_window); + + bool Initialize(); + + VkInstance instance_ = VK_NULL_HANDLE; + VkPhysicalDevice physical_device_ = VK_NULL_HANDLE; + bool supports_sparse_residency_buffer_ = false; + bool supports_texture_compression_bc_ = false; + VkDevice device_ = VK_NULL_HANDLE; + VkQueue graphics_queue_ = VK_NULL_HANDLE; +}; + +} // namespace vk +} // namespace ui +} // namespace xe + +#endif // XENIA_UI_VK_VULKAN_PROVIDER_H_ diff --git a/src/xenia/ui/vk/vulkan_util.h b/src/xenia/ui/vk/vulkan_util.h new file mode 100644 index 000000000..37e74c560 --- /dev/null +++ b/src/xenia/ui/vk/vulkan_util.h @@ -0,0 +1,35 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2019 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#ifndef XENIA_UI_VK_VULKAN_UTIL_H_ +#define XENIA_UI_VK_VULKAN_UTIL_H_ + +#include "xenia/ui/vk/vulkan_provider.h" + +namespace xe { +namespace ui { +namespace vk { +namespace util { + +template +inline bool DestroyAndNullHandle(F* destroy_function, T& handle) { + if (handle != VK_NULL_HANDLE) { + destroy_function(handle, nullptr); + handle = VK_NULL_HANDLE; + return true; + } + return false; +} + +} // namespace util +} // namespace vk +} // namespace ui +} // namespace xe + +#endif // XENIA_UI_VK_VULKAN_UTIL_H_