From 2088e1b7aa6419dec58800bc1d0cb24f5808affe Mon Sep 17 00:00:00 2001
From: Ferenc Szontágh <szf@fsociety.hu>
Date: Sat, 03 Feb 2024 21:19:43 +0000
Subject: [PATCH] added queue handler against simple start

---
 .vscode/extensions.json |    4 
 /dev/null               |   28 --
 .vscode/tasks.json      |   28 ++
 .gitignore              |    2 
 ui/MainWindowUI.h       |   55 ++--
 .vscode/settings.json   |   11 +
 ui/QueueManager.cpp     |   93 +++++++-
 ui/QueueManager.h       |   23 +
 ui/MainWindowUI.cpp     |  331 +++++++++++++++++------------
 CMakeLists.txt          |   14 +
 README.md               |   23 ++
 11 files changed, 399 insertions(+), 213 deletions(-)

diff --git a/.gitignore b/.gitignore
index 613a77a..734495b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -20,4 +20,4 @@
 *.out
 *.app
 vcpkg_installed
-build/**
\ No newline at end of file
+build/
\ No newline at end of file
diff --git a/.vscode/extensions.json b/.vscode/extensions.json
index cf925ca..f52d8f8 100644
--- a/.vscode/extensions.json
+++ b/.vscode/extensions.json
@@ -1,7 +1,7 @@
 {
     "recommendations": [
         "wayou.vscode-todo-highlight",
-        "ms-vscode.cpptools",
-        "jeff-hykin.better-cpp-syntax"
+        "jeff-hykin.better-cpp-syntax",
+        "ms-vscode.cpptools"
     ]
 }
\ No newline at end of file
diff --git a/.vscode/launch.json b/.vscode/launch.json
deleted file mode 100644
index 25482f6..0000000
--- a/.vscode/launch.json
+++ /dev/null
@@ -1,28 +0,0 @@
-{
-    "version": "0.2.0",
-    "configurations": [
-        {
-            "name": "(msvc) Launch",
-            "type": "cppvsdbg",
-            "request": "launch",
-            // Resolved by CMake Tools:
-            "program": "${command:cmake.launchTargetPath}",
-            "args": [],
-            "stopAtEntry": false,
-            "cwd": "${workspaceFolder}",
-            "environment": [
-                {
-                    // add the directory where our target was built to the PATHs
-                    // it gets resolved by CMake Tools:
-                    "name": "PATH",
-                    "value": "${env:PATH}:${command:cmake.getLaunchTargetDirectory}"
-                },
-                {
-                    "name": "OTHER_VALUE",
-                    "value": "Something something"
-                }
-            ],
-            "console": "externalTerminal"
-        }
-    ]
-}
\ No newline at end of file
diff --git a/.vscode/settings.json b/.vscode/settings.json
index a547b9b..b240519 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -100,5 +100,16 @@
         "regex": "cpp",
         "typeindex": "cpp",
         "valarray": "cpp"
+    },
+    "debug.terminal.clearBeforeReusing": true,
+    "C_Cpp.loggingLevel": "Information",
+    "remote.WSL.debug": true,
+    "cmake.configureOnOpen": false,
+    "cmake.debugConfig": {
+        "stopAtEntry": false,
+        "console": "newExternalWindow",
+        "logging": {
+            "trace": true
+        }
     }
 }
\ No newline at end of file
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
new file mode 100644
index 0000000..6532339
--- /dev/null
+++ b/.vscode/tasks.json
@@ -0,0 +1,28 @@
+{
+    "tasks": [
+        {
+            "type": "cppbuild",
+            "label": "C/C++: cl.exe build active file",
+            "command": "cl.exe",
+            "args": [
+                "/Zi",
+                "/EHsc",
+                "/nologo",
+                "/Fe${fileDirname}\\${fileBasenameNoExtension}.exe",
+                "${file}"
+            ],
+            "options": {
+                "cwd": "${fileDirname}"
+            },
+            "problemMatcher": [
+                "$msCompile"
+            ],
+            "group": {
+                "kind": "build",
+                "isDefault": true
+            },
+            "detail": "Task generated by Debugger."
+        }
+    ],
+    "version": "2.0.0"
+}
\ No newline at end of file
diff --git a/CMakeLists.txt b/CMakeLists.txt
index d0f3a81..6881227 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -39,7 +39,7 @@
     
 
     if(NOT CMAKE_BUILD_TYPE)
-        set(CMAKE_BUILD_TYPE Debug)
+        set(CMAKE_BUILD_TYPE Release)
     endif()
 
     set(STABLE_DIFFUSION_LIB ${CMAKE_SOURCE_DIR}/external/stable-diffusion.cpp/${CMAKE_BUILD_TYPE}/lib/stable-diffusion.lib)
@@ -61,6 +61,18 @@
     message("CUDA_PATH=" ${CUDA_PATH})
     
 
+    add_custom_command(TARGET sd.ui POST_BUILD        # Adds a post-build event to MyTest
+    COMMAND ${CMAKE_COMMAND} -E copy_if_different  # which executes "cmake - E copy_if_different..."
+        "${STABLE_DIFFUSION_DLL}"      # <--this is in-file
+        $<TARGET_FILE_DIR:sd.ui>)                 # <--this is out-file path0
+
+    add_custom_command(TARGET sd.ui POST_BUILD        # Adds a post-build event to MyTest
+    COMMAND ${CMAKE_COMMAND} -E copy_if_different  # which executes "cmake - E copy_if_different..."
+        "${GGML_DLL}"      # <--this is in-file
+        $<TARGET_FILE_DIR:sd.ui>)                 # <--this is out-file path0
+
+
+
 ENDIF(MSVC)
 
 set(OpenCV_DIR "${VCPKG_INSTALLED_DIR}/x64-windows/share/opencv4")
diff --git a/README.md b/README.md
index a3be9d0..79b8006 100644
--- a/README.md
+++ b/README.md
@@ -17,6 +17,29 @@
 in the external forlder, you can find the precompuled stable-diffusion.cpp and ggml as submodulde to it. 
 The default libs are configured with only CUDA, the release is Debug and Release. To use with another version (for example cpu only), you need to compile and overwrite 
 
+#### Build stable-diffusion.cpp
+```Bash
+git clone --recursive https://github.com/leejet/stable-diffusion.cpp
+cd stable-diffusion.cpp
+git pull origin master
+git submodule init
+git submodule update
+
+mkdir build
+cd build
+#with CUDA 
+cmake .. -DSD_CUBLAS=ON -DBUILD_SHARED_LIBS=ON
+
+#build
+cmake --build . --config Release
+#or Debug
+cmake --build . --config Debug
+
+#test
+./bin/sd -m ../models/sd-v1-4.ckpt -p "a lovely cat"
+```
+
+
 ## - TO-DOs
 See @git.spamming.hu ticketing system
 
diff --git a/ui/MainWindowUI.cpp b/ui/MainWindowUI.cpp
index 09a1143..a6b39c7 100644
--- a/ui/MainWindowUI.cpp
+++ b/ui/MainWindowUI.cpp
@@ -6,6 +6,7 @@
     this->ini_path = wxStandardPaths::Get().GetUserConfigDir() + wxFileName::GetPathSeparator() + "sd.ui.config.ini";
     this->sd_params = new sd_gui_utils::SDParams;
     this->notification = new wxNotificationMessage();
+    this->JobTableItems = new std::map<int, QM::QueueItem>();
 
     this->notification->SetParent(this);
 
@@ -15,7 +16,9 @@
 
     this->m_joblist->AppendTextColumn("Id");
     this->m_joblist->AppendTextColumn("Created");
-    this->m_joblist->AppendTextColumn("Update");
+    this->m_joblist->AppendTextColumn("Model");
+    this->m_joblist->AppendTextColumn("Sampler");
+    this->m_joblist->AppendTextColumn("Seed");
     this->m_joblist->AppendTextColumn("Status");
 
     this->SetTitle(this->GetTitle() + SD_GUI_VERSION);
@@ -97,12 +100,6 @@
         // add the selected vae
         this->sd_params->vae_path = this->VaeFiles.at(selection.ToStdString());
     }
-    // just select the vae file and add the live paramaeters, do not load the model, just add job to the queu on press the queue button
-    /*  if (this->m_generate->IsEnabled() == true)
-    {
-    wxPostEvent(this->m_model, event);
-    }
-    */
 }
 
 void MainWindowUI::onSamplerSelect(wxCommandEvent &event)
