Preparing to allow users to override inference settings.

pull/28/head
Pedro Cuenca 1 year ago
parent 8986b34671
commit 8df2f97f29

@ -56,6 +56,8 @@ struct ControlsView: View {
@State private var disclosedGuidance = false
@State private var disclosedSteps = false
@State private var disclosedSeed = false
@State private var disclosedAdvanced = false
@State private var useANE = false
// TODO: refactor download with similar code in Loading.swift (iOS)
@State private var stateSubscriber: Cancellable?
@ -70,7 +72,8 @@ struct ControlsView: View {
@State private var showGuidanceHelp = false
@State private var showStepsHelp = false
@State private var showSeedHelp = false
@State private var showAdvancedHelp = false
// Reasonable range for the slider
let maxSeed: UInt32 = 1000
@ -85,7 +88,7 @@ struct ControlsView: View {
pipelineLoader?.cancel()
pipelineState = .downloading(0)
Task.init {
let loader = PipelineLoader(model: model, maxSeed: maxSeed)
let loader = PipelineLoader(model: model, variant: Settings.shared.userSelectedAttentionVariant, maxSeed: maxSeed)
self.pipelineLoader = loader
stateSubscriber = loader.statePublisher.sink { state in
DispatchQueue.main.async {
@ -114,8 +117,12 @@ struct ControlsView: View {
}
}
func isModelDownloaded(_ model: ModelInfo) -> Bool {
PipelineLoader(model: model, variant: Settings.shared.userSelectedAttentionVariant).ready
}
func modelLabel(_ model: ModelInfo) -> Text {
let downloaded = PipelineLoader(model: model).ready
let downloaded = isModelDownloaded(model)
let prefix = downloaded ? "" : "" //" "
return Text(prefix).foregroundColor(downloaded ? .accentColor : .secondary) + Text(model.modelVersion)
}
@ -123,7 +130,7 @@ struct ControlsView: View {
var body: some View {
VStack(alignment: .leading) {
Label("Adjustments", systemImage: "gearshape.2")
Label("Generation Options", systemImage: "gearshape.2")
.font(.headline)
.fontWeight(.bold)
Divider()
@ -217,7 +224,6 @@ struct ControlsView: View {
}
}.foregroundColor(.secondary)
}
Divider()
DisclosureGroup(isExpanded: $disclosedSteps) {
CompactSlider(value: $generation.steps, in: 0...150, step: 5) {
@ -244,7 +250,6 @@ struct ControlsView: View {
}
}.foregroundColor(.secondary)
}
Divider()
DisclosureGroup(isExpanded: $disclosedSeed) {
let sliderLabel = generation.seed < 0 ? "Random Seed" : "Seed"
@ -272,6 +277,32 @@ struct ControlsView: View {
}
}.foregroundColor(.secondary)
}
Divider()
DisclosureGroup(isExpanded: $disclosedAdvanced) {
HStack {
Toggle("Use Neural Engine", isOn: $useANE).onChange(of: useANE) { value in
print(value)
}.padding(.leading, 10)
Spacer()
}
} label: {
HStack {
Label("Advanced", systemImage: "terminal").foregroundColor(.secondary)
Spacer()
if disclosedAdvanced {
Button {
showAdvancedHelp.toggle()
} label: {
Image(systemName: "info.circle")
}
.buttonStyle(.plain)
.popover(isPresented: $showAdvancedHelp, arrowEdge: .trailing) {
advancedHelp($showAdvancedHelp)
}
}
}.foregroundColor(.secondary)
}
}
}
.disclosureGroupStyle(LabelToggleDisclosureGroupStyle())

@ -123,3 +123,17 @@ func seedHelp(_ showing: Binding<Bool>) -> some View {
"""
return helpContent(title: "Generation Seed", description: description, showing: showing)
}
func advancedHelp(_ showing: Binding<Bool>) -> some View {
let description =
"""
This section allows you to try different optimization settings.
Diffusers will try to select the best configuration for you, but it may not always be optimal \
for your computer. You can experiment with these settings to verify the combination that works faster \
in your system.
Please, note that these settings may trigger downloads of additional model variants.
"""
return helpContent(title: "Advanced Model Settings", description: description, showing: showing)
}

@ -8,6 +8,11 @@
import CoreML
enum AttentionVariant: String {
case original
case splitEinsum
}
struct ModelInfo {
/// Hugging Face model Id that contains .zip archives with compiled Core ML models
let modelId: String
@ -19,30 +24,40 @@ struct ModelInfo {
let originalAttentionSuffix: String
/// Suffix of the archive containing the SPLIT_EINSUM attention variant. Usually something like "split_einsum_compiled"
let splitAttentionName: String
let splitAttentionSuffix: String
/// Whether the archive contains the VAE Encoder (for image to image tasks). Not yet in use.
let supportsEncoder: Bool
init(modelId: String, modelVersion: String, originalAttentionSuffix: String = "original_compiled", splitAttentionName: String = "split_einsum_compiled", supportsEncoder: Bool = false) {
init(modelId: String, modelVersion: String, originalAttentionSuffix: String = "original_compiled", splitAttentionSuffix: String = "split_einsum_compiled", supportsEncoder: Bool = false) {
self.modelId = modelId
self.modelVersion = modelVersion
self.originalAttentionSuffix = originalAttentionSuffix
self.splitAttentionName = splitAttentionName
self.splitAttentionSuffix = splitAttentionSuffix
self.supportsEncoder = supportsEncoder
}
}
extension ModelInfo {
/// Best variant for the current platform.
/// Currently using `split_einsum` for iOS and `original` for macOS, but could vary depending on model.
var bestURL: URL {
var bestAttention: AttentionVariant {
return runningOnMac ? .original : .splitEinsum
}
func modelURL(for variant: AttentionVariant) -> URL {
// Pattern: https://huggingface.co/pcuenq/coreml-stable-diffusion/resolve/main/coreml-stable-diffusion-v1-5_original_compiled.zip
let suffix = runningOnMac ? originalAttentionSuffix : splitAttentionName
let suffix: String
switch variant {
case .original: suffix = originalAttentionSuffix
case .splitEinsum: suffix = splitAttentionSuffix
}
let repo = modelId.split(separator: "/").last!
return URL(string: "https://huggingface.co/\(modelId)/resolve/main/\(repo)_\(suffix).zip")!
}
/// Best variant for the current platform.
/// Currently using `split_einsum` for iOS and `original` for macOS, but could vary depending on model.
var bestURL: URL { modelURL(for: bestAttention) }
/// Best units for current platform.
/// Currently using `cpuAndNeuralEngine` for iOS and `cpuAndGPU` for macOS, but could vary depending on model.
/// .all works for v1.4, but not for v1.5.

@ -18,12 +18,14 @@ class PipelineLoader {
static let models = Path.applicationSupport / "hf-diffusion-models"
let model: ModelInfo
let variant: AttentionVariant
let maxSeed: UInt32
private var downloadSubscriber: Cancellable?
init(model: ModelInfo, maxSeed: UInt32 = UInt32.max) {
init(model: ModelInfo, variant: AttentionVariant? = nil, maxSeed: UInt32 = UInt32.max) {
self.model = model
self.variant = variant ?? model.bestAttention
self.maxSeed = maxSeed
state = .undetermined
setInitialState()
@ -73,7 +75,7 @@ extension PipelineLoader {
extension PipelineLoader {
var url: URL {
return model.bestURL
return model.modelURL(for: variant)
}
var filename: String {

@ -73,12 +73,14 @@ class Settings {
enum Keys: String {
case model
case safetyCheckerDisclaimer
case variant
}
private init() {
defaults.register(defaults: [
Keys.model.rawValue: ModelInfo.v2Base.modelId,
Keys.safetyCheckerDisclaimer.rawValue: false
Keys.safetyCheckerDisclaimer.rawValue: false,
Keys.variant.rawValue: "- default -"
])
}
@ -100,4 +102,17 @@ class Settings {
return defaults.bool(forKey: Keys.safetyCheckerDisclaimer.rawValue)
}
}
/// Returns the option selected by the user, if overridden
/// `nil` means: guess best for this {model, device}
var userSelectedAttentionVariant: AttentionVariant? {
set {
// Any String other than the supported ones would cause `get` to return `nil`
defaults.set(newValue?.rawValue ?? "- default -", forKey: Keys.variant.rawValue)
}
get {
let current = defaults.string(forKey: Keys.variant.rawValue)
return AttentionVariant(rawValue: current ?? "")
}
}
}

Loading…
Cancel
Save