Files
envi-code/SourceCode/Code2026/inc/AecDbX3dPart.h
T
company-desktop facf14699b fix: AecDbX3dPart 记住"已尝试建过",避免 Hide 构件被反复重建
现象:开图时 X3dPart 的 EnsureCreated 命中率只有 10.1%,1914 次重建里
721 次产出 NULL(38%),单个对象最多被重建 544 次。

根因:EnsureCreated 的命中条件是 !m_bNeedCreate && m_pSolid != NULL,
而 Rebuild() 里对 Hide 构件是"故意不建几何、直接 return true"——
m_pSolid 按设计就是 NULL,于是每次访问都重新走一遍 Rebuild,永远不可能命中。
子项全为空的构件同理。

修复:
- 新增 m_bCreated 标记"已经尝试建过"(无论是否产出实体)
- EnsureCreated 命中条件改为 !m_bNeedCreate && (m_bCreated || m_pSolid != NULL)
- Rebuild 入口置 m_bCreated = true;所有设 m_bNeedCreate = true 的 setter
  同时置 m_bCreated = false,保证参数变化后仍会重建
- 重建后仍无实体时打一条日志(限前 5 条,带 m_sName),方便定位是哪些构件

实测(big1.dwg / R230 Debug):
  X3dPart 命中率      10.1% -> 41.4%
  重建次数            1914  -> 1248(-666)
  rebuild_null         721  -> 55
  单对象最多重建       544  -> <=3
  开图墙钟             93 s -> 93 s(无变化)

注意:这 666 次空转没有带来可测的提速,因为 Rebuild 里 Hide 的早退分支在
布尔运算之前,每次只有几微秒。所以这是正确性/整洁度修复,不是性能修复;
但消除了"个别构件被重试几百次"这种病态行为,也暴露了"哪些构件建不出实体"。

顺带新增诊断(Debug + AEC_PERF_COUNTER=1 才生效,Release 无开销):
- SectionPerf::NoteEnsure():按 tag 统计"延迟建几何"的命中/重建,
  并给出对象维度直方图与重建次数最高的对象
- AecDbX3dPart::Rebuild / AcDbX3dSolid::EnsureCreated / AecDbGuardBar::Draw3d 打点
- tools/run_acad_perf.ps1 增加 -CpuTrace:用 xperf 采 CPU 采样并 merge 成 etl,
  可直接喂 tools/perf_diagsession.py(VS 的 VSDiagnostics 在这台机器上会卡住,
  wpr -stop 有 COM 线程模式 bug,故用 xperf)

验证:R230 Debug 编译通过并部署,自动开图取数;截面缓存与回写仍为 0/正常。
2026-10-28 22:51:01 +08:00

152 lines
4.5 KiB
C++

