Skip to content

MGFX 内部架构

这页记录 MGFX 当前的维护边界。Public API 细节看 API 总览详细 API 参考;已移除的批处理原型看 已移除的批处理设计

分层方向

MGFX 是底层 immediate-style renderer。除了文本记录会延迟到 frame 末尾统一 flush 以外,public draw call 应该按调用顺序直接绘制。调用方应按 immediate draw 的方式思考:先画底层 shape/image,再发出应该覆盖在上面的 text。

MGFX core 负责:

  • frame scope:StartPanelStartScreenEndPanelEndScreen
  • primitive 和 widget 绘制
  • canonical visual style record:fill、stroke、radius、pattern、glow、image、progress、text effect 等
  • public render slot 的 target capability metadata
  • shader/material、text command replay、clip 和 fallback 实现细节

未来如果有更上层 UI layer,它可以负责 node、layout、interaction state、focus、scrolling、hit-testing、transition 和 component style。那一层可以内部使用 MGFX,但 MGFX 不应该反向依赖它。

Transform 边界

MGFX 拥有 draw-phase visual transform stack。public surface 是支持的 Ex 调用上的 style.transform,以及用于复合 immediate 绘制的 PushTransform / PopTransform

这是 renderer 能力,不是 layout 能力。不要增加 ProjectedRoundedBoxExProjectedRingProjectedSector 这类 primitive-specific API。能通过 MGFX textured quad 或 transform-aware polygon 绘制的 primitive,应消费同一个 transform stack。

实现上,MGFX 会变换提交的 geometry,并在 projected/perspective-like transform 下细分 textured quad。现有 pixel shader 因此继续收到相同的 local UV space,用于 gradient、mask、ring 和 backdrop pass。强透视效果应提高 steps,避免 quad faceting 可见。

支持的 public forms:

  • CSS-like record:originperspectiverotateXrotateYrotatescaletranslateskewXskewYsteps
  • intent helper:PointerTilt(x, y, spec),用于 pointer-driven 2.5D UI motion
  • expert escape hatch:ProjectedQuad({tl, tr, br, bl, steps})
  • point helper:TransformPointUntransformPoint

Transform 只影响视觉,不改变 layout、input hit testing、text flow 或矩形 scissor clip 坐标。需要 transformed hit testing 的 UI code 必须自己拥有这条策略。UntransformPoint 只用于视觉对齐场景,例如让鼠标追踪径向光点在 transform 后仍对齐。

Text 暂不属于这个 transform contract。文本 renderer 使用 deferred glyph/atlas composer,文本 transform 应作为 text composer feature 或未来 UI node feature 设计,不能半截接入 shape geometry。

Mask 边界

Mask 是每个 primitive 自己的 shader coverage,不是全局 renderer state。

  • rounded、capsule、circle、chamfer mask 使用 screen-pixel SDF coverage
  • texture mask 采样调用方提供的 mask texture channel
  • 传给 mask shader 的 SHAPE_SIZE 始终是最终 quad 的屏幕像素尺寸,因此 UI 缩放后边缘抗锯齿仍是 1px
  • mask kind 在 Lua 侧白名单化后才能进入 shader

MGFX 不用 stencil 模拟 shape mask。PushClip / PopClip 只属于矩形 scissor stack,用于 panel clipping 和 ordering barrier;MGFX.Mask + callback-only MGFX.Clip 则通过绘制前/后 framebuffer 快照与连续 SDF/coverage raster 合成实现真正的抗锯齿边缘。

Circle/Capsule/Rounded/Chamfer preset 直接走 composite shader;自定义 painter 把 MGFX coverage command 栅格化到延迟分配的全屏 RT,并按 content revision、目标/device extent 与亚像素 phase 缓存。整数像素平移复用 raster。当前 backend 拒绝 transform mapping,并把嵌套限制为四层以约束常驻 RT。

MGFX 自己 Push 的 render target、camera、model matrix、scissor 和 override 都必须在受保护作用域中对称 Pop/恢复。GMod 没有读取既有 blend/alpha-write override descriptor 的 getter,因此调用方自己持有的 override scope 不属于 Clip contract。

参数上传边界

热路径 shape shader 使用 $viewprojmat / pixel shader register c11 中的 MGFXExtraParams 作为主 16-float 参数页。Lua call site 应使用共享 matrix upload helper,不要发很多单独 SetFloat

$invviewprojmat / pixel shader register c15 中的 MGFXAuxParams 是辅助 16-float 参数页,给 fused shader、polygon 顶点、text atlas 和额外 effect 参数使用。能放进 matrix 页的参数不要占用 $c0..$c3;不要用 $c8 之类临时寄存器,它们可能能编译但在 GMod runtime material 中读到 0 或未定义值。

Pattern 是 shader-space paint field。UI code 不应该把大面积 stripe、smoke 或 scanline 背景拆成大量 LineEx 调用。如果效果能表示成 primitive shader 数学,就应该在底层 pattern path 实现。

模块边界

mgfx/init.lua 是 Plain GLua 的公共 client entry,返回独立 API 表。cl_mgfx.lua 只是内部 composition root:它负责连接模块、共享 frame/command state,并把明确 helper table 传给 feature module。它不应写入公共全局表,也不应默认安装命令、hook、诊断 cvar 或示例。

cl_mgfx_materials.lua 负责 shaderpack mount、render target、material creation 和 shader status。

cl_mgfx_style.lua 负责 public style normalization:

  • fill 和 gradient record
  • pattern constructor
  • radius 和 stroke normalization
  • color helper
  • glow softness 到 shader falloff 的转换

