Welcome to mirror list, hosted at ThFree Co, Russian Federation.

eevee_cryptomatte.c « eevee « engines « draw « blender « source - git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 53ea66bdea020d4ac7337283df0cc9cb31561419 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
/* SPDX-License-Identifier: GPL-2.0-or-later
 * Copyright 2020 Blender Foundation. */

/** \file
 * \ingroup EEVEE
 *
 * This file implements Cryptomatte for EEVEE. Cryptomatte is used to extract mattes using
 * information already available at render time. See
 * https://raw.githubusercontent.com/Psyop/Cryptomatte/master/specification/IDmattes_poster.pdf
 * for reference to the cryptomatte specification.
 *
 * The challenge with cryptomatte in EEVEE is the merging and sorting of the samples.
 * User can enable up to 3 cryptomatte layers (Object, Material and Asset).
 *
 * Process
 *
 * - Cryptomatte sample: Rendering of a cryptomatte sample is stored in a GPUBuffer. The buffer
 * holds a single float per pixel per number of active cryptomatte layers. The float is the
 * cryptomatte hash of each layer. After drawing the cryptomatte sample the intermediate result is
 * downloaded to a CPU buffer (`cryptomatte_download_buffer`).
 *
 * Accurate mode
 *
 * There are two accuracy modes. The difference between the two is the number of render samples
 * they take into account to create the render passes. When accurate mode is off the number of
 * levels is used as the number of cryptomatte samples to take. When accuracy mode is on the number
 * of render samples is used.
 *
 */

#include "DRW_engine.h"
#include "DRW_render.h"

#include "BKE_cryptomatte.h"

#include "GPU_batch.h"

#include "RE_pipeline.h"

#include "BLI_alloca.h"
#include "BLI_math_bits.h"
#include "BLI_rect.h"

#include "DNA_curves_types.h"
#include "DNA_mesh_types.h"
#include "DNA_modifier_types.h"
#include "DNA_particle_types.h"

#include "eevee_private.h"

/* -------------------------------------------------------------------- */
/** \name Data Management cryptomatte accum buffer
 * \{ */

BLI_INLINE eViewLayerCryptomatteFlags eevee_cryptomatte_active_layers(const ViewLayer *view_layer)
{
  const eViewLayerCryptomatteFlags cryptomatte_layers = view_layer->cryptomatte_flag &
                                                        VIEW_LAYER_CRYPTOMATTE_ALL;
  return cryptomatte_layers;
}

/* The number of cryptomatte layers that are enabled */
BLI_INLINE int eevee_cryptomatte_layers_count(const ViewLayer *view_layer)
{
  const eViewLayerCryptomatteFlags cryptomatte_layers = eevee_cryptomatte_active_layers(
      view_layer);
  return count_bits_i(cryptomatte_layers);
}

/* The number of render result passes are needed to store a single cryptomatte layer. Per
 * render-pass 2 cryptomatte samples can be stored. */
BLI_INLINE int eevee_cryptomatte_passes_per_layer(const ViewLayer *view_layer)
{
  const int num_cryptomatte_levels = view_layer->cryptomatte_levels;
  const int num_cryptomatte_passes = (num_cryptomatte_levels + 1) / 2;
  return num_cryptomatte_passes;
}

BLI_INLINE int eevee_cryptomatte_layer_stride(const ViewLayer *view_layer)
{
  return view_layer->cryptomatte_levels;
}

BLI_INLINE int eevee_cryptomatte_layer_offset(const ViewLayer *view_layer, const int layer)
{
  return view_layer->cryptomatte_levels * layer;
}

BLI_INLINE int eevee_cryptomatte_pixel_stride(const ViewLayer *view_layer)
{
  return eevee_cryptomatte_layer_stride(view_layer) * eevee_cryptomatte_layers_count(view_layer);
}

/** \} */

/* -------------------------------------------------------------------- */
/** \name Init Render-Passes
 * \{ */

