[Vulkan] Better handling of device lost events (present fatal error dialog)
This commit is contained in:
parent
76b577148d
commit
b5d647d540
|
@ -117,7 +117,7 @@ void CommandProcessor::ClearCaches() {}
|
||||||
void CommandProcessor::WorkerThreadMain() {
|
void CommandProcessor::WorkerThreadMain() {
|
||||||
context_->MakeCurrent();
|
context_->MakeCurrent();
|
||||||
if (!SetupContext()) {
|
if (!SetupContext()) {
|
||||||
xe::FatalError("Unable to setup command processor GL state");
|
xe::FatalError("Unable to setup command processor internal state");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -56,18 +56,17 @@ X_STATUS GraphicsSystem::Setup(cpu::Processor* processor,
|
||||||
assert_null(target_window->context());
|
assert_null(target_window->context());
|
||||||
target_window_->set_context(provider_->CreateContext(target_window_));
|
target_window_->set_context(provider_->CreateContext(target_window_));
|
||||||
|
|
||||||
// Setup the GL context the command processor will do all its drawing in.
|
// Setup the context the command processor will do all its drawing in.
|
||||||
// It's shared with the display context so that we can resolve
|
// It's shared with the display context so that we can resolve
|
||||||
// framebuffers
|
// framebuffers from it.
|
||||||
// from it.
|
|
||||||
processor_context = provider()->CreateOffscreenContext();
|
processor_context = provider()->CreateOffscreenContext();
|
||||||
});
|
});
|
||||||
if (!processor_context) {
|
if (!processor_context) {
|
||||||
xe::FatalError(
|
xe::FatalError(
|
||||||
"Unable to initialize GL context. Xenia requires OpenGL 4.5. Ensure "
|
"Unable to initialize graphics context. Xenia requires OpenGL 4.5 or "
|
||||||
"you have the latest drivers for your GPU and that it supports "
|
"Vulkan support. Ensure you have the latest drivers for your GPU and "
|
||||||
"OpenGL "
|
"that it supports OpenGL or Vulkan. See http://xenia.jp/faq/ for "
|
||||||
"4.5. See http://xenia.jp/faq/ for more information.");
|
"more information.");
|
||||||
return X_STATUS_UNSUCCESSFUL;
|
return X_STATUS_UNSUCCESSFUL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -86,6 +85,10 @@ X_STATUS GraphicsSystem::Setup(cpu::Processor* processor,
|
||||||
target_window->on_painting.AddListener(
|
target_window->on_painting.AddListener(
|
||||||
[this](xe::ui::UIEvent* e) { Swap(e); });
|
[this](xe::ui::UIEvent* e) { Swap(e); });
|
||||||
|
|
||||||
|
// Watch for context lost events.
|
||||||
|
target_window->on_context_lost.AddListener(
|
||||||
|
[this](xe::ui::UIEvent* e) { Reset(); });
|
||||||
|
|
||||||
// Let the processor know we want register access callbacks.
|
// Let the processor know we want register access callbacks.
|
||||||
memory_->AddVirtualMappedRange(
|
memory_->AddVirtualMappedRange(
|
||||||
0x7FC80000, 0xFFFF0000, 0x0000FFFF, this,
|
0x7FC80000, 0xFFFF0000, 0x0000FFFF, this,
|
||||||
|
@ -123,17 +126,29 @@ X_STATUS GraphicsSystem::Setup(cpu::Processor* processor,
|
||||||
}
|
}
|
||||||
|
|
||||||
void GraphicsSystem::Shutdown() {
|
void GraphicsSystem::Shutdown() {
|
||||||
EndTracing();
|
if (command_processor_) {
|
||||||
|
EndTracing();
|
||||||
|
}
|
||||||
|
|
||||||
vsync_worker_running_ = false;
|
if (command_processor_) {
|
||||||
vsync_worker_thread_->Wait(0, 0, 0, nullptr);
|
command_processor_->Shutdown();
|
||||||
vsync_worker_thread_.reset();
|
// TODO(benvanik): remove mapped range.
|
||||||
|
command_processor_.reset();
|
||||||
|
}
|
||||||
|
|
||||||
command_processor_->Shutdown();
|
if (vsync_worker_thread_) {
|
||||||
|
vsync_worker_running_ = false;
|
||||||
|
vsync_worker_thread_->Wait(0, 0, 0, nullptr);
|
||||||
|
vsync_worker_thread_.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO(benvanik): remove mapped range.
|
void GraphicsSystem::Reset() {
|
||||||
|
// TODO(DrChat): Reset the system.
|
||||||
|
XELOGI("Context lost; Reset invoked");
|
||||||
|
Shutdown();
|
||||||
|
|
||||||
command_processor_.reset();
|
xe::FatalError("Graphics device lost (probably due to an internal error)");
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t GraphicsSystem::ReadRegisterThunk(void* ppc_context,
|
uint32_t GraphicsSystem::ReadRegisterThunk(void* ppc_context,
|
||||||
|
|
|
@ -45,6 +45,7 @@ class GraphicsSystem {
|
||||||
kernel::KernelState* kernel_state,
|
kernel::KernelState* kernel_state,
|
||||||
ui::Window* target_window);
|
ui::Window* target_window);
|
||||||
virtual void Shutdown();
|
virtual void Shutdown();
|
||||||
|
virtual void Reset();
|
||||||
|
|
||||||
virtual std::unique_ptr<xe::ui::RawImage> Capture() { return nullptr; }
|
virtual std::unique_ptr<xe::ui::RawImage> Capture() { return nullptr; }
|
||||||
|
|
||||||
|
|
|
@ -314,8 +314,7 @@ TextureCache::Texture* TextureCache::AllocateTexture(
|
||||||
VkResult status = vmaCreateImage(mem_allocator_, &image_info, &vma_reqs,
|
VkResult status = vmaCreateImage(mem_allocator_, &image_info, &vma_reqs,
|
||||||
&image, &alloc, &vma_info);
|
&image, &alloc, &vma_info);
|
||||||
if (status != VK_SUCCESS) {
|
if (status != VK_SUCCESS) {
|
||||||
// Crap.
|
// Allocation failed.
|
||||||
assert_always();
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -332,10 +331,12 @@ TextureCache::Texture* TextureCache::AllocateTexture(
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TextureCache::FreeTexture(Texture* texture) {
|
bool TextureCache::FreeTexture(Texture* texture) {
|
||||||
if (texture->in_flight_fence &&
|
if (texture->in_flight_fence) {
|
||||||
vkGetFenceStatus(*device_, texture->in_flight_fence) != VK_SUCCESS) {
|
VkResult status = vkGetFenceStatus(*device_, texture->in_flight_fence);
|
||||||
// Texture still in flight.
|
if (status != VK_SUCCESS && status != VK_ERROR_DEVICE_LOST) {
|
||||||
return false;
|
// Texture still in flight.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (texture->framebuffer) {
|
if (texture->framebuffer) {
|
||||||
|
@ -1421,7 +1422,6 @@ bool TextureCache::SetupTextureBinding(VkCommandBuffer command_buffer,
|
||||||
|
|
||||||
auto texture = Demand(texture_info, command_buffer, completion_fence);
|
auto texture = Demand(texture_info, command_buffer, completion_fence);
|
||||||
auto sampler = Demand(sampler_info);
|
auto sampler = Demand(sampler_info);
|
||||||
// assert_true(texture != nullptr && sampler != nullptr);
|
|
||||||
if (texture == nullptr || sampler == nullptr) {
|
if (texture == nullptr || sampler == nullptr) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -537,8 +537,6 @@ void VulkanCommandProcessor::PerformSwap(uint32_t frontbuffer_ptr,
|
||||||
submit_info.pSignalSemaphores = nullptr;
|
submit_info.pSignalSemaphores = nullptr;
|
||||||
|
|
||||||
status = vkQueueSubmit(queue_, 1, &submit_info, current_batch_fence_);
|
status = vkQueueSubmit(queue_, 1, &submit_info, current_batch_fence_);
|
||||||
CheckResult(status, "vkQueueSubmit");
|
|
||||||
|
|
||||||
if (device_->is_renderdoc_attached() && capturing_) {
|
if (device_->is_renderdoc_attached() && capturing_) {
|
||||||
device_->EndRenderDocFrameCapture();
|
device_->EndRenderDocFrameCapture();
|
||||||
capturing_ = false;
|
capturing_ = false;
|
||||||
|
|
|
@ -260,8 +260,15 @@ void VulkanGraphicsSystem::Swap(xe::ui::UIEvent* e) {
|
||||||
if (!command_processor_) {
|
if (!command_processor_) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for pending swap.
|
// Check for pending swap.
|
||||||
auto& swap_state = command_processor_->swap_state();
|
auto& swap_state = command_processor_->swap_state();
|
||||||
|
if (display_context_->WasLost()) {
|
||||||
|
// We're crashing. Cheese it.
|
||||||
|
swap_state.pending = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(swap_state.mutex);
|
std::lock_guard<std::mutex> lock(swap_state.mutex);
|
||||||
if (!swap_state.pending) {
|
if (!swap_state.pending) {
|
||||||
|
|
|
@ -271,7 +271,11 @@ void Blitter::BlitTexture2D(VkCommandBuffer command_buffer, VkFence fence,
|
||||||
|
|
||||||
// Acquire and update a descriptor set for this image.
|
// Acquire and update a descriptor set for this image.
|
||||||
auto set = descriptor_pool_->AcquireEntry(descriptor_set_layout_);
|
auto set = descriptor_pool_->AcquireEntry(descriptor_set_layout_);
|
||||||
assert_not_null(set);
|
if (!set) {
|
||||||
|
assert_always();
|
||||||
|
descriptor_pool_->CancelBatch();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
VkWriteDescriptorSet write;
|
VkWriteDescriptorSet write;
|
||||||
write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||||
|
|
|
@ -50,7 +50,9 @@ class BaseFencedPool {
|
||||||
while (pending_batch_list_head_) {
|
while (pending_batch_list_head_) {
|
||||||
auto batch = pending_batch_list_head_;
|
auto batch = pending_batch_list_head_;
|
||||||
assert_not_null(batch->fence);
|
assert_not_null(batch->fence);
|
||||||
if (vkGetFenceStatus(device_, batch->fence) == VK_SUCCESS) {
|
|
||||||
|
VkResult status = vkGetFenceStatus(device_, batch->fence);
|
||||||
|
if (status == VK_SUCCESS || status == VK_ERROR_DEVICE_LOST) {
|
||||||
// Batch has completed. Reclaim.
|
// Batch has completed. Reclaim.
|
||||||
pending_batch_list_head_ = batch->next;
|
pending_batch_list_head_ = batch->next;
|
||||||
if (batch == pending_batch_list_tail_) {
|
if (batch == pending_batch_list_tail_) {
|
||||||
|
|
|
@ -37,11 +37,12 @@ VulkanContext::VulkanContext(VulkanProvider* provider, Window* target_window)
|
||||||
: GraphicsContext(provider, target_window) {}
|
: GraphicsContext(provider, target_window) {}
|
||||||
|
|
||||||
VulkanContext::~VulkanContext() {
|
VulkanContext::~VulkanContext() {
|
||||||
|
VkResult status;
|
||||||
auto provider = static_cast<VulkanProvider*>(provider_);
|
auto provider = static_cast<VulkanProvider*>(provider_);
|
||||||
auto device = provider->device();
|
auto device = provider->device();
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> queue_lock(device->primary_queue_mutex());
|
std::lock_guard<std::mutex> queue_lock(device->primary_queue_mutex());
|
||||||
vkQueueWaitIdle(device->primary_queue());
|
status = vkQueueWaitIdle(device->primary_queue());
|
||||||
}
|
}
|
||||||
immediate_drawer_.reset();
|
immediate_drawer_.reset();
|
||||||
swap_chain_.reset();
|
swap_chain_.reset();
|
||||||
|
@ -132,6 +133,8 @@ void VulkanContext::BeginSwap() {
|
||||||
auto provider = static_cast<VulkanProvider*>(provider_);
|
auto provider = static_cast<VulkanProvider*>(provider_);
|
||||||
auto device = provider->device();
|
auto device = provider->device();
|
||||||
|
|
||||||
|
VkResult status;
|
||||||
|
|
||||||
// If we have a window see if it's been resized since we last swapped.
|
// If we have a window see if it's been resized since we last swapped.
|
||||||
// If it has been, we'll need to reinitialize the swap chain before we
|
// If it has been, we'll need to reinitialize the swap chain before we
|
||||||
// start touching it.
|
// start touching it.
|
||||||
|
@ -143,13 +146,17 @@ void VulkanContext::BeginSwap() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Acquire the next image and set it up for use.
|
if (!context_lost_) {
|
||||||
swap_chain_->Begin();
|
// Acquire the next image and set it up for use.
|
||||||
|
status = swap_chain_->Begin();
|
||||||
|
if (status == VK_ERROR_DEVICE_LOST) {
|
||||||
|
context_lost_ = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO(benvanik): use a fence instead? May not be possible with target image.
|
// TODO(benvanik): use a fence instead? May not be possible with target image.
|
||||||
std::lock_guard<std::mutex> queue_lock(device->primary_queue_mutex());
|
std::lock_guard<std::mutex> queue_lock(device->primary_queue_mutex());
|
||||||
auto err = vkQueueWaitIdle(device->primary_queue());
|
status = vkQueueWaitIdle(device->primary_queue());
|
||||||
CheckResult(err, "vkQueueWaitIdle");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanContext::EndSwap() {
|
void VulkanContext::EndSwap() {
|
||||||
|
@ -157,15 +164,21 @@ void VulkanContext::EndSwap() {
|
||||||
auto provider = static_cast<VulkanProvider*>(provider_);
|
auto provider = static_cast<VulkanProvider*>(provider_);
|
||||||
auto device = provider->device();
|
auto device = provider->device();
|
||||||
|
|
||||||
// Notify the presentation engine the image is ready.
|
VkResult status;
|
||||||
// The contents must be in a coherent state.
|
|
||||||
swap_chain_->End();
|
if (!context_lost_) {
|
||||||
|
// Notify the presentation engine the image is ready.
|
||||||
|
// The contents must be in a coherent state.
|
||||||
|
status = swap_chain_->End();
|
||||||
|
if (status == VK_ERROR_DEVICE_LOST) {
|
||||||
|
context_lost_ = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Wait until the queue is idle.
|
// Wait until the queue is idle.
|
||||||
// TODO(benvanik): is this required?
|
// TODO(benvanik): is this required?
|
||||||
std::lock_guard<std::mutex> queue_lock(device->primary_queue_mutex());
|
std::lock_guard<std::mutex> queue_lock(device->primary_queue_mutex());
|
||||||
auto err = vkQueueWaitIdle(device->primary_queue());
|
status = vkQueueWaitIdle(device->primary_queue());
|
||||||
CheckResult(err, "vkQueueWaitIdle");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<RawImage> VulkanContext::Capture() {
|
std::unique_ptr<RawImage> VulkanContext::Capture() {
|
||||||
|
|
|
@ -38,11 +38,16 @@ class VulkanContext : public GraphicsContext {
|
||||||
bool MakeCurrent() override;
|
bool MakeCurrent() override;
|
||||||
void ClearCurrent() override;
|
void ClearCurrent() override;
|
||||||
|
|
||||||
|
bool WasLost() override { return context_lost_; }
|
||||||
|
|
||||||
void BeginSwap() override;
|
void BeginSwap() override;
|
||||||
void EndSwap() override;
|
void EndSwap() override;
|
||||||
|
|
||||||
std::unique_ptr<RawImage> Capture() override;
|
std::unique_ptr<RawImage> Capture() override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool context_lost_ = false;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class VulkanProvider;
|
friend class VulkanProvider;
|
||||||
|
|
||||||
|
|
|
@ -388,14 +388,18 @@ void VulkanSwapChain::Shutdown() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VulkanSwapChain::Begin() {
|
VkResult VulkanSwapChain::Begin() {
|
||||||
wait_and_signal_semaphores_.clear();
|
wait_and_signal_semaphores_.clear();
|
||||||
|
|
||||||
|
VkResult status;
|
||||||
|
|
||||||
// Get the index of the next available swapchain image.
|
// Get the index of the next available swapchain image.
|
||||||
auto err =
|
status =
|
||||||
vkAcquireNextImageKHR(*device_, handle, 0, image_available_semaphore_,
|
vkAcquireNextImageKHR(*device_, handle, 0, image_available_semaphore_,
|
||||||
nullptr, ¤t_buffer_index_);
|
nullptr, ¤t_buffer_index_);
|
||||||
CheckResult(err, "vkAcquireNextImageKHR");
|
if (status != VK_SUCCESS) {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
// Wait for the acquire semaphore to be signaled so that the following
|
// Wait for the acquire semaphore to be signaled so that the following
|
||||||
// operations know they can start modifying the image.
|
// operations know they can start modifying the image.
|
||||||
|
@ -414,10 +418,12 @@ bool VulkanSwapChain::Begin() {
|
||||||
wait_submit_info.pSignalSemaphores = &image_usage_semaphore_;
|
wait_submit_info.pSignalSemaphores = &image_usage_semaphore_;
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> queue_lock(device_->primary_queue_mutex());
|
std::lock_guard<std::mutex> queue_lock(device_->primary_queue_mutex());
|
||||||
err =
|
status =
|
||||||
vkQueueSubmit(device_->primary_queue(), 1, &wait_submit_info, nullptr);
|
vkQueueSubmit(device_->primary_queue(), 1, &wait_submit_info, nullptr);
|
||||||
}
|
}
|
||||||
CheckResult(err, "vkQueueSubmit");
|
if (status != VK_SUCCESS) {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
// Reset all command buffers.
|
// Reset all command buffers.
|
||||||
vkResetCommandBuffer(render_cmd_buffer_, 0);
|
vkResetCommandBuffer(render_cmd_buffer_, 0);
|
||||||
|
@ -441,13 +447,19 @@ bool VulkanSwapChain::Begin() {
|
||||||
begin_info.flags = VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT |
|
begin_info.flags = VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT |
|
||||||
VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
|
VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
|
||||||
begin_info.pInheritanceInfo = &inherit_info;
|
begin_info.pInheritanceInfo = &inherit_info;
|
||||||
err = vkBeginCommandBuffer(render_cmd_buffer_, &begin_info);
|
status = vkBeginCommandBuffer(render_cmd_buffer_, &begin_info);
|
||||||
CheckResult(err, "vkBeginCommandBuffer");
|
CheckResult(status, "vkBeginCommandBuffer");
|
||||||
|
if (status != VK_SUCCESS) {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
// Start recording the copy command buffer as well.
|
// Start recording the copy command buffer as well.
|
||||||
begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
|
begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
|
||||||
err = vkBeginCommandBuffer(copy_cmd_buffer_, &begin_info);
|
status = vkBeginCommandBuffer(copy_cmd_buffer_, &begin_info);
|
||||||
CheckResult(err, "vkBeginCommandBuffer");
|
CheckResult(status, "vkBeginCommandBuffer");
|
||||||
|
if (status != VK_SUCCESS) {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
// First: Issue a command to clear the render target.
|
// First: Issue a command to clear the render target.
|
||||||
VkImageSubresourceRange clear_range = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1};
|
VkImageSubresourceRange clear_range = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1};
|
||||||
|
@ -466,27 +478,42 @@ bool VulkanSwapChain::Begin() {
|
||||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &clear_color, 1,
|
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &clear_color, 1,
|
||||||
&clear_range);
|
&clear_range);
|
||||||
|
|
||||||
return true;
|
return VK_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VulkanSwapChain::End() {
|
VkResult VulkanSwapChain::End() {
|
||||||
auto& current_buffer = buffers_[current_buffer_index_];
|
auto& current_buffer = buffers_[current_buffer_index_];
|
||||||
|
VkResult status;
|
||||||
|
|
||||||
auto err = vkEndCommandBuffer(render_cmd_buffer_);
|
status = vkEndCommandBuffer(render_cmd_buffer_);
|
||||||
CheckResult(err, "vkEndCommandBuffer");
|
CheckResult(status, "vkEndCommandBuffer");
|
||||||
|
if (status != VK_SUCCESS) {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
err = vkEndCommandBuffer(copy_cmd_buffer_);
|
status = vkEndCommandBuffer(copy_cmd_buffer_);
|
||||||
CheckResult(err, "vkEndCommandBuffer");
|
CheckResult(status, "vkEndCommandBuffer");
|
||||||
|
if (status != VK_SUCCESS) {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
// Build primary command buffer.
|
// Build primary command buffer.
|
||||||
vkResetCommandBuffer(cmd_buffer_, 0);
|
status = vkResetCommandBuffer(cmd_buffer_, 0);
|
||||||
|
CheckResult(status, "vkResetCommandBuffer");
|
||||||
|
if (status != VK_SUCCESS) {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
VkCommandBufferBeginInfo begin_info;
|
VkCommandBufferBeginInfo begin_info;
|
||||||
begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
||||||
begin_info.pNext = nullptr;
|
begin_info.pNext = nullptr;
|
||||||
begin_info.flags = 0;
|
begin_info.flags = 0;
|
||||||
begin_info.pInheritanceInfo = nullptr;
|
begin_info.pInheritanceInfo = nullptr;
|
||||||
vkBeginCommandBuffer(cmd_buffer_, &begin_info);
|
status = vkBeginCommandBuffer(cmd_buffer_, &begin_info);
|
||||||
|
CheckResult(status, "vkBeginCommandBuffer");
|
||||||
|
if (status != VK_SUCCESS) {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
// Transition the image to a format we can copy to.
|
// Transition the image to a format we can copy to.
|
||||||
VkImageMemoryBarrier pre_image_copy_barrier;
|
VkImageMemoryBarrier pre_image_copy_barrier;
|
||||||
|
@ -580,9 +607,13 @@ bool VulkanSwapChain::End() {
|
||||||
|
|
||||||
current_buffer.image_layout = post_image_memory_barrier.newLayout;
|
current_buffer.image_layout = post_image_memory_barrier.newLayout;
|
||||||
|
|
||||||
vkEndCommandBuffer(cmd_buffer_);
|
status = vkEndCommandBuffer(cmd_buffer_);
|
||||||
VkPipelineStageFlags wait_dst_stage = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
|
CheckResult(status, "vkEndCommandBuffer");
|
||||||
|
if (status != VK_SUCCESS) {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
VkPipelineStageFlags wait_dst_stage = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
|
||||||
std::vector<VkSemaphore> semaphores;
|
std::vector<VkSemaphore> semaphores;
|
||||||
for (size_t i = 0; i < wait_and_signal_semaphores_.size(); i++) {
|
for (size_t i = 0; i < wait_and_signal_semaphores_.size(); i++) {
|
||||||
semaphores.push_back(wait_and_signal_semaphores_[i]);
|
semaphores.push_back(wait_and_signal_semaphores_[i]);
|
||||||
|
@ -603,10 +634,13 @@ bool VulkanSwapChain::End() {
|
||||||
render_submit_info.pSignalSemaphores = semaphores.data();
|
render_submit_info.pSignalSemaphores = semaphores.data();
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> queue_lock(device_->primary_queue_mutex());
|
std::lock_guard<std::mutex> queue_lock(device_->primary_queue_mutex());
|
||||||
err = vkQueueSubmit(device_->primary_queue(), 1, &render_submit_info,
|
status = vkQueueSubmit(device_->primary_queue(), 1, &render_submit_info,
|
||||||
nullptr);
|
nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status != VK_SUCCESS) {
|
||||||
|
return status;
|
||||||
}
|
}
|
||||||
CheckResult(err, "vkQueueSubmit");
|
|
||||||
|
|
||||||
// Queue the present of our current image.
|
// Queue the present of our current image.
|
||||||
const VkSwapchainKHR swap_chains[] = {handle};
|
const VkSwapchainKHR swap_chains[] = {handle};
|
||||||
|
@ -622,27 +656,30 @@ bool VulkanSwapChain::End() {
|
||||||
present_info.pResults = nullptr;
|
present_info.pResults = nullptr;
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> queue_lock(device_->primary_queue_mutex());
|
std::lock_guard<std::mutex> queue_lock(device_->primary_queue_mutex());
|
||||||
err = vkQueuePresentKHR(device_->primary_queue(), &present_info);
|
status = vkQueuePresentKHR(device_->primary_queue(), &present_info);
|
||||||
}
|
}
|
||||||
switch (err) {
|
switch (status) {
|
||||||
case VK_SUCCESS:
|
case VK_SUCCESS:
|
||||||
break;
|
break;
|
||||||
case VK_SUBOPTIMAL_KHR:
|
case VK_SUBOPTIMAL_KHR:
|
||||||
// We are not rendering at the right size - but the presentation engine
|
// We are not rendering at the right size - but the presentation engine
|
||||||
// will scale the output for us.
|
// will scale the output for us.
|
||||||
|
status = VK_SUCCESS;
|
||||||
break;
|
break;
|
||||||
case VK_ERROR_OUT_OF_DATE_KHR:
|
case VK_ERROR_OUT_OF_DATE_KHR:
|
||||||
// Lost presentation ability; need to recreate the swapchain.
|
// Lost presentation ability; need to recreate the swapchain.
|
||||||
// TODO(benvanik): recreate swapchain.
|
// TODO(benvanik): recreate swapchain.
|
||||||
assert_always("Swapchain recreation not implemented");
|
assert_always("Swapchain recreation not implemented");
|
||||||
break;
|
break;
|
||||||
|
case VK_ERROR_DEVICE_LOST:
|
||||||
|
// Fatal. Device lost.
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
XELOGE("Failed to queue present: %s", to_string(err));
|
XELOGE("Failed to queue present: %s", to_string(status));
|
||||||
assert_always("Unexpected queue present failure");
|
assert_always("Unexpected queue present failure");
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace vulkan
|
} // namespace vulkan
|
||||||
|
|
|
@ -57,9 +57,9 @@ class VulkanSwapChain {
|
||||||
void WaitAndSignalSemaphore(VkSemaphore sem);
|
void WaitAndSignalSemaphore(VkSemaphore sem);
|
||||||
|
|
||||||
// Begins the swap operation, preparing state for rendering.
|
// Begins the swap operation, preparing state for rendering.
|
||||||
bool Begin();
|
VkResult Begin();
|
||||||
// Ends the swap operation, finalizing rendering and presenting the results.
|
// Ends the swap operation, finalizing rendering and presenting the results.
|
||||||
bool End();
|
VkResult End();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct Buffer {
|
struct Buffer {
|
||||||
|
|
|
@ -198,6 +198,11 @@ void Window::OnPaint(UIEvent* e) {
|
||||||
ImGui::NewFrame();
|
ImGui::NewFrame();
|
||||||
|
|
||||||
context_->BeginSwap();
|
context_->BeginSwap();
|
||||||
|
if (context_->WasLost()) {
|
||||||
|
on_context_lost(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
ForEachListener([e](auto listener) { listener->OnPainting(e); });
|
ForEachListener([e](auto listener) { listener->OnPainting(e); });
|
||||||
on_painting(e);
|
on_painting(e);
|
||||||
ForEachListener([e](auto listener) { listener->OnPaint(e); });
|
ForEachListener([e](auto listener) { listener->OnPaint(e); });
|
||||||
|
|
|
@ -117,6 +117,8 @@ class Window {
|
||||||
Delegate<UIEvent*> on_painting;
|
Delegate<UIEvent*> on_painting;
|
||||||
Delegate<UIEvent*> on_paint;
|
Delegate<UIEvent*> on_paint;
|
||||||
Delegate<UIEvent*> on_painted;
|
Delegate<UIEvent*> on_painted;
|
||||||
|
Delegate<UIEvent*> on_context_lost;
|
||||||
|
|
||||||
Delegate<FileDropEvent*> on_file_drop;
|
Delegate<FileDropEvent*> on_file_drop;
|
||||||
|
|
||||||
Delegate<KeyEvent*> on_key_down;
|
Delegate<KeyEvent*> on_key_down;
|
||||||
|
|
Loading…
Reference in New Issue