From 3fe5b16998fe37a5d57f4942edbcf29970f363d2 Mon Sep 17 00:00:00 2001 From: keanu-stitch8 Date: Sat, 31 Oct 2026 22:56:42 +0700 Subject: [PATCH 1/2] DM_CTL texraw saves every slice of a texture texraw saved slice 0 only, so a cube, array or layered render target could be checked only through its first face or layer. It now saves level 0 of every slice one after another (a cube's six faces), with the slice count after the header's four fields when there is more than one. A single-slice texture's file is unchanged. Co-Authored-By: Claude Opus 5.5 --- dm/gfx/nvn/nvn_mtl.m | 10 ++++++---- dm/host/hle_hid.c | 2 +- docs/agents/05-debugging.md | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/dm/gfx/nvn/nvn_mtl.m b/dm/gfx/nvn/nvn_mtl.m index 1140f62fde..ac854ac30a 100644 --- a/dm/gfx/nvn/nvn_mtl.m +++ b/dm/gfx/nvn/nvn_mtl.m @@ -10973,7 +10973,7 @@ void td_note_layer(id t, uint64_t ser, NSUInteger lv, NSUInteger sl) [g_td_list addObject:@[t, @(ser), @(lv), @(sl)]]; } // The next frame's targets, on request (the Mac's DM_CTL "texdump ", hle_hid.c): the path is kept (strdup), the frame number is the one after the current -// The Mac's DM_CTL "texraw " (hle_hid.c): level 0 / slice 0 of the host texture behind a serial, raw, after the GPU finishes (header line: width height Metal format bytes per row); +// The Mac's DM_CTL "texraw " (hle_hid.c): level 0 of every slice (a cube's six faces) of the host texture behind a serial, raw, after the GPU finishes (header line: width height Metal format bytes per row, then the slice count when above one); // for a texture that is no render target (a default texture, a lookup table) whose content a shader depends on void dm_gfx_texraw(uint64_t serial, const char *path) { __unsafe_unretained id o = tex_get(serial); @@ -10981,12 +10981,14 @@ void dm_gfx_texraw(uint64_t serial, const char *path) { id t = o; mtl_finish_gpu(); if (t.storageMode == MTLStorageModeMemoryless || t.pixelFormat == MTLPixelFormatInvalid) return; NSUInteger w = t.width, h = t.height, row = w * 16, sz = row * h; // 16 bytes a texel: a multiple of every uncompressed texel size - id buf = [g_dev newBufferWithLength:sz options:MTLResourceStorageModeShared]; + NSUInteger ns = (t.textureType == MTLTextureTypeCube || t.textureType == MTLTextureTypeCubeArray ? 6 : 1) * MAX(t.arrayLength, 1u); // every slice (a cube's six faces), one after another + id buf = [g_dev newBufferWithLength:sz * ns options:MTLResourceStorageModeShared]; id cb = q_cb(); id b = [cb blitCommandEncoder]; MTLBlitOption bo = t.pixelFormat == MTLPixelFormatDepth32Float_Stencil8 ? MTLBlitOptionDepthFromDepthStencil : MTLBlitOptionNone; - [b copyFromTexture:t sourceSlice:0 sourceLevel:0 sourceOrigin:MTLOriginMake(0, 0, 0) sourceSize:MTLSizeMake(w, h, 1) toBuffer:buf destinationOffset:0 destinationBytesPerRow:row destinationBytesPerImage:sz options:bo]; + for (NSUInteger s = 0; s < ns; s++) + [b copyFromTexture:t sourceSlice:s sourceLevel:0 sourceOrigin:MTLOriginMake(0, 0, 0) sourceSize:MTLSizeMake(w, h, 1) toBuffer:buf destinationOffset:sz * s destinationBytesPerRow:row destinationBytesPerImage:sz options:bo]; [b endEncoding]; [cb commit]; [cb waitUntilCompleted]; - FILE *f = fopen(path, "wb"); if (f) { fprintf(f, "%lu %lu %lu %lu\n", (unsigned long)w, (unsigned long)h, (unsigned long)t.pixelFormat, (unsigned long)row); fwrite(buf.contents, 1, sz, f); fclose(f); } + FILE *f = fopen(path, "wb"); if (f) { fprintf(f, "%lu %lu %lu %lu", (unsigned long)w, (unsigned long)h, (unsigned long)t.pixelFormat, (unsigned long)row); if (ns > 1) fprintf(f, " %lu", (unsigned long)ns); fputc('\n', f); fwrite(buf.contents, 1, sz * ns, f); fclose(f); } const uint8_t *p = buf.contents; MLOG("[texraw] ser=%llu %lux%lu format %lu type %lu → %s; first bytes %02x %02x %02x %02x %02x %02x %02x %02x", (unsigned long long)serial, (unsigned long)w, (unsigned long)h, (unsigned long)t.pixelFormat, (unsigned long)t.textureType, path, p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]); } diff --git a/dm/host/hle_hid.c b/dm/host/hle_hid.c index db67a3ac9a..768a35701a 100644 --- a/dm/host/hle_hid.c +++ b/dm/host/hle_hid.c @@ -204,7 +204,7 @@ void hid_keys_init(void) { // (hle_fpslock.c dm_ctl_cvar_f32; "cvari" an int one), "peek
" logs that many floats of guest memory, "memdump
" saves guest memory // to a file (a buffer texture's storage, which the pass dump's tbuf lines print, holds what the GPU wrote: buffer textures are views of the pool), "texlist " logs the // registered textures of that target (2 = 3D) with storage address and block-linear layout (nvn_mtl.m dm_gfx_texlist), "texraw " -// saves a host texture's level 0 raw (nvn_mtl.m dm_gfx_texraw), "drag [seconds=0.3]" moves one finger in a straight line between the two normalized picture +// saves a host texture's level 0 raw, every slice (a cube's six faces) (nvn_mtl.m dm_gfx_texraw), "drag [seconds=0.3]" moves one finger in a straight line between the two normalized picture // points over that time and lifts it (the touch screen's pans and swipes, hle_touch.c gesture_sample), "dump" logs what each guest thread waits on and the graphics wait points // (crash_dump_now, as the iOS app's DM_LOG_PORT dump; scripts/regress.py asks for it before stopping a hung game), "#…" is ignored. // Look at a screen, decide, press, look again: menus whose timing moves from run to run (loading) need no blind timed script diff --git a/docs/agents/05-debugging.md b/docs/agents/05-debugging.md index 20dc66ebbe..c18a46f718 100644 --- a/docs/agents/05-debugging.md +++ b/docs/agents/05-debugging.md @@ -102,7 +102,7 @@ Patterns that worked: print every call of one function with its arguments; stop - `DM_KEYS=":