/* SPDX-License-Identifier: GPL-2.0-or-later
 * Copyright 2021 Blender Foundation.
 */

/** \file
 * \ingroup eevee
 *
 * A film is a full-screen buffer (usually at output extent)
 * that will be able to accumulate sample in any distorted camera_type
 * using a pixel filter.
 *
 * Input needs to be jittered so that the filter converges to the right result.
 */

#include "BLI_rect.h"

#include "GPU_framebuffer.h"
#include "GPU_texture.h"

#include "DRW_render.h"

#include "eevee_film.hh"
#include "eevee_instance.hh"

namespace blender::eevee {

void RenderBuffers::acquire(int2 extent)
{
  const eViewLayerEEVEEPassType enabled_passes = inst_.film.enabled_passes_get();

  auto pass_extent = [&](eViewLayerEEVEEPassType pass_bit) -> int2 {
    /* Use dummy texture for disabled passes. Allows correct bindings. */
    return (enabled_passes & pass_bit) ? extent : int2(1);
  };

  eGPUTextureFormat color_format = GPU_RGBA16F;
  eGPUTextureFormat float_format = GPU_R16F;

  /* Depth and combined are always needed. */
  depth_tx.acquire(extent, GPU_DEPTH24_STENCIL8);
  combined_tx.acquire(extent, color_format);

  bool do_vector_render_pass = (enabled_passes & EEVEE_RENDER_PASS_VECTOR) ||
                               (inst_.motion_blur.postfx_enabled() && !inst_.is_viewport());
  uint32_t max_light_color_layer = max_ii(enabled_passes & EEVEE_RENDER_PASS_DIFFUSE_LIGHT ?
                                              int(RENDER_PASS_LAYER_DIFFUSE_LIGHT) :
                                              -1,
                                          enabled_passes & EEVEE_RENDER_PASS_SPECULAR_LIGHT ?
                                              int(RENDER_PASS_LAYER_SPECULAR_LIGHT) :
                                              -1) +
                                   1;
  /* Only RG16F when only doing only reprojection or motion blur. */
  eGPUTextureFormat vector_format = do_vector_render_pass ? GPU_RGBA16F : GPU_RG16F;
  /* TODO(fclem): Make vector pass allocation optional if no TAA or motion blur is needed. */
  vector_tx.acquire(extent, vector_format);

  normal_tx.acquire(pass_extent(EEVEE_RENDER_PASS_NORMAL), color_format);
  diffuse_color_tx.acquire(pass_extent(EEVEE_RENDER_PASS_DIFFUSE_COLOR), color_format);
  specular_color_tx.acquire(pass_extent(EEVEE_RENDER_PASS_SPECULAR_COLOR), color_format);
  volume_light_tx.acquire(pass_extent(EEVEE_RENDER_PASS_VOLUME_LIGHT), color_format);
  emission_tx.acquire(pass_extent(EEVEE_RENDER_PASS_EMIT), color_format);
  environment_tx.acquire(pass_extent(EEVEE_RENDER_PASS_ENVIRONMENT), color_format);
  shadow_tx.acquire(pass_extent(EEVEE_RENDER_PASS_SHADOW), float_format);
  ambient_occlusion_tx.acquire(pass_extent(EEVEE_RENDER_PASS_AO), float_format);

  light_tx.ensure_2d_array(color_format,
                           max_light_color_layer > 0 ? extent : int2(1),
                           max_ii(1, max_light_color_layer));

  const AOVsInfoData &aovs = inst_.film.aovs_info;
  aov_color_tx.ensure_2d_array(
      color_format, (aovs.color_len > 0) ? extent : int2(1), max_ii(1, aovs.color_len));
  aov_value_tx.ensure_2d_array(
      float_format, (aovs.value_len > 0) ? extent : int2(1), max_ii(1, aovs.value_len));

  eGPUTextureFormat cryptomatte_format = GPU_R32F;
  const int cryptomatte_layer_len = inst_.film.cryptomatte_layer_max_get();
  if (cryptomatte_layer_len == 2) {
    cryptomatte_format = GPU_RG32F;
  }
  else if (cryptomatte_layer_len == 3) {
    cryptomatte_format = GPU_RGBA32F;
  }
  cryptomatte_tx.acquire(
      pass_extent(static_cast<eViewLayerEEVEEPassType>(EEVEE_RENDER_PASS_CRYPTOMATTE_OBJECT |
                                                       EEVEE_RENDER_PASS_CRYPTOMATTE_ASSET |
                                                       EEVEE_RENDER_PASS_CRYPTOMATTE_MATERIAL)),
      cryptomatte_format);
}

void RenderBuffers::release()
{
  depth_tx.release();
  combined_tx.release();

  normal_tx.release();
  vector_tx.release();
  diffuse_color_tx.release();
  specular_color_tx.release();
  volume_light_tx.release();
  emission_tx.release();
  environment_tx.release();
  shadow_tx.release();
  ambient_occlusion_tx.release();
  cryptomatte_tx.release();
}

}  // namespace blender::eevee
