diff --git a/include/libcamera/internal/software_isp/software_isp.h b/include/libcamera/internal/software_isp/software_isp.h
index 1f3eb75f6..45128f25f 100644
--- a/include/libcamera/internal/software_isp/software_isp.h
+++ b/include/libcamera/internal/software_isp/software_isp.h
@@ -104,6 +104,7 @@ private:
 	DebayerParams debayerParams_;
 	std::queue<uint32_t> availableParams_;
 	bool allocateParamsBuffers(const unsigned int bufferCount);
+	std::queue<uint32_t> availableStats_;
 	std::unique_ptr<SwStatsCpu> allocateStatsBuffers(
 		const GlobalConfiguration &configuration,
 		std::vector<SharedFD> &fdStats,
diff --git a/src/libcamera/software_isp/debayer_egl.cpp b/src/libcamera/software_isp/debayer_egl.cpp
index 5715b80ae..66e1fcac2 100644
--- a/src/libcamera/software_isp/debayer_egl.cpp
+++ b/src/libcamera/software_isp/debayer_egl.cpp
@@ -537,7 +537,7 @@ int DebayerEGL::debayerGPU(MappedFrameBuffer &in, int out_fd, const DebayerParam
 }
 
 void DebayerEGL::process(uint32_t frame,
-			 [[maybe_unused]] const uint32_t statsBufferId,
+			 const uint32_t statsBufferId,
 			 const uint32_t paramsBufferId,
 			 FrameBuffer *input,
 			 FrameBuffer *output)
@@ -573,7 +573,7 @@ void DebayerEGL::process(uint32_t frame,
 	metadata.planes()[0].bytesused = output->planes()[0].length;
 
 	/* Calculate stats for the whole frame */
-	stats_->processFrame(frame, 0, input);
+	stats_->processFrame(frame, statsBufferId, input);
 	dmaSyncers.clear();
 
 	outputBufferReady.emit(output);
diff --git a/src/libcamera/software_isp/software_isp.cpp b/src/libcamera/software_isp/software_isp.cpp
index a1a9ddbf4..f3ee5773b 100644
--- a/src/libcamera/software_isp/software_isp.cpp
+++ b/src/libcamera/software_isp/software_isp.cpp
@@ -222,6 +222,7 @@ std::unique_ptr<SwStatsCpu> SoftwareIsp::allocateStatsBuffers(
 		unsigned int bufferId = shared.fd().get();
 		fdStats.emplace_back(shared.fd());
 		sharedStats->emplace(bufferId, std::move(shared));
+		availableStats_.push(bufferId);
 	}
 
 	auto stats = std::make_unique<SwStatsCpu>(configuration, std::move(sharedStats));
@@ -457,11 +458,18 @@ void SoftwareIsp::process(uint32_t frame, FrameBuffer *input, FrameBuffer *outpu
 		while (availableParams_.empty())
 			;
 	}
+	if (availableStats_.empty()) {
+		LOG(SoftwareIsp, Error) << "Statistics buffer underrun";
+		/* Well, busy loop, but this situation shouldn't normally happen. */
+		while (availableStats_.empty())
+			;
+	}
 
 	const uint32_t paramsBufferId = availableParams_.front();
 	availableParams_.pop();
+	const uint32_t statsBufferId = availableStats_.front();
+	availableStats_.pop();
 	ipa_->computeParams(frame, paramsBufferId);
-	const uint32_t statsBufferId = 0;
 	debayer_->invokeMethod(&Debayer::process,
 			       ConnectionTypeQueued, frame,
 			       statsBufferId, paramsBufferId, input, output);
@@ -487,8 +495,9 @@ void SoftwareIsp::statsReady(uint32_t frame, const uint32_t statsBufferId)
 	ispStatsReady.emit(frame, statsBufferId);
 }
 
-void SoftwareIsp::statsProcessed([[maybe_unused]] const uint32_t statsBufferId)
+void SoftwareIsp::statsProcessed(const uint32_t statsBufferId)
 {
+	availableStats_.push(statsBufferId);
 }
 
 void SoftwareIsp::inputReady(FrameBuffer *input)
