<commit_before>/*
 ***********************************************************************************************************************
 *
 *  Copyright (c) 2019-2021 Advanced Micro Devices, Inc. All Rights Reserved.
 *
 *  Permission is hereby granted, free of charge, to any person obtaining a copy
 *  of this software and associated documentation files (the "Software"), to deal
 *  in the Software without restriction, including without limitation the rights
 *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *  copies of the Software, and to permit persons to whom the Software is
 *  furnished to do so, subject to the following conditions:
 *
 *  The above copyright notice and this permission notice shall be included in all
 *  copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 *  SOFTWARE.
 *
 **********************************************************************************************************************/
/**
***********************************************************************************************************************
* @file  YCbCrConverter.cpp
* @brief LLPC source file: Implementation of LLPC class YCbCrConverter
***********************************************************************************************************************
*/
#include "YCbCrConverter.h"
#include "lgc/LgcContext.h"
#include "llvm/IR/Intrinsics.h"
#include "llvm/IR/IntrinsicsAMDGPU.h"

using namespace lgc;
using namespace llvm;

// =====================================================================================================================
// Implement wrapped YCbCr sample
//
// @param wrapInfo : Wrapped YCbCr sample infomation
Value *YCbCrConverter::wrappedSample(YCbCrWrappedSampleInfo &wrapInfo) {
  SmallVector<Value *, 4> coordsChroma;
  YCbCrSampleInfo *sampleInfo = wrapInfo.ycbcrInfo;
  Value *chromaWidth = wrapInfo.chromaWidth;
  Value *chromaHeight = wrapInfo.chromaHeight;

  if (wrapInfo.subsampledX)
    chromaWidth = m_builder->CreateFMul(wrapInfo.chromaWidth, ConstantFP::get(m_builder->getFloatTy(), 0.5f));

  if (wrapInfo.subsampledY)
    chromaHeight = m_builder->CreateFMul(wrapInfo.chromaHeight, ConstantFP::get(m_builder->getFloatTy(), 0.5f));

  coordsChroma.push_back(m_builder->CreateFDiv(wrapInfo.coordI, chromaWidth));
  coordsChroma.push_back(m_builder->CreateFDiv(wrapInfo.coordJ, chromaHeight));

  sampleInfo->imageDesc = wrapInfo.imageDesc1;

  Value *result = nullptr;

  if (wrapInfo.planeCount == 1) {
    sampleInfo->imageDesc = wrapInfo.subsampledX ? wrapInfo.imageDesc2 : wrapInfo.imageDesc1;

    Instruction *imageOp = cast<Instruction>(createImageSampleInternal(coordsChroma, sampleInfo));
    result = m_builder->CreateShuffleVector(imageOp, imageOp, ArrayRef<int>{0, 2});
  } else if (wrapInfo.planeCount == 2) {
    sampleInfo->imageDesc = wrapInfo.imageDesc2;
    Instruction *imageOp = cast<Instruction>(createImageSampleInternal(coordsChroma, sampleInfo));
    result = m_builder->CreateShuffleVector(imageOp, imageOp, ArrayRef<int>{0, 2});
  } else if (wrapInfo.planeCount == 3) {
    sampleInfo->imageDesc = wrapInfo.imageDesc2;
    Instruction *pImageOp1 = cast<Instruction>(createImageSampleInternal(coordsChroma, sampleInfo));

    sampleInfo->imageDesc = wrapInfo.imageDesc3;
    Instruction *pImageOp2 = cast<Instruction>(createImageSampleInternal(coordsChroma, sampleInfo));
    result = m_builder->CreateShuffleVector(pImageOp2, pImageOp1, ArrayRef<int>{0, 6});
  } else {
    llvm_unreachable("Out of ranged plane count!");
  }

  return result;
}

// =====================================================================================================================
// Create YCbCr reconstruct linear X chroma sample
//
// @param xChromeInfo : Infomation for downsampled chroma channels in X dimension
Value *YCbCrConverter::reconstructLinearXChromaSample(XChromaSampleInfo &xChromaInfo) {
  YCbCrSampleInfo *sampleInfo = xChromaInfo.ycbcrInfo;
  Value *isEvenI = m_builder->CreateICmpEQ(
      m_builder->CreateSMod(m_builder->CreateFPToSI(xChromaInfo.coordI, m_builder->getInt32Ty()),
                            m_builder->getInt32(2)),
      m_builder->getInt32(0));

  Value *subCoordI = m_builder->CreateUnaryIntrinsic(
      Intrinsic::floor, m_builder->CreateFDiv(xChromaInfo.coordI, ConstantFP::get(m_builder->getFloatTy(), 2.0)));
  if (xChromaInfo.chromaOffsetX != ChromaLocation::CositedEven) {
    subCoordI = m_builder->CreateSelect(
        isEvenI, m_builder->CreateFSub(subCoordI, ConstantFP::get(m_builder->getFloatTy(), 1.0)), subCoordI);
  }

  Value *alpha = nullptr;
  if (xChromaInfo.chromaOffsetX == ChromaLocation::CositedEven) {
    alpha = m_builder->CreateSelect(isEvenI, ConstantFP::get(m_builder->getFloatTy(), 0.0),
                                    ConstantFP::get(m_builder->getFloatTy(), 0.5));
  } else {
    alpha = m_builder->CreateSelect(isEvenI, ConstantFP::get(m_builder->getFloatTy(), 0.25),
                                    ConstantFP::get(m_builder->getFloatTy(), 0.75));
  }

  Value *coordT = m_builder->CreateFDiv(xChromaInfo.coordJ, xChromaInfo.chromaHeight);

  SmallVector<Value *, 4> coordsChromaA;
  sampleInfo->imageDesc = xChromaInfo.imageDesc1;
  coordsChromaA.push_back(m_builder->CreateFDiv(subCoordI, xChromaInfo.chromaWidth));
  coordsChromaA.push_back(coordT);
  Instruction *pImageOpA = cast<Instruction>(createImageSampleInternal(coordsChromaA, sampleInfo));

  SmallVector<Value *, 4> coordsChromaB;
  coordsChromaB.push_back(m_builder->CreateFDiv(
      m_builder->CreateFAdd(subCoordI, ConstantFP::get(m_builder->getFloatTy(), 1.0)), xChromaInfo.chromaWidth));
  coordsChromaB.push_back(coordT);
  Instruction *pImageOpB = cast<Instruction>(createImageSampleInternal(coordsChromaB, sampleInfo));

  Value *result = m_builder->createFMix(pImageOpB, pImageOpA, alpha);

  return m_builder->CreateShuffleVector(result, result, ArrayRef<int>{0, 2});
}

