<div dir="ltr"><div><div><div>This patch will cause a valid change in outputs in all commandlines that use AQ/CUTree/VBV - in other words, that change QP mid-frame. <br></div>It measures the RD cost of signalling deltaQP (current QP -predicted QP), and occasionally mode/split decisions can change in <br>favour of a mode which has no residual, thus saving the trouble of sending deltaQP. <br><br></div>Most of the output changes measured by this patch are in favour of the efficiency curve. Visual quality effects are less well defined. Might be ideal to<br></div>bias away from zero/low residual modes in general for psy- optimizations, but it's not quite clear how that can be done properly. <br><div><div><div><br>Currently, this patch only changes outputs for rdLevels > 4. I will send a follow-on patch which will also change outputs at the lower rdLevels,<br></div><div>as it should.<br></div><div><br></div><div class="gmail_extra"><br><div class="gmail_quote">On Mon, Apr 6, 2015 at 3:49 PM,  <span dir="ltr"><<a href="mailto:deepthi@multicorewareinc.com" target="_blank">deepthi@multicorewareinc.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"># HG changeset patch<br>
# User Deepthi Nandakumar <<a href="mailto:deepthi@multicorewareinc.com" target="_blank">deepthi@multicorewareinc.com</a>><br>
# Date 1428314947 -19800<br>
#      Mon Apr 06 15:39:07 2015 +0530<br>
# Node ID 5e944fdd03bbebb8b2d926a848fab1fc37a9fbee<br>
# Parent  30e209c9bab6acc8028f922c652d80dd51ac263f<br>
aq: add cost of sub-LCU level QP to RD costs<br>
<br>
diff -r 30e209c9bab6 -r 5e944fdd03bb source/encoder/analysis.cpp<br>
--- a/source/encoder/analysis.cpp       Mon Mar 23 14:23:42 2015 +0530<br>
+++ b/source/encoder/analysis.cpp       Mon Apr 06 15:39:07 2015 +0530<br>
@@ -365,7 +365,7 @@<br>
         else<br>
             updateModeCost(*splitPred);<br>
<br>
-        checkDQPForSplitPred(splitPred->cu, cuGeom);<br>
+        checkDQPForSplitPred(*splitPred, cuGeom);<br>
         checkBestMode(*splitPred, depth);<br>
     }<br>
<br>
@@ -771,7 +771,7 @@<br>
         else<br>
             updateModeCost(*splitPred);<br>
<br>
-        checkDQPForSplitPred(splitPred->cu, cuGeom);<br>
+        checkDQPForSplitPred(*splitPred, cuGeom);<br>
         checkBestMode(*splitPred, depth);<br>
     }<br>
<br>
@@ -1063,7 +1063,7 @@<br>
         else if (splitPred->sa8dCost < md.bestMode->sa8dCost)<br>
             md.bestMode = splitPred;<br>
<br>
-        checkDQPForSplitPred(md.bestMode->cu, cuGeom);<br>
+        checkDQPForSplitPred(*md.bestMode, cuGeom);<br>
     }<br>
     if (mightNotSplit)<br>
     {<br>
@@ -1257,7 +1257,7 @@<br>
         else<br>
             updateModeCost(*splitPred);<br>
<br>
-        checkDQPForSplitPred(splitPred->cu, cuGeom);<br>
+        checkDQPForSplitPred(*splitPred, cuGeom);<br>
         checkBestMode(*splitPred, depth);<br>
     }<br>
<br>
@@ -1372,7 +1372,7 @@<br>
     md.bestMode->cu.setPUMv(1, candMvField[bestSadCand][1].mv, 0, 0);<br>
     md.bestMode->cu.setPURefIdx(0, (int8_t)candMvField[bestSadCand][0].refIdx, 0, 0);<br>
     md.bestMode->cu.setPURefIdx(1, (int8_t)candMvField[bestSadCand][1].refIdx, 0, 0);<br>
-    checkDQP(md.bestMode->cu, cuGeom);<br>
+    checkDQP(*md.bestMode, cuGeom);<br>
     X265_CHECK(md.bestMode->ok(), "Merge mode not ok\n");<br>
 }<br>
