BRE Architecture Series Part 8 – Environment Light Pass

In this opportunity, we are going to talk about the environment light pass. Its previous pass is AmbientOcclusionPass and its next pass is SkyBoxPass. You can check BRE Architecture Series 11 – Ambient Occlusion Pass and BRE Architecture Series 9 – Skybox Pass. Before giving more details we are going to talk about environment light.

What is Image Based Lighting (IBL)?

Image Based Lighting technique (IBL) is one of rendering techniques that is based on the image from environment map for rendering the reflection, refraction and illumination lighting effect on the object surface. Since it is able to present excellent global illumination results for the surfaces in the scene by taking the full advantage and processing the environment map, it also plays a decisive influence on the material properties. Of course, the advantages of IBL are not just limited to its capability to present a good and almost realistic result, but what more it can do is to get rid of the biggest and the most common weakness in ray tracing of traditional global illumination. It is a problem of low speed to be caused by the tree structure of importance for stopping condition in ray tracing.

In local illumination, the most important components are ambient, diffuse and specular components. They depend on the properties of light sources, objects and viewer (position of light source, color, object normal, object position, and the viewer position, etc.in order to). Therefore even though the object and viewer properties are not changed after the local illumination is converted to the IBL global illumination, the light sources that are set in the real 3D environment are changed to an environment map that contains the light source image. In other words, we should consider all the lighting and shading information which compose all pixels in the environment map as a huge information dataset for the environment light sources. Thus we have to calculate the effects of each pixel in the environment map for a known cube map lookup to simulate the correspondent result under that pixel to the surface.

I used IBLBaker to generate the diffuse irradiance & specular pre-convolved environment cube maps. You can also use AMD CubeMapGen. You can see a demo of BRE about IBL in the following video

How is Image Based Lighting (IBL) implemented in BRE?

In BRE, we have a type of command list recorder named EnvironmentLightCommandListRecorder that generates a command list related with environment light and pushes it to the CommandListExecutor to be executed. This command list recorder needs an input texture which is the current frame buffer just cleared by the RenderManager. Remember that the previous pass is GeometryPass but it does not write to the frame buffer but to geometry buffers. Also, we need the cube map textures that we generated using IBLBaker (diffuse irradiance environment cube map and the specular pre-convolved environment cube map), the geometry buffers filled in the geometry pass (to know geometry and material properties), the depth buffer (to be able to get the fragment position in view space), and the ambient accessibility texture (output of the AmbientOcclusionPass). You can see a diagram of this pass in the following picture.

environment_workflow

Its implementation is the following

EnvironmentLightCommandListRecorder.h

#pragma once

#include <CommandManager\CommandListPerFrame.h>
#include <ResourceManager\FrameUploadCBufferPerFrame.h>

namespace BRE {
struct FrameCBuffer;

///
/// @brief Responsible of recording of command lists for environment light pass.
///
class EnvironmentLightCommandListRecorder {
public:
    EnvironmentLightCommandListRecorder() = default;
    ~EnvironmentLightCommandListRecorder() = default;
    EnvironmentLightCommandListRecorder(const EnvironmentLightCommandListRecorder&) = delete;
    const EnvironmentLightCommandListRecorder& operator=(const EnvironmentLightCommandListRecorder&) = delete;
    EnvironmentLightCommandListRecorder(EnvironmentLightCommandListRecorder&&) = default;
    EnvironmentLightCommandListRecorder& operator=(EnvironmentLightCommandListRecorder&&) = default;

    ///
    /// @brief Initializes pipeline state object and root signature
    ///
    /// This method must be called at the beginning of the application, and once
    ///
    static void InitSharedPSOAndRootSignature() noexcept;

