diff --git a/src/libcamera/software_isp/gpu_pipeline_shader_pass_blc_normalise.cpp b/src/libcamera/software_isp/gpu_pipeline_shader_pass_blc_normalise.cpp
index 8e8d7b9d6..c07319ee0 100644
--- a/src/libcamera/software_isp/gpu_pipeline_shader_pass_blc_normalise.cpp
+++ b/src/libcamera/software_isp/gpu_pipeline_shader_pass_blc_normalise.cpp
@@ -102,6 +102,8 @@ int GpuIspShaderPassBlcNormalise::selectShaders(struct ShaderConfig &shaderCfg,
 		break;
 	};
 
+	LOG(GpuShaderBlc, Info) << "ShaderPass = " << typeid(this).name() << " selected shader pair ";
+
 	return 0;
 }
 
@@ -113,15 +115,14 @@ int GpuIspShaderPassBlcNormalise::getShaderVariableLocations(void)
 	textureUniformStep_ = glGetUniformLocation(programId_, "tex_step");
 	textureUniformSize_ = glGetUniformLocation(programId_, "tex_size");
 	textureUniformStrideFactor_ = glGetUniformLocation(programId_, "stride_factor");
-	textureUniformBayerFirstRed_ = glGetUniformLocation(programId_, "tex_bayer_first_red");
 	textureUniformProjMatrix_ = glGetUniformLocation(programId_, "proj_matrix");
+	blackLevelUniformDataIn_ = glGetUniformLocation(programId_, "blacklevel");
 
 	LOG(GpuShaderBlc, Debug) << "vertexIn " << attributeVertex_ << " textureIn " << attributeTexture_
 			    << " tex_y " << textureUniformBayerDataIn_
 			    << " tex_step " << textureUniformStep_
 			    << " tex_size " << textureUniformSize_
 			    << " stride_factor " << textureUniformStrideFactor_
-			    << " tex_bayer_first_red " << textureUniformBayerFirstRed_
 			    << " proj_matrix " << textureUniformProjMatrix_;
 
 	/* TODO: trap errors */