void EEVEE_cryptomatte_renderpasses_init(EEVEE_Data *vedata)
{
  EEVEE_StorageList *stl = vedata->stl;
  EEVEE_PrivateData *g_data = stl->g_data;

  const DRWContextState *draw_ctx = DRW_context_state_get();
  ViewLayer *view_layer = draw_ctx->view_layer;

  /* Cryptomatte is only rendered for final image renders */
  if (!DRW_state_is_scene_render()) {
    return;
  }
  const eViewLayerCryptomatteFlags active_layers = eevee_cryptomatte_active_layers(view_layer);
  if (active_layers) {
    struct CryptomatteSession *session = BKE_cryptomatte_init();
    if ((active_layers & VIEW_LAYER_CRYPTOMATTE_OBJECT) != 0) {
      BKE_cryptomatte_add_layer(session, "CryptoObject");
    }
    if ((active_layers & VIEW_LAYER_CRYPTOMATTE_MATERIAL) != 0) {
      BKE_cryptomatte_add_layer(session, "CryptoMaterial");
    }
    if ((active_layers & VIEW_LAYER_CRYPTOMATTE_ASSET) != 0) {
      BKE_cryptomatte_add_layer(session, "CryptoAsset");
    }
    g_data->cryptomatte_session = session;

    g_data->render_passes |= EEVEE_RENDER_PASS_CRYPTOMATTE | EEVEE_RENDER_PASS_VOLUME_LIGHT;
    g_data->cryptomatte_accurate_mode = (view_layer->cryptomatte_flag &
                                         VIEW_LAYER_CRYPTOMATTE_ACCURATE) != 0;
  }
}

void EEVEE_cryptomatte_output_init(EEVEE_ViewLayerData *UNUSED(sldata),
                                   EEVEE_Data *vedata,
                                   int UNUSED(tot_samples))
{
  EEVEE_FramebufferList *fbl = vedata->fbl;
  EEVEE_TextureList *txl = vedata->txl;
  EEVEE_StorageList *stl = vedata->stl;
  EEVEE_PrivateData *g_data = stl->g_data;

  DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
  const DRWContextState *draw_ctx = DRW_context_state_get();
  const ViewLayer *view_layer = draw_ctx->view_layer;

  const int num_cryptomatte_layers = eevee_cryptomatte_layers_count(view_layer);
  eGPUTextureFormat format = (num_cryptomatte_layers == 1) ? GPU_R32F :
                             (num_cryptomatte_layers == 2) ? GPU_RG32F :
                                                             GPU_RGBA32F;
  const float *viewport_size = DRW_viewport_size_get();
  const int buffer_size = viewport_size[0] * viewport_size[1];

  if (g_data->cryptomatte_accum_buffer == NULL) {
    g_data->cryptomatte_accum_buffer = MEM_calloc_arrayN(
        buffer_size * eevee_cryptomatte_pixel_stride(view_layer),
        sizeof(EEVEE_CryptomatteSample),
        __func__);
    /* Download buffer should store a float per active cryptomatte layer. */
    g_data->cryptomatte_download_buffer = MEM_malloc_arrayN(
        buffer_size * num_cryptomatte_layers, sizeof(float), __func__);
  }
  else {
    /* During multiview rendering the `cryptomatte_accum_buffer` is deallocated after all views
     * have been rendered. Clear it here to be reused by the next view. */
    memset(g_data->cryptomatte_accum_buffer,
           0,
           buffer_size * eevee_cryptomatte_pixel_stride(view_layer) *
               sizeof(EEVEE_CryptomatteSample));
  }

  DRW_texture_ensure_fullscreen_2d(&txl->cryptomatte, format, 0);
  GPU_framebuffer_ensure_config(&fbl->cryptomatte_fb,
                                {
                                    GPU_ATTACHMENT_TEXTURE(dtxl->depth),
                                    GPU_ATTACHMENT_TEXTURE(txl->cryptomatte),
                                });
}

/** \} */

/* -------------------------------------------------------------------- */
/** \name Populate Cache
 * \{ */

