Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optimize uiopacity component #16828

Merged
merged 3 commits into from
Mar 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
117 changes: 91 additions & 26 deletions cocos/2d/components/ui-opacity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import { Component } from '../../scene-graph/component';
import { misc } from '../../core';
import { UIRenderer } from '../framework/ui-renderer';
import { Node } from '../../scene-graph';
import { NodeEventType } from '../../scene-graph/node-event';

/**
* @en
Expand All @@ -47,12 +48,12 @@ import { Node } from '../../scene-graph';
export class UIOpacity extends Component {
/**
* @en
* Identification set by the parent node.
* The parent node's opacity.
*
* @zh
* 被父节点设置的标识
* 父节点的opacity
*/
private _setByParent = false;
private _parentOpacity: number = 1.0;

/**
* @en
Expand Down Expand Up @@ -96,8 +97,28 @@ export class UIOpacity extends Component {
}
}

// for UIOpacity
public static setEntityLocalOpacityDirtyRecursively (node: Node, dirty: boolean, interruptParentOpacity: number, setByParent: boolean): void {
/**
* @en
* Recursively sets localopacity.
*
* @zh
* 递归设置localopacity。
*
* @param node @en recursive node.
* @zh 递归的节点。
* @param dirty @en Is the color dirty.
* @zh color是否dirty。
* @param parentOpacity @en The parent node's opacity.
* @zh 父节点的opacity。
* @param stopRecursiveIfHasOpacity @en Stop recursion if UiOpacity component exists.
* @zh 如果存在UiOpacity组件则停止递归。
*/
public static setEntityLocalOpacityDirtyRecursively (
node: Node,
dirty: boolean,
parentOpacity: number,
stopRecursiveIfHasOpacity: boolean,
): void {
if (!node.isValid) {
// Since children might be destroyed before the parent,
// we should add protecting condition when executing recursion downwards.
Expand All @@ -106,50 +127,94 @@ export class UIOpacity extends Component {

const render = node._uiProps.uiComp as UIRenderer;
const uiOp = node.getComponent<UIOpacity>(UIOpacity);
let interruptOpacity = interruptParentOpacity;// if there is no UIOpacity component, it should always equal to 1.

if (uiOp && stopRecursiveIfHasOpacity) {
// Because it's possible that UiOpacity components are handled by themselves (at onEnable or onDisable)
uiOp._parentOpacity = parentOpacity;
return;
}
if (render && render.color) { // exclude UIMeshRenderer which has not color
render.renderEntity.colorDirty = dirty;
if (uiOp) {
render.renderEntity.localOpacity = interruptOpacity * uiOp.opacity / 255;
uiOp._setByParent = setByParent;
uiOp._parentOpacity = parentOpacity;
render.renderEntity.localOpacity = uiOp._parentOpacity * uiOp.opacity / 255;
} else {
// there is a just UIRenderer but no UIOpacity on the node, we should just transport the parentOpacity to the node.
render.renderEntity.localOpacity = interruptOpacity;
render.renderEntity.localOpacity = parentOpacity;
}
render.node._uiProps.localOpacity = render.renderEntity.localOpacity;
interruptOpacity = 1;
//No need for recursion here. Because it doesn't affect the capacity of the child nodes.
return;
} else if (uiOp) {
// there is a just UIOpacity but no UIRenderer on the node.
// we should transport the interrupt opacity downward
interruptOpacity = interruptOpacity * uiOp.opacity / 255;
uiOp._setByParent = setByParent;
uiOp._parentOpacity = parentOpacity;
parentOpacity = uiOp._parentOpacity * uiOp.opacity / 255;
}

for (let i = 0; i < node.children.length; i++) {
UIOpacity.setEntityLocalOpacityDirtyRecursively(node.children[i], dirty || (interruptOpacity < 1), interruptOpacity, true);
UIOpacity.setEntityLocalOpacityDirtyRecursively(
node.children[i],
dirty || (parentOpacity < 1),
parentOpacity,
stopRecursiveIfHasOpacity,
);
}
}

@serializable
protected _opacity = 255;

public onEnable (): void {
// If the ancestor node has a uiopacity component, it will be initialized when initializing
// the uiopacity component of the ancestor node, and there is no need to initialize it again.
if (this._setByParent) {
return;
protected _getParentOpacity (node: Node): number {
if (node == null || !node.isValid) {
return 1;
}
this.node._uiProps.localOpacity = this._opacity / 255;
this.setEntityLocalOpacityDirtyRecursively(true);
const render = node._uiProps.uiComp as UIRenderer;
const uiOp = node.getComponent<UIOpacity>(UIOpacity);
if (render && render.color) {
return 1;
} else if (uiOp) {
return uiOp._parentOpacity * (uiOp._opacity / 255);
}
return this._getParentOpacity(node.getParent()!);
}

public onDisable (): void {
// If the ancestor node has a uiopacity component, it will be uninitialized when uninitializing
// the uiopacity component of the ancestor node, and there is no need to uninitialize it again.
if (this._setByParent) {
protected _parentChanged (): void {
const parent = this.node.getParent();
let opacity = 1;
if (parent) {
this._parentOpacity = this._getParentOpacity(parent);
opacity = this._parentOpacity;
}
UIOpacity.setEntityLocalOpacityDirtyRecursively(this.node, true, opacity, false);
}

protected _setEntityLocalOpacityRecursively (opacity: number): void {
// Because JSB's localOpacity value is present in the renderEntity, but non-JSB's are not.
if (!JSB) {
return;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Why return if not JSB?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is only necessary for native

Copy link
Contributor

Choose a reason for hiding this comment

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

Could you please add some comment why only need in native?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Because JSB's localOpacity value is present in the renderEntity, but non-JSB's are not.

const render = this.node._uiProps.uiComp as UIRenderer;
if (render && render.color) { // exclude UIMeshRenderer which has not color
render.renderEntity.colorDirty = true;
render.renderEntity.localOpacity = opacity;
render.node._uiProps.localOpacity = render.renderEntity.localOpacity;
return;
}
// The current node is not recursive, only the child nodes are recursive.
for (let i = 0; i < this.node.children.length; i++) {
UIOpacity.setEntityLocalOpacityDirtyRecursively(this.node.children[i], true, opacity, true);
}
}

public onEnable (): void {
this.node.on(NodeEventType.PARENT_CHANGED, this._parentChanged, this);
this.node._uiProps.localOpacity = this._parentOpacity * this._opacity / 255;
this._setEntityLocalOpacityRecursively(this.node._uiProps.localOpacity);
}

public onDisable (): void {
this.node.off(NodeEventType.PARENT_CHANGED, this._parentChanged, this);
this.node._uiProps.localOpacity = 1;
this.setEntityLocalOpacityDirtyRecursively(true);
this._setEntityLocalOpacityRecursively(this.node._uiProps.localOpacity);
}
}
12 changes: 7 additions & 5 deletions native/cocos/2d/renderer/Batcher2d.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ void Batcher2d::fillBuffersAndMergeBatches() {
size_t index = 0;
for (auto* rootNode : _rootNodeArr) {
// _batches will add by generateBatch
walk(rootNode, 1);
walk(rootNode, 1, false);
generateBatch(_currEntity, _currDrawInfo);

auto* scene = rootNode->getScene()->getRenderScene();
Expand All @@ -109,19 +109,21 @@ void Batcher2d::fillBuffersAndMergeBatches() {
}
}

void Batcher2d::walk(Node* node, float parentOpacity) { // NOLINT(misc-no-recursion)
void Batcher2d::walk(Node* node, float parentOpacity, bool parentOpacityDirty) { // NOLINT(misc-no-recursion)
if (!node->isActiveInHierarchy()) {
return;
}
bool breakWalk = false;
auto* entity = static_cast<RenderEntity*>(node->getUserData());
bool opacityDirty = false;
if (entity) {
if (entity->getColorDirty()) {
if (entity->getColorDirty() || parentOpacityDirty) {
float localOpacity = entity->getLocalOpacity();
float localColorAlpha = entity->getColorAlpha();
entity->setOpacity(parentOpacity * localOpacity * localColorAlpha);
entity->setColorDirty(false);
entity->setVBColorDirty(true);
opacityDirty = true;
}
if (math::isEqualF(entity->getOpacity(), 0)) {
breakWalk = true;
Expand All @@ -143,7 +145,7 @@ void Batcher2d::walk(Node* node, float parentOpacity) { // NOLINT(misc-no-recurs
float thisOpacity = entity ? entity->getOpacity() : parentOpacity;
for (const auto& child : children) {
// we should find parent opacity recursively upwards if it doesn't have an entity.
walk(child, thisOpacity);
walk(child, thisOpacity, opacityDirty || parentOpacityDirty);
}
}

Expand Down Expand Up @@ -293,7 +295,7 @@ CC_FORCE_INLINE void Batcher2d::handleMiddlewareDraw(RenderEntity* entity, Rende

CC_FORCE_INLINE void Batcher2d::handleSubNode(RenderEntity* entity, RenderDrawInfo* drawInfo) { // NOLINT
if (drawInfo->getSubNode()) {
walk(drawInfo->getSubNode(), entity->getOpacity());
walk(drawInfo->getSubNode(), entity->getOpacity(), false);
}
}

Expand Down
2 changes: 1 addition & 1 deletion native/cocos/2d/renderer/Batcher2d.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ class Batcher2d final {
void updateDescriptorSet();

void fillBuffersAndMergeBatches();
void walk(Node* node, float parentOpacity);
void walk(Node* node, float parentOpacity, bool parentOpacityDirty);
void handlePostRender(RenderEntity* entity);
void handleDrawInfo(RenderEntity* entity, RenderDrawInfo* drawInfo, Node* node);
void handleComponentDraw(RenderEntity* entity, RenderDrawInfo* drawInfo, Node* node);
Expand Down