From d3a375c8fbd443b674eb4a1027c200cb34a59359 Mon Sep 17 00:00:00 2001 From: ltdrdata <128333288+ltdrdata@users.noreply.github.com> Date: Tue, 28 Mar 2023 02:27:09 +0900 Subject: [PATCH 1/9] Add support for file list refresh feature in node (#192) * Added file reload feature to widgets. * Modify feature name 'reload' to 'refresh' and fixed ignoring button name. * refresh widget bugfix * crash patch for "widget" input by type mismatch * compensate offset on showimage * adding widget caused misaligned offset * patch refresh feature for general method * clean up patch and following upstream * make more clean code for refresh feature * move refresh button position * robust patch for refresh feature * patch for refreh feature * avoid specify REFRESH_LIST for each node * prevent updating selected value unless removed item * update all combo list for 'required input' in node --------- Co-authored-by: Lt.Dr.Data --- web/scripts/app.js | 25 +++++++++++++++++++++++++ web/scripts/ui.js | 1 + 2 files changed, 26 insertions(+) diff --git a/web/scripts/app.js b/web/scripts/app.js index 89eb71122..43d7f7b59 100644 --- a/web/scripts/app.js +++ b/web/scripts/app.js @@ -901,6 +901,31 @@ class ComfyApp { } this.extensions.push(extension); } + + /** + * Refresh combo list on whole nodes + */ + async refreshComboInNodes() { + const defs = await api.getNodeDefs(); + + for(let nodeNum in this.graph._nodes) { + const node = this.graph._nodes[nodeNum]; + + const def = defs[node.type]; + + for(const widgetNum in node.widgets) { + const widget = node.widgets[widgetNum] + + if(widget.type == "combo" && def["input"]["required"][widget.name] !== undefined) { + widget.options.values = def["input"]["required"][widget.name][0]; + + if(!widget.options.values.includes(widget.value)) { + widget.value = widget.options.values[0]; + } + } + } + } + } } export const app = new ComfyApp(); diff --git a/web/scripts/ui.js b/web/scripts/ui.js index 94f3c528a..2c3bf87c7 100644 --- a/web/scripts/ui.js +++ b/web/scripts/ui.js @@ -376,6 +376,7 @@ export class ComfyUI { }, }), $el("button", { textContent: "Load", onclick: () => fileInput.click() }), + $el("button", { textContent: "Refresh", onclick: () => app.refreshComboInNodes() }), $el("button", { textContent: "Clear", onclick: () => app.graph.clear() }), $el("button", { textContent: "Load Default", onclick: () => app.loadGraphData() }), ]); From 4b9e11053c70925069f5bbe3adbd5c0e27e70f97 Mon Sep 17 00:00:00 2001 From: Jairo Correa Date: Mon, 27 Mar 2023 23:36:53 -0300 Subject: [PATCH 2/9] Color palette setting --- web/extensions/core/colorPalette.js | 351 ++++++++++++++++++++++++++++ web/scripts/app.js | 23 -- web/scripts/ui.js | 15 +- web/style.css | 6 + 4 files changed, 370 insertions(+), 25 deletions(-) create mode 100644 web/extensions/core/colorPalette.js diff --git a/web/extensions/core/colorPalette.js b/web/extensions/core/colorPalette.js new file mode 100644 index 000000000..e54bc2a38 --- /dev/null +++ b/web/extensions/core/colorPalette.js @@ -0,0 +1,351 @@ +import { app } from "/scripts/app.js"; +import { $el } from "/scripts/ui.js"; +import { api } from "/scripts/api.js"; + +// Manage color palettes + +const colorPalettes = { + "palette_1": { + "id": "palette_1", + "name": "Palette 1", + "colors": { + "node_slot": { + "CLIP": "#FFD500", // bright yellow + "CLIP_VISION": "#A8DADC", // light blue-gray + "CLIP_VISION_OUTPUT": "#ad7452", // rusty brown-orange + "CONDITIONING": "#FFA931", // vibrant orange-yellow + "CONTROL_NET": "#6EE7B7", // soft mint green + "IMAGE": "#64B5F6", // bright sky blue + "LATENT": "#FF9CF9", // light pink-purple + "MASK": "#81C784", // muted green + "MODEL": "#B39DDB", // light lavender-purple + "STYLE_MODEL": "#C2FFAE", // light green-yellow + "VAE": "#FF6E6E", // bright red + } + } + }, + "palette_2": { + "id": "palette_2", + "name": "Palette 2", + "colors": { + "node_slot": { + "CLIP": "#556B2F", // Dark Olive Green + "CLIP_VISION": "#4B0082", // Indigo + "CLIP_VISION_OUTPUT": "#006400", // Green + "CONDITIONING": "#FF1493", // Deep Pink + "CONTROL_NET": "#8B4513", // Saddle Brown + "IMAGE": "#8B0000", // Dark Red + "LATENT": "#00008B", // Dark Blue + "MASK": "#2F4F4F", // Dark Slate Grey + "MODEL": "#FF8C00", // Dark Orange + "STYLE_MODEL": "#004A4A", // Sherpa Blue + "UPSCALE_MODEL": "#4A004A", // Tyrian Purple + "VAE": "#4F394F", // Loulou + } + } + } +}; + +const id = "Comfy.ColorPalette"; +const idCustomColorPalettes = "Comfy.CustomColorPalettes"; +const defaultColorPaletteId = "palette_1"; +const els = {} +// const ctxMenu = LiteGraph.ContextMenu; +app.registerExtension({ + name: id, + init() { + const sortObjectKeys = (unordered) => { + return Object.keys(unordered).sort().reduce((obj, key) => { + obj[key] = unordered[key]; + return obj; + }, {}); + }; + + const getSlotTypes = async () => { + var types = []; + + const defs = await api.getNodeDefs(); + for (const nodeId in defs) { + const nodeData = defs[nodeId]; + + var inputs = nodeData["input"]["required"]; + if (nodeData["input"]["optional"] != undefined){ + inputs = Object.assign({}, nodeData["input"]["required"], nodeData["input"]["optional"]) + } + + for (const inputName in inputs) { + const inputData = inputs[inputName]; + const type = inputData[0]; + + if (!Array.isArray(type)) { + types.push(type); + } + } + + for (const o in nodeData["output"]) { + const output = nodeData["output"][o]; + types.push(output); + } + } + + return types; + }; + + const completeColorPalette = async (colorPalette) => { + var types = await getSlotTypes(); + + for (const type of types) { + if (!colorPalette.colors.node_slot[type]) { + colorPalette.colors.node_slot[type] = ""; + } + } + + colorPalette.colors.node_slot = sortObjectKeys(colorPalette.colors.node_slot); + + return colorPalette; + }; + + const getColorPaletteTemplate = async () => { + let colorPalette = { + "id": "my_color_palette_unique_id", + "name": "My Color Palette", + "colors": { + "node_slot": { + } + } + }; + + return completeColorPalette(colorPalette); + }; + + const getCustomColorPalettes = () => { + return app.ui.settings.getSettingValue(idCustomColorPalettes, {}); + }; + + const setCustomColorPalettes = (customColorPalettes) => { + return app.ui.settings.setSettingValue(idCustomColorPalettes, customColorPalettes); + }; + + const addCustomColorPalette = async (colorPalette) => { + if (typeof(colorPalette) !== "object") { + app.ui.dialog.show("Invalid color palette"); + return; + } + + if (!colorPalette.id) { + app.ui.dialog.show("Color palette missing id"); + return; + } + + if (!colorPalette.name) { + app.ui.dialog.show("Color palette missing name"); + return; + } + + if (!colorPalette.colors) { + app.ui.dialog.show("Color palette missing colors"); + return; + } + + if (colorPalette.colors.node_slot && typeof(colorPalette.colors.node_slot) !== "object") { + app.ui.dialog.show("Invalid color palette colors.node_slot"); + return; + } + + let customColorPalettes = getCustomColorPalettes(); + customColorPalettes[colorPalette.id] = colorPalette; + setCustomColorPalettes(customColorPalettes); + + for (const option of els.select.childNodes) { + if (option.value === "custom_" + colorPalette.id) { + els.select.removeChild(option); + } + } + + els.select.append($el("option", { textContent: colorPalette.name + " (custom)", value: "custom_" + colorPalette.id, selected: true })); + + setColorPalette("custom_" + colorPalette.id); + await loadColorPalette(colorPalette); + }; + + const deleteCustomColorPalette = async (colorPaletteId) => { + let customColorPalettes = getCustomColorPalettes(); + delete customColorPalettes[colorPaletteId]; + setCustomColorPalettes(customColorPalettes); + + for (const option of els.select.childNodes) { + if (option.value === defaultColorPaletteId) { + option.selected = true; + } + + if (option.value === "custom_" + colorPaletteId) { + els.select.removeChild(option); + } + } + + setColorPalette(defaultColorPaletteId); + await loadColorPalette(getColorPalette()); + }; + + const loadColorPalette = async (colorPalette) => { + colorPalette = await completeColorPalette(colorPalette); + if (colorPalette.colors) { + if (colorPalette.colors.node_slot) { + Object.assign(app.canvas.default_connection_color_byType, colorPalette.colors.node_slot); + app.canvas.draw(true, true); + } + } + }; + + const getColorPalette = (colorPaletteId) => { + if (!colorPaletteId) { + colorPaletteId = app.ui.settings.getSettingValue(id, defaultColorPaletteId); + } + + if (colorPaletteId.startsWith("custom_")) { + colorPaletteId = colorPaletteId.substr(7); + let customColorPalettes = getCustomColorPalettes(); + if (customColorPalettes[colorPaletteId]) { + return customColorPalettes[colorPaletteId]; + } + } + + return colorPalettes[colorPaletteId]; + }; + + const setColorPalette = (colorPaletteId) => { + app.ui.settings.setSettingValue(id, colorPaletteId); + }; + + const fileInput = $el("input", { + type: "file", + accept: ".json", + style: { display: "none" }, + parent: document.body, + onchange: () => { + let file = fileInput.files[0]; + + if (file.type === "application/json" || file.name.endsWith(".json")) { + const reader = new FileReader(); + reader.onload = async () => { + await addCustomColorPalette(JSON.parse(reader.result)); + }; + reader.readAsText(file); + } + }, + }); + + app.ui.settings.addSetting({ + id, + name: "Color Palette", + type: (name, setter, value) => { + let options = []; + + for (const c in colorPalettes) { + const colorPalette = colorPalettes[c]; + options.push($el("option", { textContent: colorPalette.name, value: colorPalette.id, selected: colorPalette.id === value })); + } + + let customColorPalettes = getCustomColorPalettes(); + for (const c in customColorPalettes) { + const colorPalette = customColorPalettes[c]; + options.push($el("option", { textContent: colorPalette.name + " (custom)", value: "custom_" + colorPalette.id, selected: "custom_" + colorPalette.id === value })); + } + + return $el("div", [ + $el("label", { textContent: name || id }, [ + els.select = $el("select", { + onchange: (e) => { + setter(e.target.value); + } + }, options) + ]), + $el("input", { + type: "button", + value: "Export", + onclick: async () => { + const colorPaletteId = app.ui.settings.getSettingValue(id, defaultColorPaletteId); + const colorPalette = await completeColorPalette(getColorPalette(colorPaletteId)); + const json = JSON.stringify(colorPalette, null, 2); // convert the data to a JSON string + const blob = new Blob([json], { type: "application/json" }); + const url = URL.createObjectURL(blob); + const a = $el("a", { + href: url, + download: colorPaletteId + ".json", + style: { display: "none" }, + parent: document.body, + }); + a.click(); + setTimeout(function () { + a.remove(); + window.URL.revokeObjectURL(url); + }, 0); + }, + }), + $el("input", { + type: "button", + value: "Import", + onclick: () => { + fileInput.click(); + } + }), + $el("input", { + type: "button", + value: "Template", + onclick: async () => { + const colorPalette = await getColorPaletteTemplate(); + const json = JSON.stringify(colorPalette, null, 2); // convert the data to a JSON string + const blob = new Blob([json], { type: "application/json" }); + const url = URL.createObjectURL(blob); + const a = $el("a", { + href: url, + download: "color_palette.json", + style: { display: "none" }, + parent: document.body, + }); + a.click(); + setTimeout(function () { + a.remove(); + window.URL.revokeObjectURL(url); + }, 0); + } + }), + $el("input", { + type: "button", + value: "Delete", + onclick: async () => { + let colorPaletteId = app.ui.settings.getSettingValue(id, defaultColorPaletteId); + + if (colorPalettes[colorPaletteId]) { + app.ui.dialog.show("You cannot delete built-in color palette"); + return; + } + + if (colorPaletteId.startsWith("custom_")) { + colorPaletteId = colorPaletteId.substr(7); + } + + await deleteCustomColorPalette(colorPaletteId); + } + }), + ]); + }, + defaultValue: defaultColorPaletteId, + async onChange(value) { + if (!value) { + return; + } + + if (colorPalettes[value]) { + await loadColorPalette(colorPalettes[value]); + } else if (value.startsWith("custom_")) { + value = value.substr(7); + let customColorPalettes = getCustomColorPalettes(); + if (customColorPalettes[value]) { + await loadColorPalette(customColorPalettes[value]); + } + } + }, + }); + }, +}); diff --git a/web/scripts/app.js b/web/scripts/app.js index 43d7f7b59..609cc4cf7 100644 --- a/web/scripts/app.js +++ b/web/scripts/app.js @@ -576,27 +576,6 @@ class ComfyApp { } } - /** - * Setup slot colors for types - */ - setupSlotColors() { - let colors = { - "CLIP": "#FFD500", // bright yellow - "CLIP_VISION": "#A8DADC", // light blue-gray - "CLIP_VISION_OUTPUT": "#ad7452", // rusty brown-orange - "CONDITIONING": "#FFA931", // vibrant orange-yellow - "CONTROL_NET": "#6EE7B7", // soft mint green - "IMAGE": "#64B5F6", // bright sky blue - "LATENT": "#FF9CF9", // light pink-purple - "MASK": "#81C784", // muted green - "MODEL": "#B39DDB", // light lavender-purple - "STYLE_MODEL": "#C2FFAE", // light green-yellow - "VAE": "#FF6E6E", // bright red - }; - - Object.assign(this.canvas.default_connection_color_byType, colors); - } - /** * Set up the app on the page */ @@ -614,8 +593,6 @@ class ComfyApp { const canvas = (this.canvas = new LGraphCanvas(canvasEl, this.graph)); this.ctx = canvasEl.getContext("2d"); - this.setupSlotColors(); - this.graph.start(); function resizeCanvas() { diff --git a/web/scripts/ui.js b/web/scripts/ui.js index 2c3bf87c7..c79caaa9b 100644 --- a/web/scripts/ui.js +++ b/web/scripts/ui.js @@ -1,6 +1,6 @@ import { api } from "./api.js"; -function $el(tag, propsOrChildren, children) { +export function $el(tag, propsOrChildren, children) { const split = tag.split("."); const element = document.createElement(split.shift()); element.classList.add(...split); @@ -114,6 +114,17 @@ class ComfySettingsDialog extends ComfyDialog { this.settings = []; } + getSettingValue(id, defaultValue) { + const settingId = "Comfy.Settings." + id; + const v = localStorage[settingId]; + return v == null ? defaultValue : JSON.parse(v); + } + + setSettingValue(id, value) { + const settingId = "Comfy.Settings." + id; + localStorage[settingId] = JSON.stringify(value); + } + addSetting({ id, name, type, defaultValue, onChange }) { if (!id) { throw new Error("Settings must have an ID"); @@ -142,7 +153,7 @@ class ComfySettingsDialog extends ComfyDialog { }; if (typeof type === "function") { - return type(name, setter); + return type(name, setter, value); } switch (type) { diff --git a/web/style.css b/web/style.css index d3168044f..7c3d7efa5 100644 --- a/web/style.css +++ b/web/style.css @@ -64,6 +64,12 @@ body { margin-bottom: 20px; /* Add some margin between the text and the close button*/ } +.comfy-modal select, +.comfy-modal input[type=button], +.comfy-modal input[type=checkbox] { + margin: 3px 3px 3px 4px; +} + .comfy-modal button { cursor: pointer; color: #aaaaaa; From 31dd6c0531367c40537e1fc866db31ac842cd5f4 Mon Sep 17 00:00:00 2001 From: comfyanonymous Date: Tue, 28 Mar 2023 01:42:34 -0400 Subject: [PATCH 3/9] Add way to specify listen ip with --listen. --- main.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/main.py b/main.py index 26bad1b8b..d82d1d6ed 100644 --- a/main.py +++ b/main.py @@ -12,7 +12,7 @@ if os.name == "nt": if __name__ == "__main__": if '--help' in sys.argv: print("Valid Command line Arguments:") - print("\t--listen\t\t\tListen on 0.0.0.0 so the UI can be accessed from other computers.") + print("\t--listen [ip]\t\t\tListen on ip or 0.0.0.0 if none given so the UI can be accessed from other computers.") print("\t--port 8188\t\t\tSet the listen port.") print("\t--dont-upcast-attention\t\tDisable upcasting of attention \n\t\t\t\t\tcan boost speed but increase the chances of black images.\n") print("\t--use-split-cross-attention\tUse the split cross attention optimization instead of the sub-quadratic one.\n\t\t\t\t\tIgnored when xformers is used.") @@ -92,11 +92,19 @@ if __name__ == "__main__": hijack_progress(server) threading.Thread(target=prompt_worker, daemon=True, args=(q,server,)).start() - if '--listen' in sys.argv: + try: address = '0.0.0.0' - else: + p_index = sys.argv.index('--listen') + try: + ip = sys.argv[p_index + 1] + if ip[:2] != '--': + address = ip + except: + pass + except: address = '127.0.0.1' + dont_print = False if '--dont-print-server' in sys.argv: dont_print = True From 1e0f2b232bd0b1a5dbd5c16f08e86a5d421a91aa Mon Sep 17 00:00:00 2001 From: Davemane42 Date: Tue, 28 Mar 2023 02:52:12 -0400 Subject: [PATCH 4/9] add unique_id to nodes hidden inputs @classmethod def INPUT_TYPES(cls): return { "hidden": {"unique_id": "UNIQUE_ID"}, } --- execution.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/execution.py b/execution.py index 3ca551db6..2b26a0f78 100644 --- a/execution.py +++ b/execution.py @@ -10,7 +10,7 @@ import gc import torch import nodes -def get_input_data(inputs, class_def, outputs={}, prompt={}, extra_data={}): +def get_input_data(inputs, class_def, unique_id, outputs={}, prompt={}, extra_data={}): valid_inputs = class_def.INPUT_TYPES() input_data_all = {} for x in inputs: @@ -34,6 +34,8 @@ def get_input_data(inputs, class_def, outputs={}, prompt={}, extra_data={}): if h[x] == "EXTRA_PNGINFO": if "extra_pnginfo" in extra_data: input_data_all[x] = extra_data['extra_pnginfo'] + if h[x] == "UNIQUE_ID": + input_data_all[x] = unique_id return input_data_all def recursive_execute(server, prompt, outputs, current_item, extra_data={}): @@ -55,7 +57,7 @@ def recursive_execute(server, prompt, outputs, current_item, extra_data={}): if input_unique_id not in outputs: executed += recursive_execute(server, prompt, outputs, input_unique_id, extra_data) - input_data_all = get_input_data(inputs, class_def, outputs, prompt, extra_data) + input_data_all = get_input_data(inputs, class_def, unique_id, outputs, prompt, extra_data) if server.client_id is not None: server.last_node_id = unique_id server.send_sync("executing", { "node": unique_id }, server.client_id) @@ -96,7 +98,7 @@ def recursive_output_delete_if_changed(prompt, old_prompt, outputs, current_item if unique_id in old_prompt and 'is_changed' in old_prompt[unique_id]: is_changed_old = old_prompt[unique_id]['is_changed'] if 'is_changed' not in prompt[unique_id]: - input_data_all = get_input_data(inputs, class_def, outputs) + input_data_all = get_input_data(inputs, class_def, unique_id, outputs) if input_data_all is not None: is_changed = class_def.IS_CHANGED(**input_data_all) prompt[unique_id]['is_changed'] = is_changed From 393084877c71dc7dc9549b0f3694b02060151917 Mon Sep 17 00:00:00 2001 From: Farid Safi Date: Tue, 28 Mar 2023 19:45:17 +0200 Subject: [PATCH 5/9] clean state when loading another workflow --- web/scripts/app.js | 7 +++++++ web/scripts/ui.js | 11 +++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/web/scripts/app.js b/web/scripts/app.js index 609cc4cf7..ddb829ab4 100644 --- a/web/scripts/app.js +++ b/web/scripts/app.js @@ -903,6 +903,13 @@ class ComfyApp { } } } + + /** + * Clean current state + */ + clean() { + this.nodeOutputs = {}; + } } export const app = new ComfyApp(); diff --git a/web/scripts/ui.js b/web/scripts/ui.js index c79caaa9b..7e73c1080 100644 --- a/web/scripts/ui.js +++ b/web/scripts/ui.js @@ -306,6 +306,7 @@ export class ComfyUI { style: { display: "none" }, parent: document.body, onchange: () => { + app.clean(); app.handleFile(fileInput.files[0]); }, }); @@ -388,8 +389,14 @@ export class ComfyUI { }), $el("button", { textContent: "Load", onclick: () => fileInput.click() }), $el("button", { textContent: "Refresh", onclick: () => app.refreshComboInNodes() }), - $el("button", { textContent: "Clear", onclick: () => app.graph.clear() }), - $el("button", { textContent: "Load Default", onclick: () => app.loadGraphData() }), + $el("button", { textContent: "Clear", onclick: () => { + app.clean(); + app.graph.clear(); + }}), + $el("button", { textContent: "Load Default", onclick: () => { + app.clean(); + app.loadGraphData(); + }}), ]); dragElement(this.menuContainer); From 40a377775e7e383c09418297637a0cc261ead96d Mon Sep 17 00:00:00 2001 From: Farid Safi Date: Tue, 28 Mar 2023 20:22:49 +0200 Subject: [PATCH 6/9] move clean to handleFile and loadGraphData functions --- web/scripts/app.js | 2 ++ web/scripts/ui.js | 6 +----- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/web/scripts/app.js b/web/scripts/app.js index ddb829ab4..cd7fb5d1b 100644 --- a/web/scripts/app.js +++ b/web/scripts/app.js @@ -721,6 +721,8 @@ class ComfyApp { * @param {*} graphData A serialized graph object */ loadGraphData(graphData) { + this.clean(); + if (!graphData) { graphData = defaultGraph; } diff --git a/web/scripts/ui.js b/web/scripts/ui.js index 7e73c1080..2aabd29e7 100644 --- a/web/scripts/ui.js +++ b/web/scripts/ui.js @@ -306,7 +306,6 @@ export class ComfyUI { style: { display: "none" }, parent: document.body, onchange: () => { - app.clean(); app.handleFile(fileInput.files[0]); }, }); @@ -393,10 +392,7 @@ export class ComfyUI { app.clean(); app.graph.clear(); }}), - $el("button", { textContent: "Load Default", onclick: () => { - app.clean(); - app.loadGraphData(); - }}), + $el("button", { textContent: "Load Default", onclick: () => app.loadGraphData() }), ]); dragElement(this.menuContainer); From 0d65cb17b77ff80f76a6fe8181860c1694158645 Mon Sep 17 00:00:00 2001 From: comfyanonymous Date: Tue, 28 Mar 2023 16:29:35 -0400 Subject: [PATCH 7/9] Fix ddim_uniform crashing with 37 steps. --- comfy/samplers.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/comfy/samplers.py b/comfy/samplers.py index 17201d9dc..4f61a8462 100644 --- a/comfy/samplers.py +++ b/comfy/samplers.py @@ -242,7 +242,10 @@ def ddim_scheduler(model, steps): sigs = [] ddim_timesteps = make_ddim_timesteps(ddim_discr_method="uniform", num_ddim_timesteps=steps, num_ddpm_timesteps=model.inner_model.inner_model.num_timesteps, verbose=False) for x in range(len(ddim_timesteps) - 1, -1, -1): - sigs.append(model.t_to_sigma(torch.tensor(ddim_timesteps[x]))) + ts = ddim_timesteps[x] + if ts > 999: + ts = 999 + sigs.append(model.t_to_sigma(torch.tensor(ts))) sigs += [0.0] return torch.FloatTensor(sigs) @@ -373,7 +376,7 @@ class KSampler: def set_steps(self, steps, denoise=None): self.steps = steps - if denoise is None: + if denoise is None or denoise > 0.9999: self.sigmas = self._calculate_sigmas(steps) else: new_steps = int(steps/denoise) From 3ed814b01fcad1cf95bfd3fde4c18d0ae8bc9b13 Mon Sep 17 00:00:00 2001 From: comfyanonymous Date: Tue, 28 Mar 2023 23:58:27 -0400 Subject: [PATCH 8/9] Fix colab. --- notebooks/comfyui_colab.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/notebooks/comfyui_colab.ipynb b/notebooks/comfyui_colab.ipynb index 5108ec830..276579c99 100644 --- a/notebooks/comfyui_colab.ipynb +++ b/notebooks/comfyui_colab.ipynb @@ -47,7 +47,7 @@ " !git pull\n", "\n", "!echo -= Install dependencies =-\n", - "!pip -q install xformers -r requirements.txt" + "!pip -q install xformers==0.0.16 -r requirements.txt" ] }, { From b2554bc4dd69c3f5ad965edbc5585c4ff4948458 Mon Sep 17 00:00:00 2001 From: comfyanonymous Date: Wed, 29 Mar 2023 02:24:37 -0400 Subject: [PATCH 9/9] Split VAE decode batches depending on free memory. --- comfy/sd.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/comfy/sd.py b/comfy/sd.py index d767d8671..2e1ae8409 100644 --- a/comfy/sd.py +++ b/comfy/sd.py @@ -439,9 +439,14 @@ class VAE: model_management.unload_model() self.first_stage_model = self.first_stage_model.to(self.device) try: - samples = samples_in.to(self.device) - pixel_samples = self.first_stage_model.decode(1. / self.scale_factor * samples) - pixel_samples = torch.clamp((pixel_samples + 1.0) / 2.0, min=0.0, max=1.0) + free_memory = model_management.get_free_memory(self.device) + batch_number = int((free_memory * 0.7) / (2562 * samples_in.shape[2] * samples_in.shape[3] * 64)) + batch_number = max(1, batch_number) + + pixel_samples = torch.empty((samples_in.shape[0], 3, round(samples_in.shape[2] * 8), round(samples_in.shape[3] * 8)), device="cpu") + for x in range(0, samples_in.shape[0], batch_number): + samples = samples_in[x:x+batch_number].to(self.device) + pixel_samples[x:x+batch_number] = torch.clamp((self.first_stage_model.decode(1. / self.scale_factor * samples) + 1.0) / 2.0, min=0.0, max=1.0).cpu() except model_management.OOM_EXCEPTION as e: print("Warning: Ran out of memory when regular VAE decoding, retrying with tiled VAE decoding.") pixel_samples = self.decode_tiled_(samples_in)