Skip to content

Conversation

@StarryThrone
Copy link
Collaborator

@StarryThrone StarryThrone commented Dec 9, 2025

主要功能

  • 3D 变换支持:Layer 新增 matrix3D() 和 setMatrix3D() 接口,支持完整的 4x4 矩阵变换
  • TransformStyle 属性:新增 transformStyle() 属性,支持 Flat 和 Preserve3D 两种模式
  • 深度排序:实现 BSP 树算法进行 3D 多边形深度排序,正确处理相交面的渲染顺序
  • 3D 合成器:新增 Context3DCompositor 处理 3D 渲染上下文的最终合成,支持多边形分割和边缘抗锯齿

技术实现

  • 新增 BspTree 类实现二叉空间分割树,用于 3D 多边形深度排序
  • 新增 DrawPolygon3D 类表示 3D 渲染上下文中的绘制多边形
  • 新增 Quads3DDrawOp 和 QuadPerEdgeAA3DGeometryProcessor 支持带边缘 AA 的 3D 四边形绘制
  • 扩展 Matrix3D 类,增加 project()、getRow()、setRow() 等方法
  • 重构 Layer 绘制流程,支持 3D 渲染上下文的创建、传播和合成

# Conflicts:
#	include/tgfx/layers/Layer.h
#	src/gpu/DrawingManager.cpp
#	src/layers/Layer.cpp
#	test/baseline/version.json
…semi-transparent problem when the layer is too close to the observer.
…and uses the Canvas to calculate the cropping area.
const DrawArgs& args, Canvas* canvas, float alpha,
const LayerStyleSource* layerStyleSource = nullptr, const Layer* stopChild = nullptr,
const std::unordered_set<LayerStyleExtraSourceType>& styleExtraSourceTypes = {});
void drawByStarting3DContext(const DrawArgs& args, Canvas* canvas, float alpha,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

建议把Preserve3d流程直接写在drawOffscreen和drawDirectly里。目前看来,Preserve3d就是把child和content的绘制目标从2d变成3d空间。在drawOffscreen里,其实是把离屏的纹理画在3d空间,而drawDirectly里,是把内容和子项都一个个画在3d空间里

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Layer的3D空间看起来可以让parent直接提供,这样layer也不需要处理创建3d空间的行为,只需要处理有/无3d空间的绘制流程。
对于layer来说,如果child开了3d空间,如果args存在3d空间,那直接传给child就可以,如果args不存在3d空间,则创建一个3d空间给child

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

已经修改。

*/
Matrix3D calculate3DContextDepthMatrix();

bool canExtend3DContext() const;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

由于alpha、blendmode是由parent传下来的,因此没法在updateRenderBounds里直接判断。建议如果本身在3d空间内了,舍弃GroupAlpha和blendmode的行为,仍然执行drawDirectly。这样可以保证updateRenderBounds和draw的rect是一致的

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

已经修改。

if (canPreserve3D() || child->canPreserve3D()) {
// Child is inside a 3D rendering context.
childTransformer = RegionTransformer::MakeFromMatrix3D(childMatrix, transformer);
} else if (IsMatrix3DAffine(childMatrix)) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这个用child.bitfield就能判断了吧

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

已经修改。

newTransform.postConcat(baseTransform);
_stateStack.push({});
_stateStack.top().transform = newTransform;
_stateStack.top().antialiasing = antialiasing;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里组装好再push吧···

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

已经修改。

bounds.roundOut();
auto matrix = Matrix::MakeTrans(-bounds.x(), -bounds.y());
auto image = Image::MakeFrom(std::move(picture), FloatCeilToInt(bounds.width()),
FloatCeilToInt(bounds.height()), &matrix, std::move(colorSpace));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

roundOut就不用ceil了

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

已经修改。

* These features require projecting child layers into the current layer's local coordinate
* system, which is incompatible with 3D context preservation.
*/
TransformStyle transformStyle() const {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这个和CSS一样,加个bool值的preserve3D的开关不行吗?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

已经修改。

Copy link
Collaborator

@domchen domchen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

1. RegionTransformer.cpp:190 - 逻辑错误

outerMatrix3D->canCombineWith(outerMatrix3D) 是自己与自己比较,永远返回 true(当 combineMode 都是 Combinable 时)。

应该检查当前 transformer 是否可以与 outer 合并,建议改为:

if (combineMode == Matrix3DCombineMode::Combinable &&
    outerMatrix3D->combineMode == Matrix3DCombineMode::Combinable) {

2. DrawPolygon3D.cpp:209 - 未使用的方法

isFacingPositiveZ() 方法已定义但在代码中未被使用,是否需要保留?如果是为未来扩展预留,建议添加注释说明。

3. 版权年份不一致

新增文件中部分使用 2025,部分使用 2026(如 Matrix3DUtils.h/cpp)。根据项目规范,新增文件应使用当前年份。

4. DrawPolygon3D.cpp:43 - 函数语义可优化

ProjectPoint 函数名表示投影点,但实际上 mapPoint 返回的是 Vec3,这里只取了 x, y 分量。建议改名为 ProjectTo2D 或添加注释说明只取 x, y 分量的原因。

Copy link
Collaborator

@domchen domchen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

代码简化建议

1. 重复的 styleSourceTypes 初始化逻辑

drawByStarting3DContext (Layer.cpp:1874-1875) 和 createChildArgs (Layer.cpp:1919-1920) 中有完全相同的代码:

contextArgs.styleSourceTypes = {LayerStyleExtraSourceType::None,
                                LayerStyleExtraSourceType::Contour};

建议提取为常量或辅助函数:

// 在文件顶部定义
static const std::vector<LayerStyleExtraSourceType> kStyleSourceTypesFor3DContext = {
    LayerStyleExtraSourceType::None, LayerStyleExtraSourceType::Contour};

2. DrawArgs 的 styleSourceTypes 默认值可考虑移除

DrawArgs.h:55-57 定义了 styleSourceTypes 的默认值,但每次进入 3D context 时都会重新设置。可以考虑只在需要时才设置,减少冗余赋值。

3. drawChildren 中的 clipChildScrollRectHandler lambda 可以提取

Layer.cpp:1828-1843 的 lambda 只在两处调用(canvas 和 backgroundCanvas),可以提取为私有成员函数,减少 drawChildren 的复杂度:

void Layer::clipScrollRect(Canvas* canvas, const Matrix3D& transform, bool isAffine) {
  if (!_scrollRect) return;
  if (isAffine) {
    canvas->clipRect(*_scrollRect);
  } else {
    Path path;
    path.addRect(*_scrollRect);
    path.transform3D(transform);
    canvas->clipPath(path);
  }
}

4. BspTree::traverseNode 中的重复代码

BspTree.h:78-107 中 if/else 两个分支结构相似,只是遍历顺序不同。可以提取公共部分:

template <typename Action>
void traverseNode(Action& action, const BspNode* node) const {
  BspNode* first = node->data->isFacingPositiveZ() ? node->backChild.get() : node->frontChild.get();
  BspNode* second = node->data->isFacingPositiveZ() ? node->frontChild.get() : node->backChild.get();
  auto& firstCoplanars = node->data->isFacingPositiveZ() ? node->coplanarsBack : node->coplanarsFront;
  auto& secondCoplanars = node->data->isFacingPositiveZ() ? node->coplanarsFront : node->coplanarsBack;
  
  if (first) traverseNode(action, first);
  for (const auto& p : firstCoplanars) action(p.get());
  action(node->data.get());
  for (const auto& p : secondCoplanars) action(p.get());
  if (second) traverseNode(action, second);
}

5. Context3DCompositor::drawQuads 可以简化

Context3DCompositor.cpp:169-177 中对 subQuads 是否为空的处理可以合并:

auto quadsToProcess = subQuads.empty() 
    ? std::vector<const QuadCW*>{nullptr}  // nullptr 表示使用原始 rect
    : /* convert subQuads to pointers */;

for (const auto* subQuad : quadsToProcess) {
  auto [quad, aaFlags] = GetQuadAndAAFlags(originalRect, aaType, subQuad);
  quadRecords.push_back(allocator->make<QuadRecord>(quad, aaFlags, vertexColor));
}

6. Render3DContext 中的 invScale 计算重复

Render3DContext.cpp:55 和 :86 都计算了 1.0f / _contentScale。可以在构造函数中预计算并存储为成员变量 _invContentScale

7. canPreserve3D() 调用频繁

canPreserve3D() 在 Layer.cpp 中被调用了 18 次。由于其依赖条件(_transformStyle, _filters, _layerStyles, mask, scrollRect)都会触发 invalidateTransform(),可以考虑缓存结果在 bitFields 中,类似 matrix3DIsAffine 的处理方式。

@domchen domchen merged commit 484bda4 into main Jan 9, 2026
9 checks passed
@domchen domchen deleted the feature/jasonrjchen_3D_context branch January 9, 2026 11:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants