From cb296756e99cd47f96cde69c90fd378eca1f5fc7 Mon Sep 17 00:00:00 2001 From: kirides <13602143+kirides@users.noreply.github.com> Date: Mon, 23 Feb 2026 10:53:38 +0100 Subject: [PATCH 01/13] make some features toggle-able --- D3D11Engine/D3D11GraphicsEngine.cpp | 27 ++++++++++++++++++++------- D3D11Engine/D3D11ShaderManager.cpp | 4 +++- D3D11Engine/D3D11ShadowMap.cpp | 3 +-- D3D11Engine/GothicAPI.cpp | 15 ++++++--------- D3D11Engine/GothicGraphicsState.h | 4 ++++ D3D11Engine/ImGuiShim.cpp | 6 ++++++ D3D11Engine/Shaders/GS_Cubemap.hlsl | 4 ++-- 7 files changed, 42 insertions(+), 21 deletions(-) diff --git a/D3D11Engine/D3D11GraphicsEngine.cpp b/D3D11Engine/D3D11GraphicsEngine.cpp index 38d97147..af4421e3 100644 --- a/D3D11Engine/D3D11GraphicsEngine.cpp +++ b/D3D11Engine/D3D11GraphicsEngine.cpp @@ -520,21 +520,22 @@ XRESULT D3D11GraphicsEngine::Init() { FeatureLevel10Compatibility = (maxFeatureLevel < D3D_FEATURE_LEVEL::D3D_FEATURE_LEVEL_11_0); FetchDisplayModeList(); - IUnknown* renderdoc = nullptr; - result = Device->QueryInterface( IID_IDXGIDeviceRenderDoc, reinterpret_cast(&renderdoc) ); + ComPtr renderdoc = nullptr; + result = Device.AsIID( IID_IDXGIDeviceRenderDoc, &renderdoc ); if ( SUCCEEDED( result ) ) { // Don't use extensions if they are available // renderdoc doesn't like them DrawMultiIndexedInstancedIndirect = Stub_DrawMultiIndexedInstancedIndirect; BeginUAVOverlap = Stub_BeginUAVOverlap; EndUAVOverlap = Stub_EndUAVOverlap; - renderdoc->Release(); } if ( !DrawMultiIndexedInstancedIndirect ) { DrawMultiIndexedInstancedIndirect = Stub_DrawMultiIndexedInstancedIndirect; } + Engine::GAPI->GetRendererState().RendererSettings.DebugSettings.FeatureSet.UseMDI = DrawMultiIndexedInstancedIndirect != Stub_DrawMultiIndexedInstancedIndirect; + if ( !BeginUAVOverlap || !EndUAVOverlap ) { BeginUAVOverlap = Stub_BeginUAVOverlap; EndUAVOverlap = Stub_EndUAVOverlap; @@ -544,6 +545,7 @@ XRESULT D3D11GraphicsEngine::Init() { hr = Device->CheckFeatureSupport(D3D11_FEATURE_D3D11_OPTIONS3, &options3, sizeof( options3 ) ); if ( SUCCEEDED( hr ) ) { FeatureRTArrayIndexFromAnyShader = options3.VPAndRTArrayIndexFromAnyShaderFeedingRasterizer; + Engine::GAPI->GetRendererState().RendererSettings.DebugSettings.FeatureSet.UseLayeredRendering = FeatureRTArrayIndexFromAnyShader; } LogInfo() << "Creating ShaderManager"; @@ -4493,6 +4495,7 @@ void XM_CALLCONV D3D11GraphicsEngine::DrawWorldAroundForWorldShadow( FXMVECTOR p bool colorWritesEnabled = Engine::GAPI->GetRendererState().BlendState.ColorWritesEnabled; float alphaRef = Engine::GAPI->GetRendererState().GraphicsState.FF_AlphaRef; + auto& currentFrustum = params.CascadeCameraReplacements->at(params.CascadeIndex).frustum; if ( Engine::GAPI->GetRendererState().RendererSettings.DrawWorldMesh ) { auto _ = START_TIMING( timer_labels_world_mesh[timerLabelIndex] ); @@ -4515,12 +4518,20 @@ void XM_CALLCONV D3D11GraphicsEngine::DrawWorldAroundForWorldShadow( FXMVECTOR p float dy = static_cast(ity.first - s.y); float lenSq = dx * dx + dy * dy; - if ( lenSq < sectionRangeSq ) { - visibleSections.push_back( &ity.second ); + if ( lenSq > sectionRangeSq ) { + continue; + } + if ( !currentFrustum.Intersects(Frustum::BBoxFromzTBBox3D(ity.second.BoundingBox)) ) { + continue; } + visibleSections.push_back( &ity.second ); } } + auto drawMultiIndexedInstancedIndirect = Engine::GAPI->GetRendererState().RendererSettings.DebugSettings.FeatureSet.UseMDI + ? DrawMultiIndexedInstancedIndirect + : Stub_DrawMultiIndexedInstancedIndirect; + if ( Engine::GAPI->GetRendererState().RendererSettings.FastShadows ) { if ( !linearDepth ) // Only unbind when not rendering linear depth { @@ -4591,7 +4602,7 @@ void XM_CALLCONV D3D11GraphicsEngine::DrawWorldAroundForWorldShadow( FXMVECTOR p // Execute multi-draw indirect call for all opaque meshes // DrawMultiIndexedInstancedIndirect falls back to individual DrawIndexedInstancedIndirect // calls via Stub_DrawMultiIndexedInstancedIndirect if hardware doesn't support MDI - DrawMultiIndexedInstancedIndirect( Context.Get(), + drawMultiIndexedInstancedIndirect( Context.Get(), static_cast(opaqueDrawArgs.size()), WorldMeshIndirectBuffer->GetIndirectBuffer().Get(), 0, @@ -4609,6 +4620,9 @@ void XM_CALLCONV D3D11GraphicsEngine::DrawWorldAroundForWorldShadow( FXMVECTOR p for ( const auto& [tex, mesh] : alphaMeshes ) { if ( tex != lastTex ) { + if (tex->CacheIn( 0.6f ) == zRES_CACHED_OUT) { + continue; + } tex->Bind( 0 ); lastTex = tex; } @@ -4783,7 +4797,6 @@ void XM_CALLCONV D3D11GraphicsEngine::DrawWorldAroundForWorldShadow( FXMVECTOR p auto skeletalRadiusSq = Engine::GAPI->GetRendererState().RendererSettings.SkeletalMeshDrawRadius * Engine::GAPI->GetRendererState().RendererSettings.SkeletalMeshDrawRadius; - auto& currentFrustum = params.CascadeCameraReplacements->at(params.CascadeIndex).frustum; XMVECTOR vSkeletalRadiusSq = XMVectorReplicate(skeletalRadiusSq); // Draw skeletal meshes diff --git a/D3D11Engine/D3D11ShaderManager.cpp b/D3D11Engine/D3D11ShaderManager.cpp index 4149f409..7f7abba4 100644 --- a/D3D11Engine/D3D11ShaderManager.cpp +++ b/D3D11Engine/D3D11ShaderManager.cpp @@ -534,7 +534,9 @@ XRESULT D3D11ShaderManager::Init() { Shaders.back().cBufferSizes.push_back( sizeof( VS_ExConstantBuffer_PerInstanceSkeletal ) ); Shaders.back().cBufferSizes.push_back( NUM_MAX_BONES * sizeof( XMFLOAT4X4 ) ); Shaders.back().cBufferSizes.push_back( sizeof( CubemapGSConstantBuffer ) ); // cbPerCubeRender for layered rendering - } else { + } + /*else: always compile fallback shaders*/ + { Shaders.push_back( ShaderInfo( "GS_Cubemap", "GS_Cubemap.hlsl", "g" ) ); Shaders.back().cBufferSizes.push_back( sizeof( CubemapGSConstantBuffer ) ); diff --git a/D3D11Engine/D3D11ShadowMap.cpp b/D3D11Engine/D3D11ShadowMap.cpp index 11642132..6d975d5e 100644 --- a/D3D11Engine/D3D11ShadowMap.cpp +++ b/D3D11Engine/D3D11ShadowMap.cpp @@ -20,7 +20,6 @@ using namespace DirectX; extern bool FeatureLevel10Compatibility; -extern bool FeatureRTArrayIndexFromAnyShader; const float NUM_FRAME_SHADOW_UPDATES = 2; // Fraction of lights to update per frame const int NUM_MIN_FRAME_SHADOW_UPDATES = 4; // Minimum lights to update per frame @@ -1058,7 +1057,7 @@ void XM_CALLCONV D3D11ShadowMap::RenderShadowCube( bool useLayeredPath = false; if ( !face.Get() ) { - if ( FeatureRTArrayIndexFromAnyShader ) { + if ( Engine::GAPI->GetRendererState().RendererSettings.DebugSettings.FeatureSet.UseLayeredRendering ) { useLayeredPath = true; face = targetCube.GetDepthStencilView().Get(); diff --git a/D3D11Engine/GothicAPI.cpp b/D3D11Engine/GothicAPI.cpp index 80dab10d..b400ad5b 100644 --- a/D3D11Engine/GothicAPI.cpp +++ b/D3D11Engine/GothicAPI.cpp @@ -1158,14 +1158,12 @@ void GothicAPI::DrawWorldMeshNaive() { { auto _ = START_TIMING( "World Mesh" ); auto _1 = Engine::GraphicsEngine->RecordGraphicsEvent( L"World Mesh" ); - /* - if ( FeatureLevel10Compatibility ) { - Engine::GraphicsEngine->DrawWorldMesh(); - } else { + + if ( Engine::GAPI->GetRendererState().RendererSettings.DebugSettings.FeatureSet.UseMDI ) { Engine::GraphicsEngine->DrawWorldMesh_Indirect(); + } else { + Engine::GraphicsEngine->DrawWorldMesh(); } - */ - Engine::GraphicsEngine->DrawWorldMesh(); } @@ -4132,7 +4130,6 @@ void GothicAPI::CollectVisibleSections( std::vector& sect auto cullingEnabled = Engine::GAPI->GetRendererState().RendererSettings.DebugSettings.Culling.CullBspSections; if ( Engine::GAPI->GetRendererState().RendererSettings.DrawSectionIntersections ) { - extern const float WORLD_SECTION_SIZE; const float sectionViewDist = Engine::GAPI->GetRendererState().RendererSettings.SectionDrawRadius * WORLD_SECTION_SIZE; for ( auto& itx : WorldSections ) { for ( auto& ity : itx.second ) { @@ -4140,7 +4137,7 @@ void GothicAPI::CollectVisibleSections( std::vector& sect float dist = Toolbox::ComputePointAABBDistance( camPos, section.BoundingBox.Min, section.BoundingBox.Max ); if ( dist < sectionViewDist ) { - if ( cullingEnabled && GetCameraBBox3DInFrustum( section.BoundingBox, EGothicCullFlags::CullSides ) == ZTCAM_CLIPTYPE_OUT ) + if ( cullingEnabled && GetCameraBBox3DInFrustum( section.BoundingBox, EGothicCullFlags::CullSidesNear ) == ZTCAM_CLIPTYPE_OUT ) continue; sections.push_back( §ion ); @@ -4160,7 +4157,7 @@ void GothicAPI::CollectVisibleSections( std::vector& sect // Simple range-check if ( abs( ity.first - camSection.y ) < sectionViewDist ) { - if ( cullingEnabled && GetCameraBBox3DInFrustum( section.BoundingBox, EGothicCullFlags::CullSides ) == ZTCAM_CLIPTYPE_OUT ) + if ( cullingEnabled && GetCameraBBox3DInFrustum( section.BoundingBox, EGothicCullFlags::CullSidesNear ) == ZTCAM_CLIPTYPE_OUT ) continue; sections.push_back( §ion ); diff --git a/D3D11Engine/GothicGraphicsState.h b/D3D11Engine/GothicGraphicsState.h index 327920bb..4463bcd0 100644 --- a/D3D11Engine/GothicGraphicsState.h +++ b/D3D11Engine/GothicGraphicsState.h @@ -940,6 +940,10 @@ struct GothicRendererSettings { bool CullVobs; bool CullBspSections; } Culling; + struct { + bool UseMDI; + bool UseLayeredRendering; + } FeatureSet; } DebugSettings; }; diff --git a/D3D11Engine/ImGuiShim.cpp b/D3D11Engine/ImGuiShim.cpp index 371b0bb6..09aeca93 100644 --- a/D3D11Engine/ImGuiShim.cpp +++ b/D3D11Engine/ImGuiShim.cpp @@ -1178,6 +1178,12 @@ void RenderAdvancedColumn2( GothicRendererSettings& settings, GothicAPI* gapi ) ImGui::Checkbox("Vobs", &settings.DebugSettings.Culling.CullVobs ); ImGui::EndTabItem(); } + + if (ImGui::BeginTabItem("Featureset", nullptr, ImGuiTabItemFlags_::ImGuiTabItemFlags_NoReorder)) { + ImGui::Checkbox("Use MDI", &settings.DebugSettings.FeatureSet.UseMDI ); + ImGui::Checkbox("Use Layered Drawing", &settings.DebugSettings.FeatureSet.UseLayeredRendering ); + ImGui::EndTabItem(); + } ImGui::EndTabBar(); } diff --git a/D3D11Engine/Shaders/GS_Cubemap.hlsl b/D3D11Engine/Shaders/GS_Cubemap.hlsl index dc7481df..cf9dd7bf 100644 --- a/D3D11Engine/Shaders/GS_Cubemap.hlsl +++ b/D3D11Engine/Shaders/GS_Cubemap.hlsl @@ -13,8 +13,8 @@ struct PS_INPUT float4 vDiffuse : TEXCOORD2; float3 vNormalVS : TEXCOORD4; float3 vViewPosition : TEXCOORD5; - float4 vCurrClipPos : TEXCOORD6; - float4 vPrevClipPos : TEXCOORD7; + //float4 vCurrClipPos : TEXCOORD6; + //float4 vPrevClipPos : TEXCOORD7; float4 vPosition : SV_POSITION; uint RTIndex : SV_RenderTargetArrayIndex; }; From aa0ef0fd328bdae8c5dd4f84677fe78f0d38a77a Mon Sep 17 00:00:00 2001 From: kirides <13602143+kirides@users.noreply.github.com> Date: Mon, 23 Feb 2026 11:26:43 +0100 Subject: [PATCH 02/13] allow drawing world mesh withouth MDI/indirect drawing support --- D3D11Engine/D3D11GraphicsEngine.cpp | 108 ++-------------------------- 1 file changed, 4 insertions(+), 104 deletions(-) diff --git a/D3D11Engine/D3D11GraphicsEngine.cpp b/D3D11Engine/D3D11GraphicsEngine.cpp index af4421e3..f502ecb9 100644 --- a/D3D11Engine/D3D11GraphicsEngine.cpp +++ b/D3D11Engine/D3D11GraphicsEngine.cpp @@ -4521,115 +4521,15 @@ void XM_CALLCONV D3D11GraphicsEngine::DrawWorldAroundForWorldShadow( FXMVECTOR p if ( lenSq > sectionRangeSq ) { continue; } - if ( !currentFrustum.Intersects(Frustum::BBoxFromzTBBox3D(ity.second.BoundingBox)) ) { - continue; - } + visibleSections.push_back( &ity.second ); } } - - auto drawMultiIndexedInstancedIndirect = Engine::GAPI->GetRendererState().RendererSettings.DebugSettings.FeatureSet.UseMDI - ? DrawMultiIndexedInstancedIndirect - : Stub_DrawMultiIndexedInstancedIndirect; - if ( Engine::GAPI->GetRendererState().RendererSettings.FastShadows ) { - if ( !linearDepth ) // Only unbind when not rendering linear depth - { - // Unbind PS - Context->PSSetShader( nullptr, nullptr, 0 ); - } - - for ( const WorldMeshSectionInfo* section : visibleSections ) { - if ( section->FullStaticMesh ) { - Engine::GAPI->DrawMeshInfo( nullptr, section->FullStaticMesh ); - } - } + if (Engine::GAPI->GetRendererState().RendererSettings.DebugSettings.FeatureSet.UseMDI) { + ShadowPass_DrawWorldMesh_Indirect(visibleSections); } else { - // Collect all meshes first, then batch by alpha requirement - static thread_local std::vector opaqueDrawArgs; - static thread_local std::vector> alphaMeshes; - opaqueDrawArgs.clear(); - alphaMeshes.clear(); - - for ( const WorldMeshSectionInfo* section : visibleSections ) { - for ( const auto& meshPair : section->WorldMeshes ) { - // Skip non-standard materials (water, portals, etc.) - if ( meshPair.first.Info->MaterialType != MaterialInfo::MT_None ) - continue; - - zCTexture* tex = meshPair.first.Material ? meshPair.first.Material->GetTexture() : nullptr; - - if ( tex && tex->HasAlphaChannel() && alphaRef > 0.0f ) { - // Need alpha testing - cache texture - if ( tex->CacheIn( 0.6f ) == zRES_CACHED_IN ) { - alphaMeshes.emplace_back( tex, meshPair.second ); - } - } else { - D3D11_DRAW_INDEXED_INSTANCED_INDIRECT_ARGS args; - args.IndexCountPerInstance = static_cast(meshPair.second->Indices.size()); - args.InstanceCount = 1; - args.StartIndexLocation = meshPair.second->BaseIndexLocation; - args.BaseVertexLocation = 0; - args.StartInstanceLocation = 0; - opaqueDrawArgs.push_back( args ); - } - - Engine::GAPI->GetRendererState().RendererInfo.FrameDrawnTriangles += - meshPair.second->Indices.size() / 3; - } - } - - // Draw all opaque meshes without pixel shader (depth only) using MDI - if ( !opaqueDrawArgs.empty() ) { - if ( !linearDepth ) { - // Unbind PS for depth-only rendering - Context->PSSetShader( nullptr, nullptr, 0 ); - } - - // Initialize or resize the indirect buffer if needed - const size_t requiredSize = opaqueDrawArgs.size() * sizeof( D3D11_DRAW_INDEXED_INSTANCED_INDIRECT_ARGS ); - - if ( !WorldMeshIndirectBuffer || WorldMeshIndirectBuffer->GetSizeInBytes() < requiredSize ) { - WorldMeshIndirectBuffer.reset( new D3D11IndirectBuffer ); - WorldMeshIndirectBuffer->Init( - opaqueDrawArgs.data(), requiredSize, - D3D11IndirectBuffer::B_INDEXBUFFER, D3D11IndirectBuffer::U_DYNAMIC, - D3D11IndirectBuffer::CA_WRITE ); - } else { - WorldMeshIndirectBuffer->UpdateBuffer( opaqueDrawArgs.data(), requiredSize ); - } - - // Execute multi-draw indirect call for all opaque meshes - // DrawMultiIndexedInstancedIndirect falls back to individual DrawIndexedInstancedIndirect - // calls via Stub_DrawMultiIndexedInstancedIndirect if hardware doesn't support MDI - drawMultiIndexedInstancedIndirect( Context.Get(), - static_cast(opaqueDrawArgs.size()), - WorldMeshIndirectBuffer->GetIndirectBuffer().Get(), - 0, - sizeof( D3D11_DRAW_INDEXED_INSTANCED_INDIRECT_ARGS ) ); - } - - // Draw alpha-tested meshes with texture binding - if ( !alphaMeshes.empty() ) { - // Sort by texture to minimize binding changes - std::sort( alphaMeshes.begin(), alphaMeshes.end(), - []( const auto& a, const auto& b ) { return a.first < b.first; } ); - - ActivePS->Apply(); - zCTexture* lastTex = nullptr; - - for ( const auto& [tex, mesh] : alphaMeshes ) { - if ( tex != lastTex ) { - if (tex->CacheIn( 0.6f ) == zRES_CACHED_OUT) { - continue; - } - tex->Bind( 0 ); - lastTex = tex; - } - DrawVertexBufferIndexedUINT( nullptr, nullptr, - mesh->Indices.size(), mesh->BaseIndexLocation ); - } - } + ShadowPass_DrawWorldMesh(visibleSections); } } From 5ceb3272122ac4ac77b1a1eaf2cd7e8250e18bff Mon Sep 17 00:00:00 2001 From: kirides <13602143+kirides@users.noreply.github.com> Date: Mon, 23 Feb 2026 12:59:39 +0100 Subject: [PATCH 03/13] Introduce basic RenderGraph, begin rewrite to support such. Make most of OnStartRenderingWorld use the RenderGraph --- D3D11Engine/BaseGraphicsEngine.h | 6 +- D3D11Engine/D3D11Engine.vcxproj | 7 + D3D11Engine/D3D11Engine.vcxproj.filters | 24 + D3D11Engine/D3D11GraphicsEngine.cpp | 720 +++++++++++++++--------- D3D11Engine/D3D11GraphicsEngine.h | 27 +- D3D11Engine/D3D11NVHBAO.cpp | 10 +- D3D11Engine/D3D11NVHBAO.h | 4 +- D3D11Engine/D3D11PFX_DistanceBlur.cpp | 4 +- D3D11Engine/D3D11PFX_DistanceBlur.h | 3 +- D3D11Engine/D3D11PFX_GodRays.cpp | 17 +- D3D11Engine/D3D11PFX_GodRays.h | 3 +- D3D11Engine/D3D11PFX_HDR.cpp | 6 +- D3D11Engine/D3D11PFX_HDR.h | 3 +- D3D11Engine/D3D11PfxRenderer.cpp | 19 +- D3D11Engine/D3D11PfxRenderer.h | 11 +- D3D11Engine/D3D11ShadowMap.cpp | 293 ++++++---- D3D11Engine/D3D11ShadowMap.h | 9 +- D3D11Engine/GothicAPI.cpp | 8 +- D3D11Engine/GothicAPI.h | 5 +- D3D11Engine/RGBuilder.cpp | 17 + D3D11Engine/RGBuilder.h | 24 + D3D11Engine/RGTextureDesc.h | 17 + D3D11Engine/RenderGraph.cpp | 1 + D3D11Engine/RenderGraph.h | 163 ++++++ D3D11Engine/RenderPass.cpp | 1 + D3D11Engine/RenderPass.h | 22 + 26 files changed, 984 insertions(+), 440 deletions(-) create mode 100644 D3D11Engine/RGBuilder.cpp create mode 100644 D3D11Engine/RGBuilder.h create mode 100644 D3D11Engine/RGTextureDesc.h create mode 100644 D3D11Engine/RenderGraph.cpp create mode 100644 D3D11Engine/RenderGraph.h create mode 100644 D3D11Engine/RenderPass.cpp create mode 100644 D3D11Engine/RenderPass.h diff --git a/D3D11Engine/BaseGraphicsEngine.h b/D3D11Engine/BaseGraphicsEngine.h index 8279ca1d..971037ed 100644 --- a/D3D11Engine/BaseGraphicsEngine.h +++ b/D3D11Engine/BaseGraphicsEngine.h @@ -1,6 +1,7 @@ #pragma once #include "WorldObjects.h" #include "GraphicsEventRecord.h" +#include "RenderToTextureBuffer.h" #include "ShaderCategory.h" class BaseLineRenderer; @@ -210,7 +211,10 @@ class BaseGraphicsEngine { virtual void DrawFrameParticleMeshes( std::unordered_map& progMeshes ) {} /** Draws particle effects */ - virtual void DrawFrameParticles( std::map>& particles, std::map& info ) {} + virtual void DrawFrameParticles(std::map>& particles, + std::map& info, + RenderToTextureBuffer* bufferParticleColor, + RenderToTextureBuffer* bufferParticleDistortion) {} virtual void DrawString( const std::string& str, float x, float y, const zFont* font, zColor& fontColor ) {}; diff --git a/D3D11Engine/D3D11Engine.vcxproj b/D3D11Engine/D3D11Engine.vcxproj index e3b1d579..9790e425 100644 --- a/D3D11Engine/D3D11Engine.vcxproj +++ b/D3D11Engine/D3D11Engine.vcxproj @@ -974,7 +974,11 @@ copy "$(OutDir)$(TargetName).pdb" "$(G1_SYSTEM_PATH)\ddraw.pdb" + + + + @@ -1181,6 +1185,9 @@ copy "$(OutDir)$(TargetName).pdb" "$(G1_SYSTEM_PATH)\ddraw.pdb" + + + diff --git a/D3D11Engine/D3D11Engine.vcxproj.filters b/D3D11Engine/D3D11Engine.vcxproj.filters index c5a32b2c..7c971b32 100644 --- a/D3D11Engine/D3D11Engine.vcxproj.filters +++ b/D3D11Engine/D3D11Engine.vcxproj.filters @@ -85,6 +85,9 @@ {7b924380-794f-4471-9819-06c3fca71231} + + {e7532fd2-04b8-499d-a2a9-5b64abda0850} + @@ -791,6 +794,18 @@ + + Engine\Graph + + + Engine\Graph + + + Engine\Graph + + + Engine\Graph + @@ -1059,6 +1074,15 @@ Engine\D3D11\PFX\Effects + + Engine\Graph + + + Engine\Graph + + + Engine\Graph + diff --git a/D3D11Engine/D3D11GraphicsEngine.cpp b/D3D11Engine/D3D11GraphicsEngine.cpp index f502ecb9..d454f0a2 100644 --- a/D3D11Engine/D3D11GraphicsEngine.cpp +++ b/D3D11Engine/D3D11GraphicsEngine.cpp @@ -47,6 +47,8 @@ #include "ImGuiShim.h" #include "zCModel.h" #include "zCOption.h" +#include "RenderGraph.h" +#include "RGBuilder.h" #ifdef BUILD_SPACER #define IS_SPACER_BUILD true @@ -1088,27 +1090,6 @@ XRESULT D3D11GraphicsEngine::OnResize( INT2 newSize ) { PfxRenderer->OnResize( m_scaledResolution ); - GBuffer2_SpecIntens_SpecPower = std::make_unique( - GetDevice().Get(), m_scaledResolution.x, m_scaledResolution.y, DXGI_FORMAT_R16G16_FLOAT ); - - SetDebugName( GBuffer2_SpecIntens_SpecPower->GetTexture().Get(), "GBuffer2_SpecIntens_SpecPower->TEX" ); - SetDebugName( GBuffer2_SpecIntens_SpecPower->GetShaderResView().Get(), "GBuffer2_SpecIntens_SpecPower->SRV" ); - SetDebugName( GBuffer2_SpecIntens_SpecPower->GetRenderTargetView().Get(), "GBuffer2_SpecIntens_SpecPower->RTV" ); - - GBuffer1_Normals = std::make_unique( - GetDevice().Get(), m_scaledResolution.x, m_scaledResolution.y, DXGI_FORMAT_R8G8B8A8_SNORM ); - - SetDebugName( GBuffer1_Normals->GetTexture().Get(), "GBuffer1_Normals->TEX" ); - SetDebugName( GBuffer1_Normals->GetShaderResView().Get(), "GBuffer1_Normals->SRV" ); - SetDebugName( GBuffer1_Normals->GetRenderTargetView().Get(), "GBuffer1_Normals->RTV" ); - - GBuffer0_Diffuse = std::make_unique( - GetDevice().Get(), m_scaledResolution.x, m_scaledResolution.y, DXGI_FORMAT_ENGINE_SWAPCHAIN ); - - SetDebugName( GBuffer0_Diffuse->GetTexture().Get(), "GBuffer0_Diffuse->TEX" ); - SetDebugName( GBuffer0_Diffuse->GetShaderResView().Get(), "GBuffer0_Diffuse->SRV" ); - SetDebugName( GBuffer0_Diffuse->GetRenderTargetView().Get(), "GBuffer0_Diffuse->RTV" ); - VelocityBuffer = std::make_unique( GetDevice().Get(), m_scaledResolution.x, m_scaledResolution.y, DXGI_FORMAT_R16G16_FLOAT ); @@ -1363,14 +1344,9 @@ XRESULT D3D11GraphicsEngine::Clear( const float4& color ) { context->ClearDepthStencilView( DepthStencilBuffer->GetDepthStencilView().Get(), D3D11_CLEAR_DEPTH, 0, 0 ); context->ClearDepthStencilView( m_NativeSizeDepthStencil->GetDepthStencilView().Get(), D3D11_CLEAR_DEPTH, 0, 0 ); - context->ClearRenderTargetView( GBuffer0_Diffuse->GetRenderTargetView().Get(), reinterpret_cast(&color) ); - context->ClearRenderTargetView( GBuffer1_Normals->GetRenderTargetView().Get(), reinterpret_cast(&float4( 0, 0, 0, 0 )) ); - context->ClearRenderTargetView( GBuffer2_SpecIntens_SpecPower->GetRenderTargetView().Get(), reinterpret_cast(&float4( 0, 0, 0, 0 )) ); context->ClearRenderTargetView( HDRBackBuffer->GetRenderTargetView().Get(), reinterpret_cast(&float4( 0, 0, 0, 0 )) ); context->ClearRenderTargetView( Backbuffer->GetRenderTargetView().Get(), reinterpret_cast(&float4( 0, 0, 0, 0 )) ); - // remove motion information - context->ClearRenderTargetView( VelocityBuffer->GetRenderTargetView().Get(), reinterpret_cast(&float4( 0, 0, 0, 0 )) ); return XR_SUCCESS; } @@ -2650,6 +2626,12 @@ XRESULT D3D11GraphicsEngine::OnStartWorldRendering() { // return XR_SUCCESS; if ( PresentPending ) return XR_SUCCESS; + RenderGraph graph( GetPfxRenderer()->GetTexturePool() ); + + // TODO: Replace global Resources with RenderGraph resource + RGResourceHandle backBufferHandle = graph.ImportResource( L"BackBuffer", HDRBackBuffer.get() ); + RGResourceHandle velocityBufferHandle = graph.ImportResource( L"VelocityBuffer", VelocityBuffer.get() ); + rendererState.RendererInfo.RenderStage = STAGE_DRAW_WORLD; D3D11_VIEWPORT vp; @@ -2661,6 +2643,7 @@ XRESULT D3D11GraphicsEngine::OnStartWorldRendering() { vp.Height = static_cast(GetResolution().y); GetContext()->RSSetViewports( 1, &vp ); + UpdateZEngineViewport(); GetContext()->OMSetRenderTargets( 1, HDRBackBuffer->GetRenderTargetView().GetAddressOf(), nullptr ); @@ -2696,18 +2679,9 @@ XRESULT D3D11GraphicsEngine::OnStartWorldRendering() { rendererState.RendererSettings.DrawSkeletalMeshes = bDrawVobsGlobal; #endif - ID3D11RenderTargetView* rtvs[] = { - GBuffer0_Diffuse->GetRenderTargetView().Get(), - GBuffer1_Normals->GetRenderTargetView().Get(), - GBuffer2_SpecIntens_SpecPower->GetRenderTargetView().Get(), - VelocityBuffer->GetRenderTargetView().Get() }; - GetContext()->OMSetRenderTargets( 4, rtvs, DepthStencilBuffer->GetDepthStencilView().Get() ); - - Engine::GAPI->SetFarPlane( - rendererState.RendererSettings.SectionDrawRadius * WORLD_SECTION_SIZE ); - - Clear( float4( rendererState.GraphicsState.FF_FogColor, 0.0f ) ); + Engine::GAPI->SetFarPlane( rendererState.RendererSettings.SectionDrawRadius * WORLD_SECTION_SIZE ); + // Clear textures from the last frame RenderedVobs.clear(); FrameWaterSurfaces.clear(); @@ -2733,163 +2707,380 @@ XRESULT D3D11GraphicsEngine::OnStartWorldRendering() { rendererState.RasterizerState.FrontCounterClockwise = false; rendererState.RasterizerState.SetDirty(); + RGResourceHandle colorResource; + graph.AddPass( L"Initialize Buffers", [&]( RGBuilder& builder, RenderPass& pass ) { + auto size = GetResolution(); + colorResource = builder.CreateTexture( { static_cast(size.x), static_cast(size.y), DXGI_FORMAT_ENGINE_DEFAULT, L"GBufferAlbedo" } ); + + builder.Write( colorResource ); + builder.Write( backBufferHandle ); + + pass.m_executeCallback = [this, &rendererState, colorResource](const RenderGraph& graph)->void { + const Microsoft::WRL::ComPtr& context = GetContext(); + context->ClearDepthStencilView( DepthStencilBuffer->GetDepthStencilView().Get(), D3D11_CLEAR_DEPTH, 0, 0 ); + context->ClearDepthStencilView( m_NativeSizeDepthStencil->GetDepthStencilView().Get(), D3D11_CLEAR_DEPTH, 0, 0 ); + + context->ClearRenderTargetView( HDRBackBuffer->GetRenderTargetView().Get(), reinterpret_cast(&float4( 0, 0, 0, 0 )) ); + context->ClearRenderTargetView( Backbuffer->GetRenderTargetView().Get(), reinterpret_cast(&float4( 0, 0, 0, 0 )) ); + + constexpr float black[]{ 0.f, 0.f, 0.f, 0.f }; + float4 fogColor( rendererState.GraphicsState.FF_FogColor, 0.0f ); + GetContext()->ClearRenderTargetView( graph.GetPhysicalTexture( colorResource )->GetRenderTargetView().Get(), reinterpret_cast(&fogColor) ); + }; + }); + if ( rendererState.RendererSettings.DrawSky ) { - auto _ = RecordGraphicsEvent( L"Draw Sky" ); - // Draw back of the sky if outdoor - DrawSky(); - } + graph.AddPass( L"Draw Sky", [&]( RGBuilder& builder, RenderPass& pass ) { + //// Setup / Declare + //RGTextureDesc albedoDesc{ 1920, 1080, 28 /* DXGI_FORMAT_R8G8B8A8_UNORM */, "Albedo" }; + //albedoTarget = builder.CreateTexture( albedoDesc ); + builder.Write( colorResource ); + + pass.m_executeCallback = [this, colorResource](const RenderGraph& graph)->void { + // Draw back of the sky if outdoor + GetContext()->OMSetRenderTargets( 1, graph.GetPhysicalTexture( colorResource )->GetRenderTargetView().GetAddressOf(), nullptr ); + + DrawSky(); + }; + }); + } + + RGResourceHandle normalsResource; + RGResourceHandle specularResource; + graph.AddPass( L"G-Buffer Pass", [&]( RGBuilder& builder, RenderPass& pass ) { + // Setup / Declare + auto size = GetResolution(); + normalsResource = builder.CreateTexture({ static_cast(size.x), static_cast(size.y), DXGI_FORMAT_R8G8B8A8_SNORM, L"GBufferNormals" }); + specularResource = builder.CreateTexture({ static_cast(size.x), static_cast(size.y), DXGI_FORMAT_R16G16_FLOAT, L"GBufferSpecular" }); + + builder.Write( colorResource ); + builder.Write( normalsResource ); + builder.Write( specularResource ); + builder.Write( velocityBufferHandle ); + builder.Write( backBufferHandle ); + + pass.m_executeCallback = [this, colorResource, normalsResource, specularResource](const RenderGraph& graph)-> void { + ID3D11RenderTargetView* rtvs[] = { + graph.GetPhysicalTexture(colorResource)->GetRenderTargetView().Get(), + graph.GetPhysicalTexture(normalsResource)->GetRenderTargetView().Get(), + graph.GetPhysicalTexture(specularResource)->GetRenderTargetView().Get(), + VelocityBuffer->GetRenderTargetView().Get(), + }; + + constexpr float black[] { 0.f, 0.f, 0.f, 0.f }; + GetContext()->ClearRenderTargetView( rtvs[1], black ); + GetContext()->ClearRenderTargetView( rtvs[2], black ); + GetContext()->ClearRenderTargetView( rtvs[3], black ); + GetContext()->OMSetRenderTargets( 4, rtvs, DepthStencilBuffer->GetDepthStencilView().Get() ); + + Engine::GAPI->DrawWorldMeshNaive(); + }; + }); + + graph.AddPass( L"Draw ParticleFX", [&]( RGBuilder& builder, RenderPass& pass ) { + // Setup / Declare + builder.Write( backBufferHandle ); - // Draw world - { - auto _ = RecordGraphicsEvent( L"Draw WorldMesh Naive" ); - Engine::GAPI->DrawWorldMeshNaive(); - } + pass.m_executeCallback = [this](const RenderGraph&)-> void { + if ( !Engine::GAPI->GetRendererState().RendererSettings.DrawParticleEffects ) { + return; + } + std::vector decals; + zCCamera::GetCamera()->Activate(); + Engine::GAPI->GetVisibleDecalList( decals ); - // Draw HBAO - if ( rendererState.RendererSettings.HbaoSettings.Enabled ) { - auto _ = RecordGraphicsEvent( L"Draw HBAO" ); - PfxRenderer->DrawHBAO( HDRBackBuffer->GetRenderTargetView() ); - GetContext()->PSSetSamplers( 0, 1, DefaultSamplerState.GetAddressOf() ); - } + Engine::GAPI->ResetRenderStates(); + DrawDecalList( decals, true ); + DrawQuadMarks(); + }; + }); + + graph.AddPass( L"Draw Lighting", [&]( RGBuilder& builder, RenderPass& pass ) { + // Setup / Declare + builder.Read( colorResource ); + builder.Read( normalsResource ); + builder.Read( specularResource ); + builder.Write( backBufferHandle ); + + pass.m_executeCallback = [this, colorResource, normalsResource, specularResource](const RenderGraph& graph)-> void { + if ( !Engine::GAPI->GetRendererState().RendererSettings.EnableShadows ) { + return; + } + auto colorTexture = graph.GetPhysicalTexture(colorResource); + auto normalsTexture = graph.GetPhysicalTexture(normalsResource); + auto specularTexture = graph.GetPhysicalTexture(specularResource); + + ShadowMaps->PrepareRender(); + ShadowMaps->DrawLighting(m_FrameLights, + *colorTexture, + *normalsTexture, + *specularTexture, + *GetDepthBufferCopy()); + if ( !Engine::GAPI->GetRendererState().RendererSettings.FixViewFrustum ) { + m_FrameLights.clear(); + } + }; + }); - // PfxRenderer->RenderDistanceBlur(); + graph.AddPass( L"Draw Frame AlphaMeshes", [&]( RGBuilder& builder, RenderPass& pass ) { + // Setup / Declare + builder.Write( backBufferHandle ); - // Draw water surfaces of current frame - { - auto _ = RecordGraphicsEvent( L"DrawWaterSurfaces" ); - DrawWaterSurfaces(); - } + pass.m_executeCallback = [this](const RenderGraph&)-> void { + DrawFrameAlphaMeshes(); + }; + } + ); - // Draw light-shafts - { - auto _ = RecordGraphicsEvent( L"Draw light-shafts" ); - DrawMeshInfoListAlphablended( FrameTransparencyMeshes ); - } + // Draw HBAO + if ( rendererState.RendererSettings.HbaoSettings.Enabled ) { + graph.AddPass( L"HBAO+", [&]( RGBuilder& builder, RenderPass& pass ) { + builder.Read( normalsResource ); + builder.Write( backBufferHandle ); - //draw forest / door portals - if ( rendererState.RendererSettings.DrawG1ForestPortals ) { - auto _ = RecordGraphicsEvent( L"DrawForestPortals" ); - DrawMeshInfoListAlphablended( FrameTransparencyMeshesPortal ); - } + pass.m_executeCallback = [this, normalsResource](const RenderGraph& graph) { + auto normalsTexture = graph.GetPhysicalTexture(normalsResource); - //draw waterfall foam - { - auto _ = RecordGraphicsEvent( L"Draw Waterfall Foam" ); - DrawMeshInfoListAlphablended( FrameTransparencyMeshesWaterfall ); + PfxRenderer->DrawHBAO( HDRBackBuffer->GetRenderTargetView(), + GetDepthBuffer()->GetShaderResView(), + normalsTexture->GetShaderResView()); + GetContext()->PSSetSamplers( 0, 1, DefaultSamplerState.GetAddressOf() ); + }; + }); } - // Draw ghosts - { - auto _ = RecordGraphicsEvent( L"Draw ghosts" ); - D3D11ENGINE_RENDER_STAGE oldStage = RenderingStage; - SetRenderingStage( DES_GHOST ); - Engine::GAPI->DrawTransparencyVobs(); - SetRenderingStage( oldStage ); - Engine::GAPI->DrawSkeletalVN(); - } + + graph.AddPass( L"DrawWaterSurfaces", [&]( RGBuilder& builder, RenderPass& pass ) { + builder.Read( backBufferHandle ); + builder.Write( backBufferHandle ); - if ( rendererState.RendererSettings.DrawFog && - Engine::GAPI->GetLoadedWorldInfo()->BspTree->GetBspTreeMode() == - zBSP_MODE_OUTDOOR ) { + pass.m_executeCallback = [this](const RenderGraph&) { + DrawWaterSurfaces(); + }; + }); + + graph.AddPass( L"Draw light-shafts", [&]( RGBuilder& builder, RenderPass& pass ) { + builder.Read( backBufferHandle ); + builder.Write( backBufferHandle ); - auto _ = RecordGraphicsEvent( L"RenderHeightfog" ); - PfxRenderer->RenderHeightfog(); + pass.m_executeCallback = [this](const RenderGraph&) { + DrawMeshInfoListAlphablended( FrameTransparencyMeshes ); + }; + }); + + if ( rendererState.RendererSettings.DrawG1ForestPortals ) { + graph.AddPass( L"Draw ForestPortals", [&]( RGBuilder& builder, RenderPass& pass ) { + builder.Read( backBufferHandle ); + builder.Write( backBufferHandle ); + + pass.m_executeCallback = [this](const RenderGraph&) { + DrawMeshInfoListAlphablended( FrameTransparencyMeshesPortal ); + }; + }); } + + graph.AddPass( L"Draw Waterfall Foam", [&]( RGBuilder& builder, RenderPass& pass ) { + builder.Read( backBufferHandle ); + builder.Write( backBufferHandle ); - // Draw rain - if ( Engine::GAPI->GetRainFXWeight() > 0.0f ) { - if ( FeatureLevel10Compatibility || rendererState.RendererSettings.DrawRainThroughTransformFeedback ) { - auto _ = RecordGraphicsEvent( L"DrawRain" ); - Effects->DrawRain(); + pass.m_executeCallback = [this](const RenderGraph&) { + DrawMeshInfoListAlphablended( FrameTransparencyMeshesWaterfall ); + }; + }); + + graph.AddPass( L"Draw ghosts", [&]( RGBuilder& builder, RenderPass& pass ) { + builder.Read( backBufferHandle ); + builder.Write( backBufferHandle ); + + pass.m_executeCallback = [this](const RenderGraph&) { + D3D11ENGINE_RENDER_STAGE oldStage = RenderingStage; + SetRenderingStage( DES_GHOST ); + Engine::GAPI->DrawTransparencyVobs(); + SetRenderingStage( oldStage ); + Engine::GAPI->DrawSkeletalVN(); + }; + }); + + if (rendererState.RendererSettings.DrawFog && + Engine::GAPI->GetLoadedWorldInfo()->BspTree->GetBspTreeMode() == + zBSP_MODE_OUTDOOR) { + graph.AddPass( L"Draw Heightfog", [&]( RGBuilder& builder, RenderPass& pass ) { + builder.Read( backBufferHandle ); + builder.Write( backBufferHandle ); + + pass.m_executeCallback = [this](const RenderGraph&) { + PfxRenderer->RenderHeightfog(); + }; + }); + } + + if (Engine::GAPI->GetRainFXWeight() > 0.0f) { + if ( FeatureLevel10Compatibility || Engine::GAPI->GetRendererState().RendererSettings.DrawRainThroughTransformFeedback ) { + graph.AddPass( L"Draw Rain", [&]( RGBuilder& builder, RenderPass& pass ) { + builder.Read( backBufferHandle ); + builder.Write( backBufferHandle ); + + pass.m_executeCallback = [this](const RenderGraph&) { + Effects->DrawRain(); + }; + }); } else { - auto _ = RecordGraphicsEvent( L"DrawRain_CS" ); - Effects->DrawRain_CS(); + graph.AddPass( L"Draw Rain CS", [&]( RGBuilder& builder, RenderPass& pass ) { + builder.Read( backBufferHandle ); + builder.Write( backBufferHandle ); + + pass.m_executeCallback = [this](const RenderGraph&) { + Effects->DrawRain_CS(); + }; + }); } } - - GetContext()->OMSetRenderTargets( 1, HDRBackBuffer->GetRenderTargetView().GetAddressOf(), - DepthStencilBuffer->GetDepthStencilView().Get() ); - - // Draw unlit decals - // TODO: Only get them once! - if ( rendererState.RendererSettings.DrawParticleEffects ) { - auto _ = RecordGraphicsEvent( L"DrawParticleEffects" ); - std::vector decals; - zCCamera::GetCamera()->Activate(); - Engine::GAPI->GetVisibleDecalList( decals ); - - // Draw stuff like candle-flames - Engine::GAPI->ResetRenderStates(); - DrawDecalList( decals, false ); - DrawMQuadMarks(); + + graph.AddPass( L"Reset RenderTargets", [&]( RGBuilder& builder, RenderPass& pass ) + { + builder.Write( backBufferHandle ); + pass.m_executeCallback = [this](const RenderGraph&) { + GetContext()->OMSetRenderTargets( 1, HDRBackBuffer->GetRenderTargetView().GetAddressOf(), + DepthStencilBuffer->GetDepthStencilView().Get() ); + }; + }); + + if (rendererState.RendererSettings.DrawParticleEffects) { + graph.AddPass( L"Draw ParticleFX", [&]( RGBuilder& builder, RenderPass& pass ) { + builder.Read( backBufferHandle ); + builder.Write( backBufferHandle ); + + pass.m_executeCallback = [this](const RenderGraph&) { + // Draw unlit decals + // TODO: Only get them once! + std::vector decals; + zCCamera::GetCamera()->Activate(); + Engine::GAPI->GetVisibleDecalList( decals ); + + // Draw stuff like candle-flames + DrawDecalList( decals, false ); + DrawMQuadMarks(); + }; + }); } - - // Unbind temporary backbuffer copy - Microsoft::WRL::ComPtr srv; - GetContext()->PSSetShaderResources( 5, 1, srv.GetAddressOf() ); - - // TODO: TODO: GodRays need the GBuffer1 from the scene, but Particles need to - // clear it! - if ( rendererState.RendererSettings.EnableGodRays && + + if (rendererState.RendererSettings.EnableGodRays && Engine::GAPI->GetLoadedWorldInfo()->BspTree->GetBspTreeMode() == - zBSP_MODE_OUTDOOR ) { - - auto _ = RecordGraphicsEvent( L"RenderGodRays" ); - PfxRenderer->RenderGodRays(); + zBSP_MODE_OUTDOOR) { + graph.AddPass( L"Draw Godrays", [&]( RGBuilder& builder, RenderPass& pass ) { + builder.Read( normalsResource ); + builder.Read( backBufferHandle ); + builder.Write( backBufferHandle ); + + pass.m_executeCallback = [this, backBufferHandle, normalsResource](const RenderGraph& graph) { + // Unbind temporary backbuffer copy + Microsoft::WRL::ComPtr srv; + GetContext()->PSSetShaderResources( 5, 1, srv.GetAddressOf() ); + + auto backbufferResource = graph.GetPhysicalTexture(backBufferHandle); + auto normalsTexture = graph.GetPhysicalTexture(normalsResource); + + PfxRenderer->RenderGodRays(backbufferResource->GetShaderResView().Get(), normalsTexture->GetShaderResView().Get()); + }; + }); } + + graph.AddPass( L"Draw ParticlesSimple", [&]( RGBuilder& builder, RenderPass& pass ) { + auto size = GetResolution(); - Engine::GAPI->ResetRenderStates(); - // DrawParticleEffects(); - { - auto _ = RecordGraphicsEvent( L"DrawParticlesSimple" ); - Engine::GAPI->DrawParticlesSimple(); - } + auto particleColorHandle = builder.CreateTexture( { static_cast(size.x), static_cast(size.y), DXGI_FORMAT_ENGINE_DEFAULT, L"PfxColor" } ); + auto particleDistortionHandle = builder.CreateTexture({ static_cast(size.x), static_cast(size.y), DXGI_FORMAT_R8G8B8A8_SNORM, L"PfxDistortion" }); + + builder.Write( particleColorHandle ); + builder.Write( particleDistortionHandle ); + builder.Read( particleColorHandle ); + builder.Read( particleDistortionHandle ); + builder.Write( backBufferHandle ); + + pass.m_executeCallback = [particleColorHandle, particleDistortionHandle](const RenderGraph& graph) { + Engine::GAPI->ResetRenderStates(); + Engine::GAPI->DrawParticlesSimple( + graph.GetPhysicalTexture( particleColorHandle ), + graph.GetPhysicalTexture( particleDistortionHandle ) ); + }; + }); #if (defined BUILD_GOTHIC_2_6_fix || defined BUILD_GOTHIC_1_08k) - // Calc weapon/effect trail mesh data - Engine::GAPI->CalcPolyStripMeshes(); - // Calc lightning flashes mesh data - Engine::GAPI->CalcFlashMeshes(); - // Draw those - { - auto _ = RecordGraphicsEvent( L"DrawPolyStrips" ); - // For some reasons the viewport gets messed up, so set it again - SetViewport( ViewportInfo( 0, 0, GetResolution().x, GetResolution().y)); - DrawPolyStrips(); - } + + graph.AddPass( L"Draw PolyStrips", [&]( RGBuilder& builder, RenderPass& pass ) { + builder.Write( backBufferHandle ); + + pass.m_executeCallback = [this](const RenderGraph&) { + // Calc weapon/effect trail mesh data + Engine::GAPI->CalcPolyStripMeshes(); + // Calc lightning flashes mesh data + Engine::GAPI->CalcFlashMeshes(); + // Draw those + // For some reasons the viewport gets messed up, so set it again + SetViewport( ViewportInfo( 0, 0, GetResolution().x, GetResolution().y ) ); + DrawPolyStrips(); + }; + } ); + #endif // Draw debug lines - { - auto _ = RecordGraphicsEvent( L"Draw Debug Lines" ); - LineRenderer->Flush(); - LineRenderer->FlushScreenSpace(); - } + graph.AddPass( L"Draw Debug Lines", [&]( RGBuilder& builder, RenderPass& pass ) { + builder.Write( backBufferHandle ); + + pass.m_executeCallback = [this](const RenderGraph&) { + LineRenderer->Flush(); + LineRenderer->FlushScreenSpace(); + }; + } ); + if ( rendererState.RendererSettings.AntiAliasingMode == GothicRendererSettings::AA_TAA ) { // TAA before any HDR stuff - auto _ = RecordGraphicsEvent( L"RenderTAA" ); - PfxRenderer->RenderTAA( rendererState.RendererSettings.DebugSettings.TAA.DepthMotionVectors - ? nullptr - : VelocityBuffer->GetShaderResView() ); - GetContext()->PSSetSamplers( 0, 1, DefaultSamplerState.GetAddressOf() ); + graph.AddPass( L"Render TAA", [&]( RGBuilder& builder, RenderPass& pass ) { + builder.Read( velocityBufferHandle ); + builder.Write( backBufferHandle ); + + pass.m_executeCallback = [this, &rendererState, velocityBufferHandle](const RenderGraph& graph) { + auto velocityBufferTex = graph.GetPhysicalTexture( velocityBufferHandle ); + PfxRenderer->RenderTAA( rendererState.RendererSettings.DebugSettings.TAA.DepthMotionVectors + ? nullptr + : velocityBufferTex->GetShaderResView() ); + GetContext()->PSSetSamplers( 0, 1, DefaultSamplerState.GetAddressOf() ); + }; + } ); } - if ( rendererState.RendererSettings.EnableHDR ) { - auto _ = RecordGraphicsEvent( L"RenderHDR" ); - PfxRenderer->RenderHDR(); + if ( rendererState.RendererSettings.EnableHDR ) { + graph.AddPass( L"Render HDR", [&]( RGBuilder& builder, RenderPass& pass ) { + builder.Read( backBufferHandle ); + builder.Write( backBufferHandle ); + + pass.m_executeCallback = [this, backBufferHandle](const RenderGraph& graph) { + auto backbufferTex = graph.GetPhysicalTexture( backBufferHandle ); + PfxRenderer->RenderHDR( backbufferTex->GetRenderTargetView().Get(), backbufferTex->GetShaderResView().Get() ); + }; + } ); } - // SMAA should be applied before any sharpening if ( rendererState.RendererSettings.AntiAliasingMode - == GothicRendererSettings::AA_SMAA ) { - // actually we could do TAA + SMAA - auto _ = RecordGraphicsEvent( L"RenderSMAA" ); - PfxRenderer->RenderSMAA(); - GetContext()->PSSetSamplers( 0, 1, DefaultSamplerState.GetAddressOf() ); + == GothicRendererSettings::AA_SMAA ) { + // SMAA should be applied before any sharpening + graph.AddPass( L"Render SMAA", [&]( RGBuilder& builder, RenderPass& pass ) { + builder.Read( backBufferHandle ); + builder.Write( backBufferHandle ); + + pass.m_executeCallback = [this, backBufferHandle](const RenderGraph&) { + PfxRenderer->RenderSMAA(); + GetContext()->PSSetSamplers( 0, 1, DefaultSamplerState.GetAddressOf() ); + }; + } ); } + graph.Compile(); + graph.Execute(); + PresentPending = true; // Set viewport for gothics rendering @@ -4820,24 +5011,12 @@ void D3D11GraphicsEngine::ApplyWindProps( VS_ExConstantBuffer_Wind& windBuff ) { /** Draws the static vobs instanced */ XRESULT D3D11GraphicsEngine::DrawVOBsInstanced() { static std::vector vobs; - static std::vector lights; static std::vector mobs; const auto& renderSettings = Engine::GAPI->GetRendererState().RendererSettings; - if ( RenderingStage == DES_MAIN && renderSettings.EnableShadows ) { - auto _ = START_TIMING( "Prepare Shadow" ); - ShadowMaps->PrepareRender(); // calculate cameras, frusti collect any vobs needed, etc. - } - // Need to collect alpha-meshes to render them laterdy - struct alphaMeshData { - MeshKey mk; - MeshInfo* mi; - MeshVisualInfo* vi; - std::vector instances; - }; - std::vector AlphaMeshes; - AlphaMeshes.reserve( 64 ); + // Need to collect alpha-meshes to render them later + m_AlphaMeshes.reserve( 64 ); { auto _ = START_TIMING( "VOBs" ); @@ -4883,7 +5062,7 @@ XRESULT D3D11GraphicsEngine::DrawVOBsInstanced() { if ( !renderSettings.FixViewFrustum || (renderSettings.FixViewFrustum && vobs.empty()) ) { - Engine::GAPI->CollectVisibleVobs( vobs, lights, mobs ); + Engine::GAPI->CollectVisibleVobs( vobs, m_FrameLights, mobs ); } } @@ -4990,7 +5169,7 @@ XRESULT D3D11GraphicsEngine::DrawVOBsInstanced() { if ( !doReset || blendAdd || blendBlend ) { MeshVisualInfo* info = staticMeshVisual; for ( MeshInfo* mesh : mlist ) { - alphaMeshData data = {}; + AlphaMeshData data = {}; data.mk = itt.first; data.vi = info; data.mi = mesh; @@ -4999,7 +5178,7 @@ XRESULT D3D11GraphicsEngine::DrawVOBsInstanced() { // and the only usage i found was a bug (?) in pirates camp // where there would be cobwebs randomly. data.instances = staticMeshVisual->Instances; - AlphaMeshes.emplace_back( data ); + m_AlphaMeshes.emplace_back( data ); } doReset = false; @@ -5137,23 +5316,17 @@ XRESULT D3D11GraphicsEngine::DrawVOBsInstanced() { } } - if ( RenderingStage == DES_MAIN ) { - if ( renderSettings.DrawParticleEffects ) { - auto _ = START_TIMING( "DrawVOBsInstanced->DrawParticleEffects" ); - std::vector decals; - zCCamera::GetCamera()->Activate(); - Engine::GAPI->GetVisibleDecalList( decals ); - - DrawDecalList( decals, true ); - DrawQuadMarks(); - } - - auto _ = START_TIMING( "DrawVOBsInstanced->Lighting" ); - // Draw lighting, since everything is drawn by now and we have the lights - // here - DrawLighting( lights ); + if ( !renderSettings.FixViewFrustum ) { + vobs.clear(); + mobs.clear(); } + return XR_SUCCESS; +} + +/** Draws the static VOBs */ +XRESULT D3D11GraphicsEngine::DrawFrameAlphaMeshes() +{ // Make sure lighting doesn't mess up our state SetDefaultStates(); @@ -5165,79 +5338,70 @@ XRESULT D3D11GraphicsEngine::DrawVOBsInstanced() { GetContext()->OMSetRenderTargets( 1, HDRBackBuffer->GetRenderTargetView().GetAddressOf(), DepthStencilBuffer->GetDepthStencilView().Get() ); + + // re-setup dynamic instancing buffer with correct instances and values + // shadow pass breaks the "global" state of this. + + byte* data; + UINT size; + UINT loc = 0; + DynamicInstancingBuffer->Map( D3D11VertexBuffer::M_WRITE_DISCARD, + reinterpret_cast(&data), &size ); + for ( auto const& alphaData : m_AlphaMeshes ) { + alphaData.vi->StartInstanceNum = loc; + memcpy( data + loc * sizeof( VobInstanceInfo ), &alphaData.instances[0], + sizeof( VobInstanceInfo ) * alphaData.instances.size() ); + loc += alphaData.instances.size(); + } + DynamicInstancingBuffer->Unmap(); + + for ( auto const& alphaMesh : m_AlphaMeshes ) { + const MeshKey& mk = alphaMesh.mk; + zCTexture* tx = mk.Material->GetAniTexture(); + if ( !tx ) continue; - { - auto _1 = Engine::GraphicsEngine->RecordGraphicsEvent( L"DrawVOBsInstanced->AlphaMeshes" ); + // Check for alphablending on world mesh + bool blendAdd = mk.Material->GetAlphaFunc() == zMAT_ALPHA_FUNC_ADD; + bool blendBlend = mk.Material->GetAlphaFunc() == zMAT_ALPHA_FUNC_BLEND; - // re-setup dynamic instancing buffer with correct instances and values - // shadow pass breaks the "global" state of this. + // Bind texture + MeshInfo* mi = alphaMesh.mi; + MeshVisualInfo* vi = alphaMesh.vi; + auto& instances = alphaMesh.instances; - byte* data; - UINT size; - UINT loc = 0; - DynamicInstancingBuffer->Map( D3D11VertexBuffer::M_WRITE_DISCARD, - reinterpret_cast(&data), &size ); - for ( auto const& alphaData : AlphaMeshes ) { - alphaData.vi->StartInstanceNum = loc; - memcpy( data + loc * sizeof( VobInstanceInfo ), &alphaData.instances[0], - sizeof( VobInstanceInfo ) * alphaData.instances.size() ); - loc += alphaData.instances.size(); - } - DynamicInstancingBuffer->Unmap(); + if ( tx->CacheIn( 0.6f ) == zRES_CACHED_IN ) { + MyDirectDrawSurface7* surface = tx->GetSurface(); + ID3D11ShaderResourceView* srv[3]; - for ( auto const& alphaMesh : AlphaMeshes ) { - const MeshKey& mk = alphaMesh.mk; - zCTexture* tx = mk.Material->GetAniTexture(); - if ( !tx ) continue; + // Get diffuse and normalmap + srv[0] = surface->GetEngineTexture()->GetShaderResourceView().Get(); + srv[1] = surface->GetNormalmap() + ? surface->GetNormalmap()->GetShaderResourceView().Get() + : nullptr; + srv[2] = surface->GetFxMap() + ? surface->GetFxMap()->GetShaderResourceView().Get() + : nullptr; - // Check for alphablending on world mesh - bool blendAdd = mk.Material->GetAlphaFunc() == zMAT_ALPHA_FUNC_ADD; - bool blendBlend = mk.Material->GetAlphaFunc() == zMAT_ALPHA_FUNC_BLEND; - - // Bind texture - MeshInfo* mi = alphaMesh.mi; - MeshVisualInfo* vi = alphaMesh.vi; - auto& instances = alphaMesh.instances; - - if ( tx->CacheIn( 0.6f ) == zRES_CACHED_IN ) { - MyDirectDrawSurface7* surface = tx->GetSurface(); - ID3D11ShaderResourceView* srv[3]; - - // Get diffuse and normalmap - srv[0] = surface->GetEngineTexture()->GetShaderResourceView().Get(); - srv[1] = surface->GetNormalmap() - ? surface->GetNormalmap()->GetShaderResourceView().Get() - : nullptr; - srv[2] = surface->GetFxMap() - ? surface->GetFxMap()->GetShaderResourceView().Get() - : nullptr; - - // Bind both - GetContext()->PSSetShaderResources( 0, 3, srv ); - - if ( (blendAdd || blendBlend) && - !Engine::GAPI->GetRendererState().BlendState.BlendEnabled ) { - if ( blendAdd ) - Engine::GAPI->GetRendererState().BlendState.SetAdditiveBlending(); - else if ( blendBlend ) - Engine::GAPI->GetRendererState().BlendState.SetAlphaBlending(); - - Engine::GAPI->GetRendererState().BlendState.SetDirty(); - - Engine::GAPI->GetRendererState().DepthState.DepthWriteEnabled = false; - Engine::GAPI->GetRendererState().DepthState.SetDirty(); + // Bind both + GetContext()->PSSetShaderResources( 0, 3, srv ); - UpdateRenderStates(); - } + if ( (blendAdd || blendBlend) && + !Engine::GAPI->GetRendererState().BlendState.BlendEnabled ) { + if ( blendAdd ) + Engine::GAPI->GetRendererState().BlendState.SetAdditiveBlending(); + else if ( blendBlend ) + Engine::GAPI->GetRendererState().BlendState.SetAlphaBlending(); - MaterialInfo* info = mk.Info; - if ( !info->Constantbuffer ) info->UpdateConstantbuffer(); + Engine::GAPI->GetRendererState().BlendState.SetDirty(); - info->Constantbuffer->BindToPixelShader( 2 ); + Engine::GAPI->GetRendererState().DepthState.DepthWriteEnabled = false; + Engine::GAPI->GetRendererState().DepthState.SetDirty(); + + UpdateRenderStates(); } - g_windBuffer.minHeight = vi->BBox.Min.y; - g_windBuffer.maxHeight = vi->BBox.Max.y; + MaterialInfo* info = mk.Info; + if ( !info->Constantbuffer ) info->UpdateConstantbuffer(); if ( ActiveVS ) { ActiveVS->GetConstantBuffer()[1]->UpdateBuffer( &g_windBuffer ); @@ -5252,19 +5416,28 @@ XRESULT D3D11GraphicsEngine::DrawVOBsInstanced() { // Reset visual vi->StartNewFrame(); } - AlphaMeshes.clear(); - } - if ( !renderSettings.FixViewFrustum ) { - lights.clear(); - vobs.clear(); - mobs.clear(); - } + g_windBuffer.minHeight = vi->BBox.Min.y; + g_windBuffer.maxHeight = vi->BBox.Max.y; + if ( ActiveVS ) { + ActiveVS->GetConstantBuffer()[1]->UpdateBuffer( &g_windBuffer ); + } + + // Draw batch + DrawInstanced( mi->MeshVertexBuffer.get(), mi->MeshIndexBuffer.get(), mi->Indices.size(), + DynamicInstancingBuffer.get(), sizeof( VobInstanceInfo ), + instances.size(), sizeof( ExVertexStruct ), + vi->StartInstanceNum ); + + // Reset visual + vi->StartNewFrame(); + } + m_AlphaMeshes.clear(); + return XR_SUCCESS; } -/** Draws the static VOBs */ XRESULT D3D11GraphicsEngine::DrawVOBs( bool noTextures ) { return DrawVOBsInstanced(); } @@ -5554,11 +5727,6 @@ BaseLineRenderer* D3D11GraphicsEngine::GetLineRenderer() { return LineRenderer.get(); } -/** Applys the lighting to the scene */ -XRESULT D3D11GraphicsEngine::DrawLighting( std::vector& lights ) { - return ShadowMaps->DrawLighting(lights); -} - /** Renders the shadowmaps for a pointlight */ void XM_CALLCONV D3D11GraphicsEngine::RenderShadowCube( FXMVECTOR position, float range, @@ -6319,7 +6487,9 @@ void D3D11GraphicsEngine::DrawFrameParticleMeshes( std::unordered_map>& particles, - std::map& info ) { + std::map& info, + RenderToTextureBuffer* bufferParticleColor, + RenderToTextureBuffer* bufferParticleDistortion ) { if ( particles.empty() ) return; SetDefaultStates(); @@ -6330,8 +6500,8 @@ void D3D11GraphicsEngine::DrawFrameParticles( // TODO: Maybe make particles draw at a lower res and bilinear upsample the result. // Clear GBuffer0 to hold the refraction vectors since it's not needed anymore - Context->ClearRenderTargetView( GBuffer0_Diffuse->GetRenderTargetView().Get(), reinterpret_cast(&float4( 0, 0, 0, 0 )) ); - Context->ClearRenderTargetView( GBuffer1_Normals->GetRenderTargetView().Get(), reinterpret_cast(&float4( 0, 0, 0, 0 )) ); + Context->ClearRenderTargetView( bufferParticleColor->GetRenderTargetView().Get(), reinterpret_cast(&float4( 0, 0, 0, 0 )) ); + Context->ClearRenderTargetView( bufferParticleDistortion->GetRenderTargetView().Get(), reinterpret_cast(&float4( 0, 0, 0, 0 )) ); RefractionInfoConstantBuffer ricb = {}; ricb.RI_Projection = Engine::GAPI->GetProjectionMatrix(); @@ -6369,8 +6539,8 @@ void D3D11GraphicsEngine::DrawFrameParticles( } ID3D11RenderTargetView* rtv[] = { - GBuffer0_Diffuse->GetRenderTargetView().Get(), - GBuffer1_Normals->GetRenderTargetView().Get() }; + bufferParticleColor->GetRenderTargetView().Get(), + bufferParticleDistortion->GetRenderTargetView().Get() }; Context->OMSetRenderTargets( 2, rtv, DepthStencilBuffer->GetDepthStencilView().Get() ); // Bind view/proj @@ -6455,8 +6625,8 @@ void D3D11GraphicsEngine::DrawFrameParticles( state.BlendState.SetDefault(); state.BlendState.SetDirty(); - GBuffer0_Diffuse->BindToPixelShader( Context.Get(), 1 ); - GBuffer1_Normals->BindToPixelShader( Context.Get(), 2 ); + bufferParticleColor->BindToPixelShader( Context.Get(), 1 ); + bufferParticleDistortion->BindToPixelShader( Context.Get(), 2 ); // Copy scene behind the particle systems auto tempBuffer = PfxRenderer->GetTempBuffer(); @@ -6537,16 +6707,16 @@ void D3D11GraphicsEngine::SaveScreenshot() { auto Resolution = GetResolution(); // Buffer for scaling down the image auto rt = std::make_unique( - GetDevice().Get(), Resolution.x, Resolution.y, DXGI_FORMAT_ENGINE_SWAPCHAIN ); + GetDevice().Get(), Resolution.x, Resolution.y, DXGI_FORMAT_ENGINE_DEFAULT ); // Downscale to 256x256 PfxRenderer->CopyTextureToRTV( HDRBackBuffer->GetShaderResView(), rt->GetRenderTargetView() ); - D3D11_TEXTURE2D_DESC texDesc = {}; + D3D11_TEXTURE2D_DESC texDesc; texDesc.ArraySize = 1; texDesc.BindFlags = 0; texDesc.CPUAccessFlags = 0; - texDesc.Format = DXGI_FORMAT_ENGINE_SWAPCHAIN ; + texDesc.Format = DXGI_FORMAT_ENGINE_DEFAULT; texDesc.Width = Resolution.x; // must be same as backbuffer texDesc.Height = Resolution.y; // must be same as backbuffer texDesc.MipLevels = 1; diff --git a/D3D11Engine/D3D11GraphicsEngine.h b/D3D11Engine/D3D11GraphicsEngine.h index c1119a30..66a9817c 100644 --- a/D3D11Engine/D3D11GraphicsEngine.h +++ b/D3D11Engine/D3D11GraphicsEngine.h @@ -47,6 +47,13 @@ struct MeshInfo; struct RenderToTextureBuffer; class D3D11Effect; +struct AlphaMeshData { + MeshKey mk; + MeshInfo* mi; + MeshVisualInfo* vi; + std::vector instances; +}; + class D3D11GraphicsEngine : public D3D11GraphicsEngineBase { public: D3D11GraphicsEngine(); @@ -187,13 +194,6 @@ class D3D11GraphicsEngine : public D3D11GraphicsEngineBase { RenderToDepthStencilBuffer* GetDepthBuffer() { return DepthStencilBuffer.get(); } RenderToTextureBuffer* GetDepthBufferCopy() { return DepthStencilBufferCopy.get(); } - /** Returns the first GBuffer */ - RenderToTextureBuffer& GetGBuffer0() { return *GBuffer0_Diffuse; } - - /** Returns the second GBuffer */ - RenderToTextureBuffer& GetGBuffer1() { return *GBuffer1_Normals; } - RenderToTextureBuffer& GetGBuffer2() { return *GBuffer2_SpecIntens_SpecPower; } - /** Returns the HDRBackbuffer */ RenderToTextureBuffer& GetHDRBackBuffer() { return *HDRBackBuffer; } @@ -253,13 +253,11 @@ class D3D11GraphicsEngine : public D3D11GraphicsEngineBase { /** Draws the static vobs instanced */ XRESULT DrawVOBsInstanced(); + XRESULT DrawFrameAlphaMeshes(); /** Set wind props in const buffer */ void ApplyWindProps( VS_ExConstantBuffer_Wind& buf ); - /** Applys the lighting to the scene */ - XRESULT DrawLighting( std::vector& lights ); - /** Called when we started to render the world */ virtual XRESULT OnStartWorldRendering(); @@ -331,7 +329,8 @@ class D3D11GraphicsEngine : public D3D11GraphicsEngineBase { void DrawFrameParticleMeshes( std::unordered_map& progMeshes ); /** Draws particle effects */ - void DrawFrameParticles( std::map>& particles, std::map& info ); + void DrawFrameParticles(std::map>& particles, std::map& info, RenderToTextureBuffer + * bufferParticleColor, RenderToTextureBuffer* bufferParticleDistortion); /** Returns the settings window availability */ bool HasSettingsWindow(); @@ -374,9 +373,6 @@ class D3D11GraphicsEngine : public D3D11GraphicsEngineBase { /** Swapchain buffers */ Microsoft::WRL::ComPtr BackbufferRTV; - std::unique_ptr GBuffer0_Diffuse; - std::unique_ptr GBuffer1_Normals; // Normals - std::unique_ptr GBuffer2_SpecIntens_SpecPower; // SpecIntensity / SpecPower std::unique_ptr DepthStencilBufferCopy; // DummyShadowCubemapTexture moved into ShadowMaps std::unique_ptr ShadowMaps; @@ -424,6 +420,9 @@ class D3D11GraphicsEngine : public D3D11GraphicsEngineBase { Microsoft::WRL::ComPtr ReflectionCube; Microsoft::WRL::ComPtr ReflectionCube2; private: + std::vector m_AlphaMeshes; + std::vector m_FrameLights; + /** World-Mesh indirect buffer */ std::unique_ptr WorldMeshIndirectBuffer; diff --git a/D3D11Engine/D3D11NVHBAO.cpp b/D3D11Engine/D3D11NVHBAO.cpp index 344b21d5..c863169b 100644 --- a/D3D11Engine/D3D11NVHBAO.cpp +++ b/D3D11Engine/D3D11NVHBAO.cpp @@ -32,7 +32,11 @@ XRESULT D3D11NVHBAO::Init() { } /** Renders the HBAO-Effect onto the given RTV */ -XRESULT D3D11NVHBAO::Render( Microsoft::WRL::ComPtr pOutputColorRTV ) { +XRESULT D3D11NVHBAO::Render( + const Microsoft::WRL::ComPtr& pOutputColorRTV, + const Microsoft::WRL::ComPtr& pFullResDepthTexSRV, + const Microsoft::WRL::ComPtr& pFullResNormalTexSRV + ) { D3D11GraphicsEngine* engine = reinterpret_cast(Engine::GraphicsEngine); D3D11_VIEWPORT vp; @@ -43,13 +47,13 @@ XRESULT D3D11NVHBAO::Render( Microsoft::WRL::ComPtr pOut GFSDK_SSAO_InputData_D3D11 Input; Input.DepthData.DepthTextureType = GFSDK_SSAO_HARDWARE_DEPTHS; - Input.DepthData.pFullResDepthTextureSRV = engine->GetDepthBuffer()->GetShaderResView(); + Input.DepthData.pFullResDepthTextureSRV = pFullResDepthTexSRV.Get(); Input.DepthData.ProjectionMatrix.Data = GFSDK_SSAO_Float4x4( reinterpret_cast(&Engine::GAPI->GetProjectionMatrix()) ); Input.DepthData.ProjectionMatrix.Layout = GFSDK_SSAO_COLUMN_MAJOR_ORDER; Input.DepthData.MetersToViewSpaceUnits = settings.MetersToViewSpaceUnits; Input.NormalData.Enable = true; - Input.NormalData.pFullResNormalTextureSRV = engine->GetGBuffer1().GetShaderResView(); + Input.NormalData.pFullResNormalTextureSRV = pFullResNormalTexSRV.Get(); auto identity = XMMatrixIdentity(); Input.NormalData.WorldToViewMatrix.Data = GFSDK_SSAO_Float4x4( reinterpret_cast(&identity) ); // We already have them in view-space Input.NormalData.WorldToViewMatrix.Layout = GFSDK_SSAO_COLUMN_MAJOR_ORDER; diff --git a/D3D11Engine/D3D11NVHBAO.h b/D3D11Engine/D3D11NVHBAO.h index a0caa23c..d8b5a5ec 100644 --- a/D3D11Engine/D3D11NVHBAO.h +++ b/D3D11Engine/D3D11NVHBAO.h @@ -11,7 +11,9 @@ class D3D11NVHBAO { XRESULT Init(); /** Renders the HBAO-Effect onto the given RTV */ - XRESULT Render( Microsoft::WRL::ComPtr pOutputColorRTV ); + XRESULT Render(const Microsoft::WRL::ComPtr& pOutputColorRTV, const Microsoft::WRL::ComPtr< + ID3D11ShaderResourceView> + & pFullResDepthTexSRV, const Microsoft::WRL::ComPtr& pFullResNormalTexSRV); private: /** Nvidia HBAO+ context */ GFSDK_SSAO_Context_D3D11* AOContext; diff --git a/D3D11Engine/D3D11PFX_DistanceBlur.cpp b/D3D11Engine/D3D11PFX_DistanceBlur.cpp index 979c82d9..beaf6cbb 100644 --- a/D3D11Engine/D3D11PFX_DistanceBlur.cpp +++ b/D3D11Engine/D3D11PFX_DistanceBlur.cpp @@ -16,7 +16,7 @@ D3D11PFX_DistanceBlur::D3D11PFX_DistanceBlur( D3D11PfxRenderer* rnd ) : D3D11PFX D3D11PFX_DistanceBlur::~D3D11PFX_DistanceBlur() {} /** Draws this effect to the given buffer */ -XRESULT D3D11PFX_DistanceBlur::Render( RenderToTextureBuffer* fxbuffer ) { +XRESULT D3D11PFX_DistanceBlur::Render( ID3D11ShaderResourceView* diffuse ) { D3D11GraphicsEngine* engine = reinterpret_cast(Engine::GraphicsEngine); // Save old rendertargets @@ -34,7 +34,7 @@ XRESULT D3D11PFX_DistanceBlur::Render( RenderToTextureBuffer* fxbuffer ) { auto tempBuffer = FxRenderer->GetTempBuffer(); engine->GetContext()->ClearRenderTargetView( tempBuffer->GetRenderTargetView().Get(), reinterpret_cast(&float4( 0, 0, 0, 0 )) ); - FxRenderer->CopyTextureToRTV( engine->GetGBuffer0().GetShaderResView(), tempBuffer->GetRenderTargetView(), Engine::GraphicsEngine->GetResolution() ); + FxRenderer->CopyTextureToRTV( diffuse, tempBuffer->GetRenderTargetView(), Engine::GraphicsEngine->GetResolution() ); engine->GetContext()->OMSetRenderTargets( 1, oldRTV.GetAddressOf(), nullptr ); diff --git a/D3D11Engine/D3D11PFX_DistanceBlur.h b/D3D11Engine/D3D11PFX_DistanceBlur.h index 1fb74b66..b7141509 100644 --- a/D3D11Engine/D3D11PFX_DistanceBlur.h +++ b/D3D11Engine/D3D11PFX_DistanceBlur.h @@ -8,6 +8,7 @@ class D3D11PFX_DistanceBlur : ~D3D11PFX_DistanceBlur(); /** Draws this effect to the given buffer */ - XRESULT Render( RenderToTextureBuffer* fxbuffer ); + XRESULT Render( RenderToTextureBuffer* fxbuffer ) override { return XR_FAILED; } + XRESULT Render( ID3D11ShaderResourceView* diffuse ); }; diff --git a/D3D11Engine/D3D11PFX_GodRays.cpp b/D3D11Engine/D3D11PFX_GodRays.cpp index 8877cc28..8ca52bc6 100644 --- a/D3D11Engine/D3D11PFX_GodRays.cpp +++ b/D3D11Engine/D3D11PFX_GodRays.cpp @@ -17,7 +17,9 @@ D3D11PFX_GodRays::D3D11PFX_GodRays( D3D11PfxRenderer* rnd ) : D3D11PFX_Effect( r D3D11PFX_GodRays::~D3D11PFX_GodRays() {} /** Draws this effect to the given buffer */ -XRESULT D3D11PFX_GodRays::Render( RenderToTextureBuffer* fxbuffer ) { +XRESULT D3D11PFX_GodRays::Render( + ID3D11ShaderResourceView* backbuffer, + ID3D11ShaderResourceView* normals ) { if ( Engine::GAPI->GetSky()->GetAtmoshpereSettings().LightDirection.y <= 0 ) return XR_SUCCESS; // Don't render the godrays in the night-time @@ -78,8 +80,11 @@ XRESULT D3D11PFX_GodRays::Render( RenderToTextureBuffer* fxbuffer ) { // Draw downscaled mask engine->GetContext()->OMSetRenderTargets( 1, tempBuffer->GetRenderTargetView().GetAddressOf(), nullptr ); - engine->GetHDRBackBuffer().BindToPixelShader( engine->GetContext().Get(), 0 ); - engine->GetGBuffer1().BindToPixelShader( engine->GetContext().Get(), 1 ); + ID3D11ShaderResourceView* srvs[2] { + backbuffer, + normals, + }; + engine->GetContext()->PSSetShaderResources( 0, 2, srvs ); D3D11_VIEWPORT vp = {}; vp.TopLeftX = 0.0f; @@ -112,6 +117,12 @@ XRESULT D3D11PFX_GodRays::Render( RenderToTextureBuffer* fxbuffer ) { engine->GetContext()->RSSetViewports( 1, &vp ); + ID3D11ShaderResourceView* nullSRVs[2] { + nullptr, + nullptr, + }; + engine->GetContext()->PSSetShaderResources( 0, 2, nullSRVs ); + engine->GetContext()->OMSetRenderTargets( 1, oldRTV.GetAddressOf(), oldDSV.Get() ); return XR_SUCCESS; diff --git a/D3D11Engine/D3D11PFX_GodRays.h b/D3D11Engine/D3D11PFX_GodRays.h index 3f7f987e..f5f97fe1 100644 --- a/D3D11Engine/D3D11PFX_GodRays.h +++ b/D3D11Engine/D3D11PFX_GodRays.h @@ -8,6 +8,7 @@ class D3D11PFX_GodRays : ~D3D11PFX_GodRays(); /** Draws this effect to the given buffer */ - XRESULT Render( RenderToTextureBuffer* fxbuffer ); + XRESULT Render( RenderToTextureBuffer* fxbuffer ) override { return XR_FAILED; } + XRESULT Render( ID3D11ShaderResourceView* backbuffer, ID3D11ShaderResourceView* normals ); }; diff --git a/D3D11Engine/D3D11PFX_HDR.cpp b/D3D11Engine/D3D11PFX_HDR.cpp index d6b559eb..7794bf1c 100644 --- a/D3D11Engine/D3D11PFX_HDR.cpp +++ b/D3D11Engine/D3D11PFX_HDR.cpp @@ -37,7 +37,7 @@ D3D11PFX_HDR::~D3D11PFX_HDR() { } /** Draws this effect to the given buffer */ -XRESULT D3D11PFX_HDR::Render( RenderToTextureBuffer* fxbuffer ) { +XRESULT D3D11PFX_HDR::Render( ID3D11RenderTargetView* output, ID3D11ShaderResourceView* backbuffer ) { D3D11GraphicsEngine* engine = reinterpret_cast(Engine::GraphicsEngine); engine->SetDefaultStates(); Engine::GAPI->GetRendererState().BlendState.BlendEnabled = false; @@ -56,7 +56,7 @@ XRESULT D3D11PFX_HDR::Render( RenderToTextureBuffer* fxbuffer ) { auto tempBuffer = FxRenderer->GetTempBuffer(); // Copy the original image to our temp-buffer - FxRenderer->CopyTextureToRTV( engine->GetHDRBackBuffer().GetShaderResView(), tempBuffer->GetRenderTargetView(), engine->GetResolution() ); + FxRenderer->CopyTextureToRTV( backbuffer, tempBuffer->GetRenderTargetView(), engine->GetResolution() ); // Bind scene and luminance tempBuffer->BindToPixelShader( engine->GetContext().Get(), 0 ); @@ -77,7 +77,7 @@ XRESULT D3D11PFX_HDR::Render( RenderToTextureBuffer* fxbuffer ) { hps->GetConstantBuffer()[0]->UpdateBuffer( &hcb ); hps->GetConstantBuffer()[0]->BindToPixelShader( 0 ); - FxRenderer->CopyTextureToRTV( tempBuffer->GetShaderResView(), oldRTV, engine->GetResolution(), true ); + FxRenderer->CopyTextureToRTV( tempBuffer->GetShaderResView(), output, engine->GetResolution(), true ); // Show lumBuffer //FxRenderer->CopyTextureToRTV(currentLum->GetShaderResView(), oldRTV, INT2(LUM_SIZE,LUM_SIZE), false); diff --git a/D3D11Engine/D3D11PFX_HDR.h b/D3D11Engine/D3D11PFX_HDR.h index 70583f8a..e69b690f 100644 --- a/D3D11Engine/D3D11PFX_HDR.h +++ b/D3D11Engine/D3D11PFX_HDR.h @@ -9,7 +9,8 @@ class D3D11PFX_HDR : ~D3D11PFX_HDR(); /** Draws this effect to the given buffer */ - XRESULT Render( RenderToTextureBuffer* fxbuffer ); + XRESULT Render( RenderToTextureBuffer* fxbuffer ) override { return XR_FAILED; }; + XRESULT Render( ID3D11RenderTargetView* output, ID3D11ShaderResourceView* backbuffer ); protected: /** Calcualtes the luminance */ diff --git a/D3D11Engine/D3D11PfxRenderer.cpp b/D3D11Engine/D3D11PfxRenderer.cpp index 12eb8679..09f1e914 100644 --- a/D3D11Engine/D3D11PfxRenderer.cpp +++ b/D3D11Engine/D3D11PfxRenderer.cpp @@ -47,8 +47,8 @@ D3D11PfxRenderer::~D3D11PfxRenderer() { } /** Renders the distance blur effect */ -XRESULT D3D11PfxRenderer::RenderDistanceBlur() { - FX_DistanceBlur->Render( nullptr ); +XRESULT D3D11PfxRenderer::RenderDistanceBlur(ID3D11ShaderResourceView* diffuse ) { + FX_DistanceBlur->Render( diffuse ); return XR_SUCCESS; } @@ -64,13 +64,13 @@ XRESULT D3D11PfxRenderer::RenderHeightfog() { } /** Renders the godrays-Effect */ -XRESULT D3D11PfxRenderer::RenderGodRays() { - return FX_GodRays->Render( nullptr ); +XRESULT D3D11PfxRenderer::RenderGodRays(ID3D11ShaderResourceView* backbuffer, ID3D11ShaderResourceView* normals) { + return FX_GodRays->Render( backbuffer , normals ); } /** Renders the HDR-Effect */ -XRESULT D3D11PfxRenderer::RenderHDR() { - return FX_HDR->Render( nullptr ); +XRESULT D3D11PfxRenderer::RenderHDR( ID3D11RenderTargetView* output, ID3D11ShaderResourceView* backbuffer ) { + return FX_HDR->Render( output, backbuffer ); } /** Renders the SMAA-Effect */ @@ -222,8 +222,11 @@ XRESULT D3D11PfxRenderer::OnResize( const INT2& newResolution ) { } /** Draws the HBAO-Effect to the given buffer */ -XRESULT D3D11PfxRenderer::DrawHBAO( const Microsoft::WRL::ComPtr& rtv ) { - return NvHBAO->Render( rtv.Get() ); +XRESULT D3D11PfxRenderer::DrawHBAO( + const Microsoft::WRL::ComPtr& rtv, + const Microsoft::WRL::ComPtr& pFullResDepthTexSRV, + const Microsoft::WRL::ComPtr& pFullResNormalTexSRV) { + return NvHBAO->Render( rtv.Get(), pFullResDepthTexSRV, pFullResNormalTexSRV); } TextureHandle D3D11PfxRenderer::GetTempBuffer() diff --git a/D3D11Engine/D3D11PfxRenderer.h b/D3D11Engine/D3D11PfxRenderer.h index 7a3e2cc7..a19c5621 100644 --- a/D3D11Engine/D3D11PfxRenderer.h +++ b/D3D11Engine/D3D11PfxRenderer.h @@ -30,10 +30,10 @@ class D3D11PfxRenderer { XRESULT RenderHeightfog(); /** Renders the distance blur effect */ - XRESULT RenderDistanceBlur(); + XRESULT RenderDistanceBlur(ID3D11ShaderResourceView* diffuse ); /** Renders the HDR-Effect */ - XRESULT RenderHDR(); + XRESULT RenderHDR(ID3D11RenderTargetView* output, ID3D11ShaderResourceView* backbuffer); /** Renders the SMAA-Effect */ XRESULT RenderSMAA(); @@ -43,7 +43,7 @@ class D3D11PfxRenderer { XRESULT RenderSimpleSharpen( const Microsoft::WRL::ComPtr& input, INT2 inputSize, const Microsoft::WRL::ComPtr& output, INT2 outputSize, RenderToTextureBuffer& intermediateBuffer ); /** Renders the godrays-Effect */ - XRESULT RenderGodRays(); + XRESULT RenderGodRays(ID3D11ShaderResourceView* backbuffer, ID3D11ShaderResourceView* normals); /** Copies the given texture to the given RTV */ XRESULT CopyTextureToRTV( const Microsoft::WRL::ComPtr& texture, const Microsoft::WRL::ComPtr& rtv, INT2 targetResolution = INT2( 0, 0 ), bool useCustomPS = false, INT2 offset = INT2( 0, 0 ) ); @@ -55,7 +55,8 @@ class D3D11PfxRenderer { XRESULT DrawFullScreenQuad(); /** Draws the HBAO-Effect to the given buffer */ - XRESULT DrawHBAO( const Microsoft::WRL::ComPtr& rtv ); + XRESULT DrawHBAO(const ComPtr& rtv, const ComPtr& pFullResDepthTexSRV, const ComPtr< + ID3D11ShaderResourceView>& pFullResNormalTexSRV); /** Accessors */ TextureHandle GetTempBuffer(); @@ -69,6 +70,8 @@ class D3D11PfxRenderer { void OnEndFrame() { m_texturePool->GiveTick(); } + + TexturePool* GetTexturePool() { return m_texturePool.get(); } private: /** Blur effect referenced here because it's often needed by PFX */ std::unique_ptr FX_Blur; diff --git a/D3D11Engine/D3D11ShadowMap.cpp b/D3D11Engine/D3D11ShadowMap.cpp index 6d975d5e..6a9f3858 100644 --- a/D3D11Engine/D3D11ShadowMap.cpp +++ b/D3D11Engine/D3D11ShadowMap.cpp @@ -498,9 +498,9 @@ std::vector D3D11ShadowMap::ComputeCascadeSplits( float nearPlane, float return splits; } -XRESULT D3D11ShadowMap::DrawLighting( std::vector& lights ) { +XRESULT D3D11ShadowMap::DrawPointlightShadows( std::vector& lights ) { auto graphicsEngine = reinterpret_cast(Engine::GraphicsEngine); - auto _ = graphicsEngine->RecordGraphicsEvent( L"DrawLighting" ); + auto _ = graphicsEngine->RecordGraphicsEvent( L"DrawPointlightShadows" ); auto& settings = Engine::GAPI->GetRendererState().RendererSettings; static const XMVECTORF32 xmFltMax = { { { FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX } } }; @@ -522,7 +522,6 @@ XRESULT D3D11ShadowMap::DrawLighting( std::vector& lights ) { // Draw pointlight shadows if ( settings.EnablePointlightShadows > 0 ) { std::list importantUpdates; - auto _ = graphicsEngine->RecordGraphicsEvent( L"Pointlight Shadows" ); for ( auto const& light : lights ) { // Create shadowmap in case we should have one but haven't got it yet @@ -610,7 +609,15 @@ XRESULT D3D11ShadowMap::DrawLighting( std::vector& lights ) { if ( n <= 0 ) break; } } + return XR_SUCCESS; +} +XRESULT D3D11ShadowMap::DrawWorldShadow( ) +{ + auto graphicsEngine = reinterpret_cast(Engine::GraphicsEngine); + auto _ = graphicsEngine->RecordGraphicsEvent( L"DrawWorldShadow" ); + auto& settings = Engine::GAPI->GetRendererState().RendererSettings; + int numCascades = settings.NumShadowCascades; bool isOutdoor = Engine::GAPI->GetLoadedWorldInfo()->BspTree->GetBspTreeMode() == zBSP_MODE_OUTDOOR; @@ -643,33 +650,44 @@ XRESULT D3D11ShadowMap::DrawLighting( std::vector& lights ) { } } - graphicsEngine->SetDefaultStates(); - // Restore gothics camera Engine::GAPI->SetCameraReplacementPtr( nullptr ); + + return XR_SUCCESS; +} +XRESULT D3D11ShadowMap::DrawRainShadomap() { // Draw rainmap, if raining if ( Engine::GAPI->GetSceneWetness() > 0.00001f ) { - auto _ = graphicsEngine->RecordGraphicsEvent( L"Rain Shadowmap" ); + auto graphicsEngine = reinterpret_cast(Engine::GraphicsEngine); + auto _ = graphicsEngine->RecordGraphicsEvent( L"DrawLighting" ); + graphicsEngine->Effects->DrawRainShadowmap(); } + return XR_SUCCESS; +} + +XRESULT D3D11ShadowMap::DrawPointlightLights( + std::vector& lights, + RenderToTextureBuffer& color, + RenderToTextureBuffer& normals, + RenderToTextureBuffer& specular, + RenderToTextureBuffer& depthCopy + ) { + auto graphicsEngine = reinterpret_cast(Engine::GraphicsEngine); + auto _ = graphicsEngine->RecordGraphicsEvent( L"DrawPointlightLights" ); + auto& settings = Engine::GAPI->GetRendererState().RendererSettings; XMMATRIX view = Engine::GAPI->GetViewMatrixXM(); Engine::GAPI->SetViewTransformXM( view ); + view = XMMatrixTranspose( view ); - // ******************************** - // Draw direct lighting - // ******************************** graphicsEngine->SetActiveVertexShader( "VS_ExPointLight" ); graphicsEngine->SetActivePixelShader( "PS_DS_PointLight" ); auto psPointLight = graphicsEngine->GetShaderManager().GetPShader( "PS_DS_PointLight" ); auto psPointLightDynShadow = graphicsEngine->GetShaderManager().GetPShader( "PS_DS_PointLightDynShadow" ); - Engine::GAPI->SetFarPlane( - settings.SectionDrawRadius * - WORLD_SECTION_SIZE ); - Engine::GAPI->GetRendererState().BlendState.SetAdditiveBlending(); if ( settings.LimitLightIntesity ) { Engine::GAPI->GetRendererState().BlendState.BlendOp = GothicBlendStateInfo::BO_BLEND_OP_MAX; @@ -690,9 +708,7 @@ XRESULT D3D11ShadowMap::DrawLighting( std::vector& lights ) { // Set the main rendertarget m_context->OMSetRenderTargets( 1, graphicsEngine->GetHDRBackBuffer().GetRenderTargetView().GetAddressOf(), graphicsEngine->GetDepthBuffer()->GetDepthStencilView().Get() ); - - view = XMMatrixTranspose( view ); - + DS_PointLightConstantBuffer plcb = {}; XMStoreFloat4x4( &plcb.PL_InvProj, XMMatrixInverse( nullptr, XMLoadFloat4x4( &Engine::GAPI->GetProjectionMatrix() ) ) ); @@ -701,10 +717,10 @@ XRESULT D3D11ShadowMap::DrawLighting( std::vector& lights ) { auto resolution = graphicsEngine->GetResolution(); plcb.PL_ViewportSize = float2( static_cast(resolution.x), static_cast(resolution.y) ); - graphicsEngine->GetGBuffer0().BindToPixelShader( m_context.Get(), 0 ); - graphicsEngine->GetGBuffer1().BindToPixelShader( m_context.Get(), 1 ); - graphicsEngine->GetGBuffer2().BindToPixelShader( m_context.Get(), 7 ); - graphicsEngine->GetDepthBufferCopy()->BindToPixelShader( m_context.Get(), 2 ); + color.BindToPixelShader( m_context.Get(), 0 ); + normals.BindToPixelShader( m_context.Get(), 1 ); + specular.BindToPixelShader( m_context.Get(), 7 ); + depthCopy.BindToPixelShader( m_context.Get(), 2 ); // Draw all lights for ( auto const& light : lights ) { @@ -810,7 +826,142 @@ XRESULT D3D11ShadowMap::DrawLighting( std::vector& lights ) { Engine::GAPI->GetRendererState().RendererInfo.FrameDrawnLights++; } + + return XR_SUCCESS; +} + +XRESULT D3D11ShadowMap::DrawLighting( + std::vector& lights, + RenderToTextureBuffer& color, + RenderToTextureBuffer& normals, + RenderToTextureBuffer& specular, + RenderToTextureBuffer& depthCopy) { + auto graphicsEngine = reinterpret_cast(Engine::GraphicsEngine); + auto _ = graphicsEngine->RecordGraphicsEvent( L"DrawLighting" ); + auto& settings = Engine::GAPI->GetRendererState().RendererSettings; + + graphicsEngine->SetDefaultStates(); + + // Draw pointlight shadows + DrawPointlightShadows(lights); + + DrawWorldShadow(); + + graphicsEngine->SetDefaultStates(); + + DrawRainShadomap(); + + Engine::GAPI->SetFarPlane(settings.SectionDrawRadius * WORLD_SECTION_SIZE ); + + DrawPointlightLights(lights, color, normals, specular, depthCopy); + + DrawWorldLights(); + + m_context->OMSetRenderTargets( 1, graphicsEngine->GetHDRBackBuffer().GetRenderTargetView().GetAddressOf(), + graphicsEngine->GetDepthBuffer()->GetDepthStencilView().Get() ); + + return XR_SUCCESS; +} + + + +/** Renders the shadowmaps for the sun */ +void D3D11ShadowMap::RenderShadowmaps( const RenderShadowmapsParams& params ) { + + // We now assume that "target" always is something else than the world shadowmap + UINT targetSize = !params.Target + ? m_cascadedShadowMap->GetSize() + : params.Target->GetSizeX(); + + Microsoft::WRL::ComPtr dsvOverwrite = params.DSVOverwrite; + if ( params.Target && !dsvOverwrite.Get() ) dsvOverwrite = params.Target->GetDepthStencilView().Get(); + const bool isNotWorldShadowMap = params.Target != nullptr; + + // todo: remove this dependency at some point + auto graphicsEngine = (D3D11GraphicsEngine*)Engine::GraphicsEngine; + auto _ = graphicsEngine->RecordGraphicsEvent( L"RenderShadowmaps" ); + + D3D11_VIEWPORT oldVP; + UINT n = 1; + m_context->RSGetViewports( &n, &oldVP ); + + // Apply new viewport + D3D11_VIEWPORT vp; + vp.TopLeftX = 0; + vp.TopLeftY = 0; + vp.MinDepth = 0.0f; + vp.MaxDepth = 1.0f; + vp.Width = static_cast(targetSize); + vp.Height = vp.Width; + m_context->RSSetViewports( 1, &vp ); + + // Set the rendering stage + D3D11ENGINE_RENDER_STAGE oldStage = graphicsEngine->GetRenderingStage(); + graphicsEngine->SetRenderingStage( DES_SHADOWMAP ); + + // Clear and Bind the shadowmap + + Microsoft::WRL::ComPtr srv; + m_context->PSSetShaderResources( 3, 1, srv.GetAddressOf() ); + + if ( !params.DebugRTV.Get() ) { + m_context->OMSetRenderTargets( 0, nullptr, dsvOverwrite.Get() ); + Engine::GAPI->GetRendererState().BlendState.ColorWritesEnabled = false; + } else { + m_context->OMSetRenderTargets( 1, params.DebugRTV.GetAddressOf(), dsvOverwrite.Get() ); + Engine::GAPI->GetRendererState().BlendState.ColorWritesEnabled = true; + } + Engine::GAPI->GetRendererState().BlendState.SetDirty(); + + // Dont render shadows from the sun when it isn't on the sky + if ( isNotWorldShadowMap || + (Engine::GAPI->GetSky()->GetAtmoshpereSettings().LightDirection.y > + 0 && // Only stop rendering if the sun is down on main-shadowmap + // TODO: Take this out of here! + Engine::GAPI->GetRendererState().RendererSettings.DrawShadowGeometry && + Engine::GAPI->GetRendererState().RendererSettings.EnableShadows) ) { + m_context->ClearDepthStencilView( dsvOverwrite.Get(), D3D11_CLEAR_DEPTH, 1.0f, 0 ); + + // Draw the world mesh without textures + + XMVECTOR cameraPosition = XMLoadFloat3( ¶ms.CameraPosition ); + int timerLabelIndex = std::clamp(params.CascadeIndex, 0, MAX_CSM_CASCADES-1); + static const char* timer_labels_cascades[MAX_CSM_CASCADES] + { + "Cascade 0", + "Cascade 1", + "Cascade 2", + "Cascade 3", + }; + auto _1 = START_TIMING(timer_labels_cascades[timerLabelIndex]); + graphicsEngine->DrawWorldAroundForWorldShadow( cameraPosition, 2, params ); + + } else { + if ( Engine::GAPI->GetSky()->GetAtmoshpereSettings().LightDirection.y <= 0 ) { + m_context->ClearDepthStencilView( dsvOverwrite.Get(), D3D11_CLEAR_DEPTH, 0.0f, + 0 ); // Always shadow in the night + } else { + m_context->ClearDepthStencilView( + dsvOverwrite.Get(), D3D11_CLEAR_DEPTH, 1.0f, + 0 ); // Clear shadowmap when shadows not enabled + } + } + // Restore state + graphicsEngine->SetRenderingStage( oldStage ); + m_context->RSSetViewports( 1, &oldVP ); + + Engine::GAPI->SetFarPlane( + Engine::GAPI->GetRendererState().RendererSettings.SectionDrawRadius * + WORLD_SECTION_SIZE ); +} + +XRESULT D3D11ShadowMap::DrawWorldLights() +{ + auto graphicsEngine = reinterpret_cast(Engine::GraphicsEngine); + auto _ = graphicsEngine->RecordGraphicsEvent( L"DrawWorldLights" ); + auto& settings = Engine::GAPI->GetRendererState().RendererSettings; + Engine::GAPI->GetRendererState().BlendState.BlendOp = GothicBlendStateInfo::BO_BLEND_OP_ADD; Engine::GAPI->GetRendererState().BlendState.SetDirty(); @@ -823,6 +974,8 @@ XRESULT D3D11ShadowMap::DrawLighting( std::vector& lights ) { // Modify light when raining float rain = Engine::GAPI->GetRainFXWeight(); float wetness = Engine::GAPI->GetSceneWetness(); + + XMMATRIX view = XMMatrixTranspose(Engine::GAPI->GetViewMatrixXM()); bool isSnow = oCGame::GetGame() && oCGame::GetGame()->_zCSession_world @@ -846,8 +999,8 @@ XRESULT D3D11ShadowMap::DrawLighting( std::vector& lights ) { graphicsEngine->GetActivePS()->GetConstantBuffer()[1]->BindToPixelShader( 1 ); DS_ScreenQuadConstantBuffer scb = {}; - scb.SQ_InvProj = plcb.PL_InvProj; - scb.SQ_InvView = plcb.PL_InvView; + XMStoreFloat4x4( &scb.SQ_InvProj, XMMatrixInverse( nullptr, XMLoadFloat4x4( &Engine::GAPI->GetProjectionMatrix() ) ) ); + XMStoreFloat4x4( &scb.SQ_InvView, XMMatrixInverse( nullptr, XMLoadFloat4x4( &Engine::GAPI->GetRendererState().TransformState.TransformView ) ) ); scb.SQ_View = Engine::GAPI->GetRendererState().TransformState.TransformView; XMStoreFloat3( scb.SQ_LightDirectionVS.toXMFLOAT3(), @@ -930,106 +1083,10 @@ XRESULT D3D11ShadowMap::DrawLighting( std::vector& lights ) { static ID3D11ShaderResourceView* nullSrv[] = { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr }; m_context->PSSetShaderResources( 3, ARRAYSIZE( nullSrv ), nullSrv ); - m_context->OMSetRenderTargets( 1, graphicsEngine->GetHDRBackBuffer().GetRenderTargetView().GetAddressOf(), - graphicsEngine->GetDepthBuffer()->GetDepthStencilView().Get() ); - return XR_SUCCESS; } - -/** Renders the shadowmaps for the sun */ -void D3D11ShadowMap::RenderShadowmaps( const RenderShadowmapsParams& params ) { - - // We now assume that "target" always is something else than the world shadowmap - UINT targetSize = !params.Target - ? m_cascadedShadowMap->GetSize() - : params.Target->GetSizeX(); - - Microsoft::WRL::ComPtr dsvOverwrite = params.DSVOverwrite; - if ( params.Target && !dsvOverwrite.Get() ) dsvOverwrite = params.Target->GetDepthStencilView().Get(); - const bool isNotWorldShadowMap = params.Target != nullptr; - - // todo: remove this dependency at some point - auto graphicsEngine = (D3D11GraphicsEngine*)Engine::GraphicsEngine; - auto _ = graphicsEngine->RecordGraphicsEvent( L"RenderShadowmaps" ); - - D3D11_VIEWPORT oldVP; - UINT n = 1; - m_context->RSGetViewports( &n, &oldVP ); - - // Apply new viewport - D3D11_VIEWPORT vp; - vp.TopLeftX = 0; - vp.TopLeftY = 0; - vp.MinDepth = 0.0f; - vp.MaxDepth = 1.0f; - vp.Width = static_cast(targetSize); - vp.Height = vp.Width; - m_context->RSSetViewports( 1, &vp ); - - // Set the rendering stage - D3D11ENGINE_RENDER_STAGE oldStage = graphicsEngine->GetRenderingStage(); - graphicsEngine->SetRenderingStage( DES_SHADOWMAP ); - - // Clear and Bind the shadowmap - - Microsoft::WRL::ComPtr srv; - m_context->PSSetShaderResources( 3, 1, srv.GetAddressOf() ); - - if ( !params.DebugRTV.Get() ) { - m_context->OMSetRenderTargets( 0, nullptr, dsvOverwrite.Get() ); - Engine::GAPI->GetRendererState().BlendState.ColorWritesEnabled = false; - } else { - m_context->OMSetRenderTargets( 1, params.DebugRTV.GetAddressOf(), dsvOverwrite.Get() ); - Engine::GAPI->GetRendererState().BlendState.ColorWritesEnabled = true; - } - Engine::GAPI->GetRendererState().BlendState.SetDirty(); - - // Dont render shadows from the sun when it isn't on the sky - if ( isNotWorldShadowMap || - (Engine::GAPI->GetSky()->GetAtmoshpereSettings().LightDirection.y > - 0 && // Only stop rendering if the sun is down on main-shadowmap - // TODO: Take this out of here! - Engine::GAPI->GetRendererState().RendererSettings.DrawShadowGeometry && - Engine::GAPI->GetRendererState().RendererSettings.EnableShadows) ) { - m_context->ClearDepthStencilView( dsvOverwrite.Get(), D3D11_CLEAR_DEPTH, 1.0f, 0 ); - - // Draw the world mesh without textures - - XMVECTOR cameraPosition = XMLoadFloat3( ¶ms.CameraPosition ); - int timerLabelIndex = std::clamp(params.CascadeIndex, 0, MAX_CSM_CASCADES-1); - static const char* timer_labels_cascades[MAX_CSM_CASCADES] - { - "Cascade 0", - "Cascade 1", - "Cascade 2", - "Cascade 3", - }; - auto _1 = START_TIMING(timer_labels_cascades[timerLabelIndex]); - graphicsEngine->DrawWorldAroundForWorldShadow( cameraPosition, 2, params ); - - } else { - if ( Engine::GAPI->GetSky()->GetAtmoshpereSettings().LightDirection.y <= 0 ) { - m_context->ClearDepthStencilView( dsvOverwrite.Get(), D3D11_CLEAR_DEPTH, 0.0f, - 0 ); // Always shadow in the night - } else { - m_context->ClearDepthStencilView( - dsvOverwrite.Get(), D3D11_CLEAR_DEPTH, 1.0f, - 0 ); // Clear shadowmap when shadows not enabled - } - } - - // Restore state - graphicsEngine->SetRenderingStage( oldStage ); - m_context->RSSetViewports( 1, &oldVP ); - - Engine::GAPI->SetFarPlane( - Engine::GAPI->GetRendererState().RendererSettings.SectionDrawRadius * - WORLD_SECTION_SIZE ); -} - - /** Renders the shadowmaps for a pointlight */ void XM_CALLCONV D3D11ShadowMap::RenderShadowCube( FXMVECTOR position, float range, diff --git a/D3D11Engine/D3D11ShadowMap.h b/D3D11Engine/D3D11ShadowMap.h index 6230404a..bcadb383 100644 --- a/D3D11Engine/D3D11ShadowMap.h +++ b/D3D11Engine/D3D11ShadowMap.h @@ -107,11 +107,18 @@ class D3D11ShadowMap { // For cascade i: near = splits[i], far = splits[i+1] // lambda in [0,1] interpolates between logarithmic (1.0) and uniform (0.0) splits. static std::vector ComputeCascadeSplits( float nearPlane, float farPlane, size_t numCascades, float lambda = 0.95f, float bias = 1.0f ); + XRESULT DrawPointlightShadows(std::vector& lights); + XRESULT DrawWorldShadow(); + XRESULT DrawRainShadomap(); + XRESULT DrawPointlightLights(std::vector& lights, RenderToTextureBuffer& color, RenderToTextureBuffer& normals, RenderToTextureBuffer + & specular, RenderToTextureBuffer& depthCopy); /** Renders the shadowmaps for the sun using parameter struct */ void RenderShadowmaps( const RenderShadowmapsParams& params ); - XRESULT DrawLighting( std::vector& lights ); + XRESULT DrawWorldLights(); + XRESULT DrawLighting(std::vector& lights, RenderToTextureBuffer& color, RenderToTextureBuffer& normals, RenderToTextureBuffer + & specular, RenderToTextureBuffer& depthCopy); void XM_CALLCONV RenderShadowCube( DirectX::FXMVECTOR position, float range, diff --git a/D3D11Engine/GothicAPI.cpp b/D3D11Engine/GothicAPI.cpp index b400ad5b..5dba78d9 100644 --- a/D3D11Engine/GothicAPI.cpp +++ b/D3D11Engine/GothicAPI.cpp @@ -1233,7 +1233,9 @@ void GothicAPI::DrawWorldMeshNaive() { } /** Draws particles, in a simple way */ -void GothicAPI::DrawParticlesSimple() { +void GothicAPI::DrawParticlesSimple( + RenderToTextureBuffer* bufferParticleColor, + RenderToTextureBuffer* bufferParticleDistortion) { ParticleFrameData data; if ( RendererState.RendererSettings.DrawParticleEffects ) { @@ -1250,7 +1252,7 @@ void GothicAPI::DrawParticlesSimple() { } Engine::GraphicsEngine->DrawFrameParticleMeshes( ParticleEffectProgMeshes ); - Engine::GraphicsEngine->DrawFrameParticles( FrameParticles, FrameParticleInfo ); + Engine::GraphicsEngine->DrawFrameParticles( FrameParticles, FrameParticleInfo, bufferParticleColor, bufferParticleDistortion); } } @@ -1495,7 +1497,7 @@ void GothicAPI::GetVisibleDecalList( std::vector& decals ) { if ( dist > RendererState.RendererSettings.VisualFXDrawRadius ) continue; - if ( GetCameraBBox3DInFrustum( it->GetBBox(), EGothicCullFlags::CullSides ) == ZTCAM_CLIPTYPE_OUT ) { + if ( GetCameraBBox3DInFrustum( it->GetBBox(), EGothicCullFlags::CullSidesNear ) == ZTCAM_CLIPTYPE_OUT ) { continue; } diff --git a/D3D11Engine/GothicAPI.h b/D3D11Engine/GothicAPI.h index 2097b2d6..be16eaca 100644 --- a/D3D11Engine/GothicAPI.h +++ b/D3D11Engine/GothicAPI.h @@ -7,6 +7,7 @@ #include "zCPolyStrip.h" #include "zTypes.h" #include "RenderQueue.h" +#include "RenderToTextureBuffer.h" #define START_TIMING(x) TimerScope( x, &Engine::GAPI->GetRendererState().RendererInfo.Timing.frameRecordings ) @@ -512,7 +513,9 @@ class GothicAPI { void DebugDrawTreeNode( zCBspBase* base, zTBBox3D boxCell, int clipFlags = 63 ); /** Draws particles, in a simple way */ - void DrawParticlesSimple(); + void DrawParticlesSimple( + RenderToTextureBuffer* bufferParticleColor, + RenderToTextureBuffer* bufferParticleDistortion); /** Prepares poly strips for feeding into renderer (weapon and effect trails) */ void CalcPolyStripMeshes(); diff --git a/D3D11Engine/RGBuilder.cpp b/D3D11Engine/RGBuilder.cpp new file mode 100644 index 00000000..ece662f8 --- /dev/null +++ b/D3D11Engine/RGBuilder.cpp @@ -0,0 +1,17 @@ +#include "RGBuilder.h" + +// Implement RGBuilder methods (must be done after RenderGraph is fully defined) +RGResourceHandle RGBuilder::Read( RGResourceHandle handle ) { + m_pass.m_reads.push_back( handle ); + return handle; +} + +RGResourceHandle RGBuilder::Write( RGResourceHandle handle ) { + m_pass.m_writes.push_back( handle ); + return handle; +} + +RGResourceHandle RGBuilder::CreateTexture( const RGTextureDesc& desc ) { + RGResourceHandle handle = m_graph.RegisterResource( desc ); + return Write( handle ); // Creating it implies we are writing to it +} diff --git a/D3D11Engine/RGBuilder.h b/D3D11Engine/RGBuilder.h new file mode 100644 index 00000000..f3a18c4e --- /dev/null +++ b/D3D11Engine/RGBuilder.h @@ -0,0 +1,24 @@ +#pragma once +#include "RGTextureDesc.h" +#include "RenderGraph.h" +#include "RenderPass.h" + +class RGBuilder { +public: + RGBuilder( class RenderGraph& graph, class RenderPass& pass ) + : m_graph( graph ), m_pass( pass ) { + } + + // Declare that this pass READS from a resource (Source) + RGResourceHandle Read( RGResourceHandle handle ); + + // Declare that this pass WRITES to a resource (Sink) + RGResourceHandle Write( RGResourceHandle handle ); + + // Declare a brand new transient resource that lives only for this graph execution + RGResourceHandle CreateTexture( const RGTextureDesc& desc ); + +private: + RenderGraph& m_graph; + RenderPass& m_pass; +}; diff --git a/D3D11Engine/RGTextureDesc.h b/D3D11Engine/RGTextureDesc.h new file mode 100644 index 00000000..145a52e3 --- /dev/null +++ b/D3D11Engine/RGTextureDesc.h @@ -0,0 +1,17 @@ +#pragma once +#include +#include +#include +#include + +// Typedefs to represent our virtual resources +using RGResourceHandle = uint32_t; +constexpr RGResourceHandle RG_INVALID_HANDLE = 0xFFFFFFFF; + +// Metadata to describe a texture's needs without allocating it yet +struct RGTextureDesc { + uint32_t width = 0; + uint32_t height = 0; + int format = 0; // Replace with DXGI_FORMAT in your engine + std::wstring name; +}; diff --git a/D3D11Engine/RenderGraph.cpp b/D3D11Engine/RenderGraph.cpp new file mode 100644 index 00000000..c5e88d21 --- /dev/null +++ b/D3D11Engine/RenderGraph.cpp @@ -0,0 +1 @@ +#include "RenderGraph.h" diff --git a/D3D11Engine/RenderGraph.h b/D3D11Engine/RenderGraph.h new file mode 100644 index 00000000..f9599943 --- /dev/null +++ b/D3D11Engine/RenderGraph.h @@ -0,0 +1,163 @@ +#pragma once +#include "RGTextureDesc.h" +#include + +#include "BaseGraphicsEngine.h" +#include "Engine.h" +#include "RenderPass.h" +#include "RGBuilder.h" +#include "TexturePool.h" + +// Helpers for bit-packing +inline bool IsExternalHandle(RGResourceHandle handle) { + return (handle & 1) != 0; // Check if the first bit is 1 +} + +inline uint32_t GetHandleIndex(RGResourceHandle handle) { + return handle >> 1; // Shift right by 1 to get the actual ID +} + +inline RGResourceHandle MakeHandle(uint32_t index, bool isExternal) { + return (index << 1) | (isExternal ? 1 : 0); +} + +class RenderGraph { +public: + RenderGraph( TexturePool* pool ) : m_texturePool( pool ) {} + + // Bring an existing engine resource (like the DX11 BackBuffer) into the graph + RGResourceHandle ImportResource(const std::wstring& name, RenderToTextureBuffer* externalBuffer) { + uint32_t index = m_nextHandle++; + + // Resize vectors to accommodate the new index + m_externalTextures.resize(m_nextHandle, nullptr); + m_activeTextures.resize(m_nextHandle); + m_resourceDescs.resize(m_nextHandle); + + m_externalTextures[index] = externalBuffer; + m_resourceDescs[index] = {0, 0, 0, name}; // Dummy desc for name tracking + + return MakeHandle(index, true); // Sets the first bit to 1 + } + + // Add a pass using modern C++ lambdas + template + void AddPass( const std::wstring& name, SetupFunc setupFunc ) { + auto pass = std::make_unique( name ); + RGBuilder builder( *this, *pass ); + + // 1. Run the setup function to declare reads/writes + setupFunc( builder, *pass ); + + m_passes.push_back( std::move( pass ) ); + } + + // Called by RGBuilder to register handles + RGResourceHandle RegisterResource(const RGTextureDesc& desc) { + uint32_t index = m_nextHandle++; + + m_externalTextures.resize(m_nextHandle, nullptr); + m_activeTextures.resize(m_nextHandle); + m_resourceDescs.resize(m_nextHandle); + + m_resourceDescs[index] = desc; + + return MakeHandle(index, false); // First bit remains 0 + } + + void Compile() { + m_resourceLifetimes.assign(m_nextHandle, { UINT32_MAX, 0 }); + + for ( size_t passIndex = 0; passIndex < m_passes.size(); ++passIndex ) { + const auto& pass = m_passes[passIndex]; + + // Track writes (creation/modification) + for ( RGResourceHandle writeHandle : pass->m_writes ) { + uint32_t index = GetHandleIndex(writeHandle); + + // If this is the first time we are writing to this resource, record its birth + if ( m_resourceLifetimes[index].firstPass == UINT32_MAX ) { + m_resourceLifetimes[index].firstPass = (uint32_t)passIndex; + } + + // Every write extends its lifetime to this pass + m_resourceLifetimes[index].lastPass = (uint32_t)passIndex; + } + + // Track reads (usage) + for ( RGResourceHandle readHandle : pass->m_reads ) { + uint32_t index = GetHandleIndex(readHandle); + + // Reads extend the lifetime of the resource to this pass + m_resourceLifetimes[index].lastPass = (uint32_t)passIndex; + } + } + + // TODO: remove all passes whose writes are never read. + } + + void Execute() { + for ( size_t i = 0; i < m_passes.size(); ++i ) { + const auto& pass = m_passes[i]; + + AllocateResourcesForPass( i ); + + if ( pass->m_executeCallback ) { + auto _ = Engine::GraphicsEngine->RecordGraphicsEvent( pass->m_name.c_str() ); + pass->m_executeCallback(*this); + } + + ReleaseResourcesForPass( i ); + } + } + + RenderToTextureBuffer* GetPhysicalTexture(RGResourceHandle handle) const { + uint32_t index = GetHandleIndex(handle); + + return IsExternalHandle(handle) + ? m_externalTextures[index] + : m_activeTextures[index].get(); + } +private: + struct Lifetime { uint32_t firstPass; uint32_t lastPass; }; + + TexturePool* m_texturePool; + uint32_t m_nextHandle = 0; + std::vector> m_passes; + std::vector m_resourceDescs; + std::vector m_resourceLifetimes; + + // Physical resource storage mapped by the Handle Index + std::vector m_activeTextures; + std::vector m_externalTextures; + + void AllocateResourcesForPass(size_t passIndex) { + for (uint32_t i = 0; i < m_resourceLifetimes.size(); ++i) { + // We only allocate for Graph-Managed resources + // (Assuming we track external handles in the lifetimes list too, we just skip them) + + if (m_resourceLifetimes[i].firstPass == (uint32_t)passIndex) { + // If this index is meant to be external, m_externalTextures[i] will be populated, + // so we don't allocate it from the pool. + if (m_externalTextures[i] != nullptr) continue; + + const RGTextureDesc& desc = m_resourceDescs[i]; + TexturePool::Description poolDesc{ (int)desc.width, (int)desc.height, static_cast(desc.format) }; + + m_activeTextures[i] = std::move(m_texturePool->Acquire(poolDesc)); + } + } + } + + void ReleaseResourcesForPass(size_t passIndex) { + for (uint32_t i = 0; i < m_resourceLifetimes.size(); ++i) { + if (m_resourceLifetimes[i].lastPass == (uint32_t)passIndex) { + if (m_externalTextures[i] != nullptr) continue; + + // Resetting the unique_ptr triggers + // returning it to the TexturePool automatically. + m_activeTextures[i].reset(); + } + } + } +}; diff --git a/D3D11Engine/RenderPass.cpp b/D3D11Engine/RenderPass.cpp new file mode 100644 index 00000000..1e829a2c --- /dev/null +++ b/D3D11Engine/RenderPass.cpp @@ -0,0 +1 @@ +#include "RenderPass.h" diff --git a/D3D11Engine/RenderPass.h b/D3D11Engine/RenderPass.h new file mode 100644 index 00000000..7995c040 --- /dev/null +++ b/D3D11Engine/RenderPass.h @@ -0,0 +1,22 @@ +#pragma once +#include +#include +#include +#include "RGTextureDesc.h" + +class RenderPass { + // Only the RenderGraph should trigger execution + friend class RenderGraph; + +public: + RenderPass(std::wstring name ) : m_name(std::move(name)) {} + + std::wstring m_name; + std::vector m_reads; // Sources + std::vector m_writes; // Sinks + + // The function that records the actual DX11 commands + // In your engine, pass your DX11DeviceContext or CommandList wrapper here + std::function m_executeCallback; + +}; From fc6bbfc0a86df357972f7c84151afb652d9dc208 Mon Sep 17 00:00:00 2001 From: kirides <13602143+kirides@users.noreply.github.com> Date: Mon, 23 Feb 2026 20:37:23 +0100 Subject: [PATCH 04/13] make SMAA use TexturePool --- D3D11Engine/D3D11GraphicsEngine.cpp | 11 ++-- D3D11Engine/D3D11PFX_SMAA.cpp | 13 ++--- D3D11Engine/D3D11PFX_SMAA.h | 2 - D3D11Engine/D3D11PfxRenderer.cpp | 4 +- D3D11Engine/D3D11PfxRenderer.h | 2 +- D3D11Engine/D3D11ShaderManager.cpp | 15 ++++-- D3D11Engine/D3D11ShaderManager.h | 1 + D3D11Engine/SMAA/D3D11SMAA.cpp | 80 ++++++++--------------------- D3D11Engine/SMAA/D3D11SMAA.h | 15 ++---- 9 files changed, 52 insertions(+), 91 deletions(-) diff --git a/D3D11Engine/D3D11GraphicsEngine.cpp b/D3D11Engine/D3D11GraphicsEngine.cpp index d454f0a2..650834d6 100644 --- a/D3D11Engine/D3D11GraphicsEngine.cpp +++ b/D3D11Engine/D3D11GraphicsEngine.cpp @@ -2838,10 +2838,11 @@ XRESULT D3D11GraphicsEngine::OnStartWorldRendering() { builder.Read( normalsResource ); builder.Write( backBufferHandle ); - pass.m_executeCallback = [this, normalsResource](const RenderGraph& graph) { + pass.m_executeCallback = [this, normalsResource, backBufferHandle](const RenderGraph& graph) { auto normalsTexture = graph.GetPhysicalTexture(normalsResource); + auto backBuffer = graph.GetPhysicalTexture(backBufferHandle); - PfxRenderer->DrawHBAO( HDRBackBuffer->GetRenderTargetView(), + PfxRenderer->DrawHBAO( backBuffer->GetRenderTargetView(), GetDepthBuffer()->GetShaderResView(), normalsTexture->GetShaderResView()); GetContext()->PSSetSamplers( 0, 1, DefaultSamplerState.GetAddressOf() ); @@ -3071,8 +3072,9 @@ XRESULT D3D11GraphicsEngine::OnStartWorldRendering() { builder.Read( backBufferHandle ); builder.Write( backBufferHandle ); - pass.m_executeCallback = [this, backBufferHandle](const RenderGraph&) { - PfxRenderer->RenderSMAA(); + pass.m_executeCallback = [this, backBufferHandle](const RenderGraph& graph) { + auto backbufferTex = graph.GetPhysicalTexture( backBufferHandle ); + PfxRenderer->RenderSMAA(backbufferTex->GetShaderResView().Get()); GetContext()->PSSetSamplers( 0, 1, DefaultSamplerState.GetAddressOf() ); }; } ); @@ -3080,6 +3082,7 @@ XRESULT D3D11GraphicsEngine::OnStartWorldRendering() { graph.Compile(); graph.Execute(); + GetContext()->PSSetSamplers( 0, 1, DefaultSamplerState.GetAddressOf() ); PresentPending = true; diff --git a/D3D11Engine/D3D11PFX_SMAA.cpp b/D3D11Engine/D3D11PFX_SMAA.cpp index 394e2681..bf72b05f 100644 --- a/D3D11Engine/D3D11PFX_SMAA.cpp +++ b/D3D11Engine/D3D11PFX_SMAA.cpp @@ -5,12 +5,7 @@ #include "RenderToTextureBuffer.h" #include "D3D11GraphicsEngine.h" #include "D3D11PfxRenderer.h" -#include "D3D11ShaderManager.h" -#include "D3D11VShader.h" #include "GothicAPI.h" -#include "D3D11PShader.h" -#include -#include D3D11PFX_SMAA::D3D11PFX_SMAA( D3D11PfxRenderer* rnd ) : D3D11PFX_Effect( rnd ) { Init(); @@ -42,22 +37,22 @@ void D3D11PFX_SMAA::RenderPostFX( const Microsoft::WRL::ComPtr OldRTV; Microsoft::WRL::ComPtr OldDSV; - engine->GetContext()->OMGetRenderTargets( 1, OldRTV.GetAddressOf(), OldDSV.GetAddressOf() ); + pContext->OMGetRenderTargets( 1, OldRTV.GetAddressOf(), OldDSV.GetAddressOf() ); auto TempRTV = FxRenderer->GetTempBuffer(); // update the temp buffer with the latest backbuffer data FxRenderer->CopyTextureToRTV( renderTargetSRV, TempRTV->GetRenderTargetView(), engine->GetResolution() ); - m_Native->Render( renderTargetSRV.Get(), TempRTV->GetRenderTargetView().Get() ); + m_Native->Render( renderTargetSRV.Get(), TempRTV->GetRenderTargetView().Get(), FxRenderer->GetTexturePool() ); // Copy result back to acutal RTV FxRenderer->CopyTextureToRTV( TempRTV->GetShaderResView(), OldRTV ); - engine->GetContext()->OMSetRenderTargets( 1, OldRTV.GetAddressOf(), OldDSV.Get() ); + pContext->OMSetRenderTargets( 1, OldRTV.GetAddressOf(), OldDSV.Get() ); ID3D11ShaderResourceView* const NoSRV[1] = { nullptr }; - engine->GetContext()->PSSetShaderResources( 0, 1, NoSRV ); + pContext->PSSetShaderResources( 0, 1, NoSRV ); engine->SetDefaultStates( true ); } diff --git a/D3D11Engine/D3D11PFX_SMAA.h b/D3D11Engine/D3D11PFX_SMAA.h index 2611f360..cd085035 100644 --- a/D3D11Engine/D3D11PFX_SMAA.h +++ b/D3D11Engine/D3D11PFX_SMAA.h @@ -6,8 +6,6 @@ #include "pch.h" #include "d3d11pfx_effect.h" -#include "D3D11PShader.h" -#include "D3D11VShader.h" #include "SMAA/D3D11SMAA.h" diff --git a/D3D11Engine/D3D11PfxRenderer.cpp b/D3D11Engine/D3D11PfxRenderer.cpp index 09f1e914..790ab7f6 100644 --- a/D3D11Engine/D3D11PfxRenderer.cpp +++ b/D3D11Engine/D3D11PfxRenderer.cpp @@ -74,8 +74,8 @@ XRESULT D3D11PfxRenderer::RenderHDR( ID3D11RenderTargetView* output, ID3D11Shade } /** Renders the SMAA-Effect */ -XRESULT D3D11PfxRenderer::RenderSMAA() { - FX_SMAA->RenderPostFX( reinterpret_cast(Engine::GraphicsEngine)->GetHDRBackBuffer().GetShaderResView() ); +XRESULT D3D11PfxRenderer::RenderSMAA(ID3D11ShaderResourceView* backbuffer) { + FX_SMAA->RenderPostFX( backbuffer ); return XR_SUCCESS; } diff --git a/D3D11Engine/D3D11PfxRenderer.h b/D3D11Engine/D3D11PfxRenderer.h index a19c5621..ef51a1f3 100644 --- a/D3D11Engine/D3D11PfxRenderer.h +++ b/D3D11Engine/D3D11PfxRenderer.h @@ -36,7 +36,7 @@ class D3D11PfxRenderer { XRESULT RenderHDR(ID3D11RenderTargetView* output, ID3D11ShaderResourceView* backbuffer); /** Renders the SMAA-Effect */ - XRESULT RenderSMAA(); + XRESULT RenderSMAA(ID3D11ShaderResourceView* backbuffer); XRESULT RenderTAA(const ComPtr& velocityBuffer); XRESULT RenderCAS( const Microsoft::WRL::ComPtr& input, INT2 inputSize, const Microsoft::WRL::ComPtr& output, INT2 outputSize, RenderToTextureBuffer& intermediateBuffer ); diff --git a/D3D11Engine/D3D11ShaderManager.cpp b/D3D11Engine/D3D11ShaderManager.cpp index 7f7abba4..3cca0842 100644 --- a/D3D11Engine/D3D11ShaderManager.cpp +++ b/D3D11Engine/D3D11ShaderManager.cpp @@ -132,6 +132,17 @@ D3D11ShaderManager::~D3D11ShaderManager() { // Find and compile the specified shader //-------------------------------------------------------------------------------------- HRESULT D3D11ShaderManager::CompileShaderFromFile( const CHAR* szFileName, LPCSTR szEntryPoint, LPCSTR szShaderModel, ID3DBlob** ppBlobOut, const std::vector& makros ) { + auto shaderFile = Toolbox::ToWideChar( szFileName ); + + return CompileShaderFromFile( + shaderFile.c_str(), + szEntryPoint, + szShaderModel, + ppBlobOut, + makros); +} + +HRESULT D3D11ShaderManager::CompileShaderFromFile( const WCHAR* szFileName, LPCSTR szEntryPoint, LPCSTR szShaderModel, ID3DBlob** ppBlobOut, const std::vector& makros ) { HRESULT hr = S_OK; DWORD dwShaderFlags = D3DCOMPILE_ENABLE_STRICTNESS; @@ -155,10 +166,8 @@ HRESULT D3D11ShaderManager::CompileShaderFromFile( const CHAR* szFileName, LPCST m.insert( m.begin(), makros.begin(), makros.end() ); Microsoft::WRL::ComPtr pErrorBlob; - - auto shaderFile = Toolbox::ToWideChar( szFileName ); - std::filesystem::path shaderPath( shaderFile ); + std::filesystem::path shaderPath( szFileName ); // absolute path shaderPath = Engine::GAPI->GetStartDirectory().c_str() / shaderPath; diff --git a/D3D11Engine/D3D11ShaderManager.h b/D3D11Engine/D3D11ShaderManager.h index 062711e3..b6148266 100644 --- a/D3D11Engine/D3D11ShaderManager.h +++ b/D3D11Engine/D3D11ShaderManager.h @@ -48,6 +48,7 @@ class D3D11ShaderManager { /** Compiles the shader from file and outputs error messages if needed */ static HRESULT CompileShaderFromFile( const CHAR* szFileName, LPCSTR szEntryPoint, LPCSTR szShaderModel, ID3DBlob** ppBlobOut, const std::vector& makros ); + static HRESULT CompileShaderFromFile( const WCHAR* szFileName, LPCSTR szEntryPoint, LPCSTR szShaderModel, ID3DBlob** ppBlobOut, const std::vector& makros ); /** Creates list with ShaderInfos */ XRESULT Init(); diff --git a/D3D11Engine/SMAA/D3D11SMAA.cpp b/D3D11Engine/SMAA/D3D11SMAA.cpp index c209ccb7..6c541458 100644 --- a/D3D11Engine/SMAA/D3D11SMAA.cpp +++ b/D3D11Engine/SMAA/D3D11SMAA.cpp @@ -1,7 +1,5 @@ #include "D3D11SMAA.h" -#include -#include -#include +#include "../D3D11ShaderManager.h" // Include DirectXTK or your preferred texture loader #include "DDSTextureLoader.h" // Assuming DirectXTK availability @@ -21,24 +19,6 @@ D3D11SMAA::~D3D11SMAA() { } -HRESULT D3D11SMAA::CompileShader(const std::wstring& path, const std::string& entryPoint, const std::string& profile, ID3DBlob** blob) -{ - ComPtr errorBlob; - UINT flags = D3DCOMPILE_ENABLE_STRICTNESS; -#ifdef _DEBUG - flags |= D3DCOMPILE_DEBUG; -#endif - - HRESULT hr = D3DCompileFromFile(path.c_str(), nullptr, D3D_COMPILE_STANDARD_FILE_INCLUDE, - entryPoint.c_str(), profile.c_str(), flags, 0, blob, errorBlob.GetAddressOf()); - - if (FAILED(hr) && errorBlob) - { - OutputDebugStringA((char*)errorBlob->GetBufferPointer()); - } - return hr; -} - bool D3D11SMAA::Init(const std::wstring& shaderPath, const std::wstring& areaTexPath, const std::wstring& searchTexPath) { HRESULT hr; @@ -46,24 +26,25 @@ bool D3D11SMAA::Init(const std::wstring& shaderPath, const std::wstring& areaTex // 1. Compile Shaders // Edge Detection - if (FAILED(CompileShader(shaderPath, "EdgeDetectionVS", "vs_5_0", &blob))) return false; + std::vector noMacros{}; + if (FAILED(D3D11ShaderManager::CompileShaderFromFile(shaderPath.c_str(), "EdgeDetectionVS", "vs_5_0", &blob, noMacros))) return false; m_device->CreateVertexShader(blob->GetBufferPointer(), blob->GetBufferSize(), nullptr, m_vsEdge.GetAddressOf()); - if (FAILED(CompileShader(shaderPath, "LumaEdgeDetectionPS", "ps_5_0", &blob))) return false; + if (FAILED(D3D11ShaderManager::CompileShaderFromFile(shaderPath.c_str(), "LumaEdgeDetectionPS", "ps_5_0", &blob, noMacros))) return false; m_device->CreatePixelShader(blob->GetBufferPointer(), blob->GetBufferSize(), nullptr, m_psLumaEdge.GetAddressOf()); // Blending Weight - if (FAILED(CompileShader(shaderPath, "BlendingWeightCalculationVS", "vs_5_0", &blob))) return false; + if (FAILED(D3D11ShaderManager::CompileShaderFromFile(shaderPath.c_str(), "BlendingWeightCalculationVS", "vs_5_0", &blob, noMacros))) return false; m_device->CreateVertexShader(blob->GetBufferPointer(), blob->GetBufferSize(), nullptr, m_vsBlend.GetAddressOf()); - if (FAILED(CompileShader(shaderPath, "BlendingWeightCalculationPS", "ps_5_0", &blob))) return false; + if (FAILED(D3D11ShaderManager::CompileShaderFromFile(shaderPath.c_str(), "BlendingWeightCalculationPS", "ps_5_0", &blob, noMacros))) return false; m_device->CreatePixelShader(blob->GetBufferPointer(), blob->GetBufferSize(), nullptr, m_psBlend.GetAddressOf()); // Neighborhood Blending - if (FAILED(CompileShader(shaderPath, "NeighborhoodBlendingVS", "vs_5_0", &blob))) return false; + if (FAILED(D3D11ShaderManager::CompileShaderFromFile(shaderPath.c_str(), "NeighborhoodBlendingVS", "vs_5_0", &blob, noMacros))) return false; m_device->CreateVertexShader(blob->GetBufferPointer(), blob->GetBufferSize(), nullptr, m_vsNeighbor.GetAddressOf()); - if (FAILED(CompileShader(shaderPath, "NeighborhoodBlendingPS", "ps_5_0", &blob))) return false; + if (FAILED(D3D11ShaderManager::CompileShaderFromFile(shaderPath.c_str(), "NeighborhoodBlendingPS", "ps_5_0", &blob, noMacros))) return false; m_device->CreatePixelShader(blob->GetBufferPointer(), blob->GetBufferSize(), nullptr, m_psNeighbor.GetAddressOf()); // 2. Load Textures @@ -127,44 +108,25 @@ void D3D11SMAA::OnResize(int width, int height) m_width = width; m_height = height; - // Release old - m_edgesTex.Reset(); m_edgesRTV.Reset(); m_edgesSRV.Reset(); - m_blendTex.Reset(); m_blendRTV.Reset(); m_blendSRV.Reset(); - - D3D11_TEXTURE2D_DESC desc = {}; - desc.Width = width; - desc.Height = height; - desc.MipLevels = 1; - desc.ArraySize = 1; - desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; // SMAA requires specific formats, RGBA8 UNORM is generally safe - desc.SampleDesc.Count = 1; - desc.Usage = D3D11_USAGE_DEFAULT; - desc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET; - - // Create Edges Texture - m_device->CreateTexture2D(&desc, nullptr, m_edgesTex.GetAddressOf()); - m_device->CreateRenderTargetView(m_edgesTex.Get(), nullptr, m_edgesRTV.GetAddressOf()); - m_device->CreateShaderResourceView(m_edgesTex.Get(), nullptr, m_edgesSRV.GetAddressOf()); - - // Create Blend Texture - m_device->CreateTexture2D(&desc, nullptr, m_blendTex.GetAddressOf()); - m_device->CreateRenderTargetView(m_blendTex.Get(), nullptr, m_blendRTV.GetAddressOf()); - m_device->CreateShaderResourceView(m_blendTex.Get(), nullptr, m_blendSRV.GetAddressOf()); - // Update Constant Buffer SMAAConstants constants; constants.RT_Metrics = XMFLOAT4(1.0f / width, 1.0f / height, (float)width, (float)height); m_context->UpdateSubresource(m_constantBuffer.Get(), 0, nullptr, &constants, 0, 0); } -void D3D11SMAA::Render(ID3D11ShaderResourceView* inputSRV, ID3D11RenderTargetView* outputRTV) +void D3D11SMAA::Render(ID3D11ShaderResourceView* inputSRV, + ID3D11RenderTargetView* outputRTV, + TexturePool* pool ) { if (!m_width || !m_height) return; // Save old state (Optional, but good practice in a library) // For performance in an engine, you usually don't save/restore but assume state flow. // Here we just set what we need. - + + auto edgesTex = pool->Acquire({m_width, m_height, DXGI_FORMAT_R8G8B8A8_UNORM}); + auto blendTex = pool->Acquire({m_width, m_height, DXGI_FORMAT_R8G8B8A8_UNORM}); + float clearColor[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; ID3D11ShaderResourceView* nullSRVs[3] = { nullptr, nullptr, nullptr }; @@ -188,8 +150,8 @@ void D3D11SMAA::Render(ID3D11ShaderResourceView* inputSRV, ID3D11RenderTargetVie // Input: Color (t0) // Output: EdgesTex // ----------------------------------------------------------- - m_context->ClearRenderTargetView(m_edgesRTV.Get(), clearColor); - m_context->OMSetRenderTargets(1, m_edgesRTV.GetAddressOf(), nullptr); + m_context->ClearRenderTargetView(edgesTex->GetRenderTargetView().Get(), clearColor); + m_context->OMSetRenderTargets(1, edgesTex->GetRenderTargetView().GetAddressOf(), nullptr); m_context->VSSetShader(m_vsEdge.Get(), nullptr, 0); m_context->PSSetShader(m_psLumaEdge.Get(), nullptr, 0); @@ -207,14 +169,14 @@ void D3D11SMAA::Render(ID3D11ShaderResourceView* inputSRV, ID3D11RenderTargetVie // Input: EdgesTex (t1), AreaTex (t3), SearchTex (t4) // Output: BlendTex // ----------------------------------------------------------- - m_context->ClearRenderTargetView(m_blendRTV.Get(), clearColor); - m_context->OMSetRenderTargets(1, m_blendRTV.GetAddressOf(), nullptr); + m_context->ClearRenderTargetView(blendTex->GetRenderTargetView().Get(), clearColor); + m_context->OMSetRenderTargets(1, blendTex->GetRenderTargetView().GetAddressOf(), nullptr); m_context->VSSetShader(m_vsBlend.Get(), nullptr, 0); m_context->PSSetShader(m_psBlend.Get(), nullptr, 0); // Bind resources - ID3D11ShaderResourceView* pass2SRVs[] = { nullptr, m_edgesSRV.Get(), nullptr, m_areaTexSRV.Get(), m_searchTexSRV.Get() }; + ID3D11ShaderResourceView* pass2SRVs[] = { nullptr, edgesTex->GetShaderResView().Get(), nullptr, m_areaTexSRV.Get(), m_searchTexSRV.Get() }; // We start at slot 0 to clear slot 0, or just bind specifically // Map: t1=Edges, t3=Area, t4=Search. t0 is unused here. m_context->PSSetShaderResources(0, 5, pass2SRVs); @@ -233,7 +195,7 @@ void D3D11SMAA::Render(ID3D11ShaderResourceView* inputSRV, ID3D11RenderTargetVie m_context->VSSetShader(m_vsNeighbor.Get(), nullptr, 0); m_context->PSSetShader(m_psNeighbor.Get(), nullptr, 0); - ID3D11ShaderResourceView* pass3SRVs[] = { inputSRV, nullptr, m_blendSRV.Get() }; + ID3D11ShaderResourceView* pass3SRVs[] = { inputSRV, nullptr, blendTex->GetShaderResView().Get() }; m_context->PSSetShaderResources(0, 3, pass3SRVs); m_context->Draw(3, 0); diff --git a/D3D11Engine/SMAA/D3D11SMAA.h b/D3D11Engine/SMAA/D3D11SMAA.h index 74fc0876..0a4c8767 100644 --- a/D3D11Engine/SMAA/D3D11SMAA.h +++ b/D3D11Engine/SMAA/D3D11SMAA.h @@ -1,9 +1,11 @@ #pragma once +#include "../pch.h" #include #include #include #include +#include "../TexturePool.h" // Forward decl for texture loader if you use DirectXTK, otherwise assume generic // #include "DDSTextureLoader.h" @@ -23,7 +25,7 @@ class D3D11SMAA { // Main Render Function // inputSRV: The scene color texture (Gamma space usually required for Luma Edge Detect) // outputRTV: Where the anti-aliased image will be written - void Render(ID3D11ShaderResourceView* inputSRV, ID3D11RenderTargetView* outputRTV); + void Render(ID3D11ShaderResourceView* inputSRV, ID3D11RenderTargetView* outputRTV, TexturePool* pool); private: struct SMAAConstants { @@ -51,15 +53,6 @@ class D3D11SMAA { Microsoft::WRL::ComPtr m_areaTexSRV; Microsoft::WRL::ComPtr m_searchTexSRV; - // Render Targets (Intermediate) - Microsoft::WRL::ComPtr m_edgesTex; - Microsoft::WRL::ComPtr m_edgesRTV; - Microsoft::WRL::ComPtr m_edgesSRV; - - Microsoft::WRL::ComPtr m_blendTex; - Microsoft::WRL::ComPtr m_blendRTV; - Microsoft::WRL::ComPtr m_blendSRV; - // States Microsoft::WRL::ComPtr m_constantBuffer; Microsoft::WRL::ComPtr m_samplerLinear; @@ -70,4 +63,4 @@ class D3D11SMAA { int m_width; int m_height; -}; \ No newline at end of file +}; From ec5e7c26d9f185f3b7fe65ea9c676cc44d9a16f2 Mon Sep 17 00:00:00 2001 From: kirides <13602143+kirides@users.noreply.github.com> Date: Mon, 23 Feb 2026 23:36:20 +0100 Subject: [PATCH 05/13] more usage of GetPhysicalTexture --- D3D11Engine/D3D11GraphicsEngine.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/D3D11Engine/D3D11GraphicsEngine.cpp b/D3D11Engine/D3D11GraphicsEngine.cpp index 650834d6..92fa9591 100644 --- a/D3D11Engine/D3D11GraphicsEngine.cpp +++ b/D3D11Engine/D3D11GraphicsEngine.cpp @@ -2849,7 +2849,6 @@ XRESULT D3D11GraphicsEngine::OnStartWorldRendering() { }; }); } - graph.AddPass( L"DrawWaterSurfaces", [&]( RGBuilder& builder, RenderPass& pass ) { builder.Read( backBufferHandle ); @@ -2940,10 +2939,11 @@ XRESULT D3D11GraphicsEngine::OnStartWorldRendering() { graph.AddPass( L"Reset RenderTargets", [&]( RGBuilder& builder, RenderPass& pass ) { builder.Write( backBufferHandle ); - pass.m_executeCallback = [this](const RenderGraph&) { - GetContext()->OMSetRenderTargets( 1, HDRBackBuffer->GetRenderTargetView().GetAddressOf(), - DepthStencilBuffer->GetDepthStencilView().Get() ); - }; + pass.m_executeCallback = [this, backBufferHandle](const RenderGraph& graph) { + auto backBuffer = graph.GetPhysicalTexture(backBufferHandle); + GetContext()->OMSetRenderTargets( 1, backBuffer->GetRenderTargetView().GetAddressOf(), + DepthStencilBuffer->GetDepthStencilView().Get() ); + }; }); if (rendererState.RendererSettings.DrawParticleEffects) { From 44d7dd3dbbce009caffa92a58a156da0e7b13ab9 Mon Sep 17 00:00:00 2001 From: kirides <13602143+kirides@users.noreply.github.com> Date: Mon, 23 Feb 2026 23:52:12 +0100 Subject: [PATCH 06/13] move remaining code from OnStartWorldRendering into RenderGraph --- D3D11Engine/D3D11GraphicsEngine.cpp | 152 +++++++++++++++++----------- 1 file changed, 91 insertions(+), 61 deletions(-) diff --git a/D3D11Engine/D3D11GraphicsEngine.cpp b/D3D11Engine/D3D11GraphicsEngine.cpp index 92fa9591..cb4aecd6 100644 --- a/D3D11Engine/D3D11GraphicsEngine.cpp +++ b/D3D11Engine/D3D11GraphicsEngine.cpp @@ -3034,7 +3034,6 @@ XRESULT D3D11GraphicsEngine::OnStartWorldRendering() { LineRenderer->FlushScreenSpace(); }; } ); - if ( rendererState.RendererSettings.AntiAliasingMode == GothicRendererSettings::AA_TAA ) { @@ -3079,93 +3078,124 @@ XRESULT D3D11GraphicsEngine::OnStartWorldRendering() { }; } ); } + + graph.AddPass( L"Reset Viewport", [&]( RGBuilder& builder, RenderPass& pass ) { + builder.Write( backBufferHandle ); - graph.Compile(); - graph.Execute(); - GetContext()->PSSetSamplers( 0, 1, DefaultSamplerState.GetAddressOf() ); + pass.m_executeCallback = [this](const RenderGraph&) { + GetContext()->PSSetSamplers( 0, 1, DefaultSamplerState.GetAddressOf() ); - PresentPending = true; + PresentPending = true; - // Set viewport for gothics rendering - vp.TopLeftX = 0.0f; - vp.TopLeftY = 0.0f; - vp.MinDepth = 0.0f; - vp.MaxDepth = 1.0f; - vp.Width = static_cast(GetResolution().x); - vp.Height = static_cast(GetResolution().y); + // Set viewport for gothics rendering + D3D11_VIEWPORT vp; + vp.TopLeftX = 0.0f; + vp.TopLeftY = 0.0f; + vp.MinDepth = 0.0f; + vp.MaxDepth = 1.0f; + vp.Width = static_cast(GetResolution().x); + vp.Height = static_cast(GetResolution().y); - GetContext()->RSSetViewports( 1, &vp ); + GetContext()->RSSetViewports( 1, &vp ); + }; + } ); // If we currently are underwater, then draw underwater effects if ( Engine::GAPI->IsUnderWater() ) { - auto _ = RecordGraphicsEvent( L"DrawUnderwaterEffects" ); - DrawUnderwaterEffects(); + graph.AddPass( L"Draw UnderwaterFX", [&]( RGBuilder& builder, RenderPass& pass ) { + builder.Write( backBufferHandle ); + + pass.m_executeCallback = [this](const RenderGraph&) { + DrawUnderwaterEffects(); + }; + } ); } - // Clear here to get a working depthbuffer but no interferences with world - // geometry for gothic UI-Rendering - GetContext()->OMSetRenderTargets( 1, HDRBackBuffer->GetRenderTargetView().GetAddressOf(), nullptr ); + // If we currently are underwater, then draw underwater effects + if ( Engine::GAPI->IsUnderWater() ) { + graph.AddPass( L"Prepare finalize frame", [&]( RGBuilder& builder, RenderPass& pass ) { + builder.Write( backBufferHandle ); - // Store the current depth state to the copy buffer before clear - CopyDepthStencil(); + pass.m_executeCallback = [this](const RenderGraph&) { + // Clear here to get a working depthbuffer but no interferences with world + // geometry for gothic UI-Rendering + GetContext()->OMSetRenderTargets( 1, HDRBackBuffer->GetRenderTargetView().GetAddressOf(), nullptr ); + + // Store the current depth state to the copy buffer before clear + CopyDepthStencil(); - GetContext()->ClearDepthStencilView( DepthStencilBuffer->GetDepthStencilView().Get(), D3D11_CLEAR_DEPTH, 0, 0 ); - GetContext()->ClearDepthStencilView( m_NativeSizeDepthStencil->GetDepthStencilView().Get(), D3D11_CLEAR_DEPTH, 0, 0 ); + GetContext()->ClearDepthStencilView( DepthStencilBuffer->GetDepthStencilView().Get(), D3D11_CLEAR_DEPTH, 0, 0 ); + GetContext()->ClearDepthStencilView( m_NativeSizeDepthStencil->GetDepthStencilView().Get(), D3D11_CLEAR_DEPTH, 0, 0 ); + + SetDefaultStates(); + }; + } ); + } // Before returning to gothics UI, set render target to backbuffer { // Copy HDR scene to backbuffer - - SetDefaultStates(); - if ( rendererState.RendererSettings.ResolutionScalePercent < 100 && rendererState.RendererSettings.Upscaler == GothicRendererSettings::E_Upscaler::UPSCALER_FSR_1 ) { - auto _ = RecordGraphicsEvent( L"FSR 1" ); - - // Now upscale it to backbuffer with sharpening - auto sharpenFactor = rendererState.RendererSettings.SharpenFactor; - PfxRenderer->GetFSR1()->Apply( - HDRBackBuffer->GetShaderResView(), - Backbuffer->GetRenderTargetView(), - GetResolution(), - GetBackbufferResolution(), - sharpenFactor > 0.001f, - 1.0f - sharpenFactor ); - } else { + graph.AddPass( L"FSR 1 Upscale", [&]( RGBuilder& builder, RenderPass& pass ) { + builder.Write( backBufferHandle ); - if ( rendererState.RendererSettings.SharpeningMode - && rendererState.RendererSettings.SharpenFactor > 0.0f) { + pass.m_executeCallback = [this, &rendererState](const RenderGraph&) { + // Now upscale it to backbuffer with sharpening + auto sharpenFactor = rendererState.RendererSettings.SharpenFactor; + PfxRenderer->GetFSR1()->Apply( + HDRBackBuffer->GetShaderResView(), + Backbuffer->GetRenderTargetView(), + GetResolution(), + GetBackbufferResolution(), + sharpenFactor > 0.001f, + 1.0f - sharpenFactor ); + }; + } ); + } else if (rendererState.RendererSettings.SharpeningMode + && rendererState.RendererSettings.SharpenFactor > 0.0f ) { - { - auto _ = RecordGraphicsEvent( L"Copy into native-size backbuffer" ); - PfxRenderer->CopyTextureToRTV( HDRBackBuffer->GetShaderResView(), Backbuffer->GetRenderTargetView(), GetBackbufferResolution() ); - } + graph.AddPass( L"Sharpen", [&]( RGBuilder& builder, RenderPass& pass ) { + builder.Write( backBufferHandle ); - switch ( rendererState.RendererSettings.SharpeningMode ) { - case GothicRendererSettings::SHARPEN_SIMPLE: - if ( !FeatureLevel10Compatibility ) { - auto _ = RecordGraphicsEvent( L"ApplySimpleSharpen" ); - PfxRenderer->RenderSimpleSharpen( Backbuffer->GetShaderResView(), GetBackbufferResolution(), Backbuffer->GetRenderTargetView(), GetBackbufferResolution(), *GetPfxRenderer()->GetBackbufferTempBuffer()); - GetContext()->PSSetSamplers( 0, 1, DefaultSamplerState.GetAddressOf() ); + pass.m_executeCallback = [this, &rendererState](const RenderGraph&) { + { + auto _ = RecordGraphicsEvent( L"Copy into native-size backbuffer" ); + PfxRenderer->CopyTextureToRTV( HDRBackBuffer->GetShaderResView(), Backbuffer->GetRenderTargetView(), GetBackbufferResolution() ); } - break; - case GothicRendererSettings::SHARPEN_CAS: - if ( !FeatureLevel10Compatibility ) { - auto _ = RecordGraphicsEvent( L"ApplyCAS" ); - PfxRenderer->RenderCAS( Backbuffer->GetShaderResView(), GetBackbufferResolution(), Backbuffer->GetRenderTargetView(), GetBackbufferResolution(), *GetPfxRenderer()->GetBackbufferTempBuffer()); - GetContext()->PSSetSamplers( 0, 1, DefaultSamplerState.GetAddressOf() ); + switch ( rendererState.RendererSettings.SharpeningMode ) { + case GothicRendererSettings::SHARPEN_SIMPLE: + if ( !FeatureLevel10Compatibility ) { + auto _ = RecordGraphicsEvent( L"ApplySimpleSharpen" ); + PfxRenderer->RenderSimpleSharpen( Backbuffer->GetShaderResView(), GetBackbufferResolution(), Backbuffer->GetRenderTargetView(), GetBackbufferResolution(), *GetPfxRenderer()->GetBackbufferTempBuffer()); + } + break; + + case GothicRendererSettings::SHARPEN_CAS: + if ( !FeatureLevel10Compatibility ) { + auto _ = RecordGraphicsEvent( L"ApplyCAS" ); + PfxRenderer->RenderCAS( Backbuffer->GetShaderResView(), GetBackbufferResolution(), Backbuffer->GetRenderTargetView(), GetBackbufferResolution(), *GetPfxRenderer()->GetBackbufferTempBuffer()); + } + break; } - break; - } + GetContext()->PSSetSamplers( 0, 1, DefaultSamplerState.GetAddressOf() ); + }; + } ); + } else { + graph.AddPass( L"Copy into native-size backbuffer", [&]( RGBuilder& builder, RenderPass& pass ) { + builder.Write( backBufferHandle ); - } else { - auto _ = RecordGraphicsEvent( L"Copy into native-size backbuffer" ); - PfxRenderer->CopyTextureToRTV( HDRBackBuffer->GetShaderResView(), Backbuffer->GetRenderTargetView(), GetBackbufferResolution() ); - } + pass.m_executeCallback = [this, &rendererState](const RenderGraph&) { + PfxRenderer->CopyTextureToRTV( HDRBackBuffer->GetShaderResView(), Backbuffer->GetRenderTargetView(), GetBackbufferResolution() ); + }; + } ); } + graph.Compile(); + graph.Execute(); + // Below this, we assume UI/HUD rendering rendererState.RendererInfo.RenderStage = STAGE_DRAW_HUD; From 12ef4b6b517026b730cde2ec6568818360e9eb94 Mon Sep 17 00:00:00 2001 From: kirides <13602143+kirides@users.noreply.github.com> Date: Mon, 23 Feb 2026 23:57:08 +0100 Subject: [PATCH 07/13] and make use of more GetPhysicalTexture. One day we'll have this graph completed.... --- D3D11Engine/D3D11GraphicsEngine.cpp | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/D3D11Engine/D3D11GraphicsEngine.cpp b/D3D11Engine/D3D11GraphicsEngine.cpp index cb4aecd6..c6fa6992 100644 --- a/D3D11Engine/D3D11GraphicsEngine.cpp +++ b/D3D11Engine/D3D11GraphicsEngine.cpp @@ -3139,13 +3139,16 @@ XRESULT D3D11GraphicsEngine::OnStartWorldRendering() { && rendererState.RendererSettings.Upscaler == GothicRendererSettings::E_Upscaler::UPSCALER_FSR_1 ) { graph.AddPass( L"FSR 1 Upscale", [&]( RGBuilder& builder, RenderPass& pass ) { + builder.Read( backBufferHandle ); builder.Write( backBufferHandle ); - pass.m_executeCallback = [this, &rendererState](const RenderGraph&) { + pass.m_executeCallback = [this, &rendererState, backBufferHandle](const RenderGraph& graph) { + auto backbufferTex = graph.GetPhysicalTexture( backBufferHandle ); + // Now upscale it to backbuffer with sharpening auto sharpenFactor = rendererState.RendererSettings.SharpenFactor; PfxRenderer->GetFSR1()->Apply( - HDRBackBuffer->GetShaderResView(), + backbufferTex->GetShaderResView(), Backbuffer->GetRenderTargetView(), GetResolution(), GetBackbufferResolution(), @@ -3157,12 +3160,14 @@ XRESULT D3D11GraphicsEngine::OnStartWorldRendering() { && rendererState.RendererSettings.SharpenFactor > 0.0f ) { graph.AddPass( L"Sharpen", [&]( RGBuilder& builder, RenderPass& pass ) { + builder.Read( backBufferHandle ); builder.Write( backBufferHandle ); - pass.m_executeCallback = [this, &rendererState](const RenderGraph&) { + pass.m_executeCallback = [this, &rendererState, backBufferHandle](const RenderGraph& graph) { + auto backbufferTex = graph.GetPhysicalTexture( backBufferHandle ); { auto _ = RecordGraphicsEvent( L"Copy into native-size backbuffer" ); - PfxRenderer->CopyTextureToRTV( HDRBackBuffer->GetShaderResView(), Backbuffer->GetRenderTargetView(), GetBackbufferResolution() ); + PfxRenderer->CopyTextureToRTV( backbufferTex->GetShaderResView(), Backbuffer->GetRenderTargetView(), GetBackbufferResolution() ); } switch ( rendererState.RendererSettings.SharpeningMode ) { @@ -3187,8 +3192,9 @@ XRESULT D3D11GraphicsEngine::OnStartWorldRendering() { graph.AddPass( L"Copy into native-size backbuffer", [&]( RGBuilder& builder, RenderPass& pass ) { builder.Write( backBufferHandle ); - pass.m_executeCallback = [this, &rendererState](const RenderGraph&) { - PfxRenderer->CopyTextureToRTV( HDRBackBuffer->GetShaderResView(), Backbuffer->GetRenderTargetView(), GetBackbufferResolution() ); + pass.m_executeCallback = [this, &rendererState, backBufferHandle](const RenderGraph& graph) { + auto backbufferTex = graph.GetPhysicalTexture( backBufferHandle ); + PfxRenderer->CopyTextureToRTV( backbufferTex->GetShaderResView(), Backbuffer->GetRenderTargetView(), GetBackbufferResolution() ); }; } ); } From fd7fe4259ce7dfb9fcdabf830d8351821592224e Mon Sep 17 00:00:00 2001 From: kirides <13602143+kirides@users.noreply.github.com> Date: Tue, 24 Feb 2026 00:26:31 +0100 Subject: [PATCH 08/13] fix crash when drawing Rain shadowmap, always "draw" lighting, add frustum for rain shadowmap --- D3D11Engine/D3D11Effect.cpp | 6 + D3D11Engine/D3D11GraphicsEngine.cpp | 225 ++++++++++++++++++++++++++-- D3D11Engine/D3D11GraphicsEngine.h | 3 + D3D11Engine/D3D11ShadowMap.cpp | 155 +++++++++---------- D3D11Engine/D3D11ShadowMap.h | 2 +- D3D11Engine/GothicAPI.h | 1 + 6 files changed, 301 insertions(+), 91 deletions(-) diff --git a/D3D11Engine/D3D11Effect.cpp b/D3D11Engine/D3D11Effect.cpp index a0c75ee2..7e401a33 100644 --- a/D3D11Engine/D3D11Effect.cpp +++ b/D3D11Engine/D3D11Effect.cpp @@ -531,6 +531,12 @@ XRESULT D3D11Effect::DrawRainShadowmap() { XMStoreFloat4x4( &cr.ProjectionReplacement, XMMatrixTranspose( crProjectionReplacement ) ); XMStoreFloat3( &cr.PositionReplacement, p ); XMStoreFloat3( &cr.LookAtReplacement, lookAt ); + + cr.frustum.BuildOrthographic( crViewReplacement, + size * legacySingleShadowMapScaleFactor, + size * legacySingleShadowMapScaleFactor, + 1.0f, + 20000.f ); // Replace gothics camera Engine::GAPI->SetCameraReplacementPtr( &cr ); diff --git a/D3D11Engine/D3D11GraphicsEngine.cpp b/D3D11Engine/D3D11GraphicsEngine.cpp index c6fa6992..5610c502 100644 --- a/D3D11Engine/D3D11GraphicsEngine.cpp +++ b/D3D11Engine/D3D11GraphicsEngine.cpp @@ -2803,14 +2803,14 @@ XRESULT D3D11GraphicsEngine::OnStartWorldRendering() { builder.Write( backBufferHandle ); pass.m_executeCallback = [this, colorResource, normalsResource, specularResource](const RenderGraph& graph)-> void { - if ( !Engine::GAPI->GetRendererState().RendererSettings.EnableShadows ) { - return; - } auto colorTexture = graph.GetPhysicalTexture(colorResource); auto normalsTexture = graph.GetPhysicalTexture(normalsResource); auto specularTexture = graph.GetPhysicalTexture(specularResource); - ShadowMaps->PrepareRender(); + if ( Engine::GAPI->GetRendererState().RendererSettings.EnableShadows ) { + // Cascades only get rendered if this is enabled. + ShadowMaps->PrepareRender(); + } ShadowMaps->DrawLighting(m_FrameLights, *colorTexture, *normalsTexture, @@ -4627,6 +4627,182 @@ void XM_CALLCONV D3D11GraphicsEngine::DrawWorldAround_Layered( } } +void D3D11GraphicsEngine::ShadowPass_DrawWorldMesh_Indirect(const std::vector& visibleSections) +{ + float alphaRef = Engine::GAPI->GetRendererState().GraphicsState.FF_AlphaRef; + bool linearDepth = (Engine::GAPI->GetRendererState().GraphicsState.FF_GSwitches & + GSWITCH_LINEAR_DEPTH) != 0; + + auto drawMultiIndexedInstancedIndirect = Engine::GAPI->GetRendererState().RendererSettings.DebugSettings.FeatureSet.UseMDI + ? DrawMultiIndexedInstancedIndirect + : Stub_DrawMultiIndexedInstancedIndirect; + + if ( Engine::GAPI->GetRendererState().RendererSettings.FastShadows ) + { + if ( !linearDepth ) // Only unbind when not rendering linear depth + { + // Unbind PS + Context->PSSetShader( nullptr, nullptr, 0 ); + } + + for ( const WorldMeshSectionInfo* section : visibleSections ) { + if ( section->FullStaticMesh ) { + Engine::GAPI->DrawMeshInfo( nullptr, section->FullStaticMesh ); + } + } + return; + } + // Collect all meshes first, then batch by alpha requirement + static thread_local std::vector opaqueDrawArgs; + static thread_local std::vector> alphaMeshes; + opaqueDrawArgs.clear(); + alphaMeshes.clear(); + + for ( const WorldMeshSectionInfo* section : visibleSections ) { + for ( const auto& meshPair : section->WorldMeshes ) { + // Skip non-standard materials (water, portals, etc.) + if ( meshPair.first.Info->MaterialType != MaterialInfo::MT_None ) + continue; + + zCTexture* tex = meshPair.first.Material ? meshPair.first.Material->GetTexture() : nullptr; + + if ( tex && tex->HasAlphaChannel() && alphaRef > 0.0f ) { + // Need alpha testing - cache texture + if ( tex->CacheIn( 0.6f ) == zRES_CACHED_IN ) { + alphaMeshes.emplace_back( tex, meshPair.second ); + } + } else { + D3D11_DRAW_INDEXED_INSTANCED_INDIRECT_ARGS args; + args.IndexCountPerInstance = static_cast(meshPair.second->Indices.size()); + args.InstanceCount = 1; + args.StartIndexLocation = meshPair.second->BaseIndexLocation; + args.BaseVertexLocation = 0; + args.StartInstanceLocation = 0; + opaqueDrawArgs.push_back( args ); + } + + Engine::GAPI->GetRendererState().RendererInfo.FrameDrawnTriangles += + meshPair.second->Indices.size() / 3; + } + } + + // Draw all opaque meshes without pixel shader (depth only) using MDI + if ( !opaqueDrawArgs.empty() ) { + if ( !linearDepth ) { + // Unbind PS for depth-only rendering + Context->PSSetShader( nullptr, nullptr, 0 ); + } + + // Initialize or resize the indirect buffer if needed + const size_t requiredSize = opaqueDrawArgs.size() * sizeof( D3D11_DRAW_INDEXED_INSTANCED_INDIRECT_ARGS ); + + if ( !WorldMeshIndirectBuffer || WorldMeshIndirectBuffer->GetSizeInBytes() < requiredSize ) { + WorldMeshIndirectBuffer.reset( new D3D11IndirectBuffer ); + WorldMeshIndirectBuffer->Init( + opaqueDrawArgs.data(), requiredSize, + D3D11IndirectBuffer::B_INDEXBUFFER, D3D11IndirectBuffer::U_DYNAMIC, + D3D11IndirectBuffer::CA_WRITE ); + } else { + WorldMeshIndirectBuffer->UpdateBuffer( opaqueDrawArgs.data(), requiredSize ); + } + + // Execute multi-draw indirect call for all opaque meshes + // DrawMultiIndexedInstancedIndirect falls back to individual DrawIndexedInstancedIndirect + // calls via Stub_DrawMultiIndexedInstancedIndirect if hardware doesn't support MDI + drawMultiIndexedInstancedIndirect( Context.Get(), + static_cast(opaqueDrawArgs.size()), + WorldMeshIndirectBuffer->GetIndirectBuffer().Get(), + 0, + sizeof( D3D11_DRAW_INDEXED_INSTANCED_INDIRECT_ARGS ) ); + } + + // Draw alpha-tested meshes with texture binding + if ( !alphaMeshes.empty() ) { + // Sort by texture to minimize binding changes + std::sort( alphaMeshes.begin(), alphaMeshes.end(), + []( const auto& a, const auto& b ) { return a.first < b.first; } ); + + ActivePS->Apply(); + zCTexture* lastTex = nullptr; + + for ( const auto& [tex, mesh] : alphaMeshes ) { + if ( tex != lastTex ) { + if (tex->CacheIn( 0.6f ) == zRES_CACHED_OUT) { + continue; + } + tex->Bind( 0 ); + lastTex = tex; + } + DrawVertexBufferIndexedUINT( nullptr, nullptr, + mesh->Indices.size(), mesh->BaseIndexLocation ); + } + } +} + +void D3D11GraphicsEngine::ShadowPass_DrawWorldMesh(const std::vector& visibleSections) +{ + float alphaRef = Engine::GAPI->GetRendererState().GraphicsState.FF_AlphaRef; + bool linearDepth = (Engine::GAPI->GetRendererState().GraphicsState.FF_GSwitches & + GSWITCH_LINEAR_DEPTH) != 0; + + static thread_local std::vector opaqueMeshes; + static thread_local std::vector> alphaMeshes; + opaqueMeshes.clear(); + alphaMeshes.clear(); + + for ( const WorldMeshSectionInfo* section : visibleSections ) { + for ( const auto& meshPair : section->WorldMeshes ) { + // Skip non-standard materials (water, portals, etc.) + if ( meshPair.first.Info->MaterialType != MaterialInfo::MT_None ) + continue; + + zCTexture* tex = meshPair.first.Material ? meshPair.first.Material->GetTexture() : nullptr; + + if ( tex && tex->HasAlphaChannel() && alphaRef > 0.0f ) { + // Need alpha testing - cache texture + if ( tex->CacheIn( 0.6f ) == zRES_CACHED_IN ) { + alphaMeshes.emplace_back( tex, meshPair.second ); + } + } else { + opaqueMeshes.push_back( meshPair.second ); + } + } + } + + // Draw all opaque meshes without pixel shader (depth only) + if ( !opaqueMeshes.empty() ) { + if ( !linearDepth ) // Only unbind when not rendering linear depth + { + // Unbind PS + Context->PSSetShader( nullptr, nullptr, 0 ); + } + + for ( WorldMeshInfo* mesh : opaqueMeshes ) { + DrawVertexBufferIndexedUINT( nullptr, nullptr, + mesh->Indices.size(), mesh->BaseIndexLocation ); + } + } + + // Draw alpha-tested meshes with texture binding + if ( !alphaMeshes.empty() ) { + // Sort by texture to minimize binding changes + std::sort( alphaMeshes.begin(), alphaMeshes.end(), + []( const auto& a, const auto& b ) { return a.first < b.first; } ); + + ActivePS->Apply(); + zCTexture* lastTex = nullptr; + + for ( const auto& [tex, mesh] : alphaMeshes ) { + if ( tex != lastTex ) { + tex->Bind( 0 ); + lastTex = tex; + } + DrawVertexBufferIndexedUINT( nullptr, nullptr, + mesh->Indices.size(), mesh->BaseIndexLocation ); + } + } +} + /** Draws everything around the given position */ void XM_CALLCONV D3D11GraphicsEngine::DrawWorldAroundForWorldShadow( FXMVECTOR position, float sectionRange, @@ -4725,7 +4901,11 @@ void XM_CALLCONV D3D11GraphicsEngine::DrawWorldAroundForWorldShadow( FXMVECTOR p bool colorWritesEnabled = Engine::GAPI->GetRendererState().BlendState.ColorWritesEnabled; float alphaRef = Engine::GAPI->GetRendererState().GraphicsState.FF_AlphaRef; - auto& currentFrustum = params.CascadeCameraReplacements->at(params.CascadeIndex).frustum; + auto& currentFrustum = params.CascadeIndex != -1 + ? params.CascadeCameraReplacements->at(params.CascadeIndex).frustum + : Engine::GAPI->GetCameraReplacementPtr() != nullptr + ? Engine::GAPI->GetCameraReplacementPtr()->frustum + : Frustum::AlwaysContainingFrustum(); if ( Engine::GAPI->GetRendererState().RendererSettings.DrawWorldMesh ) { auto _ = START_TIMING( timer_labels_world_mesh[timerLabelIndex] ); @@ -4764,10 +4944,31 @@ void XM_CALLCONV D3D11GraphicsEngine::DrawWorldAroundForWorldShadow( FXMVECTOR p } if ( Engine::GAPI->GetRendererState().RendererSettings.DrawVOBs ) { - auto renderQueue = ShadowMaps->GetRenderQueue( params.CascadeIndex ); - renderQueue->ProcessQueue(); + static std::vector potentialCasters; + std::vector& vobs = potentialCasters; + if (params.CascadeIndex != -1) { + auto renderQueue = ShadowMaps->GetRenderQueue( params.CascadeIndex ); + renderQueue->ProcessQueue(); - auto& vobs = renderQueue->GetVobs(); + vobs = renderQueue->GetVobs(); + } else { + static std::vector _1; + static std::vector _2; + potentialCasters.reserve(1024); + potentialCasters.clear(); + + LegacyRenderQueueProxy q(potentialCasters, _1, _2); + RndCullContext ctx; + ctx.queue = &q; + ctx.cameraPosition = Engine::GAPI->GetCameraPosition(); + ctx.stage = RenderStage::STAGE_DRAW_WORLD; + ctx.frustum = currentFrustum; + ctx.drawDistances.OutdoorVobs = Engine::GAPI->GetRendererState().RendererSettings.OutdoorVobDrawRadius; + ctx.drawDistances.OutdoorVobsSmall = Engine::GAPI->GetRendererState().RendererSettings.OutdoorSmallVobDrawRadius; + ctx.drawDistances.IndoorVobs = Engine::GAPI->GetRendererState().RendererSettings.IndoorVobDrawRadius; + ctx.drawDistances.VisualFX = Engine::GAPI->GetRendererState().RendererSettings.VisualFXDrawRadius; + Engine::GAPI->CollectVisibleVobs( ctx ); + } // clear any residue of main render pass for ( auto const& staticMeshVisual : Engine::GAPI->GetStaticMeshVisuals() ) { @@ -4934,7 +5135,8 @@ void XM_CALLCONV D3D11GraphicsEngine::DrawWorldAroundForWorldShadow( FXMVECTOR p static std::vector animatedSkeletalMeshVobs; animatedSkeletalMeshVobs.clear(); - const bool isLastCascade = params.CascadeIndex == params.CascadeSplits.size() - 2; + const bool isLastCascade = params.CascadeSplits.size() == 0 + || params.CascadeIndex == params.CascadeSplits.size() - 2; for ( auto const& skeletalMeshVob : Engine::GAPI->GetSkeletalMeshVobs() ) { if ( !skeletalMeshVob->VisualInfo ) continue; @@ -4949,9 +5151,6 @@ void XM_CALLCONV D3D11GraphicsEngine::DrawWorldAroundForWorldShadow( FXMVECTOR p } if ( enableCulling && !isLastCascade ) { - // Frustum culling using a bounding sphere - // Use the mesh size as radius, centered at the vob position - if ( currentFrustum.Contains( skeletalMeshVob->Vob->GetBBox()) == DirectX::ContainmentType::DISJOINT) { // Not hitting our frustum and not the active view. continue; @@ -5464,7 +5663,7 @@ XRESULT D3D11GraphicsEngine::DrawFrameAlphaMeshes() } // Draw batch - DrawInstanced( mi->MeshVertexBuffer.get(), mi->MeshIndexBuffer.get(), mi->Indices.size(), + DrawInstanced( mi->MeshVertexBuffer, mi->MeshIndexBuffer, mi->Indices.size(), DynamicInstancingBuffer.get(), sizeof( VobInstanceInfo ), instances.size(), sizeof( ExVertexStruct ), vi->StartInstanceNum ); diff --git a/D3D11Engine/D3D11GraphicsEngine.h b/D3D11Engine/D3D11GraphicsEngine.h index 66a9817c..e3b667f2 100644 --- a/D3D11Engine/D3D11GraphicsEngine.h +++ b/D3D11Engine/D3D11GraphicsEngine.h @@ -234,6 +234,9 @@ class D3D11GraphicsEngine : public D3D11GraphicsEngineBase { virtual void DrawVobSingle( VobInfo* vob, zCCamera& camera ) override; /** Draws everything around the given position */ + void ShadowPass_DrawWorldMesh_Indirect(const std::vector& visibleSections); + void ShadowPass_DrawWorldMesh(const std::vector& visibleSections); + void XM_CALLCONV DrawWorldAroundForWorldShadow( FXMVECTOR position, float sectionRange, const RenderShadowmapsParams& params ); void XM_CALLCONV DrawWorldAround( FXMVECTOR position, float range, diff --git a/D3D11Engine/D3D11ShadowMap.cpp b/D3D11Engine/D3D11ShadowMap.cpp index 6a9f3858..3ccfbf48 100644 --- a/D3D11Engine/D3D11ShadowMap.cpp +++ b/D3D11Engine/D3D11ShadowMap.cpp @@ -499,9 +499,13 @@ std::vector D3D11ShadowMap::ComputeCascadeSplits( float nearPlane, float } XRESULT D3D11ShadowMap::DrawPointlightShadows( std::vector& lights ) { + auto& settings = Engine::GAPI->GetRendererState().RendererSettings; + if (settings.EnablePointlightShadows <= 0) { + return XR_SUCCESS; + } + auto graphicsEngine = reinterpret_cast(Engine::GraphicsEngine); auto _ = graphicsEngine->RecordGraphicsEvent( L"DrawPointlightShadows" ); - auto& settings = Engine::GAPI->GetRendererState().RendererSettings; static const XMVECTORF32 xmFltMax = { { { FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX } } }; graphicsEngine->SetDefaultStates(); @@ -520,94 +524,92 @@ XRESULT D3D11ShadowMap::DrawPointlightShadows( std::vector& light bool partialShadowUpdate = settings.PartialDynamicShadowUpdates; // Draw pointlight shadows - if ( settings.EnablePointlightShadows > 0 ) { - std::list importantUpdates; + std::list importantUpdates; - for ( auto const& light : lights ) { - // Create shadowmap in case we should have one but haven't got it yet - if ( !light->LightShadowBuffers && light->UpdateShadows ) { - graphicsEngine->CreateShadowedPointLight( &light->LightShadowBuffers, light ); - } + for ( auto const& light : lights ) { + // Create shadowmap in case we should have one but haven't got it yet + if ( !light->LightShadowBuffers && light->UpdateShadows ) { + graphicsEngine->CreateShadowedPointLight( &light->LightShadowBuffers, light ); + } - if ( light->LightShadowBuffers ) { - // Check if this lights even needs an update - bool needsUpdate = static_cast(light->LightShadowBuffers)->NeedsUpdate(); - bool isInited = static_cast(light->LightShadowBuffers)->IsInited(); - - // Add to the updatequeue if it does - if ( isInited && (needsUpdate || light->UpdateShadows) ) { - // Always update the light if the light itself moved - if ( partialShadowUpdate && !needsUpdate ) { - // Only add once. This list should never be very big, so it should - // be ok to search it like this This needs to be done to make sure a - // light will get updated only once and won't block the queue - if ( std::find( graphicsEngine->FrameShadowUpdateLights.begin(), - graphicsEngine->FrameShadowUpdateLights.end(), - light ) == graphicsEngine->FrameShadowUpdateLights.end() ) { - // Always render the closest light to the playervob, so the player - // doesn't flicker when moving - float d; - XMStoreFloat( &d, XMVector3LengthSq( light->Vob->GetPositionWorldXM() - vPlayerPosition ) ); - - float range = light->Vob->GetLightRange(); - if ( d < range * range && - importantUpdates.size() < MAX_IMPORTANT_LIGHT_UPDATES ) { - importantUpdates.emplace_back( light ); - } else { - graphicsEngine->FrameShadowUpdateLights.emplace_back( light ); - } - } - } else { + if ( light->LightShadowBuffers ) { + // Check if this lights even needs an update + bool needsUpdate = static_cast(light->LightShadowBuffers)->NeedsUpdate(); + bool isInited = static_cast(light->LightShadowBuffers)->IsInited(); + + // Add to the updatequeue if it does + if ( isInited && (needsUpdate || light->UpdateShadows) ) { + // Always update the light if the light itself moved + if ( partialShadowUpdate && !needsUpdate ) { + // Only add once. This list should never be very big, so it should + // be ok to search it like this This needs to be done to make sure a + // light will get updated only once and won't block the queue + if ( std::find( graphicsEngine->FrameShadowUpdateLights.begin(), + graphicsEngine->FrameShadowUpdateLights.end(), + light ) == graphicsEngine->FrameShadowUpdateLights.end() ) { // Always render the closest light to the playervob, so the player // doesn't flicker when moving float d; XMStoreFloat( &d, XMVector3LengthSq( light->Vob->GetPositionWorldXM() - vPlayerPosition ) ); - float range = light->Vob->GetLightRange() * 1.5f; - - // If the engine said this light should be updated, then do so. If - // the light said this - if ( needsUpdate || d < range * range ) + float range = light->Vob->GetLightRange(); + if ( d < range * range && + importantUpdates.size() < MAX_IMPORTANT_LIGHT_UPDATES ) { importantUpdates.emplace_back( light ); + } else { + graphicsEngine->FrameShadowUpdateLights.emplace_back( light ); + } } + } else { + // Always render the closest light to the playervob, so the player + // doesn't flicker when moving + float d; + XMStoreFloat( &d, XMVector3LengthSq( light->Vob->GetPositionWorldXM() - vPlayerPosition ) ); + + float range = light->Vob->GetLightRange() * 1.5f; + + // If the engine said this light should be updated, then do so. If + // the light said this + if ( needsUpdate || d < range * range ) + importantUpdates.emplace_back( light ); } } } + } - // Render the closest light - for ( auto const& importantUpdate : importantUpdates ) { - static_cast( importantUpdate->LightShadowBuffers )->RenderCubemap( importantUpdate->UpdateShadows ); - importantUpdate->UpdateShadows = false; - } + // Render the closest light + for ( auto const& importantUpdate : importantUpdates ) { + static_cast( importantUpdate->LightShadowBuffers )->RenderCubemap( importantUpdate->UpdateShadows ); + importantUpdate->UpdateShadows = false; + } - // Update only a fraction of lights, but at least some - int n = std::max( - (UINT)NUM_MIN_FRAME_SHADOW_UPDATES, - (UINT)(graphicsEngine->FrameShadowUpdateLights.size() / NUM_FRAME_SHADOW_UPDATES) ); - while ( !graphicsEngine->FrameShadowUpdateLights.empty() ) { - auto light = graphicsEngine->FrameShadowUpdateLights.front(); - if ( !light ) { - graphicsEngine->FrameShadowUpdateLights.pop_front(); - continue; - } - D3D11PointLight* l = static_cast(light->LightShadowBuffers); - if ( !l ) { - graphicsEngine->FrameShadowUpdateLights.pop_front(); - continue; - } - // Check if we have to force this light to update itself (NPCs moving around, for example) - bool force = light->UpdateShadows; - light->UpdateShadows = false; + // Update only a fraction of lights, but at least some + int n = std::max( + (UINT)NUM_MIN_FRAME_SHADOW_UPDATES, + (UINT)(graphicsEngine->FrameShadowUpdateLights.size() / NUM_FRAME_SHADOW_UPDATES) ); + while ( !graphicsEngine->FrameShadowUpdateLights.empty() ) { + auto light = graphicsEngine->FrameShadowUpdateLights.front(); + if ( !light ) { + graphicsEngine->FrameShadowUpdateLights.pop_front(); + continue; + } + D3D11PointLight* l = static_cast(light->LightShadowBuffers); + if ( !l ) { + graphicsEngine->FrameShadowUpdateLights.pop_front(); + continue; + } + // Check if we have to force this light to update itself (NPCs moving around, for example) + bool force = light->UpdateShadows; + light->UpdateShadows = false; - l->RenderCubemap( force ); - graphicsEngine->DebugPointlight = l; + l->RenderCubemap( force ); + graphicsEngine->DebugPointlight = l; - graphicsEngine->FrameShadowUpdateLights.pop_front(); + graphicsEngine->FrameShadowUpdateLights.pop_front(); - // Only update n lights - n--; - if ( n <= 0 ) break; - } + // Only update n lights + n--; + if ( n <= 0 ) break; } return XR_SUCCESS; } @@ -656,11 +658,11 @@ XRESULT D3D11ShadowMap::DrawWorldShadow( ) return XR_SUCCESS; } -XRESULT D3D11ShadowMap::DrawRainShadomap() { +XRESULT D3D11ShadowMap::DrawRainShadowmap() { // Draw rainmap, if raining if ( Engine::GAPI->GetSceneWetness() > 0.00001f ) { auto graphicsEngine = reinterpret_cast(Engine::GraphicsEngine); - auto _ = graphicsEngine->RecordGraphicsEvent( L"DrawLighting" ); + auto _ = graphicsEngine->RecordGraphicsEvent( L"DrawRainShadowmap" ); graphicsEngine->Effects->DrawRainShadowmap(); } @@ -837,7 +839,6 @@ XRESULT D3D11ShadowMap::DrawLighting( RenderToTextureBuffer& specular, RenderToTextureBuffer& depthCopy) { auto graphicsEngine = reinterpret_cast(Engine::GraphicsEngine); - auto _ = graphicsEngine->RecordGraphicsEvent( L"DrawLighting" ); auto& settings = Engine::GAPI->GetRendererState().RendererSettings; graphicsEngine->SetDefaultStates(); @@ -849,9 +850,9 @@ XRESULT D3D11ShadowMap::DrawLighting( graphicsEngine->SetDefaultStates(); - DrawRainShadomap(); + DrawRainShadowmap(); - Engine::GAPI->SetFarPlane(settings.SectionDrawRadius * WORLD_SECTION_SIZE ); + Engine::GAPI->SetFarPlane(static_cast(settings.SectionDrawRadius) * WORLD_SECTION_SIZE ); DrawPointlightLights(lights, color, normals, specular, depthCopy); diff --git a/D3D11Engine/D3D11ShadowMap.h b/D3D11Engine/D3D11ShadowMap.h index bcadb383..8f4dd79b 100644 --- a/D3D11Engine/D3D11ShadowMap.h +++ b/D3D11Engine/D3D11ShadowMap.h @@ -109,7 +109,7 @@ class D3D11ShadowMap { static std::vector ComputeCascadeSplits( float nearPlane, float farPlane, size_t numCascades, float lambda = 0.95f, float bias = 1.0f ); XRESULT DrawPointlightShadows(std::vector& lights); XRESULT DrawWorldShadow(); - XRESULT DrawRainShadomap(); + XRESULT DrawRainShadowmap(); XRESULT DrawPointlightLights(std::vector& lights, RenderToTextureBuffer& color, RenderToTextureBuffer& normals, RenderToTextureBuffer & specular, RenderToTextureBuffer& depthCopy); diff --git a/D3D11Engine/GothicAPI.h b/D3D11Engine/GothicAPI.h index be16eaca..9c54bf7f 100644 --- a/D3D11Engine/GothicAPI.h +++ b/D3D11Engine/GothicAPI.h @@ -597,6 +597,7 @@ class GothicAPI { /** Sets the CameraReplacementPtr */ void SetCameraReplacementPtr( CameraReplacement* ptr ) { CameraReplacementPtr = ptr; } + CameraReplacement* GetCameraReplacementPtr() const { return CameraReplacementPtr; } /** Lets Gothic draw its sky */ void DrawSkyGothicOriginal(); From 2bb108ddbc11526940da5b90158ff2feacb55cb5 Mon Sep 17 00:00:00 2001 From: kirides <13602143+kirides@users.noreply.github.com> Date: Fri, 16 Jan 2026 23:35:43 +0100 Subject: [PATCH 09/13] Skeletal mesh drawing using Instancing batching: fix rendering because of broken view matrix, shortcut for single instance clean up some residue fix node batches fluctuating, simplify unordered_map usage use meshlib to identify duplicate meshes, aswell as zCProgMeshProto->progId which should be unique-enough clean up hashing, also use progid for skeletal meshes like wolfs and npcs make skeletal batch key texture aware. use hash_combine ensure we call UpdateMeshLibTexAniState before accessing GetAniTexture(), use std::string_view for hashing add memory locations for G1 and G2, ensure updated mm->GetTexAniState()->UpdateTexList(); for batched attachments disable skeletal mesh batching for g1 sequel builds ensure all cascadeCRs are populated ahead of time work on top of motion vectors fix ide warning due to static friend rebase on per-object-velocity, fix indoor vobs being drawn when outdoors, don't rely as much on global WorldTransform fix merge conflicts, batch more meshes when rendering shadows, as we mostly dont need textures do some performance profiling, reduce need to cull against frustum, beginnings of trying to implement a renderqueue add todo things --- D3D11Engine/ConstantBufferStructs.h | 29 + D3D11Engine/D3D11Engine.vcxproj | 1 + D3D11Engine/D3D11Engine.vcxproj.filters | 1 + D3D11Engine/D3D11GraphicsEngine.cpp | 507 +++++++++++++- D3D11Engine/D3D11GraphicsEngine.h | 26 +- D3D11Engine/D3D11GraphicsEngineBase.cpp | 1 - D3D11Engine/D3D11ShaderManager.cpp | 14 +- D3D11Engine/D3D11StructuredBuffer.h | 123 ++++ D3D11Engine/GothicAPI.cpp | 641 ++++++++++-------- D3D11Engine/GothicAPI.h | 33 +- D3D11Engine/GothicMemoryLocations1_08k.h | 3 + D3D11Engine/GothicMemoryLocations1_12f.h | 8 + D3D11Engine/GothicMemoryLocations2_6_fix.h | 3 + D3D11Engine/ImGuiShim.cpp | 1 + D3D11Engine/Shaders/VS_ExNodeInstanced.hlsl | 74 ++ .../Shaders/VS_ExNodeInstancedCube.hlsl | 69 ++ .../Shaders/VS_ExSkeletalInstanced.hlsl | 106 +++ .../Shaders/VS_ExSkeletalInstancedCube.hlsl | 84 +++ D3D11Engine/Toolbox.h | 20 + D3D11Engine/oCNPC.h | 4 +- D3D11Engine/pch.h | 1 + D3D11Engine/zCMaterial.h | 4 +- D3D11Engine/zCMorphMesh.h | 4 + D3D11Engine/zCObject.h | 10 + D3D11Engine/zCProgMeshProto.h | 8 + D3D11Engine/zCTexture.h | 1 - D3D11Engine/zCVisual.h | 9 + D3D11Engine/zCVob.h | 15 +- 28 files changed, 1479 insertions(+), 321 deletions(-) create mode 100644 D3D11Engine/D3D11StructuredBuffer.h create mode 100644 D3D11Engine/Shaders/VS_ExNodeInstanced.hlsl create mode 100644 D3D11Engine/Shaders/VS_ExNodeInstancedCube.hlsl create mode 100644 D3D11Engine/Shaders/VS_ExSkeletalInstanced.hlsl create mode 100644 D3D11Engine/Shaders/VS_ExSkeletalInstancedCube.hlsl diff --git a/D3D11Engine/ConstantBufferStructs.h b/D3D11Engine/ConstantBufferStructs.h index 12fd2343..bc04ea0e 100644 --- a/D3D11Engine/ConstantBufferStructs.h +++ b/D3D11Engine/ConstantBufferStructs.h @@ -223,6 +223,35 @@ struct VS_ExConstantBuffer_PerInstanceSkeletal { float3 PI_Pad1; }; +// Maximum bones per skeleton (must match shader) +static const int NUM_MAX_BONES = 96; + +// Maximum instances per batch (adjustable based on GPU memory) +static const int MAX_SKELETAL_INSTANCES = 64; + +// Per-instance data for instanced skeletal rendering +// Stored in a StructuredBuffer for GPU access +__declspec(align(16)) struct SkeletalInstanceData { + XMFLOAT4X4 World; // World transform + XMFLOAT4X4 PrevWorld; // Previous World transform + float4 Color; // Model color (RGBA) + float Fatness; // Model fatness for vertex displacement + float Scale; // Uniform scale factor + uint32_t BoneOffset; // Offset into the bone transform buffer + uint32_t Padding; // Alignment padding +}; + +// Instance data for node attachments (weapons, heads, etc.) +// Must be 16-byte aligned for SIMD operations +__declspec(align(16)) struct NodeAttachmentInstanceData { + XMFLOAT4X4 World; // World transform (includes bone transform) + XMFLOAT4X4 PrevWorld; // Previous World transform (includes bone transform) + float4 Color; // Model color + float Fatness; // Fatness for MMS meshes + float Scaling; // Scaling for MMS meshes + float2 Padding; // Alignment padding +}; + struct ScreenFadeConstantBuffer { float GA_Alpha; float3 GA_Pad; diff --git a/D3D11Engine/D3D11Engine.vcxproj b/D3D11Engine/D3D11Engine.vcxproj index 9790e425..d05f0e6a 100644 --- a/D3D11Engine/D3D11Engine.vcxproj +++ b/D3D11Engine/D3D11Engine.vcxproj @@ -838,6 +838,7 @@ copy "$(OutDir)$(TargetName).pdb" "$(G1_SYSTEM_PATH)\ddraw.pdb" + diff --git a/D3D11Engine/D3D11Engine.vcxproj.filters b/D3D11Engine/D3D11Engine.vcxproj.filters index 7c971b32..e05580c2 100644 --- a/D3D11Engine/D3D11Engine.vcxproj.filters +++ b/D3D11Engine/D3D11Engine.vcxproj.filters @@ -806,6 +806,7 @@ Engine\Graph + diff --git a/D3D11Engine/D3D11GraphicsEngine.cpp b/D3D11Engine/D3D11GraphicsEngine.cpp index 5610c502..65ac8c1d 100644 --- a/D3D11Engine/D3D11GraphicsEngine.cpp +++ b/D3D11Engine/D3D11GraphicsEngine.cpp @@ -46,6 +46,8 @@ #include "ImGuiShim.h" #include "zCModel.h" +#include "zCMorphMesh.h" + #include "zCOption.h" #include "RenderGraph.h" #include "RGBuilder.h" @@ -167,6 +169,7 @@ D3D11GraphicsEngine::D3D11GraphicsEngine() { CachedRefreshRate.Numerator = 0; CachedRefreshRate.Denominator = 0; unionCurrentCustomFontMultiplier = 1.0; + SkeletalMeshBatches = std::unordered_map(); } D3D11GraphicsEngine::~D3D11GraphicsEngine() { @@ -670,6 +673,13 @@ XRESULT D3D11GraphicsEngine::Init() { InverseUnitSphereMesh = new GMesh; InverseUnitSphereMesh->LoadMesh( "system\\GD3D11\\meshes\\icoSphere.obj" ); + // Add to initialization sequence: + if ( FAILED( InitSkeletalInstancingBuffers() ) ) { + LogError() << "Failed to initialize skeletal instancing buffers. Falling back to individual rendering."; + } + + InitNodeAttachmentInstancingBuffer(); + // Create distance-buffers D3D11ConstantBuffer* infiniteRangeConstantBuffer; D3D11ConstantBuffer* outdoorSmallVobsConstantBuffer; @@ -2220,7 +2230,7 @@ XRESULT D3D11GraphicsEngine::DrawSkeletalMesh( SkeletalVobInfo* vi, cb2.PI_ModelColor = color; cb2.PI_ModelFatness = fatness; // Set PrevWorld for motion vectors (use current world if no previous is available) - cb2.PrevWorld = vi->HasValidPrevTransforms ? vi->PrevWorldMatrix : world; + cb2.PrevWorld = vi->HasValidPrevTransforms ? vi->PrevWorldMatrix : cb2.World; ActiveVS->GetConstantBuffer()[1]->UpdateBuffer( &cb2 ); ActiveVS->GetConstantBuffer()[1]->BindToVertexShader( 1 ); @@ -4627,6 +4637,478 @@ void XM_CALLCONV D3D11GraphicsEngine::DrawWorldAround_Layered( } } +// Add near other initialization code + +XRESULT D3D11GraphicsEngine::InitSkeletalInstancingBuffers() { + // Instance data buffer - holds per-instance transforms, colors, etc. + SkeletalInstanceBuffer = std::make_unique>(); + HRESULT hr = SkeletalInstanceBuffer->Init( GetDevice().Get(), MAX_SKELETAL_INSTANCES * 4 ); // Allow some growth + if ( FAILED( hr ) ) { + LogError() << "Failed to create skeletal instance buffer"; + return XR_FAILED; + } + + // Bone transform buffer - holds all bone matrices for all instances + SkeletalBoneBuffer = std::make_unique>(); + hr = SkeletalBoneBuffer->Init( GetDevice().Get(), 2048 ); + if ( FAILED( hr ) ) { + LogError() << "Failed to create skeletal bone buffer"; + return XR_FAILED; + } + + LogInfo() << "Initialized skeletal instancing buffers"; + return XR_SUCCESS; +} + +void D3D11GraphicsEngine::BuildSkeletalMeshBatches( const std::vector& vobs ) { + // Clear previous batches + ClearSkeletalMeshBatches(); + + for ( SkeletalVobInfo* vi : vobs ) { + if ( !vi || !vi->VisualInfo || !vi->Vob ) { + continue; + } + + zCModel* model = static_cast(vi->Vob->GetVisual()); + if ( !model ) { + continue; + } + + // Update textures + model->SetIsVisible( true ); + + SkeletalMeshVisualInfo* visualInfo = dynamic_cast(vi->VisualInfo); + + // Skip if no skeletal meshes (these are MOBs like chests) + if ( visualInfo->SkeletalMeshes.empty() ) { + if ( model->GetMeshSoftSkinList()->NumInArray > 0 ) { + // Just in case somehow we end up without skeletal meshes and they are available + WorldConverter::ExtractSkeletalMeshFromVob( model, visualInfo ); + } + + if ( visualInfo->SkeletalMeshes.empty() ) { + continue; + } + } + + NodeAttachmentBatchKey batchKey = { }; + size_t hashLen = 0; + static size_t largestSeenHash = 0; + model->UpdateMeshLibTexAniState(); + + if ( model->GetMeshLibList() && model->GetMeshLibList()->GetSize() ) { + for ( unsigned int i = 0; i < model->GetMeshLibList()->GetSize(); i++ ) { + auto ptr = model->GetMeshLibList()->Array[i]->MeshLibPtr; + Toolbox::hash_combine( batchKey, (int)ptr ); + hashLen++; + } + if (RenderingStage != DES_SHADOWMAP ) { + for ( auto& [k, v] : visualInfo->SkeletalMeshes ) { + Toolbox::hash_combine( batchKey, std::string_view(k->GetAniTexture()->__GetName().ToChar()) ); + hashLen++; + } + } + } else { + // unique draw, no batching + Toolbox::hash_combine( batchKey, (int)vi->Vob ); + hashLen++; + } + if ( hashLen > largestSeenHash ) { + largestSeenHash = hashLen; + } + for ( size_t i = hashLen; i < largestSeenHash; ++i ) { + // to avoid collisions we need to make sure that shorter hashes don't collide with longer ones that have the same start + Toolbox::hash_combine( batchKey, i ); + } + + auto& batch = SkeletalMeshBatches[batchKey]; + if ( !batch.Vobs.size() ) { + batch.VisualInfo = visualInfo; + batch.Vobs.reserve(25); + } + batch.Vobs.push_back( vi ); + } + + // Build instance data for each batch + static std::vector transforms; + for ( auto& [name, batch] : SkeletalMeshBatches ) { + if ( batch.Vobs.empty() || !batch.VisualInfo ) { + continue; + } + + batch.TotalBoneCount = 0; + batch.InstanceData.clear(); + batch.AllBoneTransforms.clear(); + batch.InstanceData.reserve( batch.Vobs.size() ); + + for ( SkeletalVobInfo* vi : batch.Vobs ) { + zCModel* model = static_cast(vi->Vob->GetVisual()); + if ( !model ) { + continue; + } + + // Get bone transforms + transforms.clear(); + model->GetBoneTransforms( &transforms ); + + if ( transforms.empty() ) { + continue; + } + + // Build instance data + SkeletalInstanceData inst = {}; + + // World matrix with scale + XMMATRIX scale = XMMatrixScalingFromVector( model->GetModelScaleXM() ); + XMMATRIX world = vi->Vob->GetWorldMatrixXM() * scale; + XMStoreFloat4x4( &inst.World, world ); + inst.PrevWorld = vi->HasValidPrevTransforms ? vi->PrevWorldMatrix : inst.World; + + // Model color + if ( Engine::GAPI->GetRendererState().RendererSettings.EnableShadows ) { + inst.Color = float4( 1, 1, 1, 1 ); + } else { + if ( vi->Vob->IsIndoorVob() ) { + inst.Color = DEFAULT_LIGHTMAP_POLY_COLOR; + } else if ( zCPolygon* polygon = vi->Vob->GetGroundPoly() ) { + float3 vobPos = vi->Vob->GetPositionWorld(); + float3 polyLightStat = polygon->GetLightStatAtPos( vobPos ); + inst.Color = float4( polyLightStat.z * inv255f, polyLightStat.y * inv255f, polyLightStat.x * inv255f, 1.f ); + } else { + inst.Color = float4( 1, 1, 1, 1 ); + } + } + + inst.Fatness = model->GetModelFatness(); + inst.Scale = 1.0f; + inst.BoneOffset = batch.TotalBoneCount; + inst.Padding = 0; + + batch.InstanceData.push_back( inst ); + + // Add bone transforms + batch.AllBoneTransforms.insert( batch.AllBoneTransforms.end(), + transforms.begin(), transforms.end() ); + batch.TotalBoneCount += static_cast(transforms.size()); + + model->SetIsVisible( true ); + + batch.VisualInfo = dynamic_cast(vi->VisualInfo); + } + } +} + +XRESULT D3D11GraphicsEngine::DrawSkeletalMeshBatched() { + if ( SkeletalMeshBatches.empty() ) return XR_SUCCESS; + + // Fix the view matrix + Engine::GAPI->GetViewMatrix( &Engine::GAPI->GetRendererState().TransformState.TransformView ); + + // Draw each batch (body meshes only) + for ( auto& [name, batch] : SkeletalMeshBatches ) { + if ( batch.Vobs.empty() || !batch.VisualInfo ) { + continue; + } + + // Don't to the expensive upload if it's a single instance. + if ( batch.InstanceData.size() == 1 ) { + for ( SkeletalVobInfo* vi : batch.Vobs ) { + zCModel* model = static_cast(vi->Vob->GetVisual()); + if ( !model ) continue; + model->SetIsVisible( true ); + if ( !vi->Vob->GetShowVisual() ) + continue; + + // if we don't update the MeshLibTextAni the visual will have wrong textures + model->UpdateAttachedVobs(); + model->UpdateMeshLibTexAniState(); + + if ( !vi->VobConstantBuffer ) { + vi->UpdateVobConstantBuffer(); + } + + DrawSkeletalMesh( vi, batch.AllBoneTransforms, batch.InstanceData[0].Color, batch.InstanceData[0].World, batch.InstanceData[0].Fatness ); + } + continue; + } + + // Draw batched body mesh + DrawSkeletalMeshInstanced( batch ); + } + + + return XR_SUCCESS; +} + +XRESULT D3D11GraphicsEngine::DrawSkeletalMeshInstanced( SkeletalMeshBatch& batch ) { + if ( batch.InstanceData.empty() || !batch.VisualInfo ) return XR_SUCCESS; + + if ( SkeletalInstanceBuffer->GetMaxElementCount() < batch.InstanceData.size() ) { + auto requiredSize = Toolbox::NextPowerOfTwo( batch.InstanceData.size() ); + + SkeletalInstanceBuffer = std::make_unique>(); + HRESULT hr = SkeletalInstanceBuffer->Init( GetDevice().Get(), requiredSize ); + if ( FAILED( hr ) ) { + LogError() << "Failed to create skeletal instance buffer"; + return XR_FAILED; + } + } + + // Update structured buffers + SkeletalInstanceBuffer->UpdateBuffer( Context.Get(), batch.InstanceData.data(), + static_cast(batch.InstanceData.size()) ); + + if ( SkeletalBoneBuffer->GetMaxElementCount() < batch.AllBoneTransforms.size() ) { + auto requiredSize = Toolbox::NextPowerOfTwo( batch.AllBoneTransforms.size() ); + + SkeletalBoneBuffer = std::make_unique>(); + HRESULT hr = SkeletalBoneBuffer->Init( GetDevice().Get(), requiredSize ); + if ( FAILED( hr ) ) { + LogError() << "Failed to create skeletal bone buffer"; + return XR_FAILED; + } + } + SkeletalBoneBuffer->UpdateBuffer( Context.Get(), batch.AllBoneTransforms.data(), + static_cast(batch.AllBoneTransforms.size()) ); + + // Bind structured buffers + SkeletalInstanceBuffer->BindToVertexShader( Context.Get(), 10 ); // t10 + SkeletalBoneBuffer->BindToVertexShader( Context.Get(), 11 ); // t11 + + // Set shader based on rendering stage + if ( RenderingStage == DES_SHADOWMAP_CUBE ) { + SetActiveVertexShader( "VS_ExSkeletalInstancedCube" ); + } else { + SetActiveVertexShader( "VS_ExSkeletalInstanced" ); + } + + InfiniteRangeConstantBuffer->BindToPixelShader( 3 ); + SetupVS_ExMeshDrawCall(); + SetupVS_ExConstantBuffer(); + + Context->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST ); + + ActiveVS->Apply(); + + // Setup pixel shader + if ( RenderingStage != DES_GHOST ) { + bool linearDepth = (Engine::GAPI->GetRendererState().GraphicsState.FF_GSwitches & GSWITCH_LINEAR_DEPTH) != 0; + if ( linearDepth ) { + ActivePS = PS_LinDepth; + ActivePS->Apply(); + } else if ( RenderingStage == DES_SHADOWMAP ) { + Context->PSSetShader( nullptr, nullptr, 0 ); + ActivePS = nullptr; + } else { + ActivePS = PS_LinDepth; + } + } + + if ( RenderingStage == DES_MAIN ) { + if ( ActiveHDS ) { + Context->DSSetShader( nullptr, nullptr, 0 ); + Context->HSSetShader( nullptr, nullptr, 0 ); + ActiveHDS = nullptr; + } + } + + // Draw each submesh + SkeletalMeshVisualInfo* meshInfo = nullptr; + + for ( auto vi : batch.Vobs ) { + zCModel* model = static_cast(vi->Vob->GetVisual()); + + if ( !model || !vi->VisualInfo ) + continue; // Gothic fortunately sets this to 0 when it throws the model out of the cache + + model->SetIsVisible( true ); + if ( !vi->Vob->GetShowVisual() ) + continue; + + // if we don't update the MeshLibTextAni the visual will have wrong textures + model->UpdateMeshLibTexAniState(); + + meshInfo = static_cast(vi->VisualInfo); + break; + } + + for ( auto const& itm : batch.VisualInfo->SkeletalMeshes ) { + if ( itm.first ) { + zCTexture* tex; + if ( ActivePS && (tex = itm.first->GetAniTexture()) != nullptr ) { + if ( !BindTextureNRFX( tex, (RenderingStage != DES_GHOST) ) ) { + continue; + } + } + } + + for ( auto& mesh : itm.second ) { + D3D11VertexBuffer* vb = mesh->MeshVertexBuffer; + D3D11VertexBuffer* ib = mesh->MeshIndexBuffer; + unsigned int numIndices = static_cast(mesh->Indices.size()); + + UINT offset = 0; + UINT uStride = sizeof( ExSkelVertexStruct ); + Context->IASetVertexBuffers( 0, 1, vb->GetVertexBuffer().GetAddressOf(), &uStride, &offset ); + Context->IASetIndexBuffer( ib->GetVertexBuffer().Get(), VERTEX_INDEX_DXGI_FORMAT, 0 ); + + // Draw instanced + Context->DrawIndexedInstanced( numIndices, static_cast(batch.InstanceData.size()), 0, 0, 0 ); + + Engine::GAPI->GetRendererState().RendererInfo.FrameDrawnTriangles += + (numIndices / 3) * static_cast(batch.InstanceData.size()); + } + } + + // Unbind structured buffers + SkeletalInstanceBuffer->UnbindFromVertexShader( Context.Get(), 10 ); + SkeletalBoneBuffer->UnbindFromVertexShader( Context.Get(), 11 ); + + Engine::GAPI->GetRendererState().RendererInfo.FrameDrawnVobs += + static_cast(batch.InstanceData.size()); + + return XR_SUCCESS; +} + +void D3D11GraphicsEngine::ClearSkeletalMeshBatches() { + for ( auto& [name, batch] : SkeletalMeshBatches ) { + batch.Clear(); + } + SkeletalMeshBatches.clear(); +} + + +XRESULT D3D11GraphicsEngine::InitNodeAttachmentInstancingBuffer() { + // Instance buffer for node attachments + NodeAttachmentInstanceBuffer = std::make_unique>(); + HRESULT hr = NodeAttachmentInstanceBuffer->Init( GetDevice().Get(), 64 ); + if ( FAILED( hr ) ) { + LogError() << "Failed to create node attachment instance buffer"; + return XR_FAILED; + } + + LogInfo() << "Initialized node attachment instancing buffer"; + return XR_SUCCESS; +} + +void D3D11GraphicsEngine::DrawNodeAttachmentsBatched( + std::unordered_map& batches ) +{ + if ( batches.empty() ) return; + + // Set up shaders + if ( RenderingStage == DES_SHADOWMAP_CUBE ) { + SetActiveVertexShader( "VS_ExNodeInstancedCube" ); + } else { + SetActiveVertexShader( "VS_ExNodeInstanced" ); + } + + SetupVS_ExMeshDrawCall(); + SetupVS_ExConstantBuffer(); + + // Bind instance buffer to slot 10 + NodeAttachmentInstanceBuffer->BindToVertexShader( Context.Get(), 10 ); + + ActiveVS->Apply(); + + // Setup pixel shader + if ( RenderingStage == DES_MAIN ) { + SetActivePixelShader( "PS_DiffuseAlphaTest" ); + BindActivePixelShader(); + } else if ( RenderingStage == DES_SHADOWMAP ) { + Context->PSSetShader( nullptr, nullptr, 0 ); + ActivePS = nullptr; + } + + Context->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST ); + + // Draw each batch + for ( auto& [key, batch] : batches ) + { + if ( batch.Instances.empty() || !batch.Mesh.size() ) continue; + + zCModel* model = static_cast(batch.vobInfo->Vob->GetVisual()); + + model->UpdateAttachedVobs(); + model->UpdateMeshLibTexAniState(); + + // Update instance buffer + UINT instanceCount = batch.Instances.size(); + + if ( NodeAttachmentInstanceBuffer->GetMaxElementCount() < instanceCount ) { + auto requiredSize = Toolbox::NextPowerOfTwo( instanceCount ); + + NodeAttachmentInstanceBuffer = std::make_unique>(); + HRESULT hr = NodeAttachmentInstanceBuffer->Init( GetDevice().Get(), requiredSize ); + if ( FAILED( hr ) ) { + LogError() << "Failed to create node attachment instance buffer"; + return; + } + } + + NodeAttachmentInstanceBuffer->UpdateBuffer( Context.Get(), batch.Instances.data(), instanceCount ); + + // WICHTIG: Hole die Textur JETZT aus dem Material, nicht aus dem gecachten Key! + if ( RenderingStage != DES_SHADOWMAP && RenderingStage != DES_SHADOWMAP_CUBE ) { + + // Only update textures if not drawing shadows + const bool isMMS = strcmp( batch.MeshVisualInfo->Visual->GetFileExtension( 0 ), ".MMS" ) == 0; + batch.node->TexAniState.UpdateTexList(); + if ( isMMS ) { + // Important to also update the textures here + zCMorphMesh* mm = reinterpret_cast(batch.MeshVisualInfo->Visual); + mm->GetTexAniState()->UpdateTexList(); + } + + if ( batch.Material ) { + zCTexture* texture = batch.Material->GetAniTexture(); + if ( texture ) { + if ( texture->CacheIn( 0.6f ) != zRES_CACHED_IN ) { + continue; // Skip if texture not loaded + } + if ( !BindTextureNRFX( texture, (RenderingStage == DES_MAIN) ) ) { + continue; + } + } else { + continue; // Skip if no texture + } + } else { + continue; + } + } + + // Set up alpha test from material + if ( batch.Material && batch.Material->GetAlphaFunc() == zRND_ALPHA_FUNC_TEST ) { + Engine::GAPI->GetRendererState().GraphicsState.FF_GSwitches |= GSWITCH_ALPHAREF; + } else { + Engine::GAPI->GetRendererState().GraphicsState.FF_GSwitches &= ~GSWITCH_ALPHAREF; + } + + // Bind mesh buffers + for ( auto& mesh : batch.Mesh ) + { + if ( !mesh->MeshVertexBuffer ) continue; + + UINT offset = 0; + UINT stride = sizeof( ExVertexStruct ); + Context->IASetVertexBuffers( 0, 1, mesh->MeshVertexBuffer->GetVertexBuffer().GetAddressOf(), &stride, &offset ); + + if ( mesh->MeshIndexBuffer ) { + Context->IASetIndexBuffer( mesh->MeshIndexBuffer->GetVertexBuffer().Get(), VERTEX_INDEX_DXGI_FORMAT, 0 ); + Context->DrawIndexedInstanced( static_cast(mesh->Indices.size()), instanceCount, 0, 0, 0 ); + Engine::GAPI->GetRendererState().RendererInfo.FrameDrawnTriangles += + static_cast(mesh->Indices.size() / 3) * instanceCount; + } else + { + Context->DrawInstanced( static_cast(mesh->Vertices.size()), instanceCount, 0, 0 ); + Engine::GAPI->GetRendererState().RendererInfo.FrameDrawnTriangles += + static_cast(mesh->Vertices.size() / 3) * instanceCount; + } + } + } + NodeAttachmentInstanceBuffer->UnbindFromVertexShader( Context.Get(), 10 ); +} + void D3D11GraphicsEngine::ShadowPass_DrawWorldMesh_Indirect(const std::vector& visibleSections) { float alphaRef = Engine::GAPI->GetRendererState().GraphicsState.FF_AlphaRef; @@ -4921,7 +5403,7 @@ void XM_CALLCONV D3D11GraphicsEngine::DrawWorldAroundForWorldShadow( FXMVECTOR p static thread_local std::vector visibleSections; visibleSections.clear(); - + for ( const auto& itx : Engine::GAPI->GetWorldSections() ) { for ( const auto& ity : itx.second ) { float dx = static_cast(itx.first - s.x); @@ -5141,6 +5623,10 @@ void XM_CALLCONV D3D11GraphicsEngine::DrawWorldAroundForWorldShadow( FXMVECTOR p for ( auto const& skeletalMeshVob : Engine::GAPI->GetSkeletalMeshVobs() ) { if ( !skeletalMeshVob->VisualInfo ) continue; + if ( skeletalMeshVob->IndoorVob ) { + continue; // indoor is handled by dynamic lighting + } + // Ghosts shouldn't have shadows if ( skeletalMeshVob->Vob->GetVisualAlpha() && skeletalMeshVob->Vob->GetVobTransparency() < 0.7f ) { continue; @@ -5162,10 +5648,10 @@ void XM_CALLCONV D3D11GraphicsEngine::DrawWorldAroundForWorldShadow( FXMVECTOR p bool drawAttachments = true; if ( Engine::GAPI->GetRendererState().RendererSettings.ShadowFrustumCullingMode == GothicRendererSettings::E_ShadowFrustumCulling::SHD_FRUSTUM_CULLING_AGGRESSIVE ) { - drawAttachments = params.CascadeIndex <= 1; // skip attachments on higher cascades, player won't notice, hopefully + drawAttachments = params.CascadeIndex > 0 && !isLastCascade; // skip attachments on last cascade } // we should not need to update the skeletal meshes again, as they were updated before drawing the main scene - Engine::GAPI->DrawSkeletalMeshVobs( animatedSkeletalMeshVobs, false, drawAttachments ); + Engine::GAPI->DrawSkeletalMeshVobs( animatedSkeletalMeshVobs, true, drawAttachments ); } Engine::GAPI->GetRendererState().BlendState.ColorWritesEnabled = true; @@ -5250,6 +5736,11 @@ void D3D11GraphicsEngine::ApplyWindProps( VS_ExConstantBuffer_Wind& windBuff ) { XRESULT D3D11GraphicsEngine::DrawVOBsInstanced() { static std::vector vobs; static std::vector mobs; + + if (RenderingStage == DES_MAIN) { + auto _ = START_TIMING( "Prepare Shadow" ); + ShadowMaps->PrepareRender(); // calculate cameras, frusti collect any vobs needed, etc. + } const auto& renderSettings = Engine::GAPI->GetRendererState().RendererSettings; @@ -5529,10 +6020,12 @@ XRESULT D3D11GraphicsEngine::DrawVOBsInstanced() { // Mobs use zengine functions for binding textures so let's reset zengine texture state Engine::GAPI->ResetRenderStates(); + Engine::GAPI->DrawSkeletalMeshVobs( mobs, true, true ); + static std::vector bones = {}; for ( SkeletalVobInfo* mob : mobs ) { - Engine::GAPI->DrawSkeletalMeshVob( mob, FLT_MAX ); - + mob->VisibleInRenderPass = false; // Reset this for the next frame + zCModel* model = static_cast(mob->Vob->GetVisual()); XMMATRIX scale = XMMatrixScalingFromVector( model->GetModelScaleXM() ); XMMATRIX world = mob->Vob->GetWorldMatrixXM() * scale; @@ -6426,7 +6919,7 @@ void D3D11GraphicsEngine::DrawDecalList( const std::vector& decals, if ( !lighting ) { GhostAlphaConstantBuffer gacb; gacb.GA_ViewportSize = float2( Engine::GraphicsEngine->GetResolution().x, Engine::GraphicsEngine->GetResolution().y ); - gacb.GA_Alpha = (material->GetColor() >> 24) * inv255f; + gacb.GA_Alpha = static_cast(material->GetColor().dword >> 24) * inv255f; ActivePS->GetConstantBuffer()[0]->UpdateBuffer( &gacb ); ActivePS->GetConstantBuffer()[0]->BindToPixelShader( 0 ); } diff --git a/D3D11Engine/D3D11GraphicsEngine.h b/D3D11Engine/D3D11GraphicsEngine.h index e3b667f2..27a03330 100644 --- a/D3D11Engine/D3D11GraphicsEngine.h +++ b/D3D11Engine/D3D11GraphicsEngine.h @@ -4,6 +4,8 @@ #include "GothicAPI.h" #include "D3D11ShadowMap.h" #include "D3D11ShaderManager.h" +#include "D3D11StructuredBuffer.h" +#include "ConstantBufferStructs.h" struct RenderToDepthStencilBuffer; @@ -28,7 +30,6 @@ const unsigned int PARTICLES_BUFFER_SIZE = 3072 * sizeof( ParticleInstanceInfo ) const unsigned int MORPHEDMESH_SMALL_BUFFER_SIZE = 3072 * sizeof( ExVertexStruct ); const unsigned int MORPHEDMESH_HIGH_BUFFER_SIZE = 20480 * sizeof( ExVertexStruct ); const unsigned int HUD_BUFFER_SIZE = 6 * sizeof( ExVertexStruct ); -const int NUM_MAX_BONES = 96; const int unsigned INSTANCING_BUFFER_SIZE = sizeof( VobInstanceInfo ) * 2048; @@ -361,6 +362,21 @@ class D3D11GraphicsEngine : public D3D11GraphicsEngineBase { const XMFLOAT4X4& GetPrevViewProjMatrix() const { return m_PrevViewProjMatrix; } void StorePrevViewProjMatrix(); + + // New methods for instanced skeletal rendering + XRESULT InitSkeletalInstancingBuffers(); + void BuildSkeletalMeshBatches( const std::vector& vobs ); + XRESULT DrawSkeletalMeshBatched(); + XRESULT DrawSkeletalMeshInstanced( SkeletalMeshBatch& batch ); + void ClearSkeletalMeshBatches(); + + // Initialize node attachment instancing buffer + XRESULT InitNodeAttachmentInstancingBuffer(); + + // Draw batched node attachments + void DrawNodeAttachmentsBatched( + std::unordered_map& batches ); + protected: void StoreVobPreviousTransforms(); @@ -474,4 +490,12 @@ class D3D11GraphicsEngine : public D3D11GraphicsEngineBase { XMFLOAT4X4 m_PrevViewProjMatrix; INT2 NewResolution; + + // Instanced skeletal mesh rendering buffers + std::unique_ptr> SkeletalInstanceBuffer; + std::unique_ptr> SkeletalBoneBuffer; + + // Batched skeletal meshes for current frame + std::unordered_map SkeletalMeshBatches; + std::unique_ptr> NodeAttachmentInstanceBuffer; }; diff --git a/D3D11Engine/D3D11GraphicsEngineBase.cpp b/D3D11Engine/D3D11GraphicsEngineBase.cpp index 131d5139..25cde757 100644 --- a/D3D11Engine/D3D11GraphicsEngineBase.cpp +++ b/D3D11Engine/D3D11GraphicsEngineBase.cpp @@ -11,7 +11,6 @@ #include "zCView.h" const unsigned int DRAWVERTEXARRAY_BUFFER_SIZE = 4096 * sizeof( ExVertexStruct ); -const int NUM_MAX_BONES = 96; const unsigned int INSTANCING_BUFFER_SIZE = sizeof( VobInstanceInfo ) * 2048; #if defined(BUILD_GOTHIC_1_08k) && !defined(BUILD_1_12F) diff --git a/D3D11Engine/D3D11ShaderManager.cpp b/D3D11Engine/D3D11ShaderManager.cpp index 3cca0842..77c0476a 100644 --- a/D3D11Engine/D3D11ShaderManager.cpp +++ b/D3D11Engine/D3D11ShaderManager.cpp @@ -115,8 +115,6 @@ namespace }; } -const int NUM_MAX_BONES = 96; - extern bool FeatureLevel10Compatibility; extern bool FeatureRTArrayIndexFromAnyShader; @@ -207,6 +205,12 @@ XRESULT D3D11ShaderManager::Init() { Shaders.back().cBufferSizes.push_back( sizeof( VS_ExConstantBuffer_PerFrame ) ); Shaders.back().cBufferSizes.push_back( sizeof( VS_ExConstantBuffer_PerInstanceNode ) ); + Shaders.push_back( ShaderInfo( "VS_ExNodeInstanced", "VS_ExNodeInstanced.hlsl", "v", 1 ) ); + Shaders.back().cBufferSizes.push_back( sizeof( VS_ExConstantBuffer_PerFrame ) ); + + Shaders.push_back( ShaderInfo( "VS_ExNodeInstancedCube", "VS_ExNodeInstancedCube.hlsl", "v", 1 ) ); + Shaders.back().cBufferSizes.push_back( sizeof( VS_ExConstantBuffer_PerFrame ) ); + Shaders.push_back( ShaderInfo( "VS_Decal", "VS_Decal.hlsl", "v", 1 ) ); Shaders.back().cBufferSizes.push_back( sizeof( VS_ExConstantBuffer_PerFrame ) ); Shaders.back().cBufferSizes.push_back( sizeof( VS_ExConstantBuffer_PerInstance ) ); @@ -234,6 +238,12 @@ XRESULT D3D11ShaderManager::Init() { Shaders.back().cBufferSizes.push_back( NUM_MAX_BONES * sizeof( XMFLOAT4X4 ) ); Shaders.back().cBufferSizes.push_back( NUM_MAX_BONES * sizeof( XMFLOAT4X4 ) ); // PrevBoneTransforms for motion vectors + Shaders.push_back( ShaderInfo( "VS_ExSkeletalInstanced", "VS_ExSkeletalInstanced.hlsl", "v", 3 ) ); + Shaders.back().cBufferSizes.push_back( sizeof( VS_ExConstantBuffer_PerFrame ) ); + + Shaders.push_back( ShaderInfo( "VS_ExSkeletalInstancedCube", "VS_ExSkeletalInstancedCube.hlsl", "v", 3 ) ); + Shaders.back().cBufferSizes.push_back( sizeof( VS_ExConstantBuffer_PerFrame ) ); + Shaders.push_back( ShaderInfo( "VS_ExSkeletalVN", "VS_ExSkeletalVN.hlsl", "v", 3 ) ); Shaders.back().cBufferSizes.push_back( sizeof( VS_ExConstantBuffer_PerFrame ) ); Shaders.back().cBufferSizes.push_back( sizeof( VS_ExConstantBuffer_PerInstanceSkeletal ) ); diff --git a/D3D11Engine/D3D11StructuredBuffer.h b/D3D11Engine/D3D11StructuredBuffer.h new file mode 100644 index 00000000..38edaf64 --- /dev/null +++ b/D3D11Engine/D3D11StructuredBuffer.h @@ -0,0 +1,123 @@ +#pragma once + +#include "pch.h" +#include + +// Templated structured buffer for GPU compute/shader access +template +class D3D11StructuredBuffer { +public: + D3D11StructuredBuffer() : ElementCount(0), MaxElementCount(0) {} + + ~D3D11StructuredBuffer() = default; + + // Initialize the buffer with a maximum capacity + HRESULT Init(ID3D11Device* device, UINT maxElements, bool cpuWrite = true, bool gpuWrite = false) { + MaxElementCount = maxElements; + ElementCount = 0; + + D3D11_BUFFER_DESC desc = {}; + desc.ByteWidth = sizeof(T) * maxElements; + desc.StructureByteStride = sizeof(T); + desc.MiscFlags = D3D11_RESOURCE_MISC_BUFFER_STRUCTURED; + + if (cpuWrite) { + desc.Usage = D3D11_USAGE_DYNAMIC; + desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; + desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; + } else if (gpuWrite) { + desc.Usage = D3D11_USAGE_DEFAULT; + desc.CPUAccessFlags = 0; + desc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_UNORDERED_ACCESS; + } else { + desc.Usage = D3D11_USAGE_DEFAULT; + desc.CPUAccessFlags = 0; + desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; + } + + HRESULT hr = device->CreateBuffer(&desc, nullptr, Buffer.GetAddressOf()); + if (FAILED(hr)) return hr; + + // Create SRV + D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc = {}; + srvDesc.Format = DXGI_FORMAT_UNKNOWN; + srvDesc.ViewDimension = D3D11_SRV_DIMENSION_BUFFER; + srvDesc.Buffer.FirstElement = 0; + srvDesc.Buffer.NumElements = maxElements; + + hr = device->CreateShaderResourceView(Buffer.Get(), &srvDesc, SRV.GetAddressOf()); + if (FAILED(hr)) return hr; + + // Create UAV if GPU writable + if (gpuWrite) { + D3D11_UNORDERED_ACCESS_VIEW_DESC uavDesc = {}; + uavDesc.Format = DXGI_FORMAT_UNKNOWN; + uavDesc.ViewDimension = D3D11_UAV_DIMENSION_BUFFER; + uavDesc.Buffer.FirstElement = 0; + uavDesc.Buffer.NumElements = maxElements; + + hr = device->CreateUnorderedAccessView(Buffer.Get(), &uavDesc, UAV.GetAddressOf()); + if (FAILED(hr)) return hr; + } + + return S_OK; + } + + // Update buffer contents (for dynamic buffers) + HRESULT UpdateBuffer(ID3D11DeviceContext* context, const T* data, UINT count) { + if (count > MaxElementCount) { + LogError() << "StructuredBuffer overflow: " << count << " > " << MaxElementCount; + count = MaxElementCount; + } + + ElementCount = count; + + D3D11_MAPPED_SUBRESOURCE mapped; + HRESULT hr = context->Map(Buffer.Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped); + if (FAILED(hr)) return hr; + + memcpy(mapped.pData, data, sizeof(T) * count); + context->Unmap(Buffer.Get(), 0); + + return S_OK; + } + + // Update buffer contents (for default buffers) + void UpdateBufferDefault(ID3D11DeviceContext* context, const T* data, UINT count) { + if (count > MaxElementCount) { + LogError() << "StructuredBuffer overflow: " << count << " > " << MaxElementCount; + count = MaxElementCount; + } + ElementCount = count; + context->UpdateSubresource(Buffer.Get(), 0, nullptr, data, 0, 0); + } + + // Bind to vertex shader + void BindToVertexShader(ID3D11DeviceContext* context, UINT slot) { + context->VSSetShaderResources(slot, 1, SRV.GetAddressOf()); + } + + // Bind to pixel shader + void BindToPixelShader(ID3D11DeviceContext* context, UINT slot) { + context->PSSetShaderResources(slot, 1, SRV.GetAddressOf()); + } + + // Unbind from vertex shader + void UnbindFromVertexShader(ID3D11DeviceContext* context, UINT slot) { + ID3D11ShaderResourceView* nullSRV = nullptr; + context->VSSetShaderResources(slot, 1, &nullSRV); + } + + UINT GetElementCount() const { return ElementCount; } + UINT GetMaxElementCount() const { return MaxElementCount; } + ID3D11Buffer* GetBuffer() const { return Buffer.Get(); } + ID3D11ShaderResourceView* GetSRV() const { return SRV.Get(); } + ID3D11UnorderedAccessView* GetUAV() const { return UAV.Get(); } + +private: + Microsoft::WRL::ComPtr Buffer; + Microsoft::WRL::ComPtr SRV; + Microsoft::WRL::ComPtr UAV; + UINT ElementCount; + UINT MaxElementCount; +}; diff --git a/D3D11Engine/GothicAPI.cpp b/D3D11Engine/GothicAPI.cpp index 5dba78d9..f95c4a3c 100644 --- a/D3D11Engine/GothicAPI.cpp +++ b/D3D11Engine/GothicAPI.cpp @@ -48,6 +48,7 @@ // TODO: REMOVE THIS! #include "D3D11GraphicsEngine.h" +#include "D3D11RenderQueue.h" #ifndef PUBLIC_RELEASE #define OPT_DBG_NOINLINE __declspec(noinline) @@ -58,6 +59,8 @@ // Duration how long the scene will stay wet, in MS const DWORD SCENE_WETNESS_DURATION_MS = 30 * 1000; +bool UseExperimentalSkeletalBatching = false; + // Draw ghost from back to front of our camera auto CompareGhostDistance = []( TransparencyVobInfo& a, TransparencyVobInfo& b ) -> bool { return a.distance < b.distance; }; @@ -1184,44 +1187,101 @@ void GothicAPI::DrawWorldMeshNaive() { auto drawRadius = RendererState.RendererSettings.SkeletalMeshDrawRadius; - for ( const auto& vobInfo : AnimatedSkeletalVobs ) { - // Don't render if sleeping and has skeletal meshes available - if ( !vobInfo->VisualInfo ) continue; +#if defined(BUILD_1_12F) + // not implemented in G1 sequel + UseExperimentalSkeletalBatching = false; +#endif + // TODO: Figure out why Batching is not working properly here + // The Batches constantly use wrong textures as if just before "DrawIndexed" the texture is being changed by something else + // this happens on the Body meshes as well as all the Node Attachments. + // We need this to be fixed, so that we can actually draw hundreds of animated characters with good performance. + if ( UseExperimentalSkeletalBatching ) { - float dist; - XMStoreFloat( &dist, XMVector3Length( vobInfo->Vob->GetPositionWorldXM() - cameraPosXm ) ); - if ( dist > drawRadius ) - continue; // Skip out of range + static std::vector batchedVobs; + batchedVobs.clear(); - zCCamera::GetCamera()->SetTransform( zCCamera::ETransformType::TT_WORLD, *vobInfo->Vob->GetWorldMatrixPtr() ); + for ( const auto& vobInfo : AnimatedSkeletalVobs ) { + // Don't render if sleeping and has skeletal meshes available + if ( !vobInfo->VisualInfo ) continue; - //Engine::GraphicsEngine->GetLineRenderer()->AddAABBMinMax(bb.Min, bb.Max, XMFLOAT4(1, 1, 1, 1)); + float dist; + XMStoreFloat( &dist, XMVector3Length( vobInfo->Vob->GetPositionWorldXM() - cameraPosXm ) ); + if ( dist > drawRadius ) + continue; // Skip out of range - int clipFlags = EGothicCullFlags::CullSidesNear; // No far clip - if ( GetCameraBBox3DInFrustum( vobInfo->Vob, clipFlags, true ) == ZTCAM_CLIPTYPE_OUT ) - continue; + zCCamera::GetCamera()->SetTransform( zCCamera::ETransformType::TT_WORLD, *vobInfo->Vob->GetWorldMatrixPtr() ); + + int clipFlags = EGothicCullFlags::CullSidesNear; // No far clip + if ( GetCameraBBox3DInFrustum( vobInfo->Vob, clipFlags, true ) == ZTCAM_CLIPTYPE_OUT ) + continue; - // Indoor? - vobInfo->IndoorVob = vobInfo->Vob->IsIndoorVob(); + //Engine::GraphicsEngine->GetLineRenderer()->AddAABBMinMax(bb.Min, bb.Max, XMFLOAT4(1, 1, 1, 1)); - zCModel* model = static_cast(vobInfo->Vob->GetVisual()); - if ( !model ) - continue; // Gothic fortunately sets this to 0 when it throws the model out of the cache + // Indoor? + vobInfo->IndoorVob = vobInfo->Vob->IsIndoorVob(); - // This is important, because gothic only lerps between animation when this distance is set and below ~2000 - model->SetDistanceToCamera( dist ); + zCModel* model = static_cast(vobInfo->Vob->GetVisual()); + if ( !model ) + continue; // Gothic fortunately sets this to 0 when it throws the model out of the cache - // Schedule for drawing in later stage if this vob is ghost - if ( vobInfo->Vob->GetVisualAlpha() ) { - TransparencyVobs.emplace_back( dist, vobInfo->Vob->GetVobTransparency(), vobInfo, nullptr ); - std::push_heap( TransparencyVobs.begin(), TransparencyVobs.end(), CompareGhostDistance ); - continue; + // This is important, because gothic only lerps between animation when this distance is set and below ~2000 + model->SetDistanceToCamera( dist ); + + // Schedule for drawing in later stage if this vob is ghost + if ( vobInfo->Vob->GetVisualAlpha() ) { + TransparencyVobs.emplace_back( dist, vobInfo->Vob->GetVobTransparency(), vobInfo, nullptr ); + std::push_heap( TransparencyVobs.begin(), TransparencyVobs.end(), CompareGhostDistance ); + continue; + } + + batchedVobs.push_back( vobInfo ); + if ( RendererState.RendererSettings.ShowSkeletalVertexNormals ) + VNSkeletalVobs.emplace_back( vobInfo ); } - DrawSkeletalMeshVob( vobInfo, dist ); - if( RendererState.RendererSettings.ShowSkeletalVertexNormals ) - VNSkeletalVobs.emplace_back( vobInfo ); + DrawSkeletalMeshVobs_Batched( batchedVobs, true, true ); + } else { + + for ( const auto& vobInfo : AnimatedSkeletalVobs ) { + // Don't render if sleeping and has skeletal meshes available + if ( !vobInfo->VisualInfo ) continue; + + float dist; + XMStoreFloat( &dist, XMVector3Length( vobInfo->Vob->GetPositionWorldXM() - cameraPosXm ) ); + if ( dist > drawRadius ) + continue; // Skip out of range + + int clipFlags = EGothicCullFlags::CullSidesNear; // No far clip + + zCCamera::GetCamera()->SetTransform( zCCamera::ETransformType::TT_WORLD, *vobInfo->Vob->GetWorldMatrixPtr() ); + if ( GetCameraBBox3DInFrustum( vobInfo->Vob, clipFlags, true ) == ZTCAM_CLIPTYPE_OUT ) + continue; + //Engine::GraphicsEngine->GetLineRenderer()->AddAABBMinMax(bb.Min, bb.Max, XMFLOAT4(1, 1, 1, 1)); + + // Indoor? + vobInfo->IndoorVob = vobInfo->Vob->IsIndoorVob(); + + zCModel* model = static_cast(vobInfo->Vob->GetVisual()); + if ( !model ) + continue; // Gothic fortunately sets this to 0 when it throws the model out of the cache + + // This is important, because gothic only lerps between animation when this distance is set and below ~2000 + model->SetDistanceToCamera( dist ); + + // Schedule for drawing in later stage if this vob is ghost + if ( vobInfo->Vob->GetVisualAlpha() ) { + TransparencyVobs.emplace_back( dist, vobInfo->Vob->GetVobTransparency(), vobInfo, nullptr ); + std::push_heap( TransparencyVobs.begin(), TransparencyVobs.end(), CompareGhostDistance ); + continue; + } + + DrawSkeletalMeshVob( vobInfo, dist ); + if ( RendererState.RendererSettings.ShowSkeletalVertexNormals ) + VNSkeletalVobs.emplace_back( vobInfo ); + } } + + } // Draw vobs in view @@ -1232,6 +1292,250 @@ void GothicAPI::DrawWorldMeshNaive() { ResetWorldTransform(); } +// Add the new batched rendering method: +void GothicAPI::DrawSkeletalMeshVobs_Batched( + const std::vector& vobs, + bool updateState, + bool drawAttachments ) { + D3D11GraphicsEngine* g = reinterpret_cast(Engine::GraphicsEngine); + + Engine::GAPI->GetRendererState().TransformState.TransformView = zCCamera::GetCamera()->GetTransformDX( zCCamera::ETransformType::TT_VIEW ); + + // Build batches from animated skeletal vobs + g->BuildSkeletalMeshBatches( vobs ); + + // Draw all batches + g->DrawSkeletalMeshBatched(); + + if ( !drawAttachments ) { + g->ClearSkeletalMeshBatches(); + return; + } + + g->SetupVS_ExMeshDrawCall(); + g->SetupVS_ExConstantBuffer(); + + // Collect node attachments for batched rendering + // Key: (Texture*, Visual*) -> ensures same texture AND same mesh geometry + static std::unordered_map NodeAttachmentBatches; + NodeAttachmentBatches.clear(); + + XMVECTOR playerPosXm = oCGame::GetPlayer() ? oCGame::GetPlayer()->GetPositionWorldXM() : g_XMZero; + + D3D11GraphicsEngine* engine = (D3D11GraphicsEngine*)Engine::GraphicsEngine; + auto isShadowPass = engine->GetRenderingStage() == DES_SHADOWMAP || engine->GetRenderingStage() == DES_SHADOWMAP_CUBE; + + static std::vector transforms; + for ( const auto& vi : vobs ) { + zCModel* model = static_cast(vi->Vob->GetVisual()); + if ( !model || !vi->VisualInfo ) continue; + + //model->SetIsVisible( true ); + if ( !vi->Vob->GetShowVisual() ) continue; + + float dist; + XMStoreFloat( &dist, XMVector3Length( vi->Vob->GetPositionWorldXM() - playerPosXm ) ); + + // Calculate model color + float4 modelColor; + if ( RendererState.RendererSettings.EnableShadows ) { + modelColor = float4( 1, 1, 1, 1 ); + } else { + if ( vi->Vob->IsIndoorVob() ) { + modelColor = DEFAULT_LIGHTMAP_POLY_COLOR; + } else if ( zCPolygon* polygon = vi->Vob->GetGroundPoly() ) { + static const float inv255f = 1.0f / 255.0f; + float3 vobPos = vi->Vob->GetPositionWorld(); + float3 polyLightStat = polygon->GetLightStatAtPos( vobPos ); + modelColor = float4( polyLightStat.z * inv255f, polyLightStat.y * inv255f, polyLightStat.x * inv255f, 1.f ); + } else { + modelColor = float4( 1, 1, 1, 1 ); + } + } + + XMMATRIX scale = XMMatrixScalingFromVector( model->GetModelScaleXM() ); + XMMATRIX world = vi->Vob->GetWorldMatrixXM() * scale; + + float fatness = model->GetModelFatness(); + + // Get bone transforms + transforms.clear(); + model->GetBoneTransforms( &transforms ); + + const std::vector& prevBoneTransforms = (vi->HasValidPrevTransforms && !vi->PrevBoneTransforms.empty()) + ? vi->PrevBoneTransforms + : transforms; + const XMMATRIX prevWorldMatrix = vi->HasValidPrevTransforms + ? XMLoadFloat4x4(&vi->PrevWorldMatrix) + : world; + + // Update attachments + if ( updateState ) { + model->UpdateAttachedVobs(); + } + + std::map>& nodeAttachments = vi->NodeAttachments; + zCModel* mvis = static_cast( vi->Vob->GetVisual() ); + + for ( unsigned int i = 0; i < transforms.size(); i++ ) { + zCModelNodeInst* node = mvis->GetNodeList()->Array[i]; + + if ( !node->NodeVisual ) continue; + + if ( updateState ) { + // Load attachment if not yet loaded + if ( nodeAttachments.find( i ) == nodeAttachments.end() ) { + WorldConverter::ExtractNodeVisual( i, node, nodeAttachments ); + } + + // Check for changed visual + if ( !nodeAttachments[i].empty() && node->NodeVisual != nodeAttachments[i][0]->Visual ) { + if ( !node->NodeVisual ) { + delete nodeAttachments[i][0]; + nodeAttachments[i].clear(); + continue; + } + WorldConverter::ExtractNodeVisual( i, node, nodeAttachments ); + } + } + + // Skip non-hand visuals if model requests it + if ( model->GetDrawHandVisualsOnly() ) { + std::string NodeName = node->ProtoNode->NodeName.ToChar(); +#ifdef BUILD_GOTHIC_2_6_fix + if ( NodeName.find( "HAND" ) == std::string::npos && + (*reinterpret_cast(0x57A694) != 0x90 || NodeName.find( "ARM" ) == std::string::npos) ) { +#else + if ( NodeName.find( "HAND" ) == std::string::npos ) { +#endif + continue; + } + } + + if ( nodeAttachments.find( i ) == nodeAttachments.end() ) continue; + + XMMATRIX curTransform = XMLoadFloat4x4( &transforms[i] ); + XMMATRIX finalWorld = world * curTransform; + + XMMATRIX prevTransform = XMLoadFloat4x4( &prevBoneTransforms[i] ); + auto prevWorldNode = prevWorldMatrix * prevTransform; + + // Process each attachment visual + for ( MeshVisualInfo* mvi : nodeAttachments[i] ) { + if ( !mvi || !mvi->Visual ) continue; + + // Update animated textures BEFORE getting the texture pointer + bool isMMS = strcmp( mvi->Visual->GetFileExtension( 0 ), ".MMS" ) == 0; + if ( updateState ) { + node->TexAniState.UpdateTexList(); + if ( isMMS ) { + zCMorphMesh* mm = reinterpret_cast(mvi->Visual); + mm->GetTexAniState()->UpdateTexList(); + } + } + + // Handle MorphMeshes separately (can't be batched due to vertex animation) + if ( dist < 1000 && isMMS ) { + if ( g->GetRenderingStage() == DES_MAIN || g->GetRenderingStage() == DES_GHOST ) { + zCMorphMesh* mm = reinterpret_cast( mvi->Visual ); + + if ( updateState ) { + mm->GetTexAniState()->UpdateTexList(); + } + SetWorldViewTransform( finalWorld, GetViewMatrixXM() ); + + g->SetActiveVertexShader( "VS_ExNode" ); + g->SetupVS_ExMeshDrawCall(); + g->SetupVS_ExConstantBuffer(); + + VS_ExConstantBuffer_PerInstanceNode instanceInfo; + instanceInfo.Color = modelColor; + instanceInfo.Fatness = std::max( 0.f, fatness * 0.35f ); + instanceInfo.Scaling = fatness * 0.02f + 1.f; + XMStoreFloat4x4(&instanceInfo.World, finalWorld); + XMStoreFloat4x4(&instanceInfo.PrevWorld, prevWorldNode); + + g->GetActiveVS()->GetConstantBuffer()[1]->UpdateBuffer( &instanceInfo ); + g->GetActiveVS()->GetConstantBuffer()[1]->BindToVertexShader( 1 ); + + if ( updateState ) { + mm->AdvanceAnis(); + mm->CalcVertexPositions(); + } + DrawMorphMesh( mm, mvi->Meshes ); + continue; + } + } + + // Build instance data + NodeAttachmentInstanceData instData = {}; + XMStoreFloat4x4( &instData.World, finalWorld ); + XMStoreFloat4x4( &instData.PrevWorld, prevWorldNode ); + instData.Color = modelColor; + + if ( isMMS ) { + instData.Fatness = std::max( 0.f, fatness * 0.35f ); + instData.Scaling = fatness * 0.02f + 1.f; + } else { + instData.Fatness = 0.f; + instData.Scaling = 1.f; + } + + NodeAttachmentBatchKey baseNodeMeshKey = 0; + + if ( auto mm = mvi->Visual->As(); mm ) { + auto progMeshPtr = mm->GetMorphMesh(); + if ( !progMeshPtr ) { + continue; // no mesh no good. + } + + mm->GetTexAniState()->UpdateTexList(); + auto progMeshId = progMeshPtr->GetProgId(); + Toolbox::hash_combine( baseNodeMeshKey, progMeshId ); + } else if ( auto proto = mvi->Visual->As(); proto ) { + auto progMeshId = proto->GetProgId(); + Toolbox::hash_combine( baseNodeMeshKey, progMeshId ); + } else { + // broken inheritance check. + continue; + } + + // Collect into batches by (Meshlib, texture) pair + // This ensures same texture AND same mesh geometry for proper instancing + for ( auto const& itm : mvi->Meshes ) { + zCMaterial* mat = itm.first; + if ( !mat ) continue; + + NodeAttachmentBatchKey key = baseNodeMeshKey; + // TODO: figure out how to know if we need the texture in shadow pass. + Toolbox::hash_combine( key, std::string_view( mat->GetAniTexture() && mat->GetAniTexture()->__GetName().Length() ? mat->GetAniTexture()->__GetName().ToChar() : "")); + + auto& batch = NodeAttachmentBatches[key]; + + // Store the first mesh encountered + if ( !batch.Mesh.size() ) { + batch.Mesh = itm.second; + batch.vobInfo = vi; + batch.node = node; + batch.MeshVisualInfo = mvi; + batch.Material = mat; + } + + batch.Instances.push_back( instData ); + } + } + } + } + + // Draw all batched node attachments + if ( !NodeAttachmentBatches.empty() ) { + g->DrawNodeAttachmentsBatched( NodeAttachmentBatches ); + } + + // Clear batches for next frame + g->ClearSkeletalMeshBatches(); +} + /** Draws particles, in a simple way */ void GothicAPI::DrawParticlesSimple( RenderToTextureBuffer* bufferParticleColor, @@ -2303,7 +2607,7 @@ void GothicAPI::UpdateCompressBackBuffer() { } /** Draws a skeletal mesh-vob */ -void GothicAPI::DrawSkeletalMeshVob( SkeletalVobInfo* vi, float distance, bool updateState ) { +void GothicAPI::DrawSkeletalMeshVob( SkeletalVobInfo* vi, float distance, bool updateState, bool drawAttachments ) { // TODO: Put this into the renderer!! D3D11GraphicsEngine* g = reinterpret_cast(Engine::GraphicsEngine); @@ -2373,6 +2677,10 @@ void GothicAPI::DrawSkeletalMeshVob( SkeletalVobInfo* vi, float distance, bool u WorldConverter::ExtractSkeletalMeshFromVob( model, static_cast(vi->VisualInfo) ); } } + + if (!drawAttachments) { + return; + } if ( g->GetRenderingStage() == DES_SHADOWMAP_CUBE ) g->SetActiveVertexShader( "VS_ExNodeCube" ); @@ -2750,271 +3058,22 @@ void GothicAPI::DrawSkeletalMeshVobs( const std::vector& vis, bool updateState, bool drawAttachments ) { - // TODO: Put this into the renderer!! - D3D11GraphicsEngine* g = reinterpret_cast(Engine::GraphicsEngine); - - constexpr float distance = FLT_MAX; - - struct TempVobDrawInfo { - SkeletalVobInfo* VobInfo; - zCModel* Model; - std::vector BoneTransforms; - float4 ModelColor; - float Fatness; - XMMATRIX World; - }; - - static std::vector tempVobList; - tempVobList.clear(); - - for ( SkeletalVobInfo* vi : vis ) { - zCModel* model = static_cast(vi->Vob->GetVisual()); - if ( !model ) { - continue; - } - - model->SetIsVisible( true ); - if ( !vi->VisualInfo ) - continue; // Gothic fortunately sets this to 0 when it throws the model out of the cache - if ( !vi->Vob->GetShowVisual() ) - continue; - - - SkeletalMeshVisualInfo* visual = static_cast(vi->VisualInfo); - - float4 modelColor; - if ( Engine::GAPI->GetRendererState().RendererSettings.EnableShadows ) { - // Let shadows do the work - modelColor = 0xFFFFFFFF; - } else { - if ( vi->Vob->IsIndoorVob() ) { - // All lightmapped polys have this color, so just use it - modelColor = DEFAULT_LIGHTMAP_POLY_COLOR; - } else { - // Get the color from vob position of the ground poly - if ( zCPolygon* polygon = vi->Vob->GetGroundPoly() ) { - static const float inv255f = (1.0f / 255.0f); - float3 vobPos = vi->Vob->GetPositionWorld(); - float3 polyLightStat = polygon->GetLightStatAtPos( vobPos ); - modelColor.x = polyLightStat.z * inv255f; - modelColor.y = polyLightStat.y * inv255f; - modelColor.z = polyLightStat.x * inv255f; - modelColor.w = 1.f; - } else { - modelColor = 0xFFFFFFFF; - } - } - } - XMMATRIX scale = XMMatrixScalingFromVector( model->GetModelScaleXM() ); - - XMMATRIX xmWorld = vi->Vob->GetWorldMatrixXM() * scale; - XMFLOAT4X4 world; XMStoreFloat4x4( &world, xmWorld ); - float fatness = model->GetModelFatness(); - - // Get the bone transforms - static std::vector transforms; - transforms.clear(); - model->GetBoneTransforms( &transforms ); - - if ( updateState ) { - // Update attachments - model->UpdateAttachedVobs(); - model->UpdateMeshLibTexAniState(); - } - - if ( !static_cast(vi->VisualInfo)->SkeletalMeshes.empty() ) { -#ifdef BUILD_GOTHIC_2_6_fix - if ( !model->GetDrawHandVisualsOnly() || *reinterpret_cast(0x57A694) == 0x90 ) { -#else - if ( !model->GetDrawHandVisualsOnly() ) { +#if defined(BUILD_1_12F) + // not implemented in G1 sequel + UseExperimentalSkeletalBatching = false; #endif - Engine::GraphicsEngine->DrawSkeletalMesh( vi, transforms, modelColor, world, fatness ); - } - } else { - if ( model->GetMeshSoftSkinList()->NumInArray > 0 ) { - // Just in case somehow we end up without skeletal meshes and they are available - WorldConverter::ExtractSkeletalMeshFromVob( model, static_cast(vi->VisualInfo) ); - } - } - - if ( drawAttachments ) { - TempVobDrawInfo info = {}; - info.VobInfo = vi; - info.Model = model; - info.BoneTransforms = std::move( transforms ); - info.Fatness = fatness; - info.ModelColor = modelColor; - info.World = xmWorld; - - tempVobList.push_back( info ); - } - - RendererState.RendererInfo.FrameDrawnVobs++; - } - - if ( !drawAttachments ) { - return; - } - - if ( g->GetRenderingStage() == DES_SHADOWMAP_CUBE ) - g->SetActiveVertexShader( "VS_ExNodeCube" ); - else - g->SetActiveVertexShader( "VS_ExNode" ); - - g->SetupVS_ExMeshDrawCall(); - g->SetupVS_ExConstantBuffer(); - - for ( auto& data : tempVobList ) { - auto vi = data.VobInfo; - auto model = data.Model; - auto modelColor = data.ModelColor; - auto& transforms = data.BoneTransforms; - auto fatness = data.Fatness; - auto& world = data.World; - - SkeletalMeshVisualInfo* visual = static_cast(vi->VisualInfo); - // Set up instance info - VS_ExConstantBuffer_PerInstanceNode instanceInfo; - instanceInfo.Color = modelColor; - - // Init the constantbuffer if not already done - if ( !vi->VobConstantBuffer ) - vi->UpdateVobConstantBuffer(); - - std::map>& nodeAttachments = vi->NodeAttachments; - for ( unsigned int i = 0; i < transforms.size(); i++ ) { - // Check for new visual - zCModel* mvis = static_cast( vi->Vob->GetVisual() ); - zCModelNodeInst* node = mvis->GetNodeList()->Array[i]; - - if ( !node->NodeVisual ) - continue; // Happens when you pull your sword for example - - // Check if this is loaded - if ( node->NodeVisual && nodeAttachments.find( i ) == nodeAttachments.end() ) { - // It's not, extract it - WorldConverter::ExtractNodeVisual( i, node, nodeAttachments ); - } - - // Check for changed visual - if ( nodeAttachments[i].size() && node->NodeVisual != nodeAttachments[i][0]->Visual ) { - // Check for deleted attachment - if ( !node->NodeVisual ) { - // Remove attachment - delete nodeAttachments[i][0]; - nodeAttachments[i].clear(); - - LogInfo() << "Removed attachment from model " << vi->VisualInfo->VisualName; - - continue; // Go to next attachment - } - // Load the new one - WorldConverter::ExtractNodeVisual( i, node, nodeAttachments ); - } - - if ( model->GetDrawHandVisualsOnly() ) { - std::string NodeName = node->ProtoNode->NodeName.ToChar(); -#ifdef BUILD_GOTHIC_2_6_fix - if ( NodeName.find( "HAND" ) == std::string::npos && (*reinterpret_cast(0x57A694) != 0x90 || NodeName.find( "ARM" ) == std::string::npos) ) { -#else - if ( NodeName.find( "HAND" ) == std::string::npos ) { -#endif - continue; - } - } - - if ( nodeAttachments.find( i ) != nodeAttachments.end() ) { - - // Setup pixel shader here so that we get correct normals - // Somehow BindShaderForTexture make normals to be inversed - if ( g->GetRenderingStage() == DES_MAIN ) { - g->SetActivePixelShader( "PS_DiffuseAlphaTest" ); - g->BindActivePixelShader(); - } - - // Go through all attachments this node has - for ( MeshVisualInfo* mvi : nodeAttachments[i] ) { - XMMATRIX curTransform = XMLoadFloat4x4( &transforms[i] ); - XMFLOAT4X4 finalWorld; XMStoreFloat4x4( &finalWorld, world* curTransform ); - - if ( !mvi->Visual ) { - LogWarn() << "Attachment without visual on model: " << model->GetVisualName(); - continue; - } - - // Update animated textures - bool isMMS = strcmp( mvi->Visual->GetFileExtension( 0 ), ".MMS" ) == 0; - if ( updateState ) { - node->TexAniState.UpdateTexList(); - if ( isMMS ) { - zCMorphMesh* mm = reinterpret_cast(mvi->Visual); - mm->GetTexAniState()->UpdateTexList(); - } - } - - if ( isMMS ) { - // Only 0.35f of the fatness wanted by gothic. - // They seem to compensate for that with the scaling. - instanceInfo.Fatness = std::max( 0.f, fatness * 0.35f ); - instanceInfo.Scaling = fatness * 0.02f + 1.f; - } else { - instanceInfo.Fatness = 0.f; - instanceInfo.Scaling = 1.f; - } - - auto& VShader = g->GetActiveVS(); - if ( distance < 1000 && isMMS ) { - zCMorphMesh* mm = reinterpret_cast( mvi->Visual ); - // Only draw this as a morphmesh when rendering the main scene or when rendering as ghost - if ( g->GetRenderingStage() == DES_MAIN || g->GetRenderingStage() == DES_GHOST ) { - // Update constantbuffer - instanceInfo.World = finalWorld; - VShader->GetConstantBuffer()[1]->UpdateBuffer( &instanceInfo ); - VShader->GetConstantBuffer()[1]->BindToVertexShader( 1 ); - - if ( updateState ) { - mm->AdvanceAnis(); - mm->CalcVertexPositions(); - } - DrawMorphMesh( mm, mvi->Meshes ); - continue; - } - } - - instanceInfo.World = finalWorld; - VShader->GetConstantBuffer()[1]->UpdateBuffer( &instanceInfo ); - VShader->GetConstantBuffer()[1]->BindToVertexShader( 1 ); - - // Go through all materials registered here - - if ( g->GetRenderingStage() == DES_SHADOWMAP - || g->GetRenderingStage() == DES_SHADOWMAP_CUBE ) { - for ( auto const& itm : mvi->Meshes ) { - // no texture binding for shadowmap + if ( UseExperimentalSkeletalBatching ) { + DrawSkeletalMeshVobs_Batched( vis, updateState, drawAttachments ); + } else { + FXMVECTOR camPos = GetCameraPositionXM(); - // Go through all meshes using that material - for ( unsigned int m = 0; m < itm.second.size(); m++ ) { - DrawMeshInfo( itm.first, itm.second[m] ); - } - } - } else { - for ( auto const& itm : mvi->Meshes ) { - zCTexture* texture; - if ( itm.first && (texture = itm.first->GetAniTexture()) != nullptr ) { - if ( !g->BindTextureNRFX( texture, (g->GetRenderingStage() == DES_MAIN) ) ) - continue; - } + for ( SkeletalVobInfo* vi : vis ) { + float dist; + XMStoreFloat( &dist, XMVector3Length( camPos - vi->Vob->GetPositionWorldXM() ) ); - // Go through all meshes using that material - for ( unsigned int m = 0; m < itm.second.size(); m++ ) { - DrawMeshInfo( itm.first, itm.second[m] ); - } - } - } - } - } + DrawSkeletalMeshVob( vi, dist, updateState, drawAttachments ); } } } @@ -3992,7 +4051,7 @@ void GothicAPI::DebugDrawBSPTree() { // Recursively go through the tree and draw all nodes DebugDrawTreeNode( root, root->BBox3D ); } - + /** Collects vobs using gothics BSP-Tree */ void GothicAPI::CollectVisibleVobs( std::vector& vobs, @@ -4124,7 +4183,7 @@ void GothicAPI::CollectVisibleVobs( } } } - + /** Collects visible sections from the current camera perspective */ void GothicAPI::CollectVisibleSections( std::vector& sections ) { const XMFLOAT3 camPos = Engine::GAPI->GetCameraPosition(); @@ -4308,7 +4367,7 @@ static void CVVH_AddNotDrawnVobToList( callback( ctx, it ); } } - + static void CVVH_AddNotDrawnVobToList( std::vector& source, float dist, const RndCullContext& ctx, diff --git a/D3D11Engine/GothicAPI.h b/D3D11Engine/GothicAPI.h index 9c54bf7f..c3bec476 100644 --- a/D3D11Engine/GothicAPI.h +++ b/D3D11Engine/GothicAPI.h @@ -63,6 +63,18 @@ enum EBspTreeCollectFlags : unsigned int { COLLECT_ALL_NO_MUTATE = COLLECT_ALL_MUTATE & ~COLLECT_MUTATE, }; +typedef size_t NodeAttachmentBatchKey; + +// Batch data for node attachments +struct NodeAttachmentBatchData { + std::vector Mesh; // First mesh encountered + std::vector Instances; // All instances + SkeletalVobInfo* vobInfo; + zCModelNodeInst* node; + MeshVisualInfo* MeshVisualInfo; + zCMaterial* Material; +}; + struct BspInfo { BspInfo() { NumStaticLights = 0; @@ -211,6 +223,22 @@ class GVegetationBox; class zCMorphMesh; class zCDecal; +// CPU-side batch information for skeletal mesh rendering +struct SkeletalMeshBatch { + SkeletalMeshVisualInfo* VisualInfo; // Shared visual info + std::vector Vobs; // All vobs in this batch + std::vector InstanceData; // Per-instance GPU data + std::vector AllBoneTransforms; // All bone matrices for all instances + uint32_t TotalBoneCount; // Total bones across all instances + + void Clear() { + Vobs.clear(); + InstanceData.clear(); + AllBoneTransforms.clear(); + TotalBoneCount = 0; + } +}; + class GothicAPI { friend void CVVH_AddNotDrawnVobToList( std::vector& target, std::vector& source, float dist, int clipFlags, EBspTreeCollectFlags collectFlags, zTCam_ClipType bspClip ); friend void CVVH_AddNotDrawnVobToList( std::vector& target, std::vector& source, float dist, int clipFlags, EBspTreeCollectFlags collectFlags, zTCam_ClipType bspClip ); @@ -307,7 +335,7 @@ class GothicAPI { void DrawWorldMeshNaive(); /** Draws a skeletal mesh-vob */ - void DrawSkeletalMeshVob( SkeletalVobInfo* vi, float distance, bool updateState = true ); + void DrawSkeletalMeshVob( SkeletalVobInfo* vi, float distance, bool updateState = true, bool drawAttachments = true ); void DrawSkeletalMeshVob_Layered( SkeletalVobInfo* vi, float distance, bool updateState = true ); @@ -762,6 +790,9 @@ class GothicAPI { static void ProcessVobAnimation( zCVob* vob, zTAnimationMode aniMode, VobInstanceInfo& vobInstance ); + /** Draw skeletal meshes using batching when possible */ + void DrawSkeletalMeshVobs_Batched( const std::vector& vobs, bool updateState, bool drawAttachments ); + private: /** Collects polygons in the given AABB */ void CollectPolygonsInAABBRec( BspInfo* base, const zTBBox3D& bbox, std::vector& list ); diff --git a/D3D11Engine/GothicMemoryLocations1_08k.h b/D3D11Engine/GothicMemoryLocations1_08k.h index d6703f45..6f7a4baf 100644 --- a/D3D11Engine/GothicMemoryLocations1_08k.h +++ b/D3D11Engine/GothicMemoryLocations1_08k.h @@ -457,6 +457,7 @@ struct GothicMemoryLocations { static const unsigned int Offset_NormalsList = 0x3C; static const unsigned int Offset_Submeshes = 0xA4; static const unsigned int Offset_NumSubmeshes = 0xA8; + static const unsigned int Offset_ProgId = 0xCC; }; struct zCWorld { @@ -633,6 +634,8 @@ struct GothicMemoryLocations { static const unsigned int zCTriggerList = 0x008d7f68; static const unsigned int zCVobStair = 0x008d7708; static const unsigned int zCTexture = 0x008CF110; + static const unsigned int zCMorphMesh = 0x00873d98; + static const unsigned int zCProgMeshProto = 0x008c5b48; }; struct oCInformationManager diff --git a/D3D11Engine/GothicMemoryLocations1_12f.h b/D3D11Engine/GothicMemoryLocations1_12f.h index ea5d7c9d..bf0c8fa9 100644 --- a/D3D11Engine/GothicMemoryLocations1_12f.h +++ b/D3D11Engine/GothicMemoryLocations1_12f.h @@ -416,6 +416,9 @@ struct GothicMemoryLocations { static const unsigned int Offset_NormalsList = 0x3C; static const unsigned int Offset_Submeshes = 0xA4; static const unsigned int Offset_NumSubmeshes = 0xA8; + + // NOT IMPLEMENTED YET! + static const unsigned int Offset_ProgId = 0x0; }; struct zCWorld { @@ -542,6 +545,11 @@ struct GothicMemoryLocations { static const unsigned int oCNpc = 0x00922830; static const unsigned int zCTexture = 0x00914FE0; static const unsigned int oCVisualFX = 0x008AF438; + + // NOT IMPLEMENTED + static const unsigned int zCMorphMesh = 0; + // NOT IMPLEMENTED + static const unsigned int zCProgMeshProto = 0; }; struct oCInformationManager diff --git a/D3D11Engine/GothicMemoryLocations2_6_fix.h b/D3D11Engine/GothicMemoryLocations2_6_fix.h index 23a17b7b..67d3f3ec 100644 --- a/D3D11Engine/GothicMemoryLocations2_6_fix.h +++ b/D3D11Engine/GothicMemoryLocations2_6_fix.h @@ -453,6 +453,7 @@ struct GothicMemoryLocations { static const unsigned int Offset_NormalsList = 0x3C; static const unsigned int Offset_Submeshes = 0xA4; static const unsigned int Offset_NumSubmeshes = 0xA8; + static const unsigned int Offset_ProgId = 0xD0; }; struct zCVob { @@ -691,6 +692,8 @@ struct GothicMemoryLocations { static const unsigned int zCZoneZFogDefault = 0x009a4618; static const unsigned int oCZoneMusicDefault = 0x009a4938; static const unsigned int zCTexture = 0x0099B2F8; + static const unsigned int zCMorphMesh = 0x008d8c00; + static const unsigned int zCProgMeshProto = 0x00982b48; }; struct oCInformationManager diff --git a/D3D11Engine/ImGuiShim.cpp b/D3D11Engine/ImGuiShim.cpp index 09aeca93..dab1ce7c 100644 --- a/D3D11Engine/ImGuiShim.cpp +++ b/D3D11Engine/ImGuiShim.cpp @@ -959,6 +959,7 @@ void RenderAdvancedColumn2( GothicRendererSettings& settings, GothicAPI* gapi ) ImGui::Checkbox( "Draw Skeletal Meshes", &settings.DrawSkeletalMeshes ); ImGui::BeginDisabled( !settings.DrawSkeletalMeshes ); + ImGui::Checkbox( "Enable Experimental Batching", &UseExperimentalSkeletalBatching ); ImGui::SliderFloat( "SkeletalMeshDrawRadius", &settings.SkeletalMeshDrawRadius, 0.0f, 18000.0f, "%.0f", ImGuiSliderFlags_::ImGuiSliderFlags_ClampOnInput ); ImGui::SetItemTooltip( "Draw distance for NPCs" ); ImGui::EndDisabled(); diff --git a/D3D11Engine/Shaders/VS_ExNodeInstanced.hlsl b/D3D11Engine/Shaders/VS_ExNodeInstanced.hlsl new file mode 100644 index 00000000..0f1633d3 --- /dev/null +++ b/D3D11Engine/Shaders/VS_ExNodeInstanced.hlsl @@ -0,0 +1,74 @@ +//-------------------------------------------------------------------------------------- +// Instanced Node Attachment Vertex Shader +// For weapons, heads, and other attachments +//-------------------------------------------------------------------------------------- +#include "Globals_VS_ExConstants.h" + +cbuffer Matrices_PerFrame : register( b0 ) +{ + VS_ExConstantBuffer_PerFrame frame; +}; + +// Per-instance data from StructuredBuffer +struct NodeAttachmentInstance +{ + matrix World; + matrix PrevWorld; // TODO: implement motion vectors + float4 Color; + float Fatness; + float Scaling; + float2 Padding; +}; + +StructuredBuffer InstanceBuffer : register(t10); + +struct VS_INPUT +{ + float3 vPosition : POSITION; + float3 vNormal : NORMAL; + float2 vTex1 : TEXCOORD0; + float2 vTex2 : TEXCOORD1; + float4 vDiffuse : DIFFUSE; +}; + +struct VS_OUTPUT +{ + float2 vTexcoord : TEXCOORD0; + float2 vTexcoord2 : TEXCOORD1; + float4 vDiffuse : TEXCOORD2; + float3 vNormalVS : TEXCOORD4; + float3 vViewPosition : TEXCOORD5; + float4 vCurrClipPos : TEXCOORD6; // Current clip position for velocity + float4 vPrevClipPos : TEXCOORD7; // Previous clip position for velocity + float4 vPosition : SV_POSITION; +}; + +VS_OUTPUT VSMain(VS_INPUT Input, uint instanceID : SV_InstanceID) +{ + VS_OUTPUT Output; + + // Get instance data + NodeAttachmentInstance inst = InstanceBuffer[instanceID]; + + // Apply scaling and fatness + float3 position = Input.vPosition * inst.Scaling; + position += Input.vNormal * inst.Fatness; + + // Transform to world space + float3 positionWorld = mul(float4(position, 1), inst.World).xyz; + + // Output + Output.vPosition = mul(float4(positionWorld, 1), frame.M_ViewProj); + Output.vTexcoord = Input.vTex1; + Output.vTexcoord2 = Input.vTex2; + Output.vDiffuse = inst.Color; + Output.vNormalVS = mul(Input.vNormal, (float3x3)mul(inst.World, frame.M_View)); + Output.vViewPosition = mul(float4(positionWorld, 1), frame.M_View).xyz; + + // Motion Vectors - use UNJITTERED matrices for correct velocity + Output.vCurrClipPos = mul(float4(positionWorld, 1), frame.M_UnjitteredViewProj); + float3 prevPositionWorld = mul(float4(position, 1), inst.PrevWorld).xyz; + Output.vPrevClipPos = mul(float4(prevPositionWorld, 1), frame.M_PrevViewProj); + + return Output; +} \ No newline at end of file diff --git a/D3D11Engine/Shaders/VS_ExNodeInstancedCube.hlsl b/D3D11Engine/Shaders/VS_ExNodeInstancedCube.hlsl new file mode 100644 index 00000000..c257ee84 --- /dev/null +++ b/D3D11Engine/Shaders/VS_ExNodeInstancedCube.hlsl @@ -0,0 +1,69 @@ +//-------------------------------------------------------------------------------------- +// Instanced Node Attachment Vertex Shader for Cube Shadow Maps +// For weapons, heads, and other attachments +//-------------------------------------------------------------------------------------- + +#include "Globals_VS_ExConstants.h" + +cbuffer Matrices_PerFrame : register( b0 ) +{ + VS_ExConstantBuffer_PerFrame frame; +}; + +// Per-instance data from StructuredBuffer +struct NodeAttachmentInstance +{ + matrix World; + float4 Color; + float Fatness; + float Scaling; + float2 Padding; +}; + +StructuredBuffer InstanceBuffer : register(t10); + +struct VS_INPUT +{ + float3 vPosition : POSITION; + float3 vNormal : NORMAL; + float2 vTex1 : TEXCOORD0; + float2 vTex2 : TEXCOORD1; + float4 vDiffuse : DIFFUSE; +}; + +struct VS_OUTPUT +{ + float2 vTexcoord : TEXCOORD0; + float2 vTexcoord2 : TEXCOORD1; + float4 vDiffuse : TEXCOORD2; + float3 vNormalVS : TEXCOORD4; + float3 vViewPosition : TEXCOORD5; + float3 vWorldPosition : TEXCOORD6; + float4 vPosition : SV_POSITION; +}; + +VS_OUTPUT VSMain(VS_INPUT Input, uint instanceID : SV_InstanceID) +{ + VS_OUTPUT Output; + + // Get instance data + NodeAttachmentInstance inst = InstanceBuffer[instanceID]; + + // Apply scaling and fatness + float3 position = Input.vPosition * inst.Scaling; + position += Input.vNormal * inst.Fatness; + + // Transform to world space + float3 positionWorld = mul(float4(position, 1), inst.World).xyz; + + // Output + Output.vPosition = mul(float4(positionWorld, 1), frame.M_ViewProj); + Output.vTexcoord = Input.vTex1; + Output.vTexcoord2 = Input.vTex2; + Output.vDiffuse = inst.Color; + Output.vNormalVS = mul(Input.vNormal, (float3x3)mul(inst.World, frame.M_View)); + Output.vViewPosition = mul(float4(positionWorld, 1), frame.M_View).xyz; + Output.vWorldPosition = positionWorld; // Needed for cube shadow map distance calculation + + return Output; +} \ No newline at end of file diff --git a/D3D11Engine/Shaders/VS_ExSkeletalInstanced.hlsl b/D3D11Engine/Shaders/VS_ExSkeletalInstanced.hlsl new file mode 100644 index 00000000..2b1e8775 --- /dev/null +++ b/D3D11Engine/Shaders/VS_ExSkeletalInstanced.hlsl @@ -0,0 +1,106 @@ +//-------------------------------------------------------------------------------------- +// Instanced Skeletal Vertex Shader +// Uses StructuredBuffers for per-instance data and bone transforms +//-------------------------------------------------------------------------------------- + +static const int NUM_MAX_BONES = 96; + +#include "Globals_VS_ExConstants.h" + +cbuffer Matrices_PerFrame : register( b0 ) +{ + VS_ExConstantBuffer_PerFrame frame; +}; + +// Per-instance data stored in StructuredBuffer +struct SkeletalInstanceData +{ + matrix World; + matrix PrevWorld; + float4 Color; + float Fatness; + float Scale; + uint BoneOffset; + uint Padding; +}; + +// Structured buffers for instanced data +StructuredBuffer InstanceBuffer : register(t10); +StructuredBuffer BoneBuffer : register(t11); + +//-------------------------------------------------------------------------------------- +// Input / Output structures +//-------------------------------------------------------------------------------------- +struct VS_INPUT +{ + float4 vPosition[4] : POSITION; + float3 vNormal : NORMAL; + float3 vBindPoseNormal : TEXCOORD0; + float2 vTex1 : TEXCOORD1; + uint4 BoneIndices : BONEIDS; + float4 Weights : WEIGHTS; +}; + +struct VS_OUTPUT +{ + float2 vTexcoord : TEXCOORD0; + float2 vTexcoord2 : TEXCOORD1; + float4 vDiffuse : TEXCOORD2; + float3 vNormalVS : TEXCOORD4; + float3 vViewPosition : TEXCOORD5; + float4 vCurrClipPos : TEXCOORD6; // Current clip position for velocity + float4 vPrevClipPos : TEXCOORD7; // Previous clip position for velocity + float4 vPosition : SV_POSITION; +}; + +//-------------------------------------------------------------------------------------- +// Vertex Shader +//-------------------------------------------------------------------------------------- +VS_OUTPUT VSMain(VS_INPUT Input, uint instanceID : SV_InstanceID) +{ + VS_OUTPUT Output; + + // Get instance data + SkeletalInstanceData inst = InstanceBuffer[instanceID]; + + // Calculate bone indices with offset for this instance + uint bone0 = inst.BoneOffset + Input.BoneIndices.x; + uint bone1 = inst.BoneOffset + Input.BoneIndices.y; + uint bone2 = inst.BoneOffset + Input.BoneIndices.z; + uint bone3 = inst.BoneOffset + Input.BoneIndices.w; + + // Skin the vertex position + float3 position = float3(0, 0, 0); + position += Input.Weights.x * mul(float4(Input.vPosition[0].xyz, 1), BoneBuffer[bone0]).xyz; + position += Input.Weights.y * mul(float4(Input.vPosition[1].xyz, 1), BoneBuffer[bone1]).xyz; + position += Input.Weights.z * mul(float4(Input.vPosition[2].xyz, 1), BoneBuffer[bone2]).xyz; + position += Input.Weights.w * mul(float4(Input.vPosition[3].xyz, 1), BoneBuffer[bone3]).xyz; + + // Skin the normal + float3 normal = float3(0, 0, 0); + normal += Input.Weights.x * mul(Input.vNormal, (float3x3)BoneBuffer[bone0]); + normal += Input.Weights.y * mul(Input.vNormal, (float3x3)BoneBuffer[bone1]); + normal += Input.Weights.z * mul(Input.vNormal, (float3x3)BoneBuffer[bone2]); + normal += Input.Weights.w * mul(Input.vNormal, (float3x3)BoneBuffer[bone3]); + + // Apply fatness displacement + position += inst.Fatness * normalize(normal); + + // Transform to world space using instance world matrix + float3 positionWorld = mul(float4(position, 1), inst.World).xyz; + + // Output + Output.vPosition = mul(float4(positionWorld, 1), frame.M_ViewProj); + Output.vTexcoord = Input.vTex1; + Output.vTexcoord2 = Input.vTex1; + Output.vDiffuse = inst.Color; + Output.vNormalVS = mul(Input.vBindPoseNormal, (float3x3)mul(inst.World, frame.M_View)); + Output.vViewPosition = mul(float4(positionWorld, 1), frame.M_View).xyz; + + // Motion Vectors - use UNJITTERED matrices for correct velocity + Output.vCurrClipPos = mul(float4(positionWorld, 1), frame.M_UnjitteredViewProj); + float3 prevPositionWorld = mul(float4(position, 1), inst.PrevWorld).xyz; + Output.vPrevClipPos = mul(float4(prevPositionWorld, 1), frame.M_PrevViewProj); + + return Output; +} \ No newline at end of file diff --git a/D3D11Engine/Shaders/VS_ExSkeletalInstancedCube.hlsl b/D3D11Engine/Shaders/VS_ExSkeletalInstancedCube.hlsl new file mode 100644 index 00000000..bf1bc10a --- /dev/null +++ b/D3D11Engine/Shaders/VS_ExSkeletalInstancedCube.hlsl @@ -0,0 +1,84 @@ +//-------------------------------------------------------------------------------------- +// Instanced Skeletal Vertex Shader for Cube Shadow Maps +//-------------------------------------------------------------------------------------- + +static const int NUM_MAX_BONES = 96; + +#include "Globals_VS_ExConstants.h" + +cbuffer Matrices_PerFrame : register( b0 ) +{ + VS_ExConstantBuffer_PerFrame frame; +}; + +struct SkeletalInstanceData +{ + matrix World; + float4 Color; + float Fatness; + float Scale; + uint BoneOffset; + uint Padding; +}; + +StructuredBuffer InstanceBuffer : register(t10); +StructuredBuffer BoneBuffer : register(t11); + +struct VS_INPUT +{ + float4 vPosition[4] : POSITION; + float3 vNormal : NORMAL; + float3 vBindPoseNormal : TEXCOORD0; + float2 vTex1 : TEXCOORD1; + uint4 BoneIndices : BONEIDS; + float4 Weights : WEIGHTS; +}; + +struct VS_OUTPUT +{ + float2 vTexcoord : TEXCOORD0; + float2 vTexcoord2 : TEXCOORD1; + float4 vDiffuse : TEXCOORD2; + float3 vNormalVS : TEXCOORD4; + float3 vViewPosition : TEXCOORD5; + float3 vWorldPosition : TEXCOORD6; + float4 vPosition : SV_POSITION; +}; + +VS_OUTPUT VSMain(VS_INPUT Input, uint instanceID : SV_InstanceID) +{ + VS_OUTPUT Output; + + SkeletalInstanceData inst = InstanceBuffer[instanceID]; + + uint bone0 = inst.BoneOffset + Input.BoneIndices.x; + uint bone1 = inst.BoneOffset + Input.BoneIndices.y; + uint bone2 = inst.BoneOffset + Input.BoneIndices.z; + uint bone3 = inst.BoneOffset + Input.BoneIndices.w; + + float3 position = float3(0, 0, 0); + position += Input.Weights.x * mul(float4(Input.vPosition[0].xyz, 1), BoneBuffer[bone0]).xyz; + position += Input.Weights.y * mul(float4(Input.vPosition[1].xyz, 1), BoneBuffer[bone1]).xyz; + position += Input.Weights.z * mul(float4(Input.vPosition[2].xyz, 1), BoneBuffer[bone2]).xyz; + position += Input.Weights.w * mul(float4(Input.vPosition[3].xyz, 1), BoneBuffer[bone3]).xyz; + + float3 normal = float3(0, 0, 0); + normal += Input.Weights.x * mul(Input.vNormal, (float3x3)BoneBuffer[bone0]); + normal += Input.Weights.y * mul(Input.vNormal, (float3x3)BoneBuffer[bone1]); + normal += Input.Weights.z * mul(Input.vNormal, (float3x3)BoneBuffer[bone2]); + normal += Input.Weights.w * mul(Input.vNormal, (float3x3)BoneBuffer[bone3]); + + position += inst.Fatness * normalize(normal); + + float3 positionWorld = mul(float4(position, 1), inst.World).xyz; + + Output.vPosition = mul(float4(positionWorld, 1), frame.M_ViewProj); + Output.vTexcoord = Input.vTex1; + Output.vTexcoord2 = Input.vTex1; + Output.vDiffuse = inst.Color; + Output.vNormalVS = mul(Input.vBindPoseNormal, (float3x3)mul(inst.World, frame.M_View)); + Output.vViewPosition = mul(float4(positionWorld, 1), frame.M_View).xyz; + Output.vWorldPosition = positionWorld; + + return Output; +} \ No newline at end of file diff --git a/D3D11Engine/Toolbox.h b/D3D11Engine/Toolbox.h index 371dc8b7..6c29c1e9 100644 --- a/D3D11Engine/Toolbox.h +++ b/D3D11Engine/Toolbox.h @@ -13,6 +13,26 @@ struct zTBBox3D; struct zTPlane; namespace Toolbox { + + inline UINT NextPowerOfTwo( UINT v ) { + if ( v == 0 ) return 1; + v--; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + return v + 1; + } + + template + inline size_t hash_combine( size_t& seed, const T& v ) { + std::hash hasher; + // Boost's formula: seed ^= hash(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2) + seed ^= hasher( v ) + 0x9e3779b9 + (seed << 6) + (seed >> 2); + return seed; + } + FORCEINLINE float GetRecommendedWorldShadowRangeScaleForSize( int size ) { constexpr int MAX_SHADOWMAP_SIZE = 16384; return static_cast(MAX_SHADOWMAP_SIZE / size); diff --git a/D3D11Engine/oCNPC.h b/D3D11Engine/oCNPC.h index a5c94d2e..4b1d9ae3 100644 --- a/D3D11Engine/oCNPC.h +++ b/D3D11Engine/oCNPC.h @@ -73,10 +73,10 @@ class oCNPC : public zCVob { int IsAPlayer() { return (this == *reinterpret_cast(GothicMemoryLocations::oCGame::Var_Player)); } - zSTRING GetName( int i = 0 ) { + std::string GetName( int i = 0 ) { zSTRING str; reinterpret_cast( GothicMemoryLocations::oCNPC::GetName )( this, 0, str, i ); - return str; + return std::string(str.ToChar()); } #ifndef BUILD_SPACER bool HasFlag( oCNPCFlags flag ) { diff --git a/D3D11Engine/pch.h b/D3D11Engine/pch.h index b1c9338a..b5c9bef7 100644 --- a/D3D11Engine/pch.h +++ b/D3D11Engine/pch.h @@ -44,6 +44,7 @@ __declspec(selectany) const char* VERSION_NUMBER_STR = VERSION_NUMBER; extern bool FeatureLevel10Compatibility; extern bool GMPModeActive; +extern bool UseExperimentalSkeletalBatching; /** D3D7-Call logging */ #define DebugWriteValue(value, check) if (value == check) { LogInfo() << " - " << #check; } diff --git a/D3D11Engine/zCMaterial.h b/D3D11Engine/zCMaterial.h index e881e66b..919d069b 100644 --- a/D3D11Engine/zCMaterial.h +++ b/D3D11Engine/zCMaterial.h @@ -90,8 +90,8 @@ class zCMaterial { } /** Returns the color-mod of this material */ - DWORD GetColor() { - return *reinterpret_cast(THISPTR_OFFSET( GothicMemoryLocations::zCMaterial::Offset_Color )); + zColor GetColor() { + return *reinterpret_cast(THISPTR_OFFSET( GothicMemoryLocations::zCMaterial::Offset_Color )); } /** Returns single texture, because not all seem to be animated and returned by GetAniTexture? */ diff --git a/D3D11Engine/zCMorphMesh.h b/D3D11Engine/zCMorphMesh.h index f6a18556..f2860715 100644 --- a/D3D11Engine/zCMorphMesh.h +++ b/D3D11Engine/zCMorphMesh.h @@ -9,6 +9,10 @@ class zCMorphMesh { public: + static const zCClassDef* GetStaticClassDef() { + return reinterpret_cast(GothicMemoryLocations::zCClassDef::zCMorphMesh); + } + zCProgMeshProto* GetMorphMesh() { return *reinterpret_cast(THISPTR_OFFSET( GothicMemoryLocations::zCMorphMesh::Offset_MorphMesh )); } diff --git a/D3D11Engine/zCObject.h b/D3D11Engine/zCObject.h index 263d48cd..b1b68373 100644 --- a/D3D11Engine/zCObject.h +++ b/D3D11Engine/zCObject.h @@ -20,6 +20,16 @@ class zCObject { fn(this, arg); }*/ + static bool CheckInheritance( const zCClassDef* def, const zCClassDef* target ) { + while ( def ) { + if ( def == target ) { + return true; + } + def = def->baseClassDef; + } + return false; + } + int refCtr; VERTEX_INDEX hashIndex; zCObject* hashNext; diff --git a/D3D11Engine/zCProgMeshProto.h b/D3D11Engine/zCProgMeshProto.h index 70d58204..39e91b01 100644 --- a/D3D11Engine/zCProgMeshProto.h +++ b/D3D11Engine/zCProgMeshProto.h @@ -54,6 +54,10 @@ class zCSubMesh { class zCProgMeshProto : public zCVisual { public: + static const zCClassDef* GetStaticClassDef() { + return reinterpret_cast(GothicMemoryLocations::zCClassDef::zCProgMeshProto); + } + zCArrayAdapt* GetPositionList() { return reinterpret_cast*>(THISPTR_OFFSET( GothicMemoryLocations::zCProgMeshProto::Offset_PositionList )); } @@ -87,4 +91,8 @@ class zCProgMeshProto : public zCVisual { vertices->push_back( vx ); } } + + DWORD GetProgId() { + return *reinterpret_cast(THISPTR_OFFSET( GothicMemoryLocations::zCProgMeshProto::Offset_ProgId )); + } }; diff --git a/D3D11Engine/zCTexture.h b/D3D11Engine/zCTexture.h index 1781f956..e3c685ac 100644 --- a/D3D11Engine/zCTexture.h +++ b/D3D11Engine/zCTexture.h @@ -138,7 +138,6 @@ class zCTexture { return (flags & GothicMemoryLocations::zCTexture::Mask_FlagHasAlpha) != 0; } -private: const zSTRING& __GetName() { return reinterpret_cast( GothicMemoryLocations::zCObject::GetObjectName )( this ); } diff --git a/D3D11Engine/zCVisual.h b/D3D11Engine/zCVisual.h index b03fced0..3a5b0db9 100644 --- a/D3D11Engine/zCVisual.h +++ b/D3D11Engine/zCVisual.h @@ -71,6 +71,15 @@ class zCVisual { return VT_OTHER; } + /** Checks the inheritance chain and casts to T* if possible. Returns nullptr otherwise */ + template + T* As() { + zCClassDef* classDef = reinterpret_cast(this)->_GetClassDef(); + if ( zCObject::CheckInheritance( classDef, T::GetStaticClassDef() ) ) { + return reinterpret_cast(this); + } + return nullptr; + } private: zSTRING& __GetObjectName() { diff --git a/D3D11Engine/zCVob.h b/D3D11Engine/zCVob.h index 808f31d8..57db152f 100644 --- a/D3D11Engine/zCVob.h +++ b/D3D11Engine/zCVob.h @@ -369,24 +369,13 @@ class zCVob { template T* As() { zCClassDef* classDef = reinterpret_cast(this)->_GetClassDef(); - if ( CheckInheritance( classDef, T::GetStaticClassDef() ) ) { + if ( zCObject::CheckInheritance( classDef, T::GetStaticClassDef() ) ) { return reinterpret_cast(this); } return nullptr; } -protected: - - bool CheckInheritance( const zCClassDef* def, const zCClassDef* target ) { - while ( def ) { - if ( def == target ) { - return true; - } - def = def->baseClassDef; - } - return false; - } zSTRING& __GetObjectName() { - return reinterpret_cast( GothicMemoryLocations::zCObject::GetObjectName )( this ); + return reinterpret_cast(GothicMemoryLocations::zCObject::GetObjectName)(this); } }; From 4a8379a375f237116027ee3b3be286ce747c2891 Mon Sep 17 00:00:00 2001 From: kirides <13602143+kirides@users.noreply.github.com> Date: Tue, 17 Feb 2026 17:04:17 +0100 Subject: [PATCH 10/13] handle skeletal meshes on a mesh-by-mesh base instead of vob-kind by vob-kind, share index and vertex buffers for skeletal meshes globally --- D3D11Engine/D3D11Engine.vcxproj | 2 + D3D11Engine/D3D11Engine.vcxproj.filters | 7 + D3D11Engine/D3D11GraphicsEngine.cpp | 787 +++++++++++------- D3D11Engine/D3D11GraphicsEngine.h | 15 +- D3D11Engine/D3D11OcclusionQuerry.cpp | 2 +- D3D11Engine/GothicAPI.cpp | 210 ++--- D3D11Engine/GothicAPI.h | 33 +- D3D11Engine/MeshManager.cpp | 13 + D3D11Engine/MeshManager.h | 41 + D3D11Engine/Shaders/VS_ExNodeInstanced.hlsl | 11 +- .../Shaders/VS_ExNodeInstancedCube.hlsl | 10 +- .../Shaders/VS_ExSkeletalInstanced.hlsl | 9 +- .../Shaders/VS_ExSkeletalInstancedCube.hlsl | 9 +- D3D11Engine/WorldConverter.cpp | 289 +++++-- D3D11Engine/WorldConverter.h | 1 + D3D11Engine/WorldObjects.cpp | 17 +- D3D11Engine/WorldObjects.h | 26 +- 17 files changed, 921 insertions(+), 561 deletions(-) create mode 100644 D3D11Engine/MeshManager.cpp create mode 100644 D3D11Engine/MeshManager.h diff --git a/D3D11Engine/D3D11Engine.vcxproj b/D3D11Engine/D3D11Engine.vcxproj index d05f0e6a..fc8d1118 100644 --- a/D3D11Engine/D3D11Engine.vcxproj +++ b/D3D11Engine/D3D11Engine.vcxproj @@ -968,6 +968,7 @@ copy "$(OutDir)$(TargetName).pdb" "$(G1_SYSTEM_PATH)\ddraw.pdb" + @@ -1155,6 +1156,7 @@ copy "$(OutDir)$(TargetName).pdb" "$(G1_SYSTEM_PATH)\ddraw.pdb" + Create diff --git a/D3D11Engine/D3D11Engine.vcxproj.filters b/D3D11Engine/D3D11Engine.vcxproj.filters index e05580c2..64e07727 100644 --- a/D3D11Engine/D3D11Engine.vcxproj.filters +++ b/D3D11Engine/D3D11Engine.vcxproj.filters @@ -807,6 +807,10 @@ Engine\Graph + + + Engine\GAPI + @@ -1084,6 +1088,9 @@ Engine\Graph + + Engine\GAPI + diff --git a/D3D11Engine/D3D11GraphicsEngine.cpp b/D3D11Engine/D3D11GraphicsEngine.cpp index 65ac8c1d..39311405 100644 --- a/D3D11Engine/D3D11GraphicsEngine.cpp +++ b/D3D11Engine/D3D11GraphicsEngine.cpp @@ -2187,8 +2187,8 @@ XRESULT D3D11GraphicsEngine::DrawSkeletalVertexNormals( SkeletalVobInfo* vi, for ( auto& mesh : itm.second ) { WhiteTexture->BindToPixelShader( 0 ); - D3D11VertexBuffer* vb = mesh->MeshVertexBuffer; - D3D11VertexBuffer* ib = mesh->MeshIndexBuffer; + auto vb = mesh->MeshVertexBuffer; + auto ib = mesh->MeshIndexBuffer; unsigned int numIndices = mesh->Indices.size(); UINT offset = 0; @@ -2290,8 +2290,8 @@ XRESULT D3D11GraphicsEngine::DrawSkeletalMesh( SkeletalVobInfo* vi, } for ( auto& mesh : itm.second ) { - D3D11VertexBuffer* vb = mesh->MeshVertexBuffer; - D3D11VertexBuffer* ib = mesh->MeshIndexBuffer; + auto vb = mesh->MeshVertexBuffer; + auto ib = mesh->MeshIndexBuffer; unsigned int numIndices = mesh->Indices.size(); UINT offset = 0; @@ -2382,8 +2382,8 @@ XRESULT D3D11GraphicsEngine::DrawSkeletalMesh_Layered( SkeletalVobInfo* vi, } } - D3D11VertexBuffer* vb = mesh->MeshVertexBuffer; - D3D11VertexBuffer* ib = mesh->MeshIndexBuffer; + auto vb = mesh->MeshVertexBuffer; + auto ib = mesh->MeshIndexBuffer; unsigned int numIndices = mesh->Indices.size(); UINT offset = 0; @@ -3400,8 +3400,8 @@ XRESULT D3D11GraphicsEngine::DrawMeshInfoListAlphablended( // Bind wrapped mesh vertex buffers DrawVertexBufferIndexedUINT( - Engine::GAPI->GetWrappedWorldMesh()->MeshVertexBuffer, - Engine::GAPI->GetWrappedWorldMesh()->MeshIndexBuffer, 0, 0 ); + Engine::GAPI->GetWrappedWorldMesh()->MeshVertexBuffer.get(), + Engine::GAPI->GetWrappedWorldMesh()->MeshIndexBuffer.get(), 0, 0); int lastAlphaFunc = 0; @@ -3527,7 +3527,7 @@ XRESULT D3D11GraphicsEngine::DrawWorldMesh_Indirect( bool noTextures ) { Engine::GAPI->CollectVisibleSections( renderList ); MeshInfo* meshInfo = Engine::GAPI->GetWrappedWorldMesh(); - DrawVertexBufferIndexedUINT( meshInfo->MeshVertexBuffer, meshInfo->MeshIndexBuffer, 0, 0 ); + DrawVertexBufferIndexedUINT( meshInfo->MeshVertexBuffer.get(), meshInfo->MeshIndexBuffer.get(), 0, 0); auto& context = GetContext(); context->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST ); @@ -3775,7 +3775,7 @@ XRESULT D3D11GraphicsEngine::DrawWorldMesh( bool noTextures ) { Engine::GAPI->CollectVisibleSections( renderList ); MeshInfo* meshInfo = Engine::GAPI->GetWrappedWorldMesh(); - DrawVertexBufferIndexedUINT( meshInfo->MeshVertexBuffer, meshInfo->MeshIndexBuffer, 0, 0 ); + DrawVertexBufferIndexedUINT( meshInfo->MeshVertexBuffer.get(), meshInfo->MeshIndexBuffer.get(), 0, 0); static std::vector> meshList; auto CompareMesh = []( std::pair& a, std::pair& b ) -> bool { return a.first.Texture < b.first.Texture; }; @@ -3955,8 +3955,8 @@ void D3D11GraphicsEngine::DrawWaterSurfaces() { // Bind wrapped mesh vertex buffers DrawVertexBufferIndexedUINT( - Engine::GAPI->GetWrappedWorldMesh()->MeshVertexBuffer, - Engine::GAPI->GetWrappedWorldMesh()->MeshIndexBuffer, 0, 0 ); + Engine::GAPI->GetWrappedWorldMesh()->MeshVertexBuffer.get(), + Engine::GAPI->GetWrappedWorldMesh()->MeshIndexBuffer.get(), 0, 0); for ( const auto& [texture, meshes] : FrameWaterSurfaces ) { // Draw surfaces for ( const auto& mesh : meshes ) { @@ -4095,8 +4095,8 @@ void XM_CALLCONV D3D11GraphicsEngine::DrawWorldAround( if ( Engine::GAPI->GetRendererState().RendererSettings.DrawWorldMesh ) { // Bind wrapped mesh vertex buffers - DrawVertexBufferIndexedUINT( Engine::GAPI->GetWrappedWorldMesh()->MeshVertexBuffer, - Engine::GAPI->GetWrappedWorldMesh()->MeshIndexBuffer, 0, 0 ); + DrawVertexBufferIndexedUINT( Engine::GAPI->GetWrappedWorldMesh()->MeshVertexBuffer.get(), + Engine::GAPI->GetWrappedWorldMesh()->MeshIndexBuffer.get(), 0, 0); ActiveVS->GetConstantBuffer()[1]->UpdateBuffer( &XMMatrixIdentity() ); ActiveVS->GetConstantBuffer()[1]->BindToVertexShader( 1 ); @@ -4242,8 +4242,8 @@ void XM_CALLCONV D3D11GraphicsEngine::DrawWorldAround( } for ( auto const& meshInfo : materialMesh.second ) { DrawVertexBufferIndexed( - meshInfo->MeshVertexBuffer, - meshInfo->MeshIndexBuffer, + meshInfo->MeshVertexBuffer.get(), + meshInfo->MeshIndexBuffer.get(), meshInfo->Indices.size() ); } } @@ -4403,8 +4403,8 @@ void XM_CALLCONV D3D11GraphicsEngine::DrawWorldAround_Layered( if ( Engine::GAPI->GetRendererState().RendererSettings.DrawWorldMesh ) { // Bind wrapped mesh vertex buffers - DrawVertexBufferInstancedIndexedUINT( Engine::GAPI->GetWrappedWorldMesh()->MeshVertexBuffer, - Engine::GAPI->GetWrappedWorldMesh()->MeshIndexBuffer, 0, 0, 0 ); + DrawVertexBufferInstancedIndexedUINT( Engine::GAPI->GetWrappedWorldMesh()->MeshVertexBuffer.get(), + Engine::GAPI->GetWrappedWorldMesh()->MeshIndexBuffer.get(), 0, 0, 0 ); ActiveVS->GetConstantBuffer()[1]->UpdateBuffer( &XMMatrixIdentity() ); ActiveVS->GetConstantBuffer()[1]->BindToVertexShader( 1 ); @@ -4551,8 +4551,8 @@ void XM_CALLCONV D3D11GraphicsEngine::DrawWorldAround_Layered( } for ( auto const& meshInfo : materialMesh.second ) { DrawVertexBufferInstancedIndexed( - meshInfo->MeshVertexBuffer, - meshInfo->MeshIndexBuffer, + meshInfo->MeshVertexBuffer.get(), + meshInfo->MeshIndexBuffer.get(), meshInfo->Indices.size(), 6 ); } } @@ -4650,12 +4650,20 @@ XRESULT D3D11GraphicsEngine::InitSkeletalInstancingBuffers() { // Bone transform buffer - holds all bone matrices for all instances SkeletalBoneBuffer = std::make_unique>(); - hr = SkeletalBoneBuffer->Init( GetDevice().Get(), 2048 ); + hr = SkeletalBoneBuffer->Init( GetDevice().Get(), 512 ); if ( FAILED( hr ) ) { LogError() << "Failed to create skeletal bone buffer"; return XR_FAILED; } + // Instance index indirection buffer - maps SV_InstanceID to actual instance index + SkeletalInstanceIndexBuffer = std::make_unique>(); + hr = SkeletalInstanceIndexBuffer->Init( GetDevice().Get(), MAX_SKELETAL_INSTANCES * 4 ); + if ( FAILED( hr ) ) { + LogError() << "Failed to create skeletal instance index buffer"; + return XR_FAILED; + } + LogInfo() << "Initialized skeletal instancing buffers"; return XR_SUCCESS; } @@ -4663,7 +4671,11 @@ XRESULT D3D11GraphicsEngine::InitSkeletalInstancingBuffers() { void D3D11GraphicsEngine::BuildSkeletalMeshBatches( const std::vector& vobs ) { // Clear previous batches ClearSkeletalMeshBatches(); - + + std::vector allBones; + std::vector allInstances; + std::vector allEntries; + for ( SkeletalVobInfo* vi : vobs ) { if ( !vi || !vi->VisualInfo || !vi->Vob ) { continue; @@ -4691,111 +4703,58 @@ void D3D11GraphicsEngine::BuildSkeletalMeshBatches( const std::vectorUpdateMeshLibTexAniState(); - - if ( model->GetMeshLibList() && model->GetMeshLibList()->GetSize() ) { - for ( unsigned int i = 0; i < model->GetMeshLibList()->GetSize(); i++ ) { - auto ptr = model->GetMeshLibList()->Array[i]->MeshLibPtr; - Toolbox::hash_combine( batchKey, (int)ptr ); - hashLen++; - } - if (RenderingStage != DES_SHADOWMAP ) { - for ( auto& [k, v] : visualInfo->SkeletalMeshes ) { - Toolbox::hash_combine( batchKey, std::string_view(k->GetAniTexture()->__GetName().ToChar()) ); - hashLen++; - } - } + + // Build instance data + SkeletalInstanceData inst = {}; + + auto boneIndex = allBones.size(); + model->GetBoneTransforms(&allBones); + + // World matrix with scale + XMMATRIX scale = XMMatrixScalingFromVector( model->GetModelScaleXM() ); + XMMATRIX world = vi->Vob->GetWorldMatrixXM() * scale; + XMStoreFloat4x4( &inst.World, world ); + inst.PrevWorld = vi->HasValidPrevTransforms ? vi->PrevWorldMatrix : inst.World; + + // Model color + if ( Engine::GAPI->GetRendererState().RendererSettings.EnableShadows ) { + inst.Color = float4( 1, 1, 1, 1 ); } else { - // unique draw, no batching - Toolbox::hash_combine( batchKey, (int)vi->Vob ); - hashLen++; - } - if ( hashLen > largestSeenHash ) { - largestSeenHash = hashLen; - } - for ( size_t i = hashLen; i < largestSeenHash; ++i ) { - // to avoid collisions we need to make sure that shorter hashes don't collide with longer ones that have the same start - Toolbox::hash_combine( batchKey, i ); - } - - auto& batch = SkeletalMeshBatches[batchKey]; - if ( !batch.Vobs.size() ) { - batch.VisualInfo = visualInfo; - batch.Vobs.reserve(25); - } - batch.Vobs.push_back( vi ); - } - - // Build instance data for each batch - static std::vector transforms; - for ( auto& [name, batch] : SkeletalMeshBatches ) { - if ( batch.Vobs.empty() || !batch.VisualInfo ) { - continue; - } - - batch.TotalBoneCount = 0; - batch.InstanceData.clear(); - batch.AllBoneTransforms.clear(); - batch.InstanceData.reserve( batch.Vobs.size() ); - - for ( SkeletalVobInfo* vi : batch.Vobs ) { - zCModel* model = static_cast(vi->Vob->GetVisual()); - if ( !model ) { - continue; - } - - // Get bone transforms - transforms.clear(); - model->GetBoneTransforms( &transforms ); - - if ( transforms.empty() ) { - continue; - } - - // Build instance data - SkeletalInstanceData inst = {}; - - // World matrix with scale - XMMATRIX scale = XMMatrixScalingFromVector( model->GetModelScaleXM() ); - XMMATRIX world = vi->Vob->GetWorldMatrixXM() * scale; - XMStoreFloat4x4( &inst.World, world ); - inst.PrevWorld = vi->HasValidPrevTransforms ? vi->PrevWorldMatrix : inst.World; - - // Model color - if ( Engine::GAPI->GetRendererState().RendererSettings.EnableShadows ) { - inst.Color = float4( 1, 1, 1, 1 ); + if ( vi->Vob->IsIndoorVob() ) { + inst.Color = DEFAULT_LIGHTMAP_POLY_COLOR; + } else if ( zCPolygon* polygon = vi->Vob->GetGroundPoly() ) { + float3 vobPos = vi->Vob->GetPositionWorld(); + float3 polyLightStat = polygon->GetLightStatAtPos( vobPos ); + inst.Color = float4( polyLightStat.z * inv255f, polyLightStat.y * inv255f, polyLightStat.x * inv255f, 1.f ); } else { - if ( vi->Vob->IsIndoorVob() ) { - inst.Color = DEFAULT_LIGHTMAP_POLY_COLOR; - } else if ( zCPolygon* polygon = vi->Vob->GetGroundPoly() ) { - float3 vobPos = vi->Vob->GetPositionWorld(); - float3 polyLightStat = polygon->GetLightStatAtPos( vobPos ); - inst.Color = float4( polyLightStat.z * inv255f, polyLightStat.y * inv255f, polyLightStat.x * inv255f, 1.f ); - } else { - inst.Color = float4( 1, 1, 1, 1 ); - } + inst.Color = float4( 1, 1, 1, 1 ); } + } - inst.Fatness = model->GetModelFatness(); - inst.Scale = 1.0f; - inst.BoneOffset = batch.TotalBoneCount; - inst.Padding = 0; - - batch.InstanceData.push_back( inst ); - - // Add bone transforms - batch.AllBoneTransforms.insert( batch.AllBoneTransforms.end(), - transforms.begin(), transforms.end() ); - batch.TotalBoneCount += static_cast(transforms.size()); - - model->SetIsVisible( true ); - - batch.VisualInfo = dynamic_cast(vi->VisualInfo); + inst.Fatness = model->GetModelFatness(); + inst.Scale = 1.0f; + inst.BoneOffset = boneIndex; + inst.Padding = 0; + + auto instIndex = allInstances.size(); + allInstances.push_back(inst); + + for (auto& [mat, meshes] : visualInfo->SkeletalMeshes) { + for (auto mesh : meshes) { + SkeletalMeshBatchEntry entry; + entry.material = mat->GetAniTexture(); + entry.mesh = mesh; + entry.instanceIndex = instIndex; + allEntries.push_back(entry); + } } } + + auto& batch = SkeletalMeshBatches[0]; + batch.Entries = std::move(allEntries); + batch.BoneTransforms = std::move(allBones); + batch.Instances = std::move(allInstances); } XRESULT D3D11GraphicsEngine::DrawSkeletalMeshBatched() { @@ -4806,32 +4765,6 @@ XRESULT D3D11GraphicsEngine::DrawSkeletalMeshBatched() { // Draw each batch (body meshes only) for ( auto& [name, batch] : SkeletalMeshBatches ) { - if ( batch.Vobs.empty() || !batch.VisualInfo ) { - continue; - } - - // Don't to the expensive upload if it's a single instance. - if ( batch.InstanceData.size() == 1 ) { - for ( SkeletalVobInfo* vi : batch.Vobs ) { - zCModel* model = static_cast(vi->Vob->GetVisual()); - if ( !model ) continue; - model->SetIsVisible( true ); - if ( !vi->Vob->GetShowVisual() ) - continue; - - // if we don't update the MeshLibTextAni the visual will have wrong textures - model->UpdateAttachedVobs(); - model->UpdateMeshLibTexAniState(); - - if ( !vi->VobConstantBuffer ) { - vi->UpdateVobConstantBuffer(); - } - - DrawSkeletalMesh( vi, batch.AllBoneTransforms, batch.InstanceData[0].Color, batch.InstanceData[0].World, batch.InstanceData[0].Fatness ); - } - continue; - } - // Draw batched body mesh DrawSkeletalMeshInstanced( batch ); } @@ -4841,10 +4774,10 @@ XRESULT D3D11GraphicsEngine::DrawSkeletalMeshBatched() { } XRESULT D3D11GraphicsEngine::DrawSkeletalMeshInstanced( SkeletalMeshBatch& batch ) { - if ( batch.InstanceData.empty() || !batch.VisualInfo ) return XR_SUCCESS; + if ( batch.Instances.empty() ) return XR_SUCCESS; - if ( SkeletalInstanceBuffer->GetMaxElementCount() < batch.InstanceData.size() ) { - auto requiredSize = Toolbox::NextPowerOfTwo( batch.InstanceData.size() ); + if ( SkeletalInstanceBuffer->GetMaxElementCount() < batch.Instances.size() ) { + auto requiredSize = Toolbox::NextPowerOfTwo( batch.Instances.size() ); SkeletalInstanceBuffer = std::make_unique>(); HRESULT hr = SkeletalInstanceBuffer->Init( GetDevice().Get(), requiredSize ); @@ -4855,11 +4788,11 @@ XRESULT D3D11GraphicsEngine::DrawSkeletalMeshInstanced( SkeletalMeshBatch& batch } // Update structured buffers - SkeletalInstanceBuffer->UpdateBuffer( Context.Get(), batch.InstanceData.data(), - static_cast(batch.InstanceData.size()) ); + SkeletalInstanceBuffer->UpdateBuffer( Context.Get(), batch.Instances.data(), + static_cast(batch.Instances.size()) ); - if ( SkeletalBoneBuffer->GetMaxElementCount() < batch.AllBoneTransforms.size() ) { - auto requiredSize = Toolbox::NextPowerOfTwo( batch.AllBoneTransforms.size() ); + if ( SkeletalBoneBuffer->GetMaxElementCount() < batch.BoneTransforms.size() ) { + auto requiredSize = Toolbox::NextPowerOfTwo( batch.BoneTransforms.size() ); SkeletalBoneBuffer = std::make_unique>(); HRESULT hr = SkeletalBoneBuffer->Init( GetDevice().Get(), requiredSize ); @@ -4868,12 +4801,13 @@ XRESULT D3D11GraphicsEngine::DrawSkeletalMeshInstanced( SkeletalMeshBatch& batch return XR_FAILED; } } - SkeletalBoneBuffer->UpdateBuffer( Context.Get(), batch.AllBoneTransforms.data(), - static_cast(batch.AllBoneTransforms.size()) ); + SkeletalBoneBuffer->UpdateBuffer( Context.Get(), batch.BoneTransforms.data(), + static_cast(batch.BoneTransforms.size()) ); // Bind structured buffers SkeletalInstanceBuffer->BindToVertexShader( Context.Get(), 10 ); // t10 SkeletalBoneBuffer->BindToVertexShader( Context.Get(), 11 ); // t11 + SkeletalInstanceIndexBuffer->BindToVertexShader( Context.Get(), 12 ); // t12 - instance index indirection // Set shader based on rendering stage if ( RenderingStage == DES_SHADOWMAP_CUBE ) { @@ -4912,201 +4846,468 @@ XRESULT D3D11GraphicsEngine::DrawSkeletalMeshInstanced( SkeletalMeshBatch& batch } } - // Draw each submesh - SkeletalMeshVisualInfo* meshInfo = nullptr; - - for ( auto vi : batch.Vobs ) { - zCModel* model = static_cast(vi->Vob->GetVisual()); - - if ( !model || !vi->VisualInfo ) - continue; // Gothic fortunately sets this to 0 when it throws the model out of the cache - - model->SetIsVisible( true ); - if ( !vi->Vob->GetShowVisual() ) - continue; - - // if we don't update the MeshLibTextAni the visual will have wrong textures - model->UpdateMeshLibTexAniState(); - - meshInfo = static_cast(vi->VisualInfo); - break; + bool needsTexture = (RenderingStage != DES_GHOST && RenderingStage != DES_SHADOWMAP); + // Sort by material first, then by mesh pointer for optimal batching + if ( needsTexture ) { + std::sort(batch.Entries.begin(), batch.Entries.end(), [](const SkeletalMeshBatchEntry& a, const SkeletalMeshBatchEntry& b) + { + if ( a.material != b.material ) return a.material < b.material; + return a.mesh->MeshVertexBuffer.get() < b.mesh->MeshVertexBuffer.get(); + }); + } else { + std::sort(batch.Entries.begin(), batch.Entries.end(), [](const SkeletalMeshBatchEntry& a, const SkeletalMeshBatchEntry& b) + { + return a.mesh->MeshVertexBuffer.get() < b.mesh->MeshVertexBuffer.get(); + }); } + + // Temporary buffer for collecting instance indices per draw call + std::vector drawInstanceIndices; + drawInstanceIndices.reserve( 64 ); - for ( auto const& itm : batch.VisualInfo->SkeletalMeshes ) { - if ( itm.first ) { + for ( size_t i = 0; i < batch.Entries.size(); ) { + auto& itm = batch.Entries[i]; + + // Bind texture for this material + if ( itm.material ) { zCTexture* tex; - if ( ActivePS && (tex = itm.first->GetAniTexture()) != nullptr ) { - if ( !BindTextureNRFX( tex, (RenderingStage != DES_GHOST) ) ) { + if ( ActivePS && (tex = itm.material) != nullptr ) { + if ( !BindTextureNRFX( tex, needsTexture ) ) { + i++; continue; } } + } else { + GetContext()->PSSetShader( nullptr, nullptr, 0 ); } - for ( auto& mesh : itm.second ) { - D3D11VertexBuffer* vb = mesh->MeshVertexBuffer; - D3D11VertexBuffer* ib = mesh->MeshIndexBuffer; - unsigned int numIndices = static_cast(mesh->Indices.size()); - - UINT offset = 0; - UINT uStride = sizeof( ExSkelVertexStruct ); - Context->IASetVertexBuffers( 0, 1, vb->GetVertexBuffer().GetAddressOf(), &uStride, &offset ); - Context->IASetIndexBuffer( ib->GetVertexBuffer().Get(), VERTEX_INDEX_DXGI_FORMAT, 0 ); + // Find all entries with same material AND same mesh (can be instanced together) + size_t batchStart = i; + size_t batchEnd = i + 1; + while ( batchEnd < batch.Entries.size() + && ( !needsTexture || batch.Entries[batchEnd].material == itm.material) + && batch.Entries[batchEnd].mesh->MeshVertexBuffer.get() == itm.mesh->MeshVertexBuffer.get() ) { + batchEnd++; + } + size_t instanceCount = batchEnd - batchStart; - // Draw instanced - Context->DrawIndexedInstanced( numIndices, static_cast(batch.InstanceData.size()), 0, 0, 0 ); + // Collect instance indices for this draw call + drawInstanceIndices.clear(); + for ( size_t j = batchStart; j < batchEnd; j++ ) { + drawInstanceIndices.push_back( batch.Entries[j].instanceIndex ); + } - Engine::GAPI->GetRendererState().RendererInfo.FrameDrawnTriangles += - (numIndices / 3) * static_cast(batch.InstanceData.size()); + // Update instance index indirection buffer + if ( SkeletalInstanceIndexBuffer->GetMaxElementCount() < drawInstanceIndices.size() ) { + auto requiredSize = Toolbox::NextPowerOfTwo( drawInstanceIndices.size() ); + SkeletalInstanceIndexBuffer = std::make_unique>(); + SkeletalInstanceIndexBuffer->Init( GetDevice().Get(), requiredSize ); + SkeletalInstanceIndexBuffer->BindToVertexShader( Context.Get(), 12 ); } + SkeletalInstanceIndexBuffer->UpdateBuffer( Context.Get(), drawInstanceIndices.data(), + static_cast(drawInstanceIndices.size()) ); + + // Set up vertex/index buffers for this mesh + auto vb = itm.mesh->MeshVertexBuffer; + auto ib = itm.mesh->MeshIndexBuffer; + unsigned int numIndices = static_cast( itm.mesh->Indices.size() ); + + UINT offset = 0; + UINT uStride = sizeof( ExSkelVertexStruct ); + Context->IASetVertexBuffers( 0, 1, vb->GetVertexBuffer().GetAddressOf(), &uStride, &offset ); + Context->IASetIndexBuffer( ib->GetVertexBuffer().Get(), VERTEX_INDEX_DXGI_FORMAT, 0 ); + + // Draw instanced - SV_InstanceID will index into InstanceIndexBuffer to get actual instance index + Context->DrawIndexedInstanced( numIndices, static_cast(instanceCount), 0, 0, 0 ); + + Engine::GAPI->GetRendererState().RendererInfo.FrameDrawnTriangles += + (numIndices / 3) * static_cast(instanceCount); + + i = batchEnd; } // Unbind structured buffers SkeletalInstanceBuffer->UnbindFromVertexShader( Context.Get(), 10 ); SkeletalBoneBuffer->UnbindFromVertexShader( Context.Get(), 11 ); - - Engine::GAPI->GetRendererState().RendererInfo.FrameDrawnVobs += - static_cast(batch.InstanceData.size()); + SkeletalInstanceIndexBuffer->UnbindFromVertexShader( Context.Get(), 12 ); return XR_SUCCESS; } void D3D11GraphicsEngine::ClearSkeletalMeshBatches() { for ( auto& [name, batch] : SkeletalMeshBatches ) { - batch.Clear(); + batch.Entries.clear(); + batch.BoneTransforms.clear(); + batch.Instances.clear(); } SkeletalMeshBatches.clear(); } +void D3D11GraphicsEngine::BuildNodeAttachmentBatches( const std::vector& vobs, bool updateState ) { + // Clear previous batches + ClearNodeAttachmentBatches(); -XRESULT D3D11GraphicsEngine::InitNodeAttachmentInstancingBuffer() { - // Instance buffer for node attachments - NodeAttachmentInstanceBuffer = std::make_unique>(); - HRESULT hr = NodeAttachmentInstanceBuffer->Init( GetDevice().Get(), 64 ); - if ( FAILED( hr ) ) { - LogError() << "Failed to create node attachment instance buffer"; - return XR_FAILED; + XMVECTOR playerPosXm = oCGame::GetPlayer() ? oCGame::GetPlayer()->GetPositionWorldXM() : g_XMZero; + auto isShadowPass = RenderingStage == DES_SHADOWMAP || RenderingStage == DES_SHADOWMAP_CUBE; + + std::vector allInstances; + std::vector allEntries; + + static std::vector transforms; + + for ( const auto& vi : vobs ) { + zCModel* model = static_cast(vi->Vob->GetVisual()); + if ( !model || !vi->VisualInfo ) continue; + + if ( !vi->Vob->GetShowVisual() ) continue; + + float dist; + XMStoreFloat( &dist, XMVector3Length( vi->Vob->GetPositionWorldXM() - playerPosXm ) ); + + // Calculate model color + float4 modelColor; + if ( Engine::GAPI->GetRendererState().RendererSettings.EnableShadows ) { + modelColor = float4( 1, 1, 1, 1 ); + } else { + if ( vi->Vob->IsIndoorVob() ) { + modelColor = DEFAULT_LIGHTMAP_POLY_COLOR; + } else if ( zCPolygon* polygon = vi->Vob->GetGroundPoly() ) { + static const float inv255f = 1.0f / 255.0f; + float3 vobPos = vi->Vob->GetPositionWorld(); + float3 polyLightStat = polygon->GetLightStatAtPos( vobPos ); + modelColor = float4( polyLightStat.z * inv255f, polyLightStat.y * inv255f, polyLightStat.x * inv255f, 1.f ); + } else { + modelColor = float4( 1, 1, 1, 1 ); + } + } + + XMMATRIX scale = XMMatrixScalingFromVector( model->GetModelScaleXM() ); + XMMATRIX world = vi->Vob->GetWorldMatrixXM() * scale; + + float fatness = model->GetModelFatness(); + + // Get bone transforms + transforms.clear(); + model->GetBoneTransforms( &transforms ); + + const std::vector& prevBoneTransforms = (vi->HasValidPrevTransforms && !vi->PrevBoneTransforms.empty()) + ? vi->PrevBoneTransforms + : transforms; + const XMMATRIX prevWorldMatrix = vi->HasValidPrevTransforms + ? XMLoadFloat4x4( &vi->PrevWorldMatrix ) + : world; + + // Update attachments + if ( updateState ) { + model->UpdateAttachedVobs(); + model->UpdateMeshLibTexAniState(); + } + + std::map>& nodeAttachments = vi->NodeAttachments; + zCModel* mvis = static_cast(vi->Vob->GetVisual()); + + for ( unsigned int i = 0; i < transforms.size(); i++ ) { + zCModelNodeInst* node = mvis->GetNodeList()->Array[i]; + + if ( !node->NodeVisual ) continue; + + if ( updateState ) { + // Load attachment if not yet loaded + if ( nodeAttachments.find( i ) == nodeAttachments.end() ) { + WorldConverter::ExtractNodeVisual( i, node, nodeAttachments ); + } + + // Check for changed visual + if ( !nodeAttachments[i].empty() && node->NodeVisual != nodeAttachments[i][0]->Visual ) { + if ( !node->NodeVisual ) { + delete nodeAttachments[i][0]; + nodeAttachments[i].clear(); + continue; + } + WorldConverter::ExtractNodeVisual( i, node, nodeAttachments ); + } + } + + // Skip non-hand visuals if model requests it + if ( model->GetDrawHandVisualsOnly() ) { + std::string NodeName = node->ProtoNode->NodeName.ToChar(); +#ifdef BUILD_GOTHIC_2_6_fix + if ( NodeName.find( "HAND" ) == std::string::npos && + (*reinterpret_cast(0x57A694) != 0x90 || NodeName.find( "ARM" ) == std::string::npos) ) { +#else + if ( NodeName.find( "HAND" ) == std::string::npos ) { +#endif + continue; + } + } + + if ( nodeAttachments.find( i ) == nodeAttachments.end() ) continue; + + XMMATRIX curTransform = XMLoadFloat4x4( &transforms[i] ); + XMMATRIX finalWorld = world * curTransform; + + XMMATRIX prevTransform = XMLoadFloat4x4( &prevBoneTransforms[i] ); + auto prevWorldNode = prevWorldMatrix * prevTransform; + + // Process each attachment visual + for ( MeshVisualInfo* mvi : nodeAttachments[i] ) { + if ( !mvi || !mvi->Visual ) continue; + + // Update animated textures BEFORE getting the texture pointer + bool isMMS = strcmp( mvi->Visual->GetFileExtension( 0 ), ".MMS" ) == 0; + if ( updateState ) { + node->TexAniState.UpdateTexList(); + if ( isMMS ) { + zCMorphMesh* mm = reinterpret_cast(mvi->Visual); + mm->GetTexAniState()->UpdateTexList(); + } + } + + // Skip MorphMeshes at close distance (they need per-vertex animation, can't batch) + if ( dist < 1000 && isMMS && (RenderingStage == DES_MAIN || RenderingStage == DES_GHOST) ) { + // These are handled separately via DrawMorphMesh in GothicAPI + continue; + } + + // Build instance data + NodeAttachmentInstanceData instData = {}; + XMStoreFloat4x4( &instData.World, finalWorld ); + XMStoreFloat4x4( &instData.PrevWorld, prevWorldNode ); + instData.Color = modelColor; + + if ( isMMS ) { + instData.Fatness = std::max( 0.f, fatness * 0.35f ); + instData.Scaling = fatness * 0.02f + 1.f; + } else { + instData.Fatness = 0.f; + instData.Scaling = 1.f; + } + + auto instIndex = allInstances.size(); + allInstances.push_back( instData ); + + // Collect entries by (texture, mesh) for batching + for ( auto const& itm : mvi->Meshes ) { + zCMaterial* mat = itm.first; + if ( !mat ) continue; + + zCTexture* tex = mat->GetAniTexture(); + + for ( MeshInfo* mesh : itm.second ) { + if ( !mesh ) continue; + + NodeAttachmentBatchEntry entry; + entry.texture = tex; + entry.mesh = mesh; + entry.instanceIndex = static_cast(instIndex); + allEntries.push_back( entry ); + } + } + } + } + } + + // Store in the batch + auto& batch = NodeAttachmentBatches[0]; + batch.Entries = std::move( allEntries ); + batch.Instances = std::move( allInstances ); +} + +XRESULT D3D11GraphicsEngine::DrawNodeAttachmentBatched() { + if ( NodeAttachmentBatches.empty() ) return XR_SUCCESS; + + // Fix the view matrix + Engine::GAPI->GetViewMatrix( &Engine::GAPI->GetRendererState().TransformState.TransformView ); + + // Draw each batch + for ( auto& [name, batch] : NodeAttachmentBatches ) { + DrawNodeAttachmentInstanced( batch ); } - LogInfo() << "Initialized node attachment instancing buffer"; return XR_SUCCESS; } -void D3D11GraphicsEngine::DrawNodeAttachmentsBatched( - std::unordered_map& batches ) -{ - if ( batches.empty() ) return; +XRESULT D3D11GraphicsEngine::DrawNodeAttachmentInstanced( NodeAttachmentBatch& batch ) { + if ( batch.Instances.empty() ) return XR_SUCCESS; + + // Resize instance buffer if needed + if ( NodeAttachmentInstanceBuffer->GetMaxElementCount() < batch.Instances.size() ) { + auto requiredSize = Toolbox::NextPowerOfTwo( batch.Instances.size() ); + NodeAttachmentInstanceBuffer = std::make_unique>(); + HRESULT hr = NodeAttachmentInstanceBuffer->Init( GetDevice().Get(), requiredSize ); + if ( FAILED( hr ) ) { + LogError() << "Failed to create node attachment instance buffer"; + return XR_FAILED; + } + } - // Set up shaders + // Update instance buffer + NodeAttachmentInstanceBuffer->UpdateBuffer( Context.Get(), batch.Instances.data(), + static_cast(batch.Instances.size()) ); + + // Bind structured buffers + NodeAttachmentInstanceBuffer->BindToVertexShader( Context.Get(), 10 ); // t10 + NodeAttachmentInstanceIndexBuffer->BindToVertexShader( Context.Get(), 12 ); // t12 - instance index indirection + + // Set shader based on rendering stage if ( RenderingStage == DES_SHADOWMAP_CUBE ) { SetActiveVertexShader( "VS_ExNodeInstancedCube" ); } else { SetActiveVertexShader( "VS_ExNodeInstanced" ); } + InfiniteRangeConstantBuffer->BindToPixelShader( 3 ); SetupVS_ExMeshDrawCall(); SetupVS_ExConstantBuffer(); - // Bind instance buffer to slot 10 - NodeAttachmentInstanceBuffer->BindToVertexShader( Context.Get(), 10 ); + Context->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST ); ActiveVS->Apply(); // Setup pixel shader - if ( RenderingStage == DES_MAIN ) { - SetActivePixelShader( "PS_DiffuseAlphaTest" ); - BindActivePixelShader(); - } else if ( RenderingStage == DES_SHADOWMAP ) { - Context->PSSetShader( nullptr, nullptr, 0 ); - ActivePS = nullptr; + if ( RenderingStage != DES_GHOST ) { + bool linearDepth = (Engine::GAPI->GetRendererState().GraphicsState.FF_GSwitches & GSWITCH_LINEAR_DEPTH) != 0; + if ( linearDepth ) { + ActivePS = PS_LinDepth; + ActivePS->Apply(); + } else if ( RenderingStage == DES_SHADOWMAP ) { + Context->PSSetShader( nullptr, nullptr, 0 ); + ActivePS = nullptr; + } else { + SetActivePixelShader( "PS_DiffuseAlphaTest" ); + BindActivePixelShader(); + } } - Context->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST ); - - // Draw each batch - for ( auto& [key, batch] : batches ) - { - if ( batch.Instances.empty() || !batch.Mesh.size() ) continue; + if ( RenderingStage == DES_MAIN ) { + if ( ActiveHDS ) { + Context->DSSetShader( nullptr, nullptr, 0 ); + Context->HSSetShader( nullptr, nullptr, 0 ); + ActiveHDS = nullptr; + } + } - zCModel* model = static_cast(batch.vobInfo->Vob->GetVisual()); + // Sort by texture first, then by mesh pointer for optimal batching + bool needsTexture = (RenderingStage != DES_GHOST && RenderingStage != DES_SHADOWMAP); + // Sort by material first, then by mesh pointer for optimal batching + if ( needsTexture ) { + std::sort( batch.Entries.begin(), batch.Entries.end(), []( const NodeAttachmentBatchEntry& a, const NodeAttachmentBatchEntry& b ) { + if ( a.texture != b.texture ) return a.texture < b.texture; + return a.mesh->MeshIndexBuffer.get() < b.mesh->MeshIndexBuffer.get(); + } ); + } else { + std::sort( batch.Entries.begin(), batch.Entries.end(), []( const NodeAttachmentBatchEntry& a, const NodeAttachmentBatchEntry& b ) { + if ( a.texture && a.texture->HasAlphaChannel() && a.texture != b.texture ) + return a.texture < b.texture; + return a.mesh->MeshIndexBuffer.get() < b.mesh->MeshIndexBuffer.get(); + } ); + } - model->UpdateAttachedVobs(); - model->UpdateMeshLibTexAniState(); - - // Update instance buffer - UINT instanceCount = batch.Instances.size(); + // Temporary buffer for collecting instance indices per draw call + std::vector drawInstanceIndices; + drawInstanceIndices.reserve( 64 ); - if ( NodeAttachmentInstanceBuffer->GetMaxElementCount() < instanceCount ) { - auto requiredSize = Toolbox::NextPowerOfTwo( instanceCount ); + for ( size_t i = 0; i < batch.Entries.size(); ) { + auto& itm = batch.Entries[i]; - NodeAttachmentInstanceBuffer = std::make_unique>(); - HRESULT hr = NodeAttachmentInstanceBuffer->Init( GetDevice().Get(), requiredSize ); - if ( FAILED( hr ) ) { - LogError() << "Failed to create node attachment instance buffer"; - return; + // Bind texture for this batch + if ( itm.texture ) { + if ( ActivePS ) { + if ( !BindTextureNRFX( itm.texture, needsTexture ) ) { + i++; + continue; + } } + } else { + GetContext()->PSSetShader( nullptr, nullptr, 0 ); } - NodeAttachmentInstanceBuffer->UpdateBuffer( Context.Get(), batch.Instances.data(), instanceCount ); + // Find all entries with same texture AND same mesh (can be instanced together) + size_t batchStart = i; + size_t batchEnd = i + 1; + while ( batchEnd < batch.Entries.size() + && batch.Entries[batchEnd].texture == itm.texture + && batch.Entries[batchEnd].mesh->MeshIndexBuffer.get() == itm.mesh->MeshIndexBuffer.get() ) { + batchEnd++; + } + size_t instanceCount = batchEnd - batchStart; - // WICHTIG: Hole die Textur JETZT aus dem Material, nicht aus dem gecachten Key! - if ( RenderingStage != DES_SHADOWMAP && RenderingStage != DES_SHADOWMAP_CUBE ) { - - // Only update textures if not drawing shadows - const bool isMMS = strcmp( batch.MeshVisualInfo->Visual->GetFileExtension( 0 ), ".MMS" ) == 0; - batch.node->TexAniState.UpdateTexList(); - if ( isMMS ) { - // Important to also update the textures here - zCMorphMesh* mm = reinterpret_cast(batch.MeshVisualInfo->Visual); - mm->GetTexAniState()->UpdateTexList(); - } + // Collect instance indices for this draw call + drawInstanceIndices.clear(); + for ( size_t j = batchStart; j < batchEnd; j++ ) { + drawInstanceIndices.push_back( batch.Entries[j].instanceIndex ); + } - if ( batch.Material ) { - zCTexture* texture = batch.Material->GetAniTexture(); - if ( texture ) { - if ( texture->CacheIn( 0.6f ) != zRES_CACHED_IN ) { - continue; // Skip if texture not loaded - } - if ( !BindTextureNRFX( texture, (RenderingStage == DES_MAIN) ) ) { - continue; - } - } else { - continue; // Skip if no texture - } - } else { - continue; - } + // Update instance index indirection buffer + if ( NodeAttachmentInstanceIndexBuffer->GetMaxElementCount() < drawInstanceIndices.size() ) { + auto requiredSize = Toolbox::NextPowerOfTwo( drawInstanceIndices.size() ); + NodeAttachmentInstanceIndexBuffer = std::make_unique>(); + NodeAttachmentInstanceIndexBuffer->Init( GetDevice().Get(), requiredSize ); + NodeAttachmentInstanceIndexBuffer->BindToVertexShader( Context.Get(), 12 ); } + NodeAttachmentInstanceIndexBuffer->UpdateBuffer( Context.Get(), drawInstanceIndices.data(), + static_cast(drawInstanceIndices.size()) ); - // Set up alpha test from material - if ( batch.Material && batch.Material->GetAlphaFunc() == zRND_ALPHA_FUNC_TEST ) { - Engine::GAPI->GetRendererState().GraphicsState.FF_GSwitches |= GSWITCH_ALPHAREF; - } else { - Engine::GAPI->GetRendererState().GraphicsState.FF_GSwitches &= ~GSWITCH_ALPHAREF; + // Set up vertex/index buffers for this mesh + MeshInfo* mesh = itm.mesh; + if ( !mesh || !mesh->MeshVertexBuffer ) { + i = batchEnd; + continue; } - // Bind mesh buffers - for ( auto& mesh : batch.Mesh ) - { - if ( !mesh->MeshVertexBuffer ) continue; + UINT offset = 0; + UINT stride = sizeof( ExVertexStruct ); + Context->IASetVertexBuffers( 0, 1, mesh->MeshVertexBuffer->GetVertexBuffer().GetAddressOf(), &stride, &offset ); - UINT offset = 0; - UINT stride = sizeof( ExVertexStruct ); - Context->IASetVertexBuffers( 0, 1, mesh->MeshVertexBuffer->GetVertexBuffer().GetAddressOf(), &stride, &offset ); - - if ( mesh->MeshIndexBuffer ) { - Context->IASetIndexBuffer( mesh->MeshIndexBuffer->GetVertexBuffer().Get(), VERTEX_INDEX_DXGI_FORMAT, 0 ); - Context->DrawIndexedInstanced( static_cast(mesh->Indices.size()), instanceCount, 0, 0, 0 ); - Engine::GAPI->GetRendererState().RendererInfo.FrameDrawnTriangles += - static_cast(mesh->Indices.size() / 3) * instanceCount; - } else - { - Context->DrawInstanced( static_cast(mesh->Vertices.size()), instanceCount, 0, 0 ); - Engine::GAPI->GetRendererState().RendererInfo.FrameDrawnTriangles += - static_cast(mesh->Vertices.size() / 3) * instanceCount; - } + if ( mesh->MeshIndexBuffer ) { + Context->IASetIndexBuffer( mesh->MeshIndexBuffer->GetVertexBuffer().Get(), VERTEX_INDEX_DXGI_FORMAT, 0 ); + Context->DrawIndexedInstanced( static_cast(mesh->Indices.size()), static_cast(instanceCount), 0, 0, 0 ); + Engine::GAPI->GetRendererState().RendererInfo.FrameDrawnTriangles += + static_cast(mesh->Indices.size() / 3) * static_cast(instanceCount); + } else { + Context->DrawInstanced( static_cast(mesh->Vertices.size()), static_cast(instanceCount), 0, 0 ); + Engine::GAPI->GetRendererState().RendererInfo.FrameDrawnTriangles += + static_cast(mesh->Vertices.size() / 3) * static_cast(instanceCount); } + + i = batchEnd; } + + // Unbind structured buffers NodeAttachmentInstanceBuffer->UnbindFromVertexShader( Context.Get(), 10 ); + NodeAttachmentInstanceIndexBuffer->UnbindFromVertexShader( Context.Get(), 12 ); + + return XR_SUCCESS; +} + +void D3D11GraphicsEngine::ClearNodeAttachmentBatches() { + for ( auto& [name, batch] : NodeAttachmentBatches ) { + batch.Entries.clear(); + batch.Instances.clear(); + } + NodeAttachmentBatches.clear(); +} + + +XRESULT D3D11GraphicsEngine::InitNodeAttachmentInstancingBuffer() { + // Instance buffer for node attachments + NodeAttachmentInstanceBuffer = std::make_unique>(); + HRESULT hr = NodeAttachmentInstanceBuffer->Init( GetDevice().Get(), 128 ); + if ( FAILED( hr ) ) { + LogError() << "Failed to create node attachment instance buffer"; + return XR_FAILED; + } + + // Instance index indirection buffer for node attachments + NodeAttachmentInstanceIndexBuffer = std::make_unique>(); + hr = NodeAttachmentInstanceIndexBuffer->Init( GetDevice().Get(), 128 ); + if ( FAILED( hr ) ) { + LogError() << "Failed to create node attachment instance index buffer"; + return XR_FAILED; + } + + LogInfo() << "Initialized node attachment instancing buffers"; + return XR_SUCCESS; } void D3D11GraphicsEngine::ShadowPass_DrawWorldMesh_Indirect(const std::vector& visibleSections) @@ -5395,8 +5596,8 @@ void XM_CALLCONV D3D11GraphicsEngine::DrawWorldAroundForWorldShadow( FXMVECTOR p const auto sectionRangeSq = sectionRange * sectionRange; // Bind wrapped mesh vertex buffers DrawVertexBufferIndexedUINT( - Engine::GAPI->GetWrappedWorldMesh()->MeshVertexBuffer, - Engine::GAPI->GetWrappedWorldMesh()->MeshIndexBuffer, 0, 0 ); + Engine::GAPI->GetWrappedWorldMesh()->MeshVertexBuffer.get(), + Engine::GAPI->GetWrappedWorldMesh()->MeshIndexBuffer.get(), 0, 0 ); ActiveVS->GetConstantBuffer()[1]->UpdateBuffer( &XMMatrixIdentity() ); ActiveVS->GetConstantBuffer()[1]->BindToVertexShader( 1 ); @@ -5589,7 +5790,7 @@ void XM_CALLCONV D3D11GraphicsEngine::DrawWorldAroundForWorldShadow( FXMVECTOR p MeshInfo* mi = mlist[i]; // Draw batch - DrawInstanced( mi->MeshVertexBuffer, mi->MeshIndexBuffer, + DrawInstanced( mi->MeshVertexBuffer.get(), mi->MeshIndexBuffer.get(), mi->Indices.size(), DynamicInstancingBuffer.get(), sizeof( VobInstanceInfo ), staticMeshVisual->Instances.size(), sizeof( ExVertexStruct ), staticMeshVisual->StartInstanceNum ); @@ -5998,7 +6199,7 @@ XRESULT D3D11GraphicsEngine::DrawVOBsInstanced() { } // Draw batch - DrawInstanced( mi->MeshVertexBuffer, mi->MeshIndexBuffer, + DrawInstanced( mi->MeshVertexBuffer.get(), mi->MeshIndexBuffer.get(), mi->Indices.size(), DynamicInstancingBuffer.get(), sizeof( VobInstanceInfo ), staticMeshVisual->Instances.size(), sizeof( ExVertexStruct ), staticMeshVisual->StartInstanceNum ); @@ -6139,7 +6340,7 @@ XRESULT D3D11GraphicsEngine::DrawFrameAlphaMeshes() } // Draw batch - DrawInstanced( mi->MeshVertexBuffer, mi->MeshIndexBuffer, mi->Indices.size(), + DrawInstanced( mi->MeshVertexBuffer.get(), mi->MeshIndexBuffer.get(), mi->Indices.size(), DynamicInstancingBuffer.get(), sizeof( VobInstanceInfo ), instances.size(), sizeof( ExVertexStruct ), vi->StartInstanceNum ); @@ -6156,7 +6357,7 @@ XRESULT D3D11GraphicsEngine::DrawFrameAlphaMeshes() } // Draw batch - DrawInstanced( mi->MeshVertexBuffer, mi->MeshIndexBuffer, mi->Indices.size(), + DrawInstanced( mi->MeshVertexBuffer.get(), mi->MeshIndexBuffer.get(), mi->Indices.size(), DynamicInstancingBuffer.get(), sizeof( VobInstanceInfo ), instances.size(), sizeof( ExVertexStruct ), vi->StartInstanceNum ); @@ -6552,7 +6753,7 @@ void D3D11GraphicsEngine::DrawVobSingle( VobInfo* vob, zCCamera& camera ) { for ( auto const& itm2nd : itm.second ) { // Draw instances DrawVertexBufferIndexed( - itm2nd->MeshVertexBuffer, itm2nd->MeshIndexBuffer, + itm2nd->MeshVertexBuffer.get(), itm2nd->MeshIndexBuffer.get(), itm2nd->Indices.size() ); } } @@ -7208,7 +7409,7 @@ void D3D11GraphicsEngine::DrawFrameParticleMeshes( std::unordered_mapMeshVertexBuffer, itm2nd->MeshIndexBuffer, + itm2nd->MeshVertexBuffer.get(), itm2nd->MeshIndexBuffer.get(), itm2nd->Indices.size() ); } } diff --git a/D3D11Engine/D3D11GraphicsEngine.h b/D3D11Engine/D3D11GraphicsEngine.h index 27a03330..bcc89b5d 100644 --- a/D3D11Engine/D3D11GraphicsEngine.h +++ b/D3D11Engine/D3D11GraphicsEngine.h @@ -373,9 +373,11 @@ class D3D11GraphicsEngine : public D3D11GraphicsEngineBase { // Initialize node attachment instancing buffer XRESULT InitNodeAttachmentInstancingBuffer(); - // Draw batched node attachments - void DrawNodeAttachmentsBatched( - std::unordered_map& batches ); + // New methods for node attachment batching (moved from GothicAPI) + void BuildNodeAttachmentBatches( const std::vector& vobs, bool updateState ); + XRESULT DrawNodeAttachmentBatched(); + XRESULT DrawNodeAttachmentInstanced( NodeAttachmentBatch& batch ); + void ClearNodeAttachmentBatches(); protected: @@ -494,8 +496,15 @@ class D3D11GraphicsEngine : public D3D11GraphicsEngineBase { // Instanced skeletal mesh rendering buffers std::unique_ptr> SkeletalInstanceBuffer; std::unique_ptr> SkeletalBoneBuffer; + std::unique_ptr> SkeletalInstanceIndexBuffer; // Indirection buffer for instance lookup // Batched skeletal meshes for current frame std::unordered_map SkeletalMeshBatches; + + // Node attachment instancing buffers std::unique_ptr> NodeAttachmentInstanceBuffer; + std::unique_ptr> NodeAttachmentInstanceIndexBuffer; // Indirection buffer for instance lookup + + // Batched node attachments for current frame + std::unordered_map NodeAttachmentBatches; }; diff --git a/D3D11Engine/D3D11OcclusionQuerry.cpp b/D3D11Engine/D3D11OcclusionQuerry.cpp index bc1d35af..a084b3d7 100644 --- a/D3D11Engine/D3D11OcclusionQuerry.cpp +++ b/D3D11Engine/D3D11OcclusionQuerry.cpp @@ -124,7 +124,7 @@ void D3D11OcclusionQuerry::DoOcclusionForBSP( BspInfo* root ) { MeshInfo* mi = root->OcclusionInfo.NodeMesh; g->GetContext()->Begin( p ); - g->DrawVertexBufferIndexed( mi->MeshVertexBuffer, mi->MeshIndexBuffer, mi->Indices.size() ); + g->DrawVertexBufferIndexed( mi->MeshVertexBuffer.get(), mi->MeshIndexBuffer.get(), mi->Indices.size()); g->GetContext()->End( p ); root->OcclusionInfo.QueryInProgress = true; diff --git a/D3D11Engine/GothicAPI.cpp b/D3D11Engine/GothicAPI.cpp index f95c4a3c..70bc75c2 100644 --- a/D3D11Engine/GothicAPI.cpp +++ b/D3D11Engine/GothicAPI.cpp @@ -1312,30 +1312,27 @@ void GothicAPI::DrawSkeletalMeshVobs_Batched( return; } - g->SetupVS_ExMeshDrawCall(); - g->SetupVS_ExConstantBuffer(); - - // Collect node attachments for batched rendering - // Key: (Texture*, Visual*) -> ensures same texture AND same mesh geometry - static std::unordered_map NodeAttachmentBatches; - NodeAttachmentBatches.clear(); + // Build and draw node attachment batches (moved to D3D11GraphicsEngine) + g->BuildNodeAttachmentBatches( vobs, updateState ); + g->DrawNodeAttachmentBatched(); + // Handle MorphMeshes at close distance separately (per-vertex animation, can't batch) XMVECTOR playerPosXm = oCGame::GetPlayer() ? oCGame::GetPlayer()->GetPositionWorldXM() : g_XMZero; - - D3D11GraphicsEngine* engine = (D3D11GraphicsEngine*)Engine::GraphicsEngine; - auto isShadowPass = engine->GetRenderingStage() == DES_SHADOWMAP || engine->GetRenderingStage() == DES_SHADOWMAP_CUBE; - static std::vector transforms; + for ( const auto& vi : vobs ) { zCModel* model = static_cast(vi->Vob->GetVisual()); if ( !model || !vi->VisualInfo ) continue; - - //model->SetIsVisible( true ); if ( !vi->Vob->GetShowVisual() ) continue; float dist; XMStoreFloat( &dist, XMVector3Length( vi->Vob->GetPositionWorldXM() - playerPosXm ) ); + // Only process close MorphMeshes in main render pass + if ( dist >= 1000 || (g->GetRenderingStage() != DES_MAIN && g->GetRenderingStage() != DES_GHOST) ) { + continue; + } + // Calculate model color float4 modelColor; if ( RendererState.RendererSettings.EnableShadows ) { @@ -1355,185 +1352,72 @@ void GothicAPI::DrawSkeletalMeshVobs_Batched( XMMATRIX scale = XMMatrixScalingFromVector( model->GetModelScaleXM() ); XMMATRIX world = vi->Vob->GetWorldMatrixXM() * scale; - float fatness = model->GetModelFatness(); - // Get bone transforms transforms.clear(); model->GetBoneTransforms( &transforms ); const std::vector& prevBoneTransforms = (vi->HasValidPrevTransforms && !vi->PrevBoneTransforms.empty()) ? vi->PrevBoneTransforms : transforms; - const XMMATRIX prevWorldMatrix = vi->HasValidPrevTransforms - ? XMLoadFloat4x4(&vi->PrevWorldMatrix) + const XMMATRIX prevWorldMatrix = vi->HasValidPrevTransforms + ? XMLoadFloat4x4( &vi->PrevWorldMatrix ) : world; - - // Update attachments - if ( updateState ) { - model->UpdateAttachedVobs(); - } std::map>& nodeAttachments = vi->NodeAttachments; - zCModel* mvis = static_cast( vi->Vob->GetVisual() ); + zCModel* mvis = static_cast(vi->Vob->GetVisual()); for ( unsigned int i = 0; i < transforms.size(); i++ ) { zCModelNodeInst* node = mvis->GetNodeList()->Array[i]; - if ( !node->NodeVisual ) continue; - - if ( updateState ) { - // Load attachment if not yet loaded - if ( nodeAttachments.find( i ) == nodeAttachments.end() ) { - WorldConverter::ExtractNodeVisual( i, node, nodeAttachments ); - } - - // Check for changed visual - if ( !nodeAttachments[i].empty() && node->NodeVisual != nodeAttachments[i][0]->Visual ) { - if ( !node->NodeVisual ) { - delete nodeAttachments[i][0]; - nodeAttachments[i].clear(); - continue; - } - WorldConverter::ExtractNodeVisual( i, node, nodeAttachments ); - } - } - - // Skip non-hand visuals if model requests it - if ( model->GetDrawHandVisualsOnly() ) { - std::string NodeName = node->ProtoNode->NodeName.ToChar(); -#ifdef BUILD_GOTHIC_2_6_fix - if ( NodeName.find( "HAND" ) == std::string::npos && - (*reinterpret_cast(0x57A694) != 0x90 || NodeName.find( "ARM" ) == std::string::npos) ) { -#else - if ( NodeName.find( "HAND" ) == std::string::npos ) { -#endif - continue; - } - } - if ( nodeAttachments.find( i ) == nodeAttachments.end() ) continue; - + XMMATRIX curTransform = XMLoadFloat4x4( &transforms[i] ); XMMATRIX finalWorld = world * curTransform; - XMMATRIX prevTransform = XMLoadFloat4x4( &prevBoneTransforms[i] ); - auto prevWorldNode = prevWorldMatrix * prevTransform; + auto prevWorldNode = prevWorldMatrix * prevTransform; - // Process each attachment visual for ( MeshVisualInfo* mvi : nodeAttachments[i] ) { if ( !mvi || !mvi->Visual ) continue; - // Update animated textures BEFORE getting the texture pointer bool isMMS = strcmp( mvi->Visual->GetFileExtension( 0 ), ".MMS" ) == 0; - if ( updateState ) { - node->TexAniState.UpdateTexList(); - if ( isMMS ) { - zCMorphMesh* mm = reinterpret_cast(mvi->Visual); - mm->GetTexAniState()->UpdateTexList(); - } - } - - // Handle MorphMeshes separately (can't be batched due to vertex animation) - if ( dist < 1000 && isMMS ) { - if ( g->GetRenderingStage() == DES_MAIN || g->GetRenderingStage() == DES_GHOST ) { - zCMorphMesh* mm = reinterpret_cast( mvi->Visual ); - - if ( updateState ) { - mm->GetTexAniState()->UpdateTexList(); - } - SetWorldViewTransform( finalWorld, GetViewMatrixXM() ); - - g->SetActiveVertexShader( "VS_ExNode" ); - g->SetupVS_ExMeshDrawCall(); - g->SetupVS_ExConstantBuffer(); - - VS_ExConstantBuffer_PerInstanceNode instanceInfo; - instanceInfo.Color = modelColor; - instanceInfo.Fatness = std::max( 0.f, fatness * 0.35f ); - instanceInfo.Scaling = fatness * 0.02f + 1.f; - XMStoreFloat4x4(&instanceInfo.World, finalWorld); - XMStoreFloat4x4(&instanceInfo.PrevWorld, prevWorldNode); - - g->GetActiveVS()->GetConstantBuffer()[1]->UpdateBuffer( &instanceInfo ); - g->GetActiveVS()->GetConstantBuffer()[1]->BindToVertexShader( 1 ); - - if ( updateState ) { - mm->AdvanceAnis(); - mm->CalcVertexPositions(); - } - DrawMorphMesh( mm, mvi->Meshes ); - continue; - } - } + if ( !isMMS ) continue; - // Build instance data - NodeAttachmentInstanceData instData = {}; - XMStoreFloat4x4( &instData.World, finalWorld ); - XMStoreFloat4x4( &instData.PrevWorld, prevWorldNode ); - instData.Color = modelColor; + zCMorphMesh* mm = reinterpret_cast(mvi->Visual); - if ( isMMS ) { - instData.Fatness = std::max( 0.f, fatness * 0.35f ); - instData.Scaling = fatness * 0.02f + 1.f; - } else { - instData.Fatness = 0.f; - instData.Scaling = 1.f; + if ( updateState ) { + node->TexAniState.UpdateTexList(); + mm->GetTexAniState()->UpdateTexList(); } - NodeAttachmentBatchKey baseNodeMeshKey = 0; + SetWorldViewTransform( finalWorld, GetViewMatrixXM() ); - if ( auto mm = mvi->Visual->As(); mm ) { - auto progMeshPtr = mm->GetMorphMesh(); - if ( !progMeshPtr ) { - continue; // no mesh no good. - } + g->SetActiveVertexShader( "VS_ExNode" ); + g->SetupVS_ExMeshDrawCall(); + g->SetupVS_ExConstantBuffer(); - mm->GetTexAniState()->UpdateTexList(); - auto progMeshId = progMeshPtr->GetProgId(); - Toolbox::hash_combine( baseNodeMeshKey, progMeshId ); - } else if ( auto proto = mvi->Visual->As(); proto ) { - auto progMeshId = proto->GetProgId(); - Toolbox::hash_combine( baseNodeMeshKey, progMeshId ); - } else { - // broken inheritance check. - continue; - } + VS_ExConstantBuffer_PerInstanceNode instanceInfo; + instanceInfo.Color = modelColor; + instanceInfo.Fatness = std::max( 0.f, fatness * 0.35f ); + instanceInfo.Scaling = fatness * 0.02f + 1.f; + XMStoreFloat4x4( &instanceInfo.World, finalWorld ); + XMStoreFloat4x4( &instanceInfo.PrevWorld, prevWorldNode ); - // Collect into batches by (Meshlib, texture) pair - // This ensures same texture AND same mesh geometry for proper instancing - for ( auto const& itm : mvi->Meshes ) { - zCMaterial* mat = itm.first; - if ( !mat ) continue; - - NodeAttachmentBatchKey key = baseNodeMeshKey; - // TODO: figure out how to know if we need the texture in shadow pass. - Toolbox::hash_combine( key, std::string_view( mat->GetAniTexture() && mat->GetAniTexture()->__GetName().Length() ? mat->GetAniTexture()->__GetName().ToChar() : "")); - - auto& batch = NodeAttachmentBatches[key]; - - // Store the first mesh encountered - if ( !batch.Mesh.size() ) { - batch.Mesh = itm.second; - batch.vobInfo = vi; - batch.node = node; - batch.MeshVisualInfo = mvi; - batch.Material = mat; - } + g->GetActiveVS()->GetConstantBuffer()[1]->UpdateBuffer( &instanceInfo ); + g->GetActiveVS()->GetConstantBuffer()[1]->BindToVertexShader( 1 ); - batch.Instances.push_back( instData ); + if ( updateState ) { + mm->AdvanceAnis(); + mm->CalcVertexPositions(); } + DrawMorphMesh( mm, mvi->Meshes ); } } } - // Draw all batched node attachments - if ( !NodeAttachmentBatches.empty() ) { - g->DrawNodeAttachmentsBatched( NodeAttachmentBatches ); - } - // Clear batches for next frame g->ClearSkeletalMeshBatches(); + g->ClearNodeAttachmentBatches(); } /** Draws particles, in a simple way */ @@ -2054,9 +1938,9 @@ void GothicAPI::DrawMeshInfo( zCMaterial* mat, MeshInfo* msh ) { } if ( !msh->MeshIndexBuffer ) { - Engine::GraphicsEngine->DrawVertexBuffer( msh->MeshVertexBuffer, msh->Vertices.size() ); + Engine::GraphicsEngine->DrawVertexBuffer( msh->MeshVertexBuffer.get(), msh->Vertices.size() ); } else { - Engine::GraphicsEngine->DrawVertexBufferIndexed( msh->MeshVertexBuffer, msh->MeshIndexBuffer, msh->Indices.size() ); + Engine::GraphicsEngine->DrawVertexBufferIndexed( msh->MeshVertexBuffer.get(), msh->MeshIndexBuffer.get(), msh->Indices.size() ); } } @@ -2072,9 +1956,9 @@ void GothicAPI::DrawMeshInfo_Layered( zCMaterial* mat, MeshInfo* msh ) { D3D11GraphicsEngine* g = reinterpret_cast(Engine::GraphicsEngine); if ( !msh->MeshIndexBuffer ) { - g->DrawVertexBufferInstanced( msh->MeshVertexBuffer, msh->Vertices.size(), 6 ); + g->DrawVertexBufferInstanced( msh->MeshVertexBuffer.get(), msh->Vertices.size(), 6 ); } else { - g->DrawVertexBufferInstancedIndexed( msh->MeshVertexBuffer, msh->MeshIndexBuffer, msh->Indices.size(), 6 ); + g->DrawVertexBufferInstancedIndexed( msh->MeshVertexBuffer.get(), msh->MeshIndexBuffer.get(), msh->Indices.size(), 6 ); } } @@ -3130,8 +3014,8 @@ void GothicAPI::DrawTransparencyVobs() { for ( auto const& meshInfo : materialMesh.second ) { g->DrawVertexBufferIndexed( - meshInfo->MeshVertexBuffer, - meshInfo->MeshIndexBuffer, + meshInfo->MeshVertexBuffer.get(), + meshInfo->MeshIndexBuffer.get(), meshInfo->Indices.size() ); } } @@ -3157,8 +3041,8 @@ void GothicAPI::DrawTransparencyVobs() { for ( auto const& meshInfo : materialMesh.second ) { g->DrawVertexBufferIndexed( - meshInfo->MeshVertexBuffer, - meshInfo->MeshIndexBuffer, + meshInfo->MeshVertexBuffer.get(), + meshInfo->MeshIndexBuffer.get(), meshInfo->Indices.size() ); } } @@ -5335,7 +5219,7 @@ void GothicAPI::DrawMorphMesh( zCMorphMesh* msh, std::mapMeshIndex == i ) { mi->MeshVertexBuffer->UpdateBuffer( &vertices[0], vertices.size() * sizeof( ExVertexStruct ) ); - Engine::GraphicsEngine->DrawVertexBufferIndexed( mi->MeshVertexBuffer, mi->MeshIndexBuffer, mi->Indices.size() ); + Engine::GraphicsEngine->DrawVertexBufferIndexed( mi->MeshVertexBuffer.get(), mi->MeshIndexBuffer.get(), mi->Indices.size() ); goto Out_Of_Nested_Loop; } } @@ -5375,7 +5259,7 @@ void GothicAPI::DrawMorphMesh_Layered( zCMorphMesh* msh, std::mapMeshIndex == i ) { mi->MeshVertexBuffer->UpdateBuffer( &vertices[0], vertices.size() * sizeof( ExVertexStruct ) ); - g->DrawVertexBufferInstancedIndexed( mi->MeshVertexBuffer, mi->MeshIndexBuffer, mi->Indices.size(), 6 ); + g->DrawVertexBufferInstancedIndexed( mi->MeshVertexBuffer.get(), mi->MeshIndexBuffer.get(), mi->Indices.size(), 6 ); goto Out_Of_Nested_Loop; } } diff --git a/D3D11Engine/GothicAPI.h b/D3D11Engine/GothicAPI.h index c3bec476..b66849b7 100644 --- a/D3D11Engine/GothicAPI.h +++ b/D3D11Engine/GothicAPI.h @@ -224,19 +224,28 @@ class zCMorphMesh; class zCDecal; // CPU-side batch information for skeletal mesh rendering +struct SkeletalMeshBatchEntry { + zCTexture* material; + SkeletalMeshInfo* mesh; + uint32_t instanceIndex; +}; + struct SkeletalMeshBatch { - SkeletalMeshVisualInfo* VisualInfo; // Shared visual info - std::vector Vobs; // All vobs in this batch - std::vector InstanceData; // Per-instance GPU data - std::vector AllBoneTransforms; // All bone matrices for all instances - uint32_t TotalBoneCount; // Total bones across all instances - - void Clear() { - Vobs.clear(); - InstanceData.clear(); - AllBoneTransforms.clear(); - TotalBoneCount = 0; - } + std::vector Entries; + std::vector BoneTransforms; + std::vector Instances; +}; + +// CPU-side batch information for node attachment rendering +struct NodeAttachmentBatchEntry { + zCTexture* texture; // Texture for this entry + MeshInfo* mesh; // Mesh geometry + uint32_t instanceIndex; // Index into NodeAttachmentInstanceData buffer +}; + +struct NodeAttachmentBatch { + std::vector Entries; + std::vector Instances; }; class GothicAPI { diff --git a/D3D11Engine/MeshManager.cpp b/D3D11Engine/MeshManager.cpp new file mode 100644 index 00000000..0581d08f --- /dev/null +++ b/D3D11Engine/MeshManager.cpp @@ -0,0 +1,13 @@ +#include "MeshManager.h" +#include "BaseGraphicsEngine.h" +#include "zCProgMeshProto.h" +#include "zCMeshSoftSkin.h" + +MeshManager* s_MeshManager = new MeshManager(); + +MeshManager::MeshManager() { + m_skeletalMeshInfo = {}; + m_staticMeshInfo = {}; +} + + diff --git a/D3D11Engine/MeshManager.h b/D3D11Engine/MeshManager.h new file mode 100644 index 00000000..b4829263 --- /dev/null +++ b/D3D11Engine/MeshManager.h @@ -0,0 +1,41 @@ +#pragma once +#include "pch.h" +#include "zCProgMeshProto.h" + +class MeshManager { + +public: + MeshManager(); + + std::shared_ptr MeshManager::PutSkeletalData( zCSubMesh* subMesh, SkeletalMeshData* data ) { + auto sharedData = std::shared_ptr( data ); + m_skeletalMeshInfo[subMesh] = sharedData; + return sharedData; + } + + std::shared_ptr MeshManager::GetSkeletalData( zCSubMesh* subMesh ) { + auto f = m_skeletalMeshInfo.find( subMesh ); + if ( f != m_skeletalMeshInfo.end() ) { + return f->second; + } + return nullptr; + } + + std::shared_ptr MeshManager::PutStaticData( zCSubMesh* subMesh, StaticMeshData* data ) { + auto sharedData = std::shared_ptr( data ); + m_staticMeshInfo[subMesh] = sharedData; + return sharedData; + } + + std::shared_ptr MeshManager::GetStaticData( zCSubMesh* subMesh ) { + auto f = m_staticMeshInfo.find( subMesh ); + if ( f != m_staticMeshInfo.end() ) { + return f->second; + } + return nullptr; + } + +private: + std::unordered_map> m_skeletalMeshInfo; + std::unordered_map> m_staticMeshInfo; +}; diff --git a/D3D11Engine/Shaders/VS_ExNodeInstanced.hlsl b/D3D11Engine/Shaders/VS_ExNodeInstanced.hlsl index 0f1633d3..204f7eea 100644 --- a/D3D11Engine/Shaders/VS_ExNodeInstanced.hlsl +++ b/D3D11Engine/Shaders/VS_ExNodeInstanced.hlsl @@ -1,6 +1,7 @@ //-------------------------------------------------------------------------------------- // Instanced Node Attachment Vertex Shader // For weapons, heads, and other attachments +// Uses an indirection buffer to map SV_InstanceID to actual instance index //-------------------------------------------------------------------------------------- #include "Globals_VS_ExConstants.h" @@ -13,7 +14,7 @@ cbuffer Matrices_PerFrame : register( b0 ) struct NodeAttachmentInstance { matrix World; - matrix PrevWorld; // TODO: implement motion vectors + matrix PrevWorld; float4 Color; float Fatness; float Scaling; @@ -21,6 +22,7 @@ struct NodeAttachmentInstance }; StructuredBuffer InstanceBuffer : register(t10); +StructuredBuffer InstanceIndexBuffer : register(t12); // Indirection: SV_InstanceID -> actual instance index struct VS_INPUT { @@ -47,8 +49,11 @@ VS_OUTPUT VSMain(VS_INPUT Input, uint instanceID : SV_InstanceID) { VS_OUTPUT Output; - // Get instance data - NodeAttachmentInstance inst = InstanceBuffer[instanceID]; + // Use indirection buffer to get actual instance index + uint actualInstanceIndex = InstanceIndexBuffer[instanceID]; + + // Get instance data using the actual index + NodeAttachmentInstance inst = InstanceBuffer[actualInstanceIndex]; // Apply scaling and fatness float3 position = Input.vPosition * inst.Scaling; diff --git a/D3D11Engine/Shaders/VS_ExNodeInstancedCube.hlsl b/D3D11Engine/Shaders/VS_ExNodeInstancedCube.hlsl index c257ee84..881fe6c5 100644 --- a/D3D11Engine/Shaders/VS_ExNodeInstancedCube.hlsl +++ b/D3D11Engine/Shaders/VS_ExNodeInstancedCube.hlsl @@ -1,6 +1,7 @@ //-------------------------------------------------------------------------------------- // Instanced Node Attachment Vertex Shader for Cube Shadow Maps // For weapons, heads, and other attachments +// Uses an indirection buffer to map SV_InstanceID to actual instance index //-------------------------------------------------------------------------------------- #include "Globals_VS_ExConstants.h" @@ -14,6 +15,7 @@ cbuffer Matrices_PerFrame : register( b0 ) struct NodeAttachmentInstance { matrix World; + matrix PrevWorld; float4 Color; float Fatness; float Scaling; @@ -21,6 +23,7 @@ struct NodeAttachmentInstance }; StructuredBuffer InstanceBuffer : register(t10); +StructuredBuffer InstanceIndexBuffer : register(t12); // Indirection: SV_InstanceID -> actual instance index struct VS_INPUT { @@ -46,8 +49,11 @@ VS_OUTPUT VSMain(VS_INPUT Input, uint instanceID : SV_InstanceID) { VS_OUTPUT Output; - // Get instance data - NodeAttachmentInstance inst = InstanceBuffer[instanceID]; + // Use indirection buffer to get actual instance index + uint actualInstanceIndex = InstanceIndexBuffer[instanceID]; + + // Get instance data using the actual index + NodeAttachmentInstance inst = InstanceBuffer[actualInstanceIndex]; // Apply scaling and fatness float3 position = Input.vPosition * inst.Scaling; diff --git a/D3D11Engine/Shaders/VS_ExSkeletalInstanced.hlsl b/D3D11Engine/Shaders/VS_ExSkeletalInstanced.hlsl index 2b1e8775..35841c50 100644 --- a/D3D11Engine/Shaders/VS_ExSkeletalInstanced.hlsl +++ b/D3D11Engine/Shaders/VS_ExSkeletalInstanced.hlsl @@ -1,6 +1,7 @@ //-------------------------------------------------------------------------------------- // Instanced Skeletal Vertex Shader // Uses StructuredBuffers for per-instance data and bone transforms +// Uses an indirection buffer to map SV_InstanceID to actual instance index //-------------------------------------------------------------------------------------- static const int NUM_MAX_BONES = 96; @@ -27,6 +28,7 @@ struct SkeletalInstanceData // Structured buffers for instanced data StructuredBuffer InstanceBuffer : register(t10); StructuredBuffer BoneBuffer : register(t11); +StructuredBuffer InstanceIndexBuffer : register(t12); // Indirection: SV_InstanceID -> actual instance index //-------------------------------------------------------------------------------------- // Input / Output structures @@ -60,8 +62,11 @@ VS_OUTPUT VSMain(VS_INPUT Input, uint instanceID : SV_InstanceID) { VS_OUTPUT Output; - // Get instance data - SkeletalInstanceData inst = InstanceBuffer[instanceID]; + // Use indirection buffer to get actual instance index + uint actualInstanceIndex = InstanceIndexBuffer[instanceID]; + + // Get instance data using the actual index + SkeletalInstanceData inst = InstanceBuffer[actualInstanceIndex]; // Calculate bone indices with offset for this instance uint bone0 = inst.BoneOffset + Input.BoneIndices.x; diff --git a/D3D11Engine/Shaders/VS_ExSkeletalInstancedCube.hlsl b/D3D11Engine/Shaders/VS_ExSkeletalInstancedCube.hlsl index bf1bc10a..a28a6461 100644 --- a/D3D11Engine/Shaders/VS_ExSkeletalInstancedCube.hlsl +++ b/D3D11Engine/Shaders/VS_ExSkeletalInstancedCube.hlsl @@ -1,5 +1,6 @@ //-------------------------------------------------------------------------------------- // Instanced Skeletal Vertex Shader for Cube Shadow Maps +// Uses an indirection buffer to map SV_InstanceID to actual instance index //-------------------------------------------------------------------------------------- static const int NUM_MAX_BONES = 96; @@ -14,6 +15,7 @@ cbuffer Matrices_PerFrame : register( b0 ) struct SkeletalInstanceData { matrix World; + matrix PrevWorld; float4 Color; float Fatness; float Scale; @@ -23,6 +25,7 @@ struct SkeletalInstanceData StructuredBuffer InstanceBuffer : register(t10); StructuredBuffer BoneBuffer : register(t11); +StructuredBuffer InstanceIndexBuffer : register(t12); // Indirection: SV_InstanceID -> actual instance index struct VS_INPUT { @@ -49,7 +52,11 @@ VS_OUTPUT VSMain(VS_INPUT Input, uint instanceID : SV_InstanceID) { VS_OUTPUT Output; - SkeletalInstanceData inst = InstanceBuffer[instanceID]; + // Use indirection buffer to get actual instance index + uint actualInstanceIndex = InstanceIndexBuffer[instanceID]; + + // Get instance data using the actual index + SkeletalInstanceData inst = InstanceBuffer[actualInstanceIndex]; uint bone0 = inst.BoneOffset + Input.BoneIndices.x; uint bone1 = inst.BoneOffset + Input.BoneIndices.y; diff --git a/D3D11Engine/WorldConverter.cpp b/D3D11Engine/WorldConverter.cpp index e67657ed..5dc5d165 100644 --- a/D3D11Engine/WorldConverter.cpp +++ b/D3D11Engine/WorldConverter.cpp @@ -22,6 +22,9 @@ #include "D3D11Texture.h" #include "D3D7\MyDirectDrawSurface7.h" #include "zCQuadMark.h" +#include "MeshManager.h" + +extern MeshManager* s_MeshManager; WorldConverter::WorldConverter() {} @@ -91,8 +94,13 @@ void WorldConverter::WorldMeshCollectPolyRange( const float3& position, float ra it->second->Indices = indices; // Create the buffers - Engine::GraphicsEngine->CreateVertexBuffer( &it->second->MeshVertexBuffer ); - Engine::GraphicsEngine->CreateVertexBuffer( &it->second->MeshIndexBuffer ); + D3D11VertexBuffer* vertexBuffer; + Engine::GraphicsEngine->CreateVertexBuffer( &vertexBuffer ); + it->second->MeshVertexBuffer.reset( vertexBuffer ); + + D3D11VertexBuffer* indexBuffer; + Engine::GraphicsEngine->CreateVertexBuffer( &indexBuffer ); + it->second->MeshIndexBuffer.reset( indexBuffer ); // Init and fill them it->second->MeshVertexBuffer->Init( &it->second->Vertices[0], it->second->Vertices.size() * sizeof( ExVertexStruct ), D3D11VertexBuffer::B_VERTEXBUFFER, D3D11VertexBuffer::U_IMMUTABLE ); @@ -249,8 +257,14 @@ XRESULT WorldConverter::LoadWorldMeshFromFile( const std::string& file, std::map it.second->Indices = indices; // Create the buffers - Engine::GraphicsEngine->CreateVertexBuffer( &it.second->MeshVertexBuffer ); - Engine::GraphicsEngine->CreateVertexBuffer( &it.second->MeshIndexBuffer ); + + D3D11VertexBuffer* vertexBuffer; + Engine::GraphicsEngine->CreateVertexBuffer( &vertexBuffer ); + it.second->MeshVertexBuffer.reset( vertexBuffer ); + + D3D11VertexBuffer* indexBuffer; + Engine::GraphicsEngine->CreateVertexBuffer( &indexBuffer ); + it.second->MeshIndexBuffer.reset( indexBuffer ); // Optimize faces it.second->MeshVertexBuffer->OptimizeFaces( &it.second->Indices[0], @@ -305,8 +319,13 @@ XRESULT WorldConverter::LoadWorldMeshFromFile( const std::string& file, std::map // Create the buffers for wrapped mesh MeshInfo* wmi = new MeshInfo; - Engine::GraphicsEngine->CreateVertexBuffer( &wmi->MeshVertexBuffer ); - Engine::GraphicsEngine->CreateVertexBuffer( &wmi->MeshIndexBuffer ); + D3D11VertexBuffer* vertexBuffer; + Engine::GraphicsEngine->CreateVertexBuffer( &vertexBuffer ); + wmi->MeshVertexBuffer.reset( vertexBuffer ); + + D3D11VertexBuffer* indexBuffer; + Engine::GraphicsEngine->CreateVertexBuffer( &indexBuffer ); + wmi->MeshIndexBuffer.reset( indexBuffer ); // Init and fill them wmi->MeshVertexBuffer->Init( &wrappedVertices[0], wrappedVertices.size() * sizeof( ExVertexStruct ), D3D11VertexBuffer::B_VERTEXBUFFER, D3D11VertexBuffer::U_IMMUTABLE ); @@ -524,8 +543,13 @@ HRESULT WorldConverter::ConvertWorldMesh( zCPolygon** polys, unsigned int numPol it.second->Indices = indices; // Create the buffers - Engine::GraphicsEngine->CreateVertexBuffer( &it.second->MeshVertexBuffer ); - Engine::GraphicsEngine->CreateVertexBuffer( &it.second->MeshIndexBuffer ); + D3D11VertexBuffer* vertexBuffer; + Engine::GraphicsEngine->CreateVertexBuffer( &vertexBuffer ); + it.second->MeshVertexBuffer.reset( vertexBuffer ); + + D3D11VertexBuffer* indexBuffer; + Engine::GraphicsEngine->CreateVertexBuffer( &indexBuffer ); + it.second->MeshIndexBuffer.reset( indexBuffer ); // Generate normals GenerateVertexNormals( it.second->Vertices, it.second->Indices ); @@ -576,8 +600,13 @@ HRESULT WorldConverter::ConvertWorldMesh( zCPolygon** polys, unsigned int numPol // Create the buffers for wrapped mesh MeshInfo* wmi = new MeshInfo(); - Engine::GraphicsEngine->CreateVertexBuffer( &wmi->MeshVertexBuffer ); - Engine::GraphicsEngine->CreateVertexBuffer( &wmi->MeshIndexBuffer ); + D3D11VertexBuffer* vertexBuffer; + Engine::GraphicsEngine->CreateVertexBuffer( &vertexBuffer ); + wmi->MeshVertexBuffer.reset( vertexBuffer ); + + D3D11VertexBuffer* indexBuffer; + Engine::GraphicsEngine->CreateVertexBuffer( &indexBuffer ); + wmi->MeshIndexBuffer.reset( indexBuffer ); // Init and fill them wmi->MeshVertexBuffer->Init( &wrappedVertices[0], wrappedVertices.size() * sizeof( ExVertexStruct ), D3D11VertexBuffer::B_VERTEXBUFFER, D3D11VertexBuffer::U_IMMUTABLE ); @@ -665,7 +694,10 @@ void WorldConverter::GenerateFullSectionMesh( WorldMeshSectionInfo& section ) { section.FullStaticMesh->Vertices = vx; // Create the buffers - Engine::GraphicsEngine->CreateVertexBuffer( §ion.FullStaticMesh->MeshVertexBuffer ); + + D3D11VertexBuffer* vertexBuffer; + Engine::GraphicsEngine->CreateVertexBuffer( &vertexBuffer ); + section.FullStaticMesh->MeshVertexBuffer.reset( vertexBuffer ); // Init and fill them section.FullStaticMesh->MeshVertexBuffer->Init( §ion.FullStaticMesh->Vertices[0], section.FullStaticMesh->Vertices.size() * sizeof( ExVertexStruct ), D3D11VertexBuffer::B_VERTEXBUFFER, D3D11VertexBuffer::U_IMMUTABLE ); @@ -760,8 +792,13 @@ void WorldConverter::Extract3DSMeshFromVisual( zCProgMeshProto* visual, MeshVisu mi->Indices = indices; // Create the buffers - Engine::GraphicsEngine->CreateVertexBuffer( &mi->MeshVertexBuffer ); - Engine::GraphicsEngine->CreateVertexBuffer( &mi->MeshIndexBuffer ); + D3D11VertexBuffer* vertexBuffer; + Engine::GraphicsEngine->CreateVertexBuffer( &vertexBuffer ); + mi->MeshVertexBuffer.reset( vertexBuffer ); + + D3D11VertexBuffer* indexBuffer; + Engine::GraphicsEngine->CreateVertexBuffer( &indexBuffer ); + mi->MeshIndexBuffer.reset( indexBuffer ); // Init and fill it mi->MeshVertexBuffer->Init( &vertices[0], vertices.size() * sizeof( ExVertexStruct ) ); @@ -869,7 +906,7 @@ void WorldConverter::ExtractSkeletalMeshFromVob( zCModel* model, SkeletalMeshVis pvx.Color = 0xFFFFFFFF; } - zCMaterial* mat = s->GetSubmesh( i )->Material; + zCMaterial* mat = m->Material; SkeletalMeshInfo* mi = new SkeletalMeshInfo; mi->Vertices = vertices; @@ -877,22 +914,57 @@ void WorldConverter::ExtractSkeletalMeshFromVob( zCModel* model, SkeletalMeshVis mi->visual = s; // Create the buffers - Engine::GraphicsEngine->CreateVertexBuffer( &mi->MeshVertexBuffer ); - Engine::GraphicsEngine->CreateVertexBuffer( &mi->MeshIndexBuffer ); + std::shared_ptr skeltalMeshData = s_MeshManager->GetSkeletalData( m ); + if ( skeltalMeshData == nullptr ) { + + auto meshData = new SkeletalMeshData; + // Create the buffers + { + D3D11VertexBuffer* vertexBuffer; + Engine::GraphicsEngine->CreateVertexBuffer( &vertexBuffer ); + meshData->MeshVertexBuffer.reset( vertexBuffer ); + + D3D11VertexBuffer* indexBuffer; + Engine::GraphicsEngine->CreateVertexBuffer( &indexBuffer ); + meshData->MeshIndexBuffer.reset( indexBuffer ); + } + + // Init and fill it + meshData->MeshVertexBuffer->Init( &mi->Vertices[0], mi->Vertices.size() * sizeof( ExSkelVertexStruct ), D3D11VertexBuffer::B_VERTEXBUFFER, D3D11VertexBuffer::U_IMMUTABLE ); + meshData->MeshIndexBuffer->Init( &mi->Indices[0], mi->Indices.size() * sizeof( VERTEX_INDEX ), D3D11VertexBuffer::B_INDEXBUFFER, D3D11VertexBuffer::U_IMMUTABLE ); + + s_MeshManager->PutSkeletalData( m, meshData ); + skeltalMeshData = s_MeshManager->GetSkeletalData( m ); + } + mi->MeshIndexBuffer = skeltalMeshData->MeshIndexBuffer; + mi->MeshVertexBuffer = skeltalMeshData->MeshVertexBuffer; - // Init and fill it - mi->MeshVertexBuffer->Init( &mi->Vertices[0], mi->Vertices.size() * sizeof( ExSkelVertexStruct ), D3D11VertexBuffer::B_VERTEXBUFFER, D3D11VertexBuffer::U_IMMUTABLE ); - mi->MeshIndexBuffer->Init( &mi->Indices[0], mi->Indices.size() * sizeof( VERTEX_INDEX ), D3D11VertexBuffer::B_INDEXBUFFER, D3D11VertexBuffer::U_IMMUTABLE ); MeshInfo* bmi = new MeshInfo; bmi->Indices = indices; bmi->Vertices = bindPoseVertices; - Engine::GraphicsEngine->CreateVertexBuffer( &bmi->MeshVertexBuffer ); - Engine::GraphicsEngine->CreateVertexBuffer( &bmi->MeshIndexBuffer ); + std::shared_ptr staticMeshData = s_MeshManager->GetStaticData( m ); + if ( staticMeshData == nullptr ) { + auto meshData = new StaticMeshData; + { + D3D11VertexBuffer* vertexBuffer; + Engine::GraphicsEngine->CreateVertexBuffer( &vertexBuffer ); + meshData->MeshVertexBuffer.reset( vertexBuffer ); + + D3D11VertexBuffer* indexBuffer; + Engine::GraphicsEngine->CreateVertexBuffer( &indexBuffer ); + meshData->MeshIndexBuffer.reset( indexBuffer ); + } + + meshData->MeshVertexBuffer->Init( &bmi->Vertices[0], bmi->Vertices.size() * sizeof( ExVertexStruct ), D3D11VertexBuffer::B_VERTEXBUFFER, D3D11VertexBuffer::U_IMMUTABLE ); + meshData->MeshIndexBuffer->Init( &bmi->Indices[0], bmi->Indices.size() * sizeof( VERTEX_INDEX ), D3D11VertexBuffer::B_INDEXBUFFER, D3D11VertexBuffer::U_IMMUTABLE ); - bmi->MeshVertexBuffer->Init( &bmi->Vertices[0], bmi->Vertices.size() * sizeof( ExVertexStruct ), D3D11VertexBuffer::B_VERTEXBUFFER, D3D11VertexBuffer::U_IMMUTABLE ); - bmi->MeshIndexBuffer->Init( &bmi->Indices[0], bmi->Indices.size() * sizeof( VERTEX_INDEX ), D3D11VertexBuffer::B_INDEXBUFFER, D3D11VertexBuffer::U_IMMUTABLE ); + s_MeshManager->PutStaticData( m, meshData ); + staticMeshData = s_MeshManager->GetStaticData( m ); + } + bmi->MeshIndexBuffer = staticMeshData->MeshIndexBuffer; + bmi->MeshVertexBuffer = staticMeshData->MeshVertexBuffer; Engine::GAPI->GetRendererState().RendererInfo.SkeletalVerticesDataSize += mi->Vertices.size() * sizeof( ExVertexStruct ); Engine::GAPI->GetRendererState().RendererInfo.SkeletalVerticesDataSize += mi->Indices.size() * sizeof( VERTEX_INDEX ); @@ -974,8 +1046,6 @@ void WorldConverter::ExtractProgMeshProtoFromModel( zCModel* model, MeshVisualIn vx.Color = 0xFFFFFFFF; } - // Create the buffers and sort the mesh into the structure - MeshInfo* mi = new MeshInfo; // Create the indexed mesh if ( vertices.empty() ) { @@ -983,30 +1053,50 @@ void WorldConverter::ExtractProgMeshProtoFromModel( zCModel* model, MeshVisualIn continue; } + // Create the buffers and sort the mesh into the structure + MeshInfo* mi = new MeshInfo; mi->Vertices = vertices; mi->Indices = indices; + mi->MeshIndex = i; - // Create the buffers - Engine::GraphicsEngine->CreateVertexBuffer( &mi->MeshVertexBuffer ); - Engine::GraphicsEngine->CreateVertexBuffer( &mi->MeshIndexBuffer ); - - // Optimize faces - mi->MeshVertexBuffer->OptimizeFaces( &mi->Indices[0], - reinterpret_cast(&mi->Vertices[0]), - mi->Indices.size(), - mi->Vertices.size(), - sizeof( ExVertexStruct ) ); - - // Then optimize vertices - mi->MeshVertexBuffer->OptimizeVertices( &mi->Indices[0], - reinterpret_cast(&mi->Vertices[0]), - mi->Indices.size(), - mi->Vertices.size(), - sizeof( ExVertexStruct ) ); + std::shared_ptr staticMeshData = s_MeshManager->GetStaticData( m ); + if ( staticMeshData == nullptr ) { + auto meshData = new StaticMeshData; + + { + D3D11VertexBuffer* vertexBuffer; + Engine::GraphicsEngine->CreateVertexBuffer( &vertexBuffer ); + meshData->MeshVertexBuffer.reset( vertexBuffer ); + + D3D11VertexBuffer* indexBuffer; + Engine::GraphicsEngine->CreateVertexBuffer( &indexBuffer ); + meshData->MeshIndexBuffer.reset( indexBuffer ); + } + + // Optimize faces + mi->MeshVertexBuffer->OptimizeFaces( &mi->Indices[0], + reinterpret_cast(&mi->Vertices[0]), + mi->Indices.size(), + mi->Vertices.size(), + sizeof( ExVertexStruct ) ); + + // Then optimize vertices + mi->MeshVertexBuffer->OptimizeVertices( &mi->Indices[0], + reinterpret_cast(&mi->Vertices[0]), + mi->Indices.size(), + mi->Vertices.size(), + sizeof( ExVertexStruct ) ); + + meshData->MeshVertexBuffer->Init( &mi->Vertices[0], mi->Vertices.size() * sizeof( ExVertexStruct ), D3D11VertexBuffer::B_VERTEXBUFFER, D3D11VertexBuffer::U_IMMUTABLE ); + meshData->MeshIndexBuffer->Init( &mi->Indices[0], mi->Indices.size() * sizeof( VERTEX_INDEX ), D3D11VertexBuffer::B_INDEXBUFFER, D3D11VertexBuffer::U_IMMUTABLE ); + + s_MeshManager->PutStaticData( m, meshData ); + staticMeshData = s_MeshManager->GetStaticData( m ); + } // Init and fill it - mi->MeshVertexBuffer->Init( &mi->Vertices[0], mi->Vertices.size() * sizeof( ExVertexStruct ), D3D11VertexBuffer::B_VERTEXBUFFER, D3D11VertexBuffer::U_IMMUTABLE ); - mi->MeshIndexBuffer->Init( &mi->Indices[0], mi->Indices.size() * sizeof( VERTEX_INDEX ), D3D11VertexBuffer::B_INDEXBUFFER, D3D11VertexBuffer::U_IMMUTABLE ); + mi->MeshVertexBuffer = staticMeshData->MeshVertexBuffer; + mi->MeshIndexBuffer = staticMeshData->MeshIndexBuffer; Engine::GAPI->GetRendererState().RendererInfo.VOBVerticesDataSize += mi->Vertices.size() * sizeof( ExVertexStruct ); Engine::GAPI->GetRendererState().RendererInfo.VOBVerticesDataSize += mi->Indices.size() * sizeof( VERTEX_INDEX ); @@ -1042,8 +1132,13 @@ void WorldConverter::ExtractProgMeshProtoFromModel( zCModel* model, MeshVisualIn } MeshInfo* wmi = new MeshInfo; - Engine::GraphicsEngine->CreateVertexBuffer( &wmi->MeshVertexBuffer ); - Engine::GraphicsEngine->CreateVertexBuffer( &wmi->MeshIndexBuffer ); + D3D11VertexBuffer* vertexBuffer; + Engine::GraphicsEngine->CreateVertexBuffer( &vertexBuffer ); + wmi->MeshVertexBuffer.reset( vertexBuffer ); + + D3D11VertexBuffer* indexBuffer; + Engine::GraphicsEngine->CreateVertexBuffer( &indexBuffer ); + wmi->MeshIndexBuffer.reset( indexBuffer ); // Init and fill them wmi->MeshVertexBuffer->Init( &wrappedVertices[0], wrappedVertices.size() * sizeof( ExVertexStruct ), D3D11VertexBuffer::B_VERTEXBUFFER, D3D11VertexBuffer::U_IMMUTABLE ); @@ -1101,8 +1196,13 @@ void WorldConverter::ExtractProgMeshProtoFromMesh( zCMesh* mesh, MeshVisualInfo* mi->Indices = indices; // Create the buffers - Engine::GraphicsEngine->CreateVertexBuffer( &mi->MeshVertexBuffer ); - Engine::GraphicsEngine->CreateVertexBuffer( &mi->MeshIndexBuffer ); + D3D11VertexBuffer* vertexBuffer; + Engine::GraphicsEngine->CreateVertexBuffer( &vertexBuffer ); + mi->MeshVertexBuffer.reset( vertexBuffer ); + + D3D11VertexBuffer* indexBuffer; + Engine::GraphicsEngine->CreateVertexBuffer( &indexBuffer ); + mi->MeshIndexBuffer.reset( indexBuffer ); // Init and fill it mi->MeshVertexBuffer->Init( &vertices[0], vertices.size() * sizeof( ExVertexStruct ) ); @@ -1242,8 +1342,6 @@ void WorldConverter::Extract3DSMeshFromVisual2( zCProgMeshProto* visual, MeshVis bbmax.z = bbmax.z < vx.Position.z ? vx.Position.z : bbmax.z; } - // Create the buffers and sort the mesh into the structure - MeshInfo* mi = new MeshInfo; // Create the indexed mesh if ( vertices.empty() ) { @@ -1251,44 +1349,88 @@ void WorldConverter::Extract3DSMeshFromVisual2( zCProgMeshProto* visual, MeshVis continue; } + // Create the buffers and sort the mesh into the structure + MeshInfo* mi = new MeshInfo; mi->Vertices = vertices; mi->Indices = indices; mi->MeshIndex = i; // Create the buffers - Engine::GraphicsEngine->CreateVertexBuffer( &mi->MeshVertexBuffer ); - Engine::GraphicsEngine->CreateVertexBuffer( &mi->MeshIndexBuffer ); if ( meshInfo->MorphMeshVisual ) { // We need to keep original indices so that we can reuse them(we can't optimize them) // Use dynamic buffer since we'll reupload it every frame we see this visual + std::shared_ptr staticMeshData = s_MeshManager->GetStaticData( s ); + if ( staticMeshData == nullptr ) { + auto meshData = new StaticMeshData; + + { + D3D11VertexBuffer* vertexBuffer; + Engine::GraphicsEngine->CreateVertexBuffer( &vertexBuffer ); + meshData->MeshVertexBuffer.reset( vertexBuffer ); + + D3D11VertexBuffer* indexBuffer; + Engine::GraphicsEngine->CreateVertexBuffer( &indexBuffer ); + meshData->MeshIndexBuffer.reset( indexBuffer ); + } + + meshData->MeshVertexBuffer->Init( &vertices[0], vertices.size() * sizeof( ExVertexStruct ), D3D11VertexBuffer::B_VERTEXBUFFER, D3D11VertexBuffer::U_IMMUTABLE ); + meshData->MeshIndexBuffer->Init( &indices[0], indices.size() * sizeof( VERTEX_INDEX ), D3D11VertexBuffer::B_INDEXBUFFER, D3D11VertexBuffer::U_IMMUTABLE ); + + s_MeshManager->PutStaticData( s, meshData ); + staticMeshData = s_MeshManager->GetStaticData( s ); + } + // Init and fill it - mi->MeshVertexBuffer->Init( &mi->Vertices[0], mi->Vertices.size() * sizeof( ExVertexStruct ), D3D11VertexBuffer::B_VERTEXBUFFER, D3D11VertexBuffer::U_DYNAMIC, D3D11VertexBuffer::CA_WRITE ); + mi->MeshVertexBuffer = staticMeshData->MeshVertexBuffer; + mi->MeshIndexBuffer = staticMeshData->MeshIndexBuffer; } else { - // Optimize faces - mi->MeshVertexBuffer->OptimizeFaces(&mi->Indices[0], - reinterpret_cast(&mi->Vertices[0]), - mi->Indices.size(), - mi->Vertices.size(), - sizeof( ExVertexStruct ) ); - - // Then optimize vertices - mi->MeshVertexBuffer->OptimizeVertices( &mi->Indices[0], - reinterpret_cast(&mi->Vertices[0]), - mi->Indices.size(), - mi->Vertices.size(), - sizeof( ExVertexStruct ) ); + + std::shared_ptr staticMeshData = s_MeshManager->GetStaticData( s ); + if ( staticMeshData == nullptr ) { + auto meshData = new StaticMeshData; + + { + D3D11VertexBuffer* vertexBuffer; + Engine::GraphicsEngine->CreateVertexBuffer( &vertexBuffer ); + meshData->MeshVertexBuffer.reset( vertexBuffer ); + + D3D11VertexBuffer* indexBuffer; + Engine::GraphicsEngine->CreateVertexBuffer( &indexBuffer ); + meshData->MeshIndexBuffer.reset( indexBuffer ); + } + + // Optimize faces + mi->MeshVertexBuffer->OptimizeFaces(&mi->Indices[0], + reinterpret_cast(&mi->Vertices[0]), + mi->Indices.size(), + mi->Vertices.size(), + sizeof( ExVertexStruct ) ); + + // Then optimize vertices + mi->MeshVertexBuffer->OptimizeVertices( &mi->Indices[0], + reinterpret_cast(&mi->Vertices[0]), + mi->Indices.size(), + mi->Vertices.size(), + sizeof( ExVertexStruct ) ); + + meshData->MeshVertexBuffer->Init( &vertices[0], vertices.size() * sizeof( ExVertexStruct ), D3D11VertexBuffer::B_VERTEXBUFFER, D3D11VertexBuffer::U_IMMUTABLE ); + meshData->MeshIndexBuffer->Init( &indices[0], indices.size() * sizeof( VERTEX_INDEX ), D3D11VertexBuffer::B_INDEXBUFFER, D3D11VertexBuffer::U_IMMUTABLE ); + + s_MeshManager->PutStaticData( s, meshData ); + staticMeshData = s_MeshManager->GetStaticData( s ); + } // Init and fill it - mi->MeshVertexBuffer->Init( &mi->Vertices[0], mi->Vertices.size() * sizeof( ExVertexStruct ), D3D11VertexBuffer::B_VERTEXBUFFER, D3D11VertexBuffer::U_IMMUTABLE ); + mi->MeshVertexBuffer = staticMeshData->MeshVertexBuffer; + mi->MeshIndexBuffer = staticMeshData->MeshIndexBuffer; } - mi->MeshIndexBuffer->Init( &mi->Indices[0], mi->Indices.size() * sizeof( VERTEX_INDEX ), D3D11VertexBuffer::B_INDEXBUFFER, D3D11VertexBuffer::U_IMMUTABLE ); Engine::GAPI->GetRendererState().RendererInfo.VOBVerticesDataSize += mi->Vertices.size() * sizeof( ExVertexStruct ); Engine::GAPI->GetRendererState().RendererInfo.VOBVerticesDataSize += mi->Indices.size() * sizeof( VERTEX_INDEX ); - zCMaterial* mat = visual->GetSubmesh( i )->Material; + zCMaterial* mat = s->Material; meshInfo->Meshes[mat].emplace_back( mi ); MeshKey key; @@ -1320,8 +1462,13 @@ void WorldConverter::Extract3DSMeshFromVisual2( zCProgMeshProto* visual, MeshVis } MeshInfo* wmi = new MeshInfo; - Engine::GraphicsEngine->CreateVertexBuffer( &wmi->MeshVertexBuffer ); - Engine::GraphicsEngine->CreateVertexBuffer( &wmi->MeshIndexBuffer ); + D3D11VertexBuffer* vertexBuffer; + Engine::GraphicsEngine->CreateVertexBuffer( &vertexBuffer ); + wmi->MeshVertexBuffer.reset( vertexBuffer ); + + D3D11VertexBuffer* indexBuffer; + Engine::GraphicsEngine->CreateVertexBuffer( &indexBuffer ); + wmi->MeshIndexBuffer.reset( indexBuffer ); // Init and fill them wmi->MeshVertexBuffer->Init( &wrappedVertices[0], wrappedVertices.size() * sizeof( ExVertexStruct ), D3D11VertexBuffer::B_VERTEXBUFFER, D3D11VertexBuffer::U_IMMUTABLE ); diff --git a/D3D11Engine/WorldConverter.h b/D3D11Engine/WorldConverter.h index 26f1576b..032eb156 100644 --- a/D3D11Engine/WorldConverter.h +++ b/D3D11Engine/WorldConverter.h @@ -22,6 +22,7 @@ class zCModel; class zCModelPrototype; class zCModelMeshLib; class zCMesh; + class WorldConverter { public: WorldConverter(); diff --git a/D3D11Engine/WorldObjects.cpp b/D3D11Engine/WorldObjects.cpp index d20aceb7..8c227a75 100644 --- a/D3D11Engine/WorldObjects.cpp +++ b/D3D11Engine/WorldObjects.cpp @@ -55,16 +55,16 @@ MeshInfo::~MeshInfo() { //Engine::GAPI->GetRendererState().RendererInfo.VOBVerticesDataSize -= Indices.size() * sizeof(VERTEX_INDEX); //Engine::GAPI->GetRendererState().RendererInfo.VOBVerticesDataSize -= Vertices.size() * sizeof(ExVertexStruct); - delete MeshVertexBuffer; - delete MeshIndexBuffer; + MeshVertexBuffer.reset(); + MeshIndexBuffer.reset(); } SkeletalMeshInfo::~SkeletalMeshInfo() { Engine::GAPI->GetRendererState().RendererInfo.SkeletalVerticesDataSize -= Indices.size() * sizeof( VERTEX_INDEX ); Engine::GAPI->GetRendererState().RendererInfo.SkeletalVerticesDataSize -= Vertices.size() * sizeof( ExSkelVertexStruct ); - delete MeshVertexBuffer; - delete MeshIndexBuffer; + MeshVertexBuffer.reset(); + MeshIndexBuffer.reset(); } /** Clears the cache for the given progmesh */ @@ -95,8 +95,13 @@ XRESULT MeshInfo::Create( ExVertexStruct* vertices, unsigned int numVertices, VE memcpy( &Indices[0], indices, numIndices * sizeof( VERTEX_INDEX ) ); // Create the buffers - Engine::GraphicsEngine->CreateVertexBuffer( &MeshVertexBuffer ); - Engine::GraphicsEngine->CreateVertexBuffer( &MeshIndexBuffer ); + D3D11VertexBuffer* vertexBuffer; + Engine::GraphicsEngine->CreateVertexBuffer( &vertexBuffer ); + MeshVertexBuffer.reset( vertexBuffer ); + + D3D11VertexBuffer* indexBuffer; + Engine::GraphicsEngine->CreateVertexBuffer( &indexBuffer ); + MeshIndexBuffer.reset( indexBuffer ); // Init and fill it MeshVertexBuffer->Init( vertices, numVertices * sizeof( ExVertexStruct ) ); diff --git a/D3D11Engine/WorldObjects.h b/D3D11Engine/WorldObjects.h index 73f3ef98..78e33173 100644 --- a/D3D11Engine/WorldObjects.h +++ b/D3D11Engine/WorldObjects.h @@ -22,6 +22,7 @@ struct zCModelNodeInst; struct BspInfo; class zCQuadMark; struct MaterialInfo; +class zCSubMesh; struct ParticleRenderInfo { GothicBlendStateInfo BlendState; @@ -75,6 +76,18 @@ struct cmpMeshKey { } };*/ +// singleton data struct +struct StaticMeshData { + std::shared_ptr MeshVertexBuffer; + std::shared_ptr MeshIndexBuffer; +}; + +// singleton data struct +struct SkeletalMeshData { + std::shared_ptr MeshVertexBuffer; + std::shared_ptr MeshIndexBuffer; +}; + /** Holds information about a mesh, ready to be loaded into the renderer */ struct MeshInfo { MeshInfo() { @@ -86,11 +99,16 @@ struct MeshInfo { virtual ~MeshInfo(); + static MeshInfo* CreateStatic() { + auto info = new MeshInfo(); + return info; + } + /** Creates buffers for this mesh info */ XRESULT Create( ExVertexStruct* vertices, unsigned int numVertices, VERTEX_INDEX* indices, unsigned int numIndices ); - D3D11VertexBuffer* MeshVertexBuffer; - D3D11VertexBuffer* MeshIndexBuffer; + std::shared_ptr MeshVertexBuffer; + std::shared_ptr MeshIndexBuffer; std::vector Vertices; std::vector Indices; @@ -135,8 +153,8 @@ struct SkeletalMeshInfo { ~SkeletalMeshInfo(); - D3D11VertexBuffer* MeshVertexBuffer; - D3D11VertexBuffer* MeshIndexBuffer; + std::shared_ptr MeshVertexBuffer; + std::shared_ptr MeshIndexBuffer; std::vector Vertices; std::vector Indices; From 3a1e2576b6884ccaa5fbc97061db53e9dc2d1b3e Mon Sep 17 00:00:00 2001 From: kirides <13602143+kirides@users.noreply.github.com> Date: Tue, 17 Feb 2026 19:44:05 +0100 Subject: [PATCH 11/13] share vertex and index buffer singletons for the same meshes --- D3D11Engine/GothicAPI.cpp | 2 +- D3D11Engine/ImGuiEditorView.cpp | 7 +- D3D11Engine/VertexTypes.h | 41 +++++++ D3D11Engine/WorldConverter.cpp | 183 ++++++++++++++------------------ D3D11Engine/WorldObjects.h | 12 ++- 5 files changed, 132 insertions(+), 113 deletions(-) diff --git a/D3D11Engine/GothicAPI.cpp b/D3D11Engine/GothicAPI.cpp index 70bc75c2..3997bbc7 100644 --- a/D3D11Engine/GothicAPI.cpp +++ b/D3D11Engine/GothicAPI.cpp @@ -5500,7 +5500,7 @@ void GothicAPI::CreatezCPolygonsForSections() { it->first.Material->SetAlphaFunc( zMAT_ALPHA_FUNC_NONE ); - WorldConverter::ConvertExVerticesTozCPolygons( it->second->Vertices, it->second->Indices, it->first.Material, section.SectionPolygons ); + WorldConverter::ConvertExVerticesTozCPolygons( *it->second->Vertices.data, *it->second->Indices.data, it->first.Material, section.SectionPolygons ); } } } diff --git a/D3D11Engine/ImGuiEditorView.cpp b/D3D11Engine/ImGuiEditorView.cpp index a68c011c..e12f581b 100644 --- a/D3D11Engine/ImGuiEditorView.cpp +++ b/D3D11Engine/ImGuiEditorView.cpp @@ -1092,12 +1092,7 @@ GVegetationBox* ImGuiEditorView::TraceVegetationBoxes(const XMFLOAT3& wPos, cons } void ImGuiEditorView::SmoothMesh(WorldMeshInfo* mesh, bool tesselate) { - // Copy old vertices so we can directly write to the vectors again - std::vector vxOld = mesh->Vertices; - std::vector ixOld = mesh->Indices; - - // Mark dirty - mesh->SaveInfo = true; + // Not implemented } XRESULT ImGuiEditorView::OnVobRemovedFromWorld(zCVob* vob) { diff --git a/D3D11Engine/VertexTypes.h b/D3D11Engine/VertexTypes.h index 5cb79946..cc4a9ec1 100644 --- a/D3D11Engine/VertexTypes.h +++ b/D3D11Engine/VertexTypes.h @@ -4,6 +4,47 @@ typedef unsigned short VERTEX_INDEX; +#include +#include + +template +struct SharedVector { + std::shared_ptr> data; + + SharedVector() : data( std::make_shared>() ) {} + explicit SharedVector( std::shared_ptr> p ) : data( p ) {} + + T& operator[]( size_t index ) { return (*data)[index]; } + const T& operator[]( size_t index ) const { return (*data)[index]; } + + T& at( size_t index ) { return data->at( index ); } + const T& at( size_t index ) const { return data->at( index ); } + + T& front() { return data->front(); } + T& back() { return data->back(); } + + size_t size() const { return data ? data->size() : 0; } + bool empty() const { return !data || data->empty(); } + void reserve( size_t n ) { if ( data ) data->reserve( n ); } + void resize( size_t n ) { if ( data ) data->resize( n ); } + + void push_back( const T& value ) { data->push_back( value ); } + void push_back( T&& value ) { data->push_back( std::move( value ) ); } + + template + void emplace_back( Args&&... args ) { data->emplace_back( std::forward( args )... ); } + + void pop_back() { data->pop_back(); } + void clear() { if ( data ) data->clear(); } + + auto begin() { return data->begin(); } + auto end() { return data->end(); } + auto begin() const { return data->begin(); } + auto end() const { return data->end(); } + + T* data_ptr() { return data ? data->data() : nullptr; } +}; + /** We pack most of Gothics FVF-formats into this vertex-struct */ struct ExVertexStruct { float3 Position; diff --git a/D3D11Engine/WorldConverter.cpp b/D3D11Engine/WorldConverter.cpp index 5dc5d165..4c4a7827 100644 --- a/D3D11Engine/WorldConverter.cpp +++ b/D3D11Engine/WorldConverter.cpp @@ -90,8 +90,8 @@ void WorldConverter::WorldMeshCollectPolyRange( const float3& position, float ra std::vector vertices; IndexVertices( &it->second->Vertices[0], it->second->Vertices.size(), vertices, indices ); - it->second->Vertices = vertices; - it->second->Indices = indices; + it->second->Vertices = SharedVector{ std::make_shared>( vertices ) }; + it->second->Indices = SharedVector{ std::make_shared>( indices ) }; // Create the buffers D3D11VertexBuffer* vertexBuffer; @@ -131,7 +131,9 @@ XRESULT WorldConverter::LoadWorldMeshFromFile( const std::string& file, std::map for ( unsigned int m = 0; m < meshes.size(); m++ ) { auto& meshData = gm[textures[m]]; - meshData.emplace_back( std::make_pair( meshes[m]->Vertices, meshes[m]->Indices ) ); + meshData.emplace_back( std::make_pair( + std::vector(*meshes[m]->Vertices.data), + std::vector(*meshes[m]->Indices.data) ) ); } CacheMesh( gm, file + ".mcache" ); @@ -253,8 +255,8 @@ XRESULT WorldConverter::LoadWorldMeshFromFile( const std::string& file, std::map std::vector indices; IndexVertices( &it.second->Vertices[0], it.second->Vertices.size(), indexedVertices, indices ); - it.second->Vertices = indexedVertices; - it.second->Indices = indices; + it.second->Vertices = SharedVector{ std::make_shared>( indexedVertices ) }; + it.second->Indices = SharedVector{ std::make_shared>( indices ) }; // Create the buffers @@ -285,8 +287,8 @@ XRESULT WorldConverter::LoadWorldMeshFromFile( const std::string& file, std::map it.second->MeshIndexBuffer->Init( &it.second->Indices[0], it.second->Indices.size() * sizeof( VERTEX_INDEX ), D3D11VertexBuffer::B_INDEXBUFFER, D3D11VertexBuffer::U_IMMUTABLE ); // Remember them, to wrap then up later - vertexBuffers.emplace_back( &it.second->Vertices ); - indexBuffers.emplace_back( &it.second->Indices ); + vertexBuffers.emplace_back( it.second->Vertices.data.get() ); + indexBuffers.emplace_back( it.second->Indices.data.get() ); } } } @@ -493,7 +495,7 @@ HRESULT WorldConverter::ConvertWorldMesh( zCPolygon** polys, unsigned int numPol } } - TriangleFanToList( &polyVertices[0], polyVertices.size(), &it->second->Vertices ); + TriangleFanToList( &polyVertices[0], polyVertices.size(), it->second->Vertices.data.get() ); if ( matGroup == zMAT_GROUP_WATER && !mat->HasAlphaTest() ) { #ifdef BUILD_GOTHIC_1_08k MaterialInfo* info = Engine::GAPI->GetMaterialInfoFrom( key.Texture ); @@ -539,8 +541,8 @@ HRESULT WorldConverter::ConvertWorldMesh( zCPolygon** polys, unsigned int numPol std::vector indices; IndexVertices( &it.second->Vertices[0], it.second->Vertices.size(), indexedVertices, indices ); - it.second->Vertices = indexedVertices; - it.second->Indices = indices; + it.second->Vertices = SharedVector{ std::make_shared>( indexedVertices ) }; + it.second->Indices = SharedVector{ std::make_shared>( indices ) }; // Create the buffers D3D11VertexBuffer* vertexBuffer; @@ -552,7 +554,7 @@ HRESULT WorldConverter::ConvertWorldMesh( zCPolygon** polys, unsigned int numPol it.second->MeshIndexBuffer.reset( indexBuffer ); // Generate normals - GenerateVertexNormals( it.second->Vertices, it.second->Indices ); + GenerateVertexNormals( *it.second->Vertices.data, *it.second->Indices.data ); // Optimize faces it.second->MeshVertexBuffer->OptimizeFaces( &it.second->Indices[0], @@ -573,8 +575,8 @@ HRESULT WorldConverter::ConvertWorldMesh( zCPolygon** polys, unsigned int numPol it.second->MeshIndexBuffer->Init( &it.second->Indices[0], it.second->Indices.size() * sizeof( VERTEX_INDEX ), D3D11VertexBuffer::B_INDEXBUFFER, D3D11VertexBuffer::U_IMMUTABLE ); // Remember them, to wrap then up later - vertexBuffers.emplace_back( &it.second->Vertices ); - indexBuffers.emplace_back( &it.second->Indices ); + vertexBuffers.emplace_back( it.second->Vertices.data.get() ); + indexBuffers.emplace_back( it.second->Indices.data.get() ); } } } @@ -691,7 +693,7 @@ void WorldConverter::GenerateFullSectionMesh( WorldMeshSectionInfo& section ) { std::vector indices; section.FullStaticMesh = new MeshInfo; - section.FullStaticMesh->Vertices = vx; + section.FullStaticMesh->Vertices = SharedVector{ std::make_shared>( vx ) }; // Create the buffers @@ -787,9 +789,8 @@ void WorldConverter::Extract3DSMeshFromVisual( zCProgMeshProto* visual, MeshVisu zCMaterial* mat = visual->GetSubmesh( i )->Material; MeshInfo* mi = new MeshInfo; - - mi->Vertices = vertices; - mi->Indices = indices; + mi->Vertices = SharedVector{ std::make_shared>( vertices ) }; + mi->Indices = SharedVector{ std::make_shared>( indices ) }; // Create the buffers D3D11VertexBuffer* vertexBuffer; @@ -909,8 +910,6 @@ void WorldConverter::ExtractSkeletalMeshFromVob( zCModel* model, SkeletalMeshVis zCMaterial* mat = m->Material; SkeletalMeshInfo* mi = new SkeletalMeshInfo; - mi->Vertices = vertices; - mi->Indices = indices; mi->visual = s; // Create the buffers @@ -930,19 +929,21 @@ void WorldConverter::ExtractSkeletalMeshFromVob( zCModel* model, SkeletalMeshVis } // Init and fill it - meshData->MeshVertexBuffer->Init( &mi->Vertices[0], mi->Vertices.size() * sizeof( ExSkelVertexStruct ), D3D11VertexBuffer::B_VERTEXBUFFER, D3D11VertexBuffer::U_IMMUTABLE ); - meshData->MeshIndexBuffer->Init( &mi->Indices[0], mi->Indices.size() * sizeof( VERTEX_INDEX ), D3D11VertexBuffer::B_INDEXBUFFER, D3D11VertexBuffer::U_IMMUTABLE ); + meshData->Vertices = SharedVector{ std::make_shared>( vertices ) }; + meshData->Indices = SharedVector{ std::make_shared>( indices ) }; + meshData->MeshVertexBuffer->Init( &meshData->Vertices[0], meshData->Vertices.size() * sizeof( ExSkelVertexStruct ), D3D11VertexBuffer::B_VERTEXBUFFER, D3D11VertexBuffer::U_IMMUTABLE ); + meshData->MeshIndexBuffer->Init( &meshData->Indices[0], meshData->Indices.size() * sizeof( VERTEX_INDEX ), D3D11VertexBuffer::B_INDEXBUFFER, D3D11VertexBuffer::U_IMMUTABLE ); s_MeshManager->PutSkeletalData( m, meshData ); skeltalMeshData = s_MeshManager->GetSkeletalData( m ); } + mi->Vertices = skeltalMeshData->Vertices; + mi->Indices = skeltalMeshData->Indices; mi->MeshIndexBuffer = skeltalMeshData->MeshIndexBuffer; mi->MeshVertexBuffer = skeltalMeshData->MeshVertexBuffer; MeshInfo* bmi = new MeshInfo; - bmi->Indices = indices; - bmi->Vertices = bindPoseVertices; std::shared_ptr staticMeshData = s_MeshManager->GetStaticData( m ); if ( staticMeshData == nullptr ) { @@ -957,12 +958,16 @@ void WorldConverter::ExtractSkeletalMeshFromVob( zCModel* model, SkeletalMeshVis meshData->MeshIndexBuffer.reset( indexBuffer ); } - meshData->MeshVertexBuffer->Init( &bmi->Vertices[0], bmi->Vertices.size() * sizeof( ExVertexStruct ), D3D11VertexBuffer::B_VERTEXBUFFER, D3D11VertexBuffer::U_IMMUTABLE ); - meshData->MeshIndexBuffer->Init( &bmi->Indices[0], bmi->Indices.size() * sizeof( VERTEX_INDEX ), D3D11VertexBuffer::B_INDEXBUFFER, D3D11VertexBuffer::U_IMMUTABLE ); + meshData->Vertices = SharedVector{ std::make_shared>( bindPoseVertices ) }; + meshData->Indices = SharedVector{ std::make_shared>( indices ) }; + meshData->MeshVertexBuffer->Init( &meshData->Vertices[0], meshData->Vertices.size() * sizeof( ExVertexStruct ), D3D11VertexBuffer::B_VERTEXBUFFER, D3D11VertexBuffer::U_IMMUTABLE ); + meshData->MeshIndexBuffer->Init( &meshData->Indices[0], meshData->Indices.size() * sizeof( VERTEX_INDEX ), D3D11VertexBuffer::B_INDEXBUFFER, D3D11VertexBuffer::U_IMMUTABLE ); s_MeshManager->PutStaticData( m, meshData ); staticMeshData = s_MeshManager->GetStaticData( m ); } + bmi->Vertices = staticMeshData->Vertices; + bmi->Indices = staticMeshData->Indices; bmi->MeshIndexBuffer = staticMeshData->MeshIndexBuffer; bmi->MeshVertexBuffer = staticMeshData->MeshVertexBuffer; @@ -1055,8 +1060,6 @@ void WorldConverter::ExtractProgMeshProtoFromModel( zCModel* model, MeshVisualIn // Create the buffers and sort the mesh into the structure MeshInfo* mi = new MeshInfo; - mi->Vertices = vertices; - mi->Indices = indices; mi->MeshIndex = i; std::shared_ptr staticMeshData = s_MeshManager->GetStaticData( m ); @@ -1074,27 +1077,31 @@ void WorldConverter::ExtractProgMeshProtoFromModel( zCModel* model, MeshVisualIn } // Optimize faces - mi->MeshVertexBuffer->OptimizeFaces( &mi->Indices[0], - reinterpret_cast(&mi->Vertices[0]), - mi->Indices.size(), - mi->Vertices.size(), + mi->MeshVertexBuffer->OptimizeFaces( &indices[0], + reinterpret_cast(&vertices[0]), + indices.size(), + vertices.size(), sizeof( ExVertexStruct ) ); // Then optimize vertices - mi->MeshVertexBuffer->OptimizeVertices( &mi->Indices[0], - reinterpret_cast(&mi->Vertices[0]), - mi->Indices.size(), - mi->Vertices.size(), + mi->MeshVertexBuffer->OptimizeVertices( &indices[0], + reinterpret_cast(&vertices[0]), + indices.size(), + vertices.size(), sizeof( ExVertexStruct ) ); - meshData->MeshVertexBuffer->Init( &mi->Vertices[0], mi->Vertices.size() * sizeof( ExVertexStruct ), D3D11VertexBuffer::B_VERTEXBUFFER, D3D11VertexBuffer::U_IMMUTABLE ); - meshData->MeshIndexBuffer->Init( &mi->Indices[0], mi->Indices.size() * sizeof( VERTEX_INDEX ), D3D11VertexBuffer::B_INDEXBUFFER, D3D11VertexBuffer::U_IMMUTABLE ); + meshData->Vertices = SharedVector{ std::make_shared>( vertices ) }; + meshData->Indices = SharedVector{ std::make_shared>( indices ) }; + meshData->MeshVertexBuffer->Init( &meshData->Vertices[0], meshData->Vertices.size() * sizeof( ExVertexStruct ), D3D11VertexBuffer::B_VERTEXBUFFER, D3D11VertexBuffer::U_IMMUTABLE ); + meshData->MeshIndexBuffer->Init( &meshData->Indices[0], meshData->Indices.size() * sizeof( VERTEX_INDEX ), D3D11VertexBuffer::B_INDEXBUFFER, D3D11VertexBuffer::U_IMMUTABLE ); s_MeshManager->PutStaticData( m, meshData ); staticMeshData = s_MeshManager->GetStaticData( m ); } // Init and fill it + mi->Vertices = staticMeshData->Vertices; + mi->Indices = staticMeshData->Indices; mi->MeshVertexBuffer = staticMeshData->MeshVertexBuffer; mi->MeshIndexBuffer = staticMeshData->MeshIndexBuffer; @@ -1111,8 +1118,8 @@ void WorldConverter::ExtractProgMeshProtoFromModel( zCModel* model, MeshVisualIn meshInfo->MeshesByTexture[key].emplace_back( mi ); - vertexBuffers.emplace_back( &mi->Vertices ); - indexBuffers.emplace_back( &mi->Indices ); + vertexBuffers.emplace_back( mi->Vertices.data.get() ); + indexBuffers.emplace_back( mi->Indices.data.get() ); meshInfos.emplace_back( mi ); } } @@ -1192,8 +1199,8 @@ void WorldConverter::ExtractProgMeshProtoFromMesh( zCMesh* mesh, MeshVisualInfo* } MeshInfo* mi = new MeshInfo; - mi->Vertices = vertices; - mi->Indices = indices; + mi->Vertices = SharedVector{ std::make_shared>( vertices ) }; + mi->Indices = SharedVector{ std::make_shared>( indices ) }; // Create the buffers D3D11VertexBuffer* vertexBuffer; @@ -1351,82 +1358,54 @@ void WorldConverter::Extract3DSMeshFromVisual2( zCProgMeshProto* visual, MeshVis // Create the buffers and sort the mesh into the structure MeshInfo* mi = new MeshInfo; - mi->Vertices = vertices; - mi->Indices = indices; mi->MeshIndex = i; // Create the buffers - if ( meshInfo->MorphMeshVisual ) { - // We need to keep original indices so that we can reuse them(we can't optimize them) - // Use dynamic buffer since we'll reupload it every frame we see this visual - - std::shared_ptr staticMeshData = s_MeshManager->GetStaticData( s ); - if ( staticMeshData == nullptr ) { - auto meshData = new StaticMeshData; - - { - D3D11VertexBuffer* vertexBuffer; - Engine::GraphicsEngine->CreateVertexBuffer( &vertexBuffer ); - meshData->MeshVertexBuffer.reset( vertexBuffer ); - - D3D11VertexBuffer* indexBuffer; - Engine::GraphicsEngine->CreateVertexBuffer( &indexBuffer ); - meshData->MeshIndexBuffer.reset( indexBuffer ); - } + std::shared_ptr staticMeshData = s_MeshManager->GetStaticData( s ); + if ( staticMeshData == nullptr ) { + auto meshData = new StaticMeshData; - meshData->MeshVertexBuffer->Init( &vertices[0], vertices.size() * sizeof( ExVertexStruct ), D3D11VertexBuffer::B_VERTEXBUFFER, D3D11VertexBuffer::U_IMMUTABLE ); - meshData->MeshIndexBuffer->Init( &indices[0], indices.size() * sizeof( VERTEX_INDEX ), D3D11VertexBuffer::B_INDEXBUFFER, D3D11VertexBuffer::U_IMMUTABLE ); + { + D3D11VertexBuffer* vertexBuffer; + Engine::GraphicsEngine->CreateVertexBuffer( &vertexBuffer ); + meshData->MeshVertexBuffer.reset( vertexBuffer ); - s_MeshManager->PutStaticData( s, meshData ); - staticMeshData = s_MeshManager->GetStaticData( s ); + D3D11VertexBuffer* indexBuffer; + Engine::GraphicsEngine->CreateVertexBuffer( &indexBuffer ); + meshData->MeshIndexBuffer.reset( indexBuffer ); } - // Init and fill it - mi->MeshVertexBuffer = staticMeshData->MeshVertexBuffer; - mi->MeshIndexBuffer = staticMeshData->MeshIndexBuffer; - } else { - - std::shared_ptr staticMeshData = s_MeshManager->GetStaticData( s ); - if ( staticMeshData == nullptr ) { - auto meshData = new StaticMeshData; - - { - D3D11VertexBuffer* vertexBuffer; - Engine::GraphicsEngine->CreateVertexBuffer( &vertexBuffer ); - meshData->MeshVertexBuffer.reset( vertexBuffer ); - - D3D11VertexBuffer* indexBuffer; - Engine::GraphicsEngine->CreateVertexBuffer( &indexBuffer ); - meshData->MeshIndexBuffer.reset( indexBuffer ); - } - + if ( !meshInfo->MorphMeshVisual ) { // Optimize faces - mi->MeshVertexBuffer->OptimizeFaces(&mi->Indices[0], - reinterpret_cast(&mi->Vertices[0]), - mi->Indices.size(), - mi->Vertices.size(), + mi->MeshVertexBuffer->OptimizeFaces( &indices[0], + reinterpret_cast(&vertices[0]), + indices.size(), + vertices.size(), sizeof( ExVertexStruct ) ); // Then optimize vertices - mi->MeshVertexBuffer->OptimizeVertices( &mi->Indices[0], - reinterpret_cast(&mi->Vertices[0]), - mi->Indices.size(), - mi->Vertices.size(), + mi->MeshVertexBuffer->OptimizeVertices( &indices[0], + reinterpret_cast(&vertices[0]), + indices.size(), + vertices.size(), sizeof( ExVertexStruct ) ); - - meshData->MeshVertexBuffer->Init( &vertices[0], vertices.size() * sizeof( ExVertexStruct ), D3D11VertexBuffer::B_VERTEXBUFFER, D3D11VertexBuffer::U_IMMUTABLE ); - meshData->MeshIndexBuffer->Init( &indices[0], indices.size() * sizeof( VERTEX_INDEX ), D3D11VertexBuffer::B_INDEXBUFFER, D3D11VertexBuffer::U_IMMUTABLE ); - - s_MeshManager->PutStaticData( s, meshData ); - staticMeshData = s_MeshManager->GetStaticData( s ); } - // Init and fill it - mi->MeshVertexBuffer = staticMeshData->MeshVertexBuffer; - mi->MeshIndexBuffer = staticMeshData->MeshIndexBuffer; + meshData->MeshVertexBuffer->Init( &vertices[0], vertices.size() * sizeof( ExVertexStruct ), D3D11VertexBuffer::B_VERTEXBUFFER, D3D11VertexBuffer::U_IMMUTABLE ); + meshData->MeshIndexBuffer->Init( &indices[0], indices.size() * sizeof( VERTEX_INDEX ), D3D11VertexBuffer::B_INDEXBUFFER, D3D11VertexBuffer::U_IMMUTABLE ); + meshData->Vertices = SharedVector{ std::make_shared>( vertices ) }; + meshData->Indices = SharedVector{ std::make_shared>( indices ) }; + s_MeshManager->PutStaticData( s, meshData ); + staticMeshData = s_MeshManager->GetStaticData( s ); } + // Init and fill it + mi->MeshVertexBuffer = staticMeshData->MeshVertexBuffer; + mi->MeshIndexBuffer = staticMeshData->MeshIndexBuffer; + mi->Vertices = staticMeshData->Vertices; + mi->Indices = staticMeshData->Indices; + Engine::GAPI->GetRendererState().RendererInfo.VOBVerticesDataSize += mi->Vertices.size() * sizeof( ExVertexStruct ); Engine::GAPI->GetRendererState().RendererInfo.VOBVerticesDataSize += mi->Indices.size() * sizeof( VERTEX_INDEX ); @@ -1440,8 +1419,8 @@ void WorldConverter::Extract3DSMeshFromVisual2( zCProgMeshProto* visual, MeshVis meshInfo->MeshesByTexture[key].emplace_back( mi ); - vertexBuffers.emplace_back( &mi->Vertices ); - indexBuffers.emplace_back( &mi->Indices ); + vertexBuffers.emplace_back( mi->Vertices.data.get() ); + indexBuffers.emplace_back( mi->Indices.data.get() ); meshInfos.emplace_back( mi ); } diff --git a/D3D11Engine/WorldObjects.h b/D3D11Engine/WorldObjects.h index 78e33173..9cf520d4 100644 --- a/D3D11Engine/WorldObjects.h +++ b/D3D11Engine/WorldObjects.h @@ -80,12 +80,16 @@ struct cmpMeshKey { struct StaticMeshData { std::shared_ptr MeshVertexBuffer; std::shared_ptr MeshIndexBuffer; + SharedVector Vertices; + SharedVector Indices; }; // singleton data struct struct SkeletalMeshData { std::shared_ptr MeshVertexBuffer; std::shared_ptr MeshIndexBuffer; + SharedVector Vertices; + SharedVector Indices; }; /** Holds information about a mesh, ready to be loaded into the renderer */ @@ -109,8 +113,8 @@ struct MeshInfo { std::shared_ptr MeshVertexBuffer; std::shared_ptr MeshIndexBuffer; - std::vector Vertices; - std::vector Indices; + SharedVector Vertices; + SharedVector Indices; unsigned int BaseIndexLocation; unsigned int MeshIndex; @@ -155,8 +159,8 @@ struct SkeletalMeshInfo { std::shared_ptr MeshVertexBuffer; std::shared_ptr MeshIndexBuffer; - std::vector Vertices; - std::vector Indices; + SharedVector Vertices; + SharedVector Indices; /** Actual visual containing this */ zCMeshSoftSkin* visual; From c908b8d1fbd8694139f2e42c79775228743465a1 Mon Sep 17 00:00:00 2001 From: kirides <13602143+kirides@users.noreply.github.com> Date: Tue, 17 Feb 2026 21:15:47 +0100 Subject: [PATCH 12/13] fix zCOLOR usage --- D3D11Engine/D3D11GraphicsEngine.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/D3D11Engine/D3D11GraphicsEngine.cpp b/D3D11Engine/D3D11GraphicsEngine.cpp index 39311405..b725cc71 100644 --- a/D3D11Engine/D3D11GraphicsEngine.cpp +++ b/D3D11Engine/D3D11GraphicsEngine.cpp @@ -6132,7 +6132,7 @@ XRESULT D3D11GraphicsEngine::DrawVOBsInstanced() { /*// Apply colors for these meshes MaterialInfo::Buffer b; ZeroMemory(&b, sizeof(b)); - b.Color = itt->first.Material->GetColor(); + b.Color = itt->first.Material->GetColor().ToFloat4(); PS_Diffuse->GetConstantBuffer()[2]->UpdateBuffer(&b); PS_Diffuse->GetConstantBuffer()[2]->BindToPixelShader(2);*/ #endif @@ -6148,7 +6148,7 @@ XRESULT D3D11GraphicsEngine::DrawVOBsInstanced() { MaterialInfo::Buffer b = {}; - b.Color = itt.first.Material->GetColor(); + b.Color = itt.first.Material->GetColor().ToFloat4(); PS_DiffuseAlphatest->GetConstantBuffer()[2]->UpdateBuffer( &b ); PS_DiffuseAlphatest->GetConstantBuffer()[2]->BindToPixelShader( 2 ); From 4074c98445ddf793723312104821bf8226737154 Mon Sep 17 00:00:00 2001 From: kirides <13602143+kirides@users.noreply.github.com> Date: Mon, 23 Feb 2026 11:00:28 +0100 Subject: [PATCH 13/13] introduce Tab "Experimental" --- D3D11Engine/GothicAPI.cpp | 10 ++++------ D3D11Engine/GothicGraphicsState.h | 6 ++++++ D3D11Engine/ImGuiShim.cpp | 6 +++++- D3D11Engine/pch.h | 1 - 4 files changed, 15 insertions(+), 8 deletions(-) diff --git a/D3D11Engine/GothicAPI.cpp b/D3D11Engine/GothicAPI.cpp index 3997bbc7..ac7714d0 100644 --- a/D3D11Engine/GothicAPI.cpp +++ b/D3D11Engine/GothicAPI.cpp @@ -59,8 +59,6 @@ // Duration how long the scene will stay wet, in MS const DWORD SCENE_WETNESS_DURATION_MS = 30 * 1000; -bool UseExperimentalSkeletalBatching = false; - // Draw ghost from back to front of our camera auto CompareGhostDistance = []( TransparencyVobInfo& a, TransparencyVobInfo& b ) -> bool { return a.distance < b.distance; }; @@ -1189,13 +1187,13 @@ void GothicAPI::DrawWorldMeshNaive() { #if defined(BUILD_1_12F) // not implemented in G1 sequel - UseExperimentalSkeletalBatching = false; + RendererState.RendererSettings.Experimental.DrawSkeletalsInstanced = false; #endif // TODO: Figure out why Batching is not working properly here // The Batches constantly use wrong textures as if just before "DrawIndexed" the texture is being changed by something else // this happens on the Body meshes as well as all the Node Attachments. // We need this to be fixed, so that we can actually draw hundreds of animated characters with good performance. - if ( UseExperimentalSkeletalBatching ) { + if ( RendererState.RendererSettings.Experimental.DrawSkeletalsInstanced ) { static std::vector batchedVobs; batchedVobs.clear(); @@ -2945,10 +2943,10 @@ void GothicAPI::DrawSkeletalMeshVobs( #if defined(BUILD_1_12F) // not implemented in G1 sequel - UseExperimentalSkeletalBatching = false; + RendererState.RendererSettings.Experimental.DrawSkeletalsInstanced = false; #endif - if ( UseExperimentalSkeletalBatching ) { + if ( RendererState.RendererSettings.Experimental.DrawSkeletalsInstanced ) { DrawSkeletalMeshVobs_Batched( vis, updateState, drawAttachments ); } else { FXMVECTOR camPos = GetCameraPositionXM(); diff --git a/D3D11Engine/GothicGraphicsState.h b/D3D11Engine/GothicGraphicsState.h index 4463bcd0..6ab3237c 100644 --- a/D3D11Engine/GothicGraphicsState.h +++ b/D3D11Engine/GothicGraphicsState.h @@ -755,6 +755,8 @@ struct GothicRendererSettings { DebugSettings.Culling.CullBspSections = true; DebugSettings.Culling.CullVobs = true; DebugSettings.ShadowCascades.LazyCascadeUpdate = true; + + Experimental = {}; } void SetupOldWorldSpecificValues() { @@ -945,6 +947,10 @@ struct GothicRendererSettings { bool UseLayeredRendering; } FeatureSet; } DebugSettings; + + struct { + bool DrawSkeletalsInstanced; + } Experimental; }; struct GothicRendererTiming { diff --git a/D3D11Engine/ImGuiShim.cpp b/D3D11Engine/ImGuiShim.cpp index dab1ce7c..04c447ef 100644 --- a/D3D11Engine/ImGuiShim.cpp +++ b/D3D11Engine/ImGuiShim.cpp @@ -959,7 +959,6 @@ void RenderAdvancedColumn2( GothicRendererSettings& settings, GothicAPI* gapi ) ImGui::Checkbox( "Draw Skeletal Meshes", &settings.DrawSkeletalMeshes ); ImGui::BeginDisabled( !settings.DrawSkeletalMeshes ); - ImGui::Checkbox( "Enable Experimental Batching", &UseExperimentalSkeletalBatching ); ImGui::SliderFloat( "SkeletalMeshDrawRadius", &settings.SkeletalMeshDrawRadius, 0.0f, 18000.0f, "%.0f", ImGuiSliderFlags_::ImGuiSliderFlags_ClampOnInput ); ImGui::SetItemTooltip( "Draw distance for NPCs" ); ImGui::EndDisabled(); @@ -1185,6 +1184,11 @@ void RenderAdvancedColumn2( GothicRendererSettings& settings, GothicAPI* gapi ) ImGui::Checkbox("Use Layered Drawing", &settings.DebugSettings.FeatureSet.UseLayeredRendering ); ImGui::EndTabItem(); } + + if (ImGui::BeginTabItem("Experimental", nullptr, ImGuiTabItemFlags_::ImGuiTabItemFlags_NoReorder)) { + ImGui::Checkbox("Draw Skeletals Instanced", &settings.Experimental.DrawSkeletalsInstanced ); + ImGui::EndTabItem(); + } ImGui::EndTabBar(); } diff --git a/D3D11Engine/pch.h b/D3D11Engine/pch.h index b5c9bef7..b1c9338a 100644 --- a/D3D11Engine/pch.h +++ b/D3D11Engine/pch.h @@ -44,7 +44,6 @@ __declspec(selectany) const char* VERSION_NUMBER_STR = VERSION_NUMBER; extern bool FeatureLevel10Compatibility; extern bool GMPModeActive; -extern bool UseExperimentalSkeletalBatching; /** D3D7-Call logging */ #define DebugWriteValue(value, check) if (value == check) { LogInfo() << " - " << #check; }