1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533 |
- /**
- * @file llimagegl.cpp
- * @brief Generic GL image handler
- *
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
- #include "linden_common.h"
- #include "llimagegl.h"
- #include "llglslshader.h"
- #include "llgltexture.h"
- #include "llimage.h"
- #include "llmath.h"
- #include "llsys.h" // For LLCPUInfo
- #include "lltimer.h" // For ms_sleep()
- #include "hbtracy.h"
- #include "llvertexbuffer.h" // For wpo2()
- #include "llwindow.h"
- #define FIX_MASKS 1
- // Static members
- U32 LLImageGL::sUniqueCount = 0;
- U32 LLImageGL::sBindCount = 0;
- LLAtomicS64 LLImageGL::sGlobalTexMemBytes(0);
- std::vector<U32> LLImageGL::sFreeList[4];
- U32 LLImageGL::sCurrentFrame = 0;
- S64 LLImageGL::sBoundTexMemBytes = 0;
- S32 LLImageGL::sCount = 0;
- bool LLImageGL::sGlobalUseAnisotropic = false;
- bool LLImageGL::sPreserveDiscard = false;
- bool LLImageGL::sCompressTextures = false;
- bool LLImageGL::sSetSubImagePerLine = false;
- bool LLImageGL::sSyncInThread = true;
- bool LLImageGL::sDelayedTextureDelete = false;
- U32 LLImageGL::sCompressThreshold = 262144U;
- F32 LLImageGL::sLastFrameTime = 0.f;
- LLImageGL* LLImageGL::sDefaultGLImagep = NULL;
- LLImageGL::glimage_list_t LLImageGL::sImageList;
- LLImageGLThread* LLImageGL::sThread = NULL;
- constexpr S8 INVALID_OFFSET = -99;
- constexpr F32 STALE_IMAGES_TIMEOUT = 10.f;
- ///////////////////////////////////////////////////////////////////////////////
- // Helper functions to track bound GL textures allocations.
- static LLMutex sTexMemMutex;
- typedef flat_hmap<U32, S64> alloc_map_t;
- static alloc_map_t sTextureAllocs;
- static LLAtomicS64 sCurBoundTexBytes(0);
- void image_bound(U32 width, U32 height, U32 pixformat, U32 count = 1,
- U32 tex_name = 0)
- {
- if (!tex_name)
- {
- tex_name =
- gGL.getTexUnit(gGL.getCurrentTexUnitIndex())->getCurrTexture();
- }
- if (!tex_name)
- {
- return;
- }
- S64 new_size = S64(count) *
- LLImageGL::dataFormatBytes(pixformat, width, height);
- S64 old_size = 0;
- sTexMemMutex.lock();
- alloc_map_t::iterator it = sTextureAllocs.find(tex_name);
- if (it == sTextureAllocs.end())
- {
- sTextureAllocs[tex_name] = new_size;
- }
- else
- {
- old_size = it->second;
- it->second = new_size;
- }
- sCurBoundTexBytes += new_size - old_size;
- sTexMemMutex.unlock();
- }
- void image_unbound(U32 tex_name)
- {
- if (tex_name)
- {
- S64 size = 0;
- sTexMemMutex.lock();
- alloc_map_t::iterator it = sTextureAllocs.find(tex_name);
- if (it != sTextureAllocs.end())
- {
- size = it->second;
- sTextureAllocs.erase(it);
- }
- sCurBoundTexBytes -= size;
- sTexMemMutex.unlock();
- }
- }
- ///////////////////////////////////////////////////////////////////////////////
- //static
- U32 LLImageGL::dataFormatBits(S32 dataformat)
- {
- switch (dataformat)
- {
- case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: return 4;
- case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT: return 4;
- case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: return 8;
- case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT: return 8;
- case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: return 8;
- case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT: return 8;
- case GL_LUMINANCE: return 8;
- case GL_ALPHA: return 8;
- case GL_COLOR_INDEX: return 8;
- case GL_LUMINANCE_ALPHA: return 16;
- case GL_RED: return 8;
- case GL_RG: return 16;
- case GL_RGB: return 24;
- case GL_SRGB: return 24;
- case GL_RGB8: return 24;
- case GL_RGB16F: return 48;
- case GL_RGBA: return 32;
- case GL_RGBA8: return 32;
- case GL_RGBA16F: return 64;
- case GL_SRGB_ALPHA: return 32;
- // Used for QuickTime media textures on the Mac:
- case GL_BGRA: return 32;
- case GL_DEPTH_COMPONENT: return 24;
- default:
- llerrs << "Unknown format: " << dataformat << llendl;
- return 0;
- }
- }
- //static
- S64 LLImageGL::dataFormatBytes(S32 dataformat, U32 width, U32 height)
- {
- if (width < 4 || height < 4)
- {
- if (dataformat == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT ||
- dataformat == GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT ||
- dataformat == GL_COMPRESSED_RGBA_S3TC_DXT3_EXT ||
- dataformat == GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT ||
- dataformat == GL_COMPRESSED_RGBA_S3TC_DXT5_EXT ||
- dataformat == GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT)
- {
- width = llmax(width, 4);
- height = llmax(height, 4);
- }
- }
- S64 bytes = ((S64)width * (S64)height *
- (S64)dataFormatBits(dataformat) + 7) >> 3;
- return (bytes + 3) & ~3L; // Keep it a multiple of 4 bytes
- }
- //static
- U32 LLImageGL::dataFormatComponents(S32 dataformat)
- {
- switch (dataformat)
- {
- case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: return 3;
- case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT: return 3;
- case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: return 4;
- case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT: return 4;
- case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: return 4;
- case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT: return 4;
- case GL_LUMINANCE: return 1;
- case GL_ALPHA: return 1;
- case GL_COLOR_INDEX: return 1;
- case GL_LUMINANCE_ALPHA: return 2;
- case GL_RED: return 1;
- case GL_RG: return 2;
- case GL_RGB: return 3;
- case GL_SRGB: return 3;
- case GL_RGBA: return 4;
- case GL_SRGB_ALPHA: return 4;
- // Used for QuickTime media textures on the Mac
- case GL_BGRA: return 4;
- default:
- llerrs << "Unknown format: " << dataformat << llendl;
- return 0;
- }
- }
- bool LLImageGL::isCompressed() const
- {
- switch (mFormatPrimary)
- {
- case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
- case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT:
- case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
- case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT:
- case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
- case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT:
- return true;
- default:
- return false;
- }
- }
- //static
- void LLImageGL::destroyGL(bool save_state)
- {
- // Release all textures pending for deletion. HB
- freeDeletedTextures(true);
- for (S32 stage = 0; stage < gGLManager.mNumTextureImageUnits; ++stage)
- {
- gGL.getTexUnit(stage)->unbind(LLTexUnit::TT_TEXTURE);
- }
- for (glimage_list_t::iterator iter = sImageList.begin(),
- end = sImageList.end();
- iter != end; ++iter)
- {
- LLImageGL* imagep = *iter;
- imagep->syncTexName();
- if (imagep->mTexName)
- {
- if (save_state && imagep->isGLTextureCreated() &&
- imagep->mComponents)
- {
- imagep->mSaveData = new LLImageRaw;
- if (!imagep->readBackRaw(imagep->mCurrentDiscardLevel,
- imagep->mSaveData, false))
- {
- imagep->mSaveData = NULL;
- }
- }
- imagep->destroyGLTexture();
- stop_glerror();
- }
- }
- }
- //static
- void LLImageGL::restoreGL()
- {
- for (glimage_list_t::iterator iter = sImageList.begin(),
- end = sImageList.end();
- iter != end; ++iter)
- {
- LLImageGL* imagep = *iter;
- if (imagep->getTexName())
- {
- llwarns << "Tex name is not 0." << llendl;
- }
- if (imagep->mSaveData.notNull())
- {
- if (imagep->getComponents() &&
- imagep->mSaveData->getComponents())
- {
- imagep->createGLTexture(imagep->mCurrentDiscardLevel,
- imagep->mSaveData, 0, true);
- stop_glerror();
- }
- imagep->mSaveData = NULL; // Deletes data
- }
- }
- }
- //static
- void LLImageGL::dirtyTexOptions()
- {
- for (glimage_list_t::iterator iter = sImageList.begin(),
- end = sImageList.end();
- iter != end; ++iter)
- {
- LLImageGL* imagep = *iter;
- imagep->mTexOptionsDirty = true;
- }
- }
- // This method is used to allow releasing old NO_DELETE fetched textures which
- // associated GL image have not been used for rendering in a while. It only
- // affects fetched textures, and not the ones that must be kept untouched or
- // the "manual"/local GL textures (UI textures, bump maps, map tiles, media
- // textures, preview textures, etc). Without a periodic call to this method,
- // the viewer "leaks" (keeps uselessly) GL textures around, which clobber the
- // RAM, and worst, the VRAM. HB
- //static
- U32 LLImageGL::activateStaleTextures()
- {
- U32 activated = 0;
- for (glimage_list_t::iterator iter = sImageList.begin(),
- end = sImageList.end();
- iter != end; ++iter)
- {
- LLImageGL* imagep = *iter;
- // If this image has been recently bound, then it is not a candidate.
- if (sLastFrameTime - imagep->mLastBindTime < STALE_IMAGES_TIMEOUT)
- {
- continue;
- }
- LLGLTexture* ownerp = imagep->getOwner();
- // We only care about fetched textures, i.e. the ones with an owner.
- if (!ownerp)
- {
- continue;
- }
- U32 boost_level = ownerp->getBoostLevel();
- // Do not touch avatar bakes, sculpties, UI, map, preview, bumps, media
- // textures...
- if (boost_level <= LLGLTexture::BOOST_SUPER_HIGH &&
- boost_level != LLGLTexture::BOOST_SCULPTED &&
- // And only textures in NO_DELETE state need to be re-ACTIVATEd
- ownerp->getTextureState() == LLGLTexture::NO_DELETE)
- {
- ownerp->forceActive();
- ++activated;
- }
- }
- return activated;
- }
- LLImageGL::LLImageGL(bool usemipmaps)
- : mOwnerp(NULL),
- mSaveData(NULL),
- mAutoGenMips(false)
- {
- init(usemipmaps);
- setSize(0, 0, 0);
- sImageList.insert(this);
- ++sCount;
- }
- LLImageGL::LLImageGL(U32 width, U32 height, U8 components, bool usemipmaps)
- : mOwnerp(NULL),
- mSaveData(NULL),
- mAutoGenMips(false)
- {
- llassert(components <= 4);
- init(usemipmaps);
- setSize(width, height, components);
- sImageList.insert(this);
- ++sCount;
- }
- LLImageGL::LLImageGL(const LLImageRaw* imagerawp, bool usemipmaps)
- : mOwnerp(NULL),
- mSaveData(NULL),
- mAutoGenMips(false)
- {
- init(usemipmaps);
- setSize(0, 0, 0);
- sImageList.insert(this);
- ++sCount;
- createGLTexture(0, imagerawp);
- }
- LLImageGL::LLImageGL(U32 tex_name, U32 components, U32 target,
- S32 internal_fmt, S32 primary_fmt, S32 format_type,
- LLTexUnit::eTextureAddressMode mode)
- : mOwnerp(NULL),
- mSaveData(NULL),
- mAutoGenMips(false)
- {
- init(false);
- mTexName = tex_name;
- mComponents = components;
- mTarget = target;
- mFormatInternal = internal_fmt;
- mFormatPrimary = primary_fmt;
- mFormatType = format_type;
- mAddressMode = mode;
- }
- LLImageGL::~LLImageGL()
- {
- syncTexName();
- sImageList.erase(this);
- --sCount;
- cleanup();
- }
- void LLImageGL::init(bool usemipmaps)
- {
- mNewTexName = 0;
- mTexNameDirty = false;
- mTexNameSync = 0;
- mTextureMemory = 0;
- mLastBindTime = 0.f;
- mPickMask = NULL;
- mPickMaskWidth = 0;
- mPickMaskHeight = 0;
- mUseMipMaps = usemipmaps;
- mHasExplicitFormat = false;
- mIsMask = false;
- #if FIX_MASKS
- mNeedsAlphaAndPickMask = false;
- mAlphaOffset = INVALID_OFFSET;
- #else
- mNeedsAlphaAndPickMask = true;
- mAlphaOffset = 0;
- #endif
- mAlphaStride = 0;
- mGLTextureCreated = false;
- mTexName = 0;
- mWidth = 0;
- mHeight = 0;
- mCurrentDiscardLevel = -1;
- mAllowCompression = true;
- mTarget = GL_TEXTURE_2D;
- mBindTarget = LLTexUnit::TT_TEXTURE;
- mHasMipMaps = false;
- mMipLevels = -1;
- mComponents = 0;
- mMaxDiscardLevel = MAX_DISCARD_LEVEL;
- mTexOptionsDirty = true;
- mAddressMode = LLTexUnit::TAM_WRAP;
- mFilterOption = LLTexUnit::TFO_ANISOTROPIC;
- mFormatInternal = -1;
- mFormatPrimary = 0;
- mFormatType = GL_UNSIGNED_BYTE;
- mFormatSwapBytes = false;
- }
- void LLImageGL::cleanup()
- {
- if (!gGLManager.mIsDisabled)
- {
- destroyGLTexture();
- }
- freePickMask();
- mSaveData = NULL; // Delete data
- }
- // Helper function used to check the size of a texture image. So dim should be
- // a positive number
- LL_INLINE static bool check_power_of_two(S32 dim)
- {
- return !dim || (dim > 0 && !(dim & (dim - 1)));
- }
- //static
- bool LLImageGL::checkSize(S32 width, S32 height)
- {
- return check_power_of_two(width) && check_power_of_two(height);
- }
- bool LLImageGL::setSize(U32 width, U32 height, U8 ncomponents,
- S32 discard_level)
- {
- if (width != mWidth || height != mHeight || ncomponents != mComponents)
- {
- // Check if dimensions are a power of two
- if (!checkSize(width, height))
- {
- llwarns << "Texture has non power of two dimension: " << width
- << "x" << height << ". Aborted." << llendl;
- return false;
- }
- // Pick mask validity depends on old image size, delete it
- freePickMask();
- mWidth = width;
- mHeight = height;
- mComponents = ncomponents;
- if (ncomponents > 0)
- {
- mMaxDiscardLevel = 0;
- while (width > 1 && height > 1 &&
- mMaxDiscardLevel < MAX_DISCARD_LEVEL)
- {
- ++mMaxDiscardLevel;
- width >>= 1;
- height >>= 1;
- }
- if (!sPreserveDiscard && discard_level > 0)
- {
- mMaxDiscardLevel = llmax(mMaxDiscardLevel, (S8)discard_level);
- }
- }
- else
- {
- mMaxDiscardLevel = MAX_DISCARD_LEVEL;
- }
- }
- return true;
- }
- //virtual
- void LLImageGL::dump()
- {
- llinfos << "mMaxDiscardLevel = " << S32(mMaxDiscardLevel)
- << " - mLastBindTime = " << mLastBindTime
- << " - mTarget = " << S32(mTarget)
- << " - mBindTarget = " << S32(mBindTarget)
- << " - mUseMipMaps = " << S32(mUseMipMaps)
- << " - mHasMipMaps = " << S32(mHasMipMaps)
- << " - mCurrentDiscardLevel = " << S32(mCurrentDiscardLevel)
- << " - mFormatInternal = " << S32(mFormatInternal)
- << " - mFormatPrimary = " << S32(mFormatPrimary)
- << " - mFormatType = " << S32(mFormatType)
- << " - mFormatSwapBytes = " << S32(mFormatSwapBytes)
- << " - mHasExplicitFormat = " << S32(mHasExplicitFormat)
- << " - mTextureMemory = " << mTextureMemory
- << " - mTexName = " << mTexName
- << llendl;
- }
- //static
- void LLImageGL::dumpStaleList()
- {
- U32 num_stale_images = 0;
- S64 total_stale_memory = 0;
- U32 num_scuplties = 0;
- S64 scuplties_memory = 0;
- for (glimage_list_t::iterator iter = sImageList.begin(),
- end = sImageList.end();
- iter != end; ++iter)
- {
- LLImageGL* imagep = *iter;
- if (sLastFrameTime - imagep->mLastBindTime < STALE_IMAGES_TIMEOUT)
- {
- continue;
- }
- LLGLTexture* ownerp = imagep->getOwner();
- U32 boost_level = ownerp ? ownerp->getBoostLevel() : 0;
- if (boost_level == LLGLTexture::BOOST_SCULPTED)
- {
- scuplties_memory += imagep->mTextureMemory;
- ++num_scuplties;
- }
- else if (boost_level <= LLGLTexture::BOOST_SUPER_HIGH)
- {
- llinfos << "Image " << std::hex << (intptr_t)imagep << std::dec;
- if (boost_level >= 0)
- {
- llcont << " with boost level " << boost_level;
- }
- else
- {
- llcont << " not owned by a fetched texture";
- }
- llcont << ":" << llendl;
- imagep->dump();
- total_stale_memory += imagep->mTextureMemory;
- ++num_stale_images;
- }
- }
- llinfos << "Total number of sculpt textures: " << num_scuplties
- << " (using " << scuplties_memory / 1024
- << "KB) - Total number of stale images: " << num_stale_images
- << " - Total leaked memory: " << total_stale_memory / 1024 << "KB."
- << llendl;
- }
- //static
- void LLImageGL::freeDeletedTextures(bool delete_all)
- {
- LL_TRACY_TIMER(TRC_FREE_DELETED_IMAGE);
- U32 idx = 0;
- if (!delete_all)
- {
- ++sCurrentFrame;
- idx = (sCurrentFrame + 3) % 4;
- }
- do
- {
- if (!sFreeList[idx].empty())
- {
- std::vector<U32>& textures = sFreeList[idx];
- U32 num_textures = textures.size();
- if (gGLManager.mInited)
- {
- glDeleteTextures(num_textures, textures.data());
- }
- for (U32 i = 0; i < num_textures; ++i)
- {
- image_unbound(textures[i]);
- }
- textures.clear();
- }
- }
- while (delete_all && ++idx < 4);
- if (sCurBoundTexBytes < 0 || gDebugGL)
- {
- sTexMemMutex.lock();
- S64 total = 0;
- for (alloc_map_t::iterator it = sTextureAllocs.begin(),
- end = sTextureAllocs.end();
- it != end; ++it)
- {
- total += it->second;
- }
- if (total != sCurBoundTexBytes)
- {
- llwarns << "Bound textures accounting mismatch: "
- << sCurBoundTexBytes << ", against: " << total
- << ". Resynced." << llendl;
- sCurBoundTexBytes = total;
- }
- sTexMemMutex.unlock();
- }
- }
- //static
- void LLImageGL::updateStats(F32 current_time)
- {
- sLastFrameTime = current_time;
- sBoundTexMemBytes = sCurBoundTexBytes;
- }
- bool LLImageGL::updateBindStats() const
- {
- const_cast<LLImageGL*>(this)->syncTexName();
- if (mTexName)
- {
- ++sBindCount;
- if (mLastBindTime != sLastFrameTime)
- {
- // We have not accounted for this texture yet this frame
- ++sUniqueCount;
- mLastBindTime = sLastFrameTime;
- return true;
- }
- }
- return false;
- }
- void LLImageGL::setExplicitFormat(S32 internal_format, U32 primary_format,
- U32 type_format, bool swap_bytes)
- {
- // Notes:
- // - Must be called before createTexture()
- // - It is up to the caller to ensure that the format matches the number
- // of components.
- mHasExplicitFormat = true;
- mFormatInternal = internal_format;
- mFormatPrimary = primary_format;
- mFormatType = type_format == 0 ? GL_UNSIGNED_BYTE : type_format;
- mFormatSwapBytes = swap_bytes;
- calcAlphaChannelOffsetAndStride();
- }
- void LLImageGL::setImage(const LLImageRaw* imagerawp)
- {
- llassert(imagerawp->getWidth() == getWidth(mCurrentDiscardLevel) &&
- imagerawp->getHeight() == getHeight(mCurrentDiscardLevel) &&
- imagerawp->getComponents() == getComponents());
- const U8* rawdatap = imagerawp->getData();
- setImage(rawdatap, false);
- }
- bool LLImageGL::setImage(const U8* data_in, bool data_hasmips, S32 usename)
- {
- LL_TRACY_TIMER(TRC_SET_IMAGE);
- bool is_compressed = isCompressed();
- LLTexUnit* unit0 = gGL.getTexUnit(0);
- if (mUseMipMaps)
- {
- // Set has mip maps to true before binding image so tex parameters get
- // set properly
- unit0->unbind(mBindTarget);
- mHasMipMaps = true;
- mTexOptionsDirty = true;
- setFilteringOption(LLTexUnit::TFO_ANISOTROPIC);
- }
- else
- {
- mHasMipMaps = false;
- }
- unit0->bind(this, false, usename);
- if (!data_in)
- {
- setManualImage(mTarget, 0, mFormatInternal, getWidth(), getHeight(),
- mFormatPrimary, mFormatType, (void*)data_in,
- mAllowCompression);
- }
- else if (mUseMipMaps)
- {
- if (data_hasmips)
- {
- // NOTE: data_in points to largest image; smaller images are stored
- // BEFORE the largest image
- for (S32 d = mCurrentDiscardLevel; d <= mMaxDiscardLevel; ++d)
- {
- U32 w = getWidth(d);
- U32 h = getHeight(d);
- S32 gl_level = d - mCurrentDiscardLevel;
- mMipLevels = llmax(mMipLevels, gl_level);
- if (d > mCurrentDiscardLevel)
- {
- // See above comment
- data_in -= dataFormatBytes(mFormatPrimary, w, h);
- }
- if (is_compressed)
- {
- U32 tex_size = (U32)dataFormatBytes(mFormatPrimary, w, h);
- glCompressedTexImage2D(mTarget, gl_level, mFormatPrimary,
- w, h, 0, tex_size,
- (GLvoid*)data_in);
- stop_glerror();
- }
- else
- {
- if (mFormatSwapBytes)
- {
- glPixelStorei(GL_UNPACK_SWAP_BYTES, 1);
- stop_glerror();
- }
- setManualImage(mTarget, gl_level, mFormatInternal, w, h,
- mFormatPrimary, GL_UNSIGNED_BYTE,
- (GLvoid*)data_in, mAllowCompression);
- if (gl_level == 0)
- {
- analyzeAlpha(data_in, w, h);
- }
- updatePickMask(w, h, data_in);
- if (mFormatSwapBytes)
- {
- glPixelStorei(GL_UNPACK_SWAP_BYTES, 0);
- stop_glerror();
- }
- }
- }
- }
- else if (!is_compressed)
- {
- if (mAutoGenMips)
- {
- if (mFormatSwapBytes)
- {
- glPixelStorei(GL_UNPACK_SWAP_BYTES, 1);
- stop_glerror();
- }
- U32 w = getWidth(mCurrentDiscardLevel);
- U32 h = getHeight(mCurrentDiscardLevel);
- mMipLevels = wpo2(llmax(w, h));
- // Use legacy mipmap generation mode only when core profile is
- // not enabled (to avoid deprecation warnings), or GL version
- // is below 3.0 (to avoid rendering issues).
- bool use_legacy_mimap = !LLRender::sGLCoreProfile ||
- gGLManager.mGLVersion < 3.f;
- if (use_legacy_mimap)
- {
- glTexParameteri(mTarget, GL_GENERATE_MIPMAP, GL_TRUE);
- }
- setManualImage(mTarget, 0, mFormatInternal, w, h,
- mFormatPrimary, mFormatType, data_in,
- mAllowCompression);
- analyzeAlpha(data_in, w, h);
- updatePickMask(w, h, data_in);
- if (mFormatSwapBytes)
- {
- glPixelStorei(GL_UNPACK_SWAP_BYTES, 0);
- stop_glerror();
- }
- if (!use_legacy_mimap)
- {
- glGenerateMipmap(mTarget);
- stop_glerror();
- }
- }
- else
- {
- // Create mips by hand
- // ~4x faster than gluBuild2DMipmaps
- U32 width = getWidth(mCurrentDiscardLevel);
- U32 height = getHeight(mCurrentDiscardLevel);
- U32 nummips = mMaxDiscardLevel - mCurrentDiscardLevel + 1;
- U32 w = width;
- U32 h = height;
- U8* prev_mip_data = NULL;
- U8* cur_mip_data = NULL;
- mMipLevels = nummips;
- for (U32 m = 0; m < nummips; ++m)
- {
- if (m == 0)
- {
- cur_mip_data = const_cast<U8*>(data_in);
- }
- else
- {
- S32 bytes = w * h * mComponents;
- llassert(prev_mip_data);
- U8* new_data = new (std::nothrow) U8[bytes];
- if (!new_data)
- {
- if (prev_mip_data)
- {
- delete[] prev_mip_data;
- }
- if (cur_mip_data && cur_mip_data != prev_mip_data)
- {
- delete[] cur_mip_data;
- }
- mGLTextureCreated = false;
- return false;
- }
- LLImageBase::generateMip(prev_mip_data, new_data, w, h,
- mComponents);
- cur_mip_data = new_data;
- }
- if (w > 0 && h > 0 && cur_mip_data)
- {
- if (mFormatSwapBytes)
- {
- glPixelStorei(GL_UNPACK_SWAP_BYTES, 1);
- stop_glerror();
- }
- setManualImage(mTarget, m, mFormatInternal, w, h,
- mFormatPrimary, mFormatType,
- cur_mip_data, mAllowCompression);
- if (m == 0)
- {
- analyzeAlpha(data_in, w, h);
- updatePickMask(w, h, cur_mip_data);
- }
- if (mFormatSwapBytes)
- {
- glPixelStorei(GL_UNPACK_SWAP_BYTES, 0);
- stop_glerror();
- }
- }
- else
- {
- llassert(false);
- }
- if (prev_mip_data && prev_mip_data != data_in)
- {
- delete[] prev_mip_data;
- }
- prev_mip_data = cur_mip_data;
- w >>= 1;
- h >>= 1;
- }
- if (prev_mip_data && prev_mip_data != data_in)
- {
- delete[] prev_mip_data;
- prev_mip_data = NULL;
- }
- }
- }
- else
- {
- llerrs << "Compressed Image has mipmaps but data does not (can not auto generate compressed mips)"
- << llendl;
- }
- }
- else
- {
- mMipLevels = 0;
- U32 w = getWidth();
- U32 h = getHeight();
- if (is_compressed)
- {
- U32 tex_size = (U32)dataFormatBytes(mFormatPrimary, w, h);
- glCompressedTexImage2D(mTarget, 0, mFormatPrimary, w, h, 0,
- tex_size, (GLvoid*)data_in);
- stop_glerror();
- }
- else
- {
- if (mFormatSwapBytes)
- {
- glPixelStorei(GL_UNPACK_SWAP_BYTES, 1);
- stop_glerror();
- }
- setManualImage(mTarget, 0, mFormatInternal, w, h, mFormatPrimary,
- mFormatType, (GLvoid*)data_in, mAllowCompression);
- analyzeAlpha(data_in, w, h);
- updatePickMask(w, h, data_in);
- if (mFormatSwapBytes)
- {
- glPixelStorei(GL_UNPACK_SWAP_BYTES, 0);
- stop_glerror();
- }
- }
- }
- mGLTextureCreated = true;
- return true;
- }
- #if !LL_DARWIN
- // Breaks up glTexSubImage2D() calls to a manageable size for the GL command
- // buffer.
- static void sub_image_lines(U32 target, S32 miplevel, S32 x_offset, S32 y_offset,
- U32 width, U32 height, U32 pixformat, U32 pixtype,
- const U8* srcp, U32 data_width)
- {
- U32 components = LLImageGL::dataFormatComponents(pixformat);
- U32 type_width = 4;
- switch (pixtype)
- {
- case GL_UNSIGNED_BYTE:
- case GL_BYTE:
- case GL_UNSIGNED_INT_8_8_8_8_REV:
- type_width = 1;
- break;
- case GL_UNSIGNED_SHORT:
- case GL_SHORT:
- type_width = 2;
- break;
- case GL_UNSIGNED_INT:
- case GL_INT:
- case GL_FLOAT:
- type_width = 4;
- break;
- default:
- type_width = 1;
- llwarns << "Unknown type: " << pixtype << llendl;
- }
- U32 line_width = data_width * components * type_width;
- U32 y_offset_end = y_offset + height;
- for (U32 y_pos = y_offset; y_pos < y_offset_end; ++y_pos)
- {
- glTexSubImage2D(target, miplevel, x_offset, y_pos, width, 1, pixformat,
- pixtype, srcp);
- srcp += line_width;
- }
- }
- #endif
- bool LLImageGL::setSubImage(const U8* datap, U32 data_width, U32 data_height,
- S32 x_pos, S32 y_pos, U32 width, U32 height,
- bool force_fast_update, U32 use_name)
- {
- if (!width || !height)
- {
- return true;
- }
- syncTexName();
- if (use_name == 0)
- {
- use_name = mTexName;
- }
- if (!datap || use_name == 0)
- {
- // *TODO: Re-add warning ? Ran into thread locking issues ? - DK
- return false;
- }
- // *HACK: allow the caller to explicitly force the fast path (i.e. using
- // glTexSubImage2D here instead of calling setImage) even when updating the
- // full texture.
- if (!force_fast_update && x_pos == 0 && y_pos == 0 &&
- data_width == width && data_height == height &&
- width == getWidth() && height == getHeight())
- {
- setImage(datap, false, use_name);
- }
- else
- {
- if (mUseMipMaps)
- {
- dump();
- llerrs << "Called with mipmapped image (not supported)" << llendl;
- }
- llassert_always(mCurrentDiscardLevel == 0 && x_pos >= 0 && y_pos >= 0);
- if (U32(x_pos + width) > getWidth() ||
- U32(y_pos + height) > getHeight())
- {
- dump();
- llerrs << "Subimage not wholly in target image !"
- << " x_pos " << x_pos
- << " y_pos " << y_pos
- << " width " << width
- << " height " << height
- << " getWidth() " << getWidth()
- << " getHeight() " << getHeight()
- << llendl;
- }
- if (U32(x_pos + width) > data_width ||
- U32(y_pos + height) > data_height)
- {
- dump();
- llerrs << "Subimage not wholly in source image !"
- << " x_pos " << x_pos
- << " y_pos " << y_pos
- << " width " << width
- << " height " << height
- << " source_width " << data_width
- << " source_height " << data_height
- << llendl;
- }
- glPixelStorei(GL_UNPACK_ROW_LENGTH, data_width);
- stop_glerror();
- if (mFormatSwapBytes)
- {
- glPixelStorei(GL_UNPACK_SWAP_BYTES, 1);
- stop_glerror();
- }
- datap += (y_pos * data_width + x_pos) * getComponents();
- // Update the GL texture
- LLTexUnit* unit0 = gGL.getTexUnit(0);
- bool res = unit0->bindManual(mBindTarget, use_name);
- if (!res)
- {
- llerrs << "gGL.getTexUnit(0)->bindManual() failed" << llendl;
- }
- #if !LL_DARWIN
- if (sSetSubImagePerLine && !isCompressed() && is_main_thread())
- {
- sub_image_lines(mTarget, 0, x_pos, y_pos, width, height,
- mFormatPrimary, mFormatType, datap, data_width);
- }
- else
- #endif
- {
- glTexSubImage2D(mTarget, 0, x_pos, y_pos,
- width, height, mFormatPrimary, mFormatType, datap);
- }
- unit0->disable();
- stop_glerror();
- if (mFormatSwapBytes)
- {
- glPixelStorei(GL_UNPACK_SWAP_BYTES, 0);
- stop_glerror();
- }
- glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
- stop_glerror();
- mGLTextureCreated = true;
- }
- return true;
- }
- bool LLImageGL::setSubImage(const LLImageRaw* imagerawp, S32 x_pos, S32 y_pos,
- U32 width, U32 height, bool force_fast_update,
- U32 use_name)
- {
- return setSubImage(imagerawp->getData(), imagerawp->getWidth(),
- imagerawp->getHeight(), x_pos, y_pos, width, height,
- force_fast_update, use_name);
- }
- // Copy sub image from frame buffer
- bool LLImageGL::setSubImageFromFrameBuffer(S32 fb_x, S32 fb_y, S32 x_pos,
- S32 y_pos, U32 width, U32 height)
- {
- if (gGL.getTexUnit(0)->bind(this, true))
- {
- glCopyTexSubImage2D(GL_TEXTURE_2D, 0, fb_x, fb_y, x_pos, y_pos, width,
- height);
- stop_glerror();
- mGLTextureCreated = true;
- return true;
- }
- return false;
- }
- //static
- void LLImageGL::generateTextures(U32 num_textures, U32* textures)
- {
- LL_TRACY_TIMER(TRC_GENERATE_TEXTURES);
- for (U32 i = 0; i < num_textures; ++i)
- {
- image_unbound(textures[i]);
- }
- constexpr size_t pool_size = 1024;
- thread_local U32 name_pool[pool_size]; // Pool of texture names
- thread_local U32 name_count = 0; // Available names in pool
- if (!name_count)
- {
- glGenTextures(pool_size, name_pool);
- name_count = pool_size;
- }
- if (num_textures < name_count)
- {
- memcpy((void*)textures, (void*)(name_pool + name_count - num_textures),
- sizeof(U32) * num_textures);
- name_count -= num_textures;
- }
- else
- {
- glGenTextures(num_textures, textures);
- }
- }
- //static
- void LLImageGL::deleteTextures(U32 num_textures, const U32* textures)
- {
- LL_TRACY_TIMER(TRC_DELETE_IMAGE);
- if (gGLManager.mInited)
- {
- if (sDelayedTextureDelete)
- {
- U32 idx = sCurrentFrame % 4;
- for (U32 i = 0; i < num_textures; ++i)
- {
- sFreeList[idx].push_back(textures[i]);
- }
- }
- else
- {
- glDeleteTextures(num_textures,textures);
- for (U32 i = 0; i < num_textures; ++i)
- {
- image_unbound(textures[i]);
- }
- }
- }
- }
- //static
- void LLImageGL::setManualImage(U32 target, S32 miplevel, S32 intformat,
- U32 width, U32 height, U32 pixformat,
- U32 pixtype, const void* pixels,
- bool allow_compression)
- {
- LL_TRACY_TIMER(TRC_SET_MANUAL_IMAGE);
- const U32 pixels_count = width * height;
- if (LLRender::sGLCoreProfile)
- {
- #if GL_ARB_texture_swizzle
- if (gGLManager.mHasTextureSwizzle)
- {
- if (pixformat == GL_ALPHA)
- {
- // GL_ALPHA is deprecated, convert to RGBA
- const GLint mask[] = { GL_ZERO, GL_ZERO, GL_ZERO, GL_RED };
- glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, mask);
- pixformat = GL_RED;
- intformat = GL_R8;
- }
- if (pixformat == GL_LUMINANCE)
- {
- // GL_LUMINANCE is deprecated, convert to RGBA
- const GLint mask[] = { GL_RED, GL_RED, GL_RED, GL_ONE };
- glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, mask);
- pixformat = GL_RED;
- intformat = GL_R8;
- }
- if (pixformat == GL_LUMINANCE_ALPHA)
- {
- // GL_LUMINANCE_ALPHA is deprecated, convert to RGBA
- const GLint mask[] = { GL_RED, GL_RED, GL_RED, GL_GREEN };
- glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, mask);
- pixformat = GL_RG;
- intformat = GL_RG8;
- }
- }
- else
- #endif
- {
- thread_local std::vector<U32> scratch;
- if (pixformat == GL_ALPHA && pixtype == GL_UNSIGNED_BYTE)
- {
- if (pixels)
- {
- // GL_ALPHA is deprecated, convert to RGBA
- scratch.reserve(pixels_count);
- for (U32 i = 0; i < pixels_count; ++i)
- {
- U8* pix = (U8*)&scratch[i];
- pix[0] = pix[1] = pix[2] = 0;
- pix[3] = ((U8*)pixels)[i];
- }
- pixels = (void*)scratch.data();
- }
- pixformat = GL_RGBA;
- intformat = GL_RGBA8;
- }
- if (pixformat == GL_LUMINANCE_ALPHA && pixtype == GL_UNSIGNED_BYTE)
- {
- // GL_LUMINANCE_ALPHA is deprecated, convert to RGBA
- if (pixels)
- {
- scratch.reserve(pixels_count);
- for (U32 i = 0; i < pixels_count; ++i)
- {
- U8 lum = ((U8*)pixels)[2 * i];
- U8 alpha = ((U8*)pixels)[2 * i + 1];
- U8* pix = (U8*)&scratch[i];
- pix[0] = pix[1] = pix[2] = lum;
- pix[3] = alpha;
- }
- pixels = (void*)scratch.data();
- }
- pixformat = GL_RGBA;
- intformat = GL_RGBA8;
- }
- if (pixformat == GL_LUMINANCE && pixtype == GL_UNSIGNED_BYTE)
- {
- // GL_LUMINANCE is deprecated, convert to RGBA
- if (pixels)
- {
- scratch.reserve(pixels_count);
- for (U32 i = 0; i < pixels_count; ++i)
- {
- U8 lum = ((U8*)pixels)[i];
- U8* pix = (U8*)&scratch[i];
- pix[0] = pix[1] = pix[2] = lum;
- pix[3] = 255;
- }
- pixels = (void*)scratch.data();
- }
- pixformat = GL_RGBA;
- intformat = GL_RGB8;
- }
- }
- }
- bool compress = allow_compression && sCompressTextures &&
- pixels_count > sCompressThreshold;
- if (compress)
- {
- switch (intformat)
- {
- case GL_RED:
- case GL_R8:
- intformat = GL_COMPRESSED_RED;
- break;
- case GL_RG:
- case GL_RG8:
- intformat = GL_COMPRESSED_RG;
- break;
- case GL_RGB:
- case GL_RGB8:
- intformat = GL_COMPRESSED_RGB;
- break;
- case GL_SRGB:
- case GL_SRGB8:
- intformat = GL_COMPRESSED_SRGB;
- break;
- case GL_RGBA:
- case GL_RGBA8:
- intformat = GL_COMPRESSED_RGBA;
- break;
- case GL_SRGB_ALPHA:
- case GL_SRGB8_ALPHA8:
- intformat = GL_COMPRESSED_SRGB_ALPHA;
- break;
- case GL_LUMINANCE:
- case GL_LUMINANCE8:
- intformat = GL_COMPRESSED_LUMINANCE;
- break;
- case GL_LUMINANCE_ALPHA:
- case GL_LUMINANCE8_ALPHA8:
- intformat = GL_COMPRESSED_LUMINANCE_ALPHA;
- break;
- case GL_ALPHA:
- case GL_ALPHA8:
- intformat = GL_COMPRESSED_ALPHA;
- break;
- default:
- llwarns_once << "Could not compress format: " << std::hex
- << intformat << std::dec << llendl;
- }
- }
- #if !LL_DARWIN
- if (sSetSubImagePerLine && !compress && is_main_thread())
- {
- glTexImage2D(target, miplevel, intformat, width, height, 0, pixformat,
- pixtype, NULL);
- if (pixels)
- {
- sub_image_lines(target, miplevel, 0, 0, width, height, pixformat,
- pixtype, (U8*)pixels, width);
- }
- }
- else
- #endif
- {
- glTexImage2D(target, miplevel, intformat, width, height, 0, pixformat,
- pixtype, pixels);
- }
- image_bound(width, height, pixformat);
- stop_glerror();
- }
- // Create an empty GL texture: just create a texture name. The texture is
- // associated with some image by calling glTexImage outside LLImageGL.
- bool LLImageGL::createGLTexture()
- {
- LL_TRACY_TIMER(TRC_CREATE_GL_TEXTURE1);
- if (gGLManager.mIsDisabled)
- {
- llwarns << "Trying to create a texture while GL is disabled !"
- << llendl;
- return false;
- }
- // Do not save this texture when gl is destroyed.
- mGLTextureCreated = false;
- llassert(gGLManager.mInited);
- stop_glerror();
- syncTexName();
- if (mTexName)
- {
- deleteTextures(1, &mTexName);
- mTexName = 0;
- }
- generateTextures(1, &mTexName);
- stop_glerror();
- if (!mTexName)
- {
- llwarns << "Failed to make an empty texture" << llendl;
- return false;
- }
- return true;
- }
- bool LLImageGL::createGLTexture(S32 discard_level, const LLImageRaw* imagerawp,
- S32 usename, bool to_create, bool defer_copy,
- U32* tex_name)
- {
- LL_TRACY_TIMER(TRC_CREATE_GL_TEXTURE2);
- if (gGLManager.mIsDisabled || !gGLManager.mInited)
- {
- llwarns << "Trying to create a texture while GL is disabled or not initialized !"
- << llendl;
- return false;
- }
- if (!imagerawp || !imagerawp->getData())
- {
- llwarns_sparse << "Trying to create a texture from invalid image data"
- << llendl;
- mGLTextureCreated = false;
- return false;
- }
- if (discard_level < 0)
- {
- llassert(mCurrentDiscardLevel >= 0);
- discard_level = mCurrentDiscardLevel;
- }
- if (sPreserveDiscard)
- {
- discard_level = llclamp(discard_level, 0, (S32)mMaxDiscardLevel);
- }
- // Actual image width/height = raw image width/height * 2^discard_level
- U32 raw_w = imagerawp->getWidth();
- U32 raw_h = imagerawp->getHeight();
- U32 w = raw_w << discard_level;
- U32 h = raw_h << discard_level;
- // setSize may call destroyGLTexture if the size does not match
- if (!setSize(w, h, imagerawp->getComponents(), discard_level))
- {
- mGLTextureCreated = false;
- return false;
- }
- if (mHasExplicitFormat &&
- ((mFormatPrimary == GL_RGBA && mComponents < 4) ||
- (mFormatPrimary == GL_RGB && mComponents < 3)))
- {
- llwarns << "Incorrect format: " << std::hex << mFormatPrimary
- << std::dec << " - Number of components: " << (U32)mComponents
- << llendl;
- mHasExplicitFormat = false;
- }
- if (!mHasExplicitFormat)
- {
- switch (mComponents)
- {
- case 1:
- // Use luminance alpha (for fonts)
- mFormatInternal = GL_LUMINANCE8;
- mFormatPrimary = GL_LUMINANCE;
- mFormatType = GL_UNSIGNED_BYTE;
- break;
- case 2:
- // Use luminance alpha (for fonts)
- mFormatInternal = GL_LUMINANCE8_ALPHA8;
- mFormatPrimary = GL_LUMINANCE_ALPHA;
- mFormatType = GL_UNSIGNED_BYTE;
- break;
- case 3:
- mFormatInternal = GL_RGB8;
- mFormatPrimary = GL_RGB;
- mFormatType = GL_UNSIGNED_BYTE;
- break;
- case 4:
- mFormatInternal = GL_RGBA8;
- mFormatPrimary = GL_RGBA;
- mFormatType = GL_UNSIGNED_BYTE;
- break;
- default:
- llwarns << "Bad number of components for texture: "
- << (U32)getComponents() << LL_ENDL;
- to_create = false;
- }
- calcAlphaChannelOffsetAndStride();
- }
- if (!to_create) // Do not create a GL texture
- {
- destroyGLTexture();
- mCurrentDiscardLevel = discard_level;
- mLastBindTime = sLastFrameTime;
- mGLTextureCreated = false;
- return true;
- }
- const U8* rawdatap = imagerawp->getData();
- return createGLTexture(discard_level, rawdatap, false, usename, defer_copy,
- tex_name);
- }
- bool LLImageGL::createGLTexture(S32 discard_level, const U8* data_in,
- bool data_hasmips, S32 usename,
- bool defer_copy, U32* tex_name)
- {
- LL_TRACY_TIMER(TRC_CREATE_GL_TEXTURE3);
- bool main_thread = is_main_thread();
- if (defer_copy)
- {
- data_in = NULL;
- }
- llassert(defer_copy || data_in);
- stop_glerror();
- if (discard_level < 0)
- {
- llassert(mCurrentDiscardLevel >= 0);
- discard_level = mCurrentDiscardLevel;
- }
- discard_level = llclamp(discard_level, 0, (S32)mMaxDiscardLevel);
- // Note: always force creation of new texname when not on main thread or
- // when defer copy is set.
- if (main_thread)
- {
- syncTexName();
- if (!defer_copy && mTexName && discard_level == mCurrentDiscardLevel)
- {
- // This will only be true if the size has not changed
- if (tex_name)
- {
- *tex_name = mTexName;
- }
- return setImage(data_in, data_hasmips);
- }
- }
- LLTexUnit* unit0 = gGL.getTexUnit(0);
- U32 old_name = mTexName;
- U32 new_name = 0;
- if (usename)
- {
- llassert(main_thread);
- new_name = usename;
- }
- else
- {
- generateTextures(1, &new_name);
- unit0->bind(this, false, new_name);
- U32 type = LLTexUnit::getInternalType(mBindTarget);
- glTexParameteri(type, GL_TEXTURE_BASE_LEVEL, 0);
- glTexParameteri(type, GL_TEXTURE_MAX_LEVEL,
- mMaxDiscardLevel - discard_level);
- }
- if (tex_name)
- {
- *tex_name = new_name;
- }
- if (mUseMipMaps)
- {
- mAutoGenMips = true;
- }
- mCurrentDiscardLevel = discard_level;
- if (!setImage(data_in, data_hasmips, new_name))
- {
- return false;
- }
- // Set texture options to our defaults.
- unit0->setHasMipMaps(mHasMipMaps);
- unit0->setTextureAddressMode(mAddressMode);
- unit0->setTextureFilteringOption(mFilterOption);
- // Things will break if we do not unbind after creation
- unit0->unbind(mBindTarget);
- if (old_name)
- {
- sGlobalTexMemBytes -= mTextureMemory;
- }
- if (!defer_copy)
- {
- if (main_thread) // Not on background thread, immediately set mTexName
- {
- if (old_name != 0 && old_name != new_name)
- {
- deleteTextures(1, &old_name);
- }
- mTexName = new_name;
- }
- else
- {
- // If we are on the image loading thread, be sure to delete the
- // old texname and update mTexName on the main thread.
- syncToMainThread(new_name);
- }
- }
- mTextureMemory = getMipBytes(mCurrentDiscardLevel);
- sGlobalTexMemBytes += mTextureMemory;
- // Mark this as bound at this point, so we do not throw it out immediately
- mLastBindTime = sLastFrameTime;
- return true;
- }
- void LLImageGL::syncToMainThread(U32 new_tex_name)
- {
- LL_TRACY_TIMER(TRC_IMAGEGL_SYNC);
- llassert(!is_main_thread());
- // We must now make sure all the GL commands have been flushed down the
- // thread's GL pipeline; without this, you would see flickering/black
- // images, or sudden random texture corruptions (e.g. for UI ones).
- if (gGLManager.mHasSync)
- {
- if (mTexNameSync)
- {
- glDeleteSync(mTexNameSync);
- }
- mTexNameSync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
- glFlush();
- // With NVIDIA, we do not need to do the sync in the main thread, which
- // avoids to risk stalling the main thread GL pipeline at all. HB
- if (gGLManager.mIsNVIDIA && sSyncInThread)
- {
- glClientWaitSync(mTexNameSync, 0, GL_TIMEOUT_IGNORED);
- glDeleteSync(mTexNameSync);
- mTexNameSync = 0;
- }
- }
- else
- {
- glFinish(); // Ouch, costly !... HB
- }
- // Note: instead of using a post to the main thread work queue to swap the
- // tex name and delete the old one, we delay these operations (now both
- // performed in syncTexName(), which is called everytime before we use
- // mTexName) until we actually need to use the texture: this avoids to
- // needlessly stall the GL pipeline with a syncing when we could naturally
- // get past the fence due to the delay between the image creation and its
- // use (this was Kathrine Jansma's idea, that I reimplemented so that the
- // modification of mTexName is done only in the main thread, which also
- // allowed to remove the queueing of the deleteTextures() call into the
- // work queue). We therefore only register here the new tex name and we set
- // an atomic flag indicating that mTexName needs to be updated. HB
- mNewTexName = new_tex_name;
- mTexNameDirty = true;
- }
- void LLImageGL::setTexName(U32 name)
- {
- syncTexName();
- if (mTexName != name) // We need to update the GL image stats then. HB
- {
- image_unbound(mTexName);
- // The format may not yet have been set... In this case, the image will
- // be created later, and the bound memory will then be updated. HB
- if (mFormatPrimary > 0)
- {
- image_bound(mWidth, mHeight, mFormatPrimary, 1, name);
- }
- }
- mTexName = name;
- }
- void LLImageGL::syncTexName()
- {
- if (mTexNameDirty && is_main_thread())
- {
- if (mTexNameSync)
- {
- glClientWaitSync(mTexNameSync, 0, GL_TIMEOUT_IGNORED);
- glDeleteSync(mTexNameSync);
- mTexNameSync = 0;
- }
- if (mTexName && mTexName != mNewTexName)
- {
- deleteTextures(1, &mTexName);
- }
- mTexName = mNewTexName;
- mNewTexName = 0;
- mTexNameDirty = false;
- }
- }
- void LLImageGL::syncTexName(U32 texname)
- {
- if (texname)
- {
- syncTexName();
- if (mTexName && mTexName != texname)
- {
- deleteTextures(1, &mTexName);
- }
- mTexName = texname;
- }
- }
- bool LLImageGL::readBackRaw(S32 discard_level, LLImageRaw* imagerawp,
- bool compressed_ok) const
- {
- const_cast<LLImageGL*>(this)->syncTexName();
- if (discard_level < 0)
- {
- discard_level = mCurrentDiscardLevel;
- }
- if (mTexName == 0 || discard_level < mCurrentDiscardLevel ||
- discard_level > mMaxDiscardLevel)
- {
- return false;
- }
- GLint gl_discard = discard_level - mCurrentDiscardLevel;
- // Explicitly unbind texture
- LLTexUnit* unit0 = gGL.getTexUnit(0);
- unit0->unbind(mBindTarget);
- #if 1
- if (!unit0->bindManual(mBindTarget, mTexName))
- {
- llwarns << "Failed to bind." << llendl;
- return false;
- }
- #else
- unit0->bindManual(mBindTarget, mTexName);
- #endif
- // This is necessary to prevent previous, unrelated errors causing GL
- // textures creation failures, due to the fact we are testing here for GL
- // errors and aborting when finding one. HB
- clear_glerror();
- GLint glwidth = 0;
- glGetTexLevelParameteriv(mTarget, gl_discard, GL_TEXTURE_WIDTH, &glwidth);
- if (glwidth == 0)
- {
- // No mip data smaller than current discard level
- return false;
- }
- U32 width = getWidth(discard_level);
- U32 height = getHeight(discard_level);
- U8 ncomponents = getComponents();
- if (ncomponents == 0)
- {
- return false;
- }
- if ((GLint)width < glwidth)
- {
- llwarns << "Texture size is smaller than it should be: width: "
- << width << " - glwidth: " << glwidth << " - mWidth: "
- << mWidth << " - mCurrentDiscardLevel: "
- << (S32)mCurrentDiscardLevel << " - discard_level: "
- << (S32)discard_level << llendl;
- return false;
- }
- if (width <= 0 || width > 2048 || height <= 0 || height > 2048 ||
- ncomponents < 1 || ncomponents > 4)
- {
- llwarns << "Bogus size/components: " << width << "x" << height << "x"
- << ncomponents << llendl;
- return false;
- }
- GLint is_compressed = 0;
- if (compressed_ok)
- {
- glGetTexLevelParameteriv(mTarget, is_compressed, GL_TEXTURE_COMPRESSED,
- &is_compressed);
- }
- GLenum error = glGetError();
- if (error != GL_NO_ERROR)
- {
- llwarns << "GL Error happens before reading back texture. Error code: "
- << error << llendl;
- stop_glerror();
- // If there has been an error, we must abort (missing in LL's code). HB
- return false;
- }
- if (is_compressed)
- {
- GLint glbytes;
- glGetTexLevelParameteriv(mTarget, gl_discard,
- GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &glbytes);
- if (!imagerawp->allocateDataSize(width, height, ncomponents, glbytes))
- {
- llwarns << "Memory allocation failed for reading back texture. Size is: "
- << glbytes << " - width: " << width << " - height: "
- << height << " - components: " << ncomponents << llendl;
- return false;
- }
- glGetCompressedTexImage(mTarget, gl_discard,
- (GLvoid*)imagerawp->getData());
- }
- else
- {
- if (!imagerawp->allocateDataSize(width, height, ncomponents))
- {
- llwarns << "Memory allocation failed for reading back texture: width: "
- << width << " - height: " << height << " - components: "
- << ncomponents << llendl;
- return false;
- }
- glGetTexImage(GL_TEXTURE_2D, gl_discard, mFormatPrimary, mFormatType,
- (GLvoid*)(imagerawp->getData()));
- }
- error = glGetError();
- if (error != GL_NO_ERROR)
- {
- llwarns << "GL Error happens after reading back texture. Error code: "
- << error << llendl;
- stop_glerror();
- imagerawp->deleteData();
- return false;
- }
- return true;
- }
- void LLImageGL::destroyGLTexture()
- {
- syncTexName();
- if (mTexName)
- {
- sGlobalTexMemBytes -= mTextureMemory;
- mTextureMemory = 0;
- deleteTextures(1, &mTexName);
- mCurrentDiscardLevel = -1; // Invalidate mCurrentDiscardLevel.
- mTexName = 0;
- mGLTextureCreated = false;
- }
- }
- // Force to invalidate the gl texture, most likely a sculpty texture
- void LLImageGL::forceToInvalidateGLTexture()
- {
- syncTexName();
- if (mTexName)
- {
- destroyGLTexture();
- }
- else
- {
- mCurrentDiscardLevel = -1 ; // Invalidate mCurrentDiscardLevel.
- }
- }
- void LLImageGL::setAddressMode(LLTexUnit::eTextureAddressMode mode)
- {
- syncTexName();
- if (mAddressMode != mode)
- {
- mTexOptionsDirty = true;
- mAddressMode = mode;
- }
- LLTexUnit* unit = gGL.getTexUnit(gGL.getCurrentTexUnitIndex());
- if (unit->getCurrTexture() == mTexName)
- {
- unit->setTextureAddressMode(mode);
- mTexOptionsDirty = false;
- }
- }
- void LLImageGL::setFilteringOption(LLTexUnit::eTextureFilterOptions option)
- {
- syncTexName();
- if (mFilterOption != option)
- {
- mTexOptionsDirty = true;
- mFilterOption = option;
- }
- if (mTexName == 0)
- {
- return;
- }
- LLTexUnit* unit = gGL.getTexUnit(gGL.getCurrentTexUnitIndex());
- if (unit->getCurrTexture() == mTexName)
- {
- unit->setTextureFilteringOption(option);
- mTexOptionsDirty = false;
- stop_glerror();
- }
- }
- U32 LLImageGL::getHeight(S32 discard_level) const
- {
- if (discard_level < 0)
- {
- discard_level = mCurrentDiscardLevel;
- }
- U32 height = mHeight >> discard_level;
- return height < 1 ? 1 : height;
- }
- U32 LLImageGL::getWidth(S32 discard_level) const
- {
- if (discard_level < 0)
- {
- discard_level = mCurrentDiscardLevel;
- }
- U32 width = mWidth >> discard_level;
- if (width < 1) width = 1;
- return width;
- }
- S64 LLImageGL::getBytes(S32 discard_level) const
- {
- if (discard_level < 0)
- {
- discard_level = mCurrentDiscardLevel;
- }
- S64 w = mWidth >> discard_level;
- S64 h = mHeight >> discard_level;
- if (w == 0) w = 1;
- if (h == 0) h = 1;
- return dataFormatBytes(mFormatPrimary, w, h);
- }
- S64 LLImageGL::getMipBytes(S32 discard_level) const
- {
- if (discard_level < 0)
- {
- discard_level = mCurrentDiscardLevel;
- }
- S64 w = mWidth >> discard_level;
- S64 h = mHeight >> discard_level;
- S64 res = dataFormatBytes(mFormatPrimary, w, h);
- if (mUseMipMaps)
- {
- while (w > 1 && h > 1)
- {
- w >>= 1;
- if (w == 0)
- {
- w = 1;
- }
- h >>= 1;
- if (h == 0)
- {
- h = 1;
- }
- res += dataFormatBytes(mFormatPrimary, w, h);
- }
- }
- return res;
- }
- void LLImageGL::setTarget(U32 target, LLTexUnit::eTextureType bind_target)
- {
- mTarget = target;
- mBindTarget = bind_target;
- }
- void LLImageGL::setNeedsAlphaAndPickMask(bool need_mask)
- {
- if (mNeedsAlphaAndPickMask != need_mask)
- {
- mNeedsAlphaAndPickMask = need_mask;
- if (mNeedsAlphaAndPickMask)
- {
- mAlphaOffset = 0;
- }
- else // Do not need alpha mask
- {
- mAlphaOffset = INVALID_OFFSET;
- mIsMask = false;
- }
- }
- }
- // Helper function
- LL_INLINE static bool is_little_endian()
- {
- S32 a = 0x12345678;
- U8* c = (U8*)(&a);
- return *c == 0x78;
- }
- void LLImageGL::calcAlphaChannelOffsetAndStride()
- {
- if (mAlphaOffset == INVALID_OFFSET) // Do not need alpha mask
- {
- return;
- }
- mAlphaStride = -1;
- switch (mFormatPrimary)
- {
- case GL_LUMINANCE:
- case GL_ALPHA:
- mAlphaStride = 1;
- break;
- case GL_LUMINANCE_ALPHA:
- mAlphaStride = 2;
- break;
- case GL_RED:
- case GL_RGB:
- case GL_SRGB:
- mNeedsAlphaAndPickMask = mIsMask = false;
- #if FIX_MASKS
- mAlphaOffset = INVALID_OFFSET;
- #endif
- return; // No alpha channel.
- case GL_RGBA:
- case GL_SRGB_ALPHA:
- mAlphaStride = 4;
- break;
- case GL_BGRA_EXT:
- mAlphaStride = 4;
- break;
- default:
- break;
- }
- mAlphaOffset = -1;
- if (mFormatType == GL_UNSIGNED_BYTE)
- {
- mAlphaOffset = mAlphaStride - 1;
- }
- else if (is_little_endian())
- {
- if (mFormatType == GL_UNSIGNED_INT_8_8_8_8)
- {
- mAlphaOffset = 0;
- }
- else if (mFormatType == GL_UNSIGNED_INT_8_8_8_8_REV)
- {
- mAlphaOffset = 3;
- }
- }
- else // Big endian
- {
- if (mFormatType == GL_UNSIGNED_INT_8_8_8_8)
- {
- mAlphaOffset = 3;
- }
- else if (mFormatType == GL_UNSIGNED_INT_8_8_8_8_REV)
- {
- mAlphaOffset = 0;
- }
- }
- if (mAlphaStride < 1 || // Unsupported format
- mAlphaOffset < 0 || // Unsupported type
- (mFormatPrimary == GL_BGRA_EXT &&
- mFormatType != GL_UNSIGNED_BYTE)) // Unknown situation
- {
- llwarns << "Cannot analyze alpha for image with format type "
- << std::hex << mFormatType << std::dec << llendl;
- mNeedsAlphaAndPickMask = mIsMask = false;
- #if FIX_MASKS
- mAlphaOffset = INVALID_OFFSET;
- #endif
- }
- }
- void LLImageGL::analyzeAlpha(const void* data_in, U32 w, U32 h)
- {
- if (!mNeedsAlphaAndPickMask)
- {
- return;
- }
- U32 length = w * h;
- U32 alphatotal = 0;
- U32 sample[16];
- memset(sample, 0, sizeof(U32) * 16);
- // Generate histogram of quantized alpha. Also add-in the histogram of a
- // 2x2 box-sampled version. The idea is this will mid-skew the data (and
- // thus increase the chances of not being used as a mask) from high-
- // frequency alpha maps which suffer the worst from aliasing when used as
- // alpha masks.
- if (w >= 2 && h >= 2)
- {
- llassert(w % 2 == 0);
- llassert(h % 2 == 0);
- const GLubyte* rowstart = (const GLubyte*)data_in + mAlphaOffset;
- for (U32 y = 0; y < h; y += 2)
- {
- const GLubyte* current = rowstart;
- for (U32 x = 0; x < w; x += 2)
- {
- const U32 s1 = current[0];
- alphatotal += s1;
- const U32 s2 = current[w * mAlphaStride];
- alphatotal += s2;
- current += mAlphaStride;
- const U32 s3 = current[0];
- alphatotal += s3;
- const U32 s4 = current[w * mAlphaStride];
- alphatotal += s4;
- current += mAlphaStride;
- ++sample[s1 / 16];
- ++sample[s2 / 16];
- ++sample[s3 / 16];
- ++sample[s4 / 16];
- const U32 asum = (s1 + s2 + s3 + s4);
- alphatotal += asum;
- sample[asum / 64] += 4;
- }
- rowstart += 2 * w * mAlphaStride;
- }
- length *= 2; // we sampled everything twice, essentially
- }
- else
- {
- const GLubyte* current = ((const GLubyte*)data_in) + mAlphaOffset;
- for (U32 i = 0; i < length; ++i)
- {
- const U32 s1 = *current;
- alphatotal += s1;
- ++sample[s1 / 16];
- current += mAlphaStride;
- }
- }
- // If more than 1/16th of alpha samples are mid-range, this shouldn't be
- // treated as a 1-bit mask. Also, if all of the alpha samples are clumped
- // on one half of the range (but not at an absolute extreme), then consider
- // this to be an intentional effect and don't treat as a mask.
- U32 midrangetotal = 0;
- for (U32 i = 2; i < 13; ++i)
- {
- midrangetotal += sample[i];
- }
- U32 lowerhalftotal = 0;
- for (U32 i = 0; i < 8; ++i)
- {
- lowerhalftotal += sample[i];
- }
- U32 upperhalftotal = 0;
- for (U32 i = 8; i < 16; ++i)
- {
- upperhalftotal += sample[i];
- }
- if (midrangetotal > length / 48 || // Lots of midrange, or
- // All close to transparent but not all totally transparent, or
- (lowerhalftotal == length && alphatotal != 0) ||
- // All close to opaque but not all totally opaque
- (upperhalftotal == length && alphatotal != 255 * length))
- {
- mIsMask = false; // Not suitable for masking
- }
- else
- {
- mIsMask = true;
- }
- }
- U32 LLImageGL::createPickMask(U32 width, U32 height)
- {
- freePickMask();
- U32 pick_width = width / 2 + 1;
- U32 pick_height = height / 2 + 1;
- U32 size = pick_width * pick_height;
- size = (size + 7) / 8; // pixelcount-to-bits
- mPickMask = new (std::nothrow) U8[size];
- if (mPickMask)
- {
- mPickMaskWidth = pick_width - 1;
- mPickMaskHeight = pick_height - 1;
- memset(mPickMask, 0, sizeof(U8) * size);
- }
- else
- {
- mPickMaskWidth = 0;
- mPickMaskHeight = 0;
- size = 0;
- }
- return size;
- }
- void LLImageGL::freePickMask()
- {
- if (mPickMask)
- {
- delete[] mPickMask;
- mPickMask = NULL;
- }
- mPickMaskWidth = mPickMaskHeight = 0;
- }
- void LLImageGL::updatePickMask(U32 width, U32 height, const U8* data_in)
- {
- if (!mNeedsAlphaAndPickMask)
- {
- return;
- }
- if (mFormatType != GL_UNSIGNED_BYTE ||
- (mFormatPrimary != GL_RGBA && mFormatPrimary != GL_SRGB_ALPHA))
- {
- // Cannot generate a pick mask for this texture
- freePickMask();
- return;
- }
- #if LL_HAS_ASSERT
- const U32 size =
- #endif
- createPickMask(width, height);
- U32 pick_bit = 0;
- for (U32 y = 0; y < height; y += 2)
- {
- for (U32 x = 0; x < width; x += 2)
- {
- U8 alpha = data_in[(y * width + x) * 4 + 3];
- if (alpha > 32)
- {
- U32 pick_idx = pick_bit / 8;
- U32 pick_offset = pick_bit % 8;
- llassert(pick_idx < size);
- mPickMask[pick_idx] |= 1 << pick_offset;
- }
- ++pick_bit;
- }
- }
- }
- bool LLImageGL::getMask(const LLVector2& tc)
- {
- bool res = true;
- if (mPickMask)
- {
- F32 u, v;
- if (LL_LIKELY(tc.isFinite()))
- {
- u = tc.mV[0] - floorf(tc.mV[0]);
- v = tc.mV[1] - floorf(tc.mV[1]);
- }
- else
- {
- llwarns_sparse << "Non-finite u/v in mask pick !" << llendl;
- u = v = 0.f;
- // removing assert per EXT-4388
- //llassert(false);
- }
- if (LL_UNLIKELY(u < 0.f || u > 1.f || v < 0.f || v > 1.f))
- {
- llwarns_sparse << "u/v out of range in image mask pick !"
- << llendl;
- u = v = 0.f;
- // removing assert per EXT-4388
- //llassert(false);
- }
- U32 x = llfloor(u * mPickMaskWidth);
- U32 y = llfloor(v * mPickMaskHeight);
- if (LL_UNLIKELY(x > mPickMaskWidth))
- {
- llwarns_sparse << "Width overrun on pick mask read !" << llendl;
- x = llmax(mPickMaskWidth, 0);
- }
- if (LL_UNLIKELY(y > mPickMaskHeight))
- {
- llwarns_sparse << "Height overrun on pick mask read !" << llendl;
- y = llmax(mPickMaskHeight, 0);
- }
- U32 idx = y * mPickMaskWidth + x;
- U32 offset = idx % 8;
- res = (mPickMask[idx / 8] & (1 << offset)) != 0;
- }
- return res;
- }
- //static
- void LLImageGL::initThread(LLWindow* windowp, S32 threads)
- {
- if (!threads || sThread)
- {
- return;
- }
- if (threads < 0) // Automatic setting requested
- {
- // Limit our pool to 32 threads (and thus 32 GL contexts); this matches
- // the limit for the threaded image decoder (see LLImageDecodeThread's
- // construtor code). HB
- threads = llmin(LLCPUInfo::getInstance()->getMaxThreadConcurrency(),
- 32U);
- }
- sThread = new LLImageGLThread(windowp, threads);
- gImageQueuep = LLWorkQueue::getNamedInstance("LLImageGL");
- }
- //static
- void LLImageGL::stopThread()
- {
- LLImageGLThread::sEnabled = false;
- if (sThread)
- {
- gImageQueuep.reset();
- sThread->close();
- // Note: thread instances are kept in memory and cleared at viewer
- // exit, which prevents crashes... HB
- sThread = NULL;
- }
- }
- ///////////////////////////////////////////////////////////////////////////////
- // LLImageGLThread class
- ///////////////////////////////////////////////////////////////////////////////
- // Global variable
- LLWorkQueue::weak_t gImageQueuep;
- // Used to track the LLImageGLThread instances and destroy them on viewer
- // shutdown (i.e. once their child threads have been stopped). HB
- typedef std::set<std::unique_ptr<LLImageGLThread> > instances_set_t;
- static instances_set_t sThreadInstances;
- // Static members
- bool LLImageGLThread::sEnabled = false;
- // -1 = free VRAM is unknown.
- LLAtomicS32 LLImageGLThread::sFreeVRAMMegabytes(-1);
- // Must be called from main thread
- LLImageGLThread::LLImageGLThread(LLWindow* window, U32 threads)
- : LLThreadPool("LLImageGL", threads),
- mWindow(window),
- mTheadCounter(0)
- {
- assert_main_thread();
- llassert_always(threads > 0); // At least one thread, please !
- sThreadInstances.emplace(this);
- llinfos << "Initializing with " << threads << " worker threads." << llendl;
- // We must create one GL context per thread, while still in the main thread
- for (U32 i = 0; i < threads; ++i)
- {
- mContexts.push_back(mWindow->createSharedContext());
- }
- LLThreadPool::start(true); // true = wait until all threads are started.
- // Restore the main thread context
- mWindow->makeContextCurrent(NULL);
- // We can now use the threaded image creation
- sEnabled = true;
- }
- // Called from child threads in LLThreadPool.
- //virtual
- void LLImageGLThread::run()
- {
- // We must perform setup on this thread before actually servicing our
- // LLWorkQueue, likewise cleanup afterwards.
- // Do not let threads race for GL contexts on mWindow, and protect
- // mTheadCounter from thread concurrency.
- mThreadsMutex.lock();
- if ((size_t)mTheadCounter >= mContexts.size()) // Paranoia
- {
- llerrs << "More threads created than available contexts ("
- << mContexts.size() << ")" << llendl;
- }
- // Keep these values on stack for use on thread exit (see below)
- void* context = mContexts[mTheadCounter++];
- std::string name = getThreadName(LLThread::thisThreadIdHash());
- if (!context)
- {
- llwarns << "No available GL context for thread " << name
- << ". Aborting this thread !" << llendl;
- mThreadsMutex.unlock();
- doIncStartedThreads(); // Account as started nonetheless.
- return;
- }
- llinfos << "Initializing GL for thread " << name << " with context: "
- << std::hex << (intptr_t)context << std::dec << llendl;
- // Set the context on the viewer window and start GL for our thread (gGL is
- // thread_local).
- mWindow->makeContextCurrent(context);
- gGL.init();
- mThreadsMutex.unlock();
- // It is now safe to consider that this thread has indeed fully started.
- doIncStartedThreads();
- // Run the queue servicing, until the queue is closed.
- LLThreadPool::run();
- llinfos << "Shutting down GL for thread " << name << " with GL context: "
- << std::hex << (intptr_t)context << std::dec << llendl;
- gGL.shutdown();
- mWindow->destroySharedContext(context);
- }
- // Must be called from main thread
- //static
- void LLImageGLThread::cleanup()
- {
- assert_main_thread();
- size_t count = sThreadInstances.size();
- // Since this is a set of std::unique_ptr's, clearing it will cause all
- // stored instances to get automatically destroyed.
- sThreadInstances.clear();
- llinfos << "Number of destroyed instances: " << count << llendl;
- }
- // Called from main thread
- //static
- void LLImageGLThread::updateFreeVRAM()
- {
- LL_TRACY_TIMER(TRC_IMAGEGLTHREAD_UPDATE_FREE_VRAM);
- // Post update to background thread if available, otherwise execute
- // immediately.
- if (sEnabled)
- {
- auto queue = gImageQueuep.lock();
- if (queue)
- {
- queue->post([]() { readFreeVRAM(); });
- return;
- }
- }
- readFreeVRAM();
- }
- // Called from main or child threads
- //static
- void LLImageGLThread::readFreeVRAM()
- {
- LL_TRACY_TIMER(TRC_IMAGEGLTHREAD_READ_VRAM);
- if (gGLManager.mHasATIMemInfo)
- {
- // Reference:
- // https://registry.khronos.org/OpenGL/extensions/ATI/ATI_meminfo.txt
- GLint meminfo[4] = { -1, 0, 0, 0 };
- glGetIntegerv(GL_TEXTURE_FREE_MEMORY_ATI, meminfo);
- if (meminfo[0] >= 0) // If call succeeded. HB
- {
- sFreeVRAMMegabytes = meminfo[0] / 1024; // Convert to MB ! HB
- LL_DEBUGS("ImageGLThread") << "Free VRAM: " << sFreeVRAMMegabytes
- << "MB" << LL_ENDL;
- }
- else
- {
- LL_DEBUGS_ONCE("ImageGLThread") << "GL_TEXTURE_FREE_MEMORY_ATI failed."
- << LL_ENDL;
- }
- stop_glerror(); // Clear any error resulting from this call. HB
- }
- else if (gGLManager.mHasNVXMemInfo)
- {
- GLint free_memory_kb = -1;
- glGetIntegerv(GL_GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX,
- &free_memory_kb);
- if (free_memory_kb >= 0) // If call succeeded. HB
- {
- sFreeVRAMMegabytes = free_memory_kb / 1024; // Convert to MB
- LL_DEBUGS("ImageGLThread") << "Free VRAM: " << sFreeVRAMMegabytes
- << "MB" << LL_ENDL;
- }
- else
- {
- LL_DEBUGS_ONCE("ImageGLThread") << "GL_GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX failed."
- << LL_ENDL;
- }
- stop_glerror(); // Clear any error resulting from this call. HB
- }
- }
|