MGFX 内部架构
这页记录 MGFX 当前的维护边界。Public API 细节看 API 总览 和 详细 API 参考;已移除的批处理原型看 已移除的批处理设计。
分层方向
MGFX 是底层 immediate-style renderer。除了文本记录会延迟到 frame 末尾统一 flush 以外,public draw call 应该按调用顺序直接绘制。调用方应按 immediate draw 的方式思考:先画底层 shape/image,再发出应该覆盖在上面的 text。
MGFX core 负责:
- frame scope:
StartPanel、StartScreen、EndPanel、EndScreen - 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 能力。不要增加 ProjectedRoundedBoxEx、ProjectedRing、ProjectedSector 这类 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:
origin、perspective、rotateX、rotateY、rotate、scale、translate、skewX、skewY、steps - intent helper:
PointerTilt(x, y, spec),用于 pointer-driven 2.5D UI motion - expert escape hatch:
ProjectedQuad({tl, tr, br, bl, steps}) - point helper:
TransformPoint和UntransformPoint
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 表。这样代码链路更短,也避免每帧在 shadowRaw、outerGlowRaw、innerGlowRaw、backdropStyle、patternStyle、fillFromStyle、fillVisible 上重复付费。
推荐链路:
MGFX.RoundedBoxEx(..., style)
-> API 边界解析一次
-> scalar radius/fill/stroke/effect 参数
-> shader/fallback draw避免链路:
widget helper -> 临时 style table -> shape helper -> 再次 normalization -> drawcl_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 的
DrawTexturedRectUVhalf-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 barcl_mgfx_widgets_rings.lua:ring、arc、sectorcl_mgfx_widgets_images.lua:image、icon 和 image maskcl_mgfx_widgets_text.lua:text draw-call bridgecl_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 GLua | Lux |
|---|---|---|
| 核心库 | 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.lua 在 init.lua 建立的内部加载目标中 include。不要通过公共 _G.MGFX 传递模块 owner。
local mgfx = include("mgfx/init.lua")Gamemode code 只 include mgfx/init.lua,并保存其返回值;不要直接 include cl_mgfx*.lua。mgfx/global.lua、mgfx/devtools.lua 和 mgfx/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。 |