From f12e81d504514fcda4cea257220c7e4818f41ce2 Mon Sep 17 00:00:00 2001 From: Chuck Walbourn Date: Mon, 20 Jul 2026 12:00:27 -0700 Subject: [PATCH 1/7] Add SkinnedNPREffect --- Inc/Effects.h | 48 ++- Src/NPREffect.cpp | 536 ++++++++++++++++++++++++++------- Src/Shaders/CompileShaders.cmd | 9 + Src/Shaders/NPREffect.fx | 60 ++++ Src/Shaders/RootSig.fxh | 46 +++ 5 files changed, 593 insertions(+), 106 deletions(-) diff --git a/Inc/Effects.h b/Inc/Effects.h index 3fd7a300..96c8a031 100644 --- a/Inc/Effects.h +++ b/Inc/Effects.h @@ -562,7 +562,7 @@ namespace DirectX DIRECTX_TOOLKIT_API void __cdecl SetSpecularTexture(D3D12_GPU_DESCRIPTOR_HANDLE srvDescriptor); protected: - // Private implementation. + // internal implementation. class Impl; std::unique_ptr pImpl; @@ -663,7 +663,7 @@ namespace DirectX DIRECTX_TOOLKIT_API void __cdecl SetRenderTargetSizeInPixels(int width, int height); protected: - // Private implementation. + // Internal implementation. class Impl; std::unique_ptr pImpl; @@ -674,6 +674,7 @@ namespace DirectX const EffectPipelineStateDescription& pipelineDescription, bool skinningEnabled); + private: // Unsupported interface methods. DIRECTX_TOOLKIT_API void XM_CALLCONV SetAmbientLightColor(FXMVECTOR value) override; DIRECTX_TOOLKIT_API void XM_CALLCONV SetLightSpecularColor(int whichLight, FXMVECTOR value) override; @@ -767,7 +768,9 @@ namespace DirectX _In_ ID3D12Device* device, uint32_t effectFlags, const EffectPipelineStateDescription& pipelineDescription, - Mode nprMode = Mode_Cel); + Mode nprMode = Mode_Cel) : + NPREffect(device, effectFlags, pipelineDescription, nprMode, false) + {} DIRECTX_TOOLKIT_API NPREffect(NPREffect&&) noexcept; DIRECTX_TOOLKIT_API NPREffect& operator= (NPREffect&&) noexcept; @@ -821,12 +824,20 @@ namespace DirectX DIRECTX_TOOLKIT_API void __cdecl SetRimLightingRange(float start, float end); DIRECTX_TOOLKIT_API void __cdecl DisableRimLighting(); - private: - // Private implementation. + protected: + // Internal implementation. class Impl; std::unique_ptr pImpl; + DIRECTX_TOOLKIT_API NPREffect( + _In_ ID3D12Device* device, + uint32_t effectFlags, + const EffectPipelineStateDescription& pipelineDescription, + Mode nprMode, + bool skinningEnabled); + + private: // Unsupported interface methods. DIRECTX_TOOLKIT_API void XM_CALLCONV SetAmbientLightColor(FXMVECTOR value) override; DIRECTX_TOOLKIT_API void __cdecl SetLightEnabled(int whichLight, bool value) override; @@ -834,6 +845,33 @@ namespace DirectX DIRECTX_TOOLKIT_API void XM_CALLCONV SetLightSpecularColor(int whichLight, FXMVECTOR value) override; }; + class DIRECTX_TOOLKIT_API SkinnedNPREffect : public NPREffect, public IEffectSkinning + { + public: + SkinnedNPREffect( + _In_ ID3D12Device* device, + uint32_t effectFlags, + const EffectPipelineStateDescription& pipelineDescription, + Mode nprMode = Mode_Cel) : + NPREffect(device, effectFlags, pipelineDescription, nprMode, true) + {} + + SkinnedNPREffect(SkinnedNPREffect&&) = default; + SkinnedNPREffect& operator= (SkinnedNPREffect&&) = default; + + SkinnedNPREffect(SkinnedNPREffect const&) = delete; + SkinnedNPREffect& operator= (SkinnedNPREffect const&) = delete; + + ~SkinnedNPREffect() override; + + // IEffect methods. + DIRECTX_TOOLKIT_API void __cdecl Apply(_In_ ID3D12GraphicsCommandList* commandList) override; + + // Animation settings. + void __cdecl SetBoneTransforms(_In_reads_(count) XMMATRIX const* value, size_t count) override; + void __cdecl ResetBoneTransforms() override; + }; + //------------------------------------------------------------------------------ // Abstract interface to factory texture resources diff --git a/Src/NPREffect.cpp b/Src/NPREffect.cpp index 62ac253b..d9525573 100644 --- a/Src/NPREffect.cpp +++ b/Src/NPREffect.cpp @@ -10,6 +10,14 @@ #include "pch.h" #include "EffectCommon.h" +namespace DirectX +{ + namespace EffectDirtyFlags + { + constexpr int ConstantBufferBones = 0x100000; + } +} + using namespace DirectX; namespace @@ -32,16 +40,23 @@ namespace static_assert((sizeof(NPREffectConstants) % 16) == 0, "CB size not padded correctly"); + XM_ALIGNED_STRUCT(16) BoneConstants + { + XMVECTOR Bones[SkinnedNPREffect::MaxBones][3]; + }; + + static_assert((sizeof(BoneConstants) % 16) == 0, "CB size not padded correctly"); + // Traits type describes our characteristics to the EffectBase template. struct NPREffectTraits { using ConstantBufferType = NPREffectConstants; - static constexpr int VertexShaderCount = 32; - static constexpr int PixelShaderCount = 6; - static constexpr int ShaderPermutationCount = 48; - static constexpr int RootSignatureCount = 3; + static constexpr int VertexShaderCount = 36; + static constexpr int PixelShaderCount = 9; + static constexpr int ShaderPermutationCount = 54; + static constexpr int RootSignatureCount = 5; }; @@ -60,8 +75,7 @@ namespace class NPREffect::Impl : public EffectBase { public: - Impl(_In_ ID3D12Device* device, uint32_t effectFlags, const EffectPipelineStateDescription& pipelineDescription, - NPREffect::Mode nprMode); + Impl(_In_ ID3D12Device* device); Impl(const Impl&) = delete; Impl& operator=(const Impl&) = delete; @@ -69,6 +83,13 @@ class NPREffect::Impl : public EffectBase Impl(Impl&&) = default; Impl& operator=(Impl&&) = default; + void Initialize( + _In_ ID3D12Device* device, + uint32_t effectFlags, + const EffectPipelineStateDescription& pipelineDescription, + NPREffect::Mode nprMode, + bool enableSkinning); + enum RootParameterIndex { ConstantBuffer, @@ -78,6 +99,17 @@ class NPREffect::Impl : public EffectBase RootParameterCount }; + enum SkinRootParameterIndex + { + SkinConstantBuffer, + SkinTextureSRV, + SkinTextureSampler, + SkinConstantBufferBones, + SkinTexture2SRV, + SkinRootParameterCount + }; + + int weightsPerVertex; bool matCapEnabled; bool textureEnabled; @@ -88,6 +120,12 @@ class NPREffect::Impl : public EffectBase int GetPipelineStatePermutation(NPREffect::Mode nprMode, uint32_t effectFlags) const noexcept; void Apply(_In_ ID3D12GraphicsCommandList* commandList); + void ApplySkinning(_In_ ID3D12GraphicsCommandList* commandList); + + BoneConstants boneConstants; + +private: + GraphicsResource mBones; }; @@ -152,6 +190,17 @@ namespace #include "XboxGamingScarlettNPREffect_PSMatCapShading.inc" #include "XboxGamingScarlettNPREffect_PSMatCapShadingTx.inc" + +#include "XboxGamingScarlettNPREffect_VSSkinnedNPREffectTx.inc" +#include "XboxGamingScarlettNPREffect_VSSkinnedNPREffectTxBn.inc" + +#include "XboxGamingScarlettNPREffect_PSCelShadingSkinTx.inc" +#include "XboxGamingScarlettNPREffect_PSGoochShadingSkinTx.inc" + +#include "XboxGamingScarlettNPREffect_VSSkinnedNPREffectTxMC.inc" +#include "XboxGamingScarlettNPREffect_VSSkinnedNPREffectTxBnMC.inc" + +#include "XboxGamingScarlettNPREffect_PSMatCapShadingSkinTx.inc" #elif defined(_GAMING_XBOX) #include "XboxGamingXboxOneNPREffect_VSNPREffect.inc" #include "XboxGamingXboxOneNPREffect_VSNPREffectInst.inc" @@ -209,6 +258,17 @@ namespace #include "XboxGamingXboxOneNPREffect_PSMatCapShading.inc" #include "XboxGamingXboxOneNPREffect_PSMatCapShadingTx.inc" + +#include "XboxGamingXboxOneNPREffect_VSSkinnedNPREffectTx.inc" +#include "XboxGamingXboxOneNPREffect_VSSkinnedNPREffectTxBn.inc" + +#include "XboxGamingXboxOneNPREffect_PSCelShadingSkinTx.inc" +#include "XboxGamingXboxOneNPREffect_PSGoochShadingSkinTx.inc" + +#include "XboxGamingXboxOneNPREffect_VSSkinnedNPREffectTxMC.inc" +#include "XboxGamingXboxOneNPREffect_VSSkinnedNPREffectTxBnMC.inc" + +#include "XboxGamingXboxOneNPREffect_PSMatCapShadingSkinTx.inc" #elif defined(_XBOX_ONE) && defined(_TITLE) #include "XboxOneNPREffect_VSNPREffect.inc" #include "XboxOneNPREffect_VSNPREffectInst.inc" @@ -266,6 +326,17 @@ namespace #include "XboxOneNPREffect_PSMatCapShading.inc" #include "XboxOneNPREffect_PSMatCapShadingTx.inc" + +#include "XboxOneNPREffect_VSSkinnedNPREffectTx.inc" +#include "XboxOneNPREffect_VSSkinnedNPREffectTxBn.inc" + +#include "XboxOneNPREffect_PSCelShadingSkinTx.inc" +#include "XboxOneNPREffect_PSGoochShadingSkinTx.inc" + +#include "XboxOneNPREffect_VSSkinnedNPREffectTxMC.inc" +#include "XboxOneNPREffect_VSSkinnedNPREffectTxBnMC.inc" + +#include "XboxOneNPREffect_PSMatCapShadingSkinTx.inc" #else #include "NPREffect_VSNPREffect.inc" #include "NPREffect_VSNPREffectInst.inc" @@ -323,6 +394,17 @@ namespace #include "NPREffect_PSMatCapShading.inc" #include "NPREffect_PSMatCapShadingTx.inc" + +#include "NPREffect_VSSkinnedNPREffectTx.inc" +#include "NPREffect_VSSkinnedNPREffectTxBn.inc" + +#include "NPREffect_PSCelShadingSkinTx.inc" +#include "NPREffect_PSGoochShadingSkinTx.inc" + +#include "NPREffect_VSSkinnedNPREffectTxMC.inc" +#include "NPREffect_VSSkinnedNPREffectTxBnMC.inc" + +#include "NPREffect_PSMatCapShadingSkinTx.inc" #endif } @@ -330,38 +412,42 @@ namespace template<> const D3D12_SHADER_BYTECODE EffectBase::VertexShaderBytecode[] = { - { NPREffect_VSNPREffect, sizeof(NPREffect_VSNPREffect) }, - { NPREffect_VSNPREffectVc, sizeof(NPREffect_VSNPREffectVc) }, - { NPREffect_VSNPREffectBn, sizeof(NPREffect_VSNPREffectBn) }, - { NPREffect_VSNPREffectVcBn, sizeof(NPREffect_VSNPREffectVcBn) }, - { NPREffect_VSNPREffectInst, sizeof(NPREffect_VSNPREffectInst) }, - { NPREffect_VSNPREffectVcInst, sizeof(NPREffect_VSNPREffectVcInst) }, - { NPREffect_VSNPREffectBnInst, sizeof(NPREffect_VSNPREffectBnInst) }, - { NPREffect_VSNPREffectVcBnInst, sizeof(NPREffect_VSNPREffectVcBnInst) }, - { NPREffect_VSNPREffectTx, sizeof(NPREffect_VSNPREffectTx) }, - { NPREffect_VSNPREffectVcTx, sizeof(NPREffect_VSNPREffectVcTx) }, - { NPREffect_VSNPREffectBnTx, sizeof(NPREffect_VSNPREffectBnTx) }, - { NPREffect_VSNPREffectVcBnTx, sizeof(NPREffect_VSNPREffectVcBnTx) }, - { NPREffect_VSNPREffectInstTx, sizeof(NPREffect_VSNPREffectInstTx) }, - { NPREffect_VSNPREffectVcInstTx, sizeof(NPREffect_VSNPREffectVcInstTx) }, - { NPREffect_VSNPREffectBnInstTx, sizeof(NPREffect_VSNPREffectBnInstTx) }, - { NPREffect_VSNPREffectVcBnInstTx, sizeof(NPREffect_VSNPREffectVcBnInstTx) }, - { NPREffect_VSNPREffectMC, sizeof(NPREffect_VSNPREffectMC) }, // Different rootsig - { NPREffect_VSNPREffectVcMC, sizeof(NPREffect_VSNPREffectVcMC) }, - { NPREffect_VSNPREffectBnMC, sizeof(NPREffect_VSNPREffectBnMC) }, - { NPREffect_VSNPREffectVcBnMC, sizeof(NPREffect_VSNPREffectVcBnMC) }, - { NPREffect_VSNPREffectInstMC, sizeof(NPREffect_VSNPREffectInstMC) }, - { NPREffect_VSNPREffectVcInstMC, sizeof(NPREffect_VSNPREffectVcInstMC) }, - { NPREffect_VSNPREffectBnInstMC, sizeof(NPREffect_VSNPREffectBnInstMC) }, - { NPREffect_VSNPREffectVcBnInstMC, sizeof(NPREffect_VSNPREffectVcBnInstMC) }, - { NPREffect_VSNPREffectTxMC, sizeof(NPREffect_VSNPREffectTxMC) }, - { NPREffect_VSNPREffectVcTxMC, sizeof(NPREffect_VSNPREffectVcTxMC) }, - { NPREffect_VSNPREffectBnTxMC, sizeof(NPREffect_VSNPREffectBnTxMC) }, - { NPREffect_VSNPREffectVcBnTxMC, sizeof(NPREffect_VSNPREffectVcBnTxMC) }, - { NPREffect_VSNPREffectInstTxMC, sizeof(NPREffect_VSNPREffectInstTxMC) }, - { NPREffect_VSNPREffectVcInstTxMC, sizeof(NPREffect_VSNPREffectVcInstTxMC) }, - { NPREffect_VSNPREffectBnInstTxMC, sizeof(NPREffect_VSNPREffectBnInstTxMC) }, - { NPREffect_VSNPREffectVcBnInstTxMC, sizeof(NPREffect_VSNPREffectVcBnInstTxMC) }, + { NPREffect_VSNPREffect, sizeof(NPREffect_VSNPREffect) }, + { NPREffect_VSNPREffectVc, sizeof(NPREffect_VSNPREffectVc) }, + { NPREffect_VSNPREffectBn, sizeof(NPREffect_VSNPREffectBn) }, + { NPREffect_VSNPREffectVcBn, sizeof(NPREffect_VSNPREffectVcBn) }, + { NPREffect_VSNPREffectInst, sizeof(NPREffect_VSNPREffectInst) }, + { NPREffect_VSNPREffectVcInst, sizeof(NPREffect_VSNPREffectVcInst) }, + { NPREffect_VSNPREffectBnInst, sizeof(NPREffect_VSNPREffectBnInst) }, + { NPREffect_VSNPREffectVcBnInst, sizeof(NPREffect_VSNPREffectVcBnInst) }, + { NPREffect_VSNPREffectTx, sizeof(NPREffect_VSNPREffectTx) }, + { NPREffect_VSNPREffectVcTx, sizeof(NPREffect_VSNPREffectVcTx) }, + { NPREffect_VSNPREffectBnTx, sizeof(NPREffect_VSNPREffectBnTx) }, + { NPREffect_VSNPREffectVcBnTx, sizeof(NPREffect_VSNPREffectVcBnTx) }, + { NPREffect_VSNPREffectInstTx, sizeof(NPREffect_VSNPREffectInstTx) }, + { NPREffect_VSNPREffectVcInstTx, sizeof(NPREffect_VSNPREffectVcInstTx) }, + { NPREffect_VSNPREffectBnInstTx, sizeof(NPREffect_VSNPREffectBnInstTx) }, + { NPREffect_VSNPREffectVcBnInstTx, sizeof(NPREffect_VSNPREffectVcBnInstTx) }, + { NPREffect_VSNPREffectMC, sizeof(NPREffect_VSNPREffectMC) }, // Different rootsig + { NPREffect_VSNPREffectVcMC, sizeof(NPREffect_VSNPREffectVcMC) }, + { NPREffect_VSNPREffectBnMC, sizeof(NPREffect_VSNPREffectBnMC) }, + { NPREffect_VSNPREffectVcBnMC, sizeof(NPREffect_VSNPREffectVcBnMC) }, + { NPREffect_VSNPREffectInstMC, sizeof(NPREffect_VSNPREffectInstMC) }, + { NPREffect_VSNPREffectVcInstMC, sizeof(NPREffect_VSNPREffectVcInstMC) }, + { NPREffect_VSNPREffectBnInstMC, sizeof(NPREffect_VSNPREffectBnInstMC) }, + { NPREffect_VSNPREffectVcBnInstMC, sizeof(NPREffect_VSNPREffectVcBnInstMC) }, + { NPREffect_VSNPREffectTxMC, sizeof(NPREffect_VSNPREffectTxMC) }, + { NPREffect_VSNPREffectVcTxMC, sizeof(NPREffect_VSNPREffectVcTxMC) }, + { NPREffect_VSNPREffectBnTxMC, sizeof(NPREffect_VSNPREffectBnTxMC) }, + { NPREffect_VSNPREffectVcBnTxMC, sizeof(NPREffect_VSNPREffectVcBnTxMC) }, + { NPREffect_VSNPREffectInstTxMC, sizeof(NPREffect_VSNPREffectInstTxMC) }, + { NPREffect_VSNPREffectVcInstTxMC, sizeof(NPREffect_VSNPREffectVcInstTxMC) }, + { NPREffect_VSNPREffectBnInstTxMC, sizeof(NPREffect_VSNPREffectBnInstTxMC) }, + { NPREffect_VSNPREffectVcBnInstTxMC, sizeof(NPREffect_VSNPREffectVcBnInstTxMC) }, + { NPREffect_VSSkinnedNPREffectTx, sizeof(NPREffect_VSSkinnedNPREffectTx) }, + { NPREffect_VSSkinnedNPREffectTxBn, sizeof(NPREffect_VSSkinnedNPREffectTxBn) }, + { NPREffect_VSSkinnedNPREffectTxMC, sizeof(NPREffect_VSSkinnedNPREffectTxMC) }, + { NPREffect_VSSkinnedNPREffectTxBnMC, sizeof(NPREffect_VSSkinnedNPREffectTxBnMC) }, }; @@ -431,18 +517,29 @@ const int EffectBase::VertexShaderIndices[] = 15, // instancing + vertex color (biased vertex normal) + cel shading + texture 15, // instancing + vertex color (biased vertex normal) + gooch shading + texture 31, // instancing + vertex color (biased vertex normal) + matcap shading + texture + + 32, // skinning + cel shading + 32, // skinning + gooch shading + 34, // skinning + matcap shading + + 33, // skinning (biased vertex normal) + cel shading + 33, // skinning (biased vertex normal) + gooch shading + 35, // skinning (biased vertex normal) + matcap shading }; template<> const D3D12_SHADER_BYTECODE EffectBase::PixelShaderBytecode[] = { - { NPREffect_PSCelShading, sizeof(NPREffect_PSCelShading) }, - { NPREffect_PSGoochShading, sizeof(NPREffect_PSGoochShading) }, - { NPREffect_PSMatCapShading, sizeof(NPREffect_PSMatCapShading) }, - { NPREffect_PSCelShadingTx, sizeof(NPREffect_PSCelShadingTx) }, - { NPREffect_PSGoochShadingTx, sizeof(NPREffect_PSGoochShadingTx) }, - { NPREffect_PSMatCapShadingTx, sizeof(NPREffect_PSMatCapShadingTx) }, + { NPREffect_PSCelShading, sizeof(NPREffect_PSCelShading) }, + { NPREffect_PSGoochShading, sizeof(NPREffect_PSGoochShading) }, + { NPREffect_PSMatCapShading, sizeof(NPREffect_PSMatCapShading) }, + { NPREffect_PSCelShadingTx, sizeof(NPREffect_PSCelShadingTx) }, + { NPREffect_PSGoochShadingTx, sizeof(NPREffect_PSGoochShadingTx) }, + { NPREffect_PSMatCapShadingTx, sizeof(NPREffect_PSMatCapShadingTx) }, + { NPREffect_PSCelShadingSkinTx, sizeof(NPREffect_PSCelShadingSkinTx) }, // Different rootsig + { NPREffect_PSGoochShadingSkinTx, sizeof(NPREffect_PSGoochShadingSkinTx) }, + { NPREffect_PSMatCapShadingSkinTx, sizeof(NPREffect_PSMatCapShadingSkinTx) }, }; @@ -512,6 +609,14 @@ const int EffectBase::PixelShaderIndices[] = 3, // instancing + vertex color (biased vertex normal) + cel shading + texture 4, // instancing + vertex color (biased vertex normal) + gooch shading + texture 5, // instancing + vertex color (biased vertex normal) + matcap shading + texture + + 6, // skinning + cel shading + 7, // skinning + gooch shading + 8, // skinning + matcap shading + + 6, // skinning (biased vertex normal) + cel shading + 7, // skinning (biased vertex normal) + gooch shading + 8, // skinning (biased vertex normal) + matcap shading }; #pragma endregion @@ -522,34 +627,33 @@ SharedResourcePool::DeviceResources> // Constructor. NPREffect::Impl::Impl( - _In_ ID3D12Device* device, - uint32_t effectFlags, - const EffectPipelineStateDescription& pipelineDescription, - NPREffect::Mode nprMode) + _In_ ID3D12Device* device) : EffectBase(device), + weightsPerVertex(0), + matCapEnabled(false), + textureEnabled(false), texture{}, sampler{}, - matcap{} + matcap{}, + boneConstants{} { static_assert(static_cast(std::size(EffectBase::VertexShaderIndices)) == NPREffectTraits::ShaderPermutationCount, "array/max mismatch"); static_assert(static_cast(std::size(EffectBase::VertexShaderBytecode)) == NPREffectTraits::VertexShaderCount, "array/max mismatch"); static_assert(static_cast(std::size(EffectBase::PixelShaderBytecode)) == NPREffectTraits::PixelShaderCount, "array/max mismatch"); static_assert(static_cast(std::size(EffectBase::PixelShaderIndices)) == NPREffectTraits::ShaderPermutationCount, "array/max mismatch"); +} - constants.lightDirectionAndCelBands = s_defaultLightDir; - constants.diffuseColorAndAlpha = s_defaultDiffuse; - constants.specularColorAndSpecularThreshold = s_defaultSpecular; - constants.rimColorAndPower = s_defaultRim; - constants.extraSettings = s_defaultExtraSettings; - constants.goochCoolColorAndAlpha = s_defaultCool; - constants.goochWarmColorAndBeta = s_defaultWarm; - constants.eyePosition = g_XMZero; - +void NPREffect::Impl::Initialize( + _In_ ID3D12Device* device, + uint32_t effectFlags, + const EffectPipelineStateDescription& pipelineDescription, + NPREffect::Mode nprMode, + bool enableSkinning) +{ switch (nprMode) { case Mode_Cel: case Mode_Gooch: - matCapEnabled = false; break; case Mode_MatCap: @@ -562,6 +666,39 @@ NPREffect::Impl::Impl( textureEnabled = (effectFlags & EffectFlags::Texture) != 0; + constants.lightDirectionAndCelBands = s_defaultLightDir; + constants.diffuseColorAndAlpha = s_defaultDiffuse; + constants.specularColorAndSpecularThreshold = s_defaultSpecular; + constants.rimColorAndPower = s_defaultRim; + constants.extraSettings = s_defaultExtraSettings; + constants.goochCoolColorAndAlpha = s_defaultCool; + constants.goochWarmColorAndBeta = s_defaultWarm; + constants.eyePosition = g_XMZero; + + if (enableSkinning) + { + if (effectFlags & EffectFlags::VertexColor) + { + DebugTrace("ERROR: SkinnedNPREffect does not implement EffectFlags::VertexColor\n"); + throw std::invalid_argument("VertexColor effect flag is invalid"); + } + else if (effectFlags & EffectFlags::Instancing) + { + DebugTrace("ERROR: SkinnedNPREffect does not implement EffectFlags::Instancing\n"); + throw std::invalid_argument("Instancing effect flag is invalid"); + } + + weightsPerVertex = 4; + textureEnabled = true; + + for (size_t j = 0; j < SkinnedNPREffect::MaxBones; ++j) + { + boneConstants.Bones[j][0] = g_XMIdentityR0; + boneConstants.Bones[j][1] = g_XMIdentityR1; + boneConstants.Bones[j][2] = g_XMIdentityR2; + } + } + // Create root signature. { ENUM_FLAGS_CONSTEXPR D3D12_ROOT_SIGNATURE_FLAGS rootSignatureFlags = @@ -575,50 +712,90 @@ NPREffect::Impl::Impl( #endif ; - // Create root parameters and initialize first (constants) - CD3DX12_ROOT_PARAMETER rootParameters[RootParameterIndex::RootParameterCount] = {}; - rootParameters[RootParameterIndex::ConstantBuffer].InitAsConstantBufferView(0, 0, D3D12_SHADER_VISIBILITY_ALL); - - // Root parameter descriptor - conditionally initialized - CD3DX12_ROOT_SIGNATURE_DESC rsigDesc = {}; - - if (textureEnabled && matCapEnabled) + if (enableSkinning) { - // Include two textures and one sampler + // Create root parameters and initialize first (constants) + CD3DX12_ROOT_PARAMETER rootParameters[SkinRootParameterIndex::SkinRootParameterCount] = {}; + rootParameters[SkinRootParameterIndex::SkinConstantBuffer].InitAsConstantBufferView(0, 0, D3D12_SHADER_VISIBILITY_ALL); + + // Include texture and sampler const CD3DX12_DESCRIPTOR_RANGE textureSRV(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 0); const CD3DX12_DESCRIPTOR_RANGE textureSampler(D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER, 1, 0); - rootParameters[RootParameterIndex::TextureSRV].InitAsDescriptorTable(1, &textureSRV, D3D12_SHADER_VISIBILITY_PIXEL); - rootParameters[RootParameterIndex::TextureSampler].InitAsDescriptorTable(1, &textureSampler, D3D12_SHADER_VISIBILITY_PIXEL); + rootParameters[SkinRootParameterIndex::SkinTextureSRV].InitAsDescriptorTable(1, &textureSRV, D3D12_SHADER_VISIBILITY_PIXEL); + rootParameters[SkinRootParameterIndex::SkinTextureSampler].InitAsDescriptorTable(1, &textureSampler, D3D12_SHADER_VISIBILITY_PIXEL); - const CD3DX12_DESCRIPTOR_RANGE texture2Range(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 1); - rootParameters[RootParameterIndex::Texture2SRV].InitAsDescriptorTable(1, &texture2Range, D3D12_SHADER_VISIBILITY_PIXEL); + rootParameters[SkinRootParameterIndex::SkinConstantBufferBones].InitAsConstantBufferView(1, 0, D3D12_SHADER_VISIBILITY_VERTEX); - // use all parameters - rsigDesc.Init(static_cast(std::size(rootParameters)), rootParameters, 0, nullptr, rootSignatureFlags); + // Root parameter descriptor - conditionally initialized + CD3DX12_ROOT_SIGNATURE_DESC rsigDesc = {}; - mRootSignature = GetRootSignature(2, rsigDesc); - } - else if (textureEnabled || matCapEnabled) - { - // Include texture and sampler - const CD3DX12_DESCRIPTOR_RANGE textureSRV(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 0); - const CD3DX12_DESCRIPTOR_RANGE textureSampler(D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER, 1, 0); + if (matCapEnabled) + { + // Include second texture + const CD3DX12_DESCRIPTOR_RANGE texture2Range(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 1); + rootParameters[SkinRootParameterIndex::SkinTexture2SRV].InitAsDescriptorTable(1, &texture2Range, D3D12_SHADER_VISIBILITY_PIXEL); - rootParameters[RootParameterIndex::TextureSRV].InitAsDescriptorTable(1, &textureSRV, D3D12_SHADER_VISIBILITY_PIXEL); - rootParameters[RootParameterIndex::TextureSampler].InitAsDescriptorTable(1, &textureSampler, D3D12_SHADER_VISIBILITY_PIXEL); + // use all parameters + rsigDesc.Init(static_cast(std::size(rootParameters)), rootParameters, 0, nullptr, rootSignatureFlags); - // use all but last parameter - rsigDesc.Init(static_cast(std::size(rootParameters) - 1), rootParameters, 0, nullptr, rootSignatureFlags); + mRootSignature = GetRootSignature(4, rsigDesc); + } + else + { + // use all but last parameter + rsigDesc.Init(static_cast(std::size(rootParameters) - 1), rootParameters, 0, nullptr, rootSignatureFlags); - mRootSignature = GetRootSignature(1, rsigDesc); + mRootSignature = GetRootSignature(3, rsigDesc); + } } - else + else // !enableSkinning { - // only use constant - rsigDesc.Init(1, rootParameters, 0, nullptr, rootSignatureFlags); - - mRootSignature = GetRootSignature(0, rsigDesc); + // Create root parameters and initialize first (constants) + CD3DX12_ROOT_PARAMETER rootParameters[RootParameterIndex::RootParameterCount] = {}; + rootParameters[RootParameterIndex::ConstantBuffer].InitAsConstantBufferView(0, 0, D3D12_SHADER_VISIBILITY_ALL); + + // Root parameter descriptor - conditionally initialized + CD3DX12_ROOT_SIGNATURE_DESC rsigDesc = {}; + + if (textureEnabled && matCapEnabled) + { + // Include two textures and one sampler + const CD3DX12_DESCRIPTOR_RANGE textureSRV(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 0); + const CD3DX12_DESCRIPTOR_RANGE textureSampler(D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER, 1, 0); + + rootParameters[RootParameterIndex::TextureSRV].InitAsDescriptorTable(1, &textureSRV, D3D12_SHADER_VISIBILITY_PIXEL); + rootParameters[RootParameterIndex::TextureSampler].InitAsDescriptorTable(1, &textureSampler, D3D12_SHADER_VISIBILITY_PIXEL); + + const CD3DX12_DESCRIPTOR_RANGE texture2Range(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 1); + rootParameters[RootParameterIndex::Texture2SRV].InitAsDescriptorTable(1, &texture2Range, D3D12_SHADER_VISIBILITY_PIXEL); + + // use all parameters + rsigDesc.Init(static_cast(std::size(rootParameters)), rootParameters, 0, nullptr, rootSignatureFlags); + + mRootSignature = GetRootSignature(2, rsigDesc); + } + else if (textureEnabled || matCapEnabled) + { + // Include texture and sampler + const CD3DX12_DESCRIPTOR_RANGE textureSRV(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 0); + const CD3DX12_DESCRIPTOR_RANGE textureSampler(D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER, 1, 0); + + rootParameters[RootParameterIndex::TextureSRV].InitAsDescriptorTable(1, &textureSRV, D3D12_SHADER_VISIBILITY_PIXEL); + rootParameters[RootParameterIndex::TextureSampler].InitAsDescriptorTable(1, &textureSampler, D3D12_SHADER_VISIBILITY_PIXEL); + + // use all but last parameter + rsigDesc.Init(static_cast(std::size(rootParameters) - 1), rootParameters, 0, nullptr, rootSignatureFlags); + + mRootSignature = GetRootSignature(1, rsigDesc); + } + else + { + // only use constant + rsigDesc.Init(1, rootParameters, 0, nullptr, rootSignatureFlags); + + mRootSignature = GetRootSignature(0, rsigDesc); + } } } @@ -643,7 +820,14 @@ NPREffect::Impl::Impl( EffectBase::PixelShaderBytecode[pi], mPipelineState.GetAddressOf()); - SetDebugObjectName(mPipelineState.Get(), L"NPREffect"); + if (enableSkinning) + { + SetDebugObjectName(mPipelineState.Get(), L"SkinnedNPREffect"); + } + else + { + SetDebugObjectName(mPipelineState.Get(), L"NPREffect"); + } } @@ -651,6 +835,20 @@ int NPREffect::Impl::GetPipelineStatePermutation(NPREffect::Mode nprMode, uint32 { int permutation = static_cast(nprMode); + if (weightsPerVertex > 0) + { + if (effectFlags & EffectFlags::BiasedVertexNormals) + { + // Compressed normals need to be scaled and biased in the vertex shader. + permutation += 3; + } + + // Vertex skinning does not support vertex colors or instancing, always includes a texture. + permutation += 48; + + return permutation; + } + // Support vertex coloring? if (effectFlags & EffectFlags::VertexColor) { @@ -682,6 +880,8 @@ int NPREffect::Impl::GetPipelineStatePermutation(NPREffect::Mode nprMode, uint32 // Sets our state onto the D3D device. void NPREffect::Impl::Apply(_In_ ID3D12GraphicsCommandList* commandList) { + assert(weightsPerVertex == 0); + // Compute derived parameter values. matrices.SetConstants( dirtyFlags, @@ -695,7 +895,7 @@ void NPREffect::Impl::Apply(_In_ ID3D12GraphicsCommandList* commandList) // Set the root signature commandList->SetGraphicsRootSignature(mRootSignature); - // Set the texture + // Set the texture(s) if (textureEnabled) { if (!texture.ptr || !sampler.ptr) @@ -709,9 +909,21 @@ void NPREffect::Impl::Apply(_In_ ID3D12GraphicsCommandList* commandList) // with the required descriptor heaps. commandList->SetGraphicsRootDescriptorTable(RootParameterIndex::TextureSRV, texture); commandList->SetGraphicsRootDescriptorTable(RootParameterIndex::TextureSampler, sampler); - } - if (matCapEnabled) + if (matCapEnabled) + { + if (!matcap.ptr) + { + DebugTrace("ERROR: Missing matcap texture or sampler for NPREffect\n"); + throw std::runtime_error("NPREffect"); + } + + // **NOTE** If D3D asserts or crashes here, you probably need to call commandList->SetDescriptorHeaps() + // with the required descriptor heaps. + commandList->SetGraphicsRootDescriptorTable(RootParameterIndex::Texture2SRV, matcap); + } + } + else if (matCapEnabled) { if (!matcap.ptr || !sampler.ptr) { @@ -722,9 +934,7 @@ void NPREffect::Impl::Apply(_In_ ID3D12GraphicsCommandList* commandList) // **NOTE** If D3D asserts or crashes here, you probably need to call commandList->SetDescriptorHeaps() // with the required descriptor heaps. - commandList->SetGraphicsRootDescriptorTable( - textureEnabled ? RootParameterIndex::Texture2SRV : RootParameterIndex::TextureSRV, - matcap); + commandList->SetGraphicsRootDescriptorTable(RootParameterIndex::TextureSRV, matcap); commandList->SetGraphicsRootDescriptorTable(RootParameterIndex::TextureSampler, sampler); } @@ -736,14 +946,82 @@ void NPREffect::Impl::Apply(_In_ ID3D12GraphicsCommandList* commandList) } +void NPREffect::Impl::ApplySkinning(_In_ ID3D12GraphicsCommandList* commandList) +{ + assert(weightsPerVertex > 0); + assert(textureEnabled); + + // Compute derived parameter values. + matrices.SetConstants( + dirtyFlags, + constants.world, + constants.worldInverseTranspose, + constants.worldViewProj, + constants.eyePosition); + + UpdateConstants(); + + if (dirtyFlags & EffectDirtyFlags::ConstantBufferBones) + { + mBones = GraphicsMemory::Get(GetDevice()).AllocateConstant(boneConstants); + dirtyFlags &= ~EffectDirtyFlags::ConstantBufferBones; + } + + // Set the root signature + commandList->SetGraphicsRootSignature(mRootSignature); + + // Set the texture(s) + if (!texture.ptr || !sampler.ptr) + { + DebugTrace("ERROR: Missing texture or sampler for SkinnedNPREffect (texture %llu, sampler %llu)\n", + texture.ptr, sampler.ptr); + throw std::runtime_error("SkinnedNPREffect"); + } + + // **NOTE** If D3D asserts or crashes here, you probably need to call commandList->SetDescriptorHeaps() + // with the required descriptor heaps. + commandList->SetGraphicsRootDescriptorTable(SkinRootParameterIndex::SkinTextureSRV, texture); + commandList->SetGraphicsRootDescriptorTable(SkinRootParameterIndex::SkinTextureSampler, sampler); + + if (matCapEnabled) + { + if (!matcap.ptr) + { + DebugTrace("ERROR: Missing matcap texture for SkinnedNPREffect\n"); + throw std::runtime_error("SkinnedNPREffect"); + } + + // **NOTE** If D3D asserts or crashes here, you probably need to call commandList->SetDescriptorHeaps() + // with the required descriptor heaps. + commandList->SetGraphicsRootDescriptorTable(SkinRootParameterIndex::SkinTexture2SRV, matcap); + } + + // Set bone constants + commandList->SetGraphicsRootConstantBufferView(SkinRootParameterIndex::SkinConstantBufferBones, mBones.GpuAddress()); + + // Set constants + commandList->SetGraphicsRootConstantBufferView(SkinRootParameterIndex::SkinConstantBuffer, GetConstantBufferGpuAddress()); + + // Set the pipeline state + commandList->SetPipelineState(EffectBase::mPipelineState.Get()); +} + + +//-------------------------------------------------------------------------------------- +// NPREffect +//-------------------------------------------------------------------------------------- + // Public constructor. NPREffect::NPREffect( _In_ ID3D12Device* device, uint32_t effectFlags, const EffectPipelineStateDescription& pipelineDescription, - Mode nprMode) - : pImpl(std::make_unique(device, effectFlags, pipelineDescription, nprMode)) -{} + Mode nprMode, + bool skinningEnabled) + : pImpl(std::make_unique(device)) +{ + pImpl->Initialize(device, effectFlags, pipelineDescription, nprMode, skinningEnabled); +} NPREffect::NPREffect(NPREffect&&) noexcept = default; @@ -1017,3 +1295,59 @@ void NPREffect::DisableRimLighting() pImpl->dirtyFlags |= EffectDirtyFlags::ConstantBuffer; } + + +//-------------------------------------------------------------------------------------- +// SkinnedNPREffect +//-------------------------------------------------------------------------------------- + +SkinnedNPREffect::~SkinnedNPREffect() +{} + + +// IEffect methods. +void SkinnedNPREffect::Apply(_In_ ID3D12GraphicsCommandList* commandList) +{ + pImpl->ApplySkinning(commandList); +} + + +// Animation settings. +void SkinnedNPREffect::SetBoneTransforms(_In_reads_(count) XMMATRIX const* value, size_t count) +{ + if (count > MaxBones) + throw std::invalid_argument("count parameter exceeds MaxBones"); + + auto boneConstant = pImpl->boneConstants.Bones; + + for (size_t i = 0; i < count; i++) + { + #if DIRECTX_MATH_VERSION >= 313 + XMStoreFloat3x4A(reinterpret_cast(&boneConstant[i]), value[i]); + #else + // Xbox One XDK has an older version of DirectXMath + XMMATRIX boneMatrix = XMMatrixTranspose(value[i]); + + boneConstant[i][0] = boneMatrix.r[0]; + boneConstant[i][1] = boneMatrix.r[1]; + boneConstant[i][2] = boneMatrix.r[2]; + #endif + } + + pImpl->dirtyFlags |= EffectDirtyFlags::ConstantBufferBones; +} + + +void SkinnedNPREffect::ResetBoneTransforms() +{ + auto boneConstant = pImpl->boneConstants.Bones; + + for (size_t i = 0; i < MaxBones; ++i) + { + boneConstant[i][0] = g_XMIdentityR0; + boneConstant[i][1] = g_XMIdentityR1; + boneConstant[i][2] = g_XMIdentityR2; + } + + pImpl->dirtyFlags |= EffectDirtyFlags::ConstantBufferBones; +} diff --git a/Src/Shaders/CompileShaders.cmd b/Src/Shaders/CompileShaders.cmd index 3a817f0b..20fa6081 100644 --- a/Src/Shaders/CompileShaders.cmd +++ b/Src/Shaders/CompileShaders.cmd @@ -278,6 +278,9 @@ call :CompileShader%1 NPREffect vs VSNPREffectBnInstTx call :CompileShader%1 NPREffect vs VSNPREffectVcInstTx call :CompileShader%1 NPREffect vs VSNPREffectVcBnInstTx +call :CompileShader%1 NPREffect vs VSSkinnedNPREffectTx +call :CompileShader%1 NPREffect vs VSSkinnedNPREffectTxBn + call :CompileShader%1 NPREffect vs VSNPREffectMC call :CompileShader%1 NPREffect vs VSNPREffectBnMC call :CompileShader%1 NPREffect vs VSNPREffectVcMC @@ -298,14 +301,20 @@ call :CompileShader%1 NPREffect vs VSNPREffectBnInstTxMC call :CompileShader%1 NPREffect vs VSNPREffectVcInstTxMC call :CompileShader%1 NPREffect vs VSNPREffectVcBnInstTxMC +call :CompileShader%1 NPREffect vs VSSkinnedNPREffectTxMC +call :CompileShader%1 NPREffect vs VSSkinnedNPREffectTxBnMC + call :CompileShader%1 NPREffect ps PSCelShading call :CompileShader%1 NPREffect ps PSCelShadingTx +call :CompileShader%1 NPREffect ps PSCelShadingSkinTx call :CompileShader%1 NPREffect ps PSGoochShading call :CompileShader%1 NPREffect ps PSGoochShadingTx +call :CompileShader%1 NPREffect ps PSGoochShadingSkinTx call :CompileShader%1 NPREffect ps PSMatCapShading call :CompileShader%1 NPREffect ps PSMatCapShadingTx +call :CompileShader%1 NPREffect ps PSMatCapShadingSkinTx call :CompileShader%1 SpriteEffect vs SpriteVertexShader call :CompileShader%1 SpriteEffect ps SpritePixelShader diff --git a/Src/Shaders/NPREffect.fx b/Src/Shaders/NPREffect.fx index b33d495c..561a36c5 100644 --- a/Src/Shaders/NPREffect.fx +++ b/Src/Shaders/NPREffect.fx @@ -41,11 +41,18 @@ cbuffer Parameters : register(b0) float4x4 WorldViewProj : packoffset(c15); }; +cbuffer SkinningParameters : register(b1) +{ + float4x3 Bones[72]; +} + #include "RootSig.fxh" #include "Structures.fxh" +#include "Skinning.fxh" #include "Utilities.fxh" + VSOutputPixelLighting ComputeCommonVS(float4 position, float3 normal) { VSOutputPixelLighting vout; @@ -343,6 +350,42 @@ VSOutputPixelLightingTx VSNPREffectVcBnInstTxMC(VSInputNmTxVcInst vin) } +// Vertex shader: skinning (four bones) + texture. +[RootSignature(SkinTextureSamplerRS)] +VSOutputPixelLightingTx VSSkinnedNPREffectTx(VSInputNmTxWeights vin) +{ + float3 normal = Skin(vin, vin.Normal, 4); + + VSOutputPixelLightingTx vout = ComputeCommonVSTx(vin.Position, normal, vin.TexCoord); + vout.Diffuse = float4(DiffuseColor, Alpha); + return vout; +} + +[RootSignature(SkinDualTextureOneSamplerRS)] +VSOutputPixelLightingTx VSSkinnedNPREffectTxMC(VSInputNmTxWeights vin) +{ + return VSSkinnedNPREffectTx(vin); +} + +[RootSignature(SkinTextureSamplerRS)] +VSOutputPixelLightingTx VSSkinnedNPREffectTxBn(VSInputNmTxWeights vin) +{ + float3 normal = BiasX2(vin.Normal); + + normal = Skin(vin, normal, 4); + + VSOutputPixelLightingTx vout = ComputeCommonVSTx(vin.Position, normal, vin.TexCoord); + vout.Diffuse = float4(DiffuseColor, Alpha); + return vout; +} + +[RootSignature(SkinDualTextureOneSamplerRS)] +VSOutputPixelLightingTx VSSkinnedNPREffectTxBnMC(VSInputNmTxWeights vin) +{ + return VSSkinnedNPREffectTxBn(vin); +} + + //--- Cel shading --- float Quantize(float intensity, float bands) { @@ -427,6 +470,11 @@ float4 PSCelShadingTx(PSInputPixelLightingTx pin) : SV_Target0 return float4(color, pin.Diffuse.a * texColor.a); } +[RootSignature(SkinTextureSamplerRS)] +float4 PSCelShadingSkinTx(PSInputPixelLightingTx pin) : SV_Target0 +{ + return PSCelShadingTx(pin); +} //--- Gooch shading --- float3 GoochShading(float dot1, float3 color) @@ -500,6 +548,12 @@ float4 PSGoochShadingTx(PSInputPixelLightingTx pin) : SV_Target0 return float4(color, pin.Diffuse.a * texColor.a); } +[RootSignature(SkinTextureSamplerRS)] +float4 PSGoochShadingSkinTx(PSInputPixelLightingTx pin) : SV_Target0 +{ + return PSGoochShadingTx(pin); +} + //--- MatCap shading --- @@ -542,3 +596,9 @@ float4 PSMatCapShadingTx(PSInputPixelLightingTx pin) : SV_Target0 return float4(color, pin.Diffuse.a * texColor.a); } + +[RootSignature(SkinDualTextureOneSamplerRS)] +float4 PSMatCapShadingSkinTx(PSInputPixelLightingTx pin) : SV_Target0 +{ + return PSMatCapShadingTx(pin); +} diff --git a/Src/Shaders/RootSig.fxh b/Src/Shaders/RootSig.fxh index 416d2509..2b097331 100644 --- a/Src/Shaders/RootSig.fxh +++ b/Src/Shaders/RootSig.fxh @@ -214,6 +214,31 @@ "DescriptorTable ( Sampler(s0), visibility = SHADER_VISIBILITY_PIXEL ),"\ "DescriptorTable ( SRV(t1), visibility = SHADER_VISIBILITY_PIXEL )" +#define SkinTextureSamplerRS \ +"RootFlags ( ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT |" \ +" DENY_AMPLIFICATION_SHADER_ROOT_ACCESS |" \ +" DENY_DOMAIN_SHADER_ROOT_ACCESS |" \ +" DENY_GEOMETRY_SHADER_ROOT_ACCESS |" \ +" DENY_HULL_SHADER_ROOT_ACCESS |" \ +" DENY_MESH_SHADER_ROOT_ACCESS )," \ +"CBV(b0),"\ +"DescriptorTable ( SRV(t0), visibility = SHADER_VISIBILITY_PIXEL ),"\ +"DescriptorTable ( Sampler(s0), visibility = SHADER_VISIBILITY_PIXEL ),"\ +"CBV(b1, visibility = SHADER_VISIBILITY_VERTEX )" + +#define SkinDualTextureOneSamplerRS \ +"RootFlags ( ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT |" \ +" DENY_AMPLIFICATION_SHADER_ROOT_ACCESS |" \ +" DENY_DOMAIN_SHADER_ROOT_ACCESS |" \ +" DENY_GEOMETRY_SHADER_ROOT_ACCESS |" \ +" DENY_HULL_SHADER_ROOT_ACCESS |" \ +" DENY_MESH_SHADER_ROOT_ACCESS )," \ +"CBV(b0),"\ +"DescriptorTable ( SRV(t0), visibility = SHADER_VISIBILITY_PIXEL ),"\ +"DescriptorTable ( Sampler(s0), visibility = SHADER_VISIBILITY_PIXEL ),"\ +"CBV(b1, visibility = SHADER_VISIBILITY_VERTEX ),"\ +"DescriptorTable ( SRV(t1), visibility = SHADER_VISIBILITY_PIXEL )" + #else // !__XBOX_SCARLETT #define NoTextureRS \ @@ -393,4 +418,25 @@ "DescriptorTable ( Sampler(s0), visibility = SHADER_VISIBILITY_PIXEL ),"\ "DescriptorTable ( SRV(t1), visibility = SHADER_VISIBILITY_PIXEL )" +#define SkinTextureSamplerRS \ +"RootFlags ( ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT |" \ +" DENY_DOMAIN_SHADER_ROOT_ACCESS |" \ +" DENY_GEOMETRY_SHADER_ROOT_ACCESS |" \ +" DENY_HULL_SHADER_ROOT_ACCESS )," \ +"CBV(b0),"\ +"DescriptorTable ( SRV(t0), visibility = SHADER_VISIBILITY_PIXEL ),"\ +"DescriptorTable ( Sampler(s0), visibility = SHADER_VISIBILITY_PIXEL ),"\ +"CBV(b1, visibility = SHADER_VISIBILITY_VERTEX )" + +#define SkinDualTextureOneSamplerRS \ +"RootFlags ( ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT |" \ +" DENY_DOMAIN_SHADER_ROOT_ACCESS |" \ +" DENY_GEOMETRY_SHADER_ROOT_ACCESS |" \ +" DENY_HULL_SHADER_ROOT_ACCESS )," \ +"CBV(b0),"\ +"DescriptorTable ( SRV(t0), visibility = SHADER_VISIBILITY_PIXEL ),"\ +"DescriptorTable ( Sampler(s0), visibility = SHADER_VISIBILITY_PIXEL ),"\ +"CBV(b1, visibility = SHADER_VISIBILITY_VERTEX ),"\ +"DescriptorTable ( SRV(t1), visibility = SHADER_VISIBILITY_PIXEL )" + #endif From 53ac6f1386e5a7103d3fca54c007302bb5b6ca64 Mon Sep 17 00:00:00 2001 From: Chuck Walbourn Date: Mon, 20 Jul 2026 12:11:14 -0700 Subject: [PATCH 2/7] Fix build break --- Inc/Effects.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Inc/Effects.h b/Inc/Effects.h index 96c8a031..6198921f 100644 --- a/Inc/Effects.h +++ b/Inc/Effects.h @@ -593,8 +593,8 @@ namespace DirectX ~SkinnedNormalMapEffect() override; // Animation settings. - void __cdecl SetBoneTransforms(_In_reads_(count) XMMATRIX const* value, size_t count) override; - void __cdecl ResetBoneTransforms() override; + DIRECTX_TOOLKIT_API void __cdecl SetBoneTransforms(_In_reads_(count) XMMATRIX const* value, size_t count) override; + DIRECTX_TOOLKIT_API void __cdecl ResetBoneTransforms() override; }; @@ -699,8 +699,8 @@ namespace DirectX ~SkinnedPBREffect() override; // Animation settings. - void __cdecl SetBoneTransforms(_In_reads_(count) XMMATRIX const* value, size_t count) override; - void __cdecl ResetBoneTransforms() override; + DIRECTX_TOOLKIT_API void __cdecl SetBoneTransforms(_In_reads_(count) XMMATRIX const* value, size_t count) override; + DIRECTX_TOOLKIT_API void __cdecl ResetBoneTransforms() override; }; @@ -868,8 +868,8 @@ namespace DirectX DIRECTX_TOOLKIT_API void __cdecl Apply(_In_ ID3D12GraphicsCommandList* commandList) override; // Animation settings. - void __cdecl SetBoneTransforms(_In_reads_(count) XMMATRIX const* value, size_t count) override; - void __cdecl ResetBoneTransforms() override; + DIRECTX_TOOLKIT_API void __cdecl SetBoneTransforms(_In_reads_(count) XMMATRIX const* value, size_t count) override; + DIRECTX_TOOLKIT_API void __cdecl ResetBoneTransforms() override; }; From 7e461f28ef24d233c07ab79e377f3b46d95b5385 Mon Sep 17 00:00:00 2001 From: Chuck Walbourn Date: Mon, 20 Jul 2026 12:46:46 -0700 Subject: [PATCH 3/7] Unbreak --- Inc/Effects.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Inc/Effects.h b/Inc/Effects.h index 6198921f..e62082c4 100644 --- a/Inc/Effects.h +++ b/Inc/Effects.h @@ -593,8 +593,8 @@ namespace DirectX ~SkinnedNormalMapEffect() override; // Animation settings. - DIRECTX_TOOLKIT_API void __cdecl SetBoneTransforms(_In_reads_(count) XMMATRIX const* value, size_t count) override; - DIRECTX_TOOLKIT_API void __cdecl ResetBoneTransforms() override; + void __cdecl SetBoneTransforms(_In_reads_(count) XMMATRIX const* value, size_t count) override; + void __cdecl ResetBoneTransforms() override; }; @@ -699,8 +699,8 @@ namespace DirectX ~SkinnedPBREffect() override; // Animation settings. - DIRECTX_TOOLKIT_API void __cdecl SetBoneTransforms(_In_reads_(count) XMMATRIX const* value, size_t count) override; - DIRECTX_TOOLKIT_API void __cdecl ResetBoneTransforms() override; + void __cdecl SetBoneTransforms(_In_reads_(count) XMMATRIX const* value, size_t count) override; + void __cdecl ResetBoneTransforms() override; }; @@ -865,11 +865,11 @@ namespace DirectX ~SkinnedNPREffect() override; // IEffect methods. - DIRECTX_TOOLKIT_API void __cdecl Apply(_In_ ID3D12GraphicsCommandList* commandList) override; + void __cdecl Apply(_In_ ID3D12GraphicsCommandList* commandList) override; // Animation settings. - DIRECTX_TOOLKIT_API void __cdecl SetBoneTransforms(_In_reads_(count) XMMATRIX const* value, size_t count) override; - DIRECTX_TOOLKIT_API void __cdecl ResetBoneTransforms() override; + void __cdecl SetBoneTransforms(_In_reads_(count) XMMATRIX const* value, size_t count) override; + void __cdecl ResetBoneTransforms() override; }; From b4bef183339ab7425c3b3d93fd2234ca3ff7d0e4 Mon Sep 17 00:00:00 2001 From: Chuck Walbourn Date: Mon, 20 Jul 2026 13:14:27 -0700 Subject: [PATCH 4/7] Fix warning in MinGW --- Inc/Effects.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Inc/Effects.h b/Inc/Effects.h index e62082c4..6449a845 100644 --- a/Inc/Effects.h +++ b/Inc/Effects.h @@ -764,7 +764,7 @@ namespace DirectX Mode_MatCap, // Material Capture shading }; - DIRECTX_TOOLKIT_API NPREffect( + NPREffect( _In_ ID3D12Device* device, uint32_t effectFlags, const EffectPipelineStateDescription& pipelineDescription, From ce3349f895eb41956950e14bcb2c095efca92eba Mon Sep 17 00:00:00 2001 From: Chuck Walbourn Date: Mon, 20 Jul 2026 13:17:49 -0700 Subject: [PATCH 5/7] Fix for MinGW 16.1 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2d783008..711a86e9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -346,7 +346,7 @@ if(WIN32 AND BUILD_SHARED_LIBS) elseif(XBOX_CONSOLE_TARGET MATCHES "durango") target_link_libraries(${PROJECT_NAME} PRIVATE kernelx.lib combase.lib d3d12_x.lib xi.lib) else() - target_link_libraries(${PROJECT_NAME} PRIVATE d3d12.lib) + target_link_libraries(${PROJECT_NAME} PRIVATE d3d12.lib dxguid.lib) endif() if(MINGW AND BUILD_XINPUT) From c69a1aa14f3a2016e5beac91b9d918c6aab6dda8 Mon Sep 17 00:00:00 2001 From: Chuck Walbourn Date: Mon, 20 Jul 2026 13:56:26 -0700 Subject: [PATCH 6/7] More DLL fixe --- Inc/Effects.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Inc/Effects.h b/Inc/Effects.h index 6449a845..84d4a2f7 100644 --- a/Inc/Effects.h +++ b/Inc/Effects.h @@ -764,7 +764,7 @@ namespace DirectX Mode_MatCap, // Material Capture shading }; - NPREffect( + DIRECTX_TOOLKIT_API inline NPREffect( _In_ ID3D12Device* device, uint32_t effectFlags, const EffectPipelineStateDescription& pipelineDescription, From d7d5087de85ab075be0d392b3e2caf543c4be157 Mon Sep 17 00:00:00 2001 From: Chuck Walbourn Date: Wed, 22 Jul 2026 10:50:38 -0700 Subject: [PATCH 7/7] Add NPREffectFactory --- CMakeLists.txt | 1 + DirectXTK_Desktop_2022_Win10.vcxproj | 1 + DirectXTK_Desktop_2022_Win10.vcxproj.filters | 3 + DirectXTK_Desktop_2026.slnx | 2 +- DirectXTK_Desktop_2026.vcxproj | 1 + DirectXTK_Desktop_2026.vcxproj.filters | 3 + DirectXTK_GDKW_2022.vcxproj | 1 + DirectXTK_GDKW_2022.vcxproj.filters | 3 + DirectXTK_GDKX_2022.vcxproj | 1 + DirectXTK_GDKX_2022.vcxproj.filters | 3 + DirectXTK_GDKX_2026.vcxproj | 1 + DirectXTK_GDKX_2026.vcxproj.filters | 3 + DirectXTK_GDK_2022.vcxproj | 1 + DirectXTK_GDK_2022.vcxproj.filters | 3 + DirectXTK_Windows10_2022.vcxproj | 1 + DirectXTK_Windows10_2022.vcxproj.filters | 3 + Inc/Effects.h | 56 +++ Src/EffectFactory.cpp | 7 +- Src/NPREffectFactory.cpp | 501 +++++++++++++++++++ Src/PBREffect.cpp | 4 +- Src/PBREffectFactory.cpp | 7 +- Src/Shaders/RootSig.fxh | 32 +- 22 files changed, 617 insertions(+), 21 deletions(-) create mode 100644 Src/NPREffectFactory.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 711a86e9..8ede1777 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -152,6 +152,7 @@ set(LIBRARY_SOURCES Src/ModelLoadVBO.cpp Src/NormalMapEffect.cpp Src/NPREffect.cpp + Src/NPREffectFactory.cpp Src/PBREffect.cpp Src/PBREffectFactory.cpp Src/pch.h diff --git a/DirectXTK_Desktop_2022_Win10.vcxproj b/DirectXTK_Desktop_2022_Win10.vcxproj index 4965e855..9bfaf1ee 100644 --- a/DirectXTK_Desktop_2022_Win10.vcxproj +++ b/DirectXTK_Desktop_2022_Win10.vcxproj @@ -112,6 +112,7 @@ + diff --git a/DirectXTK_Desktop_2022_Win10.vcxproj.filters b/DirectXTK_Desktop_2022_Win10.vcxproj.filters index 5116af65..706e9879 100644 --- a/DirectXTK_Desktop_2022_Win10.vcxproj.filters +++ b/DirectXTK_Desktop_2022_Win10.vcxproj.filters @@ -314,6 +314,9 @@ Src + + Src + diff --git a/DirectXTK_Desktop_2026.slnx b/DirectXTK_Desktop_2026.slnx index 2bd6738b..100c32d9 100644 --- a/DirectXTK_Desktop_2026.slnx +++ b/DirectXTK_Desktop_2026.slnx @@ -7,5 +7,5 @@ - + diff --git a/DirectXTK_Desktop_2026.vcxproj b/DirectXTK_Desktop_2026.vcxproj index 5704fd65..f364348a 100644 --- a/DirectXTK_Desktop_2026.vcxproj +++ b/DirectXTK_Desktop_2026.vcxproj @@ -112,6 +112,7 @@ + diff --git a/DirectXTK_Desktop_2026.vcxproj.filters b/DirectXTK_Desktop_2026.vcxproj.filters index 5116af65..706e9879 100644 --- a/DirectXTK_Desktop_2026.vcxproj.filters +++ b/DirectXTK_Desktop_2026.vcxproj.filters @@ -314,6 +314,9 @@ Src + + Src + diff --git a/DirectXTK_GDKW_2022.vcxproj b/DirectXTK_GDKW_2022.vcxproj index d83bf563..84e5c989 100644 --- a/DirectXTK_GDKW_2022.vcxproj +++ b/DirectXTK_GDKW_2022.vcxproj @@ -439,6 +439,7 @@ + diff --git a/DirectXTK_GDKW_2022.vcxproj.filters b/DirectXTK_GDKW_2022.vcxproj.filters index 8a878fbe..898d5ab1 100644 --- a/DirectXTK_GDKW_2022.vcxproj.filters +++ b/DirectXTK_GDKW_2022.vcxproj.filters @@ -308,6 +308,9 @@ Src + + Src + diff --git a/DirectXTK_GDKX_2022.vcxproj b/DirectXTK_GDKX_2022.vcxproj index 60aec220..0427428c 100644 --- a/DirectXTK_GDKX_2022.vcxproj +++ b/DirectXTK_GDKX_2022.vcxproj @@ -613,6 +613,7 @@ + diff --git a/DirectXTK_GDKX_2022.vcxproj.filters b/DirectXTK_GDKX_2022.vcxproj.filters index efb30072..5c4adab8 100644 --- a/DirectXTK_GDKX_2022.vcxproj.filters +++ b/DirectXTK_GDKX_2022.vcxproj.filters @@ -317,6 +317,9 @@ Src + + Src + diff --git a/DirectXTK_GDKX_2026.vcxproj b/DirectXTK_GDKX_2026.vcxproj index 9f85832f..d2b9141e 100644 --- a/DirectXTK_GDKX_2026.vcxproj +++ b/DirectXTK_GDKX_2026.vcxproj @@ -613,6 +613,7 @@ + diff --git a/DirectXTK_GDKX_2026.vcxproj.filters b/DirectXTK_GDKX_2026.vcxproj.filters index efb30072..5c4adab8 100644 --- a/DirectXTK_GDKX_2026.vcxproj.filters +++ b/DirectXTK_GDKX_2026.vcxproj.filters @@ -317,6 +317,9 @@ Src + + Src + diff --git a/DirectXTK_GDK_2022.vcxproj b/DirectXTK_GDK_2022.vcxproj index fdbd09bf..108e995e 100644 --- a/DirectXTK_GDK_2022.vcxproj +++ b/DirectXTK_GDK_2022.vcxproj @@ -598,6 +598,7 @@ + diff --git a/DirectXTK_GDK_2022.vcxproj.filters b/DirectXTK_GDK_2022.vcxproj.filters index efb30072..5c4adab8 100644 --- a/DirectXTK_GDK_2022.vcxproj.filters +++ b/DirectXTK_GDK_2022.vcxproj.filters @@ -317,6 +317,9 @@ Src + + Src + diff --git a/DirectXTK_Windows10_2022.vcxproj b/DirectXTK_Windows10_2022.vcxproj index 6a19fdd2..25a9c240 100644 --- a/DirectXTK_Windows10_2022.vcxproj +++ b/DirectXTK_Windows10_2022.vcxproj @@ -125,6 +125,7 @@ + diff --git a/DirectXTK_Windows10_2022.vcxproj.filters b/DirectXTK_Windows10_2022.vcxproj.filters index 9f01fe06..c3f8e0c0 100644 --- a/DirectXTK_Windows10_2022.vcxproj.filters +++ b/DirectXTK_Windows10_2022.vcxproj.filters @@ -386,5 +386,8 @@ Src + + Src + \ No newline at end of file diff --git a/Inc/Effects.h b/Inc/Effects.h index 84d4a2f7..c769abee 100644 --- a/Inc/Effects.h +++ b/Inc/Effects.h @@ -1055,6 +1055,9 @@ namespace DirectX DIRECTX_TOOLKIT_API void __cdecl EnableInstancing(bool enabled) noexcept; + // Properties. + DIRECTX_TOOLKIT_API ID3D12Device* GetDevice() const noexcept; + private: // Private implementation. class Impl; @@ -1096,6 +1099,59 @@ namespace DirectX DIRECTX_TOOLKIT_API void __cdecl EnableInstancing(bool enabled) noexcept; + // Properties. + DIRECTX_TOOLKIT_API ID3D12Device* GetDevice() const noexcept; + + private: + // Private implementation. + class Impl; + + std::shared_ptr pImpl; + }; + + + // Factory for Non-Photorealistic Rendering (NPR) + class NPREffectFactory : public IEffectFactory + { + public: + DIRECTX_TOOLKIT_API NPREffectFactory(_In_ ID3D12Device* device); + DIRECTX_TOOLKIT_API NPREffectFactory( + _In_ ID3D12DescriptorHeap* textureDescriptors, + _In_ ID3D12DescriptorHeap* samplerDescriptors); + + DIRECTX_TOOLKIT_API NPREffectFactory(NPREffectFactory&&) noexcept; + DIRECTX_TOOLKIT_API NPREffectFactory& operator= (NPREffectFactory&&) noexcept; + + NPREffectFactory(NPREffectFactory const&) = delete; + NPREffectFactory& operator= (NPREffectFactory const&) = delete; + + DIRECTX_TOOLKIT_API ~NPREffectFactory() override; + + // IEffectFactory methods. + DIRECTX_TOOLKIT_API virtual std::shared_ptr __cdecl CreateEffect( + const EffectInfo& info, + const EffectPipelineStateDescription& opaquePipelineState, + const EffectPipelineStateDescription& alphaPipelineState, + const D3D12_INPUT_LAYOUT_DESC& inputLayout, + int textureDescriptorOffset = 0, + int samplerDescriptorOffset = 0) override; + + // Settings. + DIRECTX_TOOLKIT_API void __cdecl ReleaseCache(); + + DIRECTX_TOOLKIT_API void __cdecl SetSharing(bool enabled) noexcept; + + DIRECTX_TOOLKIT_API void __cdecl EnableInstancing(bool enabled) noexcept; + + DIRECTX_TOOLKIT_API void __cdecl SetMode(NPREffect::Mode mode) noexcept; + + DIRECTX_TOOLKIT_API void __cdecl SetDefaultMatCap(int textureIndex, int samplerIndex) noexcept; + + DIRECTX_TOOLKIT_API void __cdecl SetEmissiveAsMatCap(bool value) noexcept; + + // Properties. + DIRECTX_TOOLKIT_API ID3D12Device* GetDevice() const noexcept; + private: // Private implementation. class Impl; diff --git a/Src/EffectFactory.cpp b/Src/EffectFactory.cpp index 35f604c7..058e7a28 100644 --- a/Src/EffectFactory.cpp +++ b/Src/EffectFactory.cpp @@ -104,9 +104,9 @@ class EffectFactory::Impl bool mEnableFog; bool mEnableInstancing; -private: ComPtr mDevice; +private: using EffectCache = std::map< std::wstring, std::shared_ptr >; EffectCache mEffectCache; @@ -595,3 +595,8 @@ void EffectFactory::EnableNormalMapEffect(bool enabled) noexcept { pImpl->mUseNormalMapEffect = enabled; } + +ID3D12Device* EffectFactory::GetDevice() const noexcept +{ + return pImpl->mDevice.Get(); +} diff --git a/Src/NPREffectFactory.cpp b/Src/NPREffectFactory.cpp new file mode 100644 index 00000000..0311c1f0 --- /dev/null +++ b/Src/NPREffectFactory.cpp @@ -0,0 +1,501 @@ +//-------------------------------------------------------------------------------------- +// File: NPREffectFactory.cpp +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// https://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#include "pch.h" +#include "Effects.h" +#include "CommonStates.h" +#include "DirectXHelpers.h" +#include "PlatformHelpers.h" +#include "DescriptorHeap.h" + +#include + + +using namespace DirectX; +using Microsoft::WRL::ComPtr; + +namespace +{ + template + void SetMaterialProperties(_In_ T* effect, const EffectFactory::EffectInfo& info) + { + effect->EnableDefaultLighting(); + + effect->SetAlpha(info.alphaValue); + + // Most DirectX Tool Kit effects do not have an ambient material color. + + XMVECTOR color = XMLoadFloat3(&info.diffuseColor); + effect->SetDiffuseColor(color); + + if (info.specularColor.x != 0 || info.specularColor.y != 0 || info.specularColor.z != 0) + { + color = XMLoadFloat3(&info.specularColor); + effect->SetSpecularColor(color); + } + else + { + effect->DisableSpecular(); + } + } +} + +// Internal NPREffectFactory implementation class. Only one of these helpers is allocated +// per D3D device, even if there are multiple public facing NPREffectFactory instances. +class NPREffectFactory::Impl +{ +public: + Impl(_In_ ID3D12Device* device, _In_ ID3D12DescriptorHeap* textureDescriptors, _In_ ID3D12DescriptorHeap* samplerDescriptors) noexcept(false) + : mTextureDescriptors(nullptr) + , mSamplerDescriptors(nullptr) + , mMode(NPREffect::Mode_Cel) + , mSharing(true) + , mEnableInstancing(false) + , mUseEmissiveForMatCap(true) + , mMatcapTextureIndex(-1) + , mMatcapSamplerIndex(-1) + , mDevice(device) + { + if (!device) + throw std::invalid_argument("Direct3D device is null"); + + if (textureDescriptors) + mTextureDescriptors = std::make_unique(textureDescriptors); + if (samplerDescriptors) + mSamplerDescriptors = std::make_unique(samplerDescriptors); + } + + Impl(const Impl&) = delete; + Impl& operator=(const Impl&) = delete; + + Impl(Impl&&) = delete; + Impl& operator=(Impl&&) = delete; + + std::shared_ptr CreateEffect( + const EffectInfo& info, + const EffectPipelineStateDescription& opaquePipelineState, + const EffectPipelineStateDescription& alphaPipelineState, + const D3D12_INPUT_LAYOUT_DESC& inputLayout, + int textureDescriptorOffset, + int samplerDescriptorOffset); + + void ReleaseCache(); + + std::unique_ptr mTextureDescriptors; + std::unique_ptr mSamplerDescriptors; + + NPREffect::Mode mMode; + bool mSharing; + bool mEnableInstancing; + bool mUseEmissiveForMatCap; + int mMatcapTextureIndex; + int mMatcapSamplerIndex; + + ComPtr mDevice; + +private: + using EffectCache = std::map< std::wstring, std::shared_ptr >; + + EffectCache mEffectCache; + EffectCache mEffectCacheSkinning; + EffectCache mEffectCacheDualTexture; + + std::mutex mutex; +}; + + +std::shared_ptr NPREffectFactory::Impl::CreateEffect( + const EffectInfo& info, + const EffectPipelineStateDescription& opaquePipelineState, + const EffectPipelineStateDescription& alphaPipelineState, + const D3D12_INPUT_LAYOUT_DESC& inputLayoutDesc, + int textureDescriptorOffset, + int samplerDescriptorOffset) +{ + // If textures are required, make sure we have a descriptor heap + if (!mTextureDescriptors && (info.diffuseTextureIndex != -1 || info.specularTextureIndex != -1 || info.emissiveTextureIndex != -1)) + { + DebugTrace("ERROR: NPREffectFactory created without texture descriptor heap with texture index set (diffuse %d, specular %d, emissive %d)!\n", + info.diffuseTextureIndex, info.specularTextureIndex, info.emissiveTextureIndex); + throw std::runtime_error("NPREffectFactory"); + } + if (!mSamplerDescriptors && (info.samplerIndex != -1 || info.samplerIndex2 != -1)) + { + DebugTrace("ERROR: NPREffectFactory created without sampler descriptor heap with sampler index set (samplerIndex %d, samplerIndex2 %d)!\n", + info.samplerIndex, info.samplerIndex2); + throw std::runtime_error("NPREffectFactory"); + } + + // If we have descriptors, make sure we have both texture and sampler descriptors + if ((mTextureDescriptors == nullptr) != (mSamplerDescriptors == nullptr)) + { + DebugTrace("ERROR: A texture or sampler descriptor heap was provided, but both are required.\n"); + throw std::runtime_error("NPREffectFactory"); + } + + // Validate the we have either both texture and sampler descriptors, or neither + if ((info.diffuseTextureIndex == -1) != (info.samplerIndex == -1)) + { + DebugTrace("ERROR: Material provides either a texture or sampler, but both are required.\n"); + throw std::runtime_error("NPREffectFactory"); + } + + const int diffuseTextureIndex = (info.diffuseTextureIndex != -1 && mTextureDescriptors != nullptr) ? info.diffuseTextureIndex + textureDescriptorOffset : -1; + const int specularTextureIndex = (info.specularTextureIndex != -1 && mTextureDescriptors != nullptr) ? info.specularTextureIndex + textureDescriptorOffset : -1; + const int emissiveTextureIndex = (info.emissiveTextureIndex != -1 && mTextureDescriptors != nullptr) ? info.emissiveTextureIndex + textureDescriptorOffset : -1; + const int samplerIndex = (info.samplerIndex != -1 && mSamplerDescriptors != nullptr) ? info.samplerIndex + samplerDescriptorOffset : -1; + const int samplerIndex2 = (info.samplerIndex2 != -1 && mSamplerDescriptors != nullptr) ? info.samplerIndex2 + samplerDescriptorOffset : -1; + + // Modify base pipeline state + EffectPipelineStateDescription derivedPSD = (info.alphaValue < 1.0f) ? alphaPipelineState : opaquePipelineState; + derivedPSD.inputLayout = inputLayoutDesc; + + std::wstring cacheName; + if (info.enableSkinning) + { + int effectflags = EffectFlags::None; + + if (info.biasedVertexNormals) + { + effectflags |= EffectFlags::BiasedVertexNormals; + } + + // SkinnedNPREffect + if (mSharing && !info.name.empty()) + { + const uint32_t hash = derivedPSD.ComputeHash(); + cacheName = std::to_wstring(effectflags) + std::to_wstring(mMode) + info.name + std::to_wstring(hash); + + auto it = mEffectCacheSkinning.find(cacheName); + if (mSharing && it != mEffectCacheSkinning.end()) + { + return it->second; + } + } + + auto effect = std::make_shared(mDevice.Get(), effectflags, derivedPSD, mMode); + + SetMaterialProperties(effect.get(), info); + + if (diffuseTextureIndex != -1) + { + effect->SetTexture( + mTextureDescriptors->GetGpuHandle(static_cast(diffuseTextureIndex)), + mSamplerDescriptors->GetGpuHandle(static_cast(samplerIndex))); + } + + if (mMode == NPREffect::Mode_MatCap) + { + if (mUseEmissiveForMatCap && emissiveTextureIndex != -1) + { + if (samplerIndex == -1) + { + DebugTrace("ERROR: SkinnedNPREffect (matcap) requires a sampler (emissive %d)\n", emissiveTextureIndex); + throw std::runtime_error("NPREffectFactory"); + } + + effect->SetMatCap( + mTextureDescriptors->GetGpuHandle(static_cast(emissiveTextureIndex)), + mSamplerDescriptors->GetGpuHandle(static_cast(samplerIndex))); + } + else if (mMatcapTextureIndex != -1) + { + auto matcap = mTextureDescriptors->GetGpuHandle(static_cast(mMatcapTextureIndex)); + if (samplerIndex != -1) + { + effect->SetMatCap(matcap, + mSamplerDescriptors->GetGpuHandle(static_cast(samplerIndex))); + } + else if (mMatcapSamplerIndex != -1) + { + effect->SetMatCap(matcap, + mSamplerDescriptors->GetGpuHandle(static_cast(mMatcapSamplerIndex))); + } + } + } + + if (mSharing && !info.name.empty()) + { + std::lock_guard lock(mutex); + EffectCache::value_type v(cacheName, effect); + mEffectCacheSkinning.insert(v); + } + + return std::move(effect); + } + else if (info.enableDualTexture) + { + // DualTextureEffect + int effectflags = EffectFlags::None; + + if (mSharing && !info.name.empty()) + { + const uint32_t hash = derivedPSD.ComputeHash(); + cacheName = std::to_wstring(effectflags) + info.name + std::to_wstring(hash); + + auto it = mEffectCacheDualTexture.find(cacheName); + if (mSharing && it != mEffectCacheDualTexture.end()) + { + return it->second; + } + } + + if (info.perVertexColor) + { + effectflags |= EffectFlags::VertexColor; + } + + auto effect = std::make_shared(mDevice.Get(), effectflags, derivedPSD); + + // Dual texture effect doesn't support lighting (usually it's lightmaps) + effect->SetAlpha(info.alphaValue); + + const XMVECTOR color = XMLoadFloat3(&info.diffuseColor); + effect->SetDiffuseColor(color); + + if (diffuseTextureIndex != -1) + { + effect->SetTexture( + mTextureDescriptors->GetGpuHandle(static_cast(diffuseTextureIndex)), + mSamplerDescriptors->GetGpuHandle(static_cast(samplerIndex))); + } + + if (emissiveTextureIndex != -1) + { + if (samplerIndex2 == -1) + { + DebugTrace("ERROR: Dual-texture requires a second sampler (emissive %d)\n", emissiveTextureIndex); + throw std::runtime_error("NPREffectFactory"); + } + + effect->SetTexture2( + mTextureDescriptors->GetGpuHandle(static_cast(emissiveTextureIndex)), + mSamplerDescriptors->GetGpuHandle(static_cast(samplerIndex2))); + } + else if (specularTextureIndex != -1) + { + // If there's no emissive texture specified, use the specular texture as the second texture + if (samplerIndex2 == -1) + { + DebugTrace("ERROR: Dual-texture requires a second sampler (specular %d)\n", specularTextureIndex); + throw std::runtime_error("NPREffectFactory"); + } + + effect->SetTexture2( + mTextureDescriptors->GetGpuHandle(static_cast(specularTextureIndex)), + mSamplerDescriptors->GetGpuHandle(static_cast(samplerIndex2))); + } + + if (mSharing && !info.name.empty()) + { + std::lock_guard lock(mutex); + EffectCache::value_type v(cacheName, effect); + mEffectCacheDualTexture.insert(v); + } + + return std::move(effect); + } + else + { + // set effect flags for creation + int effectflags = EffectFlags::None; + + if (info.perVertexColor) + { + effectflags |= EffectFlags::VertexColor; + } + + if (diffuseTextureIndex != -1) + { + effectflags |= EffectFlags::Texture; + } + + if (info.biasedVertexNormals) + { + effectflags |= EffectFlags::BiasedVertexNormals; + } + + // NPREffect + if (mSharing && !info.name.empty()) + { + const uint32_t hash = derivedPSD.ComputeHash(); + cacheName = std::to_wstring(effectflags) + std::to_wstring(mMode) + info.name + std::to_wstring(hash); + + auto it = mEffectCache.find(cacheName); + if (mSharing && it != mEffectCache.end()) + { + return it->second; + } + } + + auto effect = std::make_shared(mDevice.Get(), effectflags, derivedPSD, mMode); + + SetMaterialProperties(effect.get(), info); + + if (diffuseTextureIndex != -1) + { + effect->SetTexture( + mTextureDescriptors->GetGpuHandle(static_cast(diffuseTextureIndex)), + mSamplerDescriptors->GetGpuHandle(static_cast(samplerIndex))); + } + + if (mMode == NPREffect::Mode_MatCap) + { + if (mUseEmissiveForMatCap && emissiveTextureIndex != -1) + { + if (samplerIndex == -1) + { + DebugTrace("ERROR: NPREffect (matcap) requires a sampler (emissive %d)\n", emissiveTextureIndex); + throw std::runtime_error("NPREffectFactory"); + } + + effect->SetMatCap( + mTextureDescriptors->GetGpuHandle(static_cast(emissiveTextureIndex)), + mSamplerDescriptors->GetGpuHandle(static_cast(samplerIndex))); + } + else if (mMatcapTextureIndex != -1) + { + auto matcap = mTextureDescriptors->GetGpuHandle(static_cast(mMatcapTextureIndex)); + if (samplerIndex != -1) + { + effect->SetMatCap(matcap, + mSamplerDescriptors->GetGpuHandle(static_cast(samplerIndex))); + } + else if (mMatcapSamplerIndex != -1) + { + effect->SetMatCap(matcap, + mSamplerDescriptors->GetGpuHandle(static_cast(mMatcapSamplerIndex))); + } + } + } + + if (mSharing && !info.name.empty()) + { + std::lock_guard lock(mutex); + EffectCache::value_type v(cacheName, effect); + mEffectCache.insert(v); + } + + return std::move(effect); + } +} + +void NPREffectFactory::Impl::ReleaseCache() +{ + std::lock_guard lock(mutex); + mEffectCache.clear(); + mEffectCacheSkinning.clear(); + mEffectCacheDualTexture.clear(); +} + + + +//-------------------------------------------------------------------------------------- +// NPREffectFactory +//-------------------------------------------------------------------------------------- + +NPREffectFactory::NPREffectFactory(_In_ ID3D12Device* device) : + pImpl(std::make_shared(device, nullptr, nullptr)) +{} + +NPREffectFactory::NPREffectFactory(_In_ ID3D12DescriptorHeap* textureDescriptors, _In_ ID3D12DescriptorHeap* samplerDescriptors) +{ + if (!textureDescriptors) + { + throw std::invalid_argument("Texture descriptor heap cannot be null if no device is provided. Use the alternative NPREffectFactory constructor instead."); + } + if (!samplerDescriptors) + { + throw std::invalid_argument("Descriptor heap cannot be null if no device is provided. Use the alternative NPREffectFactory constructor instead."); + } + +#if defined(_MSC_VER) || !defined(_WIN32) + const D3D12_DESCRIPTOR_HEAP_TYPE textureHeapType = textureDescriptors->GetDesc().Type; + const D3D12_DESCRIPTOR_HEAP_TYPE samplerHeapType = samplerDescriptors->GetDesc().Type; +#else + D3D12_DESCRIPTOR_HEAP_DESC tmpDesc1, tmpDesc2; + const D3D12_DESCRIPTOR_HEAP_TYPE textureHeapType = textureDescriptors->GetDesc(&tmpDesc1)->Type; + const D3D12_DESCRIPTOR_HEAP_TYPE samplerHeapType = samplerDescriptors->GetDesc(&tmpDesc2)->Type; +#endif + + if (textureHeapType != D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV) + { + throw std::invalid_argument("NPREffectFactory::CreateEffect requires a CBV_SRV_UAV descriptor heap for textureDescriptors."); + } + if (samplerHeapType != D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER) + { + throw std::invalid_argument("NPREffectFactory::CreateEffect requires a SAMPLER descriptor heap for samplerDescriptors."); + } + + ComPtr device; +#if (defined(_XBOX_ONE) && defined(_TITLE)) || defined(_GAMING_XBOX) + textureDescriptors->GetDevice(IID_GRAPHICS_PPV_ARGS(device.GetAddressOf())); +#else + HRESULT hr = textureDescriptors->GetDevice(IID_PPV_ARGS(device.GetAddressOf())); + ThrowIfFailed(hr); +#endif + + pImpl = std::make_shared(device.Get(), textureDescriptors, samplerDescriptors); +} + + +NPREffectFactory::NPREffectFactory(NPREffectFactory&&) noexcept = default; +NPREffectFactory& NPREffectFactory::operator= (NPREffectFactory&&) noexcept = default; +NPREffectFactory::~NPREffectFactory() = default; + + +std::shared_ptr NPREffectFactory::CreateEffect( + const EffectInfo& info, + const EffectPipelineStateDescription& opaquePipelineState, + const EffectPipelineStateDescription& alphaPipelineState, + const D3D12_INPUT_LAYOUT_DESC& inputLayout, + int textureDescriptorOffset, + int samplerDescriptorOffset) +{ + return pImpl->CreateEffect(info, opaquePipelineState, alphaPipelineState, inputLayout, textureDescriptorOffset, samplerDescriptorOffset); +} + + +void NPREffectFactory::ReleaseCache() +{ + pImpl->ReleaseCache(); +} + + +// Properties. +void NPREffectFactory::SetSharing(bool enabled) noexcept +{ + pImpl->mSharing = enabled; +} + +void NPREffectFactory::EnableInstancing(bool enabled) noexcept +{ + pImpl->mEnableInstancing = enabled; +} + +void NPREffectFactory::SetMode(NPREffect::Mode mode) noexcept +{ + pImpl->mMode = mode; +} + +void NPREffectFactory::SetDefaultMatCap(int textureIndex, int samplerIndex) noexcept +{ + pImpl->mMatcapTextureIndex = textureIndex; + pImpl->mMatcapSamplerIndex = samplerIndex; +} + +void NPREffectFactory::SetEmissiveAsMatCap(bool value) noexcept +{ + pImpl->mUseEmissiveForMatCap = value; +} + +ID3D12Device* NPREffectFactory::GetDevice() const noexcept +{ + return pImpl->mDevice.Get(); +} diff --git a/Src/PBREffect.cpp b/Src/PBREffect.cpp index 59b98f7a..ac67dd1e 100644 --- a/Src/PBREffect.cpp +++ b/Src/PBREffect.cpp @@ -407,12 +407,12 @@ void PBREffect::Impl::Initialize( for (size_t i = 0; i < std::size(textureSRV); i++) { - rootParameters[i].InitAsDescriptorTable(1, &textureSRV[i]); + rootParameters[i].InitAsDescriptorTable(1, &textureSRV[i], D3D12_SHADER_VISIBILITY_PIXEL); } for (size_t i = 0; i < std::size(textureSampler); i++) { - rootParameters[i + SurfaceSampler].InitAsDescriptorTable(1, &textureSampler[i]); + rootParameters[i + SurfaceSampler].InitAsDescriptorTable(1, &textureSampler[i], D3D12_SHADER_VISIBILITY_PIXEL); } rootParameters[ConstantBuffer].InitAsConstantBufferView(0, 0, D3D12_SHADER_VISIBILITY_ALL); diff --git a/Src/PBREffectFactory.cpp b/Src/PBREffectFactory.cpp index 0e0268db..91438ecc 100644 --- a/Src/PBREffectFactory.cpp +++ b/Src/PBREffectFactory.cpp @@ -119,9 +119,9 @@ class PBREffectFactory::Impl std::unique_ptr mTextureDescriptors; std::unique_ptr mSamplerDescriptors; -private: ComPtr mDevice; +private: using EffectCache = std::map< std::wstring, std::shared_ptr >; EffectCache mEffectCache; @@ -327,3 +327,8 @@ void PBREffectFactory::EnableInstancing(bool enabled) noexcept { pImpl->mEnableInstancing = enabled; } + +ID3D12Device* PBREffectFactory::GetDevice() const noexcept +{ + return pImpl->mDevice.Get(); +} diff --git a/Src/Shaders/RootSig.fxh b/Src/Shaders/RootSig.fxh index 2b097331..b09c63ba 100644 --- a/Src/Shaders/RootSig.fxh +++ b/Src/Shaders/RootSig.fxh @@ -182,14 +182,14 @@ " DENY_GEOMETRY_SHADER_ROOT_ACCESS |" \ " DENY_HULL_SHADER_ROOT_ACCESS |" \ " DENY_MESH_SHADER_ROOT_ACCESS )," \ -"DescriptorTable ( SRV(t0) ),"\ -"DescriptorTable ( SRV(t1) ),"\ -"DescriptorTable ( SRV(t2) ),"\ -"DescriptorTable ( SRV(t3) ),"\ -"DescriptorTable ( SRV(t4) ),"\ -"DescriptorTable ( SRV(t5) ),"\ -"DescriptorTable ( Sampler(s0) ),"\ -"DescriptorTable ( Sampler(s1) ),"\ +"DescriptorTable ( SRV(t0), visibility = SHADER_VISIBILITY_PIXEL ),"\ +"DescriptorTable ( SRV(t1), visibility = SHADER_VISIBILITY_PIXEL ),"\ +"DescriptorTable ( SRV(t2), visibility = SHADER_VISIBILITY_PIXEL ),"\ +"DescriptorTable ( SRV(t3), visibility = SHADER_VISIBILITY_PIXEL ),"\ +"DescriptorTable ( SRV(t4), visibility = SHADER_VISIBILITY_PIXEL ),"\ +"DescriptorTable ( SRV(t5), visibility = SHADER_VISIBILITY_PIXEL ),"\ +"DescriptorTable ( Sampler(s0), visibility = SHADER_VISIBILITY_PIXEL ),"\ +"DescriptorTable ( Sampler(s1), visibility = SHADER_VISIBILITY_PIXEL ),"\ "CBV(b0)," \ "CBV(b1, visibility = SHADER_VISIBILITY_VERTEX )" @@ -390,14 +390,14 @@ " DENY_DOMAIN_SHADER_ROOT_ACCESS |" \ " DENY_GEOMETRY_SHADER_ROOT_ACCESS |" \ " DENY_HULL_SHADER_ROOT_ACCESS )," \ -"DescriptorTable ( SRV(t0) ),"\ -"DescriptorTable ( SRV(t1) ),"\ -"DescriptorTable ( SRV(t2) ),"\ -"DescriptorTable ( SRV(t3) ),"\ -"DescriptorTable ( SRV(t4) ),"\ -"DescriptorTable ( SRV(t5) ),"\ -"DescriptorTable ( Sampler(s0) ),"\ -"DescriptorTable ( Sampler(s1) ),"\ +"DescriptorTable ( SRV(t0), visibility = SHADER_VISIBILITY_PIXEL ),"\ +"DescriptorTable ( SRV(t1), visibility = SHADER_VISIBILITY_PIXEL ),"\ +"DescriptorTable ( SRV(t2), visibility = SHADER_VISIBILITY_PIXEL ),"\ +"DescriptorTable ( SRV(t3), visibility = SHADER_VISIBILITY_PIXEL ),"\ +"DescriptorTable ( SRV(t4), visibility = SHADER_VISIBILITY_PIXEL ),"\ +"DescriptorTable ( SRV(t5), visibility = SHADER_VISIBILITY_PIXEL ),"\ +"DescriptorTable ( Sampler(s0), visibility = SHADER_VISIBILITY_PIXEL ),"\ +"DescriptorTable ( Sampler(s1), visibility = SHADER_VISIBILITY_PIXEL ),"\ "CBV(b0)," \ "CBV(b1, visibility = SHADER_VISIBILITY_VERTEX )"