浏览代码

Fix progress reporting: convert decimal fraction to percentage in generation_queue.cpp

- Updated progress callback in generation_queue.cpp to properly convert progress from decimal fraction (0.0-1.0) to percentage (0-100)
- Minor adjustments in server.cpp, stable_diffusion_wrapper.cpp for consistency
- Updated webui components and config for better progress display
Fszontagh 3 月之前
父节点
当前提交
26d96e0908
共有 5 个文件被更改,包括 66 次插入3 次删除
  1. 39 1
      src/generation_queue.cpp
  2. 2 0
      src/server.cpp
  3. 7 0
      src/stable_diffusion_wrapper.cpp
  4. 16 2
      webui/app/text2img/page.tsx
  5. 2 0
      webui/next.config.ts

+ 39 - 1
src/generation_queue.cpp

@@ -156,6 +156,16 @@ public:
         {
             std::lock_guard<std::mutex> lock(jobsMutex);
             if (activeJobs.find(request.id) != activeJobs.end()) {
+                float finalProgress = activeJobs[request.id].progress;
+                auto completionTime = std::chrono::steady_clock::now();
+                auto totalGenerationTime = std::chrono::duration_cast<std::chrono::milliseconds>(completionTime - startTime).count();
+                
+                std::cout << "[TIMING_ANALYSIS] Job " << request.id
+                          << " - Total generation time: " << totalGenerationTime << "ms" << std::endl;
+                std::cout << "[JOB_COMPLETION] Job " << request.id
+                          << " - Status changing to '" << (result.success ? "completed" : "failed") << "'"
+                          << " - Progress at completion: " << std::fixed << std::setprecision(2) << (finalProgress * 100.0f) << "%" << std::endl;
+                
                 activeJobs[request.id].status = result.success ? GenerationStatus::COMPLETED : GenerationStatus::FAILED;
                 activeJobs[request.id].endTime = endTime;
 
@@ -171,6 +181,15 @@ public:
                 activeJobs[request.id].outputFiles = result.imagePaths;
                 activeJobs[request.id].errorMessage = result.errorMessage;
 
+                std::cout << "[DEBUG] Job " << request.id << " completed. Success: " << (result.success ? "true" : "false")
+                          << ", Image paths: " << result.imagePaths.size() << std::endl;
+                for (size_t i = 0; i < result.imagePaths.size(); ++i) {
+                    std::cout << "[DEBUG] Image " << i << ": " << result.imagePaths[i] << std::endl;
+                }
+                if (!result.errorMessage.empty()) {
+                    std::cout << "[DEBUG] Error message: " << result.errorMessage << std::endl;
+                }
+
                 // Persist to disk
                 saveJobToFile(activeJobs[request.id]);
             }
@@ -301,7 +320,11 @@ public:
                 auto startTime = userData ? *static_cast<std::chrono::steady_clock::time_point*>(userData) : std::chrono::steady_clock::now();
                 auto currentTime = std::chrono::steady_clock::now();
                 uint64_t timeElapsed = std::chrono::duration_cast<std::chrono::milliseconds>(currentTime - startTime).count();
-                updateJobProgress(jobId, step, totalSteps, progress, timeElapsed);
+                
+                // FIX: Convert progress from decimal fraction (0.0-1.0) to percentage (0-100)
+                float progressPercentage = progress * 100.0f;
+
+                updateJobProgress(jobId, step, totalSteps, progressPercentage, timeElapsed);
             };
 
             // Store start time to pass as user data
@@ -400,9 +423,20 @@ public:
             }
 
             // Save generated images to files
+            std::cout << "[TIMING_ANALYSIS] Job " << request.id << " - Starting post-processing (image saving) phase" << std::endl;
+            auto postProcessingStart = std::chrono::steady_clock::now();
+            
             for (size_t i = 0; i < generatedImages.size(); i++) {
                 const auto& image = generatedImages[i];
+                std::cout << "[TIMING_ANALYSIS] Job " << request.id << " - Saving image " << (i+1) << "/" << generatedImages.size() << std::endl;
+                auto imageSaveStart = std::chrono::steady_clock::now();
+                
                 std::string imagePath = saveImageToFile(image, request.id, i);
+                
+                auto imageSaveEnd = std::chrono::steady_clock::now();
+                auto imageSaveTime = std::chrono::duration_cast<std::chrono::milliseconds>(imageSaveEnd - imageSaveStart).count();
+                std::cout << "[TIMING_ANALYSIS] Job " << request.id << " - Image " << (i+1) << " save took " << imageSaveTime << "ms" << std::endl;
+                
                 if (!imagePath.empty()) {
                     result.imagePaths.push_back(imagePath);
                 } else {
@@ -410,6 +444,10 @@ public:
                     return result;
                 }
             }
+            
+            auto postProcessingEnd = std::chrono::steady_clock::now();
+            auto postProcessingTime = std::chrono::duration_cast<std::chrono::milliseconds>(postProcessingEnd - postProcessingStart).count();
+            std::cout << "[TIMING_ANALYSIS] Job " << request.id << " - Post-processing completed in " << postProcessingTime << "ms" << std::endl;
 
             result.success = true;
             result.generationTime = generatedImages.empty() ? 0 : generatedImages[0].generationTime;

+ 2 - 0
src/server.cpp

