import { ExtensionType } from '../../../../extensions/Extensions.mjs';
import { Matrix } from '../../../../maths/Matrix.mjs';
import { CLEAR } from '../../gl/const.mjs';
import { isRenderingToScreen } from '../../shared/renderTarget/isRenderingToScreen.mjs';
import { RenderTarget } from '../../shared/renderTarget/RenderTarget.mjs';
import { SystemRunner } from '../../shared/system/SystemRunner.mjs';
import { TextureSource } from '../../shared/texture/sources/TextureSource.mjs';
import { Texture } from '../../shared/texture/Texture.mjs';
import { getCanvasTexture } from '../../shared/texture/utils/getCanvasTexture.mjs';
import { GpuRenderTarget } from './GpuRenderTarget.mjs';

class GpuRenderTargetSystem {
  constructor(renderer) {
    this.rootProjectionMatrix = new Matrix();
    this.onRenderTargetChange = new SystemRunner("onRenderTargetChange");
    this._renderSurfaceToRenderTargetHash = /* @__PURE__ */ new Map();
    this._gpuRenderTargetHash = /* @__PURE__ */ Object.create(null);
    this._renderTargetStack = [];
    this._defaultClearColor = [0, 0, 0, 0];
    this._renderer = renderer;
  }
  renderStart({
    target,
    clear,
    clearColor
  }) {
    this.rootRenderTarget = this.getRenderTarget(target);
    this.rootProjectionMatrix = this.rootRenderTarget.projectionMatrix;
    this.renderingToScreen = isRenderingToScreen(this.rootRenderTarget);
    this._renderTargetStack.length = 0;
    this._renderer.encoder.start();
    this.push(
      this.rootRenderTarget,
      clear,
      clearColor ?? this._renderer.background.colorRgba
    );
  }
  contextChange(gpu) {
    this._gpu = gpu;
  }
  bind(renderSurface, clear = true, clearColor) {
    const renderTarget = this.getRenderTarget(renderSurface);
    const didChange = this.renderTarget !== renderTarget;
    this.renderTarget = renderTarget;
    this._startRenderPass(clear, clearColor);
    if (didChange) {
      this.onRenderTargetChange.emit(renderTarget);
    }
    return renderTarget;
  }
  /**
   * returns the gpu texture for the first color texture in the render target
   * mainly used by the filter manager to get copy the texture for blending
   * @param renderTarget
   * @returns a gpu texture
   */
  _getGpuColorTexture(renderTarget) {
    const gpuRenderTarget = this._getGpuRenderTarget(renderTarget);
    if (gpuRenderTarget.contexts[0]) {
      return gpuRenderTarget.contexts[0].getCurrentTexture();
    }
    return this._renderer.texture.getGpuSource(
      renderTarget.colorTextures[0].source
    );
  }
  getDescriptor(renderTarget, clear, clearValue) {
    if (typeof clear === "boolean") {
      clear = clear ? CLEAR.ALL : CLEAR.NONE;
    }
    const gpuRenderTarget = this._getGpuRenderTarget(renderTarget);
    const colorAttachments = renderTarget.colorTextures.map(
      (texture, i) => {
        const context = gpuRenderTarget.contexts[i];
        let view;
        let resolveTarget;
        if (context) {
          const currentTexture = context.getCurrentTexture();
          const canvasTextureView = currentTexture.createView();
          view = canvasTextureView;
        } else {
          view = this._renderer.texture.getTextureView(texture);
        }
        if (gpuRenderTarget.msaaTextures[i]) {
          resolveTarget = view;
          view = this._renderer.texture.getTextureView(
            gpuRenderTarget.msaaTextures[i]
          );
        }
        const loadOp = clear & CLEAR.COLOR ? "clear" : "load";
        clearValue ?? (clearValue = this._defaultClearColor);
        return {
          view,
          // assign each frame based on the swap chain!
          resolveTarget,
          clearValue,
          storeOp: "store",
          loadOp
        };
      }
    );
    let depthStencilAttachment;
    if (renderTarget.depthTexture) {
      const stencilLoadOp = clear & CLEAR.STENCIL ? "clear" : "load";
      depthStencilAttachment = {
        view: this._renderer.texture.getGpuSource(renderTarget.depthTexture.source).createView(),
        stencilStoreOp: "store",
        stencilLoadOp
      };
    }
    const descriptor = {
      colorAttachments,
      depthStencilAttachment
    };
    return descriptor;
  }
  clear(clear = CLEAR.ALL, clearColor) {
    if (!clear)
      return;
    this._startRenderPass(
      clear,
      clearColor
    );
  }
  push(renderSurface, clear = CLEAR.ALL, clearColor) {
    const renderTarget = this.bind(renderSurface, clear, clearColor);
    this._renderTargetStack.push(renderTarget);
    return renderTarget;
  }
  pop() {
    this._renderTargetStack.pop();
    this.bind(
      this._renderTargetStack[this._renderTargetStack.length - 1],
      false
    );
  }
  getRenderTarget(renderSurface) {
    return this._renderSurfaceToRenderTargetHash.get(renderSurface) ?? this._initRenderTarget(renderSurface);
  }
  copyToTexture(sourceRenderSurfaceTexture, destinationTexture, origin, size) {
    const renderer = this._renderer;
    const baseGpuTexture = renderer.renderTarget._getGpuColorTexture(
      sourceRenderSurfaceTexture
    );
    const backGpuTexture = renderer.texture.getGpuSource(
      destinationTexture.source
    );
    renderer.encoder.commandEncoder.copyTextureToTexture(
      {
        texture: baseGpuTexture,
        origin
      },
      {
        texture: backGpuTexture
      },
      size
    );
    return destinationTexture;
  }
  restart() {
    this.bind(this.rootRenderTarget, CLEAR.NONE);
  }
  destroy() {
    const writeable = this;
    writeable._renderer = null;
    this._renderSurfaceToRenderTargetHash.clear();
  }
  _startRenderPass(clear = true, clearColor) {
    const renderTarget = this.renderTarget;
    const gpuRenderTarget = this._getGpuRenderTarget(renderTarget);
    if (renderTarget.width !== gpuRenderTarget.width || renderTarget.height !== gpuRenderTarget.height) {
      this._resizeGpuRenderTarget(renderTarget);
    }
    const descriptor = this.getDescriptor(renderTarget, clear, clearColor);
    gpuRenderTarget.descriptor = descriptor;
    this._renderer.encoder.beginRenderPass(renderTarget, gpuRenderTarget);
    this._renderer.pipeline.setMultisampleCount(gpuRenderTarget.msaaSamples);
  }
  _initRenderTarget(renderSurface) {
    let renderTarget = null;
    if (renderSurface instanceof HTMLCanvasElement) {
      renderSurface = getCanvasTexture(renderSurface);
    }
    if (renderSurface instanceof RenderTarget) {
      renderTarget = renderSurface;
    } else if (renderSurface instanceof Texture) {
      renderTarget = new RenderTarget({
        colorTextures: [renderSurface],
        depthTexture: renderSurface.source.depthStencil
      });
    }
    renderTarget.isRoot = true;
    this._renderSurfaceToRenderTargetHash.set(renderSurface, renderTarget);
    return renderTarget;
  }
  _getGpuRenderTarget(renderTarget) {
    return this._gpuRenderTargetHash[renderTarget.uid] || this._initGpuRenderTarget(renderTarget);
  }
  _initGpuRenderTarget(renderTarget) {
    renderTarget.isRoot = true;
    const gpuRenderTarget = new GpuRenderTarget();
    renderTarget.colorTextures.forEach((colorTexture, i) => {
      if (colorTexture.source.resource instanceof HTMLCanvasElement) {
        const context = renderTarget.colorTexture.source.resource.getContext(
          "webgpu"
        );
        try {
          context.configure({
            device: this._gpu.device,
            // eslint-disable-next-line max-len
            usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST | GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC,
            format: "bgra8unorm",
            alphaMode: "opaque"
          });
        } catch (e) {
          console.error(e);
        }
        gpuRenderTarget.contexts[i] = context;
      }
      gpuRenderTarget.msaa = colorTexture.source.antialias;
      if (colorTexture.source.antialias) {
        const msaaTexture = new TextureSource({
          width: 0,
          height: 0,
          sampleCount: 4
        });
        gpuRenderTarget.msaaTextures[i] = msaaTexture;
      }
    });
    if (gpuRenderTarget.msaa) {
      gpuRenderTarget.msaaSamples = 4;
      if (renderTarget.depthTexture) {
        renderTarget.depthTexture.source.sampleCount = 4;
      }
    }
    this._gpuRenderTargetHash[renderTarget.uid] = gpuRenderTarget;
    return gpuRenderTarget;
  }
  _resizeGpuRenderTarget(renderTarget) {
    const gpuRenderTarget = this._getGpuRenderTarget(renderTarget);
    gpuRenderTarget.width = renderTarget.width;
    gpuRenderTarget.height = renderTarget.height;
    if (gpuRenderTarget.msaa) {
      renderTarget.colorTextures.forEach((colorTexture, i) => {
        const msaaTexture = gpuRenderTarget.msaaTextures[i];
        msaaTexture?.resize(
          colorTexture.source.width,
          colorTexture.source.height,
          colorTexture.source._resolution
        );
      });
    }
  }
}
/** @ignore */
GpuRenderTargetSystem.extension = {
  type: [ExtensionType.WebGPUSystem],
  name: "renderTarget"
};

export { GpuRenderTargetSystem };
//# sourceMappingURL=GpuRenderTargetSystem.mjs.map
