summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDr. Chat <arkolbed@gmail.com>2018-05-14 19:30:01 -0500
committerDr. Chat <arkolbed@gmail.com>2018-10-12 14:54:03 -0500
commit0f2c8a4e64313ccbaf118512613303000e48de09 (patch)
treededaf85daf4aaa4cf999e742233edca603c26d5a
parent8bcd7637e597299a4ccc5a81c3fd1b6cffa3b858 (diff)
[GPU] GraphicsSystem now owns swap_state
Swapping in Qt windows now
-rw-r--r--src/xenia/app/emulator_window.cc75
-rw-r--r--src/xenia/app/emulator_window.h6
-rw-r--r--src/xenia/gpu/command_processor.cc19
-rw-r--r--src/xenia/gpu/command_processor.h25
-rw-r--r--src/xenia/gpu/graphics_system.h28
-rw-r--r--src/xenia/gpu/null/null_graphics_system.cc5
-rw-r--r--src/xenia/gpu/null/null_graphics_system.h2
-rw-r--r--src/xenia/gpu/trace_player.cc4
-rw-r--r--src/xenia/gpu/vulkan/vulkan_command_processor.cc156
-rw-r--r--src/xenia/gpu/vulkan/vulkan_command_processor.h8
-rw-r--r--src/xenia/gpu/vulkan/vulkan_graphics_system.cc108
-rw-r--r--src/xenia/gpu/vulkan/vulkan_graphics_system.h18
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<VkImage>(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<int32_t>(swap_state->width),
+ static_cast<int32_t>(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<int32_t>(dest_size.width()),
+ static_cast<int32_t>(dest_size.height()), 1};
+ vkCmdBlitImage(cmd, src, VK_IMAGE_LAYOUT_GENERAL, dest,
+ VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region,
+ 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<gpu::vulkan::VulkanGraphicsSystem>(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<QVulkanWindow>();
+ graphics_window_ = std::make_unique<VulkanWindow>(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<std::mutex> lock(swap_state_.mutex);
- if (swap_state_.pending) {
- swap_state_.pending = false;
+ std::lock_guard<std::mutex> 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<std::mutex> lock(swap_state_.mutex);
- if (!swap_state_.pending) {
+ std::lock_guard<std::mutex> 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<std::mutex> lock(swap_state_.mutex);
- swap_state_.pending = true;
+ std::lock_guard<std::mutex> 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<uint32_t>();
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<kernel::XHostThread> worker_thread_;
std::unique_ptr<ui::GraphicsContext> context_;
- SwapMode swap_mode_ = SwapMode::kNormal;
- SwapState swap_state_;
std::function<void()> swap_request_handler_;
std::queue<std::function<void()>> 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<void()> 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<bool> vsync_worker_running_;
kernel::object_ref<kernel::XHostThread> 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<std::mutex> lock(swap_state.mutex);
- swap_state.pending = false;
+ std::lock_guard<std::mutex> 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<ui::GraphicsContext> context) override;
@@ -34,6 +35,7 @@ class NullGraphicsSystem : public GraphicsSystem {
std::unique_ptr<CommandProcessor> 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<std::mutex> lock(swap_state_.mutex);
- swap_state_.front_buffer_texture = reinterpret_cast<uintptr_t>(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<std::mutex> lock(swap_state_.mutex);
- vkDestroyImage(*device_,
- reinterpret_cast<VkImage>(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<VulkanGraphicsSystem*>(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<VkImage>(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<VkImage>(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<std::mutex> lock(swap_state_.mutex);
- swap_state_.width = frontbuffer_width;
- swap_state_.height = frontbuffer_height;
+ std::lock_guard<std::mutex> 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<RawImage> VulkanGraphicsSystem::Capture() {
- auto& swap_state = command_processor_->swap_state();
- std::lock_guard<std::mutex> lock(swap_state.mutex);
- if (!swap_state.front_buffer_texture) {
+ std::lock_guard<std::mutex> lock(swap_state_.mutex);
+ if (!swap_state_.front_buffer_texture) {
return nullptr;
}
@@ -98,9 +100,9 @@ std::unique_ptr<RawImage> VulkanGraphicsSystem::Capture() {
vkBeginCommandBuffer(cmd, &begin_info);
auto front_buffer =
- reinterpret_cast<VkImage>(swap_state.front_buffer_texture);
+ reinterpret_cast<VkImage>(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<RawImage> 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<RawImage> VulkanGraphicsSystem::Capture() {
if (status == VK_SUCCESS) {
std::unique_ptr<RawImage> 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<std::mutex> lock(swap_state_.mutex);
+ swap_state_.front_buffer_texture = reinterpret_cast<uintptr_t>(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<std::mutex> lock(swap_state_.mutex);
+ VK_SAFE_DESTROY(vkFreeMemory, *device_, swap_state_.fb_memory_, nullptr);
+ if (swap_state_.front_buffer_texture) {
+ vkDestroyImage(*device_,
+ reinterpret_cast<VkImage>(swap_state_.front_buffer_texture),
+ nullptr);
+ }
+
+ swap_state_.front_buffer_texture = 0;
+}
+
std::unique_ptr<CommandProcessor>
VulkanGraphicsSystem::CreateCommandProcessor() {
return std::make_unique<VulkanCommandProcessor>(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<std::mutex> lock(swap_state.mutex);
- if (!swap_state.pending) {
+ std::lock_guard<std::mutex> 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<VkImage>(swap_state.front_buffer_texture);
+ reinterpret_cast<VkImage>(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<int32_t>(swap_state.width),
- static_cast<int32_t>(swap_state.height), 1};
+ region.srcOffsets[1] = {static_cast<int32_t>(swap_state_.width),
+ static_cast<int32_t>(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<ui::GraphicsContext> 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<CommandProcessor> 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;