Bevy 迁移指南:Render World 窗口数据改为 ECS 组件(ExtractedWindow 与 SurfaceData)
2026/9/8 22:16:37 网站建设 项目流程

Bevy 迁移指南:Render World 窗口数据改为 ECS 组件(ExtractedWindow 与 SurfaceData)

【免费下载链接】bevyA refreshingly simple>项目地址: https://gitcode.com/GitHub_Trending/be/bevy

在 Bevy 的 Render World 中,窗口数据不再以ExtractedWindows/ExtractedWindowSurfaces这类资源形式存在,而是直接作为组件挂在渲染世界中与窗口关联的实体上。如果你曾在自定义渲染逻辑中读取过这两个资源,本次迁移只需将资源查询替换为Query<&ExtractedWindow>Query<&SurfaceData>;如果你依赖ExtractedWindows::primary,现在可以改用Query<&ExtractedWindow, With<PrimaryWindow>>。读完本文,你将理解新组件的完整字段结构、它在 Extract / Render 调度中的实际数据流,以及逐条可执行的迁移替换方案。

迁移背景:从资源到组件

Bevy 的渲染流程分为 Main World 与 Render World 两个世界:Main World 承载游戏逻辑,Render World 承载渲染所需的提取(extracted)数据。过去,窗口相关的提取数据被集中放在两个资源里:

  • ExtractedWindows:所有窗口的提取数据(尺寸、present mode 等),并带有primary字段指向主窗口;
  • ExtractedWindowSurfaces:每个窗口对应的SurfaceData(wgpu surface 与 surface 配置)。

