Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

358 lines
11 KiB

//============ Copyright (c) Valve Corporation, All rights reserved. ============
//
// cglmbuffer.cpp
//
//===============================================================================
#include "glmgr/glmgr.h"
#include "glmgr/cglmbuffer.h"
// memdbgon -must- be the last include file in a .cpp file.
#include "tier0/memdbgon.h"
// void BindBufferARB(enum target, uint buffer);
// void DeleteBuffersARB(sizei n, const uint *buffers);
// void GenBuffersARB(sizei n, uint *buffers);
// boolean IsBufferARB(uint buffer);
//
// void BufferDataARB(enum target, sizeiptrARB size, const void *data,
// enum usage);
// void BufferSubDataARB(enum target, intptrARB offset, sizeiptrARB size,
// const void *data);
// void GetBufferSubDataARB(enum target, intptrARB offset,
// sizeiptrARB size, void *data);
//
// void *MapBufferARB(enum target, enum access);
// boolean UnmapBufferARB(enum target);
//
// void GetBufferParameterivARB(enum target, enum pname, int *params);
// void GetBufferPointervARB(enum target, enum pname, void **params);
//
//New Tokens
//
// Accepted by the <target> parameters of BindBufferARB, BufferDataARB,
// BufferSubDataARB, MapBufferARB, UnmapBufferARB,
// GetBufferSubDataARB, GetBufferParameterivARB, and
// GetBufferPointervARB:
//
// ARRAY_BUFFER_ARB 0x8892
// ELEMENT_ARRAY_BUFFER_ARB 0x8893
//
// Accepted by the <pname> parameter of GetBooleanv, GetIntegerv,
// GetFloatv, and GetDoublev:
//
// ARRAY_BUFFER_BINDING_ARB 0x8894
// ELEMENT_ARRAY_BUFFER_BINDING_ARB 0x8895
// VERTEX_ARRAY_BUFFER_BINDING_ARB 0x8896
// NORMAL_ARRAY_BUFFER_BINDING_ARB 0x8897
// COLOR_ARRAY_BUFFER_BINDING_ARB 0x8898
// INDEX_ARRAY_BUFFER_BINDING_ARB 0x8899
// TEXTURE_COORD_ARRAY_BUFFER_BINDING_ARB 0x889A
// EDGE_FLAG_ARRAY_BUFFER_BINDING_ARB 0x889B
// SECONDARY_COLOR_ARRAY_BUFFER_BINDING_ARB 0x889C
// FOG_COORDINATE_ARRAY_BUFFER_BINDING_ARB 0x889D
// WEIGHT_ARRAY_BUFFER_BINDING_ARB 0x889E
//
// Accepted by the <pname> parameter of GetVertexAttribivARB:
//
// VERTEX_ATTRIB_ARRAY_BUFFER_BINDING_ARB 0x889F
//
// Accepted by the <usage> parameter of BufferDataARB:
//
// STREAM_DRAW_ARB 0x88E0
// STREAM_READ_ARB 0x88E1
// STREAM_COPY_ARB 0x88E2
// STATIC_DRAW_ARB 0x88E4
// STATIC_READ_ARB 0x88E5
// STATIC_COPY_ARB 0x88E6
// DYNAMIC_DRAW_ARB 0x88E8
// DYNAMIC_READ_ARB 0x88E9
// DYNAMIC_COPY_ARB 0x88EA
//
// Accepted by the <access> parameter of MapBufferARB:
//
// READ_ONLY_ARB 0x88B8
// WRITE_ONLY_ARB 0x88B9
// READ_WRITE_ARB 0x88BA
//
// Accepted by the <pname> parameter of GetBufferParameterivARB:
//
// BUFFER_SIZE_ARB 0x8764
// BUFFER_USAGE_ARB 0x8765
// BUFFER_ACCESS_ARB 0x88BB
// BUFFER_MAPPED_ARB 0x88BC
//
// Accepted by the <pname> parameter of GetBufferPointervARB:
//
// BUFFER_MAP_POINTER_ARB 0x88BD
// http://www.opengl.org/registry/specs/ARB/pixel_buffer_object.txt
// Accepted by the <target> parameters of BindBuffer, BufferData,
// BufferSubData, MapBuffer, UnmapBuffer, GetBufferSubData,
// GetBufferParameteriv, and GetBufferPointerv:
// PIXEL_PACK_BUFFER_ARB 0x88EB
// PIXEL_UNPACK_BUFFER_ARB 0x88EC
// gl_bufmode: zero means we mark all vertex/index buffers static
// non zero means buffers are initially marked static..
// ->but can shift to dynamic upon first 'discard' (orphaning)
ConVar gl_bufmode( "gl_bufmode", "1" );
CGLMBuffer::CGLMBuffer( GLMContext *ctx, EGLMBufferType type, uint size, uint options )
{
m_ctx = ctx;
m_type = type;
switch(m_type)
{
case kGLMVertexBuffer: m_buffGLTarget = GL_ARRAY_BUFFER_ARB; break;
case kGLMIndexBuffer: m_buffGLTarget = GL_ELEMENT_ARRAY_BUFFER_ARB; break;
case kGLMUniformBuffer: m_buffGLTarget = GL_UNIFORM_BUFFER_EXT; break;
case kGLMPixelBuffer: m_buffGLTarget = GL_PIXEL_UNPACK_BUFFER_ARB; break;
default: Assert(!"Unknown buffer type" );
}
m_size = size;
m_bound = false;
m_mapped = false;
m_lastMappedAddress = NULL;
m_enableAsyncMap = false;
m_enableExplicitFlush = false;
m_dirtyMinOffset = m_dirtyMaxOffset = 0; // adjust/grow on lock, clear on unlock
m_ctx->CheckCurrent();
m_revision = rand();
// make a decision about pseudo mode
// this looked like it didn't help much or was actually slower, so leave it available but only as opt-in.
// a more clever implementation would be able to select pseudo buf storage for small batches only..
m_pseudo = (m_type==kGLMIndexBuffer) && (CommandLine()->FindParm("-gl_enable_pseudobufs"));
if (m_pseudo)
{
m_name = 0;
m_pseudoBuf = (char*)malloc( size );
m_ctx->BindBufferToCtx( m_type, NULL ); // exit with no buffer bound
}
else
{
glGenBuffersARB( 1, &m_name );
GLMCheckError();
m_ctx->BindBufferToCtx( m_type, this ); // causes glBindBufferARB
// buffers start out static, but if they get orphaned and gl_bufmode is non zero,
// then they will get flipped to dynamic.
GLenum hint = GL_STATIC_DRAW_ARB;
switch(m_type)
{
case kGLMVertexBuffer: hint = (options & GLMBufferOptionDynamic) ? GL_DYNAMIC_DRAW_ARB : GL_STATIC_DRAW_ARB; break;
case kGLMIndexBuffer: hint = (options & GLMBufferOptionDynamic) ? GL_DYNAMIC_DRAW_ARB : GL_STATIC_DRAW_ARB; break;
case kGLMUniformBuffer: hint = GL_DYNAMIC_DRAW_ARB; break; // "fwiw" - shrug
case kGLMPixelBuffer: hint = (options & GLMBufferOptionDynamic) ? GL_DYNAMIC_DRAW_ARB : GL_STATIC_DRAW_ARB; break;
default: Assert(!"Unknown buffer type" );
}
glBufferDataARB( m_buffGLTarget, m_size, NULL, hint ); // may ultimately need more hints to set the usage correctly (esp for streaming)
this->SetModes( true, true, true );
m_ctx->BindBufferToCtx( m_type, NULL ); // unbind me
}
}
CGLMBuffer::~CGLMBuffer( )
{
m_ctx->CheckCurrent();
if (m_pseudo)
{
free (m_pseudoBuf);
m_pseudoBuf = NULL;
}
else
{
glDeleteBuffersARB( 1, &m_name );
GLMCheckError();
}
m_ctx = NULL;
m_name = 0;
m_bound = 0;
m_lastMappedAddress = NULL;
}
void CGLMBuffer::SetModes ( bool asyncMap, bool explicitFlush, bool force )
{
// assumes buffer is bound. called by constructor and by Lock.
if (m_pseudo)
{
// ignore it...
}
else
{
if (force || (m_enableAsyncMap != asyncMap) )
{
// note the sense of the parameter, it's TRUE if you *want* serialization, so for async you turn it to false.
glBufferParameteriAPPLE( this->m_buffGLTarget, GL_BUFFER_SERIALIZED_MODIFY_APPLE, asyncMap==false );
m_enableAsyncMap = asyncMap;
}
if (force || (m_enableExplicitFlush != explicitFlush) )
{
// note the sense of the parameter, it's TRUE if you *want* auto-flush-on-unmap, so for explicit-flush, you turn it to false.
glBufferParameteriAPPLE( this->m_buffGLTarget, GL_BUFFER_FLUSHING_UNMAP_APPLE, explicitFlush==false );
m_enableExplicitFlush = explicitFlush;
}
}
}
void CGLMBuffer::FlushRange ( uint offset, uint size )
{
if (m_pseudo)
{
// nothing to do
}
else
{
// assumes buffer is bound.
glFlushMappedBufferRangeAPPLE(this->m_buffGLTarget, (GLintptr)offset, (GLsizeiptr)size);
}
}
ConVar gl_buffer_alignment_quantum ( "gl_buffer_alignment_quantum", "32" ); // the alignment we use pre-SLGU
ConVar gl_buffer_alignment_quantum_slgu( "gl_buffer_alignment_quantum_slgu", "2" ); // alignment used post-SLGU
void CGLMBuffer::Lock( GLMBuffLockParams *params, char **addressOut )
{
char *resultPtr = NULL;
Assert( !m_mapped);
m_ctx->CheckCurrent();
GLMCheckError();
if (params->m_offset >= m_size)
Debugger();
if (params->m_offset + params->m_size > m_size)
Debugger();
// bind (yes, even for pseudo - this binds name 0)
m_ctx->BindBufferToCtx( this->m_type, this );
if (m_pseudo)
{
// discard is a no-op
// async map modes are a no-op
// latch last mapped address (silly..)
m_lastMappedAddress = (float*)m_pseudoBuf;
// calc lock address
resultPtr = m_pseudoBuf + params->m_offset;
// dirty range is a no-op
}
else
{
// perform discard if requested
if (params->m_discard)
{
// observe gl_bufmode on any orphan event.
// if orphaned and bufmode is nonzero, flip it to dynamic.
GLenum hint = gl_bufmode.GetInt() ? GL_DYNAMIC_DRAW_ARB : GL_STATIC_DRAW_ARB;
glBufferDataARB( m_buffGLTarget, m_size, NULL, hint );
m_lastMappedAddress = NULL;
m_revision++; // revision grows on orphan event
}
// adjust async map option appropriately, leave explicit flush unchanged
this->SetModes( params->m_nonblocking, m_enableExplicitFlush );
// map
char *mapPtr = (char*)glMapBufferARB( this->m_buffGLTarget, GL_READ_WRITE_ARB );
if (!mapPtr)
{
Debugger();
}
if (m_lastMappedAddress)
{
// just check if it moved
Assert (m_lastMappedAddress == (float*)mapPtr);
}
m_lastMappedAddress = (float*)mapPtr;
// calculate offset location
resultPtr = mapPtr + params->m_offset;
// adjust dirty range
if (m_dirtyMinOffset != m_dirtyMaxOffset)
{
// grow range
m_dirtyMinOffset = MIN( m_dirtyMinOffset, params->m_offset );
m_dirtyMaxOffset = MIN( m_dirtyMaxOffset, params->m_offset+params->m_size );
}
else
{
// set range
m_dirtyMinOffset = params->m_offset;
m_dirtyMaxOffset = params->m_offset+params->m_size;
}
// pad and clamp dirty range to choice of boundary
uint quantum = (m_ctx->Caps().m_hasPerfPackage1) ? gl_buffer_alignment_quantum_slgu.GetInt() : gl_buffer_alignment_quantum.GetInt();
uint quantum_mask = quantum - 1;
m_dirtyMinOffset = m_dirtyMinOffset & (~quantum_mask);
m_dirtyMaxOffset = (m_dirtyMaxOffset + quantum_mask) & (~quantum_mask);
m_dirtyMaxOffset = MIN( m_dirtyMaxOffset, m_size );
}
m_mapped = true;
*addressOut = resultPtr;
}
void CGLMBuffer::Unlock( void )
{
m_ctx->CheckCurrent();
Assert (m_mapped);
if (m_pseudo)
{
// nothing to do actually
}
else
{
m_ctx->BindBufferToCtx( this->m_type, this );
// time to do explicit flush
if (m_enableExplicitFlush)
{
this->FlushRange( m_dirtyMinOffset, m_dirtyMaxOffset - m_dirtyMinOffset );
}
// clear dirty range no matter what
m_dirtyMinOffset = m_dirtyMaxOffset = 0; // adjust/grow on lock, clear on unlock
glUnmapBuffer( this->m_buffGLTarget );
}
m_mapped = false;
}