import BoundingRectangle from "../Core/BoundingRectangle.js";
import Color from "../Core/Color.js";
import defined from "../Core/defined.js";
import destroyObject from "../Core/destroyObject.js";
import PixelFormat from "../Core/PixelFormat.js";
import WebGLConstants from "../Core/WebGLConstants.js";
import ClearCommand from "../Renderer/ClearCommand.js";
import DrawCommand from "../Renderer/DrawCommand.js";
import Framebuffer from "../Renderer/Framebuffer.js";
import PixelDatatype from "../Renderer/PixelDatatype.js";
import RenderState from "../Renderer/RenderState.js";
import ShaderSource from "../Renderer/ShaderSource.js";
import Texture from "../Renderer/Texture.js";
import AdjustTranslucentFS from "../Shaders/AdjustTranslucentFS.js";
import CompositeOITFS from "../Shaders/CompositeOITFS.js";
import BlendEquation from "./BlendEquation.js";
import BlendFunction from "./BlendFunction.js";

/**
 * @private
 */
function OIT(context) {
  // We support multipass for the Chrome D3D9 backend and ES 2.0 on mobile.
  this._translucentMultipassSupport = false;
  this._translucentMRTSupport = false;

  var extensionsSupported = context.colorBufferFloat && context.depthTexture;
  this._translucentMRTSupport = context.drawBuffers && extensionsSupported;
  this._translucentMultipassSupport =
    !this._translucentMRTSupport && extensionsSupported;

  this._opaqueFBO = undefined;
  this._opaqueTexture = undefined;
  this._depthStencilTexture = undefined;

  this._accumulationTexture = undefined;

  this._translucentFBO = undefined;
  this._alphaFBO = undefined;

  this._adjustTranslucentFBO = undefined;
  this._adjustAlphaFBO = undefined;

  this._opaqueClearCommand = new ClearCommand({
    color: new Color(0.0, 0.0, 0.0, 0.0),
    owner: this,
  });
  this._translucentMRTClearCommand = new ClearCommand({
    color: new Color(0.0, 0.0, 0.0, 1.0),
    owner: this,
  });
  this._translucentMultipassClearCommand = new ClearCommand({
    color: new Color(0.0, 0.0, 0.0, 0.0),
    owner: this,
  });
  this._alphaClearCommand = new ClearCommand({
    color: new Color(1.0, 1.0, 1.0, 1.0),
    owner: this,
  });

  this._translucentRenderStateCache = {};
  this._alphaRenderStateCache = {};

  this._compositeCommand = undefined;
  this._adjustTranslucentCommand = undefined;
  this._adjustAlphaCommand = undefined;

  this._viewport = new BoundingRectangle();
  this._rs = undefined;

  this._useScissorTest = false;
  this._scissorRectangle = undefined;

  this._useHDR = false;
}

function destroyTextures(oit) {
  oit._accumulationTexture =
    oit._accumulationTexture &&
    !oit._accumulationTexture.isDestroyed() &&
    oit._accumulationTexture.destroy();
  oit._revealageTexture =
    oit._revealageTexture &&
    !oit._revealageTexture.isDestroyed() &&
    oit._revealageTexture.destroy();
}

function destroyFramebuffers(oit) {
  oit._translucentFBO =
    oit._translucentFBO &&
    !oit._translucentFBO.isDestroyed() &&
    oit._translucentFBO.destroy();
  oit._alphaFBO =
    oit._alphaFBO && !oit._alphaFBO.isDestroyed() && oit._alphaFBO.destroy();
  oit._adjustTranslucentFBO =
    oit._adjustTranslucentFBO &&
    !oit._adjustTranslucentFBO.isDestroyed() &&
    oit._adjustTranslucentFBO.destroy();
  oit._adjustAlphaFBO =
    oit._adjustAlphaFBO &&
    !oit._adjustAlphaFBO.isDestroyed() &&
    oit._adjustAlphaFBO.destroy();
}

function destroyResources(oit) {
  destroyTextures(oit);
  destroyFramebuffers(oit);
}

function updateTextures(oit, context, width, height) {
  destroyTextures(oit);

  oit._accumulationTexture = new Texture({
    context: context,
    width: width,
    height: height,
    pixelFormat: PixelFormat.RGBA,
    pixelDatatype: PixelDatatype.FLOAT,
  });

  // Use zeroed arraybuffer instead of null to initialize texture
  // to workaround Firefox. Only needed for the second color attachment.
  var source = new Float32Array(width * height * 4);
  oit._revealageTexture = new Texture({
    context: context,
    pixelFormat: PixelFormat.RGBA,
    pixelDatatype: PixelDatatype.FLOAT,
    source: {
      arrayBufferView: source,
      width: width,
      height: height,
    },
    flipY: false,
  });
}