本次变更(对应上游 PR #25005)将这两份数据拆散,直接作为组件挂到 Render World 中每个窗口对应的实体上。这带来三个直接收益:

  1. 逐窗口访问更自然:过去要从资源里按 entity 查找,现在标准的 ECS 查询即可,且能享受查询缓存与调度器依赖分析的好处;
  2. PrimaryWindow标签组件统一:主窗口判断从资源字段变为实体上的标签组件,可用With<PrimaryWindow>过滤;
  3. 与 Render World 实体的生命周期同步:窗口关闭时实体被 despawn,其携带的窗口数据随之销毁,无需资源手动清理。

从当前仓库源码可以确认,旧的ExtractedWindowsExtractedWindowSurfaces资源类型已不存在于 bevy_render 中,全仓库仅余一处注释提及旧名。

新 API:ExtractedWindow组件

新的核心类型是 ExtractedWindow,定义于bevy_renderview::window模块。它是#[derive(Component)]组件,字段说明如下:

字段类型说明
physical_width/physical_heightu32窗口的物理像素尺寸,提取时保证最小为 1
present_modePresentMode来自 Main World 的Window::present_mode
desired_maximum_frame_latencyOption<NonZero<u32>>期望的最大帧延迟;未设置时 surface 配置使用默认值 2(见下文create_surfaces
swap_chain_texture_viewOption<TextureView>交换链纹理视图;截图时会指向替代纹理以便把渲染结果拷回 CPU
swap_chain_textureOption<SurfaceTexture>当前帧的交换链纹理(wgpu::SurfaceTexture包装)
swap_chain_texture_formatOption<TextureFormat>交换链纹理格式(即 surface 配置格式)
swap_chain_texture_view_formatOption<TextureFormat>上者加 sRGB 后缀的视图格式,使着色器中始终处于线性空间
size_changedbool本帧尺寸是否变化,create_surfaces据此决定是否重配 surface
present_mode_changedbool本帧 present mode 是否变化
alpha_modeCompositeAlphaMode窗口的合成 alpha 模式,逐值映射到wgpu::CompositeAlphaMode
needs_initial_presentbool是否需要初始提交;Wayland 下窗口必须至少 present 一次才会显示

窗口实体上还会同时携带:

  • PrimaryWindow:标签组件,标识主窗口;
  • RawHandleWrapper:原始窗口句柄(display handle + window handle),供创建 wgpu surface 使用;
  • SurfaceData:见下一节。

ExtractedWindow还提供了一个实例方法 present():取出swap_chain_texture并调用其present(queue)将帧提交给 wgpu。

新 API:SurfaceData组件

SurfaceData 是ExtractedWindowSurfaces资源的替代,同样是窗口实体上的组件:

#[derive(Component)] pub struct SurfaceData { surface: WgpuSurface, // wgpu::Surface 的 Send 包装 configuration: SurfaceConfiguration, texture_view_format: Option<TextureFormat>, }

其中WgpuSurface通过wgpu_wrapper!宏生成(见同一文件 L212-L213),用于把wgpu::Surface<'static>装进 ECS 的 Send 约束体系。若你过去通过ExtractedWindowSurfaces读取 surface 配置(格式、宽度、高度、present mode),现在直接Query<&SurfaceData>即可。

数据如何产生:extract_windows 系统

窗口数据由 extract_windows 系统在ExtractSchedule中产生,执行顺序位于extract_cameras之前(在 WindowRenderPlugin 中注册为extract_windows.before(extract_cameras))。其工作逻辑:

  1. 遍历 Main World 中带Window组件的实体(通过Extract<Query<(RenderEntity, &Window, &RawHandleWrapper, Has<PrimaryWindow>)>>);
  2. 若窗口带PrimaryWindow,向 Render World 对应实体插入PrimaryWindow
  3. 若该实体尚无ExtractedWindow,则插入一份初始组件(尺寸取window.resolution的物理分辨率并钳制最小为 1,needs_initial_present初始为true)并附带RawHandleWrapper
  4. 否则比较新旧尺寸与 present mode,分别置位size_changed/present_mode_changed标志,并输出 debug 日志;
  5. 响应WindowClosing消息、RawHandleWrapper移除与PrimaryWindow移除事件,在 Render World 中对应地 despawn 窗口实体或移除PrimaryWindow

插件初始化部分还值得注意:它用 observer 在每次Add<Window>时向新窗口实体插入SyncToRenderWorld,并用一次run_system_once补漏——因为主窗口在该插件构建前就已存在(L36-L50)。

Surface 的创建与每帧更新流程

Render World 中还有两个系统消费ExtractedWindow

create_surfaces(L353-L455),仅在need_surface_configuration为真时运行(即存在尚未配置、或size_changed/present_mode_changed的窗口):

  • 首次运行时,用实体上的RawHandleWrapper构建SurfaceTargetUnsafe::RawHandle,经create_surface_unsafe创建 surface,并偏好 sRGB 格式(Rgba8UnormSrgb/Bgra8UnormSrgb,否则回退到第一个可用格式);desired_maximum_frame_latency缺省时使用常量DEFAULT_DESIRED_MAXIMUM_FRAME_LATENCY = 2(L346-L350,注释说明了 1 会因 CPU 等待 GPU 而可能降低帧率);随后把SurfaceData插入窗口实体;
  • 尺寸或 present mode 变化时,丢弃旧交换链纹理(避免 wgpu 校验错误),更新configuration的宽高并重新configure_surface
  • 在 macOS/iOS 上通过NonSendMarker被调度器约束到主线程,因为部分系统要求 surface 创建必须在主线程。

prepare_windows(L243-L335),每个 Render 帧在RenderSystems::PrepareViews中运行,负责获取交换链纹理:

  • 跳过没有相机以该窗口为目标的窗口(除非仍需初始 present),避免无谓的 clear pass;
  • 若已有交换链纹理且尺寸、present mode 均未变化,则继续复用;
  • surface.get_current_texture()的结果分支处理:成功/次优时调用set_swapchain_texture更新swap_chain_texture*系列字段;Outdated时重新configure_surface并重取;Occluded与超时(Linux mesa 的已知驱动怪癖)按注释中的条件安全忽略或告警。

present mode 的协商由 present_mode() 完成:按AutoVsync/AutoNoVsync/Mailbox等预设回退序列挑选 surface 实际支持的wgpu::PresentMode,兜底必为Fifo

渲染完成后的提交发生在主渲染系统 render_system 中:它对每个带ViewTarget的视图检查是否需要 present,满足条件(或needs_initial_present)时调用window.present(&render_queue)并清零标志。

迁移操作:逐条替换

结合上述源码,迁移时可以按以下对照执行:

1. 读取提取的窗口数据

// 旧:资源查询(已移除) fn my_system(windows: Res<ExtractedWindows>) { let (w, h) = (windows.physical_width, windows.physical_height); } // 新:组件查询 fn my_system(windows: Query<(MainEntity, &ExtractedWindow)>) { for (main_entity, window) in &windows { let (w, h) = (window.physical_width, window.physical_height); } }

2. 读取 surface 数据

// 旧:Res<ExtractedWindowSurfaces> // 新: fn my_system(surfaces: Query<&SurfaceData>) { /* ... */ }

3. 获取主窗口

// 旧:ExtractedWindows::primary(Option<Entity> 字段) // 新: fn my_system(primary: Query<&ExtractedWindow, With<PrimaryWindow>>) { for window in &primary { /* ... */ } }

4. 判断主窗口实体:旧代码若用windows.primary == Some(entity)比较,现在等价写法是对 Render World 实体查询Has<PrimaryWindow>

仓库内既有的用法参考

当前代码库中已有若干按新模式访问窗口数据的示例,可以直接参照:

  • bevy_render/src/camera.rs:extracted_swap_chains: Query<(MainEntity, &ExtractedWindow)>,用于解析相机交换链格式;
  • bevy_render/src/view/mod.rs:视图准备阶段查询(MainEntity, &ExtractedWindow)
  • bevy_render/src/view/window/screenshot.rs:截图系统通过(MainEntity, &'s ExtractedWindow)找到目标窗口的交换链;
  • bevy_core_pipeline/src/schedule.rs:core pipeline 每帧以SystemState<Query<(MainEntity, &ExtractedWindow)>>维护窗口状态。

这些查询普遍搭配MainEntity使用——即把 Render World 实体映射回 Main World 窗口实体,这也是替换旧windows.by_entity(entity)风格 API 时的标准做法。

小结

本次迁移的本质是把 Render World 的窗口数据从“资源容器”重构为“实体上的组件”:ExtractedWindow承载尺寸、present mode、交换链纹理等每帧数据,SurfaceData承载 wgpu surface 与配置,PrimaryWindow标签组件取代primary字段。替换工作集中在资源句柄改组件查询这一处,且仓库内bevy_renderbevy_core_pipeline的实现提供了完整的查询范式;迁移完成后,你的自定义渲染逻辑即可像访问其他渲染组件一样访问窗口数据。

(参考:迁移文档、窗口渲染模块)

【免费下载链接】bevyA refreshingly simple>项目地址: https://gitcode.com/GitHub_Trending/be/bevy

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询