Bevy Rendering

Bevy 渲染系统的核心概念、管线结构与近期关键修复汇总。

渲染管线概览

Bevy 使用基于 wgpu 的模块化渲染管线,主要阶段包括:

  • Extract:将 ECS 世界中的数据提取到渲染世界
  • Prepare:准备渲染资源(如 PrepareAssets 阶段)
  • Queue:排序并生成渲染指令
  • Render:执行 GPU 绘制调用

近期关键修复

Mesh 重分配 GPU 内存泄漏修复(2026-08-04,critical)

PR #25259(0.19.1,+735/-13)修复了 mesh handle 从 Assets 移除后同帧重新插入相同 handle 时发生的 GPU 内存泄漏(#18808)。

根因: MeshAllocator::free_meshes 原逻辑假设 extracted_meshes.added 只包含新增 mesh,但实际上它持有本帧提取的全部 mesh。当 mesh 被移除后重新插入时,旧 slab 分配未被释放,导致 GPU 内存随帧累积。

修复: free_meshes 现在释放本帧所有计划重分配的 mesh,避免旧 slab 残留。slab_allocator 新增 empty_slabs 追踪,在无 pending submit 时尝试 reallocation 会 panic/error。

测试基建副产物: 新增 test_utils.rs 使用 wgpu noop backend,让渲染单测可在无 GPU 环境创建 dummy RenderDevice/RenderQueue——为后续渲染模块单测铺设了路径。

WESL 迁移(2026-08-05,M-Release-Note,378 files)

PR #25088(+4527/-5381, 378 files, 无 milestone)将 Bevy 全部内部 shader 迁移到 WESL(webgpu-tools 的 WGSL 超集扩展),经 wesl-rs 实现。这是 0.20 前渲染侧最大的基础变更:自定义 shader / material 作者需关注 0.20 的 shader 资产格式与导入语法变化。

0.19.1 渲染 bugfix 批次(2026-08-05,同一 merge-queue 批次)

  • #25261 — 修复 binding_arrays_are_usable 检查(+8/-1)。旧检查误用 max_storage_textures_per_shader_stage(#10268 时代 wgpu 无对应 limit),现改查 max_binding_array_elements_per_shader_stage / max_binding_array_sampler_elements_per_shader_stage
  • #25256 — clearcoat 下 emission 正确分层(+2/-1)。修复 #25083,按 glTF KHR_materials_clearcoat 规范实现 emission 分层。
  • #25252 — 修复 shader 越界访问(+8/-3, 2 files)。修复 #25251 及作者另发现的越界 bug。

三者均绑定 0.19.1,指向补丁发布临近。

UI 渲染提取 removed-component 路径修复(2026-08-07,两个 P-Regression,0.20)

PR #25188 + #25184(2026-08-07 批量合并,均为 milestone=0.20 的 P-Regression 修复)补齐了 UI 渲染提取管线在组件被移除时的两条漏洞,两个 PR 都绑定 0.20——UI 渲染提取逻辑在 0.19 开发中累积的回归正在 0.20 集中清理:

  • #25188 — CalculatedClip 移除后 gradient 未被重新提取:UI 渐变依赖 CalculatedClip,组件被移除时渐变本应重新提取,但旧逻辑直接 despawn 对应 render entity。修复将 removed CalculatedClip 实体链入 UI nodes changed query,并从 cleanup iterator 移除 removed_calculated_clip_query
  • #25184 — MaterialNode 变更检测遗漏:(1) 材质异步加载完成不触发组件 Changed,导致加入 nodes_to_reextract_next_frame 的节点不保证下帧重提取;(2) CalculatedClipMaterialNode 实体移除时,ExtractedUiMaterialNode 不更新而是连同 render entity 被删除。

影响场景:UI 中使用自定义 Material(MaterialNode)或 gradient 背景且动态变更 clip 的项目。

08-11 渲染修复/性能批次(2026-08-11,合并暂停恢复后)