    ///
    /// @brief Initializes the recorder. 
    ///
    /// InitSharedPSOAndRootSignature() must be called first
    ///
    /// @param diffuseIrradianceCubeMap Diffuse irradiance environment cube map
    /// @param specularPreConvolvedCubeMap Specular pre convolved environment cube map
    /// @param outputColorBufferRenderTargetView Render target view to the output color buffer
    /// @param geometryBufferShaderResourceViewsBegin Shader resource view 
    /// to the first geometry buffer. The geometry buffer shader resource views are contiguous.
    /// @param ambientAccessibilityBufferShaderResourceView Shader resource view to the ambient accessibility buffer
    /// @param depthBufferShaderResourceView Depth buffer shader resource view
    ///
    void Init(ID3D12Resource& diffuseIrradianceCubeMap,
              ID3D12Resource& specularPreConvolvedCubeMap,
              const D3D12_CPU_DESCRIPTOR_HANDLE& outputColorBufferRenderTargetView,
              const D3D12_GPU_DESCRIPTOR_HANDLE& geometryBufferShaderResourceViewsBegin,
              const D3D12_GPU_DESCRIPTOR_HANDLE& ambientAccessibilityBufferShaderResourceView,
              const D3D12_GPU_DESCRIPTOR_HANDLE& depthBufferShaderResourceView) noexcept;

    ///
    /// @brief Records command lists and pushes them into CommandListExecutor
    ///
    /// Init() must be called first
    ///
    /// @param frameCBuffer Constant buffer per frame, for current frame
    /// @return The number of pushed command lists
    ///
    std::uint32_t RecordAndPushCommandLists(const FrameCBuffer& frameCBuffer) noexcept;

    ///
    /// @brief Checks if internal data is valid. Typically, used for assertions
    /// @return True if valid. Otherwise, false
    ///
    bool IsDataValid() const noexcept;

private:
    ///
    /// @brief Initializes shader resource views
    /// @param diffuseIrradianceCubeMap Diffuse irradiance environment cube map
    /// @param specularPreConvolvedCubeMap Specular pre convolved environment cube map
    /// @param ambientAccessibilityBuffer Ambient accessibility buffer
    ///
    void InitShaderResourceViews(ID3D12Resource& diffuseIrradianceCubeMap,
                                 ID3D12Resource& specularPreConvolvedCubeMap) noexcept;

    CommandListPerFrame mCommandListPerFrame;

    FrameUploadCBufferPerFrame mFrameUploadCBufferPerFrame;

    D3D12_CPU_DESCRIPTOR_HANDLE mOutputColorBufferRenderTargetView{ 0UL };

    // First descriptor in the list. All the others are contiguous
    D3D12_GPU_DESCRIPTOR_HANDLE mGeometryBufferShaderResourceViewsBegin{ 0UL };

    D3D12_GPU_DESCRIPTOR_HANDLE mAmbientAccessibilityBufferShaderResourceView{ 0UL };
    D3D12_GPU_DESCRIPTOR_HANDLE mDepthBufferShaderResourceView{ 0UL };

    // First descriptor in the list. All the others are contiguous
    D3D12_GPU_DESCRIPTOR_HANDLE mDiffuseAndSpecularIrradianceTextureShaderResourceViews{ 0UL };
};
}

EnvironmentLightCommandListRecorder.cpp

#include "EnvironmentLightCommandListRecorder.h"

#include <DirectXMath.h>

#include <CommandListExecutor\CommandListExecutor.h>
#include <DescriptorManager\CbvSrvUavDescriptorManager.h>
#include <PSOManager/PSOManager.h>
#include <RootSignatureManager\RootSignatureManager.h>
#include <ShaderManager\ShaderManager.h>
#include <ShaderUtils\CBuffers.h>
#include <Utils/DebugUtils.h>