@@ -146,10 +143,28 @@
 
 void MainWindowUI::onGenerate(wxCommandEvent &event)
 {
-    // this->StartGeneration();
-    //  dont start the generation, just add the job to the queue, to handle it the queue manager
-    //  so it will possible to add more job afterall
-    this->qmanager->AddItem(this->sd_params);
+    // prepare params
+    this->sd_params->model_path = this->ModelFiles.at(this->m_model->GetStringSelection().ToStdString());
+    this->sd_params->lora_model_dir = this->cfg->lora;
+    this->sd_params->embeddings_path = this->cfg->embedding;
+
+    this->sd_params->prompt = this->m_prompt->GetValue().ToStdString();
+    this->sd_params->negative_prompt = this->m_neg_prompt->GetValue().ToStdString();
+
+    this->sd_params->cfg_scale = static_cast<float>(this->m_cfg->GetValue());
+    this->sd_params->seed = this->m_seed->GetValue();
+    this->sd_params->clip_skip = this->m_clip_skip->GetValue();
+    this->sd_params->sample_steps = this->m_steps->GetValue();
+
+    this->sd_params->sample_method = (sample_method_t)this->m_sampler->GetCurrentSelection();
+
+    this->sd_params->batch_count = this->m_batch_count->GetValue();
+
+    this->sd_params->width = this->m_width->GetValue();
+    this->sd_params->height = this->m_height->GetValue();
+
+    // add the queue item
+    auto id = this->qmanager->AddItem(this->sd_params);
 }
 
 void MainWindowUI::onSavePreset(wxCommandEvent &event)
@@ -243,29 +258,30 @@
         this->LoadPresets();
     }
 }
+/*
+    this->m_joblist->AppendTextColumn("Id");
+    this->m_joblist->AppendTextColumn("Created");
+    this->m_joblist->AppendTextColumn("Model");
+    this->m_joblist->AppendTextColumn("Sampler");
+    this->m_joblist->AppendTextColumn("Seed");
+    this->m_joblist->AppendTextColumn("Status");
 
+*/
 void MainWindowUI::OnQueueItemManagerItemAdded(QM::QueueItem item)
 {
     wxVector<wxVariant> data;
 
-    data.push_back(wxVariant(item.id));
-    data.push_back(wxVariant(item.created_at));
-    data.push_back(wxVariant(item.updated_at));
+    data.push_back(wxVariant(std::to_string(item.id)));
+    data.push_back(wxVariant(std::to_string(item.created_at)));
+    data.push_back(wxVariant(item.params.model_path));
+    data.push_back(wxVariant(sd_gui_utils::sample_method_str[(int)item.params.sample_method]));
+    data.push_back(wxVariant(std::to_string(item.params.seed)));
     data.push_back(wxVariant(QM::QueueStatus_str[item.status]));
 
     this->m_joblist->AppendItem(data);
-    /*
-    for (auto model : this->ModelFiles)
-    {
-        // auto size = sd_gui_utils::HumanReadable{std::filesystem::file_size(model.second)};
-        auto size = std::filesystem::file_size(model.second);
 
-        wxVector<wxVariant> data;
-        data.push_back(wxVariant(model.first));
-        data.push_back(wxVariant(std::to_string(size)));
-        this->m_data_model_list->AppendItem(data);
-    }
-    this->m_data_model_list->Refresh();*/
+    auto store = this->m_joblist->GetStore();
+    (*this->JobTableItems)[store->GetCount() - 1] = item;
 }
 
 void MainWindowUI::LoadFileList(sd_gui_utils::DirTypes type)