08-11 单日 18 PR 中 7 个渲染相关(2 个 0.20 正确性 + 2 个性能 + 3 个 0.19.1 bugfix),显示 0.20 渲染侧在正确性与性能两个方向同时收口:

  • #24971 — 间接参数构建 shader 越界写修复(0.20, P-Regression, +521/-73, 5 files)。修复 #23662 引入的阴影偶发闪烁:indirect params 构建 shader 每次调用只写自己负责的 batch range。触发条件:多视图(如点光源提取 6 个 shadow view)+ 物体 OBB 变化导致跨视图移动时;3d_shapes 示例可复现。
  • #24148 — UI CPU clipping 支持旋转/缩放(0.20, M-Deliberate-Rendering-Change, +758/-645, 18 files)Overflow clipping 现支持变换节点。CalculatedClip 由结构体变为 enum(Rects:本地坐标 Rect 列表 + world-to-local Affine2FullyClipped),bevy_ui_render 新增 clipping 模块 + clip_polygon。修复 17630/21775/251260.20 破坏性变更(自定义 UI 渲染/提取/picking 代码需适配 enum)。
  • #25058 — material bind group 复用(pcwalton, +579/-147, 7 files)。仅 POD 变化(如 color)时不释放重建 bind group,只有 GPU 资源(buffer/texture/sampler)变化才重建——避免 slab 无谓溢出与重建开销。
  • #24928 — 2D mesh 保留式提取(+235/-46, 7 files)RenderMesh2dInstances 从逐帧全量重提取改为保留式 + 细粒度 change detection(对齐 3D mesh 已有逻辑),未变化 mesh 不再重复提取。
  • #25253 — 2D flicker:retained phase items 清理(0.19.1, +8/-4)queue_material2d_meshesrender_material_instances 为空时提前 return,最后一个使用该 material 类型的实体 despawn 帧的 retained phase items 永久滞留 → 修复 #25163。
  • #25338/#25334 — SSAO 双修复(均 0.19.1)。#25338:texture_depth_2dtextureGather 首参数非整数导致 Chrome createShaderModule 报错(+1/-1);#25334:processSample() 将归一化法线 [0..1] 与 fast_acos() 弧度值混用,front_back_horizon 需先除以 PI 归一化(+5/-6, 4 files)。
  • #25325 — 2D material 同帧 swap 渲染失败(+16/-4)。同帧添加 MeshMaterial2d<M2> + 移除 MeshMaterial2d<M1> 时,实例被错误从 RenderMaterial2dInstances 删除(类型不匹配时不该删)。

模式信号:SSAO 在 08-10/11 连续收到 3 个修复(含 open 的 #25319 viewport depth sampling、#25358 depth-writes),0.19.1/0.20 对 SSAO 正确性集中收口。

GPU sorted phase batching bugfix(2026-05-22)

PR #24365 修复了 GPU 预处理路径忽略 AUTOMATIC_BATCHING = false 标志的 bug。当 GPU 预处理启用时,本应按有序 chunk 渲染的 sorted phase 项被错误地合并批处理。

  • 影响场景: Transmissive3d 等需要按 ScreenSpaceTransmission.steps 控制渲染顺序的 pass
  • 修复: GPU 预处理路径现在检查 PhaseItem::AUTOMATIC_BATCHING 再决定是否批处理
  • 0.19 milestone bugfix,RenderDoc 已验证修复

Solari light leak 改进(2026-05-21)

PR #24332(JMS55)两项改进降低 Solari GI 的光照泄漏:

  1. Jitter 仅在 specular GI 时开启(减少非必要噪声)
  2. 选择大于 ray_t 的最大 cell size,而非立即 snap 到 base cell

标签 C-Refinement,改善输出质量。详见 bevy-solari-pathtracer

SpotLight + Camera 同实体 Frustum 覆盖(2026-05-21)

PR #24324(纯文档):当 SpotLightCamera 在同一实体时,聚光灯 frustum 计算会覆盖相机的 Frustum,导致错误的 frustum culling。文档添加了粗体警告。

PR #24216 修复了 Windows 11 Vulkan 下窗口最小化/恢复后的阴影闪烁问题。相关逻辑原在 PrepareAssets 阶段中运行;PR 将其移动到 Specialize 并在 specialize_shadows 前执行,规避了该阶段变化带来的不一致性。仅影响 Windows 11 + Vulkan 且触发最小化/恢复路径的用户。

GPU 可见性裁剪回退(待决定)

PR #24252 提议回退 #23115(GPU 可见性范围裁剪),以避免 0.19 发布时的阴影回归。JMS55 和 Zeophlite 已批准该务实选择。若合并,0.19 可能在 24-48 小时内发布。

体素渲染(Voxel Rendering)

体素场景在 Bevy 中通过表面网格化实现,主要流程为:体素数据 → Greedy Meshing → Bevy Mesh → GPU 光栅化。关键工具链:

与 Virtual Geometry(Meshlet)的结合:体素经 greedy meshing 生成的 mesh 可转换为 MeshletMesh,获得自动连续 LOD 和 GPU-driven 剔除。

物体高亮与选择(Hover Highlighting)

游戏中鼠标悬停或选中物体的高亮效果在 Bevy 中有多种实现路线:

Bevy 0.15+ 原生能力:自定义渲染阶段可在主 Pass 之后执行后处理,Prepass 提供深度/法线纹理供 Sobel 边缘检测使用。

Skybox 渲染世界清理(0.19 bugfix)

PR #24389(2026-05-22 合并)修复了一个影响所有 Skybox 用户的渲染世界残留 bug:skybox 组件从主世界移除后,渲染世界中的提取副本不会同步清除。

根因: Skybox 在 PR #22682 中从 bevy_pbr 迁移至 bevy_light 时,因 orphan rule 限制改为手动采掘逻辑,但遗漏了 SyncComponentPlugin<Skybox> 的手动注册(该 plugin 在 ExtractComponentPlugin 路径下会自动注册,手动路径则需显式声明)。详见 sync-component-plugin

修复: 一行 app.add_plugins(SyncComponentPlugin::<Skybox>::default())

复现: anisotropy 示例中按 Space 从环境贴图模式切回方向光模式时,天空盒应消失但之前一直残留。

相关页面