Added more missing files

This commit is contained in:
Robert Beckebans 2022-02-21 19:21:16 +01:00
parent 9b4b93e8ef
commit 025ba4419e
34 changed files with 4739 additions and 63 deletions

View file

@ -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)

View file

@ -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];
}
}
/*
===============================================================================

View file

@ -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 <GL/glew.h>
// 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"

View file

@ -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<nvrhi::SamplerDesc> {}( 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;
}

View file

@ -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 <http://www.gnu.org/licenses/>.
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 <nvrhi/nvrhi.h>
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<nvrhi::BindingSetHandle> 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<nvrhi::SamplerHandle> samplers;
idHashIndex samplerHash;
idSysMutex mutex;
};
#endif

View file

@ -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;

View file

@ -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 );
}

View file

@ -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;

View file

@ -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

View file

@ -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 );

View file

@ -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;

View file

@ -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;
}

View file

@ -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,

View file

@ -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 <http://www.gnu.org/licenses/>.
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<shaderMacro_t>(), true, LAYOUT_DRAW_VERT );
m_RectVS = renderProgManager.GetShader( rectIndex );
idList<shaderMacro_t> 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 );
}

View file

@ -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 <http://www.gnu.org/licenses/>.
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 <nvrhi/nvrhi.h>
#include <memory>
#include <unordered_map>
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<PsoCacheKey, nvrhi::GraphicsPipelineHandle, PsoCacheKey::Hash> 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

View file

@ -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 <http://www.gnu.org/licenses/>.
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 "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 )
{
}

View file

@ -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 <http://www.gnu.org/licenses/>.
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

View file

@ -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 <http://www.gnu.org/licenses/>.
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 "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 )
{
}

View file

@ -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 <http://www.gnu.org/licenses/>.
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

View file

@ -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 <http://www.gnu.org/licenses/>.
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 "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 );
}

View file

@ -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 <http://www.gnu.org/licenses/>.
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

View file

@ -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 <cassert>
#include <mutex>
#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<NullTextures> get( nvrhi::DeviceHandle device )
{
static std::mutex _mutex;
static std::weak_ptr<NullTextures> _nullTextures;
std::lock_guard<std::mutex> lock( _mutex );
std::shared_ptr<NullTextures> result = _nullTextures.lock();
if( !result )
{
result = std::make_shared<NullTextures>();
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<shaderMacro_t> 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"
}

View file

@ -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 <http://www.gnu.org/licenses/>.
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<nvrhi::BindingSetHandle> m_BindingSets;
nvrhi::ComputePipelineHandle m_Pso;
// Set of unique dummy textures - see details in class implementation
struct NullTextures;
std::shared_ptr<NullTextures> m_NullTextures;
BindingCache m_BindingCache;
};
#endif

View file

@ -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 <http://www.gnu.org/licenses/>.
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 "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<shaderMacro_t> 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<shaderMacro_t> 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<shaderMacro_t> 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();
}

View file

@ -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 <http://www.gnu.org/licenses/>.
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<nvrhi::BindingSetHandle> 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

View file

@ -227,7 +227,8 @@ struct vulkanContext_t
extern vulkanContext_t vkcontext;
#else //if defined( ID_OPENGL )
//#elif !defined( USE_NVRHI )
#else
struct glContext_t
{

View file

@ -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 <DXGI.h>
#endif
#if USE_DX11
#include <d3d11.h>
#endif
#if USE_DX12
#include <d3d12.h>
#endif
#if USE_VK
#include <nvrhi/vulkan.h>
#endif
#include <nvrhi/nvrhi.h>
#endif
// maximum texture units
const int MAX_PROG_TEXTURE_PARMS = 16;

350
neo/renderer/RenderPass.cpp Normal file
View file

@ -0,0 +1,350 @@
#include "precompiled.h"
#pragma hdrstop
#include "RenderPass.h"
#include "Passes/CommonPasses.h"
#include <nvrhi/utils.h>
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 );
}

71
neo/renderer/RenderPass.h Normal file
View file

@ -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

View file

@ -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 <sys/DeviceManager.h>
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

View file

@ -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;

View file

@ -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;

View file

@ -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 <sys/DeviceManager.h>
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
}
/*

View file

@ -418,7 +418,7 @@ void LightEditor::LoadLightTextures()
idImage* editorImage = mat->GetLightEditorImage();
if( !editorImage->IsLoaded() )
{
editorImage->ActuallyLoadImage( false );
editorImage->DeferredLoadImage();
}
if( !editorImage->IsDefaulted() )