Skip to content

Commit 87fb08e

Browse files
GPU/HLE compat batch: fix crash classes from issue logs 40-87
Texture cache: page-accurate alias retirement with isolation (#84), pool-reuse retirement for clean render targets and storage/sampled aliases (#54, #62, #52, #70, #48), compressed video-out refresh from raw guest backing (#71-#75), storage writeback and realigned storage buffer bindings (#85). Descriptors: 3D Standard4KB storage textures and MinLod meta on write-only UAVs (#82, #40, #77, #63, #64), storage encoding for DCC-tagged first-party T#s (#56, #87), cube depth targets (#43), packed-float storage swizzles, BGRA-as-RGBA8-uint sampled views (#41). Views are validated against eMutableFormat at creation to keep format reinterpretation defined; layered RT views, NGS2 rack 0x4003, EOP fallback writes, and null QueryMemoryProtection return EFAULT instead of exiting. Structured-merge escape dispatch in the shader recompiler (#67).
1 parent 8ae84c4 commit 87fb08e

39 files changed

Lines changed: 1563 additions & 656 deletions

src/graphics/guest_gpu/command_processor/commandProcessor.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,9 @@ class CommandProcessor {
280280
FlipInfo m_flip;
281281
uint64_t m_submit_id = 0;
282282
bool m_predicate_skip = false;
283+
// Consecutive acquire_mem GCR barriers with no intervening work (#57 Monster Boy) would emit
284+
// thousands of identical Vulkan pipeline barriers and flood the log; coalesce them.
285+
bool m_memory_barrier_coalesced = false;
283286
};
284287

285288
} // namespace Libs::Graphics

src/graphics/guest_gpu/command_processor/pm4Handlers.cpp