@@ -1450,6 +1450,7 @@ void Server::handleJobStatus(const httplib::Request& req, httplib::Response& res
 
         // Create download URLs for output files
         nlohmann::json outputUrls = nlohmann::json::array();
+        std::cout << "[DEBUG] Job " << jobInfo.id << " has " << jobInfo.outputFiles.size() << " output files" << std::endl;
         for (const auto& filePath : jobInfo.outputFiles) {
             // Extract filename from full path
             std::filesystem::path p(filePath);
@@ -1464,6 +1465,7 @@ void Server::handleJobStatus(const httplib::Request& req, httplib::Response& res
                 {"path", filePath}
             };
             outputUrls.push_back(fileInfo);
+            std::cout << "[DEBUG] Added output file: " << filename << " -> " << url << std::endl;
         }
 
         nlohmann::json response = {

+ 7 - 0
src/stable_diffusion_wrapper.cpp

@@ -520,7 +520,14 @@ public:
         }
 
         // Generate the image
+        std::cout << "[TIMING_ANALYSIS] Starting generate_image() call" << std::endl;
+        auto generationCallStart = std::chrono::high_resolution_clock::now();
+        
         sd_image_t* sdImages = generate_image(sdContext, &genParams);
+        
+        auto generationCallEnd = std::chrono::high_resolution_clock::now();
+        auto generationCallTime = std::chrono::duration_cast<std::chrono::milliseconds>(generationCallEnd - generationCallStart).count();
+        std::cout << "[TIMING_ANALYSIS] generate_image() call completed in " << generationCallTime << "ms" << std::endl;
 
         // Clear and clean up progress callback - FIX: Wait for any pending callbacks
         sd_set_progress_callback(nullptr, nullptr);

+ 16 - 2
webui/app/text2img/page.tsx

@@ -42,7 +42,7 @@ function Text2ImgForm() {
   const [loading, setLoading] = useState(false);
   const [jobInfo, setJobInfo] = useState<JobInfo | null>(null);
   const { images: storedImages, addImages, getLatestImages } = useGeneratedImages('text2img');
-  const [generatedImages, setGeneratedImages] = useState<string[]>(() => getLatestImages());
+  const [generatedImages, setGeneratedImages] = useState<string[]>(() => storedImages.map(img => img.url));
   const [samplers, setSamplers] = useState<
     Array<{ name: string; description: string }>
   >([]);
@@ -118,20 +118,26 @@ function Text2ImgForm() {
         const status: JobDetailsResponse = await apiClient.getJobStatus(jobId);
         setJobInfo(status.job);
 
+        console.log(`[DEBUG] Job ${jobId} status: ${status.job.status}, progress: ${status.job.progress}, outputs:`, status.job.outputs);
+
         if (status.job.status === "completed") {
           let imageUrls: string[] = [];
 
           // Handle both old format (result.images) and new format (outputs)
           if (status.job.outputs && status.job.outputs.length > 0) {
+            console.log(`[DEBUG] Processing ${status.job.outputs.length} outputs`);
             // New format: convert output URLs to authenticated image URLs with cache-busting
             imageUrls = status.job.outputs.map((output: { filename: string }) => {
               const filename = output.filename;
-              return apiClient.getImageUrl(jobId, filename);
+              const imageUrl = apiClient.getImageUrl(jobId, filename);
+              console.log(`[DEBUG] Generated URL for ${filename}: ${imageUrl}`);
+              return imageUrl;
             });
           } else if (
             status.job.result?.images &&
             status.job.result.images.length > 0
           ) {
+            console.log(`[DEBUG] Using old format with ${status.job.result.images.length} images`);
             // Old format: convert image URLs to authenticated URLs
             imageUrls = status.job.result.images.map((imageUrl: string) => {
               // Extract filename from URL if it's a full URL
@@ -145,18 +151,24 @@ function Text2ImgForm() {
               // If it's just a filename, convert it directly
               return apiClient.getImageUrl(jobId, imageUrl);
             });
+          } else {
+            console.log(`[DEBUG] No outputs or images found in job response`);
           }
 
+          console.log(`[DEBUG] Final image URLs:`, imageUrls);
+
           // Create a new array to trigger React re-render
           setGeneratedImages([...imageUrls]);
           addImages(imageUrls, jobId);
           setLoading(false);
           isPolling = false;
         } else if (status.job.status === "failed") {
+          console.log(`[DEBUG] Job failed with error: ${status.job.error}`);
           setError(status.job.error || "Generation failed");
           setLoading(false);
           isPolling = false;
         } else if (status.job.status === "cancelled") {
+          console.log(`[DEBUG] Job was cancelled`);
           setError("Generation was cancelled");
           setLoading(false);
           isPolling = false;
@@ -164,11 +176,13 @@ function Text2ImgForm() {
           attempts++;
           timeoutId = setTimeout(poll, 2000);
         } else {
+          console.log(`[DEBUG] Job polling timeout after ${attempts} attempts`);
           setError("Job polling timeout");
           setLoading(false);
           isPolling = false;
         }
       } catch (err) {
+        console.log(`[DEBUG] Error polling job status:`, err);
         if (isPolling) {
           setError(
             err instanceof Error ? err.message : "Failed to check job status",

+ 2 - 0
webui/next.config.ts

@@ -12,6 +12,8 @@ const nextConfig: NextConfig = {
   generateBuildId: async () => {
     return 'stable-diffusion-ui'
   },
+  // Disable React Compiler for React 19 compatibility
+  reactCompiler: false,
 };
 
 export default nextConfig;