// =====================================================================================================================
// Create YCbCr Reconstruct linear XY chroma sample
//
// @param xyChromeInfo : Information for downsampled chroma channels in XY dimension
Value *YCbCrConverter::reconstructLinearXYChromaSample(XYChromaSampleInfo &xyChromaInfo) {
  YCbCrSampleInfo *sampleInfo = xyChromaInfo.ycbcrInfo;

  Value *width = xyChromaInfo.chromaWidth;
  Value *height = xyChromaInfo.chromaHeight;

  Value *isEvenI = m_builder->CreateICmpEQ(
      m_builder->CreateSMod(m_builder->CreateFPToSI(xyChromaInfo.coordI, m_builder->getInt32Ty()),
                            m_builder->getInt32(2)),
      m_builder->getInt32(0));
  Value *isEvenJ = m_builder->CreateICmpEQ(
      m_builder->CreateSMod(m_builder->CreateFPToSI(xyChromaInfo.coordJ, m_builder->getInt32Ty()),
                            m_builder->getInt32(2)),
      m_builder->getInt32(0));

  Value *subCoordI = m_builder->CreateUnaryIntrinsic(
      Intrinsic::floor, m_builder->CreateFDiv(xyChromaInfo.coordI, ConstantFP::get(m_builder->getFloatTy(), 2.0)));
  Value *subCoordJ = m_builder->CreateUnaryIntrinsic(
      Intrinsic::floor, m_builder->CreateFDiv(xyChromaInfo.coordJ, ConstantFP::get(m_builder->getFloatTy(), 2.0)));

  if (xyChromaInfo.chromaOffsetX != ChromaLocation::CositedEven) {
    subCoordI = m_builder->CreateSelect(
        isEvenI, m_builder->CreateFSub(subCoordI, ConstantFP::get(m_builder->getFloatTy(), 1.0)), subCoordI);
  }

  if (xyChromaInfo.chromaOffsetY != ChromaLocation::CositedEven) {
    subCoordJ = m_builder->CreateSelect(
        isEvenJ, m_builder->CreateFSub(subCoordJ, ConstantFP::get(m_builder->getFloatTy(), 1.0)), subCoordJ);
  }

  Value *alpha = nullptr;
  if (xyChromaInfo.chromaOffsetX == ChromaLocation::CositedEven) {
    alpha = m_builder->CreateSelect(isEvenI, ConstantFP::get(m_builder->getFloatTy(), 0.0),
                                    ConstantFP::get(m_builder->getFloatTy(), 0.5));
  } else {
    alpha = m_builder->CreateSelect(isEvenI, ConstantFP::get(m_builder->getFloatTy(), 0.25),
                                    ConstantFP::get(m_builder->getFloatTy(), 0.75));
  }

  Value *beta = nullptr;
  if (xyChromaInfo.chromaOffsetY == ChromaLocation::CositedEven) {
    beta = m_builder->CreateSelect(isEvenJ, ConstantFP::get(m_builder->getFloatTy(), 0.0),
                                   ConstantFP::get(m_builder->getFloatTy(), 0.5));
  } else {
    beta = m_builder->CreateSelect(isEvenJ, ConstantFP::get(m_builder->getFloatTy(), 0.25),
                                   ConstantFP::get(m_builder->getFloatTy(), 0.75));
  }

  SmallVector<Value *, 4> coordsChromaTL;
  SmallVector<Value *, 4> coordsChromaTR;
  SmallVector<Value *, 4> coordsChromaBL;
  SmallVector<Value *, 4> coordsChromaBR;

  Value *result = nullptr;
  if (xyChromaInfo.planeCount == 2) {
    sampleInfo->imageDesc = xyChromaInfo.imageDesc1;

    // Sample TL
    coordsChromaTL.push_back(m_builder->CreateFDiv(subCoordI, width));
    coordsChromaTL.push_back(m_builder->CreateFDiv(subCoordJ, height));
    Instruction *coordTL = cast<Instruction>(createImageSampleInternal(coordsChromaTL, sampleInfo));

    // Sample TR
    coordsChromaTR.push_back(
        m_builder->CreateFDiv(m_builder->CreateFAdd(subCoordI, ConstantFP::get(m_builder->getFloatTy(), 1.0)), width));
    coordsChromaTR.push_back(m_builder->CreateFDiv(subCoordJ, height));
    Instruction *coordTR = cast<Instruction>(createImageSampleInternal(coordsChromaTR, sampleInfo));

    // Sample BL
    coordsChromaBL.push_back(m_builder->CreateFDiv(subCoordI, width));
    coordsChromaBL.push_back(
        m_builder->CreateFDiv(m_builder->CreateFAdd(subCoordJ, ConstantFP::get(m_builder->getFloatTy(), 1.0)), height));
    Instruction *coordBL = cast<Instruction>(createImageSampleInternal(coordsChromaBL, sampleInfo));

    // Sample BR
    coordsChromaBR.push_back(
        m_builder->CreateFDiv(m_builder->CreateFAdd(subCoordI, ConstantFP::get(m_builder->getFloatTy(), 1.0)), width));
    coordsChromaBR.push_back(
        m_builder->CreateFDiv(m_builder->CreateFAdd(subCoordJ, ConstantFP::get(m_builder->getFloatTy(), 1.0)), height));
    Instruction *coordBR = cast<Instruction>(createImageSampleInternal(coordsChromaBR, sampleInfo));

    // Linear interpolate
    result = bilinearBlend(alpha, beta, coordTL, coordTR, coordBL, coordBR);
    result = m_builder->CreateShuffleVector(result, result, ArrayRef<int>{0, 2});
  } else if (xyChromaInfo.planeCount == 3) {
    // Sample TL
    coordsChromaTL.push_back(m_builder->CreateFDiv(subCoordI, width));
    coordsChromaTL.push_back(m_builder->CreateFDiv(subCoordJ, height));
    sampleInfo->imageDesc = xyChromaInfo.imageDesc1;
    Value *coordTLb = cast<Instruction>(createImageSampleInternal(coordsChromaTL, sampleInfo));

    sampleInfo->imageDesc = xyChromaInfo.imageDesc2;
    Value *coordTLr = cast<Instruction>(createImageSampleInternal(coordsChromaTL, sampleInfo));
    Value *coordTL = m_builder->CreateShuffleVector(coordTLr, coordTLb, ArrayRef<int>{0, 6});

    // Sample TR
    coordsChromaTR.push_back(
        m_builder->CreateFDiv(m_builder->CreateFAdd(subCoordI, ConstantFP::get(m_builder->getFloatTy(), 1.0)), width));
    coordsChromaTR.push_back(m_builder->CreateFDiv(subCoordJ, height));
    sampleInfo->imageDesc = xyChromaInfo.imageDesc1;
    Value *coordTRb = cast<Instruction>(createImageSampleInternal(coordsChromaTR, sampleInfo));

    sampleInfo->imageDesc = xyChromaInfo.imageDesc2;
    Value *coordTRr = cast<Instruction>(createImageSampleInternal(coordsChromaTR, sampleInfo));
    Value *coordTR = m_builder->CreateShuffleVector(coordTRr, coordTRb, ArrayRef<int>{0, 6});

    // Sample BL
    coordsChromaBL.push_back(m_builder->CreateFDiv(subCoordI, width));
    coordsChromaBL.push_back(
        m_builder->CreateFDiv(m_builder->CreateFAdd(subCoordJ, ConstantFP::get(m_builder->getFloatTy(), 1.0)), height));
    sampleInfo->imageDesc = xyChromaInfo.imageDesc1;
    Value *coordBLb = cast<Instruction>(createImageSampleInternal(coordsChromaBL, sampleInfo));
    sampleInfo->imageDesc = xyChromaInfo.imageDesc2;
    Value *coordBLr = cast<Instruction>(createImageSampleInternal(coordsChromaBL, sampleInfo));
    Value *coordBL = m_builder->CreateShuffleVector(coordBLr, coordBLb, ArrayRef<int>{0, 6});

    // Sample BR
    coordsChromaBR.push_back(
        m_builder->CreateFDiv(m_builder->CreateFAdd(subCoordI, ConstantFP::get(m_builder->getFloatTy(), 1.0)), width));
    coordsChromaBR.push_back(
        m_builder->CreateFDiv(m_builder->CreateFAdd(subCoordJ, ConstantFP::get(m_builder->getFloatTy(), 1.0)), height));
    sampleInfo->imageDesc = xyChromaInfo.imageDesc1;
    Value *coordBRb = cast<Instruction>(createImageSampleInternal(coordsChromaBR, sampleInfo));
    sampleInfo->imageDesc = xyChromaInfo.imageDesc2;
    Value *coordBRr = cast<Instruction>(createImageSampleInternal(coordsChromaBR, sampleInfo));
    Value *coordBR = m_builder->CreateShuffleVector(coordBRr, coordBRb, ArrayRef<int>{0, 6});

    // Linear interpolate
    result = bilinearBlend(alpha, beta, coordTL, coordTR, coordBL, coordBR);
  }

  return result;
}

// =====================================================================================================================
// Create YCbCr image sample internal
//
// @param coords : The ST coordinates
// @param ycbcrInfo : YCbCr smaple information
Value *YCbCrConverter::createImageSampleInternal(SmallVectorImpl<Value *> &coordsIn, YCbCrSampleInfo *ycbcrInfo) {

  unsigned imageDim = ycbcrInfo->dim;

  Value *coords = UndefValue::get(FixedVectorType::get(coordsIn[0]->getType(), m_builder->getImageNumCoords(imageDim)));
  coords = m_builder->CreateInsertElement(coords, coordsIn[0], uint64_t(0));
  coords = m_builder->CreateInsertElement(coords, coordsIn[1], uint64_t(1));

  if (imageDim == Builder::Dim2DArray)
    coords = m_builder->CreateInsertElement(coords, m_coordZ, uint64_t(2));

  return m_builder->CreateImageSampleGather(ycbcrInfo->resultTy, ycbcrInfo->dim, ycbcrInfo->flags, coords,
                                            ycbcrInfo->imageDesc, ycbcrInfo->samplerDesc, ycbcrInfo->address,
                                            ycbcrInfo->instNameStr, ycbcrInfo->isSample);
}

// =====================================================================================================================
// YCbCrConverter
//
// @param builder : ImageBuilder instance
// @param ycbcrMetaData : YCbCr conversion metadata
// @param ycbcrSampleInfo : YCbCr sample information
// @param gfxIp : The GfxIp Version
YCbCrConverter::YCbCrConverter(ImageBuilder *builder, const SamplerYCbCrConversionMetaData &ycbcrMetaData,
                               YCbCrSampleInfo *ycbcrSampleInfo, GfxIpVersion *gfxIp)
    : m_builder(builder), m_metaData(ycbcrMetaData), m_gfxIp(gfxIp) {
  m_imgDescsChroma.resize(3);
  setYCbCrSampleInfo(ycbcrSampleInfo);
  genSamplerDescChroma();
  genImgDescChroma();
  prepareCoord();
}

