393 lines
16 KiB
C++
393 lines
16 KiB
C++
#pragma once
|
|
|
|
#include <XCEngine/Rendering/Execution/DrawSettings.h>
|
|
#include <XCEngine/Rendering/Execution/FrameExecutionContext.h>
|
|
#include <XCEngine/Rendering/Builtin/BuiltinPassTypes.h>
|
|
#include <XCEngine/Rendering/Materials/RenderMaterialResolve.h>
|
|
#include <XCEngine/Rendering/Materials/RenderMaterialStateUtils.h>
|
|
#include <XCEngine/Rendering/RenderPass.h>
|
|
#include <XCEngine/Rendering/RenderPipeline.h>
|
|
#include <XCEngine/Rendering/RenderPipelineAsset.h>
|
|
#include <XCEngine/Rendering/SceneRenderFeaturePass.h>
|
|
#include <XCEngine/Rendering/Caches/RenderResourceCache.h>
|
|
|
|
#include <XCEngine/RHI/RHIDescriptorPool.h>
|
|
#include <XCEngine/RHI/RHIDescriptorSet.h>
|
|
#include <XCEngine/RHI/RHIPipelineLayout.h>
|
|
#include <XCEngine/RHI/RHIPipelineState.h>
|
|
#include <XCEngine/RHI/RHIResourceView.h>
|
|
#include <XCEngine/RHI/RHISampler.h>
|
|
#include <XCEngine/RHI/RHITexture.h>
|
|
|
|
#include <array>
|
|
#include <functional>
|
|
#include <unordered_map>
|
|
#include <vector>
|
|
|
|
namespace XCEngine {
|
|
namespace Components {
|
|
class GameObject;
|
|
} // namespace Components
|
|
|
|
namespace Resources {
|
|
class Material;
|
|
class Shader;
|
|
class Texture;
|
|
} // namespace Resources
|
|
|
|
namespace Rendering {
|
|
class RenderSurface;
|
|
|
|
namespace Pipelines {
|
|
} // namespace Pipelines
|
|
|
|
namespace Passes {
|
|
class BuiltinGaussianSplatPass;
|
|
class BuiltinVolumetricPass;
|
|
} // namespace Passes
|
|
|
|
namespace Pipelines {
|
|
|
|
class BuiltinForwardPipeline : public RenderPipeline {
|
|
public:
|
|
BuiltinForwardPipeline();
|
|
~BuiltinForwardPipeline() override;
|
|
|
|
using RenderPipeline::Render;
|
|
|
|
static RHI::InputLayoutDesc BuildInputLayout();
|
|
|
|
bool Initialize(const RenderContext& context) override;
|
|
void Shutdown() override;
|
|
bool Render(const FrameExecutionContext& executionContext) override;
|
|
bool Render(
|
|
const RenderContext& context,
|
|
const RenderSurface& surface,
|
|
const RenderSceneData& sceneData) override;
|
|
|
|
private:
|
|
using ForwardSceneFeaturePassArray = std::array<SceneRenderFeaturePass*, 2>;
|
|
|
|
struct OwnedDescriptorSet {
|
|
RHI::RHIDescriptorPool* pool = nullptr;
|
|
RHI::RHIDescriptorSet* set = nullptr;
|
|
};
|
|
|
|
struct PerObjectConstants {
|
|
Math::Matrix4x4 projection = Math::Matrix4x4::Identity();
|
|
Math::Matrix4x4 view = Math::Matrix4x4::Identity();
|
|
Math::Matrix4x4 model = Math::Matrix4x4::Identity();
|
|
Math::Matrix4x4 normalMatrix = Math::Matrix4x4::Identity();
|
|
};
|
|
|
|
static constexpr Core::uint32 kMaxLightingAdditionalLightCount =
|
|
RenderLightingData::kMaxAdditionalLightCount;
|
|
|
|
struct AdditionalLightConstants {
|
|
Math::Vector4 colorAndIntensity = Math::Vector4::Zero();
|
|
Math::Vector4 positionAndRange = Math::Vector4::Zero();
|
|
Math::Vector4 directionAndType = Math::Vector4::Zero();
|
|
Math::Vector4 spotAnglesAndFlags = Math::Vector4::Zero();
|
|
};
|
|
|
|
struct LightingConstants {
|
|
Math::Vector4 mainLightDirectionAndIntensity = Math::Vector4::Zero();
|
|
Math::Vector4 mainLightColorAndFlags = Math::Vector4::Zero();
|
|
Math::Vector4 lightingParams = Math::Vector4::Zero();
|
|
std::array<AdditionalLightConstants, kMaxLightingAdditionalLightCount> additionalLights = {};
|
|
};
|
|
|
|
struct ShadowReceiverConstants {
|
|
Math::Matrix4x4 worldToShadow = Math::Matrix4x4::Identity();
|
|
RenderDirectionalShadowMapMetrics shadowMapMetrics = {};
|
|
RenderDirectionalShadowSamplingData shadowSampling = {};
|
|
};
|
|
|
|
struct SkyboxConstants {
|
|
Math::Vector4 topColor = Math::Vector4::Zero();
|
|
Math::Vector4 horizonColor = Math::Vector4::Zero();
|
|
Math::Vector4 bottomColor = Math::Vector4::Zero();
|
|
Math::Vector4 cameraRightAndTanHalfFov = Math::Vector4::Zero();
|
|
Math::Vector4 cameraUpAndAspect = Math::Vector4::Zero();
|
|
Math::Vector4 cameraForwardAndUnused = Math::Vector4::Zero();
|
|
};
|
|
|
|
struct SkyboxMaterialConstants {
|
|
Math::Vector4 tintAndExposure = Math::Vector4(1.0f, 1.0f, 1.0f, 1.0f);
|
|
Math::Vector4 rotationRadiansAndMode = Math::Vector4::Zero();
|
|
};
|
|
|
|
struct PassLayoutKey {
|
|
const Resources::Shader* shader = nullptr;
|
|
Containers::String passName;
|
|
|
|
bool operator==(const PassLayoutKey& other) const {
|
|
return shader == other.shader && passName == other.passName;
|
|
}
|
|
};
|
|
|
|
struct PassLayoutKeyHash {
|
|
size_t operator()(const PassLayoutKey& key) const noexcept {
|
|
size_t hash = reinterpret_cast<size_t>(key.shader);
|
|
hash ^= std::hash<Containers::String>{}(key.passName) + 0x9e3779b9u + (hash << 6) + (hash >> 2);
|
|
return hash;
|
|
}
|
|
};
|
|
|
|
struct PassResourceLayout {
|
|
RHI::RHIPipelineLayout* pipelineLayout = nullptr;
|
|
Core::uint32 firstDescriptorSet = 0;
|
|
Core::uint32 descriptorSetCount = 0;
|
|
std::vector<BuiltinPassSetLayoutMetadata> setLayouts;
|
|
std::vector<OwnedDescriptorSet> staticDescriptorSets;
|
|
PassResourceBindingLocation perObject = {};
|
|
PassResourceBindingLocation material = {};
|
|
PassResourceBindingLocation lighting = {};
|
|
PassResourceBindingLocation shadowReceiver = {};
|
|
PassResourceBindingLocation baseColorTexture = {};
|
|
PassResourceBindingLocation linearClampSampler = {};
|
|
PassResourceBindingLocation shadowMapTexture = {};
|
|
PassResourceBindingLocation shadowMapSampler = {};
|
|
};
|
|
|
|
struct DynamicDescriptorSetKey {
|
|
PassLayoutKey passLayout = {};
|
|
Core::uint32 setIndex = 0;
|
|
Core::uint64 objectId = 0;
|
|
const Resources::Material* material = nullptr;
|
|
|
|
bool operator==(const DynamicDescriptorSetKey& other) const {
|
|
return passLayout == other.passLayout &&
|
|
setIndex == other.setIndex &&
|
|
objectId == other.objectId &&
|
|
material == other.material;
|
|
}
|
|
};
|
|
|
|
struct DynamicDescriptorSetKeyHash {
|
|
size_t operator()(const DynamicDescriptorSetKey& key) const noexcept {
|
|
size_t hash = PassLayoutKeyHash()(key.passLayout);
|
|
auto combine = [&hash](size_t value) {
|
|
hash ^= value + 0x9e3779b9u + (hash << 6) + (hash >> 2);
|
|
};
|
|
|
|
combine(std::hash<Core::uint32>{}(key.setIndex));
|
|
combine(std::hash<Core::uint64>{}(key.objectId));
|
|
combine(reinterpret_cast<size_t>(key.material));
|
|
return hash;
|
|
}
|
|
};
|
|
|
|
struct CachedDescriptorSet {
|
|
OwnedDescriptorSet descriptorSet = {};
|
|
Core::uint64 materialVersion = 0;
|
|
RHI::RHIResourceView* baseColorTextureView = nullptr;
|
|
RHI::RHIResourceView* shadowMapTextureView = nullptr;
|
|
std::vector<RHI::RHIResourceView*> materialTextureViews;
|
|
};
|
|
|
|
struct ResolvedShaderPass {
|
|
const Resources::Shader* shader = nullptr;
|
|
const Resources::ShaderPass* pass = nullptr;
|
|
Containers::String passName;
|
|
};
|
|
|
|
struct PipelineStateKey {
|
|
Resources::MaterialRenderState renderState;
|
|
const Resources::Shader* shader = nullptr;
|
|
Containers::String passName;
|
|
Containers::String keywordSignature;
|
|
Core::uint32 renderTargetCount = 0;
|
|
std::array<Core::uint32, 8> renderTargetFormats = {};
|
|
Core::uint32 depthStencilFormat = 0;
|
|
Core::uint32 sampleCount = 1;
|
|
Core::uint32 sampleQuality = 0;
|
|
|
|
bool operator==(const PipelineStateKey& other) const {
|
|
return renderState == other.renderState &&
|
|
shader == other.shader &&
|
|
passName == other.passName &&
|
|
keywordSignature == other.keywordSignature &&
|
|
renderTargetCount == other.renderTargetCount &&
|
|
renderTargetFormats == other.renderTargetFormats &&
|
|
depthStencilFormat == other.depthStencilFormat &&
|
|
sampleCount == other.sampleCount &&
|
|
sampleQuality == other.sampleQuality;
|
|
}
|
|
};
|
|
|
|
struct PipelineStateKeyHash {
|
|
size_t operator()(const PipelineStateKey& key) const noexcept {
|
|
size_t hash = MaterialRenderStateHash()(key.renderState);
|
|
auto combine = [&hash](size_t value) {
|
|
hash ^= value + 0x9e3779b9u + (hash << 6) + (hash >> 2);
|
|
};
|
|
|
|
combine(reinterpret_cast<size_t>(key.shader));
|
|
combine(std::hash<Containers::String>{}(key.passName));
|
|
combine(std::hash<Containers::String>{}(key.keywordSignature));
|
|
combine(std::hash<Core::uint32>{}(key.renderTargetCount));
|
|
for (Core::uint32 format : key.renderTargetFormats) {
|
|
combine(std::hash<Core::uint32>{}(format));
|
|
}
|
|
combine(std::hash<Core::uint32>{}(key.depthStencilFormat));
|
|
combine(std::hash<Core::uint32>{}(key.sampleCount));
|
|
combine(std::hash<Core::uint32>{}(key.sampleQuality));
|
|
return hash;
|
|
}
|
|
};
|
|
|
|
struct SkyboxPipelineStateKey {
|
|
Core::uint32 renderTargetCount = 0;
|
|
std::array<Core::uint32, 8> renderTargetFormats = {};
|
|
Core::uint32 depthStencilFormat = 0;
|
|
Core::uint32 sampleCount = 1;
|
|
Core::uint32 sampleQuality = 0;
|
|
|
|
bool operator==(const SkyboxPipelineStateKey& other) const {
|
|
return renderTargetCount == other.renderTargetCount &&
|
|
renderTargetFormats == other.renderTargetFormats &&
|
|
depthStencilFormat == other.depthStencilFormat &&
|
|
sampleCount == other.sampleCount &&
|
|
sampleQuality == other.sampleQuality;
|
|
}
|
|
};
|
|
|
|
struct SkyboxPipelineStateKeyHash {
|
|
size_t operator()(const SkyboxPipelineStateKey& key) const noexcept {
|
|
size_t hash = std::hash<Core::uint32>{}(key.renderTargetCount);
|
|
auto combine = [&hash](size_t value) {
|
|
hash ^= value + 0x9e3779b9u + (hash << 6) + (hash >> 2);
|
|
};
|
|
|
|
for (Core::uint32 format : key.renderTargetFormats) {
|
|
combine(std::hash<Core::uint32>{}(format));
|
|
}
|
|
combine(std::hash<Core::uint32>{}(key.depthStencilFormat));
|
|
combine(std::hash<Core::uint32>{}(key.sampleCount));
|
|
combine(std::hash<Core::uint32>{}(key.sampleQuality));
|
|
return hash;
|
|
}
|
|
};
|
|
|
|
bool EnsureInitialized(const RenderContext& context);
|
|
ForwardSceneFeaturePassArray CollectForwardSceneFeaturePasses() const;
|
|
bool InitializeForwardSceneFeaturePasses(const RenderContext& context);
|
|
void ShutdownForwardSceneFeaturePasses();
|
|
bool PrepareForwardSceneFeaturePasses(
|
|
const FrameExecutionContext& executionContext) const;
|
|
bool ExecuteForwardSceneFeaturePasses(
|
|
const ScenePhaseExecutionContext& executionContext) const;
|
|
ScenePhaseExecutionContext BuildScenePhaseExecutionContext(
|
|
const FrameExecutionContext& executionContext,
|
|
ScenePhase scenePhase) const;
|
|
DrawSettings BuildDrawSettings(ScenePhase scenePhase) const;
|
|
bool ExecuteForwardScene(const FrameExecutionContext& executionContext);
|
|
bool ExecuteScenePhase(const ScenePhaseExecutionContext& executionContext);
|
|
bool CreatePipelineResources(const RenderContext& context);
|
|
void DestroyPipelineResources();
|
|
static bool TryResolveSurfacePassType(
|
|
const Resources::Material* material,
|
|
BuiltinMaterialPass& outPass);
|
|
ResolvedShaderPass ResolveSurfaceShaderPass(
|
|
const RenderSceneData& sceneData,
|
|
const Resources::Material* material) const;
|
|
PassResourceLayout* GetOrCreatePassResourceLayout(
|
|
const RenderContext& context,
|
|
const ResolvedShaderPass& resolvedShaderPass);
|
|
RHI::RHIPipelineState* GetOrCreatePipelineState(
|
|
const RenderContext& context,
|
|
const RenderSurface& surface,
|
|
const RenderSceneData& sceneData,
|
|
const Resources::Material* material);
|
|
RHI::RHIPipelineState* GetOrCreateSkyboxPipelineState(
|
|
const RenderContext& context,
|
|
const RenderSurface& surface);
|
|
bool CreateOwnedDescriptorSet(
|
|
const BuiltinPassSetLayoutMetadata& setLayout,
|
|
OwnedDescriptorSet& descriptorSet);
|
|
RHI::RHIDescriptorSet* GetOrCreateStaticDescriptorSet(
|
|
PassResourceLayout& passLayout,
|
|
Core::uint32 setIndex);
|
|
CachedDescriptorSet* GetOrCreateDynamicDescriptorSet(
|
|
const PassLayoutKey& passLayoutKey,
|
|
const PassResourceLayout& passLayout,
|
|
const BuiltinPassSetLayoutMetadata& setLayout,
|
|
Core::uint32 setIndex,
|
|
Core::uint64 objectId,
|
|
const Resources::Material* material,
|
|
const MaterialConstantPayloadView& materialConstants,
|
|
const LightingConstants& lightingConstants,
|
|
const ShadowReceiverConstants& shadowReceiverConstants,
|
|
RHI::RHIResourceView* baseColorTextureView,
|
|
RHI::RHIResourceView* shadowMapTextureView);
|
|
void DestroyOwnedDescriptorSet(OwnedDescriptorSet& descriptorSet);
|
|
void DestroyPassResourceLayout(PassResourceLayout& passLayout);
|
|
|
|
const Resources::Texture* ResolveTexture(const Resources::Material* material) const;
|
|
RHI::RHIResourceView* ResolveTextureView(const Resources::Texture* texture);
|
|
RHI::RHIResourceView* ResolveTextureView(const VisibleRenderItem& visibleItem);
|
|
RHI::RHIResourceView* ResolveMaterialTextureView(
|
|
const Resources::Material* material,
|
|
const BuiltinPassResourceBindingDesc& binding);
|
|
static LightingConstants BuildLightingConstants(const RenderLightingData& lightingData);
|
|
static AdditionalLightConstants BuildAdditionalLightConstants(const RenderAdditionalLightData& lightData);
|
|
bool HasSkybox(const RenderSceneData& sceneData) const;
|
|
bool HasProceduralSkybox(const RenderSceneData& sceneData) const;
|
|
bool BeginForwardScenePass(const RenderPassContext& context);
|
|
void EndForwardScenePass(const RenderPassContext& context);
|
|
bool ExecuteForwardOpaquePass(const ScenePhaseExecutionContext& context);
|
|
bool ExecuteForwardSkyboxPass(const RenderPassContext& context);
|
|
bool ExecuteForwardTransparentPass(const ScenePhaseExecutionContext& context);
|
|
bool DrawVisibleItems(
|
|
const FrameExecutionContext& executionContext,
|
|
const DrawSettings& drawSettings);
|
|
bool DrawVisibleItem(
|
|
const FrameExecutionContext& executionContext,
|
|
const VisibleRenderItem& visibleItem);
|
|
bool EnsureSkyboxResources(const RenderContext& context);
|
|
bool CreateSkyboxResources(const RenderContext& context);
|
|
void DestroySkyboxResources();
|
|
|
|
RHI::RHIDevice* m_device = nullptr;
|
|
RHI::RHIType m_backendType = RHI::RHIType::D3D12;
|
|
bool m_initialized = false;
|
|
Resources::ResourceHandle<Resources::Shader> m_builtinForwardShader;
|
|
Resources::ResourceHandle<Resources::Shader> m_builtinUnlitShader;
|
|
Resources::ResourceHandle<Resources::Shader> m_builtinSkyboxShader;
|
|
|
|
RenderResourceCache m_resourceCache;
|
|
|
|
std::unordered_map<PassLayoutKey, PassResourceLayout, PassLayoutKeyHash> m_passResourceLayouts;
|
|
std::unordered_map<PipelineStateKey, RHI::RHIPipelineState*, PipelineStateKeyHash> m_pipelineStates;
|
|
std::unordered_map<DynamicDescriptorSetKey, CachedDescriptorSet, DynamicDescriptorSetKeyHash> m_dynamicDescriptorSets;
|
|
RHI::RHISampler* m_sampler = nullptr;
|
|
RHI::RHISampler* m_shadowSampler = nullptr;
|
|
RHI::RHITexture* m_fallbackTexture2D = nullptr;
|
|
RHI::RHIResourceView* m_fallbackTexture2DView = nullptr;
|
|
RHI::RHITexture* m_fallbackTextureCube = nullptr;
|
|
RHI::RHIResourceView* m_fallbackTextureCubeView = nullptr;
|
|
RHI::RHIPipelineLayout* m_skyboxPipelineLayout = nullptr;
|
|
Containers::String m_skyboxPassName;
|
|
std::unordered_map<SkyboxPipelineStateKey, RHI::RHIPipelineState*, SkyboxPipelineStateKeyHash> m_skyboxPipelineStates;
|
|
OwnedDescriptorSet m_skyboxEnvironmentSet = {};
|
|
OwnedDescriptorSet m_skyboxMaterialSet = {};
|
|
OwnedDescriptorSet m_skyboxPanoramicTextureSet = {};
|
|
OwnedDescriptorSet m_skyboxCubemapTextureSet = {};
|
|
OwnedDescriptorSet m_skyboxSamplerSet = {};
|
|
RHI::RHIResourceView* m_skyboxBoundPanoramicTextureView = nullptr;
|
|
RHI::RHIResourceView* m_skyboxBoundCubemapTextureView = nullptr;
|
|
std::unique_ptr<Passes::BuiltinGaussianSplatPass> m_gaussianSplatPass;
|
|
std::unique_ptr<Passes::BuiltinVolumetricPass> m_volumetricPass;
|
|
};
|
|
|
|
class BuiltinForwardPipelineAsset final : public RenderPipelineAsset {
|
|
public:
|
|
std::unique_ptr<RenderPipeline> CreatePipeline() const override;
|
|
FinalColorSettings GetDefaultFinalColorSettings() const override { return {}; }
|
|
};
|
|
|
|
} // namespace Pipelines
|
|
} // namespace Rendering
|
|
} // namespace XCEngine
|