@@ -396,22 +412,22 @@
 
 void MainWindowUI::OnThreadMessage(wxThreadEvent &e)
 {
+    e.Skip();
 
     auto msg = e.GetString().ToStdString();
 
     std::string token = msg.substr(0, msg.find(":"));
     std::string content = msg.substr(msg.find(":") + 1);
 
-    //    this->logs->AppendText(fmt::format("Got thread message: {}\n", e.GetString().ToStdString()));
+    // this->logs->AppendText(fmt::format("Got thread message: {}\n", e.GetString().ToStdString()));
     if (token == "QUEUE")
     {
-
-        m_statusBar166->SetStatusText("got QUEUE cmd: " + msg);
         // only numbers here...
         QM::QueueEvents event = (QM::QueueEvents)std::stoi(content);
         // only handle the QUEUE messages, what this class generate
         // alway QM::EueueItem the payload, with the new data
-        auto payload = e.GetPayload<QM::QueueItem>();
+        QM::QueueItem payload;
+        payload = e.GetPayload<QM::QueueItem>();
         switch (event)
         {
             // new item added
@@ -420,11 +436,14 @@
             break;
             // item status changed
         case QM::QueueEvents::ITEM_STATUS_CHANGED:
-            this->OnQueueItemManagerItemAdded(payload);
+            this->OnQueueItemManagerItemStatusChanged(payload);
             break;
             // item updated... ? ? ?
         case QM::QueueEvents::ITEM_UPDATED:
-            this->OnQueueItemManagerItemAdded(payload);
+            this->OnQueueItemManagerItemUpdated(payload);
+            break;
+        case QM::QueueEvents::ITEM_START:
+            this->StartGeneration(payload);
             break;
 
         default:
@@ -433,12 +452,13 @@
     }
     if (token == "MODEL_LOAD_DONE")
     {
-        this->m_generate->Enable();
-        this->m_model->Enable();
-        this->m_vae->Enable();
-        this->m_refresh->Enable();
+        // this->m_generate->Enable();
+        // this->m_model->Enable();
+        // this->m_vae->Enable();
+        // this->m_refresh->Enable();
 
-        this->logs->AppendText(fmt::format("Model loaded: {}\n", content));
+        // this->logs->AppendText(fmt::format("Model loaded: {}\n", content));
+        this->modelLoaded = true;
         this->sd_ctx = e.GetPayload<sd_ctx_t *>();
         if (!this->IsShownOnScreen())
         {
@@ -450,19 +470,20 @@
     }
     if (token == "MODEL_LOAD_START")
     {
-        this->m_generate->Disable();
-        this->m_model->Disable();
-        this->m_vae->Disable();
-        this->m_refresh->Disable();
+        // this->m_generate->Disable();
+        // this->m_model->Disable();
+        // this->m_vae->Disable();
+        // this->m_refresh->Disable();
         this->logs->AppendText(fmt::format("Model load start: {}\n", content));
     }
     if (token == "MODEL_LOAD_ERROR")
     {
-        this->m_generate->Disable();
-        this->m_model->Enable();
-        this->m_vae->Disable();
-        this->m_refresh->Enable();
+        // this->m_generate->Disable();
+        // this->m_model->Enable();
+        // this->m_vae->Disable();
+        // this->m_refresh->Enable();
         this->logs->AppendText(fmt::format("Model load error: {}\n", content));
+        this->modelLoaded = false;
         if (!this->IsShownOnScreen())
         {
             this->notification->SetFlags(wxICON_ERROR);
@@ -474,62 +495,62 @@
 
     if (token == "GENERATION_START")
     {
-        sd_gui_utils::SDParams *params = e.GetPayload<sd_gui_utils::SDParams *>();
+        auto myjob = e.GetPayload<QM::QueueItem>();
 
-        this->m_generate->Disable();
-        this->m_model->Disable();
-        this->m_vae->Disable();
-        this->m_refresh->Disable();
-        this->logs->AppendText(fmt::format("Difusion started. Seed: {} Batch: {} {}x{}px Cfg: {} Steps: {}\n",
-                                           params->seed,
-                                           params->batch_count,
-                                           params->width,
-                                           params->height,
-                                           params->cfg_scale,
-                                           params->sample_steps));
+        // this->m_generate->Disable();
+        // this->m_model->Disable();
+        // this->m_vae->Disable();
+        // this->m_refresh->Disable();
+        this->logs->AppendText(fmt::format("Diffusion started. Seed: {} Batch: {} {}x{}px Cfg: {} Steps: {}\n",
+                                           myjob.params.seed,
+                                           myjob.params.batch_count,
+                                           myjob.params.width,
+                                           myjob.params.height,
+                                           myjob.params.cfg_scale,
+                                           myjob.params.sample_steps));
     }
     // never, not implemented in sd.cpp
     if (token == "GENERATION_PROGRESS")
     {
-        this->m_generate->Disable();
-        this->logs->AppendText(fmt::format("Generation progress: {}\n", content));
+        // this->m_generate->Disable();
+        // this->logs->AppendText(fmt::format("Generation progress: {}\n", content));
     }
     if (token == "GENERATION_DONE")
     {
-        this->m_generate->Enable();
-        this->m_model->Enable();
-        this->m_vae->Enable();
-        this->m_refresh->Enable();
-        sd_image_t *results = e.GetPayload<sd_image_t *>();
+        // this->m_generate->Enable();
+        // this->m_model->Enable();
+        // this->m_vae->Enable();
+        // this->m_refresh->Enable();
+        // sd_image_t *results = e.GetPayload<sd_image_t *>();
         // show images in new window...
-        for (int i = 0; i < this->sd_params->batch_count; i++)
-        {
-            MainWindowImageViewer *imgWindow = new MainWindowImageViewer(this);
-            // wxBitmap *img = new wxBitmap(results[i].data, (int)results[i].width, (int)results[i].height, (int)results[i].channel);
-            wxImage img(results[i].width, results[i].height, results[i].data);
+        /* for (int i = 0; i < this->sd_params->batch_count; i++)
+         {
+             MainWindowImageViewer *imgWindow = new MainWindowImageViewer(this);
+             // wxBitmap *img = new wxBitmap(results[i].data, (int)results[i].width, (int)results[i].height, (int)results[i].channel);
+             wxImage img(results[i].width, results[i].height, results[i].data);
 
-            wxBitmapBundle wxBmapB(img);
-            imgWindow->m_bitmap->SetBitmap(wxBmapB);
-            imgWindow->m_bitmap->SetSize(results[i].width, results[i].height);
-            imgWindow->SetSize(results[i].width + 200, results[i].height);
+             wxBitmapBundle wxBmapB(img);
+             imgWindow->m_bitmap->SetBitmap(wxBmapB);
+             imgWindow->m_bitmap->SetSize(results[i].width, results[i].height);
+             imgWindow->SetSize(results[i].width + 200, results[i].height);
 
-            std::string details = fmt::format("Prompt:\n\n{}\n\nNegative prompt: \n\n{}\n\nSeed: {} \nCfg scale: {}\nClip skip: {}\nSampler: {}\nSteps: {}\nWidth: {} Height: {}",
-                                              this->sd_params->prompt, this->sd_params->negative_prompt,
-                                              this->sd_params->seed + i, this->sd_params->cfg_scale,
-                                              this->sd_params->clip_skip, sd_gui_utils::sample_method_str[this->sd_params->sample_method], this->sd_params->sample_steps,
-                                              results[i].width, results[i].height);
-            imgWindow->m_textCtrl4->AppendText(wxString(details));
-            imgWindow->Show();
+             std::string details = fmt::format("Prompt:\n\n{}\n\nNegative prompt: \n\n{}\n\nSeed: {} \nCfg scale: {}\nClip skip: {}\nSampler: {}\nSteps: {}\nWidth: {} Height: {}",
+                                               this->sd_params->prompt, this->sd_params->negative_prompt,
+                                               this->sd_params->seed + i, this->sd_params->cfg_scale,
+                                               this->sd_params->clip_skip, sd_gui_utils::sample_method_str[this->sd_params->sample_method], this->sd_params->sample_steps,
+                                               results[i].width, results[i].height);
+             imgWindow->m_textCtrl4->AppendText(wxString(details));
+             imgWindow->Show();
 
-            // imgWindow->m_bitmap->SetBitmap(img);
-            /// imgWindow->m_bitmap->Set
-        }
+             // imgWindow->m_bitmap->SetBitmap(img);
+             /// imgWindow->m_bitmap->Set
+         }*/
     }
     if (token == "GENERATION_ERROR")
     {
-        this->m_generate->Enable();
-        this->m_model->Enable();
-        this->m_vae->Enable();
+        // this->m_generate->Enable();
+        // this->m_model->Enable();
+        // this->m_vae->Enable();
         this->logs->AppendText(fmt::format("Generation error: {}\n", content));
         if (!this->IsShownOnScreen())
         {
@@ -560,10 +581,11 @@
     }
 }
 
-void MainWindowUI::LoadModel(wxEvtHandler *eventHandler)
+void MainWindowUI::LoadModel(wxEvtHandler *eventHandler, QM::QueueItem myItem)
 {
     wxThreadEvent *e = new wxThreadEvent();
     e->SetString(wxString::Format("MODEL_LOAD_START:%s", this->sd_params->model_path));
+    e->SetPayload(myItem);
     wxQueueEvent(eventHandler, e);
 
     /*this->sd_ctx = new_sd_ctx(this->sd_params->model_path.c_str(),
@@ -580,22 +602,25 @@
                               this->sd_params->rng_type,
                               this->sd_params->schedule,
                               this->sd_params->control_net_cpu);*/
-    this->sd_ctx = new_sd_ctx(this->sd_params->model_path.c_str(), this->sd_params->vae_path.c_str(), this->sd_params->taesd_path.c_str(), this->sd_params->lora_model_dir.c_str(), true, false, false, this->sd_params->n_threads, this->sd_params->wtype, this->sd_params->rng_type, this->sd_params->schedule);
+    // this->sd_ctx = new_sd_ctx(this->sd_params->model_path.c_str(), this->sd_params->vae_path.c_str(), this->sd_params->taesd_path.c_str(), this->sd_params->lora_model_dir.c_str(), true, false, false, this->sd_params->n_threads, this->sd_params->wtype, this->sd_params->rng_type, this->sd_params->schedule);
+    this->sd_ctx = new_sd_ctx(myItem.params.model_path.c_str(), myItem.params.vae_path.c_str(), myItem.params.taesd_path.c_str(), myItem.params.lora_model_dir.c_str(), true, false, false, myItem.params.n_threads, myItem.params.wtype, myItem.params.rng_type, myItem.params.schedule);
 
     if (this->sd_ctx == NULL)
     {
         wxThreadEvent *c = new wxThreadEvent();
         c->SetString(wxString::Format("MODEL_LOAD_ERROR:%s", this->sd_params->model_path));
+        c->SetPayload(myItem);
         wxQueueEvent(eventHandler, c);
+        this->modelLoaded = false;
         return;
     }
     else
     {
         wxThreadEvent *c = new wxThreadEvent();
         c->SetString(wxString::Format("MODEL_LOAD_DONE:%s", this->sd_params->model_path));
-        // c->SetEventObject(this->sd_ctx);
         c->SetPayload(this->sd_ctx);
         wxQueueEvent(eventHandler, c);
+        this->modelLoaded = true;
         return;
     }
 
@@ -639,7 +664,7 @@
 
     // populate data from sd_params as default...
 
-    if (!this->m_generate->IsEnabled())
+    if (!this->modelLoaded)
     {
         this->m_cfg->SetValue(static_cast<double>(this->sd_params->cfg_scale));
         this->m_seed->SetValue(static_cast<int>(this->sd_params->seed));
@@ -649,38 +674,11 @@
         this->m_height->SetValue(this->sd_params->height);
         this->m_batch_count->SetValue(this->sd_params->batch_count);
     }
-    // hide unusable configs...
-    /*
-    if (SD_CPP_VERSION == "c6071fa") {
-            // .. nope, configs in another window...
-    }*/
 }
 
-void MainWindowUI::StartGeneration()
+void MainWindowUI::StartGeneration(QM::QueueItem myJob)
 {
-
-    // prepare params
-    this->sd_params->model_path = this->ModelFiles.at(this->m_model->GetStringSelection().ToStdString());
-    this->sd_params->lora_model_dir = this->cfg->lora;
-    this->sd_params->embeddings_path = this->cfg->embedding;
-
-    this->sd_params->prompt = this->m_prompt->GetValue().ToStdString();
-    this->sd_params->negative_prompt = this->m_neg_prompt->GetValue().ToStdString();
-
-    this->sd_params->cfg_scale = static_cast<float>(this->m_cfg->GetValue());
-    this->sd_params->seed = this->m_seed->GetValue();
-    this->sd_params->clip_skip = this->m_clip_skip->GetValue();
-    this->sd_params->sample_steps = this->m_steps->GetValue();
-
-    /* sample method */
-    this->sd_params->sample_method = (sample_method_t)this->m_sampler->GetCurrentSelection();
-    /* sample method */
-    this->sd_params->batch_count = this->m_batch_count->GetValue();
-
-    this->sd_params->width = this->m_width->GetValue();
-    this->sd_params->height = this->m_height->GetValue();
-
-    this->threads.push_back(std::thread(std::bind(&MainWindowUI::Generate, this, this->GetEventHandler())));
+    this->threads.push_back(std::thread(std::bind(&MainWindowUI::Generate, this, this->GetEventHandler(), myJob)));
 }
 
 void MainWindowUI::OnCloseSettings(wxCloseEvent &event)
@@ -717,31 +715,46 @@
     this->m_data_model_list->Refresh();
 }
 
-void MainWindowUI::Generate(wxEvtHandler *eventHandler)
+void MainWindowUI::Generate(wxEvtHandler *eventHandler, QM::QueueItem myItem)
 {
-    // calculate time
+    // @brief model loading is done in the same thread which generating stuffs... no need new thread
+    // @brief to load the model if no model loaded, or the loaded model is different from the job's model...
+    // @brief if all second job have different model, it will be slower than same model on all jobs
+
+    // TODO: abort job if model can not be loaded...
+    if (!this->modelLoaded)
+    {
+        this->LoadModel(eventHandler, myItem);
+        this->currentModel = myItem.params.model_path;
+    }
+    else
+    {
+        if (myItem.params.model_path != this->currentModel)
+        {
+            free_sd_ctx(this->sd_ctx);
+            this->LoadModel(eventHandler, myItem);
+        }
+    }
+    if (!this->modelLoaded)
+    {
+        wxThreadEvent *f = new wxThreadEvent();
+        f->SetString("GENERATION_ERROR:Model load failed...");
+        f->SetPayload(myItem);
+        wxQueueEvent(eventHandler, f);
+        return;
+    }
+
     auto start = std::chrono::system_clock::now();
 
     wxThreadEvent *e = new wxThreadEvent();
     e->SetString(wxString::Format("GENERATION_START:%s", this->sd_params->model_path));
-    e->SetPayload(this->sd_params);
+    e->SetPayload(myItem);
     wxQueueEvent(eventHandler, e);
 
     sd_image_t *control_image = NULL;
     sd_image_t *results;
-    /*results = txt2img(sd_ctx,
-                      this->sd_params->prompt.c_str(),
-                      this->sd_params->negative_prompt.c_str(),
-                      this->sd_params->clip_skip,
-                      this->sd_params->cfg_scale,
-                      this->sd_params->width,
-                      this->sd_params->height,
-                      this->sd_params->sample_method,
-                      this->sd_params->sample_steps,
-                      this->sd_params->seed,
-                      this->sd_params->batch_count,
-                      control_image,
-                      this->sd_params->control_strength);*/
+
+
     results = txt2img(this->sd_ctx,
                       this->sd_params->prompt.c_str(), this->sd_params->negative_prompt.c_str(), this->sd_params->clip_skip, this->sd_params->cfg_scale, this->sd_params->width, this->sd_params->height, this->sd_params->sample_method, this->sd_params->sample_steps, this->sd_params->seed, this->sd_params->batch_count);
 
@@ -749,6 +762,7 @@
     {
         wxThreadEvent *f = new wxThreadEvent();
         f->SetString("GENERATION_ERROR:Something wrong happened at image generation...");
+        f->SetPayload(myItem);
         wxQueueEvent(eventHandler, f);
         return;
     }
@@ -785,7 +799,12 @@
         {
             wxThreadEvent *g = new wxThreadEvent();
             g->SetString(wxString::Format("GENERATION_ERROR:Failed to save image into %s", filename));
+            g->SetPayload(myItem);
             wxQueueEvent(eventHandler, g);
+        }
+        else
+        {
+            myItem.images.emplace_back(filename);
         }
 
         // handle data??
@@ -798,18 +817,29 @@
     wxThreadEvent *h = new wxThreadEvent();
     auto msg = fmt::format("MESSAGE:Image generation done in {}s. Saved into {}", elapsed_seconds.count(), this->cfg->output);
     h->SetString(wxString(msg.c_str()));
+    h->SetPayload(myItem);
     wxQueueEvent(eventHandler, h);
 
-    // send to reset the buttons
     wxThreadEvent *i = new wxThreadEvent();
     i->SetString(wxString::Format("GENERATION_DONE:ok"));
     i->SetPayload(results);
     wxQueueEvent(eventHandler, i);
+
+    // send to the queue manager
+    wxThreadEvent *j = new wxThreadEvent();
+    j->SetString(wxString::Format("QUEUE:%d", QM::QueueEvents::ITEM_FINISHED));
+    j->SetPayload(myItem);
+    wxQueueEvent(eventHandler, j);
+
     return;
 }
 
 void MainWindowUI::HandleSDLog(sd_log_level_t level, const char *text, void *data)
 {
+    if (level != sd_log_level_t::SD_LOG_INFO)
+    {
+        return;
+    }
     // wxEvtHandler *eventHandler = (wxEvtHandler *)data;
     MainWindowUI *ui = (MainWindowUI *)data;
     wxEvtHandler *eventHandler = ui->GetEventHandler();
@@ -821,12 +851,37 @@
 
 void MainWindowUI::OnQueueItemManagerItemStatusChanged(QM::QueueItem item)
 {
+    /*
+    this->m_joblist->AppendTextColumn("Id");
+    this->m_joblist->AppendTextColumn("Created");
+    this->m_joblist->AppendTextColumn("Model");
+    this->m_joblist->AppendTextColumn("Sampler");
+    this->m_joblist->AppendTextColumn("Seed");
+    this->m_joblist->AppendTextColumn("Status");
+
+    */
+    // TODO: how to update an item in the table...
+    // auto item = this->m_joblist->RowToItem();
+    auto store = this->m_joblist->GetStore();
+
+    for (auto it = this->JobTableItems->begin(); it != this->JobTableItems->end(); ++it)
+    {
+        if (it->second.id == item.id)
+        {
+            // auto store = *this->m_joblist()->GetStore();
+            int lastCol = this->m_joblist->GetColumnCount();
+            // always update the last col, so the last col is always need to be the status
+            store->SetValueByRow(wxVariant(QM::QueueStatus_str[item.status]), it->first, lastCol - 1);
+            this->m_joblist->Refresh();
+            break;
+        }
+    }
 }
 
 MainWindowUI::~MainWindowUI()
 {
     // clean up things...
-    if (this->m_generate->IsEnabled())
+    if (this->modelLoaded)
     {
         free_sd_ctx(this->sd_ctx);
     }
@@ -861,7 +916,7 @@
         free_sd_ctx(this->sd_ctx);
         return;
     }
-    if (this->m_generate->IsEnabled())
+    if (this->modelLoaded)
     {
         free_sd_ctx(this->sd_ctx);
     }
@@ -871,5 +926,5 @@
     this->m_refresh->Disable();
     this->sd_params->model_path = this->ModelFiles.at(selection.ToStdString());
     this->sd_params->lora_model_dir = this->fileConfig->Read("/paths/lora", "").ToStdString();
-    this->threads.push_back(std::thread(std::bind(&MainWindowUI::LoadModel, this, this->GetEventHandler())));
+    // this->threads.push_back(std::thread(std::bind(&MainWindowUI::LoadModel, this, this->GetEventHandler())));
 }
\ No newline at end of file
diff --git a/ui/MainWindowUI.h b/ui/MainWindowUI.h
index fc66871..38ced0c 100644
--- a/ui/MainWindowUI.h
+++ b/ui/MainWindowUI.h
@@ -33,27 +33,28 @@
 /** Implementing UI */
 class MainWindowUI : public UI
 {
-	protected:
-		// Handlers for UI events.
-		void onSettings( wxCommandEvent& event );
-		void onModelsRefresh( wxCommandEvent& event );
-		void onModelSelect( wxCommandEvent& event );
-		void onVaeSelect( wxCommandEvent& event );
-		void onSamplerSelect( wxCommandEvent& event );
-		void onResolutionSwap( wxCommandEvent& event );
-		void onJobsStart( wxCommandEvent& event );
-		void onJobsPause( wxCommandEvent& event );
-		void onJobsDelete( wxCommandEvent& event );
-		void onJoblistItemActivated( wxDataViewEvent& event );
-		void onJoblistSelectionChanged( wxDataViewEvent& event );
-		void onGenerate( wxCommandEvent& event );
-		void onSavePreset( wxCommandEvent& event );
-		void onLoadPreset( wxCommandEvent& event );
-		void onSelectPreset( wxCommandEvent& event );
-		void onDeletePreset( wxCommandEvent& event );
-	public:
-		/** Constructor */
-		MainWindowUI( wxWindow* parent );
+protected:
+	// Handlers for UI events.
+	void onSettings(wxCommandEvent &event);
+	void onModelsRefresh(wxCommandEvent &event);
+	void onModelSelect(wxCommandEvent &event);
+	void onVaeSelect(wxCommandEvent &event);
+	void onSamplerSelect(wxCommandEvent &event);
+	void onResolutionSwap(wxCommandEvent &event);
+	void onJobsStart(wxCommandEvent &event);
+	void onJobsPause(wxCommandEvent &event);
+	void onJobsDelete(wxCommandEvent &event);
+	void onJoblistItemActivated(wxDataViewEvent &event);
+	void onJoblistSelectionChanged(wxDataViewEvent &event);
+	void onGenerate(wxCommandEvent &event);
+	void onSavePreset(wxCommandEvent &event);
+	void onLoadPreset(wxCommandEvent &event);
+	void onSelectPreset(wxCommandEvent &event);
+	void onDeletePreset(wxCommandEvent &event);
+
+public:
+	/** Constructor */
+	MainWindowUI(wxWindow *parent);
 	//// end generated class members
 	~MainWindowUI();
 	void OnThreadMessage(wxThreadEvent &e);
@@ -71,9 +72,14 @@
 
 	// the queue manager
 	QM::QueueManager *qmanager;
+	bool modelLoaded = false;
+	std::string currentModel;
 	sd_ctx_t *sd_ctx;
 	std::streambuf *buffer;
 	std::vector<std::thread> threads;
+
+	// row,QueueItem
+	std::map<int, QM::QueueItem> *JobTableItems;
 
 	void initConfig();
 	void loadModelList();
@@ -85,14 +91,14 @@
 	static void HandleSDLog(sd_log_level_t level, const char *text, void *data);
 
 	// load the model in a new thread
-	void LoadModel(wxEvtHandler *eventHandler);
+	void LoadModel(wxEvtHandler *eventHandler, QM::QueueItem myItem);
 	// generate in another thread
-	void Generate(wxEvtHandler *eventHandler);
+	void Generate(wxEvtHandler *eventHandler, QM::QueueItem myItem);
 
 	// start a thread for model loading...
 	void StartLoadModel();
 	// start a thread to generate image
-	void StartGeneration();
+	void StartGeneration(QM::QueueItem myJob);
 
 	// handle queue managers events, manipulate data table by events
 	void OnQueueItemManagerItemAdded(QM::QueueItem item);
@@ -100,7 +106,6 @@
 	void OnQueueItemManagerItemStatusChanged(QM::QueueItem item);
 
 	wxNotificationMessage *notification;
-
 };
 
 #endif // __MainWindowUI__
diff --git a/ui/QueueManager.cpp b/ui/QueueManager.cpp
index 126c7b5..bbe73cd 100644
--- a/ui/QueueManager.cpp
+++ b/ui/QueueManager.cpp
@@ -6,6 +6,7 @@
     this->eventHandler = eventHandler;
     this->eventHandler->Bind(wxEVT_THREAD, &QueueManager::OnThreadMessage, this);
     this->jobsDir = jobsdir;
+    this->QueueList = std::map<int, QM::QueueItem>();
 }
 
 int QM::QueueManager::AddItem(QM::QueueItem item)
@@ -16,6 +17,11 @@
     }
     this->QueueList[item.id] = item;
     this->SendEventToMainWindow(QM::QueueEvents::ITEM_ADDED, item);
+    if (this->isRunning == false)
+    {
+        this->SendEventToMainWindow(QM::QueueEvents::ITEM_START, item);
+        this->isRunning = true;
+    }
     return item.id;
 }
 
@@ -24,8 +30,7 @@
     QM::QueueItem item;
     item.params = *params;
     item.created_at = item.id = this->GetCurrentUnixTimestamp();
-    this->AddItem(item);
-    return item.id;
+    return this->AddItem(item);
 }
 
 int QM::QueueManager::AddItem(sd_gui_utils::SDParams params)
@@ -33,8 +38,7 @@
     QM::QueueItem item;
     item.params = params;
     item.created_at = item.id = this->GetCurrentUnixTimestamp();
-    this->AddItem(item);
-    return item.id;
+    return this->AddItem(item);
 }
 
 QM::QueueItem QM::QueueManager::GetItem(int id)