void EEVEE_cryptomatte_cache_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata)
{
  EEVEE_PassList *psl = vedata->psl;
  if ((vedata->stl->g_data->render_passes & EEVEE_RENDER_PASS_CRYPTOMATTE) != 0) {
    DRW_PASS_CREATE(psl->cryptomatte_ps, DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL);
  }
}

static DRWShadingGroup *eevee_cryptomatte_shading_group_create(
    EEVEE_Data *vedata, EEVEE_ViewLayerData *sldata, Object *ob, Material *material, bool is_hair)
{
  const DRWContextState *draw_ctx = DRW_context_state_get();
  const ViewLayer *view_layer = draw_ctx->view_layer;
  const eViewLayerCryptomatteFlags cryptomatte_layers = eevee_cryptomatte_active_layers(
      view_layer);
  EEVEE_PrivateData *g_data = vedata->stl->g_data;
  float cryptohash[4] = {0.0f};

  EEVEE_PassList *psl = vedata->psl;
  int layer_offset = 0;
  if ((cryptomatte_layers & VIEW_LAYER_CRYPTOMATTE_OBJECT) != 0) {
    uint32_t cryptomatte_hash = BKE_cryptomatte_object_hash(
        g_data->cryptomatte_session, "CryptoObject", ob);
    float cryptomatte_color_value = BKE_cryptomatte_hash_to_float(cryptomatte_hash);
    cryptohash[layer_offset] = cryptomatte_color_value;
    layer_offset++;
  }
  if ((cryptomatte_layers & VIEW_LAYER_CRYPTOMATTE_MATERIAL) != 0) {
    uint32_t cryptomatte_hash = BKE_cryptomatte_material_hash(
        g_data->cryptomatte_session, "CryptoMaterial", material);
    float cryptomatte_color_value = BKE_cryptomatte_hash_to_float(cryptomatte_hash);
    cryptohash[layer_offset] = cryptomatte_color_value;
    layer_offset++;
  }
  if ((cryptomatte_layers & VIEW_LAYER_CRYPTOMATTE_ASSET) != 0) {
    uint32_t cryptomatte_hash = BKE_cryptomatte_asset_hash(
        g_data->cryptomatte_session, "CryptoAsset", ob);
    float cryptomatte_color_value = BKE_cryptomatte_hash_to_float(cryptomatte_hash);
    cryptohash[layer_offset] = cryptomatte_color_value;
    layer_offset++;
  }

  DRWShadingGroup *grp = DRW_shgroup_create(EEVEE_shaders_cryptomatte_sh_get(is_hair),
                                            psl->cryptomatte_ps);
  DRW_shgroup_uniform_vec4_copy(grp, "cryptohash", cryptohash);
  DRW_shgroup_uniform_block(grp, "shadow_block", sldata->shadow_ubo);

  return grp;
}

static void eevee_cryptomatte_curves_cache_populate(EEVEE_Data *vedata,
                                                    EEVEE_ViewLayerData *sldata,
                                                    Object *ob,
                                                    ParticleSystem *psys,
                                                    ModifierData *md,
                                                    Material *material)
{
  DRWShadingGroup *grp = eevee_cryptomatte_shading_group_create(
      vedata, sldata, ob, material, true);
  DRW_shgroup_hair_create_sub(ob, psys, md, grp, NULL);
}

void EEVEE_cryptomatte_object_curves_cache_populate(EEVEE_Data *vedata,
                                                    EEVEE_ViewLayerData *sldata,
                                                    Object *ob)
{
  BLI_assert(ob->type == OB_CURVES);
  Material *material = BKE_object_material_get_eval(ob, CURVES_MATERIAL_NR);
  eevee_cryptomatte_curves_cache_populate(vedata, sldata, ob, NULL, NULL, material);
}

