Skip to content

Commit

Permalink
Squashed commit of the following:
Browse files Browse the repository at this point in the history
commit b8c0101
Author: Byron Mallett <[email protected]>
Date:   Mon Jun 26 00:23:31 2023 +1200

    Finished LoRA and model download support via browser widgets

    - Added huggingface web widget
    - Added style model class inheriting from the base model to differentiate between checkpoint and lora models.
    - Renamed plugin models to BaseModels and removed pre-style models that aren't base models.

commit e984f4d
Author: Byron Mallett <[email protected]>
Date:   Sat Jun 24 23:56:58 2023 +1200

    Adding conversion tools to convert SD checkpoints into diffusers models

    Refactored civit.ai widget model downloading
    Made LoRA model asset extend the base model asset and added more properties

commit a0c03bf
Author: Byron Mallett <[email protected]>
Date:   Fri Jun 23 23:54:11 2023 +1200

    Replaced HTTP module file download with Python

    Due to bug in CURL HTTP implementation that crashes with files over max int32 bytes.
    Eventual engine fix will be to replace TArray<uint8> Payload with TArray64<uint> and FThreadSafeCounter TotalBytesRead with std::atomic<int64> in CurlHttp.h

commit 1575770
Author: Byron Mallett <[email protected]>
Date:   Thu Jun 22 21:54:42 2023 +1200

    Adding browser for LORA and models

commit 8e4590d
Author: Byron Mallett <[email protected]>
Date:   Tue Jun 20 23:18:29 2023 +1200

    Working LORA support

commit e0667e4
Author: Byron Mallett <[email protected]>
Date:   Mon Jun 19 00:08:16 2023 +1200

    Initial work on supporting LoRA models
  • Loading branch information
Mystfit committed Jun 25, 2023
1 parent b8e81f8 commit 5967a2a
Show file tree
Hide file tree
Showing 70 changed files with 762 additions and 133 deletions.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file modified StableDiffusionTools/Content/Blueprints/A_ImagePlateCamera.uasset
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
265 changes: 198 additions & 67 deletions StableDiffusionTools/Content/Python/bridges/DiffusersBridge.py

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,7 @@ def MakeDependency(options):
MakeDependency({"name":"diffusers", "version": "0.17.0"}),
MakeDependency({"name":"compel", "version": "1.2.1"}),
MakeDependency({"name":"opencv-python", "version":"4.7.0.72"}),
MakeDependency({"name":"realesrgan", "version": "0.3.0"})
MakeDependency({"name":"realesrgan", "version": "0.3.0"}),
MakeDependency({"name":"tqdm", "version": "4.65.0"}),
MakeDependency({"name":"omegaconf", "version": "2.3.0"})
]
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ def __init__(self):
unreal.StableDiffusionBridge.__init__(self)

@unreal.ufunction(override=True)
def InitModel(self, new_model_options, new_pipeline_options, layers, allow_nsfw, padding_mode):
def InitModel(self, new_model_options, new_pipeline_options, lora_asset, layers, allow_nsfw, padding_mode):
result = unreal.StableDiffusionModelInitResult()
self.set_editor_property("ModelOptions", new_model_options)
self.set_editor_property("PipelineOptions", new_pipeline_options)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ def GetTokenWebsiteHint(self):
return "https://stablehorde.net/register"

