From 7c5042add71ae6cc672ce6b5557ee3ef8636730c Mon Sep 17 00:00:00 2001 From: "Dr. Chat" Date: Tue, 3 May 2016 14:12:05 -0500 Subject: [PATCH] Vulkan CP: Add in separate swap-chain images Some other changes I can't remember --- .../gpu/vulkan/vulkan_command_processor.cc | 330 +++++++++++++----- .../gpu/vulkan/vulkan_command_processor.h | 14 +- .../gpu/vulkan/vulkan_graphics_system.cc | 22 +- 3 files changed, 278 insertions(+), 88 deletions(-) diff --git a/src/xenia/gpu/vulkan/vulkan_command_processor.cc b/src/xenia/gpu/vulkan/vulkan_command_processor.cc index fd604733b..011c5b878 100644 --- a/src/xenia/gpu/vulkan/vulkan_command_processor.cc +++ b/src/xenia/gpu/vulkan/vulkan_command_processor.cc @@ -29,7 +29,7 @@ namespace vulkan { using namespace xe::gpu::xenos; using xe::ui::vulkan::CheckResult; -constexpr size_t kDefaultBufferCacheCapacity = 256 * 1024 * 1024; +constexpr size_t kDefaultBufferCacheCapacity = 128 * 1024 * 1024; VulkanCommandProcessor::VulkanCommandProcessor( VulkanGraphicsSystem* graphics_system, kernel::KernelState* kernel_state) @@ -82,6 +82,11 @@ bool VulkanCommandProcessor::SetupContext() { void VulkanCommandProcessor::ShutdownContext() { // TODO(benvanik): wait until idle. + if (swap_state_.front_buffer_texture) { + // Free swap chain images. + DestroySwapImages(); + } + buffer_cache_.reset(); pipeline_cache_.reset(); render_cache_.reset(); @@ -131,59 +136,214 @@ void VulkanCommandProcessor::ReturnFromWait() { CommandProcessor::ReturnFromWait(); } +void VulkanCommandProcessor::CreateSwapImages(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_TRANSFER_DST_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, image_bb; + auto status = vkCreateImage(*device_, &image_info, nullptr, &image_fb); + CheckResult(status, "vkCreateImage"); + + status = vkCreateImage(*device_, &image_info, nullptr, &image_bb); + CheckResult(status, "vkCreateImage"); + + // Bind memory to images. + 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"); + + vkGetImageMemoryRequirements(*device_, image_fb, &mem_requirements); + bb_memory = device_->AllocateMemory(mem_requirements, 0); + assert_not_null(bb_memory); + + status = vkBindImageMemory(*device_, image_bb, bb_memory, 0); + CheckResult(status, "vkBindImageMemory"); + + std::lock_guard lock(swap_state_.mutex); + swap_state_.front_buffer_texture = reinterpret_cast(image_fb); + swap_state_.back_buffer_texture = reinterpret_cast(image_bb); + + // Transition both images to general layout. + VkImageMemoryBarrier barrier; + std::memset(&barrier, 0, sizeof(VkImageMemoryBarrier)); + barrier.srcAccessMask = 0; + barrier.dstAccessMask = 0; + 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_TOP_OF_PIPE_BIT, 0, 0, nullptr, 0, + nullptr, 1, &barrier); + + barrier.image = image_bb; + + vkCmdPipelineBarrier(setup_buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, + VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0, 0, nullptr, 0, + nullptr, 1, &barrier); +} + +void VulkanCommandProcessor::DestroySwapImages() { + std::lock_guard lock(swap_state_.mutex); + vkDestroyImage(*device_, + reinterpret_cast(swap_state_.front_buffer_texture), + nullptr); + vkDestroyImage(*device_, + reinterpret_cast(swap_state_.back_buffer_texture), + nullptr); + vkFreeMemory(*device_, fb_memory, nullptr); + vkFreeMemory(*device_, bb_memory, nullptr); + + swap_state_.front_buffer_texture = 0; + swap_state_.back_buffer_texture = 0; + fb_memory = nullptr; + bb_memory = nullptr; +} + void VulkanCommandProcessor::PerformSwap(uint32_t frontbuffer_ptr, uint32_t frontbuffer_width, uint32_t frontbuffer_height) { SCOPE_profile_cpu_f("gpu"); + // Build a final command buffer that copies the game's frontbuffer texture + // into our backbuffer texture. + VkCommandBuffer copy_commands = nullptr; + bool opened_batch; + if (command_buffer_pool_->has_open_batch()) { + copy_commands = command_buffer_pool_->AcquireEntry(); + opened_batch = false; + } else { + command_buffer_pool_->BeginBatch(); + copy_commands = command_buffer_pool_->AcquireEntry(); + current_batch_fence_.reset(new ui::vulkan::Fence(*device_)); + opened_batch = true; + } + + VkCommandBufferBeginInfo begin_info; + std::memset(&begin_info, 0, sizeof(begin_info)); + begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + auto status = vkBeginCommandBuffer(copy_commands, &begin_info); + CheckResult(status, "vkBeginCommandBuffer"); + + if (!frontbuffer_ptr) { + // Trace viewer does this. + frontbuffer_ptr = last_copy_base_; + } + + if (!swap_state_.back_buffer_texture) { + CreateSwapImages(copy_commands, {frontbuffer_width, frontbuffer_height}); + } + auto swap_bb = reinterpret_cast(swap_state_.back_buffer_texture); + + // Issue the commands to copy the game's frontbuffer to our backbuffer. + auto texture = texture_cache_->LookupAddress( + frontbuffer_ptr, xe::round_up(frontbuffer_width, 32), + xe::round_up(frontbuffer_height, 32), TextureFormat::k_8_8_8_8); + if (texture) { + texture->in_flight_fence = current_batch_fence_; + + // Insert a barrier so the GPU finishes writing to the image. + VkImageMemoryBarrier barrier; + std::memset(&barrier, 0, sizeof(VkImageMemoryBarrier)); + barrier.srcAccessMask = + VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT; + barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + barrier.oldLayout = texture->image_layout; + barrier.newLayout = texture->image_layout; + barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.image = texture->image; + barrier.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}; + + vkCmdPipelineBarrier(copy_commands, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, + VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0, 0, nullptr, 0, + nullptr, 1, &barrier); + + // Now issue a blit command. + VkImageBlit blit; + std::memset(&blit, 0, sizeof(VkImageBlit)); + blit.srcSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}; + blit.srcOffsets[0] = {0, 0, 0}; + blit.srcOffsets[1] = {int32_t(frontbuffer_width), + int32_t(frontbuffer_height), 1}; + blit.dstSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}; + blit.dstOffsets[0] = {0, 0, 0}; + blit.dstOffsets[1] = {int32_t(frontbuffer_width), + int32_t(frontbuffer_height), 1}; + + vkCmdBlitImage(copy_commands, texture->image, texture->image_layout, + swap_bb, VK_IMAGE_LAYOUT_GENERAL, 1, &blit, + VK_FILTER_LINEAR); + + std::lock_guard lock(swap_state_.mutex); + swap_state_.width = frontbuffer_width; + swap_state_.height = frontbuffer_height; + } + + status = vkEndCommandBuffer(copy_commands); + CheckResult(status, "vkEndCommandBuffer"); + // Queue up current command buffers. // TODO(benvanik): bigger batches. + std::vector submit_buffers; if (current_command_buffer_) { if (current_render_state_) { render_cache_->EndRenderPass(); current_render_state_ = nullptr; } - auto status = vkEndCommandBuffer(current_command_buffer_); + status = vkEndCommandBuffer(current_command_buffer_); CheckResult(status, "vkEndCommandBuffer"); status = vkEndCommandBuffer(current_setup_buffer_); CheckResult(status, "vkEndCommandBuffer"); - command_buffer_pool_->EndBatch(*current_batch_fence_); + // TODO(DrChat): If the setup buffer is empty, don't bother queueing it up. + submit_buffers.push_back(current_setup_buffer_); + submit_buffers.push_back(current_command_buffer_); + + current_command_buffer_ = nullptr; + current_setup_buffer_ = nullptr; + } + + submit_buffers.push_back(copy_commands); + if (!submit_buffers.empty()) { // TODO(benvanik): move to CP or to host (trace dump, etc). // This only needs to surround a vkQueueSubmit. if (queue_mutex_) { queue_mutex_->lock(); } - // TODO(DrChat): If setup buffer is empty, don't bother queueing it up. - VkCommandBuffer command_buffers[] = { - current_setup_buffer_, current_command_buffer_, - }; - VkSubmitInfo submit_info; + std::memset(&submit_info, 0, sizeof(VkSubmitInfo)); submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; - submit_info.pNext = nullptr; - submit_info.waitSemaphoreCount = 0; - submit_info.pWaitSemaphores = nullptr; - submit_info.commandBufferCount = 2; - submit_info.pCommandBuffers = command_buffers; - submit_info.signalSemaphoreCount = 0; - submit_info.pSignalSemaphores = nullptr; - if (queue_mutex_) { - // queue_mutex_->lock(); - } + submit_info.commandBufferCount = uint32_t(submit_buffers.size()); + submit_info.pCommandBuffers = submit_buffers.data(); status = vkQueueSubmit(queue_, 1, &submit_info, *current_batch_fence_); - if (queue_mutex_) { - // queue_mutex_->unlock(); - } CheckResult(status, "vkQueueSubmit"); - // TODO(DrChat): Disable this completely. - VkFence fences[] = {*current_batch_fence_}; - status = vkWaitForFences(*device_, 1, fences, true, -1); - CheckResult(status, "vkWaitForFences"); - if (device_->is_renderdoc_attached() && capturing_) { device_->EndRenderDocFrameCapture(); capturing_ = false; @@ -197,45 +357,28 @@ void VulkanCommandProcessor::PerformSwap(uint32_t frontbuffer_ptr, if (queue_mutex_) { queue_mutex_->unlock(); } + } - // Scavenging. - current_command_buffer_ = nullptr; - current_setup_buffer_ = nullptr; + command_buffer_pool_->EndBatch(current_batch_fence_); + + // TODO(DrChat): Remove this. + VkFence fences[] = { *current_batch_fence_ }; + vkWaitForFences(*device_, 1, fences, true, -1); + + // Scavenging. + { +#if FINE_GRAINED_DRAW_SCOPES + SCOPE_profile_cpu_i( + "gpu", + "xe::gpu::vulkan::VulkanCommandProcessor::PerformSwap Scavenging"); +#endif // FINE_GRAINED_DRAW_SCOPES command_buffer_pool_->Scavenge(); texture_cache_->Scavenge(); - current_batch_fence_ = nullptr; - - // TODO: Remove this when we stop waiting on the queue. - buffer_cache_->ClearCache(); + buffer_cache_->Scavenge(); } - if (!frontbuffer_ptr) { - if (!last_copy_base_) { - // Nothing to draw. - return; - } - - // Trace viewer does this. - frontbuffer_ptr = last_copy_base_; - } - - auto texture = texture_cache_->LookupAddress( - frontbuffer_ptr, xe::round_up(frontbuffer_width, 32), - xe::round_up(frontbuffer_height, 32), TextureFormat::k_8_8_8_8); - // There shouldn't be a case where the texture is null. - assert_not_null(texture); - - if (texture) { - std::lock_guard lock(swap_state_.mutex); - swap_state_.width = frontbuffer_width; - swap_state_.height = frontbuffer_height; - swap_state_.back_buffer_texture = - reinterpret_cast(texture->image); - } - - // Remove any dead textures, etc. - texture_cache_->Scavenge(); + current_batch_fence_ = nullptr; } Shader* VulkanCommandProcessor::LoadShader(ShaderType shader_type, @@ -331,16 +474,7 @@ bool VulkanCommandProcessor::IssueDraw(PrimitiveType primitive_type, started_command_buffer = true; } auto command_buffer = current_command_buffer_; - - // Upload and set descriptors for all textures. - // We do this outside of the render pass so the texture cache can upload and - // convert textures. - // Setup buffer may be flushed to GPU if the texture cache needs it. - auto samplers = - PopulateSamplers(current_setup_buffer_, vertex_shader, pixel_shader); - if (!samplers) { - return false; - } + auto setup_buffer = current_setup_buffer_; // Begin the render pass. // This will setup our framebuffer and begin the pass in the command buffer. @@ -362,6 +496,9 @@ bool VulkanCommandProcessor::IssueDraw(PrimitiveType primitive_type, } } + // Update the render cache's tracking state. + render_cache_->UpdateState(); + // Configure the pipeline for drawing. // This encodes all render state (blend, depth, etc), our shader stages, // and our vertex input layout. @@ -373,6 +510,13 @@ bool VulkanCommandProcessor::IssueDraw(PrimitiveType primitive_type, started_command_buffer) { vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); + } else if (pipeline_status == PipelineCache::UpdateStatus::kError) { + render_cache_->EndRenderPass(); + command_buffer_pool_->CancelBatch(); + current_command_buffer_ = nullptr; + current_setup_buffer_ = nullptr; + current_batch_fence_ = nullptr; + return false; } pipeline_cache_->SetDynamicState(command_buffer, started_command_buffer); @@ -407,9 +551,17 @@ bool VulkanCommandProcessor::IssueDraw(PrimitiveType primitive_type, } // Bind samplers/textures. - vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, - pipeline_cache_->pipeline_layout(), 1, 1, &samplers, - 0, nullptr); + // Uploads all textures that need it. + // Setup buffer may be flushed to GPU if the texture cache needs it. + if (!PopulateSamplers(command_buffer, setup_buffer, vertex_shader, + pixel_shader)) { + render_cache_->EndRenderPass(); + command_buffer_pool_->CancelBatch(); + current_command_buffer_ = nullptr; + current_setup_buffer_ = nullptr; + current_batch_fence_ = nullptr; + return false; + } // Actually issue the draw. if (!index_buffer_info) { @@ -444,7 +596,7 @@ bool VulkanCommandProcessor::PopulateConstants(VkCommandBuffer command_buffer, // These are optional, and if none are defined 0 will be returned. auto constant_offsets = buffer_cache_->UploadConstantRegisters( vertex_shader->constant_register_map(), - pixel_shader->constant_register_map()); + pixel_shader->constant_register_map(), current_batch_fence_); if (constant_offsets.first == VK_WHOLE_SIZE || constant_offsets.second == VK_WHOLE_SIZE) { // Shader wants constants but we couldn't upload them. @@ -497,8 +649,8 @@ bool VulkanCommandProcessor::PopulateIndexBuffer( size_t source_length = info.count * (info.format == IndexFormat::kInt32 ? sizeof(uint32_t) : sizeof(uint16_t)); - auto buffer_ref = - buffer_cache_->UploadIndexBuffer(source_ptr, source_length, info.format); + auto buffer_ref = buffer_cache_->UploadIndexBuffer( + source_ptr, source_length, info.format, current_batch_fence_); if (buffer_ref.second == VK_WHOLE_SIZE) { // Failed to upload buffer. return false; @@ -523,6 +675,11 @@ bool VulkanCommandProcessor::PopulateVertexBuffers( #endif // FINE_GRAINED_DRAW_SCOPES auto& vertex_bindings = vertex_shader->vertex_bindings(); + if (vertex_bindings.empty()) { + // No bindings. + return true; + } + assert_true(vertex_bindings.size() <= 32); VkBuffer all_buffers[32]; VkDeviceSize all_buffer_offsets[32]; @@ -556,8 +713,8 @@ bool VulkanCommandProcessor::PopulateVertexBuffers( const void* source_ptr = memory_->TranslatePhysical(fetch->address << 2); size_t source_length = valid_range; - auto buffer_ref = - buffer_cache_->UploadVertexBuffer(source_ptr, source_length); + auto buffer_ref = buffer_cache_->UploadVertexBuffer( + source_ptr, source_length, current_batch_fence_); if (buffer_ref.second == VK_WHOLE_SIZE) { // Failed to upload buffer. return false; @@ -576,9 +733,9 @@ bool VulkanCommandProcessor::PopulateVertexBuffers( return true; } -VkDescriptorSet VulkanCommandProcessor::PopulateSamplers( - VkCommandBuffer command_buffer, VulkanShader* vertex_shader, - VulkanShader* pixel_shader) { +bool VulkanCommandProcessor::PopulateSamplers( + VkCommandBuffer command_buffer, VkCommandBuffer setup_buffer, + VulkanShader* vertex_shader, VulkanShader* pixel_shader) { #if FINE_GRAINED_DRAW_SCOPES SCOPE_profile_cpu_f("gpu"); #endif // FINE_GRAINED_DRAW_SCOPES @@ -588,10 +745,14 @@ VkDescriptorSet VulkanCommandProcessor::PopulateSamplers( pixel_shader->texture_bindings()); if (!descriptor_set) { // Unable to bind set. - return nullptr; + return false; } - return descriptor_set; + vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, + pipeline_cache_->pipeline_layout(), 1, 1, + &descriptor_set, 0, nullptr); + + return true; } bool VulkanCommandProcessor::IssueCopy() { @@ -760,6 +921,9 @@ bool VulkanCommandProcessor::IssueCopy() { tex_info.size_2d.input_pitch = copy_dest_pitch * 4; auto texture = texture_cache_->DemandResolveTexture( tex_info, ColorFormatToTextureFormat(copy_dest_format), nullptr); + assert_not_null(texture); + texture->in_flight_fence = current_batch_fence_; + if (texture->image_layout == VK_IMAGE_LAYOUT_UNDEFINED) { // Transition the image to a general layout. VkImageMemoryBarrier image_barrier; @@ -820,10 +984,12 @@ bool VulkanCommandProcessor::IssueCopy() { : static_cast(depth_format); switch (copy_command) { case CopyCommand::kRaw: + /* render_cache_->RawCopyToImage(command_buffer, edram_base, texture->image, texture->image_layout, copy_src_select <= 3, resolve_offset, resolve_extent); break; + */ case CopyCommand::kConvert: render_cache_->BlitToImage( command_buffer, edram_base, surface_pitch, resolve_extent.height, diff --git a/src/xenia/gpu/vulkan/vulkan_command_processor.h b/src/xenia/gpu/vulkan/vulkan_command_processor.h index 287e4f65e..4a7788e09 100644 --- a/src/xenia/gpu/vulkan/vulkan_command_processor.h +++ b/src/xenia/gpu/vulkan/vulkan_command_processor.h @@ -61,6 +61,9 @@ class VulkanCommandProcessor : public CommandProcessor { void PrepareForWait() override; void ReturnFromWait() override; + void CreateSwapImages(VkCommandBuffer setup_buffer, VkExtent2D extents); + void DestroySwapImages(); + void PerformSwap(uint32_t frontbuffer_ptr, uint32_t frontbuffer_width, uint32_t frontbuffer_height) override; @@ -77,13 +80,18 @@ class VulkanCommandProcessor : public CommandProcessor { IndexBufferInfo* index_buffer_info); bool PopulateVertexBuffers(VkCommandBuffer command_buffer, VulkanShader* vertex_shader); - VkDescriptorSet PopulateSamplers(VkCommandBuffer command_buffer, - VulkanShader* vertex_shader, - VulkanShader* pixel_shader); + bool PopulateSamplers(VkCommandBuffer command_buffer, + VkCommandBuffer setup_buffer, + VulkanShader* vertex_shader, + VulkanShader* pixel_shader); bool IssueCopy() override; xe::ui::vulkan::VulkanDevice* device_ = nullptr; + // front buffer / back buffer memory + VkDeviceMemory fb_memory = nullptr; + VkDeviceMemory bb_memory = nullptr; + // TODO(benvanik): abstract behind context? // Queue used to submit work. This may be a dedicated queue for the command // processor and no locking will be required for use. If a dedicated queue diff --git a/src/xenia/gpu/vulkan/vulkan_graphics_system.cc b/src/xenia/gpu/vulkan/vulkan_graphics_system.cc index 27b2ff073..08c6120d7 100644 --- a/src/xenia/gpu/vulkan/vulkan_graphics_system.cc +++ b/src/xenia/gpu/vulkan/vulkan_graphics_system.cc @@ -76,6 +76,23 @@ 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); + + 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 = front_buffer; + barrier.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}; + vkCmdPipelineBarrier(copy_cmd_buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, + VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0, 0, nullptr, 0, + nullptr, 1, &barrier); VkImageBlit region; region.srcSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}; @@ -88,9 +105,8 @@ void VulkanGraphicsSystem::Swap(xe::ui::UIEvent* e) { region.dstOffsets[1] = {static_cast(swap_chain->surface_width()), static_cast(swap_chain->surface_height()), 1}; - vkCmdBlitImage(copy_cmd_buffer, - reinterpret_cast(swap_state.front_buffer_texture), - VK_IMAGE_LAYOUT_GENERAL, swap_chain->surface_image(), + vkCmdBlitImage(copy_cmd_buffer, front_buffer, VK_IMAGE_LAYOUT_GENERAL, + swap_chain->surface_image(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion, VK_FILTER_LINEAR); }