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