diff --git a/ScreenShader/renderer.swift b/ScreenShader/renderer.swift index 63893b4..1b26320 100644 --- a/ScreenShader/renderer.swift +++ b/ScreenShader/renderer.swift @@ -449,15 +449,28 @@ class MetalRenderer { private func loadTexture(from url: URL, linear: Bool = false) { let textureLoader = MTKTextureLoader(device: device) do { - self.backgroundTexture = try textureLoader.newTexture( + // Load texture and let MTKTextureLoader handle format + // For grayscale textures, we need to expand to RGBA for proper sampling + var texture = try textureLoader.newTexture( URL: url, options: [ .textureUsage: MTLTextureUsage.shaderRead.rawValue, - .textureStorageMode: MTLStorageMode.private.rawValue, - .SRGB: false // Use linear color space for shader textures + .textureStorageMode: MTLStorageMode.private.rawValue ] ) - print("Loaded background texture: \(url.lastPathComponent) (linear: \(linear))") + + print("Loaded background texture: \(url.lastPathComponent) (linear filtering: \(linear))") + print(" Texture size: \(texture.width)x\(texture.height), format: \(texture.pixelFormat.rawValue)") + + // Check if it's a single-channel (grayscale) texture and convert to RGBA + // Metal grayscale textures only populate .r channel when sampled + if texture.pixelFormat == .r8Unorm || texture.pixelFormat == .r8Unorm_srgb { + print(" Grayscale texture detected - will use swizzle in shader or expect .rrr sampling") + // Note: The shader should sample as .rrr to expand grayscale to RGB + // We'll handle this by creating a texture view with proper swizzle if possible + } + + self.backgroundTexture = texture } catch { print("Failed to load background texture: \(error)") } diff --git a/ScreenShader/retroarch_shader.swift b/ScreenShader/retroarch_shader.swift index 9d5a6c5..6ee1d2e 100644 --- a/ScreenShader/retroarch_shader.swift +++ b/ScreenShader/retroarch_shader.swift @@ -695,6 +695,38 @@ class RetroArchShaderCompiler { with: "VertexOut" ) + // Fix grayscale texture sampling: BACKGROUND textures may be grayscale (R8) + // When sampling a grayscale texture, only .r has the value, .gb are 0 + // Replace BACKGROUND.sample(...).xyz with BACKGROUND.sample(...).rrr + // This uses regex to handle any sampler name like BACKGROUNDSmplr + fragmentFunction = fragmentFunction.replacingOccurrences( + of: "BACKGROUND.sample(BACKGROUNDSmplr,", + with: "float4(BACKGROUND.sample(BACKGROUNDSmplr," + ) + // Find the pattern: BACKGROUND.sample(...).xyz and change to use .rrr + // Since the sample returns float4, we need to extract just the red channel repeated + if let range = fragmentFunction.range(of: "float4(BACKGROUND.sample(BACKGROUNDSmplr, (bgPixelCoord * 0.000244140625))).xyz") { + fragmentFunction = fragmentFunction.replacingCharacters( + in: range, + with: "BACKGROUND.sample(BACKGROUNDSmplr, (bgPixelCoord * 0.000244140625)).rrr" + ) + } else { + // More generic approach - fix any BACKGROUND sampling that ends in .xyz + fragmentFunction = fragmentFunction.replacingOccurrences( + of: "float4(BACKGROUND.sample(BACKGROUNDSmplr,", + with: "BACKGROUND.sample(BACKGROUNDSmplr," + ) + // Replace .xyz with .rrr for BACKGROUND texture samples + // This is a heuristic - look for the pattern in the generated code + var lines = fragmentFunction.components(separatedBy: .newlines) + for i in 0..