diff --git a/source/Lib/CommonLib/CommonDef.h b/source/Lib/CommonLib/CommonDef.h
index 73acf278f2526bfa406a049aa6ffbbe5ef86dce7..e659e43397c33bff3d1cae7bc5fa3ad3aa6c7508 100644
--- a/source/Lib/CommonLib/CommonDef.h
+++ b/source/Lib/CommonLib/CommonDef.h
@@ -920,7 +920,7 @@ static const int ADAPTIVE_SUB_GROUP_SIZE_MMVD_AFF = AF_MMVD_MAX_REFINE_NUM;
 #endif
 #endif
 
-#if JVET_AA0057_CCCM
+#if JVET_AA0057_CCCM || JVET_AB0092_GLM_WITH_LUMA
 static const int CCCM_WINDOW_SIZE         = 6;
 static const int CCCM_NUM_PARAMS          = 7;
 static const int CCCM_MIN_PU_SIZE         = 0; // Set to 0 for no size restriction
@@ -938,6 +938,17 @@ static const int CCCM_DECIM_ROUND         = ( 1 << (CCCM_DECIM_BITS - 1 ) );
 #endif
 
 #if JVET_AA0126_GLM
+#if JVET_AB0092_GLM_WITH_LUMA
+#define NUM_GLM_WEIGHT                                              2
+#if JVET_AA0057_CCCM
+static const int NUM_GLM_PATTERN =                                  4;
+static const int NUM_GLM_IDC =                                      5;
+#else
+static const int NUM_GLM_PATTERN =                                 16;
+static const int NUM_GLM_PATTERN_BITS =                             4;
+static const int NUM_GLM_IDC =                                     17;
+#endif
+#else
 #if JVET_AA0057_CCCM
 #define NUM_GLM_WEIGHT                                              0
 static const int NUM_GLM_PATTERN =                                  4;
@@ -950,6 +961,7 @@ static const int NUM_GLM_PATTERN_BITS =                             4;
 static const int NUM_GLM_IDC =                                     33;
 #endif
 #endif
+#endif
 
 #if JVET_Y0152_TT_ENC_SPEEDUP
 static constexpr int   FAST_METHOD_TT_ENC_SPEEDUP = 0x0001;  ///< Embedding flag, which, if false, de-activates all the following ABT_ENC_SPEEDUP_* modes
diff --git a/source/Lib/CommonLib/IntraPrediction.cpp b/source/Lib/CommonLib/IntraPrediction.cpp
index 433cfdff5dc5e45f3c6fbac9d311242ea9042ccd..9d9afd03cb7b7cb474b34995f51ee7d2b69374b0 100644
--- a/source/Lib/CommonLib/IntraPrediction.cpp
+++ b/source/Lib/CommonLib/IntraPrediction.cpp
@@ -111,6 +111,9 @@ IntraPrediction::IntraPrediction()
   {
     m_glmTempCb[i] = nullptr;
     m_glmTempCr[i] = nullptr;
+#if JVET_AB0092_GLM_WITH_LUMA
+    m_glmGradBuf[i] = nullptr;
+#endif
   }
 #endif
 #if MMLM
@@ -154,6 +157,9 @@ void IntraPrediction::destroy()
   {
     delete[] m_glmTempCb[i]; m_glmTempCb[i] = nullptr;
     delete[] m_glmTempCr[i]; m_glmTempCr[i] = nullptr;
+#if JVET_AB0092_GLM_WITH_LUMA
+    delete[] m_glmGradBuf[i]; m_glmGradBuf[i] = nullptr;
+#endif
   }
 #endif
 