void EEVEE_cryptomatte_particle_hair_cache_populate(EEVEE_Data *vedata,
                                                    EEVEE_ViewLayerData *sldata,
                                                    Object *ob)
{
  const DRWContextState *draw_ctx = DRW_context_state_get();

  if (ob->type == OB_MESH) {
    if (ob != draw_ctx->object_edit) {
      LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) {
        if (md->type != eModifierType_ParticleSystem) {
          continue;
        }
        ParticleSystem *psys = ((ParticleSystemModifierData *)md)->psys;
        if (!DRW_object_is_visible_psys_in_active_context(ob, psys)) {
          continue;
        }
        ParticleSettings *part = psys->part;
        const int draw_as = (part->draw_as == PART_DRAW_REND) ? part->ren_as : part->draw_as;
        if (draw_as != PART_DRAW_PATH) {
          continue;
        }
        Material *material = BKE_object_material_get_eval(ob, part->omat);
        eevee_cryptomatte_curves_cache_populate(vedata, sldata, ob, psys, md, material);
      }
    }
  }
}

void EEVEE_cryptomatte_cache_populate(EEVEE_Data *vedata, EEVEE_ViewLayerData *sldata, Object *ob)
{
  const DRWContextState *draw_ctx = DRW_context_state_get();
  const ViewLayer *view_layer = draw_ctx->view_layer;
  const eViewLayerCryptomatteFlags cryptomatte_layers = eevee_cryptomatte_active_layers(
      view_layer);

  if ((cryptomatte_layers & VIEW_LAYER_CRYPTOMATTE_MATERIAL) != 0) {
    const int materials_len = DRW_cache_object_material_count_get(ob);
    struct GPUMaterial **gpumat_array = BLI_array_alloca(gpumat_array, materials_len);
    memset(gpumat_array, 0, sizeof(*gpumat_array) * materials_len);
    struct GPUBatch **geoms = DRW_cache_object_surface_material_get(
        ob, gpumat_array, materials_len);
    if (geoms) {
      for (int i = 0; i < materials_len; i++) {
        struct GPUBatch *geom = geoms[i];
        if (geom == NULL) {
          continue;
        }
        Material *material = BKE_object_material_get_eval(ob, i + 1);
        DRWShadingGroup *grp = eevee_cryptomatte_shading_group_create(
            vedata, sldata, ob, material, false);
        DRW_shgroup_call(grp, geom, ob);
      }
    }
  }
  else {
    GPUBatch *geom = DRW_cache_object_surface_get(ob);
    if (geom) {
      DRWShadingGroup *grp = eevee_cryptomatte_shading_group_create(
          vedata, sldata, ob, NULL, false);
      DRW_shgroup_call(grp, geom, ob);
    }
  }
}

/** \} */

/* -------------------------------------------------------------------- */
/** \name Accumulate Samples
 * \{ */

/* Downloads cryptomatte sample buffer from the GPU and integrate the samples with the accumulated
 * cryptomatte samples. */
