Source: packages/core/src/render/ObjectRendererSystem.ts

import { extensions, ExtensionType } from '@pixi/extensions';

import type { ExtensionMetadata } from '@pixi/extensions';
import type { Matrix } from '@pixi/math';
import type { IRenderableObject, IRendererRenderOptions } from '../IRenderer';
import type { Renderer } from '../Renderer';
import type { RenderTexture } from '../renderTexture/RenderTexture';
import type { ISystem } from '../system/ISystem';

/**
 * system that provides a render function that focussing on rendering Pixi Scene Graph objects
 * to either the main view or to a renderTexture.  Used for Canvas `WebGL` contexts
 * @memberof PIXI
 */
export class ObjectRendererSystem implements ISystem
{
    /** @ignore */
    static extension: ExtensionMetadata = {
        type: ExtensionType.RendererSystem,
        name: 'objectRenderer',
    };

    renderer: Renderer;

    /**
     * Flag if we are rendering to the screen vs renderTexture
     * @readonly
     * @default true
     */
    renderingToScreen: boolean;

    /**
     * the last object rendered by the renderer. Useful for other plugins like interaction managers
     * @readonly
     */
    lastObjectRendered: IRenderableObject;

    // renderers scene graph!
    constructor(renderer: Renderer)
    {
        this.renderer = renderer;
    }

    /**
     * Renders the object to its WebGL view.
     * @param displayObject - The object to be rendered.
     * @param options - the options to be passed to the renderer
     */
    render(displayObject: IRenderableObject, options?: IRendererRenderOptions): void
    {
        const renderer = this.renderer;

        let renderTexture: RenderTexture;
        let clear: boolean;
        let transform: Matrix;
        let skipUpdateTransform: boolean;

        if (options)
        {
            renderTexture = options.renderTexture;
            clear = options.clear;
            transform = options.transform;
            skipUpdateTransform = options.skipUpdateTransform;
        }

        // can be handy to know!
        this.renderingToScreen = !renderTexture;

        renderer.runners.prerender.emit();
        renderer.emit('prerender');

        // apply a transform at a GPU level
        renderer.projection.transform = transform;

        // no point rendering if our context has been blown up!
        if (renderer.context.isLost)
        {
            return;
        }

        if (!renderTexture)
        {
            this.lastObjectRendered = displayObject;
        }

        if (!skipUpdateTransform)
        {
            // update the scene graph
            const cacheParent = displayObject.enableTempParent();

            displayObject.updateTransform();
            displayObject.disableTempParent(cacheParent);
            // displayObject.hitArea = //TODO add a temp hit area
        }

        renderer.renderTexture.bind(renderTexture);
        renderer.batch.currentRenderer.start();

        if (clear ?? renderer.background.clearBeforeRender)
        {
            renderer.renderTexture.clear();
        }

        displayObject.render(renderer);

        // apply transform..
        renderer.batch.currentRenderer.flush();

        if (renderTexture)
        {
            if (options.blit)
            {
                renderer.framebuffer.blit();
            }

            renderTexture.baseTexture.update();
        }

        renderer.runners.postrender.emit();

        // reset transform after render
        renderer.projection.transform = null;

        renderer.emit('postrender');
    }

    destroy(): void
    {
        // ka pow!
        this.renderer = null;
        this.lastObjectRendered = null;
    }
}

extensions.add(ObjectRendererSystem);