<div dir="ltr">From 6dbf96b3a18404a01154c091a49f5ee7c206d107 Mon Sep 17 00:00:00 2001<br>From: AnusuyaKumarasamy <<a href="mailto:anusuya.kumarasamy@multicorewareinc.com">anusuya.kumarasamy@multicorewareinc.com</a>><br>Date: Wed, 3 Jul 2024 10:22:44 +0530<br>Subject: [PATCH] Add logs for both layers & fix issue while aborting in check<br> params<br><br>---<br> source/abrEncApp.cpp            |   3 +-<br> source/common/slice.h           |   2 +-<br> source/encoder/api.cpp          |  18 +-<br> source/encoder/encoder.cpp      | 507 ++++++++++++++++----------------<br> source/encoder/encoder.h        |  12 +-<br> source/encoder/frameencoder.cpp |  55 ++--<br> source/encoder/frameencoder.h   |  32 +-<br> source/encoder/framefilter.cpp  |  10 +-<br> source/x265.h                   |   2 +<br> 9 files changed, 330 insertions(+), 311 deletions(-)<br><br>diff --git a/source/abrEncApp.cpp b/source/abrEncApp.cpp<br>index eabfb7d2b..fc4262369 100644<br>--- a/source/abrEncApp.cpp<br>+++ b/source/abrEncApp.cpp<br>@@ -206,6 +206,7 @@ namespace X265_NS {<br>         {<br>             x265_log(NULL, X265_LOG_ERROR, "x265_encoder_open() failed for Enc, \n");<br>             m_ret = 2;<br>+            m_reader = NULL;<br>             return -1;<br>         }<br> <br>@@ -867,7 +868,7 @@ ret:<br>             m_reader->stop();<br>             delete m_reader;<br>         }<br>-        else<br>+        else if (m_scaler != NULL)<br>         {<br>             m_scaler->stop();<br>             m_scaler->destroy();<br>diff --git a/source/common/slice.h b/source/common/slice.h<br>index c85cf0972..2a78c9338 100644<br>--- a/source/common/slice.h<br>+++ b/source/common/slice.h<br>@@ -161,6 +161,7 @@ struct VPS<br>     uint32_t         numReorderPics[MAX_T_LAYERS];<br>     uint32_t         maxDecPicBuffering[MAX_T_LAYERS];<br>     uint32_t         maxLatencyIncrease[MAX_T_LAYERS];<br>+    int              m_numLayers;<br> <br> #if ENABLE_ALPHA<br>     bool             splitting_flag;<br>@@ -172,7 +173,6 @@ struct VPS<br>     uint8_t          m_layerIdInNuh[MAX_VPS_LAYER_ID_PLUS1];<br>     uint8_t          m_layerIdInVps[MAX_VPS_LAYER_ID_PLUS1];<br>     int              m_viewIdLen;<br>-    int              m_numLayers;<br>     int              m_vpsNumLayerSetsMinus1;<br>     bool             vps_extension_flag;<br> #endif<br>diff --git a/source/encoder/api.cpp b/source/encoder/api.cpp<br>index e21f541d9..88bc25550 100644<br>--- a/source/encoder/api.cpp<br>+++ b/source/encoder/api.cpp<br>@@ -602,7 +602,10 @@ fail:<br>         *pi_nal = 0;<br> <br>     if (numEncoded && encoder->m_param->csvLogLevel && encoder->m_outputCount >= encoder->m_latestParam->chunkStart)<br>-        x265_csvlog_frame(encoder->m_param, pic_out[0]);<br>+    {<br>+        for (int layer = 0; layer < encoder->m_param->numScalableLayers; layer++)<br>+            x265_csvlog_frame(encoder->m_param, pic_out[layer]);<br>+    }<br> <br>     if (numEncoded < 0)<br>         encoder->m_aborted = true;<br>@@ -653,11 +656,14 @@ void x265_encoder_log(x265_encoder* enc, int argc, char **argv)<br>     if (enc)<br>     {<br>         Encoder *encoder = static_cast<Encoder*>(enc);<br>-        x265_stats stats;       <br>-        encoder->fetchStats(&stats, sizeof(stats));<br>+        x265_stats stats[MAX_SCALABLE_LAYERS];<br>         int padx = encoder->m_sps.conformanceWindow.rightOffset;<br>         int pady = encoder->m_sps.conformanceWindow.bottomOffset;<br>-        x265_csvlog_encode(encoder->m_param, &stats, padx, pady, argc, argv);<br>+        for (int layer = 0; layer < encoder->m_param->numScalableLayers; layer++)<br>+        {<br>+            encoder->fetchStats(stats, sizeof(stats[layer]), layer);<br>+            x265_csvlog_encode(encoder->m_param, &stats[0], padx, pady, argc, argv);<br>+        }<br>     }<br> }<br> <br>@@ -1295,7 +1301,7 @@ FILE* x265_csvlog_open(const x265_param* param)<br>         {<br>             if (param->csvLogLevel)<br>             {<br>-                fprintf(csvfp, "Encode Order, Type, POC, QP, Bits, Scenecut, ");<br>+                fprintf(csvfp, "Layer , Encode Order, Type, POC, QP, Bits, Scenecut, ");<br>                 if (!!param->bEnableTemporalSubLayers)<br>                     fprintf(csvfp, "Temporal Sub Layer ID, ");<br>                 if (param->csvLogLevel >= 2)<br>@@ -1409,7 +1415,7 @@ void x265_csvlog_frame(const x265_param* param, const x265_picture* pic)<br>         return;<br> <br>     const x265_frame_stats* frameStats = &pic->frameData;<br>-    fprintf(param->csvfpt, "%d, %c-SLICE, %4d, %2.2lf, %10d, %d,", frameStats->encoderOrder, frameStats->sliceType, frameStats->poc,<br>+    fprintf(param->csvfpt, "%d, %d, %c-SLICE, %4d, %2.2lf, %10d, %d,", pic->layerID, frameStats->encoderOrder, frameStats->sliceType, frameStats->poc,<br>                                                                    frameStats->qp, (int)frameStats->bits, frameStats->bScenecut);<br>     if (!!param->bEnableTemporalSubLayers)<br>         fprintf(param->csvfpt, "%d,", frameStats->tLayer);<br>diff --git a/source/encoder/encoder.cpp b/source/encoder/encoder.cpp<br>index 5697ac5e5..4844a158c 100644<br>--- a/source/encoder/encoder.cpp<br>+++ b/source/encoder/encoder.cpp<br>@@ -1879,7 +1879,10 @@ int Encoder::encode(const x265_picture* pic_in, x265_picture** pic_out)<br> <br>         m_lookahead->addPicture(*inFrame[0], sliceType);<br> <br>-        m_dpb->m_picList.pushBack(*inFrame[1]); /* Add enhancement layer to DPB to be used later in frameencoder*/<br>+#if ENABLE_ALPHA<br>+        if(m_param->numScalableLayers > 1)<br>+            m_dpb->m_picList.pushBack(*inFrame[1]); /* Add enhancement layer to DPB to be used later in frameencoder*/<br>+#endif<br>         m_numDelayedPic++;<br>     }<br>     else if (m_latestParam->forceFlush == 2)<br>@@ -1924,6 +1927,7 @@ int Encoder::encode(const x265_picture* pic_in, x265_picture** pic_out)<br>                     pic_out[sLayer]->userData = outFrame->m_userData;<br>                     pic_out[sLayer]->colorSpace = m_param->internalCsp;<br>                     pic_out[sLayer]->frameData.tLayer = outFrame->m_tempLayer;<br>+                    pic_out[sLayer]->layerID = sLayer;<br>                     frameData = &(pic_out[sLayer]->frameData);<br> <br>                     pic_out[sLayer]->pts = outFrame->m_pts;<br>@@ -2059,8 +2063,8 @@ int Encoder::encode(const x265_picture* pic_in, x265_picture** pic_out)<br>                 if (m_aborted)<br>                     return -1;<br> <br>-                if ((m_outputCount + 1) >= m_param->chunkStart && !sLayer)<br>-                    finishFrameStats(outFrame, curEncoder, frameData, m_pocLast);<br>+                if ((m_outputCount + 1) >= m_param->chunkStart)<br>+                    finishFrameStats(outFrame, curEncoder, frameData, m_pocLast, sLayer);<br>                 if (m_param->analysisSave)<br>                 {<br>                     pic_out[sLayer]->analysisData.frameBits = frameData->bits;<br>@@ -2730,230 +2734,233 @@ void Encoder::printSummary()<br>     if (m_param->logLevel < X265_LOG_INFO)<br>         return;<br> <br>-    char buffer[200];<br>-    if (m_analyzeI.m_numPics)<br>-        x265_log(m_param, X265_LOG_INFO, "frame I: %s\n", statsString(m_analyzeI, buffer));<br>-    if (m_analyzeP.m_numPics)<br>-        x265_log(m_param, X265_LOG_INFO, "frame P: %s\n", statsString(m_analyzeP, buffer));<br>-    if (m_analyzeB.m_numPics)<br>-        x265_log(m_param, X265_LOG_INFO, "frame B: %s\n", statsString(m_analyzeB, buffer));<br>-    if (m_param->bEnableWeightedPred && m_analyzeP.m_numPics)<br>-    {<br>-        x265_log(m_param, X265_LOG_INFO, "Weighted P-Frames: Y:%.1f%% UV:%.1f%%\n",<br>-            (float)100.0 * m_numLumaWPFrames / m_analyzeP.m_numPics,<br>-            (float)100.0 * m_numChromaWPFrames / m_analyzeP.m_numPics);<br>-    }<br>-    if (m_param->bEnableWeightedBiPred && m_analyzeB.m_numPics)<br>+    for (int layer = 0; layer < m_param->numScalableLayers; layer++)<br>     {<br>-        x265_log(m_param, X265_LOG_INFO, "Weighted B-Frames: Y:%.1f%% UV:%.1f%%\n",<br>-            (float)100.0 * m_numLumaWPBiFrames / m_analyzeB.m_numPics,<br>-            (float)100.0 * m_numChromaWPBiFrames / m_analyzeB.m_numPics);<br>-    }<br>+        char buffer[200];<br>+        if (m_analyzeI[layer].m_numPics)<br>+            x265_log(m_param, X265_LOG_INFO, "frame I: %s\n", statsString(m_analyzeI[layer], buffer));<br>+        if (m_analyzeP[layer].m_numPics)<br>+            x265_log(m_param, X265_LOG_INFO, "frame P: %s\n", statsString(m_analyzeP[layer], buffer));<br>+        if (m_analyzeB[layer].m_numPics)<br>+            x265_log(m_param, X265_LOG_INFO, "frame B: %s\n", statsString(m_analyzeB[layer], buffer));<br>+        if (m_param->bEnableWeightedPred && m_analyzeP[layer].m_numPics)<br>+        {<br>+            x265_log(m_param, X265_LOG_INFO, "Weighted P-Frames: Y:%.1f%% UV:%.1f%%\n",<br>+                (float)100.0 * m_numLumaWPFrames / m_analyzeP[layer].m_numPics,<br>+                (float)100.0 * m_numChromaWPFrames / m_analyzeP[layer].m_numPics);<br>+        }<br>+        if (m_param->bEnableWeightedBiPred && m_analyzeB[layer].m_numPics)<br>+        {<br>+            x265_log(m_param, X265_LOG_INFO, "Weighted B-Frames: Y:%.1f%% UV:%.1f%%\n",<br>+                (float)100.0 * m_numLumaWPBiFrames / m_analyzeB[layer].m_numPics,<br>+                (float)100.0 * m_numChromaWPBiFrames / m_analyzeB[layer].m_numPics);<br>+        }<br> <br>-    if (m_param->bLossless)<br>-    {<br>-        float frameSize = (float)(m_param->sourceWidth - m_sps.conformanceWindow.rightOffset) *<br>-                                 (m_param->sourceHeight - m_sps.conformanceWindow.bottomOffset);<br>-        float uncompressed = frameSize * X265_DEPTH * m_analyzeAll.m_numPics;<br>+        if (m_param->bLossless)<br>+        {<br>+            float frameSize = (float)(m_param->sourceWidth - m_sps.conformanceWindow.rightOffset) *<br>+                (m_param->sourceHeight - m_sps.conformanceWindow.bottomOffset);<br>+            float uncompressed = frameSize * X265_DEPTH * m_analyzeAll[layer].m_numPics;<br> <br>-        x265_log(m_param, X265_LOG_INFO, "lossless compression ratio %.2f::1\n", uncompressed / m_analyzeAll.m_accBits);<br>-    }<br>-    if (m_param->bMultiPassOptRPS && m_param->rc.bStatRead)<br>-    {<br>-        x265_log(m_param, X265_LOG_INFO, "RPS in SPS: %d frames (%.2f%%), RPS not in SPS: %d frames (%.2f%%)\n", <br>-            m_rpsInSpsCount, (float)100.0 * m_rpsInSpsCount / m_rateControl->m_numEntries, <br>-            m_rateControl->m_numEntries - m_rpsInSpsCount, <br>-            (float)100.0 * (m_rateControl->m_numEntries - m_rpsInSpsCount) / m_rateControl->m_numEntries);<br>-    }<br>+            x265_log(m_param, X265_LOG_INFO, "lossless compression ratio %.2f::1\n", uncompressed / m_analyzeAll[layer].m_accBits);<br>+        }<br>+        if (m_param->bMultiPassOptRPS && m_param->rc.bStatRead)<br>+        {<br>+            x265_log(m_param, X265_LOG_INFO, "RPS in SPS: %d frames (%.2f%%), RPS not in SPS: %d frames (%.2f%%)\n",<br>+                m_rpsInSpsCount, (float)100.0 * m_rpsInSpsCount / m_rateControl->m_numEntries,<br>+                m_rateControl->m_numEntries - m_rpsInSpsCount,<br>+                (float)100.0 * (m_rateControl->m_numEntries - m_rpsInSpsCount) / m_rateControl->m_numEntries);<br>+        }<br> <br>-    if (m_analyzeAll.m_numPics)<br>-    {<br>-        int p = 0;<br>-        double elapsedEncodeTime = (double)(x265_mdate() - m_encodeStartTime) / 1000000;<br>-        double elapsedVideoTime = (double)m_analyzeAll.m_numPics * m_param->fpsDenom / m_param->fpsNum;<br>-        double bitrate = (0.001f * m_analyzeAll.m_accBits) / elapsedVideoTime;<br>+        if (m_analyzeAll[layer].m_numPics)<br>+        {<br>+            int p = 0;<br>+            double elapsedEncodeTime = (double)(x265_mdate() - m_encodeStartTime) / 1000000;<br>+            double elapsedVideoTime = (double)m_analyzeAll[layer].m_numPics * m_param->fpsDenom / m_param->fpsNum;<br>+            double bitrate = (0.001f * m_analyzeAll[layer].m_accBits) / elapsedVideoTime;<br> <br>-        p += sprintf(buffer + p, "\nencoded %d frames in %.2fs (%.2f fps), %.2f kb/s, Avg QP:%2.2lf", m_analyzeAll.m_numPics,<br>-                     elapsedEncodeTime, m_analyzeAll.m_numPics / elapsedEncodeTime, bitrate, m_analyzeAll.m_totalQp / (double)m_analyzeAll.m_numPics);<br>+            p += sprintf(buffer + p, "\nencoded %d frames in %.2fs (%.2f fps), %.2f kb/s, Avg QP:%2.2lf", m_analyzeAll[layer].m_numPics,<br>+                elapsedEncodeTime, m_analyzeAll[layer].m_numPics / elapsedEncodeTime, bitrate, m_analyzeAll[layer].m_totalQp / (double)m_analyzeAll[layer].m_numPics);<br> <br>-        if (m_param->bEnablePsnr)<br>-        {<br>-            double globalPsnr = (m_analyzeAll.m_psnrSumY * 6 + m_analyzeAll.m_psnrSumU + m_analyzeAll.m_psnrSumV) / (8 * m_analyzeAll.m_numPics);<br>-            p += sprintf(buffer + p, ", Global PSNR: %.3f", globalPsnr);<br>-        }<br>+            if (m_param->bEnablePsnr)<br>+            {<br>+                double globalPsnr = (m_analyzeAll[layer].m_psnrSumY * 6 + m_analyzeAll[layer].m_psnrSumU + m_analyzeAll[layer].m_psnrSumV) / (8 * m_analyzeAll[layer].m_numPics);<br>+                p += sprintf(buffer + p, ", Global PSNR: %.3f", globalPsnr);<br>+            }<br> <br>-        if (m_param->bEnableSsim)<br>-            p += sprintf(buffer + p, ", SSIM Mean Y: %.7f (%6.3f dB)", m_analyzeAll.m_globalSsim / m_analyzeAll.m_numPics, x265_ssim2dB(m_analyzeAll.m_globalSsim / m_analyzeAll.m_numPics));<br>+            if (m_param->bEnableSsim)<br>+                p += sprintf(buffer + p, ", SSIM Mean Y: %.7f (%6.3f dB)", m_analyzeAll[layer].m_globalSsim / m_analyzeAll[layer].m_numPics, x265_ssim2dB(m_analyzeAll[layer].m_globalSsim / m_analyzeAll[layer].m_numPics));<br> <br>-        sprintf(buffer + p, "\n");<br>-        general_log(m_param, NULL, X265_LOG_INFO, buffer);<br>-    }<br>-    else<br>-        general_log(m_param, NULL, X265_LOG_INFO, "\nencoded 0 frames\n");<br>+            sprintf(buffer + p, "\n");<br>+            general_log(m_param, NULL, X265_LOG_INFO, buffer);<br>+        }<br>+        else<br>+            general_log(m_param, NULL, X265_LOG_INFO, "\nencoded 0 frames\n");<br> <br> #if DETAILED_CU_STATS<br>-    /* Summarize stats from all frame encoders */<br>-    CUStats cuStats;<br>-    for (int i = 0; i < m_param->frameNumThreads; i++)<br>-        cuStats.accumulate(m_frameEncoder[i]->m_cuStats, *m_param);<br>+        /* Summarize stats from all frame encoders */<br>+        CUStats cuStats;<br>+        for (int i = 0; i < m_param->frameNumThreads; i++)<br>+            cuStats.accumulate(m_frameEncoder[i]->m_cuStats, *m_param);<br> <br>-    if (!cuStats.totalCTUTime)<br>-        return;<br>+        if (!cuStats.totalCTUTime)<br>+            return;<br> <br>-    int totalWorkerCount = 0;<br>-    for (int i = 0; i < m_numPools; i++)<br>-        totalWorkerCount += m_threadPool[i].m_numWorkers;<br>+        int totalWorkerCount = 0;<br>+        for (int i = 0; i < m_numPools; i++)<br>+            totalWorkerCount += m_threadPool[i].m_numWorkers;<br> <br>-    int64_t  batchElapsedTime, coopSliceElapsedTime;<br>-    uint64_t batchCount, coopSliceCount;<br>-    m_lookahead->getWorkerStats(batchElapsedTime, batchCount, coopSliceElapsedTime, coopSliceCount);<br>-    int64_t lookaheadWorkerTime = m_lookahead->m_slicetypeDecideElapsedTime + m_lookahead->m_preLookaheadElapsedTime +<br>-                                  batchElapsedTime + coopSliceElapsedTime;<br>+        int64_t  batchElapsedTime, coopSliceElapsedTime;<br>+        uint64_t batchCount, coopSliceCount;<br>+        m_lookahead->getWorkerStats(batchElapsedTime, batchCount, coopSliceElapsedTime, coopSliceCount);<br>+        int64_t lookaheadWorkerTime = m_lookahead->m_slicetypeDecideElapsedTime + m_lookahead->m_preLookaheadElapsedTime +<br>+            batchElapsedTime + coopSliceElapsedTime;<br> <br>-    int64_t totalWorkerTime = cuStats.totalCTUTime + cuStats.loopFilterElapsedTime + cuStats.pmodeTime +<br>-                              cuStats.pmeTime + lookaheadWorkerTime + cuStats.weightAnalyzeTime;<br>-    int64_t elapsedEncodeTime = x265_mdate() - m_encodeStartTime;<br>+        int64_t totalWorkerTime = cuStats.totalCTUTime + cuStats.loopFilterElapsedTime + cuStats.pmodeTime +<br>+            cuStats.pmeTime + lookaheadWorkerTime + cuStats.weightAnalyzeTime;<br>+        int64_t elapsedEncodeTime = x265_mdate() - m_encodeStartTime;<br> <br>-    int64_t interRDOTotalTime = 0, intraRDOTotalTime = 0;<br>-    uint64_t interRDOTotalCount = 0, intraRDOTotalCount = 0;<br>-    for (uint32_t i = 0; i <= m_param->maxCUDepth; i++)<br>-    {<br>-        interRDOTotalTime += cuStats.interRDOElapsedTime[i];<br>-        intraRDOTotalTime += cuStats.intraRDOElapsedTime[i];<br>-        interRDOTotalCount += cuStats.countInterRDO[i];<br>-        intraRDOTotalCount += cuStats.countIntraRDO[i];<br>-    }<br>+        int64_t interRDOTotalTime = 0, intraRDOTotalTime = 0;<br>+        uint64_t interRDOTotalCount = 0, intraRDOTotalCount = 0;<br>+        for (uint32_t i = 0; i <= m_param->maxCUDepth; i++)<br>+        {<br>+            interRDOTotalTime += cuStats.interRDOElapsedTime[i];<br>+            intraRDOTotalTime += cuStats.intraRDOElapsedTime[i];<br>+            interRDOTotalCount += cuStats.countInterRDO[i];<br>+            intraRDOTotalCount += cuStats.countIntraRDO[i];<br>+        }<br> <br>-    /* Time within compressCTU() and pmode tasks not captured by ME, Intra mode selection, or RDO (2Nx2N merge, 2Nx2N bidir, etc) */<br>-    int64_t unaccounted = (cuStats.totalCTUTime + cuStats.pmodeTime) -<br>-                          (cuStats.intraAnalysisElapsedTime + cuStats.motionEstimationElapsedTime + interRDOTotalTime + intraRDOTotalTime);<br>+        /* Time within compressCTU() and pmode tasks not captured by ME, Intra mode selection, or RDO (2Nx2N merge, 2Nx2N bidir, etc) */<br>+        int64_t unaccounted = (cuStats.totalCTUTime + cuStats.pmodeTime) -<br>+            (cuStats.intraAnalysisElapsedTime + cuStats.motionEstimationElapsedTime + interRDOTotalTime + intraRDOTotalTime);<br> <br> #define ELAPSED_SEC(val)  ((double)(val) / 1000000)<br> #define ELAPSED_MSEC(val) ((double)(val) / 1000)<br> <br>-    if (m_param->bDistributeMotionEstimation && cuStats.countPMEMasters)<br>-    {<br>-        x265_log(m_param, X265_LOG_INFO, "CU: %%%05.2lf time spent in motion estimation, averaging %.3lf CU inter modes per CTU\n",<br>-                 100.0 * (cuStats.motionEstimationElapsedTime + cuStats.pmeTime) / totalWorkerTime,<br>-                 (double)cuStats.countMotionEstimate / cuStats.totalCTUs);<br>-        x265_log(m_param, X265_LOG_INFO, "CU: %.3lf PME masters per inter CU, each blocked an average of %.3lf ns\n",<br>-                 (double)cuStats.countPMEMasters / cuStats.countMotionEstimate,<br>-                 (double)cuStats.pmeBlockTime / cuStats.countPMEMasters);<br>-        x265_log(m_param, X265_LOG_INFO, "CU:       %.3lf slaves per PME master, each took an average of %.3lf ms\n",<br>-                 (double)cuStats.countPMETasks / cuStats.countPMEMasters,<br>-                 ELAPSED_MSEC(cuStats.pmeTime) / cuStats.countPMETasks);<br>-    }<br>-    else<br>-    {<br>-        x265_log(m_param, X265_LOG_INFO, "CU: %%%05.2lf time spent in motion estimation, averaging %.3lf CU inter modes per CTU\n",<br>-                 100.0 * cuStats.motionEstimationElapsedTime / totalWorkerTime,<br>-                 (double)cuStats.countMotionEstimate / cuStats.totalCTUs);<br>-<br>-        if (cuStats.skippedMotionReferences[0] || cuStats.skippedMotionReferences[1] || cuStats.skippedMotionReferences[2])<br>-            x265_log(m_param, X265_LOG_INFO, "CU: Skipped motion searches per depth %%%.2lf %%%.2lf %%%.2lf %%%.2lf\n",<br>-                     100.0 * cuStats.skippedMotionReferences[0] / cuStats.totalMotionReferences[0],<br>-                     100.0 * cuStats.skippedMotionReferences[1] / cuStats.totalMotionReferences[1],<br>-                     100.0 * cuStats.skippedMotionReferences[2] / cuStats.totalMotionReferences[2],<br>-                     100.0 * cuStats.skippedMotionReferences[3] / cuStats.totalMotionReferences[3]);<br>-    }<br>-    x265_log(m_param, X265_LOG_INFO, "CU: %%%05.2lf time spent in intra analysis, averaging %.3lf Intra PUs per CTU\n",<br>-             100.0 * cuStats.intraAnalysisElapsedTime / totalWorkerTime,<br>-             (double)cuStats.countIntraAnalysis / cuStats.totalCTUs);<br>-    if (cuStats.skippedIntraCU[0] || cuStats.skippedIntraCU[1] || cuStats.skippedIntraCU[2])<br>-        x265_log(m_param, X265_LOG_INFO, "CU: Skipped intra CUs at depth %%%.2lf %%%.2lf %%%.2lf\n",<br>-                 100.0 * cuStats.skippedIntraCU[0] / cuStats.totalIntraCU[0],<br>-                 100.0 * cuStats.skippedIntraCU[1] / cuStats.totalIntraCU[1],<br>-                 100.0 * cuStats.skippedIntraCU[2] / cuStats.totalIntraCU[2]);<br>-    x265_log(m_param, X265_LOG_INFO, "CU: %%%05.2lf time spent in inter RDO, measuring %.3lf inter/merge predictions per CTU\n",<br>-             100.0 * interRDOTotalTime / totalWorkerTime,<br>-             (double)interRDOTotalCount / cuStats.totalCTUs);<br>-    x265_log(m_param, X265_LOG_INFO, "CU: %%%05.2lf time spent in intra RDO, measuring %.3lf intra predictions per CTU\n",<br>-             100.0 * intraRDOTotalTime / totalWorkerTime,<br>-             (double)intraRDOTotalCount / cuStats.totalCTUs);<br>-    x265_log(m_param, X265_LOG_INFO, "CU: %%%05.2lf time spent in loop filters, average %.3lf ms per call\n",<br>-             100.0 * cuStats.loopFilterElapsedTime / totalWorkerTime,<br>-             ELAPSED_MSEC(cuStats.loopFilterElapsedTime) / cuStats.countLoopFilter);<br>-    if (cuStats.countWeightAnalyze && cuStats.weightAnalyzeTime)<br>-    {<br>-        x265_log(m_param, X265_LOG_INFO, "CU: %%%05.2lf time spent in weight analysis, average %.3lf ms per call\n",<br>-                 100.0 * cuStats.weightAnalyzeTime / totalWorkerTime,<br>-                 ELAPSED_MSEC(cuStats.weightAnalyzeTime) / cuStats.countWeightAnalyze);<br>-    }<br>-    if (m_param->bDistributeModeAnalysis && cuStats.countPModeMasters)<br>-    {<br>-        x265_log(m_param, X265_LOG_INFO, "CU: %.3lf PMODE masters per CTU, each blocked an average of %.3lf ns\n",<br>-                 (double)cuStats.countPModeMasters / cuStats.totalCTUs,<br>-                 (double)cuStats.pmodeBlockTime / cuStats.countPModeMasters);<br>-        x265_log(m_param, X265_LOG_INFO, "CU:       %.3lf slaves per PMODE master, each took average of %.3lf ms\n",<br>-                 (double)cuStats.countPModeTasks / cuStats.countPModeMasters,<br>-                 ELAPSED_MSEC(cuStats.pmodeTime) / cuStats.countPModeTasks);<br>-    }<br>-<br>-    x265_log(m_param, X265_LOG_INFO, "CU: %%%05.2lf time spent in slicetypeDecide (avg %.3lfms) and prelookahead (avg %.3lfms)\n",<br>-             100.0 * lookaheadWorkerTime / totalWorkerTime,<br>-             ELAPSED_MSEC(m_lookahead->m_slicetypeDecideElapsedTime) / m_lookahead->m_countSlicetypeDecide,<br>-             ELAPSED_MSEC(m_lookahead->m_preLookaheadElapsedTime) / m_lookahead->m_countPreLookahead);<br>-<br>-    x265_log(m_param, X265_LOG_INFO, "CU: %%%05.2lf time spent in other tasks\n",<br>-             100.0 * unaccounted / totalWorkerTime);<br>-<br>-    if (intraRDOTotalTime && intraRDOTotalCount)<br>-    {<br>-        x265_log(m_param, X265_LOG_INFO, "CU: Intra RDO time  per depth %%%05.2lf %%%05.2lf %%%05.2lf %%%05.2lf\n",<br>-                 100.0 * cuStats.intraRDOElapsedTime[0] / intraRDOTotalTime,  // 64<br>-                 100.0 * cuStats.intraRDOElapsedTime[1] / intraRDOTotalTime,  // 32<br>-                 100.0 * cuStats.intraRDOElapsedTime[2] / intraRDOTotalTime,  // 16<br>-                 100.0 * cuStats.intraRDOElapsedTime[3] / intraRDOTotalTime); // 8<br>-        x265_log(m_param, X265_LOG_INFO, "CU: Intra RDO calls per depth %%%05.2lf %%%05.2lf %%%05.2lf %%%05.2lf\n",<br>-                 100.0 * cuStats.countIntraRDO[0] / intraRDOTotalCount,  // 64<br>-                 100.0 * cuStats.countIntraRDO[1] / intraRDOTotalCount,  // 32<br>-                 100.0 * cuStats.countIntraRDO[2] / intraRDOTotalCount,  // 16<br>-                 100.0 * cuStats.countIntraRDO[3] / intraRDOTotalCount); // 8<br>-    }<br>-<br>-    if (interRDOTotalTime && interRDOTotalCount)<br>-    {<br>-        x265_log(m_param, X265_LOG_INFO, "CU: Inter RDO time  per depth %%%05.2lf %%%05.2lf %%%05.2lf %%%05.2lf\n",<br>-                 100.0 * cuStats.interRDOElapsedTime[0] / interRDOTotalTime,  // 64<br>-                 100.0 * cuStats.interRDOElapsedTime[1] / interRDOTotalTime,  // 32<br>-                 100.0 * cuStats.interRDOElapsedTime[2] / interRDOTotalTime,  // 16<br>-                 100.0 * cuStats.interRDOElapsedTime[3] / interRDOTotalTime); // 8<br>-        x265_log(m_param, X265_LOG_INFO, "CU: Inter RDO calls per depth %%%05.2lf %%%05.2lf %%%05.2lf %%%05.2lf\n",<br>-                 100.0 * cuStats.countInterRDO[0] / interRDOTotalCount,  // 64<br>-                 100.0 * cuStats.countInterRDO[1] / interRDOTotalCount,  // 32<br>-                 100.0 * cuStats.countInterRDO[2] / interRDOTotalCount,  // 16<br>-                 100.0 * cuStats.countInterRDO[3] / interRDOTotalCount); // 8<br>-    }<br>-<br>-    x265_log(m_param, X265_LOG_INFO, "CU: " X265_LL " %dX%d CTUs compressed in %.3lf seconds, %.3lf CTUs per worker-second\n",<br>-             cuStats.totalCTUs, m_param->maxCUSize, m_param->maxCUSize,<br>-             ELAPSED_SEC(totalWorkerTime),<br>-             cuStats.totalCTUs / ELAPSED_SEC(totalWorkerTime));<br>-<br>-    if (m_threadPool)<br>-        x265_log(m_param, X265_LOG_INFO, "CU: %.3lf average worker utilization, %%%05.2lf of theoretical maximum utilization\n",<br>-                 (double)totalWorkerTime / elapsedEncodeTime,<br>-                 100.0 * totalWorkerTime / (elapsedEncodeTime * totalWorkerCount));<br>+        if (m_param->bDistributeMotionEstimation && cuStats.countPMEMasters)<br>+        {<br>+            x265_log(m_param, X265_LOG_INFO, "CU: %%%05.2lf time spent in motion estimation, averaging %.3lf CU inter modes per CTU\n",<br>+                100.0 * (cuStats.motionEstimationElapsedTime + cuStats.pmeTime) / totalWorkerTime,<br>+                (double)cuStats.countMotionEstimate / cuStats.totalCTUs);<br>+            x265_log(m_param, X265_LOG_INFO, "CU: %.3lf PME masters per inter CU, each blocked an average of %.3lf ns\n",<br>+                (double)cuStats.countPMEMasters / cuStats.countMotionEstimate,<br>+                (double)cuStats.pmeBlockTime / cuStats.countPMEMasters);<br>+            x265_log(m_param, X265_LOG_INFO, "CU:       %.3lf slaves per PME master, each took an average of %.3lf ms\n",<br>+                (double)cuStats.countPMETasks / cuStats.countPMEMasters,<br>+                ELAPSED_MSEC(cuStats.pmeTime) / cuStats.countPMETasks);<br>+        }<br>+        else<br>+        {<br>+            x265_log(m_param, X265_LOG_INFO, "CU: %%%05.2lf time spent in motion estimation, averaging %.3lf CU inter modes per CTU\n",<br>+                100.0 * cuStats.motionEstimationElapsedTime / totalWorkerTime,<br>+                (double)cuStats.countMotionEstimate / cuStats.totalCTUs);<br>+<br>+            if (cuStats.skippedMotionReferences[0] || cuStats.skippedMotionReferences[1] || cuStats.skippedMotionReferences[2])<br>+                x265_log(m_param, X265_LOG_INFO, "CU: Skipped motion searches per depth %%%.2lf %%%.2lf %%%.2lf %%%.2lf\n",<br>+                    100.0 * cuStats.skippedMotionReferences[0] / cuStats.totalMotionReferences[0],<br>+                    100.0 * cuStats.skippedMotionReferences[1] / cuStats.totalMotionReferences[1],<br>+                    100.0 * cuStats.skippedMotionReferences[2] / cuStats.totalMotionReferences[2],<br>+                    100.0 * cuStats.skippedMotionReferences[3] / cuStats.totalMotionReferences[3]);<br>+        }<br>+        x265_log(m_param, X265_LOG_INFO, "CU: %%%05.2lf time spent in intra analysis, averaging %.3lf Intra PUs per CTU\n",<br>+            100.0 * cuStats.intraAnalysisElapsedTime / totalWorkerTime,<br>+            (double)cuStats.countIntraAnalysis / cuStats.totalCTUs);<br>+        if (cuStats.skippedIntraCU[0] || cuStats.skippedIntraCU[1] || cuStats.skippedIntraCU[2])<br>+            x265_log(m_param, X265_LOG_INFO, "CU: Skipped intra CUs at depth %%%.2lf %%%.2lf %%%.2lf\n",<br>+                100.0 * cuStats.skippedIntraCU[0] / cuStats.totalIntraCU[0],<br>+                100.0 * cuStats.skippedIntraCU[1] / cuStats.totalIntraCU[1],<br>+                100.0 * cuStats.skippedIntraCU[2] / cuStats.totalIntraCU[2]);<br>+        x265_log(m_param, X265_LOG_INFO, "CU: %%%05.2lf time spent in inter RDO, measuring %.3lf inter/merge predictions per CTU\n",<br>+            100.0 * interRDOTotalTime / totalWorkerTime,<br>+            (double)interRDOTotalCount / cuStats.totalCTUs);<br>+        x265_log(m_param, X265_LOG_INFO, "CU: %%%05.2lf time spent in intra RDO, measuring %.3lf intra predictions per CTU\n",<br>+            100.0 * intraRDOTotalTime / totalWorkerTime,<br>+            (double)intraRDOTotalCount / cuStats.totalCTUs);<br>+        x265_log(m_param, X265_LOG_INFO, "CU: %%%05.2lf time spent in loop filters, average %.3lf ms per call\n",<br>+            100.0 * cuStats.loopFilterElapsedTime / totalWorkerTime,<br>+            ELAPSED_MSEC(cuStats.loopFilterElapsedTime) / cuStats.countLoopFilter);<br>+        if (cuStats.countWeightAnalyze && cuStats.weightAnalyzeTime)<br>+        {<br>+            x265_log(m_param, X265_LOG_INFO, "CU: %%%05.2lf time spent in weight analysis, average %.3lf ms per call\n",<br>+                100.0 * cuStats.weightAnalyzeTime / totalWorkerTime,<br>+                ELAPSED_MSEC(cuStats.weightAnalyzeTime) / cuStats.countWeightAnalyze);<br>+        }<br>+        if (m_param->bDistributeModeAnalysis && cuStats.countPModeMasters)<br>+        {<br>+            x265_log(m_param, X265_LOG_INFO, "CU: %.3lf PMODE masters per CTU, each blocked an average of %.3lf ns\n",<br>+                (double)cuStats.countPModeMasters / cuStats.totalCTUs,<br>+                (double)cuStats.pmodeBlockTime / cuStats.countPModeMasters);<br>+            x265_log(m_param, X265_LOG_INFO, "CU:       %.3lf slaves per PMODE master, each took average of %.3lf ms\n",<br>+                (double)cuStats.countPModeTasks / cuStats.countPModeMasters,<br>+                ELAPSED_MSEC(cuStats.pmodeTime) / cuStats.countPModeTasks);<br>+        }<br>+<br>+        x265_log(m_param, X265_LOG_INFO, "CU: %%%05.2lf time spent in slicetypeDecide (avg %.3lfms) and prelookahead (avg %.3lfms)\n",<br>+            100.0 * lookaheadWorkerTime / totalWorkerTime,<br>+            ELAPSED_MSEC(m_lookahead->m_slicetypeDecideElapsedTime) / m_lookahead->m_countSlicetypeDecide,<br>+            ELAPSED_MSEC(m_lookahead->m_preLookaheadElapsedTime) / m_lookahead->m_countPreLookahead);<br>+<br>+        x265_log(m_param, X265_LOG_INFO, "CU: %%%05.2lf time spent in other tasks\n",<br>+            100.0 * unaccounted / totalWorkerTime);<br>+<br>+        if (intraRDOTotalTime && intraRDOTotalCount)<br>+        {<br>+            x265_log(m_param, X265_LOG_INFO, "CU: Intra RDO time  per depth %%%05.2lf %%%05.2lf %%%05.2lf %%%05.2lf\n",<br>+                100.0 * cuStats.intraRDOElapsedTime[0] / intraRDOTotalTime,  // 64<br>+                100.0 * cuStats.intraRDOElapsedTime[1] / intraRDOTotalTime,  // 32<br>+                100.0 * cuStats.intraRDOElapsedTime[2] / intraRDOTotalTime,  // 16<br>+                100.0 * cuStats.intraRDOElapsedTime[3] / intraRDOTotalTime); // 8<br>+            x265_log(m_param, X265_LOG_INFO, "CU: Intra RDO calls per depth %%%05.2lf %%%05.2lf %%%05.2lf %%%05.2lf\n",<br>+                100.0 * cuStats.countIntraRDO[0] / intraRDOTotalCount,  // 64<br>+                100.0 * cuStats.countIntraRDO[1] / intraRDOTotalCount,  // 32<br>+                100.0 * cuStats.countIntraRDO[2] / intraRDOTotalCount,  // 16<br>+                100.0 * cuStats.countIntraRDO[3] / intraRDOTotalCount); // 8<br>+        }<br>+<br>+        if (interRDOTotalTime && interRDOTotalCount)<br>+        {<br>+            x265_log(m_param, X265_LOG_INFO, "CU: Inter RDO time  per depth %%%05.2lf %%%05.2lf %%%05.2lf %%%05.2lf\n",<br>+                100.0 * cuStats.interRDOElapsedTime[0] / interRDOTotalTime,  // 64<br>+                100.0 * cuStats.interRDOElapsedTime[1] / interRDOTotalTime,  // 32<br>+                100.0 * cuStats.interRDOElapsedTime[2] / interRDOTotalTime,  // 16<br>+                100.0 * cuStats.interRDOElapsedTime[3] / interRDOTotalTime); // 8<br>+            x265_log(m_param, X265_LOG_INFO, "CU: Inter RDO calls per depth %%%05.2lf %%%05.2lf %%%05.2lf %%%05.2lf\n",<br>+                100.0 * cuStats.countInterRDO[0] / interRDOTotalCount,  // 64<br>+                100.0 * cuStats.countInterRDO[1] / interRDOTotalCount,  // 32<br>+                100.0 * cuStats.countInterRDO[2] / interRDOTotalCount,  // 16<br>+                100.0 * cuStats.countInterRDO[3] / interRDOTotalCount); // 8<br>+        }<br>+<br>+        x265_log(m_param, X265_LOG_INFO, "CU: " X265_LL " %dX%d CTUs compressed in %.3lf seconds, %.3lf CTUs per worker-second\n",<br>+            cuStats.totalCTUs, m_param->maxCUSize, m_param->maxCUSize,<br>+            ELAPSED_SEC(totalWorkerTime),<br>+            cuStats.totalCTUs / ELAPSED_SEC(totalWorkerTime));<br>+<br>+        if (m_threadPool)<br>+            x265_log(m_param, X265_LOG_INFO, "CU: %.3lf average worker utilization, %%%05.2lf of theoretical maximum utilization\n",<br>+                (double)totalWorkerTime / elapsedEncodeTime,<br>+                100.0 * totalWorkerTime / (elapsedEncodeTime * totalWorkerCount));<br> <br> #undef ELAPSED_SEC<br> #undef ELAPSED_MSEC<br> #endif<br>+    }<br> }<br> <br>-void Encoder::fetchStats(x265_stats *stats, size_t statsSizeBytes)<br>+void Encoder::fetchStats(x265_stats *stats, size_t statsSizeBytes, int layer)<br> {<br>     if (statsSizeBytes >= sizeof(stats))<br>     {<br>-        stats->globalPsnrY = m_analyzeAll.m_psnrSumY;<br>-        stats->globalPsnrU = m_analyzeAll.m_psnrSumU;<br>-        stats->globalPsnrV = m_analyzeAll.m_psnrSumV;<br>-        stats->encodedPictureCount = m_analyzeAll.m_numPics;<br>+        stats->globalPsnrY = m_analyzeAll[layer].m_psnrSumY;<br>+        stats->globalPsnrU = m_analyzeAll[layer].m_psnrSumU;<br>+        stats->globalPsnrV = m_analyzeAll[layer].m_psnrSumV;<br>+        stats->encodedPictureCount = m_analyzeAll[layer].m_numPics;<br>         stats->totalWPFrames = m_numLumaWPFrames;<br>-        stats->accBits = m_analyzeAll.m_accBits;<br>+        stats->accBits = m_analyzeAll[layer].m_accBits;<br>         stats->elapsedEncodeTime = (double)(x265_mdate() - m_encodeStartTime) / 1000000;<br>         if (stats->encodedPictureCount > 0)<br>         {<br>-            stats->globalSsim = m_analyzeAll.m_globalSsim / stats->encodedPictureCount;<br>+            stats->globalSsim = m_analyzeAll[layer].m_globalSsim / stats->encodedPictureCount;<br>             stats->globalPsnr = (stats->globalPsnrY * 6 + stats->globalPsnrU + stats->globalPsnrV) / (8 * stats->encodedPictureCount);<br>             stats->elapsedVideoTime = (double)stats->encodedPictureCount * m_param->fpsDenom / m_param->fpsNum;<br>             stats->bitrate = (0.001f * stats->accBits) / stats->elapsedVideoTime;<br>@@ -2969,33 +2976,33 @@ void Encoder::fetchStats(x265_stats *stats, size_t statsSizeBytes)<br>         double fps = (double)m_param->fpsNum / m_param->fpsDenom;<br>         double scale = fps / 1000;<br> <br>-        stats->statsI.numPics = m_analyzeI.m_numPics;<br>-        stats->statsI.avgQp   = m_analyzeI.m_totalQp / (double)m_analyzeI.m_numPics;<br>-        stats->statsI.bitrate = m_analyzeI.m_accBits * scale / (double)m_analyzeI.m_numPics;<br>-        stats->statsI.psnrY   = m_analyzeI.m_psnrSumY / (double)m_analyzeI.m_numPics;<br>-        stats->statsI.psnrU   = m_analyzeI.m_psnrSumU / (double)m_analyzeI.m_numPics;<br>-        stats->statsI.psnrV   = m_analyzeI.m_psnrSumV / (double)m_analyzeI.m_numPics;<br>-        stats->statsI.ssim    = x265_ssim2dB(m_analyzeI.m_globalSsim / (double)m_analyzeI.m_numPics);<br>-<br>-        stats->statsP.numPics = m_analyzeP.m_numPics;<br>-        stats->statsP.avgQp   = m_analyzeP.m_totalQp / (double)m_analyzeP.m_numPics;<br>-        stats->statsP.bitrate = m_analyzeP.m_accBits * scale / (double)m_analyzeP.m_numPics;<br>-        stats->statsP.psnrY   = m_analyzeP.m_psnrSumY / (double)m_analyzeP.m_numPics;<br>-        stats->statsP.psnrU   = m_analyzeP.m_psnrSumU / (double)m_analyzeP.m_numPics;<br>-        stats->statsP.psnrV   = m_analyzeP.m_psnrSumV / (double)m_analyzeP.m_numPics;<br>-        stats->statsP.ssim    = x265_ssim2dB(m_analyzeP.m_globalSsim / (double)m_analyzeP.m_numPics);<br>-<br>-        stats->statsB.numPics = m_analyzeB.m_numPics;<br>-        stats->statsB.avgQp   = m_analyzeB.m_totalQp / (double)m_analyzeB.m_numPics;<br>-        stats->statsB.bitrate = m_analyzeB.m_accBits * scale / (double)m_analyzeB.m_numPics;<br>-        stats->statsB.psnrY   = m_analyzeB.m_psnrSumY / (double)m_analyzeB.m_numPics;<br>-        stats->statsB.psnrU   = m_analyzeB.m_psnrSumU / (double)m_analyzeB.m_numPics;<br>-        stats->statsB.psnrV   = m_analyzeB.m_psnrSumV / (double)m_analyzeB.m_numPics;<br>-        stats->statsB.ssim    = x265_ssim2dB(m_analyzeB.m_globalSsim / (double)m_analyzeB.m_numPics);<br>+        stats->statsI.numPics = m_analyzeI[layer].m_numPics;<br>+        stats->statsI.avgQp   = m_analyzeI[layer].m_totalQp / (double)m_analyzeI[layer].m_numPics;<br>+        stats->statsI.bitrate = m_analyzeI[layer].m_accBits * scale / (double)m_analyzeI[layer].m_numPics;<br>+        stats->statsI.psnrY   = m_analyzeI[layer].m_psnrSumY / (double)m_analyzeI[layer].m_numPics;<br>+        stats->statsI.psnrU   = m_analyzeI[layer].m_psnrSumU / (double)m_analyzeI[layer].m_numPics;<br>+        stats->statsI.psnrV   = m_analyzeI[layer].m_psnrSumV / (double)m_analyzeI[layer].m_numPics;<br>+        stats->statsI.ssim    = x265_ssim2dB(m_analyzeI[layer].m_globalSsim / (double)m_analyzeI[layer].m_numPics);<br>+<br>+        stats->statsP.numPics = m_analyzeP[layer].m_numPics;<br>+        stats->statsP.avgQp   = m_analyzeP[layer].m_totalQp / (double)m_analyzeP[layer].m_numPics;<br>+        stats->statsP.bitrate = m_analyzeP[layer].m_accBits * scale / (double)m_analyzeP[layer].m_numPics;<br>+        stats->statsP.psnrY   = m_analyzeP[layer].m_psnrSumY / (double)m_analyzeP[layer].m_numPics;<br>+        stats->statsP.psnrU   = m_analyzeP[layer].m_psnrSumU / (double)m_analyzeP[layer].m_numPics;<br>+        stats->statsP.psnrV   = m_analyzeP[layer].m_psnrSumV / (double)m_analyzeP[layer].m_numPics;<br>+        stats->statsP.ssim    = x265_ssim2dB(m_analyzeP[layer].m_globalSsim / (double)m_analyzeP[layer].m_numPics);<br>+<br>+        stats->statsB.numPics = m_analyzeB[layer].m_numPics;<br>+        stats->statsB.avgQp   = m_analyzeB[layer].m_totalQp / (double)m_analyzeB[layer].m_numPics;<br>+        stats->statsB.bitrate = m_analyzeB[layer].m_accBits * scale / (double)m_analyzeB[layer].m_numPics;<br>+        stats->statsB.psnrY   = m_analyzeB[layer].m_psnrSumY / (double)m_analyzeB[layer].m_numPics;<br>+        stats->statsB.psnrU   = m_analyzeB[layer].m_psnrSumU / (double)m_analyzeB[layer].m_numPics;<br>+        stats->statsB.psnrV   = m_analyzeB[layer].m_psnrSumV / (double)m_analyzeB[layer].m_numPics;<br>+        stats->statsB.ssim    = x265_ssim2dB(m_analyzeB[layer].m_globalSsim / (double)m_analyzeB[layer].m_numPics);<br>         if (m_param->csvLogLevel >= 2 || m_param->maxCLL || m_param->maxFALL)<br>         {<br>-            stats->maxCLL = m_analyzeAll.m_maxCLL;<br>-            stats->maxFALL = (uint16_t)(m_analyzeAll.m_maxFALL / m_analyzeAll.m_numPics);<br>+            stats->maxCLL = m_analyzeAll[layer].m_maxCLL;<br>+            stats->maxFALL = (uint16_t)(m_analyzeAll[layer].m_maxFALL / m_analyzeAll[layer].m_numPics);<br>         }<br>     }<br>     /* If new statistics are added to x265_stats, we must check here whether the<br>@@ -3003,10 +3010,10 @@ void Encoder::fetchStats(x265_stats *stats, size_t statsSizeBytes)<br>      * future safety) */<br> }<br> <br>-void Encoder::finishFrameStats(Frame* curFrame, FrameEncoder *curEncoder, x265_frame_stats* frameStats, int inPoc)<br>+void Encoder::finishFrameStats(Frame* curFrame, FrameEncoder *curEncoder, x265_frame_stats* frameStats, int inPoc, int layer)<br> {<br>     PicYuv* reconPic = curFrame->m_reconPic;<br>-    uint64_t bits = curEncoder->m_accessUnitBits;<br>+    uint64_t bits = curEncoder->m_accessUnitBits[layer];<br> <br>     //===== calculate PSNR =====<br>     int width  = reconPic->m_picWidth - m_sps.conformanceWindow.rightOffset;<br>@@ -3019,9 +3026,9 @@ void Encoder::finishFrameStats(Frame* curFrame, FrameEncoder *curEncoder, x265_f<br>     double refValueC = (double)maxvalC * maxvalC * size / 4.0;<br>     uint64_t ssdY, ssdU, ssdV;<br> <br>-    ssdY = curEncoder->m_SSDY;<br>-    ssdU = curEncoder->m_SSDU;<br>-    ssdV = curEncoder->m_SSDV;<br>+    ssdY = curEncoder->m_SSDY[layer];<br>+    ssdU = curEncoder->m_SSDU[layer];<br>+    ssdV = curEncoder->m_SSDV[layer];<br>     double psnrY = (ssdY ? 10.0 * log10(refValueY / (double)ssdY) : 99.99);<br>     double psnrU = (ssdU ? 10.0 * log10(refValueC / (double)ssdU) : 99.99);<br>     double psnrV = (ssdV ? 10.0 * log10(refValueC / (double)ssdV) : 99.99);<br>@@ -3030,49 +3037,49 @@ void Encoder::finishFrameStats(Frame* curFrame, FrameEncoder *curEncoder, x265_f<br>     Slice* slice = curEncData.m_slice;<br> <br>     //===== add bits, psnr and ssim =====<br>-    m_analyzeAll.addBits(bits);<br>-    m_analyzeAll.addQP(curEncData.m_avgQpAq);<br>+    m_analyzeAll[layer].addBits(bits);<br>+    m_analyzeAll[layer].addQP(curEncData.m_avgQpAq);<br> <br>     if (m_param->bEnablePsnr)<br>-        m_analyzeAll.addPsnr(psnrY, psnrU, psnrV);<br>+        m_analyzeAll[layer].addPsnr(psnrY, psnrU, psnrV);<br> <br>     double ssim = 0.0;<br>     if (m_param->bEnableSsim && curEncoder->m_ssimCnt)<br>     {<br>-        ssim = curEncoder->m_ssim / curEncoder->m_ssimCnt;<br>-        m_analyzeAll.addSsim(ssim);<br>+        ssim = curEncoder->m_ssim[layer] / curEncoder->m_ssimCnt[layer];<br>+        m_analyzeAll[layer].addSsim(ssim);<br>     }<br>     if (slice->isIntra())<br>     {<br>-        m_analyzeI.addBits(bits);<br>-        m_analyzeI.addQP(curEncData.m_avgQpAq);<br>+        m_analyzeI[layer].addBits(bits);<br>+        m_analyzeI[layer].addQP(curEncData.m_avgQpAq);<br>         if (m_param->bEnablePsnr)<br>-            m_analyzeI.addPsnr(psnrY, psnrU, psnrV);<br>+            m_analyzeI[layer].addPsnr(psnrY, psnrU, psnrV);<br>         if (m_param->bEnableSsim)<br>-            m_analyzeI.addSsim(ssim);<br>+            m_analyzeI[layer].addSsim(ssim);<br>     }<br>     else if (slice->isInterP())<br>     {<br>-        m_analyzeP.addBits(bits);<br>-        m_analyzeP.addQP(curEncData.m_avgQpAq);<br>+        m_analyzeP[layer].addBits(bits);<br>+        m_analyzeP[layer].addQP(curEncData.m_avgQpAq);<br>         if (m_param->bEnablePsnr)<br>-            m_analyzeP.addPsnr(psnrY, psnrU, psnrV);<br>+            m_analyzeP[layer].addPsnr(psnrY, psnrU, psnrV);<br>         if (m_param->bEnableSsim)<br>-            m_analyzeP.addSsim(ssim);<br>+            m_analyzeP[layer].addSsim(ssim);<br>     }<br>     else if (slice->isInterB())<br>     {<br>-        m_analyzeB.addBits(bits);<br>-        m_analyzeB.addQP(curEncData.m_avgQpAq);<br>+        m_analyzeB[layer].addBits(bits);<br>+        m_analyzeB[layer].addQP(curEncData.m_avgQpAq);<br>         if (m_param->bEnablePsnr)<br>-            m_analyzeB.addPsnr(psnrY, psnrU, psnrV);<br>+            m_analyzeB[layer].addPsnr(psnrY, psnrU, psnrV);<br>         if (m_param->bEnableSsim)<br>-            m_analyzeB.addSsim(ssim);<br>+            m_analyzeB[layer].addSsim(ssim);<br>     }<br>     if (m_param->csvLogLevel >= 2 || m_param->maxCLL || m_param->maxFALL)<br>     {<br>-        m_analyzeAll.m_maxFALL += curFrame->m_fencPic->m_avgLumaLevel;<br>-        m_analyzeAll.m_maxCLL = X265_MAX(m_analyzeAll.m_maxCLL, curFrame->m_fencPic->m_maxLumaLevel);<br>+        m_analyzeAll[layer].m_maxFALL += curFrame->m_fencPic->m_avgLumaLevel;<br>+        m_analyzeAll[layer].m_maxCLL = X265_MAX(m_analyzeAll[layer].m_maxCLL, curFrame->m_fencPic->m_maxLumaLevel);<br>     }<br>     char c = (slice->isIntra() ? (curFrame->m_lowres.sliceType == X265_TYPE_IDR ? 'I' : 'i') : slice->isInterP() ? 'P' : 'B');<br>     int poc = slice->m_poc;<br>@@ -3121,12 +3128,12 @@ void Encoder::finishFrameStats(Frame* curFrame, FrameEncoder *curEncoder, x265_f<br> #if ENABLE_LIBVMAF<br>             frameStats->vmafFrameScore = curFrame->m_fencPic->m_vmafScore;<br> #endif<br>-            frameStats->decideWaitTime = ELAPSED_MSEC(0, curEncoder->m_slicetypeWaitTime);<br>-            frameStats->row0WaitTime = ELAPSED_MSEC(curEncoder->m_startCompressTime, curEncoder->m_row0WaitTime);<br>-            frameStats->wallTime = ELAPSED_MSEC(curEncoder->m_row0WaitTime, curEncoder->m_endCompressTime);<br>-            frameStats->refWaitWallTime = ELAPSED_MSEC(curEncoder->m_row0WaitTime, curEncoder->m_allRowsAvailableTime);<br>-            frameStats->totalCTUTime = ELAPSED_MSEC(0, curEncoder->m_totalWorkerElapsedTime);<br>-            frameStats->stallTime = ELAPSED_MSEC(0, curEncoder->m_totalNoWorkerTime);<br>+            frameStats->decideWaitTime = ELAPSED_MSEC(0, curEncoder->m_slicetypeWaitTime[layer]);<br>+            frameStats->row0WaitTime = ELAPSED_MSEC(curEncoder->m_startCompressTime[layer], curEncoder->m_row0WaitTime[layer]);<br>+            frameStats->wallTime = ELAPSED_MSEC(curEncoder->m_row0WaitTime[layer], curEncoder->m_endCompressTime[layer]);<br>+            frameStats->refWaitWallTime = ELAPSED_MSEC(curEncoder->m_row0WaitTime[layer], curEncoder->m_allRowsAvailableTime[layer]);<br>+            frameStats->totalCTUTime = ELAPSED_MSEC(0, curEncoder->m_totalWorkerElapsedTime[layer]);<br>+            frameStats->stallTime = ELAPSED_MSEC(0, curEncoder->m_totalNoWorkerTime[layer]);<br>             frameStats->totalFrameTime = ELAPSED_MSEC(curFrame->m_encodeStartTime, x265_mdate());<br>             if (curEncoder->m_totalActiveWorkerCount)<br>                 frameStats->avgWPP = (double)curEncoder->m_totalActiveWorkerCount / curEncoder->m_activeWorkerCountSamples;<br>@@ -3385,6 +3392,7 @@ void Encoder::initVPS(VPS *vps)<br>     vps->ptl.interlacedSourceFlag = !!m_param->interlaceMode;<br>     vps->ptl.nonPackedConstraintFlag = false;<br>     vps->ptl.frameOnlyConstraintFlag = !m_param->interlaceMode;<br>+    vps->m_numLayers = m_param->numScalableLayers;<br> <br> #if ENABLE_ALPHA<br>     vps->vps_extension_flag = false;<br>@@ -3430,7 +3438,6 @@ void Encoder::initVPS(VPS *vps)<br>         vps->m_nuhLayerIdPresentFlag = 1;<br>         vps->m_viewIdLen = 0;<br>         vps->m_vpsNumLayerSetsMinus1 = 1;<br>-        vps->m_numLayers = m_param->numScalableLayers;<br>     }<br> #endif<br> }<br>diff --git a/source/encoder/encoder.h b/source/encoder/encoder.h<br>index 22976b180..58709e92e 100644<br>--- a/source/encoder/encoder.h<br>+++ b/source/encoder/encoder.h<br>@@ -217,10 +217,10 @@ public:<br> <br>     bool               m_externalFlush;<br>     /* Collect statistics globally */<br>-    EncStats           m_analyzeAll;<br>-    EncStats           m_analyzeI;<br>-    EncStats           m_analyzeP;<br>-    EncStats           m_analyzeB;<br>+    EncStats           m_analyzeAll[MAX_SCALABLE_LAYERS];<br>+    EncStats           m_analyzeI[MAX_SCALABLE_LAYERS];<br>+    EncStats           m_analyzeP[MAX_SCALABLE_LAYERS];<br>+    EncStats           m_analyzeB[MAX_SCALABLE_LAYERS];<br>     VPS                m_vps;<br>     SPS                m_sps;<br>     PPS                m_pps;<br>@@ -320,7 +320,7 @@ public:<br> <br>     void getEndNalUnits(NALList& list, Bitstream& bs);<br> <br>-    void fetchStats(x265_stats* stats, size_t statsSizeBytes);<br>+    void fetchStats(x265_stats* stats, size_t statsSizeBytes, int layer = 0);<br> <br>     void printSummary();<br> <br>@@ -352,7 +352,7 @@ public:<br> <br>     void copyDistortionData(x265_analysis_data* analysis, FrameData &curEncData);<br> <br>-    void finishFrameStats(Frame* pic, FrameEncoder *curEncoder, x265_frame_stats* frameStats, int inPoc);<br>+    void finishFrameStats(Frame* pic, FrameEncoder *curEncoder, x265_frame_stats* frameStats, int inPoc, int layer);<br> <br>     int validateAnalysisData(x265_analysis_validate* param, int readWriteFlag);<br> <br>diff --git a/source/encoder/frameencoder.cpp b/source/encoder/frameencoder.cpp<br>index 8d72adcf4..9263a4658 100644<br>--- a/source/encoder/frameencoder.cpp<br>+++ b/source/encoder/frameencoder.cpp<br>@@ -41,11 +41,9 @@ void weightAnalyse(Slice& slice, Frame& frame, x265_param& param);<br> <br> FrameEncoder::FrameEncoder()<br> {<br>-    m_prevOutputTime = x265_mdate();<br>     m_reconfigure = false;<br>     m_isFrameEncoder = true;<br>     m_threadActive = true;<br>-    m_slicetypeWaitTime = 0;<br>     m_activeWorkerCount = 0;<br>     m_completionCount = 0;<br>     m_outStreams = NULL;<br>@@ -60,9 +58,12 @@ FrameEncoder::FrameEncoder()<br>     m_ctuGeomMap = NULL;<br>     m_localTldIdx = 0;<br>     memset(&m_rce, 0, sizeof(RateControlEntry));<br>-    for(int layer = 0; layer < MAX_SCALABLE_LAYERS; layer++)<br>+    for (int layer = 0; layer < MAX_SCALABLE_LAYERS; layer++)<br>+    {<br>+        m_prevOutputTime[layer] = x265_mdate();<br>+        m_slicetypeWaitTime[layer] = 0;<br>         m_frame[layer] = NULL;<br>-    m_retFrameBuffer = { NULL };<br>+    }<br> }<br> <br> void FrameEncoder::destroy()<br>@@ -290,9 +291,9 @@ bool FrameEncoder::initializeGeoms()<br> <br> bool FrameEncoder::startCompressFrame(Frame* curFrame[MAX_SCALABLE_LAYERS])<br> {<br>-    m_slicetypeWaitTime = x265_mdate() - m_prevOutputTime;<br>     for (int layer = 0; layer < m_param->numScalableLayers; layer++)<br>     {<br>+        m_slicetypeWaitTime[layer] = x265_mdate() - m_prevOutputTime[layer];<br>         m_frame[layer] = curFrame[layer];<br>         curFrame[layer]->m_encData->m_frameEncoderID = m_jpId;<br>         curFrame[layer]->m_encData->m_jobProvider = this;<br>@@ -462,14 +463,14 @@ void FrameEncoder::compressFrame(int layer)<br> {<br>     ProfileScopeEvent(frameThread);<br> <br>-    m_startCompressTime = x265_mdate();<br>+    m_startCompressTime[layer] = x265_mdate();<br>     m_totalActiveWorkerCount = 0;<br>     m_activeWorkerCountSamples = 0;<br>-    m_totalWorkerElapsedTime = 0;<br>-    m_totalNoWorkerTime = 0;<br>+    m_totalWorkerElapsedTime[layer] = 0;<br>+    m_totalNoWorkerTime[layer] = 0;<br>     m_countRowBlocks = 0;<br>-    m_allRowsAvailableTime = 0;<br>-    m_stallStartTime = 0;<br>+    m_allRowsAvailableTime[layer] = 0;<br>+    m_stallStartTime[layer] = 0;<br> <br>     m_completionCount = 0;<br>     memset((void*)m_bAllRowsStop, 0, sizeof(bool) * m_param->maxSlices);<br>@@ -477,9 +478,9 @@ void FrameEncoder::compressFrame(int layer)<br>     m_rowSliceTotalBits[0] = 0;<br>     m_rowSliceTotalBits[1] = 0;<br> <br>-    m_SSDY = m_SSDU = m_SSDV = 0;<br>-    m_ssim = 0;<br>-    m_ssimCnt = 0;<br>+    m_SSDY[layer] = m_SSDU[layer] = m_SSDV[layer] = 0;<br>+    m_ssim[layer] = 0;<br>+    m_ssimCnt[layer] = 0;<br>     memset(&(m_frame[layer]->m_encData->m_frameStats), 0, sizeof(m_frame[layer]->m_encData->m_frameStats));<br>     m_sLayerId = layer;<br> <br>@@ -924,14 +925,14 @@ void FrameEncoder::compressFrame(int layer)<br>                 enableRowEncoder(m_row_to_idx[row]); /* clear external dependency for this row */<br>                 if (!rowInSlice)<br>                 {<br>-                    m_row0WaitTime = x265_mdate();<br>+                    m_row0WaitTime[layer] = x265_mdate();<br>                     enqueueRowEncoder(m_row_to_idx[row]); /* clear internal dependency, start wavefront */<br>                 }<br>                 tryWakeOne();<br>             } // end of loop rowInSlice<br>         } // end of loop sliceId<br> <br>-        m_allRowsAvailableTime = x265_mdate();<br>+        m_allRowsAvailableTime[layer] = x265_mdate();<br>         tryWakeOne(); /* ensure one thread is active or help-wanted flag is set prior to blocking */<br>         static const int block_ms = 250;<br>         while (m_completionEvent.timedWait(block_ms))<br>@@ -962,9 +963,9 @@ void FrameEncoder::compressFrame(int layer)<br>                 }<br> <br>                 if (!i)<br>-                    m_row0WaitTime = x265_mdate();<br>+                    m_row0WaitTime[layer] = x265_mdate();<br>                 else if (i == m_numRows - 1)<br>-                    m_allRowsAvailableTime = x265_mdate();<br>+                    m_allRowsAvailableTime[layer] = x265_mdate();<br>                 processRowEncoder(i, m_tld[m_localTldIdx], layer);<br>             }<br> <br>@@ -1152,12 +1153,14 @@ void FrameEncoder::compressFrame(int layer)<br>             bytes -= (!i || type == NAL_UNIT_SPS || type == NAL_UNIT_PPS) ? 4 : 3;<br>         }<br>     }<br>-    m_accessUnitBits = bytes << 3;<br>+    m_accessUnitBits[layer] = (layer) ? (bytes - (m_accessUnitBits[0] >> 3)) << 3 : bytes << 3;<br> <br>     int filler = 0;<br>     /* rateControlEnd may also block for earlier frames to call rateControlUpdateStats */<br>-    if (!layer && m_top->m_rateControl->rateControlEnd(m_frame[layer], m_accessUnitBits, &m_rce, &filler) < 0)<br>+    if (!layer && m_top->m_rateControl->rateControlEnd(m_frame[layer], m_accessUnitBits[layer], &m_rce, &filler) < 0)<br>         m_top->m_aborted = true;<br>+    if (layer)<br>+        m_frame[layer]->m_encData->m_avgQpAq = m_frame[0]->m_encData->m_avgQpAq;<br> <br>     if (filler > 0)<br>     {<br>@@ -1172,7 +1175,7 @@ void FrameEncoder::compressFrame(int layer)<br>         m_nalList.serialize(NAL_UNIT_FILLER_DATA, m_bs);<br>         bytes += m_nalList.m_nal[m_nalList.m_numNal - 1].sizeBytes;<br>         bytes -= 3; //exclude start code prefix<br>-        m_accessUnitBits = bytes << 3;<br>+        m_accessUnitBits[layer] = bytes << 3;<br>     }<br> <br>     if (m_frame[layer]->m_rpu.payloadSize)<br>@@ -1183,7 +1186,7 @@ void FrameEncoder::compressFrame(int layer)<br>         m_nalList.serialize(NAL_UNIT_UNSPECIFIED, m_bs);<br>     }<br> <br>-    m_endCompressTime = x265_mdate();<br>+    m_endCompressTime[layer] = x265_mdate();<br> <br>     /* Decrement referenced frame reference counts, allow them to be recycled */<br>     for (int l = 0; l < numPredDir; l++)<br>@@ -1234,7 +1237,7 @@ void FrameEncoder::compressFrame(int layer)<br>         m_cuStats.accumulate(m_tld[i].analysis.m_stats[m_jpId], *m_param);<br> #endif<br> <br>-    m_endFrameTime = x265_mdate();  <br>+    m_endFrameTime[layer] = x265_mdate();<br> }<br> <br> void FrameEncoder::initDecodedPictureHashSEI(int row, int cuAddr, int height, int layer)<br>@@ -1387,7 +1390,7 @@ void FrameEncoder::processRow(int row, int threadId, int layer)<br> {<br>     int64_t startTime = x265_mdate();<br>     if (ATOMIC_INC(&m_activeWorkerCount) == 1 && m_stallStartTime)<br>-        m_totalNoWorkerTime += x265_mdate() - m_stallStartTime;<br>+        m_totalNoWorkerTime[layer] += x265_mdate() - m_stallStartTime[layer];<br> <br>     const uint32_t realRow = m_idx_to_row[row >> 1];<br>     const uint32_t typeNum = m_idx_to_row[row & 1];<br>@@ -1404,9 +1407,9 @@ void FrameEncoder::processRow(int row, int threadId, int layer)<br>     }<br> <br>     if (ATOMIC_DEC(&m_activeWorkerCount) == 0)<br>-        m_stallStartTime = x265_mdate();<br>+        m_stallStartTime[layer] = x265_mdate();<br> <br>-    m_totalWorkerElapsedTime += x265_mdate() - startTime; // not thread safe, but good enough<br>+    m_totalWorkerElapsedTime[layer] += x265_mdate() - startTime; // not thread safe, but good enough<br> }<br> <br> // Called by worker threads<br>@@ -2283,9 +2286,9 @@ Frame** FrameEncoder::getEncodedPicture(NALList& output)<br>         {<br>             m_retFrameBuffer[i] = m_frame[i];<br>             m_frame[i] = NULL;<br>+            m_prevOutputTime[i] = x265_mdate();<br>         }<br>         output.takeContents(m_nalList);<br>-        m_prevOutputTime = x265_mdate();<br>         return m_retFrameBuffer;<br>     }<br> <br>diff --git a/source/encoder/frameencoder.h b/source/encoder/frameencoder.h<br>index 9fcd2dcf5..6253cbd84 100644<br>--- a/source/encoder/frameencoder.h<br>+++ b/source/encoder/frameencoder.h<br>@@ -190,27 +190,27 @@ public:<br>     RateControlEntry         m_rce;<br>     SEIDecodedPictureHash    m_seiReconPictureDigest;<br> <br>-    uint64_t                 m_SSDY;<br>-    uint64_t                 m_SSDU;<br>-    uint64_t                 m_SSDV;<br>-    double                   m_ssim;<br>-    uint64_t                 m_accessUnitBits;<br>-    uint32_t                 m_ssimCnt;<br>+    uint64_t                 m_SSDY[MAX_SCALABLE_LAYERS];<br>+    uint64_t                 m_SSDU[MAX_SCALABLE_LAYERS];<br>+    uint64_t                 m_SSDV[MAX_SCALABLE_LAYERS];<br>+    double                   m_ssim[MAX_SCALABLE_LAYERS];<br>+    uint64_t                 m_accessUnitBits[MAX_SCALABLE_LAYERS];<br>+    uint32_t                 m_ssimCnt[MAX_SCALABLE_LAYERS];<br> <br>     volatile int             m_activeWorkerCount;        // count of workers currently encoding or filtering CTUs<br>     volatile int             m_totalActiveWorkerCount;   // sum of m_activeWorkerCount sampled at end of each CTU<br>     volatile int             m_activeWorkerCountSamples; // count of times m_activeWorkerCount was sampled (think vbv restarts)<br>     volatile int             m_countRowBlocks;           // count of workers forced to abandon a row because of top dependency<br>-    int64_t                  m_startCompressTime;        // timestamp when frame encoder is given a frame<br>-    int64_t                  m_row0WaitTime;             // timestamp when row 0 is allowed to start<br>-    int64_t                  m_allRowsAvailableTime;     // timestamp when all reference dependencies are resolved<br>-    int64_t                  m_endCompressTime;          // timestamp after all CTUs are compressed<br>-    int64_t                  m_endFrameTime;             // timestamp after RCEnd, NR updates, etc<br>-    int64_t                  m_stallStartTime;           // timestamp when worker count becomes 0<br>-    int64_t                  m_prevOutputTime;           // timestamp when prev frame was retrieved by API thread<br>-    int64_t                  m_slicetypeWaitTime;        // total elapsed time waiting for decided frame<br>-    int64_t                  m_totalWorkerElapsedTime;   // total elapsed time spent by worker threads processing CTUs<br>-    int64_t                  m_totalNoWorkerTime;        // total elapsed time without any active worker threads<br>+    int64_t                  m_startCompressTime[MAX_SCALABLE_LAYERS];        // timestamp when frame encoder is given a frame<br>+    int64_t                  m_row0WaitTime[MAX_SCALABLE_LAYERS];             // timestamp when row 0 is allowed to start<br>+    int64_t                  m_allRowsAvailableTime[MAX_SCALABLE_LAYERS];     // timestamp when all reference dependencies are resolved<br>+    int64_t                  m_endCompressTime[MAX_SCALABLE_LAYERS];          // timestamp after all CTUs are compressed<br>+    int64_t                  m_endFrameTime[MAX_SCALABLE_LAYERS];             // timestamp after RCEnd, NR updates, etc<br>+    int64_t                  m_stallStartTime[MAX_SCALABLE_LAYERS];           // timestamp when worker count becomes 0<br>+    int64_t                  m_prevOutputTime[MAX_SCALABLE_LAYERS];           // timestamp when prev frame was retrieved by API thread<br>+    int64_t                  m_slicetypeWaitTime[MAX_SCALABLE_LAYERS];        // total elapsed time waiting for decided frame<br>+    int64_t                  m_totalWorkerElapsedTime[MAX_SCALABLE_LAYERS];   // total elapsed time spent by worker threads processing CTUs<br>+    int64_t                  m_totalNoWorkerTime[MAX_SCALABLE_LAYERS];        // total elapsed time without any active worker threads<br> #if DETAILED_CU_STATS<br>     CUStats                  m_cuStats;<br> #endif<br>diff --git a/source/encoder/framefilter.cpp b/source/encoder/framefilter.cpp<br>index 6b8ef03ac..da01d0ceb 100644<br>--- a/source/encoder/framefilter.cpp<br>+++ b/source/encoder/framefilter.cpp<br>@@ -673,7 +673,7 @@ void FrameFilter::processPostRow(int row, int layer)<br>         uint32_t height = m_parallelFilter[row].getCUHeight();<br> <br>         uint64_t ssdY = m_frameEncoder->m_top->computeSSD(fencPic->getLumaAddr(cuAddr), reconPic->getLumaAddr(cuAddr), stride, width, height, m_param);<br>-        m_frameEncoder->m_SSDY += ssdY;<br>+        m_frameEncoder->m_SSDY[layer] += ssdY;<br> <br>         if (m_param->internalCsp != X265_CSP_I400)<br>         {<br>@@ -684,8 +684,8 @@ void FrameFilter::processPostRow(int row, int layer)<br>             uint64_t ssdU = m_frameEncoder->m_top->computeSSD(fencPic->getCbAddr(cuAddr), reconPic->getCbAddr(cuAddr), stride, width, height, m_param);<br>             uint64_t ssdV = m_frameEncoder->m_top->computeSSD(fencPic->getCrAddr(cuAddr), reconPic->getCrAddr(cuAddr), stride, width, height, m_param);<br> <br>-            m_frameEncoder->m_SSDU += ssdU;<br>-            m_frameEncoder->m_SSDV += ssdV;<br>+            m_frameEncoder->m_SSDU[layer] += ssdU;<br>+            m_frameEncoder->m_SSDV[layer] += ssdV;<br>         }<br>     }<br> <br>@@ -705,9 +705,9 @@ void FrameFilter::processPostRow(int row, int layer)<br>         /* SSIM is done for each row in blocks of 4x4 . The First blocks are offset by 2 pixels to the right<br>         * to avoid alignment of ssim blocks with DCT blocks. */<br>         minPixY += bStart ? 2 : -6;<br>-        m_frameEncoder->m_ssim += calculateSSIM(rec + 2 + minPixY * stride1, stride1, fenc + 2 + minPixY * stride2, stride2,<br>+        m_frameEncoder->m_ssim[layer] += calculateSSIM(rec + 2 + minPixY * stride1, stride1, fenc + 2 + minPixY * stride2, stride2,<br>                                                 m_param->sourceWidth - 2, maxPixY - minPixY, m_ssimBuf, ssim_cnt);<br>-        m_frameEncoder->m_ssimCnt += ssim_cnt;<br>+        m_frameEncoder->m_ssimCnt[layer] += ssim_cnt;<br>     }<br> <br>     if (m_param->maxSlices == 1)<br>diff --git a/source/x265.h b/source/x265.h<br>index 238b1358a..084f7cd64 100644<br>--- a/source/x265.h<br>+++ b/source/x265.h<br>@@ -488,6 +488,8 @@ typedef struct x265_picture<br>     uint32_t picStruct;<br> <br>     int    width;<br>+<br>+    int   layerID;<br> } x265_picture;<br> <br> typedef enum<br>-- <br>2.36.0.windows.1<br><br></div>