Processing P3D模式setup()加载OBJ文件PShape无顶点异常问题
Processing P3D模式OBJ加载失效、运行卡顿问题修复
问题背景
- 基于Processing开发的太空星体小游戏正在进行3D版本改造,开发者已有P3D渲染模式下球体元素开发经验,并非零基础接触Processing 3D开发。
- 项目地址:SpaceStarProcessing3D,仓库内已附带符合许可协议的Cirno模型与纹理文件;岩石模型与纹理因版权归属不明确未纳入仓库,可通过仓库内指引自行获取。
异常表现
- 实现方案1(存在已知设计缺陷):在
Star类构造函数中加载星体对应的OBJ文件,程序可正常运行,但运行效率极低,搭载i5-12600k处理器的设备也会出现明显卡顿。 - 实现方案2:将OBJ文件加载逻辑迁移到
setup()函数中执行,加载功能完全失效。通过调试器排查发现,加载得到的PShape对象不包含任何顶点数据,问题成因暂不明确。 - 特殊现象:仅在调试器启动程序时,控制台会输出如下线程锁等待超时的异常堆栈:
RunnableTask.run(): A caught exception occured on thread main-Display-.x11_:0-1-EDT-1: RunnableTask[enqueued true[executed false, flushed false, thread[0x33a9822b, main-Display-.x11_:0-1-EDT-1]], tTotal 0 ms, tExec 0 ms, tQueue 0 ms, attachment null, throwable java.lang.RuntimeException: Waited 5000ms for: <364c201e, 7d976186>[count 2, qsz 0, owner <main-FPSAWTAnimator#00-Timer0>] - <main-Display-.x11_:0-1-EDT-1>] java.lang.RuntimeException: Waited 5000ms for: <364c201e, 7d976186>[count 2, qsz 0, owner <main-FPSAWTAnimator#00-Timer0>] - <main-Display-.x11_:0-1-EDT-1> at jogamp.common.util.locks.RecursiveLockImpl01Unfairish.lock(RecursiveLockImpl01Unfairish.java:198) at jogamp.newt.WindowImpl$ResizableAction.run(WindowImpl.java:2159) at jogamp.newt.DisplayImpl.runOnEDTIfAvail(DisplayImpl.java:447) at jogamp.newt.WindowImpl.runOnEDTIfAvail(WindowImpl.java:2859) at jogamp.newt.WindowImpl.setResizable(WindowImpl.java:2195) at com.jogamp.newt.opengl.GLWindow.setResizable(GLWindow.java:371) at processing.opengl.PSurfaceJOGL.lambda$setResizable$5(PSurfaceJOGL.java:436) at com.jogamp.common.util.RunnableTask.run(RunnableTask.java:125) at jogamp.newt.DefaultEDTUtil$NEDT.run(DefaultEDTUtil.java:375) DefaultEDT.run(): Caught exception occured on thread main-Display-.x11_:0-1-EDT-1: RunnableTask[enqueued false[executed true, flushed false, thread[0x33a9822b, main-Display-.x11_:0-1-EDT-1]], tTotal 5001 ms, tExec 5001 ms, tQueue 0 ms, attachment null, throwable java.lang.RuntimeException: Waited 5000ms for: <364c201e, 7d976186>[count 2, qsz 0, owner <main-FPSAWTAnimator#00-Timer0>] - <main-Display-.x11_:0-1-EDT-1>] java.lang.RuntimeException: Waited 5000ms for: <364c201e, 7d976186>[count 2, qsz 0, owner <main-FPSAWTAnimator#00-Timer0>] - <main-Display-.x11_:0-1-EDT-1> at jogamp.common.util.locks.RecursiveLockImpl01Unfairish.lock(RecursiveLockImpl01Unfairish.java:198) at jogamp.newt.WindowImpl$ResizableAction.run(WindowImpl.java:2159) at jogamp.newt.DisplayImpl.runOnEDTIfAvail(DisplayImpl.java:447) at jogamp.newt.WindowImpl.runOnEDTIfAvail(WindowImpl.java:2859) at jogamp.newt.WindowImpl.setResizable(WindowImpl.java:2195) at com.jogamp.newt.opengl.GLWindow.setResizable(GLWindow.java:371) at processing.opengl.PSurfaceJOGL.lambda$setResizable$5(PSurfaceJOGL.java:436) at com.jogamp.common.util.RunnableTask.run(RunnableTask.java:125) at jogamp.newt.DefaultEDTUtil$NEDT.run(DefaultEDTUtil.java:375)
复现条件
- 设备:台式机、笔记本均可稳定复现
- 涉及版本:Processing 4.0b7、4.0b8
- 已排除诱因:Cirno模型缺失mtl文件与该问题无关
根因分析
- 构造函数内加载导致卡顿:每实例化一个
Star对象就会触发一次OBJ文件磁盘IO读取、模型顶点/面数据解析,大量重复的IO和CPU密集型操作会占满计算资源,属于典型的重复加载静态资源的设计错误。 setup()内加载失效、伴随锁异常:Processing 4.x beta版本的JOGL P3D渲染器存在OpenGL上下文初始化时序问题。loadShape()解析OBJ模型依赖已经完成初始化的OpenGL上下文,若在setup()中调用loadShape()的时机早于P3D渲染表面的OpenGL上下文完全就绪,就会出现模型解析失败、返回无顶点空PShape的问题。控制台输出的5000ms锁等待异常,是JOGL的EDT事件调度线程和FPS动画计时器线程发生锁竞争导致的阻塞,调试模式下程序启动速度变慢,会放大这个时序冲突的触发概率,因此仅调试时会打印堆栈。
修复方案
- 解决卡顿问题:将所有OBJ模型的加载逻辑从
Star类构造函数中移出,作为全局静态资源统一加载一次,所有Star实例共用同一个加载完成的PShape对象,从根源消除重复IO和解析开销。 - 解决
setup()内加载失效问题,两种方案二选一即可:- 方案1:在
setup()最开头调用size()指定P3D渲染器,若需要开启窗口可调大小,先保持surface.setResizable(false),等待2帧渲染周期、确认OpenGL上下文完全初始化后,再执行loadShape()操作,模型全部加载完成后再开启窗口resize功能。 - 方案2(稳定性最高):不在
setup()中执行模型加载,改为在draw()函数第一帧执行加载逻辑。新增布尔标记boolean modelLoaded = false,进入draw()后首先判断该标记,若为false则执行所有loadShape()操作,加载完成后将标记设为true,后续帧直接跳过加载步骤即可。draw()执行时渲染上下文已经100%就绪,不会出现时序问题。
- 方案1:在
- 额外注意:OBJ模型的纹理绑定操作不要放在加载阶段执行,放到
draw()中模型绘制前绑定即可,避免跨线程导致纹理上下文丢失。
内容的提问来源于stack exchange,提问作者JoLi
相关产品推荐
相关产品推荐