function updateFramebuffers(oit, context) {
  destroyFramebuffers(oit);

  var completeFBO = WebGLConstants.FRAMEBUFFER_COMPLETE;
  var supported = true;

  // if MRT is supported, attempt to make an FBO with multiple color attachments
  if (oit._translucentMRTSupport) {
    oit._translucentFBO = new Framebuffer({
      context: context,
      colorTextures: [oit._accumulationTexture, oit._revealageTexture],
      depthStencilTexture: oit._depthStencilTexture,
      destroyAttachments: false,
    });
    oit._adjustTranslucentFBO = new Framebuffer({
      context: context,
      colorTextures: [oit._accumulationTexture, oit._revealageTexture],
      destroyAttachments: false,
    });

    if (
      oit._translucentFBO.status !== completeFBO ||
      oit._adjustTranslucentFBO.status !== completeFBO
    ) {
      destroyFramebuffers(oit);
      oit._translucentMRTSupport = false;
    }
  }

  // either MRT isn't supported or FBO creation failed, attempt multipass
  if (!oit._translucentMRTSupport) {
    oit._translucentFBO = new Framebuffer({
      context: context,
      colorTextures: [oit._accumulationTexture],
      depthStencilTexture: oit._depthStencilTexture,
      destroyAttachments: false,
    });
    oit._alphaFBO = new Framebuffer({
      context: context,
      colorTextures: [oit._revealageTexture],
      depthStencilTexture: oit._depthStencilTexture,
      destroyAttachments: false,
    });
    oit._adjustTranslucentFBO = new Framebuffer({
      context: context,
      colorTextures: [oit._accumulationTexture],
      destroyAttachments: false,
    });
    oit._adjustAlphaFBO = new Framebuffer({
      context: context,
      colorTextures: [oit._revealageTexture],
      destroyAttachments: false,
    });

    var translucentComplete = oit._translucentFBO.status === completeFBO;
    var alphaComplete = oit._alphaFBO.status === completeFBO;
    var adjustTranslucentComplete =
      oit._adjustTranslucentFBO.status === completeFBO;
    var adjustAlphaComplete = oit._adjustAlphaFBO.status === completeFBO;
    if (
      !translucentComplete ||
      !alphaComplete ||
      !adjustTranslucentComplete ||
      !adjustAlphaComplete
    ) {
      destroyResources(oit);
      oit._translucentMultipassSupport = false;
      supported = false;
    }
  }

  return supported;
}