static void eevee_cryptomatte_download_buffer(EEVEE_Data *vedata, GPUFrameBuffer *framebuffer)
{
  EEVEE_StorageList *stl = vedata->stl;
  EEVEE_PrivateData *g_data = stl->g_data;
  const DRWContextState *draw_ctx = DRW_context_state_get();
  const ViewLayer *view_layer = draw_ctx->view_layer;
  const int num_cryptomatte_layers = eevee_cryptomatte_layers_count(view_layer);
  const int num_levels = view_layer->cryptomatte_levels;
  const float *viewport_size = DRW_viewport_size_get();
  const int buffer_size = viewport_size[0] * viewport_size[1];

  EEVEE_CryptomatteSample *accum_buffer = g_data->cryptomatte_accum_buffer;
  float *download_buffer = g_data->cryptomatte_download_buffer;

  BLI_assert(accum_buffer);
  BLI_assert(download_buffer);

  GPU_framebuffer_read_color(framebuffer,
                             0,
                             0,
                             viewport_size[0],
                             viewport_size[1],
                             num_cryptomatte_layers,
                             0,
                             GPU_DATA_FLOAT,
                             download_buffer);

  /* Integrate download buffer into the accum buffer.
   * The download buffer contains up to 3 floats per pixel (one float per cryptomatte layer.
   *
   * NOTE: here we deviate from the cryptomatte standard. During integration the standard always
   * sort the samples by its weight to make sure that samples with the lowest weight
   * are discarded first. In our case the weight of each sample is always 1 as we don't have
   * subsamples and apply the coverage during the post processing. When there is no room for new
   * samples the new samples has a weight of 1 and will always be discarded. */
  int download_pixel_index = 0;
  int accum_pixel_index = 0;
  int accum_pixel_stride = eevee_cryptomatte_pixel_stride(view_layer);
  for (int pixel_index = 0; pixel_index < buffer_size; pixel_index++) {
    for (int layer = 0; layer < num_cryptomatte_layers; layer++) {
      const int layer_offset = eevee_cryptomatte_layer_offset(view_layer, layer);
      float download_hash = download_buffer[download_pixel_index++];
      for (int level = 0; level < num_levels; level++) {
        EEVEE_CryptomatteSample *sample = &accum_buffer[accum_pixel_index + layer_offset + level];
        if (sample->hash == download_hash) {
          sample->weight += 1.0f;
          break;
        }
        /* We test against weight as hash 0.0f is used for samples hitting the world background. */
        if (sample->weight == 0.0f) {
          sample->hash = download_hash;
          sample->weight = 1.0f;
          break;
        }
      }
    }
    accum_pixel_index += accum_pixel_stride;
  }
}

void EEVEE_cryptomatte_output_accumulate(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata)
{
  EEVEE_FramebufferList *fbl = vedata->fbl;
  EEVEE_StorageList *stl = vedata->stl;
  EEVEE_PrivateData *g_data = stl->g_data;
  EEVEE_EffectsInfo *effects = stl->effects;
  EEVEE_PassList *psl = vedata->psl;
  const DRWContextState *draw_ctx = DRW_context_state_get();
  const ViewLayer *view_layer = draw_ctx->view_layer;
  const int cryptomatte_levels = view_layer->cryptomatte_levels;
  const int current_sample = effects->taa_current_sample;

  /* In accurate mode all render samples are evaluated. In inaccurate mode this is limited to the
   * number of cryptomatte levels. This will reduce the overhead of downloading the GPU buffer and
   * integrating it into the accum buffer. */
  if (g_data->cryptomatte_accurate_mode || current_sample < cryptomatte_levels) {
    static float clear_color[4] = {0.0};
    GPU_framebuffer_bind(fbl->cryptomatte_fb);
    GPU_framebuffer_clear_color(fbl->cryptomatte_fb, clear_color);
    DRW_draw_pass(psl->cryptomatte_ps);

    eevee_cryptomatte_download_buffer(vedata, fbl->cryptomatte_fb);

    /* Restore */
    GPU_framebuffer_bind(fbl->main_fb);
  }
}

/** \} */

/* -------------------------------------------------------------------- */
/** \name Update Render Passes
 * \{ */

void EEVEE_cryptomatte_update_passes(RenderEngine *engine, Scene *scene, ViewLayer *view_layer)
{
  char cryptomatte_pass_name[MAX_NAME];
  const short num_passes = eevee_cryptomatte_passes_per_layer(view_layer);
  if ((view_layer->cryptomatte_flag & VIEW_LAYER_CRYPTOMATTE_OBJECT) != 0) {
    for (short pass = 0; pass < num_passes; pass++) {
      BLI_snprintf_rlen(cryptomatte_pass_name, MAX_NAME, "CryptoObject%02d", pass);
      RE_engine_register_pass(
          engine, scene, view_layer, cryptomatte_pass_name, 4, "RGBA", SOCK_RGBA);
    }
  }
  if ((view_layer->cryptomatte_flag & VIEW_LAYER_CRYPTOMATTE_MATERIAL) != 0) {
    for (short pass = 0; pass < num_passes; pass++) {
      BLI_snprintf_rlen(cryptomatte_pass_name, MAX_NAME, "CryptoMaterial%02d", pass);
      RE_engine_register_pass(
          engine, scene, view_layer, cryptomatte_pass_name, 4, "RGBA", SOCK_RGBA);
    }
  }
  if ((view_layer->cryptomatte_flag & VIEW_LAYER_CRYPTOMATTE_ASSET) != 0) {
    for (short pass = 0; pass < num_passes; pass++) {
      BLI_snprintf_rlen(cryptomatte_pass_name, MAX_NAME, "CryptoAsset%02d", pass);
      RE_engine_register_pass(
          engine, scene, view_layer, cryptomatte_pass_name, 4, "RGBA", SOCK_RGBA);
    }
  }
}