namespace BRE {
// Root Signature:
// "CBV(b0, visibility = SHADER_VISIBILITY_VERTEX), " \ 0 -> Frame CBuffer
// "CBV(b0, visibility = SHADER_VISIBILITY_PIXEL), " \ 1 -> Frame CBuffer
// "DescriptorTable(SRV(t0), SRV(t1), visibility = SHADER_VISIBILITY_PIXEL), " \ 2 -> Geometry buffers 
// "DescriptorTable(SRV(t2), SRV(t3), visibility = SHADER_VISIBILITY_PIXEL), " \ 3 -> diffuse & specular irradiance cube maps 
// "DescriptorTable(SRV(t4), visibility = SHADER_VISIBILITY_PIXEL), " \ 4 -> Ambient accessibility buffer 
// "DescriptorTable(SRV(t5), visibility = SHADER_VISIBILITY_PIXEL), " \ 5 -> Depth buffer
namespace {
ID3D12PipelineState* sPSO{ nullptr };
ID3D12RootSignature* sRootSignature{ nullptr };
}

void
EnvironmentLightCommandListRecorder::InitSharedPSOAndRootSignature() noexcept
{
    BRE_ASSERT(sPSO == nullptr);
    BRE_ASSERT(sRootSignature == nullptr);

    PSOManager::PSOCreationData psoData{};
    psoData.mBlendDescriptor = D3DFactory::GetAlwaysBlendDesc();
    psoData.mDepthStencilDescriptor = D3DFactory::GetDisabledDepthStencilDesc();

    psoData.mPixelShaderBytecode = ShaderManager::LoadShaderFileAndGetBytecode("EnvironmentLightPass/Shaders/EnvironmentLight/PS.cso");
    psoData.mVertexShaderBytecode = ShaderManager::LoadShaderFileAndGetBytecode("EnvironmentLightPass/Shaders/EnvironmentLight/VS.cso");

    ID3DBlob* rootSignatureBlob = &ShaderManager::LoadShaderFileAndGetBlob("EnvironmentLightPass/Shaders/EnvironmentLight/RS.cso");
    psoData.mRootSignature = &RootSignatureManager::CreateRootSignatureFromBlob(*rootSignatureBlob);
    sRootSignature = psoData.mRootSignature;

    psoData.mNumRenderTargets = 1U;
    psoData.mRenderTargetFormats[0U] = ApplicationSettings::sColorBufferFormat;
    for (std::size_t i = psoData.mNumRenderTargets; i < _countof(psoData.mRenderTargetFormats); ++i) {
        psoData.mRenderTargetFormats[i] = DXGI_FORMAT_UNKNOWN;
    }
    psoData.mPrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
    sPSO = &PSOManager::CreateGraphicsPSO(psoData);

    BRE_ASSERT(sPSO != nullptr);
    BRE_ASSERT(sRootSignature != nullptr);
}

void
EnvironmentLightCommandListRecorder::Init(ID3D12Resource& diffuseIrradianceCubeMap,
                                          ID3D12Resource& specularPreConvolvedCubeMap,
                                          const D3D12_CPU_DESCRIPTOR_HANDLE& outputColorBufferRenderTargetView,
                                          const D3D12_GPU_DESCRIPTOR_HANDLE& geometryBufferShaderResourceViewsBegin,
                                          const D3D12_GPU_DESCRIPTOR_HANDLE& ambientAccessibilityBufferShaderResourceView,
                                          const D3D12_GPU_DESCRIPTOR_HANDLE& depthBufferShaderResourceView) noexcept
{
    BRE_ASSERT(IsDataValid() == false);

    mOutputColorBufferRenderTargetView = outputColorBufferRenderTargetView;
    mGeometryBufferShaderResourceViewsBegin = geometryBufferShaderResourceViewsBegin;
    mAmbientAccessibilityBufferShaderResourceView = ambientAccessibilityBufferShaderResourceView;
    mDepthBufferShaderResourceView = depthBufferShaderResourceView;

    InitShaderResourceViews(diffuseIrradianceCubeMap,
                            specularPreConvolvedCubeMap);

    BRE_ASSERT(IsDataValid());
}

std::uint32_t
EnvironmentLightCommandListRecorder::RecordAndPushCommandLists(const FrameCBuffer& frameCBuffer) noexcept
{
    BRE_ASSERT(IsDataValid());
    BRE_ASSERT(sPSO != nullptr);
    BRE_ASSERT(sRootSignature != nullptr);

    // Update frame constants
    UploadBuffer& uploadFrameCBuffer(mFrameUploadCBufferPerFrame.GetNextFrameCBuffer());
    uploadFrameCBuffer.CopyData(0U, &frameCBuffer, sizeof(frameCBuffer));

    ID3D12GraphicsCommandList& commandList = mCommandListPerFrame.ResetCommandListWithNextCommandAllocator(sPSO);

    commandList.RSSetViewports(1U, &ApplicationSettings::sScreenViewport);
    commandList.RSSetScissorRects(1U, &ApplicationSettings::sScissorRect);
    commandList.OMSetRenderTargets(1U, &mOutputColorBufferRenderTargetView, false, nullptr);

    ID3D12DescriptorHeap* heaps[] = { &CbvSrvUavDescriptorManager::GetDescriptorHeap() };
    commandList.SetDescriptorHeaps(_countof(heaps), heaps);

    commandList.SetGraphicsRootSignature(sRootSignature);
    const D3D12_GPU_VIRTUAL_ADDRESS frameCBufferGpuVAddress(uploadFrameCBuffer.GetResource().GetGPUVirtualAddress());
    commandList.SetGraphicsRootConstantBufferView(0U, frameCBufferGpuVAddress);
    commandList.SetGraphicsRootConstantBufferView(1U, frameCBufferGpuVAddress);
    commandList.SetGraphicsRootDescriptorTable(2U, mGeometryBufferShaderResourceViewsBegin);
    commandList.SetGraphicsRootDescriptorTable(3U, mDiffuseAndSpecularIrradianceTextureShaderResourceViews);
    commandList.SetGraphicsRootDescriptorTable(4U, mAmbientAccessibilityBufferShaderResourceView);
    commandList.SetGraphicsRootDescriptorTable(5U, mDepthBufferShaderResourceView);

    commandList.IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    commandList.DrawInstanced(6U, 1U, 0U, 0U);

    commandList.Close();
    CommandListExecutor::Get().PushCommandList(commandList);

    return 1U;
}

bool
EnvironmentLightCommandListRecorder::IsDataValid() const noexcept
{
    const bool result =
        mOutputColorBufferRenderTargetView.ptr != 0UL &&
        mGeometryBufferShaderResourceViewsBegin.ptr != 0UL &&
        mAmbientAccessibilityBufferShaderResourceView.ptr != 0UL &&
        mDepthBufferShaderResourceView.ptr != 0UL &&
        mDiffuseAndSpecularIrradianceTextureShaderResourceViews.ptr != 0UL;

    return result;
}

void
EnvironmentLightCommandListRecorder::InitShaderResourceViews(ID3D12Resource& diffuseIrradianceCubeMap,
                                                             ID3D12Resource& specularPreConvolvedCubeMap) noexcept
{
    ID3D12Resource* resources[] =
    {
        &diffuseIrradianceCubeMap,
        &specularPreConvolvedCubeMap,
    };

    D3D12_SHADER_RESOURCE_VIEW_DESC srvDescriptors[_countof(resources)]{};

    // Fill cube map texture descriptors	
    srvDescriptors[0].Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
    srvDescriptors[0].ViewDimension = D3D12_SRV_DIMENSION_TEXTURECUBE;
    srvDescriptors[0].TextureCube.MostDetailedMip = 0;
    srvDescriptors[0].TextureCube.MipLevels = diffuseIrradianceCubeMap.GetDesc().MipLevels;
    srvDescriptors[0].TextureCube.ResourceMinLODClamp = 0.0f;
    srvDescriptors[0].Format = diffuseIrradianceCubeMap.GetDesc().Format;

    srvDescriptors[1].Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
    srvDescriptors[1].ViewDimension = D3D12_SRV_DIMENSION_TEXTURECUBE;
    srvDescriptors[1].TextureCube.MostDetailedMip = 0;
    srvDescriptors[1].TextureCube.MipLevels = specularPreConvolvedCubeMap.GetDesc().MipLevels;
    srvDescriptors[1].TextureCube.ResourceMinLODClamp = 0.0f;
    srvDescriptors[1].Format = specularPreConvolvedCubeMap.GetDesc().Format;

    BRE_ASSERT(_countof(resources) == _countof(srvDescriptors));

    mDiffuseAndSpecularIrradianceTextureShaderResourceViews =
        CbvSrvUavDescriptorManager::CreateShaderResourceViews(resources,
                                                              srvDescriptors,
                                                              _countof(srvDescriptors));
}
}