OIT.prototype.update = function (context, passState, framebuffer, useHDR) {
  if (!this.isSupported()) {
    return;
  }

  this._opaqueFBO = framebuffer;
  this._opaqueTexture = framebuffer.getColorTexture(0);
  this._depthStencilTexture = framebuffer.depthStencilTexture;

  var width = this._opaqueTexture.width;
  var height = this._opaqueTexture.height;

  var accumulationTexture = this._accumulationTexture;
  var textureChanged =
    !defined(accumulationTexture) ||
    accumulationTexture.width !== width ||
    accumulationTexture.height !== height ||
    useHDR !== this._useHDR;
  if (textureChanged) {
    updateTextures(this, context, width, height);
  }

  if (!defined(this._translucentFBO) || textureChanged) {
    if (!updateFramebuffers(this, context)) {
      // framebuffer creation failed
      return;
    }
  }

  this._useHDR = useHDR;

  var that = this;
  var fs;
  var uniformMap;

  if (!defined(this._compositeCommand)) {
    fs = new ShaderSource({
      sources: [CompositeOITFS],
    });
    if (this._translucentMRTSupport) {
      fs.defines.push("MRT");
    }

    uniformMap = {
      u_opaque: function () {
        return that._opaqueTexture;
      },
      u_accumulation: function () {
        return that._accumulationTexture;
      },
      u_revealage: function () {
        return that._revealageTexture;
      },
    };
    this._compositeCommand = context.createViewportQuadCommand(fs, {
      uniformMap: uniformMap,
      owner: this,
    });
  }

  if (!defined(this._adjustTranslucentCommand)) {
    if (this._translucentMRTSupport) {
      fs = new ShaderSource({
        defines: ["MRT"],
        sources: [AdjustTranslucentFS],
      });

      uniformMap = {
        u_bgColor: function () {
          return that._translucentMRTClearCommand.color;
        },
        u_depthTexture: function () {
          return that._depthStencilTexture;
        },
      };

      this._adjustTranslucentCommand = context.createViewportQuadCommand(fs, {
        uniformMap: uniformMap,
        owner: this,
      });
    } else if (this._translucentMultipassSupport) {
      fs = new ShaderSource({
        sources: [AdjustTranslucentFS],
      });

      uniformMap = {
        u_bgColor: function () {
          return that._translucentMultipassClearCommand.color;
        },
        u_depthTexture: function () {
          return that._depthStencilTexture;
        },
      };

      this._adjustTranslucentCommand = context.createViewportQuadCommand(fs, {
        uniformMap: uniformMap,
        owner: this,
      });

      uniformMap = {
        u_bgColor: function () {
          return that._alphaClearCommand.color;
        },
        u_depthTexture: function () {
          return that._depthStencilTexture;
        },
      };

      this._adjustAlphaCommand = context.createViewportQuadCommand(fs, {
        uniformMap: uniformMap,
        owner: this,
      });
    }
  }

  this._viewport.width = width;
  this._viewport.height = height;

  var useScissorTest = !BoundingRectangle.equals(
    this._viewport,
    passState.viewport
  );
  var updateScissor = useScissorTest !== this._useScissorTest;
  this._useScissorTest = useScissorTest;

  if (!BoundingRectangle.equals(this._scissorRectangle, passState.viewport)) {
    this._scissorRectangle = BoundingRectangle.clone(
      passState.viewport,
      this._scissorRectangle
    );
    updateScissor = true;
  }

  if (
    !defined(this._rs) ||
    !BoundingRectangle.equals(this._viewport, this._rs.viewport) ||
    updateScissor
  ) {
    this._rs = RenderState.fromCache({
      viewport: this._viewport,
      scissorTest: {
        enabled: this._useScissorTest,
        rectangle: this._scissorRectangle,
      },
    });
  }

  if (defined(this._compositeCommand)) {
    this._compositeCommand.renderState = this._rs;
  }

  if (this._adjustTranslucentCommand) {
    this._adjustTranslucentCommand.renderState = this._rs;
  }

  if (defined(this._adjustAlphaCommand)) {
    this._adjustAlphaCommand.renderState = this._rs;
  }
};

var translucentMRTBlend = {
  enabled: true,
  color: new Color(0.0, 0.0, 0.0, 0.0),
  equationRgb: BlendEquation.ADD,
  equationAlpha: BlendEquation.ADD,
  functionSourceRgb: BlendFunction.ONE,
  functionDestinationRgb: BlendFunction.ONE,
  functionSourceAlpha: BlendFunction.ZERO,
  functionDestinationAlpha: BlendFunction.ONE_MINUS_SOURCE_ALPHA,
};

var translucentColorBlend = {
  enabled: true,
  color: new Color(0.0, 0.0, 0.0, 0.0),
  equationRgb: BlendEquation.ADD,
  equationAlpha: BlendEquation.ADD,
  functionSourceRgb: BlendFunction.ONE,
  functionDestinationRgb: BlendFunction.ONE,
  functionSourceAlpha: BlendFunction.ONE,
  functionDestinationAlpha: BlendFunction.ONE,
};

var translucentAlphaBlend = {
  enabled: true,
  color: new Color(0.0, 0.0, 0.0, 0.0),
  equationRgb: BlendEquation.ADD,
  equationAlpha: BlendEquation.ADD,
  functionSourceRgb: BlendFunction.ZERO,
  functionDestinationRgb: BlendFunction.ONE_MINUS_SOURCE_ALPHA,
  functionSourceAlpha: BlendFunction.ZERO,
  functionDestinationAlpha: BlendFunction.ONE_MINUS_SOURCE_ALPHA,
};

function getTranslucentRenderState(
  context,
  translucentBlending,
  cache,
  renderState
) {
  var translucentState = cache[renderState.id];
  if (!defined(translucentState)) {
    var rs = RenderState.getState(renderState);
    rs.depthMask = false;
    rs.blending = translucentBlending;

    translucentState = RenderState.fromCache(rs);
    cache[renderState.id] = translucentState;
  }

  return translucentState;
}

