Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 42 additions & 1 deletion cocos/2d/components/rich-text.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
THE SOFTWARE.
*/

import { ccclass, requireComponent, executeInEditMode, executionOrder, help, menu, multiline, type, displayOrder, serializable, editable } from 'cc.decorator';

Check warning on line 26 in cocos/2d/components/rich-text.ts

View workflow job for this annotation

GitHub Actions / Run ESLint

This line has a length of 159. Maximum allowed is 150
import { DEBUG, DEV, EDITOR } from 'internal:constants';
import { Font, SpriteAtlas, TTFFont, SpriteFrame } from '../assets';
import { EventTouch } from '../../input/types';
Expand All @@ -44,6 +44,7 @@
getEnglishWordPartAtLast,
getSymbolAt,
} from '../utils/text-utils';
import { Sorting2D } from '../../sorting/sorting-2d';

const _htmlTextParser = new HtmlTextParser();
const RichTextChildName = 'RICHTEXT_CHILD';
Expand Down Expand Up @@ -504,12 +505,23 @@
protected _lineOffsetX = 0;
protected declare _updateRichTextStatus: () => void;
protected _labelChildrenNum = 0; // only ISegment
protected _currentSortingLayer = 0;
protected _currentSortingOrder = 0;
protected _sorting2d: Sorting2D | null = null;

constructor () {
super();
this._updateRichTextStatus = this._updateRichText;
}

protected override __preload (): void {
this._sorting2d = this.getComponent(Sorting2D);
if (this._sorting2d != null) {
this._currentSortingOrder = this._sorting2d.sortingOrder;
this._currentSortingLayer = this._sorting2d.sortingLayer;
}
}

public onLoad (): void {
this.node.on(NodeEventType.LAYER_CHANGED, this._applyLayer, this);
this.node.on(NodeEventType.ANCHOR_CHANGED, this._updateRichTextPosition, this);
Expand Down Expand Up @@ -864,7 +876,11 @@
this.node.insertChild(labelSegment.node, this._labelChildrenNum++);
this._applyTextAttribute(labelSegment);
this._segments.push(labelSegment);

if (this._sorting2d != null) {
const childSorting = labelSegment.node.addComponent(Sorting2D);
childSorting.sortingOrder = this._sorting2d.sortingOrder;
childSorting.sortingLayer = this._sorting2d.sortingLayer;
}
return labelSegment;
}

Expand Down Expand Up @@ -1047,6 +1063,11 @@
segment.clickHandler = event.click;
segment.clickParam = event.param;
}
if (this._sorting2d != null) {
const childSorting = segment.node.addComponent(Sorting2D);
childSorting.sortingOrder = this._sorting2d.sortingOrder;
childSorting.sortingLayer = this._sorting2d.sortingLayer;
}
}
}

Expand Down Expand Up @@ -1344,6 +1365,26 @@
label.isItalic = false;
label.isUnderline = false;
}

protected update (dt: number): void {
if (this._sorting2d == null) {
return;
}

if (this._sorting2d.sortingOrder !== this._currentSortingOrder
|| this._sorting2d.sortingLayer !== this._currentSortingLayer
) {
this._currentSortingOrder = this._sorting2d.sortingOrder;
this._currentSortingLayer = this._sorting2d.sortingLayer;
this._segments.forEach((seg) => {
const sortingChild = seg.node.getComponent(Sorting2D);
if (sortingChild) {
sortingChild.sortingOrder = this._currentSortingOrder;
sortingChild.sortingLayer = this._currentSortingLayer;
}
});
}
}
}

