Merge branch 'master' of https://github.com/vladmandic/automatic into settings-improvements

This commit is contained in:
Alex Heller
2023-04-30 05:11:08 +02:00
27 changed files with 664 additions and 28 deletions
+6 -9
View File
@@ -24,15 +24,12 @@ jobs:
python-version: 3.10.6
cache: pip
cache-dependency-path: requirements.txt
- name: Install PyLint
run: |
- name: Test Startup
run: |
export COMMANDLINE_ARGS="--debug --test"
python launch.py
- name: Linting
run: |
python -m pip install --upgrade pip
pip install pylint
# This lets PyLint check to see if it can resolve imports
- name: Install dependencies
run: |
export COMMANDLINE_ARGS="--skip-torch-cuda-test --exit"
python launch.py
- name: Analysing the code with pylint
run: |
pylint $(git ls-files '*.py')
+3
View File
@@ -11,6 +11,7 @@ fail-under=10
ignore=CVS
ignore-paths=^repositories/.*$,
^extensions/.*$,
^extensions-builtin/.*$,
/usr/lib/.*$,
ignore-patterns=
ignored-modules=
@@ -141,6 +142,8 @@ disable=raw-checker-failed,
consider-using-dict-items,
dangerous-default-value,
unnecessary-dunder-call,
invalid-name,
R0801,
enable=c-extension-no-member
[METHOD_ARGS]
+2 -3
View File
@@ -4,10 +4,7 @@
Stuff to be fixed...
- Run VAE with hires at 1280
- Transformers version
- Move Restart Server from WebUI to Launch and reload modules
- Follow-up on `p.script_args`
- Mdularize `cli` scripts
## Features
@@ -16,9 +13,11 @@ Stuff to be added...
- Update README
- Add Gradio theme maker
- Transformers version
- Create new GitHub hooks/actions for CI/CD
- Redo Extensions tab: see <https://vladmandic.github.io/sd-extension-manager/pages/extensions.html>
- Stream-load models as option for slow storage
- Auto-test `torch.layer_norm` for FP16
## Investigate
+2 -2
View File
@@ -105,8 +105,8 @@ function setupImageForLightbox(e) {
var isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1
var event = isFirefox ? 'mousedown' : 'click'
e.addEventListener(event, function (evt) {
if(!opts.js_modal_lightbox || evt.button != 0) return;
modalZoomSet(gradioApp().getElementById('modalImage'), opts.js_modal_lightbox_initially_zoomed)
if (evt.button != 0) return;
modalZoomSet(gradioApp().getElementById('modalImage'), true)
evt.preventDefault()
showModal(evt)
}, true);
+4
View File
@@ -97,5 +97,9 @@ if __name__ == "__main__":
setup.log.info(f"Server arguments: {sys.argv[1:]}")
setup.log.debug('Starting WebUI')
logging.disable(logging.INFO)
if args.test:
setup.log.info("Test only")
import webui
exit(0)
import webui
webui.webui()
+1
View File
@@ -74,6 +74,7 @@ def compatibility_args(opts, args):
parser.add_argument("--sub-quad-q-chunk-size", help=argparse.SUPPRESS, default=opts.sub_quad_q_chunk_size)
parser.add_argument("--sub-quad-kv-chunk-size", help=argparse.SUPPRESS, default=opts.sub_quad_kv_chunk_size)
parser.add_argument("--sub-quad-chunk-threshold", help=argparse.SUPPRESS, default=opts.sub_quad_chunk_threshold)
parser.add_argument('--debug', default = False, action='store_true', help = "Run installer with debug logging, default: %(default)s")
opts.use_old_emphasis_implementation = False
opts.use_old_karras_scheduler_sigmas = False
+15 -1
View File
@@ -27,12 +27,26 @@ def get_cuda_device_string():
return "cuda"
def get_dml_device_string():
from modules import shared
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 torch.cuda.is_available():
return get_cuda_device_string()
if has_mps():
return "mps"
return "cpu"
try:
import torch_directml
if torch_directml.is_available():
return get_dml_device_string()
else:
return "cpu"
except:
return "cpu"
def get_optimal_device():
+30
View File
@@ -0,0 +1,30 @@
import torch
import torch_directml
import modules.dml.hijack
from .optimizer.unknown import UnknownOptimizer
class DirectML():
def get_optimizer(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:
from .optimizer.nvidia import nVidiaOptimizer as optimizer
elif 'AMD' in device_name or 'Radeon' in device_name:
from .optimizer.amd import AMDOptimizer as optimizer
elif 'Intel' in device_name:
from .optimizer.intel import IntelOptimizer as optimizer
else:
return UnknownOptimizer
return optimizer
except:
return UnknownOptimizer
def memory_stats(device: torch.device):
optimizer = DirectML.get_optimizer(device)
return optimizer.memory_stats(device.index)
# Alternative of torch.cuda for DirectML.
torch.dml = DirectML
+5
View File
@@ -0,0 +1,5 @@
import modules.dml.hijack.kdiffusion
import modules.dml.hijack.stablediffusion
import modules.dml.hijack.torch
import modules.dml.hijack.realesrgan_model
import modules.dml.hijack.plms
+89
View File
@@ -0,0 +1,89 @@
import torch
from tqdm.auto import tqdm
from modules.shared import device
from k_diffusion import sampling
def dpm_solver_adaptive(self, x, t_start, t_end, order=3, rtol=0.05, atol=0.0078, h_init=0.05, pcoeff=0., icoeff=1., dcoeff=0., accept_safety=0.81, eta=0., s_noise=1., noise_sampler=None):
noise_sampler = sampling.default_noise_sampler(x) if noise_sampler is None else noise_sampler
if order not in {2, 3}:
raise ValueError('order should be 2 or 3')
forward = t_end > t_start
if not forward and eta:
raise ValueError('eta must be 0 for reverse sampling')
h_init = abs(h_init) * (1 if forward else -1)
atol = torch.tensor(atol).to(device)
rtol = torch.tensor(rtol).to(device)
s = t_start
x_prev = x
accept = True
pid = sampling.PIDStepSizeController(h_init, pcoeff, icoeff, dcoeff, 1.5 if eta else order, accept_safety)
info = {'steps': 0, 'nfe': 0, 'n_accept': 0, 'n_reject': 0}
while s < t_end - 1e-5 if forward else s > t_end + 1e-5:
eps_cache = {}
t = torch.minimum(t_end, s + pid.h) if forward else torch.maximum(t_end, s + pid.h)
if eta:
sd, su = sampling.get_ancestral_step(self.sigma(s), self.sigma(t), eta)
t_ = torch.minimum(t_end, self.t(sd))
su = (self.sigma(t) ** 2 - self.sigma(t_) ** 2) ** 0.5
else:
t_, su = t, 0.
eps, eps_cache = self.eps(eps_cache, 'eps', x, s)
denoised = x - self.sigma(s) * eps
if order == 2:
x_low, eps_cache = self.dpm_solver_1_step(x, s, t_, eps_cache=eps_cache)
x_high, eps_cache = self.dpm_solver_2_step(x, s, t_, eps_cache=eps_cache)
else:
x_low, eps_cache = self.dpm_solver_2_step(x, s, t_, r1=1 / 3, eps_cache=eps_cache)
x_high, eps_cache = self.dpm_solver_3_step(x, s, t_, eps_cache=eps_cache)
delta = torch.maximum(atol, rtol * torch.maximum(x_low.abs(), x_prev.abs()))
error = torch.linalg.norm((x_low - x_high) / delta) / x.numel() ** 0.5
accept = pid.propose_step(error)
if accept:
x_prev = x_low
x = x_high + su * s_noise * noise_sampler(self.sigma(s), self.sigma(t))
s = t
info['n_accept'] += 1
else:
info['n_reject'] += 1
info['nfe'] += order
info['steps'] += 1
if self.info_callback is not None:
self.info_callback({'x': x, 'i': info['steps'] - 1, 't': s, 't_up': s, 'denoised': denoised, 'error': error, 'h': pid.h, **info})
return x, info
@torch.no_grad()
def sample_dpm_fast(model, x, sigma_min, sigma_max, n, extra_args=None, callback=None, disable=None, eta=0., s_noise=1., noise_sampler=None):
"""DPM-Solver-Fast (fixed step size). See https://arxiv.org/abs/2206.00927."""
if sigma_min <= 0 or sigma_max <= 0:
raise ValueError('sigma_min and sigma_max must not be 0')
with tqdm(total=n, disable=disable) as pbar:
dpm_solver = sampling.DPMSolver(model, extra_args, eps_callback=pbar.update)
if callback is not None:
dpm_solver.info_callback = lambda info: callback({'sigma': dpm_solver.sigma(info['t']), 'sigma_hat': dpm_solver.sigma(info['t_up']), **info})
return dpm_solver.dpm_solver_fast(x, dpm_solver.t(torch.tensor(sigma_max).to(device)), dpm_solver.t(torch.tensor(sigma_min).to(device)), n, eta, s_noise, noise_sampler)
@torch.no_grad()
def sample_dpm_adaptive(model, x, sigma_min, sigma_max, extra_args=None, callback=None, disable=None, order=3, rtol=0.05, atol=0.0078, h_init=0.05, pcoeff=0., icoeff=1., dcoeff=0., accept_safety=0.81, eta=0., s_noise=1., noise_sampler=None, return_info=False):
"""DPM-Solver-12 and 23 (adaptive step size). See https://arxiv.org/abs/2206.00927."""
if sigma_min <= 0 or sigma_max <= 0:
raise ValueError('sigma_min and sigma_max must not be 0')
with tqdm(disable=disable) as pbar:
dpm_solver = sampling.DPMSolver(model, extra_args, eps_callback=pbar.update)
if callback is not None:
dpm_solver.info_callback = lambda info: callback({'sigma': dpm_solver.sigma(info['t']), 'sigma_hat': dpm_solver.sigma(info['t_up']), **info})
x, info = dpm_solver.dpm_solver_adaptive(x, dpm_solver.t(torch.tensor(sigma_max).to(device)), dpm_solver.t(torch.tensor(sigma_min).to(device)), order, rtol, atol, h_init, pcoeff, icoeff, dcoeff, accept_safety, eta, s_noise, noise_sampler)
if return_info:
return x, info
return x
sampling.DPMSolver.dpm_solver_adaptive = dpm_solver_adaptive
sampling.sample_dpm_fast = sample_dpm_fast
sampling.sample_dpm_adaptive = sample_dpm_adaptive
+91
View File
@@ -0,0 +1,91 @@
import torch
from ldm.models.diffusion.ddim import noise_like
import modules.sd_hijack_inpainting as plms_hijack
@torch.no_grad()
def p_sample_plms(self, x, c, t, index, repeat_noise=False, use_original_steps=False, quantize_denoised=False,
temperature=1., noise_dropout=0., score_corrector=None, corrector_kwargs=None,
unconditional_guidance_scale=1., unconditional_conditioning=None, old_eps=None, t_next=None, dynamic_threshold=None):
b, *_, device = *x.shape, x.device
def get_model_output(x, t):
if unconditional_conditioning is None or unconditional_guidance_scale == 1.:
e_t = self.model.apply_model(x, t, c)
else:
x_in = torch.cat([x] * 2)
t_in = torch.cat([t] * 2)
if isinstance(c, dict):
assert isinstance(unconditional_conditioning, dict)
c_in = dict()
for k in c:
if isinstance(c[k], list):
c_in[k] = [
torch.cat([unconditional_conditioning[k][i], c[k][i]])
for i in range(len(c[k]))
]
else:
c_in[k] = torch.cat([unconditional_conditioning[k], c[k]])
else:
c_in = torch.cat([unconditional_conditioning, c])
e_t_uncond, e_t = self.model.apply_model(x_in, t_in, c_in).chunk(2)
e_t = e_t_uncond + unconditional_guidance_scale * (e_t - e_t_uncond)
if score_corrector is not None:
assert self.model.parameterization == "eps"
e_t = score_corrector.modify_score(self.model, e_t, x, t, c, **corrector_kwargs)
return e_t
alphas = self.model.alphas_cumprod if use_original_steps else self.ddim_alphas
alphas_prev = self.model.alphas_cumprod_prev if use_original_steps else self.ddim_alphas_prev
sqrt_one_minus_alphas = self.model.sqrt_one_minus_alphas_cumprod if use_original_steps else self.ddim_sqrt_one_minus_alphas
sigmas = self.model.ddim_sigmas_for_original_num_steps if use_original_steps else self.ddim_sigmas
def get_x_prev_and_pred_x0(e_t, index):
# select parameters corresponding to the currently considered timestep
print(alphas[index]) # DML Solution: PLMS Sampling does not work without this print.
a_t = torch.full((b, 1, 1, 1), alphas[index], device=device)
a_prev = torch.full((b, 1, 1, 1), alphas_prev[index], device=device)
sigma_t = torch.full((b, 1, 1, 1), sigmas[index], device=device)
sqrt_one_minus_at = torch.full((b, 1, 1, 1), sqrt_one_minus_alphas[index],device=device)
# current prediction for x_0
pred_x0 = (x - sqrt_one_minus_at * e_t) / a_t.sqrt()
if quantize_denoised:
pred_x0, _, *_ = self.model.first_stage_model.quantize(pred_x0)
if dynamic_threshold is not None:
from ldm.models.diffusion.sampling_util import norm_thresholding
pred_x0 = norm_thresholding(pred_x0, dynamic_threshold)
# direction pointing to x_t
dir_xt = (1. - a_prev - sigma_t**2).sqrt() * e_t
noise = sigma_t * noise_like(x.shape, device, repeat_noise) * temperature
if noise_dropout > 0.:
noise = torch.nn.functional.dropout(noise, p=noise_dropout)
x_prev = a_prev.sqrt() * pred_x0 + dir_xt + noise
return x_prev, pred_x0
e_t = get_model_output(x, t)
if len(old_eps) == 0:
# Pseudo Improved Euler (2nd order)
x_prev, pred_x0 = get_x_prev_and_pred_x0(e_t, index)
e_t_next = get_model_output(x_prev, t_next)
e_t_prime = (e_t + e_t_next) / 2
elif len(old_eps) == 1:
# 2nd order Pseudo Linear Multistep (Adams-Bashforth)
e_t_prime = (3 * e_t - old_eps[-1]) / 2
elif len(old_eps) == 2:
# 3nd order Pseudo Linear Multistep (Adams-Bashforth)
e_t_prime = (23 * e_t - 16 * old_eps[-1] + 5 * old_eps[-2]) / 12
elif len(old_eps) >= 3:
# 4nd order Pseudo Linear Multistep (Adams-Bashforth)
e_t_prime = (55 * e_t - 59 * old_eps[-1] + 37 * old_eps[-2] - 9 * old_eps[-3]) / 24
x_prev, pred_x0 = get_x_prev_and_pred_x0(e_t_prime, index)
return x_prev, pred_x0, e_t
plms_hijack.p_sample_plms = p_sample_plms
+69
View File
@@ -0,0 +1,69 @@
import math
import torch
from realesrgan import RealESRGANer
# DML Solution: Some tensors turn 0 after Extended Slices. Move output to cpu and get it back.
def tile_process(self):
batch, channel, height, width = self.img.shape
output_height = height * self.scale
output_width = width * self.scale
output_shape = (batch, channel, output_height, output_width)
# start with black image
self.output = self.img.new_zeros(output_shape, device='cpu')
tiles_x = math.ceil(width / self.tile_size)
tiles_y = math.ceil(height / self.tile_size)
# loop over all tiles
for y in range(tiles_y):
for x in range(tiles_x):
# extract tile from input image
ofs_x = x * self.tile_size
ofs_y = y * self.tile_size
# input tile area on total image
input_start_x = ofs_x
input_end_x = min(ofs_x + self.tile_size, width)
input_start_y = ofs_y
input_end_y = min(ofs_y + self.tile_size, height)
# input tile area on total image with padding
input_start_x_pad = max(input_start_x - self.tile_pad, 0)
input_end_x_pad = min(input_end_x + self.tile_pad, width)
input_start_y_pad = max(input_start_y - self.tile_pad, 0)
input_end_y_pad = min(input_end_y + self.tile_pad, height)
# input tile dimensions
input_tile_width = input_end_x - input_start_x
input_tile_height = input_end_y - input_start_y
tile_idx = y * tiles_x + x + 1
input_tile = self.img[:, :, input_start_y_pad:input_end_y_pad, input_start_x_pad:input_end_x_pad]
# upscale tile
try:
with torch.no_grad():
output_tile = self.model(input_tile)
output_tile = output_tile.cpu()
except RuntimeError as error:
print('Error', error)
print(f'\tTile {tile_idx}/{tiles_x * tiles_y}')
# output tile area on total image
output_start_x = input_start_x * self.scale
output_end_x = input_end_x * self.scale
output_start_y = input_start_y * self.scale
output_end_y = input_end_y * self.scale
# output tile area without padding
output_start_x_tile = (input_start_x - input_start_x_pad) * self.scale
output_end_x_tile = output_start_x_tile + input_tile_width * self.scale
output_start_y_tile = (input_start_y - input_start_y_pad) * self.scale
output_end_y_tile = output_start_y_tile + input_tile_height * self.scale
# put tile into output image
self.output[:, :, output_start_y:output_end_y,
output_start_x:output_end_x] = output_tile[:, :, output_start_y_tile:output_end_y_tile,
output_start_x_tile:output_end_x_tile]
self.output = self.output.to(self.device)
RealESRGANer.tile_process = tile_process
+80
View File
@@ -0,0 +1,80 @@
import torch
from ldm.models.diffusion.ddim import DDIMSampler
from ldm.modules.diffusionmodules.util import noise_like
@torch.no_grad()
def p_sample_ddim(self, x, c, t, index, repeat_noise=False, use_original_steps=False, quantize_denoised=False,
temperature=1., noise_dropout=0., score_corrector=None, corrector_kwargs=None,
unconditional_guidance_scale=1., unconditional_conditioning=None,
dynamic_threshold=None):
b, *_, device = *x.shape, x.device
if unconditional_conditioning is None or unconditional_guidance_scale == 1.:
model_output = self.model.apply_model(x, t, c)
else:
x_in = torch.cat([x] * 2)
t_in = torch.cat([t] * 2)
if isinstance(c, dict):
assert isinstance(unconditional_conditioning, dict)
c_in = dict()
for k in c:
if isinstance(c[k], list):
c_in[k] = [torch.cat([
unconditional_conditioning[k][i],
c[k][i]]) for i in range(len(c[k]))]
else:
c_in[k] = torch.cat([
unconditional_conditioning[k],
c[k]])
elif isinstance(c, list):
c_in = list()
assert isinstance(unconditional_conditioning, list)
for i in range(len(c)):
c_in.append(torch.cat([unconditional_conditioning[i], c[i]]))
else:
c_in = torch.cat([unconditional_conditioning, c])
model_uncond, model_t = self.model.apply_model(x_in, t_in, c_in).chunk(2)
model_output = model_uncond + unconditional_guidance_scale * (model_t - model_uncond)
if self.model.parameterization == "v":
e_t = self.model.predict_eps_from_z_and_v(x, t, model_output)
else:
e_t = model_output
if score_corrector is not None:
assert self.model.parameterization == "eps", 'not implemented'
e_t = score_corrector.modify_score(self.model, e_t, x, t, c, **corrector_kwargs)
alphas = self.model.alphas_cumprod if use_original_steps else self.ddim_alphas
alphas_prev = self.model.alphas_cumprod_prev if use_original_steps else self.ddim_alphas_prev
sqrt_one_minus_alphas = self.model.sqrt_one_minus_alphas_cumprod if use_original_steps else self.ddim_sqrt_one_minus_alphas
sigmas = self.model.ddim_sigmas_for_original_num_steps if use_original_steps else self.ddim_sigmas
# select parameters corresponding to the currently considered timestep
print(alphas[index]) # DML Solution: DDIM Sampling does not work without this print.
a_t = torch.full((b, 1, 1, 1), alphas[index], device=device)
a_prev = torch.full((b, 1, 1, 1), alphas_prev[index], device=device)
sigma_t = torch.full((b, 1, 1, 1), sigmas[index], device=device)
sqrt_one_minus_at = torch.full((b, 1, 1, 1), sqrt_one_minus_alphas[index],device=device)
# current prediction for x_0
if self.model.parameterization != "v":
pred_x0 = (x - sqrt_one_minus_at * e_t) / a_t.sqrt()
else:
pred_x0 = self.model.predict_start_from_z_and_v(x, t, model_output)
if quantize_denoised:
pred_x0, _, *_ = self.model.first_stage_model.quantize(pred_x0)
if dynamic_threshold is not None:
raise NotImplementedError()
# direction pointing to x_t
dir_xt = (1. - a_prev - sigma_t**2).sqrt() * e_t
noise = sigma_t * noise_like(x.shape, device, repeat_noise) * temperature
if noise_dropout > 0.:
noise = torch.nn.functional.dropout(noise, p=noise_dropout)
x_prev = a_prev.sqrt() * pred_x0 + dir_xt + noise
return x_prev, pred_x0
DDIMSampler.p_sample_ddim = p_sample_ddim
+5
View File
@@ -0,0 +1,5 @@
import torch
from modules.sd_hijack_utils import CondFunc
CondFunc('torchsde._brownian.brownian_interval._randn', lambda _, size, dtype, device, seed: torch.randn(size, dtype=dtype, device=torch.device("cpu"), generator=torch.Generator(torch.device("cpu")).manual_seed(int(seed))).to(device), lambda _, size, dtype, device, seed: device.type == 'privateuseone')
+7
View File
@@ -0,0 +1,7 @@
from modules.dml.optimizer.optimizer import Optimizer
from .driver.atiadlxx import ATIADLxx
class AMDOptimizer(Optimizer):
driver: ATIADLxx = ATIADLxx()
def memory_stats(index):
return (AMDOptimizer.driver.iHyperMemorySize, AMDOptimizer.driver.get_dedicated_vram_usage(index))
@@ -0,0 +1,46 @@
import ctypes as C
from .atiadlxx_apis import *
from .atiadlxx_structures import *
from .atiadlxx_defines import *
class ATIADLxx(object):
iHyperMemorySize = 0
def __init__(self):
self.context = ADL_CONTEXT_HANDLE()
ADL2_Main_Control_Create(ADL_Main_Memory_Alloc, 1, C.byref(self.context))
num_adapters = C.c_int(-1)
ADL2_Adapter_NumberOfAdapters_Get(self.context, C.byref(num_adapters))
AdapterInfoArray = (AdapterInfo * num_adapters.value)()
ADL2_Adapter_AdapterInfo_Get(self.context, C.cast(AdapterInfoArray, LPAdapterInfo), C.sizeof(AdapterInfoArray))
self.devices = []
busNumbers = []
for adapter in AdapterInfoArray:
if adapter.iBusNumber not in busNumbers: # filter duplicate device
self.devices.append(adapter)
busNumbers.append(adapter.iBusNumber)
self.iHyperMemorySize = self.get_memory_info2(0).iHyperMemorySize
def get_memory_info2(self, adapterIndex: int) -> ADLMemoryInfo2:
info = ADLMemoryInfo2()
if ADL2_Adapter_MemoryInfo2_Get(self.context, adapterIndex, C.byref(info)) != ADL_OK:
raise RuntimeError("ADL2: Failed to get MemoryInfo2")
return info
def get_dedicated_vram_usage(self, index: int) -> int:
usage = C.c_int(-1)
if ADL2_Adapter_DedicatedVRAMUsage_Get(self.context, self.devices[index].iAdapterIndex, C.byref(usage)) != ADL_OK:
raise RuntimeError("ADL2: Failed to get DedicatedVRAMUsage")
return usage.value
def get_vram_usage(self, index: int) -> int:
usage = C.c_int(-1)
if ADL2_Adapter_VRAMUsage_Get(self.context, self.devices[index].iAdapterIndex, C.byref(usage)) != ADL_OK:
raise RuntimeError("ADL2: Failed to get VRAMUsage")
return usage.value
@@ -0,0 +1,45 @@
import ctypes as C
from platform import platform
from .atiadlxx_structures import *
if 'Windows' in platform():
atiadlxx = C.WinDLL("atiadlxx.dll")
else:
atiadlxx = C.CDLL("libatiadlxx.so") # Not tested on Linux system. But will be supported.
ADL_MAIN_MALLOC_CALLBACK = C.CFUNCTYPE(C.c_void_p, C.c_int)
ADL_MAIN_FREE_CALLBACK = C.CFUNCTYPE(None, C.POINTER(C.c_void_p))
@ADL_MAIN_MALLOC_CALLBACK
def ADL_Main_Memory_Alloc(iSize):
return C._malloc(iSize)
@ADL_MAIN_FREE_CALLBACK
def ADL_Main_Memory_Free(lpBuffer):
if lpBuffer[0] is not None:
C._free(lpBuffer[0])
lpBuffer[0] = None
ADL2_Main_Control_Create = atiadlxx.ADL2_Main_Control_Create
ADL2_Main_Control_Create.restype = C.c_int
ADL2_Main_Control_Create.argtypes = [ADL_MAIN_MALLOC_CALLBACK, C.c_int, ADL_CONTEXT_HANDLE]
ADL2_Adapter_NumberOfAdapters_Get = atiadlxx.ADL2_Adapter_NumberOfAdapters_Get
ADL2_Adapter_NumberOfAdapters_Get.restype = C.c_int
ADL2_Adapter_NumberOfAdapters_Get.argtypes = [ADL_CONTEXT_HANDLE, C.POINTER(C.c_int)]
ADL2_Adapter_AdapterInfo_Get = atiadlxx.ADL2_Adapter_AdapterInfo_Get
ADL2_Adapter_AdapterInfo_Get.restype = C.c_int
ADL2_Adapter_AdapterInfo_Get.argtypes = [ADL_CONTEXT_HANDLE, LPAdapterInfo, C.c_int]
ADL2_Adapter_MemoryInfo2_Get = atiadlxx.ADL2_Adapter_MemoryInfo2_Get
ADL2_Adapter_MemoryInfo2_Get.restype = C.c_int
ADL2_Adapter_MemoryInfo2_Get.argtypes = [ADL_CONTEXT_HANDLE, C.c_int, C.POINTER(ADLMemoryInfo2)]
ADL2_Adapter_DedicatedVRAMUsage_Get = atiadlxx.ADL2_Adapter_DedicatedVRAMUsage_Get
ADL2_Adapter_DedicatedVRAMUsage_Get.restype = C.c_int
ADL2_Adapter_DedicatedVRAMUsage_Get.argtypes = [ADL_CONTEXT_HANDLE, C.c_int, C.POINTER(C.c_int)]
ADL2_Adapter_VRAMUsage_Get = atiadlxx.ADL2_Adapter_VRAMUsage_Get
ADL2_Adapter_VRAMUsage_Get.restype = C.c_int
ADL2_Adapter_VRAMUsage_Get.argtypes = [ADL_CONTEXT_HANDLE, C.c_int, C.POINTER(C.c_int)]
@@ -0,0 +1 @@
ADL_OK = 0
@@ -0,0 +1,87 @@
import ctypes as C
class _ADLPMActivity(C.Structure):
__slot__ = [
'iActivityPercent',
'iCurrentBusLanes',
'iCurrentBusSpeed',
'iCurrentPerformanceLevel',
'iEngineClock',
'iMaximumBusLanes',
'iMemoryClock',
'iReserved',
'iSize',
'iVddc',
]
_ADLPMActivity._fields_ = [
('iActivityPercent', C.c_int),
('iCurrentBusLanes', C.c_int),
('iCurrentBusSpeed', C.c_int),
('iCurrentPerformanceLevel', C.c_int),
('iEngineClock', C.c_int),
('iMaximumBusLanes', C.c_int),
('iMemoryClock', C.c_int),
('iReserved', C.c_int),
('iSize', C.c_int),
('iVddc', C.c_int),
]
ADLPMActivity = _ADLPMActivity
class _ADLMemoryInfo2(C.Structure):
__slot__ = [
'iHyperMemorySize',
'iInvisibleMemorySize',
'iMemoryBandwidth',
'iMemorySize',
'iVisibleMemorySize',
'strMemoryType'
]
_ADLMemoryInfo2._fields_ = [
('iHyperMemorySize', C.c_longlong),
('iInvisibleMemorySize', C.c_longlong),
('iMemoryBandwidth', C.c_longlong),
('iMemorySize', C.c_longlong),
('iVisibleMemorySize', C.c_longlong),
('strMemoryType', C.c_char * 256)
]
ADLMemoryInfo2 = _ADLMemoryInfo2
class _AdapterInfo(C.Structure):
__slot__ = [
'iSize',
'iAdapterIndex',
'strUDID',
'iBusNumber',
'iDeviceNumber',
'iFunctionNumber',
'iVendorID',
'strAdapterName',
'strDisplayName',
'iPresent',
'iExist',
'strDriverPath',
'strDriverPathExt',
'strPNPString',
'iOSDisplayIndex',
]
_AdapterInfo._fields_ = [
('iSize', C.c_int),
('iAdapterIndex', C.c_int),
('strUDID', C.c_char * 256),
('iBusNumber', C.c_int),
('iDeviceNumber', C.c_int),
('iFunctionNumber', C.c_int),
('iVendorID', C.c_int),
('strAdapterName', C.c_char * 256),
('strDisplayName', C.c_char * 256),
('iPresent', C.c_int),
('iExist', C.c_int),
('strDriverPath', C.c_char * 256),
('strDriverPathExt', C.c_char * 256),
('strPNPString', C.c_char * 256),
('iOSDisplayIndex', C.c_int)
]
AdapterInfo = _AdapterInfo
LPAdapterInfo = C.POINTER(_AdapterInfo)
ADL_CONTEXT_HANDLE = C.c_void_p
+7
View File
@@ -0,0 +1,7 @@
from modules.dml.optimizer.optimizer import Optimizer
class IntelOptimizer(Optimizer):
def memory_stats(index):
raise NotImplementedError()
# DML TODO: Implement
return
+7
View File
@@ -0,0 +1,7 @@
from modules.dml.optimizer.optimizer import Optimizer
class nVidiaOptimizer(Optimizer):
def memory_stats(index):
raise NotImplementedError()
# DML TODO: Implement
return
+8
View File
@@ -0,0 +1,8 @@
from abc import *
from typing import *
class Optimizer(metaclass=ABCMeta):
driver: Any = None
@abstractmethod
def memory_stats(index: int) -> Tuple[int, int]:
pass
@@ -0,0 +1,6 @@
from modules.dml.optimizer.optimizer import Optimizer
class UnknownOptimizer(Optimizer):
def memory_stats(index):
# DML TODO: Implement
return (1073741824, 0)
+2 -1
View File
@@ -6,7 +6,7 @@ from PIL import Image
from basicsr.utils.download_util import load_file_from_url
from modules.upscaler import Upscaler, UpscalerData
from modules.shared import cmd_opts, opts
from modules.shared import cmd_opts, opts, device
import modules.errors as errors
@@ -53,6 +53,7 @@ class UpscalerRealESRGAN(Upscaler):
half=not cmd_opts.no_half and not opts.upcast_sampling,
tile=opts.ESRGAN_tile,
tile_pad=opts.ESRGAN_tile_overlap,
device=device,
)
upsampled = upsampler.enhance(np.array(img), outscale=info.scale)[0]
+11
View File
@@ -30,6 +30,9 @@ def get_available_vram():
mem_free_torch = mem_reserved - mem_active
mem_free_total = mem_free_cuda + mem_free_torch
return mem_free_total
elif shared.device.type == 'privateuseone':
mem_total, mem_active = torch.dml.memory_stats(shared.device)
return mem_total - mem_active * (1 << 20)
else:
return psutil.virtual_memory().available
@@ -195,6 +198,11 @@ def einsum_op_cuda(q, k, v):
# Divide factor of safety as there's copying and fragmentation
return einsum_op_tensor_mem(q, k, v, mem_free_total / 3.3 / (1 << 20))
def einsum_op_dml(q, k, v):
mem_total, mem_active = torch.dml.memory_stats(q.device)
mem_reserved = mem_total / (1 << 20) * 0.7
return einsum_op_tensor_mem(q, k, v, (mem_reserved - mem_active) if mem_reserved > mem_active else 1)
def einsum_op(q, k, v):
if q.device.type == 'cuda':
return einsum_op_cuda(q, k, v)
@@ -204,6 +212,9 @@ def einsum_op(q, k, v):
return einsum_op_mps_v1(q, k, v)
return einsum_op_mps_v2(q, k, v)
if q.device.type == 'privateuseone':
return einsum_op_dml(q, k, v)
# Smaller slices are faster due to L2/L3/SLC caches.
# Tested on i7 with 8MB L3 cache.
return einsum_op_tensor_mem(q, k, v, 32)
+9 -3
View File
@@ -52,11 +52,17 @@ ui_reorder_categories = [
cmd_opts.disable_extension_access = (cmd_opts.share or cmd_opts.listen or cmd_opts.server_name) and not cmd_opts.enable_insecure
devices.device, devices.device_interrogate, devices.device_gfpgan, devices.device_esrgan, devices.device_codeformer = (devices.cpu if any(y in cmd_opts.use_cpu for y in [x, 'all']) else devices.get_optimal_device() for x in ['sd', 'interrogate', 'gfpgan', 'esrgan', 'codeformer'])
device = devices.device
is_device_dml = False
sd_upscalers = []
sd_model = None
clip_model = None
if device.type == 'privateuseone':
import modules.dml
is_device_dml = True
def reload_hypernetworks():
from modules.hypernetworks import hypernetwork
global hypernetworks # pylint: disable=W0603
@@ -232,7 +238,7 @@ options_templates.update(options_section(('sd', "Stable Diffusion"), {
"comma_padding_backtrack": OptionInfo(20, "Increase coherency by padding from the last comma within n tokens when using more than 75 tokens", gr.Slider, {"minimum": 0, "maximum": 74, "step": 1 }),
"CLIP_stop_at_last_layers": OptionInfo(1, "Clip skip", gr.Slider, {"minimum": 1, "maximum": 12, "step": 1, "visible": False}),
"upcast_attn": OptionInfo(False, "Upcast cross attention layer to float32"),
"cross_attention_optimization": OptionInfo("Scaled-Dot-Product", "Cross-attention optimization method", gr.Radio, lambda: {"choices": shared_items.list_crossattention() }),
"cross_attention_optimization": OptionInfo("Sub-quadratic" if is_device_dml else "Scaled-Dot-Product", "Cross-attention optimization method", gr.Radio, lambda: {"choices": shared_items.list_crossattention() }),
"cross_attention_options": OptionInfo([], "Cross-attention advanced options", gr.CheckboxGroup, lambda: {"choices": ['xFormers enable flash Attention', 'SDP disable memory attention']}),
"sub_quad_q_chunk_size": OptionInfo(512, "Sub-quadratic cross-attention query chunk size for the layer optimization to use", gr.Slider, {"minimum": 16, "maximum": 8192, "step": 8}),
"sub_quad_kv_chunk_size": OptionInfo(512, "Sub-quadratic cross-attentionkv chunk size for the sub-quadratic cross-attention layer optimization to use", gr.Slider, {"minimum": 0, "maximum": 8192, "step": 8}),
@@ -310,8 +316,8 @@ options_templates.update(options_section(('cuda', "CUDA Settings"), {
"memmon_poll_rate": OptionInfo(2, "VRAM usage polls per second during generation. Set to 0 to disable.", gr.Slider, {"minimum": 0, "maximum": 40, "step": 1}),
"precision": OptionInfo("Autocast", "Precision type", gr.Radio, lambda: {"choices": ["Autocast", "Full"]}),
"cuda_dtype": OptionInfo("FP32" if sys.platform == "darwin" else "FP16", "Device precision type", gr.Radio, lambda: {"choices": ["FP32", "FP16", "BF16"]}),
"no_half": OptionInfo(False, "Use full precision for model (--no-half)"),
"no_half_vae": OptionInfo(False, "Use full precision for VAE (--no-half-vae)"),
"no_half": OptionInfo(True if is_device_dml else False, "Use full precision for model (--no-half)", None, None, lambda: print("Warning: Most of DirectML devices do not fully support half mode. Recommend to use full precision to model.") if is_device_dml else None),
"no_half_vae": OptionInfo(True if is_device_dml else False, "Use full precision for VAE (--no-half-vae)"),
"upcast_sampling": OptionInfo(True if sys.platform == "darwin" else False, "Enable upcast sampling. Usually produces similar results to --no-half with better performance while using less memory"),
"disable_nan_check": OptionInfo(True, "Do not check if produced images/latent spaces have NaN values"),
"rollback_vae": OptionInfo(False, "Attempt to roll back VAE when produced NaN values, requires NaN check (experimental)"),
+26 -9
View File
@@ -4,6 +4,7 @@ import json
import time
import shutil
import logging
import platform
import subprocess
try:
@@ -20,7 +21,7 @@ class Dot(dict): # dot notation access to dictionary attributes
log = logging.getLogger("sd")
args = Dot({ 'debug': False, 'upgrade': False, 'noupdate': False, 'skip-extensions': False, 'skip-requirements': False, 'reset': False })
args = Dot({ 'debug': False, 'upgrade': False, 'noupdate': False, 'nodirectml': False, 'skip-extensions': False, 'skip-requirements': False, 'reset': False })
quick_allowed = True
errors = 0
opts = {}
@@ -197,17 +198,21 @@ def check_torch():
torch_command = os.environ.get('TORCH_COMMAND', 'torch torchvision torchaudio --index-url https://download.pytorch.org/whl/rocm5.4.2')
xformers_package = os.environ.get('XFORMERS_PACKAGE', 'none')
else:
log.info('Using CPU-only Torch')
torch_command = os.environ.get('TORCH_COMMAND', 'torch torchaudio torchvision')
xformers_package = os.environ.get('XFORMERS_PACKAGE', 'none')
machine = platform.machine()
if 'arm' not in machine and 'aarch' not in machine and not args.nodirectml: # torch-directml is available on AMD64
log.info('Using DirectML Backend')
torch_command = os.environ.get('TORCH_COMMAND', 'torch==2.0.0 torchvision torch-directml')
xformers_package = os.environ.get('XFORMERS_PACKAGE', 'none')
else:
log.info('Using CPU-only Torch')
torch_command = os.environ.get('TORCH_COMMAND', 'torch torchaudio torchvision')
xformers_package = os.environ.get('XFORMERS_PACKAGE', 'none')
if 'torch' in torch_command:
install(torch_command, 'torch torchvision torchaudio')
try:
import torch
log.info(f'Torch {torch.__version__}')
if not torch.cuda.is_available():
log.warning("Torch repoorts CUDA not available")
else:
if torch.cuda.is_available():
if torch.version.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:
@@ -216,6 +221,17 @@ def check_torch():
log.warning('Unknown Torch backend')
for device in [torch.cuda.device(i) for i in range(torch.cuda.device_count())]:
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
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)}')
log.info(f'DirectML default device: {torch_directml.device_name(torch_directml.default_device())}')
except:
log.warning("Torch repoorts CUDA not available")
except Exception as e:
log.error(f'Could not load torch: {e}')
exit(1)
@@ -487,15 +503,16 @@ def check_timestamp():
def add_args():
if vars(parser)['_option_string_actions'].get('--debug', None) is None:
parser.add_argument('--debug', default = False, action='store_true', help = "Run installer with debug logging, default: %(default)s")
parser.add_argument('--debug', default = False, action='store_true', help = "Run installer with debug logging, default: %(default)s")
parser.add_argument('--reset', default = False, action='store_true', help = "Reset main repository to latest version, default: %(default)s")
parser.add_argument('--upgrade', default = False, action='store_true', help = "Upgrade main repository to latest version, default: %(default)s")
parser.add_argument('--noupdate', default = False, action='store_true', help = "Skip update of extensions and submodules, default: %(default)s")
parser.add_argument('--nodirectml', default = False, action='store_true', help = "Although nVidia and AMD toolkit aren't detected, use CPU not DirectML, default: %(default)s")
parser.add_argument('--skip-requirements', default = False, action='store_true', help = "Skips checking and installing requirements, default: %(default)s")
parser.add_argument('--skip-extensions', default = False, action='store_true', help = "Skips running individual extension installers, default: %(default)s")
parser.add_argument('--skip-git', default = False, action='store_true', help = "Skips running all GIT operations, default: %(default)s")
parser.add_argument('--experimental', default = False, action='store_true', help = "Allow unsupported versions of libraries, default: %(default)s")
parser.add_argument('--test', default = False, action='store_true', help = "Run test only, default: %(default)s")
def parse_args():