/* * R_UploadVBOElemData * * Upload elements into the buffer, properly offsetting them (batching) */ void R_UploadVBOElemData( mesh_vbo_t *vbo, int vertsOffset, int elemsOffset, const mesh_t *mesh, vbo_hint_t hint ) { int i; elem_t *ielems; assert( vbo != NULL ); if( !vbo->elemId ) return; if( hint == VBO_HINT_ELEMS_QUAD ) { R_UploadVBOElemQuadData( vbo, vertsOffset, elemsOffset, mesh->numVerts ); return; } if( hint == VBO_HINT_ELEMS_TRIFAN ) { R_UploadVBOElemTrifanData( vbo, vertsOffset, elemsOffset, mesh->numVerts ); return; } ielems = R_VBOElemBuffer( mesh->numElems ); for( i = 0; i < mesh->numElems; i++ ) { ielems[i] = vertsOffset + mesh->elems[i]; } RB_BindElementArrayBuffer( vbo->elemId ); qglBufferSubDataARB( GL_ELEMENT_ARRAY_BUFFER_ARB, elemsOffset * sizeof( elem_t ), mesh->numElems * sizeof( elem_t ), ielems ); }
/* * R_UploadVBOInstancesData */ vattribmask_t R_UploadVBOInstancesData( mesh_vbo_t *vbo, int instOffset, int numInstances, instancePoint_t *instances ) { vattribmask_t errMask = 0; assert( vbo != NULL ); if( !vbo->vertexId ) { return 0; } if( !instances ) { errMask |= VATTRIB_INSTANCES_BITS; } if( errMask ) { return errMask; } if( vbo->instancesOffset ) { qglBufferSubDataARB( GL_ARRAY_BUFFER_ARB, vbo->instancesOffset + instOffset * sizeof( instancePoint_t ), numInstances * sizeof( instancePoint_t ), instances ); } return 0; }
/* ======================== idJointBuffer::Update ======================== */ void idJointBuffer::Update( const float * joints, int numUpdateJoints ) const { assert( apiObject != NULL ); assert( IsMapped() == false ); assert_16_byte_aligned( joints ); assert( ( GetOffset() & 15 ) == 0 ); if ( numUpdateJoints > numJoints ) { idLib::FatalError( "idJointBuffer::Update: size overrun, %i > %i\n", numUpdateJoints, numJoints ); } const int numBytes = numUpdateJoints * 3 * 4 * sizeof( float ); qglBindBufferARB( GL_UNIFORM_BUFFER, reinterpret_cast< GLuint >( apiObject ) ); qglBufferSubDataARB( GL_UNIFORM_BUFFER, GetOffset(), (GLsizeiptrARB)numBytes, joints ); }
/* * R_UploadVBOElemTrifanData * * Builds and uploads indexes in trifan order, properly offsetting them for batching */ static int R_UploadVBOElemTrifanData( mesh_vbo_t *vbo, int vertsOffset, int elemsOffset, int numVerts ) { int numElems; elem_t *ielems; assert( vbo != NULL ); if( !vbo->elemId ) return 0; numElems = (numVerts - 2) * 3; ielems = R_VBOElemBuffer( numElems ); R_BuildTrifanElements( vertsOffset, numVerts, ielems ); RB_BindElementArrayBuffer( vbo->elemId ); qglBufferSubDataARB( GL_ELEMENT_ARRAY_BUFFER_ARB, elemsOffset * sizeof( elem_t ), numElems * sizeof( elem_t ), ielems ); return numElems; }
/* ======================== idVertexBuffer::Update ======================== */ void idVertexBuffer::Update( const void * data, int updateSize ) const { assert( apiObject != NULL ); assert( IsMapped() == false ); assert_16_byte_aligned( data ); assert( ( GetOffset() & 15 ) == 0 ); if ( updateSize > size ) { idLib::FatalError( "idVertexBuffer::Update: size overrun, %i > %i\n", updateSize, GetSize() ); } int numBytes = ( updateSize + 15 ) & ~15; GLuint bufferObject = reinterpret_cast< GLuint >( apiObject ); qglBindBufferARB( GL_ARRAY_BUFFER_ARB, bufferObject ); qglBufferSubDataARB( GL_ARRAY_BUFFER_ARB, GetOffset(), (GLsizeiptrARB)numBytes, data ); /* void * buffer = MapBuffer( BM_WRITE ); CopyBuffer( (byte *)buffer + GetOffset(), (byte *)data, numBytes ); UnmapBuffer(); */ }
/* ============== 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); } }
/* * R_UploadVBOVertexData * * Uploads required vertex data to the buffer. * * Vertex attributes masked by halfFloatVattribs will use half-precision floats * to save memory, if GL_ARB_half_float_vertex is available. Note that if * VATTRIB_POSITION_BIT is not set, it will also reset bits for other positional * attributes such as autosprite pos and instance pos. */ vattribmask_t R_UploadVBOVertexData( mesh_vbo_t *vbo, int vertsOffset, vattribmask_t vattribs, const mesh_t *mesh, vbo_hint_t hint ) { int i, j; unsigned numVerts; size_t vertSize; vattribmask_t errMask; vattribmask_t hfa; qbyte *data; assert( vbo != NULL ); assert( mesh != NULL ); if( !vbo || !vbo->vertexId ) { return 0; } errMask = 0; numVerts = mesh->numVerts; vertSize = vbo->vertexSize; hfa = vbo->halfFloatAttribs; data = R_VBOVertBuffer( numVerts, vertSize ); RB_BindArrayBuffer( vbo->vertexId ); // upload vertex xyz data if( vattribs & VATTRIB_POSITION_BIT ) { if( !mesh->xyzArray ) { errMask |= VATTRIB_POSITION_BIT; } else { R_FillVertexBuffer_float_or_half( FLOAT_VATTRIB_GL_TYPE( VATTRIB_POSITION_BIT, hfa ), mesh->xyzArray[0], 4, vertSize, numVerts, data + 0 ); } } // upload normals data if( vbo->normalsOffset && (vattribs & VATTRIB_NORMAL_BIT) ) { if( !mesh->normalsArray ) { errMask |= VATTRIB_NORMAL_BIT; } else { R_FillVertexBuffer_float_or_half( FLOAT_VATTRIB_GL_TYPE( VATTRIB_NORMAL_BIT, hfa ), mesh->normalsArray[0], 4, vertSize, numVerts, data + vbo->normalsOffset ); } } // upload tangent vectors if( vbo->sVectorsOffset && ( ( vattribs & (VATTRIB_SVECTOR_BIT|VATTRIB_AUTOSPRITE2_BIT) ) == VATTRIB_SVECTOR_BIT ) ) { if( !mesh->sVectorsArray ) { errMask |= VATTRIB_SVECTOR_BIT; } else { R_FillVertexBuffer_float_or_half( FLOAT_VATTRIB_GL_TYPE( VATTRIB_SVECTOR_BIT, hfa ), mesh->sVectorsArray[0], 4, vertSize, numVerts, data + vbo->sVectorsOffset ); } } // upload texture coordinates if( vbo->stOffset && (vattribs & VATTRIB_TEXCOORDS_BIT) ) { if( !mesh->stArray ) { errMask |= VATTRIB_TEXCOORDS_BIT; } else { R_FillVertexBuffer_float_or_half( FLOAT_VATTRIB_GL_TYPE( VATTRIB_TEXCOORDS_BIT, hfa ), mesh->stArray[0], 2, vertSize, numVerts, data + vbo->stOffset ); } } // upload lightmap texture coordinates if( vbo->lmstOffset[0] && ( vattribs & VATTRIB_LMCOORDS0_BIT ) ) { int i; vattribbit_t lmattrbit; lmattrbit = VATTRIB_LMCOORDS0_BIT; for( i = 0; i < MAX_LIGHTMAPS/2; i++ ) { if( !(vattribs & lmattrbit) ) { break; } if( !mesh->lmstArray[i*2+0] ) { errMask |= lmattrbit; break; } R_FillVertexBuffer_float_or_half( FLOAT_VATTRIB_GL_TYPE( VATTRIB_LMCOORDS0_BIT, hfa ), mesh->lmstArray[i*2+0][0], 2, vertSize, numVerts, data + vbo->lmstOffset[i] ); if( vattribs & (lmattrbit<<1) ) { if( !mesh->lmstArray[i*2+1] ) { errMask |= lmattrbit<<1; break; } R_FillVertexBuffer_float_or_half( FLOAT_VATTRIB_GL_TYPE( VATTRIB_LMCOORDS0_BIT, hfa ), mesh->lmstArray[i*2+1][0], 2, vertSize, numVerts, data + vbo->lmstOffset[i] + 2 * sizeof( float ) ); } lmattrbit <<= 2; } } // upload vertex colors (although indices > 0 are never used) if( vbo->colorsOffset[0] && (vattribs & VATTRIB_COLOR0_BIT) ) { if( !mesh->colorsArray[0] ) { errMask |= VATTRIB_COLOR0_BIT; } else { R_FillVertexBuffer( int, int, (int *)&mesh->colorsArray[0][0], 1, vertSize, numVerts, data + vbo->colorsOffset[0] ); } } // upload centre and radius for autosprites // this code assumes that the mesh has been properly pretransformed if( vbo->spritePointsOffset && ( (vattribs & VATTRIB_AUTOSPRITE2_BIT) == VATTRIB_AUTOSPRITE2_BIT ) ) { // for autosprite2 also upload vertices that form the longest axis // the remaining vertex can be trivially computed in vertex shader vec3_t vd[3]; float d[3]; int longest_edge = -1, longer_edge = -1, short_edge; float longest_dist = 0, longer_dist = 0; const int edges[3][2] = { { 1, 0 }, { 2, 0 }, { 2, 1 } }; vec4_t centre[4]; vec4_t axes[4]; vec4_t *verts = mesh->xyzArray; elem_t *elems, temp_elems[6]; int numQuads; size_t bufferOffset0 = vbo->spritePointsOffset; size_t bufferOffset1 = vbo->sVectorsOffset; if( hint == VBO_HINT_ELEMS_QUAD ) { numQuads = numVerts / 4; } else { assert( mesh->elems != NULL ); if( !mesh->elems ) { numQuads = 0; } else { numQuads = mesh->numElems / 6; } } for( i = 0, elems = mesh->elems; i < numQuads; i++, elems += 6 ) { if( hint == VBO_HINT_ELEMS_QUAD ) { elem_t firstV = i * 4; temp_elems[0] = firstV; temp_elems[1] = firstV + 2 - 1; temp_elems[2] = firstV + 2; temp_elems[3] = firstV; temp_elems[4] = firstV + 3 - 1; temp_elems[5] = firstV + 3; elems = temp_elems; } // find the longest edge, the long edge and the short edge longest_edge = longer_edge = -1; longest_dist = longer_dist = 0; for( j = 0; j < 3; j++ ) { float len; VectorSubtract( verts[elems[edges[j][0]]], verts[elems[edges[j][1]]], vd[j] ); len = VectorLength( vd[j] ); if( !len ) { len = 1; } d[j] = len; if( longest_edge == -1 || longest_dist < len ) { longer_dist = longest_dist; longer_edge = longest_edge; longest_dist = len; longest_edge = j; } else if( longer_dist < len ) { longer_dist = len; longer_edge = j; } } short_edge = 3 - (longest_edge + longer_edge); if( short_edge > 2 ) { continue; } // centre VectorAdd( verts[elems[edges[longest_edge][0]]], verts[elems[edges[longest_edge][1]]], centre[0] ); VectorScale( centre[0], 0.5, centre[0] ); // radius centre[0][3] = d[longest_edge] * 0.5; // unused // right axis, normalized VectorScale( vd[short_edge], 1.0 / d[short_edge], vd[short_edge] ); // up axis, normalized VectorScale( vd[longer_edge], 1.0 / d[longer_edge], vd[longer_edge] ); NormToLatLong( vd[short_edge], &axes[0][0] ); NormToLatLong( vd[longer_edge], &axes[0][2] ); for( j = 1; j < 4; j++ ) { Vector4Copy( centre[0], centre[j] ); Vector4Copy( axes[0], axes[j] ); } R_FillVertexBuffer_float_or_half( FLOAT_VATTRIB_GL_TYPE( VATTRIB_AUTOSPRITE_BIT, hfa ), centre[0], 4, vertSize, 4, data + bufferOffset0 ); R_FillVertexBuffer_float_or_half( FLOAT_VATTRIB_GL_TYPE( VATTRIB_SVECTOR_BIT, hfa ), axes[0], 4, vertSize, 4, data + bufferOffset1 ); bufferOffset0 += 4 * vertSize; bufferOffset1 += 4 * vertSize; } } else if( vbo->spritePointsOffset && ( (vattribs & VATTRIB_AUTOSPRITE_BIT) == VATTRIB_AUTOSPRITE_BIT ) ) { vec4_t *verts; vec4_t centre[4]; int numQuads = numVerts / 4; size_t bufferOffset = vbo->spritePointsOffset; for( i = 0, verts = mesh->xyzArray; i < numQuads; i++, verts += 4 ) { // centre for( j = 0; j < 3; j++ ) { centre[0][j] = (verts[0][j] + verts[1][j] + verts[2][j] + verts[3][j]) * 0.25; } // radius centre[0][3] = Distance( verts[0], centre[0] ) * 0.707106f; // 1.0f / sqrt(2) for( j = 1; j < 4; j++ ) { Vector4Copy( centre[0], centre[j] ); } R_FillVertexBuffer_float_or_half( FLOAT_VATTRIB_GL_TYPE( VATTRIB_AUTOSPRITE_BIT, hfa ), centre[0], 4, vertSize, 4, data + bufferOffset ); bufferOffset += 4 * vertSize; } } if( vattribs & VATTRIB_BONES_BITS ) { if( vbo->bonesIndicesOffset ) { if( !mesh->blendIndices ) { errMask |= VATTRIB_BONESINDICES_BIT; } else { R_FillVertexBuffer( int, int, (int *)&mesh->blendIndices[0], 1, vertSize, numVerts, data + vbo->bonesIndicesOffset ); } } if( vbo->bonesWeightsOffset ) { if( !mesh->blendWeights ) { errMask |= VATTRIB_BONESWEIGHTS_BIT; } else { R_FillVertexBuffer( int, int, (int *)&mesh->blendWeights[0], 1, vertSize, numVerts, data + vbo->bonesWeightsOffset ); } } } qglBufferSubDataARB( GL_ARRAY_BUFFER_ARB, vertsOffset * vertSize, numVerts * vertSize, data ); return errMask; }
/* =========== idVertexCache::AllocFrameTemp A frame temp allocation must never be allowed to fail due to overflow. We can't simply sync with the GPU and overwrite what we have, because there may still be future references to dynamically created surfaces. =========== */ vertCache_t *idVertexCache::AllocFrameTemp(void *data, int size) { vertCache_t *block; if (size <= 0) { common->Error("idVertexCache::AllocFrameTemp: size = %i\n", size); } if (dynamicAllocThisFrame + size > frameBytes) { // if we don't have enough room in the temp block, allocate a static block, // but immediately free it so it will get freed at the next frame tempOverflow = true; Alloc(data, size, &block); Free(block); return block; } // this data is just going on the shared dynamic list // if we don't have any remaining unused headers, allocate some more if (freeDynamicHeaders.next == &freeDynamicHeaders) { for (int i = 0; i < EXPAND_HEADERS; i++) { block = headerAllocator.Alloc(); block->next = freeDynamicHeaders.next; block->prev = &freeDynamicHeaders; block->next->prev = block; block->prev->next = block; } } // move it from the freeDynamicHeaders list to the dynamicHeaders list block = freeDynamicHeaders.next; block->next->prev = block->prev; block->prev->next = block->next; block->next = dynamicHeaders.next; block->prev = &dynamicHeaders; block->next->prev = block; block->prev->next = block; block->size = size; block->tag = TAG_TEMP; block->indexBuffer = false; block->offset = dynamicAllocThisFrame; dynamicAllocThisFrame += block->size; dynamicCountThisFrame++; block->user = NULL; block->frameUsed = 0; // copy the data block->virtMem = tempBuffers[listNum]->virtMem; block->vbo = tempBuffers[listNum]->vbo; if (block->vbo) { qglBindBufferARB(GL_ARRAY_BUFFER_ARB, block->vbo); qglBufferSubDataARB(GL_ARRAY_BUFFER_ARB, block->offset, (GLsizeiptrARB)size, data); } else { SIMDProcessor->Memcpy((byte *)block->virtMem + block->offset, data, size); } return block; }