Regarding shaders implementation, we use vertex and pixel shaders. We send a triangle list of 2 triangles, and in the vertex shader, we expand these triangles to match a full-screen quad dimensions in NDC space. Additionally, we need to pass the camera to fragment vector that is used to get the position in view space of the fragment in the pixel shader. Its implementation is the following

VertexShader

#include <ShaderUtils/CBuffers.hlsli>

#include "RS.hlsl"

struct Input {
    uint mVertexId : SV_VertexID;
};

static const float2 gQuadUVs[6] = {
    float2(0.0f, 1.0f),
    float2(0.0f, 0.0f),
    float2(1.0f, 0.0f),
    float2(0.0f, 1.0f),
    float2(1.0f, 0.0f),
    float2(1.0f, 1.0f)
};

ConstantBuffer<FrameCBuffer> gFrameCBuffer : register(b0);

struct Output {
    float4 mPositionNDC : SV_POSITION;
    float3 mCameraToFragmentVectorViewSpace : VIEW_RAY;
    float2 mUV : TEXCOORD;
};

[RootSignature(RS)]
Output main(in const Input input)
{
    Output output;

    output.mUV = gQuadUVs[input.mVertexId];

    // Quad covering screen in NDC space ([-1.0, 1.0] x [-1.0, 1.0] x [0.0, 1.0] x [1.0])
    output.mPositionNDC = float4(2.0f * output.mUV.x - 1.0f,
                                 1.0f - 2.0f * output.mUV.y,
                                 0.0f,
                                 1.0f);

    // Transform quad corners to view space near plane.
    const float4 ph = mul(output.mPositionNDC,
                          gFrameCBuffer.mInverseProjectionMatrix);
    output.mCameraToFragmentVectorViewSpace = ph.xyz / ph.w;

    return output;
}

