ios 打包版本使用effect报错

我有个小项目是这样的,我打包的时候,除了一个默认场景,所有的场景都是通过bundle来实现的,我为了让一个程序包做大限度的发挥作用,不用频繁提交包,就设计了一套流程,在application.js中,我是这样实现的

System.register([], function (_export, _context) {

‘use strict’;

/**

  • 打印日志
  • @param {string} message
  • @param {any} args
    */
    function bootLog() {
    if (!DEBUG_BOOT) return;
    var a = Array.prototype.slice.call(arguments);
    a.unshift(LOG_TAG);
    console.log.apply(console, a);
    }

/**

  • 瀑布流:按序执行异步步骤;上一步 resolve 的值作为下一步的第一个参数。
  • @param {Array<function(prev: *): *>} steps 每步可返回 Promise 或同步值
  • @param {*} [seed] 第一步收到的 prev,默认 undefined
  • @param {string[]|undefined} stepLabels 可选;DEBUG_BOOT 时打印 [瀑布] 序号/标签
  • @returns {Promise<*>} 最后一步的 resolve 值
    */
    function runWaterfall(steps, seed, stepLabels) {
    var initial = arguments.length > 1 ? seed : undefined;
    return steps.reduce(function (promise, step, index) {
    return promise.then(function (prev) {
    if (DEBUG_BOOT && stepLabels && stepLabels[index]) {
    bootLog(’[瀑布]’, String(index + 1) + ‘/’ + steps.length, stepLabels[index]);
    }
    return Promise.resolve(step(prev));
    });
    }, Promise.resolve(initial));
    }

/**

  • 远程 bundle 根:与 bc.json 同目录(由 BC_CONFIG_URL 推导)
    /
    function resolveBundleBaseUrl() {
    var s = String(BC_CONFIG_URL).split(’?’)[0].split(’#’)[0];
    var m = s.match(/^(\w+://[^/]+)(/.
    )?$/);
    if (m) {
    if (m[2] && m[2].length > 1) {
    var path = m[2];
    var li = path.lastIndexOf(’/’);
    return m[1] + path.slice(0, li + 1);
    }
    return m[1] + ‘/’;
    }
    var last = s.lastIndexOf(’/’);
    if (last > 6) {
    return s.slice(0, last + 1);
    }
    return s + (s.charAt(s.length - 1) === ‘/’ ? ‘’ : ‘/’);
    }

/** 为 true:引擎 DebugMode.INFO、显示 FPS,并输出启动日志;发布请 false */
var DEBUG_BOOT = true;

/** 仅 DEBUG_BOOT 时输出;统一前缀,adb:adb logcat | findstr [qt-remote] */
var LOG_TAG = ‘[qt-remote]’;

/** bc.json 请求超时(毫秒) */
var FETCH_BC_TIMEOUT_MS = 15000;

/** cc.game.init 使用的 settings 路径;打包与包内文件名对齐时改此处 */
var SETTINGS_PATH = ‘src/settings.f39eb.json’;

/** bc.json 请求地址 */
var BC_CONFIG_URL = ‘https://test.dereky.cn/bc.json’;

var cc = null;

return {
setters: [],
execute: function () {

  class Application {

    constructor() {
      /** @see Step 0.1 */
      this.settingsPath = SETTINGS_PATH;
      this.bcConfigUrl = BC_CONFIG_URL;
      this.fallbackSceneName = 'scene';
    }

    init(engine) {
      cc = engine;
      cc.game.onPostBaseInitDelegate.add(this.onPostInitBase.bind(this));
      cc.game.onPostSubsystemInitDelegate.add(this.onPostSystemInit.bind(this));
    }

    onPostInitBase() { }

    onPostSystemInit() { }

    /**
     * Step 0:引擎初始化 + 进入自定义引导链
     */
    start() {
      var debugMode = DEBUG_BOOT ? cc.DebugMode.INFO : cc.DebugMode.ERROR;

      return cc.game
        .init({
          debugMode: debugMode,
          settingsPath: this.settingsPath,
          overrideSettings: {
            launch: { launchScene: '' },
            profiling: { showFPS: DEBUG_BOOT },
          },
        })
        .then(
          function () {
            cc.game.run();
            // 启动远程链,按照顺序加载 bc.json、common、main_page
            return this.bootstrapRemoteOrFallback();
          }.bind(this),
        )
        .catch(
          function (e) {
            bootLog('启动失败', e && e.message ? e.message : e);
            return this.loadDefaultScene();
          }.bind(this),
        );
    }

    /** runWaterfall(bc → common → main);失败冒泡到 start().catch */
    bootstrapRemoteOrFallback() {
      return runWaterfall(
        [
          this.bootStep01_fetchBcConfig.bind(this),
          this.bootStep02_loadRemoteCommon.bind(this),
          this.bootStep03_loadRemoteMainPage.bind(this),
        ],
        undefined,
        ['拉取bc', '加载公共包', '加载主入口'],
      );
    }

    /** Waterfall 第 1 步:HTTP GET bc.json(超时、_cb、解析 JSON) */
    bootStep01_fetchBcConfig() {

      // 超时时长
      var ms = FETCH_BC_TIMEOUT_MS;
      // 超时控制器
      var ctrl = typeof AbortController !== 'undefined' ? new AbortController() : null;
      // 超时定时器
      var t = setTimeout(function () {
        if (ctrl) ctrl.abort();
      }, ms);
      // bc.json 请求地址
      var bcUrl = this.bcConfigUrl;
      // 请求参数,加上时间避免缓存
      var sep = bcUrl.indexOf('?') >= 0 ? '&' : '?';
      bcUrl = bcUrl + sep + '_cb=' + Date.now();

      // 打印请求地址
      bootLog('配置请求地址:', bcUrl);

      // 请求 bc.json
      return fetch(bcUrl, ctrl ? { signal: ctrl.signal } : {})
        // 请求成功
        .then(function (r) {
          // 清除超时定时器
          clearTimeout(t);
          // 打印响应状态
          bootLog('配置响应状态:', r.status, r.statusText || '');
          // 响应不成功则抛出错误
          if (!r.ok) {
            throw new Error('bc.json 响应异常 HTTP ' + r.status);
          }
          // 解析 JSON
          return r.json();
        })
        .then(function (cfg) {
          bootLog('配置内容:', JSON.stringify(cfg));
          return cfg;
        })
        .catch(function (e) {
          clearTimeout(t);
          throw e;
        });
    }

    /** Waterfall 第 2 步:按 bc.common_bundle 远程 loadBundle; */
    bootStep02_loadRemoteCommon(cfg) {
      // 远程 bundle 根
      var base = resolveBundleBaseUrl();
      // 远程 common 配置
      var cb = cfg.common_bundle;
      // 远程 common 版本
      var cv = cb.bundle_version;
      // 打印远程 common 信息
      bootLog('加载远程公共包:', cb.bundle_name, '根地址:', base);
      // 加载远程 common
      return this.loadRemoteBundleOnly(base, cb.bundle_name, cv).then(function () {
        return cfg;
      });
    }

    /** Waterfall 第 3 步:resolveBundleBaseUrl → loadBundle(main_page) → loadScene → runScene */
    bootStep03_loadRemoteMainPage(cfg) {

      // 远程 main_page 配置
      var mp = cfg.main_page;
      // 远程 bundle 根
      var base = resolveBundleBaseUrl();
      // 打印远程 main_page 信息
      bootLog('主入口包:', mp.bundle_name, '场景:', mp.scene_name);

      // 远程 bundle URL
      var bundleUrl = base + mp.bundle_name;
      // 远程场景名称
      var sceneName = mp.scene_name;
      // 远程 main_page 版本
      var version = mp.bundle_version;

      // 打印远程 main_page 信息
      bootLog('远程包根地址:', bundleUrl);
      bootLog('场景名:', sceneName, '版本号:', version);

      // 加载远程 main_page
      return this.loadRemoteBundleOnly(base, mp.bundle_name, version).then(function (bundle) {
        return new Promise(function (resolve, reject) {
          bootLog('加载场景开始:', sceneName);
          bundle.loadScene(sceneName, function (err2, scene) {
            if (err2) {
              bootLog('加载场景失败', sceneName, err2 && err2.message ? err2.message : err2);
              reject(err2);
              return;
            }
            bootLog('加载场景成功,进入场景:', sceneName);
            cc.director.runScene(scene);
            resolve();
          });
        });
      });
    }

    /** 加载默认场景 */
    loadDefaultScene() {
      // 默认场景名称
      var name = this.fallbackSceneName;
      // 加载默认场景
      return new Promise(function (resolve) {
        // 加载默认场景
        cc.assetManager.loadBundle('main', function (err, bundle) {
          // 加载失败
          if (err) {
            bootLog('默认场景:主包加载失败', err);
            // 加载默认场景
            cc.director.loadScene(name, function () {
              // 加载成功
              resolve();
            });
            return;
          }
          // 加载默认场景
          bundle.loadScene(name, function (err2, scene) {
            // 加载失败
            if (err2) {
              bootLog('默认场景:场景加载失败', err2);
              cc.director.loadScene(name, function () {
                resolve();
              });
              return;
            }
            cc.director.runScene(scene);
            resolve();
          });
        });
      });
    }

    /**
     * 仅远程 loadBundle、不进场景;第 2 步 common 与第 3 步 main_page 共用,避免复制两套 loadBundle。
     */
    loadRemoteBundleOnly(base, bundleName, bundleVersion) {

      // 远程 bundle URL
      var bundleUrl = base + bundleName;
      // 远程 bundle 选项
      var opts = {};
      // 远程 bundle 版本
      opts.version = bundleVersion;

      // 打印远程 bundle 信息
      bootLog('仅加载远程包:', bundleName, '地址:', bundleUrl, '版本:', opts.version);

      // 远程 bundle 文件进度
      opts.onFileProgress = function (finished, total, item) {
        var u = item && (item.url || item.requestURL || item.id) ? String(item.url || item.requestURL || item.id) : '';
        bootLog('远程包下载进度', bundleName, String(finished) + '/' + String(total), u || '(无地址)');
      };

      // 资产管理器
      var am = cc && cc.assetManager;
      // 已加载的远程 bundle
      var existed = bundleName && am.getBundle && am.getBundle(bundleName);

      // 已加载的远程 bundle
      if (existed) {
        // 已加载的远程 bundle 基础 URL
        var b = existed.base != null ? String(existed.base) : '';
        // 已加载的远程 bundle 是否为远程
        var isRemote = b.indexOf('http://') === 0 || b.indexOf('https://') === 0;
        // 已加载的远程 bundle 是否为远程
        if (isRemote) {
          // 已加载的远程 bundle 释放所有资源
          existed.releaseAll();
          // 已加载的远程 bundle 移除
          am.removeBundle(existed);
        }
      }

      // 加载远程 bundle
      return new Promise(function (resolve, reject) {
        // 打印加载远程 bundle 信息
        bootLog('开始加载远程包:', bundleName);
        am.loadBundle(bundleUrl, opts, function (err, bundle) {
          if (err) {
            bootLog('加载远程包失败', bundleName, err && err.message ? err.message : err);
            reject(err);
            return;
          }
          bootLog('加载远程包成功', bundle && bundle.name, bundle && bundle.base);
          resolve(bundle);
        });
      });
    }
  }

  _export('Application', Application);
},

};
});