这些 normalization 应停在 public API 边界。进入具体 renderer 后,内部函数应消费 prepared fill/stroke/effect scalar 参数,而不是继续传递 style 表或构造临时 spec 表。这样代码链路更短,也避免每帧在 shadowRawouterGlowRawinnerGlowRawbackdropStylepatternStylefillFromStylefillVisible 上重复付费。

推荐链路:

text
MGFX.RoundedBoxEx(..., style)
  -> API 边界解析一次
  -> scalar radius/fill/stroke/effect 参数
  -> shader/fallback draw

避免链路:

text
widget helper -> 临时 style table -> shape helper -> 再次 normalization -> draw

cl_mgfx_capabilities.lua 负责 target capability matrix 和 paint-slot normalization。它是 public style record 与 primitive family 之间的边界。Capability entry 必须描述已经实现的渲染行为,而不是愿望清单。

cl_mgfx_geometry.lua 负责纯底层绘制和图像几何 helper:

  • textured quad draw
  • created material 的 DrawTexturedRectUV half-pixel correction
  • image tint/crop/fit/radius helper
  • textured circle poly fallback

cl_mgfx_frame_geometry.lua 负责 frame-space geometry helper,用于 frame flushing 和 scissor restoration。Multi-stop gradient 不再拆成 geometry segment,shader path 直接采样共享 gradient LUT。

cl_mgfx_commands.lua 是 queued text/clip command 的 canonical reader boundary。Raw array command layout 不应泄漏到 frame、primitive 或 widget module。只要 command record 还不是完全 named table,这个模块就是唯一允许读取数字 slot 的地方。

cl_mgfx_frame.lua 负责 text/clip command capture、clip stack operation 和 frame flushing。Shape command 不在这里排队,仍保持 immediate path。

cl_mgfx_roundrect.lua 负责 rounded-box、circle、capsule public API,以及它们的 immediate fill、stroke、pattern、inner glow、outer glow 和 fallback pass。

cl_mgfx_primitives.lua 负责 chamfer box、line 和 convex poly。Backdrop blur 是 style.backdrop 的 shape/image effect,不是独立 public primitive。

Widget family 按职责拆分:

  • cl_mgfx_widgets_bars.lua:progress 和 segment bar
  • cl_mgfx_widgets_rings.lua:ring、arc、sector
  • cl_mgfx_widgets_images.lua:image、icon 和 image mask
  • cl_mgfx_widgets_text.lua:text draw-call bridge
  • cl_mgfx_widgets.lua:只做 thin aggregator

Public family 遵循 Name(...) / NameEx(...) 分层:短 positional hot-path call,以及高级 table-based call。

cl_mgfx_text.lua 负责文本路由:普通文本走 native drawing,shader text effects 走 whole-run native-raster composer。

Demo 文件不是 library internals。Demo 应展示 public API 和 telemetry,不要直接调用 private helper。

运行时入口分层

Plain GLuaLux
核心库include("mgfx/init.lua")@lux/mgfx
全局兼容mgfx/global.lua@lux/mgfx/global
命令与诊断mgfx/devtools.lua@lux/mgfx/devtools
示例mgfx/examples.lua显式导入 demo package

核心导入不得隐式安装公共全局、控制台命令、hook、诊断 cvar 或示例。两种实现的核心 入口应提供相同渲染行为,可选层由实际集成方明确安装。

维护规则

Public arguments 应尽早规范化一次。Immediate renderer 和 fallback renderer 应消费同一份 canonical style data。

模块依赖必须明确。Composition root 可以在 legacy module 迁移期间传大 context table;新模块应暴露一个 constructor,并返回小 helper table。

不要通过 inline split file 来掩盖加载问题。新增 Plain GLua client module 时,应把它加入 mgfx/distribute.lua 的核心文件清单,确保服务端会发送给客户端;再由 cl_mgfx.luainit.lua 建立的内部加载目标中 include。不要通过公共 _G.MGFX 传递模块 owner。

lua
local mgfx = include("mgfx/init.lua")

Gamemode code 只 include mgfx/init.lua,并保存其返回值;不要直接 include cl_mgfx*.luamgfx/global.luamgfx/devtools.luamgfx/examples.lua 分别是全局 兼容、开发工具和示例适配层,不属于核心入口。

Immediate shader path 是主 renderer path,不是 batch scheduler 的 fallback。已移除的 batch prototype 留在文档中作为经验记录,不保留 runtime hook。

除非明确要求,不新增兼容 shim。当前稳定化阶段允许 public API 为更清晰的替代方案破坏兼容,但必须同步文档。

维护清单

状态项目
完成将 round-rect/effect immediate rendering 从 cl_mgfx.lua 拆到 cl_mgfx_roundrect.lua
完成将 scissor 和 bbox helper 拆到 cl_mgfx_frame_geometry.lua,移除旧 gradient-segment clipping path。
完成将 widget family 拆成 bars、rings/arcs/sectors、images/masks 和 text bridge。
完成增加 cl_mgfx_commands.lua 作为 text/clip replay 的 command reader boundary。
待办将 queued command 从 positional array 转成 named record。现在已经隔离在 cl_mgfx_commands.lua 后面,可以渐进完成。
待办继续缩小 cl_mgfx.lua 传给模块的共享 context table;新模块优先窄 constructor argument 和返回 helper table。
完成增加 cross-cutting draw-phase transform stack 和 style.transform,避免创建 ProjectedXXX API 家族。
完成从 runtime 移除 shape/data-texture batch prototype。代表性 GMod UI profiling 显示它是净负收益。
完成保持每个 runtime Lua 文件低于 2000 行。当前最大文件仍是 cl_mgfx_text.lua、demo 和较大的 renderer module。