In the pixel shader, we convert the depth of the current fragment to view space, we sample the geometry buffers, the cube maps and the ambient accessibility texture, to compute later the environment light and ambient accessibility. Its implementation is the following

PixelShader

#include <ShaderUtils/CBuffers.hlsli>
#include <ShaderUtils/Lighting.hlsli>
#include <ShaderUtils/Utils.hlsli>

#include "RS.hlsl"

//#define SKIP_ENVIRONMENT_LIGHT 1
//#define DEBUG_AMBIENT_ACCESIBILITY 1
//#define SKIP_AMBIENT_ACCESSIBILITY 1

struct Input {
    float4 mPositionNDC : SV_POSITION;
    float3 mCameraToFragmentVectorViewSpace : VIEW_RAY;
};

ConstantBuffer<FrameCBuffer> gFrameCBuffer : register(b0);

SamplerState TextureSampler : register (s0);

Texture2D<float4> Normal_RoughnessTexture : register (t0);
Texture2D<float4> BaseColor_MetalnessTexture : register (t1);
TextureCube DiffuseIBLCubeMap : register(t2);
TextureCube SpecularIBLCubeMap : register(t3);
Texture2D<float> AmbientAccessibilityTexture : register (t4);
Texture2D<float> DepthTexture : register (t5);

struct Output {
    float4 mColor : SV_Target0;
};

