From 355693636a498ed412d7dbd6fe36588ad6f2afd1 Mon Sep 17 00:00:00 2001 From: Stuart Carnie Date: Sat, 24 Dec 2022 09:20:49 +1100 Subject: [PATCH] fix: Fixes for parsing, improved errors, more robust LUT image loading --- Source/Config.swift | 8 ++++++++ Source/FilterChain.swift | 31 ++++++++++++++++++++++++++++++- Source/SlangCompiler.swift | 22 +++++++++++----------- Source/SourceParser.swift | 28 ++++++++++++++++------------ 4 files changed, 65 insertions(+), 24 deletions(-) diff --git a/Source/Config.swift b/Source/Config.swift index ce1416a..600a73a 100644 --- a/Source/Config.swift +++ b/Source/Config.swift @@ -248,6 +248,14 @@ struct ConfigScanner { return false } + if scalars[pos] == "/" { + let peek = scalars.index(after: pos) + if peek != scalars.endIndex, scalars[pos] == "/" { + // comments can also be // + return false + } + } + return true } diff --git a/Source/FilterChain.swift b/Source/FilterChain.swift index cddde36..88e7e57 100644 --- a/Source/FilterChain.swift +++ b/Source/FilterChain.swift @@ -944,7 +944,11 @@ public final class FilterChain { let t: MTLTexture do { let data = try cc.getLUTByName(lut.name) - t = try loader.newTexture(data: data, options: opts) + if let cgImg = CGContext.makeForTexture(data: data)?.makeImage() { + t = try loader.newTexture(cgImage: cgImg, options: opts) + } else { + t = checkers + } } catch { os_log("Unable to load LUT texture, using default. Path '%{public}@: %{public}@", log: .default, type: .error, lut.url.absoluteString, error.localizedDescription) @@ -1234,3 +1238,28 @@ extension MTLPixelFormat { } } } + +extension CGContext { + /// Returns a context using the dimensions and contents from the image identified by the data. + /// The context data format is compatible with BGRA8 + /// - Parameter data: The data of the source image. + /// - Returns: a new ``CGContext`` with dimensions and contents matching the source image. + static func makeForTexture(data: Data) -> CGContext? { + guard + let src = CGImageSourceCreateWithData(data as CFData, nil), + let img = CGImageSourceCreateImageAtIndex(src, CGImageSourceGetPrimaryImageIndex(src), nil) + else { return nil } + + guard let context = CGContext(data: nil, + width: img.width, height: img.height, + bitsPerComponent: 8, bytesPerRow: img.width * 4, + space: CGColorSpaceCreateDeviceRGB(), + bitmapInfo: CGBitmapInfo.byteOrder32Little.rawValue | CGImageAlphaInfo.premultipliedFirst.rawValue) + else { return nil } + + context.interpolationQuality = .none + context.draw(img, in: CGRect(x: 0, y: 0, width: img.width, height: img.height)) + + return context + } +} diff --git a/Source/SlangCompiler.swift b/Source/SlangCompiler.swift index 4c9a81d..828f200 100644 --- a/Source/SlangCompiler.swift +++ b/Source/SlangCompiler.swift @@ -80,18 +80,18 @@ class SlangCompiler { guard shader.preprocess(input: &inp) else { - let msg = shader.preprocessed_code - os_log(.error, log: .default, "Error preprocessing shader: %{public}s", msg) + let msg = String(cString: shader.info_log) + os_log(.error, log: .default, "Error preprocessing shader: %{public}@", msg) - throw SlangCompilerError.preprocess(reason: String(cString: msg)) + throw SlangCompilerError.preprocess(reason: msg) } guard shader.parse(input: &inp) else { - let msg = shader.preprocessed_code - os_log(.error, log: .default, "Error parsing shader: %{public}s", msg) + let msg = String(cString: shader.info_log) + os_log(.error, log: .default, "Error parsing shader: %{public}@", msg) - throw SlangCompilerError.parse(reason: String(cString: msg)) + throw SlangCompilerError.parse(reason: msg) } let program = CGLSLangProgram() @@ -101,13 +101,13 @@ class SlangCompiler { guard program.link(messages: inp.messages) else { - let infoLog = program.info_log - let infoDebugLog = program.info_debug_log + let infoLog = String(cString: program.info_log) + let infoDebugLog = String(cString: program.info_debug_log) - os_log(.error, log: .default, "Error linking shader info log: %{public}s", infoLog) - os_log(.error, log: .default, "Error linking shader info debug log: %{public}s", infoDebugLog) + os_log(.error, log: .default, "Error linking shader info log: %{public}@", infoLog) + os_log(.error, log: .default, "Error linking shader info debug log: %{public}@", infoDebugLog) - throw SlangCompilerError.link(reason: String(cString: infoLog)) + throw SlangCompilerError.link(reason: infoLog) } program.spirv_generate(stage: stage) diff --git a/Source/SourceParser.swift b/Source/SourceParser.swift index 1720954..8cb2609 100644 --- a/Source/SourceParser.swift +++ b/Source/SourceParser.swift @@ -183,15 +183,12 @@ class SourceParser { var oe = lines.makeIterator() if isRoot { - let line = oe.next() - switch line?.hasPrefix(Prefixes.version) { - case .some(false), .none: + guard let line = oe.next(), line.hasPrefix(Prefixes.version) else { throw SourceParserError.missingVersion - default: - buffer.append(line!) - buffer.append("#extension GL_GOOGLE_cpp_style_line_directive : require") - lno += 1 } + buffer.append(line) + buffer.append("#extension GL_GOOGLE_cpp_style_line_directive : require") + lno += 1 } buffer.append("#line \(lno) \"\(filename)\"") @@ -213,16 +210,21 @@ class SourceParser { included.insert(file.absoluteString) } } else { - buffer.append(line) - - var hasPreprocessor = false + let hasPreprocessor: Bool if line.hasPrefix(Prefixes.pragma) { hasPreprocessor = true - try processPragma(line: line) + if try processPragma(line: line) { + // skip line + continue + } } else if line.hasPrefix(Prefixes.endif) { hasPreprocessor = true + } else { + hasPreprocessor = false } + buffer.append(line) + if hasPreprocessor { buffer.append("#line \(lno + 1) \"\(filename)\"") } @@ -237,7 +239,7 @@ class SourceParser { return set as CharacterSet } - private func processPragma(line: String) throws { + private func processPragma(line: String) throws -> Bool { if line.hasPrefix(Prefixes.pragmaName) { if name != nil { throw SourceParserError.multipleNamePragma @@ -303,6 +305,8 @@ class SourceParser { throw SourceParserError.invalidFormatPragma } } + + return !line.hasPrefix(Prefixes.pragmaStage) } }