Show free VRAM
This commit is contained in:
@@ -6,7 +6,7 @@ import type { SerializedLGraph, UUID } from "@litegraph-ts/core";
|
|||||||
import type { SerializedLayoutState } from "./stores/layoutStates";
|
import type { SerializedLayoutState } from "./stores/layoutStates";
|
||||||
import type { ComfyNodeDef, ComfyNodeDefInput } from "./ComfyNodeDef";
|
import type { ComfyNodeDef, ComfyNodeDefInput } from "./ComfyNodeDef";
|
||||||
import type { WorkflowInstID } from "./stores/workflowState";
|
import type { WorkflowInstID } from "./stores/workflowState";
|
||||||
import type { ComfyAPIPromptErrorResponse } from "./apiErrors";
|
import type { ComfyAPIPromptErrorResponse, ComfyExecutionError, ComfyInterruptedError } from "./apiErrors";
|
||||||
|
|
||||||
export type ComfyPromptRequest = {
|
export type ComfyPromptRequest = {
|
||||||
client_id?: string,
|
client_id?: string,
|
||||||
@@ -60,6 +60,20 @@ export type ComfyAPIHistoryResponse = {
|
|||||||
error?: string
|
error?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type ComfyDevice = {
|
||||||
|
name: string,
|
||||||
|
type: string,
|
||||||
|
index: number,
|
||||||
|
vram_total: number
|
||||||
|
vram_free: number
|
||||||
|
torch_vram_total: number
|
||||||
|
torch_vram_free: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ComfyAPISystemStatsResponse = {
|
||||||
|
devices: ComfyDevice[]
|
||||||
|
}
|
||||||
|
|
||||||
export type SerializedComfyBoxPromptData = {
|
export type SerializedComfyBoxPromptData = {
|
||||||
subgraphs: string[]
|
subgraphs: string[]
|
||||||
}
|
}
|
||||||
@@ -371,4 +385,9 @@ export default class ComfyAPI {
|
|||||||
async interrupt(): Promise<Response> {
|
async interrupt(): Promise<Response> {
|
||||||
return fetch(this.getBackendUrl() + "/interrupt", { method: "POST" });
|
return fetch(this.getBackendUrl() + "/interrupt", { method: "POST" });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getSystemStats(): Promise<ComfyAPISystemStatsResponse> {
|
||||||
|
return fetch(this.getBackendUrl() + "/system_stats")
|
||||||
|
.then(async (resp) => (await resp.json()) as ComfyAPISystemStatsResponse);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ import DanbooruTags from "$lib/DanbooruTags";
|
|||||||
import { deserializeTemplateFromSVG, type SerializedComfyBoxTemplate } from "$lib/ComfyBoxTemplate";
|
import { deserializeTemplateFromSVG, type SerializedComfyBoxTemplate } from "$lib/ComfyBoxTemplate";
|
||||||
import templateState from "$lib/stores/templateState";
|
import templateState from "$lib/stores/templateState";
|
||||||
import { formatValidationError, type ComfyAPIPromptErrorResponse, formatExecutionError, type ComfyExecutionError } from "$lib/apiErrors";
|
import { formatValidationError, type ComfyAPIPromptErrorResponse, formatExecutionError, type ComfyExecutionError } from "$lib/apiErrors";
|
||||||
|
import systemState from "$lib/stores/systemState";
|
||||||
|
|
||||||
export const COMFYBOX_SERIAL_VERSION = 1;
|
export const COMFYBOX_SERIAL_VERSION = 1;
|
||||||
|
|
||||||
@@ -650,6 +651,23 @@ export default class ComfyApp {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const config = get(configState);
|
||||||
|
|
||||||
|
if (config.pollSystemStatsInterval > 0) {
|
||||||
|
const interval = Math.max(config.pollSystemStatsInterval, 250);
|
||||||
|
const refresh = async () => {
|
||||||
|
try {
|
||||||
|
const resp = await this.api.getSystemStats();
|
||||||
|
systemState.updateState(resp)
|
||||||
|
} catch (error) {
|
||||||
|
// console.debug("Error retrieving stats", error)
|
||||||
|
systemState.updateState({ devices: [] })
|
||||||
|
}
|
||||||
|
setTimeout(refresh, interval);
|
||||||
|
}
|
||||||
|
setTimeout(refresh, interval);
|
||||||
|
}
|
||||||
|
|
||||||
this.api.init();
|
this.api.init();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import queueState, { type CompletedQueueEntry, type QueueEntry, type QueueEntryStatus } from "$lib/stores/queueState";
|
import queueState, { type CompletedQueueEntry, type QueueEntry, type QueueEntryStatus } from "$lib/stores/queueState";
|
||||||
import ProgressBar from "./ProgressBar.svelte";
|
import ProgressBar from "./ProgressBar.svelte";
|
||||||
|
import SystemStatsBar from "./SystemStatsBar.svelte";
|
||||||
import Spinner from "./Spinner.svelte";
|
import Spinner from "./Spinner.svelte";
|
||||||
import PromptDisplay from "./PromptDisplay.svelte";
|
import PromptDisplay from "./PromptDisplay.svelte";
|
||||||
import { List, ListUl, Grid } from "svelte-bootstrap-icons";
|
import { List, ListUl, Grid } from "svelte-bootstrap-icons";
|
||||||
@@ -241,6 +242,9 @@
|
|||||||
<div class="node-name">
|
<div class="node-name">
|
||||||
<span>Node: {getNodeInfo($queueState.runningNodeID)}</span>
|
<span>Node: {getNodeInfo($queueState.runningNodeID)}</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
<SystemStatsBar />
|
||||||
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<ProgressBar value={$queueState.progress?.value} max={$queueState.progress?.max} />
|
<ProgressBar value={$queueState.progress?.value} max={$queueState.progress?.max} />
|
||||||
</div>
|
</div>
|
||||||
@@ -263,7 +267,8 @@
|
|||||||
$bottom-bar-height: 70px;
|
$bottom-bar-height: 70px;
|
||||||
$workflow-tabs-height: 2.5rem;
|
$workflow-tabs-height: 2.5rem;
|
||||||
$mode-buttons-height: 30px;
|
$mode-buttons-height: 30px;
|
||||||
$queue-height: calc(100vh - #{$pending-height} - #{$pane-mode-buttons-height} - #{$mode-buttons-height} - #{$bottom-bar-height} - #{$workflow-tabs-height} - 0.9rem);
|
$system-stats-bar-height: 24px;
|
||||||
|
$queue-height: calc(100vh - #{$pending-height} - #{$pane-mode-buttons-height} - #{$mode-buttons-height} - #{$bottom-bar-height} - #{$workflow-tabs-height} - 0.9rem - #{$system-stats-bar-height});
|
||||||
$queue-height-history: calc(#{$queue-height} - #{$display-mode-buttons-height});
|
$queue-height-history: calc(#{$queue-height} - #{$display-mode-buttons-height});
|
||||||
|
|
||||||
.prompt-modal-header {
|
.prompt-modal-header {
|
||||||
|
|||||||
65
src/lib/components/SystemStatsBar.svelte
Normal file
65
src/lib/components/SystemStatsBar.svelte
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import type { ComfyDevice } from "$lib/api";
|
||||||
|
import systemState from "$lib/stores/systemState";
|
||||||
|
|
||||||
|
export let value: number | null = null;
|
||||||
|
export let max: number | null = null;
|
||||||
|
export let classes: string = "";
|
||||||
|
export let styles: string = "";
|
||||||
|
let percent: number = 0;
|
||||||
|
let totalGB: string = "";
|
||||||
|
let usedGB: string = "";
|
||||||
|
let text: string = ""
|
||||||
|
|
||||||
|
let device: ComfyDevice | null = null;
|
||||||
|
$: device = $systemState.devices[0]
|
||||||
|
|
||||||
|
function toGB(bytes: number): string {
|
||||||
|
return (bytes / 1024 / 1024 / 1024).toFixed(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
$: if (device) {
|
||||||
|
percent = (1 - (device.vram_free / device.vram_total)) * 100;
|
||||||
|
totalGB = toGB(device.vram_total);
|
||||||
|
usedGB = toGB(device.vram_total - device.vram_free);
|
||||||
|
text = `${usedGB} / ${totalGB}GB (${percent.toFixed(1)}%)`
|
||||||
|
} else {
|
||||||
|
percent = 0
|
||||||
|
totalGB = ""
|
||||||
|
usedGB = ""
|
||||||
|
text = "??.?%"
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="progress {classes}" style={styles}>
|
||||||
|
<div class="bar" style="width: {percent}%;">
|
||||||
|
<span class="label">VRAM: {text}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.progress {
|
||||||
|
height: 18px;
|
||||||
|
margin: 5px;
|
||||||
|
text-align: center;
|
||||||
|
color: var(--neutral-400);
|
||||||
|
border: 1px solid var(--neutral-500);
|
||||||
|
padding: 0px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bar {
|
||||||
|
height: 100%;
|
||||||
|
background: var(--secondary-800);
|
||||||
|
}
|
||||||
|
|
||||||
|
.label {
|
||||||
|
font-size: 8pt;
|
||||||
|
position: absolute;
|
||||||
|
margin: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -155,6 +155,19 @@ const defCacheBuiltInResources: ConfigDefBoolean<"cacheBuiltInResources"> = {
|
|||||||
options: {}
|
options: {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const defPollSystemStatsInterval: ConfigDefNumber<"pollSystemStatsInterval"> = {
|
||||||
|
name: "pollSystemStatsInterval",
|
||||||
|
type: "number",
|
||||||
|
defaultValue: 1000,
|
||||||
|
category: "behavior",
|
||||||
|
description: "Interval in milliseconds to refresh system stats (total/free VRAM). Set to 0 to disable",
|
||||||
|
options: {
|
||||||
|
min: 0,
|
||||||
|
max: 60000,
|
||||||
|
step: 100
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const defBuiltInTemplates: ConfigDefStringArray<"builtInTemplates"> = {
|
const defBuiltInTemplates: ConfigDefStringArray<"builtInTemplates"> = {
|
||||||
name: "builtInTemplates",
|
name: "builtInTemplates",
|
||||||
type: "string[]",
|
type: "string[]",
|
||||||
@@ -198,6 +211,7 @@ export const CONFIG_DEFS = [
|
|||||||
defPromptForWorkflowName,
|
defPromptForWorkflowName,
|
||||||
defConfirmWhenUnloadingUnsavedChanges,
|
defConfirmWhenUnloadingUnsavedChanges,
|
||||||
defCacheBuiltInResources,
|
defCacheBuiltInResources,
|
||||||
|
defPollSystemStatsInterval,
|
||||||
defBuiltInTemplates,
|
defBuiltInTemplates,
|
||||||
// defLinkDisplayType
|
// defLinkDisplayType
|
||||||
] as const;
|
] as const;
|
||||||
|
|||||||
39
src/lib/stores/systemState.ts
Normal file
39
src/lib/stores/systemState.ts
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import { debounce, isMobileBrowser } from '$lib/utils';
|
||||||
|
import { get, writable } from 'svelte/store';
|
||||||
|
import type { Readable, Writable } from 'svelte/store';
|
||||||
|
import type { WorkflowInstID, WorkflowReceiveOutputTargets } from './workflowState';
|
||||||
|
import modalState, { type ModalData } from './modalState';
|
||||||
|
import type { SlotType } from '@litegraph-ts/core';
|
||||||
|
import type ComfyApp from '$lib/components/ComfyApp';
|
||||||
|
import SendOutputModal, { type SendOutputModalResult } from "$lib/components/modal/SendOutputModal.svelte";
|
||||||
|
import workflowState from './workflowState';
|
||||||
|
import type { ComfyAPISystemStatsResponse, ComfyDevice } from '$lib/api';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export type SystemState = {
|
||||||
|
devices: ComfyDevice[]
|
||||||
|
}
|
||||||
|
|
||||||
|
type SystemStateOps = {
|
||||||
|
updateState: (resp: ComfyAPISystemStatsResponse) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export type WritableSystemStateStore = Writable<SystemState> & SystemStateOps;
|
||||||
|
const store: Writable<SystemState> = writable(
|
||||||
|
{
|
||||||
|
devices: []
|
||||||
|
})
|
||||||
|
|
||||||
|
function updateState(resp: ComfyAPISystemStatsResponse) {
|
||||||
|
store.set({
|
||||||
|
devices: resp.devices
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const interfaceStateStore: WritableSystemStateStore =
|
||||||
|
{
|
||||||
|
...store,
|
||||||
|
updateState
|
||||||
|
}
|
||||||
|
export default interfaceStateStore;
|
||||||
Reference in New Issue
Block a user