348 строки
11 KiB
C++
348 строки
11 KiB
C++
//--------------------------------------------------------------------------------------
|
|
// File: DualPostProcess.cpp
|
|
//
|
|
// Copyright (c) Microsoft Corporation.
|
|
// Licensed under the MIT License.
|
|
//
|
|
// http://go.microsoft.com/fwlink/?LinkID=615561
|
|
//--------------------------------------------------------------------------------------
|
|
|
|
#include "pch.h"
|
|
#include "PostProcess.h"
|
|
|
|
#include "AlignedNew.h"
|
|
#include "CommonStates.h"
|
|
#include "DemandCreate.h"
|
|
#include "DirectXHelpers.h"
|
|
#include "EffectPipelineStateDescription.h"
|
|
#include "GraphicsMemory.h"
|
|
#include "SharedResourcePool.h"
|
|
|
|
using namespace DirectX;
|
|
|
|
using Microsoft::WRL::ComPtr;
|
|
|
|
namespace
|
|
{
|
|
constexpr int c_MaxSamples = 16;
|
|
|
|
constexpr int Dirty_ConstantBuffer = 0x01;
|
|
constexpr int Dirty_Parameters = 0x02;
|
|
|
|
// Constant buffer layout. Must match the shader!
|
|
XM_ALIGNED_STRUCT(16) PostProcessConstants
|
|
{
|
|
XMVECTOR sampleOffsets[c_MaxSamples];
|
|
XMVECTOR sampleWeights[c_MaxSamples];
|
|
};
|
|
|
|
static_assert((sizeof(PostProcessConstants) % 16) == 0, "CB size not padded correctly");
|
|
}
|
|
|
|
// Include the precompiled shader code.
|
|
namespace
|
|
{
|
|
#ifdef _GAMING_XBOX_SCARLETT
|
|
#include "XboxGamingScarlettPostProcess_VSQuadDual.inc"
|
|
|
|
#include "XboxGamingScarlettPostProcess_PSMerge.inc"
|
|
#include "XboxGamingScarlettPostProcess_PSBloomCombine.inc"
|
|
#elif defined(_GAMING_XBOX)
|
|
#include "XboxGamingXboxOnePostProcess_VSQuadDual.inc"
|
|
|
|
#include "XboxGamingXboxOnePostProcess_PSMerge.inc"
|
|
#include "XboxGamingXboxOnePostProcess_PSBloomCombine.inc"
|
|
#elif defined(_XBOX_ONE) && defined(_TITLE)
|
|
#include "XboxOnePostProcess_VSQuadDual.inc"
|
|
|
|
#include "XboxOnePostProcess_PSMerge.inc"
|
|
#include "XboxOnePostProcess_PSBloomCombine.inc"
|
|
#else
|
|
#include "PostProcess_VSQuadDual.inc"
|
|
|
|
#include "PostProcess_PSMerge.inc"
|
|
#include "PostProcess_PSBloomCombine.inc"
|
|
#endif
|
|
}
|
|
|
|
namespace
|
|
{
|
|
const D3D12_SHADER_BYTECODE vertexShader =
|
|
{ PostProcess_VSQuadDual, sizeof(PostProcess_VSQuadDual) };
|
|
|
|
const D3D12_SHADER_BYTECODE pixelShaders[] =
|
|
{
|
|
{ PostProcess_PSMerge, sizeof(PostProcess_PSMerge) },
|
|
{ PostProcess_PSBloomCombine, sizeof(PostProcess_PSBloomCombine) },
|
|
};
|
|
|
|
static_assert(std::size(pixelShaders) == DualPostProcess::Effect_Max, "array/max mismatch");
|
|
|
|
// Factory for lazily instantiating shared root signatures.
|
|
class DeviceResources
|
|
{
|
|
public:
|
|
DeviceResources(_In_ ID3D12Device* device) noexcept
|
|
: mDevice(device)
|
|
{ }
|
|
|
|
ID3D12RootSignature* GetRootSignature(const D3D12_ROOT_SIGNATURE_DESC& desc)
|
|
{
|
|
return DemandCreate(mRootSignature, mMutex, [&](ID3D12RootSignature** pResult) noexcept -> HRESULT
|
|
{
|
|
HRESULT hr = CreateRootSignature(mDevice.Get(), &desc, pResult);
|
|
|
|
if (SUCCEEDED(hr))
|
|
SetDebugObjectName(*pResult, L"DualPostProcess");
|
|
|
|
return hr;
|
|
});
|
|
}
|
|
|
|
ID3D12Device* GetDevice() const noexcept { return mDevice.Get(); }
|
|
|
|
protected:
|
|
ComPtr<ID3D12Device> mDevice;
|
|
ComPtr<ID3D12RootSignature> mRootSignature;
|
|
std::mutex mMutex;
|
|
};
|
|
}
|
|
|
|
class DualPostProcess::Impl : public AlignedNew<PostProcessConstants>
|
|
{
|
|
public:
|
|
Impl(_In_ ID3D12Device* device, const RenderTargetState& rtState, Effect ifx);
|
|
|
|
void Process(_In_ ID3D12GraphicsCommandList* commandList);
|
|
|
|
void SetDirtyFlag() noexcept { mDirtyFlags = INT_MAX; }
|
|
|
|
enum RootParameterIndex
|
|
{
|
|
TextureSRV,
|
|
TextureSRV2,
|
|
ConstantBuffer,
|
|
RootParameterCount
|
|
};
|
|
|
|
// Fields.
|
|
DualPostProcess::Effect fx;
|
|
PostProcessConstants constants;
|
|
D3D12_GPU_DESCRIPTOR_HANDLE texture;
|
|
D3D12_GPU_DESCRIPTOR_HANDLE texture2;
|
|
float mergeWeight1;
|
|
float mergeWeight2;
|
|
float bloomIntensity;
|
|
float bloomBaseIntensity;
|
|
float bloomSaturation;
|
|
float bloomBaseSaturation;
|
|
|
|
private:
|
|
int mDirtyFlags;
|
|
|
|
// D3D constant buffer holds a copy of the same data as the public 'constants' field.
|
|
GraphicsResource mConstantBuffer;
|
|
|
|
// Per instance cache of PSOs, populated with variants for each shader & layout
|
|
Microsoft::WRL::ComPtr<ID3D12PipelineState> mPipelineState;
|
|
|
|
// Per instance root signature
|
|
ID3D12RootSignature* mRootSignature;
|
|
|
|
// Per-device resources.
|
|
std::shared_ptr<DeviceResources> mDeviceResources;
|
|
|
|
static SharedResourcePool<ID3D12Device*, DeviceResources> deviceResourcesPool;
|
|
};
|
|
|
|
|
|
// Global pool of per-device DualPostProcess resources.
|
|
SharedResourcePool<ID3D12Device*, DeviceResources> DualPostProcess::Impl::deviceResourcesPool;
|
|
|
|
|
|
// Constructor.
|
|
DualPostProcess::Impl::Impl(_In_ ID3D12Device* device, const RenderTargetState& rtState, Effect ifx)
|
|
: fx(ifx),
|
|
constants{},
|
|
texture{},
|
|
texture2{},
|
|
mergeWeight1(0.5f),
|
|
mergeWeight2(0.5f),
|
|
bloomIntensity(1.25f),
|
|
bloomBaseIntensity(1.f),
|
|
bloomSaturation(1.f),
|
|
bloomBaseSaturation(1.f),
|
|
mDirtyFlags(INT_MAX),
|
|
mDeviceResources(deviceResourcesPool.DemandCreate(device))
|
|
{
|
|
if (ifx >= Effect_Max)
|
|
throw std::invalid_argument("Effect not defined");
|
|
|
|
// Create root signature.
|
|
{
|
|
D3D12_ROOT_SIGNATURE_FLAGS rootSignatureFlags =
|
|
D3D12_ROOT_SIGNATURE_FLAG_DENY_VERTEX_SHADER_ROOT_ACCESS |
|
|
D3D12_ROOT_SIGNATURE_FLAG_DENY_DOMAIN_SHADER_ROOT_ACCESS |
|
|
D3D12_ROOT_SIGNATURE_FLAG_DENY_GEOMETRY_SHADER_ROOT_ACCESS |
|
|
D3D12_ROOT_SIGNATURE_FLAG_DENY_HULL_SHADER_ROOT_ACCESS;
|
|
|
|
// Same as CommonStates::StaticLinearClamp
|
|
CD3DX12_STATIC_SAMPLER_DESC sampler(
|
|
0, // register
|
|
D3D12_FILTER_MIN_MAG_MIP_LINEAR,
|
|
D3D12_TEXTURE_ADDRESS_MODE_CLAMP,
|
|
D3D12_TEXTURE_ADDRESS_MODE_CLAMP,
|
|
D3D12_TEXTURE_ADDRESS_MODE_CLAMP,
|
|
0.f,
|
|
16,
|
|
D3D12_COMPARISON_FUNC_LESS_EQUAL,
|
|
D3D12_STATIC_BORDER_COLOR_OPAQUE_WHITE,
|
|
0.f,
|
|
D3D12_FLOAT32_MAX,
|
|
D3D12_SHADER_VISIBILITY_PIXEL);
|
|
|
|
CD3DX12_ROOT_PARAMETER rootParameters[RootParameterIndex::RootParameterCount] = {};
|
|
|
|
CD3DX12_DESCRIPTOR_RANGE texture1Range(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 0);
|
|
rootParameters[RootParameterIndex::TextureSRV].InitAsDescriptorTable(1, &texture1Range, D3D12_SHADER_VISIBILITY_PIXEL);
|
|
|
|
CD3DX12_DESCRIPTOR_RANGE texture2Range(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 1);
|
|
rootParameters[RootParameterIndex::TextureSRV2].InitAsDescriptorTable(1, &texture2Range, D3D12_SHADER_VISIBILITY_PIXEL);
|
|
|
|
// Root parameter descriptor
|
|
CD3DX12_ROOT_SIGNATURE_DESC rsigDesc = {};
|
|
|
|
// Constant buffer
|
|
rootParameters[RootParameterIndex::ConstantBuffer].InitAsConstantBufferView(0, 0, D3D12_SHADER_VISIBILITY_PIXEL);
|
|
|
|
rsigDesc.Init(static_cast<UINT>(std::size(rootParameters)), rootParameters, 1, &sampler, rootSignatureFlags);
|
|
|
|
mRootSignature = mDeviceResources->GetRootSignature(rsigDesc);
|
|
}
|
|
|
|
assert(mRootSignature != nullptr);
|
|
|
|
// Create pipeline state.
|
|
EffectPipelineStateDescription psd(nullptr,
|
|
CommonStates::Opaque,
|
|
CommonStates::DepthNone,
|
|
CommonStates::CullNone,
|
|
rtState,
|
|
D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE);
|
|
|
|
psd.CreatePipelineState(
|
|
device,
|
|
mRootSignature,
|
|
vertexShader,
|
|
pixelShaders[ifx],
|
|
mPipelineState.GetAddressOf());
|
|
|
|
SetDebugObjectName(mPipelineState.Get(), L"DualPostProcess");
|
|
}
|
|
|
|
|
|
// Sets our state onto the D3D device.
|
|
void DualPostProcess::Impl::Process(_In_ ID3D12GraphicsCommandList* commandList)
|
|
{
|
|
// Set the root signature.
|
|
commandList->SetGraphicsRootSignature(mRootSignature);
|
|
|
|
// Set the texture.
|
|
if (!texture.ptr || !texture2.ptr)
|
|
{
|
|
DebugTrace("ERROR: Missing texture(s) for DualPostProcess (%llu, %llu)\n", texture.ptr, texture2.ptr);
|
|
throw std::runtime_error("DualPostProcess");
|
|
}
|
|
commandList->SetGraphicsRootDescriptorTable(RootParameterIndex::TextureSRV, texture);
|
|
commandList->SetGraphicsRootDescriptorTable(RootParameterIndex::TextureSRV2, texture2);
|
|
|
|
// Set constants.
|
|
if (mDirtyFlags & Dirty_Parameters)
|
|
{
|
|
mDirtyFlags &= ~Dirty_Parameters;
|
|
mDirtyFlags |= Dirty_ConstantBuffer;
|
|
|
|
switch (fx)
|
|
{
|
|
case Merge:
|
|
constants.sampleWeights[0] = XMVectorReplicate(mergeWeight1);
|
|
constants.sampleWeights[1] = XMVectorReplicate(mergeWeight2);
|
|
break;
|
|
|
|
case BloomCombine:
|
|
constants.sampleWeights[0] = XMVectorSet(bloomBaseSaturation, bloomSaturation, 0.f, 0.f);
|
|
constants.sampleWeights[1] = XMVectorReplicate(bloomBaseIntensity);
|
|
constants.sampleWeights[2] = XMVectorReplicate(bloomIntensity);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (mDirtyFlags & Dirty_ConstantBuffer)
|
|
{
|
|
mDirtyFlags &= ~Dirty_ConstantBuffer;
|
|
mConstantBuffer = GraphicsMemory::Get(mDeviceResources->GetDevice()).AllocateConstant(constants);
|
|
}
|
|
|
|
commandList->SetGraphicsRootConstantBufferView(RootParameterIndex::ConstantBuffer, mConstantBuffer.GpuAddress());
|
|
|
|
// Set the pipeline state.
|
|
commandList->SetPipelineState(mPipelineState.Get());
|
|
|
|
// Draw quad.
|
|
commandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
|
|
commandList->DrawInstanced(3, 1, 0, 0);
|
|
}
|
|
|
|
|
|
// Public constructor.
|
|
DualPostProcess::DualPostProcess(_In_ ID3D12Device* device, const RenderTargetState& rtState, Effect fx)
|
|
: pImpl(std::make_unique<Impl>(device, rtState, fx))
|
|
{
|
|
}
|
|
|
|
|
|
DualPostProcess::DualPostProcess(DualPostProcess&&) noexcept = default;
|
|
DualPostProcess& DualPostProcess::operator= (DualPostProcess&&) noexcept = default;
|
|
DualPostProcess::~DualPostProcess() = default;
|
|
|
|
|
|
// IPostProcess methods.
|
|
void DualPostProcess::Process(_In_ ID3D12GraphicsCommandList* commandList)
|
|
{
|
|
pImpl->Process(commandList);
|
|
}
|
|
|
|
|
|
// Properties
|
|
void DualPostProcess::SetSourceTexture(D3D12_GPU_DESCRIPTOR_HANDLE srvDescriptor)
|
|
{
|
|
pImpl->texture = srvDescriptor;
|
|
}
|
|
|
|
|
|
void DualPostProcess::SetSourceTexture2(D3D12_GPU_DESCRIPTOR_HANDLE srvDescriptor)
|
|
{
|
|
pImpl->texture2 = srvDescriptor;
|
|
}
|
|
|
|
|
|
void DualPostProcess::SetMergeParameters(float weight1, float weight2)
|
|
{
|
|
pImpl->mergeWeight1 = weight1;
|
|
pImpl->mergeWeight2 = weight2;
|
|
pImpl->SetDirtyFlag();
|
|
}
|
|
|
|
|
|
void DualPostProcess::SetBloomCombineParameters(float bloom, float base, float bloomSaturation, float baseSaturation)
|
|
{
|
|
pImpl->bloomIntensity = bloom;
|
|
pImpl->bloomBaseIntensity = base;
|
|
pImpl->bloomSaturation = bloomSaturation;
|
|
pImpl->bloomBaseSaturation = baseSaturation;
|
|
pImpl->SetDirtyFlag();
|
|
}
|