我有一个名叫common的bundle,在启动游戏的时候,先去远程加载这个bundle,只有在这个bundle加载成功时,才去加载下一个bundle,在这里,是加载一个名叫wether的bundle,然后在这个bundle中,主要的场景里的背景上,写了一个effect,内容如下
// Copyright © 2017-2020 Xiamen Yaji Software Co., Ltd.
// 彩虹水波纹效果 shader
CCEffect %{
techniques:

  • passes:
    • vert: rainbow-wave-vs:vert
      frag: rainbow-wave-fs:frag
      depthStencilState:
      depthTest: false
      depthWrite: false
      blendState:
      targets:
      • blend: true
        blendSrc: src_alpha
        blendDst: one_minus_src_alpha
        blendDstAlpha: one_minus_src_alpha
        rasterizerState:
        cullMode: none
        properties:
        alphaThreshold: { value: 0.5 }
        waveDirX: { value: 1.0, editor: { tooltip: ‘水波传播方向 X(与 waveDirY 组成方向向量;勿全为 0)’ } }
        waveDirY: { value: 1.0, editor: { tooltip: ‘水波传播方向 Y’ } }
        waveSpeed: { value: 2.0, editor: { tooltip: ‘水波扭曲速度(须 waveAmplitude>0 才能看到 UV扭曲;也会进 wave() 影响颜色「跟波」程度)’ } }
        waveScale: { value: 10.0, editor: { tooltip: ‘水波纹密度’ } }
        waveAmplitude: { value: 0.1, editor: { tooltip: ‘UV 扭曲强度:水波「鼓起来」靠它,建议 0.02~0.08;0 则只有颜色涟漪、贴图不形变’ } }
        rainbowSpeed: { value: 0.5, editor: { tooltip: ‘三色渐变转速:越大换色越快,与时间线性相关(建议 0.05~2)’ } }
        paletteWaveInfluence: { value: 0.75, editor: { tooltip: ‘颜色「水波感」:越大越按圆环/定向涟漪变色(推荐 0.6~1);越小越接近整张贴图匀速渐变’ } }
        rainbowIntensity: { value: 0.8, editor: { tooltip: ‘叠加颜色强度’ } }
        waveColor0: { value: [1.0, 0.25, 0.25, 1.0], editor: { type: color, displayName: ‘水波颜色 1’ } }
        waveColor1: { value: [0.25, 1.0, 0.45, 1.0], editor: { type: color, displayName: ‘水波颜色 2’ } }
        waveColor2: { value: [0.35, 0.55, 1.0, 1.0], editor: { type: color, displayName: ‘水波颜色 3’ } }
        saturation: { value: 1.2, editor: { tooltip: ‘饱和度’ } }
        brightness: { value: 1.0, editor: { tooltip: ‘亮度’ } }
        }%

