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-21 07:37:52 +02:00
parent 91690ac29e
commit aebf6b4913
15 changed files with 184 additions and 6 deletions
+5
View File
@@ -253,6 +253,11 @@ struct ClipControl {
bool cull_on_clipping_error_disable = false;
bool linear_attribute_clip_enable = false;
bool force_viewport_index_from_vs_enable = false;
[[nodiscard]] bool IsZClipModeRepresentable() const {
return min_z_clip_disable == max_z_clip_disable;
}
[[nodiscard]] bool IsZClipEnabled() const { return !min_z_clip_disable; }
};
struct DepthControl {
+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;
@@ -289,9 +289,13 @@ static void VulkanFindPhysicalDevice(vk::Instance instance, vk::SurfaceKHR surfa
color_write_ext.sType = vk::StructureType::ePhysicalDeviceColorWriteEnableFeaturesEXT;
color_write_ext.pNext = nullptr;
vk::PhysicalDeviceDepthClipEnableFeaturesEXT depth_clip_enable {};
depth_clip_enable.sType = vk::StructureType::ePhysicalDeviceDepthClipEnableFeaturesEXT;
depth_clip_enable.pNext = &color_write_ext;
vk::PhysicalDeviceDepthClipControlFeaturesEXT depth_clip_control {};
depth_clip_control.sType = vk::StructureType::ePhysicalDeviceDepthClipControlFeaturesEXT;
depth_clip_control.pNext = &color_write_ext;
depth_clip_control.pNext = &depth_clip_enable;
vk::PhysicalDeviceVulkan12Features features12 {};
features12.sType = vk::StructureType::ePhysicalDeviceVulkan12Features;
@@ -325,6 +329,10 @@ static void VulkanFindPhysicalDevice(vk::Instance instance, vk::SurfaceKHR surfa
LOGF("depthClipControl is not supported\n");
skip_device = true;
}
if (depth_clip_enable.depthClipEnable != VK_TRUE) {
LOGF("depthClipEnable is not supported\n");
skip_device = true;
}
if (features12.samplerMirrorClampToEdge != VK_TRUE) {
LOGF("samplerMirrorClampToEdge is not supported\n");
@@ -590,9 +598,14 @@ static vk::Device VulkanCreateDevice(vk::PhysicalDevice physical_device, vk::Sur
color_write_ext.pNext = nullptr;
color_write_ext.colorWriteEnable = VK_TRUE;
vk::PhysicalDeviceDepthClipEnableFeaturesEXT depth_clip_enable {};
depth_clip_enable.sType = vk::StructureType::ePhysicalDeviceDepthClipEnableFeaturesEXT;
depth_clip_enable.pNext = &color_write_ext;
depth_clip_enable.depthClipEnable = VK_TRUE;
vk::PhysicalDeviceDepthClipControlFeaturesEXT depth_clip_control {};
depth_clip_control.sType = vk::StructureType::ePhysicalDeviceDepthClipControlFeaturesEXT;
depth_clip_control.pNext = &color_write_ext;
depth_clip_control.pNext = &depth_clip_enable;
depth_clip_control.depthClipControl = VK_TRUE;
vk::PhysicalDeviceVulkan12Features features12 {};
+12
View File
@@ -9,6 +9,7 @@
#include "common/logging/log.h"
#include "common/stringUtils.h"
#include "common/threads.h"
#include "kernel/memory.h"
#include "libs/errno.h"
#include "libs/libs.h"
#include "libs/network.h"
@@ -502,6 +503,11 @@ int64_t KYTY_SYSV_ABI KernelRead(int d, void* buf, size_t nbytes) {
file->mutex.Lock();
bool is_invalid = file->f.IsInvalid();
const auto pos = file->f.Tell();
const auto file_size = file->f.Size();
const auto remaining = pos < file_size ? file_size - pos : 0;
Memory::PrepareHostWrite(reinterpret_cast<uint64_t>(buf),
std::min<uint64_t>(nbytes, remaining));
uint32_t bytes_read = 0;
file->f.Read(buf, static_cast<uint32_t>(nbytes), &bytes_read);
@@ -622,6 +628,12 @@ int64_t KYTY_SYSV_ABI KernelPread(int d, void* buf, size_t nbytes, int64_t offse
bool is_invalid = file->f.IsInvalid();
auto pos = file->f.Tell();
const auto file_size = file->f.Size();
const auto remaining = static_cast<uint64_t>(offset) < file_size
? file_size - static_cast<uint64_t>(offset)
: 0;
Memory::PrepareHostWrite(reinterpret_cast<uint64_t>(buf),
std::min<uint64_t>(nbytes, remaining));
uint32_t bytes_read = 0;
file->f.Seek(offset);
file->f.Read(buf, static_cast<uint32_t>(nbytes), &bytes_read);
+7
View File
@@ -882,6 +882,13 @@ void WriteBacking(uint64_t vaddr, const void* data, uint64_t size) noexcept {
}
}
void PrepareHostWrite(uint64_t vaddr, uint64_t size) {
if (size == 0 || Graphics::g_render_ctx == nullptr) {
return;
}
Graphics::g_render_ctx->GetGpuResources()->PrepareHostWrite(vaddr, size);
}
struct PrtAperture {
uint64_t address = 0;
uint64_t size = 0;
+1
View File
@@ -99,6 +99,7 @@ void SetFlexibleMemorySize(uint64_t size);
bool TryWriteBacking(uint64_t vaddr, const void* data, uint64_t size);
bool TryReadBacking(uint64_t vaddr, void* data, uint64_t size);
void WriteBacking(uint64_t vaddr, const void* data, uint64_t size) noexcept;
void PrepareHostWrite(uint64_t vaddr, uint64_t size);
int KYTY_SYSV_ABI KernelMapNamedFlexibleMemory(void** addr_in_out, size_t len, int prot, int flags,
const char* name);
+31
View File
@@ -168,6 +168,36 @@ void TestSharedWatcherFault() {
Check(VirtualFree(memory, 0, MEM_RELEASE) != 0, "VirtualFree failed");
}
void TestMappedHostWriteRange() {
FaultContext context;
PageManager manager(InvalidateFault, &context);
context.manager = &manager;
const auto page_size = manager.GetPageSize();
auto *memory = Allocate(page_size * 3);
const auto address = reinterpret_cast<uint64_t>(memory);
manager.OnGpuMap(address, page_size);
manager.OnGpuMap(address + page_size * 2, page_size);
manager.UpdatePageWatchers(true, address, page_size);
manager.UpdatePageWatchers(true, address + page_size * 2, page_size);
Check(manager.HasAnyMapping(address + 16, page_size * 3 - 32),
"host-write range did not find partial GPU mappings");
Check(!manager.IsMapped(address, page_size * 3),
"partial GPU mappings were reported as a full mapping");
Check(manager.HandleWriteRange(address + 16, page_size * 3 - 32),
"mapped host-write range was not handled");
Check(context.calls.load(std::memory_order_relaxed) == 2,
"host-write range did not invalidate each mapped watched page");
Check(IsWritable(memory) && IsWritable(memory + page_size * 2),
"host-write range did not restore writable protection");
manager.OnGpuUnmap(address, page_size);
manager.OnGpuUnmap(address + page_size * 2, page_size);
Check(!manager.HasAnyMapping(address, page_size * 3),
"host-write range retained stale GPU mappings");
Check(VirtualFree(memory, 0, MEM_RELEASE) != 0, "VirtualFree failed");
}
void TestReadWriteWatcherFault() {
FaultContext context;
PageManager manager(InvalidateFault, &context);
@@ -607,6 +637,7 @@ int main(int argc, char **argv) {
}
TestWatchFaultAndUnwatch();
TestSharedWatcherFault();
TestMappedHostWriteRange();
TestReadWriteWatcherFault();
TestPermittedMappedLateFaultsResume();
TestPartialMappingUnmapPreservesTokens();
+29
View File
@@ -13869,6 +13869,30 @@ void CheckReferenceClockScale() {
std::printf("[host] %-32s ok\n", "ReferenceClockScale");
}
void CheckClipControlDepthClipState() {
HW::ClipControl clip;
Require("ClipControlDepthClipState", "default",
clip.IsZClipModeRepresentable() && clip.IsZClipEnabled(),
"default paired Z clipping was not enabled");
clip.min_z_clip_disable = true;
Require("ClipControlDepthClipState", "asymmetric near",
!clip.IsZClipModeRepresentable(),
"asymmetric near-plane state was accepted");
clip.min_z_clip_disable = false;
clip.max_z_clip_disable = true;
Require("ClipControlDepthClipState", "asymmetric far",
!clip.IsZClipModeRepresentable(),
"asymmetric far-plane state was accepted");
clip.min_z_clip_disable = true;
Require("ClipControlDepthClipState", "both disabled",
clip.IsZClipModeRepresentable() && !clip.IsZClipEnabled(),
"paired Z-clip disable was not represented");
std::printf("[host] %-32s ok\n", "ClipControlDepthClipState");
}
} // namespace
} // namespace Libs::Graphics
@@ -13877,6 +13901,10 @@ int main(int argc, char **argv) {
EnsureConfigInitialized();
TileInit();
if (argc == 2 && std::strcmp(argv[1], "--clip-control-only") == 0) {
CheckClipControlDepthClipState();
return 0;
}
if (argc == 2 && std::strcmp(argv[1], "--reference-clock-only") == 0) {
CheckReferenceClockScale();
return 0;
@@ -14021,6 +14049,7 @@ int main(int argc, char **argv) {
(void)argc;
(void)argv;
#endif
CheckClipControlDepthClipState();
CheckReferenceClockScale();
CheckEmbeddedFetchVertexOffset();
CheckEmbeddedFetchLaneSpill();