/* ============== Tess_CheckVBOAndIBO ============== */ static void Tess_CheckVBOAndIBO( VBO_t *vbo, IBO_t *ibo ) { if ( glState.currentVBO != vbo || glState.currentIBO != ibo || tess.multiDrawPrimitives >= MAX_MULTIDRAW_PRIMITIVES ) { Tess_EndBegin(); R_BindVBO( vbo ); R_BindIBO( ibo ); } }
/* ============ R_CreateVBO ============ */ VBO_t *R_CreateStaticVBO( const char *name, vboData_t data, vboLayout_t layout ) { VBO_t *vbo; byte *outData; if ( strlen( name ) >= MAX_QPATH ) { ri.Error( ERR_DROP, "R_CreateVBO: \"%s\" is too long", name ); } // make sure the render thread is stopped R_SyncRenderThread(); vbo = (VBO_t*) ri.Hunk_Alloc( sizeof( *vbo ), h_low ); memset( vbo, 0, sizeof( *vbo ) ); Com_AddToGrowList( &tr.vbos, vbo ); Q_strncpyz( vbo->name, name, sizeof( vbo->name ) ); vbo->layout = layout; vbo->vertexesNum = data.numVerts; vbo->framesNum = data.numFrames; vbo->attribBits = R_DeriveAttrBits( data ); vbo->usage = GL_STATIC_DRAW; R_SetVBOAttributeLayouts( vbo ); glGenBuffers( 1, &vbo->vertexesVBO ); R_BindVBO( vbo ); #ifdef GLEW_ARB_buffer_storage if( glConfig2.bufferStorageAvailable ) { outData = (byte *)ri.Hunk_AllocateTempMemory( vbo->vertexesSize ); R_CopyVertexData( vbo, outData, data ); glBufferStorage( GL_ARRAY_BUFFER, vbo->vertexesSize, outData, 0 ); ri.Hunk_FreeTempMemory( outData ); } else #endif { glBufferData( GL_ARRAY_BUFFER, vbo->vertexesSize, nullptr, vbo->usage ); outData = (byte *)glMapBuffer( GL_ARRAY_BUFFER, GL_WRITE_ONLY ); R_CopyVertexData( vbo, outData, data ); glUnmapBuffer( GL_ARRAY_BUFFER ); } R_BindNullVBO(); GL_CheckErrors(); return vbo; }
VBO_t *R_CreateDynamicVBO( const char *name, int numVertexes, uint32_t stateBits, vboLayout_t layout ) { VBO_t *vbo; if ( !numVertexes ) { return nullptr; } if ( strlen( name ) >= MAX_QPATH ) { ri.Error( ERR_DROP, "R_CreateDynamicVBO: \"%s\" is too long", name ); } // make sure the render thread is stopped R_SyncRenderThread(); vbo = (VBO_t*) ri.Hunk_Alloc( sizeof( *vbo ), h_low ); memset( vbo, 0, sizeof( *vbo ) ); Com_AddToGrowList( &tr.vbos, vbo ); Q_strncpyz( vbo->name, name, sizeof( vbo->name ) ); vbo->layout = layout; vbo->framesNum = 0; vbo->vertexesNum = numVertexes; vbo->attribBits = stateBits; vbo->usage = GL_DYNAMIC_DRAW; R_SetVBOAttributeLayouts( vbo ); glGenBuffers( 1, &vbo->vertexesVBO ); R_BindVBO( vbo ); #if defined( GLEW_ARB_buffer_storage ) && defined( GLEW_ARB_sync ) if( glConfig2.bufferStorageAvailable && glConfig2.syncAvailable ) { R_InitRingbuffer( GL_ARRAY_BUFFER, sizeof( shaderVertex_t ), numVertexes, &tess.vertexRB ); } else #endif { glBufferData( GL_ARRAY_BUFFER, vbo->vertexesSize, nullptr, vbo->usage ); } R_BindNullVBO(); GL_CheckErrors(); return vbo; }
/* ============ R_CreateVBO2 ============ */ VBO_t *R_CreateStaticVBO2( const char *name, int numVertexes, shaderVertex_t *verts, unsigned int stateBits ) { VBO_t *vbo; if ( !numVertexes ) { return nullptr; } if ( strlen( name ) >= MAX_QPATH ) { ri.Error( ERR_DROP, "R_CreateVBO2: \"%s\" is too long", name ); } // make sure the render thread is stopped R_SyncRenderThread(); vbo = ( VBO_t * ) ri.Hunk_Alloc( sizeof( *vbo ), h_low ); memset( vbo, 0, sizeof( *vbo ) ); Com_AddToGrowList( &tr.vbos, vbo ); Q_strncpyz( vbo->name, name, sizeof( vbo->name ) ); vbo->layout = VBO_LAYOUT_STATIC; vbo->framesNum = 0; vbo->vertexesNum = numVertexes; vbo->attribBits = stateBits; vbo->usage = GL_STATIC_DRAW; R_SetVBOAttributeLayouts( vbo ); glGenBuffers( 1, &vbo->vertexesVBO ); R_BindVBO( vbo ); #ifdef GLEW_ARB_buffer_storage if( glConfig2.bufferStorageAvailable ) { glBufferStorage( GL_ARRAY_BUFFER, vbo->vertexesSize, verts, 0 ); } else #endif { glBufferData( GL_ARRAY_BUFFER, vbo->vertexesSize, verts, vbo->usage ); } R_BindNullVBO(); GL_CheckErrors(); return vbo; }
/* ============== Tess_SurfaceVBOMD5Mesh ============== */ static void Tess_SurfaceVBOMD5Mesh( srfVBOMD5Mesh_t *srf ) { int i; md5Model_t *model; GLimp_LogComment( "--- Tess_SurfaceVBOMD5Mesh ---\n" ); if ( !srf->vbo || !srf->ibo ) { return; } Tess_EndBegin(); R_BindVBO( srf->vbo ); R_BindIBO( srf->ibo ); tess.numIndexes = srf->numIndexes; tess.numVertexes = srf->numVerts; model = srf->md5Model; tess.vboVertexSkinning = true; tess.numBones = srf->numBoneRemap; for ( i = 0; i < srf->numBoneRemap; i++ ) { refBone_t *bone = &backEnd.currentEntity->e.skeleton.bones[ srf->boneRemapInverse[ i ] ]; if ( backEnd.currentEntity->e.skeleton.type == SK_ABSOLUTE ) { TransInitRotationQuat( model->bones[ srf->boneRemapInverse[ i ] ].rotation, &tess.bones[ i ] ); TransAddTranslation( model->bones[ srf->boneRemapInverse[ i ] ].origin, &tess.bones[ i ] ); TransInverse( &tess.bones[ i ], &tess.bones[ i ] ); TransCombine( &tess.bones[ i ], &bone->t, &tess.bones[ i ] ); } else { TransInit( &tess.bones[ i ] ); } TransAddScale( backEnd.currentEntity->e.skeleton.scale, &tess.bones[ i ] ); TransInsScale( model->internalScale, &tess.bones[ i ] ); } Tess_End(); }
/* ============== Tess_SurfaceVBOMDVMesh ============== */ void Tess_SurfaceVBOMDVMesh( srfVBOMDVMesh_t *surface ) { refEntity_t *refEnt; GLimp_LogComment( "--- Tess_SurfaceVBOMDVMesh ---\n" ); if ( !surface->vbo || !surface->ibo ) { return; } Tess_EndBegin(); R_BindVBO( surface->vbo ); R_BindIBO( surface->ibo ); tess.numIndexes = surface->numIndexes; tess.numVertexes = surface->numVerts; tess.vboVertexAnimation = true; refEnt = &backEnd.currentEntity->e; if ( refEnt->oldframe == refEnt->frame ) { glState.vertexAttribsInterpolation = 0; } else { glState.vertexAttribsInterpolation = ( 1.0 - refEnt->backlerp ); } glState.vertexAttribsOldFrame = refEnt->oldframe; glState.vertexAttribsNewFrame = refEnt->frame; Tess_End(); }
/* ================ Tess_StageIteratorSky All of the visible sky triangles are in tess Other things could be stuck in here, like birds in the sky, etc ================ */ void Tess_StageIteratorSky( void ) { // log this call if ( r_logFile->integer ) { // don't just call LogComment, or we will get // a call to va() every frame! GLimp_LogComment( va ( "--- Tess_StageIteratorSky( %s, %i vertices, %i triangles ) ---\n", tess.surfaceShader->name, tess.numVertexes, tess.numIndexes / 3 ) ); } if ( r_fastsky->integer ) { return; } // trebor: HACK why does this happen with cg_draw2D 0 ? if ( tess.stageIteratorFunc2 == NULL ) { //tess.stageIteratorFunc2 = Tess_StageIteratorGeneric; ri.Error( ERR_FATAL, "tess.stageIteratorFunc == NULL" ); } GL_Cull(CT_TWO_SIDED); if ( tess.stageIteratorFunc2 == &Tess_StageIteratorDepthFill ) { // go through all the polygons and project them onto // the sky box to see which blocks on each side need // to be drawn Tess_ClipSkyPolygons(); // generate the vertexes for all the clouds, which will be drawn // by the generic shader routine BuildCloudData(); if ( tess.numVertexes || tess.multiDrawPrimitives ) { tess.stageIteratorFunc2(); } } else { if ( tess.stageIteratorFunc2 == &Tess_StageIteratorGBuffer ) { R_BindFBO( tr.geometricRenderFBO ); } // go through all the polygons and project them onto // the sky box to see which blocks on each side need // to be drawn Tess_ClipSkyPolygons(); // r_showSky will let all the sky blocks be drawn in // front of everything to allow developers to see how // much sky is getting sucked in if ( r_showSky->integer ) { glDepthRange( 0.0, 0.0 ); } else { glDepthRange( 1.0, 1.0 ); } // draw the outer skybox if ( tess.surfaceShader->sky.outerbox && tess.surfaceShader->sky.outerbox != tr.blackCubeImage ) { #if 1 R_BindVBO( tess.vbo ); R_BindIBO( tess.ibo ); gl_skyboxShader->BindProgram(); gl_skyboxShader->SetUniform_ViewOrigin( backEnd.viewParms.orientation.origin ); // in world space gl_skyboxShader->SetUniform_ModelMatrix( backEnd.orientation.transformMatrix ); gl_skyboxShader->SetUniform_ModelViewProjectionMatrix( glState.modelViewProjectionMatrix[ glState.stackIndex ] ); gl_skyboxShader->SetRequiredVertexPointers(); // bind u_ColorMap GL_SelectTexture( 0 ); GL_Bind( tess.surfaceShader->sky.outerbox ); DrawSkyBox( tess.surfaceShader ); #endif } // generate the vertexes for all the clouds, which will be drawn // by the generic shader routine BuildCloudData(); if ( tess.numVertexes || tess.multiDrawPrimitives ) { tess.stageIteratorFunc2(); } // Tr3B: TODO draw the inner skybox? if ( tess.stageIteratorFunc2 == Tess_StageIteratorGBuffer ) { R_BindNullFBO(); } if ( tess.stageIteratorFunc2 != Tess_StageIteratorDepthFill ) { // back to standard depth range glDepthRange( 0.0, 1.0 ); // note that sky was drawn so we will draw a sun later backEnd.skyRenderedThisView = qtrue; } } }
/* ============== RB_UpdateVBOs Adapted from Tess_UpdateVBOs from xreal Update the default VBO to replace the client side vertex arrays ============== */ void RB_UpdateVBOs(unsigned int attribBits) { GLimp_LogComment("--- RB_UpdateVBOs ---\n"); backEnd.pc.c_dynamicVboDraws++; // update the default VBO if(tess.numVertexes > 0 && tess.numVertexes <= SHADER_MAX_VERTEXES) { R_BindVBO(tess.vbo); // orphan old buffer so we don't stall on it qglBufferDataARB(GL_ARRAY_BUFFER_ARB, tess.vbo->vertexesSize, NULL, GL_DYNAMIC_DRAW_ARB); if(attribBits & ATTR_BITS) { if(attribBits & ATTR_POSITION) { //ri.Printf(PRINT_ALL, "offset %d, size %d\n", tess.vbo->ofs_xyz, tess.numVertexes * sizeof(tess.xyz[0])); qglBufferSubDataARB(GL_ARRAY_BUFFER_ARB, tess.vbo->ofs_xyz, tess.numVertexes * sizeof(tess.xyz[0]), tess.xyz); } if(attribBits & ATTR_TEXCOORD || attribBits & ATTR_LIGHTCOORD) { // these are interleaved, so we update both if either need it //ri.Printf(PRINT_ALL, "offset %d, size %d\n", tess.vbo->ofs_st, tess.numVertexes * sizeof(tess.texCoords[0][0]) * 2); qglBufferSubDataARB(GL_ARRAY_BUFFER_ARB, tess.vbo->ofs_st, tess.numVertexes * sizeof(tess.texCoords[0][0]) * 2, tess.texCoords); } if(attribBits & ATTR_NORMAL) { //ri.Printf(PRINT_ALL, "offset %d, size %d\n", tess.vbo->ofs_normal, tess.numVertexes * sizeof(tess.normal[0])); qglBufferSubDataARB(GL_ARRAY_BUFFER_ARB, tess.vbo->ofs_normal, tess.numVertexes * sizeof(tess.normal[0]), tess.normal); } #ifdef USE_VERT_TANGENT_SPACE if(attribBits & ATTR_TANGENT) { //ri.Printf(PRINT_ALL, "offset %d, size %d\n", tess.vbo->ofs_tangent, tess.numVertexes * sizeof(tess.tangent[0])); qglBufferSubDataARB(GL_ARRAY_BUFFER_ARB, tess.vbo->ofs_tangent, tess.numVertexes * sizeof(tess.tangent[0]), tess.tangent); } #endif if(attribBits & ATTR_COLOR) { //ri.Printf(PRINT_ALL, "offset %d, size %d\n", tess.vbo->ofs_vertexcolor, tess.numVertexes * sizeof(tess.vertexColors[0])); qglBufferSubDataARB(GL_ARRAY_BUFFER_ARB, tess.vbo->ofs_vertexcolor, tess.numVertexes * sizeof(tess.vertexColors[0]), tess.vertexColors); } if(attribBits & ATTR_LIGHTDIRECTION) { //ri.Printf(PRINT_ALL, "offset %d, size %d\n", tess.vbo->ofs_lightdir, tess.numVertexes * sizeof(tess.lightdir[0])); qglBufferSubDataARB(GL_ARRAY_BUFFER_ARB, tess.vbo->ofs_lightdir, tess.numVertexes * sizeof(tess.lightdir[0]), tess.lightdir); } } else { qglBufferSubDataARB(GL_ARRAY_BUFFER_ARB, tess.vbo->ofs_xyz, tess.numVertexes * sizeof(tess.xyz[0]), tess.xyz); qglBufferSubDataARB(GL_ARRAY_BUFFER_ARB, tess.vbo->ofs_st, tess.numVertexes * sizeof(tess.texCoords[0][0]) * 2, tess.texCoords); qglBufferSubDataARB(GL_ARRAY_BUFFER_ARB, tess.vbo->ofs_normal, tess.numVertexes * sizeof(tess.normal[0]), tess.normal); #ifdef USE_VERT_TANGENT_SPACE qglBufferSubDataARB(GL_ARRAY_BUFFER_ARB, tess.vbo->ofs_tangent, tess.numVertexes * sizeof(tess.tangent[0]), tess.tangent); #endif qglBufferSubDataARB(GL_ARRAY_BUFFER_ARB, tess.vbo->ofs_vertexcolor, tess.numVertexes * sizeof(tess.vertexColors[0]), tess.vertexColors); qglBufferSubDataARB(GL_ARRAY_BUFFER_ARB, tess.vbo->ofs_lightdir, tess.numVertexes * sizeof(tess.lightdir[0]), tess.lightdir); } } // update the default IBO if(tess.numIndexes > 0 && tess.numIndexes <= SHADER_MAX_INDEXES) { R_BindIBO(tess.ibo); // orphan old buffer so we don't stall on it qglBufferDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, tess.ibo->indexesSize, NULL, GL_DYNAMIC_DRAW_ARB); qglBufferSubDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0, tess.numIndexes * sizeof(tess.indexes[0]), tess.indexes); } }
/* ================= Tess_SurfaceIQM Compute vertices for this model surface ================= */ void Tess_SurfaceIQM( srfIQModel_t *surf ) { IQModel_t *model = surf->data; int i, j; int offset = tess.numVertexes - surf->first_vertex; GLimp_LogComment( "--- RB_SurfaceIQM ---\n" ); Tess_CheckOverflow( surf->num_vertexes, surf->num_triangles * 3 ); // compute bones for ( i = 0; i < model->num_joints; i++ ) { if ( backEnd.currentEntity->e.skeleton.type == SK_ABSOLUTE ) { refBone_t *bone = &backEnd.currentEntity->e.skeleton.bones[ i ]; TransInverse( &model->joints[ i ], &bones[ i ] ); TransCombine( &bones[ i ], &bone->t, &bones[ i ] ); } else { TransInit( &bones[ i ] ); } TransAddScale( backEnd.currentEntity->e.skeleton.scale, &bones[ i ] ); TransInsScale( model->internalScale, &bones[ i ] ); } if( surf->vbo && surf->ibo ) { if( model->num_joints > 0 ) { Com_Memcpy( tess.bones, bones, model->num_joints * sizeof(transform_t) ); tess.numBones = model->num_joints; } else { TransInitScale( model->internalScale * backEnd.currentEntity->e.skeleton.scale, &tess.bones[ 0 ] ); tess.numBones = 1; } R_BindVBO( surf->vbo ); R_BindIBO( surf->ibo ); tess.vboVertexSkinning = true; tess.multiDrawIndexes[ tess.multiDrawPrimitives ] = ((glIndex_t *)nullptr) + surf->first_triangle * 3; tess.multiDrawCounts[ tess.multiDrawPrimitives ] = surf->num_triangles * 3; tess.multiDrawPrimitives++; Tess_End(); return; } for ( i = 0; i < surf->num_triangles; i++ ) { tess.indexes[ tess.numIndexes + i * 3 + 0 ] = offset + model->triangles[ 3 * ( surf->first_triangle + i ) + 0 ]; tess.indexes[ tess.numIndexes + i * 3 + 1 ] = offset + model->triangles[ 3 * ( surf->first_triangle + i ) + 1 ]; tess.indexes[ tess.numIndexes + i * 3 + 2 ] = offset + model->triangles[ 3 * ( surf->first_triangle + i ) + 2 ]; } tess.attribsSet |= ATTR_POSITION | ATTR_TEXCOORD | ATTR_QTANGENT; if( model->num_joints > 0 && model->blendWeights && model->blendIndexes ) { // deform the vertices by the lerped bones for ( i = 0; i < surf->num_vertexes; i++ ) { int idxIn = surf->first_vertex + i; int idxOut = tess.numVertexes + i; const float weightFactor = 1.0f / 255.0f; vec3_t tangent, binormal, normal, tmp; if( model->blendWeights[ 4 * idxIn + 0 ] == 0 && model->blendWeights[ 4 * idxIn + 1 ] == 0 && model->blendWeights[ 4 * idxIn + 2 ] == 0 && model->blendWeights[ 4 * idxIn + 3 ] == 0 ) model->blendWeights[ 4 * idxIn + 0 ] = 255; VectorClear( tess.verts[ idxOut ].xyz ); VectorClear( normal ); VectorClear( tangent ); VectorClear( binormal ); for ( j = 0; j < 4; j++ ) { int bone = model->blendIndexes[ 4 * idxIn + j ]; float weight = weightFactor * model->blendWeights[ 4 * idxIn + j ]; TransformPoint( &bones[ bone ], &model->positions[ 3 * idxIn ], tmp ); VectorMA( tess.verts[ idxOut ].xyz, weight, tmp, tess.verts[ idxOut ].xyz ); TransformNormalVector( &bones[ bone ], &model->normals[ 3 * idxIn ], tmp ); VectorMA( normal, weight, tmp, normal ); TransformNormalVector( &bones[ bone ], &model->tangents[ 3 * idxIn ], tmp ); VectorMA( tangent, weight, tmp, tangent ); TransformNormalVector( &bones[ bone ], &model->bitangents[ 3 * idxIn ], tmp ); VectorMA( binormal, weight, tmp, binormal ); } VectorNormalize( normal ); VectorNormalize( tangent ); VectorNormalize( binormal ); R_TBNtoQtangents( tangent, binormal, normal, tess.verts[ idxOut ].qtangents ); tess.verts[ idxOut ].texCoords[ 0 ] = model->texcoords[ 2 * idxIn + 0 ]; tess.verts[ idxOut ].texCoords[ 1 ] = model->texcoords[ 2 * idxIn + 1 ]; } } else { for ( i = 0; i < surf->num_vertexes; i++ ) { int idxIn = surf->first_vertex + i; int idxOut = tess.numVertexes + i; float scale = model->internalScale * backEnd.currentEntity->e.skeleton.scale; VectorScale( &model->positions[ 3 * idxIn ], scale, tess.verts[ idxOut ].xyz ); R_TBNtoQtangents( &model->tangents[ 3 * idxIn ], &model->bitangents[ 3 * idxIn ], &model->normals[ 3 * idxIn ], tess.verts[ idxOut ].qtangents ); tess.verts[ idxOut ].texCoords[ 0 ] = model->texcoords[ 2 * idxIn + 0 ]; tess.verts[ idxOut ].texCoords[ 1 ] = model->texcoords[ 2 * idxIn + 1 ]; } } tess.numIndexes += 3 * surf->num_triangles; tess.numVertexes += surf->num_vertexes; }
/* ============ R_ShutdownVBOs ============ */ void R_ShutdownVBOs() { int i; VBO_t *vbo; IBO_t *ibo; ri.Printf( PRINT_DEVELOPER, "------- R_ShutdownVBOs -------\n" ); if( !glConfig2.mapBufferRangeAvailable ) { // nothing } #if defined( GLEW_ARB_buffer_storage ) && defined( GLEW_ARB_sync ) else if( glConfig2.bufferStorageAvailable && glConfig2.syncAvailable ) { R_BindVBO( tess.vbo ); R_ShutdownRingbuffer( GL_ARRAY_BUFFER, &tess.vertexRB ); R_BindIBO( tess.ibo ); R_ShutdownRingbuffer( GL_ELEMENT_ARRAY_BUFFER, &tess.indexRB ); } #endif else { if( tess.verts != nullptr && tess.verts != tess.vertsBuffer ) { R_BindVBO( tess.vbo ); glUnmapBuffer( GL_ARRAY_BUFFER ); } if( tess.indexes != nullptr && tess.indexes != tess.indexesBuffer ) { R_BindIBO( tess.ibo ); glUnmapBuffer( GL_ELEMENT_ARRAY_BUFFER ); } } R_BindNullVBO(); R_BindNullIBO(); glDeleteBuffers( 1, &tr.colorGradePBO ); for ( i = 0; i < tr.vbos.currentElements; i++ ) { vbo = ( VBO_t * ) Com_GrowListElement( &tr.vbos, i ); if ( vbo->vertexesVBO ) { glDeleteBuffers( 1, &vbo->vertexesVBO ); } } for ( i = 0; i < tr.ibos.currentElements; i++ ) { ibo = ( IBO_t * ) Com_GrowListElement( &tr.ibos, i ); if ( ibo->indexesVBO ) { glDeleteBuffers( 1, &ibo->indexesVBO ); } } Com_DestroyGrowList( &tr.vbos ); Com_DestroyGrowList( &tr.ibos ); Com_Free_Aligned( tess.vertsBuffer ); Com_Free_Aligned( tess.indexesBuffer ); tess.verts = tess.vertsBuffer = nullptr; tess.indexes = tess.indexesBuffer = nullptr; }
/* ============== Tess_UpdateVBOs Tr3B: update the default VBO to replace the client side vertex arrays ============== */ void Tess_UpdateVBOs() { GLimp_LogComment( "--- Tess_UpdateVBOs( ) ---\n" ); GL_CheckErrors(); // update the default VBO if ( tess.numVertexes > 0 && tess.numVertexes <= SHADER_MAX_VERTEXES ) { GLsizei size = tess.numVertexes * sizeof( shaderVertex_t ); GL_CheckErrors(); if ( r_logFile->integer ) { GLimp_LogComment( va( "glBufferSubData( vbo = '%s', numVertexes = %i )\n", tess.vbo->name, tess.numVertexes ) ); } if( !glConfig2.mapBufferRangeAvailable ) { R_BindVBO( tess.vbo ); glBufferSubData( GL_ARRAY_BUFFER, 0, size, tess.verts ); } else { R_BindVBO( tess.vbo ); if( glConfig2.bufferStorageAvailable && glConfig2.syncAvailable ) { GLsizei offset = tess.vertexBase * sizeof( shaderVertex_t ); glFlushMappedBufferRange( GL_ARRAY_BUFFER, offset, size ); } else { glFlushMappedBufferRange( GL_ARRAY_BUFFER, 0, size ); glUnmapBuffer( GL_ARRAY_BUFFER ); } tess.vertexBase = tess.vertsWritten; tess.vertsWritten += tess.numVertexes; tess.verts = nullptr; } } GL_CheckErrors(); // update the default IBO if ( tess.numIndexes > 0 && tess.numIndexes <= SHADER_MAX_INDEXES ) { GLsizei size = tess.numIndexes * sizeof( glIndex_t ); if( !glConfig2.mapBufferRangeAvailable ) { R_BindIBO( tess.ibo ); glBufferSubData( GL_ELEMENT_ARRAY_BUFFER, 0, size, tess.indexes ); } else { R_BindIBO( tess.ibo ); if( glConfig2.bufferStorageAvailable && glConfig2.syncAvailable ) { GLsizei offset = tess.indexBase * sizeof( glIndex_t ); glFlushMappedBufferRange( GL_ELEMENT_ARRAY_BUFFER, offset, size ); } else { glFlushMappedBufferRange( GL_ELEMENT_ARRAY_BUFFER, 0, size ); glUnmapBuffer( GL_ELEMENT_ARRAY_BUFFER ); } tess.indexBase = tess.indexesWritten; tess.indexesWritten += tess.numIndexes; tess.indexes = nullptr; } } GL_CheckErrors(); }
/* ============== Tess_MapVBOs Map the default VBOs ============== */ void Tess_MapVBOs( bool forceCPU ) { if( forceCPU || !glConfig2.mapBufferRangeAvailable ) { // use host buffers tess.verts = tess.vertsBuffer; tess.indexes = tess.indexesBuffer; return; } if( tess.verts == nullptr ) { R_BindVBO( tess.vbo ); #if defined( GLEW_ARB_buffer_storage ) && defined( GL_ARB_sync ) if( glConfig2.bufferStorageAvailable && glConfig2.syncAvailable ) { GLsizei segmentEnd = (tess.vertexRB.activeSegment + 1) * tess.vertexRB.segmentElements; if( tess.vertsWritten + SHADER_MAX_VERTEXES > (unsigned) segmentEnd ) { tess.vertsWritten = R_RotateRingbuffer( &tess.vertexRB ); } tess.verts = ( shaderVertex_t * )tess.vertexRB.baseAddr + tess.vertsWritten; } else #endif { if( vertexCapacity - tess.vertsWritten < SHADER_MAX_VERTEXES ) { // buffer is full, allocate a new one glBufferData( GL_ARRAY_BUFFER, vertexCapacity * sizeof( shaderVertex_t ), nullptr, GL_DYNAMIC_DRAW ); tess.vertsWritten = 0; } tess.verts = ( shaderVertex_t *) glMapBufferRange( GL_ARRAY_BUFFER, tess.vertsWritten * sizeof( shaderVertex_t ), SHADER_MAX_VERTEXES * sizeof( shaderVertex_t ), GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_RANGE_BIT | GL_MAP_UNSYNCHRONIZED_BIT | GL_MAP_FLUSH_EXPLICIT_BIT ); } } if( tess.indexes == nullptr ) { R_BindIBO( tess.ibo ); #if defined( GLEW_ARB_buffer_storage ) && defined( GL_ARB_sync ) if( glConfig2.bufferStorageAvailable && glConfig2.syncAvailable ) { GLsizei segmentEnd = (tess.indexRB.activeSegment + 1) * tess.indexRB.segmentElements; if( tess.indexesWritten + SHADER_MAX_INDEXES > (unsigned) segmentEnd ) { tess.indexesWritten = R_RotateRingbuffer( &tess.indexRB ); } tess.indexes = ( glIndex_t * )tess.indexRB.baseAddr + tess.indexesWritten; } else #endif { if( indexCapacity - tess.indexesWritten < SHADER_MAX_INDEXES ) { // buffer is full, allocate a new one glBufferData( GL_ELEMENT_ARRAY_BUFFER, indexCapacity * sizeof( glIndex_t ), nullptr, GL_DYNAMIC_DRAW ); tess.indexesWritten = 0; } tess.indexes = ( glIndex_t *) glMapBufferRange( GL_ELEMENT_ARRAY_BUFFER, tess.indexesWritten * sizeof( glIndex_t ), SHADER_MAX_INDEXES * sizeof( glIndex_t ), GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_RANGE_BIT | GL_MAP_UNSYNCHRONIZED_BIT | GL_MAP_FLUSH_EXPLICIT_BIT ); } } }