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

@@ -36,7 +36,7 @@ void DeferredCommandBuffer::Execute(VkCommandBuffer command_buffer) {
#endif // XE_UI_VULKAN_FINE_GRAINED_DRAW_SCOPES
const ui::vulkan::VulkanProvider::DeviceFunctions& dfn =
command_processor_.GetVulkanContext().GetVulkanProvider().dfn();
command_processor_.GetVulkanProvider().dfn();
const uintmax_t* stream = command_stream_.data();
size_t stream_remaining = command_stream_.size();
while (stream_remaining) {

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(

View File

@@ -10,6 +10,7 @@
#ifndef XENIA_GPU_VULKAN_VULKAN_COMMAND_PROCESSOR_H_
#define XENIA_GPU_VULKAN_VULKAN_COMMAND_PROCESSOR_H_
#include <array>
#include <climits>
#include <cstdint>
#include <deque>
@@ -31,7 +32,8 @@
#include "xenia/gpu/xenos.h"
#include "xenia/kernel/kernel_state.h"
#include "xenia/ui/vulkan/transient_descriptor_pool.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_upload_buffer_pool.h"
namespace xe {
@@ -48,8 +50,9 @@ class VulkanCommandProcessor : public CommandProcessor {
void RestoreEdramSnapshot(const void* snapshot) override;
ui::vulkan::VulkanContext& GetVulkanContext() const {
return static_cast<ui::vulkan::VulkanContext&>(*context_);
ui::vulkan::VulkanProvider& GetVulkanProvider() const {
return *static_cast<ui::vulkan::VulkanProvider*>(
graphics_system_->provider());
}
// Returns the deferred drawing command list for the currently open
@@ -93,8 +96,8 @@ class VulkanCommandProcessor : public CommandProcessor {
void WriteRegister(uint32_t index, uint32_t value) override;
void PerformSwap(uint32_t frontbuffer_ptr, uint32_t frontbuffer_width,
uint32_t frontbuffer_height) override;
void IssueSwap(uint32_t frontbuffer_ptr, uint32_t frontbuffer_width,
uint32_t frontbuffer_height) override;
Shader* LoadShader(xenos::ShaderType shader_type, uint32_t guest_address,
const uint32_t* host_address,
@@ -159,17 +162,18 @@ class VulkanCommandProcessor : public CommandProcessor {
// Rechecks submission number and reclaims per-submission resources. Pass 0 as
// the submission to await to simply check status, or pass
// GetCurrentSubmission() to wait for all queue operations to be completed.
void CheckSubmissionFence(uint64_t await_submission);
void CheckSubmissionFenceAndDeviceLoss(uint64_t await_submission);
// If is_guest_command is true, a new full frame - with full cleanup of
// resources and, if needed, starting capturing - is opened if pending (as
// opposed to simply resuming after mid-frame synchronization).
void BeginSubmission(bool is_guest_command);
// opposed to simply resuming after mid-frame synchronization). Returns
// whether a submission is open currently and the device is not lost.
bool BeginSubmission(bool is_guest_command);
// If is_swap is true, a full frame is closed - with, if needed, cache
// clearing and stopping capturing. Returns whether the submission was done
// successfully, if it has failed, leaves it open.
bool EndSubmission(bool is_swap);
bool AwaitAllQueueOperationsCompletion() {
CheckSubmissionFence(GetCurrentSubmission());
CheckSubmissionFenceAndDeviceLoss(GetCurrentSubmission());
return !submission_open_ && submissions_in_flight_fences_.empty();
}
@@ -188,6 +192,8 @@ class VulkanCommandProcessor : public CommandProcessor {
VkDescriptorBufferInfo& descriptor_buffer_info_out,
VkWriteDescriptorSet& write_descriptor_set_out);
bool device_lost_ = false;
bool cache_clear_requested_ = false;
std::vector<VkFence> fences_free_;
@@ -200,7 +206,7 @@ class VulkanCommandProcessor : public CommandProcessor {
std::vector<VkSemaphore> current_submission_wait_semaphores_;
std::vector<VkPipelineStageFlags> current_submission_wait_stage_masks_;
std::vector<VkFence> submissions_in_flight_fences_;
std::deque<std::pair<VkSemaphore, uint64_t>>
std::deque<std::pair<uint64_t, VkSemaphore>>
submissions_in_flight_semaphores_;
static constexpr uint32_t kMaxFramesInFlight = 3;
@@ -213,7 +219,7 @@ class VulkanCommandProcessor : public CommandProcessor {
uint64_t closed_frame_submissions_[kMaxFramesInFlight] = {};
std::vector<CommandBuffer> command_buffers_writable_;
std::deque<std::pair<CommandBuffer, uint64_t>> command_buffers_submitted_;
std::deque<std::pair<uint64_t, CommandBuffer>> command_buffers_submitted_;
DeferredCommandBuffer deferred_command_buffer_;
std::vector<VkSparseMemoryBind> sparse_memory_binds_;
@@ -260,13 +266,33 @@ class VulkanCommandProcessor : public CommandProcessor {
VkDescriptorPool shared_memory_and_edram_descriptor_pool_ = VK_NULL_HANDLE;
VkDescriptorSet shared_memory_and_edram_descriptor_set_;
// Has no dependencies on specific pipeline stages on both ends to simplify
// use in different scenarios with different pipelines - use explicit barriers
// for synchronization. Drawing to VK_FORMAT_R8G8B8A8_SRGB.
VkRenderPass swap_render_pass_ = VK_NULL_HANDLE;
VkPipelineLayout swap_pipeline_layout_ = VK_NULL_HANDLE;
VkPipeline swap_pipeline_ = VK_NULL_HANDLE;
// Framebuffer for the current presenter's guest output image revision, and
// its usage tracking.
struct SwapFramebuffer {
VkFramebuffer framebuffer = VK_NULL_HANDLE;
uint64_t version = UINT64_MAX;
uint64_t last_submission = 0;
};
std::array<SwapFramebuffer,
ui::vulkan::VulkanPresenter::kMaxActiveGuestOutputImageVersions>
swap_framebuffers_;
std::deque<std::pair<uint64_t, VkFramebuffer>> swap_framebuffers_outdated_;
// The current fixed-function drawing state.
VkViewport ff_viewport_;
VkRect2D ff_scissor_;
bool ff_viewport_update_needed_;
bool ff_scissor_update_needed_;
// Cache render pass currently started in the command buffer with framebuffer.
// Cache render pass currently started in the command buffer with the
// framebuffer.
VkRenderPass current_render_pass_;
VkFramebuffer current_framebuffer_;

View File

@@ -2,7 +2,7 @@
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2020 Ben Vanik. All rights reserved. *
* Copyright 2022 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
@@ -23,28 +23,16 @@ VulkanGraphicsSystem::~VulkanGraphicsSystem() {}
X_STATUS VulkanGraphicsSystem::Setup(cpu::Processor* processor,
kernel::KernelState* kernel_state,
ui::Window* target_window) {
provider_ = xe::ui::vulkan::VulkanProvider::Create();
return GraphicsSystem::Setup(processor, kernel_state, target_window);
ui::WindowedAppContext* app_context,
bool is_surface_required) {
provider_ = xe::ui::vulkan::VulkanProvider::Create(is_surface_required);
return GraphicsSystem::Setup(processor, kernel_state, app_context,
is_surface_required);
}
void VulkanGraphicsSystem::Shutdown() { GraphicsSystem::Shutdown(); }
std::unique_ptr<CommandProcessor>
VulkanGraphicsSystem::CreateCommandProcessor() {
return std::unique_ptr<CommandProcessor>(
new VulkanCommandProcessor(this, kernel_state_));
}
void VulkanGraphicsSystem::Swap(xe::ui::UIEvent* e) {
if (!command_processor_) {
return;
}
auto& swap_state = command_processor_->swap_state();
std::lock_guard<std::mutex> lock(swap_state.mutex);
swap_state.pending = false;
return std::make_unique<VulkanCommandProcessor>(this, kernel_state_);
}
} // namespace vulkan

View File

@@ -2,7 +2,7 @@
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2020 Ben Vanik. All rights reserved. *
* Copyright 2022 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
@@ -29,13 +29,11 @@ class VulkanGraphicsSystem : public GraphicsSystem {
std::string name() const override { return "Vulkan Prototype - DO NOT USE"; }
X_STATUS Setup(cpu::Processor* processor, kernel::KernelState* kernel_state,
ui::Window* target_window) override;
void Shutdown() override;
ui::WindowedAppContext* app_context,
bool is_surface_required) override;
private:
std::unique_ptr<CommandProcessor> CreateCommandProcessor() override;
void Swap(xe::ui::UIEvent* e) override;
};
} // namespace vulkan

View File

@@ -43,7 +43,7 @@ VulkanPipelineCache::~VulkanPipelineCache() { Shutdown(); }
bool VulkanPipelineCache::Initialize() {
const ui::vulkan::VulkanProvider& provider =
command_processor_.GetVulkanContext().GetVulkanProvider();
command_processor_.GetVulkanProvider();
device_pipeline_features_.features = 0;
// TODO(Triang3l): Support the portability subset.
@@ -64,7 +64,7 @@ void VulkanPipelineCache::Shutdown() {
void VulkanPipelineCache::ClearCache() {
const ui::vulkan::VulkanProvider& provider =
command_processor_.GetVulkanContext().GetVulkanProvider();
command_processor_.GetVulkanProvider();
const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider.dfn();
VkDevice device = provider.device();
@@ -96,9 +96,9 @@ VulkanShader* VulkanPipelineCache::LoadShader(xenos::ShaderType shader_type,
// Always create the shader and stash it away.
// We need to track it even if it fails translation so we know not to try
// again.
VulkanShader* shader = new VulkanShader(
shader_type, data_hash, host_address, dword_count,
command_processor_.GetVulkanContext().GetVulkanProvider());
VulkanShader* shader =
new VulkanShader(shader_type, data_hash, host_address, dword_count,
command_processor_.GetVulkanProvider());
shaders_.emplace(data_hash, shader);
return shader;
}
@@ -396,15 +396,9 @@ bool VulkanPipelineCache::EnsurePipelineCreated(
shader_stage_fragment.pSpecializationInfo = nullptr;
}
VkPipelineVertexInputStateCreateInfo vertex_input_state;
VkPipelineVertexInputStateCreateInfo vertex_input_state = {};
vertex_input_state.sType =
VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
vertex_input_state.pNext = nullptr;
vertex_input_state.flags = 0;
vertex_input_state.vertexBindingDescriptionCount = 0;
vertex_input_state.pVertexBindingDescriptions = nullptr;
vertex_input_state.vertexAttributeDescriptionCount = 0;
vertex_input_state.pVertexAttributeDescriptions = nullptr;
VkPipelineInputAssemblyStateCreateInfo input_assembly_state;
input_assembly_state.sType =
@@ -573,10 +567,10 @@ bool VulkanPipelineCache::EnsurePipelineCreated(
pipeline_create_info.renderPass = creation_arguments.render_pass;
pipeline_create_info.subpass = 0;
pipeline_create_info.basePipelineHandle = VK_NULL_HANDLE;
pipeline_create_info.basePipelineIndex = 0;
pipeline_create_info.basePipelineIndex = UINT32_MAX;
const ui::vulkan::VulkanProvider& provider =
command_processor_.GetVulkanContext().GetVulkanProvider();
command_processor_.GetVulkanProvider();
const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider.dfn();
VkDevice device = provider.device();
VkPipeline pipeline;

View File

@@ -61,12 +61,10 @@ class VulkanPipelineCache {
// Retrieves the shader modification for the current state. The shader must
// have microcode analyzed.
SpirvShaderTranslator::Modification
VulkanPipelineCache::GetCurrentVertexShaderModification(
SpirvShaderTranslator::Modification GetCurrentVertexShaderModification(
const Shader& shader,
Shader::HostVertexShaderType host_vertex_shader_type) const;
SpirvShaderTranslator::Modification
VulkanPipelineCache::GetCurrentPixelShaderModification(
SpirvShaderTranslator::Modification GetCurrentPixelShaderModification(
const Shader& shader) const;
// TODO(Triang3l): Return a deferred creation handle.

View File

@@ -38,7 +38,7 @@ bool VulkanPrimitiveProcessor::Initialize() {
}
frame_index_buffer_pool_ =
std::make_unique<ui::vulkan::VulkanUploadBufferPool>(
command_processor_.GetVulkanContext().GetVulkanProvider(),
command_processor_.GetVulkanProvider(),
VK_BUFFER_USAGE_INDEX_BUFFER_BIT,
std::max(size_t(kMinRequiredConvertedIndexBufferSize),
ui::GraphicsUploadBufferPool::kDefaultPageSize));
@@ -47,7 +47,7 @@ bool VulkanPrimitiveProcessor::Initialize() {
void VulkanPrimitiveProcessor::Shutdown(bool from_destructor) {
const ui::vulkan::VulkanProvider& provider =
command_processor_.GetVulkanContext().GetVulkanProvider();
command_processor_.GetVulkanProvider();
const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider.dfn();
VkDevice device = provider.device();
@@ -72,7 +72,7 @@ void VulkanPrimitiveProcessor::CompletedSubmissionUpdated() {
command_processor_.GetCompletedSubmission() >=
builtin_index_buffer_upload_submission_) {
const ui::vulkan::VulkanProvider& provider =
command_processor_.GetVulkanContext().GetVulkanProvider();
command_processor_.GetVulkanProvider();
const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider.dfn();
VkDevice device = provider.device();
ui::vulkan::util::DestroyAndNullHandle(dfn.vkDestroyBuffer, device,
@@ -145,7 +145,7 @@ bool VulkanPrimitiveProcessor::InitializeBuiltin16BitIndexBuffer(
assert_true(builtin_index_buffer_upload_memory_ == VK_NULL_HANDLE);
const ui::vulkan::VulkanProvider& provider =
command_processor_.GetVulkanContext().GetVulkanProvider();
command_processor_.GetVulkanProvider();
const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider.dfn();
VkDevice device = provider.device();
@@ -199,7 +199,7 @@ bool VulkanPrimitiveProcessor::InitializeBuiltin16BitIndexBuffer(
fill_callback(reinterpret_cast<uint16_t*>(mapping));
ui::vulkan::util::FlushMappedMemoryRange(
provider, builtin_index_buffer_memory_, upload_memory_type);
dfn.vkUnmapMemory(device, builtin_index_buffer_memory_);
dfn.vkUnmapMemory(device, builtin_index_buffer_upload_memory_);
// Schedule uploading in the first submission.
builtin_index_buffer_upload_submission_ = UINT64_MAX;
@@ -219,7 +219,7 @@ void* VulkanPrimitiveProcessor::RequestHostConvertedIndexBufferForCurrentFrame(
(coalign_for_simd ? XE_GPU_PRIMITIVE_PROCESSOR_SIMD_SIZE : 0),
index_size, buffer, offset);
if (!mapping) {
return false;
return nullptr;
}
if (coalign_for_simd) {
ptrdiff_t coalignment_offset =

View File

@@ -41,7 +41,7 @@ bool VulkanRenderTargetCache::Initialize() {
void VulkanRenderTargetCache::Shutdown(bool from_destructor) {
const ui::vulkan::VulkanProvider& provider =
command_processor_.GetVulkanContext().GetVulkanProvider();
command_processor_.GetVulkanProvider();
const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider.dfn();
VkDevice device = provider.device();
@@ -65,7 +65,7 @@ void VulkanRenderTargetCache::Shutdown(bool from_destructor) {
void VulkanRenderTargetCache::ClearCache() {
const ui::vulkan::VulkanProvider& provider =
command_processor_.GetVulkanContext().GetVulkanProvider();
command_processor_.GetVulkanProvider();
const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider.dfn();
VkDevice device = provider.device();
@@ -385,7 +385,7 @@ VkRenderPass VulkanRenderTargetCache::GetRenderPass(RenderPassKey key) {
render_pass_create_info.pDependencies = subpass_dependencies;
const ui::vulkan::VulkanProvider& provider =
command_processor_.GetVulkanContext().GetVulkanProvider();
command_processor_.GetVulkanProvider();
const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider.dfn();
VkDevice device = provider.device();
VkRenderPass render_pass;
@@ -491,20 +491,20 @@ VulkanRenderTargetCache::VulkanRenderTarget::~VulkanRenderTarget() {
uint32_t VulkanRenderTargetCache::GetMaxRenderTargetWidth() const {
const ui::vulkan::VulkanProvider& provider =
command_processor_.GetVulkanContext().GetVulkanProvider();
command_processor_.GetVulkanProvider();
return provider.device_properties().limits.maxFramebufferWidth;
}
uint32_t VulkanRenderTargetCache::GetMaxRenderTargetHeight() const {
const ui::vulkan::VulkanProvider& provider =
command_processor_.GetVulkanContext().GetVulkanProvider();
command_processor_.GetVulkanProvider();
return provider.device_properties().limits.maxFramebufferHeight;
}
RenderTargetCache::RenderTarget* VulkanRenderTargetCache::CreateRenderTarget(
RenderTargetKey key) {
const ui::vulkan::VulkanProvider& provider =
command_processor_.GetVulkanContext().GetVulkanProvider();
command_processor_.GetVulkanProvider();
const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider.dfn();
VkDevice device = provider.device();
@@ -556,47 +556,11 @@ RenderTargetCache::RenderTarget* VulkanRenderTargetCache::CreateRenderTarget(
return nullptr;
}
VkImage image;
if (dfn.vkCreateImage(device, &image_create_info, nullptr, &image) !=
VK_SUCCESS) {
// TODO(Triang3l): Error message.
return nullptr;
}
// Allocate and bind the memory.
VkMemoryAllocateInfo memory_allocate_info;
VkMemoryRequirements memory_requirements;
dfn.vkGetImageMemoryRequirements(device, image, &memory_requirements);
if (!xe::bit_scan_forward(memory_requirements.memoryTypeBits &
provider.memory_types_device_local(),
&memory_allocate_info.memoryTypeIndex)) {
dfn.vkDestroyImage(device, image, nullptr);
return nullptr;
}
memory_allocate_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
VkMemoryDedicatedAllocateInfoKHR memory_dedicated_allocate_info;
if (provider.device_extensions().khr_dedicated_allocation) {
memory_dedicated_allocate_info.sType =
VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR;
memory_dedicated_allocate_info.pNext = nullptr;
memory_dedicated_allocate_info.image = image;
memory_dedicated_allocate_info.buffer = VK_NULL_HANDLE;
memory_allocate_info.pNext = &memory_dedicated_allocate_info;
} else {
memory_allocate_info.pNext = nullptr;
}
memory_allocate_info.allocationSize = memory_requirements.size;
VkDeviceMemory memory;
if (dfn.vkAllocateMemory(device, &memory_allocate_info, nullptr, &memory) !=
VK_SUCCESS) {
if (!ui::vulkan::util::CreateDedicatedAllocationImage(
provider, image_create_info,
ui::vulkan::util::MemoryPurpose::kDeviceLocal, image, memory)) {
// TODO(Triang3l): Error message.
dfn.vkDestroyImage(device, image, nullptr);
return nullptr;
}
if (dfn.vkBindImageMemory(device, image, memory, 0) != VK_SUCCESS) {
// TODO(Triang3l): Error message.
dfn.vkDestroyImage(device, image, nullptr);
dfn.vkFreeMemory(device, memory, nullptr);
return nullptr;
}
@@ -716,7 +680,7 @@ VulkanRenderTargetCache::GetFramebuffer(
}
const ui::vulkan::VulkanProvider& provider =
command_processor_.GetVulkanContext().GetVulkanProvider();
command_processor_.GetVulkanProvider();
const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider.dfn();
VkDevice device = provider.device();

View File

@@ -46,7 +46,7 @@ bool VulkanSharedMemory::Initialize() {
InitializeCommon();
const ui::vulkan::VulkanProvider& provider =
command_processor_.GetVulkanContext().GetVulkanProvider();
command_processor_.GetVulkanProvider();
const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider.dfn();
VkDevice device = provider.device();
const VkPhysicalDeviceFeatures& device_features = provider.device_features();
@@ -138,22 +138,26 @@ bool VulkanSharedMemory::Initialize() {
return false;
}
VkMemoryAllocateInfo buffer_memory_allocate_info;
VkMemoryAllocateInfo* buffer_memory_allocate_info_last =
&buffer_memory_allocate_info;
buffer_memory_allocate_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
buffer_memory_allocate_info.pNext = nullptr;
buffer_memory_allocate_info.allocationSize =
buffer_memory_requirements.size;
buffer_memory_allocate_info.memoryTypeIndex = buffer_memory_type_;
VkMemoryDedicatedAllocateInfoKHR buffer_memory_dedicated_allocate_info;
if (provider.device_extensions().khr_dedicated_allocation) {
buffer_memory_allocate_info_last->pNext =
&buffer_memory_dedicated_allocate_info;
buffer_memory_allocate_info_last =
reinterpret_cast<VkMemoryAllocateInfo*>(
&buffer_memory_dedicated_allocate_info);
buffer_memory_dedicated_allocate_info.sType =
VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR;
buffer_memory_dedicated_allocate_info.pNext = nullptr;
buffer_memory_dedicated_allocate_info.image = VK_NULL_HANDLE;
buffer_memory_dedicated_allocate_info.buffer = buffer_;
buffer_memory_allocate_info.pNext =
&buffer_memory_dedicated_allocate_info;
} else {
buffer_memory_allocate_info.pNext = nullptr;
}
buffer_memory_allocate_info.allocationSize =
buffer_memory_requirements.size;
buffer_memory_allocate_info.memoryTypeIndex = buffer_memory_type_;
VkDeviceMemory buffer_memory;
if (dfn.vkAllocateMemory(device, &buffer_memory_allocate_info, nullptr,
&buffer_memory) != VK_SUCCESS) {
@@ -190,7 +194,7 @@ void VulkanSharedMemory::Shutdown(bool from_destructor) {
last_usage_ = Usage::kTransferDestination;
const ui::vulkan::VulkanProvider& provider =
command_processor_.GetVulkanContext().GetVulkanProvider();
command_processor_.GetVulkanProvider();
const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider.dfn();
VkDevice device = provider.device();
@@ -258,7 +262,7 @@ bool VulkanSharedMemory::InitializeTraceSubmitDownloads() {
}
const ui::vulkan::VulkanProvider& provider =
command_processor_.GetVulkanContext().GetVulkanProvider();
command_processor_.GetVulkanProvider();
if (!ui::vulkan::util::CreateDedicatedAllocationBuffer(
provider, download_page_count << page_size_log2(),
VK_BUFFER_USAGE_TRANSFER_DST_BIT,
@@ -313,7 +317,7 @@ void VulkanSharedMemory::InitializeTraceCompleteDownloads() {
return;
}
const ui::vulkan::VulkanProvider& provider =
command_processor_.GetVulkanContext().GetVulkanProvider();
command_processor_.GetVulkanProvider();
const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider.dfn();
VkDevice device = provider.device();
void* download_mapping;
@@ -342,7 +346,7 @@ bool VulkanSharedMemory::AllocateSparseHostGpuMemoryRange(
}
const ui::vulkan::VulkanProvider& provider =
command_processor_.GetVulkanContext().GetVulkanProvider();
command_processor_.GetVulkanProvider();
const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider.dfn();
VkDevice device = provider.device();
@@ -474,7 +478,7 @@ void VulkanSharedMemory::GetBarrier(Usage usage,
VK_PIPELINE_STAGE_VERTEX_SHADER_BIT |
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
const ui::vulkan::VulkanProvider& provider =
command_processor_.GetVulkanContext().GetVulkanProvider();
command_processor_.GetVulkanProvider();
if (provider.device_features().tessellationShader) {
stage_mask |= VK_PIPELINE_STAGE_TESSELLATION_EVALUATION_SHADER_BIT;
}
@@ -495,7 +499,7 @@ void VulkanSharedMemory::GetBarrier(Usage usage,
void VulkanSharedMemory::ResetTraceDownload() {
const ui::vulkan::VulkanProvider& provider =
command_processor_.GetVulkanContext().GetVulkanProvider();
command_processor_.GetVulkanProvider();
const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider.dfn();
VkDevice device = provider.device();
ui::vulkan::util::DestroyAndNullHandle(dfn.vkDestroyBuffer, device,

View File

@@ -0,0 +1,63 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2022 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "xenia/base/console_app_main.h"
#include "xenia/base/logging.h"
#include "xenia/gpu/trace_dump.h"
#include "xenia/gpu/vulkan/vulkan_command_processor.h"
#include "xenia/gpu/vulkan/vulkan_graphics_system.h"
#include "xenia/ui/vulkan/vulkan_provider.h"
namespace xe {
namespace gpu {
namespace vulkan {
using namespace xe::gpu::xenos;
class VulkanTraceDump : public TraceDump {
public:
std::unique_ptr<gpu::GraphicsSystem> CreateGraphicsSystem() override {
return std::unique_ptr<gpu::GraphicsSystem>(new VulkanGraphicsSystem());
}
void BeginHostCapture() override {
const RENDERDOC_API_1_0_0* renderdoc_api =
static_cast<const ui::vulkan::VulkanProvider*>(
graphics_system_->provider())
->renderdoc_api()
.api_1_0_0();
if (renderdoc_api && !renderdoc_api->IsFrameCapturing()) {
renderdoc_api->StartFrameCapture(nullptr, nullptr);
}
}
void EndHostCapture() override {
const RENDERDOC_API_1_0_0* renderdoc_api =
static_cast<const ui::vulkan::VulkanProvider*>(
graphics_system_->provider())
->renderdoc_api()
.api_1_0_0();
if (renderdoc_api && renderdoc_api->IsFrameCapturing()) {
renderdoc_api->EndFrameCapture(nullptr, nullptr);
}
}
};
int trace_dump_main(const std::vector<std::string>& args) {
VulkanTraceDump trace_dump;
return trace_dump.Main(args);
}
} // namespace vulkan
} // namespace gpu
} // namespace xe
XE_DEFINE_CONSOLE_APP("xenia-gpu-vulkan-trace-dump",
xe::gpu::vulkan::trace_dump_main, "some.trace",
"target_trace_file");