Add material render state and pipeline caching
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <XCEngine/Rendering/RenderMaterialUtility.h>
|
||||||
#include <XCEngine/Rendering/RenderPipeline.h>
|
#include <XCEngine/Rendering/RenderPipeline.h>
|
||||||
#include <XCEngine/Rendering/RenderResourceCache.h>
|
#include <XCEngine/Rendering/RenderResourceCache.h>
|
||||||
|
|
||||||
@@ -11,6 +12,8 @@
|
|||||||
#include <XCEngine/RHI/RHISampler.h>
|
#include <XCEngine/RHI/RHISampler.h>
|
||||||
#include <XCEngine/RHI/RHITexture.h>
|
#include <XCEngine/RHI/RHITexture.h>
|
||||||
|
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
namespace XCEngine {
|
namespace XCEngine {
|
||||||
namespace Resources {
|
namespace Resources {
|
||||||
class Material;
|
class Material;
|
||||||
@@ -44,6 +47,9 @@ private:
|
|||||||
bool EnsureInitialized(const RenderContext& context);
|
bool EnsureInitialized(const RenderContext& context);
|
||||||
bool CreatePipelineResources(const RenderContext& context);
|
bool CreatePipelineResources(const RenderContext& context);
|
||||||
void DestroyPipelineResources();
|
void DestroyPipelineResources();
|
||||||
|
RHI::RHIPipelineState* GetOrCreatePipelineState(
|
||||||
|
const RenderContext& context,
|
||||||
|
const Resources::Material* material);
|
||||||
|
|
||||||
const Resources::Texture* ResolveTexture(const Resources::Material* material) const;
|
const Resources::Texture* ResolveTexture(const Resources::Material* material) const;
|
||||||
RHI::RHIResourceView* ResolveTextureView(const VisibleRenderItem& visibleItem);
|
RHI::RHIResourceView* ResolveTextureView(const VisibleRenderItem& visibleItem);
|
||||||
@@ -65,7 +71,7 @@ private:
|
|||||||
RHI::RHIDescriptorPool* m_samplerPool = nullptr;
|
RHI::RHIDescriptorPool* m_samplerPool = nullptr;
|
||||||
RHI::RHIDescriptorSet* m_samplerSet = nullptr;
|
RHI::RHIDescriptorSet* m_samplerSet = nullptr;
|
||||||
RHI::RHIPipelineLayout* m_pipelineLayout = nullptr;
|
RHI::RHIPipelineLayout* m_pipelineLayout = nullptr;
|
||||||
RHI::RHIPipelineState* m_pipelineState = nullptr;
|
std::unordered_map<Resources::MaterialRenderState, RHI::RHIPipelineState*, MaterialRenderStateHash> m_pipelineStates;
|
||||||
RHI::RHISampler* m_sampler = nullptr;
|
RHI::RHISampler* m_sampler = nullptr;
|
||||||
RHI::RHITexture* m_fallbackTexture = nullptr;
|
RHI::RHITexture* m_fallbackTexture = nullptr;
|
||||||
RHI::RHIResourceView* m_fallbackTextureView = nullptr;
|
RHI::RHIResourceView* m_fallbackTextureView = nullptr;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include <XCEngine/Components/MeshRendererComponent.h>
|
#include <XCEngine/Components/MeshRendererComponent.h>
|
||||||
#include <XCEngine/Core/Types.h>
|
#include <XCEngine/Core/Types.h>
|
||||||
|
#include <XCEngine/RHI/RHITypes.h>
|
||||||
#include <XCEngine/Resources/Material/Material.h>
|
#include <XCEngine/Resources/Material/Material.h>
|
||||||
#include <XCEngine/Resources/Mesh/Mesh.h>
|
#include <XCEngine/Resources/Mesh/Mesh.h>
|
||||||
#include <XCEngine/Rendering/VisibleRenderObject.h>
|
#include <XCEngine/Rendering/VisibleRenderObject.h>
|
||||||
@@ -93,5 +94,173 @@ inline bool MatchesBuiltinPass(const Resources::Material* material, BuiltinMater
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline RHI::CullMode ToRHICullMode(Resources::MaterialCullMode mode) {
|
||||||
|
switch (mode) {
|
||||||
|
case Resources::MaterialCullMode::Front:
|
||||||
|
return RHI::CullMode::Front;
|
||||||
|
case Resources::MaterialCullMode::Back:
|
||||||
|
return RHI::CullMode::Back;
|
||||||
|
case Resources::MaterialCullMode::None:
|
||||||
|
default:
|
||||||
|
return RHI::CullMode::None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline RHI::ComparisonFunc ToRHIComparisonFunc(Resources::MaterialComparisonFunc func) {
|
||||||
|
switch (func) {
|
||||||
|
case Resources::MaterialComparisonFunc::Never:
|
||||||
|
return RHI::ComparisonFunc::Never;
|
||||||
|
case Resources::MaterialComparisonFunc::Equal:
|
||||||
|
return RHI::ComparisonFunc::Equal;
|
||||||
|
case Resources::MaterialComparisonFunc::LessEqual:
|
||||||
|
return RHI::ComparisonFunc::LessEqual;
|
||||||
|
case Resources::MaterialComparisonFunc::Greater:
|
||||||
|
return RHI::ComparisonFunc::Greater;
|
||||||
|
case Resources::MaterialComparisonFunc::NotEqual:
|
||||||
|
return RHI::ComparisonFunc::NotEqual;
|
||||||
|
case Resources::MaterialComparisonFunc::GreaterEqual:
|
||||||
|
return RHI::ComparisonFunc::GreaterEqual;
|
||||||
|
case Resources::MaterialComparisonFunc::Always:
|
||||||
|
return RHI::ComparisonFunc::Always;
|
||||||
|
case Resources::MaterialComparisonFunc::Less:
|
||||||
|
default:
|
||||||
|
return RHI::ComparisonFunc::Less;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline RHI::BlendFactor ToRHIBlendFactor(Resources::MaterialBlendFactor factor) {
|
||||||
|
switch (factor) {
|
||||||
|
case Resources::MaterialBlendFactor::Zero:
|
||||||
|
return RHI::BlendFactor::Zero;
|
||||||
|
case Resources::MaterialBlendFactor::SrcColor:
|
||||||
|
return RHI::BlendFactor::SrcColor;
|
||||||
|
case Resources::MaterialBlendFactor::InvSrcColor:
|
||||||
|
return RHI::BlendFactor::InvSrcColor;
|
||||||
|
case Resources::MaterialBlendFactor::SrcAlpha:
|
||||||
|
return RHI::BlendFactor::SrcAlpha;
|
||||||
|
case Resources::MaterialBlendFactor::InvSrcAlpha:
|
||||||
|
return RHI::BlendFactor::InvSrcAlpha;
|
||||||
|
case Resources::MaterialBlendFactor::DstAlpha:
|
||||||
|
return RHI::BlendFactor::DstAlpha;
|
||||||
|
case Resources::MaterialBlendFactor::InvDstAlpha:
|
||||||
|
return RHI::BlendFactor::InvDstAlpha;
|
||||||
|
case Resources::MaterialBlendFactor::DstColor:
|
||||||
|
return RHI::BlendFactor::DstColor;
|
||||||
|
case Resources::MaterialBlendFactor::InvDstColor:
|
||||||
|
return RHI::BlendFactor::InvDstColor;
|
||||||
|
case Resources::MaterialBlendFactor::SrcAlphaSat:
|
||||||
|
return RHI::BlendFactor::SrcAlphaSat;
|
||||||
|
case Resources::MaterialBlendFactor::BlendFactor:
|
||||||
|
return RHI::BlendFactor::BlendFactor;
|
||||||
|
case Resources::MaterialBlendFactor::InvBlendFactor:
|
||||||
|
return RHI::BlendFactor::InvBlendFactor;
|
||||||
|
case Resources::MaterialBlendFactor::Src1Color:
|
||||||
|
return RHI::BlendFactor::Src1Color;
|
||||||
|
case Resources::MaterialBlendFactor::InvSrc1Color:
|
||||||
|
return RHI::BlendFactor::InvSrc1Color;
|
||||||
|
case Resources::MaterialBlendFactor::Src1Alpha:
|
||||||
|
return RHI::BlendFactor::Src1Alpha;
|
||||||
|
case Resources::MaterialBlendFactor::InvSrc1Alpha:
|
||||||
|
return RHI::BlendFactor::InvSrc1Alpha;
|
||||||
|
case Resources::MaterialBlendFactor::One:
|
||||||
|
default:
|
||||||
|
return RHI::BlendFactor::One;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline RHI::BlendOp ToRHIBlendOp(Resources::MaterialBlendOp op) {
|
||||||
|
switch (op) {
|
||||||
|
case Resources::MaterialBlendOp::Subtract:
|
||||||
|
return RHI::BlendOp::Subtract;
|
||||||
|
case Resources::MaterialBlendOp::ReverseSubtract:
|
||||||
|
return RHI::BlendOp::ReverseSubtract;
|
||||||
|
case Resources::MaterialBlendOp::Min:
|
||||||
|
return RHI::BlendOp::Min;
|
||||||
|
case Resources::MaterialBlendOp::Max:
|
||||||
|
return RHI::BlendOp::Max;
|
||||||
|
case Resources::MaterialBlendOp::Add:
|
||||||
|
default:
|
||||||
|
return RHI::BlendOp::Add;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline RHI::RasterizerDesc BuildRasterizerState(const Resources::Material* material) {
|
||||||
|
RHI::RasterizerDesc desc = {};
|
||||||
|
desc.fillMode = static_cast<uint32_t>(RHI::FillMode::Solid);
|
||||||
|
desc.cullMode = static_cast<uint32_t>(RHI::CullMode::None);
|
||||||
|
desc.frontFace = static_cast<uint32_t>(RHI::FrontFace::CounterClockwise);
|
||||||
|
desc.depthClipEnable = true;
|
||||||
|
|
||||||
|
if (material != nullptr) {
|
||||||
|
const Resources::MaterialRenderState& renderState = material->GetRenderState();
|
||||||
|
desc.cullMode = static_cast<uint32_t>(ToRHICullMode(renderState.cullMode));
|
||||||
|
}
|
||||||
|
|
||||||
|
return desc;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline RHI::BlendDesc BuildBlendState(const Resources::Material* material) {
|
||||||
|
RHI::BlendDesc desc = {};
|
||||||
|
if (material != nullptr) {
|
||||||
|
const Resources::MaterialRenderState& renderState = material->GetRenderState();
|
||||||
|
desc.blendEnable = renderState.blendEnable;
|
||||||
|
desc.srcBlend = static_cast<uint32_t>(ToRHIBlendFactor(renderState.srcBlend));
|
||||||
|
desc.dstBlend = static_cast<uint32_t>(ToRHIBlendFactor(renderState.dstBlend));
|
||||||
|
desc.srcBlendAlpha = static_cast<uint32_t>(ToRHIBlendFactor(renderState.srcBlendAlpha));
|
||||||
|
desc.dstBlendAlpha = static_cast<uint32_t>(ToRHIBlendFactor(renderState.dstBlendAlpha));
|
||||||
|
desc.blendOp = static_cast<uint32_t>(ToRHIBlendOp(renderState.blendOp));
|
||||||
|
desc.blendOpAlpha = static_cast<uint32_t>(ToRHIBlendOp(renderState.blendOpAlpha));
|
||||||
|
desc.colorWriteMask = renderState.colorWriteMask;
|
||||||
|
}
|
||||||
|
|
||||||
|
return desc;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline RHI::DepthStencilStateDesc BuildDepthStencilState(const Resources::Material* material) {
|
||||||
|
RHI::DepthStencilStateDesc desc = {};
|
||||||
|
desc.depthTestEnable = true;
|
||||||
|
desc.depthWriteEnable = true;
|
||||||
|
desc.depthFunc = static_cast<uint32_t>(RHI::ComparisonFunc::Less);
|
||||||
|
desc.stencilEnable = false;
|
||||||
|
|
||||||
|
if (material != nullptr) {
|
||||||
|
const Resources::MaterialRenderState& renderState = material->GetRenderState();
|
||||||
|
desc.depthTestEnable = renderState.depthTestEnable;
|
||||||
|
desc.depthWriteEnable = renderState.depthWriteEnable;
|
||||||
|
desc.depthFunc = static_cast<uint32_t>(ToRHIComparisonFunc(renderState.depthFunc));
|
||||||
|
}
|
||||||
|
|
||||||
|
return desc;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void ApplyMaterialRenderState(const Resources::Material* material, RHI::GraphicsPipelineDesc& pipelineDesc) {
|
||||||
|
pipelineDesc.rasterizerState = BuildRasterizerState(material);
|
||||||
|
pipelineDesc.blendState = BuildBlendState(material);
|
||||||
|
pipelineDesc.depthStencilState = BuildDepthStencilState(material);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct MaterialRenderStateHash {
|
||||||
|
size_t operator()(const Resources::MaterialRenderState& state) const noexcept {
|
||||||
|
size_t hash = 2166136261u;
|
||||||
|
auto combine = [&hash](size_t value) {
|
||||||
|
hash ^= value + 0x9e3779b9u + (hash << 6) + (hash >> 2);
|
||||||
|
};
|
||||||
|
|
||||||
|
combine(static_cast<size_t>(state.blendEnable));
|
||||||
|
combine(static_cast<size_t>(state.srcBlend));
|
||||||
|
combine(static_cast<size_t>(state.dstBlend));
|
||||||
|
combine(static_cast<size_t>(state.srcBlendAlpha));
|
||||||
|
combine(static_cast<size_t>(state.dstBlendAlpha));
|
||||||
|
combine(static_cast<size_t>(state.blendOp));
|
||||||
|
combine(static_cast<size_t>(state.blendOpAlpha));
|
||||||
|
combine(static_cast<size_t>(state.colorWriteMask));
|
||||||
|
combine(static_cast<size_t>(state.depthTestEnable));
|
||||||
|
combine(static_cast<size_t>(state.depthWriteEnable));
|
||||||
|
combine(static_cast<size_t>(state.depthFunc));
|
||||||
|
combine(static_cast<size_t>(state.cullMode));
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace Rendering
|
} // namespace Rendering
|
||||||
} // namespace XCEngine
|
} // namespace XCEngine
|
||||||
|
|||||||
@@ -22,6 +22,87 @@ enum class MaterialRenderQueue : Core::int32 {
|
|||||||
Overlay = 4000
|
Overlay = 4000
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class MaterialCullMode : Core::uint8 {
|
||||||
|
None = 0,
|
||||||
|
Front = 1,
|
||||||
|
Back = 2
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class MaterialComparisonFunc : Core::uint8 {
|
||||||
|
Never = 0,
|
||||||
|
Less = 1,
|
||||||
|
Equal = 2,
|
||||||
|
LessEqual = 3,
|
||||||
|
Greater = 4,
|
||||||
|
NotEqual = 5,
|
||||||
|
GreaterEqual = 6,
|
||||||
|
Always = 7
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class MaterialBlendOp : Core::uint8 {
|
||||||
|
Add = 0,
|
||||||
|
Subtract = 1,
|
||||||
|
ReverseSubtract = 2,
|
||||||
|
Min = 3,
|
||||||
|
Max = 4
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class MaterialBlendFactor : Core::uint8 {
|
||||||
|
Zero = 0,
|
||||||
|
One = 1,
|
||||||
|
SrcColor = 2,
|
||||||
|
InvSrcColor = 3,
|
||||||
|
SrcAlpha = 4,
|
||||||
|
InvSrcAlpha = 5,
|
||||||
|
DstAlpha = 6,
|
||||||
|
InvDstAlpha = 7,
|
||||||
|
DstColor = 8,
|
||||||
|
InvDstColor = 9,
|
||||||
|
SrcAlphaSat = 10,
|
||||||
|
BlendFactor = 11,
|
||||||
|
InvBlendFactor = 12,
|
||||||
|
Src1Color = 13,
|
||||||
|
InvSrc1Color = 14,
|
||||||
|
Src1Alpha = 15,
|
||||||
|
InvSrc1Alpha = 16
|
||||||
|
};
|
||||||
|
|
||||||
|
struct MaterialRenderState {
|
||||||
|
bool blendEnable = false;
|
||||||
|
MaterialBlendFactor srcBlend = MaterialBlendFactor::One;
|
||||||
|
MaterialBlendFactor dstBlend = MaterialBlendFactor::Zero;
|
||||||
|
MaterialBlendFactor srcBlendAlpha = MaterialBlendFactor::One;
|
||||||
|
MaterialBlendFactor dstBlendAlpha = MaterialBlendFactor::Zero;
|
||||||
|
MaterialBlendOp blendOp = MaterialBlendOp::Add;
|
||||||
|
MaterialBlendOp blendOpAlpha = MaterialBlendOp::Add;
|
||||||
|
Core::uint8 colorWriteMask = 0xF;
|
||||||
|
|
||||||
|
bool depthTestEnable = true;
|
||||||
|
bool depthWriteEnable = true;
|
||||||
|
MaterialComparisonFunc depthFunc = MaterialComparisonFunc::Less;
|
||||||
|
|
||||||
|
MaterialCullMode cullMode = MaterialCullMode::None;
|
||||||
|
|
||||||
|
bool operator==(const MaterialRenderState& other) const {
|
||||||
|
return blendEnable == other.blendEnable &&
|
||||||
|
srcBlend == other.srcBlend &&
|
||||||
|
dstBlend == other.dstBlend &&
|
||||||
|
srcBlendAlpha == other.srcBlendAlpha &&
|
||||||
|
dstBlendAlpha == other.dstBlendAlpha &&
|
||||||
|
blendOp == other.blendOp &&
|
||||||
|
blendOpAlpha == other.blendOpAlpha &&
|
||||||
|
colorWriteMask == other.colorWriteMask &&
|
||||||
|
depthTestEnable == other.depthTestEnable &&
|
||||||
|
depthWriteEnable == other.depthWriteEnable &&
|
||||||
|
depthFunc == other.depthFunc &&
|
||||||
|
cullMode == other.cullMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(const MaterialRenderState& other) const {
|
||||||
|
return !(*this == other);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
enum class MaterialPropertyType {
|
enum class MaterialPropertyType {
|
||||||
Float, Float2, Float3, Float4,
|
Float, Float2, Float3, Float4,
|
||||||
Int, Int2, Int3, Int4,
|
Int, Int2, Int3, Int4,
|
||||||
@@ -65,6 +146,9 @@ public:
|
|||||||
void SetRenderQueue(MaterialRenderQueue renderQueue);
|
void SetRenderQueue(MaterialRenderQueue renderQueue);
|
||||||
Core::int32 GetRenderQueue() const { return m_renderQueue; }
|
Core::int32 GetRenderQueue() const { return m_renderQueue; }
|
||||||
|
|
||||||
|
void SetRenderState(const MaterialRenderState& renderState);
|
||||||
|
const MaterialRenderState& GetRenderState() const { return m_renderState; }
|
||||||
|
|
||||||
void SetShaderPass(const Containers::String& shaderPass);
|
void SetShaderPass(const Containers::String& shaderPass);
|
||||||
const Containers::String& GetShaderPass() const { return m_shaderPass; }
|
const Containers::String& GetShaderPass() const { return m_shaderPass; }
|
||||||
|
|
||||||
@@ -105,6 +189,7 @@ private:
|
|||||||
|
|
||||||
ResourceHandle<class Shader> m_shader;
|
ResourceHandle<class Shader> m_shader;
|
||||||
Core::int32 m_renderQueue = static_cast<Core::int32>(MaterialRenderQueue::Geometry);
|
Core::int32 m_renderQueue = static_cast<Core::int32>(MaterialRenderQueue::Geometry);
|
||||||
|
MaterialRenderState m_renderState;
|
||||||
Containers::String m_shaderPass;
|
Containers::String m_shaderPass;
|
||||||
struct TagEntry {
|
struct TagEntry {
|
||||||
Containers::String name;
|
Containers::String name;
|
||||||
|
|||||||
@@ -86,7 +86,10 @@ void main() {
|
|||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
|
|
||||||
RHI::GraphicsPipelineDesc CreatePipelineDesc(RHI::RHIType backendType, RHI::RHIPipelineLayout* pipelineLayout) {
|
RHI::GraphicsPipelineDesc CreatePipelineDesc(
|
||||||
|
RHI::RHIType backendType,
|
||||||
|
RHI::RHIPipelineLayout* pipelineLayout,
|
||||||
|
const Resources::Material* material) {
|
||||||
RHI::GraphicsPipelineDesc pipelineDesc = {};
|
RHI::GraphicsPipelineDesc pipelineDesc = {};
|
||||||
pipelineDesc.pipelineLayout = pipelineLayout;
|
pipelineDesc.pipelineLayout = pipelineLayout;
|
||||||
pipelineDesc.topologyType = static_cast<uint32_t>(RHI::PrimitiveTopologyType::Triangle);
|
pipelineDesc.topologyType = static_cast<uint32_t>(RHI::PrimitiveTopologyType::Triangle);
|
||||||
@@ -94,16 +97,7 @@ RHI::GraphicsPipelineDesc CreatePipelineDesc(RHI::RHIType backendType, RHI::RHIP
|
|||||||
pipelineDesc.renderTargetFormats[0] = static_cast<uint32_t>(RHI::Format::R8G8B8A8_UNorm);
|
pipelineDesc.renderTargetFormats[0] = static_cast<uint32_t>(RHI::Format::R8G8B8A8_UNorm);
|
||||||
pipelineDesc.depthStencilFormat = static_cast<uint32_t>(RHI::Format::D24_UNorm_S8_UInt);
|
pipelineDesc.depthStencilFormat = static_cast<uint32_t>(RHI::Format::D24_UNorm_S8_UInt);
|
||||||
pipelineDesc.sampleCount = 1;
|
pipelineDesc.sampleCount = 1;
|
||||||
|
ApplyMaterialRenderState(material, pipelineDesc);
|
||||||
pipelineDesc.rasterizerState.fillMode = static_cast<uint32_t>(RHI::FillMode::Solid);
|
|
||||||
pipelineDesc.rasterizerState.cullMode = static_cast<uint32_t>(RHI::CullMode::None);
|
|
||||||
pipelineDesc.rasterizerState.frontFace = static_cast<uint32_t>(RHI::FrontFace::CounterClockwise);
|
|
||||||
pipelineDesc.rasterizerState.depthClipEnable = true;
|
|
||||||
|
|
||||||
pipelineDesc.depthStencilState.depthTestEnable = true;
|
|
||||||
pipelineDesc.depthStencilState.depthWriteEnable = true;
|
|
||||||
pipelineDesc.depthStencilState.depthFunc = static_cast<uint32_t>(RHI::ComparisonFunc::Less);
|
|
||||||
pipelineDesc.depthStencilState.stencilEnable = false;
|
|
||||||
|
|
||||||
RHI::InputElementDesc position = {};
|
RHI::InputElementDesc position = {};
|
||||||
position.semanticName = "POSITION";
|
position.semanticName = "POSITION";
|
||||||
@@ -248,14 +242,24 @@ bool BuiltinForwardPipeline::Render(
|
|||||||
commandList->ClearDepthStencil(surface.GetDepthAttachment(), 1.0f, 0);
|
commandList->ClearDepthStencil(surface.GetDepthAttachment(), 1.0f, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
commandList->SetPipelineState(m_pipelineState);
|
|
||||||
commandList->SetPrimitiveTopology(RHI::PrimitiveTopology::TriangleList);
|
commandList->SetPrimitiveTopology(RHI::PrimitiveTopology::TriangleList);
|
||||||
|
|
||||||
|
RHI::RHIPipelineState* currentPipelineState = nullptr;
|
||||||
for (const VisibleRenderItem& visibleItem : sceneData.visibleItems) {
|
for (const VisibleRenderItem& visibleItem : sceneData.visibleItems) {
|
||||||
if (!MatchesBuiltinPass(ResolveMaterial(visibleItem), BuiltinMaterialPass::Forward)) {
|
const Resources::Material* material = ResolveMaterial(visibleItem);
|
||||||
|
if (!MatchesBuiltinPass(material, BuiltinMaterialPass::Forward)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RHI::RHIPipelineState* pipelineState = GetOrCreatePipelineState(context, material);
|
||||||
|
if (pipelineState == nullptr) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (pipelineState != currentPipelineState) {
|
||||||
|
commandList->SetPipelineState(pipelineState);
|
||||||
|
currentPipelineState = pipelineState;
|
||||||
|
}
|
||||||
|
|
||||||
DrawVisibleItem(context, sceneData, visibleItem);
|
DrawVisibleItem(context, sceneData, visibleItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -387,12 +391,6 @@ bool BuiltinForwardPipeline::CreatePipelineResources(const RenderContext& contex
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const RHI::GraphicsPipelineDesc pipelineDesc = CreatePipelineDesc(context.backendType, m_pipelineLayout);
|
|
||||||
m_pipelineState = context.device->CreatePipelineState(pipelineDesc);
|
|
||||||
if (m_pipelineState == nullptr || !m_pipelineState->IsValid()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
RHI::SamplerDesc samplerDesc = {};
|
RHI::SamplerDesc samplerDesc = {};
|
||||||
samplerDesc.filter = static_cast<uint32_t>(RHI::FilterMode::Linear);
|
samplerDesc.filter = static_cast<uint32_t>(RHI::FilterMode::Linear);
|
||||||
samplerDesc.addressU = static_cast<uint32_t>(RHI::TextureAddressMode::Clamp);
|
samplerDesc.addressU = static_cast<uint32_t>(RHI::TextureAddressMode::Clamp);
|
||||||
@@ -441,6 +439,14 @@ bool BuiltinForwardPipeline::CreatePipelineResources(const RenderContext& contex
|
|||||||
void BuiltinForwardPipeline::DestroyPipelineResources() {
|
void BuiltinForwardPipeline::DestroyPipelineResources() {
|
||||||
m_resourceCache.Shutdown();
|
m_resourceCache.Shutdown();
|
||||||
|
|
||||||
|
for (auto& pipelinePair : m_pipelineStates) {
|
||||||
|
if (pipelinePair.second != nullptr) {
|
||||||
|
pipelinePair.second->Shutdown();
|
||||||
|
delete pipelinePair.second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_pipelineStates.clear();
|
||||||
|
|
||||||
if (m_fallbackTextureView != nullptr) {
|
if (m_fallbackTextureView != nullptr) {
|
||||||
m_fallbackTextureView->Shutdown();
|
m_fallbackTextureView->Shutdown();
|
||||||
delete m_fallbackTextureView;
|
delete m_fallbackTextureView;
|
||||||
@@ -459,12 +465,6 @@ void BuiltinForwardPipeline::DestroyPipelineResources() {
|
|||||||
m_sampler = nullptr;
|
m_sampler = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_pipelineState != nullptr) {
|
|
||||||
m_pipelineState->Shutdown();
|
|
||||||
delete m_pipelineState;
|
|
||||||
m_pipelineState = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_pipelineLayout != nullptr) {
|
if (m_pipelineLayout != nullptr) {
|
||||||
m_pipelineLayout->Shutdown();
|
m_pipelineLayout->Shutdown();
|
||||||
delete m_pipelineLayout;
|
delete m_pipelineLayout;
|
||||||
@@ -511,6 +511,32 @@ void BuiltinForwardPipeline::DestroyPipelineResources() {
|
|||||||
m_initialized = false;
|
m_initialized = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RHI::RHIPipelineState* BuiltinForwardPipeline::GetOrCreatePipelineState(
|
||||||
|
const RenderContext& context,
|
||||||
|
const Resources::Material* material) {
|
||||||
|
const Resources::MaterialRenderState renderState =
|
||||||
|
material != nullptr ? material->GetRenderState() : Resources::MaterialRenderState();
|
||||||
|
|
||||||
|
const auto existing = m_pipelineStates.find(renderState);
|
||||||
|
if (existing != m_pipelineStates.end()) {
|
||||||
|
return existing->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
const RHI::GraphicsPipelineDesc pipelineDesc =
|
||||||
|
CreatePipelineDesc(context.backendType, m_pipelineLayout, material);
|
||||||
|
RHI::RHIPipelineState* pipelineState = context.device->CreatePipelineState(pipelineDesc);
|
||||||
|
if (pipelineState == nullptr || !pipelineState->IsValid()) {
|
||||||
|
if (pipelineState != nullptr) {
|
||||||
|
pipelineState->Shutdown();
|
||||||
|
delete pipelineState;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_pipelineStates.emplace(renderState, pipelineState);
|
||||||
|
return pipelineState;
|
||||||
|
}
|
||||||
|
|
||||||
const Resources::Texture* BuiltinForwardPipeline::ResolveTexture(const Resources::Material* material) const {
|
const Resources::Texture* BuiltinForwardPipeline::ResolveTexture(const Resources::Material* material) const {
|
||||||
return material != nullptr ? FindMaterialTexture(*material) : nullptr;
|
return material != nullptr ? FindMaterialTexture(*material) : nullptr;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ Material::~Material() = default;
|
|||||||
void Material::Release() {
|
void Material::Release() {
|
||||||
m_shader.Reset();
|
m_shader.Reset();
|
||||||
m_renderQueue = static_cast<Core::int32>(MaterialRenderQueue::Geometry);
|
m_renderQueue = static_cast<Core::int32>(MaterialRenderQueue::Geometry);
|
||||||
|
m_renderState = MaterialRenderState();
|
||||||
m_shaderPass.Clear();
|
m_shaderPass.Clear();
|
||||||
m_tags.Clear();
|
m_tags.Clear();
|
||||||
m_properties.Clear();
|
m_properties.Clear();
|
||||||
@@ -34,6 +35,11 @@ void Material::SetRenderQueue(MaterialRenderQueue renderQueue) {
|
|||||||
SetRenderQueue(static_cast<Core::int32>(renderQueue));
|
SetRenderQueue(static_cast<Core::int32>(renderQueue));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Material::SetRenderState(const MaterialRenderState& renderState) {
|
||||||
|
m_renderState = renderState;
|
||||||
|
UpdateMemorySize();
|
||||||
|
}
|
||||||
|
|
||||||
void Material::SetShaderPass(const Containers::String& shaderPass) {
|
void Material::SetShaderPass(const Containers::String& shaderPass) {
|
||||||
m_shaderPass = shaderPass;
|
m_shaderPass = shaderPass;
|
||||||
UpdateMemorySize();
|
UpdateMemorySize();
|
||||||
@@ -267,6 +273,7 @@ void Material::ClearAllProperties() {
|
|||||||
|
|
||||||
void Material::UpdateMemorySize() {
|
void Material::UpdateMemorySize() {
|
||||||
m_memorySize = m_constantBufferData.Size() +
|
m_memorySize = m_constantBufferData.Size() +
|
||||||
|
sizeof(MaterialRenderState) +
|
||||||
m_shaderPass.Length() +
|
m_shaderPass.Length() +
|
||||||
m_tags.Size() * sizeof(TagEntry) +
|
m_tags.Size() * sizeof(TagEntry) +
|
||||||
m_textureBindings.Size() * sizeof(TextureBinding) +
|
m_textureBindings.Size() * sizeof(TextureBinding) +
|
||||||
|
|||||||
@@ -109,6 +109,24 @@ bool TryParseIntValue(const std::string& json, const char* key, Core::int32& out
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool TryParseBoolValue(const std::string& json, const char* key, bool& outValue) {
|
||||||
|
size_t valuePos = 0;
|
||||||
|
if (!FindValueStart(json, key, valuePos)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (json.compare(valuePos, 4, "true") == 0) {
|
||||||
|
outValue = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (json.compare(valuePos, 5, "false") == 0) {
|
||||||
|
outValue = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool TryExtractObject(const std::string& json, const char* key, std::string& outObject) {
|
bool TryExtractObject(const std::string& json, const char* key, std::string& outObject) {
|
||||||
size_t valuePos = 0;
|
size_t valuePos = 0;
|
||||||
if (!FindValueStart(json, key, valuePos) || valuePos >= json.size() || json[valuePos] != '{') {
|
if (!FindValueStart(json, key, valuePos) || valuePos >= json.size() || json[valuePos] != '{') {
|
||||||
@@ -234,6 +252,270 @@ bool TryParseTagMap(const std::string& objectText, Material* material) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool TryParseCullMode(const Containers::String& value, MaterialCullMode& outMode) {
|
||||||
|
const Containers::String normalized = value.Trim().ToLower();
|
||||||
|
if (normalized == "none" || normalized == "off") {
|
||||||
|
outMode = MaterialCullMode::None;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (normalized == "front") {
|
||||||
|
outMode = MaterialCullMode::Front;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (normalized == "back") {
|
||||||
|
outMode = MaterialCullMode::Back;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TryParseComparisonFunc(const Containers::String& value, MaterialComparisonFunc& outFunc) {
|
||||||
|
const Containers::String normalized = value.Trim().ToLower();
|
||||||
|
if (normalized == "never") {
|
||||||
|
outFunc = MaterialComparisonFunc::Never;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (normalized == "less") {
|
||||||
|
outFunc = MaterialComparisonFunc::Less;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (normalized == "equal") {
|
||||||
|
outFunc = MaterialComparisonFunc::Equal;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (normalized == "lessequal" || normalized == "less_equal" || normalized == "lequal") {
|
||||||
|
outFunc = MaterialComparisonFunc::LessEqual;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (normalized == "greater") {
|
||||||
|
outFunc = MaterialComparisonFunc::Greater;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (normalized == "notequal" || normalized == "not_equal") {
|
||||||
|
outFunc = MaterialComparisonFunc::NotEqual;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (normalized == "greaterequal" || normalized == "greater_equal" || normalized == "gequal") {
|
||||||
|
outFunc = MaterialComparisonFunc::GreaterEqual;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (normalized == "always") {
|
||||||
|
outFunc = MaterialComparisonFunc::Always;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TryParseBlendFactor(const Containers::String& value, MaterialBlendFactor& outFactor) {
|
||||||
|
const Containers::String normalized = value.Trim().ToLower();
|
||||||
|
if (normalized == "zero") {
|
||||||
|
outFactor = MaterialBlendFactor::Zero;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (normalized == "one") {
|
||||||
|
outFactor = MaterialBlendFactor::One;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (normalized == "srccolor" || normalized == "src_color") {
|
||||||
|
outFactor = MaterialBlendFactor::SrcColor;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (normalized == "invsrccolor" || normalized == "inv_src_color" || normalized == "one_minus_src_color") {
|
||||||
|
outFactor = MaterialBlendFactor::InvSrcColor;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (normalized == "srcalpha" || normalized == "src_alpha") {
|
||||||
|
outFactor = MaterialBlendFactor::SrcAlpha;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (normalized == "invsrcalpha" || normalized == "inv_src_alpha" || normalized == "one_minus_src_alpha") {
|
||||||
|
outFactor = MaterialBlendFactor::InvSrcAlpha;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (normalized == "dstalpha" || normalized == "dst_alpha") {
|
||||||
|
outFactor = MaterialBlendFactor::DstAlpha;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (normalized == "invdstalpha" || normalized == "inv_dst_alpha" || normalized == "one_minus_dst_alpha") {
|
||||||
|
outFactor = MaterialBlendFactor::InvDstAlpha;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (normalized == "dstcolor" || normalized == "dst_color") {
|
||||||
|
outFactor = MaterialBlendFactor::DstColor;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (normalized == "invdstcolor" || normalized == "inv_dst_color" || normalized == "one_minus_dst_color") {
|
||||||
|
outFactor = MaterialBlendFactor::InvDstColor;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (normalized == "srcalphasat" || normalized == "src_alpha_sat") {
|
||||||
|
outFactor = MaterialBlendFactor::SrcAlphaSat;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (normalized == "blendfactor" || normalized == "blend_factor") {
|
||||||
|
outFactor = MaterialBlendFactor::BlendFactor;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (normalized == "invblendfactor" || normalized == "inv_blend_factor" || normalized == "one_minus_blend_factor") {
|
||||||
|
outFactor = MaterialBlendFactor::InvBlendFactor;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (normalized == "src1color" || normalized == "src1_color") {
|
||||||
|
outFactor = MaterialBlendFactor::Src1Color;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (normalized == "invsrc1color" || normalized == "inv_src1_color" || normalized == "one_minus_src1_color") {
|
||||||
|
outFactor = MaterialBlendFactor::InvSrc1Color;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (normalized == "src1alpha" || normalized == "src1_alpha") {
|
||||||
|
outFactor = MaterialBlendFactor::Src1Alpha;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (normalized == "invsrc1alpha" || normalized == "inv_src1_alpha" || normalized == "one_minus_src1_alpha") {
|
||||||
|
outFactor = MaterialBlendFactor::InvSrc1Alpha;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TryParseBlendOp(const Containers::String& value, MaterialBlendOp& outOp) {
|
||||||
|
const Containers::String normalized = value.Trim().ToLower();
|
||||||
|
if (normalized == "add") {
|
||||||
|
outOp = MaterialBlendOp::Add;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (normalized == "subtract") {
|
||||||
|
outOp = MaterialBlendOp::Subtract;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (normalized == "reversesubtract" || normalized == "reverse_subtract") {
|
||||||
|
outOp = MaterialBlendOp::ReverseSubtract;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (normalized == "min") {
|
||||||
|
outOp = MaterialBlendOp::Min;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (normalized == "max") {
|
||||||
|
outOp = MaterialBlendOp::Max;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TryParseRenderStateObject(const std::string& objectText, Material* material) {
|
||||||
|
if (material == nullptr || objectText.empty() || objectText.front() != '{' || objectText.back() != '}') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
MaterialRenderState renderState = material->GetRenderState();
|
||||||
|
|
||||||
|
Containers::String enumValue;
|
||||||
|
if (HasKey(objectText, "cull")) {
|
||||||
|
if (!TryParseStringValue(objectText, "cull", enumValue) ||
|
||||||
|
!TryParseCullMode(enumValue, renderState.cullMode)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool boolValue = false;
|
||||||
|
if (HasKey(objectText, "depthTest")) {
|
||||||
|
if (!TryParseBoolValue(objectText, "depthTest", boolValue)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
renderState.depthTestEnable = boolValue;
|
||||||
|
}
|
||||||
|
if (HasKey(objectText, "depthWrite")) {
|
||||||
|
if (!TryParseBoolValue(objectText, "depthWrite", boolValue)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
renderState.depthWriteEnable = boolValue;
|
||||||
|
}
|
||||||
|
if (HasKey(objectText, "zWrite")) {
|
||||||
|
if (!TryParseBoolValue(objectText, "zWrite", boolValue)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
renderState.depthWriteEnable = boolValue;
|
||||||
|
}
|
||||||
|
if (HasKey(objectText, "blend")) {
|
||||||
|
if (!TryParseBoolValue(objectText, "blend", boolValue)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
renderState.blendEnable = boolValue;
|
||||||
|
}
|
||||||
|
if (HasKey(objectText, "blendEnable")) {
|
||||||
|
if (!TryParseBoolValue(objectText, "blendEnable", boolValue)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
renderState.blendEnable = boolValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (HasKey(objectText, "depthFunc")) {
|
||||||
|
if (!TryParseStringValue(objectText, "depthFunc", enumValue) ||
|
||||||
|
!TryParseComparisonFunc(enumValue, renderState.depthFunc)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (HasKey(objectText, "zTest")) {
|
||||||
|
if (!TryParseStringValue(objectText, "zTest", enumValue) ||
|
||||||
|
!TryParseComparisonFunc(enumValue, renderState.depthFunc)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (HasKey(objectText, "colorWriteMask")) {
|
||||||
|
Core::int32 colorWriteMask = 0;
|
||||||
|
if (!TryParseIntValue(objectText, "colorWriteMask", colorWriteMask) ||
|
||||||
|
colorWriteMask < 0 || colorWriteMask > 0xF) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
renderState.colorWriteMask = static_cast<Core::uint8>(colorWriteMask);
|
||||||
|
}
|
||||||
|
if (HasKey(objectText, "srcBlend")) {
|
||||||
|
if (!TryParseStringValue(objectText, "srcBlend", enumValue) ||
|
||||||
|
!TryParseBlendFactor(enumValue, renderState.srcBlend)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (HasKey(objectText, "dstBlend")) {
|
||||||
|
if (!TryParseStringValue(objectText, "dstBlend", enumValue) ||
|
||||||
|
!TryParseBlendFactor(enumValue, renderState.dstBlend)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (HasKey(objectText, "srcBlendAlpha")) {
|
||||||
|
if (!TryParseStringValue(objectText, "srcBlendAlpha", enumValue) ||
|
||||||
|
!TryParseBlendFactor(enumValue, renderState.srcBlendAlpha)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (HasKey(objectText, "dstBlendAlpha")) {
|
||||||
|
if (!TryParseStringValue(objectText, "dstBlendAlpha", enumValue) ||
|
||||||
|
!TryParseBlendFactor(enumValue, renderState.dstBlendAlpha)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (HasKey(objectText, "blendOp")) {
|
||||||
|
if (!TryParseStringValue(objectText, "blendOp", enumValue) ||
|
||||||
|
!TryParseBlendOp(enumValue, renderState.blendOp)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (HasKey(objectText, "blendOpAlpha")) {
|
||||||
|
if (!TryParseStringValue(objectText, "blendOpAlpha", enumValue) ||
|
||||||
|
!TryParseBlendOp(enumValue, renderState.blendOpAlpha)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
material->SetRenderState(renderState);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
ResourceHandle<Shader> LoadShaderHandle(const Containers::String& shaderPath) {
|
ResourceHandle<Shader> LoadShaderHandle(const Containers::String& shaderPath) {
|
||||||
ResourceHandle<Shader> shader = ResourceManager::Get().Load<Shader>(shaderPath);
|
ResourceHandle<Shader> shader = ResourceManager::Get().Load<Shader>(shaderPath);
|
||||||
if (shader.IsValid()) {
|
if (shader.IsValid()) {
|
||||||
@@ -349,6 +631,14 @@ bool MaterialLoader::ParseMaterialData(const Containers::Array<Core::uint8>& dat
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (HasKey(jsonText, "renderState")) {
|
||||||
|
std::string renderStateObject;
|
||||||
|
if (!TryExtractObject(jsonText, "renderState", renderStateObject) ||
|
||||||
|
!TryParseRenderStateObject(renderStateObject, material)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -265,4 +265,42 @@ TEST(RenderMaterialUtility_Test, MatchesBuiltinForwardPassMetadata) {
|
|||||||
EXPECT_FALSE(MatchesBuiltinPass(&depthOnlyMaterial, BuiltinMaterialPass::Forward));
|
EXPECT_FALSE(MatchesBuiltinPass(&depthOnlyMaterial, BuiltinMaterialPass::Forward));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(RenderMaterialUtility_Test, MapsMaterialRenderStateToRhiDescriptors) {
|
||||||
|
Material material;
|
||||||
|
MaterialRenderState renderState;
|
||||||
|
renderState.cullMode = MaterialCullMode::Back;
|
||||||
|
renderState.blendEnable = true;
|
||||||
|
renderState.srcBlend = MaterialBlendFactor::SrcAlpha;
|
||||||
|
renderState.dstBlend = MaterialBlendFactor::InvSrcAlpha;
|
||||||
|
renderState.srcBlendAlpha = MaterialBlendFactor::One;
|
||||||
|
renderState.dstBlendAlpha = MaterialBlendFactor::InvSrcAlpha;
|
||||||
|
renderState.blendOp = MaterialBlendOp::Add;
|
||||||
|
renderState.blendOpAlpha = MaterialBlendOp::Subtract;
|
||||||
|
renderState.colorWriteMask = 0x7;
|
||||||
|
renderState.depthTestEnable = true;
|
||||||
|
renderState.depthWriteEnable = false;
|
||||||
|
renderState.depthFunc = MaterialComparisonFunc::LessEqual;
|
||||||
|
material.SetRenderState(renderState);
|
||||||
|
|
||||||
|
const XCEngine::RHI::RasterizerDesc rasterizerState = BuildRasterizerState(&material);
|
||||||
|
const XCEngine::RHI::BlendDesc blendState = BuildBlendState(&material);
|
||||||
|
const XCEngine::RHI::DepthStencilStateDesc depthStencilState = BuildDepthStencilState(&material);
|
||||||
|
|
||||||
|
EXPECT_EQ(rasterizerState.cullMode, static_cast<uint32_t>(XCEngine::RHI::CullMode::Back));
|
||||||
|
EXPECT_EQ(rasterizerState.frontFace, static_cast<uint32_t>(XCEngine::RHI::FrontFace::CounterClockwise));
|
||||||
|
|
||||||
|
EXPECT_TRUE(blendState.blendEnable);
|
||||||
|
EXPECT_EQ(blendState.srcBlend, static_cast<uint32_t>(XCEngine::RHI::BlendFactor::SrcAlpha));
|
||||||
|
EXPECT_EQ(blendState.dstBlend, static_cast<uint32_t>(XCEngine::RHI::BlendFactor::InvSrcAlpha));
|
||||||
|
EXPECT_EQ(blendState.srcBlendAlpha, static_cast<uint32_t>(XCEngine::RHI::BlendFactor::One));
|
||||||
|
EXPECT_EQ(blendState.dstBlendAlpha, static_cast<uint32_t>(XCEngine::RHI::BlendFactor::InvSrcAlpha));
|
||||||
|
EXPECT_EQ(blendState.blendOp, static_cast<uint32_t>(XCEngine::RHI::BlendOp::Add));
|
||||||
|
EXPECT_EQ(blendState.blendOpAlpha, static_cast<uint32_t>(XCEngine::RHI::BlendOp::Subtract));
|
||||||
|
EXPECT_EQ(blendState.colorWriteMask, 0x7);
|
||||||
|
|
||||||
|
EXPECT_TRUE(depthStencilState.depthTestEnable);
|
||||||
|
EXPECT_FALSE(depthStencilState.depthWriteEnable);
|
||||||
|
EXPECT_EQ(depthStencilState.depthFunc, static_cast<uint32_t>(XCEngine::RHI::ComparisonFunc::LessEqual));
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|||||||
@@ -37,6 +37,11 @@ TEST(Material, SetGetShader) {
|
|||||||
TEST(Material, DefaultRenderMetadata) {
|
TEST(Material, DefaultRenderMetadata) {
|
||||||
Material material;
|
Material material;
|
||||||
EXPECT_EQ(material.GetRenderQueue(), static_cast<XCEngine::Core::int32>(MaterialRenderQueue::Geometry));
|
EXPECT_EQ(material.GetRenderQueue(), static_cast<XCEngine::Core::int32>(MaterialRenderQueue::Geometry));
|
||||||
|
EXPECT_EQ(material.GetRenderState().cullMode, MaterialCullMode::None);
|
||||||
|
EXPECT_FALSE(material.GetRenderState().blendEnable);
|
||||||
|
EXPECT_TRUE(material.GetRenderState().depthTestEnable);
|
||||||
|
EXPECT_TRUE(material.GetRenderState().depthWriteEnable);
|
||||||
|
EXPECT_EQ(material.GetRenderState().depthFunc, MaterialComparisonFunc::Less);
|
||||||
EXPECT_TRUE(material.GetShaderPass().Empty());
|
EXPECT_TRUE(material.GetShaderPass().Empty());
|
||||||
EXPECT_EQ(material.GetTagCount(), 0u);
|
EXPECT_EQ(material.GetTagCount(), 0u);
|
||||||
}
|
}
|
||||||
@@ -51,6 +56,40 @@ TEST(Material, SetGetRenderQueue) {
|
|||||||
EXPECT_EQ(material.GetRenderQueue(), 2600);
|
EXPECT_EQ(material.GetRenderQueue(), 2600);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(Material, SetGetRenderState) {
|
||||||
|
Material material;
|
||||||
|
|
||||||
|
MaterialRenderState renderState;
|
||||||
|
renderState.cullMode = MaterialCullMode::Back;
|
||||||
|
renderState.blendEnable = true;
|
||||||
|
renderState.srcBlend = MaterialBlendFactor::SrcAlpha;
|
||||||
|
renderState.dstBlend = MaterialBlendFactor::InvSrcAlpha;
|
||||||
|
renderState.srcBlendAlpha = MaterialBlendFactor::One;
|
||||||
|
renderState.dstBlendAlpha = MaterialBlendFactor::InvSrcAlpha;
|
||||||
|
renderState.blendOp = MaterialBlendOp::Add;
|
||||||
|
renderState.blendOpAlpha = MaterialBlendOp::Add;
|
||||||
|
renderState.depthTestEnable = true;
|
||||||
|
renderState.depthWriteEnable = false;
|
||||||
|
renderState.depthFunc = MaterialComparisonFunc::LessEqual;
|
||||||
|
renderState.colorWriteMask = 0x7;
|
||||||
|
|
||||||
|
material.SetRenderState(renderState);
|
||||||
|
|
||||||
|
const MaterialRenderState& result = material.GetRenderState();
|
||||||
|
EXPECT_EQ(result.cullMode, MaterialCullMode::Back);
|
||||||
|
EXPECT_TRUE(result.blendEnable);
|
||||||
|
EXPECT_EQ(result.srcBlend, MaterialBlendFactor::SrcAlpha);
|
||||||
|
EXPECT_EQ(result.dstBlend, MaterialBlendFactor::InvSrcAlpha);
|
||||||
|
EXPECT_EQ(result.srcBlendAlpha, MaterialBlendFactor::One);
|
||||||
|
EXPECT_EQ(result.dstBlendAlpha, MaterialBlendFactor::InvSrcAlpha);
|
||||||
|
EXPECT_EQ(result.blendOp, MaterialBlendOp::Add);
|
||||||
|
EXPECT_EQ(result.blendOpAlpha, MaterialBlendOp::Add);
|
||||||
|
EXPECT_TRUE(result.depthTestEnable);
|
||||||
|
EXPECT_FALSE(result.depthWriteEnable);
|
||||||
|
EXPECT_EQ(result.depthFunc, MaterialComparisonFunc::LessEqual);
|
||||||
|
EXPECT_EQ(result.colorWriteMask, 0x7);
|
||||||
|
}
|
||||||
|
|
||||||
TEST(Material, SetGetShaderPass) {
|
TEST(Material, SetGetShaderPass) {
|
||||||
Material material;
|
Material material;
|
||||||
|
|
||||||
|
|||||||
@@ -70,6 +70,14 @@ TEST(MaterialLoader, LoadValidMaterialParsesRenderMetadata) {
|
|||||||
materialFile << " \"tags\": {\n";
|
materialFile << " \"tags\": {\n";
|
||||||
materialFile << " \"LightMode\": \"ForwardBase\",\n";
|
materialFile << " \"LightMode\": \"ForwardBase\",\n";
|
||||||
materialFile << " \"RenderType\": \"Transparent\"\n";
|
materialFile << " \"RenderType\": \"Transparent\"\n";
|
||||||
|
materialFile << " },\n";
|
||||||
|
materialFile << " \"renderState\": {\n";
|
||||||
|
materialFile << " \"cull\": \"Back\",\n";
|
||||||
|
materialFile << " \"blend\": true,\n";
|
||||||
|
materialFile << " \"srcBlend\": \"SrcAlpha\",\n";
|
||||||
|
materialFile << " \"dstBlend\": \"InvSrcAlpha\",\n";
|
||||||
|
materialFile << " \"depthWrite\": false,\n";
|
||||||
|
materialFile << " \"depthFunc\": \"LessEqual\"\n";
|
||||||
materialFile << " }\n";
|
materialFile << " }\n";
|
||||||
materialFile << "}";
|
materialFile << "}";
|
||||||
}
|
}
|
||||||
@@ -87,6 +95,12 @@ TEST(MaterialLoader, LoadValidMaterialParsesRenderMetadata) {
|
|||||||
EXPECT_EQ(material->GetShaderPass(), "ForwardLit");
|
EXPECT_EQ(material->GetShaderPass(), "ForwardLit");
|
||||||
EXPECT_EQ(material->GetTag("LightMode"), "ForwardBase");
|
EXPECT_EQ(material->GetTag("LightMode"), "ForwardBase");
|
||||||
EXPECT_EQ(material->GetTag("RenderType"), "Transparent");
|
EXPECT_EQ(material->GetTag("RenderType"), "Transparent");
|
||||||
|
EXPECT_EQ(material->GetRenderState().cullMode, MaterialCullMode::Back);
|
||||||
|
EXPECT_TRUE(material->GetRenderState().blendEnable);
|
||||||
|
EXPECT_EQ(material->GetRenderState().srcBlend, MaterialBlendFactor::SrcAlpha);
|
||||||
|
EXPECT_EQ(material->GetRenderState().dstBlend, MaterialBlendFactor::InvSrcAlpha);
|
||||||
|
EXPECT_FALSE(material->GetRenderState().depthWriteEnable);
|
||||||
|
EXPECT_EQ(material->GetRenderState().depthFunc, MaterialComparisonFunc::LessEqual);
|
||||||
|
|
||||||
delete material;
|
delete material;
|
||||||
std::remove(materialPath.string().c_str());
|
std::remove(materialPath.string().c_str());
|
||||||
@@ -110,4 +124,21 @@ TEST(MaterialLoader, RejectsUnknownRenderQueueName) {
|
|||||||
std::remove(materialPath.string().c_str());
|
std::remove(materialPath.string().c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(MaterialLoader, RejectsUnknownRenderStateEnum) {
|
||||||
|
const std::filesystem::path materialPath =
|
||||||
|
std::filesystem::current_path() / "material_loader_invalid_render_state.material";
|
||||||
|
|
||||||
|
{
|
||||||
|
std::ofstream materialFile(materialPath);
|
||||||
|
ASSERT_TRUE(materialFile.is_open());
|
||||||
|
materialFile << "{ \"renderState\": { \"cull\": \"Sideways\" } }";
|
||||||
|
}
|
||||||
|
|
||||||
|
MaterialLoader loader;
|
||||||
|
LoadResult result = loader.Load(materialPath.string().c_str());
|
||||||
|
EXPECT_FALSE(result);
|
||||||
|
|
||||||
|
std::remove(materialPath.string().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|||||||
Reference in New Issue
Block a user