void LLDrawPoolAvatar::getRiggedGeometry(LLFace* face, LLPointer<LLVertexBuffer>& buffer, U32 data_mask, const LLMeshSkinInfo* skin, LLVolume* volume, const LLVolumeFace& vol_face) { face->setGeomIndex(0); face->setIndicesIndex(0); //rigged faces do not batch textures face->setTextureIndex(255); if (buffer.isNull() || buffer->getTypeMask() != data_mask || !buffer->isWriteable()) { //make a new buffer if (sShaderLevel > 0) { buffer = new LLVertexBuffer(data_mask, GL_DYNAMIC_DRAW_ARB); } else { buffer = new LLVertexBuffer(data_mask, GL_STREAM_DRAW_ARB); } buffer->allocateBuffer(vol_face.mNumVertices, vol_face.mNumIndices, true); } else //resize existing buffer { buffer->resizeBuffer(vol_face.mNumVertices, vol_face.mNumIndices); } face->setSize(vol_face.mNumVertices, vol_face.mNumIndices); face->setVertexBuffer(buffer); U16 offset = 0; LLMatrix4 mat_vert = skin->mBindShapeMatrix; glh::matrix4f m((F32*) mat_vert.mMatrix); m = m.inverse().transpose(); F32 mat3[] = { m.m[0], m.m[1], m.m[2], m.m[4], m.m[5], m.m[6], m.m[8], m.m[9], m.m[10] }; LLMatrix3 mat_normal(mat3); //let getGeometryVolume know if alpha should override shiny U32 type = gPipeline.getPoolTypeFromTE(face->getTextureEntry(), face->getTexture()); if (type == LLDrawPool::POOL_ALPHA) { face->setPoolType(LLDrawPool::POOL_ALPHA); } else { face->setPoolType(LLDrawPool::POOL_AVATAR); } //llinfos << "Rebuilt face " << face->getTEOffset() << " of " << face->getDrawable() << " at " << gFrameTimeSeconds << llendl; face->getGeometryVolume(*volume, face->getTEOffset(), mat_vert, mat_normal, offset, true); buffer->flush(); }
// extract the GLOD data into vertex buffers void create_vertex_buffers_from_glod_object(S32 object, S32 group, std::vector<LLPointer <LLVertexBuffer> >& vertex_buffers) { vertex_buffers.clear(); GLint patch_count = 0; glodGetObjectParameteriv(object, GLOD_NUM_PATCHES, &patch_count); stop_gloderror(); GLint* sizes = new GLint[patch_count*2]; glodGetObjectParameteriv(object, GLOD_PATCH_SIZES, sizes); stop_gloderror(); GLint* names = new GLint[patch_count]; glodGetObjectParameteriv(object, GLOD_PATCH_NAMES, names); stop_gloderror(); for (S32 i = 0; i < patch_count; i++) { LLPointer<LLVertexBuffer> buff = new LLVertexBuffer(LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TEXCOORD0, 0); if (sizes[i*2+1] > 0 && sizes[i*2] > 0) { buff->allocateBuffer(sizes[i*2+1], sizes[i*2], true); buff->setBuffer(LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TEXCOORD0); glodFillElements(object, names[i], GL_UNSIGNED_SHORT, buff->getIndicesPointer()); stop_gloderror(); } else { // this face was eliminated, create a dummy triangle (one vertex, 3 indices, all 0) buff->allocateBuffer(1, 3, true); } vertex_buffers.push_back(buff); } glodDeleteObject(object); stop_gloderror(); glodDeleteGroup(group); stop_gloderror(); delete [] sizes; delete [] names; }
void LLDrawPoolAvatar::updateRiggedFaceVertexBuffer(LLVOAvatar* avatar, LLFace* face, const LLMeshSkinInfo* skin, LLVolume* volume, const LLVolumeFace& vol_face, LLVOVolume* vobj) { LLVector4a* weight = vol_face.mWeights; if (!weight) { return; } LLPointer<LLVertexBuffer> buffer = face->getVertexBuffer(); LLDrawable* drawable = face->getDrawable(); U32 data_mask = face->getRiggedVertexBufferDataMask(); if (buffer.isNull() || buffer->getTypeMask() != data_mask || buffer->getRequestedVerts() != vol_face.mNumVertices || buffer->getRequestedIndices() != vol_face.mNumIndices || (drawable && drawable->isState(LLDrawable::REBUILD_ALL))) { face->setGeomIndex(0); face->setIndicesIndex(0); if (buffer.isNull() || buffer->getTypeMask() != data_mask) { //make a new buffer if (sShaderLevel > 0) { buffer = new LLVertexBuffer(data_mask, GL_DYNAMIC_DRAW_ARB); } else { buffer = new LLVertexBuffer(data_mask, GL_STREAM_DRAW_ARB); } buffer->allocateBuffer(vol_face.mNumVertices, vol_face.mNumIndices, true); } else { //resize existing buffer buffer->resizeBuffer(vol_face.mNumVertices, vol_face.mNumIndices); } face->setSize(vol_face.mNumVertices, vol_face.mNumIndices); face->setVertexBuffer(buffer); U16 offset = 0; LLMatrix4 mat_vert = skin->mBindShapeMatrix; glh::matrix4f m((F32*) mat_vert.mMatrix); m = m.inverse().transpose(); F32 mat3[] = { m.m[0], m.m[1], m.m[2], m.m[4], m.m[5], m.m[6], m.m[8], m.m[9], m.m[10] }; LLMatrix3 mat_normal(mat3); static LLCachedControl<bool> mesh_enable_deformer(gSavedSettings, "MeshEnableDeformer"); if (mesh_enable_deformer) { LLDeformedVolume* deformed_volume = vobj->getDeformedVolume(); deformed_volume->deform(volume, avatar, skin, face->getTEOffset()); face->getGeometryVolume(*deformed_volume, face->getTEOffset(), mat_vert, mat_normal, offset, true); } else { face->getGeometryVolume(*volume, face->getTEOffset(), mat_vert, mat_normal, offset, true); } } if (sShaderLevel <= 0 && face->mLastSkinTime < avatar->getLastSkinTime()) { //perform software vertex skinning for this face LLStrider<LLVector3> position; LLStrider<LLVector3> normal; bool has_normal = buffer->hasDataType(LLVertexBuffer::TYPE_NORMAL); buffer->getVertexStrider(position); if (has_normal) { buffer->getNormalStrider(normal); } LLVector4a* pos = (LLVector4a*) position.get(); LLVector4a* norm = has_normal ? (LLVector4a*) normal.get() : NULL; //build matrix palette LLMatrix4a mp[64]; LLMatrix4* mat = (LLMatrix4*) mp; for (U32 j = 0; j < skin->mJointNames.size(); ++j) { LLJoint* joint = avatar->getJoint(skin->mJointNames[j]); if (joint) { mat[j] = skin->mInvBindMatrix[j]; mat[j] *= joint->getWorldMatrix(); } } LLMatrix4a bind_shape_matrix; bind_shape_matrix.loadu(skin->mBindShapeMatrix); for (U32 j = 0; j < buffer->getRequestedVerts(); ++j) { LLMatrix4a final_mat; final_mat.clear(); S32 idx[4]; LLVector4 wght; F32 scale = 0.f; for (U32 k = 0; k < 4; k++) { F32 w = weight[j][k]; idx[k] = llclamp((S32) floorf(w), 0, 63); wght[k] = w - floorf(w); scale += wght[k]; } wght *= 1.f/scale; for (U32 k = 0; k < 4; k++) { F32 w = wght[k]; LLMatrix4a src; src.setMul(mp[idx[k]], w); final_mat.add(src); } LLVector4a& v = vol_face.mPositions[j]; LLVector4a t; LLVector4a dst; bind_shape_matrix.affineTransform(v, t); final_mat.affineTransform(t, dst); pos[j] = dst; if (norm) { LLVector4a& n = vol_face.mNormals[j]; bind_shape_matrix.rotate(n, t); final_mat.rotate(t, dst); norm[j] = dst; } } } if (drawable && face->getTEOffset() == drawable->getNumFaces() - 1) { drawable->clearState(LLDrawable::REBUILD_ALL); } }
BOOL LLVOWLSky::updateGeometry(LLDrawable * drawable) { LLFastTimer ftm(FTM_GEO_SKY); LLStrider<LLVector3> vertices; LLStrider<LLVector2> texCoords; LLStrider<U16> indices; #if DOME_SLICES { mFanVerts = new LLVertexBuffer(LLDrawPoolWLSky::SKY_VERTEX_DATA_MASK, GL_STATIC_DRAW_ARB); mFanVerts->allocateBuffer(getFanNumVerts(), getFanNumIndices(), TRUE); BOOL success = mFanVerts->getVertexStrider(vertices) && mFanVerts->getTexCoord0Strider(texCoords) && mFanVerts->getIndexStrider(indices); if(!success) { llerrs << "Failed updating WindLight sky geometry." << llendl; } buildFanBuffer(vertices, texCoords, indices); mFanVerts->flush(); } { const U32 max_buffer_bytes = gSavedSettings.getS32("RenderMaxVBOSize")*1024; const U32 data_mask = LLDrawPoolWLSky::SKY_VERTEX_DATA_MASK; const U32 max_verts = max_buffer_bytes / LLVertexBuffer::calcVertexSize(data_mask); const U32 total_stacks = getNumStacks(); const U32 verts_per_stack = getNumSlices(); // each seg has to have one more row of verts than it has stacks // then round down const U32 stacks_per_seg = (max_verts - verts_per_stack) / verts_per_stack; // round up to a whole number of segments const U32 strips_segments = (total_stacks+stacks_per_seg-1) / stacks_per_seg; llinfos << "WL Skydome strips in " << strips_segments << " batches." << llendl; mStripsVerts.resize(strips_segments, NULL); LLTimer timer; timer.start(); for (U32 i = 0; i < strips_segments ;++i) { LLVertexBuffer * segment = new LLVertexBuffer(LLDrawPoolWLSky::SKY_VERTEX_DATA_MASK, GL_STATIC_DRAW_ARB); mStripsVerts[i] = segment; U32 num_stacks_this_seg = stacks_per_seg; if ((i == strips_segments - 1) && (total_stacks % stacks_per_seg) != 0) { // for the last buffer only allocate what we'll use num_stacks_this_seg = total_stacks % stacks_per_seg; } // figure out what range of the sky we're filling const U32 begin_stack = i * stacks_per_seg; const U32 end_stack = begin_stack + num_stacks_this_seg; llassert(end_stack <= total_stacks); const U32 num_verts_this_seg = verts_per_stack * (num_stacks_this_seg+1); llassert(num_verts_this_seg <= max_verts); const U32 num_indices_this_seg = 1+num_stacks_this_seg*(2+2*verts_per_stack); llassert(num_indices_this_seg * sizeof(U16) <= max_buffer_bytes); segment->allocateBuffer(num_verts_this_seg, num_indices_this_seg, TRUE); // lock the buffer BOOL success = segment->getVertexStrider(vertices) && segment->getTexCoord0Strider(texCoords) && segment->getIndexStrider(indices); if(!success) { llerrs << "Failed updating WindLight sky geometry." << llendl; } // fill it buildStripsBuffer(begin_stack, end_stack, vertices, texCoords, indices); // and unlock the buffer segment->flush(); } llinfos << "completed in " << llformat("%.2f", timer.getElapsedTimeF32()) << "seconds" << llendl; } #else mStripsVerts = new LLVertexBuffer(LLDrawPoolWLSky::SKY_VERTEX_DATA_MASK, GL_STATIC_DRAW_ARB); const F32 RADIUS = LLWLParamManager::sParamMgr->getDomeRadius(); LLPointer<LLVertexBuffer> temp = new LLVertexBuffer(LLVertexBuffer::MAP_VERTEX, 0); temp->allocateBuffer(12, 60, TRUE); BOOL success = temp->getVertexStrider(vertices) && temp->getIndexStrider(indices); if (success) { for (U32 i = 0; i < 12; i++) { *vertices++ = icosahedron_vert[i]; } for (U32 i = 0; i < 60; i++) { *indices++ = icosahedron_ind[i]; } } LLPointer<LLVertexBuffer> temp2; for (U32 i = 0; i < 8; i++) { temp2 = new LLVertexBuffer(LLVertexBuffer::MAP_VERTEX, 0); subdivide(*temp, temp2); temp = temp2; } temp->getVertexStrider(vertices); for (S32 i = 0; i < temp->getNumVerts(); i++) { LLVector3 v = vertices[i]; v.normVec(); vertices[i] = v*RADIUS; } temp2 = new LLVertexBuffer(LLVertexBuffer::MAP_VERTEX, 0); chop(*temp, temp2); mStripsVerts->allocateBuffer(temp2->getNumVerts(), temp2->getNumIndices(), TRUE); success = mStripsVerts->getVertexStrider(vertices) && mStripsVerts->getTexCoordStrider(texCoords) && mStripsVerts->getIndexStrider(indices); LLStrider<LLVector3> v; temp2->getVertexStrider(v); LLStrider<U16> ind; temp2->getIndexStrider(ind); if (success) { for (S32 i = 0; i < temp2->getNumVerts(); ++i) { LLVector3 vert = *v++; vert.normVec(); F32 z0 = vert.mV[2]; F32 x0 = vert.mV[0]; vert *= RADIUS; *vertices++ = vert; *texCoords++ = LLVector2((-z0 + 1.f) / 2.f, (-x0 + 1.f) / 2.f); } for (S32 i = 0; i < temp2->getNumIndices(); ++i) { *indices++ = *ind++; } } mStripsVerts->flush(); #endif updateStarColors(); updateStarGeometry(drawable); LLPipeline::sCompiles++; return TRUE; }
F32 gpu_benchmark() { if (!gGLManager.mHasShaderObjects || !gGLManager.mHasTimerQuery) { // don't bother benchmarking the fixed function // or venerable drivers which don't support accurate timing anyway // and are likely to be correctly identified by the GPU table already. return -1.f; } if (gBenchmarkProgram.mProgramObject == 0) { LLViewerShaderMgr::instance()->initAttribsAndUniforms(); gBenchmarkProgram.mName = "Benchmark Shader"; gBenchmarkProgram.mFeatures.attachNothing = true; gBenchmarkProgram.mShaderFiles.clear(); gBenchmarkProgram.mShaderFiles.push_back(std::make_pair("interface/benchmarkV.glsl", GL_VERTEX_SHADER_ARB)); gBenchmarkProgram.mShaderFiles.push_back(std::make_pair("interface/benchmarkF.glsl", GL_FRAGMENT_SHADER_ARB)); gBenchmarkProgram.mShaderLevel = 1; if (!gBenchmarkProgram.createShader(NULL, NULL)) { return -1.f; } } LLGLDisable blend(GL_BLEND); //measure memory bandwidth by: // - allocating a batch of textures and render targets // - rendering those textures to those render targets // - recording time taken // - taking the median time for a given number of samples //resolution of textures/render targets const U32 res = 1024; //number of textures const U32 count = 32; //number of samples to take const S32 samples = 64; if (gGLManager.mHasTimerQuery) { LLGLSLShader::initProfile(); } LLRenderTarget dest[count]; U32 source[count]; LLImageGL::generateTextures(count, source); std::vector<F32> results; //build a random texture U8* pixels = new U8[res*res*4]; for (U32 i = 0; i < res*res*4; ++i) { pixels[i] = (U8) ll_rand(255); } gGL.setColorMask(true, true); LLGLDepthTest depth(GL_FALSE); for (U32 i = 0; i < count; ++i) { //allocate render targets and textures dest[i].allocate(res,res,GL_RGBA,false, false, LLTexUnit::TT_TEXTURE, true); dest[i].bindTarget(); dest[i].clear(); dest[i].flush(); gGL.getTexUnit(0)->bindManual(LLTexUnit::TT_TEXTURE, source[i]); LLImageGL::setManualImage(GL_TEXTURE_2D, 0, GL_RGBA, res,res,GL_RGBA, GL_UNSIGNED_BYTE, pixels); } delete [] pixels; //make a dummy triangle to draw with LLPointer<LLVertexBuffer> buff = new LLVertexBuffer(LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_TEXCOORD0, GL_STATIC_DRAW_ARB); buff->allocateBuffer(3, 0, true); LLStrider<LLVector3> v; LLStrider<LLVector2> tc; buff->getVertexStrider(v); v[0].set(-1,1,0); v[1].set(-1,-3,0); v[2].set(3,1,0); buff->flush(); gBenchmarkProgram.bind(); bool busted_finish = false; buff->setBuffer(LLVertexBuffer::MAP_VERTEX); glFinish(); for (S32 c = -1; c < samples; ++c) { LLTimer timer; timer.start(); for (U32 i = 0; i < count; ++i) { dest[i].bindTarget(); gGL.getTexUnit(0)->bindManual(LLTexUnit::TT_TEXTURE, source[i]); buff->drawArrays(LLRender::TRIANGLES, 0, 3); dest[i].flush(); } //wait for current batch of copies to finish if (busted_finish) { //read a pixel off the last target since some drivers seem to ignore glFinish dest[count-1].bindTarget(); U32 pixel = 0; glReadPixels(0,0,1,1,GL_RGBA, GL_UNSIGNED_BYTE, &pixel); dest[count-1].flush(); } else { glFinish(); } F32 time = timer.getElapsedTimeF32(); if (c >= 0) // <-- ignore the first sample as it tends to be artificially slow { //store result in gigabytes per second F32 gb = (F32) ((F64) (res*res*8*count))/(1000000000); F32 gbps = gb/time; if (!gGLManager.mHasTimerQuery && !busted_finish && gbps > 128.f) { //unrealistically high bandwidth for a card without timer queries, glFinish is probably ignored busted_finish = true; LL_WARNS() << "GPU Benchmark detected GL driver with broken glFinish implementation." << LL_ENDL; } else { results.push_back(gbps); } } } gBenchmarkProgram.unbind(); if (gGLManager.mHasTimerQuery) { LLGLSLShader::finishProfile(false); } LLImageGL::deleteTextures(count, source); std::sort(results.begin(), results.end()); F32 gbps = results[results.size()/2]; LL_INFOS() << "Memory bandwidth is " << llformat("%.3f", gbps) << "GB/sec according to CPU timers" << LL_ENDL; #if LL_DARWIN if (gbps > 512.f) { LL_WARNS() << "Memory bandwidth is improbably high and likely incorrect; discarding result." << LL_ENDL; //OSX is probably lying, discard result gbps = -1.f; } #endif F32 ms = gBenchmarkProgram.mTimeElapsed/1000000.f; F32 seconds = ms/1000.f; F64 samples_drawn = res*res*count*samples; F32 samples_sec = (samples_drawn/1000000000.0)/seconds; gbps = samples_sec*8; LL_INFOS() << "Memory bandwidth is " << llformat("%.3f", gbps) << "GB/sec according to ARB_timer_query" << LL_ENDL; return gbps; }