CCProgram rainbow-wave-vs %{
precision highp float;
#include <builtin/uniforms/cc-global>
#if USE_LOCAL
#include <builtin/uniforms/cc-local>
#endif
#if SAMPLE_FROM_RT
#include <common/common-define>
#endif
in vec3 a_position;
in vec2 a_texCoord;
in vec4 a_color;

out vec4 color;
out vec2 uv0;

vec4 vert () {
vec4 pos = vec4(a_position, 1);

#if USE_LOCAL
  pos = cc_matWorld * pos;
#endif

#if USE_PIXEL_ALIGNMENT
  pos = cc_matView * pos;
  pos.xyz = floor(pos.xyz);
  pos = cc_matProj * pos;
#else
  pos = cc_matViewProj * pos;
#endif

uv0 = a_texCoord;
#if SAMPLE_FROM_RT
  CC_HANDLE_RT_SAMPLE_FLIP(uv0);
#endif
color = a_color;

return pos;

}
}%

CCProgram rainbow-wave-fs %{
precision highp float;
#include <builtin/internal/embedded-alpha>
#include <builtin/internal/alpha-test>
#include <builtin/uniforms/cc-global>

in vec4 color;
// 与 rainbow-wave-vs 的 out vec2 uv0 始终成对;勿放在 #if USE_TEXTURE 内,否则 USE_TEXTURE=0 时
// Metal/SPIRV 链路的 VS/FS 接口不一致,易出现 Missing entry point / pipeline layout 报错。
in vec2 uv0;

#if USE_TEXTURE
#pragma builtin(local)
layout(set = 2, binding = 12) uniform sampler2D cc_spriteTexture;
#endif

// Constant 内避免 vec2+float 混排(部分 Metal/驱动对 UBO 步进敏感);方向用两个 float,与 plasma_jet 等一致。
uniform Constant {
vec4 waveColor0;
vec4 waveColor1;
vec4 waveColor2;
float waveDirX;
float waveDirY;
float waveSpeed;
float waveScale;
float waveAmplitude;
float rainbowSpeed;
float rainbowIntensity;
float saturation;
float brightness;
float paletteWaveInfluence;
};

// 在三种颜色间循环插值:0→1→2→0,t 为任意实数相位
vec3 paletteThree(float t, vec3 c0, vec3 c1, vec3 c2) {
float x = fract(t) * 3.0;
float seg = floor(x);
float w = fract(x);
if (seg < 0.5) {
return mix(c0, c1, w);
}
if (seg < 1.5) {
return mix(c1, c2, w);
}
return mix(c2, c0, w);
}

// 水波纹函数 - 随机律动版本
float wave(vec2 uv, float time) {

// 使用多个不同频率和相位的正弦波创造随机效果
float wave1 = sin(uv.x * waveScale * 1.0 + time * waveSpeed * 0.8) * 0.5 + 0.5;
float wave2 = sin(uv.y * waveScale * 1.3 + time * waveSpeed * 1.2) * 0.5 + 0.5;
float wave3 = sin((uv.x + uv.y) * waveScale * 0.7 + time * waveSpeed * 0.6) * 0.5 + 0.5;
float wave4 = sin((uv.x - uv.y) * waveScale * 1.1 + time * waveSpeed * 1.4) * 0.5 + 0.5;
float wave5 = sin(uv.x * waveScale * 0.5 + uv.y * waveScale * 0.9 + time * waveSpeed * 0.9) * 0.5 + 0.5;

// 添加噪声效果
float noise1 = sin(uv.x * 20.0 + time * 2.0) * sin(uv.y * 15.0 + time * 1.5) * 0.1;
float noise2 = cos(uv.x * 12.0 + time * 1.8) * cos(uv.y * 18.0 + time * 2.2) * 0.1;

// 混合多个水波纹和噪声
float waveValue = (wave1 + wave2 + wave3 + wave4 + wave5) / 5.0 + noise1 + noise2;

// 添加径向水波纹效果
float distance = length(uv - vec2(0.5, 0.5));
float radialWave = sin(distance * waveScale * 2.0 + time * waveSpeed * 0.7) * 0.3;

// 最终混合
waveValue = (waveValue + radialWave) * 0.6;

return clamp(waveValue, 0.0, 1.0);

}

vec4 frag () {

vec4 o = vec4(1, 1, 1, 1);

#if USE_TEXTURE

  float time = cc_time.x;
  
  // 计算水波纹偏移
  float waveValue = wave(uv0, time);
  
  // 应用水波纹偏移到UV坐标 - 随机版本
  vec2 waveOffset = vec2(
    sin(uv0.x * waveScale * 1.2 + time * waveSpeed * 0.9) * waveAmplitude * 0.8 +
    cos(uv0.y * waveScale * 0.8 + time * waveSpeed * 1.1) * waveAmplitude * 0.6,
    cos(uv0.y * waveScale * 1.1 + time * waveSpeed * 0.7) * waveAmplitude * 0.8 +
    sin(uv0.x * waveScale * 0.9 + time * waveSpeed * 1.3) * waveAmplitude * 0.6
  );
  
  // 添加径向扭曲(中心点避免 normalize(0))
  vec2 center = vec2(0.5, 0.5);
  vec2 fromCenter = uv0 - center;
  float distance = length(fromCenter);
  vec2 radialDir = distance > 1e-4 ? fromCenter / distance : vec2(1.0, 0.0);
  vec2 radialOffset = radialDir * sin(distance * waveScale * 3.0 + time * waveSpeed * 0.5) * waveAmplitude * 0.3;
  
  vec2 distortedUV = uv0 + waveOffset + radialOffset;
  
  // 采样纹理
  o *= CCSampleWithAlphaSeparated(cc_spriteTexture, distortedUV);
  
  // 颜色相位:同心圆涟漪 + 方向波 → 水波状色带;再与整体时间漂移、wave() 高度场混合
  vec2 p = uv0 - center;
  vec2 waveDirVec = vec2(waveDirX, waveDirY);
  float wLen = length(waveDirVec);
  vec2 wNorm = wLen > 1e-4 ? waveDirVec / wLen : vec2(1.0, 0.0);
  float ringRipple = sin(distance * waveScale * 6.2831853 - time * waveSpeed * 1.65) * 0.5 + 0.5;
  float dirRipple = sin(dot(p, wNorm) * waveScale * 6.2831853 + time * waveSpeed * 1.15) * 0.5 + 0.5;
  float ripplePhase = mix(ringRipple, dirRipple, 0.45);
  float timeDrift = time * rainbowSpeed;
  float palettePhase = mix(timeDrift, ripplePhase * 1.15 + timeDrift * 0.2, clamp(paletteWaveInfluence, 0.0, 1.0))
    + waveValue * paletteWaveInfluence * 0.22;
  vec3 paletteColor = paletteThree(
    palettePhase,
    waveColor0.rgb,
    waveColor1.rgb,
    waveColor2.rgb
  );
  
  vec3 finalColor = paletteColor * rainbowIntensity;
  
  // 应用饱和度和亮度调整
  float gray = dot(finalColor, vec3(0.299, 0.587, 0.114));
  finalColor = mix(vec3(gray), finalColor, saturation);
  finalColor *= brightness;
  
  o.rgb = finalColor;
#endif

o *= color;
ALPHA_TEST(o);
return o;

}
}%

