demotool/uniforms.cc

348 lines
8.7 KiB
C++
Raw Normal View History

2017-10-09 10:58:03 +02:00
#include "externals.hh"
2017-10-09 12:49:08 +02:00
#include "utilities.hh"
2017-10-09 10:58:03 +02:00
#include "uniforms.hh"
/*= Uniform types ============================================================*/
E_UniformCategory GetUniformCategory(
__rd__ const E_UniformType type )
{
switch ( type ) {
case E_UniformType::F1:
case E_UniformType::F2:
case E_UniformType::F3:
case E_UniformType::F4:
return E_UniformCategory::FLOAT;
case E_UniformType::I1:
case E_UniformType::I2:
case E_UniformType::I3:
case E_UniformType::I4:
return E_UniformCategory::INT;
case E_UniformType::SAMPLER2D:
return E_UniformCategory::SAMPLER2D;
}
std::abort( );
}
uint32_t GetElementCount(
__rd__ const E_UniformType type )
{
switch ( type ) {
case E_UniformType::F1:
case E_UniformType::I1:
return 1;
case E_UniformType::F2:
case E_UniformType::I2:
return 2;
case E_UniformType::F3:
case E_UniformType::I3:
return 3;
case E_UniformType::F4:
case E_UniformType::I4:
return 4;
case E_UniformType::SAMPLER2D:
return 0;
}
std::abort( );
}
bool IsVectorCategory(
__rd__ const E_UniformCategory category )
{
assert( category != E_UniformCategory::__COUNT__ );
return category == E_UniformCategory::FLOAT
|| category == E_UniformCategory::INT;
}
/*= PackUniforms =============================================================*/
namespace {
T_PackedUniforms PackVectorUniforms_(
__rd__ T_UniformDeclarations const& declarations ,
__rd__ const E_UniformCategory category )
{
std::vector< std::string > packOrder;
packOrder.reserve( declarations.size( ) );
for ( auto entry : declarations ) {
auto const& u( entry.second );
if ( GetUniformCategory( u.type ) == category ) {
packOrder.push_back( entry.first );
}
}
std::sort( packOrder.begin( ) , packOrder.end( ) ,
[&declarations]( auto const& a , auto const& b )
{
auto const& ua( declarations.find( a )->second );
auto const& ub( declarations.find( b )->second );
return GetElementCount( ub.type ) < GetElementCount( ua.type );
} );
const auto n( packOrder.size( ) );
auto index = 0u;
T_PackedUniforms pu;
// Place vec4's
while ( index < n && GetElementCount( declarations.find( packOrder[ index ] )->second.type ) == 4 ) {
pu.positions[ packOrder[ index ] ] = pu.elements;
pu.elements += 4;
index ++;
}
// Place vec3's
uint32_t s3left( 0 );
while ( index < n && GetElementCount( declarations.find( packOrder[ index ] )->second.type ) == 3 ) {
pu.positions[ packOrder[ index ] ] = pu.elements;
pu.elements += 4;
s3left ++;
index ++;
}
// Place vec2's
const uint32_t s2s( pu.elements );
bool s2spare( false );
while ( index < n && GetElementCount( declarations.find( packOrder[ index ] )->second.type ) == 2 ) {
if ( !s2spare ) {
pu.elements += 4;
}
const uint32_t pos( pu.elements - ( s2spare ? 2 : 4 ) );
pu.positions[ packOrder[ index ] ] = pos;
s2spare = !s2spare;
index ++;
}
// Place single elements
uint32_t s1pos( s2spare ? ( pu.elements - 2 ) : pu.elements );
while ( index < n ) {
uint32_t pos;
if ( s3left ) {
pos = s2s - s3left * 4 + 3;
s3left --;
} else {
if ( s1pos == pu.elements ) {
pu.elements += 4;
}
pos = s1pos;
s1pos ++;
}
pu.positions[ packOrder[ index ] ] = pos;
index ++;
}
return pu;
}
T_PackedUniforms PackOpaqueUniforms_(
__rd__ T_UniformDeclarations const& declarations ,
__rd__ const E_UniformCategory category )
{
T_PackedUniforms pu;
for ( auto entry : declarations ) {
auto const& u( entry.second );
if ( GetUniformCategory( u.type ) == category ) {
pu.positions[ entry.first ] = pu.elements;
pu.elements ++;
}
}
return pu;
}
} // namespace
/*----------------------------------------------------------------------------*/
T_PackedUniforms PackUniforms(
__rd__ T_UniformDeclarations const& declarations ,
__rd__ const E_UniformCategory category )
{
assert( category != E_UniformCategory::__COUNT__ );
if ( IsVectorCategory( category ) ) {
return PackVectorUniforms_( declarations , category );
}
return PackOpaqueUniforms_( declarations , category );
}
/*= T_UniformSet =============================================================*/
constexpr uint32_t T_UniformSet::Invalid;
T_UniformSet::T_UniformSet( )
: vectorElements_( 0 ) , opaqueElements_( 0 )
{ }
T_UniformSet::T_UniformSet(
__rw__ T_UniformDeclarations declarations )
: declarations_( std::move( declarations ) ) ,
vectorElements_( 0 ) , opaqueElements_( 0 )
{
for ( auto i = 0 ; i < int( E_UniformCategory::__COUNT__ ) ; i ++ ) {
const auto cat{ E_UniformCategory( i ) };
packed_[ i ] = PackUniforms( declarations_ , cat );
if ( IsVectorCategory( cat ) ) {
vectorElements_ += packed_[ i ].elements;
} else {
opaqueElements_ += packed_[ i ].elements;
}
}
}
uint32_t T_UniformSet::position(
__rd__ std::string const& uniform ) const
{
const auto pos( declarations_.find( uniform ) );
if ( pos == declarations_.end( ) ) {
return Invalid;
}
const int ci( int( GetUniformCategory( pos->second.type ) ) );
const auto ePos( packed_[ ci ].positions.find( uniform ) );
assert( ePos != packed_[ ci ].positions.end( ) );
return ePos->second;
}
2017-10-09 12:49:08 +02:00
/*= T_UniformStorage =========================================================*/
T_UniformStorage::T_UniformStorage(
__rw__ T_UniformSet&& uniforms )
: uniforms_( std::move( uniforms ) )
{
values_.resize( uniforms_.opaqueElements( )
+ uniforms_.vectorElements( ) , 0 );
uint32_t pos = 0;
for ( auto i = 0 ; i < int( E_UniformCategory::__COUNT__ ) ; i ++ ) {
starts_[ i ] = pos;
pos += uniforms_.inCategory( E_UniformCategory( i ) );
}
}
float* T_UniformStorage::getf(
__rd__ std::string const& name )
{
auto const& dPos( uniforms_.declarations( ).find( name ) );
if ( dPos == uniforms_.declarations( ).end( ) ) {
return nullptr;
}
auto const& decl( dPos->second );
const auto cat( GetUniformCategory( decl.type ) );
const auto pos( starts_[ int( cat ) ] + uniforms_.position( name ) );
return reinterpret_cast< float* >( &values_[ pos ] );
}
int* T_UniformStorage::geti(
__rd__ std::string const& name )
{
auto const& dPos( uniforms_.declarations( ).find( name ) );
if ( dPos == uniforms_.declarations( ).end( ) ) {
return nullptr;
}
auto const& decl( dPos->second );
const auto cat( GetUniformCategory( decl.type ) );
const auto pos( starts_[ int( cat ) ] + uniforms_.position( name ) );
return &values_[ pos ];
}
/*= T_ProgramUniforms ========================================================*/
T_ProgramUniforms::T_ProgramUniforms(
__rw__ T_UniformSet uniforms )
: T_UniformStorage( std::move( uniforms ) )
{ }
void T_ProgramUniforms::apply(
__rd__ const GLuint id )
{
GLuint uLoc( 0 );
for ( auto i = 0 ; i < int( E_UniformCategory::__COUNT__ ) ; i ++ ) {
const auto cat{ E_UniformCategory( i ) };
const auto count{ uniforms_.inCategory( cat ) };
if ( count != 0 ) {
switch ( cat ) {
case E_UniformCategory::FLOAT:
glProgramUniform4fv( id , uLoc , count / 4 ,
reinterpret_cast< float* >( &values_[ starts_[ i ] ] ) );
uLoc += count / 4;
break;
case E_UniformCategory::INT:
glProgramUniform4iv( id , uLoc , count / 4 ,
&values_[ starts_[ i ] ] );
uLoc += count / 4;
break;
case E_UniformCategory::SAMPLER2D:
glProgramUniform1iv( id , uLoc , count ,
&values_[ starts_[ i ] ] );
uLoc += count;
break;
case E_UniformCategory::__COUNT__:
std::abort( );
}
}
}
}
/*= T_UniformBuffer ==========================================================*/
T_UniformBuffer::T_UniformBuffer(
__rw__ T_UniformSet set )
: T_UniformStorage( std::move( set ) ) , buffer_( 0 )
{
assert( uniforms_.opaqueElements( ) == 0 );
glGenBuffers( 1 , &buffer_ );
glBindBuffer( GL_UNIFORM_BUFFER , buffer_ );
glBufferData( GL_UNIFORM_BUFFER , values_.size( ) * 4 , &values_[ 0 ] , GL_STREAM_DRAW );
GL_ASSERT( );
}
T_UniformBuffer::T_UniformBuffer(
__rw__ T_UniformBuffer&& ori ) noexcept
: T_UniformStorage( std::move( ori ) ) , buffer_( 0 )
{
std::swap( buffer_ , ori.buffer_ );
}
T_UniformBuffer& T_UniformBuffer::operator=(
__rw__ T_UniformBuffer&& other ) noexcept
{
if ( buffer_ ) {
glBindBuffer( GL_UNIFORM_BUFFER , 0 );
glDeleteBuffers( 1 , &buffer_ );
buffer_ = 0;
}
std::swap( buffer_ , other.buffer_ );
T_UniformStorage::operator=( std::move( other ) );
return *this;
}
void T_UniformBuffer::update( )
{
glBindBuffer( GL_UNIFORM_BUFFER , buffer_ );
glInvalidateBufferData( GL_UNIFORM_BUFFER );
glBufferData( GL_UNIFORM_BUFFER , values_.size( ) * 4 , &values_[ 0 ] , GL_STREAM_DRAW );
}
void T_UniformBuffer::bindTo(
__rd__ const GLuint binding )
{
glBindBufferBase( GL_UNIFORM_BUFFER , binding , buffer_ );
}
T_UniformBuffer::~T_UniformBuffer( )
{
if ( buffer_ ) {
glBindBuffer( GL_UNIFORM_BUFFER , 0 );
glDeleteBuffers( 1 , &buffer_ );
}
}