From patchwork Thu Jun 18 12:22:24 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Bryan O'Donoghue X-Patchwork-Id: 26944 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 284FFC3307 for ; Thu, 18 Jun 2026 12:23:22 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 1CF1A656C5; Thu, 18 Jun 2026 14:23:21 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=linaro.org header.i=@linaro.org header.b="ivn+yGV0"; dkim-atps=neutral Received: from mail-wm1-x331.google.com (mail-wm1-x331.google.com [IPv6:2a00:1450:4864:20::331]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 25674629CE for ; Thu, 18 Jun 2026 14:23:06 +0200 (CEST) Received: by mail-wm1-x331.google.com with SMTP id 5b1f17b1804b1-490a76757e5so5337745e9.2 for ; Thu, 18 Jun 2026 05:23:06 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1781785386; x=1782390186; darn=lists.libcamera.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=PNdjgk6oCBe7hqWUf41J1Jprz/dS4FsZEgZzAJ6B81A=; b=ivn+yGV0KSWZcRqF14yNXpc5vNc4KeIvDRosSvBn/xT+E1f9duzpLgOobgxBRzqdZE qIT5/ShDkAR877HcsiOZOEUBDHh4XmHAQFOhAENiEXGQo+3195rG5HfAxiMjHfRx2SEK ogkHqK0aKX1d2DWk+YaV0laXXvnKWuLysBJIxCG2cASCotpvkK4+7bK/GD+52ZPMg7kQ NuyETyZdcih0YwxSUxD16iJWsrVxoeC0auFsVCCnmqw2LodwLlny6oQtlKe6rcvZkn2i m+oHo4QbaKtEVEVyUak2xzraPgy3cpuIQHIKAIVQX+Ap6Xv2zh9Lh8MkkXWzYzik+Dle OEfA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1781785386; x=1782390186; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=PNdjgk6oCBe7hqWUf41J1Jprz/dS4FsZEgZzAJ6B81A=; b=DBevMqwBT7wZbySeRjIpkV18MMcZm12N/Zb2IVAkzcm2LrLoDr1IrrevuxyqhAx6pM uebNJaikUKGRfwXbKyr5AlpgdwaGchvMMKVwNzO9LW4EYfXygqegXb1gzfBwKaFf2lqR kZw9qMtEjuFZzMxgQcHljScxvi8aqA2R7QzUQ8jeUS7LpW767FtTFbIsTgFzumNJJf/Q +7EV6vtPqiQn9kIeKJDFVcAiFQfSOFm6Hjvwu0hNjsHNMFT1GZjNhR+vqA6RuoKNSA6o K+fRKSYQsOP2deN0mzBiu4G+4pEFGLS3Icj9IfPTAoxWCMq3/gjLjTgPbiCT/rXtt9gf p8dA== X-Gm-Message-State: AOJu0YwDr/Q8i1cTkyf2wepx+GKAGdxz/G9cCVoqhjDFhSha/HPV9D1h CaDf9tFM5C5gAL9mpkmXPYNlXJZTkmgM9wkWOsiSDr4pygU/XVNc0bNO86C1vv+MsX0ZReV6ZDQ VPQJplsg= X-Gm-Gg: AfdE7clrITOQ27CsZRffvI8bFF7VK6TJJ6sp6i1AZPs5oQalna5OcZeQoupSUzYA1mW ednsSu6E+1JNoOPZALD29o+7DyhIYsWVrB2ysbPm7wyDBGr8aZn9xxqzb/l5yjNP1UZJCJnpB8r wGBzGsntY21j8WUg5in7l01tchegLLUdAYraKL0hdtlsTPWNsd13COQB4BbbQHGUYT49WRsA46f pzx9tw0b9HK6/FyKPHJk4K8s6uk8Zayyw+VKP/icUlEW6A3jMgp2kcM+Lf1RgPqRwYIKFA3uyyn gl6xL3YUL3QJMVOXxe5t6vzA5LoZdsQO2mk7XjyVAmAq2tgkzXJUC8PUAOQTplmLQ7f8u6gzaIT v5em5tL+Oze4J/aoaJ3wBvJKUOU9ybZg8vJALBgpX9FOm44NwkvjKAarN2rb9uYy0/7ExfqeFQm +8sG3YrIlU1A7/biPmYOUku5aAv+PQBoAqgYXiqJc= X-Received: by 2002:a05:600c:83cf:b0:492:2e1c:1d19 with SMTP id 5b1f17b1804b1-49238225f8bmr54202185e9.31.1781785385527; Thu, 18 Jun 2026 05:23:05 -0700 (PDT) Received: from inspiron14p-linux ([109.76.144.236]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-4922fa3a4easm275198015e9.3.2026.06.18.05.23.04 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 18 Jun 2026 05:23:05 -0700 (PDT) From: Bryan O'Donoghue To: libcamera-devel@lists.libcamera.org Cc: bryan.odonoghue@linaro.org, pavel@ucw.cz Subject: [PATCH 11/30] libcamera: software_isp: gpu_pipeline_shader_pass: Move common shader selection logic into base class in new method initShaders() Date: Thu, 18 Jun 2026 13:22:24 +0100 Message-ID: <20260618122245.946138-12-bryan.odonoghue@linaro.org> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260618122245.946138-1-bryan.odonoghue@linaro.org> References: <20260618122245.946138-1-bryan.odonoghue@linaro.org> MIME-Version: 1.0 X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Shader initialisation has some common and some pass-specific logic that needs to happen. - Common: Environment setup such as defining top-level defines as flags to include features when compiling shaders. This will have to be consistent from one shader feeding the next. - Shader specific: Selecting which vertex, fragment and potentially compute shaders to run - populating the shader compile environment with any defines specific to that shader. - Common: Compiling and linking a shader program. Error checking and associating a compiled vertex/fragment or compute shader with a given programId_. Move the common code to the base class and have the base class call out to a pure virtual funtion that the derived class must implement. In this way GpuIspShaderPass::initShaders() calls the derived class GpuIspShaderPassDerivedClass::selectShader() while encapsulating that call in the afore mentioned common pieces of logic. Signed-off-by: Bryan O'Donoghue --- .../software_isp/gpu_pipeline_shader_pass.cpp | 118 ++++++++++++++- .../software_isp/gpu_pipeline_shader_pass.h | 11 +- .../gpu_pipeline_shader_pass_demosiac.cpp | 139 +++--------------- .../gpu_pipeline_shader_pass_demosiac.h | 3 +- 4 files changed, 151 insertions(+), 120 deletions(-) diff --git a/src/libcamera/software_isp/gpu_pipeline_shader_pass.cpp b/src/libcamera/software_isp/gpu_pipeline_shader_pass.cpp index 669a1c1b6..d0d13eef9 100644 --- a/src/libcamera/software_isp/gpu_pipeline_shader_pass.cpp +++ b/src/libcamera/software_isp/gpu_pipeline_shader_pass.cpp @@ -36,16 +36,29 @@ LOG_DEFINE_CATEGORY(GpuShaderPass) int GpuIspShaderPass::process(eGLImage &eglImageIn, eGLImage &eglImageOut, uint32_t width, uint32_t height, const DebayerParams ¶ms) { - /* Switch to the output framebuffer */ + GLenum err; + egl_.useProgram(programId_); + err = glGetError(); + if (err != GL_NO_ERROR) + LOG(GpuShaderPass, Error) << "Switch program @ error " << err; + + /* Switch to the output framebuffer */ egl_.attachTextureToFBO(eglImageOut); + err = glGetError(); + if (err != GL_NO_ERROR) + LOG(GpuShaderPass, Error) << "attachTextureToFBO @ error " << err; setShaderVariableValues(params, eglImageIn); + err = glGetError(); + if (err != GL_NO_ERROR) + LOG(GpuShaderPass, Error) << "setShaderVariables @ error " << err; + glViewport(0, 0, width, height); glClear(GL_COLOR_BUFFER_BIT); - glDrawArrays(GL_TRIANGLE_FAN, 0, DEBAYER_OPENGL_COORDS); - GLenum err = glGetError(); + glDrawArrays(GL_TRIANGLE_FAN, 0, DEBAYER_OPENGL_COORDS); + err = glGetError(); if (err != GL_NO_ERROR) { LOG(GpuShaderPass, Error) << "Drawing scene fail " << err; return -ENODEV; @@ -71,4 +84,103 @@ void GpuIspShaderPass::configure(const struct PassConfig &passInputCfg, const st passOutputCfg_ = passOutputCfg; } +int GpuIspShaderPass::initShaders(PixelFormat inputFormat, PixelFormat outputFormat) +{ + struct ShaderConfig shaderCfg; + GLenum err; + int ret; + + /* Target gles 100 glsl requires "#version x" as first directive in shader */ + egl_.pushEnv(shaderCfg.shaderEnv, "#version 100"); + + /* Specify GL_OES_EGL_image_external */ + egl_.pushEnv(shaderCfg.shaderEnv, "#extension GL_OES_EGL_image_external: enable"); + + /* The BLC and Demosiac shaders want to know this so do it in the base class */ + switch (inputFormat) { + case libcamera::formats::SBGGR8: + case libcamera::formats::SBGGR10_CSI2P: + case libcamera::formats::SBGGR12_CSI2P: + firstRed_x_ = 1.0; + firstRed_y_ = 1.0; + break; + case libcamera::formats::SGBRG8: + case libcamera::formats::SGBRG10_CSI2P: + case libcamera::formats::SGBRG12_CSI2P: + firstRed_x_ = 0.0; + firstRed_y_ = 1.0; + break; + case libcamera::formats::SGRBG8: + case libcamera::formats::SGRBG10_CSI2P: + case libcamera::formats::SGRBG12_CSI2P: + firstRed_x_ = 1.0; + firstRed_y_ = 0.0; + break; + case libcamera::formats::SRGGB8: + case libcamera::formats::SRGGB10_CSI2P: + case libcamera::formats::SRGGB12_CSI2P: + firstRed_x_ = 0.0; + firstRed_y_ = 0.0; + break; + default: + LOG(GpuShaderPass, Error) << "Unsupported input format"; + return -EINVAL; + }; + + /* + * Tell shaders how to re-order output taking account of how the pixels + * are actually stored by EGL. + */ + switch (outputFormat) { + case formats::ARGB8888: + case formats::XRGB8888: + break; + case formats::ABGR8888: + case formats::XBGR8888: + egl_.pushEnv(shaderCfg.shaderEnv, "#define SWAP_BLUE"); + break; + default: + LOG(GpuShaderPass, Error) << "Unsupported output format"; + return -EINVAL; + } + + ret = selectShaders(shaderCfg, inputFormat, outputFormat); + if (ret) { + LOG(GpuShaderPass, Error) << "selectShaders fail"; + return ret; + } + + LOG(GpuShaderPass, Info) << "ShaderPass = " << typeid(this).name(); + + if (egl_.compileVertexShader(vertexShaderId_, shaderCfg.vertexShaderData, shaderCfg.vertexShaderDataLen, shaderCfg.shaderEnv)) { + LOG(GpuShaderPass, Error) << "Compile vertex shader fail"; + return -ENODEV; + } + utils::scope_exit vShaderGuard([&] { glDeleteShader(vertexShaderId_); }); + + if (egl_.compileFragmentShader(fragmentShaderId_, shaderCfg.fragmentShaderData, shaderCfg.fragmentShaderDataLen, shaderCfg.shaderEnv)) { + LOG(GpuShaderPass, Error) << "Compile fragment shader fail"; + return -ENODEV; + } + utils::scope_exit fShaderGuard([&] { glDeleteShader(fragmentShaderId_); }); + + if (egl_.linkProgram(programId_, vertexShaderId_, fragmentShaderId_)) { + LOG(GpuShaderPass, Error) << "Linking program fail"; + return -ENODEV; + } + + egl_.dumpShaderSource(vertexShaderId_); + egl_.dumpShaderSource(fragmentShaderId_); + + /* Ensure we set the programId_ */ + egl_.useProgram(programId_); + err = glGetError(); + if (err != GL_NO_ERROR) { + LOG(GpuShaderPass, Error) << "Use program error " << err; + return -ENODEV; + } + + return getShaderVariableLocations(); +} + } diff --git a/src/libcamera/software_isp/gpu_pipeline_shader_pass.h b/src/libcamera/software_isp/gpu_pipeline_shader_pass.h index d55b27a85..a329845ee 100644 --- a/src/libcamera/software_isp/gpu_pipeline_shader_pass.h +++ b/src/libcamera/software_isp/gpu_pipeline_shader_pass.h @@ -41,6 +41,14 @@ struct PassConfig { Rectangle window; }; +struct ShaderConfig { + std::vector shaderEnv; + const unsigned char *vertexShaderData; + unsigned int vertexShaderDataLen; + const unsigned char *fragmentShaderData; + unsigned int fragmentShaderDataLen; +}; + class GpuIspShaderPass : public Object { public: @@ -51,7 +59,8 @@ public: virtual int start(); virtual void stop(); virtual void configure(const struct PassConfig &passInputCfg, const struct PassConfig &passOutputCfg); - virtual int initShaders(PixelFormat inputFormat, PixelFormat outputFormat) = 0; + virtual int initShaders(PixelFormat inputFormat, PixelFormat outputFormat); + virtual int selectShaders(struct ShaderConfig &shaderCfg, PixelFormat &inputFormat, PixelFormat &outputFormat) = 0; virtual int getShaderVariableLocations(void) = 0; virtual void setShaderVariableValues(const DebayerParams ¶ms, eGLImage &eglImageIn) = 0; virtual const char *name() const { return "GpuIspShaderPass"; } 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 02d9da2f0..b0c431c13 100644 --- a/src/libcamera/software_isp/gpu_pipeline_shader_pass_demosiac.cpp +++ b/src/libcamera/software_isp/gpu_pipeline_shader_pass_demosiac.cpp @@ -44,101 +44,41 @@ void GpuIspShaderPassDemosiac::stop() { } -int GpuIspShaderPassDemosiac::initShaders(PixelFormat inputFormat, PixelFormat outputFormat) +int GpuIspShaderPassDemosiac::selectShaders(struct ShaderConfig &shaderCfg, [[maybe_unused]]PixelFormat &inputFormat, [[maybe_unused]]PixelFormat &outputFormat) { - std::vector shaderEnv; - unsigned int fragmentShaderDataLen = 0; - const unsigned char *fragmentShaderData = 0; - unsigned int vertexShaderDataLen = 0; - const unsigned char *vertexShaderData = 0; - GLenum err; - - /* Target gles 100 glsl requires "#version x" as first directive in shader */ - egl_.pushEnv(shaderEnv, "#version 100"); - - /* Specify GL_OES_EGL_image_external */ - egl_.pushEnv(shaderEnv, "#extension GL_OES_EGL_image_external: enable"); - - /* - * Tell shaders how to re-order output taking account of how the pixels - * are actually stored by EGL. - */ - switch (outputFormat) { - case formats::ARGB8888: - case formats::XRGB8888: - break; - case formats::ABGR8888: - case formats::XBGR8888: - egl_.pushEnv(shaderEnv, "#define SWAP_BLUE"); - break; - default: - LOG(GpuShaderDemosiac, Error) << "Unsupported output format"; - return -EINVAL; - } - /* Pixel location parameters */ glFormat_ = GL_LUMINANCE; bytesPerPixel_ = 1; shaderStridePixels_ = passInputCfg_.stride; - switch (inputFormat) { - case libcamera::formats::SBGGR8: - case libcamera::formats::SBGGR10_CSI2P: - case libcamera::formats::SBGGR12_CSI2P: - firstRed_x_ = 1.0; - firstRed_y_ = 1.0; - break; - case libcamera::formats::SGBRG8: - case libcamera::formats::SGBRG10_CSI2P: - case libcamera::formats::SGBRG12_CSI2P: - firstRed_x_ = 0.0; - firstRed_y_ = 1.0; - break; - case libcamera::formats::SGRBG8: - case libcamera::formats::SGRBG10_CSI2P: - case libcamera::formats::SGRBG12_CSI2P: - firstRed_x_ = 1.0; - firstRed_y_ = 0.0; - break; - case libcamera::formats::SRGGB8: - case libcamera::formats::SRGGB10_CSI2P: - case libcamera::formats::SRGGB12_CSI2P: - firstRed_x_ = 0.0; - firstRed_y_ = 0.0; - break; - default: - LOG(GpuShaderDemosiac, Error) << "Unsupported input format"; - return -EINVAL; - }; - /* Shader selection */ switch (inputFormat) { case libcamera::formats::SBGGR8: case libcamera::formats::SGBRG8: case libcamera::formats::SGRBG8: case libcamera::formats::SRGGB8: - fragmentShaderData = bayer_unpacked_frag; - fragmentShaderDataLen = bayer_unpacked_frag_len; - vertexShaderData = bayer_unpacked_vert; - vertexShaderDataLen = bayer_unpacked_vert_len; + 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: case libcamera::formats::SGRBG10_CSI2P: case libcamera::formats::SRGGB10_CSI2P: - egl_.pushEnv(shaderEnv, "#define RAW10P"); + egl_.pushEnv(shaderCfg.shaderEnv, "#define RAW10P"); if (BayerFormat::fromPixelFormat(inputFormat).packing == BayerFormat::Packing::None) { - fragmentShaderData = bayer_unpacked_frag; - fragmentShaderDataLen = bayer_unpacked_frag_len; - vertexShaderData = bayer_unpacked_vert; - vertexShaderDataLen = bayer_unpacked_vert_len; + 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 { - fragmentShaderData = bayer_1x_packed_frag; - fragmentShaderDataLen = bayer_1x_packed_frag_len; - vertexShaderData = identity_vert; - vertexShaderDataLen = identity_vert_len; + 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; @@ -146,56 +86,25 @@ int GpuIspShaderPassDemosiac::initShaders(PixelFormat inputFormat, PixelFormat o case libcamera::formats::SGBRG12_CSI2P: case libcamera::formats::SGRBG12_CSI2P: case libcamera::formats::SRGGB12_CSI2P: - egl_.pushEnv(shaderEnv, "#define RAW12P"); + egl_.pushEnv(shaderCfg.shaderEnv, "#define RAW12P"); if (BayerFormat::fromPixelFormat(inputFormat).packing == BayerFormat::Packing::None) { - fragmentShaderData = bayer_unpacked_frag; - fragmentShaderDataLen = bayer_unpacked_frag_len; - vertexShaderData = bayer_unpacked_vert; - vertexShaderDataLen = bayer_unpacked_vert_len; + 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 { - fragmentShaderData = bayer_1x_packed_frag; - fragmentShaderDataLen = bayer_1x_packed_frag_len; - vertexShaderData = identity_vert; - vertexShaderDataLen = identity_vert_len; + 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; }; - /* TODO: move from here to the end of the method into a helper function in the base class - * this logic will be common to all pipeline instances - */ - if (egl_.compileVertexShader(vertexShaderId_, vertexShaderData, vertexShaderDataLen, shaderEnv)) { - LOG(GpuShaderDemosiac, Error) << "Compile vertex shader fail"; - return -ENODEV; - } - utils::scope_exit vShaderGuard([&] { glDeleteShader(vertexShaderId_); }); - - if (egl_.compileFragmentShader(fragmentShaderId_, fragmentShaderData, fragmentShaderDataLen, shaderEnv)) { - LOG(GpuShaderDemosiac, Error) << "Compile fragment shader fail"; - return -ENODEV; - } - utils::scope_exit fShaderGuard([&] { glDeleteShader(fragmentShaderId_); }); - - if (egl_.linkProgram(programId_, vertexShaderId_, fragmentShaderId_)) { - LOG(GpuShaderDemosiac, Error) << "Linking program fail"; - return -ENODEV; - } - - egl_.dumpShaderSource(vertexShaderId_); - egl_.dumpShaderSource(fragmentShaderId_); - - /* Ensure we set the programId_ */ - egl_.useProgram(programId_); - err = glGetError(); - if (err != GL_NO_ERROR) { - LOG(GpuShaderDemosiac, Error) << "Use program error " << err; - return -ENODEV; - } - - return getShaderVariableLocations(); + return 0; } int GpuIspShaderPassDemosiac::getShaderVariableLocations(void) 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 60f175ad3..c83024bc4 100644 --- a/src/libcamera/software_isp/gpu_pipeline_shader_pass_demosiac.h +++ b/src/libcamera/software_isp/gpu_pipeline_shader_pass_demosiac.h @@ -37,10 +37,11 @@ public: void stop(); /* Things that every ISP pipeline pass will need to do */ - int initShaders(PixelFormat inputFormat, PixelFormat outputFormat); int getShaderVariableLocations(void); void setShaderVariableValues(const DebayerParams ¶ms, eGLImage &eglImageIn); + int selectShaders(struct ShaderConfig &shaderCfg, PixelFormat &inputFormat, PixelFormat &outputFormat); const char *name() const override { return "GpuIspShaderPassDemosiac"; } + private: /* Shader parameters */ GLint textureUniformStep_;