diff --git a/src/libcamera/software_isp/debayer_egl.cpp b/src/libcamera/software_isp/debayer_egl.cpp
index cd9ad888..afe78384 100644
--- a/src/libcamera/software_isp/debayer_egl.cpp
+++ b/src/libcamera/software_isp/debayer_egl.cpp
@@ -573,8 +573,29 @@ int DebayerEGL::debayerGPU(FrameBuffer *input, FrameBuffer *output, const Debaye
 	if (!eglImageOut)
 		return -ENOMEM;
 
-	egl_.attachTextureToFBO(*eglImageOut);
-	setShaderVariableValues(*eglImageIn, params);
+	return render(*eglImageIn, *eglImageOut, params);
+}
+
+/**
+ * \brief Render the configured debayer operation into existing textures
+ * \param[in] input Texture containing the configured Bayer input
+ * \param[in,out] output Texture receiving the configured output image
+ * \param[in] params Image processing parameters
+ *
+ * The caller owns the texture storage and must start the debayer before
+ * rendering. Textures must belong to the debayer's current EGL context.
+ *
+ * \return 0 on success or a negative error code otherwise
+ */
+int DebayerEGL::render(eGLImage &input, eGLImage &output, const DebayerParams &params)
+{
+	egl_.assertThread();
+
+	int ret = egl_.attachTextureToFBO(output);
+	if (ret)
+		return ret;
+
+	setShaderVariableValues(input, params);
 
 	glViewport(0, 0, width_, height_);
 	glClear(GL_COLOR_BUFFER_BIT);
diff --git a/src/libcamera/software_isp/debayer_egl.h b/src/libcamera/software_isp/debayer_egl.h
index 5a4cd2d8..d18dc3fb 100644
--- a/src/libcamera/software_isp/debayer_egl.h
+++ b/src/libcamera/software_isp/debayer_egl.h
@@ -62,6 +62,9 @@ public:
 	SizeRange sizes(PixelFormat inputFormat, const Size &inputSize) override;
 	Rectangle inputCrop() const override { return inputCrop_; }
 
+protected:
+	int render(eGLImage &input, eGLImage &output, const DebayerParams &params);
+
 private:
 	static int getInputConfig(PixelFormat inputFormat, DebayerInputConfig &config);
 	int initBayerShaders(PixelFormat inputFormat, PixelFormat outputFormat);