<br>
@@ -1504,7 +1504,7 @@<br>
         bestPred->cu.setPUMv(1, candMvField[bestCand][1].mv, 0, 0);<br>
         bestPred->cu.setPURefIdx(0, (int8_t)candMvField[bestCand][0].refIdx, 0, 0);<br>
         bestPred->cu.setPURefIdx(1, (int8_t)candMvField[bestCand][1].refIdx, 0, 0);<br>
-        checkDQP(bestPred->cu, cuGeom);<br>
+        checkDQP(*bestPred, cuGeom);<br>
         X265_CHECK(bestPred->ok(), "merge mode is not ok");<br>
     }<br>
<br>
diff -r 30e209c9bab6 -r 5e944fdd03bb source/encoder/entropy.h<br>
--- a/source/encoder/entropy.h  Mon Mar 23 14:23:42 2015 +0530<br>
+++ b/source/encoder/entropy.h  Mon Apr 06 15:39:07 2015 +0530<br>
@@ -179,7 +179,7 @@<br>
     inline void codeQtCbfChroma(uint32_t cbf, uint32_t tuDepth)           { encodeBin(cbf, m_contextState[OFF_QT_CBF_CTX + 2 + tuDepth]); }<br>
     inline void codeQtRootCbf(uint32_t cbf)                               { encodeBin(cbf, m_contextState[OFF_QT_ROOT_CBF_CTX]); }<br>
     inline void codeTransformSkipFlags(uint32_t transformSkip, TextType ttype) { encodeBin(transformSkip, m_contextState[OFF_TRANSFORMSKIP_FLAG_CTX + (ttype ? NUM_TRANSFORMSKIP_FLAG_CTX : 0)]); }<br>
-<br>
+    void codeDeltaQP(const CUData& cu, uint32_t absPartIdx);<br>
     void codeSaoOffset(const SaoCtuParam& ctuParam, int plane);<br>
<br>
     /* RDO functions */<br>
@@ -242,7 +242,6 @@<br>
<br>
     void codeSaoMaxUvlc(uint32_t code, uint32_t maxSymbol);<br>
<br>
-    void codeDeltaQP(const CUData& cu, uint32_t absPartIdx);<br>
     void codeLastSignificantXY(uint32_t posx, uint32_t posy, uint32_t log2TrSize, bool bIsLuma, uint32_t scanIdx);<br>
<br>
     void encodeTransform(const CUData& cu, uint32_t absPartIdx, uint32_t tuDepth, uint32_t log2TrSize,<br>
diff -r 30e209c9bab6 -r 5e944fdd03bb source/encoder/search.cpp<br>
--- a/source/encoder/search.cpp Mon Mar 23 14:23:42 2015 +0530<br>
+++ b/source/encoder/search.cpp Mon Apr 06 15:39:07 2015 +0530<br>
@@ -1185,7 +1185,7 @@<br>
         intraMode.psyEnergy = m_rdCost.psyCost(cuGeom.log2CUSize - 2, fencYuv->m_buf[0], fencYuv->m_size, intraMode.reconYuv.m_buf[0], intraMode.reconYuv.m_size);<br>
     }<br>
     updateModeCost(intraMode);<br>
-    checkDQP(cu, cuGeom);<br>
+    checkDQP(intraMode, cuGeom);<br>
 }<br>
