From 0f2c8a4e64313ccbaf118512613303000e48de09 Mon Sep 17 00:00:00 2001 From: "Dr. Chat" Date: Mon, 14 May 2018 19:30:01 -0500 Subject: [PATCH] [GPU] GraphicsSystem now owns swap_state Swapping in Qt windows now --- src/xenia/app/emulator_window.cc | 75 ++++++++- src/xenia/app/emulator_window.h | 6 + src/xenia/gpu/command_processor.cc | 19 ++- src/xenia/gpu/command_processor.h | 25 --- src/xenia/gpu/graphics_system.h | 28 +++- src/xenia/gpu/null/null_graphics_system.cc | 5 +- src/xenia/gpu/null/null_graphics_system.h | 2 + src/xenia/gpu/trace_player.cc | 4 +- .../gpu/vulkan/vulkan_command_processor.cc | 156 +++++------------- .../gpu/vulkan/vulkan_command_processor.h | 8 - .../gpu/vulkan/vulkan_graphics_system.cc | 108 +++++++++--- src/xenia/gpu/vulkan/vulkan_graphics_system.h | 18 +- 12 files changed, 270 insertions(+), 184 deletions(-) diff --git a/src/xenia/app/emulator_window.cc b/src/xenia/app/emulator_window.cc index 41fefe268..79cb97311 100644 --- a/src/xenia/app/emulator_window.cc +++ b/src/xenia/app/emulator_window.cc @@ -28,6 +28,71 @@ DEFINE_bool(fullscreen, false, "Toggles fullscreen"); namespace xe { namespace app { +class VulkanWindow : public QVulkanWindow { + public: + VulkanWindow(EmulatorWindow* parent) : window_(parent) {} + QVulkanWindowRenderer* createRenderer() override; + + private: + EmulatorWindow* window_; +}; + +class VulkanRenderer : public QVulkanWindowRenderer { + public: + VulkanRenderer(VulkanWindow* window, xe::Emulator* emulator) + : window_(window), emulator_(emulator) {} + + void startNextFrame() override { + // Copy the graphics frontbuffer to our backbuffer. + auto swap_state = emulator_->graphics_system()->swap_state(); + + auto cmd = window_->currentCommandBuffer(); + auto src = reinterpret_cast(swap_state->front_buffer_texture); + 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(); + } + + private: + xe::Emulator* emulator_; + VulkanWindow* window_; +}; + +QVulkanWindowRenderer* VulkanWindow::createRenderer() { + return new VulkanRenderer(this, window_->emulator()); +} + EmulatorWindow::EmulatorWindow() {} bool EmulatorWindow::Setup() { @@ -52,8 +117,10 @@ bool EmulatorWindow::Setup() { return std::unique_ptr(nullptr); } - graphics->SetSwapCallback( - [&]() { this->graphics_window_->requestUpdate(); }); + graphics->SetSwapCallback([&]() { + QMetaObject::invokeMethod(this->graphics_window_.get(), "requestUpdate", + Qt::QueuedConnection); + }); return graphics; }; @@ -72,7 +139,7 @@ bool EmulatorWindow::InitializeVulkan() { return false; } - graphics_window_ = std::make_unique(); + graphics_window_ = std::make_unique(this); graphics_window_->setVulkanInstance(vulkan_instance_.get()); // Now set the graphics window as our central widget. @@ -83,6 +150,8 @@ bool EmulatorWindow::InitializeVulkan() { return true; } +void EmulatorWindow::SwapVulkan() {} + bool EmulatorWindow::Launch(const std::wstring& path) { return emulator_->LaunchPath(path) == X_STATUS_SUCCESS; } diff --git a/src/xenia/app/emulator_window.h b/src/xenia/app/emulator_window.h index cbe471202..86cc1a4ba 100644 --- a/src/xenia/app/emulator_window.h +++ b/src/xenia/app/emulator_window.h @@ -21,6 +21,9 @@ namespace xe { namespace app { +class VulkanWindow; +class VulkanRenderer; + class EmulatorWindow : public QMainWindow { Q_OBJECT @@ -29,8 +32,11 @@ class EmulatorWindow : public QMainWindow { bool Setup(); bool InitializeVulkan(); + void SwapVulkan(); bool Launch(const std::wstring& path); + xe::Emulator* emulator() { return emulator_.get(); } + protected: // Events diff --git a/src/xenia/gpu/command_processor.cc b/src/xenia/gpu/command_processor.cc index 07dd4d729..9b0c3c313 100644 --- a/src/xenia/gpu/command_processor.cc +++ b/src/xenia/gpu/command_processor.cc @@ -189,7 +189,7 @@ void CommandProcessor::Pause() { }); // HACK - Prevents a hang in IssueSwap() - swap_state_.pending = false; + graphics_system_->swap_state()->pending = false; fence.Wait(); } @@ -377,10 +377,11 @@ void CommandProcessor::IssueSwap(uint32_t frontbuffer_ptr, // This prevents the display from pulling the backbuffer out from under us. // If we skip a lot then we may need to buffer more, but as the display // thread should be fairly idle that shouldn't happen. + auto swap_state = graphics_system_->swap_state(); if (!FLAGS_vsync) { - std::lock_guard lock(swap_state_.mutex); - if (swap_state_.pending) { - swap_state_.pending = false; + std::lock_guard lock(swap_state->mutex); + if (swap_state->pending) { + swap_state->pending = false; // TODO(benvanik): frame skip counter. XELOGW("Skipped frame!"); } @@ -388,8 +389,8 @@ void CommandProcessor::IssueSwap(uint32_t frontbuffer_ptr, // Spin until no more pending swap. while (worker_running_) { { - std::lock_guard lock(swap_state_.mutex); - if (!swap_state_.pending) { + std::lock_guard lock(swap_state->mutex); + if (!swap_state->pending) { break; } } @@ -401,8 +402,8 @@ void CommandProcessor::IssueSwap(uint32_t frontbuffer_ptr, { // Set pending so that the display will swap the next time it can. - std::lock_guard lock(swap_state_.mutex); - swap_state_.pending = true; + std::lock_guard lock(swap_state->mutex); + swap_state->pending = true; } // Notify the display a swap is pending so that our changes are picked up. @@ -799,7 +800,7 @@ bool CommandProcessor::ExecutePacketType3_XE_SWAP(RingBuffer* reader, uint32_t frontbuffer_height = reader->ReadAndSwap(); reader->AdvanceRead((count - 4) * sizeof(uint32_t)); - if (swap_mode_ == SwapMode::kNormal) { + if (graphics_system_->swap_mode() == SwapMode::kNormal) { IssueSwap(frontbuffer_ptr, frontbuffer_width, frontbuffer_height); } diff --git a/src/xenia/gpu/command_processor.h b/src/xenia/gpu/command_processor.h index bd1d56b91..dcfd588cc 100644 --- a/src/xenia/gpu/command_processor.h +++ b/src/xenia/gpu/command_processor.h @@ -37,27 +37,6 @@ namespace gpu { class GraphicsSystem; class Shader; -struct SwapState { - // Lock must be held when changing data in this structure. - std::mutex mutex; - // Dimensions of the framebuffer textures. Should match window size. - uint32_t width = 0; - uint32_t height = 0; - // Current front buffer, being drawn to the screen. - uintptr_t front_buffer_texture = 0; - // Current back buffer, being updated by the CP. - uintptr_t back_buffer_texture = 0; - // Backend data - void* backend_data = nullptr; - // Whether the back buffer is dirty and a swap is pending. - bool pending = false; -}; - -enum class SwapMode { - kNormal, - kIgnored, -}; - enum class GammaRampType { kUnknown = 0, kNormal, @@ -121,8 +100,6 @@ class CommandProcessor { virtual void ClearCaches(); - SwapState& swap_state() { return swap_state_; } - void set_swap_mode(SwapMode swap_mode) { swap_mode_ = swap_mode; } void IssueSwap(uint32_t frontbuffer_ptr, uint32_t frontbuffer_width, uint32_t frontbuffer_height); @@ -256,8 +233,6 @@ class CommandProcessor { kernel::object_ref worker_thread_; std::unique_ptr context_; - SwapMode swap_mode_ = SwapMode::kNormal; - SwapState swap_state_; std::function swap_request_handler_; std::queue> pending_fns_; diff --git a/src/xenia/gpu/graphics_system.h b/src/xenia/gpu/graphics_system.h index f9b637338..c2ee28dcd 100644 --- a/src/xenia/gpu/graphics_system.h +++ b/src/xenia/gpu/graphics_system.h @@ -29,6 +29,27 @@ class Emulator; namespace xe { namespace gpu { +struct SwapState { + // Lock must be held when changing data in this structure. + std::mutex mutex; + // Dimensions of the framebuffer textures. Should match window size. + uint32_t width = 0; + uint32_t height = 0; + // Current front buffer, being drawn to the screen. + uintptr_t front_buffer_texture = 0; + // Current back buffer, being updated by the CP. + uintptr_t back_buffer_texture = 0; + // Backend data + void* backend_data = nullptr; + // Whether the back buffer is dirty and a swap is pending. + bool pending = false; +}; + +enum class SwapMode { + kNormal, + kIgnored, +}; + class GraphicsSystem { public: virtual ~GraphicsSystem(); @@ -62,7 +83,10 @@ class GraphicsSystem { virtual void ClearCaches(); void SetSwapCallback(std::function fn); - gpu::SwapState& swap_state() { return command_processor_->swap_state(); } + virtual SwapState* swap_state() = 0; + + SwapMode swap_mode() { return swap_mode_; } + void set_swap_mode(SwapMode swap_mode) { swap_mode_ = swap_mode; } void RequestFrameTrace(); void BeginTracing(); @@ -98,6 +122,8 @@ class GraphicsSystem { uint32_t interrupt_callback_ = 0; uint32_t interrupt_callback_data_ = 0; + SwapMode swap_mode_ = SwapMode::kNormal; + std::atomic vsync_worker_running_; kernel::object_ref vsync_worker_thread_; diff --git a/src/xenia/gpu/null/null_graphics_system.cc b/src/xenia/gpu/null/null_graphics_system.cc index 5a6b62322..914a604e3 100644 --- a/src/xenia/gpu/null/null_graphics_system.cc +++ b/src/xenia/gpu/null/null_graphics_system.cc @@ -39,9 +39,8 @@ void NullGraphicsSystem::Swap(xe::ui::UIEvent* e) { return; } - auto& swap_state = command_processor_->swap_state(); - std::lock_guard lock(swap_state.mutex); - swap_state.pending = false; + std::lock_guard lock(swap_state_.mutex); + swap_state_.pending = false; } } // namespace null diff --git a/src/xenia/gpu/null/null_graphics_system.h b/src/xenia/gpu/null/null_graphics_system.h index d96f5a15f..45ce71ad9 100644 --- a/src/xenia/gpu/null/null_graphics_system.h +++ b/src/xenia/gpu/null/null_graphics_system.h @@ -25,6 +25,7 @@ class NullGraphicsSystem : public GraphicsSystem { ~NullGraphicsSystem() override; std::wstring name() const override { return L"null"; } + SwapState* swap_state() override { return &swap_state_; } X_STATUS Setup(cpu::Processor* processor, kernel::KernelState* kernel_state, std::unique_ptr context) override; @@ -34,6 +35,7 @@ class NullGraphicsSystem : public GraphicsSystem { std::unique_ptr CreateCommandProcessor() override; void Swap(xe::ui::UIEvent* e) override; + SwapState swap_state_ = {}; }; } // namespace null diff --git a/src/xenia/gpu/trace_player.cc b/src/xenia/gpu/trace_player.cc index b79b49df2..a9bd05de9 100644 --- a/src/xenia/gpu/trace_player.cc +++ b/src/xenia/gpu/trace_player.cc @@ -103,7 +103,7 @@ void TracePlayer::PlayTraceOnThread(const uint8_t* trace_data, command_processor->ClearCaches(); } - command_processor->set_swap_mode(SwapMode::kIgnored); + graphics_system_->set_swap_mode(SwapMode::kIgnored); playback_percent_ = 0; auto trace_end = trace_data + trace_size; @@ -200,7 +200,7 @@ void TracePlayer::PlayTraceOnThread(const uint8_t* trace_data, } playing_trace_ = false; - command_processor->set_swap_mode(SwapMode::kNormal); + graphics_system_->set_swap_mode(SwapMode::kNormal); command_processor->IssueSwap(0, 1280, 720); playback_event_->Set(); diff --git a/src/xenia/gpu/vulkan/vulkan_command_processor.cc b/src/xenia/gpu/vulkan/vulkan_command_processor.cc index cce2ed3e6..ec64bc743 100644 --- a/src/xenia/gpu/vulkan/vulkan_command_processor.cc +++ b/src/xenia/gpu/vulkan/vulkan_command_processor.cc @@ -128,11 +128,6 @@ bool VulkanCommandProcessor::SetupContext() { void VulkanCommandProcessor::ShutdownContext() { // TODO(benvanik): wait until idle. - if (swap_state_.front_buffer_texture) { - // Free swap chain image. - DestroySwapImage(); - } - buffer_cache_.reset(); pipeline_cache_.reset(); render_cache_.reset(); @@ -229,105 +224,6 @@ void VulkanCommandProcessor::WriteRegister(uint32_t index, uint32_t value) { } } -void VulkanCommandProcessor::CreateSwapImage(VkCommandBuffer setup_buffer, - VkExtent2D extents) { - VkImageCreateInfo image_info; - std::memset(&image_info, 0, sizeof(VkImageCreateInfo)); - image_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; - image_info.imageType = VK_IMAGE_TYPE_2D; - image_info.format = VK_FORMAT_R8G8B8A8_UNORM; - image_info.extent = {extents.width, extents.height, 1}; - image_info.mipLevels = 1; - image_info.arrayLayers = 1; - image_info.samples = VK_SAMPLE_COUNT_1_BIT; - image_info.tiling = VK_IMAGE_TILING_OPTIMAL; - image_info.usage = - VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; - image_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - image_info.queueFamilyIndexCount = 0; - image_info.pQueueFamilyIndices = nullptr; - image_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - - VkImage image_fb; - auto status = vkCreateImage(*device_, &image_info, nullptr, &image_fb); - CheckResult(status, "vkCreateImage"); - - // Bind memory to image. - VkMemoryRequirements mem_requirements; - vkGetImageMemoryRequirements(*device_, image_fb, &mem_requirements); - fb_memory_ = device_->AllocateMemory(mem_requirements, 0); - assert_not_null(fb_memory_); - - status = vkBindImageMemory(*device_, image_fb, fb_memory_, 0); - CheckResult(status, "vkBindImageMemory"); - - std::lock_guard lock(swap_state_.mutex); - swap_state_.front_buffer_texture = reinterpret_cast(image_fb); - - VkImageViewCreateInfo view_create_info = { - VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, - nullptr, - 0, - image_fb, - VK_IMAGE_VIEW_TYPE_2D, - VK_FORMAT_R8G8B8A8_UNORM, - {VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, - VK_COMPONENT_SWIZZLE_A}, - {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}, - }; - status = - vkCreateImageView(*device_, &view_create_info, nullptr, &fb_image_view_); - CheckResult(status, "vkCreateImageView"); - - VkFramebufferCreateInfo framebuffer_create_info = { - VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, - nullptr, - 0, - blitter_->GetRenderPass(VK_FORMAT_R8G8B8A8_UNORM, true), - 1, - &fb_image_view_, - extents.width, - extents.height, - 1, - }; - status = vkCreateFramebuffer(*device_, &framebuffer_create_info, nullptr, - &fb_framebuffer_); - CheckResult(status, "vkCreateFramebuffer"); - - // Transition image to general layout. - VkImageMemoryBarrier barrier; - std::memset(&barrier, 0, sizeof(VkImageMemoryBarrier)); - barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - barrier.srcAccessMask = 0; - barrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; - barrier.newLayout = VK_IMAGE_LAYOUT_GENERAL; - barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - barrier.image = image_fb; - barrier.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}; - - vkCmdPipelineBarrier(setup_buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, - VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0, 0, - nullptr, 0, nullptr, 1, &barrier); -} - -void VulkanCommandProcessor::DestroySwapImage() { - vkDestroyFramebuffer(*device_, fb_framebuffer_, nullptr); - vkDestroyImageView(*device_, fb_image_view_, nullptr); - - std::lock_guard lock(swap_state_.mutex); - vkDestroyImage(*device_, - reinterpret_cast(swap_state_.front_buffer_texture), - nullptr); - vkFreeMemory(*device_, fb_memory_, nullptr); - - swap_state_.front_buffer_texture = 0; - fb_memory_ = nullptr; - fb_framebuffer_ = nullptr; - fb_image_view_ = nullptr; -} - void VulkanCommandProcessor::BeginFrame() { assert_false(frame_open_); @@ -422,10 +318,46 @@ void VulkanCommandProcessor::PerformSwap(uint32_t frontbuffer_ptr, frontbuffer_ptr = last_copy_base_; } - if (!swap_state_.front_buffer_texture) { - CreateSwapImage(copy_commands, {frontbuffer_width, frontbuffer_height}); + auto swap_state = + reinterpret_cast(graphics_system_)->swap_state(); + + // Create a framebuffer if it isn't already created. + if (!swap_state->fb_framebuffer_) { + VkFramebufferCreateInfo framebuffer_create_info = { + VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, + nullptr, + 0, + blitter_->GetRenderPass(VK_FORMAT_R8G8B8A8_UNORM, true), + 1, + &swap_state->fb_image_view_, + swap_state->width, + swap_state->height, + 1, + }; + status = vkCreateFramebuffer(*device_, &framebuffer_create_info, nullptr, + &swap_state->fb_framebuffer_); + CheckResult(status, "vkCreateFramebuffer"); + } + + auto swap_fb = reinterpret_cast(swap_state->front_buffer_texture); + if (swap_state->fb_image_layout_ == VK_IMAGE_LAYOUT_UNDEFINED) { + // Transition image to general layout. + VkImageMemoryBarrier barrier; + std::memset(&barrier, 0, sizeof(VkImageMemoryBarrier)); + barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + barrier.srcAccessMask = 0; + barrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; + barrier.newLayout = VK_IMAGE_LAYOUT_GENERAL; + barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.image = swap_fb; + barrier.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}; + + vkCmdPipelineBarrier(copy_commands, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0, 0, + nullptr, 0, nullptr, 1, &barrier); } - auto swap_fb = reinterpret_cast(swap_state_.front_buffer_texture); auto& regs = *register_file_; int r = XE_GPU_REG_SHADER_CONSTANT_FETCH_00_0; @@ -490,8 +422,8 @@ void VulkanCommandProcessor::PerformSwap(uint32_t frontbuffer_ptr, texture_cache_->DemandView(texture, 0x688)->view, src_rect, {texture->texture_info.width + 1, texture->texture_info.height + 1}, VK_FORMAT_R8G8B8A8_UNORM, dst_rect, - {frontbuffer_width, frontbuffer_height}, fb_framebuffer_, viewport, - scissor, VK_FILTER_LINEAR, true, true); + {frontbuffer_width, frontbuffer_height}, swap_state->fb_framebuffer_, + viewport, scissor, VK_FILTER_LINEAR, true, true); std::swap(barrier.oldLayout, barrier.newLayout); barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; @@ -500,9 +432,9 @@ void VulkanCommandProcessor::PerformSwap(uint32_t frontbuffer_ptr, copy_commands, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier); - std::lock_guard lock(swap_state_.mutex); - swap_state_.width = frontbuffer_width; - swap_state_.height = frontbuffer_height; + std::lock_guard lock(swap_state->mutex); + swap_state->width = frontbuffer_width; + swap_state->height = frontbuffer_height; } status = vkEndCommandBuffer(copy_commands); diff --git a/src/xenia/gpu/vulkan/vulkan_command_processor.h b/src/xenia/gpu/vulkan/vulkan_command_processor.h index 8cd1710b6..4c8d65cce 100644 --- a/src/xenia/gpu/vulkan/vulkan_command_processor.h +++ b/src/xenia/gpu/vulkan/vulkan_command_processor.h @@ -68,9 +68,6 @@ class VulkanCommandProcessor : public CommandProcessor { void BeginFrame(); void EndFrame(); - void CreateSwapImage(VkCommandBuffer setup_buffer, VkExtent2D extents); - void DestroySwapImage(); - void PerformSwap(uint32_t frontbuffer_ptr, uint32_t frontbuffer_width, uint32_t frontbuffer_height) override; @@ -96,11 +93,6 @@ class VulkanCommandProcessor : public CommandProcessor { xe::ui::vulkan::VulkanDevice* device_ = nullptr; - // front buffer / back buffer memory - VkDeviceMemory fb_memory_ = nullptr; - VkImageView fb_image_view_ = nullptr; - VkFramebuffer fb_framebuffer_ = nullptr; - uint64_t dirty_float_constants_ = 0; // Dirty float constants in blocks of 4 uint8_t dirty_bool_constants_ = 0; uint32_t dirty_loop_constants_ = 0; diff --git a/src/xenia/gpu/vulkan/vulkan_graphics_system.cc b/src/xenia/gpu/vulkan/vulkan_graphics_system.cc index 3637ab3e0..5c90ede3b 100644 --- a/src/xenia/gpu/vulkan/vulkan_graphics_system.cc +++ b/src/xenia/gpu/vulkan/vulkan_graphics_system.cc @@ -56,19 +56,21 @@ X_STATUS VulkanGraphicsSystem::Setup( vkCreateCommandPool(*device_, &create_info, nullptr, &command_pool_); CheckResult(status, "vkCreateCommandPool"); + // TODO(DrChat): Don't hardcode this resolution. + CreateSwapImage({1280, 720}); return X_STATUS_SUCCESS; } void VulkanGraphicsSystem::Shutdown() { GraphicsSystem::Shutdown(); - vkDestroyCommandPool(*device_, command_pool_, nullptr); + DestroySwapImage(); + VK_SAFE_DESTROY(vkDestroyCommandPool, *device_, command_pool_, nullptr); } std::unique_ptr VulkanGraphicsSystem::Capture() { - auto& swap_state = command_processor_->swap_state(); - std::lock_guard lock(swap_state.mutex); - if (!swap_state.front_buffer_texture) { + std::lock_guard lock(swap_state_.mutex); + if (!swap_state_.front_buffer_texture) { return nullptr; } @@ -98,9 +100,9 @@ std::unique_ptr VulkanGraphicsSystem::Capture() { vkBeginCommandBuffer(cmd, &begin_info); auto front_buffer = - reinterpret_cast(swap_state.front_buffer_texture); + reinterpret_cast(swap_state_.front_buffer_texture); - status = CreateCaptureBuffer(cmd, {swap_state.width, swap_state.height}); + status = CreateCaptureBuffer(cmd, {swap_state_.width, swap_state_.height}); if (status != VK_SUCCESS) { vkFreeCommandBuffers(*device_, command_pool_, 1, &cmd); return nullptr; @@ -125,7 +127,7 @@ std::unique_ptr VulkanGraphicsSystem::Capture() { VkBufferImageCopy region = { 0, 0, 0, {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}, - {0, 0, 0}, {swap_state.width, swap_state.height, 1}, + {0, 0, 0}, {swap_state_.width, swap_state_.height, 1}, }; vkCmdCopyImageToBuffer(cmd, front_buffer, VK_IMAGE_LAYOUT_GENERAL, @@ -182,9 +184,9 @@ std::unique_ptr VulkanGraphicsSystem::Capture() { if (status == VK_SUCCESS) { std::unique_ptr raw_image(new RawImage()); - raw_image->width = swap_state.width; - raw_image->height = swap_state.height; - raw_image->stride = swap_state.width * 4; + raw_image->width = swap_state_.width; + raw_image->height = swap_state_.height; + raw_image->stride = swap_state_.width * 4; raw_image->data.resize(raw_image->stride * raw_image->height); std::memcpy(raw_image->data.data(), data, @@ -247,6 +249,75 @@ void VulkanGraphicsSystem::DestroyCaptureBuffer() { capture_buffer_size_ = 0; } +void VulkanGraphicsSystem::CreateSwapImage(VkExtent2D extents) { + VkImageCreateInfo image_info; + std::memset(&image_info, 0, sizeof(VkImageCreateInfo)); + image_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + image_info.imageType = VK_IMAGE_TYPE_2D; + image_info.format = VK_FORMAT_R8G8B8A8_UNORM; + image_info.extent = {extents.width, extents.height, 1}; + image_info.mipLevels = 1; + image_info.arrayLayers = 1; + image_info.samples = VK_SAMPLE_COUNT_1_BIT; + image_info.tiling = VK_IMAGE_TILING_OPTIMAL; + image_info.usage = + VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + image_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + image_info.queueFamilyIndexCount = 0; + image_info.pQueueFamilyIndices = nullptr; + image_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + + VkImage image_fb; + auto status = vkCreateImage(*device_, &image_info, nullptr, &image_fb); + CheckResult(status, "vkCreateImage"); + + // Bind memory to image. + VkMemoryRequirements mem_requirements; + vkGetImageMemoryRequirements(*device_, image_fb, &mem_requirements); + swap_state_.fb_memory_ = device_->AllocateMemory(mem_requirements, 0); + assert_not_null(swap_state_.fb_memory_); + + status = vkBindImageMemory(*device_, image_fb, swap_state_.fb_memory_, 0); + CheckResult(status, "vkBindImageMemory"); + + std::lock_guard lock(swap_state_.mutex); + swap_state_.front_buffer_texture = reinterpret_cast(image_fb); + swap_state_.width = extents.width; + swap_state_.height = extents.height; + + VkImageViewCreateInfo view_create_info = { + VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, + nullptr, + 0, + image_fb, + VK_IMAGE_VIEW_TYPE_2D, + VK_FORMAT_R8G8B8A8_UNORM, + {VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, + VK_COMPONENT_SWIZZLE_A}, + {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}, + }; + status = vkCreateImageView(*device_, &view_create_info, nullptr, + &swap_state_.fb_image_view_); + CheckResult(status, "vkCreateImageView"); +} + +void VulkanGraphicsSystem::DestroySwapImage() { + VK_SAFE_DESTROY(vkDestroyFramebuffer, *device_, swap_state_.fb_framebuffer_, + nullptr); + VK_SAFE_DESTROY(vkDestroyImageView, *device_, swap_state_.fb_image_view_, + nullptr); + + std::lock_guard lock(swap_state_.mutex); + VK_SAFE_DESTROY(vkFreeMemory, *device_, swap_state_.fb_memory_, nullptr); + if (swap_state_.front_buffer_texture) { + vkDestroyImage(*device_, + reinterpret_cast(swap_state_.front_buffer_texture), + nullptr); + } + + swap_state_.front_buffer_texture = 0; +} + std::unique_ptr VulkanGraphicsSystem::CreateCommandProcessor() { return std::make_unique(this, kernel_state_); @@ -258,23 +329,22 @@ void VulkanGraphicsSystem::Swap(xe::ui::UIEvent* e) { } // Check for pending swap. - auto& swap_state = command_processor_->swap_state(); if (display_context_->WasLost()) { // We're crashing. Cheese it. - swap_state.pending = false; + swap_state_.pending = false; return; } { - std::lock_guard lock(swap_state.mutex); - if (!swap_state.pending) { + std::lock_guard lock(swap_state_.mutex); + if (!swap_state_.pending) { // return; } - swap_state.pending = false; + swap_state_.pending = false; } - if (!swap_state.front_buffer_texture) { + if (!swap_state_.front_buffer_texture) { // Not yet ready. return; } @@ -282,7 +352,7 @@ void VulkanGraphicsSystem::Swap(xe::ui::UIEvent* e) { auto swap_chain = display_context_->swap_chain(); auto copy_cmd_buffer = swap_chain->copy_cmd_buffer(); auto front_buffer = - reinterpret_cast(swap_state.front_buffer_texture); + reinterpret_cast(swap_state_.front_buffer_texture); VkImageMemoryBarrier barrier; std::memset(&barrier, 0, sizeof(VkImageMemoryBarrier)); @@ -302,8 +372,8 @@ void VulkanGraphicsSystem::Swap(xe::ui::UIEvent* e) { 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.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}; diff --git a/src/xenia/gpu/vulkan/vulkan_graphics_system.h b/src/xenia/gpu/vulkan/vulkan_graphics_system.h index 33158dda5..f4953260e 100644 --- a/src/xenia/gpu/vulkan/vulkan_graphics_system.h +++ b/src/xenia/gpu/vulkan/vulkan_graphics_system.h @@ -19,12 +19,21 @@ namespace xe { namespace gpu { namespace vulkan { +struct SwapState : public gpu::SwapState { + // front buffer / back buffer memory + VkDeviceMemory fb_memory_ = nullptr; + VkImageView fb_image_view_ = nullptr; + VkImageLayout fb_image_layout_ = VK_IMAGE_LAYOUT_UNDEFINED; + VkFramebuffer fb_framebuffer_ = nullptr; // Used and created by CP. +}; + class VulkanGraphicsSystem : public GraphicsSystem { public: VulkanGraphicsSystem(); ~VulkanGraphicsSystem() override; std::wstring name() const override { return L"Vulkan"; } + SwapState* swap_state() override { return &swap_state_; } X_STATUS Setup(cpu::Processor* processor, kernel::KernelState* kernel_state, std::unique_ptr context) override; @@ -36,11 +45,16 @@ class VulkanGraphicsSystem : public GraphicsSystem { VkResult CreateCaptureBuffer(VkCommandBuffer cmd, VkExtent2D extents); void DestroyCaptureBuffer(); + void CreateSwapImage(VkExtent2D extents); + void DestroySwapImage(); + std::unique_ptr CreateCommandProcessor() override; void Swap(xe::ui::UIEvent* e) override; - xe::ui::vulkan::VulkanDevice* device_ = nullptr; - xe::ui::vulkan::VulkanContext* display_context_ = nullptr; + ui::vulkan::VulkanDevice* device_ = nullptr; + ui::vulkan::VulkanContext* display_context_ = nullptr; + + SwapState swap_state_; VkCommandPool command_pool_ = nullptr;