246 lines
9.0 KiB
C++
246 lines
9.0 KiB
C++
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
/** \file
|
|
* \ingroup eevee
|
|
*
|
|
* Gbuffer layout used for deferred shading pipeline.
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include "DRW_render.hh"
|
|
#include "GPU_capabilities.hh"
|
|
|
|
#include "GPU_platform.hh"
|
|
#include "eevee_material.hh"
|
|
#include "eevee_shader_shared.hh"
|
|
|
|
namespace blender::eevee {
|
|
|
|
class Instance;
|
|
|
|
/**
|
|
* Full-screen textures containing geometric and surface data.
|
|
* Used by deferred shading passes. Only one g-buffer is allocated per view
|
|
* and is reused for each deferred layer. This is why there can only be temporary
|
|
* texture inside it.
|
|
*
|
|
* Everything is stored inside two array texture, one for each format. This is to fit the
|
|
* limitation of the number of images we can bind on a single shader.
|
|
*
|
|
* The content of the g-buffer is polymorphic. A 8bit header specify the layout of the data.
|
|
* The first layer is always written to while others are written only if needed using imageStore
|
|
* operations reducing the bandwidth needed.
|
|
* Except for some special configurations, the g-buffer holds up to 3 closures.
|
|
*
|
|
* For each output closure, we also output the color to apply after the lighting computation.
|
|
* The color is stored with a 2 exponent that allows input color with component higher than 1.
|
|
* Color degradation is expected to happen in this case.
|
|
*
|
|
* Here are special configurations:
|
|
*
|
|
* - Opaque Dielectric:
|
|
* - 1 Diffuse lobe and 1 Reflection lobe without anisotropy.
|
|
* - Share a single normal.
|
|
* - Reflection is not colored.
|
|
* - Layout:
|
|
* - Color 1 : Diffuse color
|
|
* - Closure 1 R : Normal packed X
|
|
* - Closure 1 G : Normal packed Y
|
|
* - Closure 1 B : Roughness (isotropic)
|
|
* - Closure 1 A : Reflection intensity
|
|
*
|
|
* - Simple Car-paint: (TODO)
|
|
* - 2 Reflection lobe without anisotropy.
|
|
* - Share a single normal.
|
|
* - Coat layer is not colored.
|
|
* - Layout:
|
|
* - Color 1 : Bottom layer color
|
|
* - Closure 1 R : Normal packed X
|
|
* - Closure 1 G : Normal packed Y
|
|
* - Closure 1 B : Roughness (isotropic)
|
|
* - Closure 1 A : Coat layer intensity
|
|
*
|
|
* - Simple Glass: (TODO)
|
|
* - 1 Refraction lobe and 1 Reflection lobe without anisotropy.
|
|
* - Share a single normal.
|
|
* - Reflection intensity is derived from IOR.
|
|
* - Layout:
|
|
* - Color 1 : Refraction color
|
|
* - Closure 1 R : Normal packed X
|
|
* - Closure 1 G : Normal packed Y
|
|
* - Closure 1 B : Roughness (isotropic)
|
|
* - Closure 1 A : IOR
|
|
*
|
|
* Here are Closure configurations:
|
|
*
|
|
* - Reflection (Isotropic):
|
|
* - Layout:
|
|
* - Color : Reflection color
|
|
* - Closure 1 R : Normal packed X
|
|
* - Closure 1 G : Normal packed Y
|
|
* - Closure 1 B : Roughness
|
|
* - Closure 1 A : Unused
|
|
*
|
|
* - Reflection (Anisotropic): (TODO)
|
|
* - Layout:
|
|
* - Color : Reflection color
|
|
* - Closure 1 R : Normal packed X
|
|
* - Closure 1 G : Normal packed Y
|
|
* - Closure 1 B : Tangent packed X
|
|
* - Closure 1 A : Tangent packed Y
|
|
* - Closure 2 R : Roughness X
|
|
* - Closure 2 G : Roughness Y
|
|
* - Closure 2 B : Unused
|
|
* - Closure 2 A : Unused
|
|
*
|
|
* - Refraction (Isotropic):
|
|
* - Layout:
|
|
* - Color : Refraction color
|
|
* - Closure 1 R : Normal packed X
|
|
* - Closure 1 G : Normal packed Y
|
|
* - Closure 1 B : Roughness
|
|
* - Closure 1 A : IOR
|
|
*
|
|
* - Diffuse:
|
|
* - Layout:
|
|
* - Color : Diffuse color
|
|
* - Closure 1 R : Normal packed X
|
|
* - Closure 1 G : Normal packed Y
|
|
* - Closure 1 B : Unused
|
|
* - Closure 1 A : Unused (Could be used for diffuse roughness)
|
|
*
|
|
* - Sub-Surface Scattering:
|
|
* - Layout:
|
|
* - Color : Diffuse color
|
|
* - Closure 1 R : Normal packed X
|
|
* - Closure 1 G : Normal packed Y
|
|
* - Closure 1 B : Thickness
|
|
* - Closure 1 A : Unused (Could be used for diffuse roughness)
|
|
* - Closure 2 R : Scattering radius R
|
|
* - Closure 2 G : Scattering radius G
|
|
* - Closure 2 B : Scattering radius B
|
|
* - Closure 2 A : Object ID
|
|
*
|
|
*/
|
|
struct GBuffer {
|
|
public:
|
|
/* TODO(fclem): Use texture from pool once they support texture array and layer views. */
|
|
Texture header_tx = {"GBufferHeader"};
|
|
Texture closure_tx = {"GBufferClosure"};
|
|
Texture normal_tx = {"GBufferNormal"};
|
|
|
|
/* Expected number of layer written through the framebuffer. */
|
|
const uint header_fb_layer_count = GBUF_HEADER_FB_LAYER_COUNT;
|
|
const uint closure_fb_layer_count = GBUF_CLOSURE_FB_LAYER_COUNT;
|
|
const uint normal_fb_layer_count = GBUF_NORMAL_FB_LAYER_COUNT;
|
|
|
|
private:
|
|
/* References to optional GBuffer layers that are not always required or written to.
|
|
* These will point to either the dummy textures bellow or to a layer range view of the above
|
|
* textures. In the later case, these layers are written with imageStore instead of being part
|
|
* of the #Framebuffer. */
|
|
GPUTexture *closure_opt_layers_ = nullptr;
|
|
GPUTexture *normal_opt_layers_ = nullptr;
|
|
GPUTexture *header_opt_layers_ = nullptr;
|
|
|
|
/* Textures used to fulfill the GBuffer optional layers binding when textures do not have enough
|
|
* layers for the optional layers image views. The shader are then expected to never write to
|
|
* them. */
|
|
Texture dummy_header_tx_ = {"GBufferDummyHeader"};
|
|
Texture dummy_closure_tx_ = {"GBufferDummyClosure"};
|
|
Texture dummy_normal_tx_ = {"GBufferDummyNormal"};
|
|
|
|
public:
|
|
void acquire(int2 extent, int header_count, int data_count, int normal_count)
|
|
{
|
|
/* Always allocate enough layers so that the frame-buffer attachments are always valid. */
|
|
header_count = max_ii(header_fb_layer_count, header_count);
|
|
data_count = max_ii(closure_fb_layer_count, data_count);
|
|
normal_count = max_ii(normal_fb_layer_count, normal_count);
|
|
|
|
dummy_header_tx_.ensure_2d_array(GPU_R32UI, int2(1), 1, GPU_TEXTURE_USAGE_SHADER_WRITE);
|
|
dummy_closure_tx_.ensure_2d_array(GPU_RGB10_A2, int2(1), 1, GPU_TEXTURE_USAGE_SHADER_WRITE);
|
|
dummy_normal_tx_.ensure_2d_array(GPU_RG16, int2(1), 1, GPU_TEXTURE_USAGE_SHADER_WRITE);
|
|
|
|
eGPUTextureUsage usage = GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_SHADER_WRITE |
|
|
GPU_TEXTURE_USAGE_ATTACHMENT;
|
|
header_tx.ensure_2d_array(GPU_R32UI, extent, header_count, usage);
|
|
closure_tx.ensure_2d_array(GPU_RGB10_A2, extent, data_count, usage);
|
|
normal_tx.ensure_2d_array(GPU_RG16, extent, normal_count, usage);
|
|
/* Ensure layer view for frame-buffer attachment. */
|
|
header_tx.ensure_layer_views();
|
|
closure_tx.ensure_layer_views();
|
|
normal_tx.ensure_layer_views();
|
|
/* Ensure layer view for image store. */
|
|
auto range = [](int layer_count, int fb_layer_count, Texture &tx, Texture &dummy) {
|
|
return (layer_count > fb_layer_count) ?
|
|
tx.layer_range_view(fb_layer_count, layer_count - fb_layer_count) :
|
|
dummy;
|
|
};
|
|
header_opt_layers_ = range(header_count, header_fb_layer_count, header_tx, dummy_header_tx_);
|
|
closure_opt_layers_ = range(data_count, closure_fb_layer_count, closure_tx, dummy_closure_tx_);
|
|
normal_opt_layers_ = range(normal_count, normal_fb_layer_count, normal_tx, dummy_normal_tx_);
|
|
}
|
|
|
|
/* Bind the GBuffer frame-buffer correctly using the correct workarounds. */
|
|
void bind(Framebuffer &gbuffer_fb)
|
|
{
|
|
/* Workaround a Metal bug that is only showing up on ATI/Intel GPUs. */
|
|
if (GPU_type_matches(
|
|
GPU_DEVICE_ATI | GPU_DEVICE_INTEL | GPU_DEVICE_INTEL_UHD, GPU_OS_MAC, GPU_DRIVER_ANY))
|
|
{
|
|
header_tx.clear(uint4(0));
|
|
GPU_framebuffer_bind(gbuffer_fb);
|
|
return;
|
|
}
|
|
|
|
if (!GPU_stencil_export_support()) {
|
|
/* Clearing custom load-store frame-buffers is invalid,
|
|
* clear the stencil as a regular frame-buffer first. */
|
|
GPU_framebuffer_bind(gbuffer_fb);
|
|
GPU_framebuffer_clear_stencil(gbuffer_fb, 0x0u);
|
|
}
|
|
GPU_framebuffer_bind_ex(
|
|
gbuffer_fb,
|
|
{
|
|
{GPU_LOADACTION_LOAD, GPU_STOREACTION_STORE}, /* Depth. */
|
|
{GPU_LOADACTION_LOAD, GPU_STOREACTION_STORE}, /* Combined. */
|
|
{GPU_LOADACTION_CLEAR, GPU_STOREACTION_STORE, {0}}, /* GBuf Header. */
|
|
{GPU_LOADACTION_DONT_CARE, GPU_STOREACTION_STORE}, /* GBuf Normal. */
|
|
{GPU_LOADACTION_DONT_CARE, GPU_STOREACTION_STORE}, /* GBuf Closure. */
|
|
{GPU_LOADACTION_DONT_CARE, GPU_STOREACTION_STORE}, /* GBuf Closure 2. */
|
|
});
|
|
}
|
|
|
|
void release()
|
|
{
|
|
/* TODO(fclem): Use texture from pool once they support texture array. */
|
|
// header_tx.release();
|
|
// closure_tx.release();
|
|
// normal_tx.release();
|
|
|
|
header_opt_layers_ = nullptr;
|
|
closure_opt_layers_ = nullptr;
|
|
normal_opt_layers_ = nullptr;
|
|
}
|
|
|
|
template<typename PassType> void bind_resources(PassType &pass)
|
|
{
|
|
pass.bind_texture("gbuf_header_tx", &header_tx);
|
|
pass.bind_texture("gbuf_closure_tx", &closure_tx);
|
|
pass.bind_texture("gbuf_normal_tx", &normal_tx);
|
|
}
|
|
|
|
template<typename PassType> void bind_optional_layers(PassType &pass)
|
|
{
|
|
pass.bind_image(GBUF_NORMAL_SLOT, &normal_opt_layers_);
|
|
pass.bind_image(GBUF_CLOSURE_SLOT, &closure_opt_layers_);
|
|
pass.bind_image(GBUF_HEADER_SLOT, &header_opt_layers_);
|
|
}
|
|
};
|
|
|
|
} // namespace blender::eevee
|