<br>
 /* Note that this function does not save the best intra prediction, it must<br>
@@ -1400,7 +1400,7 @@<br>
<br>
     m_entropyCoder.store(intraMode.contexts);<br>
     updateModeCost(intraMode);<br>
-    checkDQP(intraMode.cu, cuGeom);<br>
+    checkDQP(intraMode, cuGeom);<br>
 }<br>
<br>
 uint32_t Search::estIntraPredQT(Mode &intraMode, const CUGeom& cuGeom, const uint32_t depthRange[2], uint8_t* sharedModes)<br>
@@ -2620,7 +2620,7 @@<br>
     interMode.coeffBits = coeffBits;<br>
     interMode.mvBits = bits - coeffBits;<br>
     updateModeCost(interMode);<br>
-    checkDQP(interMode.cu, cuGeom);<br>
+    checkDQP(interMode, cuGeom);<br>
 }<br>
<br>
 void Search::residualTransformQuantInter(Mode& mode, const CUGeom& cuGeom, uint32_t absPartIdx, uint32_t tuDepth, const uint32_t depthRange[2])<br>
@@ -3437,22 +3437,29 @@<br>
     }<br>
 }<br>
<br>
-void Search::checkDQP(CUData& cu, const CUGeom& cuGeom)<br>
+void Search::checkDQP(Mode& mode, const CUGeom& cuGeom)<br>
 {<br>
+    CUData& cu = <a href="http://mode.cu" target="_blank">mode.cu</a>;<br>
     if (cu.m_slice->m_pps->bUseDQP && cuGeom.depth <= cu.m_slice->m_pps->maxCuDQPDepth)<br>
     {<br>
         if (cu.getQtRootCbf(0))<br>
         {<br>
-            /* When analysing RDO with DQP bits, the entropy encoder should add the cost of DQP bits here<br>
-             * i.e Encode QP */<br>
+            mode.contexts.resetBits();<br>
+            mode.contexts.codeDeltaQP(cu, 0);<br>
+            uint32_t bits = mode.contexts.getNumberOfWrittenBits();<br>
+            mode.mvBits += bits;<br>
+            mode.totalBits += bits;<br>
+            updateModeCost(mode);<br>
         }<br>
         else<br>
             cu.setQPSubParts(cu.getRefQP(0), 0, cuGeom.depth);<br>
     }<br>
 }<br>
<br>
-void Search::checkDQPForSplitPred(CUData& cu, const CUGeom& cuGeom)<br>
+void Search::checkDQPForSplitPred(Mode& mode, const CUGeom& cuGeom)<br>
 {<br>
+    CUData& cu = <a href="http://mode.cu" target="_blank">mode.cu</a>;<br>
+<br>
     if ((cuGeom.depth == cu.m_slice->m_pps->maxCuDQPDepth) && cu.m_slice->m_pps->bUseDQP)<br>
     {<br>
         bool hasResidual = false;<br>
@@ -3467,10 +3474,17 @@<br>
             }<br>
         }<br>
         if (hasResidual)<br>
-            /* TODO: Encode QP, and recalculate RD cost of splitPred */<br>
+        {<br>
+            mode.contexts.resetBits();<br>
+            mode.contexts.codeDeltaQP(cu, 0);<br>
+            uint32_t bits = mode.contexts.getNumberOfWrittenBits();<br>
+            mode.mvBits += bits;<br>
+            mode.totalBits += bits;<br>
+            updateModeCost(mode);<br>
             /* For all zero CBF sub-CUs, reset QP to RefQP (so that deltaQP is not signalled).<br>
             When the non-zero CBF sub-CU is found, stop */<br>
             cu.setQPSubCUs(cu.getRefQP(0), 0, cuGeom.depth);<br>
+        }<br>
         else<br>
             /* No residual within this CU or subCU, so reset QP to RefQP */<br>
             cu.setQPSubParts(cu.getRefQP(0), 0, cuGeom.depth);<br>
diff -r 30e209c9bab6 -r 5e944fdd03bb source/encoder/search.h<br>
--- a/source/encoder/search.h   Mon Mar 23 14:23:42 2015 +0530<br>
+++ b/source/encoder/search.h   Mon Apr 06 15:39:07 2015 +0530<br>
@@ -316,8 +316,8 @@<br>
     void     getBestIntraModeChroma(Mode& intraMode, const CUGeom& cuGeom);<br>
<br>
     /* update CBF flags and QP values to be internally consistent */<br>
-    void checkDQP(CUData& cu, const CUGeom& cuGeom);<br>
-    void checkDQPForSplitPred(CUData& cu, const CUGeom& cuGeom);<br>
+    void checkDQP(Mode& mode, const CUGeom& cuGeom);<br>
+    void checkDQPForSplitPred(Mode& mode, const CUGeom& cuGeom);<br>
<br>
     class PME : public BondedTaskGroup<br>
     {<br>
</blockquote></div><br></div></div></div></div>