admin管理员组

文章数量:1312916

Some details on the setup - I'm using React Native with Expo (SDK 51) and I'm building an app that has an image filter/editor type component on one of the pages.

I ended up using gl-react and gl-react-expo to apply some filters to the images and then use captureRef to save the edited photos. The problem comes in when I want to use LUTs - this was (or at least seemed like) the best option to easily transfer any photo edits I've made into a filter. I opted to use square LUT png textures, started with smaller ones but it introduced banding, so I went for 4096x4096 textures, and it looks fine.

Firstly, it takes ages to load each one of the filters (I still haven't tried out the app outside of Metro in a standalone build), and when the filter does load, it takes up an additional 150-250MB of RAM for each filter loaded in the performance inspector. I'm genuinely not sure why this would be happening, when the LUT images are 10-12MB at most. When clicking through all of the filters the RAM usage just goes up, going from 300MB to 3-4GB! This goes back to normal when leaving the page.

The largest of the filter textures is no more than 11-12 MB, and I have them all in a map ready to map over in the list where the user chooses filters, like this:

export const FILTERS = {
  normal: {
    key: 'normal',
    type: 'lut',
    name: 'Normal',
    displayName: <LUTPreview color1="#FFFFFF" color2="#FFFFFF" name="Normal" />,
    shader: lutShader,
    uniforms: {
      lut: require('../assets/luts/neutral.png'),
      t: { uri: '' },
      intensity: 1,
    },
  },
  'blackwhite-fade': {
    key: 'blackwhite-fade',
    type: 'lut',
    name: 'B&W Fade',
    displayName: (
      <LUTPreview color1="#FFFFFF" color2="#222222" name="B&W Fade" />
    ),
    shader: lutShader,
    uniforms: {
      lut: require('../assets/luts/blackwhite-fade.png'),
      t: { uri: '' },
      intensity: 1,
    },
  },
... rest of the filters

Then in the ImageEditor component I load up the filter and shader, pass in the t prop of the uniforms for the image being edited:

{*/ Shortened for just the part where I show the image /*}
          <Surface
            style={{
              width: imageDimensions.width,
              height: imageDimensions.height,
            }}
          >
            {selectedFilterObj && (
              <ImageFilter
                key={selectedFilterObj.key}
                shader={selectedFilterObj.shader}
                uniforms={{
                  ...selectedFilterObj.uniforms,
                  intensity: filterIntensity,
                }}
              />
            )}
          </Surface>

The ImageFilter is just a Node from gl-react:

const ImageFilter = ({ uniforms, shader }: ImageFilterProps<UniformsType>) => {
  return (
    <Node
      shader={shader}
      uniforms={{
        ...uniforms,
      }}
    />
  );
};

I'll include the shader as well, along with a sample LUT image:

Shader

import { GLSL } from 'gl-react';

const lutShader = {
  frag: GLSL`
    precision mediump float; 
    varying vec2 uv;
    uniform sampler2D t;    
    uniform sampler2D lut;  
    uniform float intensity;

    vec4 applyLUT(vec4 src_color) {
      const vec2 tiles = vec2(16.0); 
      const float tileSize = 256.0;  
      
      float b = clamp(src_color.b * 255.0, 0.0, 255.0);
      float b_i = floor(b);
      
      // Calculate tile indices more efficiently
      vec2 tileIndex;
      tileIndex.y = floor(b_i * 0.0625);  // Precalculated 1/16
      tileIndex.x = mod(b_i, 16.0);
      
      // Simplified UV calculation
      vec2 pixelPos = src_color.rg * (tileSize - 1.0) + 0.5;
      vec2 uv1 = (tileIndex * tileSize + pixelPos) * 0.000244140625;  // Precalculated 1/4096

      vec3 lutColor = texture2D(lut, uv1).rgb;
      return vec4(lutColor, 1.0);
    }

    void main() {
      vec4 color = texture2D(t, uv);
      vec4 lutColor = applyLUT(color);
      gl_FragColor = mix(color, lutColor, intensity);
    }
  `,
};

export default lutShader;

Sample LUT - Note had to compress into JPEG to post here

I tried to change the method to use Skia, but it wasn't working well and it wasn't even much faster. I believe I also tried preloading the assets with expo-asset which just kept the app loading for a long time, and didn't help at all.

本文标签: androidrequire(*image*) in React Native seems to increase RAM usage drasticallyStack Overflow