/** \} */

/* -------------------------------------------------------------------- */
/** \name Construct Render Result
 * \{ */

/* Compare function for cryptomatte samples. Samples with the highest weight will be at the
 * beginning of the list. */
static int eevee_cryptomatte_sample_cmp_reverse(const void *a_, const void *b_)
{
  const EEVEE_CryptomatteSample *a = a_;
  const EEVEE_CryptomatteSample *b = b_;
  if (a->weight < b->weight) {
    return 1;
  }
  if (a->weight > b->weight) {
    return -1;
  }

  return 0;
}

/* Post process the weights. The accumulated weights buffer adds one to each weight per sample.
 * During post processing ensure that the total of weights per sample is between 0 and 1. */
static void eevee_cryptomatte_postprocess_weights(EEVEE_Data *vedata)
{
  EEVEE_StorageList *stl = vedata->stl;
  EEVEE_PrivateData *g_data = stl->g_data;
  EEVEE_EffectsInfo *effects = stl->effects;
  EEVEE_TextureList *txl = vedata->txl;
  const DRWContextState *draw_ctx = DRW_context_state_get();
  const ViewLayer *view_layer = draw_ctx->view_layer;
  const int num_cryptomatte_layers = eevee_cryptomatte_layers_count(view_layer);
  const int num_levels = view_layer->cryptomatte_levels;
  const float *viewport_size = DRW_viewport_size_get();
  const int buffer_size = viewport_size[0] * viewport_size[1];

  EEVEE_CryptomatteSample *accum_buffer = g_data->cryptomatte_accum_buffer;
  BLI_assert(accum_buffer);
  float *volumetric_transmittance_buffer = NULL;
  if ((effects->enabled_effects & EFFECT_VOLUMETRIC) != 0) {
    volumetric_transmittance_buffer = GPU_texture_read(
        txl->volume_transmittance_accum, GPU_DATA_FLOAT, 0);
  }
  const int num_samples = effects->taa_current_sample - 1;

  int accum_pixel_index = 0;
  int accum_pixel_stride = eevee_cryptomatte_pixel_stride(view_layer);

  for (int pixel_index = 0; pixel_index < buffer_size;
       pixel_index++, accum_pixel_index += accum_pixel_stride) {
    float coverage = 1.0f;
    if (volumetric_transmittance_buffer != NULL) {
      coverage = (volumetric_transmittance_buffer[pixel_index * 4] +
                  volumetric_transmittance_buffer[pixel_index * 4 + 1] +
                  volumetric_transmittance_buffer[pixel_index * 4 + 2]) /
                 (3.0f * num_samples);
    }
    for (int layer = 0; layer < num_cryptomatte_layers; layer++) {
      const int layer_offset = eevee_cryptomatte_layer_offset(view_layer, layer);
      /* Calculate the total weight of the sample. */
      float total_weight = 0.0f;
      for (int level = 0; level < num_levels; level++) {
        EEVEE_CryptomatteSample *sample = &accum_buffer[accum_pixel_index + layer_offset + level];
        total_weight += sample->weight;
      }
      BLI_assert(total_weight > 0.0f);

      float total_weight_inv = coverage / total_weight;
      if (total_weight_inv > 0.0f) {
        for (int level = 0; level < num_levels; level++) {
          EEVEE_CryptomatteSample *sample =
              &accum_buffer[accum_pixel_index + layer_offset + level];
          /* Remove background samples. These samples were used to determine the correct weight
           * but won't be part of the final result. */
          if (sample->hash == 0.0f) {
            sample->weight = 0.0f;
          }
          sample->weight *= total_weight_inv;
        }

        /* Sort accum buffer by coverage of each sample. */
        qsort(&accum_buffer[accum_pixel_index + layer_offset],
              num_levels,
              sizeof(EEVEE_CryptomatteSample),
              eevee_cryptomatte_sample_cmp_reverse);
      }
      else {
        /* This pixel doesn't have any weight, so clear it fully. */
        for (int level = 0; level < num_levels; level++) {
          EEVEE_CryptomatteSample *sample =
              &accum_buffer[accum_pixel_index + layer_offset + level];
          sample->weight = 0.0f;
          sample->hash = 0.0f;
        }
      }
    }
  }

  if (volumetric_transmittance_buffer) {
    MEM_freeN(volumetric_transmittance_buffer);
  }
}

