Merge branch 'master' into vulkan

This commit is contained in:
Triang3l
2022-02-03 21:12:10 +03:00
501 changed files with 56183 additions and 10196 deletions

View File

@@ -29,7 +29,7 @@
#include "xenia/gpu/vulkan/vulkan_shader.h"
#include "xenia/gpu/vulkan/vulkan_shared_memory.h"
#include "xenia/gpu/xenos.h"
#include "xenia/ui/vulkan/vulkan_context.h"
#include "xenia/ui/vulkan/vulkan_presenter.h"
#include "xenia/ui/vulkan/vulkan_provider.h"
#include "xenia/ui/vulkan/vulkan_util.h"
@@ -37,6 +37,12 @@ namespace xe {
namespace gpu {
namespace vulkan {
// Generated with `xb buildshaders`.
namespace shaders {
#include "xenia/gpu/shaders/bytecode/vulkan_spirv/fullscreen_tc_vert.h"
#include "xenia/gpu/shaders/bytecode/vulkan_spirv/uv_frag.h"
} // namespace shaders
VulkanCommandProcessor::VulkanCommandProcessor(
VulkanGraphicsSystem* graphics_system, kernel::KernelState* kernel_state)
: CommandProcessor(graphics_system, kernel_state),
@@ -58,8 +64,7 @@ bool VulkanCommandProcessor::SetupContext() {
return false;
}
const ui::vulkan::VulkanProvider& provider =
GetVulkanContext().GetVulkanProvider();
const ui::vulkan::VulkanProvider& provider = GetVulkanProvider();
const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider.dfn();
VkDevice device = provider.device();
@@ -274,6 +279,215 @@ bool VulkanCommandProcessor::SetupContext() {
// interlocks case.
dfn.vkUpdateDescriptorSets(device, 1, write_descriptor_sets, 0, nullptr);
// Swap objects.
// Swap render pass. Doesn't make assumptions about outer usage (explicit
// barriers must be used instead) for simplicity of use in different scenarios
// with different pipelines.
VkAttachmentDescription swap_render_pass_attachment;
swap_render_pass_attachment.flags = 0;
swap_render_pass_attachment.format =
ui::vulkan::VulkanPresenter::kGuestOutputFormat;
swap_render_pass_attachment.samples = VK_SAMPLE_COUNT_1_BIT;
swap_render_pass_attachment.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
swap_render_pass_attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
swap_render_pass_attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
swap_render_pass_attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
swap_render_pass_attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
swap_render_pass_attachment.finalLayout =
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
VkAttachmentReference swap_render_pass_color_attachment;
swap_render_pass_color_attachment.attachment = 0;
swap_render_pass_color_attachment.layout =
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
VkSubpassDescription swap_render_pass_subpass = {};
swap_render_pass_subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
swap_render_pass_subpass.colorAttachmentCount = 1;
swap_render_pass_subpass.pColorAttachments =
&swap_render_pass_color_attachment;
VkSubpassDependency swap_render_pass_dependencies[2];
for (uint32_t i = 0; i < 2; ++i) {
VkSubpassDependency& swap_render_pass_dependency =
swap_render_pass_dependencies[i];
swap_render_pass_dependency.srcSubpass = i ? 0 : VK_SUBPASS_EXTERNAL;
swap_render_pass_dependency.dstSubpass = i ? VK_SUBPASS_EXTERNAL : 0;
swap_render_pass_dependency.srcStageMask =
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
swap_render_pass_dependency.dstStageMask =
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
swap_render_pass_dependency.srcAccessMask =
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
swap_render_pass_dependency.dstAccessMask =
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
swap_render_pass_dependency.dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;
}
VkRenderPassCreateInfo swap_render_pass_create_info;
swap_render_pass_create_info.sType =
VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
swap_render_pass_create_info.pNext = nullptr;
swap_render_pass_create_info.flags = 0;
swap_render_pass_create_info.attachmentCount = 1;
swap_render_pass_create_info.pAttachments = &swap_render_pass_attachment;
swap_render_pass_create_info.subpassCount = 1;
swap_render_pass_create_info.pSubpasses = &swap_render_pass_subpass;
swap_render_pass_create_info.dependencyCount =
uint32_t(xe::countof(swap_render_pass_dependencies));
swap_render_pass_create_info.pDependencies = swap_render_pass_dependencies;
if (dfn.vkCreateRenderPass(device, &swap_render_pass_create_info, nullptr,
&swap_render_pass_) != VK_SUCCESS) {
XELOGE("Failed to create the Vulkan render pass for presentation");
return false;
}
// Swap pipeline layout.
// TODO(Triang3l): Source binding, push constants, FXAA pipeline layout.
VkPipelineLayoutCreateInfo swap_pipeline_layout_create_info;
swap_pipeline_layout_create_info.sType =
VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
swap_pipeline_layout_create_info.pNext = nullptr;
swap_pipeline_layout_create_info.flags = 0;
swap_pipeline_layout_create_info.setLayoutCount = 0;
swap_pipeline_layout_create_info.pSetLayouts = nullptr;
swap_pipeline_layout_create_info.pushConstantRangeCount = 0;
swap_pipeline_layout_create_info.pPushConstantRanges = nullptr;
if (dfn.vkCreatePipelineLayout(device, &swap_pipeline_layout_create_info,
nullptr,
&swap_pipeline_layout_) != VK_SUCCESS) {
XELOGE("Failed to create the Vulkan pipeline layout for presentation");
return false;
}
// Swap pipeline.
VkPipelineShaderStageCreateInfo swap_pipeline_stages[2];
swap_pipeline_stages[0].sType =
VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
swap_pipeline_stages[0].pNext = nullptr;
swap_pipeline_stages[0].flags = 0;
swap_pipeline_stages[0].stage = VK_SHADER_STAGE_VERTEX_BIT;
swap_pipeline_stages[0].module = ui::vulkan::util::CreateShaderModule(
provider, shaders::fullscreen_tc_vert,
sizeof(shaders::fullscreen_tc_vert));
if (swap_pipeline_stages[0].module == VK_NULL_HANDLE) {
XELOGE("Failed to create the Vulkan vertex shader module for presentation");
return false;
}
swap_pipeline_stages[0].pName = "main";
swap_pipeline_stages[0].pSpecializationInfo = nullptr;
swap_pipeline_stages[1].sType =
VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
swap_pipeline_stages[1].pNext = nullptr;
swap_pipeline_stages[1].flags = 0;
swap_pipeline_stages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;
swap_pipeline_stages[1].module = ui::vulkan::util::CreateShaderModule(
provider, shaders::uv_frag, sizeof(shaders::uv_frag));
if (swap_pipeline_stages[1].module == VK_NULL_HANDLE) {
XELOGE(
"Failed to create the Vulkan fragment shader module for presentation");
dfn.vkDestroyShaderModule(device, swap_pipeline_stages[0].module, nullptr);
return false;
}
swap_pipeline_stages[1].pName = "main";
swap_pipeline_stages[1].pSpecializationInfo = nullptr;
VkPipelineVertexInputStateCreateInfo swap_pipeline_vertex_input_state = {};
swap_pipeline_vertex_input_state.sType =
VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
VkPipelineInputAssemblyStateCreateInfo swap_pipeline_input_assembly_state;
swap_pipeline_input_assembly_state.sType =
VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
swap_pipeline_input_assembly_state.pNext = nullptr;
swap_pipeline_input_assembly_state.flags = 0;
swap_pipeline_input_assembly_state.topology =
VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
swap_pipeline_input_assembly_state.primitiveRestartEnable = VK_FALSE;
VkPipelineViewportStateCreateInfo swap_pipeline_viewport_state;
swap_pipeline_viewport_state.sType =
VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
swap_pipeline_viewport_state.pNext = nullptr;
swap_pipeline_viewport_state.flags = 0;
swap_pipeline_viewport_state.viewportCount = 1;
swap_pipeline_viewport_state.pViewports = nullptr;
swap_pipeline_viewport_state.scissorCount = 1;
swap_pipeline_viewport_state.pScissors = nullptr;
VkPipelineRasterizationStateCreateInfo swap_pipeline_rasterization_state = {};
swap_pipeline_rasterization_state.sType =
VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
swap_pipeline_rasterization_state.polygonMode = VK_POLYGON_MODE_FILL;
swap_pipeline_rasterization_state.cullMode = VK_CULL_MODE_NONE;
swap_pipeline_rasterization_state.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
swap_pipeline_rasterization_state.lineWidth = 1.0f;
VkPipelineMultisampleStateCreateInfo swap_pipeline_multisample_state = {};
swap_pipeline_multisample_state.sType =
VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
swap_pipeline_multisample_state.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
VkPipelineColorBlendAttachmentState
swap_pipeline_color_blend_attachment_state = {};
swap_pipeline_color_blend_attachment_state.colorWriteMask =
VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
VkPipelineColorBlendStateCreateInfo swap_pipeline_color_blend_state = {};
swap_pipeline_color_blend_state.sType =
VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
swap_pipeline_color_blend_state.attachmentCount = 1;
swap_pipeline_color_blend_state.pAttachments =
&swap_pipeline_color_blend_attachment_state;
static const VkDynamicState kSwapPipelineDynamicStates[] = {
VK_DYNAMIC_STATE_VIEWPORT,
VK_DYNAMIC_STATE_SCISSOR,
};
VkPipelineDynamicStateCreateInfo swap_pipeline_dynamic_state;
swap_pipeline_dynamic_state.sType =
VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
swap_pipeline_dynamic_state.pNext = nullptr;
swap_pipeline_dynamic_state.flags = 0;
swap_pipeline_dynamic_state.dynamicStateCount =
uint32_t(xe::countof(kSwapPipelineDynamicStates));
swap_pipeline_dynamic_state.pDynamicStates = kSwapPipelineDynamicStates;
VkGraphicsPipelineCreateInfo swap_pipeline_create_info;
swap_pipeline_create_info.sType =
VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
swap_pipeline_create_info.pNext = nullptr;
swap_pipeline_create_info.flags = 0;
swap_pipeline_create_info.stageCount =
uint32_t(xe::countof(swap_pipeline_stages));
swap_pipeline_create_info.pStages = swap_pipeline_stages;
swap_pipeline_create_info.pVertexInputState =
&swap_pipeline_vertex_input_state;
swap_pipeline_create_info.pInputAssemblyState =
&swap_pipeline_input_assembly_state;
swap_pipeline_create_info.pTessellationState = nullptr;
swap_pipeline_create_info.pViewportState = &swap_pipeline_viewport_state;
swap_pipeline_create_info.pRasterizationState =
&swap_pipeline_rasterization_state;
swap_pipeline_create_info.pMultisampleState =
&swap_pipeline_multisample_state;
swap_pipeline_create_info.pDepthStencilState = nullptr;
swap_pipeline_create_info.pColorBlendState = &swap_pipeline_color_blend_state;
swap_pipeline_create_info.pDynamicState = &swap_pipeline_dynamic_state;
swap_pipeline_create_info.layout = swap_pipeline_layout_;
swap_pipeline_create_info.renderPass = swap_render_pass_;
swap_pipeline_create_info.subpass = 0;
swap_pipeline_create_info.basePipelineHandle = VK_NULL_HANDLE;
swap_pipeline_create_info.basePipelineIndex = UINT32_MAX;
VkResult swap_pipeline_create_result = dfn.vkCreateGraphicsPipelines(
device, VK_NULL_HANDLE, 1, &swap_pipeline_create_info, nullptr,
&swap_pipeline_);
for (size_t i = 0; i < xe::countof(swap_pipeline_stages); ++i) {
dfn.vkDestroyShaderModule(device, swap_pipeline_stages[i].module, nullptr);
}
if (swap_pipeline_create_result != VK_SUCCESS) {
XELOGE("Failed to create the Vulkan pipeline for presentation");
return false;
}
// Just not to expose uninitialized memory.
std::memset(&system_constants_, 0, sizeof(system_constants_));
@@ -283,11 +497,26 @@ bool VulkanCommandProcessor::SetupContext() {
void VulkanCommandProcessor::ShutdownContext() {
AwaitAllQueueOperationsCompletion();
const ui::vulkan::VulkanProvider& provider =
GetVulkanContext().GetVulkanProvider();
const ui::vulkan::VulkanProvider& provider = GetVulkanProvider();
const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider.dfn();
VkDevice device = provider.device();
for (const auto& framebuffer_pair : swap_framebuffers_outdated_) {
dfn.vkDestroyFramebuffer(device, framebuffer_pair.second, nullptr);
}
swap_framebuffers_outdated_.clear();
for (SwapFramebuffer& swap_framebuffer : swap_framebuffers_) {
ui::vulkan::util::DestroyAndNullHandle(dfn.vkDestroyFramebuffer, device,
swap_framebuffer.framebuffer);
}
ui::vulkan::util::DestroyAndNullHandle(dfn.vkDestroyPipeline, device,
swap_pipeline_);
ui::vulkan::util::DestroyAndNullHandle(dfn.vkDestroyPipelineLayout, device,
swap_pipeline_layout_);
ui::vulkan::util::DestroyAndNullHandle(dfn.vkDestroyRenderPass, device,
swap_render_pass_);
ui::vulkan::util::DestroyAndNullHandle(
dfn.vkDestroyDescriptorPool, device,
shared_memory_and_edram_descriptor_pool_);
@@ -339,7 +568,7 @@ void VulkanCommandProcessor::ShutdownContext() {
deferred_command_buffer_.Reset();
for (const auto& command_buffer_pair : command_buffers_submitted_) {
dfn.vkDestroyCommandPool(device, command_buffer_pair.first.pool, nullptr);
dfn.vkDestroyCommandPool(device, command_buffer_pair.second.pool, nullptr);
}
command_buffers_submitted_.clear();
for (const CommandBuffer& command_buffer : command_buffers_writable_) {
@@ -353,7 +582,7 @@ void VulkanCommandProcessor::ShutdownContext() {
frame_open_ = false;
for (const auto& semaphore : submissions_in_flight_semaphores_) {
dfn.vkDestroySemaphore(device, semaphore.first, nullptr);
dfn.vkDestroySemaphore(device, semaphore.second, nullptr);
}
submissions_in_flight_semaphores_.clear();
for (VkFence& fence : submissions_in_flight_fences_) {
@@ -377,6 +606,8 @@ void VulkanCommandProcessor::ShutdownContext() {
}
fences_free_.clear();
device_lost_ = false;
CommandProcessor::ShutdownContext();
}
@@ -432,18 +663,228 @@ void VulkanCommandProcessor::SparseBindBuffer(
sparse_bind_wait_stage_mask_ |= wait_stage_mask;
}
void VulkanCommandProcessor::PerformSwap(uint32_t frontbuffer_ptr,
uint32_t frontbuffer_width,
uint32_t frontbuffer_height) {
void VulkanCommandProcessor::IssueSwap(uint32_t frontbuffer_ptr,
uint32_t frontbuffer_width,
uint32_t frontbuffer_height) {
// FIXME(Triang3l): frontbuffer_ptr is currently unreliable, in the trace
// player it's set to 0, but it's not needed anyway since the fetch constant
// contains the address.
SCOPE_profile_cpu_f("gpu");
// In case the swap command is the only one in the frame.
BeginSubmission(true);
ui::Presenter* presenter = graphics_system_->presenter();
if (!presenter) {
return;
}
// TODO(Triang3l): Resolution scale.
uint32_t resolution_scale = 1;
uint32_t scaled_width = frontbuffer_width * resolution_scale;
uint32_t scaled_height = frontbuffer_height * resolution_scale;
presenter->RefreshGuestOutput(
scaled_width, scaled_height, 1280 * resolution_scale,
720 * resolution_scale,
[this, scaled_width, scaled_height](
ui::Presenter::GuestOutputRefreshContext& context) -> bool {
// In case the swap command is the only one in the frame.
if (!BeginSubmission(true)) {
return false;
}
auto& vulkan_context = static_cast<
ui::vulkan::VulkanPresenter::VulkanGuestOutputRefreshContext&>(
context);
uint64_t guest_output_image_version = vulkan_context.image_version();
const ui::vulkan::VulkanProvider& provider = GetVulkanProvider();
const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider.dfn();
VkDevice device = provider.device();
// Make sure a framebuffer is available for the current guest output
// image version.
size_t swap_framebuffer_index = SIZE_MAX;
size_t swap_framebuffer_new_index = SIZE_MAX;
// Try to find the existing framebuffer for the current guest output
// image version, or an unused (without an existing framebuffer, or with
// one, but that has never actually been used dynamically) slot.
for (size_t i = 0; i < swap_framebuffers_.size(); ++i) {
const SwapFramebuffer& existing_swap_framebuffer =
swap_framebuffers_[i];
if (existing_swap_framebuffer.framebuffer != VK_NULL_HANDLE &&
existing_swap_framebuffer.version == guest_output_image_version) {
swap_framebuffer_index = i;
break;
}
if (existing_swap_framebuffer.framebuffer == VK_NULL_HANDLE ||
!existing_swap_framebuffer.last_submission) {
swap_framebuffer_new_index = i;
}
}
if (swap_framebuffer_index == SIZE_MAX) {
if (swap_framebuffer_new_index == SIZE_MAX) {
// Replace the earliest used framebuffer.
swap_framebuffer_new_index = 0;
for (size_t i = 1; i < swap_framebuffers_.size(); ++i) {
if (swap_framebuffers_[i].last_submission <
swap_framebuffers_[swap_framebuffer_new_index]
.last_submission) {
swap_framebuffer_new_index = i;
}
}
}
swap_framebuffer_index = swap_framebuffer_new_index;
SwapFramebuffer& new_swap_framebuffer =
swap_framebuffers_[swap_framebuffer_new_index];
if (new_swap_framebuffer.framebuffer != VK_NULL_HANDLE) {
if (submission_completed_ >= new_swap_framebuffer.last_submission) {
dfn.vkDestroyFramebuffer(device, new_swap_framebuffer.framebuffer,
nullptr);
} else {
swap_framebuffers_outdated_.emplace_back(
new_swap_framebuffer.last_submission,
new_swap_framebuffer.framebuffer);
}
new_swap_framebuffer.framebuffer = VK_NULL_HANDLE;
}
VkImageView guest_output_image_view_srgb =
vulkan_context.image_view();
VkFramebufferCreateInfo swap_framebuffer_create_info;
swap_framebuffer_create_info.sType =
VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
swap_framebuffer_create_info.pNext = nullptr;
swap_framebuffer_create_info.flags = 0;
swap_framebuffer_create_info.renderPass = swap_render_pass_;
swap_framebuffer_create_info.attachmentCount = 1;
swap_framebuffer_create_info.pAttachments =
&guest_output_image_view_srgb;
swap_framebuffer_create_info.width = scaled_width;
swap_framebuffer_create_info.height = scaled_height;
swap_framebuffer_create_info.layers = 1;
if (dfn.vkCreateFramebuffer(
device, &swap_framebuffer_create_info, nullptr,
&new_swap_framebuffer.framebuffer) != VK_SUCCESS) {
XELOGE("Failed to create the Vulkan framebuffer for presentation");
return false;
}
new_swap_framebuffer.version = guest_output_image_version;
// The actual submission index will be set if the framebuffer is
// actually used, not dropped due to some error.
new_swap_framebuffer.last_submission = 0;
}
// End the current render pass before inserting barriers and starting a
// new one.
EndRenderPass();
if (vulkan_context.image_ever_written_previously()) {
// Insert a barrier after the last presenter's usage of the guest
// output image.
VkImageMemoryBarrier guest_output_image_acquire_barrier;
guest_output_image_acquire_barrier.sType =
VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
guest_output_image_acquire_barrier.pNext = nullptr;
guest_output_image_acquire_barrier.srcAccessMask =
ui::vulkan::VulkanPresenter::kGuestOutputInternalAccessMask;
guest_output_image_acquire_barrier.dstAccessMask =
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
// Will be overwriting all the contents.
guest_output_image_acquire_barrier.oldLayout =
VK_IMAGE_LAYOUT_UNDEFINED;
// The render pass will do the layout transition, but newLayout must
// not be UNDEFINED.
guest_output_image_acquire_barrier.newLayout =
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
guest_output_image_acquire_barrier.srcQueueFamilyIndex =
VK_QUEUE_FAMILY_IGNORED;
guest_output_image_acquire_barrier.dstQueueFamilyIndex =
VK_QUEUE_FAMILY_IGNORED;
guest_output_image_acquire_barrier.image = vulkan_context.image();
ui::vulkan::util::InitializeSubresourceRange(
guest_output_image_acquire_barrier.subresourceRange);
deferred_command_buffer_.CmdVkPipelineBarrier(
ui::vulkan::VulkanPresenter::kGuestOutputInternalStageMask,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0, 0, nullptr, 0,
nullptr, 1, &guest_output_image_acquire_barrier);
}
SwapFramebuffer& swap_framebuffer =
swap_framebuffers_[swap_framebuffer_index];
swap_framebuffer.last_submission = GetCurrentSubmission();
VkRenderPassBeginInfo render_pass_begin_info;
render_pass_begin_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
render_pass_begin_info.pNext = nullptr;
render_pass_begin_info.renderPass = swap_render_pass_;
render_pass_begin_info.framebuffer = swap_framebuffer.framebuffer;
render_pass_begin_info.renderArea.offset.x = 0;
render_pass_begin_info.renderArea.offset.y = 0;
render_pass_begin_info.renderArea.extent.width = scaled_width;
render_pass_begin_info.renderArea.extent.height = scaled_height;
render_pass_begin_info.clearValueCount = 0;
render_pass_begin_info.pClearValues = nullptr;
deferred_command_buffer_.CmdVkBeginRenderPass(
&render_pass_begin_info, VK_SUBPASS_CONTENTS_INLINE);
ff_viewport_update_needed_ = true;
ff_scissor_update_needed_ = true;
VkViewport viewport;
viewport.x = 0.0f;
viewport.y = 0.0f;
viewport.width = float(scaled_width);
viewport.height = float(scaled_height);
viewport.minDepth = 0.0f;
viewport.maxDepth = 1.0f;
deferred_command_buffer_.CmdVkSetViewport(0, 1, &viewport);
VkRect2D scissor_rect;
scissor_rect.offset.x = 0;
scissor_rect.offset.y = 0;
scissor_rect.extent.width = scaled_width;
scissor_rect.extent.height = scaled_height;
deferred_command_buffer_.CmdVkSetScissor(0, 1, &scissor_rect);
// Bind a non-emulation graphics pipeline and invalidate the bindings.
current_graphics_pipeline_ = VK_NULL_HANDLE;
current_graphics_pipeline_layout_ = nullptr;
deferred_command_buffer_.CmdVkBindPipeline(
VK_PIPELINE_BIND_POINT_GRAPHICS, swap_pipeline_);
deferred_command_buffer_.CmdVkDraw(3, 1, 0, 0);
deferred_command_buffer_.CmdVkEndRenderPass();
VkImageMemoryBarrier guest_output_image_release_barrier;
guest_output_image_release_barrier.sType =
VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
guest_output_image_release_barrier.pNext = nullptr;
guest_output_image_release_barrier.srcAccessMask =
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
guest_output_image_release_barrier.dstAccessMask =
ui::vulkan::VulkanPresenter::kGuestOutputInternalAccessMask;
guest_output_image_release_barrier.oldLayout =
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
guest_output_image_release_barrier.newLayout =
ui::vulkan::VulkanPresenter::kGuestOutputInternalLayout;
guest_output_image_release_barrier.srcQueueFamilyIndex =
VK_QUEUE_FAMILY_IGNORED;
guest_output_image_release_barrier.dstQueueFamilyIndex =
VK_QUEUE_FAMILY_IGNORED;
guest_output_image_release_barrier.image = vulkan_context.image();
ui::vulkan::util::InitializeSubresourceRange(
guest_output_image_release_barrier.subresourceRange);
deferred_command_buffer_.CmdVkPipelineBarrier(
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
ui::vulkan::VulkanPresenter::kGuestOutputInternalStageMask, 0, 0,
nullptr, 0, nullptr, 1, &guest_output_image_release_barrier);
// Need to submit all the commands before giving the image back to the
// presenter so it can submit its own commands for displaying it to the
// queue.
EndSubmission(true);
return true;
});
// End the frame even if did not present for any reason (the image refresher
// was not called), to prevent leaking per-frame resources.
EndSubmission(true);
}
@@ -454,6 +895,7 @@ void VulkanCommandProcessor::EndRenderPass() {
}
deferred_command_buffer_.CmdVkEndRenderPass();
current_render_pass_ = VK_NULL_HANDLE;
current_framebuffer_ = VK_NULL_HANDLE;
}
const VulkanPipelineCache::PipelineLayoutProvider*
@@ -469,8 +911,7 @@ VulkanCommandProcessor::GetPipelineLayout(uint32_t texture_count_pixel,
}
}
const ui::vulkan::VulkanProvider& provider =
GetVulkanContext().GetVulkanProvider();
const ui::vulkan::VulkanProvider& provider = GetVulkanProvider();
const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider.dfn();
VkDevice device = provider.device();
@@ -675,7 +1116,9 @@ bool VulkanCommandProcessor::IssueDraw(xenos::PrimitiveType prim_type,
}
// TODO(Triang3l): Memory export.
BeginSubmission(true);
if (!BeginSubmission(true)) {
return false;
}
// Process primitives.
PrimitiveProcessor::ProcessingResult primitive_processing_result;
@@ -765,8 +1208,7 @@ bool VulkanCommandProcessor::IssueDraw(xenos::PrimitiveType prim_type,
current_graphics_pipeline_layout_ = pipeline_layout;
}
const ui::vulkan::VulkanProvider& provider =
GetVulkanContext().GetVulkanProvider();
const ui::vulkan::VulkanProvider& provider = GetVulkanProvider();
const VkPhysicalDeviceProperties& device_properties =
provider.device_properties();
@@ -925,13 +1367,17 @@ bool VulkanCommandProcessor::IssueCopy() {
SCOPE_profile_cpu_f("gpu");
#endif // XE_UI_VULKAN_FINE_GRAINED_DRAW_SCOPES
BeginSubmission(true);
if (!BeginSubmission(true)) {
return false;
}
return true;
}
void VulkanCommandProcessor::InitializeTrace() {
BeginSubmission(false);
if (!BeginSubmission(true)) {
return;
}
bool shared_memory_submitted =
shared_memory_->InitializeTraceSubmitDownloads();
if (!shared_memory_submitted) {
@@ -943,7 +1389,13 @@ void VulkanCommandProcessor::InitializeTrace() {
}
}
void VulkanCommandProcessor::CheckSubmissionFence(uint64_t await_submission) {
void VulkanCommandProcessor::CheckSubmissionFenceAndDeviceLoss(
uint64_t await_submission) {
// Only report once, no need to retry a wait that won't succeed anyway.
if (device_lost_) {
return;
}
if (await_submission >= GetCurrentSubmission()) {
if (submission_open_) {
EndSubmission(false);
@@ -953,8 +1405,7 @@ void VulkanCommandProcessor::CheckSubmissionFence(uint64_t await_submission) {
await_submission = GetCurrentSubmission() - 1;
}
const ui::vulkan::VulkanProvider& provider =
GetVulkanContext().GetVulkanProvider();
const ui::vulkan::VulkanProvider& provider = GetVulkanProvider();
const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider.dfn();
VkDevice device = provider.device();
@@ -962,26 +1413,40 @@ void VulkanCommandProcessor::CheckSubmissionFence(uint64_t await_submission) {
size_t fences_awaited = 0;
if (await_submission > submission_completed_) {
// Await in a blocking way if requested.
if (dfn.vkWaitForFences(device,
uint32_t(await_submission - submission_completed_),
submissions_in_flight_fences_.data(), VK_TRUE,
UINT64_MAX) == VK_SUCCESS) {
// TODO(Triang3l): Await only one fence. "Fence signal operations that are
// defined by vkQueueSubmit additionally include in the first
// synchronization scope all commands that occur earlier in submission
// order."
VkResult wait_result = dfn.vkWaitForFences(
device, uint32_t(await_submission - submission_completed_),
submissions_in_flight_fences_.data(), VK_TRUE, UINT64_MAX);
if (wait_result == VK_SUCCESS) {
fences_awaited += await_submission - submission_completed_;
} else {
XELOGE("Failed to await submission completion Vulkan fences");
if (wait_result == VK_ERROR_DEVICE_LOST) {
device_lost_ = true;
}
}
}
// Check how far into the submissions the GPU currently is, in order because
// submission themselves can be executed out of order, but Xenia serializes
// that for simplicity.
while (fences_awaited < fences_total) {
if (dfn.vkWaitForFences(device, 1,
&submissions_in_flight_fences_[fences_awaited],
VK_TRUE, 0) != VK_SUCCESS) {
VkResult fence_status = dfn.vkWaitForFences(
device, 1, &submissions_in_flight_fences_[fences_awaited], VK_TRUE, 0);
if (fence_status != VK_SUCCESS) {
if (fence_status == VK_ERROR_DEVICE_LOST) {
device_lost_ = true;
}
break;
}
++fences_awaited;
}
if (device_lost_) {
graphics_system_->OnHostGpuLossFromAnyThread(true);
return;
}
if (!fences_awaited) {
// Not updated - no need to reclaim or download things.
return;
@@ -1002,48 +1467,65 @@ void VulkanCommandProcessor::CheckSubmissionFence(uint64_t await_submission) {
while (!submissions_in_flight_semaphores_.empty()) {
const auto& semaphore_submission =
submissions_in_flight_semaphores_.front();
if (semaphore_submission.second > submission_completed_) {
if (semaphore_submission.first > submission_completed_) {
break;
}
semaphores_free_.push_back(semaphore_submission.first);
semaphores_free_.push_back(semaphore_submission.second);
submissions_in_flight_semaphores_.pop_front();
}
// Reclaim command pools.
while (!command_buffers_submitted_.empty()) {
const auto& command_buffer_pair = command_buffers_submitted_.front();
if (command_buffer_pair.second > submission_completed_) {
if (command_buffer_pair.first > submission_completed_) {
break;
}
command_buffers_writable_.push_back(command_buffer_pair.first);
command_buffers_writable_.push_back(command_buffer_pair.second);
command_buffers_submitted_.pop_front();
}
shared_memory_->CompletedSubmissionUpdated();
primitive_processor_->CompletedSubmissionUpdated();
// Destroy outdated swap objects.
while (!swap_framebuffers_outdated_.empty()) {
const auto& framebuffer_pair = swap_framebuffers_outdated_.front();
if (framebuffer_pair.first > submission_completed_) {
break;
}
dfn.vkDestroyFramebuffer(device, framebuffer_pair.second, nullptr);
swap_framebuffers_outdated_.pop_front();
}
}
void VulkanCommandProcessor::BeginSubmission(bool is_guest_command) {
bool VulkanCommandProcessor::BeginSubmission(bool is_guest_command) {
#if XE_UI_VULKAN_FINE_GRAINED_DRAW_SCOPES
SCOPE_profile_cpu_f("gpu");
#endif // XE_UI_VULKAN_FINE_GRAINED_DRAW_SCOPES
if (device_lost_) {
return false;
}
bool is_opening_frame = is_guest_command && !frame_open_;
if (submission_open_ && !is_opening_frame) {
return;
return true;
}
// Check the fence - needed for all kinds of submissions (to reclaim transient
// resources early) and specifically for frames (not to queue too many), and
// await the availability of the current frame.
CheckSubmissionFence(
// await the availability of the current frame. Also check whether the device
// is still available, and whether the await was successful.
uint64_t await_submission =
is_opening_frame
? closed_frame_submissions_[frame_current_ % kMaxFramesInFlight]
: 0);
// TODO(Triang3l): If failed to await (completed submission < awaited frame
// submission), do something like dropping the draw command that wanted to
// open the frame.
: 0;
CheckSubmissionFenceAndDeviceLoss(await_submission);
if (device_lost_ || submission_completed_ < await_submission) {
return false;
}
if (is_opening_frame) {
// Update the completed frame index, also obtaining the actual completed
// frame number (since the CPU may be actually less than 3 frames behind)
@@ -1104,10 +1586,12 @@ void VulkanCommandProcessor::BeginSubmission(bool is_guest_command) {
primitive_processor_->BeginFrame();
}
return true;
}
bool VulkanCommandProcessor::EndSubmission(bool is_swap) {
ui::vulkan::VulkanProvider& provider = GetVulkanContext().GetVulkanProvider();
ui::vulkan::VulkanProvider& provider = GetVulkanProvider();
const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider.dfn();
VkDevice device = provider.device();
@@ -1219,8 +1703,14 @@ bool VulkanCommandProcessor::EndSubmission(bool is_swap) {
bind_sparse_info.pImageBinds = 0;
bind_sparse_info.signalSemaphoreCount = 1;
bind_sparse_info.pSignalSemaphores = &bind_sparse_semaphore;
if (provider.BindSparse(1, &bind_sparse_info, VK_NULL_HANDLE) !=
VK_SUCCESS) {
VkResult bind_sparse_result;
{
ui::vulkan::VulkanProvider::QueueAcquisition queue_acquisition(
provider.AcquireQueue(provider.queue_family_sparse_binding(), 0));
bind_sparse_result = dfn.vkQueueBindSparse(
queue_acquisition.queue, 1, &bind_sparse_info, VK_NULL_HANDLE);
}
if (bind_sparse_result != VK_SUCCESS) {
XELOGE("Failed to submit Vulkan sparse binds");
return false;
}
@@ -1281,19 +1771,29 @@ bool VulkanCommandProcessor::EndSubmission(bool is_swap) {
XELOGE("Failed to reset a Vulkan submission fence");
return false;
}
if (provider.SubmitToGraphicsComputeQueue(1, &submit_info, fence) !=
VK_SUCCESS) {
VkResult submit_result;
{
ui::vulkan::VulkanProvider::QueueAcquisition queue_acquisition(
provider.AcquireQueue(provider.queue_family_graphics_compute(), 0));
submit_result =
dfn.vkQueueSubmit(queue_acquisition.queue, 1, &submit_info, fence);
}
if (submit_result != VK_SUCCESS) {
XELOGE("Failed to submit a Vulkan command buffer");
if (submit_result == VK_ERROR_DEVICE_LOST && !device_lost_) {
device_lost_ = true;
graphics_system_->OnHostGpuLossFromAnyThread(true);
}
return false;
}
uint64_t submission_current = GetCurrentSubmission();
current_submission_wait_stage_masks_.clear();
for (VkSemaphore semaphore : current_submission_wait_semaphores_) {
submissions_in_flight_semaphores_.emplace_back(semaphore,
submission_current);
submissions_in_flight_semaphores_.emplace_back(submission_current,
semaphore);
}
current_submission_wait_semaphores_.clear();
command_buffers_submitted_.emplace_back(command_buffer, submission_current);
command_buffers_submitted_.emplace_back(submission_current, command_buffer);
command_buffers_writable_.pop_back();
// Increments the current submission number, going to the next submission.
submissions_in_flight_fences_.push_back(fence);
@@ -1337,6 +1837,11 @@ bool VulkanCommandProcessor::EndSubmission(bool is_swap) {
descriptor_set_layouts_textures_.clear();
primitive_processor_->ClearCache();
for (SwapFramebuffer& swap_framebuffer : swap_framebuffers_) {
ui::vulkan::util::DestroyAndNullHandle(dfn.vkDestroyFramebuffer, device,
swap_framebuffer.framebuffer);
}
}
}
@@ -1346,8 +1851,7 @@ bool VulkanCommandProcessor::EndSubmission(bool is_swap) {
VkShaderStageFlags VulkanCommandProcessor::GetGuestVertexShaderStageFlags()
const {
VkShaderStageFlags stages = VK_SHADER_STAGE_VERTEX_BIT;
const ui::vulkan::VulkanProvider& provider =
GetVulkanContext().GetVulkanProvider();
const ui::vulkan::VulkanProvider& provider = GetVulkanProvider();
if (provider.device_features().tessellationShader) {
stages |= VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT;
}
@@ -1685,8 +2189,7 @@ bool VulkanCommandProcessor::UpdateBindings(const VulkanShader* vertex_shader,
write_fetch_constants.dstSet;
}
if (write_descriptor_set_count) {
const ui::vulkan::VulkanProvider& provider =
GetVulkanContext().GetVulkanProvider();
const ui::vulkan::VulkanProvider& provider = GetVulkanProvider();
const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider.dfn();
VkDevice device = provider.device();
dfn.vkUpdateDescriptorSets(device, write_descriptor_set_count,
@@ -1748,8 +2251,7 @@ uint8_t* VulkanCommandProcessor::WriteUniformBufferBinding(
if (descriptor_set == VK_NULL_HANDLE) {
return nullptr;
}
const ui::vulkan::VulkanProvider& provider =
GetVulkanContext().GetVulkanProvider();
const ui::vulkan::VulkanProvider& provider = GetVulkanProvider();
uint8_t* mapping = uniform_buffer_pool_->Request(
frame_current_, size,
size_t(