- Published on
PixiJS 源码揭秘 - 4. 深入理解渲染系统
- Authors
- Name
- 青雲
AutoDetectRenderer工作原理
在PixiJS中,为了适应不同设备和浏览器的性能需求,引擎提供了一个自动检测渲染器的方法:autoDetectRenderer
。这个方法可以智能地选择最佳的渲染器(WebGLRenderer 或 CanvasRenderer),以确保在各种平台上的最佳性能表现。
详细流程
初始化渲染器优先级顺序
autoDetectRenderer
接收一个配置参数options
,首先根据用户提供的偏好设置(preference),初始化一个渲染器优先级列表preferredOrder
。如果用户指定了首选渲染器类型,则将其放在优先级的第一个位置,其他渲染器依次排列。
let preferredOrder: string[] = [];
if (options.preference)
{
preferredOrder.push(options.preference);
renderPriority.forEach((item) =>
{
if (item !== options.preference)
{
preferredOrder.push(item);
}
});
}
else
{
preferredOrder = renderPriority.slice();
}
尝试依次选择渲染器
接下来,函数以优先级顺序依次尝试创建不同类型的渲染器。这个过程中会检查当前环境是否支持WebGPU或WebGL,并根据支持情况来选择适合的渲染器。如果当前优先项是 canvas,则直接抛出错误,因为 Canvas 渲染器尚未实现。
let RendererClass: new () => Renderer;
let finalOptions: Partial<AutoDetectOptions> = {};
for (let i = 0; i < preferredOrder.length; i++)
{
const rendererType = preferredOrder[i];
if (rendererType === 'webgpu' && (await isWebGPUSupported()))
{
const { WebGPURenderer } = await import('./gpu/WebGPURenderer');
RendererClass = WebGPURenderer;
finalOptions = { ...options, ...options.webgpu };
break;
}
else if (
rendererType === 'webgl'
&& isWebGLSupported(
options.failIfMajorPerformanceCaveat
?? AbstractRenderer.defaultOptions.failIfMajorPerformanceCaveat
)
)
{
const { WebGLRenderer } = await import('./gl/WebGLRenderer');
RendererClass = WebGLRenderer;
finalOptions = { ...options, ...options.webgl };
break;
}
else if (rendererType === 'canvas')
{
finalOptions = { ...options };
throw new Error('CanvasRenderer is not yet implemented');
}
}
检查是否有支持的渲染器
在所有尝试结束后,如果仍未找到支持的渲染器,则抛出一个错误。
if (!RendererClass)
{
throw new Error('No available renderer for the current environment');
}
初始化和返回渲染器实例
选择到支持的渲染器类后,函数创建渲染器实例并调用renderer.init()
方法进行初始化,最后返回渲染器实例。
const renderer = new RendererClass();
await renderer.init(finalOptions);
return renderer;
WebGPU 支持检查
isWebGPUSupported
函数的主要目的是检查当前浏览器是否支持 WebGPU API。其工作流程如下:
- 检查 GPU 属性:首先,函数会通过
DOMAdapter.get().getNavigator().gpu
获取浏览器的 GPU 属性。如果该属性不存在,说明浏览器不支持 WebGPU,函数将返回 false。 - 请求适配器:如果 GPU 属性存在,函数会尝试请求一个 GPU 适配器。通过调用
gpu.requestAdapter(options)
,函数会向 GPU 请求适配器。 - 请求设备:一旦成功获取适配器,函数会调用
adapter.requestDevice()
,这一步是为了实际请求 GPU 设备。如果在这个过程中出现任何错误 (例如,设备不可用) ,函数会捕获异常并返回 false。 - 返回支持状态:如果上述步骤都成功,函数将返回 true,表示 WebGPU 被支持。
export async function isWebGPUSupported(options: GPURequestAdapterOptions = {}): Promise<boolean>
{
if (_isWebGPUSupported !== undefined) return _isWebGPUSupported;
_isWebGPUSupported = await (async (): Promise<boolean> =>
{
const gpu = DOMAdapter.get().getNavigator().gpu;
if (!gpu)
{
return false;
}
try
{
const adapter = await gpu.requestAdapter(options) as GPUAdapter;
await adapter.requestDevice();
return true;
}
catch (e)
{
return false;
}
})();
return _isWebGPUSupported;
}
GPUAdapter 和 GPUDevice概念
GPUAdapter 和 GPUDevice 是 WebGPU API 中的两个核心概念,分别代表 GPU 的适配器和设备。
GPUAdapter 是表示系统中物理 GPU 的接口。它的主要功能包括:
- 适配器选择:GPUAdapter 允许开发者请求适合的 GPU 适配器,适配器可能是独立显卡或集成显卡。通过适配器,开发者可以访问 GPU 的特性和能力。
- 设备请求:使用 GPUAdapter,开发者可以请求一个 GPUDevice,这个设备将用于执行图形渲染和计算任务。
GPUDevice 是从 GPUAdapter 请求到的具体设备实例。它的主要功能包括:
- 资源管理:GPUDevice 负责管理 GPU 中的资源,如缓冲区、纹理等。开发者可以通过设备创建和管理这些资源。
- 命令执行:GPUDevice 还负责执行图形和计算命令。开发者可以通过设备提交命令缓冲区,进行渲染或计算操作。
WebGL 支持检查
isWebGLSupported
函数用于检查浏览器对 WebGL 的支持情况,具体流程如下:
- 检查全局状态:首先,函数检查
_isWebGLSupported
变量。如果该变量已经被定义,说明支持状态已经检查过,直接返回该值。 - 创建上下文选项:接着,函数定义了一个上下文选项对象,包含
stencil
属性和一个可选的failIfMajorPerformanceCaveat
属性。后者用于控制在性能出现重大问题时是否失败。 - 创建 Canvas 和 WebGL 上下文:函数会创建一个 Canvas 元素,并尝试获取其 WebGL 上下文。如果获取成功,进一步检查该上下文的属性,确保其具有所需的特性 (例如,是否支持 stencil) 。
- 处理上下文丢失:如果获取上下文成功,函数还会检查是否支持 WEBGL_lose_context 扩展,以便在需要时可以丢失上下文。
- 返回支持状态:如果所有检查都通过,函数返回 true,表示 WebGL 被支持;否则返回 false。
export function isWebGLSupported(
failIfMajorPerformanceCaveat?: boolean
): boolean
{
if (_isWebGLSupported !== undefined) return _isWebGLSupported;
_isWebGLSupported = ((): boolean =>
{
const contextOptions = {
stencil: true,
failIfMajorPerformanceCaveat:
failIfMajorPerformanceCaveat
?? AbstractRenderer.defaultOptions.failIfMajorPerformanceCaveat,
};
try
{
if (!DOMAdapter.get().getWebGLRenderingContext())
{
return false;
}
const canvas = DOMAdapter.get().createCanvas();
let gl = canvas.getContext('webgl', contextOptions);
const success = !!gl?.getContextAttributes()?.stencil;
if (gl)
{
const loseContext = gl.getExtension('WEBGL_lose_context');
if (loseContext)
{
loseContext.loseContext();
}
}
gl = null;
return success;
}
catch (e)
{
return false;
}
})();
return _isWebGLSupported;
}
Stencil 属性
Stencil 属性在图形渲染中用于控制像素的绘制过程,主要用于实现复杂的效果,如遮罩、剪切和图形的合成。
- 遮罩效果:通过设置 Stencil 缓冲区,可以定义哪些区域可以被绘制,哪些区域被遮挡。这使得开发者能够创建复杂的视觉效果,比如动态遮罩。
- 条件渲染:Stencil 属性允许根据特定条件决定是否绘制某个像素。这意味着可以在渲染过程中使用 Stencil 缓冲区的值来控制图形的显示。
- 多层次效果:利用 Stencil 属性,可以在同一场景中实现多层次的图形效果,例如在一个对象上绘制多个效果,而不需要额外的渲染调用。
- 性能优化:通过使用 Stencil 缓冲区,可以减少不必要的渲染操作,从而提高性能,尤其是在处理复杂场景时。
示例
async function setup() {
// 自动检测渲染器
const renderer = await PIXI.autoDetectRenderer({ width: 800, height: 600, backgroundColor: 0x1099bb });
document.body.appendChild(renderer.canvas);
// 创建一个舞台
const stage = new PIXI.Container();
// 加载纹理并创建精灵
await PIXI.Assets.load('https://pixijs.io/examples/examples/assets/bunny.png');
const texture = PIXI.Texture.from('https://pixijs.io/examples/examples/assets/bunny.png');
const bunny = new PIXI.Sprite(texture);
// 设置精灵的锚点和位置
bunny.anchor.set(0.5);
bunny.x = renderer.width / 2;
bunny.y = renderer.height / 2;
// 将精灵添加到舞台
stage.addChild(bunny);
// 动画循环
function animate() {
requestAnimationFrame(animate);
bunny.rotation += 0.01; // 旋转精灵
renderer.render(stage); // 渲染舞台
}
animate(); // 启动动画
}
setup();
WebGLRenderer与WebGPURenderer
在PixiJS中,WebGLRenderer和WebGPURenderer是两种主要的渲染器,分别利用WebGL和WebGPU技术来实现高效的图形渲染。
WebGLRenderer
WebGLRenderer
由多个系统(Systems)和渲染管道(Render Pipes)组成,每个系统负责管理特定的任务。这些系统包括缓冲系统、几何系统、纹理系统等,确保了渲染过程的高效和灵活性。
主要系统
- GlUboSystem:管理WebGL统一缓冲(Uniform Buffer)对象,用于在着色器之间共享数据。
- GlBackBufferSystem:管理后台缓冲区,用于屏幕上像素的读取操作。
- GlContextSystem:管理WebGL上下文及其扩展。
- GlBufferSystem:管理缓冲区及其GPU资源。
- GlTextureSystem:管理纹理及其GPU资源。
- GlRenderTargetSystem:管理渲染目标,比如屏幕或其他纹理。
- GlGeometrySystem:管理几何体,用于通过GPU绘制图形。
- GlUniformGroupSystem:同步着色器属性和GPU之间数据。
- GlShaderSystem:管理着色器程序。
- GlEncoderSystem:管理编码器(WebGPU范例),用于绘制网格和着色器。
- GlStateSystem:管理WebGL上下文状态,比如混合模式、深度测试等。
- GlStencilSystem:管理模板缓冲区,主要用于遮罩。
- GlColorMaskSystem:管理颜色掩码,用于颜色掩蔽。
WebGPURenderer
WebGPURenderer是PixiJS中利用WebGPU API的渲染器。WebGPU是下一代图形和计算API,相较于WebGL,提供了更高性能和更低级别的控制。
与WebGLRenderer类似,WebGPURenderer也由多个系统和渲染管道组成。WebGPURenderer提供了专门用于管理WebGPU功能的系统,这些系统包括设备系统、缓冲系统、着色器系统等。
主要系统
- GpuUboSystem:管理WebGPU统一缓冲对象,用于在着色器之间共享数据。
- GpuEncoderSystem:管理WebGPU命令编码器。
- GpuDeviceSystem:管理WebGPU设备和其扩展。
- GpuBufferSystem:管理缓冲区及其GPU资源。
- GpuTextureSystem:管理纹理及其GPU资源。
- GpuRenderTargetSystem:管理渲染目标,比如屏幕或其他纹理。
- GpuShaderSystem:管理着色器程序。
- GpuStateSystem:管理WebGPU管线状态,比如混合模式、深度测试等。
- GpuColorMaskSystem:管理颜色掩码,用于颜色掩蔽。
- GpuStencilSystem:管理模板缓冲区,主要用于遮罩。
- PipelineSystem:管理WebGPU管线,用于渲染。
- BindGroupSystem:管理WebGPU绑定组,这是将数据绑定到着色器的主要方式。
两者区别
渲染器类型
- WebGLRenderer:使用 WebGL 2.0 API,适用于大多数现代浏览器。它主要依赖于 GPU 的图形渲染能力,并通过 WebGL 上下文来管理图形资源和渲染过程。
- WebGPURenderer:基于 WebGPU API,这是一个更现代的图形 API,旨在提供更高效的图形渲染和计算能力。WebGPU 支持更细粒度的控制和更高效的资源管理。
系统与管道:两者都使用一系列系统来管理渲染过程,但所使用的系统和管道有所不同:
- WebGLRenderer 包含一组特定于 WebGL 的系统,例如 GlUboSystem、GlBufferSystem 和 GlShaderSystem。它们负责处理 WebGL 的各种功能,如缓冲区管理、着色器编译和状态管理 。
- WebGPURenderer 则包含 GpuUboSystem、GpuBufferSystem 和 GpuShaderSystem 等系统,这些系统专门为 WebGPU 设计,能够更高效地管理 GPU 资源和命令编码 。
初始化与配置
- WebGLRenderer 的初始化过程涉及创建 WebGL 上下文,并配置相关的渲染管道。它的构造函数中会设置系统配置,并通过 super 调用父类的构造函数进行初始化。
- WebGPURenderer 也遵循相似的初始化流程,但它需要请求 GPU 适配器和设备,这意味着在使用之前需要进行额外的支持检测和设备请求 。
渲染过程
- 在 WebGLRenderer 中,渲染过程通常涉及调用 render 方法,使用 WebGL 上下文绘制场景中的对象。
- WebGPURenderer 的渲染过程则更为复杂,涉及命令编码和提交,这使得它能够更好地利用现代 GPU 的并行处理能力。
兼容性与性能
- WebGLRenderer 在大多数浏览器中具有良好的兼容性,适用于广泛的设备和平台,但可能在性能上受到 WebGL 的限制。
- WebGPURenderer 则提供了更高的性能和更好的资源管理,但目前的浏览器支持情况相对较少,可能不如 WebGL 渗透率高。
WebGLRenderer 和 WebGPURenderer 各有其优缺点,适用于不同的使用场景。WebGLRenderer 更加成熟,兼容性好,而 WebGPURenderer 则提供了更先进的图形处理能力,适合未来的开发需求。
AbstractRenderer详解
AbstractRenderer
是所有渲染器的基类。WebGLRenderer
和WebGPURenderer
都继承自AbstractRenderer
,共享其核心功能和机制。AbstractRenderer
封装了渲染器的一些共性逻辑,并提供了一系列系统(Systems)和渲染管道(Render Pipes)来管理具体的渲染任务和状态。
关键属性
- type:渲染器的类型(如WebGL或WebGPU)。
- name:渲染器的名称。
- runners:系统运行器(System Runners),用于管理渲染器各个生命周期阶段的系统调用。
- renderPipes:渲染管道(Render Pipes),管理具体的渲染任务。
- view:视图系统,管理与 DOM 关联的画布,负责渲染的主要输出。
- background:背景系统,管理视图的背景颜色和透明度。
- textureGenerator:生成纹理系统,管理从容器生成纹理的逻辑。
- _initOptions:存储初始化时的选项,确保在渲染过程中可以访问这些配置。
- _systemsHash:存储已添加的系统的哈希表,便于快速查找和管理。
- _lastObjectRendered:记录最后渲染的对象,通常用于交互管理和事件处理。
export class AbstractRenderer<
PIPES, OPTIONS extends SharedRendererOptions, CANVAS extends ICanvas = HTMLCanvasElement
> extends EventEmitter<{resize: [screenWidth: number, screenHeight: number, resolution: number]}>
{
public static defaultOptions = {
resolution: 1,
failIfMajorPerformanceCaveat: false,
roundPixels: false,
};
public readonly type: number;
public readonly name: string;
public _roundPixels: 0 | 1;
public readonly runners: Runners = Object.create(null) as Runners;
public readonly renderPipes = Object.create(null) as PIPES;
public view!: ViewSystem;
public background: BackgroundSystem;
public textureGenerator: GenerateTextureSystem;
protected _initOptions: OPTIONS = {} as OPTIONS;
protected config: RendererConfig;
private _systemsHash: Record<string, System> = Object.create(null);
private _lastObjectRendered: Container;
}
构造函数和初始化过程
构造函数
构造函数接收一个RendererConfig
对象,用于配置系统和渲染管道,并动态加载这些组件到渲染器中。
constructor(config: RendererConfig)
{
super();
this.type = config.type;
this.name = config.name;
this.config = config;
const combinedRunners = [...defaultRunners, ...(this.config.runners ?? [])];
this._addRunners(...combinedRunners);
this._unsafeEvalCheck();
}
初始化
初始化方法init
用于加载环境扩展,添加系统和渲染管道,并调用各系统的初始化方法。具体分为以下步骤:
- 跳过扩展导入:法首先检查
options
中是否包含skipExtensionImports
属性。如果该属性为true
,则在加载环境扩展时跳过导入。这使得开发者可以选择不加载某些扩展,以提高性能或减少依赖。 - 加载环境扩展:使用
loadEnvironmentExtensions
方法加载所需的扩展。这个步骤确保渲染器能够访问所需的功能和资源。 - 添加系统:调用
_addSystems
方法,将配置中定义的各个系统添加到渲染器中。系统负责处理渲染过程中的特定任务,如管理纹理、处理几何体等。 - 添加管道:调用
_addPipes
方法,将渲染管道和适配器添加到渲染器。管道用于管理渲染过程中的数据流和处理。 - 初始化各个系统:遍历所有已添加的系统,调用每个系统的初始化方法。此步骤确保每个系统都准备好进行渲染,并能够正确处理输入和输出。
- 合并选项:将默认选项与用户提供的选项合并,以确保渲染器在正确的配置下运行。合并后的选项将用于后续的渲染操作。
- 设置像素圆整:根据选项设置 _roundPixels 属性,指示是否在渲染时强制将坐标四舍五入为整数。这有助于避免抗锯齿效果,确保图形在屏幕上显示时的清晰度。
- 触发初始化运行器:最后,调用所有与初始化相关的运行器,确保在渲染器初始化完成后执行相应的逻辑。自定义插件就是在这个过程中完成初始化。
public async init(options: Partial<OPTIONS> = {})
{
const skip = options.skipExtensionImports === true ? true : options.manageImports === false;
await loadEnvironmentExtensions(skip);
this._addSystems(this.config.systems);
this._addPipes(this.config.renderPipes, this.config.renderPipeAdaptors);
for (const systemName in this._systemsHash)
{
const system = this._systemsHash[systemName];
const defaultSystemOptions = (system.constructor as any).defaultOptions;
options = { ...defaultSystemOptions, ...options };
}
options = { ...AbstractRenderer.defaultOptions, ...options };
this._roundPixels = options.roundPixels ? 1 : 0;
for (let i = 0; i < this.runners.init.items.length; i++)
{
await this.runners.init.items[i].init(options);
}
this._initOptions = options as OPTIONS;
}
渲染、清除和销毁
渲染
render
方法的主要目的是将指定的容器或对象渲染到画布上,并处理与渲染相关的各种选项和状态。该方法接受一个参数,可以是渲染选项对象或容器:
options
:如果是对象,则包含渲染的配置,如要渲染的容器、渲染目标等。- 如果是容器,则会将其封装为渲染选项。
工作流程:
处理参数:
- 首先,检查传入的参数。如果参数是一个容器实例,方法会将其封装为一个包含 container 属性的对象。
- 如果传入了一个 deprecated 参数 (用于兼容旧版本) ,则会发出警告,并将其处理为渲染目标。
设置渲染目标:确定渲染目标。如果没有指定目标,则使用默认的渲染目标 (通常是主画布) 。
清除颜色设置:如果渲染目标是主画布,方法会记录最后渲染的对象,并将清除颜色设置为背景颜色。
处理清除颜色:检查 clearColor 属性。如果提供了清除颜色,则将其转换为 RGBA 数组格式,确保颜色数据可以被正确使用。
更新变换矩阵:如果没有提供变换矩阵,方法会更新容器的局部变换,以确保渲染时位置和旋转等属性正确。
触发渲染事件:调用一系列运行器,以控制渲染流程的各个阶段:
- prerender:在渲染开始前触发的事件,允许开发者在渲染之前执行自定义逻辑。
- renderStart:渲染开始时触发的事件。
- render:实际执行渲染操作的事件。
- renderEnd:渲染结束时触发的事件。
- postrender:在渲染结束后触发的事件,允许进行清理或后处理。
public render(options: RenderOptions | Container): void;
public render(container: Container, options: {renderTexture: any}): void;
public render(args: RenderOptions | Container, deprecated?: {renderTexture: any}): void
{
let options = args;
if (options instanceof Container)
{
options = { container: options };
if (deprecated)
{
deprecation(v8_0_0, 'passing a second argument is deprecated, please use render options instead');
options.target = deprecated.renderTexture;
}
}
options.target ||= this.view.renderTarget;
if (options.target === this.view.renderTarget)
{
this._lastObjectRendered = options.container;
options.clearColor = this.background.colorRgba;
}
if (options.clearColor)
{
const isRGBAArray = Array.isArray(options.clearColor) && options.clearColor.length === 4;
options.clearColor = isRGBAArray ? options.clearColor : Color.shared.setValue(options.clearColor).toArray();
}
if (!options.transform)
{
options.container.updateLocalTransform();
options.transform = options.container.localTransform;
}
this.runners.prerender.emit(options);
this.runners.renderStart.emit(options);
this.runners.render.emit(options);
this.runners.renderEnd.emit(options);
this.runners.postrender.emit(options);
}
清除
clear
方法的主要目的是清除当前渲染目标,准备下一次渲染。它确保画布在每次渲染之前处于干净的状态。工作流程:
- 参数处理:方法接受一个可选的参数 options,该参数用于指定清除的目标、颜色和模式。如果未提供,则使用默认设置。
- 设置目标:确定要清除的渲染目标。如果没有指定目标,则使用当前的渲染目标。
- 设置清除颜色:如果未提供清除颜色,则使用背景颜色。
- 执行清除操作:调用渲染目标的 clear 方法,执行实际的清除操作。此步骤会根据提供的清除模式 (如全部清除或特定区域清除) 和颜色来清除画布。
public clear(options: ClearOptions = {}): void
{
const renderer = this as unknown as Renderer;
options.target ||= renderer.renderTarget.renderTarget;
options.clearColor ||= this.background.colorRgba;
options.clear ??= CLEAR.ALL;
const { clear, clearColor, target } = options;
Color.shared.setValue(clearColor ?? this.background.colorRgba);
renderer.renderTarget.clear(target, clear, Color.shared.toArray() as RgbaArray);
}
销毁
destroy
方法用于释放渲染器占用的资源,确保在不再使用渲染器时能够正确清理内存。这对于避免内存泄漏和确保应用程序稳定性至关重要。工作流程:
- 触发销毁事件:首先,方法会触发与销毁相关的运行器,允许开发者在销毁之前执行自定义逻辑。
- 销毁所有运行器:遍历所有运行器,调用它们的 destroy 方法,释放它们所占用的资源。
- 清空系统哈希:将 _systemsHash 属性设置为 null,以清除对系统的引用,帮助垃圾回收。
- 销毁所有管道:将 renderPipes 属性设置为 null,释放对渲染管道的引用。
public destroy(options: RendererDestroyOptions = false): void
{
this.runners.destroy.items.reverse();
this.runners.destroy.emit(options);
Object.values(this.runners).forEach((runner) =>
{
runner.destroy();
});
this._systemsHash = null;
(this.renderPipes as null) = null;
}
其他方法
resize 方法
调整渲染器的画布尺寸和分辨率。
工作流程:
- 首先,获取当前的分辨率。
- 调用视图系统的
resize
方法,传入新的宽度、高度和分辨率。 - 如果分辨率发生变化,触发相应的事件,通知其他系统更新。
public resize(desiredScreenWidth: number, desiredScreenHeight: number, resolution?: number): void {
const previousResolution = this.view.resolution;
this.view.resize(desiredScreenWidth, desiredScreenHeight, resolution);
this.emit('resize', this.view.screen.width, this.view.screen.height, this.view.resolution);
if (resolution !== undefined && resolution !== previousResolution) {
this.runners.resolutionChange.emit(resolution);
}
}
generateTexture 方法
从容器生成纹理,允许开发者将显示对象的内容转换为纹理以便于后续使用。
工作流程:
- 调用纹理生成器的
generateTexture
方法,传入选项或容器。 - 返回生成的纹理对象。
public generateTexture(options: GenerateTextureOptions | Container): Texture {
return this.textureGenerator.generateTexture(options);
}
_addRunners 方法
添加运行器(Runners),用于管理渲染器生命周期各个阶段的系统调用。
工作流程:
- 遍历提供的运行器 ID,创建相应的
SystemRunner
实例并存储。
private _addRunners(...runnerIds: string[]): void {
runnerIds.forEach((runnerId) => {
this.runners[runnerId] = new SystemRunner(runnerId);
});
}
_addSystems 方法
将系统添加到渲染器中。
工作流程:
- 遍历系统配置,将每个系统类实例化并添加到渲染器的系统哈希表中。
private _addSystems(systems: RendererConfig['systems']): void {
for (const { value, name } of systems) {
this._addSystem(value, name);
}
}
_addSystem 方法
添加单个系统到渲染器。
工作流程:
- 创建系统实例并存储在渲染器中。
- 注册该系统到所有运行器,使其能够在渲染过程中被调用。
private _addSystem(ClassRef: SystemConstructor, name: string): this {
const system = new ClassRef(this as unknown as Renderer);
(this as any)[name] = system;
this._systemsHash[name] = system;
for (const runner of Object.values(this.runners)) {
runner.add(system);
}
return this;
}
_addPipes 方法
添加渲染管道(Render Pipes)和适配器。
工作流程:
- 遍历管道配置,创建管道实例并与适配器关联。
private _addPipes(pipes: RendererConfig['renderPipes'], pipeAdaptors: RendererConfig['renderPipeAdaptors']): void {
const adaptors = pipeAdaptors.reduce((acc, adaptor) => {
acc[adaptor.name] = adaptor.value;
return acc;
}, {} as Record<string, any>);
pipes.forEach((pipe) => {
const PipeClass = pipe.value;
const name = pipe.name;
const Adaptor = adaptors[name];
(this.renderPipes as any)[name] = new PipeClass(this as unknown as Renderer, Adaptor ? new Adaptor() : null);
});
}
_unsafeEvalCheck 方法
检查当前环境是否支持不安全的 eval 操作。
工作流程:
- 如果当前环境不支持,抛出错误,提示开发者。
public _unsafeEvalCheck(): void {
if (!unsafeEvalSupported()) {
throw new Error('Current environment does not allow unsafe-eval, please use pixi.js/unsafe-eval module to enable support.');
}
}
Runner 详解
在PixiJS渲染系统中,SystemRunner
扮演着关键的角色。runners
属性是AbstractRenderer
中重要的一部分,用于管理渲染器生命周期各个阶段的系统调用。其类型定义为:
type Runners = { [key in DefaultRunners]: SystemRunner } & {
[K: ({} & string) | ({} & symbol)]: SystemRunner;
};
这意味着每个默认的生命周期事件(如init
、destroy
、render
等)都有一个对应的SystemRunner
实例。当这些事件被触发时,SystemRunner
会通知所有注册了这些事件的系统,确保每个系统在渲染过程中的正确执行。
SystemRunner的定义
SystemRunner
是一个简单而强大的工具类,允许在多个监听对象之间分发和广播事件。每个运行器都有一个指定的事件名称,当事件被触发时,所有监听对象中的相应方法都会被调用。
构造函数
constructor(name: string)
- 功能:构造函数接受一个字符串参数
name
,用于指定运行器的名称。这个名称将用于调用添加到运行器的监听器的方法。 - 初始化:在构造函数中,
items
数组被初始化为空,用于存储添加的监听器。
添加监听器
public add(item: unknown): this
功能:该方法用于向运行器添加一个监听器。
逻辑:
- 检查传入的
item
是否具有与运行器名称相同的方法。如果有,则将该监听器添加到items
数组中。 - 在添加之前,调用
remove
方法确保该监听器不会重复添加。
- 检查传入的
触发事件
public emit(a0?: unknown, a1?: unknown, a2?: unknown, a3?: unknown, a4?: unknown, a5?: unknown, a6?: unknown, a7?: unknown): this
功能:该方法用于触发所有已添加的监听器。
逻辑:
- 遍历
items
数组,依次调用每个监听器的方法,并传递参数。 - 允许最多传递 8 个参数,增强了灵活性。
- 遍历
移除监听器
public remove(item: unknown): this
功能:从运行器中移除指定的监听器。
逻辑:
- 查找
items
数组中的索引,如果找到,则将该监听器从数组中移除。
- 查找
检查监听器
public contains(item: unknown): boolean
- 功能:检查指定的监听器是否已经在运行器中。
- 返回值:返回一个布尔值,指示监听器是否存在于
items
数组中。
清空所有监听器
public removeAll(): this
- 功能:清空运行器中的所有监听器。
- 逻辑:通过将
items
数组的长度设置为 0 来实现。
销毁
public destroy(): void
功能:释放运行器的所有资源。
逻辑:
- 调用
removeAll
方法清空所有监听器,并将items
和_name
属性设置为null
。
- 调用
其他属性
empty:
- 只读属性,指示运行器是否没有任何监听器。
name:
- 只读属性,返回运行器的名称。
架构图
下图展示了PixiJS的核心架构,包括AutoDetectRenderer
如何自动选择最佳渲染器,WebGLRenderer
和WebGPURenderer
如何分别利用WebGL和WebGPU技术进行渲染,以及AbstractRenderer
如何通过系统和管道来管理渲染过程中的各种任务。
总结
通过对PixiJS渲染系统的详细分析,我们了解了如何智能选择渲染器以适应不同的浏览器和设备,以及利用WebGL和WebGPU渲染器实现高效的图形处理。同时,我们深入探讨了AbstractRenderer类的构造和功能,理解了其在渲染过程中的角色和重要性。无论是自动检测最佳渲染器,还是具体实现WebGL和WebGPU渲染,这些知识都将帮助开发者更好地利用PixiJS创建丰富的图形和动画效果。