[INTERNAL] Article pending updates due to changes in the Project
[DOC-413] Archive article due to several changes by RRT on the Project - Jira (configura.com)
Introduction
CET 12.5 introduced a new feature called Shape Merging, a process in the graphics layer that drastically improves performance by merging several graphics into a single big shape. The system is enabled in CET when a drawing contains more than 10,000 snappers or the frame rate FPS is below an average of 20 frames per second. However, it is possible to customize the merger by modifying how it groups snappers together. This article aims to explain how to implement such rules and how to debug the merger in action.
Debugging tools
In order to ease the development and troubleshooting process, it is possible to turn on several debugging tools available in the RED Merge Debug Dialog. Open the dialog by clicking the Merge Debug Dialog in the Walls toolbox tab (see image). The dialog has the following options:
- Active/Inactive Notification - Shows the status of the merger.
- Install merger - Install the shape merger system into the drawing. Unchecking this option disables the merger system entirely.
- Merge active - When checked, the merger is allowed to keep merging graphics in the drawing. Unchecking it pauses the merger.
- Trace merge - When checked, the merger will output debug info into the emacs output during its operations.
- Fast scheduling - When checked, the merger will merge snappers faster. Good for development/debugging but might cause a lot of shape merging overhead when enabled for a regular user.
- Debug materials - Overrides the color of merged graphics. This makes it clearer which snappers are merged and which snappers it is merged with.
In addition to these options, you can set the size of the merge group with the number option. This affects how many snappers are included in each merged shape of the default merge policy.
The disband all, disbands all merges done in the drawing. Useful when you've made changes to the merger logic and want it to start over with the merging process. The ->bg button also disbands all merges in the drawing, but in addition, moves all snappers from the edit-repository to the bg-repository in the drawing, making them available for the merger to operate on. The stats button prints information about the repositories in the drawing, which shows how many shapes, meshes, vertices, etc., they contain.
Merger customization
Customizing the merger consists of three steps:
- Creating a custom Merge Policy.
- Creating a custom Merge Collector class that the Merge Policy utilizes.
- Registering the custom Merge Policy in the Merge Scheduler.
Creating a custom Merge Policy
The Merge Policy is responsible for deciding the snapper groups and sending groups to the scheduler once all their graphics are ready. The policy gets invoked once every flush and may listen to BSP events from the space it's attached to.
Create a class extending the cm.core.red3D.REDMergePolicy class. This class has the following three abstract methods that need to be implemented:
public void step(double start)
Called once every flush.
public void bspEvent(bspOp op, CoreObject z)
The BSP operated on z by performing op.
public void dbg_fast_scheduling(bool on)
The fast scheduling option was (un)checked in the debug menu. This method can make the policy change behavior based on that checkbox.
The merge policy should create Merge Collector instances that each correspond to a specific group of snappers in the drawing. It should also, in some custom manner, identify when any collector is ready to merge (i.e. when their graphics have been generated) and finally send the collector to the Merge Scheduler. A Merge Collector is sent to the Merge Scheduler in the following fashion: mergePolicy.scheduler << mergeCollector;
Creating a custom Merge Collector
The Merge Collector is responsible for collecting all snappers (and singularities) it is associated with.
Create a class extending the cm.core.red3D.REDMergeCollector and override the following method:public void collect(Space space, Snapper{} snappers, Singularity{} sings) { ... }
The collector should add all snappers and singularities that should be merged to the snappers and sings parameters, respectively.
The constructor for the Merge Collector requires a string representing its id, and a number indicating the collectors priority. The priority is used to ensure that some collectors are handled before others, where the merger will process collectors in ascending priority order. The default collectors have a priority of 99.public constructor(str id, int priority) { ... }
Registering the Merge Policy
In order to activate your merge policy and make it operate on your drawing, you need to register it in the merge scheduler. To do that, you need to create a function that adds the policy to the scheduler. You can use the following example function:
private void reinstallMergePolicy(REDMergeScheduler scheduler) { if (scheduler) scheduler << YourMergePolicyClass(); }
Then register the function using the hook registration function:registerInitMergerHooks(function reinstallMergePolicy);
Example
The following code snippet contains a custom merge policy that gathers only scarecrows and mergers them together.
use cm.core; use cm.core.red3D; use cm.core.bsp; use cm.std.accessories; public class ScarecrowMergePolicy extends REDMergePolicy { private Snapper{} collectedScarecrows(); private Snapper{} freeScarecrows(); public constructor() { for (Scarecrow z in mainSpace.snappers) freeScarecrows << z; } public void bspEvent(bspOp op, CoreObject z) { if (scheduler.dbg_trace) pln(#op; #z); if (z as Snapper) if (z !in collectedScarecrows) { if (scheduler.dbg_trace) pln("add"; z; freeScarecrows); freeScarecrows << z; } } public void step(double start) { if (freeScarecrows.count >= 5) { scheduler << ScarecrowMergeCollector(freeScarecrows.copy(shallow=true)); collectedScarecrows += freeScarecrows; freeScarecrows.clear(); } } public void dbg_fast_scheduling(bool on) { } } public class ScarecrowMergeCollector extends REDMergeCollector { private Snapper{} scarecrows; public constructor(Snapper{} scarecrows) { static int id = 0; if (scheduler.dbg_trace) pln("constructing"; id; scarecrows); this.scarecrows = scarecrows; super("ScarecrowCollector-" # id++, 0); } public void collect(Space space, Snapper{} snappers, Singularity{} sings) { if (scheduler.dbg_trace) pln("collecting"; scarecrows); snappers += scarecrows; } } private void reinstallScarecrowMerger(REDMergeScheduler scheduler) { if (scheduler) { if (scheduler.dbg_trace) pln("adding scarecrow policy"); scheduler << ScarecrowMergePolicy(); } } init { registerInitMergerHook(function reinstallScarecrowMerger); }
Below is an image showing the result of using the ScarecrowMergePolicy in addition to the default policy (with debug materials checked). As you can see, the scarecrows have been merged into their own merge group, while the default policy dealt with the rest.
Comments
0 comments
Please sign in to leave a comment.