@unreal.ufunction(override=True)
def InitModel(self, new_model_options, new_pipeline_options, layers, allow_nsfw, padding_mode):
def InitModel(self, new_model_options, new_pipeline_options, lora_asset, layers, allow_nsfw, padding_mode):
result = unreal.StableDiffusionModelInitResult()
self.model_loaded = True
headers = {
Expand Down Expand Up @@ -136,7 +136,7 @@ def StopUpsample(self):
pass

@unreal.ufunction(override=True)
def UpsampleImage(self, image_result: unreal.StableDiffusionImageResult):
def UpsampleImage(self, image_result: unreal.StableDiffusionImageResult, out_texture):
unreal.log_warning("Upsampler not yet implemented for Stable Horde bridge")

# Build result
Expand Down
1 change: 1 addition & 0 deletions StableDiffusionTools/Content/Python/init_unreal.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import unreal
import importlib, pathlib, os, signal, venv, sys
import dependency_manager
#import model_asset_tools


# Get all python files in a folder
Expand Down
29 changes: 29 additions & 0 deletions StableDiffusionTools/Content/Python/model_asset_tools.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import requests, re, pathlib, unreal

response = requests.get(url, stream=True, allow_redirects=True)
if response.status_code != 200:
response.raise_for_status() # Will only raise for 4xx codes, so...
print(f"Request to {url} returned status code {r.status_code}")
downloader.fail_game_thread(0)
file_size = int(response.headers.get('Content-Length', 0))
desc = "(Unknown total file size)" if file_size == 0 else ""

url_filename = response.headers.get('content-disposition')
filenames = re.findall('filename=(.+)', url_filename)
if filenames:
filename = filenames[0].replace('\"', '')
filepath = pathlib.Path(destination_dir) / filename
print(f"Downloading file to {filepath}")

bytes_total = 0
with filepath.open("wb") as f:
#shutil.copyfileobj(r_raw, f)
with unreal.ScopedSlowTask(file_size, 'Downloading model') as slow_task:
slow_task.make_dialog(True)
for chunk in response.iter_content(1024):
bytes_total += len(chunk)
f.write(chunk)
f.flush()
downloader.update_game_thread(bytes_total)
slow_task.enter_progress_frame(bytes_total, "Downloading")
downloader.success_game_thread(file_size)
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file modified StableDiffusionTools/Content/UI/Widgets/BP_ToggleButton.uasset
Binary file not shown.
Binary file not shown.
1 change: 1 addition & 0 deletions StableDiffusionTools/Resources/globe.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions StableDiffusionTools/Resources/unreal-engine.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ void UStableDiffusionMoviePipeline::RenderSample_GameThreadImpl(const FMoviePipe
//Reload model if it doesn't match the current options track
if (OptionSection->ModelAsset) {
if (SDSubsystem->ModelOptions != OptionSection->ModelAsset->Options || SDSubsystem->GetModelStatus().ModelStatus != EModelStatus::Loaded) {
SDSubsystem->InitModel(OptionSection->ModelAsset->Options, OptionSection->PipelineAsset->Options, Input.ProcessedLayers, false, AllowNSFW, EPaddingMode::zeros);
SDSubsystem->InitModel(OptionSection->ModelAsset->Options, OptionSection->PipelineAsset->Options, OptionSection->LORAAsset, Input.ProcessedLayers, false, AllowNSFW, EPaddingMode::zeros);
}
}
if (SDSubsystem->GetModelStatus().ModelStatus != EModelStatus::Loaded) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ class STABLEDIFFUSIONSEQUENCER_API UStableDiffusionOptionsSection : public UMovi
UPROPERTY(EditAnywhere, Category = "StableDiffusion|Pipeline", meta = (Multiline = true))
UStableDiffusionPipelineAsset* PipelineAsset;

UPROPERTY(EditAnywhere, Category = "StableDiffusion|LORA", meta = (Multiline = true))
UStableDiffusionLORAAsset* LORAAsset;

//UPROPERTY(EditAnywhere, Category = "Model options")
//bool AllowNSFW = false;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
// Copyright Epic Games, Inc. All Rights Reserved.

#include "ModelAssetTools.h"
#include "Modules/ModuleManager.h"
#include "Misc/FileHelper.h"
#include "IImageWrapperModule.h"
#include "Interfaces/IHttpResponse.h"
#include "HttpModule.h"
#include "Async/Async.h"
#include "IPythonScriptPlugin.h"

#include UE_INLINE_GENERATED_CPP_BY_NAME(ModelAssetTools)


//----------------------------------------------------------------------//
// UAsyncTaskDownloadModel
//----------------------------------------------------------------------//

UAsyncTaskDownloadModel::UAsyncTaskDownloadModel(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
if (HasAnyFlags(RF_ClassDefaultObject) == false)
{
AddToRoot();
}
}

UAsyncTaskDownloadModel* UAsyncTaskDownloadModel::DownloadModelCURL(FString URL, FString Path)
{
UAsyncTaskDownloadModel* DownloadTask = NewObject<UAsyncTaskDownloadModel>();
DownloadTask->SavePath = Path;
DownloadTask->Start(URL);

return DownloadTask;
}

void UAsyncTaskDownloadModel::SuccessGameThread(int64 TotalBytes)
{
AsyncTask(ENamedThreads::GameThread, [this, TotalBytes]() {
OnSuccess.Broadcast(SavePath, TotalBytes);
});
}

void UAsyncTaskDownloadModel::UpdateGameThread(int64 TotalBytes)
{
AsyncTask(ENamedThreads::GameThread, [this, TotalBytes]() {
OnUpdate.Broadcast("", TotalBytes);
});
}

void UAsyncTaskDownloadModel::FailGameThread(int64 TotalBytes)
{
AsyncTask(ENamedThreads::GameThread, [this, TotalBytes]() {
OnFail.Broadcast("", TotalBytes);
});
}

void UAsyncTaskDownloadModel::Start(FString URL)
{
#if !UE_SERVER
// Create the Http request and add to pending request list
TSharedRef<IHttpRequest, ESPMode::ThreadSafe> HttpRequest = FHttpModule::Get().CreateRequest();

HttpRequest->OnProcessRequestComplete().BindUObject(this, &UAsyncTaskDownloadModel::HandleHTTPRequest);
HttpRequest->OnRequestProgress().BindUObject(this, &UAsyncTaskDownloadModel::HandleHTTPProgress);
HttpRequest->SetURL(URL);
HttpRequest->SetVerb(TEXT("GET"));
HttpRequest->ProcessRequest();
#else
// On the server we don't execute fail or success we just don't fire the request.
RemoveFromRoot();
#endif
}

void UAsyncTaskDownloadModel::HandleHTTPProgress(FHttpRequestPtr Request, int32 TotalBytesWritten, int32 BytesWrittenSinceLastUpdate)
{
// Don't send download updates too frequently otherwise we'll spam the blueprint thread
if (BytesWrittenSinceLastUpdate - TotalBytesDownloaded > 1000000) {
OnUpdate.Broadcast("", BytesWrittenSinceLastUpdate);
TotalBytesDownloaded = BytesWrittenSinceLastUpdate;
}
}

void UAsyncTaskDownloadModel::HandleHTTPRequest(FHttpRequestPtr HttpRequest, FHttpResponsePtr HttpResponse, bool bSucceeded)
{
#if !UE_SERVER

RemoveFromRoot();

if (bSucceeded && HttpResponse.IsValid() && HttpResponse->GetContentLength() > 0)
{
TArray<uint8> ResponseData = HttpResponse->GetContent();

// Get filename from Content-Disposition header
auto ContentDispHeader = HttpResponse->GetHeader(TEXT("Content-Disposition"));
if (ContentDispHeader.IsEmpty())
{
UE_LOG(LogTemp, Error, TEXT("Failed to download file, no Content-Disposition header found"));
OnFail.Broadcast("", ResponseData.Num());
return;
}
TArray<FString> ContentDisposition;
ContentDispHeader.ParseIntoArray(ContentDisposition, TEXT(";"), true);
auto Filename = ContentDisposition.FindByPredicate([](const FString& In) {
return In.Contains(TEXT("filename="));
});

if (Filename) {
FString SanitizedFilename = Filename->Replace(TEXT("filename="), TEXT("")).Replace(TEXT("\""), TEXT("")).TrimStartAndEnd();
// Save file content to disk

FString FullSavePath = FPaths::Combine(*SavePath, *SanitizedFilename);

if (FFileHelper::SaveArrayToFile(ResponseData, *FullSavePath))
{
UE_LOG(LogTemp, Warning, TEXT("File downloaded successfully to %s"), *FullSavePath);

// Call the delegate
OnSuccess.Broadcast(FullSavePath, ResponseData.Num());
}
else
{
UE_LOG(LogTemp, Error, TEXT("Failed to save file to %s"), *FullSavePath);
}
}
else {
UE_LOG(LogTemp, Error, TEXT("Failed to save file. No filename found in HTTP response."));
}
}

OnFail.Broadcast("", 0);

#endif
}


//----------------------------------------------------------------------//

UAsyncOperation::UAsyncOperation(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
if (HasAnyFlags(RF_ClassDefaultObject) == false)
{
AddToRoot();
}
}

UAsyncOperation* UAsyncOperation::StartAsyncOperation()
{
UAsyncOperation* Operation = NewObject<UAsyncOperation>();

AsyncTask(ENamedThreads::AnyThread, [Operation]() {
Operation->OnStart.Broadcast(Operation);
});

return Operation;
}

void UAsyncOperation::Complete()
{
#if !UE_SERVER
RemoveFromRoot();
AsyncTask(ENamedThreads::GameThread, [this]() {
OnComplete.Broadcast(this);
});
#endif

}
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,46 @@ FSceneView* UStableDiffusionBlueprintLibrary::CalculateEditorView(FSceneViewport
return nullptr;
}

UStableDiffusionStyleModelAsset* UStableDiffusionBlueprintLibrary::CreateModelAsset(const FString& PackagePath, const FString& Name)
{
if (Name.IsEmpty() || PackagePath.IsEmpty())
return false;

// Create package
FString FullPackagePath = FPaths::Combine(PackagePath, Name);
UPackage* Package = CreatePackage(*FullPackagePath);
Package->FullyLoad();

// Create data asset
UStableDiffusionStyleModelAsset* NewModelAsset = NewObject<UStableDiffusionStyleModelAsset>(Package, *Name, RF_Public | RF_Standalone);

// Update package
Package->MarkPackageDirty();
FAssetRegistryModule::AssetCreated(NewModelAsset);

return NewModelAsset;
}

UStableDiffusionLORAAsset* UStableDiffusionBlueprintLibrary::CreateLORAAsset(const FString& PackagePath, const FString& Name)
{
if (Name.IsEmpty() || PackagePath.IsEmpty())
return false;

// Create package
FString FullPackagePath = FPaths::Combine(PackagePath, Name);
UPackage* Package = CreatePackage(*FullPackagePath);
Package->FullyLoad();

// Create data asset
UStableDiffusionLORAAsset* NewLORAAsset = NewObject<UStableDiffusionLORAAsset>(Package, *Name, RF_Public | RF_Standalone);

// Update package
Package->MarkPackageDirty();
FAssetRegistryModule::AssetCreated(NewLORAAsset);

return NewLORAAsset;
}

UStableDiffusionImageResultAsset* UStableDiffusionBlueprintLibrary::CreateImageResultAsset(const FString& PackagePath, const FString& Name, UTexture2D* Texture, FIntPoint Size, const FStableDiffusionGenerationOptions& ImageInputs, FMinimalViewInfo View, bool Upsampled)
{
if (Name.IsEmpty() || PackagePath.IsEmpty() || !Texture)
Expand All @@ -360,11 +400,11 @@ UStableDiffusionImageResultAsset* UStableDiffusionBlueprintLibrary::CreateImageR

// Create data asset
FString AssetName = "DA_" + Name;
UStableDiffusionImageResultAsset* NewImageResultAsset = NewObject<UStableDiffusionImageResultAsset>(Package, *AssetName, RF_Public | RF_Standalone);
NewImageResultAsset->ImageInputs = ImageInputs;
NewImageResultAsset->ImageOutput.Upsampled = Upsampled;
NewImageResultAsset->ImageOutput.OutTexture = NewTexture;
NewImageResultAsset->ImageOutput.View = View;
UStableDiffusionImageResultAsset* NewModelAsset = NewObject<UStableDiffusionImageResultAsset>(Package, *AssetName, RF_Public | RF_Standalone);
NewModelAsset->ImageInputs = ImageInputs;
NewModelAsset->ImageOutput.Upsampled = Upsampled;
NewModelAsset->ImageOutput.OutTexture = NewTexture;
NewModelAsset->ImageOutput.View = View;

// Update package
Package->MarkPackageDirty();
Expand All @@ -377,7 +417,7 @@ UStableDiffusionImageResultAsset* UStableDiffusionBlueprintLibrary::CreateImageR
//PackageArgs.bForceByteSwapping = true;
//bool bSaved = UPackage::SavePackage(Package, NewTexture, *PackageFileName, PackageArgs);

return NewImageResultAsset;
return NewModelAsset;
}

void UStableDiffusionBlueprintLibrary::CopyTextureDataUsingUVs(UTexture2D* SourceTexture, UTexture2D* TargetTexture, const FIntPoint& ScreenSize, const FMatrix& ViewProjectionMatrix, UDynamicMesh* SourceMesh, const TArray<int> TriangleIDs, bool ClearCoverageMask)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,19 @@ FString UStableDiffusionBridge::GetToken()
return "";
}

FDirectoryPath UStableDiffusionBridge::GetSettingsLORASavePath()
{
auto Settings = GetMutableDefault<UStableDiffusionToolsSettings>();
return Settings->GetLORASavePath();
}

FDirectoryPath UStableDiffusionBridge::GetSettingsModelSavePath()
{
auto Settings = GetMutableDefault<UStableDiffusionToolsSettings>();
return Settings->GetModelSavePath();
}


void UStableDiffusionBridge::UpdateImageProgress(FString prompt, int32 step, int32 timestep, float progress, int32 width, int32 height, UTexture2D* Texture)
{
AsyncTask(ENamedThreads::GameThread, [this, prompt, step, timestep, progress, width, height, Texture] {
Expand Down
Loading

0 comments on commit 5967a2a

Please sign in to comment.