graphics: synchronize host file writes and depth clipping

Map paired PS5 depth-clip state through Vulkan pipeline creation and preserve asymmetric hard failures. Prepare GPU-mapped guest ranges before kernel file reads so buffer, texture, and alias ownership remains coherent. Add focused regression coverage and update the current launch target.
This commit is contained in:
nmzik
2026-07-19 14:33:38 +02:00
parent 91690ac29e
commit aebf6b4913
15 changed files with 184 additions and 6 deletions
+38
View File
@@ -316,6 +316,26 @@ bool PageManager::IsMapped(uint64_t vaddr, uint64_t size) const noexcept {
return true;
}
bool PageManager::HasAnyMapping(uint64_t vaddr, uint64_t size) const noexcept {
if (g_in_fault_resolution || vaddr == 0 || size == 0 || vaddr >= ADDRESS_SIZE ||
size > ADDRESS_SIZE - vaddr) {
return false;
}
const auto end = PageEnd(vaddr, size);
for (auto page_vaddr = PageStart(vaddr); page_vaddr < end; page_vaddr += PAGE_SIZE) {
auto* region = m_impl->FindRegion(page_vaddr);
if (region == nullptr) {
continue;
}
auto& page = m_impl->GetPage(*region, page_vaddr);
SpinGuard lock(page.lock);
if (page.mappings != 0) {
return true;
}
}
return false;
}
bool PageManager::HasGpuAccess(uint64_t vaddr, uint64_t size, GpuAccess access) const noexcept {
if (access != GpuAccess::Read && access != GpuAccess::Write && access != GpuAccess::ReadWrite) {
FailFast("HasGpuAccess received an invalid GPU access mode");
@@ -644,4 +664,22 @@ bool PageManager::HandleFault(PageFaultAccess access, uint64_t fault_vaddr) noex
return true;
}
bool PageManager::HandleWriteRange(uint64_t vaddr, uint64_t size) noexcept {
if (g_in_fault_resolution || vaddr == 0 || size == 0 || vaddr >= ADDRESS_SIZE ||
size > ADDRESS_SIZE - vaddr) {
return false;
}
const auto end = PageEnd(vaddr, size);
for (auto page_vaddr = PageStart(vaddr); page_vaddr < end; page_vaddr += PAGE_SIZE) {
if (!IsMapped(page_vaddr, 1)) {
continue;
}
const auto fault_vaddr = std::max(page_vaddr, vaddr);
if (!HandleFault(PageFaultAccess::Write, fault_vaddr)) {
return false;
}
}
return true;
}
} // namespace Libs::Graphics
+2
View File
@@ -38,6 +38,7 @@ public:
[[nodiscard]] uint64_t GetPageSize() const;
[[nodiscard]] bool IsTracked(uint64_t vaddr) const noexcept;
[[nodiscard]] bool IsMapped(uint64_t vaddr, uint64_t size) const noexcept;
[[nodiscard]] bool HasAnyMapping(uint64_t vaddr, uint64_t size) const noexcept;
[[nodiscard]] bool HasGpuAccess(uint64_t vaddr, uint64_t size, GpuAccess access) const noexcept;
void UpdatePageWatchers(bool track, uint64_t vaddr, uint64_t size,
@@ -46,6 +47,7 @@ public:
void OnGpuUnmap(uint64_t vaddr, uint64_t size, GpuAccess access = GpuAccess::ReadWrite);
[[nodiscard]] bool HandleFault(PageFaultAccess access, uint64_t fault_vaddr) noexcept;
[[nodiscard]] bool HandleWriteRange(uint64_t vaddr, uint64_t size) noexcept;
private:
void BeginBackingWrite(uint64_t vaddr, uint64_t size) noexcept;
+1 -1
View File
@@ -687,7 +687,7 @@ static void ClipCheck(const HW::ClipControl& c) {
// dx_linear_attr_clip_enable preserves linear (noperspective) attributes at clip-generated
// vertices, which Vulkan provides as part of clipping and interpolation.
EXIT_NOT_IMPLEMENTED(c.user_clip_planes != 0 || c.user_clip_plane_mode != 0 ||
c.vertex_kill_any || c.min_z_clip_disable || c.max_z_clip_disable ||
c.vertex_kill_any || !c.IsZClipModeRepresentable() ||
c.user_clip_plane_negate_y || c.clip_disable ||
c.user_clip_plane_cull_only || c.cull_on_clipping_error_disable ||
c.force_viewport_index_from_vs_enable);
@@ -61,6 +61,41 @@ bool GpuResourceManager::HandleFault(PageFaultAccess access, uint64_t fault_vadd
return m_page_manager.HandleFault(access, fault_vaddr);
}
void GpuResourceManager::PrepareHostWrite(uint64_t vaddr, uint64_t size) {
if (!m_page_manager.HasAnyMapping(vaddr, size)) {
return;
}
if (LabelInCallback()) {
EXIT("unsupported host write from an asynchronous GPU label callback, addr=0x%016" PRIx64
" size=0x%016" PRIx64 "\n",
vaddr, size);
}
const auto handle_range = [this, vaddr, size]() {
if (!m_page_manager.HandleWriteRange(vaddr, size)) {
EXIT("failed to prepare host write, addr=0x%016" PRIx64 " size=0x%016" PRIx64
"\n",
vaddr, size);
}
};
if (auto* cp = GraphicsRunCurrentCommandProcessor(); cp != nullptr) {
cp->BeginReadbackTransaction();
{
ResourceMutex::FaultScope fault(m_resource_mutex);
handle_range();
}
cp->EndReadbackTransaction();
return;
}
if (m_resource_mutex.IsOwnedByCurrentThread()) {
EXIT("unsupported host write from a pre-owned resource transaction, addr=0x%016" PRIx64
" size=0x%016" PRIx64 "\n",
vaddr, size);
}
GraphicsRunSubmissionLock submissions;
ResourceMutex::FaultScope fault(m_resource_mutex);
handle_range();
}
bool GpuResourceManager::IsMapped(uint64_t vaddr, uint64_t size) const noexcept {
return m_page_manager.IsMapped(vaddr, size);
}
@@ -24,6 +24,7 @@ public:
[[nodiscard]] TextureCache* GetTextureCache() { return &m_texture_cache; }
[[nodiscard]] bool HandleFault(PageFaultAccess access, uint64_t fault_vaddr) noexcept;
void PrepareHostWrite(uint64_t vaddr, uint64_t size);
[[nodiscard]] bool IsMapped(uint64_t vaddr, uint64_t size) const noexcept;
void MapMemory(uint64_t vaddr, uint64_t size, GpuAccess access);
void UnmapMemory(uint64_t vaddr, uint64_t size, GpuAccess access);
@@ -88,7 +88,10 @@ PipelineCache::GraphicsPipeline* PipelineCache::CreateGraphicsPipeline(
}
}
static_params.negative_one_to_one = !ctx->GetClipControl().dx_clip_space;
const auto& clip_control = ctx->GetClipControl();
EXIT_NOT_IMPLEMENTED(!clip_control.IsZClipModeRepresentable());
static_params.negative_one_to_one = !clip_control.dx_clip_space;
static_params.depth_clip_enable = clip_control.IsZClipEnabled();
static_params.topology = topology;
static_params.samples = framebuffer->samples;
static_params.sample_shading_enable =
@@ -33,6 +33,7 @@ struct PipelineStaticParameters {
float viewport_scale[3] = {};
float viewport_offset[3] = {};
bool negative_one_to_one = false;
bool depth_clip_enable = true;
int scissor_ltrb[4] = {0};
vk::PrimitiveTopology topology = vk::PrimitiveTopology::ePointList;
uint32_t samples = 1;
@@ -75,7 +76,7 @@ static_assert(std::is_trivially_copyable_v<PipelineStaticParameters>);
static_assert(std::is_standard_layout_v<PipelineStaticParameters>);
static_assert(alignof(PipelineStaticParameters) == 1);
static_assert(sizeof(PipelineStaticParameters) ==
sizeof(float[3]) + sizeof(float[3]) + sizeof(bool) + sizeof(int[4]) +
sizeof(float[3]) + sizeof(float[3]) + sizeof(bool) * 2 + sizeof(int[4]) +
sizeof(vk::PrimitiveTopology) + sizeof(uint32_t) + sizeof(bool) * 4 +
sizeof(vk::CompareOp) + sizeof(bool) + sizeof(float) * 2 + sizeof(bool) +
sizeof(PipelineStencilStaticState) * 2 + sizeof(uint32_t) +
+1 -1
View File
@@ -734,7 +734,7 @@ void CreatePipelineInternal(PipelineCache::GraphicsPipeline* pipeline, vk::Rende
clip_ext.sType = vk::StructureType::ePipelineRasterizationDepthClipStateCreateInfoEXT;
clip_ext.pNext = nullptr;
clip_ext.flags = {};
clip_ext.depthClipEnable = VK_FALSE;
clip_ext.depthClipEnable = static_params.depth_clip_enable ? VK_TRUE : VK_FALSE;
vk::PipelineRasterizationStateCreateInfo rasterizer {};
rasterizer.sType = vk::StructureType::ePipelineRasterizationStateCreateInfo;