这个效果,在安卓上正常运行,但是在ios上,就报错,效果就没有了,我之前认为可能是不同平台的渲染引擎差异,然后我调试了很久都不行,最后我把这个effect复制出来,创建了一个空项目,并且在把这个effect放到了默认场景下,然后我打包成本地资源,结果打开是可以的,然后我怀疑出现问题的地方可能是远程加载和本地资源有差异,我就把远程的bundle内容和本地的做了一个对比,发现远程的bundle没有问题,也就是说,现在只要是远程的effect,在ios里就不行

我想请大佬帮忙看一下,你们可以随便创建一个3.8.8 的项目,然后构建ios,并使用上面的application.js,里面的资源我暂时不会动,看看这到底是引擎的问题,还是我的问题

补充一个ios运行时的错误内容
16:38:17 [ERROR]: Failed to create pipeline state, please check if shader/pileinelayout match with each other!

16:38:17 [ERROR]: [ERROR] file /Applications/Cocos/Creator/3.8.8/CocosCreator.app/Contents/Resources/resources/3d/engine/native/cocos/renderer/gfx-metal/MTLPipelineState.mm: line 219

16:38:17 [ERROR]: Failed to create pipeline state, please check if shader/pileinelayout match with each other!

16:38:17 [ERROR]: [ERROR] file /Applications/Cocos/Creator/3.8.8/CocosCreator.app/Contents/Resources/resources/3d/engine/native/cocos/renderer/gfx-metal/MTLPipelineState.mm: line 219