[GPU] GraphicsSystem now owns swap_state

Swapping in Qt windows now
This commit is contained in:
Dr. Chat 2018-05-14 19:30:01 -05:00
parent 8bcd7637e5
commit 0f2c8a4e64
12 changed files with 270 additions and 184 deletions

View File

@ -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;
}

View File

@ -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

View File

@ -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);
}

View File

@ -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_;

View File

@ -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_;

View File

@ -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

View File

@ -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

View File

@ -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();

View File

@ -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);

View File

@ -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;

View File

@ -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};

View File

@ -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;