diff --git a/.ci/windows_nightly_base_files/run_nvidia_gpu_fast.bat b/.ci/windows_nightly_base_files/run_nvidia_gpu_fast.bat new file mode 100644 index 000000000..ca6d6868a --- /dev/null +++ b/.ci/windows_nightly_base_files/run_nvidia_gpu_fast.bat @@ -0,0 +1,2 @@ +.\python_embeded\python.exe -s ComfyUI\main.py --windows-standalone-build --fast +pause diff --git a/.github/workflows/windows_release_nightly_pytorch.yml b/.github/workflows/windows_release_nightly_pytorch.yml index 0b29b4d91..07f52e0b2 100644 --- a/.github/workflows/windows_release_nightly_pytorch.yml +++ b/.github/workflows/windows_release_nightly_pytorch.yml @@ -67,6 +67,7 @@ jobs: mkdir update cp -r ComfyUI/.ci/update_windows/* ./update/ cp -r ComfyUI/.ci/windows_base_files/* ./ + cp -r ComfyUI/.ci/windows_nightly_base_files/* ./ echo "call update_comfyui.bat nopause ..\python_embeded\python.exe -s -m pip install --upgrade --pre torch torchvision torchaudio --extra-index-url https://download.pytorch.org/whl/nightly/cu${{ inputs.cu }} -r ../ComfyUI/requirements.txt pygit2 diff --git a/comfy/model_management.py b/comfy/model_management.py index 2f6301346..94b335620 100644 --- a/comfy/model_management.py +++ b/comfy/model_management.py @@ -1001,16 +1001,13 @@ def should_use_fp16(device=None, model_params=0, prioritize_performance=True, ma if props.major < 6: return False - fp16_works = False - #FP16 is confirmed working on a 1080 (GP104) but it's a bit slower than FP32 so it should only be enabled - #when the model doesn't actually fit on the card - #TODO: actually test if GP106 and others have the same type of behavior + #FP16 is confirmed working on a 1080 (GP104) and on latest pytorch actually seems faster than fp32 nvidia_10_series = ["1080", "1070", "titan x", "p3000", "p3200", "p4000", "p4200", "p5000", "p5200", "p6000", "1060", "1050", "p40", "p100", "p6", "p4"] for x in nvidia_10_series: if x in props.name.lower(): - fp16_works = True + return True - if fp16_works or manual_cast: + if manual_cast: free_model_memory = maximum_vram_for_weights(device) if (not prioritize_performance) or model_params * 4 > free_model_memory: return True diff --git a/execution.py b/execution.py index 9278af35f..05a662cde 100644 --- a/execution.py +++ b/execution.py @@ -47,7 +47,8 @@ class IsChangedCache: self.is_changed[node_id] = node["is_changed"] return self.is_changed[node_id] - input_data_all, _ = get_input_data(node["inputs"], class_def, node_id, self.outputs_cache) + # Intentionally do not use cached outputs here. We only want constants in IS_CHANGED + input_data_all, _ = get_input_data(node["inputs"], class_def, node_id, None) try: is_changed = _map_node_over_list(class_def, input_data_all, "IS_CHANGED") node["is_changed"] = [None if isinstance(x, ExecutionBlocker) else x for x in is_changed] diff --git a/tests/inference/test_execution.py b/tests/inference/test_execution.py index 9df1d7df8..7965165fc 100644 --- a/tests/inference/test_execution.py +++ b/tests/inference/test_execution.py @@ -459,3 +459,22 @@ class TestExecution: assert len(images1) == 1, "Should have 1 image" assert len(images2) == 1, "Should have 1 image" + + # This tests that only constant outputs are used in the call to `IS_CHANGED` + def test_is_changed_with_outputs(self, client: ComfyClient, builder: GraphBuilder): + g = builder + input1 = g.node("StubConstantImage", value=0.5, height=512, width=512, batch_size=1) + test_node = g.node("TestIsChangedWithConstants", image=input1.out(0), value=0.5) + + output = g.node("PreviewImage", images=test_node.out(0)) + + result = client.run(g) + images = result.get_images(output) + assert len(images) == 1, "Should have 1 image" + assert numpy.array(images[0]).min() == 63 and numpy.array(images[0]).max() == 63, "Image should have value 0.25" + + result = client.run(g) + images = result.get_images(output) + assert len(images) == 1, "Should have 1 image" + assert numpy.array(images[0]).min() == 63 and numpy.array(images[0]).max() == 63, "Image should have value 0.25" + assert not result.did_run(test_node), "The execution should have been cached" diff --git a/tests/inference/testing_nodes/testing-pack/specific_tests.py b/tests/inference/testing_nodes/testing-pack/specific_tests.py index b961d1b63..dd8100237 100644 --- a/tests/inference/testing_nodes/testing-pack/specific_tests.py +++ b/tests/inference/testing_nodes/testing-pack/specific_tests.py @@ -95,6 +95,31 @@ class TestCustomIsChanged: else: return False +class TestIsChangedWithConstants: + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "image": ("IMAGE",), + "value": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0}), + }, + } + + RETURN_TYPES = ("IMAGE",) + FUNCTION = "custom_is_changed" + + CATEGORY = "Testing/Nodes" + + def custom_is_changed(self, image, value): + return (image * value,) + + @classmethod + def IS_CHANGED(cls, image, value): + if image is None: + return value + else: + return image.mean().item() * value + class TestCustomValidation1: @classmethod def INPUT_TYPES(cls): @@ -312,6 +337,7 @@ TEST_NODE_CLASS_MAPPINGS = { "TestLazyMixImages": TestLazyMixImages, "TestVariadicAverage": TestVariadicAverage, "TestCustomIsChanged": TestCustomIsChanged, + "TestIsChangedWithConstants": TestIsChangedWithConstants, "TestCustomValidation1": TestCustomValidation1, "TestCustomValidation2": TestCustomValidation2, "TestCustomValidation3": TestCustomValidation3, @@ -325,6 +351,7 @@ TEST_NODE_DISPLAY_NAME_MAPPINGS = { "TestLazyMixImages": "Lazy Mix Images", "TestVariadicAverage": "Variadic Average", "TestCustomIsChanged": "Custom IsChanged", + "TestIsChangedWithConstants": "IsChanged With Constants", "TestCustomValidation1": "Custom Validation 1", "TestCustomValidation2": "Custom Validation 2", "TestCustomValidation3": "Custom Validation 3", diff --git a/tests/inference/testing_nodes/testing-pack/stubs.py b/tests/inference/testing_nodes/testing-pack/stubs.py index 9be6eac9d..a1df87529 100644 --- a/tests/inference/testing_nodes/testing-pack/stubs.py +++ b/tests/inference/testing_nodes/testing-pack/stubs.py @@ -28,6 +28,28 @@ class StubImage: elif content == "NOISE": return (torch.rand(batch_size, height, width, 3),) +class StubConstantImage: + def __init__(self): + pass + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "value": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 1.0, "step": 0.01}), + "height": ("INT", {"default": 512, "min": 1, "max": 1024 ** 3, "step": 1}), + "width": ("INT", {"default": 512, "min": 1, "max": 4096 ** 3, "step": 1}), + "batch_size": ("INT", {"default": 1, "min": 1, "max": 1024 ** 3, "step": 1}), + }, + } + + RETURN_TYPES = ("IMAGE",) + FUNCTION = "stub_constant_image" + + CATEGORY = "Testing/Stub Nodes" + + def stub_constant_image(self, value, height, width, batch_size): + return (torch.ones(batch_size, height, width, 3) * value,) + class StubMask: def __init__(self): pass @@ -93,12 +115,14 @@ class StubFloat: TEST_STUB_NODE_CLASS_MAPPINGS = { "StubImage": StubImage, + "StubConstantImage": StubConstantImage, "StubMask": StubMask, "StubInt": StubInt, "StubFloat": StubFloat, } TEST_STUB_NODE_DISPLAY_NAME_MAPPINGS = { "StubImage": "Stub Image", + "StubConstantImage": "Stub Constant Image", "StubMask": "Stub Mask", "StubInt": "Stub Int", "StubFloat": "Stub Float",