diff --git a/src/xenia/app/emulator_window.cc b/src/xenia/app/emulator_window.cc index f74e2d744..654a0b398 100644 --- a/src/xenia/app/emulator_window.cc +++ b/src/xenia/app/emulator_window.cc @@ -11,22 +11,29 @@ #include +#include "emulator_window.h" #include "third_party/imgui/imgui.h" +#include "xenia/apu/nop/nop_audio_system.h" +#include "xenia/apu/sdl/sdl_audio_system.h" #include "xenia/apu/xaudio2/xaudio2_audio_system.h" -#include "xenia/base/clock.h" #include "xenia/base/cvar.h" #include "xenia/base/debugging.h" +#include "xenia/base/factory.h" #include "xenia/base/logging.h" #include "xenia/base/platform.h" #include "xenia/base/profiling.h" #include "xenia/base/threading.h" #include "xenia/emulator.h" +#include "xenia/gpu/command_processor.h" +#include "xenia/gpu/d3d12/d3d12_graphics_system.h" #include "xenia/gpu/graphics_system.h" +#include "xenia/gpu/null/null_graphics_system.h" #include "xenia/gpu/vulkan/vulkan_graphics_system.h" #include "xenia/hid/input_system.h" +#include "xenia/hid/nop/nop_hid.h" +#include "xenia/hid/sdl/sdl_hid.h" +#include "xenia/hid/winkey/winkey_hid.h" #include "xenia/hid/xinput/xinput_hid.h" -#include "xenia/ui/vulkan/vulkan_instance.h" -#include "xenia/ui/vulkan/vulkan_provider.h" DEFINE_string(apu, "any", "Audio system. Use: [any, nop, xaudio2]", "General"); DEFINE_string(gpu, "any", "Graphics system. Use: [any, vulkan, null]", @@ -41,158 +48,97 @@ DEFINE_bool(fullscreen, false, "Toggles fullscreen", "General"); namespace xe { namespace app { -class VulkanWindow : public QVulkanWindow { - public: - VulkanWindow(gpu::vulkan::VulkanGraphicsSystem* gfx) - : graphics_system_(gfx) {} - QVulkanWindowRenderer* createRenderer() override; +std::unique_ptr CreateAudioSystem(cpu::Processor* processor) { + Factory factory; +#if XE_PLATFORM_WIN32 + factory.Add("xaudio2"); +#endif // XE_PLATFORM_WIN32 + factory.Add("sdl"); + factory.Add("nop"); + return factory.Create(cvars::apu, processor); +} - private: - gpu::vulkan::VulkanGraphicsSystem* graphics_system_; -}; +std::unique_ptr CreateGraphicsSystem() { + Factory factory; +#if XE_PLATFORM_WIN32 + factory.Add("d3d12"); +#endif // XE_PLATFORM_WIN32 + factory.Add("vulkan"); + factory.Add("null"); + return factory.Create(cvars::gpu); +} -class VulkanRenderer : public QVulkanWindowRenderer { - public: - VulkanRenderer(VulkanWindow* window, - gpu::vulkan::VulkanGraphicsSystem* graphics_system) - : window_(window), graphics_system_(graphics_system) {} - - void startNextFrame() override { - // Copy the graphics frontbuffer to our backbuffer. - //auto swap_state = graphics_system_->swap_state(); - - auto cmd = window_->currentCommandBuffer(); - //auto src = reinterpret_cast( - // swap_state->buffer_textures[swap_state->current_buffer]); - auto dest = window_->swapChainImage(window_->currentSwapChainImageIndex()); - auto dest_size = window_->swapChainImageSize(); - - VkImageMemoryBarrier barrier; - std::memset(&barrier, 0, sizeof(VkImageMemoryBarrier)); - barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; - barrier.oldLayout = VK_IMAGE_LAYOUT_GENERAL; - barrier.newLayout = VK_IMAGE_LAYOUT_GENERAL; - barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - //barrier.image = src; - barrier.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}; - vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TRANSFER_BIT, - VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, - nullptr, 1, &barrier); - - VkImageBlit region; - region.srcSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}; - region.srcOffsets[0] = {0, 0, 0}; - /*region.srcOffsets[1] = {static_cast(swap_state->width), - static_cast(swap_state->height), 1};*/ - - region.dstSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}; - region.dstOffsets[0] = {0, 0, 0}; - region.dstOffsets[1] = {static_cast(dest_size.width()), - static_cast(dest_size.height()), 1}; - /* vkCmdBlitImage(cmd, src, VK_IMAGE_LAYOUT_GENERAL, dest, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion, - VK_FILTER_LINEAR);*/ - - //swap_state->pending = false; - window_->frameReady(); +std::vector> CreateInputDrivers( + ui::Window* window) { + std::vector> drivers; + if (cvars::hid.compare("nop") == 0) { + drivers.emplace_back(hid::nop::Create(window)); + } else { + Factory factory; +#if XE_PLATFORM_WIN32 + factory.Add("xinput", hid::xinput::Create); + // WinKey input driver should always be the last input driver added! + factory.Add("winkey", hid::winkey::Create); +#endif // XE_PLATFORM_WIN32 + factory.Add("sdl", hid::sdl::Create); + for (auto& driver : factory.CreateAll(cvars::hid, window)) { + if (XSUCCEEDED(driver->Setup())) { + drivers.emplace_back(std::move(driver)); + } + } + if (drivers.empty()) { + // Fallback to nop if none created. + drivers.emplace_back(xe::hid::nop::Create(window)); + } } - - private: - gpu::vulkan::VulkanGraphicsSystem* graphics_system_; - VulkanWindow* window_; -}; - -QVulkanWindowRenderer* VulkanWindow::createRenderer() { - return new VulkanRenderer(this, graphics_system_); + return drivers; } EmulatorWindow::EmulatorWindow(Loop* loop, const std::string& title) : QtWindow(loop, title) { // TODO(DrChat): Pass in command line arguments. - emulator_ = std::make_unique("","",""); + emulator_ = std::make_unique("", "", ""); - auto audio_factory = [&](cpu::Processor* processor, - kernel::KernelState* kernel_state) { - auto audio = apu::xaudio2::XAudio2AudioSystem::Create(processor); - if (audio->Setup(kernel_state) != X_STATUS_SUCCESS) { - audio->Shutdown(); - return std::unique_ptr(nullptr); - } - - return audio; - }; - - graphics_provider_ = ui::vulkan::VulkanProvider::Create(nullptr); - auto graphics_factory = [&](cpu::Processor* processor, - kernel::KernelState* kernel_state) { - auto graphics = std::make_unique(); - if (graphics->Setup(processor, kernel_state, - graphics_provider_->CreateOffscreenContext()->target_window())) { - graphics->Shutdown(); - return std::unique_ptr(nullptr); - } - - return graphics; - }; - - auto input_factory = [&](ui::Window* window) { - std::vector> drivers; - auto xinput_driver = hid::xinput::Create(window); - xinput_driver->Setup(); - drivers.push_back(std::move(xinput_driver)); - - return drivers; - }; - - //X_STATUS result = emulator_->Setup(this, audio_factory, graphics_factory, input_factory); - //if (result == X_STATUS_SUCCESS) { - // // Setup a callback called when the emulator wants to swap. - // emulator_->graphics_system()->SetSwapCallback([&]() { - // QMetaObject::invokeMethod(this->graphics_window_.get(), "requestUpdate", - // Qt::QueuedConnection); - // }); - //} - - //// Initialize our backend display window. - //if (!InitializeVulkan()) { - // return; - //} - - //// Set a callback on launch - //emulator_->on_launch.AddListener([this]() { - // auto title_db = this->emulator()->game_data(); - // if (title_db) { - // QPixmap p; - // auto icon_block = title_db->icon(); - // if (icon_block.buffer && - // p.loadFromData(icon_block.buffer, uint(icon_block.size), "PNG")) { - // this->setWindowIcon(QIcon(p)); - // } - // } - //}); -} - -bool EmulatorWindow::InitializeVulkan() { - auto provider = - reinterpret_cast(graphics_provider_.get()); - - // Create a Qt wrapper around our vulkan instance. - vulkan_instance_ = std::make_unique(); - vulkan_instance_->setVkInstance(*provider->instance()); - if (!vulkan_instance_->create()) { - return false; + X_STATUS result = emulator_->Setup(this, CreateAudioSystem, + CreateGraphicsSystem, CreateInputDrivers); + if (result == X_STATUS_SUCCESS) { + // Setup a callback called when the emulator wants to swap. + emulator_->graphics_system()->command_processor()->set_swap_request_handler( + [&]() { + auto graphics_window = + reinterpret_cast(this->graphics_window_.get()); + QMetaObject::invokeMethod(graphics_window, "requestUpdate", + Qt::QueuedConnection); + }); } - graphics_window_ = std::make_unique( - reinterpret_cast( - emulator_->graphics_system())); - graphics_window_->setVulkanInstance(vulkan_instance_.get()); + if (!EmulatorWindow::Initialize()) { + return; + } + // Set a callback on launch + emulator_->on_launch.AddListener( + [this](unsigned int title_id, std ::string_view title) { + auto title_db = this->emulator()->game_data(); + if (title_db) { + QPixmap p; + auto icon_block = title_db->icon(); + if (icon_block.buffer && + p.loadFromData(icon_block.buffer, uint(icon_block.size), "PNG")) { + this->setWindowIcon(QIcon(p)); + } + } + }); +} + +bool EmulatorWindow::Initialize() { + if (!graphics_window_->Initialize()) { + XELOGE("Could not initialize graphics window"); + return false; + } // Now set the graphics window as our central widget. - QWidget* wrapper = QWidget::createWindowContainer(graphics_window_.get()); + QWidget* wrapper = QWidget::createWindowContainer( + dynamic_cast(graphics_window_.get())); setCentralWidget(wrapper); return true; diff --git a/src/xenia/app/emulator_window.h b/src/xenia/app/emulator_window.h index 76b841e52..47ce1609e 100644 --- a/src/xenia/app/emulator_window.h +++ b/src/xenia/app/emulator_window.h @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2018 Ben Vanik. All rights reserved. * + * Copyright 2020 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -10,15 +10,11 @@ #ifndef XENIA_APP_MAIN_WINDOW_H_ #define XENIA_APP_MAIN_WINDOW_H_ -#include -#include -#include - #include "xenia/emulator.h" #include "xenia/ui/graphics_context.h" -#include "xenia/ui/graphics_provider.h" -#include "xenia/ui/qt/window_qt.h" +#include "xenia/ui/qt/graphics_window.h" #include "xenia/ui/qt/loop_qt.h" +#include "xenia/ui/qt/window_qt.h" namespace xe { namespace app { @@ -26,36 +22,28 @@ namespace app { class VulkanWindow; class VulkanRenderer; -using ui::qt::QtWindow; using ui::Loop; +using ui::qt::GraphicsWindow; +using ui::qt::QtWindow; -class EmulatorWindow : public ui::qt::QtWindow { +class EmulatorWindow : public QtWindow { Q_OBJECT public: - EmulatorWindow(Loop *loop, const std::string& title); + EmulatorWindow(Loop* loop, const std::string& title); bool Launch(const std::string& path); - xe::Emulator* emulator() { return emulator_.get(); } - - protected: - // Events - - private slots: - + Emulator* emulator() const { return emulator_.get(); } + GraphicsWindow* graphics_window() const { return graphics_window_.get(); } + hid::InputSystem* input_system() const { return input_system_.get(); } private: + bool Initialize() override; void CreateMenuBar(); - bool InitializeVulkan(); - - std::unique_ptr emulator_; - - std::unique_ptr graphics_window_; - std::unique_ptr graphics_provider_; + std::unique_ptr emulator_; + std::unique_ptr graphics_window_; std::unique_ptr input_system_; - - std::unique_ptr vulkan_instance_; }; } // namespace app diff --git a/src/xenia/ui/qt/d3d12_graphics_window.cc b/src/xenia/ui/qt/d3d12_graphics_window.cc new file mode 100644 index 000000000..f7a459509 --- /dev/null +++ b/src/xenia/ui/qt/d3d12_graphics_window.cc @@ -0,0 +1,24 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2020 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#include "d3d12_graphics_window.h" + +namespace xe { +namespace ui { +namespace qt { + +bool D3D12GraphicsWindow::Initialize() { + // TODO: + + return true; +} + +} // namespace qt +} // namespace ui +} // namespace xe diff --git a/src/xenia/ui/qt/d3d12_graphics_window.h b/src/xenia/ui/qt/d3d12_graphics_window.h new file mode 100644 index 000000000..20f9e99d3 --- /dev/null +++ b/src/xenia/ui/qt/d3d12_graphics_window.h @@ -0,0 +1,31 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2020 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#ifndef XENIA_UI_QT_D3D12_GRAPHICS_WINDOW_H_ +#define XENIA_UI_QT_D3D12_GRAPHICS_WINDOW_H_ + +#include "graphics_window.h" + +namespace xe { +namespace ui { +namespace qt { + +class D3D12GraphicsWindow : public GraphicsWindowImpl<> { + Q_OBJECT + D3D12GraphicsWindow(Emulator* emulator) : GraphicsWindowImpl(emulator) {} + + public: + bool Initialize() override; +}; + +} // namespace qt +} // namespace ui +} // namespace xe + +#endif diff --git a/src/xenia/ui/qt/graphics_window.h b/src/xenia/ui/qt/graphics_window.h new file mode 100644 index 000000000..e05c5b550 --- /dev/null +++ b/src/xenia/ui/qt/graphics_window.h @@ -0,0 +1,45 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2020 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#ifndef XENIA_UI_QT_GRAPHICS_WINDOW_H_ +#define XENIA_UI_QT_GRAPHICS_WINDOW_H_ + +#include + +#include "xenia/emulator.h" + +namespace xe { +namespace ui { +namespace qt { + +class GraphicsWindow { + public: + GraphicsWindow(Emulator* emulator) : emulator_(emulator) {} + virtual ~GraphicsWindow() = default; + + Emulator* emulator() const { return emulator_; } + virtual bool Initialize() = 0; + + private: + Emulator* emulator_; +}; + +template +class GraphicsWindowImpl : public T, public GraphicsWindow { + static_assert(std::is_base_of_v, + "GraphicsWindow must inherit from a QWindow-based class"); + public: + GraphicsWindowImpl(Emulator* emulator) : T(), GraphicsWindow(emulator) {} +}; + +} // namespace qt +} // namespace ui +} // namespace xe + +#endif diff --git a/src/xenia/ui/qt/vulkan_graphics_window.cc b/src/xenia/ui/qt/vulkan_graphics_window.cc new file mode 100644 index 000000000..722fa5e55 --- /dev/null +++ b/src/xenia/ui/qt/vulkan_graphics_window.cc @@ -0,0 +1,62 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2020 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#include "vulkan_graphics_window.h" + +#include + +#include "xenia/gpu/command_processor.h" +#include "xenia/gpu/vulkan/vulkan_graphics_system.h" +#include "xenia/ui/vulkan/vulkan_instance.h" +#include "xenia/ui/vulkan/vulkan_provider.h" + +namespace xe { +namespace ui { +namespace qt { + +class VulkanRenderer : public QVulkanWindowRenderer { + public: + VulkanRenderer(VulkanGraphicsWindow* window, + gpu::vulkan::VulkanGraphicsSystem* graphics_system) + : window_(window), graphics_system_(graphics_system) {} + + void startNextFrame() override { + // NEED TO DO STUFF HERE IDK + } + + private: + VulkanGraphicsWindow* window_; + gpu::vulkan::VulkanGraphicsSystem* graphics_system_; +}; + +QVulkanWindowRenderer* VulkanGraphicsWindow::createRenderer() { + auto graphics_system = reinterpret_cast( + emulator()->graphics_system()); + return new VulkanRenderer( + this, graphics_system); +} + +bool VulkanGraphicsWindow::Initialize() { + // copied from DrChat's original Qt branch + auto provider = reinterpret_cast( + emulator()->graphics_system()->provider()); + + // Create a Qt wrapper around our vulkan instance. + vulkan_instance_.setVkInstance(*provider->instance()); + if (!vulkan_instance_.create()) { + return false; + } + setVulkanInstance(&vulkan_instance_); + + return true; +} + +} // namespace qt +} // namespace ui +} // namespace xe diff --git a/src/xenia/ui/qt/vulkan_graphics_window.h b/src/xenia/ui/qt/vulkan_graphics_window.h new file mode 100644 index 000000000..7ece04e9f --- /dev/null +++ b/src/xenia/ui/qt/vulkan_graphics_window.h @@ -0,0 +1,38 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2020 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#ifndef XENIA_UI_QT_VULKAN_GRAPHICS_WINDOW_H_ +#define XENIA_UI_QT_VULKAN_GRAPHICS_WINDOW_H_ + +#include +#include "graphics_window.h" + +namespace xe { +namespace ui { +namespace qt { + +class VulkanGraphicsWindow : public GraphicsWindowImpl { + Q_OBJECT +public: + VulkanGraphicsWindow(Emulator* emulator) + : GraphicsWindowImpl(emulator) {} + + bool Initialize() override; + + private: + QVulkanWindowRenderer* createRenderer() override; + + QVulkanInstance vulkan_instance_; +}; + +} // namespace qt +} // namespace ui +} // namespace xe + +#endif \ No newline at end of file