function getTranslucentMRTRenderState(oit, context, renderState) {
  return getTranslucentRenderState(
    context,
    translucentMRTBlend,
    oit._translucentRenderStateCache,
    renderState
  );
}

function getTranslucentColorRenderState(oit, context, renderState) {
  return getTranslucentRenderState(
    context,
    translucentColorBlend,
    oit._translucentRenderStateCache,
    renderState
  );
}

function getTranslucentAlphaRenderState(oit, context, renderState) {
  return getTranslucentRenderState(
    context,
    translucentAlphaBlend,
    oit._alphaRenderStateCache,
    renderState
  );
}

var mrtShaderSource =
  "    vec3 Ci = czm_gl_FragColor.rgb * czm_gl_FragColor.a;\n" +
  "    float ai = czm_gl_FragColor.a;\n" +
  "    float wzi = czm_alphaWeight(ai);\n" +
  "    gl_FragData[0] = vec4(Ci * wzi, ai);\n" +
  "    gl_FragData[1] = vec4(ai * wzi);\n";

var colorShaderSource =
  "    vec3 Ci = czm_gl_FragColor.rgb * czm_gl_FragColor.a;\n" +
  "    float ai = czm_gl_FragColor.a;\n" +
  "    float wzi = czm_alphaWeight(ai);\n" +
  "    gl_FragColor = vec4(Ci, ai) * wzi;\n";

var alphaShaderSource =
  "    float ai = czm_gl_FragColor.a;\n" + "    gl_FragColor = vec4(ai);\n";

function getTranslucentShaderProgram(context, shaderProgram, keyword, source) {
  var shader = context.shaderCache.getDerivedShaderProgram(
    shaderProgram,
    keyword
  );
  if (!defined(shader)) {
    var attributeLocations = shaderProgram._attributeLocations;

    var fs = shaderProgram.fragmentShaderSource.clone();

    fs.sources = fs.sources.map(function (source) {
      source = ShaderSource.replaceMain(source, "czm_translucent_main");
      source = source.replace(/gl_FragColor/g, "czm_gl_FragColor");
      source = source.replace(/\bdiscard\b/g, "czm_discard = true");
      source = source.replace(/czm_phong/g, "czm_translucentPhong");
      return source;
    });

    // Discarding the fragment in main is a workaround for ANGLE D3D9
    // shader compilation errors.

    fs.sources.splice(
      0,
      0,
      (source.indexOf("gl_FragData") !== -1
        ? "#extension GL_EXT_draw_buffers : enable \n"
        : "") +
        "vec4 czm_gl_FragColor;\n" +
        "bool czm_discard = false;\n"
    );

    fs.sources.push(
      "void main()\n" +
        "{\n" +
        "    czm_translucent_main();\n" +
        "    if (czm_discard)\n" +
        "    {\n" +
        "        discard;\n" +
        "    }\n" +
        source +
        "}\n"
    );

    shader = context.shaderCache.createDerivedShaderProgram(
      shaderProgram,
      keyword,
      {
        vertexShaderSource: shaderProgram.vertexShaderSource,
        fragmentShaderSource: fs,
        attributeLocations: attributeLocations,
      }
    );
  }

  return shader;
}

function getTranslucentMRTShaderProgram(context, shaderProgram) {
  return getTranslucentShaderProgram(
    context,
    shaderProgram,
    "translucentMRT",
    mrtShaderSource
  );
}

function getTranslucentColorShaderProgram(context, shaderProgram) {
  return getTranslucentShaderProgram(
    context,
    shaderProgram,
    "translucentMultipass",
    colorShaderSource
  );
}

function getTranslucentAlphaShaderProgram(context, shaderProgram) {
  return getTranslucentShaderProgram(
    context,
    shaderProgram,
    "alphaMultipass",
    alphaShaderSource
  );
}