[RootSignature(RS)]
Output main(const in Input input)
{
    Output output = (Output)0;

#ifdef SKIP_ENVIRONMENT_LIGHT
    output.mColor = float4(0.0f, 0.0f, 0.0f, 1.0f);
#else
    const int3 fragmentPositionScreenSpace = int3(input.mPositionNDC.xy, 0);

    // Ambient accessibility (1.0f - ambient occlussion factor)
    const float ambientAccessibility = AmbientAccessibilityTexture.Load(fragmentPositionScreenSpace);

    const float4 normal_roughness = Normal_RoughnessTexture.Load(fragmentPositionScreenSpace);

    // Compute fragment position in view space
    const float fragmentZNDC = DepthTexture.Load(fragmentPositionScreenSpace);
    const float3 rayViewSpace = normalize(input.mCameraToFragmentVectorViewSpace);
    const float3 fragmentPositionViewSpace = ViewRayToViewPosition(rayViewSpace,
                                                                   fragmentZNDC,
                                                                   gFrameCBuffer.mProjectionMatrix);

    const float3 fragmentPositionWorldSpace = mul(float4(fragmentPositionViewSpace, 1.0f),
                                                  gFrameCBuffer.mInverseViewMatrix).xyz;

    const float2 encodedNormal = normal_roughness.xy;
    const float3 normalViewSpace = normalize(Decode(encodedNormal));
    const float3 normalWorldSpace = normalize(mul(float4(normalViewSpace, 0.0f),
                                                  gFrameCBuffer.mInverseViewMatrix).xyz);

    const float4 baseColor_metalness = BaseColor_MetalnessTexture.Load(fragmentPositionScreenSpace);
    const float3 baseColor = baseColor_metalness.xyz;
    const float metalness = baseColor_metalness.w;

    // As we are working at view space, we do not need camera position to 
    // compute vector from geometry position to camera.
    const float3 fragmentPositionToCameraViewSpace = normalize(-fragmentPositionViewSpace);

    const float3 indirectDiffuseColor = DiffuseIBL(baseColor,
                                                   metalness,
                                                   TextureSampler,
                                                   DiffuseIBLCubeMap,
                                                   normalWorldSpace);

    const float3 indirectSpecularColor = SpecularIBL(baseColor,
                                                     metalness,
                                                     normal_roughness.z,
                                                     TextureSampler,
                                                     SpecularIBLCubeMap,
                                                     fragmentPositionToCameraViewSpace,
                                                     fragmentPositionWorldSpace,
                                                     gFrameCBuffer.mEyePositionWorldSpace.xyz,
                                                     normalWorldSpace,
                                                     normalViewSpace);

    const float3 color = indirectDiffuseColor + indirectSpecularColor;

#ifdef DEBUG_AMBIENT_ACCESIBILITY
    output.mColor = float4(ambientAccessibility,
                           ambientAccessibility,
                           ambientAccessibility,
                           1.0f);
#elif SKIP_AMBIENT_ACCESSIBILITY
    output.mColor = float4(color, 1.0f);
#else 
    output.mColor = float4(color * ambientAccessibility, 1.0f);
#endif
#endif

    return output;
}

How are these command list recorders created and executed?

The pass responsible to create the EnvironmentLightCommandListRecorder, to execute it and to set resource barriers is EnvironmentLightPass, and its implementation is the following

EnvironmentLightPass.h

#pragma once

#include <CommandManager\CommandListPerFrame.h>
#include <EnvironmentLightPass\EnvironmentLightCommandListRecorder.h>

namespace BRE {
///
/// @brief Pass responsible to apply ambient lighting and ambient occlusion
/// 
class EnvironmentLightPass {
public:
    EnvironmentLightPass() = default;
    ~EnvironmentLightPass() = default;
    EnvironmentLightPass(const EnvironmentLightPass&) = delete;
    const EnvironmentLightPass& operator=(const EnvironmentLightPass&) = delete;
    EnvironmentLightPass(EnvironmentLightPass&&) = delete;
    EnvironmentLightPass& operator=(EnvironmentLightPass&&) = delete;