/* Extract cryptomatte layer from the cryptomatte_accum_buffer to render passes. */
static void eevee_cryptomatte_extract_render_passes(
    RenderLayer *rl,
    const char *viewname,
    const char *render_pass_name_format,
    EEVEE_CryptomatteSample *accum_buffer,
    /* number of render passes per cryptomatte layer. */
    const int num_cryptomatte_passes,
    const int num_cryptomatte_levels,
    const int accum_pixel_stride,
    const int layer_stride,
    const int layer_index,
    const int rect_width,
    const int rect_height,
    const int rect_offset_x,
    const int rect_offset_y,
    const int viewport_width)
{
  char cryptomatte_pass_name[MAX_NAME];
  /* A pass can store 2 levels. Technically the last pass can have a single level if the number of
   * levels is an odd number. This parameter counts the number of levels it has processed. */
  int levels_done = 0;
  for (int pass = 0; pass < num_cryptomatte_passes; pass++) {
    /* Each pass holds 2 cryptomatte samples. */
    const int pass_offset = pass * 2;
    BLI_snprintf_rlen(cryptomatte_pass_name, MAX_NAME, render_pass_name_format, pass);
    RenderPass *rp_object = RE_pass_find_by_name(rl, cryptomatte_pass_name, viewname);
    for (int y = 0; y < rect_height; y++) {
      for (int x = 0; x < rect_width; x++) {
        const int accum_buffer_offset = (rect_offset_x + x +
                                         (rect_offset_y + y) * viewport_width) *
                                            accum_pixel_stride +
                                        layer_index * layer_stride + pass_offset;
        const int render_pass_offset = (y * rect_width + x) * 4;
        rp_object->rect[render_pass_offset] = accum_buffer[accum_buffer_offset].hash;
        rp_object->rect[render_pass_offset + 1] = accum_buffer[accum_buffer_offset].weight;
        if (levels_done + 1 < num_cryptomatte_levels) {
          rp_object->rect[render_pass_offset + 2] = accum_buffer[accum_buffer_offset + 1].hash;
          rp_object->rect[render_pass_offset + 3] = accum_buffer[accum_buffer_offset + 1].weight;
        }
        else {
          rp_object->rect[render_pass_offset + 2] = 0.0f;
          rp_object->rect[render_pass_offset + 3] = 0.0f;
        }
      }
    }
    levels_done++;
  }
}

