Skip to content

Commit b353330

Browse files
committed
Implement transient buffer system for global uniforms and add constant buffer support
1 parent 4596428 commit b353330

9 files changed

Lines changed: 485 additions & 59 deletions

File tree

src/vrhi_backend.cpp

Lines changed: 96 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ std::unordered_map< uint64_t, const vhVertexLayoutDef* > vhCmdBackendState::s_la
3434
std::vector< nvrhi::VertexAttributeDesc > vhCmdBackendState::s_attributes;
3535

3636
// --------------------------------------------------------------------------
37-
// Implementation
37+
// Backend :: Utils & Helpers
3838
// --------------------------------------------------------------------------
3939

4040
static const char* vhResourceTypeToString( nvrhi::ResourceType type )
@@ -91,6 +91,42 @@ bool vhCmdBackendState::BE_Util_ShaderStageMatches( uint64_t flags, bool useComp
9191
return false;
9292
}
9393

94+
int64_t vhCmdBackendState::BE_Util_WriteGlobalUniform( const vhState& state, vhBackendTransientBuffer& tbuf, uint64_t& lastHash )
95+
{
96+
vhGlobalUniform u;
97+
vhWriteStateToGlobalUniform( state, u );
98+
uint64_t hash = vhHashGlobalUniform( u );
99+
100+
if ( hash == lastHash && tbuf.offset >= sizeof( vhGlobalUniform ) )
101+
return tbuf.offset - sizeof( vhGlobalUniform );
102+
103+
if ( ( tbuf.offset % VRHI_CBUF_ALIGN ) != 0 )
104+
return -1;
105+
106+
int64_t offset = tbuf.Alloc( sizeof( vhGlobalUniform ) );
107+
if ( offset < 0 )
108+
return -1;
109+
110+
{
111+
std::lock_guard<std::mutex> lock( g_nvRHIStateMutex );
112+
uint8_t* ptr = tbuf.Map_DeviceStateLocked();
113+
if ( !ptr )
114+
{
115+
VRHI_ERR("BE_Util_WriteGlobalUniform: Failed to map global uniform buffer!\n");
116+
return -1;
117+
}
118+
memcpy( ptr + offset, &u, sizeof( u ) );
119+
// Unmap is deferred until Frame/Flush boundary
120+
}
121+
122+
lastHash = hash;
123+
return offset;
124+
}
125+
126+
// --------------------------------------------------------------------------
127+
// Backend :: Complex BE Low Level NVRHI Device Functions
128+
// --------------------------------------------------------------------------
129+
94130
void vhCmdBackendState::BE_UpdateTexture( vhBackendTexture& btex, const vhMem* data, glm::ivec4 arrayMipUpdateRange )
95131
{
96132
if ( !btex.handle || !data || !data->size() ) return;
@@ -637,6 +673,47 @@ bool vhCmdBackendState::BE_PreSubmitCommon_FindResource(
637673

638674
switch ( item.type )
639675
{
676+
case nvrhi::ResourceType::ConstantBuffer:
677+
case nvrhi::ResourceType::VolatileConstantBuffer:
678+
{
679+
if ( item.slot == 0 )
680+
{
681+
// TODO: Implement global uniform buffer binding.
682+
return false;
683+
}
684+
685+
auto it = stageTable.bufferTable.find( item.slot );
686+
if ( it == stageTable.bufferTable.end() )
687+
{
688+
if ( state.debugFlags & VRHI_STATE_DEBUG_LOG_BINDING_MISMATCH ) VRHI_ERR( "FindResource: ConstantBuffer not found in cache at slot %d\n", item.slot );
689+
return false;
690+
}
691+
const auto result = &it->second;
692+
assert( result );
693+
if ( !result->handle )
694+
{
695+
if ( state.debugFlags & VRHI_STATE_DEBUG_LOG_BINDING_MISMATCH ) VRHI_ERR( "FindResource: ConstantBuffer found in cache at slot %d but null handle.\n", item.slot );
696+
return false;
697+
}
698+
if ( !result->handle->getDesc().isConstantBuffer )
699+
{
700+
if ( state.debugFlags & VRHI_STATE_DEBUG_LOG_BINDING_MISMATCH ) VRHI_ERR( "FindResource: ConstantBuffer found in cache at slot %d but NOT a ConstantBuffer.\n", item.slot );
701+
return false;
702+
}
703+
704+
uint64_t size = result->binding->byteSize ? result->binding->byteSize : result->handle->getDesc().byteSize;
705+
nvrhi::BufferRange range( result->binding->byteOffset, size );
706+
outItem = nvrhi::BindingSetItem::ConstantBuffer( item.slot, result->handle, range );
707+
if ( result->handle->getDesc().isVolatile && item.type != nvrhi::ResourceType::VolatileConstantBuffer )
708+
{
709+
if ( state.debugFlags & VRHI_STATE_DEBUG_LOG_BINDING_MISMATCH ) VRHI_ERR( "FindResource: Volatile Buffer bound to Static ConstantBuffer slot %d. This may be unsafe!\n", item.slot );
710+
return false;
711+
}
712+
outItem.type = item.type;
713+
if ( state.debugFlags & VRHI_STATE_DEBUG_LOG_ALL_BINDINGS ) VRHI_LOG( "FindResource: ConstantBuffer found in cache at slot %d\n", item.slot );
714+
return true;
715+
}
716+
640717
case nvrhi::ResourceType::Texture_SRV:
641718
case nvrhi::ResourceType::Texture_UAV:
642719
{
@@ -1029,29 +1106,17 @@ void vhCmdBackendState::init()
10291106
{
10301107
std::lock_guard< std::mutex > lock( backendMutex );
10311108

1032-
if ( !m_globalUniformBuffer )
1109+
if ( !m_globalUniformBuffer.handle[0] )
10331110
{
10341111
// Called from vhInit which already holds g_nvRHIStateMutex lock, and before RHI thread even starts.
10351112
// So we don't need to lock g_nvRHIStateMutex here.
1036-
1113+
10371114
nvrhi::BufferDesc desc;
1038-
desc.setByteSize( sizeof( vhGlobalUniform ) );
1115+
desc.setByteSize( 16 * 1024 * sizeof( vhGlobalUniform ) ); // 16MB
10391116
desc.setIsConstantBuffer( true );
1117+
desc.setCpuAccess( nvrhi::CpuAccessMode::Write );
10401118
desc.setDebugName( "GlobalUniforms" );
1041-
nvrhi::BufferHandle bhandle = g_vhDevice->createBuffer( desc );
1042-
if ( !bhandle )
1043-
{
1044-
VRHI_ERR( "vhCmdBackendState::init() : Failed to create Global Uniform Buffer!\n" );
1045-
assert( !"Failed to create Global Uniform Buffer." );
1046-
return;
1047-
}
1048-
1049-
m_globalUniformBuffer = std::make_unique< vhBackendBuffer >();
1050-
m_globalUniformBuffer->handle = bhandle;
1051-
m_globalUniformBuffer->name = "GlobalUniforms";
1052-
m_globalUniformBuffer->desc = desc;
1053-
m_globalUniformBuffer->stride = sizeof( vhGlobalUniform );
1054-
m_globalUniformBuffer->flags = VRHI_BUFFER_NONE;
1119+
m_globalUniformBuffer.Init_DeviceStateLocked( desc );
10551120
}
10561121
}
10571122

@@ -1060,7 +1125,7 @@ void vhCmdBackendState::shutdown()
10601125
std::lock_guard< std::mutex > lock( backendMutex );
10611126
std::lock_guard< std::mutex > lock2( g_nvRHIStateMutex );
10621127

1063-
m_globalUniformBuffer.reset();
1128+
m_globalUniformBuffer.Shutdown_DeviceStateLocked();
10641129

10651130
backendTextures.clear();
10661131
backendBuffers.clear();
@@ -1821,17 +1886,22 @@ void vhCmdBackendState::Handle_vhCmdSetStateAttachments( VIDL_vhCmdSetStateAttac
18211886
void vhCmdBackendState::Handle_vhFlushInternal( VIDL_vhFlushInternal* cmd )
18221887
{
18231888
BE_CmdRAII cmdRAII( cmd );
1889+
1890+
// TODO: Flush all transient buffer maps here.
1891+
{
1892+
std::lock_guard<std::mutex> lock( g_nvRHIStateMutex );
1893+
m_globalUniformBuffer.Unmap_DeviceStateLocked();
1894+
}
1895+
1896+
18241897
// Free all cmd memory allocations, because hitting this flush means all previous commands have been processed.
18251898
{
18261899
std::lock_guard<std::mutex> lock( g_vhMemListMutex );
18271900
g_vhMemList.clear();
18281901
}
18291902

1830-
if ( cmd->waitForGPU )
1831-
{
1832-
// This uses g_nvRHIStateMutex then gives it up, we need to avoid double-locking.
1833-
vhCmdListFlushAll();
1834-
}
1903+
// This uses g_nvRHIStateMutex then gives it up, we need to avoid double-locking.
1904+
vhCmdListFlushAll();
18351905

18361906
{
18371907
std::lock_guard< std::mutex > lock( g_nvRHIStateMutex );
@@ -1840,6 +1910,8 @@ void vhCmdBackendState::Handle_vhFlushInternal( VIDL_vhFlushInternal* cmd )
18401910
g_vhDevice->waitForIdle();
18411911
}
18421912
g_vhDevice->runGarbageCollection();
1913+
m_globalUniformBuffer.Step();
1914+
m_globalUniformBufferLastHash = 0;
18431915
}
18441916

18451917
// Notify caller that we're done.

src/vrhi_backend.h

Lines changed: 67 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,68 @@ struct vhBackendShader
6969
std::vector< vhSpecConstant > specConstants;
7070
};
7171

72+
struct vhBackendTransientBuffer
73+
{
74+
int64_t size = 0;
75+
nvrhi::BufferHandle handle[VRHI_MAX_FRAMES_INFLIGHT];
76+
uint32_t frameIdx = 0;
77+
int64_t offset = 0;
78+
uint8_t* ptr = nullptr; // For storing mapping.
79+
80+
inline void Reset() { size = 0; frameIdx = 0; offset = 0; ptr = 0; }
81+
82+
inline int64_t Alloc( int64_t bytes )
83+
{
84+
if ( offset + bytes > size )
85+
return -1;
86+
int64_t ret = offset;
87+
offset += bytes;
88+
return ret;
89+
}
90+
91+
inline void Step()
92+
{
93+
frameIdx = ( frameIdx + 1 ) % VRHI_MAX_FRAMES_INFLIGHT;
94+
offset = 0;
95+
}
96+
97+
inline void Init_DeviceStateLocked( const nvrhi::BufferDesc& desc )
98+
{
99+
// WARNING: Lock g_nvRHIStateMutex before calling this.
100+
Reset();
101+
size = desc.byteSize;
102+
for ( uint32_t i = 0; i < VRHI_MAX_FRAMES_INFLIGHT; i++ )
103+
{
104+
handle[i] = g_vhDevice->createBuffer( desc );
105+
assert( handle[i] != nullptr );
106+
}
107+
}
108+
109+
inline uint8_t* Map_DeviceStateLocked()
110+
{
111+
// WARNING: Lock g_nvRHIStateMutex before calling this.
112+
if ( ptr ) return ptr;
113+
ptr = ( uint8_t* ) g_vhDevice->mapBuffer( handle[frameIdx], nvrhi::CpuAccessMode::Write );
114+
return ptr;
115+
}
116+
117+
inline void Unmap_DeviceStateLocked()
118+
{
119+
// WARNING: Lock g_nvRHIStateMutex before calling this.
120+
if ( ptr ) g_vhDevice->unmapBuffer( handle[frameIdx] );
121+
ptr = nullptr;
122+
}
123+
124+
inline void Shutdown_DeviceStateLocked()
125+
{
126+
// WARNING: Lock g_nvRHIStateMutex before calling this.
127+
for ( uint32_t i = 0; i < VRHI_MAX_FRAMES_INFLIGHT; i++ )
128+
{
129+
handle[i] = nullptr;
130+
}
131+
}
132+
};
133+
72134
struct vhStateResolveCache
73135
{
74136
bool init = false;
@@ -120,25 +182,6 @@ struct vhStateResolveCache
120182
// Main Backend State
121183
// --------------------------------------------------------------------------
122184

123-
struct vhGlobalUniform
124-
{
125-
glm::vec4 u_viewRect;
126-
glm::vec4 u_viewTexel;
127-
glm::mat4 u_view;
128-
glm::mat4 u_invView;
129-
glm::mat4 u_proj;
130-
glm::mat4 u_invProj;
131-
glm::mat4 u_viewProj;
132-
glm::mat4 u_invViewProj;
133-
glm::mat4 u_model[4];
134-
glm::mat4 u_modelView;
135-
glm::mat4 u_modelViewProj;
136-
glm::vec4 u_alphaRef4;
137-
glm::vec4 u_global[32];
138-
};
139-
static_assert( sizeof( vhGlobalUniform ) < 16384, "vhGlobalUniform must be smaller than 16KB" );
140-
static_assert( sizeof( vhGlobalUniform ) == 1328, "vhGlobalUniform packing mismatch" );
141-
142185
class vhCmdBackendState : public VIDLHandler
143186
{
144187
friend class vhCmdBackendStateTest;
@@ -150,9 +193,9 @@ class vhCmdBackendState : public VIDLHandler
150193
std::map< vhShader, std::unique_ptr< vhBackendShader > > backendShaders;
151194
std::map< vhStateId, vhState > backendStates;
152195
std::unordered_map< uint64_t, nvrhi::FramebufferHandle > backendFramebuffers;
153-
154-
// Internal Global Uniform Buffer
155-
std::unique_ptr< vhBackendBuffer > m_globalUniformBuffer;
196+
vhBackendTransientBuffer m_globalUniformBuffer;
197+
uint64_t m_globalUniformBufferLastHash = 0;
198+
156199
// RAII for vhMem, takes ownership of the pointer and auto-destructs it.
157200
inline std::unique_ptr< vhMem > BE_MemRAII( const vhMem* mem )
158201
{
@@ -197,6 +240,8 @@ class vhCmdBackendState : public VIDLHandler
197240

198241
bool BE_Util_ShaderStageMatches( uint64_t flags, bool useCompute, bool useGraphics );
199242

243+
int64_t BE_Util_WriteGlobalUniform( const vhState& state, vhBackendTransientBuffer& tbuf, uint64_t& lastHash );
244+
200245
// --------------------------------------------------------------------------
201246
// Backend :: Complex BE Low Level NVRHI Device Functions
202247
// --------------------------------------------------------------------------

src/vrhi_buffer.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -403,4 +403,5 @@ uint64_t vhGetBufferInfo( vhBuffer buffer, uint32_t* outStride, uint64_t* outFla
403403
void* vhGetBufferNvrhiHandle( vhBuffer buffer )
404404
{
405405
return vhBackendQueryBufferHandle( buffer );
406-
}
406+
}
407+

src/vrhi_device.cpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -452,10 +452,10 @@ void vhFlushInternal( std::atomic<bool>* fence, bool waitForGPU )
452452
vhCmdEnqueue( cmd );
453453
}
454454

455-
void vhFlush()
455+
void vhFlush( bool wait )
456456
{
457457
std::atomic<bool> fence = false;
458-
vhFlushInternal( &fence, false );
458+
vhFlushInternal( wait ? &fence : nullptr, false );
459459

460460
// Wait for fence to be signaled
461461
while ( !fence.load() )
@@ -994,4 +994,7 @@ uint64_t vhHashSamplerDesc( const nvrhi::SamplerDesc& desc )
994994
return h;
995995
}
996996

997-
997+
uint64_t vhHashGlobalUniform( const vhGlobalUniform& u )
998+
{
999+
return komihash( &u, sizeof( u ), 0 );
1000+
}

src/vrhi_internal.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,10 @@
9393
// Internal Declarations
9494
// --------------------------------------------------------------------------
9595

96+
// Triple buffering is the max we can support.
97+
#define VRHI_MAX_FRAMES_INFLIGHT 3
98+
#define VRHI_CBUF_ALIGN 256
99+
96100
// Internal Vulkan State
97101
extern VkInstance g_vulkanInstance;
98102
extern VkPhysicalDevice g_vulkanPhysicalDevice;
@@ -198,6 +202,25 @@ nvrhi::GraphicsPipelineHandle vhPSOCacheGet( const nvrhi::GraphicsPipelineDesc&
198202
void vhBindingSetCacheClear();
199203
nvrhi::BindingSetHandle vhGetBindingSet( const nvrhi::BindingSetDesc& desc, nvrhi::BindingLayoutHandle layout );
200204

205+
struct vhGlobalUniform
206+
{
207+
glm::vec4 u_viewRect;
208+
glm::vec4 u_viewTexel;
209+
glm::mat4 u_view;
210+
glm::mat4 u_invView;
211+
glm::mat4 u_proj;
212+
glm::mat4 u_invProj;
213+
glm::mat4 u_viewProj;
214+
glm::mat4 u_invViewProj;
215+
glm::mat4 u_worldX[4]; // world[1+]. (world[0] is in pushbuffer).
216+
glm::mat4 u_worldView;
217+
glm::mat4 u_worldViewProj;
218+
glm::vec4 u_alphaRef4;
219+
glm::vec4 u_global[13];
220+
};
221+
static_assert( sizeof( vhGlobalUniform ) < 16384, "vhGlobalUniform must be smaller than 16KB" );
222+
static_assert( sizeof( vhGlobalUniform ) == 1024, "vhGlobalUniform packing mismatch" );
223+
201224
bool vhReflectSpirv(
202225
const std::vector< uint32_t >& spirvBlob,
203226
nvrhi::BindingLayoutDesc& outDesc,
@@ -217,6 +240,8 @@ uint64_t vhHashShaderDebugName( nvrhi::ShaderHandle shader );
217240
uint64_t vhHashShaderSPIRV( const std::vector< uint32_t >& spirv );
218241
uint64_t vhHashInputLayout( nvrhi::InputLayoutHandle layout );
219242
uint64_t vhHashSamplerDesc( const nvrhi::SamplerDesc& desc );
243+
uint64_t vhHashGlobalUniform( const vhGlobalUniform& u );
244+
void vhWriteStateToGlobalUniform( const vhState& state, vhGlobalUniform& out );
220245
nvrhi::PrimitiveType vhTranslatePrimitiveType( uint64_t stateFlags );
221246
nvrhi::BlendState vhTranslateBlendState( uint64_t stateFlags );
222247
nvrhi::DepthStencilState vhTranslateDepthStencilState( uint64_t stateFlags, uint32_t frontStencil, uint32_t backStencil );

0 commit comments

Comments
 (0)