'use strict';

Object.defineProperty(exports, '__esModule', { value: true });

var Extensions = require('../../../../extensions/Extensions.js');
var settings = require('../../../../settings/settings.js');
var CanvasPool = require('../../shared/texture/CanvasPool.js');
var BindGroup = require('../shader/BindGroup.js');
var gpuUploadBufferImageResource = require('./uploaders/gpuUploadBufferImageResource.js');
var gpuUploadImageSource = require('./uploaders/gpuUploadImageSource.js');
var gpuUploadVideoSource = require('./uploaders/gpuUploadVideoSource.js');
var GpuMipmapGenerator = require('./utils/GpuMipmapGenerator.js');

class GpuTextureSystem {
  constructor(renderer) {
    this.managedTextures = [];
    this._gpuSources = /* @__PURE__ */ Object.create(null);
    this._gpuSamplers = /* @__PURE__ */ Object.create(null);
    this._bindGroupHash = /* @__PURE__ */ Object.create(null);
    this._textureViewHash = /* @__PURE__ */ Object.create(null);
    this._uploads = {
      image: gpuUploadImageSource.gpuUploadImageResource,
      buffer: gpuUploadBufferImageResource.gpuUploadBufferImageResource,
      video: gpuUploadVideoSource.gpuUploadVideoResource
    };
    this._renderer = renderer;
  }
  contextChange(gpu) {
    this._gpu = gpu;
  }
  initSource(source) {
    if (source.autoGenerateMipmaps) {
      const biggestDimension = Math.max(source.pixelWidth, source.pixelHeight);
      source.mipLevelCount = Math.floor(Math.log2(biggestDimension)) + 1;
    }
    const textureDescriptor = {
      size: { width: source.pixelWidth || 1, height: source.pixelHeight || 1 },
      format: source.format,
      sampleCount: source.sampleCount,
      mipLevelCount: source.mipLevelCount,
      dimension: source.dimension,
      usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST | GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC
    };
    const gpuTexture = this._gpu.device.createTexture(textureDescriptor);
    this._gpuSources[source.uid] = gpuTexture;
    source.on("update", this.onSourceUpdate, this);
    source.on("resize", this.onSourceResize, this);
    source.on("destroy", this.onSourceDestroy, this);
    source.on("unload", this.onSourceUnload, this);
    this.managedTextures.push(source);
    this.onSourceUpdate(source);
    return gpuTexture;
  }
  onSourceUpdate(source) {
    const gpuTexture = this.getGpuSource(source);
    if (!gpuTexture)
      return;
    if (this._uploads[source.uploadMethodId]) {
      this._uploads[source.uploadMethodId].upload(source, gpuTexture, this._gpu);
    }
    if (source.autoGenerateMipmaps && source.mipLevelCount > 1) {
      if (!this._mipmapGenerator) {
        this._mipmapGenerator = new GpuMipmapGenerator.GpuMipmapGenerator(this._gpu.device);
      }
      this._mipmapGenerator.generateMipmap(gpuTexture);
    }
  }
  onSourceUnload(source) {
    const gpuTexture = this._gpuSources[source.uid];
    if (gpuTexture) {
      this._gpuSources[source.uid] = null;
      gpuTexture.destroy();
    }
  }
  onSourceDestroy(source) {
    source.off("update", this.onSourceUpdate, this);
    source.off("unload", this.onSourceUnload, this);
    source.off("destroy", this.onSourceDestroy, this);
    source.off("resize", this.onSourceResize, this);
    this.managedTextures.splice(this.managedTextures.indexOf(source), 1);
    this.onSourceUnload(source);
  }
  onSourceResize(source) {
    const gpuTexture = this._gpuSources[source.uid];
    if (gpuTexture.width !== source.pixelWidth || gpuTexture.height !== source.pixelHeight) {
      this._textureViewHash[source.uid] = null;
      this._bindGroupHash[source.uid] = null;
      this.onSourceUnload(source);
      this.initSource(source);
    }
  }
  _initSampler(sampler) {
    this._gpuSamplers[sampler.resourceId] = this._gpu.device.createSampler(sampler);
    return this._gpuSamplers[sampler.resourceId];
  }
  getGpuSampler(sampler) {
    return this._gpuSamplers[sampler.resourceId] || this._initSampler(sampler);
  }
  getGpuSource(source) {
    return this._gpuSources[source.uid] || this.initSource(source);
  }
  getTextureBindGroup(texture) {
    return this._bindGroupHash[texture.id] ?? this._createTextureBindGroup(texture);
  }
  _createTextureBindGroup(texture) {
    const source = texture.source;
    const bindGroupId = source.uid;
    this._bindGroupHash[bindGroupId] = new BindGroup.BindGroup({
      0: source,
      1: source.style
    });
    return this._bindGroupHash[bindGroupId];
  }
  getTextureView(texture) {
    const source = texture.source;
    return this._textureViewHash[source.uid] ?? this._createTextureView(source);
  }
  _createTextureView(texture) {
    this._textureViewHash[texture.uid] = this.getGpuSource(texture).createView();
    return this._textureViewHash[texture.uid];
  }
  generateCanvas(texture) {
    const renderer = this._renderer;
    const commandEncoder = renderer.gpu.device.createCommandEncoder();
    const canvas = settings.settings.ADAPTER.createCanvas();
    canvas.width = texture.source.pixelWidth;
    canvas.height = texture.source.pixelHeight;
    const context = canvas.getContext("webgpu");
    context.configure({
      device: renderer.gpu.device,
      // eslint-disable-next-line max-len
      usage: GPUTextureUsage.COPY_DST | GPUTextureUsage.COPY_SRC,
      format: "bgra8unorm",
      alphaMode: "premultiplied"
    });
    commandEncoder.copyTextureToTexture({
      texture: renderer.texture.getGpuSource(texture.source),
      origin: {
        x: 0,
        y: 0
      }
    }, {
      texture: context.getCurrentTexture()
    }, {
      width: canvas.width,
      height: canvas.height
    });
    renderer.gpu.device.queue.submit([commandEncoder.finish()]);
    return canvas;
  }
  getPixels(texture) {
    const webGPUCanvas = this.generateCanvas(texture);
    const canvasAndContext = CanvasPool.CanvasPool.getOptimalCanvasAndContext(webGPUCanvas.width, webGPUCanvas.height);
    const context = canvasAndContext.context;
    context.drawImage(webGPUCanvas, 0, 0);
    const { width, height } = webGPUCanvas;
    const imageData = context.getImageData(0, 0, width, height);
    const pixels = new Uint8ClampedArray(imageData.data.buffer);
    CanvasPool.CanvasPool.returnCanvasAndContext(canvasAndContext);
    return { pixels, width, height };
  }
  destroy() {
    for (const k of Object.keys(this._gpuSources)) {
      const key = Number(k);
      const gpuTexture = this._gpuSources[key];
      gpuTexture.destroy();
      this._gpuSources[key] = null;
    }
    for (const k of Object.keys(this._bindGroupHash)) {
      const key = Number(k);
      const bindGroup = this._bindGroupHash[key];
      bindGroup.destroy();
      this._bindGroupHash[key] = null;
    }
    this._gpu = null;
    this._mipmapGenerator = null;
    this._gpuSources = null;
    this._bindGroupHash = null;
    this._textureViewHash = null;
    this._gpuSamplers = null;
  }
}
/** @ignore */
GpuTextureSystem.extension = {
  type: [
    Extensions.ExtensionType.WebGPUSystem
  ],
  name: "texture"
};

exports.GpuTextureSystem = GpuTextureSystem;
//# sourceMappingURL=GpuTextureSystem.js.map
