diff --git a/doc/testplans/pbr_materials.md b/doc/testplans/pbr_materials.md new file mode 100644 index 0000000000..1ef7945b94 --- /dev/null +++ b/doc/testplans/pbr_materials.md @@ -0,0 +1,12 @@ +# PBR Materials + +## KHR Texture Transforms + +Texture repeats for PBR materials on prims are based on the [KHR\_texture\_transform](https://github.com/KhronosGroup/glTF/tree/main/extensions/2.0/Khronos/KHR_texture_transform) spec, and thus should be expected to behave according to the spec. We currently suport offset, rotation, and scale from the spec. texCoord is not currently supported. + +PBR materials should have approximately correct lighting based on the normal texture: + +- With default texture transforms, assuming the prim or model has correct normals and tangents +- With a texture transform applied, especially rotation or negative scale +- With a texture animation applied via `llSetTextureAnim`, especially a rotation animation + - Note: Texture animations are not guaranteed to loop when a PBR texture transform is applied diff --git a/doc/testplans/pbr_terrain_appearance.md b/doc/testplans/pbr_terrain_appearance.md index 770e39204e..eab5b8bf44 100644 --- a/doc/testplans/pbr_terrain_appearance.md +++ b/doc/testplans/pbr_terrain_appearance.md @@ -39,7 +39,7 @@ PBR terrain does not support materials with alpha blend or double-sided. In addi ## PBR Terrain Texture Transforms -Like PBR materials on prims, PBR terrain repeats are based on the [KHR\_texture\_transform](https://github.com/KhronosGroup/glTF/tree/main/extensions/2.0/Khronos/KHR_texture_transform) spec, and thus should be expected to behave the same way. +Like PBR materials on prims, PBR terrain repeats are based on the [KHR\_texture\_transform](https://github.com/KhronosGroup/glTF/tree/main/extensions/2.0/Khronos/KHR_texture_transform) spec, and thus should be expected to behave the same way. We currently suport offset, rotation, and scale from the spec. texCoord is not currently supported. The southwest corner of a region, at z=0, is the UV origin for all texture coordinates of the whole region. Unless an offset is also applied, scale and rotation of the terrain texture transforms are relative to that point. diff --git a/doc/testplans/pbr_terrain_composition.md b/doc/testplans/pbr_terrain_composition.md index 731da90aba..450e887390 100644 --- a/doc/testplans/pbr_terrain_composition.md +++ b/doc/testplans/pbr_terrain_composition.md @@ -87,6 +87,12 @@ If saving the terrain fails for any reason, the terrain should not be updated. Unlike a viewer without PBR terrain support, the new viewer will no longer treat textures with alpha channels as invalid. +### Saving PBR Terrain Texture Transforms + +If "PBR Metallic Roughness" checkbox is checked, a user with saving composition permissions should also be allowed to edit and save PBR texture transforms. + +One texture transform may be set for each material swatch. Setting texture transforms for each individual texture on the material is not currently supported. + ## Graphics Features Texture terrain with transparency is not permitted to be applied in the viewer. diff --git a/indra/llfilesystem/llfilesystem.cpp b/indra/llfilesystem/llfilesystem.cpp index 910b92bb8f..e505351430 100644 --- a/indra/llfilesystem/llfilesystem.cpp +++ b/indra/llfilesystem/llfilesystem.cpp @@ -79,7 +79,7 @@ LLFileSystem::~LLFileSystem() // static bool LLFileSystem::getExists(const LLUUID& file_id, const LLAssetType::EType file_type) { - LL_PROFILE_ZONE_COLOR(tracy::Color::Gold); // measure cache performance + LL_PROFILE_ZONE_SCOPED; std::string id_str; file_id.toString(id_str); const std::string extra_info = ""; diff --git a/indra/llmessage/llassetstorage.cpp b/indra/llmessage/llassetstorage.cpp index 70a7a34a70..2de59c1b6a 100644 --- a/indra/llmessage/llassetstorage.cpp +++ b/indra/llmessage/llassetstorage.cpp @@ -484,6 +484,8 @@ void LLAssetStorage::getAssetData(const LLUUID uuid, void *user_data, bool is_priority) { + LL_PROFILE_ZONE_SCOPED; + LL_DEBUGS("AssetStorage") << "LLAssetStorage::getAssetData() - " << uuid << "," << LLAssetType::lookup(type) << LL_ENDL; LL_DEBUGS("AssetStorage") << "ASSET_TRACE requesting " << uuid << " type " << LLAssetType::lookup(type) << LL_ENDL; @@ -529,6 +531,7 @@ void LLAssetStorage::getAssetData(const LLUUID uuid, if (size > 0) { + LL_PROFILE_ZONE_NAMED("gad - file in cache"); // we've already got the file // theoretically, partial files w/o a pending request shouldn't happen // unless there's a weird error @@ -548,7 +551,7 @@ void LLAssetStorage::getAssetData(const LLUUID uuid, } bool duplicate = false; - + LL_PROFILE_ZONE_NAMED("gad - check pending downloads"); // check to see if there's a pending download of this uuid already for (request_list_t::iterator iter = mPendingDownloads.begin(); iter != mPendingDownloads.end(); ++iter ) diff --git a/indra/llrender/llgl.h b/indra/llrender/llgl.h index f9231aa7b0..0e9e40474f 100644 --- a/indra/llrender/llgl.h +++ b/indra/llrender/llgl.h @@ -116,6 +116,9 @@ public: bool mIsNVIDIA; bool mIsIntel; + // hints to the render pipe + U32 mDownScaleMethod = 0; // see settings.xml RenderDownScaleMethod + #if LL_DARWIN // Needed to distinguish problem cards on older Macs that break with Materials bool mIsMobileGF; diff --git a/indra/llrender/llimagegl.cpp b/indra/llrender/llimagegl.cpp index bbe69da6a0..40e5fd71ce 100644 --- a/indra/llrender/llimagegl.cpp +++ b/indra/llrender/llimagegl.cpp @@ -57,6 +57,9 @@ const F32 MIN_TEXTURE_LIFETIME = 10.f; U32 wpo2(U32 i); +U32 LLImageGL::sFrameCount = 0; + + // texture memory accounting (for macOS) static LLMutex sTexMemMutex; static std::unordered_map sTextureAllocs; @@ -1003,7 +1006,7 @@ bool should_stagger_image_set(bool compressed) #else // glTexSubImage2D doesn't work with compressed textures on select tested Nvidia GPUs on Windows 10 -Cosmic,2023-03-08 // Setting media textures off-thread seems faster when not using sub_image_lines (Nvidia/Windows 10) -Cosmic,2023-03-31 - return !compressed && on_main_thread(); + return !compressed && on_main_thread() && !gGLManager.mIsIntel; #endif } @@ -1185,13 +1188,37 @@ void LLImageGL::generateTextures(S32 numTextures, U32 *textures) } } +// static +void LLImageGL::updateClass() +{ + sFrameCount++; +} + // static void LLImageGL::deleteTextures(S32 numTextures, const U32 *textures) { + // wait a few frames before actually deleting the textures to avoid + // synchronization issues with the GPU + static std::vector sFreeList[4]; + if (gGLManager.mInited) { - free_tex_images(numTextures, textures); - glDeleteTextures(numTextures, textures); + LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE; + U32 idx = sFrameCount % 4; + + for (S32 i = 0; i < numTextures; ++i) + { + sFreeList[idx].push_back(textures[i]); + } + + idx = (sFrameCount + 3) % 4; + + if (!sFreeList[idx].empty()) + { + glDeleteTextures((GLsizei) sFreeList[idx].size(), sFreeList[idx].data()); + free_tex_images((GLsizei) sFreeList[idx].size(), sFreeList[idx].data()); + sFreeList[idx].resize(0); + } } } @@ -2338,45 +2365,86 @@ bool LLImageGL::scaleDown(S32 desired_discard) S32 desired_width = getWidth(desired_discard); S32 desired_height = getHeight(desired_discard); - U64 size = getBytes(desired_discard); - llassert(size <= 2048*2048*4); // we shouldn't be using this method to downscale huge textures, but it'll work - gGL.getTexUnit(0)->bind(this); + if (gGLManager.mDownScaleMethod == 0) + { // use an FBO to downscale the texture + // allocate new texture + U32 temp_texname = 0; + generateTextures(1, &temp_texname); + gGL.getTexUnit(0)->bindManual(LLTexUnit::TT_TEXTURE, temp_texname, true); + { + LL_PROFILE_ZONE_NAMED_CATEGORY_TEXTURE("scaleDown - glTexImage2D"); + glTexImage2D(mTarget, 0, mFormatPrimary, desired_width, desired_height, 0, mFormatPrimary, mFormatType, NULL); + } + // account for new texture getting created + alloc_tex_image(desired_width, desired_height, mFormatPrimary); - if (sScratchPBO == 0) - { - glGenBuffers(1, &sScratchPBO); - sScratchPBOSize = 0; + // Use render-to-texture to scale down the texture + { + LL_PROFILE_ZONE_NAMED_CATEGORY_TEXTURE("scaleDown - glFramebufferTexture2D"); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, mTarget, temp_texname, 0); + } + + glViewport(0, 0, desired_width, desired_height); + + // draw a full screen triangle + gGL.getTexUnit(0)->bind(this); + glDrawArrays(GL_TRIANGLES, 0, 3); + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + + // delete old texture and assign new texture name + deleteTextures(1, &mTexName); + mTexName = temp_texname; + + if (mHasMipMaps) + { // generate mipmaps if needed + LL_PROFILE_ZONE_NAMED_CATEGORY_TEXTURE("scaleDown - glGenerateMipmap"); + gGL.getTexUnit(0)->bind(this); + glGenerateMipmap(mTarget); + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + } } + else + { // use a PBO to downscale the texture + U64 size = getBytes(desired_discard); + llassert(size <= 2048 * 2048 * 4); // we shouldn't be using this method to downscale huge textures, but it'll work + gGL.getTexUnit(0)->bind(this); - glBindBuffer(GL_PIXEL_PACK_BUFFER, sScratchPBO); + if (sScratchPBO == 0) + { + glGenBuffers(1, &sScratchPBO); + sScratchPBOSize = 0; + } - if (size > sScratchPBOSize) - { - glBufferData(GL_PIXEL_PACK_BUFFER, size, NULL, GL_STREAM_COPY); - sScratchPBOSize = size; + glBindBuffer(GL_PIXEL_PACK_BUFFER, sScratchPBO); + + if (size > sScratchPBOSize) + { + glBufferData(GL_PIXEL_PACK_BUFFER, size, NULL, GL_STREAM_COPY); + sScratchPBOSize = size; + } + + glGetTexImage(mTarget, mip, mFormatPrimary, mFormatType, nullptr); + + free_tex_image(mTexName); + + glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); + + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, sScratchPBO); + glTexImage2D(mTarget, 0, mFormatPrimary, desired_width, desired_height, 0, mFormatPrimary, mFormatType, nullptr); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + + alloc_tex_image(desired_width, desired_height, mFormatPrimary); + + if (mHasMipMaps) + { + LL_PROFILE_ZONE_NAMED_CATEGORY_TEXTURE("scaleDown - glGenerateMipmap"); + glGenerateMipmap(mTarget); + } + + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); } - glGetTexImage(mTarget, mip, mFormatPrimary, mFormatType, nullptr); - - free_tex_image(mTexName); - - glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); - - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, sScratchPBO); - glTexImage2D(mTarget, 0, mFormatPrimary, desired_width, desired_height, 0, mFormatPrimary, mFormatType, nullptr); - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); - - alloc_tex_image(desired_width, desired_height, mFormatPrimary); - - if (mHasMipMaps) - { - LL_PROFILE_ZONE_NAMED_CATEGORY_TEXTURE("scaleDown - glGenerateMipmap"); - glGenerateMipmap(mTarget); - } - - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - mCurrentDiscardLevel = desired_discard; return true; diff --git a/indra/llrender/llimagegl.h b/indra/llrender/llimagegl.h index 58b7d514e4..acfaa9fe07 100644 --- a/indra/llrender/llimagegl.h +++ b/indra/llrender/llimagegl.h @@ -62,6 +62,9 @@ class LLImageGL : public LLRefCount friend class LLTexUnit; public: + // call once per frame + static void updateClass(); + // Get an estimate of how many bytes have been allocated in vram for textures. // Does not include mipmaps. // NOTE: multiplying this number by two gives a good estimate for total @@ -279,7 +282,7 @@ protected: public: static std::unordered_set sImageList; static S32 sCount; - + static U32 sFrameCount; static F32 sLastFrameTime; // Global memory statistics diff --git a/indra/llrender/llrender.h b/indra/llrender/llrender.h index 26968ecb91..755f6c9fef 100644 --- a/indra/llrender/llrender.h +++ b/indra/llrender/llrender.h @@ -307,7 +307,11 @@ public: ALTERNATE_DIFFUSE_MAP = 1, NORMAL_MAP = 1, SPECULAR_MAP = 2, - NUM_TEXTURE_CHANNELS = 3, + BASECOLOR_MAP = 3, + METALLIC_ROUGHNESS_MAP = 4, + GLTF_NORMAL_MAP = 5, + EMISSIVE_MAP = 6, + NUM_TEXTURE_CHANNELS = 7, }; enum eVolumeTexIndex : U8 diff --git a/indra/llrender/llrendertarget.cpp b/indra/llrender/llrendertarget.cpp index b573618ee6..40d9a0dbee 100644 --- a/indra/llrender/llrendertarget.cpp +++ b/indra/llrender/llrendertarget.cpp @@ -426,14 +426,17 @@ void LLRenderTarget::bindTarget() GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2, GL_COLOR_ATTACHMENT3}; - glDrawBuffers(static_cast(mTex.size()), drawbuffers); if (mTex.empty()) { //no color buffer to draw to glDrawBuffer(GL_NONE); glReadBuffer(GL_NONE); } - + else + { + glDrawBuffers(static_cast(mTex.size()), drawbuffers); + glReadBuffer(GL_COLOR_ATTACHMENT0); + } check_framebuffer_status(); glViewport(0, 0, mResX, mResY); @@ -519,7 +522,8 @@ void LLRenderTarget::flush() llassert(sCurFBO == mFBO); llassert(sBoundTarget == this); - if (mGenerateMipMaps == LLTexUnit::TMG_AUTO) { + if (mGenerateMipMaps == LLTexUnit::TMG_AUTO) + { LL_PROFILE_GPU_ZONE("rt generate mipmaps"); bindTexture(0, 0, LLTexUnit::TFO_TRILINEAR); glGenerateMipmap(GL_TEXTURE_2D); @@ -540,6 +544,8 @@ void LLRenderTarget::flush() glViewport(gGLViewport[0], gGLViewport[1], gGLViewport[2], gGLViewport[3]); sCurResX = gGLViewport[2]; sCurResY = gGLViewport[3]; + glReadBuffer(GL_BACK); + glDrawBuffer(GL_BACK); } } diff --git a/indra/llrender/llrendertarget.h b/indra/llrender/llrendertarget.h index 340276a752..a1adf93fa1 100644 --- a/indra/llrender/llrendertarget.h +++ b/indra/llrender/llrendertarget.h @@ -172,6 +172,8 @@ public: // *HACK void swapFBORefs(LLRenderTarget& other); + static LLRenderTarget* sBoundTarget; + protected: U32 mResX; U32 mResY; @@ -186,8 +188,6 @@ protected: U32 mMipLevels; LLTexUnit::eTextureType mUsage; - - static LLRenderTarget* sBoundTarget; }; #endif diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 1233ecdabc..fca0f1adc4 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -11145,7 +11145,7 @@ Change of this parameter will affect the layout of buttons in notification toast Comment Minimum of available physical memory in MB before textures get scaled down Persist - 1 + 0 Type U32 Value @@ -11184,6 +11184,17 @@ Change of this parameter will affect the layout of buttons in notification toast Value 2048 + RenderDownScaleMethod + + Comment + Method to use to downscale images. 0 - FBO, 1 - PBO + Persist + 1 + Type + U32 + Value + 1 + RenderDebugTextureBind Comment diff --git a/indra/newview/featuretable.txt b/indra/newview/featuretable.txt index c0e68bb024..131f9cd44b 100644 --- a/indra/newview/featuretable.txt +++ b/indra/newview/featuretable.txt @@ -1,4 +1,4 @@ -version 60 +version 61 // The version number above should be incremented IF AND ONLY IF some // change has been made that is sufficiently important to justify // resetting the graphics preferences of all users to the recommended @@ -76,6 +76,7 @@ RenderGLMultiThreadedMedia 1 1 RenderReflectionProbeResolution 1 128 RenderScreenSpaceReflections 1 1 RenderMirrors 1 1 +RenderDownScaleMethod 1 1 // @@ -366,6 +367,7 @@ RenderAnisotropic 1 0 RenderFSAASamples 1 0 RenderGLContextCoreProfile 1 0 RenderGLMultiThreadedMedia 1 0 +RenderDownScaleMethod 1 0 list AMD RenderGLMultiThreadedTextures 1 1 diff --git a/indra/newview/featuretable_linux.txt b/indra/newview/featuretable_linux.txt index 1d6156bcb1..4e0c162c28 100644 --- a/indra/newview/featuretable_linux.txt +++ b/indra/newview/featuretable_linux.txt @@ -1,4 +1,4 @@ -version 47 +version 48 // The version number above should be incremented IF AND ONLY IF some // change has been made that is sufficiently important to justify // resetting the graphics preferences of all users to the recommended @@ -75,6 +75,8 @@ RenderGLMultiThreadedTextures 1 0 RenderGLMultiThreadedMedia 1 1 RenderReflectionProbeResolution 1 128 RenderScreenSpaceReflections 1 1 +RenderMirrors 1 1 +RenderDownScaleMethod 1 1 // @@ -110,7 +112,7 @@ RenderReflectionProbeLevel 1 0 RenderMirrors 1 0 RenderHeroProbeResolution 1 256 RenderHeroProbeDistance 1 4 -RenderHeroProbeUpdateRate 1 4 +RenderHeroProbeUpdateRate 1 6 RenderHeroProbeConservativeUpdateMultiplier 1 16 // @@ -251,8 +253,8 @@ RenderReflectionsEnabled 1 1 RenderReflectionProbeDetail 1 1 RenderScreenSpaceReflections 1 0 RenderReflectionProbeLevel 1 3 -RenderMirrors 1 0 -RenderHeroProbeResolution 1 1024 +RenderMirrors 1 1 +RenderHeroProbeResolution 1 512 RenderHeroProbeDistance 1 8 RenderHeroProbeUpdateRate 1 2 RenderHeroProbeConservativeUpdateMultiplier 1 8 @@ -287,7 +289,7 @@ RenderReflectionsEnabled 1 1 RenderReflectionProbeDetail 1 1 RenderScreenSpaceReflections 1 0 RenderReflectionProbeLevel 1 3 -RenderMirrors 1 0 +RenderMirrors 1 1 RenderHeroProbeResolution 1 1024 RenderHeroProbeDistance 1 16 RenderHeroProbeUpdateRate 1 1 @@ -367,13 +369,12 @@ RenderCubeMap 0 0 RenderFSAASamples 1 0 RenderGLContextCoreProfile 1 0 RenderGLMultiThreadedMedia 1 0 +RenderDownScaleMethod 1 0 // AMD cards generally perform better when not using VBOs for streaming data // AMD cards also prefer an OpenGL Compatibility Profile Context list AMD RenderGLMultiThreadedTextures 1 1 -RenderUseStreamVBO 1 0 -RenderGLContextCoreProfile 1 0 list GL3 RenderFSAASamples 0 0 diff --git a/indra/newview/gltf/accessor.h b/indra/newview/gltf/accessor.h index ec68c5f624..85ea0f2967 100644 --- a/indra/newview/gltf/accessor.h +++ b/indra/newview/gltf/accessor.h @@ -36,8 +36,6 @@ namespace LL { namespace GLTF { - constexpr S32 INVALID_INDEX = -1; - class Buffer { public: diff --git a/indra/newview/gltf/asset.cpp b/indra/newview/gltf/asset.cpp index a454e68a92..e07befef3b 100644 --- a/indra/newview/gltf/asset.cpp +++ b/indra/newview/gltf/asset.cpp @@ -476,6 +476,7 @@ void Asset::update() { // HACK - force texture to be loaded full rez // TODO: calculate actual vsize image.mTexture->addTextureStats(2048.f * 2048.f); + image.mTexture->setBoostLevel(LLViewerTexture::BOOST_HIGH); } } } diff --git a/indra/newview/gltf/common.h b/indra/newview/gltf/common.h index b9698d4017..742daff715 100644 --- a/indra/newview/gltf/common.h +++ b/indra/newview/gltf/common.h @@ -43,6 +43,8 @@ namespace LL { namespace GLTF { + constexpr S32 INVALID_INDEX = -1; + using Value = boost::json::value; using mat4 = glm::mat4; diff --git a/indra/newview/gltfscenemanager.cpp b/indra/newview/gltfscenemanager.cpp index aaf6eac620..cab4b227a8 100644 --- a/indra/newview/gltfscenemanager.cpp +++ b/indra/newview/gltfscenemanager.cpp @@ -345,6 +345,12 @@ void GLTFSceneManager::addGLTFObject(LLViewerObject* obj, LLUUID gltf_id) llassert(obj->getVolume()->getParams().getSculptID() == gltf_id); llassert(obj->getVolume()->getParams().getSculptType() == LL_SCULPT_TYPE_GLTF); + if (obj->mGLTFAsset) + { // object already has a GLTF asset, don't reload it + llassert(std::find(mObjects.begin(), mObjects.end(), obj) != mObjects.end()); + return; + } + obj->ref(); gAssetStorage->getAssetData(gltf_id, LLAssetType::AT_GLTF, onGLTFLoadComplete, obj); } diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index c9c8dad434..bfbcb99c2b 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -2593,8 +2593,8 @@ bool LLAppViewer::initThreads() // The viewer typically starts around 8 threads not including image decode, // so try to leave at least one core free // Override image decode thread config - //S32 image_decode_count = llclamp(cores - 9, 1, 8); - S32 image_decode_count = llclamp(cores - 4, 1, 8); + //S32 image_decode_count = llclamp(cores - 6, 2, 16); + S32 image_decode_count = llclamp(cores - 4, 2, 8); if (auto max_decodes = gSavedSettings.getU32("FSImageDecodeThreads"); max_decodes > 0) { image_decode_count = llclamp((S32)max_decodes, 1, 32); @@ -5463,6 +5463,10 @@ void LLAppViewer::idle() LLGLTFMaterialList::flushUpdates(); + static LLCachedControl downscale_method(gSavedSettings, "RenderDownScaleMethod"); + gGLManager.mDownScaleMethod = downscale_method; + LLImageGL::updateClass(); + // Service the WorkQueue we use for replies from worker threads. // Use function statics for the timeslice setting so we only have to fetch // and convert MainWorkTime once. diff --git a/indra/newview/lldrawpoolalpha.cpp b/indra/newview/lldrawpoolalpha.cpp index a1c04deaaa..ebbf023008 100644 --- a/indra/newview/lldrawpoolalpha.cpp +++ b/indra/newview/lldrawpoolalpha.cpp @@ -82,10 +82,6 @@ LLDrawPoolAlpha::~LLDrawPoolAlpha() void LLDrawPoolAlpha::prerender() { mShaderLevel = LLViewerShaderMgr::instance()->getShaderLevel(LLViewerShaderMgr::SHADER_OBJECT); - - // TODO: is this even necessay? These are probably set to never discard - LLViewerFetchedTexture::sFlatNormalImagep->addTextureStats(1024.f*1024.f); - LLViewerFetchedTexture::sWhiteImagep->addTextureStats(1024.f * 1024.f); } S32 LLDrawPoolAlpha::getNumPostDeferredPasses() diff --git a/indra/newview/llface.cpp b/indra/newview/llface.cpp index 03cbd03baa..82666717d4 100644 --- a/indra/newview/llface.cpp +++ b/indra/newview/llface.cpp @@ -173,6 +173,8 @@ void LLFace::init(LLDrawable* drawablep, LLViewerObject* objp) mImportanceToCamera = 0.f ; mBoundingSphereRadius = 0.0f ; + mTexExtents[0].set(0, 0); + mTexExtents[1].set(1, 1); mHasMedia = false ; mIsMediaAllowed = true; @@ -2181,10 +2183,12 @@ void LLFace::resetVirtualSize() F32 LLFace::getTextureVirtualSize() { LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE; + F32 radius; F32 cos_angle_to_view_dir; bool in_frustum = calcPixelArea(cos_angle_to_view_dir, radius); + if (mPixelArea < F_ALMOST_ZERO || !in_frustum) { setVirtualSize(0.f) ; diff --git a/indra/newview/llfloateremojipicker.cpp b/indra/newview/llfloateremojipicker.cpp index ae07310fbc..b5a2a383ed 100644 --- a/indra/newview/llfloateremojipicker.cpp +++ b/indra/newview/llfloateremojipicker.cpp @@ -866,8 +866,7 @@ void LLFloaterEmojiPicker::createEmojiIcon(const LLEmojiSearchResult& emoji, void LLFloaterEmojiPicker::showPreview(bool show) { - //mPreview->setIcon(nullptr); - mDummy->setVisible(show); + mDummy->setVisible(!show); mPreview->setVisible(show); } diff --git a/indra/newview/llfloatergltfasseteditor.cpp b/indra/newview/llfloatergltfasseteditor.cpp index f21b8032d0..13e0d36c35 100644 --- a/indra/newview/llfloatergltfasseteditor.cpp +++ b/indra/newview/llfloatergltfasseteditor.cpp @@ -52,8 +52,6 @@ LLFloaterGLTFAssetEditor::LLFloaterGLTFAssetEditor(const LLSD& key) LLFloaterGLTFAssetEditor::~LLFloaterGLTFAssetEditor() { - gIdleCallbacks.deleteFunction(idle, this); - if (mScroller) { removeChild(mScroller); @@ -120,7 +118,7 @@ void LLFloaterGLTFAssetEditor::initFolderRoot() // Insert that scroller into the panel widgets hierarchy mItemListPanel->addChild(mScroller); - // Create the root model and view for all conversation sessions + // Create the root model LLGLTFFolderItem* base_item = new LLGLTFFolderItem(mGLTFViewModel); LLFolderView::Params p(LLUICtrlFactory::getDefaultParams()); @@ -144,15 +142,32 @@ void LLFloaterGLTFAssetEditor::initFolderRoot() mFolderRoot->setOpen(true); mFolderRoot->setSelectCallback([this](const std::deque& items, bool user_action) { onFolderSelectionChanged(items, user_action); }); mScroller->setVisible(true); - - gIdleCallbacks.addFunction(idle, this); } void LLFloaterGLTFAssetEditor::onOpen(const LLSD& key) { + gIdleCallbacks.addFunction(idle, this); loadFromSelection(); } +void LLFloaterGLTFAssetEditor::onClose(bool app_quitting) +{ + gIdleCallbacks.deleteFunction(idle, this); + mAsset = nullptr; + mObject = nullptr; +} + +void LLFloaterGLTFAssetEditor::clearRoot() +{ + LLFolderViewFolder::folders_t::iterator folders_it = mFolderRoot->getFoldersBegin(); + while (folders_it != mFolderRoot->getFoldersEnd()) + { + (*folders_it)->destroyView(); + folders_it = mFolderRoot->getFoldersBegin(); + } + mNodeToItemMap.clear(); +} + void LLFloaterGLTFAssetEditor::idle(void* user_data) { LLFloaterGLTFAssetEditor* floater = (LLFloaterGLTFAssetEditor*)user_data; @@ -216,6 +231,8 @@ void LLFloaterGLTFAssetEditor::loadFromNode(S32 node_id, LLFolderViewFolder* par view->setVisible(true); view->setOpen(true); + mNodeToItemMap[node_id] = view; + for (S32& node_id : node.mChildren) { loadFromNode(node_id, view); @@ -246,8 +263,12 @@ void LLFloaterGLTFAssetEditor::loadFromNode(S32 node_id, LLFolderViewFolder* par void LLFloaterGLTFAssetEditor::loadFromSelection() { - if (!mFolderRoot || LLSelectMgr::getInstance()->getSelection()->getObjectCount() != 1) + clearRoot(); + + if (LLSelectMgr::getInstance()->getSelection()->getObjectCount() != 1) { + mAsset = nullptr; + mObject = nullptr; return; } @@ -255,14 +276,19 @@ void LLFloaterGLTFAssetEditor::loadFromSelection() LLViewerObject* objectp = node->getObject(); if (!objectp) { + mAsset = nullptr; + mObject = nullptr; return; } - mAsset = objectp->mGLTFAsset; - if (!mAsset) + if (!objectp->mGLTFAsset) { + mAsset = nullptr; + mObject = nullptr; return; } + mAsset = objectp->mGLTFAsset; + mObject = objectp; if (node->mName.empty()) { @@ -289,7 +315,6 @@ void LLFloaterGLTFAssetEditor::loadFromSelection() LLGLTFFolderItem* listener = new LLGLTFFolderItem(i, name, LLGLTFFolderItem::TYPE_SCENE, mGLTFViewModel); - LLFolderViewFolder::Params p; p.name = name; p.root = mFolderRoot; @@ -316,6 +341,50 @@ void LLFloaterGLTFAssetEditor::loadFromSelection() mFolderRoot->update(); } +void LLFloaterGLTFAssetEditor::dirty() +{ + if (!mObject || !mAsset || !mFolderRoot) + { + closeFloater(); + return; + } + + if (LLSelectMgr::getInstance()->getSelection()->getObjectCount() > 1) + { + closeFloater(); + return; + } + + LLSelectNode* node = LLSelectMgr::getInstance()->getSelection()->getFirstNode(NULL); + if (!node) + { + // not yet updated? + // Todo: Subscribe to deletion in some way + return; + } + + LLViewerObject* objectp = node->getObject(); + if (mObject != objectp || !objectp->mGLTFAsset) + { + closeFloater(); + return; + } + + if (mAsset != objectp->mGLTFAsset) + { + loadFromSelection(); + return; + } + + auto found = mNodeToItemMap.find(node->mSelectedGLTFNode); + if (found != mNodeToItemMap.end()) + { + LLFolderViewItem* itemp = found->second; + itemp->arrangeAndSet(true, false); + loadNodeTransforms(node->mSelectedGLTFNode); + } +} + void LLFloaterGLTFAssetEditor::onFolderSelectionChanged(const std::deque& items, bool user_action) { if (items.empty()) @@ -329,10 +398,30 @@ void LLFloaterGLTFAssetEditor::onFolderSelectionChanged(const std::dequegetType()) { + case LLGLTFFolderItem::TYPE_SCENE: + { + setTransformsEnabled(false); + LLSelectMgr::getInstance()->selectObjectOnly(mObject, SELECT_ALL_TES, -1, -1); + break; + } case LLGLTFFolderItem::TYPE_NODE: { setTransformsEnabled(true); loadNodeTransforms(vmi->getItemId()); + LLSelectMgr::getInstance()->selectObjectOnly(mObject, SELECT_ALL_TES, vmi->getItemId(), 0); + break; + } + case LLGLTFFolderItem::TYPE_MESH: + case LLGLTFFolderItem::TYPE_SKIN: + { + if (item->getParent()) // should be a node + { + LLFolderViewFolder* parent = item->getParentFolder(); + LLGLTFFolderItem* parent_vmi = static_cast(parent->getViewModelItem()); + LLSelectMgr::getInstance()->selectObjectOnly(mObject, SELECT_ALL_TES, parent_vmi->getItemId(), 0); + } + + setTransformsEnabled(false); break; } default: @@ -368,6 +457,7 @@ void LLFloaterGLTFAssetEditor::loadNodeTransforms(S32 node_id) } LL::GLTF::Node& node = mAsset->mNodes[node_id]; + node.makeTRSValid(); mCtrlPosX->set(node.mTranslation[0]); mCtrlPosY->set(node.mTranslation[1]); diff --git a/indra/newview/llfloatergltfasseteditor.h b/indra/newview/llfloatergltfasseteditor.h index e35ed30ed0..b0ba8941b9 100644 --- a/indra/newview/llfloatergltfasseteditor.h +++ b/indra/newview/llfloatergltfasseteditor.h @@ -42,6 +42,7 @@ namespace LL class LLSpinCtrl; class LLMenuButton; +class LLViewerObject; class LLFloaterGLTFAssetEditor : public LLFloater { @@ -51,6 +52,7 @@ public: bool postBuild() override; void onOpen(const LLSD& key) override; + void onClose(bool app_quitting) override; void initFolderRoot(); LLGLTFViewModel& getRootViewModel() { return mGLTFViewModel; } @@ -60,6 +62,8 @@ public: void loadFromNode(S32 node, LLFolderViewFolder* parent); void loadFromSelection(); + void dirty(); + protected: void onFolderSelectionChanged(const std::deque& items, bool user_action); void onCommitTransform(); @@ -69,8 +73,11 @@ protected: void setTransformsEnabled(bool val); void loadNodeTransforms(S32 id); + void clearRoot(); + private: + LLPointer mObject; std::shared_ptr mAsset; // Folder view related @@ -79,6 +86,7 @@ private: LLPanel* mItemListPanel = nullptr; LLFolderView* mFolderRoot = nullptr; LLScrollContainer* mScroller = nullptr; + std::map mNodeToItemMap; // Transforms panel LLVector3 mLastEulerDegrees; diff --git a/indra/newview/llmodelpreview.cpp b/indra/newview/llmodelpreview.cpp index cdfca83426..3e88bdc8bf 100644 --- a/indra/newview/llmodelpreview.cpp +++ b/indra/newview/llmodelpreview.cpp @@ -4193,6 +4193,13 @@ U32 LLModelPreview::loadTextures(LLImportMaterial& material, void* opaque) LLPointer< LLViewerFetchedTexture >& tex = (*reinterpret_cast< LLPointer< LLViewerFetchedTexture > * >(material.mOpaqueData)); tex = LLViewerTextureManager::getFetchedTextureFromUrl("file://" + LLURI::unescape(material.mDiffuseMapFilename), FTT_LOCAL_FILE, true, LLGLTexture::BOOST_PREVIEW); + if (tex->getDiscardLevel() < tex->getMaxDiscardLevel()) + { + // file was loaded previosly, reload image to get potential changes + tex->clearFetchedResults(); + } + // Todo: might cause a crash if preview gets closed before we get the callback. + // Use a callback list or guard callback in some way tex->setLoadedCallback(LLModelPreview::textureLoadedCallback, 0, true, false, opaque, NULL, false); tex->forceToSaveRawImage(0, F32_MAX); material.setDiffuseMap(tex->getID()); // record tex ID diff --git a/indra/newview/llselectmgr.cpp b/indra/newview/llselectmgr.cpp index 854dc9628a..4518257aeb 100644 --- a/indra/newview/llselectmgr.cpp +++ b/indra/newview/llselectmgr.cpp @@ -57,6 +57,7 @@ #include "llaudioengine.h" // For object deletion sound #include "llviewerwindow.h" #include "lldrawable.h" +#include "llfloatergltfasseteditor.h" #include "llfloaterinspect.h" #include "llfloaterproperties.h" // Keep legacy properties floater #include "llfloaterreporter.h" @@ -490,6 +491,11 @@ LLObjectSelectionHandle LLSelectMgr::selectObjectOnly(LLViewerObject* object, S3 if (object->isSelected() ) { // make sure point at position is updated updatePointAt(); + LLSelectNode* nodep = mSelectedObjects->findNode(object); + if (nodep) + { + nodep->selectGLTFNode(gltf_node, gltf_primitive, true); + } gEditMenuHandler = this; return NULL; } @@ -7461,6 +7467,12 @@ void dialog_refresh_all() { panel_task_info->dirty(); } + + LLFloaterGLTFAssetEditor * gltf_editor = LLFloaterReg::getTypedInstance("gltf_asset_editor"); + if (gltf_editor) + { + gltf_editor->dirty(); + } } S32 get_family_count(LLViewerObject *parent) diff --git a/indra/newview/lltextureview.cpp b/indra/newview/lltextureview.cpp index fa7656e81a..710946a374 100644 --- a/indra/newview/lltextureview.cpp +++ b/indra/newview/lltextureview.cpp @@ -554,6 +554,9 @@ void LLGLTexMemBar::draw() F64 raw_image_bytes_MB = raw_image_bytes / (1024.0 * 1024.0); F64 saved_raw_image_bytes_MB = saved_raw_image_bytes / (1024.0 * 1024.0); F64 aux_raw_image_bytes_MB = aux_raw_image_bytes / (1024.0 * 1024.0); + F64 texture_bytes_alloc = LLImageGL::getTextureBytesAllocated() / 1024.0 / 1024.0 * 1.3333f; // add 33% for mipmaps + F64 vertex_bytes_alloc = LLVertexBuffer::getBytesAllocated() / 1024.0 / 1024.0; + F64 render_bytes_alloc = LLRenderTarget::sBytesAllocated / 1024.0 / 1024.0; //---------------------------------------------------------------------------- LLGLSUIDefault gls_ui; @@ -583,7 +586,7 @@ void LLGLTexMemBar::draw() // draw a background above first line.... no idea where the rest of the background comes from for the below text gGL.color4f(0.f, 0.f, 0.f, 0.25f); - gl_rect_2d(-10, getRect().getHeight() + line_height + 1, getRect().getWidth()+2, getRect().getHeight()+2); + gl_rect_2d(-10, getRect().getHeight() + line_height*2 + 1, getRect().getWidth()+2, getRect().getHeight()+2); text = llformat("Est. Free: %d MB Sys Free: %d MB GL Tex: %d MB FBO: %d MB Bias: %.2f Cache: %.1f/%.1f MB", (S32)LLViewerTexture::sFreeVRAMMegabytes, @@ -594,8 +597,8 @@ void LLGLTexMemBar::draw() cache_usage, cache_max_usage); // Texture memory bars - //LLFontGL::getFontMonospace()->renderUTF8(text, 0, 0, v_offset + line_height*6, - LLFontGL::getFontMonospace()->renderUTF8(text, 0, 0, v_offset + line_height*8, + //LLFontGL::getFontMonospace()->renderUTF8(text, 0, 0, v_offset + line_height*7, + LLFontGL::getFontMonospace()->renderUTF8(text, 0, 0, v_offset + line_height*9, // text_color, LLFontGL::LEFT, LLFontGL::TOP); @@ -603,7 +606,7 @@ void LLGLTexMemBar::draw() S32 bar_left = 0; constexpr S32 bar_width = 200; constexpr S32 bar_space = 10; - S32 top = line_height*7 - 2 + v_offset; + S32 top = line_height*8 - 2 + v_offset; S32 bottom = top - 6; S32 left = bar_left; S32 right = left + bar_width; @@ -613,7 +616,7 @@ void LLGLTexMemBar::draw() // VRAM Mem Bar text = "VRAM"; - LLFontGL::getFontMonospace()->renderUTF8(text, 0, left, v_offset + line_height*7, + LLFontGL::getFontMonospace()->renderUTF8(text, 0, left, v_offset + line_height*8, text_color, LLFontGL::LEFT, LLFontGL::TOP); left += 35; right = left + bar_width; @@ -636,7 +639,7 @@ void LLGLTexMemBar::draw() left = bar_left; // VRAM Mem Bar text = "CACHE"; - LLFontGL::getFontMonospace()->renderUTF8(text, 0, left, v_offset + line_height*7, + LLFontGL::getFontMonospace()->renderUTF8(text, 0, left, v_offset + line_height*8, text_color, LLFontGL::LEFT, LLFontGL::TOP); left += 35; @@ -658,6 +661,14 @@ void LLGLTexMemBar::draw() text = llformat("Images: %d Raw: %d (%.2f MB) Saved: %d (%.2f MB) Aux: %d (%.2f MB)", image_count, raw_image_count, raw_image_bytes_MB, saved_raw_image_count, saved_raw_image_bytes_MB, aux_raw_image_count, aux_raw_image_bytes_MB); + LLFontGL::getFontMonospace()->renderUTF8(text, 0, 0, v_offset + line_height * 7, + text_color, LLFontGL::LEFT, LLFontGL::TOP); + + text = llformat("Textures: %.2f MB Vertex: %.2f MB Render: %.2f MB Total: %.2f MB", + texture_bytes_alloc, + vertex_bytes_alloc, + render_bytes_alloc, + texture_bytes_alloc+vertex_bytes_alloc+render_bytes_alloc); LLFontGL::getFontMonospace()->renderUTF8(text, 0, 0, v_offset + line_height * 6, text_color, LLFontGL::LEFT, LLFontGL::TOP); diff --git a/indra/newview/llviewerassetstorage.cpp b/indra/newview/llviewerassetstorage.cpp index 3576bfa33e..595e2dd0b8 100644 --- a/indra/newview/llviewerassetstorage.cpp +++ b/indra/newview/llviewerassetstorage.cpp @@ -379,6 +379,7 @@ void LLViewerAssetStorage::queueRequestHttp( bool duplicate, bool is_priority) { + LL_PROFILE_ZONE_SCOPED; LL_DEBUGS("ViewerAsset") << "Request asset via HTTP " << uuid << " type " << LLAssetType::lookup(atype) << LL_ENDL; bool with_http = true; diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp index daacc1d517..e0eed7e8ed 100644 --- a/indra/newview/llviewerobject.cpp +++ b/indra/newview/llviewerobject.cpp @@ -4581,6 +4581,7 @@ void LLViewerObject::moveGLTFNode(S32 node_index, const LLVector3& offset) matMul(trans, mat, mat); node.mMatrix = glm::make_mat4(mat.getF32ptr()); + node.mTRSValid = false; // TODO -- only update transforms for this node and its children (or use a dirty flag) mGLTFAsset->updateTransforms(); diff --git a/indra/newview/llviewertexture.cpp b/indra/newview/llviewertexture.cpp index 501421f8de..27b763b47a 100644 --- a/indra/newview/llviewertexture.cpp +++ b/indra/newview/llviewertexture.cpp @@ -97,7 +97,7 @@ S32 LLViewerTexture::sRawCount = 0; S32 LLViewerTexture::sAuxCount = 0; LLFrameTimer LLViewerTexture::sEvaluationTimer; F32 LLViewerTexture::sDesiredDiscardBias = 0.f; -F32 LLViewerTexture::sDesiredDiscardScale = 1.1f; + S32 LLViewerTexture::sMaxSculptRez = 128; //max sculpt image size const S32 MAX_CACHED_RAW_IMAGE_AREA = 64 * 64; const S32 MAX_CACHED_RAW_SCULPT_IMAGE_AREA = LLViewerTexture::sMaxSculptRez * LLViewerTexture::sMaxSculptRez; @@ -532,15 +532,18 @@ void LLViewerTexture::updateClass() F32 target = llmax(budget - 512.f, MIN_VRAM_BUDGET); sFreeVRAMMegabytes = target - used; - F32 over_pct = llmax((used-target) / target, 0.f); + F32 over_pct = (used - target) / target; + + bool is_low = over_pct > 0.f; if (isSystemMemoryLow()) { + is_low = true; // System RAM is low -> ramp up discard bias over time to free memory if (sEvaluationTimer.getElapsedTimeF32() > MEMORY_CHECK_WAIT_TIME) { static LLCachedControl low_mem_min_discard_increment(gSavedSettings, "RenderLowMemMinDiscardIncrement", .1f); - sDesiredDiscardBias += llmax(low_mem_min_discard_increment, over_pct); + sDesiredDiscardBias += (F32) low_mem_min_discard_increment * (F32) gFrameIntervalSeconds; sEvaluationTimer.reset(); } } @@ -548,12 +551,28 @@ void LLViewerTexture::updateClass() { sDesiredDiscardBias = llmax(sDesiredDiscardBias, 1.f + over_pct); - if (sDesiredDiscardBias > 1.f) + if (sDesiredDiscardBias > 1.f && over_pct < 0.f) { sDesiredDiscardBias -= gFrameIntervalSeconds * 0.01; } } + static bool was_low = false; + if (is_low && !was_low) + { + LL_WARNS() << "Low system memory detected, emergency downrezzing off screen textures" << LL_ENDL; + sDesiredDiscardBias = llmax(sDesiredDiscardBias, 1.5f); + + for (auto image : gTextureList) + { + gTextureList.updateImageDecodePriority(image); + } + } + + was_low = is_low; + + sDesiredDiscardBias = llclamp(sDesiredDiscardBias, 1.f, 3.f); + LLViewerTexture::sFreezeImageUpdates = false; } @@ -634,16 +653,15 @@ void LLViewerTexture::init(bool firstinit) mParcelMedia = NULL; memset(&mNumVolumes, 0, sizeof(U32)* LLRender::NUM_VOLUME_TEXTURE_CHANNELS); - mFaceList[LLRender::DIFFUSE_MAP].clear(); - mFaceList[LLRender::NORMAL_MAP].clear(); - mFaceList[LLRender::SPECULAR_MAP].clear(); - mNumFaces[LLRender::DIFFUSE_MAP] = - mNumFaces[LLRender::NORMAL_MAP] = - mNumFaces[LLRender::SPECULAR_MAP] = 0; - mVolumeList[LLRender::LIGHT_TEX].clear(); mVolumeList[LLRender::SCULPT_TEX].clear(); + for (U32 i = 0; i < LLRender::NUM_TEXTURE_CHANNELS; i++) + { + mNumFaces[i] = 0; + mFaceList[i].clear(); + } + mMainQueue = LL::WorkQueue::getInstance("mainloop"); mImageQueue = LL::WorkQueue::getInstance("LLImageGL"); } @@ -1674,7 +1692,11 @@ void LLViewerFetchedTexture::scheduleCreateTexture() } else { - gTextureList.mCreateTextureList.insert(this); + if (!mCreatePending) + { + mCreatePending = true; + gTextureList.mCreateTextureList.push(this); + } } } } @@ -1698,13 +1720,12 @@ void LLViewerFetchedTexture::setKnownDrawSize(S32 width, S32 height) void LLViewerFetchedTexture::setDebugText(const std::string& text) { - for (U32 ch = 0; ch < LLRender::NUM_TEXTURE_CHANNELS; ++ch) + for (U32 i = 0; i < LLRender::NUM_TEXTURE_CHANNELS; ++i) { - llassert(mNumFaces[ch] <= mFaceList[ch].size()); - - for (U32 i = 0; i < mNumFaces[ch]; i++) + for (S32 fi = 0; fi < getNumFaces(i); ++fi) { - LLFace* facep = mFaceList[ch][i]; + LLFace* facep = (*(getFaceList(i)))[fi]; + if (facep) { LLDrawable* drawable = facep->getDrawable(); @@ -1717,10 +1738,15 @@ void LLViewerFetchedTexture::setDebugText(const std::string& text) } } +extern bool gCubeSnapshot; + //virtual void LLViewerFetchedTexture::processTextureStats() { LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE; + llassert(!gCubeSnapshot); // should only be called when the main camera is active + llassert(!LLPipeline::sShadowRender); + if(mFullyLoaded) { if(mDesiredDiscardLevel > mMinDesiredDiscardLevel)//need to load more @@ -2941,6 +2967,8 @@ void LLViewerLODTexture::processTextureStats() LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE; updateVirtualSize(); + bool did_downscale = false; + static LLCachedControl textures_fullres(gSavedSettings,"TextureLoadFullRes", false); { // restrict texture resolution to download based on RenderMaxTextureResolution @@ -2998,10 +3026,7 @@ void LLViewerLODTexture::processTextureStats() mDiscardVirtualSize = mMaxVirtualSize; mCalculatedDiscardLevel = discard_level; } - if (mBoostLevel < LLGLTexture::BOOST_SCULPTED) - { - discard_level *= sDesiredDiscardScale; // scale (default 1.1f) - } + discard_level = floorf(discard_level); F32 min_discard = 0.f; @@ -3027,10 +3052,9 @@ void LLViewerLODTexture::processTextureStats() // S32 current_discard = getDiscardLevel(); - if (mBoostLevel < LLGLTexture::BOOST_AVATAR_BAKED && - current_discard >= 0) + if (mBoostLevel < LLGLTexture::BOOST_AVATAR_BAKED) { - if (current_discard < (mDesiredDiscardLevel-1) && !mForceToSaveRawImage) + if (current_discard < mDesiredDiscardLevel && !mForceToSaveRawImage) { // should scale down scaleDown(); } @@ -3050,9 +3074,6 @@ void LLViewerLODTexture::processTextureStats() mDesiredDiscardLevel = llmin(mDesiredDiscardLevel, (S8)mDesiredSavedRawDiscardLevel); } - // decay max virtual size over time - mMaxVirtualSize *= 0.8f; - // selection manager will immediately reset BOOST_SELECTED but never unsets it // unset it immediately after we consume it if (getBoostLevel() == BOOST_SELECTED) @@ -3061,14 +3082,22 @@ void LLViewerLODTexture::processTextureStats() } } +extern LLGLSLShader gCopyProgram; + bool LLViewerLODTexture::scaleDown() { - if (mGLTexturep.isNull()) + if (mGLTexturep.isNull() || !mGLTexturep->getHasGLTexture()) { return false; } - return mGLTexturep->scaleDown(mDesiredDiscardLevel); + if (!mDownScalePending) + { + mDownScalePending = true; + gTextureList.mDownScaleQueue.push(this); + } + + return true; } //---------------------------------------------------------------------------------------------- diff --git a/indra/newview/llviewertexture.h b/indra/newview/llviewertexture.h index d4971de935..8a8d167067 100644 --- a/indra/newview/llviewertexture.h +++ b/indra/newview/llviewertexture.h @@ -37,6 +37,7 @@ #include "llmetricperformancetester.h" #include "httpcommon.h" #include "workqueue.h" +#include "gltf/common.h" #include #include @@ -108,7 +109,6 @@ public: DYNAMIC_TEXTURE, FETCHED_TEXTURE, LOD_TEXTURE, - ATLAS_TEXTURE, INVALID_TEXTURE_TYPE }; @@ -181,6 +181,15 @@ public: LLViewerMediaTexture* getParcelMedia() const { return mParcelMedia;} /*virtual*/ void updateBindStatsForTester() ; + + struct MaterialEntry + { + S32 mIndex = LL::GLTF::INVALID_INDEX; + std::shared_ptr mAsset; + }; + typedef std::vector material_list_t; + material_list_t mMaterialList; // reverse pointer pointing to LL::GLTF::Materials using this image as texture + protected: void cleanup() ; void init(bool firstinit) ; @@ -222,7 +231,6 @@ public: static S32 sAuxCount; static LLFrameTimer sEvaluationTimer; static F32 sDesiredDiscardBias; - static F32 sDesiredDiscardScale; static S32 sMaxSculptRez ; static U32 sMinLargeImageSize ; static U32 sMaxSmallImageSize ; @@ -445,6 +453,9 @@ public: /*virtual*/bool isActiveFetching() override; //is actively in fetching by the fetching pipeline. + bool mCreatePending = false; // if true, this is in gTextureList.mCreateTextureList + mutable bool mDownScalePending = false; // if true, this is in gTextureList.mDownScaleQueue + // texture comment decoder std::map mComment; // @@ -460,11 +471,6 @@ private: void saveRawImage() ; - //for atlas - void resetFaceAtlas() ; - void invalidateAtlas(bool rebuild_geom) ; - bool insertToAtlas() ; - private: bool mFullyLoaded; bool mInDebug; @@ -579,9 +585,10 @@ public: /*virtual*/ void processTextureStats(); bool isUpdateFrozen() ; + bool scaleDown(); + private: void init(bool firstinit) ; - bool scaleDown() ; private: F32 mDiscardVirtualSize; // Virtual size used to calculate desired discard diff --git a/indra/newview/llviewertexturelist.cpp b/indra/newview/llviewertexturelist.cpp index 3ad6cc868f..4b5d60263c 100644 --- a/indra/newview/llviewertexturelist.cpp +++ b/indra/newview/llviewertexturelist.cpp @@ -73,6 +73,8 @@ U32 LLViewerTextureList::sNumFastCacheReads = 0; LLViewerTextureList gTextureList; +extern LLGLSLShader gCopyProgram; + ETexListType get_element_type(S32 priority) { return (priority == LLViewerFetchedTexture::BOOST_ICON || priority == LLViewerFetchedTexture::BOOST_THUMBNAIL) ? TEX_LIST_SCALE : TEX_LIST_STANDARD; @@ -360,8 +362,11 @@ void LLViewerTextureList::shutdown() mCallbackList.clear(); // Flush all of the references - mLoadingStreamList.clear(); - mCreateTextureList.clear(); + while (!mCreateTextureList.empty()) + { + mCreateTextureList.front()->mCreatePending = false; + mCreateTextureList.pop(); + } mFastCacheList.clear(); mUUIDMap.clear(); @@ -910,14 +915,6 @@ void LLViewerTextureList::clearFetchingRequests() } } -static void touch_texture(LLViewerFetchedTexture* tex, F32 vsize) -{ - if (tex) - { - tex->addTextureStats(vsize); - } -} - extern bool gCubeSnapshot; void LLViewerTextureList::updateImageDecodePriority(LLViewerFetchedTexture* imagep) @@ -934,58 +931,67 @@ void LLViewerTextureList::updateImageDecodePriority(LLViewerFetchedTexture* imag static LLCachedControl texture_scale_min(gSavedSettings, "TextureScaleMinAreaFactor", 0.04f); static LLCachedControl texture_scale_max(gSavedSettings, "TextureScaleMaxAreaFactor", 25.f); + if (imagep->getType() == LLViewerTexture::LOD_TEXTURE && imagep->getBoostLevel() == LLViewerTexture::BOOST_NONE) + { // reset max virtual size for unboosted LOD_TEXTURES + // this is an alternative to decaying mMaxVirtualSize over time + // that keeps textures from continously downrezzing and uprezzing in the background + imagep->mMaxVirtualSize = 0.f; + } + LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE + for (U32 i = 0; i < LLRender::NUM_TEXTURE_CHANNELS; ++i) { - for (U32 i = 0; i < LLRender::NUM_TEXTURE_CHANNELS; ++i) + for (S32 fi = 0; fi < imagep->getNumFaces(i); ++fi) { - for (S32 fi = 0; fi < imagep->getNumFaces(i); ++fi) + LLFace* face = (*(imagep->getFaceList(i)))[fi]; + + if (face && face->getViewerObject()) { - LLFace* face = (*(imagep->getFaceList(i)))[fi]; + F32 radius; + F32 cos_angle_to_view_dir; + bool in_frustum = face->calcPixelArea(cos_angle_to_view_dir, radius); + static LLCachedControl bias_unimportant_threshold(gSavedSettings, "TextureBiasUnimportantFactor", 0.25f); + F32 vsize = face->getPixelArea(); - if (face && face->getViewerObject() && face->getTextureEntry()) - { - F32 radius; - F32 cos_angle_to_view_dir; - bool in_frustum = face->calcPixelArea(cos_angle_to_view_dir, radius); - static LLCachedControl bias_unimportant_threshold(gSavedSettings, "TextureBiasUnimportantFactor", 0.25f); - F32 vsize = face->getPixelArea(); + // Scale desired texture resolution higher or lower depending on texture scale + // + // Minimum usage examples: a 1024x1024 texture with aplhabet, runing string + // shows one letter at a time + // + // Maximum usage examples: huge chunk of terrain repeats texture + const LLTextureEntry* te = face->getTextureEntry(); + F32 min_scale = te ? llmin(fabsf(te->getScaleS()), fabsf(te->getScaleT())) : 1.f; + min_scale = llclamp(min_scale * min_scale, texture_scale_min(), texture_scale_max()); + vsize /= min_scale; - // Scale desired texture resolution higher or lower depending on texture scale - // - // Minimum usage examples: a 1024x1024 texture with aplhabet, runing string - // shows one letter at a time - // - // Maximum usage examples: huge chunk of terrain repeats texture - const LLTextureEntry* te = face->getTextureEntry(); - F32 min_scale = te ? llmin(fabsf(te->getScaleS()), fabsf(te->getScaleT())) : 1.f; - min_scale = llclamp(min_scale*min_scale, texture_scale_min(), texture_scale_max()); + // if bias is > 2, apply to on-screen textures as well + bool apply_bias = LLViewerTexture::sDesiredDiscardBias > 2.f; - vsize /= min_scale; - if (!in_frustum || !face->getDrawable()->isVisible() || face->getImportanceToCamera() < bias_unimportant_threshold) - { // further reduce by discard bias when off screen or occluded - vsize /= LLViewerTexture::sDesiredDiscardBias; - } - // if a GLTF material is present, ignore that face - // as far as this texture stats go, but update the GLTF material - // stats - LLFetchedGLTFMaterial* mat = te ? (LLFetchedGLTFMaterial*)te->getGLTFRenderMaterial() : nullptr; - llassert(mat == nullptr || dynamic_cast(te->getGLTFRenderMaterial()) != nullptr); - if (mat) - { - touch_texture(mat->mBaseColorTexture, vsize); - touch_texture(mat->mNormalTexture, vsize); - touch_texture(mat->mMetallicRoughnessTexture, vsize); - touch_texture(mat->mEmissiveTexture, vsize); - } - else - { - imagep->addTextureStats(vsize); - } + // apply bias to off screen objects or objects that are small on screen all the time + if (!in_frustum || !face->getDrawable()->isVisible() || face->getImportanceToCamera() < bias_unimportant_threshold) + { // further reduce by discard bias when off screen or occluded + apply_bias = true; } + + if (apply_bias) + { + F32 bias = powf(4, LLViewerTexture::sDesiredDiscardBias - 1.f); + bias = llround(bias); + vsize /= bias; + } + + imagep->addTextureStats(vsize); } } } + // make sure to addTextureStats for any spotlights that are using this texture + for (S32 vi = 0; vi < imagep->getNumVolumes(LLRender::LIGHT_TEX); ++vi) + { + LLVOVolume* volume = (*imagep->getVolumeList(LLRender::LIGHT_TEX))[vi]; + volume->updateSpotLightPriority(); + } + //imagep->setDebugText(llformat("%.3f - %d", sqrtf(imagep->getMaxVirtualSize()), imagep->getBoostLevel())); F32 lazy_flush_timeout = 30.f; // stop decoding @@ -1079,22 +1085,65 @@ F32 LLViewerTextureList::updateImagesCreateTextures(F32 max_time) // LLTimer create_timer; - image_list_t::iterator enditer = mCreateTextureList.begin(); - for (image_list_t::iterator iter = mCreateTextureList.begin(); - iter != mCreateTextureList.end();) + + if (!mDownScaleQueue.empty() && gPipeline.mDownResMap.isComplete()) { - image_list_t::iterator curiter = iter++; - enditer = iter; - LLViewerFetchedTexture *imagep = *curiter; + // just in case we downres textures, bind downresmap and copy program + gPipeline.mDownResMap.bindTarget(); + gCopyProgram.bind(); + gPipeline.mScreenTriangleVB->setBuffer(); + + // give time to downscaling first -- if mDownScaleQueue is not empty, we're running out of memory and need + // to free up memory by discarding off screen textures quickly + + // do at least 5 and make sure we don't get too far behind even if it violates + // the time limit. If we don't downscale quickly the viewer will hit swap and may + // freeze. + S32 min_count = (S32)mCreateTextureList.size() / 20 + 5; + + while (!mDownScaleQueue.empty()) + { + LLViewerFetchedTexture* image = mDownScaleQueue.front(); + llassert(image->mDownScalePending); + + LLImageGL* img = image->getGLTexture(); + if (img && img->getHasGLTexture()) + { + img->scaleDown(image->getDesiredDiscardLevel()); + } + + image->mDownScalePending = false; + mDownScaleQueue.pop(); + + if (create_timer.getElapsedTimeF32() > max_time && --min_count <= 0) + { + break; + } + } + + gCopyProgram.unbind(); + gPipeline.mDownResMap.flush(); + } + + // do at least 5 and make sure we don't get too far behind even if it violates + // the time limit. Textures pending creation have a copy of their texture data + // in system memory, so we don't want to let them pile up. + S32 min_count = (S32) mCreateTextureList.size() / 20 + 5; + + while (!mCreateTextureList.empty()) + { + LLViewerFetchedTexture *imagep = mCreateTextureList.front(); + llassert(imagep->mCreatePending); imagep->createTexture(); imagep->postCreateTexture(); + imagep->mCreatePending = false; + mCreateTextureList.pop(); - if (create_timer.getElapsedTimeF32() > max_time) + if (create_timer.getElapsedTimeF32() > max_time && --min_count <= 0) { break; } } - mCreateTextureList.erase(mCreateTextureList.begin(), enditer); return create_timer.getElapsedTimeF32(); } @@ -1140,7 +1189,10 @@ void LLViewerTextureList::forceImmediateUpdate(LLViewerFetchedTexture* imagep) removeImageFromList(imagep); } - imagep->processTextureStats(); + if (!gCubeSnapshot) + { // never call processTextureStats in a cube snapshot + imagep->processTextureStats(); + } imagep->sMaxVirtualSize = LLViewerFetchedTexture::sMaxVirtualSize; addImageToList(imagep); @@ -1150,6 +1202,7 @@ void LLViewerTextureList::forceImmediateUpdate(LLViewerFetchedTexture* imagep) F32 LLViewerTextureList::updateImagesFetchTextures(F32 max_time) { LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE; + typedef std::vector > entries_list_t; entries_list_t entries; diff --git a/indra/newview/llviewertexturelist.h b/indra/newview/llviewertexturelist.h index a13a9d34a0..dbbda66dba 100644 --- a/indra/newview/llviewertexturelist.h +++ b/indra/newview/llviewertexturelist.h @@ -148,12 +148,13 @@ public: void clearFetchingRequests(); void setDebugFetching(LLViewerFetchedTexture* tex, S32 debug_level); -private: // do some book keeping on the specified texture // - updates decode priority // - updates desired discard level // - cleans up textures that haven't been referenced in awhile void updateImageDecodePriority(LLViewerFetchedTexture* imagep); + +private: F32 updateImagesCreateTextures(F32 max_time); F32 updateImagesFetchTextures(F32 max_time); void updateImagesUpdateStats(); @@ -217,8 +218,14 @@ private: // PoundLife - Improved Object Inspect public: typedef std::unordered_set > image_list_t; - image_list_t mLoadingStreamList; - image_list_t mCreateTextureList; + typedef std::queue > image_queue_t; + + // images that have been loaded but are waiting to be uploaded to GL + image_queue_t mCreateTextureList; + + // images that must be downscaled quickly so we don't run out of memory + image_queue_t mDownScaleQueue; + image_list_t mCallbackList; image_list_t mFastCacheList; diff --git a/indra/newview/llvograss.cpp b/indra/newview/llvograss.cpp index 528347d83e..b12508ff09 100644 --- a/indra/newview/llvograss.cpp +++ b/indra/newview/llvograss.cpp @@ -461,6 +461,9 @@ void LLVOGrass::plantBlades() face->setVertexBuffer(NULL); face->setTEOffset(0); face->mCenterLocal = mPosition + mRegionp->getOriginAgent(); + const LLVector4a* ext = mDrawable->getSpatialExtents(); + face->mExtents[0] = ext[0]; + face->mExtents[1] = ext[1]; } mDepth = (face->mCenterLocal - LLViewerCamera::getInstance()->getOrigin())*LLViewerCamera::getInstance()->getAtAxis(); diff --git a/indra/newview/llvotree.cpp b/indra/newview/llvotree.cpp index 2c54d63bee..710509afbb 100644 --- a/indra/newview/llvotree.cpp +++ b/indra/newview/llvotree.cpp @@ -472,9 +472,7 @@ void LLVOTree::updateTextures() { setDebugText(llformat("%4.0f", (F32) sqrt(mPixelArea))); } - mTreeImagep->addTextureStats(mPixelArea); } - } @@ -490,7 +488,7 @@ LLDrawable* LLVOTree::createDrawable(LLPipeline *pipeline) // Just a placeholder for an actual object... LLFace *facep = mDrawable->addFace(poolp, mTreeImagep); facep->setSize(1, 3); - + facep->setTexture(LLRender::DIFFUSE_MAP, mTreeImagep); updateRadius(); return mDrawable; @@ -1180,6 +1178,10 @@ void LLVOTree::updateSpatialExtents(LLVector4a& newMin, LLVector4a& newMax) LLVector4a pos; pos.load3(center.mV); mDrawable->setPositionGroup(pos); + + LLFace* facep = mDrawable->getFace(0); + facep->mExtents[0] = newMin; + facep->mExtents[1] = newMax; } bool LLVOTree::lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end, S32 face, bool pick_transparent, bool pick_rigged, bool pick_unselectable, S32 *face_hitp, diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp index 346c4e31d5..5aa7bd32f6 100644 --- a/indra/newview/llvovolume.cpp +++ b/indra/newview/llvovolume.cpp @@ -6072,18 +6072,23 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group) { continue; } -#if 0 -#if LL_RELEASE_WITH_DEBUG_INFO - const LLUUID pbr_id( "49c88210-7238-2a6b-70ac-92d4f35963cf" ); - const LLUUID obj_id( vobj->getID() ); - bool is_pbr = (obj_id == pbr_id); -#else - bool is_pbr = false; -#endif -#else - LLGLTFMaterial *gltf_mat = facep->getTextureEntry()->getGLTFRenderMaterial(); + + LLFetchedGLTFMaterial *gltf_mat = (LLFetchedGLTFMaterial*) facep->getTextureEntry()->getGLTFRenderMaterial(); bool is_pbr = gltf_mat != nullptr; -#endif + + if (is_pbr) + { + // tell texture streaming system to ignore blinn-phong textures + facep->setTexture(LLRender::DIFFUSE_MAP, nullptr); + facep->setTexture(LLRender::NORMAL_MAP, nullptr); + facep->setTexture(LLRender::SPECULAR_MAP, nullptr); + + // let texture streaming system know about PBR textures + facep->setTexture(LLRender::BASECOLOR_MAP, gltf_mat->mBaseColorTexture); + facep->setTexture(LLRender::GLTF_NORMAL_MAP, gltf_mat->mNormalTexture); + facep->setTexture(LLRender::METALLIC_ROUGHNESS_MAP, gltf_mat->mMetallicRoughnessTexture); + facep->setTexture(LLRender::EMISSIVE_MAP, gltf_mat->mEmissiveTexture); + } //ALWAYS null out vertex buffer on rebuild -- if the face lands in a render // batch, it will recover its vertex buffer reference from the spatial group diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp index b3986eb965..204333953e 100644 --- a/indra/newview/pipeline.cpp +++ b/indra/newview/pipeline.cpp @@ -961,6 +961,10 @@ bool LLPipeline::allocateScreenBuffer(U32 resX, U32 resY, U32 samples) const U32 post_color_fmt = post_hdr ? GL_RGBA16F : GL_RGBA; mPostMap.allocate(resX, resY, post_color_fmt); + // used to scale down textures + // See LLViwerTextureList::updateImagesCreateTextures and LLImageGL::scaleDown + mDownResMap.allocate(4, 4, GL_RGBA); + //HACK make screenbuffer allocations start failing after 30 seconds if (gSavedSettings.getBOOL("SimulateFBOFailure")) { @@ -1221,6 +1225,8 @@ void LLPipeline::releaseGLBuffers() mPostMap.release(); + mDownResMap.release(); + for (U32 i = 0; i < 3; i++) { mGlow[i].release(); diff --git a/indra/newview/pipeline.h b/indra/newview/pipeline.h index 2b9c75a12a..e2ede0b644 100644 --- a/indra/newview/pipeline.h +++ b/indra/newview/pipeline.h @@ -741,6 +741,9 @@ public: // tonemapped and gamma corrected render ready for post LLRenderTarget mPostMap; + // downres scratch space for GPU downscaling of textures + LLRenderTarget mDownResMap; + LLCullResult mSky; LLCullResult mReflectedObjects; LLCullResult mRefractedObjects;