Skip to content

Commit 951289b

Browse files
fix: address Copilot review — DS UV split, .metal source, changelog
- Fix default inputFrame UV split for DeSmuME2015 (bufferSize=2048×2048): the old 0–0.5 split sampled the full texture width/height, but the valid DS content only occupies the top-left 256×384 region; now normalises by DS native dimensions (256×192 per screen) so UVs land on correct texels for both padded (2048×2048) and native-size (256×384) textures - Add dual_screen_blit.metal to PVShaders/Blitters as the canonical source of the dual-screen shaders; add comment in PVMetalViewController+DualScreen explaining why the inline copy is used at runtime (cross-bundle access) - Drop trailing period from .changelog/3509.md to match project style Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 6165aa6 commit 951289b

4 files changed

Lines changed: 71 additions & 6 deletions

File tree

.changelog/3509.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
### Added
2-
- **DS Dual-Screen Metal Rendering** — Nintendo DS games with a DeltaSkin active now render both screens as GPU-side sub-rectangle blits in a single Metal render pass. The combined 256×384 DS framebuffer is split into top (rows 0–191) and bottom (rows 192–383) viewports that match the skin's screen layout, supporting portrait (stacked) and landscape (side-by-side) configurations
2+
- **DS Dual-Screen Metal Rendering** — Nintendo DS games with a DeltaSkin active now render both screens as GPU-side sub-rectangle blits in a single Metal render pass; the combined 256×384 DS framebuffer is split into top (rows 0–191) and bottom (rows 192–383) viewports that match the skin's screen layout, supporting portrait (stacked) and landscape (side-by-side) configurations
33

44
### Changed
55
- **melonDS / DeSmuME skin support**`supportsSkins` is now `true` for both DS cores, enabling DeltaSkin overlays and controller layouts when a compatible skin is installed
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// dual_screen_blit.metal
2+
// PVShaders
3+
//
4+
// Canonical Metal shaders for the dual-screen sub-rectangle renderer.
5+
//
6+
// These functions are the authoritative source for the shaders used in
7+
// PVMetalViewController+DualScreen. The identical source is also embedded
8+
// as the inline string `PVMetalViewController.dualScreenShaderSource` so
9+
// that it can be compiled at runtime via `device.makeLibrary(source:)` on
10+
// any device regardless of bundle access.
11+
//
12+
// Vertex input: buffer(0) holds an array of float4 where
13+
// .xy = clip-space (NDC) position
14+
// .zw = texture UV coordinate
15+
//
16+
// A four-vertex triangle strip draws one screen quad per draw call.
17+
18+
#include <metal_stdlib>
19+
using namespace metal;
20+
21+
struct DSVSOut {
22+
float4 position [[position]];
23+
float2 texCoord;
24+
};
25+
26+
/// Pass-through vertex shader: unpacks (NDC.xy, UV.zw) from a flat float4 array.
27+
vertex DSVSOut dual_screen_vs(
28+
uint vid [[vertex_id]],
29+
constant float4 *vertices [[buffer(0)]])
30+
{
31+
DSVSOut out;
32+
out.position = float4(vertices[vid].xy, 0.0f, 1.0f);
33+
out.texCoord = vertices[vid].zw;
34+
return out;
35+
}
36+
37+
/// Fragment shader: samples the combined DS framebuffer texture at the UV
38+
/// interpolated by the vertex shader. A constexpr sampler with linear
39+
/// filtering and clamp-to-edge is used so there are no sampler bindings
40+
/// required from the host.
41+
fragment half4 dual_screen_ps(
42+
DSVSOut in [[stage_in]],
43+
texture2d<half> source [[texture(0)]])
44+
{
45+
constexpr sampler s(coord::normalized,
46+
address::clamp_to_edge,
47+
filter::linear);
48+
half4 c = source.sample(s, in.texCoord);
49+
c.a = 1.0h;
50+
return c;
51+
}

PVUI/Sources/PVUIBase/PVEmulatorVC/PVEmulatorViewController+MetalDualScreen.swift

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -131,9 +131,18 @@ extension PVEmulatorViewController {
131131
width: inFrame.width / texW,
132132
height: inFrame.height / texH)
133133
} else {
134-
// Default: split the framebuffer into equal vertical halves.
135-
let halfH: CGFloat = 0.5
136-
srcRect = CGRect(x: 0, y: CGFloat(index) * halfH, width: 1, height: halfH)
134+
// Default: split the framebuffer based on DS native screen dimensions.
135+
// DS native: 256 px wide, 192 px per screen, 384 px combined height.
136+
// Some emulators (e.g. DeSmuME2015) report a large padded bufferSize
137+
// (2048×2048) where the valid DS content sits only in the top-left
138+
// 256×384 region. Normalising by texW/texH ensures the UVs land on
139+
// the correct texels rather than always splitting the full 0–1 range.
140+
let dsNativeW: CGFloat = 256 // DS screen width in native pixels
141+
let dsNativeH: CGFloat = 192 // DS per-screen height in native pixels
142+
srcRect = CGRect(x: 0,
143+
y: CGFloat(index) * (dsNativeH / texH),
144+
width: dsNativeW / texW,
145+
height: dsNativeH / texH)
137146
}
138147

139148
// --- Destination (view-space points) ---

PVUI/Sources/PVUIBase/PVGLViewController/PVMetalViewController+DualScreen.swift

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,13 @@ extension PVMetalViewController {
5555

5656
/// Metal source for the dual-screen sub-rectangle blit shaders.
5757
///
58-
/// Compiled at runtime via `makeLibrary(source:)` — the same pattern used
59-
/// elsewhere in PVMetalViewController for inline shaders.
58+
/// The canonical source lives in
59+
/// `PVShaders/Sources/PVShaders/Resources/Metal/Blitters/dual_screen_blit.metal`.
60+
/// That file is compiled into the PVShaders bundle's metallib during the Xcode
61+
/// build, but loading it at runtime requires explicit bundle access across the
62+
/// module boundary. To avoid that coupling (and to match the pattern used by
63+
/// every other shader in PVMetalViewController) we embed an identical copy here
64+
/// and compile it at runtime via `device.makeLibrary(source:)`.
6065
static let dualScreenShaderSource = """
6166
#include <metal_stdlib>
6267
using namespace metal;

0 commit comments

Comments
 (0)