@@ -130,6 +131,8 @@ int GpuIspShaderPassBlcNormalise::getShaderVariableLocations(void)
 
 void GpuIspShaderPassBlcNormalise::setShaderVariableValues(const DebayerParams &params, eGLImage &eglImageIn)
 {
+	GLenum err;
+
 	/*
 	 * Raw Bayer 8-bit, and packed raw Bayer 10-bit/12-bit formats
 	 * are stored in a GL_LUMINANCE texture. The texture width is
@@ -168,15 +171,34 @@ void GpuIspShaderPassBlcNormalise::setShaderVariableValues(const DebayerParams &
 		{ 1.0f, 0.0f },
 	};
 
+	err = glGetError();
+	if (err != GL_NO_ERROR)
+		LOG(GpuShaderBlc, Error) << "setShaderVariables entry @ error " << err;
+
 	/* vertexIn - bayer_8.vert */
 	glEnableVertexAttribArray(attributeVertex_);
+
+	err = glGetError();
+	if (err != GL_NO_ERROR)
+		LOG(GpuShaderBlc, Error) << "setShaderVariables error " << err << " line " << __LINE__;
+
 	glVertexAttribPointer(attributeVertex_, 2, GL_FLOAT, GL_TRUE,
 			      2 * sizeof(GLfloat), vcoordinates);
+	err = glGetError();
+	if (err != GL_NO_ERROR)
+		LOG(GpuShaderBlc, Error) << "setShaderVariables error " << err << " line " << __LINE__;
 
 	/* textureIn - bayer_8.vert */
 	glEnableVertexAttribArray(attributeTexture_);
+	err = glGetError();
+	if (err != GL_NO_ERROR)
+		LOG(GpuShaderBlc, Error) << "setShaderVariables error " << err << " line " << __LINE__;
+
 	glVertexAttribPointer(attributeTexture_, 2, GL_FLOAT, GL_TRUE,
 			      2 * sizeof(GLfloat), tcoordinates);
+	err = glGetError();
+	if (err != GL_NO_ERROR)
+		LOG(GpuShaderBlc, Error) << "setShaderVariables error " << err << " line " << __LINE__;
 
 	/*
 	 * Set the sampler2D to the respective texture unit for each texutre
@@ -184,27 +206,43 @@ void GpuIspShaderPassBlcNormalise::setShaderVariableValues(const DebayerParams &
 	 * texture units
 	 */
 	glUniform1i(textureUniformBayerDataIn_, eglImageIn.texture_unit_uniform_id_);
+	err = glGetError();
+	if (err != GL_NO_ERROR)
+		LOG(GpuShaderBlc, Error) << "setShaderVariables error " << err << " line " << __LINE__;
 
 	/*
 	 * These values are:
-	 * firstRed = tex_bayer_first_red - bayer_8.vert
 	 * imgSize = tex_size - bayer_8.vert
 	 * step = tex_step - bayer_8.vert
 	 * Stride = stride_factor identity.vert
 	 * textureUniformProjMatri = No scaling
 	 */
-	glUniform2fv(textureUniformBayerFirstRed_, 1, firstRed);
 	glUniform2fv(textureUniformSize_, 1, imgSize);
+	err = glGetError();
+	if (err != GL_NO_ERROR)
+		LOG(GpuShaderBlc, Error) << "setShaderVariables error " << err << " line " << __LINE__;
+
 	glUniform2fv(textureUniformStep_, 1, Step);
+	err = glGetError();
+	if (err != GL_NO_ERROR)
+		LOG(GpuShaderBlc, Error) << "setShaderVariables error " << err << " line " << __LINE__;
+
 	glUniform1f(textureUniformStrideFactor_, Stride);
+	err = glGetError();
+	if (err != GL_NO_ERROR)
+		LOG(GpuShaderBlc, Error) << "setShaderVariables error " << err << " line " << __LINE__;
+
 	glUniformMatrix4fv(textureUniformProjMatrix_, 1, GL_FALSE, projMatrix);
+	err = glGetError();
+	if (err != GL_NO_ERROR)
+		LOG(GpuShaderBlc, Error) << "setShaderVariables error " << err << " line " << __LINE__;
+
 
 	LOG(GpuShaderBlc, Debug) << "vertexIn " << attributeVertex_ << " textureIn " << attributeTexture_
 			    << " tex_y " << textureUniformBayerDataIn_
 			    << " tex_step " << textureUniformStep_
 			    << " tex_size " << textureUniformSize_
-			    << " stride_factor " << textureUniformStrideFactor_
-			    << " tex_bayer_first_red " << textureUniformBayerFirstRed_;
+			    << " stride_factor " << textureUniformStrideFactor_;
 
 	LOG(GpuShaderBlc, Debug) << "textureUniformY_ = 0 "
 			    << " firstRed.x " << firstRed[0]
@@ -220,6 +258,10 @@ void GpuIspShaderPassBlcNormalise::setShaderVariableValues(const DebayerParams &
 	 * 0 = Red, 1 = Green, 2 = Blue
 	 */
 	glUniform3f(blackLevelUniformDataIn_, params.blackLevel[0], params.blackLevel[1], params.blackLevel[2]);
+	err = glGetError();
+	if (err != GL_NO_ERROR)
+		LOG(GpuShaderBlc, Error) << "setShaderVariables error " << err << " line " << __LINE__;
+
 	LOG(GpuShaderBlc, Debug) << " blackLevelUniformDataIn_ " << blackLevelUniformDataIn_ << " data " << params.blackLevel;
 
 	return;
diff --git a/src/libcamera/software_isp/gpu_pipeline_shader_pass_demosiac.cpp b/src/libcamera/software_isp/gpu_pipeline_shader_pass_demosiac.cpp
index b0c431c13..a1ca1916d 100644
--- a/src/libcamera/software_isp/gpu_pipeline_shader_pass_demosiac.cpp
+++ b/src/libcamera/software_isp/gpu_pipeline_shader_pass_demosiac.cpp
@@ -47,20 +47,20 @@ void GpuIspShaderPassDemosiac::stop()
 int GpuIspShaderPassDemosiac::selectShaders(struct ShaderConfig &shaderCfg, [[maybe_unused]]PixelFormat &inputFormat, [[maybe_unused]]PixelFormat &outputFormat)
 {
 	/* Pixel location parameters */
-	glFormat_ = GL_LUMINANCE;
+	glFormat_ = GL_R16F;
 	bytesPerPixel_ = 1;
 	shaderStridePixels_ = passInputCfg_.stride;
 
-	/* Shader selection */
+	shaderCfg.vertexShaderData = bayer_unpacked_vert;
+	shaderCfg.vertexShaderDataLen = bayer_unpacked_vert_len;
+	shaderCfg.fragmentShaderData = bayer_glr16_to_rgba_frag;
+	shaderCfg.fragmentShaderDataLen = bayer_glr16_to_rgba_frag_len;
+
 	switch (inputFormat) {
 	case libcamera::formats::SBGGR8:
 	case libcamera::formats::SGBRG8:
 	case libcamera::formats::SGRBG8:
 	case libcamera::formats::SRGGB8:
-		shaderCfg.fragmentShaderData = bayer_unpacked_frag;
-		shaderCfg.fragmentShaderDataLen = bayer_unpacked_frag_len;
-		shaderCfg.vertexShaderData = bayer_unpacked_vert;
-		shaderCfg.vertexShaderDataLen = bayer_unpacked_vert_len;
 		break;
 	case libcamera::formats::SBGGR10_CSI2P:
 	case libcamera::formats::SGBRG10_CSI2P:
@@ -68,17 +68,9 @@ int GpuIspShaderPassDemosiac::selectShaders(struct ShaderConfig &shaderCfg, [[ma
 	case libcamera::formats::SRGGB10_CSI2P:
 		egl_.pushEnv(shaderCfg.shaderEnv, "#define RAW10P");
 		if (BayerFormat::fromPixelFormat(inputFormat).packing == BayerFormat::Packing::None) {
-			shaderCfg.fragmentShaderData = bayer_unpacked_frag;
-			shaderCfg.fragmentShaderDataLen = bayer_unpacked_frag_len;
-			shaderCfg.vertexShaderData = bayer_unpacked_vert;
-			shaderCfg.vertexShaderDataLen = bayer_unpacked_vert_len;
 			glFormat_ = GL_RG;
 			bytesPerPixel_ = 2;
 		} else {
-			shaderCfg.fragmentShaderData = bayer_1x_packed_frag;
-			shaderCfg.fragmentShaderDataLen = bayer_1x_packed_frag_len;
-			shaderCfg.vertexShaderData = identity_vert;
-			shaderCfg.vertexShaderDataLen = identity_vert_len;
 			shaderStridePixels_ = passInputCfg_.size.width;
 		}
 		break;
@@ -88,22 +80,16 @@ int GpuIspShaderPassDemosiac::selectShaders(struct ShaderConfig &shaderCfg, [[ma
 	case libcamera::formats::SRGGB12_CSI2P:
 		egl_.pushEnv(shaderCfg.shaderEnv, "#define RAW12P");
 		if (BayerFormat::fromPixelFormat(inputFormat).packing == BayerFormat::Packing::None) {
-			shaderCfg.fragmentShaderData = bayer_unpacked_frag;
-			shaderCfg.fragmentShaderDataLen = bayer_unpacked_frag_len;
-			shaderCfg.vertexShaderData = bayer_unpacked_vert;
-			shaderCfg.vertexShaderDataLen = bayer_unpacked_vert_len;
 			glFormat_ = GL_RG;
 			bytesPerPixel_ = 2;
 		} else {
-			shaderCfg.fragmentShaderData = bayer_1x_packed_frag;
-			shaderCfg.fragmentShaderDataLen = bayer_1x_packed_frag_len;
-			shaderCfg.vertexShaderData = identity_vert;
-			shaderCfg.vertexShaderDataLen = identity_vert_len;
 			shaderStridePixels_ = passInputCfg_.size.width;
 		}
 		break;
 	};
 
+	LOG(GpuShaderDemosiac, Info) << "ShaderPass = " << typeid(this).name() << " selected shader pair ";
+
 	return 0;
 }
 
@@ -112,26 +98,21 @@ int GpuIspShaderPassDemosiac::getShaderVariableLocations(void)
 	attributeVertex_ = glGetAttribLocation(programId_, "vertexIn");
 	attributeTexture_ = glGetAttribLocation(programId_, "textureIn");
 
-	textureUniformBayerDataIn_ = glGetUniformLocation(programId_, "tex_y");
 	ccmUniformDataIn_ = glGetUniformLocation(programId_, "ccm");
-	blackLevelUniformDataIn_ = glGetUniformLocation(programId_, "blacklevel");
 	gammaUniformDataIn_ = glGetUniformLocation(programId_, "gamma");
 	contrastExpUniformDataIn_ = glGetUniformLocation(programId_, "contrastExp");
 
-	textureUniformStep_ = glGetUniformLocation(programId_, "tex_step");
-	textureUniformSize_ = glGetUniformLocation(programId_, "tex_size");
 	textureUniformStrideFactor_ = glGetUniformLocation(programId_, "stride_factor");
 	textureUniformBayerFirstRed_ = glGetUniformLocation(programId_, "tex_bayer_first_red");
 	textureUniformProjMatrix_ = glGetUniformLocation(programId_, "proj_matrix");
+	textureUniformBayerDataIn_ = glGetUniformLocation(programId_, "tex_y");
+	textureUniformStep_ = glGetUniformLocation(programId_, "tex_step");
+	textureUniformSize_ = glGetUniformLocation(programId_, "tex_size");
 
 	LOG(GpuShaderDemosiac, Debug) << "vertexIn " << attributeVertex_ << " textureIn " << attributeTexture_
-			    << " tex_y " << textureUniformBayerDataIn_
 			    << " ccm " << ccmUniformDataIn_
-			    << " blacklevel " << blackLevelUniformDataIn_
 			    << " gamma " << gammaUniformDataIn_
 			    << " contrastExp " << contrastExpUniformDataIn_
-			    << " tex_step " << textureUniformStep_
-			    << " tex_size " << textureUniformSize_
 			    << " stride_factor " << textureUniformStrideFactor_
 			    << " tex_bayer_first_red " << textureUniformBayerFirstRed_
 			    << " proj_matrix " << textureUniformProjMatrix_;
@@ -213,8 +194,6 @@ void GpuIspShaderPassDemosiac::setShaderVariableValues(const DebayerParams &para
 
 	LOG(GpuShaderDemosiac, Debug) << "vertexIn " << attributeVertex_ << " textureIn " << attributeTexture_
 			    << " tex_y " << textureUniformBayerDataIn_
-			    << " tex_step " << textureUniformStep_
-			    << " tex_size " << textureUniformSize_
 			    << " stride_factor " << textureUniformStrideFactor_
 			    << " tex_bayer_first_red " << textureUniformBayerFirstRed_;
 
@@ -242,12 +221,6 @@ void GpuIspShaderPassDemosiac::setShaderVariableValues(const DebayerParams &para
 	glUniformMatrix3fv(ccmUniformDataIn_, 1, GL_FALSE, ccm);
 	LOG(GpuShaderDemosiac, Debug) << " ccmUniformDataIn_ " << ccmUniformDataIn_ << " data " << params.combinedMatrix;
 
-	/*
-	 * 0 = Red, 1 = Green, 2 = Blue
-	 */
-	glUniform3f(blackLevelUniformDataIn_, params.blackLevel[0], params.blackLevel[1], params.blackLevel[2]);
-	LOG(GpuShaderDemosiac, Debug) << " blackLevelUniformDataIn_ " << blackLevelUniformDataIn_ << " data " << params.blackLevel;
-
 	/*
 	 * Gamma
 	 */
diff --git a/src/libcamera/software_isp/gpu_pipeline_shader_pass_demosiac.h b/src/libcamera/software_isp/gpu_pipeline_shader_pass_demosiac.h
index c83024bc4..2302b2ea0 100644
--- a/src/libcamera/software_isp/gpu_pipeline_shader_pass_demosiac.h
+++ b/src/libcamera/software_isp/gpu_pipeline_shader_pass_demosiac.h
@@ -50,9 +50,6 @@ private:
 	/* Represent per-frame CCM as a uniform vector of floats 3 x 3 */
 	GLint ccmUniformDataIn_;
 
-	/* Black Level compensation */
-	GLint blackLevelUniformDataIn_;
-
 	/* Gamma */
 	GLint gammaUniformDataIn_;
 
diff --git a/src/libcamera/software_isp/software_isp_pipeline_gpu.cpp b/src/libcamera/software_isp/software_isp_pipeline_gpu.cpp
index 3c9b48154..14f6d2b8c 100644
--- a/src/libcamera/software_isp/software_isp_pipeline_gpu.cpp
+++ b/src/libcamera/software_isp/software_isp_pipeline_gpu.cpp
@@ -186,9 +186,12 @@ int SoftwareIspPipelineGpu::configure(const StreamConfiguration &inputCfg,
 
 	/* Configure for one pass */
 	PassConfig rawSensorIn = { inputCfg.size, inputConfig_.stride, inputPixelFormat_, window };
+	PassConfig pingPongOut = { inputCfg.size, inputCfg.size.width, inputPixelFormat_, Rectangle(inputCfg.size) };
+	PassConfig pingPongIn = pingPongOut;
 	PassConfig rgbaOut = { outputCfg.size, outputConfig_.stride, outputPixelFormat_, Rectangle(outputSize_) };
 
-	gpuIspShaderPassDemosiac_.configure(rawSensorIn, rgbaOut);
+	gpuIspShaderPassBlcNormalise_.configure(rawSensorIn, pingPongOut);
+	gpuIspShaderPassDemosiac_.configure(pingPongIn, rgbaOut);
 
 	return 0;
 }
@@ -257,7 +260,17 @@ int SoftwareIspPipelineGpu::processGPU(FrameBuffer *input, FrameBuffer *output,
 	/* Generate the output render framebuffer as render to texture */
 	egl_.createOutputDMABufTexture2D(*eglImageRGBAOut_, output->planes()[0].fd.get());
 
-	pipelineResult = gpuIspShaderPassDemosiac_.process(*eglImageBayerIn_, *eglImageRGBAOut_, width_, height_, params);
+	pipelineResult = gpuIspShaderPassBlcNormalise_.process(*eglImageBayerIn_, *eglImagePingPong_[0], width_, height_, params);
+	if (pipelineResult) {
+		LOG(Debayer, Error) << "BlcNormalise fail";
+		return pipelineResult;
+	}
+
+	pipelineResult = gpuIspShaderPassDemosiac_.process(*eglImagePingPong_[0], *eglImageRGBAOut_, width_, height_, params);
+	if (pipelineResult) {
+		LOG(Debayer, Error) << "Demosiac fail";
+		return pipelineResult;
+	}
 
 	GLenum err = glGetError();
 	if (err != GL_NO_ERROR) {
@@ -337,15 +350,18 @@ int SoftwareIspPipelineGpu::start()
 		return -EINVAL;
 
 	/* Raw bayer input as texture */
-	eglImageBayerIn_ = std::make_unique<eGLImage>(gpuIspShaderPassDemosiac_.glFormat_, inputConfig_.stride / gpuIspShaderPassDemosiac_.getBytesPerPixel(), height_, inputConfig_.stride, GL_TEXTURE0, 0);
+	eglImageBayerIn_ = std::make_unique<eGLImage>(gpuIspShaderPassBlcNormalise_.glFormat_, inputConfig_.stride / gpuIspShaderPassBlcNormalise_.getBytesPerPixel(), height_, inputConfig_.stride, GL_TEXTURE0, 0);
 
 	/* Intermediary ping/pong textures */
-	eglImagePingPong_[0] = std::make_unique<eGLImage>(gpuIspShaderPassDemosiac_.glFormat_, outputSize_.width, outputSize_.height, outputConfig_.stride, GL_TEXTURE1, 1);
-	eglImagePingPong_[1] = std::make_unique<eGLImage>(gpuIspShaderPassDemosiac_.glFormat_, outputSize_.width, outputSize_.height, outputConfig_.stride, GL_TEXTURE2, 2);
+	eglImagePingPong_[0] = std::make_unique<eGLImage>(gpuIspShaderPassDemosiac_.glFormat_, width_, height_, outputConfig_.stride, GL_TEXTURE1, 1);
+	eglImagePingPong_[1] = std::make_unique<eGLImage>(gpuIspShaderPassDemosiac_.glFormat_, width_, height_, outputConfig_.stride, GL_TEXTURE2, 2);
 
 	/* Texture we will render to */
 	eglImageRGBAOut_ = std::make_unique<eGLImage>(GL_RGBA, outputSize_.width, outputSize_.height, outputConfig_.stride, GL_TEXTURE3, 3);
 
+	egl_.createOutputTexture2D(*eglImagePingPong_[0]);
+	egl_.createOutputTexture2D(*eglImagePingPong_[1]);
+
 	return 0;
 }
 
