From d96ab6a1ae3fa46f23fd441dd9f4d0f6aa030a9c Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Sat, 13 May 2023 11:21:11 -0400 Subject: [PATCH] update directml --- cli/generate.py | 2 +- cli/modules/preview-models.py | 104 +++++++++++++++++-------- extensions-builtin/Lora/lora.py | 3 +- extensions-builtin/sd-webui-controlnet | 2 +- installer.py | 35 +++++---- modules/call_queue.py | 1 + modules/cmd_args.py | 3 +- modules/devices.py | 27 ++++--- modules/dml/__init__.py | 8 +- modules/shared.py | 10 +++ modules/ui.py | 4 +- 11 files changed, 127 insertions(+), 72 deletions(-) diff --git a/cli/generate.py b/cli/generate.py index ce4e0e750..5e617ace0 100755 --- a/cli/generate.py +++ b/cli/generate.py @@ -121,7 +121,7 @@ def sampler(params, options): # find sampler async def generate(prompt = None, options = None, quiet = False): # pylint: disable=redefined-outer-name - global sd + global sd # pylint: disable=global-statement if options: sd = Map(options) if prompt is not None: diff --git a/cli/modules/preview-models.py b/cli/modules/preview-models.py index 71b474d8b..493d080c4 100755 --- a/cli/modules/preview-models.py +++ b/cli/modules/preview-models.py @@ -11,7 +11,7 @@ from sdapi import get, post, close from grid import grid sys.path.append(os.path.join(os.path.dirname(__file__), '..')) -from generate import sd, generate +from generate import generate # pylint: disable=import-error default = 'sd-v15-runwayml.ckpt [cc6cb27103]' @@ -72,13 +72,12 @@ options = Map({ }) -async def models(params): - global sd +async def preview_models(params): data = await get('/sdapi/v1/sd-models') - all = [m['title'] for m in data] + allmodels = [m['title'] for m in data] models = [] excluded = [] - for m in all: # loop through all registered models + for m in allmodels: # loop through all registered models ok = True for e in exclude: # check if model is excluded if e in m: @@ -100,17 +99,16 @@ async def models(params): models = filtered log.info({ 'models preview' }) log.info({ 'models': len(models), 'excluded': len(excluded) }) - cmdflags = await get('/sdapi/v1/cmd-flags') opt = await get('/sdapi/v1/options') if params.output != '': - dir = params.output + folder = params.output else: - dir = os.path.abspath(os.path.join(cmdflags['hypernetwork_dir'], '..', 'Stable-diffusion')) - log.info({ 'output directory': dir }) + folder = os.path.abspath(os.path.join(opt['hypernetwork_dir'], '..', 'Stable-diffusion')) + log.info({ 'output directory': folder }) log.info({ 'total jobs': len(models) * options.generate.batch_size, 'per-model': options.generate.batch_size }) log.info(json.dumps(options, indent=2)) for model in models: - fn = os.path.join(dir, os.path.basename(model) + options.format) + fn = os.path.join(folder, os.path.basename(model) + options.format) if os.path.exists(fn) and len(params.input) == 0: # if model preview exists and not manually included log.info({ 'model preview exists': model }) continue @@ -122,8 +120,8 @@ async def models(params): images = [] labels = [] t0 = time.time() - for label, prompt in prompts: - options.generate.prompt = prompt + for label, p in prompts: + options.generate.prompt = p log.info({ 'model generating': model, 'label': label, 'prompt': options.generate.prompt }) data = await generate(options = options, quiet=True) if 'image' in data: @@ -139,7 +137,7 @@ async def models(params): t = t1 - t0 its = 1.0 * options.generate.steps * len(images) / t log.info({ 'model preview created': model, 'image': fn, 'images': len(images), 'grid': [image.width, image.height], 'time': round(t, 2), 'its': round(its, 2) }) - + opt = await get('/sdapi/v1/options') if opt['sd_model_checkpoint'] != default and not params.fixed: log.info({ 'model set default': default }) @@ -148,17 +146,17 @@ async def models(params): async def lora(params): - cmdflags = await get('/sdapi/v1/cmd-flags') - dir = cmdflags['lora_dir'] - if not os.path.exists(dir): - log.error({ 'lora directory not found': dir }) + opt = await get('/sdapi/v1/options') + folder = opt['lora_dir'] + if not os.path.exists(folder): + log.error({ 'lora directory not found': folder }) return - models1 = [f for f in Path(dir).glob('*.safetensors')] - models2 = [f for f in Path(dir).glob('*.ckpt')] + models1 = [f for f in Path(folder).glob('*.safetensors')] + models2 = [f for f in Path(folder).glob('*.ckpt')] models = [f.stem for f in models1 + models2] log.info({ 'loras': len(models) }) for model in models: - fn = os.path.join(dir, model + options.format) + fn = os.path.join(folder, model + options.format) if os.path.exists(fn) and len(params.input) == 0: # if model preview exists and not manually included log.info({ 'lora preview exists': model }) continue @@ -166,7 +164,7 @@ async def lora(params): labels = [] t0 = time.time() import re - keywords = re.sub('\d', '', model) + keywords = re.sub(r'\d', '', model) keywords = keywords.replace('-v', ' ').replace('-', ' ').strip().split(' ') keyword = '\"' + '\" \"'.join(keywords) + '\"' options.generate.prompt = prompt.replace('', keyword) @@ -188,16 +186,57 @@ async def lora(params): log.info({ 'lora preview created': model, 'image': fn, 'images': len(images), 'grid': [image.width, image.height], 'time': round(t, 2), 'its': round(its, 2) }) -async def hypernetwork(params): - cmdflags = await get('/sdapi/v1/cmd-flags') - dir = cmdflags['hypernetwork_dir'] - if not os.path.exists(dir): - log.error({ 'hypernetwork directory not found': dir }) +async def lyco(params): + opt = await get('/sdapi/v1/options') + folder = opt['lyco_dir'] + if not os.path.exists(folder): + log.error({ 'lyco directory not found': folder }) return - models = [f.stem for f in Path(dir).glob('*.pt')] - log.info({ 'loras': len(models) }) + models1 = [f for f in Path(folder).glob('*.safetensors')] + models2 = [f for f in Path(folder).glob('*.ckpt')] + models = [f.stem for f in models1 + models2] + log.info({ 'lycos': len(models) }) for model in models: - fn = os.path.join(dir, model + options.format) + fn = os.path.join(folder, model + options.format) + if os.path.exists(fn) and len(params.input) == 0: # if model preview exists and not manually included + log.info({ 'lyco preview exists': model }) + continue + images = [] + labels = [] + t0 = time.time() + import re + keywords = re.sub(r'\d', '', model) + keywords = keywords.replace('-v', ' ').replace('-', ' ').strip().split(' ') + keyword = '\"' + '\" \"'.join(keywords) + '\"' + options.generate.prompt = prompt.replace('', keyword) + options.generate.prompt = options.generate.prompt.replace('', '') + options.generate.prompt += f' ' + log.info({ 'lyco generating': model, 'keyword': keyword, 'prompt': options.generate.prompt }) + data = await generate(options = options, quiet=True) + if 'image' in data: + for img in data['image']: + images.append(img) + labels.append(keyword) + else: + log.error({ 'lyco': model, 'keyword': keyword, 'error': data }) + t1 = time.time() + image = grid(images = images, labels = labels, border = 8) + image.save(fn) + t = t1 - t0 + its = 1.0 * options.generate.steps * len(images) / t + log.info({ 'lyco preview created': model, 'image': fn, 'images': len(images), 'grid': [image.width, image.height], 'time': round(t, 2), 'its': round(its, 2) }) + + +async def hypernetwork(params): + opt = await get('/sdapi/v1/options') + folder = opt['hypernetwork_dir'] + if not os.path.exists(folder): + log.error({ 'hypernetwork directory not found': folder }) + return + models = [f.stem for f in Path(folder).glob('*.pt')] + log.info({ 'hypernetworks': len(models) }) + for model in models: + fn = os.path.join(folder, model + options.format) if os.path.exists(fn) and len(params.input) == 0: # if model preview exists and not manually included log.info({ 'hypernetwork preview exists': model }) continue @@ -225,8 +264,9 @@ async def hypernetwork(params): async def create_previews(params): - await models(params) + await preview_models(params) await lora(params) + await lyco(params) await hypernetwork(params) await close() @@ -236,5 +276,5 @@ if __name__ == '__main__': parser.add_argument('--output', type = str, default = '', required = False, help = 'output directory') parser.add_argument('--fixed', default = False, action='store_true', help = "do not change model") parser.add_argument('input', type = str, nargs = '*') - params = parser.parse_args() - asyncio.run(create_previews(params)) + args = parser.parse_args() + asyncio.run(create_previews(args)) diff --git a/extensions-builtin/Lora/lora.py b/extensions-builtin/Lora/lora.py index f80dd69a8..50dd59440 100644 --- a/extensions-builtin/Lora/lora.py +++ b/extensions-builtin/Lora/lora.py @@ -191,7 +191,8 @@ def load_lora(name, filename): warnings += 1 if len(keys_failed_to_match) > 0: - shared.log.warning(f"Failed to match keys when loading Lora {filename}: {keys_failed_to_match}") + shared.log.warning(f"Lora failed to match keys when: {filename} {len(keys_failed_to_match)}") + shared.log.info(f"Try using LyCORIS instead") warnings += 1 return lora diff --git a/extensions-builtin/sd-webui-controlnet b/extensions-builtin/sd-webui-controlnet index c9c8ca6ee..eaf993908 160000 --- a/extensions-builtin/sd-webui-controlnet +++ b/extensions-builtin/sd-webui-controlnet @@ -1 +1 @@ -Subproject commit c9c8ca6eee86e0fa4dec9f5e62a5f34e38ae1707 +Subproject commit eaf993908310ac0324f3a024438b8c47196d7056 diff --git a/installer.py b/installer.py index 01215569c..5b974f1fb 100644 --- a/installer.py +++ b/installer.py @@ -219,13 +219,13 @@ def check_torch(): log.debug(f'Torch allowed: cuda={allow_cuda} rocm={allow_rocm} ipex={allow_ipex} diml={allow_directml}') if allow_cuda and (shutil.which('nvidia-smi') is not None or os.path.exists(os.path.join(os.environ.get('SystemRoot') or r'C:\Windows', 'System32', 'nvidia-smi.exe'))): log.info('nVidia CUDA toolkit detected') - torch_command = os.environ.get('TORCH_COMMAND', 'torch torchaudio torchvision==0.15.1 --index-url https://download.pytorch.org/whl/cu118') + torch_command = os.environ.get('TORCH_COMMAND', 'torch torchvision==0.15.1 --index-url https://download.pytorch.org/whl/cu118') xformers_package = os.environ.get('XFORMERS_PACKAGE', 'xformers==0.0.17' if opts.get('cross_attention_optimization', '') == 'xFormers' else 'none') - elif allow_rocm and (shutil.which('rocminfo') is not None or os.path.exists('/opt/rocm/bin/rocminfo')): + elif allow_rocm and (shutil.which('rocminfo') is not None or os.path.exists('/opt/rocm/bin/rocminfo') or os.path.exists('/dev/kfd')): log.info('AMD ROCm toolkit detected') os.environ.setdefault('HSA_OVERRIDE_GFX_VERSION', '10.3.0') os.environ.setdefault('PYTORCH_HIP_ALLOC_CONF', 'garbage_collection_threshold:0.9,max_split_size_mb:512') - torch_command = os.environ.get('TORCH_COMMAND', 'torch==2.0.0 torchvision==0.15.1 torchaudio --index-url https://download.pytorch.org/whl/rocm5.4.2') + torch_command = os.environ.get('TORCH_COMMAND', 'torch==2.0.0 torchvision==0.15.1 --index-url https://download.pytorch.org/whl/rocm5.4.2') xformers_package = os.environ.get('XFORMERS_PACKAGE', 'none') elif allow_ipex and (shutil.which('sycl-ls') is not None or os.path.exists('/opt/intel/oneapi') or args.use_ipex): log.info('Intel OneAPI Toolkit detected') @@ -235,30 +235,30 @@ def check_torch(): machine = platform.machine() if allow_directml and ('arm' not in machine and 'aarch' not in machine and args.use_directml): log.info('Using DirectML Backend') - torch_command = os.environ.get('TORCH_COMMAND', 'torch==2.0.0 torchaudio torchvision==0.15.1 torch-directml') + torch_command = os.environ.get('TORCH_COMMAND', 'torch-directml') xformers_package = os.environ.get('XFORMERS_PACKAGE', 'none') if 'torch' in torch_command and not args.version: - install(torch_command, 'torch torchvision torchaudio') + install(torch_command, 'torch torchvision') else: log.info('Using CPU-only Torch') - torch_command = os.environ.get('TORCH_COMMAND', 'torch torchaudio torchvision==0.15.1') + torch_command = os.environ.get('TORCH_COMMAND', 'torch torchvision==0.15.1') xformers_package = os.environ.get('XFORMERS_PACKAGE', 'none') if 'torch' in torch_command and not args.version: - install(torch_command, 'torch torchvision torchaudio') + install(torch_command, 'torch torchvision') if args.skip_torch: log.info('Skipping Torch tests') else: try: import torch log.info(f'Torch {torch.__version__}') - if args.use_ipex: + if args.use_ipex and allow_ipex: import intel_extension_for_pytorch as ipex # pylint: disable=import-error, unused-import log.info(f'Torch backend: Intel OneAPI {torch.__version__}') log.info(f'Torch detected GPU: {torch.xpu.get_device_name("xpu")} VRAM {round(torch.xpu.get_device_properties("xpu").total_memory / 1024 / 1024)}') - elif torch.cuda.is_available(): - if torch.version.cuda: + elif torch.cuda.is_available() and (allow_cuda or allow_rocm): + if torch.version.cuda and allow_cuda: log.info(f'Torch backend: nVidia CUDA {torch.version.cuda} cuDNN {torch.backends.cudnn.version() if torch.backends.cudnn.is_available() else "N/A"}') - elif torch.version.hip: + elif torch.version.hip and allow_rocm: log.info(f'Torch backend: AMD ROCm HIP {torch.version.hip}') else: log.warning('Unknown Torch backend') @@ -266,12 +266,13 @@ def check_torch(): log.info(f'Torch detected GPU: {torch.cuda.get_device_name(device)} VRAM {round(torch.cuda.get_device_properties(device).total_memory / 1024 / 1024)} Arch {torch.cuda.get_device_capability(device)} Cores {torch.cuda.get_device_properties(device).multi_processor_count}') else: try: - import torch_directml # pylint: disable=import-error - import pkg_resources - version = pkg_resources.get_distribution("torch-directml") - log.info(f'Torch backend: DirectML ({version})') - for i in range(0, torch_directml.device_count()): - log.info(f'Torch detected GPU: {torch_directml.device_name(i)}') + if args.use_directml and allow_directml: + import torch_directml # pylint: disable=import-error + import pkg_resources + version = pkg_resources.get_distribution("torch-directml") + log.info(f'Torch backend: DirectML ({version})') + for i in range(0, torch_directml.device_count()): + log.info(f'Torch detected GPU: {torch_directml.device_name(i)}') except: log.warning("Torch reports CUDA not available") except Exception as e: diff --git a/modules/call_queue.py b/modules/call_queue.py index c32e856fa..f5f4c935a 100644 --- a/modules/call_queue.py +++ b/modules/call_queue.py @@ -34,6 +34,7 @@ def wrap_gradio_gpu_call(func, extra_outputs=None): progress.record_results(id_task, res) except Exception as e: shared.log.error(f"Exception: {e}") + errors.display(e, 'gradio call') res[-1] = f"
{html.escape(str(e))}
" finally: progress.finish_task(id_task) diff --git a/modules/cmd_args.py b/modules/cmd_args.py index 2316dce03..295437795 100644 --- a/modules/cmd_args.py +++ b/modules/cmd_args.py @@ -7,7 +7,8 @@ parser._optionals = parser.add_argument_group('Other options') # pylint: disable group = parser.add_argument_group('Server options') # main server args -group.add_argument("--config", type=str, default=os.path.join(data_path, 'config.json'), help="Use specific configuration file, default: %(default)s") +group.add_argument("--config", type=str, default=os.path.join(data_path, 'config.json'), help="Use specific server configuration file, default: %(default)s") +group.add_argument("--ui-config", type=str, default=os.path.join(data_path, 'ui-config.json'), help="Use specific UI configuration file, default: %(default)s") group.add_argument("--medvram", action='store_true', help="Split model stages and keep only active part in VRAM, default: %(default)s") group.add_argument("--lowvram", action='store_true', help="Split model components and keep only active part in VRAM, default: %(default)s") group.add_argument("--ckpt", type=str, default=None, help="Path to model checkpoint to load immediately, default: %(default)s") diff --git a/modules/devices.py b/modules/devices.py index 17ea93803..757cd1538 100644 --- a/modules/devices.py +++ b/modules/devices.py @@ -30,27 +30,22 @@ def get_cuda_device_string(): return "cuda" -def get_dml_device_string(): - if shared.cmd_opts.device_id is not None: - return f"privateuseone:{shared.cmd_opts.device_id}" - return "privateuseone:0" - - def get_optimal_device_name(): if shared.cmd_opts.use_ipex: return "xpu" - elif torch.cuda.is_available(): + elif torch.cuda.is_available() and not shared.cmd_opts.use_directml: return get_cuda_device_string() if has_mps(): return "mps" - try: - import torch_directml # pylint: disable=import-error + if shared.cmd_opts.use_directml: + import torch_directml if torch_directml.is_available(): - return get_dml_device_string() + torch.cuda.is_available = lambda: False + if shared.cmd_opts.device_id is not None: + return f"privateuseone:{shared.cmd_opts.device_id}" + return torch_directml.device() else: return "cpu" - except: - return "cpu" def get_optimal_device(): @@ -120,6 +115,8 @@ def set_cuda_params(): global dtype, dtype_vae, dtype_unet, unet_needs_upcast # pylint: disable=global-statement # set dtype ok = test_fp16() + if shared.cmd_opts.use_directml: + shared.opts.no_half = True if ok and shared.opts.cuda_dtype == 'FP32': shared.log.info('CUDA FP16 test passed but desired mode is set to FP32') if shared.opts.cuda_dtype == 'FP16' and ok: @@ -187,10 +184,12 @@ def autocast(disable=False): def without_autocast(disable=False): + if disable: + return contextlib.nullcontext() if shared.cmd_opts.use_ipex: - return torch.autocast("xpu", enabled=False) if torch.is_autocast_enabled() and not disable else contextlib.nullcontext() + return torch.autocast("xpu", enabled=False) if torch.is_autocast_enabled() else contextlib.nullcontext() else: - return torch.autocast("cuda", enabled=False) if torch.is_autocast_enabled() and not disable else contextlib.nullcontext() + return torch.autocast("cuda", enabled=False) if torch.is_autocast_enabled() else contextlib.nullcontext() class NansException(Exception): diff --git a/modules/dml/__init__.py b/modules/dml/__init__.py index b77db18d0..8affc859b 100644 --- a/modules/dml/__init__.py +++ b/modules/dml/__init__.py @@ -6,8 +6,8 @@ import modules.dml.hijack from .optimizer.unknown import UnknownOptimizer class DirectML(): - def get_optimizer(device: torch.device): - assert(device.type == 'privateuseone') + def get_optimizer(self, device: torch.device): + assert device.type == 'privateuseone' try: device_name = torch_directml.device_name(device.index) if 'NVIDIA' in device_name or 'GeForce' in device_name: @@ -22,8 +22,8 @@ class DirectML(): except: return UnknownOptimizer - def memory_stats(device: torch.device): - optimizer = DirectML.get_optimizer(device) + def memory_stats(self, device: torch.device): + optimizer = DirectML.get_optimizer(self, device) return optimizer.memory_stats(device.index) # Alternative of torch.cuda for DirectML. diff --git a/modules/shared.py b/modules/shared.py index 964ef814f..6c35c9252 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -705,6 +705,16 @@ def restart_server(restart=True): log.info('Server will restart') +def restore_defaults(restart=True): + if os.path.exists(cmd_opts.config): + log.info('Restoring server defaults') + os.remove(cmd_opts.config) + if os.path.exists(cmd_opts.ui_config): + log.info('Restoring UI defaults') + os.remove(cmd_opts.ui_config) + restart_server(True) + + def listfiles(dirname): filenames = [os.path.join(dirname, x) for x in sorted(os.listdir(dirname), key=str.lower) if not x.startswith(".")] return [file for file in filenames if os.path.isfile(file)] diff --git a/modules/ui.py b/modules/ui.py index 0eb6b6759..518665b01 100644 --- a/modules/ui.py +++ b/modules/ui.py @@ -1322,6 +1322,7 @@ def create_ui(): with gr.Blocks(analytics_enabled=False) as settings_interface: with gr.Row(): settings_submit = gr.Button(value="Apply settings", variant='primary', elem_id="settings_submit") + defaults_submit = gr.Button(value="Restore defaults", variant='primary', elem_id="defaults_submit") restart_submit = gr.Button(value="Restart server", variant='primary', elem_id="restart_submit") shutdown_submit = gr.Button(value="Shutdown server", variant='primary', elem_id="shutdown_submit") preview_theme = gr.Button(value="Preview theme", variant='primary', elem_id="settings_preview_theme") @@ -1453,6 +1454,7 @@ def create_ui(): inputs=components, outputs=[text_settings, result], ) + defaults_submit.click(fn=lambda x: shared.restore_defaults(restart=True), _js="restart_reload") restart_submit.click(fn=lambda x: shared.restart_server(restart=True), _js="restart_reload") shutdown_submit.click(fn=lambda x: shared.restart_server(restart=False), _js="restart_reload") @@ -1531,7 +1533,7 @@ def create_ui(): ] ) - ui_config_file = cmd_opts.ui_config_file + ui_config_file = cmd_opts.ui_config ui_settings = {} settings_count = len(ui_settings) error_loading = False