    ///
    /// @brief Initializes the pass
    /// @param baseColorMetalnessBuffer Geometry buffer that contains base color and metalness.
    /// @param normalRoughnessBuffer Geometry buffer that contains normals and roughness factors.
    /// @param depthBuffer Depth buffer
    /// @param diffuseIrradianceCubeMap Diffuse irradiance environment cube map
    /// @param specularPreConvolvedCubeMap Specular pre convolved environment cube map
    /// @param ambientAccessibilityBuffer Ambient accessibility buffer
    /// @param outputColorBufferRenderTargetView Render target view to the output color buffer
    /// @param geometryBufferShaderResourceViewsBegin Shader resource view 
    /// to the first geometry buffer. The geometry buffer shader resource views are contiguous.
    /// @param ambientAccessibilityBufferShaderResourceView Shader resource view to the ambient accessibility buffer
    /// @param depthBufferShaderResourceView Shader resource view to the depth buffer
    ///
    void Init(ID3D12Resource& baseColorMetalnessBuffer,
              ID3D12Resource& normalRoughnessBuffer,
              ID3D12Resource& depthBuffer,
              ID3D12Resource& diffuseIrradianceCubeMap,
              ID3D12Resource& specularPreConvolvedCubeMap,
              ID3D12Resource& ambientAccessibilityBuffer,
              const D3D12_CPU_DESCRIPTOR_HANDLE& outputColorBufferRenderTargetView,
              const D3D12_GPU_DESCRIPTOR_HANDLE& geometryBufferShaderResourceViewsBegin,
              const D3D12_GPU_DESCRIPTOR_HANDLE& ambientAccessibilityBufferShaderResourceView,
              const D3D12_GPU_DESCRIPTOR_HANDLE& depthBufferShaderResourceView) noexcept;

    ///
    /// @brief Executes the pass
    ///
    /// Init() must be called first. This method can record and
    /// push command lists to the CommandListExecutor.
    ///
    /// @param frameCBuffer Constant buffer per frame, for current frame
    /// @return The number of recorded command lists.
    ///
    std::uint32_t Execute(const FrameCBuffer& frameCBuffer) noexcept;

private:
    ///
    /// @brief Checks if internal data is valid. Typically, used for assertions
    /// @return True if valid. Otherwise, false
    ///
    bool IsDataValid() const noexcept;

    ///
    /// @brief Records pre pass command lists and pushes them to 
    /// the CommandListExecutor.
    /// @return The number of recorded command lists
    ///
    std::uint32_t RecordAndPushPrePassCommandLists() noexcept;

    CommandListPerFrame mPrePassCommandListPerFrame;
    CommandListPerFrame mMiddlePassCommandListPerFrame;
    CommandListPerFrame mPostPassCommandListPerFrame;

    EnvironmentLightCommandListRecorder mEnvironmentLightRecorder;

    ID3D12Resource* mBaseColorMetalnessBuffer{ nullptr };
    ID3D12Resource* mNormalRoughnessBuffer{ nullptr };
    ID3D12Resource* mDepthBuffer{ nullptr };
    ID3D12Resource* mAmbientAccessibilityBuffer{ nullptr };
};
}

EnvironmentLightPass.cpp

#include "EnvironmentLightPass.h"

#include <d3d12.h>

#include <CommandListExecutor/CommandListExecutor.h>
#include <DescriptorManager\CbvSrvUavDescriptorManager.h>
#include <DescriptorManager\RenderTargetDescriptorManager.h>
#include <DXUtils\D3DFactory.h>
#include <ResourceManager\ResourceManager.h>
#include <ResourceStateManager\ResourceStateManager.h>
#include <Utils\DebugUtils.h>