cclegacy.RichText = RichText;
69 changes: 67 additions & 2 deletions cocos/gfx/webgl/webgl-commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -445,6 +445,11 @@ export function WebGLCmdFuncCreateBuffer (device: WebGLDevice, gpuBuffer: IWebGL
const { gl, stateCache } = device;
const glUsage: GLenum = gpuBuffer.memUsage & MemoryUsageBit.HOST ? WebGLConstants.DYNAMIC_DRAW : WebGLConstants.STATIC_DRAW;

// iOS fix: Initialize version tracking for VAO invalidation
if (systemInfo.os === OS.IOS) {
gpuBuffer.updateVersion = 0;
}

if (gpuBuffer.usage & BufferUsageBit.VERTEX) {
gpuBuffer.glTarget = WebGLConstants.ARRAY_BUFFER;
const glBuffer = gl.createBuffer();
Expand Down Expand Up @@ -643,6 +648,11 @@ export function WebGLCmdFuncUpdateBuffer (
}
gfxStateCache.gpuInputAssembler = null;

// iOS fix: Increment version when buffer is updated
if (systemInfo.os === OS.IOS && gpuBuffer.updateVersion !== undefined) {
gpuBuffer.updateVersion++;
}

if (stateCache.glArrayBuffer !== gpuBuffer.glBuffer) {
gl.bindBuffer(WebGLConstants.ARRAY_BUFFER, gpuBuffer.glBuffer);
stateCache.glArrayBuffer = gpuBuffer.glBuffer;
Expand All @@ -658,6 +668,11 @@ export function WebGLCmdFuncUpdateBuffer (
}
gfxStateCache.gpuInputAssembler = null;

// iOS fix: Increment version when buffer is updated
if (systemInfo.os === OS.IOS && gpuBuffer.updateVersion !== undefined) {
gpuBuffer.updateVersion++;
}

if (stateCache.glElementArrayBuffer !== gpuBuffer.glBuffer) {
gl.bindBuffer(WebGLConstants.ELEMENT_ARRAY_BUFFER, gpuBuffer.glBuffer);
stateCache.glElementArrayBuffer = gpuBuffer.glBuffer;
Expand Down Expand Up @@ -2258,12 +2273,61 @@ export function WebGLCmdFuncBindStates (
if (device.extensions.useVAO) {
const vao = device.extensions.OES_vertex_array_object!;

// iOS Safari WebGL-to-Metal: Check if buffers were updated since VAO creation
const isIOS = systemInfo.os === OS.IOS;
let needRecreateVAO = false;

if (isIOS) {
// Compute current buffer version hash
let currentVersion = 0;
for (let i = 0; i < gpuInputAssembler.gpuVertexBuffers.length; i++) {
const buf = gpuInputAssembler.gpuVertexBuffers[i];
if (buf.updateVersion !== undefined) {
currentVersion += buf.updateVersion * (i + 1);
}
}
if (gpuInputAssembler.gpuIndexBuffer && gpuInputAssembler.gpuIndexBuffer.updateVersion !== undefined) {
currentVersion += gpuInputAssembler.gpuIndexBuffer.updateVersion * 1000;
}

// Check if VAO was created with different buffer versions
const cachedVersion = gpuInputAssembler.vaoVersions?.get(gpuShader.glProgram!);
if (cachedVersion !== undefined && cachedVersion !== currentVersion) {
needRecreateVAO = true;
}
}

// check vao
let glVAO = gpuInputAssembler.glVAOs.get(gpuShader.glProgram!);
if (!glVAO) {
if (!glVAO || needRecreateVAO) {
// Delete old VAO if recreating
if (needRecreateVAO && glVAO) {
vao.deleteVertexArrayOES(glVAO);
if (cache.glVAO === glVAO) {
cache.glVAO = null;
}
}
glVAO = vao.createVertexArrayOES()!;
gpuInputAssembler.glVAOs.set(gpuShader.glProgram!, glVAO);

// iOS fix: Store buffer versions when VAO is created
if (isIOS) {
let vaoVersion = 0;
for (let i = 0; i < gpuInputAssembler.gpuVertexBuffers.length; i++) {
const buf = gpuInputAssembler.gpuVertexBuffers[i];
if (buf.updateVersion !== undefined) {
vaoVersion += buf.updateVersion * (i + 1);
}
}
if (gpuInputAssembler.gpuIndexBuffer && gpuInputAssembler.gpuIndexBuffer.updateVersion !== undefined) {
vaoVersion += gpuInputAssembler.gpuIndexBuffer.updateVersion * 1000;
}
if (!gpuInputAssembler.vaoVersions) {
gpuInputAssembler.vaoVersions = new Map<WebGLProgram, number>();
}
gpuInputAssembler.vaoVersions.set(gpuShader.glProgram!, vaoVersion);
}

vao.bindVertexArrayOES(glVAO);
gl.bindBuffer(WebGLConstants.ARRAY_BUFFER, null);
gl.bindBuffer(WebGLConstants.ELEMENT_ARRAY_BUFFER, null);
Expand Down Expand Up @@ -2316,7 +2380,8 @@ export function WebGLCmdFuncBindStates (
cache.glElementArrayBuffer = null;
}

if (cache.glVAO !== glVAO) {
// iOS Safari: Force VAO rebinding on iOS, or when cache doesn't match
if (isIOS || cache.glVAO !== glVAO) {
vao.bindVertexArrayOES(glVAO);
cache.glVAO = glVAO;
}
Expand Down
6 changes: 6 additions & 0 deletions cocos/gfx/webgl/webgl-gpu-objects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,9 @@ export interface IWebGLGPUBuffer {
vf32: Float32Array | null;
/** @mangle */
indirects: WebGLIndirectDrawInfos;
/** @mangle */
// iOS fix: Track buffer updates to invalidate VAOs
updateVersion?: number;
}

/** @mangle */
Expand Down Expand Up @@ -348,6 +351,9 @@ export interface IWebGLGPUInputAssembler {
glAttribs: IWebGLAttrib[];
glIndexType: GLenum;
glVAOs: Map<WebGLProgram, WebGLVertexArrayObjectOES>;
/** @mangle */
// iOS fix: Track buffer versions when VAOs were created
vaoVersions?: Map<WebGLProgram, number>;
}

/** @mangle */
Expand Down
1 change: 1 addition & 0 deletions cocos/gfx/webgl/webgl-input-assembler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ export class WebGLInputAssembler extends InputAssembler {
glAttribs: [],
glIndexType,
glVAOs: new Map<WebGLProgram, WebGLVertexArrayObjectOES>(),
vaoVersions: new Map<WebGLProgram, number>(), // iOS fix
};

WebGLCmdFuncCreateInputAssember(WebGLDeviceManager.instance, this._gpuInputAssembler);
Expand Down
72 changes: 70 additions & 2 deletions cocos/gfx/webgl2/webgl2-commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -624,6 +624,11 @@ export function WebGL2CmdFuncCreateBuffer (device: WebGL2Device, gpuBuffer: IWeb
const cache = device.getStateCache();
const glUsage: GLenum = gpuBuffer.memUsage & MemoryUsageBit.HOST ? WebGLConstants.DYNAMIC_DRAW : WebGLConstants.STATIC_DRAW;

// iOS fix: Initialize buffer version tracking
if (systemInfo.os === OS.IOS) {
gpuBuffer.updateVersion = 0;
}

if (gpuBuffer.usage & BufferUsageBit.VERTEX) {
gpuBuffer.glTarget = WebGLConstants.ARRAY_BUFFER;
const glBuffer = gl.createBuffer();
Expand Down Expand Up @@ -830,12 +835,20 @@ export function WebGL2CmdFuncUpdateBuffer (

switch (gpuBuffer.glTarget) {
case WebGLConstants.ARRAY_BUFFER: {
// iOS fix: Increment buffer version to invalidate VAOs that reference this buffer
if (systemInfo.os === OS.IOS && gpuBuffer.updateVersion !== undefined) {
gpuBuffer.updateVersion++;
}

if (device.extensions.useVAO) {
// When updating vertex buffer data, unbind VAO to force rebind on next draw
// This is critical because VAO caches vertex attribute pointers to buffers
if (cache.glVAO) {
gl.bindVertexArray(null);
cache.glVAO = null;
}
}
// Invalidate input assembler cache to force VAO rebind/recreation
gfxStateCache.gpuInputAssembler = null;

if (cache.glArrayBuffer !== gpuBuffer.glBuffer) {
Expand All @@ -857,6 +870,11 @@ export function WebGL2CmdFuncUpdateBuffer (
break;
}
case WebGLConstants.ELEMENT_ARRAY_BUFFER: {
// iOS fix: Increment buffer version to invalidate VAOs that reference this buffer
if (systemInfo.os === OS.IOS && gpuBuffer.updateVersion !== undefined) {
gpuBuffer.updateVersion++;
}

if (device.extensions.useVAO) {
if (cache.glVAO) {
gl.bindVertexArray(null);
Expand Down Expand Up @@ -2345,12 +2363,61 @@ export function WebGL2CmdFuncBindStates (
gfxStateCache.gpuInputAssembler = gpuInputAssembler;

if (device.extensions.useVAO) {
// iOS Safari WebGL-to-Metal: Check if buffers were updated since VAO creation
const isIOS = systemInfo.os === OS.IOS;
let needRecreateVAO = false;

if (isIOS) {
// Compute current buffer version hash
let currentVersion = 0;
for (let i = 0; i < gpuInputAssembler.gpuVertexBuffers.length; i++) {
const buf = gpuInputAssembler.gpuVertexBuffers[i];
if (buf.updateVersion !== undefined) {
currentVersion += buf.updateVersion * (i + 1);
}
}
if (gpuInputAssembler.gpuIndexBuffer && gpuInputAssembler.gpuIndexBuffer.updateVersion !== undefined) {
currentVersion += gpuInputAssembler.gpuIndexBuffer.updateVersion * 1000;
}

// Check if VAO was created with different buffer versions
const cachedVersion = gpuInputAssembler.vaoVersions?.get(gpuShader.glProgram!);
if (cachedVersion !== undefined && cachedVersion !== currentVersion) {
needRecreateVAO = true;
}
}

// check vao
let glVAO = gpuInputAssembler.glVAOs.get(gpuShader.glProgram!);
if (!glVAO) {
if (!glVAO || needRecreateVAO) {
// Delete old VAO if recreating
if (needRecreateVAO && glVAO) {
gl.deleteVertexArray(glVAO);
if (cache.glVAO === glVAO) {
cache.glVAO = null;
}
}
glVAO = gl.createVertexArray()!;
gpuInputAssembler.glVAOs.set(gpuShader.glProgram!, glVAO);

// iOS fix: Store buffer versions when VAO is created
if (isIOS) {
let vaoVersion = 0;
for (let i = 0; i < gpuInputAssembler.gpuVertexBuffers.length; i++) {
const buf = gpuInputAssembler.gpuVertexBuffers[i];
if (buf.updateVersion !== undefined) {
vaoVersion += buf.updateVersion * (i + 1);
}
}
if (gpuInputAssembler.gpuIndexBuffer && gpuInputAssembler.gpuIndexBuffer.updateVersion !== undefined) {
vaoVersion += gpuInputAssembler.gpuIndexBuffer.updateVersion * 1000;
}
if (!gpuInputAssembler.vaoVersions) {
gpuInputAssembler.vaoVersions = new Map();
}
gpuInputAssembler.vaoVersions.set(gpuShader.glProgram!, vaoVersion);
}

gl.bindVertexArray(glVAO);
gl.bindBuffer(WebGLConstants.ARRAY_BUFFER, null);
gl.bindBuffer(WebGLConstants.ELEMENT_ARRAY_BUFFER, null);
Expand Down Expand Up @@ -2401,7 +2468,8 @@ export function WebGL2CmdFuncBindStates (
cache.glElementArrayBuffer = null;
}

if (cache.glVAO !== glVAO) {
// iOS Safari: Force VAO rebinding on iOS, or when cache doesn't match
if (isIOS || cache.glVAO !== glVAO) {
gl.bindVertexArray(glVAO);
cache.glVAO = glVAO;
}
Expand Down
6 changes: 6 additions & 0 deletions cocos/gfx/webgl2/webgl2-gpu-objects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,9 @@ export interface IWebGL2GPUBuffer {

buffer: ArrayBufferView | null;
indirects: WebGL2IndirectDrawInfos;

/** @mangle */
updateVersion: number; // iOS fix: track buffer updates to invalidate VAOs
}

/** @mangle */
Expand Down Expand Up @@ -349,6 +352,9 @@ export interface IWebGL2GPUInputAssembler {
glAttribs: IWebGL2Attrib[];
glIndexType: GLenum;
glVAOs: Map<WebGLProgram, WebGLVertexArrayObject>;

/** @mangle */
vaoVersions: Map<WebGLProgram, number>; // iOS fix: track buffer versions when VAO was created
}

/** @mangle */
Expand Down
1 change: 1 addition & 0 deletions cocos/gfx/webgl2/webgl2-input-assembler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ export class WebGL2InputAssembler extends InputAssembler {
glAttribs: [],
glIndexType,
glVAOs: new Map<WebGLProgram, WebGLVertexArrayObject>(),
vaoVersions: new Map<WebGLProgram, number>(), // iOS fix: track buffer versions
};

WebGL2CmdFuncCreateInputAssember(WebGL2DeviceManager.instance, this._gpuInputAssembler);
Expand Down
Loading