| Message ID | 20261017111036.700859-2-alain.cousinie@laposte.net |
|---|---|
| State | Superseded |
| Headers | show |
| Series |
|
| Related | show |
On 17/10/2026 12:10, alain.cousinie@laposte.net wrote: > Signed-off-by: Alain Cousinié <alain.cousinie@laposte.net> > --- > src/libcamera/shaders/meson.build | 3 + > src/libcamera/shaders/mono.vert | 25 ++++++++ > src/libcamera/shaders/mono_1x_packed.frag | 69 +++++++++++++++++++++++ > src/libcamera/shaders/mono_unpacked.frag | 51 +++++++++++++++++ > 4 files changed, 148 insertions(+) > create mode 100644 src/libcamera/shaders/mono.vert > create mode 100644 src/libcamera/shaders/mono_1x_packed.frag > create mode 100644 src/libcamera/shaders/mono_unpacked.frag > > diff --git a/src/libcamera/shaders/meson.build b/src/libcamera/shaders/meson.build > index c409ff9b0..80b127d3d 100644 > --- a/src/libcamera/shaders/meson.build > +++ b/src/libcamera/shaders/meson.build > @@ -7,6 +7,9 @@ shader_files = files([ > 'bayer_unpacked.frag', > 'bayer_unpacked.vert', > 'identity.vert', > + 'mono_1x_packed.frag', > + 'mono_unpacked.frag', > + 'mono.vert', > ]) > > # Generate header from shaders > diff --git a/src/libcamera/shaders/mono.vert b/src/libcamera/shaders/mono.vert > new file mode 100644 > index 000000000..474ef462c > --- /dev/null > +++ b/src/libcamera/shaders/mono.vert > @@ -0,0 +1,25 @@ > +/* SPDX-License-Identifier: BSD-2-Clause */ > +/* > + * Copyright (C) 2026, Alain Cousinié > + * > + * Optimized vertex shader for MIPI CSI-2 packed monochrome formats > + * Pre-calculates exact pixel coordinates to reduce Fragment Shader overhead > + */ > + > +attribute vec4 vertexIn; > +attribute vec2 textureIn; > + > +varying vec2 textureOut; > +varying vec2 v_pixelCoords; > + > +uniform mat4 proj_matrix; > +uniform float stride_factor; > +uniform vec2 tex_size; > + > +void main(void) > +{ > + gl_Position = proj_matrix * vertexIn; > + > + textureOut = vec2(textureIn.x * stride_factor, textureIn.y); > + v_pixelCoords = textureOut * tex_size; > +} You could use identity.vert if you dropped the v_pixelCoords stuff here, I don't see why you couldn't calculate that in the follow-on fragment shader. I wonder if this new .vert is strictly necessary ? > diff --git a/src/libcamera/shaders/mono_1x_packed.frag b/src/libcamera/shaders/mono_1x_packed.frag > new file mode 100644 > index 000000000..d23c8be8d > --- /dev/null > +++ b/src/libcamera/shaders/mono_1x_packed.frag > @@ -0,0 +1,69 @@ > +/* SPDX-License-Identifier: BSD-2-Clause */ > +/* > + * Copyright (C) 2026, Alain Cousinié > + * Unified Fragment shader for MIPI CSI-2 packed monochrome formats (R10P / R12P) > + */ > + > +#ifdef GL_ES > +#ifdef GL_FRAGMENT_PRECISION_HIGH > +/* High precision prevents float rounding errors during bit-shifting math. */ > +precision highp float; > +#else > +precision mediump float; > +#endif > +#endif > + > +varying vec2 textureOut; > +varying vec2 v_pixelCoords; > + > +uniform sampler2D tex_y; > +uniform float gamma; > +uniform float contrastExp; > +uniform vec3 blacklevel; > + > +#define INV_TEX_WIDTH (1.0 / TEX_WIDTH) > +#define INV_TEX_HEIGHT (1.0 / TEX_HEIGHT) > + > +void main(void) > +{ > + float px = floor(v_pixelCoords.x); > + float py = floor(v_pixelCoords.y); > + > + /* Compute absolute macro-block start byte and local pixel index. */ > + float blockStart = floor(px / BLOCK_PIXELS) * BLOCK_BYTES; > + float idx = px - (blockStart * (BLOCK_PIXELS / BLOCK_BYTES)); > + > + /* Target the center of the dedicated MSB texel. */ > + float xMSB = (blockStart + idx + 0.5) * INV_TEX_WIDTH; > + > + /* Target the shared trailing byte containing the LSBs (1/8bits = 0.125) . */ > + float lsbOffset = BLOCK_PIXELS + floor(idx * LSB_SIZE * 0.125); > + float xLSB = (blockStart + lsbOffset + 0.5) * INV_TEX_WIDTH; > + float normY = (py + 0.5) * INV_TEX_HEIGHT; > + > + float byteHigh = floor(texture2D(tex_y, vec2(xMSB, normY)).r * 255.0 + 0.5); > + float byteLow = floor(texture2D(tex_y, vec2(xLSB, normY)).r * 255.0 + 0.5); > + > + /* Isolate the pixel's LSB bits inside the shared byte */ > + float totalBitsPos = idx * LSB_SIZE; > + float bitStartInByte = totalBitsPos - (floor(totalBitsPos * 0.125) * 8.0); > + > + float divisionFactor = pow(2.0, bitStartInByte); > + float shiftedLsb = floor(byteLow / divisionFactor); > + float lsbBits = shiftedLsb - (floor(shiftedLsb / LSB_MASK_VAL) * LSB_MASK_VAL); > + > + /* Reconstruct full native intensity value. */ > + float rawValue = (byteHigh * LSB_MASK_VAL) + lsbBits; > + > + float normalizedIntensity = rawValue * INV_MAX_VAL; > + normalizedIntensity = clamp(normalizedIntensity - blacklevel.g, 0.0, 1.0); > + > + /* Apply S-Curve contrast stretching. */ > + float centerDist = 1.0 - abs(2 * normalizedIntensity - 1.0); > + float singlePow = 0.5 * pow(centerDist, contrastExp); > + normalizedIntensity = mix(singlePow, 1.0 - singlePow, step(0.5, normalizedIntensity)); > + > + /* Apply gamma correction and output. */ > + vec3 rgb = pow(vec3(normalizedIntensity), vec3(gamma)); > + gl_FragColor = vec4(rgb, 1.0); > +} > diff --git a/src/libcamera/shaders/mono_unpacked.frag b/src/libcamera/shaders/mono_unpacked.frag > new file mode 100644 > index 000000000..9ea9a7b79 > --- /dev/null > +++ b/src/libcamera/shaders/mono_unpacked.frag > @@ -0,0 +1,51 @@ > +/* SPDX-License-Identifier: BSD-2-Clause */ > +/* > + * Copyright (C) 2026, Alain Cousinié > + * Unified Fragment shader for unpacked monochrome formats (R8, R10, R12, R16) > + */ > + > +#ifdef GL_ES > +#ifdef GL_FRAGMENT_PRECISION_HIGH > +/* High precision prevents float rounding errors during bit-shifting math. */ > +precision highp float; > +#else > +precision mediump float; > +#endif > +#endif > + > +varying vec2 textureOut; > + > +uniform sampler2D tex_y; > +uniform float gamma; > +uniform float contrastExp; > +uniform vec3 blacklevel; > + > +void main(void) > +{ > + float rawValue = 0.0; > + > +#ifdef RAW8 > + /* 8-bit unpacked: single channel extraction. */ > + rawValue = floor(texture2D(tex_y, textureOut).r * 255.0 + 0.5); > +#else > + /* Multi-byte unpacked: merge Red (low byte) and Green (high byte). */ > + vec4 texel = texture2D(tex_y, textureOut); > + > + float byteLow = floor(texel.r * 255.0 + 0.5); > + float byteHigh = floor(texel.g * 255.0 + 0.5); > + > + rawValue = (byteHigh * 256.0) + byteLow; > +#endif > + > + float normalizedIntensity = rawValue * INV_MAX_VAL; > + normalizedIntensity = clamp(normalizedIntensity - blacklevel.g, 0.0, 1.0); > + > + /* S-Curve contrast stretching. */ > + float centerDist = 1.0 - abs(2.0 * normalizedIntensity - 1.0); > + float singlePow = 0.5 * pow(centerDist, contrastExp); > + normalizedIntensity = mix(singlePow, 1.0 - singlePow, step(0.5, normalizedIntensity)); > + > + /* Gamma correction and output. */ > + vec3 rgb = pow(vec3(normalizedIntensity), vec3(gamma)); > + gl_FragColor = vec4(rgb, 1.0); > +} > -- > 2.55.0
diff --git a/src/libcamera/shaders/meson.build b/src/libcamera/shaders/meson.build index c409ff9b0..80b127d3d 100644 --- a/src/libcamera/shaders/meson.build +++ b/src/libcamera/shaders/meson.build @@ -7,6 +7,9 @@ shader_files = files([ 'bayer_unpacked.frag', 'bayer_unpacked.vert', 'identity.vert', + 'mono_1x_packed.frag', + 'mono_unpacked.frag', + 'mono.vert', ]) # Generate header from shaders diff --git a/src/libcamera/shaders/mono.vert b/src/libcamera/shaders/mono.vert new file mode 100644 index 000000000..474ef462c --- /dev/null +++ b/src/libcamera/shaders/mono.vert @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2026, Alain Cousinié + * + * Optimized vertex shader for MIPI CSI-2 packed monochrome formats + * Pre-calculates exact pixel coordinates to reduce Fragment Shader overhead + */ + +attribute vec4 vertexIn; +attribute vec2 textureIn; + +varying vec2 textureOut; +varying vec2 v_pixelCoords; + +uniform mat4 proj_matrix; +uniform float stride_factor; +uniform vec2 tex_size; + +void main(void) +{ + gl_Position = proj_matrix * vertexIn; + + textureOut = vec2(textureIn.x * stride_factor, textureIn.y); + v_pixelCoords = textureOut * tex_size; +} diff --git a/src/libcamera/shaders/mono_1x_packed.frag b/src/libcamera/shaders/mono_1x_packed.frag new file mode 100644 index 000000000..d23c8be8d --- /dev/null +++ b/src/libcamera/shaders/mono_1x_packed.frag @@ -0,0 +1,69 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2026, Alain Cousinié + * Unified Fragment shader for MIPI CSI-2 packed monochrome formats (R10P / R12P) + */ + +#ifdef GL_ES +#ifdef GL_FRAGMENT_PRECISION_HIGH +/* High precision prevents float rounding errors during bit-shifting math. */ +precision highp float; +#else +precision mediump float; +#endif +#endif + +varying vec2 textureOut; +varying vec2 v_pixelCoords; + +uniform sampler2D tex_y; +uniform float gamma; +uniform float contrastExp; +uniform vec3 blacklevel; + +#define INV_TEX_WIDTH (1.0 / TEX_WIDTH) +#define INV_TEX_HEIGHT (1.0 / TEX_HEIGHT) + +void main(void) +{ + float px = floor(v_pixelCoords.x); + float py = floor(v_pixelCoords.y); + + /* Compute absolute macro-block start byte and local pixel index. */ + float blockStart = floor(px / BLOCK_PIXELS) * BLOCK_BYTES; + float idx = px - (blockStart * (BLOCK_PIXELS / BLOCK_BYTES)); + + /* Target the center of the dedicated MSB texel. */ + float xMSB = (blockStart + idx + 0.5) * INV_TEX_WIDTH; + + /* Target the shared trailing byte containing the LSBs (1/8bits = 0.125) . */ + float lsbOffset = BLOCK_PIXELS + floor(idx * LSB_SIZE * 0.125); + float xLSB = (blockStart + lsbOffset + 0.5) * INV_TEX_WIDTH; + float normY = (py + 0.5) * INV_TEX_HEIGHT; + + float byteHigh = floor(texture2D(tex_y, vec2(xMSB, normY)).r * 255.0 + 0.5); + float byteLow = floor(texture2D(tex_y, vec2(xLSB, normY)).r * 255.0 + 0.5); + + /* Isolate the pixel's LSB bits inside the shared byte */ + float totalBitsPos = idx * LSB_SIZE; + float bitStartInByte = totalBitsPos - (floor(totalBitsPos * 0.125) * 8.0); + + float divisionFactor = pow(2.0, bitStartInByte); + float shiftedLsb = floor(byteLow / divisionFactor); + float lsbBits = shiftedLsb - (floor(shiftedLsb / LSB_MASK_VAL) * LSB_MASK_VAL); + + /* Reconstruct full native intensity value. */ + float rawValue = (byteHigh * LSB_MASK_VAL) + lsbBits; + + float normalizedIntensity = rawValue * INV_MAX_VAL; + normalizedIntensity = clamp(normalizedIntensity - blacklevel.g, 0.0, 1.0); + + /* Apply S-Curve contrast stretching. */ + float centerDist = 1.0 - abs(2 * normalizedIntensity - 1.0); + float singlePow = 0.5 * pow(centerDist, contrastExp); + normalizedIntensity = mix(singlePow, 1.0 - singlePow, step(0.5, normalizedIntensity)); + + /* Apply gamma correction and output. */ + vec3 rgb = pow(vec3(normalizedIntensity), vec3(gamma)); + gl_FragColor = vec4(rgb, 1.0); +} diff --git a/src/libcamera/shaders/mono_unpacked.frag b/src/libcamera/shaders/mono_unpacked.frag new file mode 100644 index 000000000..9ea9a7b79 --- /dev/null +++ b/src/libcamera/shaders/mono_unpacked.frag @@ -0,0 +1,51 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2026, Alain Cousinié + * Unified Fragment shader for unpacked monochrome formats (R8, R10, R12, R16) + */ + +#ifdef GL_ES +#ifdef GL_FRAGMENT_PRECISION_HIGH +/* High precision prevents float rounding errors during bit-shifting math. */ +precision highp float; +#else +precision mediump float; +#endif +#endif + +varying vec2 textureOut; + +uniform sampler2D tex_y; +uniform float gamma; +uniform float contrastExp; +uniform vec3 blacklevel; + +void main(void) +{ + float rawValue = 0.0; + +#ifdef RAW8 + /* 8-bit unpacked: single channel extraction. */ + rawValue = floor(texture2D(tex_y, textureOut).r * 255.0 + 0.5); +#else + /* Multi-byte unpacked: merge Red (low byte) and Green (high byte). */ + vec4 texel = texture2D(tex_y, textureOut); + + float byteLow = floor(texel.r * 255.0 + 0.5); + float byteHigh = floor(texel.g * 255.0 + 0.5); + + rawValue = (byteHigh * 256.0) + byteLow; +#endif + + float normalizedIntensity = rawValue * INV_MAX_VAL; + normalizedIntensity = clamp(normalizedIntensity - blacklevel.g, 0.0, 1.0); + + /* S-Curve contrast stretching. */ + float centerDist = 1.0 - abs(2.0 * normalizedIntensity - 1.0); + float singlePow = 0.5 * pow(centerDist, contrastExp); + normalizedIntensity = mix(singlePow, 1.0 - singlePow, step(0.5, normalizedIntensity)); + + /* Gamma correction and output. */ + vec3 rgb = pow(vec3(normalizedIntensity), vec3(gamma)); + gl_FragColor = vec4(rgb, 1.0); +}
Signed-off-by: Alain Cousinié <alain.cousinie@laposte.net> --- src/libcamera/shaders/meson.build | 3 + src/libcamera/shaders/mono.vert | 25 ++++++++ src/libcamera/shaders/mono_1x_packed.frag | 69 +++++++++++++++++++++++ src/libcamera/shaders/mono_unpacked.frag | 51 +++++++++++++++++ 4 files changed, 148 insertions(+) create mode 100644 src/libcamera/shaders/mono.vert create mode 100644 src/libcamera/shaders/mono_1x_packed.frag create mode 100644 src/libcamera/shaders/mono_unpacked.frag -- 2.55.0