From 025ba4419e44cd32132153db395c64443381c9f0 Mon Sep 17 00:00:00 2001 From: Robert Beckebans Date: Mon, 21 Feb 2022 19:21:16 +0100 Subject: [PATCH] Added more missing files --- neo/CMakeLists.txt | 23 +- neo/idlib/math/Vector.h | 22 + neo/idlib/precompiled.h | 8 +- neo/renderer/BindingCache.cpp | 159 ++++ neo/renderer/BindingCache.h | 70 ++ neo/renderer/BufferObject.h | 4 +- neo/renderer/GuiModel.cpp | 69 +- neo/renderer/GuiModel.h | 8 +- neo/renderer/Image.h | 13 +- neo/renderer/ImageManager.cpp | 12 + neo/renderer/Image_load.cpp | 111 +++ neo/renderer/ModelOverlay.cpp | 6 +- neo/renderer/NVRHI/RenderDebug_NVRHI.cpp | 6 +- neo/renderer/Passes/CommonPasses.cpp | 285 +++++++ neo/renderer/Passes/CommonPasses.h | 142 ++++ neo/renderer/Passes/FowardShadingPass.cpp | 855 ++++++++++++++++++++ neo/renderer/Passes/FowardShadingPass.h | 68 ++ neo/renderer/Passes/GBufferFillPass.cpp | 683 ++++++++++++++++ neo/renderer/Passes/GBufferFillPass.h | 64 ++ neo/renderer/Passes/GeometryPasses.cpp | 804 ++++++++++++++++++ neo/renderer/Passes/GeometryPasses.h | 95 +++ neo/renderer/Passes/MipMapGenPass.cpp | 278 +++++++ neo/renderer/Passes/MipMapGenPass.h | 80 ++ neo/renderer/Passes/SsaoPass.cpp | 280 +++++++ neo/renderer/Passes/SsaoPass.h | 100 +++ neo/renderer/RenderBackend.h | 3 +- neo/renderer/RenderCommon.h | 20 + neo/renderer/RenderPass.cpp | 350 ++++++++ neo/renderer/RenderPass.h | 71 ++ neo/renderer/RenderSystem.cpp | 81 +- neo/renderer/RenderSystem.h | 2 +- neo/renderer/RenderWorld.cpp | 11 +- neo/renderer/RenderWorld_lightgrid.cpp | 17 + neo/tools/imgui/lighteditor/LightEditor.cpp | 2 +- 34 files changed, 4739 insertions(+), 63 deletions(-) create mode 100644 neo/renderer/BindingCache.cpp create mode 100644 neo/renderer/BindingCache.h create mode 100644 neo/renderer/Passes/CommonPasses.cpp create mode 100644 neo/renderer/Passes/CommonPasses.h create mode 100644 neo/renderer/Passes/FowardShadingPass.cpp create mode 100644 neo/renderer/Passes/FowardShadingPass.h create mode 100644 neo/renderer/Passes/GBufferFillPass.cpp create mode 100644 neo/renderer/Passes/GBufferFillPass.h create mode 100644 neo/renderer/Passes/GeometryPasses.cpp create mode 100644 neo/renderer/Passes/GeometryPasses.h create mode 100644 neo/renderer/Passes/MipMapGenPass.cpp create mode 100644 neo/renderer/Passes/MipMapGenPass.h create mode 100644 neo/renderer/Passes/SsaoPass.cpp create mode 100644 neo/renderer/Passes/SsaoPass.h create mode 100644 neo/renderer/RenderPass.cpp create mode 100644 neo/renderer/RenderPass.h diff --git a/neo/CMakeLists.txt b/neo/CMakeLists.txt index 6530ef0f..9b70fabd 100644 --- a/neo/CMakeLists.txt +++ b/neo/CMakeLists.txt @@ -487,6 +487,7 @@ if(USE_NVRHI) set(NVRHI_DIR ${CMAKE_CURRENT_SOURCE_DIR}/extern/nvrhi) add_subdirectory(${NVRHI_DIR}) + add_definitions(-DUSE_NVRHI) if(USE_DX11) find_package(FXC REQUIRED) @@ -557,17 +558,17 @@ file(GLOB RENDERER_VULKAN_SOURCES renderer/Vulkan/*.cpp) file(GLOB RENDERER_NVRHI_INCLUDES renderer/NVRHI/*.h) file(GLOB RENDERER_NVRHI_SOURCES renderer/NVRHI/*.cpp) -file(GLOB SHADERS ../base/renderprogs/*.hlsl ../base/renderprogs/*.inc ) -file(GLOB SHADERS_BUILTIN ../base/renderprogs/builtin/*.hlsl ) -file(GLOB SHADERS_BUILTIN_DEBUG ../base/renderprogs/builtin/debug/*.hlsl ) -file(GLOB SHADERS_BUILTIN_FOG ../base/renderprogs/builtin/fog/*.hlsl ) -file(GLOB SHADERS_BUILTIN_LEGACY ../base/renderprogs/builtin/legacy/*.hlsl ) -file(GLOB SHADERS_BUILTIN_LIGHTING ../base/renderprogs/builtin/lighting/*.hlsl ) -file(GLOB SHADERS_BUILTIN_POST ../base/renderprogs/builtin/post/*.hlsl ) -file(GLOB SHADERS_BUILTIN_SSAO ../base/renderprogs/builtin/SSAO/*.hlsl ) -file(GLOB SHADERS_BUILTIN_SSGI ../base/renderprogs/builtin/SSGI/*.hlsl ) -file(GLOB SHADERS_BUILTIN_VIDEO ../base/renderprogs/builtin/video/*.hlsl ) -file(GLOB SHADERS_BUILTIN_VR ../base/renderprogs/builtin/VR/*.hlsl ) +file(GLOB SHADERS shaders/*.hlsl shaders/*.inc ) +file(GLOB SHADERS_BUILTIN shaders/builtin/*.hlsl ) +file(GLOB SHADERS_BUILTIN_DEBUG shaders/builtin/debug/*.hlsl ) +file(GLOB SHADERS_BUILTIN_FOG shaders/builtin/fog/*.hlsl ) +file(GLOB SHADERS_BUILTIN_LEGACY .shaders/builtin/legacy/*.hlsl ) +file(GLOB SHADERS_BUILTIN_LIGHTING shaders/builtin/lighting/*.hlsl ) +file(GLOB SHADERS_BUILTIN_POST shaders/builtin/post/*.hlsl ) +file(GLOB SHADERS_BUILTIN_SSAO shaders/builtin/SSAO/*.hlsl ) +file(GLOB SHADERS_BUILTIN_SSGI shaders/builtin/SSGI/*.hlsl ) +file(GLOB SHADERS_BUILTIN_VIDEO shaders/builtin/video/*.hlsl ) +file(GLOB SHADERS_BUILTIN_VR shaders/builtin/VR/*.hlsl ) file(GLOB IRRXML_INCLUDES libs/irrxml/src/*.h) file(GLOB IRRXML_SOURCES libs/irrxml/src/*.cpp) diff --git a/neo/idlib/math/Vector.h b/neo/idlib/math/Vector.h index 61e87b2a..aa20651b 100644 --- a/neo/idlib/math/Vector.h +++ b/neo/idlib/math/Vector.h @@ -357,6 +357,12 @@ ID_INLINE float* idVec2::ToFloatPtr() return &x; } +ID_INLINE idVec2& operator/( float lhs, idVec2& rhs ) +{ + rhs.x = lhs / rhs.x; + rhs.y = lhs / rhs.y; + return rhs; +} //=============================================================== // @@ -963,6 +969,14 @@ ID_INLINE bool idVec3::ProjectAlongPlane( const idVec3& normal, const float epsi return true; } +ID_INLINE idVec3& operator/( float lhs, idVec3& rhs ) +{ + rhs.x = rhs.x / lhs; + rhs.y = rhs.y / lhs; + rhs.z = rhs.z / lhs; + return rhs; +} + //=============================================================== // // idTupleSize< idVec3 > - Specialization to get the size @@ -1757,6 +1771,14 @@ ID_INLINE idVec3 idPolar3::ToVec3() const return idVec3( cp * radius * ct, cp * radius * st, radius * sp ); } +namespace VectorUtil +{ +inline uint32_t Vec4ToColorInt( const idVec4& vec ) +{ + idVec4 vecCopy = 255.0f * vec; + return ( ( uint32_t )vecCopy[0] << 28 ) | ( ( uint32_t )vecCopy[1] << 20 ) | ( ( uint32_t )vecCopy[2] << 12 ) | ( uint32_t )vecCopy[3]; +} +} /* =============================================================================== diff --git a/neo/idlib/precompiled.h b/neo/idlib/precompiled.h index c02d9a7e..60a74612 100644 --- a/neo/idlib/precompiled.h +++ b/neo/idlib/precompiled.h @@ -99,10 +99,13 @@ const int MAX_EXPRESSION_REGISTERS = 4096; #elif defined(USE_VULKAN) #include "../renderer/Vulkan/qvk.h" #else - // RB: replaced QGL with GLEW + +#endif + +// FIMXE remove GL with NVRHI +// RB: replaced QGL with GLEW #include // RB end -#endif #include "../renderer/Cinematic.h" @@ -113,6 +116,7 @@ const int MAX_EXPRESSION_REGISTERS = 4096; #include "../renderer/ModelManager.h" #include "../renderer/RenderSystem.h" #include "../renderer/RenderWorld.h" +#include "../renderer/BindingCache.h" // sound engine #include "../sound/sound.h" diff --git a/neo/renderer/BindingCache.cpp b/neo/renderer/BindingCache.cpp new file mode 100644 index 00000000..5f01ae24 --- /dev/null +++ b/neo/renderer/BindingCache.cpp @@ -0,0 +1,159 @@ +#include "precompiled.h" +#pragma hdrstop + +#include "BindingCache.h" + +void BindingCache::Init( nvrhi::IDevice* _device ) +{ + device = _device; +} + +nvrhi::BindingSetHandle BindingCache::GetCachedBindingSet( const nvrhi::BindingSetDesc& desc, nvrhi::IBindingLayout* layout ) +{ + size_t hash = 0; + nvrhi::hash_combine( hash, desc ); + nvrhi::hash_combine( hash, layout ); + + mutex.Lock(); + + nvrhi::BindingSetHandle result = nullptr; + for( int i = bindingHash.First( hash ); i != -1; i = bindingHash.Next( i ) ) + { + nvrhi::BindingSetHandle bindingSet = bindingSets[i]; + if( *bindingSet->getDesc() == desc ) + { + result = bindingSet; + break; + } + } + + mutex.Unlock(); + + if( result ) + { + assert( result->getDesc() && *result->getDesc() == desc ); + } + + return result; +} + +nvrhi::BindingSetHandle BindingCache::GetOrCreateBindingSet( const nvrhi::BindingSetDesc& desc, nvrhi::IBindingLayout* layout ) +{ + size_t hash = 0; + nvrhi::hash_combine( hash, desc ); + nvrhi::hash_combine( hash, layout ); + + mutex.Lock(); + + nvrhi::BindingSetHandle result = nullptr; + for( int i = bindingHash.First( hash ); i != -1; i = bindingHash.Next( i ) ) + { + nvrhi::BindingSetHandle bindingSet = bindingSets[i]; + if( *bindingSet->getDesc() == desc ) + { + result = bindingSet; + break; + } + } + + mutex.Unlock(); + + if( !result ) + { + mutex.Lock(); + + int entryIndex = bindingSets.Append( result ); + bindingHash.Add( hash, entryIndex ); + + nvrhi::BindingSetHandle& entry = bindingSets[entryIndex]; + + if( !entry ) + { + result = device->createBindingSet( desc, layout ); + entry = result; + } + else + { + result = entry; + } + + mutex.Unlock(); + } + + if( result ) + { + assert( result->getDesc() && *result->getDesc() == desc ); + } + + return result; +} + +void BindingCache::Clear() +{ + mutex.Lock(); + bindingSets.Clear(); + bindingHash.Clear(); + mutex.Unlock(); +} + +void SamplerCache::Init( nvrhi::IDevice* _device ) +{ + device = _device; +} + +void SamplerCache::Clear() +{ + mutex.Lock(); + samplers.Clear(); + samplerHash.Clear(); + mutex.Unlock(); +} + +nvrhi::SamplerHandle SamplerCache::GetOrCreateSampler( nvrhi::SamplerDesc desc ) +{ + size_t hash = std::hash {}( desc ); + + mutex.Lock(); + + nvrhi::SamplerHandle result = nullptr; + for( int i = samplerHash.First( hash ); i != -1; i = samplerHash.Next( i ) ) + { + nvrhi::SamplerHandle sampler = samplers[i]; + if( sampler->getDesc() == desc ) + { + result = sampler; + break; + } + } + + mutex.Unlock(); + + if( !result ) + { + mutex.Lock(); + + int entryIndex = samplers.Append( result ); + samplerHash.Add( hash, entryIndex ); + + nvrhi::SamplerHandle& entry = samplers[entryIndex]; + + if( !entry ) + { + result = device->createSampler( desc ); + entry = result; + } + else + { + result = entry; + } + + mutex.Unlock(); + } + + if( result ) + { + assert( result->getDesc() == desc ); + } + + return result; +} diff --git a/neo/renderer/BindingCache.h b/neo/renderer/BindingCache.h new file mode 100644 index 00000000..d3710f8c --- /dev/null +++ b/neo/renderer/BindingCache.h @@ -0,0 +1,70 @@ +/* +=========================================================================== + +Doom 3 BFG Edition GPL Source Code +Copyright (C) 2022 Stephen Pridham + +This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). + +Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 BFG Edition Source Code. If not, see . + +In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ +#ifndef RENDERER_BINDING_CACHE_H_ +#define RENDERER_BINDING_CACHE_H_ + +#include + +class BindingCache +{ +public: + BindingCache() {} + + void Init( nvrhi::IDevice* _device ); + void Clear(); + + nvrhi::BindingSetHandle GetCachedBindingSet( const nvrhi::BindingSetDesc& desc, nvrhi::IBindingLayout* layout ); + nvrhi::BindingSetHandle GetOrCreateBindingSet( const nvrhi::BindingSetDesc& desc, nvrhi::IBindingLayout* layout ); + +private: + nvrhi::IDevice* device; + idList bindingSets; + idHashIndex bindingHash; + idSysMutex mutex; +}; + +class SamplerCache +{ +public: + + SamplerCache() {} + + void Init( nvrhi::IDevice* _device ); + void Clear(); + + nvrhi::SamplerHandle GetOrCreateSampler( nvrhi::SamplerDesc samplerDesc ); + +private: + + nvrhi::IDevice* device; + idList samplers; + idHashIndex samplerHash; + idSysMutex mutex; +}; + +#endif \ No newline at end of file diff --git a/neo/renderer/BufferObject.h b/neo/renderer/BufferObject.h index 6e6a79e9..5e08d82f 100644 --- a/neo/renderer/BufferObject.h +++ b/neo/renderer/BufferObject.h @@ -135,11 +135,11 @@ protected: #else vulkanAllocation_t allocation; #endif + +#elif defined( USE_NVRHI ) nvrhi::InputLayoutHandle inputLayout; nvrhi::BufferHandle bufferHandle; void* buffer; -#elif defined( USE_NVRHI ) - #else // GL GLintptr bufferHandle; diff --git a/neo/renderer/GuiModel.cpp b/neo/renderer/GuiModel.cpp index e81ad394..f4e7896a 100644 --- a/neo/renderer/GuiModel.cpp +++ b/neo/renderer/GuiModel.cpp @@ -87,10 +87,10 @@ void idGuiModel::ReadFromDemo( idDemoFile* demo ) idGuiModel::BeginFrame ================ */ -void idGuiModel::BeginFrame() +void idGuiModel::BeginFrame( nvrhi::ICommandList* commandList ) { - vertexBlock = vertexCache.AllocVertex( NULL, MAX_VERTS ); - indexBlock = vertexCache.AllocIndex( NULL, MAX_INDEXES ); + vertexBlock = vertexCache.AllocVertex( NULL, MAX_VERTS, sizeof( idDrawVert ), commandList ); + indexBlock = vertexCache.AllocIndex( NULL, MAX_INDEXES, sizeof( idTriList ), commandList ); vertexPointer = ( idDrawVert* )vertexCache.MappedVertexBuffer( vertexBlock ); indexPointer = ( triIndex_t* )vertexCache.MappedIndexBuffer( indexBlock ); numVerts = 0; @@ -180,6 +180,7 @@ void idGuiModel::EmitSurfaces( float modelMatrix[16], float modelViewMatrix[16], drawSurf->shaderRegisters = regs; shader->EvaluateRegisters( regs, shaderParms, tr.viewDef->renderView.shaderParms, tr.viewDef->renderView.time[1] * 0.001f, NULL ); } + R_LinkDrawSurfToView( drawSurf, tr.viewDef ); if( allowFullScreenStereoDepth ) { @@ -233,7 +234,7 @@ idGuiModel::EmitFullScreen Creates a view that covers the screen and emit the surfaces ================ */ -void idGuiModel::EmitFullScreen() +void idGuiModel::EmitFullScreen( textureStage_t* textureStage ) { if( surfaces[0].numIndexes == 0 ) { @@ -244,7 +245,19 @@ void idGuiModel::EmitFullScreen() viewDef_t* viewDef = ( viewDef_t* )R_ClearedFrameAlloc( sizeof( *viewDef ), FRAME_ALLOC_VIEW_DEF ); viewDef->is2Dgui = true; - tr.GetCroppedViewport( &viewDef->viewport ); + + if( textureStage ) + { + viewDef->targetRender = globalFramebuffers.glowFBO[0]; + viewDef->viewport.x1 = 0; + viewDef->viewport.y1 = 0; + viewDef->viewport.x2 = textureStage->width; + viewDef->viewport.y2 = textureStage->height; + } + else + { + tr.GetCroppedViewport( &viewDef->viewport ); + } bool stereoEnabled = ( renderSystem->GetStereo3DMode() != STEREO3D_OFF ); if( stereoEnabled ) @@ -262,38 +275,55 @@ void idGuiModel::EmitFullScreen() } } + idVec2 screenSize( renderSystem->GetVirtualWidth(), renderSystem->GetVirtualHeight() ); + + if( textureStage ) + { + screenSize.x = textureStage->width; + screenSize.y = textureStage->height; + } + + float xScale = 1.0f / screenSize.x; +#if defined( USE_VULKAN ) || defined( USE_NVRHI ) + float yScale = -1.0f / screenSize.y; // flip y +#else + float yScale = 1.0f / screenSize.y; +#endif + float zScale = -1.0f; + viewDef->scissor.x1 = 0; viewDef->scissor.y1 = 0; viewDef->scissor.x2 = viewDef->viewport.x2 - viewDef->viewport.x1; viewDef->scissor.y2 = viewDef->viewport.y2 - viewDef->viewport.y1; // RB: IMPORTANT - the projectionMatrix has a few changes to make it work with Vulkan - viewDef->projectionMatrix[0 * 4 + 0] = 2.0f / renderSystem->GetVirtualWidth(); + viewDef->projectionMatrix[0 * 4 + 0] = 2.f * xScale; viewDef->projectionMatrix[0 * 4 + 1] = 0.0f; viewDef->projectionMatrix[0 * 4 + 2] = 0.0f; viewDef->projectionMatrix[0 * 4 + 3] = 0.0f; viewDef->projectionMatrix[1 * 4 + 0] = 0.0f; -#if defined(USE_VULKAN) - viewDef->projectionMatrix[1 * 4 + 1] = 2.0f / renderSystem->GetVirtualHeight(); -#else - viewDef->projectionMatrix[1 * 4 + 1] = -2.0f / renderSystem->GetVirtualHeight(); -#endif + viewDef->projectionMatrix[1 * 4 + 1] = 2.f * yScale; viewDef->projectionMatrix[1 * 4 + 2] = 0.0f; viewDef->projectionMatrix[1 * 4 + 3] = 0.0f; viewDef->projectionMatrix[2 * 4 + 0] = 0.0f; viewDef->projectionMatrix[2 * 4 + 1] = 0.0f; - viewDef->projectionMatrix[2 * 4 + 2] = -1.0f; + viewDef->projectionMatrix[2 * 4 + 2] = zScale; viewDef->projectionMatrix[2 * 4 + 3] = 0.0f; +#if defined( USE_NVRHI ) + viewDef->projectionMatrix[3 * 4 + 0] = -( screenSize.x * xScale ); + viewDef->projectionMatrix[3 * 4 + 1] = -( screenSize.y * yScale ); +#else viewDef->projectionMatrix[3 * 4 + 0] = -1.0f; // RB: was -2.0f #if defined(USE_VULKAN) viewDef->projectionMatrix[3 * 4 + 1] = -1.0f; #else viewDef->projectionMatrix[3 * 4 + 1] = 1.0f; #endif - viewDef->projectionMatrix[3 * 4 + 2] = 0.0f; // RB: was 1.0f +#endif + viewDef->projectionMatrix[3 * 4 + 2] = 0.0f; viewDef->projectionMatrix[3 * 4 + 3] = 1.0f; // make a tech5 renderMatrix for faster culling @@ -322,6 +352,8 @@ void idGuiModel::EmitFullScreen() #endif viewDef_t* oldViewDef = tr.viewDef; + viewDef->superView = oldViewDef; + tr.viewDef = viewDef; EmitSurfaces( viewDef->worldSpace.modelMatrix, viewDef->worldSpace.modelViewMatrix, @@ -330,6 +362,17 @@ void idGuiModel::EmitFullScreen() tr.viewDef = oldViewDef; // add the command to draw this view + if( textureStage ) + { + textureStage->dynamicFrameCount = tr.frameCount; + + viewDef->targetRender = globalFramebuffers.glowFBO[0]; + //if (textureStage->image == NULL) + //{ + // textureStage->image = globalImages->glowImage[0]; + //} + } + R_AddDrawViewCmd( viewDef, true ); } diff --git a/neo/renderer/GuiModel.h b/neo/renderer/GuiModel.h index 67a29718..ff99bd8f 100644 --- a/neo/renderer/GuiModel.h +++ b/neo/renderer/GuiModel.h @@ -43,6 +43,8 @@ namespace ImGui struct ImDrawData; } +struct ImDrawData; + class idGuiModel { public: @@ -54,10 +56,11 @@ public: void ReadFromDemo( idDemoFile* demo ); // allocates memory for verts and indexes in frame-temporary buffer memory - void BeginFrame(); + void BeginFrame( nvrhi::ICommandList* commandList ); void EmitToCurrentView( float modelMatrix[16], bool depthHack ); - void EmitFullScreen(); + void EmitFullScreen( textureStage_t* textureStage = nullptr ); + void EmitSurfaces( float modelMatrix[16], float modelViewMatrix[16], bool depthHack, bool allowFullScreenStereoDepth, bool linkAsEntity ); // RB void EmitImGui( ImDrawData* drawData ); @@ -70,7 +73,6 @@ public: //--------------------------- private: void AdvanceSurf(); - void EmitSurfaces( float modelMatrix[16], float modelViewMatrix[16], bool depthHack, bool allowFullScreenStereoDepth, bool linkAsEntity ); guiModelSurface_t* surf; diff --git a/neo/renderer/Image.h b/neo/renderer/Image.h index 517fd793..5255339f 100644 --- a/neo/renderer/Image.h +++ b/neo/renderer/Image.h @@ -465,18 +465,7 @@ public: // DG: added for imgui integration (to be used with ImGui::Image() etc) void* GetImGuiTextureID() { - if( !IsLoaded() ) - { - // load the image on demand here, which isn't our normal game operating mode - ActuallyLoadImage( true ); - } - -#if defined( USE_VULKAN ) - return ( void* )( intptr_t )image; -#else - return ( void* )( intptr_t )texnum; -#endif - + return nullptr; } // DG end diff --git a/neo/renderer/ImageManager.cpp b/neo/renderer/ImageManager.cpp index 43005a4f..b2cd0db8 100644 --- a/neo/renderer/ImageManager.cpp +++ b/neo/renderer/ImageManager.cpp @@ -72,7 +72,15 @@ void R_ReloadImages_f( const idCmdArgs& args ) } } +#if defined( USE_NVRHI ) + nvrhi::CommandListHandle commandList = deviceManager->GetDevice()->createCommandList(); + commandList->open(); + globalImages->ReloadImages( all, commandList ); + commandList->close(); + deviceManager->GetDevice()->executeCommandList( commandList ); +#else globalImages->ReloadImages( all ); +#endif } typedef struct @@ -469,7 +477,11 @@ idImage* idImageManager::ImageFromFile( const char* _name, textureFilter_t filte image->levelLoadReferenced = true; // load it if we aren't in a level preload +#if defined( USE_NVRHI ) + if( !insideLevelLoad || preloadingMapImages ) +#else if( ( !insideLevelLoad || preloadingMapImages ) && idLib::IsMainThread() ) +#endif { image->referencedOutsideLevelLoad = ( !insideLevelLoad && !preloadingMapImages ); image->FinalizeImage( false, nullptr ); diff --git a/neo/renderer/Image_load.cpp b/neo/renderer/Image_load.cpp index 341e32e7..034a5dc4 100644 --- a/neo/renderer/Image_load.cpp +++ b/neo/renderer/Image_load.cpp @@ -990,12 +990,35 @@ void idImage::GenerateImage( const byte* pic, int width, int height, textureFilt AllocImage(); +#if defined( USE_NVRHI ) + if( commandList ) + { + const nvrhi::FormatInfo& info = nvrhi::getFormatInfo( texture->getDesc().format ); + const int bytesPerBlock = info.bytesPerBlock; + + commandList->beginTrackingTextureState( texture, nvrhi::AllSubresources, nvrhi::ResourceStates::Common ); + + for( int i = 0; i < im.NumImages(); i++ ) + { + const bimageImage_t& img = im.GetImageHeader( i ); + const byte* data = im.GetImageData( i ); + int rowPitch = GetRowPitch( opts.format, img.width ); + commandList->writeTexture( texture, img.destZ, img.level, data, rowPitch ); + } + + commandList->setPermanentTextureState( texture, nvrhi::ResourceStates::ShaderResource ); + commandList->commitBarriers(); + } +#else for( int i = 0; i < im.NumImages(); i++ ) { const bimageImage_t& img = im.GetImageHeader( i ); const byte* data = im.GetImageData( i ); SubImageUpload( img.level, 0, 0, img.destZ, img.width, img.height, data ); } +#endif + + isLoaded = true; } // RB end } @@ -1050,12 +1073,44 @@ void idImage::GenerateCubeImage( const byte* pic[6], int size, textureFilter_t f AllocImage(); +#if defined( USE_NVRHI ) + int numChannels = 4; + int bytesPerPixel = numChannels; + if( opts.format == FMT_ALPHA || opts.format == FMT_DXT1 || opts.format == FMT_INT8 || opts.format == FMT_R8 ) + { + bytesPerPixel = 1; + } + + const nvrhi::FormatInfo& info = nvrhi::getFormatInfo( texture->getDesc().format ); + bytesPerPixel = info.bytesPerBlock; + + commandList->beginTrackingTextureState( texture, nvrhi::AllSubresources, nvrhi::ResourceStates::Common ); + + for( int i = 0; i < im.NumImages(); i++ ) + { + const bimageImage_t& img = im.GetImageHeader( i ); + const byte* data = im.GetImageData( i ); + int bufferW = img.width; + if( IsCompressed() ) + { + bufferW = ( img.width + 3 ) & ~3; + } + commandList->writeTexture( texture, 0, img.level, data, GetRowPitch( opts.format, img.width ) ); + } + + commandList->setPermanentTextureState( texture, nvrhi::ResourceStates::ShaderResource ); + commandList->commitBarriers(); +#else + for( int i = 0; i < im.NumImages(); i++ ) { const bimageImage_t& img = im.GetImageHeader( i ); const byte* data = im.GetImageData( i ); SubImageUpload( img.level, 0, 0, img.destZ, img.width, img.height, data ); } +#endif + + isLoaded = true; } // RB begin @@ -1118,11 +1173,41 @@ void idImage::UploadScratch( const byte* data, int cols, int rows, nvrhi::IComma AllocImage(); } + +#if defined( USE_NVRHI ) + int numChannels = 4; + int bytesPerPixel = numChannels; + if( opts.format == FMT_ALPHA || opts.format == FMT_DXT1 || opts.format == FMT_INT8 || opts.format == FMT_R8 ) + { + bytesPerPixel = 1; + } + + const nvrhi::FormatInfo& info = nvrhi::getFormatInfo( texture->getDesc().format ); + bytesPerPixel = info.bytesPerBlock; + SetSamplerState( TF_LINEAR, TR_CLAMP ); + + commandList->beginTrackingTextureState( texture, nvrhi::AllSubresources, nvrhi::ResourceStates::Common ); + + int bufferW = opts.width; + if( IsCompressed() ) + { + bufferW = ( opts.width + 3 ) & ~3; + } + + for( int i = 0; i < 6; i++ ) + { + commandList->writeTexture( texture, i, 0, pic[i], GetRowPitch( opts.format, opts.width ) ); + } + + commandList->setPermanentTextureState( texture, nvrhi::ResourceStates::ShaderResource ); + commandList->commitBarriers(); +#else for( int i = 0; i < 6; i++ ) { SubImageUpload( 0, 0, 0, i, opts.width, opts.height, pic[i] ); } +#endif } else { @@ -1139,8 +1224,34 @@ void idImage::UploadScratch( const byte* data, int cols, int rows, nvrhi::IComma AllocImage(); } + +#if defined( USE_NVRHI ) + int numChannels = 4; + int bytesPerPixel = numChannels; + if( opts.format == FMT_ALPHA || opts.format == FMT_DXT1 || opts.format == FMT_INT8 || opts.format == FMT_R8 ) + { + bytesPerPixel = 1; + } + + const nvrhi::FormatInfo& info = nvrhi::getFormatInfo( texture->getDesc().format ); + bytesPerPixel = info.bytesPerBlock; + + SetSamplerState( TF_LINEAR, TR_REPEAT ); + + int bufferW = opts.width; + if( IsCompressed() ) + { + bufferW = ( opts.width + 3 ) & ~3; + } + + commandList->beginTrackingTextureState( texture, nvrhi::AllSubresources, nvrhi::ResourceStates::Common ); + commandList->writeTexture( texture, 0, 0, data, GetRowPitch( opts.format, opts.width ) ); + commandList->setPermanentTextureState( texture, nvrhi::ResourceStates::ShaderResource ); + commandList->commitBarriers(); +#else SetSamplerState( TF_LINEAR, TR_REPEAT ); SubImageUpload( 0, 0, 0, 0, opts.width, opts.height, data ); +#endif } isLoaded = true; diff --git a/neo/renderer/ModelOverlay.cpp b/neo/renderer/ModelOverlay.cpp index 3439f63e..4e263abd 100644 --- a/neo/renderer/ModelOverlay.cpp +++ b/neo/renderer/ModelOverlay.cpp @@ -706,8 +706,8 @@ drawSurf_t* idRenderModelOverlay::CreateOverlayDrawSurf( const viewEntity_t* spa srfTriangles_t* newTri = ( srfTriangles_t* )R_ClearedFrameAlloc( sizeof( *newTri ), FRAME_ALLOC_SURFACE_TRIANGLES ); newTri->staticModelWithJoints = ( staticModel->jointsInverted != NULL ) ? const_cast< idRenderModelStatic* >( staticModel ) : NULL; // allow GPU skinning - newTri->ambientCache = vertexCache.AllocVertex( NULL, maxVerts ); - newTri->indexCache = vertexCache.AllocIndex( NULL, maxIndexes ); + newTri->ambientCache = vertexCache.AllocVertex( NULL, maxVerts, sizeof( idDrawVert ), nullptr ); + newTri->indexCache = vertexCache.AllocIndex( NULL, maxIndexes, sizeof( idDrawVert ), nullptr ); idDrawVert* mappedVerts = ( idDrawVert* )vertexCache.MappedVertexBuffer( newTri->ambientCache ); triIndex_t* mappedIndexes = ( triIndex_t* )vertexCache.MappedIndexBuffer( newTri->indexCache ); @@ -793,7 +793,7 @@ drawSurf_t* idRenderModelOverlay::CreateOverlayDrawSurf( const viewEntity_t* spa drawSurf->renderZFail = 0; R_SetupDrawSurfShader( drawSurf, material, &space->entityDef->parms ); - R_SetupDrawSurfJoints( drawSurf, newTri, NULL ); + R_SetupDrawSurfJoints( drawSurf, newTri, NULL, nullptr ); return drawSurf; } diff --git a/neo/renderer/NVRHI/RenderDebug_NVRHI.cpp b/neo/renderer/NVRHI/RenderDebug_NVRHI.cpp index fbc5e168..7dc31049 100644 --- a/neo/renderer/NVRHI/RenderDebug_NVRHI.cpp +++ b/neo/renderer/NVRHI/RenderDebug_NVRHI.cpp @@ -882,9 +882,9 @@ void idRenderBackend::DBG_ShowSurfaceInfo( drawSurf_t** drawSurfs, int numDrawSu // transform the object verts into global space // R_AxisToModelMatrix( mt.entity->axis, mt.entity->origin, matrix ); - tr.primaryWorld->DebugText( surfModelName, surfPoint + tr.primaryView->renderView.viewaxis[2] * 12, + tr.primaryWorld->DrawText( surfModelName, surfPoint + tr.primaryView->renderView.viewaxis[2] * 12, 0.35f, colorRed, tr.primaryView->renderView.viewaxis ); - tr.primaryWorld->DebugText( surfMatName, surfPoint, + tr.primaryWorld->DrawText( surfMatName, surfPoint, 0.35f, colorBlue, tr.primaryView->renderView.viewaxis ); } @@ -956,7 +956,7 @@ void idRenderBackend::DBG_ShowViewEntitys( viewEntity_t* vModels ) idVec3 corner; R_LocalPointToGlobal( vModel->modelMatrix, edef->localReferenceBounds[1], corner ); - tr.primaryWorld->DebugText( + tr.primaryWorld->DrawText( va( "%i:%s", edef->index, edef->parms.hModel->Name() ), corner, 0.25f, color, diff --git a/neo/renderer/Passes/CommonPasses.cpp b/neo/renderer/Passes/CommonPasses.cpp new file mode 100644 index 00000000..26e83656 --- /dev/null +++ b/neo/renderer/Passes/CommonPasses.cpp @@ -0,0 +1,285 @@ +/* +=========================================================================== + +Doom 3 BFG Edition GPL Source Code +Copyright (C) 2022 Stephen Pridham + +This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). + +Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 BFG Edition Source Code. If not, see . + +In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ +#include "precompiled.h" +#pragma hdrstop + +#include "renderer/RenderCommon.h" +#include "CommonPasses.h" + + +CommonRenderPasses::CommonRenderPasses() + : m_Device( nullptr ) +{ +} + +static bool IsSupportedBlitDimension( nvrhi::TextureDimension dimension ) +{ + return dimension == nvrhi::TextureDimension::Texture2D + || dimension == nvrhi::TextureDimension::Texture2DArray + || dimension == nvrhi::TextureDimension::TextureCube + || dimension == nvrhi::TextureDimension::TextureCubeArray; +} + +static bool IsTextureArray( nvrhi::TextureDimension dimension ) +{ + return dimension == nvrhi::TextureDimension::Texture2DArray + || dimension == nvrhi::TextureDimension::TextureCube + || dimension == nvrhi::TextureDimension::TextureCubeArray; +} + +void CommonRenderPasses::Init( nvrhi::IDevice* device ) +{ + m_Device = device; + + int rectIndex = renderProgManager.FindShader( "builtin/rect", SHADER_STAGE_VERTEX, "", idList(), true, LAYOUT_DRAW_VERT ); + m_RectVS = renderProgManager.GetShader( rectIndex ); + + idList shaderMacros; + shaderMacros.Append( shaderMacro_t( "TEXTURE_ARRAY", "0" ) ); + int blitIndex = renderProgManager.FindShader( "builtin/blit", SHADER_STAGE_FRAGMENT, "", shaderMacros, true, LAYOUT_DRAW_VERT ); + m_BlitPS = renderProgManager.GetShader( blitIndex ); + + shaderMacros[0].definition = "1"; + blitIndex = renderProgManager.FindShader( "builtin/blit", SHADER_STAGE_FRAGMENT, "", shaderMacros, true, LAYOUT_DRAW_VERT ); + m_BlitArrayPS = renderProgManager.GetShader( blitIndex ); + + auto samplerDesc = nvrhi::SamplerDesc() + .setAllFilters( false ) + .setAllAddressModes( nvrhi::SamplerAddressMode::Clamp ); + m_PointClampSampler = m_Device->createSampler( samplerDesc ); + + samplerDesc.setAllFilters( true ); + m_LinearClampSampler = m_Device->createSampler( samplerDesc ); + + samplerDesc.setAllAddressModes( nvrhi::SamplerAddressMode::Wrap ); + m_LinearWrapSampler = m_Device->createSampler( samplerDesc ); + + samplerDesc.setMaxAnisotropy( 16 ); + m_AnisotropicWrapSampler = m_Device->createSampler( samplerDesc ); + + { + unsigned int blackImage = 0xff000000; + unsigned int grayImage = 0xff808080; + unsigned int whiteImage = 0xffffffff; + + nvrhi::TextureDesc textureDesc; + textureDesc.format = nvrhi::Format::RGBA8_UNORM; + textureDesc.width = 1; + textureDesc.height = 1; + textureDesc.mipLevels = 1; + + textureDesc.debugName = "BlackTexture"; + m_BlackTexture = m_Device->createTexture( textureDesc ); + + textureDesc.debugName = "GrayTexture"; + m_GrayTexture = m_Device->createTexture( textureDesc ); + + textureDesc.debugName = "WhiteTexture"; + m_WhiteTexture = m_Device->createTexture( textureDesc ); + + textureDesc.dimension = nvrhi::TextureDimension::TextureCubeArray; + textureDesc.debugName = "BlackCubeMapArray"; + textureDesc.arraySize = 6; + m_BlackCubeMapArray = m_Device->createTexture( textureDesc ); + + textureDesc.dimension = nvrhi::TextureDimension::Texture2DArray; + textureDesc.debugName = "BlackTexture2DArray"; + textureDesc.arraySize = 6; + m_BlackTexture2DArray = m_Device->createTexture( textureDesc ); + textureDesc.debugName = "WhiteTexture2DArray"; + m_WhiteTexture2DArray = m_Device->createTexture( textureDesc ); + + // Write the textures using a temporary CL + + nvrhi::CommandListHandle commandList = m_Device->createCommandList(); + commandList->open(); + + commandList->beginTrackingTextureState( m_BlackTexture, nvrhi::AllSubresources, nvrhi::ResourceStates::Common ); + commandList->beginTrackingTextureState( m_GrayTexture, nvrhi::AllSubresources, nvrhi::ResourceStates::Common ); + commandList->beginTrackingTextureState( m_WhiteTexture, nvrhi::AllSubresources, nvrhi::ResourceStates::Common ); + commandList->beginTrackingTextureState( m_BlackCubeMapArray, nvrhi::AllSubresources, nvrhi::ResourceStates::Common ); + commandList->beginTrackingTextureState( m_BlackTexture2DArray, nvrhi::AllSubresources, nvrhi::ResourceStates::Common ); + commandList->beginTrackingTextureState( m_WhiteTexture2DArray, nvrhi::AllSubresources, nvrhi::ResourceStates::Common ); + + commandList->writeTexture( m_BlackTexture, 0, 0, &blackImage, 0 ); + commandList->writeTexture( m_GrayTexture, 0, 0, &grayImage, 0 ); + commandList->writeTexture( m_WhiteTexture, 0, 0, &whiteImage, 0 ); + + for( uint32_t arraySlice = 0; arraySlice < 6; arraySlice += 1 ) + { + commandList->writeTexture( m_BlackTexture2DArray, arraySlice, 0, &blackImage, 0 ); + commandList->writeTexture( m_WhiteTexture2DArray, arraySlice, 0, &whiteImage, 0 ); + commandList->writeTexture( m_BlackCubeMapArray, arraySlice, 0, &blackImage, 0 ); + } + + commandList->setPermanentTextureState( m_BlackTexture, nvrhi::ResourceStates::ShaderResource ); + commandList->setPermanentTextureState( m_GrayTexture, nvrhi::ResourceStates::ShaderResource ); + commandList->setPermanentTextureState( m_WhiteTexture, nvrhi::ResourceStates::ShaderResource ); + commandList->setPermanentTextureState( m_BlackCubeMapArray, nvrhi::ResourceStates::ShaderResource ); + commandList->setPermanentTextureState( m_BlackTexture2DArray, nvrhi::ResourceStates::ShaderResource ); + commandList->setPermanentTextureState( m_WhiteTexture2DArray, nvrhi::ResourceStates::ShaderResource ); + commandList->commitBarriers(); + + commandList->close(); + m_Device->executeCommandList( commandList ); + } + + { + nvrhi::BindingLayoutDesc layoutDesc; + layoutDesc.visibility = nvrhi::ShaderType::All; + layoutDesc.bindings = + { + nvrhi::BindingLayoutItem::PushConstants( 0, sizeof( BlitConstants ) ), + nvrhi::BindingLayoutItem::Texture_SRV( 0 ), + nvrhi::BindingLayoutItem::Sampler( 0 ) + }; + + m_BlitBindingLayout = m_Device->createBindingLayout( layoutDesc ); + } +} + +void CommonRenderPasses::BlitTexture( nvrhi::ICommandList* commandList, const BlitParameters& params, BindingCache* bindingCache ) +{ + assert( commandList ); + assert( params.targetFramebuffer ); + assert( params.sourceTexture ); + + const nvrhi::FramebufferDesc& targetFramebufferDesc = params.targetFramebuffer->getDesc(); + assert( targetFramebufferDesc.colorAttachments.size() == 1 ); + assert( targetFramebufferDesc.colorAttachments[0].valid() ); + assert( !targetFramebufferDesc.depthAttachment.valid() ); + + const nvrhi::FramebufferInfo& fbinfo = params.targetFramebuffer->getFramebufferInfo(); + const nvrhi::TextureDesc& sourceDesc = params.sourceTexture->getDesc(); + + assert( IsSupportedBlitDimension( sourceDesc.dimension ) ); + bool isTextureArray = IsTextureArray( sourceDesc.dimension ); + + nvrhi::Viewport targetViewport = params.targetViewport; + if( targetViewport.width() == 0 && targetViewport.height() == 0 ) + { + // If no viewport is specified, create one based on the framebuffer dimensions. + // Note that the FB dimensions may not be the same as target texture dimensions, in case a non-zero mip level is used. + targetViewport = nvrhi::Viewport( float( fbinfo.width ), float( fbinfo.height ) ); + } + + nvrhi::IShader* shader = nullptr; + switch( params.sampler ) + { + case BlitSampler::Point: + case BlitSampler::Linear: + shader = isTextureArray ? m_BlitArrayPS : m_BlitPS; + break; + case BlitSampler::Sharpen: + shader = isTextureArray ? m_SharpenArrayPS : m_SharpenPS; + break; + default: + assert( false ); + } + + nvrhi::GraphicsPipelineHandle& pso = m_BlitPsoCache[PsoCacheKey{ fbinfo, shader, params.blendState }]; + if( !pso ) + { + nvrhi::GraphicsPipelineDesc psoDesc; + psoDesc.bindingLayouts = { m_BlitBindingLayout }; + psoDesc.VS = m_RectVS; + psoDesc.PS = shader; + psoDesc.primType = nvrhi::PrimitiveType::TriangleStrip; + psoDesc.renderState.rasterState.setCullNone(); + psoDesc.renderState.depthStencilState.depthTestEnable = false; + psoDesc.renderState.depthStencilState.stencilEnable = false; + psoDesc.renderState.blendState.targets[0] = params.blendState; + + pso = m_Device->createGraphicsPipeline( psoDesc, params.targetFramebuffer ); + } + + nvrhi::BindingSetDesc bindingSetDesc; + { + auto sourceDimension = sourceDesc.dimension; + if( sourceDimension == nvrhi::TextureDimension::TextureCube || sourceDimension == nvrhi::TextureDimension::TextureCubeArray ) + { + sourceDimension = nvrhi::TextureDimension::Texture2DArray; + } + + auto sourceSubresources = nvrhi::TextureSubresourceSet( params.sourceMip, 1, params.sourceArraySlice, 1 ); + + bindingSetDesc.bindings = + { + nvrhi::BindingSetItem::PushConstants( 0, sizeof( BlitConstants ) ), + nvrhi::BindingSetItem::Texture_SRV( 0, params.sourceTexture ).setSubresources( sourceSubresources ).setDimension( sourceDimension ), + nvrhi::BindingSetItem::Sampler( 0, params.sampler == BlitSampler::Point ? m_PointClampSampler : m_LinearClampSampler ) + }; + } + + // If a binding cache is provided, get the binding set from the cache. + // Otherwise, create one and then release it. + nvrhi::BindingSetHandle sourceBindingSet; + if( bindingCache ) + { + sourceBindingSet = bindingCache->GetOrCreateBindingSet( bindingSetDesc, m_BlitBindingLayout ); + } + else + { + sourceBindingSet = m_Device->createBindingSet( bindingSetDesc, m_BlitBindingLayout ); + } + + nvrhi::GraphicsState state; + state.pipeline = pso; + state.framebuffer = params.targetFramebuffer; + state.bindings = { sourceBindingSet }; + state.viewport.addViewport( targetViewport ); + state.viewport.addScissorRect( nvrhi::Rect( targetViewport ) ); + state.blendConstantColor = params.blendConstantColor; + + BlitConstants blitConstants = {}; + blitConstants.sourceOrigin = idVec2( params.sourceBox.x, params.sourceBox.y ); + blitConstants.sourceSize = idVec2( params.sourceBox.z, params.sourceBox.w ); + blitConstants.targetOrigin = idVec2( params.targetBox.x, params.targetBox.y ); + blitConstants.targetSize = idVec2( params.targetBox.z, params.targetBox.w ); + + commandList->setGraphicsState( state ); + + commandList->setPushConstants( &blitConstants, sizeof( blitConstants ) ); + + nvrhi::DrawArguments args; + args.instanceCount = 1; + args.vertexCount = 4; + commandList->draw( args ); +} + +void CommonRenderPasses::BlitTexture( nvrhi::ICommandList* commandList, nvrhi::IFramebuffer* targetFramebuffer, nvrhi::ITexture* sourceTexture, BindingCache* bindingCache ) +{ + assert( commandList ); + assert( targetFramebuffer ); + assert( sourceTexture ); + + BlitParameters params; + params.targetFramebuffer = targetFramebuffer; + params.sourceTexture = sourceTexture; + BlitTexture( commandList, params, bindingCache ); +} diff --git a/neo/renderer/Passes/CommonPasses.h b/neo/renderer/Passes/CommonPasses.h new file mode 100644 index 00000000..b285e4c1 --- /dev/null +++ b/neo/renderer/Passes/CommonPasses.h @@ -0,0 +1,142 @@ +/* +=========================================================================== + +Doom 3 BFG Edition GPL Source Code +Copyright (C) 2022 Stephen Pridham + +This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). + +Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 BFG Edition Source Code. If not, see . + +In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ +#ifndef COMMON_PASSES_H_ +#define COMMON_PASSES_H_ + +#include +#include +#include + +class BindingCache; +class ShaderFactory; + +constexpr uint32_t c_MaxRenderPassConstantBufferVersions = 16; + +enum class BlitSampler +{ + Point, + Linear, + Sharpen +}; + +struct BlitParameters +{ + nvrhi::IFramebuffer* targetFramebuffer = nullptr; + nvrhi::Viewport targetViewport; + idVec4 targetBox = idVec4( 0.f, 0.f, 1.f, 1.f ); + + nvrhi::ITexture* sourceTexture = nullptr; + uint32_t sourceArraySlice = 0; + uint32_t sourceMip = 0; + idVec4 sourceBox = idVec4( 0.f, 0.f, 1.f, 1.f ); + + BlitSampler sampler = BlitSampler::Linear; + nvrhi::BlendState::RenderTarget blendState; + nvrhi::Color blendConstantColor = nvrhi::Color( 0.f ); +}; + +struct BlitConstants +{ + idVec2 sourceOrigin; + idVec2 sourceSize; + + idVec2 targetOrigin; + idVec2 targetSize; + + float sharpenFactor; +}; + +class CommonRenderPasses +{ +protected: + nvrhi::DeviceHandle m_Device; + + struct PsoCacheKey + { + nvrhi::FramebufferInfo fbinfo; + nvrhi::IShader* shader; + nvrhi::BlendState::RenderTarget blendState; + + bool operator==( const PsoCacheKey& other ) const + { + return fbinfo == other.fbinfo && shader == other.shader && blendState == other.blendState; + } + bool operator!=( const PsoCacheKey& other ) const + { + return !( *this == other ); + } + + struct Hash + { + size_t operator()( const PsoCacheKey& s ) const + { + size_t hash = 0; + nvrhi::hash_combine( hash, s.fbinfo ); + nvrhi::hash_combine( hash, s.shader ); + nvrhi::hash_combine( hash, s.blendState ); + return hash; + } + }; + }; + + std::unordered_map m_BlitPsoCache; + +public: + nvrhi::ShaderHandle m_FullscreenVS; + nvrhi::ShaderHandle m_FullscreenAtOneVS; + nvrhi::ShaderHandle m_RectVS; + nvrhi::ShaderHandle m_BlitPS; + nvrhi::ShaderHandle m_BlitArrayPS; + nvrhi::ShaderHandle m_SharpenPS; + nvrhi::ShaderHandle m_SharpenArrayPS; + + nvrhi::TextureHandle m_BlackTexture; + nvrhi::TextureHandle m_GrayTexture; + nvrhi::TextureHandle m_WhiteTexture; + nvrhi::TextureHandle m_BlackTexture2DArray; + nvrhi::TextureHandle m_WhiteTexture2DArray; + nvrhi::TextureHandle m_BlackCubeMapArray; + + nvrhi::SamplerHandle m_PointClampSampler; + nvrhi::SamplerHandle m_LinearClampSampler; + nvrhi::SamplerHandle m_LinearWrapSampler; + nvrhi::SamplerHandle m_AnisotropicWrapSampler; + + nvrhi::BindingLayoutHandle m_BlitBindingLayout; + + CommonRenderPasses(); + + void Init( nvrhi::IDevice* device ); + + void BlitTexture( nvrhi::ICommandList* commandList, const BlitParameters& params, BindingCache* bindingCache = nullptr ); + + // Simplified form of BlitTexture that blits the entire source texture, mip 0 slice 0, into the entire target framebuffer using a linear sampler. + void BlitTexture( nvrhi::ICommandList* commandList, nvrhi::IFramebuffer* targetFramebuffer, nvrhi::ITexture* sourceTexture, BindingCache* bindingCache = nullptr ); +}; + +#endif \ No newline at end of file diff --git a/neo/renderer/Passes/FowardShadingPass.cpp b/neo/renderer/Passes/FowardShadingPass.cpp new file mode 100644 index 00000000..8e3ff63d --- /dev/null +++ b/neo/renderer/Passes/FowardShadingPass.cpp @@ -0,0 +1,855 @@ +/* +=========================================================================== + +Doom 3 BFG Edition GPL Source Code +Copyright (C) 2022 Stephen Pridham + +This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). + +Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 BFG Edition Source Code. If not, see . + +In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ +#include +#pragma hdrstop + +#include "framework/Common_local.h" +#include "renderer/RenderCommon.h" +#include "renderer/Framebuffer.h" + +#include "FowardShadingPass.h" + +/* +same as D3DXMatrixOrthoOffCenterRH + +http://msdn.microsoft.com/en-us/library/bb205348(VS.85).aspx +*/ +static void MatrixOrthogonalProjectionRH( float m[16], float left, float right, float bottom, float top, float zNear, float zFar ) +{ + m[0] = 2 / ( right - left ); + m[4] = 0; + m[8] = 0; + m[12] = ( left + right ) / ( left - right ); + m[1] = 0; + m[5] = 2 / ( top - bottom ); + m[9] = 0; + m[13] = ( top + bottom ) / ( bottom - top ); + m[2] = 0; + m[6] = 0; + m[10] = 1 / ( zNear - zFar ); + m[14] = zNear / ( zNear - zFar ); + m[3] = 0; + m[7] = 0; + m[11] = 0; + m[15] = 1; +} + +void ForwardShadingPass::Init( nvrhi::DeviceHandle deviceHandle ) +{ + device = deviceHandle; + + auto texturedBindingLayoutDesc = nvrhi::BindingLayoutDesc( ) + .setVisibility( nvrhi::ShaderType::All ) + .addItem( nvrhi::BindingLayoutItem::VolatileConstantBuffer( 0 ) ) + .addItem( nvrhi::BindingLayoutItem::Texture_SRV( 0 ) ) + .addItem( nvrhi::BindingLayoutItem::Sampler( 0 ) ); + + auto geometrySkinnedBindingLayoutDesc = nvrhi::BindingLayoutDesc( ) + .setVisibility( nvrhi::ShaderType::All ) + .addItem( nvrhi::BindingLayoutItem::VolatileConstantBuffer( 0 ) ) + .addItem( nvrhi::BindingLayoutItem::VolatileConstantBuffer( 1 ) ); + + auto geometryBindingLayoutDesc = nvrhi::BindingLayoutDesc( ) + .setVisibility( nvrhi::ShaderType::All ) + .addItem( nvrhi::BindingLayoutItem::VolatileConstantBuffer( 0 ) ); + + texturedBindingLayout = device->createBindingLayout( texturedBindingLayoutDesc ); + geometryBindingLayout = device->createBindingLayout( geometryBindingLayoutDesc ); + pipelineDesc.bindingLayouts = { geometryBindingLayout }; + + geometryBindingSetDesc = nvrhi::BindingSetDesc( ) + .addItem( nvrhi::BindingSetItem::ConstantBuffer( 0, renderProgManager.ConstantBuffer( ) ) ); + + samplerCache.Init( deviceHandle.Get( ) ); + + pipeline = nullptr; +} + +void ForwardShadingPass::DrawInteractions( nvrhi::ICommandList* commandList, const viewDef_t* viewDef ) +{ + if( r_skipInteractions.GetBool( ) || viewDef->viewLights == NULL ) + { + return; + } + + commandList->beginMarker( "DrawInteractions" ); + + GL_SelectTexture( 0 ); + + const bool useLightDepthBounds = r_useLightDepthBounds.GetBool( ) && !r_useShadowMapping.GetBool( ); + + Framebuffer* previousFramebuffer = currentFramebuffer; + + // + // for each light, perform shadowing and adding + // + for( const viewLight_t* vLight = viewDef->viewLights; vLight != NULL; vLight = vLight->next ) + { + // do fogging later + if( vLight->lightShader->IsFogLight( ) ) + { + continue; + } + if( vLight->lightShader->IsBlendLight( ) ) + { + continue; + } + + if( vLight->localInteractions == NULL && vLight->globalInteractions == NULL && vLight->translucentInteractions == NULL ) + { + continue; + } + + const idMaterial* lightShader = vLight->lightShader; + commandList->beginMarker( lightShader->GetName( ) ); + + // set the depth bounds for the whole light + if( useLightDepthBounds ) + { + GL_DepthBoundsTest( vLight->scissorRect.zmin, vLight->scissorRect.zmax ); + } + + // RB: shadow mapping + if( r_useShadowMapping.GetBool( ) ) + { + int side, sideStop; + + if( vLight->parallel ) + { + side = 0; + sideStop = r_shadowMapSplits.GetInteger( ) + 1; + } + else if( vLight->pointLight ) + { + if( r_shadowMapSingleSide.GetInteger( ) != -1 ) + { + side = r_shadowMapSingleSide.GetInteger( ); + sideStop = side + 1; + } + else + { + side = 0; + sideStop = 6; + } + } + else + { + side = -1; + sideStop = 0; + } + + for( ; side < sideStop; side++ ) + { + ShadowMapPass( commandList, vLight->globalShadows, vLight, side ); + } + + // go back to main render target + if( previousFramebuffer != NULL ) + { + previousFramebuffer->Bind( ); + } + else + { + Framebuffer::Unbind( ); + } + + renderProgManager.Unbind( ); + + GL_State( GLS_DEFAULT, false ); + } + // RB end + + commandList->endMarker( ); + } + + // disable stencil shadow test + GL_State( GLS_DEFAULT ); + + // unbind texture units + GL_SelectTexture( 0 ); + + // reset depth bounds + if( useLightDepthBounds ) + { + GL_DepthBoundsTest( 0.0f, 0.0f ); + } + + commandList->endMarker( ); +} + +void ForwardShadingPass::ShadowMapPass( nvrhi::ICommandList* commandList, const drawSurf_t* drawSurfs, const viewLight_t* vLight, int side ) +{ + if( r_skipShadows.GetBool( ) ) + { + return; + } + + if( drawSurfs == NULL ) + { + return; + } + + if( viewDef->renderView.rdflags & RDF_NOSHADOWS ) + { + return; + } + + RENDERLOG_PRINTF( "---------- RB_ShadowMapPass( side = %i ) ----------\n", side ); + + renderProgManager.BindShader_Depth( ); + + GL_SelectTexture( 0 ); + + uint64 glState = 0; + + // the actual stencil func will be set in the draw code, but we need to make sure it isn't + // disabled here, and that the value will get reset for the interactions without looking + // like a no-change-required + GL_State( glState | GLS_POLYGON_OFFSET ); + + switch( r_shadowMapOccluderFacing.GetInteger( ) ) + { + case 0: + GL_State( ( glStateBits & ~( GLS_CULL_MASK ) ) | GLS_CULL_FRONTSIDED ); + GL_PolygonOffset( r_shadowMapPolygonFactor.GetFloat( ), r_shadowMapPolygonOffset.GetFloat( ) ); + break; + + case 1: + GL_State( ( glStateBits & ~( GLS_CULL_MASK ) ) | GLS_CULL_BACKSIDED ); + GL_PolygonOffset( -r_shadowMapPolygonFactor.GetFloat( ), -r_shadowMapPolygonOffset.GetFloat( ) ); + break; + + default: + GL_State( ( glStateBits & ~( GLS_CULL_MASK ) ) | GLS_CULL_TWOSIDED ); + GL_PolygonOffset( r_shadowMapPolygonFactor.GetFloat( ), r_shadowMapPolygonOffset.GetFloat( ) ); + break; + } + + idRenderMatrix lightProjectionRenderMatrix; + idRenderMatrix lightViewRenderMatrix; + + + if( vLight->parallel && side >= 0 ) + { + assert( side >= 0 && side < 6 ); + + // original light direction is from surface to light origin + idVec3 lightDir = -vLight->lightCenter; + if( lightDir.Normalize( ) == 0.0f ) + { + lightDir[2] = -1.0f; + } + + idMat3 rotation = lightDir.ToMat3( ); + //idAngles angles = lightDir.ToAngles(); + //idMat3 rotation = angles.ToMat3(); + + const idVec3 viewDir = viewDef->renderView.viewaxis[0]; + const idVec3 viewPos = viewDef->renderView.vieworg; + +#if 1 + idRenderMatrix::CreateViewMatrix( viewDef->renderView.vieworg, rotation, lightViewRenderMatrix ); +#else + float lightViewMatrix[16]; + MatrixLookAtRH( lightViewMatrix, viewPos, lightDir, viewDir ); + idRenderMatrix::Transpose( *( idRenderMatrix* )lightViewMatrix, lightViewRenderMatrix ); +#endif + + idBounds lightBounds; + lightBounds.Clear( ); + + ALIGNTYPE16 frustumCorners_t corners; + idRenderMatrix::GetFrustumCorners( corners, vLight->inverseBaseLightProject, bounds_zeroOneCube ); + + idVec4 point, transf; + for( int j = 0; j < 8; j++ ) + { + point[0] = corners.x[j]; + point[1] = corners.y[j]; + point[2] = corners.z[j]; + point[3] = 1; + + lightViewRenderMatrix.TransformPoint( point, transf ); + transf[0] /= transf[3]; + transf[1] /= transf[3]; + transf[2] /= transf[3]; + + lightBounds.AddPoint( transf.ToVec3( ) ); + } + + float lightProjectionMatrix[16]; + MatrixOrthogonalProjectionRH( lightProjectionMatrix, lightBounds[0][0], lightBounds[1][0], lightBounds[0][1], lightBounds[1][1], -lightBounds[1][2], -lightBounds[0][2] ); + idRenderMatrix::Transpose( *( idRenderMatrix* )lightProjectionMatrix, lightProjectionRenderMatrix ); + + + // 'frustumMVP' goes from global space -> camera local space -> camera projective space + // invert the MVP projection so we can deform zero-to-one cubes into the frustum pyramid shape and calculate global bounds + + idRenderMatrix splitFrustumInverse; + if( !idRenderMatrix::Inverse( viewDef->frustumMVPs[FRUSTUM_CASCADE1 + side], splitFrustumInverse ) ) + { + idLib::Warning( "splitFrustumMVP invert failed" ); + } + + // splitFrustumCorners in global space + ALIGNTYPE16 frustumCorners_t splitFrustumCorners; + idRenderMatrix::GetFrustumCorners( splitFrustumCorners, splitFrustumInverse, bounds_unitCube ); + + idRenderMatrix lightViewProjectionRenderMatrix; + idRenderMatrix::Multiply( lightProjectionRenderMatrix, lightViewRenderMatrix, lightViewProjectionRenderMatrix ); + + // find the bounding box of the current split in the light's clip space + idBounds cropBounds; + cropBounds.Clear( ); + for( int j = 0; j < 8; j++ ) + { + point[0] = splitFrustumCorners.x[j]; + point[1] = splitFrustumCorners.y[j]; + point[2] = splitFrustumCorners.z[j]; + point[3] = 1; + + lightViewRenderMatrix.TransformPoint( point, transf ); + transf[0] /= transf[3]; + transf[1] /= transf[3]; + transf[2] /= transf[3]; + + cropBounds.AddPoint( transf.ToVec3( ) ); + } + + // don't let the frustum AABB be bigger than the light AABB + if( cropBounds[0][0] < lightBounds[0][0] ) + { + cropBounds[0][0] = lightBounds[0][0]; + } + + if( cropBounds[0][1] < lightBounds[0][1] ) + { + cropBounds[0][1] = lightBounds[0][1]; + } + + if( cropBounds[1][0] > lightBounds[1][0] ) + { + cropBounds[1][0] = lightBounds[1][0]; + } + + if( cropBounds[1][1] > lightBounds[1][1] ) + { + cropBounds[1][1] = lightBounds[1][1]; + } + + cropBounds[0][2] = lightBounds[0][2]; + cropBounds[1][2] = lightBounds[1][2]; + + //float cropMatrix[16]; + //MatrixCrop(cropMatrix, cropBounds[0], cropBounds[1]); + + //idRenderMatrix cropRenderMatrix; + //idRenderMatrix::Transpose( *( idRenderMatrix* )cropMatrix, cropRenderMatrix ); + + //idRenderMatrix tmp = lightProjectionRenderMatrix; + //idRenderMatrix::Multiply( cropRenderMatrix, tmp, lightProjectionRenderMatrix ); + + MatrixOrthogonalProjectionRH( lightProjectionMatrix, cropBounds[0][0], cropBounds[1][0], cropBounds[0][1], cropBounds[1][1], -cropBounds[1][2], -cropBounds[0][2] ); + idRenderMatrix::Transpose( *( idRenderMatrix* )lightProjectionMatrix, lightProjectionRenderMatrix ); + + shadowV[side] = lightViewRenderMatrix; + shadowP[side] = lightProjectionRenderMatrix; + } + else if( vLight->pointLight && side >= 0 ) + { + assert( side >= 0 && side < 6 ); + + // FIXME OPTIMIZE no memset + + float viewMatrix[16]; + + idVec3 vec; + idVec3 origin = vLight->globalLightOrigin; + + // side of a point light + memset( viewMatrix, 0, sizeof( viewMatrix ) ); + switch( side ) + { + case 0: + viewMatrix[0] = 1; + viewMatrix[9] = 1; + viewMatrix[6] = -1; + break; + case 1: + viewMatrix[0] = -1; + viewMatrix[9] = -1; + viewMatrix[6] = -1; + break; + case 2: + viewMatrix[4] = 1; + viewMatrix[1] = -1; + viewMatrix[10] = 1; + break; + case 3: + viewMatrix[4] = -1; + viewMatrix[1] = -1; + viewMatrix[10] = -1; + break; + case 4: + viewMatrix[8] = 1; + viewMatrix[1] = -1; + viewMatrix[6] = -1; + break; + case 5: + viewMatrix[8] = -1; + viewMatrix[1] = 1; + viewMatrix[6] = -1; + break; + } + + viewMatrix[12] = -origin[0] * viewMatrix[0] + -origin[1] * viewMatrix[4] + -origin[2] * viewMatrix[8]; + viewMatrix[13] = -origin[0] * viewMatrix[1] + -origin[1] * viewMatrix[5] + -origin[2] * viewMatrix[9]; + viewMatrix[14] = -origin[0] * viewMatrix[2] + -origin[1] * viewMatrix[6] + -origin[2] * viewMatrix[10]; + + viewMatrix[3] = 0; + viewMatrix[7] = 0; + viewMatrix[11] = 0; + viewMatrix[15] = 1; + + // from world space to light origin, looking down the X axis + float unflippedLightViewMatrix[16]; + + // from world space to OpenGL view space, looking down the negative Z axis + float lightViewMatrix[16]; + + static float s_flipMatrix[16] = + { + // convert from our coordinate system (looking down X) + // to OpenGL's coordinate system (looking down -Z) + 0, 0, -1, 0, + -1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 0, 1 + }; + + memcpy( unflippedLightViewMatrix, viewMatrix, sizeof( unflippedLightViewMatrix ) ); + R_MatrixMultiply( viewMatrix, s_flipMatrix, lightViewMatrix ); + + idRenderMatrix::Transpose( *( idRenderMatrix* )lightViewMatrix, lightViewRenderMatrix ); + + // set up 90 degree projection matrix + const float zNear = 4; + const float fov = r_shadowMapFrustumFOV.GetFloat( ); + + float ymax = zNear * tan( fov * idMath::PI / 360.0f ); + float ymin = -ymax; + + float xmax = zNear * tan( fov * idMath::PI / 360.0f ); + float xmin = -xmax; + + const float width = xmax - xmin; + const float height = ymax - ymin; + + // from OpenGL view space to OpenGL NDC ( -1 : 1 in XYZ ) + float lightProjectionMatrix[16]; + + lightProjectionMatrix[0 * 4 + 0] = 2.0f * zNear / width; + lightProjectionMatrix[1 * 4 + 0] = 0.0f; + lightProjectionMatrix[2 * 4 + 0] = ( xmax + xmin ) / width; // normally 0 + lightProjectionMatrix[3 * 4 + 0] = 0.0f; + + lightProjectionMatrix[0 * 4 + 1] = 0.0f; + lightProjectionMatrix[1 * 4 + 1] = 2.0f * zNear / height; + lightProjectionMatrix[2 * 4 + 1] = ( ymax + ymin ) / height; // normally 0 + lightProjectionMatrix[3 * 4 + 1] = 0.0f; + + // this is the far-plane-at-infinity formulation, and + // crunches the Z range slightly so w=0 vertexes do not + // rasterize right at the wraparound point + lightProjectionMatrix[0 * 4 + 2] = 0.0f; + lightProjectionMatrix[1 * 4 + 2] = 0.0f; + lightProjectionMatrix[2 * 4 + 2] = -0.999f; // adjust value to prevent imprecision issues + lightProjectionMatrix[3 * 4 + 2] = -2.0f * zNear; + + lightProjectionMatrix[0 * 4 + 3] = 0.0f; + lightProjectionMatrix[1 * 4 + 3] = 0.0f; + lightProjectionMatrix[2 * 4 + 3] = -1.0f; + lightProjectionMatrix[3 * 4 + 3] = 0.0f; + + idRenderMatrix::Transpose( *( idRenderMatrix* )lightProjectionMatrix, lightProjectionRenderMatrix ); + + shadowV[side] = lightViewRenderMatrix; + shadowP[side] = lightProjectionRenderMatrix; + } + else + { + lightViewRenderMatrix.Identity( ); + lightProjectionRenderMatrix = vLight->baseLightProject; + + shadowV[0] = lightViewRenderMatrix; + shadowP[0] = lightProjectionRenderMatrix; + } + + GL_ViewportAndScissor( 0, 0, shadowMapResolutions[vLight->shadowLOD], shadowMapResolutions[vLight->shadowLOD] ); + + //glClear( GL_DEPTH_BUFFER_BIT ); + + // process the chain of shadows with the current rendering state + currentSpace = NULL; + + nvrhi::GraphicsState graphicsState; + graphicsState.framebuffer = currentFramebuffer->GetApiObject( ); + nvrhi::Viewport viewport; + viewport.minX = currentViewport.x1; + viewport.minY = currentViewport.y1; + viewport.maxX = currentViewport.x2; + viewport.maxY = currentViewport.y2; + viewport.minZ = currentViewport.zmin; + viewport.maxZ = currentViewport.zmax; + graphicsState.viewport.addViewportAndScissorRect( viewport ); + graphicsState.viewport.addScissorRect( nvrhi::Rect( currentScissor.x1, currentScissor.y1, currentScissor.x2, currentScissor.y2 ) ); + renderProgManager.BindShader_Depth( ); + auto currentBindingSet = bindingCache.GetOrCreateBindingSet( geometryBindingSetDesc, renderProgManager.BindingLayout( ) ); + graphicsState.bindings = { currentBindingSet }; + + bool stateValid = false; + + nvrhi::DrawArguments currentDraw; + currentDraw.instanceCount = 0; + + shaderStage_t lastStage; + bool changedStage = false; + + int lastShader = -1; + + for( const drawSurf_t* drawSurf = drawSurfs; drawSurf != NULL; drawSurf = drawSurf->nextOnLight ) + { + stateValid = false; + + // Make sure the shadow occluder geometry is done + if( drawSurf->shadowVolumeState != SHADOWVOLUME_DONE ) + { + assert( drawSurf->shadowVolumeState == SHADOWVOLUME_UNFINISHED || drawSurf->shadowVolumeState == SHADOWVOLUME_DONE ); + + uint64 start = Sys_Microseconds( ); + while( drawSurf->shadowVolumeState == SHADOWVOLUME_UNFINISHED ) + { + Sys_Yield( ); + } + uint64 end = Sys_Microseconds( ); + + // TODO(Stephen): will probably need to change this if we're going to make this multi-threaded. + tr.backend.pc.cpuShadowMicroSec += end - start; + } + + if( drawSurf->numIndexes == 0 ) + { + continue; // a job may have created an empty shadow geometry + } + + if( drawSurf->space != currentSpace ) + { + idRenderMatrix modelRenderMatrix; + idRenderMatrix::Transpose( *( idRenderMatrix* )drawSurf->space->modelMatrix, modelRenderMatrix ); + + idRenderMatrix modelToLightRenderMatrix; + idRenderMatrix::Multiply( lightViewRenderMatrix, modelRenderMatrix, modelToLightRenderMatrix ); + + idRenderMatrix clipMVP; + idRenderMatrix::Multiply( lightProjectionRenderMatrix, modelToLightRenderMatrix, clipMVP ); + + if( vLight->parallel ) + { + idRenderMatrix MVP; + idRenderMatrix::Multiply( renderMatrix_clipSpaceToWindowSpace, clipMVP, MVP ); + + RB_SetMVP( clipMVP ); + } + else if( side < 0 ) + { + // from OpenGL view space to OpenGL NDC ( -1 : 1 in XYZ ) + idRenderMatrix MVP; + idRenderMatrix::Multiply( renderMatrix_windowSpaceToClipSpace, clipMVP, MVP ); + + RB_SetMVP( MVP ); + } + else + { + RB_SetMVP( clipMVP ); + } + + // set the local light position to allow the vertex program to project the shadow volume end cap to infinity + /* + idVec4 localLight( 0.0f ); + R_GlobalPointToLocal( drawSurf->space->modelMatrix, vLight->globalLightOrigin, localLight.ToVec3() ); + SetVertexParm( RENDERPARM_LOCALLIGHTORIGIN, localLight.ToFloatPtr() ); + */ + + currentSpace = drawSurf->space; + } + + bool didDraw = false; + + const idMaterial* shader = drawSurf->material; + + // get the expressions for conditionals / color / texcoords + const float* regs = drawSurf->shaderRegisters; + idVec4 color( 0, 0, 0, 1 ); + + uint64 surfGLState = 0; + + // set polygon offset if necessary + if( shader && shader->TestMaterialFlag( MF_POLYGONOFFSET ) ) + { + surfGLState |= GLS_POLYGON_OFFSET; + GL_PolygonOffset( r_offsetFactor.GetFloat( ), r_offsetUnits.GetFloat( ) * shader->GetPolygonOffset( ) ); + } + + if( shader && shader->Coverage( ) == MC_PERFORATED ) + { + // perforated surfaces may have multiple alpha tested stages + for( int stage = 0; stage < shader->GetNumStages( ); stage++ ) + { + const shaderStage_t* pStage = shader->GetStage( stage ); + + if( !pStage->hasAlphaTest ) + { + continue; + } + + // check the stage enable condition + if( regs[pStage->conditionRegister] == 0 ) + { + continue; + } + + // if we at least tried to draw an alpha tested stage, + // we won't draw the opaque surface + didDraw = true; + + // set the alpha modulate + color[3] = regs[pStage->color.registers[3]]; + + // skip the entire stage if alpha would be black + if( color[3] <= 0.0f ) + { + continue; + } + + uint64 stageGLState = surfGLState; + + // set privatePolygonOffset if necessary + if( pStage->privatePolygonOffset ) + { + GL_PolygonOffset( r_offsetFactor.GetFloat( ), r_offsetUnits.GetFloat( ) * pStage->privatePolygonOffset ); + stageGLState |= GLS_POLYGON_OFFSET; + } + + GL_Color( color ); + + GL_State( stageGLState ); + idVec4 alphaTestValue( regs[pStage->alphaTestRegister] ); + renderProgManager.SetRenderParm( RENDERPARM_ALPHA_TEST, alphaTestValue.ToFloatPtr( ) ); + + if( drawSurf->jointCache ) + { + renderProgManager.BindShader_TextureVertexColorSkinned( ); + } + else + { + renderProgManager.BindShader_TextureVertexColor( ); + } + + RB_SetVertexColorParms( SVC_IGNORE ); + + bool imageChanged = ( imageParms[0] != pStage->texture.image ); + + // bind the texture + GL_SelectTexture( 0 ); + GL_BindTexture( pStage->texture.image ); + + // set texture matrix and texGens + PrepareStageTexturing( pStage, drawSurf ); + + if( imageChanged ) + { + auto bindingSetDesc = nvrhi::BindingSetDesc( ) + .addItem( nvrhi::BindingSetItem::ConstantBuffer( 0, renderProgManager.ConstantBuffer( ) ) ) + .addItem( nvrhi::BindingSetItem::Texture_SRV( 0, ( nvrhi::ITexture* )imageParms[0]->GetTextureID( ) ) ) + .addItem( nvrhi::BindingSetItem::Sampler( 0, ( nvrhi::ISampler* )imageParms[0]->GetSampler( samplerCache ) ) ); + + auto currentBindingSet = bindingCache.GetOrCreateBindingSet( bindingSetDesc, renderProgManager.BindingLayout() ); + + graphicsState.bindings = { currentBindingSet }; + + stateValid = false; + } + + // must render with less-equal for Z-Cull to work properly + assert( ( GL_GetCurrentState( ) & GLS_DEPTHFUNC_BITS ) == GLS_DEPTHFUNC_LESS ); + + SetupInputBuffers( drawSurf, graphicsState ); + + renderProgManager.CommitConstantBuffer( commandList ); + + if( !stateValid ) + { + commandList->setGraphicsState( graphicsState ); + stateValid = true; + } + + if( !pipeline ) + { + pipeline = CreateGraphicsPipeline( currentFramebuffer->GetApiObject() ); + } + + nvrhi::DrawArguments args; + args.vertexCount = drawSurf->numIndexes; + args.instanceCount = 1; + commandList->drawIndexed( args ); + + // clean up + FinishStageTexturing( pStage, drawSurf ); + + // unset privatePolygonOffset if necessary + if( pStage->privatePolygonOffset ) + { + GL_PolygonOffset( r_offsetFactor.GetFloat( ), r_offsetUnits.GetFloat( ) * shader->GetPolygonOffset( ) ); + } + } + } + + if( !didDraw ) + { + if( drawSurf->jointCache ) + { + renderProgManager.BindShader_DepthSkinned( ); + } + else + { + renderProgManager.BindShader_Depth( ); + } + + // must render with less-equal for Z-Cull to work properly + assert( ( GL_GetCurrentState( ) & GLS_DEPTHFUNC_BITS ) == GLS_DEPTHFUNC_LESS ); + + SetupInputBuffers( drawSurf, graphicsState ); + + if( !stateValid ) + { + commandList->setGraphicsState( graphicsState ); + stateValid = true; + } + + if( !pipeline ) + { + pipeline = CreateGraphicsPipeline( currentFramebuffer->GetApiObject( ) ); + } + + renderProgManager.CommitConstantBuffer( commandList ); + + nvrhi::DrawArguments args; + args.vertexCount = drawSurf->numIndexes; + args.instanceCount = 1; + commandList->drawIndexed( args ); + } + } +} + +nvrhi::GraphicsPipelineHandle ForwardShadingPass::CreateGraphicsPipeline( nvrhi::IFramebuffer* framebuffer ) +{ + return device->createGraphicsPipeline( pipelineDesc, framebuffer ); +} + +void ForwardShadingPass::SetupView( nvrhi::ICommandList* commandList, viewDef_t* viewDef ) +{ +} + +bool ForwardShadingPass::SetupMaterial( const idMaterial* material, nvrhi::RasterCullMode cullMode, nvrhi::GraphicsState& state ) +{ + if( !pipeline ) + { + pipeline = CreateGraphicsPipeline( state.framebuffer ); + } + + if( !pipeline ) + { + return false; + } + + assert( pipeline->getFramebufferInfo( ) == state.framebuffer->getFramebufferInfo( ) ); + + state.pipeline = pipeline; + + return true; +} + +void ForwardShadingPass::SetupInputBuffers( const drawSurf_t* surf, nvrhi::GraphicsState& state ) +{ + // Get vertex buffer + const vertCacheHandle_t vbHandle = surf->ambientCache; + idVertexBuffer* vertexBuffer; + if( vertexCache.CacheIsStatic( vbHandle ) ) + { + vertexBuffer = &vertexCache.staticData.vertexBuffer; + } + else + { + const uint64 frameNum = ( int )( vbHandle >> VERTCACHE_FRAME_SHIFT ) & VERTCACHE_FRAME_MASK; + if( frameNum != ( ( vertexCache.currentFrame - 1 ) & VERTCACHE_FRAME_MASK ) ) + { + idLib::Warning( "RB_DrawElementsWithCounters, vertexBuffer == NULL" ); + return; + } + vertexBuffer = &vertexCache.frameData[vertexCache.drawListNum].vertexBuffer; + } + const uint vertOffset = ( uint )( vbHandle >> VERTCACHE_OFFSET_SHIFT ) & VERTCACHE_OFFSET_MASK; + + // Get index buffer + const vertCacheHandle_t ibHandle = surf->indexCache; + idIndexBuffer* indexBuffer; + if( vertexCache.CacheIsStatic( ibHandle ) ) + { + indexBuffer = &vertexCache.staticData.indexBuffer; + } + else + { + const uint64 frameNum = ( int )( ibHandle >> VERTCACHE_FRAME_SHIFT ) & VERTCACHE_FRAME_MASK; + if( frameNum != ( ( vertexCache.currentFrame - 1 ) & VERTCACHE_FRAME_MASK ) ) + { + idLib::Warning( "RB_DrawElementsWithCounters, indexBuffer == NULL" ); + return; + } + indexBuffer = &vertexCache.frameData[vertexCache.drawListNum].indexBuffer; + } + const uint indexOffset = ( uint )( ibHandle >> VERTCACHE_OFFSET_SHIFT ) & VERTCACHE_OFFSET_MASK; + + RENDERLOG_PRINTF( "Binding Buffers: %p:%i %p:%i\n", vertexBuffer, vertOffset, indexBuffer, indexOffset ); + + state.indexBuffer = { indexBuffer->GetAPIObject(), nvrhi::Format::R16_UINT, indexOffset }; + state.vertexBuffers = { { vertexBuffer->GetAPIObject(), 0, vertOffset } }; +} + +void ForwardShadingPass::SetPushConstants( nvrhi::ICommandList* commandList, nvrhi::GraphicsState& state, nvrhi::DrawArguments& args ) +{ +} diff --git a/neo/renderer/Passes/FowardShadingPass.h b/neo/renderer/Passes/FowardShadingPass.h new file mode 100644 index 00000000..f4e73b83 --- /dev/null +++ b/neo/renderer/Passes/FowardShadingPass.h @@ -0,0 +1,68 @@ +/* +=========================================================================== + +Doom 3 BFG Edition GPL Source Code +Copyright (C) 2022 Stephen Pridham + +This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). + +Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 BFG Edition Source Code. If not, see . + +In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ +#ifndef RENDERER_PASSES_FORWARDSHADINGPASS_H_ +#define RENDERER_PASSES_FORWARDSHADINGPASS_H_ + +#include "GeometryPasses.h" + +class ForwardShadingPass : IGeometryPass +{ +public: + + ForwardShadingPass( ) = default; + virtual ~ForwardShadingPass( ) = default; + + void Init( nvrhi::DeviceHandle deviceHandle ); + + void DrawInteractions( nvrhi::ICommandList* commandList, const viewDef_t* _viewDef ); + void ShadowMapPass( nvrhi::ICommandList* commandList, const drawSurf_t* drawSurfs, const viewLight_t* vLight, int side ); + +protected: + + nvrhi::DeviceHandle device; + nvrhi::BindingLayoutHandle geometryBindingLayout; + nvrhi::BindingLayoutHandle texturedBindingLayout; + nvrhi::BindingSetDesc geometryBindingSetDesc; + SamplerCache samplerCache; + + nvrhi::GraphicsPipelineHandle CreateGraphicsPipeline( nvrhi::IFramebuffer* framebuffer ); + +public: + + void SetupView( nvrhi::ICommandList* commandList, viewDef_t* viewDef ) override; + bool SetupMaterial( const idMaterial* material, nvrhi::RasterCullMode cullMode, nvrhi::GraphicsState& state ) override; + void SetupInputBuffers( const drawSurf_t* drawSurf, nvrhi::GraphicsState& state ) override; + void SetPushConstants( nvrhi::ICommandList* commandList, nvrhi::GraphicsState& state, nvrhi::DrawArguments& args ) override; + +private: + + idRenderMatrix shadowV[6]; // shadow depth view matrix + idRenderMatrix shadowP[6]; // shadow depth projection matrix +}; + +#endif \ No newline at end of file diff --git a/neo/renderer/Passes/GBufferFillPass.cpp b/neo/renderer/Passes/GBufferFillPass.cpp new file mode 100644 index 00000000..ee072b66 --- /dev/null +++ b/neo/renderer/Passes/GBufferFillPass.cpp @@ -0,0 +1,683 @@ +/* +=========================================================================== + +Doom 3 BFG Edition GPL Source Code +Copyright (C) 2022 Stephen Pridham + +This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). + +Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 BFG Edition Source Code. If not, see . + +In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ +#include +#pragma hdrstop + +#include "renderer/RenderCommon.h" + +#include "GBufferFillPass.h" + +void GBufferFillPass::Init( nvrhi::DeviceHandle deviceHandle ) +{ +} + +#if 0 +void GBufferFillPass::RenderView( nvrhi::ICommandList* commandList, const drawSurf_t* const* drawSurfs, int numDrawSurfs, bool fillGbuffer ) +{ + Framebuffer* previousFramebuffer = Framebuffer::GetActiveFramebuffer( ); + + if( numDrawSurfs == 0 ) + { + return; + } + + if( !drawSurfs ) + { + return; + } + + // if we are just doing 2D rendering, no need to fill the depth buffer + if( viewDef->viewEntitys == NULL ) + { + return; + } + + if( viewDef->renderView.rdflags & RDF_NOAMBIENT ) + { + return; + } + +#if defined( USE_VULKAN ) + if( fillGbuffer ) + { + return; + } +#endif + + + /* + if( !fillGbuffer ) + { + // clear gbuffer + GL_Clear( true, false, false, 0, 0.0f, 0.0f, 0.0f, 1.0f, false ); + } + */ + + if( !fillGbuffer && r_useSSAO.GetBool( ) && r_ssaoDebug.GetBool( ) ) + { + GL_State( GLS_SRCBLEND_ONE | GLS_DSTBLEND_ZERO | GLS_DEPTHMASK | GLS_DEPTHFUNC_ALWAYS ); + + // We just want to do a quad pass - so make sure we disable any texgen and + // set the texture matrix to the identity so we don't get anomalies from + // any stale uniform data being present from a previous draw call + const float texS[4] = { 1.0f, 0.0f, 0.0f, 0.0f }; + const float texT[4] = { 0.0f, 1.0f, 0.0f, 0.0f }; + renderProgManager.SetRenderParm( RENDERPARM_TEXTUREMATRIX_S, texS ); + renderProgManager.SetRenderParm( RENDERPARM_TEXTUREMATRIX_T, texT ); + + // disable any texgen + const float texGenEnabled[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; + renderProgManager.SetRenderParm( RENDERPARM_TEXGEN_0_ENABLED, texGenEnabled ); + + currentSpace = NULL; + RB_SetMVP( renderMatrix_identity ); + + renderProgManager.BindShader_Texture( ); + GL_Color( idVec4( 1 ) ); + + GL_SelectTexture( 0 ); + globalImages->ambientOcclusionImage[0]->Bind( ); + + DrawElementsWithCounters( &tr.backend.unitSquareSurface ); + + renderProgManager.Unbind( ); + GL_State( GLS_DEFAULT ); + + renderProgManager.SetRenderParm( RENDERPARM_ALPHA_TEST, vec4_zero.ToFloatPtr( ) ); + + return; + } + + renderLog.OpenMainBlock( fillGbuffer ? MRB_FILL_GEOMETRY_BUFFER : MRB_AMBIENT_PASS ); + renderLog.OpenBlock( fillGbuffer ? "Fill_GeometryBuffer" : "Render_AmbientPass", colorBlue ); + + if( fillGbuffer ) + { + globalFramebuffers.geometryBufferFBO->Bind( ); + + GL_Clear( true, false, false, 0, 0.0f, 0.0f, 0.0f, 1.0f, false ); + } + + commandList->setEnableAutomaticBarriers( false ); + commandList->setResourceStatesForFramebuffer( currentFrameBuffer->GetApiObject( ) ); + commandList->commitBarriers( ); + + // RB: not needed + // GL_StartDepthPass( backEnd.viewDef->scissor ); + + // force MVP change on first surface + currentSpace = NULL; + + // draw all the subview surfaces, which will already be at the start of the sorted list, + // with the general purpose path + GL_State( GLS_DEFAULT ); + +#define BLEND_NORMALS 1 + + // RB: even use additive blending to blend the normals + //GL_State( GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE | GLS_DEPTHMASK | GLS_DEPTHFUNC_EQUAL ); + + GL_State( GLS_SRCBLEND_ONE | GLS_DSTBLEND_ZERO | GLS_DEPTHMASK | GLS_DEPTHFUNC_EQUAL ); + + GL_Color( colorWhite ); + + idVec4 diffuseColor; + idVec4 specularColor; + idVec4 ambientColor; + + if( viewDef->renderView.rdflags & RDF_IRRADIANCE ) + { + // RB: don't let artist run into a trap when baking multibounce lightgrids + + // use default value of r_lightScale 3 + const float lightScale = 3; + const idVec4 lightColor = colorWhite * lightScale; + + // apply the world-global overbright and the 2x factor for specular + diffuseColor = lightColor; + specularColor = lightColor;// * 2.0f; + + // loose 5% with every bounce like in DDGI + const float energyConservation = 0.95f; + + //ambientColor.Set( energyConservation, energyConservation, energyConservation, 1.0f ); + float a = r_forceAmbient.GetFloat( ); + + ambientColor.Set( a, a, a, 1 ); + } + else + { + const float lightScale = r_lightScale.GetFloat( ); + const idVec4 lightColor = colorWhite * lightScale; + + // apply the world-global overbright and tune down specular a bit so we have less fresnel overglow + diffuseColor = lightColor; + specularColor = lightColor;// * 0.5f; + + float ambientBoost = 1.0f; + if( !r_usePBR.GetBool( ) ) + { + ambientBoost += r_useSSAO.GetBool( ) ? 0.2f : 0.0f; + ambientBoost *= r_useHDR.GetBool( ) ? 1.1f : 1.0f; + } + + ambientColor.x = r_forceAmbient.GetFloat( ) * ambientBoost; + ambientColor.y = r_forceAmbient.GetFloat( ) * ambientBoost; + ambientColor.z = r_forceAmbient.GetFloat( ) * ambientBoost; + ambientColor.w = 1; + } + + renderProgManager.SetRenderParm( RENDERPARM_AMBIENT_COLOR, ambientColor.ToFloatPtr( ) ); + + bool useIBL = r_usePBR.GetBool( ) && !fillGbuffer; + + // setup renderparms assuming we will be drawing trivial surfaces first + RB_SetupForFastPathInteractions( diffuseColor, specularColor ); + + for( int i = 0; i < numDrawSurfs; i++ ) + { + const drawSurf_t* drawSurf = drawSurfs[i]; + const idMaterial* surfaceMaterial = drawSurf->material; + + // translucent surfaces don't put anything in the depth buffer and don't + // test against it, which makes them fail the mirror clip plane operation + if( surfaceMaterial->Coverage( ) == MC_TRANSLUCENT ) + { + continue; + } + + // get the expressions for conditionals / color / texcoords + const float* surfaceRegs = drawSurf->shaderRegisters; + + // if all stages of a material have been conditioned off, don't do anything + int stage = 0; + for( ; stage < surfaceMaterial->GetNumStages( ); stage++ ) + { + const shaderStage_t* pStage = surfaceMaterial->GetStage( stage ); + // check the stage enable condition + if( surfaceRegs[pStage->conditionRegister] != 0 ) + { + break; + } + } + if( stage == surfaceMaterial->GetNumStages( ) ) + { + continue; + } + + //bool isWorldModel = ( drawSurf->space->entityDef->parms.origin == vec3_origin ); + + //if( isWorldModel ) + //{ + // renderProgManager.BindShader_VertexLighting(); + //} + //else + { + if( fillGbuffer ) + { + // TODO support PBR textures and store roughness in the alpha channel + + // fill geometry buffer with normal/roughness information + if( drawSurf->jointCache ) + { + renderProgManager.BindShader_SmallGeometryBufferSkinned( ); + } + else + { + renderProgManager.BindShader_SmallGeometryBuffer( ); + } + } + else + { + + // TODO support PBR textures + + // draw Quake 4 style ambient + if( drawSurf->jointCache ) + { + renderProgManager.BindShader_AmbientLightingSkinned( ); + } + else + { + renderProgManager.BindShader_AmbientLighting( ); + } + } + } + + // change the matrix if needed + if( drawSurf->space != currentSpace ) + { + currentSpace = drawSurf->space; + + RB_SetMVP( drawSurf->space->mvp ); + + // tranform the view origin into model local space + idVec4 localViewOrigin( 1.0f ); + R_GlobalPointToLocal( drawSurf->space->modelMatrix, viewDef->renderView.vieworg, localViewOrigin.ToVec3( ) ); + SetVertexParm( RENDERPARM_LOCALVIEWORIGIN, localViewOrigin.ToFloatPtr( ) ); + + // RB: if we want to store the normals in world space so we need the model -> world matrix + idRenderMatrix modelMatrix; + idRenderMatrix::Transpose( *( idRenderMatrix* )drawSurf->space->modelMatrix, modelMatrix ); + + SetVertexParms( RENDERPARM_MODELMATRIX_X, modelMatrix[0], 4 ); + + // RB: if we want to store the normals in camera space so we need the model -> camera matrix + float modelViewMatrixTranspose[16]; + R_MatrixTranspose( drawSurf->space->modelViewMatrix, modelViewMatrixTranspose ); + SetVertexParms( RENDERPARM_MODELVIEWMATRIX_X, modelViewMatrixTranspose, 4 ); + } + + /* + uint64 surfGLState = 0; + + // set polygon offset if necessary + if( surfaceMaterial->TestMaterialFlag( MF_POLYGONOFFSET ) ) + { + surfGLState |= GLS_POLYGON_OFFSET; + GL_PolygonOffset( r_offsetFactor.GetFloat(), r_offsetUnits.GetFloat() * surfaceMaterial->GetPolygonOffset() ); + } + + // subviews will just down-modulate the color buffer + idVec4 color; + if( surfaceMaterial->GetSort() == SS_SUBVIEW ) + { + surfGLState |= GLS_SRCBLEND_DST_COLOR | GLS_DSTBLEND_ZERO | GLS_DEPTHFUNC_LESS; + color[0] = 1.0f; + color[1] = 1.0f; + color[2] = 1.0f; + color[3] = 1.0f; + } + else + { + // others just draw black + #if 0 + color[0] = 0.0f; + color[1] = 0.0f; + color[2] = 0.0f; + color[3] = 1.0f; + #else + color = colorWhite; + #endif + } + */ + + drawInteraction_t inter = {}; + inter.surf = drawSurf; + + inter.diffuseColor[0] = inter.diffuseColor[1] = inter.diffuseColor[2] = inter.diffuseColor[3] = 1; + inter.specularColor[0] = inter.specularColor[1] = inter.specularColor[2] = inter.specularColor[3] = 0; + + // check for the fast path + if( surfaceMaterial->GetFastPathBumpImage( ) && !r_skipInteractionFastPath.GetBool( ) ) + { + renderLog.OpenBlock( surfaceMaterial->GetName( ), colorMdGrey ); + + inter.bumpImage = surfaceMaterial->GetFastPathBumpImage( ); + inter.specularImage = surfaceMaterial->GetFastPathSpecularImage( ); + inter.diffuseImage = surfaceMaterial->GetFastPathDiffuseImage( ); + + DrawSingleInteraction( &inter, true, useIBL, false ); + + renderLog.CloseBlock( ); + continue; + } + + renderLog.OpenBlock( surfaceMaterial->GetName( ), colorMdGrey ); + + //bool drawSolid = false; + + inter.bumpImage = NULL; + inter.specularImage = NULL; + inter.diffuseImage = NULL; + + // we may have multiple alpha tested stages + // if the only alpha tested stages are condition register omitted, + // draw a normal opaque surface + bool didDraw = false; + + // perforated surfaces may have multiple alpha tested stages + for( stage = 0; stage < surfaceMaterial->GetNumStages( ); stage++ ) + { + const shaderStage_t* surfaceStage = surfaceMaterial->GetStage( stage ); + + switch( surfaceStage->lighting ) + { + case SL_COVERAGE: + { + // ignore any coverage stages since they should only be used for the depth fill pass + // for diffuse stages that use alpha test. + break; + } + + case SL_AMBIENT: + { + // ignore ambient stages while drawing interactions + break; + } + + case SL_BUMP: + { + // ignore stage that fails the condition + if( !surfaceRegs[surfaceStage->conditionRegister] ) + { + break; + } + // draw any previous interaction + if( inter.bumpImage != NULL ) + { +#if BLEND_NORMALS + if( inter.vertexColor == SVC_IGNORE ) + { + GL_State( GLS_SRCBLEND_ONE | GLS_DSTBLEND_ZERO | GLS_DEPTHMASK | GLS_DEPTHFUNC_EQUAL ); + } + else + { + // RB: this is a bit hacky: use additive blending to blend the normals + GL_State( GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE | GLS_DEPTHMASK | GLS_DEPTHFUNC_EQUAL ); + } +#endif + + DrawSingleInteraction( &inter, false, useIBL, false ); + } + inter.bumpImage = surfaceStage->texture.image; + inter.diffuseImage = NULL; + inter.specularImage = NULL; + SetupInteractionStage( surfaceStage, surfaceRegs, NULL, + inter.bumpMatrix, NULL ); + break; + } + + case SL_DIFFUSE: + { + // ignore stage that fails the condition + if( !surfaceRegs[surfaceStage->conditionRegister] ) + { + break; + } + + // draw any previous interaction + if( inter.diffuseImage != NULL ) + { +#if BLEND_NORMALS + if( inter.vertexColor == SVC_IGNORE ) + { + GL_State( GLS_SRCBLEND_ONE | GLS_DSTBLEND_ZERO | GLS_DEPTHMASK | GLS_DEPTHFUNC_EQUAL ); + } + else + { + // RB: this is a bit hacky: use additive blending to blend the normals + GL_State( GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE | GLS_DEPTHMASK | GLS_DEPTHFUNC_EQUAL ); + } +#endif + + DrawSingleInteraction( &inter, false, useIBL, false ); + } + + inter.diffuseImage = surfaceStage->texture.image; + inter.vertexColor = surfaceStage->vertexColor; + SetupInteractionStage( surfaceStage, surfaceRegs, diffuseColor.ToFloatPtr( ), + inter.diffuseMatrix, inter.diffuseColor.ToFloatPtr( ) ); + break; + } + + case SL_SPECULAR: + case SL_RMAO: + { + // ignore stage that fails the condition + if( !surfaceRegs[surfaceStage->conditionRegister] ) + { + break; + } + // draw any previous interaction + if( inter.specularImage != NULL ) + { +#if BLEND_NORMALS + if( inter.vertexColor == SVC_IGNORE ) + { + GL_State( GLS_SRCBLEND_ONE | GLS_DSTBLEND_ZERO | GLS_DEPTHMASK | GLS_DEPTHFUNC_EQUAL ); + } + else + { + // RB: this is a bit hacky: use additive blending to blend the normals + GL_State( GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE | GLS_DEPTHMASK | GLS_DEPTHFUNC_EQUAL ); + } +#endif + + DrawSingleInteraction( &inter, false, useIBL, false ); + } + inter.specularImage = surfaceStage->texture.image; + inter.vertexColor = surfaceStage->vertexColor; + SetupInteractionStage( surfaceStage, surfaceRegs, specularColor.ToFloatPtr( ), + inter.specularMatrix, inter.specularColor.ToFloatPtr( ) ); + break; + } + } + } + + // draw the final interaction +#if BLEND_NORMALS + if( inter.vertexColor == SVC_IGNORE ) + { + GL_State( GLS_SRCBLEND_ONE | GLS_DSTBLEND_ZERO | GLS_DEPTHMASK | GLS_DEPTHFUNC_EQUAL ); + } + else + { + // RB: this is a bit hacky: use additive blending to blend the normals + GL_State( GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE | GLS_DEPTHMASK | GLS_DEPTHFUNC_EQUAL ); + } +#endif + + DrawSingleInteraction( &inter, false, useIBL, false ); + + renderLog.CloseBlock( ); + } + + // disable blending + GL_State( GLS_SRCBLEND_ONE | GLS_DSTBLEND_ZERO | GLS_DEPTHMASK | GLS_DEPTHFUNC_EQUAL ); + SetFragmentParm( RENDERPARM_ALPHA_TEST, vec4_zero.ToFloatPtr( ) ); + + GL_SelectTexture( 0 ); + + if( fillGbuffer ) + { + // go back to main render target + if( previousFramebuffer != NULL ) + { + previousFramebuffer->Bind( ); + } + else + { + Framebuffer::Unbind( ); + } + } + + renderProgManager.Unbind( ); + + renderLog.CloseBlock( ); + renderLog.CloseMainBlock( ); +} + +nvrhi::GraphicsPipelineHandle GBufferFillPass::CreateGraphicsPipeline( nvrhi::IFramebuffer* framebuffer ) +{ + return nvrhi::GraphicsPipelineHandle( ); +} + +void GBufferFillPass::DrawElementsWithCounters( const drawSurf_t* surf ) +{ + // Get vertex buffer + const vertCacheHandle_t vbHandle = surf->ambientCache; + idVertexBuffer* vertexBuffer; + if( vertexCache.CacheIsStatic( vbHandle ) ) + { + vertexBuffer = &vertexCache.staticData.vertexBuffer; + } + else + { + const uint64 frameNum = ( int )( vbHandle >> VERTCACHE_FRAME_SHIFT ) & VERTCACHE_FRAME_MASK; + if( frameNum != ( ( vertexCache.currentFrame - 1 ) & VERTCACHE_FRAME_MASK ) ) + { + idLib::Warning( "RB_DrawElementsWithCounters, vertexBuffer == NULL" ); + return; + } + vertexBuffer = &vertexCache.frameData[vertexCache.drawListNum].vertexBuffer; + } + const uint vertOffset = ( uint )( vbHandle >> VERTCACHE_OFFSET_SHIFT ) & VERTCACHE_OFFSET_MASK; + auto currentVertexBuffer = vertexBuffer->GetAPIObject( ); + + // Get index buffer + const vertCacheHandle_t ibHandle = surf->indexCache; + idIndexBuffer* indexBuffer; + if( vertexCache.CacheIsStatic( ibHandle ) ) + { + indexBuffer = &vertexCache.staticData.indexBuffer; + } + else + { + const uint64 frameNum = ( int )( ibHandle >> VERTCACHE_FRAME_SHIFT ) & VERTCACHE_FRAME_MASK; + if( frameNum != ( ( vertexCache.currentFrame - 1 ) & VERTCACHE_FRAME_MASK ) ) + { + idLib::Warning( "RB_DrawElementsWithCounters, indexBuffer == NULL" ); + return; + } + indexBuffer = &vertexCache.frameData[vertexCache.drawListNum].indexBuffer; + } + const uint indexOffset = ( uint )( ibHandle >> VERTCACHE_OFFSET_SHIFT ) & VERTCACHE_OFFSET_MASK; + + RENDERLOG_PRINTF( "Binding Buffers: %p:%i %p:%i\n", vertexBuffer, vertOffset, indexBuffer, indexOffset ); + + auto currentIndexBuffer = ( nvrhi::IBuffer* )indexBuffer->GetAPIObject( ); + + if( currentIndexBuffer != ( nvrhi::IBuffer* )indexBuffer->GetAPIObject( ) || !r_useStateCaching.GetBool( ) ) + { + currentIndexBuffer = indexBuffer->GetAPIObject( ); + } + + if( !pipeline ) + { + nvrhi::GraphicsPipelineDesc psoDesc; + psoDesc.VS = vertexShader; + psoDesc.PS = pixelShader; + psoDesc.inputLayout = inputLayout; + psoDesc.bindingLayouts = { currentBindingLayout }; + psoDesc.primType = nvrhi::PrimitiveType::TriangleList; + currentRenderState.rasterState.enableScissor( ); + psoDesc.setRenderState( currentRenderState ); + + pipeline = device->createGraphicsPipeline( psoDesc, currentFramebuffer->GetApiObject( ) ); + } + + nvrhi::BindingSetDesc bindingSetDesc; + + if( renderProgManager.BindingLayoutType( ) == BINDING_LAYOUT_DEFAULT ) + { + bindingSetDesc + .addItem( nvrhi::BindingSetItem::ConstantBuffer( 0, renderProgManager.ConstantBuffer( ) ) ) + .addItem( nvrhi::BindingSetItem::Texture_SRV( 0, ( nvrhi::ITexture* )GetImageAt( 0 )->GetTextureID( ) ) ) + .addItem( nvrhi::BindingSetItem::Sampler( 0, ( nvrhi::ISampler* )GetImageAt( 0 )->GetSampler( ) ) ); + } + else if( renderProgManager.BindingLayoutType( ) == BINDING_LAYOUT_GBUFFER ) + { + bindingSetDesc + .addItem( nvrhi::BindingSetItem::ConstantBuffer( 0, renderProgManager.ConstantBuffer( ) ) ); + } + else if( renderProgManager.BindingLayoutType( ) == BINDING_LAYOUT_LIGHTGRID ) + { + bindingSetDesc + .addItem( nvrhi::BindingSetItem::ConstantBuffer( 0, renderProgManager.ConstantBuffer( ) ) ) + .addItem( nvrhi::BindingSetItem::Texture_SRV( 0, ( nvrhi::ITexture* )GetImageAt( 0 )->GetTextureID( ) ) ) + .addItem( nvrhi::BindingSetItem::Texture_SRV( 1, ( nvrhi::ITexture* )GetImageAt( 1 )->GetTextureID( ) ) ) + .addItem( nvrhi::BindingSetItem::Texture_SRV( 2, ( nvrhi::ITexture* )GetImageAt( 2 )->GetTextureID( ) ) ) + .addItem( nvrhi::BindingSetItem::Texture_SRV( 3, ( nvrhi::ITexture* )GetImageAt( 3 )->GetTextureID( ) ) ) + .addItem( nvrhi::BindingSetItem::Texture_SRV( 4, ( nvrhi::ITexture* )GetImageAt( 4 )->GetTextureID( ) ) ) + .addItem( nvrhi::BindingSetItem::Texture_SRV( 7, ( nvrhi::ITexture* )GetImageAt( 7 )->GetTextureID( ) ) ) + .addItem( nvrhi::BindingSetItem::Texture_SRV( 8, ( nvrhi::ITexture* )GetImageAt( 8 )->GetTextureID( ) ) ) + .addItem( nvrhi::BindingSetItem::Texture_SRV( 9, ( nvrhi::ITexture* )GetImageAt( 9 )->GetTextureID( ) ) ) + .addItem( nvrhi::BindingSetItem::Texture_SRV( 10, ( nvrhi::ITexture* )GetImageAt( 10 )->GetTextureID( ) ) ) + .addItem( nvrhi::BindingSetItem::Sampler( 0, ( nvrhi::ISampler* )GetImageAt( 0 )->GetSampler( ) ) ) + .addItem( nvrhi::BindingSetItem::Sampler( 1, ( nvrhi::ISampler* )GetImageAt( 1 )->GetSampler( ) ) ) + .addItem( nvrhi::BindingSetItem::Sampler( 2, ( nvrhi::ISampler* )GetImageAt( 2 )->GetSampler( ) ) ) + .addItem( nvrhi::BindingSetItem::Sampler( 3, ( nvrhi::ISampler* )GetImageAt( 3 )->GetSampler( ) ) ) + .addItem( nvrhi::BindingSetItem::Sampler( 4, ( nvrhi::ISampler* )GetImageAt( 4 )->GetSampler( ) ) ) + .addItem( nvrhi::BindingSetItem::Sampler( 7, ( nvrhi::ISampler* )GetImageAt( 7 )->GetSampler( ) ) ) + .addItem( nvrhi::BindingSetItem::Sampler( 8, ( nvrhi::ISampler* )GetImageAt( 8 )->GetSampler( ) ) ) + .addItem( nvrhi::BindingSetItem::Sampler( 9, ( nvrhi::ISampler* )GetImageAt( 9 )->GetSampler( ) ) ) + .addItem( nvrhi::BindingSetItem::Sampler( 10, ( nvrhi::ISampler* )GetImageAt( 10 )->GetSampler( ) ) ); + } + + currentBindingSet = bindingCache.GetOrCreateBindingSet( bindingSetDesc, currentBindingLayout ); + + renderProgManager.CommitConstantBuffer( commandList ); + + nvrhi::GraphicsState state; + state.bindings = { currentBindingSet }; + state.indexBuffer = { currentIndexBuffer, nvrhi::Format::R16_UINT, indexOffset }; + state.vertexBuffers = { { currentVertexBuffer, 0, vertOffset } }; + state.pipeline = currentPipeline; + state.framebuffer = currentFrameBuffer->GetApiObject( ); + + // TODO(Stephen): use currentViewport instead. + nvrhi::Viewport viewport; + viewport.minX = currentViewport.x1; + viewport.minY = currentViewport.y1; + viewport.maxX = currentViewport.x2; + viewport.maxY = currentViewport.y2; + viewport.minZ = currentViewport.zmin; + viewport.maxZ = currentViewport.zmax; + state.viewport.addViewportAndScissorRect( viewport ); + state.viewport.addScissorRect( nvrhi::Rect( currentScissor.x1, currentScissor.y1, currentScissor.x2, currentScissor.y2 ) ); + + commandList->setGraphicsState( state ); + + nvrhi::DrawArguments args; + args.vertexCount = surf->numIndexes; + commandList->drawIndexed( args ); + + // RB: added stats + pc.c_drawElements++; + pc.c_drawIndexes += surf->numIndexes; +} + +#endif + +void GBufferFillPass::SetupView( nvrhi::ICommandList* commandList, viewDef_t* viewDef ) +{ +} + +bool GBufferFillPass::SetupMaterial( const idMaterial* material, nvrhi::RasterCullMode cullMode, nvrhi::GraphicsState& state ) +{ + return false; +} + +void GBufferFillPass::SetupInputBuffers( const drawSurf_t* drawSurf, nvrhi::GraphicsState& state ) +{ +} + +void GBufferFillPass::SetPushConstants( nvrhi::ICommandList* commandList, nvrhi::GraphicsState& state, nvrhi::DrawArguments& args ) +{ +} diff --git a/neo/renderer/Passes/GBufferFillPass.h b/neo/renderer/Passes/GBufferFillPass.h new file mode 100644 index 00000000..caca40cd --- /dev/null +++ b/neo/renderer/Passes/GBufferFillPass.h @@ -0,0 +1,64 @@ +/* +=========================================================================== + +Doom 3 BFG Edition GPL Source Code +Copyright (C) 2022 Stephen Pridham + +This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). + +Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 BFG Edition Source Code. If not, see . + +In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ +#ifndef RENDERER_PASSES_GBUFFERFILLPASS_H_ +#define RENDERER_PASSES_GBUFFERFILLPASS_H_ + +#include "GeometryPasses.h" + +// "Light" G-Buffer that renders the normals of the geometry +class GBufferFillPass : IGeometryPass +{ +public: + + GBufferFillPass( ) = default; + virtual ~GBufferFillPass( ) = default; + + void Init( nvrhi::DeviceHandle deviceHandle ); + void RenderView( nvrhi::ICommandList* commandList, const drawSurf_t* const* drawSurfs, int numDrawSurfs, bool fillGbuffer ); + +protected: + + nvrhi::DeviceHandle device; + nvrhi::BindingLayoutHandle geometryBindingLayout; + nvrhi::BindingLayoutHandle texturedBindingLayout; + nvrhi::BindingSetDesc geometryBindingSetDesc; + + nvrhi::GraphicsPipelineHandle CreateGraphicsPipeline( nvrhi::IFramebuffer* framebuffer ); + + void DrawElementsWithCounters( const drawSurf_t* surf ); + +public: + + void SetupView( nvrhi::ICommandList* commandList, viewDef_t* viewDef ) override; + bool SetupMaterial( const idMaterial* material, nvrhi::RasterCullMode cullMode, nvrhi::GraphicsState& state ) override; + void SetupInputBuffers( const drawSurf_t* drawSurf, nvrhi::GraphicsState& state ) override; + void SetPushConstants( nvrhi::ICommandList* commandList, nvrhi::GraphicsState& state, nvrhi::DrawArguments& args ) override; + +}; + +#endif \ No newline at end of file diff --git a/neo/renderer/Passes/GeometryPasses.cpp b/neo/renderer/Passes/GeometryPasses.cpp new file mode 100644 index 00000000..b53d77ac --- /dev/null +++ b/neo/renderer/Passes/GeometryPasses.cpp @@ -0,0 +1,804 @@ +/* +=========================================================================== + +Doom 3 BFG Edition GPL Source Code +Copyright (C) 2022 Stephen Pridham + +This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). + +Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 BFG Edition Source Code. If not, see . + +In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ +#include +#pragma hdrstop + +#include "renderer/RenderCommon.h" + +#include "GeometryPasses.h" + +#include "nvrhi/utils.h" + + +static ID_INLINE void SetVertexParm( renderParm_t rp, const float* value ) +{ + renderProgManager.SetUniformValue( rp, value ); +} + +static ID_INLINE void SetVertexParms( renderParm_t rp, const float* value, int num ) +{ + for( int i = 0; i < num; i++ ) + { + renderProgManager.SetUniformValue( ( renderParm_t )( rp + i ), value + ( i * 4 ) ); + } +} + +static ID_INLINE void SetFragmentParm( renderParm_t rp, const float* value ) +{ + renderProgManager.SetUniformValue( rp, value ); +} + +static void RB_GetShaderTextureMatrix( const float* shaderRegisters, const textureStage_t* texture, float matrix[16] ) +{ + matrix[0 * 4 + 0] = shaderRegisters[texture->matrix[0][0]]; + matrix[1 * 4 + 0] = shaderRegisters[texture->matrix[0][1]]; + matrix[2 * 4 + 0] = 0.0f; + matrix[3 * 4 + 0] = shaderRegisters[texture->matrix[0][2]]; + + matrix[0 * 4 + 1] = shaderRegisters[texture->matrix[1][0]]; + matrix[1 * 4 + 1] = shaderRegisters[texture->matrix[1][1]]; + matrix[2 * 4 + 1] = 0.0f; + matrix[3 * 4 + 1] = shaderRegisters[texture->matrix[1][2]]; + + // we attempt to keep scrolls from generating incredibly large texture values, but + // center rotations and center scales can still generate offsets that need to be > 1 + if( matrix[3 * 4 + 0] < -40.0f || matrix[12] > 40.0f ) + { + matrix[3 * 4 + 0] -= ( int )matrix[3 * 4 + 0]; + } + if( matrix[13] < -40.0f || matrix[13] > 40.0f ) + { + matrix[13] -= ( int )matrix[13]; + } + + matrix[0 * 4 + 2] = 0.0f; + matrix[1 * 4 + 2] = 0.0f; + matrix[2 * 4 + 2] = 1.0f; + matrix[3 * 4 + 2] = 0.0f; + + matrix[0 * 4 + 3] = 0.0f; + matrix[1 * 4 + 3] = 0.0f; + matrix[2 * 4 + 3] = 0.0f; + matrix[3 * 4 + 3] = 1.0f; +} + +static void RB_LoadShaderTextureMatrix( const float* shaderRegisters, const textureStage_t* texture ) +{ + float texS[4] = { 1.0f, 0.0f, 0.0f, 0.0f }; + float texT[4] = { 0.0f, 1.0f, 0.0f, 0.0f }; + + if( texture->hasMatrix ) + { + float matrix[16]; + RB_GetShaderTextureMatrix( shaderRegisters, texture, matrix ); + texS[0] = matrix[0 * 4 + 0]; + texS[1] = matrix[1 * 4 + 0]; + texS[2] = matrix[2 * 4 + 0]; + texS[3] = matrix[3 * 4 + 0]; + + texT[0] = matrix[0 * 4 + 1]; + texT[1] = matrix[1 * 4 + 1]; + texT[2] = matrix[2 * 4 + 1]; + texT[3] = matrix[3 * 4 + 1]; + + RENDERLOG_PRINTF( "Setting Texture Matrix\n" ); + renderLog.Indent( ); + RENDERLOG_PRINTF( "Texture Matrix S : %4.3f, %4.3f, %4.3f, %4.3f\n", texS[0], texS[1], texS[2], texS[3] ); + RENDERLOG_PRINTF( "Texture Matrix T : %4.3f, %4.3f, %4.3f, %4.3f\n", texT[0], texT[1], texT[2], texT[3] ); + renderLog.Outdent( ); + } + + SetVertexParm( RENDERPARM_TEXTUREMATRIX_S, texS ); + SetVertexParm( RENDERPARM_TEXTUREMATRIX_T, texT ); +} + +void IGeometryPass::PrepareStageTexturing( const shaderStage_t* pStage, const drawSurf_t* surf ) +{ + float useTexGenParm[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; + + // set the texture matrix if needed + RB_LoadShaderTextureMatrix( surf->shaderRegisters, &pStage->texture ); + + // texgens + if( pStage->texture.texgen == TG_REFLECT_CUBE ) + { + + // see if there is also a bump map specified + const shaderStage_t* bumpStage = surf->material->GetBumpStage( ); + if( bumpStage != NULL ) + { + // per-pixel reflection mapping with bump mapping + GL_SelectTexture( 1 ); + bumpStage->texture.image->Bind( ); + + GL_SelectTexture( 0 ); + + RENDERLOG_PRINTF( "TexGen: TG_REFLECT_CUBE: Bumpy Environment\n" ); + if( surf->jointCache ) + { + renderProgManager.BindShader_BumpyEnvironmentSkinned( ); + } + else + { + renderProgManager.BindShader_BumpyEnvironment( ); + } + } + else + { + RENDERLOG_PRINTF( "TexGen: TG_REFLECT_CUBE: Environment\n" ); + if( surf->jointCache ) + { + renderProgManager.BindShader_EnvironmentSkinned( ); + } + else + { + renderProgManager.BindShader_Environment( ); + } + } + + } + else if( pStage->texture.texgen == TG_SKYBOX_CUBE ) + { + renderProgManager.BindShader_SkyBox( ); + } + else if( pStage->texture.texgen == TG_WOBBLESKY_CUBE ) + { + + const int* parms = surf->material->GetTexGenRegisters( ); + + float wobbleDegrees = surf->shaderRegisters[parms[0]] * ( idMath::PI / 180.0f ); + float wobbleSpeed = surf->shaderRegisters[parms[1]] * ( 2.0f * idMath::PI / 60.0f ); + float rotateSpeed = surf->shaderRegisters[parms[2]] * ( 2.0f * idMath::PI / 60.0f ); + + idVec3 axis[3]; + { + // very ad-hoc "wobble" transform + float s, c; + idMath::SinCos( wobbleSpeed * viewDef->renderView.time[0] * 0.001f, s, c ); + + float ws, wc; + idMath::SinCos( wobbleDegrees, ws, wc ); + + axis[2][0] = ws * c; + axis[2][1] = ws * s; + axis[2][2] = wc; + + axis[1][0] = -s * s * ws; + axis[1][2] = -s * ws * ws; + axis[1][1] = idMath::Sqrt( idMath::Fabs( 1.0f - ( axis[1][0] * axis[1][0] + axis[1][2] * axis[1][2] ) ) ); + + // make the second vector exactly perpendicular to the first + axis[1] -= ( axis[2] * axis[1] ) * axis[2]; + axis[1].Normalize( ); + + // construct the third with a cross + axis[0].Cross( axis[1], axis[2] ); + } + + // add the rotate + float rs, rc; + idMath::SinCos( rotateSpeed * viewDef->renderView.time[0] * 0.001f, rs, rc ); + + float transform[12]; + transform[0 * 4 + 0] = axis[0][0] * rc + axis[1][0] * rs; + transform[0 * 4 + 1] = axis[0][1] * rc + axis[1][1] * rs; + transform[0 * 4 + 2] = axis[0][2] * rc + axis[1][2] * rs; + transform[0 * 4 + 3] = 0.0f; + + transform[1 * 4 + 0] = axis[1][0] * rc - axis[0][0] * rs; + transform[1 * 4 + 1] = axis[1][1] * rc - axis[0][1] * rs; + transform[1 * 4 + 2] = axis[1][2] * rc - axis[0][2] * rs; + transform[1 * 4 + 3] = 0.0f; + + transform[2 * 4 + 0] = axis[2][0]; + transform[2 * 4 + 1] = axis[2][1]; + transform[2 * 4 + 2] = axis[2][2]; + transform[2 * 4 + 3] = 0.0f; + + SetVertexParms( RENDERPARM_WOBBLESKY_X, transform, 3 ); + renderProgManager.BindShader_WobbleSky( ); + + } + else if( ( pStage->texture.texgen == TG_SCREEN ) || ( pStage->texture.texgen == TG_SCREEN2 ) ) + { + + useTexGenParm[0] = 1.0f; + useTexGenParm[1] = 1.0f; + useTexGenParm[2] = 1.0f; + useTexGenParm[3] = 1.0f; + + float mat[16]; + R_MatrixMultiply( surf->space->modelViewMatrix, viewDef->projectionMatrix, mat ); + + RENDERLOG_PRINTF( "TexGen : %s\n", ( pStage->texture.texgen == TG_SCREEN ) ? "TG_SCREEN" : "TG_SCREEN2" ); + renderLog.Indent( ); + + float plane[4]; + plane[0] = mat[0 * 4 + 0]; + plane[1] = mat[1 * 4 + 0]; + plane[2] = mat[2 * 4 + 0]; + plane[3] = mat[3 * 4 + 0]; + SetVertexParm( RENDERPARM_TEXGEN_0_S, plane ); + RENDERLOG_PRINTF( "TEXGEN_S = %4.3f, %4.3f, %4.3f, %4.3f\n", plane[0], plane[1], plane[2], plane[3] ); + + plane[0] = mat[0 * 4 + 1]; + plane[1] = mat[1 * 4 + 1]; + plane[2] = mat[2 * 4 + 1]; + plane[3] = mat[3 * 4 + 1]; + SetVertexParm( RENDERPARM_TEXGEN_0_T, plane ); + RENDERLOG_PRINTF( "TEXGEN_T = %4.3f, %4.3f, %4.3f, %4.3f\n", plane[0], plane[1], plane[2], plane[3] ); + + plane[0] = mat[0 * 4 + 3]; + plane[1] = mat[1 * 4 + 3]; + plane[2] = mat[2 * 4 + 3]; + plane[3] = mat[3 * 4 + 3]; + SetVertexParm( RENDERPARM_TEXGEN_0_Q, plane ); + RENDERLOG_PRINTF( "TEXGEN_Q = %4.3f, %4.3f, %4.3f, %4.3f\n", plane[0], plane[1], plane[2], plane[3] ); + + renderLog.Outdent( ); + + } + else if( pStage->texture.texgen == TG_DIFFUSE_CUBE ) + { + // As far as I can tell, this is never used + idLib::Warning( "Using Diffuse Cube! Please contact Brian!" ); + + } + else if( pStage->texture.texgen == TG_GLASSWARP ) + { + // As far as I can tell, this is never used + idLib::Warning( "Using GlassWarp! Please contact Brian!" ); + } + + SetVertexParm( RENDERPARM_TEXGEN_0_ENABLED, useTexGenParm ); +} + +void IGeometryPass::FinishStageTexturing( const shaderStage_t* stage, const drawSurf_t* surf ) +{ + if( stage->texture.cinematic ) + { + // unbind the extra bink textures + GL_SelectTexture( 0 ); + } + + if( stage->texture.texgen == TG_REFLECT_CUBE ) + { + // see if there is also a bump map specified + const shaderStage_t* bumpStage = surf->material->GetBumpStage( ); + if( bumpStage != NULL ) + { + // per-pixel reflection mapping with bump mapping + GL_SelectTexture( 0 ); + } + else + { + // per-pixel reflection mapping without bump mapping + } + renderProgManager.Unbind( ); + } +} + +bool IGeometryPass::GL_State( uint64 stateBits, bool forceGlState ) +{ + uint64 diff = stateBits ^ glStateBits; + + if( !r_useStateCaching.GetBool( ) || forceGlState ) + { + // make sure everything is set all the time, so we + // can see if our delta checking is screwing up + diff = 0xFFFFFFFFFFFFFFFF; + } + else if( diff == 0 ) + { + return false; + } + + // Reset pipeline + auto& currentBlendState = pipelineDesc.renderState.blendState; + auto& currentDepthStencilState = pipelineDesc.renderState.depthStencilState; + auto& currentRasterState = pipelineDesc.renderState.rasterState; + + // + // culling + // + if( diff & ( GLS_CULL_BITS ) )//| GLS_MIRROR_VIEW ) ) + { + switch( stateBits & GLS_CULL_BITS ) + { + case GLS_CULL_TWOSIDED: + currentRasterState.setCullNone( ); + break; + + case GLS_CULL_BACKSIDED: + if( viewDef != NULL && viewDef->isMirror ) + { + stateBits |= GLS_MIRROR_VIEW; + currentRasterState.setCullFront( ); + } + else + { + currentRasterState.setCullBack( ); + } + break; + + case GLS_CULL_FRONTSIDED: + default: + if( viewDef != NULL && viewDef->isMirror ) + { + stateBits |= GLS_MIRROR_VIEW; + currentRasterState.setCullBack( ); + } + else + { + currentRasterState.setCullFront( ); + } + break; + } + } + + // + // check depthFunc bits + // + if( diff & GLS_DEPTHFUNC_BITS ) + { + switch( stateBits & GLS_DEPTHFUNC_BITS ) + { + case GLS_DEPTHFUNC_EQUAL: + currentDepthStencilState.depthFunc = nvrhi::ComparisonFunc::Equal; + break; + case GLS_DEPTHFUNC_ALWAYS: + currentDepthStencilState.depthFunc = nvrhi::ComparisonFunc::Always; + break; + case GLS_DEPTHFUNC_LESS: + currentDepthStencilState.depthFunc = nvrhi::ComparisonFunc::Less; + break; + case GLS_DEPTHFUNC_GREATER: + currentDepthStencilState.depthFunc = nvrhi::ComparisonFunc::Greater; + break; + } + } + + nvrhi::BlendState::RenderTarget renderTarget; + + // + // check blend bits + // + if( diff & ( GLS_SRCBLEND_BITS | GLS_DSTBLEND_BITS ) ) + { + nvrhi::BlendFactor srcFactor = nvrhi::BlendFactor::One; + nvrhi::BlendFactor dstFactor = nvrhi::BlendFactor::One; + + switch( stateBits & GLS_SRCBLEND_BITS ) + { + case GLS_SRCBLEND_ZERO: + srcFactor = nvrhi::BlendFactor::Zero; + break; + case GLS_SRCBLEND_ONE: + srcFactor = nvrhi::BlendFactor::One; + break; + case GLS_SRCBLEND_DST_COLOR: + srcFactor = nvrhi::BlendFactor::DstColor; + break; + case GLS_SRCBLEND_ONE_MINUS_DST_COLOR: + srcFactor = nvrhi::BlendFactor::OneMinusDstColor; + break; + case GLS_SRCBLEND_SRC_ALPHA: + srcFactor = nvrhi::BlendFactor::SrcAlpha; + break; + case GLS_SRCBLEND_ONE_MINUS_SRC_ALPHA: + srcFactor = nvrhi::BlendFactor::OneMinusSrcAlpha; + break; + case GLS_SRCBLEND_DST_ALPHA: + srcFactor = nvrhi::BlendFactor::DstAlpha; + break; + case GLS_SRCBLEND_ONE_MINUS_DST_ALPHA: + srcFactor = nvrhi::BlendFactor::OneMinusDstAlpha; + break; + default: + assert( !"GL_State: invalid src blend state bits\n" ); + break; + } + + switch( stateBits & GLS_DSTBLEND_BITS ) + { + case GLS_DSTBLEND_ZERO: + dstFactor = nvrhi::BlendFactor::Zero; + break; + case GLS_DSTBLEND_ONE: + dstFactor = nvrhi::BlendFactor::One; + break; + case GLS_DSTBLEND_SRC_COLOR: + dstFactor = nvrhi::BlendFactor::SrcColor; + break; + case GLS_DSTBLEND_ONE_MINUS_SRC_COLOR: + dstFactor = nvrhi::BlendFactor::OneMinusSrcColor; + break; + case GLS_DSTBLEND_SRC_ALPHA: + dstFactor = nvrhi::BlendFactor::SrcAlpha; + break; + case GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA: + dstFactor = nvrhi::BlendFactor::OneMinusSrcAlpha; + break; + case GLS_DSTBLEND_DST_ALPHA: + dstFactor = nvrhi::BlendFactor::DstAlpha; + break; + case GLS_DSTBLEND_ONE_MINUS_DST_ALPHA: + dstFactor = nvrhi::BlendFactor::OneMinusDstAlpha; + break; + default: + assert( !"GL_State: invalid dst blend state bits\n" ); + break; + } + + // Only actually update GL's blend func if blending is enabled. + if( srcFactor == nvrhi::BlendFactor::One && dstFactor == nvrhi::BlendFactor::Zero ) + { + renderTarget.disableBlend( ); + } + else + { + currentBlendState.setAlphaToCoverageEnable( true ); + nvrhi::BlendState::RenderTarget renderTarget; + renderTarget.enableBlend( ); + renderTarget.setSrcBlend( srcFactor ); + renderTarget.setDestBlend( dstFactor ); + } + } + + // + // check depthmask + // + if( diff & GLS_DEPTHMASK ) + { + if( stateBits & GLS_DEPTHMASK ) + { + currentDepthStencilState.disableDepthWrite( ); + currentDepthStencilState.disableDepthTest( ); + } + else + { + currentDepthStencilState.enableDepthWrite( ); + currentDepthStencilState.enableDepthTest( ); + } + } + + // + // check colormask + // + if( diff & ( GLS_REDMASK | GLS_GREENMASK | GLS_BLUEMASK | GLS_ALPHAMASK ) ) + { + nvrhi::ColorMask mask{ nvrhi::ColorMask::All }; + + if( stateBits & GLS_REDMASK ) + { + mask = mask & ~nvrhi::ColorMask::Red; + } + if( stateBits & GLS_GREENMASK ) + { + mask = mask & ~nvrhi::ColorMask::Green; + } + if( stateBits & GLS_BLUEMASK ) + { + mask = mask & ~nvrhi::ColorMask::Blue; + } + if( stateBits & GLS_ALPHAMASK ) + { + mask = mask & ~nvrhi::ColorMask::Alpha; + } + + renderTarget.setColorWriteMask( mask ); + } + + currentBlendState.setRenderTarget( 0, renderTarget ); + + // + // fill/line mode + // + if( diff & GLS_POLYMODE_LINE ) + { + if( stateBits & GLS_POLYMODE_LINE ) + { + currentRasterState.setFillMode( nvrhi::RasterFillMode::Line ); + currentRasterState.setCullNone( ); + } + else + { + currentRasterState.setCullNone( ); + currentRasterState.setFillMode( nvrhi::RasterFillMode::Fill ); + } + } + + // + // polygon offset + // + if( diff & GLS_POLYGON_OFFSET ) + { + if( stateBits & GLS_POLYGON_OFFSET ) + { + currentRasterState.enableQuadFill( ); + } + else + { + currentRasterState.disableQuadFill( ); + } + } + + nvrhi::DepthStencilState::StencilOpDesc stencilOp; + + // + // stencil + // + if( diff & ( GLS_STENCIL_FUNC_BITS | GLS_STENCIL_OP_BITS ) ) + { + if( ( stateBits & ( GLS_STENCIL_FUNC_BITS | GLS_STENCIL_OP_BITS ) ) != 0 ) + { + currentDepthStencilState.enableStencil( ); + currentDepthStencilState.enableDepthWrite( ); + } + else + { + currentDepthStencilState.disableStencil( ); + //currentDepthStencilState.disableDepthWrite( ); + } + } + if( diff & ( GLS_STENCIL_FUNC_BITS | GLS_STENCIL_FUNC_REF_BITS | GLS_STENCIL_FUNC_MASK_BITS ) ) + { + GLuint ref = GLuint( ( stateBits & GLS_STENCIL_FUNC_REF_BITS ) >> GLS_STENCIL_FUNC_REF_SHIFT ); + GLuint mask = GLuint( ( stateBits & GLS_STENCIL_FUNC_MASK_BITS ) >> GLS_STENCIL_FUNC_MASK_SHIFT ); + GLenum func = 0; + + currentDepthStencilState.setStencilRefValue( ( stateBits & GLS_STENCIL_FUNC_REF_BITS ) >> GLS_STENCIL_FUNC_REF_SHIFT ); + currentDepthStencilState.setStencilReadMask( ( stateBits & GLS_STENCIL_FUNC_MASK_BITS ) >> GLS_STENCIL_FUNC_MASK_SHIFT ); + currentDepthStencilState.setStencilWriteMask( ( stateBits & GLS_STENCIL_FUNC_MASK_BITS ) >> GLS_STENCIL_FUNC_MASK_SHIFT ); + + switch( stateBits & GLS_STENCIL_FUNC_BITS ) + { + case GLS_STENCIL_FUNC_NEVER: + stencilOp.setStencilFunc( nvrhi::ComparisonFunc::Never ); + break; + case GLS_STENCIL_FUNC_LESS: + stencilOp.setStencilFunc( nvrhi::ComparisonFunc::Less ); + break; + case GLS_STENCIL_FUNC_EQUAL: + stencilOp.setStencilFunc( nvrhi::ComparisonFunc::Equal ); + break; + case GLS_STENCIL_FUNC_LEQUAL: + stencilOp.setStencilFunc( nvrhi::ComparisonFunc::LessOrEqual ); + break; + case GLS_STENCIL_FUNC_GREATER: + stencilOp.setStencilFunc( nvrhi::ComparisonFunc::Greater ); + break; + case GLS_STENCIL_FUNC_NOTEQUAL: + stencilOp.setStencilFunc( nvrhi::ComparisonFunc::NotEqual ); + break; + case GLS_STENCIL_FUNC_GEQUAL: + stencilOp.setStencilFunc( nvrhi::ComparisonFunc::GreaterOrEqual ); + break; + case GLS_STENCIL_FUNC_ALWAYS: + stencilOp.setStencilFunc( nvrhi::ComparisonFunc::Always ); + break; + } + } + if( diff & ( GLS_STENCIL_OP_FAIL_BITS | GLS_STENCIL_OP_ZFAIL_BITS | GLS_STENCIL_OP_PASS_BITS ) ) + { + GLenum sFail = 0; + GLenum zFail = 0; + GLenum pass = 0; + + switch( stateBits & GLS_STENCIL_OP_FAIL_BITS ) + { + case GLS_STENCIL_OP_FAIL_KEEP: + stencilOp.setFailOp( nvrhi::StencilOp::Keep ); + break; + case GLS_STENCIL_OP_FAIL_ZERO: + stencilOp.setFailOp( nvrhi::StencilOp::Zero ); + break; + case GLS_STENCIL_OP_FAIL_REPLACE: + stencilOp.setFailOp( nvrhi::StencilOp::Replace ); + break; + case GLS_STENCIL_OP_FAIL_INCR: + stencilOp.setFailOp( nvrhi::StencilOp::IncrementAndClamp ); + break; + case GLS_STENCIL_OP_FAIL_DECR: + stencilOp.setFailOp( nvrhi::StencilOp::DecrementAndClamp ); + break; + case GLS_STENCIL_OP_FAIL_INVERT: + stencilOp.setFailOp( nvrhi::StencilOp::Invert ); + break; + case GLS_STENCIL_OP_FAIL_INCR_WRAP: + stencilOp.setFailOp( nvrhi::StencilOp::IncrementAndWrap ); + break; + case GLS_STENCIL_OP_FAIL_DECR_WRAP: + stencilOp.setFailOp( nvrhi::StencilOp::DecrementAndWrap ); + break; + } + switch( stateBits & GLS_STENCIL_OP_ZFAIL_BITS ) + { + case GLS_STENCIL_OP_ZFAIL_KEEP: + stencilOp.setDepthFailOp( nvrhi::StencilOp::Keep ); + break; + case GLS_STENCIL_OP_ZFAIL_ZERO: + stencilOp.setDepthFailOp( nvrhi::StencilOp::Zero ); + break; + case GLS_STENCIL_OP_ZFAIL_REPLACE: + stencilOp.setDepthFailOp( nvrhi::StencilOp::Replace ); + break; + case GLS_STENCIL_OP_ZFAIL_INCR: + stencilOp.setDepthFailOp( nvrhi::StencilOp::IncrementAndClamp ); + break; + case GLS_STENCIL_OP_ZFAIL_DECR: + stencilOp.setDepthFailOp( nvrhi::StencilOp::DecrementAndClamp ); + break; + case GLS_STENCIL_OP_ZFAIL_INVERT: + stencilOp.setDepthFailOp( nvrhi::StencilOp::Invert ); + break; + case GLS_STENCIL_OP_ZFAIL_INCR_WRAP: + stencilOp.setDepthFailOp( nvrhi::StencilOp::IncrementAndWrap ); + break; + case GLS_STENCIL_OP_ZFAIL_DECR_WRAP: + stencilOp.setDepthFailOp( nvrhi::StencilOp::DecrementAndWrap ); + break; + } + switch( stateBits & GLS_STENCIL_OP_PASS_BITS ) + { + case GLS_STENCIL_OP_PASS_KEEP: + stencilOp.setPassOp( nvrhi::StencilOp::Keep ); + break; + case GLS_STENCIL_OP_PASS_ZERO: + stencilOp.setPassOp( nvrhi::StencilOp::Zero ); + break; + case GLS_STENCIL_OP_PASS_REPLACE: + stencilOp.setPassOp( nvrhi::StencilOp::Replace ); + break; + case GLS_STENCIL_OP_PASS_INCR: + stencilOp.setPassOp( nvrhi::StencilOp::IncrementAndClamp ); + break; + case GLS_STENCIL_OP_PASS_DECR: + stencilOp.setPassOp( nvrhi::StencilOp::DecrementAndClamp ); + break; + case GLS_STENCIL_OP_PASS_INVERT: + stencilOp.setPassOp( nvrhi::StencilOp::Invert ); + break; + case GLS_STENCIL_OP_PASS_INCR_WRAP: + stencilOp.setPassOp( nvrhi::StencilOp::IncrementAndWrap ); + break; + case GLS_STENCIL_OP_PASS_DECR_WRAP: + stencilOp.setPassOp( nvrhi::StencilOp::DecrementAndWrap ); + break; + } + } + + currentDepthStencilState.setFrontFaceStencil( stencilOp ); + + glStateBits = stateBits; + pipeline = nullptr; +} + +void IGeometryPass::GL_SelectTexture( int imageParm ) +{ + currentImageParm = imageParm; +} + +void IGeometryPass::GL_BindTexture( idImage* img ) +{ + imageParms[currentImageParm] = img; +} + +void IGeometryPass::GL_BindFramebuffer( Framebuffer* framebuffer ) +{ + if( currentFramebuffer != framebuffer ) + { + previousFramebuffer = currentFramebuffer; + pipeline = nullptr; + } + currentFramebuffer = framebuffer; +} + +void IGeometryPass::GL_BindGraphicsShader( int shaderIndex ) +{ + nvrhi::ShaderHandle shader = renderProgManager.GetShader( shaderIndex ); + if( shader->getDesc( ).shaderType == nvrhi::ShaderType::Vertex ) + { + if( pipelineDesc.VS != shader ) + { + pipeline = nullptr; + } + + pipelineDesc.setVertexShader( shader ); + } + + if( shader->getDesc( ).shaderType == nvrhi::ShaderType::Pixel ) + { + if( pipelineDesc.PS != shader ) + { + pipeline = nullptr; + } + + pipelineDesc.setPixelShader( shader ); + } +} + +void IGeometryPass::GL_DepthBoundsTest( const float zmin, const float zmax ) +{ +} + +void IGeometryPass::GL_PolygonOffset( float scale, float bias ) +{ + pipelineDesc.renderState.rasterState.setSlopeScaleDepthBias( scale ).setDepthBias( bias ); + pipeline = nullptr; +} + +void IGeometryPass::GL_Viewport( int x, int y, int w, int h ) +{ + currentViewport.Clear( ); + currentViewport.AddPoint( x, y ); + currentViewport.AddPoint( x + w, y + h ); +} + +void IGeometryPass::GL_Scissor( int x, int y, int w, int h ) +{ + currentScissor.Clear( ); + currentScissor.AddPoint( x, y ); + currentScissor.AddPoint( x + w, y + h ); +} + +void IGeometryPass::GL_Color( const idVec4 color ) +{ + // TODO(Stephen): Hold local copy and then set in a constant buffer later. + float parm[4]; + parm[0] = idMath::ClampFloat( 0.0f, 1.0f, color.x ); + parm[1] = idMath::ClampFloat( 0.0f, 1.0f, color.y ); + parm[2] = idMath::ClampFloat( 0.0f, 1.0f, color.z ); + parm[3] = idMath::ClampFloat( 0.0f, 1.0f, color.w ); + renderProgManager.SetRenderParm( RENDERPARM_COLOR, parm ); +} + +void IGeometryPass::GL_ClearColor( const idVec4 color ) +{ + clearColor = color; +} + +void IGeometryPass::GL_ClearDepthStencilValue( float depthValue, byte stencilValue ) +{ + depthClearValue = depthValue; + stencilClearValue = stencilValue; +} + +void IGeometryPass::GL_ClearColor( nvrhi::ICommandList* commandList, int attachmentIndex ) +{ + nvrhi::utils::ClearColorAttachment( + commandList, + currentFramebuffer->GetApiObject( ), + attachmentIndex, + nvrhi::Color( clearColor.x, clearColor.y, clearColor.z, clearColor.w ) ); +} + +void IGeometryPass::GL_ClearDepthStencil( nvrhi::ICommandList* commandList ) +{ + nvrhi::utils::ClearDepthStencilAttachment( commandList, currentFramebuffer->GetApiObject( ), depthClearValue, stencilClearValue ); +} diff --git a/neo/renderer/Passes/GeometryPasses.h b/neo/renderer/Passes/GeometryPasses.h new file mode 100644 index 00000000..eeb527a8 --- /dev/null +++ b/neo/renderer/Passes/GeometryPasses.h @@ -0,0 +1,95 @@ +/* +=========================================================================== + +Doom 3 BFG Edition GPL Source Code +Copyright (C) 2022 Stephen Pridham + +This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). + +Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 BFG Edition Source Code. If not, see . + +In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#ifndef RENDERER_PASSES_GEOMETRYPASSES_H_ +#define RENDERER_PASSES_GEOMETRYPASSES_H_ + +constexpr std::size_t MAX_IMAGE_PARMS = 16; + +class IGeometryPass +{ +public: + virtual ~IGeometryPass( ) = default; + + virtual void SetupView( nvrhi::ICommandList* commandList, viewDef_t* viewDef ) = 0; + virtual bool SetupMaterial( const idMaterial* material, nvrhi::RasterCullMode cullMode, nvrhi::GraphicsState& state ) = 0; + virtual void SetupInputBuffers( const drawSurf_t* drawSurf, nvrhi::GraphicsState& state ) = 0; + virtual void SetPushConstants( nvrhi::ICommandList* commandList, nvrhi::GraphicsState& state, nvrhi::DrawArguments& args ) = 0; + +protected: + + void PrepareStageTexturing( const shaderStage_t* stage, const drawSurf_t* surf ); + void FinishStageTexturing( const shaderStage_t* stage, const drawSurf_t* surf ); + +protected: + + int currentImageParm = 0; + idArray< idImage*, MAX_IMAGE_PARMS > imageParms; + uint64 glStateBits; + nvrhi::GraphicsPipelineDesc pipelineDesc; + nvrhi::GraphicsPipelineHandle pipeline; + BindingCache bindingCache; + Framebuffer* previousFramebuffer; + Framebuffer* currentFramebuffer; + const viewDef_t* viewDef; + const viewEntity_t* currentSpace; + idScreenRect currentViewport; + idScreenRect currentScissor; + idVec4 clearColor; + float depthClearValue; + byte stencilClearValue; + + // Updates state to bits in stateBits. Only updates the different bits. + bool GL_State( uint64 stateBits, bool forceGlState = false ); + void GL_SelectTexture( int textureNum ); + void GL_BindTexture( idImage* img ); + void GL_BindFramebuffer( Framebuffer* framebuffer ); + void GL_BindGraphicsShader( int shader ); + void GL_DepthBoundsTest( const float zmin, const float zmax ); + void GL_PolygonOffset( float scale, float bias ); + void GL_Viewport( int x, int y, int w, int h ); + void GL_Scissor( int x, int y, int w, int h ); + void GL_Color( const idVec4 color ); + void GL_ClearColor( const idVec4 color ); + void GL_ClearDepthStencilValue( float depthValue, byte stencilValue = 0xF ); + void GL_ClearColor( nvrhi::ICommandList* commandList, int attachmentIndex = 0 ); + void GL_ClearDepthStencil( nvrhi::ICommandList* commandList ); + + ID_INLINE uint64 GL_GetCurrentState( ) const + { + return glStateBits; + } + + ID_INLINE void GL_ViewportAndScissor( int x, int y, int w, int h ) + { + GL_Viewport( x, y, w, h ); + GL_Scissor( x, y, w, h ); + } +}; + +#endif \ No newline at end of file diff --git a/neo/renderer/Passes/MipMapGenPass.cpp b/neo/renderer/Passes/MipMapGenPass.cpp new file mode 100644 index 00000000..44d2101c --- /dev/null +++ b/neo/renderer/Passes/MipMapGenPass.cpp @@ -0,0 +1,278 @@ +/* +* Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +*/ + +#include "precompiled.h" +#pragma hdrstop + +#include "renderer/Passes/MipMapGenPass.h" + +#include "renderer/RenderCommon.h" + +#include +#include + +#define GROUP_SIZE 16 +#define LOD0_TILE_SIZE 8 +#define NUM_LODS 4 + +// Number of compute dispatches needed to reduce all the +// mip-levels at a maximum resolution of 16k : +// (uint)(std::ceil(std::log2f(16384)/NUM_LODS)) = 4 +#define MAX_PASSES 4 + +#define MODE_COLOR 0 +#define MODE_MIN 1 +#define MODE_MAX 2 +#define MODE_MINMAX 3 + +struct MipmmapGenConstants +{ + uint dispatch; + uint numLODs; + uint padding[2]; +}; + +// The compute shader reduces 'NUM_LODS' mip-levels at a time into an +// array of NUM_LODS bound UAVs. For textures that have a number +// of mip levels that is not a multiple of NUM_LODS, we need to bind +// "something" to the UAV slots : a set of static dummy NullTextures. +// +// The set of NullTextures is shared by all the MipMapGen compute pass +// instances and ownership is thread-safe. +// + +static nvrhi::TextureHandle createNullTexture( nvrhi::DeviceHandle device ) +{ + nvrhi::TextureDesc desc; + desc.width = 1; + desc.height = 1; + desc.isRenderTarget = false; + desc.useClearValue = false; + desc.sampleCount = 1; + desc.dimension = nvrhi::TextureDimension::Texture2D; + desc.keepInitialState = true; + desc.arraySize = 1; + desc.isUAV = true; + desc.format = nvrhi::Format::RGBA8_UNORM; + + return device->createTexture( desc ); +} + +struct MipMapGenPass::NullTextures +{ + + nvrhi::TextureHandle lod[NUM_LODS]; + + static std::shared_ptr get( nvrhi::DeviceHandle device ) + { + static std::mutex _mutex; + static std::weak_ptr _nullTextures; + + std::lock_guard lock( _mutex ); + + std::shared_ptr result = _nullTextures.lock(); + if( !result ) + { + result = std::make_shared(); + for( int i = 0; i < NUM_LODS; ++i ) + { + result->lod[i] = createNullTexture( device ); + } + _nullTextures = result; + } + return result; + } +}; + +MipMapGenPass::MipMapGenPass( + nvrhi::IDevice* device, + nvrhi::TextureHandle input, + Mode mode ) + : m_Device( device ) + , m_Texture( input ) + , m_BindingSets( MAX_PASSES ) + , m_BindingCache( ) +{ + assert( m_Texture ); + + m_BindingCache.Init( device ); + + m_NullTextures = NullTextures::get( m_Device ); + + uint nmipLevels = m_Texture->getDesc().mipLevels; + + // Shader + assert( mode >= 0 && mode <= MODE_MINMAX ); + + idList macros; + macros.Append( shaderMacro_t( "MODE", std::to_string( mode ).c_str( ) ) ); + int index = renderProgManager.FindShader( "builtin/mipmapgen", SHADER_STAGE_COMPUTE, "", macros, true ); + m_Shader = renderProgManager.GetShader( index ); + + // Constants + nvrhi::BufferDesc constantBufferDesc; + constantBufferDesc.byteSize = sizeof( MipmmapGenConstants ); + constantBufferDesc.isConstantBuffer = true; + constantBufferDesc.isVolatile = true; + constantBufferDesc.debugName = "MipMapGenPass/Constants"; + constantBufferDesc.maxVersions = c_MaxRenderPassConstantBufferVersions; + m_ConstantBuffer = m_Device->createBuffer( constantBufferDesc ); + + // BindingLayout + nvrhi::BindingLayoutDesc layoutDesc; + layoutDesc.visibility = nvrhi::ShaderType::Compute; + layoutDesc.bindings.push_back( nvrhi::BindingLayoutItem::VolatileConstantBuffer( 0 ) ), + layoutDesc.bindings.push_back( nvrhi::BindingLayoutItem::Texture_SRV( 0 ) ); + for( uint mipLevel = 1; mipLevel <= NUM_LODS; ++mipLevel ) + { + layoutDesc.bindings.push_back( nvrhi::BindingLayoutItem::Texture_UAV( mipLevel - 1 ) ); + } + m_BindingLayout = m_Device->createBindingLayout( layoutDesc ); + + // BindingSets + m_BindingSets.SetNum( MAX_PASSES ); + nvrhi::BindingSetDesc setDesc; + for( uint i = 0; i < ( uint )m_BindingSets.Num(); ++i ) + { + // Create a unique binding set for each compute pass + if( i * NUM_LODS >= nmipLevels ) + { + break; + } + + nvrhi::BindingSetHandle& set = m_BindingSets[i]; + + nvrhi::BindingSetDesc setDesc; + setDesc.bindings.push_back( nvrhi::BindingSetItem::ConstantBuffer( 0, m_ConstantBuffer ) ); + setDesc.bindings.push_back( nvrhi::BindingSetItem::Texture_SRV( 0, m_Texture, nvrhi::Format::UNKNOWN, nvrhi::TextureSubresourceSet( i * NUM_LODS, 1, 0, 1 ) ) ); + for( uint mipLevel = 1; mipLevel <= NUM_LODS; ++mipLevel ) + { + // output UAVs start after the mip-level UAV that was computed last + if( i * NUM_LODS + mipLevel < nmipLevels ) + { + setDesc.bindings.push_back( nvrhi::BindingSetItem::Texture_UAV( mipLevel - 1, m_Texture, nvrhi::Format::UNKNOWN, nvrhi::TextureSubresourceSet( i * NUM_LODS + mipLevel, 1, 0, 1 ) ) ); + } + else + { + setDesc.bindings.push_back( nvrhi::BindingSetItem::Texture_UAV( mipLevel - 1, m_NullTextures->lod[mipLevel - 1] ) ); + } + } + set = m_Device->createBindingSet( setDesc, m_BindingLayout ); + } + + nvrhi::ComputePipelineDesc computePipelineDesc; + computePipelineDesc.CS = m_Shader; + computePipelineDesc.bindingLayouts = { m_BindingLayout }; + + m_Pso = device->createComputePipeline( computePipelineDesc ); +} + +void MipMapGenPass::Dispatch( nvrhi::ICommandList* commandList, int maxLOD ) +{ + assert( m_Texture ); + + commandList->beginMarker( "MipMapGen::Dispatch" ); + + uint nmipLevels = m_Texture->getDesc().mipLevels; + if( maxLOD > 0 && maxLOD < ( int )nmipLevels ) + { + nmipLevels = maxLOD + 1; + } + + uint npasses = ( uint32_t )ceilf( ( float )nmipLevels / ( float )NUM_LODS ); + + uint width = m_Texture->getDesc().width, + height = m_Texture->getDesc().height; + + width = ( width + GROUP_SIZE - 1 ) / GROUP_SIZE; + height = ( height + GROUP_SIZE - 1 ) / GROUP_SIZE; + + for( uint i = 0; i < MAX_PASSES; ++i ) + { + if( i * NUM_LODS >= nmipLevels ) + { + break; + } + + MipmmapGenConstants constants = {}; + constants.numLODs = std::min( nmipLevels - i * NUM_LODS - 1, ( uint32_t )NUM_LODS ); + constants.dispatch = i; + commandList->writeBuffer( m_ConstantBuffer, &constants, sizeof( constants ) ); + + nvrhi::ComputeState state; + state.pipeline = m_Pso; + state.bindings = { m_BindingSets[i] }; + commandList->setComputeState( state ); + commandList->dispatch( width, height ); + } + + commandList->endMarker(); // "MipMapGen::Dispatch" +} + + +void MipMapGenPass::Display( CommonRenderPasses& commonPasses, nvrhi::ICommandList* commandList, nvrhi::IFramebuffer* target ) +{ + assert( m_Texture ); + + commandList->beginMarker( "MipMapGen::Display" ); + + nvrhi::Viewport viewport = nvrhi::Viewport( ( float )target->getFramebufferInfo().width, ( float )target->getFramebufferInfo().height ); + + idVec2 size( m_Texture->getDesc().width / 2.f, m_Texture->getDesc().height / 2.f ); + idVec2 corner( 10.f, uint( viewport.maxY ) - 10.f ); + + for( uint level = 0; level < m_Texture->getDesc().mipLevels - 1; ++level ) + { + BlitParameters blitParams; + blitParams.targetFramebuffer = target; + blitParams.sourceTexture = m_Texture; + blitParams.sourceMip = level + 1; + blitParams.targetViewport = nvrhi::Viewport( + corner.x, + corner.x + size.x, + corner.y - size.y, + corner.y, 0.f, 1.f + ); + + commonPasses.BlitTexture( commandList, blitParams, &m_BindingCache ); + + // spiral pattern + switch( level % 4 ) + { + case 0: + corner += idVec2( size.x + 10.f, 0.f ); + break; + case 1: + corner += idVec2( size.x / 2.f, -( size.y + 10.f ) ); + break; + case 2: + corner += idVec2( -size.x / 2.f - 10.f, -size.y / 2.f ); + break; + case 3: + corner += idVec2( 0.f, size.y ); + break; + } + size = idVec2( size.x / 2.f, size.y / 2.f ); + } + + commandList->endMarker(); // "MipMapGen::Display" +} \ No newline at end of file diff --git a/neo/renderer/Passes/MipMapGenPass.h b/neo/renderer/Passes/MipMapGenPass.h new file mode 100644 index 00000000..9bac8971 --- /dev/null +++ b/neo/renderer/Passes/MipMapGenPass.h @@ -0,0 +1,80 @@ +/* +=========================================================================== + +Doom 3 BFG Edition GPL Source Code +Copyright (C) 2022 Stephen Pridham + +This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). + +Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 BFG Edition Source Code. If not, see . + +In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ +#ifndef RENDERER_PASSES_MIPMAPGENPASS_H_ +#define RENDERER_PASSES_MIPMAPGENPASS_H_ + +class CommonRenderPasses; + +class MipMapGenPass +{ + +public: + + enum Mode : uint8_t + { + MODE_COLOR = 0, // bilinear reduction of RGB channels + MODE_MIN = 1, // min() reduction of R channel + MODE_MAX = 2, // max() reduction of R channel + MODE_MINMAX = 3, // min() and max() reductions of R channel into RG channels + }; + + // note : 'texture' must have been allocated with some mip levels + MipMapGenPass( + nvrhi::IDevice* device, + nvrhi::TextureHandle texture, + Mode mode = Mode::MODE_MAX ); + + // Dispatches reduction kernel : reads LOD 0 and populates + // LOD 1 and up + void Dispatch( nvrhi::ICommandList* commandList, int maxLOD = -1 ); + + // debug : blits mip-map levels in spiral pattern to 'target' + // (assumes 'target' texture resolution is high enough...) + void Display( + CommonRenderPasses& commonPasses, + nvrhi::ICommandList* commandList, + nvrhi::IFramebuffer* target ); + +private: + + nvrhi::DeviceHandle m_Device; + nvrhi::ShaderHandle m_Shader; + nvrhi::TextureHandle m_Texture; + nvrhi::BufferHandle m_ConstantBuffer; + nvrhi::BindingLayoutHandle m_BindingLayout; + idList m_BindingSets; + nvrhi::ComputePipelineHandle m_Pso; + + // Set of unique dummy textures - see details in class implementation + struct NullTextures; + std::shared_ptr m_NullTextures; + + BindingCache m_BindingCache; +}; + +#endif \ No newline at end of file diff --git a/neo/renderer/Passes/SsaoPass.cpp b/neo/renderer/Passes/SsaoPass.cpp new file mode 100644 index 00000000..41367cc0 --- /dev/null +++ b/neo/renderer/Passes/SsaoPass.cpp @@ -0,0 +1,280 @@ +/* +=========================================================================== + +Doom 3 BFG Edition GPL Source Code +Copyright (C) 2022 Stephen Pridham + +This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). + +Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 BFG Edition Source Code. If not, see . + +In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ +#include +#pragma hdrstop + +#include "renderer/RenderCommon.h" + +#include "SsaoPass.h" + +struct SsaoConstants +{ + idVec2 clipToView; + idVec2 invQuantizedGbufferSize; + + idVec2i quantizedViewportOrigin; + float amount; + float invBackgroundViewDepth; + float radiusWorld; + float surfaceBias; + + float radiusToScreen; + float powerExponent; +}; + +SsaoPass::SsaoPass( + nvrhi::IDevice* device, + const CreateParameters& params, + CommonRenderPasses* commonPasses ) + : commonRenderPasses( commonPasses ) + , m_Device( device ) +{ + nvrhi::BufferDesc constantBufferDesc; + constantBufferDesc.byteSize = sizeof( SsaoConstants ); + constantBufferDesc.debugName = "SsaoConstants"; + constantBufferDesc.isConstantBuffer = true; + constantBufferDesc.isVolatile = true; + constantBufferDesc.maxVersions = c_MaxRenderPassConstantBufferVersions; + m_ConstantBuffer = device->createBuffer( constantBufferDesc ); + + nvrhi::TextureDesc DeinterleavedTextureDesc; + DeinterleavedTextureDesc.width = ( params.dimensions.x + 3 ) / 4; + DeinterleavedTextureDesc.height = ( params.dimensions.y + 3 ) / 4; + DeinterleavedTextureDesc.arraySize = 16; + DeinterleavedTextureDesc.dimension = nvrhi::TextureDimension::Texture2DArray; + DeinterleavedTextureDesc.isUAV = true; + DeinterleavedTextureDesc.initialState = nvrhi::ResourceStates::ShaderResource; + DeinterleavedTextureDesc.keepInitialState = true; + DeinterleavedTextureDesc.debugName = "SSAO/DeinterleavedDepth"; + DeinterleavedTextureDesc.format = nvrhi::Format::R32_FLOAT; + m_DeinterleavedDepth = device->createTexture( DeinterleavedTextureDesc ); + + m_QuantizedGbufferTextureSize = idVec2( float( DeinterleavedTextureDesc.width ), float( DeinterleavedTextureDesc.height ) ) * 4.f; + + DeinterleavedTextureDesc.debugName = "SSAO/DeinterleavedOcclusion"; + DeinterleavedTextureDesc.format = params.directionalOcclusion ? nvrhi::Format::RGBA16_FLOAT : nvrhi::Format::R8_UNORM; + m_DeinterleavedOcclusion = device->createTexture( DeinterleavedTextureDesc ); + + { + idList macros = { { "LINEAR_DEPTH", params.inputLinearDepth ? "1" : "0" } }; + int shaderIdx = renderProgManager.FindShader( "builtin/SSAO/ssao_deinterleave", SHADER_STAGE_COMPUTE, "", macros, true, LAYOUT_DRAW_VERT ); + m_Deinterleave.Shader = renderProgManager.GetShader( shaderIdx ); + + nvrhi::BindingLayoutDesc DeinterleaveBindings; + DeinterleaveBindings.visibility = nvrhi::ShaderType::Compute; + DeinterleaveBindings.bindings = + { + nvrhi::BindingLayoutItem::VolatileConstantBuffer( 0 ), + nvrhi::BindingLayoutItem::VolatileConstantBuffer( 1 ), + nvrhi::BindingLayoutItem::Texture_SRV( 0 ), + nvrhi::BindingLayoutItem::Texture_UAV( 0 ), + }; + m_Deinterleave.BindingLayout = m_Device->createBindingLayout( DeinterleaveBindings ); + + nvrhi::ComputePipelineDesc DeinterleavePipelineDesc; + DeinterleavePipelineDesc.bindingLayouts = { m_Deinterleave.BindingLayout }; + DeinterleavePipelineDesc.CS = m_Deinterleave.Shader; + m_Deinterleave.Pipeline = device->createComputePipeline( DeinterleavePipelineDesc ); + + m_Deinterleave.BindingSets.resize( params.numBindingSets ); + } + + { + idList macros = + { + { "OCT_ENCODED_NORMALS", params.octEncodedNormals ? "1" : "0" }, + { "DIRECTIONAL_OCCLUSION", params.directionalOcclusion ? "1" : "0" } + }; + int shaderIdx = renderProgManager.FindShader( "builtin/SSAO/ssao_compute", SHADER_STAGE_COMPUTE, "", macros, true, LAYOUT_DRAW_VERT ); + m_Compute.Shader = renderProgManager.GetShader( shaderIdx ); + + nvrhi::BindingLayoutDesc ComputeBindings; + ComputeBindings.visibility = nvrhi::ShaderType::Compute; + ComputeBindings.bindings = + { + nvrhi::BindingLayoutItem::VolatileConstantBuffer( 0 ), + nvrhi::BindingLayoutItem::VolatileConstantBuffer( 1 ), + nvrhi::BindingLayoutItem::Texture_SRV( 0 ), + nvrhi::BindingLayoutItem::Texture_SRV( 1 ), + nvrhi::BindingLayoutItem::Texture_UAV( 0 ), + }; + m_Compute.BindingLayout = m_Device->createBindingLayout( ComputeBindings ); + + nvrhi::ComputePipelineDesc ComputePipeline; + ComputePipeline.bindingLayouts = { m_Compute.BindingLayout }; + ComputePipeline.CS = m_Compute.Shader; + m_Compute.Pipeline = device->createComputePipeline( ComputePipeline ); + + m_Compute.BindingSets.resize( params.numBindingSets ); + } + + { + idList macros = + { + { "DIRECTIONAL_OCCLUSION", params.directionalOcclusion ? "1" : "0" } + }; + int shaderIdx = renderProgManager.FindShader( "builtin/SSAO/ssao_blur", SHADER_STAGE_COMPUTE, "", macros, true, LAYOUT_DRAW_VERT ); + m_Blur.Shader = renderProgManager.GetShader( shaderIdx ); + + nvrhi::BindingLayoutDesc BlurBindings; + BlurBindings.visibility = nvrhi::ShaderType::Compute; + BlurBindings.bindings = + { + nvrhi::BindingLayoutItem::VolatileConstantBuffer( 0 ), + nvrhi::BindingLayoutItem::VolatileConstantBuffer( 1 ), + nvrhi::BindingLayoutItem::Texture_SRV( 0 ), + nvrhi::BindingLayoutItem::Texture_SRV( 1 ), + nvrhi::BindingLayoutItem::Texture_UAV( 0 ), + nvrhi::BindingLayoutItem::Sampler( 0 ), + }; + m_Blur.BindingLayout = m_Device->createBindingLayout( BlurBindings ); + + nvrhi::ComputePipelineDesc BlurPipeline; + BlurPipeline.bindingLayouts = { m_Blur.BindingLayout }; + BlurPipeline.CS = m_Blur.Shader; + m_Blur.Pipeline = device->createComputePipeline( BlurPipeline ); + + m_Blur.BindingSets.resize( params.numBindingSets ); + } +} + +// Backwards compatibility constructor +SsaoPass::SsaoPass( + nvrhi::IDevice* device, + CommonRenderPasses* commonPasses, + nvrhi::ITexture* gbufferDepth, + nvrhi::ITexture* gbufferNormals, + nvrhi::ITexture* destinationTexture ) + : SsaoPass( device, CreateParameters{ idVec2( gbufferDepth->getDesc().width, gbufferDepth->getDesc().height ), false, false, false, 1 }, commonPasses ) +{ + const nvrhi::TextureDesc& depthDesc = gbufferDepth->getDesc(); + const nvrhi::TextureDesc& normalsDesc = gbufferNormals->getDesc(); + assert( depthDesc.sampleCount == normalsDesc.sampleCount ); + assert( depthDesc.sampleCount == 1 ); // more is currently unsupported + assert( depthDesc.dimension == nvrhi::TextureDimension::Texture2D ); // arrays are currently unsupported + + CreateBindingSet( gbufferDepth, gbufferNormals, destinationTexture, 0 ); +} + +void SsaoPass::CreateBindingSet( + nvrhi::ITexture* gbufferDepth, + nvrhi::ITexture* gbufferNormals, + nvrhi::ITexture* destinationTexture, + int bindingSetIndex ) +{ + nvrhi::BindingSetDesc DeinterleaveBindings; + DeinterleaveBindings.bindings = + { + nvrhi::BindingSetItem::ConstantBuffer( 0, renderProgManager.GetConstantBuffer() ), + nvrhi::BindingSetItem::ConstantBuffer( 1, m_ConstantBuffer ), + nvrhi::BindingSetItem::Texture_SRV( 0, gbufferDepth ), + nvrhi::BindingSetItem::Texture_UAV( 0, m_DeinterleavedDepth ) + }; + m_Deinterleave.BindingSets[bindingSetIndex] = m_Device->createBindingSet( DeinterleaveBindings, m_Deinterleave.BindingLayout ); + + nvrhi::BindingSetDesc ComputeBindings; + ComputeBindings.bindings = + { + nvrhi::BindingSetItem::ConstantBuffer( 0, renderProgManager.GetConstantBuffer( ) ), + nvrhi::BindingSetItem::ConstantBuffer( 1, m_ConstantBuffer ), + nvrhi::BindingSetItem::Texture_SRV( 0, m_DeinterleavedDepth ), + nvrhi::BindingSetItem::Texture_SRV( 1, gbufferNormals ), + nvrhi::BindingSetItem::Texture_UAV( 0, m_DeinterleavedOcclusion ) + }; + m_Compute.BindingSets[bindingSetIndex] = m_Device->createBindingSet( ComputeBindings, m_Compute.BindingLayout ); + + nvrhi::BindingSetDesc BlurBindings; + BlurBindings.bindings = + { + nvrhi::BindingSetItem::ConstantBuffer( 0, renderProgManager.GetConstantBuffer( ) ), + nvrhi::BindingSetItem::ConstantBuffer( 1, m_ConstantBuffer ), + nvrhi::BindingSetItem::Texture_SRV( 0, m_DeinterleavedDepth ), + nvrhi::BindingSetItem::Texture_SRV( 1, m_DeinterleavedOcclusion ), + nvrhi::BindingSetItem::Texture_UAV( 0, destinationTexture ), + nvrhi::BindingSetItem::Sampler( 0, commonRenderPasses->m_PointClampSampler ) + }; + m_Blur.BindingSets[bindingSetIndex] = m_Device->createBindingSet( BlurBindings, m_Blur.BindingLayout ); +} + +void SsaoPass::Render( + nvrhi::ICommandList* commandList, + const SsaoParameters& params, + viewDef_t* viewDef, + int bindingSetIndex ) +{ + assert( m_Deinterleave.BindingSets[bindingSetIndex] ); + assert( m_Compute.BindingSets[bindingSetIndex] ); + assert( m_Blur.BindingSets[bindingSetIndex] ); + + commandList->beginMarker( "SSAO" ); + + nvrhi::Rect viewExtent( viewDef->viewport.x1, viewDef->viewport.x2, viewDef->viewport.y1, viewDef->viewport.y2 ); + nvrhi::Rect quarterResExtent = viewExtent; + quarterResExtent.minX /= 4; + quarterResExtent.minY /= 4; + quarterResExtent.maxX = ( quarterResExtent.maxX + 3 ) / 4; + quarterResExtent.maxY = ( quarterResExtent.maxY + 3 ) / 4; + + SsaoConstants ssaoConstants = {}; + ssaoConstants.clipToView = idVec2( + viewDef->projectionMatrix[2 * 4 + 3] / viewDef->projectionMatrix[0 * 4 + 0], + viewDef->projectionMatrix[2 * 4 + 3] / viewDef->projectionMatrix[0 * 4 + 1] ); + ssaoConstants.invQuantizedGbufferSize = 1.f / m_QuantizedGbufferTextureSize; + ssaoConstants.quantizedViewportOrigin = idVec2i( quarterResExtent.minX, quarterResExtent.minY ) * 4; + ssaoConstants.amount = params.amount; + ssaoConstants.invBackgroundViewDepth = ( params.backgroundViewDepth > 0.f ) ? 1.f / params.backgroundViewDepth : 0.f; + ssaoConstants.radiusWorld = params.radiusWorld; + ssaoConstants.surfaceBias = params.surfaceBias; + ssaoConstants.powerExponent = params.powerExponent; + ssaoConstants.radiusToScreen = 0.5f * viewDef->viewport.GetHeight() * abs( viewDef->projectionMatrix[1 * 4 + 1] ); + commandList->writeBuffer( m_ConstantBuffer, &ssaoConstants, sizeof( ssaoConstants ) ); + + uint32_t dispatchWidth = ( quarterResExtent.width() + 7 ) / 8; + uint32_t dispatchHeight = ( quarterResExtent.height() + 7 ) / 8; + + nvrhi::ComputeState state; + state.pipeline = m_Deinterleave.Pipeline; + state.bindings = { m_Deinterleave.BindingSets[bindingSetIndex] }; + commandList->setComputeState( state ); + commandList->dispatch( dispatchWidth, dispatchHeight, 1 ); + + state.pipeline = m_Compute.Pipeline; + state.bindings = { m_Compute.BindingSets[bindingSetIndex] }; + commandList->setComputeState( state ); + commandList->dispatch( dispatchWidth, dispatchHeight, 16 ); + + dispatchWidth = ( viewExtent.width() + 15 ) / 16; + dispatchHeight = ( viewExtent.height() + 15 ) / 16; + + state.pipeline = m_Blur.Pipeline; + state.bindings = { m_Blur.BindingSets[bindingSetIndex] }; + commandList->setComputeState( state ); + commandList->dispatch( dispatchWidth, dispatchHeight, 1 ); + + commandList->endMarker(); +} \ No newline at end of file diff --git a/neo/renderer/Passes/SsaoPass.h b/neo/renderer/Passes/SsaoPass.h new file mode 100644 index 00000000..edbbcd2c --- /dev/null +++ b/neo/renderer/Passes/SsaoPass.h @@ -0,0 +1,100 @@ +/* +=========================================================================== + +Doom 3 BFG Edition GPL Source Code +Copyright (C) 2022 Stephen Pridham + +This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). + +Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 BFG Edition Source Code. If not, see . + +In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ +#ifndef RENDERER_PASSES_SSAOPASS_H_ +#define RENDERER_PASSES_SSAOPASS_H_ + +struct SsaoParameters +{ + float amount = 2.f; + float backgroundViewDepth = 100.f; + float radiusWorld = 0.5f; + float surfaceBias = 0.1f; + float powerExponent = 2.f; + bool enableBlur = true; + float blurSharpness = 16.f; +}; + +class SsaoPass +{ +private: + struct SubPass + { + nvrhi::ShaderHandle Shader; + nvrhi::BindingLayoutHandle BindingLayout; + std::vector BindingSets; + nvrhi::ComputePipelineHandle Pipeline; + }; + + SubPass m_Deinterleave; + SubPass m_Compute; + SubPass m_Blur; + + nvrhi::DeviceHandle m_Device; + nvrhi::BufferHandle m_ConstantBuffer; + CommonRenderPasses* commonRenderPasses; + + nvrhi::TextureHandle m_DeinterleavedDepth; + nvrhi::TextureHandle m_DeinterleavedOcclusion; + idVec2 m_QuantizedGbufferTextureSize; + +public: + struct CreateParameters + { + idVec2 dimensions; + bool inputLinearDepth = false; + bool octEncodedNormals = false; + bool directionalOcclusion = false; + int numBindingSets = 1; + }; + + SsaoPass( + nvrhi::IDevice* device, + const CreateParameters& params, + CommonRenderPasses* commonRenderPasses ); + + SsaoPass( + nvrhi::IDevice* device, + CommonRenderPasses* commonPasses, + nvrhi::ITexture* gbufferDepth, + nvrhi::ITexture* gbufferNormals, + nvrhi::ITexture* destinationTexture ); + + void CreateBindingSet( + nvrhi::ITexture* gbufferDepth, + nvrhi::ITexture* gbufferNormals, + nvrhi::ITexture* destinationTexture, + int bindingSetIndex = 0 ); + + void Render( + nvrhi::ICommandList* commandList, + const SsaoParameters& params, + viewDef_t* viewDef, + int bindingSetIndex = 0 ); +}; + +#endif \ No newline at end of file diff --git a/neo/renderer/RenderBackend.h b/neo/renderer/RenderBackend.h index 4caebb16..847d3d9a 100644 --- a/neo/renderer/RenderBackend.h +++ b/neo/renderer/RenderBackend.h @@ -227,7 +227,8 @@ struct vulkanContext_t extern vulkanContext_t vkcontext; -#else //if defined( ID_OPENGL ) +//#elif !defined( USE_NVRHI ) +#else struct glContext_t { diff --git a/neo/renderer/RenderCommon.h b/neo/renderer/RenderCommon.h index 68366b8b..881ee828 100644 --- a/neo/renderer/RenderCommon.h +++ b/neo/renderer/RenderCommon.h @@ -40,6 +40,26 @@ If you have questions concerning this license or the applicable additional terms #include "Font.h" #include "Framebuffer.h" +#if defined( USE_NVRHI ) + +#if USE_DX11 || USE_DX12 + #include +#endif + +#if USE_DX11 + #include +#endif + +#if USE_DX12 + #include +#endif + +#if USE_VK + #include +#endif + +#include +#endif // maximum texture units const int MAX_PROG_TEXTURE_PARMS = 16; diff --git a/neo/renderer/RenderPass.cpp b/neo/renderer/RenderPass.cpp new file mode 100644 index 00000000..7d0a61cb --- /dev/null +++ b/neo/renderer/RenderPass.cpp @@ -0,0 +1,350 @@ +#include "precompiled.h" +#pragma hdrstop + +#include "RenderPass.h" +#include "Passes/CommonPasses.h" + +#include + +static triIndex_t quadPicIndexes[6] = { 3, 0, 2, 2, 0, 1 }; + +#define MAX_VERTS 8000 +#define MAX_INDEXES 120000 + +idDrawVert* BasicTriangle::AllocVerts( int vertCount, triIndex_t* tempIndexes, int indexCount ) +{ + if( numIndexes + indexCount > MAX_INDEXES ) + { + static int warningFrame = 0; + if( warningFrame != tr.frameCount ) + { + warningFrame = tr.frameCount; + idLib::Warning( "idGuiModel::AllocTris: MAX_INDEXES exceeded" ); + } + return NULL; + } + + if( numVerts + vertCount > MAX_VERTS ) + { + static int warningFrame = 0; + if( warningFrame != tr.frameCount ) + { + warningFrame = tr.frameCount; + idLib::Warning( "idGuiModel::AllocTris: MAX_VERTS exceeded" ); + } + return NULL; + } + + + int startVert = numVerts; + int startIndex = numIndexes; + + numVerts += vertCount; + numIndexes += indexCount; + + if( ( startIndex & 1 ) || ( indexCount & 1 ) ) + { + // slow for write combined memory! + // this should be very rare, since quads are always an even index count + for( int i = 0; i < indexCount; i++ ) + { + indexPointer[startIndex + i] = startVert + tempIndexes[i]; + } + } + else + { + for( int i = 0; i < indexCount; i += 2 ) + { + WriteIndexPair( indexPointer + startIndex + i, startVert + tempIndexes[i], startVert + tempIndexes[i + 1] ); + } + } + + return vertexPointer + startVert; +} + +bool BasicTriangle::Init() +{ + int v = renderProgManager.FindShader( "vertbuffershaders.hlsl", SHADER_STAGE_VERTEX ); + int f = renderProgManager.FindShader( "vertbuffershaders.hlsl", SHADER_STAGE_FRAGMENT ); + + vertexShader = renderProgManager.GetShader( v ); + pixelShader = renderProgManager.GetShader( f ); + + if( !vertexShader || !pixelShader ) + { + return false; + } + + commandList = GetDevice()->createCommandList(); + + nvrhi::VertexAttributeDesc attributes[] = + { + nvrhi::VertexAttributeDesc() + .setName( "POSITION" ) + .setFormat( nvrhi::Format::RGB32_FLOAT ) + .setOffset( offsetof( idDrawVert, xyz ) ) + .setElementStride( sizeof( idDrawVert ) ), + //nvrhi::VertexAttributeDesc() + // .setName( "NORMAL" ) + // .setFormat( nvrhi::Format::RGBA8_UINT ) + // .setOffset( offsetof( idDrawVert, normal ) ) + // .setElementStride( sizeof( idDrawVert ) ), + //nvrhi::VertexAttributeDesc() + // .setName( "COLOR" ) + // .setFormat( nvrhi::Format::RGBA8_UINT ) + // .setOffset( offsetof( idDrawVert, color ) ) + // .setElementStride( sizeof( idDrawVert ) ), + //nvrhi::VertexAttributeDesc() + // .setName( "COLOR2" ) + // .setFormat( nvrhi::Format::RGBA8_UINT ) + // .setOffset( offsetof( idDrawVert, color2 ) ) + // .setElementStride( sizeof( idDrawVert ) ), + nvrhi::VertexAttributeDesc() + .setName( "UV" ) + .setFormat( nvrhi::Format::RG16_FLOAT ) + .setOffset( offsetof( idDrawVert, st ) ) + .setElementStride( sizeof( idDrawVert ) ), + //nvrhi::VertexAttributeDesc() + // .setName( "TANGENT" ) + // .setFormat( nvrhi::Format::RGBA8_UINT ) + // .setOffset( offsetof( idDrawVert, tangent ) ) + // .setElementStride( sizeof( idDrawVert ) ), + }; + + inputLayout = GetDevice()->createInputLayout( attributes, uint32_t( std::size( attributes ) ), vertexShader ); + + material = declManager->FindMaterial( "guis/rml/shell/textures/invader" ); + + commandList->open(); + for( int i = 0; i < material->GetNumStages(); i++ ) + { + material->GetStage( i )->texture.image->FinalizeImage( true, commandList ); + } + commandList->close(); + GetDevice()->executeCommandList( commandList ); + GetDevice()->runGarbageCollection(); + + nvrhi::ITexture* texture = ( nvrhi::ITexture* )material->GetStage( 0 )->texture.image->GetTextureID(); + + CommonRenderPasses commonPasses; + commonPasses.Init( GetDevice() ); + + nvrhi::BindingSetDesc bindingSetDesc; + bindingSetDesc.bindings = + { + nvrhi::BindingSetItem::ConstantBuffer( 0, renderProgManager.ConstantBuffer() ), + nvrhi::BindingSetItem::Texture_SRV( 0, texture ), + nvrhi::BindingSetItem::Sampler( 0, commonPasses.m_AnisotropicWrapSampler ) + }; + + if( !nvrhi::utils::CreateBindingSetAndLayout( GetDevice(), nvrhi::ShaderType::All, 0, bindingSetDesc, bindingLayout, bindingSet ) ) + { + common->Error( "Couldn't create the binding set or layout" ); + return false; + } + + return true; +} + +void BasicTriangle::BackBufferResizing() +{ + pipeline = nullptr; +} + +void BasicTriangle::Animate( float fElapsedTimeSeconds ) +{ +} + +void BasicTriangle::RenderFrontend() +{ + vertexBlock = vertexCache.AllocVertex( NULL, MAX_VERTS, sizeof( idDrawVert ), commandList ); + indexBlock = vertexCache.AllocIndex( NULL, MAX_INDEXES, sizeof( triIndex_t ), commandList ); + + vertexPointer = ( idDrawVert* )vertexCache.MappedVertexBuffer( vertexBlock ); + indexPointer = ( triIndex_t* )vertexCache.MappedIndexBuffer( indexBlock ); + numVerts = 0; + numIndexes = 0; + + idDrawVert* verts = AllocVerts( 4, quadPicIndexes, 6 ); + + uint32_t currentColorNativeBytesOrder = LittleLong( PackColor( idVec4( 255, 255, 255, 255 ) ) ); + + float x = 0.f; + float y = 0.f; + float w = renderSystem->GetWidth(); + float h = renderSystem->GetHeight(); + float s1 = 0.0f, t1 = 0.0f, s2 = 1.0f, t2 = 1.0f; + + idVec4 topLeft( x, y, s1, t1 ); + idVec4 topRight( x + w, y, s2, t1 ); + idVec4 bottomRight( x + w, y + h, s2, t2 ); + idVec4 bottomLeft( x, y + h, s1, t2 ); + + float z = 0.0f; + + ALIGNTYPE16 idDrawVert localVerts[4]; + + localVerts[0].Clear(); + localVerts[0].xyz[0] = x; + localVerts[0].xyz[1] = y; + localVerts[0].xyz[2] = 0.f; + localVerts[0].SetTexCoord( s1, t1 ); + localVerts[0].SetNativeOrderColor( currentColorNativeBytesOrder ); + localVerts[0].ClearColor2(); + + localVerts[1].Clear(); + localVerts[1].xyz[0] = x + w; + localVerts[1].xyz[1] = y; + localVerts[1].xyz[2] = 0.f; + localVerts[1].SetTexCoord( s2, t1 ); + localVerts[1].SetNativeOrderColor( currentColorNativeBytesOrder ); + localVerts[1].ClearColor2(); + + localVerts[2].Clear(); + localVerts[2].xyz[0] = x + w; + localVerts[2].xyz[1] = y + h; + localVerts[2].xyz[2] = 0.f; + localVerts[2].SetTexCoord( s2, t2 ); + localVerts[2].SetNativeOrderColor( currentColorNativeBytesOrder ); + localVerts[2].ClearColor2(); + + localVerts[3].Clear(); + localVerts[3].xyz[0] = x; + localVerts[3].xyz[1] = y + h; + localVerts[3].xyz[2] = 0.f; + localVerts[3].SetTexCoord( s1, t2 ); + localVerts[3].SetNativeOrderColor( currentColorNativeBytesOrder ); + localVerts[3].ClearColor2(); + + WriteDrawVerts16( verts, localVerts, 4 ); + + for( int i = 0; i < 6; i += 2 ) + { + WriteIndexPair( indexPointer + i, quadPicIndexes[i], quadPicIndexes[i + 1] ); + } +} + +void BasicTriangle::Render( nvrhi::IFramebuffer* framebuffer ) +{ + if( !pipeline ) + { + nvrhi::GraphicsPipelineDesc psoDesc; + psoDesc.VS = vertexShader; + psoDesc.PS = pixelShader; + psoDesc.inputLayout = inputLayout; + psoDesc.bindingLayouts = { bindingLayout }; + psoDesc.primType = nvrhi::PrimitiveType::TriangleList; + psoDesc.renderState.depthStencilState.depthTestEnable = false; + //psoDesc.renderState.rasterState.frontCounterClockwise = true; + + pipeline = GetDevice()->createGraphicsPipeline( psoDesc, framebuffer ); + } + + commandList->open(); + commandList->beginMarker( "Basic" ); + + RenderFrontend(); + + nvrhi::utils::ClearColorAttachment( commandList, framebuffer, 0, nvrhi::Color( 0.f ) ); + + float w = framebuffer->getFramebufferInfo().width; + float h = framebuffer->getFramebufferInfo().height; + + idRenderMatrix projectionMatrix; + { + // orthographic matrix + float xScale = 1.0f / w; + float yScale = -1.0f / h; // flip y + float zScale = 1.0f; + + float projMat[16]; + projMat[0 * 4 + 0] = 2.f * xScale; + projMat[0 * 4 + 1] = 0.0f; + projMat[0 * 4 + 2] = 0.0f; + projMat[0 * 4 + 3] = 0.0f; + + projMat[1 * 4 + 0] = 0.0f; + projMat[1 * 4 + 1] = 2.f * yScale; + projMat[1 * 4 + 2] = 0.0f; + projMat[1 * 4 + 3] = 0.0f; + + projMat[2 * 4 + 0] = 0.0f; + projMat[2 * 4 + 1] = 0.0f; + projMat[2 * 4 + 2] = zScale; + projMat[2 * 4 + 3] = 0.0f; + + projMat[3 * 4 + 0] = -( w * xScale ); + projMat[3 * 4 + 1] = -( h * yScale ); + projMat[3 * 4 + 2] = 0.0f; + projMat[3 * 4 + 3] = 1.0f; + + float projMatT[16]; + R_MatrixTranspose( projMat, projMatT ); + + renderProgManager.SetRenderParm( renderParm_t::RENDERPARM_PROJMATRIX_X, &projMat[0] ); + renderProgManager.SetRenderParm( renderParm_t::RENDERPARM_PROJMATRIX_Y, &projMat[4] ); + renderProgManager.SetRenderParm( renderParm_t::RENDERPARM_PROJMATRIX_Z, &projMat[8] ); + renderProgManager.SetRenderParm( renderParm_t::RENDERPARM_PROJMATRIX_W, &projMat[12] ); + } + + renderProgManager.CommitConstantBuffer( commandList ); + + idVertexBuffer* vertexBuffer; + uint vertOffset = 0; + { + if( vertexCache.CacheIsStatic( vertexBlock ) ) + { + vertexBuffer = &vertexCache.staticData.vertexBuffer; + } + else + { + const uint64 frameNum = ( int )( vertexBlock >> VERTCACHE_FRAME_SHIFT ) & VERTCACHE_FRAME_MASK; + if( frameNum != ( ( vertexCache.currentFrame ) & VERTCACHE_FRAME_MASK ) ) + { + idLib::Warning( "RB_DrawElementsWithCounters, vertexBuffer == NULL" ); + return; + } + vertexBuffer = &vertexCache.frameData[vertexCache.drawListNum].vertexBuffer; + } + vertOffset = ( uint )( vertexBlock >> VERTCACHE_OFFSET_SHIFT ) & VERTCACHE_OFFSET_MASK; + } + + vertCacheHandle_t indexBlockTemp = indexBlock + ( ( int64 )( 0 * sizeof( triIndex_t ) ) << VERTCACHE_OFFSET_SHIFT ); + + idIndexBuffer* indexBuffer; + uint indexOffset = 0; + if( vertexCache.CacheIsStatic( indexBlockTemp ) ) + { + indexBuffer = &vertexCache.staticData.indexBuffer; + } + else + { + const uint64 frameNum = ( int )( indexBlockTemp >> VERTCACHE_FRAME_SHIFT ) & VERTCACHE_FRAME_MASK; + if( frameNum != ( ( vertexCache.currentFrame ) & VERTCACHE_FRAME_MASK ) ) + { + idLib::Warning( "RB_DrawElementsWithCounters, indexBuffer == NULL" ); + return; + } + indexBuffer = &vertexCache.frameData[vertexCache.drawListNum].indexBuffer; + } + indexOffset = ( uint )( indexBlock >> VERTCACHE_OFFSET_SHIFT ) & VERTCACHE_OFFSET_MASK; + + nvrhi::GraphicsState state; + state.bindings = { bindingSet }; + state.indexBuffer = { indexBuffer->GetAPIObject(), nvrhi::Format::R16_UINT, indexOffset }; + state.vertexBuffers = { { vertexBuffer->GetAPIObject(), 0, vertOffset } }; + state.pipeline = pipeline; + state.framebuffer = framebuffer; + state.viewport.addViewportAndScissorRect( framebuffer->getFramebufferInfo().getViewport() ); + + commandList->setGraphicsState( state ); + + nvrhi::DrawArguments args; + args.vertexCount = 6; + commandList->drawIndexed( args ); + + commandList->endMarker(); + commandList->close(); + + GetDevice()->executeCommandList( commandList ); +} \ No newline at end of file diff --git a/neo/renderer/RenderPass.h b/neo/renderer/RenderPass.h new file mode 100644 index 00000000..50089328 --- /dev/null +++ b/neo/renderer/RenderPass.h @@ -0,0 +1,71 @@ +#ifndef RENDER_PASS_H_ +#define RENDER_PASS_H_ + +#include "sys/DeviceManager.h" + +class IRenderPass +{ +private: + DeviceManager* deviceManager; + +public: + explicit IRenderPass( DeviceManager* deviceManager ) + : deviceManager( deviceManager ) + { } + + virtual ~IRenderPass() = default; + + virtual void RenderFrontend() { } + virtual void Render( nvrhi::IFramebuffer* framebuffer ) { } + virtual void Animate( float fElapsedTimeSeconds ) { } + virtual void BackBufferResizing() { } + virtual void BackBufferResized( const uint32_t width, const uint32_t height, const uint32_t sampleCount ) { } + + [[nodiscard]] DeviceManager* GetDeviceManager() const + { + return deviceManager; + } + [[nodiscard]] nvrhi::IDevice* GetDevice() const + { + return deviceManager->GetDevice(); + } + [[nodiscard]] uint32_t GetFrameIndex() const + { + return deviceManager->GetFrameIndex(); + } +}; + + +class BasicTriangle : public IRenderPass +{ +private: + nvrhi::ShaderHandle vertexShader; + nvrhi::ShaderHandle pixelShader; + nvrhi::GraphicsPipelineHandle pipeline; + nvrhi::CommandListHandle commandList; + nvrhi::InputLayoutHandle inputLayout; + nvrhi::BindingLayoutHandle bindingLayout; + nvrhi::BindingSetHandle bindingSet; + + vertCacheHandle_t vertexBlock; + vertCacheHandle_t indexBlock; + idDrawVert* vertexPointer; + triIndex_t* indexPointer; + const idMaterial* material; + + int numVerts; + int numIndexes; + + idDrawVert* AllocVerts( int vertCount, triIndex_t* tempIndexes, int indexCount ); + +public: + using IRenderPass::IRenderPass; + + bool Init(); + void BackBufferResizing() override; + void Animate( float fElapsedTimeSeconds ) override; + void RenderFrontend() override; + void Render( nvrhi::IFramebuffer* framebuffer ) override; +}; + +#endif \ No newline at end of file diff --git a/neo/renderer/RenderSystem.cpp b/neo/renderer/RenderSystem.cpp index 16b7fe6c..31deebc0 100644 --- a/neo/renderer/RenderSystem.cpp +++ b/neo/renderer/RenderSystem.cpp @@ -35,6 +35,12 @@ If you have questions concerning this license or the applicable additional terms #include "../framework/Common_local.h" #include "../imgui/BFGimgui.h" +#if defined( USE_NVRHI ) +#include +extern DeviceManager* deviceManager; +#endif + + idRenderSystemLocal tr; idRenderSystem* renderSystem = &tr; @@ -307,9 +313,9 @@ void idRenderSystemLocal::DrawFilled( const idVec4& color, float x, float y, flo idRenderSystemLocal::DrawStretchPic ============= */ -void idRenderSystemLocal::DrawStretchPic( float x, float y, float w, float h, float s1, float t1, float s2, float t2, const idMaterial* material ) +void idRenderSystemLocal::DrawStretchPic( float x, float y, float w, float h, float s1, float t1, float s2, float t2, const idMaterial* material, float z ) { - DrawStretchPic( idVec4( x, y, s1, t1 ), idVec4( x + w, y, s2, t1 ), idVec4( x + w, y + h, s2, t2 ), idVec4( x, y + h, s1, t2 ), material ); + DrawStretchPic( idVec4( x, y, s1, t1 ), idVec4( x + w, y, s2, t1 ), idVec4( x + w, y + h, s2, t2 ), idVec4( x, y + h, s1, t2 ), material, z ); } /* @@ -318,7 +324,7 @@ idRenderSystemLocal::DrawStretchPic ============= */ static triIndex_t quadPicIndexes[6] = { 3, 0, 2, 2, 0, 1 }; -void idRenderSystemLocal::DrawStretchPic( const idVec4& topLeft, const idVec4& topRight, const idVec4& bottomRight, const idVec4& bottomLeft, const idMaterial* material ) +void idRenderSystemLocal::DrawStretchPic( const idVec4& topLeft, const idVec4& topRight, const idVec4& bottomRight, const idVec4& bottomLeft, const idMaterial* material, float z ) { if( !IsInitialized() ) { @@ -340,6 +346,7 @@ void idRenderSystemLocal::DrawStretchPic( const idVec4& topLeft, const idVec4& t localVerts[0].Clear(); localVerts[0].xyz[0] = topLeft.x; localVerts[0].xyz[1] = topLeft.y; + localVerts[0].xyz[2] = z; localVerts[0].SetTexCoord( topLeft.z, topLeft.w ); localVerts[0].SetNativeOrderColor( currentColorNativeBytesOrder ); localVerts[0].ClearColor2(); @@ -347,6 +354,7 @@ void idRenderSystemLocal::DrawStretchPic( const idVec4& topLeft, const idVec4& t localVerts[1].Clear(); localVerts[1].xyz[0] = topRight.x; localVerts[1].xyz[1] = topRight.y; + localVerts[1].xyz[2] = z; localVerts[1].SetTexCoord( topRight.z, topRight.w ); localVerts[1].SetNativeOrderColor( currentColorNativeBytesOrder ); localVerts[1].ClearColor2(); @@ -354,6 +362,7 @@ void idRenderSystemLocal::DrawStretchPic( const idVec4& topLeft, const idVec4& t localVerts[2].Clear(); localVerts[2].xyz[0] = bottomRight.x; localVerts[2].xyz[1] = bottomRight.y; + localVerts[2].xyz[2] = z; localVerts[2].SetTexCoord( bottomRight.z, bottomRight.w ); localVerts[2].SetNativeOrderColor( currentColorNativeBytesOrder ); localVerts[2].ClearColor2(); @@ -361,6 +370,7 @@ void idRenderSystemLocal::DrawStretchPic( const idVec4& topLeft, const idVec4& t localVerts[3].Clear(); localVerts[3].xyz[0] = bottomLeft.x; localVerts[3].xyz[1] = bottomLeft.y; + localVerts[3].xyz[2] = z; localVerts[3].SetTexCoord( bottomLeft.z, bottomLeft.w ); localVerts[3].SetNativeOrderColor( currentColorNativeBytesOrder ); localVerts[3].ClearColor2(); @@ -623,7 +633,6 @@ const emptyCommand_t* idRenderSystemLocal::SwapCommandBuffers( performanceCounters_t* pc ) { - SwapCommandBuffers_FinishRendering( frontEndMicroSec, backEndMicroSec, shadowMicroSec, gpuMicroSec, bc, pc ); return SwapCommandBuffers_FinishCommandBuffers(); @@ -674,7 +683,7 @@ void idRenderSystemLocal::SwapCommandBuffers_FinishRendering( backend.GL_BlockingSwapBuffers(); } -#if defined(USE_VULKAN) +#if defined( USE_NVRHI ) || defined( USE_VULKAN ) if( gpuMicroSec != NULL ) { *gpuMicroSec = backend.pc.gpuMicroSec; @@ -887,8 +896,13 @@ const emptyCommand_t* idRenderSystemLocal::SwapCommandBuffers_FinishCommandBuffe // PC UpdateStereo3DMode(); + if( !commandList ) + { + commandList = deviceManager->GetDevice()->createCommandList(); + } + // prepare the new command buffer - guiModel->BeginFrame(); + guiModel->BeginFrame( commandList ); //------------------------------ // Make sure that geometry used by code is present in the buffer cache. @@ -899,10 +913,10 @@ const emptyCommand_t* idRenderSystemLocal::SwapCommandBuffers_FinishCommandBuffe // scene generation, the basic surfaces needed for drawing the buffers will // always be present. //------------------------------ - R_InitDrawSurfFromTri( tr.unitSquareSurface_, *tr.unitSquareTriangles ); - R_InitDrawSurfFromTri( tr.zeroOneCubeSurface_, *tr.zeroOneCubeTriangles ); - R_InitDrawSurfFromTri( tr.zeroOneSphereSurface_, *tr.zeroOneSphereTriangles ); - R_InitDrawSurfFromTri( tr.testImageSurface_, *tr.testImageTriangles ); + R_InitDrawSurfFromTri( tr.unitSquareSurface_, *tr.unitSquareTriangles, commandList ); + R_InitDrawSurfFromTri( tr.zeroOneCubeSurface_, *tr.zeroOneCubeTriangles, commandList ); + R_InitDrawSurfFromTri( tr.zeroOneSphereSurface_, *tr.zeroOneSphereTriangles, commandList ); + R_InitDrawSurfFromTri( tr.testImageSurface_, *tr.testImageTriangles, commandList ); // Reset render crop to be the full screen renderCrops[0].x1 = 0; @@ -1053,6 +1067,53 @@ void idRenderSystemLocal::CropRenderSize( int width, int height ) current.y2 = previous.y2; } +/* +================ +idRenderSystemLocal::CropRenderSize +================ +*/ +void idRenderSystemLocal::CropRenderSize( int x, int y, int width, int height ) +{ + if( !IsInitialized() ) + { + return; + } + + // close any gui drawing before changing the size + guiModel->EmitFullScreen(); + guiModel->Clear(); + + + if( width < 1 || height < 1 ) + { + common->Error( "CropRenderSize: bad sizes" ); + } + + if( common->WriteDemo() ) + { + common->WriteDemo()->WriteInt( DS_RENDER ); + common->WriteDemo()->WriteInt( DC_CROP_RENDER ); + common->WriteDemo()->WriteInt( width ); + common->WriteDemo()->WriteInt( height ); + + if( r_showDemo.GetBool() ) + { + common->Printf( "write DC_CROP_RENDER\n" ); + } + } + + idScreenRect& previous = renderCrops[currentRenderCrop]; + + currentRenderCrop++; + + idScreenRect& current = renderCrops[currentRenderCrop]; + + current.x1 = x; + current.x2 = previous.x1 + width - 1; + current.y1 = y; + current.y2 = previous.y2; +} + /* ================ idRenderSystemLocal::UnCrop diff --git a/neo/renderer/RenderSystem.h b/neo/renderer/RenderSystem.h index e18350cf..3b0e2f90 100644 --- a/neo/renderer/RenderSystem.h +++ b/neo/renderer/RenderSystem.h @@ -354,7 +354,7 @@ public: { DrawStretchPic( rect.x, rect.y, rect.z, rect.w, st.x, st.y, st.z, st.w, material, z ); } - virtual void DrawStretchPic( const idVec4& topLeft, const idVec4& topRight, const idVec4& bottomRight, const idVec4& bottomLeft, const idMaterial* material ) = 0; + virtual void DrawStretchPic( const idVec4& topLeft, const idVec4& topRight, const idVec4& bottomRight, const idVec4& bottomLeft, const idMaterial* material, float z = 0.0f ) = 0; virtual void DrawStretchTri( const idVec2& p1, const idVec2& p2, const idVec2& p3, const idVec2& t1, const idVec2& t2, const idVec2& t3, const idMaterial* material ) = 0; virtual idDrawVert* AllocTris( int numVerts, const triIndex_t* indexes, int numIndexes, const idMaterial* material, const stereoDepthType_t stereoType = STEREO_DEPTH_TYPE_NONE ) = 0; diff --git a/neo/renderer/RenderWorld.cpp b/neo/renderer/RenderWorld.cpp index 4fc42c4e..8d7640f8 100644 --- a/neo/renderer/RenderWorld.cpp +++ b/neo/renderer/RenderWorld.cpp @@ -1966,6 +1966,10 @@ void idRenderWorldLocal::GenerateAllInteractions() int size = interactionTableWidth * interactionTableHeight * sizeof( *interactionTable ); interactionTable = ( idInteraction** )R_ClearedStaticAlloc( size ); +#if defined( USE_NVRHI ) + tr.commandList->open(); +#endif + // iterate through all lights int count = 0; for( int i = 0; i < this->lightDefs.Num(); i++ ) @@ -2012,13 +2016,18 @@ void idRenderWorldLocal::GenerateAllInteractions() count++; // the interaction may create geometry - inter->CreateStaticInteraction(); + inter->CreateStaticInteraction( tr.commandList ); } } session->Pump(); } +#if defined( USE_NVRHI ) + tr.commandList->close(); + deviceManager->GetDevice()->executeCommandList( tr.commandList ); +#endif + int end = Sys_Milliseconds(); int msec = end - start; diff --git a/neo/renderer/RenderWorld_lightgrid.cpp b/neo/renderer/RenderWorld_lightgrid.cpp index c830495d..eae105bb 100644 --- a/neo/renderer/RenderWorld_lightgrid.cpp +++ b/neo/renderer/RenderWorld_lightgrid.cpp @@ -35,6 +35,11 @@ If you have questions concerning this license or the applicable additional terms #include "CmdlineProgressbar.h" #include "../framework/Common_local.h" // commonLocal.WaitGameThread(); +#if defined( USE_NVRHI ) +#include +extern DeviceManager* deviceManager; +#endif + #define LGRID_FILE_EXT "lightgrid" #define LGRID_BINARYFILE_EXT "blightgrid" @@ -414,6 +419,12 @@ void idRenderWorldLocal::LoadLightGridImages() idStr filename; +#if defined( USE_NVRHI ) + nvrhi::CommandListHandle commandList = deviceManager->GetDevice()->createCommandList(); + + commandList->open(); +#endif + // try to load existing lightgrid image data for( int i = 0; i < numPortalAreas; i++ ) { @@ -429,6 +440,12 @@ void idRenderWorldLocal::LoadLightGridImages() area->lightGrid.irradianceImage->Reload( true, commandList ); } } + +#if defined( USE_NVRHI ) + commandList->close(); + + deviceManager->GetDevice()->executeCommandList( commandList ); +#endif } /* diff --git a/neo/tools/imgui/lighteditor/LightEditor.cpp b/neo/tools/imgui/lighteditor/LightEditor.cpp index 4522b1d3..5fae7346 100644 --- a/neo/tools/imgui/lighteditor/LightEditor.cpp +++ b/neo/tools/imgui/lighteditor/LightEditor.cpp @@ -418,7 +418,7 @@ void LightEditor::LoadLightTextures() idImage* editorImage = mat->GetLightEditorImage(); if( !editorImage->IsLoaded() ) { - editorImage->ActuallyLoadImage( false ); + editorImage->DeferredLoadImage(); } if( !editorImage->IsDefaulted() )