[返回博客](/cn/blog) # 可视化：使用部分视图流式传输

Egor Slupko 二月 25, 2022 [\#Visualize](/cn/blog/tag/visualize) [\#getting started](/cn/blog/tag/getting-started)

<a class="a2a_button_facebook"></a><a class="a2a_button_twitter"></a><a class="a2a_button_linkedin"></a>

#### 概述

从 Visualize 23.1 版本开始，VSFX 文件可以在部分视图模式下进行流式传输，以在基于流的解决方案中提供类似于桌面的部分查看技术。这可用于不同的情况，例如：

- 尽快渲染一个信息更丰富（与线性流式传输相比）的不完整首帧（与常规加载相比）。
- 渲染由于RAM限制而无法完全渲染的大文件。

这项技术的另一个优点是它不会给服务器带来沉重负担；使用偏移量和长度读取二进制文件的部分内容不需要服务器上的额外服务。

##### 部分视图

部分视图技术允许您仅从文件中加载服务对象，并将所有实体和几何图形保留在文件中。每次 update() 调用都会生成屏幕上可见的实体列表，并且只有这些实体会从文件中加载。在将部分视图应用于文件之前，保存的数据库必须包含部分索引——一个 实体标识符 实体范围&gt; 对列表，用于在不实际加载实体和矢量化的情况下确定指定视图中哪些实体可见。部分视图还支持卸载在指定视图中可见但变为不可见（屏幕外）的实体。部分视图的另一个特点是支持限制管理器，当达到指定的内存限制时，它会阻止实体加载。

##### 线性流式传输

常规加载和部分文件加载都需要访问整个文件。然而，在某些情况下，这是一个不便的限制。例如，云解决方案可能仅通过将整个文件保存在内存中来提供对其的访问，因为将整个文件发送到服务器所需的时间以及它占用有限内存的方式。

VSFX 文件支持流式传输，这允许您使用原始文件的小部分来加载文件。它还提供了不完整渲染流式文件的能力。在 VSFX 文件可以流式传输之前，它必须转换为流式兼容版本，因为常规的 writeVSFX() 方法会创建流式不兼容版本。然而，常规的 readVSFX() 方法适用于流式兼容和流式不兼容版本。有关详细信息，请参阅“使用 Visualize Streaming”。

#### 部分视图流式传输

部分视图流式传输是部分视图和线性流式传输技术的结合。因此，VSFX 文件必须包含部分索引并转换为流式兼容版本。

主要步骤如下：

1. 线性流式传输开始。
2. 当所有服务数据解析完毕后，线性流式传输停止。
3. 每次 update() 调用都会生成屏幕上可见的实体列表。
4. 数据库接收器处理此列表并生成包含原始文件部分偏移量和长度的记录列表，并将这些记录发送到服务器端。
5. 服务器处理这些记录并将请求的文件部分发送给接收器。
6. 数据库接收器使用流式传输技术解析此数据，因此数据（类似于线性流式传输模式下的文件）可以作为小块列表而不是整个二进制数据数组进行处理。

##### OdTvDatabaseReceiverReactor

与线性流式传输相比，部分视图流式传输需要在客户端实现 OdTvDatabaseReceiverReactor 接口。

此反应器接口包含 OdTvRequestRecord 结构的定义——一个描述数据库接收器请求的文件部分的结构。当接收器解析所有服务部分后，它会使用以下反应器方法通知服务器：

```
virtual bool onServicePartReceived( bool bHasPartialIndex ) = 0;
```

bHasPartialIndex 参数指定数据库是否具有部分索引。此回调被调用后，常规的 OdTvDatabaseReceiver::doReceive 调用不会解析数据；它返回 true 并将数据库切换到 kReceiver\_RequestMode 状态。这意味着接收器使用 OdTvDatabaseReceiver::doReceiveResponce() 方法生成请求并解析响应。

主反应器方法是：

```
virtual void onRequest( OdUInt32 requestID, OdUInt32 nRecCount, const OdTvRequestRecord* pRecords ) = 0;
```

当接收器处理完部分视图列表并生成文件请求时，会调用此回调。requestID 参数定义请求的标识符，nRecCount 是 pRecords 数组中请求记录的数量。

由于 VSFX 文件由压缩页面组成，因此每个请求记录描述的是单个页面或几个文件页面，而不是一个实体。多个实体可以位于同一页面上，因此它们都可以通过单个记录请求。另一方面，接收器仅提供快速记录-请求组合（出于性能原因），因此接收器在同一请求期间也可能为不同的实体多次检索相同的二进制数据。这就是为什么记录长度的总和可能大于文件本身，并且记录数量不提供有关请求实体数量的任何信息的原因。

收到回调后，服务器读取文件中请求的部分，并使用以下方法将其发送给接收器：

```
virtual bool doReceiveResponce( OdUInt32 requestID, const OdUInt8* pData, OdUInt32 nSize, OdTvResult* rc = NULL ) = 0;
```

其中：

- requestID - 回调中请求的标识符。
- pData - 读取文件的一部分。
- nSize - 部分的大小。

注意：doReceiveResponce() 方法支持流式传输技术，因此可以为同一个 requestID 多次调用，以线性顺序提供请求数据的部分。

当请求完全解析后，接收器会调用 doReceiveResponce() 方法：

```
virtual bool doReceiveResponce( OdUInt32 requestID, const OdUInt8* pData, OdUInt32 nSize, OdTvResult* rc = NULL ) = 0;
```

但是，如果请求未完全解析，并且视图设置发生更改并发生新的 update() 调用，则接收器会生成一个新请求，但首先接收器需要使用以下回调取消当前请求：

```
virtual void onRequestAborted( OdUInt32 requestID ) = 0;
```

##### 工作流程

客户端代码应创建 OdTvDatabaseReceiver 类的一个实例：

```
OdTvFactoryId factId = odTvGetFactory();
OdTvDatabaseReceiverPtr pDbReceiver = factId.createDatabaseReceiver();
```

然后，使用 OdTvDatabaseReceiverReactor 接口的客户端实现将接收器切换到部分模式：

```
m_pDbReceiver->enablePartialMode( true, pReactor );
```

线性流式传输开始，直到 doReceive() 返回 true 且接收器状态变为 kReceiver\_RequestMode：

```
bool bDone = pDbReceiver->doReceive( pData, nDataSize);
OdTvDatabaseReceiver::DatabaseReceivingState state = pDbReceiver->state();
if( bDone && state != OdTvDatabaseReceiver::kReceiver_RequestMode ) throw OD_T(“Internal error” );
if( bDone ) break; //stop linear streaming, start partial-viewing streaming
```

发生这种情况时，OdTvDatabaseReceiverReactor 的客户端实现也会收到 onServicePartReceived() 通知。

服务器等待，当 OdTvDatabaseReceiverReactor::onRequest() 发生时，服务器会发送请求的响应：

```
if( !m_pDbReceiver->doReceiveResponce(requestID, binData.asArrayPtr(), binData.size() ) )
  throw OD_T(“Response not parsed” );
```

#### 附加管理

部分视图流式传输支持附加功能，这些功能使用 OdTvDatabaseReceiverManagementOptions 类和 OdTvDatabaseReceiver 方法进行管理：

```
virtual void setManagementOptions( const OdTvDatabaseReceiverManagementOptions& options ) = 0;
```

##### 限制管理器

部分视图流式传输的限制管理器在以下情况下很有用：

- 与常规部分视图类似，接收器在加载每个请求的实体之前使用；如果达到限制，接收器将不会加载后续实体。
- 接收器在生成请求时可以使用限制管理器：它使用文件中实体长度估算实体加载所需的内存。如果限制管理器指示估算值超出限制，则接收器不会在请求中包含这些实体。为此，接收器使用 OdTvDatabaseReceiverManagementOptions::memoryEstimationMultiplier() 方法。但是，此乘数的实际值纯粹是启发式的，并且对于不同的文件可能会有所不同。本主题后面描述的接收器统计信息可以帮助定义此值。

可以使用以下工厂方法指定 OdTvLimitManager 的实例：

```
void setLimitManager( OdTvLimitManager* pManager );
```

##### 卸载对象

与常规部分视图类似，部分视图流式传输支持卸载已加载但当前不在屏幕上的对象。默认情况下，如果启用卸载，所有屏幕外对象都将卸载。但是，由于此操作会降低渲染性能，因此可以指定调度程序限制。在这种情况下，接收器在每次 update() 调用期间用于卸载的时间不会超过指定的 unloadTimeLimit 参数。如果它没有时间卸载所有对象，则剩余对象将在下一次 update() 调用期间卸载。

对象卸载由以下 OdTvDatabaseReceiverManagementOptions 方法管理：

```
void setObjectsUnloading( bool bUnload, OdUInt32 unloadTimeLimit = 0 );
```

其中：

- bUnload — 启用卸载的标志。
- unloadTimeLimit — 卸载的时间限制（0表示“无限制”）。

##### 生成统计数据

可以使用以下 OdTvDatabaseReceiverManagementOptions 方法管理统计数据的生成：

```
void setEnableStatistic( bool bEnable, bool bOnlyRequestedItems = true, bool bCountUnloadObjects = false );
```

其中：

- bEnable — 启用统计数据收集。
- bOnlyRequestedItems — 指定只收集请求的统计数据（不包括来自线性流工作流的服务对象的统计数据）。
- bCountUnloadObjects — 指定应将已卸载的对象包含在统计数据中。

如果启用了统计数据，可以使用以下 OdTvDatabaseReceiver 方法从 OdTvDatabaseReceiver::StatisticTool 获取结果：

```
virtual const StatisticTool* statisticTool() const = 0;
```

统计数据包括已解析和已卸载对象的数量和长度（原始、未压缩）信息。

如果客户端支持计算已消耗内存，它可以在不同的迭代中使用此工具来估算 OdTvDatabaseReceiverManagementOptions::memoryEstimationMultiplier() 的自定义值。

## 今天就开始行动

**免费试用 ODA 软件 60 天。  
无风险，无需信用卡。**

[免费试用](https://www.opendesign.com/cn/free-trial)