namespace BRE {
void
EnvironmentLightPass::Init(ID3D12Resource& baseColorMetalnessBuffer,
                           ID3D12Resource& normalRoughnessBuffer,
                           ID3D12Resource& depthBuffer,
                           ID3D12Resource& diffuseIrradianceCubeMap,
                           ID3D12Resource& specularPreConvolvedCubeMap,
                           ID3D12Resource& ambientAccessibilityBuffer,
                           const D3D12_CPU_DESCRIPTOR_HANDLE& outputColorBufferRenderTargetView,
                           const D3D12_GPU_DESCRIPTOR_HANDLE& geometryBufferShaderResourceViewsBegin,
                           const D3D12_GPU_DESCRIPTOR_HANDLE& ambientAccessibilityBufferShaderResourceView,
                           const D3D12_GPU_DESCRIPTOR_HANDLE& depthBufferShaderResourceView) noexcept
{
    BRE_ASSERT(IsDataValid() == false);

    EnvironmentLightCommandListRecorder::InitSharedPSOAndRootSignature();

    // Initialize ambient light recorder
    mEnvironmentLightRecorder.Init(diffuseIrradianceCubeMap,
                                   specularPreConvolvedCubeMap,
                                   outputColorBufferRenderTargetView,
                                   geometryBufferShaderResourceViewsBegin,
                                   ambientAccessibilityBufferShaderResourceView,
                                   depthBufferShaderResourceView);

    mBaseColorMetalnessBuffer = &baseColorMetalnessBuffer;
    mNormalRoughnessBuffer = &normalRoughnessBuffer;
    mDepthBuffer = &depthBuffer;
    mAmbientAccessibilityBuffer = &ambientAccessibilityBuffer;

    BRE_ASSERT(IsDataValid());
}

std::uint32_t
EnvironmentLightPass::Execute(const FrameCBuffer& frameCBuffer) noexcept
{
    BRE_ASSERT(IsDataValid());

    std::uint32_t commandListCount = 0U;

    commandListCount += RecordAndPushPrePassCommandLists();
    commandListCount += mEnvironmentLightRecorder.RecordAndPushCommandLists(frameCBuffer);

    return commandListCount;
}

bool
EnvironmentLightPass::IsDataValid() const noexcept
{
    const bool b =
        mAmbientAccessibilityBuffer != nullptr &&
        mBaseColorMetalnessBuffer != nullptr &&
        mNormalRoughnessBuffer != nullptr &&
        mDepthBuffer != nullptr;

    return b;
}

std::uint32_t
EnvironmentLightPass::RecordAndPushPrePassCommandLists() noexcept
{
    BRE_ASSERT(IsDataValid());

    D3D12_RESOURCE_BARRIER barriers[5U];
    std::uint32_t barrierCount = 0UL;
    if (ResourceStateManager::GetResourceState(*mAmbientAccessibilityBuffer) != D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE) {
        barriers[barrierCount] = ResourceStateManager::ChangeResourceStateAndGetBarrier(*mAmbientAccessibilityBuffer,
                                                                                        D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
        ++barrierCount;
    }

    if (ResourceStateManager::GetResourceState(*mBaseColorMetalnessBuffer) != D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE) {
        barriers[barrierCount] = ResourceStateManager::ChangeResourceStateAndGetBarrier(*mBaseColorMetalnessBuffer,
                                                                                        D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
        ++barrierCount;
    }

    if (ResourceStateManager::GetResourceState(*mNormalRoughnessBuffer) != D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE) {
        barriers[barrierCount] = ResourceStateManager::ChangeResourceStateAndGetBarrier(*mNormalRoughnessBuffer,
                                                                                        D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
        ++barrierCount;
    }

    if (ResourceStateManager::GetResourceState(*mDepthBuffer) != D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE) {
        barriers[barrierCount] = ResourceStateManager::ChangeResourceStateAndGetBarrier(*mDepthBuffer,
                                                                                        D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
        ++barrierCount;
    }

    if (ResourceStateManager::GetResourceState(*mAmbientAccessibilityBuffer) != D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE) {
        barriers[barrierCount] = ResourceStateManager::ChangeResourceStateAndGetBarrier(*mAmbientAccessibilityBuffer,
                                                                                        D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
        ++barrierCount;
    }

    if (barrierCount > 0UL) {
        ID3D12GraphicsCommandList& commandList = mPostPassCommandListPerFrame.ResetCommandListWithNextCommandAllocator(nullptr);
        commandList.ResourceBarrier(barrierCount, barriers);
        BRE_CHECK_HR(commandList.Close());
        CommandListExecutor::Get().PushCommandList(commandList);

        return 1U;
    }

    return 0U;
}
}
Advertisements

2 thoughts on “BRE Architecture Series Part 8 – Environment Light Pass

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s