// =====================================================================================================================
// Set YCbCr sample infomation
//
// @param ycbcrSampleInfo : YCbCr sampler information
void YCbCrConverter::setYCbCrSampleInfo(YCbCrSampleInfo *ycbcrSampleInfo) {
  m_ycbcrSampleInfo = ycbcrSampleInfo;

  m_samplerDescLuma = m_ycbcrSampleInfo->samplerDesc;
  m_imgDescLuma = m_ycbcrSampleInfo->imageDesc;

  m_samplerDescChroma = m_samplerDescLuma;
  m_imgDescsChroma[0] = m_imgDescLuma;

  m_resultType = m_ycbcrSampleInfo->resultTy;
}

// =====================================================================================================================
// Generate sampler descriptor for chroma channel
void YCbCrConverter::genSamplerDescChroma() {
  m_samplerDescChroma =
      generateSamplerDesc(m_samplerDescLuma, static_cast<SamplerFilter>(m_metaData.word1.chromaFilter),
                          m_metaData.word0.forceExplicitReconstruct);
}

// =====================================================================================================================
// Generate image descriptor for chroma channel
void YCbCrConverter::genImgDescChroma() {
  SqImgRsrcRegHandler proxySqRsrcRegHelper(m_builder, m_imgDescLuma, m_gfxIp);
  Value *width = proxySqRsrcRegHelper.getReg(SqRsrcRegs::Width);
  Value *height = proxySqRsrcRegHelper.getReg(SqRsrcRegs::Height);
  m_width = m_builder->CreateUIToFP(width, m_builder->getFloatTy());
  m_height = m_builder->CreateUIToFP(height, m_builder->getFloatTy());

  if (m_metaData.word1.planes == 1) {
    Value *imgDataFmt = proxySqRsrcRegHelper.getReg(SqRsrcRegs::Format);
    Value *dstSelXYZW = proxySqRsrcRegHelper.getReg(SqRsrcRegs::DstSelXYZW);
    Value *isGbGrFmt = nullptr;
    Value *isBgRgFmt = nullptr;

    switch (m_gfxIp->major) {
    case 6:
    case 7:
    case 8:
    case 9: {
      isGbGrFmt = m_builder->CreateICmpEQ(
          imgDataFmt, m_builder->getInt32(ImageBuilder::ImgDataFormat::IMG_DATA_FORMAT_BG_RG__CORE));

      isBgRgFmt = m_builder->CreateICmpEQ(
          imgDataFmt, m_builder->getInt32(ImageBuilder::ImgDataFormat::IMG_DATA_FORMAT_GB_GR__CORE));

      proxySqRsrcRegHelper.setReg(SqRsrcRegs::Format,
                                  m_builder->getInt32(ImageBuilder::ImgDataFormat::IMG_DATA_FORMAT_8_8_8_8));
      break;
    }
    case 10: {
      isGbGrFmt = m_builder->CreateICmpEQ(imgDataFmt,
                                          m_builder->getInt32(ImageBuilder::ImgFmtGfx10::IMG_FMT_BG_RG_UNORM__GFX10CORE));
      isBgRgFmt = m_builder->CreateICmpEQ(imgDataFmt,
                                          m_builder->getInt32(ImageBuilder::ImgFmtGfx10::IMG_FMT_GB_GR_UNORM__GFX10CORE));

      proxySqRsrcRegHelper.setReg(SqRsrcRegs::Format,
                                  m_builder->getInt32(ImageBuilder::ImgFmtGfx10::IMG_FMT_8_8_8_8_UNORM__GFX10CORE));
      break;
    }
    default:
      llvm_unreachable("GFX IP not supported!");
      break;
    }

    dstSelXYZW = m_builder->CreateSelect(isGbGrFmt, m_builder->getInt32(0x977), dstSelXYZW);
    dstSelXYZW = m_builder->CreateSelect(isBgRgFmt, m_builder->getInt32(0xF2E), dstSelXYZW);

    YCbCrAddressHandler addrHelper(m_builder, &proxySqRsrcRegHelper, m_gfxIp);
    addrHelper.genHeightAndPitch(m_metaData.word0.bitDepth.channelBitsR, 32, m_metaData.word2.bitCounts.xBitCount,
                                 m_metaData.word1.planes);
    proxySqRsrcRegHelper.setReg(SqRsrcRegs::Width,
                                m_builder->CreateLShr(width, ConstantInt::get(m_builder->getInt32Ty(), 1)));
    proxySqRsrcRegHelper.setReg(SqRsrcRegs::DstSelXYZW, dstSelXYZW);
    proxySqRsrcRegHelper.setReg(SqRsrcRegs::Pitch, addrHelper.getPitchCb());
    m_imgDescsChroma[1] = proxySqRsrcRegHelper.getRegister();
  }
}

// =====================================================================================================================
// Set image descriptor for chroma channel
//
// @param planeIndex : Specific plane index for setting image descriptor
// @param imageDesc : Image descriptor
void YCbCrConverter::SetImgDescChroma(unsigned planeIndex, llvm::Value *imageDesc) {
  assert(planeIndex < 3);
  m_imgDescsChroma[planeIndex] = imageDesc;
}

// =====================================================================================================================
// Get image descriptor for chroma channel
//
// @param planeIndex : Specific plane index for loading image descriptor
llvm::Value *YCbCrConverter::GetImgDescChroma(unsigned planeIndex) {
  assert(planeIndex < 3);
  return m_imgDescsChroma[planeIndex];
}

// =====================================================================================================================
// Convert from YCbCr to RGBA color space
Value *YCbCrConverter::convertColorSpace() {
  unsigned channelBits[3] = {
      m_metaData.word0.bitDepth.channelBitsR,
      m_metaData.word0.bitDepth.channelBitsG,
      m_metaData.word0.bitDepth.channelBitsB,
  };
  return convertColor(m_resultType, static_cast<SamplerYCbCrModelConversion>(m_metaData.word0.yCbCrModel),
                      static_cast<SamplerYCbCrRange>(m_metaData.word0.yCbCrRange), channelBits, m_ycbcrData);
}

// =====================================================================================================================
// Prepare the ST coords
void YCbCrConverter::prepareCoord() {
  Value *coords = m_ycbcrSampleInfo->address[Builder::ImageAddressIdxCoordinate];

  assert(Builder::getImageNumCoords(m_ycbcrSampleInfo->dim) ==
         cast<FixedVectorType>(coords->getType())->getNumElements());

  m_coordS = m_builder->CreateExtractElement(coords, m_builder->getInt64(0));
  m_coordT = m_builder->CreateExtractElement(coords, m_builder->getInt64(1));

  if (m_ycbcrSampleInfo->dim == Builder::Dim2DArray) {
    m_coordZ = m_builder->CreateExtractElement(coords, m_builder->getInt64(2));
  }

  m_coordU = transferSTtoUVCoords(m_coordS, m_width);
  m_coordV = transferSTtoUVCoords(m_coordT, m_height);

  m_coordI = transferUVtoIJCoords(static_cast<SamplerFilter>(m_metaData.word1.lumaFilter), m_coordU);
  m_coordJ = transferUVtoIJCoords(static_cast<SamplerFilter>(m_metaData.word1.lumaFilter), m_coordV);
}

// =====================================================================================================================
// Generate sampler descriptor for YCbCr conversion
//
// @param samplerDesc : Sampler descriptor
// @param filter : The type of sampler filter
// @param forceExplicitReconstruction : Enable/Disable force explict chroma reconstruction
Value *YCbCrConverter::generateSamplerDesc(Value *samplerDesc, SamplerFilter filter, bool forceExplicitReconstruction) {
  SqImgSampRegHandler imgRegHelper(m_builder, samplerDesc, m_gfxIp);

  /// Determines if "TexFilter" should be ignored or not.
  // enum class TexFilterMode : uint32
  // {
  //     Blend = 0x0, ///< Use the filter method specified by the TexFilter enumeration
  //     Min   = 0x1, ///< Use the minimum value returned by the sampler, no blending op occurs
  //     Max   = 0x2, ///< Use the maximum value returned by the sampler, no blending op occurs
  // };
  // Force use blend mode
  // imgRegHelper.SetFilterMode(m_builder->getInt32(0b00));
  imgRegHelper.setReg(SqSampRegs::FilterMode, m_builder->getInt32(0b00));

  /// Enumeration which defines the mode for magnification and minification sampling
  // enum XyFilter : uint32
  // {
  //     XyFilterPoint = 0,          ///< Use single point sampling
  //     XyFilterLinear,             ///< Use linear sampling
  //     XyFilterAnisotropicPoint,   ///< Use anisotropic with single point sampling
  //     XyFilterAnisotropicLinear,  ///< Use anisotropic with linear sampling
  //     XyFilterCount
  // };
  if ((filter == SamplerFilter::Nearest) || forceExplicitReconstruction) {
    imgRegHelper.setReg(SqSampRegs::xyMagFilter, m_builder->getInt32(0b00));
    imgRegHelper.setReg(SqSampRegs::xyMinFilter, m_builder->getInt32(0b00));
  } else // filter == SamplerFilter::Linear
  {
    imgRegHelper.setReg(SqSampRegs::xyMagFilter, m_builder->getInt32(0b01));
    imgRegHelper.setReg(SqSampRegs::xyMinFilter, m_builder->getInt32(0b01));
  }

  return imgRegHelper.getRegister();
}