OIT.prototype.createDerivedCommands = function (command, context, result) {
  if (!defined(result)) {
    result = {};
  }

  if (this._translucentMRTSupport) {
    var translucentShader;
    var translucentRenderState;
    if (defined(result.translucentCommand)) {
      translucentShader = result.translucentCommand.shaderProgram;
      translucentRenderState = result.translucentCommand.renderState;
    }

    result.translucentCommand = DrawCommand.shallowClone(
      command,
      result.translucentCommand
    );

    if (
      !defined(translucentShader) ||
      result.shaderProgramId !== command.shaderProgram.id
    ) {
      result.translucentCommand.shaderProgram = getTranslucentMRTShaderProgram(
        context,
        command.shaderProgram
      );
      result.translucentCommand.renderState = getTranslucentMRTRenderState(
        this,
        context,
        command.renderState
      );
      result.shaderProgramId = command.shaderProgram.id;
    } else {
      result.translucentCommand.shaderProgram = translucentShader;
      result.translucentCommand.renderState = translucentRenderState;
    }
  } else {
    var colorShader;
    var colorRenderState;
    var alphaShader;
    var alphaRenderState;
    if (defined(result.translucentCommand)) {
      colorShader = result.translucentCommand.shaderProgram;
      colorRenderState = result.translucentCommand.renderState;
      alphaShader = result.alphaCommand.shaderProgram;
      alphaRenderState = result.alphaCommand.renderState;
    }

    result.translucentCommand = DrawCommand.shallowClone(
      command,
      result.translucentCommand
    );
    result.alphaCommand = DrawCommand.shallowClone(
      command,
      result.alphaCommand
    );

    if (
      !defined(colorShader) ||
      result.shaderProgramId !== command.shaderProgram.id
    ) {
      result.translucentCommand.shaderProgram = getTranslucentColorShaderProgram(
        context,
        command.shaderProgram
      );
      result.translucentCommand.renderState = getTranslucentColorRenderState(
        this,
        context,
        command.renderState
      );
      result.alphaCommand.shaderProgram = getTranslucentAlphaShaderProgram(
        context,
        command.shaderProgram
      );
      result.alphaCommand.renderState = getTranslucentAlphaRenderState(
        this,
        context,
        command.renderState
      );
      result.shaderProgramId = command.shaderProgram.id;
    } else {
      result.translucentCommand.shaderProgram = colorShader;
      result.translucentCommand.renderState = colorRenderState;
      result.alphaCommand.shaderProgram = alphaShader;
      result.alphaCommand.renderState = alphaRenderState;
    }
  }

  return result;
};

function executeTranslucentCommandsSortedMultipass(
  oit,
  scene,
  executeFunction,
  passState,
  commands,
  invertClassification
) {
  var command;
  var derivedCommand;
  var j;

  var context = scene.context;
  var useLogDepth = scene.frameState.useLogDepth;
  var useHdr = scene._hdr;
  var framebuffer = passState.framebuffer;
  var length = commands.length;

  var lightShadowsEnabled = scene.frameState.shadowState.lightShadowsEnabled;

  passState.framebuffer = oit._adjustTranslucentFBO;
  oit._adjustTranslucentCommand.execute(context, passState);
  passState.framebuffer = oit._adjustAlphaFBO;
  oit._adjustAlphaCommand.execute(context, passState);

  var debugFramebuffer = oit._opaqueFBO;
  passState.framebuffer = oit._translucentFBO;

  for (j = 0; j < length; ++j) {
    command = commands[j];
    command = useLogDepth ? command.derivedCommands.logDepth.command : command;
    command = useHdr ? command.derivedCommands.hdr.command : command;
    derivedCommand =
      lightShadowsEnabled && command.receiveShadows
        ? command.derivedCommands.oit.shadows.translucentCommand
        : command.derivedCommands.oit.translucentCommand;
    executeFunction(
      derivedCommand,
      scene,
      context,
      passState,
      debugFramebuffer
    );
  }

  if (defined(invertClassification)) {
    command = invertClassification.unclassifiedCommand;
    derivedCommand =
      lightShadowsEnabled && command.receiveShadows
        ? command.derivedCommands.oit.shadows.translucentCommand
        : command.derivedCommands.oit.translucentCommand;
    executeFunction(
      derivedCommand,
      scene,
      context,
      passState,
      debugFramebuffer
    );
  }

  passState.framebuffer = oit._alphaFBO;

  for (j = 0; j < length; ++j) {
    command = commands[j];
    command = useLogDepth ? command.derivedCommands.logDepth.command : command;
    command = useHdr ? command.derivedCommands.hdr.command : command;
    derivedCommand =
      lightShadowsEnabled && command.receiveShadows
        ? command.derivedCommands.oit.shadows.alphaCommand
        : command.derivedCommands.oit.alphaCommand;
    executeFunction(
      derivedCommand,
      scene,
      context,
      passState,
      debugFramebuffer
    );
  }

  if (defined(invertClassification)) {
    command = invertClassification.unclassifiedCommand;
    derivedCommand =
      lightShadowsEnabled && command.receiveShadows
        ? command.derivedCommands.oit.shadows.alphaCommand
        : command.derivedCommands.oit.alphaCommand;
    executeFunction(
      derivedCommand,
      scene,
      context,
      passState,
      debugFramebuffer
    );
  }

  passState.framebuffer = framebuffer;
}