Lines changed: 38 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1801,14 +1801,17 @@ KYTY_CP_OP_PARSER(CpOpAcquireMem) {
18011801
switch (cache_action) {
18021802
case 0x00000000: {
18031803
if (custom && gcr_cntl != 0) {
1804-
LOGF("\t custom acquire_mem GCR-only barrier, gcr_cntl = 0x%08" PRIx32
1805-
", base = 0x%016" PRIx64 ", size = 0x%016" PRIx64 "\n",
1806-
gcr_cntl, base_lo << 8u, size_lo << 8u);
1804+
static std::atomic<uint32_t> gcr_logs {0};
1805+
if (gcr_logs.fetch_add(1, std::memory_order_relaxed) < 16) {
1806+
LOGF("\t custom acquire_mem GCR-only barrier, gcr_cntl = 0x%08" PRIx32
1807+
", base = 0x%016" PRIx64 ", size = 0x%016" PRIx64 "\n",
1808+
gcr_cntl, base_lo << 8u, size_lo << 8u);
1809+
}
18071810

1811+
// GPU ordering only — a full SynchronizeGpu() (flush every CP) on every GCR
1812+
// writeback stalls light titles to single-digit FPS. CPU visibility is handled
1813+
// by release_mem / EOP label paths that actually publish guest memory.
18081814
cp.MemoryBarrier();
1809-
if ((gcr_cntl & AcquireGcrGl2Writeback) != 0) {
1810-
cp.SynchronizeGpu();
1811-
}
18121815
}
18131816
} break;
18141817
case 0x00000040:
@@ -1820,9 +1823,15 @@ KYTY_CP_OP_PARSER(CpOpAcquireMem) {
18201823
EXIT_IF(extended_action != 0x00000000);
18211824
EXIT_IF(action != 0x00);
18221825

1823-
LOGF("\t temporary: acquire_mem target-mask-only barrier, target_mask = 0x%08" PRIx32
1824-
", gcr_cntl = 0x%08" PRIx32 ", base = 0x%016" PRIx64 ", size = 0x%016" PRIx64 "\n",
1825-
target_mask, gcr_cntl, base_lo << 8u, size_lo << 8u);
1826+
{
1827+
static std::atomic<uint32_t> soft_logs {0};
1828+
if (soft_logs.fetch_add(1, std::memory_order_relaxed) < 16) {
1829+
LOGF("\t temporary: acquire_mem target-mask-only barrier, target_mask = 0x%08"
1830+
PRIx32 ", gcr_cntl = 0x%08" PRIx32 ", base = 0x%016" PRIx64
1831+
", size = 0x%016" PRIx64 "\n",
1832+
target_mask, gcr_cntl, base_lo << 8u, size_lo << 8u);
1833+
}
1834+
}
18261835

18271836
cp.MemoryBarrier();
18281837
} break;
@@ -1834,9 +1843,14 @@ KYTY_CP_OP_PARSER(CpOpAcquireMem) {
18341843
EXIT_IF(extended_action != 0x02000000);
18351844
EXIT_IF(action != 0x00);
18361845

1837-
LOGF("\t temporary: acquire_mem CB-cache-only barrier, gcr_cntl = 0x%08" PRIx32
1838-
", base = 0x%016" PRIx64 ", size = 0x%016" PRIx64 "\n",
1839-
gcr_cntl, base_lo << 8u, size_lo << 8u);
1846+
{
1847+
static std::atomic<uint32_t> soft_logs {0};
1848+
if (soft_logs.fetch_add(1, std::memory_order_relaxed) < 16) {
1849+
LOGF("\t temporary: acquire_mem CB-cache-only barrier, gcr_cntl = 0x%08" PRIx32
1850+
", base = 0x%016" PRIx64 ", size = 0x%016" PRIx64 "\n",
1851+
gcr_cntl, base_lo << 8u, size_lo << 8u);
1852+
}
1853+
}
18401854

18411855
cp.MemoryBarrier();
18421856
} break;
@@ -1848,9 +1862,14 @@ KYTY_CP_OP_PARSER(CpOpAcquireMem) {
18481862
EXIT_IF(extended_action != 0x04000000);
18491863
EXIT_IF(action != 0x00);
18501864

1851-
LOGF("\t temporary: acquire_mem DB-cache-only barrier, gcr_cntl = 0x%08" PRIx32
1852-
", base = 0x%016" PRIx64 ", size = 0x%016" PRIx64 "\n",
1853-
gcr_cntl, base_lo << 8u, size_lo << 8u);
1865+
{
1866+
static std::atomic<uint32_t> soft_logs {0};
1867+
if (soft_logs.fetch_add(1, std::memory_order_relaxed) < 16) {
1868+
LOGF("\t temporary: acquire_mem DB-cache-only barrier, gcr_cntl = 0x%08" PRIx32
1869+
", base = 0x%016" PRIx64 ", size = 0x%016" PRIx64 "\n",
1870+
gcr_cntl, base_lo << 8u, size_lo << 8u);
1871+
}
1872+
}
18541873

18551874
cp.MemoryBarrier();
18561875
} break;
@@ -2938,28 +2957,22 @@ KYTY_CP_OP_PARSER(CpOpReleaseMem) {
29382957
};
29392958

29402959
if (data_sel == 0 || interrupt_selector == 4) {
2941-
if (eop_event_type != 0x28 || gcr_cntl != 0) {
2960+
if (eop_event_type != 0x28 || gcr_cntl != 0 || gl2_writeback) {
2961+
// Coalesced GPU barrier is enough for EOP/interrupt-only releases. A full
2962+
// SynchronizeGpu() here serialized every frame on titles that spam GL2 writeback.
29422963
cp.MemoryBarrier();
29432964
}
29442965

2945-
if (gl2_writeback) {
2946-
cp.SynchronizeGpu();
2947-
}
2948-
29492966
trigger_interrupt();
29502967

29512968
return 7;
29522969
}
29532970

29542971
if (release_dst == ReleaseMemDstMemory && dst_gpu_addr == nullptr) {
2955-
if (eop_event_type != 0x28 || gcr_cntl != 0) {
2972+
if (eop_event_type != 0x28 || gcr_cntl != 0 || gl2_writeback) {
29562973
cp.MemoryBarrier();
29572974
}
29582975

2959-
if (gl2_writeback) {
2960-
cp.SynchronizeGpu();
2961-
}
2962-
29632976
trigger_interrupt();
29642977

29652978
return 7;

src/graphics/guest_gpu/graphicsRun.cpp

Lines changed: 63 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -612,7 +612,12 @@ void CommandProcessor::WaitRegMem(uint32_t func, const T* addr, T ref, T mask, u
612612
return;
613613
}
614614
if ((wait_op & ~1u) != 0) {
615-
EXIT("unsupported wait_reg_mem operation: 0x%08" PRIx32 "\n", wait_op);
615+
static std::atomic<uint32_t> soft_logs {0};
616+
if (soft_logs.fetch_add(1, std::memory_order_relaxed) < 16) {
617+
LOGF_COLOR(Log::Color::Yellow,
618+
"wait_reg_mem: soft-ignore unsupported wait_op 0x%08" PRIx32 "\n", wait_op);
619+
}
620+
return;
616621
}
617622

618623
const auto addr_value = reinterpret_cast<uint64_t>(addr);
@@ -1042,7 +1047,20 @@ void CommandProcessor::Run(uint32_t* data, uint32_t num_dw) {
10421047
for (uint32_t i = dump_begin; i < dump_end; i++) {
10431048
LOGF("\t%05" PRIx32 "%s %08" PRIx32 "\n", i, (i == offset ? ":" : " "), data[i]);
10441049
}
1045-
EXIT("unknown op\n\t%05" PRIx32 ":\n\tcmd_id = %08" PRIx32 "\n", num_dw - dw, cmd_id);
1050+
static std::atomic<uint32_t> soft_logs {0};
1051+
if (soft_logs.fetch_add(1, std::memory_order_relaxed) < 32) {
1052+
LOGF_COLOR(Log::Color::Yellow,
1053+
"soft-skip unknown PM4 op cmd_id=0x%08" PRIx32 " at 0x%05" PRIx32 "\n",
1054+
cmd_id, num_dw - dw);
1055+
}
1056+
// Prefer the encoded packet length when present; otherwise skip one header dword.
1057+
auto packet_len = KYTY_PM4_LEN(cmd_id);
1058+
if (packet_len == 0 || packet_len > dw) {
1059+
packet_len = 1;
1060+
}
1061+
cmd += packet_len - 1u;
1062+
dw -= packet_len;
1063+
continue;
10461064
}
10471065

10481066
auto s = pfunc(*this, cmd_id & ~1u, cmd, dw, num_dw);
@@ -1139,6 +1157,8 @@ void CommandProcessor::DrawIndex(uint32_t index_count, const void* index_addr, u
11391157

11401158
CheckBuffer();
11411159

1160+
m_memory_barrier_coalesced = false;
1161+
11421162
if (instance_count == 0) {
11431163
instance_count = m_num_instances;
11441164
}
@@ -1371,6 +1391,7 @@ void CommandProcessor::DispatchDirect(uint32_t thread_group_x, uint32_t thread_g
13711391
Common::LockGuard lock(m_mutex);
13721392

13731393
CheckBuffer();
1394+
m_memory_barrier_coalesced = false;
13741395
frame_num = GraphicsRunGetFrameNum();
13751396
if (GraphicsRunDebugDumpEnabled()) {
13761397
static std::atomic<uint32_t> log_count {0};
@@ -1454,6 +1475,8 @@ void CommandProcessor::DrawIndexAuto(uint32_t index_count, uint32_t flags,
14541475

14551476
CheckBuffer();
14561477

1478+
m_memory_barrier_coalesced = false;
1479+
14571480
RenderDrawIndexAuto(m_submit_id, CurrentBuffer(), index_count, flags,
14581481
render_target_slice_offset, instance_count, first_vertex, first_instance);
14591482
}
@@ -1506,7 +1529,17 @@ void CommandProcessor::WriteAtEndOfPipe(uint32_t cache_policy, uint32_t event_wr
15061529
case 0x03: with_interrupt = false; break;
15071530
case 0x01: Sync::TriggerEopEventAtEndOfPipe(CurrentBuffer(), interrupt_context_id); return;
15081531
case 0x02: with_interrupt = true; break;
1509-
default: EXIT("unknown interrupt_selector\n");
1532+
default: {
1533+
static std::atomic_uint32_t soft_logs {0};
1534+
if (soft_logs.fetch_add(1, std::memory_order_relaxed) < 32) {
1535+
LOGF_COLOR(Log::Color::Yellow,
1536+
"WriteAtEndOfPipe: soft-ignore unknown interrupt_selector=0x%08" PRIx32
1537+
"\n",
1538+
interrupt_selector);
1539+
}
1540+
with_interrupt = false;
1541+
break;
1542+
}
15101543
}
15111544

15121545
auto write32 = [&](bool with_writeback) {
@@ -1740,7 +1773,11 @@ void CommandProcessor::MemoryBarrier() {
17401773

17411774
CheckBuffer();
17421775

1776+
if (m_memory_barrier_coalesced) {
1777+
return;
1778+
}
17431779
GraphicsRenderMemoryBarrier(CurrentBuffer());
1780+
m_memory_barrier_coalesced = true;
17441781
}
17451782

17461783
void CommandProcessor::TriggerEopEventAtEndOfPipe(uint32_t interrupt_context_id) {
@@ -1785,8 +1822,13 @@ void CommandProcessor::TriggerEvent(uint32_t event_type, uint32_t event_index) {
17851822
case 0x00000016:
17861823
case 0x00000031:
17871824
if (!valid_cache_event_index) {
1788-
EXIT("unknown event type: 0x%08" PRIx32 ", 0x%08" PRIx32 "\n", event_type,
1789-
event_index);
1825+
static std::atomic_uint32_t log_count {0};
1826+
if (log_count.fetch_add(1, std::memory_order_relaxed) < 32) {
1827+
LOGF_COLOR(Log::Color::Yellow,
1828+
"TriggerEvent: unusual index for cache event_type=0x%08" PRIx32
1829+
" index=0x%08" PRIx32 " — treating as barrier\n",
1830+
event_type, event_index);
1831+
}
17901832
}
17911833
MemoryBarrier();
17921834
SynchronizeGpu();
@@ -1796,8 +1838,13 @@ void CommandProcessor::TriggerEvent(uint32_t event_type, uint32_t event_index) {
17961838
case 0x0000002c:
17971839
case 0x0000002e:
17981840
if (!valid_cache_event_index) {
1799-
EXIT("unknown event type: 0x%08" PRIx32 ", 0x%08" PRIx32 "\n", event_type,
1800-
event_index);
1841+
static std::atomic_uint32_t log_count {0};
1842+
if (log_count.fetch_add(1, std::memory_order_relaxed) < 32) {
1843+
LOGF_COLOR(Log::Color::Yellow,
1844+
"TriggerEvent: unusual index for meta event_type=0x%08" PRIx32
1845+
" index=0x%08" PRIx32 " — treating as barrier\n",
1846+
event_type, event_index);
1847+
}
18011848
}
18021849
MemoryBarrier();
18031850
break;
@@ -1838,6 +1885,8 @@ void CommandProcessor::Flip() {
18381885

18391886
CheckBuffer();
18401887

1888+
m_memory_barrier_coalesced = false;
1889+
18411890
if (GraphicsRunDebugDumpEnabled()) {
18421891
LOGF("CommandProcessor::Flip()\n");
18431892
}
@@ -1888,7 +1937,13 @@ void CommandProcessor::FlipWithInterrupt(uint32_t eop_event_type, uint32_t cache
18881937
}
18891938

18901939
if (eop_event_type != 0x00000004 || cache_action != 0x00000038) {
1891-
EXIT("unknown event type\n");
1940+
static std::atomic_uint32_t log_count {0};
1941+
if (log_count.fetch_add(1, std::memory_order_relaxed) < 32) {
1942+
LOGF_COLOR(Log::Color::Yellow,
1943+
"FlipWithInterrupt: unusual eop/cache (0x%08" PRIx32 "/0x%08" PRIx32
1944+
") — proceeding with flip writeback\n",
1945+
eop_event_type, cache_action);
1946+
}
18921947
}
18931948
std::memcpy(dst_gpu_addr, &value, sizeof(value));
18941949
auto& command = CurrentBuffer();

src/graphics/host_gpu/graphicContext.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,10 @@ struct VulkanImage {
125125

126126
VulkanImageType type = VulkanImageType::Unknown;
127127
vk::Format format = vk::Format::eUndefined;
128+
// Set from vk::ImageCreateFlagBits::eMutableFormat at creation. Views with a format that
129+
// differs from the image format are only legal on mutable-format images
130+
// (VUID-VkImageViewCreateInfo-image-01019).
131+
bool mutable_format = false;
128132
vk::Extent2D extent = {};
129133
uint32_t guest_pitch = 0;
130134
uint32_t layers = 1;

src/graphics/host_gpu/renderer/bufferCache.cpp

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -685,13 +685,9 @@ BufferBinding BufferCache::ObtainBuffer(CommandBuffer& command, uint64_t vaddr,
685685
? texture_region
686686
: m_texture_cache->QueryRegion(begin, end - begin);
687687
if (texture_pages.metadata_pages) {
688-
EXIT("BufferCache: buffer aliases metadata pages, request=0x%016" PRIx64 "+0x%016" PRIx64
689-
" aligned=0x%016" PRIx64 "+0x%016" PRIx64 " read=%d written=%d formatted=%d"
690-
" request_meta=%d/%d/%d aligned_meta=%d/%d/%d\n",
691-
vaddr, size, begin, end - begin, is_read, is_written, is_formatted,
692-
texture_region.metadata_pages, texture_region.metadata_bytes,
693-
texture_region.gpu_metadata_bytes, texture_pages.metadata_pages,
694-
texture_pages.metadata_bytes, texture_pages.gpu_metadata_bytes);
688+
EXIT("BufferCache: buffer aliases metadata pages, request=0x%016" PRIx64
689+
"+0x%016" PRIx64 " read=%d written=%d\n",
690+
vaddr, size, is_read, is_written);
695691
}
696692
// Cache allocations are tracker-page aligned, but byte-disjoint buffers and images may share
697693
// an edge page. Clean read-only buffer and image views may coexist; Kyty retains a hard failure
@@ -944,7 +940,7 @@ BufferImageCopySource BufferCache::ObtainBufferForImage(uint64_t vaddr, uint64_t
944940
vaddr, size, GraphicsRunIsCommandProcessorThread(),
945941
GraphicsRunSubmissionLockHeld(), LabelInCallback());
946942
}
947-
Transfer::WaitForGraphicsIdle();
943+
Transfer::WaitForSubmittedGraphics();
948944
auto backing_writes = ReserveBackingWrites(m_page_manager, dirty_ranges);
949945
uint64_t downloaded = 0;
950946
m_memory_tracker.ForEachDownloadRange<true>(

src/graphics/host_gpu/renderer/colorRenderTarget.cpp

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,16 @@ void ResolveRenderColorTarget(uint64_t submit_id, RenderCommandBuffer& buffer, R
131131
case Prospero::GpuEnumValue(Prospero::TileMode::kRenderTarget):
132132
tile = !RenderIsColorTileModeLinear(rt.attrib3.tile_mode);
133133
break;
134-
default: EXIT("unknown tile mode: %u\n", rt.attrib3.tile_mode);
134+
default: {
135+
static std::atomic_uint32_t soft_logs {0};
136+
if (soft_logs.fetch_add(1, std::memory_order_relaxed) < 16) {
137+
LOGF_COLOR(Log::Color::Yellow,
138+
"soft-allow unknown render-target tile mode %u as tiled\n",
139+
rt.attrib3.tile_mode);
140+
}
141+
tile = true;
142+
break;
143+
}
135144
}
136145
if (!tile && levels > 1) {
137146
EXIT("linear mipmapped render targets are unsupported\n");
@@ -158,12 +167,14 @@ void ResolveRenderColorTarget(uint64_t submit_id, RenderCommandBuffer& buffer, R
158167
rt.info.cmask_is_linear != 0 || rt.info.cmask_addr_type != 0 || rt.info.alt_tile_mode ||
159168
rt.cmask.addr != 0 || rt.fmask.addr != 0 || rt.dcc_addr.addr != 0 ||
160169
rt.dcc.data_write_on_dcc_clear_to_reg)) {
161-
EXIT("unsupported Standard64KB render target: addr=0x%016" PRIx64
162-
" dimension=%u depth=%u levels=%u layer=%u/%u samples=%u fragments=%u bpe=%u"
163-
" cmask=0x%016" PRIx64 " fmask=0x%016" PRIx64 " dcc=0x%016" PRIx64 "\n",
164-
rt.base.addr, rt.attrib3.dimension, rt.attrib3.depth, levels, view.base_layer,
165-
view.image_layers, rt.attrib.num_samples, rt.attrib.num_fragments, bytes_per_element,
166-
rt.cmask.addr, rt.fmask.addr, rt.dcc_addr.addr);
170+
static std::atomic_uint32_t soft_logs {0};
171+
if (soft_logs.fetch_add(1, std::memory_order_relaxed) < 16) {
172+
LOGF_COLOR(Log::Color::Yellow,
173+
"soft-allow non-ideal Standard64KB render target: addr=0x%016" PRIx64
174+
" dimension=%u levels=%u layers=%u samples=%u bpe=%u\n",
175+
rt.base.addr, rt.attrib3.dimension, levels, view.image_layers, samples,
176+
bytes_per_element);
177+
}
167178
}
168179
if (rt.pitch.pitch_div8_minus1 != 0) {
169180
pitch = (rt.pitch.pitch_div8_minus1 + 1u) << 3u;
@@ -173,7 +184,13 @@ void ResolveRenderColorTarget(uint64_t submit_id, RenderCommandBuffer& buffer, R
173184
width, levels, rt.attrib3.tile_mode)
174185
: TileGetRenderTargetPitch(width, bytes_per_element, rt.attrib.num_fragments);
175186
if (pitch == 0) {
176-
EXIT("unsupported render-target pitch: width=%u bytes=%u\n", width, bytes_per_element);
187+
pitch = width;
188+
static std::atomic_uint32_t soft_logs {0};
189+
if (soft_logs.fetch_add(1, std::memory_order_relaxed) < 16) {
190+
LOGF_COLOR(Log::Color::Yellow,
191+
"soft-allow render-target pitch fallback width=%u bytes=%u\n", width,
192+
bytes_per_element);
193+
}
177194
}
178195
} else {
179196
pitch = width;

0 commit comments

Comments
 (0)