@@ -251,8 +257,20 @@ void IntraPrediction::init(ChromaFormat chromaFormatIDC, const unsigned bitDepth
 #if JVET_AA0126_GLM
   for (int i = 0; i < NUM_GLM_IDC; i++)
   {
-    if (m_glmTempCb[i] == nullptr) m_glmTempCb[i] = new Pel[(2 * MAX_CU_SIZE + 1)*(2 * MAX_CU_SIZE + 1)];
-    if (m_glmTempCr[i] == nullptr) m_glmTempCr[i] = new Pel[(2 * MAX_CU_SIZE + 1)*(2 * MAX_CU_SIZE + 1)];
+    if (m_glmTempCb[i] == nullptr)
+    {
+      m_glmTempCb[i] = new Pel[(2 * MAX_CU_SIZE + 1)*(2 * MAX_CU_SIZE + 1)];
+    }
+    if (m_glmTempCr[i] == nullptr)
+    {
+      m_glmTempCr[i] = new Pel[(2 * MAX_CU_SIZE + 1)*(2 * MAX_CU_SIZE + 1)];
+    }
+#if JVET_AB0092_GLM_WITH_LUMA
+    if (m_glmGradBuf[i] == nullptr)
+    {
+      m_glmGradBuf[i] = new Pel[(2 * MAX_CU_SIZE + CCCM_WINDOW_SIZE) * (2 * MAX_CU_SIZE + CCCM_WINDOW_SIZE)];
+    }
+#endif
   }
 #endif
 
@@ -1174,6 +1192,17 @@ void IntraPrediction::xUpdateCclmModel(int &a, int &b, int &iShift, int midLuma,
 
 void IntraPrediction::predIntraChromaLM(const ComponentID compID, PelBuf &piPred, const PredictionUnit &pu, const CompArea& chromaArea, int intraDir, bool createModel, CclmModel *cclmModelStored)
 {
+#if JVET_AB0092_GLM_WITH_LUMA
+  if (pu.glmIdc.getIdc(compID, 0) > NUM_GLM_PATTERN)
+  {
+    CccmModel glmModel(pu.cu->slice->getSPS()->getBitDepth(CHANNEL_TYPE_LUMA));
+    xGlmCalcModel(pu, compID, chromaArea, glmModel);
+    xGlmApplyModel(pu, compID, chromaArea, glmModel, piPred);
+    return;
+  }
+  else
+  {
+#endif
   int  iLumaStride = 0;
   PelBuf Temp;
 #if JVET_AA0126_GLM
@@ -1188,35 +1217,35 @@ void IntraPrediction::predIntraChromaLM(const ComponentID compID, PelBuf &piPred
   {
 #endif
 #if MMLM
-  if ((intraDir == MDLM_L_IDX) || (intraDir == MDLM_T_IDX) || (intraDir == MMLM_L_IDX) || (intraDir == MMLM_T_IDX) || (m_encPreRDRun && intraDir == MMLM_CHROMA_IDX))
+    if ((intraDir == MDLM_L_IDX) || (intraDir == MDLM_T_IDX) || (intraDir == MMLM_L_IDX) || (intraDir == MMLM_T_IDX) || (m_encPreRDRun && intraDir == MMLM_CHROMA_IDX))
 #else
-  if ((intraDir == MDLM_L_IDX) || (intraDir == MDLM_T_IDX))
+    if ((intraDir == MDLM_L_IDX) || (intraDir == MDLM_T_IDX))
 #endif
-  {
-    iLumaStride = 2 * MAX_CU_SIZE + 1;
-    Temp = PelBuf(m_pMdlmTemp + iLumaStride + 1, iLumaStride, Size(chromaArea));
-  }
-  else
-  {
-    iLumaStride = MAX_CU_SIZE + 1;
-    Temp = PelBuf(m_piTemp + iLumaStride + 1, iLumaStride, Size(chromaArea));
-  }
+    {
+      iLumaStride = 2 * MAX_CU_SIZE + 1;
+      Temp = PelBuf(m_pMdlmTemp + iLumaStride + 1, iLumaStride, Size(chromaArea));
+    }
+    else
+    {
+      iLumaStride = MAX_CU_SIZE + 1;
+      Temp = PelBuf(m_piTemp + iLumaStride + 1, iLumaStride, Size(chromaArea));
+    }
 #if JVET_AA0126_GLM
   }
 #endif
 
   CclmModel cclmModel;
 
-  if ( createModel )
+  if (createModel)
   {
 #if LMS_LINEAR_MODEL
     xGetLMParametersLMS(pu, compID, chromaArea, cclmModel);
 #else
-    xGetLMParameters    (pu, compID, chromaArea, cclmModel);
+    xGetLMParameters(pu, compID, chromaArea, cclmModel);
 #endif
-    
+
     // Store the created model if storage struct was provided
-    if ( cclmModelStored != nullptr )
+    if (cclmModelStored != nullptr)
     {
       *cclmModelStored = cclmModel;
     }
@@ -1228,12 +1257,12 @@ void IntraPrediction::predIntraChromaLM(const ComponentID compID, PelBuf &piPred
   }
 
 #if JVET_Z0050_CCLM_SLOPE
-  xUpdateCclmModel( cclmModel.a,  cclmModel.b,  cclmModel.shift,  cclmModel.midLuma,  compID == COMPONENT_Cb ? pu.cclmOffsets.cb0 : pu.cclmOffsets.cr0 );
+  xUpdateCclmModel(cclmModel.a, cclmModel.b, cclmModel.shift, cclmModel.midLuma, compID == COMPONENT_Cb ? pu.cclmOffsets.cb0 : pu.cclmOffsets.cr0);
 #if MMLM
-  xUpdateCclmModel( cclmModel.a2, cclmModel.b2, cclmModel.shift2, cclmModel.midLuma2, compID == COMPONENT_Cb ? pu.cclmOffsets.cb1 : pu.cclmOffsets.cr1 );
+  xUpdateCclmModel(cclmModel.a2, cclmModel.b2, cclmModel.shift2, cclmModel.midLuma2, compID == COMPONENT_Cb ? pu.cclmOffsets.cb1 : pu.cclmOffsets.cr1);
 #endif
 #endif
-  
+
   ////// final prediction
   piPred.copyFrom(Temp);
 #if MMLM
@@ -1264,7 +1293,10 @@ void IntraPrediction::predIntraChromaLM(const ComponentID compID, PelBuf &piPred
   }
   else
 #endif
-  piPred.linearTransform(cclmModel.a, cclmModel.shift, cclmModel.b, true, pu.cs->slice->clpRng(compID));
+    piPred.linearTransform(cclmModel.a, cclmModel.shift, cclmModel.b, true, pu.cs->slice->clpRng(compID));
+#if JVET_AB0092_GLM_WITH_LUMA
+  }
+#endif
 }
 
 /** Function for deriving planar intra prediction. This function derives the prediction samples for planar mode (intra coding).
@@ -4781,7 +4813,7 @@ Pel IntraPrediction::xGlmGetLumaVal(const int s[6], const int c[6], const int gl
 {
   Pel grad = c[0] * s[0] + c[1] * s[1] + c[2] * s[2] 
            + c[3] * s[3] + c[4] * s[4] + c[5] * s[5];
-#if NUM_GLM_WEIGHT
+#if NUM_GLM_WEIGHT && !JVET_AB0092_GLM_WITH_LUMA
   return (glmIdx >= NUM_GLM_PATTERN ? val + grad : grad);
 #else
   return grad;
@@ -4790,6 +4822,47 @@ Pel IntraPrediction::xGlmGetLumaVal(const int s[6], const int c[6], const int gl
 
 void IntraPrediction::xGetLumaRecPixelsGlmAll(const PredictionUnit &pu, CompArea chromaArea)
 {
+#if JVET_AB0092_GLM_WITH_LUMA
+  const CPelBuf recoLuma = pu.cs->picture->getRecoBuf(COMPONENT_Y);
+  const int  maxPosPicX = pu.cs->picture->chromaSize().width - 1;
+  const int  maxPosPicY = pu.cs->picture->chromaSize().height - 1;
+
+  xGlmCalcRefArea(pu, chromaArea); // Find the reference area
+
+  int areaWidth, areaHeight, refSizeX, refSizeY, refPosPicX, refPosPicY;
+
+  PelBuf refGrad[NUM_GLM_IDC];
+  for (int i = 0; i < NUM_GLM_IDC; i++)
+  {
+    refGrad[i] = xGlmGetGradRefBuf(pu, chromaArea, areaWidth, areaHeight, refSizeX, refSizeY, refPosPicX, refPosPicY, i);
+  }
+
+  int puBorderX = refSizeX + chromaArea.width;
+  int puBorderY = refSizeY + chromaArea.height;
+
+  // Generate down-sampled luma and luma gradients
+  for (int y = 0; y < areaHeight; y++)
+  {
+    for (int x = 0; x < areaWidth; x++)
+    {
+      if ((x >= puBorderX && y >= refSizeY) || (y >= puBorderY && x >= refSizeX))
+      {
+        continue;
+      }
+
+      int chromaPosPicX = refPosPicX + x;
+      int chromaPosPicY = refPosPicY + y;
+
+      chromaPosPicX = chromaPosPicX < 0 ? 0 : chromaPosPicX > maxPosPicX ? maxPosPicX : chromaPosPicX;
+      chromaPosPicY = chromaPosPicY < 0 ? 0 : chromaPosPicY > maxPosPicY ? maxPosPicY : chromaPosPicY;
+
+      for (int i = 0; i < NUM_GLM_IDC; i++)
+      {
+        refGrad[i].at(x, y) = xGlmGetGradVal(pu, i, recoLuma, chromaPosPicX, chromaPosPicY);
+      }
+    }
+  }
+#endif
   int c[6] = { 0 };
 
   int iDstStride = 2 * MAX_CU_SIZE + 1;
@@ -5004,6 +5077,14 @@ void IntraPrediction::xGetLumaRecPixels(const PredictionUnit &pu, CompArea chrom
   }
 #endif
 
+#if JVET_AB0092_GLM_WITH_LUMA
+  if (pu.glmIdc.getIdc(chromaArea.compID, 0) > NUM_GLM_PATTERN)
+  {
+    xGlmCreateGradRef(pu, chromaArea);
+    return;
+  }
+#endif
+
 #if JVET_AA0126_GLM
   ComponentID compID = chromaArea.compID;
   int glmIdc = pu.glmIdc.getIdc(compID, 0);
@@ -7897,7 +7978,9 @@ void IntraPrediction::xCccmCreateLumaRef(const PredictionUnit& pu, CompArea chro
     }
   }
 }
+#endif
 
+#if JVET_AA0057_CCCM || JVET_AB0092_GLM_WITH_LUMA
 // LDL decomposing A to U'*diag*U
 bool CccmCovarianceInt::ldlDecomp(TE A, TE U, Ty diag, int numEq) const
 {
@@ -8017,4 +8100,409 @@ void CccmCovarianceInt::ldlSolve(TE U, Ty diag, TCccmCoeff* y, TCccmCoeff* x, in
 }
 #endif
 
+#if JVET_AB0092_GLM_WITH_LUMA
+void IntraPrediction::xGlmApplyModel(const PredictionUnit& pu, const ComponentID compId, const CompArea& chromaArea, CccmModel &glmModel, PelBuf &piPred) const
+{
+  const  ClpRng& clpRng(pu.cu->cs->slice->clpRng(compId));
+  static Pel     samples[CCCM_NUM_PARAMS];
+
+  CPelBuf refLumaBlk = xGlmGetGradPuBuf(pu, chromaArea, 0);
+  CPelBuf refGradBlk = xGlmGetGradPuBuf(pu, chromaArea, pu.glmIdc.getIdc(compId, 0));
+
+  for (int y = 0; y < refLumaBlk.height; y++)
+  {
+    for (int x = 0; x < refLumaBlk.width; x++)
+    {
+
+      samples[0] = refGradBlk.at(x, y); // luma gradient
+      samples[1] = refLumaBlk.at(x, y); // luma value
+      samples[2] = glmModel.bias();
+      samples[3] = 0;
+      samples[4] = 0;
+      samples[5] = 0;
+      samples[6] = 0;
+
+      piPred.at(x, y) = ClipPel<Pel>(glmModel.convolve(samples, CCCM_NUM_PARAMS), clpRng);
+    }
+  }
+}
+
+void IntraPrediction::xGlmCalcModel(const PredictionUnit& pu, const ComponentID compID, const CompArea& chromaArea, CccmModel &glmModel) const
+{
+  int areaWidth, areaHeight, refSizeX, refSizeY, refPosPicX, refPosPicY;
+
+  const CPelBuf reco = pu.cs->picture->getRecoBuf(compID);
+  PelBuf        refLuma = xGlmGetGradRefBuf(pu, chromaArea, areaWidth, areaHeight, refSizeX, refSizeY, refPosPicX, refPosPicY, 0);
+  PelBuf        refGrad = xGlmGetGradRefBuf(pu, chromaArea, areaWidth, areaHeight, refSizeX, refSizeY, refPosPicX, refPosPicY, pu.glmIdc.getIdc(compID, 0));
+
+  int M = CCCM_NUM_PARAMS; // align CCCM parameter number to reuse CCCM LDL method
+#if JVET_AB0174_CCCM_DIV_FREE
+  int N = 3; 
+#endif
+
+  int sampleNum = 0;
+  int sampleInd = 0;
+
+#if JVET_AB0174_CCCM_DIV_FREE
+  int chromaOffset = 1 << (pu.cu->slice->getSPS()->getBitDepth(CHANNEL_TYPE_CHROMA) - 1);
+
+  if (refSizeX || refSizeY)
+  {
+    int refPosX = refSizeX > 0 ? refSizeX - 1 : 0;
+    int refPosY = refSizeY > 0 ? refSizeY - 1 : 0;
+
+    chromaOffset = reco.at(refPosPicX + refPosX, refPosPicY + refPosY);
+  }
+#endif
+
+  // Collect reference data to input matrix A and target vector C
+  static Pel A[CCCM_NUM_PARAMS][CCCM_MAX_REF_SAMPLES];
+  static Pel C[CCCM_MAX_REF_SAMPLES];
+
+  int sizeX = refSizeX + chromaArea.width;
+  int sizeY = refSizeY + chromaArea.height;
+
+  for (int y = 0; y < areaHeight; y++)
+  {
+    for (int x = 0; x < areaWidth; x++)
+    {
+      if (x >= refSizeX && y >= refSizeY)
+      {
+        continue;
+      }
+      if (pu.intraDir[1] == MDLM_L_IDX)
+      {
+        if (y < refSizeY)
+        {
+          continue;
+        }
+      }
+      else if (pu.intraDir[1] == MDLM_T_IDX)
+      {
+        if (x < refSizeX)
+        {
+          continue;
+        }
+      }
+      else
+      {
+        if (x >= sizeX || y >= sizeY)
+        {
+          continue;
+        }
+      }
+
+      // 7-tap cross
+      A[0][sampleInd] = refGrad.at(x, y); // luma gradient
+      A[1][sampleInd] = refLuma.at(x, y); // luma value
+      A[2][sampleInd] = glmModel.bias();
+      A[3][sampleInd] = 0;
+      A[4][sampleInd] = 0;
+      A[5][sampleInd] = 0;
+      A[6][sampleInd] = 0;
+
+      C[sampleInd] = reco.at(refPosPicX + x, refPosPicY + y);
+      sampleInd++;
+    }
+  }
+  if (sampleInd == 0) // Number of sample can go to zero in the multimode case
+  {
+    glmModel.clearModel(M);
+    return;
+  }
+  else
+  {
+    sampleNum = sampleInd;
+  }
+
+  // Calculate autocorrelation matrix and cross-correlation vector
+  static CccmCovarianceInt::TE ATA;
+  static CccmCovarianceInt::Ty ATC;
+
+  memset(ATA, 0x00, sizeof(int64_t) * CCCM_NUM_PARAMS * CCCM_NUM_PARAMS);
+  memset(ATC, 0x00, sizeof(int64_t) * CCCM_NUM_PARAMS);
+
+  for (int coli0 = 0; coli0 < M; coli0++)
+  {
+    for (int coli1 = coli0; coli1 < M; coli1++)
+    {
+      Pel *col0 = A[coli0];
+      Pel *col1 = A[coli1];
+
+      for (int rowi = 0; rowi < sampleNum; rowi++)
+      {
+        ATA[coli0][coli1] += col0[rowi] * col1[rowi];
+      }
+    }
+  }
+
+  for (int coli = 0; coli < M; coli++)
+  {
+    Pel *col = A[coli];
+
+    for (int rowi = 0; rowi < sampleNum; rowi++)
+    {
+      ATC[coli] += col[rowi] * C[rowi];
+    }
+  }
+
+#if JVET_AB0174_CCCM_DIV_FREE
+  // Remove chromaOffset from stats to update cross-correlation
+  for (int coli = 0; coli < N; coli++)
+  {
+    ATC[coli] = ATC[coli] - ((ATA[coli][N - 1] * chromaOffset) >> (glmModel.bd - 1));
+  }
+#endif
+
+  // Scale the matrix and vector to selected dynamic range
+  int matrixShift = 28 - 2 * pu.cu->cs->sps->getBitDepth(CHANNEL_TYPE_CHROMA) - ceilLog2(sampleNum);
+
+  if (matrixShift > 0)
+  {
+    for (int coli0 = 0; coli0 < M; coli0++)
+    {
+      for (int coli1 = coli0; coli1 < M; coli1++)
+      {
+        ATA[coli0][coli1] <<= matrixShift;
+      }
+    }
+
+    for (int coli = 0; coli < M; coli++)
+    {
+      ATC[coli] <<= matrixShift;
+    }
+  }
+  else if (matrixShift < 0)
+  {
+    matrixShift = -matrixShift;
+
+    for (int coli0 = 0; coli0 < M; coli0++)
+    {
+      for (int coli1 = coli0; coli1 < M; coli1++)
+      {
+        ATA[coli0][coli1] >>= matrixShift;
+      }
+    }
+
+    for (int coli = 0; coli < M; coli++)
+    {
+      ATC[coli] >>= matrixShift;
+    }
+  }
+
+  // Solve the filter coefficients using LDL decomposition
+  CccmCovarianceInt glmSolver;
+  CccmCovarianceInt::TE U;       // Upper triangular L' of ATA's LDL decomposition
+  CccmCovarianceInt::Ty diag;    // Diagonal of D
+
+  bool decompOk = glmSolver.ldlDecompose(ATA, U, diag, M);
+  glmSolver.ldlSolve(U, diag, ATC, glmModel.params, M, decompOk);
+
+#if JVET_AB0174_CCCM_DIV_FREE
+  // Add the chroma offset to bias term (after shifting up by CCCM_DECIM_BITS and down by cccmModelCb.bd - 1)
+  glmModel.params[N - 1] += chromaOffset << (CCCM_DECIM_BITS - (glmModel.bd - 1));
+#endif
+}
+
+Pel IntraPrediction::xGlmGetGradVal(const PredictionUnit& pu, const int glmIdx, const CPelBuf pi, const int x, const int y) const
+{
+  const Pel* piSrc = pi.buf;
+  const int iRecStride = pi.stride;
+  Pel ypval = 0;
+  if (glmIdx == 0)
+  {
+    if (pu.chromaFormat == CHROMA_444)
+    {
+      ypval = piSrc[x + iRecStride * y];
+    }
+    else if (pu.chromaFormat == CHROMA_422)
+    {
+      int s = 2;
+      int offLeft = x > 0 ? -1 : 0;
+      s += piSrc[2 * x + iRecStride * y] * 2;
+      s += piSrc[2 * x + offLeft + iRecStride * y];
+      s += piSrc[2 * x + 1 + iRecStride * y];
+      ypval = s >> 2;
+    }
+    else if (pu.cs->sps->getCclmCollocatedChromaFlag())
+    {
+      int s = 4;
+      int offLeft = x > 0 ? -1 : 0;
+      int offAbove = y > 0 ? -1 : 0;
+      s += piSrc[2 * x + iRecStride * 2 * y] * 4;
+      s += piSrc[2 * x + offLeft + iRecStride * 2 * y];
+      s += piSrc[2 * x + 1 + iRecStride * 2 * y];
+      s += piSrc[2 * x + iRecStride * (2 * y + 1)];
+      s += piSrc[2 * x + iRecStride * (2 * y + offAbove)];
+      ypval = s >> 3;
+    }
+    else
+    {
+      int s = 4;
+      int offLeft = x > 0 ? -1 : 0;
+      s += piSrc[2 * x + iRecStride * y * 2] * 2;
+      s += piSrc[2 * x + offLeft + iRecStride * y * 2];
+      s += piSrc[2 * x + 1 + iRecStride * y * 2];
+      s += piSrc[2 * x + iRecStride * (y * 2 + 1)] * 2;
+      s += piSrc[2 * x + offLeft + iRecStride * (y * 2 + 1)];
+      s += piSrc[2 * x + 1 + iRecStride * (y * 2 + 1)];
+      ypval = s >> 3;
+    }
+#if JVET_AB0174_CCCM_DIV_FREE
+    ypval -= m_glmLumaOffset;
+#endif
+  }
+  else
+  {
+    int p = glmIdx > NUM_GLM_PATTERN ? glmIdx - NUM_GLM_PATTERN - 1 : glmIdx - 1;
+    int c[6] = { 0 };
+    c[0] = g_glmPattern[p][0], c[1] = g_glmPattern[p][1], c[2] = g_glmPattern[p][2];
+    c[3] = g_glmPattern[p][3], c[4] = g_glmPattern[p][4], c[5] = g_glmPattern[p][5];
+
+    int offLeft = x > 0 ? -1 : 0;
+    int s[6] = { piSrc[2 * x + offLeft + iRecStride * y * 2], piSrc[2 * x + iRecStride * y * 2], piSrc[2 * x + 1 + iRecStride * y * 2],
+                 piSrc[2 * x + offLeft + iRecStride * (y * 2 + 1)], piSrc[2 * x + iRecStride * (y * 2 + 1)], piSrc[2 * x + 1 + iRecStride * (y * 2 + 1)] };
+
+    ypval = xGlmGetLumaVal(s, c, p + 1, 0);
+  }
+
+  return ypval;
+}
+
+void IntraPrediction::xGlmCalcRefArea(const PredictionUnit& pu, CompArea chromaArea)
+{
+  const ChannelType     chType = CHANNEL_TYPE_CHROMA;
+  const CodingUnit&     cu = *pu.cu;
+  const CodingStructure &cs = *cu.cs;
+  const SPS             &sps = *cs.sps;
+  const PreCalcValues   &pcv = *cs.pcv;
+
+  const int tuWidth = chromaArea.width;
+  const int tuHeight = chromaArea.height;
+
+  const bool noShift = pcv.noChroma2x2 && chromaArea.width == 4;   // don't shift on the lowest level (chroma not-split)
+  const int  compScaleX = getComponentScaleX(chromaArea.compID, sps.getChromaFormatIdc());
+  const int  compScaleY = getComponentScaleY(chromaArea.compID, sps.getChromaFormatIdc());
+  const int  unitWidth = pcv.minCUWidth >> (noShift ? 0 : compScaleX);
+  const int  unitHeight = pcv.minCUHeight >> (noShift ? 0 : compScaleY);
+
+  const int  totalAboveUnits = (2 * tuWidth + (unitWidth - 1)) / unitWidth;
+  const int  totalLeftUnits = (2 * tuHeight + (unitHeight - 1)) / unitHeight;
+  const int  numAboveUnits = std::max<int>(tuWidth / unitWidth, 1);
+  const int  numLeftUnits = std::max<int>(tuHeight / unitHeight, 1);
+  const int  numAboveRightUnits = totalAboveUnits - numAboveUnits;
+  const int  numLeftBelowUnits = totalLeftUnits - numLeftUnits;
+
+  static bool neighborFlags[4 * MAX_NUM_PART_IDXS_IN_CTU_WIDTH + 1] = { false }; // Just a dummy array here, content not used
+
+  int avaiAboveRightUnits = isAboveRightAvailable(cu, chType, chromaArea.topRight(), numAboveRightUnits, unitWidth, (neighborFlags + totalLeftUnits + 1 + numAboveUnits));
+  int avaiLeftBelowUnits = isBelowLeftAvailable(cu, chType, chromaArea.bottomLeft(), numLeftBelowUnits, unitHeight, (neighborFlags + totalLeftUnits - 1 - numLeftUnits));
+
+  int refSizeX, refSizeY;
+
+  PU::getCccmRefLineNum(pu, chromaArea, refSizeX, refSizeY); // Reference lines available left and above
+
+  int refWidth = chromaArea.width + refSizeX;              // Reference buffer size excluding paddings
+  int refHeight = chromaArea.height + refSizeY;
+
+  int extWidth = avaiAboveRightUnits * unitWidth;
+  int extHeight = avaiLeftBelowUnits * unitHeight;
+
+  refWidth += refSizeY ? extWidth : 0; // Add above right if above is available
+  refHeight += refSizeX ? extHeight : 0; // Add below left if left is available
+
+  m_glmRefArea = Area(chromaArea.x - refSizeX, chromaArea.y - refSizeY, refWidth, refHeight); // Position with respect to the PU
+}
+
+PelBuf IntraPrediction::xGlmGetGradRefBuf(const PredictionUnit& pu, CompArea chromaArea, int &areaWidth, int &areaHeight, int &refSizeX, int &refSizeY, int &refPosPicX, int &refPosPicY, int glmIdx) const
+{
+  refSizeX = chromaArea.x - m_glmRefArea.x;                        // Reference lines available left and above
+  refSizeY = chromaArea.y - m_glmRefArea.y;
+  areaWidth = m_glmRefArea.width;                    // Reference buffer size excluding paddings
+  areaHeight = m_glmRefArea.height;
+  refPosPicX = m_glmRefArea.x; // Position of the reference area in picture coordinates
+  refPosPicY = m_glmRefArea.y;
+
+  int refStride = areaWidth;
+
+  int idx = glmIdx > NUM_GLM_PATTERN ? glmIdx - NUM_GLM_PATTERN : glmIdx;
+  return PelBuf(m_glmGradBuf[idx], refStride, areaWidth, areaHeight);
+}
+
+PelBuf IntraPrediction::xGlmGetGradPuBuf(const PredictionUnit& pu, CompArea chromaArea, int glmIdx) const
+{
+  int refSizeX = chromaArea.x - m_glmRefArea.x; // Reference lines available left and above
+  int refSizeY = chromaArea.y - m_glmRefArea.y;
+  int tuWidth = chromaArea.width;
+  int tuHeight = chromaArea.height;
+  int refStride = m_glmRefArea.width;
+  int refOrigin = refStride * refSizeY + refSizeX;
+
+  int idx = glmIdx > NUM_GLM_PATTERN ? glmIdx - NUM_GLM_PATTERN : glmIdx;
+  return PelBuf(m_glmGradBuf[idx] + refOrigin, refStride, tuWidth, tuHeight);
+}
+
+void IntraPrediction::xGlmCreateGradRef(const PredictionUnit& pu, CompArea chromaArea)
+{
+  const CPelBuf recoLuma = pu.cs->picture->getRecoBuf(COMPONENT_Y);
+  const int  maxPosPicX = pu.cs->picture->chromaSize().width - 1;
+  const int  maxPosPicY = pu.cs->picture->chromaSize().height - 1;
+
+  xGlmCalcRefArea(pu, chromaArea); // Find the reference area
+
+  int areaWidth, areaHeight, refSizeX, refSizeY, refPosPicX, refPosPicY;
+
+  PelBuf refLuma = xGlmGetGradRefBuf(pu, chromaArea, areaWidth, areaHeight, refSizeX, refSizeY, refPosPicX, refPosPicY, 0);
+  PelBuf refGrad = xGlmGetGradRefBuf(pu, chromaArea, areaWidth, areaHeight, refSizeX, refSizeY, refPosPicX, refPosPicY, pu.glmIdc.getIdc(chromaArea.compID, 0));
+
+  int puBorderX = refSizeX + chromaArea.width;
+  int puBorderY = refSizeY + chromaArea.height;
+
+#if JVET_AB0174_CCCM_DIV_FREE
+  xGlmSetLumaRefValue(pu, chromaArea);
+#endif
+
+  for (int y = 0; y < areaHeight; y++)
+  {
+    for (int x = 0; x < areaWidth; x++)
+    {
+      if ((x >= puBorderX && y >= refSizeY) ||
+        (y >= puBorderY && x >= refSizeX))
+      {
+        continue;
+      }
+
+      int chromaPosPicX = refPosPicX + x;
+      int chromaPosPicY = refPosPicY + y;
+
+      chromaPosPicX = chromaPosPicX < 0 ? 0 : chromaPosPicX > maxPosPicX ? maxPosPicX : chromaPosPicX;
+      chromaPosPicY = chromaPosPicY < 0 ? 0 : chromaPosPicY > maxPosPicY ? maxPosPicY : chromaPosPicY;
+
+      refLuma.at(x, y) = xGlmGetGradVal(pu, 0, recoLuma, chromaPosPicX, chromaPosPicY);
+      refGrad.at(x, y) = xGlmGetGradVal(pu, pu.glmIdc.getIdc(chromaArea.compID, 0), recoLuma, chromaPosPicX, chromaPosPicY);
+    }
+  }
+}
+
+#if JVET_AB0174_CCCM_DIV_FREE
+void IntraPrediction::xGlmSetLumaRefValue(const PredictionUnit& pu, CompArea chromaArea)
+{
+  int lumaPosX = chromaArea.x << getComponentScaleX(chromaArea.compID, pu.cu->chromaFormat);
+  int lumaPosY = chromaArea.y << getComponentScaleY(chromaArea.compID, pu.cu->chromaFormat);
+
+  if (lumaPosX || lumaPosY)
+  {
+    lumaPosX = lumaPosX ? lumaPosX - 1 : 0;
+    lumaPosY = lumaPosY ? lumaPosY - 1 : 0;
+
+    m_glmLumaOffset = pu.cs->picture->getRecoBuf(COMPONENT_Y).at(lumaPosX, lumaPosY);
+  }
+  else
+  {
+    m_glmLumaOffset = 1 << (pu.cu->slice->getSPS()->getBitDepth(CHANNEL_TYPE_LUMA) - 1);
+  }
+}
+#endif
+#endif
+
 //! \}
diff --git a/source/Lib/CommonLib/IntraPrediction.h b/source/Lib/CommonLib/IntraPrediction.h
index 65cfb9657d49173b593d1b19f1e323648f4ad634..65225dff96000c1ccce2ed826e91ce7cc9c8805f 100644
--- a/source/Lib/CommonLib/IntraPrediction.h
+++ b/source/Lib/CommonLib/IntraPrediction.h
@@ -91,7 +91,7 @@ public:
 typedef short TrainDataType;
 #endif
 
-#if JVET_AA0057_CCCM
+#if JVET_AA0057_CCCM || JVET_AB0092_GLM_WITH_LUMA
 typedef int64_t TCccmCoeff;
 
 #define FIXED_MULT(x, y) TCccmCoeff((int64_t(x)*(y) + CCCM_DECIM_ROUND) >> CCCM_DECIM_BITS )
@@ -228,6 +228,13 @@ private:
 #if JVET_AA0126_GLM
   Pel* m_glmTempCb[NUM_GLM_IDC];
   Pel* m_glmTempCr[NUM_GLM_IDC];
+#if JVET_AB0092_GLM_WITH_LUMA
+  Area m_glmRefArea;
+  Pel* m_glmGradBuf[NUM_GLM_IDC];
+#if JVET_AB0174_CCCM_DIV_FREE
+  int  m_glmLumaOffset;
+#endif
+#endif
 #endif
   MatrixIntraPrediction m_matrixIntraPred;
 
@@ -326,6 +333,18 @@ public:
   void   xCccmSetLumaRefValue     (const PredictionUnit& pu);
 #endif
 #endif
+#if JVET_AB0092_GLM_WITH_LUMA
+  void   xGlmCalcModel            (const PredictionUnit& pu, const ComponentID compId, const CompArea& chromaArea, CccmModel &glmModel) const;
+  void   xGlmApplyModel           (const PredictionUnit& pu, const ComponentID compId, const CompArea& chromaArea, CccmModel &glmModel, PelBuf &piPred) const;
+  void   xGlmCreateGradRef        (const PredictionUnit& pu, CompArea chromaArea);
+  PelBuf xGlmGetGradRefBuf        (const PredictionUnit& pu, CompArea chromaArea, int &areaWidth, int &areaHeight, int &refSizeX, int &refSizeY, int &refPosPicX, int &refPosPicY, int glmIdx) const;
+  PelBuf xGlmGetGradPuBuf         (const PredictionUnit& pu, CompArea chromaArea, int glmIdx) const;
+  Pel    xGlmGetGradVal           (const PredictionUnit& pu, const int glmIdx, const CPelBuf pi, const int x, const int y) const;
+  void   xGlmCalcRefArea          (const PredictionUnit& pu, CompArea chromaArea);
+#if JVET_AB0174_CCCM_DIV_FREE
+  void   xGlmSetLumaRefValue      (const PredictionUnit& pu, CompArea chromaArea);
+#endif
+#endif
 #if ENABLE_DIMD
   static void deriveDimdMode      (const CPelBuf &recoBuf, const CompArea &area, CodingUnit &cu);
 #if JVET_Z0050_DIMD_CHROMA_FUSION && ENABLE_DIMD
diff --git a/source/Lib/CommonLib/TypeDef.h b/source/Lib/CommonLib/TypeDef.h
index 27102bebead0a48bd509a57025782fd9529370fe..9f04d107643a32c94485b1207436faa2b86dd943 100644
--- a/source/Lib/CommonLib/TypeDef.h
+++ b/source/Lib/CommonLib/TypeDef.h
@@ -50,7 +50,6 @@
 #include <assert.h>
 #include <cassert>
 
-
 #define BASE_ENCODER                                      1
 #define BASE_NORMATIVE                                    1
 #define TOOLS                                             1
@@ -138,9 +137,14 @@
 #define JVET_AA0057_CCCM                                  1 // JVET-AA0057: Convolutional cross-component model (CCCM)
 #if JVET_AA0057_CCCM
 #define JVET_AB0143_CCCM_TS                               1 // JVET-AB0143: CCCM template selection
-#define JVET_AB0174_CCCM_DIV_FREE                         1 // JVET-AB0174: CCCM with division free operation
 #endif
 #define JVET_AA0126_GLM                                   1 // JVET-AA0126: Gradient linear model
+#if JVET_AA0126_GLM
+#define JVET_AB0092_GLM_WITH_LUMA                         1 // JVET-AB0092: Gradient linear model with luma value
+#endif
+#if JVET_AA0057_CCCM || JVET_AB0092_GLM_WITH_LUMA
+#define JVET_AB0174_CCCM_DIV_FREE                         1 // JVET-AB0174: CCCM with division free operation
+#endif
 #define JVET_AB0061_ITMP_BV_FOR_IBC                       1 // JVET-AB0061: Storing IntraTMP BV for IBC BV prediction
 
 
diff --git a/source/Lib/CommonLib/UnitTools.cpp b/source/Lib/CommonLib/UnitTools.cpp
index a07442252e5455b71bb80a44e7803f48843edb39..3028d15f6cb83e4f5f7d6e1fcb2ab4d8bda04ef5 100644
--- a/source/Lib/CommonLib/UnitTools.cpp
+++ b/source/Lib/CommonLib/UnitTools.cpp
@@ -1545,7 +1545,7 @@ bool PU::isDMChromaMIP(const PredictionUnit &pu)
 #endif
 }
 
-#if JVET_AA0057_CCCM
+#if JVET_AA0057_CCCM || JVET_AB0092_GLM_WITH_LUMA
 void PU::getCccmRefLineNum(const PredictionUnit& pu, const Area area, int& th, int& tv)
 {
   th = area.x < CCCM_WINDOW_SIZE ? area.x : CCCM_WINDOW_SIZE;
@@ -1560,7 +1560,9 @@ void PU::getCccmRefLineNum(const PredictionUnit& pu, const Area area, int& th, i
     tv = tv > tvMax ? tvMax : tv;
   }
 }
+#endif
 
+#if JVET_AA0057_CCCM
 bool PU::cccmSingleModeAvail(const PredictionUnit& pu, int intraMode)
 {
   const Area area = pu.blocks[COMPONENT_Cb];
diff --git a/source/Lib/CommonLib/UnitTools.h b/source/Lib/CommonLib/UnitTools.h
index 9dca9dd3661d7f39d4d15675c87b2200b704410a..9227d158ff35fa4cbd208110eb107ab5bfe85bc0 100644
--- a/source/Lib/CommonLib/UnitTools.h
+++ b/source/Lib/CommonLib/UnitTools.h
@@ -489,8 +489,10 @@ namespace PU
 #if JVET_AA0126_GLM
   bool hasGlmFlag      (const PredictionUnit &pu, const int mode = -1);
 #endif
-#if JVET_AA0057_CCCM
+#if JVET_AA0057_CCCM || JVET_AB0092_GLM_WITH_LUMA
   void getCccmRefLineNum  (const PredictionUnit& pu, const Area area, int& th, int& tv);
+#endif
+#if JVET_AA0057_CCCM
   bool cccmSingleModeAvail(const PredictionUnit& pu, int intraMode);
   bool cccmMultiModeAvail (const PredictionUnit& pu, int intraMode);
 #if JVET_AB0143_CCCM_TS
diff --git a/source/Lib/DecoderLib/CABACReader.cpp b/source/Lib/DecoderLib/CABACReader.cpp
index 89eec8edce80850e314034cc524c510003c2048d..7803dd7a0d4ce7f2b64f7464834f307894119bfe 100644
--- a/source/Lib/DecoderLib/CABACReader.cpp
+++ b/source/Lib/DecoderLib/CABACReader.cpp
@@ -2255,6 +2255,29 @@ void CABACReader::glmIdc(PredictionUnit& pu)
 
     if ( glmActive )
     {
+#if JVET_AB0092_GLM_WITH_LUMA
+      pu.glmIdc.cb0 = 1;
+#if NUM_GLM_WEIGHT
+      pu.glmIdc.cb0 += m_BinDecoder.decodeBin(Ctx::GlmFlags(1)) ? NUM_GLM_PATTERN : 0;
+#endif
+#if JVET_AA0057_CCCM
+      if (m_BinDecoder.decodeBin(Ctx::GlmFlags(2)))
+      {
+        pu.glmIdc.cb0 += 1;
+        if (m_BinDecoder.decodeBin(Ctx::GlmFlags(3)))
+        {
+          pu.glmIdc.cb0 += 1;
+          if (m_BinDecoder.decodeBin(Ctx::GlmFlags(4)))
+          {
+            pu.glmIdc.cb0 += 1;
+          }
+        }
+      }
+#else
+      pu.glmIdc.cb0 += m_BinDecoder.decodeBinsEP(NUM_GLM_PATTERN_BITS);
+#endif
+      pu.glmIdc.cr0 = pu.glmIdc.cb0;
+#else
       bool bothActive = m_BinDecoder.decodeBin(Ctx::GlmFlags(3));
 
       pu.glmIdc.cb0 = bothActive;
@@ -2281,6 +2304,7 @@ void CABACReader::glmIdc(PredictionUnit& pu)
 #endif
         pu.glmIdc.cr0 += m_BinDecoder.decodeBinsEP(NUM_GLM_PATTERN_BITS);
       }
+#endif
 
 #if MMLM
       if ( PU::isMultiModeLM( pu.intraDir[1] ) )
diff --git a/source/Lib/EncoderLib/CABACWriter.cpp b/source/Lib/EncoderLib/CABACWriter.cpp
index 17c6b97cdba3efbb776eca8aa6749c6acb3100d1..c1f94280388c47a7d5e81a48859124e9043f1328 100644
--- a/source/Lib/EncoderLib/CABACWriter.cpp
+++ b/source/Lib/EncoderLib/CABACWriter.cpp
@@ -1985,6 +1985,27 @@ void CABACWriter::glmIdc(const PredictionUnit& pu)
     
     if ( glmActive )
     {
+#if JVET_AB0092_GLM_WITH_LUMA
+      int glmIdx = pu.glmIdc.cb0 - 1;
+#if NUM_GLM_WEIGHT
+      m_BinEncoder.encodeBin(glmIdx >= NUM_GLM_PATTERN ? 1 : 0, Ctx::GlmFlags(1));
+      glmIdx -= glmIdx >= NUM_GLM_PATTERN ? NUM_GLM_PATTERN : 0;
+#endif
+      CHECK(pu.glmIdc.cb0 != pu.glmIdc.cr0, "wrong glm idx");
+#if JVET_AA0057_CCCM
+      m_BinEncoder.encodeBin(glmIdx > 0, Ctx::GlmFlags(2));
+      if (glmIdx > 0)
+      {
+        m_BinEncoder.encodeBin(glmIdx > 1, Ctx::GlmFlags(3));
+        if (glmIdx > 1)
+        {
+          m_BinEncoder.encodeBin(glmIdx > 2, Ctx::GlmFlags(4));
+        }
+      }
+#else
+      m_BinEncoder.encodeBinsEP(glmIdx, NUM_GLM_PATTERN_BITS);
+#endif
+#else
       bool bothActive = pu.glmIdc.cb0 && pu.glmIdc.cr0;
 
       m_BinEncoder.encodeBin( bothActive ? 1 : 0, Ctx::GlmFlags(3) );
@@ -2013,6 +2034,7 @@ void CABACWriter::glmIdc(const PredictionUnit& pu)
 #endif
         m_BinEncoder.encodeBinsEP( glmIdx, NUM_GLM_PATTERN_BITS );
       }
+#endif
         
 #if MMLM
       if ( PU::isMultiModeLM( pu.intraDir[1] ) )
diff --git a/source/Lib/EncoderLib/EncModeCtrl.cpp b/source/Lib/EncoderLib/EncModeCtrl.cpp
index 1f688924db4370b684cb94bcd3c211c61fa99298..4ca8ac885ab6e14e7787feca1e3fe941bcb99885 100644
--- a/source/Lib/EncoderLib/EncModeCtrl.cpp
+++ b/source/Lib/EncoderLib/EncModeCtrl.cpp
@@ -2348,6 +2348,12 @@ bool EncModeCtrlMTnoRQT::tryMode( const EncTestMode& encTestmode, const CodingSt
               relatedCU.skipLfnstTest = false;
             }
           }
+#endif
+#if JVET_AB0092_GLM_WITH_LUMA
+          if (!isLuma(partitioner.chType) && !(bestCU->firstPU->glmIdc.isActive()))
+          {
+            relatedCU.skipGLM = true;
+          }
 #endif
         }
 #if ENABLE_SPLIT_PARALLELISM
diff --git a/source/Lib/EncoderLib/EncModeCtrl.h b/source/Lib/EncoderLib/EncModeCtrl.h
index ee5734cbb7498c1081dd423c50d1cf0e0d857603..0f7d80f4b737b0b74fdac9a30ea85d359671a28f 100644
--- a/source/Lib/EncoderLib/EncModeCtrl.h
+++ b/source/Lib/EncoderLib/EncModeCtrl.h
@@ -995,6 +995,9 @@ struct CodedCUInfo
   uint64_t
        temporalId;
 #endif
+#if JVET_AB0092_GLM_WITH_LUMA
+  bool skipGLM;
+#endif
 };
 
 class CacheBlkInfoCtrl
diff --git a/source/Lib/EncoderLib/IntraSearch.cpp b/source/Lib/EncoderLib/IntraSearch.cpp
index 8d4ebdf2fb5176ff696e1c49cdc8f20e37d054f2..51d7f4ca7658483bdfd8dd81eebfd68efa02b775 100644
--- a/source/Lib/EncoderLib/IntraSearch.cpp
+++ b/source/Lib/EncoderLib/IntraSearch.cpp
@@ -2039,11 +2039,20 @@ void IntraSearch::estIntraPredChromaQT( CodingUnit &cu, Partitioner &partitioner
         {
           satdGlmIdcBest[mode - LM_CHROMA_IDX].setAllZero();
           
+#if JVET_AB0092_GLM_WITH_LUMA
+          CodedCUInfo& relatedCU = ((EncModeCtrlMTnoRQT *)m_modeCtrl)->getBlkInfo(partitioner.currArea());
+          if (PU::hasGlmFlag(pu, mode) && !relatedCU.skipGLM)
+#else
           if ( PU::hasGlmFlag( pu, mode ) )
+#endif
           {
+#if !JVET_AB0092_GLM_WITH_LUMA
             for ( int comp = COMPONENT_Cb; comp <= COMPONENT_Cr; comp++ )
             {
               ComponentID       compID = ComponentID( comp );
+#else
+            ComponentID       compID = COMPONENT_Cb;
+#endif
               int              idcBest = 0;
               int64_t         satdBest = 0;
               GlmIdc&         idcsBest = satdGlmIdcBest[mode - LM_CHROMA_IDX];
@@ -2055,14 +2064,21 @@ void IntraSearch::estIntraPredChromaQT( CodingUnit &cu, Partitioner &partitioner
 
               idcsBest.setIdc(compID, 0, idcBest);
               idcsBest.setIdc(compID, 1, idcBest);
+
+#if JVET_AB0092_GLM_WITH_LUMA
+              idcsBest.setIdc(COMPONENT_Cr, 0, idcBest);
+              idcsBest.setIdc(COMPONENT_Cr, 1, idcBest);
+#endif
               
               satdGlmCosts[mode - LM_CHROMA_IDX] += satdBest; // Summing up Cb and Cr cost
+#if !JVET_AB0092_GLM_WITH_LUMA
             }
             
             if ( !satdGlmIdcBest[0].isActive() )
             {
               break;
             }
+#endif
           }
         }
       }
@@ -2557,11 +2573,12 @@ void IntraSearch::estIntraPredChromaQT( CodingUnit &cu, Partitioner &partitioner
               bestBDPCMMode   = cu.bdpcmModeChroma;
               bestGlmIdc      = pu.glmIdc;
             }
-            
+#if !JVET_AB0092_GLM_WITH_LUMA
             if ( chromaIntraMode == LM_CHROMA_IDX && !bestGlmIdc.isActive() )
             {
               break;
             }
+#endif
           }
         }
       }
@@ -3003,7 +3020,11 @@ void IntraSearch::xFindBestGlmIdcSATD(PredictionUnit &pu, ComponentID compID, in
   CompArea       area = compID == COMPONENT_Cb ? pu.Cb() : pu.Cr();
   PelBuf       orgBuf = cs.getOrgBuf(area);
   PelBuf      predBuf = cs.getPredBuf(area);
+#if JVET_AB0092_GLM_WITH_LUMA
+  int          maxIdc = NUM_GLM_PATTERN * NUM_GLM_WEIGHT;
+#else
   int          maxIdc = NUM_GLM_IDC - 1;
+#endif
   int            mode = pu.intraDir[1];
 
   DistParam distParamSad;
@@ -3017,6 +3038,21 @@ void IntraSearch::xFindBestGlmIdcSATD(PredictionUnit &pu, ComponentID compID, in
   
   sadBest = -1;
 
+#if JVET_AB0092_GLM_WITH_LUMA
+  CompArea       areacr = pu.Cr();
+  PelBuf       orgBufcr = cs.getOrgBuf(areacr);
+  PelBuf      predBufcr = cs.getPredBuf(areacr);
+
+  DistParam distParamSadcr;
+  DistParam distParamSatdcr;
+
+  m_pcRdCost->setDistParam(distParamSadcr, orgBufcr, predBufcr, pu.cs->sps->getBitDepth(CHANNEL_TYPE_CHROMA), COMPONENT_Cr, false);
+  m_pcRdCost->setDistParam(distParamSatdcr, orgBufcr, predBufcr, pu.cs->sps->getBitDepth(CHANNEL_TYPE_CHROMA), COMPONENT_Cr, true);
+
+  distParamSadcr.applyWeight = false;
+  distParamSatdcr.applyWeight = false;
+#endif
+
   // Search positive idcs
   for ( int idc = 0; idc <= maxIdc; idc++ )
   {
@@ -3028,6 +3064,18 @@ void IntraSearch::xFindBestGlmIdcSATD(PredictionUnit &pu, ComponentID compID, in
     int64_t sad     = distParamSad.distFunc(distParamSad) * 2;
     int64_t satd    = distParamSatd.distFunc(distParamSatd);
     int64_t sadThis = std::min(sad, satd);
+
+#if JVET_AB0092_GLM_WITH_LUMA
+    pu.glmIdc.setIdc(COMPONENT_Cr, 0, idc);
+    pu.glmIdc.setIdc(COMPONENT_Cr, 1, idc);
+
+    predIntraChromaLM(COMPONENT_Cr, predBufcr, pu, areacr, mode);
+
+    int64_t sadcr = distParamSadcr.distFunc(distParamSadcr) * 2;
+    int64_t satdcr = distParamSatdcr.distFunc(distParamSatdcr);
+    int64_t sadThiscr = std::min(sadcr, satdcr);
+    sadThis += sadThiscr;
+#endif
     
     if ( sadBest == -1 || sadThis < sadBest )
     {
@@ -7228,7 +7276,7 @@ ChromaCbfs IntraSearch::xRecurIntraChromaCodingQT( CodingStructure &cs, Partitio
     {
       xGetLumaRecPixels( pu, cbArea );
       predIntraChromaLM( COMPONENT_Cb, piPredCb, pu, cbArea, predMode );
-#if JVET_AA0126_GLM
+#if JVET_AA0126_GLM && !JVET_AB0092_GLM_WITH_LUMA
       xGetLumaRecPixels( pu, crArea ); // generate GLM luma samples for Cr prediction
 #endif
       predIntraChromaLM( COMPONENT_Cr, piPredCr, pu, crArea, predMode );