// =====================================================================================================================
// Implement range expanding operation on checking whether the encoding uses full numerical range on luma channel
//
// @param range : Specifies whether the encoding uses the full numerical range
// @param channelBits : Channel bits
// @param sampleResult : Sample results which need range expansion, assume in sequence => Cr, Y, Cb
Value *YCbCrConverter::rangeExpand(SamplerYCbCrRange range, const unsigned *channelBits, Value *sampleResult) {
  switch (range) {
  case SamplerYCbCrRange::ItuFull: {
    //              [2^(n - 1)/((2^n) - 1)]
    // convVec1 =  [         0.0         ]
    //              [2^(n - 1)/((2^n) - 1)]
    float row0Num = static_cast<float>(0x1u << (channelBits[0] - 0x1u)) / ((0x1u << channelBits[0]) - 1u);
    float row2Num = static_cast<float>(0x1u << (channelBits[2] - 0x1u)) / ((0x1u << channelBits[2]) - 1u);

    Value *convVec1 = UndefValue::get(FixedVectorType::get(m_builder->getFloatTy(), 3));
    convVec1 = m_builder->CreateInsertElement(convVec1, ConstantFP::get(m_builder->getFloatTy(), row0Num), uint64_t(0));
    convVec1 = m_builder->CreateInsertElement(convVec1, ConstantFP::get(m_builder->getFloatTy(), 0.0f), uint64_t(1));
    convVec1 = m_builder->CreateInsertElement(convVec1, ConstantFP::get(m_builder->getFloatTy(), row2Num), uint64_t(2));

    //          [Cr]   convVec1[0]
    // result = [ Y] - convVec1[1]
    //          [Cb]   convVec1[2]
    return m_builder->CreateFSub(sampleResult, convVec1);
  }
  case SamplerYCbCrRange::ItuNarrow: {
    //             [(2^n - 1)/(224 x (2^(n - 8))]
    // convVec1 = [(2^n - 1)/(219 x (2^(n - 8))]
    //             [(2^n - 1)/(224 x (2^(n - 8))]
    float row0Num = static_cast<float>((0x1u << channelBits[0]) - 1u) / (224u * (0x1u << (channelBits[0] - 8)));
    float row1Num = static_cast<float>((0x1u << channelBits[1]) - 1u) / (219u * (0x1u << (channelBits[1] - 8)));
    float row2Num = static_cast<float>((0x1u << channelBits[2]) - 1u) / (224u * (0x1u << (channelBits[2] - 8)));

    Value *convVec1 = UndefValue::get(FixedVectorType::get(m_builder->getFloatTy(), 3));
    convVec1 = m_builder->CreateInsertElement(convVec1, ConstantFP::get(m_builder->getFloatTy(), row0Num), uint64_t(0));
    convVec1 = m_builder->CreateInsertElement(convVec1, ConstantFP::get(m_builder->getFloatTy(), row1Num), uint64_t(1));
    convVec1 = m_builder->CreateInsertElement(convVec1, ConstantFP::get(m_builder->getFloatTy(), row2Num), uint64_t(2));

    //             [(128 x (2^(n - 8))/(224 x (2^(n - 8))]
    // convVec2 = [( 16 x (2^(n - 8))/(219 x (2^(n - 8))]
    //             [(128 x (2^(n - 8))/(224 x (2^(n - 8))]
    row0Num = static_cast<float>(128u * (0x1u << (channelBits[0] - 8))) / (224u * (0x1u << (channelBits[0] - 8)));
    row1Num = static_cast<float>(16u * (0x1u << (channelBits[1] - 8))) / (219u * (0x1u << (channelBits[1] - 8)));
    row2Num = static_cast<float>(128u * (0x1u << (channelBits[2] - 8))) / (224u * (0x1u << (channelBits[2] - 8)));

    Value *convVec2 = UndefValue::get(FixedVectorType::get(m_builder->getFloatTy(), 3));
    convVec2 = m_builder->CreateInsertElement(convVec2, ConstantFP::get(m_builder->getFloatTy(), row0Num), uint64_t(0));
    convVec2 = m_builder->CreateInsertElement(convVec2, ConstantFP::get(m_builder->getFloatTy(), row1Num), uint64_t(1));
    convVec2 = m_builder->CreateInsertElement(convVec2, ConstantFP::get(m_builder->getFloatTy(), row2Num), uint64_t(2));

    //          convVec1[0]   [Cr]   convVec2[0]
    // result = convVec1[1] * [ Y] - convVec2[1]
    //          convVec1[2]   [Cb]   convVec2[2]
    return m_builder->CreateFSub(m_builder->CreateFMul(sampleResult, convVec1), convVec2);
  }

  default:
    llvm_unreachable("Unknown range expanding type!");
    return nullptr;
  }
}

