[Vulkan] Shader memory export (#145)

This commit is contained in:
Triang3l
2024-05-25 16:00:21 +03:00
parent 210ac4b2d2
commit 3d30b2eec3
8 changed files with 1535 additions and 94 deletions

View File

@@ -2165,6 +2165,11 @@ bool VulkanCommandProcessor::IssueDraw(xenos::PrimitiveType prim_type,
return IssueCopy();
}
const ui::vulkan::VulkanProvider::DeviceInfo& device_info =
GetVulkanProvider().device_info();
memexport_ranges_.clear();
// Vertex shader analysis.
auto vertex_shader = static_cast<VulkanShader*>(active_vertex_shader());
if (!vertex_shader) {
@@ -2172,7 +2177,14 @@ bool VulkanCommandProcessor::IssueDraw(xenos::PrimitiveType prim_type,
return false;
}
pipeline_cache_->AnalyzeShaderUcode(*vertex_shader);
bool memexport_used_vertex = vertex_shader->memexport_eM_written() != 0;
// TODO(Triang3l): If the shader uses memory export, but
// vertexPipelineStoresAndAtomics is not supported, convert the vertex shader
// to a compute shader and dispatch it after the draw if the draw doesn't use
// tessellation.
if (vertex_shader->memexport_eM_written() != 0 &&
device_info.vertexPipelineStoresAndAtomics) {
draw_util::AddMemExportRanges(regs, *vertex_shader, memexport_ranges_);
}
// Pixel shader analysis.
bool primitive_polygonal = draw_util::IsPrimitivePolygonal(regs);
@@ -2195,12 +2207,15 @@ bool VulkanCommandProcessor::IssueDraw(xenos::PrimitiveType prim_type,
} else {
// Disabling pixel shader for this case is also required by the pipeline
// cache.
if (!memexport_used_vertex) {
if (memexport_ranges_.empty()) {
// This draw has no effect.
return true;
}
}
// TODO(Triang3l): Memory export.
if (pixel_shader && pixel_shader->memexport_eM_written() != 0 &&
device_info.fragmentStoresAndAtomics) {
draw_util::AddMemExportRanges(regs, *pixel_shader, memexport_ranges_);
}
uint32_t ps_param_gen_pos = UINT32_MAX;
uint32_t interpolator_mask =
@@ -2416,9 +2431,6 @@ bool VulkanCommandProcessor::IssueDraw(xenos::PrimitiveType prim_type,
current_guest_graphics_pipeline_layout_ = pipeline_layout;
}
const ui::vulkan::VulkanProvider::DeviceInfo& device_info =
GetVulkanProvider().device_info();
bool host_render_targets_used = render_target_cache_->GetPath() ==
RenderTargetCache::Path::kHostRenderTargets;
@@ -2520,9 +2532,39 @@ bool VulkanCommandProcessor::IssueDraw(xenos::PrimitiveType prim_type,
<< (vfetch_index & 63);
}
// Synchronize the memory pages backing memory scatter export streams, and
// calculate the range that includes the streams for the buffer barrier.
uint32_t memexport_extent_start = UINT32_MAX, memexport_extent_end = 0;
for (const draw_util::MemExportRange& memexport_range : memexport_ranges_) {
uint32_t memexport_range_base_bytes = memexport_range.base_address_dwords
<< 2;
if (!shared_memory_->RequestRange(memexport_range_base_bytes,
memexport_range.size_bytes)) {
XELOGE(
"Failed to request memexport stream at 0x{:08X} (size {}) in the "
"shared memory",
memexport_range_base_bytes, memexport_range.size_bytes);
return false;
}
memexport_extent_start =
std::min(memexport_extent_start, memexport_range_base_bytes);
memexport_extent_end =
std::max(memexport_extent_end,
memexport_range_base_bytes + memexport_range.size_bytes);
}
// Insert the shared memory barrier if needed.
// TODO(Triang3l): Memory export.
shared_memory_->Use(VulkanSharedMemory::Usage::kRead);
// TODO(Triang3l): Find some PM4 command that can be used for indication of
// when memexports should be awaited instead of inserting the barrier in Use
// every time if memory export was done in the previous draw?
if (memexport_extent_start < memexport_extent_end) {
shared_memory_->Use(
VulkanSharedMemory::Usage::kGuestDrawReadWrite,
std::make_pair(memexport_extent_start,
memexport_extent_end - memexport_extent_start));
} else {
shared_memory_->Use(VulkanSharedMemory::Usage::kRead);
}
// After all commands that may dispatch, copy or insert barriers, submit the
// barriers (may end the render pass), and (re)enter the render pass before
@@ -2567,6 +2609,12 @@ bool VulkanCommandProcessor::IssueDraw(xenos::PrimitiveType prim_type,
primitive_processing_result.host_draw_vertex_count, 1, 0, 0, 0);
}
// Invalidate textures in memexported memory and watch for changes.
for (const draw_util::MemExportRange& memexport_range : memexport_ranges_) {
shared_memory_->RangeWrittenByGpu(memexport_range.base_address_dwords << 2,
memexport_range.size_bytes, false);
}
return true;
}