@@ -45,7 +49,7 @@
     }
     else
     {
-        return this->QueueList.at(id);
+        return this->QueueList[id];
     }
 }
 
@@ -78,15 +82,16 @@
 
 void QM::QueueManager::SetStatus(QM::QueueStatus status, int id)
 {
-    // todo, start - stop - pause
-    auto item = this->GetItem(id);
-    item.status = status;
-    this->SendEventToMainWindow(QM::QueueEvents::ITEM_STATUS_CHANGED, item);
+    if (this->QueueList.find(id) != this->QueueList.end())
+    {
+        this->QueueList[id].status = status;
+        this->SendEventToMainWindow(QM::QueueEvents::ITEM_STATUS_CHANGED, this->QueueList[id]);
+    }
 }
 
 void QM::QueueManager::PauseAll()
 {
-    for (auto &[key, value] : this->QueueList)
+    for (auto [key, value] : this->QueueList)
     {
         if (value.status == QM::QueueStatus::PENDING)
         {
@@ -99,21 +104,83 @@
 void QM::QueueManager::SendEventToMainWindow(QM::QueueEvents eventType, QM::QueueItem item)
 {
     wxThreadEvent *e = new wxThreadEvent();
-    // e->SetString(wxString::Format("MODEL_LOAD_START:%s", this->sd_params->model_path));
-    e->SetString(wxString("QUEUE:" + (int)eventType));
+    e->SetString(wxString::Format("QUEUE:%d", (int)eventType));
     e->SetPayload(item);
     wxQueueEvent(this->eventHandler, e);
 }
 
 void QM::QueueManager::OnThreadMessage(wxThreadEvent &e)
 {
+
+    e.Skip();
     auto msg = e.GetString().ToStdString();
 
     std::string token = msg.substr(0, msg.find(":"));
     std::string content = msg.substr(msg.find(":") + 1);
     // only numbers here...
-    QM::QueueEvents event = (QM::QueueEvents)std::stoi(content);
+
     // only handle the QUEUE messages, what this class generate
+    if (token == "QUEUE")
+    {
+        QM::QueueEvents event = (QM::QueueEvents)std::stoi(content);
+        auto payload = e.GetPayload<QM::QueueItem>();
+        if (event == QM::QueueEvents::ITEM_START)
+        {
+            this->SetStatus(QM::QueueStatus::RUNNING, payload.id);
+            this->isRunning = true;
+        }
+        if (event == QM::QueueEvents::ITEM_FINISHED)
+        {
+            this->SetStatus(QM::QueueStatus::DONE, payload.id);
+            this->isRunning = false;
+            // jump to the next item in queue
+            // find waiting jobs
+            for (auto job : this->QueueList)
+            {
+                if (job.second.status == QM::QueueStatus::PENDING)
+                {
+                    if (this->isRunning == false)
+                    {
+                        this->SendEventToMainWindow(QM::QueueEvents::ITEM_START, job.second);
+                        this->isRunning = true;
+                    }
+                    break;
+                }
+            }
+            // move finished job into another list - ??
+        }
+    }
+
+    if (token == "MODEL_LOAD_START")
+    {
+        auto payload = e.GetPayload<QM::QueueItem>();
+        this->SetStatus(QM::QueueStatus::MODEL_LOADING, payload.id);
+    }
+
+    // this state can not usable at here, because the payload is the sd_ctx* pointer here..
+    // we can't identify the current running job here... (we can maybe guess it, but not needed)
+    // see  GENERATION_START
+    if (token == "MODEL_LOAD_DONE")
+    {
+        // auto payload = e.GetPayload<QM::QueueItem>();
+        // this->SetStatus(QM::QueueStatus::RUNNING, payload.id);
+    }
+    if (token == "MODEL_LOAD_ERROR" || token == "GENERATION_ERROR")
+    {
+        auto payload = e.GetPayload<QM::QueueItem>();
+        this->SetStatus(QM::QueueStatus::FAILED, payload.id);
+        this->isRunning = false;
+    }
+    if (token == "GENERATION_START")
+    {
+        auto payload = e.GetPayload<QM::QueueItem>();
+        this->SetStatus(QM::QueueStatus::RUNNING, payload.id);
+        this->isRunning = true;
+    }
+    // nothing to todo here, the payload is the generated image list
+    if (token == "GENERATION_DONE")
+    {
+    }
 }
 
 int QM::QueueManager::GetCurrentUnixTimestamp()
diff --git a/ui/QueueManager.h b/ui/QueueManager.h
index f7b3e02..baaf7fb 100644
--- a/ui/QueueManager.h
+++ b/ui/QueueManager.h
@@ -17,27 +17,34 @@
         PENDING,
         RUNNING,
         PAUSED,
-        FAILED
+        FAILED,
+        MODEL_LOADING,
+        DONE
     };
     inline const char *QueueStatus_str[] = {
         "pending",
         "running",
         "paused",
-        "failed"};
+        "failed",
+        "model loading...",
+        "finished"
+        };
 
     enum QueueEvents
     {
         ITEM_DELETED,
         ITEM_ADDED,
         ITEM_STATUS_CHANGED,
-        ITEM_UPDATED
+        ITEM_UPDATED,
+        ITEM_START,
+        ITEM_FINISHED
     };
     struct QueueItem
     {
         QueueItem() = default;
         QueueItem(const QueueItem &other)
             : id(other.id), created_at(other.created_at), updated_at(other.updated_at),
-              finished_at(other.finished_at), params(other.params), status(other.status) {}
+              finished_at(other.finished_at), params(other.params), status(other.status), images(other.images) {}
 
         QueueItem &operator=(const QueueItem &other)
         {
@@ -47,6 +54,7 @@
                 created_at = other.created_at;
                 updated_at = other.updated_at;
                 finished_at = other.finished_at;
+                images = other.images;
                 // Másolatot készítünk a params referenciáról
                 // Ha a params referenciához tartozó SDParams objektum módosulna,
                 // akkor ebben az esetben másolatban marad az eredeti QueueItem-ben
@@ -55,9 +63,10 @@
             }
             return *this;
         }
-        int id, created_at, updated_at, finished_at;
+        int id, created_at = 0, updated_at = 0, finished_at = 0;
         sd_gui_utils::SDParams params;
         QM::QueueStatus status = QM::QueueStatus::PENDING;
+        std::vector<std::string> images;
     };
 
     inline void to_json(nlohmann::json &j, const QueueItem &p)
@@ -67,6 +76,7 @@
             {"created_at", p.created_at},
             {"updated_at", p.updated_at},
             {"finished_at", p.finished_at},
+            {"images", p.images},
             //   {"params", p.params},
             {"status", (int)p.status}};
     }
@@ -76,6 +86,7 @@
         j.at("id").get_to(p.id);
         j.at("created_at").get_to(p.created_at);
         j.at("updated_at").get_to(p.updated_at);
+        j.at("images").get_to(p.images);
         j.at("finished_at").get_to(p.finished_at);
         //  j.at("params").get_to(p.params);
         p.status = j.at("status").get<QM::QueueStatus>();
@@ -105,6 +116,8 @@
         void onItemAdded(QM::QueueItem item);
         void LodJobsFromFile();
         void SaveJobsToFile();
+        // @brief check if something is running or not
+        bool isRunning = false;
 
         wxEvtHandler *eventHandler;
         wxWindow *parent;

--
Gitblit v1.9.3