void EEVEE_cryptomatte_render_result(RenderLayer *rl,
                                     const char *viewname,
                                     const rcti *rect,
                                     EEVEE_Data *vedata,
                                     EEVEE_ViewLayerData *UNUSED(sldata))
{
  EEVEE_PrivateData *g_data = vedata->stl->g_data;
  const DRWContextState *draw_ctx = DRW_context_state_get();
  const ViewLayer *view_layer = draw_ctx->view_layer;
  const eViewLayerCryptomatteFlags cryptomatte_layers = view_layer->cryptomatte_flag &
                                                        VIEW_LAYER_CRYPTOMATTE_ALL;

  eevee_cryptomatte_postprocess_weights(vedata);

  const int rect_width = BLI_rcti_size_x(rect);
  const int rect_height = BLI_rcti_size_y(rect);
  const int rect_offset_x = vedata->stl->g_data->overscan_pixels + rect->xmin;
  const int rect_offset_y = vedata->stl->g_data->overscan_pixels + rect->ymin;
  const float *viewport_size = DRW_viewport_size_get();
  const int viewport_width = viewport_size[0];
  EEVEE_CryptomatteSample *accum_buffer = g_data->cryptomatte_accum_buffer;
  BLI_assert(accum_buffer);
  const int num_cryptomatte_levels = view_layer->cryptomatte_levels;
  const int num_cryptomatte_passes = eevee_cryptomatte_passes_per_layer(view_layer);
  const int layer_stride = eevee_cryptomatte_layer_stride(view_layer);
  const int accum_pixel_stride = eevee_cryptomatte_pixel_stride(view_layer);

  int layer_index = 0;
  if ((cryptomatte_layers & VIEW_LAYER_CRYPTOMATTE_OBJECT) != 0) {
    eevee_cryptomatte_extract_render_passes(rl,
                                            viewname,
                                            "CryptoObject%02d",
                                            accum_buffer,
                                            num_cryptomatte_passes,
                                            num_cryptomatte_levels,
                                            accum_pixel_stride,
                                            layer_stride,
                                            layer_index,
                                            rect_width,
                                            rect_height,
                                            rect_offset_x,
                                            rect_offset_y,
                                            viewport_width);
    layer_index++;
  }
  if ((cryptomatte_layers & VIEW_LAYER_CRYPTOMATTE_MATERIAL) != 0) {
    eevee_cryptomatte_extract_render_passes(rl,
                                            viewname,
                                            "CryptoMaterial%02d",
                                            accum_buffer,
                                            num_cryptomatte_passes,
                                            num_cryptomatte_levels,
                                            accum_pixel_stride,
                                            layer_stride,
                                            layer_index,
                                            rect_width,
                                            rect_height,
                                            rect_offset_x,
                                            rect_offset_y,
                                            viewport_width);
    layer_index++;
  }
  if ((cryptomatte_layers & VIEW_LAYER_CRYPTOMATTE_ASSET) != 0) {
    eevee_cryptomatte_extract_render_passes(rl,
                                            viewname,
                                            "CryptoAsset%02d",
                                            accum_buffer,
                                            num_cryptomatte_passes,
                                            num_cryptomatte_levels,
                                            accum_pixel_stride,
                                            layer_stride,
                                            layer_index,
                                            rect_width,
                                            rect_height,
                                            rect_offset_x,
                                            rect_offset_y,
                                            viewport_width);
    layer_index++;
  }
}

void EEVEE_cryptomatte_store_metadata(EEVEE_Data *vedata, RenderResult *render_result)
{
  EEVEE_PrivateData *g_data = vedata->stl->g_data;
  const DRWContextState *draw_ctx = DRW_context_state_get();
  const ViewLayer *view_layer = draw_ctx->view_layer;
  BLI_assert(g_data->cryptomatte_session);

  BKE_cryptomatte_store_metadata(g_data->cryptomatte_session, render_result, view_layer);
}

/** \} */

void EEVEE_cryptomatte_free(EEVEE_Data *vedata)
{
  EEVEE_PrivateData *g_data = vedata->stl->g_data;
  MEM_SAFE_FREE(g_data->cryptomatte_accum_buffer);
  MEM_SAFE_FREE(g_data->cryptomatte_download_buffer);
  if (g_data->cryptomatte_session) {
    BKE_cryptomatte_free(g_data->cryptomatte_session);
    g_data->cryptomatte_session = NULL;
  }
}