diff options
| author | Erik Kundiman <erik@megapahit.org> | 2026-09-01 18:35:23 +0800 |
|---|---|---|
| committer | Erik Kundiman <erik@megapahit.org> | 2026-09-04 21:58:53 +0800 |
| commit | 00bb3bb6f07660bd914d29a1389342bb3060d254 (patch) | |
| tree | 431f574922494d2201718f13085f17bc6bd7fb42 /indra/llrender/llimagegl.cpp | |
| parent | a8636720129952c10cf49ab80da21bf8b340b96c (diff) | |
| parent | 4ef9f8f14b35ed388c294d53767a0dca0e890924 (diff) | |
Merge remote-tracking branch 'secondlife/release/26.4' into 26.4
Diffstat (limited to 'indra/llrender/llimagegl.cpp')
| -rw-r--r-- | indra/llrender/llimagegl.cpp | 188 |
1 files changed, 140 insertions, 48 deletions
diff --git a/indra/llrender/llimagegl.cpp b/indra/llrender/llimagegl.cpp index 1942ddb648..1d51c6741c 100644 --- a/indra/llrender/llimagegl.cpp +++ b/indra/llrender/llimagegl.cpp @@ -138,7 +138,6 @@ U32 LLImageGL::sUniqueCount = 0; U32 LLImageGL::sBindCount = 0; S32 LLImageGL::sCount = 0; -bool LLImageGL::sGlobalUseAnisotropic = false; F32 LLImageGL::sLastFrameTime = 0.f; LLImageGL* LLImageGL::sDefaultGLTexture = NULL ; bool LLImageGL::sCompressTextures = false; @@ -576,6 +575,7 @@ void LLImageGL::init(bool usemipmaps, bool allow_compression) mIsMask = false; mNeedsAlphaAndPickMask = true ; + mAlphaAnalysisSerial = 0; mAlphaStride = 0 ; mAlphaOffset = 0 ; @@ -878,16 +878,6 @@ bool LLImageGL::setImage(const U8* data_in, bool data_hasmips /* = false */, S32 mMipLevels = wpo2(llmax(w, h)); - //use legacy mipmap generation mode (note: making this condional can cause rendering issues) - // -- but making it not conditional triggers deprecation warnings when core profile is enabled - // (some rendering issues while core profile is enabled are acceptable at this point in time) -#if GL_VERSION_1_4 - if (!LLRender::sGLCoreProfile) - { - glTexParameteri(mTarget, GL_GENERATE_MIPMAP, GL_TRUE); - } -#endif - LLImageGL::setManualImage(mTarget, 0, mFormatInternal, w, h, mFormatPrimary, mFormatType, @@ -905,7 +895,6 @@ bool LLImageGL::setImage(const U8* data_in, bool data_hasmips /* = false */, S32 } #endif - if (LLRender::sGLCoreProfile) { LL_PROFILE_GPU_ZONE("generate mip map"); glGenerateMipmap(mTarget); @@ -1108,7 +1097,11 @@ U32 type_width_from_pixtype(U32 pixtype) bool should_stagger_image_set(bool compressed) { -#if LL_DARWIN +#if LL_MESA_HEADLESS + return false; +#elif LL_LINUX + return !compressed && on_main_thread() && gGLManager.mIsNVIDIA; +#elif LL_DARWIN return !compressed && on_main_thread() && gGLManager.mIsAMD; #else // glTexSubImage2D doesn't work with compressed textures on select tested Nvidia GPUs on Windows 10 -Cosmic,2023-03-08 @@ -2050,6 +2043,9 @@ void LLImageGL::destroyGLTexture() mTexName = 0; mGLTextureCreated = false ; } + + // Invalidate pending jobs + ++mAlphaAnalysisSerial; } //force to invalidate the gl texture, most likely a sculpty texture @@ -2106,7 +2102,7 @@ bool LLImageGL::getIsResident(bool test_now) #if GL_VERSION_1_1 if (mTexName != 0) { - glAreTexturesResident(1, (GLuint*)&mTexName, &mIsResident); + mIsResident = true; } else #endif @@ -2211,6 +2207,9 @@ void LLImageGL::setNeedsAlphaAndPickMask(bool need_mask) { mAlphaOffset = INVALID_OFFSET ; mIsMask = false; + + // Invalidate pending jobs + ++mAlphaAnalysisSerial; } } } @@ -2244,11 +2243,9 @@ void LLImageGL::calcAlphaChannelOffsetAndStride() #endif mAlphaStride = 4; break; -#if GL_EXT_bgra - case GL_BGRA_EXT: + case GL_BGRA: mAlphaStride = 4; break; -#endif default: break; } @@ -2284,12 +2281,8 @@ void LLImageGL::calcAlphaChannelOffsetAndStride() #endif // GL_VERSION_1_2 if( mAlphaStride < 1 || //unsupported format - mAlphaOffset < 0 //unsupported type -#if GL_EXT_bgra - || - (mFormatPrimary == GL_BGRA_EXT && mFormatType != GL_UNSIGNED_BYTE) //unknown situation -#endif - ) + mAlphaOffset < 0 || //unsupported type + (mFormatPrimary == GL_BGRA && mFormatType != GL_UNSIGNED_BYTE)) //unknown situation { LL_WARNS() << "Cannot analyze alpha for image with format type " << std::hex << mFormatType << std::dec << LL_ENDL; @@ -2298,11 +2291,16 @@ void LLImageGL::calcAlphaChannelOffsetAndStride() } } -void LLImageGL::analyzeAlpha(const void* data_in, U32 w, U32 h) +bool LLImageGL::analyzeAlphaData( + const void* data_in, + U32 w, + U32 h, + S8 alpha_offset, + S8 alpha_stride) { - if(!data_in || sSkipAnalyzeAlpha || !mNeedsAlphaAndPickMask) + if (!data_in) { - return ; + return false; } LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE; @@ -2311,7 +2309,7 @@ void LLImageGL::analyzeAlpha(const void* data_in, U32 w, U32 h) U32 alphatotal = 0; U32 sample[16]; - memset(sample, 0, sizeof(U32)*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 @@ -2322,46 +2320,46 @@ void LLImageGL::analyzeAlpha(const void* data_in, U32 w, U32 h) { llassert(w % 2 == 0); llassert(h % 2 == 0); - const GLubyte* rowstart = ((const GLubyte*) data_in) + mAlphaOffset; + const GLubyte* rowstart = ((const GLubyte*)data_in) + alpha_offset; for (U32 y = 0; y < h; y += 2) { - const GLubyte* current = rowstart; + const unsigned char* current = rowstart; for (U32 x = 0; x < w; x += 2) { const U32 s1 = current[0]; alphatotal += s1; - const U32 s2 = current[w * mAlphaStride]; + const U32 s2 = current[w * alpha_stride]; alphatotal += s2; - current += mAlphaStride; + current += alpha_stride; const U32 s3 = current[0]; alphatotal += s3; - const U32 s4 = current[w * mAlphaStride]; + const U32 s4 = current[w * alpha_stride]; alphatotal += s4; - current += mAlphaStride; + current += alpha_stride; - ++sample[s1/16]; - ++sample[s2/16]; - ++sample[s3/16]; - ++sample[s4/16]; + ++sample[s1 / 16]; + ++sample[s2 / 16]; + ++sample[s3 / 16]; + ++sample[s4 / 16]; - const U32 asum = (s1+s2+s3+s4); + const U32 asum = (s1 + s2 + s3 + s4); alphatotal += asum; - sample[asum/(16*4)] += 4; + sample[asum / (16 * 4)] += 4; } - rowstart += 2 * w * mAlphaStride; + rowstart += 2 * w * alpha_stride; } length *= 2; // we sampled everything twice, essentially } else { - const GLubyte* current = ((const GLubyte*) data_in) + mAlphaOffset; + const unsigned char* current = ((const unsigned char*)data_in) + alpha_offset; for (U32 i = 0; i < length; i++) { const U32 s1 = *current; alphatotal += s1; - ++sample[s1/16]; - current += mAlphaStride; + ++sample[s1 / 16]; + current += alpha_stride; } } @@ -2388,15 +2386,103 @@ void LLImageGL::analyzeAlpha(const void* data_in, U32 w, U32 h) upperhalftotal += sample[i]; } - if (midrangetotal > length/48 || // lots of midrange, or - (lowerhalftotal == length && alphatotal != 0) || // all close to transparent but not all totally transparent, or - (upperhalftotal == length && alphatotal != 255*length)) // all close to opaque but not all totally opaque + if (midrangetotal > length / 48 || + (lowerhalftotal == length && alphatotal != 0) || + (upperhalftotal == length && alphatotal != 255 * length)) { - mIsMask = false; // not suitable for masking + return false; // not suitable for masking } else { - mIsMask = true; + return true; // is a mask + } +} + +void LLImageGL::analyzeAlpha(const void* data_in, U32 w, U32 h) +{ + // if mNeedsAlphaAndPickMask is true, then offset and stride are supposed to be valid. + if (!data_in || sSkipAnalyzeAlpha || !mNeedsAlphaAndPickMask) + return; + + // Already on a worker thread or a small image - analyze immediately + if (!on_main_thread() || (w < 64 && h < 64)) + { + LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE; + // Should have been incremented by destroyGLTexture, + // but increment either way, for extra safety. + ++mAlphaAnalysisSerial; + + mIsMask = analyzeAlphaData(data_in, w, h, mAlphaOffset, mAlphaStride); + return; + } + + // On main thread - defer to worker thread + + // Capture context + const S8 alpha_offset = mAlphaOffset; + const S8 alpha_stride = mAlphaStride; + const U32 request_serial = ++mAlphaAnalysisSerial; + + // Copy data for worker thread + const size_t data_size = size_t(w) * size_t(h) * size_t(alpha_stride); + U8* data_copy = new (std::nothrow) U8[data_size]; + + if (!data_copy) + { + LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE; + mIsMask = analyzeAlphaData(data_in, w, h, alpha_offset, alpha_stride); + return; + } + memcpy(data_copy, static_cast<const U8*>(data_in), data_size); + + // Viewer can rapidly switch between lods, which would invalidate + // previous analysis results. + // Use a serial number to filter out obsolete analysis results. + + ref(); // Keep texture alive + + auto mainq = mMainQueue.lock(); + + // Get the GL thread queue + auto workerq = LLImageGLThread::sEnabledTextures ? + LL::WorkQueue::getInstance("LLImageGL") : // Use the image processing queue if available + LL::WorkQueue::getInstance("General"); // Fallback to general + + bool posted_job = false; + if (mainq && workerq) + { + posted_job = mainq->postTo( + workerq, + // Worker thread: analyze alpha + [data_copy, w, h, alpha_offset, alpha_stride]() -> bool + { + LL_PROFILE_ZONE_NAMED("Deffered alpha mask analysis"); + bool is_mask = LLImageGL::analyzeAlphaData(data_copy, w, h, alpha_offset, alpha_stride); + delete[] data_copy; + return is_mask; + }, + // Main thread: apply result + [this, request_serial](bool is_mask) + { + // Only apply if no newer analysis has been requested + if (mAlphaAnalysisSerial == request_serial) + { + mIsMask = is_mask; + } + unref(); + } + ); + + // Conservative default until analysis completes + mIsMask = false; + } + if (!posted_job) + { + LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE; + // Queues not available - fall back to synchronous analysis + delete[] data_copy; + mIsMask = analyzeAlphaData(data_in, w, h, mAlphaOffset, mAlphaStride); + unref(); } } @@ -2608,6 +2694,12 @@ bool LLImageGL::scaleDown(S32 desired_discard) { glDrawArrays(GL_TRIANGLES, 0, 3); +#if LL_DARWIN + // On mac OS, flush before freeing, otherwise the texture may be + // freed before Metal's 'lazy' evaluation/restoration behavior triggers. + glFlush(); +#endif + free_tex_image(mTexName); glTexImage2D(mTarget, 0, mFormatInternal, desired_width, desired_height, 0, mFormatPrimary, mFormatType, nullptr); glCopyTexSubImage2D(mTarget, 0, 0, 0, 0, 0, desired_width, desired_height); |