// =====================================================================================================================
// Sample YCbCr data from each plane
void YCbCrConverter::sampleYCbCrData() {
  SmallVector<Value *, 4> coordsLuma;
  SmallVector<Value *, 4> coordsChroma;

  // coordI -> coordS
  coordsLuma.push_back(m_coordS);
  // coordJ -> coordT
  coordsLuma.push_back(m_coordT);

  // Sample Y and A channels
  Value *imageOpLuma = cast<Instruction>(createImageSampleInternal(coordsLuma, m_ycbcrSampleInfo));
  imageOpLuma = m_builder->CreateShuffleVector(imageOpLuma, imageOpLuma, ArrayRef<int>{1, 3});

  // Init sample chroma info
  m_ycbcrSampleInfo->samplerDesc = m_samplerDescChroma;

  // Init chroma width and height
  Value *chromaWidth = m_builder->CreateFMul(m_width, ConstantFP::get(m_builder->getFloatTy(), 0.5f));
  Value *chromaHeight = m_builder->CreateFMul(m_height, ConstantFP::get(m_builder->getFloatTy(), 0.5f));

  // Init smaple chroma info for downsampled chroma channels in the x dimension
  XChromaSampleInfo xChromaInfo = {};
  xChromaInfo.ycbcrInfo = m_ycbcrSampleInfo;
  xChromaInfo.imageDesc1 = m_imgDescsChroma[1];
  xChromaInfo.coordI = m_coordI;
  xChromaInfo.coordJ = m_coordJ;
  xChromaInfo.chromaWidth = chromaWidth;
  xChromaInfo.chromaHeight = m_height;
  xChromaInfo.chromaOffsetX = static_cast<ChromaLocation>(m_metaData.word1.xChromaOffset);

  // Init smaple chroma info for downsampled chroma channels in xy dimension<commit_msg>Add LLPC implementation of VK_EXT_ycbcr_image_arrays extension<commit_after>/*
 ***********************************************************************************************************************
 *
 *  Copyright (c) 2019-2021 Advanced Micro Devices, Inc. All Rights Reserved.
 *
 *  Permission is hereby granted, free of charge, to any person obtaining a copy
 *  of this software and associated documentation files (the "Software"), to deal
 *  in the Software without restriction, including without limitation the rights
 *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *  copies of the Software, and to permit persons to whom the Software is
 *  furnished to do so, subject to the following conditions:
 *
 *  The above copyright notice and this permission notice shall be included in all
 *  copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 *  SOFTWARE.
 *
 **********************************************************************************************************************/
/**
***********************************************************************************************************************
* @file  YCbCrConverter.cpp
* @brief LLPC source file: Implementation of LLPC class YCbCrConverter
***********************************************************************************************************************
*/
#include "YCbCrConverter.h"
#include "lgc/LgcContext.h"
#include "llvm/IR/Intrinsics.h"
#include "llvm/IR/IntrinsicsAMDGPU.h"

using namespace lgc;
using namespace llvm;

// =====================================================================================================================
// Implement wrapped YCbCr sample
//
// @param wrapInfo : Wrapped YCbCr sample infomation
Value *YCbCrConverter::wrappedSample(YCbCrWrappedSampleInfo &wrapInfo) {
  SmallVector<Value *, 4> coordsChroma;
  YCbCrSampleInfo *sampleInfo = wrapInfo.ycbcrInfo;
  sampleInfo->imageDesc = wrapInfo.imageDesc1;

  Value *chromaWidth = nullptr;
  Value *chromaHeight = nullptr;

  if ((m_metaData.word5.lumaDepth > 1) && (m_metaData.word1.planes > 1)) {
    SqImgRsrcRegHandler proxySqRsrcRegHelper(m_builder, wrapInfo.imageDesc2, m_gfxIp);
    chromaWidth = proxySqRsrcRegHelper.getReg(SqRsrcRegs::Width);
    chromaHeight = proxySqRsrcRegHelper.getReg(SqRsrcRegs::Height);

    chromaWidth = m_builder->CreateUIToFP(chromaWidth, m_builder->getFloatTy());
    chromaHeight = m_builder->CreateUIToFP(chromaHeight, m_builder->getFloatTy());
  } else {
    chromaWidth = wrapInfo.chromaWidth;
    chromaHeight = wrapInfo.chromaHeight;

    if (wrapInfo.subsampledX)
      chromaWidth = m_builder->CreateFMul(wrapInfo.chromaWidth, ConstantFP::get(m_builder->getFloatTy(), 0.5f));

    if (wrapInfo.subsampledY)
      chromaHeight = m_builder->CreateFMul(wrapInfo.chromaHeight, ConstantFP::get(m_builder->getFloatTy(), 0.5f));
  }

  coordsChroma.push_back(m_builder->CreateFDiv(wrapInfo.coordI, chromaWidth));
  coordsChroma.push_back(m_builder->CreateFDiv(wrapInfo.coordJ, chromaHeight));

  Value *result = nullptr;

  if (wrapInfo.planeCount == 1) {
    sampleInfo->imageDesc = wrapInfo.subsampledX ? wrapInfo.imageDesc2 : wrapInfo.imageDesc1;

    Instruction *imageOp = cast<Instruction>(createImageSampleInternal(coordsChroma, sampleInfo));
    result = m_builder->CreateShuffleVector(imageOp, imageOp, ArrayRef<int>{0, 2});
  } else if (wrapInfo.planeCount == 2) {
    sampleInfo->imageDesc = wrapInfo.imageDesc2;
    Instruction *imageOp = cast<Instruction>(createImageSampleInternal(coordsChroma, sampleInfo));
    result = m_builder->CreateShuffleVector(imageOp, imageOp, ArrayRef<int>{0, 2});
  } else if (wrapInfo.planeCount == 3) {
    sampleInfo->imageDesc = wrapInfo.imageDesc2;
    Instruction *pImageOp1 = cast<Instruction>(createImageSampleInternal(coordsChroma, sampleInfo));

    sampleInfo->imageDesc = wrapInfo.imageDesc3;
    Instruction *pImageOp2 = cast<Instruction>(createImageSampleInternal(coordsChroma, sampleInfo));
    result = m_builder->CreateShuffleVector(pImageOp2, pImageOp1, ArrayRef<int>{0, 6});
  } else {
    llvm_unreachable("Out of ranged plane count!");
  }

  return result;
}

// =====================================================================================================================
// Create YCbCr reconstruct linear X chroma sample
//
// @param xChromeInfo : Infomation for downsampled chroma channels in X dimension
Value *YCbCrConverter::reconstructLinearXChromaSample(XChromaSampleInfo &xChromaInfo) {
  YCbCrSampleInfo *sampleInfo = xChromaInfo.ycbcrInfo;
  Value *isEvenI = m_builder->CreateICmpEQ(
      m_builder->CreateSMod(m_builder->CreateFPToSI(xChromaInfo.coordI, m_builder->getInt32Ty()),
                            m_builder->getInt32(2)),
      m_builder->getInt32(0));

  Value *subCoordI = m_builder->CreateUnaryIntrinsic(
      Intrinsic::floor, m_builder->CreateFDiv(xChromaInfo.coordI, ConstantFP::get(m_builder->getFloatTy(), 2.0)));
  if (xChromaInfo.chromaOffsetX != ChromaLocation::CositedEven) {
    subCoordI = m_builder->CreateSelect(
        isEvenI, m_builder->CreateFSub(subCoordI, ConstantFP::get(m_builder->getFloatTy(), 1.0)), subCoordI);
  }

  Value *alpha = nullptr;
  if (xChromaInfo.chromaOffsetX == ChromaLocation::CositedEven) {
    alpha = m_builder->CreateSelect(isEvenI, ConstantFP::get(m_builder->getFloatTy(), 0.0),
                                    ConstantFP::get(m_builder->getFloatTy(), 0.5));
  } else {
    alpha = m_builder->CreateSelect(isEvenI, ConstantFP::get(m_builder->getFloatTy(), 0.25),
                                    ConstantFP::get(m_builder->getFloatTy(), 0.75));
  }

  Value *coordT = m_builder->CreateFDiv(xChromaInfo.coordJ, xChromaInfo.chromaHeight);

  SmallVector<Value *, 4> coordsChromaA;
  sampleInfo->imageDesc = xChromaInfo.imageDesc1;
  coordsChromaA.push_back(m_builder->CreateFDiv(subCoordI, xChromaInfo.chromaWidth));
  coordsChromaA.push_back(coordT);
  Instruction *pImageOpA = cast<Instruction>(createImageSampleInternal(coordsChromaA, sampleInfo));

  SmallVector<Value *, 4> coordsChromaB;
  coordsChromaB.push_back(m_builder->CreateFDiv(
      m_builder->CreateFAdd(subCoordI, ConstantFP::get(m_builder->getFloatTy(), 1.0)), xChromaInfo.chromaWidth));
  coordsChromaB.push_back(coordT);
  Instruction *pImageOpB = cast<Instruction>(createImageSampleInternal(coordsChromaB, sampleInfo));

  Value *result = m_builder->createFMix(pImageOpB, pImageOpA, alpha);

  return m_builder->CreateShuffleVector(result, result, ArrayRef<int>{0, 2});
}

// =====================================================================================================================
// Create YCbCr Reconstruct linear XY chroma sample
//
// @param xyChromeInfo : Information for downsampled chroma channels in XY dimension
Value *YCbCrConverter::reconstructLinearXYChromaSample(XYChromaSampleInfo &xyChromaInfo) {
  YCbCrSampleInfo *sampleInfo = xyChromaInfo.ycbcrInfo;

  Value *width = xyChromaInfo.chromaWidth;
  Value *height = xyChromaInfo.chromaHeight;

  Value *isEvenI = m_builder->CreateICmpEQ(
      m_builder->CreateSMod(m_builder->CreateFPToSI(xyChromaInfo.coordI, m_builder->getInt32Ty()),
                            m_builder->getInt32(2)),
      m_builder->getInt32(0));
  Value *isEvenJ = m_builder->CreateICmpEQ(
      m_builder->CreateSMod(m_builder->CreateFPToSI(xyChromaInfo.coordJ, m_builder->getInt32Ty()),
                            m_builder->getInt32(2)),
      m_builder->getInt32(0));

  Value *subCoordI = m_builder->CreateUnaryIntrinsic(
      Intrinsic::floor, m_builder->CreateFDiv(xyChromaInfo.coordI, ConstantFP::get(m_builder->getFloatTy(), 2.0)));
  Value *subCoordJ = m_builder->CreateUnaryIntrinsic(
      Intrinsic::floor, m_builder->CreateFDiv(xyChromaInfo.coordJ, ConstantFP::get(m_builder->getFloatTy(), 2.0)));

  if (xyChromaInfo.chromaOffsetX != ChromaLocation::CositedEven) {
    subCoordI = m_builder->CreateSelect(
        isEvenI, m_builder->CreateFSub(subCoordI, ConstantFP::get(m_builder->getFloatTy(), 1.0)), subCoordI);
  }

  if (xyChromaInfo.chromaOffsetY != ChromaLocation::CositedEven) {
    subCoordJ = m_builder->CreateSelect(
        isEvenJ, m_builder->CreateFSub(subCoordJ, ConstantFP::get(m_builder->getFloatTy(), 1.0)), subCoordJ);
  }

  Value *alpha = nullptr;
  if (xyChromaInfo.chromaOffsetX == ChromaLocation::CositedEven) {
    alpha = m_builder->CreateSelect(isEvenI, ConstantFP::get(m_builder->getFloatTy(), 0.0),
                                    ConstantFP::get(m_builder->getFloatTy(), 0.5));
  } else {
    alpha = m_builder->CreateSelect(isEvenI, ConstantFP::get(m_builder->getFloatTy(), 0.25),
                                    ConstantFP::get(m_builder->getFloatTy(), 0.75));
  }

  Value *beta = nullptr;
  if (xyChromaInfo.chromaOffsetY == ChromaLocation::CositedEven) {
    beta = m_builder->CreateSelect(isEvenJ, ConstantFP::get(m_builder->getFloatTy(), 0.0),
                                   ConstantFP::get(m_builder->getFloatTy(), 0.5));
  } else {
    beta = m_builder->CreateSelect(isEvenJ, ConstantFP::get(m_builder->getFloatTy(), 0.25),
                                   ConstantFP::get(m_builder->getFloatTy(), 0.75));
  }

  SmallVector<Value *, 4> coordsChromaTL;
  SmallVector<Value *, 4> coordsChromaTR;
  SmallVector<Value *, 4> coordsChromaBL;
  SmallVector<Value *, 4> coordsChromaBR;

  Value *result = nullptr;
  if (xyChromaInfo.planeCount == 2) {
    sampleInfo->imageDesc = xyChromaInfo.imageDesc1;

    // Sample TL
    coordsChromaTL.push_back(m_builder->CreateFDiv(subCoordI, width));
    coordsChromaTL.push_back(m_builder->CreateFDiv(subCoordJ, height));
    Instruction *coordTL = cast<Instruction>(createImageSampleInternal(coordsChromaTL, sampleInfo));

    // Sample TR
    coordsChromaTR.push_back(
        m_builder->CreateFDiv(m_builder->CreateFAdd(subCoordI, ConstantFP::get(m_builder->getFloatTy(), 1.0)), width));
    coordsChromaTR.push_back(m_builder->CreateFDiv(subCoordJ, height));
    Instruction *coordTR = cast<Instruction>(createImageSampleInternal(coordsChromaTR, sampleInfo));

    // Sample BL
    coordsChromaBL.push_back(m_builder->CreateFDiv(subCoordI, width));
    coordsChromaBL.push_back(
        m_builder->CreateFDiv(m_builder->CreateFAdd(subCoordJ, ConstantFP::get(m_builder->getFloatTy(), 1.0)), height));
    Instruction *coordBL = cast<Instruction>(createImageSampleInternal(coordsChromaBL, sampleInfo));

    // Sample BR
    coordsChromaBR.push_back(
        m_builder->CreateFDiv(m_builder->CreateFAdd(subCoordI, ConstantFP::get(m_builder->getFloatTy(), 1.0)), width));
    coordsChromaBR.push_back(
        m_builder->CreateFDiv(m_builder->CreateFAdd(subCoordJ, ConstantFP::get(m_builder->getFloatTy(), 1.0)), height));
    Instruction *coordBR = cast<Instruction>(createImageSampleInternal(coordsChromaBR, sampleInfo));

    // Linear interpolate
    result = bilinearBlend(alpha, beta, coordTL, coordTR, coordBL, coordBR);
    result = m_builder->CreateShuffleVector(result, result, ArrayRef<int>{0, 2});
  } else if (xyChromaInfo.planeCount == 3) {
    // Sample TL
    coordsChromaTL.push_back(m_builder->CreateFDiv(subCoordI, width));
    coordsChromaTL.push_back(m_builder->CreateFDiv(subCoordJ, height));
    sampleInfo->imageDesc = xyChromaInfo.imageDesc1;
    Value *coordTLb = cast<Instruction>(createImageSampleInternal(coordsChromaTL, sampleInfo));

    sampleInfo->imageDesc = xyChromaInfo.imageDesc2;
    Value *coordTLr = cast<Instruction>(createImageSampleInternal(coordsChromaTL, sampleInfo));
    Value *coordTL = m_builder->CreateShuffleVector(coordTLr, coordTLb, ArrayRef<int>{0, 6});

    // Sample TR
    coordsChromaTR.push_back(
        m_builder->CreateFDiv(m_builder->CreateFAdd(subCoordI, ConstantFP::get(m_builder->getFloatTy(), 1.0)), width));
    coordsChromaTR.push_back(m_builder->CreateFDiv(subCoordJ, height));
    sampleInfo->imageDesc = xyChromaInfo.imageDesc1;
    Value *coordTRb = cast<Instruction>(createImageSampleInternal(coordsChromaTR, sampleInfo));

    sampleInfo->imageDesc = xyChromaInfo.imageDesc2;
    Value *coordTRr = cast<Instruction>(createImageSampleInternal(coordsChromaTR, sampleInfo));
    Value *coordTR = m_builder->CreateShuffleVector(coordTRr, coordTRb, ArrayRef<int>{0, 6});

    // Sample BL
    coordsChromaBL.push_back(m_builder->CreateFDiv(subCoordI, width));
    coordsChromaBL.push_back(
        m_builder->CreateFDiv(m_builder->CreateFAdd(subCoordJ, ConstantFP::get(m_builder->getFloatTy(), 1.0)), height));
    sampleInfo->imageDesc = xyChromaInfo.imageDesc1;
    Value *coordBLb = cast<Instruction>(createImageSampleInternal(coordsChromaBL, sampleInfo));
    sampleInfo->imageDesc = xyChromaInfo.imageDesc2;
    Value *coordBLr = cast<Instruction>(createImageSampleInternal(coordsChromaBL, sampleInfo));
    Value *coordBL = m_builder->CreateShuffleVector(coordBLr, coordBLb, ArrayRef<int>{0, 6});

    // Sample BR
    coordsChromaBR.push_back(
        m_builder->CreateFDiv(m_builder->CreateFAdd(subCoordI, ConstantFP::get(m_builder->getFloatTy(), 1.0)), width));
    coordsChromaBR.push_back(
        m_builder->CreateFDiv(m_builder->CreateFAdd(subCoordJ, ConstantFP::get(m_builder->getFloatTy(), 1.0)), height));
    sampleInfo->imageDesc = xyChromaInfo.imageDesc1;
    Value *coordBRb = cast<Instruction>(createImageSampleInternal(coordsChromaBR, sampleInfo));
    sampleInfo->imageDesc = xyChromaInfo.imageDesc2;
    Value *coordBRr = cast<Instruction>(createImageSampleInternal(coordsChromaBR, sampleInfo));
    Value *coordBR = m_builder->CreateShuffleVector(coordBRr, coordBRb, ArrayRef<int>{0, 6});

    // Linear interpolate
    result = bilinearBlend(alpha, beta, coordTL, coordTR, coordBL, coordBR);
  }

  return result;
}

// =====================================================================================================================
// Create YCbCr image sample internal
//
// @param coords : The ST coordinates
// @param ycbcrInfo : YCbCr smaple information
Value *YCbCrConverter::createImageSampleInternal(SmallVectorImpl<Value *> &coordsIn, YCbCrSampleInfo *ycbcrInfo) {

  unsigned imageDim = ycbcrInfo->dim;

  Value *coords = UndefValue::get(FixedVectorType::get(coordsIn[0]->getType(), m_builder->getImageNumCoords(imageDim)));
  coords = m_builder->CreateInsertElement(coords, coordsIn[0], uint64_t(0));
  coords = m_builder->CreateInsertElement(coords, coordsIn[1], uint64_t(1));

  if (imageDim == Builder::Dim2DArray)
    coords = m_builder->CreateInsertElement(coords, m_coordZ, uint64_t(2));

  return m_builder->CreateImageSampleGather(ycbcrInfo->resultTy, ycbcrInfo->dim, ycbcrInfo->flags, coords,
                                            ycbcrInfo->imageDesc, ycbcrInfo->samplerDesc, ycbcrInfo->address,
                                            ycbcrInfo->instNameStr, ycbcrInfo->isSample);
}

// =====================================================================================================================
// YCbCrConverter
//
// @param builder : ImageBuilder instance
// @param ycbcrMetaData : YCbCr conversion metadata
// @param ycbcrSampleInfo : YCbCr sample information
// @param gfxIp : The GfxIp Version
YCbCrConverter::YCbCrConverter(ImageBuilder *builder, const SamplerYCbCrConversionMetaData &ycbcrMetaData,
                               YCbCrSampleInfo *ycbcrSampleInfo, GfxIpVersion *gfxIp)
    : m_builder(builder), m_metaData(ycbcrMetaData), m_gfxIp(gfxIp) {
  m_imgDescsChroma.resize(3);
  setYCbCrSampleInfo(ycbcrSampleInfo);
  genSamplerDescChroma();
  genImgDescChroma();
  prepareCoord();
}

// =====================================================================================================================
// Set YCbCr sample infomation
//
// @param ycbcrSampleInfo : YCbCr sampler information
void YCbCrConverter::setYCbCrSampleInfo(YCbCrSampleInfo *ycbcrSampleInfo) {
  m_ycbcrSampleInfo = ycbcrSampleInfo;

  m_samplerDescLuma = m_ycbcrSampleInfo->samplerDesc;
  m_imgDescLuma = m_ycbcrSampleInfo->imageDesc;

  m_samplerDescChroma = m_samplerDescLuma;
  m_imgDescsChroma[0] = m_imgDescLuma;

  m_resultType = m_ycbcrSampleInfo->resultTy;
}

// =====================================================================================================================
// Generate sampler descriptor for chroma channel
void YCbCrConverter::genSamplerDescChroma() {
  m_samplerDescChroma =
      generateSamplerDesc(m_samplerDescLuma, static_cast<SamplerFilter>(m_metaData.word1.chromaFilter),
                          m_metaData.word0.forceExplicitReconstruct);
}

// =====================================================================================================================
// Generate image descriptor for chroma channel
void YCbCrConverter::genImgDescChroma() {
  SqImgRsrcRegHandler proxySqRsrcRegHelper(m_builder, m_imgDescLuma, m_gfxIp);

  Value *width = nullptr;
  Value *height = nullptr;

  if ((m_metaData.word5.lumaDepth > 1) && (m_metaData.word1.planes > 1)) {
    width = ConstantInt::get(m_builder->getInt32Ty(), m_metaData.word4.lumaWidth);
    height = ConstantInt::get(m_builder->getInt32Ty(), m_metaData.word4.lumaHeight);
    m_width = ConstantFP::get(m_builder->getFloatTy(), m_metaData.word4.lumaWidth);
    m_height = ConstantFP::get(m_builder->getFloatTy(), m_metaData.word4.lumaHeight);
  } else {
    width = proxySqRsrcRegHelper.getReg(SqRsrcRegs::Width);
    height = proxySqRsrcRegHelper.getReg(SqRsrcRegs::Height);
    m_width = m_builder->CreateUIToFP(width, m_builder->getFloatTy());
    m_height = m_builder->CreateUIToFP(height, m_builder->getFloatTy());
  }

  if (m_metaData.word1.planes == 1) {
    Value *imgDataFmt = proxySqRsrcRegHelper.getReg(SqRsrcRegs::Format);
    Value *dstSelXYZW = proxySqRsrcRegHelper.getReg(SqRsrcRegs::DstSelXYZW);
    Value *isGbGrFmt = nullptr;
    Value *isBgRgFmt = nullptr;

    switch (m_gfxIp->major) {
    case 6:
    case 7:
    case 8:
    case 9: {
      isGbGrFmt = m_builder->CreateICmpEQ(
          imgDataFmt, m_builder->getInt32(ImageBuilder::ImgDataFormat::IMG_DATA_FORMAT_BG_RG__CORE));

      isBgRgFmt = m_builder->CreateICmpEQ(
          imgDataFmt, m_builder->getInt32(ImageBuilder::ImgDataFormat::IMG_DATA_FORMAT_GB_GR__CORE));

      proxySqRsrcRegHelper.setReg(SqRsrcRegs::Format,
                                  m_builder->getInt32(ImageBuilder::ImgDataFormat::IMG_DATA_FORMAT_8_8_8_8));
      break;
    }
    case 10: {
      isGbGrFmt = m_builder->CreateICmpEQ(imgDataFmt,
                                          m_builder->getInt32(ImageBuilder::ImgFmtGfx10::IMG_FMT_BG_RG_UNORM__GFX10CORE));
      isBgRgFmt = m_builder->CreateICmpEQ(imgDataFmt,
                                          m_builder->getInt32(ImageBuilder::ImgFmtGfx10::IMG_FMT_GB_GR_UNORM__GFX10CORE));

      proxySqRsrcRegHelper.setReg(SqRsrcRegs::Format,
                                  m_builder->getInt32(ImageBuilder::ImgFmtGfx10::IMG_FMT_8_8_8_8_UNORM__GFX10CORE));
      break;
    }
    default:
      llvm_unreachable("GFX IP not supported!");
      break;
    }

    dstSelXYZW = m_builder->CreateSelect(isGbGrFmt, m_builder->getInt32(0x977), dstSelXYZW);
    dstSelXYZW = m_builder->CreateSelect(isBgRgFmt, m_builder->getInt32(0xF2E), dstSelXYZW);

    YCbCrAddressHandler addrHelper(m_builder, &proxySqRsrcRegHelper, m_gfxIp);
    addrHelper.genHeightAndPitch(m_metaData.word0.bitDepth.channelBitsR, 32, m_metaData.word2.bitCounts.xBitCount,
                                 m_metaData.word1.planes);
    proxySqRsrcRegHelper.setReg(SqRsrcRegs::Width,
                                m_builder->CreateLShr(width, ConstantInt::get(m_builder->getInt32Ty(), 1)));
    proxySqRsrcRegHelper.setReg(SqRsrcRegs::DstSelXYZW, dstSelXYZW);
    proxySqRsrcRegHelper.setReg(SqRsrcRegs::Pitch, addrHelper.getPitchCb());
    m_imgDescsChroma[1] = proxySqRsrcRegHelper.getRegister();
  }
}

// =====================================================================================================================
// Set image descriptor for chroma channel
//
// @param planeIndex : Specific plane index for setting image descriptor
// @param imageDesc : Image descriptor
void YCbCrConverter::SetImgDescChroma(unsigned planeIndex, llvm::Value *imageDesc) {
  assert(planeIndex < 3);
  m_imgDescsChroma[planeIndex] = imageDesc;
}

// =====================================================================================================================
// Get image descriptor for chroma channel
//
// @param planeIndex : Specific plane index for loading image descriptor
llvm::Value *YCbCrConverter::GetImgDescChroma(unsigned planeIndex) {
  assert(planeIndex < 3);
  return m_imgDescsChroma[planeIndex];
}

// =====================================================================================================================
// Convert from YCbCr to RGBA color space
Value *YCbCrConverter::convertColorSpace() {
  unsigned channelBits[3] = {
      m_metaData.word0.bitDepth.channelBitsR,
      m_metaData.word0.bitDepth.channelBitsG,
      m_metaData.word0.bitDepth.channelBitsB,
  };
  return convertColor(m_resultType, static_cast<SamplerYCbCrModelConversion>(m_metaData.word0.yCbCrModel),
                      static_cast<SamplerYCbCrRange>(m_metaData.word0.yCbCrRange), channelBits, m_ycbcrData);
}

// =====================================================================================================================
// Prepare the ST coords
void YCbCrConverter::prepareCoord() {
  Value *coords = m_ycbcrSampleInfo->address[Builder::ImageAddressIdxCoordinate];

  assert(Builder::getImageNumCoords(m_ycbcrSampleInfo->dim) ==
         cast<FixedVectorType>(coords->getType())->getNumElements());

  m_coordS = m_builder->CreateExtractElement(coords, m_builder->getInt64(0));
  m_coordT = m_builder->CreateExtractElement(coords, m_builder->getInt64(1));

  if (m_ycbcrSampleInfo->dim == Builder::Dim2DArray) {
    m_coordZ = m_builder->CreateExtractElement(coords, m_builder->getInt64(2));
  }

  m_coordU = transferSTtoUVCoords(m_coordS, m_width);
  m_coordV = transferSTtoUVCoords(m_coordT, m_height);

  m_coordI = transferUVtoIJCoords(static_cast<SamplerFilter>(m_metaData.word1.lumaFilter), m_coordU);
  m_coordJ = transferUVtoIJCoords(static_cast<SamplerFilter>(m_metaData.word1.lumaFilter), m_coordV);
}

// =====================================================================================================================
// Generate sampler descriptor for YCbCr conversion
//
// @param samplerDesc : Sampler descriptor
// @param filter : The type of sampler filter
// @param forceExplicitReconstruction : Enable/Disable force explict chroma reconstruction
Value *YCbCrConverter::generateSamplerDesc(Value *samplerDesc, SamplerFilter filter, bool forceExplicitReconstruction) {
  SqImgSampRegHandler imgRegHelper(m_builder, samplerDesc, m_gfxIp);

  /// Determines if "TexFilter" should be ignored or not.
  // enum class TexFilterMode : uint32
  // {
  //     Blend = 0x0, ///< Use the filter method specified by the TexFilter enumeration
  //     Min   = 0x1, ///< Use the minimum value returned by the sampler, no blending op occurs
  //     Max   = 0x2, ///< Use the maximum value returned by the sampler, no blending op occurs
  // };
  // Force use blend mode
  // imgRegHelper.SetFilterMode(m_builder->getInt32(0b00));
  imgRegHelper.setReg(SqSampRegs::FilterMode, m_builder->getInt32(0b00));

  /// Enumeration which defines the mode for magnification and minification sampling
  // enum XyFilter : uint32
  // {
  //     XyFilterPoint = 0,          ///< Use single point sampling
  //     XyFilterLinear,             ///< Use linear sampling
  //     XyFilterAnisotropicPoint,   ///< Use anisotropic with single point sampling
  //     XyFilterAnisotropicLinear,  ///< Use anisotropic with linear sampling
  //     XyFilterCount
  // };
  if ((filter == SamplerFilter::Nearest) || forceExplicitReconstruction) {
    imgRegHelper.setReg(SqSampRegs::xyMagFilter, m_builder->getInt32(0b00));
    imgRegHelper.setReg(SqSampRegs::xyMinFilter, m_builder->getInt32(0b00));
  } else // filter == SamplerFilter::Linear
  {
    imgRegHelper.setReg(SqSampRegs::xyMagFilter, m_builder->getInt32(0b01));
    imgRegHelper.setReg(SqSampRegs::xyMinFilter, m_builder->getInt32(0b01));
  }

  return imgRegHelper.getRegister();
}

// =====================================================================================================================
// Implement range expanding operation on checking whether the encoding uses full numerical range on luma channel
//
// @param range : Specifies whether the encoding uses the full numerical range
// @param channelBits : Channel bits
// @param sampleResult : Sample results which need range expansion, assume in sequence => Cr, Y, Cb
Value *YCbCrConverter::rangeExpand(SamplerYCbCrRange range, const unsigned *channelBits, Value *sampleResult) {
  switch (range) {
  case SamplerYCbCrRange::ItuFull: {
    //              [2^(n - 1)/((2^n) - 1)]
    // convVec1 =  [         0.0         ]
    //              [2^(n - 1)/((2^n) - 1)]
    float row0Num = static_cast<float>(0x1u << (channelBits[0] - 0x1u)) / ((0x1u << channelBits[0]) - 1u);
    float row2Num = static_cast<float>(0x1u << (channelBits[2] - 0x1u)) / ((0x1u << channelBits[2]) - 1u);

    Value *convVec1 = UndefValue::get(FixedVectorType::get(m_builder->getFloatTy(), 3));
    convVec1 = m_builder->CreateInsertElement(convVec1, ConstantFP::get(m_builder->getFloatTy(), row0Num), uint64_t(0));
    convVec1 = m_builder->CreateInsertElement(convVec1, ConstantFP::get(m_builder->getFloatTy(), 0.0f), uint64_t(1));
    convVec1 = m_builder->CreateInsertElement(convVec1, ConstantFP::get(m_builder->getFloatTy(), row2Num), uint64_t(2));

    //          [Cr]   convVec1[0]
    // result = [ Y] - convVec1[1]
    //          [Cb]   convVec1[2]
    return m_builder->CreateFSub(sampleResult, convVec1);
  }
  case SamplerYCbCrRange::ItuNarrow: {
    //             [(2^n - 1)/(224 x (2^(n - 8))]
    // convVec1 = [(2^n - 1)/(219 x (2^(n - 8))]
    //             [(2^n - 1)/(224 x (2^(n - 8))]
    float row0Num = static_cast<float>((0x1u << channelBits[0]) - 1u) / (224u * (0x1u << (channelBits[0] - 8)));
    float row1Num = static_cast<float>((0x1u << channelBits[1]) - 1u) / (219u * (0x1u << (channelBits[1] - 8)));
    float row2Num = static_cast<float>((0x1u << channelBits[2]) - 1u) / (224u * (0x1u << (channelBits[2] - 8)));

    Value *convVec1 = UndefValue::get(FixedVectorType::get(m_builder->getFloatTy(), 3));
    convVec1 = m_builder->CreateInsertElement(convVec1, ConstantFP::get(m_builder->getFloatTy(), row0Num), uint64_t(0));
    convVec1 = m_builder->CreateInsertElement(convVec1, ConstantFP::get(m_builder->getFloatTy(), row1Num), uint64_t(1));
    convVec1 = m_builder->CreateInsertElement(convVec1, ConstantFP::get(m_builder->getFloatTy(), row2Num), uint64_t(2));

    //             [(128 x (2^(n - 8))/(224 x (2^(n - 8))]
    // convVec2 = [( 16 x (2^(n - 8))/(219 x (2^(n - 8))]
    //             [(128 x (2^(n - 8))/(224 x (2^(n - 8))]
    row0Num = static_cast<float>(128u * (0x1u << (channelBits[0] - 8))) / (224u * (0x1u << (channelBits[0] - 8)));
    row1Num = static_cast<float>(16u * (0x1u << (channelBits[1] - 8))) / (219u * (0x1u << (channelBits[1] - 8)));
    row2Num = static_cast<float>(128u * (0x1u << (channelBits[2] - 8))) / (224u * (0x1u << (channelBits[2] - 8)));

    Value *convVec2 = UndefValue::get(FixedVectorType::get(m_builder->getFloatTy(), 3));
    convVec2 = m_builder->CreateInsertElement(convVec2, ConstantFP::get(m_builder->getFloatTy(), row0Num), uint64_t(0));
    convVec2 = m_builder->CreateInsertElement(convVec2, ConstantFP::get(m_builder->getFloatTy(), row1Num), uint64_t(1));
    convVec2 = m_builder->CreateInsertElement(convVec2, ConstantFP::get(m_builder->getFloatTy(), row2Num), uint64_t(2));

    //          convVec1[0]   [Cr]   convVec2[0]
    // result = convVec1[1] * [ Y] - convVec2[1]
    //          convVec1[2]   [Cb]   convVec2[2]
    return m_builder->CreateFSub(m_builder->CreateFMul(sampleResult, convVec1), convVec2);
  }

  default:
    llvm_unreachable("Unknown range expanding type!");
    return nullptr;
  }
}

// =====================================================================================================================
// Sample YCbCr data from each plane
void YCbCrConverter::sampleYCbCrData() {
  SmallVector<Value *, 4> coordsLuma;
  SmallVector<Value *, 4> coordsChroma;

  if ((m_metaData.word5.lumaDepth > 1) && (m_metaData.word1.planes > 1)) {
    SqImgRsrcRegHandler proxySqRsrcRegHelper(m_builder, m_imgDescLuma, m_gfxIp);

    Value *widthPadding = proxySqRsrcRegHelper.getReg(SqRsrcRegs::Width);
    Value *heightPadding = proxySqRsrcRegHelper.getReg(SqRsrcRegs::Height);
    widthPadding = m_builder->CreateUIToFP(widthPadding, m_builder->getFloatTy());
    heightPadding = m_builder->CreateUIToFP(heightPadding, m_builder->getFloatTy());

    // coordST = coordST * scaleFactor
    Value *widthScaleFactor = m_builder->CreateFDiv(m_width, widthPadding);
    Value *heightScaleFactor = m_builder->CreateFDiv(m_height, heightPadding);

    m_coordS = m_builder->CreateFMul(m_coordS, widthScaleFactor);
    m_coordT = m_builder->CreateFMul(m_coordT, heightScaleFactor);
  }

  // coordI -> coordS
  coordsLuma.push_back(m_coordS);
  // coordJ -> coordT
  coordsLuma.push_back(m_coordT);

  // Sample Y and A channels
  Value *imageOpLuma = cast<Instruction>(createImageSampleInternal(coordsLuma, m_ycbcrSampleInfo));
  imageOpLuma = m_builder->CreateShuffleVector(imageOpLuma, imageOpLuma, ArrayRef<int>{1, 3});

  // Init sample chroma info
  m_ycbcrSampleInfo->samplerDesc = m_samplerDescChroma;

  // Init chroma width and height
  Value *chromaWidth = m_builder->CreateFMul(m_width, ConstantFP::get(m_builder->getFloatTy(), 0.5f));
  Value *chromaHeight = m_builder->CreateFMul(m_height, ConstantFP::get(m_builder->getFloatTy(), 0.5f));

  // Init smaple chroma info for downsampled chroma channels in the x dimension
  XChromaSampleInfo xChromaInfo = {};
  xChromaInfo.ycbcrInfo = m_ycbcrSampleInfo;
  xChromaInfo.imageDesc1 = m_imgDescsChroma[1];
  xChromaInfo.coordI = m_coordI;
  xChromaInfo.coordJ = m_coordJ;
  xChromaInfo.chromaWidth = chromaWidth;
  xChromaInfo.chromaHeight = m_height;
  xChromaInfo.chromaOffsetX = static_cast<ChromaLocation>(m_metaData.word1.xChromaOffset);

  // Init smaple chroma info for downsampled chroma channels in xy dimension
{'fraction_non_alphanumeric': 0.189865822151846, 'fraction_numerical': 0.01831819318276523, 'mean_word_length': 4.858412264427302, 'pattern_counts': {'":': 0, '<': 193, '<?xml version=': 0, '>': 555, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 34, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3492502', 'n_tokens_mistral': 20089, 'n_tokens_neox': 18936, 'n_words': 3228}