#pragma once
#ifdef EARTHWORM_EXPORT
#define DLLIMPEXP __declspec(dllexport)
#else
#define DLLIMPEXP __declspec(dllimport)
#endif
#ifndef MAX_AXISROTATS
#define MAX_AXISROTATS 16
#endif
#include "AcDbX3dSolid.h"
#include "AecDbObjXDataMap.h"
class AcDbX3dSolid;
class DLLIMPEXP AecDbX3dPart
{
public:
AecDbX3dPart(void);
AecDbX3dPart(const AecDbX3dPart& pPart);
~AecDbX3dPart(void);
static Adesk::UInt32 kCurrentVersionNumber;
void SetName(LPCTSTR pszName);
void GetName(CString& sName) const;
LPCTSTR GetGUID() const;
void ReCreateGUID();
//添加基本单元
void AddX3dSolid(AcDbX3dSolid* pXSolid,AcDb::BoolOperType bType = AcDb::kBoolUnite,bool bForHouseSub = false);
void RemoveAt(int nIndex,AcDb::BoolOperType bType = AcDb::kBoolUnite,bool bForHouseSub = false );
void RemoveAll3dSolidRec();
int GetX3dSolidCount(AcDb::BoolOperType bType = AcDb::kBoolUnite,bool bForHouseSub = false) const;
AcDbX3dSolid* GetX3dSolid(int nIndex,AcDb::BoolOperType bType = AcDb::kBoolUnite) const;
void SetOrgPoint(const AcGePoint3d& ptOrg);
AcGePoint3d GetOrgPoint(bool bByTransform = false) const;
bool SetAxis(int nIndex,const AcGeVector3d& vtAxis);
bool GetAxis(int nIndex,AcGeVector3d& vtAxis) const;
void SetHide(bool bHide);
void SetHideShow( );
bool IsHide() const;
void PeelOffTheSkin(int nPos);
bool IsPeelOffTheSkin() const;
void SetActive(bool bActive);
//移动
void AddMove(const AcGeVector3d& vtMove);
void AddMove(const AcGeMatrix3d& xMirror);
AcGeVector3d GetMove() const;
void ResetMove();
//旋转
void AddRotate(int nAxisIndex,double dRot);
double GetRotate(int nAxisIndex) const;
void ResetRotate(int nAxisIndex);
void Transformby(AcGePoint3d& pt) const;
void Transformby(AcGeVector3d& vt) const;
void SetBoolOperFor(int nOper);
int GetBoolOperFor() const;
bool Create();
bool EnsureCreated() const;
AcDb3dSolid* CreateDraw3dSolid() const;
int GetForHouseSubtract(AcDbVoidPtrArray& arSolidPtrs) const;
int GetActiveInf() const;
void SetActiveInf(int nIndex);
//开孔的接口提取
bool GetInterface(int nIndex,AecInterface& inf) const;
bool GetInterface(int nRow,int nCol,AecInterface& inf) const;
bool GetInterfaceCount(CPoint& ptRC) const;
bool SetActiveInf(const CPoint& ptRC);
bool GetActiveInf(CPoint& ptRC) const;
Acad::ErrorStatus Draw2d(AcGiDraw &dc) const;
Acad::ErrorStatus Draw3d(AcGiDraw &dc) const;
Acad::ErrorStatus DrawAxis(AcGiDraw &dc) const;
Acad::ErrorStatus DrawInf(AcGiDraw &dc) const;
bool GetSlicePlan(AcGePlane& plan) const;
// 专业数据
AecDbObjXDataMap* GetSpecialtyData();
const AecDbObjXDataMap* GetSpecialtyData() const;
Acad::ErrorStatus getOsnapPoints( AcDb::OsnapMode osnapMode,
INT_PTR gsSelectionMark,
const AcGePoint3d& pickPoint,
const AcGePoint3d& lastPoint,
const AcGeMatrix3d& viewXform,
AcGePoint3dArray& snapPoints,
AcDbIntArray& geomIds) const;
Acad::ErrorStatus dwgInFields (AcDbDwgFiler* pFiler);
Acad::ErrorStatus dwgOutFields(AcDbDwgFiler* pFiler) const;
Acad::ErrorStatus CopyFrom(AecDbX3dPart* pSrcPart);
void List() const;
SHJson::WValue& ToJson() const;
bool ByJson(const SHJson::WValue &v);
void setBimDatas(const SH_BIM_BLOCK &bims);
void getBimDatas(SH_BIM_BLOCK &bims);
protected:
bool Rebuild(); //无条件重建布尔结果
//序列化
CString m_sName;
CString m_sGUID;
Adesk::Int32 m_nBoolOper; //
AcDbVoidPtrArray m_arX3DSolidPtrsUnite;
AcDbVoidPtrArray m_arX3DSolidPtrsSubtr;
AcDbVoidPtrArray m_arX3DSolidPtrsSubtrForHouse;
//先旋转,后移动
AcGeVector3d m_vtMove; //总移动
AcGePoint3d m_ptOrg; //原点
AcGeVector3dArray m_arVtAxis; //轴向
double m_dAxisRotats[MAX_AXISROTATS];//最多10个轴向旋转
AecDbObjXDataMap m_mapXData; //BIM数据
//显示,临时
CPoint m_ptActiveInf; //开孔的活动接口索引行x列 不序列化
bool m_bActive; //活动状态
int m_nPeelOff; //是否需要剖开
AcDb3dSolid* m_pSolid; //原始solid元
mutable bool m_bNeedCreate; //几何待重建,首次使用时再算
// 是否已经"尝试建过"(无论是否产出实体)。用于区分两种情况:
// Hide 构件、或子项全是空 —— m_pSolid 按设计就是 NULL,但不能再反复重试;
// 参数变化时 m_bNeedCreate 会重新置 true,下次访问照常重建。
bool m_bCreated;
mutable AcDb3dSolid* m_pSolidDraw; //临时绘制solid元
mutable SHJson::WValue m_json;
};