function executeTranslucentCommandsSortedMRT(
  oit,
  scene,
  executeFunction,
  passState,
  commands,
  invertClassification
) {
  var context = scene.context;
  var useLogDepth = scene.frameState.useLogDepth;
  var useHdr = scene._hdr;
  var framebuffer = passState.framebuffer;
  var length = commands.length;

  var lightShadowsEnabled = scene.frameState.shadowState.lightShadowsEnabled;

  passState.framebuffer = oit._adjustTranslucentFBO;
  oit._adjustTranslucentCommand.execute(context, passState);

  var debugFramebuffer = oit._opaqueFBO;
  passState.framebuffer = oit._translucentFBO;

  var command;
  var derivedCommand;

  for (var j = 0; j < length; ++j) {
    command = commands[j];
    command = useLogDepth ? command.derivedCommands.logDepth.command : command;
    command = useHdr ? command.derivedCommands.hdr.command : command;
    derivedCommand =
      lightShadowsEnabled && command.receiveShadows
        ? command.derivedCommands.oit.shadows.translucentCommand
        : command.derivedCommands.oit.translucentCommand;
    executeFunction(
      derivedCommand,
      scene,
      context,
      passState,
      debugFramebuffer
    );
  }

  if (defined(invertClassification)) {
    command = invertClassification.unclassifiedCommand;
    derivedCommand =
      lightShadowsEnabled && command.receiveShadows
        ? command.derivedCommands.oit.shadows.translucentCommand
        : command.derivedCommands.oit.translucentCommand;
    executeFunction(
      derivedCommand,
      scene,
      context,
      passState,
      debugFramebuffer
    );
  }

  passState.framebuffer = framebuffer;
}

OIT.prototype.executeCommands = function (
  scene,
  executeFunction,
  passState,
  commands,
  invertClassification
) {
  if (this._translucentMRTSupport) {
    executeTranslucentCommandsSortedMRT(
      this,
      scene,
      executeFunction,
      passState,
      commands,
      invertClassification
    );
    return;
  }

  executeTranslucentCommandsSortedMultipass(
    this,
    scene,
    executeFunction,
    passState,
    commands,
    invertClassification
  );
};

OIT.prototype.execute = function (context, passState) {
  this._compositeCommand.execute(context, passState);
};

OIT.prototype.clear = function (context, passState, clearColor) {
  var framebuffer = passState.framebuffer;

  passState.framebuffer = this._opaqueFBO;
  Color.clone(clearColor, this._opaqueClearCommand.color);
  this._opaqueClearCommand.execute(context, passState);

  passState.framebuffer = this._translucentFBO;
  var translucentClearCommand = this._translucentMRTSupport
    ? this._translucentMRTClearCommand
    : this._translucentMultipassClearCommand;
  translucentClearCommand.execute(context, passState);

  if (this._translucentMultipassSupport) {
    passState.framebuffer = this._alphaFBO;
    this._alphaClearCommand.execute(context, passState);
  }

  passState.framebuffer = framebuffer;
};

OIT.prototype.isSupported = function () {
  return this._translucentMRTSupport || this._translucentMultipassSupport;
};

OIT.prototype.isDestroyed = function () {
  return false;
};

OIT.prototype.destroy = function () {
  destroyResources(this);

  if (defined(this._compositeCommand)) {
    this._compositeCommand.shaderProgram =
      this._compositeCommand.shaderProgram &&
      this._compositeCommand.shaderProgram.destroy();
  }

  if (defined(this._adjustTranslucentCommand)) {
    this._adjustTranslucentCommand.shaderProgram =
      this._adjustTranslucentCommand.shaderProgram &&
      this._adjustTranslucentCommand.shaderProgram.destroy();
  }

  if (defined(this._adjustAlphaCommand)) {
    this._adjustAlphaCommand.shaderProgram =
      this._adjustAlphaCommand.shaderProgram &&
      this._adjustAlphaCommand.shaderProgram.destroy();
  }

  return destroyObject(this);
};
export default OIT;
