diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index b17c3185b..ae3059556 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -7233,7 +7233,40 @@ F32 Value 0.25 - + + Jpeg2000AdvancedCompression + + Comment + Use advanced Jpeg2000 compression options (precincts, blocks, ordering, markers) + Persist + 1 + Type + Boolean + Value + 0 + + Jpeg2000PrecinctsSize + + Comment + Size of image precincts. Assumed square and same for all levels. Must be power of 2. + Persist + 1 + Type + S32 + Value + 256 + + Jpeg2000BlocksSize + + Comment + Size of encoding blocks. Assumed square and same for all levels. Must be power of 2. Max 64, Min 4. + Persist + 1 + Type + S32 + Value + 64 + KeepAspectForSnapshot Comment @@ -11207,28 +11240,51 @@ Value 1.0 - MeshStreamingCostScaler + MeshTriangleBudget Comment - DEBUG - Persist - 1 - Type - F32 - Value - 2.0 - - MeshThreadCount - - Comment - Number of threads to use for loading meshes. + Target visible triangle budget to use when estimating streaming cost. Persist 1 Type U32 Value - 8 + 250000 + MeshMetaDataDiscount + + Comment + Number of bytes to deduct for metadata when determining streaming cost. + Persist + 1 + Type + U32 + Value + 384 + + MeshMinimumByteSize + + Comment + Minimum number of bytes per LoD block when determining streaming cost. + Persist + 1 + Type + U32 + Value + 16 + + MeshBytesPerTriangle + + Comment + Approximation of bytes per triangle to use for determining mesh streaming cost. + Persist + 1 + Type + U32 + Value + 16 + + MeshMaxConcurrentRequests Comment diff --git a/indra/newview/lldrawpooltree.cpp b/indra/newview/lldrawpooltree.cpp index 1aa5a5b01..6dac77809 100644 --- a/indra/newview/lldrawpooltree.cpp +++ b/indra/newview/lldrawpooltree.cpp @@ -171,7 +171,7 @@ void LLDrawPoolTree::endDeferredPass(S32 pass) void LLDrawPoolTree::beginShadowPass(S32 pass) { LLFastTimer t(LLFastTimer::FTM_SHADOW_TREE); - gGL.setAlphaRejectSettings(LLRender::CF_GREATER, 0.5f); + static const LLCachedControl render_deferred_offset("RenderDeferredTreeShadowOffset",1.f); static const LLCachedControl render_deferred_bias("RenderDeferredTreeShadowBias",1.f); glPolygonOffset(render_deferred_offset,render_deferred_bias); @@ -187,12 +187,10 @@ void LLDrawPoolTree::renderShadow(S32 pass) void LLDrawPoolTree::endShadowPass(S32 pass) { LLFastTimer t(LLFastTimer::FTM_SHADOW_TREE); - gGL.setAlphaRejectSettings(LLRender::CF_DEFAULT); - static const LLCachedControl render_deferred_offset("RenderDeferredTreeShadowOffset",1.f); - static const LLCachedControl render_deferred_bias("RenderDeferredTreeShadowBias",1.f); - glPolygonOffset(render_deferred_offset,render_deferred_bias); - //gDeferredShadowProgram.unbind(); + static const LLCachedControl render_deferred_offset("RenderDeferredSpotShadowOffset",1.f); + static const LLCachedControl render_deferred_bias("RenderDeferredSpotShadowBias",1.f); + glPolygonOffset(render_deferred_offset,render_deferred_bias); } void LLDrawPoolTree::renderTree(BOOL selecting) diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index c55ccce10..4139fdc0b 100644 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -1717,6 +1717,7 @@ void LLMeshRepository::cacheOutgoingMesh(LLMeshUploadData& data, LLSD& header) if (data.mModel[i].notNull()) { LLPointer volume = new LLVolume(volume_params, LLVolumeLODGroup::getVolumeScaleFromDetail(i)); + volume->copyVolumeFaces(data.mModel[i]); volume->setMeshAssetLoaded(TRUE); } } @@ -2552,7 +2553,7 @@ S32 LLMeshRepository::getActualMeshLOD(const LLVolumeParams& mesh_params, S32 lo return mThread->getActualMeshLOD(mesh_params, lod); } -const LLMeshSkinInfo* LLMeshRepository::getSkinInfo(const LLUUID& mesh_id, LLVOVolume* requesting_obj) +const LLMeshSkinInfo* LLMeshRepository::getSkinInfo(const LLUUID& mesh_id, const LLVOVolume* requesting_obj) { if (mesh_id.notNull()) { @@ -2813,7 +2814,7 @@ void LLMeshRepository::uploadError(LLSD& args) } //static -F32 LLMeshRepository::getStreamingCost(LLSD& header, F32 radius, S32* bytes, S32* bytes_visible, S32 lod) +F32 LLMeshRepository::getStreamingCost(LLSD& header, F32 radius, S32* bytes, S32* bytes_visible, S32 lod, F32 *unscaled_value) { F32 max_distance = 512.f; @@ -2821,10 +2822,15 @@ F32 LLMeshRepository::getStreamingCost(LLSD& header, F32 radius, S32* bytes, S32 F32 dlow = llmin(radius/0.06f, max_distance); F32 dmid = llmin(radius/0.24f, max_distance); - F32 METADATA_DISCOUNT = (F32) gSavedSettings.getU32("MeshMetaDataDiscount"); //discount 128 bytes to cover the cost of LLSD tags and compression domain overhead - F32 MINIMUM_SIZE = (F32) gSavedSettings.getU32("MeshMinimumByteSize"); //make sure nothing is "free" + static const LLCachedControl mesh_meta_data_discount("MeshMetaDataDiscount"); + static const LLCachedControl mesh_minimum_byte_size("MeshMinimumByteSize"); + static const LLCachedControl mesh_bytes_per_triangle("MeshBytesPerTriangle"); + static const LLCachedControl mesh_triangle_budget("MeshTriangleBudget"); - F32 bytes_per_triangle = (F32) gSavedSettings.getU32("MeshBytesPerTriangle"); + F32 METADATA_DISCOUNT = (F32) mesh_meta_data_discount.get(); //discount 128 bytes to cover the cost of LLSD tags and compression domain overhead + F32 MINIMUM_SIZE = (F32) mesh_minimum_byte_size.get(); //make sure nothing is "free" + + F32 bytes_per_triangle = (F32) mesh_bytes_per_triangle.get(); S32 bytes_lowest = header["lowest_lod"]["size"].asInteger(); S32 bytes_low = header["low_lod"]["size"].asInteger(); @@ -2902,7 +2908,12 @@ F32 LLMeshRepository::getStreamingCost(LLSD& header, F32 radius, S32* bytes, S32 triangles_low*low_area + triangles_lowest*lowest_area; - return weighted_avg/gSavedSettings.getU32("MeshTriangleBudget")*15000.f; + if (unscaled_value) + { + *unscaled_value = weighted_avg; + } + + return weighted_avg/mesh_triangle_budget*15000.f; } @@ -3204,32 +3215,33 @@ void LLPhysicsDecomp::doDecompositionSingleHull() llwarns << "Could not execute decomposition stage when attempting to create single hull." << llendl; make_box(mCurRequest); } - - mMutex->lock(); - mCurRequest->mHull.clear(); - mCurRequest->mHull.resize(1); - mCurRequest->mHullMesh.clear(); - mMutex->unlock(); - - std::vector p; - LLCDHull hull; - - // if LLConvexDecomposition is a stub, num_hulls should have been set to 0 above, and we should not reach this code - decomp->getSingleHull(&hull); - - const F32* v = hull.mVertexBase; - - for (S32 j = 0; j < hull.mNumVertices; ++j) + else { - LLVector3 vert(v[0], v[1], v[2]); - p.push_back(vert); - v = (F32*) (((U8*) v) + hull.mVertexStrideBytes); - } + mMutex->lock(); + mCurRequest->mHull.clear(); + mCurRequest->mHull.resize(1); + mCurRequest->mHullMesh.clear(); + mMutex->unlock(); + + std::vector p; + LLCDHull hull; + + // if LLConvexDecomposition is a stub, num_hulls should have been set to 0 above, and we should not reach this code + decomp->getSingleHull(&hull); + + const F32* v = hull.mVertexBase; + + for (S32 j = 0; j < hull.mNumVertices; ++j) + { + LLVector3 vert(v[0], v[1], v[2]); + p.push_back(vert); + v = (F32*) (((U8*) v) + hull.mVertexStrideBytes); + } - mMutex->lock(); - mCurRequest->mHull[0] = p; - mMutex->unlock(); - + mMutex->lock(); + mCurRequest->mHull[0] = p; + mMutex->unlock(); + } #else setMeshData(mesh, false); diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h index 80511a1d8..e23a5981e 100644 --- a/indra/newview/llmeshrepository.h +++ b/indra/newview/llmeshrepository.h @@ -474,7 +474,7 @@ public: static U32 sCacheBytesWritten; static U32 sPeakKbps; - static F32 getStreamingCost(LLSD& header, F32 radius, S32* bytes = NULL, S32* visible_bytes = NULL, S32 detail = -1); + static F32 getStreamingCost(LLSD& header, F32 radius, S32* bytes = NULL, S32* visible_bytes = NULL, S32 detail = -1, F32 *unscaled_value = NULL); LLMeshRepository(); @@ -493,7 +493,7 @@ public: S32 getActualMeshLOD(const LLVolumeParams& mesh_params, S32 lod); static S32 getActualMeshLOD(LLSD& header, S32 lod); - const LLMeshSkinInfo* getSkinInfo(const LLUUID& mesh_id, LLVOVolume* requesting_obj); + const LLMeshSkinInfo* getSkinInfo(const LLUUID& mesh_id, const LLVOVolume* requesting_obj); LLModel::Decomposition* getDecomposition(const LLUUID& mesh_id); void fetchPhysicsShape(const LLUUID& mesh_id); bool hasPhysicsShape(const LLUUID& mesh_id); diff --git a/indra/newview/llselectmgr.cpp b/indra/newview/llselectmgr.cpp index ca7aad82b..e60243241 100644 --- a/indra/newview/llselectmgr.cpp +++ b/indra/newview/llselectmgr.cpp @@ -6536,31 +6536,77 @@ U32 LLObjectSelection::getSelectedObjectTriangleCount() return count; } -/*S32 LLObjectSelection::getSelectedObjectRenderCost() + +S32 LLObjectSelection::getSelectedObjectRenderCost() { S32 cost = 0; LLVOVolume::texture_cost_t textures; + typedef std::set uuid_list_t; + uuid_list_t computed_objects; + + typedef std::list > child_list_t; + typedef const child_list_t const_child_list_t; + + // add render cost of complete linksets first, to get accurate texture counts for (list_t::iterator iter = mList.begin(); iter != mList.end(); ++iter) { LLSelectNode* node = *iter; + LLVOVolume* object = (LLVOVolume*)node->getObject(); - if (object) + if (object && object->isRootEdit()) { - cost += object->getRenderCost(textures); - } + cost += object->getRenderCost(textures); + computed_objects.insert(object->getID()); - for (LLVOVolume::texture_cost_t::iterator iter = textures.begin(); iter != textures.end(); ++iter) - { - // add the cost of each individual texture in the linkset - cost += iter->second; + const_child_list_t children = object->getChildren(); + for (const_child_list_t::const_iterator child_iter = children.begin(); + child_iter != children.end(); + ++child_iter) + { + LLViewerObject* child_obj = *child_iter; + LLVOVolume *child = dynamic_cast( child_obj ); + if (child) + { + cost += child->getRenderCost(textures); + computed_objects.insert(child->getID()); + } + } + + for (LLVOVolume::texture_cost_t::iterator iter = textures.begin(); iter != textures.end(); ++iter) + { + // add the cost of each individual texture in the linkset + cost += iter->second; + } + + textures.clear(); } - textures.clear(); } + + // add any partial linkset objects, texture cost may be slightly misleading + for (list_t::iterator iter = mList.begin(); iter != mList.end(); ++iter) + { + LLSelectNode* node = *iter; + LLVOVolume* object = (LLVOVolume*)node->getObject(); + if (object && computed_objects.find(object->getID()) == computed_objects.end() ) + { + cost += object->getRenderCost(textures); + computed_objects.insert(object->getID()); + } + + for (LLVOVolume::texture_cost_t::iterator iter = textures.begin(); iter != textures.end(); ++iter) + { + // add the cost of each individual texture in the linkset + cost += iter->second; + } + + textures.clear(); + } return cost; -}*/ +} + #endif //MESH_ENABLED diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp index 9b54b6c2a..71a0e6a58 100644 --- a/indra/newview/llviewerobject.cpp +++ b/indra/newview/llviewerobject.cpp @@ -3167,12 +3167,12 @@ F32 LLViewerObject::getLinksetPhysicsCost() return mLinksetPhysicsCost; } -F32 LLViewerObject::getStreamingCost(S32* bytes, S32* visible_bytes) +F32 LLViewerObject::getStreamingCost(S32* bytes, S32* visible_bytes, F32* unscaled_value) const { return 0.f; } -U32 LLViewerObject::getTriangleCount() +U32 LLViewerObject::getTriangleCount() const { return 0; } diff --git a/indra/newview/llviewerobject.h b/indra/newview/llviewerobject.h index 4c981fa3e..fe31ec725 100644 --- a/indra/newview/llviewerobject.h +++ b/indra/newview/llviewerobject.h @@ -328,8 +328,8 @@ public: virtual void setScale(const LLVector3 &scale, BOOL damped = FALSE); #if MESH_ENABLED - virtual F32 getStreamingCost(S32* bytes = NULL, S32* visible_bytes = NULL); - virtual U32 getTriangleCount(); + virtual F32 getStreamingCost(S32* bytes = NULL, S32* visible_bytes = NULL, F32* unscaled_value = NULL) const; + virtual U32 getTriangleCount() const; virtual U32 getHighLODTriangleCount(); void setObjectCost(F32 cost); diff --git a/indra/newview/llviewertexturelist.cpp b/indra/newview/llviewertexturelist.cpp index a42c481b6..917246424 100644 --- a/indra/newview/llviewertexturelist.cpp +++ b/indra/newview/llviewertexturelist.cpp @@ -966,99 +966,54 @@ void LLViewerTextureList::decodeAllImages(F32 max_time) BOOL LLViewerTextureList::createUploadFile(const std::string& filename, const std::string& out_filename, const U8 codec) -{ - // First, load the image. +{ + // Load the image + LLPointer image = LLImageFormatted::createFromType(codec); + if (image.isNull()) + { + image->setLastError("Couldn't open the image to be uploaded."); + return FALSE; + } + if (!image->load(filename)) + { + image->setLastError("Couldn't load the image to be uploaded."); + return FALSE; + } + // Decompress or expand it in a raw image structure LLPointer raw_image = new LLImageRaw; - - switch (codec) + if (!image->decode(raw_image, 0.0f)) { - case IMG_CODEC_BMP: - { - LLPointer bmp_image = new LLImageBMP; - - if (!bmp_image->load(filename)) - { - return FALSE; - } - - if (!bmp_image->decode(raw_image, 0.0f)) - { - return FALSE; - } - } - break; - case IMG_CODEC_TGA: - { - LLPointer tga_image = new LLImageTGA; - - if (!tga_image->load(filename)) - { - return FALSE; - } - - if (!tga_image->decode(raw_image)) - { - return FALSE; - } - - if( (tga_image->getComponents() != 3) && - (tga_image->getComponents() != 4) ) - { - tga_image->setLastError( "Image files with less than 3 or more than 4 components are not supported." ); - return FALSE; - } - } - break; - case IMG_CODEC_JPEG: - { - LLPointer jpeg_image = new LLImageJPEG; - - if (!jpeg_image->load(filename)) - { - return FALSE; - } - - if (!jpeg_image->decode(raw_image, 0.0f)) - { - return FALSE; - } - } - break; - case IMG_CODEC_PNG: - { - LLPointer png_image = new LLImagePNG; - - if (!png_image->load(filename)) - { - return FALSE; - } - - if (!png_image->decode(raw_image, 0.0f)) - { - return FALSE; - } - } - break; - default: - return FALSE; - } - - LLPointer compressedImage = convertToUploadFile(raw_image); - - if( !compressedImage->save(out_filename) ) - { - llinfos << "Couldn't create output file " << out_filename << llendl; + image->setLastError("Couldn't decode the image to be uploaded."); return FALSE; } - - // test to see if the encode and save worked. + // Check the image constraints + if ((image->getComponents() != 3) && (image->getComponents() != 4)) + { + image->setLastError("Image files with less than 3 or more than 4 components are not supported."); + return FALSE; + } + // Convert to j2c (JPEG2000) and save the file locally + LLPointer compressedImage = convertToUploadFile(raw_image); + if (compressedImage.isNull()) + { + image->setLastError("Couldn't convert the image to jpeg2000."); + llinfos << "Couldn't convert to j2c, file : " << filename << llendl; + return FALSE; + } + if (!compressedImage->save(out_filename)) + { + image->setLastError("Couldn't create the jpeg2000 image for upload."); + llinfos << "Couldn't create output file : " << out_filename << llendl; + return FALSE; + } + // Test to see if the encode and save worked LLPointer integrity_test = new LLImageJ2C; - if( !integrity_test->loadAndValidate( out_filename ) ) + if (!integrity_test->loadAndValidate( out_filename )) { - llinfos << "Image: " << out_filename << " is corrupt." << llendl; + image->setLastError("The created jpeg2000 image is corrupt."); + llinfos << "Image file : " << out_filename << " is corrupt" << llendl; return FALSE; } - return TRUE; } @@ -1073,7 +1028,25 @@ LLPointer LLViewerTextureList::convertToUploadFile(LLPointergetWidth() * raw_image->getHeight() <= LL_IMAGE_REZ_LOSSLESS_CUTOFF * LL_IMAGE_REZ_LOSSLESS_CUTOFF)) compressedImage->setReversible(TRUE); - compressedImage->encode(raw_image, 0.0f); + +/* if (gSavedSettings.getBOOL("Jpeg2000AdvancedCompression")) + { + // This test option will create jpeg2000 images with precincts for each level, RPCL ordering + // and PLT markers. The block size is also optionally modifiable. + // Note: the images hence created are compatible with older versions of the viewer. + // Read the blocks and precincts size settings + S32 block_size = gSavedSettings.getS32("Jpeg2000BlocksSize"); + S32 precinct_size = gSavedSettings.getS32("Jpeg2000PrecinctsSize"); + llinfos << "Advanced JPEG2000 Compression: precinct = " << precinct_size << ", block = " << block_size << llendl; + compressedImage->initEncode(*raw_image, block_size, precinct_size, 0); + }*/ + + if (!compressedImage->encode(raw_image, 0.0f)) + { + llinfos << "convertToUploadFile : encode returns with error!!" << llendl; + // Clear up the pointer so we don't leak that one + compressedImage = NULL; + } return compressedImage; } diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index 1cb7a7f58..dfaed4a33 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -311,7 +311,8 @@ public: U32 ypos = 64; const U32 y_inc = 20; - if (gSavedSettings.getBOOL("DebugShowTime")) + static const LLCachedControl debug_show_time("DebugShowTime"); + if (debug_show_time) { const U32 y_inc2 = 15; for (std::map::reverse_iterator iter = gDebugTimers.rbegin(); @@ -336,7 +337,8 @@ public: } #if LL_WINDOWS - if (gSavedSettings.getBOOL("DebugShowMemory")) + static const LLCachedControl debug_show_memory("DebugShowMemory"); + if (debug_show_memory) { addText(xpos, ypos, llformat("Memory: %d (KB)", LLMemory::getWorkingSetSize() / 1024)); ypos += y_inc; @@ -426,7 +428,8 @@ public: ypos += y_inc; }*/ - if (gSavedSettings.getBOOL("DebugShowRenderInfo")) + static const LLCachedControl debug_show_render_info("DebugShowRenderInfo"); + if (debug_show_render_info) { if (gPipeline.getUseVertexShaders() == 0) { @@ -456,6 +459,59 @@ public: addText(xpos, ypos, llformat("%.2f MB Video Memory Free", free_memory/1024.f)); ypos += y_inc; } + +#if MESH_ENABLED + //show streaming cost/triangle count of known prims in current region OR selection + //Note: This is SUPER slow + { + F32 cost = 0.f; + S32 count = 0; + S32 object_count = 0; + S32 total_bytes = 0; + S32 visible_bytes = 0; + + const char* label = "Region"; + if (LLSelectMgr::getInstance()->getSelection()->getObjectCount() == 0) + { //region + LLViewerRegion* region = gAgent.getRegion(); + if (region) + { + for (U32 i = 0; i < (U32)gObjectList.getNumObjects(); ++i) + { + LLViewerObject* object = gObjectList.getObject(i); + if (object && + object->getRegion() == region && + object->getVolume()) + { + object_count++; + S32 bytes = 0; + S32 visible = 0; + cost += object->getStreamingCost(&bytes, &visible); + count += object->getTriangleCount(); + total_bytes += bytes; + visible_bytes += visible; + } + } + } + } + else + { + label = "Selection"; + cost = LLSelectMgr::getInstance()->getSelection()->getSelectedObjectStreamingCost(&total_bytes, &visible_bytes); + count = LLSelectMgr::getInstance()->getSelection()->getSelectedObjectTriangleCount(); + object_count = LLSelectMgr::getInstance()->getSelection()->getObjectCount(); + } + + addText(xpos,ypos, llformat("%s streaming cost: %.1f", label, cost)); + ypos += y_inc; + + addText(xpos, ypos, llformat(" %.3f KTris, %.1f/%.1f KB, %d objects", + count/1000.f, visible_bytes/1024.f, total_bytes/1024.f, object_count)); + ypos += y_inc; + + } +#endif //MESH_ENABLED + addText(xpos, ypos, llformat("%d MB Vertex Data", LLVertexBuffer::sAllocatedBytes/(1024*1024))); ypos += y_inc; @@ -520,7 +576,7 @@ public: ypos += y_inc; #if MESH_ENABLED - if (gSavedSettings.getBOOL("MeshEnabled")) + if (gMeshRepo.meshRezEnabled()) { addText(xpos, ypos, llformat("%.3f MB Mesh Data Received", LLMeshRepository::sBytesReceived/(1024.f*1024.f))); @@ -541,7 +597,8 @@ public: LLVertexBuffer::sSetCount = LLImageGL::sUniqueCount = gPipeline.mNumVisibleNodes = LLPipeline::sVisibleLightCount = 0; } - if (gSavedSettings.getBOOL("DebugShowRenderMatrices")) + static const LLCachedControl debug_show_render_matrices("DebugShowRenderMatrices"); + if (debug_show_render_matrices) { addText(xpos, ypos, llformat("%.4f .%4f %.4f %.4f", gGLProjection[12], gGLProjection[13], gGLProjection[14], gGLProjection[15])); ypos += y_inc; @@ -574,7 +631,8 @@ public: addText(xpos, ypos, "View Matrix"); ypos += y_inc; } - if (gSavedSettings.getBOOL("DebugShowColor")) + static const LLCachedControl debug_show_color("DebugShowColor"); + if (debug_show_color) { U8 color[4]; LLCoordGL coord = gViewerWindow->getCurrentMouse(); diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp index 77cd91454..e03973d7a 100644 --- a/indra/newview/llvovolume.cpp +++ b/indra/newview/llvovolume.cpp @@ -93,6 +93,8 @@ F32 LLVOVolume::sLODFactor = 1.f; F32 LLVOVolume::sLODSlopDistanceFactor = 0.5f; //Changing this to zero, effectively disables the LOD transition slop F32 LLVOVolume::sDistanceFactor = 1.0f; S32 LLVOVolume::sNumLODChanges = 0; +S32 LLVOVolume::mRenderComplexity_last = 0; +S32 LLVOVolume::mRenderComplexity_current = 0; LLVOVolume::LLVOVolume(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp) : LLViewerObject(id, pcode, regionp), @@ -489,18 +491,24 @@ BOOL LLVOVolume::isVisible() const return FALSE ; } -void LLVOVolume::updateTextureVirtualSize() + +void LLVOVolume::updateTextureVirtualSize(bool forced) { // Update the pixel area of all faces - if(!isVisible() || mDrawable.isNull()) - { + if(mDrawable.isNull()) return; - } + if(!forced) + { + if(!isVisible()) + { + return; + } - if (!gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_SIMPLE)) - { - return; + if (!gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_SIMPLE)) + { + return; + } } static LLCachedControl dont_load_textures(gSavedSettings,"TextureDisable"); @@ -559,12 +567,12 @@ void LLVOVolume::updateTextureVirtualSize() // Animating textures also rez badly in Snowglobe because the // actual displayed area is only a fraction (corresponding to one // frame) of the animating texture. Let's fix that here: - if (mTextureAnimp && mTextureAnimp->mScaleS > 0.0f && mTextureAnimp->mScaleT > 0.0f) + /* if (mTextureAnimp && mTextureAnimp->mScaleS > 0.0f && mTextureAnimp->mScaleT > 0.0f) { // Adjust to take into account the actual frame size which is only a // portion of the animating texture vsize = vsize / mTextureAnimp->mScaleS / mTextureAnimp->mScaleT; - } + }*/ if ((vsize < MIN_TEX_ANIM_SIZE && old_size > MIN_TEX_ANIM_SIZE) || (vsize > MIN_TEX_ANIM_SIZE && old_size < MIN_TEX_ANIM_SIZE)) @@ -578,13 +586,17 @@ void LLVOVolume::updateTextureVirtualSize() if (vsize < min_vsize) min_vsize = vsize; if (vsize > max_vsize) max_vsize = vsize; } -// else if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_TEXTURE_PRIORITY)) -// { -// F32 pri = imagep->getDecodePriority(); -// pri = llmax(pri, 0.0f); -// if (pri < min_vsize) min_vsize = pri; -// if (pri > max_vsize) max_vsize = pri; -// } + else if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_TEXTURE_PRIORITY)) + { + LLViewerFetchedTexture* img = LLViewerTextureManager::staticCastToFetchedTexture(imagep) ; + if(img) + { + F32 pri = img->getDecodePriority(); + pri = llmax(pri, 0.0f); + if (pri < min_vsize) min_vsize = pri; + if (pri > max_vsize) max_vsize = pri; + } + } else if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_FACE_AREA)) { F32 pri = mPixelArea; @@ -657,10 +669,10 @@ void LLVOVolume::updateTextureVirtualSize() { setDebugText(llformat("%.0f:%.0f", (F32) sqrt(min_vsize),(F32) sqrt(max_vsize))); } -// else if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_TEXTURE_PRIORITY)) -// { -// setDebugText(llformat("%.0f:%.0f", (F32) sqrt(min_vsize),(F32) sqrt(max_vsize))); -// } + else if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_TEXTURE_PRIORITY)) + { + setDebugText(llformat("%.0f:%.0f", (F32) sqrt(min_vsize),(F32) sqrt(max_vsize))); + } else if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_FACE_AREA)) { setDebugText(llformat("%.0f:%.0f", (F32) sqrt(min_vsize),(F32) sqrt(max_vsize))); @@ -995,19 +1007,11 @@ BOOL LLVOVolume::calcLOD() F32 distance; #if MESH_ENABLED - if (mDrawable->isState(LLDrawable::RIGGED)) + if (mDrawable->isState(LLDrawable::RIGGED) && getAvatar()) { LLVOAvatar* avatar = getAvatar(); - if(avatar) - { - distance = avatar->mDrawable->mDistanceWRTCamera; - radius = avatar->getBinRadius(); - } - else - { - distance = mDrawable->mDistanceWRTCamera; - radius = getVolume()->mLODScaleBias.scaledVec(getScale()).length(); - } + distance = avatar->mDrawable->mDistanceWRTCamera; + radius = avatar->getBinRadius(); } else #endif //MESH_ENABLED @@ -2211,8 +2215,270 @@ const LLMatrix4 LLVOVolume::getRenderMatrix() const return mDrawable->getWorldMatrix(); } +// Returns a base cost and adds textures to passed in set. +// total cost is returned value + 5 * size of the resulting set. +// Cannot include cost of textures, as they may be re-used in linked +// children, and cost should only be increased for unique textures -Nyx +U32 LLVOVolume::getRenderCost(texture_cost_t &textures) const +{ + // Get access to params we'll need at various points. + // Skip if this is object doesn't have a volume (e.g. is an avatar). + BOOL has_volume = (getVolume() != NULL); + LLVolumeParams volume_params; + LLPathParams path_params; + LLProfileParams profile_params; + + U32 num_triangles = 0; + + // per-prim costs + static const U32 ARC_PARTICLE_COST = 1; // determined experimentally + static const U32 ARC_PARTICLE_MAX = 2048; // default values + static const U32 ARC_TEXTURE_COST = 16; // multiplier for texture resolution - performance tested + static const U32 ARC_LIGHT_COST = 500; // static cost for light-producing prims + static const U32 ARC_MEDIA_FACE_COST = 1500; // static cost per media-enabled face + + + // per-prim multipliers + static const F32 ARC_GLOW_MULT = 1.5f; // tested based on performance + static const F32 ARC_BUMP_MULT = 1.25f; // tested based on performance + static const F32 ARC_FLEXI_MULT = 5; // tested based on performance + static const F32 ARC_SHINY_MULT = 1.6f; // tested based on performance + static const F32 ARC_INVISI_COST = 1.2f; // tested based on performance + static const F32 ARC_WEIGHTED_MESH = 1.2f; // tested based on performance + + static const F32 ARC_PLANAR_COST = 1.0f; // tested based on performance to have negligible impact + static const F32 ARC_ANIM_TEX_COST = 4.f; // tested based on performance + static const F32 ARC_ALPHA_COST = 4.f; // 4x max - based on performance + + F32 shame = 0; + + U32 invisi = 0; + U32 shiny = 0; + U32 glow = 0; + U32 alpha = 0; + U32 flexi = 0; + U32 animtex = 0; + U32 particles = 0; + U32 bump = 0; + U32 planar = 0; + U32 weighted_mesh = 0; + U32 produces_light = 0; + U32 media_faces = 0; + + const LLDrawable* drawablep = mDrawable; + U32 num_faces = drawablep->getNumFaces(); + + if (has_volume) + { + volume_params = getVolume()->getParams(); + path_params = volume_params.getPathParams(); + profile_params = volume_params.getProfileParams(); + + F32 weighted_triangles = -1.0; + getStreamingCost(NULL, NULL, &weighted_triangles); + + if (weighted_triangles > 0.0) + { + num_triangles = (U32)(weighted_triangles); + } + } + + if (num_triangles == 0) + { + num_triangles = 4; + } + + if (isSculpted()) + { + if (isMesh()) + { + // base cost is dependent on mesh complexity + // note that 3 is the highest LOD as of the time of this coding. + S32 size = gMeshRepo.getMeshSize(volume_params.getSculptID(),3); + if ( size > 0) + { + if (gMeshRepo.getSkinInfo(volume_params.getSculptID(), this)) + { + // weighted attachment - 1 point for every 3 bytes + weighted_mesh = 1; + } + + } + else + { + // something went wrong - user should know their content isn't render-free + return 0; + } + } + else + { + const LLSculptParams *sculpt_params = (LLSculptParams *) getParameterEntry(LLNetworkData::PARAMS_SCULPT); + LLUUID sculpt_id = sculpt_params->getSculptTexture(); + if (textures.find(sculpt_id) == textures.end()) + { + LLViewerFetchedTexture *texture = LLViewerTextureManager::getFetchedTexture(sculpt_id); + if (texture) + { + S32 texture_cost = 256 + (S32)(ARC_TEXTURE_COST * (texture->getFullHeight() / 128.f + texture->getFullWidth() / 128.f)); + textures.insert(texture_cost_t::value_type(sculpt_id, texture_cost)); + } + } + } + } + + if (isFlexible()) + { + flexi = 1; + } + if (isParticleSource()) + { + particles = 1; + } + + if (getIsLight()) + { + produces_light = 1; + } + + for (U32 i = 0; i < num_faces; ++i) + { + const LLFace* face = drawablep->getFace(i); + const LLTextureEntry* te = face->getTextureEntry(); + const LLViewerTexture* img = face->getTexture(); + + if (img) + { + if (textures.find(img->getID()) == textures.end()) + { + S32 texture_cost = 256 + (S32)(ARC_TEXTURE_COST * (img->getFullHeight() / 128.f + img->getFullWidth() / 128.f)); + textures.insert(texture_cost_t::value_type(img->getID(), texture_cost)); + } + } + + if (face->getPoolType() == LLDrawPool::POOL_ALPHA) + { + alpha = 1; + } + else if (img && img->getPrimaryFormat() == GL_ALPHA) + { + invisi = 1; + } + /*if (face->hasMedia()) + { + media_faces++; + }*/ + + if (te) + { + if (te->getBumpmap()) + { + // bump is a multiplier, don't add per-face + bump = 1; + } + if (te->getShiny()) + { + // shiny is a multiplier, don't add per-face + shiny = 1; + } + if (te->getGlow() > 0.f) + { + // glow is a multiplier, don't add per-face + glow = 1; + } + if (face->mTextureMatrix != NULL) + { + animtex = 1; + } + if (te->getTexGen()) + { + planar = 1; + } + } + } + + // shame currently has the "base" cost of 1 point per 15 triangles, min 2. + shame = num_triangles * 5.f; + shame = shame < 2.f ? 2.f : shame; + + // multiply by per-face modifiers + if (planar) + { + shame *= planar * ARC_PLANAR_COST; + } + + if (animtex) + { + shame *= animtex * ARC_ANIM_TEX_COST; + } + + if (alpha) + { + shame *= alpha * ARC_ALPHA_COST; + } + + if(invisi) + { + shame *= invisi * ARC_INVISI_COST; + } + + if (glow) + { + shame *= glow * ARC_GLOW_MULT; + } + + if (bump) + { + shame *= bump * ARC_BUMP_MULT; + } + + if (shiny) + { + shame *= shiny * ARC_SHINY_MULT; + } + + + // multiply shame by multipliers + if (weighted_mesh) + { + shame *= weighted_mesh * ARC_WEIGHTED_MESH; + } + + if (flexi) + { + shame *= flexi * ARC_FLEXI_MULT; + } + + + // add additional costs + if (particles) + { + const LLPartSysData *part_sys_data = &(mPartSourcep->mPartSysData); + const LLPartData *part_data = &(part_sys_data->mPartData); + U32 num_particles = (U32)(part_sys_data->mBurstPartCount * llceil( part_data->mMaxAge / part_sys_data->mBurstRate)); + num_particles = num_particles > ARC_PARTICLE_MAX ? ARC_PARTICLE_MAX : num_particles; + F32 part_size = (llmax(part_data->mStartScale[0], part_data->mEndScale[0]) + llmax(part_data->mStartScale[1], part_data->mEndScale[1])) / 2.f; + shame += num_particles * part_size * ARC_PARTICLE_COST; + } + + if (produces_light) + { + shame += ARC_LIGHT_COST; + } + + if (media_faces) + { + shame += media_faces * ARC_MEDIA_FACE_COST; + } + + if (shame > mRenderComplexity_current) + { + mRenderComplexity_current = (S32)shame; + } + + return (U32)shame; +} #if MESH_ENABLED -F32 LLVOVolume::getStreamingCost(S32* bytes, S32* visible_bytes) +F32 LLVOVolume::getStreamingCost(S32* bytes, S32* visible_bytes, F32* unscaled_value) const { F32 radius = getScale().length()*0.5f; @@ -2220,7 +2486,7 @@ F32 LLVOVolume::getStreamingCost(S32* bytes, S32* visible_bytes) { LLSD& header = gMeshRepo.getMeshHeader(getVolume()->getParams().getSculptID()); - return LLMeshRepository::getStreamingCost(header, radius, bytes, visible_bytes, mLOD); + return LLMeshRepository::getStreamingCost(header, radius, bytes, visible_bytes, mLOD, unscaled_value); } else { @@ -2234,12 +2500,18 @@ F32 LLVOVolume::getStreamingCost(S32* bytes, S32* visible_bytes) header["medium_lod"]["size"] = counts[2] * 10; header["high_lod"]["size"] = counts[3] * 10; - return LLMeshRepository::getStreamingCost(header, radius); + return LLMeshRepository::getStreamingCost(header, radius, NULL, NULL, -1, unscaled_value); } } #endif //MESH_ENABLED +//static +void LLVOVolume::updateRenderComplexity() +{ + mRenderComplexity_last = mRenderComplexity_current; + mRenderComplexity_current = 0; +} -U32 LLVOVolume::getTriangleCount() +U32 LLVOVolume::getTriangleCount() const { U32 count = 0; LLVolume* volume = getVolume(); @@ -3137,7 +3409,7 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group) } #endif //MESH_ENABLED llassert_always(vobj); - vobj->updateTextureVirtualSize(); + vobj->updateTextureVirtualSize(true); vobj->preRebuild(); drawablep->clearState(LLDrawable::HAS_ALPHA); diff --git a/indra/newview/llvovolume.h b/indra/newview/llvovolume.h index 0e91880dc..ec97df042 100644 --- a/indra/newview/llvovolume.h +++ b/indra/newview/llvovolume.h @@ -90,6 +90,7 @@ public: // Class which embodies all Volume objects (with pcode LL_PCODE_VOLUME) class LLVOVolume : public LLViewerObject { + LOG_CLASS(LLVOVolume); protected: virtual ~LLVOVolume(); @@ -131,11 +132,12 @@ public: const LLMatrix4& getRelativeXform() const { return mRelativeXform; } const LLMatrix3& getRelativeXformInvTrans() const { return mRelativeXformInvTrans; } /*virtual*/ const LLMatrix4 getRenderMatrix() const; - + typedef std::map texture_cost_t; + U32 getRenderCost(texture_cost_t &textures) const; #if MESH_ENABLED - /*virtual*/ F32 getStreamingCost(S32* bytes = NULL, S32* visible_bytes = NULL); + /*virtual*/ F32 getStreamingCost(S32* bytes = NULL, S32* visible_bytes = NULL, F32* unscaled_value = NULL) const; #endif //MESH_ENABLED - /*virtual*/ U32 getTriangleCount(); + /*virtual*/ U32 getTriangleCount() const; /*virtual*/ U32 getHighLODTriangleCount(); /*virtual*/ BOOL lineSegmentIntersect(const LLVector3& start, const LLVector3& end, S32 face = -1, // which face to check, -1 = ALL_SIDES @@ -210,7 +212,7 @@ public: /*virtual*/ BOOL updateLOD(); void updateRadius(); /*virtual*/ void updateTextures(); - void updateTextureVirtualSize(); + void updateTextureVirtualSize(bool forced = false); void updateFaceFlags(); void regenFaces(); @@ -291,7 +293,13 @@ protected: LLFace* addFace(S32 face_index); void updateTEData(); + // stats tracking for render complexity + static S32 mRenderComplexity_last; + static S32 mRenderComplexity_current; public: + + static S32 getRenderComplexityMax() {return mRenderComplexity_last;} + static void updateRenderComplexity(); LLViewerTextureAnim *mTextureAnimp; U8 mTexAnimMode; private: